summaryrefslogtreecommitdiffstats
path: root/gnu/usr.bin
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/usr.bin')
-rw-r--r--gnu/usr.bin/Makefile6
-rw-r--r--gnu/usr.bin/Makefile.inc3
-rw-r--r--gnu/usr.bin/as/CONTRIBUTORS11
-rw-r--r--gnu/usr.bin/as/COPYING339
-rw-r--r--gnu/usr.bin/as/ChangeLog429
-rw-r--r--gnu/usr.bin/as/Makefile73
-rw-r--r--gnu/usr.bin/as/Makefile.gnu356
-rw-r--r--gnu/usr.bin/as/Makefile.in409
-rw-r--r--gnu/usr.bin/as/NOTES16
-rw-r--r--gnu/usr.bin/as/NOTES.config52
-rw-r--r--gnu/usr.bin/as/README212
-rw-r--r--gnu/usr.bin/as/README-vms248
-rw-r--r--gnu/usr.bin/as/README.coff79
-rw-r--r--gnu/usr.bin/as/README.gnu133
-rw-r--r--gnu/usr.bin/as/README.pic25
-rw-r--r--gnu/usr.bin/as/README.rich144
-rw-r--r--gnu/usr.bin/as/VERSION1
-rw-r--r--gnu/usr.bin/as/app.c539
-rw-r--r--gnu/usr.bin/as/append.c37
-rw-r--r--gnu/usr.bin/as/as.1271
-rw-r--r--gnu/usr.bin/as/as.1aout271
-rw-r--r--gnu/usr.bin/as/as.c429
-rw-r--r--gnu/usr.bin/as/as.h416
-rw-r--r--gnu/usr.bin/as/atof-generic.c526
-rw-r--r--gnu/usr.bin/as/bignum-copy.c76
-rw-r--r--gnu/usr.bin/as/bignum.h64
-rw-r--r--gnu/usr.bin/as/bit_fix.h54
-rw-r--r--gnu/usr.bin/as/cond.c239
-rw-r--r--gnu/usr.bin/as/config-gas.com76
-rw-r--r--gnu/usr.bin/as/config/Makefile.hp3007
-rw-r--r--gnu/usr.bin/as/config/Makefile.i3865
-rw-r--r--gnu/usr.bin/as/config/Makefile.pc5327
-rw-r--r--gnu/usr.bin/as/config/Makefile.sparc5
-rw-r--r--gnu/usr.bin/as/config/Makefile.vax4
-rw-r--r--gnu/usr.bin/as/config/a.out.gnu.h261
-rw-r--r--gnu/usr.bin/as/config/aout.h430
-rw-r--r--gnu/usr.bin/as/config/atof-ieee.c524
-rw-r--r--gnu/usr.bin/as/config/atof-ns32k.c436
-rw-r--r--gnu/usr.bin/as/config/atof-tahoe.c428
-rw-r--r--gnu/usr.bin/as/config/atof-vax.c497
-rw-r--r--gnu/usr.bin/as/config/coff.h783
-rw-r--r--gnu/usr.bin/as/config/cplus-dem.c927
-rw-r--r--gnu/usr.bin/as/config/ho-ansi.h29
-rw-r--r--gnu/usr.bin/as/config/ho-decstation.h29
-rw-r--r--gnu/usr.bin/as/config/ho-generic.h30
-rw-r--r--gnu/usr.bin/as/config/ho-hpux.h34
-rw-r--r--gnu/usr.bin/as/config/ho-i386.h30
-rw-r--r--gnu/usr.bin/as/config/ho-i386aix.h24
-rw-r--r--gnu/usr.bin/as/config/ho-rs6000.h22
-rw-r--r--gnu/usr.bin/as/config/ho-sun3.h3
-rw-r--r--gnu/usr.bin/as/config/ho-sun386.h5
-rw-r--r--gnu/usr.bin/as/config/ho-sun4.h3
-rw-r--r--gnu/usr.bin/as/config/ho-sunos.h81
-rw-r--r--gnu/usr.bin/as/config/ho-sysv.h27
-rw-r--r--gnu/usr.bin/as/config/ho-vax.h27
-rw-r--r--gnu/usr.bin/as/config/ho-vms.h30
-rw-r--r--gnu/usr.bin/as/config/i386-opcode.h806
-rw-r--r--gnu/usr.bin/as/config/i386.c1946
-rw-r--r--gnu/usr.bin/as/config/i386.h296
-rw-r--r--gnu/usr.bin/as/config/mh-i3861
-rw-r--r--gnu/usr.bin/as/config/mh-i386aix5
-rw-r--r--gnu/usr.bin/as/config/mh-i386v41
-rw-r--r--gnu/usr.bin/as/config/mt-ebmon29k6
-rw-r--r--gnu/usr.bin/as/config/mt-h83005
-rw-r--r--gnu/usr.bin/as/config/mt-h8300hds4
-rw-r--r--gnu/usr.bin/as/config/mt-i386aix3
-rw-r--r--gnu/usr.bin/as/config/mt-mips1
-rw-r--r--gnu/usr.bin/as/config/mt-rs60001
-rw-r--r--gnu/usr.bin/as/config/obj-aout.c648
-rw-r--r--gnu/usr.bin/as/config/obj-aout.h210
-rw-r--r--gnu/usr.bin/as/config/obj-bfd-sunos.c71
-rw-r--r--gnu/usr.bin/as/config/obj-bfd-sunos.h69
-rw-r--r--gnu/usr.bin/as/config/obj-bout.c476
-rw-r--r--gnu/usr.bin/as/config/obj-bout.h313
-rw-r--r--gnu/usr.bin/as/config/obj-coff.c1978
-rw-r--r--gnu/usr.bin/as/config/obj-coff.h598
-rw-r--r--gnu/usr.bin/as/config/obj-coffbfd.c2182
-rw-r--r--gnu/usr.bin/as/config/obj-coffbfd.h516
-rw-r--r--gnu/usr.bin/as/config/obj-generic.c41
-rw-r--r--gnu/usr.bin/as/config/obj-generic.h78
-rw-r--r--gnu/usr.bin/as/config/obj-ieee.c539
-rw-r--r--gnu/usr.bin/as/config/obj-ieee.h46
-rw-r--r--gnu/usr.bin/as/config/obj-vms.c5484
-rw-r--r--gnu/usr.bin/as/config/obj-vms.h474
-rw-r--r--gnu/usr.bin/as/config/tc-a29k.c1113
-rw-r--r--gnu/usr.bin/as/config/tc-a29k.h40
-rw-r--r--gnu/usr.bin/as/config/tc-generic.c0
-rw-r--r--gnu/usr.bin/as/config/tc-generic.h37
-rw-r--r--gnu/usr.bin/as/config/tc-h8300.c1295
-rw-r--r--gnu/usr.bin/as/config/tc-h8300.h38
-rw-r--r--gnu/usr.bin/as/config/tc-i386.c2313
-rw-r--r--gnu/usr.bin/as/config/tc-i386.h254
-rw-r--r--gnu/usr.bin/as/config/tc-i860.c1295
-rw-r--r--gnu/usr.bin/as/config/tc-i860.h24
-rw-r--r--gnu/usr.bin/as/config/tc-i960.c2759
-rw-r--r--gnu/usr.bin/as/config/tc-i960.h281
-rw-r--r--gnu/usr.bin/as/config/tc-m68851.h304
-rw-r--r--gnu/usr.bin/as/config/tc-m68k.c4076
-rw-r--r--gnu/usr.bin/as/config/tc-m68k.h60
-rw-r--r--gnu/usr.bin/as/config/tc-m68kmote.h64
-rw-r--r--gnu/usr.bin/as/config/tc-m88k.c1435
-rw-r--r--gnu/usr.bin/as/config/tc-m88k.h35
-rw-r--r--gnu/usr.bin/as/config/tc-mips.c0
-rw-r--r--gnu/usr.bin/as/config/tc-mips.h0
-rw-r--r--gnu/usr.bin/as/config/tc-ns32k.c1923
-rw-r--r--gnu/usr.bin/as/config/tc-ns32k.h60
-rw-r--r--gnu/usr.bin/as/config/tc-rs6000.c0
-rw-r--r--gnu/usr.bin/as/config/tc-rs6000.h0
-rw-r--r--gnu/usr.bin/as/config/tc-sparc.c1766
-rw-r--r--gnu/usr.bin/as/config/tc-sparc.h54
-rw-r--r--gnu/usr.bin/as/config/tc-tahoe.c1924
-rw-r--r--gnu/usr.bin/as/config/tc-tahoe.h36
-rw-r--r--gnu/usr.bin/as/config/tc-vax.c3073
-rw-r--r--gnu/usr.bin/as/config/tc-vax.h25
-rw-r--r--gnu/usr.bin/as/config/te-dpx2.h8
-rw-r--r--gnu/usr.bin/as/config/te-generic.h25
-rw-r--r--gnu/usr.bin/as/config/te-hpux.h99
-rw-r--r--gnu/usr.bin/as/config/te-i386aix.h19
-rw-r--r--gnu/usr.bin/as/config/te-ic960.h46
-rw-r--r--gnu/usr.bin/as/config/te-sco386.h7
-rw-r--r--gnu/usr.bin/as/config/te-sequent.h32
-rw-r--r--gnu/usr.bin/as/config/te-sun3.h49
-rw-r--r--gnu/usr.bin/as/config/te-sysv32.h4
-rw-r--r--gnu/usr.bin/as/config/vax-inst.h77
-rw-r--r--gnu/usr.bin/as/configdos.bat14
-rwxr-xr-xgnu/usr.bin/as/configure.in204
-rw-r--r--gnu/usr.bin/as/debug.c104
-rw-r--r--gnu/usr.bin/as/doc/Makefile187
-rw-r--r--gnu/usr.bin/as/doc/Makefile.in172
-rw-r--r--gnu/usr.bin/as/doc/a29k-coff.m414
-rw-r--r--gnu/usr.bin/as/doc/a29k.m49
-rw-r--r--gnu/usr.bin/as/doc/all.m420
-rw-r--r--gnu/usr.bin/as/doc/as.texinfo6730
-rw-r--r--gnu/usr.bin/as/doc/config.status5
-rw-r--r--gnu/usr.bin/as/doc/configure.in34
-rw-r--r--gnu/usr.bin/as/doc/gen.m414
-rw-r--r--gnu/usr.bin/as/doc/h8.m415
-rw-r--r--gnu/usr.bin/as/doc/i80386.m412
-rw-r--r--gnu/usr.bin/as/doc/i960.m416
-rw-r--r--gnu/usr.bin/as/doc/m680x0.m48
-rw-r--r--gnu/usr.bin/as/doc/none.m457
-rw-r--r--gnu/usr.bin/as/doc/pretex.m4268
-rw-r--r--gnu/usr.bin/as/doc/sparc.m48
-rw-r--r--gnu/usr.bin/as/doc/vax.m47
-rw-r--r--gnu/usr.bin/as/doc/vintage.m411
-rw-r--r--gnu/usr.bin/as/expr.c1000
-rw-r--r--gnu/usr.bin/as/expr.h85
-rw-r--r--gnu/usr.bin/as/flo-const.c161
-rw-r--r--gnu/usr.bin/as/flo-copy.c70
-rw-r--r--gnu/usr.bin/as/flonum-const.c157
-rw-r--r--gnu/usr.bin/as/flonum-copy.c76
-rw-r--r--gnu/usr.bin/as/flonum-mult.c203
-rw-r--r--gnu/usr.bin/as/flonum.h125
-rw-r--r--gnu/usr.bin/as/frags.c296
-rw-r--r--gnu/usr.bin/as/frags.h89
-rw-r--r--gnu/usr.bin/as/gas-format.el79
-rw-r--r--gnu/usr.bin/as/hash.c992
-rw-r--r--gnu/usr.bin/as/hash.h65
-rw-r--r--gnu/usr.bin/as/hex-value.c61
-rw-r--r--gnu/usr.bin/as/input-file.c327
-rw-r--r--gnu/usr.bin/as/input-file.h88
-rw-r--r--gnu/usr.bin/as/input-scrub.c436
-rw-r--r--gnu/usr.bin/as/link.cmd10
-rw-r--r--gnu/usr.bin/as/listing.c849
-rw-r--r--gnu/usr.bin/as/listing.h95
-rw-r--r--gnu/usr.bin/as/make-gas.com86
-rw-r--r--gnu/usr.bin/as/makefile.dos593
-rw-r--r--gnu/usr.bin/as/md.h57
-rw-r--r--gnu/usr.bin/as/messages.c417
-rw-r--r--gnu/usr.bin/as/obj.h77
-rw-r--r--gnu/usr.bin/as/objrecdef.h255
-rw-r--r--gnu/usr.bin/as/obstack.c374
-rw-r--r--gnu/usr.bin/as/obstack.h448
-rw-r--r--gnu/usr.bin/as/opcode/ChangeLog56
-rw-r--r--gnu/usr.bin/as/opcode/a29k.h327
-rw-r--r--gnu/usr.bin/as/opcode/h8300.h266
-rw-r--r--gnu/usr.bin/as/opcode/i386.h830
-rw-r--r--gnu/usr.bin/as/opcode/i860.h495
-rw-r--r--gnu/usr.bin/as/opcode/i960.h434
-rw-r--r--gnu/usr.bin/as/opcode/m68k.h1996
-rw-r--r--gnu/usr.bin/as/opcode/m88k.h282
-rw-r--r--gnu/usr.bin/as/opcode/mips.h363
-rw-r--r--gnu/usr.bin/as/opcode/np1.h422
-rw-r--r--gnu/usr.bin/as/opcode/ns32k.h491
-rw-r--r--gnu/usr.bin/as/opcode/pn.h282
-rw-r--r--gnu/usr.bin/as/opcode/pyr.h287
-rw-r--r--gnu/usr.bin/as/opcode/sparc.h871
-rw-r--r--gnu/usr.bin/as/opcode/tahoe.h247
-rw-r--r--gnu/usr.bin/as/opcode/vax.h382
-rw-r--r--gnu/usr.bin/as/output-file.c122
-rw-r--r--gnu/usr.bin/as/output-file.h40
-rw-r--r--gnu/usr.bin/as/read.c2347
-rw-r--r--gnu/usr.bin/as/read.h149
-rw-r--r--gnu/usr.bin/as/struc-symbol.h132
-rw-r--r--gnu/usr.bin/as/subsegs.c308
-rw-r--r--gnu/usr.bin/as/subsegs.h93
-rw-r--r--gnu/usr.bin/as/symbols.c658
-rw-r--r--gnu/usr.bin/as/symbols.h82
-rw-r--r--gnu/usr.bin/as/tc.h112
-rwxr-xr-xgnu/usr.bin/as/testscripts/doboth20
-rwxr-xr-xgnu/usr.bin/as/testscripts/doobjcmp89
-rwxr-xr-xgnu/usr.bin/as/testscripts/dostriptest15
-rwxr-xr-xgnu/usr.bin/as/testscripts/dotest44
-rwxr-xr-xgnu/usr.bin/as/testscripts/dounsortreloc9
-rwxr-xr-xgnu/usr.bin/as/testscripts/dounsortsymbols9
-rw-r--r--gnu/usr.bin/as/version.c30
-rw-r--r--gnu/usr.bin/as/write.c1213
-rw-r--r--gnu/usr.bin/as/write.h120
-rw-r--r--gnu/usr.bin/as/xmalloc.c75
-rw-r--r--gnu/usr.bin/as/xrealloc.c74
-rw-r--r--gnu/usr.bin/awk/ACKNOWLEDGMENT25
-rw-r--r--gnu/usr.bin/awk/COPYING340
-rw-r--r--gnu/usr.bin/awk/FUTURES117
-rw-r--r--gnu/usr.bin/awk/LIMITATIONS16
-rw-r--r--gnu/usr.bin/awk/Makefile13
-rw-r--r--gnu/usr.bin/awk/NEWS1480
-rw-r--r--gnu/usr.bin/awk/PORTS35
-rw-r--r--gnu/usr.bin/awk/POSIX95
-rw-r--r--gnu/usr.bin/awk/PROBLEMS10
-rw-r--r--gnu/usr.bin/awk/README125
-rw-r--r--gnu/usr.bin/awk/array.c515
-rw-r--r--gnu/usr.bin/awk/awk.11969
-rw-r--r--gnu/usr.bin/awk/awk.h790
-rw-r--r--gnu/usr.bin/awk/awk.y1868
-rw-r--r--gnu/usr.bin/awk/builtin.c1239
-rw-r--r--gnu/usr.bin/awk/config.h306
-rw-r--r--gnu/usr.bin/awk/dfa.c2588
-rw-r--r--gnu/usr.bin/awk/dfa.h360
-rw-r--r--gnu/usr.bin/awk/eval.c1260
-rw-r--r--gnu/usr.bin/awk/field.c678
-rw-r--r--gnu/usr.bin/awk/gawk.texi11270
-rw-r--r--gnu/usr.bin/awk/getopt.c757
-rw-r--r--gnu/usr.bin/awk/getopt.h129
-rw-r--r--gnu/usr.bin/awk/getopt1.c187
-rw-r--r--gnu/usr.bin/awk/io.c1282
-rw-r--r--gnu/usr.bin/awk/iop.c321
-rw-r--r--gnu/usr.bin/awk/main.c817
-rw-r--r--gnu/usr.bin/awk/msg.c107
-rw-r--r--gnu/usr.bin/awk/node.c463
-rw-r--r--gnu/usr.bin/awk/patchlevel.h1
-rw-r--r--gnu/usr.bin/awk/protos.h122
-rw-r--r--gnu/usr.bin/awk/re.c212
-rw-r--r--gnu/usr.bin/awk/regex.c5070
-rw-r--r--gnu/usr.bin/awk/regex.h505
-rw-r--r--gnu/usr.bin/awk/version.c47
-rw-r--r--gnu/usr.bin/bc/COPYING341
-rw-r--r--gnu/usr.bin/bc/Makefile6
-rw-r--r--gnu/usr.bin/bc/bc.1730
-rw-r--r--gnu/usr.bin/bc/bc.c1369
-rw-r--r--gnu/usr.bin/bc/bcdefs.h154
-rw-r--r--gnu/usr.bin/bc/config.h4
-rw-r--r--gnu/usr.bin/bc/const.h87
-rw-r--r--gnu/usr.bin/bc/execute.c783
-rw-r--r--gnu/usr.bin/bc/global.c42
-rw-r--r--gnu/usr.bin/bc/global.h108
-rw-r--r--gnu/usr.bin/bc/libmath.b255
-rw-r--r--gnu/usr.bin/bc/load.c333
-rw-r--r--gnu/usr.bin/bc/main.c204
-rw-r--r--gnu/usr.bin/bc/math.h40
-rw-r--r--gnu/usr.bin/bc/number.c1405
-rw-r--r--gnu/usr.bin/bc/number.h60
-rw-r--r--gnu/usr.bin/bc/proto.h165
-rw-r--r--gnu/usr.bin/bc/scan.c1368
-rw-r--r--gnu/usr.bin/bc/storage.c967
-rw-r--r--gnu/usr.bin/bc/util.c794
-rw-r--r--gnu/usr.bin/bc/version.h3
-rw-r--r--gnu/usr.bin/bc/y.tab.h40
-rw-r--r--gnu/usr.bin/binutils/gdb/Makefile72
-rw-r--r--gnu/usr.bin/binutils/gdb/gdb.1371
-rw-r--r--gnu/usr.bin/binutils/gdb/i386/freebsd-nat.c323
-rw-r--r--gnu/usr.bin/binutils/gdb/i386/nm.h44
-rw-r--r--gnu/usr.bin/binutils/gdb/i386/tm.h76
-rw-r--r--gnu/usr.bin/binutils/gdb/i386/version.c3
-rw-r--r--gnu/usr.bin/binutils/gdb/i386/xm.h31
-rw-r--r--gnu/usr.bin/cc/Makefile8
-rw-r--r--gnu/usr.bin/cc/Makefile.inc22
-rw-r--r--gnu/usr.bin/cc/README16
-rw-r--r--gnu/usr.bin/cc/c++/Makefile12
-rw-r--r--gnu/usr.bin/cc/c++/g++.c535
-rw-r--r--gnu/usr.bin/cc/cc/Makefile11
-rw-r--r--gnu/usr.bin/cc/cc/cc.14111
-rw-r--r--gnu/usr.bin/cc/cc/gcc.c4896
-rw-r--r--gnu/usr.bin/cc/cc1/Makefile12
-rw-r--r--gnu/usr.bin/cc/cc1/c-aux-info.c639
-rw-r--r--gnu/usr.bin/cc/cc1/c-convert.c95
-rw-r--r--gnu/usr.bin/cc/cc1/c-decl.c6797
-rw-r--r--gnu/usr.bin/cc/cc1/c-iterate.c595
-rw-r--r--gnu/usr.bin/cc/cc1/c-lang.c129
-rw-r--r--gnu/usr.bin/cc/cc1/c-lex.c1983
-rw-r--r--gnu/usr.bin/cc/cc1/c-parse.c3530
-rw-r--r--gnu/usr.bin/cc/cc1/c-pragma.c188
-rw-r--r--gnu/usr.bin/cc/cc1/c-typeck.c6384
-rw-r--r--gnu/usr.bin/cc/cc1plus/Makefile12
-rw-r--r--gnu/usr.bin/cc/cc1plus/call.c2909
-rw-r--r--gnu/usr.bin/cc/cc1plus/class.c4940
-rw-r--r--gnu/usr.bin/cc/cc1plus/class.h116
-rw-r--r--gnu/usr.bin/cc/cc1plus/cp-tree.h2373
-rw-r--r--gnu/usr.bin/cc/cc1plus/cvt.c2044
-rw-r--r--gnu/usr.bin/cc/cc1plus/decl.c12030
-rw-r--r--gnu/usr.bin/cc/cc1plus/decl.h54
-rw-r--r--gnu/usr.bin/cc/cc1plus/decl2.c3102
-rw-r--r--gnu/usr.bin/cc/cc1plus/edsel.c927
-rw-r--r--gnu/usr.bin/cc/cc1plus/errfn.c217
-rw-r--r--gnu/usr.bin/cc/cc1plus/error.c1404
-rw-r--r--gnu/usr.bin/cc/cc1plus/except.c1481
-rw-r--r--gnu/usr.bin/cc/cc1plus/expr.c275
-rw-r--r--gnu/usr.bin/cc/cc1plus/gc.c988
-rw-r--r--gnu/usr.bin/cc/cc1plus/hash.h197
-rw-r--r--gnu/usr.bin/cc/cc1plus/init.c4077
-rw-r--r--gnu/usr.bin/cc/cc1plus/input.c184
-rw-r--r--gnu/usr.bin/cc/cc1plus/lex.c4818
-rw-r--r--gnu/usr.bin/cc/cc1plus/lex.h130
-rw-r--r--gnu/usr.bin/cc/cc1plus/method.c1948
-rw-r--r--gnu/usr.bin/cc/cc1plus/parse.c7604
-rw-r--r--gnu/usr.bin/cc/cc1plus/parse.h84
-rw-r--r--gnu/usr.bin/cc/cc1plus/pt.c2465
-rw-r--r--gnu/usr.bin/cc/cc1plus/ptree.c167
-rw-r--r--gnu/usr.bin/cc/cc1plus/search.c3199
-rw-r--r--gnu/usr.bin/cc/cc1plus/sig.c1023
-rw-r--r--gnu/usr.bin/cc/cc1plus/spew.c436
-rw-r--r--gnu/usr.bin/cc/cc1plus/tree.c1763
-rw-r--r--gnu/usr.bin/cc/cc1plus/tree.def103
-rw-r--r--gnu/usr.bin/cc/cc1plus/typeck.c7233
-rw-r--r--gnu/usr.bin/cc/cc1plus/typeck2.c1607
-rw-r--r--gnu/usr.bin/cc/cc1plus/xref.c839
-rw-r--r--gnu/usr.bin/cc/cc_int/Makefile12
-rw-r--r--gnu/usr.bin/cc/cc_int/aux-output.c2138
-rw-r--r--gnu/usr.bin/cc/cc_int/bc-emit.c991
-rw-r--r--gnu/usr.bin/cc/cc_int/bc-optab.c788
-rw-r--r--gnu/usr.bin/cc/cc_int/c-common.c1997
-rw-r--r--gnu/usr.bin/cc/cc_int/caller-save.c762
-rw-r--r--gnu/usr.bin/cc/cc_int/calls.c3061
-rw-r--r--gnu/usr.bin/cc/cc_int/combine.c10790
-rw-r--r--gnu/usr.bin/cc/cc_int/convert.c460
-rw-r--r--gnu/usr.bin/cc/cc_int/cse.c8546
-rw-r--r--gnu/usr.bin/cc/cc_int/dbxout.c2585
-rw-r--r--gnu/usr.bin/cc/cc_int/dwarfout.c5667
-rw-r--r--gnu/usr.bin/cc/cc_int/emit-rtl.c3359
-rw-r--r--gnu/usr.bin/cc/cc_int/explow.c1152
-rw-r--r--gnu/usr.bin/cc/cc_int/expmed.c3957
-rw-r--r--gnu/usr.bin/cc/cc_int/expr.c10192
-rw-r--r--gnu/usr.bin/cc/cc_int/final.c3069
-rw-r--r--gnu/usr.bin/cc/cc_int/flow.c2793
-rw-r--r--gnu/usr.bin/cc/cc_int/fold-const.c4889
-rw-r--r--gnu/usr.bin/cc/cc_int/function.c5496
-rw-r--r--gnu/usr.bin/cc/cc_int/getpwd.c94
-rw-r--r--gnu/usr.bin/cc/cc_int/global.c1680
-rw-r--r--gnu/usr.bin/cc/cc_int/insn-attrtab.c14
-rw-r--r--gnu/usr.bin/cc/cc_int/insn-emit.c3973
-rw-r--r--gnu/usr.bin/cc/cc_int/insn-extract.c533
-rw-r--r--gnu/usr.bin/cc/cc_int/insn-opinit.c216
-rw-r--r--gnu/usr.bin/cc/cc_int/insn-output.c6865
-rw-r--r--gnu/usr.bin/cc/cc_int/insn-peep.c28
-rw-r--r--gnu/usr.bin/cc/cc_int/insn-recog.c7138
-rw-r--r--gnu/usr.bin/cc/cc_int/integrate.c3035
-rw-r--r--gnu/usr.bin/cc/cc_int/jump.c4395
-rw-r--r--gnu/usr.bin/cc/cc_int/local-alloc.c2355
-rw-r--r--gnu/usr.bin/cc/cc_int/loop.c6587
-rw-r--r--gnu/usr.bin/cc/cc_int/obstack.c485
-rw-r--r--gnu/usr.bin/cc/cc_int/optabs.c4100
-rw-r--r--gnu/usr.bin/cc/cc_int/print-rtl.c328
-rw-r--r--gnu/usr.bin/cc/cc_int/print-tree.c642
-rw-r--r--gnu/usr.bin/cc/cc_int/real.c5969
-rw-r--r--gnu/usr.bin/cc/cc_int/recog.c1970
-rw-r--r--gnu/usr.bin/cc/cc_int/reg-stack.c3008
-rw-r--r--gnu/usr.bin/cc/cc_int/regclass.c1856
-rw-r--r--gnu/usr.bin/cc/cc_int/reload.c5650
-rw-r--r--gnu/usr.bin/cc/cc_int/reload1.c7122
-rw-r--r--gnu/usr.bin/cc/cc_int/reorg.c4281
-rw-r--r--gnu/usr.bin/cc/cc_int/rtl.c850
-rw-r--r--gnu/usr.bin/cc/cc_int/rtlanal.c1835
-rw-r--r--gnu/usr.bin/cc/cc_int/sched.c4884
-rw-r--r--gnu/usr.bin/cc/cc_int/sdbout.c1530
-rw-r--r--gnu/usr.bin/cc/cc_int/stmt.c5431
-rw-r--r--gnu/usr.bin/cc/cc_int/stor-layout.c1176
-rw-r--r--gnu/usr.bin/cc/cc_int/stupid.c518
-rw-r--r--gnu/usr.bin/cc/cc_int/toplev.c4061
-rw-r--r--gnu/usr.bin/cc/cc_int/tree.c3996
-rw-r--r--gnu/usr.bin/cc/cc_int/unroll.c3345
-rw-r--r--gnu/usr.bin/cc/cc_int/varasm.c3883
-rw-r--r--gnu/usr.bin/cc/cc_int/version.c1
-rw-r--r--gnu/usr.bin/cc/cc_int/xcoffout.c536
-rw-r--r--gnu/usr.bin/cc/cccp/Makefile11
-rw-r--r--gnu/usr.bin/cc/cpp/Makefile11
-rw-r--r--gnu/usr.bin/cc/cpp/cccp.c9804
-rw-r--r--gnu/usr.bin/cc/cpp/cexp.c1926
-rw-r--r--gnu/usr.bin/cc/cpp/cpp.11
-rw-r--r--gnu/usr.bin/cc/include/basic-block.h68
-rw-r--r--gnu/usr.bin/cc/include/bc-arity.h232
-rw-r--r--gnu/usr.bin/cc/include/bc-emit.h133
-rw-r--r--gnu/usr.bin/cc/include/bc-opcode.h238
-rw-r--r--gnu/usr.bin/cc/include/bc-optab.h74
-rw-r--r--gnu/usr.bin/cc/include/bc-typecd.def21
-rw-r--r--gnu/usr.bin/cc/include/bc-typecd.h53
-rw-r--r--gnu/usr.bin/cc/include/bi-run.h165
-rw-r--r--gnu/usr.bin/cc/include/bytecode.h91
-rw-r--r--gnu/usr.bin/cc/include/bytetypes.h35
-rw-r--r--gnu/usr.bin/cc/include/c-gperf.h184
-rw-r--r--gnu/usr.bin/cc/include/c-lex.h79
-rw-r--r--gnu/usr.bin/cc/include/c-parse.h65
-rw-r--r--gnu/usr.bin/cc/include/c-tree.h483
-rw-r--r--gnu/usr.bin/cc/include/conditions.h115
-rw-r--r--gnu/usr.bin/cc/include/config.h42
-rw-r--r--gnu/usr.bin/cc/include/convert.h23
-rw-r--r--gnu/usr.bin/cc/include/defaults.h133
-rw-r--r--gnu/usr.bin/cc/include/expr.h834
-rw-r--r--gnu/usr.bin/cc/include/flags.h359
-rw-r--r--gnu/usr.bin/cc/include/function.h216
-rw-r--r--gnu/usr.bin/cc/include/gbl-ctors.h80
-rw-r--r--gnu/usr.bin/cc/include/glimits.h93
-rw-r--r--gnu/usr.bin/cc/include/hard-reg-set.h270
-rw-r--r--gnu/usr.bin/cc/include/i386/bsd.h129
-rw-r--r--gnu/usr.bin/cc/include/i386/gas.h154
-rw-r--r--gnu/usr.bin/cc/include/i386/gstabs.h9
-rw-r--r--gnu/usr.bin/cc/include/i386/i386.h1665
-rw-r--r--gnu/usr.bin/cc/include/i386/perform.h97
-rw-r--r--gnu/usr.bin/cc/include/i386/unix.h145
-rw-r--r--gnu/usr.bin/cc/include/input.h46
-rw-r--r--gnu/usr.bin/cc/include/insn-attr.h19
-rw-r--r--gnu/usr.bin/cc/include/insn-codes.h201
-rw-r--r--gnu/usr.bin/cc/include/insn-config.h12
-rw-r--r--gnu/usr.bin/cc/include/insn-flags.h598
-rw-r--r--gnu/usr.bin/cc/include/integrate.h125
-rw-r--r--gnu/usr.bin/cc/include/longlong.h1185
-rw-r--r--gnu/usr.bin/cc/include/loop.h169
-rw-r--r--gnu/usr.bin/cc/include/machmode.def118
-rw-r--r--gnu/usr.bin/cc/include/machmode.h169
-rw-r--r--gnu/usr.bin/cc/include/modemap.def30
-rw-r--r--gnu/usr.bin/cc/include/multilib.h3
-rw-r--r--gnu/usr.bin/cc/include/obstack.h513
-rw-r--r--gnu/usr.bin/cc/include/output.h241
-rw-r--r--gnu/usr.bin/cc/include/pcp.h100
-rw-r--r--gnu/usr.bin/cc/include/real.h437
-rw-r--r--gnu/usr.bin/cc/include/recog.h120
-rw-r--r--gnu/usr.bin/cc/include/regs.h168
-rw-r--r--gnu/usr.bin/cc/include/reload.h235
-rw-r--r--gnu/usr.bin/cc/include/rtl.def764
-rw-r--r--gnu/usr.bin/cc/include/rtl.h957
-rw-r--r--gnu/usr.bin/cc/include/stack.h41
-rw-r--r--gnu/usr.bin/cc/include/tconfig.h42
-rw-r--r--gnu/usr.bin/cc/include/tm.h329
-rw-r--r--gnu/usr.bin/cc/include/tree.def695
-rw-r--r--gnu/usr.bin/cc/include/tree.h1638
-rw-r--r--gnu/usr.bin/cc/include/typeclass.h14
-rw-r--r--gnu/usr.bin/cc/legal/gen-protos.c155
-rw-r--r--gnu/usr.bin/cc/legal/md5295
-rw-r--r--gnu/usr.bin/cc/libgcc/Makefile46
-rw-r--r--gnu/usr.bin/cc/libgcc/libgcc1.c608
-rw-r--r--gnu/usr.bin/cc/libgcc/libgcc2.c2151
-rw-r--r--gnu/usr.bin/cpio/Makefile9
-rw-r--r--gnu/usr.bin/cpio/cpio.16
-rw-r--r--gnu/usr.bin/cpio/fnmatch.c200
-rw-r--r--gnu/usr.bin/cpio/fnmatch.h60
-rw-r--r--gnu/usr.bin/cpio/mt.1107
-rw-r--r--gnu/usr.bin/cpio/rmt.c281
-rw-r--r--gnu/usr.bin/cvs/Makefile3
-rw-r--r--gnu/usr.bin/cvs/Makefile.inc8
-rw-r--r--gnu/usr.bin/cvs/contrib/README68
-rw-r--r--gnu/usr.bin/cvs/contrib/cln_hist.pl91
-rw-r--r--gnu/usr.bin/cvs/contrib/commit_prep.pl168
-rw-r--r--gnu/usr.bin/cvs/contrib/cvs_acls.pl142
-rw-r--r--gnu/usr.bin/cvs/contrib/cvscheck84
-rw-r--r--gnu/usr.bin/cvs/contrib/cvscheck.man53
-rw-r--r--gnu/usr.bin/cvs/contrib/cvshelp.man562
-rw-r--r--gnu/usr.bin/cvs/contrib/descend116
-rw-r--r--gnu/usr.bin/cvs/contrib/descend.man115
-rw-r--r--gnu/usr.bin/cvs/contrib/dirfns481
-rw-r--r--gnu/usr.bin/cvs/contrib/log.pl104
-rw-r--r--gnu/usr.bin/cvs/contrib/log_accum.pl331
-rw-r--r--gnu/usr.bin/cvs/contrib/mfpipe.pl87
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/ChangeLog119
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/INSTALL83
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/Makefile78
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/README14
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/compile-all.el52
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/cookie.el884
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/elib-dll-debug.el298
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/elib-dll.el386
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/elib-node.el89
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs-startup.el6
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.el1476
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.info1367
-rw-r--r--gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.texinfo1437
-rw-r--r--gnu/usr.bin/cvs/contrib/rcs-to-cvs208
-rw-r--r--gnu/usr.bin/cvs/contrib/rcslock.pl234
-rw-r--r--gnu/usr.bin/cvs/contrib/sccs2rcs277
-rw-r--r--gnu/usr.bin/cvs/cvs/Makefile33
-rw-r--r--gnu/usr.bin/cvs/cvs/add.c447
-rw-r--r--gnu/usr.bin/cvs/cvs/admin.c124
-rw-r--r--gnu/usr.bin/cvs/cvs/checkin.c142
-rw-r--r--gnu/usr.bin/cvs/cvs/checkout.c730
-rw-r--r--gnu/usr.bin/cvs/cvs/classify.c380
-rw-r--r--gnu/usr.bin/cvs/cvs/commit.c1229
-rw-r--r--gnu/usr.bin/cvs/cvs/config.h217
-rw-r--r--gnu/usr.bin/cvs/cvs/create_adm.c100
-rw-r--r--gnu/usr.bin/cvs/cvs/cvs.11991
-rw-r--r--gnu/usr.bin/cvs/cvs/cvs.5326
-rw-r--r--gnu/usr.bin/cvs/cvs/cvs.h442
-rw-r--r--gnu/usr.bin/cvs/cvs/diff.c429
-rw-r--r--gnu/usr.bin/cvs/cvs/entries.c488
-rw-r--r--gnu/usr.bin/cvs/cvs/find_names.c272
-rw-r--r--gnu/usr.bin/cvs/cvs/history.c1373
-rw-r--r--gnu/usr.bin/cvs/cvs/ignore.c227
-rw-r--r--gnu/usr.bin/cvs/cvs/import.c1011
-rw-r--r--gnu/usr.bin/cvs/cvs/lock.c522
-rw-r--r--gnu/usr.bin/cvs/cvs/log.c132
-rw-r--r--gnu/usr.bin/cvs/cvs/logmsg.c449
-rw-r--r--gnu/usr.bin/cvs/cvs/main.c460
-rw-r--r--gnu/usr.bin/cvs/cvs/modules.c810
-rw-r--r--gnu/usr.bin/cvs/cvs/no_diff.c85
-rw-r--r--gnu/usr.bin/cvs/cvs/parseinfo.c147
-rw-r--r--gnu/usr.bin/cvs/cvs/patch.c541
-rw-r--r--gnu/usr.bin/cvs/cvs/patchlevel.h1
-rw-r--r--gnu/usr.bin/cvs/cvs/rcs.c1449
-rw-r--r--gnu/usr.bin/cvs/cvs/rcs.h102
-rw-r--r--gnu/usr.bin/cvs/cvs/recurse.c535
-rw-r--r--gnu/usr.bin/cvs/cvs/release.c223
-rw-r--r--gnu/usr.bin/cvs/cvs/remove.c176
-rw-r--r--gnu/usr.bin/cvs/cvs/repos.c169
-rw-r--r--gnu/usr.bin/cvs/cvs/rtag.c403
-rw-r--r--gnu/usr.bin/cvs/cvs/status.c230
-rw-r--r--gnu/usr.bin/cvs/cvs/tag.c263
-rw-r--r--gnu/usr.bin/cvs/cvs/update.c1076
-rw-r--r--gnu/usr.bin/cvs/cvs/vers_ts.c224
-rw-r--r--gnu/usr.bin/cvs/cvs/version.c11
-rw-r--r--gnu/usr.bin/cvs/doc/cvs.ms1073
-rw-r--r--gnu/usr.bin/cvs/examples/commitinfo21
-rw-r--r--gnu/usr.bin/cvs/examples/editinfo30
-rw-r--r--gnu/usr.bin/cvs/examples/loginfo20
-rw-r--r--gnu/usr.bin/cvs/examples/modules566
-rw-r--r--gnu/usr.bin/cvs/examples/rcsinfo18
-rw-r--r--gnu/usr.bin/cvs/lib/Makefile13
-rw-r--r--gnu/usr.bin/cvs/lib/Makefile.in91
-rw-r--r--gnu/usr.bin/cvs/lib/alloca.c191
-rw-r--r--gnu/usr.bin/cvs/lib/argmatch.c83
-rw-r--r--gnu/usr.bin/cvs/lib/dup2.c36
-rw-r--r--gnu/usr.bin/cvs/lib/error.c193
-rw-r--r--gnu/usr.bin/cvs/lib/fnmatch.c183
-rw-r--r--gnu/usr.bin/cvs/lib/fnmatch.h45
-rw-r--r--gnu/usr.bin/cvs/lib/ftruncate.c72
-rw-r--r--gnu/usr.bin/cvs/lib/getdate.y889
-rw-r--r--gnu/usr.bin/cvs/lib/getopt.c604
-rw-r--r--gnu/usr.bin/cvs/lib/getopt.h102
-rw-r--r--gnu/usr.bin/cvs/lib/getopt1.c166
-rw-r--r--gnu/usr.bin/cvs/lib/getwd.c31
-rw-r--r--gnu/usr.bin/cvs/lib/hash.c338
-rw-r--r--gnu/usr.bin/cvs/lib/hash.h77
-rw-r--r--gnu/usr.bin/cvs/lib/mkdir.c125
-rw-r--r--gnu/usr.bin/cvs/lib/myndbm.c212
-rw-r--r--gnu/usr.bin/cvs/lib/myndbm.h44
-rw-r--r--gnu/usr.bin/cvs/lib/regex.c4867
-rw-r--r--gnu/usr.bin/cvs/lib/regex.h479
-rw-r--r--gnu/usr.bin/cvs/lib/rename.c68
-rw-r--r--gnu/usr.bin/cvs/lib/sighandle.c412
-rw-r--r--gnu/usr.bin/cvs/lib/strdup.c39
-rw-r--r--gnu/usr.bin/cvs/lib/strippath.c74
-rw-r--r--gnu/usr.bin/cvs/lib/stripslash.c35
-rw-r--r--gnu/usr.bin/cvs/lib/subr.c912
-rw-r--r--gnu/usr.bin/cvs/lib/system.h223
-rw-r--r--gnu/usr.bin/cvs/lib/wait.h29
-rw-r--r--gnu/usr.bin/cvs/lib/y.tab.h18
-rw-r--r--gnu/usr.bin/cvs/lib/yesno.c37
-rw-r--r--gnu/usr.bin/cvs/mkmodules/Makefile9
-rw-r--r--gnu/usr.bin/cvs/mkmodules/mkmodules.165
-rw-r--r--gnu/usr.bin/cvs/mkmodules/mkmodules.c395
-rw-r--r--gnu/usr.bin/cvs/mkmodules/xxx5320
-rw-r--r--gnu/usr.bin/dc/COPYING339
-rw-r--r--gnu/usr.bin/dc/ChangeLog77
-rw-r--r--gnu/usr.bin/dc/Makefile7
-rw-r--r--gnu/usr.bin/dc/NEWS7
-rw-r--r--gnu/usr.bin/dc/README13
-rw-r--r--gnu/usr.bin/dc/dc.1278
-rw-r--r--gnu/usr.bin/dc/dc.c908
-rw-r--r--gnu/usr.bin/dc/dc.info330
-rw-r--r--gnu/usr.bin/dc/dc.texinfo381
-rw-r--r--gnu/usr.bin/dc/decimal.c1235
-rw-r--r--gnu/usr.bin/dc/decimal.h93
-rw-r--r--gnu/usr.bin/diff/Makefile9
-rw-r--r--gnu/usr.bin/diff/analyze.c548
-rw-r--r--gnu/usr.bin/diff/context.c58
-rw-r--r--gnu/usr.bin/diff/diff.1473
-rw-r--r--gnu/usr.bin/diff/diff.c374
-rw-r--r--gnu/usr.bin/diff/diff.h151
-rw-r--r--gnu/usr.bin/diff/diff3.c366
-rw-r--r--gnu/usr.bin/diff/dir.c85
-rw-r--r--gnu/usr.bin/diff/ed.c13
-rw-r--r--gnu/usr.bin/diff/getopt.c108
-rw-r--r--gnu/usr.bin/diff/getopt1.c13
-rw-r--r--gnu/usr.bin/diff/ifdef.c401
-rw-r--r--gnu/usr.bin/diff/io.c188
-rw-r--r--gnu/usr.bin/diff/normal.c9
-rw-r--r--gnu/usr.bin/diff/regex.c892
-rw-r--r--gnu/usr.bin/diff/regex.h2
-rw-r--r--gnu/usr.bin/diff/sdiff.c434
-rw-r--r--gnu/usr.bin/diff/side.c39
-rw-r--r--gnu/usr.bin/diff/system.h146
-rw-r--r--gnu/usr.bin/diff/util.c263
-rw-r--r--gnu/usr.bin/diff/version.c4
-rw-r--r--gnu/usr.bin/diff3/Makefile13
-rw-r--r--gnu/usr.bin/diff3/diff3.1207
-rw-r--r--gnu/usr.bin/gdb/COPYING339
-rw-r--r--gnu/usr.bin/gdb/COPYING.LIB481
-rw-r--r--gnu/usr.bin/gdb/ChangeLog4887
-rw-r--r--gnu/usr.bin/gdb/Gdbinit15
-rw-r--r--gnu/usr.bin/gdb/Makefile3
-rw-r--r--gnu/usr.bin/gdb/Makefile.dist371
-rw-r--r--gnu/usr.bin/gdb/Projects114
-rw-r--r--gnu/usr.bin/gdb/README.FreeBSD15
-rw-r--r--gnu/usr.bin/gdb/README.gnu142
-rw-r--r--gnu/usr.bin/gdb/VERSION1
-rw-r--r--gnu/usr.bin/gdb/XGdbinit.samp15
-rw-r--r--gnu/usr.bin/gdb/Xgdb.ad8
-rw-r--r--gnu/usr.bin/gdb/bfd/COPYING339
-rw-r--r--gnu/usr.bin/gdb/bfd/Makefile17
-rw-r--r--gnu/usr.bin/gdb/bfd/README.FreeBSD7
-rw-r--r--gnu/usr.bin/gdb/bfd/VERSION1
-rw-r--r--gnu/usr.bin/gdb/bfd/aout-target.h429
-rw-r--r--gnu/usr.bin/gdb/bfd/aout32.c23
-rw-r--r--gnu/usr.bin/gdb/bfd/aoutx.h2568
-rw-r--r--gnu/usr.bin/gdb/bfd/archive.c1770
-rw-r--r--gnu/usr.bin/gdb/bfd/archures.c731
-rw-r--r--gnu/usr.bin/gdb/bfd/bfd.c733
-rw-r--r--gnu/usr.bin/gdb/bfd/bfd.h1803
-rw-r--r--gnu/usr.bin/gdb/bfd/cache.c311
-rw-r--r--gnu/usr.bin/gdb/bfd/coffgen.c1519
-rw-r--r--gnu/usr.bin/gdb/bfd/core.c106
-rw-r--r--gnu/usr.bin/gdb/bfd/cpu-i386.c43
-rw-r--r--gnu/usr.bin/gdb/bfd/ctor.c148
-rw-r--r--gnu/usr.bin/gdb/bfd/ecoff.c3994
-rw-r--r--gnu/usr.bin/gdb/bfd/elf.c248
-rw-r--r--gnu/usr.bin/gdb/bfd/format.c258
-rw-r--r--gnu/usr.bin/gdb/bfd/freebsd386.c110
-rw-r--r--gnu/usr.bin/gdb/bfd/init.c78
-rw-r--r--gnu/usr.bin/gdb/bfd/libaout.h393
-rw-r--r--gnu/usr.bin/gdb/bfd/libbfd.c850
-rw-r--r--gnu/usr.bin/gdb/bfd/libbfd.h273
-rw-r--r--gnu/usr.bin/gdb/bfd/libcoff.h352
-rw-r--r--gnu/usr.bin/gdb/bfd/libecoff.h265
-rw-r--r--gnu/usr.bin/gdb/bfd/libelf.h249
-rw-r--r--gnu/usr.bin/gdb/bfd/opncls.c534
-rw-r--r--gnu/usr.bin/gdb/bfd/reloc.c1225
-rw-r--r--gnu/usr.bin/gdb/bfd/seclet.c185
-rw-r--r--gnu/usr.bin/gdb/bfd/seclet.h55
-rw-r--r--gnu/usr.bin/gdb/bfd/section.c899
-rw-r--r--gnu/usr.bin/gdb/bfd/srec.c1001
-rw-r--r--gnu/usr.bin/gdb/bfd/stab-syms.c59
-rw-r--r--gnu/usr.bin/gdb/bfd/syms.c527
-rw-r--r--gnu/usr.bin/gdb/bfd/sysdep.h47
-rw-r--r--gnu/usr.bin/gdb/bfd/targets.c635
-rw-r--r--gnu/usr.bin/gdb/bfd/trad-core.c384
-rw-r--r--gnu/usr.bin/gdb/blockframe.c622
-rw-r--r--gnu/usr.bin/gdb/breakpoint.c1383
-rw-r--r--gnu/usr.bin/gdb/command.c856
-rw-r--r--gnu/usr.bin/gdb/command.h77
-rw-r--r--gnu/usr.bin/gdb/config/Makefile.i3866
-rw-r--r--gnu/usr.bin/gdb/config/default-dep.c585
-rw-r--r--gnu/usr.bin/gdb/config/i386-dep.c1275
-rw-r--r--gnu/usr.bin/gdb/config/i386-pinsn.c1812
-rw-r--r--gnu/usr.bin/gdb/config/i386bsd-dep.c1889
-rw-r--r--gnu/usr.bin/gdb/config/m-i386-sv32.h28
-rw-r--r--gnu/usr.bin/gdb/config/m-i386.h394
-rw-r--r--gnu/usr.bin/gdb/config/m-i386bsd.h375
-rw-r--r--gnu/usr.bin/gdb/config/m-i386g-sv32.h28
-rw-r--r--gnu/usr.bin/gdb/config/m-i386gas.h37
-rw-r--r--gnu/usr.bin/gdb/copying.c215
-rw-r--r--gnu/usr.bin/gdb/core.c581
-rw-r--r--gnu/usr.bin/gdb/cplus-dem.c996
-rw-r--r--gnu/usr.bin/gdb/dbxread.c5727
-rw-r--r--gnu/usr.bin/gdb/defs.h122
-rw-r--r--gnu/usr.bin/gdb/doc/ChangeLog783
-rw-r--r--gnu/usr.bin/gdb/doc/Makefile340
-rw-r--r--gnu/usr.bin/gdb/doc/Makefile.in327
-rw-r--r--gnu/usr.bin/gdb/doc/a4rc.sed11
-rw-r--r--gnu/usr.bin/gdb/doc/all-cfg.texi117
-rwxr-xr-xgnu/usr.bin/gdb/doc/config.status5
-rw-r--r--gnu/usr.bin/gdb/doc/configure.in7
-rw-r--r--gnu/usr.bin/gdb/doc/gdb-cfg.texi117
-rw-r--r--gnu/usr.bin/gdb/doc/gdb.info213
-rw-r--r--gnu/usr.bin/gdb/doc/gdb.info-11304
-rw-r--r--gnu/usr.bin/gdb/doc/gdb.info-21165
-rw-r--r--gnu/usr.bin/gdb/doc/gdb.info-31264
-rw-r--r--gnu/usr.bin/gdb/doc/gdb.info-41349
-rw-r--r--gnu/usr.bin/gdb/doc/gdb.info-51215
-rw-r--r--gnu/usr.bin/gdb/doc/gdb.info-61220
-rw-r--r--gnu/usr.bin/gdb/doc/gdb.info-71233
-rw-r--r--gnu/usr.bin/gdb/doc/gdb.info-8657
-rw-r--r--gnu/usr.bin/gdb/doc/gdb.texinfo8591
-rw-r--r--gnu/usr.bin/gdb/doc/gdbint.texinfo2658
-rw-r--r--gnu/usr.bin/gdb/doc/h8-cfg.texi47
-rw-r--r--gnu/usr.bin/gdb/doc/libgdb.texinfo1471
-rw-r--r--gnu/usr.bin/gdb/doc/lpsrc.sed13
-rw-r--r--gnu/usr.bin/gdb/doc/psrc.sed13
-rw-r--r--gnu/usr.bin/gdb/doc/refcard.ps798
-rw-r--r--gnu/usr.bin/gdb/doc/refcard.tex646
-rw-r--r--gnu/usr.bin/gdb/doc/remote.texi1294
-rw-r--r--gnu/usr.bin/gdb/doc/stabs.texinfo3795
-rw-r--r--gnu/usr.bin/gdb/environ.c185
-rw-r--r--gnu/usr.bin/gdb/environ.h39
-rw-r--r--gnu/usr.bin/gdb/eval.c1065
-rw-r--r--gnu/usr.bin/gdb/expprint.c324
-rw-r--r--gnu/usr.bin/gdb/expread.y1782
-rw-r--r--gnu/usr.bin/gdb/expression.h191
-rw-r--r--gnu/usr.bin/gdb/findvar.c579
-rw-r--r--gnu/usr.bin/gdb/frame.h115
-rw-r--r--gnu/usr.bin/gdb/gdb.13
-rw-r--r--gnu/usr.bin/gdb/gdb/COPYING339
-rw-r--r--gnu/usr.bin/gdb/gdb/Makefile72
-rw-r--r--gnu/usr.bin/gdb/gdb/ansidecl.h139
-rw-r--r--gnu/usr.bin/gdb/gdb/aout/aout64.h431
-rw-r--r--gnu/usr.bin/gdb/gdb/aout/ar.h32
-rw-r--r--gnu/usr.bin/gdb/gdb/aout/ranlib.h62
-rw-r--r--gnu/usr.bin/gdb/gdb/aout/stab.def264
-rw-r--r--gnu/usr.bin/gdb/gdb/aout/stab_gnu.h36
-rw-r--r--gnu/usr.bin/gdb/gdb/blockframe.c821
-rw-r--r--gnu/usr.bin/gdb/gdb/breakpoint.c3301
-rw-r--r--gnu/usr.bin/gdb/gdb/breakpoint.h372
-rw-r--r--gnu/usr.bin/gdb/gdb/buildsym.c950
-rw-r--r--gnu/usr.bin/gdb/gdb/buildsym.h259
-rw-r--r--gnu/usr.bin/gdb/gdb/c-exp.tab.c2648
-rw-r--r--gnu/usr.bin/gdb/gdb/c-exp.y1601
-rw-r--r--gnu/usr.bin/gdb/gdb/c-lang.c447
-rw-r--r--gnu/usr.bin/gdb/gdb/c-lang.h31
-rw-r--r--gnu/usr.bin/gdb/gdb/c-typeprint.c797
-rw-r--r--gnu/usr.bin/gdb/gdb/c-valprint.c416
-rw-r--r--gnu/usr.bin/gdb/gdb/call-cmds.h28
-rw-r--r--gnu/usr.bin/gdb/gdb/ch-exp.tab.c2854
-rw-r--r--gnu/usr.bin/gdb/gdb/ch-exp.y1997
-rw-r--r--gnu/usr.bin/gdb/gdb/ch-lang.c341
-rw-r--r--gnu/usr.bin/gdb/gdb/ch-lang.h31
-rw-r--r--gnu/usr.bin/gdb/gdb/ch-typeprint.c225
-rw-r--r--gnu/usr.bin/gdb/gdb/ch-valprint.c332
-rw-r--r--gnu/usr.bin/gdb/gdb/coff/ecoff.h262
-rw-r--r--gnu/usr.bin/gdb/gdb/coff/internal.h538
-rw-r--r--gnu/usr.bin/gdb/gdb/coff/sym.h477
-rw-r--r--gnu/usr.bin/gdb/gdb/coff/symconst.h175
-rw-r--r--gnu/usr.bin/gdb/gdb/coffread.c2071
-rw-r--r--gnu/usr.bin/gdb/gdb/command.c1310
-rw-r--r--gnu/usr.bin/gdb/gdb/command.h241
-rw-r--r--gnu/usr.bin/gdb/gdb/complaints.c158
-rw-r--r--gnu/usr.bin/gdb/gdb/complaints.h46
-rw-r--r--gnu/usr.bin/gdb/gdb/copying.c327
-rw-r--r--gnu/usr.bin/gdb/gdb/core.c291
-rw-r--r--gnu/usr.bin/gdb/gdb/coredep.c118
-rw-r--r--gnu/usr.bin/gdb/gdb/corelow.c328
-rw-r--r--gnu/usr.bin/gdb/gdb/cp-valprint.c484
-rw-r--r--gnu/usr.bin/gdb/gdb/dbxread.c2234
-rw-r--r--gnu/usr.bin/gdb/gdb/dcache.c236
-rw-r--r--gnu/usr.bin/gdb/gdb/dcache.h83
-rw-r--r--gnu/usr.bin/gdb/gdb/defs.h901
-rw-r--r--gnu/usr.bin/gdb/gdb/demangle.c190
-rw-r--r--gnu/usr.bin/gdb/gdb/demangle.h77
-rw-r--r--gnu/usr.bin/gdb/gdb/dis-asm.h176
-rw-r--r--gnu/usr.bin/gdb/gdb/dis-buf.c69
-rw-r--r--gnu/usr.bin/gdb/gdb/dwarfread.c3866
-rw-r--r--gnu/usr.bin/gdb/gdb/elf/common.h216
-rw-r--r--gnu/usr.bin/gdb/gdb/elf/dwarf.h314
-rw-r--r--gnu/usr.bin/gdb/gdb/elf/external.h190
-rw-r--r--gnu/usr.bin/gdb/gdb/elf/internal.h195
-rw-r--r--gnu/usr.bin/gdb/gdb/elfread.c729
-rw-r--r--gnu/usr.bin/gdb/gdb/environ.c198
-rw-r--r--gnu/usr.bin/gdb/gdb/environ.h58
-rw-r--r--gnu/usr.bin/gdb/gdb/eval.c1213
-rw-r--r--gnu/usr.bin/gdb/gdb/exec.c489
-rw-r--r--gnu/usr.bin/gdb/gdb/expprint.c623
-rw-r--r--gnu/usr.bin/gdb/gdb/expression.h313
-rw-r--r--gnu/usr.bin/gdb/gdb/findvar.c971
-rw-r--r--gnu/usr.bin/gdb/gdb/fopen-same.h27
-rw-r--r--gnu/usr.bin/gdb/gdb/fork-child.c310
-rw-r--r--gnu/usr.bin/gdb/gdb/frame.h238
-rw-r--r--gnu/usr.bin/gdb/gdb/freebsd-nat.c323
-rw-r--r--gnu/usr.bin/gdb/gdb/freebsd-solib.c1469
-rw-r--r--gnu/usr.bin/gdb/gdb/gdb-stabs.h76
-rw-r--r--gnu/usr.bin/gdb/gdb/gdb.1371
-rw-r--r--gnu/usr.bin/gdb/gdb/gdbcmd.h101
-rw-r--r--gnu/usr.bin/gdb/gdb/gdbcore.h129
-rw-r--r--gnu/usr.bin/gdb/gdb/gdbtypes.c1440
-rw-r--r--gnu/usr.bin/gdb/gdb/gdbtypes.h704
-rw-r--r--gnu/usr.bin/gdb/gdb/getopt.h129
-rw-r--r--gnu/usr.bin/gdb/gdb/i386-dis.c1952
-rw-r--r--gnu/usr.bin/gdb/gdb/i386-pinsn.c37
-rw-r--r--gnu/usr.bin/gdb/gdb/i386-tdep.c595
-rw-r--r--gnu/usr.bin/gdb/gdb/infcmd.c1423
-rw-r--r--gnu/usr.bin/gdb/gdb/inferior.h401
-rw-r--r--gnu/usr.bin/gdb/gdb/inflow.c662
-rw-r--r--gnu/usr.bin/gdb/gdb/infptrace.c436
-rw-r--r--gnu/usr.bin/gdb/gdb/infrun.c1848
-rw-r--r--gnu/usr.bin/gdb/gdb/inftarg.c310
-rw-r--r--gnu/usr.bin/gdb/gdb/init.c47
-rw-r--r--gnu/usr.bin/gdb/gdb/language.c1332
-rw-r--r--gnu/usr.bin/gdb/gdb/language.h413
-rw-r--r--gnu/usr.bin/gdb/gdb/m2-exp.tab.c1991
-rw-r--r--gnu/usr.bin/gdb/gdb/m2-exp.y1169
-rw-r--r--gnu/usr.bin/gdb/gdb/m2-lang.c457
-rw-r--r--gnu/usr.bin/gdb/gdb/m2-lang.h31
-rw-r--r--gnu/usr.bin/gdb/gdb/m2-typeprint.c49
-rw-r--r--gnu/usr.bin/gdb/gdb/m2-valprint.c45
-rw-r--r--gnu/usr.bin/gdb/gdb/main.c2799
-rw-r--r--gnu/usr.bin/gdb/gdb/maint.c305
-rw-r--r--gnu/usr.bin/gdb/gdb/mem-break.c104
-rw-r--r--gnu/usr.bin/gdb/gdb/minsyms.c597
-rw-r--r--gnu/usr.bin/gdb/gdb/mipsread.c3653
-rw-r--r--gnu/usr.bin/gdb/gdb/nlmread.c300
-rw-r--r--gnu/usr.bin/gdb/gdb/nm.h44
-rw-r--r--gnu/usr.bin/gdb/gdb/objfiles.c773
-rw-r--r--gnu/usr.bin/gdb/gdb/objfiles.h437
-rw-r--r--gnu/usr.bin/gdb/gdb/obstack.h490
-rw-r--r--gnu/usr.bin/gdb/gdb/parse.c827
-rw-r--r--gnu/usr.bin/gdb/gdb/parser-defs.h188
-rw-r--r--gnu/usr.bin/gdb/gdb/partial-stab.h618
-rw-r--r--gnu/usr.bin/gdb/gdb/printcmd.c2078
-rw-r--r--gnu/usr.bin/gdb/gdb/putenv.c111
-rw-r--r--gnu/usr.bin/gdb/gdb/regex.c1725
-rw-r--r--gnu/usr.bin/gdb/gdb/regex.h179
-rw-r--r--gnu/usr.bin/gdb/gdb/remote-utils.c645
-rw-r--r--gnu/usr.bin/gdb/gdb/remote-utils.h149
-rw-r--r--gnu/usr.bin/gdb/gdb/remote.c1272
-rw-r--r--gnu/usr.bin/gdb/gdb/ser-unix.c633
-rw-r--r--gnu/usr.bin/gdb/gdb/serial.c261
-rw-r--r--gnu/usr.bin/gdb/gdb/serial.h153
-rw-r--r--gnu/usr.bin/gdb/gdb/signals.h27
-rw-r--r--gnu/usr.bin/gdb/gdb/solib.h56
-rw-r--r--gnu/usr.bin/gdb/gdb/source.c1398
-rw-r--r--gnu/usr.bin/gdb/gdb/stabsread.c3770
-rw-r--r--gnu/usr.bin/gdb/gdb/stabsread.h194
-rw-r--r--gnu/usr.bin/gdb/gdb/stack.c1379
-rw-r--r--gnu/usr.bin/gdb/gdb/symfile.c1489
-rw-r--r--gnu/usr.bin/gdb/gdb/symfile.h228
-rw-r--r--gnu/usr.bin/gdb/gdb/symmisc.c857
-rw-r--r--gnu/usr.bin/gdb/gdb/symtab.c3035
-rw-r--r--gnu/usr.bin/gdb/gdb/symtab.h1124
-rw-r--r--gnu/usr.bin/gdb/gdb/target.c789
-rw-r--r--gnu/usr.bin/gdb/gdb/target.h465
-rw-r--r--gnu/usr.bin/gdb/gdb/terminal.h62
-rw-r--r--gnu/usr.bin/gdb/gdb/thread.c248
-rw-r--r--gnu/usr.bin/gdb/gdb/thread.h36
-rw-r--r--gnu/usr.bin/gdb/gdb/tm-i386v.h309
-rw-r--r--gnu/usr.bin/gdb/gdb/tm.h76
-rw-r--r--gnu/usr.bin/gdb/gdb/typeprint.c297
-rw-r--r--gnu/usr.bin/gdb/gdb/typeprint.h21
-rw-r--r--gnu/usr.bin/gdb/gdb/utils.c1547
-rw-r--r--gnu/usr.bin/gdb/gdb/valarith.c969
-rw-r--r--gnu/usr.bin/gdb/gdb/valops.c1819
-rw-r--r--gnu/usr.bin/gdb/gdb/valprint.c1063
-rw-r--r--gnu/usr.bin/gdb/gdb/valprint.h40
-rw-r--r--gnu/usr.bin/gdb/gdb/value.h502
-rw-r--r--gnu/usr.bin/gdb/gdb/values.c1502
-rw-r--r--gnu/usr.bin/gdb/gdb/version.c3
-rw-r--r--gnu/usr.bin/gdb/gdb/wait.h38
-rw-r--r--gnu/usr.bin/gdb/gdb/xm.h31
-rw-r--r--gnu/usr.bin/gdb/gdb/y.tab.h65
-rw-r--r--gnu/usr.bin/gdb/getpagesize.h25
-rw-r--r--gnu/usr.bin/gdb/infcmd.c1204
-rw-r--r--gnu/usr.bin/gdb/inferior.h142
-rw-r--r--gnu/usr.bin/gdb/inflow.c569
-rw-r--r--gnu/usr.bin/gdb/infrun.c1459
-rw-r--r--gnu/usr.bin/gdb/kgdb_proto.h63
-rw-r--r--gnu/usr.bin/gdb/libiberty/COPYING.LIB481
-rw-r--r--gnu/usr.bin/gdb/libiberty/Makefile13
-rw-r--r--gnu/usr.bin/gdb/libiberty/README.FreeBSD7
-rw-r--r--gnu/usr.bin/gdb/libiberty/alloca-conf.h11
-rw-r--r--gnu/usr.bin/gdb/libiberty/argv.c332
-rw-r--r--gnu/usr.bin/gdb/libiberty/basename.c56
-rw-r--r--gnu/usr.bin/gdb/libiberty/concat.c118
-rw-r--r--gnu/usr.bin/gdb/libiberty/config.h1
-rw-r--r--gnu/usr.bin/gdb/libiberty/cplus-dem.c2669
-rw-r--r--gnu/usr.bin/gdb/libiberty/fdmatch.c71
-rw-r--r--gnu/usr.bin/gdb/libiberty/getopt.c750
-rw-r--r--gnu/usr.bin/gdb/libiberty/getopt1.c180
-rw-r--r--gnu/usr.bin/gdb/libiberty/ieee-float.c150
-rw-r--r--gnu/usr.bin/gdb/libiberty/ieee-float.h65
-rw-r--r--gnu/usr.bin/gdb/libiberty/obstack.c460
-rw-r--r--gnu/usr.bin/gdb/libiberty/sigsetmask.c44
-rw-r--r--gnu/usr.bin/gdb/libiberty/spaces.c67
-rw-r--r--gnu/usr.bin/gdb/libiberty/strerror.c811
-rw-r--r--gnu/usr.bin/gdb/libiberty/strsignal.c634
-rw-r--r--gnu/usr.bin/gdb/libiberty/xmalloc.c58
-rw-r--r--gnu/usr.bin/gdb/main.c2241
-rw-r--r--gnu/usr.bin/gdb/mmalloc/Makefile10
-rw-r--r--gnu/usr.bin/gdb/mmalloc/README.FreeBSD7
-rw-r--r--gnu/usr.bin/gdb/mmalloc/attach.c218
-rw-r--r--gnu/usr.bin/gdb/mmalloc/detach.c71
-rw-r--r--gnu/usr.bin/gdb/mmalloc/keys.c66
-rw-r--r--gnu/usr.bin/gdb/mmalloc/mcalloc.c53
-rw-r--r--gnu/usr.bin/gdb/mmalloc/mfree.c247
-rw-r--r--gnu/usr.bin/gdb/mmalloc/mmalloc.c334
-rw-r--r--gnu/usr.bin/gdb/mmalloc/mmalloc.h390
-rw-r--r--gnu/usr.bin/gdb/mmalloc/mmalloc.texi258
-rw-r--r--gnu/usr.bin/gdb/mmalloc/mmap-sup.c144
-rw-r--r--gnu/usr.bin/gdb/mmalloc/mmcheck.c196
-rw-r--r--gnu/usr.bin/gdb/mmalloc/mmemalign.c64
-rw-r--r--gnu/usr.bin/gdb/mmalloc/mmstats.c46
-rw-r--r--gnu/usr.bin/gdb/mmalloc/mmtrace.c166
-rw-r--r--gnu/usr.bin/gdb/mmalloc/mrealloc.c160
-rw-r--r--gnu/usr.bin/gdb/mmalloc/mvalloc.c40
-rw-r--r--gnu/usr.bin/gdb/mmalloc/sbrk-sup.c96
-rw-r--r--gnu/usr.bin/gdb/ngdb.i386/Makefile27
-rw-r--r--gnu/usr.bin/gdb/obstack.c313
-rw-r--r--gnu/usr.bin/gdb/obstack.h372
-rw-r--r--gnu/usr.bin/gdb/printcmd.c1867
-rw-r--r--gnu/usr.bin/gdb/readline/ChangeLog98
-rw-r--r--gnu/usr.bin/gdb/readline/Makefile.gnu114
-rw-r--r--gnu/usr.bin/gdb/readline/chardefs.h50
-rw-r--r--gnu/usr.bin/gdb/readline/emacs_keymap.c472
-rw-r--r--gnu/usr.bin/gdb/readline/funmap.c217
-rw-r--r--gnu/usr.bin/gdb/readline/history.c1462
-rw-r--r--gnu/usr.bin/gdb/readline/history.h108
-rw-r--r--gnu/usr.bin/gdb/readline/keymaps.c172
-rw-r--r--gnu/usr.bin/gdb/readline/keymaps.h53
-rw-r--r--gnu/usr.bin/gdb/readline/readline.c5557
-rw-r--r--gnu/usr.bin/gdb/readline/readline.h161
-rw-r--r--gnu/usr.bin/gdb/readline/vi_keymap.c484
-rw-r--r--gnu/usr.bin/gdb/readline/vi_mode.c875
-rw-r--r--gnu/usr.bin/gdb/regex.c1738
-rw-r--r--gnu/usr.bin/gdb/regex.h185
-rw-r--r--gnu/usr.bin/gdb/remote-sl.c10
-rw-r--r--gnu/usr.bin/gdb/remote.c626
-rw-r--r--gnu/usr.bin/gdb/source.c1166
-rw-r--r--gnu/usr.bin/gdb/stab.def115
-rw-r--r--gnu/usr.bin/gdb/stack.c960
-rw-r--r--gnu/usr.bin/gdb/symmisc.c584
-rw-r--r--gnu/usr.bin/gdb/symseg.h523
-rw-r--r--gnu/usr.bin/gdb/symtab.c2473
-rw-r--r--gnu/usr.bin/gdb/symtab.h384
-rw-r--r--gnu/usr.bin/gdb/utils.c1096
-rw-r--r--gnu/usr.bin/gdb/valarith.c690
-rw-r--r--gnu/usr.bin/gdb/valops.c1418
-rw-r--r--gnu/usr.bin/gdb/valprint.c1430
-rw-r--r--gnu/usr.bin/gdb/value.h212
-rw-r--r--gnu/usr.bin/gdb/values.c1059
-rw-r--r--gnu/usr.bin/gdb/version.c20
-rw-r--r--gnu/usr.bin/gdb/wait.h81
-rw-r--r--gnu/usr.bin/gdb/xgdb/Makefile33
-rw-r--r--gnu/usr.bin/gdb/xgdb/xgdb.c700
-rw-r--r--gnu/usr.bin/grep/AUTHORS29
-rw-r--r--gnu/usr.bin/grep/COPYING339
-rw-r--r--gnu/usr.bin/grep/Makefile15
-rw-r--r--gnu/usr.bin/grep/NEWS35
-rw-r--r--gnu/usr.bin/grep/PROJECTS15
-rw-r--r--gnu/usr.bin/grep/README28
-rw-r--r--gnu/usr.bin/grep/dfa.c2525
-rw-r--r--gnu/usr.bin/grep/dfa.h360
-rw-r--r--gnu/usr.bin/grep/getopt.c731
-rw-r--r--gnu/usr.bin/grep/getopt.h129
-rw-r--r--gnu/usr.bin/grep/getpagesize.h42
-rw-r--r--gnu/usr.bin/grep/grep.1375
-rw-r--r--gnu/usr.bin/grep/grep.c826
-rw-r--r--gnu/usr.bin/grep/grep.h53
-rw-r--r--gnu/usr.bin/grep/kwset.c805
-rw-r--r--gnu/usr.bin/grep/kwset.h69
-rw-r--r--gnu/usr.bin/grep/obstack.c454
-rw-r--r--gnu/usr.bin/grep/obstack.h484
-rw-r--r--gnu/usr.bin/grep/regex.c5811
-rw-r--r--gnu/usr.bin/grep/regex.h625
-rw-r--r--gnu/usr.bin/grep/search.c481
-rw-r--r--gnu/usr.bin/grep/tests/check.sh24
-rw-r--r--gnu/usr.bin/grep/tests/khadafy.lines32
-rw-r--r--gnu/usr.bin/grep/tests/khadafy.regexp1
-rw-r--r--gnu/usr.bin/grep/tests/scriptgen.awk4
-rw-r--r--gnu/usr.bin/grep/tests/spencer.tests2
-rw-r--r--gnu/usr.bin/gzip/ChangeLog137
-rw-r--r--gnu/usr.bin/gzip/Makefile17
-rw-r--r--gnu/usr.bin/gzip/NEWS65
-rw-r--r--gnu/usr.bin/gzip/README63
-rw-r--r--gnu/usr.bin/gzip/THANKS92
-rw-r--r--gnu/usr.bin/gzip/TODO12
-rw-r--r--gnu/usr.bin/gzip/algorithm.doc23
-rw-r--r--gnu/usr.bin/gzip/bits.c6
-rw-r--r--gnu/usr.bin/gzip/crypt.c2
-rw-r--r--gnu/usr.bin/gzip/deflate.c93
-rw-r--r--gnu/usr.bin/gzip/getopt.c236
-rw-r--r--gnu/usr.bin/gzip/getopt.h21
-rw-r--r--gnu/usr.bin/gzip/gzexe38
-rw-r--r--gnu/usr.bin/gzip/gzexe.17
-rw-r--r--gnu/usr.bin/gzip/gzexe.in44
-rw-r--r--gnu/usr.bin/gzip/gzip.1180
-rw-r--r--gnu/usr.bin/gzip/gzip.c560
-rw-r--r--gnu/usr.bin/gzip/gzip.h37
-rw-r--r--gnu/usr.bin/gzip/inflate.c8
-rw-r--r--gnu/usr.bin/gzip/lzw.c11
-rw-r--r--gnu/usr.bin/gzip/match.S20
-rw-r--r--gnu/usr.bin/gzip/revision.h6
-rw-r--r--gnu/usr.bin/gzip/tailor.h82
-rw-r--r--gnu/usr.bin/gzip/trees.c5
-rw-r--r--gnu/usr.bin/gzip/unlzh.c401
-rw-r--r--gnu/usr.bin/gzip/unlzw.c28
-rw-r--r--gnu/usr.bin/gzip/unpack.c22
-rw-r--r--gnu/usr.bin/gzip/unzip.c8
-rw-r--r--gnu/usr.bin/gzip/util.c65
-rw-r--r--gnu/usr.bin/gzip/zdiff18
-rw-r--r--gnu/usr.bin/gzip/zdiff.12
-rw-r--r--gnu/usr.bin/gzip/zforce22
-rw-r--r--gnu/usr.bin/gzip/zforce.12
-rw-r--r--gnu/usr.bin/gzip/zgrep72
-rw-r--r--gnu/usr.bin/gzip/zgrep.144
-rw-r--r--gnu/usr.bin/gzip/zip.c5
-rw-r--r--gnu/usr.bin/gzip/zmore17
-rw-r--r--gnu/usr.bin/gzip/zmore.117
-rw-r--r--gnu/usr.bin/gzip/znew53
-rw-r--r--gnu/usr.bin/gzip/znew.15
-rw-r--r--gnu/usr.bin/ld/Makefile19
-rw-r--r--gnu/usr.bin/ld/TODO4
-rw-r--r--gnu/usr.bin/ld/cplus-dem.c975
-rw-r--r--gnu/usr.bin/ld/etc.c66
-rw-r--r--gnu/usr.bin/ld/i386/md-static-funcs.c17
-rw-r--r--gnu/usr.bin/ld/i386/md.c357
-rw-r--r--gnu/usr.bin/ld/i386/md.h257
-rw-r--r--gnu/usr.bin/ld/i386/mdprologue.S124
-rw-r--r--gnu/usr.bin/ld/ld.1207
-rw-r--r--gnu/usr.bin/ld/ld.1aout207
-rw-r--r--gnu/usr.bin/ld/ld.c3513
-rw-r--r--gnu/usr.bin/ld/ld.h688
-rw-r--r--gnu/usr.bin/ld/ldconfig/Makefile13
-rw-r--r--gnu/usr.bin/ld/ldconfig/ldconfig.8102
-rw-r--r--gnu/usr.bin/ld/ldconfig/ldconfig.c429
-rw-r--r--gnu/usr.bin/ld/ldd/Makefile7
-rw-r--r--gnu/usr.bin/ld/ldd/ldd.124
-rw-r--r--gnu/usr.bin/ld/ldd/ldd.c136
-rw-r--r--gnu/usr.bin/ld/lib.c843
-rw-r--r--gnu/usr.bin/ld/rrs.c1179
-rw-r--r--gnu/usr.bin/ld/rtld/Makefile27
-rw-r--r--gnu/usr.bin/ld/rtld/malloc.c482
-rw-r--r--gnu/usr.bin/ld/rtld/md-prologue.c39
-rw-r--r--gnu/usr.bin/ld/rtld/rtld.c1261
-rw-r--r--gnu/usr.bin/ld/rtld/sbrk.c101
-rw-r--r--gnu/usr.bin/ld/shlib.c279
-rw-r--r--gnu/usr.bin/ld/sparc/md-static-funcs.c37
-rw-r--r--gnu/usr.bin/ld/sparc/md.c351
-rw-r--r--gnu/usr.bin/ld/sparc/md.h282
-rw-r--r--gnu/usr.bin/ld/sparc/mdprologue.S101
-rw-r--r--gnu/usr.bin/ld/symbol.c161
-rw-r--r--gnu/usr.bin/ld/symseg.h359
-rw-r--r--gnu/usr.bin/ld/warnings.c719
-rw-r--r--gnu/usr.bin/ld/xbits.c167
-rw-r--r--gnu/usr.bin/man/COPYING339
-rw-r--r--gnu/usr.bin/man/Makefile10
-rw-r--r--gnu/usr.bin/man/Makefile.inc23
-rw-r--r--gnu/usr.bin/man/README134
-rw-r--r--gnu/usr.bin/man/TODO123
-rw-r--r--gnu/usr.bin/man/apropos/Makefile44
-rw-r--r--gnu/usr.bin/man/apropos/apropos64
-rw-r--r--gnu/usr.bin/man/apropos/apropos.127
-rw-r--r--gnu/usr.bin/man/apropos/apropos.man27
-rw-r--r--gnu/usr.bin/man/apropos/apropos.sh64
-rw-r--r--gnu/usr.bin/man/catman/Makefile15
-rw-r--r--gnu/usr.bin/man/catman/catman36
-rw-r--r--gnu/usr.bin/man/catman/catman.sh43
-rw-r--r--gnu/usr.bin/man/lib/Makefile33
-rw-r--r--gnu/usr.bin/man/lib/config.h216
-rw-r--r--gnu/usr.bin/man/lib/config.h_dist210
-rw-r--r--gnu/usr.bin/man/lib/gripes.c180
-rw-r--r--gnu/usr.bin/man/lib/gripes.h30
-rw-r--r--gnu/usr.bin/man/lib/util.c149
-rw-r--r--gnu/usr.bin/man/makewhatis/Makefile17
-rw-r--r--gnu/usr.bin/man/makewhatis/makewhatis79
-rw-r--r--gnu/usr.bin/man/makewhatis/makewhatis.sh120
-rw-r--r--gnu/usr.bin/man/man/Makefile31
-rw-r--r--gnu/usr.bin/man/man/glob.c680
-rw-r--r--gnu/usr.bin/man/man/man.1132
-rw-r--r--gnu/usr.bin/man/man/man.c1431
-rw-r--r--gnu/usr.bin/man/man/man.man134
-rw-r--r--gnu/usr.bin/man/man/manpath.c520
-rw-r--r--gnu/usr.bin/man/man/manpath.h26
-rw-r--r--gnu/usr.bin/man/man/ndir.h51
-rw-r--r--gnu/usr.bin/man/man/strdup.c39
-rw-r--r--gnu/usr.bin/man/man/version.h17
-rw-r--r--gnu/usr.bin/man/manpath/Makefile30
-rw-r--r--gnu/usr.bin/man/manpath/manpath.c520
-rw-r--r--gnu/usr.bin/man/manpath/manpath.config32
-rw-r--r--gnu/usr.bin/man/manpath/manpath.h26
-rw-r--r--gnu/usr.bin/man/manpath/manpath.man56
-rw-r--r--gnu/usr.bin/man/whatis/Makefile41
-rw-r--r--gnu/usr.bin/man/whatis/whatis66
-rw-r--r--gnu/usr.bin/man/whatis/whatis.127
-rw-r--r--gnu/usr.bin/man/whatis/whatis.man27
-rw-r--r--gnu/usr.bin/man/whatis/whatis.sh66
-rw-r--r--gnu/usr.bin/patch/EXTERN.h21
-rw-r--r--gnu/usr.bin/patch/INTERN.h19
-rw-r--r--gnu/usr.bin/patch/Makefile6
-rw-r--r--gnu/usr.bin/patch/backupfile.c402
-rw-r--r--gnu/usr.bin/patch/backupfile.h46
-rw-r--r--gnu/usr.bin/patch/common.h193
-rw-r--r--gnu/usr.bin/patch/config.h81
-rw-r--r--gnu/usr.bin/patch/getopt.c731
-rw-r--r--gnu/usr.bin/patch/getopt.h129
-rw-r--r--gnu/usr.bin/patch/getopt1.c176
-rw-r--r--gnu/usr.bin/patch/inp.c369
-rw-r--r--gnu/usr.bin/patch/inp.h18
-rw-r--r--gnu/usr.bin/patch/patch.1584
-rw-r--r--gnu/usr.bin/patch/patch.c967
-rw-r--r--gnu/usr.bin/patch/patchlevel.h1
-rw-r--r--gnu/usr.bin/patch/pch.c1317
-rw-r--r--gnu/usr.bin/patch/pch.h36
-rw-r--r--gnu/usr.bin/patch/util.c433
-rw-r--r--gnu/usr.bin/patch/util.h88
-rw-r--r--gnu/usr.bin/patch/version.c25
-rw-r--r--gnu/usr.bin/patch/version.h9
-rw-r--r--gnu/usr.bin/perl/Artistic117
-rw-r--r--gnu/usr.bin/perl/Copying248
-rw-r--r--gnu/usr.bin/perl/Makefile10
-rw-r--r--gnu/usr.bin/perl/Makefile.inc5
-rw-r--r--gnu/usr.bin/perl/README195
-rw-r--r--gnu/usr.bin/perl/VERSION1
-rw-r--r--gnu/usr.bin/perl/Wishlist9
-rw-r--r--gnu/usr.bin/perl/eg/ADB8
-rw-r--r--gnu/usr.bin/perl/eg/README22
-rw-r--r--gnu/usr.bin/perl/eg/changes34
-rw-r--r--gnu/usr.bin/perl/eg/client34
-rw-r--r--gnu/usr.bin/perl/eg/down30
-rw-r--r--gnu/usr.bin/perl/eg/dus22
-rw-r--r--gnu/usr.bin/perl/eg/findcp53
-rw-r--r--gnu/usr.bin/perl/eg/findtar17
-rw-r--r--gnu/usr.bin/perl/eg/g/gcp114
-rw-r--r--gnu/usr.bin/perl/eg/g/gcp.man77
-rw-r--r--gnu/usr.bin/perl/eg/g/ged21
-rw-r--r--gnu/usr.bin/perl/eg/g/ghosts33
-rw-r--r--gnu/usr.bin/perl/eg/g/gsh117
-rw-r--r--gnu/usr.bin/perl/eg/g/gsh.man80
-rw-r--r--gnu/usr.bin/perl/eg/muck141
-rw-r--r--gnu/usr.bin/perl/eg/muck.man21
-rw-r--r--gnu/usr.bin/perl/eg/myrup29
-rw-r--r--gnu/usr.bin/perl/eg/nih10
-rw-r--r--gnu/usr.bin/perl/eg/perlsh15
-rw-r--r--gnu/usr.bin/perl/eg/relink91
-rw-r--r--gnu/usr.bin/perl/eg/rename83
-rw-r--r--gnu/usr.bin/perl/eg/rmfrom7
-rw-r--r--gnu/usr.bin/perl/eg/scan/scan_df51
-rw-r--r--gnu/usr.bin/perl/eg/scan/scan_last57
-rw-r--r--gnu/usr.bin/perl/eg/scan/scan_messages222
-rw-r--r--gnu/usr.bin/perl/eg/scan/scan_passwd30
-rw-r--r--gnu/usr.bin/perl/eg/scan/scan_ps32
-rw-r--r--gnu/usr.bin/perl/eg/scan/scan_sudo54
-rw-r--r--gnu/usr.bin/perl/eg/scan/scan_suid84
-rw-r--r--gnu/usr.bin/perl/eg/scan/scanner87
-rw-r--r--gnu/usr.bin/perl/eg/server27
-rw-r--r--gnu/usr.bin/perl/eg/shmkill24
-rw-r--r--gnu/usr.bin/perl/eg/sysvipc/README9
-rw-r--r--gnu/usr.bin/perl/eg/sysvipc/ipcmsg47
-rw-r--r--gnu/usr.bin/perl/eg/sysvipc/ipcsem46
-rw-r--r--gnu/usr.bin/perl/eg/sysvipc/ipcshm50
-rw-r--r--gnu/usr.bin/perl/eg/travesty46
-rw-r--r--gnu/usr.bin/perl/eg/van/empty45
-rw-r--r--gnu/usr.bin/perl/eg/van/unvanish66
-rw-r--r--gnu/usr.bin/perl/eg/van/vanexp21
-rw-r--r--gnu/usr.bin/perl/eg/van/vanish65
-rw-r--r--gnu/usr.bin/perl/eg/who13
-rw-r--r--gnu/usr.bin/perl/emacs/perl-mode.el631
-rw-r--r--gnu/usr.bin/perl/emacs/perldb.el423
-rw-r--r--gnu/usr.bin/perl/emacs/perldb.pl568
-rw-r--r--gnu/usr.bin/perl/emacs/tedstuff296
-rw-r--r--gnu/usr.bin/perl/h2pl/README71
-rw-r--r--gnu/usr.bin/perl/h2pl/cbreak.pl34
-rw-r--r--gnu/usr.bin/perl/h2pl/cbreak2.pl33
-rw-r--r--gnu/usr.bin/perl/h2pl/eg/sizeof.ph14
-rw-r--r--gnu/usr.bin/perl/h2pl/eg/sys/errno.pl92
-rw-r--r--gnu/usr.bin/perl/h2pl/eg/sys/ioctl.pl186
-rw-r--r--gnu/usr.bin/perl/h2pl/eg/sysexits.pl16
-rw-r--r--gnu/usr.bin/perl/h2pl/getioctlsizes13
-rw-r--r--gnu/usr.bin/perl/h2pl/mksizes42
-rw-r--r--gnu/usr.bin/perl/h2pl/mkvars31
-rw-r--r--gnu/usr.bin/perl/h2pl/tcbreak17
-rw-r--r--gnu/usr.bin/perl/h2pl/tcbreak217
-rw-r--r--gnu/usr.bin/perl/lib/Makefile19
-rw-r--r--gnu/usr.bin/perl/lib/abbrev.pl33
-rw-r--r--gnu/usr.bin/perl/lib/assert.pl52
-rw-r--r--gnu/usr.bin/perl/lib/bigfloat.pl233
-rw-r--r--gnu/usr.bin/perl/lib/bigint.pl271
-rw-r--r--gnu/usr.bin/perl/lib/bigrat.pl148
-rw-r--r--gnu/usr.bin/perl/lib/cacheout.pl40
-rw-r--r--gnu/usr.bin/perl/lib/chat2.pl339
-rw-r--r--gnu/usr.bin/perl/lib/complete.pl110
-rw-r--r--gnu/usr.bin/perl/lib/ctime.pl51
-rw-r--r--gnu/usr.bin/perl/lib/dumpvar.pl37
-rw-r--r--gnu/usr.bin/perl/lib/exceptions.pl54
-rw-r--r--gnu/usr.bin/perl/lib/fastcwd.pl35
-rw-r--r--gnu/usr.bin/perl/lib/find.pl106
-rw-r--r--gnu/usr.bin/perl/lib/finddepth.pl105
-rw-r--r--gnu/usr.bin/perl/lib/flush.pl23
-rw-r--r--gnu/usr.bin/perl/lib/getcwd.pl62
-rw-r--r--gnu/usr.bin/perl/lib/getopt.pl41
-rw-r--r--gnu/usr.bin/perl/lib/getopts.pl50
-rw-r--r--gnu/usr.bin/perl/lib/importenv.pl16
-rw-r--r--gnu/usr.bin/perl/lib/look.pl44
-rw-r--r--gnu/usr.bin/perl/lib/newgetopt.pl271
-rw-r--r--gnu/usr.bin/perl/lib/open2.pl54
-rw-r--r--gnu/usr.bin/perl/lib/perldb.pl598
-rw-r--r--gnu/usr.bin/perl/lib/pwd.pl72
-rw-r--r--gnu/usr.bin/perl/lib/shellwords.pl48
-rw-r--r--gnu/usr.bin/perl/lib/stat.pl31
-rw-r--r--gnu/usr.bin/perl/lib/syslog.pl224
-rw-r--r--gnu/usr.bin/perl/lib/termcap.pl165
-rw-r--r--gnu/usr.bin/perl/lib/timelocal.pl82
-rw-r--r--gnu/usr.bin/perl/lib/validate.pl104
-rw-r--r--gnu/usr.bin/perl/misc/c2ph1071
-rw-r--r--gnu/usr.bin/perl/misc/c2ph.1191
-rw-r--r--gnu/usr.bin/perl/misc/pstruct1071
-rw-r--r--gnu/usr.bin/perl/perl/EXTERN.h26
-rw-r--r--gnu/usr.bin/perl/perl/INTERN.h26
-rw-r--r--gnu/usr.bin/perl/perl/Makefile17
-rw-r--r--gnu/usr.bin/perl/perl/arg.h997
-rw-r--r--gnu/usr.bin/perl/perl/array.c287
-rw-r--r--gnu/usr.bin/perl/perl/array.h45
-rwxr-xr-xgnu/usr.bin/perl/perl/cflags91
-rw-r--r--gnu/usr.bin/perl/perl/cmd.c1263
-rw-r--r--gnu/usr.bin/perl/perl/cmd.h179
-rw-r--r--gnu/usr.bin/perl/perl/config.H892
-rw-r--r--gnu/usr.bin/perl/perl/config.h769
-rw-r--r--gnu/usr.bin/perl/perl/config.sh268
-rw-r--r--gnu/usr.bin/perl/perl/cons.c1450
-rw-r--r--gnu/usr.bin/perl/perl/consarg.c1295
-rw-r--r--gnu/usr.bin/perl/perl/crypt.c200
-rw-r--r--gnu/usr.bin/perl/perl/doarg.c1852
-rw-r--r--gnu/usr.bin/perl/perl/doio.c2954
-rw-r--r--gnu/usr.bin/perl/perl/dolist.c1973
-rw-r--r--gnu/usr.bin/perl/perl/dump.c375
-rw-r--r--gnu/usr.bin/perl/perl/eval.c3013
-rw-r--r--gnu/usr.bin/perl/perl/form.c419
-rw-r--r--gnu/usr.bin/perl/perl/form.h48
-rw-r--r--gnu/usr.bin/perl/perl/handy.h150
-rw-r--r--gnu/usr.bin/perl/perl/hash.c715
-rw-r--r--gnu/usr.bin/perl/perl/hash.h75
-rw-r--r--gnu/usr.bin/perl/perl/malloc.c510
-rw-r--r--gnu/usr.bin/perl/perl/patchlevel.h1
-rw-r--r--gnu/usr.bin/perl/perl/perl.16010
-rw-r--r--gnu/usr.bin/perl/perl/perl.c1449
-rw-r--r--gnu/usr.bin/perl/perl/perl.h1063
-rw-r--r--gnu/usr.bin/perl/perl/perly.c3063
-rw-r--r--gnu/usr.bin/perl/perl/perly.h83
-rw-r--r--gnu/usr.bin/perl/perl/regcomp.c1478
-rw-r--r--gnu/usr.bin/perl/perl/regcomp.h200
-rw-r--r--gnu/usr.bin/perl/perl/regexec.c910
-rw-r--r--gnu/usr.bin/perl/perl/regexp.h53
-rw-r--r--gnu/usr.bin/perl/perl/spat.h46
-rw-r--r--gnu/usr.bin/perl/perl/stab.c1055
-rw-r--r--gnu/usr.bin/perl/perl/stab.h145
-rw-r--r--gnu/usr.bin/perl/perl/str.c1599
-rw-r--r--gnu/usr.bin/perl/perl/str.h171
-rw-r--r--gnu/usr.bin/perl/perl/t/README11
-rwxr-xr-xgnu/usr.bin/perl/perl/t/TEST102
-rwxr-xr-xgnu/usr.bin/perl/perl/t/base/cond.t19
-rwxr-xr-xgnu/usr.bin/perl/perl/t/base/if.t11
-rwxr-xr-xgnu/usr.bin/perl/perl/t/base/lex.t78
-rwxr-xr-xgnu/usr.bin/perl/perl/t/base/pat.t11
-rwxr-xr-xgnu/usr.bin/perl/perl/t/base/term.t42
-rwxr-xr-xgnu/usr.bin/perl/perl/t/cmd/elsif.t25
-rwxr-xr-xgnu/usr.bin/perl/perl/t/cmd/for.t49
-rwxr-xr-xgnu/usr.bin/perl/perl/t/cmd/mod.t33
-rwxr-xr-xgnu/usr.bin/perl/perl/t/cmd/subval.t179
-rwxr-xr-xgnu/usr.bin/perl/perl/t/cmd/switch.t75
-rwxr-xr-xgnu/usr.bin/perl/perl/t/cmd/while.t110
-rwxr-xr-xgnu/usr.bin/perl/perl/t/comp/cmdopt.t83
-rwxr-xr-xgnu/usr.bin/perl/perl/t/comp/cpp.t51
-rwxr-xr-xgnu/usr.bin/perl/perl/t/comp/decl.t49
-rwxr-xr-xgnu/usr.bin/perl/perl/t/comp/multiline.t40
-rwxr-xr-xgnu/usr.bin/perl/perl/t/comp/package.t33
-rwxr-xr-xgnu/usr.bin/perl/perl/t/comp/script.t23
-rwxr-xr-xgnu/usr.bin/perl/perl/t/comp/term.t35
-rwxr-xr-xgnu/usr.bin/perl/perl/t/io/argv.t36
-rwxr-xr-xgnu/usr.bin/perl/perl/t/io/dup.t32
-rwxr-xr-xgnu/usr.bin/perl/perl/t/io/fs.t85
-rwxr-xr-xgnu/usr.bin/perl/perl/t/io/inplace.t21
-rwxr-xr-xgnu/usr.bin/perl/perl/t/io/pipe.t56
-rwxr-xr-xgnu/usr.bin/perl/perl/t/io/print.t32
-rwxr-xr-xgnu/usr.bin/perl/perl/t/io/tell.t44
-rwxr-xr-xgnu/usr.bin/perl/perl/t/lib/big.t280
-rw-r--r--gnu/usr.bin/perl/perl/t/op/Op.dbmx.dbbin0 -> 16384 bytes
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/append.t21
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/array.t120
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/auto.t48
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/chop.t30
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/cond.t12
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/dbm.t106
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/delete.t29
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/do.t44
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/each.t53
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/eval.t57
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/exec.t21
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/exp.t27
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/flip.t26
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/fork.t16
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/glob.t22
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/goto.t33
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/groups.t47
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/index.t42
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/int.t17
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/join.t12
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/list.t83
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/local.t45
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/magic.t32
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/mkdir.t15
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/oct.t9
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/ord.t14
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/pack.t20
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/pat.t184
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/push.t44
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/range.t36
-rw-r--r--gnu/usr.bin/perl/perl/t/op/re_tests274
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/read.t20
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/readdir.t20
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/regexp.t35
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/repeat.t42
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/s.t179
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/sleep.t8
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/sort.t48
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/split.t57
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/sprintf.t8
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/stat.t176
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/study.t69
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/substr.t47
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/time.t43
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/undef.t56
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/unshift.t14
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/vec.t24
-rwxr-xr-xgnu/usr.bin/perl/perl/t/op/write.t129
-rw-r--r--gnu/usr.bin/perl/perl/t/printme6
-rw-r--r--gnu/usr.bin/perl/perl/tdoio.c2948
-rw-r--r--gnu/usr.bin/perl/perl/toke.c2764
-rw-r--r--gnu/usr.bin/perl/perl/usersub.c148
-rw-r--r--gnu/usr.bin/perl/perl/usub/Makefile16
-rw-r--r--gnu/usr.bin/perl/perl/usub/README114
-rw-r--r--gnu/usr.bin/perl/perl/usub/bsdcurses.mus699
-rw-r--r--gnu/usr.bin/perl/perl/usub/curses.mus890
-rw-r--r--gnu/usr.bin/perl/perl/usub/man2mus66
-rwxr-xr-xgnu/usr.bin/perl/perl/usub/mus135
-rw-r--r--gnu/usr.bin/perl/perl/usub/pager190
-rw-r--r--gnu/usr.bin/perl/perl/usub/usersub.c75
-rw-r--r--gnu/usr.bin/perl/perl/util.c1780
-rw-r--r--gnu/usr.bin/perl/perl/util.h61
-rw-r--r--gnu/usr.bin/perl/sperl/Makefile26
-rw-r--r--gnu/usr.bin/perl/tperl/Makefile26
-rw-r--r--gnu/usr.bin/perl/x2p/EXTERN.h26
-rw-r--r--gnu/usr.bin/perl/x2p/INTERN.h26
-rw-r--r--gnu/usr.bin/perl/x2p/Makefile19
-rw-r--r--gnu/usr.bin/perl/x2p/a2p.1199
-rw-r--r--gnu/usr.bin/perl/x2p/a2p.c2715
-rw-r--r--gnu/usr.bin/perl/x2p/a2p.h341
-rw-r--r--gnu/usr.bin/perl/x2p/a2p.y406
-rw-r--r--gnu/usr.bin/perl/x2p/a2py.c1301
-rwxr-xr-xgnu/usr.bin/perl/x2p/find2perl568
-rwxr-xr-xgnu/usr.bin/perl/x2p/h2ph253
-rwxr-xr-xgnu/usr.bin/perl/x2p/h2ph.1253
-rw-r--r--gnu/usr.bin/perl/x2p/handy.h46
-rw-r--r--gnu/usr.bin/perl/x2p/hash.c250
-rw-r--r--gnu/usr.bin/perl/x2p/hash.h60
-rw-r--r--gnu/usr.bin/perl/x2p/malloc.c513
-rwxr-xr-xgnu/usr.bin/perl/x2p/s2p766
-rw-r--r--gnu/usr.bin/perl/x2p/s2p.1108
-rw-r--r--gnu/usr.bin/perl/x2p/str.c467
-rw-r--r--gnu/usr.bin/perl/x2p/str.h46
-rw-r--r--gnu/usr.bin/perl/x2p/util.c268
-rw-r--r--gnu/usr.bin/perl/x2p/util.h53
-rw-r--r--gnu/usr.bin/perl/x2p/walk.c2086
-rw-r--r--gnu/usr.bin/pr/Makefile8
-rw-r--r--gnu/usr.bin/pr/error.c13
-rw-r--r--gnu/usr.bin/pr/getopt.c108
-rw-r--r--gnu/usr.bin/pr/getopt1.c13
-rw-r--r--gnu/usr.bin/pr/pr.12
-rw-r--r--gnu/usr.bin/pr/pr.c89
-rw-r--r--gnu/usr.bin/pr/system.h34
-rw-r--r--gnu/usr.bin/pr/version.c13
-rw-r--r--gnu/usr.bin/pr/xmalloc.c43
-rw-r--r--gnu/usr.bin/ptx/.stamp-h.in0
-rw-r--r--gnu/usr.bin/ptx/COPYING339
-rw-r--r--gnu/usr.bin/ptx/ChangeLog546
-rw-r--r--gnu/usr.bin/ptx/Makefile10
-rw-r--r--gnu/usr.bin/ptx/NEWS53
-rw-r--r--gnu/usr.bin/ptx/README23
-rw-r--r--gnu/usr.bin/ptx/THANKS23
-rw-r--r--gnu/usr.bin/ptx/TODO94
-rw-r--r--gnu/usr.bin/ptx/alloca.c484
-rw-r--r--gnu/usr.bin/ptx/argmatch.c94
-rw-r--r--gnu/usr.bin/ptx/bumpalloc.h58
-rw-r--r--gnu/usr.bin/ptx/check-out65
-rw-r--r--gnu/usr.bin/ptx/config.h57
-rw-r--r--gnu/usr.bin/ptx/diacrit.c148
-rw-r--r--gnu/usr.bin/ptx/diacrit.h16
-rw-r--r--gnu/usr.bin/ptx/error.c117
-rw-r--r--gnu/usr.bin/ptx/examples/README21
-rw-r--r--gnu/usr.bin/ptx/examples/ajay/Makefile28
-rw-r--r--gnu/usr.bin/ptx/examples/ajay/README41
-rw-r--r--gnu/usr.bin/ptx/examples/ajay/footer.tex1
-rw-r--r--gnu/usr.bin/ptx/examples/ajay/header.tex21
-rw-r--r--gnu/usr.bin/ptx/examples/ajay/tip.forgptx10
-rw-r--r--gnu/usr.bin/ptx/examples/ajay/x.pl22
-rw-r--r--gnu/usr.bin/ptx/examples/ignore/README65
-rw-r--r--gnu/usr.bin/ptx/examples/ignore/bix109
-rw-r--r--gnu/usr.bin/ptx/examples/ignore/eign163
-rwxr-xr-xgnu/usr.bin/ptx/examples/include.pl79
-rw-r--r--gnu/usr.bin/ptx/examples/latex/Makefile15
-rw-r--r--gnu/usr.bin/ptx/examples/latex/README10
-rw-r--r--gnu/usr.bin/ptx/examples/latex/latex.tex11
-rw-r--r--gnu/usr.bin/ptx/examples/latex/table.tex65
-rw-r--r--gnu/usr.bin/ptx/examples/luke/README2
-rw-r--r--gnu/usr.bin/ptx/examples/luke/xxroff.sh108
-rw-r--r--gnu/usr.bin/ptx/getopt.c757
-rw-r--r--gnu/usr.bin/ptx/getopt.h129
-rw-r--r--gnu/usr.bin/ptx/getopt1.c187
-rwxr-xr-xgnu/usr.bin/ptx/mkinstalldirs35
-rw-r--r--gnu/usr.bin/ptx/ptx.c2237
-rw-r--r--gnu/usr.bin/ptx/ptx.info496
-rw-r--r--gnu/usr.bin/ptx/ptx.texinfo554
-rw-r--r--gnu/usr.bin/ptx/regex.h490
-rw-r--r--gnu/usr.bin/ptx/xmalloc.c88
-rw-r--r--gnu/usr.bin/rcs/Makefile2
-rw-r--r--gnu/usr.bin/rcs/Makefile.inc8
-rw-r--r--gnu/usr.bin/rcs/ci/Makefile7
-rw-r--r--gnu/usr.bin/rcs/co/Makefile7
-rw-r--r--gnu/usr.bin/rcs/co/co.110
-rw-r--r--gnu/usr.bin/rcs/co/co.c11
-rw-r--r--gnu/usr.bin/rcs/ident/Makefile7
-rw-r--r--gnu/usr.bin/rcs/lib/Makefile15
-rw-r--r--gnu/usr.bin/rcs/lib/conf.h19
-rw-r--r--gnu/usr.bin/rcs/lib/rcsbase.h8
-rw-r--r--gnu/usr.bin/rcs/lib/rcsedit.c11
-rw-r--r--gnu/usr.bin/rcs/lib/rcskeys.c60
-rw-r--r--gnu/usr.bin/rcs/lib/rcslex.c11
-rw-r--r--gnu/usr.bin/rcs/merge/Makefile7
-rw-r--r--gnu/usr.bin/rcs/rcs/Makefile11
-rw-r--r--gnu/usr.bin/rcs/rcsclean/Makefile7
-rw-r--r--gnu/usr.bin/rcs/rcsdiff/Makefile7
-rw-r--r--gnu/usr.bin/rcs/rcsfreeze/Makefile13
-rw-r--r--gnu/usr.bin/rcs/rcsmerge/Makefile7
-rw-r--r--gnu/usr.bin/rcs/rlog/Makefile7
-rw-r--r--gnu/usr.bin/rcs/rlog/rlog.16
-rw-r--r--gnu/usr.bin/rcs/rlog/rlog.c34
-rw-r--r--gnu/usr.bin/sdiff/Makefile9
-rw-r--r--gnu/usr.bin/sdiff/sdiff.1198
-rw-r--r--gnu/usr.bin/send-pr/COPYING339
-rw-r--r--gnu/usr.bin/send-pr/Makefile29
-rw-r--r--gnu/usr.bin/send-pr/README43
-rw-r--r--gnu/usr.bin/send-pr/categories7
-rwxr-xr-xgnu/usr.bin/send-pr/install-sid.sh82
-rw-r--r--gnu/usr.bin/send-pr/send-pr-el.in744
-rw-r--r--gnu/usr.bin/send-pr/send-pr.1269
-rw-r--r--gnu/usr.bin/send-pr/send-pr.info1604
-rw-r--r--gnu/usr.bin/send-pr/send-pr.sh523
-rw-r--r--gnu/usr.bin/send-pr/send-pr.texi657
-rw-r--r--gnu/usr.bin/sort/COPYING339
-rw-r--r--gnu/usr.bin/sort/Makefile10
-rw-r--r--gnu/usr.bin/sort/error.c116
-rw-r--r--gnu/usr.bin/sort/getopt.c757
-rw-r--r--gnu/usr.bin/sort/getopt.h129
-rw-r--r--gnu/usr.bin/sort/getopt1.c187
-rw-r--r--gnu/usr.bin/sort/long-options.c87
-rw-r--r--gnu/usr.bin/sort/long-options.h1
-rw-r--r--gnu/usr.bin/sort/sort.1231
-rw-r--r--gnu/usr.bin/sort/sort.c1829
-rw-r--r--gnu/usr.bin/sort/system.h200
-rw-r--r--gnu/usr.bin/sort/version.c13
-rw-r--r--gnu/usr.bin/sort/version.h1
-rw-r--r--gnu/usr.bin/tar/COPYING339
-rw-r--r--gnu/usr.bin/tar/ChangeLog1732
-rw-r--r--gnu/usr.bin/tar/Makefile16
-rw-r--r--gnu/usr.bin/tar/Makefile.gnu185
-rw-r--r--gnu/usr.bin/tar/README40
-rw-r--r--gnu/usr.bin/tar/buffer.c1584
-rw-r--r--gnu/usr.bin/tar/create.c1454
-rw-r--r--gnu/usr.bin/tar/diffarch.c759
-rw-r--r--gnu/usr.bin/tar/extract.c943
-rw-r--r--gnu/usr.bin/tar/fnmatch.c173
-rw-r--r--gnu/usr.bin/tar/fnmatch.h62
-rw-r--r--gnu/usr.bin/tar/getdate.y969
-rw-r--r--gnu/usr.bin/tar/getoldopt.c96
-rw-r--r--gnu/usr.bin/tar/getopt.c712
-rw-r--r--gnu/usr.bin/tar/getopt.h125
-rw-r--r--gnu/usr.bin/tar/getopt1.c161
-rw-r--r--gnu/usr.bin/tar/getpagesize.h38
-rw-r--r--gnu/usr.bin/tar/gnu.c677
-rw-r--r--gnu/usr.bin/tar/list.c881
-rw-r--r--gnu/usr.bin/tar/mangle.c270
-rw-r--r--gnu/usr.bin/tar/msd_dir.h44
-rw-r--r--gnu/usr.bin/tar/names.c149
-rw-r--r--gnu/usr.bin/tar/open3.h67
-rw-r--r--gnu/usr.bin/tar/pathmax.h53
-rw-r--r--gnu/usr.bin/tar/port.c1256
-rw-r--r--gnu/usr.bin/tar/port.h215
-rw-r--r--gnu/usr.bin/tar/regex.c4932
-rw-r--r--gnu/usr.bin/tar/regex.h490
-rw-r--r--gnu/usr.bin/tar/rmt.h98
-rw-r--r--gnu/usr.bin/tar/rtapelib.c582
-rw-r--r--gnu/usr.bin/tar/tar.c1507
-rw-r--r--gnu/usr.bin/tar/tar.h292
-rw-r--r--gnu/usr.bin/tar/update.c585
-rw-r--r--gnu/usr.bin/tar/version.c1
-rw-r--r--gnu/usr.bin/tar/y.tab.h18
1484 files changed, 867658 insertions, 3755 deletions
diff --git a/gnu/usr.bin/Makefile b/gnu/usr.bin/Makefile
new file mode 100644
index 0000000..a7ba3a8
--- /dev/null
+++ b/gnu/usr.bin/Makefile
@@ -0,0 +1,6 @@
+# $Id: Makefile,v 1.7 1994/08/24 21:17:33 paul Exp $
+
+SUBDIR= as awk bc cc cpio cvs dc diff diff3 gdb grep groff gzip ld \
+ man patch ptx rcs sdiff send-pr sort tar
+
+.include <bsd.subdir.mk>
diff --git a/gnu/usr.bin/Makefile.inc b/gnu/usr.bin/Makefile.inc
new file mode 100644
index 0000000..5371a22
--- /dev/null
+++ b/gnu/usr.bin/Makefile.inc
@@ -0,0 +1,3 @@
+# $Id$
+
+BINDIR?= /usr/bin
diff --git a/gnu/usr.bin/as/CONTRIBUTORS b/gnu/usr.bin/as/CONTRIBUTORS
new file mode 100644
index 0000000..cfcc7bc
--- /dev/null
+++ b/gnu/usr.bin/as/CONTRIBUTORS
@@ -0,0 +1,11 @@
+(This file under construction).
+
+If you've contributed to gas and your name isn't listed here, it is
+not meant as a slight. I just don't know about it. Email me,
+rich@cygnus.com and I'll correct the situation.
+
+Dean Elsnor wrote the original gas for vax.
+
+Jay Fenalson maintained gas for a while.
+
+K. Richard Pixley currently maintains gas.
diff --git a/gnu/usr.bin/as/COPYING b/gnu/usr.bin/as/COPYING
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/gnu/usr.bin/as/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/gnu/usr.bin/as/ChangeLog b/gnu/usr.bin/as/ChangeLog
new file mode 100644
index 0000000..db77234
--- /dev/null
+++ b/gnu/usr.bin/as/ChangeLog
@@ -0,0 +1,429 @@
+Sun Mar 1 17:02:06 1992 K. Richard Pixley (rich@cygnus.com)
+
+ * README: updated to 1.92.3, included mail announcement.
+
+Sat Feb 29 00:53:16 1992 K. Richard Pixley (rich@cygnus.com)
+
+ * tc-sparc.c (md_apply_fix): relocation overflow checks.
+
+ * atof-generic.c (atof_generic): recognize 99e999 as infinity for
+ older, broken, compilers.
+
+ * version.c: bump to 1.92.3, drop "Cygnus".
+
+ * input-scrub.c (as_where): use myname (which comes from argv[0])
+ as part of all error messages.
+
+ * mess-dose renaming:
+ flonum-copy.c -> flo-copy.c
+ flonum-const.c -> flo-const.c
+ config/a.out.gnu.h -> config/aout.h
+ config/coff.gnu.h -> config/coff.h
+
+ * Makefile.in, obj-aout.h, obj-coff.h: reflect file renaming.
+
+ * output-file.c (output_file_create): add "b" to the fopen to
+ humor mess-dos.
+
+ * configure.in: tahoe needs atof-tahoe.
+
+ * config/tc-tahoe.[hc], config/atof-tahoe.c, opcode/tahoe.h: new
+ files. This is kinda blind cause I don't have anything to run
+ through it or compare against.
+
+ * read.c (read_a_source_file), expr.c (operand): fix a very old
+ bug in label reading exposed by m88k. Also, m88k can't have a
+ pseudo "set".
+
+ * config/m88k.[hc]: freshen copyrights, version 2 gpl, update to
+ current gas.
+
+ * config/m88k-opcode.h moved to opcode/m88k.h
+
+ * read.c: NO_DOT_PSEUDOS from hacks unfinished work.
+
+ * opcode/m68k.h: Sun's JFcc aliases appear to be variable length.
+ Make them so.
+
+ * opcode/a29k.h: remove rcsid.
+
+ * config/te-sun3.h: remove semicolon typo.
+
+ * config/obj-vms.c: another patch from eric youngdale.
+
+ * write.c: white space only.
+
+ * config/tc-i960.c: change from intel for header flags.
+
+ * config/te-sequent.h, config/obj-aout.h: first cut at building
+ sequent headers.
+
+ * config/tc-ns32k.c: patches from Jyrki Kuoppala <jkp@cs.hut.fi>.
+
+ * struct-symbol.h: removed redundant decl of N_TYPE_seg.
+
+ * config/tc-sparc.c (sparc_ip), opcode/sparc.h: changes from chris
+ torek to correct a problem with "neg". some white space.
+
+ * confic/tc-m68k.c: a fix pulled from hack's unfinished work and
+ my mail archives. Try again to get pcrel working. Fix stupid
+ botch on cpu_type comparison.
+
+ * config/tc-sparc.c: .empty pseudo-op from
+ gordoni@cs.adelaide.edu.au.
+
+ * opcode/sparc.h: some new aliases from chris torek.
+
+ * opcode/i386.h: some new aliases and opcodes. also patches from
+ Steve Bleazard <steve@robobar.co.uk>.
+
+ * config/te-hpux.h: new file.
+
+ * configure.in: when targetting hpux, use te-hpux.h.
+
+ * config/obj-aout.c (obj-pre-write-hook), config/obj-bout.[ch]
+ (obj-pre-write-hook), config/obj-coff.[ch] (obj-pre-write-hook),
+ config/obj-generic.h, config/obj-vms.h, write.c
+ (write_object_file): move magic number fiddling out of write.c
+ and into obj-pre-write-hook.
+
+ * config/tc-i860.c: gcc -Wall cleanup.
+
+Fri Feb 28 00:30:36 1992 K. Richard Pixley (rich@rtl.cygnus.com)
+
+ * configure.in: if target is sun3, use te-sun3.h.
+
+ * config/tc-m68k.h, config/te-sun3.h: moved #define of
+ default_magic_number_for_object_file from former to latter.
+
+ * config/te-sun3.h: removed sun_asm_syntax and te_sun3, they
+ aren't used.
+
+ * all: white space changes.
+ " -> " becomes "->"
+ "foo [" becomes "foo["
+ "a . b" becomes "a.b"
+ "\(if\|for\|while\|switch\)(" become "\\1("
+ "\\([^\n]\\)[ \t]*\\([=!+-*/<>]\\)=[ \t]*" become "\\1 \\2= "
+
+ * read.c, write.c, config/tc-i386.c: white space and comments
+ only.
+
+ * config/obj-vms.c: convert PUT_LONG and PUT_SHORT to squirt byte
+ swapped numbers.
+
+ * as.c, flonum-const.c, hex-value.c, input-file.c, version.c,
+ config/obj-aout.h, config/obj-vms.c: VMS -> HO_VMS.
+
+ * config/ho-vms.h: added HO_VMS.
+
+Thu Feb 27 18:25:11 1992 K. Richard Pixley (rich@rtl.cygnus.com)
+
+ * config/ChangeLog: removed. entries merged into this file.
+
+ * config/ho-vms.h: new file. Move the VMS stuff out of ho-vax.h
+ into ho-vms.h.
+
+ * configure.in: use ho-i386v4 for i386-sysvr4.
+
+ * config/ho-i386v4: new file.
+
+Tue Feb 25 19:54:04 1992 (Eric Youngdale at youngdale@v6550c.nrl.navy.mil)
+
+ * config/obj-vms.c (VMS_write_object_file): Add work-around
+ for g++ compiler bug involving external vtables.
+
+Mon Feb 24 22:19:10 1992 (Eric Youngdale at youngdale@v6550c.nrl.navy.mil)
+
+ * README-vms: Describe how to get a VMS obj file to a vms machine
+ via NFS.
+
+ * configure.in: For i386-sysv*, use gas_host=i386.
+
+ * Makefile.in: Remove continuation line markers when the next line
+ is blank.
+
+ * read.c (line_comment_chars): Make external.
+
+ * input-file.c: Remove redundant include of <assert.h>.
+
+ * config/ho-vax.h [VMS]: Include <ctype.h> and <perror.h>.
+
+ * config/obj-vms.h: Remove said includes. Add RELOC_32 to
+ reloc_type to prevent compilation error.
+
+ * config/obj-vms.c: Change bcopy to memcpy throughout.
+ (VMS_local_stab_Parse): Fix typo.
+ (VMS_local_stab_Parse, VMS_RSYM_Parse, Define_Local_Symbols,
+ Define_Routine, VMS_write_object_file): Allow 'f' for functions
+ as well as 'F'.
+
+Mon Feb 24 03:48:04 1992 K. Richard Pixley (rich@cygnus.com)
+
+ * README: updated to reflect current testing status.
+
+ * README.rich, NOTES, NOTES.config: updated slightly, marked as
+ "under construction".
+
+ * CONTRIBUTORS: new file.
+
+ * README-vms: options to configure are now -options=, not
+ +options=.
+
+ * version.c: bumped version to 1.92.2.
+
+Mon Feb 24 03:27:00 1992 Eric Youngdale (youngdale at v6550c.nrl.navy.mil)
+
+ * config.sub: Added vms as a target system. (So people do not
+ have to try to figure out that "vax-dec-vms" would work).
+
+ * configure.in: Added vms as a target os, and object file format.
+ (Useless on a vms system, but this is for people who want to
+ cross assemble).
+
+ * config-gas.com: New file. Script for VMS systems to set up the
+ configuration to build gas for VMS, and create config.status.
+
+ * make-gas.com: Redone to work with the bfd-gas scheme.
+
+ * as.c: Add const modifier to version_string.
+
+ * atof-vax.c: Remove redundant include of flonum.h. (This is also
+ included via as.h).
+
+ * expr.c: Add "const" modifier to hex_value.
+
+ * read.c: Add "const" modifier to line_comment_chars, and
+ line_separator_chars. Make use of the -1 switch for backward
+ compatibility with gcc 1.nn.
+ (s_ignore): remove redundant declaration of is_end_of_line.
+
+ * symbols.c: Finish conversion to S_* macros in the VMS only
+ parts of the program. Add "const" modifier to
+ md_[long,short]_jump_size. Remove declaration of const_flag
+ (which will be declared in obj-vms.h).
+
+ * write.c: Add "const" modifier to md_[long,short]_jump_size.
+ Fix arguments to VMS_write_object_file.
+
+ * obj-vms.h: New file (sort of). Mostly canibalized from other
+ files, using:
+
+ - objrecdef.h: Removed structure definition that we do not use,
+ and removed dollar signs from identifiers, since Unix System 5
+ does not like them.
+
+ - obj-aout.h: Took S_*, some H_* macros, and a number of
+ symbol definitions.
+
+ - a.out.hp.h: Took nlist structure. We do not really use this
+ per se, but it is easiest to let gas think that we do. When we
+ write the object file, we just pick out the parts that we need.
+
+ - stab.h: Just included it, since on non VMS and non a.out systems
+ we have no guarantee of having it. (Define N_* symbols).
+
+ *obj-vms.c: Renamed from vms.c. Did the following:
+
+ - Reworked to use the S_* macros.
+
+ - Add "const" modifier to version_string.
+
+ - Added global[ref,def,value] support
+
+ - (VMS_Store_PIC_Symbol_Reference):fix a bug with static constants.
+
+ - Remove a few redunant includes - all are now included through as.h.
+
+ - (obj_crawl_symbol_chain): Clean up (a lot), and remove non-VMS
+ code. Add definition for obj_read_begin_hook.
+
+ - Borrow the stab[s,d,n] routines from obj-aout.c.
+
+ - Borrow the seg_N_TYPE and N_TYPE_seg arrays from aout.c
+
+ - Use <fab.h>,<rab.h> and <xab.h> instead of <vms/fabdef.h>
+ <vms/rabdef.h> and <vms/xabdef.h>, for more consistent results.
+ (Some peoples <vms/*.h> files are different than others).
+
+ - Merged vms-dbg.c into obj-vms.c. Modified to use
+ the S_* macros. Added code to remove the psect hack from
+ variable names before writing them to the debugger records.
+
+
+
+ The following patches make cross assembly possible.
+
+ * as.c, read.c, symbols.c, write.c: Change "ifdef VMS" to
+ "ifdef OBJ_VMS".
+
+ * vms.c:
+
+ - Wrap the #include of some VMS system dependent headers
+ with "ifdef VMS".
+
+ - (get_VMS_time_on_unix): Add new routine. Generates current
+ time in VMS format to be written into object file.
+
+ - (Write_VMS_MHD_Records): Use get_VMS_time_on_unix if we are not
+ running on a VMS system.
+
+ - (Flush_VMS_Object_Record_Buffer): Add code to write correct
+ record format when running on a non-VMS system.
+
+ - (Create_VMS_Object_File): Use different mode if running under
+ unix.
+
+ - (VMS_TBT_Source_File): If we are not running on a VMS system,
+ write a source file record for the debugger that looks reasonable.
+
+Mon Feb 24 02:06:00 1992 K. Richard Pixley (rich@cygnus.com)
+
+ * Makefile.in: remove $(srcdir)/../include from INCLUDES. It
+ isn't needed.
+
+ * README: updated with current state.
+
+ * read.c (stringer): read arbitrary expressions between the commas
+ and treat them as ".byte" values. At least some i860 assembler
+ does this so now we do too. Also white space throughout.
+
+ * expr.c, expr.h, frags.c, symbols.c, write.c: white space only.
+
+Mon Feb 24 01:45:40 1992 K. Richard Pixley (rich@cygnus.com)
+
+ * config/te-sequent.h, config/tc-ns32k.h, config/tc-ns32k.c:
+ SEQUENT_COMPATIBILITY -> TE_SEQUENT.
+
+ * config/obj-aout.c: if OLD_GAS and i386, then screw up the magic
+ number.
+
+ * config/obj-bout.c: do not include aout/stab_gnu.h if NO_LISTING.
+
+ * config/obj-bout.h: added enum reloc_type.
+
+ * config/tc-i386.c: on OLD_GAS, .align is power of two, rather
+ than bytes.
+
+ * config/tc-i386.h: on OLD_GAS, the filler byte should be zero
+ rather than NOOP.
+
+ * config/tc-i860.c: relocs are 12bytes on this target. Also white
+ space.
+
+ * config/tc-m68kmote.c: removed. Not ready yet.
+
+ * config/a.out.gnu.h, config/tc-a29k.c, config/tc-m68k.c,
+ config/tc-ns32k.c: white space only.
+
+ * config/tc-a29k.h, config/tc-i860.h, config/tc-i960.h,
+ config/tc-m68k.h, config/tc-ns32k.h, config/tc-sparc.h,
+ config/tc-vax.h: NO_LISTING
+
+ * config/tc-m68k.h, config/tc-i860.h, config/tc-vax.h:
+ REVERSE_SORT_RELOCS if OLD_GAS.
+
+ * config/mt-m68k: removed. not needed.
+
+Fri Feb 21 06:22:15 1992 K. Richard Pixley (rich@rtl.cygnus.com)
+
+ * config/obj-aout.c: do not include stab.gnu.h if NO_LISTING.
+
+ * config/tc-i860.c, config/a.out.gnu.h: move i860 relocs to a proper place.
+
+ * config/a.out.h: removed.
+
+Fri Feb 21 06:21:07 1992 K. Richard Pixley (rich@rtl.cygnus.com)
+
+ * Makefile.in: put header files before C source for TAGS; remove
+ references to non-existent syscalls.h.
+
+ * read.c, write.c subsegs.c: back out the .bss changes.
+
+Fri Feb 21 02:17:22 1992 Minh Tran-Le (TRANLE@INTELLICORP.COM)
+
+ * config/tc-i386.c: config/tc-i386.c: added handling of the
+ following opcodes: i/o opcodes - inb, inw, outb and outw.
+ string manipulation with att syntax - scmp, slod, smov, ssca,
+ ssto.
+
+Fri Feb 21 01:53:50 1992 Minh Tran-Le (TRANLE@INTELLICORP.COM)
+
+ * config/obj-coff.c: (for aix386) Moved the symbols .text, .data
+ and .bss to just after .file .
+
+ In obj_crawl_symbol_chain() where it tries to put the external
+ symbols apart, with the condition:
+ (!S_IS_DEFINED(symbolP) &&
+ !S_IS_DEBUG(symbolP) &&
+ !SF_GET_STATICS(symbolP))
+ it was moving too many symbols out. So I switch it back to the
+ condition:
+ (S_GET_STORAGE_CLASS(symbolP) == C_EXT && !SF_GET_FUNCTION(symbolP))
+
+ In obj_emit_relocations() added the conditional on KEEP_RELOC_INFO
+ so that we don't use the F_RELFLG which make the linker complain
+ that somebody has stripped the relocation info.
+
+ Also, the AIX ld program require that the relocation table
+ is sorted by r_vaddr like the standard ATT assembler does.
+
+ [he also changed the sizeof(struct ...)'s into the coff
+ style FOOSZ macros. I'm not sure this is right, but I can't
+ remember why. xoxorich.]
+
+Fri Feb 21 01:08:48 1992 Minh Tran-Le (TRANLE@INTELLICORP.COM)
+
+ * symbols.c (local_label_name): symbols now start with ^A.
+
+ * read.c, subsegs.c, write.c obj-coff.c: added handling of
+ `.bss` pseudo op for unitialized data. The new gcc (1.37.9x)
+ generate these sections. .align: will use NOP_OPCODE or 0
+ for padding. This is just for being nice to the
+ disassembler.
+
+ * expr.c (operand): changed to generate local label "\001L0"
+ starting with a ^A so that it is recognized as a local label.
+
+ * as.c (perform_an_assembly_pass): zero bss_fix_root, too.
+
+Fri Feb 21 01:08:48 1992 K. Richard Pixley (rich@cygnus.com)
+
+ * Makefile.in, configure.in, doc: use the doc. Build it, install
+ it, clean it, etc.
+
+Tue Feb 18 02:21:25 1992 K. Richard Pixley (rich at cygnus.com)
+
+ * read.c: white space and comments only.
+
+ * configure.in: use the new atof-ns32.c for ns32k.
+
+ * write.c: comment change only.
+
+Tue Feb 18 02:11:10 1992 K. Richard Pixley (rich at cygnus.com)
+
+ * config/tc-m88k.[hc]: pulled in from hack's unfinished work. These
+ aren't yet integrated.
+
+ * config/tc-i860.[hc]: blew off the dust. Something must still be
+ done about conflicting relocation types.
+
+ * config/tc-ns32k.c: Replaced previous tc_aout_fix_to_chars stub
+ with the real thing.
+
+ * config/tc-i960.c, tc-sparc.c: white space and comments only.
+
+ * config/tc-a29k.h: delete duplicate macro definition.
+
+ * new file config/atof-ns32k.c copied from hack's last unreleased
+ gas.
+
+Mon Feb 17 07:51:06 1992 K. Richard Pixley (rich at cygnus.com)
+
+ * config/tc-ns32k.c: actually make tc_aout_fix_to_chars work
+ rather than abort.
+
+ * nearly everything. flush ChangeLog, package as gas-1.92.1.
+ ChangeLog's prior to this are sketchy at best. I have logs.
+ They just aren't ChangeLogs.
+
diff --git a/gnu/usr.bin/as/Makefile b/gnu/usr.bin/as/Makefile
new file mode 100644
index 0000000..fd4dc30
--- /dev/null
+++ b/gnu/usr.bin/as/Makefile
@@ -0,0 +1,73 @@
+# from: @(#)Makefile 6.1 (Berkeley) 3/3/91
+# $Id: Makefile,v 1.4 1994/08/28 17:43:37 bde Exp $
+
+.include "config/Makefile.$(MACHINE)"
+
+.if !defined (gas_hosttype)
+gas_hosttype=$(MACHINE)
+.endif
+.if !defined (gas_target)
+gas_target=$(MACHINE)
+.endif
+.if !defined (gas_objformat)
+gas_objformat=aout
+.endif
+
+.if exists(${.CURDIR}/obj)
+ADDINCLUDE=-I${.CURDIR}/obj
+.endif
+
+PROG= as
+SRCS+= app.c as.c atof-generic.c bignum-copy.c \
+ cond.c expr.c flo-const.c flo-copy.c flonum-mult.c \
+ frags.c hash.c hex-value.c input-file.c input-scrub.c \
+ listing.c messages.c obstack.c output-file.c read.c subsegs.c \
+ symbols.c version.c write.c xmalloc.c xrealloc.c \
+ obj-$(gas_objformat).c
+CFLAGS+= -I$(.CURDIR) ${ADDINCLUDE} -I$(.CURDIR)/config \
+ -DPIC -DOLD_GAS -DSIGTY=void -Derror=as_fatal \
+ -DSUB_SEGMENT_ALIGN=4
+DPADD+= ${LIBGNUMALLOC}
+LDADD+= -lgnumalloc
+
+CONF_HEADERS= targ-cpu.h obj-format.h host.h targ-env.h
+
+.PATH: $(.CURDIR)/config
+
+beforedepend ${PROG}: ${CONF_HEADERS}
+
+targ-cpu.h: Makefile config/Makefile.$(MACHINE)
+ @cmp -s $(.CURDIR)/config/tc-$(gas_target).h targ-cpu.h || \
+ ( ${ECHO} "updating ${.TARGET}..." ; /bin/rm -f targ-cpu.h ; \
+ cp $(.CURDIR)/config/tc-$(gas_target).h targ-cpu.h )
+
+obj-format.h: Makefile config/Makefile.$(MACHINE)
+ @cmp -s $(.CURDIR)/config/obj-$(gas_objformat).h obj-format.h || \
+ ( ${ECHO} "updating ${.TARGET}..." ; /bin/rm -f obj-format.h ; \
+ cp $(.CURDIR)/config/obj-$(gas_objformat).h obj-format.h )
+
+.if exists ($(.CURDIR)/config/ho-$(gas_hosttype).h)
+config_hostfile= $(.CURDIR)/config/ho-$(gas_hosttype).h
+.else
+config_hostfile= $(.CURDIR)/config/ho-generic.h
+.endif
+
+host.h: Makefile config/Makefile.$(MACHINE)
+ @cmp -s $(config_hostfile) host.h || \
+ ( ${ECHO} "updating ${.TARGET}..." ; /bin/rm -f host.h ; \
+ cp $(config_hostfile) host.h )
+
+.if exists ($(.CURDIR)/config/te-$(MACHINE).h)
+config_targenvfile= $(.CURDIR)/config/te-$(MACHINE).h
+.else
+config_targenvfile= $(.CURDIR)/config/te-generic.h
+.endif
+
+targ-env.h: Makefile config/Makefile.$(MACHINE)
+ @cmp -s $(config_targenvfile) targ-env.h || \
+ ( ${ECHO} "updating ${.TARGET}..." ; /bin/rm -f targ-env.h ; \
+ cp $(config_targenvfile) targ-env.h )
+
+CLEANFILES+= ${CONF_HEADERS}
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/as/Makefile.gnu b/gnu/usr.bin/as/Makefile.gnu
new file mode 100644
index 0000000..4b81b0c
--- /dev/null
+++ b/gnu/usr.bin/as/Makefile.gnu
@@ -0,0 +1,356 @@
+# Makefile for GAS.
+# Copyright (C) 1989, Free Software Foundation
+#
+# This file is part of GAS, the GNU Assembler.
+#
+# GAS is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 1, or (at your option)
+# any later version.
+#
+# GAS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GAS; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# This makefile may be used to make the VAX, 68020, 80386,
+# SPARC, ns32k, or i860 assembler(s).
+
+BINDIR = /usr/local/bin
+#CC=gcc
+
+# If you are on a BSD system, un-comment the next two lines, and comment out
+# the lines for SystemV and HPUX below
+G0 = -g -I. #-O -Wall
+LDFLAGS = $(CFLAGS)
+#
+# To compile gas on a System Five machine, comment out the two lines above
+# and un-comment out the next three lines
+# Comment out the -lPW on the LOADLIBES line if you are using GCC.
+# G0 = -g -I. -DUSG
+# LDFLAGS = $(CFLAGS)
+# LOADLIBES = -lmalloc -lPW
+#
+# To compile gas for HPUX, link m-hpux.h to m68k.h , and un-comment the
+# next two lines. (If you are using GCC, comment out the alloca.o part)
+# (Get alloca from the emacs distribution, or use GCC.)
+# HPUX 7.0 may have a bug in setvbuf. gas gives an error message like
+# 1:"Unknown operator" -- Statement 'NO_APP' ignored
+# if setvbuf is broken. Re-compile input-file.c (and only input-file.c
+# with -DVMS and the problem should go away.
+#
+# G0 = -g -I. -DUSG
+# LOADLIBES = alloca.o
+#
+# To compile gas for a Sequent Symmetry, comment out all the above lines,
+# and un-comment the next two lines.
+# G0 = -g -I. -DUSE_SYSTEM_HDR -DEXEC_VERSION=1
+# LOADLIBES = -lc /usr/att/lib/libc.a
+
+# If you just want to compile the vax assembler, type 'make avax'
+
+# If you just want to compile the i386 assembler, type 'make a386'
+
+# If you just want to compile the ns32k assembler, type 'make a32k'
+
+# If you just want to compile the sparc assembler, type 'make asparc'
+
+# If you just want to compile the mc68020 assembler, make sure m68k.h
+# is correctly set up, and type type 'make a68' (Except on HPUX machines,
+# where you will have to make the changes marked below before typing
+# 'make a68'
+# m68k.h should be a symbolic or hard-link to one of
+# m-sun3.h , m-hpux.h or m-generic.h
+# depending on which machine you want to compile the 68020 assembler for.
+#
+# If you want the 68k assembler to be completely compatable with the the
+# SUN one, un-comment the -DSUN_ASM_SYNTAX line below.
+#
+# If you machine does not have vfprintf, but does have _doprnt(),
+# remove the # from the -DNO_VARARGS line below.
+#
+# If the return-type of a signal-hander is void (instead of int),
+# remove the # from the -DSIGTY line below.
+#
+# To include the mc68851 mmu coprocessor instructions in the 68020 assembler,
+# remove the # from the -Dm68851 line below.
+#
+# If you want the 68020 assembler use a register prefix character, un-comment
+# the REGISTER_PREFIX line, and (maybe) change the '%' to the appropriate
+# character.
+#
+# If you want the assembler to treat .L* or ..* symbols as local, instead of
+# the usual L* symbols, un-comment the DOT_LABEL_PREFIX line.
+#
+# If you want the 80386 assembler to correctly handle fsub/fsubr and fdiv/fdivr
+# opcodes (unlike most 80386 assemblers), remove the # from
+# the -DNON_BROKEN_WORDS line below.
+#
+# To compile 80386 Gas for the Sequent Symmetry, un-comment the -DEXEC_VERSION
+# and the -DUSE_SYSTEM_HDR lines below.
+#
+# To compile gas for the HP 9000/300 un-comment the -DUSE_HP_HDR line below.
+#
+# For the ns32k, the options are 32532 or 32032 CPU and 32381 or 32081 FPU.
+# To select the NS32532, remove the # from the -DNS32532 line below.
+# To compile in tne NS32381 opcodes in addition to the NS32081 opcodes
+# (the 32381 is a superset of the 32081), remove the # from the -DNS32381
+# line below.
+#
+# For the ns32k on a Sequent, uncomment the SEQUENT_COMPATABILITY line below.
+#
+# If you want the .align N directive to align to the next N byte boundry,
+# instead of the next 1<<N boundry, un-comment the OTHER_ALIGN line below.
+# (This option is automatically enabled when building the sparc assembler.
+#
+
+O1 = -DNO_VARARGS
+O2 = # -DNON_BROKEN_WORDS
+O3 = # -Dm68851
+O4 = # -DEXEC_VERSION=1
+O5 = # -DSIGTY=void
+O6 = # -DNS32532
+O6 = # -DNS32381
+O7 = # -DDOT_LABEL_PREFIX
+O8 = # -DSEQUENT_COMPATABILITY
+O9 = # -DREGISTER_PREFIX=\'%\'
+O10= # -DOTHER_ALIGN
+
+G1 = # -DUSE_SYSTEM_HDR
+G2 = # -DUSE_HP_HDR
+G3 = # -DSUN_ASM_SYNTAX
+
+OPTIONS = $(O1) $(O2) $(O3) $(O4) $(O5) $(O6) $(O7) $(O8) $(O9) $(O10)
+
+CFLAGS = $(G0) $(G1) $(G2) $(G3) $(G4)
+
+#
+# To make the 68020 assembler compile as the default, un-comment the next
+# line, and comment out all the other lines that start with DEFAULT_GAS
+DEFAULT_GAS=a68
+#
+# To make the VAX assembler compile as the default, un-comment the next
+# line and commment out all the other lines that start with DEFAULT_GAS
+#DEFAULT_GAS=avax
+#
+# To make the 80386 assembler compile as the default, un-comment the next
+# line and commment out all the other lines that start with DEFAULT_GAS
+#DEFAULT_GAS=a386
+#
+# To make the ns32k assembler compile as the default, un-comment the next
+# line and commment out all the other lines that start with DEFAULT_GAS
+#DEFAULT_GAS=a32k
+#
+# To make the sparc assembler compile as the default, un-comment the next
+# line and commment out all the other lines that start with DEFAULT_GAS
+#DEFAULT_GAS=asparc
+#
+# To make the i860 assembler compile as the default, un-comment the next
+# line and comment out all the other lines that start with DEFAULT_GAS
+#DEFAULT_GAS=a860
+
+# Global Sources -------------------------------------------------------------
+
+a =\
+as.o xrealloc.o xmalloc.o hash.o hex-value.o \
+atof-generic.o append.o messages.o expr.o app.o \
+frags.o input-file.o input-scrub.o output-file.o \
+subsegs.o symbols.o version.o \
+flonum-const.o flonum-copy.o flonum-mult.o strstr.o bignum-copy.o \
+obstack.o
+#gdb.o gdb-file.o gdb-symbols.o gdb-blocks.o gdb-lines.o
+
+a: $(DEFAULT_GAS)
+ @rm -f a
+ @ln $(DEFAULT_GAS) a
+
+# I860 GAS ------------------------------------------------------------------
+u = i860.o atof-ieee.o write-i860.o read-i860.o
+
+U = i860.c i860.h i860-opcode.h
+
+i860.o: i860.c i860.h i860-opcode.h as.h frags.h struc-symbol.h
+i860.o: flonum.h expr.h hash.h md.h write.h read.h symbols.h
+ $(CC) -c $(CFLAGS) -DI860 i860.c
+
+atof-ieee.o: flonum.h
+
+write-i860.o: write.c i860.h
+ $(CC) -c -DI860 $(CFLAGS) write.c
+ mv write.o write-i860.o
+
+read-i860.o: read.c i860.h
+ $(CC) -c -DI860 $(CFLAGS) read.c
+ mv read.o read-i860.o
+
+a860: $a $u
+ $(CC) -o a860 $(LDFLAGS) $a $u $(LOADLIBES)
+
+# SPARC GAS ------------------------------------------------------------------
+v = sparc.o atof-ieee.o write-sparc.o read-sparc.o
+
+V = sparc.c sparc.h sparc-opcode.h
+
+atof-ieee.o: flonum.h
+sparc.o: sparc.c sparc.h sparc-opcode.h as.h frags.h struc-symbol.h
+sparc.o: flonum.h expr.h hash.h md.h write.h read.h symbols.h
+ $(CC) -c $(CFLAGS) -DSPARC sparc.c
+
+write-sparc.o: write.c
+ $(CC) -c -DSPARC $(CFLAGS) write.c
+ mv write.o write-sparc.o
+
+read-sparc.o: read.c
+ $(CC) -c -DSPARC $(CFLAGS) read.c
+ mv read.o read-sparc.o
+
+asparc: $a $v
+ $(CC) -o asparc $(LDFLAGS) $a $v $(LOADLIBES)
+
+# NS32K GAS ------------------------------------------------------------------
+w = ns32k.o atof-ieee.o write-ns32k.o read-ns32k.o
+
+W = ns32k.c ns32k-opcode.h
+
+atof-ieee.o: flonum.h
+ns32k.o: as.h frags.h struc-symbol.h flonum.h expr.h md.h hash.h
+ns32k.o: write.h symbols.h ns32k-opcode.h ns32k.c
+ $(CC) $(CFLAGS) $(OPTIONS) -c ns32k.c
+
+write-ns32k.o: write.c
+ $(CC) -c -DNS32K $(CFLAGS) write.c
+ mv write.o write-ns32k.o
+
+read-ns32k.o:
+ $(CC) -c -DNS32K $(CFLAGS) read.c
+ mv read.o read-ns32k.o
+
+a32k: $a $w
+ $(CC) -o a32k $(LDFLAGS) $a $w $(LOADLIBES)
+
+# 80386 GAS ------------------------------------------------------------------
+x = i386.o atof-ieee.o write.o read.o
+
+X = i386.c i386.h i386-opcode.h
+
+i386.o: i386.c as.h read.h flonum.h frags.h struc-symbol.h expr.h
+i386.o: symbols.h hash.h md.h i386.h i386-opcode.h
+ $(CC) $(CFLAGS) $(OPTIONS) -c i386.c
+
+atof-ieee.o: flonum.h
+
+a386: $a $x
+ $(CC) -o a386 $(LDFLAGS) $a $x $(LOADLIBES)
+
+# 68020 GAS ------------------------------------------------------------------
+y = m68k.o atof-ieee.o write.o read.o
+
+Y = m68k.c atof-ieee.c m68k-opcode.h m-hpux.h m-sun3.h m-generic.h
+
+atof-ieee.o: flonum.h
+
+m68k.o: m68k.c a.out.gnu.h as.h expr.h flonum.h frags.h hash.h
+m68k.o: m68k-opcode.h m68k.h md.h obstack.h struc-symbol.h
+ $(CC) $(CFLAGS) $(OPTIONS) -c m68k.c
+
+a68: $a $y
+ $(CC) -o a68 $(LDFLAGS) $a $y $(LOADLIBES)
+
+# VAX GAS --------------------------------------------------------------------
+z = vax.o atof-vax.o write.o read.o
+
+Z = vax.c atof-vax.c vax-opcode.h vax-inst.h \
+ make-gas.com objrecdef.h vms.c vms-dbg.c README-vms-dbg
+
+vax.o: vax.c a.out.gnu.h as.h expr.h flonum.h frags.h md.h obstack.h
+vax.o: read.h struc-symbol.h symbols.h vax-inst.h vax-opcode.h
+atof-vax.o: as.h flonum.h read.h
+
+avax: $a $z
+ $(CC) -o avax $(LDFLAGS) $a $z $(LOADLIBES)
+
+# global files ---------------------------------------------------------------
+
+as.o: as.c
+ $(CC) $(CFLAGS) $(OPTIONS) -c as.c
+
+messages.o: messages.c
+ $(CC) $(CFLAGS) $(OPTIONS) -c messages.c
+
+hash.o: hash.c
+ $(CC) $(CFLAGS) -Derror=as_fatal -c hash.c
+
+xmalloc.o: xmalloc.c
+ $(CC) $(CFLAGS) -Derror=as_fatal -c xmalloc.c
+
+xrealloc.o: xrealloc.c
+ $(CC) $(CFLAGS) -Derror=as_fatal -c xrealloc.c
+
+A =\
+as.c xrealloc.c xmalloc.c hash.c hex-value.c \
+atof-generic.c append.c messages.c expr.c bignum-copy.c \
+frags.c input-file.c input-scrub.c output-file.c read.c \
+subsegs.c symbols.c write.c strstr.c \
+flonum-const.c flonum-copy.c flonum-mult.c app.c version.c \
+obstack.c \
+#gdb.c gdb-file.c gdb-symbols.c gdb-blocks.c \
+#gdb-lines.c
+
+H = \
+a.out.gnu.h as.h bignum.h expr.h flonum.h \
+frags.h hash.h input-file.h md.h \
+obstack.h read.h struc-symbol.h subsegs.h \
+symbols.h write.h
+
+dist: COPYING README ChangeLog $A $H $Z $Y $X $W $V $U Makefile
+ echo gas-`sed -n -e '/ version /s/[^0-9.]*\([0-9.]*\).*/\1/p' < version.c` > .fname
+ mkdir `cat .fname`
+
+ ln COPYING README ChangeLog $A $H $Z $Y $X $W $V $U Makefile `cat .fname`
+ tar cvhZf `cat .fname`.tar.Z `cat .fname`
+ -rm -r `cat .fname`
+ -rm .fname
+
+clean:
+ rm -f a avax a68 a386 a32k asparc $a $v $w $x $y $z a core gmon.out bugs a.out
+
+install: a
+ cp a $(BINDIR)/gas
+
+
+# General .o-->.h dependencies
+
+app.o: as.h
+as.o: a.out.gnu.h as.h read.h struc-symbol.h write.h
+atof-generic.o: flonum.h
+bignum-copy.o: bignum.h
+expr.o: a.out.gnu.h as.h expr.h flonum.h obstack.h read.h struc-symbol.h
+expr.o: symbols.h
+flonum-const.o: flonum.h
+flonum-copy.o: flonum.h
+flonum-mult.o: flonum.h
+flonum-normal.o:flonum.h
+flonum-print.o: flonum.h
+frags.o: a.out.gnu.h as.h frags.h obstack.h struc-symbol.h subsegs.h
+#gdb.o: as.h
+#gdb-blocks.o: as.h
+#gdb-lines.o: as.h frags.h obstack.h
+#gdb-symbols.o: a.out.gnu.h as.h struc-symbol.h
+hash.o: hash.h
+input-file.o: input-file.h
+input-scrub.o: as.h input-file.h read.h
+messages.o: as.h
+obstack.o: obstack.h
+read.o: a.out.gnu.h as.h expr.h flonum.h frags.h hash.h md.h obstack.h
+read.o: read.h struc-symbol.h symbols.h
+subsegs.o: a.out.gnu.h as.h frags.h obstack.h struc-symbol.h subsegs.h write.h
+symbols.o: a.out.gnu.h as.h frags.h hash.h obstack.h struc-symbol.h symbols.h
+write.o: a.out.gnu.h as.h md.h obstack.h struc-symbol.h subsegs.h
+write.o: symbols.h write.h
+
+flonum.h: bignum.h
+
diff --git a/gnu/usr.bin/as/Makefile.in b/gnu/usr.bin/as/Makefile.in
new file mode 100644
index 0000000..1497b1f
--- /dev/null
+++ b/gnu/usr.bin/as/Makefile.in
@@ -0,0 +1,409 @@
+# Makefile for GNU Assembler
+# Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+#This file is part of GNU GAS.
+
+#GNU GAS is free software; you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation; either version 2, or (at your option)
+#any later version.
+
+#GNU GAS is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+
+#You should have received a copy of the GNU General Public License
+#along with GNU GAS; see the file COPYING. If not, write to
+#the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# The targets for external use include:
+# all, doc, proto, install, uninstall, includes, TAGS,
+# clean, cleanconfig, realclean, stage1, stage2, stage3, stage4.
+
+# Variables that exist for you to override.
+# See below for how to change them for certain systems.
+
+srcdir = .
+
+prefix = /usr/local
+
+bindir = $(prefix)/bin
+datadir = $(prefix)/lib
+libdir = $(prefix)/lib
+mandir = $(datadir)/man
+man1dir = $(mandir)/man1
+man2dir = $(mandir)/man2
+man3dir = $(mandir)/man3
+man4dir = $(mandir)/man4
+man5dir = $(mandir)/man5
+man6dir = $(mandir)/man6
+man7dir = $(mandir)/man7
+man8dir = $(mandir)/man8
+man9dir = $(mandir)/man9
+infodir = $(datadir)/info
+includedir = $(prefix)/include
+docdir = $(datadir)/doc
+
+SHELL = /bin/sh
+
+INSTALL = install -c
+INSTALL_PROGRAM = $(INSTALL)
+INSTALL_DATA = $(INSTALL)
+
+AR = ar
+AR_FLAGS = qv
+BISON = bison
+MAKEINFO = makeinfo
+RANLIB = ranlib
+MINUS_G = -g
+
+# Lists of files for various purposes.
+
+REAL_SOURCES = \
+ $(srcdir)/app.c \
+ $(srcdir)/as.c \
+ $(srcdir)/atof-generic.c \
+ $(srcdir)/bignum-copy.c \
+ $(srcdir)/cond.c \
+ $(srcdir)/expr.c \
+ $(srcdir)/flo-const.c \
+ $(srcdir)/flo-copy.c \
+ $(srcdir)/flonum-mult.c \
+ $(srcdir)/frags.c \
+ $(srcdir)/hash.c \
+ $(srcdir)/hex-value.c \
+ $(srcdir)/input-file.c \
+ $(srcdir)/input-scrub.c \
+ $(srcdir)/messages.c \
+ $(srcdir)/obstack.c \
+ $(srcdir)/output-file.c \
+ $(srcdir)/read.c \
+ $(srcdir)/strerror.c \
+ $(srcdir)/strstr.c \
+ $(srcdir)/subsegs.c \
+ $(srcdir)/symbols.c \
+ $(srcdir)/version.c \
+ $(srcdir)/write.c \
+ $(srcdir)/listing.c \
+ $(srcdir)/xmalloc.c \
+ $(srcdir)/xrealloc.c
+
+# in an expedient order
+LINKED_SOURCES = \
+ targ-cpu.c \
+ obj-format.c \
+ atof-targ.c
+
+SOURCES = $(LINKED_SOURCES) $(REAL_SOURCES)
+
+REAL_HEADERS = \
+ $(srcdir)/as.h \
+ $(srcdir)/bignum.h \
+ $(srcdir)/expr.h \
+ $(srcdir)/flonum.h \
+ $(srcdir)/frags.h \
+ $(srcdir)/hash.h \
+ $(srcdir)/input-file.h \
+ $(srcdir)/listing.h \
+ $(srcdir)/tc.h \
+ $(srcdir)/obj.h \
+ $(srcdir)/obstack.h \
+ $(srcdir)/read.h \
+ $(srcdir)/struc-symbol.h \
+ $(srcdir)/subsegs.h \
+ $(srcdir)/symbols.h \
+ $(srcdir)/write.h
+
+LINKED_HEADERS = \
+ a.out.gnu.h \
+ a.out.h \
+ host.h \
+ targ-env.h \
+ targ-cpu.h \
+ obj-format.h \
+ atof-targ.h
+
+HEADERS = $(LINKED_HEADERS) $(REAL_HEADERS)
+
+OBJS = \
+ targ-cpu.o \
+ obj-format.o \
+ atof-targ.o \
+ app.o \
+ as.o \
+ atof-generic.o \
+ bignum-copy.o \
+ cond.o \
+ expr.o \
+ flo-const.o \
+ flo-copy.o \
+ flonum-mult.o \
+ frags.o \
+ hash.o \
+ hex-value.o \
+ input-file.o \
+ input-scrub.o \
+ messages.o \
+ obstack.o \
+ output-file.o \
+ read.o \
+ strerror.o \
+ strstr.o \
+ subsegs.o \
+ symbols.o \
+ version.o \
+ write.o \
+ listing.o \
+ xmalloc.o \
+ xrealloc.o
+
+#### host, target, and site specific Makefile frags come in here.
+
+all: as.new
+ (cd doc ; $(MAKE) all)
+
+info:
+ (cd doc ; $(MAKE) info)
+
+install-info:
+ (cd doc ; $(MAKE) install-info)
+
+clean-info:
+ (cd doc ; $(MAKE) clean-info)
+
+# Now figure out from those variables how to compile and link.
+
+# This is the variable actually used when we compile.
+ALL_CFLAGS = $(MINUS_G) $(INTERNAL_CFLAGS) $(CFLAGS) $(HDEFINES) $(TDEFINES) -DPIC -DOLD_GAS
+
+# How to link with both our special library facilities
+# and the system's installed libraries.
+
+LIBS = $(HLIBS)
+
+# Specify the directories to be searched for header files.
+# Both . and srcdir are used, in that order,
+# so that tm.h and config.h will be found in the compilation
+# subdirectory rather than in the source directory.
+INCLUDES = -I. -I$(srcdir) -I$(srcdir)/config # -I$(srcdir)/../include
+SUBDIR_INCLUDES = -I.. -I$(srcdir) -I$(srcdir)/config
+
+# Always use -I$(srcdir)/config when compiling.
+.c.o:
+ $(CC) -c $(ALL_CFLAGS) $(CPPFLAGS) $(INCLUDES) $<
+
+# This tells GNU make version 3 not to export all the variables
+# defined in this file into the environment.
+.NOEXPORT:
+
+# Files to be copied away after each stage in building.
+STAGESTUFF = *.o as.new
+
+as.new: $(OBJS) $(LIBDEPS)
+ -mv -f as.new as.old
+ $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o as.new $(OBJS) $(LIBS)
+
+config.status:
+ @echo You must configure gas. Look at the INSTALL file for details.
+ @false
+
+# Compiling object files from source files.
+
+app.o : app.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h
+as.o : as.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h
+atof-generic.o : atof-generic.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h
+bignum-copy.o : bignum-copy.c as.h host.h \
+ targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h
+cond.o : cond.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h
+
+debug.o : debug.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \
+ subsegs.h
+expr.o : expr.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h
+
+flo-const.o : flo-const.c flonum.h bignum.h
+flo-copy.o : flo-copy.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h
+flonum-mult.o : flonum-mult.c flonum.h bignum.h
+frags.o : frags.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \
+ subsegs.h
+hash.o : hash.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h
+hex-value.o : hex-value.c
+input-file.o : input-file.c as.h host.h \
+ targ-env.h obj-format.h targ-cpu.h \
+ struc-symbol.h write.h flonum.h bignum.h expr.h \
+ frags.h hash.h read.h symbols.h tc.h obj.h input-file.h
+input-scrub.o : input-scrub.c /usr/include/errno.h /usr/include/sys/errno.h \
+ as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \
+ input-file.h
+listing.o : listing.c as.h host.h targ-env.h flonum.h bignum.h \
+ listing.h obj-format.h targ-cpu.h struc-symbol.h write.h expr.h \
+ frags.h hash.h read.h symbols.h tc.h obj.h input-file.h
+messages.o : messages.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h
+obstack.o : obstack.c
+output-file.o : output-file.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \
+ output-file.h
+read.o : read.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h
+
+strstr.o : strstr.c
+subsegs.o : subsegs.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \
+ subsegs.h
+symbols.o : symbols.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \
+ subsegs.h
+version.o : version.c
+write.o : write.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \
+ subsegs.h output-file.h
+xmalloc.o : xmalloc.c
+xrealloc.o : xrealloc.c
+atof-targ.o : atof-targ.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h \
+ symbols.h tc.h obj.h
+obj-format.o : obj-format.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h \
+ symbols.h tc.h obj.h
+targ-cpu.o : targ-cpu.c targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h \
+ symbols.h tc.h obj.h $(TARG_CPU_DEPENDENTS)
+
+# Remake the info files.
+
+doc: $(srcdir)/as.info
+
+$(srcdir)/as.info: $(srcdir)/doc/as.texinfo
+ (cd doc; make as.info; mv as.info $srcdir)
+
+clean:
+ (cd doc ; $(MAKE) clean)
+ -rm -f $(STAGESTUFF) core
+
+# Like clean but also delete the links made to configure gas.
+distclean: clean
+ -rm -f config.status Makefile host.h targ-env.h targ-cpu.h \
+ targ-cpu.c obj-format.h obj-format.c atof-targ.c \
+ gas.aux gas.cps gas.fns gas.info gas.kys gas.pgs \
+ gas.tps gas.vrs TAGS gas.info* gas.?? gas.??s gas.log \
+ gas.toc gas.*aux *.dvi
+
+# Entry points `install', `includes' and `uninstall'.
+
+# Copy the files into directories where they will be run.
+install:
+ if [ "$(host_alias)" = "$(target_alias)" ] ; then \
+ $(INSTALL_PROGRAM) as.new $(bindir)/as ; \
+ else \
+ $(INSTALL_PROGRAM) as.new $(bindir)/as-$(target_alias) ; \
+ fi
+
+# Create the installation directory.
+install-dir:
+ -mkdir $(libdir)
+ -mkdir $(libdir)/gcc
+ -mkdir $(libdir)/gcc/$(target)
+ -mkdir $(libdir)/gcc/$(target)/$(version)
+
+# Cancel installation by deleting the installed files.
+uninstall:
+ -rm -rf $(libsubdir)
+ -rm -rf $(bindir)/as
+ -rm -rf $(mandir)/gas.$(manext)
+
+
+# These exist for maintenance purposes.
+
+tags TAGS: force
+ etags $(REAL_HEADERS) $(REAL_SOURCES) $(srcdir)/config/*.[hc] $(srcdir)/README $(srcdir)/Makefile.in
+
+bootstrap: as.new force
+ $(MAKE) stage1
+ $(MAKE) CC="$(CC)" CFLAGS="-O -Bstage1/ $(CFLAGS)" libdir=$(libdir) ALLOCA= as.new
+ $(MAKE) stage2
+ $(MAKE) CC="$(CC)" CFLAGS="-O -Bstage2/ $(CFLAGS)" libdir=$(libdir) ALLOCA= as.new
+ $(MAKE) comparison against=stage2
+
+bootstrap2: force
+ $(MAKE) CC="$(CC)" CFLAGS="-O -Bstage1/ $(CFLAGS)" libdir=$(libdir) ALLOCA= as.new
+ $(MAKE) stage2
+ $(MAKE) CC="$(CC)" CFLAGS="-O -Bstage2/ $(CFLAGS)" libdir=$(libdir) ALLOCA= as.new
+ $(MAKE) comparison against=stage2
+
+bootstrap3: force
+ $(MAKE) CC="$(CC)" CFLAGS="-O -Bstage2/ $(CFLAGS)" libdir=$(libdir) ALLOCA= as.new
+ $(MAKE) comparison against=stage2
+
+# Copy the object files from a particular stage into a subdirectory.
+stage1: force
+ -mkdir stage1
+ -mv $(STAGESTUFF) stage1
+ if [ -f stage1/as.new -a ! -f stage1/as ] ; then (cd stage1 ; ln -s as.new as) ; fi
+
+stage2: force
+ -mkdir stage2
+ -mv $(STAGESTUFF) stage2
+ if [ -f stage2/as.new -a ! -f stage2/as ] ; then (cd stage2 ; ln -s as.new as) ; fi
+
+stage3: force
+ -mkdir stage3
+ -mv $(STAGESTUFF) stage3
+ if [ -f stage3/as.new -a ! -f stage3/as ] ; then (cd stage3 ; ln -s as.new as) ; fi
+
+against=stage2
+
+comparison: force
+ for i in $(STAGESTUFF) ; do cmp $$i $(against)/$$i ; done
+
+de-stage1: force
+ - (cd stage1 ; rm as ; mv -f * ..)
+ - rmdir stage1
+
+de-stage2: force
+ - (cd stage2 ; rm as ; mv -f * ..)
+ - rmdir stage2
+
+de-stage3: force
+ - (cd stage3 ; rm as ; mv -f * ..)
+ - rmdir stage3
+
+#In GNU Make, ignore whether `stage*' exists.
+.PHONY: stage1 stage2 stage3 stage4 clean realclean TAGS bootstrap
+
+force:
+
+Makefile: $(srcdir)/Makefile.in $(host_makefile_frag) $(target_makefile_frag)
+ $(SHELL) ./config.status
+
diff --git a/gnu/usr.bin/as/NOTES b/gnu/usr.bin/as/NOTES
new file mode 100644
index 0000000..9f18fac
--- /dev/null
+++ b/gnu/usr.bin/as/NOTES
@@ -0,0 +1,16 @@
+to do:
+
+remove DONTDEF
+remove the ifdef's from fx_callj tests?
+what are callj tests?
+space tighten sparc alignment.
+fix number_to_chars, & family to have no side effects.
+md_ => tp_
+multiple segments.
+share b.out with a.out.
+
+regress:
+
++-inf
+
+stack:
diff --git a/gnu/usr.bin/as/NOTES.config b/gnu/usr.bin/as/NOTES.config
new file mode 100644
index 0000000..a511519
--- /dev/null
+++ b/gnu/usr.bin/as/NOTES.config
@@ -0,0 +1,52 @@
+(This file under construction).
+
+
+ The GAS Configuration Plan
+
+Theory:
+
+The goal of the new configuration scheme is to bury all object format,
+target processor, and host machine dependancies in object, target, and
+host specific files. That is, to move as many #ifdef's as possible
+out of the gas common code.
+
+Here's how it works. There is a .h and a .c file for each object file
+format, a .h and a .c file for each target processor, and a .h for
+each host. configure creates {sym}links in the current directory to
+the appropriate files in the config directory.
+
+Implementation:
+
+host.h is a {sym}link to .../config/ho-yourhost.h. It is intended to
+be used to hide host compiler, system header file, and system library
+differences between host machines. If your host needs actual c source
+files, then either: these are generally useful functions, in which
+case you should probably build a local library outside of the gas
+source tree, or someone, perhaps me, is confused about what is needed
+by different hosts.
+
+obj-format.h is a {sym}link to .../config/obj-something.h. It is
+intended to hide object file format differences from the bulk of gas,
+and from most of the cpu backend.
+
+All gas .c files include as.h.
+
+as.h #define's "gas", includes host.h, defines a number of gas
+specific structures and types, and then includes tp.h, obj.h, and
+target-environment.h.
+
+te-something.h defines a target environment specific preprocessor
+flag, eg, TE_SUN, and then includes obj-format.h.
+
+obj-format.h defines an object format specific preprocessor flag, eg,
+OBJ_AOUT, OBJ_BOUT, OBJ_COFF, includes "target-processor.h", and then
+defines the object specific macros, functions, types, and structures.
+
+target-processor.h
+
+target-processor.
+
+Porting:
+
+There appear to be four major types of ports; new hosts, new target
+processors, new object file formats, and new target environments.
diff --git a/gnu/usr.bin/as/README b/gnu/usr.bin/as/README
new file mode 100644
index 0000000..73b7605
--- /dev/null
+++ b/gnu/usr.bin/as/README
@@ -0,0 +1,212 @@
+This is a pre-alpha version of the GNU assembler, version 1.92.3.
+
+(this is a copy of the mail announcement. Real README follows below.)
+
+This session I merged the m88k support. It configures, builds, and
+assembles things, including some gcc2 output. I have no way of
+knowing if the output is right.
+
+I've merged the tahoe support. It configures and builds. I couldn't
+build the cygnus version of gcc2 for this machine, so I have no idea
+whether gas is assembling anything at all for it.
+
+I've walked through my bug and patch archives. Gas now makes a
+tolerable guess at a.out headers for hpux and sequent, although I have
+no way to know if these are right yet.
+
+Ming tran-le's changes for 386aix will probably drop out soon. He
+needs multiple segments and I don't plan to get that in before the
+real release.
+
+Eric youngdale's help with vms has been invaluable. According to him,
+this gas is doing vms. I didn't quite get a cross to vms working and
+don't plan to spend any more time on it.
+
+The gas manual is included in the distribution, configuration, and
+Makefiles. It should build, be printable, and readable through info.
+
+I have not yet verified that this gas has all of the unreleased
+changes that hack made after the last gas release. At this point I
+plan to ignore these until those bugs are re-reported in an alpha or
+full release I don't think it's worth my time.
+
+I have not yet verified any hosts other than sun4, although I have
+three-staged sun3 native.
+
+I have not updated the configuration doc.
+
+I do not plan to bring in any new backends for the upcoming release
+unless someone hands them to me on a platter as eric did for vms. I
+merged the m88k and tahoe ports because they were simple for me at
+this point, but would have been difficult for someone else. I may yet
+do this for the ncube support as well.
+
+I've looked at the osf stuff and discarded it for this release. I'm
+not sure I like what they've done for macho object format and without
+macho headers, I can't even build their version.
+
+I've looked at the utah stuff and discarded it for this release.
+They, too, have made some sweeping changes to support their object
+format that I'm not sure were necessary. In any case, merging this
+would be too much work for me right now.
+
+I've looked at the tron port. It's remarkably clean and it's a.out
+format. I don't plan to merge this for the full release for two
+reasons. First, it's so clean, they will be able to add their stuff
+on top and build a seperate distribution without much trouble.
+Second, I'm get responses from them, and hope that they will be able
+to do the merge.
+
+
+To do before alpha:
+
+* merge patches and address bugs as they arrive.
+
+* kill a remaining bug. The following input:
+
+ .text
+a .word 3
+b .word 4
+c .half b-a
+
+kills most risc ports. I believe that this represents a failing of
+the internal representation of relocs (aka fixS's). The fix is
+relatively straightforward and I intend to make it.
+
+* add autoconf style configuration for hosts (not targets).
+
+* test via three-staging (preferably with gcc2) on all a.out based
+ machines to which I have access.
+
+* update/clean out README's and build a brief porting guide.
+
+There is still a copyright issue on the coff back end, so it may need
+to be pulled for the full release. If this gets resolved, I hope to
+see coff run personally on at least one native machine before full
+release.
+
+
+Real README:
+
+This is a pre-alpha version of the GNU assembler, version 1.92.3.
+
+A number of things have changed and the wonderful world of gas looks
+very different. There's still a lot of irrelevant garbage lying
+around that will be cleaned up soon. The gas manual now builds and
+installs, but internal documentation is still scarce, as are logs of
+the changes made since the last gas release. My apologies, and I'll
+try to get something useful
+
+At this point I believe gas to be ansi only code for most target
+cpu's. That is, there should be relatively few, if any host system
+dependencies. Most of my recent effort has been spent testing and
+dusting off ports for which Cygnus hasn't had recent need.
+
+Hosting has recently been tested on only:
+
+ sun4
+ sun3
+
+I believe that gas can currently be targetted for:
+
+ sun4
+ sun3
+
+and "ports" for other cpu's and object file formats from the following
+set are probably trivial at this point:
+
+ a.out
+
+ a29k
+ i386
+ i860
+ i960
+ m68k
+ m88k
+ ns32k
+ tahoe
+ sparc
+ vax
+
+I have tested most of these in "generic" a.out configurations so I
+feel pretty confident in them. If anything else works, it's an
+accident.
+
+Some ports now generate object files that are somewhat differently
+shaped, but should be more correct. Specifically:
+
+* Most a.out ports now sort the relocation table in numerically
+ ascending order. In previous versions of gas, the relocation table
+ was sorted in descending order. To get the previous functionality,
+ compile with -DREVERSE_SORT_RELOCS.
+
+* ns32k: The last gas I have from hack simply looks broken for ns32k.
+ I think this one works, but don't have an assembler that I trust
+ against which to compare.
+
+* i386: now uses ".align x" to mean x bytes rather than 2^x bytes. It
+ also pads with the noop instruction rather than zeroes.
+
+In all cases, compiling with -DOLD_GAS will produce an assembler that
+should produce object files that are bitwise identical to the previous
+version of gas.
+
+
+
+ NEW FEATURES!
+
+
+This isn't a complete catalog. I've forgotten what all has been done.
+
+* support for i960, a29k, m88k, and tahoe.
+
+* support for 68030 and 68040, including the ability to limit the
+ instructions that gas will accept. ie, you can assemble for EXACTLY
+ 68000 and no more.
+
+* object file formats have been broken out into separate backends.
+
+* a new "backend" has been created to represent the target
+ environment. That is, gas now mimics various other assemblers
+ rather than creating it's own requirements. A side effect of this
+ is that this version of gas may not behave the same way as previous
+ versions.
+
+* ansi. gas is now strictly ansi code so host ports should be
+ trivial.
+
+
+
+ REPORTING BUGS IN GAS
+
+
+Bugs in THIS RELEASE of gas should be reported directly to
+rich@cygnus.com. NOT to bug-gnu-utils@prep.ai.mit.edu.
+
+If you report a bug in GAS, please remember to include:
+
+A description of exactly what went wrong.
+
+How GAS was configured,
+
+The Operating System GAS was running under.
+
+The options given to GAS.
+
+The actual input file that caused the problem.
+
+It is silly to report a bug in GAS without including an input file for
+GAS. Don't ask us to generate the file just because you made it from
+files you think we have access to.
+
+1. You might be mistaken.
+2. It might take us a lot of time to install things to regenerate that file.
+3. We might get a different file from the one you got, and might not see any
+bug.
+
+To save us these delays and uncertainties, always send the input file
+for the program that failed.
+
+If the input file is very large, and you are on the internet, you may
+want to make it avaliable for anonymous FTP instead of mailing it. If you
+do, include instructions for FTP'ing it in your bug report.
diff --git a/gnu/usr.bin/as/README-vms b/gnu/usr.bin/as/README-vms
new file mode 100644
index 0000000..796c603
--- /dev/null
+++ b/gnu/usr.bin/as/README-vms
@@ -0,0 +1,248 @@
+ This document explains a couple of things that are specific to VMS.
+There are currently two "chapters", the first deals with cross-assembly
+issues, and the second deals with the VMS debugger and GNU-CC.
+
+
+***********************************************************************
+****************** Notes for Cross Assembly with VMS ******************
+***********************************************************************
+
+ If you wish to build gas on a non-VMS system to cross-assemble,
+you should use:
+
+configure ${hosttype} -target=vms
+
+and then follow the usual procedure. The object files generated on
+Unix will be correct from a binary point of view, but the real trick is
+getting them to the VMS machine. The format of the object file is
+a variable-length record, but each record contains binary data. gas
+writes the records in the same format that VMS would expect,
+namely a two-byte count followed by that number of bytes.
+
+ If you try to copy the file to a VMS system using ftp, the ftp
+protocol will screw up the file by looking for nulls (record terminator for
+unix) and it will insert it's own record terminators at that point. This
+will obviously corrupt the file.
+
+ If you try to transfer the file with ftp in binary mode, the
+file itself will not be corrupt, but VMS will think that the file contains
+fixed-length records of 512 bytes. You can use the public-domain FILE
+utility to change this with a command like:
+
+$FILE foo.o/type=variable
+
+If you do not have this utility available, the following program can be
+used to perform this task:
+
+ #include <fab.h>
+
+ #define RME$C_SETRFM 1
+
+ struct FAB * fab;
+
+ main(int argc, char * argv[]){
+ int i, status;
+ fab = (struct FAB*) malloc(sizeof(struct FAB));
+ *fab = cc$rms_fab; /* initialize FAB*/
+ fab->fab$b_fac = FAB$M_PUT;
+ fab->fab$l_fop |= FAB$M_ESC;
+ fab->fab$l_ctx = RME$C_SETRFM;
+ fab->fab$w_ifi = 0;
+ for(i=1;i<argc;i++){
+ printf("Setting %s to variable length records.\n",argv[i]);
+ fab->fab$l_fna = argv[i];
+ fab->fab$b_fns = strlen(argv[i]);
+ status = sys$open(fab,0,0);
+ if((status & 7) != 1) lib$signal(status);
+ fab->fab$b_rfm = FAB$C_VAR;
+ status = sys$modify(fab,0,0);
+ if((status & 7) != 1) lib$signal(status);
+ status = sys$close(fab,0,0);
+ if((status & 7) != 1) lib$signal(status);
+ };
+ }
+
+ If you have NFS running on the VMS system, what you need to do
+depends upon which NFS software you are running on the VMS system. There
+are a number of different TCP/IP packages for VMS available, and only very
+limited testing has been performed. In the tests that has been done so
+far, the contents of the file will always be correct when transferring the
+file via NFS, but the record attributes may or may not be correct.
+
+ One proprietary TCP/IP/NFS package for VMS is known to
+automatically fix the record attributes of the object file if you NFS mount
+a unix disk from the VMS system, and if the file has a ".obj" extension on
+the unix system. Other TCP/IP packages might do this for you as well, but
+they have not been checked.
+
+No matter what method you use to get the file to the VMS system, it is
+always a good idea to check to make sure that it is the correct type by
+doing a "$dir/full" on the object file. The desired record attributes will
+be "None". Undesirable record attributes will be "Stream-LF" or anything
+else.
+
+Once you get the files on the VMS system, you can check their integrity
+with the "$anal/obj" command. (Naturally at some point you should rename
+the .o files to .obj). As far as the debugger is concerned, the records
+will be correct, but the debugger will not be able to find the source files,
+since it only has the file name, and not the full directory specification.
+You must give the debugger some help by telling it which directories to
+search for the individual files - once you have done this you should be
+able to proceed normally.
+
+ It is a good idea to use names for your files which will be valid
+under VMS, since otherwise you will have no way of getting the debugger to
+find the source file when deugging.
+
+The reason for this is that the object file normally contins specific
+information that the debugger can use to positively identify a file, and if
+you are assembling on a unix system this information simply does not exist
+in a meaningful way. You must help the debugger by using the "SET FILE="
+command to tell the debugger where to look for source files. The debugger
+records will be correct, except that the debugger will not be initially
+able to find the source files. You can use the "SET FILE" command to tell
+the debugger where to look for the source files.
+
+I have only tested this with a SVr4 i486 machine, and everything seems to
+work OK, with the limited testing that I have done. Other machines may
+or may not work. You should read the chapters on cross-compilers in the gcc
+manual before fooling with this. Since gas does not need to do any floating
+point arithmetic, the floating point constants that are generated here should
+be correct - the only concern is with constant folding in the main compiler.
+The range and precision of floats and doubles are similar on the 486 (with
+a builtin 80387) and the VAX, although there is a factor of 2 to 4
+difference in the range. The double, as implemented on the 486, is quite
+similar to the G_FLOAT on the VAX.
+
+***********************************************************************
+****************** Notes for using GNU CC with the VMS debugger********
+***********************************************************************
+
+
+ 1) You should be aware that GNU-C, as with any other decent compiler,
+will do things when optimization is turned on that you may not expect.
+Sometimes intermediate results are not written to variables, if they are only
+used in one place, and sometimes variables that are not used at all will not be
+written to the symbol table. Also, parameters to inline functions are often
+inaccessible. You can see the assembly code equivalent by using KP7 in the
+debugger, and from this you can tell if in fact a variable should have the
+value that you expect. You can find out if a variable lives withing a register
+by doing a 'show symbol/addr'.
+
+ 2) Overly complex data types, such as:
+
+int (*(*(*(*(*(* sarr6)[1])[1])[2])[3])[4])[5];
+
+will not be debugged properly, since the debugging record overflows an internal
+debugger buffer. gcc-as will convert these to *void as far as the debugger
+symbol table is concerned, which will avoid any problems, and the assembler
+will give you a message informing you that this has happened.
+
+ 3) You must, of course, compile and link with /debug. If you link
+without debug, you still get traceback table in the executable, but there is no
+symbol table for variables.
+
+ 4) Included in the patches to VMS.C are fixes to two bugs that are
+unrelated to the changes that I have made. One of these made it impossible to
+debug small programs sometimes, and the other caused the debugger to become
+confused about which routine it was in, and give this incorrect info in
+tracebacks.
+
+ 5) If you are using the GNU-C++ compiler, you should modify the
+compiler driver file GNU_CC:[000000]GCC.COM (or GXX.COM). If you have a
+seperate GXX.COM, then you need to change one line in GXX.COM to:
+$ if f$locate("D",p2) .ne. P2_Length then Debug = " ""-G0"""
+ Notice zero---> ^
+If you are using a GCC.COM that does both C and C++, add the following lines to
+GCC.COM:
+
+$!
+$! Use old style debugging records for VMS
+$!
+$ if (Debug.nes."" ).and. Plus then Debug = " ""-G0"""
+
+after the variables Plus and Debug are set. The reason for this, is that C++
+compiler by default generates debugging records that are more complex,
+with many new syntactical elements that allow for the new features of the
+language. The -G0 switch tells the C++ compiler to use the old style debugging
+records. Until the debugger understands C++ there is not any point to try and
+use the expanded syntax.
+
+ 6) When you have nested scopes, i.e.:
+main(){
+ int i;
+ {int i;
+ {int i;
+};};}
+and you say "EXAM i" the debugger needs to figure out which variable you
+actually want to reference. I have arranged things to define a block to the
+debugger when you use brackets to enter a new scope, so in the example above,
+the variables would be described as:
+TEST\main\i
+TEST\main\$0\i
+TEST\main\$0\$0\i
+At each level, the block name is a number with a dollar sign prefix, the
+numbers start with 0 and count upward. When you say EXAM i, the debugger looks
+at the current PC, and decides which block it is currently in. It works from
+the innermost level outward until it finds a block that has the variable "i"
+defined. You can always specify the scope explicitly.
+
+ 7) With C++, there can be a lot of inline functions, and it would be
+rather restrictive to force the user to debug the program by converting all of
+the inline functions to normal functions. What I have done is to essentially
+"add" (with the debugger) source lines from the include files that contain the
+inline functions. Thus when you step into an inline function it appears as if
+you have called the function, and you can examine variables and so forth.
+There are several *very* important differences, however. First of all, since
+there is no function call involved, you cannot step over the inline function
+call - you always step into it. Secondly, since the same source lines are used
+in many locations, there is a seperate copy of the source for *each* usage.
+Without this, breakpoints do not work, since we must have a 1-to-1 mapping
+between source lines and PC.
+ Since you cannot step over inline function calls, it can be a real pain
+if you are not really interested in what is going on for that function call.
+What I have done is to use the "-D" switch for the assembler to toggle the
+following behavior. With the "-D" switch, all inline functions are included in
+the object file, and you can debug everything. Without the "-D" switch
+(default case with VMS implementation), inline functions are included *only* if
+they did not come from system header files (i.e. from GNU_CC_INCLUDE: or
+GNU_GXX_INCLUDE:). Thus, without the switch the user only debugs his/her own
+inline functions, and not the system ones. (This is especially useful if you do
+a lot of stream I/O in C++). This probably will not provide enough granularity
+for many users, but for now this is still somewhat experimental, and I would
+like to reflect upon it and get some feedback before I go any further.
+Possible solutions include an interactive prompting, a logical name, or a new
+command line option in gcc.c (which is then passed through somehow to the guts
+of the assembler).
+ The inline functions from header files appear after the source code
+for the source file. This has the advantage that the source file itself is
+numbered with the same line numbers that you get with an editor. In addition,
+the entire header file is not included, since the assembler makes a list of
+the min and max source lines that are used, and only includes those lines from
+the first to the last actually used. (It is easy to change it to include the
+whole file).
+
+ 8) When you are debugging C++ objects, the object "this" is refered to
+as "$this". Actually, the compiler writes it as ".this", but the period is
+not good for the debugger, so I have a routine to convert it to a $. (It
+actually converts all periods to $, but only for variables, since this was
+intended to allow us to access "this".
+
+ 9) If you use the asm("...") keyword for global symbols, you will not
+be able to see that symbol with the debugger. The reason is that there are two
+records for the symbol stored in the data structures of the assembler. One
+contains the info such as psect number and offset, and the other one contains
+the information having to do with the data type of the variable. In order to
+debug as symbol, you need to be able to coorelate these records, and the only
+way to do this is by name. The record with the storage attributes will take
+the name used in the asm directive, and the record that specifies the data type
+has the actual variable name, and thus when you use the asm directive to change
+a variable name, the symbol becomes invisible.
+
+ 10) Older versions of the compiler ( GNU-C 1.37.92 and earlier) place
+global constants in the text psect. This is unfortunate, since to the linker
+this appears to be an entry point. I sent a patch to the compiler to RMS,
+which will generate a .const section for these variables, and patched the
+assembler to put these variables into a psect just like that for normal
+variables, except that they are marked NOWRT. static constants are still
+placed in the text psect, since there is no need for any external access.
diff --git a/gnu/usr.bin/as/README.coff b/gnu/usr.bin/as/README.coff
new file mode 100644
index 0000000..46c61cd
--- /dev/null
+++ b/gnu/usr.bin/as/README.coff
@@ -0,0 +1,79 @@
+The coff patches intend to do the following :
+
+ . Generate coff files very compatible with vanilla linker.
+ . Understands coff debug directives.
+
+Here are the guidelines of the work I have done :
+
+ . Encapsulate format dependent code in macros where it is possible.
+ . Where not possible differenciate with #ifdef
+ . try not to change the calling conventions of the existing functions.
+ I made one exception : symbol_new. I would be pleased to hear about
+ a better solution. (symbols.c)
+ . Extend the use of N_TYPE_seg seg_N_TYPE tables so that segments can
+ be manipulated without using their format dependent name. (subsegs.c)
+ . Write a function to parse the .def debug directives
+ . Write two small peaces of code to handle the .ln directive.
+ . In write.c try to move all the cross compilation specifics (md_..) to
+ format dependent files.
+ . Encapsulate the data structures using generic types, macros calls.
+ . Added too much code to resolve the complexity of the symbol table
+ generated. Most of the code deals with debug stuff.
+ . Create another makefile, shorter, cleaner.
+ . Create a config.gas shell script to mimic the gcc,gdb... configuration
+ mechanism. This reduce the complexity of the makefile.
+ . Isolate the format dependent code in two files
+ coff.c coff.h
+ aout.c aout.h
+ elf.c elf.h [ Not yet ;-]
+ . added a little stack management routine for coff in file stack.c
+ . isolate os specific flags in m- files
+
+If further development is planed on it is should solve the following problems :
+
+ . Encapsulate DESC & OTHER tests in a macro call. I'm not aware
+ of their exact semantics.
+ . Clean up the seg_N_TYPE N_TYPE_seg naming scheme
+ . Try to remove as much reference to segment dependent names as possible
+ . Find a cleaner solution for symbol_new.
+ . Report the modifications on vax, ns32k, sparc machine dependent files.
+ To acheive this goal, search for \<N_, sy_, symbol_new and symbolS.
+ . Allow an arbitrary number of segments (spare sections .ctor .dtor .bletch)
+ . Find a way to extend the debug information without breaking sdb
+ compatibility. Mainly intended for G++.
+ . should it do something to generate shared libraries objects ?
+
+I have tested this code on the following processor/os. gcc-1.37.1 was
+ used for all the tests.
+
+386 SCO unix ODT
+ gcc-1.37.1, gas, emacs-18.55
+
+386 Esix rev C
+ gas-1.37/write.s
+
+386 Ix 2.02
+ gas, all the X11R4 mit clients
+
+386 CTIX 3.2
+ xsol (X11R4 solitary game), gas
+
+68030 unisoft 1.3
+ the kernel (V.3.2) + tcp/ip extensions
+ bash-1.05, bison-1.11, compress-4.0, cproto, shar-3.49, diff-1.14,
+ dist-18.55, flex-2.3, gas-1.37, gcc-1.37.1, gdb-3.6, grep-1.5,
+ kermit, make-3.58, makedep, patch, printf, makeinfo, g++-1.37.1,
+ tar-1.08, texi2roff, uuencode, uutraf-1.2, libg++-1.37.2, groff-0.5
+
+68020 sunos 3.5 (no, not coff, just to be sure that I didn't
+ introduce errors)
+ gcc-1.37.1, gas, emacs-18.55, gdb-3.6, bison-1.11, diff-1.14,
+ make-3.58, tar-1.08
+
+68030 sunos 4.0.3 (idem)
+ gas
+
+I would be glad to hear about new experiences
+
+ Loic (loic@adesign.uucp or loic@afp.uucp)
+
diff --git a/gnu/usr.bin/as/README.gnu b/gnu/usr.bin/as/README.gnu
new file mode 100644
index 0000000..46f135fc
--- /dev/null
+++ b/gnu/usr.bin/as/README.gnu
@@ -0,0 +1,133 @@
+This is the beta-test version of the GNU assembler. (Probably
+around Version 1.35, but check version.c which gets updated more
+often than this readme.)
+
+The assembler has been modified to support a feature that is
+potentially useful when assembling compiler output, but which may
+confuse assembly language programmers. If assembler encounters a
+.word pseudo-op of the form symbol1-symbol2 (the difference of two
+symbols), and the difference of those two symbols will not fit in 16
+bits, the assembler will create a branch around a long jump to
+symbol1, and insert this into the output directly before the next
+label: The .word will (instead of containing garbage, or giving an
+error message) contain (the address of the long jump)-symbol2. This
+allows the assembler to assemble jump tables that jump to locations
+very far away into code that works properly. If the next label is
+more than 32K away from the .word, you lose (silently) RMS claims
+this will never happen. If the -k option is given, you will get a
+warning message when this happens.
+
+These files are currently set up to allow you to compile all of the
+versions of the assembler (68020, VAX, ns32k, and i386) on the same
+machine. To compile the 68020 version, type 'make a68'. To compile
+the VAX version, type 'make avax'. To compile the ns32k version,
+type 'make a32k'. To compile the Intel 80386 version, type 'make
+a386'. The Makefile contains instructions on how to make one of the
+assemblers compile as the default.
+
+Before you can compile the 68020 version of the assembler, you must
+make m68k.h be a link to m-sun3.h , m-hpux.h or m-generic.h . If
+you are on a SUN-3 (or other machine that uses a magic number of
+(2 << 16) | OMAGIC type 'ln -s m-sun3.h m68k.h' else if you are on a
+machine running HP-UX, type 'ln m-hpux.h m689k.h' else type
+'ln -s m-generic.h m68k.h' If your machine does not support symbolic
+links, omit the '-s'.
+
+See the instructions in the Makefile for compiling gas for the Sequent
+Symmetry (dynix 3.0.12 + others?) or for the HP 9000/300
+
+If your machine does not have both varargs.h and vfprintf(), but does have
+_doprnt() add -DNO_VARARGS to the CFLAGS line in the makefile. If your
+machine has neither vfprintf() or _doprnt(), you will have to change
+messages.c in order to get readable error messages from the assembler.
+
+
+ REPORTING BUGS IN GAS
+
+Bugs in gas should be reported to bug-gnu-utils@prep.ai.mit.edu If you can't
+get through to prep, try hack@gnu.ai.mit.edu or hack@media-lab.media.mit.edu
+
+If you report a bug in GAS, please remember to include:
+
+A description of exactly what went wrong.
+
+The type of machine GAS was running on (VAX, 68020, etc),
+
+The Operating System GAS was running under.
+
+The options given to GAS.
+
+The actual input file that caused the problem.
+
+It is silly to report a bug in GAS without including an input file for
+GAS. Don't ask us to generate the file just because you made it from
+files you think we have access to.
+
+1. You might be mistaken.
+2. It might take us a lot of time to install things to regenerate that file.
+3. We might get a different file from the one you got, and might not see any
+bug.
+
+To save us these delays and uncertainties, always send the input file
+for the program that failed.
+
+If the input file is very large, and you are on the internet, you may
+want to make it avaliable for anonymous FTP instead of mailing it. If you
+do, include instructions for FTP'ing it in your bug report.
+
+------------------------------ README.APOLLO ---------------------------------
+
+The changes required to get the GNU C compiler running on
+Apollo 68K platforms are available via anonymous ftp from
+labrea.stanford.edu (36.8.0.47) in the form of a compressed
+tar file named "/pub/gnu/apollo-gcc-1.37.tar.Z".
+The size of the file is 84145 bytes.
+
+To build GCC for the Apollo you'll need the virgin FSF
+distributions of bison-1.03, gas-1.34, and gcc-1.37. They
+are also on labrea.stanford.edu as well as prep.ai.mit.edu.
+My changes are to enable gas to produce Apollo COFF object
+files and allow gcc to parse some of the syntax extensions
+which appear in Apollo C header files. Note that the
+COFF encapsulation technique cannot be used on the Apollo.
+
+The tar file should be unpacked in the directory containing
+the gas-1.34 and gcc-1.37 directories; a few files will be overlaid,
+and an APOLLO-GCC-README file will appear in the top directory.
+This file contains detailed instructions on how to proceed.
+
+These changes will only work for SR10.1 or later systems, using
+the 6.6 or later version of the Apollo C compiler.
+
+If you do not have ftp access, I can mail you the changes in the
+form of diffs; they are approximately 40K in length. If you request
+them, be sure to give me a voice phone number so I can contact you
+in case I can't send you mail; I've had several requests in the
+past from people I can't contact.
+
+By the way, I'm working on getting the GNU C++ compiler running;
+there are a couple problems to solve. I hope to be able to announce
+the Apollo version shortly after the 1.37 version is released.
+
+John Vasta Hewlett-Packard Apollo Systems Division
+vasta@apollo.hp.com M.S. CHA-01-LT
+(508) 256-6600 x6362 300 Apollo Drive, Chelmsford, MA 01824
+UUCP: {decwrl!decvax, mit-eddie, attunix}!apollo!vasta
+
+------------------------------------
+
+You might refer others who are interested in a similar thing.
+
+Kevin Buchs buchs@mayo.edu
+
+
+------------------------------ README.COFF -----------------------------------
+
+If you have a COFF system, you may wish to aquire
+
+ UUCP: osu-cis!~/gnu/coff/gnu-coff.tar.Z
+ or
+ FTP: tut.cis.ohio-state.edu:/pub/gnu/coff/gnu-coff.tar.Z
+
+These contain patches for gas that will make it produce COFF output.
+I have never seen these patches, so I don't know how well they work.
diff --git a/gnu/usr.bin/as/README.pic b/gnu/usr.bin/as/README.pic
new file mode 100644
index 0000000..adde6fe
--- /dev/null
+++ b/gnu/usr.bin/as/README.pic
@@ -0,0 +1,25 @@
+A few short notes on PIC support.
+
+. References to the symbol "_GLOBAL_OFFSET_TABLE_" are special. These always
+ PC relative to the start of the current instruction. Also, they occur
+ in "complex" expressions in function prologs, eg.
+
+ move _GLOBAL_OFFSET_TABLE_ + (. - L1 ), %some_register
+
+ The expression parser can't handle these generically, so the expression
+ above is recognised as a special case.
+
+. Some archs have special PIC assembler syntax to reference static and global
+ data. This is handled in targ-cpu.c.
+
+. Correct relocation_info must be output (eg. fields r_jmptable and r_baserel).
+
+. Internal labels must be output in the symbol table if they are referred to
+ by PIC instructions. The linker must allocate a GOT slot for them.
+
+. The former meaning of the -k switch ("WORKING_DOT" stuff), has been nuked
+ in favour of enabling PIC code recognition.
+
+
+-pk
+
diff --git a/gnu/usr.bin/as/README.rich b/gnu/usr.bin/as/README.rich
new file mode 100644
index 0000000..5a2ecc4
--- /dev/null
+++ b/gnu/usr.bin/as/README.rich
@@ -0,0 +1,144 @@
+(This file is under construction.)
+
+
+ The Code Pedigree of This Directory
+
+
+This directory contains a big merge of several development lines of
+gas as well as a few bug fixes and some configuration that I've added
+in order to retain my own sanity.
+
+A little history.
+
+The only common baseline of all versions was gas-1.31.
+
+From 1.31, Intel branched off and added:
+
+ support for the Intel 80960 (i960) processor.
+ support for b.out object files.
+ some bug fixes.
+ sloppy mac MPW support
+ Intel gnu/960 makefiles and version numbering.
+
+Many of the bug fixes found their way into the main development line
+prior to 1.36. ALL intel changes were ifdef'd I80960. This was good
+as it isolated the changes, but bad in that it connected the b.out
+support to the i960 support, and bad in that the bug fixes were only
+active in the i960+b.out executables of gas, (although most of these
+were nicely marked with comments indicating that they were probably
+general bug fixes.)
+
+To pick up the main FSF development line again, along the way to 1.36,
+several new processors were added, many bugs fixed, and the world was
+a somewhat better place in general.
+
+From gas-1.36, Loic at Axis Design (france!) encapsulated object
+format specific actions, added coff versions of those encapsulations,
+and a config.gas style configuration and Makefile. This was a big
+change and a lot of work.
+
+Then along came the FIRST FSF release of gas-1.37. I say this because
+there have been at least two releases of gas-1.37. Only two of them
+do we care about for this story, so let's call them gas-1.37.1 and
+gas-1.37.2.
+
+Here starts the confusion. Firstly, gas-1.37.1 did not compile.
+
+In the meantime, John Gilmore at Cygnus Support had been hacking
+gas-1.37.1. He got it to compile. He added support for the AMD 29000
+processor. AND he started encapsulating some of the a.out specific
+pieces of code mostly into functions. AND he rebuilt the relocation
+info to be generic. AND he restructured somewhat so that for a single
+host, cross assemblers could be built for all targets in the same
+directory. Useful work but a considerable nuisance because the a29k
+changes were not partitioned from the encapsulation changes, the
+encapsulation changes were incomplete, and the encapsulation required
+functions where alternate structuring might have used macros. Let's
+call this version gas-1.37.1+a29k.
+
+By the time gas-1.37.2 was "released", (remember that it TOO was
+labelled by FSF as gas-1.37), it compiled, but it also added i860
+support and ansi style const declarations.
+
+At this point, Loic rolled his changes into gas-1.37.2.
+
+What I've done.
+
+I collected all the stray versions of gas that sounded relevant to my
+goals of cross assembly and alternate object file formats and the FSF
+releases from which the stray versions had branched.
+
+I rolled the Intel i960 changes from 1.31 into versions that I call
+1.34+i960, 1.36+i960, and then 1.37.1+i960.
+
+Then I merged 1.37.1+i960 with 1.37.1+a29k to produce what I call
+1.37.1+i960+a29k or 1.37.3.
+
+From 1.37.3, I pulled in Loic's stuff. This wasn't easy as Loic's
+stuff hit all the same points as John's encapsulations. Loic's goal
+was to split the a.out from coff dependancies for native assembly on
+coff, while John's was to split for multiple cross assembly from a
+single host.
+
+Loic's config arranged files much like emacs into m-*, etc. I've
+rearranged these somewhat.
+
+Theory:
+
+The goal of the new configuration scheme is to bury all object format,
+target processor, and host machine dependancies in object, target, and
+host specific files. That is, to move all #ifdef's out of the gas
+common code.
+
+Here's how it works. There is a .h and a .c file for each object file
+format, a .h and a .c file for each target processor, and a .h for
+each host. config.gas creates {sym}links in the current directory to
+the appropriate files in the config directory. config.gas also serves
+as a list of triplets {host, target, object-format} that have been
+tested at one time or another. I also recommend that config.gas be
+used to document triplet specific notes as to purpose of the triplet,
+etc.
+
+Implementation:
+
+host.h is a {sym}link to .../config/xm-yourhost.h. It is intended to
+be used to hide host compiler, system header file, and system library
+differences between host machines. If your host needs actual c source
+files, then either: these are generally useful functions, in which
+case you should probably build a local library outside of the gas
+source tree, or someone, perhaps me, is confused about what is needed
+by different hosts.
+
+obj-format.h is a {sym}link to .../config/obj-something.h. It is intended
+
+All gas .c files include as.h.
+
+as.h #define's "gas", includes host.h, defines a number of gas
+specific structures and types, and then includes tp.h, obj.h, and
+target-environment.h.
+
+target-environment.h defines a target environment specific
+preprocessor flag, eg, TE_SUN, and then includes obj-format.h.
+
+obj-format.h defines an object format specific preprocessor flag, eg,
+OBJ_AOUT, OBJ_BOUT, OBJ_COFF, includes "target-processor.h", and then
+defines the object specific macros, functions, types, and structures.
+
+target-processor.h
+
+target-processor.
+
+Porting:
+
+There appear to be four major types of ports; new hosts, new target
+processors, new object file formats, and new target environments.
+
+
+-----
+
+reloc now stored internally as generic. (symbols too?) (segment types
+vs. names?)
+
+I don't mean to overlook anyone here. There have also been several
+other development lines here that I looked at and elected to bypass.
+Specifically, xxx's stabs in coff stuff was particularly tempting.
diff --git a/gnu/usr.bin/as/VERSION b/gnu/usr.bin/as/VERSION
new file mode 100644
index 0000000..a3f79bb
--- /dev/null
+++ b/gnu/usr.bin/as/VERSION
@@ -0,0 +1 @@
+1.92.3
diff --git a/gnu/usr.bin/as/app.c b/gnu/usr.bin/as/app.c
new file mode 100644
index 0000000..4c89a77
--- /dev/null
+++ b/gnu/usr.bin/as/app.c
@@ -0,0 +1,539 @@
+/* Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ Modified by Allen Wirfs-Brock, Instantiations Inc 2/90
+ */
+/* This is the Assembler Pre-Processor
+ Copyright (C) 1987 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* App, the assembler pre-processor. This pre-processor strips out excess
+ spaces, turns single-quoted characters into a decimal constant, and turns
+ # <number> <filename> <garbage> into a .line <number>\n.app-file <filename> pair.
+ This needs better error-handling.
+ */
+#ifndef lint
+static char rcsid[] = "$Id: app.c,v 1.3 1993/10/02 20:57:12 pk Exp $";
+#endif
+
+#include <stdio.h>
+#include "as.h" /* For BAD_CASE() only */
+
+#if (__STDC__ != 1) && !defined(const)
+#define const /* Nothing */
+#endif
+
+static char lex[256];
+static char symbol_chars[] =
+ "$._ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+/* These will go in BSS if not defined elsewhere, producing empty strings. */
+extern const char comment_chars[];
+extern const char line_comment_chars[];
+extern const char line_separator_chars[];
+
+#define LEX_IS_SYMBOL_COMPONENT 1
+#define LEX_IS_WHITESPACE 2
+#define LEX_IS_LINE_SEPARATOR 3
+#define LEX_IS_COMMENT_START 4
+#define LEX_IS_LINE_COMMENT_START 5
+#define LEX_IS_TWOCHAR_COMMENT_1ST 6
+#define LEX_IS_TWOCHAR_COMMENT_2ND 7
+#define LEX_IS_STRINGQUOTE 8
+#define LEX_IS_COLON 9
+#define LEX_IS_NEWLINE 10
+#define LEX_IS_ONECHAR_QUOTE 11
+#define IS_SYMBOL_COMPONENT(c) (lex[c] == LEX_IS_SYMBOL_COMPONENT)
+#define IS_WHITESPACE(c) (lex[c] == LEX_IS_WHITESPACE)
+#define IS_LINE_SEPARATOR(c) (lex[c] == LEX_IS_LINE_SEPARATOR)
+#define IS_COMMENT(c) (lex[c] == LEX_IS_COMMENT_START)
+#define IS_LINE_COMMENT(c) (lex[c] == LEX_IS_LINE_COMMENT_START)
+#define IS_NEWLINE(c) (lex[c] == LEX_IS_NEWLINE)
+
+/* FIXME-soon: The entire lexer/parser thingy should be
+ built statically at compile time rather than dynamically
+ each and every time the assembler is run. xoxorich. */
+
+void do_scrub_begin() {
+ const char *p;
+
+ lex[' '] = LEX_IS_WHITESPACE;
+ lex['\t'] = LEX_IS_WHITESPACE;
+ lex['\n'] = LEX_IS_NEWLINE;
+ lex[';'] = LEX_IS_LINE_SEPARATOR;
+ lex['"'] = LEX_IS_STRINGQUOTE;
+ lex['\''] = LEX_IS_ONECHAR_QUOTE;
+ lex[':'] = LEX_IS_COLON;
+
+ /* Note that these override the previous defaults, e.g. if ';'
+ is a comment char, then it isn't a line separator. */
+ for (p = symbol_chars; *p; ++p) {
+ lex[*p] = LEX_IS_SYMBOL_COMPONENT;
+ } /* declare symbol characters */
+
+ for (p = line_comment_chars; *p; p++) {
+ lex[*p] = LEX_IS_LINE_COMMENT_START;
+ } /* declare line comment chars */
+
+ for (p = comment_chars; *p; p++) {
+ lex[*p] = LEX_IS_COMMENT_START;
+ } /* declare comment chars */
+
+ for (p = line_separator_chars; *p; p++) {
+ lex[*p] = LEX_IS_LINE_SEPARATOR;
+ } /* declare line separators */
+
+ /* Only allow slash-star comments if slash is not in use */
+ if (lex['/'] == 0) {
+ lex['/'] = LEX_IS_TWOCHAR_COMMENT_1ST;
+ }
+ /* FIXME-soon. This is a bad hack but otherwise, we
+ can't do c-style comments when '/' is a line
+ comment char. xoxorich. */
+ if (lex['*'] == 0) {
+ lex['*'] = LEX_IS_TWOCHAR_COMMENT_2ND;
+ }
+} /* do_scrub_begin() */
+
+FILE *scrub_file;
+
+int scrub_from_file() {
+ return getc(scrub_file);
+}
+
+void scrub_to_file(ch)
+int ch;
+{
+ ungetc(ch,scrub_file);
+} /* scrub_to_file() */
+
+char *scrub_string;
+char *scrub_last_string;
+
+int scrub_from_string() {
+ return scrub_string == scrub_last_string ? EOF : *scrub_string++;
+} /* scrub_from_string() */
+
+void scrub_to_string(ch)
+int ch;
+{
+ *--scrub_string=ch;
+} /* scrub_to_string() */
+
+/* Saved state of the scrubber */
+static int state;
+static int old_state;
+static char *out_string;
+static char out_buf[20];
+static int add_newlines = 0;
+
+/* Data structure for saving the state of app across #include's. Note that
+ app is called asynchronously to the parsing of the .include's, so our
+ state at the time .include is interpreted is completely unrelated.
+ That's why we have to save it all. */
+
+struct app_save {
+ int state;
+ int old_state;
+ char *out_string;
+ char out_buf[sizeof (out_buf)];
+ int add_newlines;
+ char *scrub_string;
+ char *scrub_last_string;
+ FILE *scrub_file;
+};
+
+char *app_push() {
+ register struct app_save *saved;
+
+ saved = (struct app_save *) xmalloc(sizeof (*saved));
+ saved->state = state;
+ saved->old_state = old_state;
+ saved->out_string = out_string;
+ memcpy(out_buf, saved->out_buf, sizeof(out_buf));
+ saved->add_newlines = add_newlines;
+ saved->scrub_string = scrub_string;
+ saved->scrub_last_string = scrub_last_string;
+ saved->scrub_file = scrub_file;
+
+ /* do_scrub_begin() is not useful, just wastes time. */
+ return (char *)saved;
+}
+
+void app_pop(arg)
+char *arg;
+{
+ register struct app_save *saved = (struct app_save *)arg;
+
+ /* There is no do_scrub_end (). */
+ state = saved->state;
+ old_state = saved->old_state;
+ out_string = saved->out_string;
+ memcpy(saved->out_buf, out_buf, sizeof (out_buf));
+ add_newlines = saved->add_newlines;
+ scrub_string = saved->scrub_string;
+ scrub_last_string = saved->scrub_last_string;
+ scrub_file = saved->scrub_file;
+
+ free (arg);
+} /* app_pop() */
+
+int do_scrub_next_char(get,unget)
+int (*get)();
+void (*unget)();
+{
+ /*State 0: beginning of normal line
+ 1: After first whitespace on line (flush more white)
+ 2: After first non-white (opcode) on line (keep 1white)
+ 3: after second white on line (into operands) (flush white)
+ 4: after putting out a .line, put out digits
+ 5: parsing a string, then go to old-state
+ 6: putting out \ escape in a "d string.
+ 7: After putting out a .app-file, put out string.
+ 8: After putting out a .app-file string, flush until newline.
+ -1: output string in out_string and go to the state in old_state
+ -2: flush text until a '*' '/' is seen, then go to state old_state
+ */
+
+ register int ch, ch2 = 0;
+
+ switch (state) {
+ case -1:
+ ch= *out_string++;
+ if (*out_string == 0) {
+ state=old_state;
+ old_state=3;
+ }
+ return ch;
+
+ case -2:
+ for (;;) {
+ do {
+ ch=(*get)();
+ } while (ch != EOF && ch != '\n' && ch != '*');
+ if (ch == '\n' || ch == EOF)
+ return ch;
+
+ /* At this point, ch must be a '*' */
+ while ( (ch=(*get)()) == '*' ){
+ ;
+ }
+ if (ch == EOF || ch == '/')
+ break;
+ (*unget)(ch);
+ }
+ state=old_state;
+ return ' ';
+
+ case 4:
+ ch=(*get)();
+ if (ch == EOF || (ch >= '0' && ch <= '9'))
+ return ch;
+ else {
+ while (ch != EOF && IS_WHITESPACE(ch))
+ ch=(*get)();
+ if (ch == '"') {
+ (*unget)(ch);
+ out_string="\n.app-file ";
+ old_state=7;
+ state= -1;
+ return *out_string++;
+ } else {
+ while (ch != EOF && ch != '\n')
+ ch=(*get)();
+ return ch;
+ }
+ }
+
+ case 5:
+ ch=(*get)();
+ if (ch == '"') {
+ state=old_state;
+ return '"';
+ } else if (ch == '\\') {
+ state=6;
+ return ch;
+ } else if (ch == EOF) {
+ as_warn("End of file in string: inserted '\"'");
+ state=old_state;
+ (*unget)('\n');
+ return '"';
+ } else {
+ return ch;
+ }
+
+ case 6:
+ state=5;
+ ch=(*get)();
+ switch (ch) {
+ /* This is neet. Turn "string
+ more string" into "string\n more string"
+ */
+ case '\n':
+ (*unget)('n');
+ add_newlines++;
+ return '\\';
+
+ case '"':
+ case '\\':
+ case 'b':
+ case 'f':
+ case 'n':
+ case 'r':
+ case 't':
+#ifdef BACKSLASH_V
+ case 'v':
+#endif /* BACKSLASH_V */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ break;
+
+#ifdef ONLY_STANDARD_ESCAPES
+ default:
+ as_warn("Unknown escape '\\%c' in string: Ignored",ch);
+ break;
+#else /* ONLY_STANDARD_ESCAPES */
+ default:
+ /* Accept \x as x for any x */
+ break;
+#endif /* ONLY_STANDARD_ESCAPES */
+
+ case EOF:
+ as_warn("End of file in string: '\"' inserted");
+ return '"';
+ }
+ return ch;
+
+ case 7:
+ ch=(*get)();
+ state=5;
+ old_state=8;
+ return ch;
+
+ case 8:
+ do ch= (*get)();
+ while (ch != '\n');
+ state=0;
+ return ch;
+ }
+
+ /* OK, we are somewhere in states 0 through 4 */
+
+ /* flushchar: */
+ ch=(*get)();
+ recycle:
+ if (ch == EOF) {
+ if (state != 0)
+ as_warn("End of file not at end of a line: Newline inserted.");
+ return ch;
+ }
+
+ switch (lex[ch]) {
+ case LEX_IS_WHITESPACE:
+ do ch=(*get)();
+ while (ch != EOF && IS_WHITESPACE(ch));
+ if (ch == EOF)
+ return ch;
+ if (IS_COMMENT(ch) || (state == 0 && IS_LINE_COMMENT(ch)) || ch == '/' || IS_LINE_SEPARATOR(ch)) {
+ goto recycle;
+ }
+ switch (state) {
+ case 0: state++; goto recycle; /* Punted leading sp */
+ case 1: BAD_CASE(state); /* We can't get here */
+ case 2: state++; (*unget)(ch); return ' '; /* Sp after opco */
+ case 3: goto recycle; /* Sp in operands */
+ default: BAD_CASE(state);
+ }
+ break;
+
+ case LEX_IS_TWOCHAR_COMMENT_1ST:
+ ch2=(*get)();
+ if (ch2 != EOF && lex[ch2] == LEX_IS_TWOCHAR_COMMENT_2ND) {
+ for (;;) {
+ do {
+ ch2=(*get)();
+ if (ch2 != EOF && IS_NEWLINE(ch2))
+ add_newlines++;
+ } while (ch2 != EOF &&
+ (lex[ch2] != LEX_IS_TWOCHAR_COMMENT_2ND));
+
+ while (ch2 != EOF &&
+ (lex[ch2] == LEX_IS_TWOCHAR_COMMENT_2ND)){
+ ch2=(*get)();
+ }
+
+ if (ch2 == EOF
+ || lex[ch2] == LEX_IS_TWOCHAR_COMMENT_1ST)
+ break;
+ (*unget)(ch);
+ }
+ if (ch2 == EOF)
+ as_warn("End of file in multiline comment");
+
+ ch = ' ';
+ goto recycle;
+ } else {
+ if (ch2 != EOF)
+ (*unget)(ch2);
+ return ch;
+ }
+ break;
+
+ case LEX_IS_STRINGQUOTE:
+ old_state=state;
+ state=5;
+ return ch;
+
+#ifndef IEEE_STYLE
+ case LEX_IS_ONECHAR_QUOTE:
+ ch=(*get)();
+ if (ch == EOF) {
+ as_warn("End-of-file after a one-character quote; \000 inserted");
+ ch=0;
+ }
+ sprintf(out_buf,"%d", (int)(unsigned char)ch);
+
+ /* None of these 'x constants for us. We want 'x'.
+ */
+ if ( (ch=(*get)()) != '\'' ) {
+#ifdef REQUIRE_CHAR_CLOSE_QUOTE
+ as_warn("Missing close quote: (assumed)");
+#else
+ (*unget)(ch);
+#endif
+ }
+
+ old_state=state;
+ state= -1;
+ out_string=out_buf;
+ return *out_string++;
+#endif
+ case LEX_IS_COLON:
+ if (state != 3)
+ state=0;
+ return ch;
+
+ case LEX_IS_NEWLINE:
+ /* Roll out a bunch of newlines from inside comments, etc. */
+ if (add_newlines) {
+ --add_newlines;
+ (*unget)(ch);
+ }
+ /* fall thru into... */
+
+ case LEX_IS_LINE_SEPARATOR:
+ state=0;
+ return ch;
+
+ case LEX_IS_LINE_COMMENT_START:
+ if (state != 0) /* Not at start of line, act normal */
+ goto de_fault;
+
+ /* FIXME-someday: The two character comment stuff was badly
+ thought out. On i386, we want '/' as line comment start
+ AND we want C style comments. hence this hack. The
+ whole lexical process should be reworked. xoxorich. */
+
+ if (ch == '/' && (ch2 = (*get)()) == '*') {
+ state = -2;
+ return(do_scrub_next_char(get, unget));
+ } else {
+ (*unget)(ch2);
+ } /* bad hack */
+
+ do ch=(*get)();
+ while (ch != EOF && IS_WHITESPACE(ch));
+ if (ch == EOF) {
+ as_warn("EOF in comment: Newline inserted");
+ return '\n';
+ }
+ if (ch<'0' || ch>'9') {
+ /* Non-numerics: Eat whole comment line */
+ while (ch != EOF && !IS_NEWLINE(ch))
+ ch=(*get)();
+ if (ch == EOF)
+ as_warn("EOF in Comment: Newline inserted");
+ state=0;
+ return '\n';
+ }
+ /* Numerics begin comment. Perhaps CPP `# 123 "filename"' */
+ (*unget)(ch);
+ old_state=4;
+ state= -1;
+ out_string=".line ";
+ return *out_string++;
+
+ case LEX_IS_COMMENT_START:
+ do ch=(*get)();
+ while (ch != EOF && !IS_NEWLINE(ch));
+ if (ch == EOF)
+ as_warn("EOF in comment: Newline inserted");
+ state=0;
+ return '\n';
+
+ default:
+ de_fault:
+ /* Some relatively `normal' character. */
+ if (state == 0) {
+ state=2; /* Now seeing opcode */
+ return ch;
+ } else if (state == 1) {
+ state=2; /* Ditto */
+ return ch;
+ } else {
+ return ch; /* Opcode or operands already */
+ }
+ }
+ return -1;
+}
+
+#ifdef TEST
+
+char comment_chars[] = "|";
+char line_comment_chars[] = "#";
+
+main()
+{
+ int ch;
+
+ app_begin();
+ while ((ch=do_scrub_next_char(stdin)) != EOF)
+ putc(ch,stdout);
+}
+
+as_warn(str)
+char *str;
+{
+ fputs(str,stderr);
+ putc('\n',stderr);
+}
+#endif
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of app.c */
diff --git a/gnu/usr.bin/as/append.c b/gnu/usr.bin/as/append.c
new file mode 100644
index 0000000..d51a27f
--- /dev/null
+++ b/gnu/usr.bin/as/append.c
@@ -0,0 +1,37 @@
+/* Append a string ontp another string
+ Copyright (C) 1987 Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* JF: This is silly. Why not stuff this in some other file? */
+#ifdef USG
+#define bcopy(from,to,n) memcpy(to,from,n)
+#endif
+
+void
+append (charPP, fromP, length)
+char **charPP;
+char *fromP;
+unsigned long length;
+{
+ if (length) { /* Don't trust bcopy() of 0 chars. */
+ bcopy (fromP, * charPP,(int) length);
+ *charPP += length;
+ }
+}
+
+/* end: append.c */
diff --git a/gnu/usr.bin/as/as.1 b/gnu/usr.bin/as/as.1
new file mode 100644
index 0000000..57e2dc1
--- /dev/null
+++ b/gnu/usr.bin/as/as.1
@@ -0,0 +1,271 @@
+.\" Copyright (c) 1991, 1992 Free Software Foundation
+.\" See section COPYING for conditions for redistribution
+.TH as 1 "21 January 1992" "cygnus support" "GNU Development Tools"
+
+.SH NAME
+GNU as \- the portable GNU assembler.
+
+.SH SYNOPSIS
+.na
+.B as
+.RB "[\|" \-a "\||\|" \-al "\||\|" -as\c
+\&\|]
+.RB "[\|" \-D "\|]"
+.RB "[\|" \-f "\|]"
+.RB "[\|" \-I
+.I path\c
+\&\|]
+.RB "[\|" \-k "\|]"
+.RB "[\|" \-L "\|]"
+.RB "[\|" \-o
+.I objfile\c
+\&\|]
+.RB "[\|" \-R "\|]"
+.RB "[\|" \-v "\|]"
+.RB "[\|" \-w "\|]"
+.RB "[\|" \-\^\- "\ |\ " \c
+.I files\c
+\&\|.\|.\|.\|]
+
+.I i960-only options:
+.br
+.RB "[\|" \-ACA "\||\|" \-ACA_A "\||\|" \-ACB\c
+.RB "\||\|" \-ACC "\||\|" \-AKA "\||\|" \-AKB\c
+.RB "\||\|" \-AKC "\||\|" \-AMC "\|]"
+.RB "[\|" \-b "\|]"
+.RB "[\|" \-norelax "\|]"
+
+.I m680x0-only options:
+.br
+.RB "[\|" \-l "\|]"
+.RB "[\|" \-mc68000 "\||\|" \-mc68010 "\||\|" \-mc68020 "\|]"
+.ad b
+
+.SH DESCRIPTION
+GNU \c
+.B as\c
+\& is really a family of assemblers.
+If you use (or have used) the GNU assembler on one architecture, you
+should find a fairly similar environment when you use it on another
+architecture. Each version has much in common with the others,
+including object file formats, most assembler directives (often called
+\c
+.I pseudo-ops)\c
+\& and assembler syntax.
+
+For information on the syntax and pseudo-ops used by GNU \c
+.B as\c
+\&, see `\|\c
+.B as\c
+\|' entry in \c
+.B info \c
+(or the manual \c
+.I
+.I
+Using as: The GNU Assembler\c
+\&).
+
+\c
+.B as\c
+\& is primarily intended to assemble the output of the GNU C
+compiler \c
+.B gcc\c
+\& for use by the linker \c
+.B ld\c
+\&. Nevertheless,
+we've tried to make \c
+.B as\c
+\& assemble correctly everything that the native
+assembler would.
+This doesn't mean \c
+.B as\c
+\& always uses the same syntax as another
+assembler for the same architecture; for example, we know of several
+incompatible versions of 680x0 assembly language syntax.
+
+Each time you run \c
+.B as\c
+\& it assembles exactly one source
+program. The source program is made up of one or more files.
+(The standard input is also a file.)
+
+If \c
+.B as\c
+\& is given no file names it attempts to read one input file
+from the \c
+.B as\c
+\& standard input, which is normally your terminal. You
+may have to type \c
+.B ctl-D\c
+\& to tell \c
+.B as\c
+\& there is no more program
+to assemble. Use `\|\c
+.B \-\^\-\c
+\|' if you need to explicitly name the standard input file
+in your command line.
+
+.B as\c
+\& may write warnings and error messages to the standard error
+file (usually your terminal). This should not happen when \c
+.B as\c
+\& is
+run automatically by a compiler. Warnings report an assumption made so
+that \c
+.B as\c
+\& could keep assembling a flawed program; errors report a
+grave problem that stops the assembly.
+
+.SH OPTIONS
+.TP
+.BR \-a \||\| \-al \||\| \-as
+Turn on assembly listings; `\|\c
+.B \-al\c
+\&\|', listing only, `\|\c
+.B \-as\c
+\&\|', symbols
+only, `\|\c
+.B \-a\c
+\&\|', everything.
+.TP
+.B \-D
+This option is accepted only for script compatibility with calls to
+other assemblers; it has no effect on \c
+.B as\c
+\&.
+.TP
+.B \-f
+``fast''--skip preprocessing (assume source is compiler output).
+.TP
+.BI "\-I\ " path
+Add
+.I path
+to the search list for
+.B .include
+directives.
+.TP
+.B \-k
+Handle position independent code, generated by gcc -fpic.
+.TP
+.B \-L
+Keep (in symbol table) local symbols, starting with `\|\c
+.B L\c
+\|'
+.TP
+.BI "\-o\ " objfile
+Name the object-file output from \c
+.B as
+.TP
+.B \-R
+Fold data section into text section
+.TP
+.B \-v
+Announce \c
+.B as\c
+\& version
+.TP
+.B \-W
+Suppress warning messages
+.TP
+.IR "\-\^\-" "\ |\ " "files\|.\|.\|."
+Source files to assemble, or standard input (\c
+.BR "\-\^\-" ")"
+.TP
+.BI \-A var
+.I
+(When configured for Intel 960.)
+Specify which variant of the 960 architecture is the target.
+.TP
+.B \-b
+.I
+(When configured for Intel 960.)
+Add code to collect statistics about branches taken.
+.TP
+.B \-norelax
+.I
+(When configured for Intel 960.)
+Do not alter compare-and-branch instructions for long displacements;
+error if necessary.
+.TP
+.B \-l
+.I
+(When configured for Motorola 68000).
+.br
+Shorten references to undefined symbols, to one word instead of two.
+.TP
+.BR "\-mc68000" "\||\|" "\-mc68010" "\||\|" "\-mc68020"
+.I
+(When configured for Motorola 68000).
+.br
+Specify what processor in the 68000 family is the target (default 68020)
+
+.PP
+Options may be in any order, and may be
+before, after, or between file names. The order of file names is
+significant.
+
+`\|\c
+.B \-\^\-\c
+\|' (two hyphens) by itself names the standard input file
+explicitly, as one of the files for \c
+.B as\c
+\& to assemble.
+
+Except for `\|\c
+.B \-\^\-\c
+\|' any command line argument that begins with a
+hyphen (`\|\c
+.B \-\c
+\|') is an option. Each option changes the behavior of
+\c
+.B as\c
+\&. No option changes the way another option works. An
+option is a `\|\c
+.B \-\c
+\|' followed by one or more letters; the case of
+the letter is important. All options are optional.
+
+The `\|\c
+.B \-o\c
+\|' option expects exactly one file name to follow. The file
+name may either immediately follow the option's letter (compatible
+with older assemblers) or it may be the next command argument (GNU
+standard).
+
+These two command lines are equivalent:
+.br
+.B
+as\ \ \-o\ \ my\-object\-file.o\ \ mumble.s
+.br
+.B
+as\ \ \-omy\-object\-file.o\ \ mumble.s
+
+.SH "SEE ALSO"
+.RB "`\|" as "\|'"
+entry in
+.B
+info\c
+\&;
+.I
+Using as: The GNU Assembler\c
+\&;
+.BR gcc "(" 1 "),"
+.BR ld "(" 1 ")."
+
+.SH COPYING
+Copyright (c) 1991, 1992 Free Software Foundation, Inc.
+.PP
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+.PP
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+.PP
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
diff --git a/gnu/usr.bin/as/as.1aout b/gnu/usr.bin/as/as.1aout
new file mode 100644
index 0000000..57e2dc1
--- /dev/null
+++ b/gnu/usr.bin/as/as.1aout
@@ -0,0 +1,271 @@
+.\" Copyright (c) 1991, 1992 Free Software Foundation
+.\" See section COPYING for conditions for redistribution
+.TH as 1 "21 January 1992" "cygnus support" "GNU Development Tools"
+
+.SH NAME
+GNU as \- the portable GNU assembler.
+
+.SH SYNOPSIS
+.na
+.B as
+.RB "[\|" \-a "\||\|" \-al "\||\|" -as\c
+\&\|]
+.RB "[\|" \-D "\|]"
+.RB "[\|" \-f "\|]"
+.RB "[\|" \-I
+.I path\c
+\&\|]
+.RB "[\|" \-k "\|]"
+.RB "[\|" \-L "\|]"
+.RB "[\|" \-o
+.I objfile\c
+\&\|]
+.RB "[\|" \-R "\|]"
+.RB "[\|" \-v "\|]"
+.RB "[\|" \-w "\|]"
+.RB "[\|" \-\^\- "\ |\ " \c
+.I files\c
+\&\|.\|.\|.\|]
+
+.I i960-only options:
+.br
+.RB "[\|" \-ACA "\||\|" \-ACA_A "\||\|" \-ACB\c
+.RB "\||\|" \-ACC "\||\|" \-AKA "\||\|" \-AKB\c
+.RB "\||\|" \-AKC "\||\|" \-AMC "\|]"
+.RB "[\|" \-b "\|]"
+.RB "[\|" \-norelax "\|]"
+
+.I m680x0-only options:
+.br
+.RB "[\|" \-l "\|]"
+.RB "[\|" \-mc68000 "\||\|" \-mc68010 "\||\|" \-mc68020 "\|]"
+.ad b
+
+.SH DESCRIPTION
+GNU \c
+.B as\c
+\& is really a family of assemblers.
+If you use (or have used) the GNU assembler on one architecture, you
+should find a fairly similar environment when you use it on another
+architecture. Each version has much in common with the others,
+including object file formats, most assembler directives (often called
+\c
+.I pseudo-ops)\c
+\& and assembler syntax.
+
+For information on the syntax and pseudo-ops used by GNU \c
+.B as\c
+\&, see `\|\c
+.B as\c
+\|' entry in \c
+.B info \c
+(or the manual \c
+.I
+.I
+Using as: The GNU Assembler\c
+\&).
+
+\c
+.B as\c
+\& is primarily intended to assemble the output of the GNU C
+compiler \c
+.B gcc\c
+\& for use by the linker \c
+.B ld\c
+\&. Nevertheless,
+we've tried to make \c
+.B as\c
+\& assemble correctly everything that the native
+assembler would.
+This doesn't mean \c
+.B as\c
+\& always uses the same syntax as another
+assembler for the same architecture; for example, we know of several
+incompatible versions of 680x0 assembly language syntax.
+
+Each time you run \c
+.B as\c
+\& it assembles exactly one source
+program. The source program is made up of one or more files.
+(The standard input is also a file.)
+
+If \c
+.B as\c
+\& is given no file names it attempts to read one input file
+from the \c
+.B as\c
+\& standard input, which is normally your terminal. You
+may have to type \c
+.B ctl-D\c
+\& to tell \c
+.B as\c
+\& there is no more program
+to assemble. Use `\|\c
+.B \-\^\-\c
+\|' if you need to explicitly name the standard input file
+in your command line.
+
+.B as\c
+\& may write warnings and error messages to the standard error
+file (usually your terminal). This should not happen when \c
+.B as\c
+\& is
+run automatically by a compiler. Warnings report an assumption made so
+that \c
+.B as\c
+\& could keep assembling a flawed program; errors report a
+grave problem that stops the assembly.
+
+.SH OPTIONS
+.TP
+.BR \-a \||\| \-al \||\| \-as
+Turn on assembly listings; `\|\c
+.B \-al\c
+\&\|', listing only, `\|\c
+.B \-as\c
+\&\|', symbols
+only, `\|\c
+.B \-a\c
+\&\|', everything.
+.TP
+.B \-D
+This option is accepted only for script compatibility with calls to
+other assemblers; it has no effect on \c
+.B as\c
+\&.
+.TP
+.B \-f
+``fast''--skip preprocessing (assume source is compiler output).
+.TP
+.BI "\-I\ " path
+Add
+.I path
+to the search list for
+.B .include
+directives.
+.TP
+.B \-k
+Handle position independent code, generated by gcc -fpic.
+.TP
+.B \-L
+Keep (in symbol table) local symbols, starting with `\|\c
+.B L\c
+\|'
+.TP
+.BI "\-o\ " objfile
+Name the object-file output from \c
+.B as
+.TP
+.B \-R
+Fold data section into text section
+.TP
+.B \-v
+Announce \c
+.B as\c
+\& version
+.TP
+.B \-W
+Suppress warning messages
+.TP
+.IR "\-\^\-" "\ |\ " "files\|.\|.\|."
+Source files to assemble, or standard input (\c
+.BR "\-\^\-" ")"
+.TP
+.BI \-A var
+.I
+(When configured for Intel 960.)
+Specify which variant of the 960 architecture is the target.
+.TP
+.B \-b
+.I
+(When configured for Intel 960.)
+Add code to collect statistics about branches taken.
+.TP
+.B \-norelax
+.I
+(When configured for Intel 960.)
+Do not alter compare-and-branch instructions for long displacements;
+error if necessary.
+.TP
+.B \-l
+.I
+(When configured for Motorola 68000).
+.br
+Shorten references to undefined symbols, to one word instead of two.
+.TP
+.BR "\-mc68000" "\||\|" "\-mc68010" "\||\|" "\-mc68020"
+.I
+(When configured for Motorola 68000).
+.br
+Specify what processor in the 68000 family is the target (default 68020)
+
+.PP
+Options may be in any order, and may be
+before, after, or between file names. The order of file names is
+significant.
+
+`\|\c
+.B \-\^\-\c
+\|' (two hyphens) by itself names the standard input file
+explicitly, as one of the files for \c
+.B as\c
+\& to assemble.
+
+Except for `\|\c
+.B \-\^\-\c
+\|' any command line argument that begins with a
+hyphen (`\|\c
+.B \-\c
+\|') is an option. Each option changes the behavior of
+\c
+.B as\c
+\&. No option changes the way another option works. An
+option is a `\|\c
+.B \-\c
+\|' followed by one or more letters; the case of
+the letter is important. All options are optional.
+
+The `\|\c
+.B \-o\c
+\|' option expects exactly one file name to follow. The file
+name may either immediately follow the option's letter (compatible
+with older assemblers) or it may be the next command argument (GNU
+standard).
+
+These two command lines are equivalent:
+.br
+.B
+as\ \ \-o\ \ my\-object\-file.o\ \ mumble.s
+.br
+.B
+as\ \ \-omy\-object\-file.o\ \ mumble.s
+
+.SH "SEE ALSO"
+.RB "`\|" as "\|'"
+entry in
+.B
+info\c
+\&;
+.I
+Using as: The GNU Assembler\c
+\&;
+.BR gcc "(" 1 "),"
+.BR ld "(" 1 ")."
+
+.SH COPYING
+Copyright (c) 1991, 1992 Free Software Foundation, Inc.
+.PP
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+.PP
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+.PP
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
diff --git a/gnu/usr.bin/as/as.c b/gnu/usr.bin/as/as.c
new file mode 100644
index 0000000..a9cbb70
--- /dev/null
+++ b/gnu/usr.bin/as/as.c
@@ -0,0 +1,429 @@
+/* as.c - GAS main program.
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * Main program for AS; a 32-bit assembler of GNU.
+ * Understands command arguments.
+ * Has a few routines that don't fit in other modules because they
+ * are shared.
+ *
+ *
+ * bugs
+ *
+ * : initialisers
+ * Since no-one else says they will support them in future: I
+ * don't support them now.
+ *
+ */
+#ifndef lint
+static char rcsid[] = "$Id: as.c,v 1.3 1993/10/02 20:57:15 pk Exp $";
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef _POSIX_SOURCE
+#include <sys/types.h> /* For pid_t in signal.h */
+#endif
+#include <signal.h>
+
+#define COMMON
+
+#include "as.h"
+#include "subsegs.h"
+#if __STDC__ == 1
+
+/* This prototype for got_sig() is ansi. If you want
+ anything else, then your compiler is lying to you when
+ it says that it is __STDC__. If you want to change it,
+ #ifdef protect it from those of us with real ansi
+ compilers. */
+
+#define SIGTY void
+
+static void got_sig(int sig);
+static char *stralloc(char *str);
+static void perform_an_assembly_pass(int argc, char **argv);
+
+#else /* __STDC__ */
+
+#ifndef SIGTY
+#define SIGTY int
+#endif
+
+static SIGTY got_sig();
+static char *stralloc(); /* Make a (safe) copy of a string. */
+static void perform_an_assembly_pass();
+
+#endif /* not __STDC__ */
+
+#ifdef DONTDEF
+static char * gdb_symbol_file_name;
+long gdb_begin();
+#endif
+
+int listing; /* true if a listing is wanted */
+
+char *myname; /* argv[0] */
+extern const char version_string[];
+
+int main(argc,argv)
+int argc;
+char **argv;
+{
+ int work_argc; /* variable copy of argc */
+ char **work_argv; /* variable copy of argv */
+ char *arg; /* an arg to program */
+ char a; /* an arg flag (after -) */
+ static const int sig[] = { SIGHUP, SIGINT, SIGPIPE, SIGTERM, 0};
+
+ for (a=0;sig[a] != 0;a++)
+ if (signal(sig[a], SIG_IGN) != SIG_IGN)
+ signal(sig[a], got_sig);
+
+ myname=argv[0];
+ memset(flagseen, '\0', sizeof(flagseen)); /* aint seen nothing yet */
+#ifndef OBJ_DEFAULT_OUTPUT_FILE_NAME
+#define OBJ_DEFAULT_OUTPUT_FILE_NAME "a.out"
+#endif /* OBJ_DEFAULT_OUTPUT_FILE_NAME */
+ out_file_name = OBJ_DEFAULT_OUTPUT_FILE_NAME;
+
+ symbol_begin(); /* symbols.c */
+ subsegs_begin(); /* subsegs.c */
+ read_begin(); /* read.c */
+ md_begin(); /* MACHINE.c */
+ input_scrub_begin(); /* input_scrub.c */
+#ifdef DONTDEF
+ gdb_symbol_file_name = 0;
+#endif
+ /*
+ * Parse arguments, but we are only interested in flags.
+ * When we find a flag, we process it then make it's argv[] NULL.
+ * This helps any future argv[] scanners avoid what we processed.
+ * Since it is easy to do here we interpret the special arg "-"
+ * to mean "use stdin" and we set that argv[] pointing to "".
+ * After we have munged argv[], the only things left are source file
+ * name(s) and ""(s) denoting stdin. These file names are used
+ * (perhaps more than once) later.
+ */
+ /* FIXME-SOMEDAY this should use getopt. */
+ work_argc = argc-1; /* don't count argv[0] */
+ work_argv = argv+1; /* skip argv[0] */
+ for (;work_argc--;work_argv++) {
+ arg = * work_argv; /* work_argv points to this argument */
+
+ if (*arg != '-') /* Filename. We need it later. */
+ continue; /* Keep scanning args looking for flags. */
+ if (arg[1] == '-' && arg[2] == 0) {
+ /* "--" as an argument means read STDIN */
+ /* on this scan, we don't want to think about filenames */
+ * work_argv = ""; /* Code that means 'use stdin'. */
+ continue;
+ }
+ /* This better be a switch. */
+ arg ++; /*->letter. */
+
+ while ((a = * arg) != '\0') {/* scan all the 1-char flags */
+ arg ++; /* arg->after letter. */
+ a &= 0x7F; /* ascii only please */
+ /* if (flagseen[a])
+ as_tsktsk("%s: Flag option - %c has already been seen.", myname, a); */
+ flagseen[a] = 1;
+ switch (a) {
+
+ case 'a':
+ {
+ int loop =1;
+
+ while (loop) {
+ switch (*arg)
+ {
+ case 'l':
+ listing |= LISTING_LISTING;
+ arg++;
+ break;
+ case 's':
+ listing |= LISTING_SYMBOLS;
+ arg++;
+ break;
+ case 'h':
+ listing |= LISTING_HLL;
+ arg++;
+ break;
+
+ case 'n':
+ listing |= LISTING_NOFORM;
+ arg++;
+ break;
+ case 'd':
+ listing |= LISTING_NODEBUG;
+ arg++;
+ break;
+ default:
+ if (!listing)
+ listing= LISTING_DEFAULT;
+ loop = 0;
+ break;
+ }
+ }
+ }
+
+ break;
+
+
+ case 'f':
+ break; /* -f means fast - no need for "app" preprocessor. */
+
+ case 'D':
+ /* DEBUG is implemented: it debugs different */
+ /* things to other people's assemblers. */
+ break;
+
+#ifdef DONTDEF
+ case 'G': /* GNU AS switch: include gdbsyms. */
+ if (*arg) /* Rest of argument is file-name. */
+ gdb_symbol_file_name = stralloc (arg);
+ else if (work_argc) { /* Next argument is file-name. */
+ work_argc --;
+ * work_argv = NULL; /* Not a source file-name. */
+ gdb_symbol_file_name = * ++ work_argv;
+ } else
+ as_warn("%s: I expected a filename after -G", myname);
+ arg = ""; /* Finished with this arg. */
+ break;
+#endif
+
+ case 'I': { /* Include file directory */
+
+ char *temp = NULL;
+ if (*arg)
+ temp = stralloc (arg);
+ else if (work_argc) {
+ * work_argv = NULL;
+ work_argc--;
+ temp = * ++ work_argv;
+ } else
+ as_warn("%s: I expected a filename after -I", myname);
+ add_include_dir (temp);
+ arg = ""; /* Finished with this arg. */
+ break;
+ }
+
+#if 00000
+ case 'k':
+ break;
+#endif
+
+ case 'L': /* -L means keep L* symbols */
+ break;
+
+ case 'o':
+ if (*arg) /* Rest of argument is object file-name. */
+ out_file_name = stralloc (arg);
+ else if (work_argc) { /* Want next arg for a file-name. */
+ * work_argv = NULL; /* This is not a file-name. */
+ work_argc--;
+ out_file_name = * ++ work_argv;
+ } else
+ as_warn("%s: I expected a filename after -o. \"%s\" assumed.", myname, out_file_name);
+ arg = ""; /* Finished with this arg. */
+ break;
+
+ case 'R':
+ /* -R means put data into text segment */
+ break;
+
+ case 'v':
+#ifdef OBJ_VMS
+ {
+ extern char *compiler_version_string;
+ compiler_version_string = arg;
+ }
+#else /* not OBJ_VMS */
+ fprintf(stderr,version_string);
+ if (*arg && strcmp(arg,"ersion"))
+ as_warn("Unknown -v option ignored");
+#endif /* not OBJ_VMS */
+ while (*arg) arg++; /* Skip the rest */
+ break;
+
+ case 'W':
+ /* -W means don't warn about things */
+ case 'X':
+ /* -X means treat warnings as errors */
+ case 'Z':
+ /* -Z means attempt to generate object file even after errors. */
+ break;
+
+ default:
+ --arg;
+ if (md_parse_option(&arg,&work_argc,&work_argv) == 0)
+ as_warn("%s: I don't understand '%c' flag.", myname, a);
+ if (arg && *arg)
+ arg++;
+ break;
+ }
+ }
+ /*
+ * We have just processed a "-..." arg, which was not a
+ * file-name. Smash it so the
+ * things that look for filenames won't ever see it.
+ *
+ * Whatever work_argv points to, it has already been used
+ * as part of a flag, so DON'T re-use it as a filename.
+ */
+ *work_argv = NULL; /* NULL means 'not a file-name' */
+ }
+#ifdef DONTDEF
+ if (gdb_begin(gdb_symbol_file_name) == 0)
+ flagseen['G'] = 0; /* Don't do any gdbsym stuff. */
+#endif
+ /* Here with flags set up in flagseen[]. */
+
+ perform_an_assembly_pass(argc,argv); /* Assemble it. */
+#ifdef TC_I960
+ brtab_emit();
+#endif
+ if (seen_at_least_1_file()
+ && !((had_warnings() && flagseen['Z'])
+ || had_errors() > 0)) {
+ write_object_file(); /* relax() addresses then emit object file */
+ } /* we also check in write_object_file() just before emit. */
+
+ input_scrub_end();
+ md_end(); /* MACHINE.c */
+
+#ifndef NO_LISTING
+ listing_print("");
+#endif
+
+#ifndef HO_VMS
+ return((had_warnings() && flagseen['Z'])
+ || had_errors() > 0); /* WIN */
+#else /* HO_VMS */
+ return(!((had_warnings() && flagseen['Z'])
+ || had_errors() > 0)); /* WIN */
+#endif /* HO_VMS */
+
+} /* main() */
+
+
+/* perform_an_assembly_pass()
+ *
+ * Here to attempt 1 pass over each input file.
+ * We scan argv[*] looking for filenames or exactly "" which is
+ * shorthand for stdin. Any argv that is NULL is not a file-name.
+ * We set need_pass_2 TRUE if, after this, we still have unresolved
+ * expressions of the form (unknown value)+-(unknown value).
+ *
+ * Note the un*x semantics: there is only 1 logical input file, but it
+ * may be a catenation of many 'physical' input files.
+ */
+static void perform_an_assembly_pass(argc, argv)
+int argc;
+char **argv;
+{
+ int saw_a_file = 0;
+ need_pass_2 = 0;
+
+#ifdef MANY_SEGMENTS
+ unsigned int i;
+
+ for (i= SEG_E0; i < SEG_UNKNOWN; i++)
+ {
+ segment_info[i].fix_root = 0;
+ }
+ /* Create the three fixed ones */
+ subseg_new (SEG_E0, 0);
+ subseg_new (SEG_E1, 0);
+ subseg_new (SEG_E2, 0);
+ strcpy(segment_info[SEG_E0].scnhdr.s_name,".text");
+ strcpy(segment_info[SEG_E1].scnhdr.s_name,".data");
+ strcpy(segment_info[SEG_E2].scnhdr.s_name,".bss");
+
+ subseg_new (SEG_E0, 0);
+#else /* not MANY_SEGMENTS */
+ text_fix_root = NULL;
+ data_fix_root = NULL;
+ bss_fix_root = NULL;
+
+ subseg_new (SEG_TEXT, 0);
+#endif /* not MANY_SEGMENTS */
+
+ argv++; /* skip argv[0] */
+ argc--; /* skip argv[0] */
+ while (argc--) {
+ if (*argv) { /* Is it a file-name argument? */
+ saw_a_file++;
+ /* argv->"" if stdin desired, else->filename */
+ read_a_source_file(*argv);
+ }
+ argv++; /* completed that argv */
+ }
+ if (!saw_a_file)
+ read_a_source_file("");
+} /* perform_an_assembly_pass() */
+
+/*
+ * stralloc()
+ *
+ * Allocate memory for a new copy of a string. Copy the string.
+ * Return the address of the new string. Die if there is any error.
+ */
+
+static char *
+ stralloc (str)
+char * str;
+{
+ register char * retval;
+ register long len;
+
+ len = strlen (str) + 1;
+ retval = xmalloc (len);
+ (void) strcpy(retval, str);
+ return(retval);
+}
+
+#ifdef comment
+static void lose() {
+ as_fatal("%s: 2nd pass not implemented - get your code from random(3)", myname);
+ return;
+} /* lose() */
+#endif /* comment */
+
+static SIGTY
+ got_sig(sig)
+int sig;
+{
+ static here_before = 0;
+
+ as_bad("Interrupted by signal %d", sig);
+ if (here_before++)
+ exit(1);
+ return((SIGTY) 0);
+}
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of as.c */
diff --git a/gnu/usr.bin/as/as.h b/gnu/usr.bin/as/as.h
new file mode 100644
index 0000000..7b1e27d
--- /dev/null
+++ b/gnu/usr.bin/as/as.h
@@ -0,0 +1,416 @@
+/* as.h - global header file
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * $Id: as.h,v 1.3 1993/10/02 20:57:16 pk Exp $
+ */
+
+#define GAS 1
+/* #include <ansidecl.h> */
+#include "host.h"
+#include "flonum.h"
+
+#if __STDC__ != 1
+#define volatile /**/
+#ifndef const
+#define const /**/
+#endif /* const */
+#endif /* __STDC__ */
+
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#define register
+#endif /* __GNUC__ */
+
+#ifndef __LINE__
+#define __LINE__ "unknown"
+#endif /* __LINE__ */
+
+#ifndef __FILE__
+#define __FILE__ "unknown"
+#endif /* __FILE__ */
+
+/*
+ * I think this stuff is largely out of date. xoxorich.
+ *
+ * CAPITALISED names are #defined.
+ * "lowercaseH" is #defined if "lowercase.h" has been #include-d.
+ * "lowercaseT" is a typedef of "lowercase" objects.
+ * "lowercaseP" is type "pointer to object of type 'lowercase'".
+ * "lowercaseS" is typedef struct ... lowercaseS.
+ *
+ * #define DEBUG to enable all the "know" assertion tests.
+ * #define SUSPECT when debugging.
+ * #define COMMON as "extern" for all modules except one, where you #define
+ * COMMON as "".
+ * If TEST is #defined, then we are testing a module: #define COMMON as "".
+ */
+
+/* These #defines are for parameters of entire assembler. */
+
+/* #define SUSPECT JF remove for speed testing */
+/* These #includes are for type definitions etc. */
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free xfree
+
+#define xfree free
+
+#define BAD_CASE(value) \
+{ \
+ as_fatal("Case value %d unexpected at line %d of file \"%s\"\n", \
+ value, __LINE__, __FILE__); \
+ }
+
+
+/* These are assembler-wide concepts */
+
+
+#ifndef COMMON
+#ifdef TEST
+#define COMMON /* declare our COMMONs storage here. */
+#else
+#define COMMON extern /* our commons live elswhere */
+#endif
+#endif
+/* COMMON now defined */
+#define DEBUG /* temporary */
+
+#ifdef DEBUG
+#undef NDEBUG
+#ifndef know
+#define know(p) assert(p) /* Verify our assumptions! */
+#endif /* not yet defined */
+#else
+#define know(p) /* know() checks are no-op.ed */
+#endif
+
+/* input_scrub.c */
+
+/*
+ * Supplies sanitised buffers to read.c.
+ * Also understands printing line-number part of error messages.
+ */
+
+
+/* subsegs.c Sub-segments. Also, segment(=expression type)s.*/
+
+/*
+ * This table describes the use of segments as EXPRESSION types.
+ *
+ * X_seg X_add_symbol X_subtract_symbol X_add_number
+ * SEG_ABSENT no (legal) expression
+ * SEG_PASS1 no (defined) "
+ * SEG_BIG * > 32 bits const.
+ * SEG_ABSOLUTE 0
+ * SEG_DATA * 0
+ * SEG_TEXT * 0
+ * SEG_BSS * 0
+ * SEG_UNKNOWN * 0
+ * SEG_DIFFERENCE 0 * 0
+ * SEG_REGISTER *
+ *
+ * The blank fields MUST be 0, and are nugatory.
+ * The '0' fields MAY be 0. The '*' fields MAY NOT be 0.
+ *
+ * SEG_BIG: X_add_number is < 0 if the result is in
+ * generic_floating_point_number. The value is -'c' where c is the
+ * character that introduced the constant. e.g. "0f6.9" will have -'f'
+ * as a X_add_number value.
+ * X_add_number > 0 is a count of how many littlenums it took to
+ * represent a bignum.
+ * SEG_DIFFERENCE:
+ * If segments of both symbols are known, they are the same segment.
+ * X_add_symbol != X_sub_symbol (then we just cancel them, => SEG_ABSOLUTE).
+ */
+
+
+#ifdef MANY_SEGMENTS
+#define N_SEGMENTS 10
+#define SEG_NORMAL(x) ((x) >= SEG_E0 && (x) <= SEG_E9)
+#define SEG_LIST SEG_E0,SEG_E1,SEG_E2,SEG_E3,SEG_E4,SEG_E5,SEG_E6,SEG_E7,SEG_E8,SEG_E9
+#define SEG_DATA SEG_E1
+#define SEG_TEXT SEG_E0
+#define SEG_BSS SEG_E2
+#else
+#define N_SEGMENTS 3
+#define SEG_NORMAL(x) ((x) == SEG_TEXT || (x) == SEG_DATA || (x) == SEG_BSS)
+#define SEG_LIST SEG_TEXT,SEG_DATA,SEG_BSS
+#endif
+
+typedef enum _segT {
+ SEG_ABSOLUTE = 0,
+ SEG_LIST,
+ SEG_UNKNOWN,
+ SEG_ABSENT, /* Mythical Segment (absent): NO expression seen. */
+ SEG_PASS1, /* Mythical Segment: Need another pass. */
+ SEG_GOOF, /* Only happens if AS has a logic error. */
+ /* Invented so we don't crash printing */
+ /* error message involving weird segment. */
+ SEG_BIG, /* Bigger than 32 bits constant. */
+ SEG_DIFFERENCE, /* Mythical Segment: absolute difference. */
+ SEG_DEBUG, /* Debug segment */
+ SEG_NTV, /* Transfert vector preload segment */
+ SEG_PTV, /* Transfert vector postload segment */
+ SEG_REGISTER, /* Mythical: a register-valued expression */
+} segT;
+
+#define SEG_MAXIMUM_ORDINAL (SEG_REGISTER)
+
+typedef int subsegT;
+
+COMMON subsegT now_subseg;
+/* What subseg we are accreting now? */
+
+
+COMMON segT now_seg;
+/* Segment our instructions emit to. */
+/* Only OK values are SEG_TEXT or SEG_DATA. */
+
+
+extern char *const seg_name[];
+extern int section_alignment[];
+
+
+/* relax() */
+
+typedef enum _relax_state {
+ rs_fill, /* Variable chars to be repeated fr_offset times. Fr_symbol
+ unused. Used with fr_offset == 0 for a constant length
+ frag. */
+
+ rs_align, /* Align: Fr_offset: power of 2. 1 variable char: fill
+ character. */
+
+ rs_org, /* Org: Fr_offset, fr_symbol: address. 1 variable char: fill
+ character. */
+
+ rs_machine_dependent,
+
+#ifndef WORKING_DOT_WORD
+ rs_broken_word, /* JF: gunpoint */
+#endif
+} relax_stateT;
+
+/* typedef unsigned char relax_substateT; */
+/* JF this is more likely to leave the end of a struct frag on an align
+ boundry. Be very careful with this. */
+typedef unsigned long relax_substateT;
+
+typedef unsigned long relax_addressT;/* Enough bits for address. */
+/* Still an integer type. */
+
+
+/* frags.c */
+
+/*
+ * A code fragment (frag) is some known number of chars, followed by some
+ * unknown number of chars. Typically the unknown number of chars is an
+ * instruction address whose size is yet unknown. We always know the greatest
+ * possible size the unknown number of chars may become, and reserve that
+ * much room at the end of the frag.
+ * Once created, frags do not change address during assembly.
+ * We chain the frags in (a) forward-linked list(s). The object-file address
+ * of the 1st char of a frag is generally not known until after relax().
+ * Many things at assembly time describe an address by {object-file-address
+ * of a particular frag}+offset.
+
+ BUG: it may be smarter to have a single pointer off to various different
+ notes for different frag kinds. See how code pans
+ */
+struct frag /* a code fragment */
+{
+ unsigned long fr_address; /* Object file address. */
+ struct frag *fr_next; /* Chain forward; ascending address order. */
+ /* Rooted in frch_root. */
+
+ long fr_fix; /* (Fixed) number of chars we know we have. */
+ /* May be 0. */
+ long fr_var; /* (Variable) number of chars after above. */
+ /* May be 0. */
+ struct symbol *fr_symbol; /* For variable-length tail. */
+ long fr_offset; /* For variable-length tail. */
+ char *fr_opcode; /*->opcode low addr byte,for relax()ation*/
+ relax_stateT fr_type; /* What state is my tail in? */
+ relax_substateT fr_subtype;
+ /* These are needed only on the NS32K machines */
+ char fr_pcrel_adjust;
+ char fr_bsr;
+#ifndef NO_LISTING
+ struct list_info_struct *line;
+#endif
+ char fr_literal[1]; /* Chars begin here. */
+ /* One day we will compile fr_literal[0]. */
+};
+#define SIZEOF_STRUCT_FRAG \
+((int)zero_address_frag.fr_literal-(int)&zero_address_frag)
+/* We want to say fr_literal[0] above. */
+
+typedef struct frag fragS;
+
+COMMON fragS *frag_now; /* -> current frag we are building. */
+/* This frag is incomplete. */
+/* It is, however, included in frchain_now. */
+/* Frag_now->fr_fix is bogus. Use: */
+/* Virtual frag_now->fr_fix == obstack_next_free(&frags)-frag_now->fr_literal.*/
+
+COMMON fragS zero_address_frag; /* For foreign-segment symbol fixups. */
+COMMON fragS bss_address_frag; /* For local common (N_BSS segment) fixups. */
+
+/* main program "as.c" (command arguments etc) */
+
+COMMON char
+ flagseen[128]; /* ['x'] TRUE if "-x" seen. */
+
+COMMON char *
+ out_file_name; /* name of emitted object file */
+
+COMMON int need_pass_2; /* TRUE if we need a second pass. */
+
+typedef struct {
+ char * poc_name; /* assembler mnemonic, lower case, no '.' */
+ void (*poc_handler)(); /* Do the work */
+ int poc_val; /* Value to pass to handler */
+} pseudo_typeS;
+
+#if (__STDC__ == 1) & !defined(NO_STDARG)
+
+int had_errors(void);
+int had_warnings(void);
+void as_bad(const char *Format, ...);
+void as_fatal(const char *Format, ...);
+void as_tsktsk(const char *Format, ...);
+void as_warn(const char *Format, ...);
+
+#else
+
+int had_errors();
+int had_warnings();
+void as_bad();
+void as_fatal();
+void as_tsktsk();
+void as_warn();
+
+#endif /* __STDC__ & !NO_STDARG */
+
+#if __STDC__ == 1
+
+char *app_push(void);
+char *atof_ieee(char *str, int what_kind, LITTLENUM_TYPE *words);
+char *input_scrub_include_file(char *filename, char *position);
+char *input_scrub_new_file(char *filename);
+char *input_scrub_next_buffer(char **bufp);
+char *strstr(const char *s, const char *wanted);
+char *xmalloc(int size);
+char *xrealloc(char *ptr, long n);
+int do_scrub_next_char(int (*get)(), void (*unget)());
+int gen_to_words(LITTLENUM_TYPE *words, int precision, long exponent_bits);
+int had_err(void);
+int had_errors(void);
+int had_warnings(void);
+int ignore_input(void);
+int scrub_from_file(void);
+int scrub_from_file(void);
+int scrub_from_string(void);
+int seen_at_least_1_file(void);
+void app_pop(char *arg);
+void as_howmuch(FILE *stream);
+void as_perror(char *gripe, char *filename);
+void as_where(void);
+void bump_line_counters(void);
+void do_scrub_begin(void);
+void input_scrub_begin(void);
+void input_scrub_close(void);
+void input_scrub_end(void);
+void int_to_gen(long x);
+void new_logical_line(char *fname, int line_number);
+void scrub_to_file(int ch);
+void scrub_to_string(int ch);
+void subseg_change(segT seg, int subseg);
+void subseg_new(segT seg, subsegT subseg);
+void subsegs_begin(void);
+
+#else /* not __STDC__ */
+
+char *app_push();
+char *atof_ieee();
+char *input_scrub_include_file();
+char *input_scrub_new_file();
+char *input_scrub_next_buffer();
+char *strstr();
+char *xmalloc();
+char *xrealloc();
+int do_scrub_next_char();
+int gen_to_words();
+int had_err();
+int had_errors();
+int had_warnings();
+int ignore_input();
+int scrub_from_file();
+int scrub_from_file();
+int scrub_from_string();
+int seen_at_least_1_file();
+void app_pop();
+void as_howmuch();
+void as_perror();
+void as_where();
+void bump_line_counters();
+void do_scrub_begin();
+void input_scrub_begin();
+void input_scrub_close();
+void input_scrub_end();
+void int_to_gen();
+void new_logical_line();
+void scrub_to_file();
+void scrub_to_string();
+void subseg_change();
+void subseg_new();
+void subsegs_begin();
+
+#endif /* not __STDC__ */
+
+/* this one starts the chain of target dependant headers */
+#include "targ-env.h"
+
+/* these define types needed by the interfaces */
+#include "struc-symbol.h"
+#include "write.h"
+#include "expr.h"
+#include "frags.h"
+#include "hash.h"
+#include "read.h"
+#include "symbols.h"
+
+#include "tc.h"
+#include "obj.h"
+
+#include "listing.h"
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of as.h */
diff --git a/gnu/usr.bin/as/atof-generic.c b/gnu/usr.bin/as/atof-generic.c
new file mode 100644
index 0000000..b4e0971
--- /dev/null
+++ b/gnu/usr.bin/as/atof-generic.c
@@ -0,0 +1,526 @@
+/* atof_generic.c - turn a string of digits into a Flonum
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef lint
+static char rcsid[] = "$Id: atof-generic.c,v 1.3 1993/10/02 20:57:17 pk Exp $";
+#endif
+
+#include <ctype.h>
+#include <string.h>
+
+#include "as.h"
+
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else
+#ifdef sparc
+#include <alloca.h>
+#endif
+#endif
+
+/* #define FALSE (0) */
+/* #define TRUE (1) */
+
+/***********************************************************************\
+ * *
+ * Given a string of decimal digits , with optional decimal *
+ * mark and optional decimal exponent (place value) of the *
+ * lowest_order decimal digit: produce a floating point *
+ * number. The number is 'generic' floating point: our *
+ * caller will encode it for a specific machine architecture. *
+ * *
+ * Assumptions *
+ * uses base (radix) 2 *
+ * this machine uses 2's complement binary integers *
+ * target flonums use " " " " *
+ * target flonums exponents fit in a long *
+ * *
+ \***********************************************************************/
+
+/*
+
+ Syntax:
+
+ <flonum> ::= <optional-sign> <decimal-number> <optional-exponent>
+ <optional-sign> ::= '+' | '-' | {empty}
+ <decimal-number> ::= <integer>
+ | <integer> <radix-character>
+ | <integer> <radix-character> <integer>
+ | <radix-character> <integer>
+
+ <optional-exponent> ::= {empty}
+ | <exponent-character> <optional-sign> <integer>
+
+ <integer> ::= <digit> | <digit> <integer>
+ <digit> ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
+ <exponent-character> ::= {one character from "string_of_decimal_exponent_marks"}
+ <radix-character> ::= {one character from "string_of_decimal_marks"}
+
+ */
+
+int /* 0 if OK */
+ atof_generic (
+ address_of_string_pointer, /* return pointer to just
+ AFTER number we read. */
+ string_of_decimal_marks, /* At most one per number. */
+ string_of_decimal_exponent_marks,
+ address_of_generic_floating_point_number)
+char **address_of_string_pointer;
+const char *string_of_decimal_marks;
+const char *string_of_decimal_exponent_marks;
+FLONUM_TYPE *address_of_generic_floating_point_number;
+{
+ int return_value; /* 0 means OK. */
+ char * first_digit;
+ /* char *last_digit; JF unused */
+ int number_of_digits_before_decimal;
+ int number_of_digits_after_decimal;
+ long decimal_exponent;
+ int number_of_digits_available;
+ char digits_sign_char;
+
+ /*
+ * Scan the input string, abstracting (1)digits (2)decimal mark (3) exponent.
+ * It would be simpler to modify the string, but we don't; just to be nice
+ * to caller.
+ * We need to know how many digits we have, so we can allocate space for
+ * the digits' value.
+ */
+
+ char *p;
+ char c;
+ int seen_significant_digit;
+
+ first_digit = *address_of_string_pointer;
+ c = *first_digit;
+
+ if (c == '-' || c == '+') {
+ digits_sign_char = c;
+ first_digit++;
+ } else
+ digits_sign_char = '+';
+
+ if ((first_digit[0] == 'n' || first_digit[0] == 'N')
+ && (first_digit[1] == 'a' || first_digit[1] == 'A')
+ && (first_digit[2] == 'n' || first_digit[2] == 'N')) {
+ address_of_generic_floating_point_number->sign = 0;
+ address_of_generic_floating_point_number->exponent = 0;
+ address_of_generic_floating_point_number->leader =
+ address_of_generic_floating_point_number->low;
+ *address_of_string_pointer = first_digit + 3;
+ return(0);
+ }
+
+ /* 99e999 is a "special" token to some older, broken compilers. */
+ if ((first_digit[0] == 'i' || first_digit[0] == 'I')
+ && (first_digit[1] == 'n' || first_digit[1] == 'N')
+ && (first_digit[2] == 'f' || first_digit[2] == 'F')) {
+ address_of_generic_floating_point_number->sign =
+ digits_sign_char == '+' ? 'P' : 'N';
+ address_of_generic_floating_point_number->exponent = 0;
+ address_of_generic_floating_point_number->leader =
+ address_of_generic_floating_point_number->low;
+
+ if ((first_digit[3] == 'i' || first_digit[3] == 'I')
+ && (first_digit[4] == 'n' || first_digit[4] == 'N')
+ && (first_digit[5] == 'i' || first_digit[5] == 'I')
+ && (first_digit[6] == 't' || first_digit[6] == 'T')
+ && (first_digit[7] == 'y' || first_digit[7] == 'Y')) {
+ *address_of_string_pointer = first_digit + 8;
+ } else {
+ *address_of_string_pointer = first_digit + 3;
+ }
+ return(0);
+ }
+
+ if (strncmp(first_digit, "99e999", 6) == 0) {
+ address_of_generic_floating_point_number->sign =
+ digits_sign_char == '+' ? 'P' : 'N';
+ address_of_generic_floating_point_number->exponent = 0;
+ address_of_generic_floating_point_number->leader =
+ address_of_generic_floating_point_number->low;
+ *address_of_string_pointer = first_digit + 6;
+ return(0);
+ }
+
+ number_of_digits_before_decimal = 0;
+ number_of_digits_after_decimal = 0;
+ decimal_exponent = 0;
+ seen_significant_digit = 0;
+ for (p = first_digit; (((c = * p) != '\0')
+ && (!c || ! strchr(string_of_decimal_marks, c))
+ && (!c || !strchr(string_of_decimal_exponent_marks, c)));
+ p++) {
+ if (isdigit(c)) {
+ if (seen_significant_digit || c > '0') {
+ ++number_of_digits_before_decimal;
+ seen_significant_digit = 1;
+ } else {
+ first_digit++;
+ }
+ } else {
+ break; /* p -> char after pre-decimal digits. */
+ }
+ } /* For each digit before decimal mark. */
+
+#ifndef OLD_FLOAT_READS
+ /* Ignore trailing 0's after the decimal point. The original code here
+ * (ifdef'd out) does not do this, and numbers like
+ * 4.29496729600000000000e+09 (2**31)
+ * come out inexact for some reason related to length of the digit
+ * string.
+ */
+ if (c && strchr(string_of_decimal_marks, c)) {
+ int zeros = 0; /* Length of current string of zeros */
+
+ for (p++; (c = *p) && isdigit(c); p++) {
+ if (c == '0') {
+ zeros++;
+ } else {
+ number_of_digits_after_decimal += 1 + zeros;
+ zeros = 0;
+ }
+ }
+ }
+#else
+ if (c && strchr(string_of_decimal_marks, c)) {
+ for (p++; (((c = *p) != '\0')
+ && (!c || !strchr(string_of_decimal_exponent_marks, c)));
+ p++) {
+ if (isdigit(c)) {
+ number_of_digits_after_decimal++; /* This may be retracted below. */
+ if (/* seen_significant_digit || */ c > '0') {
+ seen_significant_digit = TRUE;
+ }
+ } else {
+ if (!seen_significant_digit) {
+ number_of_digits_after_decimal = 0;
+ }
+ break;
+ }
+ } /* For each digit after decimal mark. */
+ }
+
+ while (number_of_digits_after_decimal && first_digit[number_of_digits_before_decimal
+ + number_of_digits_after_decimal] == '0')
+ --number_of_digits_after_decimal;
+ /* last_digit = p; JF unused */
+#endif
+
+ if (c && strchr(string_of_decimal_exponent_marks, c) ) {
+ char digits_exponent_sign_char;
+
+ c = *++p;
+ if (c && strchr ("+-",c)) {
+ digits_exponent_sign_char = c;
+ c = *++p;
+ } else {
+ digits_exponent_sign_char = '+';
+ }
+
+ for ( ; (c); c = *++p) {
+ if (isdigit(c)) {
+ decimal_exponent = decimal_exponent * 10 + c - '0';
+ /*
+ * BUG! If we overflow here, we lose!
+ */
+ } else {
+ break;
+ }
+ }
+
+ if (digits_exponent_sign_char == '-') {
+ decimal_exponent = -decimal_exponent;
+ }
+ }
+
+ *address_of_string_pointer = p;
+
+
+
+ number_of_digits_available =
+ number_of_digits_before_decimal + number_of_digits_after_decimal;
+ return_value = 0;
+ if (number_of_digits_available == 0) {
+ address_of_generic_floating_point_number->exponent = 0; /* Not strictly necessary */
+ address_of_generic_floating_point_number->leader
+ = -1 + address_of_generic_floating_point_number->low;
+ address_of_generic_floating_point_number->sign = digits_sign_char;
+ /* We have just concocted (+/-)0.0E0 */
+
+ } else {
+ int count; /* Number of useful digits left to scan. */
+
+ LITTLENUM_TYPE *digits_binary_low;
+ int precision;
+ int maximum_useful_digits;
+ int number_of_digits_to_use;
+ int more_than_enough_bits_for_digits;
+ int more_than_enough_littlenums_for_digits;
+ int size_of_digits_in_littlenums;
+ int size_of_digits_in_chars;
+ FLONUM_TYPE power_of_10_flonum;
+ FLONUM_TYPE digits_flonum;
+
+ precision = (address_of_generic_floating_point_number->high
+ - address_of_generic_floating_point_number->low
+ + 1); /* Number of destination littlenums. */
+
+ /* Includes guard bits (two littlenums worth) */
+ maximum_useful_digits = (((double) (precision - 2))
+ * ((double) (LITTLENUM_NUMBER_OF_BITS))
+ / (LOG_TO_BASE_2_OF_10))
+ + 2; /* 2 :: guard digits. */
+
+ if (number_of_digits_available > maximum_useful_digits) {
+ number_of_digits_to_use = maximum_useful_digits;
+ } else {
+ number_of_digits_to_use = number_of_digits_available;
+ }
+
+ decimal_exponent += number_of_digits_before_decimal - number_of_digits_to_use;
+
+ more_than_enough_bits_for_digits
+ = ((((double)number_of_digits_to_use) * LOG_TO_BASE_2_OF_10) + 1);
+
+ more_than_enough_littlenums_for_digits
+ = (more_than_enough_bits_for_digits
+ / LITTLENUM_NUMBER_OF_BITS)
+ + 2;
+
+ /*
+ * Compute (digits) part. In "12.34E56" this is the "1234" part.
+ * Arithmetic is exact here. If no digits are supplied then
+ * this part is a 0 valued binary integer.
+ * Allocate room to build up the binary number as littlenums.
+ * We want this memory to disappear when we leave this function.
+ * Assume no alignment problems => (room for n objects) ==
+ * n * (room for 1 object).
+ */
+
+ size_of_digits_in_littlenums = more_than_enough_littlenums_for_digits;
+ size_of_digits_in_chars = size_of_digits_in_littlenums
+ * sizeof(LITTLENUM_TYPE);
+
+ digits_binary_low = (LITTLENUM_TYPE *)
+ alloca(size_of_digits_in_chars);
+
+ memset((char *)digits_binary_low, '\0', size_of_digits_in_chars);
+
+ /* Digits_binary_low[] is allocated and zeroed. */
+
+ /*
+ * Parse the decimal digits as if * digits_low was in the units position.
+ * Emit a binary number into digits_binary_low[].
+ *
+ * Use a large-precision version of:
+ * (((1st-digit) * 10 + 2nd-digit) * 10 + 3rd-digit ...) * 10 + last-digit
+ */
+
+ for (p = first_digit, count = number_of_digits_to_use; count; p++, --count) {
+ c = *p;
+ if (isdigit(c)) {
+ /*
+ * Multiply by 10. Assume can never overflow.
+ * Add this digit to digits_binary_low[].
+ */
+
+ long carry;
+ LITTLENUM_TYPE *littlenum_pointer;
+ LITTLENUM_TYPE *littlenum_limit;
+
+ littlenum_limit = digits_binary_low
+ + more_than_enough_littlenums_for_digits
+ - 1;
+
+ carry = c - '0'; /* char -> binary */
+
+ for (littlenum_pointer = digits_binary_low;
+ littlenum_pointer <= littlenum_limit;
+ littlenum_pointer++) {
+ long work;
+
+ work = carry + 10 * (long) (*littlenum_pointer);
+ *littlenum_pointer = work & LITTLENUM_MASK;
+ carry = work >> LITTLENUM_NUMBER_OF_BITS;
+ }
+
+ if (carry != 0) {
+ /*
+ * We have a GROSS internal error.
+ * This should never happen.
+ */
+ as_fatal("failed sanity check."); /* RMS prefers abort() to any message. */
+ }
+ } else {
+ ++ count; /* '.' doesn't alter digits used count. */
+ } /* if valid digit */
+ } /* for each digit */
+
+
+ /*
+ * Digits_binary_low[] properly encodes the value of the digits.
+ * Forget about any high-order littlenums that are 0.
+ */
+ while (digits_binary_low[size_of_digits_in_littlenums - 1] == 0
+ && size_of_digits_in_littlenums >= 2)
+ size_of_digits_in_littlenums--;
+
+ digits_flonum.low = digits_binary_low;
+ digits_flonum.high = digits_binary_low + size_of_digits_in_littlenums - 1;
+ digits_flonum.leader = digits_flonum.high;
+ digits_flonum.exponent = 0;
+ /*
+ * The value of digits_flonum.sign should not be important.
+ * We have already decided the output's sign.
+ * We trust that the sign won't influence the other parts of the number!
+ * So we give it a value for these reasons:
+ * (1) courtesy to humans reading/debugging
+ * these numbers so they don't get excited about strange values
+ * (2) in future there may be more meaning attached to sign,
+ * and what was
+ * harmless noise may become disruptive, ill-conditioned (or worse)
+ * input.
+ */
+ digits_flonum.sign = '+';
+
+ {
+ /*
+ * Compute the mantssa (& exponent) of the power of 10.
+ * If sucessful, then multiply the power of 10 by the digits
+ * giving return_binary_mantissa and return_binary_exponent.
+ */
+
+ LITTLENUM_TYPE *power_binary_low;
+ int decimal_exponent_is_negative;
+ /* This refers to the "-56" in "12.34E-56". */
+ /* FALSE: decimal_exponent is positive (or 0) */
+ /* TRUE: decimal_exponent is negative */
+ FLONUM_TYPE temporary_flonum;
+ LITTLENUM_TYPE *temporary_binary_low;
+ int size_of_power_in_littlenums;
+ int size_of_power_in_chars;
+
+ size_of_power_in_littlenums = precision;
+ /* Precision has a built-in fudge factor so we get a few guard bits. */
+
+ decimal_exponent_is_negative = decimal_exponent < 0;
+ if (decimal_exponent_is_negative) {
+ decimal_exponent = -decimal_exponent;
+ }
+
+ /* From now on: the decimal exponent is > 0. Its sign is seperate. */
+
+ size_of_power_in_chars = size_of_power_in_littlenums
+ * sizeof(LITTLENUM_TYPE) + 2;
+
+ power_binary_low = (LITTLENUM_TYPE *) alloca(size_of_power_in_chars);
+ temporary_binary_low = (LITTLENUM_TYPE *) alloca(size_of_power_in_chars);
+ memset((char *)power_binary_low, '\0', size_of_power_in_chars);
+ * power_binary_low = 1;
+ power_of_10_flonum.exponent = 0;
+ power_of_10_flonum.low = power_binary_low;
+ power_of_10_flonum.leader = power_binary_low;
+ power_of_10_flonum.high = power_binary_low + size_of_power_in_littlenums - 1;
+ power_of_10_flonum.sign = '+';
+ temporary_flonum.low = temporary_binary_low;
+ temporary_flonum.high = temporary_binary_low + size_of_power_in_littlenums - 1;
+ /*
+ * (power) == 1.
+ * Space for temporary_flonum allocated.
+ */
+
+ /*
+ * ...
+ *
+ * WHILE more bits
+ * DO find next bit (with place value)
+ * multiply into power mantissa
+ * OD
+ */
+ {
+ int place_number_limit;
+ /* Any 10^(2^n) whose "n" exceeds this */
+ /* value will fall off the end of */
+ /* flonum_XXXX_powers_of_ten[]. */
+ int place_number;
+ const FLONUM_TYPE *multiplicand; /* -> 10^(2^n) */
+
+ place_number_limit = table_size_of_flonum_powers_of_ten;
+
+ multiplicand = (decimal_exponent_is_negative
+ ? flonum_negative_powers_of_ten
+ : flonum_positive_powers_of_ten);
+
+ for (place_number = 1; /* Place value of this bit of exponent. */
+ decimal_exponent; /* Quit when no more 1 bits in exponent. */
+ decimal_exponent >>= 1, place_number++) {
+ if (decimal_exponent & 1) {
+ if (place_number > place_number_limit) {
+ /*
+ * The decimal exponent has a magnitude so great that
+ * our tables can't help us fragment it. Although this
+ * routine is in error because it can't imagine a
+ * number that big, signal an error as if it is the
+ * user's fault for presenting such a big number.
+ */
+ return_value = ERROR_EXPONENT_OVERFLOW;
+ /*
+ * quit out of loop gracefully
+ */
+ decimal_exponent = 0;
+ } else {
+#ifdef TRACE
+ printf("before multiply, place_number = %d., power_of_10_flonum:\n",
+ place_number);
+
+ flonum_print(&power_of_10_flonum);
+ (void)putchar('\n');
+#endif
+ flonum_multip(multiplicand + place_number,
+ &power_of_10_flonum, &temporary_flonum);
+ flonum_copy(&temporary_flonum, &power_of_10_flonum);
+ } /* If this bit of decimal_exponent was computable.*/
+ } /* If this bit of decimal_exponent was set. */
+ } /* For each bit of binary representation of exponent */
+#ifdef TRACE
+ printf(" after computing power_of_10_flonum: ");
+ flonum_print(&power_of_10_flonum );
+ (void) putchar('\n');
+#endif
+ }
+
+ }
+
+ /*
+ * power_of_10_flonum is power of ten in binary (mantissa) , (exponent).
+ * It may be the number 1, in which case we don't NEED to multiply.
+ *
+ * Multiply (decimal digits) by power_of_10_flonum.
+ */
+
+ flonum_multip(&power_of_10_flonum, &digits_flonum, address_of_generic_floating_point_number);
+ /* Assert sign of the number we made is '+'. */
+ address_of_generic_floating_point_number->sign = digits_sign_char;
+
+ } /* If we had any significant digits. */
+ return(return_value);
+} /* atof_generic () */
+
+/* end of atof_generic.c */
diff --git a/gnu/usr.bin/as/bignum-copy.c b/gnu/usr.bin/as/bignum-copy.c
new file mode 100644
index 0000000..08c92f8
--- /dev/null
+++ b/gnu/usr.bin/as/bignum-copy.c
@@ -0,0 +1,76 @@
+/* bignum_copy.c - copy a bignum
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef lint
+static char rcsid[] = "$Id: bignum-copy.c,v 1.3 1993/10/02 20:57:18 pk Exp $";
+#endif
+
+#include "as.h"
+
+/*
+ * bignum_copy ()
+ *
+ * Copy a bignum from in to out.
+ * If the output is shorter than the input, copy lower-order littlenums.
+ * Return 0 or the number of significant littlenums dropped.
+ * Assumes littlenum arrays are densely packed: no unused chars between
+ * the littlenums. Uses memcpy() to move littlenums, and wants to
+ * know length (in chars) of the input bignum.
+ */
+
+/* void */
+int
+ bignum_copy(in, in_length, out, out_length)
+register LITTLENUM_TYPE *in;
+register int in_length; /* in sizeof(littlenum)s */
+register LITTLENUM_TYPE *out;
+register int out_length; /* in sizeof(littlenum)s */
+{
+ int significant_littlenums_dropped;
+
+ if (out_length < in_length) {
+ LITTLENUM_TYPE *p; /* -> most significant (non-zero) input
+ littlenum. */
+
+ memcpy((void *) out, (void *) in,
+ out_length << LITTLENUM_SHIFT);
+ for (p = in + in_length - 1; p >= in; --p) {
+ if (* p) break;
+ }
+ significant_littlenums_dropped = p - in - in_length + 1;
+
+ if (significant_littlenums_dropped < 0) {
+ significant_littlenums_dropped = 0;
+ }
+ } else {
+ memcpy((char *) out, (char *) in,
+ in_length << LITTLENUM_SHIFT);
+
+ if (out_length > in_length) {
+ memset((char *) (out + out_length),
+ '\0', (out_length - in_length) << LITTLENUM_SHIFT);
+ }
+
+ significant_littlenums_dropped = 0;
+ }
+
+ return(significant_littlenums_dropped);
+} /* bignum_copy() */
+
+/* end of bignum-copy.c */
diff --git a/gnu/usr.bin/as/bignum.h b/gnu/usr.bin/as/bignum.h
new file mode 100644
index 0000000..d95ca9a
--- /dev/null
+++ b/gnu/usr.bin/as/bignum.h
@@ -0,0 +1,64 @@
+/* bignum.h-arbitrary precision integers
+ Copyright (C) 1987, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * $Id: bignum.h,v 1.3 1993/10/02 20:57:19 pk Exp $
+ */
+
+/***********************************************************************\
+ * *
+ * Arbitrary-precision integer arithmetic. *
+ * For speed, we work in groups of bits, even though this *
+ * complicates algorithms. *
+ * Each group of bits is called a 'littlenum'. *
+ * A bunch of littlenums representing a (possibly large) *
+ * integer is called a 'bignum'. *
+ * Bignums are >= 0. *
+ * *
+ \***********************************************************************/
+
+#define LITTLENUM_NUMBER_OF_BITS (16)
+#define LITTLENUM_RADIX (1 << LITTLENUM_NUMBER_OF_BITS)
+#define LITTLENUM_MASK (0xFFFF)
+#define LITTLENUM_SHIFT (1)
+#define CHARS_PER_LITTLENUM (1 << LITTLENUM_SHIFT)
+#ifndef BITS_PER_CHAR
+#define BITS_PER_CHAR (8)
+#endif
+
+typedef unsigned short LITTLENUM_TYPE;
+
+/* JF truncated this to get around a problem with GCC */
+#define LOG_TO_BASE_2_OF_10 (3.3219280948873623478703194294893901758651)
+/* WARNING: I haven't checked that the trailing digits are correct! */
+
+ /* lengths are in sizeof(littlenum)s */
+#if __STDC__ == 1
+
+int bignum_copy(LITTLENUM_TYPE *in, int in_length,
+ LITTLENUM_TYPE *out, int out_length);
+
+#else
+
+int bignum_copy();
+
+#endif /* __STDC__ */
+
+
+/* end of bignum.h */
diff --git a/gnu/usr.bin/as/bit_fix.h b/gnu/usr.bin/as/bit_fix.h
new file mode 100644
index 0000000..e72d6d6
--- /dev/null
+++ b/gnu/usr.bin/as/bit_fix.h
@@ -0,0 +1,54 @@
+/* write.h
+
+ Copyright (C) 1987, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
+ * $Id: bit_fix.h,v 1.1 1993/10/02 20:57:19 pk Exp $
+ */
+
+
+/* The bit_fix was implemented to support machines that need variables
+ to be inserted in bitfields other than 1, 2 and 4 bytes.
+ Furthermore it gives us a possibillity to mask in bits in the symbol
+ when it's fixed in the objectcode and check the symbols limits.
+
+ The or-mask is used to set the huffman bits in displacements for the
+ ns32k port.
+ The acbi, addqi, movqi, cmpqi instruction requires an assembler that
+ can handle bitfields. Ie handle an expression, evaluate it and insert
+ the result in an some bitfield. ( ex: 5 bits in a short field of a opcode)
+ */
+
+#ifndef __bit_fix_h__
+#define __bit_fix_h__
+
+struct bit_fix {
+ int fx_bit_size; /* Length of bitfield */
+ int fx_bit_offset; /* Bit offset to bitfield */
+ long fx_bit_base; /* Where do we apply the bitfix.
+ If this is zero, default is assumed. */
+ long fx_bit_base_adj; /* Adjustment of base */
+ long fx_bit_max; /* Signextended max for bitfield */
+ long fx_bit_min; /* Signextended min for bitfield */
+ long fx_bit_add; /* Or mask, used for huffman prefix */
+};
+typedef struct bit_fix bit_fixS;
+
+#endif /* __bit_fix_h__ */
+
+ /* end of bit_fix.h */
diff --git a/gnu/usr.bin/as/cond.c b/gnu/usr.bin/as/cond.c
new file mode 100644
index 0000000..ad98201
--- /dev/null
+++ b/gnu/usr.bin/as/cond.c
@@ -0,0 +1,239 @@
+/* cond.c - conditional assembly pseudo-ops, and .include
+ Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef lint
+static char rcsid[] = "$Id: cond.c,v 1.1 1993/10/02 20:57:20 pk Exp $";
+#endif
+
+#include "as.h"
+
+#include "obstack.h"
+
+/* This is allocated to grow and shrink as .ifdef/.endif pairs are scanned. */
+struct obstack cond_obstack;
+
+struct file_line {
+ char *logical_file;
+ int logical_line;
+ char *physical_file;
+ int physical_line;
+}; /* file_line */
+
+/* This is what we push and pop. */
+struct conditional_frame {
+ struct file_line if_file_line; /* the source file & line number of the "if" */
+ struct file_line else_file_line; /* the source file & line of the "else" */
+ struct conditional_frame *previous_cframe;
+ int else_seen; /* have we seen an else yet? */
+ int ignoring; /* if we are currently ignoring input. */
+ int dead_tree; /* if a conditional at a higher level is ignoring input. */
+}; /* conditional_frame */
+
+#if __STDC__ == 1
+
+static void get_file_line(struct file_line *into);
+static void initialize_cframe(struct conditional_frame *cframe);
+static void set_file_line(struct file_line *from);
+
+#else
+
+static void get_file_line();
+static void initialize_cframe();
+static void set_file_line();
+
+#endif
+
+static struct conditional_frame *current_cframe = NULL;
+
+void s_ifdef(arg)
+int arg;
+{
+ register char *name; /* points to name of symbol */
+ register struct symbol *symbolP; /* Points to symbol */
+ struct conditional_frame cframe;
+
+ SKIP_WHITESPACE(); /* Leading whitespace is part of operand. */
+ name = input_line_pointer;
+
+ if (!is_name_beginner(*name)) {
+ as_bad("invalid identifier for \".ifdef\"");
+ obstack_1grow(&cond_obstack, 0);
+ } else {
+ get_symbol_end();
+ ++input_line_pointer;
+ symbolP = symbol_find(name);
+
+ initialize_cframe(&cframe);
+ cframe.ignoring = cframe.dead_tree && !((symbolP != 0) ^ arg);
+ current_cframe = (struct conditional_frame *) obstack_copy(&cond_obstack, &cframe, sizeof(cframe));
+ } /* if a valid identifyer name */
+
+ return;
+} /* s_ifdef() */
+
+void s_if(arg)
+int arg;
+{
+ expressionS operand;
+ struct conditional_frame cframe;
+
+ SKIP_WHITESPACE(); /* Leading whitespace is part of operand. */
+ expr(0, &operand);
+
+ if (operand.X_add_symbol != NULL
+ || operand.X_subtract_symbol != NULL) {
+ as_bad("non-constant expression in \".if\" statement");
+ } /* bad condition */
+
+ /* If the above error is signaled, this will dispatch
+ using an undefined result. No big deal. */
+ initialize_cframe(&cframe);
+ cframe.ignoring = cframe.dead_tree || !((operand.X_add_number != 0) ^ arg);
+ current_cframe = (struct conditional_frame *) obstack_copy(&cond_obstack, &cframe, sizeof(cframe));
+ return;
+} /* s_if() */
+
+void s_endif(arg)
+int arg;
+{
+ struct conditional_frame *hold;
+
+ if (current_cframe == NULL) {
+ as_bad("\".endif\" without \".if\"");
+ } else {
+ hold = current_cframe;
+ current_cframe = current_cframe->previous_cframe;
+ obstack_free(&cond_obstack, hold);
+ } /* if one pop too many */
+
+ return;
+} /* s_endif() */
+
+void s_else(arg)
+int arg;
+{
+ if (current_cframe == NULL) {
+ as_bad(".else without matching .if - ignored");
+
+ } else if (current_cframe->else_seen) {
+ struct file_line hold;
+ as_bad("duplicate \"else\" - ignored");
+
+ get_file_line(&hold);
+ set_file_line(&current_cframe->else_file_line);
+ as_bad("here is the previous \"else\".");
+ set_file_line(&current_cframe->if_file_line);
+ as_bad("here is the matching \".if\".");
+ set_file_line(&hold);
+
+ } else {
+ get_file_line(&current_cframe->else_file_line);
+
+ if (!current_cframe->dead_tree) {
+ current_cframe->ignoring = !current_cframe->ignoring;
+ } /* if not a dead tree */
+
+ current_cframe->else_seen = 1;
+ } /* if error else do it */
+
+ return;
+} /* s_else() */
+
+void s_ifeqs(arg)
+int arg;
+{
+ as_bad("ifeqs not implemented.");
+
+ return;
+} /* s_ifeqs() */
+
+void s_end(arg)
+int arg;
+{
+ return;
+} /* s_end() */
+
+int ignore_input() {
+ char *ptr = obstack_next_free (&cond_obstack);
+
+ /* We cannot ignore certain pseudo ops. */
+ if (input_line_pointer[-1] == '.'
+ && ((input_line_pointer[0] == 'i'
+ && (!strncmp (input_line_pointer, "if", 2)
+ || !strncmp (input_line_pointer, "ifdef", 5)
+ || !strncmp (input_line_pointer, "ifndef", 6)))
+ || (input_line_pointer[0] == 'e'
+ && (!strncmp (input_line_pointer, "else", 4)
+ || !strncmp (input_line_pointer, "endif", 5))))) {
+ return 0;
+ }
+
+ return((current_cframe != NULL) && (current_cframe->ignoring));
+} /* ignore_input() */
+
+static void initialize_cframe(cframe)
+struct conditional_frame *cframe;
+{
+ memset(cframe, 0, sizeof(*cframe));
+ get_file_line(&(cframe->if_file_line));
+ cframe->previous_cframe = current_cframe;
+ cframe->dead_tree = current_cframe != NULL && current_cframe->ignoring;
+
+ return;
+} /* initialize_cframe() */
+
+static void get_file_line(into)
+struct file_line *into;
+{
+ extern char *logical_input_file;
+ extern char *physical_input_file;
+ extern int logical_input_line;
+ extern int physical_input_line;
+
+ into->logical_file = logical_input_file;
+ into->logical_line = logical_input_line;
+ into->physical_file = physical_input_file;
+ into->physical_line = physical_input_line;
+
+ return;
+} /* get_file_line() */
+
+static void set_file_line(from)
+struct file_line *from;
+{
+ extern char *logical_input_file;
+ extern char *physical_input_file;
+ extern int logical_input_line;
+ extern int physical_input_line;
+
+ logical_input_file = from->logical_file;
+ logical_input_line = from->logical_line;
+ physical_input_file = from->physical_file;
+ physical_input_line = from->physical_line;
+ return;
+} /* set_file_line() */
+
+/*
+ * Local Variables:
+ * fill-column: 131
+ * comment-column: 0
+ * End:
+ */
+
+/* end of cond.c */
diff --git a/gnu/usr.bin/as/config-gas.com b/gnu/usr.bin/as/config-gas.com
new file mode 100644
index 0000000..48e2e49
--- /dev/null
+++ b/gnu/usr.bin/as/config-gas.com
@@ -0,0 +1,76 @@
+$!
+$! This file sets things up to build gas on a VMS system to generate object
+$! files for a VMS system. We do not use the configure script, since we
+$! do not have /bin/sh to execute it.
+$!
+$! If you are running this file, then obviously the host is vax-dec-vms.
+$!
+$gas_host="vms"
+$!
+$cpu_type="vax"
+$emulation="generic"
+$obj_format="vms"
+$atof="vax"
+$!
+$! host specific information
+$call link host.h [.config]ho-'gas_host'.h
+$!
+$! Target specific information
+$call link targ-cpu.c [.config]tc-'cpu_type'.c
+$call link targ-cpu.h [.config]tc-'cpu_type'.h
+$call link targ-env.h [.config]te-'emulation'.h
+$!
+$! Code to handle the object file format.
+$call link obj-format.h [.config]obj-'obj_format'.h
+$call link obj-format.c [.config]obj-'obj_format'.c
+$!
+$! Code to handle floating point.
+$call link atof-targ.c [.config]atof-'atof'.c
+$!
+$!
+$! Create the file version.opt, which helps identify the executalbe.
+$!
+$search version.c version_string,"="/match=and/output=t.tmp
+$open ifile$ t.tmp
+$read ifile$ line
+$close ifile$
+$delete/nolog t.tmp;
+$ijk=f$locate("""",line)+1
+$line=f$extract(ijk,f$length(line)-ijk,line)
+$ijk=f$locate("""",line)
+$line=f$extract(0,ijk,line)
+$ijk=f$locate("\n",line)
+$line=f$extract(0,ijk,line)
+$!
+$i=0
+$loop:
+$elm=f$element(i," ",line)
+$if elm.eqs."" then goto no_ident
+$if (elm.les."9").and.(elm.ges."0") then goto write_ident
+$i=i+1
+$goto loop
+$!
+$no_ident:
+$elm="?.??"
+$!
+$!
+$write_ident:
+$open ifile$ version.opt/write
+$write ifile$ "ident="+""""+elm+""""
+$close ifile$
+$!
+$ !
+$ if f$search("config.status") .nes. "" then delete config.status.*
+$ open/write file config.status
+$ write file "Links are now set up for use with a vax running VMS."
+$ close file
+$ type config.status
+$exit
+$!
+$!
+$link:
+$subroutine
+$if f$search(p1).nes."" then delete/nolog 'p1';
+$copy 'p2' 'p1'
+$write sys$output "Linked ''p2' to ''p1'."
+$endsubroutine
diff --git a/gnu/usr.bin/as/config/Makefile.hp300 b/gnu/usr.bin/as/config/Makefile.hp300
new file mode 100644
index 0000000..4261d35
--- /dev/null
+++ b/gnu/usr.bin/as/config/Makefile.hp300
@@ -0,0 +1,7 @@
+# from: @(#)Makefile.hp300 6.1 (Berkeley) 3/3/91
+# $Id: Makefile.hp300,v 1.4 1993/10/16 03:23:04 cgd Exp $
+
+CFLAGS+= -Dm68851
+SRCS+= tc-m68k.c atof-ieee.c
+
+gas_target= m68k
diff --git a/gnu/usr.bin/as/config/Makefile.i386 b/gnu/usr.bin/as/config/Makefile.i386
new file mode 100644
index 0000000..bbae017
--- /dev/null
+++ b/gnu/usr.bin/as/config/Makefile.i386
@@ -0,0 +1,5 @@
+# from: @(#)Makefile.i386 6.1 (Berkeley) 3/3/91
+# $Id: Makefile.i386,v 1.3 1993/10/02 20:58:21 pk Exp $
+
+CFLAGS+= -DNON_BROKEN_WORDS
+SRCS+= tc-i386.c atof-ieee.c
diff --git a/gnu/usr.bin/as/config/Makefile.pc532 b/gnu/usr.bin/as/config/Makefile.pc532
new file mode 100644
index 0000000..d4b22ab
--- /dev/null
+++ b/gnu/usr.bin/as/config/Makefile.pc532
@@ -0,0 +1,7 @@
+# $Id: Makefile.pc532,v 1.1 1993/10/16 03:23:37 cgd Exp $
+
+SRCS+= tc-ns32k.c atof-ns32k.c
+
+CFLAGS+= -DNS32532 -DNS32381
+
+gas_target= ns32k
diff --git a/gnu/usr.bin/as/config/Makefile.sparc b/gnu/usr.bin/as/config/Makefile.sparc
new file mode 100644
index 0000000..c9d9af1
--- /dev/null
+++ b/gnu/usr.bin/as/config/Makefile.sparc
@@ -0,0 +1,5 @@
+# from: @(#)Makefile.i386 6.1 (Berkeley) 3/3/91
+# $Id: Makefile.sparc,v 1.1 1993/10/02 20:58:22 pk Exp $
+
+CFLAGS+= -DNON_BROKEN_WORDS
+SRCS+= tc-sparc.c atof-ieee.c
diff --git a/gnu/usr.bin/as/config/Makefile.vax b/gnu/usr.bin/as/config/Makefile.vax
new file mode 100644
index 0000000..f62b087
--- /dev/null
+++ b/gnu/usr.bin/as/config/Makefile.vax
@@ -0,0 +1,4 @@
+# from: @(#)Makefile.vax 6.1 (Berkeley) 3/3/91
+# $Id: Makefile.vax,v 1.3 1993/10/02 20:58:23 pk Exp $
+
+SRCS+= tc-vax.c atof-vax.c
diff --git a/gnu/usr.bin/as/config/a.out.gnu.h b/gnu/usr.bin/as/config/a.out.gnu.h
new file mode 100644
index 0000000..71b09a0
--- /dev/null
+++ b/gnu/usr.bin/as/config/a.out.gnu.h
@@ -0,0 +1,261 @@
+#ifndef __A_OUT_GNU_H__
+#define __A_OUT_GNU_H__
+
+#define __GNU_EXEC_MACROS__
+
+#ifndef __STRUCT_EXEC_OVERRIDE__
+
+struct exec
+{
+ unsigned long a_info; /* Use macros N_MAGIC, etc for access */
+ unsigned a_text; /* length of text, in bytes */
+ unsigned a_data; /* length of data, in bytes */
+ unsigned a_bss; /* length of uninitialized data area for file, in bytes */
+ unsigned a_syms; /* length of symbol table data in file, in bytes */
+ unsigned a_entry; /* start address */
+ unsigned a_trsize; /* length of relocation info for text, in bytes */
+ unsigned a_drsize; /* length of relocation info for data, in bytes */
+};
+
+#endif /* __STRUCT_EXEC_OVERRIDE__ */
+
+/* these go in the N_MACHTYPE field */
+enum machine_type {
+#if defined (M_OLDSUN2)
+ M__OLDSUN2 = M_OLDSUN2,
+#else
+ M_OLDSUN2 = 0,
+#endif
+#if defined (M_68010)
+ M__68010 = M_68010,
+#else
+ M_68010 = 1,
+#endif
+#if defined (M_68020)
+ M__68020 = M_68020,
+#else
+ M_68020 = 2,
+#endif
+#if defined (M_SPARC)
+ M__SPARC = M_SPARC,
+#else
+ M_SPARC = 3,
+#endif
+ /* skip a bunch so we don't run into any of sun's numbers */
+ M_386 = 100,
+};
+
+#if !defined (N_MAGIC)
+#define N_MAGIC(exec) ((exec).a_info & 0xffff)
+#endif
+#define N_MACHTYPE(exec) ((enum machine_type)(((exec).a_info >> 16) & 0xff))
+#define N_FLAGS(exec) (((exec).a_info >> 24) & 0xff)
+#define N_SET_INFO(exec, magic, type, flags) \
+ ((exec).a_info = ((magic) & 0xffff) \
+ | (((int)(type) & 0xff) << 16) \
+ | (((flags) & 0xff) << 24))
+#define N_SET_MAGIC(exec, magic) \
+ ((exec).a_info = (((exec).a_info & 0xffff0000) | ((magic) & 0xffff)))
+
+#define N_SET_MACHTYPE(exec, machtype) \
+ ((exec).a_info = \
+ ((exec).a_info&0xff00ffff) | ((((int)(machtype))&0xff) << 16))
+
+#define N_SET_FLAGS(exec, flags) \
+ ((exec).a_info = \
+ ((exec).a_info&0x00ffffff) | (((flags) & 0xff) << 24))
+
+/* Code indicating object file or impure executable. */
+#define OMAGIC 0407
+/* Code indicating pure executable. */
+#define NMAGIC 0410
+/* Code indicating demand-paged executable. */
+#define ZMAGIC 0413
+
+#if !defined (N_BADMAG)
+#define N_BADMAG(x) \
+ (N_MAGIC(x) != OMAGIC && N_MAGIC(x) != NMAGIC \
+ && N_MAGIC(x) != ZMAGIC)
+#endif
+
+#define _N_BADMAG(x) \
+ (N_MAGIC(x) != OMAGIC && N_MAGIC(x) != NMAGIC \
+ && N_MAGIC(x) != ZMAGIC)
+
+#define _N_HDROFF(x) (1024 - sizeof (struct exec))
+
+#if !defined (N_TXTOFF)
+#define N_TXTOFF(x) \
+ (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : sizeof (struct exec))
+#endif
+
+#if !defined (N_DATOFF)
+#define N_DATOFF(x) (N_TXTOFF(x) + (x).a_text)
+#endif
+
+#if !defined (N_TRELOFF)
+#define N_TRELOFF(x) (N_DATOFF(x) + (x).a_data)
+#endif
+
+#if !defined (N_DRELOFF)
+#define N_DRELOFF(x) (N_TRELOFF(x) + (x).a_trsize)
+#endif
+
+#if !defined (N_SYMOFF)
+#define N_SYMOFF(x) (N_DRELOFF(x) + (x).a_drsize)
+#endif
+
+#if !defined (N_STROFF)
+#define N_STROFF(x) (N_SYMOFF(x) + (x).a_syms)
+#endif
+
+/* Address of text segment in memory after it is loaded. */
+#if !defined (N_TXTADDR)
+#define N_TXTADDR(x) 0
+#endif
+
+/* Address of data segment in memory after it is loaded.
+ Note that it is up to you to define SEGMENT_SIZE
+ on machines not listed here. */
+#if defined(vax) || defined(hp300) || defined(pyr)
+#define SEGMENT_SIZE page_size
+#endif
+#ifdef sony
+#define SEGMENT_SIZE 0x2000
+#endif /* Sony. */
+#ifdef is68k
+#define SEGMENT_SIZE 0x20000
+#endif
+#if defined(m68k) && defined(PORTAR)
+#define PAGE_SIZE 0x400
+#define SEGMENT_SIZE PAGE_SIZE
+#endif
+
+#define _N_SEGMENT_ROUND(x) (((x) + SEGMENT_SIZE - 1) & ~(SEGMENT_SIZE - 1))
+
+#define _N_TXTENDADDR(x) (N_TXTADDR(x)+(x).a_text)
+
+#ifndef N_DATADDR
+#define N_DATADDR(x) \
+ (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x)) \
+ : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x))))
+#endif
+
+/* Address of bss segment in memory after it is loaded. */
+#if !defined (N_BSSADDR)
+#define N_BSSADDR(x) (N_DATADDR(x) + (x).a_data)
+#endif
+
+#if !defined (N_NLIST_DECLARED)
+struct nlist {
+ union {
+ char *n_name;
+ struct nlist *n_next;
+ long n_strx;
+ } n_un;
+ unsigned char n_type;
+ char n_other;
+ short n_desc;
+ unsigned long n_value;
+};
+#endif /* no N_NLIST_DECLARED. */
+
+#if !defined (N_UNDF)
+#define N_UNDF 0
+#endif
+#if !defined (N_ABS)
+#define N_ABS 2
+#endif
+#if !defined (N_TEXT)
+#define N_TEXT 4
+#endif
+#if !defined (N_DATA)
+#define N_DATA 6
+#endif
+#if !defined (N_BSS)
+#define N_BSS 8
+#endif
+#if !defined (N_FN)
+#define N_FN 15
+#endif
+
+#if !defined (N_EXT)
+#define N_EXT 1
+#endif
+#if !defined (N_TYPE)
+#define N_TYPE 036
+#endif
+#if !defined (N_STAB)
+#define N_STAB 0340
+#endif
+
+/* The following type indicates the definition of a symbol as being
+ an indirect reference to another symbol. The other symbol
+ appears as an undefined reference, immediately following this symbol.
+
+ Indirection is asymmetrical. The other symbol's value will be used
+ to satisfy requests for the indirect symbol, but not vice versa.
+ If the other symbol does not have a definition, libraries will
+ be searched to find a definition. */
+#define N_INDR 0xa
+
+/* The following symbols refer to set elements.
+ All the N_SET[ATDB] symbols with the same name form one set.
+ Space is allocated for the set in the text section, and each set
+ element's value is stored into one word of the space.
+ The first word of the space is the length of the set (number of elements).
+
+ The address of the set is made into an N_SETV symbol
+ whose name is the same as the name of the set.
+ This symbol acts like a N_DATA global symbol
+ in that it can satisfy undefined external references. */
+
+/* These appear as input to LD, in a .o file. */
+#define N_SETA 0x14 /* Absolute set element symbol */
+#define N_SETT 0x16 /* Text set element symbol */
+#define N_SETD 0x18 /* Data set element symbol */
+#define N_SETB 0x1A /* Bss set element symbol */
+
+/* This is output from LD. */
+#define N_SETV 0x1C /* Pointer to set vector in data area. */
+
+#if !defined (N_RELOCATION_INFO_DECLARED)
+/* This structure describes a single relocation to be performed.
+ The text-relocation section of the file is a vector of these structures,
+ all of which apply to the text section.
+ Likewise, the data-relocation section applies to the data section. */
+
+struct relocation_info
+{
+ /* Address (within segment) to be relocated. */
+ int r_address;
+ /* The meaning of r_symbolnum depends on r_extern. */
+ unsigned int r_symbolnum:24;
+ /* Nonzero means value is a pc-relative offset
+ and it should be relocated for changes in its own address
+ as well as for changes in the symbol or section specified. */
+ unsigned int r_pcrel:1;
+ /* Length (as exponent of 2) of the field to be relocated.
+ Thus, a value of 2 indicates 1<<2 bytes. */
+ unsigned int r_length:2;
+ /* 1 => relocate with value of symbol.
+ r_symbolnum is the index of the symbol
+ in file's the symbol table.
+ 0 => relocate with the address of a segment.
+ r_symbolnum is N_TEXT, N_DATA, N_BSS or N_ABS
+ (the N_EXT bit may be set also, but signifies nothing). */
+ unsigned int r_extern:1;
+ /* Four bits that aren't used, but when writing an object file
+ it is desirable to clear them. */
+#ifdef NS32K
+ unsigned r_bsr:1;
+ unsigned r_disp:1;
+ unsigned r_pad:2;
+#else
+ unsigned int r_pad:4;
+#endif
+};
+#endif /* no N_RELOCATION_INFO_DECLARED. */
+
+
+#endif /* __A_OUT_GNU_H__ */
diff --git a/gnu/usr.bin/as/config/aout.h b/gnu/usr.bin/as/config/aout.h
new file mode 100644
index 0000000..23b085a
--- /dev/null
+++ b/gnu/usr.bin/as/config/aout.h
@@ -0,0 +1,430 @@
+/* This file is aout.h
+
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __A_OUT_GNU_H__
+#define __A_OUT_GNU_H__
+
+enum reloc_type {
+
+#ifdef TC_M88K
+ RELOC_LO16, /* lo16(sym) */
+ RELOC_HI16, /* hi16(sym) */
+ RELOC_PC16, /* bb0, bb1, bcnd */
+ RELOC_PC26, /* br, bsr */
+ RELOC_32, /* jump tables, etc */
+ RELOC_IW16, /* global access through linker regs 28 */
+ NO_RELOC,
+#else /* not TC_M88K */
+#ifdef TC_I860
+
+/* NOTE: three bits max, see struct reloc_info_i860.r_type */
+ NO_RELOC = 0, BRADDR, LOW0, LOW1, LOW2, LOW3, LOW4, SPLIT0, SPLIT1, SPLIT2, RELOC_32,
+
+#else /* not TC_I860 */
+
+ RELOC_8, RELOC_16, RELOC_32, /* simple relocations */
+ RELOC_DISP8, RELOC_DISP16, RELOC_DISP32, /* pc-rel displacement */
+ RELOC_WDISP30, RELOC_WDISP22,
+ RELOC_HI22, RELOC_22,
+ RELOC_13, RELOC_LO10,
+ RELOC_SFA_BASE, RELOC_SFA_OFF13,
+ RELOC_BASE10, RELOC_BASE13, RELOC_BASE22, /* P.I.C. (base-relative) */
+ RELOC_PC10, RELOC_PC22, /* for some sort of pc-rel P.I.C. (?) */
+ RELOC_JMP_TBL, /* P.I.C. jump table */
+ RELOC_SEGOFF16, /* reputedly for shared libraries somehow */
+ RELOC_GLOB_DAT, RELOC_JMP_SLOT, RELOC_RELATIVE,
+#ifndef TC_SPARC
+ RELOC_11,
+ RELOC_WDISP2_14,
+ RELOC_WDISP19,
+ RELOC_HHI22,
+ RELOC_HLO10,
+
+ /* 29K relocation types */
+ RELOC_JUMPTARG, RELOC_CONST, RELOC_CONSTH,
+
+ RELOC_WDISP14, RELOC_WDISP21,
+#endif /* not TC_SPARC */
+ NO_RELOC,
+
+#ifdef TC_I386
+ /* Used internally by gas */
+ RELOC_GOT,
+ RELOC_GOTOFF,
+#endif
+
+#endif /* not TC_I860 */
+#endif /* not TC_M88K */
+};
+
+
+#ifdef TC_I860
+ /* NOTE: two bits max, see reloc_info_i860.r_type */
+enum highlow_type {
+ NO_SPEC = 0, PAIR, HIGH, HIGHADJ,
+};
+#endif /* TC_I860 */
+
+
+#define __GNU_EXEC_MACROS__
+
+#ifndef __STRUCT_EXEC_OVERRIDE__
+
+/* This is the layout on disk of a Unix V7, Berkeley, SunOS, Vax Ultrix
+ "struct exec". Don't assume that on this machine, the "struct exec"
+ will lay out the same sizes or alignments. */
+
+struct exec_bytes {
+ unsigned char a_info[4];
+ unsigned char a_text[4];
+ unsigned char a_data[4];
+ unsigned char a_bss[4];
+ unsigned char a_syms[4];
+ unsigned char a_entry[4];
+ unsigned char a_trsize[4];
+ unsigned char a_drsize[4];
+};
+
+/* How big the "struct exec" is on disk */
+#define EXEC_BYTES_SIZE (8 * 4)
+
+/* This is the layout in memory of a "struct exec" while we process it. */
+
+struct exec
+{
+ unsigned long a_info; /* Use macros N_MAGIC, etc for access */
+ unsigned a_text; /* length of text, in bytes */
+ unsigned a_data; /* length of data, in bytes */
+ unsigned a_bss; /* length of uninitialized data area for file, in bytes */
+ unsigned a_syms; /* length of symbol table data in file, in bytes */
+ unsigned a_entry; /* start address */
+ unsigned a_trsize; /* length of relocation info for text, in bytes */
+ unsigned a_drsize; /* length of relocation info for data, in bytes */
+};
+
+#endif /* __STRUCT_EXEC_OVERRIDE__ */
+
+/* these go in the N_MACHTYPE field */
+/* These symbols could be defined by code from Suns...punt 'em */
+#undef M_UNKNOWN
+#undef M_68010
+#undef M_68020
+#undef M_SPARC
+enum machine_type {
+ M_UNKNOWN = 0,
+ M_68010 = 1,
+ M_68020 = 2,
+ M_SPARC = 3,
+ /* skip a bunch so we don't run into any of sun's numbers */
+ M_386 = 100,
+ M_29K = 101,
+ M_RS6000 = 102, /* IBM RS/6000 */
+ /* HP/BSD formats */
+ M_HP200 = 200, /* hp200 (68010) BSD binary */
+ M_HP300 = 300, /* hp300 (68020+68881) BSD binary */
+ M_HPUX23 = 0x020C, /* hp200/300 HPUX binary */
+};
+
+#define N_MAGIC(exec) ((exec).a_info & 0xffff)
+#define N_MACHTYPE(exec) ((enum machine_type)(((exec).a_info >> 16) & 0xff))
+#define N_FLAGS(exec) (((exec).a_info >> 24) & 0xff)
+#define N_SET_INFO(exec, magic, type, flags) \
+ ((exec).a_info = ((magic) & 0xffff) \
+ | (((int)(type) & 0xff) << 16) \
+ | (((flags) & 0xff) << 24))
+#define N_SET_MAGIC(exec, magic) \
+ ((exec).a_info = (((exec).a_info & 0xffff0000) | ((magic) & 0xffff)))
+
+#define N_SET_MACHTYPE(exec, machtype) \
+ ((exec).a_info = \
+ ((exec).a_info&0xff00ffff) | ((((int)(machtype))&0xff) << 16))
+
+#define N_SET_FLAGS(exec, flags) \
+ ((exec).a_info = \
+ ((exec).a_info&0x00ffffff) | (((flags) & 0xff) << 24))
+
+/* Code indicating object file or impure executable. */
+#define OMAGIC 0407
+/* Code indicating pure executable. */
+#define NMAGIC 0410
+/* Code indicating demand-paged executable. */
+#define ZMAGIC 0413
+
+/* Virtual Address of text segment from the a.out file. For OMAGIC,
+ (almost always "unlinked .o's" these days), should be zero.
+ For linked files, should reflect reality if we know it. */
+
+#ifndef N_TXTADDR
+#define N_TXTADDR(x) (N_MAGIC(x) == OMAGIC? 0 : TEXT_START_ADDR)
+#endif
+
+#ifndef N_BADMAG
+#define N_BADMAG(x) (N_MAGIC(x) != OMAGIC \
+ && N_MAGIC(x) != NMAGIC \
+ && N_MAGIC(x) != ZMAGIC)
+#endif
+
+/* By default, segment size is constant. But on some machines, it can
+ be a function of the a.out header (e.g. machine type). */
+#ifndef N_SEGSIZE
+#define N_SEGSIZE(x) SEGMENT_SIZE
+#endif
+
+ /* This complexity is for encapsulated COFF support */
+#ifndef _N_HDROFF
+#define _N_HDROFF(x) (N_SEGSIZE(x) - sizeof (struct exec))
+#endif
+
+#ifndef N_TXTOFF
+#define N_TXTOFF(x) (N_MAGIC(x) == ZMAGIC ? \
+ _N_HDROFF((x)) + sizeof (struct exec) : \
+ sizeof (struct exec))
+#endif
+
+
+#ifndef N_DATOFF
+#define N_DATOFF(x) ( N_TXTOFF(x) + (x).a_text )
+#endif
+
+#ifndef N_TRELOFF
+#define N_TRELOFF(x) ( N_DATOFF(x) + (x).a_data )
+#endif
+
+#ifndef N_DRELOFF
+#define N_DRELOFF(x) ( N_TRELOFF(x) + (x).a_trsize )
+#endif
+
+#ifndef N_SYMOFF
+#define N_SYMOFF(x) ( N_DRELOFF(x) + (x).a_drsize )
+#endif
+
+#ifndef N_STROFF
+#define N_STROFF(x) ( N_SYMOFF(x) + (x).a_syms )
+#endif
+
+/* Address of text segment in memory after it is loaded. */
+#ifndef N_TXTADDR
+#define N_TXTADDR(x) 0
+#endif
+
+#ifndef N_DATADDR
+#define N_DATADDR(x) \
+ (N_MAGIC(x) == OMAGIC? (N_TXTADDR(x)+(x).a_text) \
+ : (N_SEGSIZE(x) + ((N_TXTADDR(x)+(x).a_text-1) & ~(N_SEGSIZE(x)-1))))
+#endif
+
+/* Address of bss segment in memory after it is loaded. */
+#define N_BSSADDR(x) (N_DATADDR(x) + (x).a_data)
+
+struct nlist {
+ union {
+ char *n_name;
+ struct nlist *n_next;
+ long n_strx;
+ } n_un;
+ unsigned char n_type;
+ char n_other;
+ short n_desc;
+ unsigned long n_value;
+};
+
+#define N_UNDF 0
+#define N_ABS 2
+#define N_TEXT 4
+#define N_DATA 6
+#define N_BSS 8
+#define N_COMM 0x12 /* common (visible in shared lib commons) */
+#define N_FN 0x1F /* File name of a .o file */
+
+/* Note: N_EXT can only usefully be OR-ed with N_UNDF, N_ABS, N_TEXT,
+ N_DATA, or N_BSS. When the low-order bit of other types is set,
+ (e.g. N_WARNING versus N_FN), they are two different types. */
+#define N_EXT 1
+#define N_TYPE 036
+#define N_STAB 0340
+
+/* The following type indicates the definition of a symbol as being
+ an indirect reference to another symbol. The other symbol
+ appears as an undefined reference, immediately following this symbol.
+
+ Indirection is asymmetrical. The other symbol's value will be used
+ to satisfy requests for the indirect symbol, but not vice versa.
+ If the other symbol does not have a definition, libraries will
+ be searched to find a definition. */
+
+#define N_INDR 0xa
+
+/* The following type indicates the size of the symbol it refers to */
+#define N_SIZE 0xc
+
+/* The following symbols refer to set elements.
+ All the N_SET[ATDB] symbols with the same name form one set.
+ Space is allocated for the set in the text section, and each set
+ element's value is stored into one word of the space.
+ The first word of the space is the length of the set (number of elements).
+
+ The address of the set is made into an N_SETV symbol
+ whose name is the same as the name of the set.
+ This symbol acts like a N_DATA global symbol
+ in that it can satisfy undefined external references. */
+
+/* These appear as input to LD, in a .o file. */
+#define N_SETA 0x14 /* Absolute set element symbol */
+#define N_SETT 0x16 /* Text set element symbol */
+#define N_SETD 0x18 /* Data set element symbol */
+#define N_SETB 0x1A /* Bss set element symbol */
+
+/* This is output from LD. */
+#define N_SETV 0x1C /* Pointer to set vector in data area. */
+
+/* Warning symbol. The text gives a warning message, the next symbol
+ in the table will be undefined. When the symbol is referenced, the
+ message is printed. */
+
+#define N_WARNING 0x1e
+
+/* This structure describes a single relocation to be performed.
+ The text-relocation section of the file is a vector of these structures,
+ all of which apply to the text section.
+ Likewise, the data-relocation section applies to the data section. */
+
+/* The following enum and struct were borrowed from SunOS's
+ /usr/include/sun4/a.out.h and extended to handle
+ other machines. It is currently used on SPARC and AMD 29000.
+
+ reloc_ext_bytes is how it looks on disk. reloc_info_extended is
+ how we might process it on a native host. */
+
+struct reloc_ext_bytes {
+ unsigned char r_address[4];
+ unsigned char r_index[3];
+ unsigned char r_bits[1];
+ unsigned char r_addend[4];
+};
+
+struct reloc_info_i860
+{
+ unsigned long r_address;
+ /*
+ * Using bit fields here is a bad idea because the order is not portable. :-(
+ */
+ unsigned int r_symbolnum: 24;
+ unsigned int r_pcrel : 1;
+ unsigned int r_extern : 1;
+ /* combining the two field simplifies the argument passing in "new_fix()" */
+ /* and is compatible with the existing Sparc #ifdef's */
+ /* r_type: highlow_type - bits 5,4; reloc_type - bits 3-0 */
+ unsigned int r_type : 6;
+ long r_addend;
+};
+
+
+#define RELOC_EXT_BITS_EXTERN_BIG 0x80
+#define RELOC_EXT_BITS_EXTERN_LITTLE 0x01
+
+#define RELOC_EXT_BITS_TYPE_BIG 0x1F
+#define RELOC_EXT_BITS_TYPE_SH_BIG 0
+#define RELOC_EXT_BITS_TYPE_LITTLE 0xF8
+#define RELOC_EXT_BITS_TYPE_SH_LITTLE 3
+
+#define RELOC_EXT_SIZE 12 /* Bytes per relocation entry */
+
+struct reloc_info_extended
+{
+ unsigned long r_address;
+ unsigned int r_index:24;
+# define r_symbolnum r_index
+ unsigned r_extern:1;
+ unsigned :2;
+ /* RS/6000 compiler does not support enum bitfield
+ enum reloc_type r_type:5; */
+ enum reloc_type r_type;
+ long int r_addend;
+};
+
+/* The standard, old-fashioned, Berkeley compatible relocation struct */
+
+struct reloc_std_bytes {
+ unsigned char r_address[4];
+ unsigned char r_index[3];
+ unsigned char r_bits[1];
+};
+
+#define RELOC_STD_BITS_PCREL_BIG 0x80
+#define RELOC_STD_BITS_PCREL_LITTLE 0x01
+
+#define RELOC_STD_BITS_LENGTH_BIG 0x60
+#define RELOC_STD_BITS_LENGTH_SH_BIG 5 /* To shift to units place */
+#define RELOC_STD_BITS_LENGTH_LITTLE 0x06
+#define RELOC_STD_BITS_LENGTH_SH_LITTLE 1
+
+#define RELOC_STD_BITS_EXTERN_BIG 0x10
+#define RELOC_STD_BITS_EXTERN_LITTLE 0x08
+
+#define RELOC_STD_BITS_BASEREL_BIG 0x08
+#define RELOC_STD_BITS_BASEREL_LITTLE 0x08
+
+#define RELOC_STD_BITS_JMPTABLE_BIG 0x04
+#define RELOC_STD_BITS_JMPTABLE_LITTLE 0x04
+
+#define RELOC_STD_BITS_RELATIVE_BIG 0x02
+#define RELOC_STD_BITS_RELATIVE_LITTLE 0x02
+
+#define RELOC_STD_SIZE 8 /* Bytes per relocation entry */
+
+#ifndef CUSTOM_RELOC_FORMAT
+struct relocation_info {
+ /* Address (within segment) to be relocated. */
+ int r_address;
+ /* The meaning of r_symbolnum depends on r_extern. */
+ unsigned int r_symbolnum:24;
+ /* Nonzero means value is a pc-relative offset
+ and it should be relocated for changes in its own address
+ as well as for changes in the symbol or section specified. */
+ unsigned int r_pcrel:1;
+ /* Length (as exponent of 2) of the field to be relocated.
+ Thus, a value of 2 indicates 1<<2 bytes. */
+ unsigned int r_length:2;
+ /* 1 => relocate with value of symbol.
+ r_symbolnum is the index of the symbol
+ in file's the symbol table.
+ 0 => relocate with the address of a segment.
+ r_symbolnum is N_TEXT, N_DATA, N_BSS or N_ABS
+ (the N_EXT bit may be set also, but signifies nothing). */
+ unsigned int r_extern:1;
+ /* The next three bits are for SunOS shared libraries, and seem to
+ be undocumented. */
+ unsigned int r_baserel:1; /* Linkage table relative */
+ unsigned int r_jmptable:1; /* pc-relative to jump table */
+
+#ifdef TC_NS32K
+#define r_bsr r_baserel
+#define r_disp r_jmptable
+#endif /* TC_NS32K */
+
+ unsigned int r_relative:1; /* "relative relocation" */
+ /* unused */
+ unsigned int r_pad:1; /* Padding -- set to zero */
+};
+#endif /* CUSTOM_RELOC_FORMAT */
+
+#endif /* __A_OUT_GNU_H__ */
+
+/* end of aout.h */
diff --git a/gnu/usr.bin/as/config/atof-ieee.c b/gnu/usr.bin/as/config/atof-ieee.c
new file mode 100644
index 0000000..18941cc
--- /dev/null
+++ b/gnu/usr.bin/as/config/atof-ieee.c
@@ -0,0 +1,524 @@
+/* atof_ieee.c - turn a Flonum into an IEEE floating point number
+ Copyright (C) 1987, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef lint
+static char rcsid[] = "$Id: atof-ieee.c,v 1.3 1993/10/02 20:58:25 pk Exp $";
+#endif
+
+#include "as.h"
+
+extern FLONUM_TYPE generic_floating_point_number; /* Flonums returned here. */
+
+#ifndef NULL
+#define NULL (0)
+#endif
+
+extern char EXP_CHARS[];
+/* Precision in LittleNums. */
+#define MAX_PRECISION (6)
+#define F_PRECISION (2)
+#define D_PRECISION (4)
+#define X_PRECISION (6)
+#define P_PRECISION (6)
+
+/* Length in LittleNums of guard bits. */
+#define GUARD (2)
+
+static unsigned long mask[] = {
+ 0x00000000,
+ 0x00000001,
+ 0x00000003,
+ 0x00000007,
+ 0x0000000f,
+ 0x0000001f,
+ 0x0000003f,
+ 0x0000007f,
+ 0x000000ff,
+ 0x000001ff,
+ 0x000003ff,
+ 0x000007ff,
+ 0x00000fff,
+ 0x00001fff,
+ 0x00003fff,
+ 0x00007fff,
+ 0x0000ffff,
+ 0x0001ffff,
+ 0x0003ffff,
+ 0x0007ffff,
+ 0x000fffff,
+ 0x001fffff,
+ 0x003fffff,
+ 0x007fffff,
+ 0x00ffffff,
+ 0x01ffffff,
+ 0x03ffffff,
+ 0x07ffffff,
+ 0x0fffffff,
+ 0x1fffffff,
+ 0x3fffffff,
+ 0x7fffffff,
+ 0xffffffff,
+};
+
+
+static int bits_left_in_littlenum;
+static int littlenums_left;
+static LITTLENUM_TYPE *littlenum_pointer;
+
+static int
+ next_bits (number_of_bits)
+int number_of_bits;
+{
+ int return_value;
+
+ if (!littlenums_left)
+ return(0);
+ if (number_of_bits >= bits_left_in_littlenum) {
+ return_value = mask[bits_left_in_littlenum] & *littlenum_pointer;
+ number_of_bits -= bits_left_in_littlenum;
+ return_value <<= number_of_bits;
+
+ if (--littlenums_left) {
+ bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS - number_of_bits;
+ --littlenum_pointer;
+ return_value |= (*littlenum_pointer >> bits_left_in_littlenum) & mask[number_of_bits];
+ }
+ } else {
+ bits_left_in_littlenum -= number_of_bits;
+ return_value = mask[number_of_bits] & (*littlenum_pointer >> bits_left_in_littlenum);
+ }
+ return(return_value);
+}
+
+/* Num had better be less than LITTLENUM_NUMBER_OF_BITS */
+static void
+ unget_bits(num)
+int num;
+{
+ if (!littlenums_left) {
+ ++littlenum_pointer;
+ ++littlenums_left;
+ bits_left_in_littlenum = num;
+ } else if (bits_left_in_littlenum + num > LITTLENUM_NUMBER_OF_BITS) {
+ bits_left_in_littlenum = num - (LITTLENUM_NUMBER_OF_BITS - bits_left_in_littlenum);
+ ++littlenum_pointer;
+ ++littlenums_left;
+ } else
+ bits_left_in_littlenum += num;
+}
+
+static void
+ make_invalid_floating_point_number(words)
+LITTLENUM_TYPE *words;
+{
+ as_bad("cannot create floating-point number");
+ words[0] = ((unsigned) -1) >> 1; /* Zero the leftmost bit */
+ words[1] = -1;
+ words[2] = -1;
+ words[3] = -1;
+ words[4] = -1;
+ words[5] = -1;
+}
+
+/***********************************************************************\
+ * Warning: this returns 16-bit LITTLENUMs. It is up to the caller *
+ * to figure out any alignment problems and to conspire for the *
+ * bytes/word to be emitted in the right order. Bigendians beware! *
+ * *
+ \***********************************************************************/
+
+/* Note that atof-ieee always has X and P precisions enabled. it is up
+ to md_atof to filter them out if the target machine does not support
+ them. */
+
+char * /* Return pointer past text consumed. */
+ atof_ieee(str, what_kind, words)
+char *str; /* Text to convert to binary. */
+char what_kind; /* 'd', 'f', 'g', 'h' */
+LITTLENUM_TYPE *words; /* Build the binary here. */
+{
+ static LITTLENUM_TYPE bits[MAX_PRECISION + MAX_PRECISION + GUARD];
+ /* Extra bits for zeroed low-order bits. */
+ /* The 1st MAX_PRECISION are zeroed, */
+ /* the last contain flonum bits. */
+ char *return_value;
+ int precision; /* Number of 16-bit words in the format. */
+ long exponent_bits;
+ FLONUM_TYPE save_gen_flonum;
+
+ /* We have to save the generic_floating_point_number because it
+ contains storage allocation about the array of LITTLENUMs
+ where the value is actually stored. We will allocate our
+ own array of littlenums below, but have to restore the global
+ one on exit. */
+ save_gen_flonum = generic_floating_point_number;
+
+ return_value = str;
+ generic_floating_point_number.low = bits + MAX_PRECISION;
+ generic_floating_point_number.high = NULL;
+ generic_floating_point_number.leader = NULL;
+ generic_floating_point_number.exponent = NULL;
+ generic_floating_point_number.sign = '\0';
+
+ /* Use more LittleNums than seems */
+ /* necessary: the highest flonum may have */
+ /* 15 leading 0 bits, so could be useless. */
+
+ memset(bits, '\0', sizeof(LITTLENUM_TYPE) * MAX_PRECISION);
+
+ switch (what_kind) {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ precision = F_PRECISION;
+ exponent_bits = 8;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ precision = D_PRECISION;
+ exponent_bits = 11;
+ break;
+
+ case 'x':
+ case 'X':
+ case 'e':
+ case 'E':
+ precision = X_PRECISION;
+ exponent_bits = 15;
+ break;
+
+ case 'p':
+ case 'P':
+
+ precision = P_PRECISION;
+ exponent_bits = -1;
+ break;
+
+ default:
+ make_invalid_floating_point_number(words);
+ return(NULL);
+ }
+
+ generic_floating_point_number.high = generic_floating_point_number.low + precision - 1 + GUARD;
+
+ if (atof_generic(&return_value, ".", EXP_CHARS, &generic_floating_point_number)) {
+ /* as_bad("Error converting floating point number (Exponent overflow?)"); */
+ make_invalid_floating_point_number(words);
+ return(NULL);
+ }
+ gen_to_words(words, precision, exponent_bits);
+
+ /* Restore the generic_floating_point_number's storage alloc
+ (and everything else). */
+ generic_floating_point_number = save_gen_flonum;
+
+ return(return_value);
+}
+
+/* Turn generic_floating_point_number into a real float/double/extended */
+int gen_to_words(words, precision, exponent_bits)
+LITTLENUM_TYPE *words;
+int precision;
+long exponent_bits;
+{
+ int return_value = 0;
+
+ long exponent_1;
+ long exponent_2;
+ long exponent_3;
+ long exponent_4;
+ int exponent_skippage;
+ LITTLENUM_TYPE word1;
+ LITTLENUM_TYPE *lp;
+
+ if (generic_floating_point_number.low > generic_floating_point_number.leader) {
+ /* 0.0e0 seen. */
+ if (generic_floating_point_number.sign == '+')
+ words[0] = 0x0000;
+ else
+ words[0] = 0x8000;
+ memset(&words[1], '\0', sizeof(LITTLENUM_TYPE) * (precision - 1));
+ return(return_value);
+ }
+
+ /* NaN: Do the right thing */
+ if (generic_floating_point_number.sign == 0) {
+ if (precision == F_PRECISION) {
+ words[0] = 0x7fff;
+ words[1] = 0xffff;
+ } else {
+ words[0] = 0x7fff;
+ words[1] = 0xffff;
+ words[2] = 0xffff;
+ words[3] = 0xffff;
+ }
+ return return_value;
+ } else if (generic_floating_point_number.sign == 'P') {
+ /* +INF: Do the right thing */
+ if (precision == F_PRECISION) {
+ words[0] = 0x7f80;
+ words[1] = 0;
+ } else {
+ words[0] = 0x7ff0;
+ words[1] = 0;
+ words[2] = 0;
+ words[3] = 0;
+ }
+ return(return_value);
+ } else if (generic_floating_point_number.sign == 'N') {
+ /* Negative INF */
+ if (precision == F_PRECISION) {
+ words[0] = 0xff80;
+ words[1] = 0x0;
+ } else {
+ words[0] = 0xfff0;
+ words[1] = 0x0;
+ words[2] = 0x0;
+ words[3] = 0x0;
+ }
+ return(return_value);
+ }
+ /*
+ * The floating point formats we support have:
+ * Bit 15 is sign bit.
+ * Bits 14:n are excess-whatever exponent.
+ * Bits n-1:0 (if any) are most significant bits of fraction.
+ * Bits 15:0 of the next word(s) are the next most significant bits.
+ *
+ * So we need: number of bits of exponent, number of bits of
+ * mantissa.
+ */
+ bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS;
+ littlenum_pointer = generic_floating_point_number.leader;
+ littlenums_left = 1 + generic_floating_point_number.leader - generic_floating_point_number.low;
+ /* Seek (and forget) 1st significant bit */
+ for (exponent_skippage = 0; !next_bits(1); ++exponent_skippage) ;;
+ exponent_1 = generic_floating_point_number.exponent + generic_floating_point_number.leader
+ + 1 - generic_floating_point_number.low;
+ /* Radix LITTLENUM_RADIX, point just higher than generic_floating_point_number.leader. */
+ exponent_2 = exponent_1 * LITTLENUM_NUMBER_OF_BITS;
+ /* Radix 2. */
+ exponent_3 = exponent_2 - exponent_skippage;
+ /* Forget leading zeros, forget 1st bit. */
+ exponent_4 = exponent_3 + ((1 << (exponent_bits - 1)) - 2);
+ /* Offset exponent. */
+
+ lp = words;
+
+ /* Word 1. Sign, exponent and perhaps high bits. */
+ word1 = (generic_floating_point_number.sign == '+') ? 0 : (1 << (LITTLENUM_NUMBER_OF_BITS - 1));
+
+ /* Assume 2's complement integers. */
+ if (exponent_4 < 1 && exponent_4 >= -62) {
+ int prec_bits;
+ int num_bits;
+
+ unget_bits(1);
+ num_bits = -exponent_4;
+ prec_bits = LITTLENUM_NUMBER_OF_BITS * precision - (exponent_bits + 1 + num_bits);
+ if (precision == X_PRECISION && exponent_bits == 15)
+ prec_bits -= LITTLENUM_NUMBER_OF_BITS + 1;
+
+ if (num_bits >= LITTLENUM_NUMBER_OF_BITS - exponent_bits) {
+ /* Bigger than one littlenum */
+ num_bits -= (LITTLENUM_NUMBER_OF_BITS - 1) - exponent_bits;
+ *lp++ = word1;
+ if (num_bits + exponent_bits + 1 >= precision * LITTLENUM_NUMBER_OF_BITS) {
+ /* Exponent overflow */
+ make_invalid_floating_point_number(words);
+ return(return_value);
+ }
+ if (precision == X_PRECISION && exponent_bits == 15) {
+ *lp++ = 0;
+ *lp++ = 0;
+ num_bits -= LITTLENUM_NUMBER_OF_BITS - 1;
+ }
+ while (num_bits >= LITTLENUM_NUMBER_OF_BITS) {
+ num_bits -= LITTLENUM_NUMBER_OF_BITS;
+ *lp++ = 0;
+ }
+ if (num_bits)
+ *lp++ = next_bits(LITTLENUM_NUMBER_OF_BITS - (num_bits));
+ } else {
+ if (precision == X_PRECISION && exponent_bits == 15) {
+ *lp++ = word1;
+ *lp++ = 0;
+ if (num_bits == LITTLENUM_NUMBER_OF_BITS) {
+ *lp++ = 0;
+ *lp++ = next_bits(LITTLENUM_NUMBER_OF_BITS - 1);
+ } else if (num_bits == LITTLENUM_NUMBER_OF_BITS - 1)
+ *lp++ = 0;
+ else
+ *lp++ = next_bits(LITTLENUM_NUMBER_OF_BITS - 1 - num_bits);
+ num_bits = 0;
+ } else {
+ word1 |= next_bits((LITTLENUM_NUMBER_OF_BITS - 1) - (exponent_bits + num_bits));
+ *lp++ = word1;
+ }
+ }
+ while (lp < words + precision)
+ *lp++ = next_bits(LITTLENUM_NUMBER_OF_BITS);
+
+ /* Round the mantissa up, but don't change the number */
+ if (next_bits(1)) {
+ --lp;
+ if (prec_bits > LITTLENUM_NUMBER_OF_BITS) {
+ int n = 0;
+ int tmp_bits;
+
+ n = 0;
+ tmp_bits = prec_bits;
+ while (tmp_bits > LITTLENUM_NUMBER_OF_BITS) {
+ if (lp[n] != (LITTLENUM_TYPE) - 1)
+ break;
+ --n;
+ tmp_bits -= LITTLENUM_NUMBER_OF_BITS;
+ }
+ if (tmp_bits > LITTLENUM_NUMBER_OF_BITS || (lp[n] & mask[tmp_bits]) != mask[tmp_bits]) {
+ unsigned long carry;
+
+ for (carry = 1; carry && (lp >= words); lp --) {
+ carry = *lp + carry;
+ *lp = carry;
+ carry >>= LITTLENUM_NUMBER_OF_BITS;
+ }
+ }
+ } else if ((*lp & mask[prec_bits]) != mask[prec_bits])
+ lp++;
+ }
+
+ return return_value;
+ } else if (exponent_4 & ~ mask[exponent_bits]) {
+ /*
+ * Exponent overflow. Lose immediately.
+ */
+
+ /*
+ * We leave return_value alone: admit we read the
+ * number, but return a floating exception
+ * because we can't encode the number.
+ */
+ make_invalid_floating_point_number (words);
+ return return_value;
+ } else {
+ word1 |= (exponent_4 << ((LITTLENUM_NUMBER_OF_BITS - 1) - exponent_bits))
+ | next_bits ((LITTLENUM_NUMBER_OF_BITS - 1) - exponent_bits);
+ }
+
+ *lp++ = word1;
+
+ /* X_PRECISION is special: it has 16 bits of zero in the middle,
+ followed by a 1 bit. */
+ if (exponent_bits == 15 && precision == X_PRECISION) {
+ *lp++ = 0;
+ *lp++ = 1 << (LITTLENUM_NUMBER_OF_BITS) | next_bits(LITTLENUM_NUMBER_OF_BITS - 1);
+ }
+
+ /* The rest of the words are just mantissa bits. */
+ while (lp < words + precision)
+ *lp++ = next_bits(LITTLENUM_NUMBER_OF_BITS);
+
+ if (next_bits(1)) {
+ unsigned long carry;
+ /*
+ * Since the NEXT bit is a 1, round UP the mantissa.
+ * The cunning design of these hidden-1 floats permits
+ * us to let the mantissa overflow into the exponent, and
+ * it 'does the right thing'. However, we lose if the
+ * highest-order bit of the lowest-order word flips.
+ * Is that clear?
+ */
+
+ /* #if (sizeof(carry)) < ((sizeof(bits[0]) * BITS_PER_CHAR) + 2)
+ Please allow at least 1 more bit in carry than is in a LITTLENUM.
+ We need that extra bit to hold a carry during a LITTLENUM carry
+ propagation. Another extra bit (kept 0) will assure us that we
+ don't get a sticky sign bit after shifting right, and that
+ permits us to propagate the carry without any masking of bits.
+ #endif */
+ for (carry = 1, lp--; carry && (lp >= words); lp--) {
+ carry = *lp + carry;
+ *lp = carry;
+ carry >>= LITTLENUM_NUMBER_OF_BITS;
+ }
+ if ((word1 ^ *words) & (1 << (LITTLENUM_NUMBER_OF_BITS - 1))) {
+ /* We leave return_value alone: admit we read the
+ * number, but return a floating exception
+ * because we can't encode the number.
+ */
+ *words &= ~(1 << (LITTLENUM_NUMBER_OF_BITS - 1));
+ /* make_invalid_floating_point_number (words); */
+ /* return return_value; */
+ }
+ }
+ return (return_value);
+}
+
+/* This routine is a real kludge. Someone really should do it better, but
+ I'm too lazy, and I don't understand this stuff all too well anyway
+ (JF)
+ */
+void
+ int_to_gen(x)
+long x;
+{
+ char buf[20];
+ char *bufp;
+
+ sprintf(buf,"%ld",x);
+ bufp = &buf[0];
+ if (atof_generic(&bufp, ".", EXP_CHARS, &generic_floating_point_number))
+ as_bad("Error converting number to floating point (Exponent overflow?)");
+}
+
+#ifdef TEST
+char *
+ print_gen(gen)
+FLONUM_TYPE *gen;
+{
+ FLONUM_TYPE f;
+ LITTLENUM_TYPE arr[10];
+ double dv;
+ float fv;
+ static char sbuf[40];
+
+ if (gen) {
+ f = generic_floating_point_number;
+ generic_floating_point_number = *gen;
+ }
+ gen_to_words(&arr[0], 4, 11);
+ memcpy(&dv, &arr[0], sizeof(double));
+ sprintf(sbuf, "%x %x %x %x %.14G ", arr[0], arr[1], arr[2], arr[3], dv);
+ gen_to_words(&arr[0], 2, 8);
+ memcpy(&fv, &arr[0], sizeof(float));
+ sprintf(sbuf + strlen(sbuf), "%x %x %.12g\n", arr[0], arr[1], fv);
+
+ if (gen) {
+ generic_floating_point_number = f;
+ }
+
+ return(sbuf);
+}
+#endif
+
+/* end of atof-ieee.c */
diff --git a/gnu/usr.bin/as/config/atof-ns32k.c b/gnu/usr.bin/as/config/atof-ns32k.c
new file mode 100644
index 0000000..cadeec0
--- /dev/null
+++ b/gnu/usr.bin/as/config/atof-ns32k.c
@@ -0,0 +1,436 @@
+/* atof_ns32k.c - turn a Flonum into a ns32k floating point number
+ Copyright (C) 1987 Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* this is atof-m68k.c hacked for ns32k */
+
+#include "as.h"
+
+extern FLONUM_TYPE generic_floating_point_number; /* Flonums returned here. */
+
+extern char EXP_CHARS[];
+ /* Precision in LittleNums. */
+#define MAX_PRECISION (4)
+#define F_PRECISION (2)
+#define D_PRECISION (4)
+
+ /* Length in LittleNums of guard bits. */
+#define GUARD (2)
+
+int /* Number of chars in flonum type 'letter'. */
+atof_sizeof (letter)
+ char letter;
+{
+ int return_value;
+
+ /*
+ * Permitting uppercase letters is probably a bad idea.
+ * Please use only lower-cased letters in case the upper-cased
+ * ones become unsupported!
+ */
+ switch (letter)
+ {
+ case 'f':
+ return_value = F_PRECISION;
+ break;
+
+ case 'd':
+ return_value = D_PRECISION;
+ break;
+
+ default:
+ return_value = 0;
+ break;
+ }
+ return (return_value);
+}
+
+static unsigned long int mask[] = {
+ 0x00000000,
+ 0x00000001,
+ 0x00000003,
+ 0x00000007,
+ 0x0000000f,
+ 0x0000001f,
+ 0x0000003f,
+ 0x0000007f,
+ 0x000000ff,
+ 0x000001ff,
+ 0x000003ff,
+ 0x000007ff,
+ 0x00000fff,
+ 0x00001fff,
+ 0x00003fff,
+ 0x00007fff,
+ 0x0000ffff,
+ 0x0001ffff,
+ 0x0003ffff,
+ 0x0007ffff,
+ 0x000fffff,
+ 0x001fffff,
+ 0x003fffff,
+ 0x007fffff,
+ 0x00ffffff,
+ 0x01ffffff,
+ 0x03ffffff,
+ 0x07ffffff,
+ 0x0fffffff,
+ 0x1fffffff,
+ 0x3fffffff,
+ 0x7fffffff,
+ 0xffffffff
+ };
+
+static int bits_left_in_littlenum;
+static int littlenums_left;
+static LITTLENUM_TYPE * littlenum_pointer;
+
+static int
+next_bits (number_of_bits)
+ int number_of_bits;
+{
+ int return_value;
+
+ if (!littlenums_left)
+ return 0;
+ if (number_of_bits >= bits_left_in_littlenum)
+ {
+ return_value = mask[bits_left_in_littlenum] & *littlenum_pointer;
+ number_of_bits -= bits_left_in_littlenum;
+ return_value <<= number_of_bits;
+ if (littlenums_left) {
+ bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS - number_of_bits;
+ littlenum_pointer --;
+ --littlenums_left;
+ return_value |= (*littlenum_pointer>>bits_left_in_littlenum) & mask[number_of_bits];
+ }
+ }
+ else
+ {
+ bits_left_in_littlenum -= number_of_bits;
+ return_value = mask[number_of_bits] & (*littlenum_pointer>>bits_left_in_littlenum);
+ }
+ return (return_value);
+}
+
+static void
+make_invalid_floating_point_number (words)
+ LITTLENUM_TYPE * words;
+{
+ words[0]= ((unsigned)-1)>>1; /* Zero the leftmost bit */
+ words[1]= -1;
+ words[2]= -1;
+ words[3]= -1;
+}
+
+/***********************************************************************\
+* *
+* Warning: this returns 16-bit LITTLENUMs, because that is *
+* what the VAX thinks in. It is up to the caller to figure *
+* out any alignment problems and to conspire for the bytes/word *
+* to be emitted in the right order. Bigendians beware! *
+* *
+\***********************************************************************/
+
+char * /* Return pointer past text consumed. */
+atof_ns32k (str, what_kind, words)
+ char * str; /* Text to convert to binary. */
+ char what_kind; /* 'd', 'f', 'g', 'h' */
+ LITTLENUM_TYPE * words; /* Build the binary here. */
+{
+ FLONUM_TYPE f;
+ LITTLENUM_TYPE bits[MAX_PRECISION + MAX_PRECISION + GUARD];
+ /* Extra bits for zeroed low-order bits. */
+ /* The 1st MAX_PRECISION are zeroed, */
+ /* the last contain flonum bits. */
+ char * return_value;
+ int precision; /* Number of 16-bit words in the format. */
+ long int exponent_bits;
+
+ long int exponent_1;
+ long int exponent_2;
+ long int exponent_3;
+ long int exponent_4;
+ int exponent_skippage;
+ LITTLENUM_TYPE word1;
+ LITTLENUM_TYPE * lp;
+
+ return_value = str;
+ f.low = bits + MAX_PRECISION;
+ f.high = NULL;
+ f.leader = NULL;
+ f.exponent = NULL;
+ f.sign = '\0';
+
+ /* Use more LittleNums than seems */
+ /* necessary: the highest flonum may have */
+ /* 15 leading 0 bits, so could be useless. */
+
+ bzero (bits, sizeof(LITTLENUM_TYPE) * MAX_PRECISION);
+
+ switch (what_kind) {
+ case 'f':
+ precision = F_PRECISION;
+ exponent_bits = 8;
+ break;
+
+ case 'd':
+ precision = D_PRECISION;
+ exponent_bits = 11;
+ break;
+
+ default:
+ make_invalid_floating_point_number (words);
+ return NULL;
+ }
+
+ f.high = f.low + precision - 1 + GUARD;
+
+ if (atof_generic (& return_value, ".", EXP_CHARS, & f)) {
+ as_warn("Error converting floating point number (Exponent overflow?)");
+ make_invalid_floating_point_number (words);
+ return NULL;
+ }
+
+ if (f.low > f.leader) {
+ /* 0.0e0 seen. */
+ bzero (words, sizeof(LITTLENUM_TYPE) * precision);
+ return return_value;
+ }
+
+ if (f.sign != '+' && f.sign != '-') {
+ make_invalid_floating_point_number(words);
+ return NULL;
+ }
+
+
+ /*
+ * All vaxen floating_point formats (so far) have:
+ * Bit 15 is sign bit.
+ * Bits 14:n are excess-whatever exponent.
+ * Bits n-1:0 (if any) are most significant bits of fraction.
+ * Bits 15:0 of the next word are the next most significant bits.
+ * And so on for each other word.
+ *
+ * So we need: number of bits of exponent, number of bits of
+ * mantissa.
+ */
+ bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS;
+ littlenum_pointer = f.leader;
+ littlenums_left = 1 + f.leader-f.low;
+ /* Seek (and forget) 1st significant bit */
+ for (exponent_skippage = 0;! next_bits(1); exponent_skippage ++)
+ ;
+ exponent_1 = f.exponent + f.leader + 1 - f.low;
+ /* Radix LITTLENUM_RADIX, point just higher than f.leader. */
+ exponent_2 = exponent_1 * LITTLENUM_NUMBER_OF_BITS;
+ /* Radix 2. */
+ exponent_3 = exponent_2 - exponent_skippage;
+ /* Forget leading zeros, forget 1st bit. */
+ exponent_4 = exponent_3 + ((1 << (exponent_bits - 1)) - 2);
+ /* Offset exponent. */
+
+ if (exponent_4 & ~ mask[exponent_bits]) {
+ /*
+ * Exponent overflow. Lose immediately.
+ */
+
+ /*
+ * We leave return_value alone: admit we read the
+ * number, but return a floating exception
+ * because we can't encode the number.
+ */
+
+ as_warn("Exponent overflow in floating-point number");
+ make_invalid_floating_point_number (words);
+ return return_value;
+ }
+ lp = words;
+
+ /* Word 1. Sign, exponent and perhaps high bits. */
+ /* Assume 2's complement integers. */
+ word1 = ((exponent_4 & mask[exponent_bits]) << (15 - exponent_bits)) |
+ ((f.sign == '+') ? 0 : 0x8000) | next_bits (15 - exponent_bits);
+ * lp ++ = word1;
+
+ /* The rest of the words are just mantissa bits. */
+ for (; lp < words + precision; lp++)
+ * lp = next_bits (LITTLENUM_NUMBER_OF_BITS);
+
+ if (next_bits (1)) {
+ unsigned long int carry;
+ /*
+ * Since the NEXT bit is a 1, round UP the mantissa.
+ * The cunning design of these hidden-1 floats permits
+ * us to let the mantissa overflow into the exponent, and
+ * it 'does the right thing'. However, we lose if the
+ * highest-order bit of the lowest-order word flips.
+ * Is that clear?
+ */
+
+
+/* #if (sizeof(carry)) < ((sizeof(bits[0]) * BITS_PER_CHAR) + 2)
+ Please allow at least 1 more bit in carry than is in a LITTLENUM.
+ We need that extra bit to hold a carry during a LITTLENUM carry
+ propagation. Another extra bit (kept 0) will assure us that we
+ don't get a sticky sign bit after shifting right, and that
+ permits us to propagate the carry without any masking of bits.
+#endif */
+ for (carry = 1, lp --; carry && (lp >= words); lp --) {
+ carry = * lp + carry;
+ * lp = carry;
+ carry >>= LITTLENUM_NUMBER_OF_BITS;
+ }
+ if ( (word1 ^ *words) & (1 << (LITTLENUM_NUMBER_OF_BITS - 1)) ) {
+ /* We leave return_value alone: admit we read the
+ * number, but return a floating exception
+ * because we can't encode the number.
+ */
+ make_invalid_floating_point_number (words);
+ return return_value;
+ }
+ }
+ return (return_value);
+}
+
+/* This is really identical to atof_ns32k except for some details */
+
+gen_to_words(words,precision,exponent_bits)
+LITTLENUM_TYPE *words;
+long int exponent_bits;
+{
+ int return_value=0;
+
+ long int exponent_1;
+ long int exponent_2;
+ long int exponent_3;
+ long int exponent_4;
+ int exponent_skippage;
+ LITTLENUM_TYPE word1;
+ LITTLENUM_TYPE * lp;
+
+ if (generic_floating_point_number.low > generic_floating_point_number.leader) {
+ /* 0.0e0 seen. */
+ bzero (words, sizeof(LITTLENUM_TYPE) * precision);
+ return return_value;
+ }
+
+ /*
+ * All vaxen floating_point formats (so far) have:
+ * Bit 15 is sign bit.
+ * Bits 14:n are excess-whatever exponent.
+ * Bits n-1:0 (if any) are most significant bits of fraction.
+ * Bits 15:0 of the next word are the next most significant bits.
+ * And so on for each other word.
+ *
+ * So we need: number of bits of exponent, number of bits of
+ * mantissa.
+ */
+ bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS;
+ littlenum_pointer = generic_floating_point_number.leader;
+ littlenums_left = 1+generic_floating_point_number.leader - generic_floating_point_number.low;
+ /* Seek (and forget) 1st significant bit */
+ for (exponent_skippage = 0;! next_bits(1); exponent_skippage ++)
+ ;
+ exponent_1 = generic_floating_point_number.exponent + generic_floating_point_number.leader + 1 -
+ generic_floating_point_number.low;
+ /* Radix LITTLENUM_RADIX, point just higher than generic_floating_point_number.leader. */
+ exponent_2 = exponent_1 * LITTLENUM_NUMBER_OF_BITS;
+ /* Radix 2. */
+ exponent_3 = exponent_2 - exponent_skippage;
+ /* Forget leading zeros, forget 1st bit. */
+ exponent_4 = exponent_3 + ((1 << (exponent_bits - 1)) - 2);
+ /* Offset exponent. */
+
+ if (exponent_4 & ~ mask[exponent_bits]) {
+ /*
+ * Exponent overflow. Lose immediately.
+ */
+
+ /*
+ * We leave return_value alone: admit we read the
+ * number, but return a floating exception
+ * because we can't encode the number.
+ */
+
+ make_invalid_floating_point_number (words);
+ return return_value;
+ }
+ lp = words;
+
+ /* Word 1. Sign, exponent and perhaps high bits. */
+ /* Assume 2's complement integers. */
+ word1 = ((exponent_4 & mask[exponent_bits]) << (15 - exponent_bits)) |
+ ((generic_floating_point_number.sign == '+') ? 0 : 0x8000) | next_bits (15 - exponent_bits);
+ * lp ++ = word1;
+
+ /* The rest of the words are just mantissa bits. */
+ for (; lp < words + precision; lp++)
+ * lp = next_bits (LITTLENUM_NUMBER_OF_BITS);
+
+ if (next_bits (1)) {
+ unsigned long int carry;
+ /*
+ * Since the NEXT bit is a 1, round UP the mantissa.
+ * The cunning design of these hidden-1 floats permits
+ * us to let the mantissa overflow into the exponent, and
+ * it 'does the right thing'. However, we lose if the
+ * highest-order bit of the lowest-order word flips.
+ * Is that clear?
+ */
+
+
+/* #if (sizeof(carry)) < ((sizeof(bits[0]) * BITS_PER_CHAR) + 2)
+ Please allow at least 1 more bit in carry than is in a LITTLENUM.
+ We need that extra bit to hold a carry during a LITTLENUM carry
+ propagation. Another extra bit (kept 0) will assure us that we
+ don't get a sticky sign bit after shifting right, and that
+ permits us to propagate the carry without any masking of bits.
+#endif */
+ for (carry = 1, lp --; carry && (lp >= words); lp --) {
+ carry = * lp + carry;
+ * lp = carry;
+ carry >>= LITTLENUM_NUMBER_OF_BITS;
+ }
+ if ( (word1 ^ *words) & (1 << (LITTLENUM_NUMBER_OF_BITS - 1)) ) {
+ /* We leave return_value alone: admit we read the
+ * number, but return a floating exception
+ * because we can't encode the number.
+ */
+ make_invalid_floating_point_number (words);
+ return return_value;
+ }
+ }
+ return (return_value);
+}
+
+/* This routine is a real kludge. Someone really should do it better, but
+ I'm too lazy, and I don't understand this stuff all too well anyway
+ (JF)
+ */
+void int_to_gen(x)
+long x;
+{
+ char buf[20];
+ char *bufp;
+
+ sprintf(buf,"%ld",x);
+ bufp= &buf[0];
+ if (atof_generic(&bufp,".", EXP_CHARS, &generic_floating_point_number))
+ as_warn("Error converting number to floating point (Exponent overflow?)");
+}
diff --git a/gnu/usr.bin/as/config/atof-tahoe.c b/gnu/usr.bin/as/config/atof-tahoe.c
new file mode 100644
index 0000000..6425e93
--- /dev/null
+++ b/gnu/usr.bin/as/config/atof-tahoe.c
@@ -0,0 +1,428 @@
+/* atof_tahoe.c - turn a string into a Tahoe floating point number
+ Copyright (C) 1987 Free Software Foundation, Inc.
+ */
+
+/* This is really a simplified version of atof_vax.c. I glommed it wholesale
+ and then shaved it down. I don't even know how it works. (Don't you find
+ my honesty refreshing? bowen@cs.Buffalo.EDU (Devon E Bowen)
+
+ I don't allow uppercase letters in the precision descrpitors. Ie 'f' and
+ 'd' are allowed but 'F' and 'D' aren't */
+
+#include "as.h"
+
+/* Precision in LittleNums. */
+#define MAX_PRECISION (4)
+#define D_PRECISION (4)
+#define F_PRECISION (2)
+
+/* Precision in chars. */
+#define D_PRECISION_CHARS (8)
+#define F_PRECISION_CHARS (4)
+
+ /* Length in LittleNums of guard bits. */
+#define GUARD (2)
+
+static const long int mask [] = {
+ 0x00000000,
+ 0x00000001,
+ 0x00000003,
+ 0x00000007,
+ 0x0000000f,
+ 0x0000001f,
+ 0x0000003f,
+ 0x0000007f,
+ 0x000000ff,
+ 0x000001ff,
+ 0x000003ff,
+ 0x000007ff,
+ 0x00000fff,
+ 0x00001fff,
+ 0x00003fff,
+ 0x00007fff,
+ 0x0000ffff,
+ 0x0001ffff,
+ 0x0003ffff,
+ 0x0007ffff,
+ 0x000fffff,
+ 0x001fffff,
+ 0x003fffff,
+ 0x007fffff,
+ 0x00ffffff,
+ 0x01ffffff,
+ 0x03ffffff,
+ 0x07ffffff,
+ 0x0fffffff,
+ 0x1fffffff,
+ 0x3fffffff,
+ 0x7fffffff,
+ 0xffffffff
+ };
+
+
+/* Shared between flonum_gen2tahoe and next_bits */
+static int bits_left_in_littlenum;
+static LITTLENUM_TYPE * littlenum_pointer;
+static LITTLENUM_TYPE * littlenum_end;
+
+#if __STDC__ == 1
+
+int flonum_gen2tahoe(int format_letter, FLONUM_TYPE *f, LITTLENUM_TYPE *words);
+
+#else /* not __STDC__ */
+
+int flonum_gen2tahoe();
+
+#endif /* not __STDC__ */
+
+
+static int
+next_bits (number_of_bits)
+ int number_of_bits;
+{
+ int return_value;
+
+ if(littlenum_pointer<littlenum_end)
+ return 0;
+ if (number_of_bits >= bits_left_in_littlenum)
+ {
+ return_value = mask [bits_left_in_littlenum] & * littlenum_pointer;
+ number_of_bits -= bits_left_in_littlenum;
+ return_value <<= number_of_bits;
+ bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS - number_of_bits;
+ littlenum_pointer --;
+ if(littlenum_pointer>=littlenum_end)
+ return_value |= ((*littlenum_pointer) >> (bits_left_in_littlenum)) &
+ mask [number_of_bits];
+ }
+ else
+ {
+ bits_left_in_littlenum -= number_of_bits;
+ return_value = mask [number_of_bits] &
+ ((*littlenum_pointer) >> bits_left_in_littlenum);
+ }
+ return (return_value);
+}
+
+static void
+make_invalid_floating_point_number (words)
+ LITTLENUM_TYPE * words;
+{
+ *words = 0x8000; /* Floating Reserved Operand Code */
+}
+
+static int /* 0 means letter is OK. */
+what_kind_of_float (letter, precisionP, exponent_bitsP)
+ char letter; /* In: lowercase please. What kind of float? */
+ int * precisionP; /* Number of 16-bit words in the float. */
+ long int * exponent_bitsP; /* Number of exponent bits. */
+{
+ int retval; /* 0: OK. */
+
+ retval = 0;
+ switch (letter)
+ {
+ case 'f':
+ * precisionP = F_PRECISION;
+ * exponent_bitsP = 8;
+ break;
+
+ case 'd':
+ * precisionP = D_PRECISION;
+ * exponent_bitsP = 8;
+ break;
+
+ default:
+ retval = 69;
+ break;
+ }
+ return (retval);
+}
+
+/***********************************************************************\
+* *
+* Warning: this returns 16-bit LITTLENUMs, because that is *
+* what the VAX thinks in. It is up to the caller to figure *
+* out any alignment problems and to conspire for the bytes/word *
+* to be emitted in the right order. Bigendians beware! *
+* *
+\***********************************************************************/
+
+char * /* Return pointer past text consumed. */
+atof_tahoe (str, what_kind, words)
+ char * str; /* Text to convert to binary. */
+ char what_kind; /* 'd', 'f', 'g', 'h' */
+ LITTLENUM_TYPE * words; /* Build the binary here. */
+{
+ FLONUM_TYPE f;
+ LITTLENUM_TYPE bits [MAX_PRECISION + MAX_PRECISION + GUARD];
+ /* Extra bits for zeroed low-order bits. */
+ /* The 1st MAX_PRECISION are zeroed, */
+ /* the last contain flonum bits. */
+ char * return_value;
+ int precision; /* Number of 16-bit words in the format. */
+ long int exponent_bits;
+
+ return_value = str;
+ f . low = bits + MAX_PRECISION;
+ f . high = NULL;
+ f . leader = NULL;
+ f . exponent = NULL;
+ f . sign = '\0';
+
+ if (what_kind_of_float (what_kind, & precision, & exponent_bits))
+ {
+ return_value = NULL; /* We lost. */
+ make_invalid_floating_point_number (words);
+ }
+ if (return_value)
+ {
+ memset(bits, '\0', sizeof(LITTLENUM_TYPE) * MAX_PRECISION);
+
+ /* Use more LittleNums than seems */
+ /* necessary: the highest flonum may have */
+ /* 15 leading 0 bits, so could be useless. */
+ f . high = f . low + precision - 1 + GUARD;
+
+ if (atof_generic (& return_value, ".", "eE", & f))
+ {
+ make_invalid_floating_point_number (words);
+ return_value = NULL; /* we lost */
+ }
+ else
+ {
+ if (flonum_gen2tahoe (what_kind, & f, words))
+ {
+ return_value = NULL;
+ }
+ }
+ }
+ return (return_value);
+}
+
+/*
+ * In: a flonum, a Tahoe floating point format.
+ * Out: a Tahoe floating-point bit pattern.
+ */
+
+int /* 0: OK. */
+flonum_gen2tahoe (format_letter, f, words)
+ char format_letter; /* One of 'd' 'f'. */
+ FLONUM_TYPE * f;
+ LITTLENUM_TYPE * words; /* Deliver answer here. */
+{
+ LITTLENUM_TYPE * lp;
+ int precision;
+ long int exponent_bits;
+ int return_value; /* 0 == OK. */
+
+ return_value = what_kind_of_float(format_letter,&precision,&exponent_bits);
+ if (return_value != 0)
+ {
+ make_invalid_floating_point_number (words);
+ }
+ else
+ {
+ if (f -> low > f -> leader)
+ {
+ /* 0.0e0 seen. */
+ memset(words, '\0', sizeof(LITTLENUM_TYPE) * precision);
+ }
+ else
+ {
+ long int exponent_1;
+ long int exponent_2;
+ long int exponent_3;
+ long int exponent_4;
+ int exponent_skippage;
+ LITTLENUM_TYPE word1;
+
+ /* JF: Deal with new Nan, +Inf and -Inf codes */
+ if(f->sign!='-' && f->sign!='+') {
+ make_invalid_floating_point_number(words);
+ return return_value;
+ }
+ /*
+ * All tahoe floating_point formats have:
+ * Bit 15 is sign bit.
+ * Bits 14:n are excess-whatever exponent.
+ * Bits n-1:0 (if any) are most significant bits of fraction.
+ * Bits 15:0 of the next word are the next most significant bits.
+ * And so on for each other word.
+ *
+ * So we need: number of bits of exponent, number of bits of
+ * mantissa.
+ */
+
+ bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS;
+ littlenum_pointer = f -> leader;
+ littlenum_end = f->low;
+ /* Seek (and forget) 1st significant bit */
+ for (exponent_skippage = 0;
+ ! next_bits(1);
+ exponent_skippage ++)
+ {
+ }
+ exponent_1 = f -> exponent + f -> leader + 1 - f -> low;
+ /* Radix LITTLENUM_RADIX, point just higher than f -> leader. */
+ exponent_2 = exponent_1 * LITTLENUM_NUMBER_OF_BITS;
+ /* Radix 2. */
+ exponent_3 = exponent_2 - exponent_skippage;
+ /* Forget leading zeros, forget 1st bit. */
+ exponent_4 = exponent_3 + (1 << (exponent_bits - 1));
+ /* Offset exponent. */
+
+ if (exponent_4 & ~ mask [exponent_bits])
+ {
+ /*
+ * Exponent overflow. Lose immediately.
+ */
+
+ make_invalid_floating_point_number (words);
+
+ /*
+ * We leave return_value alone: admit we read the
+ * number, but return a floating exception
+ * because we can't encode the number.
+ */
+ }
+ else
+ {
+ lp = words;
+
+ /* Word 1. Sign, exponent and perhaps high bits. */
+ /* Assume 2's complement integers. */
+ word1 = ((exponent_4 & mask [exponent_bits]) << (15 - exponent_bits))
+ | ((f -> sign == '+') ? 0 : 0x8000)
+ | next_bits (15 - exponent_bits);
+ * lp ++ = word1;
+
+ /* The rest of the words are just mantissa bits. */
+ for (; lp < words + precision; lp++)
+ {
+ * lp = next_bits (LITTLENUM_NUMBER_OF_BITS);
+ }
+
+ if (next_bits (1))
+ {
+ /*
+ * Since the NEXT bit is a 1, round UP the mantissa.
+ * The cunning design of these hidden-1 floats permits
+ * us to let the mantissa overflow into the exponent, and
+ * it 'does the right thing'. However, we lose if the
+ * highest-order bit of the lowest-order word flips.
+ * Is that clear?
+ */
+
+ unsigned long int carry;
+
+ /*
+ #if (sizeof(carry)) < ((sizeof(bits[0]) * BITS_PER_CHAR) + 2)
+ Please allow at least 1 more bit in carry than is in a LITTLENUM.
+ We need that extra bit to hold a carry during a LITTLENUM carry
+ propagation. Another extra bit (kept 0) will assure us that we
+ don't get a sticky sign bit after shifting right, and that
+ permits us to propagate the carry without any masking of bits.
+ #endif
+ */
+ for (carry = 1, lp --;
+ carry && (lp >= words);
+ lp --)
+ {
+ carry = * lp + carry;
+ * lp = carry;
+ carry >>= LITTLENUM_NUMBER_OF_BITS;
+ }
+
+ if ( (word1 ^ *words) & (1 << (LITTLENUM_NUMBER_OF_BITS - 1)) )
+ {
+ make_invalid_floating_point_number (words);
+ /*
+ * We leave return_value alone: admit we read the
+ * number, but return a floating exception
+ * because we can't encode the number.
+ */
+ }
+ } /* if (we needed to round up) */
+ } /* if (exponent overflow) */
+ } /* if (0.0e0) */
+ } /* if (float_type was OK) */
+ return (return_value);
+}
+
+/*
+ * md_atof()
+ *
+ * In: input_line_pointer -> the 1st character of a floating-point
+ * number.
+ * 1 letter denoting the type of statement that wants a
+ * binary floating point number returned.
+ * Address of where to build floating point literal.
+ * Assumed to be 'big enough'.
+ * Address of where to return size of literal (in chars).
+ *
+ * Out: Input_line_pointer -> of next char after floating number.
+ * Error message, or "".
+ * Floating point literal.
+ * Number of chars we used for the literal.
+ */
+
+char *
+md_atof (what_statement_type, literalP, sizeP)
+ char what_statement_type;
+ char * literalP;
+ int * sizeP;
+{
+ LITTLENUM_TYPE words [MAX_PRECISION];
+ register char kind_of_float;
+ register int number_of_chars;
+ register LITTLENUM_TYPE * littlenum_pointer;
+
+ switch (what_statement_type)
+ {
+ case 'f': /* .ffloat */
+ case 'd': /* .dfloat */
+ kind_of_float = what_statement_type;
+ break;
+
+ default:
+ kind_of_float = 0;
+ break;
+ };
+
+ if (kind_of_float)
+ {
+ register LITTLENUM_TYPE * limit;
+
+ input_line_pointer = atof_tahoe (input_line_pointer,
+ kind_of_float,
+ words);
+ /*
+ * The atof_tahoe() builds up 16-bit numbers.
+ * Since the assembler may not be running on
+ * a different-endian machine, be very careful about
+ * converting words to chars.
+ */
+ number_of_chars = (kind_of_float == 'f' ? F_PRECISION_CHARS :
+ (kind_of_float == 'd' ? D_PRECISION_CHARS : 0));
+ know(number_of_chars<=MAX_PRECISION*sizeof(LITTLENUM_TYPE));
+ limit = words + (number_of_chars / sizeof(LITTLENUM_TYPE));
+ for (littlenum_pointer = words;
+ littlenum_pointer < limit;
+ littlenum_pointer ++)
+ {
+ md_number_to_chars(literalP,*littlenum_pointer,
+ sizeof(LITTLENUM_TYPE));
+ literalP += sizeof(LITTLENUM_TYPE);
+ };
+ }
+ else
+ {
+ number_of_chars = 0;
+ };
+
+ * sizeP = number_of_chars;
+ return (kind_of_float ? "" : "Bad call to md_atof()");
+} /* md_atof() */
+
+/* atof_tahoe.c */
diff --git a/gnu/usr.bin/as/config/atof-vax.c b/gnu/usr.bin/as/config/atof-vax.c
new file mode 100644
index 0000000..8a69502
--- /dev/null
+++ b/gnu/usr.bin/as/config/atof-vax.c
@@ -0,0 +1,497 @@
+/* atof_vax.c - turn a Flonum into a VAX floating point number
+ Copyright (C) 1987, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* JF added these two for md_atof() */
+#include "as.h"
+
+/* Precision in LittleNums. */
+#define MAX_PRECISION (8)
+#define H_PRECISION (8)
+#define G_PRECISION (4)
+#define D_PRECISION (4)
+#define F_PRECISION (2)
+
+/* Length in LittleNums of guard bits. */
+#define GUARD (2)
+
+#if __STDC__ == 1
+
+int flonum_gen2vax(int format_letter, FLONUM_TYPE *f, LITTLENUM_TYPE *words);
+
+#else /* not __STDC__ */
+
+int flonum_gen2vax();
+
+#endif /* not __STDC__ */
+
+int /* Number of chars in flonum type 'letter'. */
+ atof_vax_sizeof (letter)
+char letter;
+{
+ int return_value;
+
+ /*
+ * Permitting uppercase letters is probably a bad idea.
+ * Please use only lower-cased letters in case the upper-cased
+ * ones become unsupported!
+ */
+ switch (letter)
+ {
+ case 'f':
+ case 'F':
+ return_value = 4;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'g':
+ case 'G':
+ return_value = 8;
+ break;
+
+ case 'h':
+ case 'H':
+ return_value = 16;
+ break;
+
+ default:
+ return_value = 0;
+ break;
+ }
+ return (return_value);
+} /* atof_vax_sizeof */
+
+static const long mask[] = {
+ 0x00000000,
+ 0x00000001,
+ 0x00000003,
+ 0x00000007,
+ 0x0000000f,
+ 0x0000001f,
+ 0x0000003f,
+ 0x0000007f,
+ 0x000000ff,
+ 0x000001ff,
+ 0x000003ff,
+ 0x000007ff,
+ 0x00000fff,
+ 0x00001fff,
+ 0x00003fff,
+ 0x00007fff,
+ 0x0000ffff,
+ 0x0001ffff,
+ 0x0003ffff,
+ 0x0007ffff,
+ 0x000fffff,
+ 0x001fffff,
+ 0x003fffff,
+ 0x007fffff,
+ 0x00ffffff,
+ 0x01ffffff,
+ 0x03ffffff,
+ 0x07ffffff,
+ 0x0fffffff,
+ 0x1fffffff,
+ 0x3fffffff,
+ 0x7fffffff,
+ 0xffffffff
+ };
+
+
+/* Shared between flonum_gen2vax and next_bits */
+static int bits_left_in_littlenum;
+static LITTLENUM_TYPE * littlenum_pointer;
+static LITTLENUM_TYPE * littlenum_end;
+
+static int
+ next_bits (number_of_bits)
+int number_of_bits;
+{
+ int return_value;
+
+ if (littlenum_pointer<littlenum_end)
+ return 0;
+ if (number_of_bits >= bits_left_in_littlenum)
+ {
+ return_value = mask[bits_left_in_littlenum] & * littlenum_pointer;
+ number_of_bits -= bits_left_in_littlenum;
+ return_value <<= number_of_bits;
+ bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS - number_of_bits;
+ littlenum_pointer --;
+ if (littlenum_pointer >= littlenum_end)
+ return_value |= ( (* littlenum_pointer) >> (bits_left_in_littlenum) ) & mask[number_of_bits];
+ }
+ else
+ {
+ bits_left_in_littlenum -= number_of_bits;
+ return_value = mask[number_of_bits] & ( (* littlenum_pointer) >> bits_left_in_littlenum);
+ }
+ return (return_value);
+}
+
+static void
+ make_invalid_floating_point_number (words)
+LITTLENUM_TYPE * words;
+{
+ * words = 0x8000; /* Floating Reserved Operand Code */
+}
+
+static int /* 0 means letter is OK. */
+ what_kind_of_float (letter, precisionP, exponent_bitsP)
+char letter; /* In: lowercase please. What kind of float? */
+int * precisionP; /* Number of 16-bit words in the float. */
+long * exponent_bitsP; /* Number of exponent bits. */
+{
+ int retval; /* 0: OK. */
+
+ retval = 0;
+ switch (letter)
+ {
+ case 'f':
+ * precisionP = F_PRECISION;
+ * exponent_bitsP = 8;
+ break;
+
+ case 'd':
+ * precisionP = D_PRECISION;
+ * exponent_bitsP = 8;
+ break;
+
+ case 'g':
+ * precisionP = G_PRECISION;
+ * exponent_bitsP = 11;
+ break;
+
+ case 'h':
+ * precisionP = H_PRECISION;
+ * exponent_bitsP = 15;
+ break;
+
+ default:
+ retval = 69;
+ break;
+ }
+ return (retval);
+}
+
+/***********************************************************************\
+ * *
+ * Warning: this returns 16-bit LITTLENUMs, because that is *
+ * what the VAX thinks in. It is up to the caller to figure *
+ * out any alignment problems and to conspire for the bytes/word *
+ * to be emitted in the right order. Bigendians beware! *
+ * *
+ \***********************************************************************/
+
+char * /* Return pointer past text consumed. */
+ atof_vax(str, what_kind, words)
+char *str; /* Text to convert to binary. */
+char what_kind; /* 'd', 'f', 'g', 'h' */
+LITTLENUM_TYPE *words; /* Build the binary here. */
+{
+ FLONUM_TYPE f;
+ LITTLENUM_TYPE bits[MAX_PRECISION + MAX_PRECISION + GUARD];
+ /* Extra bits for zeroed low-order bits. */
+ /* The 1st MAX_PRECISION are zeroed, */
+ /* the last contain flonum bits. */
+ char *return_value;
+ int precision; /* Number of 16-bit words in the format. */
+ long exponent_bits;
+
+ return_value = str;
+ f.low = bits + MAX_PRECISION;
+ f.high = NULL;
+ f.leader = NULL;
+ f.exponent = NULL;
+ f.sign = '\0';
+
+ if (what_kind_of_float (what_kind, & precision, & exponent_bits)) {
+ return_value = NULL; /* We lost. */
+ make_invalid_floating_point_number (words);
+ }
+
+ if (return_value) {
+ memset(bits, '\0', sizeof(LITTLENUM_TYPE) * MAX_PRECISION);
+
+ /* Use more LittleNums than seems */
+ /* necessary: the highest flonum may have */
+ /* 15 leading 0 bits, so could be useless. */
+ f.high = f.low + precision - 1 + GUARD;
+
+ if (atof_generic (& return_value, ".", "eE", & f)) {
+ make_invalid_floating_point_number (words);
+ return_value = NULL; /* we lost */
+ } else {
+ if (flonum_gen2vax(what_kind, & f, words)) {
+ return_value = NULL;
+ }
+ }
+ }
+ return(return_value);
+} /* atof_vax() */
+
+/*
+ * In: a flonum, a vax floating point format.
+ * Out: a vax floating-point bit pattern.
+ */
+
+int /* 0: OK. */
+ flonum_gen2vax (format_letter, f, words)
+char format_letter; /* One of 'd' 'f' 'g' 'h'. */
+FLONUM_TYPE *f;
+LITTLENUM_TYPE *words; /* Deliver answer here. */
+{
+ LITTLENUM_TYPE *lp;
+ int precision;
+ long exponent_bits;
+ int return_value; /* 0 == OK. */
+
+ return_value = what_kind_of_float(format_letter, &precision, &exponent_bits);
+
+ if (return_value != 0) {
+ make_invalid_floating_point_number (words);
+ } else {
+ if (f->low > f->leader) {
+ /* 0.0e0 seen. */
+memset(words, '\0', sizeof(LITTLENUM_TYPE) * precision);
+ } else {
+ long exponent_1;
+ long exponent_2;
+ long exponent_3;
+ long exponent_4;
+ int exponent_skippage;
+ LITTLENUM_TYPE word1;
+
+ /* JF: Deal with new Nan, +Inf and -Inf codes */
+ if (f->sign != '-' && f->sign != '+') {
+ make_invalid_floating_point_number(words);
+ return return_value;
+ }
+ /*
+ * All vaxen floating_point formats (so far) have:
+ * Bit 15 is sign bit.
+ * Bits 14:n are excess-whatever exponent.
+ * Bits n-1:0 (if any) are most significant bits of fraction.
+ * Bits 15:0 of the next word are the next most significant bits.
+ * And so on for each other word.
+ *
+ * All this to be compatible with a KF11?? (Which is still faster
+ * than lots of vaxen I can think of, but it also has higher
+ * maintenance costs ... sigh).
+ *
+ * So we need: number of bits of exponent, number of bits of
+ * mantissa.
+ */
+
+#ifdef NEVER /******* This zeroing seems redundant - Dean 3may86 **********/
+ /*
+ * No matter how few bits we got back from the atof()
+ * routine, add enough zero littlenums so the rest of the
+ * code won't run out of "significant" bits in the mantissa.
+ */
+ {
+ LITTLENUM_TYPE *ltp;
+ for (ltp = f->leader + 1;
+ ltp <= f->low + precision;
+ ltp++) {
+ *ltp = 0;
+ }
+ }
+#endif
+
+ bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS;
+ littlenum_pointer = f->leader;
+ littlenum_end = f->low;
+ /* Seek (and forget) 1st significant bit */
+ for (exponent_skippage = 0;
+ ! next_bits(1);
+ exponent_skippage ++) ;;
+
+ exponent_1 = f->exponent + f->leader + 1 - f->low;
+ /* Radix LITTLENUM_RADIX, point just higher than f->leader. */
+ exponent_2 = exponent_1 * LITTLENUM_NUMBER_OF_BITS;
+ /* Radix 2. */
+ exponent_3 = exponent_2 - exponent_skippage;
+ /* Forget leading zeros, forget 1st bit. */
+ exponent_4 = exponent_3 + (1 << (exponent_bits - 1));
+ /* Offset exponent. */
+
+ if (exponent_4 & ~mask[exponent_bits]) {
+ /*
+ * Exponent overflow. Lose immediately.
+ */
+
+ make_invalid_floating_point_number (words);
+
+ /*
+ * We leave return_value alone: admit we read the
+ * number, but return a floating exception
+ * because we can't encode the number.
+ */
+ } else {
+ lp = words;
+
+ /* Word 1. Sign, exponent and perhaps high bits. */
+ /* Assume 2's complement integers. */
+ word1 = (((exponent_4 &mask[exponent_bits]) << (15 - exponent_bits))
+ | ((f->sign == '+') ? 0 : 0x8000)
+ | next_bits(15 - exponent_bits));
+ *lp++ = word1;
+
+ /* The rest of the words are just mantissa bits. */
+ for (; lp < words + precision; lp++) {
+ *lp = next_bits(LITTLENUM_NUMBER_OF_BITS);
+ }
+
+ if (next_bits (1)) {
+ /*
+ * Since the NEXT bit is a 1, round UP the mantissa.
+ * The cunning design of these hidden-1 floats permits
+ * us to let the mantissa overflow into the exponent, and
+ * it 'does the right thing'. However, we lose if the
+ * highest-order bit of the lowest-order word flips.
+ * Is that clear?
+ */
+
+ unsigned long carry;
+
+ /*
+ #if (sizeof(carry)) < ((sizeof(bits[0]) * BITS_PER_CHAR) + 2)
+ Please allow at least 1 more bit in carry than is in a LITTLENUM.
+ We need that extra bit to hold a carry during a LITTLENUM carry
+ propagation. Another extra bit (kept 0) will assure us that we
+ don't get a sticky sign bit after shifting right, and that
+ permits us to propagate the carry without any masking of bits.
+ #endif
+ */
+ for (carry = 1, lp--;
+ carry && (lp >= words);
+ lp--) {
+ carry = *lp + carry;
+ *lp = carry;
+ carry >>= LITTLENUM_NUMBER_OF_BITS;
+ }
+
+ if ((word1 ^ *words) & (1 << (LITTLENUM_NUMBER_OF_BITS - 1))) {
+ make_invalid_floating_point_number(words);
+ /*
+ * We leave return_value alone: admit we read the
+ * number, but return a floating exception
+ * because we can't encode the number.
+ */
+ }
+ } /* if (we needed to round up) */
+ } /* if (exponent overflow) */
+ } /* if (0.0e0) */
+ } /* if (float_type was OK) */
+ return(return_value);
+} /* flonum_gen2vax() */
+
+
+/* JF this used to be in vax.c but this looks like a better place for it */
+
+/*
+ * md_atof()
+ *
+ * In: input_line_pointer->the 1st character of a floating-point
+ * number.
+ * 1 letter denoting the type of statement that wants a
+ * binary floating point number returned.
+ * Address of where to build floating point literal.
+ * Assumed to be 'big enough'.
+ * Address of where to return size of literal (in chars).
+ *
+ * Out: Input_line_pointer->of next char after floating number.
+ * Error message, or "".
+ * Floating point literal.
+ * Number of chars we used for the literal.
+ */
+
+#define MAXIMUM_NUMBER_OF_LITTLENUMS (8) /* For .hfloats. */
+
+char *
+ md_atof (what_statement_type, literalP, sizeP)
+char what_statement_type;
+char * literalP;
+int * sizeP;
+{
+ LITTLENUM_TYPE words[MAXIMUM_NUMBER_OF_LITTLENUMS];
+ register char kind_of_float;
+ register int number_of_chars;
+ register LITTLENUM_TYPE * littlenum_pointer;
+
+ switch (what_statement_type)
+ {
+ case 'F': /* .float */
+ case 'f': /* .ffloat */
+ kind_of_float = 'f';
+ break;
+
+ case 'D': /* .double */
+ case 'd': /* .dfloat */
+ kind_of_float = 'd';
+ break;
+
+ case 'g': /* .gfloat */
+ kind_of_float = 'g';
+ break;
+
+ case 'h': /* .hfloat */
+ kind_of_float = 'h';
+ break;
+
+ default:
+ kind_of_float = 0;
+ break;
+ };
+
+ if (kind_of_float)
+ {
+ register LITTLENUM_TYPE * limit;
+
+ input_line_pointer = atof_vax (input_line_pointer,
+ kind_of_float,
+ words);
+ /*
+ * The atof_vax() builds up 16-bit numbers.
+ * Since the assembler may not be running on
+ * a little-endian machine, be very careful about
+ * converting words to chars.
+ */
+ number_of_chars = atof_vax_sizeof (kind_of_float);
+ know( number_of_chars <= MAXIMUM_NUMBER_OF_LITTLENUMS * sizeof(LITTLENUM_TYPE) );
+ limit = words + (number_of_chars / sizeof(LITTLENUM_TYPE));
+ for (littlenum_pointer = words;
+ littlenum_pointer < limit;
+ littlenum_pointer ++)
+ {
+ md_number_to_chars (literalP, * littlenum_pointer, sizeof(LITTLENUM_TYPE));
+ literalP += sizeof(LITTLENUM_TYPE);
+ };
+ }
+ else
+ {
+ number_of_chars = 0;
+ };
+
+ * sizeP = number_of_chars;
+ return (kind_of_float ? "" : "Bad call to md_atof()");
+} /* md_atof() */
+
+/* end of atof-vax.c */
diff --git a/gnu/usr.bin/as/config/coff.h b/gnu/usr.bin/as/config/coff.h
new file mode 100644
index 0000000..bcbb343
--- /dev/null
+++ b/gnu/usr.bin/as/config/coff.h
@@ -0,0 +1,783 @@
+/* coff.h
+ Copyright (C) 1987, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * At this point I'm sure this file is right for i960 and I'm pretty sure it's
+ * right for a29k, although it hasn't been tested rigorously. Please feel free
+ * to add your own machine's description here. Without that info, it isn't
+ * possible to build cross development tools from elsewhere nor is it easy to
+ * continue to support your machines format.
+ *
+ * The TC_foo ifdef's are mine. They are what gas uses. The other ifdef's
+ * remain for documentation from other scavenged files. xoxorich.
+ */
+
+/********************** FILE HEADER **********************/
+
+struct filehdr {
+ unsigned short f_magic; /* magic number */
+ unsigned short f_nscns; /* number of sections */
+ long f_timdat; /* time & date stamp */
+ long f_symptr; /* file pointer to symtab */
+ long f_nsyms; /* number of symtab entries */
+ unsigned short f_opthdr; /* sizeof(optional hdr) */
+ unsigned short f_flags; /* flags */
+};
+
+/* Bits for f_flags:
+ * F_RELFLG relocation info stripped from file
+ * F_EXEC file is executable (no unresolved externel references)
+ * F_LNNO line nunbers stripped from file
+ * F_LSYMS local symbols stripped from file
+ * F_AR32WR file has byte ordering of an AR32WR machine (e.g. vax)
+ */
+#define F_RELFLG (0x0001)
+#define F_EXEC (0x0002)
+#define F_LNNO (0x0004)
+#define F_LSYMS (0x0008)
+
+#ifdef TC_I960
+#define F_AR32WR (0x0010) /* File has 32 bits per word, least
+ significant byte first. */
+#else /* TC_I960 */
+#define F_AR32WR (0x0100)
+#endif /* TC_I960 */
+
+#define F_MINMAL (0x0010) /* ??? */
+#define F_UPDATE (0x0020) /* ??? */
+#define F_SWABD (0x0040) /* ??? */
+#define F_AR16WR (0x0080) /* File has the byte ordering used by
+ the PDP*-11/70 processor. */
+#define F_AR32W (0x0200) /* File has 32 bits per word, most
+ significant byte first. */
+
+/*
+ * Intel 80960 (I960) processor flags.
+ * F_I960TYPE == mask for processor type field.
+ */
+
+#define F_I960TYPE (0xf000)
+#define F_I960CORE (0x1000)
+#define F_I960KB (0x2000)
+#define F_I960SB (0x2000)
+#define F_I960MC (0x3000)
+#define F_I960XA (0x4000)
+#define F_I960CA (0x5000)
+#define F_I960KA (0x6000)
+#define F_I960SA (0x6000)
+
+/*
+ * i80960 Magic Numbers
+ */
+
+#define I960ROMAGIC (0x160) /* read-only text segments */
+#define I960RWMAGIC (0x161) /* read-write text segments */
+
+#define I960BADMAG(x) (((x).f_magic != I960ROMAGIC) && ((x).f_magic != I960RWMAGIC))
+
+#define SIPFBOMAGIC (0x17a) /* Am29000 (Byte 0 is MSB - Big Endian) */
+#define SIPRBOMAGIC (0x17b) /* Am29000 (Byte 0 is LSB - Little Endian) */
+
+#define A29KBADMAG(x) (((x).f_magic != SIPFBOMAGIC) && ((x).f_magic != SIPRBOMAGIC))
+
+#ifdef TE_I386AIX
+# define I386MAGIC (0x175) /* Danbury AIX C compiler */
+# define I386SVMAGIC (0x14c) /* System V C Compiler */
+# define I386BADMAG(x) (((x).f_magic != I386MAGIC) && \
+ ((x).f_magic != I386SVMAGIC))
+#else /* not TE_I386AIX */
+# define I386MAGIC 0x14c
+# define I386BADMAG(x) (((x).f_magic != I386MAGIC))
+#endif /* not TE_I386AIX */
+
+
+#define FILHDR struct filehdr
+#define FILHSZ sizeof(FILHDR)
+
+
+/********************** AOUT "OPTIONAL HEADER" **********************/
+
+typedef struct {
+ unsigned long phys_addr;
+ unsigned long bitarray;
+} TAGBITS;
+
+/* These appear to be used only by exec(2). I don't know who cares
+ about them in a cross development environment. In any case, this
+ is my collection after researching the issue for a few hours.
+ Apparently, most have these have remained essentially unchanged
+ since v7 days, although a few new ones have been added. xoxorich. */
+
+#define BAD0MAGIC (0401) /* (?) "lpd (UNIX/RT)" */
+#define BAD1MAGIC (0405) /* (?) overlay */
+#define OMAGIC (0407) /* old impure format. data immediately
+ follows text. both sections are rw. */
+#define NMAGIC (0410) /* split i&d, read-only text */
+#define A_MAGIC3 (0411) /* (?) "separated I&D" */
+#define ZMAGIC (0413) /* like NMAGIC, but demand loaded */
+#define PAGEMAGIC2 (0414) /* (?) like ZMAGIC, but address zero
+ explicitly unmapped. */
+#define REGMAGIC (0414) /* (?) a PAGEMAGIC2 alias? */
+#define PAGEMAGIC3 (0415) /* (?) like ZMAGIC, but address zero mapped. */
+#define A_MAGIC5 (0437) /* (?) "system overlay, separated I&D" */
+/* intended for non-unix cross development */
+#define SASMAGIC (010000) /* Single Address Space */
+#define MASMAGIC (020000) /* (?) "Multiple (separate I & D) Address Spaces" */
+
+typedef struct aouthdr {
+ short magic; /* type of file */
+ short vstamp; /* version stamp */
+ unsigned long tsize; /* text size in bytes, padded to FW bdry*/
+ unsigned long dsize; /* initialized data " " */
+ unsigned long bsize; /* uninitialized data " " */
+#if U3B
+ unsigned long dum1;
+ unsigned long dum2; /* pad to entry point */
+#endif
+ unsigned long entry; /* entry pt. */
+ unsigned long text_start; /* base of text used for this file */
+ unsigned long data_start; /* base of data used for this file */
+ /* CAREFUL: some formats omit the tagentries member. */
+ unsigned long tagentries; /* number of tag entries to
+ follow (always zero for i960) */
+} AOUTHDR;
+
+/* return a pointer to the tag bits array */
+
+#define TAGPTR(aout) ((TAGBITS *) (&(aout.tagentries)+1))
+
+/* compute size of a header */
+
+/*#define AOUTSZ(aout) (sizeof(AOUTHDR)+(aout.tagentries*sizeof(TAGBITS)))*/
+#define AOUTSZ (sizeof(AOUTHDR))
+
+
+/********************** STORAGE CLASSES **********************/
+
+#define C_EFCN -1 /* physical end of function */
+#define C_NULL 0
+#define C_AUTO 1 /* automatic variable */
+#define C_EXT 2 /* external symbol */
+#define C_STAT 3 /* static */
+#define C_REG 4 /* register variable */
+#define C_EXTDEF 5 /* external definition */
+#define C_LABEL 6 /* label */
+#define C_ULABEL 7 /* undefined label */
+#define C_MOS 8 /* member of structure */
+#define C_ARG 9 /* function argument */
+#define C_STRTAG 10 /* structure tag */
+#define C_MOU 11 /* member of union */
+#define C_UNTAG 12 /* union tag */
+#define C_TPDEF 13 /* type definition */
+#define C_USTATIC 14 /* undefined static */
+#define C_ENTAG 15 /* enumeration tag */
+#define C_MOE 16 /* member of enumeration */
+#define C_REGPARM 17 /* register parameter */
+#define C_FIELD 18 /* bit field */
+
+#ifdef TC_I960
+#define C_AUTOARG 19 /* auto argument */
+#define C_LASTENT 20 /* dummy entry (end of block) */
+#endif /* TC_I960 */
+
+#ifdef TC_A29K
+#define C_GLBLREG 19 /* global register */
+#define C_EXTREG 20 /* external global register */
+#define C_DEFREG 21 /* ext. def. of global register */
+#define C_STARTOF 22 /* as29 $SIZEOF and $STARTOF symbols */
+#endif /* TC_A29K */
+
+#define C_BLOCK 100 /* ".bb" or ".eb" */
+#define C_FCN 101 /* ".bf" or ".ef" */
+#define C_EOS 102 /* end of structure */
+#define C_FILE 103 /* file name */
+#define C_LINE 104 /* line # reformatted as symbol table entry */
+#define C_ALIAS 105 /* duplicate tag */
+#define C_HIDDEN 106 /* ext symbol in dmert public lib. like static,
+ used to avoid name conflicts. */
+
+#ifdef TC_I960
+/* New storage classes for 80960 */
+#define C_SCALL 107 /* Procedure reachable via system call */
+/* C_LEAFPROC is obsolete. Use C_LEAFEXT or C_LEAFSTAT */
+#define C_LEAFPROC 108 /* Leaf procedure, "call" via BAL */
+#define C_LEAFEXT 108
+#define C_OPTVAR 109 /* Optimized variable */
+#define C_DEFINE 110 /* Preprocessor #define */
+#define C_PRAGMA 111 /* Advice to compiler or linker */
+#define C_SEGMENT 112 /* 80960 segment name */
+#define C_LEAFSTAT 113 /* Static leaf */
+#endif /* TC_I960 */
+
+#ifdef TC_A29K
+#define C_SHADOW 107 /* shadow symbol */
+#endif /* TC_A29K */
+
+/********************** SECTION HEADER **********************/
+
+struct scnhdr {
+ char s_name[8]; /* section name */
+ long s_paddr; /* physical address, aliased s_nlib */
+ long s_vaddr; /* virtual address */
+ long s_size; /* section size */
+ long s_scnptr; /* file ptr to raw data for section */
+ long s_relptr; /* file ptr to relocation */
+ long s_lnnoptr; /* file ptr to line numbers */
+ unsigned short s_nreloc; /* number of relocation entries */
+ unsigned short s_nlnno; /* number of line number entries */
+ long s_flags; /* flags */
+
+#ifdef TC_I960
+ unsigned long s_align; /* section alignment */
+#endif /* TC_I960 */
+};
+
+#define SCNHDR struct scnhdr
+#define SCNHSZ sizeof(SCNHDR)
+
+/*
+ * names of "special" sections
+ */
+#define _TEXT ".text" /* executable code section */
+#define _DATA ".data" /* initialized data */
+#define _BSS ".bss" /* un-initialized data */
+#define _DEBUG ".debug" /* special section used by dbx */
+#define _COMMENT ".comment" /* version info */
+#define _LIB ".lib" /* shared lib info section */
+#define _TV ".tv"
+
+/*
+ * s_flags "type"
+ */
+
+/*
+ * In instances where it is necessary for a linker to
+ * produce an output file which contains text or data not
+ * based at virtual address 0, e.g. for a ROM, then the
+ * linker should accept address base information as command
+ * input and use PAD sections to skip over unused addresses.
+ * (at least for a29k. Maybe others.)
+ */
+
+#define STYP_REG (0x0000) /* "regular" section: allocated, relocated, loaded */
+#define STYP_DSECT (0x0001) /* "dummy" section: not allocated, relocated, not loaded */
+#define STYP_NOLOAD (0x0002) /* "noload" section: allocated, relocated, not loaded */
+#define STYP_GROUP (0x0004) /* "grouped" section: formed of input sections */
+#define STYP_PAD (0x0008) /* "padding" section: not allocated, not relocated, loaded */
+#define STYP_COPY (0x0010) /* "copy" section: for decision function used by field update; not allocated, not relocated,
+ loaded; reloc & lineno entries processed normally */
+#define STYP_TEXT (0x0020) /* section contains text only */
+#define S_SHRSEG (0x0020) /* In 3b Update files (output of ogen), sections which appear in SHARED segments of the Pfile
+ will have the S_SHRSEG flag set by ogen, to inform dufr that updating 1 copy of the proc. will
+ update all process invocations. */
+#define STYP_DATA (0x0040) /* section contains data only */
+#define STYP_BSS (0x0080) /* section contains bss only */
+#define S_NEWFCN (0x0100) /* In a minimal file or an update file, a new function (as compared with a replaced function) */
+#define STYP_INFO (0x0200) /* comment section : not allocated not relocated, not loaded */
+#define STYP_OVER (0x0400) /* overlay section : relocated not allocated or loaded */
+#define STYP_LIB (0x0800) /* for .lib section : same as INFO */
+#define STYP_MERGE (0x2000) /* merge section -- combines with text, data or bss sections only */
+#define STYP_REVERSE_PAD (0x4000) /* section will be padded with no-op instructions wherever padding is necessary and there is a
+ word of contiguous bytes beginning on a word boundary. */
+
+#ifdef TC_A29K
+/* NOTE: The use of STYP_BSSREG for relocation is not yet defined. */
+#define STYP_BSSREG 0x1200 /* Global register area (like STYP_INFO) */
+#define STYP_ENVIR 0x2200 /* Environment (like STYP_INFO) */
+#define STYP_ABS 0x4000 /* Absolute (allocated, not reloc, loaded) */
+#define STYP_LIT 0x8020 /* Literal data (like STYP_TEXT) */
+#endif /* TC_A29K */
+
+/********************** LINE NUMBERS **********************/
+
+/* 1 line number entry for every "breakpointable" source line in a section.
+ * Line numbers are grouped on a per function basis; first entry in a function
+ * grouping will have l_lnno = 0 and in place of physical address will be the
+ * symbol table index of the function name.
+ */
+struct lineno {
+ union {
+ long l_symndx; /* symbol index of function name, iff l_lnno == 0*/
+ long l_paddr; /* (physical) address of line number */
+ } l_addr;
+ unsigned short l_lnno; /* line number */
+#ifdef TC_I960
+ /* not used on a29k */
+ char padding[2]; /* force alignment */
+#endif /* TC_I960 */
+};
+
+#define LINENO struct lineno
+#define LINESZ sizeof(LINENO)
+
+
+/********************** SYMBOLS **********************/
+
+#define SYMNMLEN 8 /* # characters in a symbol name */
+#define FILNMLEN 14 /* # characters in a file name */
+#define DIMNUM 4 /* # array dimensions in auxiliary entry */
+
+struct syment {
+ union {
+ char _n_name[SYMNMLEN]; /* old COFF version */
+ struct {
+ long _n_zeroes; /* new == 0 */
+ long _n_offset; /* offset into string table */
+ } _n_n;
+ char *_n_nptr[2]; /* allows for overlaying */
+ } _n;
+ long n_value; /* value of symbol */
+ short n_scnum; /* section number */
+
+#ifdef TC_I960
+ /* This isn't yet used on the i960. In some formats this
+ is two bytes of padding. In others, it is missing entirely. */
+ unsigned short n_flags; /* copy of flags from filhdr */
+#endif /* TC_I960 */
+
+#ifdef TC_A29K
+ unsigned short n_type; /* type and derived type */
+#else /* TC_A29K */
+ /* at least i960 uses long */
+ unsigned long n_type; /* type and derived type */
+#endif /* TC_A29K */
+
+ char n_sclass; /* storage class */
+ char n_numaux; /* number of aux. entries */
+
+#ifndef TC_A29K
+ char pad2[2]; /* force alignment */
+#endif /* TC_A29K */
+};
+
+#define SYMENT struct syment
+#define SYMESZ sizeof(SYMENT) /* This had better also be sizeof(AUXENT) */
+
+#define n_name _n._n_name
+#define n_ptr _n._n_nptr[1]
+#define n_zeroes _n._n_n._n_zeroes
+#define n_offset _n._n_n._n_offset
+
+ /*
+ * Relocatable symbols have number of the section in which they are defined,
+ * or one of the following:
+ */
+
+#define N_SCNUM ((short) 1-65535) /* section num where symbol defined */
+#define N_UNDEF ((short)0) /* undefined symbol */
+#define N_ABS ((short)-1) /* value of symbol is absolute */
+#define N_DEBUG ((short)-2) /* debugging symbol -- symbol value is meaningless */
+#define N_TV ((short)-3) /* indicates symbol needs preload transfer vector */
+#define P_TV ((short)-4) /* indicates symbol needs transfer vector (postload) */
+
+/*
+ * Type of a symbol, in low 4 bits of the word
+ */
+#define T_NULL 0 /* type not assigned */
+#define T_VOID 1 /* function argument (only used by compiler) (but now real void). */
+#define T_CHAR 2 /* character */
+#define T_SHORT 3 /* short integer */
+#define T_INT 4 /* integer */
+#define T_LONG 5 /* long integer */
+#define T_FLOAT 6 /* floating point */
+#define T_DOUBLE 7 /* double word */
+#define T_STRUCT 8 /* structure */
+#define T_UNION 9 /* union */
+#define T_ENUM 10 /* enumeration */
+#define T_MOE 11 /* member of enumeration */
+#define T_UCHAR 12 /* unsigned character */
+#define T_USHORT 13 /* unsigned short */
+#define T_UINT 14 /* unsigned integer */
+#define T_ULONG 15 /* unsigned long */
+
+#ifdef TC_I960
+#define T_LNGDBL 16 /* long double */
+#endif /* TC_I960 */
+
+/*
+ * derived types, in n_type
+ */
+#define DT_NON (0) /* no derived type */
+#define DT_PTR (1) /* pointer */
+#define DT_FCN (2) /* function */
+#define DT_ARY (3) /* array */
+
+#ifndef TC_I960
+
+#define N_BTMASK (0x0f)
+#define N_TMASK (0x30)
+#define N_BTSHFT (4)
+#define N_TSHIFT (2)
+
+#else /* TC_I960 */
+
+#define N_BTMASK (0x1f)
+#define N_TMASK (0x60)
+#define N_BTSHFT (5)
+#define N_TSHIFT (2)
+
+#endif /* TC_I960 */
+
+#define BTYPE(x) ((x) & N_BTMASK)
+
+#define ISPTR(x) (((x) & N_TMASK) == (DT_PTR << N_BTSHFT))
+#define ISFCN(x) (((x) & N_TMASK) == (DT_FCN << N_BTSHFT))
+#define ISARY(x) (((x) & N_TMASK) == (DT_ARY << N_BTSHFT))
+
+#define DECREF(x) ((((x)>>N_TSHIFT)&~N_BTMASK)|((x)&N_BTMASK))
+
+union auxent {
+ struct {
+ long x_tagndx; /* str, un, or enum tag indx */
+ union {
+ struct {
+ unsigned short x_lnno; /* declaration line number */
+ unsigned short x_size; /* str/union/array size */
+ } x_lnsz;
+ long x_fsize; /* size of function */
+ } x_misc;
+ union {
+ struct { /* if ISFCN, tag, or .bb */
+ long x_lnnoptr; /* ptr to fcn line # */
+ long x_endndx; /* entry ndx past block end */
+ } x_fcn;
+ struct { /* if ISARY, up to 4 dimen. */
+ unsigned short x_dimen[DIMNUM];
+ } x_ary;
+ } x_fcnary;
+ unsigned short x_tvndx; /* tv index */
+ } x_sym;
+
+ /* This was just a struct x_file with x_fname only in a29k. xoxorich. */
+ union {
+ char x_fname[FILNMLEN];
+ struct {
+ long x_zeroes;
+ long x_offset;
+ } x_n;
+ } x_file;
+
+ struct {
+ long x_scnlen; /* section length */
+ unsigned short x_nreloc; /* # relocation entries */
+ unsigned short x_nlinno; /* # line numbers */
+ } x_scn;
+
+ struct {
+ long x_tvfill; /* tv fill value */
+ unsigned short x_tvlen; /* length of .tv */
+
+ /* This field was typo'd x_tvrna on a29k. xoxorich. */
+ unsigned short x_tvran[2]; /* tv range */
+ } x_tv; /* info about .tv section (in auxent of symbol .tv)) */
+
+#ifdef TC_I960
+ /******************************************
+ * I960-specific *2nd* aux. entry formats
+ ******************************************/
+ struct {
+ /* This is a very old typo that keeps getting propogated. */
+#define x_stdindx x_stindx
+ long x_stindx; /* sys. table entry */
+ } x_sc; /* system call entry */
+
+ struct {
+ unsigned long x_balntry; /* BAL entry point */
+ } x_bal; /* BAL-callable function */
+
+ struct {
+ unsigned long x_timestamp; /* time stamp */
+ char x_idstring[20]; /* producer identity string */
+ } x_ident; /* Producer ident info */
+
+ char a[sizeof(struct syment)]; /* force auxent/syment sizes to match */
+#endif /* TC_I960 */
+};
+
+#define AUXENT union auxent
+#define AUXESZ sizeof(AUXENT) /* This had better also be sizeof(SYMENT) */
+
+#if VAX || I960
+# define _ETEXT "_etext"
+#else
+# define _ETEXT "etext"
+#endif
+
+/********************** RELOCATION DIRECTIVES **********************/
+
+struct reloc {
+ long r_vaddr; /* Virtual address of reference */
+ long r_symndx; /* Index into symbol table */
+ unsigned short r_type; /* Relocation type */
+#ifdef TC_I960
+ /* not used for a29k */
+ char pad[2]; /* Unused */
+#endif /* TC_I960 */
+};
+
+#define RELOC struct reloc
+#define RELSZ sizeof(RELOC)
+
+#define R_ABS (0x00) /* reference is absolute */
+
+#ifdef TC_I960
+#define R_RELLONG (0x11) /* Direct 32-bit relocation */
+#define R_IPRSHORT (0x18)
+#define R_IPRMED (0x19) /* 24-bit ip-relative relocation */
+#define R_IPRLONG (0x1a)
+#define R_OPTCALL (0x1b) /* 32-bit optimizable call (leafproc/sysproc) */
+#define R_OPTCALLX (0x1c) /* 64-bit optimizable call (leafproc/sysproc) */
+#define R_GETSEG (0x1d)
+#define R_GETPA (0x1e)
+#define R_TAGWORD (0x1f)
+#endif /* TC_I960 */
+
+#ifdef TC_A29K
+/*
+ * NOTE: All the "I" forms refer to Am29000 instruction
+ * formats. The linker is expected to know how the numeric
+ * information is split and/or aligned within the
+ * instruction word(s). R_BYTE works for instructions, too.
+ *
+ * If the parameter to a CONSTH instruction is a relocatable
+ * type, two relocation records are written. The first has
+ * an r_type of R_IHIHALF (33 octal) and a normal r_vaddr
+ * and r_symndx. The second relocation record has an r_type
+ * of R_IHCONST (34 octal), a normal r_vaddr (which is
+ * redundant), and an r_symndx containing the 32-bit
+ * constant offset to the relocation instead of the actual
+ * symbol table index. This second record is always
+ * written, even if the constant offset is zero. The
+ * constant fields of the instruction are set to zero.
+ */
+
+#define R_IREL (0x18) /* instruction relative (jmp/call) */
+#define R_IABS (0x19) /* instruction absolute (jmp/call) */
+#define R_ILOHALF (0x1a) /* instruction low half (const) */
+#define R_IHIHALF (0x1b) /* instruction high half (consth) part 1 */
+#define R_IHCONST (0x1c) /* instruction high half (consth) part 2
+ constant offset of R_IHIHALF relocation */
+#define R_BYTE (0x1d) /* relocatable byte value */
+#define R_HWORD (0x1e) /* relocatable halfword value */
+#define R_WORD (0x1f) /* relocatable word value */
+#define R_IGLBLRC (0x20) /* instruction global register RC */
+#define R_IGLBLRA (0x21) /* instruction global register RA */
+#define R_IGLBLRB (0x22) /* instruction global register RB */
+#endif /* TC_A29K */
+
+
+#define DEFAULT_DATA_SECTION_ALIGNMENT 4
+#define DEFAULT_BSS_SECTION_ALIGNMENT 4
+#define DEFAULT_TEXT_SECTION_ALIGNMENT 16
+/* For new sections we haven't heard of before */
+#define DEFAULT_SECTION_ALIGNMENT 4
+
+#if defined(TC_I386)
+/*
+ * X86 generic
+ * 8-bit offset reference in 8-bits
+ * 8-bit offset reference in 16-bits
+ * 12-bit segment reference
+ * auxiliary relocation entry
+ */
+#define R_OFF8 07
+#define R_OFF16 010
+#define R_SEG12 011
+#define R_AUX 013
+
+/*
+ * B16 and X86 generics
+ * 16-bit direct reference
+ * 16-bit "relative" reference
+ * 16-bit "indirect" (TV) reference
+ */
+#define R_DIR16 01
+#define R_REL16 02
+#define R_IND16 03
+
+/*
+ * 3B generic
+ * 24-bit direct reference
+ * 24-bit "relative" reference
+ * 16-bit optimized "indirect" TV reference
+ * 24-bit "indirect" TV reference
+ * 32-bit "indirect" TV reference
+ */
+#define R_DIR24 04
+#define R_REL24 05
+#define R_OPT16 014
+#define R_IND24 015
+#define R_IND32 016
+
+/*
+ * XL generics
+ * 10-bit direct reference
+ * 10-bit "relative" reference
+ * 32-bit "relative" reference
+ */
+#define R_DIR10 025
+#define R_REL10 026
+#define R_REL32 027
+
+/*
+ * 3B and M32 generics
+ * 32-bit direct reference
+ */
+#define R_DIR32 06
+
+/*
+ * M32 generic
+ * 32-bit direct reference with bytes swapped
+ */
+#define R_DIR32S 012
+
+#endif /* TC_I386 */
+
+#if defined(TE_I386AIX)
+
+#define UINFOSIZ 64 /* size of user info buffer */
+typedef char uinfo_t[UINFOSIZ];
+
+struct env387 {
+ unsigned short control;
+ unsigned short r0;
+ unsigned short status;
+ unsigned short r1;
+ unsigned short tag;
+ unsigned short r2;
+ unsigned long eip;
+ unsigned short code_seg;
+ unsigned short opcode;
+ unsigned long operand;
+ unsigned short operand_seg;
+ unsigned short r3;
+ unsigned char regs[8][10];
+};
+
+#define CD_NAMELEN 16 /* length of most names in this header */
+#define CORHDRSIZ 2048 /* size to which header is padded out */
+#define MAX_CORE_SEGS 32 /* maximum segments in a core dump */
+#define NUM_FREGS 1 /* # of saved FP regs */
+
+/*
+ * These are defined such that 286 and 386 kernels can produce
+ * compatible dumps.
+ */
+#define CD_AX 0
+#define CD_BX 1
+#define CD_CX 2
+#define CD_DX 3
+#define CD_SI 4
+#define CD_DI 5
+#define CD_BP 6
+#define CD_SP 7
+#define CD_FL 8
+#define CD_IP 9
+#define CD_CS 10
+#define CD_DS 11
+#define CD_ES 12
+#define CD_FS 13
+#define CD_GS 14
+#define CD_SS 15
+#define NUM_REGS 16
+
+#ifndef SPATHLEN
+# define SPATHLEN 16 /* sys/param.h */
+#endif
+#ifndef NSIG
+# define NSIG 63 /* sys/signal.h */
+# define SIGSETSZ ((NSIG+31)/32)
+typedef struct ksigmask {
+ unsigned long sigs[SIGSETSZ];
+} ksigmask_t;
+#endif
+
+struct corehdr {
+ char cd_magic[4]; /* COR_MAGIC = "core" */
+
+ /* general information about the dump itself */
+ struct dumpseg { /* table of contents for dump */
+ long cs_type; /* seg. type; see below */
+ long cs_len; /* length (in bytes) of segment */
+ long cs_offset; /* offset (in dump) of segment */
+ long cs_address; /* address segment had in mem */
+ } cd_segs[MAX_CORE_SEGS];
+
+ /* general information about the process */
+ char cd_comm[CD_NAMELEN]; /* command being run */
+ char cd_mach[CD_NAMELEN]; /* type of machine it ran on */
+ char cd_site[CD_NAMELEN]; /* name of site it ran on */
+ long cd_ldtype; /* type of load module running */
+ char cd_intsize; /* sizeof(int) */
+ char cd_dptrsize; /* sizeof(char *) */
+ char cd_tptrsize; /* sizeof(int (*)()) */
+ char cd_unused;
+
+ /* user-mode program state */
+ long cd_regs[NUM_REGS]; /* user-mode general registers */
+ struct env387 cd_fpregs; /* user-mode floating-point state */
+
+ /* kernel-mode program state */
+ int (*cd_sig[NSIG])(); /* disposition of signals */
+ ksigmask_t cd_sigmask; /* signals to be blocked */
+ ksigmask_t cd_sigpend; /* signals currently pending */
+ long cd_cursig; /* signal that caused the dump */
+
+ long cd_pid; /* process ID of the corpse */
+ long cd_ppid; /* parent process ID of corpse */
+ short cd_uid; /* process effective user ID */
+ short cd_ruid; /* process real user ID */
+ short cd_gid; /* process effective group ID */
+ short cd_rgid; /* process real group ID */
+
+ uinfo_t cd_uinfo; /* buffer of user information */
+ char cd_locname[32]; /* name of /local */
+ char cd_uvers[CD_NAMELEN]; /* user version string */
+ unsigned short cd_spath[SPATHLEN]; /* sitepath */
+};
+
+#ifndef NOCHECKS
+/* this will generate an error if sizeof(struct corehdr) > CORHDRSIZ */
+struct { char xxcdxx[CORHDRSIZ+1-sizeof(struct corehdr)]; };
+#endif /* ! NOCHECKS */
+
+/*
+ * segment types (in cs_type)
+ * each segment in the address space appears here, whether or not it
+ * is actually dumped. Read/only segments will not actually be dumped.
+ * A segment that is not in the dump will have a cs_offset of zero.
+ */
+#define COR_TYPE_CODE 'x' /* process code - NOT IN DUMP */
+#define COR_TYPE_DATA 'd' /* process data segment */
+#define COR_TYPE_STACK 's' /* process stack segment */
+#define COR_TYPE_LIBCODE 'X' /* shared lib code - NOT IN DUMP*/
+#define COR_TYPE_LIBDATA 'D' /* shared lib data */
+#define COR_TYPE_READ 'r' /* other read/only - NOT IN DUMP*/
+#define COR_TYPE_WRITE 'w' /* other writeable */
+#define COR_TYPE_MSC '?' /* other, mapped in segment */
+
+#endif /* TE_I386AIX */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * End:
+ */
+
+/* end of coff.h */
diff --git a/gnu/usr.bin/as/config/cplus-dem.c b/gnu/usr.bin/as/config/cplus-dem.c
new file mode 100644
index 0000000..e3819bc
--- /dev/null
+++ b/gnu/usr.bin/as/config/cplus-dem.c
@@ -0,0 +1,927 @@
+/* Demangler for GNU C++
+ Copyright (C) 1989, 1992 Free Software Foundation, Inc.
+ written by James Clark (jjc@jclark.uucp)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This is for g++ 1.36.1 (November 6 version). It will probably
+ require changes for any other version. */
+
+/* This file exports one function
+
+ char *cplus_demangle (const char *name)
+
+ If `name' is a mangled function name produced by g++, then
+ a pointer to a malloced string giving a C++ representation
+ of the name will be returned; otherwise NULL will be returned.
+ It is the caller's responsibility to free the string which
+ is returned.
+
+ For example,
+
+ cplus_demangle ("_foo__1Ai")
+
+ returns
+
+ "A::foo(int)"
+
+ This file imports xmalloc and xrealloc, which are like malloc and
+ realloc except that they generate a fatal error if there is no
+ available memory. */
+
+/* #define nounderscore 1 /* define this is names don't start with _ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#if !defined(sequent) && !defined(NeXT)
+#include <memory.h>
+#else
+#define memcpy(s1, s2, n) strncpy(s1, s2, n)
+#define memcmp(s1, s2, n) strncmp(s1, s2, n)
+#define strchr(s, c) index(s, c)
+#endif
+
+#if __STDC__ != 1
+#define const
+#endif
+
+#if __STDC__ == 1
+extern char *cplus_demangle (const char *type);
+#else
+extern char *cplus_demangle ();
+#endif
+
+#if __STDC__ == 1
+extern char *xmalloc (int);
+extern char *xrealloc (char *, int);
+#else
+extern char *xmalloc ();
+extern char *xrealloc ();
+#endif
+
+static char **typevec = 0;
+static int ntypes = 0;
+static int typevec_size = 0;
+
+static struct {
+ const char *in;
+ const char *out;
+} optable[] = {
+ "new", " new",
+ "delete", " delete",
+ "ne", "!=",
+ "eq", "==",
+ "ge", ">=",
+ "gt", ">",
+ "le", "<=",
+ "lt", "<",
+ "plus", "+",
+ "minus", "-",
+ "mult", "*",
+ "negate", "-",
+ "trunc_mod", "%",
+ "trunc_div", "/",
+ "truth_andif", "&&",
+ "truth_orif", "||",
+ "postincrement", "++",
+ "postdecrement", "--",
+ "bit_ior", "|",
+ "bit_xor", "^",
+ "bit_and", "&",
+ "bit_not", "~",
+ "call", "()",
+ "cond", "?:",
+ "alshift", "<<",
+ "arshift", ">>",
+ "component", "->",
+ "nop", "", /* for operator= */
+};
+
+/* Beware: these aren't '\0' terminated. */
+
+typedef struct {
+ char *b; /* pointer to start of string */
+ char *p; /* pointer after last character */
+ char *e; /* pointer after end of allocated space */
+} string;
+
+#if __STDC__ == 1
+static void string_need (string *s, int n);
+static void string_delete (string *s);
+static void string_init (string *s);
+static void string_clear (string *s);
+static int string_empty (string *s);
+static void string_append (string *p, const char *s);
+static void string_appends (string *p, string *s);
+static void string_appendn (string *p, const char *s, int n);
+static void string_prepend (string *p, const char *s);
+#if 0
+static void string_prepends (string *p, string *s);
+#endif
+static void string_prependn (string *p, const char *s, int n);
+static int get_count (const char **type, int *count);
+static int do_args (const char **type, string *decl);
+static int do_type (const char **type, string *result);
+static int do_arg (const char **type, string *result);
+static int do_args (const char **type, string *decl);
+static void munge_function_name (string *name);
+#else
+static void string_need ();
+static void string_delete ();
+static void string_init ();
+static void string_clear ();
+static int string_empty ();
+static void string_append ();
+static void string_appends ();
+static void string_appendn ();
+static void string_prepend ();
+static void string_prepends ();
+static void string_prependn ();
+static int get_count ();
+static int do_args ();
+static int do_type ();
+static int do_arg ();
+static int do_args ();
+static void munge_function_name ();
+#endif
+
+char *
+ cplus_demangle (type)
+const char *type;
+{
+ string decl;
+ int n;
+ int success = 0;
+ int constructor = 0;
+ int const_flag = 0;
+ int i;
+ const char *p;
+
+ if (type == NULL || *type == '\0')
+ return NULL;
+#ifndef nounderscore
+ if (*type++ != '_')
+ return NULL;
+#endif
+ p = type;
+ while (*p != '\0' && !(*p == '_' && p[1] == '_'))
+ p++;
+ if (*p == '\0')
+ {
+ /* destructor */
+ if (type[0] == '_' && type[1] == '$' && type[2] == '_')
+ {
+ int n = (strlen (type) - 3)*2 + 3 + 2 + 1;
+ char *tem = (char *) xmalloc (n);
+ strcpy (tem, type + 3);
+ strcat (tem, "::~");
+ strcat (tem, type + 3);
+ strcat (tem, "()");
+ return tem;
+ }
+ /* static data member */
+ if (*type != '_' && (p = strchr (type, '$')) != '\0')
+ {
+ int n = strlen (type) + 2;
+ char *tem = (char *) xmalloc (n);
+ memcpy (tem, type, p - type);
+ strcpy (tem + (p - type), "::");
+ strcpy (tem + (p - type) + 2, p + 1);
+ return tem;
+ }
+ return NULL;
+ }
+
+ string_init (&decl);
+
+ if (p == type)
+ {
+ if (!isdigit (p[2]))
+ {
+ string_delete (&decl);
+ return NULL;
+ }
+ constructor = 1;
+ }
+ else
+ {
+ string_appendn (&decl, type, p - type);
+ munge_function_name (&decl);
+ }
+ p += 2;
+
+ switch (*p)
+ {
+ case 'C':
+ /* a const member function */
+ if (!isdigit (p[1]))
+ {
+ string_delete (&decl);
+ return NULL;
+ }
+ p += 1;
+ const_flag = 1;
+ /* fall through */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ n = 0;
+ do
+ {
+ n *= 10;
+ n += *p - '0';
+ p += 1;
+ }
+ while (isdigit (*p));
+ if (strlen (p) < n)
+ {
+ string_delete (&decl);
+ return NULL;
+ }
+ if (constructor)
+ {
+ string_appendn (&decl, p, n);
+ string_append (&decl, "::");
+ string_appendn (&decl, p, n);
+ }
+ else
+ {
+ string_prepend (&decl, "::");
+ string_prependn (&decl, p, n);
+ }
+ p += n;
+ success = do_args (&p, &decl);
+ if (const_flag)
+ string_append (&decl, " const");
+ break;
+ case 'F':
+ p += 1;
+ success = do_args (&p, &decl);
+ break;
+ }
+
+ for (i = 0; i < ntypes; i++)
+ if (typevec[i] != NULL)
+ free (typevec[i]);
+ ntypes = 0;
+ if (typevec != NULL)
+ {
+ free ((char *)typevec);
+ typevec = NULL;
+ typevec_size = 0;
+ }
+
+ if (success)
+ {
+ string_appendn (&decl, "", 1);
+ return decl.b;
+ }
+ else
+ {
+ string_delete (&decl);
+ return NULL;
+ }
+}
+
+static int
+ get_count (type, count)
+const char **type;
+int *count;
+{
+ if (!isdigit (**type))
+ return 0;
+ *count = **type - '0';
+ *type += 1;
+ /* see flush_repeats in cplus-method.c */
+ if (isdigit (**type))
+ {
+ const char *p = *type;
+ int n = *count;
+ do
+ {
+ n *= 10;
+ n += *p - '0';
+ p += 1;
+ }
+ while (isdigit (*p));
+ if (*p == '_')
+ {
+ *type = p + 1;
+ *count = n;
+ }
+ }
+ return 1;
+}
+
+/* result will be initialised here; it will be freed on failure */
+
+static int
+ do_type (type, result)
+const char **type;
+string *result;
+{
+ int n;
+ int done;
+ int non_empty;
+ int success;
+ string decl;
+ const char *remembered_type;
+
+ string_init (&decl);
+ string_init (result);
+
+ done = 0;
+ success = 1;
+ while (success && !done)
+ {
+ int member;
+ switch (**type)
+ {
+ case 'P':
+ *type += 1;
+ string_prepend (&decl, "*");
+ break;
+
+ case 'R':
+ *type += 1;
+ string_prepend (&decl, "&");
+ break;
+
+ case 'T':
+ *type += 1;
+ if (!get_count (type, &n) || n >= ntypes)
+ success = 0;
+ else
+ {
+ remembered_type = typevec[n];
+ type = &remembered_type;
+ }
+ break;
+
+ case 'F':
+ *type += 1;
+ if (!string_empty (&decl) && decl.b[0] == '*')
+ {
+ string_prepend (&decl, "(");
+ string_append (&decl, ")");
+ }
+ if (!do_args (type, &decl) || **type != '_')
+ success = 0;
+ else
+ *type += 1;
+ break;
+
+ case 'M':
+ case 'O':
+ {
+ int constp = 0;
+ int volatilep = 0;
+
+ member = **type == 'M';
+ *type += 1;
+ if (!isdigit (**type))
+ {
+ success = 0;
+ break;
+ }
+ n = 0;
+ do
+ {
+ n *= 10;
+ n += **type - '0';
+ *type += 1;
+ }
+ while (isdigit (**type));
+ if (strlen (*type) < n)
+ {
+ success = 0;
+ break;
+ }
+ string_append (&decl, ")");
+ string_prepend (&decl, "::");
+ string_prependn (&decl, *type, n);
+ string_prepend (&decl, "(");
+ *type += n;
+ if (member)
+ {
+ if (**type == 'C')
+ {
+ *type += 1;
+ constp = 1;
+ }
+ if (**type == 'V')
+ {
+ *type += 1;
+ volatilep = 1;
+ }
+ if (*(*type)++ != 'F')
+ {
+ success = 0;
+ break;
+ }
+ }
+ if ((member && !do_args (type, &decl)) || **type != '_')
+ {
+ success = 0;
+ break;
+ }
+ *type += 1;
+ if (constp)
+ {
+ if (non_empty)
+ string_append (&decl, " ");
+ else
+ non_empty = 1;
+ string_append (&decl, "const");
+ }
+ if (volatilep)
+ {
+ if (non_empty)
+ string_append (&decl, " ");
+ else
+ non_empty = 1;
+ string_append (&decl, "volatilep");
+ }
+ break;
+ }
+
+ case 'C':
+ if ((*type)[1] == 'P')
+ {
+ *type += 1;
+ if (!string_empty (&decl))
+ string_prepend (&decl, " ");
+ string_prepend (&decl, "const");
+ break;
+ }
+
+ /* fall through */
+ default:
+ done = 1;
+ break;
+ }
+ }
+
+ done = 0;
+ non_empty = 0;
+ while (success && !done)
+ {
+ switch (**type)
+ {
+ case 'C':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ else
+ non_empty = 1;
+ string_append (result, "const");
+ break;
+ case 'U':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ else
+ non_empty = 1;
+ string_append (result, "unsigned");
+ break;
+ case 'V':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ else
+ non_empty = 1;
+ string_append (result, "volatile");
+ break;
+ default:
+ done = 1;
+ break;
+ }
+ }
+
+ if (success)
+ switch (**type)
+ {
+ case '\0':
+ case '_':
+ break;
+ case 'v':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "void");
+ break;
+ case 'l':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "long");
+ break;
+ case 'i':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "int");
+ break;
+ case 's':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "short");
+ break;
+ case 'c':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "char");
+ break;
+ case 'r':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "long double");
+ break;
+ case 'd':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "double");
+ break;
+ case 'f':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "float");
+ break;
+ case 'G':
+ *type += 1;
+ if (!isdigit (**type))
+ {
+ success = 0;
+ break;
+ }
+ /* fall through */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ n = 0;
+ do
+ {
+ n *= 10;
+ n += **type - '0';
+ *type += 1;
+ }
+ while (isdigit (**type));
+ if (strlen (*type) < n)
+ {
+ success = 0;
+ break;
+ }
+ if (non_empty)
+ string_append (result, " ");
+ string_appendn (result, *type, n);
+ *type += n;
+ break;
+ default:
+ success = 0;
+ break;
+ }
+
+ if (success)
+ {
+ if (!string_empty (&decl))
+ {
+ string_append (result, " ");
+ string_appends (result, &decl);
+ }
+ string_delete (&decl);
+ return 1;
+ }
+ else
+ {
+ string_delete (&decl);
+ string_delete (result);
+ return 0;
+ }
+}
+
+/* `result' will be initialised in do_type; it will be freed on failure */
+
+static int
+ do_arg (type, result)
+const char **type;
+string *result;
+{
+ char *tem;
+ int len;
+ const char *start;
+ const char *end;
+
+ start = *type;
+ if (!do_type (type, result))
+ return 0;
+ end = *type;
+ if (ntypes >= typevec_size)
+ {
+ if (typevec_size == 0)
+ {
+ typevec_size = 3;
+ typevec = (char **) xmalloc (sizeof (char*)*typevec_size);
+ }
+ else
+ {
+ typevec_size *= 2;
+ typevec = (char **) xrealloc ((char *)typevec, sizeof (char*)*typevec_size);
+ }
+ }
+ len = end - start;
+ tem = (char *) xmalloc (len + 1);
+ memcpy (tem, start, len);
+ tem[len] = '\0';
+ typevec[ntypes++] = tem;
+ return 1;
+}
+
+/* `decl' must be already initialised, usually non-empty;
+ it won't be freed on failure */
+
+static int
+ do_args (type, decl)
+const char **type;
+string *decl;
+{
+ string arg;
+ int need_comma = 0;
+
+ string_append (decl, "(");
+
+ while (**type != '_' && **type != '\0' && **type != 'e' && **type != 'v')
+ {
+ if (**type == 'N')
+ {
+ int r;
+ int t;
+ *type += 1;
+ if (!get_count (type, &r) || !get_count (type, &t) || t >= ntypes)
+ return 0;
+ while (--r >= 0)
+ {
+ const char *tem = typevec[t];
+ if (need_comma)
+ string_append (decl, ", ");
+ if (!do_arg (&tem, &arg))
+ return 0;
+ string_appends (decl, &arg);
+ string_delete (&arg);
+ need_comma = 1;
+ }
+ }
+ else
+ {
+ if (need_comma)
+ string_append (decl, ", ");
+ if (!do_arg (type, &arg))
+ return 0;
+ string_appends (decl, &arg);
+ string_delete (&arg);
+ need_comma = 1;
+ }
+ }
+
+ if (**type == 'v')
+ *type += 1;
+ else if (**type == 'e')
+ {
+ *type += 1;
+ if (need_comma)
+ string_append (decl, ",");
+ string_append (decl, "...");
+ }
+
+ string_append (decl, ")");
+ return 1;
+}
+
+static void
+ munge_function_name (name)
+string *name;
+{
+ if (!string_empty (name) && name->p - name->b >= 3
+ && name->b[0] == 'o' && name->b[1] == 'p' && name->b[2] == '$')
+ {
+ int i;
+ /* see if it's an assignment expression */
+ if (name->p - name->b >= 10 /* op$assign_ */
+ && memcmp (name->b + 3, "assign_", 7) == 0)
+ {
+ for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++)
+ {
+ int len = name->p - name->b - 10;
+ if (strlen (optable[i].in) == len
+ && memcmp (optable[i].in, name->b + 10, len) == 0)
+ {
+ string_clear (name);
+ string_append (name, "operator");
+ string_append (name, optable[i].out);
+ string_append (name, "=");
+ return;
+ }
+ }
+ }
+ else
+ {
+ for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++)
+ {
+ int len = name->p - name->b - 3;
+ if (strlen (optable[i].in) == len
+ && memcmp (optable[i].in, name->b + 3, len) == 0)
+ {
+ string_clear (name);
+ string_append (name, "operator");
+ string_append (name, optable[i].out);
+ return;
+ }
+ }
+ }
+ return;
+ }
+ else if (!string_empty (name) && name->p - name->b >= 5
+ && memcmp (name->b, "type$", 5) == 0)
+ {
+ /* type conversion operator */
+ string type;
+ const char *tem = name->b + 5;
+ if (do_type (&tem, &type))
+ {
+ string_clear (name);
+ string_append (name, "operator ");
+ string_appends (name, &type);
+ string_delete (&type);
+ return;
+ }
+ }
+}
+
+/* a mini string-handling package */
+
+static void
+ string_need (s, n)
+string *s;
+int n;
+{
+ if (s->b == NULL)
+ {
+ if (n < 32)
+ n = 32;
+ s->p = s->b = (char *) xmalloc (n);
+ s->e = s->b + n;
+ }
+ else if (s->e - s->p < n)
+ {
+ int tem = s->p - s->b;
+ n += tem;
+ n *= 2;
+ s->b = (char *) xrealloc (s->b, n);
+ s->p = s->b + tem;
+ s->e = s->b + n;
+ }
+}
+
+static void
+ string_delete (s)
+string *s;
+{
+ if (s->b != NULL)
+ {
+ free (s->b);
+ s->b = s->e = s->p = NULL;
+ }
+}
+
+static void
+ string_init (s)
+string *s;
+{
+ s->b = s->p = s->e = NULL;
+}
+
+static void
+ string_clear (s)
+string *s;
+{
+ s->p = s->b;
+}
+
+static int
+ string_empty (s)
+string *s;
+{
+ return s->b == s->p;
+}
+
+static void
+ string_append (p, s)
+string *p;
+const char *s;
+{
+ int n;
+ if (s == NULL || *s == '\0')
+ return;
+ n = strlen (s);
+ string_need (p, n);
+ memcpy (p->p, s, n);
+ p->p += n;
+}
+
+static void
+ string_appends (p, s)
+string *p, *s;
+{
+ int n;
+ if (s->b == s->p)
+ return;
+ n = s->p - s->b;
+ string_need (p, n);
+ memcpy (p->p, s->b, n);
+ p->p += n;
+}
+
+static void
+ string_appendn (p, s, n)
+string *p;
+const char *s;
+int n;
+{
+ if (n == 0)
+ return;
+ string_need (p, n);
+ memcpy (p->p, s, n);
+ p->p += n;
+}
+
+static void
+ string_prepend (p, s)
+string *p;
+const char *s;
+{
+ if (s == NULL || *s == '\0')
+ return;
+ string_prependn (p, s, strlen (s));
+}
+
+#if 0
+static void
+ string_prepends (p, s)
+string *p, *s;
+{
+ if (s->b == s->p)
+ return;
+ string_prependn (p, s->b, s->p - s->b);
+}
+#endif
+
+static void
+ string_prependn (p, s, n)
+string *p;
+const char *s;
+int n;
+{
+ char *q;
+
+ if (n == 0)
+ return;
+ string_need (p, n);
+ for (q = p->p - 1; q >= p->b; q--)
+ q[n] = q[0];
+ memcpy (p->b, s, n);
+ p->p += n;
+}
+
+/* end of cplus-dem.c */
diff --git a/gnu/usr.bin/as/config/ho-ansi.h b/gnu/usr.bin/as/config/ho-ansi.h
new file mode 100644
index 0000000..2af0341
--- /dev/null
+++ b/gnu/usr.bin/as/config/ho-ansi.h
@@ -0,0 +1,29 @@
+/* ho-ansi.h Host-specific header file for generic ansi environments.
+ Copyright (C) 1987, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define M_ANSI 1
+
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+
+#define sys_nerr _sys_nerr
+#define sys_errlist _sys_errlist
+
+/* end of ho-ansi.h */
diff --git a/gnu/usr.bin/as/config/ho-decstation.h b/gnu/usr.bin/as/config/ho-decstation.h
new file mode 100644
index 0000000..1cab4d5
--- /dev/null
+++ b/gnu/usr.bin/as/config/ho-decstation.h
@@ -0,0 +1,29 @@
+/* ho-pmax.h Host-specific header file for decstation 3100.
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <string.h>
+
+extern char *malloc();
+extern int free();
+
+#if !defined(__GNUC__)
+#define know(x)
+#endif /* not gcc */
+
+/* end of ho-decstation.h */
diff --git a/gnu/usr.bin/as/config/ho-generic.h b/gnu/usr.bin/as/config/ho-generic.h
new file mode 100644
index 0000000..493cfc6
--- /dev/null
+++ b/gnu/usr.bin/as/config/ho-generic.h
@@ -0,0 +1,30 @@
+/* ho-generic.h Generic host-specific header file.
+ Copyright 1987, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* It is my intent that this become a file capable of config'ing and
+ compiling for nearly any host as aid for testing and porting.
+ xoxorich. */
+
+#define M_GENERIC 1
+
+#define HAVE_STRERROR
+
+extern int free();
+
+/* end of ho-generic.h */
diff --git a/gnu/usr.bin/as/config/ho-hpux.h b/gnu/usr.bin/as/config/ho-hpux.h
new file mode 100644
index 0000000..d5ff31a
--- /dev/null
+++ b/gnu/usr.bin/as/config/ho-hpux.h
@@ -0,0 +1,34 @@
+/* ho-hpux.h -- Header to compile the assembler under HP-UX
+ Copyright (C) 1988, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ho-sysv.h"
+
+/* This header file contains the #defines specific
+ to HPUX changes sent me by cph@zurich.ai.mit.edu */
+#ifndef hpux
+#define hpux
+#endif
+
+#ifdef setbuffer
+#undef setbuffer
+#endif /* setbuffer */
+
+#define setbuffer(stream, buf, size)
+
+/* end of ho-hpux.h */
diff --git a/gnu/usr.bin/as/config/ho-i386.h b/gnu/usr.bin/as/config/ho-i386.h
new file mode 100644
index 0000000..6941b7e
--- /dev/null
+++ b/gnu/usr.bin/as/config/ho-i386.h
@@ -0,0 +1,30 @@
+/* ho-i386.h i386 specific header file.
+ Copyright (C) 1987, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
+ * $Id: ho-i386.h,v 1.1 1993/10/02 20:58:36 pk Exp $
+ */
+
+
+#define HO_I386 1
+
+#define NO_STDARG
+
+#include "ho-sysv.h"
+
+/* end of ho-i386.h */
diff --git a/gnu/usr.bin/as/config/ho-i386aix.h b/gnu/usr.bin/as/config/ho-i386aix.h
new file mode 100644
index 0000000..d31b51a
--- /dev/null
+++ b/gnu/usr.bin/as/config/ho-i386aix.h
@@ -0,0 +1,24 @@
+/* ho-386aix.h AIX PS/2 i386 specific header file.
+ Copyright (C) 1987, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define HO_I386 1
+
+#include "ho-sysv.h"
+
+/* end of ho-i386aix.h */
diff --git a/gnu/usr.bin/as/config/ho-rs6000.h b/gnu/usr.bin/as/config/ho-rs6000.h
new file mode 100644
index 0000000..fe57e8e
--- /dev/null
+++ b/gnu/usr.bin/as/config/ho-rs6000.h
@@ -0,0 +1,22 @@
+/* ho-rs6000.h Rs6000 host-specific header file.
+ Copyright (C) 1987, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define M_RS6000 1
+
+/* end of ho-rs6000.h */
diff --git a/gnu/usr.bin/as/config/ho-sun3.h b/gnu/usr.bin/as/config/ho-sun3.h
new file mode 100644
index 0000000..0d68e6f
--- /dev/null
+++ b/gnu/usr.bin/as/config/ho-sun3.h
@@ -0,0 +1,3 @@
+#include <ho-sunos.h>
+
+/* end of ho-sun3.h */
diff --git a/gnu/usr.bin/as/config/ho-sun386.h b/gnu/usr.bin/as/config/ho-sun386.h
new file mode 100644
index 0000000..6c74df4
--- /dev/null
+++ b/gnu/usr.bin/as/config/ho-sun386.h
@@ -0,0 +1,5 @@
+#include <ho-sunos.h>
+
+extern int sprintf();
+
+/* end of ho-sun386.h */
diff --git a/gnu/usr.bin/as/config/ho-sun4.h b/gnu/usr.bin/as/config/ho-sun4.h
new file mode 100644
index 0000000..cf619e8
--- /dev/null
+++ b/gnu/usr.bin/as/config/ho-sun4.h
@@ -0,0 +1,3 @@
+#include <ho-sunos.h>
+
+/* end of ho-sun4.h */
diff --git a/gnu/usr.bin/as/config/ho-sunos.h b/gnu/usr.bin/as/config/ho-sunos.h
new file mode 100644
index 0000000..1193b1b
--- /dev/null
+++ b/gnu/usr.bin/as/config/ho-sunos.h
@@ -0,0 +1,81 @@
+/* This file is ho-sunos.h
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if __STDC__ != 1
+#define NO_STDARG
+#endif /* not __STDC__ */
+
+#if !defined(__GNUC__) && (__STDC__ != 1)
+#include <memory.h>
+#else
+extern int memset();
+#endif
+
+/* #include <sys/stdtypes.h> before <stddef.h> when compiling by GCC. */
+#include <sys/stdtypes.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <string.h>
+
+/* externs for system libraries. */
+
+/*extern int abort();*/
+/*extern int exit();*/
+extern char *malloc();
+extern char *realloc();
+extern char *strchr();
+extern char *strrchr();
+extern int _filbuf();
+extern int _flsbuf();
+extern int fclose();
+extern int fgetc();
+extern int fprintf();
+extern int fread();
+extern int free();
+extern int perror();
+extern int printf();
+extern int rewind();
+extern int setvbuf();
+extern int sscanf();
+extern int strcmp();
+extern int strlen();
+extern int strncmp();
+extern int time();
+extern int ungetc();
+extern int vfprintf();
+extern int vprintf();
+extern int vsprintf();
+extern long atol();
+
+#ifndef tolower
+extern int tolower();
+#endif /* tolower */
+
+#ifndef toupper
+extern int toupper();
+#endif /* toupper */
+
+/*
+ * Local Variables:
+ * fill-column: 80
+ * comment-column: 0
+ * End:
+ */
+
+/* end of ho-sunos.h */
diff --git a/gnu/usr.bin/as/config/ho-sysv.h b/gnu/usr.bin/as/config/ho-sysv.h
new file mode 100644
index 0000000..443fe3b
--- /dev/null
+++ b/gnu/usr.bin/as/config/ho-sysv.h
@@ -0,0 +1,27 @@
+/* ho-sysv.h System V specific header file.
+ Copyright (C) 1987, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define HO_USG
+
+#define setbuffer(stream, buf, size) setvbuf((stream), (buf), _IOLBF, (size))
+
+extern int free();
+extern char *malloc();
+
+/* end of ho-sysv.h */
diff --git a/gnu/usr.bin/as/config/ho-vax.h b/gnu/usr.bin/as/config/ho-vax.h
new file mode 100644
index 0000000..eee0553
--- /dev/null
+++ b/gnu/usr.bin/as/config/ho-vax.h
@@ -0,0 +1,27 @@
+/* ho-vax.h Intended for vax ultrix
+ Copyright (C) 1987, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if __STDC__ != 1
+#define NO_STDARG
+#endif /* not ansi */
+
+extern char *malloc();
+extern int free();
+
+/* end of ho-vax.h */
diff --git a/gnu/usr.bin/as/config/ho-vms.h b/gnu/usr.bin/as/config/ho-vms.h
new file mode 100644
index 0000000..4b6680e
--- /dev/null
+++ b/gnu/usr.bin/as/config/ho-vms.h
@@ -0,0 +1,30 @@
+/* ho-vax.h Intended for vax vms
+ Copyright (C) 1987, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define HO_VAX 1
+
+#include "ho-vax.h"
+
+/* We get better performance if we use the macros rather than the functions.*/
+#include <ctype.h>
+
+/* We need this to make sure that sys_nerr has the right Psect hack. */
+#include <perror.h>
+
+/* end of ho-vms.h */
diff --git a/gnu/usr.bin/as/config/i386-opcode.h b/gnu/usr.bin/as/config/i386-opcode.h
new file mode 100644
index 0000000..cace0c3
--- /dev/null
+++ b/gnu/usr.bin/as/config/i386-opcode.h
@@ -0,0 +1,806 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ *
+ * @(#)i386-opcode.h 6.3 (Berkeley) 5/8/91
+ */
+
+/* i386-opcode.h -- Intel 80386 opcode table
+ Copyright (C) 1989, Free Software Foundation.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+template i386_optab[] = {
+
+#define _ None
+/* move instructions */
+{ "mov", 2, 0xa0, _, DW|NoModrm, Disp32, Acc, 0 },
+{ "mov", 2, 0x88, _, DW|Modrm, Reg, Reg|Mem, 0 },
+{ "mov", 2, 0xb0, _, ShortFormW, Imm, Reg, 0 },
+{ "mov", 2, 0xc6, _, W|Modrm, Imm, Reg|Mem, 0 },
+{ "mov", 2, 0x8c, _, D|Modrm, SReg3|SReg2, Reg16|Mem16, 0 },
+/* move to/from control debug registers */
+{ "mov", 2, 0x0f20, _, D|Modrm, Control, Reg32, 0},
+{ "mov", 2, 0x0f21, _, D|Modrm, Debug, Reg32, 0},
+{ "mov", 2, 0x0f24, _, D|Modrm, Test, Reg32, 0},
+
+/* move with sign extend */
+/* "movsbl" & "movsbw" must not be unified into "movsb" to avoid
+ conflict with the "movs" string move instruction. Thus,
+ {"movsb", 2, 0x0fbe, _, ReverseRegRegmem|Modrm, Reg8|Mem, Reg16|Reg32, 0},
+ is not kosher; we must seperate the two instructions. */
+{"movsbl", 2, 0x0fbe, _, ReverseRegRegmem|Modrm, Reg8|Mem, Reg32, 0},
+{"movsbw", 2, 0x660fbe, _, ReverseRegRegmem|Modrm, Reg8|Mem, Reg16, 0},
+{"movswl", 2, 0x0fbf, _, ReverseRegRegmem|Modrm, Reg16|Mem, Reg32, 0},
+
+/* move with zero extend */
+{"movzb", 2, 0x0fb6, _, ReverseRegRegmem|Modrm, Reg8|Mem, Reg16|Reg32, 0},
+{"movzwl", 2, 0x0fb7, _, ReverseRegRegmem|Modrm, Reg16|Mem, Reg32, 0},
+
+/* push instructions */
+{"push", 1, 0x50, _, ShortForm, WordReg,0,0 },
+{"push", 1, 0xff, 0x6, Modrm, WordReg|WordMem, 0, 0 },
+{"push", 1, 0x6a, _, NoModrm, Imm8S, 0, 0},
+{"push", 1, 0x68, _, NoModrm, Imm16|Imm32, 0, 0},
+{"push", 1, 0x06, _, Seg2ShortForm, SReg2,0,0 },
+{"push", 1, 0x0fa0, _, Seg3ShortForm, SReg3,0,0 },
+/* push all */
+{"pusha", 0, 0x60, _, NoModrm, 0, 0, 0 },
+
+/* pop instructions */
+{"pop", 1, 0x58, _, ShortForm, WordReg,0,0 },
+{"pop", 1, 0x8f, 0x0, Modrm, WordReg|WordMem, 0, 0 },
+#define POP_SEG_SHORT 0x7
+{"pop", 1, 0x07, _, Seg2ShortForm, SReg2,0,0 },
+{"pop", 1, 0x0fa1, _, Seg3ShortForm, SReg3,0,0 },
+/* pop all */
+{"popa", 0, 0x61, _, NoModrm, 0, 0, 0 },
+
+/* xchg exchange instructions
+ xchg commutes: we allow both operand orders */
+{"xchg", 2, 0x90, _, ShortForm, WordReg, Acc, 0 },
+{"xchg", 2, 0x90, _, ShortForm, Acc, WordReg, 0 },
+{"xchg", 2, 0x86, _, W|Modrm, Reg, Reg|Mem, 0 },
+{"xchg", 2, 0x86, _, W|Modrm, Reg|Mem, Reg, 0 },
+
+/* in/out from ports */
+{"in", 2, 0xe4, _, W|NoModrm, Imm8, Acc, 0 },
+{"in", 2, 0xec, _, W|NoModrm, InOutPortReg, Acc, 0 },
+{"out", 2, 0xe6, _, W|NoModrm, Acc, Imm8, 0 },
+{"out", 2, 0xee, _, W|NoModrm, Acc, InOutPortReg, 0 },
+
+/* load effective address */
+{"lea", 2, 0x8d, _, Modrm, WordMem, WordReg, 0 },
+
+/* load segment registers from memory */
+{"lds", 2, 0xc5, _, Modrm, Mem, Reg32, 0},
+{"les", 2, 0xc4, _, Modrm, Mem, Reg32, 0},
+{"lfs", 2, 0x0fb4, _, Modrm, Mem, Reg32, 0},
+{"lgs", 2, 0x0fb5, _, Modrm, Mem, Reg32, 0},
+{"lss", 2, 0x0fb2, _, Modrm, Mem, Reg32, 0},
+
+/* flags register instructions */
+{"clc", 0, 0xf8, _, NoModrm, 0, 0, 0},
+{"cld", 0, 0xfc, _, NoModrm, 0, 0, 0},
+{"cli", 0, 0xfa, _, NoModrm, 0, 0, 0},
+{"clts", 0, 0x0f06, _, NoModrm, 0, 0, 0},
+{"cmc", 0, 0xf5, _, NoModrm, 0, 0, 0},
+{"lahf", 0, 0x9f, _, NoModrm, 0, 0, 0},
+{"sahf", 0, 0x9e, _, NoModrm, 0, 0, 0},
+{"pushf", 0, 0x9c, _, NoModrm, 0, 0, 0},
+{"popf", 0, 0x9d, _, NoModrm, 0, 0, 0},
+{"stc", 0, 0xf9, _, NoModrm, 0, 0, 0},
+{"std", 0, 0xfd, _, NoModrm, 0, 0, 0},
+{"sti", 0, 0xfb, _, NoModrm, 0, 0, 0},
+
+{"add", 2, 0x0, _, DW|Modrm, Reg, Reg|Mem, 0},
+{"add", 2, 0x83, 0, Modrm, Imm8S, WordReg|WordMem, 0},
+{"add", 2, 0x4, _, W|NoModrm, Imm, Acc, 0},
+{"add", 2, 0x80, 0, W|Modrm, Imm, Reg|Mem, 0},
+
+{"inc", 1, 0x40, _, ShortForm, WordReg, 0, 0},
+{"inc", 1, 0xfe, 0, W|Modrm, Reg|Mem, 0, 0},
+
+{"sub", 2, 0x28, _, DW|Modrm, Reg, Reg|Mem, 0},
+{"sub", 2, 0x83, 5, Modrm, Imm8S, WordReg|WordMem, 0},
+{"sub", 2, 0x2c, _, W|NoModrm, Imm, Acc, 0},
+{"sub", 2, 0x80, 5, W|Modrm, Imm, Reg|Mem, 0},
+
+{"dec", 1, 0x48, _, ShortForm, WordReg, 0, 0},
+{"dec", 1, 0xfe, 1, W|Modrm, Reg|Mem, 0, 0},
+
+{"sbb", 2, 0x18, _, DW|Modrm, Reg, Reg|Mem, 0},
+{"sbb", 2, 0x83, 3, Modrm, Imm8S, WordReg|WordMem, 0},
+{"sbb", 2, 0x1c, _, W|NoModrm, Imm, Acc, 0},
+{"sbb", 2, 0x80, 3, W|Modrm, Imm, Reg|Mem, 0},
+
+{"cmp", 2, 0x38, _, DW|Modrm, Reg, Reg|Mem, 0},
+{"cmp", 2, 0x83, 7, Modrm, Imm8S, WordReg|WordMem, 0},
+{"cmp", 2, 0x3c, _, W|NoModrm, Imm, Acc, 0},
+{"cmp", 2, 0x80, 7, W|Modrm, Imm, Reg|Mem, 0},
+
+{"test", 2, 0x84, _, W|Modrm, Reg|Mem, Reg, 0},
+{"test", 2, 0x84, _, W|Modrm, Reg, Reg|Mem, 0},
+{"test", 2, 0xa8, _, W|NoModrm, Imm, Acc, 0},
+{"test", 2, 0xf6, 0, W|Modrm, Imm, Reg|Mem, 0},
+
+{"and", 2, 0x20, _, DW|Modrm, Reg, Reg|Mem, 0},
+{"and", 2, 0x83, 4, Modrm, Imm8S, WordReg|WordMem, 0},
+{"and", 2, 0x24, _, W|NoModrm, Imm, Acc, 0},
+{"and", 2, 0x80, 4, W|Modrm, Imm, Reg|Mem, 0},
+
+{"or", 2, 0x08, _, DW|Modrm, Reg, Reg|Mem, 0},
+{"or", 2, 0x83, 1, Modrm, Imm8S, WordReg|WordMem, 0},
+{"or", 2, 0x0c, _, W|NoModrm, Imm, Acc, 0},
+{"or", 2, 0x80, 1, W|Modrm, Imm, Reg|Mem, 0},
+
+{"xor", 2, 0x30, _, DW|Modrm, Reg, Reg|Mem, 0},
+{"xor", 2, 0x83, 6, Modrm, Imm8S, WordReg|WordMem, 0},
+{"xor", 2, 0x34, _, W|NoModrm, Imm, Acc, 0},
+{"xor", 2, 0x80, 6, W|Modrm, Imm, Reg|Mem, 0},
+
+{"adc", 2, 0x10, _, DW|Modrm, Reg, Reg|Mem, 0},
+{"adc", 2, 0x83, 2, Modrm, Imm8S, WordReg|WordMem, 0},
+{"adc", 2, 0x14, _, W|NoModrm, Imm, Acc, 0},
+{"adc", 2, 0x80, 2, W|Modrm, Imm, Reg|Mem, 0},
+
+{"neg", 1, 0xf6, 3, W|Modrm, Reg|Mem, 0, 0},
+{"not", 1, 0xf6, 2, W|Modrm, Reg|Mem, 0, 0},
+
+{"aaa", 0, 0x37, _, NoModrm, 0, 0, 0},
+{"aas", 0, 0x3f, _, NoModrm, 0, 0, 0},
+{"daa", 0, 0x27, _, NoModrm, 0, 0, 0},
+{"das", 0, 0x2f, _, NoModrm, 0, 0, 0},
+{"aad", 0, 0xd50a, _, NoModrm, 0, 0, 0},
+{"aam", 0, 0xd40a, _, NoModrm, 0, 0, 0},
+
+/* conversion insns */
+/* conversion: intel naming */
+{"cbw", 0, 0x6698, _, NoModrm, 0, 0, 0},
+{"cwd", 0, 0x6699, _, NoModrm, 0, 0, 0},
+{"cwde", 0, 0x98, _, NoModrm, 0, 0, 0},
+{"cdq", 0, 0x99, _, NoModrm, 0, 0, 0},
+/* att naming */
+{"cbtw", 0, 0x6698, _, NoModrm, 0, 0, 0},
+{"cwtl", 0, 0x98, _, NoModrm, 0, 0, 0},
+{"cwtd", 0, 0x6699, _, NoModrm, 0, 0, 0},
+{"cltd", 0, 0x99, _, NoModrm, 0, 0, 0},
+
+/* Warning! the mul/imul (opcode 0xf6) must only have 1 operand! They are
+ expanding 64-bit multiplies, and *cannot* be selected to accomplish
+ 'imul %ebx, %eax' (opcode 0x0faf must be used in this case)
+ These multiplies can only be selected with single opearnd forms. */
+{"mul", 1, 0xf6, 4, W|Modrm, Reg|Mem, 0, 0},
+{"imul", 1, 0xf6, 5, W|Modrm, Reg|Mem, 0, 0},
+
+
+
+
+/* imulKludge here is needed to reverse the i.rm.reg & i.rm.regmem fields.
+ These instructions are exceptions: 'imul $2, %eax, %ecx' would put
+ '%eax' in the reg field and '%ecx' in the regmem field if we did not
+ switch them. */
+{"imul", 2, 0x0faf, _, Modrm|ReverseRegRegmem, WordReg|Mem, WordReg, 0},
+{"imul", 3, 0x6b, _, Modrm|ReverseRegRegmem, Imm8S, WordReg|Mem, WordReg},
+{"imul", 3, 0x69, _, Modrm|ReverseRegRegmem, Imm16|Imm32, WordReg|Mem, WordReg},
+/*
+ imul with 2 operands mimicks imul with 3 by puting register both
+ in i.rm.reg & i.rm.regmem fields
+*/
+{"imul", 2, 0x6b, _, Modrm|imulKludge, Imm8S, WordReg, 0},
+{"imul", 2, 0x69, _, Modrm|imulKludge, Imm16|Imm32, WordReg, 0},
+{"div", 1, 0xf6, 6, W|Modrm, Reg|Mem, 0, 0},
+{"div", 2, 0xf6, 6, W|Modrm, Reg|Mem, Acc, 0},
+{"idiv", 1, 0xf6, 7, W|Modrm, Reg|Mem, 0, 0},
+{"idiv", 2, 0xf6, 7, W|Modrm, Reg|Mem, Acc, 0},
+
+{"rol", 2, 0xd0, 0, W|Modrm, Imm1, Reg|Mem, 0},
+{"rol", 2, 0xc0, 0, W|Modrm, Imm8, Reg|Mem, 0},
+{"rol", 2, 0xd2, 0, W|Modrm, ShiftCount, Reg|Mem, 0},
+{"rol", 1, 0xd0, 0, W|Modrm, Reg|Mem, 0, 0},
+
+{"ror", 2, 0xd0, 1, W|Modrm, Imm1, Reg|Mem, 0},
+{"ror", 2, 0xc0, 1, W|Modrm, Imm8, Reg|Mem, 0},
+{"ror", 2, 0xd2, 1, W|Modrm, ShiftCount, Reg|Mem, 0},
+{"ror", 1, 0xd0, 1, W|Modrm, Reg|Mem, 0, 0},
+
+{"rcl", 2, 0xd0, 2, W|Modrm, Imm1, Reg|Mem, 0},
+{"rcl", 2, 0xc0, 2, W|Modrm, Imm8, Reg|Mem, 0},
+{"rcl", 2, 0xd2, 2, W|Modrm, ShiftCount, Reg|Mem, 0},
+{"rcl", 1, 0xd0, 2, W|Modrm, Reg|Mem, 0, 0},
+
+{"rcr", 2, 0xd0, 3, W|Modrm, Imm1, Reg|Mem, 0},
+{"rcr", 2, 0xc0, 3, W|Modrm, Imm8, Reg|Mem, 0},
+{"rcr", 2, 0xd2, 3, W|Modrm, ShiftCount, Reg|Mem, 0},
+{"rcr", 1, 0xd0, 3, W|Modrm, Reg|Mem, 0, 0},
+
+{"sal", 2, 0xd0, 4, W|Modrm, Imm1, Reg|Mem, 0},
+{"sal", 2, 0xc0, 4, W|Modrm, Imm8, Reg|Mem, 0},
+{"sal", 2, 0xd2, 4, W|Modrm, ShiftCount, Reg|Mem, 0},
+{"sal", 1, 0xd0, 4, W|Modrm, Reg|Mem, 0, 0},
+{"shl", 2, 0xd0, 4, W|Modrm, Imm1, Reg|Mem, 0},
+{"shl", 2, 0xc0, 4, W|Modrm, Imm8, Reg|Mem, 0},
+{"shl", 2, 0xd2, 4, W|Modrm, ShiftCount, Reg|Mem, 0},
+{"shl", 1, 0xd0, 4, W|Modrm, Reg|Mem, 0, 0},
+
+{"shld", 3, 0x0fa4, _, Modrm, Imm8, WordReg, WordReg|Mem},
+{"shld", 3, 0x0fa5, _, Modrm, ShiftCount, WordReg, WordReg|Mem},
+
+{"shr", 2, 0xd0, 5, W|Modrm, Imm1, Reg|Mem, 0},
+{"shr", 2, 0xc0, 5, W|Modrm, Imm8, Reg|Mem, 0},
+{"shr", 2, 0xd2, 5, W|Modrm, ShiftCount, Reg|Mem, 0},
+{"shr", 1, 0xd0, 5, W|Modrm, Reg|Mem, 0, 0},
+
+{"shrd", 3, 0x0fac, _, Modrm, Imm8, WordReg, WordReg|Mem},
+{"shrd", 3, 0x0fad, _, Modrm, ShiftCount, WordReg, WordReg|Mem},
+
+{"sar", 2, 0xd0, 7, W|Modrm, Imm1, Reg|Mem, 0},
+{"sar", 2, 0xc0, 7, W|Modrm, Imm8, Reg|Mem, 0},
+{"sar", 2, 0xd2, 7, W|Modrm, ShiftCount, Reg|Mem, 0},
+{"sar", 1, 0xd0, 7, W|Modrm, Reg|Mem, 0, 0},
+
+/* control transfer instructions */
+#define CALL_PC_RELATIVE 0xe8
+{"call", 1, 0xe8, _, JumpDword, Disp32, 0, 0},
+{"call", 1, 0xff, 2, Modrm, Reg|Mem|JumpAbsolute, 0, 0},
+#define CALL_FAR_IMMEDIATE 0x9a
+{"lcall", 2, 0x9a, _, JumpInterSegment, Imm16, Imm32, 0},
+{"lcall", 1, 0xff, 3, Modrm, Mem, 0, 0},
+
+#define JUMP_PC_RELATIVE 0xeb
+{"jmp", 1, 0xeb, _, Jump, Disp, 0, 0},
+{"jmp", 1, 0xff, 4, Modrm, Reg32|Mem|JumpAbsolute, 0, 0},
+#define JUMP_FAR_IMMEDIATE 0xea
+{"ljmp", 2, 0xea, _, JumpInterSegment, Imm16, Imm32, 0},
+{"ljmp", 1, 0xff, 5, Modrm, Mem, 0, 0},
+
+{"ret", 0, 0xc3, _, NoModrm, 0, 0, 0},
+{"ret", 1, 0xc2, _, NoModrm, Imm16, 0, 0},
+{"lret", 0, 0xcb, _, NoModrm, 0, 0, 0},
+{"lret", 1, 0xca, _, NoModrm, Imm16, 0, 0},
+{"enter", 2, 0xc8, _, NoModrm, Imm16, Imm8, 0},
+{"leave", 0, 0xc9, _, NoModrm, 0, 0, 0},
+
+/* conditional jumps */
+{"jo", 1, 0x70, _, Jump, Disp, 0, 0},
+
+{"jno", 1, 0x71, _, Jump, Disp, 0, 0},
+
+{"jb", 1, 0x72, _, Jump, Disp, 0, 0},
+{"jc", 1, 0x72, _, Jump, Disp, 0, 0},
+{"jnae", 1, 0x72, _, Jump, Disp, 0, 0},
+
+{"jnb", 1, 0x73, _, Jump, Disp, 0, 0},
+{"jnc", 1, 0x73, _, Jump, Disp, 0, 0},
+{"jae", 1, 0x73, _, Jump, Disp, 0, 0},
+
+{"je", 1, 0x74, _, Jump, Disp, 0, 0},
+{"jz", 1, 0x74, _, Jump, Disp, 0, 0},
+
+{"jne", 1, 0x75, _, Jump, Disp, 0, 0},
+{"jnz", 1, 0x75, _, Jump, Disp, 0, 0},
+
+{"jbe", 1, 0x76, _, Jump, Disp, 0, 0},
+{"jna", 1, 0x76, _, Jump, Disp, 0, 0},
+
+{"jnbe", 1, 0x77, _, Jump, Disp, 0, 0},
+{"ja", 1, 0x77, _, Jump, Disp, 0, 0},
+
+{"js", 1, 0x78, _, Jump, Disp, 0, 0},
+
+{"jns", 1, 0x79, _, Jump, Disp, 0, 0},
+
+{"jp", 1, 0x7a, _, Jump, Disp, 0, 0},
+{"jpe", 1, 0x7a, _, Jump, Disp, 0, 0},
+
+{"jnp", 1, 0x7b, _, Jump, Disp, 0, 0},
+{"jpo", 1, 0x7b, _, Jump, Disp, 0, 0},
+
+{"jl", 1, 0x7c, _, Jump, Disp, 0, 0},
+{"jnge", 1, 0x7c, _, Jump, Disp, 0, 0},
+
+{"jnl", 1, 0x7d, _, Jump, Disp, 0, 0},
+{"jge", 1, 0x7d, _, Jump, Disp, 0, 0},
+
+{"jle", 1, 0x7e, _, Jump, Disp, 0, 0},
+{"jng", 1, 0x7e, _, Jump, Disp, 0, 0},
+
+{"jnle", 1, 0x7f, _, Jump, Disp, 0, 0},
+{"jg", 1, 0x7f, _, Jump, Disp, 0, 0},
+
+/* these turn into pseudo operations when disp is larger than 8 bits */
+#define IS_JUMP_ON_CX_ZERO(o) \
+ (o == 0x67e3)
+#define IS_JUMP_ON_ECX_ZERO(o) \
+ (o == 0xe3)
+
+{"jcxz", 1, 0x67e3, _, JumpByte, Disp, 0, 0},
+{"jecxz", 1, 0xe3, _, JumpByte, Disp, 0, 0},
+
+#define IS_LOOP_ECX_TIMES(o) \
+ (o == 0xe2 || o == 0xe1 || o == 0xe0)
+
+{"loop", 1, 0xe2, _, JumpByte, Disp, 0, 0},
+
+{"loopz", 1, 0xe1, _, JumpByte, Disp, 0, 0},
+{"loope", 1, 0xe1, _, JumpByte, Disp, 0, 0},
+
+{"loopnz", 1, 0xe0, _, JumpByte, Disp, 0, 0},
+{"loopne", 1, 0xe0, _, JumpByte, Disp, 0, 0},
+
+/* set byte on flag instructions */
+{"seto", 1, 0x0f90, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setno", 1, 0x0f91, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setb", 1, 0x0f92, 0, Modrm, Reg8|Mem, 0, 0},
+{"setnae", 1, 0x0f92, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setnb", 1, 0x0f93, 0, Modrm, Reg8|Mem, 0, 0},
+{"setae", 1, 0x0f93, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"sete", 1, 0x0f94, 0, Modrm, Reg8|Mem, 0, 0},
+{"setz", 1, 0x0f94, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setne", 1, 0x0f95, 0, Modrm, Reg8|Mem, 0, 0},
+{"setnz", 1, 0x0f95, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setbe", 1, 0x0f96, 0, Modrm, Reg8|Mem, 0, 0},
+{"setna", 1, 0x0f96, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setnbe", 1, 0x0f97, 0, Modrm, Reg8|Mem, 0, 0},
+{"seta", 1, 0x0f97, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"sets", 1, 0x0f98, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setns", 1, 0x0f99, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setp", 1, 0x0f9a, 0, Modrm, Reg8|Mem, 0, 0},
+{"setpe", 1, 0x0f9a, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setnp", 1, 0x0f9b, 0, Modrm, Reg8|Mem, 0, 0},
+{"setpo", 1, 0x0f9b, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setl", 1, 0x0f9c, 0, Modrm, Reg8|Mem, 0, 0},
+{"setnge", 1, 0x0f9c, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setnl", 1, 0x0f9d, 0, Modrm, Reg8|Mem, 0, 0},
+{"setge", 1, 0x0f9d, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setle", 1, 0x0f9e, 0, Modrm, Reg8|Mem, 0, 0},
+{"setng", 1, 0x0f9e, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setnle", 1, 0x0f9f, 0, Modrm, Reg8|Mem, 0, 0},
+{"setg", 1, 0x0f9f, 0, Modrm, Reg8|Mem, 0, 0},
+
+#define IS_STRING_INSTRUCTION(o) \
+ ((o) == 0xa6 || (o) == 0x6c || (o) == 0x6e || (o) == 0x6e || \
+ (o) == 0xac || (o) == 0xa4 || (o) == 0xae || (o) == 0xaa || \
+ (o) == 0xd7)
+
+/* string manipulation */
+{"cmps", 0, 0xa6, _, W|NoModrm, 0, 0, 0},
+{"ins", 0, 0x6c, _, W|NoModrm, 0, 0, 0},
+{"outs", 0, 0x6e, _, W|NoModrm, 0, 0, 0},
+{"lods", 0, 0xac, _, W|NoModrm, 0, 0, 0},
+{"movs", 0, 0xa4, _, W|NoModrm, 0, 0, 0},
+{"scas", 0, 0xae, _, W|NoModrm, 0, 0, 0},
+{"stos", 0, 0xaa, _, W|NoModrm, 0, 0, 0},
+{"xlat", 0, 0xd7, _, NoModrm, 0, 0, 0},
+
+/* bit manipulation */
+{"bsf", 2, 0x0fbc, _, Modrm|ReverseRegRegmem, Reg|Mem, Reg, 0},
+{"bsr", 2, 0x0fbd, _, Modrm|ReverseRegRegmem, Reg|Mem, Reg, 0},
+{"bt", 2, 0x0fa3, _, Modrm, Reg, Reg|Mem, 0},
+{"bt", 2, 0x0fba, 4, Modrm, Imm8, Reg|Mem, 0},
+{"btc", 2, 0x0fbb, _, Modrm, Reg, Reg|Mem, 0},
+{"btc", 2, 0x0fba, 7, Modrm, Imm8, Reg|Mem, 0},
+{"btr", 2, 0x0fb3, _, Modrm, Reg, Reg|Mem, 0},
+{"btr", 2, 0x0fba, 6, Modrm, Imm8, Reg|Mem, 0},
+{"bts", 2, 0x0fab, _, Modrm, Reg, Reg|Mem, 0},
+{"bts", 2, 0x0fba, 5, Modrm, Imm8, Reg|Mem, 0},
+
+/* interrupts & op. sys insns */
+/* See i386.c for conversion of 'int $3' into the special int 3 insn. */
+#define INT_OPCODE 0xcd
+#define INT3_OPCODE 0xcc
+{"int", 1, 0xcd, _, NoModrm, Imm8, 0, 0},
+{"int3", 0, 0xcc, _, NoModrm, 0, 0, 0},
+{"into", 0, 0xce, _, NoModrm, 0, 0, 0},
+{"iret", 0, 0xcf, _, NoModrm, 0, 0, 0},
+
+{"boundl", 2, 0x62, _, Modrm, Reg32, Mem, 0},
+{"boundw", 2, 0x6662, _, Modrm, Reg16, Mem, 0},
+
+{"hlt", 0, 0xf4, _, NoModrm, 0, 0, 0},
+{"wait", 0, 0x9b, _, NoModrm, 0, 0, 0},
+/* nop is actually 'xchgl %eax, %eax' */
+{"nop", 0, 0x90, _, NoModrm, 0, 0, 0},
+
+/* protection control */
+{"arpl", 2, 0x63, _, Modrm, Reg16, Reg16|Mem, 0},
+{"lar", 2, 0x0f02, _, Modrm|ReverseRegRegmem, WordReg|Mem, WordReg, 0},
+{"lgdt", 1, 0x0f01, 2, Modrm, Mem, 0, 0},
+{"lidt", 1, 0x0f01, 3, Modrm, Mem, 0, 0},
+{"lldt", 1, 0x0f00, 2, Modrm, WordReg|Mem, 0, 0},
+{"lmsw", 1, 0x0f01, 6, Modrm, WordReg|Mem, 0, 0},
+{"lsl", 2, 0x0f03, _, Modrm|ReverseRegRegmem, WordReg|Mem, WordReg, 0},
+{"ltr", 1, 0x0f00, 3, Modrm, WordReg|Mem, 0, 0},
+
+{"sgdt", 1, 0x0f01, 0, Modrm, Mem, 0, 0},
+{"sidt", 1, 0x0f01, 1, Modrm, Mem, 0, 0},
+{"sldt", 1, 0x0f00, 0, Modrm, WordReg|Mem, 0, 0},
+{"smsw", 1, 0x0f01, 4, Modrm, WordReg|Mem, 0, 0},
+{"str", 1, 0x0f00, 1, Modrm, Reg16|Mem, 0, 0},
+
+{"verr", 1, 0x0f00, 4, Modrm, WordReg|Mem, 0, 0},
+{"verw", 1, 0x0f00, 5, Modrm, WordReg|Mem, 0, 0},
+
+/* floating point instructions */
+
+/* load */
+{"fld", 1, 0xd9c0, _, ShortForm, FloatReg, 0, 0}, /* register */
+{"flds", 1, 0xd9, 0, Modrm, Mem, 0, 0}, /* %st0 <-- mem float */
+{"fildl", 1, 0xdb, 0, Modrm, Mem, 0, 0}, /* %st0 <-- mem word */
+{"fldl", 1, 0xdd, 0, Modrm, Mem, 0, 0}, /* %st0 <-- mem double */
+{"fldl", 1, 0xd9c0, _, ShortForm, FloatReg, 0, 0}, /* register */
+{"filds", 1, 0xdf, 0, Modrm, Mem, 0, 0}, /* %st0 <-- mem dword */
+{"fildq", 1, 0xdf, 5, Modrm, Mem, 0, 0}, /* %st0 <-- mem qword */
+{"fldt", 1, 0xdb, 5, Modrm, Mem, 0, 0}, /* %st0 <-- mem efloat */
+{"fbld", 1, 0xdf, 4, Modrm, Mem, 0, 0}, /* %st0 <-- mem bcd */
+
+/* store (no pop) */
+{"fst", 1, 0xddd0, _, ShortForm, FloatReg, 0, 0}, /* register */
+{"fsts", 1, 0xd9, 2, Modrm, Mem, 0, 0}, /* %st0 --> mem float */
+{"fistl", 1, 0xdb, 2, Modrm, Mem, 0, 0}, /* %st0 --> mem dword */
+{"fstl", 1, 0xdd, 2, Modrm, Mem, 0, 0}, /* %st0 --> mem double */
+{"fstl", 1, 0xddd0, _, ShortForm, FloatReg, 0, 0}, /* register */
+{"fists", 1, 0xdf, 2, Modrm, Mem, 0, 0}, /* %st0 --> mem word */
+
+/* store (with pop) */
+{"fstp", 1, 0xddd8, _, ShortForm, FloatReg, 0, 0}, /* register */
+{"fstps", 1, 0xd9, 3, Modrm, Mem, 0, 0}, /* %st0 --> mem float */
+{"fistpl", 1, 0xdb, 3, Modrm, Mem, 0, 0}, /* %st0 --> mem word */
+{"fstpl", 1, 0xdd, 3, Modrm, Mem, 0, 0}, /* %st0 --> mem double */
+{"fstpl", 1, 0xddd8, _, ShortForm, FloatReg, 0, 0}, /* register */
+{"fistps", 1, 0xdf, 3, Modrm, Mem, 0, 0}, /* %st0 --> mem dword */
+{"fistpq", 1, 0xdf, 7, Modrm, Mem, 0, 0}, /* %st0 --> mem qword */
+{"fstpt", 1, 0xdb, 7, Modrm, Mem, 0, 0}, /* %st0 --> mem efloat */
+{"fbstp", 1, 0xdf, 6, Modrm, Mem, 0, 0}, /* %st0 --> mem bcd */
+
+/* exchange %st<n> with %st0 */
+{"fxch", 1, 0xd9c8, _, ShortForm, FloatReg, 0, 0},
+
+/* comparison (without pop) */
+{"fcom", 1, 0xd8d0, _, ShortForm, FloatReg, 0, 0},
+{"fcoms", 1, 0xd8, 2, Modrm, Mem, 0, 0}, /* compare %st0, mem float */
+{"ficoml", 1, 0xda, 2, Modrm, Mem, 0, 0}, /* compare %st0, mem word */
+{"fcoml", 1, 0xdc, 2, Modrm, Mem, 0, 0}, /* compare %st0, mem double */
+{"fcoml", 1, 0xd8d0, _, ShortForm, FloatReg, 0, 0},
+{"ficoms", 1, 0xde, 2, Modrm, Mem, 0, 0}, /* compare %st0, mem dword */
+
+/* comparison (with pop) */
+{"fcomp", 1, 0xd8d8, _, ShortForm, FloatReg, 0, 0},
+{"fcomps", 1, 0xd8, 3, Modrm, Mem, 0, 0}, /* compare %st0, mem float */
+{"ficompl", 1, 0xda, 3, Modrm, Mem, 0, 0}, /* compare %st0, mem word */
+{"fcompl", 1, 0xdc, 3, Modrm, Mem, 0, 0}, /* compare %st0, mem double */
+{"fcompl", 1, 0xd8d8, _, ShortForm, FloatReg, 0, 0},
+{"ficomps", 1, 0xde, 3, Modrm, Mem, 0, 0}, /* compare %st0, mem dword */
+{"fcompp", 0, 0xded9, _, NoModrm, 0, 0, 0}, /* compare %st0, %st1 & pop twice */
+
+/* unordered comparison (with pop) */
+{"fucom", 1, 0xdde0, _, ShortForm, FloatReg, 0, 0},
+{"fucomp", 1, 0xdde8, _, ShortForm, FloatReg, 0, 0},
+{"fucompp", 0, 0xdae9, _, NoModrm, 0, 0, 0}, /* ucompare %st0, %st1 & pop twice */
+
+{"ftst", 0, 0xd9e4, _, NoModrm, 0, 0, 0}, /* test %st0 */
+{"fxam", 0, 0xd9e5, _, NoModrm, 0, 0, 0}, /* examine %st0 */
+
+/* load constants into %st0 */
+{"fld1", 0, 0xd9e8, _, NoModrm, 0, 0, 0}, /* %st0 <-- 1.0 */
+{"fldl2t", 0, 0xd9e9, _, NoModrm, 0, 0, 0}, /* %st0 <-- log2(10) */
+{"fldl2e", 0, 0xd9ea, _, NoModrm, 0, 0, 0}, /* %st0 <-- log2(e) */
+{"fldpi", 0, 0xd9eb, _, NoModrm, 0, 0, 0}, /* %st0 <-- pi */
+{"fldlg2", 0, 0xd9ec, _, NoModrm, 0, 0, 0}, /* %st0 <-- log10(2) */
+{"fldln2", 0, 0xd9ed, _, NoModrm, 0, 0, 0}, /* %st0 <-- ln(2) */
+{"fldz", 0, 0xd9ee, _, NoModrm, 0, 0, 0}, /* %st0 <-- 0.0 */
+
+/* arithmetic */
+
+/* add */
+{"fadd", 1, 0xd8c0, _, ShortForm, FloatReg, 0, 0},
+{"fadd", 2, 0xd8c0, _, ShortForm|FloatD, FloatReg, FloatAcc, 0},
+{"fadd", 0, 0xdcc1, _, NoModrm, 0, 0, 0}, /* alias for fadd %st, %st(1) */
+{"faddp", 1, 0xdac0, _, ShortForm, FloatReg, 0, 0},
+{"faddp", 2, 0xdac0, _, ShortForm|FloatD, FloatReg, FloatAcc, 0},
+{"faddp", 0, 0xdec1, _, NoModrm, 0, 0, 0}, /* alias for faddp %st, %st(1) */
+{"fadds", 1, 0xd8, 0, Modrm, Mem, 0, 0},
+{"fiaddl", 1, 0xda, 0, Modrm, Mem, 0, 0},
+{"faddl", 1, 0xdc, 0, Modrm, Mem, 0, 0},
+{"fiadds", 1, 0xde, 0, Modrm, Mem, 0, 0},
+
+/* sub */
+/* Note: intel has decided that certain of these operations are reversed
+ in assembler syntax. */
+{"fsub", 1, 0xd8e0, _, ShortForm, FloatReg, 0, 0},
+{"fsub", 2, 0xd8e0, _, ShortForm, FloatReg, FloatAcc, 0},
+#ifdef NON_BROKEN_OPCODES
+{"fsub", 2, 0xdce8, _, ShortForm, FloatAcc, FloatReg, 0},
+#else
+{"fsub", 2, 0xdce0, _, ShortForm, FloatAcc, FloatReg, 0},
+#endif
+{"fsub", 0, 0xdce1, _, NoModrm, 0, 0, 0},
+{"fsubp", 1, 0xdae0, _, ShortForm, FloatReg, 0, 0},
+{"fsubp", 2, 0xdae0, _, ShortForm, FloatReg, FloatAcc, 0},
+#ifdef NON_BROKEN_OPCODES
+{"fsubp", 2, 0xdee8, _, ShortForm, FloatAcc, FloatReg, 0},
+#else
+{"fsubp", 2, 0xdee0, _, ShortForm, FloatAcc, FloatReg, 0},
+#endif
+{"fsubp", 0, 0xdee1, _, NoModrm, 0, 0, 0},
+{"fsubs", 1, 0xd8, 4, Modrm, Mem, 0, 0},
+{"fisubl", 1, 0xda, 4, Modrm, Mem, 0, 0},
+{"fsubl", 1, 0xdc, 4, Modrm, Mem, 0, 0},
+{"fisubs", 1, 0xde, 4, Modrm, Mem, 0, 0},
+
+/* sub reverse */
+{"fsubr", 1, 0xd8e8, _, ShortForm, FloatReg, 0, 0},
+{"fsubr", 2, 0xd8e8, _, ShortForm, FloatReg, FloatAcc, 0},
+#ifdef NON_BROKEN_OPCODES
+{"fsubr", 2, 0xdce0, _, ShortForm, FloatAcc, FloatReg, 0},
+#else
+{"fsubr", 2, 0xdce8, _, ShortForm, FloatAcc, FloatReg, 0},
+#endif
+{"fsubr", 0, 0xdce9, _, NoModrm, 0, 0, 0},
+{"fsubrp", 1, 0xdae8, _, ShortForm, FloatReg, 0, 0},
+{"fsubrp", 2, 0xdae8, _, ShortForm, FloatReg, FloatAcc, 0},
+#ifdef NON_BROKEN_OPCODES
+{"fsubrp", 2, 0xdee0, _, ShortForm, FloatAcc, FloatReg, 0},
+#else
+{"fsubrp", 2, 0xdee8, _, ShortForm, FloatAcc, FloatReg, 0},
+#endif
+{"fsubrp", 0, 0xdee9, _, NoModrm, 0, 0, 0},
+{"fsubrs", 1, 0xd8, 5, Modrm, Mem, 0, 0},
+{"fisubrl", 1, 0xda, 5, Modrm, Mem, 0, 0},
+{"fsubrl", 1, 0xdc, 5, Modrm, Mem, 0, 0},
+{"fisubrs", 1, 0xde, 5, Modrm, Mem, 0, 0},
+
+/* mul */
+{"fmul", 1, 0xd8c8, _, ShortForm, FloatReg, 0, 0},
+{"fmul", 2, 0xd8c8, _, ShortForm|FloatD, FloatReg, FloatAcc, 0},
+{"fmul", 0, 0xdcc9, _, NoModrm, 0, 0, 0},
+{"fmulp", 1, 0xdac8, _, ShortForm, FloatReg, 0, 0},
+{"fmulp", 2, 0xdac8, _, ShortForm|FloatD, FloatReg, FloatAcc, 0},
+{"fmulp", 0, 0xdec9, _, NoModrm, 0, 0, 0},
+{"fmuls", 1, 0xd8, 1, Modrm, Mem, 0, 0},
+{"fimull", 1, 0xda, 1, Modrm, Mem, 0, 0},
+{"fmull", 1, 0xdc, 1, Modrm, Mem, 0, 0},
+{"fimuls", 1, 0xde, 1, Modrm, Mem, 0, 0},
+
+/* div */
+/* Note: intel has decided that certain of these operations are reversed
+ in assembler syntax. */
+{"fdiv", 1, 0xd8f0, _, ShortForm, FloatReg, 0, 0},
+{"fdiv", 2, 0xd8f0, _, ShortForm, FloatReg, FloatAcc, 0},
+#ifdef NON_BROKEN_OPCODES
+{"fdiv", 2, 0xdcf8, _, ShortForm, FloatAcc, FloatReg, 0},
+#else
+{"fdiv", 2, 0xdcf0, _, ShortForm, FloatAcc, FloatReg, 0},
+#endif
+{"fdiv", 0, 0xdcf1, _, NoModrm, 0, 0, 0},
+{"fdivp", 1, 0xdaf0, _, ShortForm, FloatReg, 0, 0},
+{"fdivp", 2, 0xdaf0, _, ShortForm, FloatReg, FloatAcc, 0},
+#ifdef NON_BROKEN_OPCODES
+{"fdivp", 2, 0xdef8, _, ShortForm, FloatAcc, FloatReg, 0},
+#else
+{"fdivp", 2, 0xdef0, _, ShortForm, FloatAcc, FloatReg, 0},
+#endif
+{"fdivp", 0, 0xdef1, _, NoModrm, 0, 0, 0},
+{"fdivs", 1, 0xd8, 6, Modrm, Mem, 0, 0},
+{"fidivl", 1, 0xda, 6, Modrm, Mem, 0, 0},
+{"fdivl", 1, 0xdc, 6, Modrm, Mem, 0, 0},
+{"fidivs", 1, 0xde, 6, Modrm, Mem, 0, 0},
+
+/* div reverse */
+{"fdivr", 1, 0xd8f8, _, ShortForm, FloatReg, 0, 0},
+{"fdivr", 2, 0xd8f8, _, ShortForm, FloatReg, FloatAcc, 0},
+#ifdef NON_BROKEN_OPCODES
+{"fdivr", 2, 0xdcf0, _, ShortForm, FloatAcc, FloatReg, 0},
+#else
+{"fdivr", 2, 0xdcf8, _, ShortForm, FloatAcc, FloatReg, 0},
+#endif
+{"fdivr", 0, 0xdcf9, _, NoModrm, 0, 0, 0},
+{"fdivrp", 1, 0xdaf8, _, ShortForm, FloatReg, 0, 0},
+{"fdivrp", 2, 0xdaf8, _, ShortForm, FloatReg, FloatAcc, 0},
+#ifdef NON_BROKEN_OPCODES
+{"fdivrp", 2, 0xdef0, _, ShortForm, FloatAcc, FloatReg, 0},
+#else
+{"fdivrp", 2, 0xdef8, _, ShortForm, FloatAcc, FloatReg, 0},
+#endif
+{"fdivrp", 0, 0xdef9, _, NoModrm, 0, 0, 0},
+{"fdivrs", 1, 0xd8, 7, Modrm, Mem, 0, 0},
+{"fidivrl", 1, 0xda, 7, Modrm, Mem, 0, 0},
+{"fdivrl", 1, 0xdc, 7, Modrm, Mem, 0, 0},
+{"fidivrs", 1, 0xde, 7, Modrm, Mem, 0, 0},
+
+{"f2xm1", 0, 0xd9f0, _, NoModrm, 0, 0, 0},
+{"fyl2x", 0, 0xd9f1, _, NoModrm, 0, 0, 0},
+{"fptan", 0, 0xd9f2, _, NoModrm, 0, 0, 0},
+{"fpatan", 0, 0xd9f3, _, NoModrm, 0, 0, 0},
+{"fxtract", 0, 0xd9f4, _, NoModrm, 0, 0, 0},
+{"fprem1", 0, 0xd9f5, _, NoModrm, 0, 0, 0},
+{"fdecstp", 0, 0xd9f6, _, NoModrm, 0, 0, 0},
+{"fincstp", 0, 0xd9f7, _, NoModrm, 0, 0, 0},
+{"fprem", 0, 0xd9f8, _, NoModrm, 0, 0, 0},
+{"fyl2xp1", 0, 0xd9f9, _, NoModrm, 0, 0, 0},
+{"fsqrt", 0, 0xd9fa, _, NoModrm, 0, 0, 0},
+{"fsincos", 0, 0xd9fb, _, NoModrm, 0, 0, 0},
+{"frndint", 0, 0xd9fc, _, NoModrm, 0, 0, 0},
+{"fscale", 0, 0xd9fd, _, NoModrm, 0, 0, 0},
+{"fsin", 0, 0xd9fe, _, NoModrm, 0, 0, 0},
+{"fcos", 0, 0xd9ff, _, NoModrm, 0, 0, 0},
+
+{"fchs", 0, 0xd9e0, _, NoModrm, 0, 0, 0},
+{"fabs", 0, 0xd9e1, _, NoModrm, 0, 0, 0},
+
+/* processor control */
+{"fninit", 0, 0xdbe3, _, NoModrm, 0, 0, 0},
+{"finit", 0, 0xdbe3, _, NoModrm, 0, 0, 0},
+{"fldcw", 1, 0xd9, 5, Modrm, Mem, 0, 0},
+{"fnstcw", 1, 0xd9, 7, Modrm, Mem, 0, 0},
+{"fstcw", 1, 0xd9, 7, Modrm, Mem, 0, 0},
+{"fnstsw", 1, 0xdfe0, _, NoModrm, Acc, 0, 0},
+{"fnstsw", 1, 0xdd, 7, Modrm, Mem, 0, 0},
+{"fnstsw", 0, 0xdfe0, _, NoModrm, 0, 0, 0},
+{"fstsw", 1, 0xdfe0, _, NoModrm, Acc, 0, 0},
+{"fstsw", 1, 0xdd, 7, Modrm, Mem, 0, 0},
+{"fstsw", 0, 0xdfe0, _, NoModrm, 0, 0, 0},
+{"fnclex", 0, 0xdbe2, _, NoModrm, 0, 0, 0},
+{"fclex", 0, 0xdbe2, _, NoModrm, 0, 0, 0},
+/*
+ We ignore the short format (287) versions of fstenv/fldenv & fsave/frstor
+ instructions; i'm not sure how to add them or how they are different.
+ My 386/387 book offers no details about this.
+*/
+{"fnstenv", 1, 0xd9, 6, Modrm, Mem, 0, 0},
+{"fstenv", 1, 0xd9, 6, Modrm, Mem, 0, 0},
+{"fldenv", 1, 0xd9, 4, Modrm, Mem, 0, 0},
+{"fnsave", 1, 0xdd, 6, Modrm, Mem, 0, 0},
+{"fsave", 1, 0xdd, 6, Modrm, Mem, 0, 0},
+{"frstor", 1, 0xdd, 4, Modrm, Mem, 0, 0},
+
+{"ffree", 1, 0xddc0, _, ShortForm, FloatReg, 0, 0},
+{"fnop", 0, 0xd9d0, _, NoModrm, 0, 0, 0},
+{"fwait", 0, 0x9b, _, NoModrm, 0, 0, 0},
+
+/*
+ opcode prefixes; we allow them as seperate insns too
+ (see prefix table below)
+*/
+{"aword", 0, 0x67, _, NoModrm, 0, 0, 0},
+{"word", 0, 0x66, _, NoModrm, 0, 0, 0},
+{"lock", 0, 0xf0, _, NoModrm, 0, 0, 0},
+{"cs", 0, 0x2e, _, NoModrm, 0, 0, 0},
+{"ds", 0, 0x3e, _, NoModrm, 0, 0, 0},
+{"es", 0, 0x26, _, NoModrm, 0, 0, 0},
+{"fs", 0, 0x64, _, NoModrm, 0, 0, 0},
+{"gs", 0, 0x65, _, NoModrm, 0, 0, 0},
+{"ss", 0, 0x36, _, NoModrm, 0, 0, 0},
+{"rep", 0, 0xf3, _, NoModrm, 0, 0, 0},
+{"repe", 0, 0xf3, _, NoModrm, 0, 0, 0},
+{ "repne", 0, 0xf2, _, NoModrm, 0, 0, 0},
+
+{"", 0, 0, 0, 0, 0, 0, 0} /* sentinal */
+};
+#undef _
+
+template *i386_optab_end
+ = i386_optab + sizeof (i386_optab)/sizeof(i386_optab[0]);
+
+/* 386 register table */
+
+reg_entry i386_regtab[] = {
+ /* 8 bit regs */
+ {"al", Reg8|Acc, 0}, {"cl", Reg8|ShiftCount, 1}, {"dl", Reg8, 2},
+ {"bl", Reg8, 3},
+ {"ah", Reg8, 4}, {"ch", Reg8, 5}, {"dh", Reg8, 6}, {"bh", Reg8, 7},
+ /* 16 bit regs */
+ {"ax", Reg16|Acc, 0}, {"cx", Reg16, 1}, {"dx", Reg16|InOutPortReg, 2}, {"bx", Reg16, 3},
+ {"sp", Reg16, 4}, {"bp", Reg16, 5}, {"si", Reg16, 6}, {"di", Reg16, 7},
+ /* 32 bit regs */
+ {"eax", Reg32|Acc, 0}, {"ecx", Reg32, 1}, {"edx", Reg32, 2}, {"ebx", Reg32, 3},
+ {"esp", Reg32, 4}, {"ebp", Reg32, 5}, {"esi", Reg32, 6}, {"edi", Reg32, 7},
+ /* segment registers */
+ {"es", SReg2, 0}, {"cs", SReg2, 1}, {"ss", SReg2, 2},
+ {"ds", SReg2, 3}, {"fs", SReg3, 4}, {"gs", SReg3, 5},
+ /* control registers */
+ {"cr0", Control, 0}, {"cr2", Control, 2}, {"cr3", Control, 3},
+ /* debug registers */
+ {"db0", Debug, 0}, {"db1", Debug, 1}, {"db2", Debug, 2},
+ {"db3", Debug, 3}, {"db6", Debug, 6}, {"db7", Debug, 7},
+ /* test registers */
+ {"tr6", Test, 6}, {"tr7", Test, 7},
+ /* float registers */
+ {"st(0)", FloatReg|FloatAcc, 0},
+ {"st", FloatReg|FloatAcc, 0},
+ {"st(1)", FloatReg, 1}, {"st(2)", FloatReg, 2},
+ {"st(3)", FloatReg, 3}, {"st(4)", FloatReg, 4}, {"st(5)", FloatReg, 5},
+ {"st(6)", FloatReg, 6}, {"st(7)", FloatReg, 7}
+};
+
+#define MAX_REG_NAME_SIZE 8 /* for parsing register names from input */
+
+reg_entry *i386_regtab_end
+ = i386_regtab + sizeof(i386_regtab)/sizeof(i386_regtab[0]);
+
+/* segment stuff */
+seg_entry cs = { "cs", 0x2e };
+seg_entry ds = { "ds", 0x3e };
+seg_entry ss = { "ss", 0x36 };
+seg_entry es = { "es", 0x26 };
+seg_entry fs = { "fs", 0x64 };
+seg_entry gs = { "gs", 0x65 };
+seg_entry null = { "", 0x0 };
+
+/*
+ This table is used to store the default segment register implied by all
+ possible memory addressing modes.
+ It is indexed by the mode & modrm entries of the modrm byte as follows:
+ index = (mode<<3) | modrm;
+*/
+seg_entry *one_byte_segment_defaults[] = {
+ /* mode 0 */
+ &ds, &ds, &ds, &ds, &null, &ds, &ds, &ds,
+ /* mode 1 */
+ &ds, &ds, &ds, &ds, &null, &ss, &ds, &ds,
+ /* mode 2 */
+ &ds, &ds, &ds, &ds, &null, &ss, &ds, &ds,
+ /* mode 3 --- not a memory reference; never referenced */
+};
+
+seg_entry *two_byte_segment_defaults[] = {
+ /* mode 0 */
+ &ds, &ds, &ds, &ds, &ss, &ds, &ds, &ds,
+ /* mode 1 */
+ &ds, &ds, &ds, &ds, &ss, &ds, &ds, &ds,
+ /* mode 2 */
+ &ds, &ds, &ds, &ds, &ss, &ds, &ds, &ds,
+ /* mode 3 --- not a memory reference; never referenced */
+};
+
+prefix_entry i386_prefixtab[] = {
+ { "addr16", 0x67 }, /* address size prefix ==> 16bit addressing
+ * (How is this useful?) */
+#define WORD_PREFIX_OPCODE 0x66
+ { "data16", 0x66 }, /* operand size prefix */
+ { "lock", 0xf0 }, /* bus lock prefix */
+ { "wait", 0x9b }, /* wait for coprocessor */
+ { "cs", 0x2e }, { "ds", 0x3e }, /* segment overrides ... */
+ { "es", 0x26 }, { "fs", 0x64 },
+ { "gs", 0x65 }, { "ss", 0x36 },
+/* REPE & REPNE used to detect rep/repne with a non-string instruction */
+#define REPNE 0xf2
+#define REPE 0xf3
+ { "rep", 0xf3 }, { "repe", 0xf3 }, /* repeat string instructions */
+ { "repne", 0xf2 }
+};
+
+prefix_entry *i386_prefixtab_end
+ = i386_prefixtab + sizeof(i386_prefixtab)/sizeof(i386_prefixtab[0]);
+
diff --git a/gnu/usr.bin/as/config/i386.c b/gnu/usr.bin/as/config/i386.c
new file mode 100644
index 0000000..2281acd
--- /dev/null
+++ b/gnu/usr.bin/as/config/i386.c
@@ -0,0 +1,1946 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)i386.c 6.4 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* i386.c -- Assemble code for the Intel 80386
+ Copyright (C) 1989, Free Software Foundation.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ Intel 80386 machine specific gas.
+ Written by Eliot Dresselhaus (eliot@mgm.mit.edu).
+ Bugs & suggestions are completely welcome. This is free software.
+ Please help us make it better.
+*/
+
+#include <stdio.h>
+#include <varargs.h>
+#include <ctype.h>
+
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else
+extern char *alloca();
+#endif
+#ifdef USG
+#define index strchr
+#endif
+
+#include "as.h"
+#include "read.h"
+#include "flonum.h"
+#include "obstack.h"
+#include "frags.h"
+#include "struc-symbol.h"
+#include "expr.h"
+#include "symbols.h"
+#include "hash.h"
+#include "md.h"
+#include "i386.h"
+#include "i386-opcode.h"
+
+long omagic = OMAGIC;
+char FLT_CHARS[] = "fFdDxX";
+char EXP_CHARS[] = "eE";
+char line_comment_chars[] = "#";
+char comment_chars[] = "#";
+
+/* tables for lexical analysis */
+static char opcode_chars[256];
+static char register_chars[256];
+static char operand_chars[256];
+static char space_chars[256];
+static char identifier_chars[256];
+static char digit_chars[256];
+
+/* lexical macros */
+#define is_opcode_char(x) (opcode_chars[(unsigned char) x])
+#define is_operand_char(x) (operand_chars[(unsigned char) x])
+#define is_register_char(x) (register_chars[(unsigned char) x])
+#define is_space_char(x) (space_chars[(unsigned char) x])
+#define is_identifier_char(x) (identifier_chars[(unsigned char) x])
+#define is_digit_char(x) (digit_chars[(unsigned char) x])
+
+/* put here all non-digit non-letter charcters that may occur in an operand */
+static char operand_special_chars[] = "%$-+(,)*._~/<>|&^!:";
+
+static char *ordinal_names[] = { "first", "second", "third" }; /* for printfs */
+
+/* md_assemble() always leaves the strings it's passed unaltered. To
+ effect this we maintain a stack of saved characters that we've smashed
+ with '\0's (indicating end of strings for various sub-fields of the
+ assembler instruction). */
+static char save_stack[32];
+static char *save_stack_p; /* stack pointer */
+#define END_STRING_AND_SAVE(s) *save_stack_p++ = *s; *s = '\0'
+#define RESTORE_END_STRING(s) *s = *--save_stack_p
+
+/* The instruction we're assembling. */
+static i386_insn i;
+
+/* Per instruction expressionS buffers: 2 displacements & 2 immediate max. */
+static expressionS disp_expressions[2], im_expressions[2];
+
+/* pointers to ebp & esp entries in reg_hash hash table */
+static reg_entry *ebp, *esp;
+
+static int this_operand; /* current operand we are working on */
+
+/*
+Interface to relax_segment.
+There are 2 relax states for 386 jump insns: one for conditional & one
+for unconditional jumps. This is because the these two types of jumps
+add different sizes to frags when we're figuring out what sort of jump
+to choose to reach a given label. */
+
+/* types */
+#define COND_JUMP 1 /* conditional jump */
+#define UNCOND_JUMP 2 /* unconditional jump */
+/* sizes */
+#define BYTE 0
+#define WORD 1
+#define DWORD 2
+#define UNKNOWN_SIZE 3
+
+#define ENCODE_RELAX_STATE(type,size) ((type<<2) | (size))
+#define SIZE_FROM_RELAX_STATE(s) \
+ ( (((s) & 0x3) == BYTE ? 1 : (((s) & 0x3) == WORD ? 2 : 4)) )
+
+const relax_typeS md_relax_table[] = {
+/*
+ The fields are:
+ 1) most positive reach of this state,
+ 2) most negative reach of this state,
+ 3) how many bytes this mode will add to the size of the current frag
+ 4) which index into the table to try if we can't fit into this one.
+*/
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+
+ /* For now we don't use word displacement jumps: they may be
+ untrustworthy. */
+ {127+1, -128+1, 0, ENCODE_RELAX_STATE(COND_JUMP,DWORD) },
+ /* word conditionals add 3 bytes to frag:
+ 2 opcode prefix; 1 displacement bytes */
+ {32767+2, -32768+2, 3, ENCODE_RELAX_STATE(COND_JUMP,DWORD) },
+ /* dword conditionals adds 4 bytes to frag:
+ 1 opcode prefix; 3 displacement bytes */
+ {0, 0, 4, 0},
+ {1, 1, 0, 0},
+
+ {127+1, -128+1, 0, ENCODE_RELAX_STATE(UNCOND_JUMP,DWORD) },
+ /* word jmp adds 2 bytes to frag:
+ 1 opcode prefix; 1 displacement bytes */
+ {32767+2, -32768+2, 2, ENCODE_RELAX_STATE(UNCOND_JUMP,DWORD) },
+ /* dword jmp adds 3 bytes to frag:
+ 0 opcode prefix; 3 displacement bytes */
+ {0, 0, 3, 0},
+ {1, 1, 0, 0},
+
+};
+
+void float_cons (), cons ();
+
+/* Ignore certain directives generated by gcc. This probably should
+ not be here. */
+void dummy ()
+{
+ while (*input_line_pointer && *input_line_pointer != '\n')
+ input_line_pointer++;
+}
+
+const pseudo_typeS md_pseudo_table[] = {
+ { "ffloat", float_cons, 'f' },
+ { "dfloat", float_cons, 'd' },
+ { "tfloat", float_cons, 'x' },
+ { "value", cons, 2 },
+ { "ident", dummy, 0 }, /* ignore these directives */
+ { "def", dummy, 0 },
+ { "optim", dummy, 0 }, /* For sun386i cc */
+ { "version", dummy, 0 },
+ { "ln", dummy, 0 },
+ { 0, 0, 0 }
+};
+
+/* for interface with expression () */
+extern char * input_line_pointer;
+char * index ();
+
+char * output_invalid ();
+reg_entry * parse_register ();
+
+/* obstack for constructing various things in md_begin */
+struct obstack o;
+
+/* hash table for opcode lookup */
+static struct hash_control *op_hash = (struct hash_control *) 0;
+/* hash table for register lookup */
+static struct hash_control *reg_hash = (struct hash_control *) 0;
+/* hash table for prefix lookup */
+static struct hash_control *prefix_hash = (struct hash_control *) 0;
+
+
+void md_begin ()
+{
+ char * hash_err;
+
+ obstack_begin (&o,4096);
+
+ /* initialize op_hash hash table */
+ op_hash = hash_new(); /* xmalloc handles error */
+
+ {
+ register template *optab;
+ register templates *core_optab;
+ char *prev_name;
+
+ optab = i386_optab; /* setup for loop */
+ prev_name = optab->name;
+ obstack_grow (&o, optab, sizeof(template));
+ core_optab = (templates *) xmalloc (sizeof (templates));
+
+ for (optab++; optab < i386_optab_end; optab++) {
+ if (! strcmp (optab->name, prev_name)) {
+ /* same name as before --> append to current template list */
+ obstack_grow (&o, optab, sizeof(template));
+ } else {
+ /* different name --> ship out current template list;
+ add to hash table; & begin anew */
+ /* Note: end must be set before start! since obstack_next_free changes
+ upon opstack_finish */
+ core_optab->end = (template *) obstack_next_free(&o);
+ core_optab->start = (template *) obstack_finish(&o);
+ hash_err = hash_insert (op_hash, prev_name, (char *) core_optab);
+ if (hash_err && *hash_err) {
+ hash_error:
+ as_fatal("Internal Error: Can't hash %s: %s",prev_name, hash_err);
+ }
+ prev_name = optab->name;
+ core_optab = (templates *) xmalloc (sizeof(templates));
+ obstack_grow (&o, optab, sizeof(template));
+ }
+ }
+ }
+
+ /* initialize reg_hash hash table */
+ reg_hash = hash_new();
+ {
+ register reg_entry *regtab;
+
+ for (regtab = i386_regtab; regtab < i386_regtab_end; regtab++) {
+ hash_err = hash_insert (reg_hash, regtab->reg_name, regtab);
+ if (hash_err && *hash_err) goto hash_error;
+ }
+ }
+
+ esp = (reg_entry *) hash_find (reg_hash, "esp");
+ ebp = (reg_entry *) hash_find (reg_hash, "ebp");
+
+ /* initialize reg_hash hash table */
+ prefix_hash = hash_new();
+ {
+ register prefix_entry *prefixtab;
+
+ for (prefixtab = i386_prefixtab;
+ prefixtab < i386_prefixtab_end; prefixtab++) {
+ hash_err = hash_insert (prefix_hash, prefixtab->prefix_name, prefixtab);
+ if (hash_err && *hash_err) goto hash_error;
+ }
+ }
+
+ /* fill in lexical tables: opcode_chars, operand_chars, space_chars */
+ {
+ register unsigned int c;
+
+ bzero (opcode_chars, sizeof(opcode_chars));
+ bzero (operand_chars, sizeof(operand_chars));
+ bzero (space_chars, sizeof(space_chars));
+ bzero (identifier_chars, sizeof(identifier_chars));
+ bzero (digit_chars, sizeof(digit_chars));
+
+ for (c = 0; c < 256; c++) {
+ if (islower(c) || isdigit(c)) {
+ opcode_chars[c] = c;
+ register_chars[c] = c;
+ } else if (isupper(c)) {
+ opcode_chars[c] = tolower(c);
+ register_chars[c] = opcode_chars[c];
+ } else if (c == PREFIX_SEPERATOR) {
+ opcode_chars[c] = c;
+ } else if (c == ')' || c == '(') {
+ register_chars[c] = c;
+ }
+
+ if (isupper(c) || islower(c) || isdigit(c))
+ operand_chars[c] = c;
+ else if (c && index(operand_special_chars, c))
+ operand_chars[c] = c;
+
+ if (isdigit(c) || c == '-') digit_chars[c] = c;
+
+ if (isalpha(c) || c == '_' || c == '.' || isdigit(c))
+ identifier_chars[c] = c;
+
+ if (c == ' ' || c == '\t') space_chars[c] = c;
+ }
+ }
+}
+
+void md_end() {} /* not much to do here. */
+
+
+#ifdef DEBUG386
+
+/* debugging routines for md_assemble */
+static void pi (), pte (), pt (), pe (), ps ();
+
+static void pi (line, x)
+ char * line;
+ i386_insn *x;
+{
+ register template *p;
+ int i;
+
+ fprintf (stdout, "%s: template ", line);
+ pte (&x->tm);
+ fprintf (stdout, " modrm: mode %x reg %x reg/mem %x",
+ x->rm.mode, x->rm.reg, x->rm.regmem);
+ fprintf (stdout, " base %x index %x scale %x\n",
+ x->bi.base, x->bi.index, x->bi.scale);
+ for (i = 0; i < x->operands; i++) {
+ fprintf (stdout, " #%d: ", i+1);
+ pt (x->types[i]);
+ fprintf (stdout, "\n");
+ if (x->types[i] & Reg) fprintf (stdout, "%s\n", x->regs[i]->reg_name);
+ if (x->types[i] & Imm) pe (x->imms[i]);
+ if (x->types[i] & (Disp|Abs)) pe (x->disps[i]);
+ }
+}
+
+static void pte (t)
+ template *t;
+{
+ int i;
+ fprintf (stdout, " %d operands ", t->operands);
+ fprintf (stdout, "opcode %x ",
+ t->base_opcode);
+ if (t->extension_opcode != None)
+ fprintf (stdout, "ext %x ", t->extension_opcode);
+ if (t->opcode_modifier&D)
+ fprintf (stdout, "D");
+ if (t->opcode_modifier&W)
+ fprintf (stdout, "W");
+ fprintf (stdout, "\n");
+ for (i = 0; i < t->operands; i++) {
+ fprintf (stdout, " #%d type ", i+1);
+ pt (t->operand_types[i]);
+ fprintf (stdout, "\n");
+ }
+}
+
+char *seg_names[] = {
+"SEG_ABSOLUTE", "SEG_TEXT", "SEG_DATA", "SEG_BSS", "SEG_UNKNOWN",
+"SEG_NONE", "SEG_PASS1", "SEG_GOOF", "SEG_BIG", "SEG_DIFFERENCE" };
+
+static void pe (e)
+ expressionS *e;
+{
+ fprintf (stdout, " segment %s\n", seg_names[(int) e->X_seg]);
+ fprintf (stdout, " add_number %d (%x)\n",
+ e->X_add_number, e->X_add_number);
+ if (e->X_add_symbol) {
+ fprintf (stdout, " add_symbol ");
+ ps (e->X_add_symbol);
+ fprintf (stdout, "\n");
+ }
+ if (e->X_subtract_symbol) {
+ fprintf (stdout, " sub_symbol ");
+ ps (e->X_subtract_symbol);
+ fprintf (stdout, "\n");
+ }
+}
+
+#define SYMBOL_TYPE(t) \
+ (((t&N_TYPE) == N_UNDF) ? "UNDEFINED" : \
+ (((t&N_TYPE) == N_ABS) ? "ABSOLUTE" : \
+ (((t&N_TYPE) == N_TEXT) ? "TEXT" : \
+ (((t&N_TYPE) == N_DATA) ? "DATA" : \
+ (((t&N_TYPE) == N_BSS) ? "BSS" : "Bad n_type!")))))
+
+static void ps (s)
+ symbolS *s;
+{
+ fprintf (stdout, "%s type %s%s",
+ s->sy_nlist.n_un.n_name,
+ (s->sy_nlist.n_type&N_EXT) ? "EXTERNAL " : "",
+ SYMBOL_TYPE (s->sy_nlist.n_type));
+}
+
+struct type_name {
+ uint mask;
+ char *tname;
+} type_names[] = {
+ { Reg8, "r8" }, { Reg16, "r16" }, { Reg32, "r32" }, { Imm8, "i8" },
+ { Imm8S, "i8s" },
+ { Imm16, "i16" }, { Imm32, "i32" }, { Mem8, "Mem8"}, { Mem16, "Mem16"},
+ { Mem32, "Mem32"}, { BaseIndex, "BaseIndex" },
+ { Abs8, "Abs8" }, { Abs16, "Abs16" }, { Abs32, "Abs32" },
+ { Disp8, "d8" }, { Disp16, "d16" },
+ { Disp32, "d32" }, { SReg2, "SReg2" }, { SReg3, "SReg3" }, { Acc, "Acc" },
+ { InOutPortReg, "InOutPortReg" }, { ShiftCount, "ShiftCount" },
+ { Imm1, "i1" }, { Control, "control reg" }, {Test, "test reg"},
+ { FloatReg, "FReg"}, {FloatAcc, "FAcc"},
+ { JumpAbsolute, "Jump Absolute"},
+ { 0, "" }
+};
+
+static void pt (t)
+ uint t;
+{
+ register struct type_name *ty;
+
+ if (t == Unknown) {
+ fprintf (stdout, "Unknown");
+ } else {
+ for (ty = type_names; ty->mask; ty++)
+ if (t & ty->mask) fprintf (stdout, "%s, ", ty->tname);
+ }
+ fflush (stdout);
+}
+
+#endif /* DEBUG386 */
+
+/*
+ This is the guts of the machine-dependent assembler. LINE points to a
+ machine dependent instruction. This funciton is supposed to emit
+ the frags/bytes it assembles to.
+ */
+void md_assemble (line)
+ char *line;
+{
+ /* Holds temlate once we've found it. */
+ register template * t;
+
+ /* Possible templates for current insn */
+ templates *current_templates = (templates *) 0;
+
+ /* Initialize globals. */
+ bzero (&i, sizeof(i));
+ bzero (disp_expressions, sizeof(disp_expressions));
+ bzero (im_expressions, sizeof(im_expressions));
+ save_stack_p = save_stack; /* reset stack pointer */
+
+ /* Fist parse an opcode & call i386_operand for the operands.
+ We assume that the scrubber has arranged it so that line[0] is the valid
+ start of a (possibly prefixed) opcode. */
+ {
+ register char *l = line; /* Fast place to put LINE. */
+
+ /* TRUE if operand is pending after ','. */
+ uint expecting_operand = 0;
+ /* TRUE if we found a prefix only acceptable with string insns. */
+ uint expecting_string_instruction = 0;
+ /* Non-zero if operand parens not balenced. */
+ uint paren_not_balenced;
+ char * token_start = l;
+
+ while (! is_space_char(*l) && *l != END_OF_INSN) {
+ if (! is_opcode_char(*l)) {
+ as_bad ("invalid character %s in opcode", output_invalid(*l));
+ return;
+ } else if (*l != PREFIX_SEPERATOR) {
+ *l = opcode_chars[(unsigned char) *l]; /* fold case of opcodes */
+ l++;
+ } else { /* this opcode's got a prefix */
+ register int q;
+ register prefix_entry * prefix;
+
+ if (l == token_start) {
+ as_bad ("expecting prefix; got nothing");
+ return;
+ }
+ END_STRING_AND_SAVE (l);
+ prefix = (prefix_entry *) hash_find (prefix_hash, token_start);
+ if (! prefix) {
+ as_bad ("no such opcode prefix ('%s')", token_start);
+ return;
+ }
+ RESTORE_END_STRING (l);
+ /* check for repeated prefix */
+ for (q = 0; q < i.prefixes; q++)
+ if (i.prefix[q] == prefix->prefix_code) {
+ as_bad ("same prefix used twice; you don't really want this!");
+ return;
+ }
+ if (i.prefixes == MAX_PREFIXES) {
+ as_bad ("too many opcode prefixes");
+ return;
+ }
+ i.prefix[i.prefixes++] = prefix->prefix_code;
+ if (prefix->prefix_code == REPE || prefix->prefix_code == REPNE)
+ expecting_string_instruction = TRUE;
+ /* skip past PREFIX_SEPERATOR and reset token_start */
+ token_start = ++l;
+ }
+ }
+ END_STRING_AND_SAVE (l);
+ if (token_start == l) {
+ as_bad ("expecting opcode; got nothing");
+ return;
+ }
+
+ /* Lookup insn in hash; try intel & att naming conventions if appropriate;
+ that is: we only use the opcode suffix 'b' 'w' or 'l' if we need to. */
+ current_templates = (templates *) hash_find (op_hash, token_start);
+ if (! current_templates) {
+ int last_index = strlen(token_start) - 1;
+ char last_char = token_start[last_index];
+ switch (last_char) {
+ case DWORD_OPCODE_SUFFIX:
+ case WORD_OPCODE_SUFFIX:
+ case BYTE_OPCODE_SUFFIX:
+ token_start[last_index] = '\0';
+ current_templates = (templates *) hash_find (op_hash, token_start);
+ token_start[last_index] = last_char;
+ i.suffix = last_char;
+ }
+ if (!current_templates) {
+ as_bad ("no such 386 instruction: `%s'", token_start); return;
+ }
+ }
+ RESTORE_END_STRING (l);
+
+ /* check for rep/repne without a string instruction */
+ if (expecting_string_instruction &&
+ ! IS_STRING_INSTRUCTION (current_templates->
+ start->base_opcode)) {
+ as_bad ("expecting string instruction after rep/repne");
+ return;
+ }
+
+ /* There may be operands to parse. */
+ if (*l != END_OF_INSN &&
+ /* For string instructions, we ignore any operands if given. This
+ kludges, for example, 'rep/movsb %ds:(%esi), %es:(%edi)' where
+ the operands are always going to be the same, and are not really
+ encoded in machine code. */
+ ! IS_STRING_INSTRUCTION (current_templates->
+ start->base_opcode)) {
+ /* parse operands */
+ do {
+ /* skip optional white space before operand */
+ while (! is_operand_char(*l) && *l != END_OF_INSN) {
+ if (! is_space_char(*l)) {
+ as_bad ("invalid character %s before %s operand",
+ output_invalid(*l),
+ ordinal_names[i.operands]);
+ return;
+ }
+ l++;
+ }
+ token_start = l; /* after white space */
+ paren_not_balenced = 0;
+ while (paren_not_balenced || *l != ',') {
+ if (*l == END_OF_INSN) {
+ if (paren_not_balenced) {
+ as_bad ("unbalenced parenthesis in %s operand.",
+ ordinal_names[i.operands]);
+ return;
+ } else break; /* we are done */
+ } else if (! is_operand_char(*l)) {
+ as_bad ("invalid character %s in %s operand",
+ output_invalid(*l),
+ ordinal_names[i.operands]);
+ return;
+ }
+ if (*l == '(') ++paren_not_balenced;
+ if (*l == ')') --paren_not_balenced;
+ l++;
+ }
+ if (l != token_start) { /* yes, we've read in another operand */
+ uint operand_ok;
+ this_operand = i.operands++;
+ if (i.operands > MAX_OPERANDS) {
+ as_bad ("spurious operands; (%d operands/instruction max)",
+ MAX_OPERANDS);
+ return;
+ }
+ /* now parse operand adding info to 'i' as we go along */
+ END_STRING_AND_SAVE (l);
+ operand_ok = i386_operand (token_start);
+ RESTORE_END_STRING (l); /* restore old contents */
+ if (!operand_ok) return;
+ } else {
+ if (expecting_operand) {
+ expecting_operand_after_comma:
+ as_bad ("expecting operand after ','; got nothing");
+ return;
+ }
+ if (*l == ',') {
+ as_bad ("expecting operand before ','; got nothing");
+ return;
+ }
+ }
+
+ /* now *l must be either ',' or END_OF_INSN */
+ if (*l == ',') {
+ if (*++l == END_OF_INSN) { /* just skip it, if it's \n complain */
+ goto expecting_operand_after_comma;
+ }
+ expecting_operand = TRUE;
+ }
+ } while (*l != END_OF_INSN); /* until we get end of insn */
+ }
+ }
+
+ /* Now we've parsed the opcode into a set of templates, and have the
+ operands at hand.
+ Next, we find a template that matches the given insn,
+ making sure the overlap of the given operands types is consistent
+ with the template operand types. */
+
+#define MATCH(overlap,given_type) \
+ (overlap && \
+ (overlap & (JumpAbsolute|BaseIndex|Mem8)) \
+ == (given_type & (JumpAbsolute|BaseIndex|Mem8)))
+
+ /* If m0 and m1 are register matches they must be consistent
+ with the expected operand types t0 and t1.
+ That is, if both m0 & m1 are register matches
+ i.e. ( ((m0 & (Reg)) && (m1 & (Reg)) ) ?
+ then, either 1. or 2. must be true:
+ 1. the expected operand type register overlap is null:
+ (t0 & t1 & Reg) == 0
+ AND
+ the given register overlap is null:
+ (m0 & m1 & Reg) == 0
+ 2. the expected operand type register overlap == the given
+ operand type overlap: (t0 & t1 & m0 & m1 & Reg).
+ */
+#define CONSISTENT_REGISTER_MATCH(m0, m1, t0, t1) \
+ ( ((m0 & (Reg)) && (m1 & (Reg))) ? \
+ ( ((t0 & t1 & (Reg)) == 0 && (m0 & m1 & (Reg)) == 0) || \
+ ((t0 & t1) & (m0 & m1) & (Reg)) \
+ ) : 1)
+ {
+ register uint overlap0, overlap1;
+ expressionS * exp;
+ uint overlap2;
+ uint found_reverse_match;
+
+ overlap0 = overlap1 = overlap2 = found_reverse_match = 0;
+ for (t = current_templates->start;
+ t < current_templates->end;
+ t++) {
+
+ /* must have right number of operands */
+ if (i.operands != t->operands) continue;
+ else if (!t->operands) break; /* 0 operands always matches */
+
+ overlap0 = i.types[0] & t->operand_types[0];
+ switch (t->operands) {
+ case 1:
+ if (! MATCH (overlap0,i.types[0])) continue;
+ break;
+ case 2: case 3:
+ overlap1 = i.types[1] & t->operand_types[1];
+ if (! MATCH (overlap0,i.types[0]) ||
+ ! MATCH (overlap1,i.types[1]) ||
+ ! CONSISTENT_REGISTER_MATCH(overlap0, overlap1,
+ t->operand_types[0],
+ t->operand_types[1])) {
+
+ /* check if other direction is valid ... */
+ if (! (t->opcode_modifier & COMES_IN_BOTH_DIRECTIONS))
+ continue;
+
+ /* try reversing direction of operands */
+ overlap0 = i.types[0] & t->operand_types[1];
+ overlap1 = i.types[1] & t->operand_types[0];
+ if (! MATCH (overlap0,i.types[0]) ||
+ ! MATCH (overlap1,i.types[1]) ||
+ ! CONSISTENT_REGISTER_MATCH (overlap0, overlap1,
+ t->operand_types[0],
+ t->operand_types[1])) {
+ /* does not match either direction */
+ continue;
+ }
+ /* found a reverse match here -- slip through */
+ /* found_reverse_match holds which of D or FloatD we've found */
+ found_reverse_match = t->opcode_modifier & COMES_IN_BOTH_DIRECTIONS;
+ } /* endif: not forward match */
+ /* found either forward/reverse 2 operand match here */
+ if (t->operands == 3) {
+ overlap2 = i.types[2] & t->operand_types[2];
+ if (! MATCH (overlap2,i.types[2]) ||
+ ! CONSISTENT_REGISTER_MATCH (overlap0, overlap2,
+ t->operand_types[0],
+ t->operand_types[2]) ||
+ ! CONSISTENT_REGISTER_MATCH (overlap1, overlap2,
+ t->operand_types[1],
+ t->operand_types[2]))
+ continue;
+ }
+ /* found either forward/reverse 2 or 3 operand match here:
+ slip through to break */
+ }
+ break; /* we've found a match; break out of loop */
+ } /* for (t = ... */
+ if (t == current_templates->end) { /* we found no match */
+ as_bad ("operands given don't match any known 386 instruction");
+ return;
+ }
+
+ /* Copy the template we found (we may change it!). */
+ bcopy (t, &i.tm, sizeof (template));
+ t = &i.tm; /* alter new copy of template */
+
+ /* If there's no opcode suffix we try to invent one based on register
+ operands. */
+ if (! i.suffix && i.reg_operands) {
+ /* We take i.suffix from the LAST register operand specified. This
+ assumes that the last register operands is the destination register
+ operand. */
+ int o;
+ for (o = 0; o < MAX_OPERANDS; o++)
+ if (i.types[o] & Reg) {
+ i.suffix = (i.types[o] == Reg8) ? BYTE_OPCODE_SUFFIX :
+ (i.types[o] == Reg16) ? WORD_OPCODE_SUFFIX :
+ DWORD_OPCODE_SUFFIX;
+ }
+ }
+
+ /* Make still unresolved immediate matches conform to size of immediate
+ given in i.suffix. Note: overlap2 cannot be an immediate!
+ We assume this. */
+ if ((overlap0 & (Imm8|Imm8S|Imm16|Imm32))
+ && overlap0 != Imm8 && overlap0 != Imm8S
+ && overlap0 != Imm16 && overlap0 != Imm32) {
+ if (! i.suffix) {
+ as_bad ("no opcode suffix given; can't determine immediate size");
+ return;
+ }
+ overlap0 &= (i.suffix == BYTE_OPCODE_SUFFIX ? (Imm8|Imm8S) :
+ (i.suffix == WORD_OPCODE_SUFFIX ? Imm16 : Imm32));
+ }
+ if ((overlap1 & (Imm8|Imm8S|Imm16|Imm32))
+ && overlap1 != Imm8 && overlap1 != Imm8S
+ && overlap1 != Imm16 && overlap1 != Imm32) {
+ if (! i.suffix) {
+ as_bad ("no opcode suffix given; can't determine immediate size");
+ return;
+ }
+ overlap1 &= (i.suffix == BYTE_OPCODE_SUFFIX ? (Imm8|Imm8S) :
+ (i.suffix == WORD_OPCODE_SUFFIX ? Imm16 : Imm32));
+ }
+
+ i.types[0] = overlap0;
+ i.types[1] = overlap1;
+ i.types[2] = overlap2;
+
+ if (overlap0 & ImplicitRegister) i.reg_operands--;
+ if (overlap1 & ImplicitRegister) i.reg_operands--;
+ if (overlap2 & ImplicitRegister) i.reg_operands--;
+ if (overlap0 & Imm1) i.imm_operands = 0; /* kludge for shift insns */
+
+ if (found_reverse_match) {
+ uint save;
+ save = t->operand_types[0];
+ t->operand_types[0] = t->operand_types[1];
+ t->operand_types[1] = save;
+ }
+
+ /* Finalize opcode. First, we change the opcode based on the operand
+ size given by i.suffix: we never have to change things for byte insns,
+ or when no opcode suffix is need to size the operands. */
+
+ if (! i.suffix && (t->opcode_modifier & W)) {
+ as_bad ("no opcode suffix given and no register operands; can't size instruction");
+ return;
+ }
+
+ if (i.suffix && i.suffix != BYTE_OPCODE_SUFFIX) {
+ /* Select between byte and word/dword operations. */
+ if (t->opcode_modifier & W)
+ t->base_opcode |= W;
+ /* Now select between word & dword operations via the
+ operand size prefix. */
+ if (i.suffix == WORD_OPCODE_SUFFIX) {
+ if (i.prefixes == MAX_PREFIXES) {
+ as_bad ("%d prefixes given and 'w' opcode suffix gives too many prefixes",
+ MAX_PREFIXES);
+ return;
+ }
+ i.prefix[i.prefixes++] = WORD_PREFIX_OPCODE;
+ }
+ }
+
+ /* For insns with operands there are more diddles to do to the opcode. */
+ if (i.operands) {
+ /* If we found a reverse match we must alter the opcode direction bit
+ found_reverse_match holds bit to set (different for int &
+ float insns). */
+
+ if (found_reverse_match) {
+ t->base_opcode |= found_reverse_match;
+ }
+
+ /*
+ The imul $imm, %reg instruction is converted into
+ imul $imm, %reg, %reg. */
+ if (t->opcode_modifier & imulKludge) {
+ i.regs[2] = i.regs[1]; /* Pretend we saw the 3 operand case. */
+ i.reg_operands = 2;
+ }
+
+ /* Certain instructions expect the destination to be in the i.rm.reg
+ field. This is by far the exceptional case. For these instructions,
+ if the source operand is a register, we must reverse the i.rm.reg
+ and i.rm.regmem fields. We accomplish this by faking that the
+ two register operands were given in the reverse order. */
+ if ((t->opcode_modifier & ReverseRegRegmem) && i.reg_operands == 2) {
+ uint first_reg_operand = (i.types[0] & Reg) ? 0 : 1;
+ uint second_reg_operand = first_reg_operand + 1;
+ reg_entry *tmp = i.regs[first_reg_operand];
+ i.regs[first_reg_operand] = i.regs[second_reg_operand];
+ i.regs[second_reg_operand] = tmp;
+ }
+
+ if (t->opcode_modifier & ShortForm) {
+ /* The register or float register operand is in operand 0 or 1. */
+ uint o = (i.types[0] & (Reg|FloatReg)) ? 0 : 1;
+ /* Register goes in low 3 bits of opcode. */
+ t->base_opcode |= i.regs[o]->reg_num;
+ } else if (t->opcode_modifier & ShortFormW) {
+ /* Short form with 0x8 width bit. Register is always dest. operand */
+ t->base_opcode |= i.regs[1]->reg_num;
+ if (i.suffix == WORD_OPCODE_SUFFIX ||
+ i.suffix == DWORD_OPCODE_SUFFIX)
+ t->base_opcode |= 0x8;
+ } else if (t->opcode_modifier & Seg2ShortForm) {
+ if (t->base_opcode == POP_SEG_SHORT && i.regs[0]->reg_num == 1) {
+ as_bad ("you can't 'pop cs' on the 386.");
+ return;
+ }
+ t->base_opcode |= (i.regs[0]->reg_num << 3);
+ } else if (t->opcode_modifier & Seg3ShortForm) {
+ /* 'push %fs' is 0x0fa0; 'pop %fs' is 0x0fa1.
+ 'push %gs' is 0x0fa8; 'pop %fs' is 0x0fa9.
+ So, only if i.regs[0]->reg_num == 5 (%gs) do we need
+ to change the opcode. */
+ if (i.regs[0]->reg_num == 5)
+ t->base_opcode |= 0x08;
+ } else if (t->opcode_modifier & Modrm) {
+ /* The opcode is completed (modulo t->extension_opcode which must
+ be put into the modrm byte.
+ Now, we make the modrm & index base bytes based on all the info
+ we've collected. */
+
+ /* i.reg_operands MUST be the number of real register operands;
+ implicit registers do not count. */
+ if (i.reg_operands == 2) {
+ uint source, dest;
+ source = (i.types[0] & (Reg|SReg2|SReg3|Control|Debug|Test)) ? 0 : 1;
+ dest = source + 1;
+ i.rm.mode = 3;
+ /* We must be careful to make sure that all segment/control/test/
+ debug registers go into the i.rm.reg field (despite the whether
+ they are source or destination operands). */
+ if (i.regs[dest]->reg_type & (SReg2|SReg3|Control|Debug|Test)) {
+ i.rm.reg = i.regs[dest]->reg_num;
+ i.rm.regmem = i.regs[source]->reg_num;
+ } else {
+ i.rm.reg = i.regs[source]->reg_num;
+ i.rm.regmem = i.regs[dest]->reg_num;
+ }
+ } else { /* if it's not 2 reg operands... */
+ if (i.mem_operands) {
+ uint fake_zero_displacement = FALSE;
+ uint o = (i.types[0] & Mem) ? 0 : ((i.types[1] & Mem) ? 1 : 2);
+
+ /* Encode memory operand into modrm byte and base index byte. */
+
+ if (i.base_reg == esp && ! i.index_reg) {
+ /* <disp>(%esp) becomes two byte modrm with no index register. */
+ i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
+ i.rm.mode = MODE_FROM_DISP_SIZE (i.types[o]);
+ i.bi.base = ESP_REG_NUM;
+ i.bi.index = NO_INDEX_REGISTER;
+ i.bi.scale = 0; /* Must be zero! */
+ } else if (i.base_reg == ebp && !i.index_reg) {
+ if (! (i.types[o] & Disp)) {
+ /* Must fake a zero byte displacement.
+ There is no direct way to code '(%ebp)' directly. */
+ fake_zero_displacement = TRUE;
+ /* fake_zero_displacement code does not set this. */
+ i.types[o] |= Disp8;
+ }
+ i.rm.mode = MODE_FROM_DISP_SIZE (i.types[o]);
+ i.rm.regmem = EBP_REG_NUM;
+ } else if (! i.base_reg && (i.types[o] & BaseIndex)) {
+ /* There are three cases here.
+ Case 1: '<32bit disp>(,1)' -- indirect absolute.
+ (Same as cases 2 & 3 with NO index register)
+ Case 2: <32bit disp> (,<index>) -- no base register with disp
+ Case 3: (, <index>) --- no base register;
+ no disp (must add 32bit 0 disp). */
+ i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
+ i.rm.mode = 0; /* 32bit mode */
+ i.bi.base = NO_BASE_REGISTER;
+ i.types[o] &= ~Disp;
+ i.types[o] |= Disp32; /* Must be 32bit! */
+ if (i.index_reg) { /* case 2 or case 3 */
+ i.bi.index = i.index_reg->reg_num;
+ i.bi.scale = i.log2_scale_factor;
+ if (i.disp_operands == 0)
+ fake_zero_displacement = TRUE; /* case 3 */
+ } else {
+ i.bi.index = NO_INDEX_REGISTER;
+ i.bi.scale = 0;
+ }
+ } else if (i.disp_operands && !i.base_reg && !i.index_reg) {
+ /* Operand is just <32bit disp> */
+ i.rm.regmem = EBP_REG_NUM;
+ i.rm.mode = 0;
+ i.types[o] &= ~Disp;
+ i.types[o] |= Disp32;
+ } else {
+ /* It's not a special case; rev'em up. */
+ i.rm.regmem = i.base_reg->reg_num;
+ i.rm.mode = MODE_FROM_DISP_SIZE (i.types[o]);
+ if (i.index_reg) {
+ i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
+ i.bi.base = i.base_reg->reg_num;
+ i.bi.index = i.index_reg->reg_num;
+ i.bi.scale = i.log2_scale_factor;
+ if (i.base_reg == ebp && i.disp_operands == 0) { /* pace */
+ fake_zero_displacement = TRUE;
+ i.types[o] |= Disp8;
+ i.rm.mode = MODE_FROM_DISP_SIZE (i.types[o]);
+ }
+ }
+ }
+ if (fake_zero_displacement) {
+ /* Fakes a zero displacement assuming that i.types[o] holds
+ the correct displacement size. */
+ exp = &disp_expressions[i.disp_operands++];
+ i.disps[o] = exp;
+ exp->X_seg = SEG_ABSOLUTE;
+ exp->X_add_number = 0;
+ exp->X_add_symbol = (symbolS *) 0;
+ exp->X_subtract_symbol = (symbolS *) 0;
+ }
+
+ /* Select the correct segment for the memory operand. */
+ if (i.seg) {
+ uint seg_index;
+ seg_entry * default_seg;
+
+ if (i.rm.regmem == ESCAPE_TO_TWO_BYTE_ADDRESSING) {
+ seg_index = (i.rm.mode<<3) | i.bi.base;
+ default_seg = two_byte_segment_defaults [seg_index];
+ } else {
+ seg_index = (i.rm.mode<<3) | i.rm.regmem;
+ default_seg = one_byte_segment_defaults [seg_index];
+ }
+ /* If the specified segment is not the default, use an
+ opcode prefix to select it */
+ if (i.seg != default_seg) {
+ if (i.prefixes == MAX_PREFIXES) {
+ as_bad ("%d prefixes given and %s segment override gives too many prefixes",
+ MAX_PREFIXES, i.seg->seg_name);
+ return;
+ }
+ i.prefix[i.prefixes++] = i.seg->seg_prefix;
+ }
+ }
+ }
+
+ /* Fill in i.rm.reg or i.rm.regmem field with register operand
+ (if any) based on t->extension_opcode. Again, we must be careful
+ to make sure that segment/control/debug/test registers are coded
+ into the i.rm.reg field. */
+ if (i.reg_operands) {
+ uint o =
+ (i.types[0] & (Reg|SReg2|SReg3|Control|Debug|Test)) ? 0 :
+ (i.types[1] & (Reg|SReg2|SReg3|Control|Debug|Test)) ? 1 : 2;
+ /* If there is an extension opcode to put here, the register number
+ must be put into the regmem field. */
+ if (t->extension_opcode != None)
+ i.rm.regmem = i.regs[o]->reg_num;
+ else i.rm.reg = i.regs[o]->reg_num;
+
+ /* Now, if no memory operand has set i.rm.mode = 0, 1, 2
+ we must set it to 3 to indicate this is a register operand
+ int the regmem field */
+ if (! i.mem_operands) i.rm.mode = 3;
+ }
+
+ /* Fill in i.rm.reg field with extension opcode (if any). */
+ if (t->extension_opcode != None)
+ i.rm.reg = t->extension_opcode;
+ }
+ }
+ }
+ }
+
+ /* Handle conversion of 'int $3' --> special int3 insn. */
+ if (t->base_opcode == INT_OPCODE && i.imms[0]->X_add_number == 3) {
+ t->base_opcode = INT3_OPCODE;
+ i.imm_operands = 0;
+ }
+
+ /* We are ready to output the insn. */
+ {
+ register char * p;
+
+ /* Output jumps. */
+ if (t->opcode_modifier & Jump) {
+ int n = i.disps[0]->X_add_number;
+
+ switch (i.disps[0]->X_seg) {
+ case SEG_ABSOLUTE:
+ if (FITS_IN_SIGNED_BYTE (n)) {
+ p = frag_more (2);
+ p[0] = t->base_opcode;
+ p[1] = n;
+#if 0 /* leave out 16 bit jumps - pace */
+ } else if (FITS_IN_SIGNED_WORD (n)) {
+ p = frag_more (4);
+ p[0] = WORD_PREFIX_OPCODE;
+ p[1] = t->base_opcode;
+ md_number_to_chars (&p[2], n, 2);
+#endif
+ } else { /* It's an absolute dword displacement. */
+ if (t->base_opcode == JUMP_PC_RELATIVE) { /* pace */
+ /* unconditional jump */
+ p = frag_more (5);
+ p[0] = 0xe9;
+ md_number_to_chars (&p[1], n, 4);
+ } else {
+ /* conditional jump */
+ p = frag_more (6);
+ p[0] = TWO_BYTE_OPCODE_ESCAPE;
+ p[1] = t->base_opcode + 0x10;
+ md_number_to_chars (&p[2], n, 4);
+ }
+ }
+ break;
+ default:
+ /* It's a symbol; end frag & setup for relax.
+ Make sure there are 6 chars left in the current frag; if not
+ we'll have to start a new one. */
+ /* I caught it failing with obstack_room == 6,
+ so I changed to <= pace */
+ if (obstack_room (&frags) <= 6) {
+ frag_wane(frag_now);
+ frag_new (0);
+ }
+ p = frag_more (1);
+ p[0] = t->base_opcode;
+ frag_var (rs_machine_dependent,
+ 6, /* 2 opcode/prefix + 4 displacement */
+ 1,
+ ((uchar) *p == JUMP_PC_RELATIVE
+ ? ENCODE_RELAX_STATE (UNCOND_JUMP, BYTE)
+ : ENCODE_RELAX_STATE (COND_JUMP, BYTE)),
+ i.disps[0]->X_add_symbol,
+ n, p);
+ break;
+ }
+ } else if (t->opcode_modifier & (JumpByte|JumpDword)) {
+ int size = (t->opcode_modifier & JumpByte) ? 1 : 4;
+ int n = i.disps[0]->X_add_number;
+
+ if (FITS_IN_UNSIGNED_BYTE(t->base_opcode)) {
+ FRAG_APPEND_1_CHAR (t->base_opcode);
+ } else {
+ p = frag_more (2); /* opcode can be at most two bytes */
+ /* put out high byte first: can't use md_number_to_chars! */
+ *p++ = (t->base_opcode >> 8) & 0xff;
+ *p = t->base_opcode & 0xff;
+ }
+
+ p = frag_more (size);
+ switch (i.disps[0]->X_seg) {
+ case SEG_ABSOLUTE:
+ md_number_to_chars (p, n, size);
+ if (size == 1 && ! FITS_IN_SIGNED_BYTE (n)) {
+ as_bad ("loop/jecx only takes byte displacement; %d shortened to %d",
+ n, *p);
+ }
+ break;
+ default:
+ fix_new (frag_now, p - frag_now->fr_literal, size,
+ i.disps[0]->X_add_symbol, i.disps[0]->X_subtract_symbol,
+ i.disps[0]->X_add_number, 1);
+ break;
+ }
+ } else if (t->opcode_modifier & JumpInterSegment) {
+ p = frag_more (1 + 2 + 4); /* 1 opcode; 2 segment; 4 offset */
+ p[0] = t->base_opcode;
+ if (i.imms[1]->X_seg == SEG_ABSOLUTE)
+ md_number_to_chars (p + 1, i.imms[1]->X_add_number, 4);
+ else
+ fix_new (frag_now, p + 1 - frag_now->fr_literal, 4,
+ i.imms[1]->X_add_symbol,
+ i.imms[1]->X_subtract_symbol,
+ i.imms[1]->X_add_number, 0);
+ if (i.imms[0]->X_seg != SEG_ABSOLUTE)
+ as_bad ("can't handle non absolute segment in long call/jmp");
+ md_number_to_chars (p + 5, i.imms[0]->X_add_number, 2);
+ } else {
+ /* Output normal instructions here. */
+ register char *q;
+
+ /* First the prefix bytes. */
+ for (q = i.prefix; q < i.prefix + i.prefixes; q++) {
+ p = frag_more (1);
+ md_number_to_chars (p, (uint) *q, 1);
+ }
+
+ /* Now the opcode; be careful about word order here! */
+ if (FITS_IN_UNSIGNED_BYTE(t->base_opcode)) {
+ FRAG_APPEND_1_CHAR (t->base_opcode);
+ } else if (FITS_IN_UNSIGNED_WORD(t->base_opcode)) {
+ p = frag_more (2);
+ /* put out high byte first: can't use md_number_to_chars! */
+ *p++ = (t->base_opcode >> 8) & 0xff;
+ *p = t->base_opcode & 0xff;
+ } else { /* opcode is either 3 or 4 bytes */
+ if (t->base_opcode & 0xff000000) {
+ p = frag_more (4);
+ *p++ = (t->base_opcode >> 24) & 0xff;
+ } else p = frag_more (3);
+ *p++ = (t->base_opcode >> 16) & 0xff;
+ *p++ = (t->base_opcode >> 8) & 0xff;
+ *p = (t->base_opcode ) & 0xff;
+ }
+
+ /* Now the modrm byte and base index byte (if present). */
+ if (t->opcode_modifier & Modrm) {
+ p = frag_more (1);
+ /* md_number_to_chars (p, i.rm, 1); */
+ md_number_to_chars (p, (i.rm.regmem<<0 | i.rm.reg<<3 | i.rm.mode<<6), 1);
+ /* If i.rm.regmem == ESP (4) && i.rm.mode != Mode 3 (Register mode)
+ ==> need second modrm byte. */
+ if (i.rm.regmem == ESCAPE_TO_TWO_BYTE_ADDRESSING && i.rm.mode != 3) {
+ p = frag_more (1);
+ /* md_number_to_chars (p, i.bi, 1); */
+ md_number_to_chars (p,(i.bi.base<<0 | i.bi.index<<3 | i.bi.scale<<6), 1);
+ }
+ }
+
+ if (i.disp_operands) {
+ register int n;
+
+ for (n = 0; n < i.operands; n++) {
+ if (i.disps[n]) {
+ if (i.disps[n]->X_seg == SEG_ABSOLUTE) {
+ if (i.types[n] & (Disp8|Abs8)) {
+ p = frag_more (1);
+ md_number_to_chars (p, i.disps[n]->X_add_number, 1);
+ } else if (i.types[n] & (Disp16|Abs16)) {
+ p = frag_more (2);
+ md_number_to_chars (p, i.disps[n]->X_add_number, 2);
+ } else { /* Disp32|Abs32 */
+ p = frag_more (4);
+ md_number_to_chars (p, i.disps[n]->X_add_number, 4);
+ }
+ } else { /* not SEG_ABSOLUTE */
+ /* need a 32-bit fixup (don't support 8bit non-absolute disps) */
+ p = frag_more (4);
+ fix_new (frag_now, p - frag_now->fr_literal, 4,
+ i.disps[n]->X_add_symbol, i.disps[n]->X_subtract_symbol,
+ i.disps[n]->X_add_number, 0);
+ }
+ }
+ }
+ } /* end displacement output */
+
+ /* output immediate */
+ if (i.imm_operands) {
+ register int n;
+
+ for (n = 0; n < i.operands; n++) {
+ if (i.imms[n]) {
+ if (i.imms[n]->X_seg == SEG_ABSOLUTE) {
+ if (i.types[n] & (Imm8|Imm8S)) {
+ p = frag_more (1);
+ md_number_to_chars (p, i.imms[n]->X_add_number, 1);
+ } else if (i.types[n] & Imm16) {
+ p = frag_more (2);
+ md_number_to_chars (p, i.imms[n]->X_add_number, 2);
+ } else {
+ p = frag_more (4);
+ md_number_to_chars (p, i.imms[n]->X_add_number, 4);
+ }
+ } else { /* not SEG_ABSOLUTE */
+ /* need a 32-bit fixup (don't support 8bit non-absolute ims) */
+ /* try to support other sizes ... */
+ int size;
+ if (i.types[n] & (Imm8|Imm8S))
+ size = 1;
+ else if (i.types[n] & Imm16)
+ size = 2;
+ else
+ size = 4;
+ p = frag_more (size);
+ fix_new (frag_now, p - frag_now->fr_literal, size,
+ i.imms[n]->X_add_symbol, i.imms[n]->X_subtract_symbol,
+ i.imms[n]->X_add_number, 0);
+ }
+ }
+ }
+ } /* end immediate output */
+ }
+
+#ifdef DEBUG386
+ if (flagseen ['D']) {
+ pi (line, &i);
+ }
+#endif /* DEBUG386 */
+
+ }
+ return;
+}
+
+/* Parse OPERAND_STRING into the i386_insn structure I. Returns non-zero
+ on error. */
+
+int i386_operand (operand_string)
+ char *operand_string;
+{
+ register char *op_string = operand_string;
+
+ /* Address of '\0' at end of operand_string. */
+ char * end_of_operand_string = operand_string + strlen(operand_string);
+
+ /* Start and end of displacement string expression (if found). */
+ char * displacement_string_start = 0;
+ char * displacement_string_end;
+
+ /* We check for an absolute prefix (differentiating,
+ for example, 'jmp pc_relative_label' from 'jmp *absolute_label'. */
+ if (*op_string == ABSOLUTE_PREFIX) {
+ op_string++;
+ i.types[this_operand] |= JumpAbsolute;
+ }
+
+ /* Check if operand is a register. */
+ if (*op_string == REGISTER_PREFIX) {
+ register reg_entry * r;
+ if (! (r = parse_register (op_string))) {
+ as_bad ("bad register name ('%s')", op_string);
+ return 0;
+ }
+ /* Check for segment override, rather than segment register by
+ searching for ':' after %<x>s where <x> = s, c, d, e, f, g. */
+ if ((r->reg_type & (SReg2|SReg3)) && op_string[3] == ':') {
+ switch (r->reg_num) {
+ case 0:
+ i.seg = &es; break;
+ case 1:
+ i.seg = &cs; break;
+ case 2:
+ i.seg = &ss; break;
+ case 3:
+ i.seg = &ds; break;
+ case 4:
+ i.seg = &fs; break;
+ case 5:
+ i.seg = &gs; break;
+ }
+ op_string += 4; /* skip % <x> s : */
+ operand_string = op_string; /* Pretend given string starts here. */
+ if (!is_digit_char(*op_string) && !is_identifier_char(*op_string)
+ && *op_string != '(' && *op_string != ABSOLUTE_PREFIX) {
+ as_bad ("bad memory operand after segment override");
+ return 0;
+ }
+ /* Handle case of %es:*foo. */
+ if (*op_string == ABSOLUTE_PREFIX) {
+ op_string++;
+ i.types[this_operand] |= JumpAbsolute;
+ }
+ goto do_memory_reference;
+ }
+ i.types[this_operand] |= r->reg_type;
+ i.regs[this_operand] = r;
+ i.reg_operands++;
+ } else if (*op_string == IMMEDIATE_PREFIX) { /* ... or an immediate */
+ char * save_input_line_pointer;
+ register expressionS *exp;
+ segT exp_seg;
+ if (i.imm_operands == MAX_IMMEDIATE_OPERANDS) {
+ as_bad ("only 1 or 2 immediate operands are allowed");
+ return 0;
+ }
+ exp = &im_expressions[i.imm_operands++];
+ i.imms [this_operand] = exp;
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = ++op_string; /* must advance op_string! */
+ exp_seg = expression (exp);
+ input_line_pointer = save_input_line_pointer;
+ switch (exp_seg) {
+ case SEG_NONE: /* missing or bad expr becomes absolute 0 */
+ as_bad ("missing or invalid immediate expression '%s' taken as 0",
+ operand_string);
+ exp->X_seg = SEG_ABSOLUTE;
+ exp->X_add_number = 0;
+ exp->X_add_symbol = (symbolS *) 0;
+ exp->X_subtract_symbol = (symbolS *) 0;
+ i.types[this_operand] |= Imm;
+ break;
+ case SEG_ABSOLUTE:
+ i.types[this_operand] |= SMALLEST_IMM_TYPE (exp->X_add_number);
+ break;
+ case SEG_TEXT: case SEG_DATA: case SEG_BSS: case SEG_UNKNOWN:
+ i.types[this_operand] |= Imm32; /* this is an address ==> 32bit */
+ break;
+ default:
+seg_unimplemented:
+ as_bad ("Unimplemented segment type %d in parse_operand", exp_seg);
+ return 0;
+ }
+ /* shorten this type of this operand if the instruction wants
+ * fewer bits than are present in the immediate. The bit field
+ * code can put out 'andb $0xffffff, %al', for example. pace
+ * also 'movw $foo,(%eax)'
+ */
+ switch (i.suffix) {
+ case WORD_OPCODE_SUFFIX:
+ i.types[this_operand] |= Imm16;
+ break;
+ case BYTE_OPCODE_SUFFIX:
+ i.types[this_operand] |= Imm16 | Imm8 | Imm8S;
+ break;
+ }
+ } else if (is_digit_char(*op_string) || is_identifier_char(*op_string)
+ || *op_string == '(') {
+ /* This is a memory reference of some sort. */
+ register char * base_string;
+ uint found_base_index_form;
+
+ do_memory_reference:
+ if (i.mem_operands == MAX_MEMORY_OPERANDS) {
+ as_bad ("more than 1 memory reference in instruction");
+ return 0;
+ }
+ i.mem_operands++;
+
+ /* Determine type of memory operand from opcode_suffix;
+ no opcode suffix implies general memory references. */
+ switch (i.suffix) {
+ case BYTE_OPCODE_SUFFIX:
+ i.types[this_operand] |= Mem8;
+ break;
+ case WORD_OPCODE_SUFFIX:
+ i.types[this_operand] |= Mem16;
+ break;
+ case DWORD_OPCODE_SUFFIX:
+ default:
+ i.types[this_operand] |= Mem32;
+ }
+
+ /* Check for base index form. We detect the base index form by
+ looking for an ')' at the end of the operand, searching
+ for the '(' matching it, and finding a REGISTER_PREFIX or ','
+ after it. */
+ base_string = end_of_operand_string - 1;
+ found_base_index_form = FALSE;
+ if (*base_string == ')') {
+ uint parens_balenced = 1;
+ /* We've already checked that the number of left & right ()'s are equal,
+ so this loop will not be infinite. */
+ do {
+ base_string--;
+ if (*base_string == ')') parens_balenced++;
+ if (*base_string == '(') parens_balenced--;
+ } while (parens_balenced);
+ base_string++; /* Skip past '('. */
+ if (*base_string == REGISTER_PREFIX || *base_string == ',')
+ found_base_index_form = TRUE;
+ }
+
+ /* If we can't parse a base index register expression, we've found
+ a pure displacement expression. We set up displacement_string_start
+ and displacement_string_end for the code below. */
+ if (! found_base_index_form) {
+ displacement_string_start = op_string;
+ displacement_string_end = end_of_operand_string;
+ } else {
+ char *base_reg_name, *index_reg_name, *num_string;
+ int num;
+
+ i.types[this_operand] |= BaseIndex;
+
+ /* If there is a displacement set-up for it to be parsed later. */
+ if (base_string != op_string + 1) {
+ displacement_string_start = op_string;
+ displacement_string_end = base_string - 1;
+ }
+
+ /* Find base register (if any). */
+ if (*base_string != ',') {
+ base_reg_name = base_string++;
+ /* skip past register name & parse it */
+ while (isalpha(*base_string)) base_string++;
+ if (base_string == base_reg_name+1) {
+ as_bad ("can't find base register name after '(%c'",
+ REGISTER_PREFIX);
+ return 0;
+ }
+ END_STRING_AND_SAVE (base_string);
+ if (! (i.base_reg = parse_register (base_reg_name))) {
+ as_bad ("bad base register name ('%s')", base_reg_name);
+ return 0;
+ }
+ RESTORE_END_STRING (base_string);
+ }
+
+ /* Now check seperator; must be ',' ==> index reg
+ OR num ==> no index reg. just scale factor
+ OR ')' ==> end. (scale factor = 1) */
+ if (*base_string != ',' && *base_string != ')') {
+ as_bad ("expecting ',' or ')' after base register in `%s'",
+ operand_string);
+ return 0;
+ }
+
+ /* There may index reg here; and there may be a scale factor. */
+ if (*base_string == ',' && *(base_string+1) == REGISTER_PREFIX) {
+ index_reg_name = ++base_string;
+ while (isalpha(*++base_string));
+ END_STRING_AND_SAVE (base_string);
+ if (! (i.index_reg = parse_register(index_reg_name))) {
+ as_bad ("bad index register name ('%s')", index_reg_name);
+ return 0;
+ }
+ RESTORE_END_STRING (base_string);
+ }
+
+ /* Check for scale factor. */
+ if (*base_string == ',' && isdigit(*(base_string+1))) {
+ num_string = ++base_string;
+ while (is_digit_char(*base_string)) base_string++;
+ if (base_string == num_string) {
+ as_bad ("can't find a scale factor after ','");
+ return 0;
+ }
+ END_STRING_AND_SAVE (base_string);
+ /* We've got a scale factor. */
+ if (! sscanf (num_string, "%d", &num)) {
+ as_bad ("can't parse scale factor from '%s'", num_string);
+ return 0;
+ }
+ RESTORE_END_STRING (base_string);
+ switch (num) { /* must be 1 digit scale */
+ case 1: i.log2_scale_factor = 0; break;
+ case 2: i.log2_scale_factor = 1; break;
+ case 4: i.log2_scale_factor = 2; break;
+ case 8: i.log2_scale_factor = 3; break;
+ default:
+ as_bad ("expecting scale factor of 1, 2, 4, 8; got %d", num);
+ return 0;
+ }
+ } else {
+ if (! i.index_reg && *base_string == ',') {
+ as_bad ("expecting index register or scale factor after ','; got '%c'",
+ *(base_string+1));
+ return 0;
+ }
+ }
+ }
+
+ /* If there's an expression begining the operand, parse it,
+ assuming displacement_string_start and displacement_string_end
+ are meaningful. */
+ if (displacement_string_start) {
+ register expressionS * exp;
+ segT exp_seg;
+ char * save_input_line_pointer;
+ exp = &disp_expressions[i.disp_operands];
+ i.disps [this_operand] = exp;
+ i.disp_operands++;
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = displacement_string_start;
+ END_STRING_AND_SAVE (displacement_string_end);
+ exp_seg = expression (exp);
+ if(*input_line_pointer)
+ as_bad("Ignoring junk '%s' after expression",input_line_pointer);
+ RESTORE_END_STRING (displacement_string_end);
+ input_line_pointer = save_input_line_pointer;
+ switch (exp_seg) {
+ case SEG_NONE:
+ /* missing expr becomes absolute 0 */
+ as_bad ("missing or invalid displacement '%s' taken as 0",
+ operand_string);
+ i.types[this_operand] |= (Disp|Abs);
+ exp->X_seg = SEG_ABSOLUTE;
+ exp->X_add_number = 0;
+ exp->X_add_symbol = (symbolS *) 0;
+ exp->X_subtract_symbol = (symbolS *) 0;
+ break;
+ case SEG_ABSOLUTE:
+ i.types[this_operand] |= SMALLEST_DISP_TYPE (exp->X_add_number);
+ break;
+ case SEG_TEXT: case SEG_DATA: case SEG_BSS:
+ case SEG_UNKNOWN: /* must be 32 bit displacement (i.e. address) */
+ i.types[this_operand] |= Disp32;
+ break;
+ default:
+ goto seg_unimplemented;
+ }
+ }
+
+ /* Make sure the memory operand we've been dealt is valid. */
+ if (i.base_reg && i.index_reg &&
+ ! (i.base_reg->reg_type & i.index_reg->reg_type & Reg)) {
+ as_bad ("register size mismatch in (base,index,scale) expression");
+ return 0;
+ }
+ if ((i.base_reg && (i.base_reg->reg_type & Reg32) == 0) ||
+ (i.index_reg && (i.index_reg->reg_type & Reg32) == 0)) {
+ as_bad ("base/index register must be 32 bit register");
+ return 0;
+ }
+ if (i.index_reg && i.index_reg == esp) {
+ as_bad ("%s may not be used as an index register", esp->reg_name);
+ return 0;
+ }
+ } else { /* it's not a memory operand; argh! */
+ as_bad ("invalid char %s begining %s operand '%s'",
+ output_invalid(*op_string), ordinal_names[this_operand],
+ op_string);
+ return 0;
+ }
+ return 1; /* normal return */
+}
+
+/*
+ * md_estimate_size_before_relax()
+ *
+ * Called just before relax().
+ * Any symbol that is now undefined will not become defined.
+ * Return the correct fr_subtype in the frag.
+ * Return the initial "guess for fr_var" to caller.
+ * The guess for fr_var is ACTUALLY the growth beyond fr_fix.
+ * Whatever we do to grow fr_fix or fr_var contributes to our returned value.
+ * Although it may not be explicit in the frag, pretend fr_var starts with a
+ * 0 value.
+ */
+int
+md_estimate_size_before_relax (fragP, segment_type)
+ register fragS * fragP;
+ register int segment_type; /* N_DATA or N_TEXT. */
+{
+ register uchar * opcode;
+ register int old_fr_fix;
+
+ old_fr_fix = fragP -> fr_fix;
+ opcode = (uchar *) fragP -> fr_opcode;
+ /* We've already got fragP->fr_subtype right; all we have to do is check
+ for un-relaxable symbols. */
+ if ((fragP -> fr_symbol -> sy_type & N_TYPE) != segment_type) {
+ /* symbol is undefined in this segment */
+ switch (opcode[0]) {
+ case JUMP_PC_RELATIVE: /* make jmp (0xeb) a dword displacement jump */
+ opcode[0] = 0xe9; /* dword disp jmp */
+ fragP -> fr_fix += 4;
+ fix_new (fragP, old_fr_fix, 4,
+ fragP -> fr_symbol,
+ (symbolS *) 0,
+ fragP -> fr_offset, 1);
+ break;
+
+ default:
+ /* This changes the byte-displacement jump 0x7N -->
+ the dword-displacement jump 0x0f8N */
+ opcode[1] = opcode[0] + 0x10;
+ opcode[0] = TWO_BYTE_OPCODE_ESCAPE; /* two-byte escape */
+ fragP -> fr_fix += 1 + 4; /* we've added an opcode byte */
+ fix_new (fragP, old_fr_fix + 1, 4,
+ fragP -> fr_symbol,
+ (symbolS *) 0,
+ fragP -> fr_offset, 1);
+ break;
+ }
+ frag_wane (fragP);
+ }
+ return (fragP -> fr_var + fragP -> fr_fix - old_fr_fix);
+} /* md_estimate_size_before_relax() */
+
+/*
+ * md_convert_frag();
+ *
+ * Called after relax() is finished.
+ * In: Address of frag.
+ * fr_type == rs_machine_dependent.
+ * fr_subtype is what the address relaxed to.
+ *
+ * Out: Any fixSs and constants are set up.
+ * Caller will turn frag into a ".space 0".
+ */
+void
+md_convert_frag (fragP)
+ register fragS * fragP;
+{
+ register uchar * opcode;
+ uchar * where_to_put_displacement;
+ uint target_address, opcode_address;
+ uint extension;
+ int displacement_from_opcode_start;
+
+ opcode = (uchar *) fragP -> fr_opcode;
+
+ /* Address we want to reach in file space. */
+ target_address = fragP->fr_symbol->sy_value + fragP->fr_offset;
+
+ /* Address opcode resides at in file space. */
+ opcode_address = fragP->fr_address + fragP->fr_fix;
+
+ /* Displacement from opcode start to fill into instruction. */
+ displacement_from_opcode_start = target_address - opcode_address;
+
+ switch (fragP->fr_subtype) {
+ case ENCODE_RELAX_STATE (COND_JUMP, BYTE):
+ case ENCODE_RELAX_STATE (UNCOND_JUMP, BYTE):
+ /* don't have to change opcode */
+ extension = 1; /* 1 opcode + 1 displacement */
+ where_to_put_displacement = &opcode[1];
+ break;
+
+ case ENCODE_RELAX_STATE (COND_JUMP, WORD):
+ opcode[1] = TWO_BYTE_OPCODE_ESCAPE;
+ opcode[2] = opcode[0] + 0x10;
+ opcode[0] = WORD_PREFIX_OPCODE;
+ extension = 4; /* 3 opcode + 2 displacement */
+ where_to_put_displacement = &opcode[3];
+ break;
+
+ case ENCODE_RELAX_STATE (UNCOND_JUMP, WORD):
+ opcode[1] = 0xe9;
+ opcode[0] = WORD_PREFIX_OPCODE;
+ extension = 3; /* 2 opcode + 2 displacement */
+ where_to_put_displacement = &opcode[2];
+ break;
+
+ case ENCODE_RELAX_STATE (COND_JUMP, DWORD):
+ opcode[1] = opcode[0] + 0x10;
+ opcode[0] = TWO_BYTE_OPCODE_ESCAPE;
+ extension = 5; /* 2 opcode + 4 displacement */
+ where_to_put_displacement = &opcode[2];
+ break;
+
+ case ENCODE_RELAX_STATE (UNCOND_JUMP, DWORD):
+ opcode[0] = 0xe9;
+ extension = 4; /* 1 opcode + 4 displacement */
+ where_to_put_displacement = &opcode[1];
+ break;
+
+ default:
+ BAD_CASE(fragP -> fr_subtype);
+ break;
+ }
+ /* now put displacement after opcode */
+ md_number_to_chars (where_to_put_displacement,
+ displacement_from_opcode_start - extension,
+ SIZE_FROM_RELAX_STATE (fragP->fr_subtype));
+ fragP -> fr_fix += extension;
+}
+
+
+int md_short_jump_size = 2; /* size of byte displacement jmp */
+int md_long_jump_size = 5; /* size of dword displacement jmp */
+
+void md_create_short_jump(ptr, from_addr, to_addr)
+ char *ptr;
+ long from_addr, to_addr;
+{
+ long offset;
+
+ offset = to_addr - (from_addr + 2);
+ md_number_to_chars (ptr, (long) 0xeb, 1); /* opcode for byte-disp jump */
+ md_number_to_chars (ptr + 1, offset, 1);
+}
+
+void md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol)
+ char *ptr;
+ long from_addr, to_addr;
+ fragS *frag;
+ symbolS *to_symbol;
+{
+ long offset;
+
+ if (flagseen['m']) {
+ offset = to_addr - to_symbol->sy_value;
+ md_number_to_chars (ptr, 0xe9, 1); /* opcode for long jmp */
+ md_number_to_chars (ptr + 1, offset, 4);
+ fix_new (frag, (ptr+1) - frag->fr_literal, 4,
+ to_symbol, (symbolS *) 0, (long int) 0, 0);
+ } else {
+ offset = to_addr - (from_addr + 5);
+ md_number_to_chars(ptr, (long) 0xe9, 1);
+ md_number_to_chars(ptr + 1, offset, 4);
+ }
+}
+
+int
+md_parse_option(argP,cntP,vecP)
+char **argP;
+int *cntP;
+char ***vecP;
+{
+ return 1;
+}
+
+void /* Knows about order of bytes in address. */
+md_number_to_chars (con, value, nbytes)
+ char con []; /* Return 'nbytes' of chars here. */
+ long int value; /* The value of the bits. */
+ int nbytes; /* Number of bytes in the output. */
+{
+ register char * p = con;
+
+ switch (nbytes) {
+ case 1:
+ p[0] = value & 0xff;
+ break;
+ case 2:
+ p[0] = value & 0xff;
+ p[1] = (value >> 8) & 0xff;
+ break;
+ case 4:
+ p[0] = value & 0xff;
+ p[1] = (value>>8) & 0xff;
+ p[2] = (value>>16) & 0xff;
+ p[3] = (value>>24) & 0xff;
+ break;
+ default:
+ BAD_CASE (nbytes);
+ }
+}
+
+void /* Knows about order of bytes in address. */
+md_number_to_disp (con, value, nbytes)
+ char con []; /* Return 'nbytes' of chars here. */
+ long int value; /* The value of the bits. */
+ int nbytes; /* Number of bytes in the output. */
+{
+ char * answer = alloca (nbytes);
+ register char * p = answer;
+
+ switch (nbytes) {
+ case 1:
+ *p = value;
+ break;
+ case 2:
+ *p++ = value;
+ *p = (value>>8);
+ break;
+ case 4:
+ *p++ = value;
+ *p++ = (value>>8);
+ *p++ = (value>>16);
+ *p = (value>>24);
+ break;
+ default:
+ BAD_CASE (nbytes);
+ }
+ bcopy (answer, con, nbytes);
+}
+
+void /* Knows about order of bytes in address. */
+md_number_to_imm (con, value, nbytes)
+ char con []; /* Return 'nbytes' of chars here. */
+ long int value; /* The value of the bits. */
+ int nbytes; /* Number of bytes in the output. */
+{
+ char * answer = alloca (nbytes);
+ register char * p = answer;
+
+ switch (nbytes) {
+ case 1:
+ *p = value;
+ break;
+ case 2:
+ *p++ = value;
+ *p = (value>>8);
+ break;
+ case 4:
+ *p++ = value;
+ *p++ = (value>>8);
+ *p++ = (value>>16);
+ *p = (value>>24);
+ break;
+ default:
+ BAD_CASE (nbytes);
+ }
+ bcopy (answer, con, nbytes);
+}
+
+void /* Knows about order of bytes in address. */
+md_number_to_field (con, value, nbytes)
+ char con []; /* Return 'nbytes' of chars here. */
+ long int value; /* The value of the bits. */
+ int nbytes; /* Number of bytes in the output. */
+{
+ char * answer = alloca (nbytes);
+ register char * p = answer;
+
+ switch (nbytes) {
+ case 1:
+ *p = value;
+ break;
+ case 2:
+ *p++ = value;
+ *p = (value>>8);
+ break;
+ case 4:
+ *p++ = value;
+ *p++ = (value>>8);
+ *p++ = (value>>16);
+ *p = (value>>24);
+ break;
+ default:
+ BAD_CASE (nbytes);
+ }
+ bcopy (answer, con, nbytes);
+}
+
+long int /* Knows about the byte order in a word. */
+md_chars_to_number (con, nbytes)
+unsigned char con[]; /* Low order byte 1st. */
+ int nbytes; /* Number of bytes in the input. */
+{
+ long int retval;
+ for (retval=0, con+=nbytes-1; nbytes--; con--)
+ {
+ retval <<= BITS_PER_CHAR;
+ retval |= *con;
+ }
+ return retval;
+}
+
+void md_ri_to_chars(ri_p, ri)
+ struct relocation_info *ri_p, ri;
+{
+ unsigned char the_bytes[8];
+
+ /* this is easy */
+ md_number_to_chars(the_bytes, ri.r_address, sizeof(ri.r_address));
+ /* now the fun stuff */
+ the_bytes[6] = (ri.r_symbolnum >> 16) & 0x0ff;
+ the_bytes[5] = (ri.r_symbolnum >> 8) & 0x0ff;
+ the_bytes[4] = ri.r_symbolnum & 0x0ff;
+ the_bytes[7] = (((ri.r_extern << 3) & 0x08) | ((ri.r_length << 1) & 0x06) |
+ ((ri.r_pcrel << 0) & 0x01)) & 0x0F;
+ /* now put it back where you found it */
+ bcopy (the_bytes, (char *)ri_p, sizeof(struct relocation_info));
+}
+
+
+#define MAX_LITTLENUMS 6
+
+/* Turn the string pointed to by litP into a floating point constant of type
+ type, and emit the appropriate bytes. The number of LITTLENUMS emitted
+ is stored in *sizeP . An error message is returned, or NULL on OK.
+ */
+char *
+md_atof(type,litP,sizeP)
+ char type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+ char *atof_ieee();
+
+ switch(type) {
+ case 'f':
+ case 'F':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 5;
+ break;
+
+ default:
+ *sizeP=0;
+ return "Bad call to md_atof ()";
+ }
+ t = atof_ieee (input_line_pointer,type,words);
+ if(t)
+ input_line_pointer=t;
+
+ *sizeP = prec * sizeof(LITTLENUM_TYPE);
+ /* this loops outputs the LITTLENUMs in REVERSE order; in accord with
+ the bigendian 386 */
+ for(wordP = words + prec - 1;prec--;) {
+ md_number_to_chars (litP, (long) (*wordP--), sizeof(LITTLENUM_TYPE));
+ litP += sizeof(LITTLENUM_TYPE);
+ }
+ return ""; /* Someone should teach Dean about null pointers */
+}
+
+char output_invalid_buf[8];
+
+char * output_invalid (c)
+ char c;
+{
+ if (isprint(c)) sprintf (output_invalid_buf, "'%c'", c);
+ else sprintf (output_invalid_buf, "(0x%x)", c);
+ return output_invalid_buf;
+}
+
+reg_entry *parse_register (reg_string)
+ char *reg_string; /* reg_string starts *before* REGISTER_PREFIX */
+{
+ register char *s = reg_string;
+ register char *p;
+ char reg_name_given[MAX_REG_NAME_SIZE];
+
+ s++; /* skip REGISTER_PREFIX */
+ for (p = reg_name_given; is_register_char (*s); p++, s++) {
+ *p = register_chars [*s];
+ if (p >= reg_name_given + MAX_REG_NAME_SIZE)
+ return (reg_entry *) 0;
+ }
+ *p = '\0';
+ return (reg_entry *) hash_find (reg_hash, reg_name_given);
+}
+
diff --git a/gnu/usr.bin/as/config/i386.h b/gnu/usr.bin/as/config/i386.h
new file mode 100644
index 0000000..c569c1c
--- /dev/null
+++ b/gnu/usr.bin/as/config/i386.h
@@ -0,0 +1,296 @@
+/* i386.h -- Header file for i386.c
+ Copyright (C) 1989, Free Software Foundation.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define MAX_OPERANDS 3 /* max operands per insn */
+#define MAX_PREFIXES 4 /* max prefixes per opcode */
+#define MAX_IMMEDIATE_OPERANDS 2 /* max immediates per insn */
+#define MAX_MEMORY_OPERANDS 2 /* max memory ref per insn
+ * lcall uses 2
+ */
+/* we define the syntax here (modulo base,index,scale syntax) */
+#define REGISTER_PREFIX '%'
+#define IMMEDIATE_PREFIX '$'
+#define ABSOLUTE_PREFIX '*'
+#define PREFIX_SEPERATOR '/'
+
+#define TWO_BYTE_OPCODE_ESCAPE 0x0f
+
+/* register numbers */
+#define EBP_REG_NUM 5
+#define ESP_REG_NUM 4
+
+/* modrm_byte.regmem for twobyte escape */
+#define ESCAPE_TO_TWO_BYTE_ADDRESSING ESP_REG_NUM
+/* index_base_byte.index for no index register addressing */
+#define NO_INDEX_REGISTER ESP_REG_NUM
+/* index_base_byte.base for no base register addressing */
+#define NO_BASE_REGISTER EBP_REG_NUM
+
+/* these are the att as opcode suffixes, making movl --> mov, for example */
+#define DWORD_OPCODE_SUFFIX 'l'
+#define WORD_OPCODE_SUFFIX 'w'
+#define BYTE_OPCODE_SUFFIX 'b'
+
+/* modrm.mode = REGMEM_FIELD_HAS_REG when a register is in there */
+#define REGMEM_FIELD_HAS_REG 0x3 /* always = 0x3 */
+#define REGMEM_FIELD_HAS_MEM (~REGMEM_FIELD_HAS_REG)
+
+#define END_OF_INSN '\0'
+
+/*
+When an operand is read in it is classified by its type. This type includes
+all the possible ways an operand can be used. Thus, '%eax' is both 'register
+# 0' and 'The Accumulator'. In our language this is expressed by OR'ing
+'Reg32' (any 32 bit register) and 'Acc' (the accumulator).
+Operands are classified so that we can match given operand types with
+the opcode table in i386-opcode.h.
+ */
+#define Unknown 0x0
+/* register */
+#define Reg8 0x1 /* 8 bit reg */
+#define Reg16 0x2 /* 16 bit reg */
+#define Reg32 0x4 /* 32 bit reg */
+#define Reg (Reg8|Reg16|Reg32) /* gen'l register */
+#define WordReg (Reg16|Reg32) /* for push/pop operands */
+/* immediate */
+#define Imm8 0x8 /* 8 bit immediate */
+#define Imm8S 0x10 /* 8 bit immediate sign extended */
+#define Imm16 0x20 /* 16 bit immediate */
+#define Imm32 0x40 /* 32 bit immediate */
+#define Imm1 0x80 /* 1 bit immediate */
+#define ImmUnknown Imm32 /* for unknown expressions */
+#define Imm (Imm8|Imm8S|Imm16|Imm32) /* gen'l immediate */
+/* memory */
+#define Disp8 0x200 /* 8 bit displacement (for jumps) */
+#define Disp16 0x400 /* 16 bit displacement */
+#define Disp32 0x800 /* 32 bit displacement */
+#define Disp (Disp8|Disp16|Disp32) /* General displacement */
+#define DispUnknown Disp32 /* for unknown size displacements */
+#define Mem8 0x1000
+#define Mem16 0x2000
+#define Mem32 0x4000
+#define BaseIndex 0x8000
+#define Mem (Disp|Mem8|Mem16|Mem32|BaseIndex) /* General memory */
+#define WordMem (Mem16|Mem32|Disp|BaseIndex)
+#define ByteMem (Mem8|Disp|BaseIndex)
+/* specials */
+#define InOutPortReg 0x10000 /* register to hold in/out port addr = dx */
+#define ShiftCount 0x20000 /* register to hold shift cound = cl */
+#define Control 0x40000 /* Control register */
+#define Debug 0x80000 /* Debug register */
+#define Test 0x100000 /* Test register */
+#define FloatReg 0x200000 /* Float register */
+#define FloatAcc 0x400000 /* Float stack top %st(0) */
+#define SReg2 0x800000 /* 2 bit segment register */
+#define SReg3 0x1000000 /* 3 bit segment register */
+#define Acc 0x2000000 /* Accumulator %al or %ax or %eax */
+#define ImplicitRegister (InOutPortReg|ShiftCount|Acc|FloatAcc)
+#define JumpAbsolute 0x4000000
+#define Abs8 0x08000000
+#define Abs16 0x10000000
+#define Abs32 0x20000000
+#define Abs (Abs8|Abs16|Abs32)
+
+#define MODE_FROM_DISP_SIZE(t) \
+ ((t&(Disp8)) ? 1 : \
+ ((t&(Disp32)) ? 2 : 0))
+
+#define Byte (Reg8|Imm8|Imm8S)
+#define Word (Reg16|Imm16)
+#define DWord (Reg32|Imm32)
+
+/* convert opcode suffix ('b' 'w' 'l' typically) into type specifyer */
+#define OPCODE_SUFFIX_TO_TYPE(s) \
+ (s == BYTE_OPCODE_SUFFIX ? Byte : \
+ (s == WORD_OPCODE_SUFFIX ? Word : DWord))
+
+#define FITS_IN_SIGNED_BYTE(num) ((num) >= -128 && (num) <= 127)
+#define FITS_IN_UNSIGNED_BYTE(num) ((num) >= 0 && (num) <= 255)
+#define FITS_IN_UNSIGNED_WORD(num) ((num) >= 0 && (num) <= 65535)
+#define FITS_IN_SIGNED_WORD(num) ((num) >= -32768 && (num) <= 32767)
+
+#define SMALLEST_DISP_TYPE(num) \
+ FITS_IN_SIGNED_BYTE(num) ? (Disp8|Disp32|Abs8|Abs32) : (Disp32|Abs32)
+
+#define SMALLEST_IMM_TYPE(num) \
+ (num == 1) ? (Imm1|Imm8|Imm8S|Imm16|Imm32): \
+ FITS_IN_SIGNED_BYTE(num) ? (Imm8S|Imm8|Imm16|Imm32) : \
+ FITS_IN_UNSIGNED_BYTE(num) ? (Imm8|Imm16|Imm32): \
+ (FITS_IN_SIGNED_WORD(num)||FITS_IN_UNSIGNED_WORD(num)) ? (Imm16|Imm32) : \
+ (Imm32)
+
+typedef unsigned char uchar;
+typedef unsigned int uint;
+
+typedef struct {
+ /* instruction name sans width suffix ("mov" for movl insns) */
+ char *name;
+
+ /* how many operands */
+ uint operands;
+
+ /* base_opcode is the fundamental opcode byte with a optional prefix(es). */
+ uint base_opcode;
+
+ /* extension_opcode is the 3 bit extension for group <n> insns.
+ If this template has no extension opcode (the usual case) use None */
+ uchar extension_opcode;
+#define None 0xff /* If no extension_opcode is possible. */
+
+ /* the bits in opcode_modifier are used to generate the final opcode from
+ the base_opcode. These bits also are used to detect alternate forms of
+ the same instruction */
+ uint opcode_modifier;
+
+/* opcode_modifier bits: */
+#define W 0x1 /* set if operands are words or dwords */
+#define D 0x2 /* D = 0 if Reg --> Regmem; D = 1 if Regmem --> Reg */
+/* direction flag for floating insns: MUST BE 0x400 */
+#define FloatD 0x400
+/* shorthand */
+#define DW (D|W)
+#define ShortForm 0x10 /* register is in low 3 bits of opcode */
+#define ShortFormW 0x20 /* ShortForm and W bit is 0x8 */
+#define Seg2ShortForm 0x40 /* encoding of load segment reg insns */
+#define Seg3ShortForm 0x80 /* fs/gs segment register insns. */
+#define Jump 0x100 /* special case for jump insns. */
+#define JumpInterSegment 0x200 /* special case for intersegment leaps/calls */
+/* 0x400 CANNOT BE USED since it's already used by FloatD above */
+#define DONT_USE 0x400
+#define NoModrm 0x800
+#define Modrm 0x1000
+#define imulKludge 0x2000
+#define JumpByte 0x4000
+#define JumpDword 0x8000
+#define ReverseRegRegmem 0x10000
+
+ /* (opcode_modifier & COMES_IN_ALL_SIZES) is true if the
+ instuction comes in byte, word, and dword sizes and is encoded into
+ machine code in the canonical way. */
+#define COMES_IN_ALL_SIZES (W)
+
+ /* (opcode_modifier & COMES_IN_BOTH_DIRECTIONS) indicates that the
+ source and destination operands can be reversed by setting either
+ the D (for integer insns) or the FloatD (for floating insns) bit
+ in base_opcode. */
+#define COMES_IN_BOTH_DIRECTIONS (D|FloatD)
+
+ /* operand_types[i] describes the type of operand i. This is made
+ by OR'ing together all of the possible type masks. (e.g.
+ 'operand_types[i] = Reg|Imm' specifies that operand i can be
+ either a register or an immediate operand */
+ uint operand_types[3];
+} template;
+
+/*
+ 'templates' is for grouping together 'template' structures for opcodes
+ of the same name. This is only used for storing the insns in the grand
+ ole hash table of insns.
+ The templates themselves start at START and range up to (but not including)
+ END.
+*/
+typedef struct {
+ template *start;
+ template *end;
+} templates;
+
+/* these are for register name --> number & type hash lookup */
+typedef struct {
+ char * reg_name;
+ uint reg_type;
+ uint reg_num;
+} reg_entry;
+
+typedef struct {
+ char * seg_name;
+ uint seg_prefix;
+} seg_entry;
+
+/* these are for prefix name --> prefix code hash lookup */
+typedef struct {
+ char * prefix_name;
+ uchar prefix_code;
+} prefix_entry;
+
+/* 386 operand encoding bytes: see 386 book for details of this. */
+typedef struct {
+ unsigned regmem:3; /* codes register or memory operand */
+ unsigned reg:3; /* codes register operand (or extended opcode) */
+ unsigned mode:2; /* how to interpret regmem & reg */
+} modrm_byte;
+
+/* 386 opcode byte to code indirect addressing. */
+typedef struct {
+ unsigned base:3;
+ unsigned index:3;
+ unsigned scale:2;
+} base_index_byte;
+
+/* 'md_assemble ()' gathers together information and puts it into a
+ i386_insn. */
+
+typedef struct {
+ /* TM holds the template for the insn were currently assembling. */
+ template tm;
+ /* SUFFIX holds the opcode suffix (e.g. 'l' for 'movl') if given. */
+ char suffix;
+ /* Operands are coded with OPERANDS, TYPES, DISPS, IMMS, and REGS. */
+
+ /* OPERANDS gives the number of given operands. */
+ uint operands;
+
+ /* REG_OPERANDS, DISP_OPERANDS, MEM_OPERANDS, IMM_OPERANDS give the number of
+ given register, displacement, memory operands and immediate operands. */
+ uint reg_operands, disp_operands, mem_operands, imm_operands;
+
+ /* TYPES [i] is the type (see above #defines) which tells us how to
+ search through DISPS [i] & IMMS [i] & REGS [i] for the required
+ operand. */
+ uint types [MAX_OPERANDS];
+
+ /* Displacements (if given) for each operand. */
+ expressionS * disps [MAX_OPERANDS];
+
+ /* Immediate operands (if given) for each operand. */
+ expressionS * imms [MAX_OPERANDS];
+
+ /* Register operands (if given) for each operand. */
+ reg_entry * regs [MAX_OPERANDS];
+
+ /* BASE_REG, INDEX_REG, and LOG2_SCALE_FACTOR are used to encode
+ the base index byte below. */
+ reg_entry * base_reg;
+ reg_entry * index_reg;
+ uint log2_scale_factor;
+
+ /* SEG gives the seg_entry of this insn. It is equal to zero unless
+ an explicit segment override is given. */
+ seg_entry * seg; /* segment for memory operands (if given) */
+
+ /* PREFIX holds all the given prefix opcodes (usually null).
+ PREFIXES is the size of PREFIX. */
+ char prefix [MAX_PREFIXES];
+ uint prefixes;
+
+ /* RM and IB are the modrm byte and the base index byte where the addressing
+ modes of this insn are encoded. */
+
+ modrm_byte rm;
+ base_index_byte bi;
+} i386_insn;
diff --git a/gnu/usr.bin/as/config/mh-i386 b/gnu/usr.bin/as/config/mh-i386
new file mode 100644
index 0000000..3375d42
--- /dev/null
+++ b/gnu/usr.bin/as/config/mh-i386
@@ -0,0 +1 @@
+ALLOCA=alloca.o
diff --git a/gnu/usr.bin/as/config/mh-i386aix b/gnu/usr.bin/as/config/mh-i386aix
new file mode 100644
index 0000000..a1e5d77
--- /dev/null
+++ b/gnu/usr.bin/as/config/mh-i386aix
@@ -0,0 +1,5 @@
+# Define SYSV as -DSYSV if you are using a System V operating system.
+SYSV = -DSYSV
+RANLIB = /bin/true
+CC = gcc
+MINUS_G = -O
diff --git a/gnu/usr.bin/as/config/mh-i386v4 b/gnu/usr.bin/as/config/mh-i386v4
new file mode 100644
index 0000000..5bfcd28
--- /dev/null
+++ b/gnu/usr.bin/as/config/mh-i386v4
@@ -0,0 +1 @@
+HLIBS=-lucb
diff --git a/gnu/usr.bin/as/config/mt-ebmon29k b/gnu/usr.bin/as/config/mt-ebmon29k
new file mode 100644
index 0000000..528e6fc
--- /dev/null
+++ b/gnu/usr.bin/as/config/mt-ebmon29k
@@ -0,0 +1,6 @@
+TARG_CPU_DEPENDENTS=
+LOCAL_LOADLIBES=../bfd$(subdir)/libbfd.a
+TDEFINES=-DBFD_HEADERS -DMANY_SEGMENTS -DBFD
+
+
+
diff --git a/gnu/usr.bin/as/config/mt-h8300 b/gnu/usr.bin/as/config/mt-h8300
new file mode 100644
index 0000000..d968db2
--- /dev/null
+++ b/gnu/usr.bin/as/config/mt-h8300
@@ -0,0 +1,5 @@
+TARG_CPU_DEPENDENTS=$(srcdir)/../include/opcode/h8300.h
+LOCAL_LOADLIBES=$(srcdir)/../bfd/$(srcdir)/libbfd.a
+TDEFINES=-DBFD_HEADERS -DMANY_SEGMENTS -DBFD
+
+CC=gcc
diff --git a/gnu/usr.bin/as/config/mt-h8300hds b/gnu/usr.bin/as/config/mt-h8300hds
new file mode 100644
index 0000000..1e6eb3c
--- /dev/null
+++ b/gnu/usr.bin/as/config/mt-h8300hds
@@ -0,0 +1,4 @@
+TARG_CPU_DEPENDENTS=$(srcdir)/../include/h8300-opcode.h
+LOCAL_LOADLIBES=$(srcdir)/../bfd/$(srcdir)/libbfd.a
+TDEFINES=-DBFD -DMANY_SEGMENTS
+
diff --git a/gnu/usr.bin/as/config/mt-i386aix b/gnu/usr.bin/as/config/mt-i386aix
new file mode 100644
index 0000000..225fc36
--- /dev/null
+++ b/gnu/usr.bin/as/config/mt-i386aix
@@ -0,0 +1,3 @@
+# TDEFINES = -DBFD_HEADERS
+CC = gcc
+MINUS_G = -O
diff --git a/gnu/usr.bin/as/config/mt-mips b/gnu/usr.bin/as/config/mt-mips
new file mode 100644
index 0000000..f40f51d
--- /dev/null
+++ b/gnu/usr.bin/as/config/mt-mips
@@ -0,0 +1 @@
+ALL=fake-as
diff --git a/gnu/usr.bin/as/config/mt-rs6000 b/gnu/usr.bin/as/config/mt-rs6000
new file mode 100644
index 0000000..f40f51d
--- /dev/null
+++ b/gnu/usr.bin/as/config/mt-rs6000
@@ -0,0 +1 @@
+ALL=fake-as
diff --git a/gnu/usr.bin/as/config/obj-aout.c b/gnu/usr.bin/as/config/obj-aout.c
new file mode 100644
index 0000000..30eb2d1
--- /dev/null
+++ b/gnu/usr.bin/as/config/obj-aout.c
@@ -0,0 +1,648 @@
+/* a.out object file format
+ Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with GAS; see the file COPYING. If not, write
+ to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "as.h"
+#include "obstack.h"
+
+
+#ifndef NO_LISTING
+#include "aout/stab_gnu.h"
+#endif /* NO_LISTING */
+
+/* in: segT out: N_TYPE bits */
+const short seg_N_TYPE[] = {
+ N_ABS,
+ N_TEXT,
+ N_DATA,
+ N_BSS,
+ N_UNDF, /* unknown */
+ N_UNDF, /* absent */
+ N_UNDF, /* pass1 */
+ N_UNDF, /* error */
+ N_UNDF, /* bignum/flonum */
+ N_UNDF, /* difference */
+ N_UNDF, /* debug */
+ N_UNDF, /* ntv */
+ N_UNDF, /* ptv */
+ N_REGISTER, /* register */
+};
+
+const segT N_TYPE_seg[N_TYPE+2] = { /* N_TYPE == 0x1E = 32-2 */
+ SEG_UNKNOWN, /* N_UNDF == 0 */
+ SEG_GOOF,
+ SEG_ABSOLUTE, /* N_ABS == 2 */
+ SEG_GOOF,
+ SEG_TEXT, /* N_TEXT == 4 */
+ SEG_GOOF,
+ SEG_DATA, /* N_DATA == 6 */
+ SEG_GOOF,
+ SEG_BSS, /* N_BSS == 8 */
+ SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_REGISTER, /* dummy N_REGISTER for regs = 30 */
+ SEG_GOOF,
+};
+
+#if __STDC__ == 1
+static void obj_aout_stab(int what);
+static void obj_aout_line(void);
+static void obj_aout_desc(void);
+#else /* not __STDC__ */
+static void obj_aout_desc();
+static void obj_aout_stab();
+static void obj_aout_line();
+#endif /* not __STDC__ */
+
+const pseudo_typeS obj_pseudo_table[] = {
+#ifndef IGNORE_DEBUG
+ /* stabs debug info */
+ { "line", obj_aout_line, 0 }, /* source code line number */
+ { "ln", obj_aout_line, 0 }, /* coff line number that we use anyway */
+ { "desc", obj_aout_desc, 0 }, /* desc */
+ { "stabd", obj_aout_stab, 'd' }, /* stabs */
+ { "stabn", obj_aout_stab, 'n' }, /* stabs */
+ { "stabs", obj_aout_stab, 's' }, /* stabs */
+#else /* IGNORE_DEBUG */
+ { "line", obj_aout_line, 0 }, /* source code line number */
+ { "ln", obj_aout_line, 0 }, /* coff line number that we use anyway */
+ { "desc", obj_aout_desc, 0 }, /* desc */
+ { "stabd", obj_aout_stab, 'd' }, /* stabs */
+ { "stabn", obj_aout_stab, 'n' }, /* stabs */
+ { "stabs", obj_aout_stab, 's' }, /* stabs */
+#endif /* IGNORE_DEBUG */
+
+ /* coff debug pseudos (ignored) */
+ { "def", s_ignore, 0 },
+ { "dim", s_ignore, 0 },
+ { "endef", s_ignore, 0 },
+ { "ident", s_ignore, 0 },
+ { "line", s_ignore, 0 },
+ { "ln", s_ignore, 0 },
+ { "scl", s_ignore, 0 },
+ { "size", s_size, 0 },
+ { "tag", s_ignore, 0 },
+ { "type", s_type, 0 },
+ { "val", s_ignore, 0 },
+ { "version", s_ignore, 0 },
+
+ /* stabs-in-coff (?) debug pseudos (ignored) */
+ { "optim", s_ignore, 0 }, /* For sun386i cc (?) */
+
+ /* other stuff */
+ { "ABORT", s_abort, 0 },
+
+ { NULL} /* end sentinel */
+}; /* obj_pseudo_table */
+
+
+/* Relocation. */
+
+/*
+ * emit_relocations()
+ *
+ * Crawl along a fixS chain. Emit the segment's relocations.
+ */
+void obj_emit_relocations(where, fixP, segment_address_in_file)
+char **where;
+fixS *fixP; /* Fixup chain for this segment. */
+relax_addressT segment_address_in_file;
+{
+ for (; fixP; fixP = fixP->fx_next) {
+ if (fixP->fx_addsy != NULL) {
+ tc_aout_fix_to_chars(*where, fixP, segment_address_in_file);
+ *where += md_reloc_size;
+ } /* if there is an add symbol */
+ } /* for each fix */
+
+ return;
+} /* obj_emit_relocations() */
+
+/* Aout file generation & utilities */
+void obj_header_append(where, headers)
+char **where;
+object_headers *headers;
+{
+ tc_headers_hook(headers);
+
+#if defined(OLD_GAS) && defined(TC_I386)
+ /* I think that this old behaviour was wrong, but this lets me compare to the
+ previous gas. xoxorich. */
+ md_number_to_chars(*where, headers->header.a_info, 2);
+ *where += 2;
+ md_number_to_chars(*where, 0, 2);
+ *where += 2;
+#else /* not (TC_I386 && OLD_GAS) */
+ md_number_to_chars(*where, headers->header.a_info, sizeof(headers->header.a_info));
+ *where += sizeof(headers->header.a_info);
+#endif /* not (TC_I386 && OLD_GAS) */
+
+#ifdef TE_HPUX
+ md_number_to_chars(*where, 0, 4); *where += 4; /* a_spare1 */
+ md_number_to_chars(*where, 0, 4); *where += 4; /* a_spare2 */
+#endif /* TE_HPUX */
+
+ md_number_to_chars(*where, headers->header.a_text, 4); *where += 4;
+ md_number_to_chars(*where, headers->header.a_data, 4); *where += 4;
+ md_number_to_chars(*where, headers->header.a_bss, 4); *where += 4;
+
+#ifndef TE_HPUX
+ md_number_to_chars(*where, headers->header.a_syms, 4); *where += 4;
+ md_number_to_chars(*where, headers->header.a_entry, 4); *where += 4;
+#endif /* not TE_HPUX */
+
+ md_number_to_chars(*where, headers->header.a_trsize, 4); *where += 4;
+ md_number_to_chars(*where, headers->header.a_drsize, 4); *where += 4;
+
+#ifdef TE_SEQUENT
+ memset(*where, '\0', 3 * 2 * 4); *where += 3 * 2 * 4; /* global descriptor table? */
+ md_number_to_chars(*where, 0, 4); *where += 4; /* shdata - length of initialized shared data */
+ md_number_to_chars(*where, 0, 4); *where += 4; /* shbss - length of uninitialized shared data */
+ md_number_to_chars(*where, 0, 4); *where += 4; /* shdrsize - length of shared data relocation */
+
+ memset(*where, '\0', 11 * 4); *where += 11 * 4; /* boostrap for standalone */
+ memset(*where, '\0', 3 * 4); *where += 3 * 4; /* reserved */
+ md_number_to_chars(*where, 0, 4); *where += 4; /* version */
+#endif /* TE_SEQUENT */
+
+#ifdef TE_HPUX
+ md_number_to_chars(*where, 0, 4); *where += 4; /* a_spare3 - HP = pascal interface size */
+ md_number_to_chars(*where, 0, 4); *where += 4; /* a_spare4 - HP = symbol table size */
+ md_number_to_chars(*where, 0, 4); *where += 4; /* a_spare5 - HP = debug name table size */
+
+ md_number_to_chars(*where, headers->header.a_entry, 4); *where += 4;
+
+ md_number_to_chars(*where, 0, 4); *where += 4; /* a_spare6 - HP = source line table size */
+ md_number_to_chars(*where, 0, 4); *where += 4; /* a_spare7 - HP = value table size */
+
+ md_number_to_chars(*where, headers->header.a_syms, 4); *where += 4;
+
+ md_number_to_chars(*where, 0, 4); *where += 4; /* a_spare8 */
+#endif /* TE_HPUX */
+
+ return;
+} /* obj_append_header() */
+
+void obj_symbol_to_chars(where, symbolP)
+char **where;
+symbolS *symbolP;
+{
+ md_number_to_chars((char *)&(S_GET_OFFSET(symbolP)), S_GET_OFFSET(symbolP), sizeof(S_GET_OFFSET(symbolP)));
+ md_number_to_chars((char *)&(S_GET_DESC(symbolP)), S_GET_DESC(symbolP), sizeof(S_GET_DESC(symbolP)));
+ md_number_to_chars((char *)&(S_GET_VALUE(symbolP)), S_GET_VALUE(symbolP), sizeof(S_GET_VALUE(symbolP)));
+
+ append(where, (char *)&symbolP->sy_symbol, sizeof(obj_symbol_type));
+} /* obj_symbol_to_chars() */
+
+void obj_emit_symbols(where, symbol_rootP)
+char **where;
+symbolS *symbol_rootP;
+{
+ symbolS * symbolP;
+
+ /*
+ * Emit all symbols left in the symbol chain.
+ */
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) {
+ /* Used to save the offset of the name. It is used to point
+ to the string in memory but must be a file offset. */
+ register char *temp;
+
+ temp = S_GET_NAME(symbolP);
+ S_SET_OFFSET(symbolP, symbolP->sy_name_offset);
+
+ /*
+ * Put aux info in lower four bits of `n_other' field
+ * Do this only now, because things like S_IS_DEFINED()
+ * depend on S_GET_OTHER() for some unspecified reason.
+ */
+ if (symbolP->sy_aux)
+ S_SET_OTHER(symbolP, (symbolP->sy_aux & 0xf));
+
+ /* Any symbol still undefined and is not a dbg symbol is made N_EXT. */
+ if (!S_IS_DEBUG(symbolP) && !S_IS_DEFINED(symbolP))
+ S_SET_EXTERNAL(symbolP);
+
+ if (S_GET_TYPE(symbolP) == N_SIZE) {
+ expressionS *exp = (expressionS*)symbolP->sy_sizexp;
+ long size = 0;
+
+ if (exp == NULL) {
+ as_bad("Internal error: no size expression");
+ return;
+ }
+
+ switch (exp->X_seg) {
+ case SEG_ABSOLUTE:
+ size = exp->X_add_number;
+ break;
+ case SEG_DIFFERENCE:
+ size = S_GET_VALUE(exp->X_add_symbol) -
+ S_GET_VALUE(exp->X_subtract_symbol) +
+ exp->X_add_number;
+ break;
+ default:
+ as_bad("Unsupported .size expression");
+ break;
+ }
+ S_SET_VALUE(symbolP, size);
+ }
+
+ obj_symbol_to_chars(where, symbolP);
+ S_SET_NAME(symbolP,temp);
+ }
+} /* emit_symbols() */
+
+#if comment
+/* uneeded if symbol is born zeroed. */
+void obj_symbol_new_hook(symbolP)
+symbolS *symbolP;
+{
+ S_SET_OTHER(symbolP, 0);
+ S_SET_DESC(symbolP, 0);
+ return;
+} /* obj_symbol_new_hook() */
+#endif /* comment */
+
+static void obj_aout_line() {
+ /* Assume delimiter is part of expression. */
+ /* BSD4.2 as fails with delightful bug, so we */
+ /* are not being incompatible here. */
+ new_logical_line((char *)NULL, (int)(get_absolute_expression()));
+ demand_empty_rest_of_line();
+} /* obj_aout_line() */
+
+/*
+ * stab()
+ *
+ * Handle .stabX directives, which used to be open-coded.
+ * So much creeping featurism overloaded the semantics that we decided
+ * to put all .stabX thinking in one place. Here.
+ *
+ * We try to make any .stabX directive legal. Other people's AS will often
+ * do assembly-time consistency checks: eg assigning meaning to n_type bits
+ * and "protecting" you from setting them to certain values. (They also zero
+ * certain bits before emitting symbols. Tut tut.)
+ *
+ * If an expression is not absolute we either gripe or use the relocation
+ * information. Other people's assemblers silently forget information they
+ * don't need and invent information they need that you didn't supply.
+ *
+ * .stabX directives always make a symbol table entry. It may be junk if
+ * the rest of your .stabX directive is malformed.
+ */
+static void obj_aout_stab(what)
+int what;
+{
+#ifndef NO_LISTING
+ extern int listing;
+#endif /* NO_LISTING */
+
+ register symbolS *symbolP = 0;
+ register char *string;
+ int saved_type = 0;
+ int length;
+ int goof; /* TRUE if we have aborted. */
+ long longint;
+
+ /*
+ * Enter with input_line_pointer pointing past .stabX and any following
+ * whitespace.
+ */
+ goof = 0; /* JF who forgot this?? */
+ if (what == 's') {
+ string = demand_copy_C_string(& length);
+ SKIP_WHITESPACE();
+ if (* input_line_pointer == ',')
+ input_line_pointer ++;
+ else {
+ as_bad("I need a comma after symbol's name");
+ goof = 1;
+ }
+ } else
+ string = "";
+
+ /*
+ * Input_line_pointer->after ','. String->symbol name.
+ */
+ if (! goof) {
+ symbolP = symbol_new(string,
+ SEG_UNKNOWN,
+ 0,
+ (struct frag *)0);
+ switch (what) {
+ case 'd':
+ S_SET_NAME(symbolP, NULL); /* .stabd feature. */
+ S_SET_VALUE(symbolP, obstack_next_free(&frags) - frag_now->fr_literal);
+ symbolP->sy_frag = frag_now;
+ break;
+
+ case 'n':
+ symbolP->sy_frag = &zero_address_frag;
+ break;
+
+ case 's':
+ symbolP->sy_frag = & zero_address_frag;
+ break;
+
+ default:
+ BAD_CASE(what);
+ break;
+ }
+
+ if (get_absolute_expression_and_terminator(&longint) == ',')
+ symbolP->sy_symbol.n_type = saved_type = longint;
+ else {
+ as_bad("I want a comma after the n_type expression");
+ goof = 1;
+ input_line_pointer --; /* Backup over a non-',' char. */
+ }
+ }
+
+ if (!goof) {
+ if (get_absolute_expression_and_terminator(&longint) == ',')
+ S_SET_OTHER(symbolP, longint);
+ else {
+ as_bad("I want a comma after the n_other expression");
+ goof = 1;
+ input_line_pointer--; /* Backup over a non-',' char. */
+ }
+ }
+
+ if (!goof) {
+ S_SET_DESC(symbolP, get_absolute_expression());
+ if (what == 's' || what == 'n') {
+ if (*input_line_pointer != ',') {
+ as_bad("I want a comma after the n_desc expression");
+ goof = 1;
+ } else {
+ input_line_pointer++;
+ }
+ }
+ }
+
+ if ((!goof) && (what == 's' || what == 'n')) {
+ pseudo_set(symbolP);
+ symbolP->sy_symbol.n_type = saved_type;
+ }
+#ifndef NO_LISTING
+ if (listing && !goof)
+ {
+ if (symbolP->sy_symbol.n_type == N_SLINE)
+ {
+
+ listing_source_line(symbolP->sy_symbol.n_desc);
+ }
+ else if (symbolP->sy_symbol.n_type == N_SO
+ || symbolP->sy_symbol.n_type == N_SOL)
+ {
+ listing_source_file(string);
+ }
+ }
+#endif
+
+ if (goof)
+ ignore_rest_of_line();
+ else
+ demand_empty_rest_of_line ();
+} /* obj_aout_stab() */
+
+static void obj_aout_desc() {
+ register char *name;
+ register char c;
+ register char *p;
+ register symbolS *symbolP;
+ register int temp;
+
+ /*
+ * Frob invented at RMS' request. Set the n_desc of a symbol.
+ */
+ name = input_line_pointer;
+ c = get_symbol_end();
+ p = input_line_pointer;
+ * p = c;
+ SKIP_WHITESPACE();
+ if (*input_line_pointer != ',') {
+ *p = 0;
+ as_bad("Expected comma after name \"%s\"", name);
+ *p = c;
+ ignore_rest_of_line();
+ } else {
+ input_line_pointer ++;
+ temp = get_absolute_expression();
+ *p = 0;
+ symbolP = symbol_find_or_make(name);
+ *p = c;
+ S_SET_DESC(symbolP,temp);
+ }
+ demand_empty_rest_of_line();
+} /* obj_aout_desc() */
+
+void obj_read_begin_hook() {
+ return;
+} /* obj_read_begin_hook() */
+
+void obj_crawl_symbol_chain(headers)
+object_headers *headers;
+{
+ symbolS *symbolP;
+ symbolS **symbolPP;
+ int symbol_number = 0;
+
+ /* JF deal with forward references first... */
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) {
+ if (symbolP->sy_forward) {
+ S_SET_VALUE(symbolP, S_GET_VALUE(symbolP)
+ + S_GET_VALUE(symbolP->sy_forward)
+ + symbolP->sy_forward->sy_frag->fr_address);
+
+ symbolP->sy_forward=0;
+ } /* if it has a forward reference */
+ } /* walk the symbol chain */
+
+ tc_crawl_symbol_chain(headers);
+
+ symbolPP = &symbol_rootP; /*->last symbol chain link. */
+ while ((symbolP = *symbolPP) != NULL) {
+ if (flagseen['R'] && (S_GET_SEGMENT(symbolP) == SEG_DATA)) {
+ S_SET_SEGMENT(symbolP, SEG_TEXT);
+ } /* if pusing data into text */
+
+ S_SET_VALUE(symbolP, S_GET_VALUE(symbolP) + symbolP->sy_frag->fr_address);
+
+ /* OK, here is how we decide which symbols go out into the
+ brave new symtab. Symbols that do are:
+
+ * symbols with no name (stabd's?)
+ * symbols with debug info in their N_TYPE
+ * symbols marked "forceout" (to force out local `L'
+ symbols in PIC code)
+
+ Symbols that don't are:
+ * symbols that are registers
+ * symbols with \1 as their 3rd character (numeric labels)
+ * "local labels" as defined by S_LOCAL_NAME(name)
+ if the -L switch was passed to gas.
+
+ All other symbols are output. We complain if a deleted
+ symbol was marked external. */
+
+
+ if (!S_IS_REGISTER(symbolP)
+ && (!S_GET_NAME(symbolP)
+ || S_IS_DEBUG(symbolP)
+#ifdef TC_I960
+ /* FIXME-SOON this ifdef seems highly dubious to me. xoxorich. */
+ || !S_IS_DEFINED(symbolP)
+ || S_IS_EXTERNAL(symbolP)
+#endif /* TC_I960 */
+ || (S_GET_NAME(symbolP)[0] != '\001' &&
+ (flagseen['L'] || ! S_LOCAL_NAME(symbolP)
+#ifdef PIC
+ || flagseen['k'] && symbolP->sy_forceout
+#endif
+ )
+ )
+ )
+#ifdef PIC
+ && (!flagseen['k'] ||
+ symbolP != GOT_symbol || got_referenced != 0
+ )
+#endif
+ ) {
+ symbolP->sy_number = symbol_number++;
+
+ /* The + 1 after strlen account for the \0 at the
+ end of each string */
+ if (!S_IS_STABD(symbolP)) {
+ /* Ordinary case. */
+ symbolP->sy_name_offset = string_byte_count;
+ string_byte_count += strlen(S_GET_NAME(symbolP)) + 1;
+ }
+ else /* .Stabd case. */
+ symbolP->sy_name_offset = 0;
+
+ /*
+ * If symbol has a known size, output an extra symbol
+ * of type N_SIZE and with the same name.
+ * We cannot evaluate the size expression just yet, as
+ * some its terms may not have had their final values
+ * set. We defer this until `obj_emit_symbols()'
+ */
+ if (flagseen['k'] &&
+ S_GET_TYPE(symbolP) != N_SIZE &&
+#ifndef GRACE_PERIOD_EXPIRED
+ /*Can be enabled when no more old ld's around*/
+ (symbolP->sy_aux == AUX_OBJECT) &&
+#endif
+ symbolP->sy_sizexp) {
+
+ symbolS *addme;
+
+ /* Put a new symbol on the chain */
+#ifdef NSIZE_PREFIX /*XXX*/
+ char buf[BUFSIZ];
+
+ buf[0] = NSIZE_PREFIX;
+ strncpy(buf+1, S_GET_NAME(symbolP), BUFSIZ-2);
+ addme = symbol_make(buf);
+#else
+ addme = symbol_make(S_GET_NAME(symbolP));
+#endif
+ /* Set type and transfer size expression */
+ addme->sy_symbol.n_type = N_SIZE;
+ addme->sy_sizexp = symbolP->sy_sizexp;
+ symbolP->sy_sizexp = NULL;
+
+ /* Set external if symbolP is */
+ if (S_IS_EXTERN(symbolP))
+ S_SET_EXTERNAL(addme);
+
+ }
+ symbolPP = &(symbol_next(symbolP));
+ } else {
+ if ((S_IS_EXTERNAL(symbolP) || !S_IS_DEFINED(symbolP))
+#ifdef PIC
+ && (!flagseen['k'] ||
+ symbolP != GOT_symbol || got_referenced != 0
+ )
+#endif
+ ) {
+ as_bad("Local symbol %s never defined.", decode_local_label_name(S_GET_NAME(symbolP)));
+ } /* oops. */
+
+ /* Unhook it from the chain */
+ *symbolPP = symbol_next(symbolP);
+ } /* if this symbol should be in the output */
+ } /* for each symbol */
+
+ H_SET_SYMBOL_TABLE_SIZE(headers, symbol_number);
+
+ return;
+} /* obj_crawl_symbol_chain() */
+
+/*
+ * Find strings by crawling along symbol table chain.
+ */
+
+void obj_emit_strings(where)
+char **where;
+{
+ symbolS *symbolP;
+
+#ifdef CROSS_COMPILE
+ /* Gotta do md_ byte-ordering stuff for string_byte_count first - KWK */
+ md_number_to_chars(*where, string_byte_count, sizeof(string_byte_count));
+ *where += sizeof(string_byte_count);
+#else /* CROSS_COMPILE */
+ append (where, (char *)&string_byte_count, (unsigned long)sizeof(string_byte_count));
+#endif /* CROSS_COMPILE */
+
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) {
+ if (S_GET_NAME(symbolP))
+ append(&next_object_file_charP, S_GET_NAME(symbolP),
+ (unsigned long)(strlen (S_GET_NAME(symbolP)) + 1));
+ } /* walk symbol chain */
+
+ return;
+} /* obj_emit_strings() */
+
+void obj_pre_write_hook(headers)
+object_headers *headers;
+{
+ H_SET_DYNAMIC(headers, 0);
+ H_SET_VERSION(headers, 0);
+ H_SET_MACHTYPE(headers, AOUT_MACHTYPE);
+
+ H_SET_MAGIC_NUMBER(headers, DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE);
+ H_SET_ENTRY_POINT(headers, 0);
+
+ tc_aout_pre_write_hook(headers);
+ return;
+} /* obj_pre_write_hook() */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of obj-aout.c */
diff --git a/gnu/usr.bin/as/config/obj-aout.h b/gnu/usr.bin/as/config/obj-aout.h
new file mode 100644
index 0000000..af3d9b0
--- /dev/null
+++ b/gnu/usr.bin/as/config/obj-aout.h
@@ -0,0 +1,210 @@
+/* obj-aout.h, a.out object file format for gas, the assembler.
+ Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with GAS; see the file COPYING. If not, write
+ to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
+ * $Id: obj-aout.h,v 1.1 1993/11/03 00:53:50 paul Exp $
+ */
+
+
+/* Tag to validate a.out object file format processing */
+#define OBJ_AOUT 1
+
+#include "targ-cpu.h"
+
+#include "aout.h" /* Needed to define struct nlist. Sigh. */
+
+#ifndef AOUT_MACHTYPE
+#define AOUT_MACHTYPE 0
+#endif /* AOUT_MACHTYPE */
+
+extern const short seg_N_TYPE[];
+extern const segT N_TYPE_seg[];
+
+#ifndef DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE
+#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE (OMAGIC)
+#endif /* DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE */
+
+/* First character of operand in `.type' directives */
+#define TYPE_OPERAND_FMT '@'
+
+/* SYMBOL TABLE */
+/* Symbol table entry data type */
+
+typedef struct nlist obj_symbol_type; /* Symbol table entry */
+
+/* Symbol table macros and constants */
+
+/*
+ * Macros to extract information from a symbol table entry.
+ * This syntaxic indirection allows independence regarding a.out or coff.
+ * The argument (s) of all these macros is a pointer to a symbol table entry.
+ */
+
+/* True if the symbol is external */
+#define S_IS_EXTERNAL(s) ((s)->sy_symbol.n_type & N_EXT)
+
+/* True if symbol has been defined, ie is in N_{TEXT,DATA,BSS,ABS} or N_EXT */
+#define S_IS_DEFINED(s) ((S_GET_TYPE(s) != N_UNDF) || (S_GET_OTHER(s) != 0) || (S_GET_DESC(s) != 0))
+
+#define S_IS_REGISTER(s) ((s)->sy_symbol.n_type == N_REGISTER)
+
+/* True if a debug special symbol entry */
+#define S_IS_DEBUG(s) ((s)->sy_symbol.n_type & N_STAB)
+/* True if a symbol is local symbol name */
+/* A symbol name whose name begin with ^A is a gas internal pseudo symbol
+ nameless symbols come from .stab directives. */
+#define S_IS_LOCAL(s) (S_GET_NAME(s) && \
+ !S_IS_DEBUG(s) && \
+ (S_GET_NAME(s)[0] == '\001' || \
+ (S_LOCAL_NAME(s) && !flagseen['L'])))
+/* True if a symbol is not defined in this file */
+#define S_IS_EXTERN(s) ((s)->sy_symbol.n_type & N_EXT)
+/* True if the symbol has been generated because of a .stabd directive */
+#define S_IS_STABD(s) (S_GET_NAME(s) == (char *)0)
+
+/* Accessors */
+/* The value of the symbol */
+#define S_GET_VALUE(s) (((s)->sy_symbol.n_value))
+/* The name of the symbol */
+#define S_GET_NAME(s) ((s)->sy_symbol.n_un.n_name)
+/* The pointer to the string table */
+#define S_GET_OFFSET(s) ((s)->sy_symbol.n_un.n_strx)
+/* The type of the symbol */
+#define S_GET_TYPE(s) ((s)->sy_symbol.n_type & N_TYPE)
+/* The numeric value of the segment */
+#define S_GET_SEGMENT(s) (N_TYPE_seg[S_GET_TYPE(s)])
+/* The n_other expression value */
+#define S_GET_OTHER(s) ((s)->sy_symbol.n_other)
+/* The n_desc expression value */
+#define S_GET_DESC(s) ((s)->sy_symbol.n_desc)
+
+/* Modifiers */
+/* Set the value of the symbol */
+#define S_SET_VALUE(s,v) ((s)->sy_symbol.n_value = (unsigned long) (v))
+/* Assume that a symbol cannot be simultaneously in more than on segment */
+/* set segment */
+#define S_SET_SEGMENT(s,seg) ((s)->sy_symbol.n_type &= ~N_TYPE,(s)->sy_symbol.n_type|=SEGMENT_TO_SYMBOL_TYPE(seg))
+/* The symbol is external */
+#define S_SET_EXTERNAL(s) ((s)->sy_symbol.n_type |= N_EXT)
+/* The symbol is not external */
+#define S_CLEAR_EXTERNAL(s) ((s)->sy_symbol.n_type &= ~N_EXT)
+/* Set the name of the symbol */
+#define S_SET_NAME(s,v) ((s)->sy_symbol.n_un.n_name = (v))
+/* Set the offset in the string table */
+#define S_SET_OFFSET(s,v) ((s)->sy_symbol.n_un.n_strx = (v))
+/* Set the n_other expression value */
+#define S_SET_OTHER(s,v) ((s)->sy_symbol.n_other = (v))
+/* Set the n_desc expression value */
+#define S_SET_DESC(s,v) ((s)->sy_symbol.n_desc = (v))
+
+/* File header macro and type definition */
+
+#define H_GET_FILE_SIZE(h) (H_GET_HEADER_SIZE(h) \
+ + H_GET_TEXT_SIZE(h) \
+ + H_GET_DATA_SIZE(h) \
+ + H_GET_SYMBOL_TABLE_SIZE(h) \
+ + H_GET_TEXT_RELOCATION_SIZE(h) \
+ + H_GET_DATA_RELOCATION_SIZE(h) \
+ + H_GET_STRING_SIZE(h))
+
+#ifndef H_GET_HEADER_SIZE
+#define H_GET_HEADER_SIZE(h) (sizeof(struct exec))
+#endif /* not H_GET_HEADER_SIZE */
+
+#define H_GET_TEXT_SIZE(h) ((h)->header.a_text)
+#define H_GET_DATA_SIZE(h) ((h)->header.a_data)
+#define H_GET_BSS_SIZE(h) ((h)->header.a_bss)
+#define H_GET_TEXT_RELOCATION_SIZE(h) ((h)->header.a_trsize)
+#define H_GET_DATA_RELOCATION_SIZE(h) ((h)->header.a_drsize)
+#define H_GET_SYMBOL_TABLE_SIZE(h) ((h)->header.a_syms)
+#define H_GET_ENTRY_POINT(h) ((h)->header.a_entry)
+#define H_GET_STRING_SIZE(h) ((h)->string_table_size)
+#define H_GET_LINENO_SIZE(h) (0)
+
+#define H_GET_DYNAMIC(h) ((h)->header.a_info >> 31)
+#define H_GET_VERSION(h) (((h)->header.a_info >> 24) & 0x7f)
+#define H_GET_MACHTYPE(h) (((h)->header.a_info >> 16) & 0xff)
+#define H_GET_MAGIC_NUMBER(h) ((h)->header.a_info & 0xffff)
+
+#define H_SET_DYNAMIC(h,v) ((h)->header.a_info = (((v) << 31) \
+ | (H_GET_VERSION(h) << 24) \
+ | (H_GET_MACHTYPE(h) << 16) \
+ | (H_GET_MAGIC_NUMBER(h))))
+
+#define H_SET_VERSION(h,v) ((h)->header.a_info = ((H_GET_DYNAMIC(h) << 31) \
+ | ((v) << 24) \
+ | (H_GET_MACHTYPE(h) << 16) \
+ | (H_GET_MAGIC_NUMBER(h))))
+
+#define H_SET_MACHTYPE(h,v) ((h)->header.a_info = ((H_GET_DYNAMIC(h) << 31) \
+ | (H_GET_VERSION(h) << 24) \
+ | ((v) << 16) \
+ | (H_GET_MAGIC_NUMBER(h))))
+
+#define H_SET_MAGIC_NUMBER(h,v) ((h)->header.a_info = ((H_GET_DYNAMIC(h) << 31) \
+ | (H_GET_VERSION(h) << 24) \
+ | (H_GET_MACHTYPE(h) << 16) \
+ | ((v))))
+
+#define H_SET_TEXT_SIZE(h,v) ((h)->header.a_text = md_section_align(SEG_TEXT, (v)))
+#define H_SET_DATA_SIZE(h,v) ((h)->header.a_data = md_section_align(SEG_DATA, (v)))
+#define H_SET_BSS_SIZE(h,v) ((h)->header.a_bss = md_section_align(SEG_BSS, (v)))
+
+#define H_SET_RELOCATION_SIZE(h,t,d) (H_SET_TEXT_RELOCATION_SIZE((h),(t)),\
+ H_SET_DATA_RELOCATION_SIZE((h),(d)))
+
+#define H_SET_TEXT_RELOCATION_SIZE(h,v) ((h)->header.a_trsize = (v))
+#define H_SET_DATA_RELOCATION_SIZE(h,v) ((h)->header.a_drsize = (v))
+#define H_SET_SYMBOL_TABLE_SIZE(h,v) ((h)->header.a_syms = (v) * \
+ sizeof(struct nlist))
+
+#define H_SET_ENTRY_POINT(h,v) ((h)->header.a_entry = (v))
+#define H_SET_STRING_SIZE(h,v) ((h)->string_table_size = (v))
+
+/*
+ * Current means for getting the name of a segment.
+ * This will change for infinite-segments support (e.g. COFF).
+ */
+#define segment_name(seg) (seg_name[(int)(seg)])
+extern char *const seg_name[];
+
+typedef struct {
+ struct exec header; /* a.out header */
+ long string_table_size; /* names + '\0' + sizeof(int) */
+} object_headers;
+
+/* line numbering stuff. */
+#define OBJ_EMIT_LINENO(a, b, c) {;}
+
+#define obj_symbol_new_hook(s) {;}
+
+#if __STDC__ == 1
+struct fix;
+void tc_aout_fix_to_chars(char *where, struct fix *fixP, relax_addressT segment_address);
+#else /* not __STDC__ */
+void tc_aout_fix_to_chars();
+#endif /* not __STDC__ */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of obj-aout.h */
diff --git a/gnu/usr.bin/as/config/obj-bfd-sunos.c b/gnu/usr.bin/as/config/obj-bfd-sunos.c
new file mode 100644
index 0000000..626516b
--- /dev/null
+++ b/gnu/usr.bin/as/config/obj-bfd-sunos.c
@@ -0,0 +1,71 @@
+/* obj-bfd-sunos.c
+ Copyright (C) 1987, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "as.h"
+
+static
+
+ const short seg_N_TYPE[] = {
+ N_ABS,
+ N_TEXT,
+ N_DATA,
+ N_BSS,
+ N_UNDF, /* unknown */
+ N_UNDF, /* absent */
+ N_UNDF, /* pass1 */
+ N_UNDF, /* error */
+ N_UNDF, /* bignum/flonum */
+ N_UNDF, /* difference */
+ N_REGISTER, /* register */
+ };
+
+const segT N_TYPE_seg[N_TYPE+2] = { /* N_TYPE == 0x1E = 32-2 */
+ SEG_UNKNOWN, /* N_UNDF == 0 */
+ SEG_GOOF,
+ SEG_ABSOLUTE, /* N_ABS == 2 */
+ SEG_GOOF,
+ SEG_TEXT, /* N_TEXT == 4 */
+ SEG_GOOF,
+ SEG_DATA, /* N_DATA == 6 */
+ SEG_GOOF,
+ SEG_BSS, /* N_BSS == 8 */
+ SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_REGISTER, /* dummy N_REGISTER for regs = 30 */
+ SEG_GOOF,
+};
+
+
+void obj_symbol_new_hook(symbolP)
+symbolS *symbolP;
+{
+ return;
+} /* obj_symbol_new_hook() */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of obj-bfd-sunos.c */
diff --git a/gnu/usr.bin/as/config/obj-bfd-sunos.h b/gnu/usr.bin/as/config/obj-bfd-sunos.h
new file mode 100644
index 0000000..958d8a9
--- /dev/null
+++ b/gnu/usr.bin/as/config/obj-bfd-sunos.h
@@ -0,0 +1,69 @@
+/*
+ Copyright (C) 1987, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * This file is obj-bfd-sunos.h.
+ */
+
+/* define an obj specific macro off which target cpu back ends may key. */
+#define OBJ_BFD
+#define OBJ_BFD_SUNOS
+
+#include "bfd.h"
+
+/* include whatever target cpu is appropriate. */
+#include "targ-cpu.h"
+
+/*
+ * SYMBOLS
+ */
+
+/*
+ * If your object format needs to reorder symbols, define this. When
+ * defined, symbols are kept on a doubly linked list and functions are
+ * made available for push, insert, append, and delete. If not defined,
+ * symbols are kept on a singly linked list, only the append and clear
+ * facilities are available, and they are macros.
+ */
+
+/* #define SYMBOLS_NEED_PACKPOINTERS */
+
+typedef asymbol obj_symbol_type;
+typedef void *object_headers;
+
+#define S_SET_NAME(s, v) ((s)->sy_symbol.name = (v))
+#define S_GET_NAME(s) ((s)->sy_symbol.name)
+#define S_SET_SEGMENT(s,v) ((s)->sy_symbol.udata = (v))
+#define S_GET_SEGMENT(s) ((s)->sy_symbol.udata)
+#define S_SET_EXTERNAL(s) ((s)->sy_symbol.flags |= BSF_GLOBAL)
+#define S_SET_VALUE(s,v) ((s)->sy_symbol.value = (v))
+#define S_GET_VALUE(s) ((s)->sy_symbol.value)
+#define S_IS_DEFINED(s) (!((s)->sy_symbol.flags & BSF_UNDEFINED))
+
+#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE (0) /* your magic number */
+#define OBJ_EMIT_LINENO(a,b,c) /* must be *something*. This no-op's it out. */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of obj-bfd-sunos.h */
diff --git a/gnu/usr.bin/as/config/obj-bout.c b/gnu/usr.bin/as/config/obj-bout.c
new file mode 100644
index 0000000..f6d9302
--- /dev/null
+++ b/gnu/usr.bin/as/config/obj-bout.c
@@ -0,0 +1,476 @@
+/* b.out object file format
+ Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with GAS; see the file COPYING. If not, write
+ to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "as.h"
+#include "obstack.h"
+
+#ifndef NO_LISTING
+#include "aout/stab_gnu.h"
+#endif /* NO_LISTING */
+
+const short /* in: segT out: N_TYPE bits */
+ seg_N_TYPE[] = {
+ N_ABS,
+ N_TEXT,
+ N_DATA,
+ N_BSS,
+ N_UNDF, /* unknown */
+ N_UNDF, /* absent */
+ N_UNDF, /* pass1 */
+ N_UNDF, /* error */
+ N_UNDF, /* bignum/flonum */
+ N_UNDF, /* difference */
+ N_REGISTER, /* register */
+ };
+
+const segT N_TYPE_seg[N_TYPE+2] = { /* N_TYPE == 0x1E = 32-2 */
+ SEG_UNKNOWN, /* N_UNDF == 0 */
+ SEG_GOOF,
+ SEG_ABSOLUTE, /* N_ABS == 2 */
+ SEG_GOOF,
+ SEG_TEXT, /* N_TEXT == 4 */
+ SEG_GOOF,
+ SEG_DATA, /* N_DATA == 6 */
+ SEG_GOOF,
+ SEG_BSS, /* N_BSS == 8 */
+ SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_REGISTER, /* dummy N_REGISTER for regs = 30 */
+ SEG_GOOF,
+};
+
+#if __STDC__ == 1
+static void obj_bout_stab(int what);
+static void obj_bout_line(void);
+static void obj_bout_desc(void);
+#else /* not __STDC__ */
+static void obj_bout_desc();
+static void obj_bout_stab();
+static void obj_bout_line();
+#endif /* not __STDC__ */
+
+const pseudo_typeS obj_pseudo_table[] = {
+ /* stabs (aka a.out aka b.out directives for debug symbols) */
+ { "desc", obj_bout_desc, 0 }, /* def */
+ { "line", obj_bout_line, 0 }, /* source code line number */
+ { "stabd", obj_bout_stab, 'd' }, /* stabs */
+ { "stabn", obj_bout_stab, 'n' }, /* stabs */
+ { "stabs", obj_bout_stab, 's' }, /* stabs */
+
+ /* coff debugging directives. Currently ignored silently */
+ { "def", s_ignore, 0 },
+ { "dim", s_ignore, 0 },
+ { "endef", s_ignore, 0 },
+ { "ln", s_ignore, 0 },
+ { "scl", s_ignore, 0 },
+ { "size", s_ignore, 0 },
+ { "tag", s_ignore, 0 },
+ { "type", s_ignore, 0 },
+ { "val", s_ignore, 0 },
+
+ /* other stuff we don't handle */
+ { "ABORT", s_ignore, 0 },
+ { "ident", s_ignore, 0 },
+
+ { NULL} /* end sentinel */
+}; /* obj_pseudo_table */
+
+/* Relocation. */
+
+/*
+ * emit_relocations()
+ *
+ * Crawl along a fixS chain. Emit the segment's relocations.
+ */
+void obj_emit_relocations(where, fixP, segment_address_in_file)
+char **where;
+fixS *fixP; /* Fixup chain for this segment. */
+relax_addressT segment_address_in_file;
+{
+ for (; fixP; fixP = fixP->fx_next) {
+ if (fixP->fx_addsy != NULL) {
+ tc_bout_fix_to_chars(*where, fixP, segment_address_in_file);
+ *where += sizeof(struct relocation_info);
+ } /* if there's a symbol */
+ } /* for each fixup */
+
+} /* emit_relocations() */
+
+/* Aout file generation & utilities */
+
+/* Convert a lvalue to machine dependent data */
+void obj_header_append(where, headers)
+char **where;
+object_headers *headers;
+{
+ /* Always leave in host byte order */
+
+ headers->header.a_talign = section_alignment[SEG_TEXT];
+
+ if (headers->header.a_talign < 2){
+ headers->header.a_talign = 2;
+ } /* force to at least 2 */
+
+ headers->header.a_dalign = section_alignment[SEG_DATA];
+ headers->header.a_balign = section_alignment[SEG_BSS];
+
+ headers->header.a_tload = 0;
+ headers->header.a_dload = md_section_align(SEG_DATA, H_GET_TEXT_SIZE(headers));
+
+ append(where, (char *) &headers->header, sizeof(headers->header));
+} /* a_header_append() */
+
+void obj_symbol_to_chars(where, symbolP)
+char **where;
+symbolS *symbolP;
+{
+ /* leave in host byte order */
+ append(where, (char *)&symbolP->sy_symbol, sizeof(obj_symbol_type));
+} /* obj_symbol_to_chars() */
+
+void obj_emit_symbols(where, symbol_rootP)
+char **where;
+symbolS *symbol_rootP;
+{
+ symbolS * symbolP;
+
+ /*
+ * Emit all symbols left in the symbol chain.
+ */
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) {
+ /* Used to save the offset of the name. It is used to point
+ to the string in memory but must be a file offset. */
+ char *temp;
+
+ temp = S_GET_NAME(symbolP);
+ S_SET_OFFSET(symbolP, symbolP->sy_name_offset);
+
+ /* Any symbol still undefined and is not a dbg symbol is made N_EXT. */
+ if (!S_IS_DEBUG(symbolP) && !S_IS_DEFINED(symbolP)) S_SET_EXTERNAL(symbolP);
+
+ obj_symbol_to_chars(where, symbolP);
+ S_SET_NAME(symbolP,temp);
+ }
+} /* emit_symbols() */
+
+void obj_symbol_new_hook(symbolP)
+symbolS *symbolP;
+{
+ S_SET_OTHER(symbolP, 0);
+ S_SET_DESC(symbolP, 0);
+ return;
+} /* obj_symbol_new_hook() */
+
+static void obj_bout_line() {
+ /* Assume delimiter is part of expression. */
+ /* BSD4.2 as fails with delightful bug, so we */
+ /* are not being incompatible here. */
+ new_logical_line ((char *)NULL, (int)(get_absolute_expression ()));
+ demand_empty_rest_of_line();
+} /* obj_bout_line() */
+
+/*
+ * stab()
+ *
+ * Handle .stabX directives, which used to be open-coded.
+ * So much creeping featurism overloaded the semantics that we decided
+ * to put all .stabX thinking in one place. Here.
+ *
+ * We try to make any .stabX directive legal. Other people's AS will often
+ * do assembly-time consistency checks: eg assigning meaning to n_type bits
+ * and "protecting" you from setting them to certain values. (They also zero
+ * certain bits before emitting symbols. Tut tut.)
+ *
+ * If an expression is not absolute we either gripe or use the relocation
+ * information. Other people's assemblers silently forget information they
+ * don't need and invent information they need that you didn't supply.
+ *
+ * .stabX directives always make a symbol table entry. It may be junk if
+ * the rest of your .stabX directive is malformed.
+ */
+static void obj_bout_stab(what)
+int what;
+{
+ register symbolS * symbolP = 0;
+ register char * string;
+ int saved_type = 0;
+ int length;
+ int goof; /* TRUE if we have aborted. */
+ long longint;
+
+ /*
+ * Enter with input_line_pointer pointing past .stabX and any following
+ * whitespace.
+ */
+ goof = 0; /* JF who forgot this?? */
+ if (what == 's') {
+ string = demand_copy_C_string(& length);
+ SKIP_WHITESPACE();
+ if (*input_line_pointer == ',')
+ input_line_pointer ++;
+ else {
+ as_bad("I need a comma after symbol's name");
+ goof = 1;
+ }
+ } else
+ string = "";
+
+ /*
+ * Input_line_pointer->after ','. String->symbol name.
+ */
+ if (!goof) {
+ symbolP = symbol_new(string,
+ SEG_UNKNOWN,
+ 0,
+ (struct frag *)0);
+ switch (what) {
+ case 'd':
+ S_SET_NAME(symbolP,NULL); /* .stabd feature. */
+ S_SET_VALUE(symbolP,obstack_next_free(&frags) -
+ frag_now->fr_literal);
+ symbolP->sy_frag = frag_now;
+ break;
+
+ case 'n':
+ symbolP->sy_frag = &zero_address_frag;
+ break;
+
+ case 's':
+ symbolP->sy_frag = & zero_address_frag;
+ break;
+
+ default:
+ BAD_CASE(what);
+ break;
+ }
+ if (get_absolute_expression_and_terminator(& longint) == ',')
+ symbolP->sy_symbol.n_type = saved_type = longint;
+ else {
+ as_bad("I want a comma after the n_type expression");
+ goof = 1;
+ input_line_pointer--; /* Backup over a non-',' char. */
+ }
+ }
+ if (! goof) {
+ if (get_absolute_expression_and_terminator (& longint) == ',')
+ S_SET_OTHER(symbolP,longint);
+ else {
+ as_bad("I want a comma after the n_other expression");
+ goof = 1;
+ input_line_pointer--; /* Backup over a non-',' char. */
+ }
+ }
+ if (! goof) {
+ S_SET_DESC(symbolP, get_absolute_expression ());
+ if (what == 's' || what == 'n') {
+ if (* input_line_pointer != ',') {
+ as_bad("I want a comma after the n_desc expression");
+ goof = 1;
+ } else {
+ input_line_pointer ++;
+ }
+ }
+ }
+ if ((!goof) && (what == 's' || what == 'n')) {
+ pseudo_set(symbolP);
+ symbolP->sy_symbol.n_type = saved_type;
+ }
+#ifndef NO_LISTING
+ {
+ extern int listing;
+
+ if (listing && !goof) {
+ if (symbolP->sy_symbol.n_type == N_SLINE) {
+
+ listing_source_line(symbolP->sy_symbol.n_desc);
+ } else if (symbolP->sy_symbol.n_type == N_SO
+ || symbolP->sy_symbol.n_type == N_SOL) {
+ listing_source_file(string);
+ }
+ }
+ }
+
+#endif
+
+ if (goof)
+ ignore_rest_of_line ();
+ else
+ demand_empty_rest_of_line ();
+} /* obj_bout_stab() */
+
+static void obj_bout_desc() {
+ register char *name;
+ register char c;
+ register char *p;
+ register symbolS * symbolP;
+ register int temp;
+
+ /*
+ * Frob invented at RMS' request. Set the n_desc of a symbol.
+ */
+ name = input_line_pointer;
+ c = get_symbol_end();
+ p = input_line_pointer;
+ * p = c;
+ SKIP_WHITESPACE();
+ if (*input_line_pointer != ',') {
+ *p = 0;
+ as_bad("Expected comma after name \"%s\"", name);
+ *p = c;
+ ignore_rest_of_line();
+ } else {
+ input_line_pointer ++;
+ temp = get_absolute_expression ();
+ *p = 0;
+ symbolP = symbol_find_or_make(name);
+ *p = c;
+ S_SET_DESC(symbolP,temp);
+ }
+ demand_empty_rest_of_line();
+} /* obj_bout_desc() */
+
+void obj_read_begin_hook() {
+ return;
+} /* obj_read_begin_hook() */
+
+void obj_crawl_symbol_chain(headers)
+object_headers *headers;
+{
+ symbolS **symbolPP;
+ symbolS *symbolP;
+ int symbol_number = 0;
+
+ /* JF deal with forward references first... */
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) {
+ if (symbolP->sy_forward) {
+ S_SET_VALUE(symbolP, S_GET_VALUE(symbolP)
+ + S_GET_VALUE(symbolP->sy_forward)
+ + symbolP->sy_forward->sy_frag->fr_address);
+
+ symbolP->sy_forward=0;
+ } /* if it has a forward reference */
+ } /* walk the symbol chain */
+
+ tc_crawl_symbol_chain(headers);
+
+ symbolPP = & symbol_rootP; /*->last symbol chain link. */
+ while ((symbolP = *symbolPP) != NULL) {
+ if (flagseen['R'] && (S_GET_SEGMENT(symbolP) == SEG_DATA)) {
+ S_SET_SEGMENT(symbolP, SEG_TEXT);
+ } /* if pusing data into text */
+
+ S_SET_VALUE(symbolP, S_GET_VALUE(symbolP) + symbolP->sy_frag->fr_address);
+
+ /* OK, here is how we decide which symbols go out into the
+ brave new symtab. Symbols that do are:
+
+ * symbols with no name (stabd's?)
+ * symbols with debug info in their N_TYPE
+
+ Symbols that don't are:
+ * symbols that are registers
+ * symbols with \1 as their 3rd character (numeric labels)
+ * "local labels" as defined by S_LOCAL_NAME(name)
+ if the -L switch was passed to gas.
+
+ All other symbols are output. We complain if a deleted
+ symbol was marked external. */
+
+
+ if (1
+ && !S_IS_REGISTER(symbolP)
+ && (!S_GET_NAME(symbolP)
+ || S_IS_DEBUG(symbolP)
+#ifdef TC_I960
+ /* FIXME-SOON this ifdef seems highly dubious to me. xoxorich. */
+ || !S_IS_DEFINED(symbolP)
+ || S_IS_EXTERNAL(symbolP)
+#endif /* TC_I960 */
+ || (S_GET_NAME(symbolP)[0] != '\001' && (flagseen['L'] || ! S_LOCAL_NAME(symbolP))))) {
+ symbolP->sy_number = symbol_number++;
+
+ /* The + 1 after strlen account for the \0 at the
+ end of each string */
+ if (!S_IS_STABD(symbolP)) {
+ /* Ordinary case. */
+ symbolP->sy_name_offset = string_byte_count;
+ string_byte_count += strlen(S_GET_NAME(symbolP)) + 1;
+ }
+ else /* .Stabd case. */
+ symbolP->sy_name_offset = 0;
+ symbolPP = &(symbol_next(symbolP));
+ } else {
+ if (S_IS_EXTERNAL(symbolP) || !S_IS_DEFINED(symbolP)) {
+ as_bad("Local symbol %s never defined", S_GET_NAME(symbolP));
+ } /* oops. */
+
+ /* Unhook it from the chain */
+ *symbolPP = symbol_next(symbolP);
+ } /* if this symbol should be in the output */
+ } /* for each symbol */
+
+ H_SET_SYMBOL_TABLE_SIZE(headers, symbol_number);
+
+ return;
+} /* obj_crawl_symbol_chain() */
+
+/*
+ * Find strings by crawling along symbol table chain.
+ */
+
+void obj_emit_strings(where)
+char **where;
+{
+ symbolS *symbolP;
+
+#ifdef CROSS_COMPILE
+ /* Gotta do md_ byte-ordering stuff for string_byte_count first - KWK */
+ md_number_to_chars(*where, string_byte_count, sizeof(string_byte_count));
+ *where += sizeof(string_byte_count);
+#else /* CROSS_COMPILE */
+ append(where, (char *) &string_byte_count, (unsigned long) sizeof(string_byte_count));
+#endif /* CROSS_COMPILE */
+
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) {
+ if (S_GET_NAME(symbolP))
+ append(where, S_GET_NAME(symbolP), (unsigned long)(strlen (S_GET_NAME(symbolP)) + 1));
+ } /* walk symbol chain */
+
+ return;
+} /* obj_emit_strings() */
+
+void obj_pre_write_hook(headers)
+object_headers *headers;
+{
+ H_SET_MAGIC_NUMBER(headers, BMAGIC);
+ H_SET_ENTRY_POINT(headers, 0);
+
+ return;
+} /* obj_pre_write_hook() */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of obj-bout.c */
diff --git a/gnu/usr.bin/as/config/obj-bout.h b/gnu/usr.bin/as/config/obj-bout.h
new file mode 100644
index 0000000..e28d435
--- /dev/null
+++ b/gnu/usr.bin/as/config/obj-bout.h
@@ -0,0 +1,313 @@
+/* b.out object file format
+ Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with GAS; see the file COPYING. If not, write
+ to the Free Software Foundation, 675 Mass Ave, Cambridge, MA
+ 02139, USA. */
+
+/*
+ * This file is a modified version of 'a.out.h'. It is to be used in all GNU
+ * tools modified to support the i80960 b.out format (or tools that operate on
+ * object files created by such tools).
+ *
+ * All i80960 development is done in a CROSS-DEVELOPMENT environment. I.e.,
+ * object code is generated on, and executed under the direction of a symbolic
+ * debugger running on, a host system. We do not want to be subject to the
+ * vagaries of which host it is or whether it supports COFF or a.out format, or
+ * anything else. We DO want to:
+ *
+ * o always generate the same format object files, regardless of host.
+ *
+ * o have an 'a.out' header that we can modify for our own purposes
+ * (the 80960 is typically an embedded processor and may require
+ * enhanced linker support that the normal a.out.h header can't
+ * accommodate).
+ *
+ * As for byte-ordering, the following rules apply:
+ *
+ * o Text and data that is actually downloaded to the target is always
+ * in i80960 (little-endian) order.
+ *
+ * o All other numbers (in the header, symbols, relocation directives)
+ * are in host byte-order: object files CANNOT be lifted from a
+ * little-end host and used on a big-endian (or vice versa) without
+ * modification.
+ *
+ * o The downloader ('comm960') takes care to generate a pseudo-header
+ * with correct (i80960) byte-ordering before shipping text and data
+ * off to the NINDY monitor in the target systems. Symbols and
+ * relocation info are never sent to the target.
+ */
+
+
+#define OBJ_BOUT 1
+
+#include "targ-cpu.h"
+
+/* bout uses host byte order for headers */
+#ifdef CROSS_COMPILE
+#undef CROSS_COMPILE
+#endif /* CROSS_COMPILE */
+
+/* We want \v. */
+#define BACKSLASH_V 1
+
+#define OBJ_DEFAULT_OUTPUT_FILE_NAME "b.out"
+
+extern const short seg_N_TYPE[];
+extern const segT N_TYPE_seg[];
+
+#define BMAGIC 0415
+/* We don't accept the following (see N_BADMAG macro).
+ * They're just here so GNU code will compile.
+ */
+#define OMAGIC 0407 /* old impure format */
+#define NMAGIC 0410 /* read-only text */
+#define ZMAGIC 0413 /* demand load format */
+
+/* FILE HEADER
+ * All 'lengths' are given as a number of bytes.
+ * All 'alignments' are for relinkable files only; an alignment of
+ * 'n' indicates the corresponding segment must begin at an
+ * address that is a multiple of (2**n).
+ */
+struct exec {
+ /* Standard stuff */
+ unsigned long a_magic; /* Identifies this as a b.out file */
+ unsigned long a_text; /* Length of text */
+ unsigned long a_data; /* Length of data */
+ unsigned long a_bss; /* Length of runtime uninitialized data area */
+ unsigned long a_syms; /* Length of symbol table */
+ unsigned long a_entry; /* Runtime start address */
+ unsigned long a_trsize; /* Length of text relocation info */
+ unsigned long a_drsize; /* Length of data relocation info */
+
+ /* Added for i960 */
+ unsigned long a_tload; /* Text runtime load address */
+ unsigned long a_dload; /* Data runtime load address */
+ unsigned char a_talign; /* Alignment of text segment */
+ unsigned char a_dalign; /* Alignment of data segment */
+ unsigned char a_balign; /* Alignment of bss segment */
+ unsigned char unused; /* (Just to make struct size a multiple of 4) */
+};
+
+#define N_BADMAG(x) (((x).a_magic) != BMAGIC)
+#define N_TXTOFF(x) ( sizeof(struct exec) )
+#define N_DATOFF(x) ( N_TXTOFF(x) + (x).a_text )
+#define N_TROFF(x) ( N_DATOFF(x) + (x).a_data )
+#define N_DROFF(x) ( N_TROFF(x) + (x).a_trsize )
+#define N_SYMOFF(x) ( N_DROFF(x) + (x).a_drsize )
+#define N_STROFF(x) ( N_SYMOFF(x) + (x).a_syms )
+
+/* A single entry in the symbol table
+ */
+struct nlist {
+ union {
+ char *n_name;
+ struct nlist *n_next;
+ long n_strx; /* Index into string table */
+ } n_un;
+ unsigned char n_type; /* See below */
+ char n_other; /* Used in i80960 support -- see below */
+ short n_desc;
+ unsigned long n_value;
+};
+
+typedef struct nlist obj_symbol_type;
+
+/* Legal values of n_type
+ */
+#define N_UNDF 0 /* Undefined symbol */
+#define N_ABS 2 /* Absolute symbol */
+#define N_TEXT 4 /* Text symbol */
+#define N_DATA 6 /* Data symbol */
+#define N_BSS 8 /* BSS symbol */
+#define N_FN 31 /* Filename symbol */
+
+#define N_EXT 1 /* External symbol (OR'd in with one of above) */
+#define N_TYPE 036 /* Mask for all the type bits */
+#define N_STAB 0340 /* Mask for all bits used for SDB entries */
+
+#ifndef CUSTOM_RELOC_FORMAT
+struct relocation_info {
+ int r_address; /* File address of item to be relocated */
+ unsigned
+ r_index:24,/* Index of symbol on which relocation is based*/
+ r_pcrel:1, /* 1 => relocate PC-relative; else absolute
+ * On i960, pc-relative implies 24-bit
+ * address, absolute implies 32-bit.
+ */
+ r_length:2, /* Number of bytes to relocate:
+ * 0 => 1 byte
+ * 1 => 2 bytes
+ * 2 => 4 bytes -- only value used for i960
+ */
+ r_extern:1,
+ r_bsr:1, /* Something for the GNU NS32K assembler */
+ r_disp:1, /* Something for the GNU NS32K assembler */
+ r_callj:1, /* 1 if relocation target is an i960 'callj' */
+ nuthin:1; /* Unused */
+};
+#endif /* CUSTOM_RELOC_FORMAT */
+
+/*
+ * Macros to extract information from a symbol table entry.
+ * This syntaxic indirection allows independence regarding a.out or coff.
+ * The argument (s) of all these macros is a pointer to a symbol table entry.
+ */
+
+/* Predicates */
+/* True if the symbol is external */
+#define S_IS_EXTERNAL(s) ((s)->sy_symbol.n_type & N_EXT)
+
+/* True if symbol has been defined, ie is in N_{TEXT,DATA,BSS,ABS} or N_EXT */
+#define S_IS_DEFINED(s) ((S_GET_TYPE(s) != N_UNDF) || (S_GET_DESC(s) != 0))
+#define S_IS_REGISTER(s) ((s)->sy_symbol.n_type == N_REGISTER)
+
+/* True if a debug special symbol entry */
+#define S_IS_DEBUG(s) ((s)->sy_symbol.n_type & N_STAB)
+/* True if a symbol is local symbol name */
+/* A symbol name whose name begin with ^A is a gas internal pseudo symbol
+ nameless symbols come from .stab directives. */
+#define S_IS_LOCAL(s) (S_GET_NAME(s) && \
+ !S_IS_DEBUG(s) && \
+ (S_GET_NAME(s)[0] == '\001' || \
+ (S_LOCAL_NAME(s) && !flagseen['L'])))
+/* True if a symbol is not defined in this file */
+#define S_IS_EXTERN(s) ((s)->sy_symbol.n_type & N_EXT)
+/* True if the symbol has been generated because of a .stabd directive */
+#define S_IS_STABD(s) (S_GET_NAME(s) == NULL)
+
+/* Accessors */
+/* The value of the symbol */
+#define S_GET_VALUE(s) ((unsigned long) ((s)->sy_symbol.n_value))
+/* The name of the symbol */
+#define S_GET_NAME(s) ((s)->sy_symbol.n_un.n_name)
+/* The pointer to the string table */
+#define S_GET_OFFSET(s) ((s)->sy_symbol.n_un.n_strx)
+/* The type of the symbol */
+#define S_GET_TYPE(s) ((s)->sy_symbol.n_type & N_TYPE)
+/* The numeric value of the segment */
+#define S_GET_SEGMENT(s) (N_TYPE_seg[S_GET_TYPE(s)])
+/* The n_other expression value */
+#define S_GET_OTHER(s) ((s)->sy_symbol.n_other)
+/* The n_desc expression value */
+#define S_GET_DESC(s) ((s)->sy_symbol.n_desc)
+
+/* Modifiers */
+/* Set the value of the symbol */
+#define S_SET_VALUE(s,v) ((s)->sy_symbol.n_value = (unsigned long) (v))
+/* Assume that a symbol cannot be simultaneously in more than on segment */
+/* set segment */
+#define S_SET_SEGMENT(s,seg) ((s)->sy_symbol.n_type &= ~N_TYPE,(s)->sy_symbol.n_type|=SEGMENT_TO_SYMBOL_TYPE(seg))
+/* The symbol is external */
+#define S_SET_EXTERNAL(s) ((s)->sy_symbol.n_type |= N_EXT)
+/* The symbol is not external */
+#define S_CLEAR_EXTERNAL(s) ((s)->sy_symbol.n_type &= ~N_EXT)
+/* Set the name of the symbol */
+#define S_SET_NAME(s,v) ((s)->sy_symbol.n_un.n_name = (v))
+/* Set the offset in the string table */
+#define S_SET_OFFSET(s,v) ((s)->sy_symbol.n_un.n_strx = (v))
+/* Set the n_other expression value */
+#define S_SET_OTHER(s,v) ((s)->sy_symbol.n_other = (v))
+/* Set the n_desc expression value */
+#define S_SET_DESC(s,v) ((s)->sy_symbol.n_desc = (v))
+
+/* File header macro and type definition */
+
+#define H_GET_FILE_SIZE(h) (sizeof(struct exec) + \
+ H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h) + \
+ H_GET_SYMBOL_TABLE_SIZE(h) + \
+ H_GET_TEXT_RELOCATION_SIZE(h) + \
+ H_GET_DATA_RELOCATION_SIZE(h) + \
+ (h)->string_table_size)
+
+#define H_GET_HEADER_SIZE(h) (sizeof(struct exec))
+#define H_GET_TEXT_SIZE(h) ((h)->header.a_text)
+#define H_GET_DATA_SIZE(h) ((h)->header.a_data)
+#define H_GET_BSS_SIZE(h) ((h)->header.a_bss)
+#define H_GET_TEXT_RELOCATION_SIZE(h) ((h)->header.a_trsize)
+#define H_GET_DATA_RELOCATION_SIZE(h) ((h)->header.a_drsize)
+#define H_GET_SYMBOL_TABLE_SIZE(h) ((h)->header.a_syms)
+#define H_GET_MAGIC_NUMBER(h) ((h)->header.a_info)
+#define H_GET_ENTRY_POINT(h) ((h)->header.a_entry)
+#define H_GET_STRING_SIZE(h) ((h)->string_table_size)
+#define H_GET_LINENO_SIZE(h) (0)
+
+#ifdef EXEC_MACHINE_TYPE
+#define H_GET_MACHINE_TYPE(h) ((h)->header.a_machtype)
+#endif /* EXEC_MACHINE_TYPE */
+#ifdef EXEC_VERSION
+#define H_GET_VERSION(h) ((h)->header.a_version)
+#endif /* EXEC_VERSION */
+
+#define H_SET_TEXT_SIZE(h,v) ((h)->header.a_text = (v))
+#define H_SET_DATA_SIZE(h,v) ((h)->header.a_data = (v))
+#define H_SET_BSS_SIZE(h,v) ((h)->header.a_bss = (v))
+
+#define H_SET_RELOCATION_SIZE(h,t,d) (H_SET_TEXT_RELOCATION_SIZE((h),(t)),\
+ H_SET_DATA_RELOCATION_SIZE((h),(d)))
+
+#define H_SET_TEXT_RELOCATION_SIZE(h,v) ((h)->header.a_trsize = (v))
+#define H_SET_DATA_RELOCATION_SIZE(h,v) ((h)->header.a_drsize = (v))
+#define H_SET_SYMBOL_TABLE_SIZE(h,v) ((h)->header.a_syms = (v) * \
+ sizeof(struct nlist))
+
+#define H_SET_MAGIC_NUMBER(h,v) ((h)->header.a_magic = (v))
+
+#define H_SET_ENTRY_POINT(h,v) ((h)->header.a_entry = (v))
+#define H_SET_STRING_SIZE(h,v) ((h)->string_table_size = (v))
+#ifdef EXEC_MACHINE_TYPE
+#define H_SET_MACHINE_TYPE(h,v) ((h)->header.a_machtype = (v))
+#endif /* EXEC_MACHINE_TYPE */
+#ifdef EXEC_VERSION
+#define H_SET_VERSION(h,v) ((h)->header.a_version = (v))
+#endif /* EXEC_VERSION */
+
+/*
+ * Current means for getting the name of a segment.
+ * This will change for infinite-segments support (e.g. COFF).
+ */
+#define segment_name(seg) ( seg_name[(int)(seg)] )
+extern char *const seg_name[];
+
+typedef struct {
+ struct exec header; /* a.out header */
+ long string_table_size; /* names + '\0' + sizeof(int) */
+} object_headers;
+
+/* unused hooks. */
+#define OBJ_EMIT_LINENO(a, b, c) {;}
+
+#if __STDC__
+struct fix;
+void tc_aout_fix_to_chars(char *where, struct fix *fixP, relax_addressT segment_address);
+#else /* not __STDC__ */
+void tc_aout_fix_to_chars();
+#endif /* not __STDC__ */
+
+enum reloc_type {
+ NO_RELOC, RELOC_32,
+};
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of obj-bout.h */
diff --git a/gnu/usr.bin/as/config/obj-coff.c b/gnu/usr.bin/as/config/obj-coff.c
new file mode 100644
index 0000000..238e6c5
--- /dev/null
+++ b/gnu/usr.bin/as/config/obj-coff.c
@@ -0,0 +1,1978 @@
+/* coff object file format
+ Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "as.h"
+
+#include "obstack.h"
+
+lineno* lineno_rootP;
+
+const short seg_N_TYPE[] = { /* in: segT out: N_TYPE bits */
+ C_ABS_SECTION,
+ C_TEXT_SECTION,
+ C_DATA_SECTION,
+ C_BSS_SECTION,
+ C_UNDEF_SECTION, /* SEG_UNKNOWN */
+ C_UNDEF_SECTION, /* SEG_ABSENT */
+ C_UNDEF_SECTION, /* SEG_PASS1 */
+ C_UNDEF_SECTION, /* SEG_GOOF */
+ C_UNDEF_SECTION, /* SEG_BIG */
+ C_UNDEF_SECTION, /* SEG_DIFFERENCE */
+ C_DEBUG_SECTION, /* SEG_DEBUG */
+ C_NTV_SECTION, /* SEG_NTV */
+ C_PTV_SECTION, /* SEG_PTV */
+ C_REGISTER_SECTION, /* SEG_REGISTER */
+};
+
+
+/* Add 4 to the real value to get the index and compensate the negatives */
+
+const segT N_TYPE_seg[32] =
+{
+ SEG_PTV, /* C_PTV_SECTION == -4 */
+ SEG_NTV, /* C_NTV_SECTION == -3 */
+ SEG_DEBUG, /* C_DEBUG_SECTION == -2 */
+ SEG_ABSOLUTE, /* C_ABS_SECTION == -1 */
+ SEG_UNKNOWN, /* C_UNDEF_SECTION == 0 */
+ SEG_TEXT, /* C_TEXT_SECTION == 1 */
+ SEG_DATA, /* C_DATA_SECTION == 2 */
+ SEG_BSS, /* C_BSS_SECTION == 3 */
+ SEG_REGISTER, /* C_REGISTER_SECTION == 4 */
+ SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,
+ SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,
+ SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF
+ };
+
+#if __STDC__ == 1
+
+char *s_get_name(symbolS *s);
+static symbolS *tag_find_or_make(char *name);
+static symbolS* tag_find(char *name);
+#ifdef BFD_HEADERS
+static void obj_coff_section_header_append(char **where, struct internal_scnhdr *header);
+#else
+static void obj_coff_section_header_append(char **where, SCNHDR *header);
+#endif
+static void obj_coff_def(int what);
+static void obj_coff_dim(void);
+static void obj_coff_endef(void);
+static void obj_coff_line(void);
+static void obj_coff_ln(void);
+static void obj_coff_scl(void);
+static void obj_coff_size(void);
+static void obj_coff_stab(int what);
+static void obj_coff_tag(void);
+static void obj_coff_type(void);
+static void obj_coff_val(void);
+static void tag_init(void);
+static void tag_insert(char *name, symbolS *symbolP);
+
+#else /* not __STDC__ */
+
+char *s_get_name();
+static symbolS *tag_find();
+static symbolS *tag_find_or_make();
+static void obj_coff_section_header_append();
+static void obj_coff_def();
+static void obj_coff_dim();
+static void obj_coff_endef();
+static void obj_coff_line();
+static void obj_coff_ln();
+static void obj_coff_scl();
+static void obj_coff_size();
+static void obj_coff_stab();
+static void obj_coff_tag();
+static void obj_coff_type();
+static void obj_coff_val();
+static void tag_init();
+static void tag_insert();
+
+#endif /* not __STDC__ */
+
+static struct hash_control *tag_hash;
+static symbolS *def_symbol_in_progress = NULL;
+
+const pseudo_typeS obj_pseudo_table[] = {
+#ifndef IGNORE_DEBUG
+ { "def", obj_coff_def, 0 },
+ { "dim", obj_coff_dim, 0 },
+ { "endef", obj_coff_endef, 0 },
+ { "line", obj_coff_line, 0 },
+ { "ln", obj_coff_ln, 0 },
+ { "scl", obj_coff_scl, 0 },
+ { "size", obj_coff_size, 0 },
+ { "tag", obj_coff_tag, 0 },
+ { "type", obj_coff_type, 0 },
+ { "val", obj_coff_val, 0 },
+#else
+ { "def", s_ignore, 0 },
+ { "dim", s_ignore, 0 },
+ { "endef", s_ignore, 0 },
+ { "line", s_ignore, 0 },
+ { "ln", s_ignore, 0 },
+ { "scl", s_ignore, 0 },
+ { "size", s_ignore, 0 },
+ { "tag", s_ignore, 0 },
+ { "type", s_ignore, 0 },
+ { "val", s_ignore, 0 },
+#endif /* ignore debug */
+
+ { "ident", s_ignore, 0 }, /* we don't yet handle this. */
+
+
+ /* stabs aka a.out aka b.out directives for debug symbols.
+ Currently ignored silently. Except for .line at which
+ we guess from context. */
+ { "desc", s_ignore, 0 }, /* def */
+ /* { "line", s_ignore, 0 }, */ /* source code line number */
+ { "stabd", obj_coff_stab, 'd' }, /* stabs */
+ { "stabn", obj_coff_stab, 'n' }, /* stabs */
+ { "stabs", obj_coff_stab, 's' }, /* stabs */
+
+ /* stabs-in-coff (?) debug pseudos (ignored) */
+ { "optim", s_ignore, 0 }, /* For sun386i cc (?) */
+ /* other stuff */
+ { "ABORT", s_abort, 0 },
+
+ { NULL} /* end sentinel */
+}; /* obj_pseudo_table */
+
+
+/* obj dependant output values */
+#ifdef BFD_HEADERS
+static struct internal_scnhdr bss_section_header;
+struct internal_scnhdr data_section_header;
+struct internal_scnhdr text_section_header;
+#else
+static SCNHDR bss_section_header;
+SCNHDR data_section_header;
+SCNHDR text_section_header;
+#endif
+/* Relocation. */
+
+static int reloc_compare(p1, p2)
+#ifdef BFD_HEADERS
+struct internal_reloc *p1, *p2;
+#else
+RELOC *p1, *p2;
+#endif
+{
+ return (int)(p1->r_vaddr - p2->r_vaddr);
+}
+
+/*
+ * emit_relocations()
+ *
+ * Crawl along a fixS chain. Emit the segment's relocations.
+ */
+
+void obj_emit_relocations(where, fixP, segment_address_in_file)
+char **where;
+fixS *fixP; /* Fixup chain for this segment. */
+relax_addressT segment_address_in_file;
+{
+#ifdef BFD_HEADERS
+ struct internal_reloc *ri_table;
+#else
+ RELOC *ri_table;
+#endif
+ symbolS *symbolP;
+ int i, count;
+ fixS *p;
+
+ for (count = 0, p = fixP; p ; p = p->fx_next)
+ if (p->fx_addsy) count++;
+ if (!count)
+ return;
+
+#ifdef BFD_HEADERS
+ ri_table = (struct internal_reloc *) calloc(sizeof(*ri_table),count);
+#else
+ ri_table = (RELOC *) calloc(sizeof(*ri_table),count);
+#endif
+ if (!ri_table)
+ as_fatal ("obj_emit_relocations: Could not malloc relocation table");
+
+#ifdef TC_I960
+ callj_table = (char *)malloc (sizeof(char)*count);
+ if (!callj_table)
+ as_fatal ("obj_emit_relocations: Could not malloc callj table");
+#endif
+
+ for (i = 0; fixP; fixP = fixP->fx_next) {
+ if (symbolP = fixP->fx_addsy) {
+#if defined(TC_M68K)
+ ri_table[i].r_type = (fixP->fx_pcrel ?
+ (fixP->fx_size == 1 ? R_PCRBYTE :
+ fixP->fx_size == 2 ? R_PCRWORD :
+ R_PCRLONG):
+ (fixP->fx_size == 1 ? R_RELBYTE :
+ fixP->fx_size == 2 ? R_RELWORD :
+ R_RELLONG));
+#elif defined(TC_I386)
+ /* FIXME-SOON R_OFF8 & R_DIR16 are a vague guess, completly
+ untested. */
+ ri_table[i].r_type = (fixP->fx_pcrel ?
+ (fixP->fx_size == 1 ? R_PCRBYTE :
+ fixP->fx_size == 2 ? R_PCRWORD :
+ R_PCRLONG):
+ (fixP->fx_size == 1 ? R_OFF8 :
+ fixP->fx_size == 2 ? R_DIR16 :
+ R_DIR32));
+#elif defined(TC_I960)
+ ri_table[i].r_type = (fixP->fx_pcrel
+ ? R_IPRMED
+ : R_RELLONG);
+ callj_table[i] = fixP->fx_callj ? 1 : 0;
+#elif defined(TC_A29K)
+ ri_table[i].r_type = tc_coff_fix2rtype(fixP);
+
+#else
+#error you lose
+#endif /* TC_M68K || TC_I386 */
+ ri_table[i].r_vaddr = (fixP->fx_frag->fr_address
+ + fixP->fx_where);
+ /* If symbol associated to relocation entry is a bss symbol
+ or undefined symbol just remember the index of the symbol.
+ Otherwise store the index of the symbol describing the
+ section the symbol belong to. This heuristic speeds up ld.
+ */
+ /* Local symbols can generate relocation information. In case
+ of structure return for instance. But they have no symbol
+ number because they won't be emitted in the final object.
+ In the case where they are in the BSS section, this leads
+ to an incorrect r_symndx.
+ Under bsd the loader do not care if the symbol reference
+ is incorrect. But the SYS V ld complains about this. To
+ avoid this we associate the symbol to the associated
+ section, *even* if it is the BSS section. */
+ /* If someone can tell me why the other symbols of the bss
+ section are not associated with the .bss section entry,
+ I'd be gratefull. I guess that it has to do with the special
+ nature of the .bss section. Or maybe this is because the
+ bss symbols are declared in the common section and can
+ be resized later. Can it break code some where ? */
+ ri_table[i].r_symndx = (S_GET_SEGMENT(symbolP) == SEG_TEXT
+ ? dot_text_symbol->sy_number
+ : (S_GET_SEGMENT(symbolP) == SEG_DATA
+ ? dot_data_symbol->sy_number
+ : ((SF_GET_LOCAL(symbolP)
+ ? dot_bss_symbol->sy_number
+ : symbolP->sy_number)))); /* bss or undefined */
+
+ /* md_ri_to_chars((char *) &ri, ri); */ /* Last step : write md f */
+
+ i++;
+ } /* if there's a symbol */
+ } /* for each fixP */
+
+ /*
+ * AIX ld prefer to have the reloc table with r_vaddr sorted.
+ * But sorting it should not hurt any other ld.
+ */
+ qsort (ri_table, count, sizeof(*ri_table), reloc_compare);
+
+ for (i = 0; i < count; i++)
+ {
+#ifdef BFD_HEADERS
+ *where += bfd_coff_swap_reloc_out(stdoutput, &ri_table[i], *where);
+# ifdef TC_A29K
+ /* The 29k has a special kludge for the high 16 bit reloc.
+ Two relocations are emmited, R_IHIHALF, and R_IHCONST.
+ The second one doesn't contain a symbol, but uses the
+ value for offset */
+ if (ri_table[i].r_type == R_IHIHALF)
+ {
+ /* now emit the second bit */
+ ri_table[i].r_type = R_IHCONST;
+ ri_table[i].r_symndx = fixP->fx_addnumber;
+ *where += bfd_coff_swap_reloc_out(stdoutput, &ri_table[i],
+ *where);
+ }
+# endif /* TC_A29K */
+
+#else /* not BFD_HEADERS */
+ append(where, (char *) &ri_table[i], RELSZ);
+#endif /* not BFD_HEADERS */
+
+#ifdef TC_I960
+ if (callj_table[i])
+ {
+ ri_table[i].r_type = R_OPTCALL;
+# ifdef BFD_HEADERS
+ *where += bfd_coff_swap_reloc_out(stdoutput, &ri_table[i],
+ *where);
+# else
+ append(where, (char *) &ri_table[i], (unsigned long)RELSZ);
+# endif /* BFD_HEADERS */
+ } /* if it's a callj, do it again for the opcode */
+#endif /* TC_I960 */
+ }
+
+ free (ri_table);
+#ifdef TC_I960
+ free (callj_table);
+#endif
+
+ return;
+} /* obj_emit_relocations() */
+
+/* Coff file generation & utilities */
+
+#ifdef BFD_HEADERS
+void obj_header_append(where, headers)
+char **where;
+object_headers *headers;
+{
+ tc_headers_hook(headers);
+ *where += bfd_coff_swap_filehdr_out(stdoutput, &(headers->filehdr), *where);
+#ifndef OBJ_COFF_OMIT_OPTIONAL_HEADER
+ *where += bfd_coff_swap_aouthdr_out(stdoutput, &(headers->aouthdr), *where);
+#endif
+ obj_coff_section_header_append(where, &text_section_header);
+ obj_coff_section_header_append(where, &data_section_header);
+ obj_coff_section_header_append(where, &bss_section_header);
+
+}
+
+#else
+
+void obj_header_append(where, headers)
+char **where;
+object_headers *headers;
+{
+ tc_headers_hook(headers);
+
+#ifdef CROSS_COMPILE
+ /* Eventually swap bytes for cross compilation for file header */
+ md_number_to_chars(*where, headers->filehdr.f_magic, sizeof(headers->filehdr.f_magic));
+ *where += sizeof(headers->filehdr.f_magic);
+ md_number_to_chars(*where, headers->filehdr.f_nscns, sizeof(headers->filehdr.f_nscns));
+ *where += sizeof(headers->filehdr.f_nscns);
+ md_number_to_chars(*where, headers->filehdr.f_timdat, sizeof(headers->filehdr.f_timdat));
+ *where += sizeof(headers->filehdr.f_timdat);
+ md_number_to_chars(*where, headers->filehdr.f_symptr, sizeof(headers->filehdr.f_symptr));
+ *where += sizeof(headers->filehdr.f_symptr);
+ md_number_to_chars(*where, headers->filehdr.f_nsyms, sizeof(headers->filehdr.f_nsyms));
+ *where += sizeof(headers->filehdr.f_nsyms);
+ md_number_to_chars(*where, headers->filehdr.f_opthdr, sizeof(headers->filehdr.f_opthdr));
+ *where += sizeof(headers->filehdr.f_opthdr);
+ md_number_to_chars(*where, headers->filehdr.f_flags, sizeof(headers->filehdr.f_flags));
+ *where += sizeof(headers->filehdr.f_flags);
+
+#ifndef OBJ_COFF_OMIT_OPTIONAL_HEADER
+ /* Eventually swap bytes for cross compilation for a.out header */
+ md_number_to_chars(*where, headers->aouthdr.magic, sizeof(headers->aouthdr.magic));
+ *where += sizeof(headers->aouthdr.magic);
+ md_number_to_chars(*where, headers->aouthdr.vstamp, sizeof(headers->aouthdr.vstamp));
+ *where += sizeof(headers->aouthdr.vstamp);
+ md_number_to_chars(*where, headers->aouthdr.tsize, sizeof(headers->aouthdr.tsize));
+ *where += sizeof(headers->aouthdr.tsize);
+ md_number_to_chars(*where, headers->aouthdr.dsize, sizeof(headers->aouthdr.dsize));
+ *where += sizeof(headers->aouthdr.dsize);
+ md_number_to_chars(*where, headers->aouthdr.bsize, sizeof(headers->aouthdr.bsize));
+ *where += sizeof(headers->aouthdr.bsize);
+ md_number_to_chars(*where, headers->aouthdr.entry, sizeof(headers->aouthdr.entry));
+ *where += sizeof(headers->aouthdr.entry);
+ md_number_to_chars(*where, headers->aouthdr.text_start, sizeof(headers->aouthdr.text_start));
+ *where += sizeof(headers->aouthdr.text_start);
+ md_number_to_chars(*where, headers->aouthdr.data_start, sizeof(headers->aouthdr.data_start));
+ *where += sizeof(headers->aouthdr.data_start);
+ md_number_to_chars(*where, headers->aouthdr.tagentries, sizeof(headers->aouthdr.tagentries));
+ *where += sizeof(headers->aouthdr.tagentries);
+#endif /* OBJ_COFF_OMIT_OPTIONAL_HEADER */
+
+#else /* CROSS_COMPILE */
+
+ append(where, (char *) &headers->filehdr, sizeof(headers->filehdr));
+#ifndef OBJ_COFF_OMIT_OPTIONAL_HEADER
+ append(where, (char *) &headers->aouthdr, sizeof(headers->aouthdr));
+#endif /* OBJ_COFF_OMIT_OPTIONAL_HEADER */
+
+#endif /* CROSS_COMPILE */
+
+ /* Output the section headers */
+ obj_coff_section_header_append(where, &text_section_header);
+ obj_coff_section_header_append(where, &data_section_header);
+ obj_coff_section_header_append(where, &bss_section_header);
+
+ return;
+} /* obj_header_append() */
+#endif
+void obj_symbol_to_chars(where, symbolP)
+char **where;
+symbolS *symbolP;
+{
+#ifdef BFD_HEADERS
+ unsigned int numaux = symbolP->sy_symbol.ost_entry.n_numaux;
+ unsigned int i;
+
+ if (S_GET_SEGMENT(symbolP) == SEG_REGISTER) {
+ S_SET_SEGMENT(symbolP, SEG_ABSOLUTE);
+ }
+ *where += bfd_coff_swap_sym_out(stdoutput, &symbolP->sy_symbol.ost_entry,
+ *where);
+
+ for (i = 0; i < numaux; i++)
+ {
+ *where += bfd_coff_swap_aux_out(stdoutput,
+ &symbolP->sy_symbol.ost_auxent[i],
+ S_GET_DATA_TYPE(symbolP),
+ S_GET_STORAGE_CLASS(symbolP),
+ *where);
+ }
+
+#else /* BFD_HEADERS */
+ SYMENT *syment = &symbolP->sy_symbol.ost_entry;
+ int i;
+ char numaux = syment->n_numaux;
+ unsigned short type = S_GET_DATA_TYPE(symbolP);
+
+#ifdef CROSS_COMPILE
+ md_number_to_chars(*where, syment->n_value, sizeof(syment->n_value));
+ *where += sizeof(syment->n_value);
+ md_number_to_chars(*where, syment->n_scnum, sizeof(syment->n_scnum));
+ *where += sizeof(syment->n_scnum);
+ md_number_to_chars(*where, 0, sizeof(short)); /* pad n_flags */
+ *where += sizeof(short);
+ md_number_to_chars(*where, syment->n_type, sizeof(syment->n_type));
+ *where += sizeof(syment->n_type);
+ md_number_to_chars(*where, syment->n_sclass, sizeof(syment->n_sclass));
+ *where += sizeof(syment->n_sclass);
+ md_number_to_chars(*where, syment->n_numaux, sizeof(syment->n_numaux));
+ *where += sizeof(syment->n_numaux);
+#else /* CROSS_COMPILE */
+ append(where, (char *) syment, sizeof(*syment));
+#endif /* CROSS_COMPILE */
+
+ /* Should do the following : if (.file entry) MD(..)... else if (static entry) MD(..) */
+ if (numaux > OBJ_COFF_MAX_AUXENTRIES) {
+ as_bad("Internal error? too many auxents for symbol");
+ } /* too many auxents */
+
+ for (i = 0; i < numaux; ++i) {
+#ifdef CROSS_COMPILE
+#if 0 /* This code has never been tested */
+ /* The most common case, x_sym entry. */
+ if ((SF_GET(symbolP) & (SF_FILE | SF_STATICS)) == 0) {
+ md_number_to_chars(*where, auxP->x_sym.x_tagndx, sizeof(auxP->x_sym.x_tagndx));
+ *where += sizeof(auxP->x_sym.x_tagndx);
+ if (ISFCN(type)) {
+ md_number_to_chars(*where, auxP->x_sym.x_misc.x_fsize, sizeof(auxP->x_sym.x_misc.x_fsize));
+ *where += sizeof(auxP->x_sym.x_misc.x_fsize);
+ } else {
+ md_number_to_chars(*where, auxP->x_sym.x_misc.x_lnno, sizeof(auxP->x_sym.x_misc.x_lnno));
+ *where += sizeof(auxP->x_sym.x_misc.x_lnno);
+ md_number_to_chars(*where, auxP->x_sym.x_misc.x_size, sizeof(auxP->x_sym.x_misc.x_size));
+ *where += sizeof(auxP->x_sym.x_misc.x_size);
+ }
+ if (ISARY(type)) {
+ register int index;
+ for (index = 0; index < DIMNUM; index++)
+ md_number_to_chars(*where, auxP->x_sym.x_fcnary.x_ary.x_dimen[index], sizeof(auxP->x_sym.x_fcnary.x_ary.x_dimen[index]));
+ *where += sizeof(auxP->x_sym.x_fcnary.x_ary.x_dimen[index]);
+ } else {
+ md_number_to_chars(*where, auxP->x_sym.x_fcnary.x_fcn.x_lnnoptr, sizeof(auxP->x_sym.x_fcnary.x_fcn.x_lnnoptr));
+ *where += sizeof(auxP->x_sym.x_fcnary.x_fcn.x_lnnoptr);
+ md_number_to_chars(*where, auxP->x_sym.x_fcnary.x_fcn.x_endndx, sizeof(auxP->x_sym.x_fcnary.x_fcn.x_endndx));
+ *where += sizeof(auxP->x_sym.x_fcnary.x_fcn.x_endndx);
+ }
+ md_number_to_chars(*where, auxP->x_sym.x_tvndx, sizeof(auxP->x_sym.x_tvndx));
+ *where += sizeof(auxP->x_sym.x_tvndx);
+ } else if (SF_GET_FILE(symbolP)) { /* .file */
+ ;
+ } else if (SF_GET_STATICS(symbolP)) { /* .text, .data, .bss symbols */
+ md_number_to_chars(*where, auxP->x_scn.x_scnlen, sizeof(auxP->x_scn.x_scnlen));
+ *where += sizeof(auxP->x_scn.x_scnlen);
+ md_number_to_chars(*where, auxP->x_scn.x_nreloc, sizeof(auxP->x_scn.x_nreloc));
+ *where += sizeof(auxP->x_scn.x_nreloc);
+ md_number_to_chars(*where, auxP->x_scn.x_nlinno, sizeof(auxP->x_scn.x_nlinno));
+ *where += sizeof(auxP->x_scn.x_nlinno);
+ }
+#endif /* 0 */
+#else /* CROSS_COMPILE */
+ append(where, (char *) &symbolP->sy_symbol.ost_auxent[i], sizeof(symbolP->sy_symbol.ost_auxent[i]));
+#endif /* CROSS_COMPILE */
+
+ }; /* for each aux in use */
+#endif /* BFD_HEADERS */
+ return;
+} /* obj_symbol_to_chars() */
+
+#ifdef BFD_HEADERS
+static void obj_coff_section_header_append(where, header)
+char **where;
+struct internal_scnhdr *header;
+{
+ *where += bfd_coff_swap_scnhdr_out(stdoutput, header, *where);
+}
+#else
+static void obj_coff_section_header_append(where, header)
+char **where;
+SCNHDR *header;
+{
+#ifdef CROSS_COMPILE
+ memcpy(*where, header->s_name, sizeof(header->s_name));
+ *where += sizeof(header->s_name);
+
+ md_number_to_chars(*where, header->s_paddr, sizeof(header->s_paddr));
+ *where += sizeof(header->s_paddr);
+
+ md_number_to_chars(*where, header->s_vaddr, sizeof(header->s_vaddr));
+ *where += sizeof(header->s_vaddr);
+
+ md_number_to_chars(*where, header->s_size, sizeof(header->s_size));
+ *where += sizeof(header->s_size);
+
+ md_number_to_chars(*where, header->s_scnptr, sizeof(header->s_scnptr));
+ *where += sizeof(header->s_scnptr);
+
+ md_number_to_chars(*where, header->s_relptr, sizeof(header->s_relptr));
+ *where += sizeof(header->s_relptr);
+
+ md_number_to_chars(*where, header->s_lnnoptr, sizeof(header->s_lnnoptr));
+ *where += sizeof(header->s_lnnoptr);
+
+ md_number_to_chars(*where, header->s_nreloc, sizeof(header->s_nreloc));
+ *where += sizeof(header->s_nreloc);
+
+ md_number_to_chars(*where, header->s_nlnno, sizeof(header->s_nlnno));
+ *where += sizeof(header->s_nlnno);
+
+ md_number_to_chars(*where, header->s_flags, sizeof(header->s_flags));
+ *where += sizeof(header->s_flags);
+
+#ifdef TC_I960
+ md_number_to_chars(*where, header->s_align, sizeof(header->s_align));
+ *where += sizeof(header->s_align);
+#endif /* TC_I960 */
+
+#else /* CROSS_COMPILE */
+
+ append(where, (char *) header, sizeof(*header));
+
+#endif /* CROSS_COMPILE */
+
+ return;
+} /* obj_coff_section_header_append() */
+
+#endif
+void obj_emit_symbols(where, symbol_rootP)
+char **where;
+symbolS *symbol_rootP;
+{
+ symbolS *symbolP;
+ /*
+ * Emit all symbols left in the symbol chain.
+ */
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) {
+ /* Used to save the offset of the name. It is used to point
+ to the string in memory but must be a file offset. */
+ register char * temp;
+
+ tc_coff_symbol_emit_hook(symbolP);
+
+ temp = S_GET_NAME(symbolP);
+ if (SF_GET_STRING(symbolP)) {
+ S_SET_OFFSET(symbolP, symbolP->sy_name_offset);
+ S_SET_ZEROES(symbolP, 0);
+ } else {
+ memset(symbolP->sy_symbol.ost_entry.n_name, '\0', SYMNMLEN);
+ strncpy(symbolP->sy_symbol.ost_entry.n_name, temp, SYMNMLEN);
+ }
+ obj_symbol_to_chars(where, symbolP);
+ S_SET_NAME(symbolP,temp);
+ }
+} /* obj_emit_symbols() */
+
+/* Merge a debug symbol containing debug information into a normal symbol. */
+
+void c_symbol_merge(debug, normal)
+symbolS *debug;
+symbolS *normal;
+{
+ S_SET_DATA_TYPE(normal, S_GET_DATA_TYPE(debug));
+ S_SET_STORAGE_CLASS(normal, S_GET_STORAGE_CLASS(debug));
+
+ if (S_GET_NUMBER_AUXILIARY(debug) > S_GET_NUMBER_AUXILIARY(normal)) {
+ S_SET_NUMBER_AUXILIARY(normal, S_GET_NUMBER_AUXILIARY(debug));
+ } /* take the most we have */
+
+ if (S_GET_NUMBER_AUXILIARY(debug) > 0) {
+ memcpy((char*)&normal->sy_symbol.ost_auxent[0], (char*)&debug->sy_symbol.ost_auxent[0], S_GET_NUMBER_AUXILIARY(debug) * AUXESZ);
+ } /* Move all the auxiliary information */
+
+ /* Move the debug flags. */
+ SF_SET_DEBUG_FIELD(normal, SF_GET_DEBUG_FIELD(debug));
+} /* c_symbol_merge() */
+
+static symbolS *previous_file_symbol = NULL;
+
+void c_dot_file_symbol(filename)
+char *filename;
+{
+ symbolS* symbolP;
+
+ symbolP = symbol_new(".file",
+ SEG_DEBUG,
+ 0,
+ &zero_address_frag);
+
+ S_SET_STORAGE_CLASS(symbolP, C_FILE);
+ S_SET_NUMBER_AUXILIARY(symbolP, 1);
+ SA_SET_FILE_FNAME(symbolP, filename);
+ SF_SET_DEBUG(symbolP);
+ S_SET_VALUE(symbolP, (long) previous_file_symbol);
+
+ previous_file_symbol = symbolP;
+
+ /* Make sure that the symbol is first on the symbol chain */
+ if (symbol_rootP != symbolP) {
+ if (symbolP == symbol_lastP) {
+ symbol_lastP = symbol_lastP->sy_previous;
+ } /* if it was the last thing on the list */
+
+ symbol_remove(symbolP, &symbol_rootP, &symbol_lastP);
+ symbol_insert(symbolP, symbol_rootP, &symbol_rootP, &symbol_lastP);
+ symbol_rootP = symbolP;
+ } /* if not first on the list */
+
+} /* c_dot_file_symbol() */
+/*
+ * Build a 'section static' symbol.
+ */
+
+char *c_section_symbol(name, value, length, nreloc, nlnno)
+char *name;
+long value;
+long length;
+unsigned short nreloc;
+unsigned short nlnno;
+{
+ symbolS *symbolP;
+
+ symbolP = symbol_new(name,
+ (name[1] == 't'
+ ? SEG_TEXT
+ : (name[1] == 'd'
+ ? SEG_DATA
+ : SEG_BSS)),
+ value,
+ &zero_address_frag);
+
+ S_SET_STORAGE_CLASS(symbolP, C_STAT);
+ S_SET_NUMBER_AUXILIARY(symbolP, 1);
+
+ SA_SET_SCN_SCNLEN(symbolP, length);
+ SA_SET_SCN_NRELOC(symbolP, nreloc);
+ SA_SET_SCN_NLINNO(symbolP, nlnno);
+
+ SF_SET_STATICS(symbolP);
+
+ return (char*)symbolP;
+} /* c_section_symbol() */
+
+void c_section_header(header,
+ name,
+ core_address,
+ size,
+ data_ptr,
+ reloc_ptr,
+ lineno_ptr,
+ reloc_number,
+ lineno_number,
+ alignment)
+#ifdef BFD_HEADERS
+struct internal_scnhdr *header;
+#else
+SCNHDR *header;
+#endif
+char *name;
+long core_address;
+long size;
+long data_ptr;
+long reloc_ptr;
+long lineno_ptr;
+long reloc_number;
+long lineno_number;
+long alignment;
+{
+ strncpy(header->s_name, name, 8);
+ header->s_paddr = header->s_vaddr = core_address;
+ header->s_scnptr = ((header->s_size = size) != 0) ? data_ptr : 0;
+ header->s_relptr = reloc_ptr;
+ header->s_lnnoptr = lineno_ptr;
+ header->s_nreloc = reloc_number;
+ header->s_nlnno = lineno_number;
+
+#ifdef OBJ_COFF_SECTION_HEADER_HAS_ALIGNMENT
+#ifdef OBJ_COFF_BROKEN_ALIGNMENT
+ header->s_align = ((name[1] == 'b' || (size > 0)) ? 16 : 0);
+#else
+ header->s_align = ((alignment == 0)
+ ? 0
+ : (1 << alignment));
+#endif /* OBJ_COFF_BROKEN_ALIGNMENT */
+#endif /* OBJ_COFF_SECTION_HEADER_HAS_ALIGNMENT */
+
+ header->s_flags = STYP_REG | (name[1] == 't'
+ ? STYP_TEXT
+ : (name[1] == 'd'
+ ? STYP_DATA
+ : (name[1] == 'b'
+ ? STYP_BSS
+ : STYP_INFO)));
+ return;
+} /* c_section_header() */
+
+/* Line number handling */
+
+int function_lineoff = -1; /* Offset in line#s where the last function
+ started (the odd entry for line #0) */
+int text_lineno_number = 0;
+int our_lineno_number = 0; /* we use this to build pointers from .bf's
+ into the linetable. It should match
+ exactly the values that are later
+ assigned in text_lineno_number by
+ write.c. */
+lineno* lineno_lastP = (lineno*)0;
+
+int
+ c_line_new(paddr, line_number, frag)
+long paddr;
+unsigned short line_number;
+fragS* frag;
+{
+ lineno* new_line = (lineno*)xmalloc(sizeof(lineno));
+
+ new_line->line.l_addr.l_paddr = paddr;
+ new_line->line.l_lnno = line_number;
+ new_line->frag = (char*)frag;
+ new_line->next = (lineno*)0;
+
+ if (lineno_rootP == (lineno*)0)
+ lineno_rootP = new_line;
+ else
+ lineno_lastP->next = new_line;
+ lineno_lastP = new_line;
+ return LINESZ * our_lineno_number++;
+}
+
+void obj_emit_lineno(where, line, file_start)
+char **where;
+lineno *line;
+char *file_start;
+{
+#ifdef BFD_HEADERS
+ struct bfd_internal_lineno *line_entry;
+#else
+ LINENO *line_entry;
+#endif
+ for (; line; line = line->next) {
+ line_entry = &line->line;
+
+ /* FIXME-SOMEDAY Resolving the sy_number of function linno's used to be done in
+ write_object_file() but their symbols need a fileptr to the lnno, so
+ I moved this resolution check here. xoxorich. */
+
+ if (line_entry->l_lnno == 0) {
+ /* There is a good chance that the symbol pointed to
+ is not the one that will be emitted and that the
+ sy_number is not accurate. */
+ /* char *name; */
+ symbolS *symbolP;
+
+ symbolP = (symbolS *) line_entry->l_addr.l_symndx;
+
+ line_entry->l_addr.l_symndx = symbolP->sy_number;
+ symbolP->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_fcn.x_lnnoptr = *where - file_start;
+
+ } /* if this is a function linno */
+#ifdef BFD_HEADERS
+ *where += bfd_coff_swap_lineno_out(stdoutput, line_entry, *where);
+#else
+ /* No matter which member of the union we process, they are
+ both long. */
+#ifdef CROSS_COMPILE
+ md_number_to_chars(*where, line_entry->l_addr.l_paddr, sizeof(line_entry->l_addr.l_paddr));
+ *where += sizeof(line_entry->l_addr.l_paddr);
+
+ md_number_to_chars(*where, line_entry->l_lnno, sizeof(line_entry->l_lnno));
+ *where += sizeof(line_entry->l_lnno);
+
+#ifdef TC_I960
+ **where = '0';
+ ++*where;
+ **where = '0';
+ ++*where;
+#endif /* TC_I960 */
+
+#else /* CROSS_COMPILE */
+ append(where, (char *) line_entry, LINESZ);
+#endif /* CROSS_COMPILE */
+#endif /* BFD_HEADERS */
+ } /* for each line number */
+
+ return ;
+} /* obj_emit_lineno() */
+
+void obj_symbol_new_hook(symbolP)
+symbolS *symbolP;
+{
+ char underscore = 0; /* Symbol has leading _ */
+
+ /* Effective symbol */
+ /* Store the pointer in the offset. */
+ S_SET_ZEROES(symbolP, 0L);
+ S_SET_DATA_TYPE(symbolP, T_NULL);
+ S_SET_STORAGE_CLASS(symbolP, 0);
+ S_SET_NUMBER_AUXILIARY(symbolP, 0);
+ /* Additional information */
+ symbolP->sy_symbol.ost_flags = 0;
+ /* Auxiliary entries */
+ memset((char*) &symbolP->sy_symbol.ost_auxent[0], '\0', AUXESZ);
+
+#ifdef STRIP_UNDERSCORE
+ /* Remove leading underscore at the beginning of the symbol.
+ * This is to be compatible with the standard librairies.
+ */
+ if (*S_GET_NAME(symbolP) == '_') {
+ underscore = 1;
+ S_SET_NAME(symbolP, S_GET_NAME(symbolP) + 1);
+ } /* strip underscore */
+#endif /* STRIP_UNDERSCORE */
+
+ if (S_IS_STRING(symbolP))
+ SF_SET_STRING(symbolP);
+ if (!underscore && S_IS_LOCAL(symbolP))
+ SF_SET_LOCAL(symbolP);
+
+ return;
+} /* obj_symbol_new_hook() */
+
+/* stack stuff */
+stack* stack_init(chunk_size, element_size)
+unsigned long chunk_size;
+unsigned long element_size;
+{
+ stack* st;
+
+ if ((st = (stack*)malloc(sizeof(stack))) == (stack*)0)
+ return (stack*)0;
+ if ((st->data = malloc(chunk_size)) == (char*)0) {
+ free(st);
+ return (stack*)0;
+ }
+ st->pointer = 0;
+ st->size = chunk_size;
+ st->chunk_size = chunk_size;
+ st->element_size = element_size;
+ return st;
+} /* stack_init() */
+
+void stack_delete(st)
+stack* st;
+{
+ free(st->data);
+ free(st);
+}
+
+char *stack_push(st, element)
+stack *st;
+char *element;
+{
+ if (st->pointer + st->element_size >= st->size) {
+ st->size += st->chunk_size;
+ if ((st->data = xrealloc(st->data, st->size)) == (char*)0)
+ return (char*)0;
+ }
+ memcpy(st->data + st->pointer, element, st->element_size);
+ st->pointer += st->element_size;
+ return st->data + st->pointer;
+} /* stack_push() */
+
+char* stack_pop(st)
+stack* st;
+{
+ if ((st->pointer -= st->element_size) < 0) {
+ st->pointer = 0;
+ return (char*)0;
+ }
+ return st->data + st->pointer;
+}
+
+char* stack_top(st)
+stack* st;
+{
+ return st->data + st->pointer - st->element_size;
+}
+
+
+/*
+ * Handle .ln directives.
+ */
+
+static void obj_coff_ln() {
+ if (def_symbol_in_progress != NULL) {
+ as_warn(".ln pseudo-op inside .def/.endef: ignored.");
+ demand_empty_rest_of_line();
+ return;
+ } /* wrong context */
+
+ c_line_new(obstack_next_free(&frags) - frag_now->fr_literal,
+ get_absolute_expression(),
+ frag_now);
+
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_line() */
+
+/*
+ * def()
+ *
+ * Handle .def directives.
+ *
+ * One might ask : why can't we symbol_new if the symbol does not
+ * already exist and fill it with debug information. Because of
+ * the C_EFCN special symbol. It would clobber the value of the
+ * function symbol before we have a chance to notice that it is
+ * a C_EFCN. And a second reason is that the code is more clear this
+ * way. (at least I think it is :-).
+ *
+ */
+
+#define SKIP_SEMI_COLON() while (*input_line_pointer++ != ';')
+#define SKIP_WHITESPACES() while (*input_line_pointer == ' ' || \
+ *input_line_pointer == '\t') \
+ input_line_pointer++;
+
+static void obj_coff_def(what)
+int what;
+{
+ char name_end; /* Char after the end of name */
+ char *symbol_name; /* Name of the debug symbol */
+ char *symbol_name_copy; /* Temporary copy of the name */
+ unsigned int symbol_name_length;
+ /*$char* directiveP;$ */ /* Name of the pseudo opcode */
+ /*$char directive[MAX_DIRECTIVE];$ */ /* Backup of the directive */
+ /*$char end = 0;$ */ /* If 1, stop parsing */
+
+ if (def_symbol_in_progress != NULL) {
+ as_warn(".def pseudo-op used inside of .def/.endef: ignored.");
+ demand_empty_rest_of_line();
+ return;
+ } /* if not inside .def/.endef */
+
+ SKIP_WHITESPACES();
+
+ def_symbol_in_progress = (symbolS *) obstack_alloc(&notes, sizeof(*def_symbol_in_progress));
+ memset(def_symbol_in_progress, '\0', sizeof(*def_symbol_in_progress));
+
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end();
+ symbol_name_length = strlen(symbol_name);
+ symbol_name_copy = xmalloc(symbol_name_length + 1);
+ strcpy(symbol_name_copy, symbol_name);
+
+ /* Initialize the new symbol */
+#ifdef STRIP_UNDERSCORE
+ S_SET_NAME(def_symbol_in_progress, (*symbol_name_copy == '_'
+ ? symbol_name_copy + 1
+ : symbol_name_copy));
+#else /* STRIP_UNDERSCORE */
+ S_SET_NAME(def_symbol_in_progress, symbol_name_copy);
+#endif /* STRIP_UNDERSCORE */
+ /* free(symbol_name_copy); */
+ def_symbol_in_progress->sy_name_offset = ~0;
+ def_symbol_in_progress->sy_number = ~0;
+ def_symbol_in_progress->sy_frag = &zero_address_frag;
+
+ if (S_IS_STRING(def_symbol_in_progress)) {
+ SF_SET_STRING(def_symbol_in_progress);
+ } /* "long" name */
+
+ *input_line_pointer = name_end;
+
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_def() */
+
+unsigned int dim_index;
+static void obj_coff_endef() {
+ symbolS *symbolP;
+ /* DIM BUG FIX sac@cygnus.com */
+ dim_index =0;
+ if (def_symbol_in_progress == NULL) {
+ as_warn(".endef pseudo-op used outside of .def/.endef: ignored.");
+ demand_empty_rest_of_line();
+ return;
+ } /* if not inside .def/.endef */
+
+ /* Set the section number according to storage class. */
+ switch (S_GET_STORAGE_CLASS(def_symbol_in_progress)) {
+ case C_STRTAG:
+ case C_ENTAG:
+ case C_UNTAG:
+ SF_SET_TAG(def_symbol_in_progress);
+ /* intentional fallthrough */
+ case C_FILE:
+ case C_TPDEF:
+ SF_SET_DEBUG(def_symbol_in_progress);
+ S_SET_SEGMENT(def_symbol_in_progress, SEG_DEBUG);
+ break;
+
+ case C_EFCN:
+ SF_SET_LOCAL(def_symbol_in_progress); /* Do not emit this symbol. */
+ /* intentional fallthrough */
+ case C_BLOCK:
+ SF_SET_PROCESS(def_symbol_in_progress); /* Will need processing before writing */
+ /* intentional fallthrough */
+ case C_FCN:
+ S_SET_SEGMENT(def_symbol_in_progress, SEG_TEXT);
+
+ if (def_symbol_in_progress->sy_symbol.ost_entry.n_name[1] == 'b') { /* .bf */
+ if (function_lineoff < 0) {
+ fprintf(stderr, "`.bf' symbol without preceding function\n");
+ } /* missing function symbol */
+ SA_GET_SYM_LNNOPTR(def_symbol_in_progress) = function_lineoff;
+ SF_SET_PROCESS(def_symbol_in_progress); /* Will need relocating */
+ function_lineoff = -1;
+ }
+ break;
+
+#ifdef C_AUTOARG
+ case C_AUTOARG:
+#endif /* C_AUTOARG */
+ case C_AUTO:
+ case C_REG:
+ case C_MOS:
+ case C_MOE:
+ case C_MOU:
+ case C_ARG:
+ case C_REGPARM:
+ case C_FIELD:
+ case C_EOS:
+ SF_SET_DEBUG(def_symbol_in_progress);
+ S_SET_SEGMENT(def_symbol_in_progress, SEG_ABSOLUTE);
+ break;
+
+ case C_EXT:
+ case C_STAT:
+ case C_LABEL:
+ /* Valid but set somewhere else (s_comm, s_lcomm, colon) */
+ break;
+
+ case C_USTATIC:
+ case C_EXTDEF:
+ case C_ULABEL:
+ as_warn("unexpected storage class %d", S_GET_STORAGE_CLASS(def_symbol_in_progress));
+ break;
+ } /* switch on storage class */
+
+ /* Now that we have built a debug symbol, try to
+ find if we should merge with an existing symbol
+ or not. If a symbol is C_EFCN or SEG_ABSOLUTE or
+ untagged SEG_DEBUG it never merges. */
+
+ /* Two cases for functions. Either debug followed
+ by definition or definition followed by debug.
+ For definition first, we will merge the debug
+ symbol into the definition. For debug first, the
+ lineno entry MUST point to the definition
+ function or else it will point off into space
+ when obj_crawl_symbol_chain() merges the debug
+ symbol into the real symbol. Therefor, let's
+ presume the debug symbol is a real function
+ reference. */
+
+ /* FIXME-SOON If for some reason the definition
+ label/symbol is never seen, this will probably
+ leave an undefined symbol at link time. */
+
+ if (S_GET_STORAGE_CLASS(def_symbol_in_progress) == C_EFCN
+ || (S_GET_SEGMENT(def_symbol_in_progress) == SEG_DEBUG
+ && !SF_GET_TAG(def_symbol_in_progress))
+ || S_GET_SEGMENT(def_symbol_in_progress) == SEG_ABSOLUTE
+ || (symbolP = symbol_find_base(S_GET_NAME(def_symbol_in_progress), DO_NOT_STRIP)) == NULL) {
+
+ symbol_append(def_symbol_in_progress, symbol_lastP, &symbol_rootP, &symbol_lastP);
+
+ } else {
+ /* This symbol already exists, merge the
+ newly created symbol into the old one.
+ This is not mandatory. The linker can
+ handle duplicate symbols correctly. But I
+ guess that it save a *lot* of space if
+ the assembly file defines a lot of
+ symbols. [loic] */
+
+ /* The debug entry (def_symbol_in_progress)
+ is merged into the previous definition. */
+
+ c_symbol_merge(def_symbol_in_progress, symbolP);
+ /* FIXME-SOON Should *def_symbol_in_progress be free'd? xoxorich. */
+ def_symbol_in_progress = symbolP;
+
+ if (SF_GET_FUNCTION(def_symbol_in_progress)
+ || SF_GET_TAG(def_symbol_in_progress)) {
+ /* For functions, and tags, the symbol *must* be where the debug symbol
+ appears. Move the existing symbol to the current place. */
+ /* If it already is at the end of the symbol list, do nothing */
+ if (def_symbol_in_progress != symbol_lastP) {
+ symbol_remove(def_symbol_in_progress, &symbol_rootP, &symbol_lastP);
+ symbol_append(def_symbol_in_progress, symbol_lastP, &symbol_rootP, &symbol_lastP);
+ } /* if not already in place */
+ } /* if function */
+ } /* normal or mergable */
+
+ if (SF_GET_TAG(def_symbol_in_progress)
+ && symbol_find_base(S_GET_NAME(def_symbol_in_progress), DO_NOT_STRIP) == NULL) {
+ tag_insert(S_GET_NAME(def_symbol_in_progress), def_symbol_in_progress);
+ } /* If symbol is a {structure,union} tag, associate symbol to its name. */
+
+ if (SF_GET_FUNCTION(def_symbol_in_progress)) {
+ know(sizeof(def_symbol_in_progress) <= sizeof(long));
+ function_lineoff = c_line_new((long) def_symbol_in_progress, 0, &zero_address_frag);
+ SF_SET_PROCESS(def_symbol_in_progress);
+
+ if (symbolP == NULL) {
+ /* That is, if this is the first
+ time we've seen the function... */
+ symbol_table_insert(def_symbol_in_progress);
+ } /* definition follows debug */
+ } /* Create the line number entry pointing to the function being defined */
+
+ def_symbol_in_progress = NULL;
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_endef() */
+
+static void obj_coff_dim()
+{
+ register int dim_index;
+
+ if (def_symbol_in_progress == NULL) {
+ as_warn(".dim pseudo-op used outside of .def/.endef: ignored.");
+ demand_empty_rest_of_line();
+ return;
+ } /* if not inside .def/.endef */
+
+ S_SET_NUMBER_AUXILIARY(def_symbol_in_progress, 1);
+
+ for (dim_index = 0; dim_index < DIMNUM; dim_index++) {
+ SKIP_WHITESPACES();
+ SA_SET_SYM_DIMEN(def_symbol_in_progress, dim_index, get_absolute_expression());
+
+ switch (*input_line_pointer) {
+
+ case ',':
+ input_line_pointer++;
+ break;
+
+ default:
+ as_warn("badly formed .dim directive ignored");
+ /* intentional fallthrough */
+ case '\n':
+ case ';':
+ dim_index = DIMNUM;
+ break;
+ } /* switch on following character */
+ } /* for each dimension */
+
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_dim() */
+
+static void obj_coff_line() {
+ if (def_symbol_in_progress == NULL) {
+ obj_coff_ln();
+ return;
+ } /* if it looks like a stabs style line */
+
+ S_SET_NUMBER_AUXILIARY(def_symbol_in_progress, 1);
+ SA_SET_SYM_LNNO(def_symbol_in_progress, get_absolute_expression());
+
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_line() */
+
+static void obj_coff_size() {
+ if (def_symbol_in_progress == NULL) {
+ as_warn(".size pseudo-op used outside of .def/.endef ignored.");
+ demand_empty_rest_of_line();
+ return;
+ } /* if not inside .def/.endef */
+
+ S_SET_NUMBER_AUXILIARY(def_symbol_in_progress, 1);
+ SA_SET_SYM_SIZE(def_symbol_in_progress, get_absolute_expression());
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_size() */
+
+static void obj_coff_scl() {
+ if (def_symbol_in_progress == NULL) {
+ as_warn(".scl pseudo-op used outside of .def/.endef ignored.");
+ demand_empty_rest_of_line();
+ return;
+ } /* if not inside .def/.endef */
+
+ S_SET_STORAGE_CLASS(def_symbol_in_progress, get_absolute_expression());
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_scl() */
+
+static void obj_coff_tag() {
+ char *symbol_name;
+ char name_end;
+
+ if (def_symbol_in_progress == NULL) {
+ as_warn(".tag pseudo-op used outside of .def/.endef ignored.");
+ demand_empty_rest_of_line();
+ return;
+ } /* if not inside .def/.endef */
+
+ S_SET_NUMBER_AUXILIARY(def_symbol_in_progress, 1);
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end();
+
+ /* Assume that the symbol referred to by .tag is always defined. */
+ /* This was a bad assumption. I've added find_or_make. xoxorich. */
+ SA_SET_SYM_TAGNDX(def_symbol_in_progress, (long) tag_find_or_make(symbol_name));
+ if (SA_GET_SYM_TAGNDX(def_symbol_in_progress) == 0L) {
+ as_warn("tag not found for .tag %s", symbol_name);
+ } /* not defined */
+
+ SF_SET_TAGGED(def_symbol_in_progress);
+ *input_line_pointer = name_end;
+
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_tag() */
+
+static void obj_coff_type() {
+ if (def_symbol_in_progress == NULL) {
+ as_warn(".type pseudo-op used outside of .def/.endef ignored.");
+ demand_empty_rest_of_line();
+ return;
+ } /* if not inside .def/.endef */
+
+ S_SET_DATA_TYPE(def_symbol_in_progress, get_absolute_expression());
+
+ if (ISFCN(S_GET_DATA_TYPE(def_symbol_in_progress)) &&
+ S_GET_STORAGE_CLASS(def_symbol_in_progress) != C_TPDEF) {
+ SF_SET_FUNCTION(def_symbol_in_progress);
+ } /* is a function */
+
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_type() */
+
+static void obj_coff_val() {
+ if (def_symbol_in_progress == NULL) {
+ as_warn(".val pseudo-op used outside of .def/.endef ignored.");
+ demand_empty_rest_of_line();
+ return;
+ } /* if not inside .def/.endef */
+
+ if (is_name_beginner(*input_line_pointer)) {
+ char *symbol_name = input_line_pointer;
+ char name_end = get_symbol_end();
+
+ if (!strcmp(symbol_name, ".")) {
+ def_symbol_in_progress->sy_frag = frag_now;
+ S_SET_VALUE(def_symbol_in_progress, obstack_next_free(&frags) - frag_now->fr_literal);
+ /* If the .val is != from the .def (e.g. statics) */
+ } else if (strcmp(S_GET_NAME(def_symbol_in_progress), symbol_name)) {
+ def_symbol_in_progress->sy_forward = symbol_find_or_make(symbol_name);
+
+ /* If the segment is undefined when the forward
+ reference is solved, then copy the segment id
+ from the forward symbol. */
+ SF_SET_GET_SEGMENT(def_symbol_in_progress);
+ }
+ /* Otherwise, it is the name of a non debug symbol and its value will be calculated later. */
+ *input_line_pointer = name_end;
+ } else {
+ S_SET_VALUE(def_symbol_in_progress, get_absolute_expression());
+ } /* if symbol based */
+
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_val() */
+
+/*
+ * Maintain a list of the tagnames of the structres.
+ */
+
+static void tag_init() {
+ tag_hash = hash_new();
+ return ;
+} /* tag_init() */
+
+static void tag_insert(name, symbolP)
+char *name;
+symbolS *symbolP;
+{
+ register char * error_string;
+
+ if (*(error_string = hash_jam(tag_hash, name, (char *)symbolP))) {
+ as_fatal("Inserting \"%s\" into structure table failed: %s",
+ name, error_string);
+ }
+ return ;
+} /* tag_insert() */
+
+static symbolS *tag_find_or_make(name)
+char *name;
+{
+ symbolS *symbolP;
+
+ if ((symbolP = tag_find(name)) == NULL) {
+ symbolP = symbol_new(name,
+ SEG_UNKNOWN,
+ 0,
+ &zero_address_frag);
+
+ tag_insert(S_GET_NAME(symbolP), symbolP);
+ symbol_table_insert(symbolP);
+ } /* not found */
+
+ return(symbolP);
+} /* tag_find_or_make() */
+
+static symbolS *tag_find(name)
+char *name;
+{
+#ifdef STRIP_UNDERSCORE
+ if (*name == '_') name++;
+#endif /* STRIP_UNDERSCORE */
+ return((symbolS*)hash_find(tag_hash, name));
+} /* tag_find() */
+
+void obj_read_begin_hook() {
+ /* These had better be the same. Usually 18 bytes. */
+#ifndef BFD_HEADERS
+ know(sizeof(SYMENT) == sizeof(AUXENT));
+ know(SYMESZ == AUXESZ);
+#endif
+ tag_init();
+
+ return;
+} /* obj_read_begin_hook() */
+
+void obj_crawl_symbol_chain(headers)
+object_headers *headers;
+{
+ int symbol_number = 0;
+ lineno *lineP;
+ symbolS *last_functionP = NULL;
+ symbolS *last_tagP;
+ symbolS *symbolP;
+ symbolS *symbol_externP = NULL;
+ symbolS *symbol_extern_lastP = NULL;
+
+ /* Initialize the stack used to keep track of the matching .bb .be */
+ stack* block_stack = stack_init(512, sizeof(symbolS*));
+
+ /* JF deal with forward references first... */
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) {
+
+ if (symbolP->sy_forward) {
+ S_SET_VALUE(symbolP, (S_GET_VALUE(symbolP)
+ + S_GET_VALUE(symbolP->sy_forward)
+ + symbolP->sy_forward->sy_frag->fr_address));
+
+ if (
+#ifndef TE_I386AIX
+ SF_GET_GET_SEGMENT(symbolP)
+#else
+ SF_GET_GET_SEGMENT(symbolP)
+ && S_GET_SEGMENT(symbolP) == SEG_UNKNOWN
+#endif /* TE_I386AIX */
+ ) {
+ S_SET_SEGMENT(symbolP, S_GET_SEGMENT(symbolP->sy_forward));
+ } /* forward segment also */
+
+ symbolP->sy_forward=0;
+ } /* if it has a forward reference */
+ } /* walk the symbol chain */
+
+ tc_crawl_symbol_chain(headers);
+
+ /* The symbol list should be ordered according to the following sequence
+ * order :
+ * . .file symbol
+ * . debug entries for functions
+ * . fake symbols for .text .data and .bss
+ * . defined symbols
+ * . undefined symbols
+ * But this is not mandatory. The only important point is to put the
+ * undefined symbols at the end of the list.
+ */
+
+ if (symbol_rootP == NULL
+ || S_GET_STORAGE_CLASS(symbol_rootP) != C_FILE) {
+ know(!previous_file_symbol);
+ c_dot_file_symbol("fake");
+ } /* Is there a .file symbol ? If not insert one at the beginning. */
+
+ /*
+ * Build up static symbols for .text, .data and .bss
+ */
+ dot_text_symbol = (symbolS*)
+ c_section_symbol(".text",
+ 0,
+ H_GET_TEXT_SIZE(headers),
+ 0/*text_relocation_number */,
+ 0/*text_lineno_number */);
+#ifdef TE_I386AIX
+ symbol_remove(dot_text_symbol, &symbol_rootP, &symbol_lastP);
+ symbol_append(dot_text_symbol, previous_file_symbol,
+ &symbol_rootP, &symbol_lastP);
+#endif /* TE_I386AIX */
+
+ dot_data_symbol = (symbolS*)
+ c_section_symbol(".data",
+ H_GET_TEXT_SIZE(headers),
+ H_GET_DATA_SIZE(headers),
+ 0/*data_relocation_number */,
+ 0); /* There are no data lineno entries */
+#ifdef TE_I386AIX
+ symbol_remove(dot_data_symbol, &symbol_rootP, &symbol_lastP);
+ symbol_append(dot_data_symbol, dot_text_symbol,
+ &symbol_rootP, &symbol_lastP);
+#endif /* TE_I386AIX */
+
+ dot_bss_symbol = (symbolS*)
+ c_section_symbol(".bss",
+ H_GET_TEXT_SIZE(headers) + H_GET_DATA_SIZE(headers),
+ H_GET_BSS_SIZE(headers),
+ 0, /* No relocation for a bss section. */
+ 0); /* There are no bss lineno entries */
+#ifdef TE_I386AIX
+ symbol_remove(dot_bss_symbol, &symbol_rootP, &symbol_lastP);
+ symbol_append(dot_bss_symbol, dot_data_symbol,
+ &symbol_rootP, &symbol_lastP);
+#endif /* TE_I386AIX */
+
+#if defined(DEBUG)
+ verify_symbol_chain(symbol_rootP, symbol_lastP);
+#endif /* DEBUG */
+
+ /* Three traversals of symbol chains here. The
+ first traversal yanks externals into a temporary
+ chain, removing the externals from the global
+ chain, numbers symbols, and does some other guck.
+ The second traversal is on the temporary chain of
+ externals and just appends them to the global
+ chain again, numbering them as we go. The third
+ traversal patches pointers to symbols (using sym
+ indexes). The last traversal was once done as
+ part of the first pass, but that fails when a
+ reference preceeds a definition as the definition
+ has no number at the time we process the
+ reference. */
+
+ /* Note that symbolP will be NULL at the end of a loop
+ if an external was at the beginning of the list (it
+ gets moved off the list). Hence the weird check in
+ the loop control.
+ */
+ for (symbolP = symbol_rootP;
+ symbolP;
+ symbolP = symbolP ? symbol_next(symbolP) : symbol_rootP) {
+ if (!SF_GET_DEBUG(symbolP)) {
+ /* Debug symbols do not need all this rubbish */
+ symbolS* real_symbolP;
+
+ /* L* and C_EFCN symbols never merge. */
+ if (!SF_GET_LOCAL(symbolP)
+ && (real_symbolP = symbol_find_base(S_GET_NAME(symbolP), DO_NOT_STRIP))
+ && real_symbolP != symbolP) {
+ /* FIXME-SOON: where do dups come from? Maybe tag references before definitions? xoxorich. */
+ /* Move the debug data from the debug symbol to the
+ real symbol. Do NOT do the oposite (i.e. move from
+ real symbol to debug symbol and remove real symbol from the
+ list.) Because some pointers refer to the real symbol
+ whereas no pointers refer to the debug symbol. */
+ c_symbol_merge(symbolP, real_symbolP);
+ /* Replace the current symbol by the real one */
+ /* The symbols will never be the last or the first
+ because : 1st symbol is .file and 3 last symbols are
+ .text, .data, .bss */
+ symbol_remove(real_symbolP, &symbol_rootP, &symbol_lastP);
+ symbol_insert(real_symbolP, symbolP, &symbol_rootP, &symbol_lastP);
+ symbol_remove(symbolP, &symbol_rootP, &symbol_lastP);
+ symbolP = real_symbolP;
+ } /* if not local but dup'd */
+
+ if (flagseen['R'] && (S_GET_SEGMENT(symbolP) == SEG_DATA)) {
+ S_SET_SEGMENT(symbolP, SEG_TEXT);
+ } /* push data into text */
+
+ S_SET_VALUE(symbolP, S_GET_VALUE(symbolP) + symbolP->sy_frag->fr_address);
+
+ if (!S_IS_DEFINED(symbolP) && !SF_GET_LOCAL(symbolP)) {
+ S_SET_EXTERNAL(symbolP);
+ } else if (S_GET_STORAGE_CLASS(symbolP) == C_NULL) {
+ if (S_GET_SEGMENT(symbolP) == SEG_TEXT){
+ S_SET_STORAGE_CLASS(symbolP, C_LABEL);
+ } else {
+ S_SET_STORAGE_CLASS(symbolP, C_STAT);
+ }
+ } /* no storage class yet */
+
+ /* Mainly to speed up if not -g */
+ if (SF_GET_PROCESS(symbolP)) {
+ /* Handle the nested blocks auxiliary info. */
+ if (S_GET_STORAGE_CLASS(symbolP) == C_BLOCK) {
+ if (!strcmp(S_GET_NAME(symbolP), ".bb"))
+ stack_push(block_stack, (char *) &symbolP);
+ else { /* .eb */
+ register symbolS* begin_symbolP;
+ begin_symbolP = *(symbolS**)stack_pop(block_stack);
+ if (begin_symbolP == (symbolS*)0)
+ as_warn("mismatched .eb");
+ else
+ SA_SET_SYM_ENDNDX(begin_symbolP, symbol_number+2);
+ }
+ }
+ /* If we are able to identify the type of a function, and we
+ are out of a function (last_functionP == 0) then, the
+ function symbol will be associated with an auxiliary
+ entry. */
+ if (last_functionP == (symbolS*)0 &&
+ SF_GET_FUNCTION(symbolP)) {
+ last_functionP = symbolP;
+
+ if (S_GET_NUMBER_AUXILIARY(symbolP) < 1) {
+ S_SET_NUMBER_AUXILIARY(symbolP, 1);
+ } /* make it at least 1 */
+
+ /* Clobber possible stale .dim information. */
+ memset(symbolP->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_ary.x_dimen,
+ '\0', sizeof(symbolP->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_ary.x_dimen));
+ }
+ /* The C_FCN doesn't need any additional information.
+ I don't even know if this is needed for sdb. But the
+ standard assembler generates it, so...
+ */
+ if (S_GET_STORAGE_CLASS(symbolP) == C_EFCN) {
+ if (last_functionP == (symbolS*)0)
+ as_fatal("C_EFCN symbol out of scope");
+ SA_SET_SYM_FSIZE(last_functionP,
+ (long)(S_GET_VALUE(symbolP) -
+ S_GET_VALUE(last_functionP)));
+ SA_SET_SYM_ENDNDX(last_functionP, symbol_number);
+ last_functionP = (symbolS*)0;
+ }
+ }
+ } else if (SF_GET_TAG(symbolP)) {
+ /* First descriptor of a structure must point to
+ the first slot after the structure description. */
+ last_tagP = symbolP;
+
+ } else if (S_GET_STORAGE_CLASS(symbolP) == C_EOS) {
+ /* +2 take in account the current symbol */
+ SA_SET_SYM_ENDNDX(last_tagP, symbol_number + 2);
+ } else if (S_GET_STORAGE_CLASS(symbolP) == C_FILE) {
+ if (S_GET_VALUE(symbolP)) {
+ S_SET_VALUE((symbolS *) S_GET_VALUE(symbolP), symbol_number);
+ S_SET_VALUE(symbolP, 0);
+ } /* no one points at the first .file symbol */
+ } /* if debug or tag or eos or file */
+
+ /* We must put the external symbols apart. The loader
+ does not bomb if we do not. But the references in
+ the endndx field for a .bb symbol are not corrected
+ if an external symbol is removed between .bb and .be.
+ I.e in the following case :
+ [20] .bb endndx = 22
+ [21] foo external
+ [22] .be
+ ld will move the symbol 21 to the end of the list but
+ endndx will still be 22 instead of 21. */
+
+
+ if (SF_GET_LOCAL(symbolP)) {
+ /* remove C_EFCN and LOCAL (L...) symbols */
+ /* next pointer remains valid */
+ symbol_remove(symbolP, &symbol_rootP, &symbol_lastP);
+
+ } else if (
+#ifdef TE_I386AIX
+ S_GET_STORAGE_CLASS(symbolP) == C_EXT && !SF_GET_FUNCTION(symbolP)
+#else /* not TE_I386AIX */
+ !S_IS_DEFINED(symbolP) && !S_IS_DEBUG(symbolP) && !SF_GET_STATICS(symbolP)
+#endif /* not TE_I386AIX */
+ ) {
+ /* if external, Remove from the list */
+ symbolS *hold = symbol_previous(symbolP);
+
+ symbol_remove(symbolP, &symbol_rootP, &symbol_lastP);
+ symbol_clear_list_pointers(symbolP);
+ symbol_append(symbolP, symbol_extern_lastP, &symbol_externP, &symbol_extern_lastP);
+ symbolP = hold;
+ } else {
+ if (SF_GET_STRING(symbolP)) {
+ symbolP->sy_name_offset = string_byte_count;
+ string_byte_count += strlen(S_GET_NAME(symbolP)) + 1;
+ } else {
+ symbolP->sy_name_offset = 0;
+ } /* fix "long" names */
+
+ symbolP->sy_number = symbol_number;
+ symbol_number += 1 + S_GET_NUMBER_AUXILIARY(symbolP);
+ } /* if local symbol */
+ } /* traverse the symbol list */
+
+ for (symbolP = symbol_externP; symbol_externP;) {
+ symbolS *tmp = symbol_externP;
+
+ /* append */
+ symbol_remove(tmp, &symbol_externP, &symbol_extern_lastP);
+ symbol_append(tmp, symbol_lastP, &symbol_rootP, &symbol_lastP);
+
+ /* and process */
+ if (SF_GET_STRING(tmp)) {
+ tmp->sy_name_offset = string_byte_count;
+ string_byte_count += strlen(S_GET_NAME(tmp)) + 1;
+ } else {
+ tmp->sy_name_offset = 0;
+ } /* fix "long" names */
+
+ tmp->sy_number = symbol_number;
+ symbol_number += 1 + S_GET_NUMBER_AUXILIARY(tmp);
+ } /* append the entire extern chain */
+
+ /* When a tag reference preceeds the tag definition,
+ the definition will not have a number at the time
+ we process the reference during the first
+ traversal. Thus, a second traversal. */
+
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) {
+ if (SF_GET_TAGGED(symbolP)) {
+ SA_SET_SYM_TAGNDX(symbolP, ((symbolS*) SA_GET_SYM_TAGNDX(symbolP))->sy_number);
+ } /* If the symbol has a tagndx entry, resolve it */
+ } /* second traversal */
+
+ know(symbol_externP == NULL);
+ know(symbol_extern_lastP == NULL);
+
+ /* FIXME-SOMEDAY I'm counting line no's here so we know what to put in the section
+ headers, and I'm resolving the addresses since I'm not sure how to
+ do it later. I am NOT resolving the linno's representing functions.
+ Their symbols need a fileptr pointing to this linno when emitted.
+ Thus, I resolve them on emit. xoxorich. */
+
+ for (lineP = lineno_rootP; lineP; lineP = lineP->next) {
+ if (lineP->line.l_lnno > 0) {
+ lineP->line.l_addr.l_paddr += ((fragS*)lineP->frag)->fr_address;
+ } else {
+ ;
+ }
+ text_lineno_number++;
+ } /* for each line number */
+
+ H_SET_SYMBOL_TABLE_SIZE(headers, symbol_number);
+
+ return;
+} /* obj_crawl_symbol_chain() */
+
+/*
+ * Find strings by crawling along symbol table chain.
+ */
+
+void obj_emit_strings(where)
+char **where;
+{
+ symbolS *symbolP;
+
+#ifdef CROSS_COMPILE
+ /* Gotta do md_ byte-ordering stuff for string_byte_count first - KWK */
+ md_number_to_chars(*where, string_byte_count, sizeof(string_byte_count));
+ *where += sizeof(string_byte_count);
+#else /* CROSS_COMPILE */
+ append(where, (char *) &string_byte_count, (unsigned long) sizeof(string_byte_count));
+#endif /* CROSS_COMPILE */
+
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) {
+ if (SF_GET_STRING(symbolP)) {
+ append(where, S_GET_NAME(symbolP), (unsigned long)(strlen(S_GET_NAME(symbolP)) + 1));
+ } /* if it has a string */
+ } /* walk the symbol chain */
+
+ return;
+} /* obj_emit_strings() */
+
+void obj_pre_write_hook(headers)
+object_headers *headers;
+{
+ register int text_relocation_number = 0;
+ register int data_relocation_number = 0;
+ register fixS *fixP;
+
+ H_SET_MAGIC_NUMBER(headers, FILE_HEADER_MAGIC);
+ H_SET_ENTRY_POINT(headers, 0);
+
+ /* FIXME-SOMEDAY this should be done at
+ fixup_segment time but I'm going to wait until I
+ do multiple segments. xoxorich. */
+ /* Count the number of relocation entries for text and data */
+ for (fixP = text_fix_root; fixP; fixP = fixP->fx_next) {
+ if (fixP->fx_addsy) {
+ ++text_relocation_number;
+#ifdef TC_I960
+ /* two relocs per callj under coff. */
+ if (fixP->fx_callj) {
+ ++text_relocation_number;
+ } /* if callj and not already fixed. */
+#endif /* TC_I960 */
+#ifdef TC_A29K
+ /* Count 2 for a constH */
+ if (fixP->fx_r_type == RELOC_CONSTH) {
+ ++text_relocation_number;
+ }
+#endif
+
+ } /* if not yet fixed */
+ } /* for each fix */
+
+ SA_SET_SCN_NRELOC(dot_text_symbol, text_relocation_number);
+ /* Assign the number of line number entries for the text section */
+ SA_SET_SCN_NLINNO(dot_text_symbol, text_lineno_number);
+ /* Assign the size of the section */
+ SA_SET_SCN_SCNLEN(dot_text_symbol, H_GET_TEXT_SIZE(headers));
+
+ for (fixP = data_fix_root; fixP; fixP = fixP->fx_next) {
+ if (fixP->fx_addsy) {
+ ++data_relocation_number;
+ } /* if still relocatable */
+#ifdef TC_A29K
+ /* Count 2 for a constH */
+ if (fixP->fx_r_type == RELOC_CONSTH) {
+ ++data_relocation_number;
+ }
+#endif
+
+ } /* for each fix */
+
+
+ SA_SET_SCN_NRELOC(dot_data_symbol, data_relocation_number);
+ /* Assign the size of the section */
+ SA_SET_SCN_SCNLEN(dot_data_symbol, H_GET_DATA_SIZE(headers));
+
+ /* Assign the size of the section */
+ SA_SET_SCN_SCNLEN(dot_bss_symbol, H_GET_BSS_SIZE(headers));
+
+ /* pre write hook can add relocs (for 960 and 29k coff) so */
+ headers->relocation_size = text_relocation_number * RELSZ +
+ data_relocation_number *RELSZ;
+
+
+
+ /* Fill in extra coff fields */
+
+ /* Initialize general line number information. */
+ H_SET_LINENO_SIZE(headers, text_lineno_number * LINESZ);
+
+ /* filehdr */
+ H_SET_FILE_MAGIC_NUMBER(headers, FILE_HEADER_MAGIC);
+ H_SET_NUMBER_OF_SECTIONS(headers, 3); /* text+data+bss */
+#ifndef OBJ_COFF_OMIT_TIMESTAMP
+ H_SET_TIME_STAMP(headers, (long)time((long*)0));
+#else /* OBJ_COFF_OMIT_TIMESTAMP */
+ H_SET_TIME_STAMP(headers, 0);
+#endif /* OBJ_COFF_OMIT_TIMESTAMP */
+ H_SET_SYMBOL_TABLE_POINTER(headers, H_GET_SYMBOL_TABLE_FILE_OFFSET(headers));
+#if 0
+ printf("FILHSZ %x\n", FILHSZ);
+ printf("OBJ_COFF_AOUTHDRSZ %x\n", OBJ_COFF_AOUTHDRSZ);
+ printf("section headers %x\n", H_GET_NUMBER_OF_SECTIONS(headers) * SCNHSZ);
+ printf("get text size %x\n", H_GET_TEXT_SIZE(headers));
+ printf("get data size %x\n", H_GET_DATA_SIZE(headers));
+ printf("get relocation size %x\n", H_GET_RELOCATION_SIZE(headers));
+ printf("get lineno size %x\n", H_GET_LINENO_SIZE(headers));
+#endif
+ /* symbol table size allready set */
+ H_SET_SIZEOF_OPTIONAL_HEADER(headers, OBJ_COFF_AOUTHDRSZ);
+
+ /* do not added the F_RELFLG for the standard COFF.
+ * The AIX linker complain on file with relocation info striped flag.
+ */
+#ifdef KEEP_RELOC_INFO
+ H_SET_FLAGS(headers, (text_lineno_number == 0 ? F_LNNO : 0)
+ | BYTE_ORDERING);
+#else
+ H_SET_FLAGS(headers, (text_lineno_number == 0 ? F_LNNO : 0)
+ | ((text_relocation_number + data_relocation_number) ? 0 : F_RELFLG)
+ | BYTE_ORDERING);
+#endif
+ /* aouthdr */
+ /* magic number allready set */
+ H_SET_VERSION_STAMP(headers, 0);
+ /* Text, data, bss size; entry point; text_start and data_start are already set */
+
+ /* Build section headers */
+
+ c_section_header(&text_section_header,
+ ".text",
+ 0,
+ H_GET_TEXT_SIZE(headers),
+ H_GET_TEXT_FILE_OFFSET(headers),
+ (SA_GET_SCN_NRELOC(dot_text_symbol)
+ ? H_GET_RELOCATION_FILE_OFFSET(headers)
+ : 0),
+ (text_lineno_number
+ ? H_GET_LINENO_FILE_OFFSET(headers)
+ : 0),
+ SA_GET_SCN_NRELOC(dot_text_symbol),
+ text_lineno_number,
+ section_alignment[(int) SEG_TEXT]);
+
+ c_section_header(&data_section_header,
+ ".data",
+ H_GET_TEXT_SIZE(headers),
+ H_GET_DATA_SIZE(headers),
+ (H_GET_DATA_SIZE(headers)
+ ? H_GET_DATA_FILE_OFFSET(headers)
+ : 0),
+ (SA_GET_SCN_NRELOC(dot_data_symbol)
+ ? (H_GET_RELOCATION_FILE_OFFSET(headers)
+ + text_section_header.s_nreloc * RELSZ)
+ : 0),
+ 0, /* No line number information */
+ SA_GET_SCN_NRELOC(dot_data_symbol),
+ 0, /* No line number information */
+ section_alignment[(int) SEG_DATA]);
+
+ c_section_header(&bss_section_header,
+ ".bss",
+ H_GET_TEXT_SIZE(headers) + H_GET_DATA_SIZE(headers),
+ H_GET_BSS_SIZE(headers),
+ 0, /* No file offset */
+ 0, /* No relocation information */
+ 0, /* No line number information */
+ 0, /* No relocation information */
+ 0, /* No line number information */
+ section_alignment[(int) SEG_BSS]);
+
+ return;
+} /* obj_pre_write_hook() */
+
+/* This is a copy from aout. All I do is neglect to actually build the symbol. */
+
+static void obj_coff_stab(what)
+int what;
+{
+ char *string;
+ expressionS e;
+ int goof = 0; /* TRUE if we have aborted. */
+ int length;
+ int saved_type = 0;
+ long longint;
+ symbolS *symbolP = 0;
+
+ if (what == 's') {
+ string = demand_copy_C_string(&length);
+ SKIP_WHITESPACE();
+
+ if (*input_line_pointer == ',') {
+ input_line_pointer++;
+ } else {
+ as_bad("I need a comma after symbol's name");
+ goof = 1;
+ } /* better be a comma */
+ } /* skip the string */
+
+ /*
+ * Input_line_pointer->after ','. String->symbol name.
+ */
+ if (!goof) {
+ if (get_absolute_expression_and_terminator(&longint) != ',') {
+ as_bad("I want a comma after the n_type expression");
+ goof = 1;
+ input_line_pointer--; /* Backup over a non-',' char. */
+ } /* on error */
+ } /* no error */
+
+ if (!goof) {
+ if (get_absolute_expression_and_terminator(&longint) != ',') {
+ as_bad("I want a comma after the n_other expression");
+ goof = 1;
+ input_line_pointer--; /* Backup over a non-',' char. */
+ } /* on error */
+ } /* no error */
+
+ if (!goof) {
+ get_absolute_expression();
+
+ if (what == 's' || what == 'n') {
+ if (*input_line_pointer != ',') {
+ as_bad("I want a comma after the n_desc expression");
+ goof = 1;
+ } else {
+ input_line_pointer++;
+ } /* on goof */
+ } /* not stabd */
+ } /* no error */
+
+ expression(&e);
+
+ if (goof) {
+ ignore_rest_of_line();
+ } else {
+ demand_empty_rest_of_line();
+ } /* on error */
+} /* obj_coff_stab() */
+
+#ifdef DEBUG
+/* for debugging */
+char *s_get_name(s)
+symbolS *s;
+{
+ return((s == NULL) ? "(NULL)" : S_GET_NAME(s));
+} /* s_get_name() */
+
+void symbol_dump() {
+ symbolS *symbolP;
+
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) {
+ printf("%3ld: 0x%lx \"%s\" type = %ld, class = %d, segment = %d\n",
+ symbolP->sy_number,
+ (unsigned long) symbolP,
+ S_GET_NAME(symbolP),
+ (long) S_GET_DATA_TYPE(symbolP),
+ S_GET_STORAGE_CLASS(symbolP),
+ (int) S_GET_SEGMENT(symbolP));
+ } /* traverse symbols */
+
+ return;
+} /* symbol_dump() */
+#endif /* DEBUG */
+
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of obj-coff.c */
diff --git a/gnu/usr.bin/as/config/obj-coff.h b/gnu/usr.bin/as/config/obj-coff.h
new file mode 100644
index 0000000..9720d5e
--- /dev/null
+++ b/gnu/usr.bin/as/config/obj-coff.h
@@ -0,0 +1,598 @@
+/* coff object file format
+ Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define OBJ_COFF 1
+
+#include "targ-cpu.h"
+
+
+
+#ifdef BFD_HEADERS
+#ifdef TC_A29K
+#include "bfd.h"
+#include "coff/a29k.h"
+
+/* This internal_lineno crap is to stop namespace pollution from the bfd internal
+ coff headerfile. */
+
+#define internal_lineno bfd_internal_lineno
+#include "coff/internal.h"
+#undef internal_lineno
+/*
+ #undef RELOC
+ #undef SYMENT
+ #undef AUXENT
+ #undef LINENO
+ #undef FILHDR
+ #undef SCNHDR
+ #define RELOC struct internal_reloc
+ #define SYMENT struct internal_syment
+ #define AUXENT union internal_auxent
+ #define SCNHDR struct internal_scnhdr
+ #define LINENO struct bfd_internal_lineno
+ #define AOUTHDR struct internal_aouthdr
+ #define FILHDR struct internal_filehdr
+ #define AOUTHDRSZ sizeof(struct external_aouthdr)
+ */
+/*#define x_endndx x_endndx.l
+ #define x_tagndx x_tagndx.l*/
+#define TARGET_FORMAT "coff-a29k-big"
+extern bfd *stdoutput;
+
+#else /* not TC_A29K */
+# ifdef TC_I386
+# include "bfd.h"
+# include "coff/i386.h"
+# define internal_lineno bfd_internal_lineno
+# include "coff/internal.h"
+# undef internal_lineno
+# define TARGET_FORMAT "coff-i386"
+extern bfd *stdoutput;
+
+#else /* not TC_I386 */
+#error help me
+#endif /* not TC_I386 */
+
+#endif /* not TC_A29K */
+
+#else /* not BFD_HEADERS */
+
+#ifdef USE_NATIVE_HEADERS
+#include <filehdr.h>
+#include <aouthdr.h>
+#include <scnhdr.h>
+#include <storclass.h>
+#include <linenum.h>
+#include <syms.h>
+#include <reloc.h>
+#include <sys/types.h>
+#else /* not USE_NATIVE_HEADERS */
+#include "coff.h"
+#endif /* not USE_NATIVE_HEADERS */
+
+#endif /* not BFD_HEADERS */
+
+/* Define some processor dependent values according to the processor we are on. */
+#ifdef TC_M68K
+
+#define BYTE_ORDERING F_AR32W /* See filehdr.h for more info. */
+#ifndef FILE_HEADER_MAGIC
+#define FILE_HEADER_MAGIC MC68MAGIC /* ... */
+#endif /* FILE_HEADER_MAGIC */
+
+#elif defined(TC_I386)
+
+#define BYTE_ORDERING F_AR32WR /* See filehdr.h for more info. */
+#ifndef FILE_HEADER_MAGIC
+#define FILE_HEADER_MAGIC I386MAGIC /* ... */
+#endif /* FILE_HEADER_MAGIC */
+
+#elif defined(TC_I960)
+
+#define BYTE_ORDERING F_AR32WR /* See filehdr.h for more info. */
+#ifndef FILE_HEADER_MAGIC
+#define FILE_HEADER_MAGIC I960ROMAGIC /* ... */
+#endif /* FILE_HEADER_MAGIC */
+
+#elif defined(TC_A29K)
+
+#define BYTE_ORDERING F_AR32W /* big endian. */
+#ifndef FILE_HEADER_MAGIC
+#define FILE_HEADER_MAGIC SIPFBOMAGIC
+#endif /* FILE_HEADER_MAGIC */
+
+#else
+you lose
+#endif
+
+#ifndef OBJ_COFF_MAX_AUXENTRIES
+#define OBJ_COFF_MAX_AUXENTRIES 1
+#endif /* OBJ_COFF_MAX_AUXENTRIES */
+
+ extern const short seg_N_TYPE[];
+extern const segT N_TYPE_seg[];
+
+#ifndef BFD_HEADERS
+
+/* Add these definitions to have a consistent convention for all the
+ types used in COFF format. */
+#define AOUTHDR struct aouthdr
+#define AOUTHDRSZ sizeof(AOUTHDR)
+#endif
+
+/* SYMBOL TABLE */
+
+/* targets may also set this */
+#ifndef SYMBOLS_NEED_BACKPOINTERS
+#define SYMBOLS_NEED_BACKPOINTERS 1
+#endif /* SYMBOLS_NEED_BACKPOINTERS */
+
+/* Symbol table entry data type */
+
+typedef struct {
+#ifdef BFD_HEADERS
+ struct internal_syment ost_entry; /* Basic symbol */
+ union internal_auxent ost_auxent[OBJ_COFF_MAX_AUXENTRIES]; /* Auxiliary entry. */
+#else
+ SYMENT ost_entry; /* Basic symbol */
+ AUXENT ost_auxent[OBJ_COFF_MAX_AUXENTRIES]; /* Auxiliary entry. */
+#endif
+ unsigned int ost_flags; /* obj_coff internal use only flags */
+} obj_symbol_type;
+
+#define DO_NOT_STRIP 0
+#define DO_STRIP 1
+
+/* Symbol table macros and constants */
+
+/* Possible and usefull section number in symbol table
+ * The values of TEXT, DATA and BSS may not be portable.
+ */
+
+#define C_TEXT_SECTION ((short)1)
+#define C_DATA_SECTION ((short)2)
+#define C_BSS_SECTION ((short)3)
+#define C_ABS_SECTION N_ABS
+#define C_UNDEF_SECTION N_UNDEF
+#define C_DEBUG_SECTION N_DEBUG
+#define C_NTV_SECTION N_TV
+#define C_PTV_SECTION P_TV
+#define C_REGISTER_SECTION 4
+
+/*
+ * Macros to extract information from a symbol table entry.
+ * This syntaxic indirection allows independence regarding a.out or coff.
+ * The argument (s) of all these macros is a pointer to a symbol table entry.
+ */
+
+/* Predicates */
+/* True if the symbol is external */
+#define S_IS_EXTERNAL(s) ((s)->sy_symbol.ost_entry.n_scnum == C_UNDEF_SECTION)
+/* True if symbol has been defined, ie :
+ section > 0 (DATA, TEXT or BSS)
+ section == 0 and value > 0 (external bss symbol) */
+#define S_IS_DEFINED(s) ((s)->sy_symbol.ost_entry.n_scnum > C_UNDEF_SECTION || \
+ ((s)->sy_symbol.ost_entry.n_scnum == C_UNDEF_SECTION && \
+ (s)->sy_symbol.ost_entry.n_value > 0))
+/* True if a debug special symbol entry */
+#define S_IS_DEBUG(s) ((s)->sy_symbol.ost_entry.n_scnum == C_DEBUG_SECTION)
+/* True if a symbol is local symbol name */
+/* A symbol name whose name begin with ^A is a gas internal pseudo symbol */
+#define S_IS_LOCAL(s) (S_GET_NAME(s)[0] == '\001' || \
+ (s)->sy_symbol.ost_entry.n_scnum == C_REGISTER_SECTION || \
+ (S_LOCAL_NAME(s) && !flagseen['L']))
+/* True if a symbol is not defined in this file */
+#define S_IS_EXTERN(s) ((s)->sy_symbol.ost_entry.n_scnum == 0 && (s)->sy_symbol.ost_entry.n_value == 0)
+/*
+ * True if a symbol can be multiply defined (bss symbols have this def
+ * though it is bad practice)
+ */
+#define S_IS_COMMON(s) ((s)->sy_symbol.ost_entry.n_scnum == 0 && (s)->sy_symbol.ost_entry.n_value != 0)
+/* True if a symbol name is in the string table, i.e. its length is > 8. */
+#define S_IS_STRING(s) (strlen(S_GET_NAME(s)) > 8 ? 1 : 0)
+
+/* Accessors */
+/* The name of the symbol */
+#define S_GET_NAME(s) ((char*)(s)->sy_symbol.ost_entry.n_offset)
+/* The pointer to the string table */
+#define S_GET_OFFSET(s) ((s)->sy_symbol.ost_entry.n_offset)
+/* The zeroes if symbol name is longer than 8 chars */
+#define S_GET_ZEROES(s) ((s)->sy_symbol.ost_entry.n_zeroes)
+/* The value of the symbol */
+#define S_GET_VALUE(s) ((unsigned) ((s)->sy_symbol.ost_entry.n_value))
+/* The numeric value of the segment */
+#define S_GET_SEGMENT(s) (N_TYPE_seg[(s)->sy_symbol.ost_entry.n_scnum+4])
+/* The data type */
+#define S_GET_DATA_TYPE(s) ((s)->sy_symbol.ost_entry.n_type)
+/* The storage class */
+#define S_GET_STORAGE_CLASS(s) ((s)->sy_symbol.ost_entry.n_sclass)
+/* The number of auxiliary entries */
+#define S_GET_NUMBER_AUXILIARY(s) ((s)->sy_symbol.ost_entry.n_numaux)
+
+/* Modifiers */
+/* Set the name of the symbol */
+#define S_SET_NAME(s,v) ((s)->sy_symbol.ost_entry.n_offset = (unsigned long)(v))
+/* Set the offset of the symbol */
+#define S_SET_OFFSET(s,v) ((s)->sy_symbol.ost_entry.n_offset = (v))
+/* The zeroes if symbol name is longer than 8 chars */
+#define S_SET_ZEROES(s,v) ((s)->sy_symbol.ost_entry.n_zeroes = (v))
+/* Set the value of the symbol */
+#define S_SET_VALUE(s,v) ((s)->sy_symbol.ost_entry.n_value = (v))
+/* The numeric value of the segment */
+#define S_SET_SEGMENT(s,v) ((s)->sy_symbol.ost_entry.n_scnum = SEGMENT_TO_SYMBOL_TYPE(v))
+/* The data type */
+#define S_SET_DATA_TYPE(s,v) ((s)->sy_symbol.ost_entry.n_type = (v))
+/* The storage class */
+#define S_SET_STORAGE_CLASS(s,v) ((s)->sy_symbol.ost_entry.n_sclass = (v))
+/* The number of auxiliary entries */
+#define S_SET_NUMBER_AUXILIARY(s,v) ((s)->sy_symbol.ost_entry.n_numaux = (v))
+
+/* Additional modifiers */
+/* The symbol is external (does not mean undefined) */
+#define S_SET_EXTERNAL(s) { S_SET_STORAGE_CLASS(s, C_EXT) ; SF_CLEAR_LOCAL(s); }
+
+/* Auxiliary entry macros. SA_ stands for symbol auxiliary */
+/* Omit the tv related fields */
+/* Accessors */
+#ifdef BFD_HEADERS
+#define SA_GET_SYM_TAGNDX(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_tagndx.l)
+#else
+#define SA_GET_SYM_TAGNDX(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_tagndx)
+#endif
+#define SA_GET_SYM_LNNO(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_misc.x_lnsz.x_lnno)
+#define SA_GET_SYM_SIZE(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_misc.x_lnsz.x_size)
+#define SA_GET_SYM_FSIZE(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_misc.x_fsize)
+#define SA_GET_SYM_LNNOPTR(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_fcn.x_lnnoptr)
+#ifdef BFD_HEADERS
+#define SA_GET_SYM_ENDNDX(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_fcn.x_endndx.l)
+#else
+#define SA_GET_SYM_ENDNDX(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_fcn.x_endndx)
+#endif
+#define SA_GET_SYM_DIMEN(s,i) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_ary.x_dimen[(i)])
+#define SA_GET_FILE_FNAME(s) ((s)->sy_symbol.ost_auxent[0].x_file.x_fname)
+#define SA_GET_SCN_SCNLEN(s) ((s)->sy_symbol.ost_auxent[0].x_scn.x_scnlen)
+#define SA_GET_SCN_NRELOC(s) ((s)->sy_symbol.ost_auxent[0].x_scn.x_nreloc)
+#define SA_GET_SCN_NLINNO(s) ((s)->sy_symbol.ost_auxent[0].x_scn.x_nlinno)
+
+/* Modifiers */
+#ifdef BFD_HEADERS
+#define SA_SET_SYM_TAGNDX(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_tagndx.l=(v))
+#else
+#define SA_SET_SYM_TAGNDX(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_tagndx=(v))
+#endif
+#define SA_SET_SYM_LNNO(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_misc.x_lnsz.x_lnno=(v))
+#define SA_SET_SYM_SIZE(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_misc.x_lnsz.x_size=(v))
+#define SA_SET_SYM_FSIZE(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_misc.x_fsize=(v))
+#define SA_SET_SYM_LNNOPTR(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_fcn.x_lnnoptr=(v))
+#ifdef BFD_HEADERS
+#define SA_SET_SYM_ENDNDX(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_fcn.x_endndx.l=(v))
+#else
+#define SA_SET_SYM_ENDNDX(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_fcn.x_endndx=(v))
+#endif
+#define SA_SET_SYM_DIMEN(s,i,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_ary.x_dimen[(i)]=(v))
+#define SA_SET_FILE_FNAME(s,v) strncpy((s)->sy_symbol.ost_auxent[0].x_file.x_fname,(v),FILNMLEN)
+#define SA_SET_SCN_SCNLEN(s,v) ((s)->sy_symbol.ost_auxent[0].x_scn.x_scnlen=(v))
+#define SA_SET_SCN_NRELOC(s,v) ((s)->sy_symbol.ost_auxent[0].x_scn.x_nreloc=(v))
+#define SA_SET_SCN_NLINNO(s,v) ((s)->sy_symbol.ost_auxent[0].x_scn.x_nlinno=(v))
+
+/*
+ * Internal use only definitions. SF_ stands for symbol flags.
+ *
+ * These values can be assigned to sy_symbol.ost_flags field of a symbolS.
+ *
+ * You'll break i960 if you shift the SYSPROC bits anywhere else. for
+ * more on the balname/callname hack, see tc-i960.h. b.out is done
+ * differently.
+ */
+
+#define SF_I960_MASK (0x000001ff) /* Bits 0-8 are used by the i960 port. */
+#define SF_SYSPROC (0x0000003f) /* bits 0-5 are used to store the sysproc number */
+#define SF_IS_SYSPROC (0x00000040) /* bit 6 marks symbols that are sysprocs */
+#define SF_BALNAME (0x00000080) /* bit 7 marks BALNAME symbols */
+#define SF_CALLNAME (0x00000100) /* bit 8 marks CALLNAME symbols */
+
+#define SF_NORMAL_MASK (0x0000ffff) /* bits 12-15 are general purpose. */
+
+#define SF_STATICS (0x00001000) /* Mark the .text & all symbols */
+#define SF_DEFINED (0x00002000) /* Symbol is defined in this file */
+#define SF_STRING (0x00004000) /* Symbol name length > 8 */
+#define SF_LOCAL (0x00008000) /* Symbol must not be emitted */
+
+#define SF_DEBUG_MASK (0xffff0000) /* bits 16-31 are debug info */
+
+#define SF_FUNCTION (0x00010000) /* The symbol is a function */
+#define SF_PROCESS (0x00020000) /* Process symbol before write */
+#define SF_TAGGED (0x00040000) /* Is associated with a tag */
+#define SF_TAG (0x00080000) /* Is a tag */
+#define SF_DEBUG (0x00100000) /* Is in debug or abs section */
+#define SF_GET_SEGMENT (0x00200000) /* Get the section of the forward symbol. */
+/* All other bits are unused. */
+
+/* Accessors */
+#define SF_GET(s) ((s)->sy_symbol.ost_flags)
+#define SF_GET_NORMAL_FIELD(s) ((s)->sy_symbol.ost_flags & SF_NORMAL_MASK)
+#define SF_GET_DEBUG_FIELD(s) ((s)->sy_symbol.ost_flags & SF_DEBUG_MASK)
+#define SF_GET_FILE(s) ((s)->sy_symbol.ost_flags & SF_FILE)
+#define SF_GET_STATICS(s) ((s)->sy_symbol.ost_flags & SF_STATICS)
+#define SF_GET_DEFINED(s) ((s)->sy_symbol.ost_flags & SF_DEFINED)
+#define SF_GET_STRING(s) ((s)->sy_symbol.ost_flags & SF_STRING)
+#define SF_GET_LOCAL(s) ((s)->sy_symbol.ost_flags & SF_LOCAL)
+#define SF_GET_FUNCTION(s) ((s)->sy_symbol.ost_flags & SF_FUNCTION)
+#define SF_GET_PROCESS(s) ((s)->sy_symbol.ost_flags & SF_PROCESS)
+#define SF_GET_DEBUG(s) ((s)->sy_symbol.ost_flags & SF_DEBUG)
+#define SF_GET_TAGGED(s) ((s)->sy_symbol.ost_flags & SF_TAGGED)
+#define SF_GET_TAG(s) ((s)->sy_symbol.ost_flags & SF_TAG)
+#define SF_GET_GET_SEGMENT(s) ((s)->sy_symbol.ost_flags & SF_GET_SEGMENT)
+#define SF_GET_I960(s) ((s)->sy_symbol.ost_flags & SF_I960_MASK) /* used by i960 */
+#define SF_GET_BALNAME(s) ((s)->sy_symbol.ost_flags & SF_BALNAME) /* used by i960 */
+#define SF_GET_CALLNAME(s) ((s)->sy_symbol.ost_flags & SF_CALLNAME) /* used by i960 */
+#define SF_GET_IS_SYSPROC(s) ((s)->sy_symbol.ost_flags & SF_IS_SYSPROC) /* used by i960 */
+#define SF_GET_SYSPROC(s) ((s)->sy_symbol.ost_flags & SF_SYSPROC) /* used by i960 */
+
+/* Modifiers */
+#define SF_SET(s,v) ((s)->sy_symbol.ost_flags = (v))
+#define SF_SET_NORMAL_FIELD(s,v)((s)->sy_symbol.ost_flags |= ((v) & SF_NORMAL_MASK))
+#define SF_SET_DEBUG_FIELD(s,v) ((s)->sy_symbol.ost_flags |= ((v) & SF_DEBUG_MASK))
+#define SF_SET_FILE(s) ((s)->sy_symbol.ost_flags |= SF_FILE)
+#define SF_SET_STATICS(s) ((s)->sy_symbol.ost_flags |= SF_STATICS)
+#define SF_SET_DEFINED(s) ((s)->sy_symbol.ost_flags |= SF_DEFINED)
+#define SF_SET_STRING(s) ((s)->sy_symbol.ost_flags |= SF_STRING)
+#define SF_SET_LOCAL(s) ((s)->sy_symbol.ost_flags |= SF_LOCAL)
+#define SF_CLEAR_LOCAL(s) ((s)->sy_symbol.ost_flags &= ~SF_LOCAL)
+#define SF_SET_FUNCTION(s) ((s)->sy_symbol.ost_flags |= SF_FUNCTION)
+#define SF_SET_PROCESS(s) ((s)->sy_symbol.ost_flags |= SF_PROCESS)
+#define SF_SET_DEBUG(s) ((s)->sy_symbol.ost_flags |= SF_DEBUG)
+#define SF_SET_TAGGED(s) ((s)->sy_symbol.ost_flags |= SF_TAGGED)
+#define SF_SET_TAG(s) ((s)->sy_symbol.ost_flags |= SF_TAG)
+#define SF_SET_GET_SEGMENT(s) ((s)->sy_symbol.ost_flags |= SF_GET_SEGMENT)
+#define SF_SET_I960(s,v) ((s)->sy_symbol.ost_flags |= ((v) & SF_I960_MASK)) /* used by i960 */
+#define SF_SET_BALNAME(s) ((s)->sy_symbol.ost_flags |= SF_BALNAME) /* used by i960 */
+#define SF_SET_CALLNAME(s) ((s)->sy_symbol.ost_flags |= SF_CALLNAME) /* used by i960 */
+#define SF_SET_IS_SYSPROC(s) ((s)->sy_symbol.ost_flags |= SF_IS_SYSPROC) /* used by i960 */
+#define SF_SET_SYSPROC(s,v) ((s)->sy_symbol.ost_flags |= ((v) & SF_SYSPROC)) /* used by i960 */
+
+/* File header macro and type definition */
+
+/*
+ * File position calculators. Beware to use them when all the
+ * appropriate fields are set in the header.
+ */
+
+#ifdef OBJ_COFF_OMIT_OPTIONAL_HEADER
+#define OBJ_COFF_AOUTHDRSZ (0)
+#else
+#define OBJ_COFF_AOUTHDRSZ (AOUTHDRSZ)
+#endif /* OBJ_COFF_OMIT_OPTIONAL_HEADER */
+
+#define H_GET_FILE_SIZE(h) \
+ (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \
+ H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \
+ H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h) + \
+ H_GET_RELOCATION_SIZE(h) + H_GET_LINENO_SIZE(h) + \
+ H_GET_SYMBOL_TABLE_SIZE(h) + \
+ (h)->string_table_size)
+#define H_GET_TEXT_FILE_OFFSET(h) \
+ (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \
+ H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ)
+#define H_GET_DATA_FILE_OFFSET(h) \
+ (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \
+ H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \
+ H_GET_TEXT_SIZE(h))
+#define H_GET_BSS_FILE_OFFSET(h) 0
+#define H_GET_RELOCATION_FILE_OFFSET(h) \
+ (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \
+ H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \
+ H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h))
+#define H_GET_LINENO_FILE_OFFSET(h) \
+ (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \
+ H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \
+ H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h) + \
+ H_GET_RELOCATION_SIZE(h))
+#define H_GET_SYMBOL_TABLE_FILE_OFFSET(h) \
+ (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \
+ H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \
+ H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h) + \
+ H_GET_RELOCATION_SIZE(h) + H_GET_LINENO_SIZE(h))
+
+/* Accessors */
+/* aouthdr */
+#define H_GET_MAGIC_NUMBER(h) ((h)->aouthdr.magic)
+#define H_GET_VERSION_STAMP(h) ((h)->aouthdr.vstamp)
+#define H_GET_TEXT_SIZE(h) ((h)->aouthdr.tsize)
+#define H_GET_DATA_SIZE(h) ((h)->aouthdr.dsize)
+#define H_GET_BSS_SIZE(h) ((h)->aouthdr.bsize)
+#define H_GET_ENTRY_POINT(h) ((h)->aouthdr.entry)
+#define H_GET_TEXT_START(h) ((h)->aouthdr.text_start)
+#define H_GET_DATA_START(h) ((h)->aouthdr.data_start)
+/* filehdr */
+#define H_GET_FILE_MAGIC_NUMBER(h) ((h)->filehdr.f_magic)
+#define H_GET_NUMBER_OF_SECTIONS(h) ((h)->filehdr.f_nscns)
+#define H_GET_TIME_STAMP(h) ((h)->filehdr.f_timdat)
+#define H_GET_SYMBOL_TABLE_POINTER(h) ((h)->filehdr.f_symptr)
+#define H_GET_SYMBOL_COUNT(h) ((h)->filehdr.f_nsyms)
+#define H_GET_SYMBOL_TABLE_SIZE(h) (H_GET_SYMBOL_COUNT(h) * SYMESZ)
+#define H_GET_SIZEOF_OPTIONAL_HEADER(h) ((h)->filehdr.f_opthdr)
+#define H_GET_FLAGS(h) ((h)->filehdr.f_flags)
+/* Extra fields to achieve bsd a.out compatibility and for convenience */
+#define H_GET_RELOCATION_SIZE(h) ((h)->relocation_size)
+#define H_GET_STRING_SIZE(h) ((h)->string_table_size)
+#define H_GET_LINENO_SIZE(h) ((h)->lineno_size)
+
+#ifndef OBJ_COFF_OMIT_OPTIONAL_HEADER
+#define H_GET_HEADER_SIZE(h) (sizeof(FILHDR) \
+ + sizeof(AOUTHDR)\
+ + (H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ))
+#else /* OBJ_COFF_OMIT_OPTIONAL_HEADER */
+#define H_GET_HEADER_SIZE(h) (sizeof(FILHDR) \
+ + (H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ))
+#endif /* OBJ_COFF_OMIT_OPTIONAL_HEADER */
+
+#define H_GET_TEXT_RELOCATION_SIZE(h) (text_section_header.s_nreloc * RELSZ)
+#define H_GET_DATA_RELOCATION_SIZE(h) (data_section_header.s_nreloc * RELSZ)
+
+/* Modifiers */
+/* aouthdr */
+#define H_SET_MAGIC_NUMBER(h,v) ((h)->aouthdr.magic = (v))
+#define H_SET_VERSION_STAMP(h,v) ((h)->aouthdr.vstamp = (v))
+#define H_SET_TEXT_SIZE(h,v) ((h)->aouthdr.tsize = (v))
+#define H_SET_DATA_SIZE(h,v) ((h)->aouthdr.dsize = (v))
+#define H_SET_BSS_SIZE(h,v) ((h)->aouthdr.bsize = (v))
+#define H_SET_ENTRY_POINT(h,v) ((h)->aouthdr.entry = (v))
+#define H_SET_TEXT_START(h,v) ((h)->aouthdr.text_start = (v))
+#define H_SET_DATA_START(h,v) ((h)->aouthdr.data_start = (v))
+/* filehdr */
+#define H_SET_FILE_MAGIC_NUMBER(h,v) ((h)->filehdr.f_magic = (v))
+#define H_SET_NUMBER_OF_SECTIONS(h,v) ((h)->filehdr.f_nscns = (v))
+#define H_SET_TIME_STAMP(h,v) ((h)->filehdr.f_timdat = (v))
+#define H_SET_SYMBOL_TABLE_POINTER(h,v) ((h)->filehdr.f_symptr = (v))
+#define H_SET_SYMBOL_TABLE_SIZE(h,v) ((h)->filehdr.f_nsyms = (v))
+#define H_SET_SIZEOF_OPTIONAL_HEADER(h,v) ((h)->filehdr.f_opthdr = (v))
+#define H_SET_FLAGS(h,v) ((h)->filehdr.f_flags = (v))
+/* Extra fields to achieve bsd a.out compatibility and for convinience */
+#define H_SET_RELOCATION_SIZE(h,t,d) ((h)->relocation_size = (t)+(d))
+#define H_SET_STRING_SIZE(h,v) ((h)->string_table_size = (v))
+#define H_SET_LINENO_SIZE(h,v) ((h)->lineno_size = (v))
+
+/* Segment flipping */
+#define segment_name(v) (seg_name[(int) (v)])
+
+typedef struct {
+#ifdef BFD_HEADERS
+ struct internal_aouthdr aouthdr; /* a.out header */
+ struct internal_filehdr filehdr; /* File header, not machine dep. */
+#else
+ AOUTHDR aouthdr; /* a.out header */
+ FILHDR filehdr; /* File header, not machine dep. */
+#endif
+ long string_table_size; /* names + '\0' + sizeof(int) */
+ long relocation_size; /* Cumulated size of relocation
+ information for all sections in
+ bytes. */
+ long lineno_size; /* Size of the line number information
+ table in bytes */
+} object_headers;
+
+/* -------------- Line number handling ------- */
+extern int text_lineno_number;
+
+/* line numbering stuff. */
+
+typedef struct internal_lineno {
+#ifdef BFD_HEADERS
+ struct bfd_internal_lineno line;
+#else
+ LINENO line; /* The lineno structure itself */
+#endif
+ char* frag; /* Frag to which the line number is related */
+ struct internal_lineno* next; /* Forward chain pointer */
+} lineno;
+
+extern lineno *lineno_lastP;
+extern lineno *lineno_rootP;
+#define OBJ_EMIT_LINENO(a, b, c) obj_emit_lineno((a),(b),(c))
+
+#if __STDC__ == 1
+void obj_emit_lineno(char **where, lineno *line, char *file_start);
+#else /* not __STDC__ */
+void obj_emit_lineno();
+#endif /* not __STDC__ */
+
+/* stack stuff */
+typedef struct {
+ unsigned long chunk_size;
+ unsigned long element_size;
+ unsigned long size;
+ char* data;
+ unsigned long pointer;
+} stack;
+
+#if __STDC__ == 1
+
+char *stack_pop(stack *st);
+char *stack_push(stack *st, char *element);
+char *stack_top(stack *st);
+stack *stack_init(unsigned long chunk_size, unsigned long element_size);
+void c_dot_file_symbol(char *filename);
+void obj_extra_stuff(object_headers *headers);
+void stack_delete(stack *st);
+
+#ifndef tc_headers_hook
+void tc_headers_hook(object_headers *headers);
+#endif /* tc_headers_hook */
+
+#ifndef tc_coff_symbol_emit_hook
+void tc_coff_symbol_emit_hook(); /* really tc_coff_symbol_emit_hook(symbolS *symbolP) */
+#endif /* tc_coff_symbol_emit_hook */
+
+void c_section_header(
+#ifdef BFD_HEADERS
+ struct internal_scnhdr *header,
+#else
+ SCNHDR *header,
+#endif
+
+ char *name,
+ long core_address,
+ long size,
+ long data_ptr,
+ long reloc_ptr,
+ long lineno_ptr,
+ long reloc_number,
+ long lineno_number,
+ long alignment);
+
+#else /* not __STDC__ */
+
+char *stack_pop();
+char *stack_push();
+char *stack_top();
+stack *stack_init();
+void c_dot_file_symbol();
+void c_section_header();
+void obj_extra_stuff();
+void stack_delete();
+void tc_headers_hook();
+void tc_coff_symbol_emit_hook();
+
+#endif /* not __STDC__ */
+
+
+/* sanity check */
+
+#ifdef TC_I960
+#ifndef C_LEAFSTAT
+hey! Where is the C_LEAFSTAT definition? i960-coff support is depending on it.
+#endif /* no C_LEAFSTAT */
+#endif /* TC_I960 */
+#ifdef BFD_HEADERS
+ extern struct internal_scnhdr data_section_header;
+extern struct internal_scnhdr text_section_header;
+#else
+extern SCNHDR data_section_header;
+extern SCNHDR text_section_header;
+#endif
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of obj-coff.h */
diff --git a/gnu/usr.bin/as/config/obj-coffbfd.c b/gnu/usr.bin/as/config/obj-coffbfd.c
new file mode 100644
index 0000000..d69c7a0
--- /dev/null
+++ b/gnu/usr.bin/as/config/obj-coffbfd.c
@@ -0,0 +1,2182 @@
+/* coff object file format with bfd
+ Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+
+ How does this releate to the rest of GAS ?
+
+ Well, all the other files in gas are more or less a black box. It
+ takes care of opening files, parsing command lines, stripping blanks
+ etc etc. This module gets a chance to register what it wants to do by
+ saying that it is interested in various pseduo ops. The other big
+ change is write_object_file. This runs through all the data
+ structures that gas builds, and outputs the file in the format of our
+ choice.
+
+ Hacked for BFDness by steve chamberlain
+
+ This object module now supports the Hitachi H8/300 and the AMD 29k
+
+ sac@cygnus.com
+ */
+
+#include "as.h"
+#include "obstack.h"
+#include "subsegs.h"
+#include "frags.h"
+#include "../bfd/libbfd.h"
+
+
+/* This vector is used to turn an internal segment into a section #
+ suitable for insertion into a coff symbol table
+ */
+
+const short seg_N_TYPE[] = { /* in: segT out: N_TYPE bits */
+ C_ABS_SECTION,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ C_UNDEF_SECTION, /* SEG_UNKNOWN */
+ C_UNDEF_SECTION, /* SEG_ABSENT */
+ C_UNDEF_SECTION, /* SEG_PASS1 */
+ C_UNDEF_SECTION, /* SEG_GOOF */
+ C_UNDEF_SECTION, /* SEG_BIG */
+ C_UNDEF_SECTION, /* SEG_DIFFERENCE */
+ C_DEBUG_SECTION, /* SEG_DEBUG */
+ C_NTV_SECTION, /* SEG_NTV */
+ C_PTV_SECTION, /* SEG_PTV */
+ C_REGISTER_SECTION, /* SEG_REGISTER */
+};
+
+
+int function_lineoff = -1; /* Offset in line#s where the last function
+ started (the odd entry for line #0) */
+
+int our_lineno_number = 0; /* we use this to build pointers from .bf's
+ into the linetable. It should match
+ exactly the values that are later
+ assigned in text_lineno_number by
+ write.c. */
+
+int text_lineno_number = 0;
+
+/* Add 4 to the real value to get the index and compensate the
+ negatives. This vector is used by S_GET_SEGMENT to turn a coff
+ section number into a segment number
+ */
+static symbolS *previous_file_symbol = NULL;
+void c_symbol_merge();
+static int line_base;
+
+symbolS *c_section_symbol();
+bfd *abfd;
+void EXFUN(bfd_as_write_hook,(struct internal_filehdr *,
+ bfd *abfd));
+
+static void EXFUN(fixup_segment,(fixS * fixP,
+ segT this_segment_type));
+
+static void EXFUN(fill_section,(bfd *abfd ,
+ struct internal_filehdr *f, unsigned
+ long *));
+
+
+char *EXFUN(s_get_name,(symbolS *s));
+static symbolS *EXFUN(tag_find_or_make,(char *name));
+static symbolS* EXFUN(tag_find,(char *name));
+
+
+static int
+ EXFUN(c_line_new,(
+ symbolS *symbol,
+ long paddr,
+ unsigned short line_number,
+ fragS* frag));
+
+
+static void EXFUN(w_symbols,
+ (bfd *abfd ,
+ char *where ,
+ symbolS *symbol_rootP));
+
+
+
+static void EXFUN( obj_coff_def,(int what));
+static void EXFUN( obj_coff_lcomm,(void));
+static void EXFUN( obj_coff_dim,(void));
+static void EXFUN( obj_coff_text,(void));
+static void EXFUN( obj_coff_data,(void));
+static void EXFUN( obj_coff_endef,(void));
+static void EXFUN( obj_coff_line,(void));
+static void EXFUN( obj_coff_ln,(void));
+static void EXFUN( obj_coff_scl,(void));
+static void EXFUN( obj_coff_size,(void));
+static void EXFUN( obj_coff_tag,(void));
+static void EXFUN( obj_coff_type,(void));
+static void EXFUN( obj_coff_val,(void));
+static void EXFUN( obj_coff_section,(void));
+static void EXFUN( tag_init,(void));
+static void EXFUN( tag_insert,(char *name, symbolS *symbolP));
+
+
+static struct hash_control *tag_hash;
+static symbolS *def_symbol_in_progress = NULL;
+
+const pseudo_typeS obj_pseudo_table[] = {
+ { "def", obj_coff_def, 0 },
+ { "dim", obj_coff_dim, 0 },
+ { "endef", obj_coff_endef, 0 },
+ { "line", obj_coff_line, 0 },
+ { "ln", obj_coff_ln, 0 },
+ { "scl", obj_coff_scl, 0 },
+ { "size", obj_coff_size, 0 },
+ { "tag", obj_coff_tag, 0 },
+ { "type", obj_coff_type, 0 },
+ { "val", obj_coff_val, 0 },
+ { "section", obj_coff_section, 0 },
+ { "text", obj_coff_text, 0 },
+ { "data", obj_coff_data, 0 },
+ /* we don't yet handle this. */
+ { "ident", s_ignore, 0 },
+ { "ABORT", s_abort, 0 },
+ { "lcomm", obj_coff_lcomm, 0},
+ { NULL} /* end sentinel */
+}; /* obj_pseudo_table */
+
+
+
+/* Section stuff
+
+ We allow more than just the standard 3 sections, infact, we allow
+ 10 sections, (though the usual three have to be there).
+
+ This structure performs the mappings for us:
+
+ */
+
+/* OBS stuff
+ static struct internal_scnhdr bss_section_header;
+ struct internal_scnhdr data_section_header;
+ struct internal_scnhdr text_section_header;
+
+ const segT N_TYPE_seg[32] =
+ {
+
+ };
+
+ */
+
+#define N_SEG 32
+typedef struct
+{
+ segT seg_t;
+ int i;
+} seg_info_type;
+
+seg_info_type seg_info_off_by_4[N_SEG] =
+{
+ {SEG_PTV, },
+ {SEG_NTV, },
+ {SEG_DEBUG, },
+ {SEG_ABSOLUTE, },
+ {SEG_UNKNOWN, },
+ {SEG_E0},
+ {SEG_E1},
+ {SEG_E2},
+ {SEG_E3},
+ {SEG_E4},
+ {SEG_E5},
+ {SEG_E6},
+ {SEG_E7},
+ {SEG_E8},
+ {SEG_E9},
+ {15},
+ {16},
+ {17},
+ {18},
+ {19},
+ {20},
+ {0},
+ {0},
+ {0},
+ {SEG_REGISTER},0,0,0,0};
+
+#define SEG_INFO_FROM_SECTION_NUMBER(x) (seg_info_off_by_4[(x)+4])
+#define SEG_INFO_FROM_SEG_NUMBER(x) (seg_info_off_by_4[(x)])
+
+
+relax_addressT
+ DEFUN(relax_align,(address, alignment),
+ register relax_addressT address AND
+ register long alignment )
+{
+ relax_addressT mask;
+ relax_addressT new_address;
+
+ mask = ~ ( (~0) << alignment );
+ new_address = (address + mask) & (~ mask);
+ return (new_address - address);
+} /* relax_align() */
+
+
+segT
+ DEFUN(s_get_segment,(x) ,
+ symbolS* x)
+{
+ return SEG_INFO_FROM_SECTION_NUMBER(x->sy_symbol.ost_entry.n_scnum).seg_t;
+}
+
+
+
+/* calculate the size of the frag chain and fill in the section header
+ to contain all of it, also fill in the addr of the sections */
+static unsigned int DEFUN(size_section,(abfd, idx),
+ bfd *abfd AND
+ unsigned int idx)
+{
+
+ unsigned int size = 0;
+ fragS *frag = segment_info[idx].frchainP->frch_root;
+ while (frag) {
+ if (frag->fr_address != size) {
+ printf("Out of step\n");
+ size = frag->fr_address;
+ }
+ size += frag->fr_fix;
+ switch (frag->fr_type) {
+ case rs_fill:
+ case rs_org:
+ size += frag->fr_offset * frag->fr_var;
+ break;
+ case rs_align:
+ size += relax_align(size, frag->fr_offset);
+ }
+ frag = frag->fr_next;
+ }
+ segment_info[idx].scnhdr.s_size = size;
+ return size;
+}
+
+
+static unsigned int DEFUN(count_entries_in_chain,(idx),
+ unsigned int idx)
+{
+ unsigned int nrelocs;
+ fixS *fixup_ptr;
+
+ /* Count the relocations */
+ fixup_ptr = segment_info[idx].fix_root;
+ nrelocs = 0;
+ while (fixup_ptr != (fixS *)NULL)
+ {
+ if (TC_COUNT_RELOC(fixup_ptr))
+ {
+
+#ifdef TC_A29K
+
+ if (fixup_ptr->fx_r_type == RELOC_CONSTH)
+ nrelocs+=2;
+ else
+ nrelocs++;
+#else
+ nrelocs++;
+#endif
+ }
+
+ fixup_ptr = fixup_ptr->fx_next;
+ }
+ return nrelocs;
+}
+
+/* output all the relocations for a section */
+void DEFUN(do_relocs_for,(abfd, file_cursor),
+ bfd *abfd AND
+ unsigned long *file_cursor)
+{
+ unsigned int nrelocs;
+ unsigned int idx;
+
+ for (idx = SEG_E0; idx < SEG_E9; idx++)
+ {
+ if (segment_info[idx].scnhdr.s_name[0])
+ {
+
+ struct external_reloc *ext_ptr;
+ struct external_reloc *external_reloc_vec;
+ unsigned int external_reloc_size;
+ unsigned int count = 0;
+ unsigned int base = segment_info[idx].scnhdr.s_paddr;
+ fixS * fix_ptr = segment_info[idx].fix_root;
+ nrelocs = count_entries_in_chain(idx);
+ external_reloc_size = nrelocs * RELSZ;
+ external_reloc_vec =
+ (struct external_reloc*)malloc(external_reloc_size);
+
+
+
+ ext_ptr = external_reloc_vec;
+
+ /* Fill in the internal coff style reloc struct from the
+ internal fix list */
+ while (fix_ptr)
+ {
+ symbolS *symbol_ptr;
+ struct internal_reloc intr;
+
+ /* Only output some of the relocations */
+ if (TC_COUNT_RELOC(fix_ptr))
+ {
+#ifdef TC_RELOC_MANGLE
+ TC_RELOC_MANGLE(fix_ptr, &intr, base);
+
+#else
+ symbolS *dot;
+ symbol_ptr = fix_ptr->fx_addsy;
+
+ intr.r_type = TC_COFF_FIX2RTYPE(fix_ptr);
+ intr.r_vaddr =
+ base + fix_ptr->fx_frag->fr_address + fix_ptr->fx_where ;
+
+ intr.r_offset = fix_ptr->fx_offset;
+
+ intr.r_offset = 0;
+
+ /* Turn the segment of the symbol into an offset
+ */
+ if (symbol_ptr)
+ {
+ dot = segment_info[S_GET_SEGMENT(symbol_ptr)].dot;
+ if (dot)
+ {
+ intr.r_symndx = dot->sy_number;
+ }
+ else
+ {
+ intr.r_symndx = symbol_ptr->sy_number;
+ }
+
+ }
+ else
+ {
+ intr.r_symndx = -1;
+
+
+ }
+#endif
+
+ (void)bfd_coff_swap_reloc_out(abfd, &intr, ext_ptr);
+ ext_ptr++;
+
+#if defined(TC_A29K)
+ /* The 29k has a special kludge for the high 16 bit reloc.
+ Two relocations are emmited, R_IHIHALF, and
+ R_IHCONST. The second one doesn't contain a symbol,
+ but uses the value for offset */
+
+ if (intr.r_type == R_IHIHALF)
+ {
+ /* now emit the second bit */
+ intr.r_type = R_IHCONST;
+ intr.r_symndx = fix_ptr->fx_addnumber;
+ (void)bfd_coff_swap_reloc_out(abfd,&intr,ext_ptr);
+ ext_ptr++;
+ }
+#endif
+ }
+
+ fix_ptr = fix_ptr->fx_next;
+ }
+
+ /* Write out the reloc table */
+ segment_info[idx].scnhdr.s_relptr = *file_cursor;
+ segment_info[idx].scnhdr.s_nreloc = nrelocs;
+ bfd_write((PTR)external_reloc_vec, 1, external_reloc_size, abfd);
+ *file_cursor += external_reloc_size;
+ free( external_reloc_vec);
+ }
+ }
+}
+
+
+/* run through a frag chain and write out the data to go with it, fill
+ in the scnhdrs with the info on the file postions
+ */
+static void DEFUN(fill_section,(abfd, filehdr, file_cursor),
+ bfd *abfd AND
+ struct internal_filehdr *filehdr AND
+ unsigned long *file_cursor)
+{
+
+ unsigned int i;
+ unsigned int paddr = 0;
+
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ {
+ unsigned int offset = 0;
+
+ struct internal_scnhdr *s = &( segment_info[i].scnhdr);
+
+ if (s->s_name[0])
+ {
+ fragS *frag = segment_info[i].frchainP->frch_root;
+ char *buffer = malloc(s->s_size);
+ s->s_scnptr = *file_cursor;
+ s->s_paddr = paddr;
+ s->s_vaddr = paddr;
+
+ s->s_flags = STYP_REG;
+ if (strcmp(s->s_name,".text") == 0)
+ s->s_flags |= STYP_TEXT;
+ else if (strcmp(s->s_name,".data") == 0)
+ s->s_flags |= STYP_DATA;
+ else if (strcmp(s->s_name,".bss") == 0)
+ s->s_flags |= STYP_BSS | STYP_NOLOAD;
+
+ while (frag) {
+ unsigned int fill_size;
+ switch (frag->fr_type) {
+
+ case rs_fill:
+ case rs_align:
+ case rs_org:
+ if (frag->fr_fix)
+ {
+ memcpy(buffer + frag->fr_address,
+ frag->fr_literal,
+ frag->fr_fix);
+ offset += frag->fr_fix;
+ }
+
+ fill_size = frag->fr_var;
+ if (fill_size)
+ {
+ unsigned int count ;
+ unsigned int off = frag->fr_fix;
+ for (count = frag->fr_offset; count; count--)
+ {
+ memcpy(buffer + frag->fr_address + off,
+ frag->fr_literal + frag->fr_fix,
+ fill_size);
+ off += fill_size;
+ offset += fill_size;
+
+ }
+
+ }
+ break;
+ default:
+ abort();
+ }
+ frag = frag->fr_next;
+ }
+
+
+ bfd_write(buffer, s->s_size,1,abfd);
+ free(buffer);
+
+ *file_cursor += s->s_size;
+ paddr += s->s_size;
+ }
+ }
+
+}
+
+
+
+/* Coff file generation & utilities */
+
+
+static void
+ DEFUN(coff_header_append,(abfd, filehdr, aouthdr),
+ bfd *abfd AND
+ struct internal_filehdr *filehdr AND
+ struct internal_aouthdr *aouthdr)
+{
+ unsigned int i;
+ char buffer[1000];
+ char buffero[1000];
+
+ bfd_seek(abfd, 0, 0);
+#if 0
+ filehdr.f_opthdr = bfd_coff_swap_aouthdr_out(abfd, aouthdr,
+ buffero);
+#else
+ filehdr->f_opthdr = 0;
+#endif
+ i = bfd_coff_swap_filehdr_out(abfd, filehdr, buffer);
+
+ bfd_write(buffer, i ,1, abfd);
+ bfd_write(buffero, filehdr->f_opthdr, 1, abfd);
+
+ for (i = SEG_E0; i < SEG_E9; i++)
+ {
+ if (segment_info[i].scnhdr.s_name[0])
+ {
+ unsigned int size =
+ bfd_coff_swap_scnhdr_out(abfd,
+ &(segment_info[i].scnhdr),
+ buffer);
+ bfd_write(buffer, size, 1, abfd);
+ }
+ }
+}
+
+
+char *
+ DEFUN(symbol_to_chars,(abfd, where, symbolP),
+ bfd*abfd AND
+ char *where AND
+ symbolS *symbolP)
+{
+ unsigned int numaux = symbolP->sy_symbol.ost_entry.n_numaux;
+ unsigned int i;
+
+ /* Turn any symbols with register attributes into abs symbols */
+ if (S_GET_SEGMENT(symbolP) == SEG_REGISTER)
+ {
+ S_SET_SEGMENT(symbolP, SEG_ABSOLUTE);
+ }
+ /* At the same time, relocate all symbols to their output value */
+
+ S_SET_VALUE(symbolP,
+ segment_info[S_GET_SEGMENT(symbolP)].scnhdr.s_paddr
+ + S_GET_VALUE(symbolP));
+
+ where += bfd_coff_swap_sym_out(abfd, &symbolP->sy_symbol.ost_entry,
+ where);
+
+ for (i = 0; i < numaux; i++)
+ {
+ where += bfd_coff_swap_aux_out(abfd,
+ &symbolP->sy_symbol.ost_auxent[i],
+ S_GET_DATA_TYPE(symbolP),
+ S_GET_STORAGE_CLASS(symbolP),
+ where);
+ }
+ return where;
+
+}
+
+
+
+
+void obj_symbol_new_hook(symbolP)
+symbolS *symbolP;
+{
+ char underscore = 0; /* Symbol has leading _ */
+
+ /* Effective symbol */
+ /* Store the pointer in the offset. */
+ S_SET_ZEROES(symbolP, 0L);
+ S_SET_DATA_TYPE(symbolP, T_NULL);
+ S_SET_STORAGE_CLASS(symbolP, 0);
+ S_SET_NUMBER_AUXILIARY(symbolP, 0);
+ /* Additional information */
+ symbolP->sy_symbol.ost_flags = 0;
+ /* Auxiliary entries */
+ memset((char*) &symbolP->sy_symbol.ost_auxent[0], '\0', AUXESZ);
+
+#ifdef STRIP_UNDERSCORE
+ /* Remove leading underscore at the beginning of the symbol.
+ * This is to be compatible with the standard librairies.
+ */
+ if (*S_GET_NAME(symbolP) == '_') {
+ underscore = 1;
+ S_SET_NAME(symbolP, S_GET_NAME(symbolP) + 1);
+ } /* strip underscore */
+#endif /* STRIP_UNDERSCORE */
+
+ if (S_IS_STRING(symbolP))
+ SF_SET_STRING(symbolP);
+ if (!underscore && S_IS_LOCAL(symbolP))
+ SF_SET_LOCAL(symbolP);
+
+ return;
+} /* obj_symbol_new_hook() */
+
+/* stack stuff */
+stack* stack_init(chunk_size, element_size)
+unsigned long chunk_size;
+unsigned long element_size;
+{
+ stack* st;
+
+ if ((st = (stack*)malloc(sizeof(stack))) == (stack*)0)
+ return (stack*)0;
+ if ((st->data = malloc(chunk_size)) == (char*)0) {
+ free(st);
+ return (stack*)0;
+ }
+ st->pointer = 0;
+ st->size = chunk_size;
+ st->chunk_size = chunk_size;
+ st->element_size = element_size;
+ return st;
+} /* stack_init() */
+
+void stack_delete(st)
+stack* st;
+{
+ free(st->data);
+ free(st);
+}
+
+char *stack_push(st, element)
+stack *st;
+char *element;
+{
+ if (st->pointer + st->element_size >= st->size) {
+ st->size += st->chunk_size;
+ if ((st->data = xrealloc(st->data, st->size)) == (char*)0)
+ return (char*)0;
+ }
+ memcpy(st->data + st->pointer, element, st->element_size);
+ st->pointer += st->element_size;
+ return st->data + st->pointer;
+} /* stack_push() */
+
+char* stack_pop(st)
+stack* st;
+{
+ if ((st->pointer -= st->element_size) < 0) {
+ st->pointer = 0;
+ return (char*)0;
+ }
+
+ return st->data + st->pointer;
+}
+
+char* stack_top(st)
+stack* st;
+{
+ return st->data + st->pointer - st->element_size;
+}
+
+
+/*
+ * Handle .ln directives.
+ */
+
+static void obj_coff_ln()
+{
+ int l;
+
+ if (def_symbol_in_progress != NULL) {
+ as_warn(".ln pseudo-op inside .def/.endef: ignored.");
+ demand_empty_rest_of_line();
+ return;
+ } /* wrong context */
+
+ c_line_new(0,
+ obstack_next_free(&frags) - frag_now->fr_literal,
+ l = get_absolute_expression(),
+ frag_now);
+#ifndef NO_LISTING
+ {
+ extern int listing;
+
+ if (listing)
+ {
+ listing_source_line(l + line_base - 1);
+ }
+
+ }
+#endif
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_line() */
+
+/*
+ * def()
+ *
+ * Handle .def directives.
+ *
+ * One might ask : why can't we symbol_new if the symbol does not
+ * already exist and fill it with debug information. Because of
+ * the C_EFCN special symbol. It would clobber the value of the
+ * function symbol before we have a chance to notice that it is
+ * a C_EFCN. And a second reason is that the code is more clear this
+ * way. (at least I think it is :-).
+ *
+ */
+
+#define SKIP_SEMI_COLON() while (*input_line_pointer++ != ';')
+#define SKIP_WHITESPACES() while (*input_line_pointer == ' ' || \
+ *input_line_pointer == '\t') \
+ input_line_pointer++;
+
+static void
+ DEFUN(obj_coff_def,(what),
+ int what)
+{
+ char name_end; /* Char after the end of name */
+ char *symbol_name; /* Name of the debug symbol */
+ char *symbol_name_copy; /* Temporary copy of the name */
+ unsigned int symbol_name_length;
+ /*$char* directiveP;$ */ /* Name of the pseudo opcode */
+ /*$char directive[MAX_DIRECTIVE];$ */ /* Backup of the directive */
+ /*$char end = 0;$ */ /* If 1, stop parsing */
+
+ if (def_symbol_in_progress != NULL) {
+ as_warn(".def pseudo-op used inside of .def/.endef: ignored.");
+ demand_empty_rest_of_line();
+ return;
+ } /* if not inside .def/.endef */
+
+ SKIP_WHITESPACES();
+
+ def_symbol_in_progress = (symbolS *) obstack_alloc(&notes, sizeof(*def_symbol_in_progress));
+ memset(def_symbol_in_progress, '\0', sizeof(*def_symbol_in_progress));
+
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end();
+ symbol_name_length = strlen(symbol_name);
+ symbol_name_copy = xmalloc(symbol_name_length + 1);
+ strcpy(symbol_name_copy, symbol_name);
+
+ /* Initialize the new symbol */
+#ifdef STRIP_UNDERSCORE
+ S_SET_NAME(def_symbol_in_progress, (*symbol_name_copy == '_'
+ ? symbol_name_copy + 1
+ : symbol_name_copy));
+#else /* STRIP_UNDERSCORE */
+ S_SET_NAME(def_symbol_in_progress, symbol_name_copy);
+#endif /* STRIP_UNDERSCORE */
+ /* free(symbol_name_copy); */
+ def_symbol_in_progress->sy_name_offset = ~0;
+ def_symbol_in_progress->sy_number = ~0;
+ def_symbol_in_progress->sy_frag = &zero_address_frag;
+
+ if (S_IS_STRING(def_symbol_in_progress)) {
+ SF_SET_STRING(def_symbol_in_progress);
+ } /* "long" name */
+
+ *input_line_pointer = name_end;
+
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_def() */
+
+unsigned int dim_index;
+static void
+ DEFUN_VOID(obj_coff_endef)
+{
+ symbolS *symbolP = 0;
+ /* DIM BUG FIX sac@cygnus.com */
+ dim_index =0;
+ if (def_symbol_in_progress == NULL) {
+ as_warn(".endef pseudo-op used outside of .def/.endef: ignored.");
+ demand_empty_rest_of_line();
+ return;
+ } /* if not inside .def/.endef */
+
+ /* Set the section number according to storage class. */
+ switch (S_GET_STORAGE_CLASS(def_symbol_in_progress)) {
+ case C_STRTAG:
+ case C_ENTAG:
+ case C_UNTAG:
+ SF_SET_TAG(def_symbol_in_progress);
+ /* intentional fallthrough */
+ case C_FILE:
+ case C_TPDEF:
+ SF_SET_DEBUG(def_symbol_in_progress);
+ S_SET_SEGMENT(def_symbol_in_progress, SEG_DEBUG);
+ break;
+
+ case C_EFCN:
+ SF_SET_LOCAL(def_symbol_in_progress); /* Do not emit this symbol. */
+ /* intentional fallthrough */
+ case C_BLOCK:
+ SF_SET_PROCESS(def_symbol_in_progress); /* Will need processing before writing */
+ /* intentional fallthrough */
+ case C_FCN:
+ S_SET_SEGMENT(def_symbol_in_progress, SEG_E0);
+
+ if (def_symbol_in_progress->sy_symbol.ost_entry.n_name[1] == 'b') { /* .bf */
+ if (function_lineoff < 0) {
+ fprintf(stderr, "`.bf' symbol without preceding function\n");
+ } /* missing function symbol */
+ SA_GET_SYM_LNNOPTR(def_symbol_in_progress) = function_lineoff;
+ SF_SET_PROCESS(def_symbol_in_progress); /* Will need relocating */
+ function_lineoff = -1;
+ }
+ break;
+
+#ifdef C_AUTOARG
+ case C_AUTOARG:
+#endif /* C_AUTOARG */
+ case C_AUTO:
+ case C_REG:
+ case C_MOS:
+ case C_MOE:
+ case C_MOU:
+ case C_ARG:
+ case C_REGPARM:
+ case C_FIELD:
+ case C_EOS:
+ SF_SET_DEBUG(def_symbol_in_progress);
+ S_SET_SEGMENT(def_symbol_in_progress, SEG_ABSOLUTE);
+ break;
+
+ case C_EXT:
+ case C_STAT:
+ case C_LABEL:
+ /* Valid but set somewhere else (s_comm, s_lcomm, colon) */
+ break;
+
+ case C_USTATIC:
+ case C_EXTDEF:
+ case C_ULABEL:
+ as_warn("unexpected storage class %d", S_GET_STORAGE_CLASS(def_symbol_in_progress));
+ break;
+ } /* switch on storage class */
+
+ /* Now that we have built a debug symbol, try to
+ find if we should merge with an existing symbol
+ or not. If a symbol is C_EFCN or SEG_ABSOLUTE or
+ untagged SEG_DEBUG it never merges. */
+
+ /* Two cases for functions. Either debug followed
+ by definition or definition followed by debug.
+ For definition first, we will merge the debug
+ symbol into the definition. For debug first, the
+ lineno entry MUST point to the definition
+ function or else it will point off into space
+ when crawl_symbols() merges the debug
+ symbol into the real symbol. Therefor, let's
+ presume the debug symbol is a real function
+ reference. */
+
+ /* FIXME-SOON If for some reason the definition
+ label/symbol is never seen, this will probably
+ leave an undefined symbol at link time. */
+
+ if (S_GET_STORAGE_CLASS(def_symbol_in_progress) == C_EFCN
+ || (S_GET_SEGMENT(def_symbol_in_progress) == SEG_DEBUG
+ && !SF_GET_TAG(def_symbol_in_progress))
+ || S_GET_SEGMENT(def_symbol_in_progress) == SEG_ABSOLUTE
+ || (symbolP = symbol_find_base(S_GET_NAME(def_symbol_in_progress), DO_NOT_STRIP)) == NULL) {
+
+ symbol_append(def_symbol_in_progress, symbol_lastP, &symbol_rootP, &symbol_lastP);
+
+ } else {
+ /* This symbol already exists, merge the
+ newly created symbol into the old one.
+ This is not mandatory. The linker can
+ handle duplicate symbols correctly. But I
+ guess that it save a *lot* of space if
+ the assembly file defines a lot of
+ symbols. [loic] */
+
+ /* The debug entry (def_symbol_in_progress)
+ is merged into the previous definition. */
+
+ c_symbol_merge(def_symbol_in_progress, symbolP);
+ /* FIXME-SOON Should *def_symbol_in_progress be free'd? xoxorich. */
+ def_symbol_in_progress = symbolP;
+
+ if (SF_GET_FUNCTION(def_symbol_in_progress)
+ || SF_GET_TAG(def_symbol_in_progress)) {
+ /* For functions, and tags, the symbol *must* be where the debug symbol
+ appears. Move the existing symbol to the current place. */
+ /* If it already is at the end of the symbol list, do nothing */
+ if (def_symbol_in_progress != symbol_lastP) {
+ symbol_remove(def_symbol_in_progress, &symbol_rootP, &symbol_lastP);
+ symbol_append(def_symbol_in_progress, symbol_lastP, &symbol_rootP, &symbol_lastP);
+ } /* if not already in place */
+ } /* if function */
+ } /* normal or mergable */
+
+ if (SF_GET_TAG(def_symbol_in_progress)
+ && symbol_find_base(S_GET_NAME(def_symbol_in_progress), DO_NOT_STRIP) == NULL) {
+ tag_insert(S_GET_NAME(def_symbol_in_progress), def_symbol_in_progress);
+ } /* If symbol is a {structure,union} tag, associate symbol to its name. */
+
+ if (SF_GET_FUNCTION(def_symbol_in_progress)) {
+ know(sizeof(def_symbol_in_progress) <= sizeof(long));
+ function_lineoff
+ = c_line_new(def_symbol_in_progress,0, 0, &zero_address_frag);
+
+
+
+ SF_SET_PROCESS(def_symbol_in_progress);
+
+ if (symbolP == NULL) {
+ /* That is, if this is the first
+ time we've seen the function... */
+ symbol_table_insert(def_symbol_in_progress);
+ } /* definition follows debug */
+ } /* Create the line number entry pointing to the function being defined */
+
+ def_symbol_in_progress = NULL;
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_endef() */
+
+static void
+ DEFUN_VOID(obj_coff_dim)
+{
+ register int dim_index;
+
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn(".dim pseudo-op used outside of .def/.endef: ignored.");
+ demand_empty_rest_of_line();
+ return;
+ } /* if not inside .def/.endef */
+
+ S_SET_NUMBER_AUXILIARY(def_symbol_in_progress, 1);
+
+ for (dim_index = 0; dim_index < DIMNUM; dim_index++)
+ {
+ SKIP_WHITESPACES();
+ SA_SET_SYM_DIMEN(def_symbol_in_progress, dim_index, get_absolute_expression());
+
+ switch (*input_line_pointer)
+ {
+
+ case ',':
+ input_line_pointer++;
+ break;
+
+ default:
+ as_warn("badly formed .dim directive ignored");
+ /* intentional fallthrough */
+ case '\n':
+ case ';':
+ dim_index = DIMNUM;
+ break;
+ } /* switch on following character */
+ } /* for each dimension */
+
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_dim() */
+
+static void obj_coff_line()
+{
+ int this_base;
+
+ if (def_symbol_in_progress == NULL) {
+ obj_coff_ln();
+ return;
+ } /* if it looks like a stabs style line */
+
+ this_base = get_absolute_expression();
+ if (this_base > line_base)
+ {
+ line_base = this_base;
+ }
+
+
+#ifndef NO_LISTING
+ {
+ extern int listing;
+ if (listing && 0) {
+ listing_source_line(line_base);
+ }
+ }
+#endif
+ S_SET_NUMBER_AUXILIARY(def_symbol_in_progress, 1);
+ SA_SET_SYM_LNNO(def_symbol_in_progress, line_base);
+
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_line() */
+
+static void obj_coff_size() {
+ if (def_symbol_in_progress == NULL) {
+ as_warn(".size pseudo-op used outside of .def/.endef ignored.");
+ demand_empty_rest_of_line();
+ return;
+ } /* if not inside .def/.endef */
+
+ S_SET_NUMBER_AUXILIARY(def_symbol_in_progress, 1);
+ SA_SET_SYM_SIZE(def_symbol_in_progress, get_absolute_expression());
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_size() */
+
+static void obj_coff_scl() {
+ if (def_symbol_in_progress == NULL) {
+ as_warn(".scl pseudo-op used outside of .def/.endef ignored.");
+ demand_empty_rest_of_line();
+ return;
+ } /* if not inside .def/.endef */
+
+ S_SET_STORAGE_CLASS(def_symbol_in_progress, get_absolute_expression());
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_scl() */
+
+static void obj_coff_tag() {
+ char *symbol_name;
+ char name_end;
+
+ if (def_symbol_in_progress == NULL) {
+ as_warn(".tag pseudo-op used outside of .def/.endef ignored.");
+ demand_empty_rest_of_line();
+ return;
+ } /* if not inside .def/.endef */
+
+ S_SET_NUMBER_AUXILIARY(def_symbol_in_progress, 1);
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end();
+
+ /* Assume that the symbol referred to by .tag is always defined. */
+ /* This was a bad assumption. I've added find_or_make. xoxorich. */
+ SA_SET_SYM_TAGNDX(def_symbol_in_progress, (long) tag_find_or_make(symbol_name));
+ if (SA_GET_SYM_TAGNDX(def_symbol_in_progress) == 0L) {
+ as_warn("tag not found for .tag %s", symbol_name);
+ } /* not defined */
+
+ SF_SET_TAGGED(def_symbol_in_progress);
+ *input_line_pointer = name_end;
+
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_tag() */
+
+static void obj_coff_type() {
+ if (def_symbol_in_progress == NULL) {
+ as_warn(".type pseudo-op used outside of .def/.endef ignored.");
+ demand_empty_rest_of_line();
+ return;
+ } /* if not inside .def/.endef */
+
+ S_SET_DATA_TYPE(def_symbol_in_progress, get_absolute_expression());
+
+ if (ISFCN(S_GET_DATA_TYPE(def_symbol_in_progress)) &&
+ S_GET_STORAGE_CLASS(def_symbol_in_progress) != C_TPDEF) {
+ SF_SET_FUNCTION(def_symbol_in_progress);
+ } /* is a function */
+
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_type() */
+
+static void obj_coff_val() {
+ if (def_symbol_in_progress == NULL) {
+ as_warn(".val pseudo-op used outside of .def/.endef ignored.");
+ demand_empty_rest_of_line();
+ return;
+ } /* if not inside .def/.endef */
+
+ if (is_name_beginner(*input_line_pointer)) {
+ char *symbol_name = input_line_pointer;
+ char name_end = get_symbol_end();
+
+ if (!strcmp(symbol_name, ".")) {
+ def_symbol_in_progress->sy_frag = frag_now;
+ S_SET_VALUE(def_symbol_in_progress, obstack_next_free(&frags) - frag_now->fr_literal);
+ /* If the .val is != from the .def (e.g. statics) */
+ } else if (strcmp(S_GET_NAME(def_symbol_in_progress), symbol_name)) {
+ def_symbol_in_progress->sy_forward = symbol_find_or_make(symbol_name);
+
+ /* If the segment is undefined when the forward
+ reference is solved, then copy the segment id
+ from the forward symbol. */
+ SF_SET_GET_SEGMENT(def_symbol_in_progress);
+ }
+ /* Otherwise, it is the name of a non debug symbol and its value will be calculated later. */
+ *input_line_pointer = name_end;
+ } else {
+ S_SET_VALUE(def_symbol_in_progress, get_absolute_expression());
+ } /* if symbol based */
+
+ demand_empty_rest_of_line();
+ return;
+} /* obj_coff_val() */
+
+/*
+ * Maintain a list of the tagnames of the structres.
+ */
+
+static void tag_init() {
+ tag_hash = hash_new();
+ return ;
+} /* tag_init() */
+
+static void tag_insert(name, symbolP)
+char *name;
+symbolS *symbolP;
+{
+ register char * error_string;
+
+ if (*(error_string = hash_jam(tag_hash, name, (char *)symbolP))) {
+ as_fatal("Inserting \"%s\" into structure table failed: %s",
+ name, error_string);
+ }
+ return ;
+} /* tag_insert() */
+
+static symbolS *tag_find_or_make(name)
+char *name;
+{
+ symbolS *symbolP;
+
+ if ((symbolP = tag_find(name)) == NULL) {
+ symbolP = symbol_new(name,
+ SEG_UNKNOWN,
+ 0,
+ &zero_address_frag);
+
+ tag_insert(S_GET_NAME(symbolP), symbolP);
+ symbol_table_insert(symbolP);
+ } /* not found */
+
+ return(symbolP);
+} /* tag_find_or_make() */
+
+static symbolS *tag_find(name)
+char *name;
+{
+#ifdef STRIP_UNDERSCORE
+ if (*name == '_') name++;
+#endif /* STRIP_UNDERSCORE */
+ return((symbolS*)hash_find(tag_hash, name));
+} /* tag_find() */
+
+void obj_read_begin_hook() {
+ /* These had better be the same. Usually 18 bytes. */
+#ifndef BFD_HEADERS
+ know(sizeof(SYMENT) == sizeof(AUXENT));
+ know(SYMESZ == AUXESZ);
+#endif
+ tag_init();
+
+ return;
+} /* obj_read_begin_hook() */
+
+/* This function runs through the symbol table and puts all the
+ externals onto another chain */
+
+/* The chain of externals */
+symbolS *symbol_externP = NULL;
+symbolS *symbol_extern_lastP = NULL;
+
+stack*block_stack;
+symbolS *last_functionP = NULL;
+symbolS *last_tagP;
+
+
+static unsigned int DEFUN_VOID(yank_symbols)
+{
+ symbolS *symbolP;
+ unsigned int symbol_number =0;
+
+ for (symbolP = symbol_rootP;
+ symbolP;
+ symbolP = symbolP ? symbol_next(symbolP) : symbol_rootP) {
+ if (!SF_GET_DEBUG(symbolP)) {
+ /* Debug symbols do not need all this rubbish */
+ symbolS* real_symbolP;
+
+ /* L* and C_EFCN symbols never merge. */
+ if (!SF_GET_LOCAL(symbolP)
+ && (real_symbolP = symbol_find_base(S_GET_NAME(symbolP), DO_NOT_STRIP))
+ && real_symbolP != symbolP) {
+ /* FIXME-SOON: where do dups come from?
+ Maybe tag references before definitions? xoxorich. */
+ /* Move the debug data from the debug symbol to the
+ real symbol. Do NOT do the oposite (i.e. move from
+ real symbol to debug symbol and remove real symbol from the
+ list.) Because some pointers refer to the real symbol
+ whereas no pointers refer to the debug symbol. */
+ c_symbol_merge(symbolP, real_symbolP);
+ /* Replace the current symbol by the real one */
+ /* The symbols will never be the last or the first
+ because : 1st symbol is .file and 3 last symbols are
+ .text, .data, .bss */
+ symbol_remove(real_symbolP, &symbol_rootP, &symbol_lastP);
+ symbol_insert(real_symbolP, symbolP, &symbol_rootP, &symbol_lastP);
+ symbol_remove(symbolP, &symbol_rootP, &symbol_lastP);
+ symbolP = real_symbolP;
+ } /* if not local but dup'd */
+
+ if (flagseen['R'] && (S_GET_SEGMENT(symbolP) == SEG_E1)) {
+ S_SET_SEGMENT(symbolP, SEG_E0);
+ } /* push data into text */
+
+ S_SET_VALUE(symbolP,
+ S_GET_VALUE(symbolP) + symbolP->sy_frag->fr_address);
+
+ if (!S_IS_DEFINED(symbolP) && !SF_GET_LOCAL(symbolP))
+ {
+ S_SET_EXTERNAL(symbolP);
+ }
+ else if (S_GET_STORAGE_CLASS(symbolP) == C_NULL)
+ {
+ if (S_GET_SEGMENT(symbolP) == SEG_E0)
+ {
+ S_SET_STORAGE_CLASS(symbolP, C_LABEL);
+ }
+ else
+ {
+ S_SET_STORAGE_CLASS(symbolP, C_STAT);
+ }
+ }
+
+ /* Mainly to speed up if not -g */
+ if (SF_GET_PROCESS(symbolP))
+ {
+ /* Handle the nested blocks auxiliary info. */
+ if (S_GET_STORAGE_CLASS(symbolP) == C_BLOCK) {
+ if (!strcmp(S_GET_NAME(symbolP), ".bb"))
+ stack_push(block_stack, (char *) &symbolP);
+ else { /* .eb */
+ register symbolS* begin_symbolP;
+ begin_symbolP = *(symbolS**)stack_pop(block_stack);
+ if (begin_symbolP == (symbolS*)0)
+ as_warn("mismatched .eb");
+ else
+ SA_SET_SYM_ENDNDX(begin_symbolP, symbol_number+2);
+ }
+ }
+ /* If we are able to identify the type of a function, and we
+ are out of a function (last_functionP == 0) then, the
+ function symbol will be associated with an auxiliary
+ entry. */
+ if (last_functionP == (symbolS*)0 &&
+ SF_GET_FUNCTION(symbolP)) {
+ last_functionP = symbolP;
+
+ if (S_GET_NUMBER_AUXILIARY(symbolP) < 1) {
+ S_SET_NUMBER_AUXILIARY(symbolP, 1);
+ } /* make it at least 1 */
+
+ /* Clobber possible stale .dim information. */
+ memset(symbolP->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_ary.x_dimen,
+ '\0', sizeof(symbolP->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_ary.x_dimen));
+ }
+ /* The C_FCN doesn't need any additional information.
+ I don't even know if this is needed for sdb. But the
+ standard assembler generates it, so...
+ */
+ if (S_GET_STORAGE_CLASS(symbolP) == C_EFCN) {
+ if (last_functionP == (symbolS*)0)
+ as_fatal("C_EFCN symbol out of scope");
+ SA_SET_SYM_FSIZE(last_functionP,
+ (long)(S_GET_VALUE(symbolP) -
+ S_GET_VALUE(last_functionP)));
+ SA_SET_SYM_ENDNDX(last_functionP, symbol_number);
+ last_functionP = (symbolS*)0;
+ }
+ }
+ } else if (SF_GET_TAG(symbolP)) {
+ /* First descriptor of a structure must point to
+ the first slot after the structure description. */
+ last_tagP = symbolP;
+
+ } else if (S_GET_STORAGE_CLASS(symbolP) == C_EOS) {
+ /* +2 take in account the current symbol */
+ SA_SET_SYM_ENDNDX(last_tagP, symbol_number + 2);
+ } else if (S_GET_STORAGE_CLASS(symbolP) == C_FILE) {
+ if (S_GET_VALUE(symbolP)) {
+ S_SET_VALUE((symbolS *) S_GET_VALUE(symbolP), symbol_number);
+ S_SET_VALUE(symbolP, 0);
+ } /* no one points at the first .file symbol */
+ } /* if debug or tag or eos or file */
+
+ /* We must put the external symbols apart. The loader
+ does not bomb if we do not. But the references in
+ the endndx field for a .bb symbol are not corrected
+ if an external symbol is removed between .bb and .be.
+ I.e in the following case :
+ [20] .bb endndx = 22
+ [21] foo external
+ [22] .be
+ ld will move the symbol 21 to the end of the list but
+ endndx will still be 22 instead of 21. */
+
+
+ if (SF_GET_LOCAL(symbolP)) {
+ /* remove C_EFCN and LOCAL (L...) symbols */
+ /* next pointer remains valid */
+ symbol_remove(symbolP, &symbol_rootP, &symbol_lastP);
+
+ }
+ else if (!S_IS_DEFINED(symbolP)
+ && !S_IS_DEBUG(symbolP)
+ && !SF_GET_STATICS(symbolP) &&
+ S_GET_STORAGE_CLASS(symbolP) == C_EXT)
+ { /* C_EXT && !SF_GET_FUNCTION(symbolP)) */
+ /* if external, Remove from the list */
+ symbolS *hold = symbol_previous(symbolP);
+
+ symbol_remove(symbolP, &symbol_rootP, &symbol_lastP);
+ symbol_clear_list_pointers(symbolP);
+ symbol_append(symbolP, symbol_extern_lastP, &symbol_externP, &symbol_extern_lastP);
+ symbolP = hold;
+ } else {
+ if (SF_GET_STRING(symbolP)) {
+ symbolP->sy_name_offset = string_byte_count;
+ string_byte_count += strlen(S_GET_NAME(symbolP)) + 1;
+ } else {
+ symbolP->sy_name_offset = 0;
+ } /* fix "long" names */
+
+ symbolP->sy_number = symbol_number;
+ symbol_number += 1 + S_GET_NUMBER_AUXILIARY(symbolP);
+ } /* if local symbol */
+ } /* traverse the symbol list */
+ return symbol_number;
+
+}
+
+
+static unsigned int DEFUN_VOID(glue_symbols)
+{
+ unsigned int symbol_number = 0;
+ symbolS *symbolP;
+ for (symbolP = symbol_externP; symbol_externP;) {
+ symbolS *tmp = symbol_externP;
+
+ /* append */
+ symbol_remove(tmp, &symbol_externP, &symbol_extern_lastP);
+ symbol_append(tmp, symbol_lastP, &symbol_rootP, &symbol_lastP);
+
+ /* and process */
+ if (SF_GET_STRING(tmp)) {
+ tmp->sy_name_offset = string_byte_count;
+ string_byte_count += strlen(S_GET_NAME(tmp)) + 1;
+ } else {
+ tmp->sy_name_offset = 0;
+ } /* fix "long" names */
+
+ tmp->sy_number = symbol_number;
+ symbol_number += 1 + S_GET_NUMBER_AUXILIARY(tmp);
+ } /* append the entire extern chain */
+ return symbol_number;
+
+}
+
+static unsigned int DEFUN_VOID(tie_tags)
+{
+ unsigned int symbol_number = 0;
+
+ symbolS*symbolP;
+ for (symbolP = symbol_rootP; symbolP; symbolP =
+ symbol_next(symbolP))
+ {
+ symbolP->sy_number = symbol_number;
+
+
+
+ if (SF_GET_TAGGED(symbolP))
+ {
+ SA_SET_SYM_TAGNDX
+ (symbolP,
+ ((symbolS*) SA_GET_SYM_TAGNDX(symbolP))->sy_number);
+ }
+
+ symbol_number += 1 + S_GET_NUMBER_AUXILIARY(symbolP);
+ }
+ return symbol_number;
+
+}
+
+static void
+ DEFUN(crawl_symbols,(headers, abfd),
+ struct internal_filehdr *headers AND
+ bfd *abfd)
+{
+
+ unsigned int i;
+ unsigned int ptr = 0;
+
+
+ symbolS *symbolP;
+
+ /* Initialize the stack used to keep track of the matching .bb .be */
+
+ block_stack = stack_init(512, sizeof(symbolS*));
+ /* JF deal with forward references first... */
+ for (symbolP = symbol_rootP;
+ symbolP;
+ symbolP = symbol_next(symbolP))
+ {
+
+ if (symbolP->sy_forward) {
+ S_SET_VALUE(symbolP, (S_GET_VALUE(symbolP)
+ + S_GET_VALUE(symbolP->sy_forward)
+ + symbolP->sy_forward->sy_frag->fr_address));
+
+ if (SF_GET_GET_SEGMENT(symbolP)) {
+ S_SET_SEGMENT(symbolP, S_GET_SEGMENT(symbolP->sy_forward));
+ } /* forward segment also */
+
+ symbolP->sy_forward=0;
+ } /* if it has a forward reference */
+ } /* walk the symbol chain */
+
+
+ /* The symbol list should be ordered according to the following sequence
+ * order :
+ * . .file symbol
+ * . debug entries for functions
+ * . fake symbols for the sections, including.text .data and .bss
+ * . defined symbols
+ * . undefined symbols
+ * But this is not mandatory. The only important point is to put the
+ * undefined symbols at the end of the list.
+ */
+
+ if (symbol_rootP == NULL
+ || S_GET_STORAGE_CLASS(symbol_rootP) != C_FILE) {
+ c_dot_file_symbol("fake");
+ }
+ /* Is there a .file symbol ? If not insert one at the beginning. */
+
+ /*
+ * Build up static symbols for the sections, they are filled in later
+ */
+
+
+ for (i = SEG_E0; i < SEG_E9; i++)
+ {
+ if (segment_info[i].scnhdr.s_name[0])
+ {
+ segment_info[i].dot =
+ c_section_symbol(segment_info[i].scnhdr.s_name,
+ i-SEG_E0+1);
+
+ }
+ }
+
+
+ /* Take all the externals out and put them into another chain */
+ headers->f_nsyms = yank_symbols();
+ /* Take the externals and glue them onto the end.*/
+ headers->f_nsyms += glue_symbols();
+
+ headers->f_nsyms = tie_tags();
+ know(symbol_externP == NULL);
+ know(symbol_extern_lastP == NULL);
+
+ return;
+}
+
+/*
+ * Find strings by crawling along symbol table chain.
+ */
+
+void DEFUN(w_strings,(where),
+ char *where)
+{
+ symbolS *symbolP;
+
+ /* Gotta do md_ byte-ordering stuff for string_byte_count first - KWK */
+ md_number_to_chars(where, string_byte_count, sizeof(string_byte_count));
+ where += sizeof(string_byte_count);
+ for (symbolP = symbol_rootP;
+ symbolP;
+ symbolP = symbol_next(symbolP))
+ {
+ unsigned int size;
+
+ if (SF_GET_STRING(symbolP)) {
+ size = strlen(S_GET_NAME(symbolP)) + 1;
+
+ memcpy(where, S_GET_NAME(symbolP),size);
+ where += size;
+
+ }
+ }
+
+}
+
+
+
+
+
+static void
+ DEFUN(do_linenos_for,(abfd, file_cursor),
+ bfd *abfd AND
+ unsigned long *file_cursor)
+{
+ unsigned int idx;
+
+ for (idx = SEG_E0; idx < SEG_E9; idx++)
+ {
+ segment_info_type *s = segment_info + idx;
+
+
+ if (s->scnhdr.s_nlnno != 0)
+ {
+ struct lineno_list *line_ptr ;
+
+ struct external_lineno *buffer =
+ (struct external_lineno *)xmalloc(s->scnhdr.s_nlnno * LINESZ);
+
+ struct external_lineno *dst= buffer;
+
+ /* Run through the table we've built and turn it into its external
+ form, take this chance to remove duplicates */
+
+ for (line_ptr = s->lineno_list_head;
+ line_ptr != (struct lineno_list *)NULL;
+ line_ptr = line_ptr->next)
+ {
+
+ if (line_ptr->line.l_lnno == 0)
+ {
+ /* Turn a pointer to a symbol into the symbols' index */
+ line_ptr->line.l_addr.l_symndx =
+ ( (symbolS *)line_ptr->line.l_addr.l_symndx)->sy_number;
+ }
+ else
+ {
+ line_ptr->line.l_addr.l_paddr += ((struct frag * )(line_ptr->frag))->fr_address;
+ }
+
+
+ (void) bfd_coff_swap_lineno_out(abfd, &(line_ptr->line), dst);
+ dst++;
+
+ }
+
+ s->scnhdr.s_lnnoptr = *file_cursor;
+
+ bfd_write(buffer, 1, s->scnhdr.s_nlnno* LINESZ, abfd);
+ free(buffer);
+
+ *file_cursor += s->scnhdr.s_nlnno * LINESZ;
+ }
+ }
+}
+
+
+/* Now we run through the list of frag chains in a segment and
+ make all the subsegment frags appear at the end of the
+ list, as if the seg 0 was extra long */
+
+static void DEFUN_VOID(remove_subsegs)
+{
+ unsigned int i;
+
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ {
+ frchainS *head = segment_info[i].frchainP;
+ fragS dummy;
+ fragS * prev_frag = &dummy;
+
+ while (head && head->frch_seg == i)
+ {
+ prev_frag->fr_next = head->frch_root;
+ prev_frag = head->frch_last;
+ head = head->frch_next;
+ }
+ prev_frag->fr_next = 0;
+ }
+}
+
+
+extern void DEFUN_VOID(write_object_file)
+{
+ int i;
+ struct frchain *frchain_ptr;
+
+ struct internal_filehdr filehdr;
+ struct internal_aouthdr aouthdr;
+ unsigned long file_cursor;
+ bfd *abfd;
+ unsigned int addr = 0;
+ abfd = bfd_openw(out_file_name, TARGET_FORMAT);
+
+
+ if (abfd == 0) {
+ as_perror ("FATAL: Can't create %s", out_file_name);
+ exit(42);
+ }
+ bfd_set_format(abfd, bfd_object);
+ bfd_set_arch_mach(abfd, BFD_ARCH, 0);
+
+
+
+ string_byte_count = 4;
+
+ for (frchain_ptr = frchain_root;
+ frchain_ptr != (struct frchain *)NULL;
+ frchain_ptr = frchain_ptr->frch_next) {
+ /* Run through all the sub-segments and align them up. Also close any
+ open frags. We tack a .fill onto the end of the frag chain so
+ that any .align's size can be worked by looking at the next
+ frag */
+
+ subseg_new(frchain_ptr->frch_seg, frchain_ptr->frch_subseg);
+#define SUB_SEGMENT_ALIGN 1
+ frag_align(SUB_SEGMENT_ALIGN,0);
+ frag_wane(frag_now);
+ frag_now->fr_fix = 0;
+ know( frag_now->fr_next == NULL );
+ }
+
+
+ remove_subsegs();
+
+
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ {
+ relax_segment(segment_info[i].frchainP->frch_root, i);
+ }
+
+
+
+
+
+ filehdr.f_nscns = 0;
+
+ /* Find out how big the sections are */
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ {
+
+ if (segment_info[i].scnhdr.s_name[0])
+ {
+ filehdr.f_nscns++;
+ }
+ segment_info[i].scnhdr.s_paddr = addr;
+ if (i == SEG_E2) {
+ /* THis is a special case, we leave the size alone, which will have */
+ /* been made up from all and any lcomms seen */
+ }
+ else {
+ addr += size_section(abfd, i);
+ }
+ }
+
+
+
+ /* Turn the gas native symbol table shape into a coff symbol table */
+ crawl_symbols(&filehdr, abfd);
+#ifndef TC_H8300
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ {
+ fixup_segment(segment_info[i].fix_root, i);
+ }
+#endif
+
+ file_cursor = FILHSZ + SCNHSZ * filehdr.f_nscns ;
+
+ bfd_seek(abfd, file_cursor, 0);
+
+
+ do_relocs_for(abfd, &file_cursor);
+
+ do_linenos_for(abfd, &file_cursor);
+
+
+ /* Plant the data */
+
+ fill_section(abfd,&filehdr, &file_cursor);
+
+ filehdr.f_magic = COFF_MAGIC;
+ filehdr.f_timdat = 0;
+ filehdr.f_flags = 0;
+
+
+
+ {
+
+ unsigned int symtable_size = filehdr.f_nsyms * SYMESZ;
+ char *buffer1 = malloc(symtable_size + string_byte_count + 4);
+ char *ptr = buffer1;
+ filehdr.f_symptr = bfd_tell(abfd);
+ w_symbols(abfd, buffer1, symbol_rootP);
+ w_strings(buffer1 + symtable_size);
+ bfd_write(buffer1, 1,symtable_size + string_byte_count + 4, abfd);
+ free(buffer1);
+
+ }
+ coff_header_append(abfd, &filehdr, &aouthdr);
+
+ bfd_close_all_done(abfd);
+}
+
+
+static void DEFUN(change_to_section,(name, len, exp),
+ char *name AND
+ unsigned int len AND
+ unsigned int exp)
+{
+ unsigned int i;
+ /* Find out if we've already got a section of this name etc */
+ for (i = SEG_E0; i < SEG_E9 && segment_info[i].scnhdr.s_name[0] ; i++)
+ {
+ if (strncmp(segment_info[i].scnhdr.s_name, name, len) == 0)
+ {
+ subseg_new(i, exp);
+ return;
+
+ }
+ }
+ /* No section, add one */
+ strncpy(segment_info[i].scnhdr.s_name, name, 8);
+ subseg_new(i, exp);
+}
+
+static void
+ DEFUN_VOID(obj_coff_section)
+{
+ /* Strip out the section name */
+ char *section_name ;
+ char *section_name_end;
+ char c;
+
+ unsigned int len;
+ unsigned int exp;
+
+ section_name = input_line_pointer;
+ c = get_symbol_end();
+ section_name_end = input_line_pointer;
+
+ len = section_name_end - section_name ;
+ input_line_pointer++;
+ SKIP_WHITESPACE();
+ if (c == ',')
+ {
+ exp = get_absolute_expression();
+ }
+ else if ( *input_line_pointer == ',')
+ {
+
+ input_line_pointer++;
+ exp = get_absolute_expression();
+ }
+ else
+ {
+ exp = 0;
+ }
+
+ change_to_section(section_name, len,exp);
+ *section_name_end = c;
+
+}
+
+
+static void obj_coff_text()
+{
+ change_to_section(".text",5, get_absolute_expression());
+}
+
+
+static void obj_coff_data()
+{
+ change_to_section(".data",5, get_absolute_expression());
+}
+
+void c_symbol_merge(debug, normal)
+symbolS *debug;
+symbolS *normal;
+{
+ S_SET_DATA_TYPE(normal, S_GET_DATA_TYPE(debug));
+ S_SET_STORAGE_CLASS(normal, S_GET_STORAGE_CLASS(debug));
+
+ if (S_GET_NUMBER_AUXILIARY(debug) > S_GET_NUMBER_AUXILIARY(normal)) {
+ S_SET_NUMBER_AUXILIARY(normal, S_GET_NUMBER_AUXILIARY(debug));
+ } /* take the most we have */
+
+ if (S_GET_NUMBER_AUXILIARY(debug) > 0) {
+ memcpy((char*)&normal->sy_symbol.ost_auxent[0], (char*)&debug->sy_symbol.ost_auxent[0], S_GET_NUMBER_AUXILIARY(debug) * AUXESZ);
+ } /* Move all the auxiliary information */
+
+ /* Move the debug flags. */
+ SF_SET_DEBUG_FIELD(normal, SF_GET_DEBUG_FIELD(debug));
+} /* c_symbol_merge() */
+
+static int
+ DEFUN(c_line_new,(symbol, paddr, line_number, frag),
+ symbolS *symbol AND
+ long paddr AND
+ unsigned short line_number AND
+ fragS* frag)
+{
+ struct lineno_list* new_line =
+ (struct lineno_list *)xmalloc(sizeof(struct lineno_list));
+
+ segment_info_type *s = segment_info + now_seg;
+ new_line->line.l_lnno = line_number;
+
+ if (line_number == 0)
+ {
+ new_line->line.l_addr.l_symndx = (long)symbol;
+ }
+ else
+ {
+ new_line->line.l_addr.l_paddr = paddr;
+ }
+
+ new_line->frag = (char*)frag;
+ new_line->next = (struct lineno_list*)NULL;
+
+
+ if (s->lineno_list_head == (struct lineno_list *)NULL)
+ {
+ s->lineno_list_head = new_line;
+ }
+ else
+ {
+ s->lineno_list_tail->next = new_line;
+ }
+ s->lineno_list_tail = new_line;
+ return LINESZ * s->scnhdr.s_nlnno ++;
+}
+
+void c_dot_file_symbol(filename)
+char *filename;
+{
+ symbolS* symbolP;
+
+ symbolP = symbol_new(".file",
+ SEG_DEBUG,
+ 0,
+ &zero_address_frag);
+
+ S_SET_STORAGE_CLASS(symbolP, C_FILE);
+ S_SET_NUMBER_AUXILIARY(symbolP, 1);
+ SA_SET_FILE_FNAME(symbolP, filename);
+#ifndef NO_LISTING
+ {
+ extern int listing;
+ if (listing)
+ {
+ listing_source_file(filename);
+ }
+
+ }
+
+#endif
+ SF_SET_DEBUG(symbolP);
+ S_SET_VALUE(symbolP, (long) previous_file_symbol);
+
+ previous_file_symbol = symbolP;
+
+ /* Make sure that the symbol is first on the symbol chain */
+ if (symbol_rootP != symbolP) {
+ if (symbolP == symbol_lastP) {
+ symbol_lastP = symbol_lastP->sy_previous;
+ } /* if it was the last thing on the list */
+
+ symbol_remove(symbolP, &symbol_rootP, &symbol_lastP);
+ symbol_insert(symbolP, symbol_rootP, &symbol_rootP, &symbol_lastP);
+ symbol_rootP = symbolP;
+ } /* if not first on the list */
+
+} /* c_dot_file_symbol() */
+
+/*
+ * Build a 'section static' symbol.
+ */
+
+symbolS *c_section_symbol(name,idx)
+char *name;
+int idx;
+{
+ symbolS *symbolP;
+
+ symbolP = symbol_new(name,idx,
+ 0,
+ &zero_address_frag);
+
+ S_SET_STORAGE_CLASS(symbolP, C_STAT);
+ S_SET_NUMBER_AUXILIARY(symbolP, 1);
+
+ SF_SET_STATICS(symbolP);
+
+ return symbolP;
+} /* c_section_symbol() */
+
+static void
+ DEFUN(w_symbols,(abfd, where, symbol_rootP),
+ bfd *abfd AND
+ char *where AND
+ symbolS *symbol_rootP)
+{
+ symbolS *symbolP;
+ unsigned int i;
+
+ /* First fill in those values we have only just worked out */
+ for (i = SEG_E0; i < SEG_E9; i++)
+ {
+ symbolP = segment_info[i].dot;
+ if (symbolP)
+ {
+
+ SA_SET_SCN_SCNLEN(symbolP, segment_info[i].scnhdr.s_size);
+ SA_SET_SCN_NRELOC(symbolP, segment_info[i].scnhdr.s_nreloc);
+ SA_SET_SCN_NLINNO(symbolP, segment_info[i].scnhdr.s_nlnno);
+
+ }
+ }
+
+ /*
+ * Emit all symbols left in the symbol chain.
+ */
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) {
+ /* Used to save the offset of the name. It is used to point
+ to the string in memory but must be a file offset. */
+ register char * temp;
+
+ tc_coff_symbol_emit_hook(symbolP);
+
+ temp = S_GET_NAME(symbolP);
+ if (SF_GET_STRING(symbolP)) {
+ S_SET_OFFSET(symbolP, symbolP->sy_name_offset);
+ S_SET_ZEROES(symbolP, 0);
+ } else {
+ memset(symbolP->sy_symbol.ost_entry.n_name, '\0', SYMNMLEN);
+ strncpy(symbolP->sy_symbol.ost_entry.n_name, temp, SYMNMLEN);
+ }
+ where = symbol_to_chars(abfd, where, symbolP);
+ S_SET_NAME(symbolP,temp);
+ }
+
+} /* w_symbols() */
+
+static void DEFUN_VOID(obj_coff_lcomm)
+{
+ char *name;
+ char c;
+ int temp;
+ char *p;
+ symbolS *symbolP;
+ name = input_line_pointer;
+
+
+
+ c = get_symbol_end();
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE();
+ if (*input_line_pointer != ',') {
+ as_bad("Expected comma after name");
+ ignore_rest_of_line();
+ return;
+ }
+ if (*input_line_pointer == '\n') {
+ as_bad("Missing size expression");
+ return;
+ }
+ input_line_pointer++;
+ if ((temp = get_absolute_expression ()) < 0) {
+ as_warn("lcomm length (%d.) <0! Ignored.", temp);
+ ignore_rest_of_line();
+ return;
+ }
+ *p = 0;
+ symbolP = symbol_find_or_make(name);
+ S_SET_VALUE(symbolP, segment_info[SEG_E2].scnhdr.s_size);
+ S_SET_SEGMENT(symbolP, SEG_E2);
+ segment_info[SEG_E2].scnhdr.s_size += temp;
+ S_SET_STORAGE_CLASS(symbolP, C_STAT);
+ demand_empty_rest_of_line();
+}
+
+
+#if 1
+static void DEFUN(fixup_segment,(fixP, this_segment_type),
+ register fixS * fixP AND
+ segT this_segment_type)
+{
+ register symbolS *add_symbolP;
+ register symbolS *sub_symbolP;
+ register long add_number;
+ register int size;
+ register char *place;
+ register long where;
+ register char pcrel;
+ register fragS *fragP;
+ register segT add_symbol_segment = SEG_ABSOLUTE;
+
+
+ for ( ; fixP; fixP = fixP->fx_next)
+ {
+ fragP = fixP->fx_frag;
+ know(fragP);
+ where = fixP->fx_where;
+ place = fragP->fr_literal + where;
+ size = fixP->fx_size;
+ add_symbolP = fixP->fx_addsy;
+#ifdef TC_I960
+ if (fixP->fx_callj && TC_S_IS_CALLNAME(add_symbolP)) {
+ /* Relocation should be done via the
+ associated 'bal' entry point
+ symbol. */
+
+ if (!TC_S_IS_BALNAME(tc_get_bal_of_call(add_symbolP))) {
+ as_bad("No 'bal' entry point for leafproc %s",
+ S_GET_NAME(add_symbolP));
+ continue;
+ }
+ fixP->fx_addsy = add_symbolP = tc_get_bal_of_call(add_symbolP);
+ } /* callj relocation */
+#endif
+ sub_symbolP = fixP->fx_subsy;
+ add_number = fixP->fx_offset;
+ pcrel = fixP->fx_pcrel;
+
+ if (add_symbolP) {
+ add_symbol_segment = S_GET_SEGMENT(add_symbolP);
+ } /* if there is an addend */
+
+ if (sub_symbolP) {
+ if (!add_symbolP) {
+ /* Its just -sym */
+ if (S_GET_SEGMENT(sub_symbolP) != SEG_ABSOLUTE) {
+ as_bad("Negative of non-absolute symbol %s", S_GET_NAME(sub_symbolP));
+ } /* not absolute */
+
+ add_number -= S_GET_VALUE(sub_symbolP);
+
+ /* if sub_symbol is in the same segment that add_symbol
+ and add_symbol is either in DATA, TEXT, BSS or ABSOLUTE */
+ } else if ((S_GET_SEGMENT(sub_symbolP) == add_symbol_segment)
+ && (SEG_NORMAL(add_symbol_segment)
+ || (add_symbol_segment == SEG_ABSOLUTE))) {
+ /* Difference of 2 symbols from same segment. */
+ /* Can't make difference of 2 undefineds: 'value' means */
+ /* something different for N_UNDF. */
+#ifdef TC_I960
+ /* Makes no sense to use the difference of 2 arbitrary symbols
+ * as the target of a call instruction.
+ */
+ if (fixP->fx_callj) {
+ as_bad("callj to difference of 2 symbols");
+ }
+#endif /* TC_I960 */
+ add_number += S_GET_VALUE(add_symbolP) -
+ S_GET_VALUE(sub_symbolP);
+
+ add_symbolP = NULL;
+ fixP->fx_addsy = NULL;
+ } else {
+ /* Different segments in subtraction. */
+ know(!(S_IS_EXTERNAL(sub_symbolP) && (S_GET_SEGMENT(sub_symbolP) == SEG_ABSOLUTE)));
+
+ if ((S_GET_SEGMENT(sub_symbolP) == SEG_ABSOLUTE)) {
+ add_number -= S_GET_VALUE(sub_symbolP);
+ } else {
+ as_bad("Can't emit reloc {- %s-seg symbol \"%s\"} @ file address %d.",
+ segment_name(S_GET_SEGMENT(sub_symbolP)),
+ S_GET_NAME(sub_symbolP), fragP->fr_address + where);
+ } /* if absolute */
+ }
+ } /* if sub_symbolP */
+
+ if (add_symbolP) {
+ if (add_symbol_segment == this_segment_type && pcrel) {
+ /*
+ * This fixup was made when the symbol's segment was
+ * SEG_UNKNOWN, but it is now in the local segment.
+ * So we know how to do the address without relocation.
+ */
+#ifdef TC_I960
+ /* reloc_callj() may replace a 'call' with a 'calls' or a 'bal',
+ * in which cases it modifies *fixP as appropriate. In the case
+ * of a 'calls', no further work is required, and *fixP has been
+ * set up to make the rest of the code below a no-op.
+ */
+ reloc_callj(fixP);
+#endif /* TC_I960 */
+
+ add_number += S_GET_VALUE(add_symbolP);
+ add_number -= md_pcrel_from (fixP);
+ pcrel = 0; /* Lie. Don't want further pcrel processing. */
+ fixP->fx_addsy = NULL; /* No relocations please. */
+ } else
+ {
+ switch (add_symbol_segment)
+ {
+ case SEG_ABSOLUTE:
+#ifdef TC_I960
+ reloc_callj(fixP); /* See comment about reloc_callj() above*/
+#endif /* TC_I960 */
+ add_number += S_GET_VALUE(add_symbolP);
+ fixP->fx_addsy = NULL;
+ add_symbolP = NULL;
+ break;
+ default:
+
+ add_number += S_GET_VALUE(add_symbolP) +
+ segment_info[S_GET_SEGMENT(add_symbolP)].scnhdr.s_paddr ;
+ break;
+
+ case SEG_UNKNOWN:
+#ifdef TC_I960
+ if ((int)fixP->fx_bit_fixP == 13) {
+ /* This is a COBR instruction. They have only a
+ * 13-bit displacement and are only to be used
+ * for local branches: flag as error, don't generate
+ * relocation.
+ */
+ as_bad("can't use COBR format with external label");
+ fixP->fx_addsy = NULL; /* No relocations please. */
+ continue;
+ } /* COBR */
+#endif /* TC_I960 */
+
+
+
+ break;
+
+
+ } /* switch on symbol seg */
+ } /* if not in local seg */
+ } /* if there was a + symbol */
+
+ if (pcrel) {
+ add_number -= md_pcrel_from(fixP);
+ if (add_symbolP == 0) {
+ fixP->fx_addsy = & abs_symbol;
+ } /* if there's an add_symbol */
+ } /* if pcrel */
+
+ if (!fixP->fx_bit_fixP) {
+ if ((size == 1
+ && (add_number & ~0xFF) && (add_number & ~0xFF != (-1 & ~0xFF))) ||
+ (size == 2
+ && (add_number & ~0xFFFF) && (add_number & ~0xFFFF != (-1 & ~0xFFFF)))) {
+ as_bad("Value of %d too large for field of %d bytes at 0x%x",
+ add_number, size, fragP->fr_address + where);
+ } /* generic error checking */
+ } /* not a bit fix */
+ /* once this fix has been applied, we don't have to output anything
+ nothing more need be done -*/
+ md_apply_fix(fixP, add_number);
+
+ } /* For each fixS in this segment. */
+
+
+} /* fixup_segment() */
+#endif
+
+/*
+ * Local Variables:
+ * fill-column: 131
+ * End:
+ */
+
+/* end of obj-coffbfd.c */
diff --git a/gnu/usr.bin/as/config/obj-coffbfd.h b/gnu/usr.bin/as/config/obj-coffbfd.h
new file mode 100644
index 0000000..d1afabb
--- /dev/null
+++ b/gnu/usr.bin/as/config/obj-coffbfd.h
@@ -0,0 +1,516 @@
+/* coff object file format
+ Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef OBJ_FORMAT_H
+#define OBJ_FORMAT_H
+
+#define OBJ_COFF 1
+
+#include "targ-cpu.h"
+
+#include "bfd.h"
+
+/*extern bfd *stdoutput;*/
+/* This internal_lineno crap is to stop namespace pollution from the
+ bfd internal coff headerfile. */
+
+#define internal_lineno bfd_internal_lineno
+#include "coff/internal.h"
+#undef internal_lineno
+
+#if defined(TC_H8300)
+#include "coff/h8300.h"
+#define TARGET_FORMAT "coff-h8300"
+#elif defined(TC_A29K)
+#include "coff/a29k.h"
+#define TARGET_FORMAT "coff-a29k-big"
+#else
+help me
+#endif
+
+#if 0
+ /* Define some processor dependent values according to the processor we are
+ on. */
+#if defined(TC_H8300)
+#define BYTE_ORDERING 0
+#define FILE_HEADER_MAGIC H8300MAGIC
+#elif defined(TC_M68K)
+
+#define BYTE_ORDERING F_AR32W /* See filehdr.h for more info. */
+#ifndef FILE_HEADER_MAGIC
+#define FILE_HEADER_MAGIC MC68MAGIC /* ... */
+#endif /* FILE_HEADER_MAGIC */
+
+#elif defined(TC_I386)
+
+#define BYTE_ORDERING F_AR32WR /* See filehdr.h for more info. */
+#ifndef FILE_HEADER_MAGIC
+#define FILE_HEADER_MAGIC I386MAGIC /* ... */
+#endif /* FILE_HEADER_MAGIC */
+
+#elif defined(TC_I960)
+
+#define BYTE_ORDERING F_AR32WR /* See filehdr.h for more info. */
+#ifndef FILE_HEADER_MAGIC
+#define FILE_HEADER_MAGIC I960ROMAGIC /* ... */
+#endif /* FILE_HEADER_MAGIC */
+
+#elif defined(TC_A29K)
+
+#define BYTE_ORDERING F_AR32W /* big endian. */
+#ifndef FILE_HEADER_MAGIC
+#define FILE_HEADER_MAGIC SIPFBOMAGIC
+#endif /* FILE_HEADER_MAGIC */
+
+#else
+you lose
+#endif
+
+#endif
+
+#ifndef OBJ_COFF_MAX_AUXENTRIES
+#define OBJ_COFF_MAX_AUXENTRIES 1
+#endif /* OBJ_COFF_MAX_AUXENTRIES */
+
+
+ extern const segT N_TYPE_seg[];
+
+/* Magic number of paged executable. */
+#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE 0x8300
+
+
+/* SYMBOL TABLE */
+
+/* targets may also set this */
+#ifndef SYMBOLS_NEED_BACKPOINTERS
+#define SYMBOLS_NEED_BACKPOINTERS 1
+#endif /* SYMBOLS_NEED_BACKPOINTERS */
+
+/* Symbol table entry data type */
+
+typedef struct
+{
+ struct internal_syment ost_entry; /* Basic symbol */
+ union internal_auxent ost_auxent[OBJ_COFF_MAX_AUXENTRIES]; /* Auxiliary entry. */
+
+ unsigned int ost_flags; /* obj_coff internal use only flags */
+} obj_symbol_type;
+
+#ifndef DO_NOT_STRIP
+#define DO_NOT_STRIP 0
+#define DO_STRIP 1
+#endif
+/* Symbol table macros and constants */
+
+/* Possible and usefull section number in symbol table
+ * The values of TEXT, DATA and BSS may not be portable.
+ */
+
+#define C_ABS_SECTION N_ABS
+#define C_UNDEF_SECTION N_UNDEF
+#define C_DEBUG_SECTION N_DEBUG
+#define C_NTV_SECTION N_TV
+#define C_PTV_SECTION P_TV
+#define C_REGISTER_SECTION 20
+
+/*
+ * Macros to extract information from a symbol table entry.
+ * This syntaxic indirection allows independence regarding a.out or coff.
+ * The argument (s) of all these macros is a pointer to a symbol table entry.
+ */
+
+/* Predicates */
+/* True if the symbol is external */
+#define S_IS_EXTERNAL(s) ((s)->sy_symbol.ost_entry.n_scnum == C_UNDEF_SECTION)
+/* True if symbol has been defined, ie :
+ section > 0 (DATA, TEXT or BSS)
+ section == 0 and value > 0 (external bss symbol) */
+#define S_IS_DEFINED(s) ((s)->sy_symbol.ost_entry.n_scnum > C_UNDEF_SECTION || \
+ ((s)->sy_symbol.ost_entry.n_scnum == C_UNDEF_SECTION && \
+ (s)->sy_symbol.ost_entry.n_value > 0))
+/* True if a debug special symbol entry */
+#define S_IS_DEBUG(s) ((s)->sy_symbol.ost_entry.n_scnum == C_DEBUG_SECTION)
+/* True if a symbol is local symbol name */
+/* A symbol name whose name begin with ^A is a gas internal pseudo symbol */
+#define S_IS_LOCAL(s) (S_GET_NAME(s)[0] == '\001' || \
+ (s)->sy_symbol.ost_entry.n_scnum == C_REGISTER_SECTION || \
+ (S_LOCAL_NAME(s) && !flagseen['L']))
+/* True if a symbol is not defined in this file */
+#define S_IS_EXTERN(s) ((s)->sy_symbol.ost_entry.n_scnum == 0 && (s)->sy_symbol.ost_entry.n_value == 0)
+/*
+ * True if a symbol can be multiply defined (bss symbols have this def
+ * though it is bad practice)
+ */
+#define S_IS_COMMON(s) ((s)->sy_symbol.ost_entry.n_scnum == 0 && (s)->sy_symbol.ost_entry.n_value != 0)
+/* True if a symbol name is in the string table, i.e. its length is > 8. */
+#define S_IS_STRING(s) (strlen(S_GET_NAME(s)) > 8 ? 1 : 0)
+
+/* Accessors */
+/* The name of the symbol */
+#define S_GET_NAME(s) ((char*)(s)->sy_symbol.ost_entry.n_offset)
+/* The pointer to the string table */
+#define S_GET_OFFSET(s) ((s)->sy_symbol.ost_entry.n_offset)
+/* The zeroes if symbol name is longer than 8 chars */
+#define S_GET_ZEROES(s) ((s)->sy_symbol.ost_entry.n_zeroes)
+/* The value of the symbol */
+#define S_GET_VALUE(s) ((unsigned) ((s)->sy_symbol.ost_entry.n_value))
+/* The numeric value of the segment */
+#define S_GET_SEGMENT(s) s_get_segment(s)
+/* The data type */
+#define S_GET_DATA_TYPE(s) ((s)->sy_symbol.ost_entry.n_type)
+/* The storage class */
+#define S_GET_STORAGE_CLASS(s) ((s)->sy_symbol.ost_entry.n_sclass)
+/* The number of auxiliary entries */
+#define S_GET_NUMBER_AUXILIARY(s) ((s)->sy_symbol.ost_entry.n_numaux)
+
+/* Modifiers */
+/* Set the name of the symbol */
+#define S_SET_NAME(s,v) ((s)->sy_symbol.ost_entry.n_offset = (unsigned long)(v))
+/* Set the offset of the symbol */
+#define S_SET_OFFSET(s,v) ((s)->sy_symbol.ost_entry.n_offset = (v))
+/* The zeroes if symbol name is longer than 8 chars */
+#define S_SET_ZEROES(s,v) ((s)->sy_symbol.ost_entry.n_zeroes = (v))
+/* Set the value of the symbol */
+#define S_SET_VALUE(s,v) ((s)->sy_symbol.ost_entry.n_value = (v))
+/* The numeric value of the segment */
+#define S_SET_SEGMENT(s,v) ((s)->sy_symbol.ost_entry.n_scnum = SEGMENT_TO_SYMBOL_TYPE(v))
+/* The data type */
+#define S_SET_DATA_TYPE(s,v) ((s)->sy_symbol.ost_entry.n_type = (v))
+/* The storage class */
+#define S_SET_STORAGE_CLASS(s,v) ((s)->sy_symbol.ost_entry.n_sclass = (v))
+/* The number of auxiliary entries */
+#define S_SET_NUMBER_AUXILIARY(s,v) ((s)->sy_symbol.ost_entry.n_numaux = (v))
+
+/* Additional modifiers */
+/* The symbol is external (does not mean undefined) */
+#define S_SET_EXTERNAL(s) { S_SET_STORAGE_CLASS(s, C_EXT) ; SF_CLEAR_LOCAL(s); }
+
+/* Auxiliary entry macros. SA_ stands for symbol auxiliary */
+/* Omit the tv related fields */
+/* Accessors */
+#ifdef BFD_HEADERS
+#define SA_GET_SYM_TAGNDX(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_tagndx.l)
+#else
+#define SA_GET_SYM_TAGNDX(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_tagndx)
+#endif
+#define SA_GET_SYM_LNNO(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_misc.x_lnsz.x_lnno)
+#define SA_GET_SYM_SIZE(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_misc.x_lnsz.x_size)
+#define SA_GET_SYM_FSIZE(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_misc.x_fsize)
+#define SA_GET_SYM_LNNOPTR(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_fcn.x_lnnoptr)
+#ifdef BFD_HEADERS
+#define SA_GET_SYM_ENDNDX(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_fcn.x_endndx.l)
+#else
+#define SA_GET_SYM_ENDNDX(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_fcn.x_endndx)
+#endif
+#define SA_GET_SYM_DIMEN(s,i) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_ary.x_dimen[(i)])
+#define SA_GET_FILE_FNAME(s) ((s)->sy_symbol.ost_auxent[0].x_file.x_fname)
+#define SA_GET_SCN_SCNLEN(s) ((s)->sy_symbol.ost_auxent[0].x_scn.x_scnlen)
+#define SA_GET_SCN_NRELOC(s) ((s)->sy_symbol.ost_auxent[0].x_scn.x_nreloc)
+#define SA_GET_SCN_NLINNO(s) ((s)->sy_symbol.ost_auxent[0].x_scn.x_nlinno)
+
+/* Modifiers */
+#ifdef BFD_HEADERS
+#define SA_SET_SYM_TAGNDX(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_tagndx.l=(v))
+#else
+#define SA_SET_SYM_TAGNDX(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_tagndx=(v))
+#endif
+#define SA_SET_SYM_LNNO(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_misc.x_lnsz.x_lnno=(v))
+#define SA_SET_SYM_SIZE(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_misc.x_lnsz.x_size=(v))
+#define SA_SET_SYM_FSIZE(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_misc.x_fsize=(v))
+#define SA_SET_SYM_LNNOPTR(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_fcn.x_lnnoptr=(v))
+#ifdef BFD_HEADERS
+#define SA_SET_SYM_ENDNDX(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_fcn.x_endndx.l=(v))
+#else
+#define SA_SET_SYM_ENDNDX(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_fcn.x_endndx=(v))
+#endif
+#define SA_SET_SYM_DIMEN(s,i,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_ary.x_dimen[(i)]=(v))
+#define SA_SET_FILE_FNAME(s,v) strncpy((s)->sy_symbol.ost_auxent[0].x_file.x_fname,(v),FILNMLEN)
+#define SA_SET_SCN_SCNLEN(s,v) ((s)->sy_symbol.ost_auxent[0].x_scn.x_scnlen=(v))
+#define SA_SET_SCN_NRELOC(s,v) ((s)->sy_symbol.ost_auxent[0].x_scn.x_nreloc=(v))
+#define SA_SET_SCN_NLINNO(s,v) ((s)->sy_symbol.ost_auxent[0].x_scn.x_nlinno=(v))
+
+/*
+ * Internal use only definitions. SF_ stands for symbol flags.
+ *
+ * These values can be assigned to sy_symbol.ost_flags field of a symbolS.
+ *
+ * You'll break i960 if you shift the SYSPROC bits anywhere else. for
+ * more on the balname/callname hack, see tc-i960.h. b.out is done
+ * differently.
+ */
+
+#define SF_I960_MASK (0x000001ff) /* Bits 0-8 are used by the i960 port. */
+#define SF_SYSPROC (0x0000003f) /* bits 0-5 are used to store the sysproc number */
+#define SF_IS_SYSPROC (0x00000040) /* bit 6 marks symbols that are sysprocs */
+#define SF_BALNAME (0x00000080) /* bit 7 marks BALNAME symbols */
+#define SF_CALLNAME (0x00000100) /* bit 8 marks CALLNAME symbols */
+
+#define SF_NORMAL_MASK (0x0000ffff) /* bits 12-15 are general purpose. */
+
+#define SF_STATICS (0x00001000) /* Mark the .text & all symbols */
+#define SF_DEFINED (0x00002000) /* Symbol is defined in this file */
+#define SF_STRING (0x00004000) /* Symbol name length > 8 */
+#define SF_LOCAL (0x00008000) /* Symbol must not be emitted */
+
+#define SF_DEBUG_MASK (0xffff0000) /* bits 16-31 are debug info */
+
+#define SF_FUNCTION (0x00010000) /* The symbol is a function */
+#define SF_PROCESS (0x00020000) /* Process symbol before write */
+#define SF_TAGGED (0x00040000) /* Is associated with a tag */
+#define SF_TAG (0x00080000) /* Is a tag */
+#define SF_DEBUG (0x00100000) /* Is in debug or abs section */
+#define SF_GET_SEGMENT (0x00200000) /* Get the section of the forward symbol. */
+/* All other bits are unused. */
+
+/* Accessors */
+#define SF_GET(s) ((s)->sy_symbol.ost_flags)
+#define SF_GET_NORMAL_FIELD(s) ((s)->sy_symbol.ost_flags & SF_NORMAL_MASK)
+#define SF_GET_DEBUG_FIELD(s) ((s)->sy_symbol.ost_flags & SF_DEBUG_MASK)
+#define SF_GET_FILE(s) ((s)->sy_symbol.ost_flags & SF_FILE)
+#define SF_GET_STATICS(s) ((s)->sy_symbol.ost_flags & SF_STATICS)
+#define SF_GET_DEFINED(s) ((s)->sy_symbol.ost_flags & SF_DEFINED)
+#define SF_GET_STRING(s) ((s)->sy_symbol.ost_flags & SF_STRING)
+#define SF_GET_LOCAL(s) ((s)->sy_symbol.ost_flags & SF_LOCAL)
+#define SF_GET_FUNCTION(s) ((s)->sy_symbol.ost_flags & SF_FUNCTION)
+#define SF_GET_PROCESS(s) ((s)->sy_symbol.ost_flags & SF_PROCESS)
+#define SF_GET_DEBUG(s) ((s)->sy_symbol.ost_flags & SF_DEBUG)
+#define SF_GET_TAGGED(s) ((s)->sy_symbol.ost_flags & SF_TAGGED)
+#define SF_GET_TAG(s) ((s)->sy_symbol.ost_flags & SF_TAG)
+#define SF_GET_GET_SEGMENT(s) ((s)->sy_symbol.ost_flags & SF_GET_SEGMENT)
+#define SF_GET_I960(s) ((s)->sy_symbol.ost_flags & SF_I960_MASK) /* used by i960 */
+#define SF_GET_BALNAME(s) ((s)->sy_symbol.ost_flags & SF_BALNAME) /* used by i960 */
+#define SF_GET_CALLNAME(s) ((s)->sy_symbol.ost_flags & SF_CALLNAME) /* used by i960 */
+#define SF_GET_IS_SYSPROC(s) ((s)->sy_symbol.ost_flags & SF_IS_SYSPROC) /* used by i960 */
+#define SF_GET_SYSPROC(s) ((s)->sy_symbol.ost_flags & SF_SYSPROC) /* used by i960 */
+
+/* Modifiers */
+#define SF_SET(s,v) ((s)->sy_symbol.ost_flags = (v))
+#define SF_SET_NORMAL_FIELD(s,v)((s)->sy_symbol.ost_flags |= ((v) & SF_NORMAL_MASK))
+#define SF_SET_DEBUG_FIELD(s,v) ((s)->sy_symbol.ost_flags |= ((v) & SF_DEBUG_MASK))
+#define SF_SET_FILE(s) ((s)->sy_symbol.ost_flags |= SF_FILE)
+#define SF_SET_STATICS(s) ((s)->sy_symbol.ost_flags |= SF_STATICS)
+#define SF_SET_DEFINED(s) ((s)->sy_symbol.ost_flags |= SF_DEFINED)
+#define SF_SET_STRING(s) ((s)->sy_symbol.ost_flags |= SF_STRING)
+#define SF_SET_LOCAL(s) ((s)->sy_symbol.ost_flags |= SF_LOCAL)
+#define SF_CLEAR_LOCAL(s) ((s)->sy_symbol.ost_flags &= ~SF_LOCAL)
+#define SF_SET_FUNCTION(s) ((s)->sy_symbol.ost_flags |= SF_FUNCTION)
+#define SF_SET_PROCESS(s) ((s)->sy_symbol.ost_flags |= SF_PROCESS)
+#define SF_SET_DEBUG(s) ((s)->sy_symbol.ost_flags |= SF_DEBUG)
+#define SF_SET_TAGGED(s) ((s)->sy_symbol.ost_flags |= SF_TAGGED)
+#define SF_SET_TAG(s) ((s)->sy_symbol.ost_flags |= SF_TAG)
+#define SF_SET_GET_SEGMENT(s) ((s)->sy_symbol.ost_flags |= SF_GET_SEGMENT)
+#define SF_SET_I960(s,v) ((s)->sy_symbol.ost_flags |= ((v) & SF_I960_MASK)) /* used by i960 */
+#define SF_SET_BALNAME(s) ((s)->sy_symbol.ost_flags |= SF_BALNAME) /* used by i960 */
+#define SF_SET_CALLNAME(s) ((s)->sy_symbol.ost_flags |= SF_CALLNAME) /* used by i960 */
+#define SF_SET_IS_SYSPROC(s) ((s)->sy_symbol.ost_flags |= SF_IS_SYSPROC) /* used by i960 */
+#define SF_SET_SYSPROC(s,v) ((s)->sy_symbol.ost_flags |= ((v) & SF_SYSPROC)) /* used by i960 */
+
+/* File header macro and type definition */
+
+/*
+ * File position calculators. Beware to use them when all the
+ * appropriate fields are set in the header.
+ */
+
+#ifdef OBJ_COFF_OMIT_OPTIONAL_HEADER
+#define OBJ_COFF_AOUTHDRSZ (0)
+#else
+#define OBJ_COFF_AOUTHDRSZ (AOUTHDRSZ)
+#endif /* OBJ_COFF_OMIT_OPTIONAL_HEADER */
+
+#define H_GET_FILE_SIZE(h) \
+ (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \
+ H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \
+ H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h) + \
+ H_GET_RELOCATION_SIZE(h) + H_GET_LINENO_SIZE(h) + \
+ H_GET_SYMBOL_TABLE_SIZE(h) + \
+ (h)->string_table_size)
+#define H_GET_TEXT_FILE_OFFSET(h) \
+ (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \
+ H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ)
+#define H_GET_DATA_FILE_OFFSET(h) \
+ (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \
+ H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \
+ H_GET_TEXT_SIZE(h))
+#define H_GET_BSS_FILE_OFFSET(h) 0
+#define H_GET_RELOCATION_FILE_OFFSET(h) \
+ (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \
+ H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \
+ H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h))
+#define H_GET_LINENO_FILE_OFFSET(h) \
+ (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \
+ H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \
+ H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h) + \
+ H_GET_RELOCATION_SIZE(h))
+#define H_GET_SYMBOL_TABLE_FILE_OFFSET(h) \
+ (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \
+ H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \
+ H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h) + \
+ H_GET_RELOCATION_SIZE(h) + H_GET_LINENO_SIZE(h))
+
+/* Accessors */
+/* aouthdr */
+#define H_GET_MAGIC_NUMBER(h) ((h)->aouthdr.magic)
+#define H_GET_VERSION_STAMP(h) ((h)->aouthdr.vstamp)
+#define H_GET_TEXT_SIZE(h) ((h)->aouthdr.tsize)
+#define H_GET_DATA_SIZE(h) ((h)->aouthdr.dsize)
+#define H_GET_BSS_SIZE(h) ((h)->aouthdr.bsize)
+#define H_GET_ENTRY_POINT(h) ((h)->aouthdr.entry)
+#define H_GET_TEXT_START(h) ((h)->aouthdr.text_start)
+#define H_GET_DATA_START(h) ((h)->aouthdr.data_start)
+/* filehdr */
+#define H_GET_FILE_MAGIC_NUMBER(h) ((h)->filehdr.f_magic)
+#define H_GET_NUMBER_OF_SECTIONS(h) ((h)->filehdr.f_nscns)
+#define H_GET_TIME_STAMP(h) ((h)->filehdr.f_timdat)
+#define H_GET_SYMBOL_TABLE_POINTER(h) ((h)->filehdr.f_symptr)
+#define H_GET_SYMBOL_COUNT(h) ((h)->filehdr.f_nsyms)
+#define H_GET_SYMBOL_TABLE_SIZE(h) (H_GET_SYMBOL_COUNT(h) * SYMESZ)
+#define H_GET_SIZEOF_OPTIONAL_HEADER(h) ((h)->filehdr.f_opthdr)
+#define H_GET_FLAGS(h) ((h)->filehdr.f_flags)
+/* Extra fields to achieve bsd a.out compatibility and for convenience */
+#define H_GET_RELOCATION_SIZE(h) ((h)->relocation_size)
+#define H_GET_STRING_SIZE(h) ((h)->string_table_size)
+#define H_GET_LINENO_SIZE(h) ((h)->lineno_size)
+
+#ifndef OBJ_COFF_OMIT_OPTIONAL_HEADER
+#define H_GET_HEADER_SIZE(h) (sizeof(FILHDR) \
+ + sizeof(AOUTHDR)\
+ + (H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ))
+#else /* OBJ_COFF_OMIT_OPTIONAL_HEADER */
+#define H_GET_HEADER_SIZE(h) (sizeof(FILHDR) \
+ + (H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ))
+#endif /* OBJ_COFF_OMIT_OPTIONAL_HEADER */
+
+#define H_GET_TEXT_RELOCATION_SIZE(h) (text_section_header.s_nreloc * RELSZ)
+#define H_GET_DATA_RELOCATION_SIZE(h) (data_section_header.s_nreloc * RELSZ)
+
+/* Modifiers */
+/* aouthdr */
+#define H_SET_MAGIC_NUMBER(h,v) ((h)->aouthdr.magic = (v))
+#define H_SET_VERSION_STAMP(h,v) ((h)->aouthdr.vstamp = (v))
+#define H_SET_TEXT_SIZE(h,v) ((h)->aouthdr.tsize = (v))
+#define H_SET_DATA_SIZE(h,v) ((h)->aouthdr.dsize = (v))
+#define H_SET_BSS_SIZE(h,v) ((h)->aouthdr.bsize = (v))
+#define H_SET_ENTRY_POINT(h,v) ((h)->aouthdr.entry = (v))
+#define H_SET_TEXT_START(h,v) ((h)->aouthdr.text_start = (v))
+#define H_SET_DATA_START(h,v) ((h)->aouthdr.data_start = (v))
+/* filehdr */
+#define H_SET_FILE_MAGIC_NUMBER(h,v) ((h)->filehdr.f_magic = (v))
+#define H_SET_NUMBER_OF_SECTIONS(h,v) ((h)->filehdr.f_nscns = (v))
+#define H_SET_TIME_STAMP(h,v) ((h)->filehdr.f_timdat = (v))
+#define H_SET_SYMBOL_TABLE_POINTER(h,v) ((h)->filehdr.f_symptr = (v))
+#define H_SET_SYMBOL_TABLE_SIZE(h,v) ((h)->filehdr.f_nsyms = (v))
+#define H_SET_SIZEOF_OPTIONAL_HEADER(h,v) ((h)->filehdr.f_opthdr = (v))
+#define H_SET_FLAGS(h,v) ((h)->filehdr.f_flags = (v))
+/* Extra fields to achieve bsd a.out compatibility and for convinience */
+#define H_SET_RELOCATION_SIZE(h,t,d) ((h)->relocation_size = (t)+(d))
+#define H_SET_STRING_SIZE(h,v) ((h)->string_table_size = (v))
+#define H_SET_LINENO_SIZE(h,v) ((h)->lineno_size = (v))
+
+/* Segment flipping */
+#define segment_name(v) (seg_name[(int) (v)])
+
+typedef struct {
+#ifdef BFD_HEADERS
+ struct internal_aouthdr aouthdr; /* a.out header */
+ struct internal_filehdr filehdr; /* File header, not machine dep. */
+#else
+ AOUTHDR aouthdr; /* a.out header */
+ FILHDR filehdr; /* File header, not machine dep. */
+#endif
+ long string_table_size; /* names + '\0' + sizeof(int) */
+ long relocation_size; /* Cumulated size of relocation
+ information for all sections in
+ bytes. */
+ long lineno_size; /* Size of the line number information
+ table in bytes */
+} object_headers;
+
+
+
+struct lineno_list
+{
+
+ struct bfd_internal_lineno line;
+ char* frag; /* Frag to which the line number is related */
+ struct lineno_list* next; /* Forward chain pointer */
+} ;
+
+
+
+
+/* stack stuff */
+typedef struct {
+ unsigned long chunk_size;
+ unsigned long element_size;
+ unsigned long size;
+ char* data;
+ unsigned long pointer;
+} stack;
+
+
+
+char *EXFUN(stack_pop,(stack *st));
+char *EXFUN(stack_push,(stack *st, char *element));
+char *EXFUN(stack_top,(stack *st));
+stack *EXFUN(stack_init,(unsigned long chunk_size, unsigned long element_size));
+void EXFUN(c_dot_file_symbol,(char *filename));
+void EXFUN(obj_extra_stuff,(object_headers *headers));
+void EXFUN(stack_delete,(stack *st));
+
+
+
+void EXFUN(c_section_header,(
+
+ struct internal_scnhdr *header,
+ char *name,
+ long core_address,
+ long size,
+ long data_ptr,
+ long reloc_ptr,
+ long lineno_ptr,
+ long reloc_number,
+ long lineno_number,
+ long alignment));
+
+
+/* sanity check */
+
+#ifdef TC_I960
+#ifndef C_LEAFSTAT
+hey! Where is the C_LEAFSTAT definition? i960-coff support is depending on it.
+#endif /* no C_LEAFSTAT */
+#endif /* TC_I960 */
+#ifdef BFD_HEADERS
+ extern struct internal_scnhdr data_section_header;
+extern struct internal_scnhdr text_section_header;
+#else
+extern SCNHDR data_section_header;
+extern SCNHDR text_section_header;
+#endif
+#endif
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of obj-coffbfd.h */
diff --git a/gnu/usr.bin/as/config/obj-generic.c b/gnu/usr.bin/as/config/obj-generic.c
new file mode 100644
index 0000000..a91eff9
--- /dev/null
+++ b/gnu/usr.bin/as/config/obj-generic.c
@@ -0,0 +1,41 @@
+/* This file is obj-generic.c and is intended to be a template for
+ object format specific source files.
+
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* These chars start a comment anywhere in a source file (except inside
+ another comment */
+const char comment_chars[] = "#";
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of obj-generic.c */
diff --git a/gnu/usr.bin/as/config/obj-generic.h b/gnu/usr.bin/as/config/obj-generic.h
new file mode 100644
index 0000000..f370722
--- /dev/null
+++ b/gnu/usr.bin/as/config/obj-generic.h
@@ -0,0 +1,78 @@
+/* This file is obj-generic.h
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * This file is obj-generic.h and is intended to be a template for
+ * object format specific header files.
+ */
+
+/* define an obj specific macro off which target cpu back ends may key. */
+#define OBJ_GENERIC 1
+
+/* include whatever target cpu is appropriate. */
+#include "targ-cpu.h"
+
+/*
+ * SYMBOLS
+ */
+
+/*
+ * If your object format needs to reorder symbols, define this. When
+ * defined, symbols are kept on a doubly linked list and functions are
+ * made available for push, insert, append, and delete. If not defined,
+ * symbols are kept on a singly linked list, only the append and clear
+ * facilities are available, and they are macros.
+ */
+
+/* #define SYMBOLS_NEED_PACKPOINTERS */
+
+/* */
+typedef struct {
+ void *nothing;
+} obj_symbol_type; /* should be the format's symbol structure */
+
+typedef void *object_headers;
+
+/* symbols have names */
+#define S_GET_NAME(s) ("foo") /* get the name of a symbolP */
+#define S_SET_NAME(s,v) ;
+ /* symbols have segments */
+#define S_GET_SEGMENT(s) (SEG_UNKNOWN)
+#define S_SET_SEGMENT(s,v) ;
+ /* symbols have a value */
+#define S_GET_VALUE(s) (0)
+#define S_SET_VALUE(s,v) ;
+ /* symbols may be external */
+#define S_IS_EXTERNAL(s) (0)
+#define S_SET_EXTERNAL(s) ;
+
+ /* symbols may or may not be defined */
+#define S_IS_DEFINED(s) (0)
+
+
+#define OBJ_EMIT_LINENO(a,b,c) /* must be *something*. This no-op's it out. */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of obj-generic.h */
diff --git a/gnu/usr.bin/as/config/obj-ieee.c b/gnu/usr.bin/as/config/obj-ieee.c
new file mode 100644
index 0000000..5f74a5f
--- /dev/null
+++ b/gnu/usr.bin/as/config/obj-ieee.c
@@ -0,0 +1,539 @@
+/* obj-format for ieee-695 records.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/*
+ created by
+
+ steve chamberlain steve@cygnus.com
+ */
+
+/*
+ this will hopefully become the port through which bfd and gas talk,
+ for the moment, only ieee is known to work well.
+ */
+
+#include "bfd.h"
+#include "as.h"
+#include "subsegs.h"
+#include "output-file.h"
+#include "frags.h"
+
+bfd *abfd;
+
+/* How many addresses does the .align take? */
+static relax_addressT relax_align(address, alignment)
+register relax_addressT address; /* Address now. */
+register long alignment; /* Alignment (binary). */
+{
+ relax_addressT mask;
+ relax_addressT new_address;
+
+ mask = ~ ( (~0) << alignment );
+ new_address = (address + mask) & (~ mask);
+ return (new_address - address);
+} /* relax_align() */
+
+/* calculate the size of the frag chain and create a bfd section
+ to contain all of it */
+static void DEFUN(size_section,(abfd, idx),
+ bfd *abfd AND
+ unsigned int idx)
+{
+ asection *sec;
+ unsigned int size = 0;
+ fragS *frag = segment_info[idx].frag_root;
+ while (frag) {
+ if (frag->fr_address != size) {
+ printf("Out of step\n");
+ size = frag->fr_address;
+ }
+ size += frag->fr_fix;
+ switch (frag->fr_type) {
+ case rs_fill:
+ case rs_org:
+ size += frag->fr_offset * frag->fr_var;
+ break;
+ case rs_align:
+ size += relax_align(size, frag->fr_offset);
+ }
+ frag = frag->fr_next;
+ }
+ if (size) {
+ char *name = segment_info[idx].name;
+ if (name == (char *)NULL) {
+ name = ".data";
+ }
+ segment_info[idx].user_stuff = (char *)(sec = bfd_make_section(abfd, name));
+ /* Make it output through itself */
+ sec->output_section = sec;
+ sec->flags |= SEC_HAS_CONTENTS;
+ bfd_set_section_size(abfd, sec, size);
+ }
+}
+
+/* run through a frag chain and write out the data to go with it */
+static void DEFUN(fill_section,(abfd, idx),
+ bfd *abfd AND
+ unsigned int idx)
+{
+ asection *sec = segment_info[idx].user_stuff;
+ if (sec) {
+ fragS *frag = segment_info[idx].frag_root;
+ unsigned int offset = 0;
+ while (frag) {
+ unsigned int fill_size;
+ unsigned int count;
+ switch (frag->fr_type) {
+ case rs_fill:
+ case rs_align:
+ case rs_org:
+ if (frag->fr_fix)
+ {
+ bfd_set_section_contents(abfd,
+ sec,
+ frag->fr_literal,
+ frag->fr_address,
+ frag->fr_fix);
+ }
+ offset += frag->fr_fix;
+ fill_size = frag->fr_var;
+ if (fill_size)
+ {
+ unsigned int off = frag->fr_fix;
+ for (count = frag->fr_offset; count; count--)
+ {
+ bfd_set_section_contents(abfd, sec,
+ frag->fr_literal +
+ frag->fr_fix,
+ frag->fr_address + off,
+ fill_size);
+ off += fill_size;
+ }
+ }
+ break;
+ default:
+ abort();
+ }
+ frag = frag->fr_next;
+ }
+ }
+}
+
+/* Count the relocations in a chain */
+
+static unsigned int DEFUN(count_entries_in_chain,(idx),
+ unsigned int idx)
+{
+ unsigned int nrelocs;
+ fixS *fixup_ptr;
+
+ /* Count the relocations */
+ fixup_ptr = segment_info[idx].fix_root;
+ nrelocs = 0;
+ while (fixup_ptr != (fixS *)NULL)
+ {
+ fixup_ptr = fixup_ptr->fx_next;
+ nrelocs ++ ;
+ }
+ return nrelocs;
+}
+
+/* output all the relocations for a section */
+void DEFUN(do_relocs_for,(idx),
+ unsigned int idx)
+{
+ unsigned int nrelocs;
+ arelent **reloc_ptr_vector;
+ arelent *reloc_vector;
+ asymbol **ptrs;
+ asection *section = (asection *)(segment_info[idx].user_stuff);
+ unsigned int i;
+ fixS *from;
+ if (section) {
+ nrelocs = count_entries_in_chain(idx);
+
+ reloc_ptr_vector = (arelent**)malloc((nrelocs+1) * sizeof(arelent *));
+ reloc_vector = (arelent*)malloc(nrelocs * sizeof(arelent));
+ ptrs = (asymbol **)malloc(nrelocs * sizeof(asymbol *));
+ from = segment_info[idx].fix_root;
+ for (i = 0; i < nrelocs; i++)
+ {
+ arelent *to = reloc_vector + i;
+ asymbol *s ;
+ reloc_ptr_vector[i] = to;
+ to->howto = (reloc_howto_type *)(from->fx_r_type);
+
+ /* We can't represent complicated things in a reloc yet */
+ /* if (from->fx_addsy == 0 ||
+ from->fx_subsy != 0) abort();
+ */
+ s = &( from->fx_addsy->sy_symbol.sy);
+ to->address = ((char *)( from->fx_frag->fr_address +
+ from->fx_where))
+ - ((char *)(&(from->fx_frag->fr_literal)));
+ to->addend = from->fx_offset ;
+ /* If we know the symbol which we want to relocate to, turn this
+ reloaction into a section relative.
+
+ If this relocation is pcrelative, and we know the
+ destination, we still want to keep the relocation - since
+ the linker might relax some of the bytes, but it stops
+ being pc relative and turns into an absolute relocation.
+
+ */
+ if (s) {
+ if ((s->flags & BSF_UNDEFINED) == 0) {
+ to->section = s->section;
+ to->addend += s->value ;
+ to->sym_ptr_ptr = 0;
+ if (to->howto->pcrel_offset) {
+ /* This is a pcrel relocation, the addend should be adjusted */
+ to->addend -= to->address +1;
+ }
+ }
+ else {
+ to->section = 0;
+ *ptrs = &(from->fx_addsy->sy_symbol.sy);
+ to->sym_ptr_ptr = ptrs;
+
+ if (to->howto->pcrel_offset) {
+ /* This is a pcrel relocation, the addend should be adjusted */
+ to->addend -= to->address -1;
+ }
+ }
+
+ }
+ else {
+ to->section = 0;
+ }
+
+ ptrs++;
+ from = from->fx_next;
+ }
+
+ /* attatch to the section */
+ section->orelocation = reloc_ptr_vector;
+ section->reloc_count = nrelocs;
+ section->flags |= SEC_LOAD;
+ }
+}
+
+/* do the symbols.. */
+static void DEFUN(do_symbols, (abfd),
+ bfd *abfd)
+{
+ extern symbolS *symbol_rootP;
+ symbolS *ptr;
+ asymbol **symbol_ptr_vec;
+ asymbol *symbol_vec;
+ unsigned int count = 0;
+ unsigned int index;
+
+
+ for (ptr = symbol_rootP;
+ ptr != (symbolS *)NULL;
+ ptr = ptr->sy_next)
+ {
+ if (SEG_NORMAL(ptr->sy_symbol.seg))
+ {
+ ptr->sy_symbol.sy.section =
+ (asection *)(segment_info[ptr->sy_symbol.seg].user_stuff);
+ ptr->sy_symbol.sy.value += ptr->sy_frag->fr_address;
+ if (ptr->sy_symbol.sy.flags == 0) {
+ ptr->sy_symbol.sy.flags = BSF_LOCAL ;
+ }
+ }
+ else {
+ switch (ptr->sy_symbol.seg) {
+ case SEG_ABSOLUTE:
+ ptr->sy_symbol.sy.flags |= BSF_ABSOLUTE;
+ ptr->sy_symbol.sy.section = 0;
+ break;
+ case SEG_UNKNOWN:
+ ptr->sy_symbol.sy.flags = BSF_UNDEFINED ;
+ ptr->sy_symbol.sy.section = 0;
+ break;
+ default:
+ abort();
+ }
+ }
+ count++;
+ }
+ symbol_ptr_vec = (asymbol **)malloc((count+1) * sizeof(asymbol *));
+
+ index = 0;
+ for (ptr = symbol_rootP;
+ ptr != (symbolS *)NULL;
+ ptr = ptr->sy_next)
+ {
+ symbol_ptr_vec[index] = &(ptr->sy_symbol.sy);
+ index++;
+ }
+ symbol_ptr_vec[index] =0;
+ abfd->outsymbols = symbol_ptr_vec;
+ abfd->symcount = count;
+}
+
+/* The generic as->bfd converter. Other backends may have special case
+ code */
+
+void DEFUN_VOID(bfd_as_write_hook)
+{
+ int i;
+
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++) {
+ size_section(abfd, i);
+ }
+
+
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ fill_section(abfd,i);
+
+ do_symbols(abfd);
+
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ do_relocs_for(i);
+
+}
+
+
+
+S_GET_VALUE(x)
+symbolS *x;
+{
+ return x->sy_symbol.sy.value;
+}
+
+S_SET_SEGMENT(x,y)
+symbolS *x ;
+int y;
+{
+ x->sy_symbol.seg = y;
+}
+
+S_IS_DEFINED(x)
+symbolS *x;
+{
+ if (SEG_NORMAL(x->sy_symbol.seg))
+ {
+ return 1;
+ }
+ switch (x->sy_symbol.seg)
+ {
+ case SEG_UNKNOWN:
+ return 0;
+ default:
+ abort();
+ }
+}
+
+S_IS_EXTERNAL(x) { abort(); }
+S_GET_DESC(x) { abort() ; }
+
+S_GET_SEGMENT(x)
+symbolS *x;
+{ return x->sy_symbol.seg; }
+
+S_SET_EXTERNAL(x)
+symbolS *x;
+{
+ x->sy_symbol.sy.flags |= BSF_GLOBAL | BSF_EXPORT;
+}
+
+S_SET_NAME(x,y)
+symbolS*x;
+char *y; {
+ x->sy_symbol.sy.name = y; }
+
+S_SET_VALUE(s,v)
+symbolS *s;
+long v;
+{
+ s->sy_symbol.sy.value = v;
+}
+
+S_GET_OTHER(x) { abort() ;}
+S_IS_DEBUG(x) { abort(); }
+
+char *segment_name() { abort(); }
+
+void obj_read_begin_hook() { }
+
+static void obj_ieee_section(ignore)
+int ignore;
+{
+ extern char *input_line_pointer;
+ extern char is_end_of_line[];
+ char *p= input_line_pointer;
+ char *s = p;
+ int i;
+ /* Look up the name, if it doesn't exist, make it */
+ while (*p &&* p != ' ' && *p != ',' && !is_end_of_line[*p]) {
+ p++;
+ }
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++) {
+ if (segment_info[i].hadone){
+ if (strncmp(segment_info[i].name, s, p-s) == 0) {
+ goto ok;
+
+ }
+ }
+ else break;
+ }
+ if (i == SEG_UNKNOWN) {
+ as_bad("too many sections");
+ return;
+ }
+
+ segment_info[i].hadone = 1;
+ segment_info[i].name = malloc(p-s + 1);
+ memcpy(segment_info[i].name, s, p-s);
+ segment_info[i].name[p-s] = 0;
+ ok:
+ subseg_new(i,0);
+ while (!is_end_of_line[*p])
+ p++;
+ input_line_pointer = p;
+
+}
+
+
+void cons();
+void s_ignore();
+
+
+/*
+ * stringer()
+ *
+ * We read 0 or more ',' seperated, double-quoted strings.
+ *
+ * Caller should have checked need_pass_2 is FALSE because we don't check it.
+ */
+
+void stringer();
+void s_globl();
+const pseudo_typeS obj_pseudo_table[] =
+{
+ {"section", obj_ieee_section, 0},
+ {"data.b", cons, 1},
+ {"data.w", cons, 2},
+ {"data.l", cons, 4},
+ {"export", s_globl, 0},
+ {"option", s_ignore, 0},
+ {"end", s_ignore, 0},
+ {"import", s_ignore, 0},
+ {"sdata", stringer, 0},
+ 0,
+
+};
+
+
+
+void obj_symbol_new_hook(symbolP)
+symbolS *symbolP;
+{
+ symbolP->sy_symbol.sy.the_bfd = abfd;
+}
+
+
+
+
+
+#if 1
+extern void DEFUN_VOID(write_object_file)
+{
+ int i;
+ struct frchain *frchain_ptr;
+ struct frag *frag_ptr;
+
+ abfd = bfd_openw(out_file_name, "ieee");
+
+ if (abfd == 0) {
+ as_perror ("FATAL: Can't create %s", out_file_name);
+ exit(42);
+ }
+ bfd_set_format(abfd, bfd_object);
+ bfd_set_arch_mach(abfd, bfd_arch_h8300, 0);
+ subseg_new(1,0);
+ subseg_new(2,0);
+ subseg_new(3,0);
+ for (frchain_ptr = frchain_root;
+ frchain_ptr != (struct frchain *)NULL;
+ frchain_ptr = frchain_ptr->frch_next) {
+ /* Run through all the sub-segments and align them up. Also close any
+ open frags. We tack a .fill onto the end of the frag chain so
+ that any .align's size can be worked by looking at the next
+ frag */
+
+ subseg_new(frchain_ptr->frch_seg, frchain_ptr->frch_subseg);
+#define SUB_SEGMENT_ALIGN 2
+ frag_align(SUB_SEGMENT_ALIGN,0);
+ frag_wane(frag_now);
+ frag_now->fr_fix = 0;
+ know( frag_now->fr_next == NULL );
+ }
+
+ /* Now build one big frag chain for each segment, linked through
+ fr_next. */
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ {
+
+ fragS ** prev_frag_ptr_ptr ;
+ struct frchain *next_frchain_ptr;
+
+ /* struct frag **head_ptr = segment_info[i].frag_root;*/
+
+ segment_info[i].frag_root = segment_info[i].frchainP->frch_root;
+#if 0
+ /* Im not sure what this is for */
+ for (frchain_ptr = segment_info[i].frchainP->frch_root;
+ frchain_ptr != (struct frchain *)NULL;
+ frchain_ptr = frchain_ptr->frch_next)
+ {
+ *head_ptr = frchain_ptr;
+ head_ptr = &frchain_ptr->next;
+ }
+
+
+#endif
+ }
+
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++) {
+ relax_segment(segment_info[i].frag_root, i);
+ }
+
+ /* Now the addresses of the frags are correct within the segment */
+
+ bfd_as_write_hook();
+ bfd_close(abfd);
+}
+
+#endif
+
+H_SET_TEXT_SIZE(a,b) { abort(); }
+H_GET_TEXT_SIZE() { abort(); }
+H_SET_BSS_SIZE() { abort(); }
+H_SET_STRING_SIZE() { abort(); }
+H_SET_RELOCATION_SIZE() { abort(); }
+H_SET_MAGIC_NUMBER() { abort(); }
+H_GET_FILE_SIZE() { abort(); }
+H_GET_TEXT_RELOCATION_SIZE() { abort(); }
+
+/* end of obj-ieee.c */
diff --git a/gnu/usr.bin/as/config/obj-ieee.h b/gnu/usr.bin/as/config/obj-ieee.h
new file mode 100644
index 0000000..3baa081
--- /dev/null
+++ b/gnu/usr.bin/as/config/obj-ieee.h
@@ -0,0 +1,46 @@
+/* This file is obj-ieee.h
+
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define BFD 1
+
+#include <bfd.h>
+
+typedef struct
+{
+asymbol sy;
+int seg;
+} obj_symbol_type;
+
+#define S_GET_NAME(s) (((s)->sy_symbol.sy.name))
+
+typedef struct {
+int x;
+}
+object_headers;
+
+#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE 1
+
+
+int lineno_rootP;
+
+
+#define IEEE_STYLE
+
+/* end of obj-ieee.h */
diff --git a/gnu/usr.bin/as/config/obj-vms.c b/gnu/usr.bin/as/config/obj-vms.c
new file mode 100644
index 0000000..5d12387
--- /dev/null
+++ b/gnu/usr.bin/as/config/obj-vms.c
@@ -0,0 +1,5484 @@
+/* vms.c -- Write out a VAX/VMS object file
+ Copyright (C) 1987, 1988, 1992 Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by David L. Kashtan */
+/* Modified by Eric Youngdale to write VMS debug records for program
+ variables */
+#include "as.h"
+#include "subsegs.h"
+#include "obstack.h"
+
+/* What we do if there is a goof. */
+#define error as_fatal
+
+#ifdef HO_VMS /* These are of no use if we are cross assembling. */
+#include <fab.h> /* Define File Access Block */
+#include <nam.h> /* Define NAM Block */
+#include <xab.h> /* Define XAB - all different types*/
+#endif
+/*
+ * Version string of the compiler that produced the code we are
+ * assembling. (And this assembler, if we do not have compiler info.)
+ */
+extern const char version_string[];
+char *compiler_version_string;
+
+/* Flag that determines how we map names. This takes several values, and
+ * is set with the -h switch. A value of zero implies names should be
+ * upper case, and the presence of the -h switch inhibits the case hack.
+ * No -h switch at all sets vms_name_mapping to 0, and allows case hacking.
+ * A value of 2 (set with -h2) implies names should be
+ * all lower case, with no case hack. A value of 3 (set with -h3) implies
+ * that case should be preserved. */
+
+/* If the -+ switch is given, then the hash is appended to any name that is
+ * longer than 31 characters, irregardless of the setting of the -h switch.
+ */
+
+char vms_name_mapping = 0;
+
+
+extern char *strchr ();
+extern char *myname;
+static symbolS *Entry_Point_Symbol = 0; /* Pointer to "_main" */
+
+/*
+ * We augment the "gas" symbol structure with this
+ */
+struct VMS_Symbol
+{
+ struct VMS_Symbol *Next;
+ struct symbol *Symbol;
+ int Size;
+ int Psect_Index;
+ int Psect_Offset;
+};
+struct VMS_Symbol *VMS_Symbols = 0;
+
+/* We need this to keep track of the various input files, so that we can
+ * give the debugger the correct source line.
+ */
+
+struct input_file
+{
+ struct input_file *next;
+ struct input_file *same_file_fpnt;
+ int file_number;
+ int max_line;
+ int min_line;
+ int offset;
+ char flag;
+ char *name;
+ symbolS *spnt;
+};
+
+static struct input_file *file_root = (struct input_file *) NULL;
+
+
+static struct input_file *find_file (symbolS *);
+
+/*
+ * This enum is used to keep track of the various types of variables that
+ * may be present.
+ */
+
+enum advanced_type
+{
+ BASIC, POINTER, ARRAY, ENUM, STRUCT, UNION, FUNCTION, VOID, UNKNOWN
+};
+
+/*
+ * This structure contains the information from the stabs directives, and the
+ * information is filled in by VMS_typedef_parse. Everything that is needed
+ * to generate the debugging record for a given symbol is present here.
+ * This could be done more efficiently, using nested struct/unions, but for now
+ * I am happy that it works.
+ */
+struct VMS_DBG_Symbol
+{
+ struct VMS_DBG_Symbol *next;
+ enum advanced_type advanced; /* description of what this is */
+ int dbx_type; /* this record is for this type */
+ int type2; /* For advanced types this is the type referred to.
+ i.e. the type a pointer points to, or the type
+ of object that makes up an array */
+ int VMS_type; /* Use this type when generating a variable def */
+ int index_min; /* used for arrays - this will be present for all */
+ int index_max; /* entries, but will be meaningless for non-arrays */
+ int data_size; /* size in bytes of the data type. For an array, this
+ is the size of one element in the array */
+ int struc_numb; /* Number of the structure/union/enum - used for ref */
+};
+
+struct VMS_DBG_Symbol *VMS_Symbol_type_list =
+{(struct VMS_DBG_Symbol *) NULL};
+
+/*
+ * We need this structure to keep track of forward references to
+ * struct/union/enum that have not been defined yet. When they are ultimately
+ * defined, then we can go back and generate the TIR commands to make a back
+ * reference.
+ */
+
+struct forward_ref
+{
+ struct forward_ref *next;
+ int dbx_type;
+ int struc_numb;
+ char resolved;
+};
+
+struct forward_ref *f_ref_root =
+{(struct forward_ref *) NULL};
+
+/*
+ * This routine is used to compare the names of certain types to various
+ * fixed types that are known by the debugger.
+ */
+#define type_check(x) !strcmp( symbol_name , x )
+
+/*
+ * This variable is used to keep track of the name of the symbol we are
+ * working on while we are parsing the stabs directives.
+ */
+static char *symbol_name;
+
+/* We use this counter to assign numbers to all of the structures, unions
+ * and enums that we define. When we actually declare a variable to the
+ * debugger, we can simply do it by number, rather than describing the
+ * whole thing each time.
+ */
+
+static structure_count = 0;
+
+/* This variable is used to keep track of the current structure number
+ * for a given variable. If this is < 0, that means that the structure
+ * has not yet been defined to the debugger. This is still cool, since
+ * the VMS object language has ways of fixing things up after the fact,
+ * so we just make a note of this, and generate fixups at the end.
+ */
+static int struct_number;
+
+
+/*
+ * Variable descriptors are used tell the debugger the data types of certain
+ * more complicated variables (basically anything involving a structure,
+ * union, enum, array or pointer). Some non-pointer variables of the
+ * basic types that the debugger knows about do not require a variable
+ * descriptor.
+ *
+ * Since it is impossible to have a variable descriptor longer than 128
+ * bytes by virtue of the way that the VMS object language is set up,
+ * it makes not sense to make the arrays any longer than this, or worrying
+ * about dynamic sizing of the array.
+ *
+ * These are the arrays and counters that we use to build a variable
+ * descriptor.
+ */
+
+#define MAX_DEBUG_RECORD 128
+static char Local[MAX_DEBUG_RECORD]; /* buffer for variable descriptor */
+static char Asuffix[MAX_DEBUG_RECORD]; /* buffer for array descriptor */
+static int Lpnt; /* index into Local */
+static int Apoint; /* index into Asuffix */
+static char overflow; /* flag to indicate we have written too much*/
+static int total_len; /* used to calculate the total length of variable
+ descriptor plus array descriptor - used for len byte*/
+
+/* Flag if we have told user about finding global constants in the text
+ section. */
+static gave_compiler_message = 0;
+
+/* A pointer to the current routine that we are working on. */
+
+static symbolS *Current_Routine;
+
+/* The psect number for $code a.k.a. the text section. */
+
+static int Text_Psect;
+
+
+/*
+ * Global data (Object records limited to 512 bytes by VAX-11 "C" runtime)
+ */
+static int VMS_Object_File_FD; /* File Descriptor for object file */
+static char Object_Record_Buffer[512]; /* Buffer for object file records */
+static int Object_Record_Offset;/* Offset to end of data */
+static int Current_Object_Record_Type; /* Type of record in above */
+
+/*
+ * Macros for placing data into the object record buffer
+ */
+
+#define PUT_LONG(val) \
+{ md_number_to_chars(Object_Record_Buffer + \
+ Object_Record_Offset, val, 4); \
+ Object_Record_Offset += 4; }
+
+#define PUT_SHORT(val) \
+{ md_number_to_chars(Object_Record_Buffer + \
+ Object_Record_Offset, val, 2); \
+ Object_Record_Offset += 2; }
+
+#define PUT_CHAR(val) Object_Record_Buffer[Object_Record_Offset++] = val
+
+#define PUT_COUNTED_STRING(cp) {\
+ register char *p = cp; \
+ PUT_CHAR(strlen(p)); \
+ while (*p) PUT_CHAR(*p++);}
+
+/*
+ * Macro for determining if a Name has psect attributes attached
+ * to it.
+ */
+#define PSECT_ATTRIBUTES_STRING "$$PsectAttributes_"
+#define PSECT_ATTRIBUTES_STRING_LENGTH 18
+
+#define HAS_PSECT_ATTRIBUTES(Name) \
+ (strncmp((Name[0] == '_' ? Name + 1 : Name), \
+ PSECT_ATTRIBUTES_STRING, \
+ PSECT_ATTRIBUTES_STRING_LENGTH) == 0)
+
+
+ /* in: segT out: N_TYPE bits */
+const short seg_N_TYPE[] =
+{
+ N_ABS,
+ N_TEXT,
+ N_DATA,
+ N_BSS,
+ N_UNDF, /* unknown */
+ N_UNDF, /* absent */
+ N_UNDF, /* pass1 */
+ N_UNDF, /* error */
+ N_UNDF, /* bignum/flonum */
+ N_UNDF, /* difference */
+ N_UNDF, /* debug */
+ N_UNDF, /* ntv */
+ N_UNDF, /* ptv */
+ N_REGISTER, /* register */
+};
+
+const segT N_TYPE_seg[N_TYPE + 2] =
+{ /* N_TYPE == 0x1E = 32-2 */
+ SEG_UNKNOWN, /* N_UNDF == 0 */
+ SEG_GOOF,
+ SEG_ABSOLUTE, /* N_ABS == 2 */
+ SEG_GOOF,
+ SEG_TEXT, /* N_TEXT == 4 */
+ SEG_GOOF,
+ SEG_DATA, /* N_DATA == 6 */
+ SEG_GOOF,
+ SEG_BSS, /* N_BSS == 8 */
+ SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_REGISTER, /* dummy N_REGISTER for regs = 30 */
+ SEG_GOOF,
+};
+
+
+/* The following code defines the special types of pseudo-ops that we
+ * use with VMS.
+ */
+
+char const_flag = 0;
+
+void
+s_const ()
+{
+ register int temp;
+
+ temp = get_absolute_expression ();
+ subseg_new (SEG_DATA, (subsegT) temp);
+ const_flag = 1;
+ demand_empty_rest_of_line ();
+}
+
+/*
+ * stab()
+ *
+ * Handle .stabX directives, which used to be open-coded.
+ * So much creeping featurism overloaded the semantics that we decided
+ * to put all .stabX thinking in one place. Here.
+ *
+ * We try to make any .stabX directive legal. Other people's AS will often
+ * do assembly-time consistency checks: eg assigning meaning to n_type bits
+ * and "protecting" you from setting them to certain values. (They also zero
+ * certain bits before emitting symbols. Tut tut.)
+ *
+ * If an expression is not absolute we either gripe or use the relocation
+ * information. Other people's assemblers silently forget information they
+ * don't need and invent information they need that you didn't supply.
+ *
+ * .stabX directives always make a symbol table entry. It may be junk if
+ * the rest of your .stabX directive is malformed.
+ */
+static void
+obj_aout_stab (what)
+ int what;
+{
+ register symbolS *symbolP = 0;
+ register char *string;
+ int saved_type = 0;
+ int length;
+ int goof; /* TRUE if we have aborted. */
+ long longint;
+
+/*
+ * Enter with input_line_pointer pointing past .stabX and any following
+ * whitespace.
+ */
+ goof = 0; /* JF who forgot this?? */
+ if (what == 's')
+ {
+ string = demand_copy_C_string (&length);
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == ',')
+ input_line_pointer++;
+ else
+ {
+ as_bad ("I need a comma after symbol's name");
+ goof = 1;
+ }
+ }
+ else
+ string = "";
+
+/*
+ * Input_line_pointer->after ','. String->symbol name.
+ */
+ if (!goof)
+ {
+ symbolP = symbol_new (string,
+ SEG_UNKNOWN,
+ 0,
+ (struct frag *) 0);
+ switch (what)
+ {
+ case 'd':
+ S_SET_NAME (symbolP, NULL); /* .stabd feature. */
+ S_SET_VALUE (symbolP, obstack_next_free (&frags) - frag_now->fr_literal);
+ symbolP->sy_frag = frag_now;
+ break;
+
+ case 'n':
+ symbolP->sy_frag = &zero_address_frag;
+ break;
+
+ case 's':
+ symbolP->sy_frag = &zero_address_frag;
+ break;
+
+ default:
+ BAD_CASE (what);
+ break;
+ }
+
+ if (get_absolute_expression_and_terminator (&longint) == ',')
+ symbolP->sy_symbol.n_type = saved_type = longint;
+ else
+ {
+ as_bad ("I want a comma after the n_type expression");
+ goof = 1;
+ input_line_pointer--; /* Backup over a non-',' char. */
+ }
+ }
+
+ if (!goof)
+ {
+ if (get_absolute_expression_and_terminator (&longint) == ',')
+ S_SET_OTHER (symbolP, longint);
+ else
+ {
+ as_bad ("I want a comma after the n_other expression");
+ goof = 1;
+ input_line_pointer--; /* Backup over a non-',' char. */
+ }
+ }
+
+ if (!goof)
+ {
+ S_SET_DESC (symbolP, get_absolute_expression ());
+ if (what == 's' || what == 'n')
+ {
+ if (*input_line_pointer != ',')
+ {
+ as_bad ("I want a comma after the n_desc expression");
+ goof = 1;
+ }
+ else
+ {
+ input_line_pointer++;
+ }
+ }
+ }
+
+ if ((!goof) && (what == 's' || what == 'n'))
+ {
+ pseudo_set (symbolP);
+ symbolP->sy_symbol.n_type = saved_type;
+ }
+
+ if (goof)
+ ignore_rest_of_line ();
+ else
+ demand_empty_rest_of_line ();
+} /* obj_aout_stab() */
+
+const pseudo_typeS obj_pseudo_table[] =
+{
+ {"stabd", obj_aout_stab, 'd'},/* stabs */
+ {"stabn", obj_aout_stab, 'n'},/* stabs */
+ {"stabs", obj_aout_stab, 's'},/* stabs */
+ {"const", s_const, 0},
+ {0, 0, 0},
+
+}; /* obj_pseudo_table */
+
+void
+obj_read_begin_hook ()
+{
+ return;
+} /* obj_read_begin_hook() */
+
+void
+obj_crawl_symbol_chain (headers)
+ object_headers *headers;
+{
+ symbolS *symbolP;
+ symbolS **symbolPP;
+ int symbol_number = 0;
+
+ /* JF deal with forward references first... */
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next (symbolP))
+ {
+ if (symbolP->sy_forward)
+ {
+ S_SET_VALUE (symbolP, S_GET_VALUE (symbolP)
+ + S_GET_VALUE (symbolP->sy_forward)
+ + symbolP->sy_forward->sy_frag->fr_address);
+ symbolP->sy_forward = 0;
+ } /* if it has a forward reference */
+ } /* walk the symbol chain */
+
+ { /* crawl symbol table */
+ register int symbol_number = 0;
+
+ {
+ symbolPP = &symbol_rootP; /* -> last symbol chain link. */
+ while ((symbolP = *symbolPP) != NULL)
+ {
+ S_GET_VALUE (symbolP) += symbolP->sy_frag->fr_address;
+
+ /* OK, here is how we decide which symbols go out into the
+ brave new symtab. Symbols that do are:
+
+ * symbols with no name (stabd's?)
+ * symbols with debug info in their N_TYPE
+
+ Symbols that don't are:
+ * symbols that are registers
+ * symbols with \1 as their 3rd character (numeric labels)
+ * "local labels" as defined by S_LOCAL_NAME(name)
+ if the -L switch was passed to gas.
+
+ All other symbols are output. We complain if a deleted
+ symbol was marked external. */
+
+
+ if (!S_IS_REGISTER (symbolP))
+ {
+ symbolP->sy_name_offset = 0;
+ symbolPP = &(symbol_next (symbolP));
+ }
+ else
+ {
+ if (S_IS_EXTERNAL (symbolP) || !S_IS_DEFINED (symbolP))
+ {
+ as_bad ("Local symbol %s never defined", S_GET_NAME (symbolP));
+ } /* oops. */
+
+ } /* if this symbol should be in the output */
+ } /* for each symbol */
+ }
+ H_SET_STRING_SIZE (headers, string_byte_count);
+ H_SET_SYMBOL_TABLE_SIZE (headers, symbol_number);
+ } /* crawl symbol table */
+
+} /* obj_crawl_symbol_chain() */
+
+
+ /****** VMS OBJECT FILE HACKING ROUTINES *******/
+
+
+/*
+ * Create the VMS object file
+ */
+static
+Create_VMS_Object_File ()
+{
+#if defined(eunice) || !defined(HO_VMS)
+ VMS_Object_File_FD = creat (out_file_name, 0777, "var");
+#else /* eunice */
+ VMS_Object_File_FD = creat (out_file_name, 0, "rfm=var",
+ "mbc=16", "deq=64", "fop=tef", "shr=nil");
+#endif /* eunice */
+ /*
+ * Deal with errors
+ */
+ if (VMS_Object_File_FD < 0)
+ {
+ char Error_Line[256];
+
+ sprintf (Error_Line, "Couldn't create VMS object file \"%s\"",
+ out_file_name);
+ error (Error_Line);
+ }
+ /*
+ * Initialize object file hacking variables
+ */
+ Object_Record_Offset = 0;
+ Current_Object_Record_Type = -1;
+}
+
+
+/*
+ * Flush the object record buffer to the object file
+ */
+static
+Flush_VMS_Object_Record_Buffer ()
+{
+ int i;
+ short int zero;
+ /*
+ * If the buffer is empty, we are done
+ */
+ if (Object_Record_Offset == 0)
+ return;
+ /*
+ * Write the data to the file
+ */
+#ifndef HO_VMS /* For cross-assembly purposes. */
+ i = write (VMS_Object_File_FD, &Object_Record_Offset, 2);
+#endif /* not HO_VMS */
+ i = write (VMS_Object_File_FD,
+ Object_Record_Buffer,
+ Object_Record_Offset);
+ if (i != Object_Record_Offset)
+ error ("I/O error writing VMS object file");
+#ifndef HO_VMS /* When cross-assembling, we need to pad the record to an even
+ number of bytes. */
+ /* pad it if needed */
+ zero = 0;
+ if (Object_Record_Offset & 1 != 0)
+ write (VMS_Object_File_FD, &zero, 1);
+#endif /* not HO_VMS */
+ /*
+ * The buffer is now empty
+ */
+ Object_Record_Offset = 0;
+}
+
+
+/*
+ * Declare a particular type of object file record
+ */
+static
+Set_VMS_Object_File_Record (Type)
+ int Type;
+{
+ /*
+ * If the type matches, we are done
+ */
+ if (Type == Current_Object_Record_Type)
+ return;
+ /*
+ * Otherwise: flush the buffer
+ */
+ Flush_VMS_Object_Record_Buffer ();
+ /*
+ * Set the new type
+ */
+ Current_Object_Record_Type = Type;
+}
+
+
+
+/*
+ * Close the VMS Object file
+ */
+static
+Close_VMS_Object_File ()
+{
+ short int m_one = -1;
+#ifndef HO_VMS /* For cross-assembly purposes. */
+/* Write a 0xffff into the file, which means "End of File" */
+ write (VMS_Object_File_FD, &m_one, 2);
+#endif /* not HO_VMS */
+ close (VMS_Object_File_FD);
+}
+
+
+/*
+ * Store immediate data in current Psect
+ */
+static
+VMS_Store_Immediate_Data (Pointer, Size, Record_Type)
+ register char *Pointer;
+ int Size;
+ int Record_Type;
+{
+ register int i;
+
+ /*
+ * We are writing a "Record_Type" record
+ */
+ Set_VMS_Object_File_Record (Record_Type);
+ /*
+ * We can only store 128 bytes at a time
+ */
+ while (Size > 0)
+ {
+ /*
+ * Store a maximum of 128 bytes
+ */
+ i = (Size > 128) ? 128 : Size;
+ Size -= i;
+ /*
+ * If we cannot accommodate this record, flush the
+ * buffer.
+ */
+ if ((Object_Record_Offset + i + 1) >=
+ sizeof (Object_Record_Buffer))
+ Flush_VMS_Object_Record_Buffer ();
+ /*
+ * If the buffer is empty we must insert record type
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (Record_Type);
+ /*
+ * Store the count
+ */
+ PUT_CHAR (-i & 0xff);
+ /*
+ * Store the data
+ */
+ while (--i >= 0)
+ PUT_CHAR (*Pointer++);
+ /*
+ * Flush the buffer if it is more than 75% full
+ */
+ if (Object_Record_Offset >
+ (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+ }
+}
+
+/*
+ * Make a data reference
+ */
+static
+VMS_Set_Data (Psect_Index, Offset, Record_Type, Force)
+ int Psect_Index;
+ int Offset;
+ int Record_Type;
+ int Force;
+{
+ /*
+ * We are writing a "Record_Type" record
+ */
+ Set_VMS_Object_File_Record (Record_Type);
+ /*
+ * If the buffer is empty we must insert the record type
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (Record_Type);
+ /*
+ * Stack the Psect base + Longword Offset
+ */
+ if (Force == 1)
+ {
+ if (Psect_Index > 127)
+ {
+ PUT_CHAR (TIR_S_C_STA_WPL);
+ PUT_SHORT (Psect_Index);
+ PUT_LONG (Offset);
+ }
+ else
+ {
+ PUT_CHAR (TIR_S_C_STA_PL);
+ PUT_CHAR (Psect_Index);
+ PUT_LONG (Offset);
+ }
+ }
+ else
+ {
+ if (Offset > 32767)
+ {
+ PUT_CHAR (TIR_S_C_STA_WPL);
+ PUT_SHORT (Psect_Index);
+ PUT_LONG (Offset);
+ }
+ else if (Offset > 127)
+ {
+ PUT_CHAR (TIR_S_C_STA_WPW);
+ PUT_SHORT (Psect_Index);
+ PUT_SHORT (Offset);
+ }
+ else
+ {
+ PUT_CHAR (TIR_S_C_STA_WPB);
+ PUT_SHORT (Psect_Index);
+ PUT_CHAR (Offset);
+ };
+ };
+ /*
+ * Set relocation base
+ */
+ PUT_CHAR (TIR_S_C_STO_PIDR);
+ /*
+ * Flush the buffer if it is more than 75% full
+ */
+ if (Object_Record_Offset >
+ (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+/*
+ * Make a debugger reference to a struct, union or enum.
+ */
+static
+VMS_Store_Struct (int Struct_Index)
+{
+ /*
+ * We are writing a "OBJ_S_C_DBG" record
+ */
+ Set_VMS_Object_File_Record (OBJ_S_C_DBG);
+ /*
+ * If the buffer is empty we must insert the record type
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (OBJ_S_C_DBG);
+ PUT_CHAR (TIR_S_C_STA_UW);
+ PUT_SHORT (Struct_Index);
+ PUT_CHAR (TIR_S_C_CTL_STKDL);
+ PUT_CHAR (TIR_S_C_STO_L);
+ /*
+ * Flush the buffer if it is more than 75% full
+ */
+ if (Object_Record_Offset >
+ (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+/*
+ * Make a debugger reference to partially define a struct, union or enum.
+ */
+static
+VMS_Def_Struct (int Struct_Index)
+{
+ /*
+ * We are writing a "OBJ_S_C_DBG" record
+ */
+ Set_VMS_Object_File_Record (OBJ_S_C_DBG);
+ /*
+ * If the buffer is empty we must insert the record type
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (OBJ_S_C_DBG);
+ PUT_CHAR (TIR_S_C_STA_UW);
+ PUT_SHORT (Struct_Index);
+ PUT_CHAR (TIR_S_C_CTL_DFLOC);
+ /*
+ * Flush the buffer if it is more than 75% full
+ */
+ if (Object_Record_Offset >
+ (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+static
+VMS_Set_Struct (int Struct_Index)
+{ /* see previous functions for comments */
+ Set_VMS_Object_File_Record (OBJ_S_C_DBG);
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (OBJ_S_C_DBG);
+ PUT_CHAR (TIR_S_C_STA_UW);
+ PUT_SHORT (Struct_Index);
+ PUT_CHAR (TIR_S_C_CTL_STLOC);
+ if (Object_Record_Offset >
+ (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+/*
+ * Write the Traceback Module Begin record
+ */
+static
+VMS_TBT_Module_Begin ()
+{
+ register char *cp, *cp1;
+ int Size;
+ char Module_Name[256];
+ char Local[256];
+
+ /*
+ * Get module name (the FILENAME part of the object file)
+ */
+ cp = out_file_name;
+ cp1 = Module_Name;
+ while (*cp)
+ {
+ if ((*cp == ']') || (*cp == '>') ||
+ (*cp == ':') || (*cp == '/'))
+ {
+ cp1 = Module_Name;
+ cp++;
+ continue;
+ }
+ *cp1++ = islower (*cp) ? toupper (*cp++) : *cp++;
+ }
+ *cp1 = 0;
+ /*
+ * Limit it to 31 characters
+ */
+ while (--cp1 >= Module_Name)
+ if (*cp1 == '.')
+ *cp1 = 0;
+ if (strlen (Module_Name) > 31)
+ {
+ if (flagseen['+'])
+ printf ("%s: Module name truncated: %s\n", myname, Module_Name);
+ Module_Name[31] = 0;
+ }
+ /*
+ * Arrange to store the data locally (leave room for size byte)
+ */
+ cp = Local + 1;
+ /*
+ * Begin module
+ */
+ *cp++ = DST_S_C_MODBEG;
+ /*
+ * Unused
+ */
+ *cp++ = 0;
+ /*
+ * Language type == "C"
+ */
+ *(long *) cp = DST_S_C_C;
+ cp += sizeof (long);
+ /*
+ * Store the module name
+ */
+ *cp++ = strlen (Module_Name);
+ cp1 = Module_Name;
+ while (*cp1)
+ *cp++ = *cp1++;
+ /*
+ * Now we can store the record size
+ */
+ Size = (cp - Local);
+ Local[0] = Size - 1;
+ /*
+ * Put it into the object record
+ */
+ VMS_Store_Immediate_Data (Local, Size, OBJ_S_C_TBT);
+}
+
+
+/*
+ * Write the Traceback Module End record
+*/
+static
+VMS_TBT_Module_End ()
+{
+ char Local[2];
+
+ /*
+ * End module
+ */
+ Local[0] = 1;
+ Local[1] = DST_S_C_MODEND;
+ /*
+ * Put it into the object record
+ */
+ VMS_Store_Immediate_Data (Local, 2, OBJ_S_C_TBT);
+}
+
+
+/*
+ * Write the Traceback Routine Begin record
+ */
+static
+VMS_TBT_Routine_Begin (symbolP, Psect)
+ struct symbol *symbolP;
+ int Psect;
+{
+ register char *cp, *cp1;
+ char *Name;
+ int Offset;
+ int Size;
+ char Local[512];
+
+ /*
+ * Strip the leading "_" from the name
+ */
+ Name = S_GET_NAME (symbolP);
+ if (*Name == '_')
+ Name++;
+ /*
+ * Get the text psect offset
+ */
+ Offset = S_GET_VALUE (symbolP);
+ /*
+ * Calculate the record size
+ */
+ Size = 1 + 1 + 4 + 1 + strlen (Name);
+ /*
+ * Record Size
+ */
+ Local[0] = Size;
+ /*
+ * Begin Routine
+ */
+ Local[1] = DST_S_C_RTNBEG;
+ /*
+ * Uses CallS/CallG
+ */
+ Local[2] = 0;
+ /*
+ * Store the data so far
+ */
+ VMS_Store_Immediate_Data (Local, 3, OBJ_S_C_TBT);
+ /*
+ * Make sure we are still generating a OBJ_S_C_TBT record
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (OBJ_S_C_TBT);
+ /*
+ * Now get the symbol address
+ */
+ PUT_CHAR (TIR_S_C_STA_WPL);
+ PUT_SHORT (Psect);
+ PUT_LONG (Offset);
+ /*
+ * Store the data reference
+ */
+ PUT_CHAR (TIR_S_C_STO_PIDR);
+ /*
+ * Store the counted string as data
+ */
+ cp = Local;
+ cp1 = Name;
+ Size = strlen (cp1) + 1;
+ *cp++ = Size - 1;
+ while (*cp1)
+ *cp++ = *cp1++;
+ VMS_Store_Immediate_Data (Local, Size, OBJ_S_C_TBT);
+}
+
+
+/*
+ * Write the Traceback Routine End record
+ * We *must* search the symbol table to find the next routine, since
+ * the assember has a way of reassembling the symbol table OUT OF ORDER
+ * Thus the next routine in the symbol list is not necessarily the
+ * next one in memory. For debugging to work correctly we must know the
+ * size of the routine.
+ */
+static
+VMS_TBT_Routine_End (Max_Size, sp)
+ int Max_Size;
+ symbolS *sp;
+{
+ symbolS *symbolP;
+ int Size = 0x7fffffff;
+ char Local[16];
+
+
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next (symbolP))
+ {
+ if (!S_IS_DEBUG (symbolP) && S_GET_TYPE (symbolP) == N_TEXT)
+ {
+ if (*S_GET_NAME (symbolP) == 'L')
+ continue;
+ if ((S_GET_VALUE (symbolP) > S_GET_VALUE (sp)) &&
+ (S_GET_VALUE (symbolP) < Size))
+ Size = S_GET_VALUE (symbolP);
+ /* check if gcc_compiled. has size of zero */
+ if ((S_GET_VALUE (symbolP) == S_GET_VALUE (sp)) &&
+ sp != symbolP &&
+ (!strcmp (S_GET_NAME (sp), "gcc_compiled.") ||
+ !strcmp (S_GET_NAME (sp), "gcc2_compiled.")))
+ Size = S_GET_VALUE (symbolP);
+
+ };
+ };
+ if (Size == 0x7fffffff)
+ Size = Max_Size;
+ Size -= S_GET_VALUE (sp); /* and get the size of the routine */
+ /*
+ * Record Size
+ */
+ Local[0] = 6;
+ /*
+ * End of Routine
+ */
+ Local[1] = DST_S_C_RTNEND;
+ /*
+ * Unused
+ */
+ Local[2] = 0;
+ /*
+ * Size of routine
+ */
+ *((long *) (Local + 3)) = Size;
+ /*
+ * Store the record
+ */
+ VMS_Store_Immediate_Data (Local, 7, OBJ_S_C_TBT);
+}
+
+/*
+ * Write the Traceback Block End record
+ */
+static
+VMS_TBT_Block_Begin (symbolP, Psect, Name)
+ struct symbol *symbolP;
+ int Psect;
+ char *Name;
+{
+ register char *cp, *cp1;
+ int Offset;
+ int Size;
+ char Local[512];
+ /*
+ * Begin block
+ */
+ Size = 1 + 1 + 4 + 1 + strlen (Name);
+ /*
+ * Record Size
+ */
+ Local[0] = Size;
+ /*
+ * Begin Block - We simulate with a phony routine
+ */
+ Local[1] = DST_S_C_BLKBEG;
+ /*
+ * Uses CallS/CallG
+ */
+ Local[2] = 0;
+ /*
+ * Store the data so far
+ */
+ VMS_Store_Immediate_Data (Local, 3, OBJ_S_C_DBG);
+ /*
+ * Make sure we are still generating a OBJ_S_C_DBG record
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (OBJ_S_C_DBG);
+ /*
+ * Now get the symbol address
+ */
+ PUT_CHAR (TIR_S_C_STA_WPL);
+ PUT_SHORT (Psect);
+ /*
+ * Get the text psect offset
+ */
+ Offset = S_GET_VALUE (symbolP);
+ PUT_LONG (Offset);
+ /*
+ * Store the data reference
+ */
+ PUT_CHAR (TIR_S_C_STO_PIDR);
+ /*
+ * Store the counted string as data
+ */
+ cp = Local;
+ cp1 = Name;
+ Size = strlen (cp1) + 1;
+ *cp++ = Size - 1;
+ while (*cp1)
+ *cp++ = *cp1++;
+ VMS_Store_Immediate_Data (Local, Size, OBJ_S_C_DBG);
+}
+
+
+/*
+ * Write the Traceback Block End record
+ */
+static
+VMS_TBT_Block_End (int Size)
+{
+ char Local[16];
+
+ /*
+ * End block - simulate with a phony end routine
+ */
+ Local[0] = 6;
+ Local[1] = DST_S_C_BLKEND;
+ *((long *) (Local + 3)) = Size;
+ /*
+ * Unused
+ */
+ Local[2] = 0;
+ VMS_Store_Immediate_Data (Local, 7, OBJ_S_C_DBG);
+}
+
+
+
+/*
+ * Write a Line number / PC correlation record
+ */
+static
+VMS_TBT_Line_PC_Correlation (Line_Number, Offset, Psect, Do_Delta)
+ int Line_Number;
+ int Offset;
+ int Psect;
+ int Do_Delta;
+{
+ register char *cp;
+ char Local[64];
+
+ /*
+* If not delta, set our PC/Line number correlation
+*/
+ if (Do_Delta == 0)
+ {
+ /*
+ * Size
+ */
+ Local[0] = 1 + 1 + 2 + 1 + 4;
+ /*
+ * Line Number/PC correlation
+ */
+ Local[1] = DST_S_C_LINE_NUM;
+ /*
+ * Set Line number
+ */
+ Local[2] = DST_S_C_SET_LINE_NUM;
+ *((unsigned short *) (Local + 3)) = Line_Number - 1;
+ /*
+ * Set PC
+ */
+ Local[5] = DST_S_C_SET_ABS_PC;
+ VMS_Store_Immediate_Data (Local, 6, OBJ_S_C_TBT);
+ /*
+ * Make sure we are still generating a OBJ_S_C_TBT record
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (OBJ_S_C_TBT);
+ if (Psect < 255)
+ {
+ PUT_CHAR (TIR_S_C_STA_PL);
+ PUT_CHAR (Psect);
+ }
+ else
+ {
+ PUT_CHAR (TIR_S_C_STA_WPL);
+ PUT_SHORT (Psect);
+ }
+ PUT_LONG (Offset);
+ PUT_CHAR (TIR_S_C_STO_PIDR);
+ /*
+ * Do a PC offset of 0 to register the line number
+ */
+ Local[0] = 2;
+ Local[1] = DST_S_C_LINE_NUM;
+ Local[2] = 0; /* Increment PC by 0 and register line # */
+ VMS_Store_Immediate_Data (Local, 3, OBJ_S_C_TBT);
+ }
+ else
+ {
+ /*
+ * If Delta is negative, terminate the line numbers
+ */
+ if (Do_Delta < 0)
+ {
+ Local[0] = 1 + 1 + 4;
+ Local[1] = DST_S_C_LINE_NUM;
+ Local[2] = DST_S_C_TERM_L;
+ *((long *) (Local + 3)) = Offset;
+ VMS_Store_Immediate_Data (Local, 7, OBJ_S_C_TBT);
+ /*
+ * Done
+ */
+ return;
+ }
+ /*
+ * Do a PC/Line delta
+ */
+ cp = Local + 1;
+ *cp++ = DST_S_C_LINE_NUM;
+ if (Line_Number > 1)
+ {
+ /*
+ * We need to increment the line number
+ */
+ if (Line_Number - 1 <= 255)
+ {
+ *cp++ = DST_S_C_INCR_LINUM;
+ *cp++ = Line_Number - 1;
+ }
+ else
+ {
+ *cp++ = DST_S_C_INCR_LINUM_W;
+ *(short *) cp = Line_Number - 1;
+ cp += sizeof (short);
+ }
+ }
+ /*
+ * Increment the PC
+ */
+ if (Offset <= 128)
+ {
+ *cp++ = -Offset;
+ }
+ else
+ {
+ if (Offset < 0x10000)
+ {
+ *cp++ = DST_S_C_DELTA_PC_W;
+ *(short *) cp = Offset;
+ cp += sizeof (short);
+ }
+ else
+ {
+ *cp++ = DST_S_C_DELTA_PC_L;
+ *(long *) cp = Offset;
+ cp += sizeof (long);
+ }
+ }
+ Local[0] = cp - (Local + 1);
+ VMS_Store_Immediate_Data (Local, cp - Local, OBJ_S_C_TBT);
+ }
+}
+
+
+/*
+ * Describe a source file to the debugger
+ */
+static
+VMS_TBT_Source_File (Filename, ID_Number)
+ char *Filename;
+ int ID_Number;
+{
+ register char *cp, *cp1;
+ int Status, i;
+ char Local[512];
+#ifndef HO_VMS /* Used for cross-assembly */
+ i = strlen (Filename);
+#else /* HO_VMS */
+ static struct FAB Fab;
+ static struct NAM Nam;
+ static struct XABDAT Date_Xab;
+ static struct XABFHC File_Header_Xab;
+ char Es_String[255], Rs_String[255];
+
+ /*
+ * Setup the Fab
+ */
+ Fab.fab$b_bid = FAB$C_BID;
+ Fab.fab$b_bln = sizeof (Fab);
+ Fab.fab$l_nam = (&Nam);
+ Fab.fab$l_xab = (char *) &Date_Xab;
+ /*
+ * Setup the Nam block so we can find out the FULL name
+ * of the source file.
+ */
+ Nam.nam$b_bid = NAM$C_BID;
+ Nam.nam$b_bln = sizeof (Nam);
+ Nam.nam$l_rsa = Rs_String;
+ Nam.nam$b_rss = sizeof (Rs_String);
+ Nam.nam$l_esa = Es_String;
+ Nam.nam$b_ess = sizeof (Es_String);
+ /*
+ * Setup the Date and File Header Xabs
+ */
+ Date_Xab.xab$b_cod = XAB$C_DAT;
+ Date_Xab.xab$b_bln = sizeof (Date_Xab);
+ Date_Xab.xab$l_nxt = (char *) &File_Header_Xab;
+ File_Header_Xab.xab$b_cod = XAB$C_FHC;
+ File_Header_Xab.xab$b_bln = sizeof (File_Header_Xab);
+ /*
+ * Get the file information
+ */
+ Fab.fab$l_fna = Filename;
+ Fab.fab$b_fns = strlen (Filename);
+ Status = sys$open (&Fab);
+ if (!(Status & 1))
+ {
+ printf ("gas: Couldn't find source file \"%s\", Error = %%X%x\n",
+ Filename, Status);
+ return (0);
+ }
+ sys$close (&Fab);
+ /*
+ * Calculate the size of the resultant string
+ */
+ i = Nam.nam$b_rsl;
+#endif /* HO_VMS */
+ /*
+ * Size of record
+ */
+ Local[0] = 1 + 1 + 1 + 1 + 1 + 2 + 8 + 4 + 2 + 1 + 1 + i + 1;
+ /*
+ * Source declaration
+ */
+ Local[1] = DST_S_C_SOURCE;
+ /*
+ * Make formfeeds count as source records
+ */
+ Local[2] = DST_S_C_SRC_FORMFEED;
+ /*
+ * Declare source file
+ */
+ Local[3] = DST_S_C_SRC_DECLFILE;
+ Local[4] = 1 + 2 + 8 + 4 + 2 + 1 + 1 + i + 1;
+ cp = Local + 5;
+ /*
+ * Flags
+ */
+ *cp++ = 0;
+ /*
+ * File ID
+ */
+ *(short *) cp = ID_Number;
+ cp += sizeof (short);
+#ifndef HO_VMS
+ /*
+ * Creation Date. Unknown, so we fill with zeroes.
+ */
+ *(long *) cp = 0;
+ cp += sizeof (long);
+ *(long *) cp = 0;
+ cp += sizeof (long);
+ /*
+ * End of file block
+ */
+ *(long *) cp = 0;
+ cp += sizeof (long);
+ /*
+ * First free byte
+ */
+ *(short *) cp = 0;
+ cp += sizeof (short);
+ /*
+ * Record format
+ */
+ *cp++ = 0;
+ /*
+ * Filename
+ */
+ *cp++ = i;
+ cp1 = Filename;
+#else /* Use this code when assembling for VMS on a VMS system */
+ /*
+ * Creation Date
+ */
+ *(long *) cp = ((long *) &Date_Xab.xab$q_cdt)[0];
+ cp += sizeof (long);
+ *(long *) cp = ((long *) &Date_Xab.xab$q_cdt)[1];
+ cp += sizeof (long);
+ /*
+ * End of file block
+ */
+ *(long *) cp = File_Header_Xab.xab$l_ebk;
+ cp += sizeof (long);
+ /*
+ * First free byte
+ */
+ *(short *) cp = File_Header_Xab.xab$w_ffb;
+ cp += sizeof (short);
+ /*
+ * Record format
+ */
+ *cp++ = File_Header_Xab.xab$b_rfo;
+ /*
+ * Filename
+ */
+ *cp++ = i;
+ cp1 = Rs_String;
+#endif /* HO_VMS */
+ while (--i >= 0)
+ *cp++ = *cp1++;
+ /*
+ * Library module name (none)
+ */
+ *cp++ = 0;
+ /*
+ * Done
+ */
+ VMS_Store_Immediate_Data (Local, cp - Local, OBJ_S_C_TBT);
+ return 1;
+}
+
+
+/*
+ * Give the number of source lines to the debugger
+ */
+static
+VMS_TBT_Source_Lines (ID_Number, Starting_Line_Number, Number_Of_Lines)
+ int ID_Number;
+ int Starting_Line_Number;
+ int Number_Of_Lines;
+{
+ char *cp, *cp1;
+ char Local[16];
+
+ /*
+ * Size of record
+ */
+ Local[0] = 1 + 1 + 2 + 1 + 4 + 1 + 2;
+ /*
+ * Source declaration
+ */
+ Local[1] = DST_S_C_SOURCE;
+ /*
+ * Set Source File
+ */
+ cp = Local + 2;
+ *cp++ = DST_S_C_SRC_SETFILE;
+ /*
+ * File ID Number
+ */
+ *(short *) cp = ID_Number;
+ cp += sizeof (short);
+ /*
+ * Set record number
+ */
+ *cp++ = DST_S_C_SRC_SETREC_L;
+ *(long *) cp = Starting_Line_Number;
+ cp += sizeof (long);
+ /*
+ * Define lines
+ */
+ *cp++ = DST_S_C_SRC_DEFLINES_W;
+ *(short *) cp = Number_Of_Lines;
+ cp += sizeof (short);
+ /*
+ * Done
+ */
+ VMS_Store_Immediate_Data (Local, cp - Local, OBJ_S_C_TBT);
+}
+
+
+
+
+/* This routine locates a file in the list of files. If an entry does not
+ * exist, one is created. For include files, a new entry is always created
+ * such that inline functions can be properly debugged. */
+static struct input_file *
+find_file (sp)
+ symbolS *sp;
+{
+ struct input_file *same_file;
+ struct input_file *fpnt;
+ same_file = (struct input_file *) NULL;
+ for (fpnt = file_root; fpnt; fpnt = fpnt->next)
+ {
+ if (fpnt == (struct input_file *) NULL)
+ break;
+ if (fpnt->spnt == sp)
+ return fpnt;
+ };
+ for (fpnt = file_root; fpnt; fpnt = fpnt->next)
+ {
+ if (fpnt == (struct input_file *) NULL)
+ break;
+ if (strcmp (S_GET_NAME (sp), fpnt->name) == 0)
+ {
+ if (fpnt->flag == 1)
+ return fpnt;
+ same_file = fpnt;
+ break;
+ };
+ };
+ fpnt = (struct input_file *) malloc (sizeof (struct input_file));
+ if (file_root == (struct input_file *) NULL)
+ file_root = fpnt;
+ else
+ {
+ struct input_file *fpnt1;
+ for (fpnt1 = file_root; fpnt1->next; fpnt1 = fpnt1->next) ;
+ fpnt1->next = fpnt;
+ };
+ fpnt->next = (struct input_file *) NULL;
+ fpnt->name = S_GET_NAME (sp);
+ fpnt->min_line = 0x7fffffff;
+ fpnt->max_line = 0;
+ fpnt->offset = 0;
+ fpnt->flag = 0;
+ fpnt->file_number = 0;
+ fpnt->spnt = sp;
+ fpnt->same_file_fpnt = same_file;
+ return fpnt;
+}
+
+/*
+ * The following functions and definitions are used to generate object records
+ * that will describe program variables to the VMS debugger.
+ *
+ * This file contains many of the routines needed to output debugging info into
+ * the object file that the VMS debugger needs to understand symbols. These
+ * routines are called very late in the assembly process, and thus we can be
+ * fairly lax about changing things, since the GSD and the TIR sections have
+ * already been output.
+ */
+
+
+/* This routine converts a number string into an integer, and stops when it
+ * sees an invalid character the return value is the address of the character
+ * just past the last character read. No error is generated.
+ */
+static char *
+cvt_integer (str, rtn)
+ char *str;
+ int *rtn;
+{
+ int ival, neg;
+ neg = *str == '-' ? ++str, -1 : 1;
+ ival = 0; /* first get the number of the type for dbx */
+ while ((*str <= '9') && (*str >= '0'))
+ ival = 10 * ival + *str++ - '0';
+ *rtn = neg * ival;
+ return str;
+}
+
+/* this routine fixes the names that are generated by C++, ".this" is a good
+ * example. The period does not work for the debugger, since it looks like
+ * the syntax for a structure element, and thus it gets mightily confused
+ *
+ * We also use this to strip the PsectAttribute hack from the name before we
+ * write a debugger record */
+
+static char *
+fix_name (pnt)
+ char *pnt;
+{
+ char *pnt1;
+ /*
+ * Kill any leading "_"
+ */
+ if (*pnt == '_')
+ pnt++;
+ /*
+ * Is there a Psect Attribute to skip??
+ */
+ if (HAS_PSECT_ATTRIBUTES (pnt))
+ {
+ /*
+ * Yes: Skip it
+ */
+ pnt += PSECT_ATTRIBUTES_STRING_LENGTH;
+ while (*pnt)
+ {
+ if ((pnt[0] == '$') && (pnt[1] == '$'))
+ {
+ pnt += 2;
+ break;
+ }
+ pnt++;
+ }
+ }
+/* Here we fix the .this -> $this conversion */
+ for (pnt1 = pnt; *pnt1 != 0; pnt1++)
+ {
+ if (*pnt1 == '.')
+ *pnt1 = '$';
+ };
+ return pnt;
+}
+
+/* When defining a structure, this routine is called to find the name of
+ * the actual structure. It is assumed that str points to the equal sign
+ * in the definition, and it moves backward until it finds the start of the
+ * name. If it finds a 0, then it knows that this structure def is in the
+ * outermost level, and thus symbol_name points to the symbol name.
+ */
+static char *
+get_struct_name (str)
+ char *str;
+{
+ char *pnt;
+ pnt = str;
+ while ((*pnt != ':') && (*pnt != '\0'))
+ pnt--;
+ if (*pnt == '\0')
+ return symbol_name;
+ *pnt-- = '\0';
+ while ((*pnt != ';') && (*pnt != '='))
+ pnt--;
+ if (*pnt == ';')
+ return pnt + 1;
+ while ((*pnt < '0') || (*pnt > '9'))
+ pnt++;
+ while ((*pnt >= '0') && (*pnt <= '9'))
+ pnt++;
+ return pnt;
+}
+
+/* search symbol list for type number dbx_type. Return a pointer to struct */
+static struct VMS_DBG_Symbol *
+find_symbol (dbx_type)
+ int dbx_type;
+{
+ struct VMS_DBG_Symbol *spnt;
+ spnt = VMS_Symbol_type_list;
+ while (spnt != (struct VMS_DBG_Symbol *) NULL)
+ {
+ if (spnt->dbx_type == dbx_type)
+ break;
+ spnt = spnt->next;
+ };
+ if (spnt == (struct VMS_DBG_Symbol *) NULL)
+ return 0; /*Dunno what this is*/
+ return spnt;
+}
+
+
+/* this routine puts info into either Local or Asuffix, depending on the sign
+ * of size. The reason is that it is easier to build the variable descriptor
+ * backwards, while the array descriptor is best built forwards. In the end
+ * they get put together, if there is not a struct/union/enum along the way
+ */
+static
+push (value, size)
+ int value, size;
+{
+ char *pnt;
+ int i;
+ int size1;
+ long int val;
+ val = value;
+ pnt = (char *) &val;
+ size1 = size;
+ if (size < 0)
+ {
+ size1 = -size;
+ pnt += size1 - 1;
+ };
+ if (size < 0)
+ for (i = 0; i < size1; i++)
+ {
+ Local[Lpnt--] = *pnt--;
+ if (Lpnt < 0)
+ {
+ overflow = 1;
+ Lpnt = 1;
+ };
+ }
+ else
+ for (i = 0; i < size1; i++)
+ {
+ Asuffix[Apoint++] = *pnt++;
+ if (Apoint >= MAX_DEBUG_RECORD)
+ {
+ overflow = 1;
+ Apoint = MAX_DEBUG_RECORD - 1;
+ };
+ }
+}
+
+/* this routine generates the array descriptor for a given array */
+static
+array_suffix (spnt2)
+ struct VMS_DBG_Symbol *spnt2;
+{
+ struct VMS_DBG_Symbol *spnt;
+ struct VMS_DBG_Symbol *spnt1;
+ int rank;
+ int total_size;
+ int i;
+ rank = 0;
+ spnt = spnt2;
+ while (spnt->advanced != ARRAY)
+ {
+ spnt = find_symbol (spnt->type2);
+ if (spnt == (struct VMS_DBG_Symbol *) NULL)
+ return;
+ };
+ spnt1 = spnt;
+ spnt1 = spnt;
+ total_size = 1;
+ while (spnt1->advanced == ARRAY)
+ {
+ rank++;
+ total_size *= (spnt1->index_max - spnt1->index_min + 1);
+ spnt1 = find_symbol (spnt1->type2);
+ };
+ total_size = total_size * spnt1->data_size;
+ push (spnt1->data_size, 2);
+ if (spnt1->VMS_type == 0xa3)
+ push (0, 1);
+ else
+ push (spnt1->VMS_type, 1);
+ push (4, 1);
+ for (i = 0; i < 6; i++)
+ push (0, 1);
+ push (0xc0, 1);
+ push (rank, 1);
+ push (total_size, 4);
+ push (0, 4);
+ spnt1 = spnt;
+ while (spnt1->advanced == ARRAY)
+ {
+ push (spnt1->index_max - spnt1->index_min + 1, 4);
+ spnt1 = find_symbol (spnt1->type2);
+ };
+ spnt1 = spnt;
+ while (spnt1->advanced == ARRAY)
+ {
+ push (spnt1->index_min, 4);
+ push (spnt1->index_max, 4);
+ spnt1 = find_symbol (spnt1->type2);
+ };
+}
+
+/* this routine generates the start of a variable descriptor based upon
+ * a struct/union/enum that has yet to be defined. We define this spot as
+ * a new location, and save four bytes for the address. When the struct is
+ * finally defined, then we can go back and plug in the correct address
+*/
+static
+new_forward_ref (dbx_type)
+ int dbx_type;
+{
+ struct forward_ref *fpnt;
+ fpnt = (struct forward_ref *) malloc (sizeof (struct forward_ref));
+ fpnt->next = f_ref_root;
+ f_ref_root = fpnt;
+ fpnt->dbx_type = dbx_type;
+ fpnt->struc_numb = ++structure_count;
+ fpnt->resolved = 'N';
+ push (3, -1);
+ total_len = 5;
+ push (total_len, -2);
+ struct_number = -fpnt->struc_numb;
+}
+
+/* this routine generates the variable descriptor used to describe non-basic
+ * variables. It calls itself recursively until it gets to the bottom of it
+ * all, and then builds the descriptor backwards. It is easiest to do it this
+ *way since we must periodically write length bytes, and it is easiest if we know
+ *the value when it is time to write it.
+ */
+static int
+gen1 (spnt, array_suffix_len)
+ struct VMS_DBG_Symbol *spnt;
+ int array_suffix_len;
+{
+ struct VMS_DBG_Symbol *spnt1;
+ int i;
+ switch (spnt->advanced)
+ {
+ case VOID:
+ push (DBG_S_C_VOID, -1);
+ total_len += 1;
+ push (total_len, -2);
+ return 0;
+ case BASIC:
+ case FUNCTION:
+ if (array_suffix_len == 0)
+ {
+ push (spnt->VMS_type, -1);
+ push (DBG_S_C_BASIC, -1);
+ total_len = 2;
+ push (total_len, -2);
+ return 1;
+ };
+ push (0, -4);
+ push (0xfa02, -2);
+ total_len = -2;
+ return 1;
+ case STRUCT:
+ case UNION:
+ case ENUM:
+ struct_number = spnt->struc_numb;
+ if (struct_number < 0)
+ {
+ new_forward_ref (spnt->dbx_type);
+ return 1;
+ }
+ push (DBG_S_C_STRUCT, -1);
+ total_len = 5;
+ push (total_len, -2);
+ return 1;
+ case POINTER:
+ spnt1 = find_symbol (spnt->type2);
+ i = 1;
+ if (spnt1 == (struct VMS_DBG_Symbol *) NULL)
+ new_forward_ref (spnt->type2);
+ else
+ i = gen1 (spnt1, 0);
+ if (i)
+ { /* (*void) is a special case, do not put pointer suffix*/
+ push (DBG_S_C_POINTER, -1);
+ total_len += 3;
+ push (total_len, -2);
+ };
+ return 1;
+ case ARRAY:
+ spnt1 = spnt;
+ while (spnt1->advanced == ARRAY)
+ {
+ spnt1 = find_symbol (spnt1->type2);
+ if (spnt1 == (struct VMS_DBG_Symbol *) NULL)
+ {
+ printf ("gcc-as warning(debugger output):");
+ printf ("Forward reference error, dbx type %d\n",
+ spnt->type2);
+ return;
+ }
+ };
+/* It is too late to generate forward references, so the user gets a message.
+ * This should only happen on a compiler error */
+ i = gen1 (spnt1, 1);
+ i = Apoint;
+ array_suffix (spnt);
+ array_suffix_len = Apoint - i;
+ switch (spnt1->advanced)
+ {
+ case BASIC:
+ case FUNCTION:
+ break;
+ default:
+ push (0, -2);
+ total_len += 2;
+ push (total_len, -2);
+ push (0xfa, -1);
+ push (0x0101, -2);
+ push (DBG_S_C_COMPLEX_ARRAY, -1);
+ };
+ total_len += array_suffix_len + 8;
+ push (total_len, -2);
+ };
+}
+
+/* This generates a suffix for a variable. If it is not a defined type yet,
+ * then dbx_type contains the type we are expecting so we can generate a
+ * forward reference. This calls gen1 to build most of the descriptor, and
+ * then it puts the icing on at the end. It then dumps whatever is needed
+ * to get a complete descriptor (i.e. struct reference, array suffix ).
+ */
+static
+generate_suffix (spnt, dbx_type)
+ struct VMS_DBG_Symbol *spnt;
+ int dbx_type;
+{
+ int ilen;
+ int i;
+ char pvoid[6] =
+ {5, 0xaf, 0, 1, 0, 5};
+ struct VMS_DBG_Symbol *spnt1;
+ Apoint = 0;
+ Lpnt = MAX_DEBUG_RECORD - 1;
+ total_len = 0;
+ struct_number = 0;
+ overflow = 0;
+ if (spnt == (struct VMS_DBG_Symbol *) NULL)
+ new_forward_ref (dbx_type);
+ else
+ {
+ if (spnt->VMS_type != 0xa3)
+ return 0; /* no suffix needed */
+ gen1 (spnt, 0);
+ };
+ push (0x00af, -2);
+ total_len += 4;
+ push (total_len, -1);
+/* if the variable descriptor overflows the record, output a descriptor for
+ * a pointer to void.
+ */
+ if ((total_len >= MAX_DEBUG_RECORD) || overflow)
+ {
+ printf (" Variable descriptor %d too complicated. Defined as *void ", spnt->dbx_type);
+ VMS_Store_Immediate_Data (pvoid, 6, OBJ_S_C_DBG);
+ return;
+ };
+ i = 0;
+ while (Lpnt < MAX_DEBUG_RECORD - 1)
+ Local[i++] = Local[++Lpnt];
+ Lpnt = i;
+/* we use this for a reference to a structure that has already been defined */
+ if (struct_number > 0)
+ {
+ VMS_Store_Immediate_Data (Local, Lpnt, OBJ_S_C_DBG);
+ Lpnt = 0;
+ VMS_Store_Struct (struct_number);
+ };
+/* we use this for a forward reference to a structure that has yet to be
+*defined. We store four bytes of zero to make room for the actual address once
+* it is known
+*/
+ if (struct_number < 0)
+ {
+ struct_number = -struct_number;
+ VMS_Store_Immediate_Data (Local, Lpnt, OBJ_S_C_DBG);
+ Lpnt = 0;
+ VMS_Def_Struct (struct_number);
+ for (i = 0; i < 4; i++)
+ Local[Lpnt++] = 0;
+ VMS_Store_Immediate_Data (Local, Lpnt, OBJ_S_C_DBG);
+ Lpnt = 0;
+ };
+ i = 0;
+ while (i < Apoint)
+ Local[Lpnt++] = Asuffix[i++];
+ if (Lpnt != 0)
+ VMS_Store_Immediate_Data (Local, Lpnt, OBJ_S_C_DBG);
+ Lpnt = 0;
+}
+
+/* This routine generates a symbol definition for a C sybmol for the debugger.
+ * It takes a psect and offset for global symbols - if psect < 0, then this is
+ * a local variable and the offset is relative to FP. In this case it can
+ * be either a variable (Offset < 0) or a parameter (Offset > 0).
+ */
+static
+VMS_DBG_record (spnt, Psect, Offset, Name)
+ struct VMS_DBG_Symbol *spnt;
+ int Psect;
+ int Offset;
+ char *Name;
+{
+ char *pnt;
+ char *Name_pnt;
+ int j;
+ int maxlen;
+ int i = 0;
+ Name_pnt = fix_name (Name); /* if there are bad characters in name, convert them */
+ if (Psect < 0)
+ { /* this is a local variable, referenced to SP */
+ maxlen = 7 + strlen (Name_pnt);
+ Local[i++] = maxlen;
+ Local[i++] = spnt->VMS_type;
+ if (Offset > 0)
+ Local[i++] = DBG_S_C_FUNCTION_PARAMETER;
+ else
+ Local[i++] = DBG_S_C_LOCAL_SYM;
+ pnt = (char *) &Offset;
+ for (j = 0; j < 4; j++)
+ Local[i++] = *pnt++; /* copy the offset */
+ }
+ else
+ {
+ maxlen = 7 + strlen (Name_pnt); /* symbols fixed in memory */
+ Local[i++] = 7 + strlen (Name_pnt);
+ Local[i++] = spnt->VMS_type;
+ Local[i++] = 1;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ i = 0;
+ VMS_Set_Data (Psect, Offset, OBJ_S_C_DBG, 0);
+ }
+ Local[i++] = strlen (Name_pnt);
+ while (*Name_pnt != '\0')
+ Local[i++] = *Name_pnt++;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ if (spnt->VMS_type == DBG_S_C_ADVANCED_TYPE)
+ generate_suffix (spnt, 0);
+}
+
+
+/* This routine parses the stabs entries in order to make the definition
+ * for the debugger of local symbols and function parameters
+ */
+static int
+VMS_local_stab_Parse (sp)
+ symbolS *sp;
+{
+ char *pnt;
+ char *pnt1;
+ char *str;
+ struct VMS_DBG_Symbol *spnt;
+ struct VMS_Symbol *vsp;
+ int dbx_type;
+ int VMS_type;
+ dbx_type = 0;
+ str = S_GET_NAME (sp);
+ pnt = (char *) strchr (str, ':');
+ if (pnt == (char *) NULL)
+ return; /* no colon present */
+ pnt1 = pnt++; /* save this for later, and skip colon */
+ if (*pnt == 'c')
+ return 0; /* ignore static constants */
+/* there is one little catch that we must be aware of. Sometimes function
+ * parameters are optimized into registers, and the compiler, in its infiite
+ * wisdom outputs stabs records for *both*. In general we want to use the
+ * register if it is present, so we must search the rest of the symbols for
+ * this function to see if this parameter is assigned to a register.
+ */
+ {
+ char *str1;
+ char *pnt2;
+ symbolS *sp1;
+ if (*pnt == 'p')
+ {
+ for (sp1 = symbol_next (sp); sp1; sp1 = symbol_next (sp1))
+ {
+ if (!S_IS_DEBUG (sp1))
+ continue;
+ if (S_GET_RAW_TYPE (sp1) == N_FUN)
+ {
+ char * pnt3=(char*) strchr (S_GET_NAME (sp1), ':') + 1;
+ if (*pnt3 == 'F' || *pnt3 == 'f') break;
+ };
+ if (S_GET_RAW_TYPE (sp1) != N_RSYM)
+ continue;
+ str1 = S_GET_NAME (sp1); /* and get the name */
+ pnt2 = str;
+ while (*pnt2 != ':')
+ {
+ if (*pnt2 != *str1)
+ break;
+ pnt2++;
+ str1++;
+ };
+ if ((*str1 != ':') || (*pnt2 != ':'))
+ continue;
+ return; /* they are the same! lets skip this one */
+ }; /* for */
+/* first find the dbx symbol type from list, and then find VMS type */
+ pnt++; /* skip p in case no register */
+ }; /* if */
+ }; /* p block */
+ pnt = cvt_integer (pnt, &dbx_type);
+ spnt = find_symbol (dbx_type);
+ if (spnt == (struct VMS_DBG_Symbol *) NULL)
+ return 0; /*Dunno what this is*/
+ *pnt1 = '\0';
+ VMS_DBG_record (spnt, -1, S_GET_VALUE (sp), str);
+ *pnt1 = ':'; /* and restore the string */
+ return 1;
+}
+
+/* This routine parses a stabs entry to find the information required to define
+ * a variable. It is used for global and static variables.
+ * Basically we need to know the address of the symbol. With older versions
+ * of the compiler, const symbols are
+ * treated differently, in that if they are global they are written into the
+ * text psect. The global symbol entry for such a const is actually written
+ * as a program entry point (Yuk!!), so if we cannot find a symbol in the list
+ * of psects, we must search the entry points as well. static consts are even
+ * harder, since they are never assigned a memory address. The compiler passes
+ * a stab to tell us the value, but I am not sure what to do with it.
+ */
+
+static
+VMS_stab_parse (sp, expected_type, type1, type2, Text_Psect)
+ symbolS *sp;
+ char expected_type;
+ int type1, type2, Text_Psect;
+{
+ char *pnt;
+ char *pnt1;
+ char *str;
+ symbolS *sp1;
+ struct VMS_DBG_Symbol *spnt;
+ struct VMS_Symbol *vsp;
+ int dbx_type;
+ int VMS_type;
+ dbx_type = 0;
+ str = S_GET_NAME (sp);
+ pnt = (char *) strchr (str, ':');
+ if (pnt == (char *) NULL)
+ return; /* no colon present */
+ pnt1 = pnt; /* save this for later*/
+ pnt++;
+ if (*pnt == expected_type)
+ {
+ pnt = cvt_integer (pnt + 1, &dbx_type);
+ spnt = find_symbol (dbx_type);
+ if (spnt == (struct VMS_DBG_Symbol *) NULL)
+ return 0; /*Dunno what this is*/
+/* now we need to search the symbol table to find the psect and offset for
+ * this variable.
+ */
+ *pnt1 = '\0';
+ vsp = VMS_Symbols;
+ while (vsp != (struct VMS_Symbol *) NULL)
+ {
+ pnt = S_GET_NAME (vsp->Symbol);
+ if (pnt != (char *) NULL)
+ if (*pnt++ == '_')
+/* make sure name is the same, and make sure correct symbol type */
+ if ((strlen (pnt) == strlen (str)) && (strcmp (pnt, str) == 0)
+ && ((S_GET_RAW_TYPE (vsp->Symbol) == type1) ||
+ (S_GET_RAW_TYPE (vsp->Symbol) == type2)))
+ break;
+ vsp = vsp->Next;
+ };
+ if (vsp != (struct VMS_Symbol *) NULL)
+ {
+ VMS_DBG_record (spnt, vsp->Psect_Index, vsp->Psect_Offset, str);
+ *pnt1 = ':'; /* and restore the string */
+ return 1;
+ };
+/* the symbol was not in the symbol list, but it may be an "entry point"
+ if it was a constant */
+ for (sp1 = symbol_rootP; sp1; sp1 = symbol_next (sp1))
+ {
+ /*
+ * Dispatch on STAB type
+ */
+ if (S_IS_DEBUG (sp1) || (S_GET_TYPE (sp1) != N_TEXT))
+ continue;
+ pnt = S_GET_NAME (sp1);
+ if (*pnt == '_')
+ pnt++;
+ if (strcmp (pnt, str) == 0)
+ {
+ if (!gave_compiler_message && expected_type == 'G')
+ {
+ printf ("***Warning - the assembly code generated by the compiler has placed\n");
+ printf ("global constant(s) in the text psect. These will not be available to\n");
+ printf ("other modules, since this is not the correct way to handle this. You\n");
+ printf ("have two options: 1) get a patched compiler that does not put global\n");
+ printf ("constants in the text psect, or 2) remove the 'const' keyword from\n");
+ printf ("definitions of global variables in your source module(s). Don't say\n");
+ printf ("I didn't warn you!");
+ gave_compiler_message = 1;
+ };
+ VMS_DBG_record (spnt,
+ Text_Psect,
+ S_GET_VALUE (sp1),
+ str);
+ *pnt1 = ':';
+ *S_GET_NAME (sp1) = 'L';
+ /* fool assembler to not output this
+ * as a routine in the TBT */
+ return 1;
+ };
+ };
+ };
+ *pnt1 = ':'; /* and restore the string */
+ return 0;
+}
+
+static
+VMS_GSYM_Parse (sp, Text_Psect)
+ symbolS *sp;
+ int Text_Psect;
+{ /* Global variables */
+ VMS_stab_parse (sp, 'G', (N_UNDF | N_EXT), (N_DATA | N_EXT), Text_Psect);
+}
+
+
+static
+VMS_LCSYM_Parse (sp, Text_Psect)
+ symbolS *sp;
+ int Text_Psect;
+{ /* Static symbols - uninitialized */
+ VMS_stab_parse (sp, 'S', N_BSS, -1, Text_Psect);
+}
+
+static
+VMS_STSYM_Parse (sp, Text_Psect)
+ symbolS *sp;
+ int Text_Psect;
+{ /* Static symbols - initialized */
+ VMS_stab_parse (sp, 'S', N_DATA, -1, Text_Psect);
+}
+
+
+/* for register symbols, we must figure out what range of addresses within the
+ * psect are valid. We will use the brackets in the stab directives to give us
+ * guidance as to the PC range that this variable is in scope. I am still not
+ * completely comfortable with this but as I learn more, I seem to get a better
+ * handle on what is going on.
+ * Caveat Emptor.
+ */
+static
+VMS_RSYM_Parse (sp, Current_Routine, Text_Psect)
+ symbolS *sp, *Current_Routine;
+ int Text_Psect;
+{
+ char *pnt;
+ char *pnt1;
+ char *str;
+ int dbx_type;
+ struct VMS_DBG_Symbol *spnt;
+ int j;
+ int maxlen;
+ int i = 0;
+ int bcnt = 0;
+ int Min_Offset = -1; /* min PC of validity */
+ int Max_Offset = 0; /* max PC of validity */
+ symbolS *symbolP;
+ for (symbolP = sp; symbolP; symbolP = symbol_next (symbolP))
+ {
+ /*
+ * Dispatch on STAB type
+ */
+ switch (S_GET_RAW_TYPE (symbolP))
+ {
+ case N_LBRAC:
+ if (bcnt++ == 0)
+ Min_Offset = S_GET_VALUE (symbolP);
+ break;
+ case N_RBRAC:
+ if (--bcnt == 0)
+ Max_Offset =
+ S_GET_VALUE (symbolP) - 1;
+ break;
+ }
+ if ((Min_Offset != -1) && (bcnt == 0))
+ break;
+ if (S_GET_RAW_TYPE (symbolP) == N_FUN)
+ {
+ pnt=(char*) strchr (S_GET_NAME (symbolP), ':') + 1;
+ if (*pnt == 'F' || *pnt == 'f') break;
+ };
+ }
+/* check to see that the addresses were defined. If not, then there were no
+ * brackets in the function, and we must try to search for the next function
+ * Since functions can be in any order, we should search all of the symbol list
+ * to find the correct ending address. */
+ if (Min_Offset == -1)
+ {
+ int Max_Source_Offset;
+ int This_Offset;
+ Min_Offset = S_GET_VALUE (sp);
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next (symbolP))
+ {
+ /*
+ * Dispatch on STAB type
+ */
+ This_Offset = S_GET_VALUE (symbolP);
+ switch (S_GET_RAW_TYPE (symbolP))
+ {
+ case N_TEXT | N_EXT:
+ if ((This_Offset > Min_Offset) && (This_Offset < Max_Offset))
+ Max_Offset = This_Offset;
+ break;
+ case N_SLINE:
+ if (This_Offset > Max_Source_Offset)
+ Max_Source_Offset = This_Offset;
+ }
+ }
+/* if this is the last routine, then we use the PC of the last source line
+ * as a marker of the max PC for which this reg is valid */
+ if (Max_Offset == 0x7fffffff)
+ Max_Offset = Max_Source_Offset;
+ };
+ dbx_type = 0;
+ str = S_GET_NAME (sp);
+ pnt = (char *) strchr (str, ':');
+ if (pnt == (char *) NULL)
+ return; /* no colon present */
+ pnt1 = pnt; /* save this for later*/
+ pnt++;
+ if (*pnt != 'r')
+ return 0;
+ pnt = cvt_integer (pnt + 1, &dbx_type);
+ spnt = find_symbol (dbx_type);
+ if (spnt == (struct VMS_DBG_Symbol *) NULL)
+ return 0; /*Dunno what this is yet*/
+ *pnt1 = '\0';
+ pnt = fix_name (S_GET_NAME (sp)); /* if there are bad characters in name, convert them */
+ maxlen = 25 + strlen (pnt);
+ Local[i++] = maxlen;
+ Local[i++] = spnt->VMS_type;
+ Local[i++] = 0xfb;
+ Local[i++] = strlen (pnt) + 1;
+ Local[i++] = 0x00;
+ Local[i++] = 0x00;
+ Local[i++] = 0x00;
+ Local[i++] = strlen (pnt);
+ while (*pnt != '\0')
+ Local[i++] = *pnt++;
+ Local[i++] = 0xfd;
+ Local[i++] = 0x0f;
+ Local[i++] = 0x00;
+ Local[i++] = 0x03;
+ Local[i++] = 0x01;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ i = 0;
+ VMS_Set_Data (Text_Psect, Min_Offset, OBJ_S_C_DBG, 1);
+ VMS_Set_Data (Text_Psect, Max_Offset, OBJ_S_C_DBG, 1);
+ Local[i++] = 0x03;
+ Local[i++] = S_GET_VALUE (sp);
+ Local[i++] = 0x00;
+ Local[i++] = 0x00;
+ Local[i++] = 0x00;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ *pnt1 = ':';
+ if (spnt->VMS_type == DBG_S_C_ADVANCED_TYPE)
+ generate_suffix (spnt, 0);
+}
+
+/* this function examines a structure definition, checking all of the elements
+ * to make sure that all of them are fully defined. The only thing that we
+ * kick out are arrays of undefined structs, since we do not know how big
+ * they are. All others we can handle with a normal forward reference.
+ */
+static int
+forward_reference (pnt)
+ char *pnt;
+{
+ int i;
+ struct VMS_DBG_Symbol *spnt;
+ struct VMS_DBG_Symbol *spnt1;
+ pnt = cvt_integer (pnt + 1, &i);
+ if (*pnt == ';')
+ return 0; /* no forward references */
+ do
+ {
+ pnt = (char *) strchr (pnt, ':');
+ pnt = cvt_integer (pnt + 1, &i);
+ spnt = find_symbol (i);
+ if (spnt == (struct VMS_DBG_Symbol *) NULL)
+ return 0;
+ while ((spnt->advanced == POINTER) || (spnt->advanced == ARRAY))
+ {
+ i = spnt->type2;
+ spnt1 = find_symbol (spnt->type2);
+ if ((spnt->advanced == ARRAY) &&
+ (spnt1 == (struct VMS_DBG_Symbol *) NULL))
+ return 1;
+ if (spnt1 == (struct VMS_DBG_Symbol *) NULL)
+ break;
+ spnt = spnt1;
+ };
+ pnt = cvt_integer (pnt + 1, &i);
+ pnt = cvt_integer (pnt + 1, &i);
+ } while (*++pnt != ';');
+ return 0; /* no forward refences found */
+}
+
+/* This routine parses the stabs directives to find any definitions of dbx type
+ * numbers. It makes a note of all of them, creating a structure element
+ * of VMS_DBG_Symbol that describes it. This also generates the info for the
+ * debugger that describes the struct/union/enum, so that further references
+ * to these data types will be by number
+ * We have to process pointers right away, since there can be references
+ * to them later in the same stabs directive. We cannot have forward
+ * references to pointers, (but we can have a forward reference to a pointer to
+ * a structure/enum/union) and this is why we process them immediately.
+ * After we process the pointer, then we search for defs that are nested even
+ * deeper.
+ */
+static int
+VMS_typedef_parse (str)
+ char *str;
+{
+ char *pnt;
+ char *pnt1;
+ char *pnt2;
+ int i;
+ int dtype;
+ struct forward_ref *fpnt;
+ int i1, i2, i3;
+ int convert_integer;
+ struct VMS_DBG_Symbol *spnt;
+ struct VMS_DBG_Symbol *spnt1;
+/* check for any nested def's */
+ pnt = (char *) strchr (str + 1, '=');
+ if ((pnt != (char *) NULL) && (*(str + 1) != '*'))
+ if (VMS_typedef_parse (pnt) == 1)
+ return 1;
+/* now find dbx_type of entry */
+ pnt = str - 1;
+ if (*pnt == 'c')
+ { /* check for static constants */
+ *str = '\0'; /* for now we ignore them */
+ return 0;
+ };
+ while ((*pnt <= '9') && (*pnt >= '0'))
+ pnt--;
+ pnt++; /* and get back to the number */
+ cvt_integer (pnt, &i1);
+ spnt = find_symbol (i1);
+/* first we see if this has been defined already, due to a forward reference*/
+ if (spnt == (struct VMS_DBG_Symbol *) NULL)
+ {
+ if (VMS_Symbol_type_list == (struct VMS_DBG_Symbol *) NULL)
+ {
+ spnt = (struct VMS_DBG_Symbol *) malloc (sizeof (struct VMS_DBG_Symbol));
+ spnt->next = (struct VMS_DBG_Symbol *) NULL;
+ VMS_Symbol_type_list = spnt;
+ }
+ else
+ {
+ spnt = (struct VMS_DBG_Symbol *) malloc (sizeof (struct VMS_DBG_Symbol));
+ spnt->next = VMS_Symbol_type_list;
+ VMS_Symbol_type_list = spnt;
+ };
+ spnt->dbx_type = i1; /* and save the type */
+ };
+/* for structs and unions, do a partial parse, otherwise we sometimes get
+ * circular definitions that are impossible to resolve. We read enough info
+ * so that any reference to this type has enough info to be resolved
+ */
+ pnt = str + 1; /* point to character past equal sign */
+ if ((*pnt == 'u') || (*pnt == 's'))
+ {
+ };
+ if ((*pnt <= '9') && (*pnt >= '0'))
+ {
+ if (type_check ("void"))
+ { /* this is the void symbol */
+ *str = '\0';
+ spnt->advanced = VOID;
+ return 0;
+ };
+ if (type_check ("unknown type"))
+ { /* this is the void symbol */
+ *str = '\0';
+ spnt->advanced = UNKNOWN;
+ return 0;
+ };
+ printf ("gcc-as warning(debugger output):");
+ printf (" %d is an unknown untyped variable.\n", spnt->dbx_type);
+ return 1; /* do not know what this is */
+ };
+/* now define this module*/
+ pnt = str + 1; /* point to character past equal sign */
+ switch (*pnt)
+ {
+ case 'r':
+ spnt->advanced = BASIC;
+ if (type_check ("int"))
+ {
+ spnt->VMS_type = DBG_S_C_SLINT;
+ spnt->data_size = 4;
+ }
+ else if (type_check ("long int"))
+ {
+ spnt->VMS_type = DBG_S_C_SLINT;
+ spnt->data_size = 4;
+ }
+ else if (type_check ("unsigned int"))
+ {
+ spnt->VMS_type = DBG_S_C_ULINT;
+ spnt->data_size = 4;
+ }
+ else if (type_check ("long unsigned int"))
+ {
+ spnt->VMS_type = DBG_S_C_ULINT;
+ spnt->data_size = 4;
+ }
+ else if (type_check ("short int"))
+ {
+ spnt->VMS_type = DBG_S_C_SSINT;
+ spnt->data_size = 2;
+ }
+ else if (type_check ("short unsigned int"))
+ {
+ spnt->VMS_type = DBG_S_C_USINT;
+ spnt->data_size = 2;
+ }
+ else if (type_check ("char"))
+ {
+ spnt->VMS_type = DBG_S_C_SCHAR;
+ spnt->data_size = 1;
+ }
+ else if (type_check ("signed char"))
+ {
+ spnt->VMS_type = DBG_S_C_SCHAR;
+ spnt->data_size = 1;
+ }
+ else if (type_check ("unsigned char"))
+ {
+ spnt->VMS_type = DBG_S_C_UCHAR;
+ spnt->data_size = 1;
+ }
+ else if (type_check ("float"))
+ {
+ spnt->VMS_type = DBG_S_C_REAL4;
+ spnt->data_size = 4;
+ }
+ else if (type_check ("double"))
+ {
+ spnt->VMS_type = DBG_S_C_REAL8;
+ spnt->data_size = 8;
+ }
+ pnt1 = (char *) strchr (str, ';') + 1;
+ break;
+ case 's':
+ case 'u':
+ if (*pnt == 's')
+ spnt->advanced = STRUCT;
+ else
+ spnt->advanced = UNION;
+ spnt->VMS_type = DBG_S_C_ADVANCED_TYPE;
+ pnt1 = cvt_integer (pnt + 1, &spnt->data_size);
+ if (forward_reference (pnt))
+ {
+ spnt->struc_numb = -1;
+ return 1;
+ }
+ spnt->struc_numb = ++structure_count;
+ pnt1--;
+ pnt = get_struct_name (str);
+ VMS_Def_Struct (spnt->struc_numb);
+ fpnt = f_ref_root;
+ while (fpnt != (struct forward_ref *) NULL)
+ {
+ if (fpnt->dbx_type == spnt->dbx_type)
+ {
+ fpnt->resolved = 'Y';
+ VMS_Set_Struct (fpnt->struc_numb);
+ VMS_Store_Struct (spnt->struc_numb);
+ };
+ fpnt = fpnt->next;
+ };
+ VMS_Set_Struct (spnt->struc_numb);
+ i = 0;
+ Local[i++] = 11 + strlen (pnt);
+ Local[i++] = DBG_S_C_STRUCT_START;
+ Local[i++] = 0x80;
+ for (i1 = 0; i1 < 4; i1++)
+ Local[i++] = 0x00;
+ Local[i++] = strlen (pnt);
+ pnt2 = pnt;
+ while (*pnt2 != '\0')
+ Local[i++] = *pnt2++;
+ i2 = spnt->data_size * 8; /* number of bits */
+ pnt2 = (char *) &i2;
+ for (i1 = 0; i1 < 4; i1++)
+ Local[i++] = *pnt2++;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ i = 0;
+ if (pnt != symbol_name)
+ {
+ pnt += strlen (pnt);
+ *pnt = ':';
+ }; /* replace colon for later */
+ while (*++pnt1 != ';')
+ {
+ pnt = (char *) strchr (pnt1, ':');
+ *pnt = '\0';
+ pnt2 = pnt1;
+ pnt1 = cvt_integer (pnt + 1, &dtype);
+ pnt1 = cvt_integer (pnt1 + 1, &i2);
+ pnt1 = cvt_integer (pnt1 + 1, &i3);
+ if ((dtype == 1) && (i3 != 32))
+ { /* bitfield */
+ Apoint = 0;
+ push (19 + strlen (pnt2), 1);
+ push (0xfa22, 2);
+ push (1 + strlen (pnt2), 4);
+ push (strlen (pnt2), 1);
+ while (*pnt2 != '\0')
+ push (*pnt2++, 1);
+ push (i3, 2); /* size of bitfield */
+ push (0x0d22, 2);
+ push (0x00, 4);
+ push (i2, 4); /* start position */
+ VMS_Store_Immediate_Data (Asuffix, Apoint, OBJ_S_C_DBG);
+ Apoint = 0;
+ }
+ else
+ {
+ Local[i++] = 7 + strlen (pnt2);
+ spnt1 = find_symbol (dtype);
+ /* check if this is a forward reference */
+ if (spnt1 != (struct VMS_DBG_Symbol *) NULL)
+ Local[i++] = spnt1->VMS_type;
+ else
+ Local[i++] = DBG_S_C_ADVANCED_TYPE;
+ Local[i++] = DBG_S_C_STRUCT_ITEM;
+ pnt = (char *) &i2;
+ for (i1 = 0; i1 < 4; i1++)
+ Local[i++] = *pnt++;
+ Local[i++] = strlen (pnt2);
+ while (*pnt2 != '\0')
+ Local[i++] = *pnt2++;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ i = 0;
+ if (spnt1 == (struct VMS_DBG_Symbol *) NULL)
+ generate_suffix (spnt1, dtype);
+ else if (spnt1->VMS_type == DBG_S_C_ADVANCED_TYPE)
+ generate_suffix (spnt1, 0);
+ };
+ };
+ pnt1++;
+ Local[i++] = 0x01; /* length byte */
+ Local[i++] = DBG_S_C_STRUCT_END;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ i = 0;
+ break;
+ case 'e':
+ spnt->advanced = ENUM;
+ spnt->VMS_type = DBG_S_C_ADVANCED_TYPE;
+ spnt->struc_numb = ++structure_count;
+ spnt->data_size = 4;
+ VMS_Def_Struct (spnt->struc_numb);
+ fpnt = f_ref_root;
+ while (fpnt != (struct forward_ref *) NULL)
+ {
+ if (fpnt->dbx_type == spnt->dbx_type)
+ {
+ fpnt->resolved = 'Y';
+ VMS_Set_Struct (fpnt->struc_numb);
+ VMS_Store_Struct (spnt->struc_numb);
+ };
+ fpnt = fpnt->next;
+ };
+ VMS_Set_Struct (spnt->struc_numb);
+ i = 0;
+ Local[i++] = 3 + strlen (symbol_name);
+ Local[i++] = DBG_S_C_ENUM_START;
+ Local[i++] = 0x20;
+ Local[i++] = strlen (symbol_name);
+ pnt2 = symbol_name;
+ while (*pnt2 != '\0')
+ Local[i++] = *pnt2++;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ i = 0;
+ while (*++pnt != ';')
+ {
+ pnt1 = (char *) strchr (pnt, ':');
+ *pnt1++ = '\0';
+ pnt1 = cvt_integer (pnt1, &i1);
+ Local[i++] = 7 + strlen (pnt);
+ Local[i++] = DBG_S_C_ENUM_ITEM;
+ Local[i++] = 0x00;
+ pnt2 = (char *) &i1;
+ for (i2 = 0; i2 < 4; i2++)
+ Local[i++] = *pnt2++;
+ Local[i++] = strlen (pnt);
+ pnt2 = pnt;
+ while (*pnt != '\0')
+ Local[i++] = *pnt++;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ i = 0;
+ pnt = pnt1; /* Skip final semicolon */
+ };
+ Local[i++] = 0x01; /* len byte */
+ Local[i++] = DBG_S_C_ENUM_END;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ i = 0;
+ pnt1 = pnt + 1;
+ break;
+ case 'a':
+ spnt->advanced = ARRAY;
+ spnt->VMS_type = DBG_S_C_ADVANCED_TYPE;
+ pnt = (char *) strchr (pnt, ';');
+ if (pnt == (char *) NULL)
+ return 1;
+ pnt1 = cvt_integer (pnt + 1, &spnt->index_min);
+ pnt1 = cvt_integer (pnt1 + 1, &spnt->index_max);
+ pnt1 = cvt_integer (pnt1 + 1, &spnt->type2);
+ break;
+ case 'f':
+ spnt->advanced = FUNCTION;
+ spnt->VMS_type = DBG_S_C_FUNCTION_ADDR;
+ /* this masquerades as a basic type*/
+ spnt->data_size = 4;
+ pnt1 = cvt_integer (pnt + 1, &spnt->type2);
+ break;
+ case '*':
+ spnt->advanced = POINTER;
+ spnt->VMS_type = DBG_S_C_ADVANCED_TYPE;
+ spnt->data_size = 4;
+ pnt1 = cvt_integer (pnt + 1, &spnt->type2);
+ pnt = (char *) strchr (str + 1, '=');
+ if ((pnt != (char *) NULL))
+ if (VMS_typedef_parse (pnt) == 1)
+ return 1;
+ break;
+ default:
+ spnt->advanced = UNKNOWN;
+ spnt->VMS_type = 0;
+ printf ("gcc-as warning(debugger output):");
+ printf (" %d is an unknown type of variable.\n", spnt->dbx_type);
+ return 1; /* unable to decipher */
+ };
+/* this removes the evidence of the definition so that the outer levels of
+parsing do not have to worry about it */
+ pnt = str;
+ while (*pnt1 != '\0')
+ *pnt++ = *pnt1++;
+ *pnt = '\0';
+ return 0;
+}
+
+
+/*
+ * This is the root routine that parses the stabs entries for definitions.
+ * it calls VMS_typedef_parse, which can in turn call itself.
+ * We need to be careful, since sometimes there are forward references to
+ * other symbol types, and these cannot be resolved until we have completed
+ * the parse.
+ */
+static int
+VMS_LSYM_Parse ()
+{
+ char *pnt;
+ char *pnt1;
+ char *pnt2;
+ char *str;
+ char fixit[10];
+ int incomplete, i, pass, incom1;
+ struct VMS_DBG_Symbol *spnt;
+ struct VMS_Symbol *vsp;
+ struct forward_ref *fpnt;
+ symbolS *sp;
+ pass = 0;
+ incomplete = 0;
+ do
+ {
+ incom1 = incomplete;
+ incomplete = 0;
+ for (sp = symbol_rootP; sp; sp = symbol_next (sp))
+ {
+ /*
+ * Deal with STAB symbols
+ */
+ if (S_IS_DEBUG (sp))
+ {
+ /*
+ * Dispatch on STAB type
+ */
+ switch (S_GET_RAW_TYPE (sp))
+ {
+ case N_GSYM:
+ case N_LCSYM:
+ case N_STSYM:
+ case N_PSYM:
+ case N_RSYM:
+ case N_LSYM:
+ case N_FUN: /*sometimes these contain typedefs*/
+ str = S_GET_NAME (sp);
+ symbol_name = str;
+ pnt = (char *) strchr (str, ':');
+ if (pnt == (char *) NULL)
+ break;
+ *pnt = '\0';
+ pnt1 = pnt + 1;
+ pnt2 = (char *) strchr (pnt1, '=');
+ if (pnt2 == (char *) NULL)
+ {
+ *pnt = ':'; /* replace colon */
+ break;
+ }; /* no symbol here */
+ incomplete += VMS_typedef_parse (pnt2);
+ *pnt = ':'; /* put back colon so variable def code finds dbx_type*/
+ break;
+ } /*switch*/
+ } /* if */
+ } /*for*/
+ pass++;
+ } while ((incomplete != 0) && (incomplete != incom1));
+ /* repeat until all refs resolved if possible */
+/* if (pass > 1) printf(" Required %d passes\n",pass);*/
+ if (incomplete != 0)
+ {
+ printf ("gcc-as warning(debugger output):");
+ printf ("Unable to resolve %d circular references.\n", incomplete);
+ };
+ fpnt = f_ref_root;
+ symbol_name = "\0";
+ while (fpnt != (struct forward_ref *) NULL)
+ {
+ if (fpnt->resolved != 'Y')
+ {
+ if (find_symbol (fpnt->dbx_type) !=
+ (struct VMS_DBG_Symbol *) NULL)
+ {
+ printf ("gcc-as warning(debugger output):");
+ printf ("Forward reference error, dbx type %d\n",
+ fpnt->dbx_type);
+ break;
+ };
+ fixit[0] = 0;
+ sprintf (&fixit[1], "%d=s4;", fpnt->dbx_type);
+ pnt2 = (char *) strchr (&fixit[1], '=');
+ VMS_typedef_parse (pnt2);
+ };
+ fpnt = fpnt->next;
+ };
+}
+
+static
+Define_Local_Symbols (s1, s2)
+ symbolS *s1, *s2;
+{
+ symbolS *symbolP1;
+ for (symbolP1 = symbol_next (s1); symbolP1 != s2; symbolP1 = symbol_next (symbolP1))
+ {
+ if (symbolP1 == (symbolS *) NULL)
+ return;
+ if (S_GET_RAW_TYPE (symbolP1) == N_FUN)
+ {
+ char * pnt=(char*) strchr (S_GET_NAME (symbolP1), ':') + 1;
+ if (*pnt == 'F' || *pnt == 'f') break;
+ };
+ /*
+ * Deal with STAB symbols
+ */
+ if (S_IS_DEBUG (symbolP1))
+ {
+ /*
+ * Dispatch on STAB type
+ */
+ switch (S_GET_RAW_TYPE (symbolP1))
+ {
+ case N_LSYM:
+ case N_PSYM:
+ VMS_local_stab_Parse (symbolP1);
+ break;
+ case N_RSYM:
+ VMS_RSYM_Parse (symbolP1, Current_Routine, Text_Psect);
+ break;
+ } /*switch*/
+ } /* if */
+ } /* for */
+}
+
+
+/* This function crawls the symbol chain searching for local symbols that need
+ * to be described to the debugger. When we enter a new scope with a "{", it
+ * creates a new "block", which helps the debugger keep track of which scope
+ * we are currently in.
+ */
+
+static symbolS *
+Define_Routine (symbolP, Level)
+ symbolS *symbolP;
+ int Level;
+{
+ symbolS *sstart;
+ symbolS *symbolP1;
+ char str[10];
+ int rcount = 0;
+ int Offset;
+ sstart = symbolP;
+ for (symbolP1 = symbol_next (symbolP); symbolP1; symbolP1 = symbol_next (symbolP1))
+ {
+ if (S_GET_RAW_TYPE (symbolP1) == N_FUN)
+ {
+ char * pnt=(char*) strchr (S_GET_NAME (symbolP1), ':') + 1;
+ if (*pnt == 'F' || *pnt == 'f') break;
+ };
+ /*
+ * Deal with STAB symbols
+ */
+ if (S_IS_DEBUG (symbolP1))
+ {
+ /*
+ * Dispatch on STAB type
+ */
+ switch (S_GET_RAW_TYPE (symbolP1))
+ {
+ case N_LBRAC:
+ if (Level != 0)
+ {
+ sprintf (str, "$%d", rcount++);
+ VMS_TBT_Block_Begin (symbolP1, Text_Psect, str);
+ };
+ Offset = S_GET_VALUE (symbolP1);
+ Define_Local_Symbols (sstart, symbolP1);
+ symbolP1 =
+ Define_Routine (symbolP1, Level + 1);
+ if (Level != 0)
+ VMS_TBT_Block_End (S_GET_VALUE (symbolP1) -
+ Offset);
+ sstart = symbolP1;
+ break;
+ case N_RBRAC:
+ return symbolP1;
+ } /*switch*/
+ } /* if */
+ } /* for */
+ /* we end up here if there were no brackets in this function. Define
+everything */
+ Define_Local_Symbols (sstart, (symbolS *) 0);
+ return symbolP1;
+}
+
+
+static
+VMS_DBG_Define_Routine (symbolP, Curr_Routine, Txt_Psect)
+ symbolS *symbolP;
+ symbolS *Curr_Routine;
+ int Txt_Psect;
+{
+ Current_Routine = Curr_Routine;
+ Text_Psect = Txt_Psect;
+ Define_Routine (symbolP, 0);
+}
+
+
+
+
+#ifndef HO_VMS
+#include <sys/types.h>
+#include <time.h>
+
+/* Manufacure a VMS like time on a unix based system. */
+get_VMS_time_on_unix (char *Now)
+{
+ char *pnt;
+ time_t timeb;
+ time (&timeb);
+ pnt = ctime (&timeb);
+ pnt[3] = 0;
+ pnt[7] = 0;
+ pnt[10] = 0;
+ pnt[16] = 0;
+ pnt[24] = 0;
+ sprintf (Now, "%2s-%3s-%s %s", pnt + 8, pnt + 4, pnt + 20, pnt + 11);
+}
+
+#endif /* not HO_VMS */
+/*
+ * Write the MHD (Module Header) records
+ */
+static
+Write_VMS_MHD_Records ()
+{
+ register char *cp, *cp1;
+ register int i;
+ struct
+ {
+ int Size;
+ char *Ptr;
+ } Descriptor;
+ char Module_Name[256];
+ char Now[18];
+
+ /*
+ * We are writing a module header record
+ */
+ Set_VMS_Object_File_Record (OBJ_S_C_HDR);
+ /*
+ * ***************************
+ * *MAIN MODULE HEADER RECORD*
+ * ***************************
+ *
+ * Store record type and header type
+ */
+ PUT_CHAR (OBJ_S_C_HDR);
+ PUT_CHAR (MHD_S_C_MHD);
+ /*
+ * Structure level is 0
+ */
+ PUT_CHAR (OBJ_S_C_STRLVL);
+ /*
+ * Maximum record size is size of the object record buffer
+ */
+ PUT_SHORT (sizeof (Object_Record_Buffer));
+ /*
+ * Get module name (the FILENAME part of the object file)
+ */
+ cp = out_file_name;
+ cp1 = Module_Name;
+ while (*cp)
+ {
+ if ((*cp == ']') || (*cp == '>') ||
+ (*cp == ':') || (*cp == '/'))
+ {
+ cp1 = Module_Name;
+ cp++;
+ continue;
+ }
+ *cp1++ = islower (*cp) ? toupper (*cp++) : *cp++;
+ }
+ *cp1 = 0;
+ /*
+ * Limit it to 31 characters and store in the object record
+ */
+ while (--cp1 >= Module_Name)
+ if (*cp1 == '.')
+ *cp1 = 0;
+ if (strlen (Module_Name) > 31)
+ {
+ if (flagseen['+'])
+ printf ("%s: Module name truncated: %s\n", myname, Module_Name);
+ Module_Name[31] = 0;
+ }
+ PUT_COUNTED_STRING (Module_Name);
+ /*
+ * Module Version is "V1.0"
+ */
+ PUT_COUNTED_STRING ("V1.0");
+ /*
+ * Creation time is "now" (17 chars of time string)
+ */
+#ifndef HO_VMS
+ get_VMS_time_on_unix (&Now[0]);
+#else /* HO_VMS */
+ Descriptor.Size = 17;
+ Descriptor.Ptr = Now;
+ sys$asctim (0, &Descriptor, 0, 0);
+#endif /* HO_VMS */
+ for (i = 0; i < 17; i++)
+ PUT_CHAR (Now[i]);
+ /*
+ * Patch time is "never" (17 zeros)
+ */
+ for (i = 0; i < 17; i++)
+ PUT_CHAR (0);
+ /*
+ * Flush the record
+ */
+ Flush_VMS_Object_Record_Buffer ();
+ /*
+ * *************************
+ * *LANGUAGE PROCESSOR NAME*
+ * *************************
+ *
+ * Store record type and header type
+ */
+ PUT_CHAR (OBJ_S_C_HDR);
+ PUT_CHAR (MHD_S_C_LNM);
+ /*
+ * Store language processor name and version
+ * (not a counted string!)
+ */
+ cp = compiler_version_string;
+ if (cp == 0)
+ {
+ cp = "GNU AS V";
+ while (*cp)
+ PUT_CHAR (*cp++);
+ cp = strchr (&version_string, '.');
+ while (*cp != ' ')
+ cp--;
+ cp++;
+ };
+ while (*cp >= 32)
+ PUT_CHAR (*cp++);
+ /*
+ * Flush the record
+ */
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+
+/*
+ * Write the EOM (End Of Module) record
+ */
+static
+Write_VMS_EOM_Record (Psect, Offset)
+ int Psect;
+ int Offset;
+{
+ /*
+ * We are writing an end-of-module record
+ */
+ Set_VMS_Object_File_Record (OBJ_S_C_EOM);
+ /*
+ * Store record Type
+ */
+ PUT_CHAR (OBJ_S_C_EOM);
+ /*
+ * Store the error severity (0)
+ */
+ PUT_CHAR (0);
+ /*
+ * Store the entry point, if it exists
+ */
+ if (Psect >= 0)
+ {
+ /*
+ * Store the entry point Psect
+ */
+ PUT_CHAR (Psect);
+ /*
+ * Store the entry point Psect offset
+ */
+ PUT_LONG (Offset);
+ }
+ /*
+ * Flush the record
+ */
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+
+/* this hash routine borrowed from GNU-EMACS, and strengthened slightly ERY*/
+
+static int
+hash_string (ptr)
+ unsigned char *ptr;
+{
+ register unsigned char *p = ptr;
+ register unsigned char *end = p + strlen (ptr);
+ register unsigned char c;
+ register int hash = 0;
+
+ while (p != end)
+ {
+ c = *p++;
+ hash = ((hash << 3) + (hash << 15) + (hash >> 28) + c);
+ }
+ return hash;
+}
+
+/*
+ * Generate a Case-Hacked VMS symbol name (limited to 31 chars)
+ */
+static
+VMS_Case_Hack_Symbol (In, Out)
+ register char *In;
+ register char *Out;
+{
+ long int init = 0;
+ long int result;
+ char *pnt;
+ char *new_name;
+ char *old_name;
+ register int i;
+ int destructor = 0; /*hack to allow for case sens in a destructor*/
+ int truncate = 0;
+ int Case_Hack_Bits = 0;
+ int Saw_Dollar = 0;
+ static char Hex_Table[16] =
+ {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+ /*
+ * Kill any leading "_"
+ */
+ if ((In[0] == '_') && ((In[1] > '9') || (In[1] < '0')))
+ In++;
+
+ new_name = Out; /* save this for later*/
+
+#if barfoo /* Dead code */
+ if ((In[0] == '_') && (In[1] == '$') && (In[2] == '_'))
+ destructor = 1;
+#endif
+
+ /* We may need to truncate the symbol, save the hash for later*/
+ if (strlen (In) > 23)
+ result = hash_string (In);
+ /*
+ * Is there a Psect Attribute to skip??
+ */
+ if (HAS_PSECT_ATTRIBUTES (In))
+ {
+ /*
+ * Yes: Skip it
+ */
+ In += PSECT_ATTRIBUTES_STRING_LENGTH;
+ while (*In)
+ {
+ if ((In[0] == '$') && (In[1] == '$'))
+ {
+ In += 2;
+ break;
+ }
+ In++;
+ }
+ }
+
+ old_name = In;
+/* if (strlen(In) > 31 && flagseen['+'])
+ printf("%s: Symbol name truncated: %s\n",myname,In);*/
+ /*
+ * Do the case conversion
+ */
+ i = 23; /* Maximum of 23 chars */
+ while (*In && (--i >= 0))
+ {
+ Case_Hack_Bits <<= 1;
+ if (*In == '$')
+ Saw_Dollar = 1;
+ if ((destructor == 1) && (i == 21))
+ Saw_Dollar = 0;
+ switch (vms_name_mapping)
+ {
+ case 0:
+ if (isupper(*In)) {
+ *Out++ = *In++;
+ Case_Hack_Bits |= 1;
+ } else {
+ *Out++ = islower(*In) ? toupper(*In++) : *In++;
+ }
+ break;
+ case 3: *Out++ = *In++;
+ break;
+ case 2:
+ if (islower(*In)) {
+ *Out++ = *In++;
+ } else {
+ *Out++ = isupper(*In) ? tolower(*In++) : *In++;
+ }
+ break;
+ };
+ }
+ /*
+ * If we saw a dollar sign, we don't do case hacking
+ */
+ if (flagseen['h'] || Saw_Dollar)
+ Case_Hack_Bits = 0;
+
+ /*
+ * If we have more than 23 characters and everything is lowercase
+ * we can insert the full 31 characters
+ */
+ if (*In)
+ {
+ /*
+ * We have more than 23 characters
+ * If we must add the case hack, then we have truncated the str
+ */
+ pnt = Out;
+ truncate = 1;
+ if (Case_Hack_Bits == 0)
+ {
+ /*
+ * And so far they are all lower case:
+ * Check up to 8 more characters
+ * and ensure that they are lowercase
+ */
+ for (i = 0; (In[i] != 0) && (i < 8); i++)
+ if (isupper(In[i]) && !Saw_Dollar && !flagseen['h'])
+ break;
+
+ if (In[i] == 0)
+ truncate = 0;
+
+ if ((i == 8) || (In[i] == 0))
+ {
+ /*
+ * They are: Copy up to 31 characters
+ * to the output string
+ */
+ i = 8;
+ while ((--i >= 0) && (*In))
+ switch (vms_name_mapping){
+ case 0: *Out++ = islower(*In) ?
+ toupper (*In++) :
+ *In++;
+ break;
+ case 3: *Out++ = *In++;
+ break;
+ case 2: *Out++ = isupper(*In) ?
+ tolower(*In++) :
+ *In++;
+ break;
+ };
+ }
+ }
+ }
+ /*
+ * If there were any uppercase characters in the name we
+ * take on the case hacking string
+ */
+
+ /* Old behavior for regular GNU-C compiler */
+ if (!flagseen['+'])
+ truncate = 0;
+ if ((Case_Hack_Bits != 0) || (truncate == 1))
+ {
+ if (truncate == 0)
+ {
+ *Out++ = '_';
+ for (i = 0; i < 6; i++)
+ {
+ *Out++ = Hex_Table[Case_Hack_Bits & 0xf];
+ Case_Hack_Bits >>= 4;
+ }
+ *Out++ = 'X';
+ }
+ else
+ {
+ Out = pnt; /*Cut back to 23 characters maximum */
+ *Out++ = '_';
+ for (i = 0; i < 7; i++)
+ {
+ init = result & 0x01f;
+ if (init < 10)
+ *Out++ = '0' + init;
+ else
+ *Out++ = 'A' + init - 10;
+ result = result >> 5;
+ }
+ }
+ } /*Case Hack */
+ /*
+ * Done
+ */
+ *Out = 0;
+ if (truncate == 1 && flagseen['+'] && flagseen['H'])
+ printf ("%s: Symbol %s replaced by %s\n", myname, old_name, new_name);
+}
+
+
+/*
+ * Scan a symbol name for a psect attribute specification
+ */
+#define GLOBALSYMBOL_BIT 0x10000
+#define GLOBALVALUE_BIT 0x20000
+
+
+static
+VMS_Modify_Psect_Attributes (Name, Attribute_Pointer)
+ char *Name;
+ int *Attribute_Pointer;
+{
+ register int i;
+ register char *cp;
+ int Negate;
+ static struct
+ {
+ char *Name;
+ int Value;
+ } Attributes[] =
+ {
+ {"PIC", GPS_S_M_PIC},
+ {"LIB", GPS_S_M_LIB},
+ {"OVR", GPS_S_M_OVR},
+ {"REL", GPS_S_M_REL},
+ {"GBL", GPS_S_M_GBL},
+ {"SHR", GPS_S_M_SHR},
+ {"EXE", GPS_S_M_EXE},
+ {"RD", GPS_S_M_RD},
+ {"WRT", GPS_S_M_WRT},
+ {"VEC", GPS_S_M_VEC},
+ {"GLOBALSYMBOL", GLOBALSYMBOL_BIT},
+ {"GLOBALVALUE", GLOBALVALUE_BIT},
+ {0, 0}
+ };
+
+ /*
+ * Kill leading "_"
+ */
+ if (*Name == '_')
+ Name++;
+ /*
+ * Check for a PSECT attribute list
+ */
+ if (!HAS_PSECT_ATTRIBUTES (Name))
+ return; /* If not, return */
+ /*
+ * Skip the attribute list indicator
+ */
+ Name += PSECT_ATTRIBUTES_STRING_LENGTH;
+ /*
+ * Process the attributes ("_" separated, "$" terminated)
+ */
+ while (*Name != '$')
+ {
+ /*
+ * Assume not negating
+ */
+ Negate = 0;
+ /*
+ * Check for "NO"
+ */
+ if ((Name[0] == 'N') && (Name[1] == 'O'))
+ {
+ /*
+ * We are negating (and skip the NO)
+ */
+ Negate = 1;
+ Name += 2;
+ }
+ /*
+ * Find the token delimiter
+ */
+ cp = Name;
+ while (*cp && (*cp != '_') && (*cp != '$'))
+ cp++;
+ /*
+ * Look for the token in the attribute list
+ */
+ for (i = 0; Attributes[i].Name; i++)
+ {
+ /*
+ * If the strings match, set/clear the attr.
+ */
+ if (strncmp (Name, Attributes[i].Name, cp - Name) == 0)
+ {
+ /*
+ * Set or clear
+ */
+ if (Negate)
+ *Attribute_Pointer &=
+ ~Attributes[i].Value;
+ else
+ *Attribute_Pointer |=
+ Attributes[i].Value;
+ /*
+ * Done
+ */
+ break;
+ }
+ }
+ /*
+ * Now skip the attribute
+ */
+ Name = cp;
+ if (*Name == '_')
+ Name++;
+ }
+ /*
+ * Done
+ */
+ return;
+}
+
+
+/*
+ * Define a global symbol
+ */
+static
+VMS_Global_Symbol_Spec (Name, Psect_Number, Psect_Offset, Defined)
+ char *Name;
+ int Psect_Number;
+ int Psect_Offset;
+{
+ char Local[32];
+
+ /*
+ * We are writing a GSD record
+ */
+ Set_VMS_Object_File_Record (OBJ_S_C_GSD);
+ /*
+ * If the buffer is empty we must insert the GSD record type
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (OBJ_S_C_GSD);
+ /*
+ * We are writing a Global symbol definition subrecord
+ */
+ if (Psect_Number <= 255)
+ {
+ PUT_CHAR (GSD_S_C_SYM);
+ }
+ else
+ {
+ PUT_CHAR (GSD_S_C_SYMW);
+ }
+ /*
+ * Data type is undefined
+ */
+ PUT_CHAR (0);
+ /*
+ * Switch on Definition/Reference
+ */
+ if ((Defined & 1) != 0)
+ {
+ /*
+ * Definition:
+ * Flags = "RELOCATABLE" and "DEFINED" for regular symbol
+ * = "DEFINED" for globalvalue (Defined & 2 == 1)
+ */
+ if ((Defined & 2) == 0)
+ {
+ PUT_SHORT (GSY_S_M_DEF | GSY_S_M_REL);
+ }
+ else
+ {
+ PUT_SHORT (GSY_S_M_DEF);
+ };
+ /*
+ * Psect Number
+ */
+ if (Psect_Number <= 255)
+ {
+ PUT_CHAR (Psect_Number);
+ }
+ else
+ {
+ PUT_SHORT (Psect_Number);
+ }
+ /*
+ * Offset
+ */
+ PUT_LONG (Psect_Offset);
+ }
+ else
+ {
+ /*
+ * Reference:
+ * Flags = "RELOCATABLE" for regular symbol,
+ * = "" for globalvalue (Defined & 2 == 1)
+ */
+ if ((Defined & 2) == 0)
+ {
+ PUT_SHORT (GSY_S_M_REL);
+ }
+ else
+ {
+ PUT_SHORT (0);
+ };
+ }
+ /*
+ * Finally, the global symbol name
+ */
+ VMS_Case_Hack_Symbol (Name, Local);
+ PUT_COUNTED_STRING (Local);
+ /*
+ * Flush the buffer if it is more than 75% full
+ */
+ if (Object_Record_Offset >
+ (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+
+/*
+ * Define a psect
+ */
+static int
+VMS_Psect_Spec (Name, Size, Type, vsp)
+ char *Name;
+ int Size;
+ char *Type;
+ struct VMS_Symbol *vsp;
+{
+ char Local[32];
+ int Psect_Attributes;
+
+ /*
+ * Generate the appropriate PSECT flags given the PSECT type
+ */
+ if (strcmp (Type, "COMMON") == 0)
+ {
+ /*
+ * Common block psects are: PIC,OVR,REL,GBL,SHR,RD,WRT
+ */
+ Psect_Attributes = (GPS_S_M_PIC | GPS_S_M_OVR | GPS_S_M_REL | GPS_S_M_GBL |
+ GPS_S_M_SHR | GPS_S_M_RD | GPS_S_M_WRT);
+ }
+ else if (strcmp (Type, "CONST") == 0)
+ {
+ /*
+ * Common block psects are: PIC,OVR,REL,GBL,SHR,RD
+ */
+ Psect_Attributes = (GPS_S_M_PIC | GPS_S_M_OVR | GPS_S_M_REL | GPS_S_M_GBL |
+ GPS_S_M_SHR | GPS_S_M_RD);
+ }
+ else if (strcmp (Type, "DATA") == 0)
+ {
+ /*
+ * The Data psects are PIC,REL,RD,WRT
+ */
+ Psect_Attributes =
+ (GPS_S_M_PIC | GPS_S_M_REL | GPS_S_M_RD | GPS_S_M_WRT);
+ }
+ else if (strcmp (Type, "TEXT") == 0)
+ {
+ /*
+ * The Text psects are PIC,REL,SHR,EXE,RD
+ */
+ Psect_Attributes =
+ (GPS_S_M_PIC | GPS_S_M_REL | GPS_S_M_SHR |
+ GPS_S_M_EXE | GPS_S_M_RD);
+ }
+ else
+ {
+ /*
+ * Error: Unknown psect type
+ */
+ error ("Unknown VMS psect type");
+ }
+ /*
+ * Modify the psect attributes according to any attribute string
+ */
+ if (HAS_PSECT_ATTRIBUTES (Name))
+ VMS_Modify_Psect_Attributes (Name, &Psect_Attributes);
+ /*
+ * Check for globalref/def/val.
+ */
+ if ((Psect_Attributes & GLOBALVALUE_BIT) != 0)
+ {
+ /*
+ * globalvalue symbols were generated before. This code
+ * prevents unsightly psect buildup, and makes sure that
+ * fixup references are emitted correctly.
+ */
+ vsp->Psect_Index = -1; /* to catch errors */
+ S_GET_RAW_TYPE (vsp->Symbol) = N_UNDF; /* make refs work */
+ return 1; /* decrement psect counter */
+ };
+
+ if ((Psect_Attributes & GLOBALSYMBOL_BIT) != 0)
+ {
+ switch (S_GET_RAW_TYPE (vsp->Symbol))
+ {
+ case N_UNDF | N_EXT:
+ VMS_Global_Symbol_Spec (Name, vsp->Psect_Index,
+ vsp->Psect_Offset, 0);
+ vsp->Psect_Index = -1;
+ S_GET_RAW_TYPE (vsp->Symbol) = N_UNDF;
+ return 1; /* return and indicate no psect */
+ case N_DATA | N_EXT:
+ VMS_Global_Symbol_Spec (Name, vsp->Psect_Index,
+ vsp->Psect_Offset, 1);
+ /* In this case we still generate the psect */
+ break;
+ default:
+ {
+ char Error_Line[256];
+ sprintf (Error_Line, "Globalsymbol attribute for"
+ " symbol %s was unexpected.\n", Name);
+ error (Error_Line);
+ break;
+ };
+ }; /* switch */
+ };
+
+ Psect_Attributes &= 0xffff; /* clear out the globalref/def stuff */
+ /*
+ * We are writing a GSD record
+ */
+ Set_VMS_Object_File_Record (OBJ_S_C_GSD);
+ /*
+ * If the buffer is empty we must insert the GSD record type
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (OBJ_S_C_GSD);
+ /*
+ * We are writing a PSECT definition subrecord
+ */
+ PUT_CHAR (GSD_S_C_PSC);
+ /*
+ * Psects are always LONGWORD aligned
+ */
+ PUT_CHAR (2);
+ /*
+ * Specify the psect attributes
+ */
+ PUT_SHORT (Psect_Attributes);
+ /*
+ * Specify the allocation
+ */
+ PUT_LONG (Size);
+ /*
+ * Finally, the psect name
+ */
+ VMS_Case_Hack_Symbol (Name, Local);
+ PUT_COUNTED_STRING (Local);
+ /*
+ * Flush the buffer if it is more than 75% full
+ */
+ if (Object_Record_Offset >
+ (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+ return 0;
+}
+
+
+/*
+ * Given the pointer to a symbol we calculate how big the data at the
+ * symbol is. We do this by looking for the next symbol (local or
+ * global) which will indicate the start of another datum.
+ */
+static int
+VMS_Initialized_Data_Size (sp, End_Of_Data)
+ register struct symbol *sp;
+ int End_Of_Data;
+{
+ register struct symbol *sp1, *Next_Symbol;
+
+ /*
+ * Find the next symbol
+ * it delimits this datum
+ */
+ Next_Symbol = 0;
+ for (sp1 = symbol_rootP; sp1; sp1 = symbol_next (sp1))
+ {
+ /*
+ * The data type must match
+ */
+ if (S_GET_TYPE (sp1) != N_DATA)
+ continue;
+ /*
+ * The symbol must be AFTER this symbol
+ */
+ if (S_GET_VALUE (sp1) <= S_GET_VALUE (sp))
+ continue;
+ /*
+ * We ignore THIS symbol
+ */
+ if (sp1 == sp)
+ continue;
+ /*
+ * If there is already a candidate selected for the
+ * next symbol, see if we are a better candidate
+ */
+ if (Next_Symbol)
+ {
+ /*
+ * We are a better candidate if we are "closer"
+ * to the symbol
+ */
+ if (S_GET_VALUE (sp1) >
+ S_GET_VALUE (Next_Symbol))
+ continue;
+ /*
+ * Win: Make this the candidate
+ */
+ Next_Symbol = sp1;
+ }
+ else
+ {
+ /*
+ * This is the 1st candidate
+ */
+ Next_Symbol = sp1;
+ }
+ }
+ /*
+ * Calculate its size
+ */
+ return (Next_Symbol ?
+ (S_GET_VALUE (Next_Symbol) -
+ S_GET_VALUE (sp)) :
+ (End_Of_Data - S_GET_VALUE (sp)));
+}
+
+/*
+ * Check symbol names for the Psect hack with a globalvalue, and then
+ * generate globalvalues for those that have it.
+ */
+static
+VMS_Emit_Globalvalues (text_siz, data_siz, Data_Segment)
+ unsigned text_siz;
+ unsigned data_siz;
+ char *Data_Segment;
+{
+ register symbolS *sp;
+ char *stripped_name, *Name;
+ int Size;
+ int Psect_Attributes;
+ int globalvalue;
+
+ /*
+ * Scan the symbol table for globalvalues, and emit def/ref when
+ * required. These will be caught again later and converted to
+ * N_UNDF
+ */
+ for (sp = symbol_rootP; sp; sp = sp->sy_next)
+ {
+ /*
+ * See if this is something we want to look at.
+ */
+ if ((S_GET_RAW_TYPE (sp) != (N_DATA | N_EXT)) &&
+ (S_GET_RAW_TYPE (sp) != (N_UNDF | N_EXT)))
+ continue;
+ /*
+ * See if this has globalvalue specification.
+ */
+ Name = S_GET_NAME (sp);
+
+ if (!HAS_PSECT_ATTRIBUTES (Name))
+ continue;
+
+ stripped_name = (char *) malloc (strlen (Name) + 1);
+ strcpy (stripped_name, Name);
+ Psect_Attributes = 0;
+ VMS_Modify_Psect_Attributes (stripped_name, &Psect_Attributes);
+
+ if ((Psect_Attributes & GLOBALVALUE_BIT) != 0)
+ {
+ switch (S_GET_RAW_TYPE (sp))
+ {
+ case N_UNDF | N_EXT:
+ VMS_Global_Symbol_Spec (stripped_name, 0, 0, 2);
+ break;
+ case N_DATA | N_EXT:
+ Size = VMS_Initialized_Data_Size (sp, text_siz + data_siz);
+ if (Size > 4)
+ error ("Invalid data type for globalvalue");
+ globalvalue = 0;
+
+ memcpy (&globalvalue, Data_Segment + S_GET_VALUE (sp) -
+ text_siz, Size);
+ /* Three times for good luck. The linker seems to get confused
+ if there are fewer than three */
+ VMS_Global_Symbol_Spec (stripped_name, 0, 0, 2);
+ VMS_Global_Symbol_Spec (stripped_name, 0, globalvalue, 3);
+ VMS_Global_Symbol_Spec (stripped_name, 0, globalvalue, 3);
+ break;
+ default:
+ printf (" Invalid globalvalue of %s\n", stripped_name);
+ break;
+ }; /* switch */
+ }; /* if */
+ free (stripped_name); /* clean up */
+ }; /* for */
+
+}
+
+
+/*
+ * Define a procedure entry pt/mask
+ */
+static
+VMS_Procedure_Entry_Pt (Name, Psect_Number, Psect_Offset, Entry_Mask)
+ char *Name;
+ int Psect_Number;
+ int Psect_Offset;
+ int Entry_Mask;
+{
+ char Local[32];
+
+ /*
+ * We are writing a GSD record
+ */
+ Set_VMS_Object_File_Record (OBJ_S_C_GSD);
+ /*
+ * If the buffer is empty we must insert the GSD record type
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (OBJ_S_C_GSD);
+ /*
+ * We are writing a Procedure Entry Pt/Mask subrecord
+ */
+ if (Psect_Number <= 255)
+ {
+ PUT_CHAR (GSD_S_C_EPM);
+ }
+ else
+ {
+ PUT_CHAR (GSD_S_C_EPMW);
+ }
+ /*
+ * Data type is undefined
+ */
+ PUT_CHAR (0);
+ /*
+ * Flags = "RELOCATABLE" and "DEFINED"
+ */
+ PUT_SHORT (GSY_S_M_DEF | GSY_S_M_REL);
+ /*
+ * Psect Number
+ */
+ if (Psect_Number <= 255)
+ {
+ PUT_CHAR (Psect_Number);
+ }
+ else
+ {
+ PUT_SHORT (Psect_Number);
+ }
+ /*
+ * Offset
+ */
+ PUT_LONG (Psect_Offset);
+ /*
+ * Entry mask
+ */
+ PUT_SHORT (Entry_Mask);
+ /*
+ * Finally, the global symbol name
+ */
+ VMS_Case_Hack_Symbol (Name, Local);
+ PUT_COUNTED_STRING (Local);
+ /*
+ * Flush the buffer if it is more than 75% full
+ */
+ if (Object_Record_Offset >
+ (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+
+/*
+ * Set the current location counter to a particular Psect and Offset
+ */
+static
+VMS_Set_Psect (Psect_Index, Offset, Record_Type)
+ int Psect_Index;
+ int Offset;
+ int Record_Type;
+{
+ /*
+ * We are writing a "Record_Type" record
+ */
+ Set_VMS_Object_File_Record (Record_Type);
+ /*
+ * If the buffer is empty we must insert the record type
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (Record_Type);
+ /*
+ * Stack the Psect base + Longword Offset
+ */
+ if (Psect_Index < 255)
+ {
+ PUT_CHAR (TIR_S_C_STA_PL);
+ PUT_CHAR (Psect_Index);
+ }
+ else
+ {
+ PUT_CHAR (TIR_S_C_STA_WPL);
+ PUT_SHORT (Psect_Index);
+ }
+ PUT_LONG (Offset);
+ /*
+ * Set relocation base
+ */
+ PUT_CHAR (TIR_S_C_CTL_SETRB);
+ /*
+ * Flush the buffer if it is more than 75% full
+ */
+ if (Object_Record_Offset >
+ (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+
+/*
+ * Store repeated immediate data in current Psect
+ */
+static
+VMS_Store_Repeated_Data (Repeat_Count, Pointer, Size, Record_Type)
+ int Repeat_Count;
+ register char *Pointer;
+ int Size;
+ int Record_Type;
+{
+
+ /*
+ * Ignore zero bytes/words/longwords
+ */
+ if ((Size == sizeof (char)) && (*Pointer == 0))
+ return;
+ if ((Size == sizeof (short)) && (*(short *) Pointer == 0))
+ return;
+ if ((Size == sizeof (long)) && (*(long *) Pointer == 0))
+ return;
+ /*
+ * If the data is too big for a TIR_S_C_STO_RIVB sub-record
+ * then we do it manually
+ */
+ if (Size > 255)
+ {
+ while (--Repeat_Count >= 0)
+ VMS_Store_Immediate_Data (Pointer, Size, Record_Type);
+ return;
+ }
+ /*
+ * We are writing a "Record_Type" record
+ */
+ Set_VMS_Object_File_Record (Record_Type);
+ /*
+ * If the buffer is empty we must insert record type
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (Record_Type);
+ /*
+ * Stack the repeat count
+ */
+ PUT_CHAR (TIR_S_C_STA_LW);
+ PUT_LONG (Repeat_Count);
+ /*
+ * And now the command and its data
+ */
+ PUT_CHAR (TIR_S_C_STO_RIVB);
+ PUT_CHAR (Size);
+ while (--Size >= 0)
+ PUT_CHAR (*Pointer++);
+ /*
+ * Flush the buffer if it is more than 75% full
+ */
+ if (Object_Record_Offset >
+ (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+
+/*
+ * Store a Position Independent Reference
+ */
+static
+VMS_Store_PIC_Symbol_Reference (Symbol, Offset, PC_Relative,
+ Psect, Psect_Offset, Record_Type)
+ struct symbol *Symbol;
+ int Offset;
+ int PC_Relative;
+ int Psect;
+ int Psect_Offset;
+ int Record_Type;
+{
+ register struct VMS_Symbol *vsp =
+ (struct VMS_Symbol *) (Symbol->sy_number);
+ char Local[32];
+
+ /*
+ * We are writing a "Record_Type" record
+ */
+ Set_VMS_Object_File_Record (Record_Type);
+ /*
+ * If the buffer is empty we must insert record type
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (Record_Type);
+ /*
+ * Set to the appropriate offset in the Psect
+ */
+ if (PC_Relative)
+ {
+ /*
+ * For a Code reference we need to fix the operand
+ * specifier as well (so back up 1 byte)
+ */
+ VMS_Set_Psect (Psect, Psect_Offset - 1, Record_Type);
+ }
+ else
+ {
+ /*
+ * For a Data reference we just store HERE
+ */
+ VMS_Set_Psect (Psect, Psect_Offset, Record_Type);
+ }
+ /*
+ * Make sure we are still generating a "Record Type" record
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (Record_Type);
+ /*
+ * Dispatch on symbol type (so we can stack its value)
+ */
+ switch (S_GET_RAW_TYPE (Symbol))
+ {
+ /*
+ * Global symbol
+ */
+#ifdef NOT_VAX_11_C_COMPATIBLE
+ case N_UNDF | N_EXT:
+ case N_DATA | N_EXT:
+#endif /* NOT_VAX_11_C_COMPATIBLE */
+ case N_UNDF:
+ case N_TEXT | N_EXT:
+ /*
+ * Get the symbol name (case hacked)
+ */
+ VMS_Case_Hack_Symbol (S_GET_NAME (Symbol), Local);
+ /*
+ * Stack the global symbol value
+ */
+ PUT_CHAR (TIR_S_C_STA_GBL);
+ PUT_COUNTED_STRING (Local);
+ if (Offset)
+ {
+ /*
+ * Stack the longword offset
+ */
+ PUT_CHAR (TIR_S_C_STA_LW);
+ PUT_LONG (Offset);
+ /*
+ * Add the two, leaving the result on the stack
+ */
+ PUT_CHAR (TIR_S_C_OPR_ADD);
+ }
+ break;
+ /*
+ * Uninitialized local data
+ */
+ case N_BSS:
+ /*
+ * Stack the Psect (+offset)
+ */
+ if (vsp->Psect_Index < 255)
+ {
+ PUT_CHAR (TIR_S_C_STA_PL);
+ PUT_CHAR (vsp->Psect_Index);
+ }
+ else
+ {
+ PUT_CHAR (TIR_S_C_STA_WPL);
+ PUT_SHORT (vsp->Psect_Index);
+ }
+ PUT_LONG (vsp->Psect_Offset + Offset);
+ break;
+ /*
+ * Local text
+ */
+ case N_TEXT:
+ /*
+ * Stack the Psect (+offset)
+ */
+ if (vsp->Psect_Index < 255)
+ {
+ PUT_CHAR (TIR_S_C_STA_PL);
+ PUT_CHAR (vsp->Psect_Index);
+ }
+ else
+ {
+ PUT_CHAR (TIR_S_C_STA_WPL);
+ PUT_SHORT (vsp->Psect_Index);
+ }
+ PUT_LONG (S_GET_VALUE (Symbol) + Offset);
+ break;
+ /*
+ * Initialized local or global data
+ */
+ case N_DATA:
+#ifndef NOT_VAX_11_C_COMPATIBLE
+ case N_UNDF | N_EXT:
+ case N_DATA | N_EXT:
+#endif /* NOT_VAX_11_C_COMPATIBLE */
+ /*
+ * Stack the Psect (+offset)
+ */
+ if (vsp->Psect_Index < 255)
+ {
+ PUT_CHAR (TIR_S_C_STA_PL);
+ PUT_CHAR (vsp->Psect_Index);
+ }
+ else
+ {
+ PUT_CHAR (TIR_S_C_STA_WPL);
+ PUT_SHORT (vsp->Psect_Index);
+ }
+ PUT_LONG (vsp->Psect_Offset + Offset);
+ break;
+ }
+ /*
+ * Store either a code or data reference
+ */
+ PUT_CHAR (PC_Relative ? TIR_S_C_STO_PICR : TIR_S_C_STO_PIDR);
+ /*
+ * Flush the buffer if it is more than 75% full
+ */
+ if (Object_Record_Offset >
+ (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+
+/*
+ * Check in the text area for an indirect pc-relative reference
+ * and fix it up with addressing mode 0xff [PC indirect]
+ *
+ * THIS SHOULD BE REPLACED BY THE USE OF TIR_S_C_STO_PIRR IN THE
+ * PIC CODE GENERATING FIXUP ROUTINE.
+ */
+static
+VMS_Fix_Indirect_Reference (Text_Psect, Offset, fragP, text_frag_root)
+ int Text_Psect;
+ int Offset;
+ register fragS *fragP;
+ struct frag *text_frag_root;
+{
+ /*
+ * The addressing mode byte is 1 byte before the address
+ */
+ Offset--;
+ /*
+ * Is it in THIS frag??
+ */
+ if ((Offset < fragP->fr_address) ||
+ (Offset >= (fragP->fr_address + fragP->fr_fix)))
+ {
+ /*
+ * We need to search for the fragment containing this
+ * Offset
+ */
+ for (fragP = text_frag_root; fragP; fragP = fragP->fr_next)
+ {
+ if ((Offset >= fragP->fr_address) &&
+ (Offset < (fragP->fr_address + fragP->fr_fix)))
+ break;
+ }
+ /*
+ * If we couldn't find the frag, things are BAD!!
+ */
+ if (fragP == 0)
+ error ("Couldn't find fixup fragment when checking for indirect reference");
+ }
+ /*
+ * Check for indirect PC relative addressing mode
+ */
+ if (fragP->fr_literal[Offset - fragP->fr_address] == (char) 0xff)
+ {
+ static char Address_Mode = 0xff;
+
+ /*
+ * Yes: Store the indirect mode back into the image
+ * to fix up the damage done by STO_PICR
+ */
+ VMS_Set_Psect (Text_Psect, Offset, OBJ_S_C_TIR);
+ VMS_Store_Immediate_Data (&Address_Mode, 1, OBJ_S_C_TIR);
+ }
+}
+
+
+
+/*
+ * This is a hacked _doprnt() for VAX-11 "C". It understands that
+ * it is ONLY called by as_fatal(Format, Args) with a pointer to the
+ * "Args" argument. From this we can make it all work right!
+ */
+#if !defined(eunice) && defined(HO_VMS)
+_doprnt (Format, a, f)
+ char *Format;
+ FILE *f;
+ char **a;
+{
+ int Nargs = ((int *) a)[-2]; /* This understands as_fatal() */
+
+ switch (Nargs)
+ {
+ default:
+ fprintf (f, "_doprnt error on \"%s\"!!", Format);
+ break;
+ case 1:
+ fprintf (f, Format);
+ break;
+ case 2:
+ fprintf (f, Format, a[0]);
+ break;
+ case 3:
+ fprintf (f, Format, a[0], a[1]);
+ break;
+ case 4:
+ fprintf (f, Format, a[0], a[1], a[2]);
+ break;
+ case 5:
+ fprintf (f, Format, a[0], a[1], a[2], a[3]);
+ break;
+ case 6:
+ fprintf (f, Format, a[0], a[1], a[2], a[3], a[4]);
+ break;
+ case 7:
+ fprintf (f, Format, a[0], a[1], a[2], a[3], a[4], a[5]);
+ break;
+ case 8:
+ fprintf (f, Format, a[0], a[1], a[2], a[3], a[4], a[5], a[6]);
+ break;
+ case 9:
+ fprintf (f, Format, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
+ break;
+ case 10:
+ fprintf (f, Format, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]);
+ break;
+ }
+}
+
+#endif /* eunice */
+
+
+/*
+ * If the procedure "main()" exists we have to add the instruction
+ * "jsb c$main_args" at the beginning to be compatible with VAX-11 "C".
+ */
+VMS_Check_For_Main ()
+{
+ register symbolS *symbolP;
+#ifdef HACK_DEC_C_STARTUP /* JF */
+ register struct frchain *frchainP;
+ register fragS *fragP;
+ register fragS **prev_fragPP;
+ register struct fix *fixP;
+ register fragS *New_Frag;
+ int i;
+#endif /* HACK_DEC_C_STARTUP */
+
+ symbolP = (struct symbol *) symbol_find ("_main");
+ if (symbolP && !S_IS_DEBUG (symbolP) &&
+ S_IS_EXTERNAL (symbolP) && (S_GET_TYPE (symbolP) == N_TEXT))
+ {
+#ifdef HACK_DEC_C_STARTUP
+ if (!flagseen['+'])
+ {
+#endif
+ /*
+ * Remember the entry point symbol
+ */
+ Entry_Point_Symbol = symbolP;
+#ifdef HACK_DEC_C_STARTUP
+ }
+ else
+ {
+ /*
+ * Scan all the fragment chains for the one with "_main"
+ * (Actually we know the fragment from the symbol, but we need
+ * the previous fragment so we can change its pointer)
+ */
+ frchainP = frchain_root;
+ while (frchainP)
+ {
+ /*
+ * Scan all the fragments in this chain, remembering
+ * the "previous fragment"
+ */
+ prev_fragPP = &frchainP->frch_root;
+ fragP = frchainP->frch_root;
+ while (fragP && (fragP != frchainP->frch_last))
+ {
+ /*
+ * Is this the fragment?
+ */
+ if (fragP == symbolP->sy_frag)
+ {
+ /*
+ * Yes: Modify the fragment by replacing
+ * it with a new fragment.
+ */
+ New_Frag = (fragS *)
+ xmalloc (sizeof (*New_Frag) +
+ fragP->fr_fix +
+ fragP->fr_var +
+ 5);
+ /*
+ * The fragments are the same except
+ * that the "fixed" area is larger
+ */
+ *New_Frag = *fragP;
+ New_Frag->fr_fix += 6;
+ /*
+ * Copy the literal data opening a hole
+ * 2 bytes after "_main" (i.e. just after
+ * the entry mask). Into which we place
+ * the JSB instruction.
+ */
+ New_Frag->fr_literal[0] = fragP->fr_literal[0];
+ New_Frag->fr_literal[1] = fragP->fr_literal[1];
+ New_Frag->fr_literal[2] = 0x16; /* Jsb */
+ New_Frag->fr_literal[3] = 0xef;
+ New_Frag->fr_literal[4] = 0;
+ New_Frag->fr_literal[5] = 0;
+ New_Frag->fr_literal[6] = 0;
+ New_Frag->fr_literal[7] = 0;
+ for (i = 2; i < fragP->fr_fix + fragP->fr_var; i++)
+ New_Frag->fr_literal[i + 6] =
+ fragP->fr_literal[i];
+ /*
+ * Now replace the old fragment with the
+ * newly generated one.
+ */
+ *prev_fragPP = New_Frag;
+ /*
+ * Remember the entry point symbol
+ */
+ Entry_Point_Symbol = symbolP;
+ /*
+ * Scan the text area fixup structures
+ * as offsets in the fragment may have
+ * changed
+ */
+ for (fixP = text_fix_root; fixP; fixP = fixP->fx_next)
+ {
+ /*
+ * Look for references to this
+ * fragment.
+ */
+ if (fixP->fx_frag == fragP)
+ {
+ /*
+ * Change the fragment
+ * pointer
+ */
+ fixP->fx_frag = New_Frag;
+ /*
+ * If the offset is after
+ * the entry mask we need
+ * to account for the JSB
+ * instruction we just
+ * inserted.
+ */
+ if (fixP->fx_where >= 2)
+ fixP->fx_where += 6;
+ }
+ }
+ /*
+ * Scan the symbols as offsets in the
+ * fragment may have changed
+ */
+ for (symbolP = symbol_rootP;
+ symbolP;
+ symbolP = symbol_next (symbolP))
+ {
+ /*
+ * Look for references to this
+ * fragment.
+ */
+ if (symbolP->sy_frag == fragP)
+ {
+ /*
+ * Change the fragment
+ * pointer
+ */
+ symbolP->sy_frag = New_Frag;
+ /*
+ * If the offset is after
+ * the entry mask we need
+ * to account for the JSB
+ * instruction we just
+ * inserted.
+ */
+ if (S_GET_VALUE (symbolP) >= 2)
+ S_GET_VALUE (symbolP) += 6;
+ }
+ }
+ /*
+ * Make a symbol reference to
+ * "_c$main_args" so we can get
+ * its address inserted into the
+ * JSB instruction.
+ */
+ symbolP = (symbolS *) xmalloc (sizeof (*symbolP));
+ S_GET_NAME (symbolP) = "_c$main_args";
+ S_SET_TYPE (symbolP, N_UNDF);
+ S_GET_OTHER (symbolP) = 0;
+ S_GET_DESC (symbolP) = 0;
+ S_GET_VALUE (symbolP) = 0;
+ symbolP->sy_name_offset = 0;
+ symbolP->sy_number = 0;
+ symbolP->sy_frag = New_Frag;
+ symbolP->sy_forward = 0;
+ /* this actually inserts at the beginning of the list */
+ symbol_append (symbol_rootP, symbolP, &symbol_rootP, &symbol_lastP);
+
+ symbol_rootP = symbolP;
+ /*
+ * Generate a text fixup structure
+ * to get "_c$main_args" stored into the
+ * JSB instruction.
+ */
+ fixP = (struct fix *) xmalloc (sizeof (*fixP));
+ fixP->fx_frag = New_Frag;
+ fixP->fx_where = 4;
+ fixP->fx_addsy = symbolP;
+ fixP->fx_subsy = 0;
+ fixP->fx_offset = 0;
+ fixP->fx_size = sizeof (long);
+ fixP->fx_pcrel = 1;
+ fixP->fx_next = text_fix_root;
+ text_fix_root = fixP;
+ /*
+ * Now make sure we exit from the loop
+ */
+ frchainP = 0;
+ break;
+ }
+ /*
+ * Try the next fragment
+ */
+ prev_fragPP = &fragP->fr_next;
+ fragP = fragP->fr_next;
+ }
+ /*
+ * Try the next fragment chain
+ */
+ if (frchainP)
+ frchainP = frchainP->frch_next;
+ }
+ }
+#endif /* HACK_DEC_C_STARTUP */
+ }
+}
+
+/*
+ * Write a VAX/VMS object file (everything else has been done!)
+ */
+VMS_write_object_file (text_siz, data_siz, text_frag_root, data_frag_root)
+ unsigned text_siz;
+ unsigned data_siz;
+ struct frag *text_frag_root;
+ struct frag *data_frag_root;
+{
+ register fragS *fragP;
+ register symbolS *symbolP;
+ register symbolS *sp;
+ register struct fix *fixP;
+ register struct VMS_Symbol *vsp;
+ char *Data_Segment;
+ int Local_Initialized_Data_Size = 0;
+ int Globalref;
+ int Psect_Number = 0; /* Psect Index Number */
+ int Text_Psect = -1; /* Text Psect Index */
+ int Data_Psect = -2; /* Data Psect Index JF: Was -1 */
+ int Bss_Psect = -3; /* Bss Psect Index JF: Was -1 */
+
+ /*
+ * Create the VMS object file
+ */
+ Create_VMS_Object_File ();
+ /*
+ * Write the module header records
+ */
+ Write_VMS_MHD_Records ();
+
+ /*
+ * Store the Data segment:
+ *
+ * Since this is REALLY hard to do any other way,
+ * we actually manufacture the data segment and
+ * the store the appropriate values out of it.
+ * We need to generate this early, so that globalvalues
+ * can be properly emitted.
+ */
+ if (data_siz > 0)
+ {
+ /*
+ * Allocate the data segment
+ */
+ Data_Segment = (char *) xmalloc (data_siz);
+ /*
+ * Run through the data fragments, filling in the segment
+ */
+ for (fragP = data_frag_root; fragP; fragP = fragP->fr_next)
+ {
+ register long int count;
+ register char *fill_literal;
+ register long int fill_size;
+ int i;
+
+ i = fragP->fr_address - text_siz;
+ if (fragP->fr_fix)
+ memcpy (Data_Segment + i,
+ fragP->fr_literal,
+ fragP->fr_fix);
+ i += fragP->fr_fix;
+
+ fill_literal = fragP->fr_literal + fragP->fr_fix;
+ fill_size = fragP->fr_var;
+ for (count = fragP->fr_offset; count; count--)
+ {
+ if (fill_size)
+ memcpy (Data_Segment + i, fill_literal, fill_size);
+ i += fill_size;
+ }
+ }
+ }
+
+
+ /*
+ * Generate the VMS object file records
+ * 1st GSD then TIR records
+ */
+
+ /******* Global Symbol Dictionary *******/
+ /*
+ * Emit globalvalues now. We must do this before the text psect
+ * is defined, or we will get linker warnings about multiply defined
+ * symbols. All of the globalvalues "reference" psect 0, although
+ * it really does not have anything to do with it.
+ */
+ VMS_Emit_Globalvalues (text_siz, data_siz, Data_Segment);
+ /*
+ * Define the Text Psect
+ */
+ Text_Psect = Psect_Number++;
+ VMS_Psect_Spec ("$code", text_siz, "TEXT", 0);
+ /*
+ * Define the BSS Psect
+ */
+ if (local_bss_counter > 0)
+ {
+ Bss_Psect = Psect_Number++;
+ VMS_Psect_Spec ("$uninitialized_data", local_bss_counter, "DATA",
+ 0);
+ }
+#ifndef gxx_bug_fixed
+ /*
+ * The g++ compiler does not write out external references to vtables
+ * correctly. Check for this and holler if we see it happening.
+ * If that compiler bug is ever fixed we can remove this.
+ */
+ for (sp = symbol_rootP; sp; sp = symbol_next (sp))
+ {
+ /*
+ * Dispatch on symbol type
+ */
+ switch (S_GET_RAW_TYPE (sp)) {
+ /*
+ * Global Reference
+ */
+ case N_UNDF:
+ /*
+ * Make a GSD global symbol reference
+ * record.
+ */
+ if (strncmp (S_GET_NAME (sp),"__vt.",5) == 0)
+ {
+ S_GET_RAW_TYPE (sp) = N_UNDF | N_EXT;
+ as_warn("g++ wrote an extern reference to %s as a routine.",
+ S_GET_NAME (sp));
+ as_warn("I will fix it, but I hope that it was not really a routine");
+ };
+ break;
+ default:
+ break;
+ }
+ }
+#endif /* gxx_bug_fixed */
+ /*
+ * Now scan the symbols and emit the appropriate GSD records
+ */
+ for (sp = symbol_rootP; sp; sp = symbol_next (sp))
+ {
+ /*
+ * Dispatch on symbol type
+ */
+ switch (S_GET_RAW_TYPE (sp))
+ {
+ /*
+ * Global uninitialized data
+ */
+ case N_UNDF | N_EXT:
+ /*
+ * Make a VMS data symbol entry
+ */
+ vsp = (struct VMS_Symbol *)
+ xmalloc (sizeof (*vsp));
+ vsp->Symbol = sp;
+ vsp->Size = S_GET_VALUE (sp);
+ vsp->Psect_Index = Psect_Number++;
+ vsp->Psect_Offset = 0;
+ vsp->Next = VMS_Symbols;
+ VMS_Symbols = vsp;
+ sp->sy_number = (int) vsp;
+ /*
+ * Make the psect for this data
+ */
+ if (S_GET_OTHER (sp))
+ Globalref = VMS_Psect_Spec (
+ S_GET_NAME (sp),
+ vsp->Size,
+ "CONST",
+ vsp);
+ else
+ Globalref = VMS_Psect_Spec (
+ S_GET_NAME (sp),
+ vsp->Size,
+ "COMMON",
+ vsp);
+ if (Globalref)
+ Psect_Number--;
+#ifdef NOT_VAX_11_C_COMPATIBLE
+ /*
+ * Place a global symbol at the
+ * beginning of the Psect
+ */
+ VMS_Global_Symbol_Spec (S_GET_NAME (sp),
+ vsp->Psect_Index,
+ 0,
+ 1);
+#endif /* NOT_VAX_11_C_COMPATIBLE */
+ break;
+ /*
+ * Local uninitialized data
+ */
+ case N_BSS:
+ /*
+ * Make a VMS data symbol entry
+ */
+ vsp = (struct VMS_Symbol *)
+ xmalloc (sizeof (*vsp));
+ vsp->Symbol = sp;
+ vsp->Size = 0;
+ vsp->Psect_Index = Bss_Psect;
+ vsp->Psect_Offset =
+ S_GET_VALUE (sp) -
+ bss_address_frag.fr_address;
+ vsp->Next = VMS_Symbols;
+ VMS_Symbols = vsp;
+ sp->sy_number = (int) vsp;
+ break;
+ /*
+ * Global initialized data
+ */
+ case N_DATA | N_EXT:
+ /*
+ * Make a VMS data symbol entry
+ */
+ vsp = (struct VMS_Symbol *)
+ xmalloc (sizeof (*vsp));
+ vsp->Symbol = sp;
+ vsp->Size = VMS_Initialized_Data_Size (sp,
+ text_siz + data_siz);
+ vsp->Psect_Index = Psect_Number++;
+ vsp->Psect_Offset = 0;
+ vsp->Next = VMS_Symbols;
+ VMS_Symbols = vsp;
+ sp->sy_number = (int) vsp;
+ /*
+ * Make its psect
+ */
+ if (S_GET_OTHER (sp))
+ Globalref = VMS_Psect_Spec (
+ S_GET_NAME (sp),
+ vsp->Size,
+ "CONST",
+ vsp);
+ else
+ Globalref = VMS_Psect_Spec (
+ S_GET_NAME (sp),
+ vsp->Size,
+ "COMMON",
+ vsp);
+ if (Globalref)
+ Psect_Number--;
+#ifdef NOT_VAX_11_C_COMPATIBLE
+ /*
+ * Place a global symbol at the
+ * beginning of the Psect
+ */
+ VMS_Global_Symbol_Spec (S_GET_NAME (sp),
+ vsp->Psect_Index,
+ 0,
+ 1);
+#endif /* NOT_VAX_11_C_COMPATIBLE */
+ break;
+ /*
+ * Local initialized data
+ */
+ case N_DATA:
+ /*
+ * Make a VMS data symbol entry
+ */
+ vsp = (struct VMS_Symbol *)
+ xmalloc (sizeof (*vsp));
+ vsp->Symbol = sp;
+ vsp->Size =
+ VMS_Initialized_Data_Size (sp,
+ text_siz + data_siz);
+ vsp->Psect_Index = Data_Psect;
+ vsp->Psect_Offset =
+ Local_Initialized_Data_Size;
+ Local_Initialized_Data_Size += vsp->Size;
+ vsp->Next = VMS_Symbols;
+ VMS_Symbols = vsp;
+ sp->sy_number = (int) vsp;
+ break;
+ /*
+ * Global Text definition
+ */
+ case N_TEXT | N_EXT:
+ {
+ unsigned short Entry_Mask;
+
+ /*
+ * Get the entry mask
+ */
+ fragP = sp->sy_frag;
+ Entry_Mask = (fragP->fr_literal[0] & 0xff) +
+ ((fragP->fr_literal[1] & 0xff)
+ << 8);
+ /*
+ * Define the Procedure entry pt.
+ */
+ VMS_Procedure_Entry_Pt (S_GET_NAME (sp),
+ Text_Psect,
+ S_GET_VALUE (sp),
+ Entry_Mask);
+ break;
+ }
+ /*
+ * Local Text definition
+ */
+ case N_TEXT:
+ /*
+ * Make a VMS data symbol entry
+ */
+ if (Text_Psect != -1)
+ {
+ vsp = (struct VMS_Symbol *)
+ xmalloc (sizeof (*vsp));
+ vsp->Symbol = sp;
+ vsp->Size = 0;
+ vsp->Psect_Index = Text_Psect;
+ vsp->Psect_Offset = S_GET_VALUE (sp);
+ vsp->Next = VMS_Symbols;
+ VMS_Symbols = vsp;
+ sp->sy_number = (int) vsp;
+ }
+ break;
+ /*
+ * Global Reference
+ */
+ case N_UNDF:
+ /*
+ * Make a GSD global symbol reference
+ * record.
+ */
+ VMS_Global_Symbol_Spec (S_GET_NAME (sp),
+ 0,
+ 0,
+ 0);
+ break;
+ /*
+ * Anything else
+ */
+ default:
+ /*
+ * Ignore STAB symbols
+ * Including .stabs emitted by g++
+ */
+ if (S_IS_DEBUG (sp) || (S_GET_TYPE (sp) == 22))
+ break;
+ /*
+ * Error
+ */
+ if (S_GET_TYPE (sp) != 22)
+ printf (" ERROR, unknown type (%d)\n",
+ S_GET_TYPE (sp));
+ break;
+ }
+ }
+ /*
+ * Define the Data Psect
+ */
+ if ((data_siz > 0) && (Local_Initialized_Data_Size > 0))
+ {
+ /*
+ * Do it
+ */
+ Data_Psect = Psect_Number++;
+ VMS_Psect_Spec ("$data",
+ Local_Initialized_Data_Size,
+ "DATA", 0);
+ /*
+ * Scan the VMS symbols and fill in the data psect
+ */
+ for (vsp = VMS_Symbols; vsp; vsp = vsp->Next)
+ {
+ /*
+ * Only look for undefined psects
+ */
+ if (vsp->Psect_Index < 0)
+ {
+ /*
+ * And only initialized data
+ */
+ if ((S_GET_TYPE (vsp->Symbol) == N_DATA) && !S_IS_EXTERNAL (vsp->Symbol))
+ vsp->Psect_Index = Data_Psect;
+ }
+ }
+ }
+
+ /******* Text Information and Relocation Records *******/
+ /*
+ * Write the text segment data
+ */
+ if (text_siz > 0)
+ {
+ /*
+ * Scan the text fragments
+ */
+ for (fragP = text_frag_root; fragP; fragP = fragP->fr_next)
+ {
+ /*
+ * Stop if we get to the data fragments
+ */
+ if (fragP == data_frag_root)
+ break;
+ /*
+ * Ignore fragments with no data
+ */
+ if ((fragP->fr_fix == 0) && (fragP->fr_var == 0))
+ continue;
+ /*
+ * Go the the appropriate offset in the
+ * Text Psect.
+ */
+ VMS_Set_Psect (Text_Psect, fragP->fr_address, OBJ_S_C_TIR);
+ /*
+ * Store the "fixed" part
+ */
+ if (fragP->fr_fix)
+ VMS_Store_Immediate_Data (fragP->fr_literal,
+ fragP->fr_fix,
+ OBJ_S_C_TIR);
+ /*
+ * Store the "variable" part
+ */
+ if (fragP->fr_var && fragP->fr_offset)
+ VMS_Store_Repeated_Data (fragP->fr_offset,
+ fragP->fr_literal +
+ fragP->fr_fix,
+ fragP->fr_var,
+ OBJ_S_C_TIR);
+ }
+ /*
+ * Now we go through the text segment fixups and
+ * generate TIR records to fix up addresses within
+ * the Text Psect
+ */
+ for (fixP = text_fix_root; fixP; fixP = fixP->fx_next)
+ {
+ /*
+ * We DO handle the case of "Symbol - Symbol" as
+ * long as it is in the same segment.
+ */
+ if (fixP->fx_subsy && fixP->fx_addsy)
+ {
+ int i;
+
+ /*
+ * They need to be in the same segment
+ */
+ if (S_GET_RAW_TYPE (fixP->fx_subsy) !=
+ S_GET_RAW_TYPE (fixP->fx_addsy))
+ error ("Fixup data addsy and subsy didn't have the same type");
+ /*
+ * And they need to be in one that we
+ * can check the psect on
+ */
+ if ((S_GET_TYPE (fixP->fx_addsy) != N_DATA) &&
+ (S_GET_TYPE (fixP->fx_addsy) != N_TEXT))
+ error ("Fixup data addsy and subsy didn't have an appropriate type");
+ /*
+ * This had better not be PC relative!
+ */
+ if (fixP->fx_pcrel)
+ error ("Fixup data was erroneously \"pcrel\"");
+ /*
+ * Subtract their values to get the
+ * difference.
+ */
+ i = S_GET_VALUE (fixP->fx_addsy) -
+ S_GET_VALUE (fixP->fx_subsy);
+ /*
+ * Now generate the fixup object records
+ * Set the psect and store the data
+ */
+ VMS_Set_Psect (Text_Psect,
+ fixP->fx_where +
+ fixP->fx_frag->fr_address,
+ OBJ_S_C_TIR);
+ VMS_Store_Immediate_Data (&i,
+ fixP->fx_size,
+ OBJ_S_C_TIR);
+ /*
+ * Done
+ */
+ continue;
+ }
+ /*
+ * Size will HAVE to be "long"
+ */
+ if (fixP->fx_size != sizeof (long))
+ error ("Fixup datum was not a longword");
+ /*
+ * Symbol must be "added" (if it is ever
+ * subtracted we can
+ * fix this assumption)
+ */
+ if (fixP->fx_addsy == 0)
+ error ("Fixup datum was not \"fixP->fx_addsy\"");
+ /*
+ * Store the symbol value in a PIC fashion
+ */
+ VMS_Store_PIC_Symbol_Reference (fixP->fx_addsy,
+ fixP->fx_offset,
+ fixP->fx_pcrel,
+ Text_Psect,
+ fixP->fx_where +
+ fixP->fx_frag->fr_address,
+ OBJ_S_C_TIR);
+ /*
+ * Check for indirect address reference,
+ * which has to be fixed up (as the linker
+ * will screw it up with TIR_S_C_STO_PICR).
+ */
+ if (fixP->fx_pcrel)
+ VMS_Fix_Indirect_Reference (Text_Psect,
+ fixP->fx_where +
+ fixP->fx_frag->fr_address,
+ fixP->fx_frag,
+ text_frag_root);
+ }
+ }
+ /*
+ * Store the Data segment:
+ *
+ * Since this is REALLY hard to do any other way,
+ * we actually manufacture the data segment and
+ * the store the appropriate values out of it.
+ * The segment was manufactured before, now we just
+ * dump it into the appropriate psects.
+ */
+ if (data_siz > 0)
+ {
+
+ /*
+ * Now we can run through all the data symbols
+ * and store the data
+ */
+ for (vsp = VMS_Symbols; vsp; vsp = vsp->Next)
+ {
+ /*
+ * Ignore anything other than data symbols
+ */
+ if (S_GET_TYPE (vsp->Symbol) != N_DATA)
+ continue;
+ /*
+ * Set the Psect + Offset
+ */
+ VMS_Set_Psect (vsp->Psect_Index,
+ vsp->Psect_Offset,
+ OBJ_S_C_TIR);
+ /*
+ * Store the data
+ */
+ VMS_Store_Immediate_Data (Data_Segment +
+ S_GET_VALUE (vsp->Symbol) -
+ text_siz,
+ vsp->Size,
+ OBJ_S_C_TIR);
+ }
+ /*
+ * Now we go through the data segment fixups and
+ * generate TIR records to fix up addresses within
+ * the Data Psects
+ */
+ for (fixP = data_fix_root; fixP; fixP = fixP->fx_next)
+ {
+ /*
+ * Find the symbol for the containing datum
+ */
+ for (vsp = VMS_Symbols; vsp; vsp = vsp->Next)
+ {
+ /*
+ * Only bother with Data symbols
+ */
+ sp = vsp->Symbol;
+ if (S_GET_TYPE (sp) != N_DATA)
+ continue;
+ /*
+ * Ignore symbol if After fixup
+ */
+ if (S_GET_VALUE (sp) >
+ (fixP->fx_where +
+ fixP->fx_frag->fr_address))
+ continue;
+ /*
+ * See if the datum is here
+ */
+ if ((S_GET_VALUE (sp) + vsp->Size) <=
+ (fixP->fx_where +
+ fixP->fx_frag->fr_address))
+ continue;
+ /*
+ * We DO handle the case of "Symbol - Symbol" as
+ * long as it is in the same segment.
+ */
+ if (fixP->fx_subsy && fixP->fx_addsy)
+ {
+ int i;
+
+ /*
+ * They need to be in the same segment
+ */
+ if (S_GET_RAW_TYPE (fixP->fx_subsy) !=
+ S_GET_RAW_TYPE (fixP->fx_addsy))
+ error ("Fixup data addsy and subsy didn't have the same type");
+ /*
+ * And they need to be in one that we
+ * can check the psect on
+ */
+ if ((S_GET_TYPE (fixP->fx_addsy) != N_DATA) &&
+ (S_GET_TYPE (fixP->fx_addsy) != N_TEXT))
+ error ("Fixup data addsy and subsy didn't have an appropriate type");
+ /*
+ * This had better not be PC relative!
+ */
+ if (fixP->fx_pcrel)
+ error ("Fixup data was erroneously \"pcrel\"");
+ /*
+ * Subtract their values to get the
+ * difference.
+ */
+ i = S_GET_VALUE (fixP->fx_addsy) -
+ S_GET_VALUE (fixP->fx_subsy);
+ /*
+ * Now generate the fixup object records
+ * Set the psect and store the data
+ */
+ VMS_Set_Psect (vsp->Psect_Index,
+ fixP->fx_frag->fr_address +
+ fixP->fx_where -
+ S_GET_VALUE (vsp->Symbol) +
+ vsp->Psect_Offset,
+ OBJ_S_C_TIR);
+ VMS_Store_Immediate_Data (&i,
+ fixP->fx_size,
+ OBJ_S_C_TIR);
+ /*
+ * Done
+ */
+ break;
+ }
+ /*
+ * Size will HAVE to be "long"
+ */
+ if (fixP->fx_size != sizeof (long))
+ error ("Fixup datum was not a longword");
+ /*
+ * Symbol must be "added" (if it is ever
+ * subtracted we can
+ * fix this assumption)
+ */
+ if (fixP->fx_addsy == 0)
+ error ("Fixup datum was not \"fixP->fx_addsy\"");
+ /*
+ * Store the symbol value in a PIC fashion
+ */
+ VMS_Store_PIC_Symbol_Reference (
+ fixP->fx_addsy,
+ fixP->fx_offset,
+ fixP->fx_pcrel,
+ vsp->Psect_Index,
+ fixP->fx_frag->fr_address +
+ fixP->fx_where -
+ S_GET_VALUE (vsp->Symbol) +
+ vsp->Psect_Offset,
+ OBJ_S_C_TIR);
+ /*
+ * Done
+ */
+ break;
+ }
+
+ }
+ }
+
+ /*
+ * Write the Traceback Begin Module record
+ */
+ VMS_TBT_Module_Begin ();
+ /*
+ * Scan the symbols and write out the routines
+ * (this makes the assumption that symbols are in
+ * order of ascending text segment offset)
+ */
+ {
+ struct symbol *Current_Routine = 0;
+ int Current_Line_Number = 0;
+ int Current_Offset = -1;
+ struct input_file *Current_File;
+
+/* Output debugging info for global variables and static variables that are not
+ * specific to one routine. We also need to examine all stabs directives, to
+ * find the definitions to all of the advanced data types, and this is done by
+ * VMS_LSYM_Parse. This needs to be done before any definitions are output to
+ * the object file, since there can be forward references in the stabs
+ * directives. When through with parsing, the text of the stabs directive
+ * is altered, with the definitions removed, so that later passes will see
+ * directives as they would be written if the type were already defined.
+ *
+ * We also look for files and include files, and make a list of them. We
+ * examine the source file numbers to establish the actual lines that code was
+ * generated from, and then generate offsets.
+ */
+ VMS_LSYM_Parse ();
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next (symbolP))
+ {
+ /*
+ * Deal with STAB symbols
+ */
+ if (S_IS_DEBUG (symbolP))
+ {
+ /*
+ * Dispatch on STAB type
+ */
+ switch ((unsigned char) S_GET_RAW_TYPE (symbolP))
+ {
+ case N_SLINE:
+ if (S_GET_DESC (symbolP) > Current_File->max_line)
+ Current_File->max_line = S_GET_DESC (symbolP);
+ if (S_GET_DESC (symbolP) < Current_File->min_line)
+ Current_File->min_line = S_GET_DESC (symbolP);
+ break;
+ case N_SO:
+ Current_File = find_file (symbolP);
+ Current_File->flag = 1;
+ Current_File->min_line = 1;
+ break;
+ case N_SOL:
+ Current_File = find_file (symbolP);
+ break;
+ case N_GSYM:
+ VMS_GSYM_Parse (symbolP, Text_Psect);
+ break;
+ case N_LCSYM:
+ VMS_LCSYM_Parse (symbolP, Text_Psect);
+ break;
+ case N_FUN: /* For static constant symbols */
+ case N_STSYM:
+ VMS_STSYM_Parse (symbolP, Text_Psect);
+ break;
+ }
+ }
+ }
+
+ /* now we take a quick sweep through the files and assign offsets
+ to each one. This will essentially be the starting line number to the
+ debugger for each file. Output the info for the debugger to specify the
+ files, and then tell it how many lines to use */
+ {
+ int File_Number = 0;
+ int Debugger_Offset = 0;
+ int file_available;
+ Current_File = file_root;
+ for (Current_File = file_root; Current_File; Current_File = Current_File->next)
+ {
+ if (Current_File == (struct input_file *) NULL)
+ break;
+ if (Current_File->max_line == 0)
+ continue;
+ if ((strncmp (Current_File->name, "GNU_GXX_INCLUDE:", 16) == 0) &&
+ !flagseen['D'])
+ continue;
+ if ((strncmp (Current_File->name, "GNU_CC_INCLUDE:", 15) == 0) &&
+ !flagseen['D'])
+ continue;
+/* show a few extra lines at the start of the region selected */
+ if (Current_File->min_line > 2)
+ Current_File->min_line -= 2;
+ Current_File->offset = Debugger_Offset - Current_File->min_line + 1;
+ Debugger_Offset += Current_File->max_line - Current_File->min_line + 1;
+ if (Current_File->same_file_fpnt != (struct input_file *) NULL)
+ Current_File->file_number = Current_File->same_file_fpnt->file_number;
+ else
+ {
+ Current_File->file_number = ++File_Number;
+ file_available = VMS_TBT_Source_File (Current_File->name,
+ Current_File->file_number);
+ if (!file_available)
+ {
+ Current_File->file_number = 0;
+ File_Number--;
+ continue;
+ };
+ };
+ VMS_TBT_Source_Lines (Current_File->file_number,
+ Current_File->min_line,
+ Current_File->max_line - Current_File->min_line + 1);
+ }; /* for */
+ }; /* scope */
+ Current_File = (struct input_file *) NULL;
+
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next (symbolP))
+ {
+ /*
+ * Deal with text symbols
+ */
+ if (!S_IS_DEBUG (symbolP) && (S_GET_TYPE (symbolP) == N_TEXT))
+ {
+ /*
+ * Ignore symbols starting with "L",
+ * as they are local symbols
+ */
+ if (*S_GET_NAME (symbolP) == 'L')
+ continue;
+ /*
+ * If there is a routine start defined,
+ * terminate it.
+ */
+ if (Current_Routine)
+ {
+ /*
+ * End the routine
+ */
+ VMS_TBT_Routine_End (text_siz, Current_Routine);
+ }
+ /*
+ * Store the routine begin traceback info
+ */
+ if (Text_Psect != -1)
+ {
+ VMS_TBT_Routine_Begin (symbolP, Text_Psect);
+ Current_Routine = symbolP;
+ }
+/* Output local symbols, i.e. all symbols that are associated with a specific
+ * routine. We output them now so the debugger recognizes them as local to
+ * this routine.
+ */
+ {
+ symbolS *symbolP1;
+ char *pnt;
+ char *pnt1;
+ for (symbolP1 = Current_Routine; symbolP1; symbolP1 = symbol_next (symbolP1))
+ {
+ if (!S_IS_DEBUG (symbolP1))
+ continue;
+ if (S_GET_RAW_TYPE (symbolP1) != N_FUN)
+ continue;
+ pnt = S_GET_NAME (symbolP);
+ pnt1 = S_GET_NAME (symbolP1);
+ if (*pnt++ != '_')
+ continue;
+ while (*pnt++ == *pnt1++)
+ {
+ };
+ if (*pnt1 != 'F' && *pnt1 != 'f') continue;
+ if ((*(--pnt) == '\0') && (*(--pnt1) == ':'))
+ break;
+ };
+ if (symbolP1 != (symbolS *) NULL)
+ VMS_DBG_Define_Routine (symbolP1, Current_Routine, Text_Psect);
+ } /* local symbol block */
+ /*
+ * Done
+ */
+ continue;
+ }
+ /*
+ * Deal with STAB symbols
+ */
+ if (S_IS_DEBUG (symbolP))
+ {
+ /*
+ * Dispatch on STAB type
+ */
+ switch ((unsigned char) S_GET_RAW_TYPE (symbolP))
+ {
+ /*
+ * Line number
+ */
+ case N_SLINE:
+ /* Offset the line into the correct portion
+ * of the file */
+ if (Current_File->file_number == 0)
+ break;
+ /* Sometimes the same offset gets several source
+ * lines assigned to it.
+ * We should be selective about which lines
+ * we allow, we should prefer lines that are
+ * in the main source file when debugging
+ * inline functions. */
+ if ((Current_File->file_number != 1) &&
+ S_GET_VALUE (symbolP) ==
+ Current_Offset)
+ break;
+ /* calculate actual debugger source line */
+ S_GET_DESC (symbolP)
+ += Current_File->offset;
+ /*
+ * If this is the 1st N_SLINE, setup
+ * PC/Line correlation. Otherwise
+ * do the delta PC/Line. If the offset
+ * for the line number is not +ve we need
+ * to do another PC/Line correlation
+ * setup
+ */
+ if (Current_Offset == -1)
+ {
+ VMS_TBT_Line_PC_Correlation (
+ S_GET_DESC (symbolP),
+ S_GET_VALUE (symbolP),
+ Text_Psect,
+ 0);
+ }
+ else
+ {
+ if ((S_GET_DESC (symbolP) -
+ Current_Line_Number) <= 0)
+ {
+ /*
+ * Line delta is not +ve, we
+ * need to close the line and
+ * start a new PC/Line
+ * correlation.
+ */
+ VMS_TBT_Line_PC_Correlation (0,
+ S_GET_VALUE (symbolP) -
+ Current_Offset,
+ 0,
+ -1);
+ VMS_TBT_Line_PC_Correlation (
+ S_GET_DESC (symbolP),
+ S_GET_VALUE (symbolP),
+ Text_Psect,
+ 0);
+ }
+ else
+ {
+ /*
+ * Line delta is +ve, all is well
+ */
+ VMS_TBT_Line_PC_Correlation (
+ S_GET_DESC (symbolP) -
+ Current_Line_Number,
+ S_GET_VALUE (symbolP) -
+ Current_Offset,
+ 0,
+ 1);
+ }
+ }
+ /*
+ * Update the current line/PC
+ */
+ Current_Line_Number = S_GET_DESC (symbolP);
+ Current_Offset = S_GET_VALUE (symbolP);
+ /*
+ * Done
+ */
+ break;
+ /*
+ * Source file
+ */
+ case N_SO:
+ /*
+ * Remember that we had a source file
+ * and emit the source file debugger
+ * record
+ */
+ Current_File =
+ find_file (symbolP);
+ break;
+/* We need to make sure that we are really in the actual source file when
+ * we compute the maximum line number. Otherwise the debugger gets really
+ * confused */
+ case N_SOL:
+ Current_File =
+ find_file (symbolP);
+ break;
+ }
+ }
+ }
+ /*
+ * If there is a routine start defined,
+ * terminate it (and the line numbers)
+ */
+ if (Current_Routine)
+ {
+ /*
+ * Terminate the line numbers
+ */
+ VMS_TBT_Line_PC_Correlation (0,
+ text_siz - S_GET_VALUE (Current_Routine),
+ 0,
+ -1);
+ /*
+ * Terminate the routine
+ */
+ VMS_TBT_Routine_End (text_siz, Current_Routine);
+ }
+ }
+ /*
+ * Write the Traceback End Module TBT record
+ */
+ VMS_TBT_Module_End ();
+
+ /*
+ * Write the End Of Module record
+ */
+ if (Entry_Point_Symbol == 0)
+ Write_VMS_EOM_Record (-1, 0);
+ else
+ Write_VMS_EOM_Record (Text_Psect,
+ S_GET_VALUE (Entry_Point_Symbol));
+
+ /*
+ * All done, close the object file
+ */
+ Close_VMS_Object_File ();
+}
+
+/* end of obj-vms.c */
diff --git a/gnu/usr.bin/as/config/obj-vms.h b/gnu/usr.bin/as/config/obj-vms.h
new file mode 100644
index 0000000..fec056d
--- /dev/null
+++ b/gnu/usr.bin/as/config/obj-vms.h
@@ -0,0 +1,474 @@
+/* VMS object file format
+ Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2,
+or (at your option) any later version.
+
+GAS is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public
+License along with GAS; see the file COPYING. If not, write
+to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Tag to validate a.out object file format processing */
+#define OBJ_VMS 1
+
+#include "targ-cpu.h"
+
+/* This flag is used to remember whether we are in the const or the
+ data section. By and large they are identical, but we set a no-write
+ bit for psects in the const section. */
+
+extern char const_flag;
+
+
+/* These are defined in obj-vms.c. */
+extern const short seg_N_TYPE[];
+extern const segT N_TYPE_seg[];
+
+enum reloc_type {
+ NO_RELOC, RELOC_32
+};
+
+#define N_BADMAG(x) (0)
+#define N_TXTOFF(x) ( sizeof(struct exec) )
+#define N_DATOFF(x) ( N_TXTOFF(x) + (x).a_text )
+#define N_TROFF(x) ( N_DATOFF(x) + (x).a_data )
+#define N_DROFF(x) ( N_TROFF(x) + (x).a_trsize )
+#define N_SYMOFF(x) ( N_DROFF(x) + (x).a_drsize )
+#define N_STROFF(x) ( N_SYMOFF(x) + (x).a_syms )
+
+/* We use this copy of the exec header for VMS. We do not actually use it, but
+ what we actually do is let gas fill in the relevant slots, and when we get
+ around to writing an obj file, we just pick out what we need. */
+
+struct exec
+{
+ unsigned long a_text; /* length of text, in bytes */
+ unsigned long a_data; /* length of data, in bytes */
+ unsigned long a_bss; /* length of uninitialized data area for file, in bytes */
+ unsigned long a_trsize; /* length of relocation info for text, in bytes */
+ unsigned long a_drsize; /* length of relocation info for data, in bytes */
+ unsigned long a_entry; /* start address */
+ unsigned long a_syms; /* length of symbol table data in file, in bytes */
+};
+
+typedef struct {
+ struct exec header; /* a.out header */
+ long string_table_size; /* names + '\0' + sizeof(int) */
+} object_headers;
+
+/* A single entry in the symbol table
+ */
+struct nlist {
+ union {
+ char *n_name;
+ struct nlist *n_next;
+ long n_strx; /* Index into string table */
+ } n_un;
+ unsigned char n_type; /* See below */
+ char n_other; /* Used in i80960 support -- see below */
+ short n_desc;
+ unsigned long n_value;
+};
+
+/* Legal values of n_type
+ */
+#define N_UNDF 0 /* Undefined symbol */
+#define N_ABS 2 /* Absolute symbol */
+#define N_TEXT 4 /* Text symbol */
+#define N_DATA 6 /* Data symbol */
+#define N_BSS 8 /* BSS symbol */
+#define N_FN 31 /* Filename symbol */
+
+#define N_EXT 1 /* External symbol (OR'd in with one of above) */
+#define N_TYPE 036 /* Mask for all the type bits */
+
+#define N_STAB 0340 /* Mask for all bits used for SDB entries */
+
+#define N_GSYM 0x20 /* global symbol: name,,0,type,0 */
+#define N_FNAME 0x22 /* procedure name (f77 kludge): name,,0 */
+#define N_FUN 0x24 /* procedure: name,,0,linenumber,address */
+#define N_STSYM 0x26 /* static symbol: name,,0,type,address */
+#define N_LCSYM 0x28 /* .lcomm symbol: name,,0,type,address */
+#define N_RSYM 0x40 /* register sym: name,,0,type,register */
+#define N_SLINE 0x44 /* src line: 0,,0,linenumber,address */
+#define N_CATCH 0x54 /* */
+#define N_SSYM 0x60 /* structure elt: name,,0,type,struct_offset */
+#define N_SO 0x64 /* source file name: name,,0,0,address */
+#define N_LSYM 0x80 /* local sym: name,,0,type,offset */
+#define N_SOL 0x84 /* #included file name: name,,0,0,address */
+#define N_PSYM 0xa0 /* parameter: name,,0,type,offset */
+#define N_ENTRY 0xa4 /* alternate entry: name,linenumber,address */
+#define N_LBRAC 0xc0 /* left bracket: 0,,0,nesting level,address */
+#define N_RBRAC 0xe0 /* right bracket: 0,,0,nesting level,address */
+#define N_BCOMM 0xe2 /* begin common: name,, */
+#define N_ECOMM 0xe4 /* end common: name,, */
+#define N_ECOML 0xe8 /* end common (local name): ,,address */
+#define N_LENG 0xfe /* second stab entry with length information */
+
+/* SYMBOL TABLE */
+/* Symbol table entry data type */
+
+typedef struct nlist obj_symbol_type; /* Symbol table entry */
+
+/* Symbol table macros and constants */
+
+/*
+ * Macros to extract information from a symbol table entry.
+ * This syntaxic indirection allows independence regarding a.out or coff.
+ * The argument (s) of all these macros is a pointer to a symbol table entry.
+ */
+
+/* True if the symbol is external */
+#define S_IS_EXTERNAL(s) ((s)->sy_symbol.n_type & N_EXT)
+
+/* True if symbol has been defined, ie is in N_{TEXT,DATA,BSS,ABS} or N_EXT */
+#define S_IS_DEFINED(s) (S_GET_TYPE(s) != N_UNDF)
+
+#define S_IS_REGISTER(s) ((s)->sy_symbol.n_type == N_REGISTER)
+
+/* True if a debug special symbol entry */
+#define S_IS_DEBUG(s) ((s)->sy_symbol.n_type & N_STAB)
+/* True if a symbol is local symbol name */
+/* A symbol name whose name begin with ^A is a gas internal pseudo symbol
+ nameless symbols come from .stab directives. */
+#define S_IS_LOCAL(s) (S_GET_NAME(s) && \
+ !S_IS_DEBUG(s) && \
+ (S_GET_NAME(s)[0] == '\001' || \
+ (S_LOCAL_NAME(s) && !flagseen['L'])))
+/* True if a symbol is not defined in this file */
+#define S_IS_EXTERN(s) ((s)->sy_symbol.n_type & N_EXT)
+/* True if the symbol has been generated because of a .stabd directive */
+#define S_IS_STABD(s) (S_GET_NAME(s) == (char *)0)
+
+/* Accessors */
+/* The value of the symbol */
+#define S_GET_VALUE(s) (((s)->sy_symbol.n_value))
+/* The name of the symbol */
+#define S_GET_NAME(s) ((s)->sy_symbol.n_un.n_name)
+/* The pointer to the string table */
+#define S_GET_OFFSET(s) ((s)->sy_symbol.n_un.n_strx)
+/* The raw type of the symbol */
+#define S_GET_RAW_TYPE(s) ((s)->sy_symbol.n_type)
+/* The type of the symbol */
+#define S_GET_TYPE(s) ((s)->sy_symbol.n_type & N_TYPE)
+/* The numeric value of the segment */
+#define S_GET_SEGMENT(s) (N_TYPE_seg[S_GET_TYPE(s)])
+/* The n_other expression value */
+#define S_GET_OTHER(s) ((s)->sy_symbol.n_other)
+/* The n_desc expression value */
+#define S_GET_DESC(s) ((s)->sy_symbol.n_desc)
+
+/* Modifiers */
+/* Set the value of the symbol */
+#define S_SET_VALUE(s,v) ((s)->sy_symbol.n_value = (unsigned long) (v))
+/* Assume that a symbol cannot be simultaneously in more than on segment */
+ /* set segment */
+#define S_SET_SEGMENT(s,seg) ((s)->sy_symbol.n_type &= ~N_TYPE,(s)->sy_symbol.n_type|=SEGMENT_TO_SYMBOL_TYPE(seg))
+/* The symbol is external */
+#define S_SET_EXTERNAL(s) ((s)->sy_symbol.n_type |= N_EXT)
+/* The symbol is not external */
+#define S_CLEAR_EXTERNAL(s) ((s)->sy_symbol.n_type &= ~N_EXT)
+/* Set the name of the symbol */
+#define S_SET_NAME(s,v) ((s)->sy_symbol.n_un.n_name = (v))
+/* Set the offset in the string table */
+#define S_SET_OFFSET(s,v) ((s)->sy_symbol.n_un.n_strx = (v))
+/* Set the n_other expression value */
+#define S_SET_OTHER(s,v) ((s)->sy_symbol.n_other = (v))
+/* Set the n_desc expression value */
+#define S_SET_DESC(s,v) ((s)->sy_symbol.n_desc = (v))
+
+
+/* File header macro and type definition */
+
+#define H_GET_TEXT_SIZE(h) ((h)->header.a_text)
+#define H_GET_DATA_SIZE(h) ((h)->header.a_data)
+#define H_GET_BSS_SIZE(h) ((h)->header.a_bss)
+
+#define H_SET_TEXT_SIZE(h,v) ((h)->header.a_text = md_section_align(SEG_TEXT, (v)))
+#define H_SET_DATA_SIZE(h,v) ((h)->header.a_data = md_section_align(SEG_DATA, (v)))
+#define H_SET_BSS_SIZE(h,v) ((h)->header.a_bss = md_section_align(SEG_BSS, (v)))
+
+#define H_SET_STRING_SIZE(h,v) ((h)->string_table_size = (v))
+#define H_SET_SYMBOL_TABLE_SIZE(h,v) ((h)->header.a_syms = (v) * \
+ sizeof(struct nlist))
+
+/*
+ * Current means for getting the name of a segment.
+ * This will change for infinite-segments support (e.g. COFF).
+ */
+#define segment_name(seg) ( seg_name[(int)(seg)] )
+extern char *const seg_name[];
+
+
+/* line numbering stuff. */
+#define OBJ_EMIT_LINENO(a, b, c) {;}
+
+#define obj_symbol_new_hook(s) {;}
+
+#ifdef __STDC__
+struct fix;
+void tc_aout_fix_to_chars(char *where, struct fix *fixP, relax_addressT segment_address);
+#else
+void tc_aout_fix_to_chars();
+#endif /* __STDC__ */
+
+/* The rest of this file contains definitions for constants used within the actual
+ VMS object file. We do not use a $ in the symbols (as per usual VMS
+ convention) since System V gags on it. */
+
+#define OBJ_S_C_HDR 0
+#define OBJ_S_C_HDR_MHD 0
+#define OBJ_S_C_HDR_LNM 1
+#define OBJ_S_C_HDR_SRC 2
+#define OBJ_S_C_HDR_TTL 3
+#define OBJ_S_C_HDR_CPR 4
+#define OBJ_S_C_HDR_MTC 5
+#define OBJ_S_C_HDR_GTX 6
+#define OBJ_S_C_GSD 1
+#define OBJ_S_C_GSD_PSC 0
+#define OBJ_S_C_GSD_SYM 1
+#define OBJ_S_C_GSD_EPM 2
+#define OBJ_S_C_GSD_PRO 3
+#define OBJ_S_C_GSD_SYMW 4
+#define OBJ_S_C_GSD_EPMW 5
+#define OBJ_S_C_GSD_PROW 6
+#define OBJ_S_C_GSD_IDC 7
+#define OBJ_S_C_GSD_ENV 8
+#define OBJ_S_C_GSD_LSY 9
+#define OBJ_S_C_GSD_LEPM 10
+#define OBJ_S_C_GSD_LPRO 11
+#define OBJ_S_C_GSD_SPSC 12
+#define OBJ_S_C_TIR 2
+#define OBJ_S_C_EOM 3
+#define OBJ_S_C_DBG 4
+#define OBJ_S_C_TBT 5
+#define OBJ_S_C_LNK 6
+#define OBJ_S_C_EOMW 7
+#define OBJ_S_C_MAXRECTYP 7
+#define OBJ_S_K_SUBTYP 1
+#define OBJ_S_C_SUBTYP 1
+#define OBJ_S_C_MAXRECSIZ 2048
+#define OBJ_S_C_STRLVL 0
+#define OBJ_S_C_SYMSIZ 31
+#define OBJ_S_C_STOREPLIM -1
+#define OBJ_S_C_PSCALILIM 9
+
+#define MHD_S_C_MHD 0
+#define MHD_S_C_LNM 1
+#define MHD_S_C_SRC 2
+#define MHD_S_C_TTL 3
+#define MHD_S_C_CPR 4
+#define MHD_S_C_MTC 5
+#define MHD_S_C_GTX 6
+#define MHD_S_C_MAXHDRTYP 6
+
+#define GSD_S_K_ENTRIES 1
+#define GSD_S_C_ENTRIES 1
+#define GSD_S_C_PSC 0
+#define GSD_S_C_SYM 1
+#define GSD_S_C_EPM 2
+#define GSD_S_C_PRO 3
+#define GSD_S_C_SYMW 4
+#define GSD_S_C_EPMW 5
+#define GSD_S_C_PROW 6
+#define GSD_S_C_IDC 7
+#define GSD_S_C_ENV 8
+#define GSD_S_C_LSY 9
+#define GSD_S_C_LEPM 10
+#define GSD_S_C_LPRO 11
+#define GSD_S_C_SPSC 12
+#define GSD_S_C_SYMV 13
+#define GSD_S_C_EPMV 14
+#define GSD_S_C_PROV 15
+#define GSD_S_C_MAXRECTYP 15
+
+#define GSY_S_M_WEAK 1
+#define GSY_S_M_DEF 2
+#define GSY_S_M_UNI 4
+#define GSY_S_M_REL 8
+
+#define GPS_S_M_PIC 1
+#define GPS_S_M_LIB 2
+#define GPS_S_M_OVR 4
+#define GPS_S_M_REL 8
+#define GPS_S_M_GBL 16
+#define GPS_S_M_SHR 32
+#define GPS_S_M_EXE 64
+#define GPS_S_M_RD 128
+#define GPS_S_M_WRT 256
+#define GPS_S_M_VEC 512
+#define GPS_S_K_NAME 9
+#define GPS_S_C_NAME 9
+
+#define TIR_S_C_STA_GBL 0
+#define TIR_S_C_STA_SB 1
+#define TIR_S_C_STA_SW 2
+#define TIR_S_C_STA_LW 3
+#define TIR_S_C_STA_PB 4
+#define TIR_S_C_STA_PW 5
+#define TIR_S_C_STA_PL 6
+#define TIR_S_C_STA_UB 7
+#define TIR_S_C_STA_UW 8
+#define TIR_S_C_STA_BFI 9
+#define TIR_S_C_STA_WFI 10
+#define TIR_S_C_STA_LFI 11
+#define TIR_S_C_STA_EPM 12
+#define TIR_S_C_STA_CKARG 13
+#define TIR_S_C_STA_WPB 14
+#define TIR_S_C_STA_WPW 15
+#define TIR_S_C_STA_WPL 16
+#define TIR_S_C_STA_LSY 17
+#define TIR_S_C_STA_LIT 18
+#define TIR_S_C_STA_LEPM 19
+#define TIR_S_C_MAXSTACOD 19
+#define TIR_S_C_MINSTOCOD 20
+#define TIR_S_C_STO_SB 20
+#define TIR_S_C_STO_SW 21
+#define TIR_S_C_STO_L 22
+#define TIR_S_C_STO_BD 23
+#define TIR_S_C_STO_WD 24
+#define TIR_S_C_STO_LD 25
+#define TIR_S_C_STO_LI 26
+#define TIR_S_C_STO_PIDR 27
+#define TIR_S_C_STO_PICR 28
+#define TIR_S_C_STO_RSB 29
+#define TIR_S_C_STO_RSW 30
+#define TIR_S_C_STO_RL 31
+#define TIR_S_C_STO_VPS 32
+#define TIR_S_C_STO_USB 33
+#define TIR_S_C_STO_USW 34
+#define TIR_S_C_STO_RUB 35
+#define TIR_S_C_STO_RUW 36
+#define TIR_S_C_STO_B 37
+#define TIR_S_C_STO_W 38
+#define TIR_S_C_STO_RB 39
+#define TIR_S_C_STO_RW 40
+#define TIR_S_C_STO_RIVB 41
+#define TIR_S_C_STO_PIRR 42
+#define TIR_S_C_MAXSTOCOD 42
+#define TIR_S_C_MINOPRCOD 50
+#define TIR_S_C_OPR_NOP 50
+#define TIR_S_C_OPR_ADD 51
+#define TIR_S_C_OPR_SUB 52
+#define TIR_S_C_OPR_MUL 53
+#define TIR_S_C_OPR_DIV 54
+#define TIR_S_C_OPR_AND 55
+#define TIR_S_C_OPR_IOR 56
+#define TIR_S_C_OPR_EOR 57
+#define TIR_S_C_OPR_NEG 58
+#define TIR_S_C_OPR_COM 59
+#define TIR_S_C_OPR_INSV 60
+#define TIR_S_C_OPR_ASH 61
+#define TIR_S_C_OPR_USH 62
+#define TIR_S_C_OPR_ROT 63
+#define TIR_S_C_OPR_SEL 64
+#define TIR_S_C_OPR_REDEF 65
+#define TIR_S_C_OPR_DFLIT 66
+#define TIR_S_C_MAXOPRCOD 66
+#define TIR_S_C_MINCTLCOD 80
+#define TIR_S_C_CTL_SETRB 80
+#define TIR_S_C_CTL_AUGRB 81
+#define TIR_S_C_CTL_DFLOC 82
+#define TIR_S_C_CTL_STLOC 83
+#define TIR_S_C_CTL_STKDL 84
+#define TIR_S_C_MAXCTLCOD 84
+
+/*
+ * Debugger symbol definitions: These are done by hand, as no
+ * machine-readable version seems
+ * to be available.
+ */
+#define DST_S_C_C 7 /* Language == "C" */
+#define DST_S_C_VERSION 153
+#define DST_S_C_SOURCE 155 /* Source file */
+#define DST_S_C_PROLOG 162
+#define DST_S_C_BLKBEG 176 /* Beginning of block */
+#define DST_S_C_BLKEND 177 /* End of block */
+#define DST_S_C_ENTRY 181
+#define DST_S_C_PSECT 184
+#define DST_S_C_LINE_NUM 185 /* Line Number */
+#define DST_S_C_LBLORLIT 186
+#define DST_S_C_LABEL 187
+#define DST_S_C_MODBEG 188 /* Beginning of module */
+#define DST_S_C_MODEND 189 /* End of module */
+#define DST_S_C_RTNBEG 190 /* Beginning of routine */
+#define DST_S_C_RTNEND 191 /* End of routine */
+#define DST_S_C_DELTA_PC_W 1 /* Incr PC */
+#define DST_S_C_INCR_LINUM 2 /* Incr Line # */
+#define DST_S_C_INCR_LINUM_W 3 /* Incr Line # */
+#define DST_S_C_SET_LINUM_INCR 4
+#define DST_S_C_SET_LINUM_INCR_W 5
+#define DST_S_C_RESET_LINUM_INCR 6
+#define DST_S_C_BEG_STMT_MODE 7
+#define DST_S_C_END_STMT_MODE 8
+#define DST_S_C_SET_LINE_NUM 9 /* Set Line # */
+#define DST_S_C_SET_PC 10
+#define DST_S_C_SET_PC_W 11
+#define DST_S_C_SET_PC_L 12
+#define DST_S_C_SET_STMTNUM 13
+#define DST_S_C_TERM 14 /* End of lines */
+#define DST_S_C_TERM_W 15 /* End of lines */
+#define DST_S_C_SET_ABS_PC 16 /* Set PC */
+#define DST_S_C_DELTA_PC_L 17 /* Incr PC */
+#define DST_S_C_INCR_LINUM_L 18 /* Incr Line # */
+#define DST_S_C_SET_LINUM_B 19 /* Set Line # */
+#define DST_S_C_SET_LINUM_L 20 /* Set Line # */
+#define DST_S_C_TERM_L 21 /* End of lines */
+/* these are used with DST_S_C_SOURCE */
+#define DST_S_C_SRC_FORMFEED 16 /* ^L counts */
+#define DST_S_C_SRC_DECLFILE 1 /* Declare file */
+#define DST_S_C_SRC_SETFILE 2 /* Set file */
+#define DST_S_C_SRC_SETREC_L 3 /* Set record */
+#define DST_S_C_SRC_DEFLINES_W 10 /* # of line */
+/* the following are the codes for the various data types. Anything not on
+ * the list is included under 'advanced_type'
+ */
+#define DBG_S_C_UCHAR 0x02
+#define DBG_S_C_USINT 0x03
+#define DBG_S_C_ULINT 0x04
+#define DBG_S_C_SCHAR 0x06
+#define DBG_S_C_SSINT 0x07
+#define DBG_S_C_SLINT 0x08
+#define DBG_S_C_REAL4 0x0a
+#define DBG_S_C_REAL8 0x0b
+#define DBG_S_C_FUNCTION_ADDR 0x17
+#define DBG_S_C_ADVANCED_TYPE 0xa3
+/* These are the codes that are used to generate the definitions of struct
+ * union and enum records
+ */
+#define DBG_S_C_ENUM_ITEM 0xa4
+#define DBG_S_C_ENUM_START 0xa5
+#define DBG_S_C_ENUM_END 0xa6
+#define DBG_S_C_STRUCT_START 0xab
+#define DBG_S_C_STRUCT_ITEM 0xff
+#define DBG_S_C_STRUCT_END 0xac
+/* These are the codes that are used in the suffix records to determine the
+ * actual data type
+ */
+#define DBG_S_C_BASIC 0x01
+#define DBG_S_C_BASIC_ARRAY 0x02
+#define DBG_S_C_STRUCT 0x03
+#define DBG_S_C_POINTER 0x04
+#define DBG_S_C_VOID 0x05
+#define DBG_S_C_COMPLEX_ARRAY 0x07
+/* These codes are used in the generation of the symbol definition records
+ */
+#define DBG_S_C_FUNCTION_PARAMETER 0xc9
+#define DBG_S_C_LOCAL_SYM 0xd9
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of obj-vms.h */
diff --git a/gnu/usr.bin/as/config/tc-a29k.c b/gnu/usr.bin/as/config/tc-a29k.c
new file mode 100644
index 0000000..84ec97a
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-a29k.c
@@ -0,0 +1,1113 @@
+/* tc-a29k.c -- Assemble for the AMD 29000.
+ Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* John Gilmore has reorganized this module somewhat, to make it easier
+ to convert it to new machines' assemblers as desired. There was too
+ much bloody rewriting required before. There still probably is. */
+
+#include "as.h"
+
+#include "opcode/a29k.h"
+
+/* Make it easier to clone this machine desc into another one. */
+#define machine_opcode a29k_opcode
+#define machine_opcodes a29k_opcodes
+#define machine_ip a29k_ip
+#define machine_it a29k_it
+
+const relax_typeS md_relax_table[] = { 0 };
+
+#define IMMEDIATE_BIT 0x01000000 /* Turns RB into Immediate */
+#define ABSOLUTE_BIT 0x01000000 /* Turns PC-relative to Absolute */
+#define CE_BIT 0x00800000 /* Coprocessor enable in LOAD */
+#define UI_BIT 0x00000080 /* Unsigned integer in CONVERT */
+
+/* handle of the OPCODE hash table */
+static struct hash_control *op_hash = NULL;
+
+struct machine_it {
+ char *error;
+ unsigned long opcode;
+ struct nlist *nlistp;
+ expressionS exp;
+ int pcrel;
+ int reloc_offset; /* Offset of reloc within insn */
+ enum reloc_type reloc;
+} the_insn;
+
+#if __STDC__ == 1
+
+/* static int getExpression(char *str); */
+static void machine_ip(char *str);
+/* static void print_insn(struct machine_it *insn); */
+static void s_data1(void);
+static void s_use(void);
+
+#else /* not __STDC__ */
+
+/* static int getExpression(); */
+static void machine_ip();
+/* static void print_insn(); */
+static void s_data1();
+static void s_use();
+
+#endif /* not __STDC__ */
+
+const pseudo_typeS
+ md_pseudo_table[] = {
+ { "align", s_align_bytes, 4 },
+ { "block", s_space, 0 },
+ { "cputype", s_ignore, 0 }, /* CPU as 29000 or 29050 */
+ { "reg", s_lsym, 0 }, /* Register equate, same as equ */
+ { "space", s_ignore, 0 }, /* Listing control */
+ { "sect", s_ignore, 0 }, /* Creation of coff sections */
+ { "use", s_use, 0 },
+ { "word", cons, 4 },
+ { NULL, 0, 0 },
+ };
+
+int md_short_jump_size = 4;
+int md_long_jump_size = 4;
+#if defined(BFD_HEADERS)
+#ifdef RELSZ
+int md_reloc_size = RELSZ; /* Coff headers */
+#else
+int md_reloc_size = 12; /* something else headers */
+#endif
+#else
+int md_reloc_size = 12; /* Not bfdized*/
+#endif
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful */
+char comment_chars[] = ";";
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that comments like this one will always work */
+char line_comment_chars[] = "#";
+
+/* We needed an unused char for line separation to work around the
+ lack of macros, using sed and such. */
+char line_separator_chars[] = "@";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c. Ideally it shouldn't have to know about it at all,
+ but nothing is ideal around here.
+ */
+
+static unsigned char octal[256];
+#define isoctal(c) octal[c]
+ static unsigned char toHex[256];
+
+/*
+ * anull bit - causes the branch delay slot instructions to not be executed
+ */
+#define ANNUL (1 << 29)
+
+static void
+ s_use()
+{
+
+ if (strncmp(input_line_pointer, ".text", 5) == 0) {
+ input_line_pointer += 5;
+ s_text();
+ return;
+ }
+ if (strncmp(input_line_pointer, ".data", 5) == 0) {
+ input_line_pointer += 5;
+ s_data();
+ return;
+ }
+ if (strncmp(input_line_pointer, ".data1", 6) == 0) {
+ input_line_pointer += 6;
+ s_data1();
+ return;
+ }
+ /* Literals can't go in the text segment because you can't read
+ from instruction memory on some 29k's. So, into initialized data. */
+ if (strncmp(input_line_pointer, ".lit", 4) == 0) {
+ input_line_pointer += 4;
+ subseg_new(SEG_DATA, 200);
+ demand_empty_rest_of_line();
+ return;
+ }
+
+ as_bad("Unknown segment type");
+ demand_empty_rest_of_line();
+ return;
+}
+
+static void
+ s_data1()
+{
+ subseg_new(SEG_DATA, 1);
+ demand_empty_rest_of_line();
+ return;
+}
+
+/* Install symbol definition that maps REGNAME to REGNO.
+ FIXME-SOON: These are not recognized in mixed case. */
+
+static void
+ insert_sreg (regname, regnum)
+char *regname;
+int regnum;
+{
+ /* FIXME-SOON, put something in these syms so they won't be output to the symbol
+ table of the resulting object file. */
+
+ /* Must be large enough to hold the names of the special registers. */
+ char buf[80];
+ int i;
+
+ symbol_table_insert(symbol_new(regname, SEG_REGISTER, regnum, &zero_address_frag));
+ for (i = 0; regname[i]; i++)
+ buf[i] = islower (regname[i]) ? toupper (regname[i]) : regname[i];
+ buf[i] = '\0';
+
+ symbol_table_insert(symbol_new(buf, SEG_REGISTER, regnum, &zero_address_frag));
+} /* insert_sreg() */
+
+/* Install symbol definitions for assorted special registers.
+ See ASM29K Ref page 2-9. */
+
+void define_some_regs() {
+#define SREG 256
+
+ /* Protected special-purpose register names */
+ insert_sreg ("vab", SREG+0);
+ insert_sreg ("ops", SREG+1);
+ insert_sreg ("cps", SREG+2);
+ insert_sreg ("cfg", SREG+3);
+ insert_sreg ("cha", SREG+4);
+ insert_sreg ("chd", SREG+5);
+ insert_sreg ("chc", SREG+6);
+ insert_sreg ("rbp", SREG+7);
+ insert_sreg ("tmc", SREG+8);
+ insert_sreg ("tmr", SREG+9);
+ insert_sreg ("pc0", SREG+10);
+ insert_sreg ("pc1", SREG+11);
+ insert_sreg ("pc2", SREG+12);
+ insert_sreg ("mmu", SREG+13);
+ insert_sreg ("lru", SREG+14);
+
+ /* Unprotected special-purpose register names */
+ insert_sreg ("ipc", SREG+128);
+ insert_sreg ("ipa", SREG+129);
+ insert_sreg ("ipb", SREG+130);
+ insert_sreg ("q", SREG+131);
+ insert_sreg ("alu", SREG+132);
+ insert_sreg ("bp", SREG+133);
+ insert_sreg ("fc", SREG+134);
+ insert_sreg ("cr", SREG+135);
+ insert_sreg ("fpe", SREG+160);
+ insert_sreg ("inte",SREG+161);
+ insert_sreg ("fps", SREG+162);
+ /* "", SREG+163); Reserved */
+ insert_sreg ("exop",SREG+164);
+} /* define_some_regs() */
+
+/* This function is called once, at assembler startup time. It should
+ set up all the tables, etc. that the MD part of the assembler will need. */
+void
+ md_begin()
+{
+ register char *retval = NULL;
+ int lose = 0;
+ register int skipnext = 0;
+ register unsigned int i;
+ register char *strend, *strend2;
+
+ /* Hash up all the opcodes for fast use later. */
+
+ op_hash = hash_new();
+ if (op_hash == NULL)
+ as_fatal("Virtual memory exhausted");
+
+ for (i = 0; i < num_opcodes; i++)
+ {
+ const char *name = machine_opcodes[i].name;
+
+ if (skipnext) {
+ skipnext = 0;
+ continue;
+ }
+
+ /* Hack to avoid multiple opcode entries. We pre-locate all the
+ variations (b/i field and P/A field) and handle them. */
+
+ if (!strcmp (name, machine_opcodes[i+1].name)) {
+ if ((machine_opcodes[i].opcode ^ machine_opcodes[i+1].opcode)
+ != 0x01000000)
+ goto bad_table;
+ strend = machine_opcodes[i ].args+strlen(machine_opcodes[i ].args)-1;
+ strend2 = machine_opcodes[i+1].args+strlen(machine_opcodes[i+1].args)-1;
+ switch (*strend) {
+ case 'b':
+ if (*strend2 != 'i') goto bad_table;
+ break;
+ case 'i':
+ if (*strend2 != 'b') goto bad_table;
+ break;
+ case 'P':
+ if (*strend2 != 'A') goto bad_table;
+ break;
+ case 'A':
+ if (*strend2 != 'P') goto bad_table;
+ break;
+ default:
+ bad_table:
+ fprintf (stderr, "internal error: can't handle opcode %s\n", name);
+ lose = 1;
+ }
+
+ /* OK, this is an i/b or A/P pair. We skip the higher-valued one,
+ and let the code for operand checking handle OR-ing in the bit. */
+ if (machine_opcodes[i].opcode & 1)
+ continue;
+ else
+ skipnext = 1;
+ }
+
+ retval = hash_insert (op_hash, name, &machine_opcodes[i]);
+ if (retval != NULL && *retval != '\0')
+ {
+ fprintf (stderr, "internal error: can't hash `%s': %s\n",
+ machine_opcodes[i].name, retval);
+ lose = 1;
+ }
+ }
+
+ if (lose)
+ as_fatal("Broken assembler. No assembly attempted.");
+
+ for (i = '0'; i < '8'; ++i)
+ octal[i] = 1;
+ for (i = '0'; i <= '9'; ++i)
+ toHex[i] = i - '0';
+ for (i = 'a'; i <= 'f'; ++i)
+ toHex[i] = i + 10 - 'a';
+ for (i = 'A'; i <= 'F'; ++i)
+ toHex[i] = i + 10 - 'A';
+
+ define_some_regs ();
+}
+
+void md_end() {
+ return;
+}
+
+/* Assemble a single instruction. Its label has already been handled
+ by the generic front end. We just parse opcode and operands, and
+ produce the bytes of data and relocation. */
+
+void md_assemble(str)
+char *str;
+{
+ char *toP;
+ /* !!!! int rsd; */
+
+ know(str);
+ machine_ip(str);
+ toP = frag_more(4);
+ /* put out the opcode */
+ md_number_to_chars(toP, the_insn.opcode, 4);
+
+ /* put out the symbol-dependent stuff */
+ if (the_insn.reloc != NO_RELOC) {
+ fix_new(
+ frag_now, /* which frag */
+ (toP - frag_now->fr_literal + the_insn.reloc_offset), /* where */
+ 4, /* size */
+ the_insn.exp.X_add_symbol,
+ the_insn.exp.X_subtract_symbol,
+ the_insn.exp.X_add_number,
+ the_insn.pcrel,
+ the_insn.reloc
+ );
+ }
+}
+
+char *
+ parse_operand (s, operandp)
+char *s;
+expressionS *operandp;
+{
+ char *save = input_line_pointer;
+ char *new;
+ segT seg;
+
+ input_line_pointer = s;
+ seg = expr (0, operandp);
+ new = input_line_pointer;
+ input_line_pointer = save;
+
+ switch (seg) {
+ case SEG_ABSOLUTE:
+ case SEG_TEXT:
+ case SEG_DATA:
+ case SEG_BSS:
+ case SEG_UNKNOWN:
+ case SEG_DIFFERENCE:
+ case SEG_BIG:
+ case SEG_REGISTER:
+ return new;
+
+ case SEG_ABSENT:
+ as_bad("Missing operand");
+ return new;
+
+ default:
+ as_bad("Don't understand operand of type %s", segment_name (seg));
+ return new;
+ }
+}
+
+/* Instruction parsing. Takes a string containing the opcode.
+ Operands are at input_line_pointer. Output is in the_insn.
+ Warnings or errors are generated. */
+
+static void
+ machine_ip(str)
+char *str;
+{
+ char *s;
+ const char *args;
+ /* !!!! char c; */
+ /* !!!! unsigned long i; */
+ struct machine_opcode *insn;
+ char *argsStart;
+ unsigned long opcode;
+ /* !!!! unsigned int mask; */
+ expressionS the_operand;
+ expressionS *operand = &the_operand;
+ unsigned int reg;
+
+ /* Must handle `div0' opcode. */
+ s = str;
+ if (isalpha(*s))
+ for (; isalnum(*s); ++s)
+ if (isupper (*s))
+ *s = tolower (*s);
+
+ switch (*s) {
+ case '\0':
+ break;
+
+ case ' ': /* FIXME-SOMEDAY more whitespace */
+ *s++ = '\0';
+ break;
+
+ default:
+ as_bad("Unknown opcode: `%s'", str);
+ return;
+ }
+ if ((insn = (struct machine_opcode *) hash_find(op_hash, str)) == NULL) {
+ as_bad("Unknown opcode `%s'.", str);
+ return;
+ }
+ argsStart = s;
+ opcode = insn->opcode;
+ memset(&the_insn, '\0', sizeof(the_insn));
+ the_insn.reloc = NO_RELOC;
+
+ /*
+ * Build the opcode, checking as we go to make
+ * sure that the operands match.
+ *
+ * If an operand matches, we modify the_insn or opcode appropriately,
+ * and do a "continue". If an operand fails to match, we "break".
+ */
+ if (insn->args[0] != '\0')
+ s = parse_operand (s, operand); /* Prime the pump */
+
+ for (args = insn->args; ; ++args) {
+ switch (*args) {
+
+ case '\0': /* end of args */
+ if (*s == '\0') {
+ /* We are truly done. */
+ the_insn.opcode = opcode;
+ return;
+ }
+ as_bad("Too many operands: %s", s);
+ break;
+
+ case ',': /* Must match a comma */
+ if (*s++ == ',') {
+ s = parse_operand (s, operand); /* Parse next opnd */
+ continue;
+ }
+ break;
+
+ case 'v': /* Trap numbers (immediate field) */
+ if (operand->X_seg == SEG_ABSOLUTE) {
+ if (operand->X_add_number < 256) {
+ opcode |= (operand->X_add_number << 16);
+ continue;
+ } else {
+ as_bad("Immediate value of %d is too large",
+ operand->X_add_number);
+ continue;
+ }
+ }
+ the_insn.reloc = RELOC_8;
+ the_insn.reloc_offset = 1; /* BIG-ENDIAN Byte 1 of insn */
+ the_insn.exp = *operand;
+ continue;
+
+ case 'b': /* A general register or 8-bit immediate */
+ case 'i':
+ /* We treat the two cases identically since we mashed
+ them together in the opcode table. */
+ if (operand->X_seg == SEG_REGISTER)
+ goto general_reg;
+
+ opcode |= IMMEDIATE_BIT;
+ if (operand->X_seg == SEG_ABSOLUTE) {
+ if (operand->X_add_number < 256) {
+ opcode |= operand->X_add_number;
+ continue;
+ } else {
+ as_bad("Immediate value of %d is too large",
+ operand->X_add_number);
+ continue;
+ }
+ }
+ the_insn.reloc = RELOC_8;
+ the_insn.reloc_offset = 3; /* BIG-ENDIAN Byte 3 of insn */
+ the_insn.exp = *operand;
+ continue;
+
+ case 'a': /* next operand must be a register */
+ case 'c':
+ general_reg:
+ /* lrNNN or grNNN or %%expr or a user-def register name */
+ if (operand->X_seg != SEG_REGISTER)
+ break; /* Only registers */
+ know (operand->X_add_symbol == 0);
+ know (operand->X_subtract_symbol == 0);
+ reg = operand->X_add_number;
+ if (reg >= SREG)
+ break; /* No special registers */
+
+ /*
+ * Got the register, now figure out where
+ * it goes in the opcode.
+ */
+ switch (*args) {
+ case 'a':
+ opcode |= reg << 8;
+ continue;
+
+ case 'b':
+ case 'i':
+ opcode |= reg;
+ continue;
+
+ case 'c':
+ opcode |= reg << 16;
+ continue;
+ }
+ as_fatal("failed sanity check.");
+ break;
+
+ case 'x': /* 16 bit constant, zero-extended */
+ case 'X': /* 16 bit constant, one-extended */
+ if (operand->X_seg == SEG_ABSOLUTE) {
+ opcode |= (operand->X_add_number & 0xFF) << 0 |
+ ((operand->X_add_number & 0xFF00) << 8);
+ continue;
+ }
+ the_insn.reloc = RELOC_CONST;
+ the_insn.exp = *operand;
+ continue;
+
+ case 'h':
+ if (operand->X_seg == SEG_ABSOLUTE) {
+ opcode |= (operand->X_add_number & 0x00FF0000) >> 16 |
+ (((unsigned long)operand->X_add_number
+ /* avoid sign ext */ & 0xFF000000) >> 8);
+ continue;
+ }
+ the_insn.reloc = RELOC_CONSTH;
+ the_insn.exp = *operand;
+ continue;
+
+ case 'P': /* PC-relative jump address */
+ case 'A': /* Absolute jump address */
+ /* These two are treated together since we folded the
+ opcode table entries together. */
+ if (operand->X_seg == SEG_ABSOLUTE) {
+ opcode |= ABSOLUTE_BIT |
+ (operand->X_add_number & 0x0003FC00) << 6 |
+ ((operand->X_add_number & 0x000003FC) >> 2);
+ continue;
+ }
+ the_insn.reloc = RELOC_JUMPTARG;
+ the_insn.exp = *operand;
+ the_insn.pcrel = 1; /* Assume PC-relative jump */
+ /* FIXME-SOON, Do we figure out whether abs later, after know sym val? */
+ continue;
+
+ case 'e': /* Coprocessor enable bit for LOAD/STORE insn */
+ if (operand->X_seg == SEG_ABSOLUTE) {
+ if (operand->X_add_number == 0)
+ continue;
+ if (operand->X_add_number == 1) {
+ opcode |= CE_BIT;
+ continue;
+ }
+ }
+ break;
+
+ case 'n': /* Control bits for LOAD/STORE instructions */
+ if (operand->X_seg == SEG_ABSOLUTE &&
+ operand->X_add_number < 128) {
+ opcode |= (operand->X_add_number << 16);
+ continue;
+ }
+ break;
+
+ case 's': /* Special register number */
+ if (operand->X_seg != SEG_REGISTER)
+ break; /* Only registers */
+ if (operand->X_add_number < SREG)
+ break; /* Not a special register */
+ opcode |= (operand->X_add_number & 0xFF) << 8;
+ continue;
+
+ case 'u': /* UI bit of CONVERT */
+ if (operand->X_seg == SEG_ABSOLUTE) {
+ if (operand->X_add_number == 0)
+ continue;
+ if (operand->X_add_number == 1) {
+ opcode |= UI_BIT;
+ continue;
+ }
+ }
+ break;
+
+ case 'r': /* RND bits of CONVERT */
+ if (operand->X_seg == SEG_ABSOLUTE &&
+ operand->X_add_number < 8) {
+ opcode |= operand->X_add_number << 4;
+ continue;
+ }
+ break;
+
+ case 'd': /* FD bits of CONVERT */
+ if (operand->X_seg == SEG_ABSOLUTE &&
+ operand->X_add_number < 4) {
+ opcode |= operand->X_add_number << 2;
+ continue;
+ }
+ break;
+
+
+ case 'f': /* FS bits of CONVERT */
+ if (operand->X_seg == SEG_ABSOLUTE &&
+ operand->X_add_number < 4) {
+ opcode |= operand->X_add_number << 0;
+ continue;
+ }
+ break;
+
+ case 'C':
+ if (operand->X_seg == SEG_ABSOLUTE &&
+ operand->X_add_number < 4) {
+ opcode |= operand->X_add_number << 16;
+ continue;
+ }
+ break;
+
+ case 'F':
+ if (operand->X_seg == SEG_ABSOLUTE &&
+ operand->X_add_number < 16) {
+ opcode |= operand->X_add_number << 18;
+ continue;
+ }
+ break;
+
+ default:
+ BAD_CASE (*args);
+ }
+ /* Types or values of args don't match. */
+ as_bad("Invalid operands");
+ return;
+ }
+}
+
+/*
+ This is identical to the md_atof in m68k.c. I think this is right,
+ but I'm not sure.
+
+ Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP. An error message is returned, or NULL on OK.
+ */
+
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+char *
+ md_atof(type,litP,sizeP)
+char type;
+char *litP;
+int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+
+ switch (type) {
+
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP=0;
+ return "Bad call to MD_ATOF()";
+ }
+ t=atof_ieee(input_line_pointer,type,words);
+ if (t)
+ input_line_pointer=t;
+ *sizeP=prec * sizeof(LITTLENUM_TYPE);
+ for (wordP=words;prec--;) {
+ md_number_to_chars(litP,(long)(*wordP++),sizeof(LITTLENUM_TYPE));
+ litP+=sizeof(LITTLENUM_TYPE);
+ }
+ return ""; /* Someone should teach Dean about null pointers */
+}
+
+/*
+ * Write out big-endian.
+ */
+void
+ md_number_to_chars(buf, val, n)
+char *buf;
+long val;
+int n;
+{
+
+ switch (n) {
+
+ case 4:
+ *buf++ = val >> 24;
+ *buf++ = val >> 16;
+ case 2:
+ *buf++ = val >> 8;
+ case 1:
+ *buf = val;
+ break;
+
+ default:
+ as_fatal("failed sanity check.");
+ }
+ return;
+}
+
+void md_apply_fix(fixP, val)
+fixS *fixP;
+long val;
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ fixP->fx_addnumber = val; /* Remember value for emit_reloc */
+
+
+ know(fixP->fx_size == 4);
+ know(fixP->fx_r_type < NO_RELOC);
+
+ /*
+ * This is a hack. There should be a better way to
+ * handle this.
+ */
+ if (fixP->fx_r_type == RELOC_WDISP30 && fixP->fx_addsy) {
+ val += fixP->fx_where + fixP->fx_frag->fr_address;
+ }
+
+ switch (fixP->fx_r_type) {
+
+ case RELOC_32:
+ buf[0] = val >> 24;
+ buf[1] = val >> 16;
+ buf[2] = val >> 8;
+ buf[3] = val;
+ break;
+
+ case RELOC_8:
+ buf[0] = val;
+ break;
+
+ case RELOC_WDISP30:
+ val = (val >>= 2) + 1;
+ buf[0] |= (val >> 24) & 0x3f;
+ buf[1]= (val >> 16);
+ buf[2] = val >> 8;
+ buf[3] = val;
+ break;
+
+ case RELOC_HI22:
+ buf[1] |= (val >> 26) & 0x3f;
+ buf[2] = val >> 18;
+ buf[3] = val >> 10;
+ break;
+
+ case RELOC_LO10:
+ buf[2] |= (val >> 8) & 0x03;
+ buf[3] = val;
+ break;
+
+ case RELOC_BASE13:
+ buf[2] |= (val >> 8) & 0x1f;
+ buf[3] = val;
+ break;
+
+ case RELOC_WDISP22:
+ val = (val >>= 2) + 1;
+ /* FALLTHROUGH */
+ case RELOC_BASE22:
+ buf[1] |= (val >> 16) & 0x3f;
+ buf[2] = val >> 8;
+ buf[3] = val;
+ break;
+
+#if 0
+ case RELOC_PC10:
+ case RELOC_PC22:
+ case RELOC_JMP_TBL:
+ case RELOC_SEGOFF16:
+ case RELOC_GLOB_DAT:
+ case RELOC_JMP_SLOT:
+ case RELOC_RELATIVE:
+#endif
+ case RELOC_JUMPTARG: /* 00XX00XX pattern in a word */
+ buf[1] = val >> 10; /* Holds bits 0003FFFC of address */
+ buf[3] = val >> 2;
+ break;
+
+ case RELOC_CONST: /* 00XX00XX pattern in a word */
+ buf[1] = val >> 8; /* Holds bits 0000XXXX */
+ buf[3] = val;
+ break;
+
+ case RELOC_CONSTH: /* 00XX00XX pattern in a word */
+ buf[1] = val >> 24; /* Holds bits XXXX0000 */
+ buf[3] = val >> 16;
+ break;
+
+ case NO_RELOC:
+ default:
+ as_bad("bad relocation type: 0x%02x", fixP->fx_r_type);
+ break;
+ }
+ return;
+}
+
+#ifdef OBJ_COFF
+short tc_coff_fix2rtype(fixP)
+fixS *fixP;
+{
+
+ /* FIXME-NOW: relocation type handling is not yet written for
+ a29k. */
+
+
+ switch (fixP->fx_r_type) {
+ case RELOC_32: return(R_WORD);
+ case RELOC_8: return(R_BYTE);
+ case RELOC_CONST: return (R_ILOHALF);
+ case RELOC_CONSTH: return (R_IHIHALF);
+ case RELOC_JUMPTARG: return (R_IREL);
+ default: printf("need %o3\n", fixP->fx_r_type);
+ abort(0);
+ } /* switch on type */
+
+ return(0);
+} /* tc_coff_fix2rtype() */
+#endif /* OBJ_COFF */
+
+/* should never be called for sparc */
+void md_create_short_jump(ptr, from_addr, to_addr, frag, to_symbol)
+char *ptr;
+long from_addr, to_addr;
+fragS *frag;
+symbolS *to_symbol;
+{
+ as_fatal("a29k_create_short_jmp\n");
+}
+
+/* should never be called for 29k */
+void md_convert_frag(headers, fragP)
+object_headers *headers;
+register fragS *fragP;
+{
+ as_fatal("sparc_convert_frag\n");
+}
+
+/* should never be called for 29k */
+void md_create_long_jump(ptr, from_addr, to_addr, frag, to_symbol)
+char *ptr;
+long from_addr;
+long to_addr;
+fragS *frag;
+symbolS *to_symbol;
+{
+ as_fatal("sparc_create_long_jump\n");
+}
+
+/* should never be called for a29k */
+int md_estimate_size_before_relax(fragP, segtype)
+register fragS *fragP;
+segT segtype;
+{
+ as_fatal("sparc_estimate_size_before_relax\n");
+ return(0);
+}
+
+#if 0
+/* for debugging only */
+static void
+ print_insn(insn)
+struct machine_it *insn;
+{
+ char *Reloc[] = {
+ "RELOC_8",
+ "RELOC_16",
+ "RELOC_32",
+ "RELOC_DISP8",
+ "RELOC_DISP16",
+ "RELOC_DISP32",
+ "RELOC_WDISP30",
+ "RELOC_WDISP22",
+ "RELOC_HI22",
+ "RELOC_22",
+ "RELOC_13",
+ "RELOC_LO10",
+ "RELOC_SFA_BASE",
+ "RELOC_SFA_OFF13",
+ "RELOC_BASE10",
+ "RELOC_BASE13",
+ "RELOC_BASE22",
+ "RELOC_PC10",
+ "RELOC_PC22",
+ "RELOC_JMP_TBL",
+ "RELOC_SEGOFF16",
+ "RELOC_GLOB_DAT",
+ "RELOC_JMP_SLOT",
+ "RELOC_RELATIVE",
+ "NO_RELOC"
+ };
+
+ if (insn->error) {
+ fprintf(stderr, "ERROR: %s\n");
+ }
+ fprintf(stderr, "opcode=0x%08x\n", insn->opcode);
+ fprintf(stderr, "reloc = %s\n", Reloc[insn->reloc]);
+ fprintf(stderr, "exp = {\n");
+ fprintf(stderr, "\t\tX_add_symbol = %s\n",
+ insn->exp.X_add_symbol ?
+ (S_GET_NAME(insn->exp.X_add_symbol) ?
+ S_GET_NAME(insn->exp.X_add_symbol) : "???") : "0");
+ fprintf(stderr, "\t\tX_sub_symbol = %s\n",
+ insn->exp.X_subtract_symbol ?
+ (S_GET_NAME(insn->exp.X_subtract_symbol) ?
+ S_GET_NAME(insn->exp.X_subtract_symbol) : "???") : "0");
+ fprintf(stderr, "\t\tX_add_number = %d\n",
+ insn->exp.X_add_number);
+ fprintf(stderr, "}\n");
+ return;
+}
+#endif
+
+/* Translate internal representation of relocation info to target format.
+
+ On sparc/29k: first 4 bytes are normal unsigned long address, next three
+ bytes are index, most sig. byte first. Byte 7 is broken up with
+ bit 7 as external, bits 6 & 5 unused, and the lower
+ five bits as relocation type. Next 4 bytes are long addend. */
+/* Thanx and a tip of the hat to Michael Bloom, mb@ttidca.tti.com */
+
+#ifdef OBJ_AOUT
+
+void tc_aout_fix_to_chars(where, fixP, segment_address_in_file)
+char *where;
+fixS *fixP;
+relax_addressT segment_address_in_file;
+{
+ long r_symbolnum;
+
+ know(fixP->fx_r_type < NO_RELOC);
+ know(fixP->fx_addsy != NULL);
+
+ md_number_to_chars(where,
+ fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file,
+ 4);
+
+ r_symbolnum = (S_IS_DEFINED(fixP->fx_addsy)
+ ? S_GET_TYPE(fixP->fx_addsy)
+ : fixP->fx_addsy->sy_number);
+
+ where[4] = (r_symbolnum >> 16) & 0x0ff;
+ where[5] = (r_symbolnum >> 8) & 0x0ff;
+ where[6] = r_symbolnum & 0x0ff;
+ where[7] = (((!S_IS_DEFINED(fixP->fx_addsy)) << 7) & 0x80) | (0 & 0x60) | (fixP->fx_r_type & 0x1F);
+ /* Also easy */
+ md_number_to_chars(&where[8], fixP->fx_addnumber, 4);
+
+ return;
+} /* tc_aout_fix_to_chars() */
+
+#endif /* OBJ_AOUT */
+
+int
+ md_parse_option(argP,cntP,vecP)
+char **argP;
+int *cntP;
+char ***vecP;
+{
+ return(0);
+}
+
+
+/* Default the values of symbols known that should be "predefined". We
+ don't bother to predefine them unless you actually use one, since there
+ are a lot of them. */
+
+symbolS *md_undefined_symbol (name)
+char *name;
+{
+ long regnum;
+ char testbuf[5+ /*SLOP*/ 5];
+
+ if (name[0] == 'g' || name[0] == 'G' || name[0] == 'l' || name[0] == 'L')
+ {
+ /* Perhaps a global or local register name */
+ if (name[1] == 'r' || name[1] == 'R')
+ {
+ /* Parse the number, make sure it has no extra zeroes or trailing
+ chars */
+ regnum = atol(&name[2]);
+ if (regnum > 127)
+ return 0;
+ sprintf(testbuf, "%ld", regnum);
+ if (strcmp (testbuf, &name[2]) != 0)
+ return 0; /* gr007 or lr7foo or whatever */
+
+ /* We have a wiener! Define and return a new symbol for it. */
+ if (name[0] == 'l' || name[0] == 'L')
+ regnum += 128;
+ return(symbol_new(name, SEG_REGISTER, regnum, &zero_address_frag));
+ }
+ }
+
+ return 0;
+}
+
+/* Parse an operand that is machine-specific. */
+
+void md_operand(expressionP)
+expressionS *expressionP;
+{
+
+ if (input_line_pointer[0] == '%' && input_line_pointer[1] == '%')
+ {
+ /* We have a numeric register expression. No biggy. */
+ input_line_pointer += 2; /* Skip %% */
+ (void)expression (expressionP);
+ if (expressionP->X_seg != SEG_ABSOLUTE
+ || expressionP->X_add_number > 255)
+ as_bad("Invalid expression after %%%%\n");
+ expressionP->X_seg = SEG_REGISTER;
+ }
+ else if (input_line_pointer[0] == '&')
+ {
+ /* We are taking the 'address' of a register...this one is not
+ in the manual, but it *is* in traps/fpsymbol.h! What they
+ seem to want is the register number, as an absolute number. */
+ input_line_pointer++; /* Skip & */
+ (void)expression (expressionP);
+ if (expressionP->X_seg != SEG_REGISTER)
+ as_bad("Invalid register in & expression");
+ else
+ expressionP->X_seg = SEG_ABSOLUTE;
+ }
+}
+
+/* Round up a section size to the appropriate boundary. */
+long
+ md_section_align (segment, size)
+segT segment;
+long size;
+{
+ return size; /* Byte alignment is fine */
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the 29000, they're relative to the address of the instruction,
+ which we have set up as the address of the fixup too. */
+long md_pcrel_from (fixP)
+fixS *fixP;
+{
+ return fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * End:
+ */
+
+/* end of tc-a29k.c */
diff --git a/gnu/usr.bin/as/config/tc-a29k.h b/gnu/usr.bin/as/config/tc-a29k.h
new file mode 100644
index 0000000..fee1ca2
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-a29k.h
@@ -0,0 +1,40 @@
+/* tc-a29k.h -- Assemble for the AMD 29000.
+ Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define TC_A29K
+
+#define NO_LISTING
+
+#define tc_aout_pre_write_hook(x) {;} /* not used */
+#define tc_coff_symbol_emit_hook(a) {;} /* not used */
+#define tc_crawl_symbol_chain(a) {;} /* not used */
+#define tc_headers_hook(a) {;} /* not used */
+
+#define AOUT_MACHTYPE 101
+#define TC_COFF_FIX2RTYPE(fix_ptr) tc_coff_fix2rtype(fix_ptr)
+#define BFD_ARCH bfd_arch_a29k
+#define COFF_MAGIC SIPFBOMAGIC
+
+/* Should the reloc be output ?
+ on the 29k, this is true only if there is a symbol attatched.
+ on the h8, this is allways true, since no fixup is done
+ */
+#define TC_COUNT_RELOC(x) (x->fx_addsy)
+
+/* end of tc-a29k.h */
diff --git a/gnu/usr.bin/as/config/tc-generic.c b/gnu/usr.bin/as/config/tc-generic.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-generic.c
diff --git a/gnu/usr.bin/as/config/tc-generic.h b/gnu/usr.bin/as/config/tc-generic.h
new file mode 100644
index 0000000..181d4aa
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-generic.h
@@ -0,0 +1,37 @@
+/* This file is tc-generic.h
+
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * This file is tc-generic.h and is intended to be a template for target cpu
+ * specific header files. It is my intent that this file compile. It is also
+ * my intent that this file grow into something that can be used as both a
+ * template for porting, and a stub for testing. xoxorich.
+ */
+
+#define TC_GENERIC 1
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of tc-generic.h */
diff --git a/gnu/usr.bin/as/config/tc-h8300.c b/gnu/usr.bin/as/config/tc-h8300.c
new file mode 100644
index 0000000..db4786b
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-h8300.c
@@ -0,0 +1,1295 @@
+/* tc-h8300.c -- Assemble code for the Hitachi H8/300
+ Copyright (C) 1991, 1992 Free Software Foundation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/*
+ Written By Steve Chamberlain
+ sac@cygnus.com
+ */
+
+#include <stdio.h>
+#include "as.h"
+#include "bfd.h"
+#include "opcode/h8300.h"
+#include <ctype.h>
+#include "listing.h"
+
+char comment_chars[] = { ';',0 };
+char line_separator_chars[] = { '$' ,0};
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ Integer arg to pass to the function
+ */
+
+void cons();
+
+const pseudo_typeS md_pseudo_table[] = {
+ { "int", cons, 2 },
+ { 0,0,0 }
+};
+
+int md_reloc_size ;
+
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+
+const relax_typeS md_relax_table[1];
+
+
+static struct hash_control *opcode_hash_control; /* Opcode mnemonics */
+
+
+/*
+ This function is called once, at assembler startup time. This should
+ set up all the tables, etc that the MD part of the assembler needs
+ */
+#if 0
+/* encode the size and number into the number field
+ xxnnnn
+ 00 8 bit
+ 01 16 bit
+ 10 ccr
+ nnnnreg number
+ */
+#define WORD_REG 0x10
+#define BYTE_REG 0x00
+#define CCR_REG 0x20
+struct reg_entry
+{
+ char *name;
+ char number;
+};
+
+struct reg_entry reg_list[] = {
+ "r0",WORD_REG +0,
+ "r1",WORD_REG +1,
+ "r2",WORD_REG +2,
+ "r3",WORD_REG +3,
+ "r4",WORD_REG +4,
+ "r5",WORD_REG +5,
+ "r6",WORD_REG +6,
+ "r7",WORD_REG +7,
+ "fp",WORD_REG +6,
+ "sp",WORD_REG +7,
+ "r0h",BYTE_REG + 0,
+ "r0l",BYTE_REG + 1,
+ "r1h",BYTE_REG + 2,
+ "r1l",BYTE_REG + 3,
+ "r2h",BYTE_REG + 4,
+ "r2l",BYTE_REG + 5,
+ "r3h",BYTE_REG + 6,
+ "r3l",BYTE_REG + 7,
+ "r4h",BYTE_REG + 8,
+ "r4l",BYTE_REG + 9,
+ "r5h",BYTE_REG + 10,
+ "r5l",BYTE_REG + 11,
+ "r6h",BYTE_REG + 12,
+ "r6l",BYTE_REG + 13,
+ "r7h",BYTE_REG + 14,
+ "r7l",BYTE_REG + 15,
+ "ccr",CCR_REG,
+ 0,0
+ }
+;
+
+
+#endif
+
+
+void md_begin ()
+{
+ struct h8_opcode *opcode;
+ const struct reg_entry *reg;
+ char prev_buffer[100];
+ int idx = 0;
+
+ opcode_hash_control = hash_new();
+ prev_buffer[0] = 0;
+
+ for (opcode = h8_opcodes; opcode->name; opcode++)
+ {
+ /* Strip off any . part when inserting the opcode and only enter
+ unique codes into the hash table
+ */
+ char *src= opcode->name;
+ unsigned int len = strlen(src);
+ char *dst = malloc(len+1);
+ char *buffer = dst;
+ opcode->size = 0;
+ while (*src) {
+ if (*src == '.') {
+ *dst++ = 0;
+ src++;
+ opcode->size = *src;
+ break;
+ }
+ *dst++ = *src++;
+ }
+ if (strcmp(buffer, prev_buffer))
+ {
+ hash_insert(opcode_hash_control, buffer, (char *)opcode);
+ strcpy(prev_buffer, buffer);
+ idx++;
+ }
+ opcode->idx = idx;
+
+
+ /* Find the number of operands */
+ opcode->noperands = 0;
+ while (opcode->args.nib[opcode->noperands] != E)
+ opcode->noperands ++;
+ /* Find the length of the opcode in bytes */
+ opcode->length =0;
+ while (opcode->data.nib[opcode->length*2] != E)
+ opcode->length++;
+ }
+
+}
+
+
+struct h8_exp {
+ char *e_beg;
+ char *e_end;
+ expressionS e_exp;
+};
+struct h8_op
+{
+ unsigned int dispreg;
+ op_type mode;
+ unsigned reg;
+ expressionS exp;
+};
+
+
+
+/*
+ parse operands
+ WREG r0,r1,r2,r3,r4,r5,r6,r7,fp,sp
+ r0l,r0h,..r7l,r7h
+ @WREG
+ @WREG+
+ @-WREG
+ #const
+
+ */
+
+op_type r8_sord[] = {RS8, RD8};
+op_type r16_sord[] = {RS16, RD16};
+op_type rind_sord[] = {RSIND, RDIND};
+op_type abs_sord[2] = {ABS16SRC, ABS16DST};
+op_type disp_sord[] = {DISPSRC, DISPDST};
+
+/* try and parse a reg name, returns number of chars consumed */
+int
+ DEFUN(parse_reg,(src, mode, reg, dst),
+ char *src AND
+ op_type *mode AND
+ unsigned int *reg AND
+ int dst)
+{
+ if (src[0] == 's' && src[1] == 'p')
+ {
+ *mode = r16_sord[dst];
+ *reg = 7;
+ return 2;
+ }
+ if (src[0] == 'c' && src[1] == 'c' && src[2] == 'r')
+ {
+ *mode = CCR;
+ *reg = 0;
+ return 3;
+ }
+ if (src[0] == 'f' && src[1] == 'p')
+ {
+ *mode = r16_sord[dst];
+ *reg = 6;
+ return 2;
+ }
+ if (src[0] == 'r')
+ {
+ if (src[1] >= '0' && src[1] <= '7')
+ {
+ if (src[2] == 'l')
+ {
+ *mode = r8_sord[dst];
+ *reg = (src[1] - '0') + 8;
+ return 3;
+ }
+ if (src[2] == 'h')
+ {
+ *mode = r8_sord[dst];
+ *reg = (src[1] - '0') ;
+ return 3;
+ }
+ *mode = r16_sord[dst];
+ *reg = (src[1] - '0');
+ return 2;
+ }
+ }
+ return 0;
+}
+
+char *
+ DEFUN(parse_exp,(s, op),
+ char *s AND
+ expressionS *op)
+{
+ char *save = input_line_pointer;
+ char *new;
+ segT seg;
+ input_line_pointer = s;
+ seg = expr(0,op);
+ new = input_line_pointer;
+ input_line_pointer = save;
+ if (SEG_NORMAL(seg))
+ return new;
+ switch (seg) {
+ case SEG_ABSOLUTE:
+ case SEG_UNKNOWN:
+ case SEG_DIFFERENCE:
+ case SEG_BIG:
+ case SEG_REGISTER:
+ return new;
+ case SEG_ABSENT:
+ as_bad("Missing operand");
+ return new;
+ default:
+ as_bad("Don't understand operand of type %s", segment_name (seg));
+ return new;
+ }
+}
+
+static char *
+ DEFUN(skip_colonthing,(ptr),
+ char *ptr)
+{
+ if (*ptr == ':') {
+ ptr++;
+ while (isdigit(*ptr))
+ ptr++;
+
+ }
+ return ptr;
+}
+
+/* The many forms of operand:
+
+ Rn Register direct
+ @Rn Register indirect
+ @(exp[:16], Rn) Register indirect with displacement
+ @Rn+
+ @-Rn
+ @aa:8 absolute 8 bit
+ @aa:16 absolute 16 bit
+ @aa absolute 16 bit
+
+ #xx[:size] immediate data
+ @(exp:[8], pc) pc rel
+ @@aa[:8] memory indirect
+
+ */
+
+static void
+ DEFUN(get_operand,(ptr, op, dst),
+ char **ptr AND
+ struct h8_op *op AND
+ unsigned int dst)
+{
+ char *src = *ptr;
+ op_type mode;
+ unsigned int num;
+ unsigned int len;
+ unsigned int size;
+ op->mode = E;
+
+ len = parse_reg(src, &op->mode, &op->reg, dst);
+ if (len) {
+ *ptr = src + len;
+ return ;
+ }
+
+ if (*src == '@')
+ {
+ src++;
+ if (*src == '@')
+ {
+ src++;
+ src = parse_exp(src,&op->exp);
+ src = skip_colonthing(src);
+
+ *ptr = src;
+
+ op->mode = MEMIND;
+ return;
+
+ }
+
+
+ if (*src == '-')
+ {
+ src++;
+ len = parse_reg(src, &mode, &num, dst);
+ if (len == 0)
+ {
+ /* Oops, not a reg after all, must be ordinary exp */
+ src--;
+ /* must be a symbol */
+ op->mode = abs_sord[dst];
+ *ptr = skip_colonthing(parse_exp(src, &op->exp));
+
+ return;
+
+
+ }
+
+ if (mode != r16_sord[dst])
+ {
+ as_bad("@- needs word register");
+ }
+ op->mode = RDDEC;
+ op->reg = num;
+ *ptr = src + len;
+ return;
+ }
+ if (*src == '(' && ')')
+ {
+ /* Disp */
+ src++;
+ src = parse_exp(src, &op->exp);
+
+ if (*src == ')')
+ {
+ src++;
+ op->mode = abs_sord[dst];
+ *ptr = src;
+ return;
+ }
+ src = skip_colonthing(src);
+
+ if (*src != ',')
+ {
+ as_bad("expected @(exp, reg16)");
+ }
+ src++;
+ len = parse_reg(src, &mode, &op->reg, dst);
+ if (len == 0 || mode != r16_sord[dst])
+ {
+ as_bad("expected @(exp, reg16)");
+ }
+ op->mode = disp_sord[dst];
+ src += len;
+ src = skip_colonthing(src);
+
+ if (*src != ')' && '(')
+ {
+ as_bad("expected @(exp, reg16)");
+
+ }
+ *ptr = src +1;
+
+ return;
+ }
+ len = parse_reg(src, &mode, &num, dst);
+
+ if (len) {
+ src += len;
+ if (*src == '+')
+ {
+ src++;
+ if (mode != RS16)
+ {
+ as_bad("@Rn+ needs src word register");
+ }
+ op->mode = RSINC;
+ op->reg = num;
+ *ptr = src;
+ return;
+ }
+ if (mode != r16_sord[dst])
+ {
+ as_bad("@Rn needs word register");
+ }
+ op->mode =rind_sord[dst];
+ op->reg = num;
+ *ptr = src;
+ return;
+ }
+ else
+ {
+ /* must be a symbol */
+ op->mode = abs_sord[dst];
+ *ptr = skip_colonthing(parse_exp(src, &op->exp));
+
+ return;
+ }
+ }
+
+
+ if (*src == '#') {
+ src++;
+ op->mode = IMM16;
+ src = parse_exp(src, &op->exp);
+ *ptr= skip_colonthing(src);
+
+ return;
+ }
+ else {
+ *ptr = parse_exp(src, &op->exp);
+ op->mode = DISP8;
+ }
+}
+
+
+static
+ char *
+ DEFUN(get_operands,(noperands,op_end, operand),
+ unsigned int noperands AND
+ char *op_end AND
+ struct h8_op *operand)
+{
+ char *ptr = op_end;
+ switch (noperands)
+ {
+ case 0:
+ operand[0].mode = 0;
+ operand[1].mode = 0;
+ break;
+
+ case 1:
+ ptr++;
+ get_operand(& ptr, operand +0,0);
+ operand[1].mode =0;
+ break;
+
+ case 2:
+ ptr++;
+ get_operand(& ptr, operand +0,0);
+ if (*ptr == ',') ptr++;
+ get_operand(& ptr, operand +1, 1);
+ break;
+
+ default:
+ abort();
+ }
+
+
+ return ptr;
+}
+
+/* Passed a pointer to a list of opcodes which use different
+ addressing modes, return the opcode which matches the opcodes
+ provided
+ */
+static
+ struct h8_opcode *
+ DEFUN(get_specific,(opcode, operands),
+ struct h8_opcode *opcode AND
+ struct h8_op *operands)
+
+{
+ struct h8_opcode *this_try = opcode ;
+ int found = 0;
+ unsigned int noperands = opcode->noperands;
+
+ unsigned int dispreg;
+ unsigned int this_index = opcode->idx;
+ while (this_index == opcode->idx && !found)
+ {
+ unsigned int i;
+
+ this_try = opcode ++;
+ for (i = 0; i < noperands; i++)
+ {
+ op_type op = (this_try->args.nib[i]) & ~(B30|B31);
+ switch (op)
+ {
+ case Hex0:
+ case Hex1:
+ case Hex2:
+ case Hex3:
+ case Hex4:
+ case Hex5:
+ case Hex6:
+ case Hex7:
+ case Hex8:
+ case Hex9:
+ case HexA:
+ case HexB:
+ case HexC:
+ case HexD:
+ case HexE:
+ case HexF:
+ break;
+ case DISPSRC:
+ case DISPDST:
+ operands[0].dispreg = operands[i].reg;
+ case RD8:
+ case RS8:
+ case RDIND:
+ case RSIND:
+ case RD16:
+ case RS16:
+ case CCR:
+ case RSINC:
+ case RDDEC:
+ if (operands[i].mode != op) goto fail;
+ break;
+ case KBIT:
+ case IMM16:
+ case IMM3:
+ case IMM8:
+ if (operands[i].mode != IMM16) goto fail;
+ break;
+ case MEMIND:
+ if (operands[i].mode != MEMIND) goto fail;
+ break;
+ case ABS16SRC:
+ case ABS8SRC:
+ case ABS16OR8SRC:
+ case ABS16ORREL8SRC:
+
+ if (operands[i].mode != ABS16SRC) goto fail;
+ break;
+ case ABS16OR8DST:
+ case ABS16DST:
+ case ABS8DST:
+ if (operands[i].mode != ABS16DST) goto fail;
+ break;
+ }
+ }
+ found =1;
+ fail: ;
+ }
+ if (found)
+ return this_try;
+ else
+ return 0;
+}
+
+static void
+ DEFUN(check_operand,(operand, width, string),
+ struct h8_op *operand AND
+ unsigned int width AND
+ char *string)
+{
+ if (operand->exp.X_add_symbol == 0
+ && operand->exp.X_subtract_symbol == 0)
+ {
+
+ /* No symbol involved, let's look at offset, it's dangerous if any of
+ the high bits are not 0 or ff's, find out by oring or anding with
+ the width and seeing if the answer is 0 or all fs*/
+ if ((operand->exp.X_add_number | width) != ~0 &&
+ (operand->exp.X_add_number & ~width) != 0)
+ {
+ as_warn("operand %s0x%x out of range.", string, operand->exp.X_add_number);
+ }
+ }
+
+}
+
+/* Now we know what sort of opcodes it is, lets build the bytes -
+ */
+static void
+ DEFUN (build_bytes,(this_try, operand),
+ struct h8_opcode *this_try AND
+ struct h8_op *operand)
+
+{
+ unsigned int i;
+
+ char *output = frag_more(this_try->length);
+ char *output_ptr = output;
+ op_type *nibble_ptr = this_try->data.nib;
+ char part;
+ op_type c;
+ char high;
+ int nib;
+ top: ;
+ while (*nibble_ptr != E)
+ {
+ int nibble;
+ for (nibble = 0; nibble <2; nibble++)
+ {
+ c = *nibble_ptr & ~(B30|B31);
+ switch (c)
+ {
+ default:
+ abort();
+ case KBIT:
+ switch (operand[0].exp.X_add_number)
+ {
+ case 1:
+ nib = 0;
+ break;
+ case 2:
+ nib = 8;
+ break;
+ default:
+ as_bad("Need #1 or #2 here");
+ break;
+ }
+ /* stop it making a fix */
+ operand[0].mode = 0;
+ break;
+ case 0:
+ case 1:
+ case 2: case 3: case 4: case 5: case 6:
+ case 7: case 8: case 9: case 10: case 11:
+ case 12: case 13: case 14: case 15:
+ nib = c;
+ break;
+ case DISPREG:
+ nib = operand[0].dispreg;
+ break;
+ case IMM8:
+ operand[0].mode = IMM8;
+ nib = 0;
+ break;
+
+ case DISPDST:
+ nib = 0;
+ break;
+ case IMM3:
+ if (operand[0].exp.X_add_symbol == 0) {
+ operand[0].mode = 0; /* stop it making a fix */
+ nib = (operand[0].exp.X_add_number);
+ }
+ else as_bad("can't have symbol for bit number");
+ if (nib < 0 || nib > 7)
+ {
+ as_bad("Bit number out of range %d", nib);
+ }
+
+ break;
+
+ case ABS16DST:
+ nib = 0;
+ break;
+ case ABS8DST:
+ operand[1].mode = ABS8DST;
+ nib = 0;
+ break;
+ case ABS8SRC:
+ operand[0].mode = ABS8SRC;
+ nib = 0;
+ break;
+ case ABS16OR8DST:
+ operand[1].mode = c;
+
+ nib = 0;
+
+ break;
+
+ case ABS16ORREL8SRC:
+ operand[0].mode = c;
+ nib=0;
+ break;
+
+ case ABS16OR8SRC:
+ operand[0].mode = ABS16OR8SRC;
+ nib = 0;
+ break;
+ case DISPSRC:
+ operand[0].mode = ABS16SRC;
+ nib = 0;
+ break;
+
+ case DISP8:
+ operand[0].mode = DISP8;
+ nib = 0;
+ break;
+
+ case ABS16SRC:
+ case IMM16:
+ case IGNORE:
+ case MEMIND:
+
+ nib=0;
+ break;
+ case RS8:
+ case RS16:
+ case RSIND:
+ case RSINC:
+ nib = operand[0].reg;
+ break;
+
+ case RD8:
+ case RD16:
+ case RDDEC:
+ case RDIND:
+ nib = operand[1].reg;
+ break;
+
+ case E:
+ abort();
+ break;
+ }
+ if (*nibble_ptr & B31) {
+ nib |=0x8;
+ }
+
+ if (nibble == 0) {
+ *output_ptr = nib << 4;
+ }
+ else {
+ *output_ptr |= nib;
+ output_ptr++;
+ }
+ nibble_ptr++;
+ }
+
+ }
+
+ /* output any fixes */
+ for (i = 0; i < 2; i++)
+ {
+ switch (operand[i].mode) {
+ case 0:
+ break;
+
+ case DISP8:
+ check_operand(operand+i, 0x7f,"@");
+
+ fix_new(frag_now,
+ output - frag_now->fr_literal + 1,
+ 1,
+ operand[i].exp.X_add_symbol,
+ operand[i].exp.X_subtract_symbol,
+ operand[i].exp.X_add_number -1,
+ 1,
+ R_PCRBYTE);
+ break;
+ case IMM8:
+ check_operand(operand+i, 0xff,"#");
+ /* If there is nothing else going on we can safely
+ reloc in place */
+ if (operand[i].exp.X_add_symbol == 0)
+ {
+ output[1] = operand[i].exp.X_add_number;
+ }
+ else
+ {
+ fix_new(frag_now,
+ output - frag_now->fr_literal + 1,
+ 1,
+ operand[i].exp.X_add_symbol,
+ operand[i].exp.X_subtract_symbol,
+ operand[i].exp.X_add_number,
+ 0,
+ R_RELBYTE);
+ }
+
+ break;
+ case MEMIND:
+ check_operand(operand+i, 0xff,"@@");
+ fix_new(frag_now,
+ output - frag_now->fr_literal + 1,
+ 1,
+ operand[i].exp.X_add_symbol,
+ operand[i].exp.X_subtract_symbol,
+ operand[i].exp.X_add_number,
+ 0,
+ R_RELBYTE);
+ break;
+ case ABS8DST:
+ case ABS8SRC:
+ check_operand(operand+i, 0xff,"@");
+ fix_new(frag_now,
+ output - frag_now->fr_literal + 1,
+ 1,
+ operand[i].exp.X_add_symbol,
+ operand[i].exp.X_subtract_symbol,
+ operand[i].exp.X_add_number,
+ 0,
+ R_RELBYTE);
+ break;
+
+ case ABS16OR8SRC:
+ case ABS16OR8DST:
+ check_operand(operand+i, 0xffff,"@");
+
+ fix_new(frag_now,
+ output - frag_now->fr_literal + 2,
+ 2,
+ operand[i].exp.X_add_symbol,
+ operand[i].exp.X_subtract_symbol,
+ operand[i].exp.X_add_number,
+ 0,
+ R_MOVB1);
+ break;
+
+ case ABS16ORREL8SRC:
+ check_operand(operand+i, 0xffff,"@");
+
+ fix_new(frag_now,
+ output - frag_now->fr_literal + 2,
+ 2,
+ operand[i].exp.X_add_symbol,
+ operand[i].exp.X_subtract_symbol,
+ operand[i].exp.X_add_number,
+ 0,
+ R_JMP1);
+ break;
+
+
+ case ABS16SRC:
+ case ABS16DST:
+ case IMM16:
+ case DISPSRC:
+ case DISPDST:
+ check_operand(operand+i, 0xffff,"@");
+ if (operand[i].exp.X_add_symbol == 0)
+ {
+ /* This should be done with bfd */
+ output[3] = operand[i].exp.X_add_number & 0xff;
+ output[2] = operand[i].exp.X_add_number >> 8;
+
+ }
+ else
+ {
+
+ fix_new(frag_now,
+ output - frag_now->fr_literal + 2,
+ 2,
+ operand[i].exp.X_add_symbol,
+ operand[i].exp.X_subtract_symbol,
+ operand[i].exp.X_add_number,
+ 0,
+ R_RELWORD);
+ }
+
+ break;
+ case RS8:
+ case RD8:
+ case RS16:
+ case RD16:
+ case RDDEC:
+ case KBIT:
+ case RSINC:
+ case RDIND:
+ case RSIND:
+ case CCR:
+
+ break;
+ default:
+ abort();
+ }
+ }
+
+}
+/*
+ try and give an intelligent error message for common and simple to
+ detect errors
+ */
+
+static void
+ DEFUN(clever_message, (opcode, operand),
+ struct h8_opcode *opcode AND
+ struct h8_op *operand)
+{
+ struct h8_opcode *scan = opcode;
+
+ /* Find out if there was more than one possible opccode */
+
+ if ((opcode+1)->idx != opcode->idx)
+ {
+ unsigned int argn;
+
+ /* Only one opcode of this flavour, try and guess which operand
+ didn't match */
+ for (argn = 0; argn < opcode->noperands; argn++)
+ {
+ switch (opcode->args.nib[argn])
+ {
+ case RD16:
+ if (operand[argn].mode != RD16)
+ {
+ as_bad("destination operand must be 16 bit register");
+ }
+ return;
+ case RS8:
+
+ if (operand[argn].mode != RS8)
+ {
+ as_bad("source operand must be 8 bit register");
+ }
+ return;
+ case ABS16DST:
+ if (operand[argn].mode != ABS16DST)
+ {
+ as_bad("destination operand must be 16bit absolute address");
+ return;
+ }
+
+ case RD8:
+ if (operand[argn].mode != RD8)
+ {
+ as_bad("destination operand must be 8 bit register");
+ }
+ return;
+
+ case ABS16SRC:
+ if (operand[argn].mode != ABS16SRC)
+ {
+ as_bad("source operand must be 16bit absolute address");
+ return;
+ }
+ }
+ }
+ }
+ as_bad("invalid operands");
+}
+
+/* This is the guts of the machine-dependent assembler. STR points to a
+ machine dependent instruction. This funciton is supposed to emit
+ the frags/bytes it assembles to.
+ */
+
+
+
+void
+ DEFUN(md_assemble,(str),
+ char *str)
+{
+ char *op_start;
+ char *op_end;
+ unsigned int i;
+ struct h8_op operand[2];
+ struct h8_opcode * opcode;
+ struct h8_opcode * prev_opcode;
+
+ char *dot = 0;
+ char c;
+ /* Drop leading whitespace */
+ while (*str == ' ')
+ str++;
+
+ /* find the op code end */
+ for (op_start = op_end = str;
+ *op_end != 0 && *op_end != ' ';
+ op_end ++)
+ {
+ if (*op_end == '.') {
+ dot = op_end+1;
+ *op_end = 0;
+ op_end+=2;
+ break;
+ }
+ }
+
+ ;
+
+ if (op_end == op_start)
+ {
+ as_bad("can't find opcode ");
+ }
+ c = *op_end;
+
+ *op_end = 0;
+
+ opcode = (struct h8_opcode *) hash_find(opcode_hash_control,
+ op_start);
+
+ if (opcode == NULL)
+ {
+ as_bad("unknown opcode");
+ return;
+ }
+
+
+ input_line_pointer = get_operands(opcode->noperands, op_end,
+ operand);
+ *op_end = c;
+ prev_opcode = opcode;
+
+ opcode = get_specific(opcode, operand);
+
+ if (opcode == 0)
+ {
+ /* Couldn't find an opcode which matched the operands */
+ char *where =frag_more(2);
+ where[0] = 0x0;
+ where[1] = 0x0;
+ clever_message(prev_opcode, operand);
+
+ return;
+ }
+ if (opcode->size && dot)
+ {
+ if (opcode->size != *dot)
+ {
+ as_warn("mismatch between opcode size and operand size");
+ }
+ }
+
+ build_bytes(opcode, operand);
+
+}
+
+void
+ DEFUN(tc_crawl_symbol_chain, (headers),
+ object_headers *headers)
+{
+ printf("call to tc_crawl_symbol_chain \n");
+}
+
+symbolS *DEFUN(md_undefined_symbol,(name),
+ char *name)
+{
+ return 0;
+}
+
+void
+ DEFUN(tc_headers_hook,(headers),
+ object_headers *headers)
+{
+ printf("call to tc_headers_hook \n");
+}
+void
+ DEFUN_VOID(md_end)
+{
+}
+
+/* Various routines to kill one day */
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+/* Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP. An error message is returned, or NULL on OK.
+ */
+char *
+ md_atof(type,litP,sizeP)
+char type;
+char *litP;
+int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+ char *atof_ieee();
+
+ switch (type) {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP=0;
+ return "Bad call to MD_ATOF()";
+ }
+ t=atof_ieee(input_line_pointer,type,words);
+ if (t)
+ input_line_pointer=t;
+
+ *sizeP=prec * sizeof(LITTLENUM_TYPE);
+ for (wordP=words;prec--;) {
+ md_number_to_chars(litP,(long)(*wordP++),sizeof(LITTLENUM_TYPE));
+ litP+=sizeof(LITTLENUM_TYPE);
+ }
+ return ""; /* Someone should teach Dean about null pointers */
+}
+
+int
+ md_parse_option(argP, cntP, vecP)
+char **argP;
+int *cntP;
+char ***vecP;
+
+{
+ return 0;
+
+}
+
+int md_short_jump_size;
+
+void tc_aout_fix_to_chars () { printf("call to tc_aout_fix_to_chars \n");
+ abort(); }
+void md_create_short_jump(ptr, from_addr, to_addr, frag, to_symbol)
+char *ptr;
+long from_addr;
+long to_addr;
+fragS *frag;
+symbolS *to_symbol;
+{
+ as_fatal("failed sanity check.");
+}
+
+void
+ md_create_long_jump(ptr,from_addr,to_addr,frag,to_symbol)
+char *ptr;
+long from_addr, to_addr;
+fragS *frag;
+symbolS *to_symbol;
+{
+ as_fatal("failed sanity check.");
+}
+
+void
+ md_convert_frag(headers, fragP)
+object_headers *headers;
+fragS * fragP;
+
+{ printf("call to md_convert_frag \n"); abort(); }
+
+long
+ DEFUN(md_section_align,(seg, size),
+ segT seg AND
+ long size)
+{
+ return((size + (1 << section_alignment[(int) seg]) - 1) & (-1 << section_alignment[(int) seg]));
+
+}
+
+void
+ md_apply_fix(fixP, val)
+fixS *fixP;
+long val;
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ switch (fixP->fx_size) {
+ case 1:
+ *buf++=val;
+ break;
+ case 2:
+ *buf++=(val>>8);
+ *buf++=val;
+ break;
+ case 4:
+ *buf++=(val>>24);
+ *buf++=(val>>16);
+ *buf++=(val>>8);
+ *buf++=val;
+ break;
+ default:
+ abort();
+
+ }
+}
+
+void DEFUN(md_operand, (expressionP),expressionS *expressionP)
+{ }
+
+int md_long_jump_size;
+int
+ md_estimate_size_before_relax(fragP, segment_type)
+register fragS *fragP;
+register segT segment_type;
+{
+ printf("call tomd_estimate_size_before_relax \n"); abort(); }
+/* Put number into target byte order */
+
+void DEFUN(md_number_to_chars,(ptr, use, nbytes),
+ char *ptr AND
+ long use AND
+ int nbytes)
+{
+ switch (nbytes) {
+ case 4: *ptr++ = (use >> 24) & 0xff;
+ case 3: *ptr++ = (use >> 16) & 0xff;
+ case 2: *ptr++ = (use >> 8) & 0xff;
+ case 1: *ptr++ = (use >> 0) & 0xff;
+ break;
+ default:
+ abort();
+ }
+}
+long md_pcrel_from(fixP)
+fixS *fixP; { abort(); }
+
+void tc_coff_symbol_emit_hook() { }
+
+
+void tc_reloc_mangle(fix_ptr, intr, base)
+fixS *fix_ptr;
+struct internal_reloc *intr;
+bfd_vma base;
+
+{
+ symbolS *symbol_ptr;
+
+ symbol_ptr = fix_ptr->fx_addsy;
+
+ /* If this relocation is attached to a symbol then it's ok
+ to output it */
+ if (fix_ptr->fx_r_type == RELOC_32) {
+ /* cons likes to create reloc32's whatever the size of the reloc..
+ */
+ switch (fix_ptr->fx_size)
+ {
+
+ case 2:
+ intr->r_type = R_RELWORD;
+ break;
+ case 1:
+ intr->r_type = R_RELBYTE;
+ break;
+ default:
+ abort();
+
+ }
+
+ }
+ else {
+ intr->r_type = fix_ptr->fx_r_type;
+ }
+
+ intr->r_vaddr = fix_ptr->fx_frag->fr_address + fix_ptr->fx_where +base;
+ intr->r_offset = fix_ptr->fx_offset;
+
+ if (symbol_ptr)
+ intr->r_symndx = symbol_ptr->sy_number;
+ else
+ intr->r_symndx = -1;
+
+
+}
+
+/* end of tc-h8300.c */
diff --git a/gnu/usr.bin/as/config/tc-h8300.h b/gnu/usr.bin/as/config/tc-h8300.h
new file mode 100644
index 0000000..6da7896
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-h8300.h
@@ -0,0 +1,38 @@
+/* This file is tc-h8300.h
+
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#define TC_H8300
+
+/* This macro translates between an internal fix and an coff reloc type */
+#define TC_COFF_FIX2RTYPE(fixP) abort();
+
+#define BFD_ARCH bfd_arch_h8300
+#define COFF_MAGIC 0x8300
+#define TC_COUNT_RELOC(x) (1)
+
+
+#define TC_RELOC_MANGLE(a,b,c) tc_reloc_mangle(a,b,c)
+
+#define DO_NOT_STRIP 1
+#define DO_STRIP 0
+#define LISTING_HEADER "Hitachi H8/300 GAS "
+
+/* end of tc-h8300.h */
diff --git a/gnu/usr.bin/as/config/tc-i386.c b/gnu/usr.bin/as/config/tc-i386.c
new file mode 100644
index 0000000..84848d2
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-i386.c
@@ -0,0 +1,2313 @@
+/* i386.c -- Assemble code for the Intel 80386
+ Copyright (C) 1989, 1991, 1992 Free Software Foundation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ Intel 80386 machine specific gas.
+ Written by Eliot Dresselhaus (eliot@mgm.mit.edu).
+ Bugs & suggestions are completely welcome. This is free software.
+ Please help us make it better.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: tc-i386.c,v 1.1 1993/11/03 00:54:23 paul Exp $";
+#endif
+
+#include "as.h"
+
+#include "obstack.h"
+#include "opcode/i386.h"
+
+/* 'md_assemble ()' gathers together information and puts it into a
+ i386_insn. */
+
+typedef struct {
+ /* TM holds the template for the insn were currently assembling. */
+ template tm;
+ /* SUFFIX holds the opcode suffix (e.g. 'l' for 'movl') if given. */
+ char suffix;
+ /* Operands are coded with OPERANDS, TYPES, DISPS, IMMS, and REGS. */
+
+ /* OPERANDS gives the number of given operands. */
+ unsigned int operands;
+
+ /* REG_OPERANDS, DISP_OPERANDS, MEM_OPERANDS, IMM_OPERANDS give the number of
+ given register, displacement, memory operands and immediate operands. */
+ unsigned int reg_operands, disp_operands, mem_operands, imm_operands;
+
+ /* TYPES [i] is the type (see above #defines) which tells us how to
+ search through DISPS [i] & IMMS [i] & REGS [i] for the required
+ operand. */
+ unsigned int types[MAX_OPERANDS];
+
+ /* Displacements (if given) for each operand. */
+ expressionS *disps[MAX_OPERANDS];
+
+#ifdef PIC
+ /* Relocation type for operand */
+ enum reloc_type disp_reloc[MAX_OPERANDS];
+#endif
+
+ /* Immediate operands (if given) for each operand. */
+ expressionS *imms[MAX_OPERANDS];
+
+ /* Register operands (if given) for each operand. */
+ reg_entry *regs[MAX_OPERANDS];
+
+ /* BASE_REG, INDEX_REG, and LOG2_SCALE_FACTOR are used to encode
+ the base index byte below. */
+ reg_entry *base_reg;
+ reg_entry *index_reg;
+ unsigned int log2_scale_factor;
+
+ /* SEG gives the seg_entry of this insn. It is equal to zero unless
+ an explicit segment override is given. */
+ const seg_entry *seg; /* segment for memory operands (if given) */
+
+ /* PREFIX holds all the given prefix opcodes (usually null).
+ PREFIXES is the size of PREFIX. */
+ /* richfix: really unsigned? */
+ unsigned char prefix[MAX_PREFIXES];
+ unsigned int prefixes;
+
+ /* RM and IB are the modrm byte and the base index byte where the addressing
+ modes of this insn are encoded. */
+
+ modrm_byte rm;
+ base_index_byte bi;
+
+} i386_insn;
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful */
+const char comment_chars[] = "#";
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that comments started like this one will always work if
+ '/' isn't otherwise defined. */
+const char line_comment_chars[] = "#/"; /* removed '#' xoxorich. */
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "fFdDxX";
+
+/* tables for lexical analysis */
+static char opcode_chars[256];
+static char register_chars[256];
+static char operand_chars[256];
+static char space_chars[256];
+static char identifier_chars[256];
+static char digit_chars[256];
+
+/* lexical macros */
+#define is_opcode_char(x) (opcode_chars[(unsigned char) x])
+#define is_operand_char(x) (operand_chars[(unsigned char) x])
+#define is_register_char(x) (register_chars[(unsigned char) x])
+#define is_space_char(x) (space_chars[(unsigned char) x])
+#define is_identifier_char(x) (identifier_chars[(unsigned char) x])
+#define is_digit_char(x) (digit_chars[(unsigned char) x])
+
+/* put here all non-digit non-letter charcters that may occur in an operand */
+static char operand_special_chars[] = "%$-+(,)*._~/<>|&^!:";
+
+static char *ordinal_names[] = { "first", "second", "third" }; /* for printfs */
+
+/* md_assemble() always leaves the strings it's passed unaltered. To
+ effect this we maintain a stack of saved characters that we've smashed
+ with '\0's (indicating end of strings for various sub-fields of the
+ assembler instruction). */
+static char save_stack[32];
+static char *save_stack_p; /* stack pointer */
+#define END_STRING_AND_SAVE(s) *save_stack_p++ = *s; *s = '\0'
+#define RESTORE_END_STRING(s) *s = *--save_stack_p
+
+ /* The instruction we're assembling. */
+ static i386_insn i;
+
+/* Per instruction expressionS buffers: 2 displacements & 2 immediate max. */
+static expressionS disp_expressions[2], im_expressions[2];
+
+/* pointers to ebp & esp entries in reg_hash hash table */
+static reg_entry *ebp, *esp;
+
+static int this_operand; /* current operand we are working on */
+
+/*
+ Interface to relax_segment.
+ There are 2 relax states for 386 jump insns: one for conditional & one
+ for unconditional jumps. This is because the these two types of jumps
+ add different sizes to frags when we're figuring out what sort of jump
+ to choose to reach a given label. */
+
+/* types */
+#define COND_JUMP 1 /* conditional jump */
+#define UNCOND_JUMP 2 /* unconditional jump */
+/* sizes */
+#define BYTE 0
+#define WORD 1
+#define DWORD 2
+#define UNKNOWN_SIZE 3
+
+#define ENCODE_RELAX_STATE(type,size) ((type<<2) | (size))
+#define SIZE_FROM_RELAX_STATE(s) \
+ ( (((s) & 0x3) == BYTE ? 1 : (((s) & 0x3) == WORD ? 2 : 4)) )
+
+const relax_typeS md_relax_table[] = {
+ /*
+ The fields are:
+ 1) most positive reach of this state,
+ 2) most negative reach of this state,
+ 3) how many bytes this mode will add to the size of the current frag
+ 4) which index into the table to try if we can't fit into this one.
+ */
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+
+ /* For now we don't use word displacement jumps: they may be
+ untrustworthy. */
+ {127+1, -128+1, 0, ENCODE_RELAX_STATE(COND_JUMP,DWORD) },
+ /* word conditionals add 3 bytes to frag:
+ 2 opcode prefix; 1 displacement bytes */
+ {32767+2, -32768+2, 3, ENCODE_RELAX_STATE(COND_JUMP,DWORD) },
+ /* dword conditionals adds 4 bytes to frag:
+ 1 opcode prefix; 3 displacement bytes */
+ {0, 0, 4, 0},
+ {1, 1, 0, 0},
+
+ {127+1, -128+1, 0, ENCODE_RELAX_STATE(UNCOND_JUMP,DWORD) },
+ /* word jmp adds 2 bytes to frag:
+ 1 opcode prefix; 1 displacement bytes */
+ {32767+2, -32768+2, 2, ENCODE_RELAX_STATE(UNCOND_JUMP,DWORD) },
+ /* dword jmp adds 3 bytes to frag:
+ 0 opcode prefix; 3 displacement bytes */
+ {0, 0, 3, 0},
+ {1, 1, 0, 0},
+
+};
+
+#if __STDC__ == 1
+
+static char *output_invalid(int c);
+static int fits_in_signed_byte(long num);
+static int fits_in_signed_word(long num);
+static int fits_in_unsigned_byte(long num);
+static int fits_in_unsigned_word(long num);
+static int i386_operand(char *operand_string);
+static int smallest_imm_type(long num);
+static reg_entry *parse_register(char *reg_string);
+static unsigned long mode_from_disp_size(unsigned long t);
+static unsigned long opcode_suffix_to_type(unsigned long s);
+static void s_bss(void);
+
+#else /* not __STDC__ */
+
+static char *output_invalid();
+static int fits_in_signed_byte();
+static int fits_in_signed_word();
+static int fits_in_unsigned_byte();
+static int fits_in_unsigned_word();
+static int i386_operand();
+static int smallest_imm_type();
+static reg_entry *parse_register();
+static unsigned long mode_from_disp_size();
+static unsigned long opcode_suffix_to_type();
+static void s_bss();
+
+#endif /* not __STDC__ */
+
+
+/* Ignore certain directives generated by gcc. This probably should
+ not be here. */
+void dummy ()
+{
+ while (*input_line_pointer && *input_line_pointer != '\n')
+ input_line_pointer++;
+}
+
+const pseudo_typeS md_pseudo_table[] = {
+ { "bss", s_bss, 0 },
+
+#ifndef OLD_GAS
+ { "align", s_align_bytes, 0 },
+#else /* OLD_GAS */
+ { "align", s_align_ptwo, 0 },
+#endif /* OLD_GAS */
+
+ { "ffloat", float_cons, 'f' },
+ { "dfloat", float_cons, 'd' },
+ { "tfloat", float_cons, 'x' },
+ { "value", cons, 2 },
+ { 0, 0, 0 }
+};
+
+/* for interface with expression () */
+extern char * input_line_pointer;
+
+/* obstack for constructing various things in md_begin */
+struct obstack o;
+
+/* hash table for opcode lookup */
+static struct hash_control *op_hash = (struct hash_control *) 0;
+/* hash table for register lookup */
+static struct hash_control *reg_hash = (struct hash_control *) 0;
+/* hash table for prefix lookup */
+static struct hash_control *prefix_hash = (struct hash_control *) 0;
+
+
+void md_begin ()
+{
+ char * hash_err;
+
+ obstack_begin (&o,4096);
+
+ /* initialize op_hash hash table */
+ op_hash = hash_new(); /* xmalloc handles error */
+
+ {
+ register const template *optab;
+ register templates *core_optab;
+ char *prev_name;
+
+ optab = i386_optab; /* setup for loop */
+ prev_name = optab->name;
+ obstack_grow (&o, optab, sizeof(template));
+ core_optab = (templates *) xmalloc (sizeof (templates));
+
+ for (optab++; optab < i386_optab_end; optab++) {
+ if (! strcmp (optab->name, prev_name)) {
+ /* same name as before --> append to current template list */
+ obstack_grow (&o, optab, sizeof(template));
+ } else {
+ /* different name --> ship out current template list;
+ add to hash table; & begin anew */
+ /* Note: end must be set before start! since obstack_next_free changes
+ upon opstack_finish */
+ core_optab->end = (template *) obstack_next_free(&o);
+ core_optab->start = (template *) obstack_finish(&o);
+ hash_err = hash_insert (op_hash, prev_name, (char *) core_optab);
+ if (hash_err && *hash_err) {
+ hash_error:
+ as_fatal("Internal Error: Can't hash %s: %s", prev_name, hash_err);
+ }
+ prev_name = optab->name;
+ core_optab = (templates *) xmalloc (sizeof(templates));
+ obstack_grow (&o, optab, sizeof(template));
+ }
+ }
+ }
+
+ /* initialize reg_hash hash table */
+ reg_hash = hash_new();
+ {
+ register const reg_entry *regtab;
+
+ for (regtab = i386_regtab; regtab < i386_regtab_end; regtab++) {
+ hash_err = hash_insert (reg_hash, regtab->reg_name, regtab);
+ if (hash_err && *hash_err) goto hash_error;
+ }
+ }
+
+ esp = (reg_entry *) hash_find (reg_hash, "esp");
+ ebp = (reg_entry *) hash_find (reg_hash, "ebp");
+
+ /* initialize reg_hash hash table */
+ prefix_hash = hash_new();
+ {
+ register const prefix_entry *prefixtab;
+
+ for (prefixtab = i386_prefixtab;
+ prefixtab < i386_prefixtab_end; prefixtab++) {
+ hash_err = hash_insert (prefix_hash, prefixtab->prefix_name, prefixtab);
+ if (hash_err && *hash_err) goto hash_error;
+ }
+ }
+
+ /* fill in lexical tables: opcode_chars, operand_chars, space_chars */
+ {
+ register unsigned int c;
+
+ memset(opcode_chars, '\0', sizeof(opcode_chars));
+ memset(operand_chars, '\0', sizeof(operand_chars));
+ memset(space_chars, '\0', sizeof(space_chars));
+ memset(identifier_chars, '\0', sizeof(identifier_chars));
+ memset(digit_chars, '\0', sizeof(digit_chars));
+
+ for (c = 0; c < 256; c++) {
+ if (islower(c) || isdigit(c)) {
+ opcode_chars[c] = c;
+ register_chars[c] = c;
+ } else if (isupper(c)) {
+ opcode_chars[c] = tolower(c);
+ register_chars[c] = opcode_chars[c];
+ } else if (c == PREFIX_SEPERATOR) {
+ opcode_chars[c] = c;
+ } else if (c == ')' || c == '(') {
+ register_chars[c] = c;
+ }
+
+ if (isupper(c) || islower(c) || isdigit(c))
+ operand_chars[c] = c;
+ else if (c && strchr(operand_special_chars, c))
+ operand_chars[c] = c;
+
+ if (isdigit(c) || c == '-') digit_chars[c] = c;
+
+ if (isalpha(c) || c == '_' || c == '.' || isdigit(c))
+ identifier_chars[c] = c;
+
+ if (c == ' ' || c == '\t') space_chars[c] = c;
+ }
+ }
+}
+
+void md_end() {} /* not much to do here. */
+
+
+#define DEBUG386
+#ifdef DEBUG386
+
+/* debugging routines for md_assemble */
+static void pi (), pte (), pt (), pe (), ps ();
+
+static void pi (line, x)
+char * line;
+i386_insn *x;
+{
+ register template *p;
+ int i;
+
+ fprintf (stdout, "%s: template ", line);
+ pte (&x->tm);
+ fprintf (stdout, " modrm: mode %x reg %x reg/mem %x",
+ x->rm.mode, x->rm.reg, x->rm.regmem);
+ fprintf (stdout, " base %x index %x scale %x\n",
+ x->bi.base, x->bi.index, x->bi.scale);
+ for (i = 0; i < x->operands; i++) {
+ fprintf (stdout, " #%d: ", i+1);
+ pt (x->types[i]);
+ fprintf (stdout, "\n");
+ if (x->types[i] & Reg) fprintf (stdout, "%s\n", x->regs[i]->reg_name);
+ if (x->types[i] & Imm) pe (x->imms[i]);
+ if (x->types[i] & (Disp|Abs)) pe (x->disps[i]);
+ }
+}
+
+static void pte (t)
+template *t;
+{
+ int i;
+ fprintf (stdout, " %d operands ", t->operands);
+ fprintf (stdout, "opcode %x ",
+ t->base_opcode);
+ if (t->extension_opcode != None)
+ fprintf (stdout, "ext %x ", t->extension_opcode);
+ if (t->opcode_modifier&D)
+ fprintf (stdout, "D");
+ if (t->opcode_modifier&W)
+ fprintf (stdout, "W");
+ fprintf (stdout, "\n");
+ for (i = 0; i < t->operands; i++) {
+ fprintf (stdout, " #%d type ", i+1);
+ pt (t->operand_types[i]);
+ fprintf (stdout, "\n");
+ }
+}
+
+static void pe (e)
+expressionS *e;
+{
+ fprintf (stdout, " segment %s\n", segment_name (e->X_seg));
+ fprintf (stdout, " add_number %d (%x)\n",
+ e->X_add_number, e->X_add_number);
+ if (e->X_add_symbol) {
+ fprintf (stdout, " add_symbol ");
+ ps (e->X_add_symbol);
+ fprintf (stdout, "\n");
+ }
+ if (e->X_subtract_symbol) {
+ fprintf (stdout, " sub_symbol ");
+ ps (e->X_subtract_symbol);
+ fprintf (stdout, "\n");
+ }
+}
+
+static void ps (s)
+symbolS *s;
+{
+ fprintf (stdout, "%s type %s%s",
+ S_GET_NAME(s),
+ S_IS_EXTERNAL(s) ? "EXTERNAL " : "",
+ segment_name(S_GET_SEGMENT(s)));
+}
+
+struct type_name {
+ unsigned int mask;
+ char *tname;
+} type_names[] = {
+ { Reg8, "r8" }, { Reg16, "r16" }, { Reg32, "r32" }, { Imm8, "i8" },
+ { Imm8S, "i8s" },
+ { Imm16, "i16" }, { Imm32, "i32" }, { Mem8, "Mem8"}, { Mem16, "Mem16"},
+ { Mem32, "Mem32"}, { BaseIndex, "BaseIndex" },
+ { Abs8, "Abs8" }, { Abs16, "Abs16" }, { Abs32, "Abs32" },
+ { Disp8, "d8" }, { Disp16, "d16" },
+ { Disp32, "d32" }, { SReg2, "SReg2" }, { SReg3, "SReg3" }, { Acc, "Acc" },
+ { InOutPortReg, "InOutPortReg" }, { ShiftCount, "ShiftCount" },
+ { Imm1, "i1" }, { Control, "control reg" }, {Test, "test reg"},
+ { FloatReg, "FReg"}, {FloatAcc, "FAcc"},
+ { JumpAbsolute, "Jump Absolute"},
+ { 0, "" }
+};
+
+static void pt (t)
+unsigned int t;
+{
+ register struct type_name *ty;
+
+ if (t == Unknown) {
+ fprintf (stdout, "Unknown");
+ } else {
+ for (ty = type_names; ty->mask; ty++)
+ if (t & ty->mask) fprintf (stdout, "%s, ", ty->tname);
+ }
+ fflush (stdout);
+}
+
+#endif /* DEBUG386 */
+
+/*
+ This is the guts of the machine-dependent assembler. LINE points to a
+ machine dependent instruction. This funciton is supposed to emit
+ the frags/bytes it assembles to.
+ */
+void md_assemble (line)
+char *line;
+{
+ /* Holds temlate once we've found it. */
+ register template *t;
+
+ /* Possible templates for current insn */
+ templates *current_templates = (templates *) 0;
+
+ /* Initialize globals. */
+ memset(&i, '\0', sizeof(i));
+ memset(disp_expressions, '\0', sizeof(disp_expressions));
+ memset(im_expressions, '\0', sizeof(im_expressions));
+ save_stack_p = save_stack; /* reset stack pointer */
+
+ /* Fist parse an opcode & call i386_operand for the operands.
+ We assume that the scrubber has arranged it so that line[0] is the valid
+ start of a (possibly prefixed) opcode. */
+ {
+ register char *l = line; /* Fast place to put LINE. */
+
+ /* 1 if operand is pending after ','. */
+ unsigned int expecting_operand = 0;
+ /* 1 if we found a prefix only acceptable with string insns. */
+ unsigned int expecting_string_instruction = 0;
+ /* Non-zero if operand parens not balenced. */
+ unsigned int paren_not_balenced;
+ char * token_start = l;
+
+ while (! is_space_char(*l) && *l != END_OF_INSN) {
+ if (! is_opcode_char(*l)) {
+ as_bad("invalid character %s in opcode", output_invalid(*l));
+ return;
+ } else if (*l != PREFIX_SEPERATOR) {
+ *l = opcode_chars[(unsigned char) *l]; /* fold case of opcodes */
+ l++;
+ } else { /* this opcode's got a prefix */
+ register unsigned int q;
+ register prefix_entry * prefix;
+
+ if (l == token_start) {
+ as_bad("expecting prefix; got nothing");
+ return;
+ }
+ END_STRING_AND_SAVE (l);
+ prefix = (prefix_entry *) hash_find (prefix_hash, token_start);
+ if (! prefix) {
+ as_bad("no such opcode prefix ('%s')", token_start);
+ return;
+ }
+ RESTORE_END_STRING (l);
+ /* check for repeated prefix */
+ for (q = 0; q < i.prefixes; q++)
+ if (i.prefix[q] == prefix->prefix_code) {
+ as_bad("same prefix used twice; you don't really want this!");
+ return;
+ }
+ if (i.prefixes == MAX_PREFIXES) {
+ as_bad("too many opcode prefixes");
+ return;
+ }
+ i.prefix[i.prefixes++] = prefix->prefix_code;
+ if (prefix->prefix_code == REPE || prefix->prefix_code == REPNE)
+ expecting_string_instruction = 1;
+ /* skip past PREFIX_SEPERATOR and reset token_start */
+ token_start = ++l;
+ }
+ }
+ END_STRING_AND_SAVE (l);
+ if (token_start == l) {
+ as_bad("expecting opcode; got nothing");
+ return;
+ }
+
+ /* Lookup insn in hash; try intel & att naming conventions if appropriate;
+ that is: we only use the opcode suffix 'b' 'w' or 'l' if we need to. */
+ current_templates = (templates *) hash_find (op_hash, token_start);
+ if (! current_templates) {
+ int last_index = strlen(token_start) - 1;
+ char last_char = token_start[last_index];
+ switch (last_char) {
+ case DWORD_OPCODE_SUFFIX:
+ case WORD_OPCODE_SUFFIX:
+ case BYTE_OPCODE_SUFFIX:
+ token_start[last_index] = '\0';
+ current_templates = (templates *) hash_find (op_hash, token_start);
+ token_start[last_index] = last_char;
+ i.suffix = last_char;
+ }
+ if (!current_templates) {
+ as_bad("no such 386 instruction: `%s'", token_start); return;
+ }
+ }
+ RESTORE_END_STRING (l);
+
+ /* check for rep/repne without a string instruction */
+ if (expecting_string_instruction &&
+ ! IS_STRING_INSTRUCTION (current_templates->
+ start->base_opcode)) {
+ as_bad("expecting string instruction after rep/repne");
+ return;
+ }
+
+ /* There may be operands to parse. */
+ if (*l != END_OF_INSN &&
+ /* For string instructions, we ignore any operands if given. This
+ kludges, for example, 'rep/movsb %ds:(%esi), %es:(%edi)' where
+ the operands are always going to be the same, and are not really
+ encoded in machine code. */
+ ! IS_STRING_INSTRUCTION (current_templates->
+ start->base_opcode)) {
+ /* parse operands */
+ do {
+ /* skip optional white space before operand */
+ while (! is_operand_char(*l) && *l != END_OF_INSN) {
+ if (! is_space_char(*l)) {
+ as_bad("invalid character %s before %s operand",
+ output_invalid(*l),
+ ordinal_names[i.operands]);
+ return;
+ }
+ l++;
+ }
+ token_start = l; /* after white space */
+ paren_not_balenced = 0;
+ while (paren_not_balenced || *l != ',') {
+ if (*l == END_OF_INSN) {
+ if (paren_not_balenced) {
+ as_bad("unbalenced parenthesis in %s operand.",
+ ordinal_names[i.operands]);
+ return;
+ } else break; /* we are done */
+ } else if (! is_operand_char(*l)) {
+ as_bad("invalid character %s in %s operand",
+ output_invalid(*l),
+ ordinal_names[i.operands]);
+ return;
+ }
+ if (*l == '(') ++paren_not_balenced;
+ if (*l == ')') --paren_not_balenced;
+ l++;
+ }
+ if (l != token_start) { /* yes, we've read in another operand */
+ unsigned int operand_ok;
+ this_operand = i.operands++;
+ if (i.operands > MAX_OPERANDS) {
+ as_bad("spurious operands; (%d operands/instruction max)",
+ MAX_OPERANDS);
+ return;
+ }
+ /* now parse operand adding info to 'i' as we go along */
+ END_STRING_AND_SAVE (l);
+ operand_ok = i386_operand (token_start);
+ RESTORE_END_STRING (l); /* restore old contents */
+ if (!operand_ok) return;
+ } else {
+ if (expecting_operand) {
+ expecting_operand_after_comma:
+ as_bad("expecting operand after ','; got nothing");
+ return;
+ }
+ if (*l == ',') {
+ as_bad("expecting operand before ','; got nothing");
+ return;
+ }
+ }
+
+ /* now *l must be either ',' or END_OF_INSN */
+ if (*l == ',') {
+ if (*++l == END_OF_INSN) { /* just skip it, if it's \n complain */
+ goto expecting_operand_after_comma;
+ }
+ expecting_operand = 1;
+ }
+ } while (*l != END_OF_INSN); /* until we get end of insn */
+ }
+ }
+
+ /* Now we've parsed the opcode into a set of templates, and have the
+ operands at hand.
+ Next, we find a template that matches the given insn,
+ making sure the overlap of the given operands types is consistent
+ with the template operand types. */
+
+#define MATCH(overlap,given_type) \
+ (overlap && \
+ (overlap & (JumpAbsolute|BaseIndex|Mem8)) \
+ == (given_type & (JumpAbsolute|BaseIndex|Mem8)))
+
+ /* If m0 and m1 are register matches they must be consistent
+ with the expected operand types t0 and t1.
+ That is, if both m0 & m1 are register matches
+ i.e. ( ((m0 & (Reg)) && (m1 & (Reg)) ) ?
+ then, either 1. or 2. must be true:
+ 1. the expected operand type register overlap is null:
+ (t0 & t1 & Reg) == 0
+ AND
+ the given register overlap is null:
+ (m0 & m1 & Reg) == 0
+ 2. the expected operand type register overlap == the given
+ operand type overlap: (t0 & t1 & m0 & m1 & Reg).
+ */
+#define CONSISTENT_REGISTER_MATCH(m0, m1, t0, t1) \
+ ( ((m0 & (Reg)) && (m1 & (Reg))) ? \
+ ( ((t0 & t1 & (Reg)) == 0 && (m0 & m1 & (Reg)) == 0) || \
+ ((t0 & t1) & (m0 & m1) & (Reg)) \
+ ) : 1)
+ {
+ register unsigned int overlap0, overlap1;
+ expressionS * exp;
+ unsigned int overlap2;
+ unsigned int found_reverse_match;
+
+ overlap0 = overlap1 = overlap2 = found_reverse_match = 0;
+ for (t = current_templates->start;
+ t < current_templates->end;
+ t++) {
+
+ /* must have right number of operands */
+ if (i.operands != t->operands) continue;
+ else if (!t->operands) break; /* 0 operands always matches */
+
+ overlap0 = i.types[0] & t->operand_types[0];
+ switch (t->operands) {
+ case 1:
+ if (! MATCH (overlap0,i.types[0])) continue;
+ break;
+ case 2: case 3:
+ overlap1 = i.types[1] & t->operand_types[1];
+ if (! MATCH (overlap0,i.types[0]) ||
+ ! MATCH (overlap1,i.types[1]) ||
+ ! CONSISTENT_REGISTER_MATCH(overlap0, overlap1,
+ t->operand_types[0],
+ t->operand_types[1])) {
+
+ /* check if other direction is valid ... */
+ if (! (t->opcode_modifier & COMES_IN_BOTH_DIRECTIONS))
+ continue;
+
+ /* try reversing direction of operands */
+ overlap0 = i.types[0] & t->operand_types[1];
+ overlap1 = i.types[1] & t->operand_types[0];
+ if (! MATCH (overlap0,i.types[0]) ||
+ ! MATCH (overlap1,i.types[1]) ||
+ ! CONSISTENT_REGISTER_MATCH (overlap0, overlap1,
+ t->operand_types[0],
+ t->operand_types[1])) {
+ /* does not match either direction */
+ continue;
+ }
+ /* found a reverse match here -- slip through */
+ /* found_reverse_match holds which of D or FloatD we've found */
+ found_reverse_match = t->opcode_modifier & COMES_IN_BOTH_DIRECTIONS;
+ } /* endif: not forward match */
+ /* found either forward/reverse 2 operand match here */
+ if (t->operands == 3) {
+ overlap2 = i.types[2] & t->operand_types[2];
+ if (! MATCH (overlap2,i.types[2]) ||
+ ! CONSISTENT_REGISTER_MATCH (overlap0, overlap2,
+ t->operand_types[0],
+ t->operand_types[2]) ||
+ ! CONSISTENT_REGISTER_MATCH (overlap1, overlap2,
+ t->operand_types[1],
+ t->operand_types[2]))
+ continue;
+ }
+ /* found either forward/reverse 2 or 3 operand match here:
+ slip through to break */
+ }
+ break; /* we've found a match; break out of loop */
+ } /* for (t = ... */
+ if (t == current_templates->end) { /* we found no match */
+ as_bad("operands given don't match any known 386 instruction");
+ return;
+ }
+
+ /* Copy the template we found (we may change it!). */
+ memcpy(&i.tm, t, sizeof(template));
+ t = &i.tm; /* alter new copy of template */
+
+ /* If there's no opcode suffix we try to invent one based on register
+ operands. */
+ if (! i.suffix && i.reg_operands) {
+ /* We take i.suffix from the LAST register operand specified. This
+ assumes that the last register operands is the destination register
+ operand. */
+ int o;
+ for (o = 0; o < MAX_OPERANDS; o++)
+ if (i.types[o] & Reg) {
+ i.suffix = (i.types[o] == Reg8) ? BYTE_OPCODE_SUFFIX :
+ (i.types[o] == Reg16) ? WORD_OPCODE_SUFFIX :
+ DWORD_OPCODE_SUFFIX;
+ }
+ }
+
+ /* Make still unresolved immediate matches conform to size of immediate
+ given in i.suffix. Note: overlap2 cannot be an immediate!
+ We assume this. */
+ if ((overlap0 & (Imm8|Imm8S|Imm16|Imm32))
+ && overlap0 != Imm8 && overlap0 != Imm8S
+ && overlap0 != Imm16 && overlap0 != Imm32) {
+ if (! i.suffix) {
+ as_bad("no opcode suffix given; can't determine immediate size");
+ return;
+ }
+ overlap0 &= (i.suffix == BYTE_OPCODE_SUFFIX ? (Imm8|Imm8S) :
+ (i.suffix == WORD_OPCODE_SUFFIX ? Imm16 : Imm32));
+ }
+ if ((overlap1 & (Imm8|Imm8S|Imm16|Imm32))
+ && overlap1 != Imm8 && overlap1 != Imm8S
+ && overlap1 != Imm16 && overlap1 != Imm32) {
+ if (! i.suffix) {
+ as_bad("no opcode suffix given; can't determine immediate size");
+ return;
+ }
+ overlap1 &= (i.suffix == BYTE_OPCODE_SUFFIX ? (Imm8|Imm8S) :
+ (i.suffix == WORD_OPCODE_SUFFIX ? Imm16 : Imm32));
+ }
+
+ i.types[0] = overlap0;
+ i.types[1] = overlap1;
+ i.types[2] = overlap2;
+
+ if (overlap0 & ImplicitRegister) i.reg_operands--;
+ if (overlap1 & ImplicitRegister) i.reg_operands--;
+ if (overlap2 & ImplicitRegister) i.reg_operands--;
+ if (overlap0 & Imm1) i.imm_operands = 0; /* kludge for shift insns */
+
+ if (found_reverse_match) {
+ unsigned int save;
+ save = t->operand_types[0];
+ t->operand_types[0] = t->operand_types[1];
+ t->operand_types[1] = save;
+ }
+
+ /* Finalize opcode. First, we change the opcode based on the operand
+ size given by i.suffix: we never have to change things for byte insns,
+ or when no opcode suffix is need to size the operands. */
+
+ if (! i.suffix && (t->opcode_modifier & W)) {
+ as_bad("no opcode suffix given and no register operands; can't size instruction");
+ return;
+ }
+
+ if (i.suffix && i.suffix != BYTE_OPCODE_SUFFIX) {
+ /* Select between byte and word/dword operations. */
+ if (t->opcode_modifier & W)
+ t->base_opcode |= W;
+ /* Now select between word & dword operations via the
+ operand size prefix. */
+ if (i.suffix == WORD_OPCODE_SUFFIX) {
+ if (i.prefixes == MAX_PREFIXES) {
+ as_bad("%d prefixes given and 'w' opcode suffix gives too many prefixes",
+ MAX_PREFIXES);
+ return;
+ }
+ i.prefix[i.prefixes++] = WORD_PREFIX_OPCODE;
+ }
+ }
+
+ /* For insns with operands there are more diddles to do to the opcode. */
+ if (i.operands) {
+ /* If we found a reverse match we must alter the opcode direction bit
+ found_reverse_match holds bit to set (different for int &
+ float insns). */
+
+ if (found_reverse_match) {
+ t->base_opcode |= found_reverse_match;
+ }
+
+ /*
+ The imul $imm, %reg instruction is converted into
+ imul $imm, %reg, %reg. */
+ if (t->opcode_modifier & imulKludge) {
+ i.regs[2] = i.regs[1]; /* Pretend we saw the 3 operand case. */
+ i.reg_operands = 2;
+ }
+
+ /* Certain instructions expect the destination to be in the i.rm.reg
+ field. This is by far the exceptional case. For these instructions,
+ if the source operand is a register, we must reverse the i.rm.reg
+ and i.rm.regmem fields. We accomplish this by faking that the
+ two register operands were given in the reverse order. */
+ if ((t->opcode_modifier & ReverseRegRegmem) && i.reg_operands == 2) {
+ unsigned int first_reg_operand = (i.types[0] & Reg) ? 0 : 1;
+ unsigned int second_reg_operand = first_reg_operand + 1;
+ reg_entry *tmp = i.regs[first_reg_operand];
+ i.regs[first_reg_operand] = i.regs[second_reg_operand];
+ i.regs[second_reg_operand] = tmp;
+ }
+
+ if (t->opcode_modifier & ShortForm) {
+ /* The register or float register operand is in operand 0 or 1. */
+ unsigned int o = (i.types[0] & (Reg|FloatReg)) ? 0 : 1;
+ /* Register goes in low 3 bits of opcode. */
+ t->base_opcode |= i.regs[o]->reg_num;
+ } else if (t->opcode_modifier & ShortFormW) {
+ /* Short form with 0x8 width bit. Register is always dest. operand */
+ t->base_opcode |= i.regs[1]->reg_num;
+ if (i.suffix == WORD_OPCODE_SUFFIX ||
+ i.suffix == DWORD_OPCODE_SUFFIX)
+ t->base_opcode |= 0x8;
+ } else if (t->opcode_modifier & Seg2ShortForm) {
+ if (t->base_opcode == POP_SEG_SHORT && i.regs[0]->reg_num == 1) {
+ as_bad("you can't 'pop cs' on the 386.");
+ return;
+ }
+ t->base_opcode |= (i.regs[0]->reg_num << 3);
+ } else if (t->opcode_modifier & Seg3ShortForm) {
+ /* 'push %fs' is 0x0fa0; 'pop %fs' is 0x0fa1.
+ 'push %gs' is 0x0fa8; 'pop %fs' is 0x0fa9.
+ So, only if i.regs[0]->reg_num == 5 (%gs) do we need
+ to change the opcode. */
+ if (i.regs[0]->reg_num == 5)
+ t->base_opcode |= 0x08;
+ } else if (t->opcode_modifier & Modrm) {
+ /* The opcode is completed (modulo t->extension_opcode which must
+ be put into the modrm byte.
+ Now, we make the modrm & index base bytes based on all the info
+ we've collected. */
+
+ /* i.reg_operands MUST be the number of real register operands;
+ implicit registers do not count. */
+ if (i.reg_operands == 2) {
+ unsigned int source, dest;
+ source = (i.types[0] & (Reg|SReg2|SReg3|Control|Debug|Test)) ? 0 : 1;
+ dest = source + 1;
+ i.rm.mode = 3;
+ /* We must be careful to make sure that all segment/control/test/
+ debug registers go into the i.rm.reg field (despite the whether
+ they are source or destination operands). */
+ if (i.regs[dest]->reg_type & (SReg2|SReg3|Control|Debug|Test)) {
+ i.rm.reg = i.regs[dest]->reg_num;
+ i.rm.regmem = i.regs[source]->reg_num;
+ } else {
+ i.rm.reg = i.regs[source]->reg_num;
+ i.rm.regmem = i.regs[dest]->reg_num;
+ }
+ } else { /* if it's not 2 reg operands... */
+ if (i.mem_operands) {
+ unsigned int fake_zero_displacement = 0;
+ unsigned int o = (i.types[0] & Mem) ? 0 : ((i.types[1] & Mem) ? 1 : 2);
+
+ /* Encode memory operand into modrm byte and base index byte. */
+
+ if (i.base_reg == esp && ! i.index_reg) {
+ /* <disp>(%esp) becomes two byte modrm with no index register. */
+ i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
+ i.rm.mode = mode_from_disp_size(i.types[o]);
+ i.bi.base = ESP_REG_NUM;
+ i.bi.index = NO_INDEX_REGISTER;
+ i.bi.scale = 0; /* Must be zero! */
+ } else if (i.base_reg == ebp && !i.index_reg) {
+ if (! (i.types[o] & Disp)) {
+ /* Must fake a zero byte displacement.
+ There is no direct way to code '(%ebp)' directly. */
+ fake_zero_displacement = 1;
+ /* fake_zero_displacement code does not set this. */
+ i.types[o] |= Disp8;
+ }
+ i.rm.mode = mode_from_disp_size(i.types[o]);
+ i.rm.regmem = EBP_REG_NUM;
+ } else if (! i.base_reg && (i.types[o] & BaseIndex)) {
+ /* There are three cases here.
+ Case 1: '<32bit disp>(,1)' -- indirect absolute.
+ (Same as cases 2 & 3 with NO index register)
+ Case 2: <32bit disp> (,<index>) -- no base register with disp
+ Case 3: (, <index>) --- no base register;
+ no disp (must add 32bit 0 disp). */
+ i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
+ i.rm.mode = 0; /* 32bit mode */
+ i.bi.base = NO_BASE_REGISTER;
+ i.types[o] &= ~Disp;
+ i.types[o] |= Disp32; /* Must be 32bit! */
+ if (i.index_reg) { /* case 2 or case 3 */
+ i.bi.index = i.index_reg->reg_num;
+ i.bi.scale = i.log2_scale_factor;
+ if (i.disp_operands == 0)
+ fake_zero_displacement = 1; /* case 3 */
+ } else {
+ i.bi.index = NO_INDEX_REGISTER;
+ i.bi.scale = 0;
+ }
+ } else if (i.disp_operands && !i.base_reg && !i.index_reg) {
+ /* Operand is just <32bit disp> */
+ i.rm.regmem = EBP_REG_NUM;
+ i.rm.mode = 0;
+ i.types[o] &= ~Disp;
+ i.types[o] |= Disp32;
+ } else {
+ /* It's not a special case; rev'em up. */
+ i.rm.regmem = i.base_reg->reg_num;
+ i.rm.mode = mode_from_disp_size(i.types[o]);
+ if (i.index_reg) {
+ i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
+ i.bi.base = i.base_reg->reg_num;
+ i.bi.index = i.index_reg->reg_num;
+ i.bi.scale = i.log2_scale_factor;
+ if (i.base_reg == ebp && i.disp_operands == 0) { /* pace */
+ fake_zero_displacement = 1;
+ i.types[o] |= Disp8;
+ i.rm.mode = mode_from_disp_size(i.types[o]);
+ }
+ }
+ }
+ if (fake_zero_displacement) {
+ /* Fakes a zero displacement assuming that i.types[o] holds
+ the correct displacement size. */
+ exp = &disp_expressions[i.disp_operands++];
+ i.disps[o] = exp;
+ exp->X_seg = SEG_ABSOLUTE;
+ exp->X_add_number = 0;
+ exp->X_add_symbol = (symbolS *) 0;
+ exp->X_subtract_symbol = (symbolS *) 0;
+ }
+
+ /* Select the correct segment for the memory operand. */
+ if (i.seg) {
+ unsigned int seg_index;
+ const seg_entry *default_seg;
+
+ if (i.rm.regmem == ESCAPE_TO_TWO_BYTE_ADDRESSING) {
+ seg_index = (i.rm.mode<<3) | i.bi.base;
+ default_seg = two_byte_segment_defaults[seg_index];
+ } else {
+ seg_index = (i.rm.mode<<3) | i.rm.regmem;
+ default_seg = one_byte_segment_defaults[seg_index];
+ }
+ /* If the specified segment is not the default, use an
+ opcode prefix to select it */
+ if (i.seg != default_seg) {
+ if (i.prefixes == MAX_PREFIXES) {
+ as_bad("%d prefixes given and %s segment override gives too many prefixes",
+ MAX_PREFIXES, i.seg->seg_name);
+ return;
+ }
+ i.prefix[i.prefixes++] = i.seg->seg_prefix;
+ }
+ }
+ }
+
+ /* Fill in i.rm.reg or i.rm.regmem field with register operand
+ (if any) based on t->extension_opcode. Again, we must be careful
+ to make sure that segment/control/debug/test registers are coded
+ into the i.rm.reg field. */
+ if (i.reg_operands) {
+ unsigned int o =
+ (i.types[0] & (Reg|SReg2|SReg3|Control|Debug|Test)) ? 0 :
+ (i.types[1] & (Reg|SReg2|SReg3|Control|Debug|Test)) ? 1 : 2;
+ /* If there is an extension opcode to put here, the register number
+ must be put into the regmem field. */
+ if (t->extension_opcode != None)
+ i.rm.regmem = i.regs[o]->reg_num;
+ else i.rm.reg = i.regs[o]->reg_num;
+
+ /* Now, if no memory operand has set i.rm.mode = 0, 1, 2
+ we must set it to 3 to indicate this is a register operand
+ int the regmem field */
+ if (! i.mem_operands) i.rm.mode = 3;
+ }
+
+ /* Fill in i.rm.reg field with extension opcode (if any). */
+ if (t->extension_opcode != None)
+ i.rm.reg = t->extension_opcode;
+ }
+ }
+ }
+ }
+
+ /* Handle conversion of 'int $3' --> special int3 insn. */
+ if (t->base_opcode == INT_OPCODE && i.imms[0]->X_add_number == 3) {
+ t->base_opcode = INT3_OPCODE;
+ i.imm_operands = 0;
+ }
+
+ /* We are ready to output the insn. */
+ {
+ register char * p;
+
+ /* Output jumps. */
+ if (t->opcode_modifier & Jump) {
+ int n = i.disps[0]->X_add_number;
+
+ switch (i.disps[0]->X_seg) {
+ case SEG_ABSOLUTE:
+ if (fits_in_signed_byte(n)) {
+ p = frag_more (2);
+ p[0] = t->base_opcode;
+ p[1] = n;
+#if 0 /* leave out 16 bit jumps - pace */
+ } else if (fits_in_signed_word(n)) {
+ p = frag_more (4);
+ p[0] = WORD_PREFIX_OPCODE;
+ p[1] = t->base_opcode;
+ md_number_to_chars (&p[2], n, 2);
+#endif
+ } else { /* It's an absolute dword displacement. */
+ if (t->base_opcode == JUMP_PC_RELATIVE) { /* pace */
+ /* unconditional jump */
+ p = frag_more (5);
+ p[0] = 0xe9;
+ md_number_to_chars (&p[1], n, 4);
+ } else {
+ /* conditional jump */
+ p = frag_more (6);
+ p[0] = TWO_BYTE_OPCODE_ESCAPE;
+ p[1] = t->base_opcode + 0x10;
+ md_number_to_chars (&p[2], n, 4);
+ }
+ }
+ break;
+ default:
+ /* It's a symbol; end frag & setup for relax.
+ Make sure there are 6 chars left in the current frag; if not
+ we'll have to start a new one. */
+ /* I caught it failing with obstack_room == 6,
+ so I changed to <= pace */
+ if (obstack_room (&frags) <= 6) {
+ frag_wane(frag_now);
+ frag_new (0);
+ }
+ p = frag_more (1);
+ p[0] = t->base_opcode;
+ frag_var (rs_machine_dependent,
+ 6, /* 2 opcode/prefix + 4 displacement */
+ 1,
+ ((unsigned char) *p == JUMP_PC_RELATIVE
+ ? ENCODE_RELAX_STATE (UNCOND_JUMP, BYTE)
+ : ENCODE_RELAX_STATE (COND_JUMP, BYTE)),
+ i.disps[0]->X_add_symbol,
+ n, p);
+/*
+ * XXX - what do we do about jmp x@PLT ??
+ * kludged in md_estimate_size_before_relax() below
+ */
+ break;
+ }
+ } else if (t->opcode_modifier & (JumpByte|JumpDword)) {
+ int size = (t->opcode_modifier & JumpByte) ? 1 : 4;
+ int n = i.disps[0]->X_add_number;
+
+ if (fits_in_unsigned_byte(t->base_opcode)) {
+ FRAG_APPEND_1_CHAR (t->base_opcode);
+ } else {
+ p = frag_more (2); /* opcode can be at most two bytes */
+ /* put out high byte first: can't use md_number_to_chars! */
+ *p++ = (t->base_opcode >> 8) & 0xff;
+ *p = t->base_opcode & 0xff;
+ }
+
+ p = frag_more (size);
+ switch (i.disps[0]->X_seg) {
+ case SEG_ABSOLUTE:
+ md_number_to_chars (p, n, size);
+ if (size == 1 && ! fits_in_signed_byte(n)) {
+ as_bad("loop/jecx only takes byte displacement; %d shortened to %d",
+ n, *p);
+ }
+ break;
+ default:
+ fix_new (frag_now, p - frag_now->fr_literal, size,
+ i.disps[0]->X_add_symbol, i.disps[0]->X_subtract_symbol,
+ i.disps[0]->X_add_number, 1, i.disp_reloc[0], i.disps[0]->X_got_symbol);
+ break;
+ }
+ } else if (t->opcode_modifier & JumpInterSegment) {
+ p = frag_more (1 + 2 + 4); /* 1 opcode; 2 segment; 4 offset */
+ p[0] = t->base_opcode;
+ if (i.imms[1]->X_seg == SEG_ABSOLUTE)
+ md_number_to_chars (p + 1, i.imms[1]->X_add_number, 4);
+ else
+ fix_new (frag_now, p + 1 - frag_now->fr_literal, 4,
+ i.imms[1]->X_add_symbol,
+ i.imms[1]->X_subtract_symbol,
+ i.imms[1]->X_add_number, 0, NO_RELOC, i.imms[1]->X_got_symbol);
+ if (i.imms[0]->X_seg != SEG_ABSOLUTE)
+ as_bad("can't handle non absolute segment in long call/jmp");
+ md_number_to_chars (p + 5, i.imms[0]->X_add_number, 2);
+ } else {
+ /* Output normal instructions here. */
+ unsigned char *q;
+#ifdef PIC
+ /*
+ * Remember # of opcode bytes to put in pcrel_adjust
+ * for use in _GLOBAL_OFFSET_TABLE_ expressions.
+ */
+ int nopbytes = 0;
+#endif
+
+ /* First the prefix bytes. */
+ for (q = i.prefix; q < i.prefix + i.prefixes; q++) {
+ p = frag_more (1);
+ nopbytes += 1;
+ md_number_to_chars (p, (unsigned int) *q, 1);
+ }
+
+ /* Now the opcode; be careful about word order here! */
+ if (fits_in_unsigned_byte(t->base_opcode)) {
+ nopbytes += 1;
+ FRAG_APPEND_1_CHAR (t->base_opcode);
+ } else if (fits_in_unsigned_word(t->base_opcode)) {
+ p = frag_more (2);
+ nopbytes += 2;
+ /* put out high byte first: can't use md_number_to_chars! */
+ *p++ = (t->base_opcode >> 8) & 0xff;
+ *p = t->base_opcode & 0xff;
+ } else { /* opcode is either 3 or 4 bytes */
+ if (t->base_opcode & 0xff000000) {
+ p = frag_more (4);
+ nopbytes += 4;
+ *p++ = (t->base_opcode >> 24) & 0xff;
+ } else {
+ p = frag_more (3);
+ nopbytes += 3;
+ }
+ *p++ = (t->base_opcode >> 16) & 0xff;
+ *p++ = (t->base_opcode >> 8) & 0xff;
+ *p = (t->base_opcode ) & 0xff;
+ }
+
+ /* Now the modrm byte and base index byte (if present). */
+ if (t->opcode_modifier & Modrm) {
+ p = frag_more (1);
+ nopbytes += 1;
+ /* md_number_to_chars (p, i.rm, 1); */
+ md_number_to_chars (p, (i.rm.regmem<<0 | i.rm.reg<<3 | i.rm.mode<<6), 1);
+ /* If i.rm.regmem == ESP (4) && i.rm.mode != Mode 3 (Register mode)
+ ==> need second modrm byte. */
+ if (i.rm.regmem == ESCAPE_TO_TWO_BYTE_ADDRESSING && i.rm.mode != 3) {
+ p = frag_more (1);
+ nopbytes += 1;
+ /* md_number_to_chars (p, i.bi, 1); */
+ md_number_to_chars (p,(i.bi.base<<0 | i.bi.index<<3 | i.bi.scale<<6), 1);
+ }
+ }
+
+ if (i.disp_operands) {
+ register unsigned int n;
+
+ for (n = 0; n < i.operands; n++) {
+ if (i.disps[n]) {
+ if (i.disps[n]->X_seg == SEG_ABSOLUTE) {
+ if (i.types[n] & (Disp8|Abs8)) {
+ p = frag_more (1);
+ md_number_to_chars (p, i.disps[n]->X_add_number, 1);
+ } else if (i.types[n] & (Disp16|Abs16)) {
+ p = frag_more (2);
+ md_number_to_chars (p, i.disps[n]->X_add_number, 2);
+ } else { /* Disp32|Abs32 */
+ p = frag_more (4);
+ md_number_to_chars (p, i.disps[n]->X_add_number, 4);
+ }
+ } else { /* not SEG_ABSOLUTE */
+ /* need a 32-bit fixup (don't support 8bit non-absolute disps) */
+
+ fixS *fixP;
+ p = frag_more (4);
+ fixP = fix_new (frag_now, p - frag_now->fr_literal, 4,
+ i.disps[n]->X_add_symbol, i.disps[n]->X_subtract_symbol,
+ i.disps[n]->X_add_number, 0, i.disp_reloc[n], i.disps[n]->X_got_symbol);
+#ifdef PIC
+ if (i.disps[n]->X_got_symbol) {
+ fixP->fx_pcrel_adjust = nopbytes;
+ }
+#endif
+ }
+ }
+ }
+ } /* end displacement output */
+
+ /* output immediate */
+ if (i.imm_operands) {
+ register unsigned int n;
+
+ for (n = 0; n < i.operands; n++) {
+ if (i.imms[n]) {
+ if (i.imms[n]->X_seg == SEG_ABSOLUTE) {
+ if (i.types[n] & (Imm8|Imm8S)) {
+ p = frag_more (1);
+ md_number_to_chars (p, i.imms[n]->X_add_number, 1);
+ } else if (i.types[n] & Imm16) {
+ p = frag_more (2);
+ md_number_to_chars (p, i.imms[n]->X_add_number, 2);
+ } else {
+ p = frag_more (4);
+ md_number_to_chars (p, i.imms[n]->X_add_number, 4);
+ }
+ } else { /* not SEG_ABSOLUTE */
+ /* need a 32-bit fixup (don't support 8bit non-absolute ims) */
+ /* try to support other sizes ... */
+ fixS *fixP;
+ int size;
+ if (i.types[n] & (Imm8|Imm8S))
+ size = 1;
+ else if (i.types[n] & Imm16)
+ size = 2;
+ else
+ size = 4;
+ p = frag_more (size);
+ fixP = fix_new (frag_now, p - frag_now->fr_literal, size,
+ i.imms[n]->X_add_symbol, i.imms[n]->X_subtract_symbol,
+ i.imms[n]->X_add_number, 0, NO_RELOC, i.imms[n]->X_got_symbol);
+#ifdef PIC
+ if (i.imms[n]->X_got_symbol) {
+ fixP->fx_pcrel_adjust = nopbytes;
+ }
+#endif
+ }
+ }
+ }
+ } /* end immediate output */
+ }
+
+#ifdef DEBUG386
+ if (flagseen['D']) {
+ pi (line, &i);
+ }
+#endif /* DEBUG386 */
+
+ }
+ return;
+}
+
+/* Parse OPERAND_STRING into the i386_insn structure I. Returns non-zero
+ on error. */
+
+static int i386_operand (operand_string)
+char *operand_string;
+{
+ register char *op_string = operand_string;
+
+ /* Address of '\0' at end of operand_string. */
+ char * end_of_operand_string = operand_string + strlen(operand_string);
+
+ /* Start and end of displacement string expression (if found). */
+ char *displacement_string_start = NULL;
+ char *displacement_string_end = NULL;
+
+ /* We check for an absolute prefix (differentiating,
+ for example, 'jmp pc_relative_label' from 'jmp *absolute_label'. */
+ if (*op_string == ABSOLUTE_PREFIX) {
+ op_string++;
+ i.types[this_operand] |= JumpAbsolute;
+ }
+
+ /* Check if operand is a register. */
+ if (*op_string == REGISTER_PREFIX) {
+ register reg_entry *r;
+ if (!(r = parse_register (op_string))) {
+ as_bad("bad register name ('%s')", op_string);
+ return 0;
+ }
+ /* Check for segment override, rather than segment register by
+ searching for ':' after %<x>s where <x> = s, c, d, e, f, g. */
+ if ((r->reg_type & (SReg2|SReg3)) && op_string[3] == ':') {
+ switch (r->reg_num) {
+ case 0:
+ i.seg = (seg_entry *) &es; break;
+ case 1:
+ i.seg = (seg_entry *) &cs; break;
+ case 2:
+ i.seg = (seg_entry *) &ss; break;
+ case 3:
+ i.seg = (seg_entry *) &ds; break;
+ case 4:
+ i.seg = (seg_entry *) &fs; break;
+ case 5:
+ i.seg = (seg_entry *) &gs; break;
+ }
+ op_string += 4; /* skip % <x> s : */
+ operand_string = op_string; /* Pretend given string starts here. */
+ if (!is_digit_char(*op_string) && !is_identifier_char(*op_string)
+ && *op_string != '(' && *op_string != ABSOLUTE_PREFIX) {
+ as_bad("bad memory operand after segment override");
+ return 0;
+ }
+ /* Handle case of %es:*foo. */
+ if (*op_string == ABSOLUTE_PREFIX) {
+ op_string++;
+ i.types[this_operand] |= JumpAbsolute;
+ }
+ goto do_memory_reference;
+ }
+ i.types[this_operand] |= r->reg_type;
+ i.regs[this_operand] = r;
+ i.reg_operands++;
+ } else if (*op_string == IMMEDIATE_PREFIX) { /* ... or an immediate */
+ char *save_input_line_pointer;
+ segT exp_seg = SEG_GOOF;
+ expressionS *exp;
+
+ if (i.imm_operands == MAX_IMMEDIATE_OPERANDS) {
+ as_bad("only 1 or 2 immediate operands are allowed");
+ return 0;
+ }
+
+ exp = &im_expressions[i.imm_operands++];
+ i.imms[this_operand] = exp;
+ save_input_line_pointer = input_line_pointer;
+ /* must advance op_string! */
+ input_line_pointer = ++op_string;
+
+ exp_seg = expression(exp);
+ input_line_pointer = save_input_line_pointer;
+
+ switch (exp_seg) {
+ case SEG_ABSENT: /* missing or bad expr becomes absolute 0 */
+ as_bad("missing or invalid immediate expression '%s' taken as 0",
+ operand_string);
+ exp->X_seg = SEG_ABSOLUTE;
+ exp->X_add_number = 0;
+ exp->X_add_symbol = (symbolS *) 0;
+ exp->X_subtract_symbol = (symbolS *) 0;
+ i.types[this_operand] |= Imm;
+ break;
+ case SEG_ABSOLUTE:
+ i.types[this_operand] |= smallest_imm_type(exp->X_add_number);
+ break;
+ case SEG_TEXT: case SEG_DATA: case SEG_BSS: case SEG_UNKNOWN:
+ case SEG_DIFFERENCE:
+ i.types[this_operand] |= Imm32; /* this is an address ==> 32bit */
+ break;
+ default:
+ seg_unimplemented:
+ as_bad("Unimplemented segment type %d in parse_operand", exp_seg);
+ return 0;
+ }
+ /* shorten this type of this operand if the instruction wants
+ * fewer bits than are present in the immediate. The bit field
+ * code can put out 'andb $0xffffff, %al', for example. pace
+ * also 'movw $foo,(%eax)'
+ */
+ switch (i.suffix) {
+ case WORD_OPCODE_SUFFIX:
+ i.types[this_operand] |= Imm16;
+ break;
+ case BYTE_OPCODE_SUFFIX:
+ i.types[this_operand] |= Imm16 | Imm8 | Imm8S;
+ break;
+ }
+ } else if (is_digit_char(*op_string) || is_identifier_char(*op_string)
+ || *op_string == '(') {
+ /* This is a memory reference of some sort. */
+ register char * base_string;
+ unsigned int found_base_index_form;
+
+ do_memory_reference:
+ if (i.mem_operands == MAX_MEMORY_OPERANDS) {
+ as_bad("more than 1 memory reference in instruction");
+ return 0;
+ }
+ i.mem_operands++;
+
+ /* Determine type of memory operand from opcode_suffix;
+ no opcode suffix implies general memory references. */
+ switch (i.suffix) {
+ case BYTE_OPCODE_SUFFIX:
+ i.types[this_operand] |= Mem8;
+ break;
+ case WORD_OPCODE_SUFFIX:
+ i.types[this_operand] |= Mem16;
+ break;
+ case DWORD_OPCODE_SUFFIX:
+ default:
+ i.types[this_operand] |= Mem32;
+ }
+
+ /* Check for base index form. We detect the base index form by
+ looking for an ')' at the end of the operand, searching
+ for the '(' matching it, and finding a REGISTER_PREFIX or ','
+ after it. */
+ base_string = end_of_operand_string - 1;
+ found_base_index_form = 0;
+ if (*base_string == ')') {
+ unsigned int parens_balenced = 1;
+ /* We've already checked that the number of left & right ()'s are equal,
+ so this loop will not be infinite. */
+ do {
+ base_string--;
+ if (*base_string == ')') parens_balenced++;
+ if (*base_string == '(') parens_balenced--;
+ } while (parens_balenced);
+ base_string++; /* Skip past '('. */
+ if (*base_string == REGISTER_PREFIX || *base_string == ',')
+ found_base_index_form = 1;
+ }
+
+ /* If we can't parse a base index register expression, we've found
+ a pure displacement expression. We set up displacement_string_start
+ and displacement_string_end for the code below. */
+ if (! found_base_index_form) {
+ displacement_string_start = op_string;
+ displacement_string_end = end_of_operand_string;
+ } else {
+ char *base_reg_name, *index_reg_name, *num_string;
+ int num;
+
+ i.types[this_operand] |= BaseIndex;
+
+ /* If there is a displacement set-up for it to be parsed later. */
+ if (base_string != op_string + 1) {
+ displacement_string_start = op_string;
+ displacement_string_end = base_string - 1;
+ }
+
+ /* Find base register (if any). */
+ if (*base_string != ',') {
+ base_reg_name = base_string++;
+ /* skip past register name & parse it */
+ while (isalpha(*base_string)) base_string++;
+ if (base_string == base_reg_name+1) {
+ as_bad("can't find base register name after '(%c'",
+ REGISTER_PREFIX);
+ return 0;
+ }
+ END_STRING_AND_SAVE (base_string);
+ if (! (i.base_reg = parse_register (base_reg_name))) {
+ as_bad("bad base register name ('%s')", base_reg_name);
+ return 0;
+ }
+ RESTORE_END_STRING (base_string);
+ }
+
+ /* Now check seperator; must be ',' ==> index reg
+ OR num ==> no index reg. just scale factor
+ OR ')' ==> end. (scale factor = 1) */
+ if (*base_string != ',' && *base_string != ')') {
+ as_bad("expecting ',' or ')' after base register in `%s'",
+ operand_string);
+ return 0;
+ }
+
+ /* There may index reg here; and there may be a scale factor. */
+ if (*base_string == ',' && *(base_string+1) == REGISTER_PREFIX) {
+ index_reg_name = ++base_string;
+ while (isalpha(*++base_string));
+ END_STRING_AND_SAVE (base_string);
+ if (! (i.index_reg = parse_register(index_reg_name))) {
+ as_bad("bad index register name ('%s')", index_reg_name);
+ return 0;
+ }
+ RESTORE_END_STRING (base_string);
+ }
+
+ /* Check for scale factor. */
+ if (*base_string == ',' && isdigit(*(base_string+1))) {
+ num_string = ++base_string;
+ while (is_digit_char(*base_string)) base_string++;
+ if (base_string == num_string) {
+ as_bad("can't find a scale factor after ','");
+ return 0;
+ }
+ END_STRING_AND_SAVE (base_string);
+ /* We've got a scale factor. */
+ if (! sscanf (num_string, "%d", &num)) {
+ as_bad("can't parse scale factor from '%s'", num_string);
+ return 0;
+ }
+ RESTORE_END_STRING (base_string);
+ switch (num) { /* must be 1 digit scale */
+ case 1: i.log2_scale_factor = 0; break;
+ case 2: i.log2_scale_factor = 1; break;
+ case 4: i.log2_scale_factor = 2; break;
+ case 8: i.log2_scale_factor = 3; break;
+ default:
+ as_bad("expecting scale factor of 1, 2, 4, 8; got %d", num);
+ return 0;
+ }
+ } else {
+ if (! i.index_reg && *base_string == ',') {
+ as_bad("expecting index register or scale factor after ','; got '%c'",
+ *(base_string+1));
+ return 0;
+ }
+ }
+ }
+
+ /* If there's an expression begining the operand, parse it,
+ assuming displacement_string_start and displacement_string_end
+ are meaningful. */
+ if (displacement_string_start) {
+ register expressionS *exp;
+ segT exp_seg = SEG_GOOF;
+ char *save_input_line_pointer;
+ exp = &disp_expressions[i.disp_operands];
+ i.disps[this_operand] = exp;
+ i.disp_reloc[this_operand] = NO_RELOC;
+ i.disp_operands++;
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = displacement_string_start;
+ END_STRING_AND_SAVE (displacement_string_end);
+#ifdef PIC
+ {
+ /*
+ * We can have operands of the form
+ * <symbol>@GOTOFF+<nnn>
+ * Take the easy way out here and copy everything
+ * into a temporary buffer...
+ */
+ register char *cp;
+ if (flagseen['k'] &&
+ (cp = strchr(input_line_pointer,'@'))) {
+ char tmpbuf[BUFSIZ];
+
+ if (strncmp(cp+1, "PLT", 3) == 0) {
+ i.disp_reloc[this_operand] = RELOC_JMP_TBL;
+ *cp = '\0';
+ strcpy(tmpbuf, input_line_pointer);
+ strcat(tmpbuf, cp+1+3);
+ *cp = '@';
+ } else if (strncmp(cp+1, "GOTOFF", 6) == 0) {
+ i.disp_reloc[this_operand] = RELOC_GOTOFF;
+ *cp = '\0';
+ strcpy(tmpbuf, input_line_pointer);
+ strcat(tmpbuf, cp+1+6);
+ *cp = '@';
+ } else if (strncmp(cp+1, "GOT", 3) == 0) {
+ i.disp_reloc[this_operand] = RELOC_GOT;
+ *cp = '\0';
+ strcpy(tmpbuf, input_line_pointer);
+ strcat(tmpbuf, cp+1+3);
+ *cp = '@';
+ } else
+ as_bad("Bad reloc specifier '%s' in expression", cp+1);
+ input_line_pointer = tmpbuf;
+ }
+ }
+#endif
+ exp_seg = expression(exp);
+#ifdef PIC
+ if (i.disp_reloc[this_operand] == RELOC_GOTOFF)
+ exp->X_add_symbol->sy_forceout = 1;
+#endif
+ if (*input_line_pointer)
+ as_bad("Ignoring junk '%s' after expression",input_line_pointer);
+ RESTORE_END_STRING (displacement_string_end);
+ input_line_pointer = save_input_line_pointer;
+ switch (exp_seg) {
+ case SEG_ABSENT:
+ /* missing expr becomes absolute 0 */
+ as_bad("missing or invalid displacement '%s' taken as 0",
+ operand_string);
+ i.types[this_operand] |= (Disp|Abs);
+ exp->X_seg = SEG_ABSOLUTE;
+ exp->X_add_number = 0;
+ exp->X_add_symbol = (symbolS *) 0;
+ exp->X_subtract_symbol = (symbolS *) 0;
+ break;
+ case SEG_ABSOLUTE:
+ i.types[this_operand] |= SMALLEST_DISP_TYPE (exp->X_add_number);
+ break;
+ case SEG_TEXT: case SEG_DATA: case SEG_BSS:
+ case SEG_UNKNOWN: /* must be 32 bit displacement (i.e. address) */
+ i.types[this_operand] |= Disp32;
+ break;
+ default:
+ goto seg_unimplemented;
+ }
+ }
+
+ /* Make sure the memory operand we've been dealt is valid. */
+ if (i.base_reg && i.index_reg &&
+ ! (i.base_reg->reg_type & i.index_reg->reg_type & Reg)) {
+ as_bad("register size mismatch in (base,index,scale) expression");
+ return 0;
+ }
+ /*
+ * special case for (%dx) while doing input/output op
+ */
+ if ((i.base_reg &&
+ (i.base_reg->reg_type == (Reg16|InOutPortReg)) &&
+ (i.index_reg == 0)))
+ return 1;
+ if ((i.base_reg && (i.base_reg->reg_type & Reg32) == 0) ||
+ (i.index_reg && (i.index_reg->reg_type & Reg32) == 0)) {
+ as_bad("base/index register must be 32 bit register");
+ return 0;
+ }
+ if (i.index_reg && i.index_reg == esp) {
+ as_bad("%s may not be used as an index register", esp->reg_name);
+ return 0;
+ }
+ } else { /* it's not a memory operand; argh! */
+ as_bad("invalid char %s begining %s operand '%s'",
+ output_invalid(*op_string), ordinal_names[this_operand],
+ op_string);
+ return 0;
+ }
+ return 1; /* normal return */
+}
+
+/*
+ * md_estimate_size_before_relax()
+ *
+ * Called just before relax().
+ * Any symbol that is now undefined will not become defined.
+ * Return the correct fr_subtype in the frag.
+ * Return the initial "guess for fr_var" to caller.
+ * The guess for fr_var is ACTUALLY the growth beyond fr_fix.
+ * Whatever we do to grow fr_fix or fr_var contributes to our returned value.
+ * Although it may not be explicit in the frag, pretend fr_var starts with a
+ * 0 value.
+ */
+int
+ md_estimate_size_before_relax (fragP, segment)
+register fragS * fragP;
+register segT segment;
+{
+ register unsigned char * opcode;
+ register int old_fr_fix;
+
+ old_fr_fix = fragP->fr_fix;
+ opcode = (unsigned char *) fragP->fr_opcode;
+ /* We've already got fragP->fr_subtype right; all we have to do is check
+ for un-relaxable symbols. */
+ if (S_GET_SEGMENT(fragP->fr_symbol) != segment) {
+ /* symbol is undefined in this segment */
+ switch (opcode[0]) {
+ case JUMP_PC_RELATIVE: /* make jmp (0xeb) a dword displacement jump */
+ opcode[0] = 0xe9; /* dword disp jmp */
+ fragP->fr_fix += 4;
+ fix_new (fragP, old_fr_fix, 4,
+ fragP->fr_symbol,
+ (symbolS *) 0,
+ fragP->fr_offset, 1,
+#ifdef PIC
+/* XXX - oops, the JMP_TBL relocation info should have percolated through
+ * here, define a field in frag to this?
+ */
+ (flagseen['k'] && S_GET_SEGMENT(fragP->fr_symbol) == SEG_UNKNOWN)?
+ RELOC_JMP_TBL :
+#endif
+ NO_RELOC, (symbolS *)0);
+ break;
+
+ default:
+ /* This changes the byte-displacement jump 0x7N -->
+ the dword-displacement jump 0x0f8N */
+ opcode[1] = opcode[0] + 0x10;
+ opcode[0] = TWO_BYTE_OPCODE_ESCAPE; /* two-byte escape */
+ fragP->fr_fix += 1 + 4; /* we've added an opcode byte */
+ fix_new (fragP, old_fr_fix + 1, 4,
+ fragP->fr_symbol,
+ (symbolS *) 0,
+ fragP->fr_offset, 1,
+#ifdef PIC
+/*XXX*/ (flagseen['k'] && S_GET_SEGMENT(fragP->fr_symbol) == SEG_UNKNOWN)?
+ RELOC_JMP_TBL :
+#endif
+ NO_RELOC, (symbolS *)0);
+ break;
+ }
+ frag_wane (fragP);
+ }
+ return (fragP->fr_var + fragP->fr_fix - old_fr_fix);
+} /* md_estimate_size_before_relax() */
+
+/*
+ * md_convert_frag();
+ *
+ * Called after relax() is finished.
+ * In: Address of frag.
+ * fr_type == rs_machine_dependent.
+ * fr_subtype is what the address relaxed to.
+ *
+ * Out: Any fixSs and constants are set up.
+ * Caller will turn frag into a ".space 0".
+ */
+void
+ md_convert_frag (headers, fragP)
+object_headers *headers;
+register fragS * fragP;
+{
+ register unsigned char *opcode;
+ unsigned char *where_to_put_displacement = NULL;
+ unsigned int target_address;
+ unsigned int opcode_address;
+ unsigned int extension = 0;
+ int displacement_from_opcode_start;
+
+ opcode = (unsigned char *) fragP->fr_opcode;
+
+ /* Address we want to reach in file space. */
+ target_address = S_GET_VALUE(fragP->fr_symbol) + fragP->fr_offset;
+
+ /* Address opcode resides at in file space. */
+ opcode_address = fragP->fr_address + fragP->fr_fix;
+
+ /* Displacement from opcode start to fill into instruction. */
+ displacement_from_opcode_start = target_address - opcode_address;
+
+ switch (fragP->fr_subtype) {
+ case ENCODE_RELAX_STATE (COND_JUMP, BYTE):
+ case ENCODE_RELAX_STATE (UNCOND_JUMP, BYTE):
+ /* don't have to change opcode */
+ extension = 1; /* 1 opcode + 1 displacement */
+ where_to_put_displacement = &opcode[1];
+ break;
+
+ case ENCODE_RELAX_STATE (COND_JUMP, WORD):
+ opcode[1] = TWO_BYTE_OPCODE_ESCAPE;
+ opcode[2] = opcode[0] + 0x10;
+ opcode[0] = WORD_PREFIX_OPCODE;
+ extension = 4; /* 3 opcode + 2 displacement */
+ where_to_put_displacement = &opcode[3];
+ break;
+
+ case ENCODE_RELAX_STATE (UNCOND_JUMP, WORD):
+ opcode[1] = 0xe9;
+ opcode[0] = WORD_PREFIX_OPCODE;
+ extension = 3; /* 2 opcode + 2 displacement */
+ where_to_put_displacement = &opcode[2];
+ break;
+
+ case ENCODE_RELAX_STATE (COND_JUMP, DWORD):
+ opcode[1] = opcode[0] + 0x10;
+ opcode[0] = TWO_BYTE_OPCODE_ESCAPE;
+ extension = 5; /* 2 opcode + 4 displacement */
+ where_to_put_displacement = &opcode[2];
+ break;
+
+ case ENCODE_RELAX_STATE (UNCOND_JUMP, DWORD):
+ opcode[0] = 0xe9;
+ extension = 4; /* 1 opcode + 4 displacement */
+ where_to_put_displacement = &opcode[1];
+ break;
+
+ default:
+ BAD_CASE(fragP->fr_subtype);
+ break;
+}
+ /* now put displacement after opcode */
+ md_number_to_chars ((char *) where_to_put_displacement,
+ displacement_from_opcode_start - extension,
+ SIZE_FROM_RELAX_STATE (fragP->fr_subtype));
+ fragP->fr_fix += extension;
+}
+
+
+int md_short_jump_size = 2; /* size of byte displacement jmp */
+int md_long_jump_size = 5; /* size of dword displacement jmp */
+int md_reloc_size = 8; /* Size of relocation record */
+
+void md_create_short_jump(ptr, from_addr, to_addr, frag, to_symbol)
+char *ptr;
+long from_addr, to_addr;
+fragS *frag;
+symbolS *to_symbol;
+{
+ long offset;
+
+ offset = to_addr - (from_addr + 2);
+ md_number_to_chars (ptr, (long) 0xeb, 1); /* opcode for byte-disp jump */
+ md_number_to_chars (ptr + 1, offset, 1);
+}
+
+void md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol)
+char *ptr;
+long from_addr, to_addr;
+fragS *frag;
+symbolS *to_symbol;
+{
+ long offset;
+
+ if (flagseen['m']) {
+ offset = to_addr - S_GET_VALUE(to_symbol);
+ md_number_to_chars (ptr, 0xe9, 1); /* opcode for long jmp */
+ md_number_to_chars (ptr + 1, offset, 4);
+ fix_new (frag, (ptr+1) - frag->fr_literal, 4,
+ to_symbol, (symbolS *) 0, (long) 0, 0, NO_RELOC, (symbolS *)0);
+ } else {
+ offset = to_addr - (from_addr + 5);
+ md_number_to_chars(ptr, (long) 0xe9, 1);
+ md_number_to_chars(ptr + 1, offset, 4);
+ }
+}
+
+int
+ md_parse_option(argP,cntP,vecP)
+char **argP;
+int *cntP;
+char ***vecP;
+{
+#ifdef PIC
+ if (argP && *argP && **argP == 'k') {
+#if 00
+ char *tmp = xmalloc(3+1+strlen(operand_special_chars));
+ strcpy(tmp, operand_special_chars);
+ strcat(tmp, "@[]");
+ operand_special_chars = tmp;
+#endif
+ /* Allow `[', `]' in expressions and `@' in operands */
+ operand_chars['@'] = '@';
+ operand_chars['['] = '[';
+ operand_chars[']'] = ']';
+
+ /* Disallow `[' as a name beginner */
+ lex_type['['] = 0;
+
+ /* Predefine GOT symbol */
+ GOT_symbol = symbol_find_or_make("__GLOBAL_OFFSET_TABLE_");
+ }
+#endif
+ return 1;
+}
+
+ /* write out in little endian. */
+void /* Knows about order of bytes in address. */
+ md_number_to_chars(con, value, nbytes)
+char con[]; /* Return 'nbytes' of chars here. */
+long value; /* The value of the bits. */
+int nbytes; /* Number of bytes in the output. */
+{
+ register char * p = con;
+
+ switch (nbytes) {
+ case 1:
+ p[0] = value & 0xff;
+ break;
+ case 2:
+ p[0] = value & 0xff;
+ p[1] = (value >> 8) & 0xff;
+ break;
+ case 4:
+ p[0] = value & 0xff;
+ p[1] = (value>>8) & 0xff;
+ p[2] = (value>>16) & 0xff;
+ p[3] = (value>>24) & 0xff;
+ break;
+ default:
+ BAD_CASE (nbytes);
+ }
+}
+
+
+/* Apply a fixup (fixS) to segment data, once it has been determined
+ by our caller that we have all the info we need to fix it up.
+
+ On the 386, immediates, displacements, and data pointers are all in
+ the same (little-endian) format, so we don't need to care about which
+ we are handling. */
+
+void
+ md_apply_fix (fixP, value)
+fixS * fixP; /* The fix we're to put in */
+long value; /* The value of the bits. */
+{
+ register char * p = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ switch (fixP->fx_size) {
+ case 1:
+ *p = value;
+ break;
+ case 2:
+ *p++ = value;
+ *p = (value>>8);
+ break;
+ case 4:
+ *p++ = value;
+ *p++ = (value>>8);
+ *p++ = (value>>16);
+ *p = (value>>24);
+ break;
+ default:
+ BAD_CASE (fixP->fx_size);
+ }
+}
+
+long /* Knows about the byte order in a word. */
+ md_chars_to_number (con, nbytes)
+unsigned char con[]; /* Low order byte 1st. */
+int nbytes; /* Number of bytes in the input. */
+{
+ long retval;
+ for (retval=0, con+=nbytes-1; nbytes--; con--)
+ {
+ retval <<= BITS_PER_CHAR;
+ retval |= *con;
+ }
+ return retval;
+}
+
+/* Not needed for coff since relocation structure does not
+ contain bitfields. */
+#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
+#ifdef comment
+/* Output relocation information in the target's format. */
+void
+ md_ri_to_chars(the_bytes, ri)
+char *the_bytes;
+struct reloc_info_generic *ri;
+{
+ /* this is easy */
+ md_number_to_chars(the_bytes, ri->r_address, 4);
+ /* now the fun stuff */
+ the_bytes[6] = (ri->r_symbolnum >> 16) & 0x0ff;
+ the_bytes[5] = (ri->r_symbolnum >> 8) & 0x0ff;
+ the_bytes[4] = ri->r_symbolnum & 0x0ff;
+ the_bytes[7] = (((ri->r_extern << 3) & 0x08) | ((ri->r_length << 1) & 0x06) |
+ ((ri->r_pcrel << 0) & 0x01)) & 0x0F;
+}
+#endif /* comment */
+
+void tc_aout_fix_to_chars(where, fixP, segment_address_in_file)
+char *where;
+fixS *fixP;
+relax_addressT segment_address_in_file;
+{
+ /*
+ * In: length of relocation (or of address) in chars: 1, 2 or 4.
+ * Out: GNU LD relocation length code: 0, 1, or 2.
+ */
+
+ static unsigned char nbytes_r_length[] = { 42, 0, 1, 42, 2 };
+ long r_symbolnum;
+
+ know(fixP->fx_addsy != NULL);
+
+ md_number_to_chars(where,
+ fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file,
+ 4);
+
+ r_symbolnum = (S_IS_DEFINED(fixP->fx_addsy)
+ ? S_GET_TYPE(fixP->fx_addsy)
+ : fixP->fx_addsy->sy_number);
+
+#ifdef PIC
+ {
+ int extra_bits = 0;
+ int extrn_bit = !S_IS_DEFINED(fixP->fx_addsy);
+
+ switch (fixP->fx_r_type) {
+ case NO_RELOC:
+ break;
+ case RELOC_32:
+ if (!flagseen['k'] || !S_IS_EXTERNAL(fixP->fx_addsy))
+ break;
+ r_symbolnum = fixP->fx_addsy->sy_number;
+ extrn_bit = 1;
+ break;
+ case RELOC_GOT:
+ extra_bits = (1 << 4) & 0x10; /* r_baserel */
+ r_symbolnum = fixP->fx_addsy->sy_number;
+ if (!extrn_bit && !S_IS_EXTERNAL(fixP->fx_addsy))
+ as_warn("GOT relocation burb: `%s' should be global",
+ S_GET_NAME(fixP->fx_addsy));
+ extrn_bit = 1;
+ break;
+ case RELOC_GOTOFF:
+ extra_bits = (1 << 4) & 0x10; /* r_baserel */
+ r_symbolnum = fixP->fx_addsy->sy_number;
+ if (extrn_bit || S_IS_EXTERNAL(fixP->fx_addsy))
+ as_warn("GOT relocation burb: `%s' should be static",
+ S_GET_NAME(fixP->fx_addsy));
+ break;
+ case RELOC_JMP_TBL:
+ extra_bits = (1 << 5) & 0x20; /* r_jmptable */
+ break;
+ case RELOC_RELATIVE:
+ /* consider using this bit (together with r_baserel) for
+ * GOTOFFs, so ld can check
+ */
+ as_fatal("relocation botch");
+ extra_bits = (1 << 6) & 0x40; /* r_relative */
+ break;
+ }
+ where[6] = (r_symbolnum >> 16) & 0x0ff;
+ where[5] = (r_symbolnum >> 8) & 0x0ff;
+ where[4] = r_symbolnum & 0x0ff;
+ where[7] = ( ((extrn_bit << 3) & 0x08)
+ | ((nbytes_r_length[fixP->fx_size] << 1) & 0x06)
+ | ((fixP->fx_pcrel << 0) & 0x01)
+ | (extra_bits)
+ );
+ }
+#else
+ where[6] = (r_symbolnum >> 16) & 0x0ff;
+ where[5] = (r_symbolnum >> 8) & 0x0ff;
+ where[4] = r_symbolnum & 0x0ff;
+ where[7] = ((((!S_IS_DEFINED(fixP->fx_addsy)) << 3) & 0x08)
+ | ((nbytes_r_length[fixP->fx_size] << 1) & 0x06)
+ | (((fixP->fx_pcrel << 0) & 0x01) & 0x0f));
+#endif
+
+ return;
+} /* tc_aout_fix_to_chars() */
+
+#endif /* OBJ_AOUT or OBJ_BOUT */
+
+
+#define MAX_LITTLENUMS 6
+
+/* Turn the string pointed to by litP into a floating point constant of type
+ type, and emit the appropriate bytes. The number of LITTLENUMS emitted
+ is stored in *sizeP. An error message is returned, or NULL on OK.
+ */
+char *
+ md_atof(type,litP,sizeP)
+char type;
+char *litP;
+int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+
+ switch (type) {
+ case 'f':
+ case 'F':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 5;
+ break;
+
+ default:
+ *sizeP=0;
+ return "Bad call to md_atof ()";
+ }
+ t = atof_ieee (input_line_pointer,type,words);
+ if (t)
+ input_line_pointer=t;
+
+ *sizeP = prec * sizeof(LITTLENUM_TYPE);
+ /* this loops outputs the LITTLENUMs in REVERSE order; in accord with
+ the bigendian 386 */
+ for (wordP = words + prec - 1;prec--;) {
+ md_number_to_chars (litP, (long) (*wordP--), sizeof(LITTLENUM_TYPE));
+ litP += sizeof(LITTLENUM_TYPE);
+ }
+ return ""; /* Someone should teach Dean about null pointers */
+}
+
+char output_invalid_buf[8];
+
+static char * output_invalid (c)
+char c;
+{
+ if (isprint(c)) sprintf (output_invalid_buf, "'%c'", c);
+ else sprintf (output_invalid_buf, "(0x%x)", (unsigned) c);
+ return output_invalid_buf;
+}
+
+static reg_entry *parse_register (reg_string)
+char *reg_string; /* reg_string starts *before* REGISTER_PREFIX */
+{
+ register char *s = reg_string;
+ register char *p;
+ char reg_name_given[MAX_REG_NAME_SIZE];
+
+ s++; /* skip REGISTER_PREFIX */
+ for (p = reg_name_given; is_register_char (*s); p++, s++) {
+ *p = register_chars[*s];
+ if (p >= reg_name_given + MAX_REG_NAME_SIZE)
+ return (reg_entry *) 0;
+ }
+ *p = '\0';
+ return (reg_entry *) hash_find (reg_hash, reg_name_given);
+}
+
+
+/* We have no need to default values of symbols. */
+
+/* ARGSUSED */
+symbolS *
+ md_undefined_symbol (name)
+char *name;
+{
+#ifdef PIC
+ /* HACK:
+ * Sun's ld expects __GLOBAL_OFFSET_TABLE_,
+ * gcc generates _GLOBAL_OFFSET_TABLE_
+ * should probably fix ld - new SVR4 style??
+ */
+ if (*name == '_' && *(name+1) == 'G' &&
+ strcmp(name, "_GLOBAL_OFFSET_TABLE_") == 0)
+ return symbol_find("__GLOBAL_OFFSET_TABLE_");
+#endif
+ return 0;
+}
+
+/* Parse an operand that is machine-specific.
+ We just return without modifying the expression if we have nothing
+ to do. */
+
+/* ARGSUSED */
+void
+ md_operand (expressionP)
+expressionS *expressionP;
+{
+}
+
+/* Round up a section size to the appropriate boundary. */
+long
+ md_section_align (segment, size)
+segT segment;
+long size;
+{
+ return size; /* Byte alignment is fine */
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the i386, they're relative to the address of the offset, plus
+ its size. (??? Is this right? FIXME-SOON!) */
+long
+ md_pcrel_from (fixP)
+fixS *fixP;
+{
+#ifdef PIC
+ /*
+ * _GLOBAL_OFFSET_TABLE_ refs are relative to the offset of the
+ * current instruction. fx_pcrel_adjust has been setup to account
+ * for the number of opcode bytes preceding the fixup location,
+ * it is zero for eg. .long pseudo-ops.
+ */
+ if (fixP->fx_gotsy)
+ return fixP->fx_where + fixP->fx_frag->fr_address - fixP->fx_pcrel_adjust;
+ else
+#endif
+ return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+ /* these were macros, but I don't trust macros that eval their
+ arguments more than once. Besides, gcc can static inline them.
+ xoxorich. */
+
+static unsigned long mode_from_disp_size(t)
+unsigned long t;
+{
+ return((t & (Disp8))
+ ? 1
+ : ((t & (Disp32)) ? 2 : 0));
+} /* mode_from_disp_size() */
+
+/* convert opcode suffix ('b' 'w' 'l' typically) into type specifyer */
+
+static unsigned long opcode_suffix_to_type(s)
+unsigned long s;
+{
+ return(s == BYTE_OPCODE_SUFFIX
+ ? Byte : (s == WORD_OPCODE_SUFFIX
+ ? Word : DWord));
+} /* opcode_suffix_to_type() */
+
+static int fits_in_signed_byte(num)
+long num;
+{
+ return((num >= -128) && (num <= 127));
+} /* fits_in_signed_byte() */
+
+static int fits_in_unsigned_byte(num)
+long num;
+{
+ return((num & 0xff) == num);
+} /* fits_in_unsigned_byte() */
+
+static int fits_in_unsigned_word(num)
+long num;
+{
+ return((num & 0xffff) == num);
+} /* fits_in_unsigned_word() */
+
+static int fits_in_signed_word(num)
+long num;
+{
+ return((-32768 <= num) && (num <= 32767));
+} /* fits_in_signed_word() */
+
+static int smallest_imm_type(num)
+long num;
+{
+ return((num == 1)
+ ? (Imm1|Imm8|Imm8S|Imm16|Imm32)
+ : (fits_in_signed_byte(num)
+ ? (Imm8S|Imm8|Imm16|Imm32)
+ : (fits_in_unsigned_byte(num)
+ ? (Imm8|Imm16|Imm32)
+ : ((fits_in_signed_word(num) || fits_in_unsigned_word(num))
+ ? (Imm16|Imm32)
+ : (Imm32)))));
+} /* smallest_imm_type() */
+
+static void s_bss()
+{
+ register int temp;
+
+ temp = get_absolute_expression ();
+ subseg_new (SEG_BSS, (subsegT)temp);
+ demand_empty_rest_of_line();
+}
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * End:
+ */
+
+/* end of tc-i386.c */
diff --git a/gnu/usr.bin/as/config/tc-i386.h b/gnu/usr.bin/as/config/tc-i386.h
new file mode 100644
index 0000000..b9ac3fe
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-i386.h
@@ -0,0 +1,254 @@
+/* tc-i386.h -- Header file for tc-i386.c
+ Copyright (C) 1989, 1992 Free Software Foundation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * $Id: tc-i386.h,v 1.1 1993/10/02 20:59:21 pk Exp $
+ */
+
+#ifndef TC_I386
+#define TC_I386 1
+
+#if 0
+#define AOUT_MACHTYPE 100
+#endif
+#define REVERSE_SORT_RELOCS
+
+#define LOCAL_LABELS_FB
+
+#define NO_LISTING
+
+#define tc_coff_symbol_emit_hook(a) ; /* not used */
+
+ /* Local labels starts with .L */
+ /* fixme-now: this is for testing against old gas */
+/* #define LOCAL_LABEL(name) ((name)[0] == '.' && (name)[1] == 'L') */
+#define tc_aout_pre_write_hook(x) {;} /* not used */
+#define tc_crawl_symbol_chain(a) {;} /* not used */
+#define tc_headers_hook(a) {;} /* not used */
+
+#define MAX_OPERANDS 3 /* max operands per insn */
+#define MAX_PREFIXES 4 /* max prefixes per opcode */
+#define MAX_IMMEDIATE_OPERANDS 2 /* max immediates per insn */
+#define MAX_MEMORY_OPERANDS 2 /* max memory ref per insn
+ * lcall uses 2
+ */
+/* we define the syntax here (modulo base,index,scale syntax) */
+#define REGISTER_PREFIX '%'
+#define IMMEDIATE_PREFIX '$'
+#define ABSOLUTE_PREFIX '*'
+#define PREFIX_SEPERATOR '/'
+
+#define TWO_BYTE_OPCODE_ESCAPE 0x0f
+
+#ifndef OLD_GAS
+#define NOP_OPCODE 0x90
+#else /* OLD_GAS */
+#define NOP_OPCODE 0x00
+#endif /* OLD_GAS */
+
+/* register numbers */
+#define EBP_REG_NUM 5
+#define ESP_REG_NUM 4
+
+/* modrm_byte.regmem for twobyte escape */
+#define ESCAPE_TO_TWO_BYTE_ADDRESSING ESP_REG_NUM
+/* index_base_byte.index for no index register addressing */
+#define NO_INDEX_REGISTER ESP_REG_NUM
+/* index_base_byte.base for no base register addressing */
+#define NO_BASE_REGISTER EBP_REG_NUM
+
+ /* these are the att as opcode suffixes, making movl --> mov, for example */
+#define DWORD_OPCODE_SUFFIX 'l'
+#define WORD_OPCODE_SUFFIX 'w'
+#define BYTE_OPCODE_SUFFIX 'b'
+
+ /* modrm.mode = REGMEM_FIELD_HAS_REG when a register is in there */
+#define REGMEM_FIELD_HAS_REG 0x3 /* always = 0x3 */
+#define REGMEM_FIELD_HAS_MEM (~REGMEM_FIELD_HAS_REG)
+
+#define END_OF_INSN '\0'
+
+/*
+ When an operand is read in it is classified by its type. This type includes
+ all the possible ways an operand can be used. Thus, '%eax' is both 'register
+ # 0' and 'The Accumulator'. In our language this is expressed by OR'ing
+ 'Reg32' (any 32 bit register) and 'Acc' (the accumulator).
+ Operands are classified so that we can match given operand types with
+ the opcode table in i386-opcode.h.
+ */
+#define Unknown 0x0
+/* register */
+#define Reg8 0x1 /* 8 bit reg */
+#define Reg16 0x2 /* 16 bit reg */
+#define Reg32 0x4 /* 32 bit reg */
+#define Reg (Reg8|Reg16|Reg32) /* gen'l register */
+#define WordReg (Reg16|Reg32) /* for push/pop operands */
+/* immediate */
+#define Imm8 0x8 /* 8 bit immediate */
+#define Imm8S 0x10 /* 8 bit immediate sign extended */
+#define Imm16 0x20 /* 16 bit immediate */
+#define Imm32 0x40 /* 32 bit immediate */
+#define Imm1 0x80 /* 1 bit immediate */
+#define ImmUnknown Imm32 /* for unknown expressions */
+#define Imm (Imm8|Imm8S|Imm16|Imm32) /* gen'l immediate */
+/* memory */
+#define Disp8 0x200 /* 8 bit displacement (for jumps) */
+#define Disp16 0x400 /* 16 bit displacement */
+#define Disp32 0x800 /* 32 bit displacement */
+#define Disp (Disp8|Disp16|Disp32) /* General displacement */
+#define DispUnknown Disp32 /* for unknown size displacements */
+#define Mem8 0x1000
+#define Mem16 0x2000
+#define Mem32 0x4000
+#define BaseIndex 0x8000
+#define Mem (Disp|Mem8|Mem16|Mem32|BaseIndex) /* General memory */
+#define WordMem (Mem16|Mem32|Disp|BaseIndex)
+#define ByteMem (Mem8|Disp|BaseIndex)
+/* specials */
+#define InOutPortReg 0x10000 /* register to hold in/out port addr = dx */
+#define ShiftCount 0x20000 /* register to hold shift cound = cl */
+#define Control 0x40000 /* Control register */
+#define Debug 0x80000 /* Debug register */
+#define Test 0x100000 /* Test register */
+#define FloatReg 0x200000 /* Float register */
+#define FloatAcc 0x400000 /* Float stack top %st(0) */
+#define SReg2 0x800000 /* 2 bit segment register */
+#define SReg3 0x1000000 /* 3 bit segment register */
+#define Acc 0x2000000 /* Accumulator %al or %ax or %eax */
+#define ImplicitRegister (InOutPortReg|ShiftCount|Acc|FloatAcc)
+#define JumpAbsolute 0x4000000
+#define Abs8 0x08000000
+#define Abs16 0x10000000
+#define Abs32 0x20000000
+#define Abs (Abs8|Abs16|Abs32)
+
+#define Byte (Reg8|Imm8|Imm8S)
+#define Word (Reg16|Imm16)
+#define DWord (Reg32|Imm32)
+
+#define SMALLEST_DISP_TYPE(num) \
+ fits_in_signed_byte(num) ? (Disp8|Disp32|Abs8|Abs32) : (Disp32|Abs32)
+
+typedef struct {
+ /* instruction name sans width suffix ("mov" for movl insns) */
+ char *name;
+
+ /* how many operands */
+ unsigned int operands;
+
+ /* base_opcode is the fundamental opcode byte with a optional prefix(es). */
+ unsigned int base_opcode;
+
+ /* extension_opcode is the 3 bit extension for group <n> insns.
+ If this template has no extension opcode (the usual case) use None */
+ unsigned char extension_opcode;
+#define None 0xff /* If no extension_opcode is possible. */
+
+ /* the bits in opcode_modifier are used to generate the final opcode from
+ the base_opcode. These bits also are used to detect alternate forms of
+ the same instruction */
+ unsigned int opcode_modifier;
+
+ /* opcode_modifier bits: */
+#define W 0x1 /* set if operands are words or dwords */
+#define D 0x2 /* D = 0 if Reg --> Regmem; D = 1 if Regmem --> Reg */
+ /* direction flag for floating insns: MUST BE 0x400 */
+#define FloatD 0x400
+ /* shorthand */
+#define DW (D|W)
+#define ShortForm 0x10 /* register is in low 3 bits of opcode */
+#define ShortFormW 0x20 /* ShortForm and W bit is 0x8 */
+#define Seg2ShortForm 0x40 /* encoding of load segment reg insns */
+#define Seg3ShortForm 0x80 /* fs/gs segment register insns. */
+#define Jump 0x100 /* special case for jump insns. */
+#define JumpInterSegment 0x200 /* special case for intersegment leaps/calls */
+ /* 0x400 CANNOT BE USED since it's already used by FloatD above */
+#define DONT_USE 0x400
+#define NoModrm 0x800
+#define Modrm 0x1000
+#define imulKludge 0x2000
+#define JumpByte 0x4000
+#define JumpDword 0x8000
+#define ReverseRegRegmem 0x10000
+
+ /* (opcode_modifier & COMES_IN_ALL_SIZES) is true if the
+ instuction comes in byte, word, and dword sizes and is encoded into
+ machine code in the canonical way. */
+#define COMES_IN_ALL_SIZES (W)
+
+ /* (opcode_modifier & COMES_IN_BOTH_DIRECTIONS) indicates that the
+ source and destination operands can be reversed by setting either
+ the D (for integer insns) or the FloatD (for floating insns) bit
+ in base_opcode. */
+#define COMES_IN_BOTH_DIRECTIONS (D|FloatD)
+
+ /* operand_types[i] describes the type of operand i. This is made
+ by OR'ing together all of the possible type masks. (e.g.
+ 'operand_types[i] = Reg|Imm' specifies that operand i can be
+ either a register or an immediate operand */
+ unsigned int operand_types[3];
+} template;
+
+/*
+ 'templates' is for grouping together 'template' structures for opcodes
+ of the same name. This is only used for storing the insns in the grand
+ ole hash table of insns.
+ The templates themselves start at START and range up to (but not including)
+ END.
+ */
+typedef struct {
+ template *start;
+ template *end;
+} templates;
+
+/* these are for register name --> number & type hash lookup */
+typedef struct {
+ char *reg_name;
+ unsigned int reg_type;
+ unsigned int reg_num;
+} reg_entry;
+
+typedef struct {
+ char *seg_name;
+ unsigned int seg_prefix;
+} seg_entry;
+
+/* these are for prefix name --> prefix code hash lookup */
+typedef struct {
+ char *prefix_name;
+ unsigned char prefix_code;
+} prefix_entry;
+
+/* 386 operand encoding bytes: see 386 book for details of this. */
+typedef struct {
+ unsigned regmem:3; /* codes register or memory operand */
+ unsigned reg:3; /* codes register operand (or extended opcode) */
+ unsigned mode:2; /* how to interpret regmem & reg */
+} modrm_byte;
+
+/* 386 opcode byte to code indirect addressing. */
+typedef struct {
+ unsigned base:3;
+ unsigned index:3;
+ unsigned scale:2;
+} base_index_byte;
+
+#endif /* TC_I386 */
+
+/* end of tc-i386.h */
diff --git a/gnu/usr.bin/as/config/tc-i860.c b/gnu/usr.bin/as/config/tc-i860.c
new file mode 100644
index 0000000..0123138
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-i860.c
@@ -0,0 +1,1295 @@
+/* tc-i860.c -- Assemble for the I860
+ Copyright (C) 1989, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "as.h"
+
+#include "opcode/i860.h"
+
+void md_begin();
+void md_end();
+void md_number_to_chars();
+void md_assemble();
+char *md_atof();
+void md_convert_frag();
+void md_create_short_jump();
+void md_create_long_jump();
+int md_estimate_size_before_relax();
+void md_number_to_imm();
+void md_number_to_disp();
+void md_number_to_field();
+void md_ri_to_chars();
+static void i860_ip();
+
+const relax_typeS md_relax_table[] = { 0 };
+
+/* handle of the OPCODE hash table */
+static struct hash_control *op_hash = NULL;
+
+static void s_dual(), s_enddual();
+static void s_atmp();
+
+const pseudo_typeS
+ md_pseudo_table[] = {
+ { "dual", s_dual, 4 },
+ { "enddual", s_enddual, 4 },
+ { "atmp", s_atmp, 4 },
+ { NULL, 0, 0 },
+ };
+
+int md_short_jump_size = 4;
+int md_long_jump_size = 4;
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful */
+char comment_chars[] = "!/"; /* JF removed '|' from comment_chars */
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that comments like this one will always work. */
+char line_comment_chars[] = "#/";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c. Ideally it shouldn't have to know about it at all,
+ but nothing is ideal around here.
+ */
+int size_reloc_info = sizeof(struct relocation_info);
+
+static unsigned char octal[256];
+#define isoctal(c) octal[c]
+ static unsigned char toHex[256];
+
+struct i860_it {
+ char *error;
+ unsigned long opcode;
+ struct nlist *nlistp;
+ expressionS exp;
+ int pcrel;
+ enum expand_type expand;
+ enum highlow_type highlow;
+ enum reloc_type reloc;
+} the_insn;
+
+#if __STDC__ == 1
+
+#ifdef comment
+static void print_insn(struct i860_it *insn);
+#endif /* comment */
+
+static int getExpression(char *str);
+
+#else /* not __STDC__ */
+
+#ifdef comment
+static void print_insn();
+#endif /* comment */
+
+static int getExpression();
+
+#endif /* not __STDC__ */
+
+static char *expr_end;
+static char last_expand; /* error if expansion after branch */
+
+enum dual
+{
+ DUAL_OFF = 0, DUAL_ON, DUAL_DDOT, DUAL_ONDDOT,
+};
+static enum dual dual_mode = DUAL_OFF; /* dual-instruction mode */
+
+static void
+ s_dual() /* floating point instructions have dual set */
+{
+ dual_mode = DUAL_ON;
+}
+
+static void
+ s_enddual() /* floating point instructions have dual set */
+{
+ dual_mode = DUAL_OFF;
+}
+
+static int atmp = 31; /* temporary register for pseudo's */
+
+static void
+ s_atmp()
+{
+ register int temp;
+ if (strncmp(input_line_pointer, "sp", 2) == 0) {
+ input_line_pointer += 2;
+ atmp = 2;
+ }
+ else if (strncmp(input_line_pointer, "fp", 2) == 0) {
+ input_line_pointer += 2;
+ atmp = 3;
+ }
+ else if (strncmp(input_line_pointer, "r", 1) == 0) {
+ input_line_pointer += 1;
+ temp = get_absolute_expression();
+ if (temp >= 0 && temp <= 31)
+ atmp = temp;
+ else
+ as_bad("Unknown temporary pseudo register");
+ }
+ else {
+ as_bad("Unknown temporary pseudo register");
+ }
+ demand_empty_rest_of_line();
+ return;
+}
+
+/* This function is called once, at assembler startup time. It should
+ set up all the tables, etc. that the MD part of the assembler will need. */
+void
+ md_begin()
+{
+ register char *retval = NULL;
+ int lose = 0;
+ register unsigned int i = 0;
+
+ op_hash = hash_new();
+ if (op_hash == NULL)
+ as_fatal("Virtual memory exhausted");
+
+ while (i < NUMOPCODES)
+ {
+ const char *name = i860_opcodes[i].name;
+ retval = hash_insert(op_hash, name, &i860_opcodes[i]);
+ if (retval != NULL && *retval != '\0')
+ {
+ fprintf (stderr, "internal error: can't hash `%s': %s\n",
+ i860_opcodes[i].name, retval);
+ lose = 1;
+ }
+ do
+ {
+ if (i860_opcodes[i].match & i860_opcodes[i].lose)
+ {
+ fprintf (stderr, "internal error: losing opcode: `%s' \"%s\"\n",
+ i860_opcodes[i].name, i860_opcodes[i].args);
+ lose = 1;
+ }
+ ++i;
+ } while (i < NUMOPCODES
+ && !strcmp(i860_opcodes[i].name, name));
+ }
+
+ if (lose)
+ as_fatal("Broken assembler. No assembly attempted.");
+
+ for (i = '0'; i < '8'; ++i)
+ octal[i] = 1;
+ for (i = '0'; i <= '9'; ++i)
+ toHex[i] = i - '0';
+ for (i = 'a'; i <= 'f'; ++i)
+ toHex[i] = i + 10 - 'a';
+ for (i = 'A'; i <= 'F'; ++i)
+ toHex[i] = i + 10 - 'A';
+}
+
+void
+ md_end()
+{
+ return;
+}
+
+void
+ md_assemble(str)
+char *str;
+{
+ char *toP;
+/* int rsd; FIXME: remove this line. */
+ int no_opcodes = 1;
+ int i;
+ struct i860_it pseudo[3];
+
+ assert(str);
+ i860_ip(str);
+
+ /* check for expandable flag to produce pseudo-instructions */
+ if (the_insn.expand != 0 && the_insn.highlow == NO_SPEC) {
+ for (i = 0; i < 3; i++)
+ pseudo[i] = the_insn;
+
+ switch (the_insn.expand) {
+
+ case E_DELAY:
+ no_opcodes = 1;
+ break;
+
+ case E_MOV:
+ if (the_insn.exp.X_add_symbol == NULL &&
+ the_insn.exp.X_subtract_symbol == NULL &&
+ (the_insn.exp.X_add_number < (1 << 15) &&
+ the_insn.exp.X_add_number >= -(1 << 15)))
+ break;
+ /* or l%const,r0,ireg_dest */
+ pseudo[0].opcode = (the_insn.opcode & 0x001f0000) | 0xe4000000;
+ pseudo[0].highlow = PAIR;
+ /* orh h%const,ireg_dest,ireg_dest */
+ pseudo[1].opcode = (the_insn.opcode & 0x03ffffff) | 0xec000000 |
+ ((the_insn.opcode & 0x001f0000) << 5);
+ pseudo[1].highlow = HIGH;
+ no_opcodes = 2;
+ break;
+
+ case E_ADDR:
+ if (the_insn.exp.X_add_symbol == NULL &&
+ the_insn.exp.X_subtract_symbol == NULL)
+ break;
+ /* orh ha%addr_expr,r0,r31 */
+ pseudo[0].opcode = 0xec000000 | (atmp<<16);
+ pseudo[0].highlow = HIGHADJ;
+ pseudo[0].reloc = LOW0; /* must overwrite */
+ /* l%addr_expr(r31),ireg_dest */
+ pseudo[1].opcode = (the_insn.opcode & ~0x003e0000) | (atmp << 21);
+ pseudo[1].highlow = PAIR;
+ no_opcodes = 2;
+ break;
+
+ case E_U32: /* 2nd version emulates Intel as, not doc. */
+ if (the_insn.exp.X_add_symbol == NULL &&
+ the_insn.exp.X_subtract_symbol == NULL &&
+ (the_insn.exp.X_add_number < (1 << 16) &&
+ the_insn.exp.X_add_number >= 0))
+ break;
+ /* $(opcode)h h%const,ireg_src2,ireg_dest
+ pseudo[0].opcode = (the_insn.opcode & 0xf3ffffff) | 0x0c000000; */
+ /* $(opcode)h h%const,ireg_src2,r31 */
+ pseudo[0].opcode = (the_insn.opcode & 0xf3e0ffff) | 0x0c000000 |
+ (atmp << 16);
+ pseudo[0].highlow = HIGH;
+ /* $(opcode) l%const,ireg_dest,ireg_dest
+ pseudo[1].opcode = (the_insn.opcode & 0xf01f0000) | 0x04000000 |
+ ((the_insn.opcode & 0x001f0000) << 5); */
+ /* $(opcode) l%const,r31,ireg_dest */
+ pseudo[1].opcode = (the_insn.opcode & 0xf01f0000) | 0x04000000 |
+ (atmp << 21);
+ pseudo[1].highlow = PAIR;
+ no_opcodes = 2;
+ break;
+
+ case E_AND: /* 2nd version emulates Intel as, not doc. */
+ if (the_insn.exp.X_add_symbol == NULL &&
+ the_insn.exp.X_subtract_symbol == NULL &&
+ (the_insn.exp.X_add_number < (1 << 16) &&
+ the_insn.exp.X_add_number >= 0))
+ break;
+ /* andnot h%const,ireg_src2,ireg_dest
+ pseudo[0].opcode = (the_insn.opcode & 0x03ffffff) | 0xd4000000; */
+ /* andnot h%const,ireg_src2,r31 */
+ pseudo[0].opcode = (the_insn.opcode & 0x03e0ffff) | 0xd4000000 |
+ (atmp << 16);
+ pseudo[0].highlow = HIGH;
+ pseudo[0].exp.X_add_number = -1 - the_insn.exp.X_add_number;
+ /* andnot l%const,ireg_dest,ireg_dest
+ pseudo[1].opcode = (the_insn.opcode & 0x001f0000) | 0xd4000000 |
+ ((the_insn.opcode & 0x001f0000) << 5); */
+ /* andnot l%const,r31,ireg_dest */
+ pseudo[1].opcode = (the_insn.opcode & 0x001f0000) | 0xd4000000 |
+ (atmp << 21);
+ pseudo[1].highlow = PAIR;
+ pseudo[1].exp.X_add_number = -1 - the_insn.exp.X_add_number;
+ no_opcodes = 2;
+ break;
+
+ case E_S32:
+ if (the_insn.exp.X_add_symbol == NULL &&
+ the_insn.exp.X_subtract_symbol == NULL &&
+ (the_insn.exp.X_add_number < (1 << 15) &&
+ the_insn.exp.X_add_number >= -(1 << 15)))
+ break;
+ /* orh h%const,r0,r31 */
+ pseudo[0].opcode = 0xec000000 | (atmp << 16);
+ pseudo[0].highlow = HIGH;
+ /* or l%const,r31,r31 */
+ pseudo[1].opcode = 0xe4000000 | (atmp << 21) | (atmp << 16);
+ pseudo[1].highlow = PAIR;
+ /* r31,ireg_src2,ireg_dest */
+ pseudo[2].opcode = (the_insn.opcode & ~0x0400ffff) | (atmp << 11);
+ pseudo[2].reloc = NO_RELOC;
+ no_opcodes = 3;
+ break;
+
+ default:
+ as_fatal("failed sanity check.");
+ }
+
+ the_insn = pseudo[0];
+ /* check for expanded opcode after branch or in dual */
+ if (no_opcodes > 1 && last_expand == 1)
+ as_warn("Expanded opcode after delayed branch: `%s'", str);
+ if (no_opcodes > 1 && dual_mode != DUAL_OFF)
+ as_warn("Expanded opcode in dual mode: `%s'", str);
+ }
+
+ i = 0;
+ do { /* always produce at least one opcode */
+ toP = frag_more(4);
+ /* put out the opcode */
+ md_number_to_chars(toP, the_insn.opcode, 4);
+
+ /* check for expanded opcode after branch or in dual */
+ last_expand = the_insn.pcrel;
+
+ /* put out the symbol-dependent stuff */
+ if (the_insn.reloc != NO_RELOC) {
+ fix_new(frag_now, /* which frag */
+ (toP - frag_now->fr_literal), /* where */
+ 4, /* size */
+ the_insn.exp.X_add_symbol,
+ the_insn.exp.X_subtract_symbol,
+ the_insn.exp.X_add_number,
+ the_insn.pcrel,
+ /* merge bit fields into one argument */
+ (int)(((the_insn.highlow & 0x3) << 4) | (the_insn.reloc & 0xf)));
+ }
+ the_insn = pseudo[++i];
+ } while (--no_opcodes > 0);
+
+}
+
+static void
+ i860_ip(str)
+char *str;
+{
+ char *s;
+ const char *args;
+ char c;
+/* unsigned long i; FIXME: remove this line. */
+ struct i860_opcode *insn;
+ char *argsStart;
+ unsigned long opcode;
+ unsigned int mask;
+ int match = 0;
+ int comma = 0;
+
+
+ for (s = str; islower(*s) || *s == '.' || *s == '3'; ++s)
+ ;
+ switch (*s) {
+
+ case '\0':
+ break;
+
+ case ',':
+ comma = 1;
+
+ /*FALLTHROUGH*/
+
+ case ' ':
+ *s++ = '\0';
+ break;
+
+ default:
+ as_bad("Unknown opcode: `%s'", str);
+ exit(1);
+ }
+
+ if (strncmp(str, "d.", 2) == 0) { /* check for d. opcode prefix */
+ if (dual_mode == DUAL_ON)
+ dual_mode = DUAL_ONDDOT;
+ else
+ dual_mode = DUAL_DDOT;
+ str += 2;
+ }
+
+ if ((insn = (struct i860_opcode *) hash_find(op_hash, str)) == NULL) {
+ if (dual_mode == DUAL_DDOT || dual_mode == DUAL_ONDDOT)
+ str -= 2;
+ as_bad("Unknown opcode: `%s'", str);
+ return;
+ }
+ if (comma) {
+ *--s = ',';
+ }
+ argsStart = s;
+ for (;;) {
+ opcode = insn->match;
+ memset(&the_insn, '\0', sizeof(the_insn));
+ the_insn.reloc = NO_RELOC;
+
+ /*
+ * Build the opcode, checking as we go to make
+ * sure that the operands match
+ */
+ for (args = insn->args; ; ++args) {
+ switch (*args) {
+
+ case '\0': /* end of args */
+ if (*s == '\0') {
+ match = 1;
+ }
+ break;
+
+ case '+':
+ case '(': /* these must match exactly */
+ case ')':
+ case ',':
+ case ' ':
+ if (*s++ == *args)
+ continue;
+ break;
+
+ case '#': /* must be at least one digit */
+ if (isdigit(*s++)) {
+ while (isdigit(*s)) {
+ ++s;
+ }
+ continue;
+ }
+ break;
+
+ case '1': /* next operand must be a register */
+ case '2':
+ case 'd':
+ switch (*s) {
+
+ case 'f': /* frame pointer */
+ s++;
+ if (*s++ == 'p') {
+ mask = 0x3;
+ break;
+ }
+ goto error;
+
+ case 's': /* stack pointer */
+ s++;
+ if (*s++ == 'p') {
+ mask= 0x2;
+ break;
+ }
+ goto error;
+
+ case 'r': /* any register */
+ s++;
+ if (!isdigit(c = *s++)) {
+ goto error;
+ }
+ if (isdigit(*s)) {
+ if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32) {
+ goto error;
+ }
+ } else {
+ c -= '0';
+ }
+ mask= c;
+ break;
+
+ default: /* not this opcode */
+ goto error;
+ }
+ /*
+ * Got the register, now figure out where
+ * it goes in the opcode.
+ */
+ switch (*args) {
+
+ case '1':
+ opcode |= mask << 11;
+ continue;
+
+ case '2':
+ opcode |= mask << 21;
+ continue;
+
+ case 'd':
+ opcode |= mask << 16;
+ continue;
+
+ }
+ break;
+
+ case 'e': /* next operand is a floating point register */
+ case 'f':
+ case 'g':
+ if (*s++ == 'f' && isdigit(*s)) {
+ mask = *s++;
+ if (isdigit(*s)) {
+ mask = 10 * (mask - '0') + (*s++ - '0');
+ if (mask >= 32) {
+ break;
+ }
+ } else {
+ mask -= '0';
+ }
+ switch (*args) {
+
+ case 'e':
+ opcode |= mask << 11;
+ continue;
+
+ case 'f':
+ opcode |= mask << 21;
+ continue;
+
+ case 'g':
+ opcode |= mask << 16;
+ if (dual_mode != DUAL_OFF)
+ opcode |= (1 << 9); /* dual mode instruction */
+ if (dual_mode == DUAL_DDOT)
+ dual_mode = DUAL_OFF;
+ if (dual_mode == DUAL_ONDDOT)
+ dual_mode = DUAL_ON;
+ if ((opcode & (1 << 10)) && (mask == ((opcode >> 11) & 0x1f)))
+ as_warn("Fsr1 equals fdest with Pipelining");
+ continue;
+ }
+ }
+ break;
+
+ case 'c': /* next operand must be a control register */
+ if (strncmp(s, "fir", 3) == 0) {
+ opcode |= 0x0 << 21;
+ s += 3;
+ continue;
+ }
+ if (strncmp(s, "psr", 3) == 0) {
+ opcode |= 0x1 << 21;
+ s += 3;
+ continue;
+ }
+ if (strncmp(s, "dirbase", 7) == 0) {
+ opcode |= 0x2 << 21;
+ s += 7;
+ continue;
+ }
+ if (strncmp(s, "db", 2) == 0) {
+ opcode |= 0x3 << 21;
+ s += 2;
+ continue;
+ }
+ if (strncmp(s, "fsr", 3) == 0) {
+ opcode |= 0x4 << 21;
+ s += 3;
+ continue;
+ }
+ if (strncmp(s, "epsr", 4) == 0) {
+ opcode |= 0x5 << 21;
+ s += 4;
+ continue;
+ }
+ break;
+
+ case '5': /* 5 bit immediate in src1 */
+ memset(&the_insn, '\0', sizeof(the_insn));
+ if ( !getExpression(s)) {
+ s = expr_end;
+ if (the_insn.exp.X_add_number & ~0x1f)
+ as_bad("5-bit immediate too large");
+ opcode |= (the_insn.exp.X_add_number & 0x1f) << 11;
+ memset(&the_insn, '\0', sizeof(the_insn));
+ the_insn.reloc = NO_RELOC;
+ continue;
+ }
+ break;
+
+ case 'l': /* 26 bit immediate, relative branch */
+ the_insn.reloc = BRADDR;
+ the_insn.pcrel = 1;
+ goto immediate;
+
+ case 's': /* 16 bit immediate, split relative branch */
+ /* upper 5 bits of offset in dest field */
+ the_insn.pcrel = 1;
+ the_insn.reloc = SPLIT0;
+ goto immediate;
+
+ case 'S': /* 16 bit immediate, split (st), aligned */
+ if (opcode & (1 << 28))
+ if (opcode & 0x1)
+ the_insn.reloc = SPLIT2;
+ else
+ the_insn.reloc = SPLIT1;
+ else
+ the_insn.reloc = SPLIT0;
+ goto immediate;
+
+ case 'I': /* 16 bit immediate, aligned */
+ if (opcode & (1 << 28))
+ if (opcode & 0x1)
+ the_insn.reloc = LOW2;
+ else
+ the_insn.reloc = LOW1;
+ else
+ the_insn.reloc = LOW0;
+ goto immediate;
+
+ case 'i': /* 16 bit immediate */
+ the_insn.reloc = LOW0;
+
+ /*FALLTHROUGH*/
+
+ immediate:
+ if (*s == ' ')
+ s++;
+ if (strncmp(s, "ha%", 3) == 0) {
+ the_insn.highlow = HIGHADJ;
+ s += 3;
+ } else if (strncmp(s, "h%", 2) == 0) {
+ the_insn.highlow = HIGH;
+ s += 2;
+ } else if (strncmp(s, "l%", 2) == 0) {
+ the_insn.highlow = PAIR;
+ s += 2;
+ }
+ the_insn.expand = insn->expand;
+
+ /* Note that if the getExpression() fails, we will still have
+ created U entries in the symbol table for the 'symbols'
+ in the input string. Try not to create U symbols for
+ registers, etc. */
+
+ if ( !getExpression(s)) {
+ s = expr_end;
+ continue;
+ }
+ break;
+
+ default:
+ as_fatal("failed sanity check.");
+ }
+ break;
+ }
+ error:
+ if (match == 0)
+ {
+ /* Args don't match. */
+ if (&insn[1] - i860_opcodes < NUMOPCODES
+ && !strcmp(insn->name, insn[1].name))
+ {
+ ++insn;
+ s = argsStart;
+ continue;
+ }
+ else
+ {
+ as_bad("Illegal operands");
+ return;
+ }
+ }
+ break;
+ }
+
+ the_insn.opcode = opcode;
+ return;
+}
+
+static int
+ getExpression(str)
+char *str;
+{
+ char *save_in;
+ segT seg;
+
+ save_in = input_line_pointer;
+ input_line_pointer = str;
+ switch (seg = expression(&the_insn.exp)) {
+
+ case SEG_ABSOLUTE:
+ case SEG_TEXT:
+ case SEG_DATA:
+ case SEG_BSS:
+ case SEG_UNKNOWN:
+ case SEG_DIFFERENCE:
+ case SEG_BIG:
+ case SEG_ABSENT:
+ break;
+
+ default:
+ the_insn.error = "bad segment";
+ expr_end = input_line_pointer;
+ input_line_pointer=save_in;
+ return 1;
+ }
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+ return 0;
+}
+
+
+/*
+ This is identical to the md_atof in m68k.c. I think this is right,
+ but I'm not sure.
+
+ Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP. An error message is returned, or NULL on OK.
+ */
+
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+char *
+ md_atof(type,litP,sizeP)
+char type;
+char *litP;
+int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+ char *atof_ieee();
+
+ switch (type) {
+
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP=0;
+ return "Bad call to MD_ATOF()";
+ }
+ t=atof_ieee(input_line_pointer,type,words);
+ if (t)
+ input_line_pointer=t;
+ *sizeP=prec * sizeof(LITTLENUM_TYPE);
+ for (wordP=words;prec--;) {
+ md_number_to_chars(litP,(long)(*wordP++),sizeof(LITTLENUM_TYPE));
+ litP+=sizeof(LITTLENUM_TYPE);
+ }
+ return ""; /* Someone should teach Dean about null pointers */
+}
+
+/*
+ * Write out big-endian.
+ */
+void
+ md_number_to_chars(buf, val, n)
+char *buf;
+long val;
+int n;
+{
+ switch (n) {
+
+ case 4:
+ *buf++ = val >> 24;
+ *buf++ = val >> 16;
+ case 2:
+ *buf++ = val >> 8;
+ case 1:
+ *buf = val;
+ break;
+
+ default:
+ as_fatal("failed sanity check.");
+ }
+ return;
+}
+
+void md_number_to_imm(buf, val, n, fixP)
+char *buf;
+long val;
+int n;
+fixS *fixP;
+{
+ enum reloc_type reloc = fixP->fx_r_type & 0xf;
+ enum highlow_type highlow = (fixP->fx_r_type >> 4) & 0x3;
+
+ assert(buf);
+ assert(n == 4); /* always on i860 */
+
+ switch (highlow) {
+
+ case HIGHADJ: /* adjusts the high-order 16-bits */
+ if (val & (1 << 15))
+ val += (1 << 16);
+
+ /*FALLTHROUGH*/
+
+ case HIGH: /* selects the high-order 16-bits */
+ val >>= 16;
+ break;
+
+ case PAIR: /* selects the low-order 16-bits */
+ val = val & 0xffff;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (reloc) {
+
+ case BRADDR: /* br, call, bc, bc.t, bnc, bnc.t w/26-bit immediate */
+ if (fixP->fx_pcrel != 1)
+ as_bad("26-bit branch w/o pc relative set: 0x%08x", val);
+ val >>= 2; /* align pcrel offset, see manual */
+
+ if (val >= (1 << 25) || val < -(1 << 25)) /* check for overflow */
+ as_bad("26-bit branch offset overflow: 0x%08x", val);
+ buf[0] = (buf[0] & 0xfc) | ((val >> 24) & 0x3);
+ buf[1] = val >> 16;
+ buf[2] = val >> 8;
+ buf[3] = val;
+ break;
+
+ case SPLIT2: /* 16 bit immediate, 4-byte aligned */
+ if (val & 0x3)
+ as_bad("16-bit immediate 4-byte alignment error: 0x%08x", val);
+ val &= ~0x3; /* 4-byte align value */
+ /*FALLTHROUGH*/
+ case SPLIT1: /* 16 bit immediate, 2-byte aligned */
+ if (val & 0x1)
+ as_bad("16-bit immediate 2-byte alignment error: 0x%08x", val);
+ val &= ~0x1; /* 2-byte align value */
+ /*FALLTHROUGH*/
+ case SPLIT0: /* st,bla,bte,btne w/16-bit immediate */
+ if (fixP->fx_pcrel == 1)
+ val >>= 2; /* align pcrel offset, see manual */
+ /* check for bounds */
+ if (highlow != PAIR && (val >= (1 << 16) || val < -(1 << 15)))
+ as_bad("16-bit branch offset overflow: 0x%08x", val);
+ buf[1] = (buf[1] & ~0x1f) | ((val >> 11) & 0x1f);
+ buf[2] = (buf[2] & ~0x7) | ((val >> 8) & 0x7);
+ buf[3] |= val; /* perserve bottom opcode bits */
+ break;
+
+ case LOW4: /* fld,pfld,pst,flush 16-byte aligned */
+ if (val & 0xf)
+ as_bad("16-bit immediate 16-byte alignment error: 0x%08x", val);
+ val &= ~0xf; /* 16-byte align value */
+ /*FALLTHROUGH*/
+ case LOW3: /* fld,pfld,pst,flush 8-byte aligned */
+ if (val & 0x7)
+ as_bad("16-bit immediate 8-byte alignment error: 0x%08x", val);
+ val &= ~0x7; /* 8-byte align value */
+ /*FALLTHROUGH*/
+ case LOW2: /* 16 bit immediate, 4-byte aligned */
+ if (val & 0x3)
+ as_bad("16-bit immediate 4-byte alignment error: 0x%08x", val);
+ val &= ~0x3; /* 4-byte align value */
+ /*FALLTHROUGH*/
+ case LOW1: /* 16 bit immediate, 2-byte aligned */
+ if (val & 0x1)
+ as_bad("16-bit immediate 2-byte alignment error: 0x%08x", val);
+ val &= ~0x1; /* 2-byte align value */
+ /*FALLTHROUGH*/
+ case LOW0: /* 16 bit immediate, byte aligned */
+ /* check for bounds */
+ if (highlow != PAIR && (val >= (1 << 16) || val < -(1 << 15)))
+ as_bad("16-bit immediate overflow: 0x%08x", val);
+ buf[2] = val >> 8;
+ buf[3] |= val; /* perserve bottom opcode bits */
+ break;
+
+ case RELOC_32:
+ md_number_to_chars(buf, val, 4);
+ break;
+
+ case NO_RELOC:
+ default:
+ as_bad("bad relocation type: 0x%02x", reloc);
+ break;
+ }
+ return;
+}
+
+/* should never be called for i860 */
+void
+ md_create_short_jump(ptr, from_addr, to_addr, frag, to_symbol)
+char *ptr;
+long from_addr, to_addr;
+fragS *frag;
+symbolS *to_symbol;
+{
+ as_fatal("i860_create_short_jmp\n");
+}
+
+/* should never be called for i860 */
+void
+ md_number_to_disp(buf, val, n)
+char *buf;
+long val;
+int n;
+{
+ as_fatal("md_number_to_disp\n");
+}
+
+/* should never be called for i860 */
+void
+ md_number_to_field(buf,val,fix)
+char *buf;
+long val;
+void *fix;
+{
+ as_fatal("i860_number_to_field\n");
+}
+
+/* the bit-field entries in the relocation_info struct plays hell
+ with the byte-order problems of cross-assembly. So as a hack,
+ I added this mach. dependent ri twiddler. Ugly, but it gets
+ you there. -KWK */
+/* on i860: first 4 bytes are normal unsigned long address, next three
+ bytes are index, most sig. byte first. Byte 7 is broken up with
+ bit 7 as pcrel, bit 6 as extern, and the lower six bits as
+ relocation type (highlow 5-4). Next 4 bytes are long addend. */
+/* Thanx and a tip of the hat to Michael Bloom, mb@ttidca.tti.com */
+void
+ md_ri_to_chars(ri_p, ri)
+struct relocation_info *ri_p, ri;
+{
+#if 0
+ unsigned char the_bytes[sizeof(*ri_p)];
+
+ /* this is easy */
+ md_number_to_chars(the_bytes, ri.r_address, sizeof(ri.r_address));
+ /* now the fun stuff */
+ the_bytes[4] = (ri.r_index >> 16) & 0x0ff;
+ the_bytes[5] = (ri.r_index >> 8) & 0x0ff;
+ the_bytes[6] = ri.r_index & 0x0ff;
+ the_bytes[7] = ((ri.r_extern << 7) & 0x80) | (0 & 0x60) | (ri.r_type & 0x1F);
+ /* Also easy */
+ md_number_to_chars(&the_bytes[8], ri.r_addend, sizeof(ri.r_addend));
+ /* now put it back where you found it, Junior... */
+ memcpy((char *) ri_p, the_bytes, sizeof(*ri_p));
+#endif
+}
+
+/* should never be called for i860 */
+void
+ md_convert_frag(headers, fragP)
+object_headers *headers;
+register fragS *fragP;
+{
+ as_fatal("i860_convert_frag\n");
+}
+
+/* should never be called for i860 */
+void
+ md_create_long_jump(ptr, from_addr, to_addr, frag, to_symbol)
+char *ptr;
+long from_addr,
+ to_addr;
+fragS *frag;
+symbolS *to_symbol;
+{
+ as_fatal("i860_create_long_jump\n");
+}
+
+/* should never be called for i860 */
+int
+ md_estimate_size_before_relax(fragP, segtype)
+register fragS *fragP;
+segT segtype;
+{
+ as_fatal("i860_estimate_size_before_relax\n");
+ return(0);
+}
+
+#ifdef comment
+/* for debugging only, must match enum reloc_type */
+static char *Reloc[] = {
+ "NO_RELOC",
+ "BRADDR",
+ "LOW0",
+ "LOW1",
+ "LOW2",
+ "LOW3",
+ "LOW4",
+ "SPLIT0",
+ "SPLIT1",
+ "SPLIT2",
+ "RELOC_32",
+};
+static char *Highlow[] = {
+ "NO_SPEC",
+ "PAIR",
+ "HIGH",
+ "HIGHADJ",
+};
+
+static void
+ print_insn(insn)
+struct i860_it *insn;
+{
+ if (insn->error) {
+ fprintf(stderr, "ERROR: %s\n", insn->error);
+ }
+ fprintf(stderr, "opcode=0x%08x\t", insn->opcode);
+ fprintf(stderr, "expand=0x%08x\t", insn->expand);
+ fprintf(stderr, "reloc = %s\t", Reloc[insn->reloc]);
+ fprintf(stderr, "highlow = %s\n", Highlow[insn->highlow]);
+ fprintf(stderr, "exp = {\n");
+ fprintf(stderr, "\t\tX_add_symbol = %s\n",
+ insn->exp.X_add_symbol ?
+ (S_GET_NAME(insn->exp.X_add_symbol) ?
+ S_GET_NAME(insn->exp.X_add_symbol) : "???") : "0");
+ fprintf(stderr, "\t\tX_sub_symbol = %s\n",
+ insn->exp.X_subtract_symbol ?
+ (S_GET_NAME(insn->exp.X_subtract_symbol) ?
+ S_GET_NAME(insn->exp.X_subtract_symbol) : "???") : "0");
+ fprintf(stderr, "\t\tX_add_number = %d\n",
+ insn->exp.X_add_number);
+ fprintf(stderr, "}\n");
+ return;
+}
+#endif /* comment */
+
+int
+ md_parse_option(argP,cntP,vecP)
+char **argP;
+int *cntP;
+char ***vecP;
+{
+ return 1;
+}
+
+#ifdef comment
+/*
+ * I860 relocations are completely different, so it needs
+ * this machine dependent routine to emit them.
+ */
+void
+ emit_machine_reloc(fixP, segment_address_in_file)
+register fixS *fixP;
+relax_addressT segment_address_in_file;
+{
+ struct reloc_info_i860 ri;
+ register symbolS *symbolP;
+ extern char *next_object_file_charP;
+ long add_number;
+
+ memset((char *) &ri, '\0', sizeof(ri));
+ for (; fixP; fixP = fixP->fx_next) {
+
+ if (fixP->fx_r_type & ~0x3f) {
+ as_fatal("fixP->fx_r_type = %d\n", fixP->fx_r_type);
+ }
+ ri.r_pcrel = fixP->fx_pcrel;
+ ri.r_type = fixP->fx_r_type;
+
+ if ((symbolP = fixP->fx_addsy) != NULL) {
+ ri.r_address = fixP->fx_frag->fr_address +
+ fixP->fx_where - segment_address_in_file;
+ if (!S_IS_DEFINED(symbolP)) {
+ ri.r_extern = 1;
+ ri.r_symbolnum = symbolP->sy_number;
+ } else {
+ ri.r_extern = 0;
+ ri.r_symbolnum = S_GET_TYPE(symbolP);
+ }
+ if (symbolP && symbolP->sy_frag) {
+ ri.r_addend = symbolP->sy_frag->fr_address;
+ }
+ ri.r_type = fixP->fx_r_type;
+ if (fixP->fx_pcrel) {
+ /* preserve actual offset vs. pc + 4 */
+ ri.r_addend -= (ri.r_address + 4);
+ } else {
+ ri.r_addend = fixP->fx_addnumber;
+ }
+
+ md_ri_to_chars((char *) &ri, ri);
+ append(&next_object_file_charP, (char *)& ri, sizeof(ri));
+ }
+ }
+ return;
+}
+#endif /* comment */
+
+#ifdef OBJ_AOUT
+
+/* on i860: first 4 bytes are normal unsigned long address, next three
+ bytes are index, most sig. byte first. Byte 7 is broken up with
+ bit 7 as pcrel, bit 6 as extern, and the lower six bits as
+ relocation type (highlow 5-4). Next 4 bytes are long addend.
+
+ ie,
+
+ struct reloc_info_i860 {
+ unsigned long r_address;
+ unsigned int r_symbolnum : 24;
+ unsigned int r_pcrel : 1;
+ unsigned int r_extern : 1;
+ unsigned int r_type : 6;
+ long r_addend;
+ }
+
+ */
+
+int md_reloc_size = 12;
+
+void tc_aout_fix_to_chars(where, fixP, segment_address_in_file)
+char *where;
+fixS *fixP;
+relax_addressT segment_address_in_file;
+{
+ long r_index;
+ long r_extern;
+ long r_addend = 0;
+ long r_address;
+
+ know(fixP->fx_addsy);
+ know(!(fixP->fx_r_type & ~0x3f));
+
+ if (!S_IS_DEFINED(fixP->fx_addsy)) {
+ r_extern = 1;
+ r_index = fixP->fx_addsy->sy_number;
+ } else {
+ r_extern = 0;
+ r_index = S_GET_TYPE(fixP->fx_addsy);
+ }
+
+ md_number_to_chars(where,
+ r_address = fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file,
+ 4);
+
+ where[4] = (r_index >> 16) & 0x0ff;
+ where[5] = (r_index >> 8) & 0x0ff;
+ where[6] = r_index & 0x0ff;
+ where[7] = (((fixP->fx_pcrel << 7) & 0x80)
+ | ((r_extern << 6) & 0x40)
+ | (fixP->fx_r_type & 0x3F));
+
+ if (fixP->fx_addsy->sy_frag) {
+ r_addend = fixP->fx_addsy->sy_frag->fr_address;
+ }
+
+ if (fixP->fx_pcrel) {
+ /* preserve actual offset vs. pc + 4 */
+ r_addend -= (r_address + 4);
+ } else {
+ r_addend = fixP->fx_addnumber;
+ }
+
+ md_number_to_chars(&where[8], r_addend, 4);
+
+ return;
+} /* tc_aout_fix_to_chars() */
+
+#endif /* OBJ_AOUT */
+
+/* Parse an operand that is machine-specific.
+ We just return without modifying the expression if we have nothing
+ to do. */
+
+/* ARGSUSED */
+void
+ md_operand (expressionP)
+expressionS *expressionP;
+{
+}
+
+/* We have no need to default values of symbols. */
+
+/* ARGSUSED */
+symbolS *
+ md_undefined_symbol (name)
+char *name;
+{
+ return 0;
+}
+
+/* Round up a section size to the appropriate boundary. */
+long
+ md_section_align (segment, size)
+segT segment;
+long size;
+{
+ return size; /* Byte alignment is fine */
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the i860, they're relative to the address of the offset, plus
+ its size. (??? Is this right? FIXME-SOON!) */
+long
+ md_pcrel_from (fixP)
+fixS *fixP;
+{
+ return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+void
+ md_apply_fix(fixP, val)
+fixS *fixP;
+long val;
+{
+ char *place = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ /* fixme-soon: looks to me like i860 never has bit fixes. Let's see. xoxorich. */
+ know(fixP->fx_bit_fixP == NULL);
+ if (!fixP->fx_bit_fixP) {
+
+ /* fixme-soon: also looks like fx_im_disp is always 0. Let's see. xoxorich. */
+ know(fixP->fx_im_disp == 0);
+ switch (fixP->fx_im_disp) {
+ case 0:
+ fixP->fx_addnumber = val;
+ md_number_to_imm(place, val, fixP->fx_size, fixP);
+ break;
+ case 1:
+ md_number_to_disp(place,
+ fixP->fx_pcrel ? val + fixP->fx_pcrel_adjust : val,
+ fixP->fx_size);
+ break;
+ case 2: /* fix requested for .long .word etc */
+ md_number_to_chars(place, val, fixP->fx_size);
+ break;
+ default:
+ as_fatal("Internal error in md_apply_fix() in file \"%s\"", __FILE__);
+ } /* OVE: maybe one ought to put _imm _disp _chars in one md-func */
+ } else {
+ md_number_to_field(place, val, fixP->fx_bit_fixP);
+ }
+
+ return;
+} /* md_apply_fix() */
+
+/*
+ * Local Variables:
+ * fill-column: 131
+ * comment-column: 0
+ * End:
+ */
+
+/* end of tc-i860.c */
diff --git a/gnu/usr.bin/as/config/tc-i860.h b/gnu/usr.bin/as/config/tc-i860.h
new file mode 100644
index 0000000..adc0d8f
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-i860.h
@@ -0,0 +1,24 @@
+/*
+ * This file is tc-i860.h.
+ */
+
+#define TC_I860 1
+
+#define NO_LISTING
+
+#ifdef OLD_GAS
+#define REVERSE_SORT_RELOCS
+#endif /* OLD_GAS */
+
+#define tc_headers_hook(a) {;} /* not used */
+#define tc_crawl_symbol_chain(a) {;} /* not used */
+#define tc_aout_pre_write_hook(x) {;} /* not used */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of tc-i860.h */
diff --git a/gnu/usr.bin/as/config/tc-i960.c b/gnu/usr.bin/as/config/tc-i960.c
new file mode 100644
index 0000000..8f9091c
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-i960.c
@@ -0,0 +1,2759 @@
+/* tc-i960.c - All the i80960-specific stuff
+ Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* See comment on md_parse_option for 80960-specific invocation options. */
+
+/******************************************************************************
+ * i80690 NOTE!!!:
+ * Header, symbol, and relocation info will be used on the host machine
+ * only -- only executable code is actually downloaded to the i80960.
+ * Therefore, leave all such information in host byte order.
+ *
+ * (That's a slight lie -- we DO download some header information, but
+ * the downloader converts the file format and corrects the byte-ordering
+ * of the relevant fields while doing so.)
+ *
+ ***************************************************************************** */
+
+/* There are 4 different lengths of (potentially) symbol-based displacements
+ * in the 80960 instruction set, each of which could require address fix-ups
+ * and (in the case of external symbols) emission of relocation directives:
+ *
+ * 32-bit (MEMB)
+ * This is a standard length for the base assembler and requires no
+ * special action.
+ *
+ * 13-bit (COBR)
+ * This is a non-standard length, but the base assembler has a hook for
+ * bit field address fixups: the fixS structure can point to a descriptor
+ * of the field, in which case our md_number_to_field() routine gets called
+ * to process it.
+ *
+ * I made the hook a little cleaner by having fix_new() (in the base
+ * assembler) return a pointer to the fixS in question. And I made it a
+ * little simpler by storing the field size (in this case 13) instead of
+ * of a pointer to another structure: 80960 displacements are ALWAYS
+ * stored in the low-order bits of a 4-byte word.
+ *
+ * Since the target of a COBR cannot be external, no relocation directives
+ * for this size displacement have to be generated. But the base assembler
+ * had to be modified to issue error messages if the symbol did turn out
+ * to be external.
+ *
+ * 24-bit (CTRL)
+ * Fixups are handled as for the 13-bit case (except that 24 is stored
+ * in the fixS).
+ *
+ * The relocation directive generated is the same as that for the 32-bit
+ * displacement, except that it's PC-relative (the 32-bit displacement
+ * never is). The i80960 version of the linker needs a mod to
+ * distinguish and handle the 24-bit case.
+ *
+ * 12-bit (MEMA)
+ * MEMA formats are always promoted to MEMB (32-bit) if the displacement
+ * is based on a symbol, because it could be relocated at link time.
+ * The only time we use the 12-bit format is if an absolute value of
+ * less than 4096 is specified, in which case we need neither a fixup nor
+ * a relocation directive.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "as.h"
+
+#include "obstack.h"
+
+#include "opcode/i960.h"
+
+extern char *input_line_pointer;
+extern struct hash_control *po_hash;
+extern char *next_object_file_charP;
+
+#ifdef OBJ_COFF
+int md_reloc_size = sizeof(struct reloc);
+#else /* OBJ_COFF */
+int md_reloc_size = sizeof(struct relocation_info);
+#endif /* OBJ_COFF */
+
+/***************************
+ * Local i80960 routines *
+ ************************** */
+
+static void brcnt_emit(); /* Emit branch-prediction instrumentation code */
+static char * brlab_next(); /* Return next branch local label */
+void brtab_emit(); /* Emit br-predict instrumentation table */
+static void cobr_fmt(); /* Generate COBR instruction */
+static void ctrl_fmt(); /* Generate CTRL instruction */
+static char * emit(); /* Emit (internally) binary */
+static int get_args(); /* Break arguments out of comma-separated list */
+static void get_cdisp(); /* Handle COBR or CTRL displacement */
+static char * get_ispec(); /* Find index specification string */
+static int get_regnum(); /* Translate text to register number */
+static int i_scan(); /* Lexical scan of instruction source */
+static void mem_fmt(); /* Generate MEMA or MEMB instruction */
+static void mema_to_memb(); /* Convert MEMA instruction to MEMB format */
+static segT parse_expr(); /* Parse an expression */
+static int parse_ldconst();/* Parse and replace a 'ldconst' pseudo-op */
+static void parse_memop(); /* Parse a memory operand */
+static void parse_po(); /* Parse machine-dependent pseudo-op */
+static void parse_regop(); /* Parse a register operand */
+static void reg_fmt(); /* Generate a REG format instruction */
+void reloc_callj(); /* Relocate a 'callj' instruction */
+static void relax_cobr(); /* "De-optimize" cobr into compare/branch */
+static void s_leafproc(); /* Process '.leafproc' pseudo-op */
+static void s_sysproc(); /* Process '.sysproc' pseudo-op */
+static int shift_ok(); /* Will a 'shlo' substiture for a 'ldconst'? */
+static void syntax(); /* Give syntax error */
+static int targ_has_sfr(); /* Target chip supports spec-func register? */
+static int targ_has_iclass();/* Target chip supports instruction set? */
+/* static void unlink_sym(); */ /* Remove a symbol from the symbol list */
+
+/* See md_parse_option() for meanings of these options */
+static char norelax = 0; /* True if -norelax switch seen */
+static char instrument_branches = 0; /* True if -b switch seen */
+
+/* Characters that always start a comment.
+ * If the pre-processor is disabled, these aren't very useful.
+ */
+char comment_chars[] = "#";
+
+/* Characters that only start a comment at the beginning of
+ * a line. If the line seems to have the form '# 123 filename'
+ * .line and .file directives will appear in the pre-processed output.
+ *
+ * Note that input_file.c hand checks for '#' at the beginning of the
+ * first line of the input file. This is because the compiler outputs
+ * #NO_APP at the beginning of its output.
+ */
+
+/* Also note that comments started like this one will always work. */
+
+char line_comment_chars[] = "";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant,
+ * as in 0f12.456 or 0d1.2345e12
+ */
+char FLT_CHARS[] = "fFdDtT";
+
+
+/* Table used by base assembler to relax addresses based on varying length
+ * instructions. The fields are:
+ * 1) most positive reach of this state,
+ * 2) most negative reach of this state,
+ * 3) how many bytes this mode will add to the size of the current frag
+ * 4) which index into the table to try if we can't fit into this one.
+ *
+ * For i80960, the only application is the (de-)optimization of cobr
+ * instructions into separate compare and branch instructions when a 13-bit
+ * displacement won't hack it.
+ */
+const relax_typeS
+ md_relax_table[] = {
+ {0, 0, 0,0}, /* State 0 => no more relaxation possible */
+ {4088, -4096, 0,2}, /* State 1: conditional branch (cobr) */
+ {0x800000-8,-0x800000,4,0}, /* State 2: compare (reg) & branch (ctrl) */
+ };
+
+
+/* These are the machine dependent pseudo-ops.
+ *
+ * This table describes all the machine specific pseudo-ops the assembler
+ * has to support. The fields are:
+ * pseudo-op name without dot
+ * function to call to execute this pseudo-op
+ * integer arg to pass to the function
+ */
+#define S_LEAFPROC 1
+#define S_SYSPROC 2
+
+const pseudo_typeS
+ md_pseudo_table[] = {
+
+ { "bss", s_lcomm, 1 },
+ { "extended", float_cons, 't' },
+ { "leafproc", parse_po, S_LEAFPROC },
+ { "sysproc", parse_po, S_SYSPROC },
+
+ { "word", cons, 4 },
+ { "quad", big_cons, 16 },
+
+ { 0, 0, 0 }
+ };
+
+/* Macros to extract info from an 'expressionS' structure 'e' */
+#define adds(e) e.X_add_symbol
+#define subs(e) e.X_subtract_symbol
+#define offs(e) e.X_add_number
+#define segs(e) e.X_seg
+
+
+ /* Branch-prediction bits for CTRL/COBR format opcodes */
+#define BP_MASK 0x00000002 /* Mask for branch-prediction bit */
+#define BP_TAKEN 0x00000000 /* Value to OR in to predict branch */
+#define BP_NOT_TAKEN 0x00000002 /* Value to OR in to predict no branch */
+
+
+ /* Some instruction opcodes that we need explicitly */
+#define BE 0x12000000
+#define BG 0x11000000
+#define BGE 0x13000000
+#define BL 0x14000000
+#define BLE 0x16000000
+#define BNE 0x15000000
+#define BNO 0x10000000
+#define BO 0x17000000
+#define CHKBIT 0x5a002700
+#define CMPI 0x5a002080
+#define CMPO 0x5a002000
+
+#define B 0x08000000
+#define BAL 0x0b000000
+#define CALL 0x09000000
+#define CALLS 0x66003800
+#define RET 0x0a000000
+
+
+ /* These masks are used to build up a set of MEMB mode bits. */
+#define A_BIT 0x0400
+#define I_BIT 0x0800
+#define MEMB_BIT 0x1000
+#define D_BIT 0x2000
+
+
+ /* Mask for the only mode bit in a MEMA instruction (if set, abase reg is used) */
+#define MEMA_ABASE 0x2000
+
+ /* Info from which a MEMA or MEMB format instruction can be generated */
+ typedef struct {
+ long opcode; /* (First) 32 bits of instruction */
+ int disp; /* 0-(none), 12- or, 32-bit displacement needed */
+ char *e; /* The expression in the source instruction from
+ * which the displacement should be determined
+ */
+ } memS;
+
+
+/* The two pieces of info we need to generate a register operand */
+struct regop {
+ int mode; /* 0 =>local/global/spec reg; 1=> literal or fp reg */
+ int special; /* 0 =>not a sfr; 1=> is a sfr (not valid w/mode=0) */
+ int n; /* Register number or literal value */
+};
+
+
+/* Number and assembler mnemonic for all registers that can appear in operands */
+static struct {
+ char *reg_name;
+ int reg_num;
+} regnames[] = {
+ { "pfp", 0 }, { "sp", 1 }, { "rip", 2 }, { "r3", 3 },
+ { "r4", 4 }, { "r5", 5 }, { "r6", 6 }, { "r7", 7 },
+ { "r8", 8 }, { "r9", 9 }, { "r10", 10 }, { "r11", 11 },
+ { "r12", 12 }, { "r13", 13 }, { "r14", 14 }, { "r15", 15 },
+ { "g0", 16 }, { "g1", 17 }, { "g2", 18 }, { "g3", 19 },
+ { "g4", 20 }, { "g5", 21 }, { "g6", 22 }, { "g7", 23 },
+ { "g8", 24 }, { "g9", 25 }, { "g10", 26 }, { "g11", 27 },
+ { "g12", 28 }, { "g13", 29 }, { "g14", 30 }, { "fp", 31 },
+
+ /* Numbers for special-function registers are for assembler internal
+ * use only: they are scaled back to range [0-31] for binary output.
+ */
+# define SF0 32
+
+ { "sf0", 32 }, { "sf1", 33 }, { "sf2", 34 }, { "sf3", 35 },
+ { "sf4", 36 }, { "sf5", 37 }, { "sf6", 38 }, { "sf7", 39 },
+ { "sf8", 40 }, { "sf9", 41 }, { "sf10",42 }, { "sf11",43 },
+ { "sf12",44 }, { "sf13",45 }, { "sf14",46 }, { "sf15",47 },
+ { "sf16",48 }, { "sf17",49 }, { "sf18",50 }, { "sf19",51 },
+ { "sf20",52 }, { "sf21",53 }, { "sf22",54 }, { "sf23",55 },
+ { "sf24",56 }, { "sf25",57 }, { "sf26",58 }, { "sf27",59 },
+ { "sf28",60 }, { "sf29",61 }, { "sf30",62 }, { "sf31",63 },
+
+ /* Numbers for floating point registers are for assembler internal use
+ * only: they are scaled back to [0-3] for binary output.
+ */
+# define FP0 64
+
+ { "fp0", 64 }, { "fp1", 65 }, { "fp2", 66 }, { "fp3", 67 },
+
+ { NULL, 0 }, /* END OF LIST */
+};
+
+#define IS_RG_REG(n) ((0 <= (n)) && ((n) < SF0))
+#define IS_SF_REG(n) ((SF0 <= (n)) && ((n) < FP0))
+#define IS_FP_REG(n) ((n) >= FP0)
+
+/* Number and assembler mnemonic for all registers that can appear as 'abase'
+ * (indirect addressing) registers.
+ */
+static struct {
+ char *areg_name;
+ int areg_num;
+} aregs[] = {
+ { "(pfp)", 0 }, { "(sp)", 1 }, { "(rip)", 2 }, { "(r3)", 3 },
+ { "(r4)", 4 }, { "(r5)", 5 }, { "(r6)", 6 }, { "(r7)", 7 },
+ { "(r8)", 8 }, { "(r9)", 9 }, { "(r10)", 10 }, { "(r11)", 11 },
+ { "(r12)", 12 }, { "(r13)", 13 }, { "(r14)", 14 }, { "(r15)", 15 },
+ { "(g0)", 16 }, { "(g1)", 17 }, { "(g2)", 18 }, { "(g3)", 19 },
+ { "(g4)", 20 }, { "(g5)", 21 }, { "(g6)", 22 }, { "(g7)", 23 },
+ { "(g8)", 24 }, { "(g9)", 25 }, { "(g10)", 26 }, { "(g11)", 27 },
+ { "(g12)", 28 }, { "(g13)", 29 }, { "(g14)", 30 }, { "(fp)", 31 },
+
+# define IPREL 32
+ /* for assembler internal use only: this number never appears in binary
+ * output.
+ */
+ { "(ip)", IPREL },
+
+ { NULL, 0 }, /* END OF LIST */
+};
+
+
+/* Hash tables */
+static struct hash_control *op_hash = NULL; /* Opcode mnemonics */
+static struct hash_control *reg_hash = NULL; /* Register name hash table */
+static struct hash_control *areg_hash = NULL; /* Abase register hash table */
+
+
+/* Architecture for which we are assembling */
+#define ARCH_ANY 0 /* Default: no architecture checking done */
+#define ARCH_KA 1
+#define ARCH_KB 2
+#define ARCH_MC 3
+#define ARCH_CA 4
+int architecture = ARCH_ANY; /* Architecture requested on invocation line */
+int iclasses_seen = 0; /* OR of instruction classes (I_* constants)
+ * for which we've actually assembled
+ * instructions.
+ */
+
+
+/* BRANCH-PREDICTION INSTRUMENTATION
+ *
+ * The following supports generation of branch-prediction instrumentation
+ * (turned on by -b switch). The instrumentation collects counts
+ * of branches taken/not-taken for later input to a utility that will
+ * set the branch prediction bits of the instructions in accordance with
+ * the behavior observed. (Note that the KX series does not have
+ * brach-prediction.)
+ *
+ * The instrumentation consists of:
+ *
+ * (1) before and after each conditional branch, a call to an external
+ * routine that increments and steps over an inline counter. The
+ * counter itself, initialized to 0, immediately follows the call
+ * instruction. For each branch, the counter following the branch
+ * is the number of times the branch was not taken, and the difference
+ * between the counters is the number of times it was taken. An
+ * example of an instrumented conditional branch:
+ *
+ * call BR_CNT_FUNC
+ * .word 0
+ * LBRANCH23: be label
+ * call BR_CNT_FUNC
+ * .word 0
+ *
+ * (2) a table of pointers to the instrumented branches, so that an
+ * external postprocessing routine can locate all of the counters.
+ * the table begins with a 2-word header: a pointer to the next in
+ * a linked list of such tables (initialized to 0); and a count
+ * of the number of entries in the table (exclusive of the header.
+ *
+ * Note that input source code is expected to already contain calls
+ * an external routine that will link the branch local table into a
+ * list of such tables.
+ */
+
+static int br_cnt = 0; /* Number of branches instrumented so far.
+ * Also used to generate unique local labels
+ * for each instrumented branch
+ */
+
+#define BR_LABEL_BASE "LBRANCH"
+/* Basename of local labels on instrumented
+ * branches, to avoid conflict with compiler-
+ * generated local labels.
+ */
+
+#define BR_CNT_FUNC "__inc_branch"
+/* Name of the external routine that will
+ * increment (and step over) an inline counter.
+ */
+
+#define BR_TAB_NAME "__BRANCH_TABLE__"
+/* Name of the table of pointers to branches.
+ * A local (i.e., non-external) symbol.
+ */
+
+/*****************************************************************************
+ * md_begin: One-time initialization.
+ *
+ * Set up hash tables.
+ *
+ **************************************************************************** */
+void
+ md_begin()
+{
+ int i; /* Loop counter */
+ const struct i960_opcode *oP; /* Pointer into opcode table */
+ char *retval; /* Value returned by hash functions */
+
+ if (((op_hash = hash_new()) == 0)
+ || ((reg_hash = hash_new()) == 0)
+ || ((areg_hash = hash_new()) == 0)) {
+ as_fatal("virtual memory exceeded");
+ }
+
+ retval = ""; /* For some reason, the base assembler uses an empty
+ * string for "no error message", instead of a NULL
+ * pointer.
+ */
+
+ for (oP=i960_opcodes; oP->name && !*retval; oP++) {
+ retval = hash_insert(op_hash, oP->name, oP);
+ }
+
+ for (i=0; regnames[i].reg_name && !*retval; i++) {
+ retval = hash_insert(reg_hash, regnames[i].reg_name,
+ &regnames[i].reg_num);
+ }
+
+ for (i=0; aregs[i].areg_name && !*retval; i++){
+ retval = hash_insert(areg_hash, aregs[i].areg_name,
+ &aregs[i].areg_num);
+ }
+
+ if (*retval) {
+ as_fatal("Hashing returned \"%s\".", retval);
+ }
+} /* md_begin() */
+
+/*****************************************************************************
+ * md_end: One-time final cleanup
+ *
+ * None necessary
+ *
+ **************************************************************************** */
+void
+ md_end()
+{
+}
+
+/*****************************************************************************
+ * md_assemble: Assemble an instruction
+ *
+ * Assumptions about the passed-in text:
+ * - all comments, labels removed
+ * - text is an instruction
+ * - all white space compressed to single blanks
+ * - all character constants have been replaced with decimal
+ *
+ **************************************************************************** */
+void
+ md_assemble(textP)
+char *textP; /* Source text of instruction */
+{
+ char *args[4]; /* Parsed instruction text, containing NO whitespace:
+ * arg[0]->opcode mnemonic
+ * arg[1-3]->operands, with char constants
+ * replaced by decimal numbers
+ */
+ int n_ops; /* Number of instruction operands */
+
+ struct i960_opcode *oP;
+ /* Pointer to instruction description */
+ int branch_predict;
+ /* TRUE iff opcode mnemonic included branch-prediction
+ * suffix (".f" or ".t")
+ */
+ long bp_bits; /* Setting of branch-prediction bit(s) to be OR'd
+ * into instruction opcode of CTRL/COBR format
+ * instructions.
+ */
+ int n; /* Offset of last character in opcode mnemonic */
+
+ static const char bp_error_msg[] = "branch prediction invalid on this opcode";
+
+
+ /* Parse instruction into opcode and operands */
+ memset(args, '\0', sizeof(args));
+ n_ops = i_scan(textP, args);
+ if (n_ops == -1){
+ return; /* Error message already issued */
+ }
+
+ /* Do "macro substitution" (sort of) on 'ldconst' pseudo-instruction */
+ if (!strcmp(args[0],"ldconst")){
+ n_ops = parse_ldconst(args);
+ if (n_ops == -1){
+ return;
+ }
+ }
+
+ /* Check for branch-prediction suffix on opcode mnemonic, strip it off */
+ n = strlen(args[0]) - 1;
+ branch_predict = 0;
+ bp_bits = 0;
+ if (args[0][n-1] == '.' && (args[0][n] == 't' || args[0][n] == 'f')){
+ /* We could check here to see if the target architecture
+ * supports branch prediction, but why bother? The bit
+ * will just be ignored by processors that don't use it.
+ */
+ branch_predict = 1;
+ bp_bits = (args[0][n] == 't') ? BP_TAKEN : BP_NOT_TAKEN;
+ args[0][n-1] = '\0'; /* Strip suffix from opcode mnemonic */
+ }
+
+ /* Look up opcode mnemonic in table and check number of operands.
+ * Check that opcode is legal for the target architecture.
+ * If all looks good, assemble instruction.
+ */
+ oP = (struct i960_opcode *) hash_find(op_hash, args[0]);
+ if (!oP || !targ_has_iclass(oP->iclass)) {
+ as_bad("invalid opcode, \"%s\".", args[0]);
+
+ } else if (n_ops != oP->num_ops) {
+ as_bad("improper number of operands. expecting %d, got %d", oP->num_ops, n_ops);
+
+ } else {
+ switch (oP->format){
+ case FBRA:
+ case CTRL:
+ ctrl_fmt(args[1], oP->opcode | bp_bits, oP->num_ops);
+ if (oP->format == FBRA){
+ /* Now generate a 'bno' to same arg */
+ ctrl_fmt(args[1], BNO | bp_bits, 1);
+ }
+ break;
+ case COBR:
+ case COJ:
+ cobr_fmt(args, oP->opcode | bp_bits, oP);
+ break;
+ case REG:
+ if (branch_predict){
+ as_warn(bp_error_msg);
+ }
+ reg_fmt(args, oP);
+ break;
+ case MEM1:
+ case MEM2:
+ case MEM4:
+ case MEM8:
+ case MEM12:
+ case MEM16:
+ if (branch_predict){
+ as_warn(bp_error_msg);
+ }
+ mem_fmt(args, oP);
+ break;
+ case CALLJ:
+ if (branch_predict){
+ as_warn(bp_error_msg);
+ }
+ /* Output opcode & set up "fixup" (relocation);
+ * flag relocation as 'callj' type.
+ */
+ know(oP->num_ops == 1);
+ get_cdisp(args[1], "CTRL", oP->opcode, 24, 0, 1);
+ break;
+ default:
+ BAD_CASE(oP->format);
+ break;
+ }
+ }
+} /* md_assemble() */
+
+/*****************************************************************************
+ * md_number_to_chars: convert a number to target byte order
+ *
+ **************************************************************************** */
+void
+ md_number_to_chars(buf, value, n)
+char *buf; /* Put output here */
+long value; /* The integer to be converted */
+int n; /* Number of bytes to output (significant bytes
+ * in 'value')
+ */
+{
+ while (n--){
+ *buf++ = value;
+ value >>= 8;
+ }
+
+ /* XXX line number probably botched for this warning message. */
+ if (value != 0 && value != -1){
+ as_bad("Displacement too long for instruction field length.");
+ }
+
+ return;
+} /* md_number_to_chars() */
+
+/*****************************************************************************
+ * md_chars_to_number: convert from target byte order to host byte order.
+ *
+ **************************************************************************** */
+int
+ md_chars_to_number(val, n)
+unsigned char *val; /* Value in target byte order */
+int n; /* Number of bytes in the input */
+{
+ int retval;
+
+ for (retval=0; n--;){
+ retval <<= 8;
+ retval |= val[n];
+ }
+ return retval;
+}
+
+
+#define MAX_LITTLENUMS 6
+#define LNUM_SIZE sizeof(LITTLENUM_TYPE)
+
+/*****************************************************************************
+ * md_atof: convert ascii to floating point
+ *
+ * Turn a string at input_line_pointer into a floating point constant of type
+ * 'type', and store the appropriate bytes at *litP. The number of LITTLENUMS
+ * emitted is returned at 'sizeP'. An error message is returned, or a pointer
+ * to an empty message if OK.
+ *
+ * Note we call the i386 floating point routine, rather than complicating
+ * things with more files or symbolic links.
+ *
+ **************************************************************************** */
+char * md_atof(type, litP, sizeP)
+int type;
+char *litP;
+int *sizeP;
+{
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ int prec;
+ char *t;
+ char *atof_ieee();
+
+ switch (type) {
+ case 'f':
+ case 'F':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ prec = 4;
+ break;
+
+ case 't':
+ case 'T':
+ prec = 5;
+ type = 'x'; /* That's what atof_ieee() understands */
+ break;
+
+ default:
+ *sizeP=0;
+ return "Bad call to md_atof()";
+ }
+
+ t = atof_ieee(input_line_pointer, type, words);
+ if (t){
+ input_line_pointer = t;
+ }
+
+ *sizeP = prec * LNUM_SIZE;
+
+ /* Output the LITTLENUMs in REVERSE order in accord with i80960
+ * word-order. (Dunno why atof_ieee doesn't do it in the right
+ * order in the first place -- probably because it's a hack of
+ * atof_m68k.)
+ */
+
+ for (wordP = words + prec - 1; prec--;){
+ md_number_to_chars(litP, (long) (*wordP--), LNUM_SIZE);
+ litP += sizeof(LITTLENUM_TYPE);
+ }
+
+ return ""; /* Someone should teach Dean about null pointers */
+}
+
+
+/*****************************************************************************
+ * md_number_to_imm
+ *
+ **************************************************************************** */
+void
+ md_number_to_imm(buf, val, n)
+char *buf;
+long val;
+int n;
+{
+ md_number_to_chars(buf, val, n);
+}
+
+
+/*****************************************************************************
+ * md_number_to_disp
+ *
+ **************************************************************************** */
+void
+ md_number_to_disp(buf, val, n)
+char *buf;
+long val;
+int n;
+{
+ md_number_to_chars(buf, val, n);
+}
+
+/*****************************************************************************
+ * md_number_to_field:
+ *
+ * Stick a value (an address fixup) into a bit field of
+ * previously-generated instruction.
+ *
+ **************************************************************************** */
+void
+ md_number_to_field(instrP, val, bfixP)
+char *instrP; /* Pointer to instruction to be fixed */
+long val; /* Address fixup value */
+bit_fixS *bfixP; /* Description of bit field to be fixed up */
+{
+ int numbits; /* Length of bit field to be fixed */
+ long instr; /* 32-bit instruction to be fixed-up */
+ long sign; /* 0 or -1, according to sign bit of 'val' */
+
+ /* Convert instruction back to host byte order
+ */
+ instr = md_chars_to_number(instrP, 4);
+
+ /* Surprise! -- we stored the number of bits
+ * to be modified rather than a pointer to a structure.
+ */
+ numbits = (int)bfixP;
+ if (numbits == 1){
+ /* This is a no-op, stuck here by reloc_callj() */
+ return;
+ }
+
+ know ((numbits == 13) || (numbits == 24));
+
+ /* Propagate sign bit of 'val' for the given number of bits.
+ * Result should be all 0 or all 1
+ */
+ sign = val >> ((int)numbits - 1);
+ if (((val < 0) && (sign != -1))
+ || ((val > 0) && (sign != 0))){
+ as_bad("Fixup of %d too large for field width of %d",
+ val, numbits);
+ } else {
+ /* Put bit field into instruction and write back in target
+ * byte order.
+ */
+ val &= ~(-1 << (int)numbits); /* Clear unused sign bits */
+ instr |= val;
+ md_number_to_chars(instrP, instr, 4);
+ }
+} /* md_number_to_field() */
+
+
+/*****************************************************************************
+ * md_parse_option
+ * Invocation line includes a switch not recognized by the base assembler.
+ * See if it's a processor-specific option. For the 960, these are:
+ *
+ * -norelax:
+ * Conditional branch instructions that require displacements
+ * greater than 13 bits (or that have external targets) should
+ * generate errors. The default is to replace each such
+ * instruction with the corresponding compare (or chkbit) and
+ * branch instructions. Note that the Intel "j" cobr directives
+ * are ALWAYS "de-optimized" in this way when necessary,
+ * regardless of the setting of this option.
+ *
+ * -b:
+ * Add code to collect information about branches taken, for
+ * later optimization of branch prediction bits by a separate
+ * tool. COBR and CNTL format instructions have branch
+ * prediction bits (in the CX architecture); if "BR" represents
+ * an instruction in one of these classes, the following rep-
+ * resents the code generated by the assembler:
+ *
+ * call <increment routine>
+ * .word 0 # pre-counter
+ * Label: BR
+ * call <increment routine>
+ * .word 0 # post-counter
+ *
+ * A table of all such "Labels" is also generated.
+ *
+ *
+ * -AKA, -AKB, -AKC, -ASA, -ASB, -AMC, -ACA:
+ * Select the 80960 architecture. Instructions or features not
+ * supported by the selected architecture cause fatal errors.
+ * The default is to generate code for any instruction or feature
+ * that is supported by SOME version of the 960 (even if this
+ * means mixing architectures!).
+ *
+ **************************************************************************** */
+int
+ md_parse_option(argP, cntP, vecP)
+char **argP;
+int *cntP;
+char ***vecP;
+{
+ char *p;
+ struct tabentry { char *flag; int arch; };
+ static struct tabentry arch_tab[] = {
+ "KA", ARCH_KA,
+ "KB", ARCH_KB,
+ "SA", ARCH_KA, /* Synonym for KA */
+ "SB", ARCH_KB, /* Synonym for KB */
+ "KC", ARCH_MC, /* Synonym for MC */
+ "MC", ARCH_MC,
+ "CA", ARCH_CA,
+ NULL, 0
+ };
+ struct tabentry *tp;
+
+ if (!strcmp(*argP,"norelax")){
+ norelax = 1;
+
+ } else if (**argP == 'b'){
+ instrument_branches = 1;
+
+ } else if (**argP == 'A'){
+ p = (*argP) + 1;
+
+ for (tp = arch_tab; tp->flag != NULL; tp++){
+ if (!strcmp(p,tp->flag)){
+ break;
+ }
+ }
+
+ if (tp->flag == NULL){
+ as_bad("unknown architecture: %s", p);
+ } else {
+ architecture = tp->arch;
+ }
+ } else {
+ /* Unknown option */
+ (*argP)++;
+ return 0;
+ }
+ **argP = '\0'; /* Done parsing this switch */
+ return 1;
+}
+
+/*****************************************************************************
+ * md_convert_frag:
+ * Called by base assembler after address relaxation is finished: modify
+ * variable fragments according to how much relaxation was done.
+ *
+ * If the fragment substate is still 1, a 13-bit displacement was enough
+ * to reach the symbol in question. Set up an address fixup, but otherwise
+ * leave the cobr instruction alone.
+ *
+ * If the fragment substate is 2, a 13-bit displacement was not enough.
+ * Replace the cobr with a two instructions (a compare and a branch).
+ *
+ **************************************************************************** */
+void
+ md_convert_frag(headers, fragP)
+object_headers *headers;
+fragS * fragP;
+{
+ fixS *fixP; /* Structure describing needed address fix */
+
+ switch (fragP->fr_subtype){
+ case 1:
+ /* LEAVE SINGLE COBR INSTRUCTION */
+ fixP = fix_new(fragP,
+ fragP->fr_opcode-fragP->fr_literal,
+ 4,
+ fragP->fr_symbol,
+ 0,
+ fragP->fr_offset,
+ 1,
+ 0);
+
+ fixP->fx_bit_fixP = (bit_fixS *) 13; /* size of bit field */
+ break;
+ case 2:
+ /* REPLACE COBR WITH COMPARE/BRANCH INSTRUCTIONS */
+ relax_cobr(fragP);
+ break;
+ default:
+ BAD_CASE(fragP->fr_subtype);
+ break;
+ }
+}
+
+/*****************************************************************************
+ * md_estimate_size_before_relax: How much does it look like *fragP will grow?
+ *
+ * Called by base assembler just before address relaxation.
+ * Return the amount by which the fragment will grow.
+ *
+ * Any symbol that is now undefined will not become defined; cobr's
+ * based on undefined symbols will have to be replaced with a compare
+ * instruction and a branch instruction, and the code fragment will grow
+ * by 4 bytes.
+ *
+ **************************************************************************** */
+int
+ md_estimate_size_before_relax(fragP, segment_type)
+register fragS *fragP;
+register segT segment_type;
+{
+ /* If symbol is undefined in this segment, go to "relaxed" state
+ * (compare and branch instructions instead of cobr) right now.
+ */
+ if (S_GET_SEGMENT(fragP->fr_symbol) != segment_type) {
+ relax_cobr(fragP);
+ return 4;
+ }
+ return 0;
+} /* md_estimate_size_before_relax() */
+
+
+/*****************************************************************************
+ * md_ri_to_chars:
+ * This routine exists in order to overcome machine byte-order problems
+ * when dealing with bit-field entries in the relocation_info struct.
+ *
+ * But relocation info will be used on the host machine only (only
+ * executable code is actually downloaded to the i80960). Therefore,
+ * we leave it in host byte order.
+ *
+ **************************************************************************** */
+void md_ri_to_chars(where, ri)
+char *where;
+struct relocation_info *ri;
+{
+ *((struct relocation_info *) where) = *ri; /* structure assignment */
+} /* md_ri_to_chars() */
+
+#ifndef WORKING_DOT_WORD
+
+int md_short_jump_size = 0;
+int md_long_jump_size = 0;
+
+void md_create_short_jump(ptr, from_addr, to_addr, frag, to_symbol)
+char *ptr;
+long from_addr;
+long to_addr;
+fragS *frag;
+symbolS *to_symbol;
+{
+ as_fatal("failed sanity check.");
+}
+
+void
+ md_create_long_jump(ptr,from_addr,to_addr,frag,to_symbol)
+char *ptr;
+long from_addr, to_addr;
+fragS *frag;
+symbolS *to_symbol;
+{
+ as_fatal("failed sanity check.");
+}
+#endif
+
+/*************************************************************
+ * *
+ * FOLLOWING ARE THE LOCAL ROUTINES, IN ALPHABETICAL ORDER *
+ * *
+ ************************************************************ */
+
+
+
+/*****************************************************************************
+ * brcnt_emit: Emit code to increment inline branch counter.
+ *
+ * See the comments above the declaration of 'br_cnt' for details on
+ * branch-prediction instrumentation.
+ **************************************************************************** */
+static void
+ brcnt_emit()
+{
+ ctrl_fmt(BR_CNT_FUNC,CALL,1);/* Emit call to "increment" routine */
+ emit(0); /* Emit inline counter to be incremented */
+}
+
+/*****************************************************************************
+ * brlab_next: generate the next branch local label
+ *
+ * See the comments above the declaration of 'br_cnt' for details on
+ * branch-prediction instrumentation.
+ **************************************************************************** */
+static char *
+ brlab_next()
+{
+ static char buf[20];
+
+ sprintf(buf, "%s%d", BR_LABEL_BASE, br_cnt++);
+ return buf;
+}
+
+/*****************************************************************************
+ * brtab_emit: generate the fetch-prediction branch table.
+ *
+ * See the comments above the declaration of 'br_cnt' for details on
+ * branch-prediction instrumentation.
+ *
+ * The code emitted here would be functionally equivalent to the following
+ * example assembler source.
+ *
+ * .data
+ * .align 2
+ * BR_TAB_NAME:
+ * .word 0 # link to next table
+ * .word 3 # length of table
+ * .word LBRANCH0 # 1st entry in table proper
+ * .word LBRANCH1
+ * .word LBRANCH2
+ ***************************************************************************** */
+void
+ brtab_emit()
+{
+ int i;
+ char buf[20];
+ char *p; /* Where the binary was output to */
+ fixS *fixP; /*->description of deferred address fixup */
+
+ if (!instrument_branches){
+ return;
+ }
+
+ subseg_new(SEG_DATA,0); /* .data */
+ frag_align(2,0); /* .align 2 */
+ record_alignment(now_seg,2);
+ colon(BR_TAB_NAME); /* BR_TAB_NAME: */
+ emit(0); /* .word 0 #link to next table */
+ emit(br_cnt); /* .word n #length of table */
+
+ for (i=0; i<br_cnt; i++){
+ sprintf(buf, "%s%d", BR_LABEL_BASE, i);
+ p = emit(0);
+ fixP = fix_new(frag_now,
+ p - frag_now->fr_literal,
+ 4,
+ symbol_find(buf),
+ 0,
+ 0,
+ 0,
+ 0);
+ fixP->fx_im_disp = 2; /* 32-bit displacement fix */
+ }
+}
+
+/*****************************************************************************
+ * cobr_fmt: generate a COBR-format instruction
+ *
+ **************************************************************************** */
+static
+ void
+ cobr_fmt(arg, opcode, oP)
+char *arg[]; /* arg[0]->opcode mnemonic, arg[1-3]->operands (ascii) */
+long opcode; /* Opcode, with branch-prediction bits already set
+ * if necessary.
+ */
+struct i960_opcode *oP;
+/*->description of instruction */
+{
+ long instr; /* 32-bit instruction */
+ struct regop regop; /* Description of register operand */
+ int n; /* Number of operands */
+ int var_frag; /* 1 if varying length code fragment should
+ * be emitted; 0 if an address fix
+ * should be emitted.
+ */
+
+ instr = opcode;
+ n = oP->num_ops;
+
+ if (n >= 1) {
+ /* First operand (if any) of a COBR is always a register
+ * operand. Parse it.
+ */
+ parse_regop(&regop, arg[1], oP->operand[0]);
+ instr |= (regop.n << 19) | (regop.mode << 13);
+ }
+ if (n >= 2) {
+ /* Second operand (if any) of a COBR is always a register
+ * operand. Parse it.
+ */
+ parse_regop(&regop, arg[2], oP->operand[1]);
+ instr |= (regop.n << 14) | regop.special;
+ }
+
+
+ if (n < 3){
+ emit(instr);
+
+ } else {
+ if (instrument_branches){
+ brcnt_emit();
+ colon(brlab_next());
+ }
+
+ /* A third operand to a COBR is always a displacement.
+ * Parse it; if it's relaxable (a cobr "j" directive, or any
+ * cobr other than bbs/bbc when the "-norelax" option is not in
+ * use) set up a variable code fragment; otherwise set up an
+ * address fix.
+ */
+ var_frag = !norelax || (oP->format == COJ); /* TRUE or FALSE */
+ get_cdisp(arg[3], "COBR", instr, 13, var_frag, 0);
+
+ if (instrument_branches){
+ brcnt_emit();
+ }
+ }
+} /* cobr_fmt() */
+
+
+/*****************************************************************************
+ * ctrl_fmt: generate a CTRL-format instruction
+ *
+ **************************************************************************** */
+static
+ void
+ ctrl_fmt(targP, opcode, num_ops)
+char *targP; /* Pointer to text of lone operand (if any) */
+long opcode; /* Template of instruction */
+int num_ops; /* Number of operands */
+{
+ int instrument; /* TRUE iff we should add instrumentation to track
+ * how often the branch is taken
+ */
+
+
+ if (num_ops == 0){
+ emit(opcode); /* Output opcode */
+ } else {
+
+ instrument = instrument_branches && (opcode != CALL)
+ && (opcode != B) && (opcode != RET) && (opcode != BAL);
+
+ if (instrument){
+ brcnt_emit();
+ colon(brlab_next());
+ }
+
+ /* The operand MUST be an ip-relative displacment. Parse it
+ * and set up address fix for the instruction we just output.
+ */
+ get_cdisp(targP, "CTRL", opcode, 24, 0, 0);
+
+ if (instrument){
+ brcnt_emit();
+ }
+ }
+
+}
+
+
+/*****************************************************************************
+ * emit: output instruction binary
+ *
+ * Output instruction binary, in target byte order, 4 bytes at a time.
+ * Return pointer to where it was placed.
+ *
+ **************************************************************************** */
+static
+ char *
+ emit(instr)
+long instr; /* Word to be output, host byte order */
+{
+ char *toP; /* Where to output it */
+
+ toP = frag_more(4); /* Allocate storage */
+ md_number_to_chars(toP, instr, 4); /* Convert to target byte order */
+ return toP;
+}
+
+
+/*****************************************************************************
+ * get_args: break individual arguments out of comma-separated list
+ *
+ * Input assumptions:
+ * - all comments and labels have been removed
+ * - all strings of whitespace have been collapsed to a single blank.
+ * - all character constants ('x') have been replaced with decimal
+ *
+ * Output:
+ * args[0] is untouched. args[1] points to first operand, etc. All args:
+ * - are NULL-terminated
+ * - contain no whitespace
+ *
+ * Return value:
+ * Number of operands (0,1,2, or 3) or -1 on error.
+ *
+ **************************************************************************** */
+static int get_args(p, args)
+register char *p; /* Pointer to comma-separated operands; MUCKED BY US */
+char *args[]; /* Output arg: pointers to operands placed in args[1-3].
+ * MUST ACCOMMODATE 4 ENTRIES (args[0-3]).
+ */
+{
+ register int n; /* Number of operands */
+ register char *to;
+ /* char buf[4]; */
+ /* int len; */
+
+
+ /* Skip lead white space */
+ while (*p == ' '){
+ p++;
+ }
+
+ if (*p == '\0'){
+ return 0;
+ }
+
+ n = 1;
+ args[1] = p;
+
+ /* Squeze blanks out by moving non-blanks toward start of string.
+ * Isolate operands, whenever comma is found.
+ */
+ to = p;
+ while (*p != '\0'){
+
+ if (*p == ' '){
+ p++;
+
+ } else if (*p == ','){
+
+ /* Start of operand */
+ if (n == 3){
+ as_bad("too many operands");
+ return -1;
+ }
+ *to++ = '\0'; /* Terminate argument */
+ args[++n] = to; /* Start next argument */
+ p++;
+
+ } else {
+ *to++ = *p++;
+ }
+ }
+ *to = '\0';
+ return n;
+}
+
+
+/*****************************************************************************
+ * get_cdisp: handle displacement for a COBR or CTRL instruction.
+ *
+ * Parse displacement for a COBR or CTRL instruction.
+ *
+ * If successful, output the instruction opcode and set up for it,
+ * depending on the arg 'var_frag', either:
+ * o an address fixup to be done when all symbol values are known, or
+ * o a varying length code fragment, with address fixup info. This
+ * will be done for cobr instructions that may have to be relaxed
+ * in to compare/branch instructions (8 bytes) if the final address
+ * displacement is greater than 13 bits.
+ *
+ **************************************************************************** */
+static
+ void
+ get_cdisp(dispP, ifmtP, instr, numbits, var_frag, callj)
+char *dispP; /*->displacement as specified in source instruction */
+char *ifmtP; /*->"COBR" or "CTRL" (for use in error message) */
+long instr; /* Instruction needing the displacement */
+int numbits; /* # bits of displacement (13 for COBR, 24 for CTRL) */
+int var_frag; /* 1 if varying length code fragment should be emitted;
+ * 0 if an address fix should be emitted.
+ */
+int callj; /* 1 if callj relocation should be done; else 0 */
+{
+ expressionS e; /* Parsed expression */
+ fixS *fixP; /* Structure describing needed address fix */
+ char *outP; /* Where instruction binary is output to */
+
+ fixP = NULL;
+
+ switch (parse_expr(dispP,&e)) {
+
+ case SEG_GOOF:
+ as_bad("expression syntax error");
+ break;
+
+ case SEG_TEXT:
+ case SEG_UNKNOWN:
+ if (var_frag) {
+ outP = frag_more(8); /* Allocate worst-case storage */
+ md_number_to_chars(outP, instr, 4);
+ frag_variant(rs_machine_dependent, 4, 4, 1,
+ adds(e), offs(e), outP, 0, 0);
+ } else {
+ /* Set up a new fix structure, so address can be updated
+ * when all symbol values are known.
+ */
+ outP = emit(instr);
+ fixP = fix_new(frag_now,
+ outP - frag_now->fr_literal,
+ 4,
+ adds(e),
+ 0,
+ offs(e),
+ 1,
+ 0);
+
+ fixP->fx_callj = callj;
+
+ /* We want to modify a bit field when the address is
+ * known. But we don't need all the garbage in the
+ * bit_fix structure. So we're going to lie and store
+ * the number of bits affected instead of a pointer.
+ */
+ fixP->fx_bit_fixP = (bit_fixS *) numbits;
+ }
+ break;
+
+ case SEG_DATA:
+ case SEG_BSS:
+ as_bad("attempt to branch into different segment");
+ break;
+
+ default:
+ as_bad("target of %s instruction must be a label", ifmtP);
+ break;
+ }
+}
+
+
+/*****************************************************************************
+ * get_ispec: parse a memory operand for an index specification
+ *
+ * Here, an "index specification" is taken to be anything surrounded
+ * by square brackets and NOT followed by anything else.
+ *
+ * If it's found, detach it from the input string, remove the surrounding
+ * square brackets, and return a pointer to it. Otherwise, return NULL.
+ *
+ **************************************************************************** */
+static
+ char *
+ get_ispec(textP)
+char *textP; /*->memory operand from source instruction, no white space */
+{
+ char *start; /*->start of index specification */
+ char *end; /*->end of index specification */
+
+ /* Find opening square bracket, if any
+ */
+ start = strchr(textP, '[');
+
+ if (start != NULL){
+
+ /* Eliminate '[', detach from rest of operand */
+ *start++ = '\0';
+
+ end = strchr(start, ']');
+
+ if (end == NULL){
+ as_bad("unmatched '['");
+
+ } else {
+ /* Eliminate ']' and make sure it was the last thing
+ * in the string.
+ */
+ *end = '\0';
+ if (*(end+1) != '\0'){
+ as_bad("garbage after index spec ignored");
+ }
+ }
+ }
+ return start;
+}
+
+/*****************************************************************************
+ * get_regnum:
+ *
+ * Look up a (suspected) register name in the register table and return the
+ * associated register number (or -1 if not found).
+ *
+ **************************************************************************** */
+static
+ int
+ get_regnum(regname)
+char *regname; /* Suspected register name */
+{
+ int *rP;
+
+ rP = (int *) hash_find(reg_hash, regname);
+ return (rP == NULL) ? -1 : *rP;
+}
+
+
+/*****************************************************************************
+ * i_scan: perform lexical scan of ascii assembler instruction.
+ *
+ * Input assumptions:
+ * - input string is an i80960 instruction (not a pseudo-op)
+ * - all comments and labels have been removed
+ * - all strings of whitespace have been collapsed to a single blank.
+ *
+ * Output:
+ * args[0] points to opcode, other entries point to operands. All strings:
+ * - are NULL-terminated
+ * - contain no whitespace
+ * - have character constants ('x') replaced with a decimal number
+ *
+ * Return value:
+ * Number of operands (0,1,2, or 3) or -1 on error.
+ *
+ **************************************************************************** */
+static int i_scan(iP, args)
+register char *iP; /* Pointer to ascii instruction; MUCKED BY US. */
+char *args[]; /* Output arg: pointers to opcode and operands placed
+ * here. MUST ACCOMMODATE 4 ENTRIES.
+ */
+{
+
+ /* Isolate opcode */
+ if (*(iP) == ' ') {
+ iP++;
+ } /* Skip lead space, if any */
+ args[0] = iP;
+ for (; *iP != ' '; iP++) {
+ if (*iP == '\0') {
+ /* There are no operands */
+ if (args[0] == iP) {
+ /* We never moved: there was no opcode either! */
+ as_bad("missing opcode");
+ return -1;
+ }
+ return 0;
+ }
+ }
+ *iP++ = '\0'; /* Terminate opcode */
+ return(get_args(iP, args));
+} /* i_scan() */
+
+
+/*****************************************************************************
+ * mem_fmt: generate a MEMA- or MEMB-format instruction
+ *
+ **************************************************************************** */
+static void mem_fmt(args, oP)
+char *args[]; /* args[0]->opcode mnemonic, args[1-3]->operands */
+struct i960_opcode *oP; /* Pointer to description of instruction */
+{
+ int i; /* Loop counter */
+ struct regop regop; /* Description of register operand */
+ char opdesc; /* Operand descriptor byte */
+ memS instr; /* Description of binary to be output */
+ char *outP; /* Where the binary was output to */
+ expressionS expr; /* Parsed expression */
+ fixS *fixP; /*->description of deferred address fixup */
+
+ memset(&instr, '\0', sizeof(memS));
+ instr.opcode = oP->opcode;
+
+ /* Process operands. */
+ for (i = 1; i <= oP->num_ops; i++){
+ opdesc = oP->operand[i-1];
+
+ if (MEMOP(opdesc)){
+ parse_memop(&instr, args[i], oP->format);
+ } else {
+ parse_regop(&regop, args[i], opdesc);
+ instr.opcode |= regop.n << 19;
+ }
+ }
+
+ /* Output opcode */
+ outP = emit(instr.opcode);
+
+ if (instr.disp == 0){
+ return;
+ }
+
+ /* Parse and process the displacement */
+ switch (parse_expr(instr.e,&expr)){
+
+ case SEG_GOOF:
+ as_bad("expression syntax error");
+ break;
+
+ case SEG_ABSOLUTE:
+ if (instr.disp == 32){
+ (void) emit(offs(expr)); /* Output displacement */
+ } else {
+ /* 12-bit displacement */
+ if (offs(expr) & ~0xfff){
+ /* Won't fit in 12 bits: convert already-output
+ * instruction to MEMB format, output
+ * displacement.
+ */
+ mema_to_memb(outP);
+ (void) emit(offs(expr));
+ } else {
+ /* WILL fit in 12 bits: OR into opcode and
+ * overwrite the binary we already put out
+ */
+ instr.opcode |= offs(expr);
+ md_number_to_chars(outP, instr.opcode, 4);
+ }
+ }
+ break;
+
+ case SEG_DIFFERENCE:
+ case SEG_TEXT:
+ case SEG_DATA:
+ case SEG_BSS:
+ case SEG_UNKNOWN:
+ if (instr.disp == 12){
+ /* Displacement is dependent on a symbol, whose value
+ * may change at link time. We HAVE to reserve 32 bits.
+ * Convert already-output opcode to MEMB format.
+ */
+ mema_to_memb(outP);
+ }
+
+ /* Output 0 displacement and set up address fixup for when
+ * this symbol's value becomes known.
+ */
+ outP = emit((long) 0);
+ fixP = fix_new(frag_now,
+ outP - frag_now->fr_literal,
+ 4,
+ adds(expr),
+ subs(expr),
+ offs(expr),
+ 0,
+ 0);
+ fixP->fx_im_disp = 2; /* 32-bit displacement fix */
+ break;
+
+ default:
+ BAD_CASE(segs(expr));
+ break;
+ }
+} /* memfmt() */
+
+
+/*****************************************************************************
+ * mema_to_memb: convert a MEMA-format opcode to a MEMB-format opcode.
+ *
+ * There are 2 possible MEMA formats:
+ * - displacement only
+ * - displacement + abase
+ *
+ * They are distinguished by the setting of the MEMA_ABASE bit.
+ *
+ **************************************************************************** */
+static void mema_to_memb(opcodeP)
+char *opcodeP; /* Where to find the opcode, in target byte order */
+{
+ long opcode; /* Opcode in host byte order */
+ long mode; /* Mode bits for MEMB instruction */
+
+ opcode = md_chars_to_number(opcodeP, 4);
+ know(!(opcode & MEMB_BIT));
+
+ mode = MEMB_BIT | D_BIT;
+ if (opcode & MEMA_ABASE){
+ mode |= A_BIT;
+ }
+
+ opcode &= 0xffffc000; /* Clear MEMA offset and mode bits */
+ opcode |= mode; /* Set MEMB mode bits */
+
+ md_number_to_chars(opcodeP, opcode, 4);
+} /* mema_to_memb() */
+
+
+/*****************************************************************************
+ * parse_expr: parse an expression
+ *
+ * Use base assembler's expression parser to parse an expression.
+ * It, unfortunately, runs off a global which we have to save/restore
+ * in order to make it work for us.
+ *
+ * An empty expression string is treated as an absolute 0.
+ *
+ * Return "segment" to which the expression evaluates.
+ * Return SEG_GOOF regardless of expression evaluation if entire input
+ * string is not consumed in the evaluation -- tolerate no dangling junk!
+ *
+ **************************************************************************** */
+static
+ segT
+ parse_expr(textP, expP)
+char *textP; /* Text of expression to be parsed */
+expressionS *expP; /* Where to put the results of parsing */
+{
+ char *save_in; /* Save global here */
+ segT seg; /* Segment to which expression evaluates */
+ symbolS *symP;
+
+ know(textP);
+
+ if (*textP == '\0') {
+ /* Treat empty string as absolute 0 */
+ expP->X_add_symbol = expP->X_subtract_symbol = NULL;
+ expP->X_add_number = 0;
+ seg = expP->X_seg = SEG_ABSOLUTE;
+
+ } else {
+ save_in = input_line_pointer; /* Save global */
+ input_line_pointer = textP; /* Make parser work for us */
+
+ seg = expression(expP);
+ if (input_line_pointer - textP != strlen(textP)) {
+ /* Did not consume all of the input */
+ seg = SEG_GOOF;
+ }
+ symP = expP->X_add_symbol;
+ if (symP && (hash_find(reg_hash, S_GET_NAME(symP)))) {
+ /* Register name in an expression */
+ seg = SEG_GOOF;
+ }
+
+ input_line_pointer = save_in; /* Restore global */
+ }
+ return seg;
+}
+
+
+/*****************************************************************************
+ * parse_ldcont:
+ * Parse and replace a 'ldconst' pseudo-instruction with an appropriate
+ * i80960 instruction.
+ *
+ * Assumes the input consists of:
+ * arg[0] opcode mnemonic ('ldconst')
+ * arg[1] first operand (constant)
+ * arg[2] name of register to be loaded
+ *
+ * Replaces opcode and/or operands as appropriate.
+ *
+ * Returns the new number of arguments, or -1 on failure.
+ *
+ **************************************************************************** */
+static
+ int
+ parse_ldconst(arg)
+char *arg[]; /* See above */
+{
+ int n; /* Constant to be loaded */
+ int shift; /* Shift count for "shlo" instruction */
+ static char buf[5]; /* Literal for first operand */
+ static char buf2[5]; /* Literal for second operand */
+ expressionS e; /* Parsed expression */
+
+
+ arg[3] = NULL; /* So we can tell at the end if it got used or not */
+
+ switch (parse_expr(arg[1],&e)){
+
+ case SEG_TEXT:
+ case SEG_DATA:
+ case SEG_BSS:
+ case SEG_UNKNOWN:
+ case SEG_DIFFERENCE:
+ /* We're dependent on one or more symbols -- use "lda" */
+ arg[0] = "lda";
+ break;
+
+ case SEG_ABSOLUTE:
+ /* Try the following mappings:
+ * ldconst 0,<reg> ->mov 0,<reg>
+ * ldconst 31,<reg> ->mov 31,<reg>
+ * ldconst 32,<reg> ->addo 1,31,<reg>
+ * ldconst 62,<reg> ->addo 31,31,<reg>
+ * ldconst 64,<reg> ->shlo 8,3,<reg>
+ * ldconst -1,<reg> ->subo 1,0,<reg>
+ * ldconst -31,<reg>->subo 31,0,<reg>
+ *
+ * anthing else becomes:
+ * lda xxx,<reg>
+ */
+ n = offs(e);
+ if ((0 <= n) && (n <= 31)){
+ arg[0] = "mov";
+
+ } else if ((-31 <= n) && (n <= -1)){
+ arg[0] = "subo";
+ arg[3] = arg[2];
+ sprintf(buf, "%d", -n);
+ arg[1] = buf;
+ arg[2] = "0";
+
+ } else if ((32 <= n) && (n <= 62)){
+ arg[0] = "addo";
+ arg[3] = arg[2];
+ arg[1] = "31";
+ sprintf(buf, "%d", n-31);
+ arg[2] = buf;
+
+ } else if ((shift = shift_ok(n)) != 0){
+ arg[0] = "shlo";
+ arg[3] = arg[2];
+ sprintf(buf, "%d", shift);
+ arg[1] = buf;
+ sprintf(buf2, "%d", n >> shift);
+ arg[2] = buf2;
+
+ } else {
+ arg[0] = "lda";
+ }
+ break;
+
+ default:
+ as_bad("invalid constant");
+ return -1;
+ break;
+ }
+ return (arg[3] == 0) ? 2: 3;
+}
+
+/*****************************************************************************
+ * parse_memop: parse a memory operand
+ *
+ * This routine is based on the observation that the 4 mode bits of the
+ * MEMB format, taken individually, have fairly consistent meaning:
+ *
+ * M3 (bit 13): 1 if displacement is present (D_BIT)
+ * M2 (bit 12): 1 for MEMB instructions (MEMB_BIT)
+ * M1 (bit 11): 1 if index is present (I_BIT)
+ * M0 (bit 10): 1 if abase is present (A_BIT)
+ *
+ * So we parse the memory operand and set bits in the mode as we find
+ * things. Then at the end, if we go to MEMB format, we need only set
+ * the MEMB bit (M2) and our mode is built for us.
+ *
+ * Unfortunately, I said "fairly consistent". The exceptions:
+ *
+ * DBIA
+ * 0100 Would seem illegal, but means "abase-only".
+ *
+ * 0101 Would seem to mean "abase-only" -- it means IP-relative.
+ * Must be converted to 0100.
+ *
+ * 0110 Would seem to mean "index-only", but is reserved.
+ * We turn on the D bit and provide a 0 displacement.
+ *
+ * The other thing to observe is that we parse from the right, peeling
+ * things * off as we go: first any index spec, then any abase, then
+ * the displacement.
+ *
+ **************************************************************************** */
+static
+ void
+ parse_memop(memP, argP, optype)
+memS *memP; /* Where to put the results */
+char *argP; /* Text of the operand to be parsed */
+int optype; /* MEM1, MEM2, MEM4, MEM8, MEM12, or MEM16 */
+{
+ char *indexP; /* Pointer to index specification with "[]" removed */
+ char *p; /* Temp char pointer */
+ char iprel_flag;/* True if this is an IP-relative operand */
+ int regnum; /* Register number */
+ int scale; /* Scale factor: 1,2,4,8, or 16. Later converted
+ * to internal format (0,1,2,3,4 respectively).
+ */
+ int mode; /* MEMB mode bits */
+ int *intP; /* Pointer to register number */
+
+ /* The following table contains the default scale factors for each
+ * type of memory instruction. It is accessed using (optype-MEM1)
+ * as an index -- thus it assumes the 'optype' constants are assigned
+ * consecutive values, in the order they appear in this table
+ */
+ static int def_scale[] = {
+ 1, /* MEM1 */
+ 2, /* MEM2 */
+ 4, /* MEM4 */
+ 8, /* MEM8 */
+ -1, /* MEM12 -- no valid default */
+ 16 /* MEM16 */
+ };
+
+
+ iprel_flag = mode = 0;
+
+ /* Any index present? */
+ indexP = get_ispec(argP);
+ if (indexP) {
+ p = strchr(indexP, '*');
+ if (p == NULL) {
+ /* No explicit scale -- use default for this
+ *instruction type.
+ */
+ scale = def_scale[ optype - MEM1 ];
+ } else {
+ *p++ = '\0'; /* Eliminate '*' */
+
+ /* Now indexP->a '\0'-terminated register name,
+ * and p->a scale factor.
+ */
+
+ if (!strcmp(p,"16")){
+ scale = 16;
+ } else if (strchr("1248",*p) && (p[1] == '\0')){
+ scale = *p - '0';
+ } else {
+ scale = -1;
+ }
+ }
+
+ regnum = get_regnum(indexP); /* Get index reg. # */
+ if (!IS_RG_REG(regnum)){
+ as_bad("invalid index register");
+ return;
+ }
+
+ /* Convert scale to its binary encoding */
+ switch (scale){
+ case 1: scale = 0 << 7; break;
+ case 2: scale = 1 << 7; break;
+ case 4: scale = 2 << 7; break;
+ case 8: scale = 3 << 7; break;
+ case 16: scale = 4 << 7; break;
+ default: as_bad("invalid scale factor"); return;
+ };
+
+ memP->opcode |= scale | regnum; /* Set index bits in opcode */
+ mode |= I_BIT; /* Found a valid index spec */
+ }
+
+ /* Any abase (Register Indirect) specification present? */
+ if ((p = strrchr(argP,'(')) != NULL) {
+ /* "(" is there -- does it start a legal abase spec?
+ * (If not it could be part of a displacement expression.)
+ */
+ intP = (int *) hash_find(areg_hash, p);
+ if (intP != NULL){
+ /* Got an abase here */
+ regnum = *intP;
+ *p = '\0'; /* discard register spec */
+ if (regnum == IPREL){
+ /* We have to specialcase ip-rel mode */
+ iprel_flag = 1;
+ } else {
+ memP->opcode |= regnum << 14;
+ mode |= A_BIT;
+ }
+ }
+ }
+
+ /* Any expression present? */
+ memP->e = argP;
+ if (*argP != '\0'){
+ mode |= D_BIT;
+ }
+
+ /* Special-case ip-relative addressing */
+ if (iprel_flag){
+ if (mode & I_BIT){
+ syntax();
+ } else {
+ memP->opcode |= 5 << 10; /* IP-relative mode */
+ memP->disp = 32;
+ }
+ return;
+ }
+
+ /* Handle all other modes */
+ switch (mode){
+ case D_BIT | A_BIT:
+ /* Go with MEMA instruction format for now (grow to MEMB later
+ * if 12 bits is not enough for the displacement).
+ * MEMA format has a single mode bit: set it to indicate
+ * that abase is present.
+ */
+ memP->opcode |= MEMA_ABASE;
+ memP->disp = 12;
+ break;
+
+ case D_BIT:
+ /* Go with MEMA instruction format for now (grow to MEMB later
+ * if 12 bits is not enough for the displacement).
+ */
+ memP->disp = 12;
+ break;
+
+ case A_BIT:
+ /* For some reason, the bit string for this mode is not
+ * consistent: it should be 0 (exclusive of the MEMB bit),
+ * so we set it "by hand" here.
+ */
+ memP->opcode |= MEMB_BIT;
+ break;
+
+ case A_BIT | I_BIT:
+ /* set MEMB bit in mode, and OR in mode bits */
+ memP->opcode |= mode | MEMB_BIT;
+ break;
+
+ case I_BIT:
+ /* Treat missing displacement as displacement of 0 */
+ mode |= D_BIT;
+ /***********************
+ * Fall into next case *
+ ********************** */
+ case D_BIT | A_BIT | I_BIT:
+ case D_BIT | I_BIT:
+ /* set MEMB bit in mode, and OR in mode bits */
+ memP->opcode |= mode | MEMB_BIT;
+ memP->disp = 32;
+ break;
+
+ default:
+ syntax();
+ break;
+ }
+}
+
+/*****************************************************************************
+ * parse_po: parse machine-dependent pseudo-op
+ *
+ * This is a top-level routine for machine-dependent pseudo-ops. It slurps
+ * up the rest of the input line, breaks out the individual arguments,
+ * and dispatches them to the correct handler.
+ **************************************************************************** */
+static
+ void
+ parse_po(po_num)
+int po_num; /* Pseudo-op number: currently S_LEAFPROC or S_SYSPROC */
+{
+ char *args[4]; /* Pointers operands, with no embedded whitespace.
+ * arg[0] unused.
+ * arg[1-3]->operands
+ */
+ int n_ops; /* Number of operands */
+ char *p; /* Pointer to beginning of unparsed argument string */
+ char eol; /* Character that indicated end of line */
+
+ extern char is_end_of_line[];
+
+ /* Advance input pointer to end of line. */
+ p = input_line_pointer;
+ while (!is_end_of_line[ *input_line_pointer ]){
+ input_line_pointer++;
+ }
+ eol = *input_line_pointer; /* Save end-of-line char */
+ *input_line_pointer = '\0'; /* Terminate argument list */
+
+ /* Parse out operands */
+ n_ops = get_args(p, args);
+ if (n_ops == -1){
+ return;
+ }
+
+ /* Dispatch to correct handler */
+ switch (po_num){
+ case S_SYSPROC: s_sysproc(n_ops, args); break;
+ case S_LEAFPROC: s_leafproc(n_ops, args); break;
+ default: BAD_CASE(po_num); break;
+ }
+
+ /* Restore eol, so line numbers get updated correctly. Base assembler
+ * assumes we leave input pointer pointing at char following the eol.
+ */
+ *input_line_pointer++ = eol;
+}
+
+/*****************************************************************************
+ * parse_regop: parse a register operand.
+ *
+ * In case of illegal operand, issue a message and return some valid
+ * information so instruction processing can continue.
+ **************************************************************************** */
+static
+ void
+ parse_regop(regopP, optext, opdesc)
+struct regop *regopP; /* Where to put description of register operand */
+char *optext; /* Text of operand */
+char opdesc; /* Descriptor byte: what's legal for this operand */
+{
+ int n; /* Register number */
+ expressionS e; /* Parsed expression */
+
+ /* See if operand is a register */
+ n = get_regnum(optext);
+ if (n >= 0){
+ if (IS_RG_REG(n)){
+ /* global or local register */
+ if (!REG_ALIGN(opdesc,n)){
+ as_bad("unaligned register");
+ }
+ regopP->n = n;
+ regopP->mode = 0;
+ regopP->special = 0;
+ return;
+ } else if (IS_FP_REG(n) && FP_OK(opdesc)){
+ /* Floating point register, and it's allowed */
+ regopP->n = n - FP0;
+ regopP->mode = 1;
+ regopP->special = 0;
+ return;
+ } else if (IS_SF_REG(n) && SFR_OK(opdesc)){
+ /* Special-function register, and it's allowed */
+ regopP->n = n - SF0;
+ regopP->mode = 0;
+ regopP->special = 1;
+ if (!targ_has_sfr(regopP->n)){
+ as_bad("no such sfr in this architecture");
+ }
+ return;
+ }
+ } else if (LIT_OK(opdesc)){
+ /*
+ * How about a literal?
+ */
+ regopP->mode = 1;
+ regopP->special = 0;
+ if (FP_OK(opdesc)){ /* floating point literal acceptable */
+ /* Skip over 0f, 0d, or 0e prefix */
+ if ( (optext[0] == '0')
+ && (optext[1] >= 'd')
+ && (optext[1] <= 'f') ){
+ optext += 2;
+ }
+
+ if (!strcmp(optext,"0.0") || !strcmp(optext,"0") ){
+ regopP->n = 0x10;
+ return;
+ }
+ if (!strcmp(optext,"1.0") || !strcmp(optext,"1") ){
+ regopP->n = 0x16;
+ return;
+ }
+
+ } else { /* fixed point literal acceptable */
+ if ((parse_expr(optext,&e) != SEG_ABSOLUTE)
+ || (offs(e) < 0) || (offs(e) > 31)){
+ as_bad("illegal literal");
+ offs(e) = 0;
+ }
+ regopP->n = offs(e);
+ return;
+ }
+ }
+
+ /* Nothing worked */
+ syntax();
+ regopP->mode = 0; /* Register r0 is always a good one */
+ regopP->n = 0;
+ regopP->special = 0;
+} /* parse_regop() */
+
+/*****************************************************************************
+ * reg_fmt: generate a REG-format instruction
+ *
+ **************************************************************************** */
+static void reg_fmt(args, oP)
+char *args[]; /* args[0]->opcode mnemonic, args[1-3]->operands */
+struct i960_opcode *oP; /* Pointer to description of instruction */
+{
+ long instr; /* Binary to be output */
+ struct regop regop; /* Description of register operand */
+ int n_ops; /* Number of operands */
+
+
+ instr = oP->opcode;
+ n_ops = oP->num_ops;
+
+ if (n_ops >= 1){
+ parse_regop(&regop, args[1], oP->operand[0]);
+
+ if ((n_ops == 1) && !(instr & M3)){
+ /* 1-operand instruction in which the dst field should
+ * be used (instead of src1).
+ */
+ regop.n <<= 19;
+ if (regop.special){
+ regop.mode = regop.special;
+ }
+ regop.mode <<= 13;
+ regop.special = 0;
+ } else {
+ /* regop.n goes in bit 0, needs no shifting */
+ regop.mode <<= 11;
+ regop.special <<= 5;
+ }
+ instr |= regop.n | regop.mode | regop.special;
+ }
+
+ if (n_ops >= 2) {
+ parse_regop(&regop, args[2], oP->operand[1]);
+
+ if ((n_ops == 2) && !(instr & M3)){
+ /* 2-operand instruction in which the dst field should
+ * be used instead of src2).
+ */
+ regop.n <<= 19;
+ if (regop.special){
+ regop.mode = regop.special;
+ }
+ regop.mode <<= 13;
+ regop.special = 0;
+ } else {
+ regop.n <<= 14;
+ regop.mode <<= 12;
+ regop.special <<= 6;
+ }
+ instr |= regop.n | regop.mode | regop.special;
+ }
+ if (n_ops == 3){
+ parse_regop(&regop, args[3], oP->operand[2]);
+ if (regop.special){
+ regop.mode = regop.special;
+ }
+ instr |= (regop.n <<= 19) | (regop.mode <<= 13);
+ }
+ emit(instr);
+}
+
+
+/*****************************************************************************
+ * relax_cobr:
+ * Replace cobr instruction in a code fragment with equivalent branch and
+ * compare instructions, so it can reach beyond a 13-bit displacement.
+ * Set up an address fix/relocation for the new branch instruction.
+ *
+ **************************************************************************** */
+
+/* This "conditional jump" table maps cobr instructions into equivalent
+ * compare and branch opcodes.
+ */
+static
+ struct {
+ long compare;
+ long branch;
+ } coj[] = { /* COBR OPCODE: */
+ CHKBIT, BNO, /* 0x30 - bbc */
+ CMPO, BG, /* 0x31 - cmpobg */
+ CMPO, BE, /* 0x32 - cmpobe */
+ CMPO, BGE, /* 0x33 - cmpobge */
+ CMPO, BL, /* 0x34 - cmpobl */
+ CMPO, BNE, /* 0x35 - cmpobne */
+ CMPO, BLE, /* 0x36 - cmpoble */
+ CHKBIT, BO, /* 0x37 - bbs */
+ CMPI, BNO, /* 0x38 - cmpibno */
+ CMPI, BG, /* 0x39 - cmpibg */
+ CMPI, BE, /* 0x3a - cmpibe */
+ CMPI, BGE, /* 0x3b - cmpibge */
+ CMPI, BL, /* 0x3c - cmpibl */
+ CMPI, BNE, /* 0x3d - cmpibne */
+ CMPI, BLE, /* 0x3e - cmpible */
+ CMPI, BO, /* 0x3f - cmpibo */
+ };
+
+static
+ void
+ relax_cobr(fragP)
+register fragS *fragP; /* fragP->fr_opcode is assumed to point to
+ * the cobr instruction, which comes at the
+ * end of the code fragment.
+ */
+{
+ int opcode, src1, src2, m1, s2;
+ /* Bit fields from cobr instruction */
+ long bp_bits; /* Branch prediction bits from cobr instruction */
+ long instr; /* A single i960 instruction */
+ char *iP; /*->instruction to be replaced */
+ fixS *fixP; /* Relocation that can be done at assembly time */
+
+ /* PICK UP & PARSE COBR INSTRUCTION */
+ iP = fragP->fr_opcode;
+ instr = md_chars_to_number(iP, 4);
+ opcode = ((instr >> 24) & 0xff) - 0x30; /* "-0x30" for table index */
+ src1 = (instr >> 19) & 0x1f;
+ m1 = (instr >> 13) & 1;
+ s2 = instr & 1;
+ src2 = (instr >> 14) & 0x1f;
+ bp_bits= instr & BP_MASK;
+
+ /* GENERATE AND OUTPUT COMPARE INSTRUCTION */
+ instr = coj[opcode].compare
+ | src1 | (m1 << 11) | (s2 << 6) | (src2 << 14);
+ md_number_to_chars(iP, instr, 4);
+
+ /* OUTPUT BRANCH INSTRUCTION */
+ md_number_to_chars(iP+4, coj[opcode].branch | bp_bits, 4);
+
+ /* SET UP ADDRESS FIXUP/RELOCATION */
+ fixP = fix_new(fragP,
+ iP+4 - fragP->fr_literal,
+ 4,
+ fragP->fr_symbol,
+ 0,
+ fragP->fr_offset,
+ 1,
+ 0);
+
+ fixP->fx_bit_fixP = (bit_fixS *) 24; /* Store size of bit field */
+
+ fragP->fr_fix += 4;
+ frag_wane(fragP);
+}
+
+
+/*****************************************************************************
+ * reloc_callj: Relocate a 'callj' instruction
+ *
+ * This is a "non-(GNU)-standard" machine-dependent hook. The base
+ * assembler calls it when it decides it can relocate an address at
+ * assembly time instead of emitting a relocation directive.
+ *
+ * Check to see if the relocation involves a 'callj' instruction to a:
+ * sysproc: Replace the default 'call' instruction with a 'calls'
+ * leafproc: Replace the default 'call' instruction with a 'bal'.
+ * other proc: Do nothing.
+ *
+ * See b.out.h for details on the 'n_other' field in a symbol structure.
+ *
+ * IMPORTANT!:
+ * Assumes the caller has already figured out, in the case of a leafproc,
+ * to use the 'bal' entry point, and has substituted that symbol into the
+ * passed fixup structure.
+ *
+ **************************************************************************** */
+void reloc_callj(fixP)
+fixS *fixP; /* Relocation that can be done at assembly time */
+{
+ char *where; /*->the binary for the instruction being relocated */
+
+ if (!fixP->fx_callj) {
+ return;
+ } /* This wasn't a callj instruction in the first place */
+
+ where = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ if (TC_S_IS_SYSPROC(fixP->fx_addsy)) {
+ /* Symbol is a .sysproc: replace 'call' with 'calls'.
+ * System procedure number is (other-1).
+ */
+ md_number_to_chars(where, CALLS|TC_S_GET_SYSPROC(fixP->fx_addsy), 4);
+
+ /* Nothing else needs to be done for this instruction.
+ * Make sure 'md_number_to_field()' will perform a no-op.
+ */
+ fixP->fx_bit_fixP = (bit_fixS *) 1;
+
+ } else if (TC_S_IS_CALLNAME(fixP->fx_addsy)) {
+ /* Should not happen: see block comment above */
+ as_fatal("Trying to 'bal' to %s", S_GET_NAME(fixP->fx_addsy));
+
+ } else if (TC_S_IS_BALNAME(fixP->fx_addsy)) {
+ /* Replace 'call' with 'bal'; both instructions have
+ * the same format, so calling code should complete
+ * relocation as if nothing happened here.
+ */
+ md_number_to_chars(where, BAL, 4);
+ } else if (TC_S_IS_BADPROC(fixP->fx_addsy)) {
+ as_bad("Looks like a proc, but can't tell what kind.\n");
+ } /* switch on proc type */
+
+ /* else Symbol is neither a sysproc nor a leafproc */
+
+ return;
+} /* reloc_callj() */
+
+
+/*****************************************************************************
+ * s_leafproc: process .leafproc pseudo-op
+ *
+ * .leafproc takes two arguments, the second one is optional:
+ * arg[1]: name of 'call' entry point to leaf procedure
+ * arg[2]: name of 'bal' entry point to leaf procedure
+ *
+ * If the two arguments are identical, or if the second one is missing,
+ * the first argument is taken to be the 'bal' entry point.
+ *
+ * If there are 2 distinct arguments, we must make sure that the 'bal'
+ * entry point immediately follows the 'call' entry point in the linked
+ * list of symbols.
+ *
+ **************************************************************************** */
+static void s_leafproc(n_ops, args)
+int n_ops; /* Number of operands */
+char *args[]; /* args[1]->1st operand, args[2]->2nd operand */
+{
+ symbolS *callP; /* Pointer to leafproc 'call' entry point symbol */
+ symbolS *balP; /* Pointer to leafproc 'bal' entry point symbol */
+
+ if ((n_ops != 1) && (n_ops != 2)) {
+ as_bad("should have 1 or 2 operands");
+ return;
+ } /* Check number of arguments */
+
+ /* Find or create symbol for 'call' entry point. */
+ callP = symbol_find_or_make(args[1]);
+
+ if (TC_S_IS_CALLNAME(callP)) {
+ as_warn("Redefining leafproc %s", S_GET_NAME(callP));
+ } /* is leafproc */
+
+ /* If that was the only argument, use it as the 'bal' entry point.
+ * Otherwise, mark it as the 'call' entry point and find or create
+ * another symbol for the 'bal' entry point.
+ */
+ if ((n_ops == 1) || !strcmp(args[1],args[2])) {
+ TC_S_FORCE_TO_BALNAME(callP);
+
+ } else {
+ TC_S_FORCE_TO_CALLNAME(callP);
+
+ balP = symbol_find_or_make(args[2]);
+ if (TC_S_IS_CALLNAME(balP)) {
+ as_warn("Redefining leafproc %s", S_GET_NAME(balP));
+ }
+ TC_S_FORCE_TO_BALNAME(balP);
+
+ tc_set_bal_of_call(callP, balP);
+ } /* if only one arg, or the args are the same */
+
+ return;
+} /* s_leafproc() */
+
+
+/*
+ * s_sysproc: process .sysproc pseudo-op
+ *
+ * .sysproc takes two arguments:
+ * arg[1]: name of entry point to system procedure
+ * arg[2]: 'entry_num' (index) of system procedure in the range
+ * [0,31] inclusive.
+ *
+ * For [ab].out, we store the 'entrynum' in the 'n_other' field of
+ * the symbol. Since that entry is normally 0, we bias 'entrynum'
+ * by adding 1 to it. It must be unbiased before it is used.
+ */
+static void s_sysproc(n_ops, args)
+int n_ops; /* Number of operands */
+char *args[]; /* args[1]->1st operand, args[2]->2nd operand */
+{
+ expressionS exp;
+ symbolS *symP;
+
+ if (n_ops != 2) {
+ as_bad("should have two operands");
+ return;
+ } /* bad arg count */
+
+ /* Parse "entry_num" argument and check it for validity. */
+ if ((parse_expr(args[2],&exp) != SEG_ABSOLUTE)
+ || (offs(exp) < 0)
+ || (offs(exp) > 31)) {
+ as_bad("'entry_num' must be absolute number in [0,31]");
+ return;
+ }
+
+ /* Find/make symbol and stick entry number (biased by +1) into it */
+ symP = symbol_find_or_make(args[1]);
+
+ if (TC_S_IS_SYSPROC(symP)) {
+ as_warn("Redefining entrynum for sysproc %s", S_GET_NAME(symP));
+ } /* redefining */
+
+ TC_S_SET_SYSPROC(symP, offs(exp)); /* encode entry number */
+ TC_S_FORCE_TO_SYSPROC(symP);
+
+ return;
+} /* s_sysproc() */
+
+
+/*****************************************************************************
+ * shift_ok:
+ * Determine if a "shlo" instruction can be used to implement a "ldconst".
+ * This means that some number X < 32 can be shifted left to produce the
+ * constant of interest.
+ *
+ * Return the shift count, or 0 if we can't do it.
+ * Caller calculates X by shifting original constant right 'shift' places.
+ *
+ **************************************************************************** */
+static
+ int
+ shift_ok(n)
+int n; /* The constant of interest */
+{
+ int shift; /* The shift count */
+
+ if (n <= 0){
+ /* Can't do it for negative numbers */
+ return 0;
+ }
+
+ /* Shift 'n' right until a 1 is about to be lost */
+ for (shift = 0; (n & 1) == 0; shift++){
+ n >>= 1;
+ }
+
+ if (n >= 32){
+ return 0;
+ }
+ return shift;
+}
+
+
+/*****************************************************************************
+ * syntax: issue syntax error
+ *
+ **************************************************************************** */
+static void syntax() {
+ as_bad("syntax error");
+} /* syntax() */
+
+
+/*****************************************************************************
+ * targ_has_sfr:
+ * Return TRUE iff the target architecture supports the specified
+ * special-function register (sfr).
+ *
+ **************************************************************************** */
+static
+ int
+ targ_has_sfr(n)
+int n; /* Number (0-31) of sfr */
+{
+ switch (architecture){
+ case ARCH_KA:
+ case ARCH_KB:
+ case ARCH_MC:
+ return 0;
+ case ARCH_CA:
+ default:
+ return ((0 <= n) && (n <= 2));
+ }
+}
+
+
+/*****************************************************************************
+ * targ_has_iclass:
+ * Return TRUE iff the target architecture supports the indicated
+ * class of instructions.
+ *
+ **************************************************************************** */
+static
+ int
+ targ_has_iclass(ic)
+int ic; /* Instruction class; one of:
+ * I_BASE, I_CX, I_DEC, I_KX, I_FP, I_MIL, I_CASIM
+ */
+{
+ iclasses_seen |= ic;
+ switch (architecture){
+ case ARCH_KA: return ic & (I_BASE | I_KX);
+ case ARCH_KB: return ic & (I_BASE | I_KX | I_FP | I_DEC);
+ case ARCH_MC: return ic & (I_BASE | I_KX | I_FP | I_DEC | I_MIL);
+ case ARCH_CA: return ic & (I_BASE | I_CX | I_CASIM);
+ default:
+ if ((iclasses_seen & (I_KX|I_FP|I_DEC|I_MIL))
+ && (iclasses_seen & I_CX)){
+ as_warn("architecture of opcode conflicts with that of earlier instruction(s)");
+ iclasses_seen &= ~ic;
+ }
+ return 1;
+ }
+}
+
+
+/* Parse an operand that is machine-specific.
+ We just return without modifying the expression if we have nothing
+ to do. */
+
+/* ARGSUSED */
+void
+ md_operand (expressionP)
+expressionS *expressionP;
+{
+}
+
+/* We have no need to default values of symbols. */
+
+/* ARGSUSED */
+symbolS *md_undefined_symbol(name)
+char *name;
+{
+ return 0;
+} /* md_undefined_symbol() */
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the i960, they're relative to the address of the instruction,
+ which we have set up as the address of the fixup too. */
+long
+ md_pcrel_from (fixP)
+fixS *fixP;
+{
+ return fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+void
+ md_apply_fix(fixP, val)
+fixS *fixP;
+long val;
+{
+ char *place = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ if (!fixP->fx_bit_fixP) {
+
+ switch (fixP->fx_im_disp) {
+ case 0:
+ fixP->fx_addnumber = val;
+ md_number_to_imm(place, val, fixP->fx_size, fixP);
+ break;
+ case 1:
+ md_number_to_disp(place,
+ fixP->fx_pcrel ? val + fixP->fx_pcrel_adjust : val,
+ fixP->fx_size);
+ break;
+ case 2: /* fix requested for .long .word etc */
+ md_number_to_chars(place, val, fixP->fx_size);
+ break;
+ default:
+ as_fatal("Internal error in md_apply_fix() in file \"%s\"", __FILE__);
+ } /* OVE: maybe one ought to put _imm _disp _chars in one md-func */
+ } else {
+ md_number_to_field(place, val, fixP->fx_bit_fixP);
+ }
+
+ return;
+} /* md_apply_fix() */
+
+#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
+void tc_bout_fix_to_chars(where, fixP, segment_address_in_file)
+char *where;
+fixS *fixP;
+relax_addressT segment_address_in_file;
+{
+ static unsigned char nbytes_r_length[] = { 42, 0, 1, 42, 2 };
+ struct relocation_info ri;
+ symbolS *symbolP;
+
+ /* JF this is for paranoia */
+ memset((char *)&ri, '\0', sizeof(ri));
+
+ know((symbolP = fixP->fx_addsy) != 0);
+
+ /* These two 'cuz of NS32K */
+ ri.r_callj = fixP->fx_callj;
+
+ ri.r_length = nbytes_r_length[fixP->fx_size];
+ ri.r_pcrel = fixP->fx_pcrel;
+ ri.r_address = fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file;
+
+ if (!S_IS_DEFINED(symbolP)) {
+ ri.r_extern = 1;
+ ri.r_index = symbolP->sy_number;
+ } else {
+ ri.r_extern = 0;
+ ri.r_index = S_GET_TYPE(symbolP);
+ }
+
+ /* Output the relocation information in machine-dependent form. */
+ md_ri_to_chars(where, &ri);
+
+ return;
+} /* tc_bout_fix_to_chars() */
+
+#endif /* OBJ_AOUT or OBJ_BOUT */
+
+/* Align an address by rounding it up to the specified boundary.
+ */
+long md_section_align(seg, addr)
+segT seg;
+long addr; /* Address to be rounded up */
+{
+ return((addr + (1 << section_alignment[(int) seg]) - 1) & (-1 << section_alignment[(int) seg]));
+} /* md_section_align() */
+
+#ifdef OBJ_COFF
+void tc_headers_hook(headers)
+object_headers *headers;
+{
+ /* FIXME: remove this line */ /* unsigned short arch_flag = 0; */
+
+ if ((iclasses_seen == I_BASE) || (iclasses_seen == 0)) {
+ headers->filehdr.f_flags |= F_I960CORE;
+ } else if (iclasses_seen & I_CX){
+ headers->filehdr.f_flags |= F_I960CA;
+ } else if (iclasses_seen & I_MIL){
+ headers->filehdr.f_flags |= F_I960MC;
+ } else if (iclasses_seen & (I_DEC|I_FP)){
+ headers->filehdr.f_flags |= F_I960KB;
+ } else {
+ headers->filehdr.f_flags |= F_I960KA;
+ } /* set arch flag */
+
+ if (flagseen['R']) {
+ headers->filehdr.f_magic = I960RWMAGIC;
+ headers->aouthdr.magic = OMAGIC;
+ } else {
+ headers->filehdr.f_magic = I960ROMAGIC;
+ headers->aouthdr.magic = NMAGIC;
+ } /* set magic numbers */
+
+ return;
+} /* tc_headers_hook() */
+#endif /* OBJ_COFF */
+
+/*
+ * Things going on here:
+ *
+ * For bout, We need to assure a couple of simplifying
+ * assumptions about leafprocs for the linker: the leafproc
+ * entry symbols will be defined in the same assembly in
+ * which they're declared with the '.leafproc' directive;
+ * and if a leafproc has both 'call' and 'bal' entry points
+ * they are both global or both local.
+ *
+ * For coff, the call symbol has a second aux entry that
+ * contains the bal entry point. The bal symbol becomes a
+ * label.
+ *
+ * For coff representation, the call symbol has a second aux entry that
+ * contains the bal entry point. The bal symbol becomes a label.
+ *
+ */
+
+void tc_crawl_symbol_chain(headers)
+object_headers *headers;
+{
+ symbolS *symbolP;
+
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) {
+#ifdef OBJ_COFF
+ if (TC_S_IS_SYSPROC(symbolP)) {
+ /* second aux entry already contains the sysproc number */
+ S_SET_NUMBER_AUXILIARY(symbolP, 2);
+ S_SET_STORAGE_CLASS(symbolP, C_SCALL);
+ S_SET_DATA_TYPE(symbolP, S_GET_DATA_TYPE(symbolP) | (DT_FCN << N_BTSHFT));
+ continue;
+ } /* rewrite sysproc */
+#endif /* OBJ_COFF */
+
+ if (!TC_S_IS_BALNAME(symbolP) && !TC_S_IS_CALLNAME(symbolP)) {
+ continue;
+ } /* Not a leafproc symbol */
+
+ if (!S_IS_DEFINED(symbolP)) {
+ as_bad("leafproc symbol '%s' undefined", S_GET_NAME(symbolP));
+ } /* undefined leaf */
+
+ if (TC_S_IS_CALLNAME(symbolP)) {
+ symbolS *balP = tc_get_bal_of_call(symbolP);
+ if (S_IS_EXTERNAL(symbolP) != S_IS_EXTERNAL(balP)) {
+ S_SET_EXTERNAL(symbolP);
+ S_SET_EXTERNAL(balP);
+ as_warn("Warning: making leafproc entries %s and %s both global\n",
+ S_GET_NAME(symbolP), S_GET_NAME(balP));
+ } /* externality mismatch */
+ } /* if callname */
+ } /* walk the symbol chain */
+
+ return;
+} /* tc_crawl_symbol_chain() */
+
+/*
+ * For aout or bout, the bal immediately follows the call.
+ *
+ * For coff, we cheat and store a pointer to the bal symbol
+ * in the second aux entry of the call.
+ */
+
+void tc_set_bal_of_call(callP, balP)
+symbolS *callP;
+symbolS *balP;
+{
+ know(TC_S_IS_CALLNAME(callP));
+ know(TC_S_IS_BALNAME(balP));
+
+#ifdef OBJ_COFF
+
+ callP->sy_symbol.ost_auxent[1].x_bal.x_balntry = (int) balP;
+ S_SET_NUMBER_AUXILIARY(callP,2);
+
+#elif defined(OBJ_AOUT) || defined(OBJ_BOUT)
+
+ /* If the 'bal' entry doesn't immediately follow the 'call'
+ * symbol, unlink it from the symbol list and re-insert it.
+ */
+ if (symbol_next(callP) != balP) {
+ symbol_remove(balP, &symbol_rootP, &symbol_lastP);
+ symbol_append(balP, callP, &symbol_rootP, &symbol_lastP);
+ } /* if not in order */
+
+#else
+ (as yet unwritten.);
+#endif /* switch on OBJ_FORMAT */
+
+ return;
+} /* tc_set_bal_of_call() */
+
+char *_tc_get_bal_of_call(callP)
+symbolS *callP;
+{
+ symbolS *retval;
+
+ know(TC_S_IS_CALLNAME(callP));
+
+#ifdef OBJ_COFF
+ retval = (symbolS *) (callP->sy_symbol.ost_auxent[1].x_bal.x_balntry);
+#elif defined(OBJ_AOUT) || defined(OBJ_BOUT)
+ retval = symbol_next(callP);
+#else
+ (as yet unwritten.);
+#endif /* switch on OBJ_FORMAT */
+
+ know(TC_S_IS_BALNAME(retval));
+ return((char *) retval);
+} /* _tc_get_bal_of_call() */
+
+void tc_coff_symbol_emit_hook(symbolP)
+symbolS *symbolP;
+{
+ if (TC_S_IS_CALLNAME(symbolP)) {
+#ifdef OBJ_COFF
+ symbolS *balP = tc_get_bal_of_call(symbolP);
+
+ /* second aux entry contains the bal entry point */
+ /* S_SET_NUMBER_AUXILIARY(symbolP, 2); */
+ symbolP->sy_symbol.ost_auxent[1].x_bal.x_balntry = S_GET_VALUE(balP);
+ S_SET_STORAGE_CLASS(symbolP, (!SF_GET_LOCAL(symbolP) ? C_LEAFEXT : C_LEAFSTAT));
+ S_SET_DATA_TYPE(symbolP, S_GET_DATA_TYPE(symbolP) | (DT_FCN << N_BTSHFT));
+ /* fix up the bal symbol */
+ S_SET_STORAGE_CLASS(balP, C_LABEL);
+#endif /* OBJ_COFF */
+ } /* only on calls */
+
+ return;
+} /* tc_coff_symbol_emit_hook() */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of tc-i960.c */
diff --git a/gnu/usr.bin/as/config/tc-i960.h b/gnu/usr.bin/as/config/tc-i960.h
new file mode 100644
index 0000000..caad4d6
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-i960.h
@@ -0,0 +1,281 @@
+/* tc-i960.h - Basic 80960 instruction formats.
+ Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with GAS; see the file COPYING. If not, write
+ to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef TC_I960
+#define TC_I960 1
+
+#define NO_LISTING
+
+/*
+ * The 'COJ' instructions are actually COBR instructions with the 'b' in
+ * the mnemonic replaced by a 'j'; they are ALWAYS "de-optimized" if necessary:
+ * if the displacement will not fit in 13 bits, the assembler will replace them
+ * with the corresponding compare and branch instructions.
+ *
+ * All of the 'MEMn' instructions are the same format; the 'n' in the name
+ * indicates the default index scale factor (the size of the datum operated on).
+ *
+ * The FBRA formats are not actually an instruction format. They are the
+ * "convenience directives" for branching on floating-point comparisons,
+ * each of which generates 2 instructions (a 'bno' and one other branch).
+ *
+ * The CALLJ format is not actually an instruction format. It indicates that
+ * the instruction generated (a CTRL-format 'call') should have its relocation
+ * specially flagged for link-time replacement with a 'bal' or 'calls' if
+ * appropriate.
+ */
+
+/* tailor gas */
+#define SYMBOLS_NEED_BACKPOINTERS
+#define LOCAL_LABELS_FB
+#define WANT_BITFIELDS
+
+/* tailor the coff format */
+#define OBJ_COFF_SECTION_HEADER_HAS_ALIGNMENT
+#define OBJ_COFF_MAX_AUXENTRIES (2)
+
+/* other */
+#define CTRL 0
+#define COBR 1
+#define COJ 2
+#define REG 3
+#define MEM1 4
+#define MEM2 5
+#define MEM4 6
+#define MEM8 7
+#define MEM12 8
+#define MEM16 9
+#define FBRA 10
+#define CALLJ 11
+
+/* Masks for the mode bits in REG format instructions */
+#define M1 0x0800
+#define M2 0x1000
+#define M3 0x2000
+
+/* Generate the 12-bit opcode for a REG format instruction by placing the
+ * high 8 bits in instruction bits 24-31, the low 4 bits in instruction bits
+ * 7-10.
+ */
+
+#define REG_OPC(opc) ((opc & 0xff0) << 20) | ((opc & 0xf) << 7)
+
+/* Generate a template for a REG format instruction: place the opcode bits
+ * in the appropriate fields and OR in mode bits for the operands that will not
+ * be used. I.e.,
+ * set m1=1, if src1 will not be used
+ * set m2=1, if src2 will not be used
+ * set m3=1, if dst will not be used
+ *
+ * Setting the "unused" mode bits to 1 speeds up instruction execution(!).
+ * The information is also useful to us because some 1-operand REG instructions
+ * use the src1 field, others the dst field; and some 2-operand REG instructions
+ * use src1/src2, others src1/dst. The set mode bits enable us to distinguish.
+ */
+#define R_0(opc) ( REG_OPC(opc) | M1 | M2 | M3 ) /* No operands */
+#define R_1(opc) ( REG_OPC(opc) | M2 | M3 ) /* 1 operand: src1 */
+#define R_1D(opc) ( REG_OPC(opc) | M1 | M2 ) /* 1 operand: dst */
+#define R_2(opc) ( REG_OPC(opc) | M3 ) /* 2 ops: src1/src2 */
+#define R_2D(opc) ( REG_OPC(opc) | M2 ) /* 2 ops: src1/dst */
+#define R_3(opc) ( REG_OPC(opc) ) /* 3 operands */
+
+/* DESCRIPTOR BYTES FOR REGISTER OPERANDS
+ *
+ * Interpret names as follows:
+ * R: global or local register only
+ * RS: global, local, or (if target allows) special-function register only
+ * RL: global or local register, or integer literal
+ * RSL: global, local, or (if target allows) special-function register;
+ * or integer literal
+ * F: global, local, or floating-point register
+ * FL: global, local, or floating-point register; or literal (including
+ * floating point)
+ *
+ * A number appended to a name indicates that registers must be aligned,
+ * as follows:
+ * 2: register number must be multiple of 2
+ * 4: register number must be multiple of 4
+ */
+
+#define SFR 0x10 /* Mask for the "sfr-OK" bit */
+#define LIT 0x08 /* Mask for the "literal-OK" bit */
+#define FP 0x04 /* Mask for "floating-point-OK" bit */
+
+/* This macro ors the bits together. Note that 'align' is a mask
+ * for the low 0, 1, or 2 bits of the register number, as appropriate.
+ */
+#define OP(align,lit,fp,sfr) ( align | lit | fp | sfr )
+
+#define R OP( 0, 0, 0, 0 )
+#define RS OP( 0, 0, 0, SFR )
+#define RL OP( 0, LIT, 0, 0 )
+#define RSL OP( 0, LIT, 0, SFR )
+#define F OP( 0, 0, FP, 0 )
+#define FL OP( 0, LIT, FP, 0 )
+#define R2 OP( 1, 0, 0, 0 )
+#define RL2 OP( 1, LIT, 0, 0 )
+#define F2 OP( 1, 0, FP, 0 )
+#define FL2 OP( 1, LIT, FP, 0 )
+#define R4 OP( 3, 0, 0, 0 )
+#define RL4 OP( 3, LIT, 0, 0 )
+#define F4 OP( 3, 0, FP, 0 )
+#define FL4 OP( 3, LIT, FP, 0 )
+
+#define M 0x7f /* Memory operand (MEMA & MEMB format instructions) */
+
+/* Macros to extract info from the register operand descriptor byte 'od'.
+ */
+#define SFR_OK(od) (od & SFR) /* TRUE if sfr operand allowed */
+#define LIT_OK(od) (od & LIT) /* TRUE if literal operand allowed */
+#define FP_OK(od) (od & FP) /* TRUE if floating-point op allowed */
+#define REG_ALIGN(od,n) ((od & 0x3 & n) == 0)
+/* TRUE if reg #n is properly aligned */
+#define MEMOP(od) (od == M) /* TRUE if operand is a memory operand*/
+
+/* Classes of 960 intructions:
+ * - each instruction falls into one class.
+ * - each target architecture supports one or more classes.
+ *
+ * EACH CONSTANT MUST CONTAIN 1 AND ONLY 1 SET BIT!: see targ_has_iclass().
+ */
+#define I_BASE 0x01 /* 80960 base instruction set */
+#define I_CX 0x02 /* 80960Cx instruction */
+#define I_DEC 0x04 /* Decimal instruction */
+#define I_FP 0x08 /* Floating point instruction */
+#define I_KX 0x10 /* 80960Kx instruction */
+#define I_MIL 0x20 /* Military instruction */
+
+/* MEANING OF 'n_other' in the symbol record.
+ *
+ * If non-zero, the 'n_other' fields indicates either a leaf procedure or
+ * a system procedure, as follows:
+ *
+ * 1 <= n_other <= 32 :
+ * The symbol is the entry point to a system procedure.
+ * 'n_value' is the address of the entry, as for any other
+ * procedure. The system procedure number (which can be used in
+ * a 'calls' instruction) is (n_other-1). These entries come from
+ * '.sysproc' directives.
+ *
+ * n_other == N_CALLNAME
+ * the symbol is the 'call' entry point to a leaf procedure.
+ * The *next* symbol in the symbol table must be the corresponding
+ * 'bal' entry point to the procedure (see following). These
+ * entries come from '.leafproc' directives in which two different
+ * symbols are specified (the first one is represented here).
+ *
+ *
+ * n_other == N_BALNAME
+ * the symbol is the 'bal' entry point to a leaf procedure.
+ * These entries result from '.leafproc' directives in which only
+ * one symbol is specified, or in which the same symbol is
+ * specified twice.
+ *
+ * Note that an N_CALLNAME entry *must* have a corresponding N_BALNAME entry,
+ * but not every N_BALNAME entry must have an N_CALLNAME entry.
+ */
+#define N_CALLNAME (-1)
+#define N_BALNAME (-2)
+
+
+/* i960 uses a custom relocation record. */
+
+/* let obj-aout.h know */
+#define CUSTOM_RELOC_FORMAT 1
+/* let a.out.gnu.h know */
+#define N_RELOCATION_INFO_DECLARED 1
+struct relocation_info {
+ int r_address; /* File address of item to be relocated */
+ unsigned
+ r_index:24,/* Index of symbol on which relocation is based*/
+ r_pcrel:1, /* 1 => relocate PC-relative; else absolute
+ * On i960, pc-relative implies 24-bit
+ * address, absolute implies 32-bit.
+ */
+ r_length:2, /* Number of bytes to relocate:
+ * 0 => 1 byte
+ * 1 => 2 bytes
+ * 2 => 4 bytes -- only value used for i960
+ */
+ r_extern:1,
+ r_bsr:1, /* Something for the GNU NS32K assembler */
+ r_disp:1, /* Something for the GNU NS32K assembler */
+ r_callj:1, /* 1 if relocation target is an i960 'callj' */
+ nuthin:1; /* Unused */
+};
+
+/* hacks for tracking callj's */
+#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
+
+#define TC_S_IS_SYSPROC(s) ((1 <= S_GET_OTHER(s)) && (S_GET_OTHER(s) <= 32))
+#define TC_S_IS_BALNAME(s) (S_GET_OTHER(s) == N_BALNAME)
+#define TC_S_IS_CALLNAME(s) (S_GET_OTHER(s) == N_CALLNAME)
+#define TC_S_IS_BADPROC(s) ((S_GET_OTHER(s) != 0) && !TC_S_IS_CALLNAME(s) && !TC_S_IS_BALNAME(s) && !TC_S_IS_SYSPROC(s))
+
+#define TC_S_SET_SYSPROC(s, p) (S_SET_OTHER((s), (p)+1))
+#define TC_S_GET_SYSPROC(s) (S_GET_OTHER(s)-1)
+
+#define TC_S_FORCE_TO_BALNAME(s) (S_SET_OTHER((s), N_BALNAME))
+#define TC_S_FORCE_TO_CALLNAME(s) (S_SET_OTHER((s), N_CALLNAME))
+#define TC_S_FORCE_TO_SYSPROC(s) {;}
+
+#elif defined(OBJ_COFF)
+
+#define TC_S_IS_SYSPROC(s) (S_GET_STORAGE_CLASS(s) == C_SCALL)
+#define TC_S_IS_BALNAME(s) (SF_GET_BALNAME(s))
+#define TC_S_IS_CALLNAME(s) (SF_GET_CALLNAME(s))
+#define TC_S_IS_BADPROC(s) (TC_S_IS_SYSPROC(s) && TC_S_GET_SYSPROC(s) < 0 && 31 < TC_S_GET_SYSPROC(s))
+
+#define TC_S_SET_SYSPROC(s, p) ((s)->sy_symbol.ost_auxent[1].x_sc.x_stindx = (p))
+#define TC_S_GET_SYSPROC(s) ((s)->sy_symbol.ost_auxent[1].x_sc.x_stindx)
+
+#define TC_S_FORCE_TO_BALNAME(s) (SF_SET_BALNAME(s))
+#define TC_S_FORCE_TO_CALLNAME(s) (SF_SET_CALLNAME(s))
+#define TC_S_FORCE_TO_SYSPROC(s) (S_SET_STORAGE_CLASS((s), C_SCALL))
+
+#else /* switch on OBJ */
+you lose
+#endif /* witch on OBJ */
+
+#if __STDC__ == 1
+
+ void brtab_emit(void);
+void reloc_callj(); /* this is really reloc_callj(fixS *fixP) but I don't want to change header inclusion order. */
+void tc_set_bal_of_call(); /* this is really tc_set_bal_of_call(symbolS *callP, symbolS *balP) */
+
+#else /* not __STDC__ */
+
+void brtab_emit();
+void reloc_callj();
+void tc_set_bal_of_call();
+
+#endif /* not __STDC__ */
+
+char *_tc_get_bal_of_call(); /* this is really symbolS *tc_get_bal_of_call(symbolS *callP). */
+#define tc_get_bal_of_call(c) ((symbolS *) _tc_get_bal_of_call(c))
+#endif
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of tc-i960.h */
diff --git a/gnu/usr.bin/as/config/tc-m68851.h b/gnu/usr.bin/as/config/tc-m68851.h
new file mode 100644
index 0000000..5f70e42
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-m68851.h
@@ -0,0 +1,304 @@
+/* This file is tc-m68851.h
+
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * pmmu.h
+ */
+
+/* I suppose we have to copyright this file. Someone on the net sent it
+ to us as part of the changes for the m68851 Memory Management Unit */
+
+/* Copyright (C) 1987 Free Software Foundation, Inc.
+
+ This file is part of Gas, the GNU Assembler.
+
+ The GNU assembler is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY. No author or distributor
+ accepts responsibility to anyone for the consequences of using it
+ or for whether it serves any particular purpose or works at all,
+ unless he says so in writing. Refer to the GNU Assembler General
+ Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute
+ the GNU Assembler, but only under the conditions described in the
+ GNU Assembler General Public License. A copy of this license is
+ supposed to have been given to you along with the GNU Assembler
+ so you can know your rights and responsibilities. It should be
+ in a file named COPYING. Among other things, the copyright
+ notice and this notice must be preserved on all copies. */
+
+#ifdef m68851
+
+/*
+ I didn't use much imagination in choosing the
+ following codes, so many of them aren't very
+ mnemonic. -rab
+
+ P pmmu register
+ Possible values:
+ 000 TC Translation Control reg
+ 100 CAL Current Access Level
+ 101 VAL Validate Access Level
+ 110 SCC Stack Change Control
+ 111 AC Access Control
+
+ W wide pmmu registers
+ Possible values:
+ 001 DRP Dma Root Pointer
+ 010 SRP Supervisor Root Pointer
+ 011 CRP Cpu Root Pointer
+
+ f function code register
+ 0 SFC
+ 1 DFC
+
+ V VAL register only
+
+ X BADx, BACx
+ 100 BAD Breakpoint Acknowledge Data
+ 101 BAC Breakpoint Acknowledge Control
+
+ Y PSR
+ Z PCSR
+
+ | memory (modes 2-6, 7.*)
+
+ */
+
+/*
+ * these defines should be in m68k.c but
+ * i put them here to keep all the m68851 stuff
+ * together -rab
+ * JF--Make sure these #s don't clash with the ones in m68k.c
+ * That would be BAD.
+ */
+#define TC (FPS+1) /* 48 */
+#define DRP (TC+1) /* 49 */
+#define SRP (DRP+1) /* 50 */
+#define CRP (SRP+1) /* 51 */
+#define CAL (CRP+1) /* 52 */
+#define VAL (CAL+1) /* 53 */
+#define SCC (VAL+1) /* 54 */
+#define AC (SCC+1) /* 55 */
+#define BAD (AC+1) /* 56,57,58,59, 60,61,62,63 */
+#define BAC (BAD+8) /* 64,65,66,67, 68,69,70,71 */
+#define PSR (BAC+8) /* 72 */
+#define PCSR (PSR+1) /* 73 */
+
+/* name */ /* opcode */ /* match */ /* args */
+
+{"pbac", one(0xf0c7), one(0xffbf), "Bc"},
+{"pbacw", one(0xf087), one(0xffbf), "Bc"},
+{"pbas", one(0xf0c6), one(0xffbf), "Bc"},
+{"pbasw", one(0xf086), one(0xffbf), "Bc"},
+{"pbbc", one(0xf0c1), one(0xffbf), "Bc"},
+{"pbbcw", one(0xf081), one(0xffbf), "Bc"},
+{"pbbs", one(0xf0c0), one(0xffbf), "Bc"},
+{"pbbsw", one(0xf080), one(0xffbf), "Bc"},
+{"pbcc", one(0xf0cf), one(0xffbf), "Bc"},
+{"pbccw", one(0xf08f), one(0xffbf), "Bc"},
+{"pbcs", one(0xf0ce), one(0xffbf), "Bc"},
+{"pbcsw", one(0xf08e), one(0xffbf), "Bc"},
+{"pbgc", one(0xf0cd), one(0xffbf), "Bc"},
+{"pbgcw", one(0xf08d), one(0xffbf), "Bc"},
+{"pbgs", one(0xf0cc), one(0xffbf), "Bc"},
+{"pbgsw", one(0xf08c), one(0xffbf), "Bc"},
+{"pbic", one(0xf0cb), one(0xffbf), "Bc"},
+{"pbicw", one(0xf08b), one(0xffbf), "Bc"},
+{"pbis", one(0xf0ca), one(0xffbf), "Bc"},
+{"pbisw", one(0xf08a), one(0xffbf), "Bc"},
+{"pblc", one(0xf0c3), one(0xffbf), "Bc"},
+{"pblcw", one(0xf083), one(0xffbf), "Bc"},
+{"pbls", one(0xf0c2), one(0xffbf), "Bc"},
+{"pblsw", one(0xf082), one(0xffbf), "Bc"},
+{"pbsc", one(0xf0c5), one(0xffbf), "Bc"},
+{"pbscw", one(0xf085), one(0xffbf), "Bc"},
+{"pbss", one(0xf0c4), one(0xffbf), "Bc"},
+{"pbssw", one(0xf084), one(0xffbf), "Bc"},
+{"pbwc", one(0xf0c9), one(0xffbf), "Bc"},
+{"pbwcw", one(0xf089), one(0xffbf), "Bc"},
+{"pbws", one(0xf0c8), one(0xffbf), "Bc"},
+{"pbwsw", one(0xf088), one(0xffbf), "Bc"},
+
+
+{"pdbac", two(0xf048, 0x0007), two(0xfff8, 0xffff), "DsBw"},
+{"pdbas", two(0xf048, 0x0006), two(0xfff8, 0xffff), "DsBw"},
+{"pdbbc", two(0xf048, 0x0001), two(0xfff8, 0xffff), "DsBw"},
+{"pdbbs", two(0xf048, 0x0000), two(0xfff8, 0xffff), "DsBw"},
+{"pdbcc", two(0xf048, 0x000f), two(0xfff8, 0xffff), "DsBw"},
+{"pdbcs", two(0xf048, 0x000e), two(0xfff8, 0xffff), "DsBw"},
+{"pdbgc", two(0xf048, 0x000d), two(0xfff8, 0xffff), "DsBw"},
+{"pdbgs", two(0xf048, 0x000c), two(0xfff8, 0xffff), "DsBw"},
+{"pdbic", two(0xf048, 0x000b), two(0xfff8, 0xffff), "DsBw"},
+{"pdbis", two(0xf048, 0x000a), two(0xfff8, 0xffff), "DsBw"},
+{"pdblc", two(0xf048, 0x0003), two(0xfff8, 0xffff), "DsBw"},
+{"pdbls", two(0xf048, 0x0002), two(0xfff8, 0xffff), "DsBw"},
+{"pdbsc", two(0xf048, 0x0005), two(0xfff8, 0xffff), "DsBw"},
+{"pdbss", two(0xf048, 0x0004), two(0xfff8, 0xffff), "DsBw"},
+{"pdbwc", two(0xf048, 0x0009), two(0xfff8, 0xffff), "DsBw"},
+{"pdbws", two(0xf048, 0x0008), two(0xfff8, 0xffff), "DsBw"},
+
+{"pflusha", two(0xf000, 0x2400), two(0xffff, 0xffff), "" },
+
+{"pflush", two(0xf000, 0x3010), two(0xffc0, 0xfe10), "T3T9" },
+{"pflush", two(0xf000, 0x3810), two(0xffc0, 0xfe10), "T3T9&s" },
+{"pflush", two(0xf000, 0x3008), two(0xffc0, 0xfe18), "D3T9" },
+{"pflush", two(0xf000, 0x3808), two(0xffc0, 0xfe18), "D3T9&s" },
+{"pflush", two(0xf000, 0x3000), two(0xffc0, 0xfe1e), "f3T9" },
+{"pflush", two(0xf000, 0x3800), two(0xffc0, 0xfe1e), "f3T9&s" },
+
+{"pflushs", two(0xf000, 0x3410), two(0xfff8, 0xfe10), "T3T9" },
+{"pflushs", two(0xf000, 0x3c00), two(0xfff8, 0xfe00), "T3T9&s" },
+{"pflushs", two(0xf000, 0x3408), two(0xfff8, 0xfe18), "D3T9" },
+{"pflushs", two(0xf000, 0x3c08), two(0xfff8, 0xfe18), "D3T9&s" },
+{"pflushs", two(0xf000, 0x3400), two(0xfff8, 0xfe1e), "f3T9" },
+{"pflushs", two(0xf000, 0x3c00), two(0xfff8, 0xfe1e), "f3T9&s"},
+
+{"pflushr", two(0xf000, 0xa000), two(0xffc0, 0xffff), "|s" },
+
+{"ploadr", two(0xf000, 0x2210), two(0xffc0, 0xfff0), "T3&s" },
+{"ploadr", two(0xf000, 0x2208), two(0xffc0, 0xfff8), "D3&s" },
+{"ploadr", two(0xf000, 0x2200), two(0xffc0, 0xfffe), "f3&s" },
+{"ploadw", two(0xf000, 0x2010), two(0xffc0, 0xfff0), "T3&s" },
+{"ploadw", two(0xf000, 0x2008), two(0xffc0, 0xfff8), "D3&s" },
+{"ploadw", two(0xf000, 0x2000), two(0xffc0, 0xfffe), "f3&s" },
+
+ /* TC, CRP, DRP, SRP, CAL, VAL, SCC, AC */
+{"pmove", two(0xf000, 0x4000), two(0xffc0, 0xe3ff), "*sP8" },
+{"pmove", two(0xf000, 0x4200), two(0xffc0, 0xe3ff), "P8%s" },
+{"pmove", two(0xf000, 0x4000), two(0xffc0, 0xe3ff), "|sW8" },
+{"pmove", two(0xf000, 0x4200), two(0xffc0, 0xe3ff), "W8~s" },
+
+ /* BADx, BACx */
+{"pmove", two(0xf000, 0x6200), two(0xffc0, 0xe3e3), "*sX3" },
+{"pmove", two(0xf000, 0x6000), two(0xffc0, 0xe3e3), "X3%s" },
+
+ /* PSR, PCSR */
+ /* {"pmove", two(0xf000, 0x6100), two(oxffc0, oxffff), "*sZ8" }, */
+{"pmove", two(0xf000, 0x6000), two(0xffc0, 0xffff), "*sY8" },
+{"pmove", two(0xf000, 0x6200), two(0xffc0, 0xffff), "Y8%s" },
+{"pmove", two(0xf000, 0x6600), two(0xffc0, 0xffff), "Z8%s" },
+
+{"prestore", one(0xf140), one(0xffc0), "&s"},
+{"prestore", one(0xf158), one(0xfff8), "+s"},
+{"psave", one(0xf100), one(0xffc0), "&s"},
+{"psave", one(0xf100), one(0xffc0), "+s"},
+
+{"psac", two(0xf040, 0x0007), two(0xffc0, 0xffff), "@s"},
+{"psas", two(0xf040, 0x0006), two(0xffc0, 0xffff), "@s"},
+{"psbc", two(0xf040, 0x0001), two(0xffc0, 0xffff), "@s"},
+{"psbs", two(0xf040, 0x0000), two(0xffc0, 0xffff), "@s"},
+{"pscc", two(0xf040, 0x000f), two(0xffc0, 0xffff), "@s"},
+{"pscs", two(0xf040, 0x000e), two(0xffc0, 0xffff), "@s"},
+{"psgc", two(0xf040, 0x000d), two(0xffc0, 0xffff), "@s"},
+{"psgs", two(0xf040, 0x000c), two(0xffc0, 0xffff), "@s"},
+{"psic", two(0xf040, 0x000b), two(0xffc0, 0xffff), "@s"},
+{"psis", two(0xf040, 0x000a), two(0xffc0, 0xffff), "@s"},
+{"pslc", two(0xf040, 0x0003), two(0xffc0, 0xffff), "@s"},
+{"psls", two(0xf040, 0x0002), two(0xffc0, 0xffff), "@s"},
+{"pssc", two(0xf040, 0x0005), two(0xffc0, 0xffff), "@s"},
+{"psss", two(0xf040, 0x0004), two(0xffc0, 0xffff), "@s"},
+{"pswc", two(0xf040, 0x0009), two(0xffc0, 0xffff), "@s"},
+{"psws", two(0xf040, 0x0008), two(0xffc0, 0xffff), "@s"},
+
+{"ptestr", two(0xf000, 0x8210), two(0xffc0, 0xe3f0), "T3&sQ8" },
+{"ptestr", two(0xf000, 0x8310), two(0xffc0, 0xe310), "T3&sQ8A9" },
+{"ptestr", two(0xf000, 0x8208), two(0xffc0, 0xe3f8), "D3&sQ8" },
+{"ptestr", two(0xf000, 0x8308), two(0xffc0, 0xe318), "D3&sQ8A9" },
+{"ptestr", two(0xf000, 0x8200), two(0xffc0, 0xe3fe), "f3&sQ8" },
+{"ptestr", two(0xf000, 0x8300), two(0xffc0, 0xe31e), "f3&sQ8A9" },
+
+{"ptestw", two(0xf000, 0x8010), two(0xffc0, 0xe3f0), "T3&sQ8" },
+{"ptestw", two(0xf000, 0x8110), two(0xffc0, 0xe310), "T3&sQ8A9" },
+{"ptestw", two(0xf000, 0x8008), two(0xffc0, 0xe3f8), "D3&sQ8" },
+{"ptestw", two(0xf000, 0x8108), two(0xffc0, 0xe318), "D3&sQ8A9" },
+{"ptestw", two(0xf000, 0x8000), two(0xffc0, 0xe3fe), "f3&sQ8" },
+{"ptestw", two(0xf000, 0x8100), two(0xffc0, 0xe31e), "f3&sQ8A9" },
+
+{"ptrapacw", two(0xf07a, 0x0007), two(0xffff, 0xffff), "#w"},
+{"ptrapacl", two(0xf07b, 0x0007), two(0xffff, 0xffff), "#l"},
+{"ptrapac", two(0xf07c, 0x0007), two(0xffff, 0xffff), ""},
+
+{"ptrapasw", two(0xf07a, 0x0006), two(0xffff, 0xffff), "#w"},
+{"ptrapasl", two(0xf07b, 0x0006), two(0xffff, 0xffff), "#l"},
+{"ptrapas", two(0xf07c, 0x0006), two(0xffff, 0xffff), ""},
+
+{"ptrapbcw", two(0xf07a, 0x0001), two(0xffff, 0xffff), "#w"},
+{"ptrapbcl", two(0xf07b, 0x0001), two(0xffff, 0xffff), "#l"},
+{"ptrapbc", two(0xf07c, 0x0001), two(0xffff, 0xffff), ""},
+
+{"ptrapbsw", two(0xf07a, 0x0000), two(0xffff, 0xffff), "#w"},
+{"ptrapbsl", two(0xf07b, 0x0000), two(0xffff, 0xffff), "#l"},
+{"ptrapbs", two(0xf07c, 0x0000), two(0xffff, 0xffff), ""},
+
+{"ptrapccw", two(0xf07a, 0x000f), two(0xffff, 0xffff), "#w"},
+{"ptrapccl", two(0xf07b, 0x000f), two(0xffff, 0xffff), "#l"},
+{"ptrapcc", two(0xf07c, 0x000f), two(0xffff, 0xffff), ""},
+
+{"ptrapcsw", two(0xf07a, 0x000e), two(0xffff, 0xffff), "#w"},
+{"ptrapcsl", two(0xf07b, 0x000e), two(0xffff, 0xffff), "#l"},
+{"ptrapcs", two(0xf07c, 0x000e), two(0xffff, 0xffff), ""},
+
+{"ptrapgcw", two(0xf07a, 0x000d), two(0xffff, 0xffff), "#w"},
+{"ptrapgcl", two(0xf07b, 0x000d), two(0xffff, 0xffff), "#l"},
+{"ptrapgc", two(0xf07c, 0x000d), two(0xffff, 0xffff), ""},
+
+{"ptrapgsw", two(0xf07a, 0x000c), two(0xffff, 0xffff), "#w"},
+{"ptrapgsl", two(0xf07b, 0x000c), two(0xffff, 0xffff), "#l"},
+{"ptrapgs", two(0xf07c, 0x000c), two(0xffff, 0xffff), ""},
+
+{"ptrapicw", two(0xf07a, 0x000b), two(0xffff, 0xffff), "#w"},
+{"ptrapicl", two(0xf07b, 0x000b), two(0xffff, 0xffff), "#l"},
+{"ptrapic", two(0xf07c, 0x000b), two(0xffff, 0xffff), ""},
+
+{"ptrapisw", two(0xf07a, 0x000a), two(0xffff, 0xffff), "#w"},
+{"ptrapisl", two(0xf07b, 0x000a), two(0xffff, 0xffff), "#l"},
+{"ptrapis", two(0xf07c, 0x000a), two(0xffff, 0xffff), ""},
+
+{"ptraplcw", two(0xf07a, 0x0003), two(0xffff, 0xffff), "#w"},
+{"ptraplcl", two(0xf07b, 0x0003), two(0xffff, 0xffff), "#l"},
+{"ptraplc", two(0xf07c, 0x0003), two(0xffff, 0xffff), ""},
+
+{"ptraplsw", two(0xf07a, 0x0002), two(0xffff, 0xffff), "#w"},
+{"ptraplsl", two(0xf07b, 0x0002), two(0xffff, 0xffff), "#l"},
+{"ptrapls", two(0xf07c, 0x0002), two(0xffff, 0xffff), ""},
+
+{"ptrapscw", two(0xf07a, 0x0005), two(0xffff, 0xffff), "#w"},
+{"ptrapscl", two(0xf07b, 0x0005), two(0xffff, 0xffff), "#l"},
+{"ptrapsc", two(0xf07c, 0x0005), two(0xffff, 0xffff), ""},
+
+{"ptrapssw", two(0xf07a, 0x0004), two(0xffff, 0xffff), "#w"},
+{"ptrapssl", two(0xf07b, 0x0004), two(0xffff, 0xffff), "#l"},
+{"ptrapss", two(0xf07c, 0x0004), two(0xffff, 0xffff), ""},
+
+{"ptrapwcw", two(0xf07a, 0x0009), two(0xffff, 0xffff), "#w"},
+{"ptrapwcl", two(0xf07b, 0x0009), two(0xffff, 0xffff), "#l"},
+{"ptrapwc", two(0xf07c, 0x0009), two(0xffff, 0xffff), ""},
+
+{"ptrapwsw", two(0xf07a, 0x0008), two(0xffff, 0xffff), "#w"},
+{"ptrapwsl", two(0xf07b, 0x0008), two(0xffff, 0xffff), "#l"},
+{"ptrapws", two(0xf07c, 0x0008), two(0xffff, 0xffff), ""},
+
+{"pvalid", two(0xf000, 0x2800), two(0xffc0, 0xffff), "Vs&s"},
+{"pvalid", two(0xf000, 0x2c00), two(0xffc0, 0xfff8), "A3&s" },
+
+#endif /* m68851 */
+
+/* end of tc-m68851.h */
diff --git a/gnu/usr.bin/as/config/tc-m68k.c b/gnu/usr.bin/as/config/tc-m68k.c
new file mode 100644
index 0000000..2dac35b
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-m68k.c
@@ -0,0 +1,4076 @@
+/* tc-m68k.c All the m68020 specific stuff in one convenient, huge,
+ slow to compile, easy to find file.
+
+ Copyright (C) 1987, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <ctype.h>
+
+#include "as.h"
+
+#include "obstack.h"
+
+/* note that this file includes real declarations and thus can only be included by one source file per executable. */
+#include "opcode/m68k.h"
+#ifdef TE_SUN
+/* This variable contains the value to write out at the beginning of
+ the a.out file. The 2<<16 means that this is a 68020 file instead
+ of an old-style 68000 file */
+
+long omagic = 2<<16|OMAGIC; /* Magic byte for header file */
+#else
+long omagic = OMAGIC;
+#endif
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful */
+const char comment_chars[] = "|";
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that comments like this one will always work. */
+const char line_comment_chars[] = "#";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+
+const char FLT_CHARS[] = "rRsSfFdDxXeEpP";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c. Ideally it shouldn't have to know about it at all,
+ but nothing is ideal around here.
+ */
+
+int md_reloc_size = 8; /* Size of relocation record */
+
+/* Its an arbitrary name: This means I don't approve of it */
+/* See flames below */
+static struct obstack robyn;
+
+#define TAB(x,y) (((x)<<2)+(y))
+#define TABTYPE(xy) ((xy) >> 2)
+#define BYTE 0
+#define SHORT 1
+#define LONG 2
+#define SZ_UNDEF 3
+
+#define BRANCH 1
+#define FBRANCH 2
+#define PCREL 3
+#define BCC68000 4
+#define DBCC 5
+#define PCLEA 6
+
+/* Operands we can parse: (And associated modes)
+
+ numb: 8 bit num
+ numw: 16 bit num
+ numl: 32 bit num
+ dreg: data reg 0-7
+ reg: address or data register
+ areg: address register
+ apc: address register, PC, ZPC or empty string
+ num: 16 or 32 bit num
+ num2: like num
+ sz: w or l if omitted, l assumed
+ scale: 1 2 4 or 8 if omitted, 1 assumed
+
+ 7.4 IMMED #num --> NUM
+ 0.? DREG dreg --> dreg
+ 1.? AREG areg --> areg
+ 2.? AINDR areg@ --> *(areg)
+ 3.? AINC areg@+ --> *(areg++)
+ 4.? ADEC areg@- --> *(--areg)
+ 5.? AOFF apc@(numw) --> *(apc+numw) -- empty string and ZPC not allowed here
+ 6.? AINDX apc@(num,reg:sz:scale) --> *(apc+num+reg*scale)
+ 6.? AINDX apc@(reg:sz:scale) --> same, with num=0
+ 6.? APODX apc@(num)@(num2,reg:sz:scale) --> *(*(apc+num)+num2+reg*scale)
+ 6.? APODX apc@(num)@(reg:sz:scale) --> same, with num2=0
+ 6.? AMIND apc@(num)@(num2) --> *(*(apc+num)+num2) (previous mode without an index reg)
+ 6.? APRDX apc@(num,reg:sz:scale)@(num2) --> *(*(apc+num+reg*scale)+num2)
+ 6.? APRDX apc@(reg:sz:scale)@(num2) --> same, with num=0
+ 7.0 ABSL num:sz --> *(num)
+ num --> *(num) (sz L assumed)
+ *** MSCR otherreg --> Magic
+ With -l option
+ 5.? AOFF apc@(num) --> *(apc+num) -- empty string and ZPC not allowed here still
+
+ examples:
+ #foo #0x35 #12
+ d2
+ a4
+ a3@
+ a5@+
+ a6@-
+ a2@(12) pc@(14)
+ a1@(5,d2:w:1) @(45,d6:l:4)
+ pc@(a2) @(d4)
+ etc...
+
+
+ #name@(numw) -->turn into PC rel mode
+ apc@(num8,reg:sz:scale) --> *(apc+num8+reg*scale)
+
+ */
+
+enum operand_type {
+ IMMED = 1,
+ DREG,
+ AREG,
+ AINDR,
+ ADEC,
+ AINC,
+ AOFF,
+ AINDX,
+ APODX,
+ AMIND,
+ APRDX,
+ ABSL,
+ MSCR,
+ REGLST,
+};
+
+
+struct m68k_exp {
+ char *e_beg;
+ char *e_end;
+ expressionS e_exp;
+ short e_siz; /* 0 == default 1 == short/byte 2 == word 3 == long */
+};
+
+/* DATA and ADDR have to be contiguous, so that reg-DATA gives 0-7 == data reg,
+ 8-15 == addr reg for operands that take both types */
+
+enum _register {
+ DATA = 1, /* 1- 8 == data registers 0-7 */
+ DATA0 = DATA,
+ DATA1,
+ DATA2,
+ DATA3,
+ DATA4,
+ DATA5,
+ DATA6,
+ DATA7,
+
+ ADDR,
+ ADDR0 = ADDR,
+ ADDR1,
+ ADDR2,
+ ADDR3,
+ ADDR4,
+ ADDR5,
+ ADDR6,
+ ADDR7,
+
+ /* Note that COPNUM == processor #1 -- COPNUM+7 == #8, which stores as 000 */
+ /* I think... */
+
+ SP = ADDR7,
+
+ FPREG, /* Eight FP registers */
+ FP0 = FPREG,
+ FP1,
+ FP2,
+ FP3,
+ FP4,
+ FP5,
+ FP6,
+ FP7,
+ COPNUM = (FPREG+8), /* Co-processor #1-#8 */
+ COP0 = COPNUM,
+ COP1,
+ COP2,
+ COP3,
+ COP4,
+ COP5,
+ COP6,
+ COP7,
+ PC, /* Program counter */
+ ZPC, /* Hack for Program space, but 0 addressing */
+ SR, /* Status Reg */
+ CCR, /* Condition code Reg */
+
+ /* These have to be in order for the movec instruction to work. */
+ USP, /* User Stack Pointer */
+ ISP, /* Interrupt stack pointer */
+ SFC,
+ DFC,
+ CACR,
+ VBR,
+ CAAR,
+ MSP,
+ ITT0,
+ ITT1,
+ DTT0,
+ DTT1,
+ MMUSR,
+ TC,
+ SRP,
+ URP,
+ /* end of movec ordering constraints */
+
+ FPI,
+ FPS,
+ FPC,
+
+ DRP,
+ CRP,
+ CAL,
+ VAL,
+ SCC,
+ AC,
+ BAD,
+ BAD0 = BAD,
+ BAD1,
+ BAD2,
+ BAD3,
+ BAD4,
+ BAD5,
+ BAD6,
+ BAD7,
+ BAC,
+ BAC0 = BAC,
+ BAC1,
+ BAC2,
+ BAC3,
+ BAC4,
+ BAC5,
+ BAC6,
+ BAC7,
+ PSR,
+ PCSR,
+
+ IC, /* instruction cache token */
+ DC, /* data cache token */
+ NC, /* no cache token */
+ BC, /* both caches token */
+
+};
+
+/* Internal form of an operand. */
+struct m68k_op {
+ char *error; /* Couldn't parse it */
+ enum operand_type mode; /* What mode this instruction is in. */
+ enum _register reg; /* Base register */
+ struct m68k_exp *con1;
+ int ireg; /* Index register */
+ int isiz; /* 0 == unspec 1 == byte(?) 2 == short 3 == long */
+ int imul; /* Multipy ireg by this (1,2,4,or 8) */
+ struct m68k_exp *con2;
+};
+
+/* internal form of a 68020 instruction */
+struct m68k_it {
+ char *error;
+ char *args; /* list of opcode info */
+ int numargs;
+
+ int numo; /* Number of shorts in opcode */
+ short opcode[11];
+
+ struct m68k_op operands[6];
+
+ int nexp; /* number of exprs in use */
+ struct m68k_exp exprs[4];
+
+ int nfrag; /* Number of frags we have to produce */
+ struct {
+ int fragoff; /* Where in the current opcode[] the frag ends */
+ symbolS *fadd;
+ long foff;
+ int fragty;
+ } fragb[4];
+
+ int nrel; /* Num of reloc strucs in use */
+ struct {
+ int n;
+ symbolS *add,
+ *sub,
+ *got;
+ long off;
+ char wid;
+ char pcrel;
+ enum reloc_type rtype;
+ } reloc[5]; /* Five is enough??? */
+};
+
+#define cpu_of_arch(x) ((x) & m68000up)
+#define float_of_arch(x) ((x) & mfloat)
+#define mmu_of_arch(x) ((x) & mmmu)
+
+static struct m68k_it the_ins; /* the instruction being assembled */
+
+/* Macros for adding things to the m68k_it struct */
+
+#define addword(w) the_ins.opcode[the_ins.numo++]=(w)
+
+/* Like addword, but goes BEFORE general operands */
+#define insop(w) { \
+ int z; \
+ for (z=the_ins.numo;z>opcode->m_codenum;--z) \
+ the_ins.opcode[z]=the_ins.opcode[z-1]; \
+ for (z=0;z<the_ins.nrel;z++) \
+ the_ins.reloc[z].n+=2; \
+ the_ins.opcode[opcode->m_codenum]=w; \
+ the_ins.numo++; \
+ }
+
+
+#define add_exp(beg,end) (the_ins.exprs[the_ins.nexp].e_beg=beg, \
+ the_ins.exprs[the_ins.nexp].e_end=end, \
+ &the_ins.exprs[the_ins.nexp++] \
+ )
+
+
+/* The numo+1 kludge is so we can hit the low order byte of the prev word. Blecch*/
+#define add_fix(width, exp, pc_rel, r_type) { \
+ the_ins.reloc[the_ins.nrel].n= ((width) == 'B') ? (the_ins.numo*2-1) : \
+ (((width) == 'b') ? ((the_ins.numo-1)*2) : (the_ins.numo*2)); \
+ the_ins.reloc[the_ins.nrel].add=adds((exp)); \
+ the_ins.reloc[the_ins.nrel].sub=subs((exp)); \
+ the_ins.reloc[the_ins.nrel].off=offs((exp)); \
+ the_ins.reloc[the_ins.nrel].got=gots((exp)); \
+ the_ins.reloc[the_ins.nrel].wid=width; \
+ the_ins.reloc[the_ins.nrel].pcrel=pc_rel; \
+ the_ins.reloc[the_ins.nrel++].rtype=r_type; \
+ }
+
+#define add_frag(add,off,type) {\
+ the_ins.fragb[the_ins.nfrag].fragoff=the_ins.numo;\
+ the_ins.fragb[the_ins.nfrag].fadd=add;\
+ the_ins.fragb[the_ins.nfrag].foff=off;\
+ the_ins.fragb[the_ins.nfrag++].fragty=type;\
+ }
+
+#define isvar(exp) ((exp) && (adds(exp) || subs(exp) || gots(exp)))
+
+#define seg(exp) ((exp)->e_exp.X_seg)
+#define adds(exp) ((exp)->e_exp.X_add_symbol)
+#define subs(exp) ((exp)->e_exp.X_subtract_symbol)
+#define offs(exp) ((exp)->e_exp.X_add_number)
+#define gots(exp) ((exp)->e_exp.X_got_symbol)
+
+
+struct m68k_incant {
+ char *m_operands;
+ unsigned long m_opcode;
+ short m_opnum;
+ short m_codenum;
+ enum m68k_architecture m_arch;
+ struct m68k_incant *m_next;
+};
+
+#define getone(x) ((((x)->m_opcode)>>16)&0xffff)
+#define gettwo(x) (((x)->m_opcode)&0xffff)
+
+
+#if __STDC__ == 1
+
+static char *crack_operand(char *str, struct m68k_op *opP);
+static int get_num(struct m68k_exp *exp, int ok);
+static int get_regs(int i, char *str, struct m68k_op *opP);
+static int reverse_16_bits(int in);
+static int reverse_8_bits(int in);
+static int try_index(char **s, struct m68k_op *opP);
+static void install_gen_operand(int mode, int val);
+static void install_operand(int mode, int val);
+static void s_bss(void);
+static void s_data1(void);
+static void s_data2(void);
+static void s_even(void);
+static void s_proc(void);
+
+#else /* not __STDC__ */
+
+static char *crack_operand();
+static int get_num();
+static int get_regs();
+static int reverse_16_bits();
+static int reverse_8_bits();
+static int try_index();
+static void install_gen_operand();
+static void install_operand();
+static void s_bss();
+static void s_data1();
+static void s_data2();
+static void s_even();
+static void s_proc();
+
+#endif /* not __STDC__ */
+
+static enum m68k_architecture current_architecture = 0;
+
+/* BCC68000 is for patching in an extra jmp instruction for long offsets
+ on the 68000. The 68000 doesn't support long branches with branchs */
+
+/* This table desribes how you change sizes for the various types of variable
+ size expressions. This version only supports two kinds. */
+
+/* Note that calls to frag_var need to specify the maximum expansion needed */
+/* This is currently 10 bytes for DBCC */
+
+/* The fields are:
+ How far Forward this mode will reach:
+ How far Backward this mode will reach:
+ How many bytes this mode will add to the size of the frag
+ Which mode to go to if the offset won't fit in this one
+ */
+const relax_typeS
+ md_relax_table[] = {
+ { 1, 1, 0, 0 }, /* First entries aren't used */
+ { 1, 1, 0, 0 }, /* For no good reason except */
+ { 1, 1, 0, 0 }, /* that the VAX doesn't either */
+ { 1, 1, 0, 0 },
+
+ { (127), (-128), 0, TAB(BRANCH,SHORT)},
+ { (32767), (-32768), 2, TAB(BRANCH,LONG) },
+ { 0, 0, 4, 0 },
+ { 1, 1, 0, 0 },
+
+ { 1, 1, 0, 0 }, /* FBRANCH doesn't come BYTE */
+ { (32767), (-32768), 2, TAB(FBRANCH,LONG)},
+ { 0, 0, 4, 0 },
+ { 1, 1, 0, 0 },
+
+ { 1, 1, 0, 0 }, /* PCREL doesn't come BYTE */
+ { (32767), (-32768), 2, TAB(PCREL,LONG)},
+ { 0, 0, 4, 0 },
+ { 1, 1, 0, 0 },
+
+ { (127), (-128), 0, TAB(BCC68000,SHORT)},
+ { (32767), (-32768), 2, TAB(BCC68000,LONG) },
+ { 0, 0, 6, 0 }, /* jmp long space */
+ { 1, 1, 0, 0 },
+
+ { 1, 1, 0, 0 }, /* DBCC doesn't come BYTE */
+ { (32767), (-32768), 2, TAB(DBCC,LONG) },
+ { 0, 0, 10, 0 }, /* bra/jmp long space */
+ { 1, 1, 0, 0 },
+
+ { 1, 1, 0, 0 }, /* PCLEA doesn't come BYTE */
+ { 32767, -32768, 2, TAB(PCLEA,LONG) },
+ { 0, 0, 6, 0 },
+ { 1, 1, 0, 0 },
+
+ };
+
+/* These are the machine dependent pseudo-ops. These are included so
+ the assembler can work on the output from the SUN C compiler, which
+ generates these.
+ */
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ Integer arg to pass to the function
+ */
+const pseudo_typeS md_pseudo_table[] = {
+ { "data1", s_data1, 0 },
+ { "data2", s_data2, 0 },
+ { "bss", s_bss, 0 },
+ { "even", s_even, 0 },
+ { "skip", s_space, 0 },
+ { "proc", s_proc, 0 },
+ { 0, 0, 0 }
+};
+
+
+/* #define isbyte(x) ((x) >= -128 && (x) <= 127) */
+/* #define isword(x) ((x) >= -32768 && (x) <= 32767) */
+
+#define issbyte(x) ((x) >= -128 && (x) <= 127)
+#define isubyte(x) ((x) >= 0 && (x) <= 255)
+#define issword(x) ((x) >= -32768 && (x) <= 32767)
+#define isuword(x) ((x) >= 0 && (x) <= 65535)
+
+#define isbyte(x) ((x) >= -128 && (x) <= 255)
+#define isword(x) ((x) >= -32768 && (x) <= 65535)
+#define islong(x) (1)
+
+extern char *input_line_pointer;
+
+enum {
+ FAIL = 0,
+ OK = 1,
+};
+
+/* JF these tables here are for speed at the expense of size */
+/* You can replace them with the #if 0 versions if you really
+ need space and don't mind it running a bit slower */
+
+static char mklower_table[256];
+#define mklower(c) (mklower_table[(unsigned char)(c)])
+static char notend_table[256];
+static char alt_notend_table[256];
+#define notend(s) (!(notend_table[(unsigned char)(*s)] || (*s == ':' &&\
+ alt_notend_table[(unsigned char)(s[1])])))
+
+#if 0
+#define mklower(c) (isupper(c) ? tolower(c) : c)
+#endif
+
+/* Handle the extra arg for fix_new when doing PIC */
+#ifdef PIC
+#define FIX_NO_RELOC NO_RELOC, NULL
+#else
+#define FIX_NO_RELOC NO_RELOC
+#endif /* PIC */
+
+
+/* JF modified this to handle cases where the first part of a symbol name
+ looks like a register */
+
+/*
+ * m68k_reg_parse() := if it looks like a register, return it's token &
+ * advance the pointer.
+ */
+
+enum _register m68k_reg_parse(ccp)
+register char **ccp;
+{
+#ifndef MAX_REG_NAME_LEN
+#define MAX_REG_NAME_LEN (6)
+#endif /* MAX_REG_NAME_LEN */
+ register char c[MAX_REG_NAME_LEN];
+ char *p, *q;
+ register int n = 0,
+ ret = FAIL;
+
+ c[0] = mklower(ccp[0][0]);
+#ifdef REGISTER_PREFIX
+ if (c[0] != REGISTER_PREFIX) {
+ return(FAIL);
+ } /* need prefix */
+#endif
+
+ for (p = c, q = ccp[0]; p < c + MAX_REG_NAME_LEN; ++p, ++q)
+ {
+ if (*q == 0)
+ {
+ *p = 0;
+ break;
+ }
+ else
+ *p = mklower(*q);
+ } /* downcase */
+
+ switch (c[0]) {
+ case 'a':
+ if (c[1] >= '0' && c[1] <= '7') {
+ n=2;
+ ret=ADDR+c[1]-'0';
+ }
+#ifndef NO_68851
+ else if (c[1] == 'c') {
+ n = 2;
+ ret = AC;
+ }
+#endif
+ break;
+#ifndef NO_68851
+ case 'b':
+ if (c[1] == 'a') {
+ if (c[2] == 'd') {
+ if (c[3] >= '0' && c[3] <= '7') {
+ n = 4;
+ ret = BAD + c[3] - '0';
+ }
+ } /* BAD */
+ if (c[2] == 'c') {
+ if (c[3] >= '0' && c[3] <= '7') {
+ n = 4;
+ ret = BAC + c[3] - '0';
+ }
+ } /* BAC */
+ } else if (c[1] == 'c') {
+ n = 2;
+ ret = BC;
+ } /* BC */
+ break;
+#endif
+ case 'c':
+#ifndef NO_68851
+ if (c[1] == 'a' && c[2] == 'l') {
+ n = 3;
+ ret = CAL;
+ } else
+#endif
+ /* This supports both CCR and CC as the ccr reg. */
+ if (c[1] == 'c' && c[2] == 'r') {
+ n=3;
+ ret = CCR;
+ } else if (c[1] == 'c') {
+ n=2;
+ ret = CCR;
+ } else if (c[1] == 'a' && (c[2] == 'a' || c[2] == 'c') && c[3] == 'r') {
+ n=4;
+ ret = c[2] == 'a' ? CAAR : CACR;
+ }
+#ifndef NO_68851
+ else if (c[1] == 'r' && c[2] == 'p') {
+ n = 3;
+ ret = (CRP);
+ }
+#endif
+ break;
+ case 'd':
+ if (c[1] >= '0' && c[1] <= '7') {
+ n = 2;
+ ret = DATA + c[1] - '0';
+ } else if (c[1] == 'f' && c[2] == 'c') {
+ n = 3;
+ ret = DFC;
+ } else if (c[1] == 'c') {
+ n = 2;
+ ret = DC;
+ } else if (c[1] == 't' && c[2] == 't') {
+ if ('0' <= c[3] && c[3] <= '1') {
+ n = 4;
+ ret = DTT0 + (c[3] - '0');
+ } /* DTT[01] */
+ }
+#ifndef NO_68851
+ else if (c[1] == 'r' && c[2] == 'p') {
+ n = 3;
+ ret = (DRP);
+ }
+#endif
+ break;
+ case 'f':
+ if (c[1] == 'p') {
+ if (c[2] >= '0' && c[2] <= '7') {
+ n=3;
+ ret = FPREG+c[2]-'0';
+ if (c[3] == ':')
+ ccp[0][3]=',';
+ } else if (c[2] == 'i') {
+ n=3;
+ ret = FPI;
+ } else if (c[2] == 's') {
+ n= (c[3] == 'r' ? 4 : 3);
+ ret = FPS;
+ } else if (c[2] == 'c') {
+ n= (c[3] == 'r' ? 4 : 3);
+ ret = FPC;
+ }
+ }
+ break;
+ case 'i':
+ if (c[1] == 's' && c[2] == 'p') {
+ n = 3;
+ ret = ISP;
+ } else if (c[1] == 'c') {
+ n = 2;
+ ret = IC;
+ } else if (c[1] == 't' && c[2] == 't') {
+ if ('0' <= c[3] && c[3] <= '1') {
+ n = 4;
+ ret = ITT0 + (c[3] - '0');
+ } /* ITT[01] */
+ }
+ break;
+ case 'm':
+ if (c[1] == 's' && c[2] == 'p') {
+ n = 3;
+ ret = MSP;
+ } else if (c[1] == 'm' && c[2] == 'u' && c[3] == 's' && c[4] == 'r') {
+ n = 5;
+ ret = MMUSR;
+ }
+ break;
+ case 'n':
+ if (c[1] == 'c') {
+ n = 2;
+ ret = NC;
+ }
+ break;
+ case 'p':
+ if (c[1] == 'c') {
+#ifndef NO_68851
+ if (c[2] == 's' && c[3] == 'r') {
+ n=4;
+ ret = (PCSR);
+ } else
+#endif
+ {
+ n=2;
+ ret = PC;
+ }
+ }
+#ifndef NO_68851
+ else if (c[1] == 's' && c[2] == 'r') {
+ n = 3;
+ ret = (PSR);
+ }
+#endif
+ break;
+ case 's':
+#ifndef NO_68851
+ if (c[1] == 'c' && c[2] == 'c') {
+ n = 3;
+ ret = (SCC);
+ } else
+#endif
+ if (c[1] == 'r') {
+ if (c[2] == 'p') {
+ n = 3;
+ ret = SRP;
+ } else {
+ n = 2;
+ ret = SR;
+ } /* srp else sr */
+ } else if (c[1] == 'p') {
+ n = 2;
+ ret = SP;
+ } else if (c[1] == 'f' && c[2] == 'c') {
+ n = 3;
+ ret = SFC;
+ }
+ break;
+ case 't':
+ if (c[1] == 'c') {
+ n = 2;
+ ret = TC;
+ }
+ break;
+ case 'u':
+ if (c[1] == 's' && c[2] == 'p') {
+ n=3;
+ ret = USP;
+ } else if (c[1] == 'r' && c[2] == 'p') {
+ n = 3;
+ ret = URP;
+ }
+ break;
+ case 'v':
+#ifndef NO_68851
+ if (c[1] == 'a' && c[2] == 'l') {
+ n = 3;
+ ret = (VAL);
+ } else
+#endif
+ if (c[1] == 'b' && c[2] == 'r') {
+ n=3;
+ ret = VBR;
+ }
+ break;
+ case 'z':
+ if (c[1] == 'p' && c[2] == 'c') {
+ n=3;
+ ret = ZPC;
+ }
+ break;
+ default:
+ break;
+ }
+ if (n) {
+#ifdef REGISTER_PREFIX
+ n++;
+#endif
+ if (isalnum(ccp[0][n]) || ccp[0][n] == '_')
+ ret=FAIL;
+ else
+ ccp[0]+=n;
+ } else
+ ret = FAIL;
+ return ret;
+}
+
+#define SKIP_WHITE() { str++; if (*str == ' ') str++;}
+
+/*
+ * m68k_ip_op := '#' + <anything>
+ * | <register> + range_sep + get_regs
+ * ;
+ *
+ * range_sep := '/' | '-' ;
+ *
+ * SKIP_WHITE := <empty> | ' ' ;
+ *
+ */
+
+int
+ m68k_ip_op(str,opP)
+char *str;
+register struct m68k_op *opP;
+{
+ char *strend;
+ long i;
+ char *parse_index();
+
+ if (*str == ' ') {
+ str++;
+ } /* Find the beginning of the string */
+
+ if (!*str) {
+ opP->error="Missing operand";
+ return FAIL;
+ } /* Out of gas */
+
+ for (strend = str; *strend; strend++) ;;
+
+ --strend;
+
+ if (*str == '#') {
+ str++;
+ opP->con1=add_exp(str,strend);
+ opP->mode=IMMED;
+ return OK;
+ } /* Guess what: A constant. Shar and enjoy */
+
+ i = m68k_reg_parse(&str);
+
+ /* is a register, is exactly a register, and is followed by '@' */
+
+ if ((i == FAIL || *str != '\0') && *str != '@') {
+ char *stmp;
+
+ if (i != FAIL && (*str == '/' || *str == '-')) {
+ opP->mode=REGLST;
+ return(get_regs(i,str,opP));
+ }
+ if ((stmp=strchr(str,'@')) != '\0') {
+ opP->con1=add_exp(str,stmp-1);
+ if (stmp == strend) {
+ opP->mode=AINDX;
+ return(OK);
+ }
+
+ if ((current_architecture & m68020up) == 0) {
+ return(FAIL);
+ } /* if target is not a '20 or better */
+
+ stmp++;
+ if (*stmp++ != '(' || *strend-- != ')') {
+ opP->error="Malformed operand";
+ return(FAIL);
+ }
+ i=try_index(&stmp,opP);
+ opP->con2=add_exp(stmp,strend);
+
+ if (i == FAIL) {
+ opP->mode=AMIND;
+ } else {
+ opP->mode=APODX;
+ }
+ return(OK);
+ } /* if there's an '@' */
+ opP->mode = ABSL;
+ opP->con1 = add_exp(str,strend);
+ return(OK);
+ } /* not a register, not exactly a register, or no '@' */
+
+ opP->reg=i;
+
+ if (*str == '\0') {
+ if (i >= DATA+0 && i <= DATA+7)
+ opP->mode=DREG;
+ else if (i >= ADDR+0 && i <= ADDR+7)
+ opP->mode=AREG;
+ else
+ opP->mode=MSCR;
+ return OK;
+ }
+
+ if ((i<ADDR+0 || i>ADDR+7) && i != PC && i != ZPC && i != FAIL) { /* Can't indirect off non address regs */
+ opP->error="Invalid indirect register";
+ return FAIL;
+ }
+ know(*str == '@');
+
+ str++;
+ switch (*str) {
+ case '\0':
+ opP->mode=AINDR;
+ return OK;
+ case '-':
+ opP->mode=ADEC;
+ return OK;
+ case '+':
+ opP->mode=AINC;
+ return OK;
+ case '(':
+ str++;
+ break;
+ default:
+ opP->error="Junk after indirect";
+ return FAIL;
+ }
+ /* Some kind of indexing involved. Lets find out how bad it is */
+ i=try_index(&str,opP);
+ /* Didn't start with an index reg, maybe its offset or offset,reg */
+ if (i == FAIL) {
+ char *beg_str;
+
+ beg_str=str;
+ for (i=1;i;) {
+ switch (*str++) {
+ case '\0':
+ opP->error="Missing )";
+ return FAIL;
+ case ',': i=0; break;
+ case '(': i++; break;
+ case ')': --i; break;
+ }
+ }
+ /* if (str[-3] == ':') {
+ int siz;
+
+ switch (str[-2]) {
+ case 'b':
+ case 'B':
+ siz=1;
+ break;
+ case 'w':
+ case 'W':
+ siz=2;
+ break;
+ case 'l':
+ case 'L':
+ siz=3;
+ break;
+ default:
+ opP->error="Specified size isn't :w or :l";
+ return FAIL;
+ }
+ opP->con1=add_exp(beg_str,str-4);
+ opP->con1->e_siz=siz;
+ } else */
+ opP->con1=add_exp(beg_str,str-2);
+ /* Should be offset,reg */
+ if (str[-1] == ',') {
+ i=try_index(&str,opP);
+ if (i == FAIL) {
+ opP->error="Malformed index reg";
+ return FAIL;
+ }
+ }
+ }
+ /* We've now got offset) offset,reg) or reg) */
+
+ if (*str == '\0') {
+ /* Th-the-thats all folks */
+ if (opP->reg == FAIL) opP->mode = AINDX; /* Other form of indirect */
+ else if (opP->ireg == FAIL) opP->mode = AOFF;
+ else opP->mode = AINDX;
+ return(OK);
+ }
+ /* Next thing had better be another @ */
+ if (*str != '@' || str[1] != '(') {
+ opP->error = "junk after indirect";
+ return(FAIL);
+ }
+
+ if ((current_architecture & m68020up) == 0) {
+ return(FAIL);
+ } /* if target is not a '20 or better */
+
+ str+=2;
+
+ if (opP->ireg != FAIL) {
+ opP->mode = APRDX;
+
+ i = try_index(&str, opP);
+ if (i != FAIL) {
+ opP->error = "Two index registers! not allowed!";
+ return(FAIL);
+ }
+ } else {
+ i = try_index(&str, opP);
+ }
+
+ if (i == FAIL) {
+ char *beg_str;
+
+ beg_str = str;
+
+ for (i = 1; i; ) {
+ switch (*str++) {
+ case '\0':
+ opP->error="Missing )";
+ return(FAIL);
+ case ',': i=0; break;
+ case '(': i++; break;
+ case ')': --i; break;
+ }
+ }
+
+ opP->con2=add_exp(beg_str,str-2);
+
+ if (str[-1] == ',') {
+ if (opP->ireg != FAIL) {
+ opP->error = "Can't have two index regs";
+ return(FAIL);
+ }
+
+ i = try_index(&str, opP);
+
+ if (i == FAIL) {
+ opP->error = "malformed index reg";
+ return(FAIL);
+ }
+
+ opP->mode = APODX;
+ } else if (opP->ireg != FAIL) {
+ opP->mode = APRDX;
+ } else {
+ opP->mode = AMIND;
+ }
+ } else {
+ opP->mode = APODX;
+ }
+
+ if (*str != '\0') {
+ opP->error="Junk after indirect";
+ return FAIL;
+ }
+ return(OK);
+} /* m68k_ip_op() */
+
+/*
+ *
+ * try_index := data_or_address_register + ')' + SKIP_W
+ * | data_or_address_register + ':' + SKIP_W + size_spec + SKIP_W + multiplier + ')' + SKIP_W
+ *
+ * multiplier := <empty>
+ * | ':' + multiplier_number
+ * ;
+ *
+ * multiplier_number := '1' | '2' | '4' | '8' ;
+ *
+ * size_spec := 'l' | 'L' | 'w' | 'W' ;
+ *
+ * SKIP_W := <empty> | ' ' ;
+ *
+ */
+
+static int try_index(s,opP)
+char **s;
+struct m68k_op *opP;
+{
+ register int i;
+ char *ss;
+#define SKIP_W() { ss++; if (*ss == ' ') ss++;}
+
+ ss= *s;
+ /* SKIP_W(); */
+ i=m68k_reg_parse(&ss);
+ if (!(i >= DATA+0 && i <= ADDR+7)) { /* if i is not DATA or ADDR reg */
+ *s=ss;
+ return FAIL;
+ }
+ opP->ireg=i;
+ /* SKIP_W(); */
+ if (*ss == ')') {
+ opP->isiz=0;
+ opP->imul=1;
+ SKIP_W();
+ *s=ss;
+ return OK;
+ }
+ if (*ss != ':') {
+ opP->error="Missing : in index register";
+ *s=ss;
+ return FAIL;
+ }
+ SKIP_W();
+ switch (*ss) {
+ case 'w':
+ case 'W':
+ opP->isiz=2;
+ break;
+ case 'l':
+ case 'L':
+ opP->isiz=3;
+ break;
+ default:
+ opP->error="Index register size spec not :w or :l";
+ *s=ss;
+ return FAIL;
+ }
+ SKIP_W();
+ if (*ss == ':') {
+ SKIP_W();
+ switch (*ss) {
+ case '1':
+ case '2':
+ case '4':
+ case '8':
+ opP->imul= *ss-'0';
+ break;
+ default:
+ opP->error="index multiplier not 1, 2, 4 or 8";
+ *s=ss;
+ return FAIL;
+ }
+ SKIP_W();
+ } else opP->imul=1;
+ if (*ss != ')') {
+ opP->error="Missing )";
+ *s=ss;
+ return FAIL;
+ }
+ SKIP_W();
+ *s=ss;
+ return OK;
+} /* try_index() */
+
+#ifdef TEST1 /* TEST1 tests m68k_ip_op(), which parses operands */
+main()
+{
+ char buf[128];
+ struct m68k_op thark;
+
+ for (;;) {
+ if (!gets(buf))
+ break;
+ memset(&thark, '\0', sizeof(thark));
+ if (!m68k_ip_op(buf,&thark)) printf("FAIL:");
+ if (thark.error)
+ printf("op1 error %s in %s\n",thark.error,buf);
+ printf("mode %d, reg %d, ",thark.mode,thark.reg);
+ if (thark.b_const)
+ printf("Constant: '%.*s',",1+thark.e_const-thark.b_const,thark.b_const);
+ printf("ireg %d, isiz %d, imul %d ",thark.ireg,thark.isiz,thark.imul);
+ if (thark.b_iadd)
+ printf("Iadd: '%.*s'",1+thark.e_iadd-thark.b_iadd,thark.b_iadd);
+ printf("\n");
+ }
+ exit(0);
+}
+
+#endif
+
+
+static struct hash_control* op_hash = NULL; /* handle of the OPCODE hash table
+ NULL means any use before m68k_ip_begin()
+ will crash */
+
+
+/*
+ * m 6 8 k _ i p ( )
+ *
+ * This converts a string into a 68k instruction.
+ * The string must be a bare single instruction in sun format
+ * with RMS-style 68020 indirects
+ * (example: )
+ *
+ * It provides some error messages: at most one fatal error message (which
+ * stops the scan) and at most one warning message for each operand.
+ * The 68k instruction is returned in exploded form, since we have no
+ * knowledge of how you parse (or evaluate) your expressions.
+ * We do however strip off and decode addressing modes and operation
+ * mnemonic.
+ *
+ * This function's value is a string. If it is not "" then an internal
+ * logic error was found: read this code to assign meaning to the string.
+ * No argument string should generate such an error string:
+ * it means a bug in our code, not in the user's text.
+ *
+ * You MUST have called m68k_ip_begin() once and m86_ip_end() never before using
+ * this function.
+ */
+
+/* JF this function no longer returns a useful value. Sorry */
+void m68k_ip (instring)
+char *instring;
+{
+ register char *p;
+ register struct m68k_op *opP;
+ register struct m68k_incant *opcode;
+ register char *s;
+ register int tmpreg = 0,
+ baseo = 0,
+ outro = 0,
+ nextword;
+ int siz1,
+ siz2;
+ char c;
+ int losing;
+ int opsfound;
+ int reloc_type;
+ char *crack_operand();
+ LITTLENUM_TYPE words[6];
+ LITTLENUM_TYPE *wordp;
+
+ if (*instring == ' ')
+ instring++; /* skip leading whitespace */
+
+ /* Scan up to end of operation-code, which MUST end in end-of-string
+ or exactly 1 space. */
+ for (p = instring; *p != '\0'; p++)
+ if (*p == ' ')
+ break;
+
+
+ if (p == instring) {
+ the_ins.error = "No operator";
+ the_ins.opcode[0] = NULL;
+ /* the_ins.numo=1; */
+ return;
+ }
+
+ /* p now points to the end of the opcode name, probably whitespace.
+ make sure the name is null terminated by clobbering the whitespace,
+ look it up in the hash table, then fix it back. */
+ c = *p;
+ *p = '\0';
+ opcode = (struct m68k_incant *)hash_find (op_hash, instring);
+ *p = c;
+
+ if (opcode == NULL) {
+ the_ins.error = "Unknown operator";
+ the_ins.opcode[0] = NULL;
+ /* the_ins.numo=1; */
+ return;
+ }
+
+ /* found a legitimate opcode, start matching operands */
+ while (*p == ' ') ++p;
+
+ for (opP = &the_ins.operands[0]; *p; opP++) {
+
+ p = crack_operand(p, opP);
+
+ if (opP->error) {
+ the_ins.error=opP->error;
+ return;
+ }
+ }
+
+ opsfound = opP - &the_ins.operands[0];
+
+ /* This ugly hack is to support the floating pt opcodes in their standard form */
+ /* Essentially, we fake a first enty of type COP#1 */
+ if (opcode->m_operands[0] == 'I') {
+ int n;
+
+ for (n=opsfound;n>0;--n)
+ the_ins.operands[n]=the_ins.operands[n-1];
+
+ /* memcpy((char *)(&the_ins.operands[1]), (char *)(&the_ins.operands[0]), opsfound*sizeof(the_ins.operands[0])); */
+ memset((char *)(&the_ins.operands[0]), '\0', sizeof(the_ins.operands[0]));
+ the_ins.operands[0].mode=MSCR;
+ the_ins.operands[0].reg=COPNUM; /* COP #1 */
+ opsfound++;
+ }
+
+ /* We've got the operands. Find an opcode that'll accept them */
+ for (losing = 0; ; ) {
+ /* if we didn't get the right number of ops,
+ or we have no common model with this pattern
+ then reject this pattern. */
+
+ if (opsfound != opcode->m_opnum
+ || ((opcode->m_arch & current_architecture) == 0)) {
+
+ ++losing;
+
+ } else {
+ for (s=opcode->m_operands, opP = &the_ins.operands[0]; *s && !losing; s += 2, opP++) {
+ /* Warning: this switch is huge! */
+ /* I've tried to organize the cases into this order:
+ non-alpha first, then alpha by letter. lower-case goes directly
+ before uppercase counterpart. */
+ /* Code with multiple case ...: gets sorted by the lowest case ...
+ it belongs to. I hope this makes sense. */
+ switch (*s) {
+#ifdef PIC
+ case ' ':
+ /* this operand is just here to indicate a jump-table branch */
+ if (!flagseen['k'])
+ losing++;
+ break;
+#endif /* PIC */
+
+ case '!':
+ if (opP->mode == MSCR || opP->mode == IMMED
+ || opP->mode == DREG || opP->mode == AREG
+ || opP->mode == AINC || opP->mode == ADEC
+ || opP->mode == REGLST)
+ losing++;
+ break;
+
+ case '#':
+ if (opP->mode != IMMED)
+ losing++;
+ else {
+ long t;
+
+ t=get_num(opP->con1,80);
+ if (s[1] == 'b' && !isbyte(t))
+ losing++;
+ else if (s[1] == 'w' && !isword(t))
+ losing++;
+ }
+ break;
+
+ case '^':
+ case 'T':
+ if (opP->mode != IMMED)
+ losing++;
+ break;
+
+ case '$':
+ if (opP->mode == MSCR || opP->mode == AREG ||
+ opP->mode == IMMED || opP->reg == PC || opP->reg == ZPC || opP->mode == REGLST)
+ losing++;
+ break;
+
+ case '%':
+ if (opP->mode == MSCR || opP->reg == PC ||
+ opP->reg == ZPC || opP->mode == REGLST)
+ losing++;
+ break;
+
+
+ case '&':
+ if (opP->mode == MSCR || opP->mode == DREG ||
+ opP->mode == AREG || opP->mode == IMMED || opP->reg == PC || opP->reg == ZPC ||
+ opP->mode == AINC || opP->mode == ADEC || opP->mode == REGLST)
+ losing++;
+ break;
+
+ case '*':
+ if (opP->mode == MSCR || opP->mode == REGLST)
+ losing++;
+ break;
+
+ case '+':
+ if (opP->mode != AINC)
+ losing++;
+ break;
+
+ case '-':
+ if (opP->mode != ADEC)
+ losing++;
+ break;
+
+ case '/':
+ if (opP->mode == MSCR || opP->mode == AREG ||
+ opP->mode == AINC || opP->mode == ADEC || opP->mode == IMMED || opP->mode == REGLST)
+ losing++;
+ break;
+
+ case ';':
+ if (opP->mode == MSCR || opP->mode == AREG || opP->mode == REGLST)
+ losing++;
+ break;
+
+ case '?':
+ if (opP->mode == MSCR || opP->mode == AREG ||
+ opP->mode == AINC || opP->mode == ADEC || opP->mode == IMMED || opP->reg == PC ||
+ opP->reg == ZPC || opP->mode == REGLST)
+ losing++;
+ break;
+
+ case '@':
+ if (opP->mode == MSCR || opP->mode == AREG ||
+ opP->mode == IMMED || opP->mode == REGLST)
+ losing++;
+ break;
+
+ case '~': /* For now! (JF FOO is this right?) */
+ if (opP->mode == MSCR || opP->mode == DREG ||
+ opP->mode == AREG || opP->mode == IMMED || opP->reg == PC || opP->reg == ZPC || opP->mode == REGLST)
+ losing++;
+ break;
+
+ case 'A':
+ if (opP->mode != AREG)
+ losing++;
+ break;
+ case 'a':
+ if (opP->mode != AINDR) {
+ ++losing;
+ } /* if not address register indirect */
+ break;
+ case 'B': /* FOO */
+ if (opP->mode != ABSL || (flagseen['S'] && instring[0] == 'j'
+ && instring[1] == 'b'
+ && instring[2] == 's'
+ && instring[3] == 'r'))
+ losing++;
+ break;
+
+ case 'C':
+ if (opP->mode != MSCR || opP->reg != CCR)
+ losing++;
+ break;
+
+ case 'd': /* FOO This mode is a KLUDGE!! */
+ if (opP->mode != AOFF && (opP->mode != ABSL ||
+ opP->con1->e_beg[0] != '(' || opP->con1->e_end[0] != ')'))
+ losing++;
+ break;
+
+ case 'D':
+ if (opP->mode != DREG)
+ losing++;
+ break;
+
+ case 'F':
+ if (opP->mode != MSCR || opP->reg<(FPREG+0) || opP->reg>(FPREG+7))
+ losing++;
+ break;
+
+ case 'I':
+ if (opP->mode != MSCR || opP->reg<COPNUM ||
+ opP->reg >= COPNUM+7)
+ losing++;
+ break;
+
+ case 'J':
+ if (opP->mode != MSCR
+ || opP->reg < USP
+ || opP->reg > URP
+ || cpu_of_arch(current_architecture) < m68010 /* before 68010 had none */
+ || (cpu_of_arch(current_architecture) < m68020
+ && opP->reg != SFC
+ && opP->reg != DFC
+ && opP->reg != USP
+ && opP->reg != VBR) /* 68010's had only these */
+ || (cpu_of_arch(current_architecture) < m68040
+ && opP->reg != SFC
+ && opP->reg != DFC
+ && opP->reg != USP
+ && opP->reg != VBR
+ && opP->reg != CACR
+ && opP->reg != CAAR
+ && opP->reg != MSP
+ && opP->reg != ISP) /* 680[23]0's have only these */
+ || (cpu_of_arch(current_architecture) == m68040 /* 68040 has all but this */
+ && opP->reg == CAAR)) {
+ losing++;
+ } /* doesn't cut it */
+ break;
+
+ case 'k':
+ if (opP->mode != IMMED)
+ losing++;
+ break;
+
+ case 'l':
+ case 'L':
+ if (opP->mode == DREG || opP->mode == AREG || opP->mode == FPREG) {
+ if (s[1] == '8')
+ losing++;
+ else {
+ opP->mode=REGLST;
+ opP->reg=1<<(opP->reg-DATA);
+ }
+ } else if (opP->mode != REGLST) {
+ losing++;
+ } else if (s[1] == '8' && opP->reg&0x0FFffFF)
+ losing++;
+ else if (s[1] == '3' && opP->reg&0x7000000)
+ losing++;
+ break;
+
+ case 'M':
+ if (opP->mode != IMMED)
+ losing++;
+ else {
+ long t;
+
+ t=get_num(opP->con1,80);
+ if (!issbyte(t) || isvar(opP->con1))
+ losing++;
+ }
+ break;
+
+ case 'O':
+ if (opP->mode != DREG && opP->mode != IMMED)
+ losing++;
+ break;
+
+ case 'Q':
+ if (opP->mode != IMMED)
+ losing++;
+ else {
+ long t;
+
+ t=get_num(opP->con1,80);
+ if (t<1 || t>8 || isvar(opP->con1))
+ losing++;
+ }
+ break;
+
+ case 'R':
+ if (opP->mode != DREG && opP->mode != AREG)
+ losing++;
+ break;
+
+ case 's':
+ if (opP->mode != MSCR || !(opP->reg == FPI || opP->reg == FPS || opP->reg == FPC))
+ losing++;
+ break;
+
+ case 'S':
+ if (opP->mode != MSCR || opP->reg != SR)
+ losing++;
+ break;
+
+ case 'U':
+ if (opP->mode != MSCR || opP->reg != USP)
+ losing++;
+ break;
+
+ /* JF these are out of order. We could put them
+ in order if we were willing to put up with
+ bunches of #ifdef m68851s in the code */
+#ifndef NO_68851
+ /* Memory addressing mode used by pflushr */
+ case '|':
+ if (opP->mode == MSCR || opP->mode == DREG ||
+ opP->mode == AREG || opP->mode == REGLST)
+ losing++;
+ break;
+
+ case 'f':
+ if (opP->mode != MSCR || (opP->reg != SFC && opP->reg != DFC))
+ losing++;
+ break;
+
+ case 'P':
+ if (opP->mode != MSCR || (opP->reg != TC && opP->reg != CAL &&
+ opP->reg != VAL && opP->reg != SCC && opP->reg != AC))
+ losing++;
+ break;
+
+ case 'V':
+ if (opP->reg != VAL)
+ losing++;
+ break;
+
+ case 'W':
+ if (opP->mode != MSCR || (opP->reg != DRP && opP->reg != SRP &&
+ opP->reg != CRP))
+ losing++;
+ break;
+
+ case 'X':
+ if (opP->mode != MSCR ||
+ (!(opP->reg >= BAD && opP->reg <= BAD+7) &&
+ !(opP->reg >= BAC && opP->reg <= BAC+7)))
+ losing++;
+ break;
+
+ case 'Y':
+ if (opP->reg != PSR)
+ losing++;
+ break;
+
+ case 'Z':
+ if (opP->reg != PCSR)
+ losing++;
+ break;
+#endif
+ case 'c':
+ if (opP->reg != NC
+ && opP->reg != IC
+ && opP->reg != DC
+ && opP->reg != BC) {
+ losing++;
+ } /* not a cache specifier. */
+ break;
+
+ case '_':
+ if (opP->mode != ABSL) {
+ ++losing;
+ } /* not absolute */
+ break;
+
+ default:
+ as_fatal("Internal error: Operand mode %c unknown in line %s of file \"%s\"",
+ *s, __LINE__, __FILE__);
+ } /* switch on type of operand */
+
+ if (losing) break;
+ } /* for each operand */
+ } /* if immediately wrong */
+
+ if (!losing) {
+ break;
+ } /* got it. */
+
+ opcode = opcode->m_next;
+
+ if (!opcode) {
+ the_ins.error = "instruction/operands mismatch";
+ return;
+ } /* Fell off the end */
+
+ losing = 0;
+ }
+
+ /* now assemble it */
+
+ the_ins.args=opcode->m_operands;
+ the_ins.numargs=opcode->m_opnum;
+ the_ins.numo=opcode->m_codenum;
+ the_ins.opcode[0]=getone(opcode);
+ the_ins.opcode[1]=gettwo(opcode);
+
+ for (s = the_ins.args, opP = &the_ins.operands[0]; *s; s += 2, opP++) {
+ /* This switch is a doozy.
+ Watch the first step; its a big one! */
+ switch (s[0]) {
+
+#ifdef PIC
+ case ' ':
+ /* this operand is just here to indicate a jump-table branch */
+ break;
+#endif /* PIC */
+
+ case '*':
+ case '~':
+ case '%':
+ case ';':
+ case '@':
+ case '!':
+ case '&':
+ case '$':
+ case '?':
+ case '/':
+#ifndef NO_68851
+ case '|':
+#endif
+
+#ifdef PIC
+ /* Use GLOB_DAT for operand references in PIC mode */
+ if (flagseen['k'])
+ reloc_type = RELOC_GLOB_DAT;
+ else
+#endif /* PIC */
+ reloc_type = NO_RELOC;
+
+ switch (opP->mode) {
+ case IMMED:
+ tmpreg=0x3c; /* 7.4 */
+ if (strchr("bwl",s[1])) nextword=get_num(opP->con1,80);
+ else nextword=nextword=get_num(opP->con1,0);
+ if (isvar(opP->con1)) {
+#ifdef PIC
+ /* KLUDGE!!! In PIC assembly, an immediate reference to
+ __GLOBAL_OFFSET_TABLE_ is turned into a pc-relative
+ reference to __GLOBAL_OFFSET_TABLE_ - 6,
+ for the sake of Sun compatibility. */
+ if (s[1] == 'l' && flagseen['k'] && gots(opP->con1)) {
+ offs(opP->con1) -= 6;
+ add_fix(s[1], opP->con1, 1, NO_RELOC);
+ } else
+#endif /* PIC */
+ add_fix(s[1],opP->con1,0,reloc_type);
+ }
+ switch (s[1]) {
+ case 'b':
+ if (!isbyte(nextword))
+ opP->error="operand out of range";
+ addword(nextword);
+ baseo=0;
+ break;
+ case 'w':
+ if (!isword(nextword))
+ opP->error="operand out of range";
+ addword(nextword);
+ baseo=0;
+ break;
+ case 'l':
+ addword(nextword>>16);
+ addword(nextword);
+ baseo=0;
+ break;
+
+ case 'f':
+ baseo=2;
+ outro=8;
+ break;
+ case 'F':
+ baseo=4;
+ outro=11;
+ break;
+ case 'x':
+ baseo=6;
+ outro=15;
+ break;
+ case 'p':
+ baseo=6;
+ outro= -1;
+ break;
+ default:
+ as_fatal("Internal error: Can't decode %c%c in line %s of file \"%s\"",
+ *s, s[1], __LINE__, __FILE__);
+ }
+ if (!baseo)
+ break;
+
+ /* We gotta put out some float */
+ if (seg(opP->con1) != SEG_BIG) {
+ int_to_gen(nextword);
+ gen_to_words(words,baseo,(long int)outro);
+ for (wordp=words;baseo--;wordp++)
+ addword(*wordp);
+ break;
+ } /* Its BIG */
+ if (offs(opP->con1)>0) {
+ as_warn("Bignum assumed to be binary bit-pattern");
+ if (offs(opP->con1)>baseo) {
+ as_warn("Bignum too big for %c format; truncated",s[1]);
+ offs(opP->con1)=baseo;
+ }
+ baseo-=offs(opP->con1);
+ for (wordp=generic_bignum+offs(opP->con1)-1;offs(opP->con1)--;--wordp)
+ addword(*wordp);
+ while (baseo--)
+ addword(0);
+ break;
+ }
+ gen_to_words(words,baseo,(long)outro);
+ for (wordp=words;baseo--;wordp++)
+ addword(*wordp);
+ break;
+ case DREG:
+ tmpreg=opP->reg-DATA; /* 0.dreg */
+ break;
+ case AREG:
+ tmpreg=0x08+opP->reg-ADDR; /* 1.areg */
+ break;
+ case AINDR:
+ tmpreg=0x10+opP->reg-ADDR; /* 2.areg */
+ break;
+ case ADEC:
+ tmpreg=0x20+opP->reg-ADDR; /* 4.areg */
+ break;
+ case AINC:
+ tmpreg=0x18+opP->reg-ADDR; /* 3.areg */
+ break;
+ case AOFF:
+
+ nextword=get_num(opP->con1,80);
+ /* Force into index mode. Hope this works */
+
+ /* We do the first bit for 32-bit displacements,
+ and the second bit for 16 bit ones. It is
+ possible that we should make the default be
+ WORD instead of LONG, but I think that'd
+ break GCC, so we put up with a little
+ inefficiency for the sake of working output.
+ */
+
+ if ( !issword(nextword)
+ || ( isvar(opP->con1)
+ && ((opP->con1->e_siz == 0
+ && flagseen['l'] == 0)
+ || opP->con1->e_siz == 3))) {
+
+ if (opP->reg == PC)
+ tmpreg=0x3B; /* 7.3 */
+ else
+ tmpreg=0x30+opP->reg-ADDR; /* 6.areg */
+ if (isvar(opP->con1)) {
+ if (opP->reg == PC && !subs(opP->con1)) {
+ add_frag(adds(opP->con1),
+ offs(opP->con1),
+ TAB(PCLEA,SZ_UNDEF));
+ break;
+ } else {
+ addword(0x0170);
+ add_fix('l',opP->con1,0,reloc_type);
+ }
+ } else
+ addword(0x0170);
+ addword(nextword>>16);
+ } else {
+ if (opP->reg == PC)
+ tmpreg=0x3A; /* 7.2 */
+ else
+ tmpreg=0x28+opP->reg-ADDR; /* 5.areg */
+
+ if (isvar(opP->con1)) {
+ if (opP->reg == PC) {
+ add_fix('w',opP->con1,1,NO_RELOC);
+ } else
+ add_fix('w',opP->con1,0,reloc_type);
+ }
+ }
+ addword(nextword);
+ break;
+
+ case APODX:
+ case AMIND:
+ case APRDX:
+ know(current_architecture & m68020up);
+ /* intentional fall-through */
+ case AINDX:
+ nextword=0;
+ baseo=get_num(opP->con1,80);
+ outro=get_num(opP->con2,80);
+ /* Figure out the 'addressing mode' */
+ /* Also turn on the BASE_DISABLE bit, if needed */
+ if (opP->reg == PC || opP->reg == ZPC) {
+ tmpreg=0x3b; /* 7.3 */
+ if (opP->reg == ZPC)
+ nextword|=0x80;
+ } else if (opP->reg == FAIL) {
+ nextword|=0x80;
+ tmpreg=0x30; /* 6.garbage */
+ } else tmpreg=0x30+opP->reg-ADDR; /* 6.areg */
+
+ siz1= (opP->con1) ? opP->con1->e_siz : 0;
+ siz2= (opP->con2) ? opP->con2->e_siz : 0;
+
+ /* Index register stuff */
+ if (opP->ireg >= DATA+0 && opP->ireg <= ADDR+7) {
+ nextword|=(opP->ireg-DATA)<<12;
+
+ if (opP->isiz == 0 || opP->isiz == 3)
+ nextword|=0x800;
+ switch (opP->imul) {
+ case 1: break;
+ case 2: nextword|=0x200; break;
+ case 4: nextword|=0x400; break;
+ case 8: nextword|=0x600; break;
+ default: as_fatal("failed sanity check.");
+ }
+ /* IF its simple,
+ GET US OUT OF HERE! */
+
+ /* Must be INDEX, with an index
+ register. Address register
+ cannot be ZERO-PC, and either
+ :b was forced, or we know
+ it will fit */
+ if (opP->mode == AINDX
+ && opP->reg != FAIL
+ && opP->reg != ZPC
+ && (siz1 == 1
+ || ( issbyte(baseo)
+ && !isvar(opP->con1)))) {
+ nextword +=baseo&0xff;
+ addword(nextword);
+ if (isvar(opP->con1))
+ add_fix('B',opP->con1,0,reloc_type);
+ break;
+ }
+ } else
+ nextword|=0x40; /* No index reg */
+
+ /* It aint simple */
+ nextword|=0x100;
+ /* If the guy specified a width, we assume that
+ it is wide enough. Maybe it isn't. If so, we lose
+ */
+ switch (siz1) {
+ case 0:
+ if (isvar(opP->con1) || !issword(baseo)) {
+ siz1=3;
+ nextword|=0x30;
+ } else if (baseo == 0)
+ nextword|=0x10;
+ else {
+ nextword|=0x20;
+ siz1=2;
+ }
+ break;
+ case 1:
+ as_warn("Byte dispacement won't work. Defaulting to :w");
+ case 2:
+ nextword|=0x20;
+ break;
+ case 3:
+ nextword|=0x30;
+ break;
+ }
+
+ /* Figure out innner displacement stuff */
+ if (opP->mode != AINDX) {
+ switch (siz2) {
+ case 0:
+ if (isvar(opP->con2) || !issword(outro)) {
+ siz2=3;
+ nextword|=0x3;
+ } else if (outro == 0)
+ nextword|=0x1;
+ else {
+ nextword|=0x2;
+ siz2=2;
+ }
+ break;
+ case 1:
+ as_warn("Byte dispacement won't work. Defaulting to :w");
+ case 2:
+ nextword|=0x2;
+ break;
+ case 3:
+ nextword|=0x3;
+ break;
+ }
+ if (opP->mode == APODX) nextword|=0x04;
+ else if (opP->mode == AMIND) nextword|=0x40;
+ }
+ addword(nextword);
+
+ if (isvar(opP->con1)) {
+ if (opP->reg == PC || opP->reg == ZPC) {
+ add_fix(siz1 == 3 ? 'l' : 'w',opP->con1,1,NO_RELOC);
+ opP->con1->e_exp.X_add_number+=6;
+ } else
+ add_fix(siz1 == 3 ? 'l' : 'w',opP->con1,0,reloc_type);
+ }
+ if (siz1 == 3)
+ addword(baseo>>16);
+ if (siz1)
+ addword(baseo);
+
+ if (isvar(opP->con2)) {
+ if (opP->reg == PC || opP->reg == ZPC) {
+ add_fix(siz2 == 3 ? 'l' : 'w',opP->con2,1,NO_RELOC);
+ opP->con1->e_exp.X_add_number+=6;
+ } else
+ add_fix(siz2 == 3 ? 'l' : 'w',opP->con2,0,reloc_type);
+ }
+ if (siz2 == 3)
+ addword(outro>>16);
+ if (siz2)
+ addword(outro);
+
+ break;
+
+ case ABSL:
+ nextword=get_num(opP->con1,80);
+ switch (opP->con1->e_siz) {
+ default:
+ as_warn("Unknown size for absolute reference");
+ case 0:
+ if (!isvar(opP->con1) && issword(offs(opP->con1))) {
+ tmpreg=0x38; /* 7.0 */
+ addword(nextword);
+ break;
+ }
+ /* Don't generate pc relative code
+ on 68010 and 68000 */
+ if (isvar(opP->con1)
+ && !subs(opP->con1)
+ && seg(opP->con1) == SEG_TEXT
+ && now_seg == SEG_TEXT
+ && cpu_of_arch(current_architecture) >= m68020
+ && !flagseen['S']
+ && !strchr("~%&$?", s[0])) {
+ tmpreg=0x3A; /* 7.2 */
+ add_frag(adds(opP->con1),
+ offs(opP->con1),
+ TAB(PCREL,SZ_UNDEF));
+ break;
+ }
+ case 3: /* Fall through into long */
+ if (isvar(opP->con1))
+ add_fix('l',opP->con1,0,NO_RELOC);
+
+ tmpreg=0x39; /* 7.1 mode */
+ addword(nextword>>16);
+ addword(nextword);
+ break;
+
+ case 2: /* Word */
+ if (isvar(opP->con1))
+ add_fix('w',opP->con1,0,NO_RELOC);
+
+ tmpreg=0x38; /* 7.0 mode */
+ addword(nextword);
+ break;
+ }
+ break;
+ case MSCR:
+ default:
+ as_bad("unknown/incorrect operand");
+ /* abort(); */
+ }
+ install_gen_operand(s[1],tmpreg);
+ break;
+
+ case '#':
+ case '^':
+ switch (s[1]) { /* JF: I hate floating point! */
+ case 'j':
+ tmpreg=70;
+ break;
+ case '8':
+ tmpreg=20;
+ break;
+ case 'C':
+ tmpreg=50;
+ break;
+ case '3':
+ default:
+ tmpreg=80;
+ break;
+ }
+ tmpreg=get_num(opP->con1,tmpreg);
+ if (isvar(opP->con1))
+ add_fix(s[1],opP->con1,0,NO_RELOC);
+ switch (s[1]) {
+ case 'b': /* Danger: These do no check for
+ certain types of overflow.
+ user beware! */
+ if (!isbyte(tmpreg))
+ opP->error="out of range";
+ insop(tmpreg);
+ if (isvar(opP->con1))
+ the_ins.reloc[the_ins.nrel-1].n=(opcode->m_codenum)*2;
+ break;
+ case 'w':
+ if (!isword(tmpreg))
+ opP->error="out of range";
+ insop(tmpreg);
+ if (isvar(opP->con1))
+ the_ins.reloc[the_ins.nrel-1].n=(opcode->m_codenum)*2;
+ break;
+ case 'l':
+ insop(tmpreg); /* Because of the way insop works, we put these two out backwards */
+ insop(tmpreg>>16);
+ if (isvar(opP->con1))
+ the_ins.reloc[the_ins.nrel-1].n=(opcode->m_codenum)*2;
+ break;
+ case '3':
+ tmpreg&=0xFF;
+ case '8':
+ case 'C':
+ install_operand(s[1],tmpreg);
+ break;
+ default:
+ as_fatal("Internal error: Unknown mode #%c in line %s of file \"%s\"", s[1], __LINE__, __FILE__);
+ }
+ break;
+
+ case '+':
+ case '-':
+ case 'A':
+ case 'a':
+ install_operand(s[1], opP->reg - ADDR);
+ break;
+
+ case 'B':
+ tmpreg = get_num(opP->con1, 80);
+ switch (s[1]) {
+ case 'B':
+ /* Offset is relative to next word */
+ opP->con1->e_exp.X_add_number -= 1;
+ add_fix('B', opP->con1, 1,NO_RELOC);
+ break;
+ case 'W':
+ add_fix('w', opP->con1, 1,NO_RELOC);
+ addword(0);
+ break;
+ case 'L':
+ long_branch:
+ if (cpu_of_arch(current_architecture) < m68020) /* 68000 or 010 */
+ as_warn("Can't use long branches on 68000/68010");
+ the_ins.opcode[the_ins.numo-1]|=0xff;
+ add_fix('l',opP->con1,1,NO_RELOC);
+ addword(0);
+ addword(0);
+ break;
+ case 'g':
+#ifdef PIC
+ /* If we have the optional kludgey 2nd operand,
+ make this go via the jump table. */
+ if (flagseen['k'] && s[2] == ' ') {
+ the_ins.opcode[the_ins.numo-1] |= 0xFF;
+ add_fix('l', opP->con1, 1, RELOC_JMP_TBL);
+ addword(0);
+ addword(0);
+ break;
+ }
+#endif /* PIC */
+ if (subs(opP->con1)) /* We can't relax it */
+ goto long_branch;
+
+ /* This could either be a symbol, or an
+ absolute address. No matter, the
+ frag hacking will finger it out.
+ Not quite: it can't switch from
+ BRANCH to BCC68000 for the case
+ where opnd is absolute (it needs
+ to use the 68000 hack since no
+ conditional abs jumps). */
+ if (((cpu_of_arch(current_architecture) < m68020) || (0 == adds(opP->con1)))
+ && (the_ins.opcode[0] >= 0x6200)
+ && (the_ins.opcode[0] <= 0x6f00)) {
+ add_frag(adds(opP->con1),offs(opP->con1),TAB(BCC68000,SZ_UNDEF));
+ } else {
+ add_frag(adds(opP->con1),offs(opP->con1),TAB(BRANCH,SZ_UNDEF));
+ }
+ break;
+ case 'w':
+ if (isvar(opP->con1)) {
+ /* check for DBcc instruction */
+ if ((the_ins.opcode[0] & 0xf0f8) == 0x50c8) {
+ /* size varies if patch */
+ /* needed for long form */
+ add_frag(adds(opP->con1),offs(opP->con1),TAB(DBCC,SZ_UNDEF));
+ break;
+ }
+
+ /* Don't ask! */
+ opP->con1->e_exp.X_add_number+=2;
+ add_fix('w',opP->con1,1,NO_RELOC);
+ }
+ addword(0);
+ break;
+ case 'C': /* Fixed size LONG coproc branches */
+ the_ins.opcode[the_ins.numo-1]|=0x40;
+ /* Offset the displacement to be relative to byte disp location */
+ /* Coproc branches don't have a byte disp option, but they are
+ compatible with the ordinary branches, which do... */
+ opP->con1->e_exp.X_add_number+=4;
+ add_fix('l',opP->con1,1,NO_RELOC);
+ addword(0);
+ addword(0);
+ break;
+ case 'c': /* Var size Coprocesssor branches */
+ if (subs(opP->con1)) {
+ add_fix('l',opP->con1,1,NO_RELOC);
+ add_frag((symbolS *)0,(long)0,TAB(FBRANCH,LONG));
+ } else if (adds(opP->con1)) {
+ add_frag(adds(opP->con1),offs(opP->con1),TAB(FBRANCH,SZ_UNDEF));
+ } else {
+ /* add_frag((symbolS *)0,offs(opP->con1),TAB(FBRANCH,SHORT)); */
+ the_ins.opcode[the_ins.numo-1]|=0x40;
+ add_fix('l',opP->con1,1,NO_RELOC);
+ addword(0);
+ addword(4);
+ }
+ break;
+ default:
+ as_fatal("Internal error: operand type B%c unknown in line %s of file \"%s\"",
+ s[1], __LINE__, __FILE__);
+ }
+ break;
+
+ case 'C': /* Ignore it */
+ break;
+
+ case 'd': /* JF this is a kludge */
+ if (opP->mode == AOFF) {
+ install_operand('s',opP->reg-ADDR);
+ } else {
+ char *tmpP;
+
+ tmpP=opP->con1->e_end-2;
+ opP->con1->e_beg++;
+ opP->con1->e_end-=4; /* point to the , */
+ baseo=m68k_reg_parse(&tmpP);
+ if (baseo<ADDR+0 || baseo>ADDR+7) {
+ as_bad("Unknown address reg, using A0");
+ baseo=0;
+ } else baseo-=ADDR;
+ install_operand('s',baseo);
+ }
+ tmpreg=get_num(opP->con1,80);
+ if (!issword(tmpreg)) {
+ as_warn("Expression out of range, using 0");
+ tmpreg=0;
+ }
+ addword(tmpreg);
+ break;
+
+ case 'D':
+ install_operand(s[1],opP->reg-DATA);
+ break;
+
+ case 'F':
+ install_operand(s[1],opP->reg-FPREG);
+ break;
+
+ case 'I':
+ tmpreg=1+opP->reg-COPNUM;
+ if (tmpreg == 8)
+ tmpreg=0;
+ install_operand(s[1],tmpreg);
+ break;
+
+ case 'J': /* JF foo */
+ switch (opP->reg) {
+ case SFC: tmpreg=0x000; break;
+ case DFC: tmpreg=0x001; break;
+ case CACR: tmpreg=0x002; break;
+ case TC: tmpreg=0x003; break;
+ case ITT0: tmpreg=0x004; break;
+ case ITT1: tmpreg=0x005; break;
+ case DTT0: tmpreg=0x006; break;
+ case DTT1: tmpreg=0x007; break;
+
+ case USP: tmpreg=0x800; break;
+ case VBR: tmpreg=0x801; break;
+ case CAAR: tmpreg=0x802; break;
+ case MSP: tmpreg=0x803; break;
+ case ISP: tmpreg=0x804; break;
+ case MMUSR: tmpreg=0x805; break;
+ case URP: tmpreg=0x806; break;
+ case SRP: tmpreg=0x807; break;
+ default:
+ as_fatal("failed sanity check.");
+ }
+ install_operand(s[1],tmpreg);
+ break;
+
+ case 'k':
+ tmpreg=get_num(opP->con1,55);
+ install_operand(s[1],tmpreg&0x7f);
+ break;
+
+ case 'l':
+ tmpreg=opP->reg;
+ if (s[1] == 'w') {
+ if (tmpreg&0x7FF0000)
+ as_bad("Floating point register in register list");
+ insop(reverse_16_bits(tmpreg));
+ } else {
+ if (tmpreg&0x700FFFF)
+ as_bad("Wrong register in floating-point reglist");
+ install_operand(s[1],reverse_8_bits(tmpreg>>16));
+ }
+ break;
+
+ case 'L':
+ tmpreg=opP->reg;
+ if (s[1] == 'w') {
+ if (tmpreg&0x7FF0000)
+ as_bad("Floating point register in register list");
+ insop(tmpreg);
+ } else if (s[1] == '8') {
+ if (tmpreg&0x0FFFFFF)
+ as_bad("incorrect register in reglist");
+ install_operand(s[1],tmpreg>>24);
+ } else {
+ if (tmpreg&0x700FFFF)
+ as_bad("wrong register in floating-point reglist");
+ else
+ install_operand(s[1],tmpreg>>16);
+ }
+ break;
+
+ case 'M':
+ install_operand(s[1],get_num(opP->con1,60));
+ break;
+
+ case 'O':
+ tmpreg= (opP->mode == DREG)
+ ? 0x20+opP->reg-DATA
+ : (get_num(opP->con1,40)&0x1F);
+ install_operand(s[1],tmpreg);
+ break;
+
+ case 'Q':
+ tmpreg=get_num(opP->con1,10);
+ if (tmpreg == 8)
+ tmpreg=0;
+ install_operand(s[1],tmpreg);
+ break;
+
+ case 'R':
+ /* This depends on the fact that ADDR registers are
+ eight more than their corresponding DATA regs, so
+ the result will have the ADDR_REG bit set */
+ install_operand(s[1],opP->reg-DATA);
+ break;
+
+ case 's':
+ if (opP->reg == FPI) tmpreg=0x1;
+ else if (opP->reg == FPS) tmpreg=0x2;
+ else if (opP->reg == FPC) tmpreg=0x4;
+ else as_fatal("failed sanity check.");
+ install_operand(s[1],tmpreg);
+ break;
+
+ case 'S': /* Ignore it */
+ break;
+
+ case 'T':
+ install_operand(s[1],get_num(opP->con1,30));
+ break;
+
+ case 'U': /* Ignore it */
+ break;
+
+ case 'c':
+ switch (opP->reg) {
+ case NC: tmpreg = 0; break;
+ case DC: tmpreg = 1; break;
+ case IC: tmpreg = 2; break;
+ case BC: tmpreg = 3; break;
+ default:
+ as_fatal("failed sanity check");
+ } /* switch on cache token */
+ install_operand(s[1], tmpreg);
+ break;
+#ifndef NO_68851
+ /* JF: These are out of order, I fear. */
+ case 'f':
+ switch (opP->reg) {
+ case SFC:
+ tmpreg=0;
+ break;
+ case DFC:
+ tmpreg=1;
+ break;
+ default:
+ as_fatal("failed sanity check.");
+ }
+ install_operand(s[1],tmpreg);
+ break;
+
+ case 'P':
+ switch (opP->reg) {
+ case TC:
+ tmpreg=0;
+ break;
+ case CAL:
+ tmpreg=4;
+ break;
+ case VAL:
+ tmpreg=5;
+ break;
+ case SCC:
+ tmpreg=6;
+ break;
+ case AC:
+ tmpreg=7;
+ break;
+ default:
+ as_fatal("failed sanity check.");
+ }
+ install_operand(s[1],tmpreg);
+ break;
+
+ case 'V':
+ if (opP->reg == VAL)
+ break;
+ as_fatal("failed sanity check.");
+
+ case 'W':
+ switch (opP->reg) {
+
+ case DRP:
+ tmpreg=1;
+ break;
+ case SRP:
+ tmpreg=2;
+ break;
+ case CRP:
+ tmpreg=3;
+ break;
+ default:
+ as_fatal("failed sanity check.");
+ }
+ install_operand(s[1],tmpreg);
+ break;
+
+ case 'X':
+ switch (opP->reg) {
+ case BAD: case BAD+1: case BAD+2: case BAD+3:
+ case BAD+4: case BAD+5: case BAD+6: case BAD+7:
+ tmpreg = (4 << 10) | ((opP->reg - BAD) << 2);
+ break;
+
+ case BAC: case BAC+1: case BAC+2: case BAC+3:
+ case BAC+4: case BAC+5: case BAC+6: case BAC+7:
+ tmpreg = (5 << 10) | ((opP->reg - BAC) << 2);
+ break;
+
+ default:
+ as_fatal("failed sanity check.");
+ }
+ install_operand(s[1], tmpreg);
+ break;
+ case 'Y':
+ know(opP->reg == PSR);
+ break;
+ case 'Z':
+ know(opP->reg == PCSR);
+ break;
+#endif /* m68851 */
+ case '_':
+ tmpreg=get_num(opP->con1,80);
+ install_operand(s[1], tmpreg);
+ break;
+ default:
+ as_fatal("Internal error: Operand type %c unknown in line %s of file \"%s\"", s[0], __LINE__, __FILE__);
+ }
+ }
+ /* By the time whe get here (FINALLY) the_ins contains the complete
+ instruction, ready to be emitted... */
+} /* m68k_ip() */
+
+/*
+ * get_regs := '/' + ?
+ * | '-' + <register>
+ * | '-' + <register> + ?
+ * | <empty>
+ * ;
+ *
+
+ * The idea here must be to scan in a set of registers but I don't
+ * understand it. Looks awfully sloppy to me but I don't have any doc on
+ * this format so...
+
+ *
+ *
+ */
+
+static int get_regs(i,str,opP)
+int i;
+struct m68k_op *opP;
+char *str;
+{
+ /* 26, 25, 24, 23-16, 15-8, 0-7 */
+ /* Low order 24 bits encoded fpc,fps,fpi,fp7-fp0,a7-a0,d7-d0 */
+ unsigned long cur_regs = 0;
+ int reg1,
+ reg2;
+
+#define ADD_REG(x) { if (x == FPI) cur_regs|=(1<<24);\
+else if (x == FPS) cur_regs|=(1<<25);\
+else if (x == FPC) cur_regs|=(1<<26);\
+else cur_regs|=(1<<(x-1)); }
+
+ reg1=i;
+ for (;;) {
+ if (*str == '/') {
+ ADD_REG(reg1);
+ str++;
+ } else if (*str == '-') {
+ str++;
+ reg2=m68k_reg_parse(&str);
+ if (reg2<DATA || reg2 >= FPREG+8 || reg1 == FPI || reg1 == FPS || reg1 == FPC) {
+ opP->error="unknown register in register list";
+ return FAIL;
+ }
+ while (reg1 <= reg2) {
+ ADD_REG(reg1);
+ reg1++;
+ }
+ if (*str == '\0')
+ break;
+ } else if (*str == '\0') {
+ ADD_REG(reg1);
+ break;
+ } else {
+ opP->error="unknow character in register list";
+ return FAIL;
+ }
+ /* DJA -- Bug Fix. Did't handle d1-d2/a1 until the following instruction was added */
+ if (*str == '/')
+ str ++;
+ reg1=m68k_reg_parse(&str);
+ if ((reg1<DATA || reg1 >= FPREG+8) && !(reg1 == FPI || reg1 == FPS || reg1 == FPC)) {
+ opP->error="unknown register in register list";
+ return FAIL;
+ }
+ }
+ opP->reg=cur_regs;
+ return OK;
+} /* get_regs() */
+
+static int reverse_16_bits(in)
+int in;
+{
+ int out=0;
+ int n;
+
+ static int mask[16] = {
+ 0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080,
+ 0x0100,0x0200,0x0400,0x0800,0x1000,0x2000,0x4000,0x8000
+ };
+ for (n=0;n<16;n++) {
+ if (in&mask[n])
+ out|=mask[15-n];
+ }
+ return out;
+} /* reverse_16_bits() */
+
+static int reverse_8_bits(in)
+int in;
+{
+ int out=0;
+ int n;
+
+ static int mask[8] = {
+ 0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080,
+ };
+
+ for (n=0;n<8;n++) {
+ if (in&mask[n])
+ out|=mask[7-n];
+ }
+ return out;
+} /* reverse_8_bits() */
+
+static void install_operand(mode,val)
+int mode;
+int val;
+{
+ switch (mode) {
+ case 's':
+ the_ins.opcode[0]|=val & 0xFF; /* JF FF is for M kludge */
+ break;
+ case 'd':
+ the_ins.opcode[0]|=val<<9;
+ break;
+ case '1':
+ the_ins.opcode[1]|=val<<12;
+ break;
+ case '2':
+ the_ins.opcode[1]|=val<<6;
+ break;
+ case '3':
+ the_ins.opcode[1]|=val;
+ break;
+ case '4':
+ the_ins.opcode[2]|=val<<12;
+ break;
+ case '5':
+ the_ins.opcode[2]|=val<<6;
+ break;
+ case '6':
+ /* DANGER! This is a hack to force cas2l and cas2w cmds
+ to be three words long! */
+ the_ins.numo++;
+ the_ins.opcode[2]|=val;
+ break;
+ case '7':
+ the_ins.opcode[1]|=val<<7;
+ break;
+ case '8':
+ the_ins.opcode[1]|=val<<10;
+ break;
+#ifndef NO_68851
+ case '9':
+ the_ins.opcode[1]|=val<<5;
+ break;
+#endif
+
+ case 't':
+ the_ins.opcode[1]|=(val<<10)|(val<<7);
+ break;
+ case 'D':
+ the_ins.opcode[1]|=(val<<12)|val;
+ break;
+ case 'g':
+ the_ins.opcode[0]|=val=0xff;
+ break;
+ case 'i':
+ the_ins.opcode[0]|=val<<9;
+ break;
+ case 'C':
+ the_ins.opcode[1]|=val;
+ break;
+ case 'j':
+ the_ins.opcode[1]|=val;
+ the_ins.numo++; /* What a hack */
+ break;
+ case 'k':
+ the_ins.opcode[1]|=val<<4;
+ break;
+ case 'b':
+ case 'w':
+ case 'l':
+ break;
+ case 'e':
+ the_ins.opcode[0] |= (val << 6);
+ break;
+ case 'L':
+ the_ins.opcode[1] = (val >> 16);
+ the_ins.opcode[2] = val & 0xffff;
+ break;
+ case 'c':
+ default:
+ as_fatal("failed sanity check.");
+ }
+} /* install_operand() */
+
+static void install_gen_operand(mode,val)
+int mode;
+int val;
+{
+ switch (mode) {
+ case 's':
+ the_ins.opcode[0]|=val;
+ break;
+ case 'd':
+ /* This is a kludge!!! */
+ the_ins.opcode[0]|=(val&0x07)<<9|(val&0x38)<<3;
+ break;
+ case 'b':
+ case 'w':
+ case 'l':
+ case 'f':
+ case 'F':
+ case 'x':
+ case 'p':
+ the_ins.opcode[0]|=val;
+ break;
+ /* more stuff goes here */
+ default:
+ as_fatal("failed sanity check.");
+ }
+} /* install_gen_operand() */
+
+/*
+ * verify that we have some number of paren pairs, do m68k_ip_op(), and
+ * then deal with the bitfield hack.
+ */
+
+static char *crack_operand(str,opP)
+register char *str;
+register struct m68k_op *opP;
+{
+ register int parens;
+ register int c;
+ register char *beg_str;
+
+ if (!str) {
+ return str;
+ }
+ beg_str=str;
+ for (parens=0;*str && (parens>0 || notend(str));str++) {
+ if (*str == '(') parens++;
+ else if (*str == ')') {
+ if (!parens) { /* ERROR */
+ opP->error="Extra )";
+ return str;
+ }
+ --parens;
+ }
+ }
+ if (!*str && parens) { /* ERROR */
+ opP->error="Missing )";
+ return str;
+ }
+ c= *str;
+ *str='\0';
+ if (m68k_ip_op(beg_str,opP) == FAIL) {
+ *str=c;
+ return str;
+ }
+ *str=c;
+ if (c == '}')
+ c= *++str; /* JF bitfield hack */
+ if (c) {
+ c= *++str;
+ if (!c)
+ as_bad("Missing operand");
+ }
+ return str;
+}
+
+/* See the comment up above where the #define notend(... is */
+#if 0
+notend(s)
+char *s;
+{
+ if (*s == ',') return 0;
+ if (*s == '{' || *s == '}')
+ return 0;
+ if (*s != ':') return 1;
+ /* This kludge here is for the division cmd, which is a kludge */
+ if (index("aAdD#",s[1])) return 0;
+ return 1;
+}
+#endif
+
+/*
+ * Generate a new fixup for one of the relocs in the_ins.
+ */
+static void
+ make_fix(m, where)
+int m;
+char *where;
+{
+ int n;
+
+ switch (the_ins.reloc[m].wid) {
+ case 'B':
+ case 'b':
+ n=1;
+ break;
+ case '3':
+ case 'w':
+ n=2;
+ break;
+ case 'l':
+ n=4;
+ break;
+ default:
+ as_fatal("Don't know how to figure width of %c in md_assemble()",the_ins.reloc[m].wid);
+ }
+ fix_new(frag_now,
+ where - frag_now->fr_literal + the_ins.reloc[m].n,
+ n,
+ the_ins.reloc[m].add,
+ the_ins.reloc[m].sub,
+ the_ins.reloc[m].off,
+ the_ins.reloc[m].pcrel,
+ the_ins.reloc[m].rtype
+#ifdef PIC
+ , the_ins.reloc[m].got
+#endif /* PIC */
+ );
+#ifdef PIC
+ if (the_ins.reloc[m].rtype == RELOC_GLOB_DAT
+ && the_ins.reloc[m].add != NULL)
+ the_ins.reloc[m].add->sy_forceout = 1;
+#endif /* PIC */
+}
+
+/* This is the guts of the machine-dependent assembler. STR points to a
+ machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles to.
+ */
+void
+ md_assemble(str)
+char *str;
+{
+ char *er;
+ short *fromP;
+ char *toP = NULL;
+ int m,n = 0;
+ char *to_beg_P;
+ int shorts_this_frag;
+
+
+ if (current_architecture == 0) {
+ current_architecture = (m68020
+#ifndef NO_68881
+ | m68881
+#endif
+#ifndef NO_68851
+ | m68851
+#endif
+ );
+ } /* default current_architecture */
+
+ memset((char *)(&the_ins), '\0', sizeof(the_ins)); /* JF for paranoia sake */
+ m68k_ip(str);
+ er=the_ins.error;
+ if (!er) {
+ for (n=the_ins.numargs;n;--n)
+ if (the_ins.operands[n].error) {
+ er=the_ins.operands[n].error;
+ break;
+ }
+ }
+ if (er) {
+ as_bad("\"%s\" -- Statement '%s' ignored",er,str);
+ return;
+ }
+
+ if (the_ins.nfrag == 0) { /* No frag hacking involved; just put it out */
+ toP=frag_more(2*the_ins.numo);
+ fromP= &the_ins.opcode[0];
+ for (m=the_ins.numo;m;--m) {
+ md_number_to_chars(toP,(long)(*fromP),2);
+ toP+=2;
+ fromP++;
+ }
+ /* put out symbol-dependent info */
+ for (m = 0; m < the_ins.nrel; m++) {
+ make_fix(m, toP-the_ins.numo*2);
+ }
+ return;
+ }
+
+ /* There's some frag hacking */
+ for (n=0,fromP= &the_ins.opcode[0];n<the_ins.nfrag;n++) {
+ int wid;
+
+ if (n == 0) wid=2*the_ins.fragb[n].fragoff;
+ else wid=2*(the_ins.numo-the_ins.fragb[n-1].fragoff);
+ toP=frag_more(wid);
+ to_beg_P=toP;
+ shorts_this_frag=0;
+ for (m=wid/2;m;--m) {
+ md_number_to_chars(toP,(long)(*fromP),2);
+ toP+=2;
+ fromP++;
+ shorts_this_frag++;
+ }
+ for (m=0;m<the_ins.nrel;m++) {
+ if ((the_ins.reloc[m].n) >= 2*shorts_this_frag /* 2*the_ins.fragb[n].fragoff */) {
+ the_ins.reloc[m].n-= 2*shorts_this_frag /* 2*the_ins.fragb[n].fragoff */;
+ break;
+ }
+ if (the_ins.reloc[m].wid == 0)
+ continue;
+ make_fix(m, toP-the_ins.numo*2);
+ the_ins.reloc[m].wid=0;
+ }
+ /* know(the_ins.fragb[n].fadd); */
+ (void)frag_var(rs_machine_dependent,10,0,(relax_substateT)(the_ins.fragb[n].fragty),
+ the_ins.fragb[n].fadd,the_ins.fragb[n].foff,to_beg_P);
+ }
+ n=(the_ins.numo-the_ins.fragb[n-1].fragoff);
+ shorts_this_frag=0;
+ if (n) {
+ toP=frag_more(n*sizeof(short));
+ while (n--) {
+ md_number_to_chars(toP,(long)(*fromP),2);
+ toP+=2;
+ fromP++;
+ shorts_this_frag++;
+ }
+ }
+ for (m=0;m<the_ins.nrel;m++) {
+ if (the_ins.reloc[m].wid == 0)
+ continue;
+ make_fix(m, toP - /* the_ins.numo */ shorts_this_frag*2);
+ }
+}
+
+/* This function is called once, at assembler startup time. This should
+ set up all the tables, etc that the MD part of the assembler needs
+ */
+void
+ md_begin()
+{
+ /*
+ * md_begin -- set up hash tables with 68000 instructions.
+ * similar to what the vax assembler does. ---phr
+ */
+ /* RMS claims the thing to do is take the m68k-opcode.h table, and make
+ a copy of it at runtime, adding in the information we want but isn't
+ there. I think it'd be better to have an awk script hack the table
+ at compile time. Or even just xstr the table and use it as-is. But
+ my lord ghod hath spoken, so we do it this way. Excuse the ugly var
+ names. */
+
+ register const struct m68k_opcode *ins;
+ register struct m68k_incant *hack,
+ *slak;
+ register char *retval = 0; /* empty string, or error msg text */
+ register unsigned int i;
+ register char c;
+
+ if ((op_hash = hash_new()) == NULL)
+ as_fatal("Virtual memory exhausted");
+
+ obstack_begin(&robyn,4000);
+ for (ins = m68k_opcodes; ins < endop; ins++) {
+ hack=slak=(struct m68k_incant *)obstack_alloc(&robyn,sizeof(struct m68k_incant));
+ do {
+ /* we *could* ignore insns that don't match our
+ arch here but just leaving them out of the
+ hash. */
+ slak->m_operands=ins->args;
+ slak->m_opnum=strlen(slak->m_operands)/2;
+ slak->m_arch = ins->arch;
+ slak->m_opcode=ins->opcode;
+ /* This is kludgey */
+ slak->m_codenum=((ins->match)&0xffffL) ? 2 : 1;
+ if ((ins+1) != endop && !strcmp(ins->name,(ins+1)->name)) {
+ slak->m_next=(struct m68k_incant *) obstack_alloc(&robyn,sizeof(struct m68k_incant));
+ ins++;
+ } else
+ slak->m_next=0;
+ slak=slak->m_next;
+ } while (slak);
+
+ retval = hash_insert (op_hash, ins->name,(char *)hack);
+ /* Didn't his mommy tell him about null pointers? */
+ if (retval && *retval)
+ as_fatal("Internal Error: Can't hash %s: %s",ins->name,retval);
+ }
+
+ for (i = 0; i < sizeof(mklower_table) ; i++)
+ mklower_table[i] = (isupper(c = (char) i)) ? tolower(c) : c;
+
+ for (i = 0 ; i < sizeof(notend_table) ; i++) {
+ notend_table[i] = 0;
+ alt_notend_table[i] = 0;
+ }
+ notend_table[','] = 1;
+ notend_table['{'] = 1;
+ notend_table['}'] = 1;
+ alt_notend_table['a'] = 1;
+ alt_notend_table['A'] = 1;
+ alt_notend_table['d'] = 1;
+ alt_notend_table['D'] = 1;
+ alt_notend_table['#'] = 1;
+ alt_notend_table['f'] = 1;
+ alt_notend_table['F'] = 1;
+#ifdef REGISTER_PREFIX
+ alt_notend_table[REGISTER_PREFIX] = 1;
+#endif
+}
+
+#if 0
+#define notend(s) ((*s == ',' || *s == '}' || *s == '{' \
+ || (*s == ':' && strchr("aAdD#", s[1]))) \
+ ? 0 : 1)
+#endif
+
+/* This funciton is called once, before the assembler exits. It is
+ supposed to do any final cleanup for this part of the assembler.
+ */
+void
+ md_end()
+{
+}
+
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+/* Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP. An error message is returned, or NULL on OK.
+ */
+char *
+ md_atof(type,litP,sizeP)
+char type;
+char *litP;
+int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+ char *atof_ieee();
+
+ switch (type) {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP=0;
+ return "Bad call to MD_ATOF()";
+ }
+ t=atof_ieee(input_line_pointer,type,words);
+ if (t)
+ input_line_pointer=t;
+
+ *sizeP=prec * sizeof(LITTLENUM_TYPE);
+ for (wordP=words;prec--;) {
+ md_number_to_chars(litP,(long)(*wordP++),sizeof(LITTLENUM_TYPE));
+ litP+=sizeof(LITTLENUM_TYPE);
+ }
+ return ""; /* Someone should teach Dean about null pointers */
+}
+
+/* Turn an integer of n bytes (in val) into a stream of bytes appropriate
+ for use in the a.out file, and stores them in the array pointed to by buf.
+ This knows about the endian-ness of the target machine and does
+ THE RIGHT THING, whatever it is. Possible values for n are 1 (byte)
+ 2 (short) and 4 (long) Floating numbers are put out as a series of
+ LITTLENUMS (shorts, here at least)
+ */
+void
+ md_number_to_chars(buf, val, n)
+char *buf;
+long val;
+int n;
+{
+ switch (n) {
+ case 1:
+ *buf++=val;
+ break;
+ case 2:
+ *buf++=(val>>8);
+ *buf++=val;
+ break;
+ case 4:
+ *buf++=(val>>24);
+ *buf++=(val>>16);
+ *buf++=(val>>8);
+ *buf++=val;
+ break;
+ default:
+ as_fatal("failed sanity check.");
+ }
+}
+
+void
+ md_apply_fix(fixP, val)
+fixS *fixP;
+long val;
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ switch (fixP->fx_size) {
+ case 1:
+ *buf++ = val;
+ break;
+ case 2:
+ *buf++ = (val >> 8);
+ *buf++ = val;
+ break;
+ case 4:
+ *buf++ = (val >> 24);
+ *buf++ = (val >> 16);
+ *buf++ = (val >> 8);
+ *buf++ = val;
+ break;
+ default:
+ BAD_CASE (fixP->fx_size);
+ }
+}
+
+
+/* *fragP has been relaxed to its final size, and now needs to have
+ the bytes inside it modified to conform to the new size There is UGLY
+ MAGIC here. ..
+ */
+void
+ md_convert_frag(headers, fragP)
+object_headers *headers;
+register fragS *fragP;
+{
+ long disp;
+ long ext = 0;
+
+ /* Address in object code of the displacement. */
+ register int object_address = fragP->fr_fix + fragP->fr_address;
+
+#ifdef IBM_COMPILER_SUX
+ /* This is wrong but it convinces the native rs6000 compiler to
+ generate the code we want. */
+ register char *buffer_address = fragP->fr_literal;
+ buffer_address += fragP->fr_fix;
+#else /* IBM_COMPILER_SUX */
+ /* Address in gas core of the place to store the displacement. */
+ register char *buffer_address = fragP->fr_fix + fragP->fr_literal;
+#endif /* IBM_COMPILER_SUX */
+
+ /* No longer true: know(fragP->fr_symbol); */
+
+ /* The displacement of the address, from current location. */
+ disp = fragP->fr_symbol ? S_GET_VALUE(fragP->fr_symbol) : 0;
+ disp = (disp + fragP->fr_offset) - object_address;
+
+ switch (fragP->fr_subtype) {
+ case TAB(BCC68000,BYTE):
+ case TAB(BRANCH,BYTE):
+ know(issbyte(disp));
+ if (disp == 0)
+ as_bad("short branch with zero offset: use :w");
+ fragP->fr_opcode[1]=disp;
+ ext=0;
+ break;
+ case TAB(DBCC,SHORT):
+ know(issword(disp));
+ ext=2;
+ break;
+ case TAB(BCC68000,SHORT):
+ case TAB(BRANCH,SHORT):
+ know(issword(disp));
+ fragP->fr_opcode[1]=0x00;
+ ext=2;
+ break;
+ case TAB(BRANCH,LONG):
+ if (cpu_of_arch(current_architecture) < m68020) {
+ if (fragP->fr_opcode[0] == 0x61) {
+ fragP->fr_opcode[0]= 0x4E;
+ fragP->fr_opcode[1]= 0xB9; /* JBSR with ABSL LONG offset */
+ subseg_change(SEG_TEXT, 0);
+
+ fix_new(fragP,
+ fragP->fr_fix,
+ 4,
+ fragP->fr_symbol,
+ 0,
+ fragP->fr_offset,
+ 0,
+ FIX_NO_RELOC);
+
+ fragP->fr_fix+=4;
+ ext=0;
+ } else if (fragP->fr_opcode[0] == 0x60) {
+ fragP->fr_opcode[0]= 0x4E;
+ fragP->fr_opcode[1]= 0xF9; /* JMP with ABSL LONG offset */
+ subseg_change(SEG_TEXT, 0);
+ fix_new(fragP, fragP->fr_fix, 4, fragP->fr_symbol, 0, fragP->fr_offset,0,
+ FIX_NO_RELOC);
+ fragP->fr_fix+=4;
+ ext=0;
+ } else {
+ as_bad("Long branch offset not supported.");
+ }
+ } else {
+ fragP->fr_opcode[1]=0xff;
+ ext=4;
+ }
+ break;
+ case TAB(BCC68000,LONG):
+ /* only Bcc 68000 instructions can come here */
+ /* change bcc into b!cc/jmp absl long */
+ fragP->fr_opcode[0] ^= 0x01; /* invert bcc */
+ fragP->fr_opcode[1] = 0x6; /* branch offset = 6 */
+
+ /* JF: these used to be fr_opcode[2,3], but they may be in a
+ different frag, in which case refering to them is a no-no.
+ Only fr_opcode[0,1] are guaranteed to work. */
+ *buffer_address++ = 0x4e; /* put in jmp long (0x4ef9) */
+ *buffer_address++ = 0xf9;
+ fragP->fr_fix += 2; /* account for jmp instruction */
+ subseg_change(SEG_TEXT,0);
+ fix_new(fragP, fragP->fr_fix, 4, fragP->fr_symbol, 0,
+ fragP->fr_offset,0,
+ FIX_NO_RELOC);
+ fragP->fr_fix += 4;
+ ext=0;
+ break;
+ case TAB(DBCC,LONG):
+ /* only DBcc 68000 instructions can come here */
+ /* change dbcc into dbcc/jmp absl long */
+ /* JF: these used to be fr_opcode[2-7], but that's wrong */
+ *buffer_address++ = 0x00; /* branch offset = 4 */
+ *buffer_address++ = 0x04;
+ *buffer_address++ = 0x60; /* put in bra pc+6 */
+ *buffer_address++ = 0x06;
+ *buffer_address++ = 0x4e; /* put in jmp long (0x4ef9) */
+ *buffer_address++ = 0xf9;
+
+ fragP->fr_fix += 6; /* account for bra/jmp instructions */
+ subseg_change(SEG_TEXT,0);
+ fix_new(fragP, fragP->fr_fix, 4, fragP->fr_symbol, 0,
+ fragP->fr_offset,0,
+ FIX_NO_RELOC);
+ fragP->fr_fix += 4;
+ ext=0;
+ break;
+ case TAB(FBRANCH,SHORT):
+ know((fragP->fr_opcode[1]&0x40) == 0);
+ ext=2;
+ break;
+ case TAB(FBRANCH,LONG):
+ fragP->fr_opcode[1]|=0x40; /* Turn on LONG bit */
+ ext=4;
+ break;
+ case TAB(PCREL,SHORT):
+ ext=2;
+ break;
+ case TAB(PCREL,LONG):
+ /* The thing to do here is force it to ABSOLUTE LONG, since
+ PCREL is really trying to shorten an ABSOLUTE address anyway */
+ /* JF FOO This code has not been tested */
+ subseg_change(SEG_TEXT,0);
+ fix_new(fragP, fragP->fr_fix, 4, fragP->fr_symbol, 0, fragP->fr_offset, 0, FIX_NO_RELOC);
+ if ((fragP->fr_opcode[1] & 0x3F) != 0x3A)
+ as_bad("Internal error (long PC-relative operand) for insn 0x%04lx at 0x%lx",
+ fragP->fr_opcode[0],fragP->fr_address);
+ fragP->fr_opcode[1]&= ~0x3F;
+ fragP->fr_opcode[1]|=0x39; /* Mode 7.1 */
+ fragP->fr_fix+=4;
+ /* md_number_to_chars(buffer_address,
+ (long)(fragP->fr_symbol->sy_value + fragP->fr_offset),
+ 4); */
+ ext=0;
+ break;
+ case TAB(PCLEA,SHORT):
+ subseg_change(SEG_TEXT,0);
+ fix_new(fragP, (int) (fragP->fr_fix), 2, fragP->fr_symbol, (symbolS *) 0, fragP->fr_offset, 1, FIX_NO_RELOC);
+ fragP->fr_opcode[1] &= ~0x3F;
+ fragP->fr_opcode[1] |= 0x3A;
+ ext=2;
+ break;
+ case TAB(PCLEA,LONG):
+ subseg_change(SEG_TEXT,0);
+ fix_new(fragP, (int) (fragP->fr_fix) + 2, 4, fragP->fr_symbol, (symbolS *) 0, fragP->fr_offset + 2, 1, FIX_NO_RELOC);
+ *buffer_address++ = 0x01;
+ *buffer_address++ = 0x70;
+ fragP->fr_fix+=2;
+ /* buffer_address+=2; */
+ ext=4;
+ break;
+
+} /* switch on subtype */
+
+ if (ext) {
+ md_number_to_chars(buffer_address, (long) disp, (int) ext);
+ fragP->fr_fix += ext;
+ /* H_SET_TEXT_SIZE(headers, H_GET_TEXT_SIZE(headers) + ext); */
+ } /* if extending */
+
+ return;
+} /* md_convert_frag() */
+
+/* Force truly undefined symbols to their maximum size, and generally set up
+ the frag list to be relaxed
+ */
+int md_estimate_size_before_relax(fragP, segment)
+register fragS *fragP;
+segT segment;
+{
+ int old_fix;
+ register char *buffer_address = fragP->fr_fix + fragP->fr_literal;
+
+ old_fix = fragP->fr_fix;
+
+ /* handle SZ_UNDEF first, it can be changed to BYTE or SHORT */
+ switch (fragP->fr_subtype) {
+
+ case TAB(BRANCH,SZ_UNDEF): {
+ if ((fragP->fr_symbol != NULL) /* Not absolute */
+ && S_GET_SEGMENT(fragP->fr_symbol) == segment) {
+ fragP->fr_subtype = TAB(TABTYPE(fragP->fr_subtype), BYTE);
+ break;
+ } else if ((fragP->fr_symbol == 0) || (cpu_of_arch(current_architecture) < m68020)) {
+ /* On 68000, or for absolute value, switch to abs long */
+ /* FIXME, we should check abs val, pick short or long */
+ if (fragP->fr_opcode[0] == 0x61) {
+ fragP->fr_opcode[0]= 0x4E;
+ fragP->fr_opcode[1]= 0xB9; /* JSR with ABSL LONG offset */
+ subseg_change(SEG_TEXT, 0);
+ fix_new(fragP, fragP->fr_fix, 4,
+ fragP->fr_symbol, 0, fragP->fr_offset, 0, FIX_NO_RELOC);
+ fragP->fr_fix+=4;
+ frag_wane(fragP);
+ } else if (fragP->fr_opcode[0] == 0x60) {
+ fragP->fr_opcode[0]= 0x4E;
+ fragP->fr_opcode[1]= 0xF9; /* JMP with ABSL LONG offset */
+ subseg_change(SEG_TEXT, 0);
+ fix_new(fragP, fragP->fr_fix, 4,
+ fragP->fr_symbol, 0, fragP->fr_offset, 0, FIX_NO_RELOC);
+ fragP->fr_fix+=4;
+ frag_wane(fragP);
+ } else {
+ as_warn("Long branch offset to extern symbol not supported.");
+ }
+ } else if (flagseen['l']) { /* Symbol is still undefined. Make it simple */
+ fix_new(fragP, (int) (fragP->fr_fix), 2, fragP->fr_symbol,
+ (symbolS *) 0, fragP->fr_offset, 1, FIX_NO_RELOC);
+ fragP->fr_fix += 2;
+ fragP->fr_opcode[1] = 0x00;
+ frag_wane(fragP);
+ } else {
+ fix_new(fragP, (int) (fragP->fr_fix), 4, fragP->fr_symbol,
+ (symbolS *) 0, fragP->fr_offset, 1,
+#ifdef PIC
+ /* With -k, make all external branches go via the jump table. */
+ (flagseen['k']? RELOC_JMP_TBL: NO_RELOC), NULL
+#else
+ NO_RELOC
+#endif
+ );
+ fragP->fr_fix += 4;
+ fragP->fr_opcode[1] = 0xff;
+ frag_wane(fragP);
+ break;
+ }
+
+ break;
+ } /* case TAB(BRANCH,SZ_UNDEF) */
+
+ case TAB(FBRANCH,SZ_UNDEF): {
+ if (S_GET_SEGMENT(fragP->fr_symbol) == segment || flagseen['l']) {
+ fragP->fr_subtype = TAB(FBRANCH,SHORT);
+ fragP->fr_var += 2;
+ } else {
+ fragP->fr_subtype = TAB(FBRANCH,LONG);
+ fragP->fr_var += 4;
+ }
+ break;
+ } /* TAB(FBRANCH,SZ_UNDEF) */
+
+ case TAB(PCREL,SZ_UNDEF): {
+ if (S_GET_SEGMENT(fragP->fr_symbol) == segment || flagseen['l']) {
+ fragP->fr_subtype = TAB(PCREL,SHORT);
+ fragP->fr_var += 2;
+ } else {
+ fragP->fr_subtype = TAB(PCREL,LONG);
+ fragP->fr_var += 4;
+ }
+ break;
+ } /* TAB(PCREL,SZ_UNDEF) */
+
+ case TAB(BCC68000,SZ_UNDEF): {
+ if ((fragP->fr_symbol != NULL)
+ && S_GET_SEGMENT(fragP->fr_symbol) == segment) {
+ fragP->fr_subtype=TAB(BCC68000,BYTE);
+ break;
+ }
+ /* only Bcc 68000 instructions can come here */
+ /* change bcc into b!cc/jmp absl long */
+ fragP->fr_opcode[0] ^= 0x01; /* invert bcc */
+ if (flagseen['l']) {
+ fragP->fr_opcode[1] = 0x04; /* branch offset = 6 */
+ /* JF: these were fr_opcode[2,3] */
+ buffer_address[0] = 0x4e; /* put in jmp long (0x4ef9) */
+ buffer_address[1] = 0xf8;
+ fragP->fr_fix += 2; /* account for jmp instruction */
+ subseg_change(SEG_TEXT,0);
+ fix_new(fragP, fragP->fr_fix, 2, fragP->fr_symbol, 0,
+ fragP->fr_offset, 0, FIX_NO_RELOC);
+ fragP->fr_fix += 2;
+ } else {
+ fragP->fr_opcode[1] = 0x06; /* branch offset = 6 */
+ /* JF: these were fr_opcode[2,3] */
+ buffer_address[2] = 0x4e; /* put in jmp long (0x4ef9) */
+ buffer_address[3] = 0xf9;
+ fragP->fr_fix += 2; /* account for jmp instruction */
+ subseg_change(SEG_TEXT,0);
+ fix_new(fragP, fragP->fr_fix, 4, fragP->fr_symbol, 0,
+ fragP->fr_offset, 0, FIX_NO_RELOC);
+ fragP->fr_fix += 4;
+ }
+ frag_wane(fragP);
+ break;
+ } /* case TAB(BCC68000,SZ_UNDEF) */
+
+ case TAB(DBCC,SZ_UNDEF): {
+ if (fragP->fr_symbol != NULL && S_GET_SEGMENT(fragP->fr_symbol) == segment) {
+ fragP->fr_subtype=TAB(DBCC,SHORT);
+ fragP->fr_var+=2;
+ break;
+ }
+ /* only DBcc 68000 instructions can come here */
+ /* change dbcc into dbcc/jmp absl long */
+ /* JF: these used to be fr_opcode[2-4], which is wrong. */
+ buffer_address[0] = 0x00; /* branch offset = 4 */
+ buffer_address[1] = 0x04;
+ buffer_address[2] = 0x60; /* put in bra pc + ... */
+
+ if (flagseen['l']) {
+ /* JF: these were fr_opcode[5-7] */
+ buffer_address[3] = 0x04; /* plus 4 */
+ buffer_address[4] = 0x4e;/* Put in Jump Word */
+ buffer_address[5] = 0xf8;
+ fragP->fr_fix += 6; /* account for bra/jmp instruction */
+ subseg_change(SEG_TEXT,0);
+ fix_new(fragP, fragP->fr_fix, 2, fragP->fr_symbol, 0,
+ fragP->fr_offset, 0, FIX_NO_RELOC);
+ fragP->fr_fix += 2;
+ } else {
+ /* JF: these were fr_opcode[5-7] */
+ buffer_address[3] = 0x06; /* Plus 6 */
+ buffer_address[4] = 0x4e; /* put in jmp long (0x4ef9) */
+ buffer_address[5] = 0xf9;
+ fragP->fr_fix += 6; /* account for bra/jmp instruction */
+ subseg_change(SEG_TEXT,0);
+ fix_new(fragP, fragP->fr_fix, 4, fragP->fr_symbol, 0,
+ fragP->fr_offset, 0, FIX_NO_RELOC);
+ fragP->fr_fix += 4;
+ }
+
+ frag_wane(fragP);
+ break;
+ } /* case TAB(DBCC,SZ_UNDEF) */
+
+ case TAB(PCLEA,SZ_UNDEF): {
+ if ((S_GET_SEGMENT(fragP->fr_symbol)) == segment || flagseen['l']) {
+ fragP->fr_subtype=TAB(PCLEA,SHORT);
+ fragP->fr_var+=2;
+ } else {
+ fragP->fr_subtype=TAB(PCLEA,LONG);
+ fragP->fr_var+=6;
+ }
+ break;
+ } /* TAB(PCLEA,SZ_UNDEF) */
+
+ default:
+ break;
+
+ } /* switch on subtype looking for SZ_UNDEF's. */
+
+ /* now that SZ_UNDEF are taken care of, check others */
+ switch (fragP->fr_subtype) {
+ case TAB(BCC68000,BYTE):
+ case TAB(BRANCH,BYTE):
+ /* We can't do a short jump to the next instruction,
+ so we force word mode. */
+ if (fragP->fr_symbol && S_GET_VALUE(fragP->fr_symbol) == 0 &&
+ fragP->fr_symbol->sy_frag == fragP->fr_next) {
+ fragP->fr_subtype=TAB(TABTYPE(fragP->fr_subtype),SHORT);
+ fragP->fr_var+=2;
+ }
+ break;
+ default:
+ break;
+}
+ return fragP->fr_var + fragP->fr_fix - old_fix;
+}
+
+#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
+/* the bit-field entries in the relocation_info struct plays hell
+ with the byte-order problems of cross-assembly. So as a hack,
+ I added this mach. dependent ri twiddler. Ugly, but it gets
+ you there. -KWK */
+/* on m68k: first 4 bytes are normal unsigned long, next three bytes
+ are symbolnum, most sig. byte first. Last byte is broken up with
+ bit 7 as pcrel, bits 6 & 5 as length, bit 4 as pcrel, and the lower
+ nibble as nuthin. (on Sun 3 at least) */
+/* Translate the internal relocation information into target-specific
+ format. */
+#ifdef comment
+void
+ md_ri_to_chars(the_bytes, ri)
+char *the_bytes;
+struct reloc_info_generic *ri;
+{
+ /* this is easy */
+ md_number_to_chars(the_bytes, ri->r_address, 4);
+ /* now the fun stuff */
+ the_bytes[4] = (ri->r_symbolnum >> 16) & 0x0ff;
+ the_bytes[5] = (ri->r_symbolnum >> 8) & 0x0ff;
+ the_bytes[6] = ri->r_symbolnum & 0x0ff;
+ the_bytes[7] = (((ri->r_pcrel << 7) & 0x80) | ((ri->r_length << 5) & 0x60) |
+ ((ri->r_extern << 4) & 0x10));
+}
+#endif /* comment */
+
+void tc_aout_fix_to_chars(where, fixP, segment_address_in_file)
+char *where;
+fixS *fixP;
+relax_addressT segment_address_in_file;
+{
+ /*
+ * In: length of relocation (or of address) in chars: 1, 2 or 4.
+ * Out: GNU LD relocation length code: 0, 1, or 2.
+ */
+
+ static unsigned char nbytes_r_length[] = { 42, 0, 1, 42, 2 };
+ long r_symbolnum;
+ int r_flags;
+
+ know(fixP->fx_addsy != NULL);
+
+ md_number_to_chars(where,
+ fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file,
+ 4);
+
+ r_symbolnum = (S_IS_DEFINED(fixP->fx_addsy)
+ ? S_GET_TYPE(fixP->fx_addsy)
+ : fixP->fx_addsy->sy_number);
+ r_flags = (fixP->fx_pcrel? 0x80: 0)
+ | ((nbytes_r_length[fixP->fx_size] & 3) << 5)
+ | (!S_IS_DEFINED(fixP->fx_addsy)? 0x10: 0);
+
+#ifdef PIC
+ switch (fixP->fx_r_type) {
+ case NO_RELOC:
+ break;
+ case RELOC_32:
+ if (flagseen['k'] && S_IS_EXTERNAL(fixP->fx_addsy)) {
+ r_symbolnum = fixP->fx_addsy->sy_number;
+ r_flags |= 0x10; /* set extern bit */
+ }
+ break;
+ case RELOC_GLOB_DAT:
+ r_flags |= 8; /* set baserel bit */
+ r_symbolnum = fixP->fx_addsy->sy_number;
+ if (S_IS_EXTERNAL(fixP->fx_addsy))
+ r_flags |= 0x10;
+ break;
+ case RELOC_JMP_TBL:
+ r_flags |= 4; /* set jmptable bit */
+ break;
+ case RELOC_RELATIVE:
+ /* should never happen */
+ r_flags |= 2; /* set relative bit */
+ break;
+ }
+#endif /* PIC */
+
+ where[4] = (r_symbolnum >> 16) & 0x0ff;
+ where[5] = (r_symbolnum >> 8) & 0x0ff;
+ where[6] = r_symbolnum & 0x0ff;
+ where[7] = r_flags;
+
+ return;
+} /* tc_aout_fix_to_chars() */
+
+#endif /* OBJ_AOUT or OBJ_BOUT */
+
+#ifndef WORKING_DOT_WORD
+const int md_short_jump_size = 4;
+const int md_long_jump_size = 6;
+
+void
+ md_create_short_jump(ptr,from_addr,to_addr,frag,to_symbol)
+char *ptr;
+long from_addr,
+ to_addr;
+fragS *frag;
+symbolS *to_symbol;
+{
+ long offset;
+
+ offset = to_addr - (from_addr+2);
+
+ md_number_to_chars(ptr ,(long)0x6000,2);
+ md_number_to_chars(ptr+2,(long)offset,2);
+}
+
+void
+ md_create_long_jump(ptr,from_addr,to_addr,frag,to_symbol)
+char *ptr;
+long from_addr,
+ to_addr;
+fragS *frag;
+symbolS *to_symbol;
+{
+ long offset;
+
+ if (cpu_of_arch(current_architecture) < m68020) {
+ offset=to_addr-S_GET_VALUE(to_symbol);
+ md_number_to_chars(ptr ,(long)0x4EF9,2);
+ md_number_to_chars(ptr+2,(long)offset,4);
+ fix_new(frag,(ptr+2)-frag->fr_literal,4,to_symbol,(symbolS *)0,(long)0,0,
+ FIX_NO_RELOC);
+ } else {
+ offset=to_addr - (from_addr+2);
+ md_number_to_chars(ptr ,(long)0x60ff,2);
+ md_number_to_chars(ptr+2,(long)offset,4);
+ }
+}
+
+#endif
+/* Different values of OK tell what its OK to return. Things that aren't OK are an error (what a shock, no?)
+
+ 0: Everything is OK
+ 10: Absolute 1:8 only
+ 20: Absolute 0:7 only
+ 30: absolute 0:15 only
+ 40: Absolute 0:31 only
+ 50: absolute 0:127 only
+ 55: absolute -64:63 only
+ 60: absolute -128:127 only
+ 70: absolute 0:4095 only
+ 80: No bignums
+
+ */
+
+static int get_num(exp,ok)
+struct m68k_exp *exp;
+int ok;
+{
+#ifdef TEST2
+ long l = 0;
+
+ if (!exp->e_beg)
+ return 0;
+ if (*exp->e_beg == '0') {
+ if (exp->e_beg[1] == 'x')
+ sscanf(exp->e_beg+2,"%x",&l);
+ else
+ sscanf(exp->e_beg+1,"%O",&l);
+ return l;
+ }
+ return atol(exp->e_beg);
+#else
+ char *save_in;
+ char c_save;
+
+ if (!exp) {
+ /* Can't do anything */
+ return 0;
+ }
+ if (!exp->e_beg || !exp->e_end) {
+ seg(exp)=SEG_ABSOLUTE;
+ adds(exp)=0;
+ subs(exp)=0;
+ offs(exp)= (ok == 10) ? 1 : 0;
+ as_warn("Null expression defaults to %ld",offs(exp));
+ return 0;
+ }
+
+ exp->e_siz=0;
+ if (/* ok != 80 && */exp->e_end[-1] == ':' && (exp->e_end-exp->e_beg) >= 2) {
+ switch (exp->e_end[0]) {
+ case 's':
+ case 'S':
+ case 'b':
+ case 'B':
+ exp->e_siz=1;
+ break;
+ case 'w':
+ case 'W':
+ exp->e_siz=2;
+ break;
+ case 'l':
+ case 'L':
+ exp->e_siz=3;
+ break;
+ default:
+ as_bad("Unknown size for expression \"%c\"",exp->e_end[0]);
+ }
+ exp->e_end-=2;
+ }
+ c_save=exp->e_end[1];
+ exp->e_end[1]='\0';
+ save_in=input_line_pointer;
+ input_line_pointer=exp->e_beg;
+ switch (expression(&(exp->e_exp))) {
+ case SEG_PASS1:
+ seg(exp)=SEG_ABSOLUTE;
+ adds(exp)=0;
+ subs(exp)=0;
+ offs(exp)= (ok == 10) ? 1 : 0;
+ as_warn("Unknown expression: '%s' defaulting to %d",exp->e_beg,offs(exp));
+ break;
+
+ case SEG_ABSENT:
+ /* Do the same thing the VAX asm does */
+ seg(exp)=SEG_ABSOLUTE;
+ adds(exp)=0;
+ subs(exp)=0;
+ offs(exp)=0;
+ if (ok == 10) {
+ as_warn("expression out of range: defaulting to 1");
+ offs(exp)=1;
+ }
+ break;
+ case SEG_ABSOLUTE:
+ switch (ok) {
+ case 10:
+ if (offs(exp)<1 || offs(exp)>8) {
+ as_warn("expression out of range: defaulting to 1");
+ offs(exp)=1;
+ }
+ break;
+ case 20:
+ if (offs(exp)<0 || offs(exp)>7)
+ goto outrange;
+ break;
+ case 30:
+ if (offs(exp)<0 || offs(exp)>15)
+ goto outrange;
+ break;
+ case 40:
+ if (offs(exp)<0 || offs(exp)>32)
+ goto outrange;
+ break;
+ case 50:
+ if (offs(exp)<0 || offs(exp)>127)
+ goto outrange;
+ break;
+ case 55:
+ if (offs(exp)<-64 || offs(exp)>63)
+ goto outrange;
+ break;
+ case 60:
+ if (offs(exp)<-128 || offs(exp)>127)
+ goto outrange;
+ break;
+ case 70:
+ if (offs(exp)<0 || offs(exp)>4095) {
+ outrange:
+ as_warn("expression out of range: defaulting to 0");
+ offs(exp)=0;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case SEG_TEXT:
+ case SEG_DATA:
+ case SEG_BSS:
+ case SEG_UNKNOWN:
+ case SEG_DIFFERENCE:
+ if (ok >= 10 && ok <= 70) {
+ seg(exp)=SEG_ABSOLUTE;
+ adds(exp)=0;
+ subs(exp)=0;
+ offs(exp)= (ok == 10) ? 1 : 0;
+ as_warn("Can't deal with expression \"%s\": defaulting to %ld",exp->e_beg,offs(exp));
+ }
+ break;
+ case SEG_BIG:
+ if (ok == 80 && offs(exp)<0) { /* HACK! Turn it into a long */
+ LITTLENUM_TYPE words[6];
+
+ gen_to_words(words,2,8L);/* These numbers are magic! */
+ seg(exp)=SEG_ABSOLUTE;
+ adds(exp)=0;
+ subs(exp)=0;
+ offs(exp)=words[1]|(words[0]<<16);
+ } else if (ok != 0) {
+ seg(exp)=SEG_ABSOLUTE;
+ adds(exp)=0;
+ subs(exp)=0;
+ offs(exp)= (ok == 10) ? 1 : 0;
+ as_warn("Can't deal with expression \"%s\": defaulting to %ld",exp->e_beg,offs(exp));
+ }
+ break;
+ default:
+ as_fatal("failed sanity check.");
+ }
+ if (input_line_pointer != exp->e_end+1)
+ as_bad("Ignoring junk after expression");
+ exp->e_end[1]=c_save;
+ input_line_pointer=save_in;
+ if (exp->e_siz) {
+ switch (exp->e_siz) {
+ case 1:
+ if (!isbyte(offs(exp)))
+ as_warn("expression doesn't fit in BYTE");
+ break;
+ case 2:
+ if (!isword(offs(exp)))
+ as_warn("expression doesn't fit in WORD");
+ break;
+ }
+ }
+ return offs(exp);
+#endif
+} /* get_num() */
+
+/* These are the back-ends for the various machine dependent pseudo-ops. */
+void demand_empty_rest_of_line(); /* Hate those extra verbose names */
+
+static void s_data1() {
+ subseg_new(SEG_DATA,1);
+ demand_empty_rest_of_line();
+} /* s_data1() */
+
+static void s_data2() {
+ subseg_new(SEG_DATA,2);
+ demand_empty_rest_of_line();
+} /* s_data2() */
+
+static void s_bss() {
+ /* We don't support putting frags in the BSS segment, but we
+ can put them into initialized data for now... */
+ subseg_new(SEG_DATA,255); /* FIXME-SOON */
+ demand_empty_rest_of_line();
+} /* s_bss() */
+
+static void s_even() {
+ register int temp;
+ register long temp_fill;
+
+ temp = 1; /* JF should be 2? */
+ temp_fill = get_absolute_expression ();
+ if ( ! need_pass_2 ) /* Never make frag if expect extra pass. */
+ frag_align (temp, (int)temp_fill);
+ demand_empty_rest_of_line();
+} /* s_even() */
+
+static void s_proc() {
+ demand_empty_rest_of_line();
+} /* s_proc() */
+
+/* s_space is defined in read.c .skip is simply an alias to it. */
+
+/*
+ * md_parse_option
+ * Invocation line includes a switch not recognized by the base assembler.
+ * See if it's a processor-specific option. These are:
+ *
+ * -[A]m[c]68000, -[A]m[c]68008, -[A]m[c]68010, -[A]m[c]68020, -[A]m[c]68030, -[A]m[c]68040
+ * -[A]m[c]68881, -[A]m[c]68882, -[A]m[c]68851
+ * Select the architecture. Instructions or features not
+ * supported by the selected architecture cause fatal
+ * errors. More than one may be specified. The default is
+ * -m68020 -m68851 -m68881. Note that -m68008 is a synonym
+ * for -m68000, and -m68882 is a synonym for -m68881.
+ *
+ * MAYBE_FLOAT_TOO is defined below so that specifying a processor type
+ * (e.g. m68020) also requests that float instructions be included. This
+ * is the default setup, mostly to avoid hassling users. A better
+ * rearrangement of this structure would be to add an option to DENY
+ * floating point opcodes, for people who want to really know there's none
+ * of that funny floaty stuff going on. FIXME-later.
+ */
+#ifndef MAYBE_FLOAT_TOO
+#define MAYBE_FLOAT_TOO m68881
+#endif
+
+int md_parse_option(argP,cntP,vecP)
+char **argP;
+int *cntP;
+char ***vecP;
+{
+ switch (**argP) {
+ case 'l': /* -l means keep external to 2 bit offset
+ rather than 16 bit one */
+ break;
+
+ case 'S': /* -S means that jbsr's always turn into jsr's. */
+ break;
+
+ case 'A':
+ (*argP)++;
+ /* intentional fall-through */
+ case 'm':
+ (*argP)++;
+
+ if (**argP == 'c') {
+ (*argP)++;
+ } /* allow an optional "c" */
+
+ if (!strcmp(*argP, "68000")
+ || !strcmp(*argP, "68008")) {
+ current_architecture |= m68000;
+ } else if (!strcmp(*argP, "68010")) {
+#ifdef TE_SUN
+ omagic= 1<<16|OMAGIC;
+#endif
+ current_architecture |= m68010;
+
+ } else if (!strcmp(*argP, "68020")) {
+ current_architecture |= m68020 | MAYBE_FLOAT_TOO;
+
+ } else if (!strcmp(*argP, "68030")) {
+ current_architecture |= m68030 | MAYBE_FLOAT_TOO;
+
+ } else if (!strcmp(*argP, "68040")) {
+ current_architecture |= m68040 | MAYBE_FLOAT_TOO;
+
+#ifndef NO_68881
+ } else if (!strcmp(*argP, "68881")) {
+ current_architecture |= m68881;
+
+ } else if (!strcmp(*argP, "68882")) {
+ current_architecture |= m68882;
+
+#endif /* NO_68881 */
+#ifndef NO_68851
+ } else if (!strcmp(*argP,"68851")) {
+ current_architecture |= m68851;
+
+#endif /* NO_68851 */
+ } else {
+ as_warn("Unknown architecture, \"%s\". option ignored", *argP);
+ } /* switch on architecture */
+
+ while (**argP) (*argP)++;
+
+ break;
+
+ case 'p':
+ if (!strcmp(*argP,"pic")) {
+ (*argP) += 3;
+ break; /* -pic, Position Independent Code */
+ } else {
+ return(0);
+ } /* pic or not */
+
+#ifdef PIC
+ case 'k':
+ /* Predefine GOT symbol */
+ GOT_symbol = symbol_find_or_make("__GLOBAL_OFFSET_TABLE_");
+ break;
+#endif /* PIC */
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+
+#ifdef TEST2
+
+/* TEST2: Test md_assemble() */
+/* Warning, this routine probably doesn't work anymore */
+
+main()
+{
+ struct m68k_it the_ins;
+ char buf[120];
+ char *cp;
+ int n;
+
+ m68k_ip_begin();
+ for (;;) {
+ if (!gets(buf) || !*buf)
+ break;
+ if (buf[0] == '|' || buf[1] == '.')
+ continue;
+ for (cp=buf;*cp;cp++)
+ if (*cp == '\t')
+ *cp=' ';
+ if (is_label(buf))
+ continue;
+ memset(&the_ins, '\0', sizeof(the_ins));
+ m68k_ip(&the_ins,buf);
+ if (the_ins.error) {
+ printf("Error %s in %s\n",the_ins.error,buf);
+ } else {
+ printf("Opcode(%d.%s): ",the_ins.numo,the_ins.args);
+ for (n=0;n<the_ins.numo;n++)
+ printf(" 0x%x",the_ins.opcode[n]&0xffff);
+ printf(" ");
+ print_the_insn(&the_ins.opcode[0],stdout);
+ (void)putchar('\n');
+ }
+ for (n=0;n<strlen(the_ins.args)/2;n++) {
+ if (the_ins.operands[n].error) {
+ printf("op%d Error %s in %s\n",n,the_ins.operands[n].error,buf);
+ continue;
+ }
+ printf("mode %d, reg %d, ",the_ins.operands[n].mode,the_ins.operands[n].reg);
+ if (the_ins.operands[n].b_const)
+ printf("Constant: '%.*s', ",1+the_ins.operands[n].e_const-the_ins.operands[n].b_const,the_ins.operands[n].b_const);
+ printf("ireg %d, isiz %d, imul %d, ",the_ins.operands[n].ireg,the_ins.operands[n].isiz,the_ins.operands[n].imul);
+ if (the_ins.operands[n].b_iadd)
+ printf("Iadd: '%.*s',",1+the_ins.operands[n].e_iadd-the_ins.operands[n].b_iadd,the_ins.operands[n].b_iadd);
+ (void)putchar('\n');
+ }
+ }
+ m68k_ip_end();
+ return 0;
+}
+
+is_label(str)
+char *str;
+{
+ while (*str == ' ')
+ str++;
+ while (*str && *str != ' ')
+ str++;
+ if (str[-1] == ':' || str[1] == '=')
+ return 1;
+ return 0;
+}
+
+#endif
+
+/* Possible states for relaxation:
+
+ 0 0 branch offset byte (bra, etc)
+ 0 1 word
+ 0 2 long
+
+ 1 0 indexed offsets byte a0@(32,d4:w:1) etc
+ 1 1 word
+ 1 2 long
+
+ 2 0 two-offset index word-word a0@(32,d4)@(45) etc
+ 2 1 word-long
+ 2 2 long-word
+ 2 3 long-long
+
+ */
+
+
+
+#ifdef DONTDEF
+abort()
+{
+ printf("ABORT!\n");
+ exit(12);
+}
+
+print_frags()
+{
+ fragS *fragP;
+ extern fragS *text_frag_root;
+
+ for (fragP=text_frag_root;fragP;fragP=fragP->fr_next) {
+ printf("addr %lu next 0x%x fix %ld var %ld symbol 0x%x offset %ld\n",
+ fragP->fr_address,fragP->fr_next,fragP->fr_fix,fragP->fr_var,fragP->fr_symbol,fragP->fr_offset);
+ printf("opcode 0x%x type %d subtype %d\n\n",fragP->fr_opcode,fragP->fr_type,fragP->fr_subtype);
+ }
+ fflush(stdout);
+ return 0;
+}
+#endif
+
+#ifdef DONTDEF
+/*VARARGS1*/
+panic(format,args)
+char *format;
+{
+ fputs("Internal error:",stderr);
+ _doprnt(format,&args,stderr);
+ (void)putc('\n',stderr);
+ as_where();
+ abort();
+}
+#endif
+
+/* We have no need to default values of symbols. */
+
+/* ARGSUSED */
+symbolS *
+ md_undefined_symbol (name)
+char *name;
+{
+ return 0;
+}
+
+/* Parse an operand that is machine-specific.
+ We just return without modifying the expression if we have nothing
+ to do. */
+
+/* ARGSUSED */
+void
+ md_operand (expressionP)
+expressionS *expressionP;
+{
+}
+
+/* Round up a section size to the appropriate boundary. */
+long
+ md_section_align (segment, size)
+segT segment;
+long size;
+{
+ return size; /* Byte alignment is fine */
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the 68k, they're relative to the address of the offset. */
+long
+ md_pcrel_from (fixP)
+fixS *fixP;
+{
+ return(fixP->fx_where + fixP->fx_frag->fr_address);
+}
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of tc-m68k.c */
diff --git a/gnu/usr.bin/as/config/tc-m68k.h b/gnu/usr.bin/as/config/tc-m68k.h
new file mode 100644
index 0000000..ce69252
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-m68k.h
@@ -0,0 +1,60 @@
+/* This file is tc-m68k.h
+
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * This file is tp-generic.h and is intended to be a template for
+ * target processor specific header files.
+ */
+
+#define TC_M68K 1
+
+#define NO_LISTING
+
+#ifdef OLD_GAS
+#define REVERSE_SORT_RELOCS
+#endif /* OLD_GAS */
+
+#define AOUT_MACHTYPE 0x2
+#define LOCAL_LABELS_FB
+
+#define tc_crawl_symbol_chain(a) {;} /* not used */
+#define tc_headers_hook(a) {;} /* not used */
+#define tc_aout_pre_write_hook(x) {;} /* not used */
+
+#define LISTING_WORD_SIZE 2 /* A word is 2 bytes */
+#define LISTING_LHS_WIDTH 2 /* One word on the first line */
+#define LISTING_LHS_WIDTH_SECOND 2 /* One word on the second line */
+#define LISTING_LHS_CONT_LINES 4 /* And 4 lines max */
+#define LISTING_HEADER "68K GAS "
+
+/* Copied from write.c */
+#define M68K_AIM_KLUDGE(aim, this_state,this_type) \
+ if (aim == 0 && this_state == 4) { /* hard encoded from tc-m68k.c */ \
+ aim=this_type->rlx_forward+1; /* Force relaxation into word mode */ \
+ }
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of tc-m68k.h */
diff --git a/gnu/usr.bin/as/config/tc-m68kmote.h b/gnu/usr.bin/as/config/tc-m68kmote.h
new file mode 100644
index 0000000..8d98baf
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-m68kmote.h
@@ -0,0 +1,64 @@
+/* This file is tc-m68kmote.h
+
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * This file is tp-generic.h and is intended to be a template for
+ * target processor specific header files.
+ */
+
+#define TC_M68K 1
+
+#ifdef TE_SUN3
+/* This variable contains the value to write out at the beginning of
+ the a.out file. The 2<<16 means that this is a 68020 file instead
+ of an old-style 68000 file */
+
+#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE (2<<16|OMAGIC); /* Magic byte for file header */
+#endif /* TE_SUN3 */
+
+#define AOUT_MACHTYPE 0x2
+#define REVERSE_SORT_RELOCS /* FIXME-NOW: this line can be removed. */
+#define LOCAL_LABELS_FB
+
+#define tc_crawl_symbol_chain(a) {;} /* not used */
+#define tc_headers_hook(a) {;} /* not used */
+#define tc_aout_pre_write_hook(x) {;} /* not used */
+
+#define LISTING_WORD_SIZE 2 /* A word is 2 bytes */
+#define LISTING_LHS_WIDTH 3 /* 3 word on the first line */
+#define LISTING_LHS_WIDTH_SECOND 3 /* One word on the second line */
+#define LISTING_LHS_CONT_LINES 4 /* And 4 lines max */
+#define LISTING_HEADER "68K GAS "
+
+/* Copied from write.c */
+#define M68K_AIM_KLUDGE(aim, this_state,this_type) \
+ if (aim == 0 && this_state == 4) { /* hard encoded from tc-m68k.c */ \
+ aim=this_type->rlx_forward+1; /* Force relaxation into word mode */ \
+ }
+#define MRI
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of tc-m68kmote.h */
diff --git a/gnu/usr.bin/as/config/tc-m88k.c b/gnu/usr.bin/as/config/tc-m88k.c
new file mode 100644
index 0000000..fd7dd86
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-m88k.c
@@ -0,0 +1,1435 @@
+/* m88k.c -- Assembler for the Motorola 88000
+ Contributed by Devon Bowen of Buffalo University
+ and Torbjorn Granlund of the Swedish Institute of Computer Science.
+ Copyright (C) 1989-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "as.h"
+#include "opcode/m88k.h"
+
+struct m88k_insn
+{
+ unsigned long opcode;
+ expressionS exp;
+ enum reloc_type reloc;
+};
+
+#if __STDC__ == 1
+
+static int calcop(struct m88k_opcode *format, char *param, struct m88k_insn *insn);
+
+#else /* not __STDC__ */
+
+static int calcop();
+
+#endif /* not __STDC__ */
+
+char *getval ();
+char *get_reg ();
+char *get_imm16 ();
+char *get_bf ();
+char *get_pcr ();
+char *get_cmp ();
+char *get_cnd ();
+char *get_cr ();
+char *get_fcr ();
+char *get_vec9 ();
+
+struct field_val_assoc
+{
+ char *name;
+ unsigned val;
+};
+
+struct field_val_assoc cr_regs[] =
+{
+ {"PID", 0},
+ {"PSR", 1},
+ {"EPSR", 2},
+ {"SSBR", 3},
+ {"SXIP", 4},
+ {"SNIP", 5},
+ {"SFIP", 6},
+ {"VBR", 7},
+ {"DMT0", 8},
+ {"DMD0", 9},
+ {"DMA0", 10},
+ {"DMT1", 11},
+ {"DMD1", 12},
+ {"DMA1", 13},
+ {"DMT2", 14},
+ {"DMD2", 15},
+ {"DMA2", 16},
+ {"SR0", 17},
+ {"SR1", 18},
+ {"SR2", 19},
+ {"SR3", 20},
+
+ {NULL, 0},
+};
+
+struct field_val_assoc fcr_regs[] =
+{
+ {"FPECR", 0},
+ {"FPHS1", 1},
+ {"FPLS1", 2},
+ {"FPHS2", 3},
+ {"FPLS2", 4},
+ {"FPPT", 5},
+ {"FPRH", 6},
+ {"FPRL", 7},
+ {"FPIT", 8},
+
+ {"FPSR", 62},
+ {"FPCR", 63},
+
+ {NULL, 0},
+};
+
+struct field_val_assoc cmpslot[] =
+{
+/* Integer Floating point */
+ {"nc", 0},
+ {"cp", 1},
+ {"eq", 2},
+ {"ne", 3},
+ {"gt", 4},
+ {"le", 5},
+ {"lt", 6},
+ {"ge", 7},
+ {"hi", 8}, {"ou", 8},
+ {"ls", 9}, {"ib", 9},
+ {"lo", 10}, {"in", 10},
+ {"hs", 11}, {"ob", 11},
+
+ {NULL, 0},
+};
+
+struct field_val_assoc cndmsk[] =
+{
+ {"gt0", 1},
+ {"eq0", 2},
+ {"ge0", 3},
+ {"lt0", 12},
+ {"ne0", 13},
+ {"le0", 14},
+
+ {NULL, 0},
+};
+
+extern char *myname;
+static struct hash_control *op_hash = NULL;
+
+/* These bits should be turned off in the first address of every segment */
+int md_seg_align = 7;
+
+/* This is the number to put at the beginning of the a.out file */
+long omagic = OMAGIC;
+
+/* These chars start a comment anywhere in a source file (except inside
+ another comment */
+char comment_chars[] = ";";
+
+/* These chars only start a comment at the beginning of a line. */
+char line_comment_chars[] = "#";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* as in 0f123.456 */
+/* or 0H1.234E-12 (see exp chars above) */
+char FLT_CHARS[] = "dDfF";
+
+extern void float_cons (), cons (), s_globl (), s_line (),
+ s_space (), s_set (), stringer (), s_lcomm ();
+static void s_bss ();
+
+const pseudo_typeS md_pseudo_table[] = {
+ {"align", s_align_bytes, 0 },
+ {"def", s_set, 0},
+ {"dfloat", float_cons, 'd'},
+ {"ffloat", float_cons, 'f'},
+ {"global", s_globl, 0},
+ {"half", cons, 2 },
+ {"bss", s_bss, 0},
+ {"string", stringer, 0},
+ {"word", cons, 4 },
+ {"zero", s_space, 0},
+ {0}
+};
+
+const int md_reloc_size = 12; /* Size of relocation record */
+
+void
+md_begin ()
+{
+ char *retval = NULL;
+ unsigned int i = 0;
+
+ /* initialize hash table */
+
+ op_hash = hash_new ();
+ if (op_hash == NULL)
+ as_fatal ("Could not initialize hash table");
+
+ /* loop until you see the end of the list */
+
+ while (*m88k_opcodes[i].name)
+ {
+ char *name = m88k_opcodes[i].name;
+
+ /* hash each mnemonic and record its position */
+
+ retval = hash_insert (op_hash, name, &m88k_opcodes[i]);
+
+ if (retval != NULL && *retval != '\0')
+ as_fatal ("Can't hash instruction '%s':%s",
+ m88k_opcodes[i].name, retval);
+
+ /* skip to next unique mnemonic or end of list */
+
+ for (i++; !strcmp (m88k_opcodes[i].name, name); i++)
+ ;
+ }
+}
+
+int
+md_parse_option (argP, cntP, vecP)
+ char **argP;
+ int *cntP;
+ char ***vecP;
+{
+ as_warn ("unknown option: -%s", *argP);
+ return(0);
+}
+
+void
+md_assemble (op)
+ char *op;
+{
+ char *param, *thisfrag;
+ struct m88k_opcode *format;
+ struct m88k_insn insn;
+
+ assert (op);
+
+ /* skip over instruction to find parameters */
+
+ for (param = op; *param != 0 && !isspace (*param); param++)
+ ;
+ if (*param != 0)
+ *param++ = 0;
+
+ /* try to find the instruction in the hash table */
+
+ if ((format = (struct m88k_opcode *) hash_find (op_hash, op)) == NULL)
+ {
+ as_fatal ("Invalid mnemonic '%s'", op);
+ return;
+ }
+
+ /* try parsing this instruction into insn */
+
+ insn.exp.X_add_symbol = 0;
+ insn.exp.X_subtract_symbol = 0;
+ insn.exp.X_add_number = 0;
+ insn.exp.X_seg = 0;
+ insn.reloc = NO_RELOC;
+
+ while (!calcop(format, param, &insn))
+ {
+ /* if it doesn't parse try the next instruction */
+
+ if (!strcmp (format[0].name, format[1].name))
+ format++;
+ else
+ {
+ as_fatal ("Parameter syntax error");
+ return;
+ }
+ }
+
+ /* grow the current frag and plop in the opcode */
+
+ thisfrag = frag_more (4);
+ md_number_to_chars (thisfrag, insn.opcode, 4);
+
+ /* if this instruction requires labels mark it for later */
+
+ switch (insn.reloc)
+ {
+ case NO_RELOC:
+ break;
+
+ case RELOC_LO16:
+ case RELOC_HI16:
+ fix_new (frag_now,
+ thisfrag - frag_now->fr_literal + 2,
+ 2,
+ insn.exp.X_add_symbol,
+ insn.exp.X_subtract_symbol,
+ insn.exp.X_add_number,
+ 0,
+ insn.reloc);
+ break;
+
+ case RELOC_IW16:
+ fix_new (frag_now,
+ thisfrag - frag_now->fr_literal,
+ 4,
+ insn.exp.X_add_symbol,
+ insn.exp.X_subtract_symbol,
+ insn.exp.X_add_number,
+ 0,
+ insn.reloc);
+ break;
+
+ case RELOC_PC16:
+ fix_new (frag_now,
+ thisfrag - frag_now->fr_literal + 2,
+ 2,
+ insn.exp.X_add_symbol,
+ insn.exp.X_subtract_symbol,
+ insn.exp.X_add_number,
+ 1,
+ insn.reloc);
+ break;
+
+ case RELOC_PC26:
+ fix_new (frag_now,
+ thisfrag - frag_now->fr_literal,
+ 4,
+ insn.exp.X_add_symbol,
+ insn.exp.X_subtract_symbol,
+ insn.exp.X_add_number,
+ 1,
+ insn.reloc);
+ break;
+
+ default:
+ as_fatal ("Unknown relocation type");
+ break;
+ }
+}
+
+int
+calcop (format, param, insn)
+ struct m88k_opcode *format;
+ char *param;
+ struct m88k_insn *insn;
+{
+ char *fmt = format->op_spec;
+ int f;
+ unsigned val;
+ unsigned opcode;
+
+ insn->opcode = format->opcode;
+ opcode = 0;
+
+ for (;;)
+ {
+ if (param == 0)
+ return 0;
+ f = *fmt++;
+ switch (f)
+ {
+ case 0:
+ insn->opcode |= opcode;
+ return *param == 0;
+
+ default:
+ if (f != *param++)
+ return 0;
+ break;
+
+ case 'd':
+ param = get_reg (param, &val);
+ opcode |= val << 21;
+ break;
+
+ case '1':
+ param = get_reg (param, &val);
+ opcode |= val << 16;
+ break;
+
+ case '2':
+ param = get_reg (param, &val);
+ opcode |= val;
+ break;
+
+ case '3':
+ param = get_reg (param, &val);
+ opcode |= (val << 16) | val;
+ break;
+
+ case 'I':
+ param = get_imm16 (param, insn);
+ break;
+
+ case 'b':
+ param = get_bf (param, &val);
+ opcode |= val;
+ break;
+
+ case 'p':
+ param = get_pcr (param, insn, RELOC_PC16);
+ break;
+
+ case 'P':
+ param = get_pcr (param, insn, RELOC_PC26);
+ break;
+
+ case 'B':
+ param = get_cmp (param, &val);
+ opcode |= val;
+ break;
+
+ case 'M':
+ param = get_cnd (param, &val);
+ opcode |= val;
+ break;
+
+ case 'c':
+ param = get_cr (param, &val);
+ opcode |= val << 5;
+ break;
+
+ case 'f':
+ param = get_fcr (param, &val);
+ opcode |= val << 5;
+ break;
+
+ case 'V':
+ param = get_vec9 (param, &val);
+ opcode |= val;
+ break;
+
+ case '?':
+ /* Having this here repeats the warning somtimes.
+ But can't we stand that? */
+ as_warn ("Use of obsolete instruction");
+ break;
+ }
+ }
+}
+
+char *
+match_name (param, assoc_tab, valp)
+ char *param;
+ struct field_val_assoc *assoc_tab;
+ unsigned *valp;
+{
+ int i;
+ char *name;
+ int name_len;
+
+ for (i = 0;; i++)
+ {
+ name = assoc_tab[i].name;
+ if (name == NULL)
+ return NULL;
+ name_len = strlen (name);
+ if (!strncmp (param, name, name_len))
+ {
+ *valp = assoc_tab[i].val;
+ return param + name_len;
+ }
+ }
+}
+
+char *
+get_reg (param, regnop)
+ char *param;
+ unsigned *regnop;
+{
+ unsigned c;
+ unsigned regno;
+
+ c = *param++;
+ if (c == 'r')
+ {
+ regno = *param++ - '0';
+ if (regno < 10)
+ {
+ if (regno == 0)
+ {
+ *regnop = 0;
+ return param;
+ }
+ c = *param - '0';
+ if (c < 10)
+ {
+ regno = regno * 10 + c;
+ if (c < 32)
+ {
+ *regnop = regno;
+ return param + 1;
+ }
+ }
+ else
+ {
+ *regnop = regno;
+ return param;
+ }
+ }
+ return NULL;
+ }
+ else if (c == 's' && param[0] == 'p')
+ {
+ *regnop = 31;
+ return param + 1;
+ }
+
+ return 0;
+}
+
+char *
+get_imm16 (param, insn)
+ char *param;
+ struct m88k_insn *insn;
+{
+ enum reloc_type reloc = NO_RELOC;
+ unsigned int val;
+ segT seg;
+ char *save_ptr;
+
+ if (!strncmp (param, "hi16", 4) && !isalnum (param[4]))
+ {
+ reloc = RELOC_HI16;
+ param += 4;
+ }
+ else if (!strncmp (param, "lo16", 4) && !isalnum (param[4]))
+ {
+ reloc = RELOC_LO16;
+ param += 4;
+ }
+ else if (!strncmp (param, "iw16", 4) && !isalnum (param[4]))
+ {
+ reloc = RELOC_IW16;
+ param += 4;
+ }
+
+ save_ptr = input_line_pointer;
+ input_line_pointer = param;
+ seg = expression (&insn->exp);
+ param = input_line_pointer;
+ input_line_pointer = save_ptr;
+
+ val = insn->exp.X_add_number;
+
+ if (seg == SEG_ABSOLUTE)
+ {
+ /* Insert the value now, and reset reloc to NO_RELOC. */
+ if (reloc == NO_RELOC)
+ {
+ /* Warn about too big expressions if not surrounded by xx16. */
+ if (val > 0xffff)
+ as_warn ("Expression truncated to 16 bits");
+ }
+
+ if (reloc == RELOC_HI16)
+ val >>= 16;
+
+ insn->opcode |= val & 0xffff;
+ reloc = NO_RELOC;
+ }
+ else if (reloc == NO_RELOC)
+ /* We accept a symbol even without lo16, hi16, etc, and assume
+ lo16 was intended. */
+ reloc = RELOC_LO16;
+
+ insn->reloc = reloc;
+
+ return param;
+}
+
+char *
+get_pcr (param, insn, reloc)
+ char *param;
+ struct m88k_insn *insn;
+ enum reloc_type reloc;
+{
+ char *saveptr, *saveparam;
+ segT seg;
+
+ saveptr = input_line_pointer;
+ input_line_pointer = param;
+
+ seg = expression (&insn->exp);
+
+ saveparam = input_line_pointer;
+ input_line_pointer = saveptr;
+
+ /* Botch: We should relocate now if SEG_ABSOLUTE. */
+ insn->reloc = reloc;
+
+ return saveparam;
+}
+
+char *
+get_cmp (param, valp)
+ char *param;
+ unsigned *valp;
+{
+ unsigned int val;
+ char *save_ptr;
+
+ save_ptr = param;
+
+ param = match_name (param, cmpslot, valp);
+ val = *valp;
+
+ if (param == NULL)
+ {
+ param = save_ptr;
+
+ save_ptr = input_line_pointer;
+ input_line_pointer = param;
+ val = get_absolute_expression ();
+ param = input_line_pointer;
+ input_line_pointer = save_ptr;
+
+ if (val >= 32)
+ {
+ as_warn ("Expression truncated to 5 bits");
+ val %= 32;
+ }
+ }
+
+ *valp = val << 21;
+ return param;
+}
+
+char *
+get_cnd (param, valp)
+ char *param;
+ unsigned *valp;
+{
+ unsigned int val;
+
+ if (isdigit (*param))
+ {
+ param = getval (param, &val);
+
+ if (val >= 32)
+ {
+ as_warn ("Expression truncated to 5 bits");
+ val %= 32;
+ }
+ }
+ else
+ {
+ if (isupper (*param))
+ *param = tolower (*param);
+
+ if (isupper (param[1]))
+ param[1] = tolower (param[1]);
+
+ param = match_name (param, cndmsk, valp);
+
+ if (param == NULL)
+ return NULL;
+
+ val = *valp;
+ }
+
+ *valp = val << 21;
+ return param;
+}
+
+char *
+get_bf2 (param, bc)
+ char *param;
+ int bc;
+{
+ int depth = 0;
+ int c;
+
+ for (;;)
+ {
+ c = *param;
+ if (c == 0)
+ return param;
+ else if (c == '(')
+ depth++;
+ else if (c == ')')
+ depth--;
+ else if (c == bc && depth <= 0)
+ return param;
+ param++;
+ }
+}
+
+char *
+get_bf_offset_expression (param, offsetp)
+ char *param;
+ unsigned *offsetp;
+{
+ unsigned offset;
+
+ if (isalpha (param[0]))
+ {
+ if (isupper (param[0]))
+ param[0] = tolower (param[0]);
+ if (isupper (param[1]))
+ param[1] = tolower (param[1]);
+
+ param = match_name (param, cmpslot, offsetp);
+
+ return param;
+ }
+ else
+ {
+ input_line_pointer = param;
+ offset = get_absolute_expression ();
+ param = input_line_pointer;
+ }
+
+ *offsetp = offset;
+ return param;
+}
+
+char *
+get_bf (param, valp)
+ char *param;
+ unsigned *valp;
+{
+ unsigned offset = 0;
+ unsigned width = 0;
+ char *xp;
+ char *save_ptr;
+
+ xp = get_bf2 (param, '<');
+
+ save_ptr = input_line_pointer;
+ input_line_pointer = param;
+ if (*xp == 0)
+ {
+ /* We did not find '<'. We have an offset (width implicitly 32). */
+ param = get_bf_offset_expression (param, &offset);
+ if (param == NULL)
+ return NULL;
+ input_line_pointer = save_ptr;
+ }
+ else
+ {
+ *xp++ = 0; /* Overwrite the '<' */
+ param = get_bf2 (xp, '>');
+ if (*param == 0)
+ return NULL;
+ *param++ = 0; /* Overwrite the '>' */
+
+ width = get_absolute_expression ();
+ xp = get_bf_offset_expression (xp, &offset);
+ input_line_pointer = save_ptr;
+
+ if (xp + 1 != param)
+ return NULL;
+ }
+
+ *valp = ((width % 32) << 5) | (offset % 32);
+
+ return param;
+}
+
+char *
+get_cr (param, regnop)
+ char *param;
+ unsigned *regnop;
+{
+ unsigned regno;
+ unsigned c;
+/* int i; FIXME remove this */
+/* int name_len; FIXME remove this */
+
+ if (!strncmp (param, "cr", 2))
+ {
+ param += 2;
+
+ regno = *param++ - '0';
+ if (regno < 10)
+ {
+ if (regno == 0)
+ {
+ *regnop = 0;
+ return param;
+ }
+ c = *param - '0';
+ if (c < 10)
+ {
+ regno = regno * 10 + c;
+ if (c < 64)
+ {
+ *regnop = regno;
+ return param + 1;
+ }
+ }
+ else
+ {
+ *regnop = regno;
+ return param;
+ }
+ }
+ return NULL;
+ }
+
+ param = match_name (param, cr_regs, regnop);
+
+ return param;
+}
+
+char *
+get_fcr (param, regnop)
+ char *param;
+ unsigned *regnop;
+{
+ unsigned regno;
+ unsigned c;
+/* int i; FIXME remove this */
+/* int name_len; FIXME: remove this */
+
+ if (!strncmp (param, "fcr", 3))
+ {
+ param += 3;
+
+ regno = *param++ - '0';
+ if (regno < 10)
+ {
+ if (regno == 0)
+ {
+ *regnop = 0;
+ return param;
+ }
+ c = *param - '0';
+ if (c < 10)
+ {
+ regno = regno * 10 + c;
+ if (c < 64)
+ {
+ *regnop = regno;
+ return param + 1;
+ }
+ }
+ else
+ {
+ *regnop = regno;
+ return param;
+ }
+ }
+ return NULL;
+ }
+
+ param = match_name (param, fcr_regs, regnop);
+
+ return param;
+}
+
+char *
+get_vec9 (param, valp)
+ char *param;
+ unsigned *valp;
+{
+ unsigned val;
+ char *save_ptr;
+
+ save_ptr = input_line_pointer;
+ input_line_pointer = param;
+ val = get_absolute_expression ();
+ param = input_line_pointer;
+ input_line_pointer = save_ptr;
+
+ if (val >= 1 << 9)
+ as_warn ("Expression truncated to 9 bits");
+
+ *valp = val % (1 << 9);
+
+ return param;
+}
+
+#define hexval(z) \
+ (isdigit (z) ? (z) - '0' : \
+ islower (z) ? (z) - 'a' + 10 : \
+ isupper (z) ? (z) - 'A' + 10 : -1)
+
+char *
+getval (param, valp)
+ char *param;
+ unsigned int *valp;
+{
+ unsigned int val = 0;
+ unsigned int c;
+
+ c = *param++;
+ if (c == '0')
+ {
+ c = *param++;
+ if (c == 'x' || c == 'X')
+ {
+ c = *param++;
+ c = hexval (c);
+ while (c < 16)
+ {
+ val = val * 16 + c;
+ c = *param++;
+ c = hexval (c);
+ }
+ }
+ else
+ {
+ c -= '0';
+ while (c < 8)
+ {
+ val = val * 8 + c;
+ c = *param++ - '0';
+ }
+ }
+ }
+ else
+ {
+ c -= '0';
+ while (c < 10)
+ {
+ val = val * 10 + c;
+ c = *param++ - '0';
+ }
+ }
+
+ *valp = val;
+ return param - 1;
+}
+
+void
+md_number_to_chars (buf, val, nbytes)
+char *buf;
+long val;
+int nbytes;
+{
+ switch (nbytes)
+ {
+ case 4:
+ *buf++ = val >> 24;
+ *buf++ = val >> 16;
+ case 2:
+ *buf++ = val >> 8;
+ case 1:
+ *buf = val;
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+#ifdef comment
+
+void
+md_number_to_imm (buf, val, nbytes, fixP, seg_type)
+unsigned char *buf;
+unsigned int val;
+int nbytes;
+fixS *fixP;
+int seg_type;
+{
+ if (seg_type != N_TEXT || fixP->fx_r_type == NO_RELOC)
+ {
+ switch (nbytes)
+ {
+ case 4:
+ *buf++ = val >> 24;
+ *buf++ = val >> 16;
+ case 2:
+ *buf++ = val >> 8;
+ case 1:
+ *buf = val;
+ break;
+
+ default:
+ abort ();
+ }
+ return;
+ }
+
+ switch (fixP->fx_r_type)
+ {
+ case RELOC_IW16:
+ buf[2] = val >> 8;
+ buf[3] = val;
+ break;
+
+ case RELOC_LO16:
+ buf[0] = val >> 8;
+ buf[1] = val;
+ break;
+
+ case RELOC_HI16:
+ buf[0] = val >> 24;
+ buf[1] = val >> 16;
+ break;
+
+ case RELOC_PC16:
+ val += 4;
+ buf[0] = val >> 10;
+ buf[1] = val >> 2;
+ break;
+
+ case RELOC_PC26:
+ val += 4;
+ buf[0] |= (val >> 26) & 0x03;
+ buf[1] = val >> 18;
+ buf[2] = val >> 10;
+ buf[3] = val >> 2;
+ break;
+
+ case RELOC_32:
+ buf[0] = val >> 24;
+ buf[1] = val >> 16;
+ buf[2] = val >> 8;
+ buf[3] = val;
+ break;
+
+ default:
+ as_fatal ("Bad relocation type");
+ break;
+ }
+}
+#endif /* comment */
+
+/* Apply a fixS to the frags, now that we know the value it ought to
+ hold. */
+
+void md_apply_fix(fixP, val)
+fixS *fixP;
+long val;
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ fixP->fx_addnumber = val;
+
+
+ switch (fixP->fx_r_type) {
+
+ case RELOC_IW16:
+ buf[2] = val >> 8;
+ buf[3] = val;
+ break;
+
+ case RELOC_LO16:
+ buf[0] = val >> 8;
+ buf[1] = val;
+ break;
+
+ case RELOC_HI16:
+ buf[0] = val >> 24;
+ buf[1] = val >> 16;
+ break;
+
+ case RELOC_PC16:
+ val += 4;
+ buf[0] = val >> 10;
+ buf[1] = val >> 2;
+ break;
+
+ case RELOC_PC26:
+ val += 4;
+ buf[0] |= (val >> 26) & 0x03;
+ buf[1] = val >> 18;
+ buf[2] = val >> 10;
+ buf[3] = val >> 2;
+ break;
+
+ case RELOC_32:
+ buf[0] = val >> 24;
+ buf[1] = val >> 16;
+ buf[2] = val >> 8;
+ buf[3] = val;
+ break;
+
+ case NO_RELOC:
+ switch (fixP->fx_size) {
+ case 4:
+ *buf++ = val >> 24;
+ *buf++ = val >> 16;
+ case 2:
+ *buf++ = val >> 8;
+ case 1:
+ *buf = val;
+ break;
+
+ default:
+ abort ();
+ }
+
+ default:
+ as_bad("bad relocation type: 0x%02x", fixP->fx_r_type);
+ break;
+ }
+
+ return;
+} /* md_apply_fix() */
+
+void
+md_number_to_disp (buf, val, nbytes)
+char *buf;
+int val;
+int nbytes;
+{
+ as_fatal ("md_number_to_disp not defined");
+ md_number_to_chars (buf, val, nbytes);
+}
+
+void
+md_number_to_field (buf, val, nbytes)
+char *buf;
+int val;
+int nbytes;
+{
+ as_fatal ("md_number_to_field not defined");
+ md_number_to_chars (buf, val, nbytes);
+}
+
+#define MAX_LITTLENUMS 6
+
+/* Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP. An error message is returned, or NULL on OK.
+ */
+char *
+md_atof (type, litP, sizeP)
+ char type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+ char *atof_ieee ();
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP=0;
+ return "Bad call to MD_ATOF()";
+ }
+ t=atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer=t;
+
+ *sizeP=prec * sizeof (LITTLENUM_TYPE);
+ for (wordP=words;prec--;)
+ {
+ md_number_to_chars (litP, (long) (*wordP++), sizeof (LITTLENUM_TYPE));
+ litP+=sizeof (LITTLENUM_TYPE);
+ }
+ return ""; /* Someone should teach Dean about null pointers */
+}
+
+int md_short_jump_size = 4;
+
+void
+md_create_short_jump (ptr, from_addr, to_addr, frag, to_symbol)
+ char *ptr;
+ long from_addr, to_addr;
+ fragS *frag;
+ symbolS *to_symbol;
+{
+ ptr[0] = 0xc0; ptr[1] = 0x00; ptr[2] = 0x00; ptr[3] = 0x00;
+ fix_new (frag,
+ ptr - frag->fr_literal,
+ 4,
+ to_symbol,
+ (symbolS *) 0,
+ (long int) 0,
+ 0,
+ RELOC_PC26); /* Botch: Shouldn't this be RELOC_PC16? */
+}
+
+int md_long_jump_size = 4;
+
+void
+md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol)
+ char *ptr;
+ long from_addr, to_addr;
+ fragS *frag;
+ symbolS *to_symbol;
+{
+ ptr[0] = 0xc0; ptr[1] = 0x00; ptr[2] = 0x00; ptr[3] = 0x00;
+ fix_new (frag,
+ ptr - frag->fr_literal,
+ 4,
+ to_symbol,
+ (symbolS *) 0,
+ (long int) 0,
+ 0,
+ RELOC_PC26);
+}
+
+int
+md_estimate_size_before_relax (fragP, segment_type)
+ fragS *fragP;
+ segT segment_type;
+{
+ as_fatal("Relaxation should never occur");
+ return(0);
+}
+
+const relax_typeS md_relax_table[] = {0};
+
+void
+md_convert_frag (headers, fragP)
+object_headers *headers;
+ fragS *fragP;
+{
+ as_fatal ("Relaxation should never occur");
+}
+
+void
+md_end ()
+{
+}
+
+#ifdef comment
+
+/*
+ * Risc relocations are completely different, so it needs
+ * this machine dependent routine to emit them.
+ */
+void
+emit_relocations (fixP, segment_address_in_file)
+ fixS *fixP;
+ relax_addressT segment_address_in_file;
+{
+ struct reloc_info_m88k ri;
+ symbolS *symbolP;
+ extern char *next_object_file_charP;
+
+ bzero ((char *) &ri, sizeof (ri));
+ for (; fixP; fixP = fixP->fx_next) {
+
+ if (fixP->fx_r_type >= NO_RELOC) {
+ fprintf (stderr, "fixP->fx_r_type = %d\n", fixP->fx_r_type);
+ abort ();
+ }
+
+ if ((symbolP = fixP->fx_addsy) != NULL) {
+ ri.r_address = fixP->fx_frag->fr_address +
+ fixP->fx_where - segment_address_in_file;
+ if ((symbolP->sy_type & N_TYPE) == N_UNDF) {
+ ri.r_extern = 1;
+ ri.r_symbolnum = symbolP->sy_number;
+ } else {
+ ri.r_extern = 0;
+ ri.r_symbolnum = symbolP->sy_type & N_TYPE;
+ }
+ if (symbolP && symbolP->sy_frag) {
+ ri.r_addend = symbolP->sy_frag->fr_address;
+ }
+ ri.r_type = fixP->fx_r_type;
+ if (fixP->fx_pcrel) {
+/* ri.r_addend -= fixP->fx_where; */
+ ri.r_addend -= ri.r_address;
+ } else {
+ ri.r_addend = fixP->fx_addnumber;
+ }
+
+/* md_ri_to_chars ((char *) &ri, ri); */
+ append (&next_object_file_charP, (char *)& ri, sizeof (ri));
+ }
+ }
+ return;
+}
+#endif /* comment */
+
+/* Translate internal representation of relocation info to target format.
+
+ On m88k: first 4 bytes are normal unsigned long address,
+ next three bytes are index, most sig. byte first.
+ Byte 7 is broken up with bit 7 as external,
+ bits 6, 5, & 4 unused, and the lower four bits as relocation
+ type.
+ Next 4 bytes are long addend. */
+
+void tc_aout_fix_to_chars(where, fixP, segment_address_in_file)
+char *where;
+fixS *fixP;
+relax_addressT segment_address_in_file;
+{
+ long r_index;
+ long r_extern;
+ long r_addend = 0;
+ long r_address;
+
+ know(fixP->fx_addsy);
+
+ if (!S_IS_DEFINED(fixP->fx_addsy)) {
+ r_extern = 1;
+ r_index = fixP->fx_addsy->sy_number;
+ } else {
+ r_extern = 0;
+ r_index = S_GET_TYPE(fixP->fx_addsy);
+ }
+
+ /* this is easy */
+ md_number_to_chars(where,
+ r_address = fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file,
+ 4);
+
+ /* now the fun stuff */
+ where[4] = (r_index >> 16) & 0x0ff;
+ where[5] = (r_index >> 8) & 0x0ff;
+ where[6] = r_index & 0x0ff;
+ where[7] = ((r_extern << 7) & 0x80) | (0 & 0x70) | (fixP->fx_r_type & 0xf);
+
+ /* Also easy */
+ if (fixP->fx_addsy->sy_frag) {
+ r_addend = fixP->fx_addsy->sy_frag->fr_address;
+ }
+
+ if (fixP->fx_pcrel) {
+ r_addend -= r_address;
+ } else {
+ r_addend = fixP->fx_addnumber;
+ }
+
+ md_number_to_chars(&where[8], r_addend, 4);
+
+ return;
+} /* tc_aout_fix_to_chars() */
+
+
+static void
+s_bss()
+{
+ char *name;
+ char c;
+ char *p;
+ int temp, bss_align = 1;
+ symbolS *symbolP;
+ extern char is_end_of_line[256];
+
+ name = input_line_pointer;
+ c = get_symbol_end();
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE();
+ if ( * input_line_pointer != ',' )
+ {
+ as_warn("Expected comma after name");
+ ignore_rest_of_line();
+ return;
+ }
+ input_line_pointer ++;
+ if ((temp = get_absolute_expression()) < 0)
+ {
+ as_warn("BSS length (%d.) <0! Ignored.", temp);
+ ignore_rest_of_line();
+ return;
+ }
+ *p = 0;
+ symbolP = symbol_find_or_make(name);
+ *p = c;
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ bss_align = get_absolute_expression();
+ while (local_bss_counter % bss_align != 0)
+ local_bss_counter++;
+ }
+
+ if (!S_IS_DEFINED(symbolP)
+ || (S_GET_SEGMENT(symbolP) == SEG_BSS
+ && S_GET_VALUE(symbolP) == local_bss_counter)) {
+ S_SET_VALUE(symbolP, local_bss_counter);
+ S_SET_SEGMENT(symbolP, SEG_BSS);
+ symbolP->sy_frag = &bss_address_frag;
+ local_bss_counter += temp;
+ } else {
+ as_warn( "Ignoring attempt to re-define symbol from %d. to %d.",
+ S_GET_VALUE(symbolP), local_bss_counter );
+ }
+ while (!is_end_of_line[*input_line_pointer])
+ {
+ input_line_pointer++;
+ }
+
+ return;
+}
+
+/* We have no need to default values of symbols. */
+
+/* ARGSUSED */
+symbolS *md_undefined_symbol(name)
+char *name;
+{
+ return 0;
+} /* md_undefined_symbol() */
+
+/* Parse an operand that is machine-specific.
+ We just return without modifying the expression if we have nothing
+ to do. */
+
+/* ARGSUSED */
+void md_operand(expressionP)
+expressionS *expressionP;
+{
+} /* md_operand() */
+
+/* Round up a section size to the appropriate boundary. */
+long md_section_align(segment, size)
+segT segment;
+long size;
+{
+ return((size + 7) & ~7); /* Round all sects to multiple of 8 */
+} /* md_section_align() */
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the sparc, they're relative to the address of the offset, plus
+ its size. This gets us to the following instruction.
+ (??? Is this right? FIXME-SOON) */
+long md_pcrel_from(fixP)
+fixS *fixP;
+{
+ return(fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address);
+} /* md_pcrel_from() */
+
+ /* end of tc-m88k.c */
diff --git a/gnu/usr.bin/as/config/tc-m88k.h b/gnu/usr.bin/as/config/tc-m88k.h
new file mode 100644
index 0000000..d5960d1
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-m88k.h
@@ -0,0 +1,35 @@
+/* m88k.h -- Assembler for the Motorola 88000
+ Contributed by Devon Bowen of Buffalo University
+ and Torbjorn Granlund of the Swedish Institute of Computer Science.
+ Copyright (C) 1989-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define TC_M88K 1
+
+#define NO_LISTING
+#define NO_DOT_PSEUDOS
+#define ALLOW_ATSIGN
+
+#define LOCAL_LABEL(name) (name[0] == '@' \
+ && ( name[1] == 'L' || name[1] == '.' ))
+
+#define tc_crawl_symbol_chain(a) {;} /* not used */
+#define tc_headers_hook(a) {;} /* not used */
+#define tc_aout_pre_write_hook(x) {;} /* not used */
+
+ /* end of tc-m88k.h */
diff --git a/gnu/usr.bin/as/config/tc-mips.c b/gnu/usr.bin/as/config/tc-mips.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-mips.c
diff --git a/gnu/usr.bin/as/config/tc-mips.h b/gnu/usr.bin/as/config/tc-mips.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-mips.h
diff --git a/gnu/usr.bin/as/config/tc-ns32k.c b/gnu/usr.bin/as/config/tc-ns32k.c
new file mode 100644
index 0000000..02d86c4
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-ns32k.c
@@ -0,0 +1,1923 @@
+/* ns32k.c -- Assemble on the National Semiconductor 32k series
+ Copyright (C) 1987, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*#define SHOW_NUM 1*/ /* uncomment for debugging */
+
+#include <stdio.h>
+#include <ctype.h>
+#ifdef USG
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+#include "opcode/ns32k.h"
+
+#include "as.h"
+
+#include "obstack.h"
+
+/* Macros */
+#define IIF_ENTRIES 13 /* number of entries in iif */
+#define PRIVATE_SIZE 256 /* size of my garbage memory */
+#define MAX_ARGS 4
+#define DEFAULT -1 /* addr_mode returns this value when plain constant or label is encountered */
+
+#define IIF(ptr,a1,c1,e1,g1,i1,k1,m1,o1,q1,s1,u1) \
+ iif.iifP[ptr].type= a1; \
+ iif.iifP[ptr].size= c1; \
+ iif.iifP[ptr].object= e1; \
+ iif.iifP[ptr].object_adjust= g1; \
+ iif.iifP[ptr].pcrel= i1; \
+ iif.iifP[ptr].pcrel_adjust= k1; \
+ iif.iifP[ptr].im_disp= m1; \
+ iif.iifP[ptr].relax_substate= o1; \
+ iif.iifP[ptr].bit_fixP= q1; \
+ iif.iifP[ptr].addr_mode= s1; \
+ iif.iifP[ptr].bsr= u1;
+
+#ifdef TE_SEQUENT
+#define LINE_COMMENT_CHARS "|"
+#define ABSOLUTE_PREFIX '@'
+#define IMMEDIATE_PREFIX '#'
+#endif
+
+#ifndef LINE_COMMENT_CHARS
+#define LINE_COMMENT_CHARS "#"
+#endif
+
+char comment_chars[] = "#";
+char line_comment_chars[] = LINE_COMMENT_CHARS;
+#if !defined(ABSOLUTE_PREFIX) && !defined(IMMEDIATE_PREFIX)
+#define ABSOLUTE_PREFIX '@' /* One or the other MUST be defined */
+#endif
+
+struct addr_mode {
+ char mode; /* addressing mode of operand (0-31) */
+ char scaled_mode; /* mode combined with scaled mode */
+ char scaled_reg; /* register used in scaled+1 (1-8) */
+ char float_flag; /* set if R0..R7 was F0..F7 ie a floating-point-register */
+ char am_size; /* estimated max size of general addr-mode parts*/
+ char im_disp; /* if im_disp == 1 we have a displacement */
+ char pcrel; /* 1 if pcrel, this is really redundant info */
+ char disp_suffix[2]; /* length of displacement(s), 0=undefined */
+ char *disp[2]; /* pointer(s) at displacement(s)
+ or immediates(s) (ascii) */
+ char index_byte; /* index byte */
+};
+typedef struct addr_mode addr_modeS;
+
+
+char *freeptr,*freeptr_static; /* points at some number of free bytes */
+struct hash_control *inst_hash_handle;
+
+struct ns32k_opcode *desc; /* pointer at description of instruction */
+addr_modeS addr_modeP;
+char EXP_CHARS[] = "eE";
+char FLT_CHARS[] = "fd"; /* we don't want to support lowercase, do we */
+
+/* UPPERCASE denotes live names
+ * when an instruction is built, IIF is used as an intermidiate form to store
+ * the actual parts of the instruction. A ns32k machine instruction can
+ * be divided into a couple of sub PARTs. When an instruction is assembled
+ * the appropriate PART get an assignment. When an IIF has been completed it's
+ * converted to a FRAGment as specified in AS.H */
+
+/* internal structs */
+struct option {
+ char *pattern;
+ unsigned long or;
+ unsigned long and;
+};
+
+typedef struct {
+ int type; /* how to interpret object */
+ int size; /* Estimated max size of object */
+ unsigned long object; /* binary data */
+ int object_adjust; /* number added to object */
+ int pcrel; /* True if object is pcrel */
+ int pcrel_adjust; /* length in bytes from the
+ instruction start to the
+ displacement */
+ int im_disp; /* True if the object is a displacement */
+ relax_substateT relax_substate; /* Initial relaxsubstate */
+ bit_fixS *bit_fixP; /* Pointer at bit_fix struct */
+ int addr_mode; /* What addrmode do we associate with this iif-entry */
+ char bsr; /* Sequent hack */
+}iif_entryT; /* Internal Instruction Format */
+
+struct int_ins_form {
+ int instr_size; /* Max size of instruction in bytes. */
+ iif_entryT iifP[IIF_ENTRIES + 1];
+};
+struct int_ins_form iif;
+expressionS exprP;
+char *input_line_pointer;
+/* description of the PARTs in IIF
+ *object[n]:
+ * 0 total length in bytes of entries in iif
+ * 1 opcode
+ * 2 index_byte_a
+ * 3 index_byte_b
+ * 4 disp_a_1
+ * 5 disp_a_2
+ * 6 disp_b_1
+ * 7 disp_b_2
+ * 8 imm_a
+ * 9 imm_b
+ * 10 implied1
+ * 11 implied2
+ *
+ * For every entry there is a datalength in bytes. This is stored in size[n].
+ * 0, the objectlength is not explicitly given by the instruction
+ * and the operand is undefined. This is a case for relaxation.
+ * Reserve 4 bytes for the final object.
+ *
+ * 1, the entry contains one byte
+ * 2, the entry contains two bytes
+ * 3, the entry contains three bytes
+ * 4, the entry contains four bytes
+ * etc
+ *
+ * Furthermore, every entry has a data type identifier in type[n].
+ *
+ * 0, the entry is void, ignore it.
+ * 1, the entry is a binary number.
+ * 2, the entry is a pointer at an expression.
+ * Where expression may be as simple as a single '1',
+ * and as complicated as foo-bar+12,
+ * foo and bar may be undefined but suffixed by :{b|w|d} to
+ * control the length of the object.
+ *
+ * 3, the entry is a pointer at a bignum struct
+ *
+ *
+ * The low-order-byte coresponds to low physical memory.
+ * Obviously a FRAGment must be created for each valid disp in PART whose
+ * datalength is undefined (to bad) .
+ * The case where just the expression is undefined is less severe and is
+ * handled by fix. Here the number of bytes in the objectfile is known.
+ * With this representation we simplify the assembly and separates the
+ * machine dependent/independent parts in a more clean way (said OE)
+ */
+
+struct option opt1[]= /* restore, exit */
+{
+ { "r0", 0x80, 0xff },
+ { "r1", 0x40, 0xff },
+ { "r2", 0x20, 0xff },
+ { "r3", 0x10, 0xff },
+ { "r4", 0x08, 0xff },
+ { "r5", 0x04, 0xff },
+ { "r6", 0x02, 0xff },
+ { "r7", 0x01, 0xff },
+ { 0 , 0x00, 0xff }
+};
+struct option opt2[]= /* save, enter */
+{
+ { "r0", 0x01, 0xff },
+ { "r1", 0x02, 0xff },
+ { "r2", 0x04, 0xff },
+ { "r3", 0x08, 0xff },
+ { "r4", 0x10, 0xff },
+ { "r5", 0x20, 0xff },
+ { "r6", 0x40, 0xff },
+ { "r7", 0x80, 0xff },
+ { 0 , 0x00, 0xff }
+};
+struct option opt3[]= /* setcfg */
+{
+ { "c", 0x8, 0xff },
+ { "m", 0x4, 0xff },
+ { "f", 0x2, 0xff },
+ { "i", 0x1, 0xff },
+ { 0 , 0x0, 0xff }
+};
+struct option opt4[]= /* cinv */
+{
+ { "a", 0x4, 0xff },
+ { "i", 0x2, 0xff },
+ { "d", 0x1, 0xff },
+ { 0 , 0x0, 0xff }
+};
+struct option opt5[]= /* string inst */
+{
+ { "b", 0x2, 0xff },
+ { "u", 0xc, 0xff },
+ { "w", 0x4, 0xff },
+ { 0 , 0x0, 0xff }
+};
+struct option opt6[]= /* plain reg ext,cvtp etc */
+{
+ { "r0", 0x00, 0xff },
+ { "r1", 0x01, 0xff },
+ { "r2", 0x02, 0xff },
+ { "r3", 0x03, 0xff },
+ { "r4", 0x04, 0xff },
+ { "r5", 0x05, 0xff },
+ { "r6", 0x06, 0xff },
+ { "r7", 0x07, 0xff },
+ { 0 , 0x00, 0xff }
+};
+
+#if !defined(NS32032) && !defined(NS32532)
+#define NS32032
+#endif
+
+struct option cpureg_532[]= /* lpr spr */
+{
+ { "us", 0x0, 0xff },
+ { "dcr", 0x1, 0xff },
+ { "bpc", 0x2, 0xff },
+ { "dsr", 0x3, 0xff },
+ { "car", 0x4, 0xff },
+ { "fp", 0x8, 0xff },
+ { "sp", 0x9, 0xff },
+ { "sb", 0xa, 0xff },
+ { "usp", 0xb, 0xff },
+ { "cfg", 0xc, 0xff },
+ { "psr", 0xd, 0xff },
+ { "intbase", 0xe, 0xff },
+ { "mod", 0xf, 0xff },
+ { 0 , 0x00, 0xff }
+};
+struct option mmureg_532[]= /* lmr smr */
+{
+ { "mcr", 0x9, 0xff },
+ { "msr", 0xa, 0xff },
+ { "tear", 0xb, 0xff },
+ { "ptb0", 0xc, 0xff },
+ { "ptb1", 0xd, 0xff },
+ { "ivar0", 0xe, 0xff },
+ { "ivar1", 0xf, 0xff },
+ { 0 , 0x0, 0xff }
+};
+
+struct option cpureg_032[]= /* lpr spr */
+{
+ { "upsr", 0x0, 0xff },
+ { "fp", 0x8, 0xff },
+ { "sp", 0x9, 0xff },
+ { "sb", 0xa, 0xff },
+ { "psr", 0xd, 0xff },
+ { "intbase", 0xe, 0xff },
+ { "mod", 0xf, 0xff },
+ { 0 , 0x0, 0xff }
+};
+struct option mmureg_032[]= /* lmr smr */
+{
+ { "bpr0", 0x0, 0xff },
+ { "bpr1", 0x1, 0xff },
+ { "pf0", 0x4, 0xff },
+ { "pf1", 0x5, 0xff },
+ { "sc", 0x8, 0xff },
+ { "msr", 0xa, 0xff },
+ { "bcnt", 0xb, 0xff },
+ { "ptb0", 0xc, 0xff },
+ { "ptb1", 0xd, 0xff },
+ { "eia", 0xf, 0xff },
+ { 0 , 0x0, 0xff }
+};
+
+#if defined(NS32532)
+struct option *cpureg = cpureg_532;
+struct option *mmureg = mmureg_532;
+#else
+struct option *cpureg = cpureg_032;
+struct option *mmureg = mmureg_032;
+#endif
+
+
+const pseudo_typeS md_pseudo_table[]={ /* so far empty */
+ { 0, 0, 0 }
+};
+
+#define IND(x,y) (((x)<<2)+(y))
+
+/* those are index's to relax groups in md_relax_table
+ ie it must be multiplied by 4 to point at a group start. Viz IND(x,y)
+ Se function relax_segment in write.c for more info */
+
+#define BRANCH 1
+#define PCREL 2
+
+/* those are index's to entries in a relax group */
+
+#define BYTE 0
+#define WORD 1
+#define DOUBLE 2
+#define UNDEF 3
+/* Those limits are calculated from the displacement start in memory.
+ The ns32k uses the begining of the instruction as displacement base.
+ This type of displacements could be handled here by moving the limit window
+ up or down. I choose to use an internal displacement base-adjust as there
+ are other routines that must consider this. Also, as we have two various
+ offset-adjusts in the ns32k (acb versus br/brs/jsr/bcond), two set of limits
+ would have had to be used.
+ Now we dont have to think about that. */
+
+
+const relax_typeS md_relax_table[] = {
+ { 1, 1, 0, 0 },
+ { 1, 1, 0, 0 },
+ { 1, 1, 0, 0 },
+ { 1, 1, 0, 0 },
+
+ { (63), (-64), 1, IND(BRANCH,WORD) },
+ { (8192), (-8192), 2, IND(BRANCH,DOUBLE) },
+ { 0, 0, 4, 0 },
+ { 1, 1, 0, 0 }
+};
+
+/* Array used to test if mode contains displacements.
+ Value is true if mode contains displacement. */
+
+char disp_test[] = { 0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,
+ 1,1,1,0,0,1,1,0,
+ 1,1,1,1,1,1,1,1 };
+
+/* Array used to calculate max size of displacements */
+
+char disp_size[] = { 4,1,2,0,4 };
+
+
+#if __STDC__ == 1
+
+static segT evaluate_expr(expressionS *resultP, char *ptr);
+static void md_number_to_disp(char *buf, long val, int n);
+static void md_number_to_imm(char *buf, long val, int n);
+
+#else /* not __STDC__ */
+
+static segT evaluate_expr();
+static void md_number_to_disp();
+static void md_number_to_imm();
+
+#endif /* not __STDC__ */
+
+/* Parses a general operand into an addressingmode struct
+
+ in: pointer at operand in ascii form
+ pointer at addr_mode struct for result
+ the level of recursion. (always 0 or 1)
+
+ out: data in addr_mode struct
+ */
+int addr_mode(operand,addr_modeP,recursive_level)
+char *operand;
+register addr_modeS *addr_modeP;
+int recursive_level;
+{
+ register char *str;
+ register int i;
+ register int strl;
+ register int mode;
+ int j;
+ mode = DEFAULT; /* default */
+ addr_modeP->scaled_mode=0; /* why not */
+ addr_modeP->scaled_reg=0; /* if 0, not scaled index */
+ addr_modeP->float_flag=0;
+ addr_modeP->am_size=0;
+ addr_modeP->im_disp=0;
+ addr_modeP->pcrel=0; /* not set in this function */
+ addr_modeP->disp_suffix[0]=0;
+ addr_modeP->disp_suffix[1]=0;
+ addr_modeP->disp[0]=NULL;
+ addr_modeP->disp[1]=NULL;
+ str=operand;
+ if (str[0] == 0) {return (0);} /* we don't want this */
+ strl=strlen(str);
+ switch (str[0]) {
+ /* the following three case statements controls the mode-chars
+ this is the place to ed if you want to change them */
+#ifdef ABSOLUTE_PREFIX
+ case ABSOLUTE_PREFIX:
+ if (str[strl-1] == ']') break;
+ addr_modeP->mode=21; /* absolute */
+ addr_modeP->disp[0]=str+1;
+ return (-1);
+#endif
+#ifdef IMMEDIATE_PREFIX
+ case IMMEDIATE_PREFIX:
+ if (str[strl-1] == ']') break;
+ addr_modeP->mode=20; /* immediate */
+ addr_modeP->disp[0]=str+1;
+ return (-1);
+#endif
+ case '.':
+ if (str[strl-1] != ']') {
+ switch (str[1]) {
+ case'-':case'+':
+ if (str[2] != '\000') {
+ addr_modeP->mode=27; /* pc-relativ */
+ addr_modeP->disp[0]=str+2;
+ return (-1);
+ }
+ default:
+ as_warn("Invalid syntax in PC-relative addressing mode");
+ return(0);
+ }
+ }
+ break;
+ case'e':
+ if (str[strl-1] != ']') {
+ if ((!strncmp(str,"ext(",4)) && strl>7) { /* external */
+ addr_modeP->disp[0]=str+4;
+ i=0;
+ j=2;
+ do { /* disp[0]'s termination point */
+ j+=1;
+ if (str[j] == '(') i++;
+ if (str[j] == ')') i--;
+ } while (j < strl && i != 0);
+ if (i != 0 || !(str[j+1] == '-' || str[j+1] == '+') ) {
+ as_warn("Invalid syntax in External addressing mode");
+ return(0);
+ }
+ str[j]='\000'; /* null terminate disp[0] */
+ addr_modeP->disp[1]=str+j+2;
+ addr_modeP->mode=22;
+ return (-1);
+ }
+ }
+ break;
+ default:;
+ }
+ strl=strlen(str);
+ switch (strl) {
+ case 2:
+ switch (str[0]) {
+ case'f':addr_modeP->float_flag=1;
+ case'r':
+ if (str[1] >= '0' && str[1] < '8') {
+ addr_modeP->mode=str[1]-'0';
+ return (-1);
+ }
+ }
+ case 3:
+ if (!strncmp(str,"tos",3)) {
+ addr_modeP->mode=23; /* TopOfStack */
+ return (-1);
+ }
+ default:;
+ }
+ if (strl>4) {
+ if (str[strl - 1] == ')') {
+ if (str[strl - 2] == ')') {
+ if (!strncmp(&str[strl-5],"(fp",3)) {
+ mode=16; /* Memory Relative */
+ }
+ if (!strncmp(&str[strl-5],"(sp",3)) {
+ mode=17;
+ }
+ if (!strncmp(&str[strl-5],"(sb",3)) {
+ mode=18;
+ }
+ if (mode != DEFAULT) { /* memory relative */
+ addr_modeP->mode=mode;
+ j=strl-5; /* temp for end of disp[0] */
+ i=0;
+ do {
+ strl-=1;
+ if (str[strl] == ')') i++;
+ if (str[strl] == '(') i--;
+ } while (strl>-1 && i != 0);
+ if (i != 0) {
+ as_warn("Invalid syntax in Memory Relative addressing mode");
+ return(0);
+ }
+ addr_modeP->disp[1]=str;
+ addr_modeP->disp[0]=str+strl+1;
+ str[j]='\000'; /* null terminate disp[0] */
+ str[strl]='\000'; /* null terminate disp[1] */
+ return (-1);
+ }
+ }
+ switch (str[strl-3]) {
+ case'r':case'R':
+ if (str[strl - 2] >= '0' && str[strl - 2] < '8' && str[strl - 4] == '(') {
+ addr_modeP->mode=str[strl-2]-'0'+8;
+ addr_modeP->disp[0]=str;
+ str[strl-4]=0;
+ return (-1); /* reg rel */
+ }
+ default:
+ if (!strncmp(&str[strl-4],"(fp",3)) {
+ mode=24;
+ }
+ if (!strncmp(&str[strl-4],"(sp",3)) {
+ mode=25;
+ }
+ if (!strncmp(&str[strl-4],"(sb",3)) {
+ mode=26;
+ }
+ if (!strncmp(&str[strl-4],"(pc",3)) {
+ mode=27;
+ }
+ if (mode != DEFAULT) {
+ addr_modeP->mode=mode;
+ addr_modeP->disp[0]=str;
+ str[strl-4]='\0';
+ return (-1); /* memory space */
+ }
+ }
+ }
+ /* no trailing ')' do we have a ']' ? */
+ if (str[strl - 1] == ']') {
+ switch (str[strl-2]) {
+ case'b':mode=28;break;
+ case'w':mode=29;break;
+ case'd':mode=30;break;
+ case'q':mode=31;break;
+ default:;
+ as_warn("Invalid scaled-indexed mode, use (b,w,d,q)");
+ if (str[strl - 3] != ':' || str[strl - 6] != '[' ||
+ str[strl - 5] == 'r' || str[strl - 4] < '0' || str[strl - 4] > '7') {
+ as_warn("Syntax in scaled-indexed mode, use [Rn:m] where n=[0..7] m={b,w,d,q}");
+ }
+ } /* scaled index */
+ {
+ if (recursive_level>0) {
+ as_warn("Scaled-indexed addressing mode combined with scaled-index");
+ return(0);
+ }
+ addr_modeP->am_size+=1; /* scaled index byte */
+ j=str[strl-4]-'0'; /* store temporary */
+ str[strl-6]='\000'; /* nullterminate for recursive call */
+ i=addr_mode(str,addr_modeP,1);
+ if (!i || addr_modeP->mode == 20) {
+ as_warn("Invalid or illegal addressing mode combined with scaled-index");
+ return(0);
+ }
+ addr_modeP->scaled_mode=addr_modeP->mode; /* store the inferior mode */
+ addr_modeP->mode=mode;
+ addr_modeP->scaled_reg=j+1;
+ return (-1);
+ }
+ }
+ }
+ addr_modeP->mode = DEFAULT; /* default to whatever */
+ addr_modeP->disp[0]=str;
+ return (-1);
+}
+
+/* ptr points at string
+ addr_modeP points at struct with result
+ This routine calls addr_mode to determine the general addr.mode of
+ the operand. When this is ready it parses the displacements for size
+ specifying suffixes and determines size of immediate mode via ns32k-opcode.
+ Also builds index bytes if needed.
+ */
+int get_addr_mode(ptr,addr_modeP)
+char *ptr;
+addr_modeS *addr_modeP;
+{
+ int tmp;
+ addr_mode(ptr,addr_modeP,0);
+ if (addr_modeP->mode == DEFAULT || addr_modeP->scaled_mode == -1) {
+ /* resolve ambigious operands, this shouldn't
+ be necessary if one uses standard NSC operand
+ syntax. But the sequent compiler doesn't!!!
+ This finds a proper addressinging mode if it
+ is implicitly stated. See ns32k-opcode.h */
+ (void)evaluate_expr(&exprP,ptr); /* this call takes time Sigh! */
+ if (addr_modeP->mode == DEFAULT) {
+ if (exprP.X_add_symbol || exprP.X_subtract_symbol) {
+ addr_modeP->mode=desc->default_model; /* we have a label */
+ } else {
+ addr_modeP->mode=desc->default_modec; /* we have a constant */
+ }
+ } else {
+ if (exprP.X_add_symbol || exprP.X_subtract_symbol) {
+ addr_modeP->scaled_mode=desc->default_model;
+ } else {
+ addr_modeP->scaled_mode=desc->default_modec;
+ }
+ }
+ /* must put this mess down in addr_mode to handle the scaled case better */
+ }
+ /* It appears as the sequent compiler wants an absolute when we have a
+ label without @. Constants becomes immediates besides the addr case.
+ Think it does so with local labels too, not optimum, pcrel is better.
+ When I have time I will make gas check this and select pcrel when possible
+ Actually that is trivial.
+ */
+ if (tmp=addr_modeP->scaled_reg) { /* build indexbyte */
+ tmp--; /* remember regnumber comes incremented for flagpurpose */
+ tmp|=addr_modeP->scaled_mode<<3;
+ addr_modeP->index_byte=(char)tmp;
+ addr_modeP->am_size+=1;
+ }
+ if (disp_test[addr_modeP->mode]) { /* there was a displacement, probe for length specifying suffix*/
+ {
+ register char c;
+ register char suffix;
+ register char suffix_sub;
+ register int i;
+ register char *toP;
+ register char *fromP;
+
+ addr_modeP->pcrel=0;
+ if (disp_test[addr_modeP->mode]) { /* there is a displacement */
+ if (addr_modeP->mode == 27 || addr_modeP->scaled_mode == 27) { /* do we have pcrel. mode */
+ addr_modeP->pcrel=1;
+ }
+ addr_modeP->im_disp=1;
+ for (i=0;i<2;i++) {
+ suffix_sub=suffix=0;
+ if (toP=addr_modeP->disp[i]) { /* suffix of expression, the largest size rules */
+ fromP=toP;
+ while (c = *fromP++) {
+ *toP++=c;
+ if (c == ':') {
+ switch (*fromP) {
+ case '\0':
+ as_warn("Premature end of suffix--Defaulting to d");
+ suffix=4;
+ continue;
+ case 'b':suffix_sub=1;break;
+ case 'w':suffix_sub=2;break;
+ case 'd':suffix_sub=4;break;
+ default:
+ as_warn("Bad suffix after ':' use {b|w|d} Defaulting to d");
+ suffix=4;
+ }
+ fromP++;
+ toP--; /* So we write over the ':' */
+ if (suffix<suffix_sub) suffix=suffix_sub;
+ }
+ }
+ *toP='\0'; /* terminate properly */
+ addr_modeP->disp_suffix[i]=suffix;
+ addr_modeP->am_size+=suffix ? suffix : 4;
+ }
+ }
+ }
+ }
+ } else {
+ if (addr_modeP->mode == 20) { /* look in ns32k_opcode for size */
+ addr_modeP->disp_suffix[0]=addr_modeP->am_size=desc->im_size;
+ addr_modeP->im_disp=0;
+ }
+ }
+ return addr_modeP->mode;
+}
+
+
+/* read an optionlist */
+void optlist(str,optionP,default_map)
+char *str; /* the string to extract options from */
+struct option *optionP; /* how to search the string */
+unsigned long *default_map; /* default pattern and output */
+{
+ register int i,j,k,strlen1,strlen2;
+ register char *patternP,*strP;
+ strlen1=strlen(str);
+ if (strlen1<1) {
+ as_fatal("Very short instr to option, ie you can't do it on a NULLstr");
+ }
+ for (i = 0; optionP[i].pattern != 0; i++) {
+ strlen2=strlen(optionP[i].pattern);
+ for (j=0;j<strlen1;j++) {
+ patternP=optionP[i].pattern;
+ strP = &str[j];
+ for (k=0;k<strlen2;k++) {
+ if (*(strP++) != *(patternP++)) break;
+ }
+ if (k == strlen2) { /* match */
+ *default_map|=optionP[i].or;
+ *default_map&=optionP[i].and;
+ }
+ }
+ }
+}
+/* search struct for symbols
+ This function is used to get the short integer form of reg names
+ in the instructions lmr, smr, lpr, spr
+ return true if str is found in list */
+
+int list_search(str,optionP,default_map)
+char *str; /* the string to match */
+struct option *optionP; /* list to search */
+unsigned long *default_map; /* default pattern and output */
+{
+ register int i;
+ for (i = 0; optionP[i].pattern != 0; i++) {
+ if (!strncmp(optionP[i].pattern,str,20)) { /* use strncmp to be safe */
+ *default_map|=optionP[i].or;
+ *default_map&=optionP[i].and;
+ return -1;
+ }
+ }
+ as_warn("No such entry in list. (cpu/mmu register)");
+ return 0;
+}
+static segT evaluate_expr(resultP,ptr)
+expressionS *resultP;
+char *ptr;
+{
+ register char *tmp_line;
+ register segT segment;
+ tmp_line = input_line_pointer;
+ input_line_pointer = ptr;
+ segment = expression(&exprP);
+ input_line_pointer = tmp_line;
+ return(segment);
+}
+
+/* Convert operands to iif-format and adds bitfields to the opcode.
+ Operands are parsed in such an order that the opcode is updated from
+ its most significant bit, that is when the operand need to alter the
+ opcode.
+ Be carefull not to put to objects in the same iif-slot.
+ */
+
+void encode_operand(argc,argv,operandsP,suffixP,im_size,opcode_bit_ptr)
+int argc;
+char **argv;
+char *operandsP;
+char *suffixP;
+char im_size;
+char opcode_bit_ptr;
+{
+ register int i,j;
+ int pcrel,tmp,b,loop,pcrel_adjust;
+ for (loop=0;loop<argc;loop++) {
+ i=operandsP[loop<<1]-'1'; /* what operand are we supposed to work on */
+ if (i>3) as_fatal("Internal consistency error. check ns32k-opcode.h");
+ pcrel=0;
+ pcrel_adjust=0;
+ tmp=0;
+ switch (operandsP[(loop<<1)+1]) {
+ case 'f': /* operand of sfsr turns out to be a nasty specialcase */
+ opcode_bit_ptr-=5;
+ case 'F': /* 32 bit float general form */
+ case 'L': /* 64 bit float */
+ case 'Q': /* quad-word */
+ case 'B': /* byte */
+ case 'W': /* word */
+ case 'D': /* double-word */
+ case 'A': /* double-word gen-address-form ie no regs allowed */
+ get_addr_mode(argv[i],&addr_modeP);
+ iif.instr_size+=addr_modeP.am_size;
+ if (opcode_bit_ptr == desc->opcode_size) b = 4; else b = 6;
+ for (j=b;j<(b+2);j++) {
+ if (addr_modeP.disp[j-b]) {
+ IIF(j,
+ 2,
+ addr_modeP.disp_suffix[j-b],
+ (unsigned long)addr_modeP.disp[j-b],
+ 0,
+ addr_modeP.pcrel,
+ iif.instr_size-addr_modeP.am_size, /* this aint used (now) */
+ addr_modeP.im_disp,
+ IND(BRANCH,BYTE),
+ NULL,
+ addr_modeP.scaled_reg ? addr_modeP.scaled_mode:addr_modeP.mode,
+ 0);
+ }
+ }
+ opcode_bit_ptr-=5;
+ iif.iifP[1].object|=((long)addr_modeP.mode)<<opcode_bit_ptr;
+ if (addr_modeP.scaled_reg) {
+ j=b/2;
+ IIF(j,1,1, (unsigned long)addr_modeP.index_byte,0,0,0,0,0, NULL,-1,0);
+ }
+ break;
+ case 'b': /* multiple instruction disp */
+ freeptr++; /* OVE:this is an useful hack */
+ tmp = (int) sprintf(freeptr,
+ "((%s-1)*%d)\000",
+ argv[i], desc->im_size);
+ argv[i]=freeptr;
+ freeptr=(char*)tmp;
+ pcrel-=1; /* make pcrel 0 inspite of what case 'p': wants */
+ /* fall thru */
+ case 'p': /* displacement - pc relative addressing */
+ pcrel+=1;
+ /* fall thru */
+ case 'd': /* displacement */
+ iif.instr_size+=suffixP[i] ? suffixP[i] : 4;
+ IIF(12, 2, suffixP[i], (unsigned long)argv[i], 0,
+ pcrel, pcrel_adjust, 1, IND(BRANCH,BYTE), NULL,-1,0);
+ break;
+ case 'H': /* sequent-hack: the linker wants a bit set when bsr */
+ pcrel=1;
+ iif.instr_size+=suffixP[i] ? suffixP[i] : 4;
+ IIF(12, 2, suffixP[i], (unsigned long)argv[i], 0,
+ pcrel, pcrel_adjust, 1, IND(BRANCH,BYTE), NULL,-1,1);break;
+ case 'q': /* quick */
+ opcode_bit_ptr-=4;
+ IIF(11,2,42,(unsigned long)argv[i],0,0,0,0,0,
+ bit_fix_new(4,opcode_bit_ptr,-8,7,0,1,0),-1,0);
+ break;
+ case 'r': /* register number (3 bits) */
+ list_search(argv[i],opt6,&tmp);
+ opcode_bit_ptr-=3;
+ iif.iifP[1].object|=tmp<<opcode_bit_ptr;
+ break;
+ case 'O': /* setcfg instruction optionslist */
+ optlist(argv[i],opt3,&tmp);
+ opcode_bit_ptr-=4;
+ iif.iifP[1].object|=tmp<<15;
+ break;
+ case 'C': /* cinv instruction optionslist */
+ optlist(argv[i],opt4,&tmp);
+ opcode_bit_ptr-=4;
+ iif.iifP[1].object|=tmp<<15;/*insert the regtype in opcode */
+ break;
+ case 'S': /* stringinstruction optionslist */
+ optlist(argv[i],opt5,&tmp);
+ opcode_bit_ptr-=4;
+ iif.iifP[1].object|=tmp<<15;
+ break;
+ case 'u':case 'U': /* registerlist */
+ IIF(10,1,1,0,0,0,0,0,0,NULL,-1,0);
+ switch (operandsP[(i<<1)+1]) {
+ case 'u': /* restore, exit */
+ optlist(argv[i],opt1,&iif.iifP[10].object);
+ break;
+ case 'U': /* save,enter */
+ optlist(argv[i],opt2,&iif.iifP[10].object);
+ break;
+ }
+ iif.instr_size+=1;
+ break;
+ case 'M': /* mmu register */
+ list_search(argv[i],mmureg,&tmp);
+ opcode_bit_ptr-=4;
+ iif.iifP[1].object|=tmp<<opcode_bit_ptr;
+ break;
+ case 'P': /* cpu register */
+ list_search(argv[i],cpureg,&tmp);
+ opcode_bit_ptr-=4;
+ iif.iifP[1].object|=tmp<<opcode_bit_ptr;
+ break;
+ case 'g': /* inss exts */
+ iif.instr_size+=1; /* 1 byte is allocated after the opcode */
+ IIF(10,2,1,
+ (unsigned long)argv[i], /* i always 2 here */
+ 0,0,0,0,0,
+ bit_fix_new(3,5,0,7,0,0,0), /* a bit_fix is targeted to the byte */
+ -1,0);
+ case 'G':
+ IIF(11,2,42,
+ (unsigned long)argv[i], /* i always 3 here */
+ 0,0,0,0,0,
+ bit_fix_new(5,0,1,32,-1,0,-1),-1,0);
+ break;
+ case 'i':
+ iif.instr_size+=1;
+ b=2+i; /* put the extension byte after opcode */
+ IIF(b,2,1,0,0,0,0,0,0,0,-1,0);
+ default:
+ as_fatal("Bad opcode-table-option, check in file ns32k-opcode.h");
+ }
+ }
+}
+
+/* in: instruction line
+ out: internal structure of instruction
+ that has been prepared for direct conversion to fragment(s) and
+ fixes in a systematical fashion
+ Return-value = recursive_level
+ */
+/* build iif of one assembly text line */
+int parse(line,recursive_level)
+char *line;
+int recursive_level;
+{
+ register char *lineptr,c,suffix_separator;
+ register int i;
+ int argc,arg_type;
+ char sqr,sep;
+ char suffix[MAX_ARGS],*argv[MAX_ARGS];/* no more than 4 operands */
+ if (recursive_level <= 0) { /* called from md_assemble */
+ for (lineptr=line; (*lineptr) != '\0' && (*lineptr) != ' '; lineptr++);
+ c = *lineptr;
+ *lineptr = '\0';
+ desc = (struct ns32k_opcode*) hash_find(inst_hash_handle,line);
+ if (!desc) {
+ as_fatal("No such opcode");
+ }
+ *lineptr = c;
+ } else {
+ lineptr = line;
+ }
+ argc = 0;
+ if (*desc->operands != NULL) {
+ if (*lineptr++ != '\0') {
+ sqr='[';
+ sep=',';
+ while (*lineptr != '\0') {
+ if (desc->operands[argc << 1]) {
+ suffix[argc] = 0;
+ arg_type =
+ desc->operands[(argc << 1) + 1];
+ switch (arg_type) {
+ case 'd':
+ case 'b':
+ case 'p':
+ case 'H': /* the operand is supposed to be a displacement */
+ /* Hackwarning: do not forget to update the 4 cases above when editing ns32k-opcode.h */
+ suffix_separator = ':';
+ break;
+ default:
+ suffix_separator = '\255'; /* if this char occurs we loose */
+ }
+ suffix[argc] = 0; /* 0 when no ':' is encountered */
+ argv[argc] = freeptr;
+ *freeptr = '\0';
+ while ((c = *lineptr) != '\0' && c != sep) {
+ if (c == sqr) {
+ if (sqr == '[') {
+ sqr = ']';
+ sep = '\0';
+ } else {
+ sqr = '[';
+ sep = ',';
+ }
+ }
+ if (c == suffix_separator) { /* ':' - label/suffix separator */
+ switch (lineptr[1]) {
+ case 'b': suffix[argc] = 1; break;
+ case 'w': suffix[argc] = 2; break;
+ case 'd': suffix[argc] = 4; break;
+ default: as_warn("Bad suffix, defaulting to d");
+ suffix[argc] = 4;
+ if (lineptr[1] == '\0' || lineptr[1] == sep) {
+ lineptr += 1;
+ continue;
+ }
+ }
+ lineptr += 2;
+ continue;
+ }
+ *freeptr++ = c;
+ lineptr++;
+ }
+ *freeptr++ = '\0';
+ argc += 1;
+ if (*lineptr == '\0') continue;
+ lineptr += 1;
+ } else {
+ as_fatal("Too many operands passed to instruction");
+ }
+ }
+ }
+ }
+ if (argc != strlen(desc->operands) / 2) {
+ if (strlen(desc->default_args) != 0) { /* we can apply default, dont goof */
+ if (parse(desc->default_args,1) != 1) { /* check error in default */
+ as_fatal("Wrong numbers of operands in default, check ns32k-opcodes.h");
+ }
+ } else {
+ as_fatal("Wrong number of operands");
+ }
+
+ }
+ for (i = 0; i < IIF_ENTRIES; i++) {
+ iif.iifP[i].type = 0; /* mark all entries as void*/
+ }
+
+ /* build opcode iif-entry */
+ iif.instr_size = desc->opcode_size / 8;
+ IIF(1,1,iif.instr_size,desc->opcode_seed,0,0,0,0,0,0,-1,0);
+
+ /* this call encodes operands to iif format */
+ if (argc) {
+ encode_operand(argc,
+ argv,
+ &desc->operands[0],
+ &suffix[0],
+ desc->im_size,
+ desc->opcode_size);
+ }
+ return(recursive_level);
+}
+
+
+/* Convert iif to fragments.
+ From this point we start to dribble with functions in other files than
+ this one.(Except hash.c) So, if it's possible to make an iif for an other
+ CPU, you don't need to know what frags, relax, obstacks, etc is in order
+ to port this assembler. You only need to know if it's possible to reduce
+ your cpu-instruction to iif-format (takes some work) and adopt the other
+ md_? parts according to given instructions
+ Note that iif was invented for the clean ns32k`s architecure.
+ */
+void convert_iif() {
+ int i;
+ int j;
+ fragS *inst_frag;
+ char *inst_offset;
+ char **inst_opcode;
+ char *memP;
+ segT segment;
+ int l;
+ int k;
+ int rem_size; /* count the remaining bytes of instruction */
+ char type;
+ char size = 0;
+ int size_so_far = 0; /* used to calculate pcrel_adjust */
+ int pcrel_symbols=0; /* kludge by jkp@hut.fi to make
+ movd _foo(pc),_bar(pc) work.
+ It should be done with two frags
+ for one insn, but I don't understand
+ enough to make it work */
+
+ rem_size=iif.instr_size;
+ memP=frag_more(iif.instr_size); /* make sure we have enough bytes for instruction */
+ inst_opcode=memP;
+ inst_offset=(char*)(memP-frag_now->fr_literal);
+ inst_frag=frag_now;
+ for (i=0;i<IIF_ENTRIES;i++) { /* jkp kludge alert */
+ if (iif.iifP[i].type && iif.iifP[i].size == 0 &&
+ iif.iifP[i].pcrel) {
+ evaluate_expr(&exprP,(char*)iif.iifP[i].object);
+ if (exprP.X_add_symbol || exprP.X_subtract_symbol)
+ pcrel_symbols++;
+ }
+ }
+ for (i=0;i<IIF_ENTRIES;i++) {
+ if (type=iif.iifP[i].type) { /* the object exist, so handle it */
+ switch (size=iif.iifP[i].size) {
+ case 42: size=0; /* it's a bitfix that operates on an existing object*/
+ if (iif.iifP[i].bit_fixP->fx_bit_base) { /* expand fx_bit_base to point at opcode */
+ iif.iifP[i].bit_fixP->fx_bit_base=(long)inst_opcode;
+ }
+ case 8: /* bignum or doublefloat */
+ memset(memP, '\0', 8);
+ case 1:case 2:case 3:case 4:/* the final size in objectmemory is known */
+ j=(unsigned long)iif.iifP[i].bit_fixP;
+ switch (type) {
+ case 1: /* the object is pure binary */
+ if (j || iif.iifP[i].pcrel) {
+ fix_new_ns32k(frag_now,
+ (long)(memP-frag_now->fr_literal),
+ size,
+ 0,
+ 0,
+ iif.iifP[i].object,
+ iif.iifP[i].pcrel,
+ (char)size_so_far, /*iif.iifP[i].pcrel_adjust,*/
+ iif.iifP[i].im_disp,
+ j,
+ iif.iifP[i].bsr); /* sequent hack */
+ } else { /* good, just put them bytes out */
+ switch (iif.iifP[i].im_disp) {
+ case 0:
+ md_number_to_chars(memP,iif.iifP[i].object,size);break;
+ case 1:
+ md_number_to_disp(memP,iif.iifP[i].object,size);break;
+ default: as_fatal("iif convert internal pcrel/binary");
+ }
+ }
+ memP+=size;
+ rem_size-=size;
+ break;
+ case 2: /* the object is a pointer at an expression, so unpack
+ it, note that bignums may result from the expression
+ */
+ if ((segment = evaluate_expr(&exprP, (char*)iif.iifP[i].object)) == SEG_BIG || size == 8) {
+ if ((k=exprP.X_add_number)>0) { /* we have a bignum ie a quad */
+ /* this can only happens in a long suffixed instruction */
+ memset(memP, '\0', size); /* size normally is 8 */
+ if (k*2>size) as_warn("Bignum too big for long");
+ if (k == 3) memP += 2;
+ for (l=0;k>0;k--,l+=2) {
+ md_number_to_chars(memP+l,generic_bignum[l>>1],sizeof(LITTLENUM_TYPE));
+ }
+ } else { /* flonum */
+ LITTLENUM_TYPE words[4];
+
+ switch (size) {
+ case 4:
+ gen_to_words(words,2,8);
+ md_number_to_imm(memP ,(long)words[0],sizeof(LITTLENUM_TYPE));
+ md_number_to_imm(memP+sizeof(LITTLENUM_TYPE),(long)words[1],sizeof(LITTLENUM_TYPE));
+ break;
+ case 8:
+ gen_to_words(words,4,11);
+ md_number_to_imm(memP ,(long)words[0],sizeof(LITTLENUM_TYPE));
+ md_number_to_imm(memP+sizeof(LITTLENUM_TYPE) ,(long)words[1],sizeof(LITTLENUM_TYPE));
+ md_number_to_imm(memP+2*sizeof(LITTLENUM_TYPE),(long)words[2],sizeof(LITTLENUM_TYPE));
+ md_number_to_imm(memP+3*sizeof(LITTLENUM_TYPE),(long)words[3],sizeof(LITTLENUM_TYPE));
+ break;
+ }
+ }
+ memP+=size;
+ rem_size-=size;
+ break;
+ }
+ if (j ||
+ exprP.X_add_symbol ||
+ exprP.X_subtract_symbol ||
+ iif.iifP[i].pcrel) { /* fixit */
+ /* the expression was undefined due to an undefined label */
+ /* create a fix so we can fix the object later */
+ exprP.X_add_number+=iif.iifP[i].object_adjust;
+ fix_new_ns32k(frag_now,
+ (long)(memP-frag_now->fr_literal),
+ size,
+ exprP.X_add_symbol,
+ exprP.X_subtract_symbol,
+ exprP.X_add_number,
+ iif.iifP[i].pcrel,
+ (char)size_so_far, /*iif.iifP[i].pcrel_adjust,*/
+ iif.iifP[i].im_disp,
+ j,
+ iif.iifP[i].bsr); /* sequent hack */
+
+ } else { /* good, just put them bytes out */
+ switch (iif.iifP[i].im_disp) {
+ case 0:
+ md_number_to_imm(memP,exprP.X_add_number,size);break;
+ case 1:
+ md_number_to_disp(memP,exprP.X_add_number,size);break;
+ default: as_fatal("iif convert internal pcrel/pointer");
+ }
+ }
+ memP+=size;
+ rem_size-=size;
+ break;
+ default: as_fatal("Internal logic error in iif.iifP[n].type");
+ }
+ break;
+ case 0: /* To bad, the object may be undefined as far as its final
+ nsize in object memory is concerned. The size of the object
+ in objectmemory is not explicitly given.
+ If the object is defined its length can be determined and
+ a fix can replace the frag.
+ */
+ {
+ int temp;
+ segment = evaluate_expr(&exprP, (char*)iif.iifP[i].object);
+ if (((exprP.X_add_symbol || exprP.X_subtract_symbol) &&
+ !iif.iifP[i].pcrel) || pcrel_symbols >= 2 /*jkp*/) { /* OVE: hack, clamp to 4 bytes */
+ size=4; /* we dont wan't to frag this, use 4 so it reaches */
+ fix_new_ns32k(frag_now,
+ (long)(memP-frag_now->fr_literal),
+ size,
+ exprP.X_add_symbol,
+ exprP.X_subtract_symbol,
+ exprP.X_add_number,
+ pcrel_symbols >= 2 ? iif.iifP[i].pcrel : 0, /*jkp*//* never iif.iifP[i].pcrel, */
+ (char)size_so_far, /*iif.iifP[i].pcrel_adjust,*/
+ 1, /* always iif.iifP[i].im_disp, */
+ 0,0);
+ memP+=size;
+ rem_size-=4;
+ break; /* exit this absolute hack */
+ }
+
+ if (exprP.X_add_symbol || exprP.X_subtract_symbol) { /* frag it */
+ if (exprP.X_subtract_symbol) { /* We cant relax this case */
+ as_fatal("Can't relax difference");
+ } else {
+ /* at this stage we must undo some of the effect caused
+ by frag_more, ie we must make sure that frag_var causes
+ frag_new to creat a valid fix-size in the frag it`s closing
+ */
+ temp = -(rem_size-4);
+ obstack_blank_fast(&frags,temp);
+ /* we rewind none, some or all of the requested size we
+ requested by the first frag_more for this iif chunk.
+ Note: that we allocate 4 bytes to an object we NOT YET
+ know the size of, thus rem_size-4.
+ */
+ (void) frag_variant(rs_machine_dependent,
+ 4,
+ 0,
+ IND(BRANCH,UNDEF), /* expecting the worst */
+ exprP.X_add_symbol,
+ exprP.X_add_number,
+ (char*)inst_opcode,
+ (char)size_so_far, /*iif.iifP[i].pcrel_adjust);*/
+ iif.iifP[i].bsr); /* sequent linker hack */
+ rem_size -= 4;
+ if (rem_size > 0) {
+ memP = frag_more(rem_size);
+ }
+ }
+ } else {/* Double work, this is done in md_number_to_disp */
+ /* exprP.X_add_number; fixme-soon what was this supposed to be? xoxorich. */
+ if (-64 <= exprP.X_add_number && exprP.X_add_number <= 63) {
+ size = 1;
+ } else {
+ if (-8192 <= exprP.X_add_number && exprP.X_add_number <= 8191) {
+ size = 2;
+
+ /* Dave Taylor <taylor@think.com> says: Note: The reason the lower
+ limit is -0x1f000000 and not -0x20000000 is that, according to
+ Nat'l Semi's data sheet on the ns32532, ``the pattern 11100000
+ for the most significant byte of the displacement is reserved by
+ National for future enhancements''. */
+ } else if (/* -0x40000000 <= exprP.X_add_number &&
+ exprP.X_add_number <= 0x3fffffff */
+ -0x1f000000 <= exprP.X_add_number &&
+ exprP.X_add_number <= 0x1fffffff) {
+ size = 4;
+ } else {
+ as_warn("Displacement too large for :d");
+ size = 4;
+ }
+ }
+ /* rewind the bytes not used */
+ temp = -(4-size);
+ md_number_to_disp(memP,exprP.X_add_number,size);
+ obstack_blank_fast(&frags,temp);
+ memP += size;
+ rem_size -= 4; /* we allocated this amount */
+ }
+ }
+ break;
+ default:
+ as_fatal("Internal logic error in iif.iifP[].type");
+ }
+ size_so_far += size;
+ size = 0;
+ }
+ }
+}
+
+void md_assemble(line)
+char *line;
+{
+ freeptr=freeptr_static;
+ parse(line,0); /* explode line to more fix form in iif */
+ convert_iif(); /* convert iif to frags, fix's etc */
+#ifdef SHOW_NUM
+ printf(" \t\t\t%s\n",line);
+#endif
+}
+
+
+void md_begin() {
+ /* build a hashtable of the instructions */
+ register const struct ns32k_opcode *ptr;
+ register char *stat;
+ inst_hash_handle=hash_new();
+ for (ptr=ns32k_opcodes;ptr<endop;ptr++) {
+ if (*(stat=hash_insert(inst_hash_handle,ptr->name,(char*)ptr))) {
+ as_fatal("Can't hash %s: %s", ptr->name,stat); /*fatal*/
+ exit(0);
+ }
+ }
+ freeptr_static=(char*)malloc(PRIVATE_SIZE); /* some private space please! */
+}
+
+
+void
+ md_end() {
+ free(freeptr_static);
+ }
+
+/* Must be equal to MAX_PRECISON in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+/* Turn the string pointed to by litP into a floating point constant of type
+ type, and emit the appropriate bytes. The number of LITTLENUMS emitted
+ is stored in *sizeP. An error message is returned, or NULL on OK.
+ */
+char *
+ md_atof(type,litP,sizeP)
+char type;
+char *litP;
+int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+
+ switch (type) {
+ case 'f':
+ prec = 2;
+ break;
+
+ case 'd':
+ prec = 4;
+ break;
+ default:
+ *sizeP = 0;
+ return "Bad call to MD_ATOF()";
+ }
+ t = atof_ns32k(input_line_pointer, type, words);
+ if (t)
+ input_line_pointer=t;
+
+ *sizeP = prec * sizeof(LITTLENUM_TYPE);
+ for (wordP = words +prec; prec--;) {
+ md_number_to_chars(litP, (long)(*--wordP), sizeof(LITTLENUM_TYPE));
+ litP+=sizeof(LITTLENUM_TYPE);
+ }
+ return ""; /* Someone should teach Dean about null pointers */
+}
+
+/* Convert number to chars in correct order */
+
+void
+ md_number_to_chars(buf, value, nbytes)
+char *buf;
+long value;
+int nbytes;
+{
+ while (nbytes--) {
+#ifdef SHOW_NUM
+ printf("%x ",value & 0xff);
+#endif
+ *buf++ = value; /* Lint wants & MASK_CHAR. */
+ value >>= BITS_PER_CHAR;
+ }
+} /* md_number_to_chars() */
+
+
+/* This is a variant of md_numbers_to_chars. The reason for its' existence
+ is the fact that ns32k uses Huffman coded displacements. This implies
+ that the bit order is reversed in displacements and that they are prefixed
+ with a size-tag.
+
+ binary: msb->lsb
+ 0xxxxxxx byte
+ 10xxxxxx xxxxxxxx word
+ 11xxxxxx xxxxxxxx xxxxxxxx xxxxxxxx double word
+
+ This must be taken care of and we do it here!
+ */
+static void md_number_to_disp(buf, val, n)
+char *buf;
+long val;
+char n;
+{
+ switch (n) {
+ case 1:
+ if (val < -64 || val > 63)
+ as_warn("Byte displacement out of range. line number not valid");
+ val &= 0x7f;
+#ifdef SHOW_NUM
+ printf("%x ",val & 0xff);
+#endif
+ *buf++ = val;
+ break;
+ case 2:
+ if (val < -8192 || val > 8191)
+ as_warn("Word displacement out of range. line number not valid");
+ val&=0x3fff;
+ val|=0x8000;
+#ifdef SHOW_NUM
+ printf("%x ",val>>8 & 0xff);
+#endif
+ *buf++=(val>>8);
+#ifdef SHOW_NUM
+ printf("%x ",val & 0xff);
+#endif
+ *buf++=val;
+ break;
+ case 4:
+
+ /* Dave Taylor <taylor@think.com> says: Note: The reason the
+ lower limit is -0x1f000000 and not -0x20000000 is that,
+ according to Nat'l Semi's data sheet on the ns32532, ``the
+ pattern 11100000 for the most significant byte of the
+ displacement is reserved by National for future
+ enhancements''. */
+
+ if (val < -0x1f000000 || val >= 0x20000000)
+ as_warn("Double word displacement out of range");
+ val|=0xc0000000;
+#ifdef SHOW_NUM
+ printf("%x ",val>>24 & 0xff);
+#endif
+ *buf++=(val>>24);
+#ifdef SHOW_NUM
+ printf("%x ",val>>16 & 0xff);
+#endif
+ *buf++=(val>>16);
+#ifdef SHOW_NUM
+ printf("%x ",val>>8 & 0xff);
+#endif
+ *buf++=(val>>8);
+#ifdef SHOW_NUM
+ printf("%x ",val & 0xff);
+#endif
+ *buf++=val;
+ break;
+ default:
+ as_fatal("Internal logic error. line %s, file \"%s\"", __LINE__, __FILE__);
+ }
+}
+
+static void md_number_to_imm(buf,val,n)
+char *buf;
+long val;
+char n;
+{
+ switch (n) {
+ case 1:
+#ifdef SHOW_NUM
+ printf("%x ",val & 0xff);
+#endif
+ *buf++=val;
+ break;
+ case 2:
+#ifdef SHOW_NUM
+ printf("%x ",val>>8 & 0xff);
+#endif
+ *buf++=(val>>8);
+#ifdef SHOW_NUM
+ printf("%x ",val & 0xff);
+#endif
+ *buf++=val;
+ break;
+ case 4:
+#ifdef SHOW_NUM
+ printf("%x ",val>>24 & 0xff);
+#endif
+ *buf++=(val>>24);
+#ifdef SHOW_NUM
+ printf("%x ",val>>16 & 0xff);
+#endif
+ *buf++=(val>>16);
+#ifdef SHOW_NUM
+ printf("%x ",val>>8 & 0xff);
+#endif
+ *buf++=(val>>8);
+#ifdef SHOW_NUM
+ printf("%x ",val & 0xff);
+#endif
+ *buf++=val;
+ break;
+ default:
+ as_fatal("Internal logic error. line %s, file \"%s\"", __LINE__, __FILE__);
+ }
+}
+
+/* Translate internal representation of relocation info into target format.
+
+ OVE: on a ns32k the twiddling continues at an even deeper level
+ here we have to distinguish between displacements and immediates.
+
+ The sequent has a bit for this. It also has a bit for relocobjects that
+ points at the target for a bsr (BranchSubRoutine) !?!?!?!
+
+ This md_ri.... is tailored for sequent.
+ */
+
+#ifdef comment
+void
+ md_ri_to_chars(the_bytes, ri)
+char *the_bytes;
+struct reloc_info_generic *ri;
+{
+ if (ri->r_bsr) { ri->r_pcrel = 0; } /* sequent seems to want this */
+ md_number_to_chars(the_bytes, ri->r_address, sizeof(ri->r_address));
+ md_number_to_chars(the_bytes+4, ((long)(ri->r_symbolnum )
+ | (long)(ri->r_pcrel << 24 )
+ | (long)(ri->r_length << 25 )
+ | (long)(ri->r_extern << 27 )
+ | (long)(ri->r_bsr << 28 )
+ | (long)(ri->r_disp << 29 )),
+ 4);
+ /* the first and second md_number_to_chars never overlaps (32bit cpu case) */
+}
+#endif /* comment */
+
+#ifdef OBJ_AOUT
+void tc_aout_fix_to_chars(where, fixP, segment_address_in_file)
+char *where;
+struct fix *fixP;
+relax_addressT segment_address_in_file;
+{
+ /*
+ * In: length of relocation (or of address) in chars: 1, 2 or 4.
+ * Out: GNU LD relocation length code: 0, 1, or 2.
+ */
+
+ static unsigned char nbytes_r_length[] = { 42, 0, 1, 42, 2 };
+ long r_symbolnum;
+
+ know(fixP->fx_addsy != NULL);
+
+ md_number_to_chars(where,
+ fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file,
+ 4);
+
+ r_symbolnum = (S_IS_DEFINED(fixP->fx_addsy)
+ ? S_GET_TYPE(fixP->fx_addsy)
+ : fixP->fx_addsy->sy_number);
+
+ md_number_to_chars(where,
+ ((long)(r_symbolnum)
+ | (long)(fixP->fx_pcrel << 24)
+ | (long)(nbytes_r_length[fixP->fx_size] << 25)
+ | (long)((!S_IS_DEFINED(fixP->fx_addsy)) << 27)
+ | (long)(fixP->fx_bsr << 28)
+ | (long)(fixP->fx_im_disp << 29)),
+ 4);
+
+ return;
+} /* tc_aout_fix_to_chars() */
+#endif /* OBJ_AOUT */
+
+/* fast bitfiddling support */
+/* mask used to zero bitfield before oring in the true field */
+
+static unsigned long l_mask[] = {
+ 0xffffffff, 0xfffffffe, 0xfffffffc, 0xfffffff8,
+ 0xfffffff0, 0xffffffe0, 0xffffffc0, 0xffffff80,
+ 0xffffff00, 0xfffffe00, 0xfffffc00, 0xfffff800,
+ 0xfffff000, 0xffffe000, 0xffffc000, 0xffff8000,
+ 0xffff0000, 0xfffe0000, 0xfffc0000, 0xfff80000,
+ 0xfff00000, 0xffe00000, 0xffc00000, 0xff800000,
+ 0xff000000, 0xfe000000, 0xfc000000, 0xf8000000,
+ 0xf0000000, 0xe0000000, 0xc0000000, 0x80000000,
+};
+static unsigned long r_mask[] = {
+ 0x00000000, 0x00000001, 0x00000003, 0x00000007,
+ 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f,
+ 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff,
+ 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff,
+ 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff,
+ 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff,
+ 0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff,
+ 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
+};
+#define MASK_BITS 31
+/* Insert bitfield described by field_ptr and val at buf
+ This routine is written for modification of the first 4 bytes pointed
+ to by buf, to yield speed.
+ The ifdef stuff is for selection between a ns32k-dependent routine
+ and a general version. (My advice: use the general version!)
+ */
+
+static void
+ md_number_to_field(buf,val,field_ptr)
+register char *buf;
+register long val;
+register bit_fixS *field_ptr;
+{
+ register unsigned long object;
+ register unsigned long mask;
+ /* define ENDIAN on a ns32k machine */
+#ifdef ENDIAN
+ register unsigned long *mem_ptr;
+#else
+ register char *mem_ptr;
+#endif
+ if (field_ptr->fx_bit_min <= val && val <= field_ptr->fx_bit_max) {
+#ifdef ENDIAN
+ if (field_ptr->fx_bit_base) { /* override buf */
+ mem_ptr=(unsigned long*)field_ptr->fx_bit_base;
+ } else {
+ mem_ptr=(unsigned long*)buf;
+ }
+#else
+ if (field_ptr->fx_bit_base) { /* override buf */
+ mem_ptr=(char*)field_ptr->fx_bit_base;
+ } else {
+ mem_ptr=buf;
+ }
+#endif
+ mem_ptr+=field_ptr->fx_bit_base_adj;
+#ifdef ENDIAN /* we have a nice ns32k machine with lowbyte at low-physical mem */
+ object = *mem_ptr; /* get some bytes */
+#else /* OVE Goof! the machine is a m68k or dito */
+ /* That takes more byte fiddling */
+ object=0;
+ object|=mem_ptr[3] & 0xff;
+ object<<=8;
+ object|=mem_ptr[2] & 0xff;
+ object<<=8;
+ object|=mem_ptr[1] & 0xff;
+ object<<=8;
+ object|=mem_ptr[0] & 0xff;
+#endif
+ mask=0;
+ mask|=(r_mask[field_ptr->fx_bit_offset]);
+ mask|=(l_mask[field_ptr->fx_bit_offset+field_ptr->fx_bit_size]);
+ object&=mask;
+ val+=field_ptr->fx_bit_add;
+ object|=((val<<field_ptr->fx_bit_offset) & (mask ^ 0xffffffff));
+#ifdef ENDIAN
+ *mem_ptr=object;
+#else
+ mem_ptr[0]=(char)object;
+ object>>=8;
+ mem_ptr[1]=(char)object;
+ object>>=8;
+ mem_ptr[2]=(char)object;
+ object>>=8;
+ mem_ptr[3]=(char)object;
+#endif
+ } else {
+ as_warn("Bit field out of range");
+ }
+}
+
+/* Apply a fixS (fixup of an instruction or data that we didn't have
+ enough info to complete immediately) to the data in a frag.
+
+ On the ns32k, everything is in a different format, so we have broken
+ out separate functions for each kind of thing we could be fixing.
+ They all get called from here. */
+
+void
+ md_apply_fix(fixP, val)
+fixS *fixP;
+long val;
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ if (fixP->fx_bit_fixP) { /* Bitfields to fix, sigh */
+ md_number_to_field (buf, val, fixP->fx_bit_fixP);
+ } else switch (fixP->fx_im_disp) {
+
+ case 0: /* Immediate field */
+ md_number_to_imm (buf, val, fixP->fx_size);
+ break;
+
+ case 1: /* Displacement field */
+ md_number_to_disp (buf,
+ fixP->fx_pcrel? val + fixP->fx_pcrel_adjust: val,
+ fixP->fx_size);
+ break;
+
+ case 2: /* Pointer in a data object */
+ md_number_to_chars (buf, val, fixP->fx_size);
+ break;
+ }
+}
+
+/* Convert a relaxed displacement to ditto in final output */
+
+void
+ md_convert_frag(headers, fragP)
+object_headers *headers;
+register fragS *fragP;
+{
+ long disp;
+ long ext = 0;
+
+ /* Address in gas core of the place to store the displacement. */
+ register char *buffer_address = fragP->fr_fix + fragP->fr_literal;
+ /* Address in object code of the displacement. */
+ register int object_address = fragP->fr_fix + fragP->fr_address;
+
+ know(fragP->fr_symbol);
+
+ /* The displacement of the address, from current location. */
+ disp = (S_GET_VALUE(fragP->fr_symbol) + fragP->fr_offset) - object_address;
+ disp += fragP->fr_pcrel_adjust;
+
+ switch (fragP->fr_subtype) {
+ case IND(BRANCH,BYTE):
+ ext = 1;
+ break;
+ case IND(BRANCH,WORD):
+ ext = 2;
+ break;
+ case IND(BRANCH,DOUBLE):
+ ext = 4;
+ break;
+ }
+ if (ext) {
+ md_number_to_disp(buffer_address, (long)disp, (int)ext);
+ fragP->fr_fix += ext;
+ }
+} /* md_convert_frag() */
+
+
+
+/* This function returns the estimated size a variable object will occupy,
+ one can say that we tries to guess the size of the objects before we
+ actually know it */
+
+int md_estimate_size_before_relax(fragP, segment)
+register fragS *fragP;
+segT segment;
+{
+ int old_fix;
+ old_fix = fragP->fr_fix;
+ switch (fragP->fr_subtype) {
+ case IND(BRANCH,UNDEF):
+ if (S_GET_SEGMENT(fragP->fr_symbol) == segment) {
+ /* the symbol has been assigned a value */
+ fragP->fr_subtype = IND(BRANCH,BYTE);
+ } else {
+ /* we don't relax symbols defined in an other segment
+ the thing to do is to assume the object will occupy 4 bytes */
+ fix_new_ns32k(fragP,
+ (int)(fragP->fr_fix),
+ 4,
+ fragP->fr_symbol,
+ (symbolS *)0,
+ fragP->fr_offset,
+ 1,
+ fragP->fr_pcrel_adjust,
+ 1,
+ 0,
+ fragP->fr_bsr); /*sequent hack */
+ fragP->fr_fix+=4;
+ /* fragP->fr_opcode[1]=0xff; */
+ frag_wane(fragP);
+ break;
+ }
+ case IND(BRANCH,BYTE):
+ fragP->fr_var+=1;
+ break;
+ default:
+ break;
+ }
+ return fragP->fr_var + fragP->fr_fix - old_fix;
+}
+
+int md_short_jump_size = 3;
+int md_long_jump_size = 5;
+int md_reloc_size = 8; /* Size of relocation record */
+
+void
+ md_create_short_jump(ptr,from_addr,to_addr,frag,to_symbol)
+char *ptr;
+long from_addr,
+ to_addr;
+fragS *frag;
+symbolS *to_symbol;
+{
+ long offset;
+
+ offset = to_addr - from_addr;
+ md_number_to_chars(ptr, (long)0xEA ,1);
+ md_number_to_disp(ptr+1,(long)offset,2);
+}
+
+void
+ md_create_long_jump(ptr,from_addr,to_addr,frag,to_symbol)
+char *ptr;
+long from_addr,
+ to_addr;
+fragS *frag;
+symbolS *to_symbol;
+{
+ long offset;
+
+ offset= to_addr - from_addr;
+ md_number_to_chars(ptr, (long)0xEA, 2);
+ md_number_to_disp(ptr+2,(long)offset,4);
+}
+
+/* JF this is a new function to parse machine-dep options */
+int
+ md_parse_option(argP,cntP,vecP)
+char **argP;
+int *cntP;
+char ***vecP;
+{
+ switch (**argP) {
+ case 'm':
+ (*argP)++;
+
+ if (!strcmp(*argP,"32032")) {
+ cpureg = cpureg_032;
+ mmureg = mmureg_032;
+ } else if (!strcmp(*argP, "32532")) {
+ cpureg = cpureg_532;
+ mmureg = mmureg_532;
+ } else
+ as_warn("Unknown -m option ignored");
+
+ while (**argP)
+ (*argP)++;
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * bit_fix_new()
+ *
+ * Create a bit_fixS in obstack 'notes'.
+ * This struct is used to profile the normal fix. If the bit_fixP is a
+ * valid pointer (not NULL) the bit_fix data will be used to format the fix.
+ */
+bit_fixS *bit_fix_new(size, offset, min, max, add, base_type, base_adj)
+char size; /* Length of bitfield */
+char offset; /* Bit offset to bitfield */
+long base_type; /* 0 or 1, if 1 it's exploded to opcode ptr */
+long base_adj;
+long min; /* Signextended min for bitfield */
+long max; /* Signextended max for bitfield */
+long add; /* Add mask, used for huffman prefix */
+{
+ register bit_fixS * bit_fixP;
+
+ bit_fixP = (bit_fixS *)obstack_alloc(&notes,sizeof(bit_fixS));
+
+ bit_fixP->fx_bit_size = size;
+ bit_fixP->fx_bit_offset = offset;
+ bit_fixP->fx_bit_base = base_type;
+ bit_fixP->fx_bit_base_adj = base_adj;
+ bit_fixP->fx_bit_max = max;
+ bit_fixP->fx_bit_min = min;
+ bit_fixP->fx_bit_add = add;
+
+ return(bit_fixP);
+}
+
+void
+ fix_new_ns32k(frag, where, size, add_symbol, sub_symbol, offset, pcrel,
+ pcrel_adjust, im_disp, bit_fixP, bsr)
+fragS *frag; /* Which frag? */
+int where; /* Where in that frag? */
+int size; /* 1, 2 or 4 usually. */
+symbolS *add_symbol; /* X_add_symbol. */
+symbolS *sub_symbol; /* X_subtract_symbol. */
+long offset; /* X_add_number. */
+int pcrel; /* TRUE if PC-relative relocation. */
+char pcrel_adjust; /* not zero if adjustment of pcrel offset is needed */
+char im_disp; /* true if the value to write is a displacement */
+bit_fixS *bit_fixP; /* pointer at struct of bit_fix's, ignored if NULL */
+char bsr; /* sequent-linker-hack: 1 when relocobject is a bsr */
+
+{
+ fixS *fixP = fix_new(frag, where, size, add_symbol, sub_symbol,
+ offset, pcrel, NO_RELOC);
+
+ fixP->fx_pcrel_adjust = pcrel_adjust;
+ fixP->fx_im_disp = im_disp;
+ fixP->fx_bit_fixP = bit_fixP;
+ fixP->fx_bsr = bsr;
+} /* fix_new_ns32k() */
+
+/* We have no need to default values of symbols. */
+
+symbolS *
+ md_undefined_symbol (name)
+char *name;
+{
+ return 0;
+}
+
+/* Parse an operand that is machine-specific.
+ We just return without modifying the expression if we have nothing
+ to do. */
+
+/* ARGSUSED */
+void
+ md_operand (expressionP)
+expressionS *expressionP;
+{
+}
+
+/* Round up a section size to the appropriate boundary. */
+long
+ md_section_align (segment, size)
+segT segment;
+long size;
+{
+ return size; /* Byte alignment is fine */
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the National warts, they're relative to the address of the offset,
+ with some funny adjustments in some circumstances during blue moons.
+ (??? Is this right? FIXME-SOON) */
+long
+ md_pcrel_from (fixP)
+fixS *fixP;
+{
+ long res;
+ res = fixP->fx_where + fixP->fx_frag->fr_address;
+#ifdef TE_SEQUENT
+ if (fixP->fx_frag->fr_bsr)
+ res += 0x12; /* FOO Kludge alert! */
+#endif
+ return(res);
+}
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * End:
+ */
+
+/* end of tc-ns32k.c */
diff --git a/gnu/usr.bin/as/config/tc-ns32k.h b/gnu/usr.bin/as/config/tc-ns32k.h
new file mode 100644
index 0000000..c3c09db
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-ns32k.h
@@ -0,0 +1,60 @@
+/* tc-ns32k.h -- Opcode table for National Semi 32k processor
+ Copyright (C) 1987, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "bit_fix.h"
+
+#define NO_LISTING
+
+#define tc_aout_pre_write_hook(x) {;} /* not used */
+#define tc_crawl_symbol_chain(a) {;} /* not used */
+#define tc_headers_hook(a) {;} /* not used */
+
+#ifndef DEF_MODEC
+#define DEF_MODEC 20
+#endif
+
+#ifndef DEF_MODEL
+#define DEF_MODEL 20
+#endif
+
+#define MAX_ARGS 4
+#define ARG_LEN 50
+
+#if __STDC__ == 1
+
+void fix_new_ns32k(fragS *frag,
+ int where,
+ int size,
+ struct symbol *add_symbol,
+ struct symbol *sub_symbol,
+ long offset,
+ int pcrel,
+ int pcrel_adjust,
+ int im_disp,
+ bit_fixS *bit_fixP, /* really bit_fixS */
+ int bsr);
+
+#else /* not __STDC__ */
+
+void fix_new_ns32k();
+
+#endif /* not __STDC__ */
+
+
+/* end of tc-ns32k.h */
diff --git a/gnu/usr.bin/as/config/tc-rs6000.c b/gnu/usr.bin/as/config/tc-rs6000.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-rs6000.c
diff --git a/gnu/usr.bin/as/config/tc-rs6000.h b/gnu/usr.bin/as/config/tc-rs6000.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-rs6000.h
diff --git a/gnu/usr.bin/as/config/tc-sparc.c b/gnu/usr.bin/as/config/tc-sparc.c
new file mode 100644
index 0000000..ae50027
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-sparc.c
@@ -0,0 +1,1766 @@
+/* tc-sparc.c -- Assemble for the SPARC
+ Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef lint
+static char rcsid[] = "$Id: tc-sparc.c,v 1.1 1993/11/03 00:54:52 paul Exp $";
+#endif
+
+#define cypress 1234
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "as.h"
+
+/* careful, this file includes data *declarations* */
+#include "opcode/sparc.h"
+
+#define DEBUG_SPARC 1
+void md_begin();
+void md_end();
+void md_number_to_chars();
+void md_assemble();
+char *md_atof();
+void md_convert_frag();
+void md_create_short_jump();
+void md_create_long_jump();
+int md_estimate_size_before_relax();
+void md_ri_to_chars();
+symbolS *md_undefined_symbol();
+static void sparc_ip();
+
+static enum sparc_architecture current_architecture = v6;
+static int architecture_requested = 0;
+static int warn_on_bump = 0;
+
+const relax_typeS md_relax_table[] = {
+ 0 };
+
+/* handle of the OPCODE hash table */
+static struct hash_control *op_hash = NULL;
+
+static void s_seg(), s_proc(), s_data1(), s_reserve(), s_common(), s_empty();
+extern void s_globl(), s_long(), s_short(), s_space(), cons();
+extern void s_align_bytes(), s_ignore();
+
+const pseudo_typeS md_pseudo_table[] = {
+ { "align", s_align_bytes, 0 }, /* Defaulting is invalid (0) */
+ { "empty", s_empty, 0 },
+ { "common", s_common, 0 },
+ { "global", s_globl, 0 },
+ { "half", cons, 2 },
+ { "optim", s_ignore, 0 },
+ { "proc", s_proc, 0 },
+ { "reserve", s_reserve, 0 },
+ { "seg", s_seg, 0 },
+ { "skip", s_space, 0 },
+ { "word", cons, 4 },
+ { NULL, 0, 0 },
+};
+
+const int md_short_jump_size = 4;
+const int md_long_jump_size = 4;
+const int md_reloc_size = 12; /* Size of relocation record */
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful */
+const char comment_chars[] = "!"; /* JF removed '|' from comment_chars */
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that comments started like this one will always
+ work if '/' isn't otherwise defined. */
+const char line_comment_chars[] = "#";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c. Ideally it shouldn't have to know about it at all,
+ but nothing is ideal around here.
+ */
+
+static unsigned char octal[256];
+#define isoctal(c) octal[c]
+ static unsigned char toHex[256];
+
+struct sparc_it {
+ char *error;
+ unsigned long opcode;
+ struct nlist *nlistp;
+ expressionS exp;
+ int pcrel;
+ enum reloc_type reloc;
+} the_insn, set_insn;
+
+#if __STDC__ == 1
+#if DEBUG_SPARC
+static void print_insn(struct sparc_it *insn);
+#endif
+static int getExpression(char *str);
+#else /* not __STDC__ */
+#if DEBUG_SPARC
+static void print_insn();
+#endif
+static int getExpression();
+#endif /* not __STDC__ */
+
+static char *expr_end;
+static int special_case;
+
+/*
+ * Instructions that require wierd handling because they're longer than
+ * 4 bytes.
+ */
+#define SPECIAL_CASE_SET 1
+#define SPECIAL_CASE_FDIV 2
+
+/*
+ * sort of like s_lcomm
+ *
+ */
+static int max_alignment = 15;
+
+static void s_reserve() {
+ char *name;
+ char *p;
+ char c;
+ int align;
+ int size;
+ int temp;
+ symbolS *symbolP;
+
+ name = input_line_pointer;
+ c = get_symbol_end();
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE();
+
+ if (*input_line_pointer != ',') {
+ as_bad("Expected comma after name");
+ ignore_rest_of_line();
+ return;
+ }
+
+ ++input_line_pointer;
+
+ if ((size = get_absolute_expression()) < 0) {
+ as_bad("BSS length (%d.) <0! Ignored.", size);
+ ignore_rest_of_line();
+ return;
+ } /* bad length */
+
+ *p = 0;
+ symbolP = symbol_find_or_make(name);
+ *p = c;
+
+ if (strncmp(input_line_pointer, ",\"bss\"", 6) != 0) {
+ as_bad("bad .reserve segment: `%s'", input_line_pointer);
+ return;
+ } /* if not bss */
+
+ input_line_pointer += 6;
+ SKIP_WHITESPACE();
+
+ if (*input_line_pointer == ',') {
+ ++input_line_pointer;
+
+ SKIP_WHITESPACE();
+ if (*input_line_pointer == '\n') {
+ as_bad("Missing alignment");
+ return;
+ }
+
+ align = get_absolute_expression();
+ if (align > max_alignment){
+ align = max_alignment;
+ as_warn("Alignment too large: %d. assumed.", align);
+ } else if (align < 0) {
+ align = 0;
+ as_warn("Alignment negative. 0 assumed.");
+ }
+#ifdef MANY_SEGMENTS
+#define SEG_BSS SEG_E2
+ record_alignment(SEG_E2, align);
+#else
+ record_alignment(SEG_BSS, align);
+#endif
+
+ /* convert to a power of 2 alignment */
+ for (temp = 0; (align & 1) == 0; align >>= 1, ++temp) ;;
+
+ if (align != 1) {
+ as_bad("Alignment not a power of 2");
+ ignore_rest_of_line();
+ return;
+ } /* not a power of two */
+
+ align = temp;
+
+ /* Align */
+ align = ~((~0) << align); /* Convert to a mask */
+ local_bss_counter = (local_bss_counter + align) & (~align);
+ } /* if has optional alignment */
+
+ if (S_GET_OTHER(symbolP) == 0
+ && S_GET_DESC(symbolP) == 0
+ && ((S_GET_SEGMENT(symbolP) == SEG_BSS
+ && S_GET_VALUE(symbolP) == local_bss_counter)
+ || !S_IS_DEFINED(symbolP))) {
+ S_SET_VALUE(symbolP, local_bss_counter);
+ S_SET_SEGMENT(symbolP, SEG_BSS);
+ symbolP->sy_frag = &bss_address_frag;
+ local_bss_counter += size;
+ } else {
+ as_warn("Ignoring attempt to re-define symbol from %d. to %d.",
+ S_GET_VALUE(symbolP), local_bss_counter);
+ } /* if not redefining */
+
+ demand_empty_rest_of_line();
+ return;
+} /* s_reserve() */
+
+static void s_common() {
+ register char *name;
+ register char c;
+ register char *p;
+ register int temp;
+ register symbolS * symbolP;
+
+ name = input_line_pointer;
+ c = get_symbol_end();
+ /* just after name is now '\0' */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE();
+ if (* input_line_pointer != ',') {
+ as_bad("Expected comma after symbol-name");
+ ignore_rest_of_line();
+ return;
+ }
+ input_line_pointer ++; /* skip ',' */
+ if ((temp = get_absolute_expression ()) < 0) {
+ as_bad(".COMMon length (%d.) <0! Ignored.", temp);
+ ignore_rest_of_line();
+ return;
+ }
+ *p = 0;
+ symbolP = symbol_find_or_make(name);
+ *p = c;
+ if (S_IS_DEFINED(symbolP)) {
+ as_bad("Ignoring attempt to re-define symbol");
+ ignore_rest_of_line();
+ return;
+ }
+ if (S_GET_VALUE(symbolP) != 0) {
+ if (S_GET_VALUE(symbolP) != temp) {
+ as_warn("Length of .comm \"%s\" is already %d. Not changed to %d.",
+ S_GET_NAME(symbolP), S_GET_VALUE(symbolP), temp);
+ }
+ } else {
+ S_SET_VALUE(symbolP, temp);
+ S_SET_EXTERNAL(symbolP);
+ }
+ know(symbolP->sy_frag == &zero_address_frag);
+ if (strncmp(input_line_pointer, ",\"bss\"", 6) != 0
+ && strncmp(input_line_pointer, ",\"data\"", 7) != 0) {
+ p=input_line_pointer;
+ while (*p && *p != '\n')
+ p++;
+ c= *p;
+ *p='\0';
+ as_bad("bad .common segment: `%s'", input_line_pointer);
+ *p=c;
+ return;
+ }
+ input_line_pointer += 6 + (input_line_pointer[2] == 'd'); /* Skip either */
+ demand_empty_rest_of_line();
+ return;
+} /* s_common() */
+
+static void s_seg() {
+
+ if (strncmp(input_line_pointer, "\"text\"", 6) == 0) {
+ input_line_pointer += 6;
+ s_text();
+ return;
+ }
+ if (strncmp(input_line_pointer, "\"data\"", 6) == 0) {
+ input_line_pointer += 6;
+ s_data();
+ return;
+ }
+ if (strncmp(input_line_pointer, "\"data1\"", 7) == 0) {
+ input_line_pointer += 7;
+ s_data1();
+ return;
+ }
+ if (strncmp(input_line_pointer, "\"bss\"", 5) == 0) {
+ input_line_pointer += 5;
+ /* We only support 2 segments -- text and data -- for now, so
+ things in the "bss segment" will have to go into data for now.
+ You can still allocate SEG_BSS stuff with .lcomm or .reserve. */
+ subseg_new(SEG_DATA, 255); /* FIXME-SOMEDAY */
+ return;
+ }
+ as_bad("Unknown segment type");
+ demand_empty_rest_of_line();
+ return;
+} /* s_seg() */
+
+static void s_data1() {
+ subseg_new(SEG_DATA, 1);
+ demand_empty_rest_of_line();
+ return;
+} /* s_data1() */
+
+static void s_proc() {
+ extern char is_end_of_line[];
+
+ while (!is_end_of_line[*input_line_pointer]) {
+ ++input_line_pointer;
+ }
+ ++input_line_pointer;
+ return;
+} /* s_proc() */
+
+/*
+ * GI: This is needed for compatability with Sun's assembler - which
+ * otherwise generates a warning when certain "suspect" instructions
+ * appear in the delay slot of a branch. And more seriously without
+ * this directive in certain cases Sun's assembler will rearrange
+ * code thinking it knows how to alter things when it doesn't.
+ */
+static void
+s_empty()
+{
+ demand_empty_rest_of_line();
+ return;
+} /* s_empty() */
+
+/* This function is called once, at assembler startup time. It should
+ set up all the tables, etc. that the MD part of the assembler will need. */
+void md_begin() {
+ register char *retval = NULL;
+ int lose = 0;
+ register unsigned int i = 0;
+
+ op_hash = hash_new();
+ if (op_hash == NULL)
+ as_fatal("Virtual memory exhausted");
+
+ while (i < NUMOPCODES) {
+ const char *name = sparc_opcodes[i].name;
+ retval = hash_insert(op_hash, name, &sparc_opcodes[i]);
+ if (retval != NULL && *retval != '\0') {
+ fprintf (stderr, "internal error: can't hash `%s': %s\n",
+ sparc_opcodes[i].name, retval);
+ lose = 1;
+ }
+ do
+ {
+ if (sparc_opcodes[i].match & sparc_opcodes[i].lose) {
+ fprintf (stderr, "internal error: losing opcode: `%s' \"%s\"\n",
+ sparc_opcodes[i].name, sparc_opcodes[i].args);
+ lose = 1;
+ }
+ ++i;
+ } while (i < NUMOPCODES
+ && !strcmp(sparc_opcodes[i].name, name));
+ }
+
+ if (lose)
+ as_fatal("Broken assembler. No assembly attempted.");
+
+ for (i = '0'; i < '8'; ++i)
+ octal[i] = 1;
+ for (i = '0'; i <= '9'; ++i)
+ toHex[i] = i - '0';
+ for (i = 'a'; i <= 'f'; ++i)
+ toHex[i] = i + 10 - 'a';
+ for (i = 'A'; i <= 'F'; ++i)
+ toHex[i] = i + 10 - 'A';
+
+#if 0
+ if (flagseen['k'])
+ GOT_symbol = symbol_find_or_make("__GLOBAL_OFFSET_TABLE_");
+#endif
+} /* md_begin() */
+
+void md_end() {
+ return;
+} /* md_end() */
+
+void md_assemble(str)
+char *str;
+{
+ char *toP;
+ int rsd;
+
+ know(str);
+ sparc_ip(str);
+
+ /* See if "set" operand is absolute and small; skip sethi if so. */
+ if (special_case == SPECIAL_CASE_SET && the_insn.exp.X_seg == SEG_ABSOLUTE) {
+ if (the_insn.exp.X_add_number >= -(1<<12)
+ && the_insn.exp.X_add_number < (1<<12)) {
+ the_insn.opcode = 0x80102000 /* or %g0,imm,... */
+ | (the_insn.opcode & 0x3E000000) /* dest reg */
+ | (the_insn.exp.X_add_number & 0x1FFF); /* imm */
+ special_case = 0; /* No longer special */
+ the_insn.reloc = NO_RELOC; /* No longer relocated */
+ }
+ }
+
+ toP = frag_more(4);
+ /* put out the opcode */
+ md_number_to_chars(toP, the_insn.opcode, 4);
+
+ /* put out the symbol-dependent stuff */
+ if (the_insn.reloc != NO_RELOC) {
+ fix_new(frag_now, /* which frag */
+ (toP - frag_now->fr_literal), /* where */
+ 4, /* size */
+ the_insn.exp.X_add_symbol,
+ the_insn.exp.X_subtract_symbol,
+ the_insn.exp.X_add_number,
+ the_insn.pcrel,
+ the_insn.reloc,
+ the_insn.exp.X_got_symbol);
+ }
+ switch (special_case) {
+
+ case SPECIAL_CASE_SET:
+ special_case = 0;
+ know(the_insn.reloc == RELOC_HI22);
+ /* See if "set" operand has no low-order bits; skip OR if so. */
+ if (the_insn.exp.X_seg == SEG_ABSOLUTE
+ && ((the_insn.exp.X_add_number & 0x3FF) == 0))
+ return;
+ toP = frag_more(4);
+ rsd = (the_insn.opcode >> 25) & 0x1f;
+ the_insn.opcode = 0x80102000 | (rsd << 25) | (rsd << 14);
+ md_number_to_chars(toP, the_insn.opcode, 4);
+ fix_new(frag_now, /* which frag */
+ (toP - frag_now->fr_literal), /* where */
+ 4, /* size */
+ the_insn.exp.X_add_symbol,
+ the_insn.exp.X_subtract_symbol,
+ the_insn.exp.X_add_number,
+ the_insn.pcrel,
+ RELOC_LO10,
+ the_insn.exp.X_got_symbol);
+ return;
+
+ case SPECIAL_CASE_FDIV:
+ /* According to information leaked from Sun, the "fdiv" instructions
+ on early SPARC machines would produce incorrect results sometimes.
+ The workaround is to add an fmovs of the destination register to
+ itself just after the instruction. This was true on machines
+ with Weitek 1165 float chips, such as the Sun-4/260 and /280. */
+ special_case = 0;
+ assert(the_insn.reloc == NO_RELOC);
+ toP = frag_more(4);
+ rsd = (the_insn.opcode >> 25) & 0x1f;
+ the_insn.opcode = 0x81A00020 | (rsd << 25) | rsd; /* fmovs dest,dest */
+ md_number_to_chars(toP, the_insn.opcode, 4);
+ return;
+
+ case 0:
+ return;
+
+ default:
+ as_fatal("failed sanity check.");
+ }
+} /* md_assemble() */
+
+static void sparc_ip(str)
+char *str;
+{
+ char *error_message = "";
+ char *s;
+ const char *args;
+ char c;
+ struct sparc_opcode *insn;
+ char *argsStart;
+ unsigned long opcode;
+ unsigned int mask = 0;
+ int match = 0;
+ int comma = 0;
+
+ for (s = str; islower(*s) || (*s >= '0' && *s <= '3'); ++s)
+ ;
+ switch (*s) {
+
+ case '\0':
+ break;
+
+ case ',':
+ comma = 1;
+
+ /*FALLTHROUGH */
+
+ case ' ':
+ *s++ = '\0';
+ break;
+
+ default:
+ as_bad("Unknown opcode: `%s'", str);
+ exit(1);
+ }
+ if ((insn = (struct sparc_opcode *) hash_find(op_hash, str)) == NULL) {
+ as_bad("Unknown opcode: `%s'", str);
+ return;
+ }
+ if (comma) {
+ *--s = ',';
+ }
+ argsStart = s;
+ for (;;) {
+ opcode = insn->match;
+ memset(&the_insn, '\0', sizeof(the_insn));
+ the_insn.reloc = NO_RELOC;
+
+ /*
+ * Build the opcode, checking as we go to make
+ * sure that the operands match
+ */
+ for (args = insn->args; ; ++args) {
+ switch (*args) {
+
+ case 'M':
+ case 'm':
+ if (strncmp(s, "%asr", 4) == 0) {
+ s += 4;
+
+ if (isdigit(*s)) {
+ long num = 0;
+
+ while (isdigit(*s)) {
+ num = num*10 + *s-'0';
+ ++s;
+ }
+
+ if (num < 16 || 31 < num) {
+ error_message = ": asr number must be between 15 and 31";
+ goto error;
+ } /* out of range */
+
+ opcode |= (*args == 'M' ? RS1(num) : RD(num));
+ continue;
+ } else {
+ error_message = ": expecting %asrN";
+ goto error;
+ } /* if %asr followed by a number. */
+
+ } /* if %asr */
+ break;
+
+
+ case '\0': /* end of args */
+ if (*s == '\0') {
+ match = 1;
+ }
+ break;
+
+ case '+':
+ if (*s == '+') {
+ ++s;
+ continue;
+ }
+ if (*s == '-') {
+ continue;
+ }
+ break;
+
+ case '[': /* these must match exactly */
+ case ']':
+ case ',':
+ case ' ':
+ if (*s++ == *args)
+ continue;
+ break;
+
+ case '#': /* must be at least one digit */
+ if (isdigit(*s++)) {
+ while (isdigit(*s)) {
+ ++s;
+ }
+ continue;
+ }
+ break;
+
+ case 'C': /* coprocessor state register */
+ if (strncmp(s, "%csr", 4) == 0) {
+ s += 4;
+ continue;
+ }
+ break;
+
+ case 'b': /* next operand is a coprocessor register */
+ case 'c':
+ case 'D':
+ if (*s++ == '%' && *s++ == 'c' && isdigit(*s)) {
+ mask = *s++;
+ if (isdigit(*s)) {
+ mask = 10 * (mask - '0') + (*s++ - '0');
+ if (mask >= 32) {
+ break;
+ }
+ } else {
+ mask -= '0';
+ }
+ switch (*args) {
+
+ case 'b':
+ opcode |= mask << 14;
+ continue;
+
+ case 'c':
+ opcode |= mask;
+ continue;
+
+ case 'D':
+ opcode |= mask << 25;
+ continue;
+ }
+ }
+ break;
+
+ case 'r': /* next operand must be a register */
+ case 's':
+ case '1':
+ case '2':
+ case 'd':
+ case 'x':
+ if (*s++ == '%') {
+ switch (c = *s++) {
+
+ case 'f': /* frame pointer */
+ if (*s++ == 'p') {
+ mask = 0x1e;
+ break;
+ }
+ goto error;
+
+ case 'g': /* global register */
+ if (isoctal(c = *s++)) {
+ mask = c - '0';
+ break;
+ }
+ goto error;
+
+ case 'i': /* in register */
+ if (isoctal(c = *s++)) {
+ mask = c - '0' + 24;
+ break;
+ }
+ goto error;
+
+ case 'l': /* local register */
+ if (isoctal(c = *s++)) {
+ mask= (c - '0' + 16) ;
+ break;
+ }
+ goto error;
+
+ case 'o': /* out register */
+ if (isoctal(c = *s++)) {
+ mask= (c - '0' + 8) ;
+ break;
+ }
+ goto error;
+
+ case 's': /* stack pointer */
+ if (*s++ == 'p') {
+ mask= 0xe;
+ break;
+ }
+ goto error;
+
+ case 'r': /* any register */
+ if (!isdigit(c = *s++)) {
+ goto error;
+ }
+ /* FALLTHROUGH */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (isdigit(*s)) {
+ if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32) {
+ goto error;
+ }
+ } else {
+ c -= '0';
+ }
+ mask= c;
+ break;
+
+ case 'x':
+ opcode |= (mask << 25) | mask;
+ continue;
+
+ default:
+ goto error;
+ }
+ /*
+ * Got the register, now figure out where
+ * it goes in the opcode.
+ */
+ switch (*args) {
+
+ case '1':
+ opcode |= mask << 14;
+ continue;
+
+ case '2':
+ opcode |= mask;
+ continue;
+
+ case 'd':
+ opcode |= mask << 25;
+ continue;
+
+ case 'r':
+ opcode |= (mask << 25) | (mask << 14);
+ continue;
+ case 'x':
+ opcode |= (mask << 25) | mask;
+ continue;
+ }
+ }
+ break;
+
+ case 'e': /* next operand is a floating point register */
+ case 'v':
+ case 'V':
+
+ case 'f':
+ case 'B':
+ case 'R':
+
+ case 'g':
+ case 'H':
+ case 'J': {
+ char format;
+
+ if (*s++ == '%'
+
+ && ((format = *s) == 'f')
+
+ && isdigit(*++s)) {
+
+
+
+ for (mask = 0; isdigit(*s); ++s) {
+ mask = 10 * mask + (*s - '0');
+ } /* read the number */
+
+ if ((*args == 'u'
+ || *args == 'v'
+ || *args == 'B'
+ || *args == 'H')
+ && (mask & 1)) {
+ break;
+ } /* register must be even numbered */
+
+ if ((*args == 'U'
+ || *args == 'V'
+ || *args == 'R'
+ || *args == 'J')
+ && (mask & 3)) {
+ break;
+ } /* register must be multiple of 4 */
+
+ if (format == 'f') {
+ if (mask >= 32) {
+ error_message = ": There are only 32 f registers; [0-31]";
+ goto error;
+ } /* on error */
+ } /* if not an 'f' register. */
+ } /* on error */
+
+ switch (*args) {
+
+ case 'v':
+ case 'V':
+ case 'e':
+ opcode |= RS1(mask);
+ continue;
+
+
+ case 'f':
+ case 'B':
+ case 'R':
+ opcode |= RS2(mask);
+ continue;
+
+ case 'g':
+ case 'H':
+ case 'J':
+ opcode |= RD(mask);
+ continue;
+ } /* pack it in. */
+
+ know(0);
+ break;
+ } /* float arg */
+
+ case 'F':
+ if (strncmp(s, "%fsr", 4) == 0) {
+ s += 4;
+ continue;
+ }
+ break;
+
+ case 'h': /* high 22 bits */
+#ifdef PIC
+ the_insn.reloc = flagseen['k']?
+ RELOC_BASE22:
+ RELOC_HI22;
+#else
+ the_insn.reloc = RELOC_HI22;
+#endif
+ goto immediate;
+
+ case 'l': /* 22 bit PC relative immediate */
+ the_insn.reloc = RELOC_WDISP22;
+ the_insn.pcrel = 1;
+ goto immediate;
+
+ case 'L': /* 30 bit immediate */
+#ifdef PIC
+ the_insn.reloc = flagseen['k']?
+ RELOC_JMP_TBL:
+ RELOC_WDISP30;
+#else
+ the_insn.reloc = RELOC_WDISP30;
+#endif
+ the_insn.pcrel = 1;
+ goto immediate;
+
+ case 'n': /* 22 bit immediate */
+ the_insn.reloc = RELOC_22;
+ goto immediate;
+
+ case 'i': /* 13 bit immediate */
+ the_insn.reloc = RELOC_BASE13;
+
+ /*FALLTHROUGH */
+
+ immediate:
+ if (*s == ' ')
+ s++;
+ if (*s == '%') {
+ if ((c = s[1]) == 'h' && s[2] == 'i') {
+ the_insn.reloc = RELOC_HI22;
+ s+=3;
+ } else if (c == 'l' && s[2] == 'o') {
+ the_insn.reloc = RELOC_LO10;
+ s+=3;
+ } else
+ break;
+ }
+ /* Note that if the getExpression() fails, we
+ will still have created U entries in the
+ symbol table for the 'symbols' in the input
+ string. Try not to create U symbols for
+ registers, etc. */
+ {
+ /* This stuff checks to see if the
+ expression ends in +%reg If it does,
+ it removes the register from the
+ expression, and re-sets 's' to point
+ to the right place */
+
+ char *s1;
+
+ for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++) ;;
+
+ if (s1 != s && isdigit(s1[-1])) {
+ if (s1[-2] == '%' && s1[-3] == '+') {
+ s1 -= 3;
+ *s1 = '\0';
+ (void) getExpression(s);
+ *s1 = '+';
+ s = s1;
+ continue;
+ } else if (strchr("goli0123456789", s1[-2]) && s1[-3] == '%' && s1[-4] == '+') {
+ s1 -= 4;
+ *s1 = '\0';
+ (void) getExpression(s);
+ *s1 = '+';
+ s = s1;
+ continue;
+ }
+ }
+ }
+ (void)getExpression(s);
+#ifdef PIC
+ if (the_insn.exp.X_got_symbol) {
+ switch(the_insn.reloc) {
+ case RELOC_HI22:
+ the_insn.reloc = RELOC_PC22;
+ the_insn.pcrel = 1;
+ break;
+ case RELOC_LO10:
+ the_insn.reloc = RELOC_PC10;
+ the_insn.pcrel = 1;
+ break;
+ default:
+ break;
+ }
+ }
+#endif
+ s = expr_end;
+ continue;
+
+ case 'a':
+ if (*s++ == 'a') {
+ opcode |= ANNUL;
+ continue;
+ }
+ break;
+
+ case 'A': {
+ char *push = input_line_pointer;
+ expressionS e;
+
+ input_line_pointer = s;
+
+ if (expression(&e) == SEG_ABSOLUTE) {
+ opcode |= e.X_add_number << 5;
+ s = input_line_pointer;
+ input_line_pointer = push;
+ continue;
+ } /* if absolute */
+
+ break;
+ } /* alternate space */
+
+ case 'p':
+ if (strncmp(s, "%psr", 4) == 0) {
+ s += 4;
+ continue;
+ }
+ break;
+
+ case 'q': /* floating point queue */
+ if (strncmp(s, "%fq", 3) == 0) {
+ s += 3;
+ continue;
+ }
+ break;
+
+ case 'Q': /* coprocessor queue */
+ if (strncmp(s, "%cq", 3) == 0) {
+ s += 3;
+ continue;
+ }
+ break;
+
+ case 'S':
+ if (strcmp(str, "set") == 0) {
+ special_case = SPECIAL_CASE_SET;
+ continue;
+ } else if (strncmp(str, "fdiv", 4) == 0) {
+ special_case = SPECIAL_CASE_FDIV;
+ continue;
+ }
+ break;
+
+ case 't':
+ if (strncmp(s, "%tbr", 4) != 0)
+ break;
+ s += 4;
+ continue;
+
+ case 'w':
+ if (strncmp(s, "%wim", 4) != 0)
+ break;
+ s += 4;
+ continue;
+
+ case 'y':
+ if (strncmp(s, "%y", 2) != 0)
+ break;
+ s += 2;
+ continue;
+
+ default:
+ as_fatal("failed sanity check.");
+ } /* switch on arg code */
+ break;
+ } /* for each arg that we expect */
+ error:
+ if (match == 0) {
+ /* Args don't match. */
+ if (((unsigned) (&insn[1] - sparc_opcodes)) < NUMOPCODES
+ && !strcmp(insn->name, insn[1].name)) {
+ ++insn;
+ s = argsStart;
+ continue;
+ } else {
+ as_bad("Illegal operands%s", error_message);
+ return;
+ }
+ } else {
+ if (insn->architecture > current_architecture) {
+ if (!architecture_requested || warn_on_bump) {
+
+ if (warn_on_bump) {
+ as_warn("architecture bumped from \"%s\" to \"%s\" on \"%s\"",
+ architecture_pname[current_architecture],
+ architecture_pname[insn->architecture],
+ str);
+ } /* if warning */
+
+ current_architecture = insn->architecture;
+ } else {
+ as_bad("architecture mismatch on \"%s\" (\"%s\"). current architecture is \"%s\"",
+ str,
+ architecture_pname[insn->architecture],
+ architecture_pname[current_architecture]);
+ return;
+ } /* if bump ok else error */
+ } /* if architecture higher */
+ } /* if no match */
+
+ break;
+ } /* forever looking for a match */
+
+ the_insn.opcode = opcode;
+#if DEBUG_SPARC
+ if (flagseen['D'])
+ print_insn(&the_insn);
+#endif
+ return;
+} /* sparc_ip() */
+
+static int getExpression(str)
+char *str;
+{
+ char *save_in;
+ segT seg;
+
+ save_in = input_line_pointer;
+ input_line_pointer = str;
+ switch (seg = expression(&the_insn.exp)) {
+
+ case SEG_ABSOLUTE:
+ switch (the_insn.reloc) {
+ case RELOC_LO10:
+ the_insn.exp.X_add_number &= ~(~(0) << 10);
+ break;
+ case RELOC_HI22:
+ the_insn.exp.X_add_number &= (~(0) << 10);
+ break;
+ default:
+ break;
+ }
+ break;
+ case SEG_TEXT:
+ case SEG_DATA:
+ case SEG_BSS:
+ case SEG_UNKNOWN:
+ case SEG_DIFFERENCE:
+ case SEG_BIG:
+ case SEG_ABSENT:
+ break;
+
+ default:
+ the_insn.error = "bad segment";
+ expr_end = input_line_pointer;
+ input_line_pointer=save_in;
+ return 1;
+ }
+ switch (the_insn.reloc) {
+ case RELOC_BASE10:
+ case RELOC_BASE13:
+ case RELOC_BASE22:
+ if (the_insn.exp.X_add_symbol)
+ the_insn.exp.X_add_symbol->sy_forceout = 1;
+ break;
+ default:
+ break;
+ }
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+ return 0;
+} /* getExpression() */
+
+
+/*
+ This is identical to the md_atof in m68k.c. I think this is right,
+ but I'm not sure.
+
+ Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP. An error message is returned, or NULL on OK.
+ */
+
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+char *md_atof(type,litP,sizeP)
+char type;
+char *litP;
+int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+ char *atof_ieee();
+
+ switch (type) {
+
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP=0;
+ return "Bad call to MD_ATOF()";
+ }
+ t=atof_ieee(input_line_pointer,type,words);
+ if (t)
+ input_line_pointer=t;
+ *sizeP=prec * sizeof(LITTLENUM_TYPE);
+ for (wordP=words;prec--;) {
+ md_number_to_chars(litP,(long)(*wordP++),sizeof(LITTLENUM_TYPE));
+ litP+=sizeof(LITTLENUM_TYPE);
+ }
+ return ""; /* Someone should teach Dean about null pointers */
+} /* md_atof() */
+
+/*
+ * Write out big-endian.
+ */
+void md_number_to_chars(buf,val,n)
+char *buf;
+long val;
+int n;
+{
+
+ switch (n) {
+
+ case 4:
+ *buf++ = val >> 24;
+ *buf++ = val >> 16;
+ case 2:
+ *buf++ = val >> 8;
+ case 1:
+ *buf = val;
+ break;
+
+ default:
+ as_fatal("failed sanity check.");
+ }
+ return;
+} /* md_number_to_chars() */
+
+static void reloc_check(val, bits)
+long val;
+int bits;
+{
+ if (((val & (-1 << bits)) != 0)
+ && ((val & (-1 << bits)) != (-1 << (bits - 0)))) {
+ as_warn("Relocation overflow. Value truncated.");
+ } /* on overflow */
+
+ return;
+} /* reloc_check() */
+
+/* Apply a fixS to the frags, now that we know the value it ought to
+ hold. */
+
+void md_apply_fix(fixP, val)
+fixS *fixP;
+long val;
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+#if DEBUG_SPARC
+ static char *Reloc[] = {
+ "RELOC_8", "RELOC_16", "RELOC_32",
+ "RELOC_DISP8", "RELOC_DISP16", "RELOC_DISP32",
+ "RELOC_WDISP30", "RELOC_WDISP22",
+ "RELOC_HI22",
+ "RELOC_22", "RELOC_13",
+ "RELOC_LO10", "RELOC_SFA_BASE", "RELOC_SFA_OFF13",
+ "RELOC_BASE10", "RELOC_BASE13", "RELOC_BASE22", "RELOC_PC10",
+ "RELOC_PC22", "RELOC_JMP_TBL", "RELOC_SEGOFF16",
+ "RELOC_GLOB_DAT", "RELOC_JMP_SLOT", "RELOC_RELATIVE",
+ "NO_RELOC"
+ };
+ if (flagseen['D'])
+ fprintf(stderr, "md_apply_fix: \"%s\" \"%s\", val %d -- %s\n",
+ ((fixP->fx_addsy != NULL)
+ ? ((S_GET_NAME(fixP->fx_addsy) != NULL)
+ ? S_GET_NAME(fixP->fx_addsy)
+ : "???")
+ : "0"),
+ ((fixP->fx_subsy != NULL)
+ ? ((S_GET_NAME(fixP->fx_subsy) != NULL)
+ ? S_GET_NAME(fixP->fx_subsy)
+ : "???")
+ : "0"),
+ val, Reloc[fixP->fx_r_type]);
+#endif
+
+ assert(fixP->fx_size == 4);
+ assert(fixP->fx_r_type < NO_RELOC);
+
+ fixP->fx_addnumber = val; /* Remember value for emit_reloc */
+
+ /*
+ * This is a hack. There should be a better way to
+ * handle this.
+ */
+ if (fixP->fx_r_type == RELOC_WDISP30 && fixP->fx_addsy) {
+ val += fixP->fx_where + fixP->fx_frag->fr_address;
+ }
+
+ switch (fixP->fx_r_type) {
+
+ /* Michael Bloom <mb@ttidca.tti.com> says... [This] change was
+ made to match the behavior of Sun's assembler. Some broken
+ loaders depend on that. At least one such loader actually
+ adds the section data to what it finds in the addend. (It
+ should only be using the addend like Sun's loader seems to).
+ This caused incorrect relocation: (addend + adjustment)
+ became ( ( 2 * addend ) + adjustment ). [and there should
+ be no cases that reach here anyway. */
+ case RELOC_32:
+ buf[0] = 0; /* val >> 24; */
+ buf[1] = 0; /* val >> 16; */
+ buf[2] = 0; /* val >> 8; */
+ buf[3] = 0; /* val; */
+ break;
+
+#if 0
+ case RELOC_8: /* These don't seem to ever be needed. */
+ case RELOC_16:
+ case RELOC_DISP8:
+ case RELOC_DISP16:
+ case RELOC_DISP32:
+#endif
+
+ case RELOC_JMP_TBL:
+ case RELOC_WDISP30:
+ val = (val >>= 2) + 1;
+ reloc_check(val, 30);
+
+ buf[0] |= (val >> 24) & 0x3f;
+ buf[1]= (val >> 16);
+ buf[2] = val >> 8;
+ buf[3] = val;
+ break;
+
+
+ case RELOC_HI22:
+ reloc_check(val >> 10, 22);
+
+ if (!fixP->fx_addsy) {
+ buf[1] |= (val >> 26) & 0x3f;
+ buf[2] = val >> 18;
+ buf[3] = val >> 10;
+ } else {
+ buf[2]=0;
+ buf[3]=0;
+ }
+ break;
+
+ case RELOC_PC22:
+ case RELOC_22:
+ reloc_check(val, 22);
+
+ buf[1] |= (val >> 16) & 0x3f;
+ buf[2] = val >> 8;
+ buf[3] = val & 0xff;
+ break;
+
+ case RELOC_13:
+ reloc_check(val, 13);
+
+ buf[2] = (val >> 8) & 0x1f;
+ buf[3] = val & 0xff;
+ break;
+
+
+ case RELOC_PC10:
+ case RELOC_LO10:
+ case RELOC_BASE10:
+ reloc_check(val, 10);
+
+ if (!fixP->fx_addsy) {
+ buf[2] |= (val >> 8) & 0x03;
+ buf[3] = val;
+ } else
+ buf[3]=0;
+ break;
+#if 0
+ case RELOC_SFA_BASE:
+ case RELOC_SFA_OFF13:
+#endif
+ case RELOC_BASE13:
+ reloc_check(val, 13);
+
+ buf[2] |= (val >> 8) & 0x1f;
+ buf[3] = val;
+ break;
+
+ case RELOC_WDISP22:
+ val = (val >>= 2) + 1;
+ /* FALLTHROUGH */
+ case RELOC_BASE22:
+ reloc_check(val, 22);
+
+ buf[1] |= (val >> 16) & 0x3f;
+ buf[2] = val >> 8;
+ buf[3] = val;
+ break;
+
+#if 0
+ case RELOC_PC10:
+ case RELOC_PC22:
+ case RELOC_JMP_TBL:
+ case RELOC_SEGOFF16:
+ case RELOC_GLOB_DAT:
+ case RELOC_JMP_SLOT:
+ case RELOC_RELATIVE:
+#endif
+
+ case NO_RELOC:
+ default:
+ as_bad("bad relocation type: 0x%02x", fixP->fx_r_type);
+ break;
+ }
+} /* md_apply_fix() */
+
+/* should never be called for sparc */
+void md_create_short_jump(ptr, from_addr, to_addr, frag, to_symbol)
+char *ptr;
+long from_addr;
+long to_addr;
+fragS *frag;
+symbolS *to_symbol;
+{
+ as_fatal("sparc_create_short_jmp\n");
+} /* md_create_short_jump() */
+
+/* Translate internal representation of relocation info to target format.
+
+ On sparc: first 4 bytes are normal unsigned long address, next three
+ bytes are index, most sig. byte first. Byte 7 is broken up with
+ bit 7 as external, bits 6 & 5 unused, and the lower
+ five bits as relocation type. Next 4 bytes are long addend. */
+/* Thanx and a tip of the hat to Michael Bloom, mb@ttidca.tti.com */
+void tc_aout_fix_to_chars(where, fixP, segment_address_in_file)
+char *where;
+fixS *fixP;
+relax_addressT segment_address_in_file;
+{
+ long r_index;
+ long r_extern;
+ long r_addend = 0;
+ long r_address;
+#ifdef PIC
+ int kflag = 0;
+#endif
+
+ know(fixP->fx_addsy);
+
+ if (!S_IS_DEFINED(fixP->fx_addsy)) {
+ r_extern = 1;
+ r_index = fixP->fx_addsy->sy_number;
+ } else {
+ r_extern = 0;
+ r_index = S_GET_TYPE(fixP->fx_addsy);
+#ifdef PIC
+ if (flagseen['k']) {
+ switch (fixP->fx_r_type) {
+ case RELOC_BASE10:
+ case RELOC_BASE13:
+ case RELOC_BASE22:
+ r_index = fixP->fx_addsy->sy_number;
+ if (S_IS_EXTERNAL(fixP->fx_addsy))
+ r_extern = 1;
+ kflag = 1;
+ break;
+
+ case RELOC_32:
+ if (!S_IS_EXTERNAL(fixP->fx_addsy))
+ break;
+ r_index = fixP->fx_addsy->sy_number;
+ /*kflag = 1;*/
+ break;
+
+ default:
+ break;
+ }
+ }
+#endif
+ }
+
+ /* this is easy */
+ md_number_to_chars(where,
+ r_address = fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file,
+ 4);
+
+ /* now the fun stuff */
+ where[4] = (r_index >> 16) & 0x0ff;
+ where[5] = (r_index >> 8) & 0x0ff;
+ where[6] = r_index & 0x0ff;
+ where[7] = ((r_extern << 7) & 0x80) | (0 & 0x60) | (fixP->fx_r_type & 0x1F);
+
+ /* Also easy */
+ if (fixP->fx_addsy->sy_frag) {
+ r_addend = fixP->fx_addsy->sy_frag->fr_address;
+ }
+
+ if (fixP->fx_pcrel) {
+#ifdef PIC
+ if (fixP->fx_gotsy) {
+ r_addend = r_address;
+ r_addend += fixP->fx_addnumber;
+ } else
+#endif
+ r_addend -= r_address;
+ } else {
+#ifdef PIC
+ if (kflag)
+ r_addend = 0;
+ else
+#endif
+ r_addend = fixP->fx_addnumber;
+ }
+
+ md_number_to_chars(&where[8], r_addend, 4);
+
+ return;
+} /* tc_aout_fix_to_chars() */
+
+/* should never be called for sparc */
+void md_convert_frag(headers, fragP)
+object_headers *headers;
+register fragS *fragP;
+{
+ as_fatal("sparc_convert_frag\n");
+} /* md_convert_frag() */
+
+/* should never be called for sparc */
+void md_create_long_jump(ptr, from_addr, to_addr, frag, to_symbol)
+char *ptr;
+long from_addr, to_addr;
+fragS *frag;
+symbolS *to_symbol;
+{
+ as_fatal("sparc_create_long_jump\n");
+} /* md_create_long_jump() */
+
+/* should never be called for sparc */
+int md_estimate_size_before_relax(fragP, segtype)
+fragS *fragP;
+segT segtype;
+{
+ as_fatal("sparc_estimate_size_before_relax\n");
+ return(1);
+} /* md_estimate_size_before_relax() */
+
+#if DEBUG_SPARC
+/* for debugging only */
+static void print_insn(insn)
+struct sparc_it *insn;
+{
+ static char *Reloc[] = {
+ "RELOC_8",
+ "RELOC_16",
+ "RELOC_32",
+ "RELOC_DISP8",
+ "RELOC_DISP16",
+ "RELOC_DISP32",
+ "RELOC_WDISP30",
+ "RELOC_WDISP22",
+ "RELOC_HI22",
+ "RELOC_22",
+ "RELOC_13",
+ "RELOC_LO10",
+ "RELOC_SFA_BASE",
+ "RELOC_SFA_OFF13",
+ "RELOC_BASE10",
+ "RELOC_BASE13",
+ "RELOC_BASE22",
+ "RELOC_PC10",
+ "RELOC_PC22",
+ "RELOC_JMP_TBL",
+ "RELOC_SEGOFF16",
+ "RELOC_GLOB_DAT",
+ "RELOC_JMP_SLOT",
+ "RELOC_RELATIVE",
+ "NO_RELOC"
+ };
+
+ if (insn->error) {
+ fprintf(stderr, "ERROR: %s\n", insn->error);
+ }
+ fprintf(stderr, "opcode=0x%08x\n", insn->opcode);
+ fprintf(stderr, "reloc = %s\n", Reloc[insn->reloc]);
+ fprintf(stderr, "exp = {\n");
+ fprintf(stderr, "\t\tX_add_symbol = %s\n",
+ ((insn->exp.X_add_symbol != NULL)
+ ? ((S_GET_NAME(insn->exp.X_add_symbol) != NULL)
+ ? S_GET_NAME(insn->exp.X_add_symbol)
+ : "???")
+ : "0"));
+ fprintf(stderr, "\t\tX_sub_symbol = %s\n",
+ ((insn->exp.X_subtract_symbol != NULL)
+ ? (S_GET_NAME(insn->exp.X_subtract_symbol)
+ ? S_GET_NAME(insn->exp.X_subtract_symbol)
+ : "???")
+ : "0"));
+ fprintf(stderr, "\t\tX_got_symbol = %s\n",
+ ((insn->exp.X_got_symbol != NULL)
+ ? (S_GET_NAME(insn->exp.X_got_symbol)
+ ? S_GET_NAME(insn->exp.X_got_symbol)
+ : "???")
+ : "0"));
+ fprintf(stderr, "\t\tX_add_number = %d\n",
+ insn->exp.X_add_number);
+ fprintf(stderr, "}\n");
+ return;
+} /* print_insn() */
+#endif
+
+/* Set the hook... */
+
+/* void emit_sparc_reloc();
+ void (*md_emit_relocations)() = emit_sparc_reloc; */
+
+#ifdef comment
+
+/*
+ * Sparc/AM29K relocations are completely different, so it needs
+ * this machine dependent routine to emit them.
+ */
+#if defined(OBJ_AOUT) || defined(OBJ_BOUT)
+void emit_sparc_reloc(fixP, segment_address_in_file)
+register fixS *fixP;
+relax_addressT segment_address_in_file;
+{
+ struct reloc_info_generic ri;
+ register symbolS *symbolP;
+ extern char *next_object_file_charP;
+ /* long add_number; */
+
+ memset((char *) &ri, '\0', sizeof(ri));
+ for (; fixP; fixP = fixP->fx_next) {
+
+ if (fixP->fx_r_type >= NO_RELOC) {
+ as_fatal("fixP->fx_r_type = %d\n", fixP->fx_r_type);
+ }
+
+ if ((symbolP = fixP->fx_addsy) != NULL) {
+ ri.r_address = fixP->fx_frag->fr_address +
+ fixP->fx_where - segment_address_in_file;
+ if ((S_GET_TYPE(symbolP)) == N_UNDF) {
+ ri.r_extern = 1;
+ ri.r_index = symbolP->sy_number;
+ } else {
+ ri.r_extern = 0;
+ ri.r_index = S_GET_TYPE(symbolP);
+ }
+ if (symbolP && symbolP->sy_frag) {
+ ri.r_addend = symbolP->sy_frag->fr_address;
+ }
+ ri.r_type = fixP->fx_r_type;
+ if (fixP->fx_pcrel) {
+ /* ri.r_addend -= fixP->fx_where; */
+ ri.r_addend -= ri.r_address;
+ } else {
+ ri.r_addend = fixP->fx_addnumber;
+ }
+
+ md_ri_to_chars(next_object_file_charP, &ri);
+ next_object_file_charP += md_reloc_size;
+ }
+ }
+ return;
+} /* emit_sparc_reloc() */
+#endif /* aout or bout */
+#endif /* comment */
+
+/*
+ * md_parse_option
+ * Invocation line includes a switch not recognized by the base assembler.
+ * See if it's a processor-specific option. These are:
+ *
+ * -bump
+ * Warn on architecture bumps. See also -A.
+ *
+ * -Av6, -Av7, -Av8
+ * Select the architecture. Instructions or features not
+ * supported by the selected architecture cause fatal errors.
+ *
+ * The default is to start at v6, and bump the architecture up
+ * whenever an instruction is seen at a higher level.
+ *
+ * If -bump is specified, a warning is printing when bumping to
+ * higher levels.
+ *
+ * If an architecture is specified, all instructions must match
+ * that architecture. Any higher level instructions are flagged
+ * as errors.
+ *
+ * if both an architecture and -bump are specified, the
+ * architecture starts at the specified level, but bumps are
+ * warnings.
+ *
+ */
+int md_parse_option(argP, cntP, vecP)
+char **argP;
+int *cntP;
+char ***vecP;
+{
+ char *p;
+ const char **arch;
+
+ if (!strcmp(*argP,"bump")){
+ warn_on_bump = 1;
+
+ } else if (**argP == 'A'){
+ p = (*argP) + 1;
+
+ for (arch = architecture_pname; *arch != NULL; ++arch){
+ if (strcmp(p, *arch) == 0){
+ break;
+ } /* found a match */
+ } /* walk the pname table */
+
+ if (*arch == NULL){
+ as_bad("unknown architecture: %s", p);
+ } else {
+ current_architecture = (enum sparc_architecture) (arch - architecture_pname);
+ architecture_requested = 1;
+ }
+#ifdef PIC
+ } else if (**argP == 'k') {
+ /* Predefine GOT symbol */
+ GOT_symbol = symbol_find_or_make("__GLOBAL_OFFSET_TABLE_");
+#endif
+ } else {
+ /* Unknown option */
+ (*argP)++;
+ return 0;
+ }
+ **argP = '\0'; /* Done parsing this switch */
+ return 1;
+} /* md_parse_option() */
+
+/* We have no need to default values of symbols. */
+
+/* ARGSUSED */
+symbolS *md_undefined_symbol(name)
+char *name;
+{
+ return 0;
+} /* md_undefined_symbol() */
+
+/* Parse an operand that is machine-specific.
+ We just return without modifying the expression if we have nothing
+ to do. */
+
+/* ARGSUSED */
+void md_operand(expressionP)
+expressionS *expressionP;
+{
+} /* md_operand() */
+
+/* Round up a section size to the appropriate boundary. */
+long md_section_align(segment, size)
+segT segment;
+long size;
+{
+ return((size + 7) & ~7); /* Round all sects to multiple of 8 */
+} /* md_section_align() */
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the sparc, they're relative to the address of the offset, plus
+ its size. This gets us to the following instruction.
+ (??? Is this right? FIXME-SOON) */
+long md_pcrel_from(fixP)
+fixS *fixP;
+{
+#ifdef PIC
+ /*
+ * _GLOBAL_OFFSET_TABLE_ refs are relative to the offset of the
+ * current instruction. We omit fx_size from the computation (which
+ * is always 4 anyway).
+ */
+ if (fixP->fx_gotsy)
+ return fixP->fx_where + fixP->fx_frag->fr_address;
+ else
+#endif
+ return(fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address);
+} /* md_pcrel_from() */
+
+void tc_aout_pre_write_hook(headers)
+object_headers *headers;
+{
+ H_SET_VERSION(headers, 1);
+ return;
+} /* tc_aout_pre_write_hook() */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of tc-sparc.c */
diff --git a/gnu/usr.bin/as/config/tc-sparc.h b/gnu/usr.bin/as/config/tc-sparc.h
new file mode 100644
index 0000000..27355a7
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-sparc.h
@@ -0,0 +1,54 @@
+/* tc-sparc.h - Macros and type defines for the sparc.
+ Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with GAS; see the file COPYING. If not, write
+ to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * $Id: tc-sparc.h,v 1.1 1993/10/02 20:59:41 pk Exp $
+ */
+
+#define TC_SPARC 1
+
+#define NO_LISTING
+#define LOCAL_LABELS_FB
+#define WORKING_DOT_WORD
+
+#ifdef OBJ_BOUT
+#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE ((0x103 << 16) | BMAGIC) /* Magic number for header */
+#else
+#ifdef OBJ_AOUT
+#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE ((0x103 << 16) | OMAGIC) /* Magic number for header */
+#endif /* OBJ_AOUT */
+#endif /* OBJ_BOUT */
+
+#define AOUT_MACHTYPE 3
+
+#define tc_headers_hook(a) {;} /* don't need it. */
+#define tc_crawl_symbol_chain(a) {;} /* don't need it. */
+
+void tc_aout_pre_write_hook();
+
+#define LISTING_HEADER "SPARC GAS "
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of tc-sparc.h */
diff --git a/gnu/usr.bin/as/config/tc-tahoe.c b/gnu/usr.bin/as/config/tc-tahoe.c
new file mode 100644
index 0000000..68dc5b9
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-tahoe.c
@@ -0,0 +1,1924 @@
+/* tc-tahoe.c
+ Not part of GAS yet. */
+
+#include "as.h"
+#include "obstack.h"
+
+ /* this bit glommed from tahoe-inst.h */
+
+typedef unsigned char byte;
+typedef byte tahoe_opcodeT;
+
+/*
+ * This is part of tahoe-ins-parse.c & friends.
+ * We want to parse a tahoe instruction text into a tree defined here.
+ */
+
+#define TIT_MAX_OPERANDS (4) /* maximum number of operands in one
+ single tahoe instruction */
+
+struct top /* tahoe instruction operand */
+{
+ int top_ndx; /* -1, or index register. eg 7=[R7] */
+ int top_reg; /* -1, or register number. eg 7 = R7 or (R7) */
+ byte top_mode; /* Addressing mode byte. This byte, defines
+ which of the 11 modes opcode is. */
+
+ char top_access; /* Access type wanted for this opperand
+ 'b'branch ' 'no-instruction 'amrvw' */
+ char top_width; /* Operand width expected, one of "bwlq?-:!" */
+
+ char *top_error; /* Say if operand is inappropriate */
+
+ expressionS exp_of_operand; /* The expression as parsed by expression()*/
+
+ byte top_dispsize; /* Number of bytes in the displacement if we
+ can figure it out */
+};
+
+/* The addressing modes for an operand. These numbers are the acutal values
+ for certain modes, so be carefull if you screw with them. */
+#define TAHOE_DIRECT_REG (0x50)
+#define TAHOE_REG_DEFERRED (0x60)
+
+#define TAHOE_REG_DISP (0xE0)
+#define TAHOE_REG_DISP_DEFERRED (0xF0)
+
+#define TAHOE_IMMEDIATE (0x8F)
+#define TAHOE_IMMEDIATE_BYTE (0x88)
+#define TAHOE_IMMEDIATE_WORD (0x89)
+#define TAHOE_IMMEDIATE_LONGWORD (0x8F)
+#define TAHOE_ABSOLUTE_ADDR (0x9F)
+
+#define TAHOE_DISPLACED_RELATIVE (0xEF)
+#define TAHOE_DISP_REL_DEFERRED (0xFF)
+
+#define TAHOE_AUTO_DEC (0x7E)
+#define TAHOE_AUTO_INC (0x8E)
+#define TAHOE_AUTO_INC_DEFERRED (0x9E)
+/* INDEXED_REG is decided by the existance or lack of a [reg] */
+
+/* These are encoded into top_width when top_access=='b'
+ and it's a psuedo op.*/
+#define TAHOE_WIDTH_ALWAYS_JUMP '-'
+#define TAHOE_WIDTH_CONDITIONAL_JUMP '?'
+#define TAHOE_WIDTH_BIG_REV_JUMP '!'
+#define TAHOE_WIDTH_BIG_NON_REV_JUMP ':'
+
+/* The hex code for certain tahoe commands and modes.
+ This is just for readability. */
+#define TAHOE_JMP (0x71)
+#define TAHOE_PC_REL_LONG (0xEF)
+#define TAHOE_BRB (0x11)
+#define TAHOE_BRW (0x13)
+/* These, when 'ored' with, or added to, a register number,
+ set up the number for the displacement mode. */
+#define TAHOE_PC_OR_BYTE (0xA0)
+#define TAHOE_PC_OR_WORD (0xC0)
+#define TAHOE_PC_OR_LONG (0xE0)
+
+struct tit /* get it out of the sewer, it stands for
+ tahoe instruction tree (Geeze!) */
+{
+ tahoe_opcodeT tit_opcode; /* The opcode. */
+ byte tit_operands; /* How many operands are here. */
+ struct top tit_operand[TIT_MAX_OPERANDS]; /* Operands */
+ char *tit_error; /* "" or fatal error text */
+};
+
+/* end: tahoe-inst.h */
+
+/* tahoe.c - tahoe-specific -
+ Not part of gas yet.
+ */
+
+#include "opcode/tahoe.h"
+
+/* This is the number to put at the beginning of the a.out file */
+long omagic = OMAGIC;
+
+/* These chars start a comment anywhere in a source file (except inside
+ another comment or a quoted string. */
+const char comment_chars[] = "#;";
+
+/* These chars only start a comment at the beginning of a line. */
+const char line_comment_chars[] = "#";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant
+ as in 0f123.456
+ or 0d1.234E-12 (see exp chars above)
+ Note: The Tahoe port doesn't support floating point constants. This is
+ consistant with 'as' If it's needed, I can always add it later. */
+const char FLT_CHARS[] = "df";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c . Ideally it shouldn't have to know about it at all,
+ but nothing is ideal around here.
+ (The tahoe has plenty of room, so the change currently isn't needed.)
+ */
+
+static struct tit t; /* A tahoe instruction after decoding. */
+
+void float_cons ();
+/* A table of pseudo ops (sans .), the function called, and an integer op
+ that the function is called with. */
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"dfloat", float_cons, 'd'},
+ {"ffloat", float_cons, 'f'},
+ {0}
+};
+
+/*
+ * For Tahoe, relative addresses of "just the right length" are pretty easy.
+ * The branch displacement is always the last operand, even in
+ * synthetic instructions.
+ * For Tahoe, we encode the relax_substateTs (in e.g. fr_substate) as:
+ *
+ * 4 3 2 1 0 bit number
+ * ---/ /--+-------+-------+-------+-------+-------+
+ * | what state ? | how long ? |
+ * ---/ /--+-------+-------+-------+-------+-------+
+ *
+ * The "how long" bits are 00=byte, 01=word, 10=long.
+ * This is a Un*x convention.
+ * Not all lengths are legit for a given value of (what state).
+ * The four states are listed below.
+ * The "how long" refers merely to the displacement length.
+ * The address usually has some constant bytes in it as well.
+ *
+
+States for Tahoe address relaxing.
+1. TAHOE_WIDTH_ALWAYS_JUMP (-)
+ Format: "b-"
+ Tahoe opcodes are: (Hex)
+ jr 11
+ jbr 11
+ Simple branch.
+ Always, 1 byte opcode, then displacement/absolute.
+ If word or longword, change opcode to brw or jmp.
+
+
+2. TAHOE_WIDTH_CONDITIONAL_JUMP (?)
+ J<cond> where <cond> is a simple flag test.
+ Format: "b?"
+ Tahoe opcodes are: (Hex)
+ jneq/jnequ 21
+ jeql/jeqlu 31
+ jgtr 41
+ jleq 51
+ jgeq 81
+ jlss 91
+ jgtru a1
+ jlequ b1
+ jvc c1
+ jvs d1
+ jlssu/jcs e1
+ jgequ/jcc f1
+ Always, you complement 4th bit to reverse the condition.
+ Always, 1-byte opcode, then 1-byte displacement.
+
+3. TAHOE_WIDTH_BIG_REV_JUMP (!)
+ Jbc/Jbs where cond tests a memory bit.
+ Format: "rlvlb!"
+ Tahoe opcodes are: (Hex)
+ jbs 0e
+ jbc 1e
+ Always, you complement 4th bit to reverse the condition.
+ Always, 1-byte opcde, longword, longword-address, 1-word-displacement
+
+4. TAHOE_WIDTH_BIG_NON_REV_JUMP (:)
+ JaoblXX/Jbssi
+ Format: "rlmlb:"
+ Tahoe opcodes are: (Hex)
+ aojlss 2f
+ jaoblss 2f
+ aojleq 3f
+ jaobleq 3f
+ jbssi 5f
+ Always, we cannot reverse the sense of the branch; we have a word
+ displacement.
+
+We need to modify the opcode is for class 1, 2 and 3 instructions.
+After relax() we may complement the 4th bit of 2 or 3 to reverse sense of
+branch.
+
+We sometimes store context in the operand literal. This way we can figure out
+after relax() what the original addressing mode was. (Was is pc_rel, or
+pc_rel_disp? That sort of thing.) */
+
+/* These displacements are relative to the START address of the
+ displacement which is at the start of the displacement, not the end of
+ the instruction. The hardware pc_rel is at the end of the instructions.
+ That's why all the displacements have the length of the displacement added
+ to them. (WF + length(word))
+
+ The first letter is Byte, Word.
+ 2nd letter is Forward, Backward. */
+#define BF (1+ 127)
+#define BB (1+-128)
+#define WF (2+ 32767)
+#define WB (2+-32768)
+/* Dont need LF, LB because they always reach. [They are coded as 0.] */
+
+#define C(a,b) ENCODE_RELAX(a,b)
+ /* This macro has no side-effects. */
+#define ENCODE_RELAX(what,length) (((what) << 2) + (length))
+#define RELAX_STATE(what) ((what) >> 2)
+#define RELAX_LENGTH(length) ((length) && 3)
+
+#define STATE_ALWAYS_BRANCH (1)
+#define STATE_CONDITIONAL_BRANCH (2)
+#define STATE_BIG_REV_BRANCH (3)
+#define STATE_BIG_NON_REV_BRANCH (4)
+#define STATE_PC_RELATIVE (5)
+
+#define STATE_BYTE (0)
+#define STATE_WORD (1)
+#define STATE_LONG (2)
+#define STATE_UNDF (3) /* Symbol undefined in pass1 */
+
+/* This is the table used by gas to figure out relaxing modes. The fields are
+ forward_branch reach, backward_branch reach, number of bytes it would take,
+ where the next biggest branch is. */
+const relax_typeS
+md_relax_table[] =
+{
+ {
+ 1, 1, 0, 0
+ }, /* error sentinel 0,0 */
+ {
+ 1, 1, 0, 0
+ }, /* unused 0,1 */
+ {
+ 1, 1, 0, 0
+ }, /* unused 0,2 */
+ {
+ 1, 1, 0, 0
+ }, /* unused 0,3 */
+ /* Unconditional branch cases "jrb"
+ The relax part is the actual displacement */
+ {
+ BF, BB, 1, C (1, 1)
+ }, /* brb B`foo 1,0 */
+ {
+ WF, WB, 2, C (1, 2)
+ }, /* brw W`foo 1,1 */
+ {
+ 0, 0, 5, 0
+ }, /* Jmp L`foo 1,2 */
+ {
+ 1, 1, 0, 0
+ }, /* unused 1,3 */
+ /* Reversible Conditional Branch. If the branch won't reach, reverse
+ it, and jump over a brw or a jmp that will reach. The relax part is the
+ actual address. */
+ {
+ BF, BB, 1, C (2, 1)
+ }, /* b<cond> B`foo 2,0 */
+ {
+ WF + 2, WB + 2, 4, C (2, 2)
+ }, /* brev over, brw W`foo, over: 2,1 */
+ {
+ 0, 0, 7, 0
+ }, /* brev over, jmp L`foo, over: 2,2 */
+ {
+ 1, 1, 0, 0
+ }, /* unused 2,3 */
+ /* Another type of reversable branch. But this only has a word
+ displacement. */
+ {
+ 1, 1, 0, 0
+ }, /* unused 3,0 */
+ {
+ WF, WB, 2, C(3, 2)
+ }, /* jbX W`foo 3,1 */
+ {
+ 0, 0, 8, 0
+ }, /* jrevX over, jmp L`foo, over: 3,2 */
+ {
+ 1, 1, 0, 0
+ }, /* unused 3,3 */
+ /* These are the non reversable branches, all of which have a word
+ displacement. If I can't reach, branch over a byte branch, to a
+ jump that will reach. The jumped branch jumps over the reaching
+ branch, to continue with the flow of the program. It's like playing
+ leap frog. */
+ {
+ 1, 1, 0, 0
+ }, /* unused 4,0 */
+ {
+ WF, WB, 2, C (4, 2)
+ }, /* aobl_ W`foo 4,1 */
+ {
+ 0, 0, 10, 0
+ }, /*aobl_ W`hop,br over,hop: jmp L^foo,over 4,2*/
+ {
+ 1, 1, 0, 0
+ }, /* unused 4,3 */
+ /* Normal displacement mode, no jumping or anything like that.
+ The relax points to one byte before the address, thats why all
+ the numbers are up by one. */
+ {
+ BF + 1, BB + 1, 2, C (5, 1)
+ }, /* B^"foo" 5,0 */
+ {
+ WF + 1, WB + 1, 3, C (5, 2)
+ }, /* W^"foo" 5,1 */
+ {
+ 0, 0, 5, 0
+ }, /* L^"foo" 5,2 */
+ {
+ 1, 1, 0, 0
+ }, /* unused 5,3 */
+};
+
+#undef C
+#undef BF
+#undef BB
+#undef WF
+#undef WB
+/* End relax stuff */
+
+static struct hash_control *op_hash = NULL; /* handle of the OPCODE hash table
+ NULL means any use before md_begin() will
+ crash */
+
+/* Init function. Build the hash table. */
+void
+md_begin()
+{
+ struct tot *tP;
+ char *errorval = "";
+ int synthetic_too = 1; /* If 0, just use real opcodes. */
+
+ if ((op_hash = hash_new())){
+ for (tP= totstrs; *tP->name && !*errorval; tP++){
+ errorval = hash_insert (op_hash, tP->name, &tP->detail);
+ }
+ if (synthetic_too){
+ for (tP = synthetic_totstrs; *tP->name && !*errorval; tP++){
+ errorval = hash_insert (op_hash, tP->name, &tP->detail);
+ }
+ }
+ }else{
+ errorval = "Virtual memory exceeded";
+ }
+ if (*errorval)
+ as_fatal(errorval);
+}/* md_begin */
+
+void
+md_end()
+{
+}/* md_end */
+
+int
+md_parse_option (argP, cntP, vecP)
+ char **argP;
+ int *cntP;
+ char ***vecP;
+{
+ char *temp_name; /* name for -t or -d options */
+ char opt;
+
+ switch (**argP){
+ case 'a':
+ as_warn("The -a option doesn't exits. (Dispite what the man page says!");
+
+ case 'J':
+ as_warn("JUMPIFY (-J) not implemented, use psuedo ops instead.");
+ break;
+
+ case 'S':
+ as_warn ("SYMBOL TABLE not implemented");
+ break; /* SYMBOL TABLE not implemented */
+
+ case 'T':
+ as_warn ("TOKEN TRACE not implemented");
+ break; /* TOKEN TRACE not implemented */
+
+ case 'd':
+ case 't':
+ opt= **argP;
+ if (**argP){ /* Rest of argument is filename. */
+ temp_name = *argP;
+ while (**argP)
+ (*argP)++;
+ }else if (*cntP){
+ while (**argP)
+ (*argP)++;
+ --(*cntP);
+ temp_name = *++(*vecP);
+ **vecP = NULL; /* Remember this is not a file-name. */
+ }else{
+ as_warn ("I expected a filename after -%c.",opt);
+ temp_name = "{absent}";
+ }
+
+ if(opt=='d')
+ as_warn ("Displacement length %s ignored!", temp_name);
+ else
+ as_warn ("I don't need or use temp. file \"%s\".", temp_name);
+ break;
+
+ case 'V':
+ as_warn ("I don't use an interpass file! -V ignored");
+ break;
+
+ default:
+ return 0;
+
+ }
+ return 1;
+}
+
+/* The functions in this section take numbers in the machine format, and
+ munges them into Tahoe byte order.
+ They exist primarily for cross assembly purpose. */
+void /* Knows about order of bytes in address. */
+md_number_to_chars (con, value, nbytes)
+ char con[]; /* Return 'nbytes' of chars here. */
+ long int value; /* The value of the bits. */
+ int nbytes; /* Number of bytes in the output. */
+{
+ int n = nbytes;
+ long int v = value;
+
+ con += nbytes - 1; /* Tahoes is (Bleah!) big endian */
+ while (nbytes--){
+ *con-- = value; /* Lint wants & MASK_CHAR. */
+ value >>= BITS_PER_CHAR;
+ }
+ /* XXX line number probably botched for this warning message. */
+ if (value != 0 && value != -1)
+ as_warn ("Displacement (%ld) long for instruction field length (%d).",v,n);
+}
+
+#ifdef comment
+void /* Knows about order of bytes in address. */
+md_number_to_imm (con, value, nbytes)
+ char con[]; /* Return 'nbytes' of chars here. */
+ long int value; /* The value of the bits. */
+ int nbytes; /* Number of bytes in the output. */
+{
+ md_number_to_chars(con, value, nbytes);
+}
+#endif /* comment */
+
+void
+ md_apply_fix(fixP, val)
+fixS *fixP;
+long val;
+{
+ char *place = fixP->fx_where + fixP->fx_frag->fr_literal;
+ md_number_to_chars(place, val, fixP->fx_size);
+ return;
+} /* md_apply_fix() */
+
+void /* Knows about order of bytes in address. */
+md_number_to_disp (con, value, nbytes)
+ char con[]; /* Return 'nbytes' of chars here. */
+ long int value; /* The value of the bits. */
+ int nbytes; /* Number of bytes in the output. */
+{
+ md_number_to_chars(con, value, nbytes);
+}
+
+void /* Knows about order of bytes in address. */
+md_number_to_field (con, value, nbytes)
+ char con[]; /* Return 'nbytes' of chars here. */
+ long int value; /* The value of the bits. */
+ int nbytes; /* Number of bytes in the output. */
+{
+ md_number_to_chars(con, value, nbytes);
+}
+
+/* Put the bits in an order that a tahoe will understand, despite the ordering
+ of the native machine.
+ On Tahoe: first 4 bytes are normal unsigned big endian long,
+ next three bytes are symbolnum, in kind of 3 byte big endian (least sig. byte last).
+ The last byte is broken up with bit 7 as pcrel,
+ bits 6 & 5 as length,
+ bit 4 as extern and the last nibble as 'undefined'. */
+
+#if comment
+void
+md_ri_to_chars (ri_p, ri)
+ struct relocation_info *ri_p, ri;
+{
+ byte the_bytes[sizeof(struct relocation_info)];
+ /* The reason I can't just encode these directly into ri_p is that
+ ri_p may point to ri. */
+
+ /* This is easy */
+ md_number_to_chars (the_bytes, ri.r_address, sizeof(ri.r_address));
+
+ /* now the fun stuff */
+ the_bytes[4] = (ri.r_symbolnum >> 16) & 0x0ff;
+ the_bytes[5] = (ri.r_symbolnum >> 8) & 0x0ff;
+ the_bytes[6] = ri.r_symbolnum & 0x0ff;
+ the_bytes[7] = (((ri.r_extern << 4) & 0x10) | ((ri.r_length << 5) & 0x60) |
+ ((ri.r_pcrel << 7) & 0x80)) & 0xf0;
+
+ bcopy (the_bytes, (char *) ri_p, sizeof (struct relocation_info));
+}
+#endif /* comment */
+
+/* Put the bits in an order that a tahoe will understand, despite the ordering
+ of the native machine.
+ On Tahoe: first 4 bytes are normal unsigned big endian long,
+ next three bytes are symbolnum, in kind of 3 byte big endian (least sig. byte last).
+ The last byte is broken up with bit 7 as pcrel,
+ bits 6 & 5 as length,
+ bit 4 as extern and the last nibble as 'undefined'. */
+
+void tc_aout_fix_to_chars(where, fixP, segment_address_in_file)
+char *where;
+fixS *fixP;
+relax_addressT segment_address_in_file;
+{
+ /*
+ * In: length of relocation (or of address) in chars: 1, 2 or 4.
+ * Out: GNU LD relocation length code: 0, 1, or 2.
+ */
+
+ static unsigned char nbytes_r_length[] = { 42, 0, 1, 42, 2 };
+ long r_symbolnum;
+
+ know(fixP->fx_addsy != NULL);
+
+ md_number_to_chars(where,
+ fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file,
+ 4);
+
+ r_symbolnum = (S_IS_DEFINED(fixP->fx_addsy)
+ ? S_GET_TYPE(fixP->fx_addsy)
+ : fixP->fx_addsy->sy_number);
+
+ where[4] = (r_symbolnum >> 16) & 0x0ff;
+ where[5] = (r_symbolnum >> 8) & 0x0ff;
+ where[6] = r_symbolnum & 0x0ff;
+ where[7] = (((fixP->fx_pcrel << 7) & 0x80)
+ | ((nbytes_r_length[fixP->fx_size] << 5) & 0x60)
+ | ((!S_IS_DEFINED(fixP->fx_addsy) << 4) & 0x10));
+
+ return;
+} /* tc_aout_fix_to_chars() */
+
+/* Relocate byte stuff */
+
+/* This is for broken word. */
+const int md_short_jump_size = 3;
+
+void
+md_create_short_jump (ptr, from_addr, to_addr, frag, to_symbol)
+ char *ptr;
+ long from_addr, to_addr;
+ fragS *frag;
+ symbolS *to_symbol;
+{
+ long offset;
+
+ offset = to_addr - (from_addr + 1);
+ *ptr++ = TAHOE_BRW;
+ md_number_to_chars (ptr, offset, 2);
+}
+
+const int md_long_jump_size = 6;
+const int md_reloc_size = 8; /* Size of relocation record */
+
+void
+md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol)
+ char *ptr;
+ long from_addr, to_addr;
+ fragS *frag;
+ symbolS *to_symbol;
+{
+ long offset;
+
+ offset = to_addr - (from_addr + 4);
+ *ptr++ = TAHOE_JMP;
+ *ptr++ = TAHOE_PC_REL_LONG;
+ md_number_to_chars (ptr, offset, 4);
+}
+
+/*
+ * md_estimate_size_before_relax()
+ *
+ * Called just before relax().
+ * Any symbol that is now undefined will not become defined, so we assumed
+ * that it will be resolved by the linker.
+ * Return the correct fr_subtype in the frag, for relax()
+ * Return the initial "guess for fr_var" to caller. (How big I think this
+ * will be.)
+ * The guess for fr_var is ACTUALLY the growth beyond fr_fix.
+ * Whatever we do to grow fr_fix or fr_var contributes to our returned value.
+ * Although it may not be explicit in the frag, pretend fr_var starts with a
+ * 0 value.
+ */
+int
+md_estimate_size_before_relax (fragP, segment_type)
+ register fragS *fragP;
+ segT segment_type; /* N_DATA or N_TEXT. */
+{
+ register char *p;
+ register int old_fr_fix;
+/* int pc_rel; FIXME: remove this */
+
+ old_fr_fix = fragP->fr_fix;
+ switch (fragP->fr_subtype){
+ case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_UNDF):
+ if (S_GET_SEGMENT(fragP->fr_symbol) == segment_type) {
+ /* The symbol was in the same segment as the opcode, and it's
+ a real pc_rel case so it's a relaxable case. */
+ fragP->fr_subtype = ENCODE_RELAX(STATE_PC_RELATIVE, STATE_BYTE);
+ }else{
+ /* This case is still undefined, so asume it's a long word for the
+ linker to fix. */
+ p = fragP->fr_literal + old_fr_fix;
+ *p |= TAHOE_PC_OR_LONG;
+ /* We now know how big it will be, one long word. */
+ fragP->fr_fix += 1 + 4;
+ fix_new (fragP, old_fr_fix + 1, 4, fragP->fr_symbol, 0,
+ fragP->fr_offset, 1, NO_RELOC);
+ frag_wane (fragP);
+ }
+ break;
+
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_UNDF):
+ if (S_GET_SEGMENT(fragP->fr_symbol) == segment_type){
+ fragP->fr_subtype = ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_BYTE);
+ }else{
+ p = fragP->fr_literal + old_fr_fix;
+ *fragP->fr_opcode ^= 0x10; /* Reverse sense of branch. */
+ *p++ = 6;
+ *p++ = TAHOE_JMP;
+ *p++ = TAHOE_PC_REL_LONG;
+ fragP->fr_fix += 1 + 1 + 1 + 4;
+ fix_new (fragP, old_fr_fix + 3, 4, fragP->fr_symbol, 0,
+ fragP->fr_offset, 1, NO_RELOC);
+ frag_wane (fragP);
+ }
+ break;
+
+ case ENCODE_RELAX (STATE_BIG_REV_BRANCH, STATE_UNDF):
+ if (S_GET_SEGMENT(fragP->fr_symbol) == segment_type){
+ fragP->fr_subtype =
+ ENCODE_RELAX (STATE_BIG_REV_BRANCH, STATE_WORD);
+ }else{
+ p = fragP->fr_literal + old_fr_fix;
+ *fragP->fr_opcode ^= 0x10; /* Reverse sense of branch. */
+ *p++ = 0;
+ *p++ = 6;
+ *p++ = TAHOE_JMP;
+ *p++ = TAHOE_PC_REL_LONG;
+ fragP->fr_fix += 2 + 2 + 4;
+ fix_new (fragP, old_fr_fix + 4, 4, fragP->fr_symbol, 0,
+ fragP->fr_offset, 1, NO_RELOC);
+ frag_wane (fragP);
+ }
+ break;
+
+ case ENCODE_RELAX (STATE_BIG_NON_REV_BRANCH, STATE_UNDF):
+ if (S_GET_SEGMENT(fragP->fr_symbol) == segment_type){
+ fragP->fr_subtype = ENCODE_RELAX (STATE_BIG_NON_REV_BRANCH, STATE_WORD);
+ }else{
+ p = fragP->fr_literal + old_fr_fix;
+ *p++ = 2;
+ *p++ = 0;
+ *p++ = TAHOE_BRB;
+ *p++ = 6;
+ *p++ = TAHOE_JMP;
+ *p++ = TAHOE_PC_REL_LONG;
+ fragP->fr_fix += 2 + 2 + 2 + 4;
+ fix_new (fragP, old_fr_fix + 6, 4, fragP->fr_symbol, 0,
+ fragP->fr_offset, 1, NO_RELOC);
+ frag_wane (fragP);
+ }
+ break;
+
+ case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_UNDF):
+ if (S_GET_SEGMENT(fragP->fr_symbol) == segment_type){
+ fragP->fr_subtype = ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_BYTE);
+ }else{
+ p = fragP->fr_literal + old_fr_fix;
+ *fragP->fr_opcode = TAHOE_JMP;
+ *p++ = TAHOE_PC_REL_LONG;
+ fragP->fr_fix += 1 + 4;
+ fix_new (fragP, old_fr_fix + 1, 4, fragP->fr_symbol, 0,
+ fragP->fr_offset, 1, NO_RELOC);
+ frag_wane (fragP);
+ }
+ break;
+
+ default:
+ break;
+ }
+ return (fragP->fr_var + fragP->fr_fix - old_fr_fix);
+} /* md_estimate_size_before_relax() */
+
+/*
+ * md_convert_frag();
+ *
+ * Called after relax() is finished.
+ * In: Address of frag.
+ * fr_type == rs_machine_dependent.
+ * fr_subtype is what the address relaxed to.
+ *
+ * Out: Any fixSs and constants are set up.
+ * Caller will turn frag into a ".space 0".
+ */
+void
+md_convert_frag (headers, fragP)
+object_headers *headers;
+ register fragS *fragP;
+{
+ register char *addressP; /* -> _var to change. */
+ register char *opcodeP; /* -> opcode char(s) to change. */
+ register short int length_code; /* 2=long 1=word 0=byte */
+ register short int extension = 0; /* Size of relaxed address.
+ Added to fr_fix: incl. ALL var chars. */
+ register symbolS *symbolP;
+ register long int where;
+ register long int address_of_var;
+ /* Where, in file space, is _var of *fragP? */
+ register long int target_address;
+ /* Where, in file space, does addr point? */
+
+ know (fragP->fr_type == rs_machine_dependent);
+ length_code = RELAX_LENGTH(fragP->fr_subtype);
+ know (length_code >= 0 && length_code < 3);
+ where = fragP->fr_fix;
+ addressP = fragP->fr_literal + where;
+ opcodeP = fragP->fr_opcode;
+ symbolP = fragP->fr_symbol;
+ know(symbolP);
+ target_address = S_GET_VALUE(symbolP) + fragP->fr_offset;
+ address_of_var = fragP->fr_address + where;
+ switch (fragP->fr_subtype){
+ case ENCODE_RELAX(STATE_PC_RELATIVE, STATE_BYTE):
+ /* *addressP holds the registers number, plus 0x10, if it's deferred
+ mode. To set up the right mode, just OR the size of this displacement */
+ /* Byte displacement. */
+ *addressP++ |= TAHOE_PC_OR_BYTE;
+ *addressP = target_address - (address_of_var + 2);
+ extension = 2;
+ break;
+
+ case ENCODE_RELAX(STATE_PC_RELATIVE, STATE_WORD):
+ /* Word displacement. */
+ *addressP++ |= TAHOE_PC_OR_WORD;
+ md_number_to_chars(addressP, target_address - (address_of_var + 3), 2);
+ extension = 3;
+ break;
+
+ case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_LONG):
+ /* Long word displacement. */
+ *addressP++ |= TAHOE_PC_OR_LONG;
+ md_number_to_chars(addressP, target_address - (address_of_var + 5), 4);
+ extension = 5;
+ break;
+
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_BYTE):
+ *addressP = target_address - (address_of_var + 1);
+ extension = 1;
+ break;
+
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_WORD):
+ *opcodeP ^= 0x10; /* Reverse sense of test. */
+ *addressP++ = 3; /* Jump over word branch */
+ *addressP++ = TAHOE_BRW;
+ md_number_to_chars (addressP, target_address - (address_of_var + 4), 2);
+ extension = 4;
+ break;
+
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_LONG):
+ *opcodeP ^= 0x10; /* Reverse sense of test. */
+ *addressP++ = 6;
+ *addressP++ = TAHOE_JMP;
+ *addressP++ = TAHOE_PC_REL_LONG;
+ md_number_to_chars (addressP, target_address, 4);
+ extension = 7;
+ break;
+
+ case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_BYTE):
+ *addressP = target_address - (address_of_var + 1);
+ extension = 1;
+ break;
+
+ case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_WORD):
+ *opcodeP = TAHOE_BRW;
+ md_number_to_chars (addressP, target_address - (address_of_var + 2), 2);
+ extension = 2;
+ break;
+
+ case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_LONG):
+ *opcodeP = TAHOE_JMP;
+ *addressP++ = TAHOE_PC_REL_LONG;
+ md_number_to_chars(addressP, target_address - (address_of_var + 5), 4);
+ extension = 5;
+ break;
+
+ case ENCODE_RELAX (STATE_BIG_REV_BRANCH, STATE_WORD):
+ md_number_to_chars (addressP, target_address - (address_of_var + 2), 2);
+ extension = 2;
+ break;
+
+ case ENCODE_RELAX (STATE_BIG_REV_BRANCH, STATE_LONG):
+ *opcodeP ^= 0x10;
+ *addressP++ = 0;
+ *addressP++ = 6;
+ *addressP++ = TAHOE_JMP;
+ *addressP++ = TAHOE_PC_REL_LONG;
+ md_number_to_chars (addressP, target_address, 4);
+ extension = 8;
+ break;
+
+ case ENCODE_RELAX (STATE_BIG_NON_REV_BRANCH, STATE_WORD):
+ md_number_to_chars (addressP, target_address - (address_of_var + 2), 2);
+ extension = 2;
+ break;
+
+ case ENCODE_RELAX (STATE_BIG_NON_REV_BRANCH, STATE_LONG):
+ *addressP++ = 0;
+ *addressP++ = 2;
+ *addressP++ = TAHOE_BRB;
+ *addressP++ = 6;
+ *addressP++ = TAHOE_JMP;
+ *addressP++ = TAHOE_PC_REL_LONG;
+ md_number_to_chars (addressP, target_address, 4);
+ extension = 10;
+ break;
+
+ default:
+ BAD_CASE (fragP->fr_subtype);
+ break;
+ }
+ fragP->fr_fix += extension;
+} /* md_convert_frag */
+
+
+/* This is the stuff for md_assemble. */
+#define FP_REG 13
+#define SP_REG 14
+#define PC_REG 15
+#define BIGGESTREG PC_REG
+
+/*
+ * Parse the string pointed to by START
+ * If it represents a valid register, point START to the character after
+ * the last valid register char, and return the register number (0-15).
+ * If invalid, leave START alone, return -1.
+ * The format has to be exact. I don't do things like eat leading zeros
+ * or the like.
+ * Note: This doesn't check for the next character in the string making
+ * this invalid. Ex: R123 would return 12, it's the callers job to check
+ * what start is point to apon return.
+ *
+ * Valid registers are R1-R15, %1-%15, FP (13), SP (14), PC (15)
+ * Case doesn't matter.
+ */
+int
+tahoe_reg_parse(start)
+ char **start; /* A pointer to the string to parse. */
+{
+ register char *regpoint = *start;
+ register int regnum = -1;
+
+ switch(*regpoint++){
+ case '%': /* Registers can start with a %,
+ R or r, and then a number. */
+ case 'R':
+ case 'r':
+ if (isdigit(*regpoint)){
+ /* Got the first digit. */
+ regnum = *regpoint++ - '0';
+ if ((regnum == 1) && isdigit(*regpoint)){
+ /* Its a two digit number. */
+ regnum = 10 + (*regpoint++ - '0');
+ if (regnum > BIGGESTREG){ /* Number too big? */
+ regnum = -1;
+ }
+ }
+ }
+ break;
+ case 'F': /* Is it the FP */
+ case 'f':
+ switch(*regpoint++){
+ case 'p':
+ case 'P':
+ regnum = FP_REG;
+ }
+ break;
+ case 's': /* How about the SP */
+ case 'S':
+ switch(*regpoint++){
+ case 'p':
+ case 'P':
+ regnum = SP_REG;
+ }
+ break;
+ case 'p': /* OR the PC even */
+ case 'P':
+ switch(*regpoint++){
+ case 'c':
+ case 'C':
+ regnum = PC_REG;
+ }
+ break;
+ }
+
+ if (regnum != -1){ /* No error, so move string pointer */
+ *start = regpoint;
+ }
+ return regnum; /* Return results */
+} /* tahoe_reg_parse */
+
+/*
+ * This chops up an operand and figures out its modes and stuff.
+ * It's a little touchy about extra characters.
+ * Optex to start with one extra character so it can be overwritten for
+ * the backward part of the parsing.
+ * You can't put a bunch of extra characters in side to
+ * make the command look cute. ie: * foo ( r1 ) [ r0 ]
+ * If you like doing a lot of typing, try COBOL!
+ * Actually, this parser is a little weak all around. It's designed to be
+ * used with compliers, so I emphisise correct decoding of valid code quickly
+ * rather that catching every possable error.
+ * Note: This uses the expression function, so save input_line_pointer before
+ * calling.
+ *
+ * Sperry defines the semantics of address modes (and values)
+ * by a two-letter code, explained here.
+ *
+ * letter 1: access type
+ *
+ * a address calculation - no data access, registers forbidden
+ * b branch displacement
+ * m read - let go of bus - write back "modify"
+ * r read
+ * w write
+ * v bit field address: like 'a' but registers are OK
+ *
+ * letter 2: data type (i.e. width, alignment)
+ *
+ * b byte
+ * w word
+ * l longword
+ * q quadword (Even regs < 14 allowed) (if 12, you get a warning)
+ * - unconditional synthetic jbr operand
+ * ? simple synthetic reversable branch operand
+ * ! complex synthetic reversable branch operand
+ * : complex synthetic non-reversable branch operand
+ *
+ * The '-?!:' letter 2's are not for external consumption. They are used
+ * by GAS for psuedo ops relaxing code.
+ *
+ * After parsing topP has:
+ *
+ * top_ndx: -1, or the index register. eg 7=[R7]
+ * top_reg: -1, or register number. eg 7 = R7 or (R7)
+ * top_mode: The addressing mode byte. This byte, defines which of
+ * the 11 modes opcode is.
+ * top_access: Access type wanted for this opperand 'b'branch ' '
+ * no-instruction 'amrvw'
+ * top_width: Operand width expected, one of "bwlq?-:!"
+ * exp_of_operand: The expression as parsed by expression()
+ * top_dispsize: Number of bytes in the displacement if we can figure it
+ * out and it's relavent.
+ *
+ * Need syntax checks built.
+ */
+
+void
+tip_op (optex,topP)
+ char *optex; /* The users text input, with one leading character */
+ struct top *topP;/* The tahoe instruction with some fields already set:
+ in: access, width
+ out: ndx, reg, mode, error, dispsize */
+
+{
+ int mode = 0; /* This operand's mode. */
+ char segfault = *optex; /* To keep the back parsing from freaking. */
+ char *point = optex+1; /* Parsing from front to back. */
+ char *end; /* Parsing from back to front. */
+ int reg = -1; /* major register, -1 means absent */
+ int imreg = -1; /* Major register in immediate mode */
+ int ndx = -1; /* index register number, -1 means absent */
+ char dec_inc = ' '; /* Is the SP auto-incremented '+' or
+ auto-decremented '-' or neither ' '. */
+ int immediate = 0; /* 1 if '$' immediate mode */
+ int call_width = 0; /* If the caller casts the displacement */
+ int abs_width = 0; /* The width of the absolute displacment */
+ int com_width = 0; /* Displacement width required by branch */
+ int deferred = 0; /* 1 if '*' deferral is used */
+ byte disp_size = 0; /* How big is this operand. 0 == don't know */
+ char *op_bad = ""; /* Bad operand error */
+
+ char *tp, *temp, c; /* Temporary holders */
+
+ char access = topP->top_access; /* Save on a deref. */
+ char width = topP->top_width;
+
+ int really_none = 0; /* Empty expressions evaluate to 0
+ but I need to know if it's there or not */
+ expressionS *expP; /* -> expression values for this operand */
+
+ /* Does this command restrict the displacement size. */
+ if (access == 'b')
+ com_width = (width == 'b' ? 1 :
+ (width == 'w' ? 2 :
+ (width == 'l' ? 4 : 0)));
+
+ *optex = '\0'; /* This is kind of a back stop for all
+ the searches to fail on if needed.*/
+ if (*point == '*') { /* A dereference? */
+ deferred = 1;
+ point++;
+ }
+
+ /* Force words into a certain mode */
+ /* Bitch, Bitch, Bitch! */
+ /*
+ * Using the ^ operator is ambigous. If I have an absolute label
+ * called 'w' set to, say 2, and I have the expression 'w^1', do I get
+ * 1, forced to be in word displacement mode, or do I get the value of
+ * 'w' or'ed with 1 (3 in this case).
+ * The default is 'w' as an offset, so that's what I use.
+ * Stick with `, it does the same, and isn't ambig.
+ */
+
+ if (*point != '\0' && ((point[1] == '^') || (point[1] == '`')))
+ switch(*point){
+ case 'b':
+ case 'B':
+ case 'w':
+ case 'W':
+ case 'l':
+ case 'L':
+ if (com_width)
+ as_warn("Casting a branch displacement is bad form, and is ignored.");
+ else{
+ c = (isupper(*point) ? tolower(*point) : *point);
+ call_width = ((c == 'b') ? 1 :
+ ((c == 'w') ? 2 : 4));
+ }
+ point += 2;
+ break;
+ }
+
+ /* Setting immediate mode */
+ if (*point == '$'){
+ immediate = 1;
+ point++;
+ }
+
+ /*
+ * I've pulled off all the easy stuff off the front, move to the end and
+ * yank.
+ */
+
+ for(end = point;*end != '\0';end++) /* Move to the end. */
+ ;
+
+ if(end != point) /* Null string? */
+ end--;
+
+ if (end > point && *end == ' ' && end[-1] != '\'')
+ end--; /* Hop white space */
+
+ /* Is this an index reg. */
+ if ((*end == ']') && (end[-1] != '\'')){
+ temp = end;
+
+ /* Find opening brace. */
+ for(--end;(*end != '[' && end != point);end--)
+ ;
+
+ /* If I found the opening brace, get the index register number. */
+ if (*end == '['){
+ tp = end + 1; /* tp should point to the start of a reg. */
+ ndx = tahoe_reg_parse(&tp);
+ if (tp != temp){ /* Reg. parse error. */
+ ndx = -1;
+ } else {
+ end--; /* Found it, move past brace. */
+ }
+ if (ndx == -1){
+ op_bad = "Couldn't parse the [index] in this operand.";
+ end = point; /* Force all the rest of the tests to fail. */
+ }
+ }else{
+ op_bad = "Couldn't find the opening '[' for the index of this operand.";
+ end = point; /* Force all the rest of the tests to fail. */
+ }
+ }
+
+ /* Post increment? */
+ if (*end == '+'){
+ dec_inc = '+';
+/* was: *end--; */
+ end--;
+ }
+
+ /* register in parens? */
+ if ((*end == ')') && (end[-1] != '\'')){
+ temp = end;
+
+ /* Find opening paren. */
+ for(--end;(*end != '(' && end != point);end--)
+ ;
+
+ /* If I found the opening paren, get the register number. */
+ if (*end == '('){
+ tp = end + 1;
+ reg = tahoe_reg_parse(&tp);
+ if (tp != temp){
+ /* Not a register, but could be part of the expression. */
+ reg = -1;
+ end = temp; /* Rest the pointer back */
+ } else {
+ end--; /* Found the reg. move before opening paren. */
+ }
+ }else{
+ op_bad = "Couldn't find the opening '(' for the deref of this operand.";
+ end = point; /* Force all the rest of the tests to fail. */
+ }
+ }
+
+ /* Pre decrement? */
+ if (*end == '-'){
+ if (dec_inc != ' '){
+ op_bad = "Operand can't be both pre-inc and post-dec.";
+ end = point;
+ }else{
+ dec_inc = '-';
+/* was: *end--; */
+ end--;
+ }
+ }
+
+ /*
+ * Everything between point and end is the 'expression', unless it's
+ * a register name.
+ */
+
+ c = end[1];
+ end[1] = '\0';
+
+ tp = point;
+ imreg = tahoe_reg_parse(&point); /* Get the immediate register
+ if it is there.*/
+ if (*point != '\0'){
+ /* If there is junk after point, then the it's not immediate reg. */
+ point = tp;
+ imreg = -1;
+ }
+
+ if (imreg != -1 && reg != -1)
+ op_bad = "I parsed 2 registers in this operand.";
+
+ /*
+ * Evaluate whats left of the expression to see if it's valid.
+ * Note again: This assumes that the calling expression has saved
+ * input_line_pointer. (Nag, nag, nag!)
+ */
+
+ if (*op_bad == '\0'){
+ /* statement has no syntax goofs yet: lets sniff the expression */
+ input_line_pointer = point;
+ expP = &(topP->exp_of_operand);
+ switch (expression (expP)){
+ /* If expression == SEG_PASS1, expression() will have set
+ need_pass_2 = 1. */
+ case SEG_ABSENT:
+ /* No expression. For BSD4.2 compatibility, missing expression is
+ absolute 0 */
+ expP->X_seg = SEG_ABSOLUTE;
+ expP->X_add_number = 0;
+ really_none = 1;
+ case SEG_ABSOLUTE:
+ /* for SEG_ABSOLUTE, we shouldnt need to set X_subtract_symbol,
+ X_add_symbol to any particular value. */
+ /* But, we will program defensively. Since this situation occurs
+ rarely so it costs us little to do so. */
+ expP->X_add_symbol = NULL;
+ expP->X_subtract_symbol = NULL;
+ /* How many bytes are needed to express this abs value? */
+ abs_width =
+ ((((expP->X_add_number & 0xFFFFFF80) == 0) ||
+ ((expP->X_add_number & 0xFFFFFF80) == 0xFFFFFF80)) ? 1 :
+ (((expP->X_add_number & 0xFFFF8000) == 0) ||
+ ((expP->X_add_number & 0xFFFF8000) == 0xFFFF8000)) ? 2 : 4);
+ case SEG_TEXT:
+ case SEG_DATA:
+ case SEG_BSS:
+ case SEG_UNKNOWN:
+ break;
+
+ case SEG_DIFFERENCE:
+ /*
+ * Major bug. We can't handle the case of a
+ * SEG_DIFFERENCE expression in a synthetic opcode
+ * variable-length instruction.
+ * We don't have a frag type that is smart enough to
+ * relax a SEG_DIFFERENCE, and so we just force all
+ * SEG_DIFFERENCEs to behave like SEG_PASS1s.
+ * Clearly, if there is a demand we can invent a new or
+ * modified frag type and then coding up a frag for this
+ * case will be easy. SEG_DIFFERENCE was invented for the
+ * .words after a CASE opcode, and was never intended for
+ * instruction operands.
+ */
+ need_pass_2 = 1;
+ case SEG_PASS1:
+ op_bad = "Can't relocate expression error.";
+ break;
+
+ case SEG_BIG:
+ /* This is an error. Tahoe doesn't allow any expressions
+ bigger that a 32 bit long word. Any bigger has to be referenced
+ by address. */
+ op_bad = "Expression is too large for a 32 bits.";
+ break;
+
+ default:
+ as_fatal("Complier Bug: I got segment %d in tip_op.",expP->X_seg);
+ break;
+ }
+ if (*input_line_pointer != '\0'){
+ op_bad = "Junk at end of expression.";
+ }
+ }
+
+ end[1] = c;
+
+ /* I'm done, so restore optex */
+ *optex = segfault;
+
+
+ /*
+ * At this point in the game, we (in theory) have all the components of
+ * the operand at least parsed. Now it's time to check for syntax/semantic
+ * errors, and build the mode.
+ * This is what I have:
+ * deferred = 1 if '*'
+ * call_width = 0,1,2,4
+ * abs_width = 0,1,2,4
+ * com_width = 0,1,2,4
+ * immediate = 1 if '$'
+ * ndx = -1 or reg num
+ * dec_inc = '-' or '+' or ' '
+ * reg = -1 or reg num
+ * imreg = -1 or reg num
+ * topP->exp_of_operand
+ * really_none
+ */
+ /* Is there a displacement size? */
+ disp_size = (call_width ? call_width :
+ (com_width ? com_width :
+ abs_width ? abs_width : 0));
+
+ if (*op_bad == '\0'){
+ if (imreg != -1){
+ /* Rn */
+ mode = TAHOE_DIRECT_REG;
+ if (deferred || immediate || (dec_inc != ' ') ||
+ (reg != -1) || !really_none)
+ op_bad = "Syntax error in direct register mode.";
+ else if (ndx != -1)
+ op_bad = "You can't index a register in direct register mode.";
+ else if (imreg == SP_REG && access == 'r')
+ op_bad =
+ "SP can't be the source operand with direct register addressing.";
+ else if (access == 'a')
+ op_bad = "Can't take the address of a register.";
+ else if (access == 'b')
+ op_bad = "Direct Register can't be used in a branch.";
+ else if (width == 'q' && ((imreg % 2) || (imreg > 13)))
+ op_bad = "For quad access, the register must be even and < 14.";
+ else if (call_width)
+ op_bad = "You can't cast a direct register.";
+
+ if (*op_bad == '\0'){
+ /* No errors, check for warnings */
+ if (width == 'q' && imreg == 12)
+ as_warn("Using reg 14 for quadwords can tromp the FP register.");
+
+ reg = imreg;
+ }
+
+ /* We know: imm = -1 */
+ }else if (dec_inc == '-'){
+ /* -(SP) */
+ mode = TAHOE_AUTO_DEC;
+ if (deferred || immediate || !really_none)
+ op_bad = "Syntax error in auto-dec mode.";
+ else if (ndx != -1)
+ op_bad = "You can't have an index auto dec mode.";
+ else if (access == 'r')
+ op_bad = "Auto dec mode cant be used for reading.";
+ else if (reg != SP_REG)
+ op_bad = "Auto dec only works of the SP register.";
+ else if (access == 'b')
+ op_bad = "Auto dec can't be used in a branch.";
+ else if (width == 'q')
+ op_bad = "Auto dec won't work with quadwords.";
+
+ /* We know: imm = -1, dec_inc != '-' */
+ }else if (dec_inc == '+'){
+ if (immediate || !really_none)
+ op_bad = "Syntax error in one of the auto-inc modes.";
+ else if (deferred){
+ /* *(SP)+ */
+ mode = TAHOE_AUTO_INC_DEFERRED;
+ if (reg != SP_REG)
+ op_bad = "Auto inc deferred only works of the SP register.";
+ else if (ndx != -1)
+ op_bad = "You can't have an index auto inc deferred mode.";
+ else if (access == 'b')
+ op_bad = "Auto inc can't be used in a branch.";
+ }else{
+ /* (SP)+ */
+ mode = TAHOE_AUTO_INC;
+ if (access == 'm' || access == 'w')
+ op_bad = "You can't write to an auto inc register.";
+ else if (reg != SP_REG)
+ op_bad = "Auto inc only works of the SP register.";
+ else if (access == 'b')
+ op_bad = "Auto inc can't be used in a branch.";
+ else if (width == 'q')
+ op_bad = "Auto inc won't work with quadwords.";
+ else if (ndx != -1)
+ op_bad = "You can't have an index in auto inc mode.";
+ }
+
+ /* We know: imm = -1, dec_inc == ' ' */
+ }else if (reg != -1){
+ if ((ndx != -1) && (reg == SP_REG))
+ op_bad = "You can't index the sp register.";
+ if (deferred){
+ /* *<disp>(Rn) */
+ mode = TAHOE_REG_DISP_DEFERRED;
+ if (immediate)
+ op_bad = "Syntax error in register displaced mode.";
+ }else if (really_none){
+ /* (Rn) */
+ mode = TAHOE_REG_DEFERRED;
+ /* if reg = SP then cant be indexed */
+ }else{
+ /* <disp>(Rn) */
+ mode = TAHOE_REG_DISP;
+ }
+
+ /* We know: imm = -1, dec_inc == ' ', Reg = -1 */
+ }else{
+ if (really_none)
+ op_bad = "An offest is needed for this operand.";
+ if (deferred && immediate){
+ /* *$<ADDR> */
+ mode = TAHOE_ABSOLUTE_ADDR;
+ disp_size = 4;
+ }else if (immediate){
+ /* $<disp> */
+ mode = TAHOE_IMMEDIATE;
+ if (ndx != -1)
+ op_bad = "You can't index a register in immediate mode.";
+ if (access == 'a')
+ op_bad = "Immediate access can't be used as an address.";
+ /* ponder the wisdom of a cast because it doesn't do any good. */
+ }else if (deferred){
+ /* *<disp> */
+ mode = TAHOE_DISP_REL_DEFERRED;
+ }else{
+ /* <disp> */
+ mode = TAHOE_DISPLACED_RELATIVE;
+ }
+ }
+ }
+
+ /*
+ * At this point, all the errors we can do have be checked for.
+ * We can build the 'top'. */
+
+ topP->top_ndx = ndx;
+ topP->top_reg = reg;
+ topP->top_mode = mode;
+ topP->top_error = op_bad;
+ topP->top_dispsize = disp_size;
+} /* tip_op */
+
+/*
+ * t i p ( )
+ *
+ * This converts a string into a tahoe instruction.
+ * The string must be a bare single instruction in tahoe (with BSD4 frobs)
+ * format.
+ * It provides at most one fatal error message (which stops the scan)
+ * some warning messages as it finds them.
+ * The tahoe instruction is returned in exploded form.
+ *
+ * The exploded instruction is returned to a struct tit of your choice.
+ * #include "tahoe-inst.h" to know what a struct tit is.
+ *
+ */
+
+static void
+tip (titP, instring)
+ struct tit *titP; /* We build an exploded instruction here. */
+ char *instring; /* Text of a vax instruction: we modify. */
+{
+ register struct tot_wot *twP = NULL; /* How to bit-encode this opcode. */
+ register char *p; /* 1/skip whitespace.2/scan vot_how */
+ register char *q; /* */
+ register unsigned char count; /* counts number of operands seen */
+ register struct top *operandp;/* scan operands in struct tit */
+ register char *alloperr = ""; /* error over all operands */
+ register char c; /* Remember char, (we clobber it
+ with '\0' temporarily). */
+ char *save_input_line_pointer;
+
+ if (*instring == ' ')
+ ++instring; /* Skip leading whitespace. */
+ for (p = instring; *p && *p != ' '; p++)
+ ; /* MUST end in end-of-string or
+ exactly 1 space. */
+ /* Scanned up to end of operation-code. */
+ /* Operation-code is ended with whitespace. */
+ if (p == instring){
+ titP->tit_error = "No operator";
+ count = 0;
+ titP->tit_opcode = 0;
+ } else {
+ c = *p;
+ *p = '\0';
+ /*
+ * Here with instring pointing to what better be an op-name, and p
+ * pointing to character just past that.
+ * We trust instring points to an op-name, with no whitespace.
+ */
+ twP = (struct tot_wot *) hash_find(op_hash, instring);
+ *p = c; /* Restore char after op-code. */
+ if (twP == 0){
+ titP->tit_error = "Unknown operator";
+ count = 0;
+ titP->tit_opcode = 0;
+ }else{
+ /*
+ * We found a match! So lets pick up as many operands as the
+ * instruction wants, and even gripe if there are too many.
+ * We expect comma to seperate each operand.
+ * We let instring track the text, while p tracks a part of the
+ * struct tot.
+ */
+
+ count = 0; /* no operands seen yet */
+ instring = p+(*p!='\0'); /* point past the operation code */
+ /* tip_op() screws with the input_line_pointer, so save it before
+ I jump in */
+ save_input_line_pointer = input_line_pointer;
+ for (p = twP->args, operandp = titP->tit_operand;
+ !*alloperr && *p;
+ operandp++, p += 2){
+ /*
+ * Here to parse one operand. Leave instring pointing just
+ * past any one ',' that marks the end of this operand.
+ */
+ if (!p[1])
+ as_fatal("Compiler bug: ODD number of bytes in arg structure %s.",
+ twP->args);
+ else if (*instring){
+ for (q = instring; (*q != ',' && *q != '\0'); q++){
+ if (*q == '\'' && q[1] != '\0') /* Jump quoted characters */
+ q++;
+ }
+ c = *q;
+ /*
+ * Q points to ',' or '\0' that ends argument. C is that
+ * character.
+ */
+ *q = '\0';
+ operandp->top_access = p[0];
+ operandp->top_width = p[1];
+ tip_op(instring-1, operandp);
+ *q = c; /* Restore input text. */
+ if (*(operandp->top_error)){
+ alloperr = operandp->top_error;
+ }
+ instring = q + (c ? 1 : 0); /* next operand (if any) */
+ count++; /* won another argument, may have an operr */
+ }else
+ alloperr = "Not enough operands";
+ }
+ /* Restore the pointer. */
+ input_line_pointer = save_input_line_pointer;
+
+ if (!*alloperr){
+ if (*instring == ' ')
+ instring++; /* Skip whitespace. */
+ if (*instring)
+ alloperr = "Too many operands";
+ }
+ titP->tit_error = alloperr;
+ }
+ }
+
+ titP->tit_opcode = twP->code; /* The op-code. */
+ titP->tit_operands = count;
+} /* tip */
+
+/* md_assemble() emit frags for 1 instruction */
+void
+md_assemble (instruction_string)
+ char *instruction_string; /* A string: assemble 1 instruction. */
+{
+ char *p;
+ register struct top *operandP; /* An operand. Scans all operands. */
+/* char c_save; fixme: remove this line */ /* What used to live after an expression. */
+/* struct frag *fragP; fixme: remove this line */ /* Fragment of code we just made. */
+/* register struct top *end_operandP; fixme: remove this line */ /* -> slot just after last operand
+ Limit of the for (each operand). */
+ register expressionS *expP; /* -> expression values for this operand */
+
+ /* These refer to an instruction operand expression. */
+ segT to_seg; /* Target segment of the address. */
+
+ register valueT this_add_number;
+ register struct symbol *this_add_symbol; /* +ve (minuend) symbol. */
+
+/* tahoe_opcodeT opcode_as_number; fixme: remove this line */ /* The opcode as a number. */
+ char *opcodeP; /* Where it is in a frag. */
+/* char *opmodeP; fixme: remove this line */ /* Where opcode type is, in a frag. */
+
+ int dispsize; /* From top_dispsize: tahoe_operand_width
+ (in bytes) */
+ int is_undefined; /* 1 if operand expression's
+ segment not known yet. */
+ int pc_rel; /* Is this operand pc relative? */
+
+ /* Decode the operand. */
+ tip(&t, instruction_string);
+
+ /*
+ * Check to see if this operand decode properly.
+ * Notice that we haven't made any frags yet.
+ * If it goofed, then this instruction will wedge in any pass,
+ * and we can safely flush it, without causing interpass symbol phase
+ * errors. That is, without changing label values in different passes.
+ */
+ if (*t.tit_error){
+ as_warn("Ignoring statement due to \"%s\"", t.tit_error);
+ }else{
+ /* We saw no errors in any operands - try to make frag(s) */
+ /* Emit op-code. */
+ /* Remember where it is, in case we want to modify the op-code later. */
+ opcodeP = frag_more(1);
+ *opcodeP = t.tit_opcode;
+ /* Now do each operand. */
+ for (operandP = t.tit_operand;
+ operandP < t.tit_operand + t.tit_operands;
+ operandP++){ /* for each operand */
+ expP = &(operandP->exp_of_operand);
+ if (operandP->top_ndx >= 0){
+ /* Indexed addressing byte
+ Legality of indexed mode already checked: it is OK */
+ FRAG_APPEND_1_CHAR(0x40 + operandP->top_ndx);
+ } /* if(top_ndx>=0) */
+
+ /* Here to make main operand frag(s). */
+ this_add_number = expP->X_add_number;
+ this_add_symbol = expP->X_add_symbol;
+ to_seg = expP->X_seg;
+ know (to_seg == SEG_UNKNOWN||\
+ to_seg == SEG_ABSOLUTE||\
+ to_seg == SEG_DATA||\
+ to_seg == SEG_TEXT||\
+ to_seg == SEG_BSS);
+ is_undefined = (to_seg == SEG_UNKNOWN);
+ /* Do we know how big this opperand is? */
+ dispsize = operandP->top_dispsize;
+ pc_rel = 0;
+ /* Deal with the branch possabilities. (Note, this doesn't include
+ jumps.)*/
+ if (operandP->top_access == 'b'){
+ /* Branches must be expressions. A psuedo branch can also jump to
+ an absolute address. */
+ if (to_seg == now_seg || is_undefined){
+ /* If is_undefined, then it might BECOME now_seg by relax time. */
+ if (dispsize){
+ /* I know how big the branch is supposed to be (it's a normal
+ branch), so I set up the frag, and let GAS do the rest. */
+ p = frag_more (dispsize);
+ fix_new (frag_now, p - frag_now->fr_literal, dispsize,
+ this_add_symbol, 0, this_add_number, 1, NO_RELOC);
+ } else {
+ /* (to_seg==now_seg || to_seg == SEG_UNKNOWN) && dispsize==0 */
+ /* If we don't know how big it is, then its a synthetic branch,
+ so we set up a simple relax state. */
+ switch (operandP->top_width){
+ case TAHOE_WIDTH_CONDITIONAL_JUMP:
+ /* Simple (conditional) jump. I may have to reverse the
+ condition of opcodeP, and then jump to my destination.
+ I set 1 byte aside for the branch off set, and could need 6
+ more bytes for the pc_rel jump */
+ frag_var (rs_machine_dependent, 7, 1,
+ ENCODE_RELAX (STATE_CONDITIONAL_BRANCH,
+ is_undefined ? STATE_UNDF : STATE_BYTE),
+ this_add_symbol, this_add_number, opcodeP);
+ break;
+ case TAHOE_WIDTH_ALWAYS_JUMP:
+ /* Simple (unconditional) jump. I may have to convert this to
+ a word branch, or an absolute jump. */
+ frag_var (rs_machine_dependent, 5, 1,
+ ENCODE_RELAX (STATE_ALWAYS_BRANCH,
+ is_undefined ? STATE_UNDF : STATE_BYTE),
+ this_add_symbol, this_add_number, opcodeP);
+ break;
+ /* The smallest size for the next 2 cases is word. */
+ case TAHOE_WIDTH_BIG_REV_JUMP:
+ frag_var (rs_machine_dependent, 8, 2,
+ ENCODE_RELAX (STATE_BIG_REV_BRANCH,
+ is_undefined ? STATE_UNDF : STATE_WORD),
+ this_add_symbol, this_add_number,
+ opcodeP);
+ break;
+ case TAHOE_WIDTH_BIG_NON_REV_JUMP:
+ frag_var (rs_machine_dependent, 10, 2,
+ ENCODE_RELAX (STATE_BIG_NON_REV_BRANCH,
+ is_undefined ? STATE_UNDF : STATE_WORD),
+ this_add_symbol, this_add_number,
+ opcodeP);
+ break;
+ default:
+ as_fatal("Compliler bug: Got a case (%d) I wasn't expecting.",
+ operandP->top_width);
+ }
+ }
+ }else{
+ /* to_seg != now_seg && to_seg != seg_unknown (still in branch)
+ In other words, I'm jumping out of my segment so extend the
+ branches to jumps, and let GAS fix them. */
+
+ /* These are "branches" what will always be branches around a jump
+ to the correct addresss in real life.
+ If to_seg is SEG_ABSOLUTE, just encode the branch in,
+ else let GAS fix the address. */
+
+ switch (operandP->top_width){
+ /* The theory:
+ For SEG_ABSOLUTE, then mode is ABSOLUTE_ADDR, jump
+ to that addresss (not pc_rel).
+ For other segs, address is a long word PC rel jump. */
+ case TAHOE_WIDTH_CONDITIONAL_JUMP:
+ /* b<cond> */
+ /* To reverse the condition in a TAHOE branch,
+ complement bit 4 */
+ *opcodeP ^= 0x10;
+ p = frag_more (7);
+ *p++ = 6;
+ *p++ = TAHOE_JMP;
+ *p++ = (operandP->top_mode ==
+ TAHOE_ABSOLUTE_ADDR ? TAHOE_ABSOLUTE_ADDR :
+ TAHOE_PC_REL_LONG);
+ fix_new (frag_now, p - frag_now->fr_literal, 4,
+ this_add_symbol, 0, this_add_number,
+ (to_seg != SEG_ABSOLUTE), NO_RELOC);
+ /*
+ * Now (eg) BLEQ 1f
+ * JMP foo
+ * 1:
+ */
+ break;
+ case TAHOE_WIDTH_ALWAYS_JUMP:
+ /* br, just turn it into a jump */
+ *opcodeP = TAHOE_JMP;
+ p = frag_more (5);
+ *p++ = (operandP->top_mode ==
+ TAHOE_ABSOLUTE_ADDR ? TAHOE_ABSOLUTE_ADDR :
+ TAHOE_PC_REL_LONG);
+ fix_new (frag_now, p - frag_now->fr_literal, 4,
+ this_add_symbol, 0, this_add_number,
+ (to_seg != SEG_ABSOLUTE), NO_RELOC);
+ /* Now (eg) JMP foo */
+ break;
+ case TAHOE_WIDTH_BIG_REV_JUMP:
+ p = frag_more (8);
+ *opcodeP ^= 0x10;
+ *p++ = 0;
+ *p++ = 6;
+ *p++ = TAHOE_JMP;
+ *p++ = (operandP->top_mode ==
+ TAHOE_ABSOLUTE_ADDR ? TAHOE_ABSOLUTE_ADDR :
+ TAHOE_PC_REL_LONG);
+ fix_new (frag_now, p - frag_now->fr_literal, 4,
+ this_add_symbol, 0, this_add_number,
+ (to_seg != SEG_ABSOLUTE), NO_RELOC);
+ /*
+ * Now (eg) ACBx 1f
+ * JMP foo
+ * 1:
+ */
+ break;
+ case TAHOE_WIDTH_BIG_NON_REV_JUMP:
+ p = frag_more (10);
+ *p++ = 0;
+ *p++ = 2;
+ *p++ = TAHOE_BRB;
+ *p++ = 6;
+ *p++ = TAHOE_JMP;
+ *p++ = (operandP->top_mode ==
+ TAHOE_ABSOLUTE_ADDR ? TAHOE_ABSOLUTE_ADDR :
+ TAHOE_PC_REL_LONG);
+ fix_new (frag_now, p - frag_now->fr_literal, 4,
+ this_add_symbol, 0, this_add_number,
+ (to_seg != SEG_ABSOLUTE), NO_RELOC);
+ /*
+ * Now (eg) xOBxxx 1f
+ * BRB 2f
+ * 1: JMP @#foo
+ * 2:
+ */
+ break;
+ case 'b':
+ case 'w':
+ as_warn("Real branch displacements must be expressions.");
+ break;
+ default:
+ as_fatal("Complier error: I got an unknown synthetic branch :%c",
+ operandP->top_width);
+ break;
+ }
+ }
+ }else{
+ /* It ain't a branch operand. */
+ switch (operandP->top_mode){
+ /* Auto-foo access, only works for one reg (SP)
+ so the only thing needed is the mode. */
+ case TAHOE_AUTO_DEC:
+ case TAHOE_AUTO_INC:
+ case TAHOE_AUTO_INC_DEFERRED:
+ FRAG_APPEND_1_CHAR(operandP->top_mode);
+ break;
+
+ /* Numbered Register only access. Only thing needed is the
+ mode + Register number */
+ case TAHOE_DIRECT_REG:
+ case TAHOE_REG_DEFERRED:
+ FRAG_APPEND_1_CHAR(operandP->top_mode + operandP->top_reg);
+ break;
+
+ /* An absolute address. It's size is always 5 bytes.
+ (mode_type + 4 byte address). */
+ case TAHOE_ABSOLUTE_ADDR:
+ know((this_add_symbol == NULL));
+ p = frag_more(5);
+ *p = TAHOE_ABSOLUTE_ADDR;
+ md_number_to_chars(p+1,this_add_number,4);
+ break;
+
+ /* Immediate data. If the size isn't known, then it's an address
+ + and offset, which is 4 bytes big. */
+ case TAHOE_IMMEDIATE:
+ if (this_add_symbol != NULL){
+ p = frag_more (5);
+ *p++ = TAHOE_IMMEDIATE_LONGWORD;
+ fix_new (frag_now, p - frag_now->fr_literal,
+ 4, this_add_symbol,0,this_add_number,
+ 0, NO_RELOC);
+ }else{
+ /* It's a integer, and I know it's size. */
+ if ((unsigned) this_add_number < 0x40){
+ /* Will it fit in a literal? */
+ FRAG_APPEND_1_CHAR((byte) this_add_number);
+ }else{
+ p = frag_more(dispsize+1);
+ switch(dispsize){
+ case 1:
+ *p++ = TAHOE_IMMEDIATE_BYTE;
+ *p = (byte) this_add_number;
+ break;
+ case 2:
+ *p++ = TAHOE_IMMEDIATE_WORD;
+ md_number_to_chars(p,this_add_number,2);
+ break;
+ case 4:
+ *p++ = TAHOE_IMMEDIATE_LONGWORD;
+ md_number_to_chars(p,this_add_number,4);
+ break;
+ }
+ }
+ }
+ break;
+
+ /* Distance from the PC. If the size isn't known, we have to relax
+ into it. The difference between this and disp(sp) is that
+ this offset is pc_rel, and disp(sp) isn't.
+ Note the drop through code. */
+
+ case TAHOE_DISPLACED_RELATIVE:
+ case TAHOE_DISP_REL_DEFERRED:
+ operandP->top_reg = PC_REG;
+ pc_rel = 1;
+
+ /* Register, plus a displacement mode. Save the register number,
+ and weather its deffered or not, and relax the size if it isn't
+ known. */
+ case TAHOE_REG_DISP:
+ case TAHOE_REG_DISP_DEFERRED:
+ if (operandP->top_mode == TAHOE_DISP_REL_DEFERRED ||
+ operandP->top_mode == TAHOE_REG_DISP_DEFERRED)
+ operandP->top_reg += 0x10; /* deffered mode is always 0x10 higher
+ than it's non-deffered sibling. */
+
+ /* Is this a value out of this segment?
+ The first part of this conditional is a cludge to make gas
+ produce the same output as 'as' when there is a lable, in
+ the current segment, displaceing a register. It's strange,
+ and no one in their right mind would do it, but it's easy
+ to cludge. */
+ if ((dispsize == 0 && !pc_rel) ||
+ (to_seg != now_seg && !is_undefined && to_seg != SEG_ABSOLUTE))
+ dispsize = 4;
+
+ if (dispsize == 0){
+ /*
+ * We have a SEG_UNKNOWN symbol, or the size isn't cast.
+ * It might turn out to be in the same segment as
+ * the instruction, permitting relaxation.
+ */
+ p = frag_var(rs_machine_dependent, 5, 2,
+ ENCODE_RELAX(STATE_PC_RELATIVE,
+ is_undefined ? STATE_UNDF:STATE_BYTE),
+ this_add_symbol, this_add_number,0);
+ *p = operandP->top_reg;
+ }else{
+ /* Either this is an abs, or a cast. */
+ p = frag_more (dispsize + 1);
+ switch(dispsize){
+ case 1:
+ *p = TAHOE_PC_OR_BYTE + operandP->top_reg;
+ break;
+ case 2:
+ *p = TAHOE_PC_OR_WORD + operandP->top_reg;
+ break;
+ case 4:
+ *p = TAHOE_PC_OR_LONG + operandP->top_reg;
+ break;
+ };
+ fix_new (frag_now, p + 1 - frag_now->fr_literal,
+ dispsize, this_add_symbol,0,this_add_number,
+ pc_rel, NO_RELOC);
+ }
+ break;
+ default:
+ as_fatal("Barf, bad mode %x\n",operandP->top_mode);
+ }
+ }
+ } /* for(operandP) */
+ } /* if(!need_pass_2 && !goofed) */
+} /* tahoe_assemble() */
+
+
+/* We have no need to default values of symbols. */
+
+/* ARGSUSED */
+symbolS *md_undefined_symbol(name)
+char *name;
+{
+ return 0;
+} /* md_undefined_symbol() */
+
+/* Parse an operand that is machine-specific.
+ We just return without modifying the expression if we have nothing
+ to do. */
+
+/* ARGSUSED */
+void md_operand(expressionP)
+expressionS *expressionP;
+{
+} /* md_operand() */
+
+/* Round up a section size to the appropriate boundary. */
+long md_section_align(segment, size)
+segT segment;
+long size;
+{
+ return((size + 7) & ~7); /* Round all sects to multiple of 8 */
+} /* md_section_align() */
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the sparc, they're relative to the address of the offset, plus
+ its size. This gets us to the following instruction.
+ (??? Is this right? FIXME-SOON) */
+long md_pcrel_from(fixP)
+fixS *fixP;
+{
+ return(fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address);
+} /* md_pcrel_from() */
+
+/* end of tc-tahoe.c */
diff --git a/gnu/usr.bin/as/config/tc-tahoe.h b/gnu/usr.bin/as/config/tc-tahoe.h
new file mode 100644
index 0000000..e63cb63
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-tahoe.h
@@ -0,0 +1,36 @@
+/* This file is tc-tahoe.h
+
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define TC_TAHOE 1
+
+#define NO_LISTING
+
+#define tc_headers_hook(a) {;} /* don't need it. */
+#define tc_crawl_symbol_chain(a) {;} /* don't need it. */
+#define tc_aout_pre_write_hook(a) {;}
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of tc-tahoe.h */
diff --git a/gnu/usr.bin/as/config/tc-vax.c b/gnu/usr.bin/as/config/tc-vax.c
new file mode 100644
index 0000000..9133c84
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-vax.c
@@ -0,0 +1,3073 @@
+/* tc-vax.c - vax-specific -
+ Copyright (C) 1987, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* JF I moved almost all the vax specific stuff into this one file 'cuz RMS
+ seems to think its a good idea. I hope I managed to get all the VAX-isms */
+
+
+#include "as.h"
+
+#include "read.h"
+#include "vax-inst.h"
+#include "obstack.h" /* For FRAG_APPEND_1_CHAR macro in "frags.h" */
+
+/* These chars start a comment anywhere in a source file (except inside
+ another comment */
+const char comment_chars[] = "#";
+
+/* These chars only start a comment at the beginning of a line. */
+/* Note that for the VAX the are the same as comment_chars above. */
+const char line_comment_chars[] = "#";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* as in 0f123.456 */
+/* or 0H1.234E-12 (see exp chars above) */
+const char FLT_CHARS[] = "dDfFgGhH";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c. Ideally it shouldn't have to know about it at all,
+ but nothing is ideal around here.
+ */
+
+static expressionS /* Hold details of an operand expression */
+ exp_of_operand[VIT_MAX_OPERANDS];
+
+static struct vit
+ v; /* A vax instruction after decoding. */
+
+LITTLENUM_TYPE big_operand_bits[VIT_MAX_OPERANDS][SIZE_OF_LARGE_NUMBER];
+/* Hold details of big operands. */
+FLONUM_TYPE float_operand[VIT_MAX_OPERANDS];
+/* Above is made to point into */
+/* big_operand_bits by md_begin(). */
+
+/*
+ * For VAX, relative addresses of "just the right length" are easy.
+ * The branch displacement is always the last operand, even in
+ * synthetic instructions.
+ * For VAX, we encode the relax_substateTs (in e.g. fr_substate) as:
+ *
+ * 4 3 2 1 0 bit number
+ * ---/ /--+-------+-------+-------+-------+-------+
+ * | what state ? | how long ? |
+ * ---/ /--+-------+-------+-------+-------+-------+
+ *
+ * The "how long" bits are 00=byte, 01=word, 10=long.
+ * This is a Un*x convention.
+ * Not all lengths are legit for a given value of (what state).
+ * The "how long" refers merely to the displacement length.
+ * The address usually has some constant bytes in it as well.
+ *
+
+ groups for VAX address relaxing.
+
+ 1. "foo" pc-relative.
+ length of byte, word, long
+
+ 2a. J<cond> where <cond> is a simple flag test.
+ length of byte, word, long.
+ VAX opcodes are: (Hex)
+ bneq/bnequ 12
+ beql/beqlu 13
+ bgtr 14
+ bleq 15
+ bgeq 18
+ blss 19
+ bgtru 1a
+ blequ 1b
+ bvc 1c
+ bvs 1d
+ bgequ/bcc 1e
+ blssu/bcs 1f
+ Always, you complement 0th bit to reverse condition.
+ Always, 1-byte opcode, then 1-byte displacement.
+
+ 2b. J<cond> where cond tests a memory bit.
+ length of byte, word, long.
+ Vax opcodes are: (Hex)
+ bbs e0
+ bbc e1
+ bbss e2
+ bbcs e3
+ bbsc e4
+ bbcc e5
+ bbssi e6
+ bbcci e7
+ Always, you complement 0th bit to reverse condition.
+ Always, 1-byte opcde, longword-address, byte-address, 1-byte-displacement
+
+ 2c. J<cond> where cond tests low-order memory bit
+ length of byte,word,long.
+ Vax opcodes are: (Hex)
+ blbs e8
+ blbc e9
+ Always, you complement 0th bit to reverse condition.
+ Always, 1-byte opcode, longword-address, 1-byte displacement.
+
+ 3. Jbs/Jbr.
+ length of byte,word,long.
+ Vax opcodes are: (Hex)
+ bsbb 10
+ brb 11
+ These are like (2) but there is no condition to reverse.
+ Always, 1 byte opcode, then displacement/absolute.
+
+ 4a. JacbX
+ length of word, long.
+ Vax opcodes are: (Hex)
+ acbw 3d
+ acbf 4f
+ acbd 6f
+ abcb 9d
+ acbl f1
+ acbg 4ffd
+ acbh 6ffd
+ Always, we cannot reverse the sense of the branch; we have a word
+ displacement.
+ The double-byte op-codes don't hurt: we never want to modify the
+ opcode, so we don't care how many bytes are between the opcode and
+ the operand.
+
+ 4b. JXobXXX
+ length of long, long, byte.
+ Vax opcodes are: (Hex)
+ aoblss f2
+ aobleq f3
+ sobgeq f4
+ sobgtr f5
+ Always, we cannot reverse the sense of the branch; we have a byte
+ displacement.
+
+ The only time we need to modify the opcode is for class 2 instructions.
+ After relax() we may complement the lowest order bit of such instruction
+ to reverse sense of branch.
+
+ For class 2 instructions, we store context of "where is the opcode literal".
+ We can change an opcode's lowest order bit without breaking anything else.
+
+ We sometimes store context in the operand literal. This way we can figure out
+ after relax() what the original addressing mode was.
+ */
+
+/* These displacements are relative to */
+/* the start address of the displacement. */
+/* The first letter is Byte, Word. */
+/* 2nd letter is Forward, Backward. */
+#define BF (1+ 127)
+#define BB (1+-128)
+#define WF (2+ 32767)
+#define WB (2+-32768)
+/* Dont need LF, LB because they always */
+/* reach. [They are coded as 0.] */
+
+
+#define C(a,b) ENCODE_RELAX(a,b)
+/* This macro has no side-effects. */
+#define ENCODE_RELAX(what,length) (((what) << 2) + (length))
+
+const relax_typeS
+ md_relax_table[] =
+{
+ { 1, 1, 0, 0 }, /* error sentinel 0,0 */
+ { 1, 1, 0, 0 }, /* unused 0,1 */
+ { 1, 1, 0, 0 }, /* unused 0,2 */
+ { 1, 1, 0, 0 }, /* unused 0,3 */
+ { BF + 1, BB + 1, 2, C(1, 1) }, /* B^"foo" 1,0 */
+ { WF + 1, WB + 1, 3, C (1, 2) }, /* W^"foo" 1,1 */
+ { 0, 0, 5, 0 }, /* L^"foo" 1,2 */
+ { 1, 1, 0, 0 }, /* unused 1,3 */
+ { BF, BB, 1, C(2, 1) }, /* b<cond> B^"foo" 2,0 */
+ { WF + 2, WB + 2, 4, C (2, 2) }, /* br.+? brw X 2,1 */
+ { 0, 0, 7, 0 }, /* br.+? jmp X 2,2 */
+ { 1, 1, 0, 0 }, /* unused 2,3 */
+ { BF, BB, 1, C (3, 1) }, /* brb B^foo 3,0 */
+ { WF, WB, 2, C (3, 2) }, /* brw W^foo 3,1 */
+ { 0, 0, 5, 0 }, /* Jmp L^foo 3,2 */
+ { 1, 1, 0, 0 }, /* unused 3,3 */
+ { 1, 1, 0, 0 }, /* unused 4,0 */
+ { WF, WB, 2, C (4, 2) }, /* acb_ ^Wfoo 4,1 */
+ { 0, 0, 10, 0 }, /* acb_,br,jmp L^foo4,2 */
+ { 1, 1, 0, 0 }, /* unused 4,3 */
+ { BF, BB, 1, C (5, 1) }, /* Xob___,,foo 5,0 */
+ { WF + 4, WB + 4, 6, C (5, 2) }, /* Xob.+2,brb.+3,brw5,1 */
+ { 0, 0, 9, 0 }, /* Xob.+2,brb.+6,jmp5,2 */
+};
+
+#undef C
+#undef BF
+#undef BB
+#undef WF
+#undef WB
+
+void float_cons ();
+
+const pseudo_typeS md_pseudo_table[] = {
+ {"dfloat", float_cons, 'd'},
+ {"ffloat", float_cons, 'f'},
+ {"gfloat", float_cons, 'g'},
+ {"hfloat", float_cons, 'h'},
+ {0},
+};
+
+#define STATE_PC_RELATIVE (1)
+#define STATE_CONDITIONAL_BRANCH (2)
+#define STATE_ALWAYS_BRANCH (3) /* includes BSB... */
+#define STATE_COMPLEX_BRANCH (4)
+#define STATE_COMPLEX_HOP (5)
+
+#define STATE_BYTE (0)
+#define STATE_WORD (1)
+#define STATE_LONG (2)
+#define STATE_UNDF (3) /* Symbol undefined in pass1 */
+
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+#if __STDC__ == 1
+
+int flonum_gen2vax(char format_letter, FLONUM_TYPE *f, LITTLENUM_TYPE *words);
+static void vip_end(void);
+static void vip_op_defaults(char *immediate, char *indirect, char *displen);
+
+#else /* not __STDC__ */
+
+int flonum_gen2vax();
+static void vip_end();
+static void vip_op_defaults();
+
+#endif /* not __STDC__ */
+
+void
+ md_begin ()
+{
+ char *vip_begin ();
+ char *errtxt;
+ FLONUM_TYPE *fP;
+ int i;
+
+ if (*(errtxt = vip_begin (1, "$", "*", "`"))) {
+ as_fatal("VIP_BEGIN error:%s", errtxt);
+ }
+
+ for (i = 0, fP = float_operand;
+ fP < float_operand + VIT_MAX_OPERANDS;
+ i++, fP++) {
+ fP->low = &big_operand_bits[i][0];
+ fP->high = &big_operand_bits[i][SIZE_OF_LARGE_NUMBER - 1];
+ }
+}
+
+void
+ md_end ()
+{
+ vip_end ();
+}
+
+void /* Knows about order of bytes in address. */
+ md_number_to_chars(con, value, nbytes)
+char con[]; /* Return 'nbytes' of chars here. */
+long value; /* The value of the bits. */
+int nbytes; /* Number of bytes in the output. */
+{
+ int n;
+ long v;
+
+ n = nbytes;
+ v = value;
+ while (nbytes--) {
+ *con++ = value; /* Lint wants & MASK_CHAR. */
+ value >>= BITS_PER_CHAR;
+ }
+ /* XXX line number probably botched for this warning message. */
+ if (value != 0 && value != -1)
+ as_bad("Displacement (%ld) long for instruction field length (%d).", v, n);
+}
+
+/* Fix up some data or instructions after we find out the value of a symbol
+ that they reference. */
+
+void /* Knows about order of bytes in address. */
+ md_apply_fix(fixP, value)
+fixS *fixP; /* Fixup struct pointer */
+long value; /* The value of the bits. */
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ int nbytes; /* Number of bytes in the output. */
+
+ nbytes = fixP->fx_size;
+ while (nbytes--) {
+ *buf++ = value; /* Lint wants & MASK_CHAR. */
+ value >>= BITS_PER_CHAR;
+ }
+}
+
+long /* Knows about the byte order in a word. */
+ md_chars_to_number (con, nbytes)
+unsigned char con[]; /* Low order byte 1st. */
+int nbytes; /* Number of bytes in the input. */
+{
+ long retval;
+ for (retval = 0, con += nbytes - 1; nbytes--; con--) {
+ retval <<= BITS_PER_CHAR;
+ retval |= *con;
+ }
+ return retval;
+}
+
+/* vax:md_assemble() emit frags for 1 instruction */
+
+void
+ md_assemble (instruction_string)
+char *instruction_string; /* A string: assemble 1 instruction. */
+{
+ /* We saw no errors in any operands - try to make frag(s) */
+ int is_undefined; /* 1 if operand expression's */
+ /* segment not known yet. */
+ int length_code;
+
+ char *p;
+ register struct vop *operandP;/* An operand. Scans all operands. */
+ char *save_input_line_pointer;
+ char c_save; /* What used to live after an expression. */
+ /* fixme: unused? */
+/* struct frag *fragP; */ /* Fragment of code we just made. */
+ register int goofed; /* 1: instruction_string bad for all passes. */
+ register struct vop *end_operandP; /* -> slot just after last operand */
+ /* Limit of the for (each operand). */
+ register expressionS *expP; /* -> expression values for this operand */
+
+ /* These refer to an instruction operand expression. */
+ segT to_seg; /* Target segment of the address. */
+ register valueT this_add_number;
+ register struct symbol *this_add_symbol; /* +ve (minuend) symbol. */
+ register struct symbol *this_subtract_symbol; /* -ve(subtrahend) symbol. */
+
+ long opcode_as_number; /* As a number. */
+ char *opcode_as_chars; /* Least significant byte 1st. */
+ /* As an array of characters. */
+ char *opcode_low_byteP; /* Least significant byte 1st */
+ /* richfix: unused? */
+/* struct details *detP; */ /* The details of an ADxxx frag. */
+ int length; /* length (bytes) meant by vop_short. */
+ int at; /* 0, or 1 if '@' is in addressing mode. */
+ int nbytes; /* From vop_nbytes: vax_operand_width (in bytes) */
+ FLONUM_TYPE *floatP;
+ char *vip ();
+ LITTLENUM_TYPE literal_float[8];
+ /* Big enough for any floating point literal. */
+
+ if (*(p = vip (&v, instruction_string))) {
+ as_fatal("vax_assemble\"%s\" in=\"%s\"", p, instruction_string);
+ }
+ /*
+ * Now we try to find as many as_warn()s as we can. If we do any as_warn()s
+ * then goofed=1. Notice that we don't make any frags yet.
+ * Should goofed be 1, then this instruction will wedge in any pass,
+ * and we can safely flush it, without causing interpass symbol phase
+ * errors. That is, without changing label values in different passes.
+ */
+ if (goofed = (*v.vit_error)) {
+ as_warn ("Ignoring statement due to \"%s\"", v.vit_error);
+ }
+ /*
+ * We need to use expression() and friends, which require us to diddle
+ * input_line_pointer. So we save it and restore it later.
+ */
+ save_input_line_pointer = input_line_pointer;
+ for (operandP = v.vit_operand,
+ expP = exp_of_operand,
+ floatP = float_operand,
+ end_operandP = v.vit_operand + v.vit_operands;
+
+ operandP < end_operandP;
+
+ operandP++, expP++, floatP++) { /* for each operand */
+ if (*(operandP->vop_error)) {
+ as_warn ("Ignoring statement because \"%s\"", (operandP->vop_error));
+ goofed = 1;
+ } else { /* statement has no syntax goofs: lets sniff the expression */
+ int can_be_short = 0; /* 1 if a bignum can be reduced to a short literal. */
+
+ input_line_pointer = operandP->vop_expr_begin;
+ c_save = operandP->vop_expr_end[1];
+ operandP->vop_expr_end[1] = '\0';
+ /* If to_seg == SEG_PASS1, expression() will have set need_pass_2 = 1. */
+ switch (to_seg = expression (expP)) {
+ case SEG_ABSENT:
+ /* for BSD4.2 compatibility, missing expression is absolute 0 */
+ to_seg = expP->X_seg = SEG_ABSOLUTE;
+ expP->X_add_number = 0;
+ /* for SEG_ABSOLUTE, we shouldnt need to set X_subtract_symbol, X_add_symbol to any
+ particular value. But, we will program defensively. Since this situation occurs rarely
+ so it costs us little to do, and stops Dean worrying about the origin of random bits in
+ expressionS's. */
+ expP->X_add_symbol = NULL;
+ expP->X_subtract_symbol = NULL;
+ case SEG_TEXT:
+ case SEG_DATA:
+ case SEG_BSS:
+ case SEG_ABSOLUTE:
+ case SEG_UNKNOWN:
+ break;
+
+ case SEG_DIFFERENCE:
+ case SEG_PASS1:
+ /*
+ * Major bug. We can't handle the case of a
+ * SEG_DIFFERENCE expression in a VIT_OPCODE_SYNTHETIC
+ * variable-length instruction.
+ * We don't have a frag type that is smart enough to
+ * relax a SEG_DIFFERENCE, and so we just force all
+ * SEG_DIFFERENCEs to behave like SEG_PASS1s.
+ * Clearly, if there is a demand we can invent a new or
+ * modified frag type and then coding up a frag for this
+ * case will be easy. SEG_DIFFERENCE was invented for the
+ * .words after a CASE opcode, and was never intended for
+ * instruction operands.
+ */
+ need_pass_2 = 1;
+ as_warn("Can't relocate expression");
+ break;
+
+ case SEG_BIG:
+ /* Preserve the bits. */
+ if (expP->X_add_number > 0) {
+ bignum_copy(generic_bignum, expP->X_add_number,
+ floatP->low, SIZE_OF_LARGE_NUMBER);
+ } else {
+ know(expP->X_add_number < 0);
+ flonum_copy (&generic_floating_point_number,
+ floatP);
+ if (strchr("s i", operandP->vop_short)) { /* Could possibly become S^# */
+ flonum_gen2vax(-expP->X_add_number, floatP, literal_float);
+ switch (-expP->X_add_number) {
+ case 'f':
+ can_be_short =
+ (literal_float[0] & 0xFC0F) == 0x4000
+ && literal_float[1] == 0;
+ break;
+
+ case 'd':
+ can_be_short =
+ (literal_float[0] & 0xFC0F) == 0x4000
+ && literal_float[1] == 0
+ && literal_float[2] == 0
+ && literal_float[3] == 0;
+ break;
+
+ case 'g':
+ can_be_short =
+ (literal_float[0] & 0xFF81) == 0x4000
+ && literal_float[1] == 0
+ && literal_float[2] == 0
+ && literal_float[3] == 0;
+ break;
+
+ case 'h':
+ can_be_short = ((literal_float[0] & 0xFFF8) == 0x4000
+ && (literal_float[1] & 0xE000) == 0
+ && literal_float[2] == 0
+ && literal_float[3] == 0
+ && literal_float[4] == 0
+ && literal_float[5] == 0
+ && literal_float[6] == 0
+ && literal_float[7] == 0);
+ break;
+
+ default:
+ BAD_CASE(-expP->X_add_number);
+ break;
+ } /* switch (float type) */
+ } /* if (could want to become S^#...) */
+ } /* bignum or flonum ? */
+
+ if (operandP->vop_short == 's'
+ || operandP->vop_short == 'i'
+ || (operandP->vop_short == ' '
+ && operandP->vop_reg == 0xF
+ && (operandP->vop_mode & 0xE) == 0x8)) {
+ /* Saw a '#'. */
+ if (operandP->vop_short == ' ') { /* We must chose S^ or I^. */
+ if (expP->X_add_number > 0) { /* Bignum: Short literal impossible. */
+ operandP->vop_short = 'i';
+ operandP->vop_mode = 8;
+ operandP->vop_reg = 0xF; /* VAX PC. */
+ } else { /* Flonum: Try to do it. */
+ if (can_be_short)
+ {
+ operandP->vop_short = 's';
+ operandP->vop_mode = 0;
+ operandP->vop_ndx = -1;
+ operandP->vop_reg = -1;
+ /* JF hope this is the right thing */
+ expP->X_seg = SEG_ABSOLUTE;
+ } else {
+ operandP->vop_short = 'i';
+ operandP->vop_mode = 8;
+ operandP->vop_reg = 0xF; /* VAX PC */
+ }
+ } /* bignum or flonum ? */
+ } /* if #, but no S^ or I^ seen. */
+ /* No more ' ' case: either 's' or 'i'. */
+ if (operandP->vop_short == 's') {
+ /* Wants to be a short literal. */
+ if (expP->X_add_number > 0) {
+ as_warn ("Bignum not permitted in short literal. Immediate mode assumed.");
+ operandP->vop_short = 'i';
+ operandP->vop_mode = 8;
+ operandP->vop_reg = 0xF; /* VAX PC. */
+ } else {
+ if (!can_be_short) {
+ as_warn ("Can't do flonum short literal: immediate mode used.");
+ operandP->vop_short = 'i';
+ operandP->vop_mode = 8;
+ operandP->vop_reg = 0xF; /* VAX PC. */
+ } else { /* Encode short literal now. */
+ int temp = 0;
+
+ switch (-expP->X_add_number) {
+ case 'f':
+ case 'd':
+ temp = literal_float[0] >> 4;
+ break;
+
+ case 'g':
+ temp = literal_float[0] >> 1;
+ break;
+
+ case 'h':
+ temp = ((literal_float[0] << 3) & 070)
+ | ((literal_float[1] >> 13) & 07);
+ break;
+
+ default:
+ BAD_CASE(-expP->X_add_number);
+ break;
+ }
+
+ floatP->low[0] = temp & 077;
+ floatP->low[1] = 0;
+ } /* if can be short literal float */
+ } /* flonum or bignum ? */
+ } else { /* I^# seen: set it up if float. */
+ if (expP->X_add_number < 0) {
+ memcpy(floatP->low, literal_float, sizeof(literal_float));
+ }
+ } /* if S^# seen. */
+ } else {
+ as_warn ("A bignum/flonum may not be a displacement: 0x%x used",
+ expP->X_add_number = 0x80000000);
+ /* Chosen so luser gets the most offset bits to patch later. */
+ }
+ expP->X_add_number = floatP->low[0]
+ | ((LITTLENUM_MASK & (floatP->low[1])) << LITTLENUM_NUMBER_OF_BITS);
+ /*
+ * For the SEG_BIG case we have:
+ * If vop_short == 's' then a short floating literal is in the
+ * lowest 6 bits of floatP->low [0], which is
+ * big_operand_bits [---] [0].
+ * If vop_short == 'i' then the appropriate number of elements
+ * of big_operand_bits [---] [...] are set up with the correct
+ * bits.
+ * Also, just in case width is byte word or long, we copy the lowest
+ * 32 bits of the number to X_add_number.
+ */
+ break;
+
+ default:
+ BAD_CASE (to_seg);
+ break;
+ }
+ if (input_line_pointer != operandP->vop_expr_end + 1) {
+ as_warn ("Junk at end of expression \"%s\"", input_line_pointer);
+ goofed = 1;
+ }
+ operandP->vop_expr_end[1] = c_save;
+ }
+ } /* for (each operand) */
+
+ input_line_pointer = save_input_line_pointer;
+
+ if (need_pass_2 || goofed) {
+ return;
+ }
+
+
+ /* Emit op-code. */
+ /* Remember where it is, in case we want to modify the op-code later. */
+ opcode_low_byteP = frag_more (v.vit_opcode_nbytes);
+ memcpy(opcode_low_byteP, v.vit_opcode, v.vit_opcode_nbytes);
+ opcode_as_number = md_chars_to_number (opcode_as_chars = v.vit_opcode, 4);
+ for (operandP = v.vit_operand,
+ expP = exp_of_operand,
+ floatP = float_operand,
+ end_operandP = v.vit_operand + v.vit_operands;
+
+ operandP < end_operandP;
+
+ operandP++,
+ floatP++,
+ expP++) { /* for each operand */
+ if (operandP->vop_ndx >= 0) {
+ /* indexed addressing byte */
+ /* Legality of indexed mode already checked: it is OK */
+ FRAG_APPEND_1_CHAR (0x40 + operandP->vop_ndx);
+ } /* if (vop_ndx >= 0) */
+
+ /* Here to make main operand frag(s). */
+ this_add_number = expP->X_add_number;
+ this_add_symbol = expP->X_add_symbol;
+ this_subtract_symbol = expP->X_subtract_symbol;
+ to_seg = expP->X_seg;
+ is_undefined = (to_seg == SEG_UNKNOWN);
+ know(to_seg == SEG_UNKNOWN
+ || to_seg == SEG_ABSOLUTE
+ || to_seg == SEG_DATA
+ || to_seg == SEG_TEXT
+ || to_seg == SEG_BSS
+ || to_seg == SEG_BIG);
+ at = operandP->vop_mode & 1;
+ length = (operandP->vop_short == 'b'
+ ? 1 : (operandP->vop_short == 'w'
+ ? 2 : (operandP->vop_short == 'l'
+ ? 4 : 0)));
+ nbytes = operandP->vop_nbytes;
+ if (operandP->vop_access == 'b') {
+ if (to_seg == now_seg || is_undefined) {
+ /* If is_undefined, then it might BECOME now_seg. */
+ if (nbytes) {
+ p = frag_more(nbytes);
+ fix_new(frag_now, p - frag_now->fr_literal, nbytes,
+ this_add_symbol, 0, this_add_number, 1, NO_RELOC);
+ } else { /* to_seg == now_seg || to_seg == SEG_UNKNOWN */
+ /* nbytes == 0 */
+ length_code = is_undefined ? STATE_UNDF : STATE_BYTE;
+ if (opcode_as_number & VIT_OPCODE_SPECIAL) {
+ if (operandP->vop_width == VAX_WIDTH_UNCONDITIONAL_JUMP) {
+ /* br or jsb */
+ frag_var(rs_machine_dependent, 5, 1,
+ ENCODE_RELAX (STATE_ALWAYS_BRANCH, length_code),
+ this_add_symbol, this_add_number,
+ opcode_low_byteP);
+ } else {
+ if (operandP->vop_width == VAX_WIDTH_WORD_JUMP) {
+ length_code = STATE_WORD;
+ /* JF: There is no state_byte for this one! */
+ frag_var(rs_machine_dependent, 10, 2,
+ ENCODE_RELAX (STATE_COMPLEX_BRANCH, length_code),
+ this_add_symbol, this_add_number,
+ opcode_low_byteP);
+ } else {
+ know(operandP->vop_width == VAX_WIDTH_BYTE_JUMP);
+ frag_var(rs_machine_dependent, 9, 1,
+ ENCODE_RELAX (STATE_COMPLEX_HOP, length_code),
+ this_add_symbol, this_add_number,
+ opcode_low_byteP);
+ }
+ }
+ } else {
+ know(operandP->vop_width == VAX_WIDTH_CONDITIONAL_JUMP);
+ frag_var(rs_machine_dependent, 7, 1,
+ ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, length_code),
+ this_add_symbol, this_add_number,
+ opcode_low_byteP);
+ }
+ }
+ } else { /* to_seg != now_seg && to_seg != SEG_UNKNOWN */
+ /*
+ * --- SEG FLOAT MAY APPEAR HERE ----
+ */
+ if (to_seg == SEG_ABSOLUTE) {
+ if (nbytes) {
+ know(!(opcode_as_number & VIT_OPCODE_SYNTHETIC));
+ p = frag_more (nbytes);
+ /* Conventional relocation. */
+ fix_new(frag_now, p - frag_now->fr_literal,
+ nbytes, &abs_symbol, 0, this_add_number, 1, NO_RELOC);
+ } else {
+ know(opcode_as_number & VIT_OPCODE_SYNTHETIC);
+ if (opcode_as_number & VIT_OPCODE_SPECIAL) {
+ if (operandP->vop_width == VAX_WIDTH_UNCONDITIONAL_JUMP) {
+ /* br or jsb */
+ *opcode_low_byteP = opcode_as_chars[0] + VAX_WIDEN_LONG;
+ know(opcode_as_chars[1] == 0);
+ p = frag_more (5);
+ p[0] = VAX_ABSOLUTE_MODE; /* @#... */
+ md_number_to_chars(p + 1, this_add_number, 4);
+ /* Now (eg) JMP @#foo or JSB @#foo. */
+ } else {
+ if (operandP->vop_width == VAX_WIDTH_WORD_JUMP) {
+ p = frag_more (10);
+ p[0] = 2;
+ p[1] = 0;
+ p[2] = VAX_BRB;
+ p[3] = 6;
+ p[4] = VAX_JMP;
+ p[5] = VAX_ABSOLUTE_MODE; /* @#... */
+ md_number_to_chars(p + 6, this_add_number, 4);
+ /*
+ * Now (eg) ACBx 1f
+ * BRB 2f
+ * 1: JMP @#foo
+ * 2:
+ */
+ } else {
+ know(operandP->vop_width == VAX_WIDTH_BYTE_JUMP);
+ p = frag_more (9);
+ p[0] = 2;
+ p[1] = VAX_BRB;
+ p[2] = 6;
+ p[3] = VAX_JMP;
+ p[4] = VAX_PC_RELATIVE_MODE + 1; /* @#... */
+ md_number_to_chars(p + 5, this_add_number, 4);
+ /*
+ * Now (eg) xOBxxx 1f
+ * BRB 2f
+ * 1: JMP @#foo
+ * 2:
+ */
+ }
+ }
+ } else {
+ /* b<cond> */
+ *opcode_low_byteP ^= 1;
+ /* To reverse the condition in a VAX branch, complement the lowest order
+ bit. */
+ p = frag_more (7);
+ p[0] = 6;
+ p[1] = VAX_JMP;
+ p[2] = VAX_ABSOLUTE_MODE; /* @#... */
+ md_number_to_chars(p + 3, this_add_number, 4);
+ /*
+ * Now (eg) BLEQ 1f
+ * JMP @#foo
+ * 1:
+ */
+ }
+ }
+ } else { /* to_seg != now_seg && to_seg != SEG_UNKNOWN && to_Seg != SEG_ABSOLUTE */
+ if (nbytes > 0) {
+ /* Pc-relative. Conventional relocation. */
+ know(!(opcode_as_number & VIT_OPCODE_SYNTHETIC));
+ p = frag_more (nbytes);
+ fix_new(frag_now, p - frag_now->fr_literal,
+ nbytes, &abs_symbol, 0, this_add_number, 1, NO_RELOC);
+ } else {
+ know(opcode_as_number & VIT_OPCODE_SYNTHETIC);
+ if (opcode_as_number & VIT_OPCODE_SPECIAL) {
+ if (operandP->vop_width == VAX_WIDTH_UNCONDITIONAL_JUMP) {
+ /* br or jsb */
+ know(opcode_as_chars[1] == 0);
+ *opcode_low_byteP = opcode_as_chars[0] + VAX_WIDEN_LONG;
+ p = frag_more (5);
+ p[0] = VAX_PC_RELATIVE_MODE;
+ fix_new(frag_now,
+ p + 1 - frag_now->fr_literal, 4,
+ this_add_symbol, 0,
+ this_add_number, 1, NO_RELOC);
+ /* Now eg JMP foo or JSB foo. */
+ } else {
+ if (operandP->vop_width == VAX_WIDTH_WORD_JUMP) {
+ p = frag_more (10);
+ p[0] = 0;
+ p[1] = 2;
+ p[2] = VAX_BRB;
+ p[3] = 6;
+ p[4] = VAX_JMP;
+ p[5] = VAX_PC_RELATIVE_MODE;
+ fix_new(frag_now,
+ p + 6 - frag_now->fr_literal, 4,
+ this_add_symbol, 0,
+ this_add_number, 1, NO_RELOC);
+ /*
+ * Now (eg) ACBx 1f
+ * BRB 2f
+ * 1: JMP foo
+ * 2:
+ */
+ } else {
+ know(operandP->vop_width == VAX_WIDTH_BYTE_JUMP);
+ p = frag_more (10);
+ p[0] = 2;
+ p[1] = VAX_BRB;
+ p[2] = 6;
+ p[3] = VAX_JMP;
+ p[4] = VAX_PC_RELATIVE_MODE;
+ fix_new(frag_now,
+ p + 5 - frag_now->fr_literal,
+ 4, this_add_symbol, 0,
+ this_add_number, 1, NO_RELOC);
+ /*
+ * Now (eg) xOBxxx 1f
+ * BRB 2f
+ * 1: JMP foo
+ * 2:
+ */
+ }
+ }
+ } else {
+ know(operandP->vop_width == VAX_WIDTH_CONDITIONAL_JUMP);
+ *opcode_low_byteP ^= 1; /* Reverse branch condition. */
+ p = frag_more (7);
+ p[0] = 6;
+ p[1] = VAX_JMP;
+ p[2] = VAX_PC_RELATIVE_MODE;
+ fix_new(frag_now, p + 3 - frag_now->fr_literal,
+ 4, this_add_symbol, 0,
+ this_add_number, 1, NO_RELOC);
+ }
+ }
+ }
+ }
+ } else {
+ know(operandP->vop_access != 'b'); /* So it is ordinary operand. */
+ know(operandP->vop_access != ' '); /* ' ' target-independent: elsewhere. */
+ know(operandP->vop_access == 'a'
+ || operandP->vop_access == 'm'
+ || operandP->vop_access == 'r'
+ || operandP->vop_access == 'v'
+ || operandP->vop_access == 'w');
+ if (operandP->vop_short == 's') {
+ if (to_seg == SEG_ABSOLUTE) {
+ if (this_add_number < 0 || this_add_number >= 64) {
+ as_warn("Short literal overflow(%d.), immediate mode assumed.", this_add_number);
+ operandP->vop_short = 'i';
+ operandP->vop_mode = 8;
+ operandP->vop_reg = 0xF;
+ }
+ } else {
+ as_warn ("Forced short literal to immediate mode. now_seg=%s to_seg=%s",
+ segment_name(now_seg), segment_name(to_seg));
+ operandP->vop_short = 'i';
+ operandP->vop_mode = 8;
+ operandP->vop_reg = 0xF;
+ }
+ }
+ if (operandP->vop_reg >= 0 && (operandP->vop_mode < 8
+ || (operandP->vop_reg != 0xF && operandP->vop_mode < 10))) {
+ /* One byte operand. */
+ know(operandP->vop_mode > 3);
+ FRAG_APPEND_1_CHAR (operandP->vop_mode << 4 | operandP->vop_reg);
+ /* All 1-bytes except S^# happen here. */
+ } else { /* {@}{q^}foo{(Rn)} or S^#foo */
+ if (operandP->vop_reg == -1 && operandP->vop_short != 's') {
+ /* "{@}{q^}foo" */
+ if (to_seg == now_seg) {
+ if (length == 0) {
+ know(operandP->vop_short == ' ');
+ p = frag_var(rs_machine_dependent, 10, 2,
+ ENCODE_RELAX (STATE_PC_RELATIVE, STATE_BYTE),
+ this_add_symbol, this_add_number,
+ opcode_low_byteP);
+ know(operandP->vop_mode == 10 + at);
+ *p = at << 4;
+ /* At is the only context we need to carry to */
+ /* other side of relax() process. */
+ /* Must be in the correct bit position of VAX */
+ /* operand spec. byte. */
+ } else {
+ know(length);
+ know(operandP->vop_short != ' ');
+ p = frag_more (length + 1);
+ /* JF is this array stuff really going to work? */
+ p[0] = 0xF | ((at + "?\12\14?\16"[length]) << 4);
+ fix_new(frag_now, p + 1 - frag_now->fr_literal,
+ length, this_add_symbol, 0,
+ this_add_number, 1, NO_RELOC);
+ }
+ } else { /* to_seg != now_seg */
+ if (this_add_symbol == NULL) {
+ know(to_seg == SEG_ABSOLUTE);
+ /* Do @#foo: simpler relocation than foo-.(pc) anyway. */
+ p = frag_more (5);
+ p[0] = VAX_ABSOLUTE_MODE; /* @#... */
+ md_number_to_chars(p + 1, this_add_number, 4);
+ if (length && length != 4)
+ {
+ as_warn ("Length specification ignored. Address mode 9F used");
+ }
+ } else {
+ /* {@}{q^}other_seg */
+ know((length == 0 && operandP->vop_short == ' ')
+ ||(length > 0 && operandP->vop_short != ' '));
+ if (is_undefined) {
+ /*
+ * We have a SEG_UNKNOWN symbol. It might
+ * turn out to be in the same segment as
+ * the instruction, permitting relaxation.
+ */
+ p = frag_var(rs_machine_dependent, 5, 2,
+ ENCODE_RELAX (STATE_PC_RELATIVE, STATE_UNDF),
+ this_add_symbol, this_add_number,
+ 0);
+ p[0] = at << 4;
+ } else {
+ if (length == 0) {
+ know(operandP->vop_short == ' ');
+ length = 4; /* Longest possible. */
+ }
+ p = frag_more (length + 1);
+ p[0] = 0xF | ((at + "?\12\14?\16"[length]) << 4);
+ md_number_to_chars(p + 1, this_add_number, length);
+ fix_new(frag_now,
+ p + 1 - frag_now->fr_literal,
+ length, this_add_symbol, 0,
+ this_add_number, 1, NO_RELOC);
+ }
+ }
+ }
+ } else { /* {@}{q^}foo(Rn) or S^# or I^# or # */
+ if (operandP->vop_mode < 0xA) { /* # or S^# or I^# */
+ /* know( (length == 0 && operandP->vop_short == ' ')
+ || (length > 0 && operandP->vop_short != ' ')); */
+ if (length == 0
+ && to_seg == SEG_ABSOLUTE
+ && operandP->vop_mode == 8 /* No '@'. */
+ && this_add_number < 64
+ && this_add_number >= 0) {
+ operandP->vop_short = 's';
+ }
+ if (operandP->vop_short == 's') {
+ FRAG_APPEND_1_CHAR (this_add_number);
+ } else { /* I^#... */
+ know(nbytes);
+ p = frag_more (nbytes + 1);
+ know(operandP->vop_reg == 0xF);
+ p[0] = (operandP->vop_mode << 4) | 0xF;
+ if (to_seg == SEG_ABSOLUTE) {
+ /*
+ * If nbytes > 4, then we are scrod. We don't know if the
+ * high order bytes are to be 0xFF or 0x00.
+ * BSD4.2 & RMS say use 0x00. OK --- but this
+ * assembler needs ANOTHER rewrite to
+ * cope properly with this bug.
+ */
+ md_number_to_chars(p + 1, this_add_number, min (4, nbytes));
+ if (nbytes > 4)
+ {
+ memset(p + 5, '\0', nbytes - 4);
+ }
+ } else {
+ if (to_seg == SEG_BIG) {
+ /*
+ * Problem here is to get the bytes in the right order.
+ * We stored our constant as LITTLENUMs, not bytes.
+ */
+ LITTLENUM_TYPE *lP;
+
+ lP = floatP->low;
+ if (nbytes & 1) {
+ know(nbytes == 1);
+ p[1] = *lP;
+ } else {
+ for (p++; nbytes; nbytes -= 2, p += 2, lP++)
+ {
+ md_number_to_chars(p, *lP, 2);
+ }
+ }
+ } else {
+ fix_new(frag_now, p + 1 - frag_now->fr_literal,
+ nbytes, this_add_symbol, 0,
+ this_add_number, 0, NO_RELOC);
+ }
+ }
+ }
+ } else { /* {@}{q^}foo(Rn) */
+ know((length == 0 && operandP->vop_short == ' ')
+ ||(length > 0 && operandP->vop_short != ' '));
+ if (length == 0) {
+ if (to_seg == SEG_ABSOLUTE) {
+ register long test;
+
+ test = this_add_number;
+
+ if (test < 0)
+ test = ~test;
+
+ length = test & 0xffff8000 ? 4
+ : test & 0xffffff80 ? 2
+ : 1;
+ } else {
+ length = 4;
+ }
+ }
+ p = frag_more (1 + length);
+ know(operandP->vop_reg >= 0);
+ p[0] = operandP->vop_reg
+ | ((at | "?\12\14?\16"[length]) << 4);
+ if (to_seg == SEG_ABSOLUTE) {
+ md_number_to_chars(p + 1, this_add_number, length);
+ } else {
+ fix_new(frag_now, p + 1 - frag_now->fr_literal,
+ length, this_add_symbol, 0,
+ this_add_number, 0, NO_RELOC);
+ }
+ }
+ }
+ } /* if (single-byte-operand) */
+ }
+ } /* for (operandP) */
+} /* vax_assemble() */
+
+/*
+ * md_estimate_size_before_relax()
+ *
+ * Called just before relax().
+ * Any symbol that is now undefined will not become defined.
+ * Return the correct fr_subtype in the frag.
+ * Return the initial "guess for fr_var" to caller.
+ * The guess for fr_var is ACTUALLY the growth beyond fr_fix.
+ * Whatever we do to grow fr_fix or fr_var contributes to our returned value.
+ * Although it may not be explicit in the frag, pretend fr_var starts with a
+ * 0 value.
+ */
+int
+ md_estimate_size_before_relax (fragP, segment)
+register fragS *fragP;
+register segT segment;
+{
+ register char *p;
+ register int old_fr_fix;
+
+ old_fr_fix = fragP->fr_fix;
+ switch (fragP->fr_subtype) {
+ case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_UNDF):
+ if (S_GET_SEGMENT(fragP->fr_symbol) == segment) { /* A relaxable case. */
+ fragP->fr_subtype = ENCODE_RELAX (STATE_PC_RELATIVE, STATE_BYTE);
+ } else {
+ p = fragP->fr_literal + old_fr_fix;
+ p[0] |= VAX_PC_RELATIVE_MODE; /* Preserve @ bit. */
+ fragP->fr_fix += 1 + 4;
+ fix_new(fragP, old_fr_fix + 1, 4, fragP->fr_symbol, 0,
+ fragP->fr_offset, 1, NO_RELOC);
+ frag_wane(fragP);
+ }
+ break;
+
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_UNDF):
+ if (S_GET_SEGMENT(fragP->fr_symbol) == segment) {
+ fragP->fr_subtype = ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_BYTE);
+ } else {
+ p = fragP->fr_literal + old_fr_fix;
+ *fragP->fr_opcode ^= 1; /* Reverse sense of branch. */
+ p[0] = 6;
+ p[1] = VAX_JMP;
+ p[2] = VAX_PC_RELATIVE_MODE; /* ...(PC) */
+ fragP->fr_fix += 1 + 1 + 1 + 4;
+ fix_new(fragP, old_fr_fix + 3, 4, fragP->fr_symbol, 0,
+ fragP->fr_offset, 1, NO_RELOC);
+ frag_wane(fragP);
+ }
+ break;
+
+ case ENCODE_RELAX (STATE_COMPLEX_BRANCH, STATE_UNDF):
+ if (S_GET_SEGMENT(fragP->fr_symbol) == segment) {
+ fragP->fr_subtype = ENCODE_RELAX (STATE_COMPLEX_BRANCH, STATE_WORD);
+ } else {
+ p = fragP->fr_literal + old_fr_fix;
+ p[0] = 2;
+ p[1] = 0;
+ p[2] = VAX_BRB;
+ p[3] = 6;
+ p[4] = VAX_JMP;
+ p[5] = VAX_PC_RELATIVE_MODE; /* ...(pc) */
+ fragP->fr_fix += 2 + 2 + 1 + 1 + 4;
+ fix_new(fragP, old_fr_fix + 6, 4, fragP->fr_symbol, 0,
+ fragP->fr_offset, 1, NO_RELOC);
+ frag_wane(fragP);
+ }
+ break;
+
+ case ENCODE_RELAX (STATE_COMPLEX_HOP, STATE_UNDF):
+ if (S_GET_SEGMENT(fragP->fr_symbol) == segment) {
+ fragP->fr_subtype = ENCODE_RELAX (STATE_COMPLEX_HOP, STATE_BYTE);
+ } else {
+ p = fragP->fr_literal + old_fr_fix;
+ p[0] = 2;
+ p[1] = VAX_BRB;
+ p[2] = 6;
+ p[3] = VAX_JMP;
+ p[4] = VAX_PC_RELATIVE_MODE; /* ...(pc) */
+ fragP->fr_fix += 1 + 2 + 1 + 1 + 4;
+ fix_new(fragP, old_fr_fix + 5, 4, fragP->fr_symbol, 0,
+ fragP->fr_offset, 1, NO_RELOC);
+ frag_wane(fragP);
+ }
+ break;
+
+ case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_UNDF):
+ if (S_GET_SEGMENT(fragP->fr_symbol) == segment) {
+ fragP->fr_subtype = ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_BYTE);
+ } else {
+ p = fragP->fr_literal + old_fr_fix;
+ *fragP->fr_opcode += VAX_WIDEN_LONG;
+ p[0] = VAX_PC_RELATIVE_MODE; /* ...(PC) */
+ fragP->fr_fix += 1 + 4;
+ fix_new(fragP, old_fr_fix + 1, 4, fragP->fr_symbol, 0,
+ fragP->fr_offset, 1, NO_RELOC);
+ frag_wane(fragP);
+ }
+ break;
+
+ default:
+ break;
+ }
+ return (fragP->fr_var + fragP->fr_fix - old_fr_fix);
+} /* md_estimate_size_before_relax() */
+
+/*
+ * md_convert_frag();
+ *
+ * Called after relax() is finished.
+ * In: Address of frag.
+ * fr_type == rs_machine_dependent.
+ * fr_subtype is what the address relaxed to.
+ *
+ * Out: Any fixSs and constants are set up.
+ * Caller will turn frag into a ".space 0".
+ */
+void
+ md_convert_frag (headers, fragP)
+object_headers *headers;
+register fragS *fragP;
+{
+ char *addressP; /* -> _var to change. */
+ char *opcodeP; /* -> opcode char(s) to change. */
+ short int length_code; /* 2=long 1=word 0=byte */
+ short int extension = 0; /* Size of relaxed address. */
+ /* Added to fr_fix: incl. ALL var chars. */
+ symbolS *symbolP;
+ long where;
+ long address_of_var;
+ /* Where, in file space, is _var of *fragP? */
+ long target_address = 0;
+ /* Where, in file space, does addr point? */
+
+ know(fragP->fr_type == rs_machine_dependent);
+ length_code = fragP->fr_subtype & 3; /* depends on ENCODE_RELAX() */
+ know(length_code >= 0 && length_code < 3);
+ where = fragP->fr_fix;
+ addressP = fragP->fr_literal + where;
+ opcodeP = fragP->fr_opcode;
+ symbolP = fragP->fr_symbol;
+ know(symbolP);
+ target_address = S_GET_VALUE(symbolP) + fragP->fr_offset;
+ address_of_var = fragP->fr_address + where;
+
+ switch (fragP->fr_subtype) {
+
+ case ENCODE_RELAX(STATE_PC_RELATIVE, STATE_BYTE):
+ know(*addressP == 0 || *addressP == 0x10); /* '@' bit. */
+ addressP[0] |= 0xAF; /* Byte displacement. */
+ addressP[1] = target_address - (address_of_var + 2);
+ extension = 2;
+ break;
+
+ case ENCODE_RELAX(STATE_PC_RELATIVE, STATE_WORD):
+ know(*addressP == 0 || *addressP == 0x10); /* '@' bit. */
+ addressP[0] |= 0xCF; /* Word displacement. */
+ md_number_to_chars(addressP + 1, target_address - (address_of_var + 3), 2);
+ extension = 3;
+ break;
+
+ case ENCODE_RELAX(STATE_PC_RELATIVE, STATE_LONG):
+ know(*addressP == 0 || *addressP == 0x10); /* '@' bit. */
+ addressP[0] |= 0xEF; /* Long word displacement. */
+ md_number_to_chars(addressP + 1, target_address - (address_of_var + 5), 4);
+ extension = 5;
+ break;
+
+ case ENCODE_RELAX(STATE_CONDITIONAL_BRANCH, STATE_BYTE):
+ addressP[0] = target_address - (address_of_var + 1);
+ extension = 1;
+ break;
+
+ case ENCODE_RELAX(STATE_CONDITIONAL_BRANCH, STATE_WORD):
+ opcodeP[0] ^= 1; /* Reverse sense of test. */
+ addressP[0] = 3;
+ addressP[1] = VAX_BRB + VAX_WIDEN_WORD;
+ md_number_to_chars(addressP + 2, target_address - (address_of_var + 4), 2);
+ extension = 4;
+ break;
+
+ case ENCODE_RELAX(STATE_CONDITIONAL_BRANCH, STATE_LONG):
+ opcodeP[0] ^= 1; /* Reverse sense of test. */
+ addressP[0] = 6;
+ addressP[1] = VAX_JMP;
+ addressP[2] = VAX_PC_RELATIVE_MODE;
+ md_number_to_chars(addressP + 3, target_address, 4);
+ extension = 7;
+ break;
+
+ case ENCODE_RELAX(STATE_ALWAYS_BRANCH, STATE_BYTE):
+ addressP[0] = target_address - (address_of_var + 1);
+ extension = 1;
+ break;
+
+ case ENCODE_RELAX(STATE_ALWAYS_BRANCH, STATE_WORD):
+ opcodeP[0] += VAX_WIDEN_WORD; /* brb -> brw, bsbb -> bsbw */
+ md_number_to_chars(addressP, target_address - (address_of_var + 2), 2);
+ extension = 2;
+ break;
+
+ case ENCODE_RELAX(STATE_ALWAYS_BRANCH, STATE_LONG):
+ opcodeP[0] += VAX_WIDEN_LONG; /* brb -> jmp, bsbb -> jsb */
+ addressP[0] = VAX_PC_RELATIVE_MODE;
+ md_number_to_chars(addressP + 1, target_address - (address_of_var + 5), 4);
+ extension = 5;
+ break;
+
+ case ENCODE_RELAX(STATE_COMPLEX_BRANCH, STATE_WORD):
+ md_number_to_chars(addressP, target_address - (address_of_var + 2), 2);
+ extension = 2;
+ break;
+
+ case ENCODE_RELAX(STATE_COMPLEX_BRANCH, STATE_LONG):
+ addressP[0] = 2;
+ addressP[1] = 0;
+ addressP[2] = VAX_BRB;
+ addressP[3] = 6;
+ addressP[4] = VAX_JMP;
+ addressP[5] = VAX_PC_RELATIVE_MODE;
+ md_number_to_chars(addressP + 6, target_address, 4);
+ extension = 10;
+ break;
+
+ case ENCODE_RELAX(STATE_COMPLEX_HOP, STATE_BYTE):
+ addressP[0] = target_address - (address_of_var + 1);
+ extension = 1;
+ break;
+
+ case ENCODE_RELAX(STATE_COMPLEX_HOP, STATE_WORD):
+ addressP[0] = 2;
+ addressP[1] = VAX_BRB;
+ addressP[2] = 3;
+ addressP[3] = VAX_BRW;
+ md_number_to_chars(addressP + 4, target_address - (address_of_var + 6), 2);
+ extension = 6;
+ break;
+
+ case ENCODE_RELAX(STATE_COMPLEX_HOP, STATE_LONG):
+ addressP[0] = 2;
+ addressP[1] = VAX_BRB;
+ addressP[2] = 6;
+ addressP[3] = VAX_JMP;
+ addressP[4] = VAX_PC_RELATIVE_MODE;
+ md_number_to_chars(addressP + 5, target_address, 4);
+ extension = 9;
+ break;
+
+ default:
+ BAD_CASE(fragP->fr_subtype);
+ break;
+ }
+ fragP->fr_fix += extension;
+} /* md_convert_frag() */
+
+/* Translate internal format of relocation info into target format.
+
+ On vax: first 4 bytes are normal unsigned long, next three bytes
+ are symbolnum, least sig. byte first. Last byte is broken up with
+ the upper nibble as nuthin, bit 3 as extern, bits 2 & 1 as length, and
+ bit 0 as pcrel. */
+#ifdef comment
+void
+ md_ri_to_chars (the_bytes, ri)
+char *the_bytes;
+struct reloc_info_generic ri;
+{
+ /* this is easy */
+ md_number_to_chars(the_bytes, ri.r_address, sizeof (ri.r_address));
+ /* now the fun stuff */
+ the_bytes[6] = (ri.r_symbolnum >> 16) & 0x0ff;
+ the_bytes[5] = (ri.r_symbolnum >> 8) & 0x0ff;
+ the_bytes[4] = ri.r_symbolnum & 0x0ff;
+ the_bytes[7] = (((ri.r_extern << 3) & 0x08) | ((ri.r_length << 1) & 0x06) |
+ ((ri.r_pcrel << 0) & 0x01)) & 0x0F;
+}
+#endif /* comment */
+
+void tc_aout_fix_to_chars(where, fixP, segment_address_in_file)
+char *where;
+fixS *fixP;
+relax_addressT segment_address_in_file;
+{
+ /*
+ * In: length of relocation (or of address) in chars: 1, 2 or 4.
+ * Out: GNU LD relocation length code: 0, 1, or 2.
+ */
+
+ static unsigned char nbytes_r_length[] = { 42, 0, 1, 42, 2 };
+ long r_symbolnum;
+
+ know(fixP->fx_addsy != NULL);
+
+ md_number_to_chars(where,
+ fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file,
+ 4);
+
+ r_symbolnum = (S_IS_DEFINED(fixP->fx_addsy)
+ ? S_GET_TYPE(fixP->fx_addsy)
+ : fixP->fx_addsy->sy_number);
+
+ where[6] = (r_symbolnum >> 16) & 0x0ff;
+ where[5] = (r_symbolnum >> 8) & 0x0ff;
+ where[4] = r_symbolnum & 0x0ff;
+ where[7] = ((((!S_IS_DEFINED(fixP->fx_addsy)) << 3) & 0x08)
+ | ((nbytes_r_length[fixP->fx_size] << 1) & 0x06)
+ | (((fixP->fx_pcrel << 0) & 0x01) & 0x0f));
+
+ return;
+} /* tc_aout_fix_to_chars() */
+/*
+ * BUGS, GRIPES, APOLOGIA, etc.
+ *
+ * The opcode table 'votstrs' needs to be sorted on opcode frequency.
+ * That is, AFTER we hash it with hash_...(), we want most-used opcodes
+ * to come out of the hash table faster.
+ *
+ * I am sorry to inflict
+ * yet another VAX assembler on the world, but RMS says we must
+ * do everything from scratch, to prevent pin-heads restricting
+ * this software.
+ */
+
+/*
+ * This is a vaguely modular set of routines in C to parse VAX
+ * assembly code using DEC mnemonics. It is NOT un*x specific.
+ *
+ * The idea here is that the assembler has taken care of all:
+ * labels
+ * macros
+ * listing
+ * pseudo-ops
+ * line continuation
+ * comments
+ * condensing any whitespace down to exactly one space
+ * and all we have to do is parse 1 line into a vax instruction
+ * partially formed. We will accept a line, and deliver:
+ * an error message (hopefully empty)
+ * a skeleton VAX instruction (tree structure)
+ * textual pointers to all the operand expressions
+ * a warning message that notes a silly operand (hopefully empty)
+ */
+
+/*
+ * E D I T H I S T O R Y
+ *
+ * 17may86 Dean Elsner. Bug if line ends immediately after opcode.
+ * 30apr86 Dean Elsner. New vip_op() uses arg block so change call.
+ * 6jan86 Dean Elsner. Crock vip_begin() to call vip_op_defaults().
+ * 2jan86 Dean Elsner. Invent synthetic opcodes.
+ * Widen vax_opcodeT to 32 bits. Use a bit for VIT_OPCODE_SYNTHETIC,
+ * which means this is not a real opcode, it is like a macro; it will
+ * be relax()ed into 1 or more instructions.
+ * Use another bit for VIT_OPCODE_SPECIAL if the op-code is not optimised
+ * like a regular branch instruction. Option added to vip_begin():
+ * exclude synthetic opcodes. Invent synthetic_votstrs[].
+ * 31dec85 Dean Elsner. Invent vit_opcode_nbytes.
+ * Also make vit_opcode into a char[]. We now have n-byte vax opcodes,
+ * so caller's don't have to know the difference between a 1-byte & a
+ * 2-byte op-code. Still need vax_opcodeT concept, so we know how
+ * big an object must be to hold an op.code.
+ * 30dec85 Dean Elsner. Widen typedef vax_opcodeT in "vax-inst.h"
+ * because vax opcodes may be 16 bits. Our crufty C compiler was
+ * happily initialising 8-bit vot_codes with 16-bit numbers!
+ * (Wouldn't the 'phone company like to compress data so easily!)
+ * 29dec85 Dean Elsner. New static table vax_operand_width_size[].
+ * Invented so we know hw many bytes a "I^#42" needs in its immediate
+ * operand. Revised struct vop in "vax-inst.h": explicitly include
+ * byte length of each operand, and it's letter-code datum type.
+ * 17nov85 Dean Elsner. Name Change.
+ * Due to ar(1) truncating names, we learned the hard way that
+ * "vax-inst-parse.c" -> "vax-inst-parse." dropping the "o" off
+ * the archived object name. SO... we shortened the name of this
+ * source file, and changed the makefile.
+ */
+
+static struct hash_control *op_hash = NULL; /* handle of the OPCODE hash table */
+/* NULL means any use before vip_begin() */
+/* will crash */
+
+/*
+ * In: 1 character, from "bdfghloqpw" being the data-type of an operand
+ * of a vax instruction.
+ *
+ * Out: the length of an operand of that type, in bytes.
+ * Special branch operands types "-?!" have length 0.
+ */
+
+static const short int vax_operand_width_size[256] =
+{
+
+#define _ 0
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+ _, _, 1, _, 8, _, 4, 8, 16, _, _, _, 4, _, _, 16, /* ..b.d.fgh...l..o */
+ _, 8, _, _, _, _, _, 2, _, _, _, _, _, _, _, _, /* .q.....w........ */
+ _, _, 1, _, 8, _, 4, 8, 16, _, _, _, 4, _, _, 16, /* ..b.d.fgh...l..o */
+ _, 8, _, _, _, _, _, 2, _, _, _, _, _, _, _, _, /* .q.....w........ */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _};
+#undef _
+
+/*
+ * This perversion encodes all the vax opcodes as a bunch of strings.
+ * RMS says we should build our hash-table at run-time. Hmm.
+ * Please would someone arrange these in decreasing frequency of opcode?
+ * Because of the way hash_...() works, the most frequently used opcode
+ * should be textually first and so on.
+ *
+ * Input for this table was 'vax.opcodes', awk(1)ed by 'vax.opcodes.c.awk' .
+ * So change 'vax.opcodes', then re-generate this table.
+ */
+
+#include "opcode/vax.h"
+
+/*
+ * This is a table of optional op-codes. All of them represent
+ * 'synthetic' instructions that seem popular.
+ *
+ * Here we make some pseudo op-codes. Every code has a bit set to say
+ * it is synthetic. This lets you catch them if you want to
+ * ban these opcodes. They are mnemonics for "elastic" instructions
+ * that are supposed to assemble into the fewest bytes needed to do a
+ * branch, or to do a conditional branch, or whatever.
+ *
+ * The opcode is in the usual place [low-order n*8 bits]. This means
+ * that if you mask off the bucky bits, the usual rules apply about
+ * how long the opcode is.
+ *
+ * All VAX branch displacements come at the end of the instruction.
+ * For simple branches (1-byte opcode + 1-byte displacement) the last
+ * operand is coded 'b?' where the "data type" '?' is a clue that we
+ * may reverse the sense of the branch (complement lowest order bit)
+ * and branch around a jump. This is by far the most common case.
+ * That is why the VIT_OPCODE_SYNTHETIC bit is set: it says this is
+ * a 0-byte op-code followed by 2 or more bytes of operand address.
+ *
+ * If the op-code has VIT_OPCODE_SPECIAL set, then we have a more unusual
+ * case.
+ *
+ * For JBSB & JBR the treatment is the similar, except (1) we have a 'bw'
+ * option before (2) we can directly JSB/JMP because there is no condition.
+ * These operands have 'b-' as their access/data type.
+ *
+ * That leaves a bunch of random opcodes: JACBx, JxOBxxx. In these
+ * cases, we do the same idea. JACBxxx are all marked with a 'b!'
+ * JAOBxxx & JSOBxxx are marked with a 'b:'.
+ *
+ */
+#if (VIT_OPCODE_SYNTHETIC != 0x80000000)
+You have just broken the encoding below, which assumes the sign bit
+ means 'I am an imaginary instruction'.
+#endif
+
+#if (VIT_OPCODE_SPECIAL != 0x40000000)
+You have just broken the encoding below, which assumes the 0x40 M bit means
+ 'I am not to be "optimised" the way normal branches are'.
+#endif
+
+ static const struct vot
+ synthetic_votstrs[] =
+{
+ {"jbsb", {"b-", 0xC0000010}}, /* BSD 4.2 */
+ /* jsb used already */
+ {"jbr", {"b-", 0xC0000011}}, /* BSD 4.2 */
+ {"jr", {"b-", 0xC0000011}}, /* consistent */
+ {"jneq", {"b?", 0x80000012}},
+ {"jnequ", {"b?", 0x80000012}},
+ {"jeql", {"b?", 0x80000013}},
+ {"jeqlu", {"b?", 0x80000013}},
+ {"jgtr", {"b?", 0x80000014}},
+ {"jleq", {"b?", 0x80000015}},
+ /* un-used opcodes here */
+ {"jgeq", {"b?", 0x80000018}},
+ {"jlss", {"b?", 0x80000019}},
+ {"jgtru", {"b?", 0x8000001a}},
+ {"jlequ", {"b?", 0x8000001b}},
+ {"jvc", {"b?", 0x8000001c}},
+ {"jvs", {"b?", 0x8000001d}},
+ {"jgequ", {"b?", 0x8000001e}},
+ {"jcc", {"b?", 0x8000001e}},
+ {"jlssu", {"b?", 0x8000001f}},
+ {"jcs", {"b?", 0x8000001f}},
+
+ {"jacbw", {"rwrwmwb!", 0xC000003d}},
+ {"jacbf", {"rfrfmfb!", 0xC000004f}},
+ {"jacbd", {"rdrdmdb!", 0xC000006f}},
+ {"jacbb", {"rbrbmbb!", 0xC000009d}},
+ {"jacbl", {"rlrlmlb!", 0xC00000f1}},
+ {"jacbg", {"rgrgmgb!", 0xC0004ffd}},
+ {"jacbh", {"rhrhmhb!", 0xC0006ffd}},
+
+ {"jbs", {"rlvbb?", 0x800000e0}},
+ {"jbc", {"rlvbb?", 0x800000e1}},
+ {"jbss", {"rlvbb?", 0x800000e2}},
+ {"jbcs", {"rlvbb?", 0x800000e3}},
+ {"jbsc", {"rlvbb?", 0x800000e4}},
+ {"jbcc", {"rlvbb?", 0x800000e5}},
+ {"jbssi", {"rlvbb?", 0x800000e6}},
+ {"jbcci", {"rlvbb?", 0x800000e7}},
+ {"jlbs", {"rlb?", 0x800000e8}}, /* JF changed from rlvbb? */
+ {"jlbc", {"rlb?", 0x800000e9}}, /* JF changed from rlvbb? */
+
+ {"jaoblss", {"rlmlb:", 0xC00000f2}},
+ {"jaobleq", {"rlmlb:", 0xC00000f3}},
+ {"jsobgeq", {"mlb:", 0xC00000f4}}, /* JF was rlmlb: */
+ {"jsobgtr", {"mlb:", 0xC00000f5}}, /* JF was rlmlb: */
+
+ /* CASEx has no branch addresses in our conception of it. */
+ /* You should use ".word ..." statements after the "case ...". */
+
+ {"", ""} /* empty is end sentinel */
+
+}; /* synthetic_votstrs */
+
+/*
+ * v i p _ b e g i n ( )
+ *
+ * Call me once before you decode any lines.
+ * I decode votstrs into a hash table at op_hash (which I create).
+ * I return an error text: hopefully "".
+ * If you want, I will include the 'synthetic' jXXX instructions in the
+ * instruction table.
+ * You must nominate metacharacters for eg DEC's "#", "@", "^".
+ */
+
+char *
+ vip_begin (synthetic_too, immediate, indirect, displen)
+int synthetic_too; /* 1 means include jXXX op-codes. */
+char *immediate, *indirect, *displen;
+{
+ const struct vot *vP; /* scan votstrs */
+ char *retval; /* error text */
+
+ if ((op_hash = hash_new())) {
+ retval = ""; /* OK so far */
+ for (vP = votstrs; *vP->vot_name && !*retval; vP++) {
+ retval = hash_insert(op_hash, vP->vot_name, &vP->vot_detail);
+ }
+ if (synthetic_too) {
+ for (vP = synthetic_votstrs; *vP->vot_name && !*retval; vP++) {
+ retval = hash_insert(op_hash, vP->vot_name, &vP->vot_detail);
+ }
+ }
+ } else {
+ retval = "virtual memory exceeded";
+ }
+#ifndef CONST_TABLE
+ vip_op_defaults(immediate, indirect, displen);
+#endif
+
+ return (retval);
+}
+
+
+/*
+ * v i p _ e n d ( )
+ *
+ * Call me once after you have decoded all lines.
+ * I do any cleaning-up needed.
+ *
+ * We don't have to do any cleanup ourselves: all of our operand
+ * symbol table is static, and free()ing it is naughty.
+ */
+static void vip_end () { }
+
+/*
+ * v i p ( )
+ *
+ * This converts a string into a vax instruction.
+ * The string must be a bare single instruction in dec-vax (with BSD4 frobs)
+ * format.
+ * It provides some error messages: at most one fatal error message (which
+ * stops the scan) and at most one warning message for each operand.
+ * The vax instruction is returned in exploded form, since we have no
+ * knowledge of how you parse (or evaluate) your expressions.
+ * We do however strip off and decode addressing modes and operation
+ * mnemonic.
+ *
+ * The exploded instruction is returned to a struct vit of your choice.
+ * #include "vax-inst.h" to know what a struct vit is.
+ *
+ * This function's value is a string. If it is not "" then an internal
+ * logic error was found: read this code to assign meaning to the string.
+ * No argument string should generate such an error string:
+ * it means a bug in our code, not in the user's text.
+ *
+ * You MUST have called vip_begin() once and vip_end() never before using
+ * this function.
+ */
+
+char * /* "" or bug string */
+ vip (vitP, instring)
+struct vit *vitP; /* We build an exploded instruction here. */
+char *instring; /* Text of a vax instruction: we modify. */
+{
+ register struct vot_wot *vwP; /* How to bit-encode this opcode. */
+ register char *p; /* 1/skip whitespace.2/scan vot_how */
+ register char *q; /* */
+ register char *bug; /* "" or program logic error */
+ register unsigned char count; /* counts number of operands seen */
+ register struct vop *operandp;/* scan operands in struct vit */
+ register char *alloperr; /* error over all operands */
+ register char c; /* Remember char, (we clobber it */
+ /* with '\0' temporarily). */
+ register vax_opcodeT oc; /* Op-code of this instruction. */
+
+ char *vip_op ();
+
+ bug = "";
+ if (*instring == ' ')
+ ++instring; /* Skip leading whitespace. */
+ for (p = instring; *p && *p != ' '; p++) ;; /* MUST end in end-of-string or exactly 1 space. */
+ /* Scanned up to end of operation-code. */
+ /* Operation-code is ended with whitespace. */
+ if (p - instring == 0) {
+ vitP->vit_error = "No operator";
+ count = 0;
+ memset(vitP->vit_opcode, '\0', sizeof(vitP->vit_opcode));
+ } else {
+ c = *p;
+ *p = '\0';
+ /*
+ * Here with instring pointing to what better be an op-name, and p
+ * pointing to character just past that.
+ * We trust instring points to an op-name, with no whitespace.
+ */
+ vwP = (struct vot_wot *) hash_find(op_hash, instring);
+ *p = c; /* Restore char after op-code. */
+ if (vwP == 0) {
+ vitP->vit_error = "Unknown operator";
+ count = 0;
+ memset(vitP->vit_opcode, '\0', sizeof(vitP->vit_opcode));
+ } else {
+ /*
+ * We found a match! So lets pick up as many operands as the
+ * instruction wants, and even gripe if there are too many.
+ * We expect comma to seperate each operand.
+ * We let instring track the text, while p tracks a part of the
+ * struct vot.
+ */
+ /*
+ * The lines below know about 2-byte opcodes starting FD,FE or FF.
+ * They also understand synthetic opcodes. Note:
+ * we return 32 bits of opcode, including bucky bits, BUT
+ * an opcode length is either 8 or 16 bits for vit_opcode_nbytes.
+ */
+ oc = vwP->vot_code; /* The op-code. */
+ vitP->vit_opcode_nbytes = (oc & 0xFF) >= 0xFD ? 2 : 1;
+ md_number_to_chars(vitP->vit_opcode, oc, 4);
+ count = 0; /* no operands seen yet */
+ instring = p; /* point just past operation code */
+ alloperr = "";
+ for (p = vwP->vot_how, operandp = vitP->vit_operand;
+ !*alloperr && !*bug && *p;
+ operandp++, p += 2
+ ) {
+ /*
+ * Here to parse one operand. Leave instring pointing just
+ * past any one ',' that marks the end of this operand.
+ */
+ if (!p[1])
+ bug = "p"; /* ODD(!!) number of bytes in vot_how?? */
+ else if (*instring) {
+ for (q = instring; (c = *q) && c != ','; q++)
+ ;
+ /*
+ * Q points to ',' or '\0' that ends argument. C is that
+ * character.
+ */
+ *q = 0;
+ operandp->vop_width = p[1];
+ operandp->vop_nbytes = vax_operand_width_size[p[1]];
+ operandp->vop_access = p[0];
+ bug = vip_op (instring, operandp);
+ *q = c; /* Restore input text. */
+ if (*(operandp->vop_error))
+ alloperr = "Bad operand";
+ instring = q + (c ? 1 : 0); /* next operand (if any) */
+ count++; /* won another argument, may have an operr */
+ } else
+ alloperr = "Not enough operands";
+ }
+ if (!*alloperr) {
+ if (*instring == ' ')
+ instring++; /* Skip whitespace. */
+ if (*instring)
+ alloperr = "Too many operands";
+ }
+ vitP->vit_error = alloperr;
+ }
+ }
+ vitP->vit_operands = count;
+ return (bug);
+}
+
+#ifdef test
+
+/*
+ * Test program for above.
+ */
+
+struct vit myvit; /* build an exploded vax instruction here */
+char answer[100]; /* human types a line of vax assembler here */
+char *mybug; /* "" or an internal logic diagnostic */
+int mycount; /* number of operands */
+struct vop *myvop; /* scan operands from myvit */
+int mysynth; /* 1 means want synthetic opcodes. */
+char my_immediate[200];
+char my_indirect[200];
+char my_displen[200];
+
+char *vip ();
+
+main ()
+{
+ char *p;
+ char *vip_begin ();
+
+ printf ("0 means no synthetic instructions. ");
+ printf ("Value for vip_begin? ");
+ gets (answer);
+ sscanf (answer, "%d", &mysynth);
+ printf ("Synthetic opcodes %s be included.\n", mysynth ? "will" : "will not");
+ printf ("enter immediate symbols eg enter # ");
+ gets (my_immediate);
+ printf ("enter indirect symbols eg enter @ ");
+ gets (my_indirect);
+ printf ("enter displen symbols eg enter ^ ");
+ gets (my_displen);
+ if (*(p = vip_begin (mysynth, my_immediate, my_indirect, my_displen))) {
+ error ("vip_begin=%s", p);
+ }
+ printf ("An empty input line will quit you from the vax instruction parser\n");
+ for (;;) {
+ printf ("vax instruction: ");
+ fflush (stdout);
+ gets (answer);
+ if (!*answer) {
+ break; /* out of for each input text loop */
+ }
+ mybug = vip (&myvit, answer);
+ if (*mybug) {
+ printf ("BUG:\"%s\"\n", mybug);
+ }
+ if (*myvit.vit_error) {
+ printf ("ERR:\"%s\"\n", myvit.vit_error);
+ }
+ printf ("opcode=");
+ for (mycount = myvit.vit_opcode_nbytes, p = myvit.vit_opcode;
+ mycount;
+ mycount--, p++
+ ) {
+ printf ("%02x ", *p & 0xFF);
+ }
+ printf (" operand count=%d.\n", mycount = myvit.vit_operands);
+ for (myvop = myvit.vit_operand; mycount; mycount--, myvop++) {
+ printf ("mode=%xx reg=%xx ndx=%xx len='%c'=%c%c%d. expr=\"",
+ myvop->vop_mode, myvop->vop_reg, myvop->vop_ndx,
+ myvop->vop_short, myvop->vop_access, myvop->vop_width,
+ myvop->vop_nbytes);
+ for (p = myvop->vop_expr_begin; p <= myvop->vop_expr_end; p++) {
+ putchar (*p);
+ }
+ printf ("\"\n");
+ if (*myvop->vop_error) {
+ printf (" err:\"%s\"\n", myvop->vop_error);
+ }
+ if (*myvop->vop_warn) {
+ printf (" wrn:\"%s\"\n", myvop->vop_warn);
+ }
+ }
+ }
+ vip_end ();
+ exit ();
+}
+
+#endif /* #ifdef test */
+
+/* end of vax_ins_parse.c */
+
+/* JF this used to be a separate file also */
+/* vax_reg_parse.c - convert a VAX register name to a number */
+
+/* Copyright (C) 1987 Free Software Foundation, Inc. A part of GNU. */
+
+/*
+ * v a x _ r e g _ p a r s e ( )
+ *
+ * Take 3 char.s, the last of which may be `\0` (non-existent)
+ * and return the VAX register number that they represent.
+ *
+ * Return -1 if they don't form a register name. Good names return
+ * a number from 0:15 inclusive.
+ *
+ * Case is not important in a name.
+ *
+ * Register names understood are:
+ *
+ * R0
+ * R1
+ * R2
+ * R3
+ * R4
+ * R5
+ * R6
+ * R7
+ * R8
+ * R9
+ * R10
+ * R11
+ * R12 AP
+ * R13 FP
+ * R14 SP
+ * R15 PC
+ *
+ */
+
+#include <ctype.h>
+#define AP (12)
+#define FP (13)
+#define SP (14)
+#define PC (15)
+
+int /* return -1 or 0:15 */
+ vax_reg_parse (c1, c2, c3) /* 3 chars of register name */
+char c1, c2, c3; /* c3 == 0 if 2-character reg name */
+{
+ register int retval; /* return -1:15 */
+
+ retval = -1;
+
+ if (isupper (c1))
+ c1 = tolower (c1);
+ if (isupper (c2))
+ c2 = tolower (c2);
+ if (isdigit (c2) && c1 == 'r') {
+ retval = c2 - '0';
+ if (isdigit (c3)) {
+ retval = retval * 10 + c3 - '0';
+ retval = (retval > 15) ? -1 : retval;
+ /* clamp the register value to 1 hex digit */
+ } else if (c3)
+ retval = -1; /* c3 must be '\0' or a digit */
+ } else if (c3) /* There are no three letter regs */
+ retval = -1;
+ else if (c2 == 'p') {
+ switch (c1) {
+ case 's':
+ retval = SP;
+ break;
+ case 'f':
+ retval = FP;
+ break;
+ case 'a':
+ retval = AP;
+ break;
+ default:
+ retval = -1;
+ }
+ } else if (c1 == 'p' && c2 == 'c')
+ retval = PC;
+ else
+ retval = -1;
+ return (retval);
+}
+
+/*
+ * v i p _ o p ( )
+ *
+ * Parse a vax operand in DEC assembler notation.
+ * For speed, expect a string of whitespace to be reduced to a single ' '.
+ * This is the case for GNU AS, and is easy for other DEC-compatible
+ * assemblers.
+ *
+ * Knowledge about DEC VAX assembler operand notation lives here.
+ * This doesn't even know what a register name is, except it believes
+ * all register names are 2 or 3 characters, and lets vax_reg_parse() say
+ * what number each name represents.
+ * It does, however, know that PC, SP etc are special registers so it can
+ * detect addressing modes that are silly for those registers.
+ *
+ * Where possible, it delivers 1 fatal or 1 warning message if the operand
+ * is suspect. Exactly what we test for is still evolving.
+ */
+
+/*
+ * B u g s
+ *
+ * Arg block.
+ *
+ * There were a number of 'mismatched argument type' bugs to vip_op.
+ * The most general solution is to typedef each (of many) arguments.
+ * We used instead a typedef'd argument block. This is less modular
+ * than using seperate return pointers for each result, but runs faster
+ * on most engines, and seems to keep programmers happy. It will have
+ * to be done properly if we ever want to use vip_op as a general-purpose
+ * module (it was designed to be).
+ *
+ * G^
+ *
+ * Doesn't support DEC "G^" format operands. These always take 5 bytes
+ * to express, and code as modes 8F or 9F. Reason: "G^" deprives you of
+ * optimising to (say) a "B^" if you are lucky in the way you link.
+ * When someone builds a linker smart enough to convert "G^" to "B^", "W^"
+ * whenever possible, then we should implement it.
+ * If there is some other use for "G^", feel free to code it in!
+ *
+ *
+ * speed
+ *
+ * If I nested if ()s more, I could avoid testing (*err) which would save
+ * time, space and page faults. I didn't nest all those if ()s for clarity
+ * and because I think the mode testing can be re-arranged 1st to test the
+ * commoner constructs 1st. Does anybody have statistics on this?
+ *
+ *
+ *
+ * error messages
+ *
+ * In future, we should be able to 'compose' error messages in a scratch area
+ * and give the user MUCH more informative error messages. Although this takes
+ * a little more code at run-time, it will make this module much more self-
+ * documenting. As an example of what sucks now: most error messages have
+ * hardwired into them the DEC VAX metacharacters "#^@" which are nothing like
+ * the Un*x characters "$`*", that most users will expect from this AS.
+ */
+
+/*
+ * The input is a string, ending with '\0'.
+ *
+ * We also require a 'hint' of what kind of operand is expected: so
+ * we can remind caller not to write into literals for instance.
+ *
+ * The output is a skeletal instruction.
+ *
+ * The algorithm has two parts.
+ * 1. extract the syntactic features (parse off all the @^#-()+[] mode crud);
+ * 2. express the @^#-()+[] as some parameters suited to further analysis.
+ *
+ * 2nd step is where we detect the googles of possible invalid combinations
+ * a human (or compiler) might write. Note that if we do a half-way
+ * decent assembler, we don't know how long to make (eg) displacement
+ * fields when we first meet them (because they may not have defined values).
+ * So we must wait until we know how many bits are needed for each address,
+ * then we can know both length and opcodes of instructions.
+ * For reason(s) above, we will pass to our caller a 'broken' instruction
+ * of these major components, from which our caller can generate instructions:
+ * - displacement length I^ S^ L^ B^ W^ unspecified
+ * - mode (many)
+ * - register R0-R15 or absent
+ * - index register R0-R15 or absent
+ * - expression text what we don't parse
+ * - error text(s) why we couldn't understand the operand
+ */
+
+/*
+ * To decode output of this, test errtxt. If errtxt[0] == '\0', then
+ * we had no errors that prevented parsing. Also, if we ever report
+ * an internal bug, errtxt[0] is set non-zero. So one test tells you
+ * if the other outputs are to be taken seriously.
+ */
+
+
+/* vax registers we need to know */
+/* JF #define SP (14) */
+/* JF for one big happy file #define PC (15) */
+
+/*
+ * Because this module is useful for both VMS and UN*X style assemblers
+ * and because of the variety of UN*X assemblers we must recognise
+ * the different conventions for assembler operand notation. For example
+ * VMS says "#42" for immediate mode, while most UN*X say "$42".
+ * We permit arbitrary sets of (single) characters to represent the
+ * 3 concepts that DEC writes '#', '@', '^'.
+ */
+
+/* character tests */
+#define VIP_IMMEDIATE 01 /* Character is like DEC # */
+#define VIP_INDIRECT 02 /* Char is like DEC @ */
+#define VIP_DISPLEN 04 /* Char is like DEC ^ */
+
+#define IMMEDIATEP(c) (vip_metacharacters[(c)&0xff]&VIP_IMMEDIATE)
+#define INDIRECTP(c) (vip_metacharacters[(c)&0xff]&VIP_INDIRECT)
+#define DISPLENP(c) (vip_metacharacters[(c)&0xff]&VIP_DISPLEN)
+
+/* We assume 8 bits per byte. Use vip_op_defaults() to set these up BEFORE we
+ * are ever called.
+ */
+
+#if defined(CONST_TABLE)
+#define _ 0,
+#define I VIP_IMMEDIATE,
+#define S VIP_INDIRECT,
+#define D VIP_DISPLEN,
+static const char
+ vip_metacharacters[256] = {
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _/* ^@ ^A ^B ^C ^D ^E ^F ^G ^H ^I ^J ^K ^L ^M ^N ^O*/
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _/* ^P ^Q ^R ^S ^T ^U ^V ^W ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
+ _ _ _ _ I _ _ _ _ _ S _ _ _ _ _/* sp ! " # $ % & ' ( ) * + , - . / */
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _/*0 1 2 3 4 5 6 7 8 9 : ; < = > ?*/
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _/*@ A B C D E F G H I J K L M N O*/
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _/*P Q R S T U V W X Y Z [ \ ] ^ _*/
+ D _ _ _ _ _ _ _ _ _ _ _ _ _ _ _/*` a b c d e f g h i j k l m n o*/
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _/*p q r s t u v w x y z { | } ~ ^?*/
+
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ };
+#undef _
+#undef I
+#undef S
+#undef D
+#else
+static char vip_metacharacters[256];
+
+/* Macro is faster under GCC; The constant table is faster yet, but only works with ASCII */
+#if 0
+static
+#ifdef __GNUC__
+ inline
+#endif
+ static void
+ vip_op_1(bit,syms)
+int bit;
+char *syms;
+{
+ unsigned char t;
+
+ while (t= *syms++)
+ vip_metacharacters[t]|=bit;
+}
+#else
+#define vip_op_1(bit,syms) { \
+ unsigned char t; \
+ char *table=vip_metacharacters; \
+ while (t= *syms++) \
+ table[t]|=bit; \
+ }
+#endif
+
+static void vip_op_defaults(immediate, indirect, displen) /* can be called any time */
+char *immediate; /* Strings of characters for each job. */
+char *indirect;
+char *displen; /* more arguments may appear in future! */
+{
+ vip_op_1 (VIP_IMMEDIATE, immediate);
+ vip_op_1 (VIP_INDIRECT, indirect);
+ vip_op_1 (VIP_DISPLEN, displen);
+
+ return;
+}
+#endif
+
+
+/*
+ * Dec defines the semantics of address modes (and values)
+ * by a two-letter code, explained here.
+ *
+ * letter 1: access type
+ *
+ * a address calculation - no data access, registers forbidden
+ * b branch displacement
+ * m read - let go of bus - write back "modify"
+ * r read
+ * v bit field address: like 'a' but registers are OK
+ * w write
+ * space no operator (eg ".long foo") [our convention]
+ *
+ * letter 2: data type (i.e. width, alignment)
+ *
+ * b byte
+ * d double precision floating point (D format)
+ * f single precision floating point (F format)
+ * g G format floating
+ * h H format floating
+ * l longword
+ * o octaword
+ * q quadword
+ * w word
+ * ? simple synthetic branch operand
+ * - unconditional synthetic JSB/JSR operand
+ * ! complex synthetic branch operand
+ *
+ * The '-?!' letter 2's are not for external consumption. They are used
+ * for various assemblers. Generally, all unknown widths are assumed 0.
+ * We don't limit your choice of width character.
+ *
+ * DEC operands are hard work to parse. For example, '@' as the first
+ * character means indirect (deferred) mode but elswhere it is a shift
+ * operator.
+ * The long-winded explanation of how this is supposed to work is
+ * cancelled. Read a DEC vax manual.
+ * We try hard not to parse anything that MIGHT be part of the expression
+ * buried in that syntax. For example if we see @...(Rn) we don't check
+ * for '-' before the '(' because mode @-(Rn) does not exist.
+ *
+ * After parsing we have:
+ *
+ * at 1 if leading '@' (or Un*x '*')
+ * len takes one value from " bilsw". eg B^ -> 'b'.
+ * hash 1 if leading '#' (or Un*x '$')
+ * expr_begin, expr_end the expression we did not parse
+ * even though we don't interpret it, we make use
+ * of its presence or absence.
+ * sign -1: -(Rn) 0: absent +1: (Rn)+
+ * paren 1 if () are around register
+ * reg major register number 0:15 -1 means absent
+ * ndx index register number 0:15 -1 means absent
+ *
+ * Again, I dare not explain it: just trace ALL the code!
+ */
+
+char * /* (code here) bug message, "" = OK */
+ /* our code bug, NOT bad assembly language */
+ vip_op (optext, vopP)
+char *optext; /* user's input string e.g.: */
+/* "@B^foo@bar(AP)[FP]:" */
+struct vop *vopP; /* In: vop_access, vop_width. */
+/* Out: _ndx, _reg, _mode, _short, _warn, */
+/* _error _expr_begin, _expr_end, _nbytes. */
+/* vop_nbytes : number of bytes in a datum. */
+{
+ char *p; /* track operand text forward */
+ char *q; /* track operand text backward */
+ int at; /* 1 if leading '@' ('*') seen */
+ char len; /* one of " bilsw" */
+ int hash; /* 1 if leading '#' ('$') seen */
+ int sign = 0; /* -1, 0 or +1 */
+ int paren = 0; /* 1 if () surround register */
+ int reg = 0; /* register number, -1:absent */
+ int ndx = 0; /* index register number -1:absent */
+ char *bug; /* report any logic error in here, "" == OK */
+ char *err; /* report illegal operand, "" == OK */
+ /* " " is a FAKE error: means we won */
+ /* ANY err that begins with ' ' is a fake. */
+ /* " " is converted to "" before return */
+ char *wrn; /* warn about weird modes pf address */
+ char *oldq = NULL; /* preserve q in case we backup */
+ int mode = 0; /* build up 4-bit operand mode here */
+ /* note: index mode is in ndx, this is */
+ /* the major mode of operand address */
+ /*
+ * Notice how we move wrong-arg-type bugs INSIDE this module: if we
+ * get the types wrong below, we lose at compile time rather than at
+ * lint or run time.
+ */
+ char access; /* vop_access. */
+ char width; /* vop_width. */
+
+ int vax_reg_parse (); /* returns 0:15 or -1 if not a register */
+
+ access = vopP->vop_access;
+ width = vopP->vop_width;
+ bug = /* none of our code bugs (yet) */
+ err = /* no user text errors */
+ wrn = ""; /* no warnings even */
+
+ p = optext;
+
+ if (*p == ' ') /* Expect all whitespace reduced to ' '. */
+ p++; /* skip over whitespace */
+
+ if (at = INDIRECTP (*p)) { /* 1 if *p == '@'(or '*' for Un*x) */
+ p++; /* at is determined */
+ if (*p == ' ') /* Expect all whitespace reduced to ' '. */
+ p++; /* skip over whitespace */
+ }
+
+ /*
+ * This code is subtle. It tries to detect all legal (letter)'^'
+ * but it doesn't waste time explicitly testing for premature '\0' because
+ * this case is rejected as a mismatch against either (letter) or '^'.
+ */
+ {
+ register char c;
+
+ c = *p;
+ if (isupper (c))
+ c = tolower (c);
+ if (DISPLENP (p[1]) && strchr ("bilws", len = c))
+ p += 2; /* skip (letter) '^' */
+ else /* no (letter) '^' seen */
+ len = ' '; /* len is determined */
+ }
+
+ if (*p == ' ') /* Expect all whitespace reduced to ' '. */
+ p++; /* skip over whitespace */
+
+ if (hash = IMMEDIATEP (*p)) /* 1 if *p == '#' ('$' for Un*x) */
+ p++; /* hash is determined */
+
+ /*
+ * p points to what may be the beginning of an expression.
+ * We have peeled off the front all that is peelable.
+ * We know at, len, hash.
+ *
+ * Lets point q at the end of the text and parse that (backwards).
+ */
+
+ for (q = p; *q; q++)
+ ;
+ q--; /* now q points at last char of text */
+
+ if (*q == ' ' && q >= p) /* Expect all whitespace reduced to ' '. */
+ q--;
+ /* reverse over whitespace, but don't */
+ /* run back over *p */
+
+ /*
+ * As a matter of policy here, we look for [Rn], although both Rn and S^#
+ * forbid [Rn]. This is because it is easy, and because only a sick
+ * cyborg would have [...] trailing an expression in a VAX-like assembler.
+ * A meticulous parser would first check for Rn followed by '(' or '['
+ * and not parse a trailing ']' if it found another. We just ban expressions
+ * ending in ']'.
+ */
+ if (*q == ']') {
+ while (q >= p && *q != '[')
+ q--;
+ /* either q<p or we got matching '[' */
+ if (q < p)
+ err = "no '[' to match ']'";
+ else {
+ /*
+ * Confusers like "[]" will eventually lose with a bad register
+ * name error. So again we don't need to check for early '\0'.
+ */
+ if (q[3] == ']')
+ ndx = vax_reg_parse (q[1], q[2], 0);
+ else if (q[4] == ']')
+ ndx = vax_reg_parse (q[1], q[2], q[3]);
+ else
+ ndx = -1;
+ /*
+ * Since we saw a ']' we will demand a register name in the [].
+ * If luser hasn't given us one: be rude.
+ */
+ if (ndx < 0)
+ err = "bad register in []";
+ else if (ndx == PC)
+ err = "[PC] index banned";
+ else
+ q--; /* point q just before "[...]" */
+ }
+ } else
+ ndx = -1; /* no ']', so no iNDeX register */
+
+ /*
+ * If err = "..." then we lost: run away.
+ * Otherwise ndx == -1 if there was no "[...]".
+ * Otherwise, ndx is index register number, and q points before "[...]".
+ */
+
+ if (*q == ' ' && q >= p) /* Expect all whitespace reduced to ' '. */
+ q--;
+ /* reverse over whitespace, but don't */
+ /* run back over *p */
+ if (!*err) {
+ sign = 0; /* no ()+ or -() seen yet */
+
+ if (q > p + 3 && *q == '+' && q[-1] == ')') {
+ sign = 1; /* we saw a ")+" */
+ q--; /* q points to ')' */
+ }
+
+ if (*q == ')' && q > p + 2) {
+ paren = 1; /* assume we have "(...)" */
+ while (q >= p && *q != '(')
+ q--;
+ /* either q<p or we got matching '(' */
+ if (q < p)
+ err = "no '(' to match ')'";
+ else {
+ /*
+ * Confusers like "()" will eventually lose with a bad register
+ * name error. So again we don't need to check for early '\0'.
+ */
+ if (q[3] == ')')
+ reg = vax_reg_parse (q[1], q[2], 0);
+ else if (q[4] == ')')
+ reg = vax_reg_parse (q[1], q[2], q[3]);
+ else
+ reg = -1;
+ /*
+ * Since we saw a ')' we will demand a register name in the ')'.
+ * This is nasty: why can't our hypothetical assembler permit
+ * parenthesised expressions? BECAUSE I AM LAZY! That is why.
+ * Abuse luser if we didn't spy a register name.
+ */
+ if (reg < 0) {
+ /* JF allow parenthasized expressions. I hope this works */
+ paren = 0;
+ while (*q != ')')
+ q++;
+ /* err = "unknown register in ()"; */
+ } else
+ q--; /* point just before '(' of "(...)" */
+ /*
+ * If err == "..." then we lost. Run away.
+ * Otherwise if reg >= 0 then we saw (Rn).
+ */
+ }
+ /*
+ * If err == "..." then we lost.
+ * Otherwise paren == 1 and reg = register in "()".
+ */
+ } else
+ paren = 0;
+ /*
+ * If err == "..." then we lost.
+ * Otherwise, q points just before "(Rn)", if any.
+ * If there was a "(...)" then paren == 1, and reg is the register.
+ */
+
+ /*
+ * We should only seek '-' of "-(...)" if:
+ * we saw "(...)" paren == 1
+ * we have no errors so far ! *err
+ * we did not see '+' of "(...)+" sign < 1
+ * We don't check len. We want a specific error message later if
+ * user tries "x^...-(Rn)". This is a feature not a bug.
+ */
+ if (!*err) {
+ if (paren && sign < 1)/* !sign is adequate test */ {
+ if (*q == '-') {
+ sign = -1;
+ q--;
+ }
+ }
+ /*
+ * We have back-tracked over most
+ * of the crud at the end of an operand.
+ * Unless err, we know: sign, paren. If paren, we know reg.
+ * The last case is of an expression "Rn".
+ * This is worth hunting for if !err, !paren.
+ * We wouldn't be here if err.
+ * We remember to save q, in case we didn't want "Rn" anyway.
+ */
+ if (!paren) {
+ if (*q == ' ' && q >= p) /* Expect all whitespace reduced to ' '. */
+ q--;
+ /* reverse over whitespace, but don't */
+ /* run back over *p */
+ if (q > p && q < p + 3) /* room for Rn or Rnn exactly? */
+ reg = vax_reg_parse (p[0], p[1], q < p + 2 ? 0 : p[2]);
+ else
+ reg = -1; /* always comes here if no register at all */
+ /*
+ * Here with a definitive reg value.
+ */
+ if (reg >= 0) {
+ oldq = q;
+ q = p - 1;
+ }
+ }
+ }
+ }
+ /*
+ * have reg. -1:absent; else 0:15
+ */
+
+ /*
+ * We have: err, at, len, hash, ndx, sign, paren, reg.
+ * Also, any remaining expression is from *p through *q inclusive.
+ * Should there be no expression, q == p-1. So expression length = q-p+1.
+ * This completes the first part: parsing the operand text.
+ */
+
+ /*
+ * We now want to boil the data down, checking consistency on the way.
+ * We want: len, mode, reg, ndx, err, p, q, wrn, bug.
+ * We will deliver a 4-bit reg, and a 4-bit mode.
+ */
+
+ /*
+ * Case of branch operand. Different. No L^B^W^I^S^ allowed for instance.
+ *
+ * in: at ?
+ * len ?
+ * hash ?
+ * p:q ?
+ * sign ?
+ * paren ?
+ * reg ?
+ * ndx ?
+ *
+ * out: mode 0
+ * reg -1
+ * len ' '
+ * p:q whatever was input
+ * ndx -1
+ * err " " or error message, and other outputs trashed
+ */
+ /* branch operands have restricted forms */
+ if (!*err && access == 'b') {
+ if (at || hash || sign || paren || ndx >= 0 || reg >= 0 || len != ' ')
+ err = "invalid branch operand";
+ else
+ err = " ";
+ }
+
+ /* Since nobody seems to use it: comment this 'feature'(?) out for now. */
+#ifdef NEVER
+ /*
+ * Case of stand-alone operand. e.g. ".long foo"
+ *
+ * in: at ?
+ * len ?
+ * hash ?
+ * p:q ?
+ * sign ?
+ * paren ?
+ * reg ?
+ * ndx ?
+ *
+ * out: mode 0
+ * reg -1
+ * len ' '
+ * p:q whatever was input
+ * ndx -1
+ * err " " or error message, and other outputs trashed
+ */
+ if (!*err) {
+ if (access == ' ') { /* addresses have restricted forms */
+ if (at)
+ err = "address prohibits @";
+ else {
+ if (hash)
+ err = "address prohibits #";
+ else {
+ if (sign) {
+ if (sign < 0)
+ err = "address prohibits -()";
+ else
+ err = "address prohibits ()+";
+ } else {
+ if (paren)
+ err = "address prohibits ()";
+ else {
+ if (ndx >= 0)
+ err = "address prohibits []";
+ else {
+ if (reg >= 0)
+ err = "address prohibits register";
+ else {
+ if (len != ' ')
+ err = "address prohibits displacement length specifier";
+ else {
+ err = " "; /* succeed */
+ mode = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+#endif /*#Ifdef NEVER*/
+
+ /*
+ * Case of S^#.
+ *
+ * in: at 0
+ * len 's' definition
+ * hash 1 demand
+ * p:q demand not empty
+ * sign 0 by paren == 0
+ * paren 0 by "()" scan logic because "S^" seen
+ * reg -1 or nn by mistake
+ * ndx -1
+ *
+ * out: mode 0
+ * reg -1
+ * len 's'
+ * exp
+ * ndx -1
+ */
+ if (!*err && len == 's') {
+ if (!hash || paren || at || ndx >= 0)
+ err = "invalid operand of S^#";
+ else {
+ if (reg >= 0) {
+ /*
+ * SHIT! we saw S^#Rnn ! put the Rnn back in
+ * expression. KLUDGE! Use oldq so we don't
+ * need to know exact length of reg name.
+ */
+ q = oldq;
+ reg = 0;
+ }
+ /*
+ * We have all the expression we will ever get.
+ */
+ if (p > q)
+ err = "S^# needs expression";
+ else if (access == 'r') {
+ err = " "; /* WIN! */
+ mode = 0;
+ } else
+ err = "S^# may only read-access";
+ }
+ }
+
+ /*
+ * Case of -(Rn), which is weird case.
+ *
+ * in: at 0
+ * len '
+ * hash 0
+ * p:q q<p
+ * sign -1 by definition
+ * paren 1 by definition
+ * reg present by definition
+ * ndx optional
+ *
+ * out: mode 7
+ * reg present
+ * len ' '
+ * exp "" enforce empty expression
+ * ndx optional warn if same as reg
+ */
+ if (!*err && sign < 0) {
+ if (len != ' ' || hash || at || p <= q)
+ err = "invalid operand of -()";
+ else {
+ err = " "; /* win */
+ mode = 7;
+ if (reg == PC)
+ wrn = "-(PC) unpredictable";
+ else if (reg == ndx)
+ wrn = "[]index same as -()register: unpredictable";
+ }
+ }
+
+ /*
+ * We convert "(Rn)" to "@Rn" for our convenience.
+ * (I hope this is convenient: has someone got a better way to parse this?)
+ * A side-effect of this is that "@Rn" is a valid operand.
+ */
+ if (paren && !sign && !hash && !at && len == ' ' && p > q) {
+ at = 1;
+ paren = 0;
+ }
+
+ /*
+ * Case of (Rn)+, which is slightly different.
+ *
+ * in: at
+ * len ' '
+ * hash 0
+ * p:q q<p
+ * sign +1 by definition
+ * paren 1 by definition
+ * reg present by definition
+ * ndx optional
+ *
+ * out: mode 8+@
+ * reg present
+ * len ' '
+ * exp "" enforce empty expression
+ * ndx optional warn if same as reg
+ */
+ if (!*err && sign > 0) {
+ if (len != ' ' || hash || p <= q)
+ err = "invalid operand of ()+";
+ else {
+ err = " "; /* win */
+ mode = 8 + (at ? 1 : 0);
+ if (reg == PC)
+ wrn = "(PC)+ unpredictable";
+ else if (reg == ndx)
+ wrn = "[]index same as ()+register: unpredictable";
+ }
+ }
+
+ /*
+ * Case of #, without S^.
+ *
+ * in: at
+ * len ' ' or 'i'
+ * hash 1 by definition
+ * p:q
+ * sign 0
+ * paren 0
+ * reg absent
+ * ndx optional
+ *
+ * out: mode 8+@
+ * reg PC
+ * len ' ' or 'i'
+ * exp
+ * ndx optional
+ */
+ if (!*err && hash) {
+ if (len != 'i' && len != ' ')
+ err = "# conflicts length";
+ else if (paren)
+ err = "# bars register";
+ else {
+ if (reg >= 0) {
+ /*
+ * SHIT! we saw #Rnn! Put the Rnn back into the expression.
+ * By using oldq, we don't need to know how long Rnn was.
+ * KLUDGE!
+ */
+ q = oldq;
+ reg = -1; /* no register any more */
+ }
+ err = " "; /* win */
+
+ /* JF a bugfix, I think! */
+ if (at && access == 'a')
+ vopP->vop_nbytes=4;
+
+ mode = (at ? 9 : 8);
+ reg = PC;
+ if ((access == 'm' || access == 'w') && !at)
+ wrn = "writing or modifying # is unpredictable";
+ }
+ }
+ /*
+ * If !*err, then sign == 0
+ * hash == 0
+ */
+
+ /*
+ * Case of Rn. We seperate this one because it has a few special
+ * errors the remaining modes lack.
+ *
+ * in: at optional
+ * len ' '
+ * hash 0 by program logic
+ * p:q empty
+ * sign 0 by program logic
+ * paren 0 by definition
+ * reg present by definition
+ * ndx optional
+ *
+ * out: mode 5+@
+ * reg present
+ * len ' ' enforce no length
+ * exp "" enforce empty expression
+ * ndx optional warn if same as reg
+ */
+ if (!*err && !paren && reg >= 0) {
+ if (len != ' ')
+ err = "length not needed";
+ else if (at) {
+ err = " "; /* win */
+ mode = 6; /* @Rn */
+ } else if (ndx >= 0)
+ err = "can't []index a register, because it has no address";
+ else if (access == 'a')
+ err = "a register has no address";
+ else {
+ /*
+ * Idea here is to detect from length of datum
+ * and from register number if we will touch PC.
+ * Warn if we do.
+ * vop_nbytes is number of bytes in operand.
+ * Compute highest byte affected, compare to PC0.
+ */
+ if ((vopP->vop_nbytes + reg * 4) > 60)
+ wrn = "PC part of operand unpredictable";
+ err = " "; /* win */
+ mode = 5; /* Rn */
+ }
+ }
+ /*
+ * If !*err, sign == 0
+ * hash == 0
+ * paren == 1 OR reg == -1
+ */
+
+ /*
+ * Rest of cases fit into one bunch.
+ *
+ * in: at optional
+ * len ' ' or 'b' or 'w' or 'l'
+ * hash 0 by program logic
+ * p:q expected (empty is not an error)
+ * sign 0 by program logic
+ * paren optional
+ * reg optional
+ * ndx optional
+ *
+ * out: mode 10 + @ + len
+ * reg optional
+ * len ' ' or 'b' or 'w' or 'l'
+ * exp maybe empty
+ * ndx optional warn if same as reg
+ */
+ if (!*err) {
+ err = " "; /* win (always) */
+ mode = 10 + (at ? 1 : 0);
+ switch (len) {
+ case 'l':
+ mode += 2;
+ case 'w':
+ mode += 2;
+ case ' ': /* assumed B^ until our caller changes it */
+ case 'b':
+ break;
+ }
+ }
+
+ /*
+ * here with completely specified mode
+ * len
+ * reg
+ * expression p,q
+ * ndx
+ */
+
+ if (*err == ' ')
+ err = ""; /* " " is no longer an error */
+
+ vopP->vop_mode = mode;
+ vopP->vop_reg = reg;
+ vopP->vop_short = len;
+ vopP->vop_expr_begin = p;
+ vopP->vop_expr_end = q;
+ vopP->vop_ndx = ndx;
+ vopP->vop_error = err;
+ vopP->vop_warn = wrn;
+ return (bug);
+
+} /* vip_op() */
+
+/*
+
+ Summary of vip_op outputs.
+
+ mode reg len ndx
+ (Rn) => @Rn
+ {@}Rn 5+@ n ' ' optional
+ branch operand 0 -1 ' ' -1
+ S^#foo 0 -1 's' -1
+ -(Rn) 7 n ' ' optional
+ {@}(Rn)+ 8+@ n ' ' optional
+ {@}#foo, no S^ 8+@ PC " i" optional
+ {@}{q^}{(Rn)} 10+@+q option " bwl" optional
+
+ */
+
+#ifdef TEST /* #Define to use this testbed. */
+
+/*
+ * Follows a test program for this function.
+ * We declare arrays non-local in case some of our tiny-minded machines
+ * default to small stacks. Also, helps with some debuggers.
+ */
+
+#include <stdio.h>
+
+char answer[100]; /* human types into here */
+char *p; /* */
+char *myerr;
+char *mywrn;
+char *mybug;
+char myaccess;
+char mywidth;
+char mymode;
+char myreg;
+char mylen;
+char *myleft;
+char *myright;
+char myndx;
+int my_operand_length;
+char my_immediate[200];
+char my_indirect[200];
+char my_displen[200];
+
+main ()
+{
+ char *vip_op (); /* make cc happy */
+
+ printf ("enter immediate symbols eg enter # ");
+ gets (my_immediate);
+ printf ("enter indirect symbols eg enter @ ");
+ gets (my_indirect);
+ printf ("enter displen symbols eg enter ^ ");
+ gets (my_displen);
+ vip_op_defaults (my_immediate, my_indirect, my_displen);
+ for (;;) {
+ printf ("access,width (eg 'ab' or 'wh') [empty line to quit] : ");
+ fflush (stdout);
+ gets (answer);
+ if (!answer[0])
+ exit (0);
+ myaccess = answer[0];
+ mywidth = answer[1];
+ switch (mywidth) {
+ case 'b':
+ my_operand_length = 1;
+ break;
+ case 'd':
+ my_operand_length = 8;
+ break;
+ case 'f':
+ my_operand_length = 4;
+ break;
+ case 'g':
+ my_operand_length = 16;
+ break;
+ case 'h':
+ my_operand_length = 32;
+ break;
+ case 'l':
+ my_operand_length = 4;
+ break;
+ case 'o':
+ my_operand_length = 16;
+ break;
+ case 'q':
+ my_operand_length = 8;
+ break;
+ case 'w':
+ my_operand_length = 2;
+ break;
+ case '!':
+ case '?':
+ case '-':
+ my_operand_length = 0;
+ break;
+
+ default:
+ my_operand_length = 2;
+ printf ("I dn't understand access width %c\n", mywidth);
+ break;
+ }
+ printf ("VAX assembler instruction operand: ");
+ fflush (stdout);
+ gets (answer);
+ mybug = vip_op (answer, myaccess, mywidth, my_operand_length,
+ &mymode, &myreg, &mylen, &myleft, &myright, &myndx,
+ &myerr, &mywrn);
+ if (*myerr) {
+ printf ("error: \"%s\"\n", myerr);
+ if (*mybug)
+ printf (" bug: \"%s\"\n", mybug);
+ } else {
+ if (*mywrn)
+ printf ("warning: \"%s\"\n", mywrn);
+ mumble ("mode", mymode);
+ mumble ("register", myreg);
+ mumble ("index", myndx);
+ printf ("width:'%c' ", mylen);
+ printf ("expression: \"");
+ while (myleft <= myright)
+ putchar (*myleft++);
+ printf ("\"\n");
+ }
+ }
+}
+
+mumble (text, value)
+char *text;
+int value;
+{
+ printf ("%s:", text);
+ if (value >= 0)
+ printf ("%xx", value);
+ else
+ printf ("ABSENT");
+ printf (" ");
+}
+
+#endif /* ifdef TEST */
+
+/* end: vip_op.c */
+
+const int md_short_jump_size = 3;
+const int md_long_jump_size = 6;
+const int md_reloc_size = 8; /* Size of relocation record */
+
+void
+ md_create_short_jump (ptr, from_addr, to_addr, frag, to_symbol)
+char *ptr;
+long from_addr, to_addr;
+fragS *frag;
+symbolS *to_symbol;
+{
+ long offset;
+
+ offset = to_addr - (from_addr + 1);
+ *ptr++ = 0x31;
+ md_number_to_chars(ptr, offset, 2);
+}
+
+void
+ md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol)
+char *ptr;
+long from_addr, to_addr;
+fragS *frag;
+symbolS *to_symbol;
+{
+ long offset;
+
+ offset = to_addr - S_GET_VALUE(to_symbol);
+ *ptr++ = 0x17;
+ *ptr++ = 0x9F;
+ md_number_to_chars(ptr, offset, 4);
+ fix_new(frag, ptr - frag->fr_literal, 4, to_symbol, (symbolS *) 0, (long) 0, 0, NO_RELOC);
+}
+
+#ifdef OBJ_VMS
+extern char vms_name_mapping;
+#endif
+
+int
+ md_parse_option (argP, cntP, vecP)
+char **argP;
+int *cntP;
+char ***vecP;
+{
+ char *temp_name; /* name for -t or -d options */
+ char opt;
+
+ switch (**argP) {
+ case 'J':
+ /* as_warn ("I can do better than -J!"); */
+ break;
+
+ case 'S':
+ as_warn ("SYMBOL TABLE not implemented");
+ break; /* SYMBOL TABLE not implemented */
+
+ case 'T':
+ as_warn ("TOKEN TRACE not implemented");
+ break; /* TOKEN TRACE not implemented */
+
+ case 'd':
+ case 't':
+ opt= **argP;
+ if (**argP) { /* Rest of argument is filename. */
+ temp_name = *argP;
+ while (**argP)
+ (*argP)++;
+ } else if (*cntP) {
+ while (**argP)
+ (*argP)++;
+ --(*cntP);
+ temp_name = *++(*vecP);
+ **vecP = NULL; /* Remember this is not a file-name. */
+ } else {
+ as_warn ("I expected a filename after -%c.",opt);
+ temp_name = "{absent}";
+ }
+
+ if (opt == 'd')
+ as_warn ("Displacement length %s ignored!", temp_name);
+ else
+ as_warn ("I don't need or use temp. file \"%s\".", temp_name);
+ break;
+
+ case 'V':
+ as_warn ("I don't use an interpass file! -V ignored");
+ break;
+
+#ifdef OBJ_VMS
+ case '+': /* For g++ */
+ break;
+
+ case '1': /* For backward compatibility */
+ break;
+
+ case 'h': /* No hashing of mixed-case names */
+ vms_name_mapping = 0;
+ (*argP)++;
+ if (**argP) vms_name_mapping = *((*argP)++) - '0';
+ (*argP)--;
+ break;
+
+ case 'H': /* Show new symbol after hash truncation */
+ break;
+#endif
+
+ default:
+ return 0;
+
+ }
+ return 1;
+}
+
+/* We have no need to default values of symbols. */
+
+/* ARGSUSED */
+symbolS *
+ md_undefined_symbol (name)
+char *name;
+{
+ return 0;
+}
+
+/* Parse an operand that is machine-specific.
+ We just return without modifying the expression if we have nothing
+ to do. */
+
+/* ARGSUSED */
+void
+ md_operand (expressionP)
+expressionS *expressionP;
+{
+}
+
+/* Round up a section size to the appropriate boundary. */
+long
+ md_section_align (segment, size)
+segT segment;
+long size;
+{
+ return size; /* Byte alignment is fine */
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the vax, they're relative to the address of the offset, plus
+ its size. (??? Is this right? FIXME-SOON) */
+long
+ md_pcrel_from (fixP)
+fixS *fixP;
+{
+ return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/* end of tc-vax.c */
diff --git a/gnu/usr.bin/as/config/tc-vax.h b/gnu/usr.bin/as/config/tc-vax.h
new file mode 100644
index 0000000..d3972e1
--- /dev/null
+++ b/gnu/usr.bin/as/config/tc-vax.h
@@ -0,0 +1,25 @@
+/*
+ * This file is tc-vax.h.
+ */
+
+#define TC_VAX 1
+
+#define NO_LISTING
+
+ /* use this to compare against gas-1.38 */
+#ifdef OLD_GAS
+#define REVERSE_SORT_RELOCS
+#endif
+
+#define tc_aout_pre_write_hook(x) {;} /* not used */
+#define tc_crawl_symbol_chain(a) {;} /* not used */
+#define tc_headers_hook(a) {;} /* not used */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of tc-vax.h */
diff --git a/gnu/usr.bin/as/config/te-dpx2.h b/gnu/usr.bin/as/config/te-dpx2.h
new file mode 100644
index 0000000..5f358a2
--- /dev/null
+++ b/gnu/usr.bin/as/config/te-dpx2.h
@@ -0,0 +1,8 @@
+/* Machine specific defines for the dpx2 machine */
+#define dpx2
+#define TC_M68K
+
+/* The magic number is not the usual MC68MAGIC. */
+#define FILE_HEADER_MAGIC MC68KBCSMAGIC
+
+/* end of te-dpx2.h */
diff --git a/gnu/usr.bin/as/config/te-generic.h b/gnu/usr.bin/as/config/te-generic.h
new file mode 100644
index 0000000..f72d5ee
--- /dev/null
+++ b/gnu/usr.bin/as/config/te-generic.h
@@ -0,0 +1,25 @@
+/*
+ * This file is te-generic.h and is intended to be a template for
+ * target environment specific header files.
+ *
+ * It is my intent that this file will evolve into a file suitable for config,
+ * compile, and copying as an aid for testing and porting. xoxorich.
+ */
+/*
+ * $Id: te-generic.h,v 1.1 1993/10/02 20:59:49 pk Exp $
+ */
+
+
+#define TE_GENERIC 1
+
+/* these define interfaces */
+#include "obj-format.h"
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of te-generic.h */
diff --git a/gnu/usr.bin/as/config/te-hpux.h b/gnu/usr.bin/as/config/te-hpux.h
new file mode 100644
index 0000000..5458df6
--- /dev/null
+++ b/gnu/usr.bin/as/config/te-hpux.h
@@ -0,0 +1,99 @@
+/* Special version of <a.out.h> for use under hp-ux.
+ Copyright (C) 1988, 1992 Free Software Foundation, Inc.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This file is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this file; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define TE_HPUX
+
+#define HP9000S200_ID (0x20C)
+#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE (HP9000S200_ID)
+
+ /* hpux has "special" headers. */
+#define H_GET_HEADER_SIZE(h) (64)
+
+#include "obj-format.h"
+
+/* This stuff is from an old a.out.hpux.h. It isn't used anymore,
+ (see obj-aout.c, obj_header_append) but I'm including it here for
+ context. xoxorich. */
+
+#if comment
+
+/* The `exec' structure and overall layout must be close to HP's when
+ we are running on an HP system, otherwise we will not be able to
+ execute the resulting file. */
+
+/* Allow this file to be included twice. */
+#ifndef __GNU_EXEC_MACROS__
+
+struct exec
+{
+ unsigned short a_machtype; /* machine type */
+ unsigned short a_info; /* magic number */
+ unsigned long a_spare1;
+ unsigned long a_spare2;
+ unsigned long a_text; /* length of text, in bytes */
+ unsigned long a_data; /* length of data, in bytes */
+ unsigned long a_bss; /* length of uninitialized data area for file, in bytes */
+ unsigned long a_trsize; /* length of relocation info for text, in bytes */
+ unsigned long a_drsize; /* length of relocation info for data, in bytes */
+ unsigned long a_spare3; /* HP = pascal interface size */
+ unsigned long a_spare4; /* HP = symbol table size */
+ unsigned long a_spare5; /* HP = debug name table size */
+ unsigned long a_entry; /* start address */
+ unsigned long a_spare6; /* HP = source line table size */
+ unsigned long a_spare7; /* HP = value table size */
+ unsigned long a_syms; /* length of symbol table data in file, in bytes */
+ unsigned long a_spare8;
+};
+
+/* Tell a.out.gnu.h not to define `struct exec'. */
+#define __STRUCT_EXEC_OVERRIDE__
+
+#include "a.out.gnu.h"
+
+#undef N_MAGIC
+#undef N_MACHTYPE
+#undef N_FLAGS
+#undef N_SET_INFO
+#undef N_SET_MAGIC
+#undef N_SET_MACHTYPE
+#undef N_SET_FLAGS
+
+#define N_MAGIC(exec) ((exec) . a_magic)
+#define N_MACHTYPE(exec) ((exec) . a_machtype)
+#define N_SET_MAGIC(exec, magic) (((exec) . a_magic) = (magic))
+#define N_SET_MACHTYPE(exec, machtype) (((exec) . a_machtype) = (machtype))
+
+#undef N_BADMAG
+#define N_BADMAG(x) ((_N_BADMAG (x)) || (_N_BADMACH (x)))
+
+#define _N_BADMACH(x) \
+(((N_MACHTYPE (x)) != HP9000S200_ID) && \
+ ((N_MACHTYPE (x)) != HP98x6_ID))
+
+#define HP98x6_ID 0x20A
+#define HP9000S200_ID 0x20C
+
+#undef _N_HDROFF
+#define _N_HDROFF(x) (SEGMENT_SIZE - (sizeof (struct exec)))
+
+#define SEGMENT_SIZE 0x1000
+
+#endif /* __GNU_EXEC_MACROS__ */
+
+#endif /* comment */
+
+/* end of te-hpux.h */
diff --git a/gnu/usr.bin/as/config/te-i386aix.h b/gnu/usr.bin/as/config/te-i386aix.h
new file mode 100644
index 0000000..dcadbc3
--- /dev/null
+++ b/gnu/usr.bin/as/config/te-i386aix.h
@@ -0,0 +1,19 @@
+/*
+ * This file is te-i386aix.h and is built from pieces of code from Minh Tran-Le
+ * <TRANLE@INTELLICORP.COM> by rich@cygnus.com.
+ */
+
+#define TE_I386AIX 1
+
+#include "obj-format.h"
+
+#define KEEP_RELOC_INFO
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 79
+ * End:
+ */
+
+/* end of te-i386aix.h */
diff --git a/gnu/usr.bin/as/config/te-ic960.h b/gnu/usr.bin/as/config/te-ic960.h
new file mode 100644
index 0000000..4858c7d
--- /dev/null
+++ b/gnu/usr.bin/as/config/te-ic960.h
@@ -0,0 +1,46 @@
+/* This file is twe-ic960.h
+
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * This file is te-ic960.h and is intended to define ic960 environment
+ * specific differences.
+ */
+
+#define TE_IC960 1
+
+/* intel uses host byte order for headers */
+#ifdef CROSS_COMPILE
+#undef CROSS_COMPILE
+#endif /* CROSS_COMPILE */
+
+#define OBJ_COFF_OMIT_OPTIONAL_HEADER
+#define LOCAL_LABEL(name) ( (name[0] == 'L') \
+ || (name[0] == '.' \
+ && (name[1] == 'C' || name[1] == 'I' || name[1] == '.')))
+#include "obj-format.h"
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of te-ic960.h */
diff --git a/gnu/usr.bin/as/config/te-sco386.h b/gnu/usr.bin/as/config/te-sco386.h
new file mode 100644
index 0000000..da8de1d
--- /dev/null
+++ b/gnu/usr.bin/as/config/te-sco386.h
@@ -0,0 +1,7 @@
+/* Machine specific defines for the SCO Unix V.3.2 ODT */
+#define scounix
+
+/* Return true if s (a non null string pointer), points to a local variable name. */
+#define LOCAL_LABEL(n) ((n)[0] == '.' && (n)[1] == 'L')
+
+/* end of te-sco386.h */
diff --git a/gnu/usr.bin/as/config/te-sequent.h b/gnu/usr.bin/as/config/te-sequent.h
new file mode 100644
index 0000000..fbf9d9a
--- /dev/null
+++ b/gnu/usr.bin/as/config/te-sequent.h
@@ -0,0 +1,32 @@
+/*
+ * This file is te-sequent.h and is intended to set up emulation with
+ * sequent's development tools.
+ *
+ */
+
+#define TE_SEQUENT 1
+
+ /* sequent has a "special" header. */
+#define H_GET_HEADER_SIZE(h) (128)
+
+#ifdef TC_I386
+ /* zmagic is 0x22eb */
+#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE (0x12eb)
+#endif /* TC_I386 */
+
+#ifdef TC_NS32K
+ /* zmagic is 0x10ea */
+#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE (0x00ea)
+#endif /* TC_NS32K */
+
+/* these define interfaces */
+#include "obj-format.h"
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of te-sequent.h */
diff --git a/gnu/usr.bin/as/config/te-sun3.h b/gnu/usr.bin/as/config/te-sun3.h
new file mode 100644
index 0000000..e559f28
--- /dev/null
+++ b/gnu/usr.bin/as/config/te-sun3.h
@@ -0,0 +1,49 @@
+/* te-sun3.h -- Sun-3 target environment declarations.
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This header file contains the #defines specific
+ to SUN computer SUN 3 series computers. (The only kind
+ we have around here, unfortunatly.)
+
+ Rumor has it that this file will work on the Sun-2 if the assembler
+ is called with -m68010 This is not tested. */
+
+
+/* Could also be :
+ #define S_LOCAL_NAME(s) (S_GET_NAME(s)[0] == '.' &&
+ S_GET_NAME(s)[1] == 'L' ||
+ S_GET_NAME(s)[1] == '.')
+ */
+
+/* This variable contains the value to write out at the beginning of
+ the a.out file. The 2<<16 means that this is a 68020 file instead
+ of an old-style 68000 file */
+
+#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE (2<<16|OMAGIC) /* Magic byte for file header */
+
+#include "obj-format.h"
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of te-sun3.h */
diff --git a/gnu/usr.bin/as/config/te-sysv32.h b/gnu/usr.bin/as/config/te-sysv32.h
new file mode 100644
index 0000000..99702fb
--- /dev/null
+++ b/gnu/usr.bin/as/config/te-sysv32.h
@@ -0,0 +1,4 @@
+/* Remove leading underscore from the gcc generated symbol names */
+#define STRIP_UNDERSCORE
+
+/* end of te-sysv32.h */
diff --git a/gnu/usr.bin/as/config/vax-inst.h b/gnu/usr.bin/as/config/vax-inst.h
new file mode 100644
index 0000000..1c10191
--- /dev/null
+++ b/gnu/usr.bin/as/config/vax-inst.h
@@ -0,0 +1,77 @@
+/* vax-inst.h - GNU - Part of vax.c
+ Copyright (C) 1987, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * This is part of vax-ins-parse.c & friends.
+ * We want to parse a vax instruction text into a tree defined here.
+ */
+
+#define VIT_MAX_OPERANDS (6) /* maximum number of operands in one */
+/* single vax instruction */
+
+struct vop /* vax instruction operand */
+{
+ short int vop_ndx; /* -1, or index register. eg 7=[R7] */
+ short int vop_reg; /* -1, or register number. eg @I^#=0xF */
+ /* Helps distinguish "abs" from "abs(PC)". */
+ short int vop_mode; /* addressing mode 4 bits. eg I^#=0x9 */
+ char vop_short; /* operand displacement length as written */
+ /* ' '=none, "bilsw"=B^I^L^S^W^. */
+ char vop_access; /* 'b'branch ' 'no-instruction 'amrvw'norm */
+ char vop_width; /* Operand width, one of "bdfghloqw" */
+ char *vop_warn; /* warning message of this operand, if any */
+ char *vop_error; /* say if operand is inappropriate */
+ char *vop_expr_begin; /* Unparsed expression, 1st char ... */
+ char *vop_expr_end; /* ... last char. */
+ unsigned char vop_nbytes; /* number of bytes in datum */
+};
+
+
+typedef long vax_opcodeT; /* For initialising array of opcodes */
+/* Some synthetic opcodes > 16 bits! */
+
+#define VIT_OPCODE_SYNTHETIC 0x80000000 /* Not real hardware instruction. */
+#define VIT_OPCODE_SPECIAL 0x40000000 /* Not normal branch optimising. */
+/* Never set without ..._SYNTHETIC */
+
+#define VAX_WIDTH_UNCONDITIONAL_JUMP '-' /* These are encoded into */
+#define VAX_WIDTH_CONDITIONAL_JUMP '?' /* vop_width when vop_access == 'b' */
+#define VAX_WIDTH_WORD_JUMP '!' /* and VIT_OPCODE_SYNTHETIC set. */
+#define VAX_WIDTH_BYTE_JUMP ':' /* */
+
+#define VAX_JMP (0x17) /* Useful for branch optimising. Jump instr*/
+#define VAX_PC_RELATIVE_MODE (0xef) /* Use it after VAX_JMP */
+#define VAX_ABSOLUTE_MODE (0x9F) /* Use as @#... */
+#define VAX_BRB (0x11) /* Canonical branch. */
+#define VAX_BRW (0x31) /* Another canonical branch */
+#define VAX_WIDEN_WORD (0x20) /* Add this to byte branch to get word br. */
+#define VAX_WIDEN_LONG (0x6) /* Add this to byte branch to get long jmp.*/
+/* Needs VAX_PC_RELATIVE_MODE byte after it*/
+
+struct vit /* vax instruction tree */
+{
+ /* vit_opcode is char[] for portability. */
+ char vit_opcode[ sizeof (vax_opcodeT) ];
+ unsigned char vit_opcode_nbytes; /* How long is _opcode? (chars) */
+ unsigned char vit_operands;/* */
+ struct vop vit_operand[VIT_MAX_OPERANDS]; /* operands */
+ char * vit_error; /* "" or error text */
+};
+
+/* end of vax-inst.h */
diff --git a/gnu/usr.bin/as/configdos.bat b/gnu/usr.bin/as/configdos.bat
new file mode 100644
index 0000000..18331cd
--- /dev/null
+++ b/gnu/usr.bin/as/configdos.bat
@@ -0,0 +1,14 @@
+@echo off
+echo Configuring GAS for H8/300
+
+copy config\ho-go32.h host.h
+copy config\tc-h8300.c targ-cpu.c
+copy config\tc-h8300.h targ-cpu.h
+copy config\te-generic.h targ-env.h
+copy config\objcoff-bfd.h obj-format.h
+copy config\objcoff-bfd.c obj-format.c
+copy config\atof-ieee.c atof-targ.c
+
+copy Makefile.dos Makefile
+
+
diff --git a/gnu/usr.bin/as/configure.in b/gnu/usr.bin/as/configure.in
new file mode 100755
index 0000000..52f4b29
--- /dev/null
+++ b/gnu/usr.bin/as/configure.in
@@ -0,0 +1,204 @@
+# This file is configure.in
+#
+# Copyright (C) 1987-1992 Free Software Foundation, Inc.
+#
+# This file is part of GAS, the GNU Assembler.
+#
+# GAS is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GAS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GAS; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+#
+
+# This file is a shell script that supplies the information necessary
+# to tailor a template configure script into the configure script
+# appropriate for this directory. For more information, check any
+# existing configure script.
+
+srctrigger=as.c
+srcname="gas"
+need_bfd=
+configdirs=doc
+
+# per-host:
+
+gas_host=generic
+
+case "${host_cpu}" in
+a29k | rs6000 | vax)
+ case "${host_os}" in
+ vms*) gas_host=vms ;;
+ *) gas_host=${host_cpu} ;;
+ esac
+ ;;
+mips)
+ case "${host_os}" in
+ ultrix) gas_host=decstation ;;
+ esac
+ ;;
+i386)
+ case "${host_os}" in
+ aix*) gas_host=i386aix ;;
+ sysv4*)
+ gas_host=i386
+ host_makefile_frag=config/ho-i386v4
+ ;;
+ esac
+ ;;
+*)
+ case "${host_os}" in
+ ansi | ultrix | hpux | sysv*) gas_host=${host_os} ;;
+ *)
+ case "${host_vendor}" in
+ sun)
+ case "${host_cpu}" in
+ m68k) gas_host=sun3 ;;
+ i386) gas_host=sun386 ;;
+ sparc) gas_host=sun4 ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+esac
+
+# per-target:
+
+# assign cpu type
+environment=generic
+
+cpu_type=${target_cpu}
+
+# assign object format
+case ${target_os} in
+aix*)
+ case "${target_cpu}" in
+ i386) obj_format=coff
+ target_cpu=i386aix
+ environment=i386aix
+ ;;
+ esac
+ ;;
+
+bout*) obj_format=bout ;;
+nindy*) obj_format=bout ;;
+bsd* | sunos*)
+ obj_format=aout
+ case "${target_cpu}" in
+ m68k) environment=sun3 ;;
+ i386 | ns32k)
+ case "${target_vendor}" in
+ sequent) environment=${target_vendor} ;;
+ esac
+ esac
+ ;;
+
+ebmon-old)
+ obj_format=coff
+ need_bfd="$(unsubdir)/../bfd$(subdir)/libbfd.a"
+ target_cpu=ebmon29k
+ ;;
+
+ebmon)
+ obj_format=coffbfd
+ need_bfd="$(unsubdir)/../bfd$(subdir)/libbfd.a"
+ target_cpu=ebmon29k
+ ;;
+
+generic) obj_format=generic ;;
+
+hms)
+ obj_format=coffbfd
+ need_bfd="$(unsubdir)/../bfd$(subdir)/libbfd.a"
+ ;;
+
+hpux)
+ obj_format=aout
+ environment=hpux
+ ;;
+
+sysv32)
+ obj_format=coff
+ environment=sysv32
+ ;;
+
+vms)
+ obj_format=vms
+ ;;
+
+coff* | sysv*)
+ obj_format=coff
+
+ case ${target_vendor} in
+ bull) environment=dpx2 ;;
+ sco) environment=sco386 ;;
+ sun) environment=sun3 ;;
+ *)
+ esac
+ ;;
+vxworks)
+ case ${target_cpu} in
+ i960) obj_format=bout ;;
+ *) obj_format=aout ;;
+ esac
+ ;;
+*)
+ case ${target_vendor} in
+ aout) obj_format=aout ;;
+ bout) obj_format=bout ;;
+ coff)
+ obj_format=coff
+ case ${target_cpu} in
+ i960) environment=ic960 ;;
+ esac
+ ;;
+ sequent)
+ obj_format=aout
+ environment=sequent
+ ;;
+ *) obj_format=aout ;;
+ esac
+ ;;
+
+esac
+
+# assign floating point type
+case ${target_cpu} in
+ns32k) atof=ns32k ;;
+tahoe) atof=tahoe ;;
+vax) atof=vax ;;
+*) atof=ieee ;;
+esac
+
+# and target makefile frag
+
+target_makefile_frag=config/mt-${target_cpu}
+
+files="config/ho-${gas_host}.h config/tc-${cpu_type}.c \
+ config/tc-${cpu_type}.h config/te-${environment}.h \
+ config/obj-${obj_format}.h config/obj-${obj_format}.c \
+ config/atof-${atof}.c"
+
+links="host.h targ-cpu.c targ-cpu.h targ-env.h obj-format.h obj-format.c atof-targ.c"
+
+# post-target:
+
+if [ ${target_alias} != ${host_alias} ] ; then
+ echo INTERNAL_CFLAGS=-DCROSS_COMPILE > Makefile.tem
+ cat Makefile >> Makefile.tem
+ mv Makefile.tem Makefile
+else
+ true
+fi
+
+# end of gas/configure.in
diff --git a/gnu/usr.bin/as/debug.c b/gnu/usr.bin/as/debug.c
new file mode 100644
index 0000000..30717ff
--- /dev/null
+++ b/gnu/usr.bin/as/debug.c
@@ -0,0 +1,104 @@
+/* This file is debug.c
+
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Routines for debug use only. */
+
+#ifndef lint
+static char rcsid[] = "$Id: debug.c,v 1.1 1993/10/02 20:57:24 pk Exp $";
+#endif
+
+#include "as.h"
+#include "subsegs.h"
+
+dmp_frags()
+{
+ frchainS *chp;
+ char *p;
+
+ for ( chp=frchain_root; chp; chp = chp->frch_next ){
+ switch ( chp->frch_seg ){
+ case SEG_DATA:
+ p ="Data";
+ break;
+ case SEG_TEXT:
+ p ="Text";
+ break;
+ default:
+ p ="???";
+ break;
+ }
+ printf("\nSEGMENT %s %d\n", p, chp->frch_subseg);
+ dmp_frag( chp->frch_root,"\t");
+ }
+}
+
+dmp_frag( fp, indent )
+ struct frag *fp;
+ char *indent;
+{
+ for ( ; fp; fp = fp->fr_next ){
+ printf("%sFRAGMENT @ 0x%x\n", indent, fp);
+ switch( fp->fr_type ){
+ case rs_align:
+ printf("%srs_align(%d)\n",indent, fp->fr_offset);
+ break;
+ case rs_fill:
+ printf("%srs_fill(%d)\n",indent, fp->fr_offset);
+ printf("%s", indent);
+ var_chars( fp, fp->fr_var + fp->fr_fix );
+ printf("%s\t repeated %d times,",
+ indent, fp->fr_offset);
+ printf(" fixed length if # chars == 0)\n");
+ break;
+ case rs_org:
+ printf("%srs_org(%d+sym @0x%x)\n",indent,
+ fp->fr_offset, fp->fr_symbol);
+ printf("%sfill with ",indent);
+ var_chars( fp, 1 );
+ printf("\n");
+ break;
+ case rs_machine_dependent:
+ printf("%smachine_dep\n",indent);
+ break;
+ default:
+ printf("%sunknown type\n",indent);
+ break;
+ }
+ printf("%saddr=%d(0x%x)\n",indent,fp->fr_address,fp->fr_address);
+ printf("%sfr_fix=%d\n",indent,fp->fr_fix);
+ printf("%sfr_var=%d\n",indent,fp->fr_var);
+ printf("%sfr_offset=%d\n",indent,fp->fr_offset);
+ printf("%schars @ 0x%x\n",indent,fp->fr_literal);
+ printf("\n");
+ }
+}
+
+var_chars( fp, n )
+ struct frag *fp;
+ int n;
+{
+ unsigned char *p;
+
+ for ( p=(unsigned char*)fp->fr_literal; n; n-- , p++ ){
+ printf("%02x ", *p );
+ }
+}
+
+/* end of debug.c */
diff --git a/gnu/usr.bin/as/doc/Makefile b/gnu/usr.bin/as/doc/Makefile
new file mode 100644
index 0000000..f2c319f
--- /dev/null
+++ b/gnu/usr.bin/as/doc/Makefile
@@ -0,0 +1,187 @@
+# This file was generated automatically by configure. Do not edit.
+host_alias = i386
+host_cpu = i386
+host_vendor = unknown
+host_os = scosysv322
+target_alias = i386
+target_cpu = i386
+target_vendor = unknown
+target_os = scosysv322
+target_makefile_frag =
+host_makefile_frag =
+site_makefile_frag =
+links =
+VPATH = .
+ALL=all.internal
+# Makefile for GNU Assembler documentation
+# - see pretex.m4 for discussion of preprocessor definitions
+# Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+#This file is part of GNU GAS.
+
+#GNU GAS is free software; you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation; either version 2, or (at your option)
+#any later version.
+
+#GNU GAS is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+
+#You should have received a copy of the GNU General Public License
+#along with GNU GAS; see the file COPYING. If not, write to
+#the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# The targets for external use include:
+# all, doc, proto, install, uninstall, includes, TAGS,
+# clean, cleanconfig, realclean, stage1, stage2, stage3, stage4.
+
+# Variables that exist for you to override.
+# See below for how to change them for certain systems.
+
+srcdir = .
+
+prefix = /usr/local
+
+bindir = $(prefix)/bin
+datadir = $(prefix)/lib
+libdir = $(prefix)/lib
+mandir = $(datadir)/man
+man1dir = $(mandir)/man1
+man2dir = $(mandir)/man2
+man3dir = $(mandir)/man3
+man4dir = $(mandir)/man4
+man5dir = $(mandir)/man5
+man6dir = $(mandir)/man6
+man7dir = $(mandir)/man7
+man8dir = $(mandir)/man8
+man9dir = $(mandir)/man9
+infodir = $(datadir)/info
+includedir = $(prefix)/include
+docdir = $(datadir)/doc
+
+SHELL = /bin/sh
+
+INSTALL = install -c
+INSTALL_PROGRAM = $(INSTALL)
+INSTALL_DATA = $(INSTALL)
+
+AR = ar
+AR_FLAGS = qv
+BISON = bison
+MAKEINFO = makeinfo
+RANLIB = ranlib
+
+# What version of the manual you want (see *.m4); "all" includes everything
+CONFIG=all
+
+# Sun/Berkeley m4 doesn't have all the things we need; use GNU or sV
+M4=gm4
+#M4=/usr/5bin/m4
+
+# Directory for gas source
+srcdir = .
+
+# Where to find texinfo.tex to format docn with TeX
+TEXIDIR = $(srcdir)/../texinfo/fsf
+
+#### host, target, and site specific Makefile frags come in here.
+##
+
+all:
+clean:
+install:
+ $(INSTALL_DATA) $(srcdir)/as.1 $(man1dir)/as.1
+
+info: as.info
+
+as.info: as-${CONFIG}.texinfo
+ makeinfo -o as.info as-${CONFIG}.texinfo
+
+install-info: as.info
+ [ -d $(infodir) ] || mkdir $(infodir)
+ for i in as.info* ; do \
+ $(INSTALL_DATA) $$i $(infodir)/$$i ; \
+ done
+
+as.dvi: as-${CONFIG}.texinfo
+ TEXINPUTS=${TEXIDIR}:.:$$TEXINPUTS tex as-${CONFIG}.texinfo
+ texindex as-${CONFIG}.??
+ TEXINPUTS=${TEXIDIR}:.:$$TEXINPUTS tex as-${CONFIG}.texinfo
+ mv as-${CONFIG}.dvi as.dvi
+ rm as-${CONFIG}.?? as-${CONFIG}.???
+
+# ROFF doc targets as.ms, as.mm, as.me
+# (we don't use a variable because we don't trust all makes to handle
+# a var in the target name right).
+# roff output (-ms)
+as.ms: as-${CONFIG}.texinfo
+ sed -e '/\\input texinfo/d' \
+ -e '/@c TEXI2ROFF-KILL/,/@c END TEXI2ROFF-KILL/d' \
+ -e 's/{.*,,/{/' \
+ as-${CONFIG}.texinfo | \
+ texi2roff -ms >as.ms
+
+# roff output (-mm)
+as.mm: as-${CONFIG}.texinfo
+ sed -e '/\\input texinfo/d' \
+ -e '/@c TEXI2ROFF-KILL/,/@c END TEXI2ROFF-KILL/d' \
+ -e 's/{.*,,/{/' \
+ -e '/@noindent/d' \
+ as-${CONFIG}.texinfo | \
+ texi2roff -mm | \
+ sed -e 's/---/\\(em/g' \
+ >as.mm
+
+# roff output (-me)
+as.me: as-${CONFIG}.texinfo
+ sed -e '/\\input texinfo/d' \
+ -e '/@c TEXI2ROFF-KILL/,/@c END TEXI2ROFF-KILL/d' \
+ -e 's/{.*,,/{/' \
+ as-${CONFIG}.texinfo | \
+ texi2roff -me >as.me
+
+
+
+as-all.texinfo: as.texinfo pretex.m4 none.m4 all.m4
+ ${M4} $(srcdir)/pretex.m4 $(srcdir)/none.m4 $(srcdir)/all.m4 $(srcdir)/as.texinfo >as-all.texinfo
+
+as-a29k.texinfo: as.texinfo pretex.m4 none.m4 a29k.m4
+ ${M4} pretex.m4 none.m4 a29k.m4 as.texinfo >as-a29k.texinfo
+
+as-a29k-coff.texinfo: as.texinfo pretex.m4 none.m4 a29k-coff.m4
+ ${M4} pretex.m4 none.m4 a29k-coff.m4 as.texinfo >as-a29k-coff.texinfo
+
+as-gen.texinfo: as.texinfo pretex.m4 none.m4 gen.m4
+ ${M4} pretex.m4 none.m4 gen.m4 as.texinfo >as-gen.texinfo
+
+as-h8.texinfo: as.texinfo pretex.m4 none.m4 h8.m4
+ ${M4} pretex.m4 none.m4 h8.m4 as.texinfo >as-h8.texinfo
+
+as-i80386.texinfo: as.texinfo pretex.m4 none.m4 i80386.m4
+ ${M4} pretex.m4 none.m4 i80386.m4 as.texinfo >as-i80386.texinfo
+
+as-i960.texinfo: as.texinfo pretex.m4 none.m4 i960.m4
+ ${M4} pretex.m4 none.m4 i960.m4 as.texinfo >as-i960.texinfo
+
+as-m680x0.texinfo: as.texinfo pretex.m4 none.m4 m680x0.m4
+ ${M4} pretex.m4 none.m4 m680x0.m4 as.texinfo >as-m680x0.texinfo
+
+as-sparc.texinfo: as.texinfo pretex.m4 none.m4 sparc.m4
+ ${M4} pretex.m4 none.m4 sparc.m4 as.texinfo >as-sparc.texinfo
+
+as-vax.texinfo: as.texinfo pretex.m4 none.m4 vax.m4
+ ${M4} pretex.m4 none.m4 vax.m4 as.texinfo >as-vax.texinfo
+
+as-vintage.texinfo: as.texinfo pretex.m4 none.m4 vintage.m4
+ ${M4} pretex.m4 none.m4 vintage.m4 as.texinfo >as-vintage.texinfo
+
+clean-info:
+ rm -f as-${CONFIG}.* as.dvi as.info*
+
+force:
+
+Makefile: $(srcdir)/Makefile.in $(host_makefile_frag) $(target_makefile_frag)
+ $(SHELL) ./config.status
+
diff --git a/gnu/usr.bin/as/doc/Makefile.in b/gnu/usr.bin/as/doc/Makefile.in
new file mode 100644
index 0000000..fdae0b2
--- /dev/null
+++ b/gnu/usr.bin/as/doc/Makefile.in
@@ -0,0 +1,172 @@
+# Makefile for GNU Assembler documentation
+# - see pretex.m4 for discussion of preprocessor definitions
+# Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+#This file is part of GNU GAS.
+
+#GNU GAS is free software; you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation; either version 2, or (at your option)
+#any later version.
+
+#GNU GAS is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+
+#You should have received a copy of the GNU General Public License
+#along with GNU GAS; see the file COPYING. If not, write to
+#the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# The targets for external use include:
+# all, doc, proto, install, uninstall, includes, TAGS,
+# clean, cleanconfig, realclean, stage1, stage2, stage3, stage4.
+
+# Variables that exist for you to override.
+# See below for how to change them for certain systems.
+
+srcdir = .
+
+prefix = /usr/local
+
+bindir = $(prefix)/bin
+datadir = $(prefix)/lib
+libdir = $(prefix)/lib
+mandir = $(datadir)/man
+man1dir = $(mandir)/man1
+man2dir = $(mandir)/man2
+man3dir = $(mandir)/man3
+man4dir = $(mandir)/man4
+man5dir = $(mandir)/man5
+man6dir = $(mandir)/man6
+man7dir = $(mandir)/man7
+man8dir = $(mandir)/man8
+man9dir = $(mandir)/man9
+infodir = $(datadir)/info
+includedir = $(prefix)/include
+docdir = $(datadir)/doc
+
+SHELL = /bin/sh
+
+INSTALL = install -c
+INSTALL_PROGRAM = $(INSTALL)
+INSTALL_DATA = $(INSTALL)
+
+AR = ar
+AR_FLAGS = qv
+BISON = bison
+MAKEINFO = makeinfo
+RANLIB = ranlib
+
+# What version of the manual you want (see *.m4); "all" includes everything
+CONFIG=all
+
+# Sun/Berkeley m4 doesn't have all the things we need; use GNU or sV
+M4=gm4
+#M4=/usr/5bin/m4
+
+# Directory for gas source
+srcdir=..
+
+# Where to find texinfo.tex to format docn with TeX
+TEXIDIR = $(srcdir)/../texinfo/fsf
+
+#### host, target, and site specific Makefile frags come in here.
+##
+
+all:
+clean:
+install:
+ $(INSTALL_DATA) $(srcdir)/as.1 $(man1dir)/as.1
+
+info: as.info
+
+as.info: as-${CONFIG}.texinfo
+ makeinfo -o as.info as-${CONFIG}.texinfo
+
+install-info: as.info
+ [ -d $(infodir) ] || mkdir $(infodir)
+ for i in as.info* ; do \
+ $(INSTALL_DATA) $$i $(infodir)/$$i ; \
+ done
+
+as.dvi: as-${CONFIG}.texinfo
+ TEXINPUTS=${TEXIDIR}:.:$$TEXINPUTS tex as-${CONFIG}.texinfo
+ texindex as-${CONFIG}.??
+ TEXINPUTS=${TEXIDIR}:.:$$TEXINPUTS tex as-${CONFIG}.texinfo
+ mv as-${CONFIG}.dvi as.dvi
+ rm as-${CONFIG}.?? as-${CONFIG}.???
+
+# ROFF doc targets as.ms, as.mm, as.me
+# (we don't use a variable because we don't trust all makes to handle
+# a var in the target name right).
+# roff output (-ms)
+as.ms: as-${CONFIG}.texinfo
+ sed -e '/\\input texinfo/d' \
+ -e '/@c TEXI2ROFF-KILL/,/@c END TEXI2ROFF-KILL/d' \
+ -e 's/{.*,,/{/' \
+ as-${CONFIG}.texinfo | \
+ texi2roff -ms >as.ms
+
+# roff output (-mm)
+as.mm: as-${CONFIG}.texinfo
+ sed -e '/\\input texinfo/d' \
+ -e '/@c TEXI2ROFF-KILL/,/@c END TEXI2ROFF-KILL/d' \
+ -e 's/{.*,,/{/' \
+ -e '/@noindent/d' \
+ as-${CONFIG}.texinfo | \
+ texi2roff -mm | \
+ sed -e 's/---/\\(em/g' \
+ >as.mm
+
+# roff output (-me)
+as.me: as-${CONFIG}.texinfo
+ sed -e '/\\input texinfo/d' \
+ -e '/@c TEXI2ROFF-KILL/,/@c END TEXI2ROFF-KILL/d' \
+ -e 's/{.*,,/{/' \
+ as-${CONFIG}.texinfo | \
+ texi2roff -me >as.me
+
+
+
+as-all.texinfo: as.texinfo pretex.m4 none.m4 all.m4
+ ${M4} $(srcdir)/pretex.m4 $(srcdir)/none.m4 $(srcdir)/all.m4 $(srcdir)/as.texinfo >as-all.texinfo
+
+as-a29k.texinfo: as.texinfo pretex.m4 none.m4 a29k.m4
+ ${M4} pretex.m4 none.m4 a29k.m4 as.texinfo >as-a29k.texinfo
+
+as-a29k-coff.texinfo: as.texinfo pretex.m4 none.m4 a29k-coff.m4
+ ${M4} pretex.m4 none.m4 a29k-coff.m4 as.texinfo >as-a29k-coff.texinfo
+
+as-gen.texinfo: as.texinfo pretex.m4 none.m4 gen.m4
+ ${M4} pretex.m4 none.m4 gen.m4 as.texinfo >as-gen.texinfo
+
+as-h8.texinfo: as.texinfo pretex.m4 none.m4 h8.m4
+ ${M4} pretex.m4 none.m4 h8.m4 as.texinfo >as-h8.texinfo
+
+as-i80386.texinfo: as.texinfo pretex.m4 none.m4 i80386.m4
+ ${M4} pretex.m4 none.m4 i80386.m4 as.texinfo >as-i80386.texinfo
+
+as-i960.texinfo: as.texinfo pretex.m4 none.m4 i960.m4
+ ${M4} pretex.m4 none.m4 i960.m4 as.texinfo >as-i960.texinfo
+
+as-m680x0.texinfo: as.texinfo pretex.m4 none.m4 m680x0.m4
+ ${M4} pretex.m4 none.m4 m680x0.m4 as.texinfo >as-m680x0.texinfo
+
+as-sparc.texinfo: as.texinfo pretex.m4 none.m4 sparc.m4
+ ${M4} pretex.m4 none.m4 sparc.m4 as.texinfo >as-sparc.texinfo
+
+as-vax.texinfo: as.texinfo pretex.m4 none.m4 vax.m4
+ ${M4} pretex.m4 none.m4 vax.m4 as.texinfo >as-vax.texinfo
+
+as-vintage.texinfo: as.texinfo pretex.m4 none.m4 vintage.m4
+ ${M4} pretex.m4 none.m4 vintage.m4 as.texinfo >as-vintage.texinfo
+
+clean-info:
+ rm -f as-${CONFIG}.* as.dvi as.info*
+
+force:
+
+Makefile: $(srcdir)/Makefile.in $(host_makefile_frag) $(target_makefile_frag)
+ $(SHELL) ./config.status
+
diff --git a/gnu/usr.bin/as/doc/a29k-coff.m4 b/gnu/usr.bin/as/doc/a29k-coff.m4
new file mode 100644
index 0000000..c3b04e1
--- /dev/null
+++ b/gnu/usr.bin/as/doc/a29k-coff.m4
@@ -0,0 +1,14 @@
+_divert__(-1)
+_define__(<_A29K__>,<1>)
+_define__(<_GENERIC__>,<0>)
+_define__(<_HOST__>,<AMD 29K>)
+_define__(<_MACH_DEP__>,<AMD29K-Dependent>)
+_define__(<_AOUT__>,<0>)
+_define__(<_BOUT__>,<0>)
+_define__(<_COFF__>,<1>)
+_define__(<_ELF__>,<0>)
+_define__(<_DIFFTABKLUG__>,0) NO difference-table kluge
+_define__(<_IEEEFLOAT__>,1) IEEE floating point
+_define__(<_W32__>,1) 32-bit words
+_define__(<_W16__>,0)
+_divert__<>
diff --git a/gnu/usr.bin/as/doc/a29k.m4 b/gnu/usr.bin/as/doc/a29k.m4
new file mode 100644
index 0000000..9564387
--- /dev/null
+++ b/gnu/usr.bin/as/doc/a29k.m4
@@ -0,0 +1,9 @@
+_divert__(-1)
+_define__(<_A29K__>,<1>)
+_define__(<_HOST__>,<AMD 29K>)
+_define__(<_MACH_DEP__>,<AMD29K-Dependent>)
+_define__(<_DIFFTABKLUG__>,0) NO difference-table kluge
+_define__(<_IEEEFLOAT__>,1) IEEE floating point
+_define__(<_W32__>,1) 32-bit words
+_define__(<_W16__>,0)
+_divert__<>
diff --git a/gnu/usr.bin/as/doc/all.m4 b/gnu/usr.bin/as/doc/all.m4
new file mode 100644
index 0000000..3d4e7fd
--- /dev/null
+++ b/gnu/usr.bin/as/doc/all.m4
@@ -0,0 +1,20 @@
+_divert__(-1)
+<$Id: all.m4,v 1.1 1993/10/02 21:00:13 pk Exp $>
+_define__(<_ALL_ARCH__>,<1>)
+_define__(<_GENERIC__>,<1>) In case none.m4 changes its mind abt default
+
+_define__(<_AOUT__>,<1>)
+_define__(<_BOUT__>,<1>)
+_define__(<_COFF__>,<1>)
+_define__(<_ELF__>,<1>)
+
+_define__(<_A29K__>,<1>)
+_define__(<_H8__>,<1>)
+_define__(<_I80386__>,<1>)
+_define__(<_I960__>,<1>)
+_define__(<_M680X0__>,<1>)
+_define__(<_SPARC__>,<1>)
+_define__(<_VAX__>,<1>)
+_define__(<_VXWORKS__>,<1>)
+
+_divert__<>
diff --git a/gnu/usr.bin/as/doc/as.texinfo b/gnu/usr.bin/as/doc/as.texinfo
new file mode 100644
index 0000000..c9e0f57
--- /dev/null
+++ b/gnu/usr.bin/as/doc/as.texinfo
@@ -0,0 +1,6730 @@
+_dnl__ -*-Texinfo-*-
+_dnl__ Copyright (c) 1991 1992 Free Software Foundation, Inc.
+_dnl__ $Id: as.texinfo,v 1.1 1993/10/02 21:00:15 pk Exp $
+\input texinfo @c -*-Texinfo-*-
+@c Copyright (c) 1991 1992 Free Software Foundation, Inc.
+@c %**start of header
+@setfilename _AS__.info
+_if__(_GENERIC__)
+@settitle Using _AS__
+_fi__(_GENERIC__)
+_if__(!_GENERIC__)
+@settitle Using _AS__ (_HOST__)
+_fi__(!_GENERIC__)
+@setchapternewpage odd
+@c @smallbook
+@c @cropmarks
+@c %**end of header
+
+@finalout
+@syncodeindex ky cp
+
+_if__(0)
+
+NOTE: this manual is marked up for preprocessing with a collection
+of m4 macros called "pretex.m4".
+
+THIS IS THE FULL SOURCE. The full source needs to be run through m4
+before either tex- or info- formatting: for example,
+ m4 pretex.m4 none.m4 m680x0.m4 as.texinfo >as-680x0.texinfo
+will produce (assuming your path finds either GNU or SysV m4; Berkeley
+won't do) a file, configured for the M680x0 version of GAS, suitable for
+formatting. See the text in "pretex.m4" for a fuller explanation (and
+the macro definitions).
+
+_fi__(0)
+@c
+@ifinfo
+This file documents the GNU Assembler "_AS__".
+
+Copyright (C) 1991 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+@ignore
+Permission is granted to process this file through Tex and print the
+results, provided the printed document carries copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+@end ignore
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided also that the
+section entitled ``GNU General Public License'' is included exactly as
+in the original, and provided that the entire resulting derived work is
+distributed under the terms of a permission notice identical to this
+one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that the section entitled ``GNU General Public License'' may be
+included in a translation approved by the Free Software Foundation
+instead of in the original English.
+@end ifinfo
+
+@titlepage
+@title Using _AS__
+@subtitle The GNU Assembler
+_if__(!_GENERIC__)
+@subtitle for the _HOST__ family
+_fi__(!_GENERIC__)
+@sp 1
+@subtitle January 1992
+@sp 1
+@sp 13
+The Free Software Foundation Inc. thanks The Nice Computer
+Company of Australia for loaning Dean Elsner to write the
+first (Vax) version of @code{as} for Project GNU.
+The proprietors, management and staff of TNCCA thank FSF for
+distracting the boss while they got some work
+done.
+@sp 3
+@author Dean Elsner, Jay Fenlason & friends
+@c edited by: pesch@cygnus.com
+@page
+@tex
+\def\$#1${{#1}} % Kluge: collect RCS revision info without $...$
+\xdef\manvers{\$Revision: 1.1 $} % For use in headers, footers too
+{\parskip=0pt
+\hfill \manvers\par
+\hfill \TeX{}info \texinfoversion\par
+}
+%"boxit" macro for figures:
+%Modified from Knuth's ``boxit'' macro from TeXbook (answer to exercise 21.3)
+\gdef\boxit#1#2{\vbox{\hrule\hbox{\vrule\kern3pt
+ \vbox{\parindent=0pt\parskip=0pt\hsize=#1\kern3pt\strut\hfil
+#2\hfil\strut\kern3pt}\kern3pt\vrule}\hrule}}%box with visible outline
+\gdef\ibox#1#2{\hbox to #1{#2\hfil}\kern8pt}% invisible box
+@end tex
+
+Edited by Roland Pesch for Cygnus Support.
+
+@vskip 0pt plus 1filll
+Copyright @copyright{} 1991 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided also that the
+section entitled ``GNU General Public License'' is included exactly as
+in the original, and provided that the entire resulting derived work is
+distributed under the terms of a permission notice identical to this
+one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that the section entitled ``GNU General Public License'' may be
+included in a translation approved by the Free Software Foundation
+instead of in the original English.
+@end titlepage
+@page
+@node Top, Overview, (dir), (dir)
+@ifinfo
+This file is a user guide to the GNU assembler @code{_AS__}.
+_if__(!_GENERIC__)
+This version of the file describes @code{_AS__} configured to generate
+code for _HOST__ architectures.
+_fi__(!_GENERIC__)
+@end ifinfo
+@menu
+* Overview:: Overview
+* Invoking:: Command-Line Options
+* Syntax:: Syntax
+* Sections:: Sections and Relocation
+* Symbols:: Symbols
+* Expressions:: Expressions
+* Pseudo Ops:: Assembler Directives
+* _MACH_DEP__:: Machine Dependent Features
+* Copying:: GNU GENERAL PUBLIC LICENSE
+* Index:: Index
+@end menu
+
+@node Overview, Invoking, Top, Top
+@chapter Overview
+@iftex
+This manual is a user guide to the GNU assembler @code{_AS__}.
+_if__(!_GENERIC__)
+This version of the manual describes @code{_AS__} configured to generate
+code for _HOST__ architectures.
+_fi__(!_GENERIC__)
+@end iftex
+
+@cindex invocation summary
+@cindex option summary
+@cindex summary of options
+Here is a brief summary of how to invoke @code{_AS__}. For details,
+@pxref{Invoking,,Comand-Line Options}.
+
+@c We don't use deffn and friends for the following because they seem
+@c to be limited to one line for the header.
+@smallexample
+ _AS__ [ -a | -al | -as ] [ -D ] [ -f ]
+ [ -I @var{path} ] [ -k ] [ -L ]
+ [ -o @var{objfile} ] [ -R ] [ -v ] [ -w ]
+_if__(_A29K__)
+@c am29k has no machine-dependent assembler options
+_fi__(_A29K__)
+_if__(_H8__)
+@c h8/300 has no machine-dependent assembler options
+_fi__(_H8__)
+_if__(_I960__)
+@c see md_parse_option in i960.c
+ [ -ACA | -ACA_A | -ACB | -ACC | -AKA | -AKB | -AKC | -AMC ]
+ [ -b ] [ -norelax ]
+_fi__(_I960__)
+_if__(_M680X0__)
+ [ -l ] [ -mc68000 | -mc68010 | -mc68020 ]
+_fi__(_M680X0__)
+ [ -- | @var{files} @dots{} ]
+@end smallexample
+
+@table @code
+@item -a | -al | -as
+Turn on assembly listings; @samp{-al}, listing only, @samp{-as}, symbols
+only, @samp{-a}, everything.
+
+@item -D
+This option is accepted only for script compatibility with calls to
+other assemblers; it has no effect on @code{_AS__}.
+
+@item -f
+``fast''---skip preprocessing (assume source is compiler output)
+
+@item -I @var{path}
+Add @var{path} to the search list for @code{.include} directives
+
+@item -k
+_if__((!_GENERIC__) && !_DIFFTABKLUG__)
+This option is accepted but has no effect on the _HOST__ family.
+_fi__((!_GENERIC__) && !_DIFFTABKLUG__)
+_if__(_GENERIC__ || _DIFFTABKLUG__)
+Issue warnings when difference tables altered for long displacements.
+_fi__(_GENERIC__ || _DIFFTABKLUG__)
+
+@item -L
+Keep (in symbol table) local symbols, starting with @samp{L}
+
+@item -o @var{objfile}
+Name the object-file output from @code{_AS__}
+
+@item -R
+Fold data section into text section
+
+@item -v
+Announce @code{as} version
+
+@item -W
+Suppress warning messages
+
+_if__(_I960__)
+@item -ACA | -ACA_A | -ACB | -ACC | -AKA | -AKB | -AKC | -AMC
+_if__(_GENERIC__)
+(When configured for Intel 960).
+_fi__(_GENERIC__)
+Specify which variant of the 960 architecture is the target.
+
+@item -b
+_if__(_GENERIC__)
+(When configured for Intel 960).
+_fi__(_GENERIC__)
+Add code to collect statistics about branches taken.
+
+@item -norelax
+_if__(_GENERIC__)
+(When configured for Intel 960).
+_fi__(_GENERIC__)
+Do not alter compare-and-branch instructions for long displacements;
+error if necessary.
+_fi__(_I960__)
+
+_if__(_M680X0__)
+@item -l
+_if__(_GENERIC__)
+(When configured for Motorola 68000).
+_fi__(_GENERIC__)
+Shorten references to undefined symbols, to one word instead of two
+
+@item -mc68000 | -mc68010 | -mc68020
+_if__(_GENERIC__)
+(When configured for Motorola 68000).
+_fi__(_GENERIC__)
+Specify what processor in the 68000 family is the target (default 68020)
+_fi__(_M680X0__)
+
+@item -- | @var{files} @dots{}
+Standard input, or source files to assemble
+@end table
+
+@menu
+* Manual:: Structure of this Manual
+* GNU Assembler:: _AS__, the GNU Assembler
+* Object Formats:: Object File Formats
+* Command Line:: Command Line
+* Input Files:: Input Files
+* Object:: Output (Object) File
+* Errors:: Error and Warning Messages
+@end menu
+
+@node Manual, GNU Assembler, Overview, Overview
+@section Structure of this Manual
+
+@cindex manual, structure and purpose
+This manual is intended to describe what you need to know to use
+@sc{gnu} @code{_AS__}. We cover the syntax expected in source files, including
+notation for symbols, constants, and expressions; the directives that
+@code{_AS__} understands; and of course how to invoke @code{_AS__}.
+
+_if__(!_GENERIC__)
+We also cover special features in the _HOST__
+configuration of @code{_AS__}, including assembler directives.
+_fi__(!_GENERIC__)
+_if__(_GENERIC__)
+This manual also describes some of the machine-dependent features of
+various flavors of the assembler.
+_fi__(_GENERIC__)
+_if__(_INTERNALS__)
+This manual also describes how the assembler works internally, and
+provides some information that may be useful to people attempting to
+port the assembler to another machine.
+_fi__(_INTERNALS__)
+@refill
+
+@cindex machine instructions (not covered)
+On the other hand, this manual is @emph{not} intended as an introduction
+to programming in assembly language---let alone programming in general!
+In a similar vein, we make no attempt to introduce the machine
+architecture; we do @emph{not} describe the instruction set, standard
+mnemonics, registers or addressing modes that are standard to a
+particular architecture.
+_if__(_GENERIC__)
+You may want to consult the manufacturer's
+machine architecture manual for this information.
+_fi__(_GENERIC__)
+_if__(_H8__&&!_GENERIC__)
+For information on the H8/300 machine instruction set, see @cite{H8/300
+Series Programming Manual} (Hitachi ADE--602--025).
+_fi__(_H8__&&!_GENERIC__)
+
+
+@c I think this is premature---pesch@cygnus.com, 17jan1991
+@ignore
+Throughout this manual, we assume that you are running @dfn{GNU},
+the portable operating system from the @dfn{Free Software
+Foundation, Inc.}. This restricts our attention to certain kinds of
+computer (in particular, the kinds of computers that GNU can run on);
+once this assumption is granted examples and definitions need less
+qualification.
+
+@code{_AS__} is part of a team of programs that turn a high-level
+human-readable series of instructions into a low-level
+computer-readable series of instructions. Different versions of
+@code{_AS__} are used for different kinds of computer.
+@end ignore
+
+@c There used to be a section "Terminology" here, which defined
+@c "contents", "byte", "word", and "long". Defining "word" to any
+@c particular size is confusing when the .word directive may generate 16
+@c bits on one machine and 32 bits on another; in general, for the user
+@c version of this manual, none of these terms seem essential to define.
+@c They were used very little even in the former draft of the manual;
+@c this draft makes an effort to avoid them (except in names of
+@c directives).
+
+@node GNU Assembler, Object Formats, Manual, Overview
+@section _AS__, the GNU Assembler
+
+GNU @code{as} is really a family of assemblers.
+_if__(!_GENERIC__)
+This manual describes @code{_AS__}, a member of that family which is
+configured for the _HOST__ architectures.
+_fi__(!_GENERIC__)
+If you use (or have used) the GNU assembler on one architecture, you
+should find a fairly similar environment when you use it on another
+architecture. Each version has much in common with the others,
+including object file formats, most assembler directives (often called
+@dfn{pseudo-ops)} and assembler syntax.@refill
+
+_if__(_GENERIC__||!_H8__)
+@cindex purpose of @sc{gnu} @code{_AS__}
+@code{_AS__} is primarily intended to assemble the output of the GNU C
+compiler @code{_GCC__} for use by the linker @code{_LD__}. Nevertheless,
+we've tried to make @code{_AS__} assemble correctly everything that the native
+assembler would.
+_fi__(_GENERIC__||!_H8__)
+_if__(_VAX__)
+Any exceptions are documented explicitly (@pxref{_MACH_DEP__}).
+_fi__(_VAX__)
+_if__(_GENERIC__||_M680X0__)
+This doesn't mean @code{_AS__} always uses the same syntax as another
+assembler for the same architecture; for example, we know of several
+incompatible versions of 680x0 assembly language syntax.
+_fi__(_GENERIC__||_M680X0__)
+
+Unlike older assemblers, @code{_AS__} is designed to assemble a source
+program in one pass of the source file. This has a subtle impact on the
+@kbd{.org} directive (@pxref{Org,,@code{.org}}).
+
+@node Object Formats, Command Line, GNU Assembler, Overview
+@section Object File Formats
+
+@cindex object file format
+The GNU assembler can be configured to produce several alternative
+object file formats. For the most part, this does not affect how you
+write assembly language programs; but directives for debugging symbols
+are typically different in different file formats. @xref{Symbol
+Attributes,,Symbol Attributes}.
+_if__(!_GENERIC__)
+_if__(!(_I960__||_A29K__))
+_if__(_AOUT__ && (!_COFF__) && (!_ELF__))
+On the _HOST__, @code{_AS__} is configured to produce @code{a.out} format object
+files.@refill
+_fi__(_AOUT__ && (!_COFF__) && (!_ELF__))
+_if__((!_AOUT__) && _COFF__ && (!_ELF__))
+On the _HOST__, @code{_AS__} is configured to produce COFF format object
+files.@refill
+_fi__((!_AOUT__) && _COFF__ && (!_ELF__))
+_fi__(!(_I960__||_A29K__))
+_if__(_A29K__)
+On the _HOST__, @code{_AS__} can be configured to produce either
+@code{a.out} or COFF format object files.
+_fi__(_A29K__)
+_if__(_I960__)
+On the _HOST__, @code{_AS__} can be configured to produce either @code{b.out} or COFF
+format object files.
+_fi__(_I960__)
+_fi__(!_GENERIC__)
+
+@node Command Line, Input Files, Object Formats, Overview
+@section Command Line
+
+@cindex command line conventions
+After the program name @code{_AS__}, the command line may contain
+options and file names. Options may appear in any order, and may be
+before, after, or between file names. The order of file names is
+significant.
+
+@cindex standard input, as input file
+@kindex --
+@file{--} (two hyphens) by itself names the standard input file
+explicitly, as one of the files for @code{_AS__} to assemble.
+
+@cindex options, command line
+Except for @samp{--} any command line argument that begins with a
+hyphen (@samp{-}) is an option. Each option changes the behavior of
+@code{_AS__}. No option changes the way another option works. An
+option is a @samp{-} followed by one or more letters; the case of
+the letter is important. All options are optional.
+
+Some options expect exactly one file name to follow them. The file
+name may either immediately follow the option's letter (compatible
+with older assemblers) or it may be the next command argument (GNU
+standard). These two command lines are equivalent:
+
+@smallexample
+_AS__ -o my-object-file.o mumble.s
+_AS__ -omy-object-file.o mumble.s
+@end smallexample
+
+@node Input Files, Object, Command Line, Overview
+@section Input Files
+
+@cindex input
+@cindex source program
+@cindex files, input
+We use the phrase @dfn{source program}, abbreviated @dfn{source}, to
+describe the program input to one run of @code{_AS__}. The program may
+be in one or more files; how the source is partitioned into files
+doesn't change the meaning of the source.
+
+@c I added "con" prefix to "catenation" just to prove I can overcome my
+@c APL training... pesch@cygnus.com
+The source program is a concatenation of the text in all the files, in the
+order specified.
+
+Each time you run @code{_AS__} it assembles exactly one source
+program. The source program is made up of one or more files.
+(The standard input is also a file.)
+
+You give @code{_AS__} a command line that has zero or more input file
+names. The input files are read (from left file name to right). A
+command line argument (in any position) that has no special meaning
+is taken to be an input file name.
+
+If you give @code{_AS__} no file names it attempts to read one input file
+from the @code{_AS__} standard input, which is normally your terminal. You
+may have to type @key{ctl-D} to tell @code{_AS__} there is no more program
+to assemble.
+
+Use @samp{--} if you need to explicitly name the standard input file
+in your command line.
+
+If the source is empty, @code{_AS__} will produce a small, empty object
+file.
+
+@subheading Filenames and Line-numbers
+
+@cindex input file linenumbers
+@cindex line numbers, in input files
+There are two ways of locating a line in the input file (or files) and
+either may be used in reporting error messages. One way refers to a line
+number in a physical file; the other refers to a line number in a
+``logical'' file. @xref{Errors, ,Error and Warning Messages}.
+
+@dfn{Physical files} are those files named in the command line given
+to @code{_AS__}.
+
+@dfn{Logical files} are simply names declared explicitly by assembler
+directives; they bear no relation to physical files. Logical file names
+help error messages reflect the original source file, when @code{_AS__}
+source is itself synthesized from other files.
+@xref{App-File,,@code{.app-file}}.
+
+@node Object, Errors, Input Files, Overview
+@section Output (Object) File
+
+@cindex object file
+@cindex output file
+@kindex a.out
+@kindex .o
+Every time you run @code{_AS__} it produces an output file, which is
+your assembly language program translated into numbers. This file
+is the object file, named @code{a.out} unless you tell @code{_AS__} to
+give it another name by using the @code{-o} option. Conventionally,
+object file names end with @file{.o}. The default name of
+@file{a.out} is used for historical reasons: older assemblers were
+capable of assembling self-contained programs directly into a
+runnable program.
+@c This may still work, but hasn't been tested.
+
+@cindex linker
+@kindex ld
+The object file is meant for input to the linker @code{_LD__}. It contains
+assembled program code, information to help @code{_LD__} integrate
+the assembled program into a runnable file, and (optionally) symbolic
+information for the debugger.
+
+@c link above to some info file(s) like the description of a.out.
+@c don't forget to describe GNU info as well as Unix lossage.
+
+@node Errors, , Object, Overview
+@section Error and Warning Messages
+
+@cindex error messsages
+@cindex warning messages
+@cindex messages from @code{_AS__}
+@code{_AS__} may write warnings and error messages to the standard error
+file (usually your terminal). This should not happen when a compiler
+runs @code{_AS__} automatically. Warnings report an assumption made so
+that @code{_AS__} could keep assembling a flawed program; errors report a
+grave problem that stops the assembly.
+
+@cindex format of warning messages
+Warning messages have the format
+
+@smallexample
+file_name:@b{NNN}:Warning Message Text
+@end smallexample
+
+@noindent
+@cindex line numbers, in warnings/errors
+(where @b{NNN} is a line number). If a logical file name has
+been given (@pxref{App-File,,@code{.app-file}}) it is used for the filename, otherwise the
+name of the current input file is used. If a logical line number was
+given
+_if__(!_A29K__)
+(@pxref{Line,,@code{.line}})
+_fi__(!_A29K__)
+_if__(_A29K__)
+(@pxref{Ln,,@code{.ln}})
+_fi__(_A29K__)
+then it is used to calculate the number printed,
+otherwise the actual line in the current source file is printed. The
+message text is intended to be self explanatory (in the grand Unix
+tradition). @refill
+
+@cindex format of error messages
+Error messages have the format
+@smallexample
+file_name:@b{NNN}:FATAL:Error Message Text
+@end smallexample
+The file name and line number are derived as for warning
+messages. The actual message text may be rather less explanatory
+because many of them aren't supposed to happen.
+
+@node Invoking, Syntax, Overview, Top
+@chapter Command-Line Options
+
+@cindex options, all versions of @code{_AS__}
+This chapter describes command-line options available in @emph{all}
+versions of the GNU assembler; @pxref{_MACH_DEP__}, for options specific
+_if__(!_GENERIC__)
+to the _HOST__.
+_fi__(!_GENERIC__)
+_if__(_GENERIC__)
+to particular machine architectures.
+_fi__(_GENERIC__)
+
+@section Enable Listings: @code{-a}, @code{-al}, @code{-as}
+
+@kindex -a
+@kindex -al
+@kindex -as
+@cindex listings, enabling
+@cindex assembly listings, enabling
+These options enable listing output from the assembler. @samp{-a} by
+itself requests all listing output; @samp{-al} requests only the
+output-program listing, and @samp{-as} requests only a symbol table
+listing.
+
+Once you have specified one of these options, you can further control
+listing output and its appearance using the directives @code{.list},
+@code{.nolist}, @code{.psize}, @code{.eject}, @code{.title}, and
+@code{.sbttl}.
+
+If you do not request listing output with one of the @samp{-a} options, the
+listing-control directives have no effect.
+
+@section @code{-D}
+
+@kindex -D
+This option has no effect whatsoever, but it is accepted to make it more
+likely that scripts written for other assemblers will also work with
+@code{_AS__}.
+
+@section Work Faster: @code{-f}
+
+@kindex -f
+@cindex trusted compiler
+@cindex faster processing (@code{-f})
+@samp{-f} should only be used when assembling programs written by a
+(trusted) compiler. @samp{-f} stops the assembler from pre-processing
+the input file(s) before assembling them. @xref{Pre-processing,
+,Pre-processing}.
+
+@quotation
+@emph{Warning:} if the files actually need to be pre-processed (if they
+contain comments, for example), @code{_AS__} will not work correctly if
+@samp{-f} is used.
+@end quotation
+
+@section @code{.include} search path: @code{-I} @var{path}
+
+@kindex -I @var{path}
+@cindex paths for @code{.include}
+@cindex search path for @code{.include}
+@cindex @code{include} directive search path
+Use this option to add a @var{path} to the list of directories
+@code{_AS__} will search for files specified in @code{.include}
+directives (@pxref{Include,,@code{.include}}). You may use @code{-I} as
+many times as necessary to include a variety of paths. The current
+working directory is always searched first; after that, @code{_AS__}
+searches any @samp{-I} directories in the same order as they were
+specified (left to right) on the command line.
+
+@section Difference Tables: @code{-k}
+
+@kindex -k
+_if__((!_GENERIC__) && (!_DIFFTABKLUG__))
+On the _HOST__ family, this option is allowed, but has no effect. It is
+permitted for compatibility with the GNU assembler on other platforms,
+where it can be used to warn when the assembler alters the machine code
+generated for @samp{.word} directives in difference tables. The _HOST__
+family does not have the addressing limitations that sometimes lead to this
+alteration on other platforms.
+_fi__((!_GENERIC__) && (!_DIFFTABKLUG__))
+
+_if__(_GENERIC__ || _DIFFTABKLUG__ )
+@cindex difference tables, warning
+@cindex warning for altered difference tables
+@code{_AS__} sometimes alters the code emitted for directives of the form
+@samp{.word @var{sym1}-@var{sym2}}; @pxref{Word,,@code{.word}}.
+You can use the @samp{-k} option if you want a warning issued when this
+is done.
+_fi__(_GENERIC__ || _DIFFTABKLUG__ )
+
+@section Include Local Labels: @code{-L}
+
+@kindex -L
+@cindex local labels, retaining in output
+Labels beginning with @samp{L} (upper case only) are called @dfn{local
+labels}. @xref{Symbol Names}. Normally you don't see such labels when
+debugging, because they are intended for the use of programs (like
+compilers) that compose assembler programs, not for your notice.
+Normally both @code{_AS__} and @code{_LD__} discard such labels, so you don't
+normally debug with them.
+
+This option tells @code{_AS__} to retain those @samp{L@dots{}} symbols
+in the object file. Usually if you do this you also tell the linker
+@code{_LD__} to preserve symbols whose names begin with @samp{L}.
+
+@section Name the Object File: @code{-o}
+
+@kindex -o
+@cindex naming object file
+@cindex object file name
+There is always one object file output when you run @code{_AS__}. By
+default it has the name @file{a.out}. You use this option (which
+takes exactly one filename) to give the object file a different name.
+
+Whatever the object file is called, @code{_AS__} will overwrite any
+existing file of the same name.
+
+@section Join Data and Text Sections: @code{-R}
+
+@kindex -R
+@cindex data and text sections, joining
+@cindex text and data sections, joining
+@cindex joining text and data sections
+@cindex merging text and data sections
+@code{-R} tells @code{_AS__} to write the object file as if all
+data-section data lives in the text section. This is only done at
+the very last moment: your binary data are the same, but data
+section parts are relocated differently. The data section part of
+your object file is zero bytes long because all it bytes are
+appended to the text section. (@xref{Sections,,Sections and Relocation}.)
+
+When you specify @code{-R} it would be possible to generate shorter
+address displacements (because we don't have to cross between text and
+data section). We refrain from doing this simply for compatibility with
+older versions of @code{_AS__}. In future, @code{-R} may work this way.
+
+_if__(_COFF__)
+When @code{_AS__} is configured for COFF output,
+this option is only useful if you use sections named @samp{.text} and
+@samp{.data}.
+_fi__(_COFF__)
+
+@section Announce Version: @code{-v}
+
+@kindex -v
+@kindex -version
+@cindex @code{_AS__} version
+@cindex version of @code{_AS__}
+You can find out what version of as is running by including the
+option @samp{-v} (which you can also spell as @samp{-version}) on the
+command line.
+
+@section Suppress Warnings: @code{-W}
+
+@kindex -W
+@cindex suppressing warnings
+@cindex warnings, suppressing
+@code{_AS__} should never give a warning or error message when
+assembling compiler output. But programs written by people often
+cause @code{_AS__} to give a warning that a particular assumption was
+made. All such warnings are directed to the standard error file.
+If you use this option, no warnings are issued. This option only
+affects the warning messages: it does not change any particular of how
+@code{_AS__} assembles your file. Errors, which stop the assembly, are
+still reported.
+
+@node Syntax, Sections, Invoking, Top
+@chapter Syntax
+
+@cindex machine-independent syntax
+@cindex syntax, machine-independent
+This chapter describes the machine-independent syntax allowed in a
+source file. @code{_AS__} syntax is similar to what many other assemblers
+use; it is inspired in BSD 4.2
+_if__(!_VAX__)
+assembler. @refill
+_fi__(!_VAX__)
+_if__(_VAX__)
+assembler, except that @code{_AS__} does not assemble Vax bit-fields.
+_fi__(_VAX__)
+
+@menu
+* Pre-processing:: Pre-processing
+* Whitespace:: Whitespace
+* Comments:: Comments
+* Symbol Intro:: Symbols
+* Statements:: Statements
+* Constants:: Constants
+@end menu
+
+@node Pre-processing, Whitespace, Syntax, Syntax
+@section Pre-Processing
+
+@cindex preprocessing
+The pre-processor:
+@itemize @bullet
+@cindex whitespace, removed by preprocessor
+@item
+adjusts and removes extra whitespace. It leaves one space or tab before
+the keywords on a line, and turns any other whitespace on the line into
+a single space.
+
+@cindex comments, removed by preprocessor
+@item
+removes all comments, replacing them with a single space, or an
+appropriate number of newlines.
+
+@cindex constants, converted by preprocessor
+@item
+converts character constants into the appropriate numeric values.
+@end itemize
+
+Excess whitespace, comments, and character constants
+cannot be used in the portions of the input text that are not
+pre-processed.
+
+@cindex turning preprocessing on and off
+@cindex preprocessing, turning on and off
+@kindex #NO_APP
+@kindex #APP
+If the first line of an input file is @code{#NO_APP} or the @samp{-f}
+option is given, the input file will not be pre-processed. Within such
+an input file, parts of the file can be pre-processed by putting a line
+that says @code{#APP} before the text that should be pre-processed, and
+putting a line that says @code{#NO_APP} after them. This feature is
+mainly intend to support @code{asm} statements in compilers whose output
+normally does not need to be pre-processed.
+
+@node Whitespace, Comments, Pre-processing, Syntax
+@section Whitespace
+
+@cindex whitespace
+@dfn{Whitespace} is one or more blanks or tabs, in any order.
+Whitespace is used to separate symbols, and to make programs neater for
+people to read. Unless within character constants
+(@pxref{Characters,,Character Constants}), any whitespace means the same
+as exactly one space.
+
+@node Comments, Symbol Intro, Whitespace, Syntax
+@section Comments
+
+@cindex comments
+There are two ways of rendering comments to @code{_AS__}. In both
+cases the comment is equivalent to one space.
+
+Anything from @samp{/*} through the next @samp{*/} is a comment.
+This means you may not nest these comments.
+
+@smallexample
+/*
+ The only way to include a newline ('\n') in a comment
+ is to use this sort of comment.
+*/
+
+/* This sort of comment does not nest. */
+@end smallexample
+
+@cindex line comment character
+Anything from the @dfn{line comment} character to the next newline
+is considered a comment and is ignored. The line comment character is
+_if__(_VAX__)
+@samp{#} on the Vax;
+_fi__(_VAX__)
+_if__(_I960__)
+@samp{#} on the i960;
+_fi__(_I960__)
+_if__(_M680X0__)
+@samp{|} on the 680x0;
+_fi__(_M680X0__)
+_if__(_A29K__)
+@samp{;} for the AMD 29K family;
+_fi__(_A29K__)
+_if__(_H8__)
+@samp{;} for the _HOST__ family;
+_fi__(_H8__)
+@pxref{_MACH_DEP__}. @refill
+@c FIXME: fill in SPARC line comment char
+
+_if__(_GENERIC__)
+On some machines there are two different line comment characters. One
+will only begin a comment if it is the first non-whitespace character on
+a line, while the other will always begin a comment.
+_fi__(_GENERIC__)
+
+@kindex #
+@cindex lines starting with @code{#}
+@cindex logical line numbers
+To be compatible with past assemblers, a special interpretation is
+given to lines that begin with @samp{#}. Following the @samp{#} an
+absolute expression (@pxref{Expressions}) is expected: this will be
+the logical line number of the @b{next} line. Then a string
+(@xref{Strings}.) is allowed: if present it is a new logical file
+name. The rest of the line, if any, should be whitespace.
+
+If the first non-whitespace characters on the line are not numeric,
+the line is ignored. (Just like a comment.)
+@smallexample
+ # This is an ordinary comment.
+# 42-6 "new_file_name" # New logical file name
+ # This is logical line # 36.
+@end smallexample
+This feature is deprecated, and may disappear from future versions
+of @code{_AS__}.
+
+@node Symbol Intro, Statements, Comments, Syntax
+@section Symbols
+
+@cindex symbols
+@cindex characters used in symbols
+A @dfn{symbol} is one or more characters chosen from the set of all
+letters (both upper and lower case), digits and
+_if__(!_H8__)
+the three characters @samp{_.$}
+_fi__(!_H8__)
+_if__(_H8__)
+the two characters @samp{_.}
+_if__(_GENERIC__)
+On most machines, you can also use @code{$} in symbol names; exceptions
+are noted in @ref{_MACH_DEP__}.
+_fi__(_GENERIC__)
+_fi__(_H8__)
+No symbol may begin with a digit. Case is significant.
+There is no length limit: all characters are significant. Symbols are
+delimited by characters not in that set, or by the beginning of a file
+(since the source program must end with a newline, the end of a file is
+not a possible symbol delimiter). @xref{Symbols}.
+@cindex length of symbols
+
+@node Statements, Constants, Symbol Intro, Syntax
+@section Statements
+
+@cindex statements, structure of
+@cindex line separator character
+@cindex statement separator character
+_if__(!_GENERIC__)
+_if__(!(_A29K__||_H8__))
+A @dfn{statement} ends at a newline character (@samp{\n}) or at a
+semicolon (@samp{;}). The newline or semicolon is considered part of
+the preceding statement. Newlines and semicolons within character
+constants are an exception: they don't end statements.
+_fi__(!(_A29K__||_H8__))
+_if__(_A29K__)
+A @dfn{statement} ends at a newline character (@samp{\n}) or an ``at''
+sign (@samp{@@}). The newline or at sign is considered part of the
+preceding statement. Newlines and at signs within character constants
+are an exception: they don't end statements.
+_fi__(_A29K__)
+_if__(_H8__)
+A @dfn{statement} ends at a newline character (@samp{\n}) or a dollar
+sign (@samp{$}). The newline or dollar sign is considered part of the
+preceding statement. Newlines and dollar signs within character constants
+are an exception: they don't end statements.
+_fi__(_H8__)
+_fi__(!_GENERIC__)
+_if__(_GENERIC__)
+A @dfn{statement} ends at a newline character (@samp{\n}) or line
+separator character. (The line separator is usually @samp{;}, unless
+this conflicts with the comment character; @pxref{_MACH_DEP__}.) The
+newline or separator character is considered part of the preceding
+statement. Newlines and separators within character constants are an
+exception: they don't end statements.
+_fi__(_GENERIC__)
+
+@cindex newline, required at file end
+@cindex EOF, newline must precede
+It is an error to end any statement with end-of-file: the last
+character of any input file should be a newline.@refill
+
+@cindex continuing statements
+@cindex multi-line statements
+@cindex statement on multiple lines
+You may write a statement on more than one line if you put a
+backslash (@kbd{\}) immediately in front of any newlines within the
+statement. When @code{_AS__} reads a backslashed newline both
+characters are ignored. You can even put backslashed newlines in
+the middle of symbol names without changing the meaning of your
+source program.
+
+An empty statement is allowed, and may include whitespace. It is ignored.
+
+@cindex instructions and directives
+@cindex directives and instructions
+@c "key symbol" is not used elsewhere in the document; seems pedantic to
+@c @defn{} it in that case, as was done previously... pesch@cygnus.com,
+@c 13feb91.
+A statement begins with zero or more labels, optionally followed by a
+key symbol which determines what kind of statement it is. The key
+symbol determines the syntax of the rest of the statement. If the
+symbol begins with a dot @samp{.} then the statement is an assembler
+directive: typically valid for any computer. If the symbol begins with
+a letter the statement is an assembly language @dfn{instruction}: it
+will assemble into a machine language instruction.
+_if__(_GENERIC__)
+Different versions of @code{_AS__} for different computers will
+recognize different instructions. In fact, the same symbol may
+represent a different instruction in a different computer's assembly
+language.@refill
+_fi__(_GENERIC__)
+
+@cindex @code{:} (label)
+@cindex label (@code{:})
+A label is a symbol immediately followed by a colon (@code{:}).
+Whitespace before a label or after a colon is permitted, but you may not
+have whitespace between a label's symbol and its colon. @xref{Labels}.
+
+@smallexample
+label: .directive followed by something
+another_label: # This is an empty statement.
+ instruction operand_1, operand_2, @dots{}
+@end smallexample
+
+@node Constants, , Statements, Syntax
+@section Constants
+
+@cindex constants
+A constant is a number, written so that its value is known by
+inspection, without knowing any context. Like this:
+@smallexample
+.byte 74, 0112, 092, 0x4A, 0X4a, 'J, '\J # All the same value.
+.ascii "Ring the bell\7" # A string constant.
+.octa 0x123456789abcdef0123456789ABCDEF0 # A bignum.
+.float 0f-314159265358979323846264338327\
+95028841971.693993751E-40 # - pi, a flonum.
+@end smallexample
+
+@menu
+* Characters:: Character Constants
+* Numbers:: Number Constants
+@end menu
+
+@node Characters, Numbers, Constants, Constants
+@subsection Character Constants
+
+@cindex character constants
+@cindex constants, character
+There are two kinds of character constants. A @dfn{character} stands
+for one character in one byte and its value may be used in
+numeric expressions. String constants (properly called string
+@emph{literals}) are potentially many bytes and their values may not be
+used in arithmetic expressions.
+
+@menu
+* Strings:: Strings
+* Chars:: Characters
+@end menu
+
+@node Strings, Chars, Characters, Characters
+@subsubsection Strings
+
+@cindex string constants
+@cindex constants, string
+A @dfn{string} is written between double-quotes. It may contain
+double-quotes or null characters. The way to get special characters
+into a string is to @dfn{escape} these characters: precede them with
+a backslash @samp{\} character. For example @samp{\\} represents
+one backslash: the first @code{\} is an escape which tells
+@code{_AS__} to interpret the second character literally as a backslash
+(which prevents @code{_AS__} from recognizing the second @code{\} as an
+escape character). The complete list of escapes follows.
+
+@cindex escape codes, character
+@cindex character escape codes
+@table @kbd
+@c @item \a
+@c Mnemonic for ACKnowledge; for ASCII this is octal code 007.
+@c
+@item \b
+@cindex @code{\b} (backspace character)
+@cindex backspace (@code{\b})
+Mnemonic for backspace; for ASCII this is octal code 010.
+
+@c @item \e
+@c Mnemonic for EOText; for ASCII this is octal code 004.
+@c
+@item \f
+@cindex @code{\f} (formfeed character)
+@cindex formfeed (@code{\f})
+Mnemonic for FormFeed; for ASCII this is octal code 014.
+
+@item \n
+@cindex @code{\n} (newline character)
+@cindex newline (@code{\n})
+Mnemonic for newline; for ASCII this is octal code 012.
+
+@c @item \p
+@c Mnemonic for prefix; for ASCII this is octal code 033, usually known as @code{escape}.
+@c
+@item \r
+@cindex @code{\r} (carriage return character)
+@cindex carriage return (@code{\r})
+Mnemonic for carriage-Return; for ASCII this is octal code 015.
+
+@c @item \s
+@c Mnemonic for space; for ASCII this is octal code 040. Included for compliance with
+@c other assemblers.
+@c
+@item \t
+@cindex @code{\t} (tab)
+@cindex tab (@code{\t})
+Mnemonic for horizontal Tab; for ASCII this is octal code 011.
+
+@c @item \v
+@c Mnemonic for Vertical tab; for ASCII this is octal code 013.
+@c @item \x @var{digit} @var{digit} @var{digit}
+@c A hexadecimal character code. The numeric code is 3 hexadecimal digits.
+@c
+@item \ @var{digit} @var{digit} @var{digit}
+@cindex @code{\@var{ddd}} (octal character code)
+@cindex octal character code (@code{\@var{ddd}})
+An octal character code. The numeric code is 3 octal digits.
+For compatibility with other Unix systems, 8 and 9 are accepted as digits:
+for example, @code{\008} has the value 010, and @code{\009} the value 011.
+
+@item \\
+@cindex @code{\\} (@samp{\} character)
+@cindex backslash (@code{\\})
+Represents one @samp{\} character.
+
+@c @item \'
+@c Represents one @samp{'} (accent acute) character.
+@c This is needed in single character literals
+@c (@xref{Characters,,Character Constants}.) to represent
+@c a @samp{'}.
+@c
+@item \"
+@cindex @code{\"} (doublequote character)
+@cindex doublequote (@code{\"})
+Represents one @samp{"} character. Needed in strings to represent
+this character, because an unescaped @samp{"} would end the string.
+
+@item \ @var{anything-else}
+Any other character when escaped by @kbd{\} will give a warning, but
+assemble as if the @samp{\} was not present. The idea is that if
+you used an escape sequence you clearly didn't want the literal
+interpretation of the following character. However @code{_AS__} has no
+other interpretation, so @code{_AS__} knows it is giving you the wrong
+code and warns you of the fact.
+@end table
+
+Which characters are escapable, and what those escapes represent,
+varies widely among assemblers. The current set is what we think
+the BSD 4.2 assembler recognizes, and is a subset of what most C
+compilers recognize. If you are in doubt, don't use an escape
+sequence.
+
+@node Chars, , Strings, Characters
+@subsubsection Characters
+
+@cindex single character constant
+@cindex character, single
+@cindex constant, single character
+A single character may be written as a single quote immediately
+followed by that character. The same escapes apply to characters as
+to strings. So if you want to write the character backslash, you
+must write @kbd{'\\} where the first @code{\} escapes the second
+@code{\}. As you can see, the quote is an acute accent, not a
+grave accent. A newline
+_if__(!_GENERIC__)
+_if__(!(_A29K__||_H8__))
+(or semicolon @samp{;})
+_fi__(!(_A29K__||_H8__))
+_if__(_A29K__)
+(or at sign @samp{@@})
+_fi__(_A29K__)
+_if__(_H8__)
+(or dollar sign @samp{$})
+_fi__(_H8__)
+_fi__(!_GENERIC__)
+immediately following an acute accent is taken as a literal character
+and does not count as the end of a statement. The value of a character
+constant in a numeric expression is the machine's byte-wide code for
+that character. @code{_AS__} assumes your character code is ASCII:
+@kbd{'A} means 65, @kbd{'B} means 66, and so on. @refill
+
+@node Numbers, , Characters, Constants
+@subsection Number Constants
+
+@cindex constants, number
+@cindex number constants
+@code{_AS__} distinguishes three kinds of numbers according to how they
+are stored in the target machine. @emph{Integers} are numbers that
+would fit into an @code{int} in the C language. @emph{Bignums} are
+integers, but they are stored in more than 32 bits. @emph{Flonums}
+are floating point numbers, described below.
+
+@menu
+* Integers:: Integers
+* Bignums:: Bignums
+* Flonums:: Flonums
+_if__(_I960__&&!_GENERIC__)
+* Bit Fields:: Bit Fields
+_fi__(_I960__&&!_GENERIC__)
+@end menu
+
+@node Integers, Bignums, Numbers, Numbers
+@subsubsection Integers
+@cindex integers
+@cindex constants, integer
+
+@cindex binary integers
+@cindex integers, binary
+A binary integer is @samp{0b} or @samp{0B} followed by zero or more of
+the binary digits @samp{01}.
+
+@cindex octal integers
+@cindex integers, octal
+An octal integer is @samp{0} followed by zero or more of the octal
+digits (@samp{01234567}).
+
+@cindex decimal integers
+@cindex integers, decimal
+A decimal integer starts with a non-zero digit followed by zero or
+more digits (@samp{0123456789}).
+
+@cindex hexadecimal integers
+@cindex integers, hexadecimal
+A hexadecimal integer is @samp{0x} or @samp{0X} followed by one or
+more hexadecimal digits chosen from @samp{0123456789abcdefABCDEF}.
+
+Integers have the usual values. To denote a negative integer, use
+the prefix operator @samp{-} discussed under expressions
+(@pxref{Prefix Ops,,Prefix Operators}).
+
+@node Bignums, Flonums, Integers, Numbers
+@subsubsection Bignums
+
+@cindex bignums
+@cindex constants, bignum
+A @dfn{bignum} has the same syntax and semantics as an integer
+except that the number (or its negative) takes more than 32 bits to
+represent in binary. The distinction is made because in some places
+integers are permitted while bignums are not.
+
+_if__(_I960__&&!_GENERIC__)
+@node Flonums, Bit Fields, Bignums, Numbers
+_fi__(_I960__&&!_GENERIC__)
+_if__(_GENERIC__||!_I960__)
+@node Flonums, , Bignums, Numbers
+_fi__(_GENERIC__||!_I960__)
+@subsubsection Flonums
+@cindex flonums
+@cindex floating point numbers
+@cindex constants, floating point
+
+@cindex precision, floating point
+A @dfn{flonum} represents a floating point number. The translation is
+indirect: a decimal floating point number from the text is converted by
+@code{_AS__} to a generic binary floating point number of more than
+sufficient precision. This generic floating point number is converted
+to a particular computer's floating point format (or formats) by a
+portion of @code{_AS__} specialized to that computer.
+
+A flonum is written by writing (in order)
+@itemize @bullet
+@item
+The digit @samp{0}.
+@item
+A letter, to tell @code{_AS__} the rest of the number is a flonum.
+_if__(_GENERIC__)
+@kbd{e} is recommended. Case is not important.
+@ignore
+@c FIXME: verify if flonum syntax really this vague for most cases
+ (Any otherwise illegal letter
+will work here, but that might be changed. Vax BSD 4.2 assembler seems
+to allow any of @samp{defghDEFGH}.)
+@end ignore
+_fi__(_GENERIC__)
+_if__(_A29K__||_H8__)
+_if__(_GENERIC__)
+On the AMD 29K and H8/300 architectures, the letter must be:
+_fi__(_GENERIC__)
+One of the letters @samp{DFPRSX} (in upper or lower case).
+_fi__(_A29K__||_H8__)
+_if__(_I960__)
+_if__(_GENERIC__)
+On the Intel 960 architecture, the letter must be:
+_fi__(_GENERIC__)
+One of the letters @samp{DFT} (in upper or lower case).
+_fi__(_I960__)
+@item
+An optional sign: either @samp{+} or @samp{-}.
+@item
+An optional @dfn{integer part}: zero or more decimal digits.
+@item
+An optional @dfn{fractional part}: @samp{.} followed by zero
+or more decimal digits.
+@item
+An optional exponent, consisting of:
+@itemize @bullet
+@item
+An @samp{E} or @samp{e}.
+@c I can't find a config where "EXP_CHARS" is other than 'eE', but in
+@c principle this can perfectly well be different on different targets.
+@item
+Optional sign: either @samp{+} or @samp{-}.
+@item
+One or more decimal digits.
+@end itemize
+@end itemize
+
+At least one of the integer part or the fractional part must be
+present. The floating point number has the usual base-10 value.
+
+@code{_AS__} does all processing using integers. Flonums are computed
+independently of any floating point hardware in the computer running
+@code{_AS__}.
+
+_if__(_I960__&&!_GENERIC__)
+@c Bit fields are written as a general facility but are also controlled
+@c by a conditional-compilation flag---which is as of now (21mar91)
+@c turned on only by the i960 config of GAS.
+@node Bit Fields, , Flonums, Numbers
+@subsubsection Bit Fields
+
+@cindex bit fields
+@cindex constants, bit field
+You can also define numeric constants as @dfn{bit fields}.
+specify two numbers separated by a colon---
+@example
+@var{mask}:@var{value}
+@end example
+@noindent
+the first will act as a mask; @code{_AS__} will bitwise-and it with the
+second value.
+
+The resulting number is then packed
+_if__(_GENERIC__)
+@c this conditional paren in case bit fields turned on elsewhere than 960
+(in host-dependent byte order)
+_fi__(_GENERIC__)
+into a field whose width depends on which assembler directive has the
+bit-field as its argument. Overflow (a result from the bitwise and
+requiring more binary digits to represent) is not an error; instead,
+more constants are generated, of the specified width, beginning with the
+least significant digits.@refill
+
+The directives @code{.byte}, @code{.hword}, @code{.int}, @code{.long},
+@code{.short}, and @code{.word} accept bit-field arguments.
+_fi__(_I960__&&!_GENERIC__)
+
+@node Sections, Symbols, Syntax, Top
+@chapter Sections and Relocation
+@cindex sections
+@cindex relocation
+
+@menu
+* Secs Background:: Background
+* _LD__ Sections:: _LD__ Sections
+* _AS__ Sections:: _AS__ Internal Sections
+* Sub-Sections:: Sub-Sections
+* bss:: bss Section
+@end menu
+
+@node Secs Background, _LD__ Sections, Sections, Sections
+@section Background
+
+Roughly, a section is a range of addresses, with no gaps; all data
+``in'' those addresses is treated the same for some particular purpose.
+For example there may be a ``read only'' section.
+
+@cindex linker, and assembler
+@cindex assembler, and linker
+The linker @code{_LD__} reads many object files (partial programs) and
+combines their contents to form a runnable program. When @code{_AS__}
+emits an object file, the partial program is assumed to start at address
+0. @code{_LD__} will assign the final addresses the partial program
+occupies, so that different partial programs don't overlap. This is
+actually an over-simplification, but it will suffice to explain how
+@code{_AS__} uses sections.
+
+@code{_LD__} moves blocks of bytes of your program to their run-time
+addresses. These blocks slide to their run-time addresses as rigid
+units; their length does not change and neither does the order of bytes
+within them. Such a rigid unit is called a @emph{section}. Assigning
+run-time addresses to sections is called @dfn{relocation}. It includes
+the task of adjusting mentions of object-file addresses so they refer to
+the proper run-time addresses.
+_if__(_H8__)
+For the H8/300, @code{_AS__} pads sections if needed to ensure they end
+on a word (sixteen bit) boundary.
+_fi__(_H8__)
+
+@cindex standard @code{_AS__} sections
+An object file written by @code{_AS__} has at least three sections, any
+of which may be empty. These are named @dfn{text}, @dfn{data} and
+@dfn{bss} sections.
+
+_if__(_COFF__)
+_if__(_GENERIC__)
+When it generates COFF output,
+_fi__(_GENERIC__)
+@code{_AS__} can also generate whatever other named sections you specify
+using the @samp{.section} directive (@pxref{Section,,@code{.section}}).
+If you don't use any directives that place output in the @samp{.text}
+or @samp{.data} sections, these sections will still exist, but will be empty.
+_fi__(_COFF__)
+
+Within the object file, the text section starts at address @code{0}, the
+data section follows, and the bss section follows the data section.
+
+To let @code{_LD__} know which data will change when the sections are
+relocated, and how to change that data, @code{_AS__} also writes to the
+object file details of the relocation needed. To perform relocation
+@code{_LD__} must know, each time an address in the object
+file is mentioned:
+@itemize @bullet
+@item
+Where in the object file is the beginning of this reference to
+an address?
+@item
+How long (in bytes) is this reference?
+@item
+Which section does the address refer to? What is the numeric value of
+@display
+(@var{address}) @minus{} (@var{start-address of section})?
+@end display
+@item
+Is the reference to an address ``Program-Counter relative''?
+@end itemize
+
+@cindex addresses, format of
+@cindex section-relative addressing
+In fact, every address @code{_AS__} ever uses is expressed as
+@display
+(@var{section}) + (@var{offset into section})
+@end display
+@noindent
+Further, every expression @code{_AS__} computes is of this section-relative
+nature. @dfn{Absolute expression} means an expression with section
+``absolute'' (@pxref{_LD__ Sections}). A @dfn{pass1 expression} means
+an expression with section ``pass1'' (@pxref{_AS__ Sections,,_AS__
+Internal Sections}). In this manual we use the notation @{@var{secname}
+@var{N}@} to mean ``offset @var{N} into section @var{secname}''.
+
+Apart from text, data and bss sections you need to know about the
+@dfn{absolute} section. When @code{_LD__} mixes partial programs,
+addresses in the absolute section remain unchanged. For example, address
+@code{@{absolute 0@}} is ``relocated'' to run-time address 0 by @code{_LD__}.
+Although two partial programs' data sections will not overlap addresses
+after linking, @emph{by definition} their absolute sections will overlap.
+Address @code{@{absolute@ 239@}} in one partial program will always be the same
+address when the program is running as address @code{@{absolute@ 239@}} in any
+other partial program.
+
+The idea of sections is extended to the @dfn{undefined} section. Any
+address whose section is unknown at assembly time is by definition
+rendered @{undefined @var{U}@}---where @var{U} will be filled in later.
+Since numbers are always defined, the only way to generate an undefined
+address is to mention an undefined symbol. A reference to a named
+common block would be such a symbol: its value is unknown at assembly
+time so it has section @emph{undefined}.
+
+By analogy the word @emph{section} is used to describe groups of sections in
+the linked program. @code{_LD__} puts all partial programs' text
+sections in contiguous addresses in the linked program. It is
+customary to refer to the @emph{text section} of a program, meaning all
+the addresses of all partial program's text sections. Likewise for
+data and bss sections.
+
+Some sections are manipulated by @code{_LD__}; others are invented for
+use of @code{_AS__} and have no meaning except during assembly.
+
+@node _LD__ Sections, _AS__ Sections, Secs Background, Sections
+@section _LD__ Sections
+@code{_LD__} deals with just four kinds of sections, summarized below.
+
+@table @strong
+
+_if__(_GENERIC__||_COFF__)
+@cindex named sections
+@cindex sections, named
+@item named sections
+_fi__(_GENERIC__||_COFF__)
+_if__(_AOUT__||_BOUT__)
+@cindex text section
+@cindex data section
+@item text section
+@itemx data section
+_fi__(_AOUT__||_BOUT__)
+These sections hold your program. @code{_AS__} and @code{_LD__} treat them as
+separate but equal sections. Anything you can say of one section is
+true another.
+_if__(_AOUT__||_BOUT__)
+When the program is running, however, it is
+customary for the text section to be unalterable. The
+text section is often shared among processes: it will contain
+instructions, constants and the like. The data section of a running
+program is usually alterable: for example, C variables would be stored
+in the data section.
+_fi__(_AOUT__||_BOUT__)
+
+@cindex bss section
+@item bss section
+This section contains zeroed bytes when your program begins running. It
+is used to hold unitialized variables or common storage. The length of
+each partial program's bss section is important, but because it starts
+out containing zeroed bytes there is no need to store explicit zero
+bytes in the object file. The bss section was invented to eliminate
+those explicit zeros from object files.
+
+@cindex absolute section
+@item absolute section
+Address 0 of this section is always ``relocated'' to runtime address 0.
+This is useful if you want to refer to an address that @code{_LD__} must
+not change when relocating. In this sense we speak of absolute
+addresses being ``unrelocatable'': they don't change during relocation.
+
+@cindex undefined section
+@item undefined section
+This ``section'' is a catch-all for address references to objects not in
+the preceding sections.
+@c FIXME: ref to some other doc on obj-file formats could go here.
+@end table
+
+@cindex relocation example
+An idealized example of three relocatable sections follows.
+_if__(_COFF__)
+The example uses the traditional section names @samp{.text} and @samp{.data}.
+_fi__(_COFF__)
+Memory addresses are on the horizontal axis.
+
+@c TEXI2ROFF-KILL
+@ifinfo
+@c END TEXI2ROFF-KILL
+@smallexample
+ +-----+----+--+
+partial program # 1: |ttttt|dddd|00|
+ +-----+----+--+
+
+ text data bss
+ seg. seg. seg.
+
+ +---+---+---+
+partial program # 2: |TTT|DDD|000|
+ +---+---+---+
+
+ +--+---+-----+--+----+---+-----+~~
+linked program: | |TTT|ttttt| |dddd|DDD|00000|
+ +--+---+-----+--+----+---+-----+~~
+
+ addresses: 0 @dots{}
+@end smallexample
+@c TEXI2ROFF-KILL
+@end ifinfo
+@c FIXME make sure no page breaks inside figure!!
+@tex
+
+\line{\it Partial program \#1: \hfil}
+\line{\ibox{2.5cm}{\tt text}\ibox{2cm}{\tt data}\ibox{1cm}{\tt bss}\hfil}
+\line{\boxit{2.5cm}{\tt ttttt}\boxit{2cm}{\tt dddd}\boxit{1cm}{\tt 00}\hfil}
+
+\line{\it Partial program \#2: \hfil}
+\line{\ibox{1cm}{\tt text}\ibox{1.5cm}{\tt data}\ibox{1cm}{\tt bss}\hfil}
+\line{\boxit{1cm}{\tt TTT}\boxit{1.5cm}{\tt DDDD}\boxit{1cm}{\tt 000}\hfil}
+
+\line{\it linked program: \hfil}
+\line{\ibox{.5cm}{}\ibox{1cm}{\tt text}\ibox{2.5cm}{}\ibox{.75cm}{}\ibox{2cm}{\tt data}\ibox{1.5cm}{}\ibox{2cm}{\tt bss}\hfil}
+\line{\boxit{.5cm}{}\boxit{1cm}{\tt TTT}\boxit{2.5cm}{\tt
+ttttt}\boxit{.75cm}{}\boxit{2cm}{\tt dddd}\boxit{1.5cm}{\tt
+DDDD}\boxit{2cm}{\tt 00000}\ \dots\hfil}
+
+\line{\it addresses: \hfil}
+\line{0\dots\hfil}
+
+@end tex
+@c END TEXI2ROFF-KILL
+
+@node _AS__ Sections, Sub-Sections, _LD__ Sections, Sections
+@section _AS__ Internal Sections
+
+@cindex internal @code{_AS__} sections
+@cindex sections in messages, internal
+These sections are meant only for the internal use of @code{_AS__}. They
+have no meaning at run-time. You don't really need to know about these
+sections for most purposes; but they can be mentioned in @code{_AS__}
+warning messages, so it might be helpful to have an idea of their
+meanings to @code{_AS__}. These sections are used to permit the
+value of every expression in your assembly language program to be a
+section-relative address.
+
+@table @b
+@item absent
+@cindex absent (internal section)
+An expression was expected and none was found.
+
+@item ASSEMBLER-INTERNAL-LOGIC-ERROR!
+@cindex assembler internal logic error
+An internal assembler logic error has been found. This means there is a
+bug in the assembler.
+
+@item bignum/flonum
+@cindex bignum/flonum (internal section)
+If a number can't be written as a C @code{int} constant (a bignum or a
+flonum, but not an integer), it is recorded as belonging to this
+``section''. @code{_AS__} has to remember that a flonum or a bignum
+does not fit into 32 bits, and cannot be an argument (@pxref{Arguments})
+in an expression: this is done by making a flonum or bignum be in a
+separate internal section. This is purely for internal @code{_AS__}
+convenience; bignum/flonum section behaves similarly to absolute
+section.
+
+@item pass1 section
+@cindex pass1 (internal section)
+The expression was impossible to evaluate in the first pass. The
+assembler will attempt a second pass (second reading of the source) to
+evaluate the expression. Your expression mentioned an undefined symbol
+in a way that defies the one-pass (section + offset in section) assembly
+process. No compiler need emit such an expression.
+
+@quotation
+@emph{Warning:} the second pass is currently not implemented. @code{_AS__}
+will abort with an error message if one is required.
+@end quotation
+
+@item difference section
+@cindex difference (internal section)
+As an assist to the C compiler, expressions of the forms
+@display
+ (@var{undefined symbol}) @minus{} (@var{expression})
+ @var{something} @minus{} (@var{undefined symbol})
+ (@var{undefined symbol}) @minus{} (@var{undefined symbol})
+@end display
+
+are permitted, and belong to the difference section. @code{_AS__}
+re-evaluates such expressions after the source file has been read and
+the symbol table built. If by that time there are no undefined symbols
+in the expression then the expression assumes a new section. The
+intention is to permit statements like
+@samp{.word label - base_of_table}
+to be assembled in one pass where both @code{label} and
+@code{base_of_table} are undefined. This is useful for compiling C and
+Algol switch statements, Pascal case statements, FORTRAN computed goto
+statements and the like.
+@c FIXME item debug
+@c FIXME item transfer[t] vector preload
+@c FIXME item transfer[t] vector postload
+@c FIXME item register
+@end table
+
+@node Sub-Sections, bss, _AS__ Sections, Sections
+@section Sub-Sections
+
+@cindex numbered subsections
+@cindex grouping data
+_if__(_AOUT__||_BOUT__)
+Assembled bytes
+_if__(_COFF__)
+conventionally
+_fi__(_COFF__)
+fall into two sections: text and data.
+_fi__(_AOUT__||_BOUT__)
+You may have separate groups of
+_if__(_COFF__||_GENERIC__)
+data in named sections
+_fi__(_COFF__||_GENERIC__)
+_if__((_AOUT__||_BOUT__)&&!_GENERIC__)
+text or data
+_fi__((_AOUT__||_BOUT__)&&!_GENERIC__)
+that you want to end up near to each other in the object
+file, even though they are not contiguous in the assembler source.
+@code{_AS__} allows you to use @dfn{subsections} for this purpose.
+Within each section, there can be numbered subsections with
+values from 0 to 8192. Objects assembled into the same subsection will
+be grouped with other objects in the same subsection when they are all
+put into the object file. For example, a compiler might want to store
+constants in the text section, but might not want to have them
+interspersed with the program being assembled. In this case, the
+compiler could issue a @samp{.text 0} before each section of code being
+output, and a @samp{.text 1} before each group of constants being output.
+
+Subsections are optional. If you don't use subsections, everything
+will be stored in subsection number zero.
+
+_if__(_GENERIC__)
+Each subsection is zero-padded up to a multiple of four bytes.
+(Subsections may be padded a different amount on different flavors
+of @code{_AS__}.)
+_fi__(_GENERIC__)
+_if__(!_GENERIC__)
+_if__(_H8__)
+On the H8/300 platform, each subsection is zero-padded to a word
+boundary (two bytes).
+_fi__(_H8__)
+_if__(_I960__)
+@c FIXME section padding (alignment)?
+@c Rich Pixley says padding here depends on target obj code format; that
+@c doesn't seem particularly useful to say without further elaboration,
+@c so for now I say nothing about it. If this is a generic BFD issue,
+@c these paragraphs might need to vanish from this manual, and be
+@c discussed in BFD chapter of binutils (or some such).
+_fi__(_I960__)
+_if__(_A29K__)
+On the AMD 29K family, no particular padding is added to section or
+subsection sizes; _AS__ forces no alignment on this platform.
+_fi__(_A29K__)
+_fi__(!_GENERIC__)
+
+Subsections appear in your object file in numeric order, lowest numbered
+to highest. (All this to be compatible with other people's assemblers.)
+The object file contains no representation of subsections; @code{_LD__} and
+other programs that manipulate object files will see no trace of them.
+They just see all your text subsections as a text section, and all your
+data subsections as a data section.
+
+To specify which subsection you want subsequent statements assembled
+into, use a numeric argument to specify it, in a @samp{.text
+@var{expression}} or a @samp{.data @var{expression}} statement.
+_if__(_COFF__)
+_if__(_GENERIC__)
+When generating COFF output, you
+_fi__(_GENERIC__)
+_if__(!_GENERIC__)
+You
+_fi__(!_GENERIC__)
+can also use an extra subsection
+argument with arbitrary named sections: @samp{.section @var{name},
+@var{expression}}.
+_fi__(_COFF__)
+@var{Expression} should be an absolute expression.
+(@xref{Expressions}.) If you just say @samp{.text} then @samp{.text 0}
+is assumed. Likewise @samp{.data} means @samp{.data 0}. Assembly
+begins in @code{text 0}. For instance:
+@smallexample
+.text 0 # The default subsection is text 0 anyway.
+.ascii "This lives in the first text subsection. *"
+.text 1
+.ascii "But this lives in the second text subsection."
+.data 0
+.ascii "This lives in the data section,"
+.ascii "in the first data subsection."
+.text 0
+.ascii "This lives in the first text section,"
+.ascii "immediately following the asterisk (*)."
+@end smallexample
+
+Each section has a @dfn{location counter} incremented by one for every
+byte assembled into that section. Because subsections are merely a
+convenience restricted to @code{_AS__} there is no concept of a subsection
+location counter. There is no way to directly manipulate a location
+counter---but the @code{.align} directive will change it, and any label
+definition will capture its current value. The location counter of the
+section that statements are being assembled into is said to be the
+@dfn{active} location counter.
+
+@node bss, , Sub-Sections, Sections
+@section bss Section
+
+@cindex bss section
+@cindex common variable storage
+The bss section is used for local common variable storage.
+You may allocate address space in the bss section, but you may
+not dictate data to load into it before your program executes. When
+your program starts running, all the contents of the bss
+section are zeroed bytes.
+
+Addresses in the bss section are allocated with special directives; you
+may not assemble anything directly into the bss section. Hence there
+are no bss subsections. @xref{Comm,,@code{.comm}},
+@pxref{Lcomm,,@code{.lcomm}}.
+
+@node Symbols, Expressions, Sections, Top
+@chapter Symbols
+
+@cindex symbols
+Symbols are a central concept: the programmer uses symbols to name
+things, the linker uses symbols to link, and the debugger uses symbols
+to debug.
+
+@quotation
+@cindex debuggers, and symbol order
+@emph{Warning:} @code{_AS__} does not place symbols in the object file in
+the same order they were declared. This may break some debuggers.
+@end quotation
+
+@menu
+* Labels:: Labels
+* Setting Symbols:: Giving Symbols Other Values
+* Symbol Names:: Symbol Names
+* Dot:: The Special Dot Symbol
+* Symbol Attributes:: Symbol Attributes
+@end menu
+
+@node Labels, Setting Symbols, Symbols, Symbols
+@section Labels
+
+@cindex labels
+A @dfn{label} is written as a symbol immediately followed by a colon
+@samp{:}. The symbol then represents the current value of the
+active location counter, and is, for example, a suitable instruction
+operand. You are warned if you use the same symbol to represent two
+different locations: the first definition overrides any other
+definitions.
+
+@node Setting Symbols, Symbol Names, Labels, Symbols
+@section Giving Symbols Other Values
+
+@cindex assigning values to symbols
+@cindex symbol values, assigning
+A symbol can be given an arbitrary value by writing a symbol, followed
+by an equals sign @samp{=}, followed by an expression
+(@pxref{Expressions}). This is equivalent to using the @code{.set}
+directive. @xref{Set,,@code{.set}}.
+
+@node Symbol Names, Dot, Setting Symbols, Symbols
+@section Symbol Names
+
+@cindex symbol names
+@cindex names, symbol
+Symbol names begin with a letter or with one of
+_if__(!_H8__)
+@samp{_.$}
+_fi__(!_H8__)
+_if__(_H8__)
+@samp{_.}
+_if__(_GENERIC__)
+(On most machines, you can also use @code{$} in symbol names; exceptions
+are noted in @ref{_MACH_DEP__}.)
+_fi__(_GENERIC__)
+_fi__(_H8__)
+That character may be followed by any string of digits, letters,
+_if__(!_H8__)
+underscores and dollar signs.
+_fi__(!_H8__)
+_if__(_H8__)
+_if__(_GENERIC__)
+dollar signs (unless otherwise noted in @ref{_MACH_DEP__}),
+_fi__(_GENERIC__)
+and underscores.
+_fi__(_H8__)
+Case of letters is significant:
+@code{foo} is a different symbol name than @code{Foo}.
+
+_if__(_A29K__)
+For the AMD 29K family, @samp{?} is also allowed in the
+body of a symbol name, though not at its beginning.
+_fi__(_A29K__)
+
+Each symbol has exactly one name. Each name in an assembly language
+program refers to exactly one symbol. You may use that symbol name any
+number of times in a program.
+
+@subheading Local Symbol Names
+
+@cindex local symbol names
+@cindex symbol names, local
+@cindex temporary symbol names
+@cindex symbol names, temporary
+Local symbols help compilers and programmers use names temporarily.
+There are ten local symbol names, which are re-used throughout the
+program. You may refer to them using the names @samp{0} @samp{1}
+@dots{} @samp{9}. To define a local symbol, write a label of the form
+@samp{@b{N}:} (where @b{N} represents any digit). To refer to the most
+recent previous definition of that symbol write @samp{@b{N}b}, using the
+same digit as when you defined the label. To refer to the next
+definition of a local label, write @samp{@b{N}f}---where @b{N} gives you
+a choice of 10 forward references. The @samp{b} stands for
+``backwards'' and the @samp{f} stands for ``forwards''.
+
+Local symbols are not emitted by the current GNU C compiler.
+
+There is no restriction on how you can use these labels, but
+remember that at any point in the assembly you can refer to at most
+10 prior local labels and to at most 10 forward local labels.
+
+Local symbol names are only a notation device. They are immediately
+transformed into more conventional symbol names before the assembler
+uses them. The symbol names stored in the symbol table, appearing in
+error messages and optionally emitted to the object file have these
+parts:
+
+@table @code
+@item L
+All local labels begin with @samp{L}. Normally both @code{_AS__} and
+@code{_LD__} forget symbols that start with @samp{L}. These labels are
+used for symbols you are never intended to see. If you give the
+@samp{-L} option then @code{_AS__} will retain these symbols in the
+object file. If you also instruct @code{_LD__} to retain these symbols,
+you may use them in debugging.
+
+@item @var{digit}
+If the label is written @samp{0:} then the digit is @samp{0}.
+If the label is written @samp{1:} then the digit is @samp{1}.
+And so on up through @samp{9:}.
+
+@item @ctrl{A}
+This unusual character is included so you don't accidentally invent
+a symbol of the same name. The character has ASCII value
+@samp{\001}.
+
+@item @emph{ordinal number}
+This is a serial number to keep the labels distinct. The first
+@samp{0:} gets the number @samp{1}; The 15th @samp{0:} gets the
+number @samp{15}; @emph{etc.}. Likewise for the other labels @samp{1:}
+through @samp{9:}.
+@end table
+
+For instance, the first @code{1:} is named @code{L1@ctrl{A}1}, the 44th
+@code{3:} is named @code{L3@ctrl{A}44}.
+
+@node Dot, Symbol Attributes, Symbol Names, Symbols
+@section The Special Dot Symbol
+
+@cindex dot (symbol)
+@cindex @code{.} (symbol)
+@cindex current address
+@cindex location counter
+The special symbol @samp{.} refers to the current address that
+@code{_AS__} is assembling into. Thus, the expression @samp{melvin:
+.long .} will cause @code{melvin} to contain its own address.
+Assigning a value to @code{.} is treated the same as a @code{.org}
+directive. Thus, the expression @samp{.=.+4} is the same as saying
+_if__(!_A29K__)
+@samp{.space 4}.
+_fi__(!_A29K__)
+_if__(_A29K__)
+@samp{.block 4}.
+_fi__(_A29K__)
+
+@node Symbol Attributes, , Dot, Symbols
+@section Symbol Attributes
+
+@cindex symbol attributes
+@cindex attributes, symbol
+Every symbol has, as well as its name, the attributes ``Value'' and
+``Type''. Depending on output format, symbols can also have auxiliary
+attributes.
+_if__(_INTERNALS__)
+The detailed definitions are in _0__<a.out.h>_1__.
+_fi__(_INTERNALS__)
+
+If you use a symbol without defining it, @code{_AS__} assumes zero for
+all these attributes, and probably won't warn you. This makes the
+symbol an externally defined symbol, which is generally what you
+would want.
+
+@menu
+* Symbol Value:: Value
+* Symbol Type:: Type
+_if__(_AOUT__||_BOUT__)
+_if__(_GENERIC__||!_BOUT__)
+* a.out Symbols:: Symbol Attributes: @code{a.out}
+_fi__(_GENERIC__||!_BOUT__)
+_if__(_BOUT__&&!_GENERIC__)
+* a.out Symbols:: Symbol Attributes: @code{a.out}, @code{b.out}
+_fi__(_BOUT__&&!_GENERIC__)
+_fi__(_AOUT__||_BOUT__)
+_if__(_COFF__)
+* COFF Symbols:: Symbol Attributes for COFF
+_fi__(_COFF__)
+@end menu
+
+@node Symbol Value, Symbol Type, Symbol Attributes, Symbol Attributes
+@subsection Value
+
+@cindex value of a symbol
+@cindex symbol value
+The value of a symbol is (usually) 32 bits. For a symbol which labels a
+location in the text, data, bss or absolute sections the value is the
+number of addresses from the start of that section to the label.
+Naturally for text, data and bss sections the value of a symbol changes
+as @code{_LD__} changes section base addresses during linking. Absolute
+symbols' values do not change during linking: that is why they are
+called absolute.
+
+The value of an undefined symbol is treated in a special way. If it is
+0 then the symbol is not defined in this assembler source program, and
+@code{_LD__} will try to determine its value from other programs it is
+linked with. You make this kind of symbol simply by mentioning a symbol
+name without defining it. A non-zero value represents a @code{.comm}
+common declaration. The value is how much common storage to reserve, in
+bytes (addresses). The symbol refers to the first address of the
+allocated storage.
+
+_if__(!(_AOUT__||_BOUT__))
+@node Symbol Type, COFF Symbols, Symbol Value, Symbol Attributes
+_fi__(!(_AOUT__||_BOUT__))
+_if__((_AOUT__||_BOUT__))
+@node Symbol Type, a.out Symbols, Symbol Value, Symbol Attributes
+_fi__((_AOUT__||_BOUT__))
+@subsection Type
+
+@cindex type of a symbol
+@cindex symbol type
+The type attribute of a symbol contains relocation (section)
+information, any flag settings indicating that a symbol is external, and
+(optionally), other information for linkers and debuggers. The exact
+format depends on the object-code output format in use.
+
+_if__(_AOUT__||_BOUT__)
+_if__(_COFF__)
+@node a.out Symbols, COFF Symbols, Symbol Type, Symbol Attributes
+_fi__(_COFF__)
+_if__(!_COFF__)
+@node a.out Symbols, , Symbol Type, Symbol Attributes
+_fi__(!_COFF__)
+_if__(_BOUT__&&!_GENERIC__)
+@subsection Symbol Attributes: @code{a.out}, @code{b.out}
+
+@cindex @code{b.out} symbol attributes
+@cindex symbol attributes, @code{b.out}
+These symbol attributes appear only when @code{_AS__} is configured for
+one of the Berkeley-descended object output formats.
+_fi__(_BOUT__&&!_GENERIC__)
+_if__(_GENERIC__||!_BOUT__)
+@subsection Symbol Attributes: @code{a.out}
+_fi__(_GENERIC__||!_BOUT__)
+
+@cindex @code{a.out} symbol attributes
+@cindex symbol attributes, @code{a.out}
+
+@menu
+* Symbol Desc:: Descriptor
+* Symbol Other:: Other
+@end menu
+
+@node Symbol Desc, Symbol Other, a.out Symbols, a.out Symbols
+@subsubsection Descriptor
+
+@cindex descriptor, of @code{a.out} symbol
+This is an arbitrary 16-bit value. You may establish a symbol's
+descriptor value by using a @code{.desc} statement
+(@pxref{Desc,,@code{.desc}}). A descriptor value means nothing to
+@code{_AS__}.
+
+@node Symbol Other, , Symbol Desc, a.out Symbols
+@subsubsection Other
+
+@cindex other attribute, of @code{a.out} symbol
+This is an arbitrary 8-bit value. It means nothing to @code{_AS__}.
+_fi__(_AOUT__||_BOUT__)
+
+_if__(_COFF__)
+_if__(!(_AOUT__||_BOUT__))
+@node COFF Symbols, , Symbol Type, Symbol Attributes
+_fi__(!(_AOUT__||_BOUT__))
+_if__(_AOUT__||_BOUT__)
+@node COFF Symbols, , a.out Symbols, Symbol Attributes
+_fi__(_AOUT__||_BOUT__)
+@subsection Symbol Attributes for COFF
+
+@cindex COFF symbol attributes
+@cindex symbol attributes, COFF
+
+The COFF format supports a multitude of auxiliary symbol attributes;
+like the primary symbol attributes, they are set between @code{.def} and
+@code{.endef} directives.
+
+@subsubsection Primary Attributes
+
+@cindex primary attributes, COFF symbols
+The symbol name is set with @code{.def}; the value and type,
+respectively, with @code{.val} and @code{.type}.
+
+@subsubsection Auxiliary Attributes
+
+@cindex auxiliary attributes, COFF symbols
+The @code{_AS__} directives @code{.dim}, @code{.line}, @code{.scl},
+@code{.size}, and @code{.tag} can generate auxiliary symbol table
+information for COFF.
+_fi__(_COFF__)
+
+@node Expressions, Pseudo Ops, Symbols, Top
+@chapter Expressions
+
+@cindex expressions
+@cindex addresses
+@cindex numeric values
+An @dfn{expression} specifies an address or numeric value.
+Whitespace may precede and/or follow an expression.
+
+@menu
+* Empty Exprs:: Empty Expressions
+* Integer Exprs:: Integer Expressions
+@end menu
+
+@node Empty Exprs, Integer Exprs, Expressions, Expressions
+@section Empty Expressions
+
+@cindex empty expressions
+@cindex expressions, empty
+An empty expression has no value: it is just whitespace or null.
+Wherever an absolute expression is required, you may omit the
+expression and @code{_AS__} will assume a value of (absolute) 0. This
+is compatible with other assemblers.
+
+@node Integer Exprs, , Empty Exprs, Expressions
+@section Integer Expressions
+
+@cindex integer expressions
+@cindex expressions, integer
+An @dfn{integer expression} is one or more @emph{arguments} delimited
+by @emph{operators}.
+
+@menu
+* Arguments:: Arguments
+* Operators:: Operators
+* Prefix Ops:: Prefix Operators
+* Infix Ops:: Infix Operators
+@end menu
+
+@node Arguments, Operators, Integer Exprs, Integer Exprs
+@subsection Arguments
+
+@cindex expression arguments
+@cindex arguments in expressions
+@cindex operands in expressions
+@cindex arithmetic operands
+@dfn{Arguments} are symbols, numbers or subexpressions. In other
+contexts arguments are sometimes called ``arithmetic operands''. In
+this manual, to avoid confusing them with the ``instruction operands'' of
+the machine language, we use the term ``argument'' to refer to parts of
+expressions only, reserving the word ``operand'' to refer only to machine
+instruction operands.
+
+Symbols are evaluated to yield @{@var{section} @var{NNN}@} where
+@var{section} is one of text, data, bss, absolute,
+or undefined. @var{NNN} is a signed, 2's complement 32 bit
+integer.
+
+Numbers are usually integers.
+
+A number can be a flonum or bignum. In this case, you are warned
+that only the low order 32 bits are used, and @code{_AS__} pretends
+these 32 bits are an integer. You may write integer-manipulating
+instructions that act on exotic constants, compatible with other
+assemblers.
+
+@cindex subexpressions
+Subexpressions are a left parenthesis @samp{(} followed by an integer
+expression, followed by a right parenthesis @samp{)}; or a prefix
+operator followed by an argument.
+
+@node Operators, Prefix Ops, Arguments, Integer Exprs
+@subsection Operators
+
+@cindex operators, in expressions
+@cindex arithmetic functions
+@cindex functions, in expressions
+@dfn{Operators} are arithmetic functions, like @code{+} or @code{%}. Prefix
+operators are followed by an argument. Infix operators appear
+between their arguments. Operators may be preceded and/or followed by
+whitespace.
+
+@node Prefix Ops, Infix Ops, Operators, Integer Exprs
+@subsection Prefix Operator
+
+@cindex prefix operators
+@code{_AS__} has the following @dfn{prefix operators}. They each take
+one argument, which must be absolute.
+
+@c the tex/end tex stuff surrounding this small table is meant to make
+@c it align, on the printed page, with the similar table in the next
+@c section (which is inside an enumerate).
+@tex
+\global\advance\leftskip by \itemindent
+@end tex
+
+@table @code
+@item -
+@dfn{Negation}. Two's complement negation.
+@item ~
+@dfn{Complementation}. Bitwise not.
+@end table
+
+@tex
+\global\advance\leftskip by -\itemindent
+@end tex
+
+@node Infix Ops, , Prefix Ops, Integer Exprs
+@subsection Infix Operators
+
+@cindex infix operators
+@cindex operators, permitted arguments
+@dfn{Infix operators} take two arguments, one on either side. Operators
+have precedence, but operations with equal precedence are performed left
+to right. Apart from @code{+} or @code{-}, both arguments must be
+absolute, and the result is absolute.
+
+@enumerate
+@cindex operator precedence
+@cindex precedence of operators
+
+@item
+Highest Precedence
+
+@table @code
+@item *
+@dfn{Multiplication}.
+
+@item /
+@dfn{Division}. Truncation is the same as the C operator @samp{/}
+
+@item %
+@dfn{Remainder}.
+
+@item _0__<_1__
+@itemx _0__<<_1__
+@dfn{Shift Left}. Same as the C operator @samp{_0__<<_1__}
+
+@item _0__>_1__
+@itemx _0__>>_1__
+@dfn{Shift Right}. Same as the C operator @samp{_0__>>_1__}
+@end table
+
+@item
+Intermediate precedence
+
+@table @code
+@item |
+
+@dfn{Bitwise Inclusive Or}.
+
+@item &
+@dfn{Bitwise And}.
+
+@item ^
+@dfn{Bitwise Exclusive Or}.
+
+@item !
+@dfn{Bitwise Or Not}.
+@end table
+
+@item
+Lowest Precedence
+
+@table @code
+@item +
+@cindex addition, permitted arguments
+@cindex plus, permitted arguments
+@cindex arguments for addition
+@dfn{Addition}. If either argument is absolute, the result
+has the section of the other argument.
+If either argument is pass1 or undefined, the result is pass1.
+Otherwise @code{+} is illegal.
+
+@item -
+@cindex subtraction, permitted arguments
+@cindex minus, permitted arguments
+@cindex arguments for subtraction
+@dfn{Subtraction}. If the right argument is absolute, the
+result has the section of the left argument.
+If either argument is pass1 the result is pass1.
+If either argument is undefined the result is difference section.
+If both arguments are in the same section, the result is absolute---provided
+that section is one of text, data or bss.
+Otherwise subtraction is illegal.
+@end table
+@end enumerate
+
+The sense of the rule for addition is that it's only meaningful to add
+the @emph{offsets} in an address; you can only have a defined section in
+one of the two arguments.
+
+Similarly, you can't subtract quantities from two different sections.
+
+@node Pseudo Ops, _MACH_DEP__, Expressions, Top
+@chapter Assembler Directives
+
+@cindex directives, machine independent
+@cindex pseudo-ops, machine independent
+@cindex machine independent directives
+All assembler directives have names that begin with a period (@samp{.}).
+The rest of the name is letters, usually in lower case.
+
+This chapter discusses directives present regardless of the target
+machine configuration for the GNU assembler.
+_if__(!_H8__)
+@xref{_MACH_DEP__} for additional directives.
+_fi__(!_H8__)
+
+@menu
+* Abort:: @code{.abort}
+_if__(_COFF__)
+* coff-ABORT:: @code{.ABORT}
+_fi__(_COFF__)
+_if__(_BOUT__&&!_COFF__)
+* bout-ABORT:: @code{.ABORT}
+_fi__(_BOUT__&&!_COFF__)
+* Align:: @code{.align @var{abs-expr} , @var{abs-expr}}
+* App-File:: @code{.app-file @var{string}}
+* Ascii:: @code{.ascii "@var{string}"}@dots{}
+* Asciz:: @code{.asciz "@var{string}"}@dots{}
+* Byte:: @code{.byte @var{expressions}}
+* Comm:: @code{.comm @var{symbol} , @var{length} }
+* Data:: @code{.data @var{subsection}}
+_if__(_COFF__||_BOUT__)
+* Def:: @code{.def @var{name}}
+_fi__(_COFF__||_BOUT__)
+_if__(_AOUT__||_BOUT__)
+* Desc:: @code{.desc @var{symbol}, @var{abs-expression}}
+_fi__(_AOUT__||_BOUT__)
+_if__(_COFF__||_BOUT__)
+* Dim:: @code{.dim}
+_fi__(_COFF__||_BOUT__)
+* Double:: @code{.double @var{flonums}}
+* Eject:: @code{.eject}
+* Else:: @code{.else}
+_if__(_COFF__||_BOUT__)
+* Endef:: @code{.endef}
+_fi__(_COFF__||_BOUT__)
+* Endif:: @code{.endif}
+* Equ:: @code{.equ @var{symbol}, @var{expression}}
+* Extern:: @code{.extern}
+_if__(_GENERIC__||!_A29K__)
+* File:: @code{.file @var{string}}
+_fi__(_GENERIC__||!_A29K__)
+* Fill:: @code{.fill @var{repeat} , @var{size} , @var{value}}
+* Float:: @code{.float @var{flonums}}
+* Global:: @code{.global @var{symbol}}, @code{.globl @var{symbol}}
+* hword:: @code{.hword @var{expressions}}
+* Ident:: @code{.ident}
+* If:: @code{.if @var{absolute expression}}
+* Include:: @code{.include "@var{file}"}
+* Int:: @code{.int @var{expressions}}
+* Lcomm:: @code{.lcomm @var{symbol} , @var{length}}
+* Lflags:: @code{.lflags}
+_if__(_GENERIC__||!_A29K__)
+* Line:: @code{.line @var{line-number}}
+_fi__(_GENERIC__||!_A29K__)
+* Ln:: @code{.ln @var{line-number}}
+* List:: @code{.list}
+* Long:: @code{.long @var{expressions}}
+* Lsym:: @code{.lsym @var{symbol}, @var{expression}}
+* Nolist:: @code{.nolist}
+* Octa:: @code{.octa @var{bignums}}
+* Org:: @code{.org @var{new-lc} , @var{fill}}
+* Psize:: @code{.psize @var{lines}, @var{columns}}
+* Quad:: @code{.quad @var{bignums}}
+* Sbttl:: @code{.sbttl "@var{subheading}"}
+_if__(_COFF__||_BOUT__)
+* Scl:: @code{.scl @var{class}}
+_fi__(_COFF__||_BOUT__)
+_if__(_COFF__)
+* Section:: @code{.section @var{name}, @var{subsection}}
+_fi__(_COFF__)
+* Set:: @code{.set @var{symbol}, @var{expression}}
+* Short:: @code{.short @var{expressions}}
+* Single:: @code{.single @var{flonums}}
+_if__(_COFF__||_BOUT__)
+* Size:: @code{.size}
+_fi__(_COFF__||_BOUT__)
+* Space:: @code{.space @var{size} , @var{fill}}
+_if__(_GENERIC__||!_H8__)
+* Stab:: @code{.stabd, .stabn, .stabs}
+_fi__(_GENERIC__||!_H8__)
+_if__(_COFF__||_BOUT__)
+* Tag:: @code{.tag @var{structname}}
+_fi__(_COFF__||_BOUT__)
+* Text:: @code{.text @var{subsection}}
+* Title:: @code{.title "@var{heading}"}
+_if__(_COFF__||_BOUT__)
+* Type:: @code{.type @var{int}}
+* Val:: @code{.val @var{addr}}
+_fi__(_COFF__||_BOUT__)
+* Word:: @code{.word @var{expressions}}
+* Deprecated:: Deprecated Directives
+@end menu
+
+_if__(_COFF__)
+@node Abort, coff-ABORT, Pseudo Ops, Pseudo Ops
+_fi__(_COFF__)
+_if__((!_COFF__) && _BOUT__)
+@node Abort, bout-ABORT, Pseudo Ops, Pseudo Ops
+_fi__((!_COFF__) && _BOUT__)
+_if__(! (_BOUT__ || _COFF__) )
+@node Abort, Align, Pseudo Ops, Pseudo Ops
+_fi__(! (_BOUT__ || _COFF__) )
+@section @code{.abort}
+
+@cindex @code{abort} directive
+@cindex stopping the assembly
+This directive stops the assembly immediately. It is for
+compatibility with other assemblers. The original idea was that the
+assembly language source would be piped into the assembler. If the sender
+of the source quit, it could use this directive tells @code{_AS__} to
+quit also. One day @code{.abort} will not be supported.
+
+_if__(_COFF__)
+@node coff-ABORT, Align, Abort, Pseudo Ops
+@section @code{.ABORT}
+
+@cindex @code{ABORT} directive
+When producing COFF output, @code{_AS__} accepts this directive as a
+synonym for @samp{.abort}.
+_fi__(_COFF__)
+
+_if__(_BOUT__)
+_if__(!_COFF__)
+@node bout-ABORT, Align, Abort, Pseudo Ops
+@section @code{.ABORT}
+
+@cindex @code{ABORT} directive
+_fi__(!_COFF__)
+
+When producing @code{b.out} output, @code{_AS__} accepts this directive,
+but ignores it.
+_fi__(_BOUT__)
+
+_if__( ! (_COFF__ || _BOUT__) )
+@node Align, App-File, Abort, Pseudo Ops
+_fi__( ! (_COFF__ || _BOUT__) )
+_if__( _COFF__)
+@node Align, App-File, coff-ABORT, Pseudo Ops
+_fi__( _COFF__)
+_if__( _BOUT__ && (! _COFF__))
+@node Align, App-File, bout-ABORT, Pseudo Ops
+_fi__( _BOUT__ && (! _COFF__))
+@section @code{.align @var{abs-expr} , @var{abs-expr}}
+
+@cindex padding the location counter
+@cindex advancing location counter
+@cindex location counter, advancing
+@cindex @code{align} directive
+Pad the location counter (in the current subsection) to a particular
+storage boundary. The first expression (which must be absolute) is the
+number of low-order zero bits the location counter will have after
+advancement. For example @samp{.align 3} will advance the location
+counter until it a multiple of 8. If the location counter is already a
+multiple of 8, no change is needed.
+
+The second expression (also absolute) gives the value to be stored in
+the padding bytes. It (and the comma) may be omitted. If it is
+omitted, the padding bytes are zero.
+
+@node App-File, Ascii, Align, Pseudo Ops
+@section @code{.app-file @var{string}}
+
+@cindex logical file name
+@cindex file name, logical
+@cindex @code{app-file} directive
+@code{.app-file}
+_if__(!_A29K__)
+(which may also be spelled @samp{.file})
+_fi__(!_A29K__)
+tells @code{_AS__} that we are about to start a new
+logical file. @var{string} is the new file name. In general, the
+filename is recognized whether or not it is surrounded by quotes @samp{"};
+but if you wish to specify an empty file name is permitted,
+you must give the quotes--@code{""}. This statement may go away in
+future: it is only recognized to be compatible with old @code{_AS__}
+programs.@refill
+
+@node Ascii, Asciz, App-File, Pseudo Ops
+@section @code{.ascii "@var{string}"}@dots{}
+
+@cindex @code{ascii} directive
+@cindex string literals
+@code{.ascii} expects zero or more string literals (@pxref{Strings})
+separated by commas. It assembles each string (with no automatic
+trailing zero byte) into consecutive addresses.
+
+@node Asciz, Byte, Ascii, Pseudo Ops
+@section @code{.asciz "@var{string}"}@dots{}
+
+@cindex @code{asciz} directive
+@cindex zero-terminated strings
+@cindex null-terminated strings
+@code{.asciz} is just like @code{.ascii}, but each string is followed by
+a zero byte. The ``z'' in @samp{.asciz} stands for ``zero''.
+
+@node Byte, Comm, Asciz, Pseudo Ops
+@section @code{.byte @var{expressions}}
+
+@cindex @code{byte} directive
+@cindex integers, one byte
+@code{.byte} expects zero or more expressions, separated by commas.
+Each expression is assembled into the next byte.
+
+@node Comm, Data, Byte, Pseudo Ops
+@section @code{.comm @var{symbol} , @var{length} }
+
+@cindex @code{comm} directive
+@cindex symbol, common
+@code{.comm} declares a named common area in the bss section. Normally
+@code{_LD__} reserves memory addresses for it during linking, so no partial
+program defines the location of the symbol. Use @code{.comm} to tell
+@code{_LD__} that it must be at least @var{length} bytes long. @code{_LD__}
+will allocate space for each @code{.comm} symbol that is at least as
+long as the longest @code{.comm} request in any of the partial programs
+linked. @var{length} is an absolute expression.
+
+_if__(_COFF__ || _BOUT__)
+@node Data, Def, Comm, Pseudo Ops
+_fi__(_COFF__ || _BOUT__)
+_if__(!(_COFF__ || _BOUT__) && _AOUT__)
+@node Data, Desc, Comm, Pseudo Ops
+_fi__(!(_COFF__ || _BOUT__) && _AOUT__)
+_if__(! (_COFF__ || _BOUT__ || _AOUT__) )
+@c Well, this *might* happen...
+@node Data, Double, Comm, Pseudo Ops
+_fi__(! (_COFF__ || _BOUT__ || _AOUT__) )
+@section @code{.data @var{subsection}}
+
+@cindex @code{data} directive
+@code{.data} tells @code{_AS__} to assemble the following statements onto the
+end of the data subsection numbered @var{subsection} (which is an
+absolute expression). If @var{subsection} is omitted, it defaults
+to zero.
+
+_if__(_COFF__ || _BOUT__)
+_if__(_AOUT__ || _BOUT__)
+@node Def, Desc, Data, Pseudo Ops
+_fi__(_AOUT__ || _BOUT__)
+_if__(!(_AOUT__ || _BOUT__))
+@node Def, Dim, Data, Pseudo Ops
+_fi__(!(_AOUT__ || _BOUT__))
+@section @code{.def @var{name}}
+
+@cindex @code{def} directive
+@cindex COFF symbols, debugging
+@cindex debugging COFF symbols
+Begin defining debugging information for a symbol @var{name}; the
+definition extends until the @code{.endef} directive is encountered.
+_if__(_BOUT__)
+
+This directive is only observed when @code{_AS__} is configured for COFF
+format output; when producing @code{b.out}, @samp{.def} is recognized,
+but ignored.
+_fi__(_BOUT__)
+_fi__(_COFF__ || _BOUT__)
+
+_if__(_AOUT__||_BOUT__)
+_if__(_COFF__||_BOUT__)
+@node Desc, Dim, Def, Pseudo Ops
+_fi__(_COFF__||_BOUT__)
+_if__(!(_COFF__||_BOUT__))
+@node Desc, Double, Data, Pseudo Ops
+_fi__(!(_COFF__||_BOUT__))
+@section @code{.desc @var{symbol}, @var{abs-expression}}
+
+@cindex @code{desc} directive
+@cindex COFF symbol descriptor
+@cindex symbol descriptor, COFF
+This directive sets the descriptor of the symbol (@pxref{Symbol Attributes})
+to the low 16 bits of an absolute expression.
+
+_if__(_COFF__)
+The @samp{.desc} directive is not available when @code{_AS__} is
+configured for COFF output; it is only for @code{a.out} or @code{b.out}
+object format. For the sake of compatibility, @code{_AS__} will accept
+it, but produce no output, when configured for COFF.
+_fi__(_COFF__)
+_fi__(_AOUT__||_BOUT__)
+
+_if__(_COFF__ || _BOUT__)
+_if__(_AOUT__ || _BOUT__)
+@node Dim, Double, Desc, Pseudo Ops
+_fi__(_AOUT__ || _BOUT__)
+_if__(!(_AOUT__ || _BOUT__))
+@node Dim, Double, Def, Pseudo Ops
+_fi__(!(_AOUT__ || _BOUT__))
+@section @code{.dim}
+
+@cindex @code{dim} directive
+@cindex COFF auxiliary symbol information
+@cindex auxiliary symbol information, COFF
+This directive is generated by compilers to include auxiliary debugging
+information in the symbol table. It is only permitted inside
+@code{.def}/@code{.endef} pairs.
+_if__(_BOUT__)
+
+@samp{.dim} is only meaningful when generating COFF format output; when
+@code{_AS__} is generating @code{b.out}, it accepts this directive but
+ignores it.
+_fi__(_BOUT__)
+_fi__(_COFF__ || _BOUT__)
+
+_if__(_COFF__||_BOUT__)
+@node Double, Eject, Dim, Pseudo Ops
+_fi__(_COFF__||_BOUT__)
+_if__(!(_COFF__||_BOUT__))
+@node Double, Eject, Desc, Pseudo Ops
+_fi__(!(_COFF__||_BOUT__))
+@section @code{.double @var{flonums}}
+
+@cindex @code{double} directive
+@cindex floating point numbers (double)
+@code{.double} expects zero or more flonums, separated by commas. It
+assembles floating point numbers.
+_if__(_GENERIC__)
+The exact kind of floating point numbers emitted depends on how
+@code{_AS__} is configured. @xref{_MACH_DEP__}.
+_fi__(_GENERIC__)
+_if__((!_GENERIC__) && _IEEEFLOAT__)
+On the _HOST__ family @samp{.double} emits 64-bit floating-point numbers
+in @sc{ieee} format.
+_fi__((!_GENERIC__) && _IEEEFLOAT__)
+
+@node Eject, Else, Double, Pseudo Ops
+@section @code{.eject}
+
+@cindex @code{eject} directive
+@cindex new page, in listings
+@cindex page, in listings
+@cindex listing control: new page
+Force a page break at this point, when generating assembly listings.
+
+_if__(_COFF__||_BOUT__)
+@node Else, Endef, Eject, Pseudo Ops
+_fi__(_COFF__||_BOUT__)
+_if__(!(_COFF__||_BOUT__))
+@node Else, Endif, Eject, Pseudo Ops
+_fi__(!(_COFF__||_BOUT__))
+@section @code{.else}
+
+@cindex @code{else} directive
+@code{.else} is part of the @code{_AS__} support for conditional
+assembly; @pxref{If,,@code{.if}}. It marks the beginning of a section
+of code to be assembled if the condition for the preceding @code{.if}
+was false.
+
+_if__(0)
+@node End, Endef, Else, Pseudo Ops
+@section @code{.end}
+
+@cindex @code{end} directive
+This doesn't do anything---but isn't an s_ignore, so I suspect it's
+meant to do something eventually (which is why it isn't documented here
+as "for compatibility with blah").
+_fi__(0)
+
+_if__(_COFF__||_BOUT__)
+@node Endef, Endif, Else, Pseudo Ops
+@section @code{.endef}
+
+@cindex @code{endef} directive
+This directive flags the end of a symbol definition begun with
+@code{.def}.
+_if__(_BOUT__)
+
+@samp{.endef} is only meaningful when generating COFF format output; if
+@code{_AS__} is configured to generate @code{b.out}, it accepts this
+directive but ignores it.
+_fi__(_BOUT__)
+_fi__(_COFF__||_BOUT__)
+
+_if__(_COFF__||_BOUT__)
+@node Endif, Equ, Endef, Pseudo Ops
+_fi__(_COFF__||_BOUT__)
+_if__(!(_COFF__||_BOUT__))
+@node Endif, Equ, Else, Pseudo Ops
+_fi__(!(_COFF__||_BOUT__))
+@section @code{.endif}
+
+@cindex @code{endif} directive
+@code{.endif} is part of the @code{_AS__} support for conditional assembly;
+it marks the end of a block of code that is only assembled
+conditionally. @xref{If,,@code{.if}}.
+
+@node Equ, Extern, Endif, Pseudo Ops
+@section @code{.equ @var{symbol}, @var{expression}}
+
+@cindex @code{equ} directive
+@cindex assigning values to symbols
+@cindex symbols, assigning values to
+This directive sets the value of @var{symbol} to @var{expression}.
+It is synonymous with @samp{.set}; @pxref{Set,,@code{.set}}.
+
+_if__(_GENERIC__||!_A29K__)
+@node Extern, File, Equ, Pseudo Ops
+_fi__(_GENERIC__||!_A29K__)
+_if__(_A29K__&&!_GENERIC__)
+@node Extern, Fill, Equ, Pseudo Ops
+_fi__(_A29K__&&!_GENERIC__)
+@section @code{.extern}
+
+@cindex @code{extern} directive
+@code{.extern} is accepted in the source program---for compatibility
+with other assemblers---but it is ignored. @code{_AS__} treats
+all undefined symbols as external.
+
+_if__(_GENERIC__||!_A29K__)
+@node File, Fill, Extern, Pseudo Ops
+@section @code{.file @var{string}}
+
+@cindex @code{file} directive
+@cindex logical file name
+@cindex file name, logical
+@code{.file} (which may also be spelled @samp{.app-file}) tells
+@code{_AS__} that we are about to start a new logical file.
+@var{string} is the new file name. In general, the filename is
+recognized whether or not it is surrounded by quotes @samp{"}; but if
+you wish to specify an empty file name, you must give the
+quotes--@code{""}. This statement may go away in future: it is only
+recognized to be compatible with old @code{_AS__} programs.
+_if__(_A29K__)
+In some configurations of @code{_AS__}, @code{.file} has already been
+removed to avoid conflicts with other assemblers. @xref{_MACH_DEP__}.
+_fi__(_A29K__)
+_fi__(_GENERIC__||!_A29K__)
+
+_if__(_GENERIC__||!_A29K__)
+@node Fill, Float, File, Pseudo Ops
+_fi__(_GENERIC__||!_A29K__)
+_if__(_A29K__&&!_GENERIC__)
+@node Fill, Float, Extern, Pseudo Ops
+_fi__(_A29K__&&!_GENERIC__)
+@section @code{.fill @var{repeat} , @var{size} , @var{value}}
+
+@cindex @code{fill} directive
+@cindex writing patterns in memory
+@cindex patterns, writing in memory
+@var{result}, @var{size} and @var{value} are absolute expressions.
+This emits @var{repeat} copies of @var{size} bytes. @var{Repeat}
+may be zero or more. @var{Size} may be zero or more, but if it is
+more than 8, then it is deemed to have the value 8, compatible with
+other people's assemblers. The contents of each @var{repeat} bytes
+is taken from an 8-byte number. The highest order 4 bytes are
+zero. The lowest order 4 bytes are @var{value} rendered in the
+byte-order of an integer on the computer @code{_AS__} is assembling for.
+Each @var{size} bytes in a repetition is taken from the lowest order
+@var{size} bytes of this number. Again, this bizarre behavior is
+compatible with other people's assemblers.
+
+@var{size} and @var{value} are optional.
+If the second comma and @var{value} are absent, @var{value} is
+assumed zero. If the first comma and following tokens are absent,
+@var{size} is assumed to be 1.
+
+@node Float, Global, Fill, Pseudo Ops
+@section @code{.float @var{flonums}}
+
+@cindex floating point numbers (single)
+@cindex @code{float} directive
+This directive assembles zero or more flonums, separated by commas. It
+has the same effect as @code{.single}.
+_if__(_GENERIC__)
+The exact kind of floating point numbers emitted depends on how
+@code{_AS__} is configured.
+@xref{_MACH_DEP__}.
+_fi__(_GENERIC__)
+_if__((!_GENERIC__) && _IEEEFLOAT__)
+On the _HOST__ family, @code{.float} emits 32-bit floating point numbers
+in @sc{ieee} format.
+_fi__((!_GENERIC__) && _IEEEFLOAT__)
+
+@node Global, hword, Float, Pseudo Ops
+@section @code{.global @var{symbol}}, @code{.globl @var{symbol}}
+
+@cindex @code{global} directive
+@cindex symbol, making visible to linker
+@code{.global} makes the symbol visible to @code{_LD__}. If you define
+@var{symbol} in your partial program, its value is made available to
+other partial programs that are linked with it. Otherwise,
+@var{symbol} will take its attributes from a symbol of the same name
+from another partial program it is linked with.
+
+Both spellings (@samp{.globl} and @samp{.global}) are accepted, for
+compatibility with other assemblers.
+
+_if__(_AOUT__||_BOUT__||_COFF__)
+@node hword, Ident, Global, Pseudo Ops
+_fi__(_AOUT__||_BOUT__||_COFF__)
+_if__(!(_AOUT__||_BOUT__||_COFF__))
+@node hword, If, Global, Pseudo Ops
+_fi__(!(_AOUT__||_BOUT__||_COFF__))
+@section @code{.hword @var{expressions}}
+
+@cindex @code{hword} directive
+@cindex integers, 16-bit
+@cindex numbers, 16-bit
+@cindex sixteen bit integers
+This expects zero or more @var{expressions}, and emits
+a 16 bit number for each.
+
+_if__(_GENERIC__)
+This directive is a synonym for @samp{.short}; depending on the target
+architecture, it may also be a synonym for @samp{.word}.
+_fi__(_GENERIC__)
+_if__( _W32__ && !_GENERIC__ )
+This directive is a synonym for @samp{.short}.
+_fi__( _W32__ && !_GENERIC__ )
+_if__(_W16__ && !_GENERIC__ )
+This directive is a synonym for both @samp{.short} and @samp{.word}.
+_fi__(_W16__ && !_GENERIC__ )
+
+_if__(_AOUT__||_BOUT__||_COFF__)
+@node Ident, If, hword, Pseudo Ops
+@section @code{.ident}
+
+@cindex @code{ident} directive
+This directive is used by some assemblers to place tags in object files.
+@code{_AS__} simply accepts the directive for source-file
+compatibility with such assemblers, but does not actually emit anything
+for it.
+_fi__(_AOUT__||_BOUT__||_COFF__)
+
+_if__(_AOUT__||_BOUT__||_COFF__)
+@node If, Include, Ident, Pseudo Ops
+_fi__(_AOUT__||_BOUT__||_COFF__)
+_if__(!(_AOUT__||_BOUT__||_COFF__))
+@node If, Include, hword, Pseudo Ops
+_fi__(!(_AOUT__||_BOUT__||_COFF__))
+@section @code{.if @var{absolute expression}}
+
+@cindex conditional assembly
+@cindex @code{if} directive
+@code{.if} marks the beginning of a section of code which is only
+considered part of the source program being assembled if the argument
+(which must be an @var{absolute expression}) is non-zero. The end of
+the conditional section of code must be marked by @code{.endif}
+(@pxref{Endif,,@code{.endif}}); optionally, you may include code for the
+alternative condition, flagged by @code{.else} (@pxref{Else,,@code{.else}}.
+
+The following variants of @code{.if} are also supported:
+@table @code
+@item .ifdef @var{symbol}
+@cindex @code{ifdef} directive
+Assembles the following section of code if the specified @var{symbol}
+has been defined.
+
+_if__(0)
+@item .ifeqs
+@cindex @code{ifeqs} directive
+Not yet implemented.
+_fi__(0)
+
+@item .ifndef @var{symbol}
+@itemx ifnotdef @var{symbol}
+@cindex @code{ifndef} directive
+@cindex @code{ifnotdef} directive
+Assembles the following section of code if the specified @var{symbol}
+has not been defined. Both spelling variants are equivalent.
+
+_if__(0)
+@item ifnes
+Not yet implemented.
+_fi__(0)
+@end table
+
+@node Include, Int, If, Pseudo Ops
+@section @code{.include "@var{file}"}
+
+@cindex @code{include} directive
+@cindex supporting files, including
+@cindex files, including
+This directive provides a way to include supporting files at specified
+points in your source program. The code from @var{file} is assembled as
+if it followed the point of the @code{.include}; when the end of the
+included file is reached, assembly of the original file continues. You
+can control the search paths used with the @samp{-I} command-line option
+(@pxref{Invoking,,Command-Line Options}). Quotation marks are required
+around @var{file}.
+
+@node Int, Lcomm, Include, Pseudo Ops
+@section @code{.int @var{expressions}}
+
+@cindex @code{int} directive
+_if__(_GENERIC__||!_H8__)
+@cindex integers, 32-bit
+_fi__(_GENERIC__||!_H8__)
+Expect zero or more @var{expressions}, of any section, separated by
+commas. For each expression, emit a
+_if__(_GENERIC__||!_H8__)
+32-bit
+_fi__(_GENERIC__||!_H8__)
+_if__(_H8__&&!_GENERIC__)
+16-bit
+_fi__(_H8__&&!_GENERIC__)
+number that will, at run
+time, be the value of that expression. The byte order of the
+expression depends on what kind of computer will run the program.
+
+@node Lcomm, Lflags, Int, Pseudo Ops
+@section @code{.lcomm @var{symbol} , @var{length}}
+
+@cindex @code{lcomm} directive
+@cindex local common symbols
+@cindex symbols, local common
+Reserve @var{length} (an absolute expression) bytes for a local common
+denoted by @var{symbol}. The section and value of @var{symbol} are
+those of the new local common. The addresses are allocated in the bss
+section, so at run-time the bytes will start off zeroed. @var{Symbol}
+is not declared global (@pxref{Global,,@code{.global}}), so is normally
+not visible to @code{_LD__}.
+
+_if__(_GENERIC__||(!_A29K__))
+@node Lflags, Line, Lcomm, Pseudo Ops
+_fi__(_GENERIC__||(!_A29K__))
+_if__((!_GENERIC__)&& _A29K__)
+@node Lflags, Ln, Lcomm, Pseudo Ops
+_fi__((!_GENERIC__)&& _A29K__)
+@section @code{.lflags}
+
+@cindex @code{lflags} directive (ignored)
+@code{_AS__} accepts this directive, for compatibility with other
+assemblers, but ignores it.
+
+_if__(_GENERIC__ || !_A29K__)
+@node Line, Ln, Lflags, Pseudo Ops
+@section @code{.line @var{line-number}}
+
+@cindex @code{line} directive
+_fi__(_GENERIC__ || (!_A29K__))
+_if__(_A29K__ && (!_GENERIC__))
+@node Ln, List, Lflags, Pseudo Ops
+@section @code{.ln @var{line-number}}
+
+@cindex @code{ln} directive
+_fi__(_A29K__ && (!_GENERIC__))
+@cindex logical line number
+_if__(_AOUT__||_BOUT__)
+Tell @code{_AS__} to change the logical line number. @var{line-number} must be
+an absolute expression. The next line will have that logical line
+number. So any other statements on the current line (after a statement
+separator
+_if__(_GENERIC__)
+character)
+_fi__(_GENERIC__)
+_if__(!_GENERIC__)
+_if__(! (_A29K__||_H8__) )
+character @code{;})
+_fi__(! (_A29K__||_H8__) )
+_if__(_A29K__)
+character @samp{@@})
+_fi__(_A29K__)
+_if__(_H8__)
+character @samp{$})
+_fi__(_H8__)
+_fi__(!_GENERIC__)
+will be reported as on logical line number
+@var{line-number} @minus{} 1.
+One day this directive will be unsupported: it is used only
+for compatibility with existing assembler programs. @refill
+
+_if__(_GENERIC__ && _A29K__)
+@emph{Warning:} In the AMD29K configuration of _AS__, this command is
+only available with the name @code{.ln}, rather than as either
+@code{.line} or @code{.ln}.
+_fi__(_GENERIC__ && _A29K__)
+_fi__(_AOUT__||_BOUT__)
+_if__(_COFF__)
+
+Even though this is a directive associated with the @code{a.out} or
+@code{b.out} object-code formats, @code{_AS__} will still recognize it
+when producing COFF output, and will treat @samp{.line} as though it
+were the COFF @samp{.ln} @emph{if} it is found outside a
+@code{.def}/@code{.endef} pair.
+
+Inside a @code{.def}, @samp{.line} is, instead, one of the directives
+used by compilers to generate auxiliary symbol information for
+debugging.
+_fi__(_COFF__)
+
+_if__(_AOUT__&&(_GENERIC__||!_A29K__))
+@node Ln, List, Line, Pseudo Ops
+@section @code{.ln @var{line-number}}
+
+@cindex @code{ln} directive
+@samp{.ln} is a synonym for @samp{.line}.
+_fi__(_AOUT__&&(_GENERIC__||!_A29K__))
+_if__(_COFF__&&!_AOUT__)
+@node Ln, List, Line, Pseudo Ops
+@section @code{.ln @var{line-number}}
+
+@cindex @code{ln} directive
+Tell @code{_AS__} to change the logical line number. @var{line-number}
+must be an absolute expression. The next line will have that logical
+line number, so any other statements on the current line (after a
+statement separator character @code{;}) will be reported as on logical
+line number @var{line-number} @minus{} 1.
+_if__(_BOUT__)
+
+This directive is accepted, but ignored, when @code{_AS__} is configured for
+@code{b.out}; its effect is only associated with COFF output format.
+_fi__(_BOUT__)
+_fi__(_COFF__&&!_AOUT__)
+
+@node List, Long, Ln, Pseudo Ops
+@section @code{.list}
+
+@cindex @code{list} directive
+@cindex listing control, turning on
+Control (in conjunction with the @code{.nolist} directive) whether or
+not assembly listings are generated. These two directives maintain an
+internal counter (which is zero initially). @code{.list} increments the
+counter, and @code{.nolist} decrements it. Assembly listings are
+generated whenever the counter is greater than zero.
+
+By default, listings are disabled. When you enable them (with the
+@samp{-a} command line option; @pxref{Invoking,,Command-Line Options}),
+the initial value of the listing counter is one.
+
+@node Long, Lsym, List, Pseudo Ops
+@section @code{.long @var{expressions}}
+
+@cindex @code{long} directive
+@code{.long} is the same as @samp{.int}, @pxref{Int,,@code{.int}}.
+
+@node Lsym, Nolist, Long, Pseudo Ops
+@section @code{.lsym @var{symbol}, @var{expression}}
+
+@cindex @code{lsym} directive
+@cindex symbol, not referenced in assembly
+@code{.lsym} creates a new symbol named @var{symbol}, but does not put it in
+the hash table, ensuring it cannot be referenced by name during the
+rest of the assembly. This sets the attributes of the symbol to be
+the same as the expression value:
+@smallexample
+@var{other} = @var{descriptor} = 0
+@var{type} = @r{(section of @var{expression})}
+@var{value} = @var{expression}
+@end smallexample
+@noindent
+The new symbol is not flagged as external.
+
+@node Nolist, Octa, Lsym, Pseudo Ops
+@section @code{.nolist}
+
+@cindex @code{nolist} directive
+@cindex listing control, turning off
+Control (in conjunction with the @code{.list} directive) whether or
+not assembly listings are generated. These two directives maintain an
+internal counter (which is zero initially). @code{.list} increments the
+counter, and @code{.nolist} decrements it. Assembly listings are
+generated whenever the counter is greater than zero.
+
+@node Octa, Org, Nolist, Pseudo Ops
+@section @code{.octa @var{bignums}}
+
+@c FIXME: double size emitted for "octa" on i960, others? Or warn?
+@cindex @code{octa} directive
+@cindex integer, 16-byte
+@cindex sixteen byte integer
+This directive expects zero or more bignums, separated by commas. For each
+bignum, it emits a 16-byte integer.
+
+The term ``octa'' comes from contexts in which a ``word'' is two bytes;
+hence @emph{octa}-word for 16 bytes.
+
+@node Org, Psize, Octa, Pseudo Ops
+@section @code{.org @var{new-lc} , @var{fill}}
+
+@cindex @code{org} directive
+@cindex location counter, advancing
+@cindex advancing location counter
+@cindex current address, advancing
+@code{.org} will advance the location counter of the current section to
+@var{new-lc}. @var{new-lc} is either an absolute expression or an
+expression with the same section as the current subsection. That is,
+you can't use @code{.org} to cross sections: if @var{new-lc} has the
+wrong section, the @code{.org} directive is ignored. To be compatible
+with former assemblers, if the section of @var{new-lc} is absolute,
+@code{_AS__} will issue a warning, then pretend the section of @var{new-lc}
+is the same as the current subsection.
+
+@code{.org} may only increase the location counter, or leave it
+unchanged; you cannot use @code{.org} to move the location counter
+backwards.
+
+@c double negative used below "not undefined" because this is a specific
+@c reference to "undefined" (as SEG_UNKNOWN is called in this manual)
+@c section. pesch@cygnus.com 18feb91
+Because @code{_AS__} tries to assemble programs in one pass @var{new-lc}
+may not be undefined. If you really detest this restriction we eagerly await
+a chance to share your improved assembler.
+
+Beware that the origin is relative to the start of the section, not
+to the start of the subsection. This is compatible with other
+people's assemblers.
+
+When the location counter (of the current subsection) is advanced, the
+intervening bytes are filled with @var{fill} which should be an
+absolute expression. If the comma and @var{fill} are omitted,
+@var{fill} defaults to zero.
+
+@node Psize, Quad, Org, Pseudo Ops
+@section @code{.psize @var{lines} , @var{columns}}
+
+@cindex @code{psize} directive
+@cindex listing control: paper size
+@cindex paper size, for listings
+Use this directive to declare the number of lines---and, optionally, the
+number of columns---to use for each page, when generating listings.
+
+If you don't use @code{.psize}, listings will use a default line-count
+of 60. You may omit the comma and @var{columns} specification; the
+default width is 200 columns.
+
+@code{_AS__} will generate formfeeds whenever the specified number of
+lines is exceeded (or whenever you explicitly request one, using
+@code{.eject}).
+
+If you specify @var{lines} as @code{0}, no formfeeds are generated save
+those explicitly specified with @code{.eject}.
+
+@node Quad, Sbttl, Psize, Pseudo Ops
+@section @code{.quad @var{bignums}}
+
+@cindex @code{quad} directive
+@code{.quad} expects zero or more bignums, separated by commas. For
+each bignum, it emits
+_if__(_GENERIC__||(!_I960__))
+an 8-byte integer. If the bignum won't fit in 8
+bytes, it prints a warning message; and just takes the lowest order 8
+bytes of the bignum.@refill
+@cindex eight-byte integer
+@cindex integer, 8-byte
+
+The term ``quad'' comes from contexts in which a ``word'' is two bytes;
+hence @emph{quad}-word for 8 bytes.
+_fi__(_GENERIC__||(!_I960__))
+_if__(_I960__&&(!_GENERIC__))
+a 16-byte integer. If the bignum won't fit in 16 bytes, it prints a
+warning message; and just takes the lowest order 16 bytes of the
+bignum.@refill
+@cindex sixteen-byte integer
+@cindex integer, 16-byte
+_fi__(_I960__&&(!_GENERIC__))
+
+_if__(_COFF__||_BOUT__)
+@node Sbttl, Scl, Quad, Pseudo Ops
+_fi__(_COFF__||_BOUT__)
+_if__(!(_COFF__||_BOUT__))
+@node Sbttl, Set, Quad, Pseudo Ops
+_fi__(!(_COFF__||_BOUT__))
+@section @code{.sbttl "@var{subheading}"}
+
+@cindex @code{sbttl} directive
+@cindex subtitles for listings
+@cindex listing control: subtitle
+Use @var{subheading} as the title (third line, immediately after the
+title line) when generating assembly listings.
+
+This directive affects subsequent pages, as well as the current page if
+it appears within ten lines of the top of a page.
+
+_if__(_COFF__||_BOUT__)
+_if__(!_COFF__)
+@node Scl, Set, Sbttl, Pseudo Ops
+_fi__(!_COFF__)
+_if__(_COFF__)
+@node Scl, Section, Sbttl, Pseudo Ops
+_fi__(_COFF__)
+@section @code{.scl @var{class}}
+
+@cindex @code{scl} directive
+@cindex symbol storage class (COFF)
+@cindex COFF symbol storage class
+Set the storage-class value for a symbol. This directive may only be
+used inside a @code{.def}/@code{.endef} pair. Storage class may flag
+whether a symbol is static or external, or it may record further
+symbolic debugging information.
+_if__(_BOUT__)
+
+The @samp{.scl} directive is primarily associated with COFF output; when
+configured to generate @code{b.out} output format, @code{_AS__} will
+accept this directive but ignore it.
+_fi__(_BOUT__)
+_fi__(_COFF__||_BOUT__)
+
+_if__(_COFF__)
+@node Section, Set, Scl, Pseudo Ops
+@section @code{.section @var{name}, @var{subsection}}
+
+@cindex @code{section} directive
+@cindex named section (COFF)
+@cindex COFF named section
+Assemble the following code into end of subsection numbered
+@var{subsection} in the COFF named section @var{name}. If you omit
+@var{subsection}, @code{_AS__} uses subsection number zero.
+@samp{.section .text} is equivalent to the @code{.text} directive;
+@samp{.section .data} is equivalent to the @code{.data} directive.
+
+@node Set, Short, Section, Pseudo Ops
+_fi__(_COFF__)
+_if__(_BOUT__&&!_COFF__)
+@node Set, Short, Scl, Pseudo Ops
+_fi__(_BOUT__&&!_COFF__)
+_if__(!(_COFF__||_BOUT__))
+@node Set, Short, Quad, Pseudo Ops
+_fi__(!(_COFF__||_BOUT__))
+@section @code{.set @var{symbol}, @var{expression}}
+
+@cindex @code{set} directive
+@cindex symbol value, setting
+This directive sets the value of @var{symbol} to @var{expression}. This
+will change @var{symbol}'s value and type to conform to
+@var{expression}. If @var{symbol} was flagged as external, it remains
+flagged. (@xref{Symbol Attributes}.)
+
+You may @code{.set} a symbol many times in the same assembly.
+If the expression's section is unknowable during pass 1, a second
+pass over the source program will be forced. The second pass is
+currently not implemented. @code{_AS__} will abort with an error
+message if one is required.
+
+If you @code{.set} a global symbol, the value stored in the object
+file is the last value stored into it.
+
+@node Short, Single, Set, Pseudo Ops
+@section @code{.short @var{expressions}}
+
+@cindex @code{short} directive
+_if__(_GENERIC__ || _W16__)
+@code{.short} is the same as @samp{.word}. @xref{Word,,@code{.word}}.
+_if__(_W32__)
+In some configurations, however, @code{.short} and @code{.word} generate
+numbers of different lengths; @pxref{_MACH_DEP__}.
+_fi__(_W32__)
+_fi__(_GENERIC__|| _W16__)
+_if__((!_GENERIC__) && _W32__)
+This expects zero or more @var{expressions}, and emits
+a 16 bit number for each.
+_fi__((!_GENERIC__) && _W32__)
+_if__(_COFF__||_BOUT__)
+@node Single, Size, Short, Pseudo Ops
+_fi__(_COFF__||_BOUT__)
+_if__(!(_COFF__||_BOUT__))
+@node Single, Space, Short, Pseudo Ops
+_fi__(!(_COFF__||_BOUT__))
+@section @code{.single @var{flonums}}
+
+@cindex @code{single} directive
+@cindex floating point numbers (single)
+This directive assembles zero or more flonums, separated by commas. It
+has the same effect as @code{.float}.
+_if__(_GENERIC__)
+The exact kind of floating point numbers emitted depends on how
+@code{_AS__} is configured. @xref{_MACH_DEP__}.
+_fi__(_GENERIC__)
+_if__((!_GENERIC__) && _IEEEFLOAT__)
+On the _HOST__ family, @code{.single} emits 32-bit floating point
+numbers in @sc{ieee} format.
+_fi__((!_GENERIC__) && _IEEEFLOAT__)
+
+_if__(_COFF__||_BOUT__)
+@node Size, Space, Single, Pseudo Ops
+@section @code{.size}
+
+@cindex @code{size} directive
+This directive is generated by compilers to include auxiliary debugging
+information in the symbol table. It is only permitted inside
+@code{.def}/@code{.endef} pairs.
+_if__(_BOUT__)
+
+@samp{.size} is only meaningful when generating COFF format output; when
+@code{_AS__} is generating @code{b.out}, it accepts this directive but
+ignores it.
+_fi__(_BOUT__)
+_fi__(_COFF__||_BOUT__)
+
+_if__(_H8__&&!_GENERIC__)
+@node Space, Tag, Size, Pseudo Ops
+_fi__(_H8__&&!_GENERIC__)
+_if__(_GENERIC__||!_H8__)
+_if__(_COFF__||_BOUT__)
+@node Space, Stab, Size, Pseudo Ops
+_fi__(_COFF__||_BOUT__)
+_if__(!(_COFF__||_BOUT__))
+@node Space, Stab, Single, Pseudo Ops
+_fi__(!(_COFF__||_BOUT__))
+_fi__(_GENERIC__||!_H8__)
+_if__(_GENERIC__ || !_A29K__)
+@section @code{.space @var{size} , @var{fill}}
+
+@cindex @code{space} directive
+@cindex filling memory
+This directive emits @var{size} bytes, each of value @var{fill}. Both
+@var{size} and @var{fill} are absolute expressions. If the comma
+and @var{fill} are omitted, @var{fill} is assumed to be zero.
+_fi__(_GENERIC__ || !_A29K__)
+
+_if__(_A29K__)
+@section @code{.space}
+
+@cindex @code{space} directive
+On the AMD 29K, this directive is ignored; it is accepted for
+compatibility with other AMD 29K assemblers.
+
+@quotation
+@emph{Warning:} In other versions of the GNU assembler, the directive
+@code{.space} has the effect of @code{.block} @xref{_MACH_DEP__}.
+@end quotation
+_fi__(_A29K__)
+
+_if__(_GENERIC__||!_H8__)
+_if__(_AOUT__||_BOUT__||_COFF__)
+_if__(_COFF__||_BOUT__)
+@node Stab, Tag, Space, Pseudo Ops
+_fi__(_COFF__||_BOUT__)
+_if__(!(_COFF__||_BOUT__))
+@node Stab, Text, Space, Pseudo Ops
+_fi__(!(_COFF__||_BOUT__))
+@section @code{.stabd, .stabn, .stabs}
+
+@cindex symbolic debuggers, information for
+@cindex @code{stab@var{x}} directives
+There are three directives that begin @samp{.stab}.
+All emit symbols (@pxref{Symbols}), for use by symbolic debuggers.
+The symbols are not entered in the @code{_AS__} hash table: they
+cannot be referenced elsewhere in the source file.
+Up to five fields are required:
+@table @var
+@item string
+This is the symbol's name. It may contain any character except @samp{\000},
+so is more general than ordinary symbol names. Some debuggers used to
+code arbitrarily complex structures into symbol names using this field.
+@item type
+An absolute expression. The symbol's type is set to the low 8
+bits of this expression.
+Any bit pattern is permitted, but @code{_LD__} and debuggers will choke on
+silly bit patterns.
+@item other
+An absolute expression.
+The symbol's ``other'' attribute is set to the low 8 bits of this expression.
+@item desc
+An absolute expression.
+The symbol's descriptor is set to the low 16 bits of this expression.
+@item value
+An absolute expression which becomes the symbol's value.
+@end table
+
+If a warning is detected while reading a @code{.stabd}, @code{.stabn},
+or @code{.stabs} statement, the symbol has probably already been created
+and you will get a half-formed symbol in your object file. This is
+compatible with earlier assemblers!
+
+@table @code
+@cindex @code{stabd} directive
+@item .stabd @var{type} , @var{other} , @var{desc}
+
+The ``name'' of the symbol generated is not even an empty string.
+It is a null pointer, for compatibility. Older assemblers used a
+null pointer so they didn't waste space in object files with empty
+strings.
+
+The symbol's value is set to the location counter,
+relocatably. When your program is linked, the value of this symbol
+will be where the location counter was when the @code{.stabd} was
+assembled.
+
+@item .stabn @var{type} , @var{other} , @var{desc} , @var{value}
+@cindex @code{stabn} directive
+The name of the symbol is set to the empty string @code{""}.
+
+@item .stabs @var{string} , @var{type} , @var{other} , @var{desc} , @var{value}
+@cindex @code{stabs} directive
+All five fields are specified.
+@end table
+_fi__(_AOUT__||_BOUT__||_COFF__)
+_fi__(_GENERIC__||!_H8__)
+
+_if__(_COFF__||_BOUT__)
+_if__(_GENERIC__||!_H8__)
+@node Tag, Text, Stab, Pseudo Ops
+_fi__(_GENERIC__||!_H8__)
+_if__(_H8__&&!_GENERIC__)
+@node Tag, Text, Space, Pseudo Ops
+_fi__(_H8__&&!_GENERIC__)
+@section @code{.tag @var{structname}}
+
+@cindex COFF structure debugging
+@cindex structure debugging, COFF
+@cindex @code{tag} directive
+This directive is generated by compilers to include auxiliary debugging
+information in the symbol table. It is only permitted inside
+@code{.def}/@code{.endef} pairs. Tags are used to link structure
+definitions in the symbol table with instances of those structures.
+_if__(_BOUT__)
+
+@samp{.tag} is only used when generating COFF format output; when
+@code{_AS__} is generating @code{b.out}, it accepts this directive but
+ignores it.
+_fi__(_BOUT__)
+_fi__(_COFF__||_BOUT__)
+
+_if__(_COFF__||_BOUT__)
+@node Text, Title, Tag, Pseudo Ops
+_fi__(_COFF__||_BOUT__)
+_if__(!(_COFF__||_BOUT__))
+@node Text, Title, Stab, Pseudo Ops
+_fi__(!(_COFF__||_BOUT__))
+@section @code{.text @var{subsection}}
+
+@cindex @code{text} directive
+Tells @code{_AS__} to assemble the following statements onto the end of
+the text subsection numbered @var{subsection}, which is an absolute
+expression. If @var{subsection} is omitted, subsection number zero
+is used.
+
+_if__(_COFF__||_BOUT__)
+@node Title, Type, Text, Pseudo Ops
+_fi__(_COFF__||_BOUT__)
+_if__(!(_COFF__||_BOUT__))
+@node Title, Word, Text, Pseudo Ops
+_fi__(!(_COFF__||_BOUT__))
+@section @code{.title "@var{heading}"}
+
+@cindex @code{title} directive
+@cindex listing control: title line
+Use @var{heading} as the title (second line, immediately after the
+source file name and pagenumber) when generating assembly listings.
+
+This directive affects subsequent pages, as well as the current page if
+it appears within ten lines of the top of a page.
+
+_if__(_COFF__||_BOUT__)
+@node Type, Val, Title, Pseudo Ops
+@section @code{.type @var{int}}
+
+@cindex COFF symbol type
+@cindex symbol type, COFF
+@cindex @code{type} directive
+This directive, permitted only within @code{.def}/@code{.endef} pairs,
+records the integer @var{int} as the type attribute of a symbol table entry.
+_if__(_BOUT__)
+
+@samp{.type} is associated only with COFF format output; when
+@code{_AS__} is configured for @code{b.out} output, it accepts this
+directive but ignores it.
+_fi__(_BOUT__)
+_fi__(_COFF__||_BOUT__)
+
+_if__(_COFF__||_BOUT__)
+@node Val, Word, Type, Pseudo Ops
+@section @code{.val @var{addr}}
+
+@cindex @code{val} directive
+@cindex COFF value attribute
+@cindex value attribute, COFF
+This directive, permitted only within @code{.def}/@code{.endef} pairs,
+records the address @var{addr} as the value attribute of a symbol table
+entry.
+_if__(_BOUT__)
+
+@samp{.val} is used only for COFF output; when @code{_AS__} is
+configured for @code{b.out}, it accepts this directive but ignores it.
+_fi__(_BOUT__)
+_fi__(_COFF__||_BOUT__)
+
+_if__(_COFF__||_BOUT__)
+@node Word, Deprecated, Val, Pseudo Ops
+_fi__(_COFF__||_BOUT__)
+_if__(!(_COFF__||_BOUT__))
+@node Word, Deprecated, Text, Pseudo Ops
+_fi__(!(_COFF__||_BOUT__))
+@section @code{.word @var{expressions}}
+
+@cindex @code{word} directive
+This directive expects zero or more @var{expressions}, of any section,
+separated by commas.
+_if__((!_GENERIC__) && _W32__)
+For each expression, @code{_AS__} emits a 32-bit number.
+_fi__((!_GENERIC__) && _W32__)
+_if__((!_GENERIC__) && _W16__)
+For each expression, @code{_AS__} emits a 16-bit number.
+_fi__((!_GENERIC__) && _W16__)
+
+_if__(_GENERIC__)
+The size of the number emitted, and its byte order,
+depends on what kind of computer will run the program.
+_fi__(_GENERIC__)
+
+@c on amd29k, i960, sparc the "special treatment to support compilers" doesn't
+@c happen---32-bit addressability, period; no long/short jumps.
+_if__(_GENERIC__ || _DIFFTABKLUG__)
+@cindex difference tables altered
+@cindex altered difference tables
+@quotation
+@emph{Warning: Special Treatment to support Compilers}
+@end quotation
+
+_if__(_GENERIC__)
+Machines with a 32-bit address space, but that do less than 32-bit
+addressing, require the following special treatment. If the machine of
+interest to you does 32-bit addressing (or doesn't require it;
+@pxref{_MACH_DEP__}), you can ignore this issue.
+
+_fi__(_GENERIC__)
+In order to assemble compiler output into something that will work,
+@code{_AS__} will occasionlly do strange things to @samp{.word} directives.
+Directives of the form @samp{.word sym1-sym2} are often emitted by
+compilers as part of jump tables. Therefore, when @code{_AS__} assembles a
+directive of the form @samp{.word sym1-sym2}, and the difference between
+@code{sym1} and @code{sym2} does not fit in 16 bits, @code{_AS__} will
+create a @dfn{secondary jump table}, immediately before the next label.
+This secondary jump table will be preceded by a short-jump to the
+first byte after the secondary table. This short-jump prevents the flow
+of control from accidentally falling into the new table. Inside the
+table will be a long-jump to @code{sym2}. The original @samp{.word}
+will contain @code{sym1} minus the address of the long-jump to
+@code{sym2}.
+
+If there were several occurrences of @samp{.word sym1-sym2} before the
+secondary jump table, all of them will be adjusted. If there was a
+@samp{.word sym3-sym4}, that also did not fit in sixteen bits, a
+long-jump to @code{sym4} will be included in the secondary jump table,
+and the @code{.word} directives will be adjusted to contain @code{sym3}
+minus the address of the long-jump to @code{sym4}; and so on, for as many
+entries in the original jump table as necessary.
+
+_if__(_INTERNALS__)
+@emph{This feature may be disabled by compiling @code{_AS__} with the
+@samp{-DWORKING_DOT_WORD} option.} This feature is likely to confuse
+assembly language programmers.
+_fi__(_INTERNALS__)
+_fi__(_GENERIC__ || _DIFFTABKLUG__)
+
+@node Deprecated, , Word, Pseudo Ops
+@section Deprecated Directives
+
+@cindex deprecated directives
+@cindex obsolescent directives
+One day these directives won't work.
+They are included for compatibility with older assemblers.
+@table @t
+@item .abort
+@item .app-file
+@item .line
+@end table
+
+@node _MACH_DEP__, Copying, Pseudo Ops, Top
+_if__(_GENERIC__)
+@chapter Machine Dependent Features
+
+@cindex machine dependencies
+The machine instruction sets are (almost by definition) different on
+each machine where @code{_AS__} runs. Floating point representations
+vary as well, and @code{_AS__} often supports a few additional
+directives or command-line options for compatibility with other
+assemblers on a particular platform. Finally, some versions of
+@code{_AS__} support special pseudo-instructions for branch
+optimization.
+
+This chapter discusses most of these differences, though it does not
+include details on any machine's instruction set. For details on that
+subject, see the hardware manufacturer's manual.
+
+@menu
+_if__(_VAX__)
+* Vax-Dependent:: VAX Dependent Features
+_fi__(_VAX__)
+_if__(_A29K__)
+* AMD29K-Dependent:: AMD 29K Dependent Features
+_fi__(_A29K__)
+_if__(_H8__)
+* H8/300-Dependent:: AMD 29K Dependent Features
+_fi__(_H8__)
+_if__(_I960__)
+* i960-Dependent:: Intel 80960 Dependent Features
+_fi__(_I960__)
+_if__(_M680X0__)
+* M68K-Dependent:: M680x0 Dependent Features
+_fi__(_M680X0__)
+_if__(_SPARC__)
+* Sparc-Dependent:: SPARC Dependent Features
+_fi__(_SPARC__)
+_if__(_I80386__)
+* i386-Dependent:: 80386 Dependent Features
+_fi__(_I80386__)
+@end menu
+
+_fi__(_GENERIC__)
+_if__(_VAX__)
+_if__(_GENERIC__)
+@node Vax-Dependent, AMD29K-Dependent, Machine Dependent, Machine Dependent
+_fi__(_GENERIC__)
+_CHAPSEC__(0+_GENERIC__) VAX Dependent Features
+
+@cindex VAX support
+@menu
+* Vax-Opts:: VAX Command-Line Options
+* VAX-float:: VAX Floating Point
+* VAX-directives:: Vax Machine Directives
+* VAX-opcodes:: VAX Opcodes
+* VAX-branch:: VAX Branch Improvement
+* VAX-operands:: VAX Operands
+* VAX-no:: Not Supported on VAX
+@end menu
+
+@node Vax-Opts, VAX-float, Vax-Dependent, Vax-Dependent
+_CHAPSEC__(1+_GENERIC__) VAX Command-Line Options
+
+@cindex command-line options ignored, VAX
+@cindex VAX command-line options ignored
+The Vax version of @code{_AS__} accepts any of the following options,
+gives a warning message that the option was ignored and proceeds.
+These options are for compatibility with scripts designed for other
+people's assemblers.
+
+@table @asis
+@item @kbd{-D} (Debug)
+@itemx @kbd{-S} (Symbol Table)
+@itemx @kbd{-T} (Token Trace)
+@cindex @code{-D}, ignored on VAX
+@cindex @code{-S}, ignored on VAX
+@cindex @code{-T}, ignored on VAX
+These are obsolete options used to debug old assemblers.
+
+@item @kbd{-d} (Displacement size for JUMPs)
+@cindex @code{-d}, VAX option
+This option expects a number following the @kbd{-d}. Like options
+that expect filenames, the number may immediately follow the
+@kbd{-d} (old standard) or constitute the whole of the command line
+argument that follows @kbd{-d} (GNU standard).
+
+@item @kbd{-V} (Virtualize Interpass Temporary File)
+@cindex @code{-V}, redundant on VAX
+Some other assemblers use a temporary file. This option
+commanded them to keep the information in active memory rather
+than in a disk file. @code{_AS__} always does this, so this
+option is redundant.
+
+@item @kbd{-J} (JUMPify Longer Branches)
+@cindex @code{-J}, ignored on VAX
+Many 32-bit computers permit a variety of branch instructions
+to do the same job. Some of these instructions are short (and
+fast) but have a limited range; others are long (and slow) but
+can branch anywhere in virtual memory. Often there are 3
+flavors of branch: short, medium and long. Some other
+assemblers would emit short and medium branches, unless told by
+this option to emit short and long branches.
+
+@item @kbd{-t} (Temporary File Directory)
+@cindex @code{-t}, ignored on VAX
+Some other assemblers may use a temporary file, and this option
+takes a filename being the directory to site the temporary
+file. @code{_AS__} does not use a temporary disk file, so this
+option makes no difference. @kbd{-t} needs exactly one
+filename.
+@end table
+
+@cindex VMS (VAX) options
+@cindex options for VAX/VMS
+@cindex VAX/VMS options
+@cindex @code{-h} option, VAX/VMS
+@cindex @code{-+} option, VAX/VMS
+@cindex Vax-11 C compatibility
+@cindex symbols with lowercase, VAX/VMS
+@c FIXME! look into "I think" below, correct if needed, delete.
+The Vax version of the assembler accepts two options when
+compiled for VMS. They are @kbd{-h}, and @kbd{-+}. The
+@kbd{-h} option prevents @code{_AS__} from modifying the
+symbol-table entries for symbols that contain lowercase
+characters (I think). The @kbd{-+} option causes @code{_AS__} to
+print warning messages if the FILENAME part of the object file,
+or any symbol name is larger than 31 characters. The @kbd{-+}
+option also insertes some code following the @samp{_main}
+symbol so that the object file will be compatible with Vax-11
+"C".
+
+@node VAX-float, VAX-directives, Vax-Opts, Vax-Dependent
+_CHAPSEC__(1+_GENERIC__) VAX Floating Point
+
+@cindex VAX floating point
+@cindex floating point, VAX
+Conversion of flonums to floating point is correct, and
+compatible with previous assemblers. Rounding is
+towards zero if the remainder is exactly half the least significant bit.
+
+@code{D}, @code{F}, @code{G} and @code{H} floating point formats
+are understood.
+
+Immediate floating literals (@emph{e.g.} @samp{S`$6.9})
+are rendered correctly. Again, rounding is towards zero in the
+boundary case.
+
+@cindex @code{float} directive, VAX
+@cindex @code{double} directive, VAX
+The @code{.float} directive produces @code{f} format numbers.
+The @code{.double} directive produces @code{d} format numbers.
+
+@node VAX-directives, VAX-opcodes, VAX-float, Vax-Dependent
+_CHAPSEC__(1+_GENERIC__) Vax Machine Directives
+
+@cindex machine directives, VAX
+@cindex VAX machine directives
+The Vax version of the assembler supports four directives for
+generating Vax floating point constants. They are described in the
+table below.
+
+@cindex wide floating point directives, VAX
+@table @code
+@item .dfloat
+@cindex @code{dfloat} directive, VAX
+This expects zero or more flonums, separated by commas, and
+assembles Vax @code{d} format 64-bit floating point constants.
+
+@item .ffloat
+@cindex @code{ffloat} directive, VAX
+This expects zero or more flonums, separated by commas, and
+assembles Vax @code{f} format 32-bit floating point constants.
+
+@item .gfloat
+@cindex @code{gfloat} directive, VAX
+This expects zero or more flonums, separated by commas, and
+assembles Vax @code{g} format 64-bit floating point constants.
+
+@item .hfloat
+@cindex @code{hfloat} directive, VAX
+This expects zero or more flonums, separated by commas, and
+assembles Vax @code{h} format 128-bit floating point constants.
+
+@end table
+
+@node VAX-opcodes, VAX-branch, VAX-directives, Vax-Dependent
+_CHAPSEC__(1+_GENERIC__) VAX Opcodes
+
+@cindex VAX opcode mnemonics
+@cindex opcode mnemonics, VAX
+@cindex mnemonics for opcodes, VAX
+All DEC mnemonics are supported. Beware that @code{case@dots{}}
+instructions have exactly 3 operands. The dispatch table that
+follows the @code{case@dots{}} instruction should be made with
+@code{.word} statements. This is compatible with all unix
+assemblers we know of.
+
+@node VAX-branch, VAX-operands, VAX-opcodes, Vax-Dependent
+_CHAPSEC__(1+_GENERIC__) VAX Branch Improvement
+
+@cindex VAX branch improvement
+@cindex branch improvement, VAX
+@cindex pseudo-ops for branch, VAX
+Certain pseudo opcodes are permitted. They are for branch
+instructions. They expand to the shortest branch instruction that
+will reach the target. Generally these mnemonics are made by
+substituting @samp{j} for @samp{b} at the start of a DEC mnemonic.
+This feature is included both for compatibility and to help
+compilers. If you don't need this feature, don't use these
+opcodes. Here are the mnemonics, and the code they can expand into.
+
+@table @code
+@item jbsb
+@samp{Jsb} is already an instruction mnemonic, so we chose @samp{jbsb}.
+@table @asis
+@item (byte displacement)
+@kbd{bsbb @dots{}}
+@item (word displacement)
+@kbd{bsbw @dots{}}
+@item (long displacement)
+@kbd{jsb @dots{}}
+@end table
+@item jbr
+@itemx jr
+Unconditional branch.
+@table @asis
+@item (byte displacement)
+@kbd{brb @dots{}}
+@item (word displacement)
+@kbd{brw @dots{}}
+@item (long displacement)
+@kbd{jmp @dots{}}
+@end table
+@item j@var{COND}
+@var{COND} may be any one of the conditional branches
+@code{neq nequ eql eqlu gtr geq lss gtru lequ vc vs gequ cc lssu cs}.
+@var{COND} may also be one of the bit tests
+@code{bs bc bss bcs bsc bcc bssi bcci lbs lbc}.
+@var{NOTCOND} is the opposite condition to @var{COND}.
+@table @asis
+@item (byte displacement)
+@kbd{b@var{COND} @dots{}}
+@item (word displacement)
+@kbd{b@var{NOTCOND} foo ; brw @dots{} ; foo:}
+@item (long displacement)
+@kbd{b@var{NOTCOND} foo ; jmp @dots{} ; foo:}
+@end table
+@item jacb@var{X}
+@var{X} may be one of @code{b d f g h l w}.
+@table @asis
+@item (word displacement)
+@kbd{@var{OPCODE} @dots{}}
+@item (long displacement)
+@example
+@var{OPCODE} @dots{}, foo ;
+brb bar ;
+foo: jmp @dots{} ;
+bar:
+@end example
+@end table
+@item jaob@var{YYY}
+@var{YYY} may be one of @code{lss leq}.
+@item jsob@var{ZZZ}
+@var{ZZZ} may be one of @code{geq gtr}.
+@table @asis
+@item (byte displacement)
+@kbd{@var{OPCODE} @dots{}}
+@item (word displacement)
+@example
+@var{OPCODE} @dots{}, foo ;
+brb bar ;
+foo: brw @var{destination} ;
+bar:
+@end example
+@item (long displacement)
+@example
+@var{OPCODE} @dots{}, foo ;
+brb bar ;
+foo: jmp @var{destination} ;
+bar:
+@end example
+@end table
+@item aobleq
+@itemx aoblss
+@itemx sobgeq
+@itemx sobgtr
+@table @asis
+@item (byte displacement)
+@kbd{@var{OPCODE} @dots{}}
+@item (word displacement)
+@example
+@var{OPCODE} @dots{}, foo ;
+brb bar ;
+foo: brw @var{destination} ;
+bar:
+@end example
+@item (long displacement)
+@example
+@var{OPCODE} @dots{}, foo ;
+brb bar ;
+foo: jmp @var{destination} ;
+bar:
+@end example
+@end table
+@end table
+
+@node VAX-operands, VAX-no, VAX-branch, Vax-Dependent
+_CHAPSEC__(1+_GENERIC__) VAX Operands
+
+@cindex VAX operand notation
+@cindex operand notation, VAX
+@cindex immediate character, VAX
+@cindex VAX immediate character
+The immediate character is @samp{$} for Unix compatibility, not
+@samp{#} as DEC writes it.
+
+@cindex indirect character, VAX
+@cindex VAX indirect character
+The indirect character is @samp{*} for Unix compatibility, not
+@samp{@@} as DEC writes it.
+
+@cindex displacement sizing character, VAX
+@cindex VAX displacement sizing character
+The displacement sizing character is @samp{`} (an accent grave) for
+Unix compatibility, not @samp{^} as DEC writes it. The letter
+preceding @samp{`} may have either case. @samp{G} is not
+understood, but all other letters (@code{b i l s w}) are understood.
+
+@cindex register names, VAX
+@cindex VAX register names
+Register names understood are @code{r0 r1 r2 @dots{} r15 ap fp sp
+pc}. Any case of letters will do.
+
+For instance
+@smallexample
+tstb *w`$4(r5)
+@end smallexample
+
+Any expression is permitted in an operand. Operands are comma
+separated.
+
+@c There is some bug to do with recognizing expressions
+@c in operands, but I forget what it is. It is
+@c a syntax clash because () is used as an address mode
+@c and to encapsulate sub-expressions.
+
+@node VAX-no, , VAX-operands, Vax-Dependent
+_CHAPSEC__(1+_GENERIC__) Not Supported on VAX
+
+@cindex VAX bitfields not supported
+@cindex bitfields, not supported on VAX
+Vax bit fields can not be assembled with @code{_AS__}. Someone
+can add the required code if they really need it.
+
+_fi__(_VAX__)
+_if__(_A29K__)
+_if__(_GENERIC__)
+@node AMD29K-Dependent, H8/300-Dependent, Vax-Dependent, Machine Dependent
+_fi__(_GENERIC__)
+_CHAPSEC__(0+_GENERIC__) AMD 29K Dependent Features
+
+@cindex AMD 29K support
+@cindex 29K support
+@menu
+* AMD29K Options:: Options
+* AMD29K Syntax:: Syntax
+* AMD29K Floating Point:: Floating Point
+* AMD29K Directives:: AMD 29K Machine Directives
+* AMD29K Opcodes:: Opcodes
+@end menu
+
+@node AMD29K Options, AMD29K Syntax, AMD29K-Dependent, AMD29K-Dependent
+_CHAPSEC__(1+_GENERIC__) Options
+@cindex AMD 29K options (none)
+@cindex options for AMD29K (none)
+@code{_AS__} has no additional command-line options for the AMD
+29K family.
+
+@node AMD29K Syntax, AMD29K Floating Point, AMD29K Options, AMD29K-Dependent
+_CHAPSEC__(1+_GENERIC__) Syntax
+@menu
+* AMD29K-Chars:: Special Characters
+* AMD29K-Regs:: Register Names
+@end menu
+
+@node AMD29K-Chars, AMD29K-Regs, AMD29K Syntax, AMD29K Syntax
+_CHAPSEC__(2+_GENERIC__) Special Characters
+
+@cindex line comment character, AMD 29K
+@cindex AMD 29K line comment character
+@samp{;} is the line comment character.
+
+@cindex line separator, AMD 29K
+@cindex AMD 29K line separator
+@cindex statement separator, AMD 29K
+@cindex AMD 29K statement separator
+@samp{@@} can be used instead of a newline to separate statements.
+
+@cindex identifiers, AMD 29K
+@cindex AMD 29K identifiers
+The character @samp{?} is permitted in identifiers (but may not begin
+an identifier).
+
+@node AMD29K-Regs, , AMD29K-Chars, AMD29K Syntax
+_CHAPSEC__(2+_GENERIC__) Register Names
+
+@cindex AMD 29K register names
+@cindex register names, AMD 29K
+General-purpose registers are represented by predefined symbols of the
+form @samp{GR@var{nnn}} (for global registers) or @samp{LR@var{nnn}}
+(for local registers), where @var{nnn} represents a number between
+@code{0} and @code{127}, written with no leading zeros. The leading
+letters may be in either upper or lower case; for example, @samp{gr13}
+and @samp{LR7} are both valid register names.
+
+You may also refer to general-purpose registers by specifying the
+register number as the result of an expression (prefixed with @samp{%%}
+to flag the expression as a register number):
+@smallexample
+%%@var{expression}
+@end smallexample
+@noindent
+---where @var{expression} must be an absolute expression evaluating to a
+number between @code{0} and @code{255}. The range [0, 127] refers to
+global registers, and the range [128, 255] to local registers.
+
+@cindex special purpose registers, AMD 29K
+@cindex AMD 29K special purpose registers
+@cindex protected registers, AMD 29K
+@cindex AMD 29K protected registers
+In addition, @code{_AS__} understands the following protected
+special-purpose register names for the AMD 29K family:
+
+@smallexample
+ vab chd pc0
+ ops chc pc1
+ cps rbp pc2
+ cfg tmc mmu
+ cha tmr lru
+@end smallexample
+
+These unprotected special-purpose register names are also recognized:
+@smallexample
+ ipc alu fpe
+ ipa bp inte
+ ipb fc fps
+ q cr exop
+@end smallexample
+
+@node AMD29K Floating Point, AMD29K Directives, AMD29K Syntax, AMD29K-Dependent
+_CHAPSEC__(1+_GENERIC__) Floating Point
+
+@cindex floating point, AMD 29K (@sc{ieee})
+@cindex AMD 29K floating point (@sc{ieee})
+The AMD 29K family uses @sc{ieee} floating-point numbers.
+
+@node AMD29K Directives, AMD29K Opcodes, AMD29K Floating Point, AMD29K-Dependent
+_CHAPSEC__(1+_GENERIC__) AMD 29K Machine Directives
+
+@cindex machine directives, AMD 29K
+@cindex AMD 29K machine directives
+@table @code
+@item .block @var{size} , @var{fill}
+@cindex @code{block} directive, AMD 29K
+This directive emits @var{size} bytes, each of value @var{fill}. Both
+@var{size} and @var{fill} are absolute expressions. If the comma
+and @var{fill} are omitted, @var{fill} is assumed to be zero.
+
+In other versions of the GNU assembler, this directive is called
+@samp{.space}.
+@end table
+
+@table @code
+@item .cputype
+@cindex @code{cputype} directive, AMD 29K
+This directive is ignored; it is accepted for compatibility with other
+AMD 29K assemblers.
+
+@item .file
+@cindex @code{file} directive, AMD 29K
+This directive is ignored; it is accepted for compatibility with other
+AMD 29K assemblers.
+
+@quotation
+@emph{Warning:} in other versions of the GNU assembler, @code{.file} is
+used for the directive called @code{.app-file} in the AMD 29K support.
+@end quotation
+
+@item .line
+@cindex @code{line} directive, AMD 29K
+This directive is ignored; it is accepted for compatibility with other
+AMD 29K assemblers.
+
+@item .reg @var{symbol}, @var{expression}
+@cindex @code{reg} directive, AMD 29K
+@code{.reg} has the same effect as @code{.lsym}; @pxref{Lsym,,@code{.lsym}}.
+
+@item .sect
+@cindex @code{sect} directive, AMD 29K
+This directive is ignored; it is accepted for compatibility with other
+AMD 29K assemblers.
+
+@item .use @var{section name}
+@cindex @code{use} directive, AMD 29K
+Establishes the section and subsection for the following code;
+@var{section name} may be one of @code{.text}, @code{.data},
+@code{.data1}, or @code{.lit}. With one of the first three @var{section
+name} options, @samp{.use} is equivalent to the machine directive
+@var{section name}; the remaining case, @samp{.use .lit}, is the same as
+@samp{.data 200}.
+@end table
+
+@node AMD29K Opcodes, , AMD29K Directives, AMD29K-Dependent
+_CHAPSEC__(1+_GENERIC__) Opcodes
+
+@cindex AMD 29K opcodes
+@cindex opcodes for AMD 29K
+@code{_AS__} implements all the standard AMD 29K opcodes. No
+additional pseudo-instructions are needed on this family.
+
+For information on the 29K machine instruction set, see @cite{Am29000
+User's Manual}, Advanced Micro Devices, Inc.
+
+_fi__(_A29K__)
+_if__(_H8__)
+_if__(_GENERIC__)
+@node H8/300-Dependent, i960-Dependent, AMD29K-Dependent, Machine Dependent
+_fi__(_GENERIC__)
+_CHAPSEC__(0+_GENERIC__) H8/300 Dependent Features
+
+@cindex H8/300 support
+@menu
+* H8/300 Options:: Options
+* H8/300 Syntax:: Syntax
+* H8/300 Floating Point:: Floating Point
+* H8/300 Directives:: H8/300 Machine Directives
+* H8/300 Opcodes:: Opcodes
+@end menu
+
+@node H8/300 Options, H8/300 Syntax, H8/300-Dependent, H8/300-Dependent
+_CHAPSEC__(1+_GENERIC__) Options
+
+@cindex H8/300 options (none)
+@cindex options, H8/300 (none)
+@code{_AS__} has no additional command-line options for the Hitachi
+H8/300 family.
+
+@node H8/300 Syntax, H8/300 Floating Point, H8/300 Options, H8/300-Dependent
+_CHAPSEC__(1+_GENERIC__) Syntax
+@menu
+* H8/300-Chars:: Special Characters
+* H8/300-Regs:: Register Names
+* H8/300-Addressing:: Addressing Modes
+@end menu
+
+@node H8/300-Chars, H8/300-Regs, H8/300 Syntax, H8/300 Syntax
+_CHAPSEC__(2+_GENERIC__) Special Characters
+
+@cindex line comment character, H8/300
+@cindex H8/300 line comment character
+@samp{;} is the line comment character.
+
+@cindex line separator, H8/300
+@cindex statement separator, H8/300
+@cindex H8/300 line separator
+@samp{$} can be used instead of a newline to separate statements.
+Therefore @emph{you may not use @samp{$} in symbol names} on the H8/300.
+
+@node H8/300-Regs, H8/300-Addressing, H8/300-Chars, H8/300 Syntax
+_CHAPSEC__(2+_GENERIC__) Register Names
+
+@cindex H8/300 registers
+@cindex registers, H8/300
+You can use predefined symbols of the form @samp{r@var{n}h} and
+@samp{r@var{n}l} to refer to the H8/300 registers as sixteen 8-bit
+general-purpose registers. @var{n} is a digit from @samp{0} to
+@samp{7}); for instance, both @samp{r0h} and @samp{r7l} are valid
+register names.
+
+You can also use the eight predefined symbols @samp{r@var{n}} to refer
+to the H8/300 registers as 16-bit registers (you must use this form for
+addressing).
+
+The two control registers are called @code{pc} (program counter; a
+16-bit register) and @code{ccr} (condition code register; an 8-bit
+register). @code{r7} is used as the stack pointer, and can also be
+called @code{sp}.
+
+@node H8/300-Addressing, , H8/300-Regs, H8/300 Syntax
+_CHAPSEC__(2+_GENERIC__) Addressing Modes
+
+@cindex addressing modes, H8/300
+@cindex H8/300 addressing modes
+_AS__ understands the following addressing modes for the H8/300:
+@table @code
+@item r@var{n}
+Register direct
+
+@item @@r@var{n}
+Register indirect
+
+@item @@(@var{d}, r@var{n})
+@itemx @@(@var{d}:16, r@var{n})
+Register indirect: 16-bit displacement @var{d} from register @var{n}.
+(You may specify the @samp{:16} for clarity if you wish, but it is not
+required and has no effect.)
+
+@item @@r@var{n}+
+Register indirect with post-increment
+
+@item @@-r@var{n}
+Register indirect with pre-decrement
+
+@item @code{@@}@var{aa}
+@itemx @code{@@}@var{aa}:8
+@itemx @code{@@}@var{aa}:16
+Absolute address @code{aa}. You may specify the @samp{:8} or @samp{:16}
+for clarity, if you wish; but @code{_AS__} neither requires this nor
+uses it---the address size required is taken from context.
+
+@item #@var{xx}
+@itemx #@var{xx}:8
+@itemx #@var{xx}:16
+Immediate data @var{xx}. You may specify the @samp{:8} or @samp{:16}
+for clarity, if you wish; but @code{_AS__} neither requires this nor
+uses it---the data size required is taken from context.
+
+@item @code{@@}@code{@@}@var{aa}
+@itemx @code{@@}@code{@@}@var{aa}:8
+Memory indirect. You may specify the @samp{:8} for clarity, if you
+wish; but @code{_AS__} neither requires this nor uses it.
+@end table
+
+@node H8/300 Floating Point, H8/300 Directives, H8/300 Syntax, H8/300-Dependent
+_CHAPSEC__(1+_GENERIC__) Floating Point
+
+@cindex floating point, H8/300 (@sc{ieee})
+@cindex H8/300 floating point (@sc{ieee})
+The H8/300 family uses @sc{ieee} floating-point numbers.
+
+@node H8/300 Directives, H8/300 Opcodes, H8/300 Floating Point, H8/300-Dependent
+_CHAPSEC__(1+_GENERIC__) H8/300 Machine Directives
+
+@cindex H8/300 machine directives (none)
+@cindex machine directives, H8/300 (none)
+@cindex @code{word} directive, H8/300
+@cindex @code{int} directive, H8/300
+@code{_AS__} has no machine-dependent directives for the H8/300.
+However, on this platform the @samp{.int} and @samp{.word} directives
+generate 16-bit numbers.
+
+@node H8/300 Opcodes, , H8/300 Directives, H8/300-Dependent
+_CHAPSEC__(1+_GENERIC__) Opcodes
+
+@cindex H8/300 opcode summary
+@cindex opcode summary, H8/300
+@cindex mnemonics, H8/300
+@cindex instruction summary, H8/300
+For detailed information on the H8/300 machine instruction set, see
+@cite{H8/300 Series Programming Manual} (Hitachi ADE--602--025).
+
+@code{_AS__} implements all the standard H8/300 opcodes. No additional
+pseudo-instructions are needed on this family.
+
+The following table summarizes the opcodes and their arguments:
+@c kluge due to lack of group outside example
+@page
+@smallexample
+@group
+ Rs @r{source register}
+ Rd @r{destination register}
+ imm @r{immediate data}
+ x:3 @r{a bit (as a number between 0 and 7)}
+ d:8 @r{eight bit displacement from @code{pc}}
+ d:16 @r{sixteen bit displacement from @code{Rs}}
+
+add.b Rs,Rd biand #x:3,Rd
+add.b #imm:8,Rd biand #x:3,@@Rd
+add.w Rs,Rd biand #x:3,@@aa:8
+adds #1,Rd bild #x:3,Rd
+adds #2,Rd bild #x:3,@@Rd
+addx #imm:8,Rd bild #x:3,@@aa:8
+addx Rs,Rd bior #x:3,Rd
+and #imm:8,Rd bior #x:3,@@Rd
+and Rs,Rd bior #x:3,@@aa:8
+andc #imm:8,ccr bist #x:3,Rd
+band #x:3,Rd bist #x:3,@@Rd
+band #x:3,@@Rd bist #x:3,@@aa:8
+bra d:8 bixor #x:3,Rd
+bt d:8 bixor #x:3,@@Rd
+brn d:8 bixor #x:3,@@aa:8
+bf d:8 bld #x:3,Rd
+bhi d:8 bld #x:3,@@Rd
+bls d:8 bld #x:3,@@aa:8
+bcc d:8 bnot #x:3,Rd
+bhs d:8 bnot #x:3,@@Rd
+bcs d:8 bnot #x:3,@@aa:8
+blo d:8 bnot Rs,Rd
+bne d:8 bnot Rs,@@Rd
+beq d:8 bnot Rs,@@aa:8
+bvc d:8 bor #x:3,Rd
+bvs d:8 bor #x:3,@@Rd
+bpl d:8 bor #x:3,@@aa:8
+bmi d:8 bset #x:3,@@Rd
+bge d:8 bset #x:3,@@aa:8
+blt d:8 bset Rs,Rd
+bgt d:8 bset Rs,@@Rd
+ble d:8 bset Rs,@@aa:8
+bclr #x:3,Rd bsr d:8
+bclr #x:3,@@Rd bst #x:3,Rd
+bclr #x:3,@@aa:8 bst #x:3,@@Rd
+bclr Rs,Rd bst #x:3,@@aa:8
+bclr Rs,@@Rd btst #x:3,Rd
+@end group
+@group
+btst #x:3,@@Rd mov.w @@(d:16, Rs),Rd
+btst #x:3,@@aa:8 mov.w @@Rs+,Rd
+btst Rs,Rd mov.w @@aa:16,Rd
+btst Rs,@@Rd mov.w Rs,@@Rd
+btst Rs,@@aa:8 mov.w Rs,@@(d:16, Rd)
+bxor #x:3,Rd mov.w Rs,@@-Rd
+bxor #x:3,@@Rd mov.w Rs,@@aa:16
+bxor #x:3,@@aa:8 movfpe @@aa:16,Rd
+cmp.b #imm:8,Rd movtpe Rs,@@aa:16
+cmp.b Rs,Rd mulxu Rs,Rd
+cmp.w Rs,Rd neg Rs
+daa Rs nop
+das Rs not Rs
+dec Rs or #imm:8,Rd
+divxu Rs,Rd or Rs,Rd
+eepmov orc #imm:8,ccr
+inc Rs pop Rs
+jmp @@Rs push Rs
+jmp @@aa:16 rotl Rs
+jmp @@@@aa rotr Rs
+jsr @@Rs rotxl Rs
+jsr @@aa:16 rotxr Rs
+jsr @@@@aa:8 rte
+ldc #imm:8,ccr rts
+ldc Rs,ccr shal Rs
+mov.b Rs,Rd shar Rs
+mov.b #imm:8,Rd shll Rs
+mov.b @@Rs,Rd shlr Rs
+mov.b @@(d:16, Rs),Rd sleep
+mov.b @@Rs+,Rd stc ccr,Rd
+mov.b @@aa:16,Rd sub.b Rs,Rd
+mov.b @@aa:8,Rd sub.w Rs,Rd
+mov.b Rs,@@Rd subs #1,Rd
+mov.b Rs,@@(d:16, Rd) subs #2,Rd
+mov.b Rs,@@-Rd subx #imm:8,Rd
+mov.b Rs,@@aa:16 subx Rs,Rd
+mov.b Rs,@@aa:8 xor #imm:8,Rd
+mov.w Rs,Rd xor Rs,Rd
+mov.w #imm:16,Rd xorc #imm:8,ccr
+mov.w @@Rs,Rd
+@end group
+@end smallexample
+
+@cindex size suffixes, H8/300
+@cindex H8/300 size suffixes
+Four H8/300 instructions (@code{add}, @code{cmp}, @code{mov},
+@code{sub}) are defined with variants using the suffixes @samp{.b} and
+@samp{.w} to specify the size of a memory operand. @code{_AS__}
+supports these suffixes, but does not require them; since one of the
+operands is always a register, @code{_AS__} can deduce the correct size.
+
+For example, since @code{r0} refers to a 16-bit register,
+@example
+mov r0,@@foo
+@exdent is equivalent to
+mov.w r0,@@foo
+@end example
+
+If you use the size suffixes, @code{_AS__} will issue a warning if
+there's a mismatch between the suffix and the register size.
+
+_fi__(_H8__)
+_if__(_I960__)
+_if__(_GENERIC__)
+@node i960-Dependent, M68K-Dependent, H8/300-Dependent, Machine Dependent
+_fi__(_GENERIC__)
+_CHAPSEC__(0+_GENERIC__) Intel 80960 Dependent Features
+
+@cindex i960 support
+@menu
+* Options-i960:: i960 Command-line Options
+* Floating Point-i960:: Floating Point
+* Directives-i960:: i960 Machine Directives
+* Opcodes for i960:: i960 Opcodes
+@end menu
+
+@c FIXME! Add Syntax sec with discussion of bitfields here, at least so
+@c long as they're not turned on for other machines than 960.
+@node Options-i960, Floating Point-i960, i960-Dependent, i960-Dependent
+
+_CHAPSEC__(1+_GENERIC__) i960 Command-line Options
+
+@cindex i960 options
+@cindex options, i960
+@table @code
+
+@item -ACA | -ACA_A | -ACB | -ACC | -AKA | -AKB | -AKC | -AMC
+@cindex i960 architecture options
+@cindex architecture options, i960
+@cindex @code{-A} options, i960
+Select the 80960 architecture. Instructions or features not supported
+by the selected architecture cause fatal errors.
+
+@samp{-ACA} is equivalent to @samp{-ACA_A}; @samp{-AKC} is equivalent to
+@samp{-AMC}. Synonyms are provided for compatibility with other tools.
+
+If none of these options is specified, @code{_AS__} will generate code for any
+instruction or feature that is supported by @emph{some} version of the
+960 (even if this means mixing architectures!). In principle,
+@code{_AS__} will attempt to deduce the minimal sufficient processor
+type if none is specified; depending on the object code format, the
+processor type may be recorded in the object file. If it is critical
+that the @code{_AS__} output match a specific architecture, specify that
+architecture explicitly.
+
+@item -b
+@cindex @code{-b} option, i960
+@cindex branch recording, i960
+@cindex i960 branch recording
+Add code to collect information about conditional branches taken, for
+later optimization using branch prediction bits. (The conditional branch
+instructions have branch prediction bits in the CA, CB, and CC
+architectures.) If @var{BR} represents a conditional branch instruction,
+the following represents the code generated by the assembler when
+@samp{-b} is specified:
+
+@smallexample
+ call @var{increment routine}
+ .word 0 # pre-counter
+Label: @var{BR}
+ call @var{increment routine}
+ .word 0 # post-counter
+@end smallexample
+
+The counter following a branch records the number of times that branch
+was @emph{not} taken; the differenc between the two counters is the
+number of times the branch @emph{was} taken.
+
+@cindex @code{gbr960}, i960 postprocessor
+@cindex branch statistics table, i960
+A table of every such @code{Label} is also generated, so that the
+external postprocessor @code{gbr960} (supplied by Intel) can locate all
+the counters. This table is always labelled @samp{__BRANCH_TABLE__};
+this is a local symbol to permit collecting statistics for many separate
+object files. The table is word aligned, and begins with a two-word
+header. The first word, initialized to 0, is used in maintaining linked
+lists of branch tables. The second word is a count of the number of
+entries in the table, which follow immediately: each is a word, pointing
+to one of the labels illustrated above.
+
+@c TEXI2ROFF-KILL
+@ifinfo
+@c END TEXI2ROFF-KILL
+@example
+ +------------+------------+------------+ ... +------------+
+ | | | | | |
+ | *NEXT | COUNT: N | *BRLAB 1 | | *BRLAB N |
+ | | | | | |
+ +------------+------------+------------+ ... +------------+
+
+ __BRANCH_TABLE__ layout
+@end example
+@c TEXI2ROFF-KILL
+@end ifinfo
+@tex
+\vskip 1pc
+\line{\leftskip=0pt\hskip\tableindent
+\boxit{2cm}{\tt *NEXT}\boxit{2cm}{\tt COUNT: \it N}\boxit{2cm}{\tt
+*BRLAB 1}\ibox{1cm}{\quad\dots}\boxit{2cm}{\tt *BRLAB \it N}\hfil}
+\centerline{\it {\tt \_\_BRANCH\_TABLE\_\_} layout}
+@end tex
+@c END TEXI2ROFF-KILL
+
+The first word of the header is used to locate multiple branch tables,
+since each object file may contain one. Normally the links are
+maintained with a call to an initialization routine, placed at the
+beginning of each function in the file. The GNU C compiler will
+generate these calls automatically when you give it a @samp{-b} option.
+For further details, see the documentation of @samp{gbr960}.
+
+@item -norelax
+@cindex @code{-norelax} option, i960
+Normally, Compare-and-Branch instructions with targets that require
+displacements greater than 13 bits (or that have external targets) are
+replaced with the corresponding compare (or @samp{chkbit}) and branch
+instructions. You can use the @samp{-norelax} option to specify that
+@code{_AS__} should generate errors instead, if the target displacement
+is larger than 13 bits.
+
+This option does not affect the Compare-and-Jump instructions; the code
+emitted for them is @emph{always} adjusted when necessary (depending on
+displacement size), regardless of whether you use @samp{-norelax}.
+@end table
+
+@node Floating Point-i960, Directives-i960, Options-i960, i960-Dependent
+_CHAPSEC__(1+_GENERIC__) Floating Point
+
+@cindex floating point, i960 (@sc{ieee})
+@cindex i960 floating point (@sc{ieee})
+@code{_AS__} generates @sc{ieee} floating-point numbers for the directives
+@samp{.float}, @samp{.double}, @samp{.extended}, and @samp{.single}.
+
+@node Directives-i960, Opcodes for i960, Floating Point-i960, i960-Dependent
+_CHAPSEC__(1+_GENERIC__) i960 Machine Directives
+
+@cindex machine directives, i960
+@cindex i960 machine directives
+
+@table @code
+@cindex @code{bss} directive, i960
+@item .bss @var{symbol}, @var{length}, @var{align}
+Reserve @var{length} bytes in the bss section for a local @var{symbol},
+aligned to the power of two specified by @var{align}. @var{length} and
+@var{align} must be positive absolute expressions. This directive
+differs from @samp{.lcomm} only in that it permits you to specify
+an alignment. @xref{Lcomm,,@code{.lcomm}}.
+@end table
+
+@table @code
+@item .extended @var{flonums}
+@cindex @code{extended} directive, i960
+@code{.extended} expects zero or more flonums, separated by commas; for
+each flonum, @samp{.extended} emits an @sc{ieee} extended-format (80-bit)
+floating-point number.
+
+@item .leafproc @var{call-lab}, @var{bal-lab}
+@cindex @code{leafproc} directive, i960
+You can use the @samp{.leafproc} directive in conjunction with the
+optimized @code{callj} instruction to enable faster calls of leaf
+procedures. If a procedure is known to call no other procedures, you
+may define an entry point that skips procedure prolog code (and that does
+not depend on system-supplied saved context), and declare it as the
+@var{bal-lab} using @samp{.leafproc}. If the procedure also has an
+entry point that goes through the normal prolog, you can specify that
+entry point as @var{call-lab}.
+
+A @samp{.leafproc} declaration is meant for use in conjunction with the
+optimized call instruction @samp{callj}; the directive records the data
+needed later to choose between converting the @samp{callj} into a
+@code{bal} or a @code{call}.
+
+@var{call-lab} is optional; if only one argument is present, or if the
+two arguments are identical, the single argument is assumed to be the
+@code{bal} entry point.
+
+@item .sysproc @var{name}, @var{index}
+@cindex @code{sysproc} directive, i960
+The @samp{.sysproc} directive defines a name for a system procedure.
+After you define it using @samp{.sysproc}, you can use @var{name} to
+refer to the system procedure identified by @var{index} when calling
+procedures with the optimized call instruction @samp{callj}.
+
+Both arguments are required; @var{index} must be between 0 and 31
+(inclusive).
+@end table
+
+@node Opcodes for i960, , Directives-i960, i960-Dependent
+_CHAPSEC__(1+_GENERIC__) i960 Opcodes
+
+@cindex opcodes, i960
+@cindex i960 opcodes
+All Intel 960 machine instructions are supported;
+@pxref{Options-i960,,i960 Command-line Options} for a discussion of
+selecting the instruction subset for a particular 960
+architecture.@refill
+
+Some opcodes are processed beyond simply emitting a single corresponding
+instruction: @samp{callj}, and Compare-and-Branch or Compare-and-Jump
+instructions with target displacements larger than 13 bits.
+
+@menu
+* callj-i960:: @code{callj}
+* Compare-and-branch-i960:: Compare-and-Branch
+@end menu
+
+@node callj-i960, Compare-and-branch-i960, Opcodes for i960, Opcodes for i960
+_CHAPSEC__(2+_GENERIC__) @code{callj}
+
+@cindex @code{callj}, i960 pseudo-opcode
+@cindex i960 @code{callj} pseudo-opcode
+You can write @code{callj} to have the assembler or the linker determine
+the most appropriate form of subroutine call: @samp{call},
+@samp{bal}, or @samp{calls}. If the assembly source contains
+enough information---a @samp{.leafproc} or @samp{.sysproc} directive
+defining the operand---then @code{_AS__} will translate the
+@code{callj}; if not, it will simply emit the @code{callj}, leaving it
+for the linker to resolve.
+
+@node Compare-and-branch-i960, , callj-i960, Opcodes for i960
+_CHAPSEC__(2+_GENERIC__) Compare-and-Branch
+
+@cindex i960 compare and branch instructions
+@cindex compare and branch instructions, i960
+The 960 architectures provide combined Compare-and-Branch instructions
+that permit you to store the branch target in the lower 13 bits of the
+instruction word itself. However, if you specify a branch target far
+enough away that its address won't fit in 13 bits, the assembler can
+either issue an error, or convert your Compare-and-Branch instruction
+into separate instructions to do the compare and the branch.
+
+@cindex compare and jump expansions, i960
+@cindex i960 compare and jump expansions
+Whether @code{_AS__} gives an error or expands the instruction depends
+on two choices you can make: whether you use the @samp{-norelax} option,
+and whether you use a ``Compare and Branch'' instruction or a ``Compare
+and Jump'' instruction. The ``Jump'' instructions are @emph{always}
+expanded if necessary; the ``Branch'' instructions are expanded when
+necessary @emph{unless} you specify @code{-norelax}---in which case
+@code{_AS__} gives an error instead.
+
+These are the Compare-and-Branch instructions, their ``Jump'' variants,
+and the instruction pairs they may expand into:
+
+@c TEXI2ROFF-KILL
+@ifinfo
+@c END TEXI2ROFF-KILL
+@example
+ Compare and
+ Branch Jump Expanded to
+ ------ ------ ------------
+ bbc chkbit; bno
+ bbs chkbit; bo
+ cmpibe cmpije cmpi; be
+ cmpibg cmpijg cmpi; bg
+ cmpibge cmpijge cmpi; bge
+ cmpibl cmpijl cmpi; bl
+ cmpible cmpijle cmpi; ble
+ cmpibno cmpijno cmpi; bno
+ cmpibne cmpijne cmpi; bne
+ cmpibo cmpijo cmpi; bo
+ cmpobe cmpoje cmpo; be
+ cmpobg cmpojg cmpo; bg
+ cmpobge cmpojge cmpo; bge
+ cmpobl cmpojl cmpo; bl
+ cmpoble cmpojle cmpo; ble
+ cmpobne cmpojne cmpo; bne
+@end example
+@c TEXI2ROFF-KILL
+@end ifinfo
+@tex
+\hskip\tableindent
+\halign{\hfil {\tt #}\quad&\hfil {\tt #}\qquad&{\tt #}\hfil\cr
+\omit{\hfil\it Compare and\hfil}\span\omit&\cr
+{\it Branch}&{\it Jump}&{\it Expanded to}\cr
+ bbc& & chkbit; bno\cr
+ bbs& & chkbit; bo\cr
+ cmpibe& cmpije& cmpi; be\cr
+ cmpibg& cmpijg& cmpi; bg\cr
+ cmpibge& cmpijge& cmpi; bge\cr
+ cmpibl& cmpijl& cmpi; bl\cr
+ cmpible& cmpijle& cmpi; ble\cr
+ cmpibno& cmpijno& cmpi; bno\cr
+ cmpibne& cmpijne& cmpi; bne\cr
+ cmpibo& cmpijo& cmpi; bo\cr
+ cmpobe& cmpoje& cmpo; be\cr
+ cmpobg& cmpojg& cmpo; bg\cr
+ cmpobge& cmpojge& cmpo; bge\cr
+ cmpobl& cmpojl& cmpo; bl\cr
+ cmpoble& cmpojle& cmpo; ble\cr
+ cmpobne& cmpojne& cmpo; bne\cr}
+@end tex
+@c END TEXI2ROFF-KILL
+_fi__(_I960__)
+
+_if__(_M680X0__)
+_if__(_GENERIC__)
+@c FIXME! node conds are only sufficient for m68k alone, all, and vintage
+_if__(_I960__)
+@node M68K-Dependent, Sparc-Dependent, i960-Dependent, Machine Dependent
+_fi__(_I960__)
+_if__(!_I960__)
+@node M68K-Dependent, Sparc-Dependent, Machine Dependent, Machine Dependent
+_fi__(!_I960__)
+_fi__(_GENERIC__)
+_CHAPSEC__(0+_GENERIC__) M680x0 Dependent Features
+
+@cindex M680x0 support
+@menu
+* M68K-Opts:: M680x0 Options
+* M68K-Syntax:: Syntax
+* M68K-Float:: Floating Point
+* M68K-Directives:: 680x0 Machine Directives
+* M68K-opcodes:: Opcodes
+@end menu
+
+@node M68K-Opts, M68K-Syntax, M68K-Dependent, M68K-Dependent
+_CHAPSEC__(1+_GENERIC__) M680x0 Options
+
+@cindex options, M680x0
+@cindex M680x0 options
+The Motorola 680x0 version of @code{_AS__} has two machine dependent options.
+One shortens undefined references from 32 to 16 bits, while the
+other is used to tell @code{_AS__} what kind of machine it is
+assembling for.
+
+@cindex @code{-l} option, M680x0
+You can use the @kbd{-l} option to shorten the size of references to
+undefined symbols. If the @kbd{-l} option is not given, references to
+undefined symbols will be a full long (32 bits) wide. (Since @code{_AS__}
+cannot know where these symbols will end up, @code{_AS__} can only allocate
+space for the linker to fill in later. Since @code{_AS__} doesn't know how
+far away these symbols will be, it allocates as much space as it can.)
+If this option is given, the references will only be one word wide (16
+bits). This may be useful if you want the object file to be as small as
+possible, and you know that the relevant symbols will be less than 17
+bits away.
+
+@cindex @code{-m68000} and related options, M680x0
+@cindex architecture options, M680x0
+@cindex M680x0 architecture options
+The 680x0 version of @code{_AS__} is most frequently used to assemble
+programs for the Motorola MC68020 microprocessor. Occasionally it is
+used to assemble programs for the mostly similar, but slightly different
+MC68000 or MC68010 microprocessors. You can give @code{_AS__} the options
+@samp{-m68000}, @samp{-mc68000}, @samp{-m68010}, @samp{-mc68010},
+@samp{-m68020}, and @samp{-mc68020} to tell it what processor is the
+target.
+
+@node M68K-Syntax, M68K-Float, M68K-Opts, M68K-Dependent
+_CHAPSEC__(1+_GENERIC__) Syntax
+
+@cindex M680x0 syntax
+@cindex syntax, M680x0
+@cindex M680x0 size modifiers
+@cindex size modifiers, M680x0
+The 680x0 version of @code{_AS__} uses syntax similar to the Sun assembler.
+Size modifiers are appended directly to the end of the opcode without an
+intervening period. For example, write @samp{movl} rather than
+@samp{move.l}.
+
+_if__(_INTERNALS__)
+If @code{_AS__} is compiled with SUN_ASM_SYNTAX defined, it will also allow
+Sun-style local labels of the form @samp{1$} through @samp{$9}.
+_fi__(_INTERNALS__)
+
+In the following table @dfn{apc} stands for any of the address
+registers (@samp{a0} through @samp{a7}), nothing, (@samp{}), the
+Program Counter (@samp{pc}), or the zero-address relative to the
+program counter (@samp{zpc}).
+
+@cindex M680x0 addressing modes
+@cindex addressing modes, M680x0
+The following addressing modes are understood:
+@table @dfn
+@item Immediate
+@samp{#@var{digits}}
+
+@item Data Register
+@samp{d0} through @samp{d7}
+
+@item Address Register
+@samp{a0} through @samp{a7}
+
+@item Address Register Indirect
+@samp{a0@@} through @samp{a7@@}
+
+@item Address Register Postincrement
+@samp{a0@@+} through @samp{a7@@+}
+
+@item Address Register Predecrement
+@samp{a0@@-} through @samp{a7@@-}
+
+@item Indirect Plus Offset
+@samp{@var{apc}@@(@var{digits})}
+
+@item Index
+@samp{@var{apc}@@(@var{digits},@var{register}:@var{size}:@var{scale})}
+
+or @samp{@var{apc}@@(@var{register}:@var{size}:@var{scale})}
+
+@item Postindex
+@samp{@var{apc}@@(@var{digits})@@(@var{digits},@var{register}:@var{size}:@var{scale})}
+
+or @samp{@var{apc}@@(@var{digits})@@(@var{register}:@var{size}:@var{scale})}
+
+@item Preindex
+@samp{@var{apc}@@(@var{digits},@var{register}:@var{size}:@var{scale})@@(@var{digits})}
+
+or @samp{@var{apc}@@(@var{register}:@var{size}:@var{scale})@@(@var{digits})}
+
+@item Memory Indirect
+@samp{@var{apc}@@(@var{digits})@@(@var{digits})}
+
+@item Absolute
+@samp{@var{symbol}}, or @samp{@var{digits}}
+@ignore
+@c pesch@cygnus.com: gnu, rich concur the following needs careful
+@c research before documenting.
+ , or either of the above followed
+by @samp{:b}, @samp{:w}, or @samp{:l}.
+@end ignore
+@end table
+
+@node M68K-Float, M68K-Directives, M68K-Syntax, M68K-Dependent
+_CHAPSEC__(1+_GENERIC__) Floating Point
+
+@cindex floating point, M680x0
+@cindex M680x0 floating point
+@c FIXME is this "not too well tested" crud STILL true?
+The floating point code is not too well tested, and may have
+subtle bugs in it.
+
+Packed decimal (P) format floating literals are not supported.
+Feel free to add the code!
+
+The floating point formats generated by directives are these.
+
+@table @code
+@item .float
+@cindex @code{float} directive, M680x0
+@code{Single} precision floating point constants.
+
+@item .double
+@cindex @code{double} directive, M680x0
+@code{Double} precision floating point constants.
+@end table
+
+There is no directive to produce regions of memory holding
+extended precision numbers, however they can be used as
+immediate operands to floating-point instructions. Adding a
+directive to create extended precision numbers would not be
+hard, but it has not yet seemed necessary.
+
+@node M68K-Directives, M68K-opcodes, M68K-Float, M68K-Dependent
+_CHAPSEC__(1+_GENERIC__) 680x0 Machine Directives
+
+@cindex M680x0 directives
+@cindex directives, M680x0
+In order to be compatible with the Sun assembler the 680x0 assembler
+understands the following directives.
+
+@table @code
+@item .data1
+@cindex @code{data1} directive, M680x0
+This directive is identical to a @code{.data 1} directive.
+
+@item .data2
+@cindex @code{data2} directive, M680x0
+This directive is identical to a @code{.data 2} directive.
+
+@item .even
+@cindex @code{even} directive, M680x0
+This directive is identical to a @code{.align 1} directive.
+@c Is this true? does it work???
+
+@item .skip
+@cindex @code{skip} directive, M680x0
+This directive is identical to a @code{.space} directive.
+@end table
+
+@node M68K-opcodes, , M68K-Directives, M68K-Dependent
+_CHAPSEC__(1+_GENERIC__) Opcodes
+
+@cindex M680x0 opcodes
+@cindex opcodes, M680x0
+@cindex instruction set, M680x0
+@c pesch@cygnus.com: I don't see any point in the following
+@c paragraph. Bugs are bugs; how does saying this
+@c help anyone?
+@ignore
+Danger: Several bugs have been found in the opcode table (and
+fixed). More bugs may exist. Be careful when using obscure
+instructions.
+@end ignore
+
+@menu
+* M68K-Branch:: Branch Improvement
+* M68K-Chars:: Special Characters
+@end menu
+
+@node M68K-Branch, M68K-Chars, M68K-opcodes, M68K-opcodes
+_CHAPSEC__(2+_GENERIC__) Branch Improvement
+
+@cindex pseudo-opcodes, M680x0
+@cindex M680x0 pseudo-opcodes
+@cindex branch improvement, M680x0
+@cindex M680x0 branch improvement
+Certain pseudo opcodes are permitted for branch instructions.
+They expand to the shortest branch instruction that will reach the
+target. Generally these mnemonics are made by substituting @samp{j} for
+@samp{b} at the start of a Motorola mnemonic.
+
+The following table summarizes the pseudo-operations. A @code{*} flags
+cases that are more fully described after the table:
+
+@smallexample
+ Displacement
+ +---------------------------------------------------------
+ | 68020 68000/10
+Pseudo-Op |BYTE WORD LONG LONG non-PC relative
+ +---------------------------------------------------------
+ jbsr |bsrs bsr bsrl jsr jsr
+ jra |bras bra bral jmp jmp
+* jXX |bXXs bXX bXXl bNXs;jmpl bNXs;jmp
+* dbXX |dbXX dbXX dbXX; bra; jmpl
+* fjXX |fbXXw fbXXw fbXXl fbNXw;jmp
+
+XX: condition
+NX: negative of condition XX
+
+@end smallexample
+@center @code{*}---see full description below
+
+@table @code
+@item jbsr
+@itemx jra
+These are the simplest jump pseudo-operations; they always map to one
+particular machine instruction, depending on the displacement to the
+branch target.
+
+@item j@var{XX}
+Here, @samp{j@var{XX}} stands for an entire family of pseudo-operations,
+where @var{XX} is a conditional branch or condition-code test. The full
+list of pseudo-ops in this family is:
+@smallexample
+ jhi jls jcc jcs jne jeq jvc
+ jvs jpl jmi jge jlt jgt jle
+@end smallexample
+
+For the cases of non-PC relative displacements and long displacements on
+the 68000 or 68010, @code{_AS__} will issue a longer code fragment in terms of
+@var{NX}, the opposite condition to @var{XX}:
+@smallexample
+ j@var{XX} foo
+@end smallexample
+gives
+@smallexample
+ b@var{NX}s oof
+ jmp foo
+ oof:
+@end smallexample
+
+@item db@var{XX}
+The full family of pseudo-operations covered here is
+@smallexample
+ dbhi dbls dbcc dbcs dbne dbeq dbvc
+ dbvs dbpl dbmi dbge dblt dbgt dble
+ dbf dbra dbt
+@end smallexample
+
+Other than for word and byte displacements, when the source reads
+@samp{db@var{XX} foo}, @code{_AS__} will emit
+@smallexample
+ db@var{XX} oo1
+ bra oo2
+ oo1:jmpl foo
+ oo2:
+@end smallexample
+
+@item fj@var{XX}
+This family includes
+@smallexample
+ fjne fjeq fjge fjlt fjgt fjle fjf
+ fjt fjgl fjgle fjnge fjngl fjngle fjngt
+ fjnle fjnlt fjoge fjogl fjogt fjole fjolt
+ fjor fjseq fjsf fjsne fjst fjueq fjuge
+ fjugt fjule fjult fjun
+@end smallexample
+
+For branch targets that are not PC relative, @code{_AS__} emits
+@smallexample
+ fb@var{NX} oof
+ jmp foo
+ oof:
+@end smallexample
+when it encounters @samp{fj@var{XX} foo}.
+
+@end table
+
+@node M68K-Chars, , M68K-Branch, M68K-opcodes
+_CHAPSEC__(2+_GENERIC__) Special Characters
+
+@cindex special characters, M680x0
+@cindex M680x0 immediate character
+@cindex immediate character, M680x0
+@cindex M680x0 line comment character
+@cindex line comment character, M680x0
+@cindex comments, M680x0
+The immediate character is @samp{#} for Sun compatibility. The
+line-comment character is @samp{|}. If a @samp{#} appears at the
+beginning of a line, it is treated as a comment unless it looks like
+@samp{# line file}, in which case it is treated normally.
+
+_fi__(_M680X0__)
+_if__(0)
+@c pesch@cygnus.com: conditionalize on something other than 0 when filled in.
+@section 32x32
+@section Options
+The 32x32 version of @code{_AS__} accepts a @kbd{-m32032} option to
+specify thiat it is compiling for a 32032 processor, or a
+@kbd{-m32532} to specify that it is compiling for a 32532 option.
+The default (if neither is specified) is chosen when the assembler
+is compiled.
+
+@subsection Syntax
+I don't know anything about the 32x32 syntax assembled by
+@code{_AS__}. Someone who undersands the processor (I've never seen
+one) and the possible syntaxes should write this section.
+
+@subsection Floating Point
+The 32x32 uses @sc{ieee} floating point numbers, but @code{_AS__} will only
+create single or double precision values. I don't know if the 32x32
+understands extended precision numbers.
+
+@subsection 32x32 Machine Directives
+The 32x32 has no machine dependent directives.
+
+_fi__(0)
+_if__(_SPARC__)
+_if__(_GENERIC__)
+_if__(_I80386__&&_M680X0__)
+@node Sparc-Dependent, i386-Dependent, M68K-Dependent, Machine Dependent
+_fi__(_I80386__&&_M680X0__)
+_if__(_I80386__&&_I960__&&!_M680X0__)
+@node Sparc-Dependent, i386-Dependent, i960-Dependent, Machine Dependent
+_fi__(_I80386__&&_I960__&&!_M680X0__)
+_if__(_I80386__&&_A29K__&&(!_I960__)&&!_M680X0__)
+@node Sparc-Dependent, i386-Dependent, AMD29K-Dependent, Machine Dependent
+_fi__(_I80386__&&_A29K__&&(!_I960__)&&!_M680X0__)
+_if__(_I80386__&&_VAX__&&(!_A29K__)&&(!_I960__)&&!_M680X0__)
+@node Sparc-Dependent, i386-Dependent, Vax-Dependent, Machine Dependent
+_fi__(_I80386__&&_VAX__&&(!_A29K__)&&(!_I960__)&&!_M680X0__)
+_if__(_I80386__&&(!_VAX__)&&(!_A29K__)&&(!_I960__)&&!_M680X0__)
+@node Sparc-Dependent, i386-Dependent, Machine Dependent, Machine Dependent
+_fi__(_I80386__&&(!_VAX__)&&(!_A29K__)&&(!_I960__)&&!_M680X0__)
+_if__((!_I80386__)&&_M680X0__)
+@node Sparc-Dependent, , M68K-Dependent, Machine Dependent
+_fi__((!_I80386__)&&_M680X0__)
+_if__((!_I80386__)&&_I960__&&!_M680X0__)
+@node Sparc-Dependent, , i960-Dependent, Machine Dependent
+_fi__((!_I80386__)&&_I960__&&!_M680X0__)
+_if__((!_I80386__)&&_A29K__&&(!_I960__)&&!_M680X0__)
+@node Sparc-Dependent, , AMD29K-Dependent, Machine Dependent
+_fi__((!_I80386__)&&_A29K__&&(!_I960__)&&!_M680X0__)
+_if__((!_I80386__)&&_VAX__&&(!_A29K__)&&(!_I960__)&&!_M680X0__)
+@node Sparc-Dependent, , Vax-Dependent, Machine Dependent
+_fi__((!_I80386__)&&_VAX__&&(!_A29K__)&&(!_I960__)&&!_M680X0__)
+_if__((!_I80386__)&&(!_VAX__)&&(!_A29K__)&&(!_I960__)&&!_M680X0__)
+@node Sparc-Dependent, , Machine Dependent, Machine Dependent
+_fi__((!_I80386__)&&(!_VAX__)&&(!_A29K__)&&(!_I960__)&&!_M680X0__)
+_fi__(_GENERIC__)
+_CHAPSEC__(0+_GENERIC__) SPARC Dependent Features
+
+@cindex SPARC support
+@menu
+* Sparc-Opts:: Options
+* Sparc-Float:: Floating Point
+* Sparc-Directives:: Sparc Machine Directives
+@end menu
+
+@node Sparc-Opts, Sparc-Float, Sparc-Dependent, Sparc-Dependent
+_CHAPSEC__(1+_GENERIC__) Options
+
+@cindex options for SPARC (none)
+@cindex SPARC options (none)
+The Sparc has no machine dependent options.
+
+@ignore
+@c FIXME: (sparc) Fill in "syntax" section!
+@c subsection syntax
+I don't know anything about Sparc syntax. Someone who does
+will have to write this section.
+@end ignore
+
+@node Sparc-Float, Sparc-Directives, Sparc-Opts, Sparc-Dependent
+_CHAPSEC__(1+_GENERIC__) Floating Point
+
+@cindex floating point, SPARC (@sc{ieee})
+@cindex SPARC floating point (@sc{ieee})
+The Sparc uses @sc{ieee} floating-point numbers.
+
+@node Sparc-Directives, , Sparc-Float, Sparc-Dependent
+_CHAPSEC__(1+_GENERIC__) Sparc Machine Directives
+
+@cindex SPARC machine directives
+@cindex machine directives, SPARC
+The Sparc version of @code{_AS__} supports the following additional
+machine directives:
+
+@table @code
+@item .common
+@cindex @code{common} directive, SPARC
+This must be followed by a symbol name, a positive number, and
+@code{"bss"}. This behaves somewhat like @code{.comm}, but the
+syntax is different.
+
+@item .half
+@cindex @code{half} directive, SPARC
+This is functionally identical to @code{.short}.
+
+@item .proc
+@cindex @code{proc} directive, SPARC
+This directive is ignored. Any text following it on the same
+line is also ignored.
+
+@item .reserve
+@cindex @code{reserve} directive, SPARC
+This must be followed by a symbol name, a positive number, and
+@code{"bss"}. This behaves somewhat like @code{.lcomm}, but the
+syntax is different.
+
+@item .seg
+@cindex @code{seg} directive, SPARC
+This must be followed by @code{"text"}, @code{"data"}, or
+@code{"data1"}. It behaves like @code{.text}, @code{.data}, or
+@code{.data 1}.
+
+@item .skip
+@cindex @code{skip} directive, SPARC
+This is functionally identical to the @code{.space} directive.
+
+@item .word
+@cindex @code{word} directive, SPARC
+On the Sparc, the .word directive produces 32 bit values,
+instead of the 16 bit values it produces on many other machines.
+@end table
+
+_fi__(_SPARC__)
+_if__(_I80386__)
+_if__(_GENERIC__)
+@c FIXME! Conditionalize for all combinations in this section
+@node i386-Dependent, , Sparc-Dependent, Machine Dependent
+_fi__(_GENERIC__)
+_CHAPSEC__(0+_GENERIC__) 80386 Dependent Features
+
+@cindex i386 support
+@cindex i80306 support
+@menu
+* i386-Options:: Options
+* i386-Syntax:: AT&T Syntax versus Intel Syntax
+* i386-Opcodes:: Opcode Naming
+* i386-Regs:: Register Naming
+* i386-prefixes:: Opcode Prefixes
+* i386-Memory:: Memory References
+* i386-jumps:: Handling of Jump Instructions
+* i386-Float:: Floating Point
+* i386-Notes:: Notes
+@end menu
+
+@node i386-Options, i386-Syntax, i386-Dependent, i386-Dependent
+_CHAPSEC__(1+_GENERIC__) Options
+
+@cindex options for i386 (none)
+@cindex i386 options (none)
+The 80386 has no machine dependent options.
+
+@node i386-Syntax, i386-Opcodes, i386-Options, i386-Dependent
+_CHAPSEC__(1+_GENERIC__) AT&T Syntax versus Intel Syntax
+
+@cindex i386 syntax compatibility
+@cindex syntax compatibility, i386
+In order to maintain compatibility with the output of @code{_GCC__},
+@code{_AS__} supports AT&T System V/386 assembler syntax. This is quite
+different from Intel syntax. We mention these differences because
+almost all 80386 documents used only Intel syntax. Notable differences
+between the two syntaxes are:
+
+@itemize @bullet
+@item
+@cindex immediate operands, i386
+@cindex i386 immediate operands
+@cindex register operands, i386
+@cindex i386 register operands
+@cindex jump/call operands, i386
+@cindex i386 jump/call operands
+@cindex operand delimiters, i386
+AT&T immediate operands are preceded by @samp{$}; Intel immediate
+operands are undelimited (Intel @samp{push 4} is AT&T @samp{pushl $4}).
+AT&T register operands are preceded by @samp{%}; Intel register operands
+are undelimited. AT&T absolute (as opposed to PC relative) jump/call
+operands are prefixed by @samp{*}; they are undelimited in Intel syntax.
+
+@item
+@cindex i386 source, destination operands
+@cindex source, destination operands; i386
+AT&T and Intel syntax use the opposite order for source and destination
+operands. Intel @samp{add eax, 4} is @samp{addl $4, %eax}. The
+@samp{source, dest} convention is maintained for compatibility with
+previous Unix assemblers.
+
+@item
+@cindex opcode suffixes, i386
+@cindex sizes operands, i386
+@cindex i386 size suffixes
+In AT&T syntax the size of memory operands is determined from the last
+character of the opcode name. Opcode suffixes of @samp{b}, @samp{w},
+and @samp{l} specify byte (8-bit), word (16-bit), and long (32-bit)
+memory references. Intel syntax accomplishes this by prefixes memory
+operands (@emph{not} the opcodes themselves) with @samp{byte ptr},
+@samp{word ptr}, and @samp{dword ptr}. Thus, Intel @samp{mov al, byte
+ptr @var{foo}} is @samp{movb @var{foo}, %al} in AT&T syntax.
+
+@item
+@cindex return instructions, i386
+@cindex i386 jump, call, return
+Immediate form long jumps and calls are
+@samp{lcall/ljmp $@var{section}, $@var{offset}} in AT&T syntax; the
+Intel syntax is
+@samp{call/jmp far @var{section}:@var{offset}}. Also, the far return
+instruction
+is @samp{lret $@var{stack-adjust}} in AT&T syntax; Intel syntax is
+@samp{ret far @var{stack-adjust}}.
+
+@item
+@cindex sections, i386
+@cindex i386 sections
+The AT&T assembler does not provide support for multiple section
+programs. Unix style systems expect all programs to be single sections.
+@end itemize
+
+@node i386-Opcodes, i386-Regs, i386-Syntax, i386-Dependent
+_CHAPSEC__(1+_GENERIC__) Opcode Naming
+
+@cindex i386 opcode naming
+@cindex opcode naming, i386
+Opcode names are suffixed with one character modifiers which specify the
+size of operands. The letters @samp{b}, @samp{w}, and @samp{l} specify
+byte, word, and long operands. If no suffix is specified by an
+instruction and it contains no memory operands then @code{_AS__} tries to
+fill in the missing suffix based on the destination register operand
+(the last one by convention). Thus, @samp{mov %ax, %bx} is equivalent
+to @samp{movw %ax, %bx}; also, @samp{mov $1, %bx} is equivalent to
+@samp{movw $1, %bx}. Note that this is incompatible with the AT&T Unix
+assembler which assumes that a missing opcode suffix implies long
+operand size. (This incompatibility does not affect compiler output
+since compilers always explicitly specify the opcode suffix.)
+
+Almost all opcodes have the same names in AT&T and Intel format. There
+are a few exceptions. The sign extend and zero extend instructions need
+two sizes to specify them. They need a size to sign/zero extend
+@emph{from} and a size to zero extend @emph{to}. This is accomplished
+by using two opcode suffixes in AT&T syntax. Base names for sign extend
+and zero extend are @samp{movs@dots{}} and @samp{movz@dots{}} in AT&T
+syntax (@samp{movsx} and @samp{movzx} in Intel syntax). The opcode
+suffixes are tacked on to this base name, the @emph{from} suffix before
+the @emph{to} suffix. Thus, @samp{movsbl %al, %edx} is AT&T syntax for
+``move sign extend @emph{from} %al @emph{to} %edx.'' Possible suffixes,
+thus, are @samp{bl} (from byte to long), @samp{bw} (from byte to word),
+and @samp{wl} (from word to long).
+
+@cindex conversion instructions, i386
+@cindex i386 conversion instructions
+The Intel-syntax conversion instructions
+
+@itemize @bullet
+@item
+@samp{cbw} --- sign-extend byte in @samp{%al} to word in @samp{%ax},
+
+@item
+@samp{cwde} --- sign-extend word in @samp{%ax} to long in @samp{%eax},
+
+@item
+@samp{cwd} --- sign-extend word in @samp{%ax} to long in @samp{%dx:%ax},
+
+@item
+@samp{cdq} --- sign-extend dword in @samp{%eax} to quad in @samp{%edx:%eax},
+@end itemize
+
+@noindent
+are called @samp{cbtw}, @samp{cwtl}, @samp{cwtd}, and @samp{cltd} in
+AT&T naming. @code{_AS__} accepts either naming for these instructions.
+
+@cindex jump instructions, i386
+@cindex call instructions, i386
+Far call/jump instructions are @samp{lcall} and @samp{ljmp} in
+AT&T syntax, but are @samp{call far} and @samp{jump far} in Intel
+convention.
+
+@node i386-Regs, i386-prefixes, i386-Opcodes, i386-Dependent
+_CHAPSEC__(1+_GENERIC__) Register Naming
+
+@cindex i386 registers
+@cindex registers, i386
+Register operands are always prefixes with @samp{%}. The 80386 registers
+consist of
+
+@itemize @bullet
+@item
+the 8 32-bit registers @samp{%eax} (the accumulator), @samp{%ebx},
+@samp{%ecx}, @samp{%edx}, @samp{%edi}, @samp{%esi}, @samp{%ebp} (the
+frame pointer), and @samp{%esp} (the stack pointer).
+
+@item
+the 8 16-bit low-ends of these: @samp{%ax}, @samp{%bx}, @samp{%cx},
+@samp{%dx}, @samp{%di}, @samp{%si}, @samp{%bp}, and @samp{%sp}.
+
+@item
+the 8 8-bit registers: @samp{%ah}, @samp{%al}, @samp{%bh},
+@samp{%bl}, @samp{%ch}, @samp{%cl}, @samp{%dh}, and @samp{%dl} (These
+are the high-bytes and low-bytes of @samp{%ax}, @samp{%bx},
+@samp{%cx}, and @samp{%dx})
+
+@item
+the 6 section registers @samp{%cs} (code section), @samp{%ds}
+(data section), @samp{%ss} (stack section), @samp{%es}, @samp{%fs},
+and @samp{%gs}.
+
+@item
+the 3 processor control registers @samp{%cr0}, @samp{%cr2}, and
+@samp{%cr3}.
+
+@item
+the 6 debug registers @samp{%db0}, @samp{%db1}, @samp{%db2},
+@samp{%db3}, @samp{%db6}, and @samp{%db7}.
+
+@item
+the 2 test registers @samp{%tr6} and @samp{%tr7}.
+
+@item
+the 8 floating point register stack @samp{%st} or equivalently
+@samp{%st(0)}, @samp{%st(1)}, @samp{%st(2)}, @samp{%st(3)},
+@samp{%st(4)}, @samp{%st(5)}, @samp{%st(6)}, and @samp{%st(7)}.
+@end itemize
+
+@node i386-prefixes, i386-Memory, i386-Regs, i386-Dependent
+_CHAPSEC__(1+_GENERIC__) Opcode Prefixes
+
+@cindex i386 opcode prefixes
+@cindex opcode prefixes, i386
+@cindex prefixes, i386
+Opcode prefixes are used to modify the following opcode. They are used
+to repeat string instructions, to provide section overrides, to perform
+bus lock operations, and to give operand and address size (16-bit
+operands are specified in an instruction by prefixing what would
+normally be 32-bit operands with a ``operand size'' opcode prefix).
+Opcode prefixes are usually given as single-line instructions with no
+operands, and must directly precede the instruction they act upon. For
+example, the @samp{scas} (scan string) instruction is repeated with:
+@smallexample
+ repne
+ scas
+@end smallexample
+
+Here is a list of opcode prefixes:
+
+@itemize @bullet
+@item
+@cindex section override prefixes, i386
+Section override prefixes @samp{cs}, @samp{ds}, @samp{ss}, @samp{es},
+@samp{fs}, @samp{gs}. These are automatically added by specifying
+using the @var{section}:@var{memory-operand} form for memory references.
+
+@item
+@cindex size prefixes, i386
+Operand/Address size prefixes @samp{data16} and @samp{addr16}
+change 32-bit operands/addresses into 16-bit operands/addresses. Note
+that 16-bit addressing modes (i.e. 8086 and 80286 addressing modes)
+are not supported (yet).
+
+@item
+@cindex bus lock prefixes, i386
+@cindex inhibiting interrupts, i386
+The bus lock prefix @samp{lock} inhibits interrupts during
+execution of the instruction it precedes. (This is only valid with
+certain instructions; see a 80386 manual for details).
+
+@item
+@cindex coprocessor wait, i386
+The wait for coprocessor prefix @samp{wait} waits for the
+coprocessor to complete the current instruction. This should never be
+needed for the 80386/80387 combination.
+
+@item
+@cindex repeat prefixes, i386
+The @samp{rep}, @samp{repe}, and @samp{repne} prefixes are added
+to string instructions to make them repeat @samp{%ecx} times.
+@end itemize
+
+@node i386-Memory, i386-jumps, i386-prefixes, i386-Dependent
+_CHAPSEC__(1+_GENERIC__) Memory References
+
+@cindex i386 memory references
+@cindex memory references, i386
+An Intel syntax indirect memory reference of the form
+
+@smallexample
+@var{section}:[@var{base} + @var{index}*@var{scale} + @var{disp}]
+@end smallexample
+
+@noindent
+is translated into the AT&T syntax
+
+@smallexample
+@var{section}:@var{disp}(@var{base}, @var{index}, @var{scale})
+@end smallexample
+
+@noindent
+where @var{base} and @var{index} are the optional 32-bit base and
+index registers, @var{disp} is the optional displacement, and
+@var{scale}, taking the values 1, 2, 4, and 8, multiplies @var{index}
+to calculate the address of the operand. If no @var{scale} is
+specified, @var{scale} is taken to be 1. @var{section} specifies the
+optional section register for the memory operand, and may override the
+default section register (see a 80386 manual for section register
+defaults). Note that section overrides in AT&T syntax @emph{must} have
+be preceded by a @samp{%}. If you specify a section override which
+coincides with the default section register, @code{_AS__} will @emph{not}
+output any section register override prefixes to assemble the given
+instruction. Thus, section overrides can be specified to emphasize which
+section register is used for a given memory operand.
+
+Here are some examples of Intel and AT&T style memory references:
+
+@table @asis
+@item AT&T: @samp{-4(%ebp)}, Intel: @samp{[ebp - 4]}
+@var{base} is @samp{%ebp}; @var{disp} is @samp{-4}. @var{section} is
+missing, and the default section is used (@samp{%ss} for addressing with
+@samp{%ebp} as the base register). @var{index}, @var{scale} are both missing.
+
+@item AT&T: @samp{foo(,%eax,4)}, Intel: @samp{[foo + eax*4]}
+@var{index} is @samp{%eax} (scaled by a @var{scale} 4); @var{disp} is
+@samp{foo}. All other fields are missing. The section register here
+defaults to @samp{%ds}.
+
+@item AT&T: @samp{foo(,1)}; Intel @samp{[foo]}
+This uses the value pointed to by @samp{foo} as a memory operand.
+Note that @var{base} and @var{index} are both missing, but there is only
+@emph{one} @samp{,}. This is a syntactic exception.
+
+@item AT&T: @samp{%gs:foo}; Intel @samp{gs:foo}
+This selects the contents of the variable @samp{foo} with section
+register @var{section} being @samp{%gs}.
+@end table
+
+Absolute (as opposed to PC relative) call and jump operands must be
+prefixed with @samp{*}. If no @samp{*} is specified, @code{_AS__} will
+always choose PC relative addressing for jump/call labels.
+
+Any instruction that has a memory operand @emph{must} specify its size (byte,
+word, or long) with an opcode suffix (@samp{b}, @samp{w}, or @samp{l},
+respectively).
+
+@node i386-jumps, i386-Float, i386-Memory, i386-Dependent
+_CHAPSEC__(1+_GENERIC__) Handling of Jump Instructions
+
+@cindex jump optimization, i386
+@cindex i386 jump optimization
+Jump instructions are always optimized to use the smallest possible
+displacements. This is accomplished by using byte (8-bit) displacement
+jumps whenever the target is sufficiently close. If a byte displacement
+is insufficient a long (32-bit) displacement is used. We do not support
+word (16-bit) displacement jumps (i.e. prefixing the jump instruction
+with the @samp{addr16} opcode prefix), since the 80386 insists upon masking
+@samp{%eip} to 16 bits after the word displacement is added.
+
+Note that the @samp{jcxz}, @samp{jecxz}, @samp{loop}, @samp{loopz},
+@samp{loope}, @samp{loopnz} and @samp{loopne} instructions only come in
+byte displacements, so that it is possible that use of these
+instructions (@code{_GCC__} does not use them) will cause the assembler to
+print an error message (and generate incorrect code). The AT&T 80386
+assembler tries to get around this problem by expanding @samp{jcxz foo} to
+@smallexample
+ jcxz cx_zero
+ jmp cx_nonzero
+cx_zero: jmp foo
+cx_nonzero:
+@end smallexample
+
+@node i386-Float, i386-Notes, i386-jumps, i386-Dependent
+_CHAPSEC__(1+_GENERIC__) Floating Point
+
+@cindex i386 floating point
+@cindex floating point, i386
+All 80387 floating point types except packed BCD are supported.
+(BCD support may be added without much difficulty). These data
+types are 16-, 32-, and 64- bit integers, and single (32-bit),
+double (64-bit), and extended (80-bit) precision floating point.
+Each supported type has an opcode suffix and a constructor
+associated with it. Opcode suffixes specify operand's data
+types. Constructors build these data types into memory.
+
+@itemize @bullet
+@item
+@cindex @code{float} directive, i386
+@cindex @code{single} directive, i386
+@cindex @code{double} directive, i386
+@cindex @code{tfloat} directive, i386
+Floating point constructors are @samp{.float} or @samp{.single},
+@samp{.double}, and @samp{.tfloat} for 32-, 64-, and 80-bit formats.
+These correspond to opcode suffixes @samp{s}, @samp{l}, and @samp{t}.
+@samp{t} stands for temporary real, and that the 80387 only supports
+this format via the @samp{fldt} (load temporary real to stack top) and
+@samp{fstpt} (store temporary real and pop stack) instructions.
+
+@item
+@cindex @code{word} directive, i386
+@cindex @code{long} directive, i386
+@cindex @code{int} directive, i386
+@cindex @code{quad} directive, i386
+Integer constructors are @samp{.word}, @samp{.long} or @samp{.int}, and
+@samp{.quad} for the 16-, 32-, and 64-bit integer formats. The corresponding
+opcode suffixes are @samp{s} (single), @samp{l} (long), and @samp{q}
+(quad). As with the temporary real format the 64-bit @samp{q} format is
+only present in the @samp{fildq} (load quad integer to stack top) and
+@samp{fistpq} (store quad integer and pop stack) instructions.
+@end itemize
+
+Register to register operations do not require opcode suffixes,
+so that @samp{fst %st, %st(1)} is equivalent to @samp{fstl %st, %st(1)}.
+
+@cindex i386 @code{fwait} instruction
+@cindex @code{fwait instruction}, i386
+Since the 80387 automatically synchronizes with the 80386 @samp{fwait}
+instructions are almost never needed (this is not the case for the
+80286/80287 and 8086/8087 combinations). Therefore, @code{_AS__} suppresses
+the @samp{fwait} instruction whenever it is implicitly selected by one
+of the @samp{fn@dots{}} instructions. For example, @samp{fsave} and
+@samp{fnsave} are treated identically. In general, all the @samp{fn@dots{}}
+instructions are made equivalent to @samp{f@dots{}} instructions. If
+@samp{fwait} is desired it must be explicitly coded.
+
+@node i386-Notes, , i386-Float, i386-Dependent
+_CHAPSEC__(1+_GENERIC__) Notes
+
+@cindex i386 @code{mul}, @code{imul} instructions
+@cindex @code{mul} instruction, i386
+@cindex @code{imul} instruction, i386
+There is some trickery concerning the @samp{mul} and @samp{imul}
+instructions that deserves mention. The 16-, 32-, and 64-bit expanding
+multiplies (base opcode @samp{0xf6}; extension 4 for @samp{mul} and 5
+for @samp{imul}) can be output only in the one operand form. Thus,
+@samp{imul %ebx, %eax} does @emph{not} select the expanding multiply;
+the expanding multiply would clobber the @samp{%edx} register, and this
+would confuse @code{_GCC__} output. Use @samp{imul %ebx} to get the
+64-bit product in @samp{%edx:%eax}.
+
+We have added a two operand form of @samp{imul} when the first operand
+is an immediate mode expression and the second operand is a register.
+This is just a shorthand, so that, multiplying @samp{%eax} by 69, for
+example, can be done with @samp{imul $69, %eax} rather than @samp{imul
+$69, %eax, %eax}.
+
+_fi__(_I80386__)
+_if__(0)
+@c pesch@cygnus.com: we ignore the following chapters, since internals are
+@c changing rapidly. These may need to be moved to another
+@c book anyhow, if we adopt the model of user/modifier
+@c books.
+@node Maintenance, Retargeting, _MACH_DEP__, Top
+@chapter Maintaining the Assembler
+[[this chapter is still being built]]
+
+@section Design
+We had these goals, in descending priority:
+@table @b
+@item Accuracy.
+For every program composed by a compiler, @code{_AS__} should emit
+``correct'' code. This leaves some latitude in choosing addressing
+modes, order of @code{relocation_info} structures in the object
+file, @emph{etc}.
+
+@item Speed, for usual case.
+By far the most common use of @code{_AS__} will be assembling compiler
+emissions.
+
+@item Upward compatibility for existing assembler code.
+Well @dots{} we don't support Vax bit fields but everything else
+seems to be upward compatible.
+
+@item Readability.
+The code should be maintainable with few surprises. (JF: ha!)
+
+@end table
+
+We assumed that disk I/O was slow and expensive while memory was
+fast and access to memory was cheap. We expect the in-memory data
+structures to be less than 10 times the size of the emitted object
+file. (Contrast this with the C compiler where in-memory structures
+might be 100 times object file size!)
+This suggests:
+@itemize @bullet
+@item
+Try to read the source file from disk only one time. For other
+reasons, we keep large chunks of the source file in memory during
+assembly so this is not a problem. Also the assembly algorithm
+should only scan the source text once if the compiler composed the
+text according to a few simple rules.
+@item
+Emit the object code bytes only once. Don't store values and then
+backpatch later.
+@item
+Build the object file in memory and do direct writes to disk of
+large buffers.
+@end itemize
+
+RMS suggested a one-pass algorithm which seems to work well. By not
+parsing text during a second pass considerable time is saved on
+large programs (@emph{e.g.} the sort of C program @code{yacc} would
+emit).
+
+It happened that the data structures needed to emit relocation
+information to the object file were neatly subsumed into the data
+structures that do backpatching of addresses after pass 1.
+
+Many of the functions began life as re-usable modules, loosely
+connected. RMS changed this to gain speed. For example, input
+parsing routines which used to work on pre-sanitized strings now
+must parse raw data. Hence they have to import knowledge of the
+assemblers' comment conventions @emph{etc}.
+
+@section Deprecated Feature(?)s
+We have stopped supporting some features:
+@itemize @bullet
+@item
+@code{.org} statements must have @b{defined} expressions.
+@item
+Vax Bit fields (@kbd{:} operator) are entirely unsupported.
+@end itemize
+
+It might be a good idea to not support these features in a future release:
+@itemize @bullet
+@item
+@kbd{#} should begin a comment, even in column 1.
+@item
+Why support the logical line & file concept any more?
+@item
+Subsections are a good candidate for flushing.
+Depends on which compilers need them I guess.
+@end itemize
+
+@section Bugs, Ideas, Further Work
+Clearly the major improvement is DON'T USE A TEXT-READING
+ASSEMBLER for the back end of a compiler. It is much faster to
+interpret binary gobbledygook from a compiler's tables than to
+ask the compiler to write out human-readable code just so the
+assembler can parse it back to binary.
+
+Assuming you use @code{_AS__} for human written programs: here are
+some ideas:
+@itemize @bullet
+@item
+Document (here) @code{APP}.
+@item
+Take advantage of knowing no spaces except after opcode
+to speed up @code{_AS__}. (Modify @code{app.c} to flush useless spaces:
+only keep space/tabs at begin of line or between 2
+symbols.)
+@item
+Put pointers in this documentation to @file{a.out} documentation.
+@item
+Split the assembler into parts so it can gobble direct binary
+from @emph{e.g.} @code{cc}. It is silly for@code{cc} to compose text
+just so @code{_AS__} can parse it back to binary.
+@item
+Rewrite hash functions: I want a more modular, faster library.
+@item
+Clean up LOTS of code.
+@item
+Include all the non-@file{.c} files in the maintenance chapter.
+@item
+Document flonums.
+@item
+Implement flonum short literals.
+@item
+Change all talk of expression operands to expression quantities,
+or perhaps to expression arguments.
+@item
+Implement pass 2.
+@item
+Whenever a @code{.text} or @code{.data} statement is seen, we close
+of the current frag with an imaginary @code{.fill 0}. This is
+because we only have one obstack for frags, and we can't grow new
+frags for a new subsection, then go back to the old subsection and
+append bytes to the old frag. All this nonsense goes away if we
+give each subsection its own obstack. It makes code simpler in
+about 10 places, but nobody has bothered to do it because C compiler
+output rarely changes subsections (compared to ending frags with
+relaxable addresses, which is common).
+@end itemize
+
+@section Sources
+@c The following files in the @file{_AS__} directory
+@c are symbolic links to other files, of
+@c the same name, in a different directory.
+@c @itemize @bullet
+@c @item
+@c @file{atof_generic.c}
+@c @item
+@c @file{atof_vax.c}
+@c @item
+@c @file{flonum_const.c}
+@c @item
+@c @file{flonum_copy.c}
+@c @item
+@c @file{flonum_get.c}
+@c @item
+@c @file{flonum_multip.c}
+@c @item
+@c @file{flonum_normal.c}
+@c @item
+@c @file{flonum_print.c}
+@c @end itemize
+
+Here is a list of the source files in the @file{_AS__} directory.
+
+@table @file
+@item app.c
+This contains the pre-processing phase, which deletes comments,
+handles whitespace, etc. This was recently re-written, since app
+used to be a separate program, but RMS wanted it to be inline.
+
+@item append.c
+This is a subroutine to append a string to another string returning a
+pointer just after the last @code{char} appended. (JF: All these
+little routines should probably all be put in one file.)
+
+@item as.c
+Here you will find the main program of the assembler @code{_AS__}.
+
+@item expr.c
+This is a branch office of @file{read.c}. This understands
+expressions, arguments. Inside @code{_AS__}, arguments are called
+(expression) @emph{operands}. This is confusing, because we also talk
+(elsewhere) about instruction @emph{operands}. Also, expression
+operands are called @emph{quantities} explicitly to avoid confusion
+with instruction operands. What a mess.
+
+@item frags.c
+This implements the @b{frag} concept. Without frags, finding the
+right size for branch instructions would be a lot harder.
+
+@item hash.c
+This contains the symbol table, opcode table @emph{etc.} hashing
+functions.
+
+@item hex_value.c
+This is a table of values of digits, for use in atoi() type
+functions. Could probably be flushed by using calls to strtol(), or
+something similar.
+
+@item input-file.c
+This contains Operating system dependent source file reading
+routines. Since error messages often say where we are in reading
+the source file, they live here too. Since @code{_AS__} is intended to
+run under GNU and Unix only, this might be worth flushing. Anyway,
+almost all C compilers support stdio.
+
+@item input-scrub.c
+This deals with calling the pre-processor (if needed) and feeding the
+chunks back to the rest of the assembler the right way.
+
+@item messages.c
+This contains operating system independent parts of fatal and
+warning message reporting. See @file{append.c} above.
+
+@item output-file.c
+This contains operating system dependent functions that write an
+object file for @code{_AS__}. See @file{input-file.c} above.
+
+@item read.c
+This implements all the directives of @code{_AS__}. This also deals
+with passing input lines to the machine dependent part of the
+assembler.
+
+@item strstr.c
+This is a C library function that isn't in most C libraries yet.
+See @file{append.c} above.
+
+@item subsegs.c
+This implements subsections.
+
+@item symbols.c
+This implements symbols.
+
+@item write.c
+This contains the code to perform relaxation, and to write out
+the object file. It is mostly operating system independent, but
+different OSes have different object file formats in any case.
+
+@item xmalloc.c
+This implements @code{malloc()} or bust. See @file{append.c} above.
+
+@item xrealloc.c
+This implements @code{realloc()} or bust. See @file{append.c} above.
+
+@item atof-generic.c
+The following files were taken from a machine-independent subroutine
+library for manipulating floating point numbers and very large
+integers.
+
+@file{atof-generic.c} turns a string into a flonum internal format
+floating-point number.
+
+@item flonum-const.c
+This contains some potentially useful floating point numbers in
+flonum format.
+
+@item flonum-copy.c
+This copies a flonum.
+
+@item flonum-multip.c
+This multiplies two flonums together.
+
+@item bignum-copy.c
+This copies a bignum.
+
+@end table
+
+Here is a table of all the machine-specific files (this includes
+both source and header files). Typically, there is a
+@var{machine}.c file, a @var{machine}-opcode.h file, and an
+atof-@var{machine}.c file. The @var{machine}-opcode.h file should
+be identical to the one used by GDB (which uses it for disassembly.)
+
+@table @file
+
+@item atof-ieee.c
+This contains code to turn a flonum into a ieee literal constant.
+This is used by tye 680x0, 32x32, sparc, and i386 versions of @code{_AS__}.
+
+@item i386-opcode.h
+This is the opcode-table for the i386 version of the assembler.
+
+@item i386.c
+This contains all the code for the i386 version of the assembler.
+
+@item i386.h
+This defines constants and macros used by the i386 version of the assembler.
+
+@item m-generic.h
+generic 68020 header file. To be linked to m68k.h on a
+non-sun3, non-hpux system.
+
+@item m-sun2.h
+68010 header file for Sun2 workstations. Not well tested. To be linked
+to m68k.h on a sun2. (See also @samp{-DSUN_ASM_SYNTAX} in the
+@file{Makefile}.)
+
+@item m-sun3.h
+68020 header file for Sun3 workstations. To be linked to m68k.h before
+compiling on a Sun3 system. (See also @samp{-DSUN_ASM_SYNTAX} in the
+@file{Makefile}.)
+
+@item m-hpux.h
+68020 header file for a HPUX (system 5?) box. Which box, which
+version of HPUX, etc? I don't know.
+
+@item m68k.h
+A hard- or symbolic- link to one of @file{m-generic.h},
+@file{m-hpux.h} or @file{m-sun3.h} depending on which kind of
+680x0 you are assembling for. (See also @samp{-DSUN_ASM_SYNTAX} in the
+@file{Makefile}.)
+
+@item m68k-opcode.h
+Opcode table for 68020. This is now a link to the opcode table
+in the @code{GDB} source directory.
+
+@item m68k.c
+All the mc680x0 code, in one huge, slow-to-compile file.
+
+@item ns32k.c
+This contains the code for the ns32032/ns32532 version of the
+assembler.
+
+@item ns32k-opcode.h
+This contains the opcode table for the ns32032/ns32532 version
+of the assembler.
+
+@item vax-inst.h
+Vax specific file for describing Vax operands and other Vax-ish things.
+
+@item vax-opcode.h
+Vax opcode table.
+
+@item vax.c
+Vax specific parts of @code{_AS__}. Also includes the former files
+@file{vax-ins-parse.c}, @file{vax-reg-parse.c} and @file{vip-op.c}.
+
+@item atof-vax.c
+Turns a flonum into a Vax constant.
+
+@item vms.c
+This file contains the special code needed to put out a VMS
+style object file for the Vax.
+
+@end table
+
+Here is a list of the header files in the source directory.
+(Warning: This section may not be very accurate. I didn't
+write the header files; I just report them.) Also note that I
+think many of these header files could be cleaned up or
+eliminated.
+
+@table @file
+
+@item a.out.h
+This describes the structures used to create the binary header data
+inside the object file. Perhaps we should use the one in
+@file{/usr/include}?
+
+@item as.h
+This defines all the globally useful things, and pulls in _0__<stdio.h>_1__
+and _0__<assert.h>_1__.
+
+@item bignum.h
+This defines macros useful for dealing with bignums.
+
+@item expr.h
+Structure and macros for dealing with expression()
+
+@item flonum.h
+This defines the structure for dealing with floating point
+numbers. It #includes @file{bignum.h}.
+
+@item frags.h
+This contains macro for appending a byte to the current frag.
+
+@item hash.h
+Structures and function definitions for the hashing functions.
+
+@item input-file.h
+Function headers for the input-file.c functions.
+
+@item md.h
+structures and function headers for things defined in the
+machine dependent part of the assembler.
+
+@item obstack.h
+This is the GNU systemwide include file for manipulating obstacks.
+Since nobody is running under real GNU yet, we include this file.
+
+@item read.h
+Macros and function headers for reading in source files.
+
+@item struct-symbol.h
+Structure definition and macros for dealing with the _AS__
+internal form of a symbol.
+
+@item subsegs.h
+structure definition for dealing with the numbered subsections
+of the text and data sections.
+
+@item symbols.h
+Macros and function headers for dealing with symbols.
+
+@item write.h
+Structure for doing section fixups.
+@end table
+
+@comment ~subsection Test Directory
+@comment (Note: The test directory seems to have disappeared somewhere
+@comment along the line. If you want it, you'll probably have to find a
+@comment REALLY OLD dump tape~dots{})
+@comment
+@comment The ~file{test/} directory is used for regression testing.
+@comment After you modify ~@code{_AS__}, you can get a quick go/nogo
+@comment confidence test by running the new ~@code{_AS__} over the source
+@comment files in this directory. You use a shell script ~file{test/do}.
+@comment
+@comment The tests in this suite are evolving. They are not comprehensive.
+@comment They have, however, caught hundreds of bugs early in the debugging
+@comment cycle of ~@code{_AS__}. Most test statements in this suite were naturally
+@comment selected: they were used to demonstrate actual ~@code{_AS__} bugs rather
+@comment than being written ~i{a prioi}.
+@comment
+@comment Another testing suggestion: over 30 bugs have been found simply by
+@comment running examples from this manual through ~@code{_AS__}.
+@comment Some examples in this manual are selected
+@comment to distinguish boundary conditions; they are good for testing ~@code{_AS__}.
+@comment
+@comment ~subsubsection Regression Testing
+@comment Each regression test involves assembling a file and comparing the
+@comment actual output of ~@code{_AS__} to ``known good'' output files. Both
+@comment the object file and the error/warning message file (stderr) are
+@comment inspected. Optionally the ~@code{_AS__} exit status may be checked.
+@comment Discrepencies are reported. Each discrepency means either that
+@comment you broke some part of ~@code{_AS__} or that the ``known good'' files
+@comment are now out of date and should be changed to reflect the new
+@comment definition of ``good''.
+@comment
+@comment Each regression test lives in its own directory, in a tree
+@comment rooted in the directory ~file{test/}. Each such directory
+@comment has a name ending in ~file{.ret}, where `ret' stands for
+@comment REgression Test. The ~file{.ret} ending allows ~code{find
+@comment (1)} to find all regression tests in the tree, without
+@comment needing to list them explicitly.
+@comment
+@comment Any ~file{.ret} directory must contain a file called
+@comment ~file{input} which is the source file to assemble. During
+@comment testing an object file ~file{output} is created, as well as
+@comment a file ~file{stdouterr} which contains the output to both
+@comment stderr and stderr. If there is a file ~file{output.good} in
+@comment the directory, and if ~file{output} contains exactly the
+@comment same data as ~file{output.good}, the file ~file{output} is
+@comment deleted. Likewise ~file{stdouterr} is removed if it exactly
+@comment matches a file ~file{stdouterr.good}. If file
+@comment ~file{status.good} is present, containing a decimal number
+@comment before a newline, the exit status of ~@code{_AS__} is compared
+@comment to this number. If the status numbers are not equal, a file
+@comment ~file{status} is written to the directory, containing the
+@comment actual status as a decimal number followed by newline.
+@comment
+@comment Should any of the ~file{*.good} files fail to match their corresponding
+@comment actual files, this is noted by a 1-line message on the screen during
+@comment the regression test, and you can use ~@code{find (1)} to find any
+@comment files named ~file{status}, ~file {output} or ~file{stdouterr}.
+@comment
+@node Retargeting, Copying, Maintenance, Top
+@chapter Teaching the Assembler about a New Machine
+
+This chapter describes the steps required in order to make the
+assembler work with another machine's assembly language. This
+chapter is not complete, and only describes the steps in the
+broadest terms. You should look at the source for the
+currently supported machine in order to discover some of the
+details that aren't mentioned here.
+
+You should create a new file called @file{@var{machine}.c}, and
+add the appropriate lines to the file @file{Makefile} so that
+you can compile your new version of the assembler. This should
+be straighforward; simply add lines similar to the ones there
+for the four current versions of the assembler.
+
+If you want to be compatible with GDB, (and the current
+machine-dependent versions of the assembler), you should create
+a file called @file{@var{machine}-opcode.h} which should
+contain all the information about the names of the machine
+instructions, their opcodes, and what addressing modes they
+support. If you do this right, the assembler and GDB can share
+this file, and you'll only have to write it once. Note that
+while you're writing @code{_AS__}, you may want to use an
+independent program (if you have access to one), to make sure
+that @code{_AS__} is emitting the correct bytes. Since @code{_AS__}
+and @code{GDB} share the opcode table, an incorrect opcode
+table entry may make invalid bytes look OK when you disassemble
+them with @code{GDB}.
+
+@section Functions You will Have to Write
+
+Your file @file{@var{machine}.c} should contain definitions for
+the following functions and variables. It will need to include
+some header files in order to use some of the structures
+defined in the machine-independent part of the assembler. The
+needed header files are mentioned in the descriptions of the
+functions that will need them.
+
+@table @code
+
+@item long omagic;
+This long integer holds the value to place at the beginning of
+the @file{a.out} file. It is usually @samp{OMAGIC}, except on
+machines that store additional information in the magic-number.
+
+@item char comment_chars[];
+This character array holds the values of the characters that
+start a comment anywhere in a line. Comments are stripped off
+automatically by the machine independent part of the
+assembler. Note that the @samp{/*} will always start a
+comment, and that only @samp{*/} will end a comment started by
+@samp{*/}.
+
+@item char line_comment_chars[];
+This character array holds the values of the chars that start a
+comment only if they are the first (non-whitespace) character
+on a line. If the character @samp{#} does not appear in this
+list, you may get unexpected results. (Various
+machine-independent parts of the assembler treat the comments
+@samp{#APP} and @samp{#NO_APP} specially, and assume that lines
+that start with @samp{#} are comments.)
+
+@item char EXP_CHARS[];
+This character array holds the letters that can separate the
+mantissa and the exponent of a floating point number. Typical
+values are @samp{e} and @samp{E}.
+
+@item char FLT_CHARS[];
+This character array holds the letters that--when they appear
+immediately after a leading zero--indicate that a number is a
+floating-point number. (Sort of how 0x indicates that a
+hexadecimal number follows.)
+
+@item pseudo_typeS md_pseudo_table[];
+(@var{pseudo_typeS} is defined in @file{md.h})
+This array contains a list of the machine_dependent directives
+the assembler must support. It contains the name of each
+pseudo op (Without the leading @samp{.}), a pointer to a
+function to be called when that directive is encountered, and
+an integer argument to be passed to that function.
+
+@item void md_begin(void)
+This function is called as part of the assembler's
+initialization. It should do any initialization required by
+any of your other routines.
+
+@item int md_parse_option(char **optionPTR, int *argcPTR, char ***argvPTR)
+This routine is called once for each option on the command line
+that the machine-independent part of @code{_AS__} does not
+understand. This function should return non-zero if the option
+pointed to by @var{optionPTR} is a valid option. If it is not
+a valid option, this routine should return zero. The variables
+@var{argcPTR} and @var{argvPTR} are provided in case the option
+requires a filename or something similar as an argument. If
+the option is multi-character, @var{optionPTR} should be
+advanced past the end of the option, otherwise every letter in
+the option will be treated as a separate single-character
+option.
+
+@item void md_assemble(char *string)
+This routine is called for every machine-dependent
+non-directive line in the source file. It does all the real
+work involved in reading the opcode, parsing the operands,
+etc. @var{string} is a pointer to a null-terminated string,
+that comprises the input line, with all excess whitespace and
+comments removed.
+
+@item void md_number_to_chars(char *outputPTR,long value,int nbytes)
+This routine is called to turn a C long int, short int, or char
+into the series of bytes that represents that number on the
+target machine. @var{outputPTR} points to an array where the
+result should be stored; @var{value} is the value to store; and
+@var{nbytes} is the number of bytes in 'value' that should be
+stored.
+
+@item void md_number_to_imm(char *outputPTR,long value,int nbytes)
+This routine is called to turn a C long int, short int, or char
+into the series of bytes that represent an immediate value on
+the target machine. It is identical to the function @code{md_number_to_chars},
+except on NS32K machines.@refill
+
+@item void md_number_to_disp(char *outputPTR,long value,int nbytes)
+This routine is called to turn a C long int, short int, or char
+into the series of bytes that represent an displacement value on
+the target machine. It is identical to the function @code{md_number_to_chars},
+except on NS32K machines.@refill
+
+@item void md_number_to_field(char *outputPTR,long value,int nbytes)
+This routine is identical to @code{md_number_to_chars},
+except on NS32K machines.
+
+@item void md_ri_to_chars(struct relocation_info *riPTR,ri)
+(@code{struct relocation_info} is defined in @file{a.out.h})
+This routine emits the relocation info in @var{ri}
+in the appropriate bit-pattern for the target machine.
+The result should be stored in the location pointed
+to by @var{riPTR}. This routine may be a no-op unless you are
+attempting to do cross-assembly.
+
+@item char *md_atof(char type,char *outputPTR,int *sizePTR)
+This routine turns a series of digits into the appropriate
+internal representation for a floating-point number.
+@var{type} is a character from @var{FLT_CHARS[]} that describes
+what kind of floating point number is wanted; @var{outputPTR}
+is a pointer to an array that the result should be stored in;
+and @var{sizePTR} is a pointer to an integer where the size (in
+bytes) of the result should be stored. This routine should
+return an error message, or an empty string (not (char *)0) for
+success.
+
+@item int md_short_jump_size;
+This variable holds the (maximum) size in bytes of a short (16
+bit or so) jump created by @code{md_create_short_jump()}. This
+variable is used as part of the broken-word feature, and isn't
+needed if the assembler is compiled with
+@samp{-DWORKING_DOT_WORD}.
+
+@item int md_long_jump_size;
+This variable holds the (maximum) size in bytes of a long (32
+bit or so) jump created by @code{md_create_long_jump()}. This
+variable is used as part of the broken-word feature, and isn't
+needed if the assembler is compiled with
+@samp{-DWORKING_DOT_WORD}.
+
+@item void md_create_short_jump(char *resultPTR,long from_addr,
+@code{long to_addr,fragS *frag,symbolS *to_symbol)}
+This function emits a jump from @var{from_addr} to @var{to_addr} in
+the array of bytes pointed to by @var{resultPTR}. If this creates a
+type of jump that must be relocated, this function should call
+@code{fix_new()} with @var{frag} and @var{to_symbol}. The jump
+emitted by this function may be smaller than @var{md_short_jump_size},
+but it must never create a larger one.
+(If it creates a smaller jump, the extra bytes of memory will not be
+used.) This function is used as part of the broken-word feature,
+and isn't needed if the assembler is compiled with
+@samp{-DWORKING_DOT_WORD}.@refill
+
+@item void md_create_long_jump(char *ptr,long from_addr,
+@code{long to_addr,fragS *frag,symbolS *to_symbol)}
+This function is similar to the previous function,
+@code{md_create_short_jump()}, except that it creates a long
+jump instead of a short one. This function is used as part of
+the broken-word feature, and isn't needed if the assembler is
+compiled with @samp{-DWORKING_DOT_WORD}.
+
+@item int md_estimate_size_before_relax(fragS *fragPTR,int segment_type)
+This function does the initial setting up for relaxation. This
+includes forcing references to still-undefined symbols to the
+appropriate addressing modes.
+
+@item relax_typeS md_relax_table[];
+(relax_typeS is defined in md.h)
+This array describes the various machine dependent states a
+frag may be in before relaxation. You will need one group of
+entries for each type of addressing mode you intend to relax.
+
+@item void md_convert_frag(fragS *fragPTR)
+(@var{fragS} is defined in @file{as.h})
+This routine does the required cleanup after relaxation.
+Relaxation has changed the type of the frag to a type that can
+reach its destination. This function should adjust the opcode
+of the frag to use the appropriate addressing mode.
+@var{fragPTR} points to the frag to clean up.
+
+@item void md_end(void)
+This function is called just before the assembler exits. It
+need not free up memory unless the operating system doesn't do
+it automatically on exit. (In which case you'll also have to
+track down all the other places where the assembler allocates
+space but never frees it.)
+
+@end table
+
+@section External Variables You will Need to Use
+
+You will need to refer to or change the following external variables
+from within the machine-dependent part of the assembler.
+
+@table @code
+@item extern char flagseen[];
+This array holds non-zero values in locations corresponding to
+the options that were on the command line. Thus, if the
+assembler was called with @samp{-W}, @var{flagseen['W']} would
+be non-zero.
+
+@item extern fragS *frag_now;
+This pointer points to the current frag--the frag that bytes
+are currently being added to. If nothing else, you will need
+to pass it as an argument to various machine-independent
+functions. It is maintained automatically by the
+frag-manipulating functions; you should never have to change it
+yourself.
+
+@item extern LITTLENUM_TYPE generic_bignum[];
+(@var{LITTLENUM_TYPE} is defined in @file{bignum.h}.
+This is where @dfn{bignums}--numbers larger than 32 bits--are
+returned when they are encountered in an expression. You will
+need to use this if you need to implement directives (or
+anything else) that must deal with these large numbers.
+@code{Bignums} are of @code{segT} @code{SEG_BIG} (defined in
+@file{as.h}, and have a positive @code{X_add_number}. The
+@code{X_add_number} of a @code{bignum} is the number of
+@code{LITTLENUMS} in @var{generic_bignum} that the number takes
+up.
+
+@item extern FLONUM_TYPE generic_floating_point_number;
+(@var{FLONUM_TYPE} is defined in @file{flonum.h}.
+The is where @dfn{flonums}--floating-point numbers within
+expressions--are returned. @code{Flonums} are of @code{segT}
+@code{SEG_BIG}, and have a negative @code{X_add_number}.
+@code{Flonums} are returned in a generic format. You will have
+to write a routine to turn this generic format into the
+appropriate floating-point format for your machine.
+
+@item extern int need_pass_2;
+If this variable is non-zero, the assembler has encountered an
+expression that cannot be assembled in a single pass. Since
+the second pass isn't implemented, this flag means that the
+assembler is punting, and is only looking for additional syntax
+errors. (Or something like that.)
+
+@item extern segT now_seg;
+This variable holds the value of the section the assembler is
+currently assembling into.
+
+@end table
+
+@section External functions will you need
+
+You will find the following external functions useful (or
+indispensable) when you're writing the machine-dependent part
+of the assembler.
+
+@table @code
+
+@item char *frag_more(int bytes)
+This function allocates @var{bytes} more bytes in the current
+frag (or starts a new frag, if it can't expand the current frag
+any more.) for you to store some object-file bytes in. It
+returns a pointer to the bytes, ready for you to store data in.
+
+@item void fix_new(fragS *frag, int where, short size, symbolS *add_symbol, symbolS *sub_symbol, long offset, int pcrel)
+This function stores a relocation fixup to be acted on later.
+@var{frag} points to the frag the relocation belongs in;
+@var{where} is the location within the frag where the relocation begins;
+@var{size} is the size of the relocation, and is usually 1 (a single byte),
+ 2 (sixteen bits), or 4 (a longword).
+The value @var{add_symbol} @minus{} @var{sub_symbol} + @var{offset}, is added to the byte(s)
+at _0__@var{frag->literal[where]}_1__. If @var{pcrel} is non-zero, the address of the
+location is subtracted from the result. A relocation entry is also added
+to the @file{a.out} file. @var{add_symbol}, @var{sub_symbol}, and/or
+@var{offset} may be NULL.@refill
+
+@item char *frag_var(relax_stateT type, int max_chars, int var,
+@code{relax_substateT subtype, symbolS *symbol, char *opcode)}
+This function creates a machine-dependent frag of type @var{type}
+(usually @code{rs_machine_dependent}).
+@var{max_chars} is the maximum size in bytes that the frag may grow by;
+@var{var} is the current size of the variable end of the frag;
+@var{subtype} is the sub-type of the frag. The sub-type is used to index into
+@var{md_relax_table[]} during @code{relaxation}.
+@var{symbol} is the symbol whose value should be used to when relax-ing this frag.
+@var{opcode} points into a byte whose value may have to be modified if the
+addressing mode used by this frag changes. It typically points into the
+@var{fr_literal[]} of the previous frag, and is used to point to a location
+that @code{md_convert_frag()}, may have to change.@refill
+
+@item void frag_wane(fragS *fragPTR)
+This function is useful from within @code{md_convert_frag}. It
+changes a frag to type rs_fill, and sets the variable-sized
+piece of the frag to zero. The frag will never change in size
+again.
+
+@item segT expression(expressionS *retval)
+(@var{segT} is defined in @file{as.h}; @var{expressionS} is defined in @file{expr.h})
+This function parses the string pointed to by the external char
+pointer @var{input_line_pointer}, and returns the section-type
+of the expression. It also stores the results in the
+@var{expressionS} pointed to by @var{retval}.
+@var{input_line_pointer} is advanced to point past the end of
+the expression. (@var{input_line_pointer} is used by other
+parts of the assembler. If you modify it, be sure to restore
+it to its original value.)
+
+@item as_warn(char *message,@dots{})
+If warning messages are disabled, this function does nothing.
+Otherwise, it prints out the current file name, and the current
+line number, then uses @code{fprintf} to print the
+@var{message} and any arguments it was passed.
+
+@item as_bad(char *message,@dots{})
+This function should be called when @code{_AS__} encounters
+conditions that are bad enough that @code{_AS__} should not
+produce an object file, but should continue reading input and
+printing warning and bad error messages.
+
+@item as_fatal(char *message,@dots{})
+This function prints out the current file name and line number,
+prints the word @samp{FATAL:}, then uses @code{fprintf} to
+print the @var{message} and any arguments it was passed. Then
+the assembler exits. This function should only be used for
+serious, unrecoverable errors.
+
+@item void float_const(int float_type)
+This function reads floating-point constants from the current
+input line, and calls @code{md_atof} to assemble them. It is
+useful as the function to call for the directives
+@samp{.single}, @samp{.double}, @samp{.float}, etc.
+@var{float_type} must be a character from @var{FLT_CHARS}.
+
+@item void demand_empty_rest_of_line(void);
+This function can be used by machine-dependent directives to
+make sure the rest of the input line is empty. It prints a
+warning message if there are additional characters on the line.
+
+@item long int get_absolute_expression(void)
+This function can be used by machine-dependent directives to
+read an absolute number from the current input line. It
+returns the result. If it isn't given an absolute expression,
+it prints a warning message and returns zero.
+
+@end table
+
+
+@section The concept of Frags
+
+This assembler works to optimize the size of certain addressing
+modes. (e.g. branch instructions) This means the size of many
+pieces of object code cannot be determined until after assembly
+is finished. (This means that the addresses of symbols cannot be
+determined until assembly is finished.) In order to do this,
+@code{_AS__} stores the output bytes as @dfn{frags}.
+
+Here is the definition of a frag (from @file{as.h})
+@smallexample
+struct frag
+@{
+ long int fr_fix;
+ long int fr_var;
+ relax_stateT fr_type;
+ relax_substateT fr_substate;
+ unsigned long fr_address;
+ long int fr_offset;
+ struct symbol *fr_symbol;
+ char *fr_opcode;
+ struct frag *fr_next;
+ char fr_literal[];
+@}
+@end smallexample
+
+@table @var
+@item fr_fix
+is the size of the fixed-size piece of the frag.
+
+@item fr_var
+is the maximum (?) size of the variable-sized piece of the frag.
+
+@item fr_type
+is the type of the frag.
+Current types are:
+rs_fill
+rs_align
+rs_org
+rs_machine_dependent
+
+@item fr_substate
+This stores the type of machine-dependent frag this is. (what
+kind of addressing mode is being used, and what size is being
+tried/will fit/etc.
+
+@item fr_address
+@var{fr_address} is only valid after relaxation is finished.
+Before relaxation, the only way to store an address is (pointer
+to frag containing the address) plus (offset into the frag).
+
+@item fr_offset
+This contains a number, whose meaning depends on the type of
+the frag.
+for machine_dependent frags, this contains the offset from
+fr_symbol that the frag wants to go to. Thus, for branch
+instructions it is usually zero. (unless the instruction was
+@samp{jba foo+12} or something like that.)
+
+@item fr_symbol
+for machine_dependent frags, this points to the symbol the frag
+needs to reach.
+
+@item fr_opcode
+This points to the location in the frag (or in a previous frag)
+of the opcode for the instruction that caused this to be a frag.
+@var{fr_opcode} is needed if the actual opcode must be changed
+in order to use a different form of the addressing mode.
+(For example, if a conditional branch only comes in size tiny,
+a large-size branch could be implemented by reversing the sense
+of the test, and turning it into a tiny branch over a large jump.
+This would require changing the opcode.)
+
+@var{fr_literal} is a variable-size array that contains the
+actual object bytes. A frag consists of a fixed size piece of
+object data, (which may be zero bytes long), followed by a
+piece of object data whose size may not have been determined
+yet. Other information includes the type of the frag (which
+controls how it is relaxed),
+
+@item fr_next
+This is the next frag in the singly-linked list. This is
+usually only needed by the machine-independent part of
+@code{_AS__}.
+
+@end table
+_fi__(0)
+
+@node Copying, Index, _MACH_DEP__, Top
+@unnumbered GNU GENERAL PUBLIC LICENSE
+
+@cindex license
+@cindex GPL
+@cindex copying @code{_AS__}
+@center Version 2, June 1991
+
+@display
+Copyright @copyright{} 1989, 1991 Free Software Foundation, Inc.
+675 Mass Ave, Cambridge, MA 02139, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+@end display
+
+@unnumberedsec Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software---to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+@iftex
+@unnumberedsec TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+@end iftex
+@ifinfo
+@center TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+@end ifinfo
+
+@enumerate
+@item
+This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The ``Program'', below,
+refers to any such program or work, and a ``work based on the Program''
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term ``modification''.) Each licensee is addressed as ``you''.
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+@item
+You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+@item
+You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+@enumerate a
+@item
+You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+@item
+You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+@item
+If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+@end enumerate
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+@item
+You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+@enumerate a
+@item
+Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+@item
+Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+@item
+Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+@end enumerate
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+@item
+You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+@item
+You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+@item
+Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+@item
+If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+@item
+If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+@item
+The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and ``any
+later version'', you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+@item
+If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+@iftex
+@heading NO WARRANTY
+@end iftex
+@ifinfo
+@center NO WARRANTY
+@end ifinfo
+
+@item
+BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM ``AS IS'' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+@item
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+@end enumerate
+
+@iftex
+@heading END OF TERMS AND CONDITIONS
+@end iftex
+@ifinfo
+@center END OF TERMS AND CONDITIONS
+@end ifinfo
+
+@page
+@unnumberedsec Applying These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the ``copyright'' line and a pointer to where the full notice is found.
+
+@smallexample
+@var{one line to give the program's name and an idea of what it does.}
+Copyright (C) 19@var{yy} @var{name of author}
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the
+Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+@end smallexample
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+@smallexample
+Gnomovision version 69, Copyright (C) 19@var{yy} @var{name of author}
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
+type `show w'. This is free software, and you are welcome
+to redistribute it under certain conditions; type `show c'
+for details.
+@end smallexample
+
+The hypothetical commands @samp{show w} and @samp{show c} should show
+the appropriate parts of the General Public License. Of course, the
+commands you use may be called something other than @samp{show w} and
+@samp{show c}; they could even be mouse-clicks or menu items---whatever
+suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a ``copyright disclaimer'' for the program, if
+necessary. Here is a sample; alter the names:
+
+@smallexample
+Yoyodyne, Inc., hereby disclaims all copyright interest in
+the program `Gnomovision' (which makes passes at compilers)
+written by James Hacker.
+
+@var{signature of Ty Coon}, 1 April 1989
+Ty Coon, President of Vice
+@end smallexample
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
+@node Index, , Copying, Top
+@unnumbered Index
+
+@printindex cp
+
+@summarycontents
+@contents
+@bye
diff --git a/gnu/usr.bin/as/doc/config.status b/gnu/usr.bin/as/doc/config.status
new file mode 100644
index 0000000..f1e7f63
--- /dev/null
+++ b/gnu/usr.bin/as/doc/config.status
@@ -0,0 +1,5 @@
+#!/bin/sh
+# This file was generated automatically by configure. Do not edit.
+# /d/users/pk/src/gnu/usr.bin/gas.1.93/gas/doc was configured as follows:
+/d/users/pk/src/gnu/usr.bin/gas.1.93/./configure i386 -target=i386 -norecursion
+#
diff --git a/gnu/usr.bin/as/doc/configure.in b/gnu/usr.bin/as/doc/configure.in
new file mode 100644
index 0000000..f9820ea
--- /dev/null
+++ b/gnu/usr.bin/as/doc/configure.in
@@ -0,0 +1,34 @@
+# This file is configure.in
+#
+# Copyright (C) 1987-1992 Free Software Foundation, Inc.
+#
+# This file is part of GAS, the GNU Assembler.
+#
+# GAS is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GAS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GAS; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+#
+
+# This file is a shell script that supplies the information necessary
+# to tailor a template configure script into the configure script
+# appropriate for this directory. For more information, check any
+# existing configure script.
+
+srctrigger=all.m4
+srcname="gas doc"
+
+# per-host:
+
+# per-target:
+
+# end of gas/doc/configure.in
diff --git a/gnu/usr.bin/as/doc/gen.m4 b/gnu/usr.bin/as/doc/gen.m4
new file mode 100644
index 0000000..bf444a6
--- /dev/null
+++ b/gnu/usr.bin/as/doc/gen.m4
@@ -0,0 +1,14 @@
+_divert__(-1)
+<$Id: gen.m4,v 1.1 1993/10/02 21:00:19 pk Exp $>
+_define__(<_GENERIC__>,<1>) In case none.m4 changes its mind abt default
+
+_define__(<_AOUT__>,<1>)
+_define__(<_COFF__>,<1>)
+_define__(<_ELF__>,<1>)
+
+_define__(<_I80386__>,<1>)
+_define__(<_M680X0__>,<1>)
+_define__(<_SPARC__>,<1>)
+_define__(<_VAX__>,<1>)
+
+_divert__<>
diff --git a/gnu/usr.bin/as/doc/h8.m4 b/gnu/usr.bin/as/doc/h8.m4
new file mode 100644
index 0000000..ed52c85
--- /dev/null
+++ b/gnu/usr.bin/as/doc/h8.m4
@@ -0,0 +1,15 @@
+_divert__(-1)
+_define__(<_H8__>,<1>)
+_define__(<_AS__>,<as83>)
+_define__(<_GENERIC__>,<0>)
+_define__(<_HOST__>,<H8/300>)
+_define__(<_MACH_DEP__>,<H8/300-Dependent>)
+_define__(<_AOUT__>,<0>)
+_define__(<_BOUT__>,<0>)
+_define__(<_COFF__>,<1>)
+_define__(<_ELF__>,<0>)
+_define__(<_DIFFTABKLUG__>,0) NO difference-table kluge
+_define__(<_IEEEFLOAT__>,1) IEEE floating point
+_define__(<_W32__>,0)
+_define__(<_W16__>,1) 16-bit words
+_divert__<>
diff --git a/gnu/usr.bin/as/doc/i80386.m4 b/gnu/usr.bin/as/doc/i80386.m4
new file mode 100644
index 0000000..e8718aa
--- /dev/null
+++ b/gnu/usr.bin/as/doc/i80386.m4
@@ -0,0 +1,12 @@
+_divert__(-1)
+_define__(<_I80386__>,<1>)
+_define__(<_GENERIC__>,<0>)
+_define__(<_HOST__>,<Intel 80386>)
+_define__(<_MACH_DEP__>,<i386-Dependent>)
+_define__(<_AOUT__>,<1>)
+_define__(<_BOUT__>,<0>)
+_define__(<_COFF__>,<0>)
+_define__(<_ELF__>,<0>)
+_define__(<_W32__>,0)
+_define__(<_W16__>,1) 16-bit words
+_divert__<>
diff --git a/gnu/usr.bin/as/doc/i960.m4 b/gnu/usr.bin/as/doc/i960.m4
new file mode 100644
index 0000000..1fca147
--- /dev/null
+++ b/gnu/usr.bin/as/doc/i960.m4
@@ -0,0 +1,16 @@
+_divert__(-1)
+_define__(<_I960__>,<1>)
+_define__(<_AOUT__>,<0>)
+_define__(<_BOUT__>,<1>)
+_define__(<_COFF__>,<1>)
+_define__(<_AS__>,<gas960>)
+_define__(<_GCC__>,<gcc960>)
+_define__(<_LD__>,<gld960>)
+_define__(<_GDB__>,<gdb960>)
+_define__(<_HOST__>,<Intel 960>)
+_define__(<_MACH_DEP__>,<i960-Dependent>)
+_define__(<_DIFFTABKLUG__>,0) NO difference-table kluge
+_define__(<_IEEEFLOAT__>,1) IEEE floating point
+_define__(<_W32__>,1) 32-bit words
+_define__(<_W16__>,0)
+_divert__<>
diff --git a/gnu/usr.bin/as/doc/m680x0.m4 b/gnu/usr.bin/as/doc/m680x0.m4
new file mode 100644
index 0000000..4013e72
--- /dev/null
+++ b/gnu/usr.bin/as/doc/m680x0.m4
@@ -0,0 +1,8 @@
+_divert__(-1)
+_define__(<_GENERIC__>,<0>)
+_define__(<_M680X0__>,<1>)
+_define__(<_HOST__>,<Motorola 680x0>)
+_define__(<_MACH_DEP__>,<M68K-Dependent>)
+_define__(<_W32__>,0)
+_define__(<_W16__>,1) 16-bit words
+_divert__<>
diff --git a/gnu/usr.bin/as/doc/none.m4 b/gnu/usr.bin/as/doc/none.m4
new file mode 100644
index 0000000..dfa17d3
--- /dev/null
+++ b/gnu/usr.bin/as/doc/none.m4
@@ -0,0 +1,57 @@
+_divert__(-1)
+<$Id: none.m4,v 1.1 1993/10/02 21:00:24 pk Exp $>
+
+Switches:
+
+_define__(<_ALL_ARCH__>,<0>) (Meant as most inclusive; file turning
+ it on is expected to also turn on
+ all arch-related switches including
+ "_GENERIC__")
+_define__(<_GENERIC__>,<1>) (may not be quite all configs;
+ meant for "most vanilla" manual)
+_define__(<_INTERNALS__>,<0>)
+
+_define__(<_AOUT__>,<1>) Object formats. Note we turn on one.
+_define__(<_BOUT__>,<0>)
+_define__(<_COFF__>,<0>)
+_define__(<_ELF__>,<0>)
+
+ Properties of the assembler
+_define__(<_DIFFTABKLUG__>,1) Do we use the difference-table kluge?
+_define__(<_IEEEFLOAT__>,0) IEEE floating-point?
+_define__(<_W32__>,0) word is 32 bits
+_define__(<_W16__>,1) word is 16 bits
+
+_define__(<_A29K__>,<0>) Specific architectures. Note none
+_define__(<_H8__>,<0>) starts out on.
+_define__(<_I80386__>,<0>)
+_define__(<_I960__>,<0>)
+_define__(<_M680X0__>,<0>)
+_define__(<_SPARC__>,<0>)
+_define__(<_VAX__>,<0>)
+_define__(<_VXWORKS__>,<0>)
+
+Text:
+
+Default names; individual configs may override
+Assembler:
+_define__(<_AS__>,<as>)
+C Compiler:
+_define__(<_GCC__>,<gcc>)
+Linker:
+_define__(<_LD__>,<ld>)
+Debugger name:
+_define__(<_GDBN__>,<GDB>)
+Debugger program:
+_define__(<_GDBP__>,<gdb>)
+Debugger init file:
+_define__(<_GDBINIT__>,<.gdbinit>)
+
+Text for host; individual configs *should* override, but this may
+catch some flubs
+_define__(<_HOST__>,<machine specific>)
+
+"Machine Dependent" nodename
+_define__(<_MACH_DEP__>,<Machine Dependent>)
+
+_divert__<>
diff --git a/gnu/usr.bin/as/doc/pretex.m4 b/gnu/usr.bin/as/doc/pretex.m4
new file mode 100644
index 0000000..9a9696f
--- /dev/null
+++ b/gnu/usr.bin/as/doc/pretex.m4
@@ -0,0 +1,268 @@
+divert(-1) -*-Text-*-
+` Copyright (c) 1991 Free Software Foundation, Inc.'
+` This file defines and documents the M4 macros used '
+` to preprocess some GNU manuals'
+` $Id: pretex.m4,v 1.1 1993/10/02 21:00:25 pk Exp $'
+
+I. INTRODUCTION
+
+This collection of M4 macros is meant to help in pre-processing texinfo
+files to allow configuring them by hosts; for example, the reader of an
+as manual who only has access to a 386 may not really want to see crud about
+VAXen.
+
+A preprocessor is used, rather than extending texinfo, because this
+way we can hack the conditionals in only one place; otherwise we would
+have to write TeX macros, update makeinfo, and update the Emacs
+info-formatting functions.
+
+II. COMPATIBILITY
+
+These macros should work with GNU m4 and System V m4; they do not work
+with Sun or Berkeley M4.
+
+III. USAGE
+
+A. M4 INVOCATION
+Assume this file is called "pretex.m4". Then, to preprocess a
+document "mybook.texinfo" you might do something like the following:
+
+ m4 pretex.m4 none.m4 PARTIC.m4 mybook.texinfo >mybook-PARTIC.texinfo
+
+---where your path is set to find GNU or SysV "m4", and the other m4
+files mentioned are as follows:
+
+ none.m4: A file that defines, as 0, all the options you might
+ want to turn on using the conditionals defined below.
+ Unlike the C preprocessor, m4 does not default
+ undefined macros to 0. For example, here is a "none.m4"
+ I have been using:
+ _divert__(-1)
+
+ _define__(<_ALL_ARCH__>,<0>)
+ _define__(<_INTERNALS__>,<0>)
+
+ _define__(<_AMD29K__>,<0>)
+ _define__(<_I80386__>,<0>)
+ _define__(<_I960__>,<0>)
+ _define__(<_M680X0__>,<0>)
+ _define__(<_SPARC__>,<0>)
+ _define__(<_VAX__>,<0>)
+
+ _divert__<>
+
+ PARTIC.m4: A file that turns on whichever options you actually
+ want the manual configured for, in this particular
+ instance. Its contents are similar to one or more of
+ the lines in "none.m4", but of course the second
+ argument to _define__ is <1> rather than <0>.
+
+ This is also a convenient place to _define__ any macros
+ that you want to expand to different text for
+ different configurations---for example, the name of
+ the program being described.
+
+Naturally, these are just suggested conventions; you could put your macro
+definitions in any files or combinations of files you like.
+
+These macros use the characters < and > as m4 quotes; if you need
+these characters in your text, you will also want to use the macros
+_0__ and _1__ from this package---see the description of "Quote
+Handling" in the "Implementation" section below.
+
+B. WHAT GOES IN THE PRE-TEXINFO SOURCE
+
+For the most part, the text of your book. In addition, you can
+have text that is included only conditionally, using the macros
+_if__ and _fi__ defined below. They BOTH take an argument! This is
+primarily meant for readability (so a human can more easily see what
+conditional end matches what conditional beginning), but the argument
+is actually used in the _fi__ as well as the _if__ implementation.
+You should always give a _fi__ the same argument as its matching
+_if__. Other arguments may appear to work for a while, but are almost
+certain to produce the wrong output for some configurations.
+
+For example, here is an excerpt from the very beginning of the
+documentation for GNU as, to name the info file appropriately for
+different configurations:
+ _if__(_ALL_ARCH__)
+ @setfilename as.info
+ _fi__(_ALL_ARCH__)
+ _if__(_M680X0__ && !_ALL_ARCH__)
+ @setfilename as-m680x0.info
+ _fi__(_M680X0__ && !_ALL_ARCH__)
+ _if__(_AMD29K__ && !_ALL_ARCH__)
+ @setfilename as-29k.info
+ _fi__(_AMD29K__ && !_ALL_ARCH__)
+
+Note that you can use Boolean expressions in the arguments; the
+expression language is that of the built-in m4 macro `eval', described
+in the m4 manual.
+
+IV. IMPLEMENTATION
+
+A.PRIMITIVE RENAMING
+First, we redefine m4's built-ins to avoid conflict with plain text.
+The naming convention used is that our macros all begin with a single
+underbar and end with two underbars. The asymmetry is meant to avoid
+conflict with some other conventions (which we may want to document) that
+are intended to avoid conflict, like ANSI C predefined macros.
+
+define(`_undefine__',defn(`undefine'))
+define(`_define__',defn(`define'))
+define(`_defn__',defn(`defn'))
+define(`_ppf__',`_define__(`_$1__',_defn__(`$1'))_undefine__(`$1')')
+_ppf__(`builtin')
+_ppf__(`changecom')
+_ppf__(`changequote')
+_ppf__(`decr')
+_ppf__(`define')
+_ppf__(`defn')
+_ppf__(`divert')
+_ppf__(`divnum')
+_ppf__(`dnl')
+_ppf__(`dumpdef')
+_ppf__(`errprint')
+_ppf__(`esyscmd')
+_ppf__(`eval')
+_ppf__(`format')
+_ppf__(`ifdef')
+_ppf__(`ifelse')
+_ppf__(`include')
+_ppf__(`incr')
+_ppf__(`index')
+_ppf__(`len')
+_ppf__(`m4exit')
+_ppf__(`m4wrap')
+_ppf__(`maketemp')
+_ppf__(`patsubst')
+_ppf__(`popdef')
+_ppf__(`pushdef')
+_ppf__(`regexp')
+_ppf__(`shift')
+_ppf__(`sinclude')
+_ppf__(`substr')
+_ppf__(`syscmd')
+_ppf__(`sysval')
+_ppf__(`traceoff')
+_ppf__(`traceon')
+_ppf__(`translit')
+_ppf__(`undefine')
+_ppf__(`undivert')
+_ppf__(`unix')
+
+B. QUOTE HANDLING.
+
+The characters used as quotes by M4, by default, are unfortunately
+quite likely to occur in ordinary text. To avoid surprises, we will
+use the characters <> ---which are just as suggestive (more so to
+Francophones, perhaps) but a little less common in text (save for
+those poor Francophones. You win some, you lose some). Still, we
+expect also to have to set < and > occasionally in text; to do that,
+we define a macro to turn off quote handling (_0__) and a macro to
+turn it back on (_1__), according to our convention.
+
+ BEWARE: This seems to make < and > unusable as relational operations
+ in calls to the builtin "eval". So far I've gotten
+ along without; but a better choice may be possible.
+
+Note that we postponed this for a while, for convenience in discussing
+the issue and in the primitive renaming---not to mention in defining
+_0__ and _1__ themselves! However, the quote redefinitions MUST
+precede the _if__ / _fi__ definitions, because M4 will expand the text
+as given---if we use the wrong quotes here, we will get the wrong
+quotes when we use the conditionals.
+
+_define__(_0__,`_changequote__(,)')_define__(_1__,`_changequote__(<,>)')
+_1__
+
+C. CONDITIONALS
+
+We define two macros, _if__ and _fi__. BOTH take arguments! This is
+meant both to help the human reader match up a _fi__ with its
+corresponding _if__ and to aid in the implementation. You may use the
+full expression syntax supported by M4 (see docn of `eval' builtin in
+the m4 manual).
+
+The conditional macros are carefully defined to avoid introducing
+extra whitespace (i.e., blank lines or blank characters). One side
+effect exists---
+
+ BEWARE: text following an `_if__' on the same line is
+ DISCARDED even if the condition is true; text
+ following a `_fi__' on the same line is also
+ always discarded.
+
+The recommended convention is to always place _if__ and _fi__ on a
+line by themselves. This will also aid the human reader. TeX won't
+care about the line breaks; as for info, you may want to insert calls
+to `@refill' at the end of paragraphs containing conditionalized text,
+where you don't want line breaks separating unconditional from
+conditional text. info formatting will then give you nice looking
+paragraphs in the info file.
+
+Nesting: conditionals are designed to nest, in the following way:
+*nothing* is output between an outer pair of false conditionals, even
+if there are true conditionals inside. A false conditional "defeats"
+all conditionals within it. The counter _IF_FS__ is used to
+implement this; kindly avoid redefining it directly.
+
+_define__(<_IF_FS__>,<0>)
+
+NOTE: The definitions for our "pushf" and "popf" macros use eval
+rather than incr and decr, because GNU m4 (0.75) tries to call eval
+for us when we say "incr" or "decr"---but doesn't notice we've changed
+eval's name.
+
+_define__(
+ <_pushf__>,
+ <_define__(<_IF_FS__>,
+ _eval__((_IF_FS__)+1))>)
+_define__(
+ <_popf__>,
+ <_ifelse__(0,_IF_FS__,
+ <<>_dnl__<>>,
+ <_define__(<_IF_FS__>,_eval__((_IF_FS__)-1))>)>)
+
+_define__(
+ <_if__>,
+ <_ifelse__(1,_eval__( ($1) ),
+ <<>_dnl__<>>,
+ <_pushf__<>_divert__(-1)>)>)
+_define__(
+ <_fi__>,
+ <_ifelse__(1,_eval__( ($1) ),
+ <<>_dnl__<>>,
+ <_popf__<>_ifelse__(0,_IF_FS__,
+ <_divert__<>_dnl__<>>,<>)>)>)
+
+D. CHAPTER/SECTION MACRO
+In a parametrized manual, the heading level may need to be calculated;
+for example, a manual that has a chapter on machine dependencies
+should be conditionally structured as follows:
+ - IF the manual is configured for a SINGLE machine type, use
+the chapter heading for that machine type, and run headings down
+from there (top level for a particular machine is chapter, then within
+that we have section, subsection etc);
+ - ELSE, if MANY machine types are described in the chapter,
+use a generic chapter heading such as "@chapter Machine Dependencies",
+use "section" for the top level description of EACH machine, and run
+headings down from there (top level for a particular machine is
+section, then within that we have subsection, subsubsection etc).
+
+The macro <_CHAPSEC__> is for this purpose: its argument is evaluated (so
+you can construct expressions to express choices such as above), then
+expands as follows:
+ 0: @chapter
+ 1: @section
+ 2: @subsection
+ 3: @subsubsection
+ ...and so on.
+
+_define__(<_CHAPSEC__>,<@_cs__(_eval__($1))>)
+_define__(<_cs__>,<_ifelse__(
+ 0, $1, <chapter>,
+ 1, $1, <section>,
+ <sub<>_cs__(_eval__($1 - 1))>)>)
+
+_divert__<>_dnl__<>
diff --git a/gnu/usr.bin/as/doc/sparc.m4 b/gnu/usr.bin/as/doc/sparc.m4
new file mode 100644
index 0000000..121855a
--- /dev/null
+++ b/gnu/usr.bin/as/doc/sparc.m4
@@ -0,0 +1,8 @@
+_divert__(-1)
+_define__(<_SPARC__>,<1>)
+_define__(<_HOST__>,<SPARC>)
+_define__(<_MACH_DEP__>,<Sparc-Dependent>)
+_define__(<_IEEEFLOAT__>,1) IEEE floating point
+_define__(<_W32__>,1) 32-bit words
+_define__(<_W16__>,0)
+_divert__<>
diff --git a/gnu/usr.bin/as/doc/vax.m4 b/gnu/usr.bin/as/doc/vax.m4
new file mode 100644
index 0000000..009e334
--- /dev/null
+++ b/gnu/usr.bin/as/doc/vax.m4
@@ -0,0 +1,7 @@
+_divert__(-1)
+_define__(<_VAX__>,<1>)
+_define__(<_HOST__>,<VAX>)
+_define__(<_MACH_DEP__>,<VAX-Dependent>)
+_define__(<_W32__>,0)
+_define__(<_W16__>,1) 16-bit words
+_divert__<>
diff --git a/gnu/usr.bin/as/doc/vintage.m4 b/gnu/usr.bin/as/doc/vintage.m4
new file mode 100644
index 0000000..d5913be
--- /dev/null
+++ b/gnu/usr.bin/as/doc/vintage.m4
@@ -0,0 +1,11 @@
+_divert__(-1)
+<$Id: vintage.m4,v 1.1 1993/10/02 21:00:29 pk Exp $>
+_define__(<_ALL_ARCH__>,<1>)
+_define__(<_GENERIC__>,<1>) In case none.m4 changes its mind abt default
+
+_define__(<_AOUT__>,<1>)
+
+_define__(<_M680X0__>,<1>)
+_define__(<_SPARC__>,<1>)
+
+_divert__<>
diff --git a/gnu/usr.bin/as/expr.c b/gnu/usr.bin/as/expr.c
new file mode 100644
index 0000000..413917d
--- /dev/null
+++ b/gnu/usr.bin/as/expr.c
@@ -0,0 +1,1000 @@
+/* expr.c -operands, expressions-
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * This is really a branch office of as-read.c. I split it out to clearly
+ * distinguish the world of expressions from the world of statements.
+ * (It also gives smaller files to re-compile.)
+ * Here, "operand"s are of expressions, not instructions.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: expr.c,v 1.3 1993/10/02 20:57:26 pk Exp $";
+#endif
+
+#include <ctype.h>
+#include <string.h>
+
+#include "as.h"
+
+#include "obstack.h"
+
+#if __STDC__ == 1
+static void clean_up_expression(expressionS *expressionP);
+#else /* __STDC__ */
+static void clean_up_expression(); /* Internal. */
+#endif /* not __STDC__ */
+extern const char EXP_CHARS[]; /* JF hide MD floating pt stuff all the same place */
+extern const char FLT_CHARS[];
+
+#ifdef LOCAL_LABELS_DOLLAR
+extern int local_label_defined[];
+#endif
+
+/*
+ * Build any floating-point literal here.
+ * Also build any bignum literal here.
+ */
+
+/* LITTLENUM_TYPE generic_buffer[6]; */ /* JF this is a hack */
+/* Seems atof_machine can backscan through generic_bignum and hit whatever
+ happens to be loaded before it in memory. And its way too complicated
+ for me to fix right. Thus a hack. JF: Just make generic_bignum bigger,
+ and never write into the early words, thus they'll always be zero.
+ I hate Dean's floating-point code. Bleh.
+ */
+LITTLENUM_TYPE generic_bignum[SIZE_OF_LARGE_NUMBER+6];
+FLONUM_TYPE generic_floating_point_number =
+{
+ &generic_bignum[6], /* low (JF: Was 0) */
+ &generic_bignum[SIZE_OF_LARGE_NUMBER+6 - 1], /* high JF: (added +6) */
+ 0, /* leader */
+ 0, /* exponent */
+ 0 /* sign */
+ };
+/* If nonzero, we've been asked to assemble nan, +inf or -inf */
+int generic_floating_point_magic;
+
+/*
+ * Summary of operand().
+ *
+ * in: Input_line_pointer points to 1st char of operand, which may
+ * be a space.
+ *
+ * out: A expressionS. X_seg determines how to understand the rest of the
+ * expressionS.
+ * The operand may have been empty: in this case X_seg == SEG_ABSENT.
+ * Input_line_pointer->(next non-blank) char after operand.
+ *
+ */
+
+static segT
+ operand (expressionP)
+register expressionS * expressionP;
+{
+ register char c;
+ register char *name; /* points to name of symbol */
+ register symbolS * symbolP; /* Points to symbol */
+
+ extern const char hex_value[]; /* In hex_value.c */
+
+#ifdef PIC
+/* XXX */ expressionP->X_got_symbol = 0;
+#endif
+ SKIP_WHITESPACE(); /* Leading whitespace is part of operand. */
+ c = * input_line_pointer ++; /* Input_line_pointer->past char in c. */
+ if (isdigit(c) || (c == 'H' && input_line_pointer[0] == '\''))
+ {
+ register valueT number; /* offset or (absolute) value */
+ register short int digit; /* value of next digit in current radix */
+ /* invented for humans only, hope */
+ /* optimising compiler flushes it! */
+ register short int radix; /* 2, 8, 10 or 16 */
+ /* 0 means we saw start of a floating- */
+ /* point constant. */
+ register short int maxdig = 0;/* Highest permitted digit value. */
+ register int too_many_digits = 0; /* If we see >= this number of */
+ /* digits, assume it is a bignum. */
+ register char * digit_2; /*->2nd digit of number. */
+ int small; /* TRUE if fits in 32 bits. */
+
+
+ if (c == 'H' || c == '0') { /* non-decimal radix */
+ if ((c = *input_line_pointer ++) == 'x' || c == 'X' || c == '\'') {
+ c = *input_line_pointer ++; /* read past "0x" or "0X" or H' */
+ maxdig = radix = 16;
+ too_many_digits = 9;
+ } else {
+ /* If it says '0f' and the line ends or it DOESN'T look like
+ a floating point #, its a local label ref. DTRT */
+ /* likewise for the b's. xoxorich. */
+ if ((c == 'f' || c == 'b' || c == 'B')
+ && (!*input_line_pointer ||
+ (!strchr("+-.0123456789",*input_line_pointer) &&
+ !strchr(EXP_CHARS,*input_line_pointer)))) {
+ maxdig = radix = 10;
+ too_many_digits = 11;
+ c = '0';
+ input_line_pointer -= 2;
+
+ } else if (c == 'b' || c == 'B') {
+ c = *input_line_pointer++;
+ maxdig = radix = 2;
+ too_many_digits = 33;
+
+ } else if (c && strchr(FLT_CHARS,c)) {
+ radix = 0; /* Start of floating-point constant. */
+ /* input_line_pointer->1st char of number. */
+ expressionP->X_add_number = -(isupper(c) ? tolower(c) : c);
+
+ } else { /* By elimination, assume octal radix. */
+ radix = maxdig = 8;
+ too_many_digits = 11;
+ }
+ } /* c == char after "0" or "0x" or "0X" or "0e" etc. */
+ } else {
+ maxdig = radix = 10;
+ too_many_digits = 11;
+ } /* if operand starts with a zero */
+
+ if (radix) { /* Fixed-point integer constant. */
+ /* May be bignum, or may fit in 32 bits. */
+ /*
+ * Most numbers fit into 32 bits, and we want this case to be fast.
+ * So we pretend it will fit into 32 bits. If, after making up a 32
+ * bit number, we realise that we have scanned more digits than
+ * comfortably fit into 32 bits, we re-scan the digits coding
+ * them into a bignum. For decimal and octal numbers we are conservative: some
+ * numbers may be assumed bignums when in fact they do fit into 32 bits.
+ * Numbers of any radix can have excess leading zeros: we strive
+ * to recognise this and cast them back into 32 bits.
+ * We must check that the bignum really is more than 32
+ * bits, and change it back to a 32-bit number if it fits.
+ * The number we are looking for is expected to be positive, but
+ * if it fits into 32 bits as an unsigned number, we let it be a 32-bit
+ * number. The cavalier approach is for speed in ordinary cases.
+ */
+ digit_2 = input_line_pointer;
+ for (number=0; (digit=hex_value[c])<maxdig; c = * input_line_pointer ++)
+ {
+ number = number * radix + digit;
+ }
+ /* C contains character after number. */
+ /* Input_line_pointer->char after C. */
+ small = input_line_pointer - digit_2 < too_many_digits;
+ if (!small)
+ {
+ /*
+ * We saw a lot of digits. Manufacture a bignum the hard way.
+ */
+ LITTLENUM_TYPE *leader; /*->high order littlenum of the bignum. */
+ LITTLENUM_TYPE *pointer; /*->littlenum we are frobbing now. */
+ long carry;
+
+ leader = generic_bignum;
+ generic_bignum[0] = 0;
+ generic_bignum[1] = 0;
+ /* We could just use digit_2, but lets be mnemonic. */
+ input_line_pointer = --digit_2; /*->1st digit. */
+ c = *input_line_pointer++;
+ for (; (carry = hex_value[c]) < maxdig; c = *input_line_pointer++)
+ {
+ for (pointer = generic_bignum;
+ pointer <= leader;
+ pointer++)
+ {
+ long work;
+
+ work = carry + radix * *pointer;
+ *pointer = work & LITTLENUM_MASK;
+ carry = work >> LITTLENUM_NUMBER_OF_BITS;
+ }
+ if (carry)
+ {
+ if (leader < generic_bignum + SIZE_OF_LARGE_NUMBER - 1)
+ { /* Room to grow a longer bignum. */
+ *++leader = carry;
+ }
+ }
+ }
+ /* Again, C is char after number, */
+ /* input_line_pointer->after C. */
+ know(sizeof (int) * 8 == 32);
+ know(LITTLENUM_NUMBER_OF_BITS == 16);
+ /* Hence the constant "2" in the next line. */
+ if (leader < generic_bignum + 2)
+ { /* Will fit into 32 bits. */
+ number =
+ ((generic_bignum[1] & LITTLENUM_MASK) << LITTLENUM_NUMBER_OF_BITS)
+ | (generic_bignum[0] & LITTLENUM_MASK);
+ small = 1;
+ }
+ else
+ {
+ number = leader - generic_bignum + 1; /* Number of littlenums in the bignum. */
+ }
+ }
+ if (small)
+ {
+ /*
+ * Here with number, in correct radix. c is the next char.
+ * Note that unlike Un*x, we allow "011f" "0x9f" to
+ * both mean the same as the (conventional) "9f". This is simply easier
+ * than checking for strict canonical form. Syntax sux!
+ */
+ if (number<10)
+ {
+ if (0
+#ifdef LOCAL_LABELS_FB
+ || c == 'b'
+#endif
+#ifdef LOCAL_LABELS_DOLLAR
+ || (c == '$' && local_label_defined[number])
+#endif
+ )
+ {
+ /*
+ * Backward ref to local label.
+ * Because it is backward, expect it to be DEFINED.
+ */
+ /*
+ * Construct a local label.
+ */
+ name = local_label_name ((int)number, 0);
+ if (((symbolP = symbol_find(name)) != NULL) /* seen before */
+ && (S_IS_DEFINED(symbolP))) /* symbol is defined: OK */
+ { /* Expected path: symbol defined. */
+ /* Local labels are never absolute. Don't waste time checking absoluteness. */
+ know(SEG_NORMAL(S_GET_SEGMENT(symbolP)));
+
+ expressionP->X_add_symbol = symbolP;
+ expressionP->X_add_number = 0;
+ expressionP->X_seg = S_GET_SEGMENT(symbolP);
+ }
+ else
+ { /* Either not seen or not defined. */
+ as_bad("Backw. ref to unknown label \"%d:\", 0 assumed.",
+ number);
+ expressionP->X_add_number = 0;
+ expressionP->X_seg = SEG_ABSOLUTE;
+ }
+ }
+ else
+ {
+ if (0
+#ifdef LOCAL_LABELS_FB
+ || c == 'f'
+#endif
+#ifdef LOCAL_LABELS_DOLLAR
+ || (c == '$' && !local_label_defined[number])
+#endif
+ )
+ {
+ /*
+ * Forward reference. Expect symbol to be undefined or
+ * unknown. Undefined: seen it before. Unknown: never seen
+ * it in this pass.
+ * Construct a local label name, then an undefined symbol.
+ * Don't create a XSEG frag for it: caller may do that.
+ * Just return it as never seen before.
+ */
+ name = local_label_name((int)number, 1);
+ symbolP = symbol_find_or_make(name);
+ /* We have no need to check symbol properties. */
+#ifndef MANY_SEGMENTS
+ /* Since "know" puts its arg into a "string", we
+ can't have newlines in the argument. */
+ know(S_GET_SEGMENT(symbolP) == SEG_UNKNOWN || S_GET_SEGMENT(symbolP) == SEG_TEXT || S_GET_SEGMENT(symbolP) == SEG_DATA);
+#endif
+ expressionP->X_add_symbol = symbolP;
+ expressionP->X_seg = SEG_UNKNOWN;
+ expressionP->X_subtract_symbol = NULL;
+ expressionP->X_add_number = 0;
+ }
+ else
+ { /* Really a number, not a local label. */
+ expressionP->X_add_number = number;
+ expressionP->X_seg = SEG_ABSOLUTE;
+ input_line_pointer--; /* Restore following character. */
+ } /* if (c == 'f') */
+ } /* if (c == 'b') */
+ }
+ else
+ { /* Really a number. */
+ expressionP->X_add_number = number;
+ expressionP->X_seg = SEG_ABSOLUTE;
+ input_line_pointer--; /* Restore following character. */
+ } /* if (number<10) */
+ }
+ else
+ {
+ expressionP->X_add_number = number;
+ expressionP->X_seg = SEG_BIG;
+ input_line_pointer --; /*->char following number. */
+ } /* if (small) */
+ } /* (If integer constant) */
+ else
+ { /* input_line_pointer->*/
+ /* floating-point constant. */
+ int error_code;
+
+ error_code = atof_generic
+ (& input_line_pointer, ".", EXP_CHARS,
+ & generic_floating_point_number);
+
+ if (error_code)
+ {
+ if (error_code == ERROR_EXPONENT_OVERFLOW)
+ {
+ as_bad("Bad floating-point constant: exponent overflow, probably assembling junk");
+ }
+ else
+ {
+ as_bad("Bad floating-point constant: unknown error code=%d.", error_code);
+ }
+ }
+ expressionP->X_seg = SEG_BIG;
+ /* input_line_pointer->just after constant, */
+ /* which may point to whitespace. */
+ know(expressionP->X_add_number < 0); /* < 0 means "floating point". */
+ } /* if (not floating-point constant) */
+ }
+ else if (c == '.' && !is_part_of_name(*input_line_pointer)) {
+ extern struct obstack frags;
+
+ /*
+ JF: '.' is pseudo symbol with value of current location in current
+ segment...
+ */
+ symbolP = symbol_new("\001L0",
+ now_seg,
+ (valueT)(obstack_next_free(&frags)-frag_now->fr_literal),
+ frag_now);
+
+ expressionP->X_add_number=0;
+ expressionP->X_add_symbol=symbolP;
+ expressionP->X_seg = now_seg;
+
+ } else if (is_name_beginner(c)) { /* here if did not begin with a digit */
+
+ /*
+ * Identifier begins here.
+ * This is kludged for speed, so code is repeated.
+ */
+ name = input_line_pointer - 1;
+ c = get_symbol_end();
+ symbolP = symbol_find_or_make(name);
+ /*
+ * If we have an absolute symbol or a reg, then we know its value now.
+ */
+ expressionP->X_seg = S_GET_SEGMENT(symbolP);
+ switch (expressionP->X_seg)
+ {
+ case SEG_ABSOLUTE:
+ case SEG_REGISTER:
+ expressionP->X_add_number = S_GET_VALUE(symbolP);
+ break;
+
+ default:
+ expressionP->X_add_number = 0;
+#ifdef PIC
+ if (symbolP == GOT_symbol) {
+ expressionP->X_got_symbol = symbolP;
+ got_referenced = 1;
+ } else
+#endif
+ expressionP->X_add_symbol = symbolP;
+ }
+ *input_line_pointer = c;
+ expressionP->X_subtract_symbol = NULL;
+ } else if (c == '(' || c == '[') {/* didn't begin with digit & not a name */
+ (void)expression(expressionP);
+ /* Expression() will pass trailing whitespace */
+ if (c == '(' && *input_line_pointer++ != ')' ||
+ c == '[' && *input_line_pointer++ != ']') {
+ as_bad("Missing ')' assumed");
+ input_line_pointer--;
+ }
+ /* here with input_line_pointer->char after "(...)" */
+ } else if (c == '~' || c == '-' || c == '+') {
+ /* unary operator: hope for SEG_ABSOLUTE */
+ switch (operand (expressionP)) {
+ case SEG_ABSOLUTE:
+ /* input_line_pointer->char after operand */
+ if (c == '-') {
+ expressionP->X_add_number = - expressionP->X_add_number;
+ /*
+ * Notice: '-' may overflow: no warning is given. This is compatible
+ * with other people's assemblers. Sigh.
+ */
+ } else if (c == '~') {
+ expressionP->X_add_number = ~ expressionP->X_add_number;
+ } else if (c != '+') {
+ know(0);
+ } /* switch on unary operator */
+ break;
+
+ default: /* unary on non-absolute is unsuported */
+ if (!SEG_NORMAL(operand(expressionP)))
+ {
+ as_bad("Unary operator %c ignored because bad operand follows", c);
+ break;
+ }
+ /* Fall through for normal segments ****/
+ case SEG_PASS1:
+ case SEG_UNKNOWN:
+ if (c == '-') { /* JF I hope this hack works */
+ expressionP->X_subtract_symbol=expressionP->X_add_symbol;
+ expressionP->X_add_symbol=0;
+ expressionP->X_seg=SEG_DIFFERENCE;
+ break;
+ }
+ /* Expression undisturbed from operand(). */
+ }
+ }
+ else if (c == '\'')
+ {
+ /*
+ * Warning: to conform to other people's assemblers NO ESCAPEMENT is permitted
+ * for a single quote. The next character, parity errors and all, is taken
+ * as the value of the operand. VERY KINKY.
+ */
+ expressionP->X_add_number = * input_line_pointer ++;
+ expressionP->X_seg = SEG_ABSOLUTE;
+ }
+ else
+ {
+ /* can't imagine any other kind of operand */
+ expressionP->X_seg = SEG_ABSENT;
+ input_line_pointer --;
+ md_operand (expressionP);
+ }
+ /*
+ * It is more 'efficient' to clean up the expressions when they are created.
+ * Doing it here saves lines of code.
+ */
+ clean_up_expression(expressionP);
+ SKIP_WHITESPACE(); /*->1st char after operand. */
+ know(*input_line_pointer != ' ');
+ return(expressionP->X_seg);
+} /* operand() */
+
+/* Internal. Simplify a struct expression for use by expr() */
+
+/*
+ * In: address of a expressionS.
+ * The X_seg field of the expressionS may only take certain values.
+ * Now, we permit SEG_PASS1 to make code smaller & faster.
+ * Elsewise we waste time special-case testing. Sigh. Ditto SEG_ABSENT.
+ * Out: expressionS may have been modified:
+ * 'foo-foo' symbol references cancelled to 0,
+ * which changes X_seg from SEG_DIFFERENCE to SEG_ABSOLUTE;
+ * Unused fields zeroed to help expr().
+ */
+
+static void
+ clean_up_expression (expressionP)
+register expressionS *expressionP;
+{
+ switch (expressionP->X_seg) {
+ case SEG_ABSENT:
+ case SEG_PASS1:
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_subtract_symbol = NULL;
+ expressionP->X_add_number = 0;
+ break;
+
+ case SEG_BIG:
+ case SEG_ABSOLUTE:
+ expressionP->X_subtract_symbol = NULL;
+ expressionP->X_add_symbol = NULL;
+ break;
+
+ case SEG_UNKNOWN:
+ expressionP->X_subtract_symbol = NULL;
+ break;
+
+ case SEG_DIFFERENCE:
+ /*
+ * It does not hurt to 'cancel' NULL == NULL
+ * when comparing symbols for 'eq'ness.
+ * It is faster to re-cancel them to NULL
+ * than to check for this special case.
+ */
+ if (expressionP->X_subtract_symbol == expressionP->X_add_symbol
+ || (expressionP->X_subtract_symbol
+ && expressionP->X_add_symbol
+ && expressionP->X_subtract_symbol->sy_frag == expressionP->X_add_symbol->sy_frag
+ && S_GET_VALUE(expressionP->X_subtract_symbol) == S_GET_VALUE(expressionP->X_add_symbol))) {
+ expressionP->X_subtract_symbol = NULL;
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_seg = SEG_ABSOLUTE;
+ }
+ break;
+
+ case SEG_REGISTER:
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_subtract_symbol = NULL;
+ break;
+
+ default:
+ if (SEG_NORMAL(expressionP->X_seg)) {
+ expressionP->X_subtract_symbol = NULL;
+ }
+ else {
+ BAD_CASE (expressionP->X_seg);
+ }
+ break;
+ }
+} /* clean_up_expression() */
+
+/*
+ * expr_part ()
+ *
+ * Internal. Made a function because this code is used in 2 places.
+ * Generate error or correct X_?????_symbol of expressionS.
+ */
+
+/*
+ * symbol_1 += symbol_2 ... well ... sort of.
+ */
+
+static segT
+ expr_part (symbol_1_PP, symbol_2_P)
+symbolS ** symbol_1_PP;
+symbolS * symbol_2_P;
+{
+ segT return_value;
+#ifndef MANY_SEGMENTS
+ know((* symbol_1_PP) == NULL || (S_GET_SEGMENT(*symbol_1_PP) == SEG_TEXT) || (S_GET_SEGMENT(*symbol_1_PP) == SEG_DATA) || (S_GET_SEGMENT(*symbol_1_PP) == SEG_BSS) || (!S_IS_DEFINED(* symbol_1_PP)));
+ know(symbol_2_P == NULL || (S_GET_SEGMENT(symbol_2_P) == SEG_TEXT) || (S_GET_SEGMENT(symbol_2_P) == SEG_DATA) || (S_GET_SEGMENT(symbol_2_P) == SEG_BSS) || (!S_IS_DEFINED(symbol_2_P)));
+#endif
+ if (* symbol_1_PP)
+ {
+ if (!S_IS_DEFINED(* symbol_1_PP))
+ {
+ if (symbol_2_P)
+ {
+ return_value = SEG_PASS1;
+ * symbol_1_PP = NULL;
+ }
+ else
+ {
+ know(!S_IS_DEFINED(* symbol_1_PP));
+ return_value = SEG_UNKNOWN;
+ }
+ }
+ else
+ {
+ if (symbol_2_P)
+ {
+ if (!S_IS_DEFINED(symbol_2_P))
+ {
+ * symbol_1_PP = NULL;
+ return_value = SEG_PASS1;
+ }
+ else
+ {
+ /* {seg1} - {seg2} */
+ as_bad("Expression too complex, 2 symbols forgotten: \"%s\" \"%s\"",
+ S_GET_NAME(* symbol_1_PP), S_GET_NAME(symbol_2_P));
+ * symbol_1_PP = NULL;
+ return_value = SEG_ABSOLUTE;
+ }
+ }
+ else
+ {
+ return_value = S_GET_SEGMENT(* symbol_1_PP);
+ }
+ }
+ }
+ else
+ { /* (* symbol_1_PP) == NULL */
+ if (symbol_2_P)
+ {
+ * symbol_1_PP = symbol_2_P;
+ return_value = S_GET_SEGMENT(symbol_2_P);
+ }
+ else
+ {
+ * symbol_1_PP = NULL;
+ return_value = SEG_ABSOLUTE;
+ }
+ }
+#ifndef MANY_SEGMENTS
+ know(return_value == SEG_ABSOLUTE || return_value == SEG_TEXT || return_value == SEG_DATA || return_value == SEG_BSS || return_value == SEG_UNKNOWN || return_value == SEG_PASS1);
+#endif
+ know((*symbol_1_PP) == NULL || (S_GET_SEGMENT(*symbol_1_PP) == return_value));
+ return (return_value);
+} /* expr_part() */
+
+void ps (s)
+symbolS *s;
+{
+ fprintf (stdout, "%s type %s%s",
+ S_GET_NAME(s),
+ S_IS_EXTERNAL(s) ? "EXTERNAL " : "",
+ segment_name(S_GET_SEGMENT(s)));
+}
+void pe (e)
+expressionS *e;
+{
+ fprintf (stdout, " segment %s\n", segment_name (e->X_seg));
+ fprintf (stdout, " add_number %d (%x)\n",
+ e->X_add_number, e->X_add_number);
+ if (e->X_add_symbol) {
+ fprintf (stdout, " add_symbol ");
+ ps (e->X_add_symbol);
+ fprintf (stdout, "\n");
+ }
+ if (e->X_subtract_symbol) {
+ fprintf (stdout, " sub_symbol ");
+ ps (e->X_subtract_symbol);
+ fprintf (stdout, "\n");
+ }
+}
+
+/* Expression parser. */
+
+/*
+ * We allow an empty expression, and just assume (absolute,0) silently.
+ * Unary operators and parenthetical expressions are treated as operands.
+ * As usual, Q == quantity == operand, O == operator, X == expression mnemonics.
+ *
+ * We used to do a aho/ullman shift-reduce parser, but the logic got so
+ * warped that I flushed it and wrote a recursive-descent parser instead.
+ * Now things are stable, would anybody like to write a fast parser?
+ * Most expressions are either register (which does not even reach here)
+ * or 1 symbol. Then "symbol+constant" and "symbol-symbol" are common.
+ * So I guess it doesn't really matter how inefficient more complex expressions
+ * are parsed.
+ *
+ * After expr(RANK,resultP) input_line_pointer->operator of rank <= RANK.
+ * Also, we have consumed any leading or trailing spaces (operand does that)
+ * and done all intervening operators.
+ */
+
+typedef enum
+{
+ O_illegal, /* (0) what we get for illegal op */
+
+ O_multiply, /* (1) * */
+ O_divide, /* (2) / */
+ O_modulus, /* (3) % */
+ O_left_shift, /* (4) < */
+ O_right_shift, /* (5) > */
+ O_bit_inclusive_or, /* (6) | */
+ O_bit_or_not, /* (7) ! */
+ O_bit_exclusive_or, /* (8) ^ */
+ O_bit_and, /* (9) & */
+ O_add, /* (10) + */
+ O_subtract /* (11) - */
+ }
+operatorT;
+
+#define __ O_illegal
+
+static const operatorT op_encoding[256] = { /* maps ASCII->operators */
+
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+
+ __, O_bit_or_not, __, __, __, O_modulus, O_bit_and, __,
+ __, __, O_multiply, O_add, __, O_subtract, __, O_divide,
+ __, __, __, __, __, __, __, __,
+ __, __, __, __, O_left_shift, __, O_right_shift, __,
+ __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, O_bit_exclusive_or, __,
+ __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __,
+ __, __, __, __, O_bit_inclusive_or, __, __, __,
+
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __
+ };
+
+
+/*
+ * Rank Examples
+ * 0 operand, (expression)
+ * 1 + -
+ * 2 & ^ ! |
+ * 3 * / % << >>
+ */
+static const operator_rankT
+ op_rank[] = { 0, 3, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1 };
+
+/* Return resultP->X_seg. */
+segT expr(rank, resultP)
+ register operator_rankT rank; /* Larger # is higher rank. */
+ register expressionS *resultP; /* Deliver result here. */
+{
+ expressionS right;
+ register operatorT op_left;
+ register char c_left; /* 1st operator character. */
+ register operatorT op_right;
+ register char c_right;
+
+ know(rank >= 0);
+ (void) operand(resultP);
+ know(*input_line_pointer != ' '); /* Operand() gobbles spaces. */
+ c_left = *input_line_pointer; /* Potential operator character. */
+ op_left = op_encoding[c_left];
+
+ while (op_left != O_illegal && op_rank[(int) op_left] > rank) {
+ input_line_pointer++; /*->after 1st character of operator. */
+
+ /* Operators "<<" and ">>" have 2 characters. */
+ if (*input_line_pointer == c_left && (c_left == '<' || c_left == '>')) {
+ input_line_pointer ++;
+ } /*->after operator. */
+ if (SEG_ABSENT == expr (op_rank[(int) op_left], &right)) {
+ as_warn("Missing operand value assumed absolute 0.");
+ resultP->X_add_number = 0;
+ resultP->X_subtract_symbol = NULL;
+ resultP->X_add_symbol = NULL;
+ resultP->X_seg = SEG_ABSOLUTE;
+ }
+
+ know(*input_line_pointer != ' ');
+ c_right = *input_line_pointer;
+ op_right = op_encoding[c_right];
+
+ if (*input_line_pointer == c_right && (c_right == '<' || c_right == '>')) {
+ input_line_pointer ++;
+ } /*->after operator. */
+
+ know((int) op_right == 0 || op_rank[(int) op_right] <= op_rank[(int) op_left]);
+ /* input_line_pointer->after right-hand quantity. */
+ /* left-hand quantity in resultP */
+ /* right-hand quantity in right. */
+ /* operator in op_left. */
+ if (resultP->X_seg == SEG_PASS1 || right.X_seg == SEG_PASS1) {
+ resultP->X_seg = SEG_PASS1;
+ } else {
+ if (resultP->X_seg == SEG_BIG) {
+ as_warn("Left operand of %c is a %s. Integer 0 assumed.",
+ c_left, resultP->X_add_number > 0 ? "bignum" : "float");
+ resultP->X_seg = SEG_ABSOLUTE;
+ resultP->X_add_symbol = 0;
+ resultP->X_subtract_symbol = 0;
+ resultP->X_add_number = 0;
+ }
+ if (right.X_seg == SEG_BIG) {
+ as_warn("Right operand of %c is a %s. Integer 0 assumed.",
+ c_left, right.X_add_number > 0 ? "bignum" : "float");
+ right.X_seg = SEG_ABSOLUTE;
+ right.X_add_symbol = 0;
+ right.X_subtract_symbol = 0;
+ right.X_add_number = 0;
+ }
+ if (op_left == O_subtract) {
+ /*
+ * Convert - into + by exchanging symbols and negating number.
+ * I know -infinity can't be negated in 2's complement:
+ * but then it can't be subtracted either. This trick
+ * does not cause any further inaccuracy.
+ */
+
+ register symbolS * symbolP;
+
+ right.X_add_number = - right.X_add_number;
+ symbolP = right.X_add_symbol;
+ right.X_add_symbol = right.X_subtract_symbol;
+ right.X_subtract_symbol = symbolP;
+ if (symbolP) {
+ right.X_seg = SEG_DIFFERENCE;
+ }
+ op_left = O_add;
+ }
+
+ if (op_left == O_add) {
+ segT seg1;
+ segT seg2;
+#ifndef MANY_SEGMENTS
+ know(resultP->X_seg == SEG_DATA
+ || resultP->X_seg == SEG_TEXT
+ || resultP->X_seg == SEG_BSS
+ || resultP->X_seg == SEG_UNKNOWN
+ || resultP->X_seg == SEG_DIFFERENCE
+ || resultP->X_seg == SEG_ABSOLUTE
+ || resultP->X_seg == SEG_PASS1);
+ know(right.X_seg == SEG_DATA
+ || right.X_seg == SEG_TEXT
+ || right.X_seg == SEG_BSS
+ || right.X_seg == SEG_UNKNOWN
+ || right.X_seg == SEG_DIFFERENCE
+ || right.X_seg == SEG_ABSOLUTE
+ || right.X_seg == SEG_PASS1);
+#endif
+ clean_up_expression(& right);
+ clean_up_expression(resultP);
+
+#ifdef PIC
+/* XXX - kludge here to accomodate "_GLOBAL_OFFSET_TABLE + (x - y)"
+ * expressions: this only works for this special case, the
+ * _GLOBAL_OFFSET_TABLE thing *must* be the left operand, the whole
+ * expression is given the segment of right expression (always a DIFFERENCE,
+ * which should get resolved by fixup_segment())
+ */
+ if (resultP->X_got_symbol) {
+ resultP->X_add_symbol = right.X_add_symbol;
+ resultP->X_subtract_symbol = right.X_subtract_symbol;
+ seg1 = S_GET_SEGMENT(right.X_add_symbol);
+ seg2 = S_GET_SEGMENT(right.X_subtract_symbol);
+ resultP->X_seg = right.X_seg;
+ } else {
+#endif
+ seg1 = expr_part(&resultP->X_add_symbol, right.X_add_symbol);
+ seg2 = expr_part(&resultP->X_subtract_symbol, right.X_subtract_symbol);
+#ifdef PIC
+ }
+#endif
+ if (seg1 == SEG_PASS1 || seg2 == SEG_PASS1) {
+ need_pass_2 = 1;
+ resultP->X_seg = SEG_PASS1;
+ } else if (seg2 == SEG_ABSOLUTE)
+ resultP->X_seg = seg1;
+ else if (seg1 != SEG_UNKNOWN
+ && seg1 != SEG_ABSOLUTE
+ && seg2 != SEG_UNKNOWN
+ && seg1 != seg2) {
+ know(seg2 != SEG_ABSOLUTE);
+ know(resultP->X_subtract_symbol);
+#ifndef MANY_SEGMENTS
+ know(seg1 == SEG_TEXT || seg1 == SEG_DATA || seg1 == SEG_BSS);
+ know(seg2 == SEG_TEXT || seg2 == SEG_DATA || seg2 == SEG_BSS);
+#endif
+ know(resultP->X_add_symbol);
+ know(resultP->X_subtract_symbol);
+ as_bad("Expression too complex: forgetting %s - %s",
+ S_GET_NAME(resultP->X_add_symbol),
+ S_GET_NAME(resultP->X_subtract_symbol));
+ resultP->X_seg = SEG_ABSOLUTE;
+ /* Clean_up_expression() will do the rest. */
+ } else
+ resultP->X_seg = SEG_DIFFERENCE;
+
+ resultP->X_add_number += right.X_add_number;
+ clean_up_expression(resultP);
+ } else { /* Not +. */
+ if (resultP->X_seg == SEG_UNKNOWN || right.X_seg == SEG_UNKNOWN) {
+ resultP->X_seg = SEG_PASS1;
+ need_pass_2 = 1;
+ } else {
+ resultP->X_subtract_symbol = NULL;
+ resultP->X_add_symbol = NULL;
+
+ /* Will be SEG_ABSOLUTE. */
+ if (resultP->X_seg != SEG_ABSOLUTE || right.X_seg != SEG_ABSOLUTE) {
+ as_bad("Relocation error. Absolute 0 assumed.");
+ resultP->X_seg = SEG_ABSOLUTE;
+ resultP->X_add_number = 0;
+ } else {
+ switch (op_left) {
+ case O_bit_inclusive_or:
+ resultP->X_add_number |= right.X_add_number;
+ break;
+
+ case O_modulus:
+ if (right.X_add_number) {
+ resultP->X_add_number %= right.X_add_number;
+ } else {
+ as_warn("Division by 0. 0 assumed.");
+ resultP->X_add_number = 0;
+ }
+ break;
+
+ case O_bit_and:
+ resultP->X_add_number &= right.X_add_number;
+ break;
+
+ case O_multiply:
+ resultP->X_add_number *= right.X_add_number;
+ break;
+
+ case O_divide:
+ if (right.X_add_number) {
+ resultP->X_add_number /= right.X_add_number;
+ } else {
+ as_warn("Division by 0. 0 assumed.");
+ resultP->X_add_number = 0;
+ }
+ break;
+
+ case O_left_shift:
+ resultP->X_add_number <<= right.X_add_number;
+ break;
+
+ case O_right_shift:
+ resultP->X_add_number >>= right.X_add_number;
+ break;
+
+ case O_bit_exclusive_or:
+ resultP->X_add_number ^= right.X_add_number;
+ break;
+
+ case O_bit_or_not:
+ resultP->X_add_number |= ~ right.X_add_number;
+ break;
+
+ default:
+ BAD_CASE(op_left);
+ break;
+ } /* switch (operator) */
+ }
+ } /* If we have to force need_pass_2. */
+ } /* If operator was +. */
+ } /* If we didn't set need_pass_2. */
+ op_left = op_right;
+ } /* While next operator is >= this rank. */
+
+ return(resultP->X_seg);
+} /* expr() */
+
+/*
+ * get_symbol_end()
+ *
+ * This lives here because it belongs equally in expr.c & read.c.
+ * Expr.c is just a branch office read.c anyway, and putting it
+ * here lessens the crowd at read.c.
+ *
+ * Assume input_line_pointer is at start of symbol name.
+ * Advance input_line_pointer past symbol name.
+ * Turn that character into a '\0', returning its former value.
+ * This allows a string compare (RMS wants symbol names to be strings)
+ * of the symbol name.
+ * There will always be a char following symbol name, because all good
+ * lines end in end-of-line.
+ */
+char
+ get_symbol_end()
+{
+ register char c;
+
+ while (is_part_of_name(c = *input_line_pointer++)) ;;
+ *--input_line_pointer = 0;
+ return (c);
+}
+
+
+unsigned int get_single_number()
+{
+ expressionS exp;
+ operand(&exp);
+ return exp.X_add_number;
+
+}
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of expr.c */
diff --git a/gnu/usr.bin/as/expr.h b/gnu/usr.bin/as/expr.h
new file mode 100644
index 0000000..2706d4d
--- /dev/null
+++ b/gnu/usr.bin/as/expr.h
@@ -0,0 +1,85 @@
+/* expr.h -> header file for expr.c
+ Copyright (C) 1987, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
+ * $Id: expr.h,v 1.3 1993/10/02 20:57:27 pk Exp $
+ */
+
+
+/*
+ * Abbreviations (mnemonics).
+ *
+ * O operator
+ * Q quantity, operand
+ * X eXpression
+ */
+
+/*
+ * By popular demand, we define a struct to represent an expression.
+ * This will no doubt mutate as expressions become baroque.
+ *
+ * Currently, we support expressions like "foo-bar+42".
+ * In other words we permit a (possibly undefined) minuend, a
+ * (possibly undefined) subtrahend and an (absolute) augend.
+ * RMS says this is so we can have 1-pass assembly for any compiler
+ * emmissions, and a 'case' statement might emit 'undefined1 - undefined2'.
+ *
+ * To simplify table-driven dispatch, we also have a "segment" for the
+ * entire expression. That way we don't require complex reasoning about
+ * whether particular components are defined; and we can change component
+ * semantics without re-working all the dispatch tables in the assembler.
+ * In other words the "type" of an expression is its segment.
+ */
+
+typedef struct {
+ symbolS *X_add_symbol; /* foo */
+ symbolS *X_subtract_symbol; /* bar */
+ symbolS *X_got_symbol; /* got */
+ long X_add_number; /* 42. Must be signed. */
+ segT X_seg; /* What segment (expr type)? */
+}
+expressionS;
+
+/* result should be type (expressionS *). */
+#define expression(result) expr(0,result)
+
+/* If an expression is SEG_BIG, look here */
+/* for its value. These common data may */
+/* be clobbered whenever expr() is called. */
+extern FLONUM_TYPE generic_floating_point_number; /* Flonums returned here. */
+/* Enough to hold most precise flonum. */
+extern LITTLENUM_TYPE generic_bignum[]; /* Bignums returned here. */
+#define SIZE_OF_LARGE_NUMBER (20) /* Number of littlenums in above. */
+
+typedef char operator_rankT;
+
+#if __STDC__ == 1
+
+char get_symbol_end(void);
+segT expr(int rank, expressionS *resultP);
+unsigned int get_single_number(void);
+
+#else /* not __STDC__ */
+
+char get_symbol_end();
+segT expr();
+unsigned int get_single_number();
+
+#endif /* not __STDC__ */
+
+/* end of expr.h */
diff --git a/gnu/usr.bin/as/flo-const.c b/gnu/usr.bin/as/flo-const.c
new file mode 100644
index 0000000..28d8008
--- /dev/null
+++ b/gnu/usr.bin/as/flo-const.c
@@ -0,0 +1,161 @@
+/* flonum_const.c - Useful Flonum constants
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef lint
+static char rcsid[] = "$Id: flo-const.c,v 1.1 1993/10/02 20:57:28 pk Exp $";
+#endif
+
+#include "flonum.h"
+/* JF: I added the last entry to this table, and I'm not
+ sure if its right or not. Could go either way. I wish
+ I really understood this stuff. */
+
+
+const int table_size_of_flonum_powers_of_ten = 11;
+
+static const LITTLENUM_TYPE zero[] = { 1 };
+
+/***********************************************************************\
+ * *
+ * Warning: the low order bits may be WRONG here. *
+ * I took this from a suspect bc(1) script. *
+ * "minus_X"[] is supposed to be 10^(2^-X) expressed in base 2^16. *
+ * The radix point is just AFTER the highest element of the [] *
+ * *
+ * Because bc rounds DOWN for printing (I think), the lowest *
+ * significance littlenums should probably have 1 added to them. *
+ * *
+ \***********************************************************************/
+
+/* JF: If this equals 6553/(2^16)+39321/(2^32)+... it approaches .1 */
+static const LITTLENUM_TYPE minus_1[] = {
+ 39321, 39321, 39321, 39321, 39321, 39321, 39321, 39321, 39321, 39321,
+ 39321, 39321, 39321, 39321, 39321, 39321, 39321, 39321, 39321, 6553 };
+static const LITTLENUM_TYPE plus_1[] = { 10 };
+
+/* JF: If this equals 655/(2^16) + 23592/(2^32) + ... it approaches .01 */
+static const LITTLENUM_TYPE minus_2[] = {
+ 10485, 36700, 62914, 23592, 49807, 10485, 36700, 62914, 23592, 49807,
+ 10485, 36700, 62914, 23592, 49807, 10485, 36700, 62914, 23592, 655 };
+static const LITTLENUM_TYPE plus_2[] = { 100 };
+
+/* This approaches .0001 */
+static const LITTLENUM_TYPE minus_3[] = {
+ 52533, 20027, 37329, 65116, 64067, 60397, 14784, 18979, 33659, 19503,
+ 2726, 9542, 629, 2202, 40475, 10590, 4299, 47815, 36280, 6 };
+static const LITTLENUM_TYPE plus_3[] = { 10000 };
+
+/* JF: this approaches 1e-8 */
+static const LITTLENUM_TYPE minus_4[] = {
+ 22516, 49501, 54293, 19424, 60699, 6716, 24348, 22618, 23904, 21327,
+ 3919, 44703, 19149, 28803, 48959, 6259, 50273, 62237, 42 };
+/* This equals 1525 * 2^16 + 57600 */
+static const LITTLENUM_TYPE plus_4[] = { 57600, 1525 };
+
+/* This approaches 1e-16 */
+static const LITTLENUM_TYPE minus_5[] = {
+ 22199, 45957, 17005, 26266, 10526, 16260, 55017, 35680, 40443, 19789,
+ 17356, 30195, 55905, 28426, 63010, 44197, 1844 };
+static const LITTLENUM_TYPE plus_5[] = { 28609, 34546, 35 };
+
+static const LITTLENUM_TYPE minus_6[] = {
+ 30926, 26518, 13110, 43018, 54982, 48258, 24658, 15209, 63366, 11929,
+ 20069, 43857, 60487, 51 };
+static const LITTLENUM_TYPE plus_6[] = { 61313, 34220, 16731, 11629, 1262 };
+
+static const LITTLENUM_TYPE minus_7[] = {
+ 29819, 14733, 21490, 40602, 31315, 65186, 2695 };
+static const LITTLENUM_TYPE plus_7[] = {
+ 7937, 49002, 60772, 28216, 38893, 55975, 63988, 59711, 20227, 24 };
+
+static const LITTLENUM_TYPE minus_8[] = {
+ 45849, 19069, 18068, 36324, 37948, 48745, 10873, 64360, 15961, 20566,
+ 24178, 15922, 59427, 110 };
+static const LITTLENUM_TYPE plus_8[] = {
+ 15873, 11925, 39177, 991, 14589, 19735, 25347, 65086, 53853, 938,
+ 37209, 47086, 33626, 23253, 32586, 42547, 9731, 59679, 590 };
+
+static const LITTLENUM_TYPE minus_9[] = {
+ 63601, 55221, 43562, 33661, 29067, 28203, 65417, 64352, 22462, 41110,
+ 12570, 28635, 23199, 50572, 28471, 27074, 46375, 64028, 13106, 63700,
+ 32698, 17493, 32420, 34382, 22750, 20681, 12300 };
+static const LITTLENUM_TYPE plus_9[] = {
+ 63564, 61556, 29377, 54467, 18621, 28141, 36415, 61241, 47119, 30026,
+ 19740, 46002, 13541, 61413, 30480, 38664, 32205, 50593, 51112, 48904,
+ 48263, 43814, 286, 30826, 52813, 62575, 61390, 24540, 21495, 5 };
+
+static const LITTLENUM_TYPE minus_10[] = {
+ 50313, 34681, 1464, 25889, 19575, 41125, 17635, 4598, 49708, 13427,
+ 17287, 56115, 53783, 38255, 32415, 17778, 31596, 7557, 20951, 18477,
+ 40353, 1178, 44405, 11837, 11571, 50963, 15649, 11698, 40675, 2308, };
+static const LITTLENUM_TYPE plus_10[] = {
+ 18520, 53764, 54535, 61910, 61962, 59843, 46270, 58053, 12473, 63785,
+ 2449, 43230, 50044, 47595, 10403, 35766, 32607, 1124, 24966, 35044,
+ 25524, 23631, 18826, 14518, 58448, 14562, 49618, 5588, 25396, 28 };
+
+static const LITTLENUM_TYPE minus_11[] = {
+ 6223, 59909, 62437, 59960, 14652, 45336, 48800, 7647, 51962, 37982,
+ 60436, 58176, 26767, 8440, 9831, 48556, 20994, 14148, 6757, 17221,
+ 60624, 46129, 53210, 44085, 54016, 24259, 11232, 21229, 21313, 81, };
+static const LITTLENUM_TYPE plus_11[] = {
+ 36159, 2055, 33615, 61362, 23581, 62454, 9748, 15275, 39284, 58636,
+ 16269, 42793, 47240, 45774, 50861, 48400, 9413, 40281, 4030, 9572,
+ 7984, 33038, 59522, 19450, 40593, 24486, 54320, 6661, 55766, 805, };
+
+/* Shut up complaints about differing pointer types. They only differ
+ in the const attribute, but there isn't any easy way to do this
+ */
+#define X (LITTLENUM_TYPE *)
+
+const FLONUM_TYPE flonum_negative_powers_of_ten[] = {
+ {X zero, X zero, X zero, 0, '+'},
+ {X minus_1, X minus_1 +19, X minus_1 + 19, -20, '+'},
+ {X minus_2, X minus_2 +19, X minus_2 + 19, -20, '+'},
+ {X minus_3, X minus_3 +19, X minus_3 + 19, -20, '+'},
+ {X minus_4, X minus_4 +18, X minus_4 + 18, -20, '+'},
+ {X minus_5, X minus_5 +16, X minus_5 + 16, -20, '+'},
+ {X minus_6, X minus_6 +13, X minus_6 + 13, -20, '+'},
+ {X minus_7, X minus_7 + 6, X minus_7 + 6, -20, '+'},
+ {X minus_8, X minus_8 +13, X minus_8 + 13, -40, '+'},
+ {X minus_9, X minus_9 +26, X minus_9 + 26, -80, '+'},
+ {X minus_10, X minus_10+29, X minus_10 + 29,-136, '+'},
+ {X minus_11, X minus_11+29, X minus_11 + 29,-242, '+'},
+};
+
+const FLONUM_TYPE flonum_positive_powers_of_ten[] = {
+ {X zero, X zero, X zero, 0, '+'},
+ {X plus_1, X plus_1 + 0, X plus_1 + 0, 0, '+'},
+ {X plus_2, X plus_2 + 0, X plus_2 + 0, 0, '+'},
+ {X plus_3, X plus_3 + 0, X plus_3 + 0, 0, '+'},
+ {X plus_4, X plus_4 + 1, X plus_4 + 1, 0, '+'},
+ {X plus_5, X plus_5 + 2, X plus_5 + 2, 1, '+'},
+ {X plus_6, X plus_6 + 4, X plus_6 + 4, 2, '+'},
+ {X plus_7, X plus_7 + 9, X plus_7 + 9, 4, '+'},
+ {X plus_8, X plus_8 + 18, X plus_8 + 18, 8, '+'},
+ {X plus_9, X plus_9 + 29, X plus_9 + 29, 24, '+'},
+ {X plus_10, X plus_10 + 29, X plus_10 + 29, 77, '+'},
+ {X plus_11, X plus_11 + 29, X plus_11 + 29, 183, '+'},
+};
+
+#ifdef HO_VMS
+dummy1()
+{
+}
+#endif
+/* end of flonum_const.c */
diff --git a/gnu/usr.bin/as/flo-copy.c b/gnu/usr.bin/as/flo-copy.c
new file mode 100644
index 0000000..8fcdb52
--- /dev/null
+++ b/gnu/usr.bin/as/flo-copy.c
@@ -0,0 +1,70 @@
+/* flonum_copy.c - copy a flonum
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef lint
+static char rcsid[] = "$Id: flo-copy.c,v 1.1 1993/10/02 20:57:29 pk Exp $";
+#endif
+
+#include "as.h"
+
+void
+ flonum_copy(in, out)
+FLONUM_TYPE *in;
+FLONUM_TYPE *out;
+{
+ int in_length; /* 0 origin */
+ int out_length; /* 0 origin */
+
+ out->sign = in->sign;
+ in_length = in->leader - in->low;
+
+ if (in_length < 0) {
+ out->leader = out->low - 1; /* 0.0 case */
+ } else {
+ out_length = out->high - out->low;
+ /*
+ * Assume no GAPS in packing of littlenums.
+ * I.e. sizeof(array) == sizeof(element) * number_of_elements.
+ */
+ if (in_length <= out_length) {
+ {
+ /*
+ * For defensive programming, zero any high-order littlenums we don't need.
+ * This is destroying evidence and wasting time, so why bother???
+ */
+ if (in_length < out_length) {
+memset((char *)(out->low + in_length + 1), '\0', out_length - in_length);
+ }
+ }
+ memcpy((void *)(out->low), (void *)(in->low), (int)((in_length + 1) * sizeof(LITTLENUM_TYPE)));
+ out->exponent = in->exponent;
+ out->leader = in->leader - in->low + out->low;
+ } else {
+ int shorten; /* 1-origin. Number of littlenums we drop. */
+
+ shorten = in_length - out_length;
+ /* Assume out_length >= 0 ! */
+ memcpy((void *)( out->low), (void *)(in->low + shorten), (int)((out_length + 1) * sizeof(LITTLENUM_TYPE)));
+ out->leader = out->high;
+ out->exponent = in->exponent + shorten;
+ }
+ } /* if any significant bits */
+} /* flonum_copy() */
+
+/* end of flonum_copy.c */
diff --git a/gnu/usr.bin/as/flonum-const.c b/gnu/usr.bin/as/flonum-const.c
new file mode 100644
index 0000000..617e585
--- /dev/null
+++ b/gnu/usr.bin/as/flonum-const.c
@@ -0,0 +1,157 @@
+/* flonum_const.c - Useful Flonum constants
+ Copyright (C) 1987 Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "flonum.h"
+/* JF: I added the last entry to this table, and I'm not
+ sure if its right or not. Could go either way. I wish
+ I really understood this stuff. */
+
+
+const int table_size_of_flonum_powers_of_ten = 11;
+
+static const LITTLENUM_TYPE zero[] = { 1 };
+
+/***********************************************************************\
+* *
+* Warning: the low order bits may be WRONG here. *
+* I took this from a suspect bc(1) script. *
+* "minus_X"[] is supposed to be 10^(2^-X) expressed in base 2^16. *
+* The radix point is just AFTER the highest element of the [] *
+* *
+* Because bc rounds DOWN for printing (I think), the lowest *
+* significance littlenums should probably have 1 added to them. *
+* *
+\***********************************************************************/
+
+/* JF: If this equals 6553/(2^16)+39321/(2^32)+... it approaches .1 */
+static const LITTLENUM_TYPE minus_1 [] = {
+ 39321, 39321, 39321, 39321, 39321, 39321, 39321, 39321, 39321, 39321,
+ 39321, 39321, 39321, 39321, 39321, 39321, 39321, 39321, 39321, 6553 };
+static const LITTLENUM_TYPE plus_1 [] = { 10 };
+
+/* JF: If this equals 655/(2^16) + 23592/(2^32) + ... it approaches .01 */
+static const LITTLENUM_TYPE minus_2 [] = {
+ 10485, 36700, 62914, 23592, 49807, 10485, 36700, 62914, 23592, 49807,
+ 10485, 36700, 62914, 23592, 49807, 10485, 36700, 62914, 23592, 655 };
+static const LITTLENUM_TYPE plus_2 [] = { 100 };
+
+/* This approaches .0001 */
+static const LITTLENUM_TYPE minus_3 [] = {
+ 52533, 20027, 37329, 65116, 64067, 60397, 14784, 18979, 33659, 19503,
+ 2726, 9542, 629, 2202, 40475, 10590, 4299, 47815, 36280, 6 };
+static const LITTLENUM_TYPE plus_3 [] = { 10000 };
+
+/* JF: this approaches 1e-8 */
+static const LITTLENUM_TYPE minus_4 [] = {
+ 22516, 49501, 54293, 19424, 60699, 6716, 24348, 22618, 23904, 21327,
+ 3919, 44703, 19149, 28803, 48959, 6259, 50273, 62237, 42 };
+/* This equals 1525 * 2^16 + 57600 */
+static const LITTLENUM_TYPE plus_4 [] = { 57600, 1525 };
+
+/* This approaches 1e-16 */
+static const LITTLENUM_TYPE minus_5 [] = {
+ 22199, 45957, 17005, 26266, 10526, 16260, 55017, 35680, 40443, 19789,
+ 17356, 30195, 55905, 28426, 63010, 44197, 1844 };
+static const LITTLENUM_TYPE plus_5 [] = { 28609, 34546, 35 };
+
+static const LITTLENUM_TYPE minus_6 [] = {
+ 30926, 26518, 13110, 43018, 54982, 48258, 24658, 15209, 63366, 11929,
+ 20069, 43857, 60487, 51 };
+static const LITTLENUM_TYPE plus_6 [] = { 61313, 34220, 16731, 11629, 1262 };
+
+static const LITTLENUM_TYPE minus_7 [] = {
+ 29819, 14733, 21490, 40602, 31315, 65186, 2695 };
+static const LITTLENUM_TYPE plus_7 [] = {
+ 7937, 49002, 60772, 28216, 38893, 55975, 63988, 59711, 20227, 24 };
+
+static const LITTLENUM_TYPE minus_8 [] = {
+ 45849, 19069, 18068, 36324, 37948, 48745, 10873, 64360, 15961, 20566,
+ 24178, 15922, 59427, 110 };
+static const LITTLENUM_TYPE plus_8 [] = {
+ 15873, 11925, 39177, 991, 14589, 19735, 25347, 65086, 53853, 938,
+ 37209, 47086, 33626, 23253, 32586, 42547, 9731, 59679, 590 };
+
+static const LITTLENUM_TYPE minus_9 [] = {
+ 63601, 55221, 43562, 33661, 29067, 28203, 65417, 64352, 22462, 41110,
+ 12570, 28635, 23199, 50572, 28471, 27074, 46375, 64028, 13106, 63700,
+ 32698, 17493, 32420, 34382, 22750, 20681, 12300 };
+static const LITTLENUM_TYPE plus_9 [] = {
+ 63564, 61556, 29377, 54467, 18621, 28141, 36415, 61241, 47119, 30026,
+ 19740, 46002, 13541, 61413, 30480, 38664, 32205, 50593, 51112, 48904,
+ 48263, 43814, 286, 30826, 52813, 62575, 61390, 24540, 21495, 5 };
+
+static const LITTLENUM_TYPE minus_10 [] = {
+ 50313, 34681, 1464, 25889, 19575, 41125, 17635, 4598, 49708, 13427,
+ 17287, 56115, 53783, 38255, 32415, 17778, 31596, 7557, 20951, 18477,
+ 40353, 1178, 44405, 11837, 11571, 50963, 15649, 11698, 40675, 2308, };
+static const LITTLENUM_TYPE plus_10[] = {
+18520, 53764, 54535, 61910, 61962, 59843, 46270, 58053, 12473, 63785,
+ 2449, 43230, 50044, 47595, 10403, 35766, 32607, 1124, 24966, 35044,
+25524, 23631, 18826, 14518, 58448, 14562, 49618, 5588, 25396, 28 };
+
+static const LITTLENUM_TYPE minus_11 [] = {
+ 6223, 59909, 62437, 59960, 14652, 45336, 48800, 7647, 51962, 37982,
+ 60436, 58176, 26767, 8440, 9831, 48556, 20994, 14148, 6757, 17221,
+ 60624, 46129, 53210, 44085, 54016, 24259, 11232, 21229, 21313, 81, };
+static const LITTLENUM_TYPE plus_11 [] = {
+ 36159, 2055, 33615, 61362, 23581, 62454, 9748, 15275, 39284, 58636,
+ 16269, 42793, 47240, 45774, 50861, 48400, 9413, 40281, 4030, 9572,
+ 7984, 33038, 59522, 19450, 40593, 24486, 54320, 6661, 55766, 805, };
+
+/* Shut up complaints about differing pointer types. They only differ
+ in the const attribute, but there isn't any easy way to do this
+ */
+#define X (LITTLENUM_TYPE *)
+
+const FLONUM_TYPE flonum_negative_powers_of_ten [] = {
+ {X zero, X zero, X zero, 0, '+'},
+ {X minus_1, X minus_1 +19, X minus_1 + 19, -20, '+'},
+ {X minus_2, X minus_2 +19, X minus_2 + 19, -20, '+'},
+ {X minus_3, X minus_3 +19, X minus_3 + 19, -20, '+'},
+ {X minus_4, X minus_4 +18, X minus_4 + 18, -20, '+'},
+ {X minus_5, X minus_5 +16, X minus_5 + 16, -20, '+'},
+ {X minus_6, X minus_6 +13, X minus_6 + 13, -20, '+'},
+ {X minus_7, X minus_7 + 6, X minus_7 + 6, -20, '+'},
+ {X minus_8, X minus_8 +13, X minus_8 + 13, -40, '+'},
+ {X minus_9, X minus_9 +26, X minus_9 + 26, -80, '+'},
+ {X minus_10, X minus_10+29, X minus_10 + 29,-136, '+'},
+ {X minus_11, X minus_11+29, X minus_11 + 29,-242, '+'},
+};
+
+const FLONUM_TYPE flonum_positive_powers_of_ten [] = {
+ {X zero, X zero, X zero, 0, '+'},
+ {X plus_1, X plus_1 + 0, X plus_1 + 0, 0, '+'},
+ {X plus_2, X plus_2 + 0, X plus_2 + 0, 0, '+'},
+ {X plus_3, X plus_3 + 0, X plus_3 + 0, 0, '+'},
+ {X plus_4, X plus_4 + 1, X plus_4 + 1, 0, '+'},
+ {X plus_5, X plus_5 + 2, X plus_5 + 2, 1, '+'},
+ {X plus_6, X plus_6 + 4, X plus_6 + 4, 2, '+'},
+ {X plus_7, X plus_7 + 9, X plus_7 + 9, 4, '+'},
+ {X plus_8, X plus_8 + 18, X plus_8 + 18, 8, '+'},
+ {X plus_9, X plus_9 + 29, X plus_9 + 29, 24, '+'},
+ {X plus_10, X plus_10 + 29, X plus_10 + 29, 77, '+'},
+ {X plus_11, X plus_11 + 29, X plus_11 + 29, 183, '+'},
+};
+
+#ifdef VMS
+dummy1()
+{
+}
+#endif
+/* end: flonum_const.c */
diff --git a/gnu/usr.bin/as/flonum-copy.c b/gnu/usr.bin/as/flonum-copy.c
new file mode 100644
index 0000000..3a51f06
--- /dev/null
+++ b/gnu/usr.bin/as/flonum-copy.c
@@ -0,0 +1,76 @@
+/* flonum_copy.c - copy a flonum
+ Copyright (C) 1987 Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "flonum.h"
+#ifdef USG
+#define bzero(s,n) memset(s,0,n)
+#define bcopy(from,to,n) memcpy(to,from,n)
+#endif
+
+void
+flonum_copy (in, out)
+ FLONUM_TYPE * in;
+ FLONUM_TYPE * out;
+{
+ int in_length; /* 0 origin */
+ int out_length; /* 0 origin */
+
+ out -> sign = in -> sign;
+ in_length = in -> leader - in -> low;
+ if (in_length < 0)
+ {
+ out -> leader = out -> low - 1; /* 0.0 case */
+ }
+ else
+ {
+ out_length = out -> high - out -> low;
+ /*
+ * Assume no GAPS in packing of littlenums.
+ * I.e. sizeof(array) == sizeof(element) * number_of_elements.
+ */
+ if (in_length <= out_length)
+ {
+ {
+ /*
+ * For defensive programming, zero any high-order littlenums we don't need.
+ * This is destroying evidence and wasting time, so why bother???
+ */
+ if (in_length < out_length)
+ {
+ bzero ((char *)(out->low + in_length + 1), out_length - in_length);
+ }
+ }
+ bcopy ((char *)(in->low), (char *)(out->low), (int)((in_length + 1) * sizeof(LITTLENUM_TYPE)));
+ out -> exponent = in -> exponent;
+ out -> leader = in -> leader - in -> low + out -> low;
+ }
+ else
+ {
+ int shorten; /* 1-origin. Number of littlenums we drop. */
+
+ shorten = in_length - out_length;
+ /* Assume out_length >= 0 ! */
+ bcopy ((char *)(in->low + shorten),(char *)( out->low), (int)((out_length + 1) * sizeof(LITTLENUM_TYPE)));
+ out -> leader = out -> high;
+ out -> exponent = in -> exponent + shorten;
+ }
+ } /* if any significant bits */
+}
+
+/* end: flonum_copy.c */
diff --git a/gnu/usr.bin/as/flonum-mult.c b/gnu/usr.bin/as/flonum-mult.c
new file mode 100644
index 0000000..41e90bd
--- /dev/null
+++ b/gnu/usr.bin/as/flonum-mult.c
@@ -0,0 +1,203 @@
+/* flonum_mult.c - multiply two flonums
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of Gas, the GNU Assembler.
+
+ The GNU assembler is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY. No author or distributor
+ accepts responsibility to anyone for the consequences of using it
+ or for whether it serves any particular purpose or works at all,
+ unless he says so in writing. Refer to the GNU Assembler General
+ Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute
+ the GNU Assembler, but only under the conditions described in the
+ GNU Assembler General Public License. A copy of this license is
+ supposed to have been given to you along with the GNU Assembler
+ so you can know your rights and responsibilities. It should be
+ in a file named COPYING. Among other things, the copyright
+ notice and this notice must be preserved on all copies. */
+
+#ifndef lint
+static char rcsid[] = "$Id: flonum-mult.c,v 1.3 1993/10/02 20:57:30 pk Exp $";
+#endif
+
+#include "flonum.h"
+
+/* plan for a . b => p(roduct)
+
+
+ +-------+-------+-/ /-+-------+-------+
+ | a | a | ... | a | a |
+ | A | A-1 | | 1 | 0 |
+ +-------+-------+-/ /-+-------+-------+
+
+
+ +-------+-------+-/ /-+-------+-------+
+ | b | b | ... | b | b |
+ | B | B-1 | | 1 | 0 |
+ +-------+-------+-/ /-+-------+-------+
+
+
+ +-------+-------+-/ /-+-------+-/ /-+-------+-------+
+ | p | p | ... | p | ... | p | p |
+ | A+B+1| A+B | | N | | 1 | 0 |
+ +-------+-------+-/ /-+-------+-/ /-+-------+-------+
+
+ /^\
+ (carry) a .b ... | ... a .b a .b
+ A B | 0 1 0 0
+ |
+ ... | ... a .b
+ | 1 0
+ |
+ | ...
+ |
+ |
+ |
+ | ___
+ | \
+ +----- P = > a .b
+ N /__ i j
+
+ N = 0 ... A+B
+
+ for all i,j where i+j=N
+ [i,j integers > 0]
+
+ a[], b[], p[] may not intersect.
+ Zero length factors signify 0 significant bits: treat as 0.0.
+ 0.0 factors do the right thing.
+ Zero length product OK.
+
+ I chose the ForTran accent "foo[bar]" instead of the C accent "*garply"
+ because I felt the ForTran way was more intuitive. The C way would
+ probably yield better code on most C compilers. Dean Elsner.
+ (C style also gives deeper insight [to me] ... oh well ...)
+ */
+
+void flonum_multip (a, b, product)
+const FLONUM_TYPE *a;
+const FLONUM_TYPE *b;
+FLONUM_TYPE *product;
+{
+ int size_of_a; /* 0 origin */
+ int size_of_b; /* 0 origin */
+ int size_of_product; /* 0 origin */
+ int size_of_sum; /* 0 origin */
+ int extra_product_positions;/* 1 origin */
+ unsigned long work;
+ unsigned long carry;
+ long exponent;
+ LITTLENUM_TYPE * q;
+ long significant; /* TRUE when we emit a non-0 littlenum */
+ /* ForTran accent follows. */
+ int P; /* Scan product low-order -> high. */
+ int N; /* As in sum above. */
+ int A; /* Which [] of a? */
+ int B; /* Which [] of b? */
+
+ if ((a->sign != '-' && a->sign != '+') || (b->sign != '-' && b->sign != '+')) {
+ /* ...
+ Got to fail somehow. Any suggestions? */
+ product->sign=0;
+ return;
+ }
+ product->sign = (a->sign == b->sign) ? '+' : '-';
+ size_of_a = a->leader - a->low;
+ size_of_b = b->leader - b->low;
+ exponent = a->exponent + b->exponent;
+ size_of_product = product->high - product->low;
+ size_of_sum = size_of_a + size_of_b;
+ extra_product_positions = size_of_product - size_of_sum;
+ if (extra_product_positions < 0)
+ {
+ P = extra_product_positions; /* P < 0 */
+ exponent -= extra_product_positions; /* Increases exponent. */
+ }
+ else
+ {
+ P = 0;
+ }
+ carry = 0;
+ significant = 0;
+ for (N = 0;
+ N <= size_of_sum;
+ N++)
+ {
+ work = carry;
+ carry = 0;
+ for (A = 0;
+ A <= N;
+ A ++)
+ {
+ B = N - A;
+ if (A <= size_of_a && B <= size_of_b && B >= 0)
+ {
+#ifdef TRACE
+ printf("a:low[%d.]=%04x b:low[%d.]=%04x work_before=%08x\n", A, a->low[A], B, b->low[B], work);
+#endif
+ work += a->low[A] * b->low[B];
+ carry += work >> LITTLENUM_NUMBER_OF_BITS;
+ work &= LITTLENUM_MASK;
+#ifdef TRACE
+ printf("work=%08x carry=%04x\n", work, carry);
+#endif
+ }
+ }
+ significant |= work;
+ if (significant || P<0)
+ {
+ if (P >= 0)
+ {
+ product->low[P] = work;
+#ifdef TRACE
+ printf("P=%d. work[p]:=%04x\n", P, work);
+#endif
+ }
+ P ++;
+ }
+ else
+ {
+ extra_product_positions ++;
+ exponent ++;
+ }
+ }
+ /*
+ * [P]->position # size_of_sum + 1.
+ * This is where 'carry' should go.
+ */
+#ifdef TRACE
+ printf("final carry =%04x\n", carry);
+#endif
+ if (carry)
+ {
+ if (extra_product_positions > 0)
+ {
+ product->low[P] = carry;
+ }
+ else
+ {
+ /* No room at high order for carry littlenum. */
+ /* Shift right 1 to make room for most significant littlenum. */
+ exponent ++;
+ P --;
+ for (q = product->low + P;
+ q >= product->low;
+ q --)
+ {
+ work = * q;
+ * q = carry;
+ carry = work;
+ }
+ }
+ }
+ else
+ {
+ P --;
+ }
+ product->leader = product->low + P;
+ product->exponent = exponent;
+}
+
+/* end of flonum_mult.c */
diff --git a/gnu/usr.bin/as/flonum.h b/gnu/usr.bin/as/flonum.h
new file mode 100644
index 0000000..a1a8f08
--- /dev/null
+++ b/gnu/usr.bin/as/flonum.h
@@ -0,0 +1,125 @@
+/* flonum.h - Floating point package
+
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
+ * $Id: flonum.h,v 1.3 1993/10/02 20:57:31 pk Exp $
+ */
+
+
+/***********************************************************************\
+ * *
+ * Arbitrary-precision floating point arithmetic. *
+ * *
+ * *
+ * Notation: a floating point number is expressed as *
+ * MANTISSA * (2 ** EXPONENT). *
+ * *
+ * If this offends more traditional mathematicians, then *
+ * please tell me your nomenclature for flonums! *
+ * *
+ \***********************************************************************/
+#if (__STDC__ != 1) && !defined(const)
+#define const /* empty */
+#endif
+
+#include "bignum.h"
+
+/***********************************************************************\
+ * *
+ * Variable precision floating point numbers. *
+ * *
+ * Exponent is the place value of the low littlenum. E.g.: *
+ * If 0: low points to the units littlenum. *
+ * If 1: low points to the LITTLENUM_RADIX littlenum. *
+ * If -1: low points to the 1/LITTLENUM_RADIX littlenum. *
+ * *
+ \***********************************************************************/
+
+/* JF: A sign value of 0 means we have been asked to assemble NaN
+ A sign value of 'P' means we've been asked to assemble +Inf
+ A sign value of 'N' means we've been asked to assemble -Inf
+ */
+struct FLONUM_STRUCT
+{
+ LITTLENUM_TYPE *low; /* low order littlenum of a bignum */
+ LITTLENUM_TYPE *high; /* high order littlenum of a bignum */
+ LITTLENUM_TYPE *leader; /* -> 1st non-zero littlenum */
+ /* If flonum is 0.0, leader == low-1 */
+ long exponent; /* base LITTLENUM_RADIX */
+ char sign; /* '+' or '-' */
+};
+
+typedef struct FLONUM_STRUCT FLONUM_TYPE;
+
+
+/***********************************************************************\
+ * *
+ * Since we can (& do) meet with exponents like 10^5000, it *
+ * is silly to make a table of ~ 10,000 entries, one for each *
+ * power of 10. We keep a table where item [n] is a struct *
+ * FLONUM_FLOATING_POINT representing 10^(2^n). We then *
+ * multiply appropriate entries from this table to get any *
+ * particular power of 10. For the example of 10^5000, a table *
+ * of just 25 entries suffices: 10^(2^-12)...10^(2^+12). *
+ * *
+ \***********************************************************************/
+
+
+extern const FLONUM_TYPE flonum_positive_powers_of_ten[];
+extern const FLONUM_TYPE flonum_negative_powers_of_ten[];
+extern const int table_size_of_flonum_powers_of_ten;
+/* Flonum_XXX_powers_of_ten[] table has */
+/* legal indices from 0 to */
+/* + this number inclusive. */
+
+
+
+/***********************************************************************\
+ * *
+ * Declare worker functions. *
+ * *
+ \***********************************************************************/
+
+#if __STDC__ == 1
+
+int atof_generic(char **address_of_string_pointer,
+ const char *string_of_decimal_marks,
+ const char *string_of_decimal_exponent_marks,
+ FLONUM_TYPE *address_of_generic_floating_point_number);
+
+void flonum_copy(FLONUM_TYPE *in, FLONUM_TYPE *out);
+void flonum_multip(const FLONUM_TYPE *a, const FLONUM_TYPE *b, FLONUM_TYPE *product);
+
+#else /* not __STDC__ */
+
+int atof_generic();
+void flonum_copy();
+void flonum_multip();
+
+#endif /* not __STDC__ */
+
+/***********************************************************************\
+ * *
+ * Declare error codes. *
+ * *
+ \***********************************************************************/
+
+#define ERROR_EXPONENT_OVERFLOW (2)
+
+/* end of flonum.h */
diff --git a/gnu/usr.bin/as/frags.c b/gnu/usr.bin/as/frags.c
new file mode 100644
index 0000000..208aa7d
--- /dev/null
+++ b/gnu/usr.bin/as/frags.c
@@ -0,0 +1,296 @@
+/* frags.c - manage frags -
+
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef lint
+static char rcsid[] = "$Id: frags.c,v 1.3 1993/10/02 20:57:31 pk Exp $";
+#endif
+
+#include "as.h"
+#include "subsegs.h"
+#include "obstack.h"
+
+struct obstack frags; /* All, and only, frags live here. */
+
+fragS zero_address_frag = {
+ 0, /* fr_address */
+ NULL, /* fr_next */
+ 0, /* fr_fix */
+ 0, /* fr_var */
+ 0, /* fr_symbol */
+ 0, /* fr_offset */
+ NULL, /* fr_opcode */
+ rs_fill, /* fr_type */
+ 0, /* fr_subtype */
+ 0, /* fr_pcrel_adjust */
+ 0, /* fr_bsr */
+ 0 /* fr_literal[0] */
+ };
+
+fragS bss_address_frag = {
+ 0, /* fr_address. Gets filled in to make up
+ sy_value-s. */
+ NULL, /* fr_next */
+ 0, /* fr_fix */
+ 0, /* fr_var */
+ 0, /* fr_symbol */
+ 0, /* fr_offset */
+ NULL, /* fr_opcode */
+ rs_fill, /* fr_type */
+ 0, /* fr_subtype */
+ 0, /* fr_pcrel_adjust */
+ 0, /* fr_bsr */
+ 0 /* fr_literal[0] */
+ };
+
+/*
+ * frag_grow()
+ *
+ * Internal.
+ * Try to augment current frag by nchars chars.
+ * If there is no room, close of the current frag with a ".fill 0"
+ * and begin a new frag. Unless the new frag has nchars chars available
+ * do not return. Do not set up any fields of *now_frag.
+ */
+static void frag_grow(nchars)
+unsigned int nchars;
+{
+ if (obstack_room (&frags) < nchars) {
+ unsigned int n,oldn;
+ long oldc;
+
+ frag_wane(frag_now);
+ frag_new(0);
+ oldn=(unsigned)-1;
+ oldc=frags.chunk_size;
+ frags.chunk_size=2*nchars;
+ while ((n=obstack_room(&frags))<nchars && n<oldn) {
+ frag_wane(frag_now);
+ frag_new(0);
+ oldn=n;
+ }
+ frags.chunk_size=oldc;
+ }
+ if (obstack_room (&frags) < nchars)
+ as_fatal("Can't extend frag %d. chars", nchars);
+} /* frag_grow() */
+
+/*
+ * frag_new()
+ *
+ * Call this to close off a completed frag, and start up a new (empty)
+ * frag, in the same subsegment as the old frag.
+ * [frchain_now remains the same but frag_now is updated.]
+ * Because this calculates the correct value of fr_fix by
+ * looking at the obstack 'frags', it needs to know how many
+ * characters at the end of the old frag belong to (the maximal)
+ * fr_var: the rest must belong to fr_fix.
+ * It doesn't actually set up the old frag's fr_var: you may have
+ * set fr_var == 1, but allocated 10 chars to the end of the frag:
+ * in this case you pass old_frags_var_max_size == 10.
+ *
+ * Make a new frag, initialising some components. Link new frag at end
+ * of frchain_now.
+ */
+void frag_new(old_frags_var_max_size)
+int old_frags_var_max_size; /* Number of chars (already allocated on
+ obstack frags) */
+/* in variable_length part of frag. */
+{
+ register fragS * former_last_fragP;
+ /* char *throw_away_pointer; JF unused */
+ register frchainS * frchP;
+ long tmp; /* JF */
+
+ frag_now->fr_fix = (char *) (obstack_next_free (&frags)) -
+ (frag_now->fr_literal) - old_frags_var_max_size;
+ /* Fix up old frag's fr_fix. */
+
+ obstack_finish (&frags);
+ /* This will align the obstack so the */
+ /* next struct we allocate on it will */
+ /* begin at a correct boundary. */
+ frchP = frchain_now;
+ know (frchP);
+ former_last_fragP = frchP->frch_last;
+ know (former_last_fragP);
+ know (former_last_fragP == frag_now);
+ obstack_blank (&frags, SIZEOF_STRUCT_FRAG);
+ /* We expect this will begin at a correct */
+ /* boundary for a struct. */
+ tmp=obstack_alignment_mask(&frags);
+ obstack_alignment_mask(&frags)=0; /* Turn off alignment */
+ /* If we ever hit a machine
+ where strings must be
+ aligned, we Lose Big */
+ frag_now=(fragS *)obstack_finish(&frags);
+ obstack_alignment_mask(&frags)=tmp; /* Restore alignment */
+
+ /* Just in case we don't get zero'd bytes */
+ memset(frag_now, '\0', SIZEOF_STRUCT_FRAG);
+
+ /* obstack_unaligned_done (&frags, &frag_now); */
+ /* know (frags.obstack_c_next_free == frag_now->fr_literal); */
+ /* Generally, frag_now->points to an */
+ /* address rounded up to next alignment. */
+ /* However, characters will add to obstack */
+ /* frags IMMEDIATELY after the struct frag, */
+ /* even if they are not starting at an */
+ /* alignment address. */
+ former_last_fragP->fr_next = frag_now;
+ frchP->frch_last = frag_now;
+
+#ifndef NO_LISTING
+ {
+ extern struct list_info_struct *listing_tail;
+ frag_now->line = listing_tail;
+ }
+#endif
+
+ frag_now->fr_next = NULL;
+} /* frag_new() */
+
+/*
+ * frag_more()
+ *
+ * Start a new frag unless we have n more chars of room in the current frag.
+ * Close off the old frag with a .fill 0.
+ *
+ * Return the address of the 1st char to write into. Advance
+ * frag_now_growth past the new chars.
+ */
+
+char *frag_more (nchars)
+int nchars;
+{
+ register char *retval;
+
+ frag_grow (nchars);
+ retval = obstack_next_free (&frags);
+ obstack_blank_fast (&frags, nchars);
+ return (retval);
+} /* frag_more() */
+
+/*
+ * frag_var()
+ *
+ * Start a new frag unless we have max_chars more chars of room in the current frag.
+ * Close off the old frag with a .fill 0.
+ *
+ * Set up a machine_dependent relaxable frag, then start a new frag.
+ * Return the address of the 1st char of the var part of the old frag
+ * to write into.
+ */
+
+char *frag_var(type, max_chars, var, subtype, symbol, offset, opcode)
+relax_stateT type;
+int max_chars;
+int var;
+relax_substateT subtype;
+symbolS *symbol;
+long offset;
+char *opcode;
+{
+ register char *retval;
+
+ frag_grow (max_chars);
+ retval = obstack_next_free (&frags);
+ obstack_blank_fast (&frags, max_chars);
+ frag_now->fr_var = var;
+ frag_now->fr_type = type;
+ frag_now->fr_subtype = subtype;
+ frag_now->fr_symbol = symbol;
+ frag_now->fr_offset = offset;
+ frag_now->fr_opcode = opcode;
+ /* default these to zero. */
+ frag_now->fr_pcrel_adjust = 0;
+ frag_now->fr_bsr = 0;
+ frag_new (max_chars);
+ return (retval);
+} /* frag_var() */
+
+/*
+ * frag_variant()
+ *
+ * OVE: This variant of frag_var assumes that space for the tail has been
+ * allocated by caller.
+ * No call to frag_grow is done.
+ * Two new arguments have been added.
+ */
+
+char *frag_variant(type, max_chars, var, subtype, symbol, offset, opcode, pcrel_adjust,bsr)
+relax_stateT type;
+int max_chars;
+int var;
+relax_substateT subtype;
+symbolS *symbol;
+long offset;
+char *opcode;
+int pcrel_adjust;
+char bsr;
+{
+ register char *retval;
+
+ /* frag_grow (max_chars); */
+ retval = obstack_next_free (&frags);
+ /* obstack_blank_fast (&frags, max_chars); */ /* OVE: so far the only diff */
+ frag_now->fr_var = var;
+ frag_now->fr_type = type;
+ frag_now->fr_subtype = subtype;
+ frag_now->fr_symbol = symbol;
+ frag_now->fr_offset = offset;
+ frag_now->fr_opcode = opcode;
+ frag_now->fr_pcrel_adjust = pcrel_adjust;
+ frag_now->fr_bsr = bsr;
+ frag_new(max_chars);
+ return(retval);
+} /* frag_variant() */
+
+/*
+ * frag_wane()
+ *
+ * Reduce the variable end of a frag to a harmless state.
+ */
+void frag_wane(fragP)
+register fragS * fragP;
+{
+ fragP->fr_type = rs_fill;
+ fragP->fr_offset = 0;
+ fragP->fr_var = 0;
+}
+
+/*
+ * frag_align()
+ *
+ * Make a frag for ".align foo,bar". Call is "frag_align (foo,bar);".
+ * Foo & bar are absolute integers.
+ *
+ * Call to close off the current frag with a ".align", then start a new
+ * (so far empty) frag, in the same subsegment as the last frag.
+ */
+
+void frag_align(alignment, fill_character)
+int alignment;
+int fill_character;
+{
+ *(frag_var (rs_align, 1, 1, (relax_substateT)0, (symbolS *)0,
+ (long)alignment, (char *)0)) = fill_character;
+} /* frag_align() */
+
+/* end of frags.c */
diff --git a/gnu/usr.bin/as/frags.h b/gnu/usr.bin/as/frags.h
new file mode 100644
index 0000000..4532dda
--- /dev/null
+++ b/gnu/usr.bin/as/frags.h
@@ -0,0 +1,89 @@
+/* frags.h - Header file for the frag concept.
+
+ Copyright (C) 1987, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
+ * $Id: frags.h,v 1.3 1993/10/02 20:57:32 pk Exp $
+ */
+
+
+extern struct obstack frags;
+/* Frags ONLY live in this obstack. */
+/* We use obstack_next_free() macro */
+/* so please don't put any other objects */
+/* on this stack! */
+
+/*
+ * A macro to speed up appending exactly 1 char
+ * to current frag.
+ */
+/* JF changed < 1 to <= 1 to avoid a race conditon */
+#define FRAG_APPEND_1_CHAR(datum) \
+{ \
+ if (obstack_room( &frags ) <= 1) {\
+ frag_wane (frag_now); \
+ frag_new (0); \
+ } \
+ obstack_1grow( &frags, datum ); \
+ }
+
+
+#if __STDC__ == 1
+
+char *frag_more(int nchars);
+void frag_align(int alignment, int fill_character);
+void frag_new(int old_frags_var_max_size);
+void frag_wane(fragS *fragP);
+
+char *frag_variant(relax_stateT type,
+ int max_chars,
+ int var,
+ relax_substateT subtype,
+ symbolS *symbol,
+ long offset,
+ char *opcode,
+ int pcrel_adjust,
+ int bsr);
+
+char *frag_var(relax_stateT type,
+ int max_chars,
+ int var,
+ relax_substateT subtype,
+ symbolS *symbol,
+ long offset,
+ char *opcode);
+
+#else /* not __STDC__ */
+
+char *frag_more();
+char *frag_var();
+char *frag_variant();
+void frag_align();
+void frag_new();
+void frag_wane();
+
+#endif /* not __STDC__ */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of frags.h */
diff --git a/gnu/usr.bin/as/gas-format.el b/gnu/usr.bin/as/gas-format.el
new file mode 100644
index 0000000..32c6426
--- /dev/null
+++ b/gnu/usr.bin/as/gas-format.el
@@ -0,0 +1,79 @@
+;; -*- lisp-interaction -*-
+;; -*- emacs-lisp -*-
+;;
+;;
+;; originally from...
+;; Rich's personal .emacs file. feel free to copy.
+;;
+;; this file sets emacs up for the type of C source code formatting used within
+;; gas. I don't use gnu indent. If you do, and find a setup that approximates
+;; these settings, please send it to me.
+;;
+;; Last Mod Thu Feb 13 00:59:16 PST 1992, by rich@sendai
+;;
+
+;;
+;;
+;; This section sets constants used by c-mode for formating
+;;
+;;
+
+
+;; If `c-auto-newline' is non-`nil', newlines are inserted both
+;;before and after braces that you insert, and after colons and semicolons.
+;;Correct C indentation is done on all the lines that are made this way.
+
+(setq c-auto-newline nil)
+
+
+;; If `c-tab-always-indent' is non-`nil', the TAB command
+;;in C mode does indentation only if point is at the left margin or within
+;;the line's indentation. If there is non-whitespace to the left of point,
+;;then TAB just inserts a tab character in the buffer. Normally,
+;;this variable is `nil', and TAB always reindents the current line.
+
+(setq c-tab-always-indent nil)
+
+;; C does not have anything analogous to particular function names for which
+;;special forms of indentation are desirable. However, it has a different
+;;need for customization facilities: many different styles of C indentation
+;;are in common use.
+;;
+;; There are six variables you can set to control the style that Emacs C
+;;mode will use.
+;;
+;;`c-indent-level'
+;; Indentation of C statements within surrounding block. The surrounding
+;; block's indentation is the indentation of the line on which the
+;; open-brace appears.
+
+(setq c-indent-level 8)
+
+;;`c-continued-statement-offset'
+;; Extra indentation given to a substatement, such as the then-clause of
+;; an if or body of a while.
+
+(setq c-continued-statement-offset 4)
+
+;;`c-brace-offset'
+;; Extra indentation for line if it starts with an open brace.
+
+(setq c-brace-offset 0)
+
+;;`c-brace-imaginary-offset'
+;; An open brace following other text is treated as if it were this far
+;; to the right of the start of its line.
+
+(setq c-brace-imaginary-offset 0)
+
+;;`c-argdecl-indent'
+;; Indentation level of declarations of C function arguments.
+
+(setq c-argdecl-indent 0)
+
+;;`c-label-offset'
+;; Extra indentation for line that is a label, or case or default.
+
+(setq c-label-offset -8)
+
+;; end of gas-format.el
diff --git a/gnu/usr.bin/as/hash.c b/gnu/usr.bin/as/hash.c
new file mode 100644
index 0000000..68a5f14
--- /dev/null
+++ b/gnu/usr.bin/as/hash.c
@@ -0,0 +1,992 @@
+/* hash.c - hash table lookup strings -
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * BUGS, GRIPES, APOLOGIA etc.
+ *
+ * A typical user doesn't need ALL this: I intend to make a library out
+ * of it one day - Dean Elsner.
+ * Also, I want to change the definition of a symbol to (address,length)
+ * so I can put arbitrary binary in the names stored. [see hsh.c for that]
+ *
+ * This slime is common coupled inside the module. Com-coupling (and other
+ * vandalism) was done to speed running time. The interfaces at the
+ * module's edges are adequately clean.
+ *
+ * There is no way to (a) run a test script through this heap and (b)
+ * compare results with previous scripts, to see if we have broken any
+ * code. Use GNU (f)utilities to do this. A few commands assist test.
+ * The testing is awkward: it tries to be both batch & interactive.
+ * For now, interactive rules!
+ */
+
+/*
+ * The idea is to implement a symbol table. A test jig is here.
+ * Symbols are arbitrary strings; they can't contain '\0'.
+ * [See hsh.c for a more general symbol flavour.]
+ * Each symbol is associated with a char*, which can point to anything
+ * you want, allowing an arbitrary property list for each symbol.
+ *
+ * The basic operations are:
+ *
+ * new creates symbol table, returns handle
+ * find (symbol) returns char*
+ * insert (symbol,char*) error if symbol already in table
+ * delete (symbol) returns char* if symbol was in table
+ * apply so you can delete all symbols before die()
+ * die destroy symbol table (free up memory)
+ *
+ * Supplementary functions include:
+ *
+ * say how big? what % full?
+ * replace (symbol,newval) report previous value
+ * jam (symbol,value) assert symbol:=value
+ *
+ * You, the caller, have control over errors: this just reports them.
+ *
+ * This package requires malloc(), free().
+ * Malloc(size) returns NULL or address of char[size].
+ * Free(address) frees same.
+ */
+
+/*
+ * The code and its structures are re-enterent.
+ * Before you do anything else, you must call hash_new() which will
+ * return the address of a hash-table-control-block (or NULL if there
+ * is not enough memory). You then use this address as a handle of the
+ * symbol table by passing it to all the other hash_...() functions.
+ * The only approved way to recover the memory used by the symbol table
+ * is to call hash_die() with the handle of the symbol table.
+ *
+ * Before you call hash_die() you normally delete anything pointed to
+ * by individual symbols. After hash_die() you can't use that symbol
+ * table again.
+ *
+ * The char* you associate with a symbol may not be NULL (0) because
+ * NULL is returned whenever a symbol is not in the table. Any other
+ * value is OK, except DELETED, #defined below.
+ *
+ * When you supply a symbol string for insertion, YOU MUST PRESERVE THE
+ * STRING until that symbol is deleted from the table. The reason is that
+ * only the address you supply, NOT the symbol string itself, is stored
+ * in the symbol table.
+ *
+ * You may delete and add symbols arbitrarily.
+ * Any or all symbols may have the same 'value' (char *). In fact, these
+ * routines don't do anything with your symbol values.
+ *
+ * You have no right to know where the symbol:char* mapping is stored,
+ * because it moves around in memory; also because we may change how it
+ * works and we don't want to break your code do we? However the handle
+ * (address of struct hash_control) is never changed in
+ * the life of the symbol table.
+ *
+ * What you CAN find out about a symbol table is:
+ * how many slots are in the hash table?
+ * how many slots are filled with symbols?
+ * (total hashes,collisions) for (reads,writes) (*)
+ * All of the above values vary in time.
+ * (*) some of these numbers will not be meaningful if we change the
+ * internals.
+ */
+
+/*
+ * I N T E R N A L
+ *
+ * Hash table is an array of hash_entries; each entry is a pointer to a
+ * a string and a user-supplied value 1 char* wide.
+ *
+ * The array always has 2 ** n elements, n>0, n integer.
+ * There is also a 'wall' entry after the array, which is always empty
+ * and acts as a sentinel to stop running off the end of the array.
+ * When the array gets too full, we create a new array twice as large
+ * and re-hash the symbols into the new array, then forget the old array.
+ * (Of course, we copy the values into the new array before we junk the
+ * old array!)
+ *
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: hash.c,v 1.3 1993/10/02 20:57:34 pk Exp $";
+#endif
+
+#include <stdio.h>
+
+#ifndef FALSE
+#define FALSE (0)
+#define TRUE (!FALSE)
+#endif /* no FALSE yet */
+
+#include <ctype.h>
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+#include "as.h"
+
+#define error as_fatal
+
+#define DELETED ((char *)1) /* guarenteed invalid address */
+#define START_POWER (11) /* power of two: size of new hash table *//* JF was 6 */
+/* JF These next two aren't used any more. */
+/* #define START_SIZE (64) / * 2 ** START_POWER */
+/* #define START_FULL (32) / * number of entries before table expands */
+#define islive(ptr) (ptr->hash_string && ptr->hash_string != DELETED)
+/* above TRUE if a symbol is in entry @ ptr */
+
+#define STAT_SIZE (0) /* number of slots in hash table */
+/* the wall does not count here */
+/* we expect this is always a power of 2 */
+#define STAT_ACCESS (1) /* number of hash_ask()s */
+#define STAT__READ (0) /* reading */
+#define STAT__WRITE (1) /* writing */
+#define STAT_COLLIDE (3) /* number of collisions (total) */
+/* this may exceed STAT_ACCESS if we have */
+/* lots of collisions/access */
+#define STAT_USED (5) /* slots used right now */
+#define STATLENGTH (6) /* size of statistics block */
+#if STATLENGTH != HASH_STATLENGTH
+Panic! Please make #include "stat.h" agree with previous definitions!
+#endif
+
+ /* #define SUSPECT to do runtime checks */
+ /* #define TEST to be a test jig for hash...() */
+
+#ifdef TEST /* TEST: use smaller hash table */
+#undef START_POWER
+#define START_POWER (3)
+#undef START_SIZE
+#define START_SIZE (8)
+#undef START_FULL
+#define START_FULL (4)
+#endif
+
+/*------------------ plan ---------------------------------- i = internal
+
+ struct hash_control * c;
+ struct hash_entry * e; i
+ int b[z]; buffer for statistics
+ z size of b
+ char * s; symbol string (address) [ key ]
+ char * v; value string (address) [datum]
+ boolean f; TRUE if we found s in hash table i
+ char * t; error string; "" means OK
+ int a; access type [0...n) i
+
+ c=hash_new () create new hash_control
+
+ hash_die (c) destroy hash_control (and hash table)
+ table should be empty.
+ doesn't check if table is empty.
+ c has no meaning after this.
+
+ hash_say (c,b,z) report statistics of hash_control.
+ also report number of available statistics.
+
+ v=hash_delete (c,s) delete symbol, return old value if any.
+ ask() NULL means no old value.
+ f
+
+ v=hash_replace (c,s,v) replace old value of s with v.
+ ask() NULL means no old value: no table change.
+ f
+
+ t=hash_insert (c,s,v) insert (s,v) in c.
+ ask() return error string.
+ f it is an error to insert if s is already
+ in table.
+ if any error, c is unchanged.
+
+ t=hash_jam (c,s,v) assert that new value of s will be v. i
+ ask() it may decide to GROW the table. i
+ f i
+ grow() i
+ t=hash_grow (c) grow the hash table. i
+ jam() will invoke JAM. i
+
+ ?=hash_apply (c,y) apply y() to every symbol in c.
+ y evtries visited in 'unspecified' order.
+
+ v=hash_find (c,s) return value of s, or NULL if s not in c.
+ ask()
+ f
+
+ f,e=hash_ask() (c,s,a) return slot where s SHOULD live. i
+ code() maintain collision stats in c. i
+
+ .=hash_code (c,s) compute hash-code for s, i
+ from parameters of c. i
+
+ */
+
+static char hash_found; /* returned by hash_ask() to stop extra */
+/* testing. hash_ask() wants to return both */
+/* a slot and a status. This is the status. */
+/* TRUE: found symbol */
+/* FALSE: absent: empty or deleted slot */
+/* Also returned by hash_jam(). */
+/* TRUE: we replaced a value */
+/* FALSE: we inserted a value */
+
+static struct hash_entry * hash_ask();
+static int hash_code ();
+static char * hash_grow();
+
+/*
+ * h a s h _ n e w ( )
+ *
+ */
+struct hash_control *
+ hash_new() /* create a new hash table */
+/* return NULL if failed */
+/* return handle (address of struct hash) */
+{
+ register struct hash_control * retval;
+ register struct hash_entry * room; /* points to hash table */
+ register struct hash_entry * wall;
+ register struct hash_entry * entry;
+ register int * ip; /* scan stats block of struct hash_control */
+ register int * nd; /* limit of stats block */
+
+ if (( room = (struct hash_entry *) malloc( sizeof(struct
+ hash_entry)*((1<<START_POWER) + 1) ) ) != NULL)
+ /* +1 for the wall entry */
+ {
+ if (( retval = (struct hash_control *) malloc(sizeof(struct
+ hash_control)) ) != NULL)
+ {
+ nd = retval->hash_stat + STATLENGTH;
+ for (ip=retval->hash_stat; ip<nd; ip++)
+ {
+ *ip = 0;
+ }
+
+ retval->hash_stat[STAT_SIZE] = 1<<START_POWER;
+ retval->hash_mask = (1<<START_POWER) - 1;
+ retval->hash_sizelog = START_POWER;
+ /* works for 1's compl ok */
+ retval->hash_where = room;
+ retval->hash_wall =
+ wall = room + (1<<START_POWER);
+ retval->hash_full = (1<<START_POWER)/2;
+ for (entry=room; entry <= wall; entry++)
+ {
+ entry->hash_string = NULL;
+ }
+ }
+ }
+ else
+ {
+ retval = NULL; /* no room for table: fake a failure */
+ }
+ return(retval); /* return NULL or set-up structs */
+}
+
+/*
+ * h a s h _ d i e ( )
+ *
+ * Table should be empty, but this is not checked.
+ * To empty the table, try hash_apply()ing a symbol deleter.
+ * Return to free memory both the hash table and it's control
+ * block.
+ * 'handle' has no meaning after this function.
+ * No errors are recoverable.
+ */
+void
+ hash_die(handle)
+struct hash_control * handle;
+{
+ free((char *)handle->hash_where);
+ free((char *)handle);
+}
+
+/*
+ * h a s h _ s a y ( )
+ *
+ * Return the size of the statistics table, and as many statistics as
+ * we can until either (a) we have run out of statistics or (b) caller
+ * has run out of buffer.
+ * NOTE: hash_say treats all statistics alike.
+ * These numbers may change with time, due to insertions, deletions
+ * and expansions of the table.
+ * The first "statistic" returned is the length of hash_stat[].
+ * Then contents of hash_stat[] are read out (in ascending order)
+ * until your buffer or hash_stat[] is exausted.
+ */
+void
+ hash_say(handle,buffer,bufsiz)
+register struct hash_control * handle;
+register int buffer[/*bufsiz*/];
+register int bufsiz;
+{
+ register int * nd; /* limit of statistics block */
+ register int * ip; /* scan statistics */
+
+ ip = handle->hash_stat;
+ nd = ip + min(bufsiz-1,STATLENGTH);
+ if (bufsiz>0) /* trust nothing! bufsiz <= 0 is dangerous */
+ {
+ *buffer++ = STATLENGTH;
+ for (; ip<nd; ip++,buffer++)
+ {
+ *buffer = *ip;
+ }
+ }
+}
+
+/*
+ * h a s h _ d e l e t e ( )
+ *
+ * Try to delete a symbol from the table.
+ * If it was there, return its value (and adjust STAT_USED).
+ * Otherwise, return NULL.
+ * Anyway, the symbol is not present after this function.
+ *
+ */
+char * /* NULL if string not in table, else */
+ /* returns value of deleted symbol */
+ hash_delete(handle,string)
+register struct hash_control * handle;
+register char * string;
+{
+ register char * retval; /* NULL if string not in table */
+ register struct hash_entry * entry; /* NULL or entry of this symbol */
+
+ entry = hash_ask(handle,string,STAT__WRITE);
+ if (hash_found)
+ {
+ retval = entry->hash_value;
+ entry->hash_string = DELETED; /* mark as deleted */
+ handle->hash_stat[STAT_USED] -= 1; /* slots-in-use count */
+#ifdef SUSPECT
+ if (handle->hash_stat[STAT_USED]<0)
+ {
+ error("hash_delete");
+ }
+#endif /* def SUSPECT */
+ }
+ else
+ {
+ retval = NULL;
+ }
+ return(retval);
+}
+
+/*
+ * h a s h _ r e p l a c e ( )
+ *
+ * Try to replace the old value of a symbol with a new value.
+ * Normally return the old value.
+ * Return NULL and don't change the table if the symbol is not already
+ * in the table.
+ */
+char *
+ hash_replace(handle,string,value)
+register struct hash_control * handle;
+register char * string;
+register char * value;
+{
+ register struct hash_entry * entry;
+ register char * retval;
+
+ entry = hash_ask(handle,string,STAT__WRITE);
+ if (hash_found)
+ {
+ retval = entry->hash_value;
+ entry->hash_value = value;
+ }
+ else
+ {
+ retval = NULL;
+ }
+ ;
+ return (retval);
+}
+
+/*
+ * h a s h _ i n s e r t ( )
+ *
+ * Insert a (symbol-string, value) into the hash table.
+ * Return an error string, "" means OK.
+ * It is an 'error' to insert an existing symbol.
+ */
+
+char * /* return error string */
+ hash_insert(handle,string,value)
+register struct hash_control * handle;
+register char * string;
+register char * value;
+{
+ register struct hash_entry * entry;
+ register char * retval;
+
+ retval = "";
+ if (handle->hash_stat[STAT_USED] > handle->hash_full)
+ {
+ retval = hash_grow(handle);
+ }
+ if ( ! * retval)
+ {
+ entry = hash_ask(handle,string,STAT__WRITE);
+ if (hash_found)
+ {
+ retval = "exists";
+ }
+ else
+ {
+ entry->hash_value = value;
+ entry->hash_string = string;
+ handle->hash_stat[STAT_USED] += 1;
+ }
+ }
+ return(retval);
+}
+
+/*
+ * h a s h _ j a m ( )
+ *
+ * Regardless of what was in the symbol table before, after hash_jam()
+ * the named symbol has the given value. The symbol is either inserted or
+ * (its value is) relpaced.
+ * An error message string is returned, "" means OK.
+ *
+ * WARNING: this may decide to grow the hashed symbol table.
+ * To do this, we call hash_grow(), WHICH WILL recursively CALL US.
+ *
+ * We report status internally: hash_found is TRUE if we replaced, but
+ * false if we inserted.
+ */
+char *
+ hash_jam(handle,string,value)
+register struct hash_control * handle;
+register char * string;
+register char * value;
+{
+ register char * retval;
+ register struct hash_entry * entry;
+
+ retval = "";
+ if (handle->hash_stat[STAT_USED] > handle->hash_full)
+ {
+ retval = hash_grow(handle);
+ }
+ if (! * retval)
+ {
+ entry = hash_ask(handle,string,STAT__WRITE);
+ if ( ! hash_found)
+ {
+ entry->hash_string = string;
+ handle->hash_stat[STAT_USED] += 1;
+ }
+ entry->hash_value = value;
+ }
+ return(retval);
+}
+
+/*
+ * h a s h _ g r o w ( )
+ *
+ * Grow a new (bigger) hash table from the old one.
+ * We choose to double the hash table's size.
+ * Return a human-scrutible error string: "" if OK.
+ * Warning! This uses hash_jam(), which had better not recurse
+ * back here! Hash_jam() conditionally calls us, but we ALWAYS
+ * call hash_jam()!
+ * Internal.
+ */
+static char *
+ hash_grow(handle) /* make a hash table grow */
+struct hash_control * handle;
+{
+ register struct hash_entry * newwall;
+ register struct hash_entry * newwhere;
+ struct hash_entry * newtrack;
+ register struct hash_entry * oldtrack;
+ register struct hash_entry * oldwhere;
+ register struct hash_entry * oldwall;
+ register int temp;
+ int newsize;
+ char * string;
+ char * retval;
+#ifdef SUSPECT
+ int oldused;
+#endif
+
+ /*
+ * capture info about old hash table
+ */
+ oldwhere = handle->hash_where;
+ oldwall = handle->hash_wall;
+#ifdef SUSPECT
+ oldused = handle->hash_stat[STAT_USED];
+#endif
+ /*
+ * attempt to get enough room for a hash table twice as big
+ */
+ temp = handle->hash_stat[STAT_SIZE];
+ if (( newwhere = (struct hash_entry *)
+ xmalloc((long)((temp+temp+1)*sizeof(struct hash_entry)))) != NULL)
+ /* +1 for wall slot */
+ {
+ retval = ""; /* assume success until proven otherwise */
+ /*
+ * have enough room: now we do all the work.
+ * double the size of everything in handle,
+ * note: hash_mask frob works for 1's & for 2's complement machines
+ */
+ handle->hash_mask = handle->hash_mask + handle->hash_mask + 1;
+ handle->hash_stat[STAT_SIZE] <<= 1;
+ newsize = handle->hash_stat[STAT_SIZE];
+ handle->hash_where = newwhere;
+ handle->hash_full <<= 1;
+ handle->hash_sizelog += 1;
+ handle->hash_stat[STAT_USED] = 0;
+ handle->hash_wall =
+ newwall = newwhere + newsize;
+ /*
+ * set all those pesky new slots to vacant.
+ */
+ for (newtrack=newwhere; newtrack <= newwall; newtrack++)
+ {
+ newtrack->hash_string = NULL;
+ }
+ /*
+ * we will do a scan of the old table, the hard way, using the
+ * new control block to re-insert the data into new hash table.
+ */
+ handle->hash_stat[STAT_USED] = 0; /* inserts will bump it up to correct */
+ for (oldtrack=oldwhere; oldtrack < oldwall; oldtrack++)
+ {
+ if (((string = oldtrack->hash_string) != NULL) && string != DELETED)
+ {
+ if ( * (retval = hash_jam(handle,string,oldtrack->hash_value) ) )
+ {
+ break;
+ }
+ }
+ }
+#ifdef SUSPECT
+ if ( !*retval && handle->hash_stat[STAT_USED] != oldused)
+ {
+ retval = "hash_used";
+ }
+#endif
+ if (!*retval)
+ {
+ /*
+ * we have a completely faked up control block.
+ * return the old hash table.
+ */
+ free((char *)oldwhere);
+ /*
+ * Here with success. retval is already "".
+ */
+ }
+ }
+ else
+ {
+ retval = "no room";
+ }
+ return(retval);
+}
+
+/*
+ * h a s h _ a p p l y ( )
+ *
+ * Use this to scan each entry in symbol table.
+ * For each symbol, this calls (applys) a nominated function supplying the
+ * symbol's value (and the symbol's name).
+ * The idea is you use this to destroy whatever is associted with
+ * any values in the table BEFORE you destroy the table with hash_die.
+ * Of course, you can use it for other jobs; whenever you need to
+ * visit all extant symbols in the table.
+ *
+ * We choose to have a call-you-back idea for two reasons:
+ * asthetic: it is a neater idea to use apply than an explicit loop
+ * sensible: if we ever had to grow the symbol table (due to insertions)
+ * then we would lose our place in the table when we re-hashed
+ * symbols into the new table in a different order.
+ *
+ * The order symbols are visited depends entirely on the hashing function.
+ * Whenever you insert a (symbol, value) you risk expanding the table. If
+ * you do expand the table, then the hashing function WILL change, so you
+ * MIGHT get a different order of symbols visited. In other words, if you
+ * want the same order of visiting symbols as the last time you used
+ * hash_apply() then you better not have done any hash_insert()s or
+ * hash_jam()s since the last time you used hash_apply().
+ *
+ * In future we may use the value returned by your nominated function.
+ * One idea is to abort the scan if, after applying the function to a
+ * certain node, the function returns a certain code.
+ * To be safe, please make your functions of type char *. If you always
+ * return NULL, then the scan will complete, visiting every symbol in
+ * the table exactly once. ALL OTHER RETURNED VALUES have no meaning yet!
+ * Caveat Actor!
+ *
+ * The function you supply should be of the form:
+ * char * myfunct(string,value)
+ * char * string; |* the symbol's name *|
+ * char * value; |* the symbol's value *|
+ * {
+ * |* ... *|
+ * return(NULL);
+ * }
+ *
+ * The returned value of hash_apply() is (char*)NULL. In future it may return
+ * other values. NULL means "completed scan OK". Other values have no meaning
+ * yet. (The function has no graceful failures.)
+ */
+char *
+ hash_apply(handle,function)
+struct hash_control * handle;
+char* (*function)();
+{
+ register struct hash_entry * entry;
+ register struct hash_entry * wall;
+
+ wall = handle->hash_wall;
+ for (entry = handle->hash_where; entry < wall; entry++)
+ {
+ if (islive(entry)) /* silly code: tests entry->string twice! */
+ {
+ (*function)(entry->hash_string,entry->hash_value);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * h a s h _ f i n d ( )
+ *
+ * Given symbol string, find value (if any).
+ * Return found value or NULL.
+ */
+char *
+ hash_find(handle,string) /* return char* or NULL */
+struct hash_control * handle;
+char * string;
+{
+ register struct hash_entry * entry;
+ register char * retval;
+
+ entry = hash_ask(handle,string,STAT__READ);
+ if (hash_found)
+ {
+ retval = entry->hash_value;
+ }
+ else
+ {
+ retval = NULL;
+ }
+ return(retval);
+}
+
+/*
+ * h a s h _ a s k ( )
+ *
+ * Searches for given symbol string.
+ * Return the slot where it OUGHT to live. It may be there.
+ * Return hash_found: TRUE only if symbol is in that slot.
+ * Access argument is to help keep statistics in control block.
+ * Internal.
+ */
+static struct hash_entry * /* string slot, may be empty or deleted */
+ hash_ask(handle,string,access)
+struct hash_control * handle;
+char * string;
+int access; /* access type */
+{
+ register char *string1; /* JF avoid strcmp calls */
+ register char * s;
+ register int c;
+ register struct hash_entry * slot;
+ register int collision; /* count collisions */
+
+ slot = handle->hash_where + hash_code(handle,string); /* start looking here */
+ handle->hash_stat[STAT_ACCESS+access] += 1;
+ collision = 0;
+ hash_found = FALSE;
+ while (((s = slot->hash_string) != NULL) && s != DELETED)
+ {
+ for (string1=string;;) {
+ if ((c= *s++) == 0) {
+ if (!*string1)
+ hash_found = TRUE;
+ break;
+ }
+ if (*string1++ != c)
+ break;
+ }
+ if (hash_found)
+ break;
+ collision++;
+ slot++;
+ }
+ /*
+ * slot: return:
+ * in use: we found string slot
+ * at empty:
+ * at wall: we fell off: wrap round ????
+ * in table: dig here slot
+ * at DELETED: dig here slot
+ */
+ if (slot == handle->hash_wall)
+ {
+ slot = handle->hash_where; /* now look again */
+ while (((s = slot->hash_string) != NULL) && s != DELETED)
+ {
+ for (string1=string;*s;string1++,s++) {
+ if (*string1 != *s)
+ break;
+ }
+ if (*s == *string1) {
+ hash_found = TRUE;
+ break;
+ }
+ collision++;
+ slot++;
+ }
+ /*
+ * slot: return:
+ * in use: we found it slot
+ * empty: wall: ERROR IMPOSSIBLE !!!!
+ * in table: dig here slot
+ * DELETED:dig here slot
+ */
+ }
+ /* fprintf(stderr,"hash_ask(%s)->%d(%d)\n",string,hash_code(handle,string),collision); */
+ handle->hash_stat[STAT_COLLIDE+access] += collision;
+ return(slot); /* also return hash_found */
+}
+
+/*
+ * h a s h _ c o d e
+ *
+ * Does hashing of symbol string to hash number.
+ * Internal.
+ */
+static int
+ hash_code(handle,string)
+struct hash_control * handle;
+register char * string;
+{
+ register long h; /* hash code built here */
+ register long c; /* each character lands here */
+ register int n; /* Amount to shift h by */
+
+ n = (handle->hash_sizelog - 3);
+ h = 0;
+ while ((c = *string++) != 0)
+ {
+ h += c;
+ h = (h<<3) + (h>>n) + c;
+ }
+ return (h & handle->hash_mask);
+}
+
+/*
+ * Here is a test program to exercise above.
+ */
+#ifdef TEST
+
+#define TABLES (6) /* number of hash tables to maintain */
+/* (at once) in any testing */
+#define STATBUFSIZE (12) /* we can have 12 statistics */
+
+int statbuf[STATBUFSIZE]; /* display statistics here */
+char answer[100]; /* human farts here */
+char * hashtable[TABLES]; /* we test many hash tables at once */
+char * h; /* points to curent hash_control */
+char ** pp;
+char * p;
+char * name;
+char * value;
+int size;
+int used;
+char command;
+int number; /* number 0:TABLES-1 of current hashed */
+/* symbol table */
+
+main()
+{
+ char (*applicatee());
+ char * hash_find();
+ char * destroy();
+ char * what();
+ struct hash_control * hash_new();
+ char * hash_replace();
+ int * ip;
+
+ number = 0;
+ h = 0;
+ printf("type h <RETURN> for help\n");
+ for (;;)
+ {
+ printf("hash_test command: ");
+ gets(answer);
+ command = answer[0];
+ if (isupper(command)) command = tolower(command); /* ecch! */
+ switch (command)
+ {
+ case '#':
+ printf("old hash table #=%d.\n",number);
+ whattable();
+ break;
+ case '?':
+ for (pp=hashtable; pp<hashtable+TABLES; pp++)
+ {
+ printf("address of hash table #%d control block is %xx\n"
+ ,pp-hashtable,*pp);
+ }
+ break;
+ case 'a':
+ hash_apply(h,applicatee);
+ break;
+ case 'd':
+ hash_apply(h,destroy);
+ hash_die(h);
+ break;
+ case 'f':
+ p = hash_find(h,name=what("symbol"));
+ printf("value of \"%s\" is \"%s\"\n",name,p?p:"NOT-PRESENT");
+ break;
+ case 'h':
+ printf("# show old, select new default hash table number\n");
+ printf("? display all hashtable control block addresses\n");
+ printf("a apply a simple display-er to each symbol in table\n");
+ printf("d die: destroy hashtable\n");
+ printf("f find value of nominated symbol\n");
+ printf("h this help\n");
+ printf("i insert value into symbol\n");
+ printf("j jam value into symbol\n");
+ printf("n new hashtable\n");
+ printf("r replace a value with another\n");
+ printf("s say what %% of table is used\n");
+ printf("q exit this program\n");
+ printf("x delete a symbol from table, report its value\n");
+ break;
+ case 'i':
+ p = hash_insert(h,name=what("symbol"),value=what("value"));
+ if (*p)
+ {
+ printf("symbol=\"%s\" value=\"%s\" error=%s\n",name,value,p);
+ }
+ break;
+ case 'j':
+ p = hash_jam(h,name=what("symbol"),value=what("value"));
+ if (*p)
+ {
+ printf("symbol=\"%s\" value=\"%s\" error=%s\n",name,value,p);
+ }
+ break;
+ case 'n':
+ h = hashtable[number] = (char *) hash_new();
+ break;
+ case 'q':
+ exit();
+ case 'r':
+ p = hash_replace(h,name=what("symbol"),value=what("value"));
+ printf("old value was \"%s\"\n",p?p:"{}");
+ break;
+ case 's':
+ hash_say(h,statbuf,STATBUFSIZE);
+ for (ip=statbuf; ip<statbuf+STATBUFSIZE; ip++)
+ {
+ printf("%d ",*ip);
+ }
+ printf("\n");
+ break;
+ case 'x':
+ p = hash_delete(h,name=what("symbol"));
+ printf("old value was \"%s\"\n",p?p:"{}");
+ break;
+ default:
+ printf("I can't understand command \"%c\"\n",command);
+ break;
+ }
+ }
+}
+
+char *
+ what(description)
+char * description;
+{
+ char * retval;
+ char * malloc();
+
+ printf(" %s : ",description);
+ gets(answer);
+ /* will one day clean up answer here */
+ retval = malloc(strlen(answer)+1);
+ if (!retval)
+ {
+ error("room");
+ }
+ (void)strcpy(retval,answer);
+ return(retval);
+}
+
+char *
+ destroy(string,value)
+char * string;
+char * value;
+{
+ free(string);
+ free(value);
+ return(NULL);
+}
+
+
+char *
+ applicatee(string,value)
+char * string;
+char * value;
+{
+ printf("%.20s-%.20s\n",string,value);
+ return(NULL);
+}
+
+whattable() /* determine number: what hash table to use */
+/* also determine h: points to hash_control */
+{
+
+ for (;;)
+ {
+ printf(" what hash table (%d:%d) ? ",0,TABLES-1);
+ gets(answer);
+ sscanf(answer,"%d",&number);
+ if (number >= 0 && number<TABLES)
+ {
+ h = hashtable[number];
+ if (!h)
+ {
+ printf("warning: current hash-table-#%d. has no hash-control\n",number);
+ }
+ return;
+ }
+ else
+ {
+ printf("invalid hash table number: %d\n",number);
+ }
+ }
+}
+
+
+
+#endif /* #ifdef TEST */
+
+/* end of hash.c */
diff --git a/gnu/usr.bin/as/hash.h b/gnu/usr.bin/as/hash.h
new file mode 100644
index 0000000..6918561
--- /dev/null
+++ b/gnu/usr.bin/as/hash.h
@@ -0,0 +1,65 @@
+/* hash.h - for hash.c
+ Copyright (C) 1987, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
+ * $Id: hash.h,v 1.3 1993/10/02 20:57:35 pk Exp $
+ */
+
+
+#ifndef hashH
+#define hashH
+
+struct hash_entry
+{
+ char *hash_string; /* points to where the symbol string is */
+ /* NULL means slot is not used */
+ /* DELETED means slot was deleted */
+ char *hash_value; /* user's datum, associated with symbol */
+};
+
+
+#define HASH_STATLENGTH (6)
+struct hash_control
+{
+ struct hash_entry *hash_where; /* address of hash table */
+ int hash_sizelog; /* Log of ( hash_mask + 1 ) */
+ int hash_mask; /* masks a hash into index into table */
+ int hash_full; /* when hash_stat[STAT_USED] exceeds this, */
+ /* grow table */
+ struct hash_entry * hash_wall; /* point just after last (usable) entry */
+ /* here we have some statistics */
+ int hash_stat[HASH_STATLENGTH]; /* lies & statistics */
+ /* we need STAT_USED & STAT_SIZE */
+};
+
+ /* fixme: prototype. */
+
+/* returns */
+struct hash_control *hash_new(); /* [control block] */
+void hash_die();
+void hash_say();
+char *hash_delete(); /* previous value */
+char *hash_relpace(); /* previous value */
+char *hash_insert(); /* error string */
+char *hash_apply(); /* 0 means OK */
+char *hash_find(); /* value */
+char *hash_jam(); /* error text (internal) */
+
+#endif /* #ifdef hashH */
+
+/* end of hash.h */
diff --git a/gnu/usr.bin/as/hex-value.c b/gnu/usr.bin/as/hex-value.c
new file mode 100644
index 0000000..d27d4b3
--- /dev/null
+++ b/gnu/usr.bin/as/hex-value.c
@@ -0,0 +1,61 @@
+/* hex_value.c - char=>radix-value -
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * Export: Hex_value[]. Converts digits to their radix-values.
+ * As distributed assumes 8 bits per char (256 entries) and ASCII.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: hex-value.c,v 1.3 1993/10/02 20:57:36 pk Exp $";
+#endif
+
+#define __ (42) /* blatently illegal digit value */
+/* exceeds any normal radix */
+
+#if (__STDC__ != 1) && !defined(const)
+#define const /* empty */
+#endif
+const char
+ hex_value[256] = { /* for fast ASCII -> binary */
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, __, __, __, __, __, __,
+ __, 10, 11, 12, 13, 14, 15, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ __, 10, 11, 12, 13, 14, 15, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
+ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __
+ };
+
+#ifdef HO_VMS
+dummy2()
+{
+}
+#endif
+
+/* end of hex_value.c */
diff --git a/gnu/usr.bin/as/input-file.c b/gnu/usr.bin/as/input-file.c
new file mode 100644
index 0000000..3b124cc
--- /dev/null
+++ b/gnu/usr.bin/as/input-file.c
@@ -0,0 +1,327 @@
+/* input_file.c - Deal with Input Files -
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * Confines all details of reading source bytes to this module.
+ * All O/S specific crocks should live here.
+ * What we lose in "efficiency" we gain in modularity.
+ * Note we don't need to #include the "as.h" file. No common coupling!
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: input-file.c,v 1.3 1993/10/02 20:57:37 pk Exp $";
+#endif
+
+#ifdef USG
+#define setbuffer(stream, buf, size) setvbuf((stream), (buf), _IOFBF, (size))
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include "as.h"
+#include "input-file.h"
+
+/* This variable is non-zero if the file currently being read should be
+ preprocessed by app. It is zero if the file can be read straight in.
+ */
+int preprocess = 0;
+
+/*
+ * This code opens a file, then delivers BUFFER_SIZE character
+ * chunks of the file on demand.
+ * BUFFER_SIZE is supposed to be a number chosen for speed.
+ * The caller only asks once what BUFFER_SIZE is, and asks before
+ * the nature of the input files (if any) is known.
+ */
+
+#define BUFFER_SIZE (32 * 1024)
+
+/*
+ * We use static data: the data area is not sharable.
+ */
+
+FILE *f_in;
+/* static JF remove static so app.c can use file_name */
+char * file_name;
+
+/* Struct for saving the state of this module for file includes. */
+struct saved_file {
+ FILE *f_in;
+ char *file_name;
+ int preprocess;
+ char *app_save;
+};
+
+/* These hooks accomodate most operating systems. */
+
+void input_file_begin() {
+ f_in = (FILE *)0;
+}
+
+void input_file_end () { }
+
+/* Return BUFFER_SIZE. */
+int input_file_buffer_size() {
+ return (BUFFER_SIZE);
+}
+
+int input_file_is_open() {
+ return f_in != (FILE *)0;
+}
+
+/* Push the state of our input, returning a pointer to saved info that
+ can be restored with input_file_pop (). */
+char *input_file_push () {
+ register struct saved_file *saved;
+
+ saved = (struct saved_file *)xmalloc (sizeof *saved);
+
+ saved->f_in = f_in;
+ saved->file_name = file_name;
+ saved->preprocess = preprocess;
+ if (preprocess)
+ saved->app_save = app_push ();
+
+ input_file_begin (); /* Initialize for new file */
+
+ return (char *)saved;
+}
+
+void
+ input_file_pop (arg)
+char *arg;
+{
+ register struct saved_file *saved = (struct saved_file *)arg;
+
+ input_file_end (); /* Close out old file */
+
+ f_in = saved->f_in;
+ file_name = saved->file_name;
+ preprocess = saved->preprocess;
+ if (preprocess)
+ app_pop (saved->app_save);
+
+ free(arg);
+}
+
+#ifdef DONTDEF /* JF save old version in case we need it */
+void
+ input_file_open (filename, preprocess, debugging)
+char * filename; /* "" means use stdin. Must not be 0. */
+int preprocess; /* TRUE if needs app. */
+int debugging; /* TRUE if we are debugging assembler. */
+{
+ assert( filename != 0 ); /* Filename may not be NULL. */
+ if (filename[0])
+ { /* We have a file name. Suck it and see. */
+ file_handle = open (filename, O_RDONLY, 0);
+ file_name = filename;
+ }
+ else
+ { /* use stdin for the input file. */
+ file_handle = fileno (stdin);
+ file_name = "{standard input}"; /* For error messages. */
+ }
+ if (file_handle < 0)
+ as_perror ("Can't open %s for reading", file_name);
+ if ( preprocess )
+ {
+ /*
+ * This code was written in haste for a frobbed BSD 4.2.
+ * I have a flight to catch: will someone please do proper
+ * error checks? - Dean.
+ */
+ int pid;
+ char temporary_file_name[12];
+ int fd;
+ union wait status;
+
+ (void)strcpy (temporary_file_name, "#appXXXXXX");
+ (void)mktemp (temporary_file_name);
+ pid = vfork ();
+ if (pid == -1)
+ {
+ as_perror ("Vfork failed", file_name);
+ _exit (144);
+ }
+ if (pid == 0)
+ {
+ (void)dup2 (file_handle, fileno(stdin));
+ fd = open (temporary_file_name, O_WRONLY + O_TRUNC + O_CREAT, 0666);
+ if (fd == -1)
+ {
+ (void)write(2,"Can't open temporary\n",21);
+ _exit (99);
+ }
+ (void)dup2 (fd, fileno(stdout));
+ /* JF for testing #define PREPROCESSOR "/lib/app" */
+#define PREPROCESSOR "./app"
+ execl (PREPROCESSOR, PREPROCESSOR, 0);
+ execl ("app","app",0);
+ (void)write(2,"Exec of app failed. Get help.\n",31);
+ (void)unlink(temporary_file_name);
+ _exit (11);
+ }
+ (void)wait (& status);
+ if (status.w_status & 0xFF00) /* JF was 0xF000, was wrong */
+ {
+ file_handle = -1;
+ as_bad( "Can't preprocess file \"%s\", status = %xx", file_name, status.w_status );
+ }
+ else
+ {
+ file_handle = open (temporary_file_name, O_RDONLY, 0);
+ if ( ! debugging && unlink(temporary_file_name))
+ as_perror ("Can't delete temp file %s", temporary_file_name);
+ }
+ if (file_handle == -1)
+ as_perror ("Can't retrieve temp file %s", temporary_file_name);
+ }
+}
+#else
+
+void
+ input_file_open (filename,pre)
+char * filename; /* "" means use stdin. Must not be 0. */
+int pre;
+{
+ int c;
+ char buf[80];
+
+ preprocess = pre;
+
+ assert( filename != 0 ); /* Filename may not be NULL. */
+ if (filename[0]) { /* We have a file name. Suck it and see. */
+ f_in=fopen(filename,"r");
+ file_name=filename;
+ } else { /* use stdin for the input file. */
+ f_in = stdin;
+ file_name = "{standard input}"; /* For error messages. */
+ }
+ if (f_in == (FILE *)0) {
+ as_perror ("Can't open %s for reading", file_name);
+ return;
+ }
+
+#ifndef HO_VMS
+ /* Ask stdio to buffer our input at BUFFER_SIZE, with a dynamically
+ allocated buffer. */
+ setvbuf(f_in, (char *)NULL, _IOFBF, BUFFER_SIZE);
+#endif /* HO_VMS */
+
+ c = getc(f_in);
+ if (c == '#') { /* Begins with comment, may not want to preprocess */
+ c = getc(f_in);
+ if (c == 'N') {
+ fgets(buf,80,f_in);
+ if (!strcmp(buf,"O_APP\n"))
+ preprocess=0;
+ if (!strchr(buf,'\n'))
+ ungetc('#',f_in); /* It was longer */
+ else
+ ungetc('\n',f_in);
+ } else if (c == '\n')
+ ungetc('\n',f_in);
+ else
+ ungetc('#',f_in);
+ } else
+ ungetc(c,f_in);
+
+#ifdef DONTDEF
+ if ( preprocess ) {
+ char temporary_file_name[17];
+ FILE *f_out;
+
+ (void)strcpy (temporary_file_name, "/tmp/#appXXXXXX");
+ (void)mktemp (temporary_file_name);
+ f_out=fopen(temporary_file_name,"w+");
+ if (f_out == (FILE *)0)
+ as_perror("Can't open temp file %s",temporary_file_name);
+
+ /* JF this will have to be moved on any system that
+ does not support removal of open files. */
+ (void)unlink(temporary_file_name);/* JF do it NOW */
+ do_scrub(f_in,f_out);
+ (void)fclose(f_in); /* All done with it */
+ (void)rewind(f_out);
+ f_in=f_out;
+ }
+#endif
+}
+#endif
+
+/* Close input file. */
+void input_file_close() {
+ if (f_in != NULL) {
+ fclose (f_in);
+ } /* don't close a null file pointer */
+ f_in = 0;
+} /* input_file_close() */
+
+char *
+ input_file_give_next_buffer (where)
+char * where; /* Where to place 1st character of new buffer. */
+{
+ char * return_value; /* -> Last char of what we read, + 1. */
+ register int size;
+
+ if (f_in == (FILE *)0)
+ return 0;
+ /*
+ * fflush (stdin); could be done here if you want to synchronise
+ * stdin and stdout, for the case where our input file is stdin.
+ * Since the assembler shouldn't do any output to stdout, we
+ * don't bother to synch output and input.
+ */
+ if (preprocess) {
+ char *p;
+ int n;
+ int ch;
+ extern FILE *scrub_file;
+
+ scrub_file=f_in;
+ for (p = where, n = BUFFER_SIZE; n; --n) {
+
+ ch = do_scrub_next_char(scrub_from_file, scrub_to_file);
+ if (ch == EOF)
+ break;
+ *p++=ch;
+ }
+ size=BUFFER_SIZE-n;
+ } else
+ size= fread(where,sizeof(char),BUFFER_SIZE,f_in);
+ if (size < 0)
+ {
+ as_perror ("Can't read from %s", file_name);
+ size = 0;
+ }
+ if (size)
+ return_value = where + size;
+ else
+ {
+ if (fclose (f_in))
+ as_perror ("Can't close %s", file_name);
+ f_in = (FILE *)0;
+ return_value = 0;
+ }
+ return (return_value);
+}
+
+/* end of input-file.c */
diff --git a/gnu/usr.bin/as/input-file.h b/gnu/usr.bin/as/input-file.h
new file mode 100644
index 0000000..5c88c7c
--- /dev/null
+++ b/gnu/usr.bin/as/input-file.h
@@ -0,0 +1,88 @@
+/* input_file.h header for input-file.c
+ Copyright (C) 1987, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*"input_file.c":Operating-system dependant functions to read source files.*/
+
+/*
+ * $Id: input-file.h,v 1.3 1993/10/02 20:57:38 pk Exp $
+ */
+
+
+/*
+ * No matter what the operating system, this module must provide the
+ * following services to its callers.
+ *
+ * input_file_begin() Call once before anything else.
+ *
+ * input_file_end() Call once after everything else.
+ *
+ * input_file_buffer_size() Call anytime. Returns largest possible
+ * delivery from
+ * input_file_give_next_buffer().
+ *
+ * input_file_open(name) Call once for each input file.
+ *
+ * input_file_give_next_buffer(where) Call once to get each new buffer.
+ * Return 0: no more chars left in file,
+ * the file has already been closed.
+ * Otherwise: return a pointer to just
+ * after the last character we read
+ * into the buffer.
+ * If we can only read 0 characters, then
+ * end-of-file is faked.
+ *
+ * input_file_push() Push state, which can be restored
+ * later. Does implicit input_file_begin.
+ * Returns char * to saved state.
+ *
+ * input_file_pop (arg) Pops previously saved state.
+ *
+ * input_file_close () Closes opened file.
+ *
+ * All errors are reported (using as_perror) so caller doesn't have to think
+ * about I/O errors. No I/O errors are fatal: an end-of-file may be faked.
+ */
+
+#if __STDC__ == 1
+
+char *input_file_give_next_buffer(char *where);
+char *input_file_push(void);
+int input_file_buffer_size(void);
+int input_file_is_open(void);
+void input_file_begin(void);
+void input_file_close(void);
+void input_file_end(void);
+void input_file_open(char *filename, int pre);
+void input_file_pop(char *arg);
+
+#else /* not __STDC__ */
+
+char *input_file_give_next_buffer();
+char *input_file_push();
+int input_file_buffer_size();
+int input_file_is_open();
+void input_file_begin();
+void input_file_close();
+void input_file_end();
+void input_file_open();
+void input_file_pop();
+
+#endif /* not __STDC__ */
+
+/* end of input_file.h */
diff --git a/gnu/usr.bin/as/input-scrub.c b/gnu/usr.bin/as/input-scrub.c
new file mode 100644
index 0000000..73df1a2
--- /dev/null
+++ b/gnu/usr.bin/as/input-scrub.c
@@ -0,0 +1,436 @@
+/* input_scrub.c - Break up input buffers into whole numbers of lines.
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef lint
+static char rcsid[] = "$Id: input-scrub.c,v 1.3 1993/10/02 20:57:39 pk Exp $";
+#endif
+
+#include <errno.h> /* Need this to make errno declaration right */
+#include "as.h"
+#include "input-file.h"
+
+/*
+ * O/S independent module to supply buffers of sanitised source code
+ * to rest of assembler. We get sanitized input data of arbitrary length.
+ * We break these buffers on line boundaries, recombine pieces that
+ * were broken across buffers, and return a buffer of full lines to
+ * the caller.
+ * The last partial line begins the next buffer we build and return to caller.
+ * The buffer returned to caller is preceeded by BEFORE_STRING and followed
+ * by AFTER_STRING, as sentinels. The last character before AFTER_STRING
+ * is a newline.
+ * Also looks after line numbers, for e.g. error messages.
+ */
+
+/*
+ * We don't care how filthy our buffers are, but our callers assume
+ * that the following sanitation has already been done.
+ *
+ * No comments, reduce a comment to a space.
+ * Reduce a tab to a space unless it is 1st char of line.
+ * All multiple tabs and spaces collapsed into 1 char. Tab only
+ * legal if 1st char of line.
+ * # line file statements converted to .line x;.file y; statements.
+ * Escaped newlines at end of line: remove them but add as many newlines
+ * to end of statement as you removed in the middle, to synch line numbers.
+ */
+
+#define BEFORE_STRING ("\n")
+#define AFTER_STRING ("\0") /* memcpy of 0 chars might choke. */
+#define BEFORE_SIZE (1)
+#define AFTER_SIZE (1)
+
+static char * buffer_start; /*->1st char of full buffer area. */
+static char * partial_where; /*->after last full line in buffer. */
+static int partial_size; /* >= 0. Number of chars in partial line in buffer. */
+static char save_source[AFTER_SIZE];
+/* Because we need AFTER_STRING just after last */
+/* full line, it clobbers 1st part of partial */
+/* line. So we preserve 1st part of partial */
+/* line here. */
+static int buffer_length; /* What is the largest size buffer that */
+/* input_file_give_next_buffer() could */
+/* return to us? */
+
+/* Saved information about the file that .include'd this one. When we hit EOF,
+ we automatically pop to that file. */
+
+static char *next_saved_file;
+
+/* We can have more than one source file open at once, though the info for all
+ but the latest one are saved off in a struct input_save. These files remain
+ open, so we are limited by the number of open files allowed by the
+ underlying OS. We may also sequentially read more than one source file in an
+ assembly. */
+
+/* We must track the physical file and line number for error messages. We also
+ track a "logical" file and line number corresponding to (C?) compiler
+ source line numbers. Whenever we open a file we must fill in
+ physical_input_file. So if it is NULL we have not opened any files yet. */
+
+char *physical_input_file;
+char *logical_input_file;
+
+typedef unsigned int line_numberT; /* 1-origin line number in a source file. */
+/* A line ends in '\n' or eof. */
+
+line_numberT physical_input_line;
+line_numberT logical_input_line;
+
+/* Struct used to save the state of the input handler during include files */
+struct input_save {
+ char *buffer_start;
+ char *partial_where;
+ int partial_size;
+ char save_source[AFTER_SIZE];
+ int buffer_length;
+ char *physical_input_file;
+ char *logical_input_file;
+ line_numberT physical_input_line;
+ line_numberT logical_input_line;
+ char *next_saved_file; /* Chain of input_saves */
+ char *input_file_save; /* Saved state of input routines */
+ char *saved_position; /* Caller's saved position in buf */
+};
+
+#if __STDC__ == 1
+static void as_1_char(unsigned int c, FILE *stream);
+#else /* __STDC__ */
+static void as_1_char();
+#endif /* not __STDC__ */
+
+/* Push the state of input reading and scrubbing so that we can #include.
+ The return value is a 'void *' (fudged for old compilers) to a save
+ area, which can be restored by passing it to input_scrub_pop(). */
+char *input_scrub_push(saved_position)
+char *saved_position;
+{
+ register struct input_save *saved;
+
+ saved = (struct input_save *) xmalloc(sizeof *saved);
+
+ saved->saved_position = saved_position;
+ saved->buffer_start = buffer_start;
+ saved->partial_where = partial_where;
+ saved->partial_size = partial_size;
+ saved->buffer_length = buffer_length;
+ saved->physical_input_file = physical_input_file;
+ saved->logical_input_file = logical_input_file;
+ saved->physical_input_line = physical_input_line;
+ saved->logical_input_line = logical_input_line;
+ memcpy(save_source, saved->save_source, sizeof(save_source));
+ saved->next_saved_file = next_saved_file;
+ saved->input_file_save = input_file_push();
+
+ input_scrub_begin(); /* Reinitialize! */
+
+ return((char *) saved);
+} /* input_scrub_push() */
+
+char *
+ input_scrub_pop (arg)
+char *arg;
+{
+ register struct input_save *saved;
+ char *saved_position;
+
+ input_scrub_end (); /* Finish off old buffer */
+
+ saved = (struct input_save *)arg;
+
+ input_file_pop (saved->input_file_save);
+ saved_position = saved->saved_position;
+ buffer_start = saved->buffer_start;
+ buffer_length = saved->buffer_length;
+ physical_input_file = saved->physical_input_file;
+ logical_input_file = saved->logical_input_file;
+ physical_input_line = saved->physical_input_line;
+ logical_input_line = saved->logical_input_line;
+ partial_where = saved->partial_where;
+ partial_size = saved->partial_size;
+ next_saved_file = saved->next_saved_file;
+ memcpy(saved->save_source, save_source, sizeof (save_source));
+
+ free(arg);
+ return saved_position;
+}
+
+
+void
+ input_scrub_begin ()
+{
+ know(strlen(BEFORE_STRING) == BEFORE_SIZE);
+ know(strlen(AFTER_STRING) == AFTER_SIZE || (AFTER_STRING[0] == '\0' && AFTER_SIZE == 1));
+
+ input_file_begin ();
+
+ buffer_length = input_file_buffer_size ();
+
+ buffer_start = xmalloc((long)(BEFORE_SIZE + buffer_length + buffer_length + AFTER_SIZE));
+ memcpy(buffer_start, BEFORE_STRING, (int) BEFORE_SIZE);
+
+ /* Line number things. */
+ logical_input_line = 0;
+ logical_input_file = (char *)NULL;
+ physical_input_file = NULL; /* No file read yet. */
+ next_saved_file = NULL; /* At EOF, don't pop to any other file */
+ do_scrub_begin();
+}
+
+void
+ input_scrub_end ()
+{
+ if (buffer_start)
+ {
+ free (buffer_start);
+ buffer_start = 0;
+ input_file_end ();
+ }
+}
+
+/* Start reading input from a new file. */
+
+char * /* Return start of caller's part of buffer. */
+ input_scrub_new_file (filename)
+char * filename;
+{
+ input_file_open (filename, !flagseen['f']);
+ physical_input_file = filename[0] ? filename : "{standard input}";
+ physical_input_line = 0;
+
+ partial_size = 0;
+ return (buffer_start + BEFORE_SIZE);
+}
+
+
+/* Include a file from the current file. Save our state, cause it to
+ be restored on EOF, and begin handling a new file. Same result as
+ input_scrub_new_file. */
+
+char *
+ input_scrub_include_file (filename, position)
+char *filename;
+char *position;
+{
+ next_saved_file = input_scrub_push(position);
+ return input_scrub_new_file (filename);
+}
+
+void
+ input_scrub_close ()
+{
+ input_file_close ();
+}
+char *
+ input_scrub_next_buffer (bufp)
+char **bufp;
+{
+ register char * limit; /*->just after last char of buffer. */
+
+ *bufp = buffer_start + BEFORE_SIZE;
+
+#ifdef DONTDEF
+ if (preprocess) {
+ if (save_buffer) {
+ *bufp = save_buffer;
+ save_buffer = 0;
+ }
+ limit = input_file_give_next_buffer(buffer_start+BEFORE_SIZE);
+ if (!limit) {
+ partial_where = 0;
+ if (partial_size)
+ as_warn("Partial line at end of file ignored");
+ return partial_where;
+ }
+
+ if (partial_size)
+ memcpy(partial_where, save_source, (int) AFTER_SIZE);
+ do_scrub(partial_where, partial_size,
+ buffer_start + BEFORE_SIZE,
+ limit - (buffer_start + BEFORE_SIZE),
+ &out_string, &out_length);
+ limit=out_string + out_length;
+ for (p=limit;*--p != '\n';)
+ ;
+ p++;
+ if (p <= buffer_start+BEFORE_SIZE)
+ as_fatal("Source line too long. Please change file '%s' and re-make the assembler.", __FILE__);
+
+ partial_where = p;
+ partial_size = limit-p;
+ memcpy(save_source, partial_where, (int) AFTER_SIZE);
+ memcpy(partial_where, AFTER_STRING, (int) AFTER_SIZE);
+
+ save_buffer = *bufp;
+ *bufp = out_string;
+
+ return partial_where;
+ }
+
+ /* We're not preprocessing. Do the right thing */
+#endif
+ if (partial_size) {
+ memcpy(buffer_start + BEFORE_SIZE, partial_where, (int) partial_size);
+ memcpy(buffer_start + BEFORE_SIZE, save_source, (int) AFTER_SIZE);
+ }
+ limit = input_file_give_next_buffer (buffer_start + BEFORE_SIZE + partial_size);
+ if (limit) {
+ register char * p; /* Find last newline. */
+
+ for (p = limit; *--p != '\n';) ;;
+ ++p;
+ if (p <= buffer_start + BEFORE_SIZE) {
+ as_fatal("Source line too long. Please change file %s then rebuild assembler.", __FILE__);
+ }
+ partial_where = p;
+ partial_size = limit - p;
+ memcpy(save_source, partial_where, (int) AFTER_SIZE);
+ memcpy(partial_where, AFTER_STRING, (int) AFTER_SIZE);
+ } else {
+ partial_where = 0;
+ if (partial_size > 0) {
+ as_warn("Partial line at end of file ignored");
+ }
+ /* If we should pop to another file at EOF, do it. */
+ if (next_saved_file) {
+ *bufp = input_scrub_pop (next_saved_file); /* Pop state */
+ /* partial_where is now correct to return, since we popped it. */
+ }
+ }
+ return(partial_where);
+} /* input_scrub_next_buffer() */
+
+/*
+ * The remaining part of this file deals with line numbers, error
+ * messages and so on.
+ */
+
+
+int
+ seen_at_least_1_file () /* TRUE if we opened any file. */
+{
+ return (physical_input_file != NULL);
+}
+
+void
+ bump_line_counters ()
+{
+ ++ physical_input_line;
+ /* ++ logical_input_line; FIXME-now remove this. */
+}
+
+/*
+ * new_logical_line()
+ *
+ * Tells us what the new logical line number and file are.
+ * If the line_number is <0, we don't change the current logical line number.
+ * If the fname is NULL, we don't change the current logical file name.
+ */
+void new_logical_line(fname, line_number)
+char *fname; /* DON'T destroy it! We point to it! */
+int line_number;
+{
+ if (fname) {
+ logical_input_file = fname;
+ } /* if we have a file name */
+
+ if (line_number >= 0) {
+ logical_input_line = line_number;
+ } /* if we have a line number */
+} /* new_logical_line() */
+
+/*
+ * a s _ w h e r e ()
+ *
+ * Write a line to stderr locating where we are in reading
+ * input source files.
+ * As a sop to the debugger of AS, pretty-print the offending line.
+ */
+void as_where() {
+ char *p;
+ line_numberT line;
+ extern char *myname;
+
+ if (logical_input_file && (logical_input_line > 0)) {
+ p = logical_input_file;
+ line = logical_input_line;
+ } else {
+ p = physical_input_file;
+ line = physical_input_line;
+ } /* line number should match file name */
+
+ fprintf(stderr, "%s: %s:%u: ", myname, p, line);
+
+ return;
+} /* as_where() */
+
+
+
+
+/*
+ * a s _ h o w m u c h ()
+ *
+ * Output to given stream how much of line we have scanned so far.
+ * Assumes we have scanned up to and including input_line_pointer.
+ * No free '\n' at end of line.
+ */
+void
+ as_howmuch (stream)
+FILE * stream; /* Opened for write please. */
+{
+ register char * p; /* Scan input line. */
+ /* register char c; JF unused */
+
+ for (p = input_line_pointer - 1; * p != '\n'; --p)
+ {
+ }
+ ++ p; /* p->1st char of line. */
+ for (; p <= input_line_pointer; p++)
+ {
+ /* Assume ASCII. EBCDIC & other micro-computer char sets ignored. */
+ /* c = *p & 0xFF; JF unused */
+ as_1_char(*p, stream);
+ }
+}
+
+static void as_1_char (c,stream)
+unsigned int c;
+FILE *stream;
+{
+ if (c > 127)
+ {
+ (void)putc('%', stream);
+ c -= 128;
+ }
+ if (c < 32)
+ {
+ (void)putc('^', stream);
+ c += '@';
+ }
+ (void)putc(c, stream);
+}
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of input_scrub.c */
diff --git a/gnu/usr.bin/as/link.cmd b/gnu/usr.bin/as/link.cmd
new file mode 100644
index 0000000..a035ca8
--- /dev/null
+++ b/gnu/usr.bin/as/link.cmd
@@ -0,0 +1,10 @@
+ALIGN=1024
+RESNUM 0x0000, 0x8000
+; Putting in .lit1 gives errors.
+ORDER .data=0x80002000, .data1, .lit, .bss
+; Let's put this on the command line so it goes first, which is what
+; GDB expects.
+; LOAD /s2/amd/29k/lib/crt0.o
+LOAD /s2/amd/29k/lib/libqcb0h.lib
+LOAD /s2/amd/29k/lib/libscb0h.lib
+LOAD /s2/amd/29k/lib/libacb0h.lib
diff --git a/gnu/usr.bin/as/listing.c b/gnu/usr.bin/as/listing.c
new file mode 100644
index 0000000..2f8b1f0
--- /dev/null
+++ b/gnu/usr.bin/as/listing.c
@@ -0,0 +1,849 @@
+/* listing.c - mainting assembly listings
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ Contributed by Steve Chamberlain
+ sac@cygnus.com
+
+
+ A listing page looks like:
+
+ LISTING_HEADER sourcefilename pagenumber
+ TITLE LINE
+ SUBTITLE LINE
+ linenumber address data source
+ linenumber address data source
+ linenumber address data source
+ linenumber address data source
+
+ If not overridden, the listing commands are:
+
+ .title "stuff"
+ Put "stuff" onto the title line
+ .sbttl "stuff"
+ Put stuff onto the subtitle line
+
+ If these commands come within 10 lines of the top of the page, they
+ will affect the page they are on, as well as any subsequent page
+
+ .eject
+ Thow a page
+ .list
+ Increment the enable listing counter
+ .nolist
+ Decrement the enable listing counter
+
+ .psize Y[,X]
+ Set the paper size to X wide and Y high. Setting a psize Y of
+ zero will suppress form feeds except where demanded by .eject
+
+ If the counter goes below zero, listing is suppressed.
+
+
+ Listings are a maintained by read calling various listing_<foo>
+ functions. What happens most is that the macro NO_LISTING is not
+ defined (from the Makefile), then the macro LISTING_NEWLINE expands
+ into a call to listing_newline. The call is done from read.c, every
+ time it sees a newline, and -l is on the command line.
+
+ The function listing_newline remembers the frag associated with the
+ newline, and creates a new frag - note that this is wasteful, but not
+ a big deal, since listing slows things down a lot anyway. The
+ function also rememebers when the filename changes.
+
+ When all the input has finished, and gas has had a chance to settle
+ down, the listing is output. This is done by running down the list of
+ frag/source file records, and opening the files as needed and printing
+ out the bytes and chars associated with them.
+
+ The only things which the architecture can change about the listing
+ are defined in these macros:
+
+ LISTING_HEADER The name of the architecture
+ LISTING_WORD_SIZE The make of the number of bytes in a word, this determines
+ the clumping of the output data. eg a value of
+ 2 makes words look like 1234 5678, whilst 1
+ would make the same value look like 12 34 56
+ 78
+ LISTING_LHS_WIDTH Number of words of above size for the lhs
+
+ LISTING_LHS_WIDTH_SECOND Number of words for the data on the lhs
+ for the second line
+
+ LISTING_LHS_CONT_LINES Max number of lines to use up for a continutation
+ LISTING_RHS_WIDTH Number of chars from the input file to print
+ on a line
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: listing.c,v 1.1 1993/10/02 20:57:40 pk Exp $";
+#endif
+
+#include "as.h"
+
+#ifndef NO_LISTING
+
+#include <obstack.h>
+#include "input-file.h"
+#include "targ-cpu.h"
+
+#ifndef LISTING_HEADER
+#define LISTING_HEADER "GAS LISTING"
+#endif
+#ifndef LISTING_WORD_SIZE
+#define LISTING_WORD_SIZE 4
+#endif
+#ifndef LISTING_LHS_WIDTH
+#define LISTING_LHS_WIDTH 1
+#endif
+#ifndef LISTING_LHS_WIDTH_SECOND
+#define LISTING_LHS_WIDTH_SECOND 1
+#endif
+#ifndef LISTING_RHS_WIDTH
+#define LISTING_RHS_WIDTH 100
+#endif
+#ifndef LISTING_LHS_CONT_LINES
+#define LISTING_LHS_CONT_LINES 4
+#endif
+
+
+/* This structure remembers which .s were used */
+typedef struct file_info_struct {
+ char *filename;
+ int linenum;
+ FILE *file;
+ struct file_info_struct *next;
+ int end_pending;
+} file_info_type ;
+
+
+/* this structure rememebrs which line from which file goes into which frag */
+typedef struct list_info_struct {
+ /* Frag which this line of source is nearest to */
+ fragS *frag;
+ /* The actual line in the source file */
+ unsigned int line;
+ /* Pointer to the file info struct for the file which this line
+ belongs to */
+ file_info_type *file;
+
+ /* Next in list */
+ struct list_info_struct *next;
+
+
+ /* Pointer to the file info struct for the high level language
+ source line that belongs here */
+ file_info_type *hll_file;
+
+ /* High level language source line */
+ int hll_line;
+
+
+ /* Pointer to any error message associated with this line */
+ char *message;
+
+ enum {
+ EDICT_NONE,
+ EDICT_SBTTL,
+ EDICT_TITLE,
+ EDICT_NOLIST,
+ EDICT_LIST,
+ EDICT_EJECT,
+ } edict;
+ char *edict_arg;
+} list_info_type;
+
+static struct list_info_struct *head;
+struct list_info_struct *listing_tail;
+extern int listing;
+extern unsigned int physical_input_line;
+extern fragS *frag_now;
+
+static int paper_width = 200;
+static int paper_height = 60;
+
+/* this static array is used to keep the text of data to be printed
+ before the start of the line. It is stored so we can give a bit
+ more info on the next line. To much, and large initialized arrays
+ will use up lots of paper. */
+
+static char data_buffer[100];
+static unsigned int data_buffer_size;
+
+static void
+ listing_message(name, message)
+char *name;
+char *message;
+{
+ unsigned int l = strlen(name) + strlen(message) + 1;
+ char *n = malloc(l);
+ strcpy(n,name);
+ strcat(n,message);
+ if (listing_tail != (list_info_type *)NULL) {
+ listing_tail->message = n;
+ }
+
+ return;
+} /* lising_message() */
+
+void
+ listing_warning(message)
+char *message;
+{
+ listing_message("Warning:", message);
+}
+
+void
+ listing_error(message)
+char *message;
+{
+ listing_message("Error:", message);
+}
+
+static file_info_type *file_info_head;
+
+static file_info_type *
+ file_info(file_name)
+char *file_name;
+{
+ /* Find an entry with this file name */
+ file_info_type *p = file_info_head;
+
+ while (p != (file_info_type *)NULL) {
+ if (strcmp(p->filename, file_name) == 0)
+ return(p);
+ p = p->next;
+ }
+
+ /* Make new entry */
+
+ p = (file_info_type *) xmalloc(sizeof(file_info_type));
+ p->next = file_info_head;
+ file_info_head = p;
+ p->filename = xmalloc(strlen(file_name)+1);
+ strcpy(p->filename, file_name);
+ p->linenum = 0;
+ p->end_pending = 0;
+
+ p->file = fopen(p->filename,"r");
+ return(p);
+} /* file_info() */
+
+
+static void
+ new_frag()
+{
+ frag_wane(frag_now);
+ frag_new(0);
+}
+
+void
+ listing_newline(ps)
+char *ps;
+{
+ char *s = ps;
+ extern char *file_name;
+ static unsigned int last_line = 0xffff ;
+
+
+ list_info_type *new;
+ if (physical_input_line != last_line) {
+ last_line = physical_input_line;
+ new_frag();
+
+ new = (list_info_type *) malloc(sizeof(list_info_type));
+ new->frag = frag_now;
+ new->line = physical_input_line ;
+ new->file = file_info(file_name);
+
+ if (listing_tail) {
+ listing_tail->next = new;
+ } else {
+ head = new;
+ }
+
+ listing_tail = new;
+ new->next = (list_info_type *) NULL;
+ new->message = (char *) NULL;
+ new->edict = EDICT_NONE;
+ new->hll_file = (file_info_type*) NULL;
+ new->hll_line = 0;
+ new_frag();
+ }
+
+ return;
+} /* listing_newline() */
+
+
+/* This function returns the next source line from the file supplied,
+ truncated to size. It appends a fake line to the end of each input
+ file to make. */
+
+static char *
+ buffer_line(file, line, size)
+file_info_type *file;
+char *line;
+unsigned int size;
+{
+ unsigned int count = 0;
+ int c;
+
+ char *p = line;
+
+ /* If we couldn't open the file, return an empty line */
+ if (file->file == (FILE*) NULL) {
+ return("");
+ }
+
+ if (file->end_pending == 10) {
+ *p ++ = '\n';
+ rewind(file->file);
+ file->linenum = 0;
+ file->end_pending = 0;
+ }
+
+ c = fgetc(file->file);
+ size -= 1; /* leave room for null */
+
+ while (c != EOF && c != '\n') {
+ if (count < size)
+ *p++ = c;
+ count++;
+
+ c = fgetc(file->file);
+ }
+
+ if (c == EOF) {
+ file->end_pending ++;
+ *p++ = 'E';
+ *p++ = 'O';
+ *p++ = 'F';
+ }
+
+ file->linenum++;
+ *p++ = 0;
+ return(line);
+} /* buffer_line() */
+
+static char *fn;
+
+static unsigned int eject; /* Eject pending */
+static unsigned int page; /* Current page number */
+static char *title; /* current title */
+static char *subtitle; /* current subtitle */
+static unsigned int on_page; /* number of lines printed on current page */
+
+static void
+ listing_page(list)
+list_info_type *list;
+{
+ /* Grope around, see if we can see a title or subtitle edict
+ coming up soon (we look down 10 lines of the page and see
+ if it's there). */
+
+ if ((eject || (on_page >= paper_height)) && paper_height != 0) {
+ unsigned int c = 10;
+ int had_title = 0;
+ int had_subtitle = 0;
+
+ page++;
+
+ while (c != 0 && list) {
+ if (list->edict == EDICT_SBTTL && !had_subtitle) {
+ had_subtitle = 1;
+ subtitle = list->edict_arg;
+ }
+
+ if (list->edict == EDICT_TITLE && !had_title) {
+ had_title = 1;
+ title = list->edict_arg;
+ }
+ list = list->next;
+ --c;
+ }
+
+ if (page > 1) {
+ printf("\f");
+ }
+
+ printf("%s %s \t\t\tpage %d\n", LISTING_HEADER, fn, page);
+ printf("%s\n", title);
+ printf("%s\n", subtitle);
+ on_page = 3;
+ eject = 0;
+ }
+
+ return;
+} /* listing_page() */
+
+
+static unsigned int
+ calc_hex(list)
+list_info_type *list;
+{
+ list_info_type *first = list;
+ list_info_type *last = first;
+ unsigned int address = ~0;
+
+ fragS *frag;
+ fragS *frag_ptr;
+
+ unsigned int byte_in_frag = 0;
+
+ int anything = 0;
+
+ /* Find first frag which says it belongs to this line */
+ frag = list->frag;
+ while (frag && frag->line != list)
+ frag = frag->fr_next;
+
+ frag_ptr = frag;
+
+ data_buffer_size = 0;
+
+ /* Dump all the frags which belong to this line */
+ while (frag_ptr != (fragS *)NULL && frag_ptr->line == first) {
+ /* Print as many bytes from the fixed part as is sensible */
+ while (byte_in_frag < frag_ptr->fr_fix && data_buffer_size < sizeof(data_buffer)-10) {
+ if (address == ~0) {
+ address = frag_ptr->fr_address;
+ }
+
+ sprintf(data_buffer + data_buffer_size, "%02X", (frag_ptr->fr_literal[byte_in_frag]) & 0xff);
+ data_buffer_size += 2;
+ byte_in_frag++;
+ }
+
+ /* Print as many bytes from the variable part as is sensible */
+ while (byte_in_frag < frag_ptr->fr_var * frag_ptr->fr_offset
+ && data_buffer_size < sizeof(data_buffer)-10) {
+ if (address == ~0) {
+ address = frag_ptr->fr_address;
+ }
+ data_buffer[data_buffer_size++] = '*';
+ data_buffer[data_buffer_size++] = '*';
+
+ byte_in_frag++;
+ }
+
+ frag_ptr = frag_ptr->fr_next;
+ }
+
+ data_buffer[data_buffer_size++] = 0;
+ return address;
+} /* calc_hex() */
+
+static void
+ print_lines(list, string, address)
+list_info_type *list;
+char *string;
+unsigned int address;
+{
+ unsigned int idx;
+ unsigned int nchars;
+ unsigned int lines;
+ unsigned int byte_in_word =0;
+ char *src = data_buffer;
+
+ /* Print the stuff on the first line */
+ listing_page(list);
+ nchars = (LISTING_WORD_SIZE * 2 + 1) * LISTING_LHS_WIDTH ;
+
+ /* Print the hex for the first line */
+ if (address == ~0) {
+ printf("% 4d ", list->line);
+ for (idx = 0; idx < nchars; idx++)
+ printf(" ");
+
+ printf("\t%s\n", string ? string : "");
+ on_page++;
+ listing_page(0);
+ } else {
+ if (had_errors()) {
+ printf("% 4d ???? ", list->line);
+ } else {
+ printf("% 4d %04x ", list->line, address);
+ }
+
+ /* And the data to go along with it */
+ idx = 0;
+
+ while (*src && idx < nchars) {
+ printf("%c%c", src[0], src[1]);
+ src += 2;
+ byte_in_word++;
+
+ if (byte_in_word == LISTING_WORD_SIZE) {
+ printf(" ");
+ idx++;
+ byte_in_word = 0;
+ }
+ idx+=2;
+ }
+
+ for (;idx < nchars; idx++)
+ printf(" ");
+
+ printf("\t%s\n", string ? string : "");
+ on_page++;
+ listing_page(list);
+ if (list->message) {
+ printf("**** %s\n",list->message);
+ listing_page(list);
+ on_page++;
+ }
+
+ for (lines = 0; lines < LISTING_LHS_CONT_LINES && *src; lines++) {
+ nchars = ((LISTING_WORD_SIZE*2) +1) * LISTING_LHS_WIDTH_SECOND -1;
+ idx = 0;
+ /* Print any more lines of data, but more compactly */
+ printf("% 4d ", list->line);
+
+ while (*src && idx < nchars) {
+ printf("%c%c", src[0], src[1]);
+ src+=2;
+ idx+=2;
+ byte_in_word++;
+ if (byte_in_word == LISTING_WORD_SIZE) {
+ printf(" ");
+ idx++;
+ byte_in_word = 0;
+ }
+ }
+
+ printf("\n");
+ on_page++;
+ listing_page(list);
+ }
+ }
+} /* print_lines() */
+
+
+static void
+ list_symbol_table()
+{
+ extern symbolS *symbol_rootP;
+ symbolS *ptr;
+
+ eject = 1;
+ listing_page(0);
+ printf("DEFINED SYMBOLS\n");
+ on_page++;
+
+ for (ptr = symbol_rootP; ptr != (symbolS*)NULL; ptr = symbol_next(ptr)) {
+ if (ptr->sy_frag->line) {
+ if (strlen(S_GET_NAME(ptr))) {
+ printf("%20s:%-5d %2d:%08x %s \n",
+ ptr->sy_frag->line->file->filename,
+ ptr->sy_frag->line->line,
+ S_GET_SEGMENT(ptr),
+ S_GET_VALUE(ptr),
+ S_GET_NAME(ptr));
+
+ on_page++;
+ listing_page(0);
+ }
+ }
+
+ }
+
+ printf("\n");
+ on_page++;
+ listing_page(0);
+ printf("UNDEFINED SYMBOLS\n");
+ on_page++;
+ listing_page(0);
+
+ for (ptr = symbol_rootP; ptr != (symbolS*)NULL; ptr = symbol_next(ptr)) {
+ if (ptr && strlen(S_GET_NAME(ptr)) != 0) {
+ if (ptr->sy_frag->line == 0) {
+ printf("%s\n", S_GET_NAME(ptr));
+ on_page++;
+ listing_page(0);
+ }
+ }
+ }
+
+ return;
+} /* list_symbol_table() */
+
+void
+ print_source(current_file, list, buffer, width)
+file_info_type *current_file;
+list_info_type *list;
+char *buffer;
+unsigned int width;
+{
+ if (current_file->file) {
+ while (current_file->linenum < list->hll_line) {
+ char * p = buffer_line(current_file, buffer, width);
+ printf("%4d:%-13s **** %s\n", current_file->linenum, current_file->filename, p);
+ on_page++;
+ listing_page(list);
+ }
+ }
+
+ return;
+} /* print_source() */
+
+/* Sometimes the user doesn't want to be bothered by the debugging
+ records inserted by the compiler, see if the line is suspicioous */
+
+static int
+ debugging_pseudo(line)
+char *line;
+{
+ while (isspace(*line))
+ line++;
+
+ if (*line != '.') return 0;
+
+ line++;
+
+ if (strncmp(line, "def",3) == 0) return 1;
+ if (strncmp(line, "val",3) == 0) return 1;
+ if (strncmp(line, "scl",3) == 0) return 1;
+ if (strncmp(line, "line",4) == 0) return 1;
+ if (strncmp(line, "endef",5) == 0) return 1;
+ if (strncmp(line, "ln",2) == 0) return 1;
+ if (strncmp(line, "type",4) == 0) return 1;
+ if (strncmp(line, "size",4) == 0) return 1;
+ if (strncmp(line, "dim",3) == 0) return 1;
+ if (strncmp(line, "tag",3) == 0) return 1;
+
+ return(0);
+} /* debugging_pseudo() */
+
+void
+ listing_listing(name)
+char *name;
+{
+ char *buffer;
+ char *message;
+ char *p;
+ file_info_type *current_hll_file = (file_info_type *) NULL;
+ int on_page = 0;
+ int show_listing = 1;
+ list_info_type *list = head;
+ unsigned int addr = 0;
+ unsigned int page = 1;
+ unsigned int prev = 0;
+ unsigned int width;
+
+ buffer = malloc(LISTING_RHS_WIDTH);
+ eject = 1;
+ list = head;
+
+ while (list != (list_info_type *)NULL && 0) {
+ if (list->next)
+ list->frag = list->next->frag;
+ list = list->next;
+ }
+
+ list = head->next;
+
+ while (list) {
+ width = LISTING_RHS_WIDTH > paper_width ? paper_width : LISTING_RHS_WIDTH;
+
+ switch (list->edict) {
+ case EDICT_LIST:
+ show_listing++;
+ break;
+ case EDICT_NOLIST:
+ show_listing--;
+ break;
+ case EDICT_EJECT:
+ break;
+ case EDICT_NONE:
+ break;
+ case EDICT_TITLE:
+ title = list->edict_arg;
+ break;
+ case EDICT_SBTTL:
+ subtitle = list->edict_arg;
+ break;
+ default:
+ abort();
+ }
+
+ if (show_listing > 0) {
+ /* Scan down the list and print all the stuff which can be done
+ with this line (or lines) */
+ message = 0;
+
+ if (list->hll_file) {
+ current_hll_file = list->hll_file;
+ }
+
+ if (current_hll_file && list->hll_line && listing & LISTING_HLL) {
+ print_source(current_hll_file, list, buffer, width);
+ }
+
+ p = buffer_line(list->file, buffer, width);
+
+ if (! ((listing & LISTING_NODEBUG) && debugging_pseudo(p))) {
+ print_lines(list, p, calc_hex(list));
+ }
+
+ if (list->edict == EDICT_EJECT) {
+ eject = 1;
+ }
+ } else {
+
+ p = buffer_line(list->file, buffer, width);
+ }
+
+ list = list->next;
+ }
+ free(buffer);
+} /* listing_listing() */
+
+void
+ listing_print(name)
+char *name;
+{
+ title = "";
+ subtitle = "";
+
+ if (listing & LISTING_NOFORM)
+ {
+ paper_height = 0;
+ }
+
+ if (listing & LISTING_LISTING)
+ {
+ listing_listing(name);
+
+ }
+ if (listing & LISTING_SYMBOLS)
+ {
+ list_symbol_table();
+ }
+} /* listing_print() */
+
+
+void
+ listing_file(name)
+char *name;
+{
+ fn = name;
+}
+
+void
+ listing_eject()
+{
+ listing_tail->edict = EDICT_EJECT;
+ return;
+}
+
+void
+ listing_flags()
+{
+
+}
+
+void
+ listing_list(on)
+unsigned int on;
+{
+ listing_tail->edict = on ? EDICT_LIST : EDICT_NOLIST;
+}
+
+
+void
+ listing_psize()
+{
+ paper_height = get_absolute_expression();
+
+ if (paper_height < 0 || paper_height > 1000) {
+ paper_height = 0;
+ as_warn("strantge paper height, set to no form");
+ }
+
+ if (*input_line_pointer == ',') {
+ input_line_pointer++;
+ paper_width = get_absolute_expression();
+ }
+
+ return;
+} /* listing_psize() */
+
+
+void
+ listing_title(depth)
+unsigned int depth;
+{
+ char *start;
+ char *title;
+ unsigned int length;
+
+ SKIP_WHITESPACE();
+
+ if (*input_line_pointer == '\"') {
+ input_line_pointer++;
+ start = input_line_pointer;
+
+ while (*input_line_pointer) {
+ if (*input_line_pointer == '\"') {
+ length = input_line_pointer - start;
+ title = malloc(length + 1);
+ memcpy(title, start, length);
+ title[length] = 0;
+ listing_tail->edict = depth ? EDICT_SBTTL : EDICT_TITLE;
+ listing_tail->edict_arg = title;
+ input_line_pointer++;
+ demand_empty_rest_of_line();
+ return;
+ } else if (*input_line_pointer == '\n') {
+ as_bad("New line in title");
+ demand_empty_rest_of_line();
+ return;
+ } else {
+ input_line_pointer++;
+ }
+ }
+ } else {
+ as_bad("expecting title in quotes");
+ }
+
+ return;
+} /* listing_title() */
+
+
+
+void
+ listing_source_line(line)
+unsigned int line;
+{
+ new_frag();
+ listing_tail->hll_line = line;
+ new_frag();
+ return;
+} /* lising_source_line() */
+
+void
+ listing_source_file(file)
+char *file;
+{
+ listing_tail->hll_file = file_info(file);
+}
+
+#endif /* not NO_LISTING */
+
+/* end of listing.c */
diff --git a/gnu/usr.bin/as/listing.h b/gnu/usr.bin/as/listing.h
new file mode 100644
index 0000000..37a8e87
--- /dev/null
+++ b/gnu/usr.bin/as/listing.h
@@ -0,0 +1,95 @@
+/* This file is listing.h
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
+ * $Id: listing.h,v 1.1 1993/10/02 20:57:41 pk Exp $
+ */
+
+
+
+#ifndef __listing_h__
+#define __listing_h__
+
+#define LISTING_LISTING 1
+#define LISTING_SYMBOLS 2
+#define LISTING_NOFORM 4
+#define LISTING_HLL 8
+#define LISTING_NODEBUG 16
+
+#define LISTING_DEFAULT (LISTING_LISTING | LISTING_HLL | LISTING_SYMBOLS)
+
+#ifndef NO_LISTING
+
+#define LISTING_NEWLINE() { if (listing) listing_newline(input_line_pointer); }
+
+
+#if __STDC__ == 1
+
+void listing_eject(void);
+void listing_error(char *message);
+void listing_file(char *name);
+void listing_flags(void);
+void listing_list(unsigned int on);
+void listing_newline(char *ps);
+void listing_print(char *name);
+void listing_psize(void);
+void listing_source_file(char *);
+void listing_source_line(unsigned int);
+void listing_title(unsigned int depth);
+void listing_warning(char *message);
+void listing_width(unsigned int x);
+
+#else /* not __STDC__ */
+
+void listing_eject();
+void listing_error();
+void listing_file();
+void listing_flags();
+void listing_list();
+void listing_newline();
+void listing_print();
+void listing_psize();
+void listing_source_file();
+void listing_source_line();
+void listing_title();
+void listing_warning();
+void listing_width();
+
+#endif /* not __STDC__ */
+
+#else /* NO_LISTING */
+
+#define LISTING_NEWLINE() {;}
+
+/* Dummy functions for when compiled without listing enabled */
+
+#define listing_flags() {;}
+#define listing_list() {;}
+#define listing_eject() {;}
+#define listing_psize() {;}
+#define listing_title(depth) {;}
+#define listing_file(name) {;}
+#define listing_newline(name) {;}
+#define listing_source_line(n) {;}
+#define listing_source_file(n) {;}
+
+#endif /* NO_LISTING */
+
+#endif /* __listing_h__ */
+
+/* end of listing.h */
diff --git a/gnu/usr.bin/as/make-gas.com b/gnu/usr.bin/as/make-gas.com
new file mode 100644
index 0000000..cb3064d
--- /dev/null
+++ b/gnu/usr.bin/as/make-gas.com
@@ -0,0 +1,86 @@
+$! Set the def dir to proper place for use in batch. Works for interactive to.
+$flnm = f$enviroment("PROCEDURE") ! get current procedure name
+$set default 'f$parse(flnm,,,"DEVICE")''f$parse(flnm,,,"DIRECTORY")'
+$!
+$! Command file to build a GNU assembler on VMS
+$!
+$! If you are using a version of GCC that supports global constants
+$! you should remove the define="const=" from the gcc lines.
+$!
+$! Caution: Versions 1.38.1 and earlier had a bug in the handling of
+$! some static constants. If you are using such a version of the
+$! assembler, and you wish to compile without the "const=" hack,
+$! you should first build this version *with* the "const="
+$! definition, and then use that assembler to rebuild it without the
+$! "const=" definition. Failure to do this will result in an assembler
+$! that will mung floating point constants.
+$!
+$! Note: The version of gas shipped on the GCC VMS tapes has been patched
+$! to fix the above mentioned bug.
+$!
+$ write sys$output "If this assembler is going to be used with GCC 1.n, you"
+$ write sys$Output "need to modify the driver to supply the -1 switch to gas."
+$ write sys$output "This is required because of a small change in how global"
+$ write sys$Output "constant variables are handled. Failure to include this"
+$ write sys$output "will result in linker warning messages about mismatched
+$ write sys$output "psect attributes."
+$!
+$ C_DEFS :="""VMS"""
+$! C_DEFS :="""VMS""","""const="""
+$ C_INCLUDES :=/include=([],[.config],[-.include])
+$ C_FLAGS := /debug 'c_includes'
+$!
+$!
+$ if "''p1'" .eqs. "LINK" then goto Link
+$!
+$! This helps gcc 1.nn find the aout/* files.
+$!
+$ aout_dev = f$parse(flnm,,,"DEVICE")
+$ tmp = aout_dev - ":"
+$if f$trnlnm(tmp).nes."" then aout_dev = f$trnlnm(tmp)
+$ aout_dir = aout_dev+f$parse(flnm,,,"DIRECTORY")' -
+ - "GAS]" + "INCLUDE.AOUT.]" - "]["
+$assign 'aout_dir' aout/tran=conc
+$ opcode_dir = aout_dev+f$parse(flnm,,,"DIRECTORY")' -
+ - "GAS]" + "INCLUDE.OPCODE.]" - "]["
+$assign 'opcode_dir' opcode/tran=conc
+$!
+$ gcc 'c_flags'/define=('C_DEFS') as.c
+$ gcc 'c_flags'/define=("error=as_fatal",'C_DEFS') xrealloc.c
+$ gcc 'c_flags'/define=("error=as_fatal",'C_DEFS') xmalloc.c
+$ gcc 'c_flags'/define=("error=as_fatal",'C_DEFS') hash.c
+$ gcc 'c_flags'/define=('C_DEFS') obstack.c
+$ gcc 'c_flags'/define=('C_DEFS') hex-value.c
+$ gcc 'c_flags'/define=('C_DEFS') messages.c
+$ gcc 'c_flags'/define=('C_DEFS') atof-generic.c
+$ gcc 'c_flags'/define=('C_DEFS') expr.c
+$ gcc 'c_flags'/define=('C_DEFS') cond.c
+$ gcc 'c_flags'/define=('C_DEFS') app.c
+$ gcc 'c_flags'/define=('C_DEFS') frags.c
+$ gcc 'c_flags'/define=('C_DEFS') input-file.c
+$ gcc 'c_flags'/define=('C_DEFS') input-scrub.c
+$ gcc 'c_flags'/define=('C_DEFS') output-file.c
+$ gcc 'c_flags'/define=('C_DEFS') read.c
+$ gcc 'c_flags'/define=('C_DEFS') subsegs.c
+$ gcc 'c_flags'/define=('C_DEFS') symbols.c
+$ gcc 'c_flags'/define=('C_DEFS') write.c
+$ gcc 'c_flags'/define=('C_DEFS') version.c
+$ gcc 'c_flags'/define=('C_DEFS') flonum-const.c
+$ gcc 'c_flags'/define=('C_DEFS') flonum-copy.c
+$ gcc 'c_flags'/define=('C_DEFS') flonum-mult.c
+$ gcc 'c_flags'/define=('C_DEFS') strstr.c
+$ gcc 'c_flags'/define=('C_DEFS') bignum-copy.c
+$ gcc 'c_flags'/define=('C_DEFS') listing.c
+$ gcc 'c_flags'/define=('C_DEFS') atof-targ.c
+$ gcc 'c_flags'/define=("error=as_fatal",'C_DEFS') targ-cpu.c
+$ gcc 'c_flags'/define=("error=as_fatal",'C_DEFS') obj-format.c
+$ Link:
+$ link/nomap/exec=gcc-as version.opt/opt+sys$input:/opt
+!
+! Linker options file for GNU assembler
+!
+as,xrealloc,xmalloc,hash,hex-value,atof-generic,messages,expr,app,cond,-
+frags,input-file,input-scrub,output-file,read,subsegs,symbols,write,-
+version,flonum-const,flonum-copy,flonum-mult,strstr,bignum-copy,listing,-
+obstack,targ-cpu,atof-targ,obj-format,-
+gnu_cc:[000000]gcclib/lib,sys$share:vaxcrtl/lib
diff --git a/gnu/usr.bin/as/makefile.dos b/gnu/usr.bin/as/makefile.dos
new file mode 100644
index 0000000..89c74c7
--- /dev/null
+++ b/gnu/usr.bin/as/makefile.dos
@@ -0,0 +1,593 @@
+# Makefile for GNU Assembler
+# Copyright (C) 1987, 1988, 1990, 1991 Free Software Foundation, Inc.
+
+#This file is part of GNU GAS.
+
+#GNU GAS is free software; you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation; either version 1, or (at your option)
+#any later version.
+
+#GNU GAS is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+
+#You should have received a copy of the GNU General Public License
+#along with GNU GAS; see the file COPYING. If not, write to
+#the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# $Id: makefile.dos,v 1.1 1993/10/02 20:57:43 pk Exp $
+
+# The targets for external use include:
+# all, doc, proto, install, uninstall, includes, TAGS,
+# clean, cleanconfig, realclean, stage1, stage2, stage3, stage4.
+
+# Variables that exist for you to override.
+# See below for how to change them for certain systems.
+
+LIBDEPS=
+CROSS=
+HDEFINES=
+CPPFLAGS=
+
+ALLOCA =
+CFLAGS = -g -D__MSDOS__ -D__GO32__ -I../include
+INTERNAL_CFLAGS = $(CROSS)
+OLDCC = cc
+BISON = bison
+BISONFLAGS = -v
+AR = ar
+OLDAR_FLAGS = qc
+AR_FLAGS = rc
+SHELL = /bin/sh
+# on sysV, define this as cp.
+INSTALL = install -c
+# These permit overriding just for certain files.
+INSTALL_PROGRAM = $(INSTALL)
+INSTALL_FILE = $(INSTALL)
+
+# Define this as & to perform parallel make on a Sequent.
+# Note that this has some bugs, and it seems currently necessary
+# to compile all the gen* files first by hand to avoid erroneous results.
+P =
+
+# How to invoke ranlib.
+RANLIB = ranlib
+# Test to use to see whether ranlib exists on the system.
+RANLIB_TEST = [ -f /usr/bin/ranlib -o -f /bin/ranlib ]
+
+# CFLAGS for use with OLDCC, for compiling gnulib.
+# NOTE: -O does not work on some Unix systems!
+CCLIBFLAGS = -O
+
+# Version of ar to use when compiling gnulib.
+OLDAR = ar
+
+version=`$(unsubdir)/../gcc$(subdir)/gcc -dumpversion`
+
+# Directory where sources are, from where we are.
+srcdir = .
+# Common prefix for installation directories.
+# NOTE: This directory must exist when you start installation.
+ddestdir = /usr/local
+# Directory in which to put the executable for the command `gcc'
+bindir = $(ddestdir)/bin
+# Directory in which to put the directories used by the compiler.
+libdir = $(ddestdir)/lib
+# Directory in which the compiler finds executables, libraries, etc.
+libsubdir = $(libdir)/gcc/$(target_alias)/$(version)
+# Number to put in man-page filename.
+manext = 1
+# Directory in which to put man pages.
+mandir = $(destdir)/H-independent/man/man$(manext)
+
+# Additional system libraries to link with.
+CLIB=
+
+# Specify the rule for actually making gnulib.
+GNULIB = gnulib.portable
+
+# Specify the rule for actually making gnulib2.
+GNULIB2 = gnulib2.portable
+
+# List of extra C and assembler files to add to gnulib.
+# Assembler files should have names ending in `.asm'.
+LIBFUNCS_EXTRA =
+
+# Program to convert libraries.
+LIBCONVERT =
+
+# Control whether header files are installed.
+INSTALL_HEADERS=install-headers
+
+# Change this to empty to prevent installing limits.h
+LIMITS_H = limits.h
+
+# Directory to link to, when using the target `maketest'.
+DIR = ../gcc
+
+# For better debugging under COFF, define SEPARATE_AUX_OUTPUT in config.h
+# and define the following variable as `aux-output2.c' in make-...
+AUX_OUTPUT2 =
+
+# Flags to use when cross-building GCC.
+# Prefix to apply to names of object files when using them
+# to run on the machine we are compiling on.
+HOST_PREFIX=
+# Prefix to apply to names of object files when compiling them
+# to run on the machine we are compiling on.
+# The default for this variable is chosen to keep these rules
+# out of the way of the other rules for compiling the same source files.
+HOST_PREFIX_1=loser-
+HOST_CC=$(CC)
+HOST_CFLAGS=$(ALL_CFLAGS)
+HOST_LDFLAGS=$(LDFLAGS)
+HOST_CPPFLAGS=$(CPPFLAGS)
+
+# Choose the real default target.
+ALL=as.new
+
+# End of variables for you to override.
+
+# Lists of files for various purposes.
+
+REAL_SOURCES = \
+ app.c \
+ as.c \
+ atof-generic.c \
+ bignum-copy.c \
+ cond.c \
+ expr.c \
+ fn-const.c \
+ fn-copy.c \
+ flonum-mult.c \
+ frags.c \
+ hash.c \
+ hex-value.c \
+ input-file.c \
+ input-scrub.c \
+ messages.c \
+ output-file.c \
+ read.c \
+ strstr.c \
+ subsegs.c \
+ symbols.c \
+ version.c \
+ write.c \
+ xmalloc.c \
+ xrealloc.c
+
+# in an expedient order
+LINKED_SOURCES = \
+ targ-cpu.c \
+ obj-format.c \
+ atof-targ.c
+
+SOURCES = $(LINKED_SOURCES) $(REAL_SOURCES)
+
+REAL_HEADERS = \
+ as.h \
+ bignum.h \
+ expr.h \
+ flonum.h \
+ frags.h \
+ hash.h \
+ input-file.h \
+ tc.h \
+ obj.h \
+ read.h \
+ struc-symbol.h \
+ subsegs.h \
+ symbols.h \
+ syscalls.h \
+ write.h
+
+LINKED_HEADERS = \
+ a.out.gnu.h \
+ a.out.h \
+ host.h \
+ targ-env.h \
+ targ-cpu.h \
+ obj-format.h \
+ atof-targ.h
+
+HEADERS = $(LINKED_HEADERS) $(REAL_HEADERS)
+
+OBJS = \
+ targ-cpu.o \
+ obj-format.o \
+ atof-targ.o \
+ app.o \
+ as.o \
+ atof-generic.o \
+ bignum-copy.o \
+ cond.o \
+ expr.o \
+ fn-const.o \
+ fn-copy.o \
+ flonum-mult.o \
+ frags.o \
+ hash.o \
+ hex-value.o \
+ input-file.o \
+ input-scrub.o \
+ messages.o \
+ output-file.o \
+ read.o \
+ strstr.o \
+ subsegs.o \
+ symbols.o \
+ version.o \
+ write.o \
+ xmalloc.o \
+ xrealloc.o
+
+#### host, target, and site specific Makefile frags come in here.
+TARG_CPU_DEPENDENTS=../include/h8300-opcode.h
+LOCAL_LOADLIBES=../bfd/libbfd.a
+TDEFINES=-DBFD -DBFD_HEADERS -DMANY_SEGMENTS
+
+
+# Definition of `all' is here so that new rules inserted by sed
+# do not specify the default target.
+# The real definition is under `all.internal'.
+
+all: $(ALL)
+all-info:
+install-info:
+
+fake-as: force
+ - rm -f ./as.new
+ cp /bin/as ./fake-as
+
+# Now figure out from those variables how to compile and link.
+
+# This is the variable actually used when we compile.
+ALL_CFLAGS = $(INTERNAL_CFLAGS) $(CFLAGS) $(HDEFINES) $(TDEFINES)
+
+# Even if ALLOCA is set, don't use it if compiling with GCC.
+USE_ALLOCA= `if [ x"${CC}" = x"${OLDCC}" ] ; then echo ${ALLOCA}; else true; fi`
+USE_HOST_ALLOCA= `if [ x"${CC}" = x"${OLDCC}" ] ; then echo ${HOST_PREFIX}${ALLOCA}; else true; fi`
+
+# Likewise, for use in the tools that must run on this machine
+# even if we are cross-building GCC.
+# We don't use USE_ALLOCA because backquote expansion doesn't work in deps.
+HOST_LIBDEPS= $(HOST_PREFIX)$(OBSTACK) $(HOST_PREFIX)$(ALLOCA) $(HOST_PREFIX)$(MALLOC)
+
+# How to link with both our special library facilities
+# and the system's installed libraries.
+
+LIBS = $(LOCAL_LOADLIBES) $(CLIB) $(unsubdir)/../libiberty$(subdir)/libiberty.a
+
+# Likewise, for use in the tools that must run on this machine
+# even if we are cross-building GCC.
+HOST_LIBS = $(HOST_PREFIX)$(OBSTACK) $(USE_HOST_ALLOCA) $(HOST_PREFIX)$(MALLOC) $(CLIB)
+
+# Specify the directories to be searched for header files.
+# Both . and srcdir are used, in that order,
+# so that tm.h and config.h will be found in the compilation
+# subdirectory rather than in the source directory.
+INCLUDES = -I. -I$(srcdir) -Iconfig
+SUBDIR_INCLUDES = -I.. -I../$(srcdir) -I../config
+
+# Always use -Iconfig when compiling.
+.c.o:
+ $(CC) -c $(ALL_CFLAGS) $(CPPFLAGS) $(INCLUDES) $<
+
+# This tells GNU make version 3 not to export all the variables
+# defined in this file into the environment.
+.NOEXPORT:
+
+# Files to be copied away after each stage in building.
+STAGE_GCC=gcc
+STAGESTUFF = *.o as.new
+
+# The files that "belong" in CONFIG_H are deliberately omitted
+# because having them there would not be useful in actual practice.
+# All they would do is cause complete recompilation every time
+# one of the machine description files is edited.
+# That may or may not be what one wants to do.
+# If it is, rm *.o is an easy way to do it.
+# CONFIG_H = config.h tm.h
+CONFIG_H =
+
+as.new: $(OBJS) $(LIBDEPS)
+ -mv -f as.new as.old
+ >as.rf $(ALL_CFLAGS) $(LDFLAGS) -o as.new $(OBJS) $(LIBS) $(LOADLIBES)
+ $(CC) @as.rf
+
+objdump:
+
+all.internal: native
+# This is what is made with the host's compiler if making a cross assembler.
+native: config.status as
+
+config.status:
+ @echo You must configure gas. Look at the INSTALL file for details.
+ @false
+
+compilations: ${OBJS}
+
+# Compiling object files from source files.
+
+app.o : app.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h
+as.o : as.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h
+atof-generic.o : atof-generic.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h
+bignum-copy.o : bignum-copy.c as.h host.h \
+ targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h
+cond.o : cond.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \
+
+debug.o : debug.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \
+ subsegs.h
+expr.o : expr.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \
+
+fn-const.o : fn-const.c flonum.h bignum.h
+fn-copy.o : fn-copy.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h
+flonum-mult.o : flonum-mult.c flonum.h bignum.h
+frags.o : frags.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \
+ subsegs.h
+hash.o : hash.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h
+hex-value.o : hex-value.c
+input-file.o : input-file.c as.h host.h \
+ targ-env.h obj-format.h targ-cpu.h \
+ struc-symbol.h write.h flonum.h bignum.h expr.h \
+ frags.h hash.h read.h symbols.h tc.h obj.h input-file.h
+input-scrub.o : input-scrub.c \
+ as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \
+ input-file.h
+messages.o : messages.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h
+obstack.o : obstack.c
+output-file.o : output-file.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \
+ output-file.h
+read.o : read.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \
+
+strstr.o : strstr.c
+subsegs.o : subsegs.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \
+ subsegs.h
+symbols.o : symbols.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \
+ subsegs.h
+version.o : version.c
+write.o : write.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \
+ subsegs.h output-file.h
+xmalloc.o : xmalloc.c
+xrealloc.o : xrealloc.c
+atof-targ.o : atof-targ.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h \
+ symbols.h tc.h obj.h
+obj-format.o : obj-format.c as.h host.h targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h \
+ symbols.h tc.h obj.h
+targ-cpu.o : targ-cpu.c targ-env.h obj-format.h \
+ targ-cpu.h struc-symbol.h \
+ write.h flonum.h bignum.h expr.h frags.h hash.h read.h \
+ symbols.h tc.h obj.h $(TARG_CPU_DEPENDENTS)
+
+
+# Compile the libraries to be used by gen*.
+# If we are not cross-building, gen* use the same .o's that cc1 will use,
+# and HOST_PREFIX_1 is `foobar', just to ensure these rules don't conflict
+# with the rules for rtl.o, alloca.o, etc.
+$(HOST_PREFIX_1)alloca.o: alloca.c
+ rm -f $(HOST_PREFIX)alloca.c
+ cp alloca.c $(HOST_PREFIX)alloca.c
+ $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(HOST_PREFIX)alloca.c
+
+$(HOST_PREFIX_1)obstack.o: obstack.c
+ rm -f $(HOST_PREFIX)obstack.c
+ cp obstack.c $(HOST_PREFIX)obstack.c
+ $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(HOST_PREFIX)obstack.c
+
+$(HOST_PREFIX_1)malloc.o: malloc.c
+ rm -f $(HOST_PREFIX)malloc.c
+ cp malloc.c $(HOST_PREFIX)malloc.c
+ $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(HOST_PREFIX)malloc.c
+
+# Remake the info files.
+
+doc: as.info
+
+as.info: doc/as.texinfo
+ (cd doc; make as.info; mv as.info $srcdir)
+
+
+# Deletion of files made during compilation.
+# There are three levels of this: `clean', `cleanconfig' and `realclean'.
+# `clean' deletes what you want to delete ordinarily to save space.
+# This is most, but not all, of the files made by compilation.
+# `cleanconfig' also deletes everything depending
+# on the choice of config files.
+# `realclean' also deletes everything that could be regenerated automatically.
+
+clean:
+ -rm -f $(STAGESTUFF)
+# Delete the temporary source copies for cross compilation.
+ -rm -f $(HOST_PREFIX_1)alloca.c $(HOST_PREFIX_1)malloc.c
+ -rm -f $(HOST_PREFIX_1)obstack.c
+# Delete the stamp files except stamp-gnulib2.
+ -rm -f core
+
+# Like clean but also delete the links made to configure gas.
+cleanconfig: clean
+ -rm -f config.status Makefile host.h targ-env.h
+ -rm -f targ-cpu.h targ-cpu.c
+ -rm -f obj-format.h obj-format.c
+ -rm -f atof-targ.c
+
+# Get rid of every file that's generated from some other file (except INSTALL).
+realclean: cleanconfig
+ -rm -f gas.aux gas.cps gas.fns gas.info gas.kys gas.pgs gas.tps gas.vrs
+ -rm -f TAGS
+ -rm -f gas.info* gas.?? gas.??s gas.log gas.toc gas.*aux
+ -rm -f *.dvi
+
+# Entry points `install', `includes' and `uninstall'.
+
+# Copy the files into directories where they will be run.
+install: $(ALL)
+ $(INSTALL_PROGRAM) $(ALL) $(libsubdir)/as
+# cp $(ALL) $(bindir)/as.new
+# mv -f $(bindir)/as.new $(bindir)/as
+
+# Create the installation directory.
+install-dir:
+ -mkdir $(libdir)
+ -mkdir $(libdir)/gcc
+ -mkdir $(libdir)/gcc/$(target)
+ -mkdir $(libdir)/gcc/$(target)/$(version)
+
+# Install the compiler executables built during cross compilation.
+install-cross: native install-dir
+ -if [ -f cc1 ] ; then $(INSTALL_PROGRAM) cc1 $(libsubdir)/cc1; else true; fi
+ -if [ -f cc1plus ] ; then $(INSTALL_PROGRAM) cc1plus $(libsubdir)/cc1plus; else true; fi
+ $(INSTALL_PROGRAM) cpp $(libsubdir)/cpp
+ ./gcc -dumpspecs > $(libsubdir)/specs
+ $(INSTALL_PROGRAM) gcc $(bindir)/gcc
+
+# Install the man pages.
+install-man: install-dir gcc.1 protoize.1 unprotoize.1
+ $(INSTALL_FILE) gcc.1 $(mandir)/gcc.$(manext)
+ chmod a-x $(mandir)/gcc.$(manext)
+ $(INSTALL_FILE) protoize.1 $(mandir)/protoize.$(manext)
+ chmod a-x $(mandir)/protoize.$(manext)
+ $(INSTALL_FILE) unprotoize.1 $(mandir)/unprotoize.$(manext)
+ chmod a-x $(mandir)/unprotoize.$(manext)
+
+# Cancel installation by deleting the installed files.
+uninstall:
+ -rm -rf $(libsubdir)
+ -rm -rf $(bindir)/as
+ -rm -rf $(mandir)/gas.$(manext)
+
+
+# These exist for maintenance purposes.
+
+tags TAGS: force
+ etags $(REAL_SOURCES) $(REAL_HEADERS) README Makefile config/*.[hc]
+
+bootstrap: $(ALL) force
+ $(MAKE) stage1
+ $(MAKE) CC="$(CC)" CFLAGS="-O -Bstage1/ $(CFLAGS)" libdir=$(libdir) ALLOCA= $(ALL)
+ $(MAKE) stage2
+ $(MAKE) CC="$(CC)" CFLAGS="-O -Bstage2/ $(CFLAGS)" libdir=$(libdir) ALLOCA= $(ALL)
+ $(MAKE) comparison against=stage2
+
+bootstrap2: force
+ $(MAKE) CC="$(CC)" CFLAGS="-O -Bstage1/ $(CFLAGS)" libdir=$(libdir) ALLOCA= $(ALL)
+ $(MAKE) stage2
+ $(MAKE) CC="$(CC)" CFLAGS="-O -Bstage2/ $(CFLAGS)" libdir=$(libdir) ALLOCA= $(ALL)
+ $(MAKE) comparison against=stage2
+
+bootstrap3: force
+ $(MAKE) CC="$(CC)" CFLAGS="-O -Bstage2/ $(CFLAGS)" libdir=$(libdir) ALLOCA= $(ALL)
+ $(MAKE) comparison against=stage2
+
+# Copy the object files from a particular stage into a subdirectory.
+stage1: force
+ -mkdir stage1
+ -mv $(STAGESTUFF) stage1
+ if [ -f stage1/as.new -a ! -f stage1/as ] ; then (cd stage1 ; ln -s as.new as) ; fi
+
+stage2: force
+ -mkdir stage2
+ -mv $(STAGESTUFF) stage2
+ if [ -f stage2/as.new -a ! -f stage2/as ] ; then (cd stage2 ; ln -s as.new as) ; fi
+
+stage3: force
+ -mkdir stage3
+ -mv $(STAGESTUFF) stage3
+ if [ -f stage3/as.new -a ! -f stage3/as ] ; then (cd stage3 ; ln -s as.new as) ; fi
+
+against=stage2
+
+comparison: force
+ for i in $(STAGESTUFF) ; do cmp $$i $(against)/$$i ; done
+
+de-stage1: force
+ - (cd stage1 ; rm as ; mv -f * ..)
+ - rmdir stage1
+
+de-stage2: force
+ - (cd stage2 ; rm as ; mv -f * ..)
+ - rmdir stage2
+
+de-stage3: force
+ - (cd stage3 ; rm as ; mv -f * ..)
+ - rmdir stage3
+
+# Copy just the executable files from a particular stage into a subdirectory,
+# and delete the object files. Use this if you're just verifying a version
+# that is pretty sure to work, and you are short of disk space.
+risky-stage1: force
+ -mkdir stage1
+ -mv cc1 cpp cccp gcc stage1
+ -rm -f stage1/gnulib
+ -cp gnulib stage1 && $(RANLIB) stage1/gnulib
+ -make clean
+
+risky-stage2: force
+ -mkdir stage2
+ -mv cc1 cpp cccp gcc stage2
+ -rm -f stage2/gnulib
+ -cp gnulib stage2 && $(RANLIB) stage2/gnulib
+ -make clean
+
+risky-stage3: force
+ -mkdir stage3
+ -mv cc1 cpp cccp gcc stage3
+ -rm -f stage3/gnulib
+ -cp gnulib stage3 && $(RANLIB) stage3/gnulib
+ -make clean
+
+risky-stage4: force
+ -mkdir stage4
+ -mv cc1 cpp cccp gcc stage4
+ -rm -f stage4/gnulib
+ -cp gnulib stage4 && $(RANLIB) stage4/gnulib
+ -make clean
+
+#In GNU Make, ignore whether `stage*' exists.
+.PHONY: stage1 stage2 stage3 stage4 clean realclean TAGS bootstrap
+.PHONY: risky-stage1 risky-stage2 risky-stage3 risky-stage4
+
+force:
+
+Makefile: Makefile.in $(host_makefile_frag) $(target_makefile_frag)
+ $(SHELL) ./config.status
+
diff --git a/gnu/usr.bin/as/md.h b/gnu/usr.bin/as/md.h
new file mode 100644
index 0000000..681d027
--- /dev/null
+++ b/gnu/usr.bin/as/md.h
@@ -0,0 +1,57 @@
+/* md.h -machine dependent- */
+
+/* Copyright (C) 1987 Free Software Foundation, Inc.
+
+This file is part of Gas, the GNU Assembler.
+
+The GNU assembler is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY. No author or distributor
+accepts responsibility to anyone for the consequences of using it
+or for whether it serves any particular purpose or works at all,
+unless he says so in writing. Refer to the GNU Assembler General
+Public License for full details.
+
+Everyone is granted permission to copy, modify and redistribute
+the GNU Assembler, but only under the conditions described in the
+GNU Assembler General Public License. A copy of this license is
+supposed to have been given to you along with the GNU Assembler
+so you can know your rights and responsibilities. It should be
+in a file named COPYING. Among other things, the copyright
+notice and this notice must be preserved on all copies. */
+
+/* In theory (mine, at least!) the machine dependent part of the assembler
+ should only have to include one file. This one. -- JF */
+
+/* JF added this here */
+typedef struct {
+ char * poc_name; /* assembler mnemonic, lower case, no '.' */
+ void (*poc_handler)(); /* Do the work */
+ int poc_val; /* Value to pass to handler */
+}
+pseudo_typeS;
+extern const pseudo_typeS md_pseudo_table[];
+
+/* JF moved this here from as.h under the theory that nobody except MACHINE.c
+ and write.c care about it anyway. */
+
+typedef struct
+{
+ long rlx_forward; /* Forward reach. Signed number. > 0. */
+ long rlx_backward; /* Backward reach. Signed number. < 0. */
+ unsigned char rlx_length; /* Bytes length of this address. */
+ relax_substateT rlx_more; /* Next longer relax-state. */
+ /* 0 means there is no 'next' relax-state. */
+}
+relax_typeS;
+
+extern const relax_typeS md_relax_table[]; /* Define it in MACHINE.c */
+
+char * md_atof();
+void md_assemble();
+void md_begin();
+void md_convert_frag();
+void md_end();
+int md_estimate_size_before_relax();
+void md_number_to_chars();
+
+/* end: md.h */
diff --git a/gnu/usr.bin/as/messages.c b/gnu/usr.bin/as/messages.c
new file mode 100644
index 0000000..b1ba3f7
--- /dev/null
+++ b/gnu/usr.bin/as/messages.c
@@ -0,0 +1,417 @@
+/* messages.c - error reporter -
+ Copyright (C) 1987, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef lint
+static char rcsid[] = "$Id: messages.c,v 1.3 1993/10/02 20:57:45 pk Exp $";
+#endif
+
+#include <stdio.h> /* define stderr */
+#include <errno.h>
+
+#include "as.h"
+
+#ifndef NO_STDARG
+#include <stdarg.h>
+#else
+#ifndef NO_VARARGS
+#include <varargs.h>
+#endif /* NO_VARARGS */
+#endif /* NO_STDARG */
+
+/*
+ * Despite the rest of the comments in this file, (FIXME-SOON),
+ * here is the current scheme for error messages etc:
+ *
+ * as_fatal() is used when gas is quite confused and
+ * continuing the assembly is pointless. In this case we
+ * exit immediately with error status.
+ *
+ * as_bad() is used to mark errors that result in what we
+ * presume to be a useless object file. Say, we ignored
+ * something that might have been vital. If we see any of
+ * these, assembly will continue to the end of the source,
+ * no object file will be produced, and we will terminate
+ * with error status. The new option, -Z, tells us to
+ * produce an object file anyway but we still exit with
+ * error status. The assumption here is that you don't want
+ * this object file but we could be wrong.
+ *
+ * as_warn() is used when we have an error from which we
+ * have a plausible error recovery. eg, masking the top
+ * bits of a constant that is longer than will fit in the
+ * destination. In this case we will continue to assemble
+ * the source, although we may have made a bad assumption,
+ * and we will produce an object file and return normal exit
+ * status (ie, no error). The new option -X tells us to
+ * treat all as_warn() errors as as_bad() errors. That is,
+ * no object file will be produced and we will exit with
+ * error status. The idea here is that we don't kill an
+ * entire make because of an error that we knew how to
+ * correct. On the other hand, sometimes you might want to
+ * stop the make at these points.
+ *
+ * as_tsktsk() is used when we see a minor error for which
+ * our error recovery action is almost certainly correct.
+ * In this case, we print a message and then assembly
+ * continues as though no error occurred.
+ */
+
+/*
+ ERRORS
+
+ JF: this is now bogus. We now print more standard error messages
+ that try to look like everyone else's.
+
+ We print the error message 1st, beginning in column 1.
+ All ancillary info starts in column 2 on lines after the
+ key error text.
+ We try to print a location in logical and physical file
+ just after the main error text.
+ Caller then prints any appendices after that, begining all
+ lines with at least 1 space.
+
+ Optionally, we may die.
+ There is no need for a trailing '\n' in your error text format
+ because we supply one.
+
+ as_warn(fmt,args) Like fprintf(stderr,fmt,args) but also call errwhere().
+
+ as_fatal(fmt,args) Like as_warn() but exit with a fatal status.
+
+ */
+
+static int warning_count = 0; /* Count of number of warnings issued */
+
+int had_warnings() {
+ return(warning_count);
+} /* had_err() */
+
+/* Nonzero if we've hit a 'bad error', and should not write an obj file,
+ and exit with a nonzero error code */
+
+static int error_count = 0;
+
+int had_errors() {
+ return(error_count);
+} /* had_errors() */
+
+
+/*
+ * a s _ p e r r o r
+ *
+ * Like perror(3), but with more info.
+ */
+void as_perror(gripe, filename)
+char *gripe; /* Unpunctuated error theme. */
+char *filename;
+{
+#ifndef HAVE_STRERROR
+ extern char *strerror();
+#endif /* HAVE_STRERROR */
+
+ as_where();
+ fprintf(stderr, gripe, filename);
+ fprintf(stderr, "%s.\n", strerror(errno));
+ errno = 0; /* After reporting, clear it. */
+} /* as_perror() */
+
+/*
+ * a s _ t s k t s k ()
+ *
+ * Send to stderr a string (with bell) (JF: Bell is obnoxious!) as a warning, and locate warning
+ * in input file(s).
+ * Please only use this for when we have some recovery action.
+ * Please explain in string (which may have '\n's) what recovery was done.
+ */
+
+#ifndef NO_STDARG
+void as_tsktsk(Format)
+const char *Format;
+{
+ va_list args;
+
+ as_where();
+ va_start(args, Format);
+ vfprintf(stderr, Format, args);
+ va_end(args);
+ (void) putc('\n', stderr);
+} /* as_tsktsk() */
+#else
+#ifndef NO_VARARGS
+void as_tsktsk(Format,va_alist)
+char *Format;
+va_dcl
+{
+ va_list args;
+
+ as_where();
+ va_start(args);
+ vfprintf(stderr, Format, args);
+ va_end(args);
+ (void) putc('\n', stderr);
+} /* as_tsktsk() */
+#else
+/*VARARGS1 */
+as_tsktsk(Format,args)
+char *Format;
+{
+ as_where();
+ _doprnt (Format, &args, stderr);
+ (void)putc ('\n', stderr);
+ /* as_where(); */
+} /* as_tsktsk */
+#endif /* not NO_VARARGS */
+#endif /* not NO_STDARG */
+
+#ifdef DONTDEF
+void as_tsktsk(Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an)
+char *format;
+{
+ as_where();
+ fprintf(stderr,Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an);
+ (void)putc('\n',stderr);
+} /* as_tsktsk() */
+#endif
+/*
+ * a s _ w a r n ()
+ *
+ * Send to stderr a string (with bell) (JF: Bell is obnoxious!) as a warning, and locate warning
+ * in input file(s).
+ * Please only use this for when we have some recovery action.
+ * Please explain in string (which may have '\n's) what recovery was done.
+ */
+
+#ifndef NO_STDARG
+void as_warn(Format)
+const char *Format;
+{
+ va_list args;
+ char buffer[200];
+
+ if (!flagseen['W']) {
+ ++warning_count;
+ as_where();
+ va_start(args, Format);
+ fprintf(stderr,"Warning: ");
+ vsprintf(buffer, Format, args);
+ fprintf(stderr, buffer);
+#ifndef NO_LISTING
+ listing_warning(buffer);
+#endif
+ va_end(args);
+ (void) putc('\n', stderr);
+ }
+} /* as_warn() */
+#else
+#ifndef NO_VARARGS
+void as_warn(Format,va_alist)
+char *Format;
+va_dcl
+{
+ va_list args;
+ char buffer[200];
+
+ if (!flagseen['W']) {
+ ++warning_count;
+ as_where();
+ va_start(args);
+ fprintf(stderr,"Warning: ");
+ vsprintf(buffer, Format, args);
+ fprintf(stderr,buffer);
+#ifndef NO_LISTING
+ listing_warning(buffer);
+#endif
+ va_end(args);
+ (void) putc('\n', stderr);
+ }
+} /* as_warn() */
+#else
+/*VARARGS1 */
+as_warn(Format,args)
+char *Format;
+{
+ /* -W supresses warning messages. */
+ if (! flagseen['W']) {
+ ++warning_count;
+ as_where();
+ _doprnt (Format, &args, stderr);
+ (void)putc ('\n', stderr);
+ /* as_where(); */
+ }
+} /* as_warn() */
+#endif /* not NO_VARARGS */
+#endif /* not NO_STDARG */
+
+#ifdef DONTDEF
+void as_warn(Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an)
+char *format;
+{
+ if (!flagseen['W']) {
+ ++warning_count;
+ as_where();
+ fprintf(stderr,Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an);
+ (void)putc('\n',stderr);
+ }
+} /* as_warn() */
+#endif
+/*
+ * a s _ b a d ()
+ *
+ * Send to stderr a string (with bell) (JF: Bell is obnoxious!) as a warning,
+ * and locate warning in input file(s).
+ * Please us when there is no recovery, but we want to continue processing
+ * but not produce an object file.
+ * Please explain in string (which may have '\n's) what recovery was done.
+ */
+
+#ifndef NO_STDARG
+void as_bad(Format)
+const char *Format;
+{
+ va_list args;
+ char buffer[200];
+
+ ++error_count;
+ as_where();
+ va_start(args, Format);
+ fprintf(stderr,"Error: ");
+
+ vsprintf(buffer, Format, args);
+ fprintf(stderr,buffer);
+#ifndef NO_LISTING
+ listing_error(buffer);
+#endif
+ va_end(args);
+ (void) putc('\n', stderr);
+} /* as_bad() */
+#else
+#ifndef NO_VARARGS
+void as_bad(Format,va_alist)
+char *Format;
+va_dcl
+{
+ va_list args;
+ char buffer[200];
+
+ ++error_count;
+ as_where();
+ va_start(args);
+ vsprintf(buffer, Format, args);
+ fprintf(stderr,buffer);
+#ifndef NO_LISTING
+ listing_error(buffer);
+#endif
+
+ va_end(args);
+ (void) putc('\n', stderr);
+} /* as_bad() */
+#else
+/*VARARGS1 */
+as_bad(Format,args)
+char *Format;
+{
+ ++error_count;
+
+ as_where();
+ fprintf(stderr,"Error: ");
+ _doprnt (Format, &args, stderr);
+ (void)putc ('\n', stderr);
+ /* as_where(); */
+} /* as_bad() */
+#endif /* not NO_VARARGS */
+#endif /* not NO_STDARG */
+
+#ifdef DONTDEF
+void as_bad(Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an)
+char *format;
+{
+ ++error_count;
+ as_where();
+ fprintf(stderr,Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an);
+ (void)putc('\n',stderr);
+} /* as_bad() */
+#endif
+
+/*
+ * a s _ f a t a l ()
+ *
+ * Send to stderr a string (with bell) (JF: Bell is obnoxious!) as a fatal
+ * message, and locate stdsource in input file(s).
+ * Please only use this for when we DON'T have some recovery action.
+ * It exit()s with a warning status.
+ */
+
+#ifndef NO_STDARG
+void as_fatal(Format)
+const char *Format;
+{
+ va_list args;
+
+ as_where();
+ va_start(args, Format);
+ fprintf (stderr, "FATAL:");
+ vfprintf(stderr, Format, args);
+ (void) putc('\n', stderr);
+ va_end(args);
+ exit(33);
+} /* as_fatal() */
+#else
+#ifndef NO_VARARGS
+void as_fatal(Format,va_alist)
+char *Format;
+va_dcl
+{
+ va_list args;
+
+ as_where();
+ va_start(args);
+ fprintf (stderr, "FATAL:");
+ vfprintf(stderr, Format, args);
+ (void) putc('\n', stderr);
+ va_end(args);
+ exit(33);
+} /* as_fatal() */
+#else
+/*VARARGS1 */
+as_fatal(Format, args)
+char *Format;
+{
+ as_where();
+ fprintf(stderr,"FATAL:");
+ _doprnt (Format, &args, stderr);
+ (void)putc ('\n', stderr);
+ /* as_where(); */
+ exit(33); /* What is a good exit status? */
+} /* as_fatal() */
+#endif /* not NO_VARARGS */
+#endif /* not NO_STDARG */
+
+#ifdef DONTDEF
+void as_fatal(Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an)
+char *Format;
+{
+ as_where();
+ fprintf (stderr, "FATAL:");
+ fprintf(stderr, Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an);
+ (void) putc('\n', stderr);
+ exit(33);
+} /* as_fatal() */
+#endif
+
+/* end of messages.c */
diff --git a/gnu/usr.bin/as/obj.h b/gnu/usr.bin/as/obj.h
new file mode 100644
index 0000000..ac5b158
--- /dev/null
+++ b/gnu/usr.bin/as/obj.h
@@ -0,0 +1,77 @@
+/* obj.h - defines the object dependent hooks for all object
+ format backends.
+
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
+ * $Id: obj.h,v 1.1 1993/10/02 20:57:45 pk Exp $
+ */
+
+
+#if __STDC__ == 1
+
+char *obj_default_output_file_name(void);
+void obj_crawl_symbol_chain(object_headers *headers);
+void obj_emit_relocations(char **where, fixS *fixP, relax_addressT segment_address_in_file);
+void obj_emit_strings(char **where);
+void obj_emit_symbols(char **where, symbolS *symbol_rootP);
+void obj_header_append(char **where, object_headers *headers);
+void obj_read_begin_hook(void);
+
+#ifndef obj_symbol_new_hook
+void obj_symbol_new_hook(symbolS *symbolP);
+#endif /* obj_symbol_new_hook */
+
+void obj_symbol_to_chars(char **where, symbolS *symbolP);
+
+#ifndef obj_pre_write_hook
+void obj_pre_write_hook(object_headers *headers);
+#endif /* obj_pre_write_hook */
+
+#else /* not __STDC__ */
+
+char *obj_default_output_file_name();
+void obj_crawl_symbol_chain();
+void obj_emit_relocations();
+void obj_emit_strings();
+void obj_emit_symbols();
+void obj_header_append();
+void obj_read_begin_hook();
+
+#ifndef obj_symbol_new_hook
+void obj_symbol_new_hook();
+#endif /* obj_symbol_new_hook */
+
+void obj_symbol_to_chars();
+
+#ifndef obj_pre_write_hook
+void obj_pre_write_hook();
+#endif /* obj_pre_write_hook */
+
+#endif /* not __STDC__ */
+
+extern const pseudo_typeS obj_pseudo_table[];
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of obj.h */
diff --git a/gnu/usr.bin/as/objrecdef.h b/gnu/usr.bin/as/objrecdef.h
new file mode 100644
index 0000000..fca8af4
--- /dev/null
+++ b/gnu/usr.bin/as/objrecdef.h
@@ -0,0 +1,255 @@
+/*
+ *
+ * $OBJRECDEF
+ * Generated automatically by "vms_struct Version 1.00"
+ * Created from VMS definition file "objrecdef.mar"
+ * Mon Oct 14 14:01:29 1985
+ *
+ */
+struct OBJREC {
+ unsigned char obj$b_rectyp;
+ unsigned char obj$b_subtyp;
+ unsigned char obj$b_mhd_strlv;
+ unsigned char obj$b_mhd_recsz[2];
+ unsigned char obj$t_mhd_name[1];
+ };
+
+#define OBJ$C_HDR 0
+#define OBJ$C_HDR_MHD 0
+#define OBJ$C_HDR_LNM 1
+#define OBJ$C_HDR_SRC 2
+#define OBJ$C_HDR_TTL 3
+#define OBJ$C_HDR_CPR 4
+#define OBJ$C_HDR_MTC 5
+#define OBJ$C_HDR_GTX 6
+#define OBJ$C_GSD 1
+#define OBJ$C_GSD_PSC 0
+#define OBJ$C_GSD_SYM 1
+#define OBJ$C_GSD_EPM 2
+#define OBJ$C_GSD_PRO 3
+#define OBJ$C_GSD_SYMW 4
+#define OBJ$C_GSD_EPMW 5
+#define OBJ$C_GSD_PROW 6
+#define OBJ$C_GSD_IDC 7
+#define OBJ$C_GSD_ENV 8
+#define OBJ$C_GSD_LSY 9
+#define OBJ$C_GSD_LEPM 10
+#define OBJ$C_GSD_LPRO 11
+#define OBJ$C_GSD_SPSC 12
+#define OBJ$C_TIR 2
+#define OBJ$C_EOM 3
+#define OBJ$C_DBG 4
+#define OBJ$C_TBT 5
+#define OBJ$C_LNK 6
+#define OBJ$C_EOMW 7
+#define OBJ$C_MAXRECTYP 7
+#define OBJ$K_SUBTYP 1
+#define OBJ$C_SUBTYP 1
+#define OBJ$C_MAXRECSIZ 2048
+#define OBJ$C_STRLVL 0
+#define OBJ$C_SYMSIZ 31
+#define OBJ$C_STOREPLIM -1
+#define OBJ$C_PSCALILIM 9
+
+#define MHD$C_MHD 0
+#define MHD$C_LNM 1
+#define MHD$C_SRC 2
+#define MHD$C_TTL 3
+#define MHD$C_CPR 4
+#define MHD$C_MTC 5
+#define MHD$C_GTX 6
+#define MHD$C_MAXHDRTYP 6
+
+#define GSD$K_ENTRIES 1
+#define GSD$C_ENTRIES 1
+#define GSD$C_PSC 0
+#define GSD$C_SYM 1
+#define GSD$C_EPM 2
+#define GSD$C_PRO 3
+#define GSD$C_SYMW 4
+#define GSD$C_EPMW 5
+#define GSD$C_PROW 6
+#define GSD$C_IDC 7
+#define GSD$C_ENV 8
+#define GSD$C_LSY 9
+#define GSD$C_LEPM 10
+#define GSD$C_LPRO 11
+#define GSD$C_SPSC 12
+#define GSD$C_SYMV 13
+#define GSD$C_EPMV 14
+#define GSD$C_PROV 15
+#define GSD$C_MAXRECTYP 15
+
+#define GSY$M_WEAK 1
+#define GSY$M_DEF 2
+#define GSY$M_UNI 4
+#define GSY$M_REL 8
+
+#define GPS$M_PIC 1
+#define GPS$M_LIB 2
+#define GPS$M_OVR 4
+#define GPS$M_REL 8
+#define GPS$M_GBL 16
+#define GPS$M_SHR 32
+#define GPS$M_EXE 64
+#define GPS$M_RD 128
+#define GPS$M_WRT 256
+#define GPS$M_VEC 512
+#define GPS$K_NAME 9
+#define GPS$C_NAME 9
+
+#define TIR$C_STA_GBL 0
+#define TIR$C_STA_SB 1
+#define TIR$C_STA_SW 2
+#define TIR$C_STA_LW 3
+#define TIR$C_STA_PB 4
+#define TIR$C_STA_PW 5
+#define TIR$C_STA_PL 6
+#define TIR$C_STA_UB 7
+#define TIR$C_STA_UW 8
+#define TIR$C_STA_BFI 9
+#define TIR$C_STA_WFI 10
+#define TIR$C_STA_LFI 11
+#define TIR$C_STA_EPM 12
+#define TIR$C_STA_CKARG 13
+#define TIR$C_STA_WPB 14
+#define TIR$C_STA_WPW 15
+#define TIR$C_STA_WPL 16
+#define TIR$C_STA_LSY 17
+#define TIR$C_STA_LIT 18
+#define TIR$C_STA_LEPM 19
+#define TIR$C_MAXSTACOD 19
+#define TIR$C_MINSTOCOD 20
+#define TIR$C_STO_SB 20
+#define TIR$C_STO_SW 21
+#define TIR$C_STO_L 22
+#define TIR$C_STO_BD 23
+#define TIR$C_STO_WD 24
+#define TIR$C_STO_LD 25
+#define TIR$C_STO_LI 26
+#define TIR$C_STO_PIDR 27
+#define TIR$C_STO_PICR 28
+#define TIR$C_STO_RSB 29
+#define TIR$C_STO_RSW 30
+#define TIR$C_STO_RL 31
+#define TIR$C_STO_VPS 32
+#define TIR$C_STO_USB 33
+#define TIR$C_STO_USW 34
+#define TIR$C_STO_RUB 35
+#define TIR$C_STO_RUW 36
+#define TIR$C_STO_B 37
+#define TIR$C_STO_W 38
+#define TIR$C_STO_RB 39
+#define TIR$C_STO_RW 40
+#define TIR$C_STO_RIVB 41
+#define TIR$C_STO_PIRR 42
+#define TIR$C_MAXSTOCOD 42
+#define TIR$C_MINOPRCOD 50
+#define TIR$C_OPR_NOP 50
+#define TIR$C_OPR_ADD 51
+#define TIR$C_OPR_SUB 52
+#define TIR$C_OPR_MUL 53
+#define TIR$C_OPR_DIV 54
+#define TIR$C_OPR_AND 55
+#define TIR$C_OPR_IOR 56
+#define TIR$C_OPR_EOR 57
+#define TIR$C_OPR_NEG 58
+#define TIR$C_OPR_COM 59
+#define TIR$C_OPR_INSV 60
+#define TIR$C_OPR_ASH 61
+#define TIR$C_OPR_USH 62
+#define TIR$C_OPR_ROT 63
+#define TIR$C_OPR_SEL 64
+#define TIR$C_OPR_REDEF 65
+#define TIR$C_OPR_DFLIT 66
+#define TIR$C_MAXOPRCOD 66
+#define TIR$C_MINCTLCOD 80
+#define TIR$C_CTL_SETRB 80
+#define TIR$C_CTL_AUGRB 81
+#define TIR$C_CTL_DFLOC 82
+#define TIR$C_CTL_STLOC 83
+#define TIR$C_CTL_STKDL 84
+#define TIR$C_MAXCTLCOD 84
+
+/*
+ * Debugger symbol definitions: These are done by hand, as no
+ * machine-readable version seems
+ * to be available.
+ */
+#define DST$C_C 7 /* Language == "C" */
+#define DST$C_VERSION 153
+#define DST$C_SOURCE 155 /* Source file */
+#define DST$C_PROLOG 162
+#define DST$C_BLKBEG 176 /* Beginning of block */
+#define DST$C_BLKEND 177 /* End of block */
+#define DST$C_ENTRY 181
+#define DST$C_PSECT 184
+#define DST$C_LINE_NUM 185 /* Line Number */
+#define DST$C_LBLORLIT 186
+#define DST$C_LABEL 187
+#define DST$C_MODBEG 188 /* Beginning of module */
+#define DST$C_MODEND 189 /* End of module */
+#define DST$C_RTNBEG 190 /* Beginning of routine */
+#define DST$C_RTNEND 191 /* End of routine */
+#define DST$C_DELTA_PC_W 1 /* Incr PC */
+#define DST$C_INCR_LINUM 2 /* Incr Line # */
+#define DST$C_INCR_LINUM_W 3 /* Incr Line # */
+#define DST$C_SET_LINUM_INCR 4
+#define DST$C_SET_LINUM_INCR_W 5
+#define DST$C_RESET_LINUM_INCR 6
+#define DST$C_BEG_STMT_MODE 7
+#define DST$C_END_STMT_MODE 8
+#define DST$C_SET_LINE_NUM 9 /* Set Line # */
+#define DST$C_SET_PC 10
+#define DST$C_SET_PC_W 11
+#define DST$C_SET_PC_L 12
+#define DST$C_SET_STMTNUM 13
+#define DST$C_TERM 14 /* End of lines */
+#define DST$C_TERM_W 15 /* End of lines */
+#define DST$C_SET_ABS_PC 16 /* Set PC */
+#define DST$C_DELTA_PC_L 17 /* Incr PC */
+#define DST$C_INCR_LINUM_L 18 /* Incr Line # */
+#define DST$C_SET_LINUM_B 19 /* Set Line # */
+#define DST$C_SET_LINUM_L 20 /* Set Line # */
+#define DST$C_TERM_L 21 /* End of lines */
+/* these are used with DST$C_SOURCE */
+#define DST$C_SRC_FORMFEED 16 /* ^L counts */
+#define DST$C_SRC_DECLFILE 1 /* Declare file */
+#define DST$C_SRC_SETFILE 2 /* Set file */
+#define DST$C_SRC_SETREC_L 3 /* Set record */
+#define DST$C_SRC_DEFLINES_W 10 /* # of line */
+/* the following are the codes for the various data types. Anything not on
+ * the list is included under 'advanced_type'
+ */
+#define DBG$C_UCHAR 0x02
+#define DBG$C_USINT 0x03
+#define DBG$C_ULINT 0x04
+#define DBG$C_SCHAR 0x06
+#define DBG$C_SSINT 0x07
+#define DBG$C_SLINT 0x08
+#define DBG$C_REAL4 0x0a
+#define DBG$C_REAL8 0x0b
+#define DBG$C_FUNCTION_ADDR 0x17
+#define DBG$C_ADVANCED_TYPE 0xa3
+/* These are the codes that are used to generate the definitions of struct
+ * union and enum records
+ */
+#define DBG$C_ENUM_ITEM 0xa4
+#define DBG$C_ENUM_START 0xa5
+#define DBG$C_ENUM_END 0xa6
+#define DBG$C_STRUCT_START 0xab
+#define DBG$C_STRUCT_ITEM 0xff
+#define DBG$C_STRUCT_END 0xac
+/* These are the codes that are used in the suffix records to determine the
+ * actual data type
+ */
+#define DBG$C_BASIC 0x01
+#define DBG$C_BASIC_ARRAY 0x02
+#define DBG$C_STRUCT 0x03
+#define DBG$C_POINTER 0x04
+#define DBG$C_VOID 0x05
+#define DBG$C_COMPLEX_ARRAY 0x07
+/* These codes are used in the generation of the symbol definition records
+ */
+#define DBG$C_FUNCTION_PARAMETER 0xc9
+#define DBG$C_LOCAL_SYM 0xd9
diff --git a/gnu/usr.bin/as/obstack.c b/gnu/usr.bin/as/obstack.c
new file mode 100644
index 0000000..d7302ea
--- /dev/null
+++ b/gnu/usr.bin/as/obstack.c
@@ -0,0 +1,374 @@
+/* obstack.c - subroutines used implicitly by object stack macros
+ Copyright (C) 1988 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef lint
+static char rcsid[] = "$Id: obstack.c,v 1.3 1993/10/02 20:57:47 pk Exp $";
+#endif
+
+#include "obstack.h"
+
+#ifdef __STDC__
+#define POINTER void *
+#else
+#define POINTER char *
+#endif
+
+/* Determine default alignment. */
+struct fooalign {char x; double d;};
+#define DEFAULT_ALIGNMENT ((char *)&((struct fooalign *) 0)->d - (char *)0)
+/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT.
+ But in fact it might be less smart and round addresses to as much as
+ DEFAULT_ROUNDING. So we prepare for it to do that. */
+union fooround {long x; double d;};
+#define DEFAULT_ROUNDING (sizeof (union fooround))
+
+/* When we copy a long block of data, this is the unit to do it with.
+ On some machines, copying successive ints does not work;
+ in such a case, redefine COPYING_UNIT to `long' (if that works)
+ or `char' as a last resort. */
+#ifndef COPYING_UNIT
+#define COPYING_UNIT int
+#endif
+
+/* The non-GNU-C macros copy the obstack into this global variable
+ to avoid multiple evaluation. */
+
+struct obstack *_obstack;
+
+/* Initialize an obstack H for use. Specify chunk size SIZE (0 means default).
+ Objects start on multiples of ALIGNMENT (0 means use default).
+ CHUNKFUN is the function to use to allocate chunks,
+ and FREEFUN the function to free them. */
+
+void
+_obstack_begin (h, size, alignment, chunkfun, freefun)
+ struct obstack *h;
+ int size;
+ int alignment;
+ POINTER (*chunkfun) ();
+ void (*freefun) ();
+{
+ register struct _obstack_chunk* chunk; /* points to new chunk */
+
+ if (alignment == 0)
+ alignment = DEFAULT_ALIGNMENT;
+ if (size == 0)
+ /* Default size is what GNU malloc can fit in a 4096-byte block. */
+ {
+ /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
+ Use the values for range checking, because if range checking is off,
+ the extra bytes won't be missed terribly, but if range checking is on
+ and we used a larger request, a whole extra 4096 bytes would be
+ allocated.
+
+ These number are irrelevant to the new GNU malloc. I suspect it is
+ less sensitive to the size of the request. */
+ int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
+ + 4 + DEFAULT_ROUNDING - 1)
+ & ~(DEFAULT_ROUNDING - 1));
+ size = 4096 - extra;
+ }
+
+ h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun;
+ h->freefun = freefun;
+ h->chunk_size = size;
+ h->alignment_mask = alignment - 1;
+
+ chunk = h->chunk = (*h->chunkfun) (h->chunk_size);
+ h->next_free = h->object_base = chunk->contents;
+ h->chunk_limit = chunk->limit
+ = (char *) chunk + h->chunk_size;
+ chunk->prev = 0;
+ /* The initial chunk now contains no empty object. */
+ h->maybe_empty_object = 0;
+}
+
+/* Allocate a new current chunk for the obstack *H
+ on the assumption that LENGTH bytes need to be added
+ to the current object, or a new object of length LENGTH allocated.
+ Copies any partial object from the end of the old chunk
+ to the beginning of the new one. */
+
+void
+_obstack_newchunk (h, length)
+ struct obstack *h;
+ int length;
+{
+ register struct _obstack_chunk* old_chunk = h->chunk;
+ register struct _obstack_chunk* new_chunk;
+ register long new_size;
+ register int obj_size = h->next_free - h->object_base;
+ register int i;
+ int already;
+
+ /* Compute size for new chunk. */
+ new_size = (obj_size + length) + (obj_size >> 3) + 100;
+ if (new_size < h->chunk_size)
+ new_size = h->chunk_size;
+
+ /* Allocate and initialize the new chunk. */
+ new_chunk = h->chunk = (*h->chunkfun) (new_size);
+ new_chunk->prev = old_chunk;
+ new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size;
+
+ /* Move the existing object to the new chunk.
+ Word at a time is fast and is safe if the object
+ is sufficiently aligned. */
+ if (h->alignment_mask + 1 >= DEFAULT_ALIGNMENT)
+ {
+ for (i = obj_size / sizeof (COPYING_UNIT) - 1;
+ i >= 0; i--)
+ ((COPYING_UNIT *)new_chunk->contents)[i]
+ = ((COPYING_UNIT *)h->object_base)[i];
+ /* We used to copy the odd few remaining bytes as one extra COPYING_UNIT,
+ but that can cross a page boundary on a machine
+ which does not do strict alignment for COPYING_UNITS. */
+ already = obj_size / sizeof (COPYING_UNIT) * sizeof (COPYING_UNIT);
+ }
+ else
+ already = 0;
+ /* Copy remaining bytes one by one. */
+ for (i = already; i < obj_size; i++)
+ new_chunk->contents[i] = h->object_base[i];
+
+ /* If the object just copied was the only data in OLD_CHUNK,
+ free that chunk and remove it from the chain.
+ But not if that chunk might contain an empty object. */
+ if (h->object_base == old_chunk->contents && ! h->maybe_empty_object)
+ {
+ new_chunk->prev = old_chunk->prev;
+ (*h->freefun) (old_chunk);
+ }
+
+ h->object_base = new_chunk->contents;
+ h->next_free = h->object_base + obj_size;
+ /* The new chunk certainly contains no empty object yet. */
+ h->maybe_empty_object = 0;
+}
+
+/* Return nonzero if object OBJ has been allocated from obstack H.
+ This is here for debugging.
+ If you use it in a program, you are probably losing. */
+
+int
+_obstack_allocated_p (h, obj)
+ struct obstack *h;
+ POINTER obj;
+{
+ register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk* plp; /* point to previous chunk if any */
+
+ lp = (h)->chunk;
+ /* We use >= rather than > since the object cannot be exactly at
+ the beginning of the chunk but might be an empty object exactly
+ at the end of an adjacent chunk. */
+ while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj))
+ {
+ plp = lp->prev;
+ lp = plp;
+ }
+ return lp != 0;
+}
+
+/* Free objects in obstack H, including OBJ and everything allocate
+ more recently than OBJ. If OBJ is zero, free everything in H. */
+
+#undef obstack_free
+
+/* This function has two names with identical definitions.
+ This is the first one, called from non-ANSI code. */
+
+void
+_obstack_free (h, obj)
+ struct obstack *h;
+ POINTER obj;
+{
+ register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk* plp; /* point to previous chunk if any */
+
+ lp = h->chunk;
+ /* We use >= because there cannot be an object at the beginning of a chunk.
+ But there can be an empty object at that address
+ at the end of another chunk. */
+ while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj))
+ {
+ plp = lp->prev;
+ (*h->freefun) (lp);
+ lp = plp;
+ /* If we switch chunks, we can't tell whether the new current
+ chunk contains an empty object, so assume that it may. */
+ h->maybe_empty_object = 1;
+ }
+ if (lp)
+ {
+ h->object_base = h->next_free = (char *)(obj);
+ h->chunk_limit = lp->limit;
+ h->chunk = lp;
+ }
+ else if (obj != 0)
+ /* obj is not in any of the chunks! */
+ abort ();
+}
+
+/* This function is used from ANSI code. */
+
+void
+obstack_free (h, obj)
+ struct obstack *h;
+ POINTER obj;
+{
+ register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk* plp; /* point to previous chunk if any */
+
+ lp = h->chunk;
+ /* We use >= because there cannot be an object at the beginning of a chunk.
+ But there can be an empty object at that address
+ at the end of another chunk. */
+ while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj))
+ {
+ plp = lp->prev;
+ (*h->freefun) (lp);
+ lp = plp;
+ /* If we switch chunks, we can't tell whether the new current
+ chunk contains an empty object, so assume that it may. */
+ h->maybe_empty_object = 1;
+ }
+ if (lp)
+ {
+ h->object_base = h->next_free = (char *)(obj);
+ h->chunk_limit = lp->limit;
+ h->chunk = lp;
+ }
+ else if (obj != 0)
+ /* obj is not in any of the chunks! */
+ abort ();
+}
+
+#if 0
+/* These are now turned off because the applications do not use it
+ and it uses bcopy via obstack_grow, which causes trouble on sysV. */
+
+/* Now define the functional versions of the obstack macros.
+ Define them to simply use the corresponding macros to do the job. */
+
+#ifdef __STDC__
+/* These function definitions do not work with non-ANSI preprocessors;
+ they won't pass through the macro names in parentheses. */
+
+/* The function names appear in parentheses in order to prevent
+ the macro-definitions of the names from being expanded there. */
+
+POINTER (obstack_base) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_base (obstack);
+}
+
+POINTER (obstack_next_free) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_next_free (obstack);
+}
+
+int (obstack_object_size) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_object_size (obstack);
+}
+
+int (obstack_room) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_room (obstack);
+}
+
+void (obstack_grow) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ obstack_grow (obstack, pointer, length);
+}
+
+void (obstack_grow0) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ obstack_grow0 (obstack, pointer, length);
+}
+
+void (obstack_1grow) (obstack, character)
+ struct obstack *obstack;
+ int character;
+{
+ obstack_1grow (obstack, character);
+}
+
+void (obstack_blank) (obstack, length)
+ struct obstack *obstack;
+ int length;
+{
+ obstack_blank (obstack, length);
+}
+
+void (obstack_1grow_fast) (obstack, character)
+ struct obstack *obstack;
+ int character;
+{
+ obstack_1grow_fast (obstack, character);
+}
+
+void (obstack_blank_fast) (obstack, length)
+ struct obstack *obstack;
+ int length;
+{
+ obstack_blank_fast (obstack, length);
+}
+
+POINTER (obstack_finish) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_finish (obstack);
+}
+
+POINTER (obstack_alloc) (obstack, length)
+ struct obstack *obstack;
+ int length;
+{
+ return obstack_alloc (obstack, length);
+}
+
+POINTER (obstack_copy) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ return obstack_copy (obstack, pointer, length);
+}
+
+POINTER (obstack_copy0) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ return obstack_copy0 (obstack, pointer, length);
+}
+
+#endif /* __STDC__ */
+
+#endif /* 0 */
diff --git a/gnu/usr.bin/as/obstack.h b/gnu/usr.bin/as/obstack.h
new file mode 100644
index 0000000..880015f
--- /dev/null
+++ b/gnu/usr.bin/as/obstack.h
@@ -0,0 +1,448 @@
+/* obstack.h - object stack macros
+ Copyright (C) 1988 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
+ * $Id: obstack.h,v 1.3 1993/10/02 20:57:48 pk Exp $
+ */
+
+
+/* Summary:
+
+All the apparent functions defined here are macros. The idea
+is that you would use these pre-tested macros to solve a
+very specific set of problems, and they would run fast.
+Caution: no side-effects in arguments please!! They may be
+evaluated MANY times!!
+
+These macros operate a stack of objects. Each object starts life
+small, and may grow to maturity. (Consider building a word syllable
+by syllable.) An object can move while it is growing. Once it has
+been "finished" it never changes address again. So the "top of the
+stack" is typically an immature growing object, while the rest of the
+stack is of mature, fixed size and fixed address objects.
+
+These routines grab large chunks of memory, using a function you
+supply, called `obstack_chunk_alloc'. On occasion, they free chunks,
+by calling `obstack_chunk_free'. You must define them and declare
+them before using any obstack macros.
+
+Each independent stack is represented by a `struct obstack'.
+Each of the obstack macros expects a pointer to such a structure
+as the first argument.
+
+One motivation for this package is the problem of growing char strings
+in symbol tables. Unless you are "fascist pig with a read-only mind"
+[Gosper's immortal quote from HAKMEM item 154, out of context] you
+would not like to put any arbitrary upper limit on the length of your
+symbols.
+
+In practice this often means you will build many short symbols and a
+few long symbols. At the time you are reading a symbol you don't know
+how long it is. One traditional method is to read a symbol into a
+buffer, realloc()ating the buffer every time you try to read a symbol
+that is longer than the buffer. This is beaut, but you still will
+want to copy the symbol from the buffer to a more permanent
+symbol-table entry say about half the time.
+
+With obstacks, you can work differently. Use one obstack for all symbol
+names. As you read a symbol, grow the name in the obstack gradually.
+When the name is complete, finalize it. Then, if the symbol exists already,
+free the newly read name.
+
+The way we do this is to take a large chunk, allocating memory from
+low addresses. When you want to build a symbol in the chunk you just
+add chars above the current "high water mark" in the chunk. When you
+have finished adding chars, because you got to the end of the symbol,
+you know how long the chars are, and you can create a new object.
+Mostly the chars will not burst over the highest address of the chunk,
+because you would typically expect a chunk to be (say) 100 times as
+long as an average object.
+
+In case that isn't clear, when we have enough chars to make up
+the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed)
+so we just point to it where it lies. No moving of chars is
+needed and this is the second win: potentially long strings need
+never be explicitly shuffled. Once an object is formed, it does not
+change its address during its lifetime.
+
+When the chars burst over a chunk boundary, we allocate a larger
+chunk, and then copy the partly formed object from the end of the old
+chunk to the beginning of the new larger chunk. We then carry on
+accreting characters to the end of the object as we normally would.
+
+A special macro is provided to add a single char at a time to a
+growing object. This allows the use of register variables, which
+break the ordinary 'growth' macro.
+
+Summary:
+ We allocate large chunks.
+ We carve out one object at a time from the current chunk.
+ Once carved, an object never moves.
+ We are free to append data of any size to the currently
+ growing object.
+ Exactly one object is growing in an obstack at any one time.
+ You can run one obstack per control block.
+ You may have as many control blocks as you dare.
+ Because of the way we do it, you can `unwind' a obstack
+ back to a previous state. (You may remove objects much
+ as you would with a stack.)
+*/
+
+
+/* Don't do the contents of this file more than once. */
+
+#ifndef __OBSTACKS__
+#define __OBSTACKS__
+
+/* We use subtraction of (char *)0 instead of casting to int
+ because on word-addressable machines a simple cast to int
+ may ignore the byte-within-word field of the pointer. */
+
+#ifndef __PTR_TO_INT
+#define __PTR_TO_INT(P) ((P) - (char *)0)
+#endif
+
+#ifndef __INT_TO_PTR
+#define __INT_TO_PTR(P) ((P) + (char *)0)
+#endif
+
+struct _obstack_chunk /* Lives at front of each chunk. */
+{
+ char *limit; /* 1 past end of this chunk */
+ struct _obstack_chunk *prev; /* address of prior chunk or NULL */
+ char contents[4]; /* objects begin here */
+};
+
+struct obstack /* control current object in current chunk */
+{
+ long chunk_size; /* preferred size to allocate chunks in */
+ struct _obstack_chunk* chunk; /* address of current struct obstack_chunk */
+ char *object_base; /* address of object we are building */
+ char *next_free; /* where to add next char to current object */
+ char *chunk_limit; /* address of char after current chunk */
+ int temp; /* Temporary for some macros. */
+ int alignment_mask; /* Mask of alignment for each object. */
+ struct _obstack_chunk *(*chunkfun) (); /* User's fcn to allocate a chunk. */
+ void (*freefun) (); /* User's function to free a chunk. */
+ /* Nonzero means there is a possibility the current chunk contains
+ a zero-length object. This prevents freeing the chunk
+ if we allocate a bigger chunk to replace it. */
+ char maybe_empty_object;
+};
+
+/* Declare the external functions we use; they are in obstack.c. */
+
+#ifdef __STDC__
+ extern void _obstack_newchunk (struct obstack *, int);
+ extern void _obstack_free (struct obstack *, void *);
+ extern void _obstack_begin (struct obstack *, int, int,
+ void *(*) (), void (*) ());
+#else
+ extern void _obstack_newchunk ();
+ extern void _obstack_free ();
+ extern void _obstack_begin ();
+#endif
+
+#ifdef __STDC__
+
+/* Do the function-declarations after the structs
+ but before defining the macros. */
+
+void obstack_init (struct obstack *obstack);
+
+void * obstack_alloc (struct obstack *obstack, int size);
+
+void * obstack_copy (struct obstack *obstack, void *address, int size);
+void * obstack_copy0 (struct obstack *obstack, void *address, int size);
+
+void obstack_free (struct obstack *obstack, void *block);
+
+void obstack_blank (struct obstack *obstack, int size);
+
+void obstack_grow (struct obstack *obstack, void *data, int size);
+void obstack_grow0 (struct obstack *obstack, void *data, int size);
+
+void obstack_1grow (struct obstack *obstack, int data_char);
+void obstack_ptr_grow (struct obstack *obstack, void *data);
+void obstack_int_grow (struct obstack *obstack, int data);
+
+void * obstack_finish (struct obstack *obstack);
+
+int obstack_object_size (struct obstack *obstack);
+
+int obstack_room (struct obstack *obstack);
+void obstack_1grow_fast (struct obstack *obstack, int data_char);
+void obstack_ptr_grow_fast (struct obstack *obstack, void *data);
+void obstack_int_grow_fast (struct obstack *obstack, int data);
+void obstack_blank_fast (struct obstack *obstack, int size);
+
+void * obstack_base (struct obstack *obstack);
+void * obstack_next_free (struct obstack *obstack);
+int obstack_alignment_mask (struct obstack *obstack);
+int obstack_chunk_size (struct obstack *obstack);
+
+#endif /* __STDC__ */
+
+/* Non-ANSI C cannot really support alternative functions for these macros,
+ so we do not declare them. */
+
+/* Pointer to beginning of object being allocated or to be allocated next.
+ Note that this might not be the final address of the object
+ because a new chunk might be needed to hold the final size. */
+
+#define obstack_base(h) ((h)->object_base)
+
+/* Size for allocating ordinary chunks. */
+
+#define obstack_chunk_size(h) ((h)->chunk_size)
+
+/* Pointer to next byte not yet allocated in current chunk. */
+
+#define obstack_next_free(h) ((h)->next_free)
+
+/* Mask specifying low bits that should be clear in address of an object. */
+
+#define obstack_alignment_mask(h) ((h)->alignment_mask)
+
+#define obstack_init(h) \
+ _obstack_begin ((h), 0, 0, \
+ (void *(*) ()) obstack_chunk_alloc, (void (*) ())obstack_chunk_free)
+
+#define obstack_begin(h, size) \
+ _obstack_begin ((h), (size), 0, \
+ (void *(*) ()) obstack_chunk_alloc, (void (*) ())obstack_chunk_free)
+
+#define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = achar)
+
+#define obstack_blank_fast(h,n) ((h)->next_free += (n))
+
+#if defined (__GNUC__) && defined (__STDC__)
+#if __GNUC__ < 2
+#define __extension__
+#endif
+
+/* For GNU C, if not -traditional,
+ we can define these macros to compute all args only once
+ without using a global variable.
+ Also, we can avoid using the `temp' slot, to make faster code. */
+
+#define obstack_object_size(OBSTACK) \
+ __extension__ \
+ ({ struct obstack *__o = (OBSTACK); \
+ (unsigned) (__o->next_free - __o->object_base); })
+
+#define obstack_room(OBSTACK) \
+ __extension__ \
+ ({ struct obstack *__o = (OBSTACK); \
+ (unsigned) (__o->chunk_limit - __o->next_free); })
+
+/* Note that the call to _obstack_newchunk is enclosed in (..., 0)
+ so that we can avoid having void expressions
+ in the arms of the conditional expression.
+ Casting the third operand to void was tried before,
+ but some compilers won't accept it. */
+#define obstack_grow(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ int __len = (length); \
+ ((__o->next_free + __len > __o->chunk_limit) \
+ ? (_obstack_newchunk (__o, __len), 0) : 0); \
+ memcpy (__o->next_free, where, __len); \
+ __o->next_free += __len; \
+ (void) 0; })
+
+#define obstack_grow0(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ int __len = (length); \
+ ((__o->next_free + __len + 1 > __o->chunk_limit) \
+ ? (_obstack_newchunk (__o, __len + 1), 0) : 0), \
+ memcpy (__o->next_free, where, __len), \
+ __o->next_free += __len, \
+ *(__o->next_free)++ = 0; \
+ (void) 0; })
+
+#define obstack_1grow(OBSTACK,datum) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ ((__o->next_free + 1 > __o->chunk_limit) \
+ ? (_obstack_newchunk (__o, 1), 0) : 0), \
+ *(__o->next_free)++ = (datum); \
+ (void) 0; })
+
+/* These assume that the obstack alignment is good enough for pointers or ints,
+ and that the data added so far to the current object
+ shares that much alignment. */
+
+#define obstack_ptr_grow(OBSTACK,datum) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ ((__o->next_free + sizeof (void *) > __o->chunk_limit) \
+ ? (_obstack_newchunk (__o, sizeof (void *)), 0) : 0), \
+ *(*(void ***)&__o->next_free)++ = ((void *)datum); \
+ (void) 0; })
+
+#define obstack_int_grow(OBSTACK,datum) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ ((__o->next_free + sizeof (int) > __o->chunk_limit) \
+ ? (_obstack_newchunk (__o, sizeof (int)), 0) : 0), \
+ *(*(int **)&__o->next_free)++ = ((int)datum); \
+ (void) 0; })
+
+#define obstack_ptr_grow_fast(h,aptr) (*(*(void ***)&(h)->next_free)++ = (void *)aptr)
+#define obstack_int_grow_fast(h,aint) (*(*(int **)&(h)->next_free)++ = (int)aint)
+
+#define obstack_blank(OBSTACK,length) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ int __len = (length); \
+ ((__o->chunk_limit - __o->next_free < __len) \
+ ? (_obstack_newchunk (__o, __len), 0) : 0); \
+ __o->next_free += __len; \
+ (void) 0; })
+
+#define obstack_alloc(OBSTACK,length) \
+__extension__ \
+({ struct obstack *__h = (OBSTACK); \
+ obstack_blank (__h, (length)); \
+ obstack_finish (__h); })
+
+#define obstack_copy(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__h = (OBSTACK); \
+ obstack_grow (__h, (where), (length)); \
+ obstack_finish (__h); })
+
+#define obstack_copy0(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__h = (OBSTACK); \
+ obstack_grow0 (__h, (where), (length)); \
+ obstack_finish (__h); })
+
+/* The local variable is named __o1 to avoid a name conflict
+ when obstack_blank is called. */
+#define obstack_finish(OBSTACK) \
+__extension__ \
+({ struct obstack *__o1 = (OBSTACK); \
+ void *value = (void *) __o1->object_base; \
+ if (__o1->next_free == value) \
+ __o1->maybe_empty_object = 1; \
+ __o1->next_free \
+ = __INT_TO_PTR ((__PTR_TO_INT (__o1->next_free)+__o1->alignment_mask)\
+ & ~ (__o1->alignment_mask)); \
+ ((__o1->next_free - (char *)__o1->chunk \
+ > __o1->chunk_limit - (char *)__o1->chunk) \
+ ? (__o1->next_free = __o1->chunk_limit) : 0); \
+ __o1->object_base = __o1->next_free; \
+ value; })
+
+#define obstack_free(OBSTACK, OBJ) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ void *__obj = (OBJ); \
+ if (__obj > (void *)__o->chunk && __obj < (void *)__o->chunk_limit) \
+ __o->next_free = __o->object_base = __obj; \
+ else (obstack_free) (__o, __obj); })
+
+#else /* not __GNUC__ or not __STDC__ */
+
+#define obstack_object_size(h) \
+ (unsigned) ((h)->next_free - (h)->object_base)
+
+#define obstack_room(h) \
+ (unsigned) ((h)->chunk_limit - (h)->next_free)
+
+#define obstack_grow(h,where,length) \
+( (h)->temp = (length), \
+ (((h)->next_free + (h)->temp > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), (h)->temp), 0) : 0), \
+ memcpy ((h)->next_free, where, (h)->temp), \
+ (h)->next_free += (h)->temp)
+
+#define obstack_grow0(h,where,length) \
+( (h)->temp = (length), \
+ (((h)->next_free + (h)->temp + 1 > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), (h)->temp + 1), 0) : 0), \
+ memcpy ((h)->next_free, where, (h)->temp), \
+ (h)->next_free += (h)->temp, \
+ *((h)->next_free)++ = 0)
+
+#define obstack_1grow(h,datum) \
+( (((h)->next_free + 1 > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), 1), 0) : 0), \
+ *((h)->next_free)++ = (datum))
+
+#define obstack_ptr_grow(h,datum) \
+( (((h)->next_free + sizeof (char *) > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), sizeof (char *)), 0) : 0), \
+ *(*(char ***)&(h)->next_free)++ = ((char *)datum))
+
+#define obstack_int_grow(h,datum) \
+( (((h)->next_free + sizeof (int) > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), sizeof (int)), 0) : 0), \
+ *(*(int **)&(h)->next_free)++ = ((int)datum))
+
+#define obstack_ptr_grow_fast(h,aptr) (*(*(char ***)&(h)->next_free)++ = (char *)aptr)
+#define obstack_int_grow_fast(h,aint) (*(*(int **)&(h)->next_free)++ = (int)aint)
+#define obstack_blank(h,length) \
+( (h)->temp = (length), \
+ (((h)->chunk_limit - (h)->next_free < (h)->temp) \
+ ? (_obstack_newchunk ((h), (h)->temp), 0) : 0), \
+ (h)->next_free += (h)->temp)
+
+#define obstack_alloc(h,length) \
+ (obstack_blank ((h), (length)), obstack_finish ((h)))
+
+#define obstack_copy(h,where,length) \
+ (obstack_grow ((h), (where), (length)), obstack_finish ((h)))
+
+#define obstack_copy0(h,where,length) \
+ (obstack_grow0 ((h), (where), (length)), obstack_finish ((h)))
+
+#define obstack_finish(h) \
+( ((h)->next_free == (h)->object_base \
+ ? (((h)->maybe_empty_object = 1), 0) \
+ : 0), \
+ (h)->temp = __PTR_TO_INT ((h)->object_base), \
+ (h)->next_free \
+ = __INT_TO_PTR ((__PTR_TO_INT ((h)->next_free)+(h)->alignment_mask) \
+ & ~ ((h)->alignment_mask)), \
+ (((h)->next_free - (char *)(h)->chunk \
+ > (h)->chunk_limit - (char *)(h)->chunk) \
+ ? ((h)->next_free = (h)->chunk_limit) : 0), \
+ (h)->object_base = (h)->next_free, \
+ __INT_TO_PTR ((h)->temp))
+
+#ifdef __STDC__
+#define obstack_free(h,obj) \
+( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \
+ (((h)->temp > 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\
+ ? (int) ((h)->next_free = (h)->object_base \
+ = (h)->temp + (char *) (h)->chunk) \
+ : (((obstack_free) ((h), (h)->temp + (char *) (h)->chunk), 0), 0)))
+#else
+#define obstack_free(h,obj) \
+( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \
+ (((h)->temp > 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\
+ ? (int) ((h)->next_free = (h)->object_base \
+ = (h)->temp + (char *) (h)->chunk) \
+ : (_obstack_free ((h), (h)->temp + (char *) (h)->chunk), 0)))
+#endif
+
+#endif /* not __GNUC__ or not __STDC__ */
+
+#endif /* not __OBSTACKS__ */
diff --git a/gnu/usr.bin/as/opcode/ChangeLog b/gnu/usr.bin/as/opcode/ChangeLog
new file mode 100644
index 0000000..dcb0498
--- /dev/null
+++ b/gnu/usr.bin/as/opcode/ChangeLog
@@ -0,0 +1,56 @@
+Mon Feb 24 02:02:04 1992 K. Richard Pixley (rich@cygnus.com)
+
+ * ns32k.h: SEQUENT_COMPATIBILITY -> TE_SEQUENT.
+
+ * i860.h: added "fst.q".
+
+Fri Feb 21 01:29:51 1992 K. Richard Pixley (rich@cygnus.com)
+
+ * i386.h: added inb, inw, outb, outw opcodes, added att syntax for
+ scmp, slod, smov, ssca, ssto. Curtesy Minh Tran-Le
+ <TRANLE@INTELLICORP.COM>.
+
+Thu Jan 30 07:31:44 1992 Steve Chamberlain (sac at rtl.cygnus.com)
+
+ * h8300.h: turned op_type enum into #define list
+
+Thu Jan 30 01:07:24 1992 John Gilmore (gnu at cygnus.com)
+
+ * sparc.h: Remove "cypress" architecture. Remove "fitox" and
+ similar instructions -- they've been renamed to "fitoq", etc.
+ REALLY fix tsubcctv. Fix "fcmpeq" and "fcmpq" which had wrong
+ number of arguments.
+ * h8300.h: Remove extra ; which produces compiler warning.
+
+Tue Jan 28 22:59:22 1992 Stu Grossman (grossman at cygnus.com)
+
+ * sparc.h: fix opcode for tsubcctv.
+
+Tue Jan 7 17:19:39 1992 K. Richard Pixley (rich at cygnus.com)
+
+ * sparc.h: fba and cba are now aliases for fb and cb respectively.
+
+Fri Dec 27 10:55:50 1991 Per Bothner (bothner at cygnus.com)
+
+ * sparc.h (nop): Made the 'lose' field be even tighter,
+ so only a standard 'nop' is disassembled as a nop.
+
+Sun Dec 22 12:18:18 1991 Michael Tiemann (tiemann at cygnus.com)
+
+ * sparc.h (nop): Add RD_GO to `lose' so that only %g0 in dest is
+ disassembled as a nop.
+
+Tue Dec 10 00:22:20 1991 K. Richard Pixley (rich at rtl.cygnus.com)
+
+ * sparc.h: fix a typo.
+
+Sat Nov 30 20:40:51 1991 Steve Chamberlain (sac at rtl.cygnus.com)
+
+ * a29k.h, arm.h, h8300.h, i386.h, i860.h, i960.h , m68k.h,
+ m88k.h, mips.h , np1.h, ns32k.h, pn.h, pyr.h, sparc.h, tahoe.h,
+ vax.h, ChangeLog: renamed from ../<foo>-opcode.h
+
+
+
+
+
diff --git a/gnu/usr.bin/as/opcode/a29k.h b/gnu/usr.bin/as/opcode/a29k.h
new file mode 100644
index 0000000..8c36167
--- /dev/null
+++ b/gnu/usr.bin/as/opcode/a29k.h
@@ -0,0 +1,327 @@
+/* Table of opcodes for the AMD 29000
+ Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB and GAS.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+struct a29k_opcode {
+ /* Name of the instruction. */
+ char *name;
+
+ /* Opcode word */
+ unsigned long opcode;
+
+ /* A string of characters which describe the operands.
+ Valid characters are:
+ , Itself. The character appears in the assembly code.
+ a RA. The register number is in bits 8-15 of the instruction.
+ b RB. The register number is in bits 0-7 of the instruction.
+ c RC. The register number is in bits 16-23 of the instruction.
+ i An immediate operand is in bits 0-7 of the instruction.
+ x Bits 0-7 and 16-23 of the instruction are bits 0-7 and 8-15
+ (respectively) of the immediate operand.
+ h Same as x but the instruction contains bits 16-31 of the
+ immediate operand.
+ X Same as x but bits 16-31 of the signed immediate operand
+ are set to 1 (thus the operand is always negative).
+ P,A Bits 0-7 and 16-23 of the instruction are bits 2-9 and 10-17
+ (respectively) of the immediate operand.
+ P=PC-relative, sign-extended to 32 bits.
+ A=Absolute, zero-extended to 32 bits.
+ e CE bit (bit 23) for a load/store instruction.
+ n Control field (bits 16-22) for a load/store instruction.
+ v Immediate operand in bits 16-23 of the instruction.
+ (used for trap numbers).
+ s SA. Special-purpose register number in bits 8-15
+ of the instruction.
+ u UI--bit 7 of the instruction.
+ r RND--bits 4-6 of the instruction.
+ d FD--bits 2-3 of the instruction.
+ f FS--bits 0-1 of the instruction.
+
+ Extensions for 29050:
+
+ d FMT--bits 2-3 of the instruction (not really new).
+ f ACN--bits 0-1 of the instruction (not really new).
+ F FUNC--Special function in bits 18-21 of the instruction.
+ C ACN--bits 16-17 specifying the accumlator register. */
+ char *args;
+};
+
+#ifndef CONST
+#define CONST
+#endif /* CONST */
+
+static CONST struct a29k_opcode a29k_opcodes[] =
+{
+
+{ "add", 0x14000000, "c,a,b" },
+{ "add", 0x15000000, "c,a,i" },
+{ "addc", 0x1c000000, "c,a,b" },
+{ "addc", 0x1d000000, "c,a,i" },
+{ "addcs", 0x18000000, "c,a,b" },
+{ "addcs", 0x19000000, "c,a,i" },
+{ "addcu", 0x1a000000, "c,a,b" },
+{ "addcu", 0x1b000000, "c,a,i" },
+{ "adds", 0x10000000, "c,a,b" },
+{ "adds", 0x11000000, "c,a,i" },
+{ "addu", 0x12000000, "c,a,b" },
+{ "addu", 0x13000000, "c,a,i" },
+{ "and", 0x90000000, "c,a,b" },
+{ "and", 0x91000000, "c,a,i" },
+{ "andn", 0x9c000000, "c,a,b" },
+{ "andn", 0x9d000000, "c,a,i" },
+{ "aseq", 0x70000000, "v,a,b" },
+{ "aseq", 0x71000000, "v,a,i" },
+{ "asge", 0x5c000000, "v,a,b" },
+{ "asge", 0x5d000000, "v,a,i" },
+{ "asgeu", 0x5e000000, "v,a,b" },
+{ "asgeu", 0x5f000000, "v,a,i" },
+{ "asgt", 0x58000000, "v,a,b" },
+{ "asgt", 0x59000000, "v,a,i" },
+{ "asgtu", 0x5a000000, "v,a,b" },
+{ "asgtu", 0x5b000000, "v,a,i" },
+{ "asle", 0x54000000, "v,a,b" },
+{ "asle", 0x55000000, "v,a,i" },
+{ "asleu", 0x56000000, "v,a,b" },
+{ "asleu", 0x57000000, "v,a,i" },
+{ "aslt", 0x50000000, "v,a,b" },
+{ "aslt", 0x51000000, "v,a,i" },
+{ "asltu", 0x52000000, "v,a,b" },
+{ "asltu", 0x53000000, "v,a,i" },
+{ "asneq", 0x72000000, "v,a,b" },
+{ "asneq", 0x73000000, "v,a,i" },
+{ "call", 0xa8000000, "a,P" },
+{ "call", 0xa9000000, "a,A" },
+{ "calli", 0xc8000000, "a,b" },
+{ "class", 0xe6000000, "c,a,f" },
+{ "clz", 0x08000000, "c,b" },
+{ "clz", 0x09000000, "c,i" },
+{ "const", 0x03000000, "a,x" },
+{ "consth", 0x02000000, "a,h" },
+{ "consthz", 0x05000000, "a,h" },
+{ "constn", 0x01000000, "a,X" },
+{ "convert", 0xe4000000, "c,a,u,r,d,f" },
+{ "cpbyte", 0x2e000000, "c,a,b" },
+{ "cpbyte", 0x2f000000, "c,a,i" },
+{ "cpeq", 0x60000000, "c,a,b" },
+{ "cpeq", 0x61000000, "c,a,i" },
+{ "cpge", 0x4c000000, "c,a,b" },
+{ "cpge", 0x4d000000, "c,a,i" },
+{ "cpgeu", 0x4e000000, "c,a,b" },
+{ "cpgeu", 0x4f000000, "c,a,i" },
+{ "cpgt", 0x48000000, "c,a,b" },
+{ "cpgt", 0x49000000, "c,a,i" },
+{ "cpgtu", 0x4a000000, "c,a,b" },
+{ "cpgtu", 0x4b000000, "c,a,i" },
+{ "cple", 0x44000000, "c,a,b" },
+{ "cple", 0x45000000, "c,a,i" },
+{ "cpleu", 0x46000000, "c,a,b" },
+{ "cpleu", 0x47000000, "c,a,i" },
+{ "cplt", 0x40000000, "c,a,b" },
+{ "cplt", 0x41000000, "c,a,i" },
+{ "cpltu", 0x42000000, "c,a,b" },
+{ "cpltu", 0x43000000, "c,a,i" },
+{ "cpneq", 0x62000000, "c,a,b" },
+{ "cpneq", 0x63000000, "c,a,i" },
+{ "dadd", 0xf1000000, "c,a,b" },
+{ "ddiv", 0xf7000000, "c,a,b" },
+{ "deq", 0xeb000000, "c,a,b" },
+{ "dge", 0xef000000, "c,a,b" },
+{ "dgt", 0xed000000, "c,a,b" },
+{ "div", 0x6a000000, "c,a,b" },
+{ "div", 0x6b000000, "c,a,i" },
+{ "div0", 0x68000000, "c,b" },
+{ "div0", 0x69000000, "c,i" },
+{ "divide", 0xe1000000, "c,a,b" },
+{ "dividu", 0xe3000000, "c,a,b" },
+{ "divl", 0x6c000000, "c,a,b" },
+{ "divl", 0x6d000000, "c,a,i" },
+{ "divrem", 0x6e000000, "c,a,b" },
+{ "divrem", 0x6f000000, "c,a,i" },
+{ "dmac", 0xd9000000, "F,C,a,b" },
+{ "dmsm", 0xdb000000, "c,a,b" },
+{ "dmul", 0xf5000000, "c,a,b" },
+{ "dsub", 0xf3000000, "c,a,b" },
+{ "emulate", 0xd7000000, "v,a,b" },
+{ "exbyte", 0x0a000000, "c,a,b" },
+{ "exbyte", 0x0b000000, "c,a,i" },
+{ "exhw", 0x7c000000, "c,a,b" },
+{ "exhw", 0x7d000000, "c,a,i" },
+{ "exhws", 0x7e000000, "c,a" },
+{ "extract", 0x7a000000, "c,a,b" },
+{ "extract", 0x7b000000, "c,a,i" },
+{ "fadd", 0xf0000000, "c,a,b" },
+{ "fdiv", 0xf6000000, "c,a,b" },
+{ "fdmul", 0xf9000000, "c,a,b" },
+{ "feq", 0xea000000, "c,a,b" },
+{ "fge", 0xee000000, "c,a,b" },
+{ "fgt", 0xec000000, "c,a,b" },
+{ "fmac", 0xd8000000, "F,C,a,b" },
+{ "fmsm", 0xda000000, "c,a,b" },
+{ "fmul", 0xf4000000, "c,a,b" },
+{ "fsub", 0xf2000000, "c,a,b" },
+{ "halt", 0x89000000, "" },
+{ "inbyte", 0x0c000000, "c,a,b" },
+{ "inbyte", 0x0d000000, "c,a,i" },
+{ "inhw", 0x78000000, "c,a,b" },
+{ "inhw", 0x79000000, "c,a,i" },
+{ "inv", 0x9f000000, "" },
+{ "iret", 0x88000000, "" },
+{ "iretinv", 0x8c000000, "" },
+{ "jmp", 0xa0000000, "P" },
+{ "jmp", 0xa1000000, "A" },
+{ "jmpf", 0xa4000000, "a,P" },
+{ "jmpf", 0xa5000000, "a,A" },
+{ "jmpfdec", 0xb4000000, "a,P" },
+{ "jmpfdec", 0xb5000000, "a,A" },
+{ "jmpfi", 0xc4000000, "a,b" },
+{ "jmpi", 0xc0000000, "b" },
+{ "jmpt", 0xac000000, "a,P" },
+{ "jmpt", 0xad000000, "a,A" },
+{ "jmpti", 0xcc000000, "a,b" },
+{ "load", 0x16000000, "e,n,a,b" },
+{ "load", 0x17000000, "e,n,a,i" },
+{ "loadl", 0x06000000, "e,n,a,b" },
+{ "loadl", 0x07000000, "e,n,a,i" },
+{ "loadm", 0x36000000, "e,n,a,b" },
+{ "loadm", 0x37000000, "e,n,a,i" },
+{ "loadset", 0x26000000, "e,n,a,b" },
+{ "loadset", 0x27000000, "e,n,a,i" },
+{ "mfacc", 0xe9000100, "c,d,f" },
+{ "mfsr", 0xc6000000, "c,s" },
+{ "mftlb", 0xb6000000, "c,a" },
+{ "mtacc", 0xe8010000, "a,d,f" },
+{ "mtsr", 0xce000000, "s,b" },
+{ "mtsrim", 0x04000000, "s,x" },
+{ "mttlb", 0xbe000000, "a,b" },
+{ "mul", 0x64000000, "c,a,b" },
+{ "mul", 0x65000000, "c,a,i" },
+{ "mull", 0x66000000, "c,a,b" },
+{ "mull", 0x67000000, "c,a,i" },
+{ "multiplu", 0xe2000000, "c,a,b" },
+{ "multiply", 0xe0000000, "c,a,b" },
+{ "multm", 0xde000000, "c,a,b" },
+{ "multmu", 0xdf000000, "c,a,b" },
+{ "mulu", 0x74000000, "c,a,b" },
+{ "mulu", 0x75000000, "c,a,i" },
+{ "nand", 0x9a000000, "c,a,b" },
+{ "nand", 0x9b000000, "c,a,i" },
+{ "nop", 0x70400101, "" },
+{ "nor", 0x98000000, "c,a,b" },
+{ "nor", 0x99000000, "c,a,i" },
+{ "or", 0x92000000, "c,a,b" },
+{ "or", 0x93000000, "c,a,i" },
+{ "orn", 0xaa000000, "c,a,b" },
+{ "orn", 0xab000000, "c,a,i" },
+
+/* The description of "setip" in Chapter 8 ("instruction set") of the user's
+ manual claims that these are absolute register numbers. But section
+ 7.2.1 explains that they are not. The latter is correct, so print
+ these normally ("lr0", "lr5", etc.). */
+{ "setip", 0x9e000000, "c,a,b" },
+
+{ "sll", 0x80000000, "c,a,b" },
+{ "sll", 0x81000000, "c,a,i" },
+{ "sqrt", 0xe5000000, "c,a,f" },
+{ "sra", 0x86000000, "c,a,b" },
+{ "sra", 0x87000000, "c,a,i" },
+{ "srl", 0x82000000, "c,a,b" },
+{ "srl", 0x83000000, "c,a,i" },
+{ "store", 0x1e000000, "e,n,a,b" },
+{ "store", 0x1f000000, "e,n,a,i" },
+{ "storel", 0x0e000000, "e,n,a,b" },
+{ "storel", 0x0f000000, "e,n,a,i" },
+{ "storem", 0x3e000000, "e,n,a,b" },
+{ "storem", 0x3f000000, "e,n,a,i" },
+{ "sub", 0x24000000, "c,a,b" },
+{ "sub", 0x25000000, "c,a,i" },
+{ "subc", 0x2c000000, "c,a,b" },
+{ "subc", 0x2d000000, "c,a,i" },
+{ "subcs", 0x28000000, "c,a,b" },
+{ "subcs", 0x29000000, "c,a,i" },
+{ "subcu", 0x2a000000, "c,a,b" },
+{ "subcu", 0x2b000000, "c,a,i" },
+{ "subr", 0x34000000, "c,a,b" },
+{ "subr", 0x35000000, "c,a,i" },
+{ "subrc", 0x3c000000, "c,a,b" },
+{ "subrc", 0x3d000000, "c,a,i" },
+{ "subrcs", 0x38000000, "c,a,b" },
+{ "subrcs", 0x39000000, "c,a,i" },
+{ "subrcu", 0x3a000000, "c,a,b" },
+{ "subrcu", 0x3b000000, "c,a,i" },
+{ "subrs", 0x30000000, "c,a,b" },
+{ "subrs", 0x31000000, "c,a,i" },
+{ "subru", 0x32000000, "c,a,b" },
+{ "subru", 0x33000000, "c,a,i" },
+{ "subs", 0x20000000, "c,a,b" },
+{ "subs", 0x21000000, "c,a,i" },
+{ "subu", 0x22000000, "c,a,b" },
+{ "subu", 0x23000000, "c,a,i" },
+{ "xnor", 0x96000000, "c,a,b" },
+{ "xnor", 0x97000000, "c,a,i" },
+{ "xor", 0x94000000, "c,a,b" },
+{ "xor", 0x95000000, "c,a,i" },
+
+{ "", 0x0, "" } /* Dummy entry, not included in NUM_OPCODES. This
+ lets code examine entry i+1 without checking
+ if we've run off the end of the table. */
+};
+
+CONST unsigned int num_opcodes = (((sizeof a29k_opcodes) / (sizeof a29k_opcodes[0])) - 1);
+
+/*
+ * $Log: a29k.h,v $
+ * Revision 1.1 1993/10/02 21:00:40 pk
+ * GNU gas 1.92.3 based assembler supporting PIC code (for i386 and sparc).
+ *
+ * Revision 1.2 1992/02/29 17:10:43 rich
+ * various smallish fixes from mail archives
+ *
+ * Revision 1.1.1.1 1992/02/24 02:34:30 rich
+ * devo fork
+ *
+ * Revision 1.1 1991/12/01 02:22:19 sac
+ * Initial revision
+ *
+ * Revision 1.5 1991/11/07 16:59:19 sac
+ * Fixed encoding of mtacc instruction.
+ *
+ * Revision 1.4 1991/08/06 07:20:27 rich
+ * Fixing CONST declarations.
+ *
+ * Revision 1.3 1991/08/05 22:31:05 rich
+ * *** empty log message ***
+ *
+ * Revision 1.2 1991/07/15 23:34:04 steve
+ * *** empty log message ***
+ *
+ * Revision 1.1 1991/05/19 00:19:33 rich
+ * Initial revision
+ *
+ * Revision 1.1.1.1 1991/04/04 18:15:23 rich
+ * new gas main line
+ *
+ * Revision 1.1 1991/04/04 18:15:23 rich
+ * Initial revision
+ *
+ * Revision 1.2 1991/03/30 17:13:19 rich
+ * num_opcodes now unsigned. Also, added rcsid and log.
+ *
+ *
+ */
+
+/* end of a29k-opcode.h */
diff --git a/gnu/usr.bin/as/opcode/h8300.h b/gnu/usr.bin/as/opcode/h8300.h
new file mode 100644
index 0000000..f4702a8
--- /dev/null
+++ b/gnu/usr.bin/as/opcode/h8300.h
@@ -0,0 +1,266 @@
+/* Opcode table for the H8-300
+ Copyright (C) 1991,1992 Free Software Foundation.
+ Written by Steve Chamberlain, sac@cygnus.com.
+
+This file is part of GDB, the GNU Debugger and GAS, the GNU Assembler.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+typedef int op_type;
+
+#define Hex0 0
+#define Hex1 1
+#define Hex2 2
+#define Hex3 3
+#define Hex4 4
+#define Hex5 5
+#define Hex6 6
+#define Hex7 7
+#define Hex8 8
+#define Hex9 9
+#define HexA 10
+#define HexB 11
+#define HexC 12
+#define HexD 13
+#define HexE 14
+#define HexF 15
+#define START 0x20
+#define KBIT 0x21 /* K is #1, or #2, yielding 0x0 or 0x8 */
+#define IMM3 0x22 /* bit number */
+#define RD8 0x23 /* 8 bit reg as 2nd op */
+#define RD16 0x24 /* 16 bit reg as 2nd op */
+#define RS8 0x25 /* 8 bit reg as 1st op */
+#define RS16 0x26 /* 16 bit reg 1st op */
+#define IMM8 0x27 /* constant which fits into 8 bits */
+#define IMM16 0x28 /* constant which fits into 16 bits */
+#define CCR 0x29 /* CCR reg */
+#define ABS8SRC 0x2a /* abs 8 address mode */
+#define ABS8DST 0x2b /* abs 8 address mode */
+#define DISP8 0x2c /* pc rel displacement */
+#define ABS16SRC 0x2d /* abs 16 address mode */
+#define ABS16OR8SRC 0x2e /* abs 16 address mode, but could be abs 8 */
+#define ABS16DST 0x2f /* abs 16 address mode */
+#define ABS16OR8DST 0x30 /* abs 16 address mode */
+#define DISPSRC 0x31 /* @(r:16) address mode src */
+#define DISPDST 0x32 /* @(r:16) address mode dst*/
+#define DISPREG 0x33 /* register from DISP address mode */
+#define RDDEC 0x34 /* @-rn mode */
+#define RSINC 0x35 /* @rn+ mode */
+#define RDIND 0x36 /* @R mode dst */
+#define RSIND 0x37 /* @R mode src */
+#define MEMIND 0x38 /* @@abs8 mode */
+#define ABS16ORREL8SRC 0x39 /* abs 16bit or pcrel */
+#define IGNORE 0x3a
+#define B30 0x40 /* bit 3 must be low */
+#define B31 0x80 /* bit 3 must be high */
+#define E 0x81 /* End of list */
+
+
+
+struct code
+{
+ op_type nib[9];
+} ;
+
+struct arg
+{
+ op_type nib[3];
+} ;
+
+struct h8_opcode
+{
+ char *name;
+ struct arg args;
+ struct code data;
+ char length;
+ char noperands;
+ char idx;
+ char size;
+
+};
+
+
+
+
+
+#ifdef DEFINE_TABLE
+
+#define BITOP(imm, name, op00, op01,op10,op11, op20,op21)\
+{ name, {imm,RD8,E}, {op00, op01, imm, RD8,E}},\
+{ name, {imm,RDIND,E}, {op10, op11, RDIND, 0, op00,op01, imm, 0,E}},\
+{ name, {imm,ABS8DST,E},{op20, op21, ABS8DST, IGNORE, op00,op01, imm, 0,E}}
+
+#define EBITOP(imm, name, op00, op01,op10,op11, op20,op21)\
+ BITOP(imm, name, op00+1, op01, op10,op11, op20,op21),\
+ BITOP(RS8, name, op00, op01, op10,op11, op20,op21)
+
+#define WTWOP(name, op1, op2) \
+{ name, {RS16, RD16, E}, { op1, op2, RS16, RD16, E}}
+
+#define BRANCH(name, op) \
+{ name,{DISP8,E}, { Hex4, op, DISP8,IGNORE,E }}
+
+#define SOP(name) \
+{ name
+#define EOP }
+
+
+#define TWOOP(name, op1, op2,op3) \
+{ name, {IMM8, RD8,E}, { op1, RD8, IMM8,IGNORE,E}},\
+{ name, {RS8, RD8, E}, { op2, op3, RS8, RD8 ,E}}
+
+#define UNOP(name, op1, op2) \
+{ name, {RS8, E}, { op1, op2, 0, RS8, E}}
+
+#define UNOP3(name, op1, op2, op3) \
+{ name , {RS8, E}, {op1, op2, op3, RS8, E}}
+
+struct h8_opcode h8_opcodes[]
+=
+{
+ TWOOP("add.b", Hex8, Hex0,Hex8),
+ WTWOP("add.w", Hex0, Hex9),
+ SOP("adds"), {KBIT,RD16|B30, E}, {Hex0, HexB, KBIT, RD16|B30, E} EOP,
+ TWOOP("addx", Hex9,Hex0,HexE),
+ TWOOP("and", HexE,Hex1,Hex6),
+ SOP("andc"), {IMM8, CCR, E}, { Hex0, Hex6, IMM8,IGNORE, E} EOP,
+ BITOP(IMM3|B30, "band", Hex7, Hex6, Hex7, HexC, Hex7, HexE),
+ BRANCH("bra", Hex0),
+ BRANCH("bt", Hex0),
+ BRANCH("brn", Hex1),
+ BRANCH("bf", Hex1),
+ BRANCH("bhi", Hex2),
+ BRANCH("bls", Hex3),
+ BRANCH("bcc", Hex4),
+ BRANCH("bhs", Hex4),
+ BRANCH("bcs", Hex5),
+ BRANCH("blo", Hex5),
+ BRANCH("bne", Hex6),
+ BRANCH("beq", Hex7),
+ BRANCH("bvc", Hex8),
+ BRANCH("bvs", Hex9),
+ BRANCH("bpl", HexA),
+ BRANCH("bmi", HexB),
+ BRANCH("bge", HexC),
+ BRANCH("blt", HexD),
+ BRANCH("bgt", HexE),
+ BRANCH("ble", HexF),
+ EBITOP(IMM3|B30,"bclr", Hex6, Hex2, Hex7, HexD, Hex7, HexF),
+ BITOP(IMM3|B31,"biand", Hex7, Hex6, Hex7, HexC, Hex7, HexE),
+ BITOP(IMM3|B31, "bild", Hex7, Hex7,Hex7, HexC, Hex7, HexE),
+ BITOP(IMM3|B31, "bior", Hex7, Hex4,Hex7, HexC, Hex7, HexE),
+ BITOP(IMM3|B31, "bist", Hex6, Hex7,Hex7, HexD, Hex7, HexE),
+ BITOP(IMM3|B31, "bixor", Hex7, Hex5,Hex7, HexC, Hex7, HexE),
+ BITOP(IMM3|B30, "bld", Hex7, Hex7,Hex7, HexC, Hex7, HexE),
+ EBITOP(IMM3|B30,"bnot", Hex6, Hex1, Hex7, HexD, Hex7, HexF),
+ BITOP(IMM3|B30,"bor", Hex7, Hex4,Hex7, HexC, Hex7, HexE),
+ EBITOP(IMM3|B30,"bset", Hex6, Hex0,Hex7, HexD, Hex7, HexF),
+ SOP("bsr"),{DISP8, E},{ Hex5, Hex5, DISP8,IGNORE, E}, EOP,
+ BITOP(IMM3|B30, "bst", Hex6, Hex7,Hex7, HexD, Hex7, HexF),
+ EBITOP(IMM3|B30, "btst", Hex6, Hex3,Hex7, HexC, Hex7, HexE),
+ BITOP(IMM3|B30, "bxor", Hex7,Hex5,Hex7, HexC, Hex7, HexE),
+ TWOOP( "cmp.b",HexA, Hex1, HexC),
+ WTWOP( "cmp.w",Hex1,HexD),
+ UNOP( "daa",Hex0, HexF),
+ UNOP( "das",Hex1, HexF),
+ UNOP( "dec",Hex1, HexA),
+ SOP("divxu"),{RS8, RD16|B30, E}, { Hex5, Hex1, RS8, RD16|B30, E} EOP,
+ SOP("eepmov"),{ E}, {Hex7, HexB, Hex5, HexC, Hex5, Hex9, Hex8, HexF,E} EOP,
+ UNOP( "inc", Hex0, HexA),
+ SOP("jmp"),{RSIND|B30, E}, {Hex5, Hex9, RSIND|B30, Hex0, E} EOP,
+ SOP("jmp"),{ABS16ORREL8SRC, E}, {Hex5, HexA, Hex0, Hex0, ABS16ORREL8SRC, IGNORE,IGNORE,IGNORE,E} EOP,
+ SOP("jmp"),{MEMIND, E}, {Hex5, HexB, MEMIND,IGNORE, E} EOP,
+ SOP("jsr"),{RSIND|B30, E}, {Hex5, HexD, RSIND|B30, Hex0, E} EOP,
+ SOP("jsr"),{ABS16ORREL8SRC, E}, {Hex5, HexE, Hex0, Hex0,
+ ABS16ORREL8SRC,IGNORE,IGNORE,IGNORE, E} EOP,
+ SOP("jsr"),{MEMIND, E}, {Hex5, HexF, MEMIND, IGNORE,E} EOP,
+ SOP("ldc"),{IMM8, CCR, E}, { Hex0, Hex7, IMM8,IGNORE, E} EOP,
+ SOP("ldc"),{RS8, CCR, E}, { Hex0, Hex3, Hex0, RS8, E} EOP,
+ SOP("mov.b"),{RS8, RD8, E}, { Hex0, HexC, RS8, RD8, E} EOP,
+ SOP("mov.b"),{IMM8, RD8, E}, { HexF, RD8, IMM8,IGNORE, E} EOP,
+ SOP("mov.b"),{RSIND|B30,RD8, E}, { Hex6, Hex8, RSIND|B30, RD8, E} EOP,
+ SOP("mov.b"),{DISPSRC,RD8, E}, { Hex6, HexE, DISPREG|B30, RD8,
+ DISPSRC, IGNORE, IGNORE, IGNORE, E} EOP,
+ SOP("mov.b"),{RSINC|B30, RD8, E}, { Hex6, HexC, RSINC|B30, RD8, E} EOP,
+ SOP("mov.b"),{ABS16OR8SRC, RD8, E}, { Hex6, HexA, Hex0, RD8,ABS16OR8SRC,
+ IGNORE,IGNORE,IGNORE,E} EOP,
+ SOP("mov.b"),{ABS8SRC, RD8, E}, { Hex2, RD8, ABS8SRC,IGNORE, E} EOP,
+ SOP("mov.b"),{RS8, RDIND|B30, E}, { Hex6, Hex8, RDIND|B31, RS8, E} EOP,
+ SOP("mov.b"),{RS8, DISPDST, E}, { Hex6, HexE, DISPREG|B31,
+ RS8,DISPDST, IGNORE, IGNORE, IGNORE, E} EOP,
+ SOP("mov.b"),{RS8, RDDEC|B31, E}, { Hex6, HexC, RDDEC|B31, RS8, E} EOP,
+ SOP( "mov.b"),{RS8, ABS16OR8DST, E}, { Hex6, HexA, Hex8, RS8,
+ ABS16OR8DST,IGNORE,IGNORE,IGNORE, E} EOP,
+ SOP( "mov.b"),{RS8, ABS8DST, E}, { Hex3, RS8, ABS8DST,IGNORE, E} EOP,
+ SOP( "mov.w"),{RS16|B30, RD16|B30, E},{ Hex0, HexD, RS16|B30,
+ RD16|B30, E} EOP,
+ SOP("mov.w"),{IMM16, RD16|B30, E}, { Hex7, Hex9, Hex0, RD16|B30,
+ IMM16,IGNORE,IGNORE,IGNORE, E} EOP,
+ SOP("mov.w"),{RSIND|B30,RD16|B30, E},{ Hex6, Hex9, RSIND|B30,
+ RD16|B30, E} EOP,
+ SOP("mov.w"),{DISPSRC,RD16|B30, E}, { Hex6, HexF, DISPREG|B30,
+ RD16|B30, DISPSRC, IGNORE, IGNORE, IGNORE,E} EOP,
+ SOP("mov.w"),{RSINC|B30, RD16|B30, E}, { Hex6, HexD, RSINC|B30,
+ RD16|B30, E}EOP,
+ SOP("mov.w"), {ABS16SRC, RD16|B30, E}, { Hex6, HexB, Hex0,
+ RD16|B30,ABS16SRC,IGNORE,IGNORE,IGNORE, E} EOP,
+SOP("mov.w"), {RS16|B30, RDIND|B30, E},{ Hex6, Hex9, RDIND|B31,
+ RS16|B30, E} EOP,
+SOP("mov.w"), {RS16|B30, DISPDST, E}, { Hex6, HexF, DISPREG|B31,
+ RS16|B30,DISPDST, IGNORE,IGNORE,IGNORE,E} EOP,
+SOP("mov.w"), {RS16|B30, RDDEC|B30, E},{ Hex6, HexD, RDDEC|B31,
+ RS16|B30, E} EOP,
+SOP("mov.w"), {RS16|B30, ABS16DST, E}, { Hex6, HexB, Hex8, RS16|B30,
+ ABS16DST, IGNORE, IGNORE, IGNORE, E} EOP,
+SOP("movfpe"), {ABS16SRC, RD8, E}, { Hex6, HexA, Hex4, RD8,
+ ABS16SRC,IGNORE,IGNORE,IGNORE, E} EOP,
+SOP("movtpe"), {RS8, ABS16DST, E}, { Hex6, HexA, HexC, RS8,
+ ABS16DST,IGNORE,IGNORE,IGNORE,
+ E} EOP,
+SOP("mulxu"), {RS8, RD16|B30, E}, { Hex5, Hex0, RS8, RD16|B30, E} EOP,
+SOP( "neg"), {RS8, E}, { Hex1, Hex7, Hex8, RS8, E} EOP,
+SOP( "nop"), {E}, { Hex0, Hex0, Hex0, Hex0,E} EOP,
+SOP( "not"), {RS8,E}, { Hex1, Hex7, Hex0, RS8,E} EOP,
+TWOOP("or", HexC, Hex1, Hex4),
+SOP( "orc"), {IMM8, CCR,E}, { Hex0, Hex4, IMM8,IGNORE,E} EOP,
+SOP( "pop"), {RS16|B30,E}, { Hex6, HexD, Hex7, RS16|B30,E} EOP,
+SOP( "push"), {RS16|B30,E}, { Hex6, HexD, HexF, RS16|B30,E} EOP,
+ UNOP3( "rotl",Hex1, Hex2,Hex8),
+ UNOP3( "rotr",Hex1, Hex3, Hex8),
+ UNOP3( "rotxl",Hex1, Hex2, Hex0),
+ UNOP3( "rotxr",Hex1, Hex3, Hex0),
+SOP("rte"), {E}, { Hex5, Hex6, Hex7, Hex0,E} EOP,
+SOP("rts"), {E}, { Hex5, Hex4, Hex7, Hex0,E} EOP,
+ UNOP3( "shal", Hex1, Hex0, Hex8),
+ UNOP3( "shar", Hex1, Hex1, Hex8),
+ UNOP3( "shll", Hex1, Hex0, Hex0),
+ UNOP3( "shlr", Hex1, Hex1, Hex0),
+SOP("sleep"), {E}, { Hex0, Hex1, Hex8, Hex0,E} EOP,
+SOP("stc"), {CCR, RD8,E}, { Hex0, Hex2, Hex0, RD8,E} EOP,
+SOP("sub.b"), {RS8,RD8,E}, { Hex1, Hex8, RS8, RD8,E} EOP,
+SOP("sub.w"), {RS16|B30, RD16|B30,E}, {Hex1, Hex9, RS16|B30,RD16|B30,E} EOP,
+SOP("subs"), {KBIT,RD16|B30,E}, { Hex1, HexB, KBIT, RD16|B30,E} EOP,
+ TWOOP("subx",HexB, Hex1, HexE),
+ TWOOP("xor", HexD, Hex1, Hex5),
+SOP("xorc"), {IMM8, CCR,E}, { Hex0, Hex5, IMM8,IGNORE,E} EOP,
+ 0
+};
+#else
+extern struct h8_opcode h8_opcodes[] ;
+#endif
+
+
+
+
diff --git a/gnu/usr.bin/as/opcode/i386.h b/gnu/usr.bin/as/opcode/i386.h
new file mode 100644
index 0000000..cc8fe1c
--- /dev/null
+++ b/gnu/usr.bin/as/opcode/i386.h
@@ -0,0 +1,830 @@
+/* i386-opcode.h -- Intel 80386 opcode table
+ Copyright (C) 1989, 1991, Free Software Foundation.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* $Id: i386.h,v 1.2 1993/10/04 22:53:32 pk Exp $ */
+
+static const template i386_optab[] = {
+
+#define _ None
+/* move instructions */
+{ "mov", 2, 0xa0, _, DW|NoModrm, Disp32, Acc, 0 },
+{ "mov", 2, 0x88, _, DW|Modrm, Reg, Reg|Mem, 0 },
+{ "mov", 2, 0xb0, _, ShortFormW, Imm, Reg, 0 },
+{ "mov", 2, 0xc6, _, W|Modrm, Imm, Reg|Mem, 0 },
+{ "mov", 2, 0x8c, _, D|Modrm, SReg3|SReg2, Reg16|Mem16, 0 },
+/* move to/from control debug registers */
+{ "mov", 2, 0x0f20, _, D|Modrm, Control, Reg32, 0},
+{ "mov", 2, 0x0f21, _, D|Modrm, Debug, Reg32, 0},
+{ "mov", 2, 0x0f24, _, D|Modrm, Test, Reg32, 0},
+
+/* move with sign extend */
+/* "movsbl" & "movsbw" must not be unified into "movsb" to avoid
+ conflict with the "movs" string move instruction. Thus,
+ {"movsb", 2, 0x0fbe, _, ReverseRegRegmem|Modrm, Reg8|Mem, Reg16|Reg32, 0},
+ is not kosher; we must seperate the two instructions. */
+{"movsbl", 2, 0x0fbe, _, ReverseRegRegmem|Modrm, Reg8|Mem, Reg32, 0},
+{"movsbw", 2, 0x660fbe, _, ReverseRegRegmem|Modrm, Reg8|Mem, Reg16, 0},
+{"movswl", 2, 0x0fbf, _, ReverseRegRegmem|Modrm, Reg16|Mem, Reg32, 0},
+
+/* move with zero extend */
+{"movzb", 2, 0x0fb6, _, ReverseRegRegmem|Modrm, Reg8|Mem, Reg16|Reg32, 0},
+{"movzwl", 2, 0x0fb7, _, ReverseRegRegmem|Modrm, Reg16|Mem, Reg32, 0},
+
+/* push instructions */
+{"push", 1, 0x50, _, ShortForm, WordReg,0,0 },
+{"push", 1, 0xff, 0x6, Modrm, WordReg|WordMem, 0, 0 },
+{"push", 1, 0x6a, _, NoModrm, Imm8S, 0, 0},
+{"push", 1, 0x68, _, NoModrm, Imm16|Imm32, 0, 0},
+{"push", 1, 0x06, _, Seg2ShortForm, SReg2,0,0 },
+{"push", 1, 0x0fa0, _, Seg3ShortForm, SReg3,0,0 },
+/* push all */
+{"pusha", 0, 0x60, _, NoModrm, 0, 0, 0 },
+
+/* pop instructions */
+{"pop", 1, 0x58, _, ShortForm, WordReg,0,0 },
+{"pop", 1, 0x8f, 0x0, Modrm, WordReg|WordMem, 0, 0 },
+#define POP_SEG_SHORT 0x7
+{"pop", 1, 0x07, _, Seg2ShortForm, SReg2,0,0 },
+{"pop", 1, 0x0fa1, _, Seg3ShortForm, SReg3,0,0 },
+/* pop all */
+{"popa", 0, 0x61, _, NoModrm, 0, 0, 0 },
+
+/* xchg exchange instructions
+ xchg commutes: we allow both operand orders */
+{"xchg", 2, 0x90, _, ShortForm, WordReg, Acc, 0 },
+{"xchg", 2, 0x90, _, ShortForm, Acc, WordReg, 0 },
+{"xchg", 2, 0x86, _, W|Modrm, Reg, Reg|Mem, 0 },
+{"xchg", 2, 0x86, _, W|Modrm, Reg|Mem, Reg, 0 },
+
+/* in/out from ports */
+{"in", 2, 0xe4, _, W|NoModrm, Imm8, Acc, 0 },
+{"in", 2, 0xec, _, W|NoModrm, InOutPortReg, Acc, 0 },
+{"out", 2, 0xe6, _, W|NoModrm, Acc, Imm8, 0 },
+{"out", 2, 0xee, _, W|NoModrm, Acc, InOutPortReg, 0 },
+
+#if 0
+{"inb", 1, 0xe4, _, NoModrm, Imm8, 0, 0 },
+{"inb", 1, 0xec, _, NoModrm, WordMem, 0, 0 },
+{"inw", 1, 0x66e5, _, NoModrm, Imm8, 0, 0 },
+{"inw", 1, 0x66ed, _, NoModrm, WordMem, 0, 0 },
+{"outb", 1, 0xe6, _, NoModrm, Imm8, 0, 0 },
+{"outb", 1, 0xee, _, NoModrm, WordMem, 0, 0 },
+{"outw", 1, 0x66e7, _, NoModrm, Imm8, 0, 0 },
+{"outw", 1, 0x66ef, _, NoModrm, WordMem, 0, 0 },
+#endif
+
+/* load effective address */
+{"lea", 2, 0x8d, _, Modrm, WordMem, WordReg, 0 },
+
+/* load segment registers from memory */
+{"lds", 2, 0xc5, _, Modrm, Mem, Reg32, 0},
+{"les", 2, 0xc4, _, Modrm, Mem, Reg32, 0},
+{"lfs", 2, 0x0fb4, _, Modrm, Mem, Reg32, 0},
+{"lgs", 2, 0x0fb5, _, Modrm, Mem, Reg32, 0},
+{"lss", 2, 0x0fb2, _, Modrm, Mem, Reg32, 0},
+
+/* flags register instructions */
+{"clc", 0, 0xf8, _, NoModrm, 0, 0, 0},
+{"cld", 0, 0xfc, _, NoModrm, 0, 0, 0},
+{"cli", 0, 0xfa, _, NoModrm, 0, 0, 0},
+{"clts", 0, 0x0f06, _, NoModrm, 0, 0, 0},
+{"cmc", 0, 0xf5, _, NoModrm, 0, 0, 0},
+{"lahf", 0, 0x9f, _, NoModrm, 0, 0, 0},
+{"sahf", 0, 0x9e, _, NoModrm, 0, 0, 0},
+{"pushf", 0, 0x9c, _, NoModrm, 0, 0, 0},
+{"popf", 0, 0x9d, _, NoModrm, 0, 0, 0},
+{"stc", 0, 0xf9, _, NoModrm, 0, 0, 0},
+{"std", 0, 0xfd, _, NoModrm, 0, 0, 0},
+{"sti", 0, 0xfb, _, NoModrm, 0, 0, 0},
+
+{"add", 2, 0x0, _, DW|Modrm, Reg, Reg|Mem, 0},
+{"add", 2, 0x83, 0, Modrm, Imm8S, WordReg|WordMem, 0},
+{"add", 2, 0x4, _, W|NoModrm, Imm, Acc, 0},
+{"add", 2, 0x80, 0, W|Modrm, Imm, Reg|Mem, 0},
+
+{"inc", 1, 0x40, _, ShortForm, WordReg, 0, 0},
+{"inc", 1, 0xfe, 0, W|Modrm, Reg|Mem, 0, 0},
+
+{"sub", 2, 0x28, _, DW|Modrm, Reg, Reg|Mem, 0},
+{"sub", 2, 0x83, 5, Modrm, Imm8S, WordReg|WordMem, 0},
+{"sub", 2, 0x2c, _, W|NoModrm, Imm, Acc, 0},
+{"sub", 2, 0x80, 5, W|Modrm, Imm, Reg|Mem, 0},
+
+{"dec", 1, 0x48, _, ShortForm, WordReg, 0, 0},
+{"dec", 1, 0xfe, 1, W|Modrm, Reg|Mem, 0, 0},
+
+{"sbb", 2, 0x18, _, DW|Modrm, Reg, Reg|Mem, 0},
+{"sbb", 2, 0x83, 3, Modrm, Imm8S, WordReg|WordMem, 0},
+{"sbb", 2, 0x1c, _, W|NoModrm, Imm, Acc, 0},
+{"sbb", 2, 0x80, 3, W|Modrm, Imm, Reg|Mem, 0},
+
+{"cmp", 2, 0x38, _, DW|Modrm, Reg, Reg|Mem, 0},
+{"cmp", 2, 0x83, 7, Modrm, Imm8S, WordReg|WordMem, 0},
+{"cmp", 2, 0x3c, _, W|NoModrm, Imm, Acc, 0},
+{"cmp", 2, 0x80, 7, W|Modrm, Imm, Reg|Mem, 0},
+
+{"test", 2, 0x84, _, W|Modrm, Reg|Mem, Reg, 0},
+{"test", 2, 0x84, _, W|Modrm, Reg, Reg|Mem, 0},
+{"test", 2, 0xa8, _, W|NoModrm, Imm, Acc, 0},
+{"test", 2, 0xf6, 0, W|Modrm, Imm, Reg|Mem, 0},
+
+{"and", 2, 0x20, _, DW|Modrm, Reg, Reg|Mem, 0},
+{"and", 2, 0x83, 4, Modrm, Imm8S, WordReg|WordMem, 0},
+{"and", 2, 0x24, _, W|NoModrm, Imm, Acc, 0},
+{"and", 2, 0x80, 4, W|Modrm, Imm, Reg|Mem, 0},
+
+{"or", 2, 0x08, _, DW|Modrm, Reg, Reg|Mem, 0},
+{"or", 2, 0x83, 1, Modrm, Imm8S, WordReg|WordMem, 0},
+{"or", 2, 0x0c, _, W|NoModrm, Imm, Acc, 0},
+{"or", 2, 0x80, 1, W|Modrm, Imm, Reg|Mem, 0},
+
+{"xor", 2, 0x30, _, DW|Modrm, Reg, Reg|Mem, 0},
+{"xor", 2, 0x83, 6, Modrm, Imm8S, WordReg|WordMem, 0},
+{"xor", 2, 0x34, _, W|NoModrm, Imm, Acc, 0},
+{"xor", 2, 0x80, 6, W|Modrm, Imm, Reg|Mem, 0},
+
+{"adc", 2, 0x10, _, DW|Modrm, Reg, Reg|Mem, 0},
+{"adc", 2, 0x83, 2, Modrm, Imm8S, WordReg|WordMem, 0},
+{"adc", 2, 0x14, _, W|NoModrm, Imm, Acc, 0},
+{"adc", 2, 0x80, 2, W|Modrm, Imm, Reg|Mem, 0},
+
+{"neg", 1, 0xf6, 3, W|Modrm, Reg|Mem, 0, 0},
+{"not", 1, 0xf6, 2, W|Modrm, Reg|Mem, 0, 0},
+
+{"aaa", 0, 0x37, _, NoModrm, 0, 0, 0},
+{"aas", 0, 0x3f, _, NoModrm, 0, 0, 0},
+{"daa", 0, 0x27, _, NoModrm, 0, 0, 0},
+{"das", 0, 0x2f, _, NoModrm, 0, 0, 0},
+{"aad", 0, 0xd50a, _, NoModrm, 0, 0, 0},
+{"aam", 0, 0xd40a, _, NoModrm, 0, 0, 0},
+
+/* conversion insns */
+/* conversion: intel naming */
+{"cbw", 0, 0x6698, _, NoModrm, 0, 0, 0},
+{"cwd", 0, 0x6699, _, NoModrm, 0, 0, 0},
+{"cwde", 0, 0x98, _, NoModrm, 0, 0, 0},
+{"cdq", 0, 0x99, _, NoModrm, 0, 0, 0},
+/* att naming */
+{"cbtw", 0, 0x6698, _, NoModrm, 0, 0, 0},
+{"cwtl", 0, 0x98, _, NoModrm, 0, 0, 0},
+{"cwtd", 0, 0x6699, _, NoModrm, 0, 0, 0},
+{"cltd", 0, 0x99, _, NoModrm, 0, 0, 0},
+
+/* Warning! the mul/imul (opcode 0xf6) must only have 1 operand! They are
+ expanding 64-bit multiplies, and *cannot* be selected to accomplish
+ 'imul %ebx, %eax' (opcode 0x0faf must be used in this case)
+ These multiplies can only be selected with single opearnd forms. */
+{"mul", 1, 0xf6, 4, W|Modrm, Reg|Mem, 0, 0},
+{"imul", 1, 0xf6, 5, W|Modrm, Reg|Mem, 0, 0},
+
+
+
+
+/* imulKludge here is needed to reverse the i.rm.reg & i.rm.regmem fields.
+ These instructions are exceptions: 'imul $2, %eax, %ecx' would put
+ '%eax' in the reg field and '%ecx' in the regmem field if we did not
+ switch them. */
+{"imul", 2, 0x0faf, _, Modrm|ReverseRegRegmem, WordReg|Mem, WordReg, 0},
+{"imul", 3, 0x6b, _, Modrm|ReverseRegRegmem, Imm8S, WordReg|Mem, WordReg},
+{"imul", 3, 0x69, _, Modrm|ReverseRegRegmem, Imm16|Imm32, WordReg|Mem, WordReg},
+/*
+ imul with 2 operands mimicks imul with 3 by puting register both
+ in i.rm.reg & i.rm.regmem fields
+*/
+{"imul", 2, 0x6b, _, Modrm|imulKludge, Imm8S, WordReg, 0},
+{"imul", 2, 0x69, _, Modrm|imulKludge, Imm16|Imm32, WordReg, 0},
+{"div", 1, 0xf6, 6, W|Modrm, Reg|Mem, 0, 0},
+{"div", 2, 0xf6, 6, W|Modrm, Reg|Mem, Acc, 0},
+{"idiv", 1, 0xf6, 7, W|Modrm, Reg|Mem, 0, 0},
+{"idiv", 2, 0xf6, 7, W|Modrm, Reg|Mem, Acc, 0},
+
+{"rol", 2, 0xd0, 0, W|Modrm, Imm1, Reg|Mem, 0},
+{"rol", 2, 0xc0, 0, W|Modrm, Imm8, Reg|Mem, 0},
+{"rol", 2, 0xd2, 0, W|Modrm, ShiftCount, Reg|Mem, 0},
+{"rol", 1, 0xd0, 0, W|Modrm, Reg|Mem, 0, 0},
+
+{"ror", 2, 0xd0, 1, W|Modrm, Imm1, Reg|Mem, 0},
+{"ror", 2, 0xc0, 1, W|Modrm, Imm8, Reg|Mem, 0},
+{"ror", 2, 0xd2, 1, W|Modrm, ShiftCount, Reg|Mem, 0},
+{"ror", 1, 0xd0, 1, W|Modrm, Reg|Mem, 0, 0},
+
+{"rcl", 2, 0xd0, 2, W|Modrm, Imm1, Reg|Mem, 0},
+{"rcl", 2, 0xc0, 2, W|Modrm, Imm8, Reg|Mem, 0},
+{"rcl", 2, 0xd2, 2, W|Modrm, ShiftCount, Reg|Mem, 0},
+{"rcl", 1, 0xd0, 2, W|Modrm, Reg|Mem, 0, 0},
+
+{"rcr", 2, 0xd0, 3, W|Modrm, Imm1, Reg|Mem, 0},
+{"rcr", 2, 0xc0, 3, W|Modrm, Imm8, Reg|Mem, 0},
+{"rcr", 2, 0xd2, 3, W|Modrm, ShiftCount, Reg|Mem, 0},
+{"rcr", 1, 0xd0, 3, W|Modrm, Reg|Mem, 0, 0},
+
+{"sal", 2, 0xd0, 4, W|Modrm, Imm1, Reg|Mem, 0},
+{"sal", 2, 0xc0, 4, W|Modrm, Imm8, Reg|Mem, 0},
+{"sal", 2, 0xd2, 4, W|Modrm, ShiftCount, Reg|Mem, 0},
+{"sal", 1, 0xd0, 4, W|Modrm, Reg|Mem, 0, 0},
+{"shl", 2, 0xd0, 4, W|Modrm, Imm1, Reg|Mem, 0},
+{"shl", 2, 0xc0, 4, W|Modrm, Imm8, Reg|Mem, 0},
+{"shl", 2, 0xd2, 4, W|Modrm, ShiftCount, Reg|Mem, 0},
+{"shl", 1, 0xd0, 4, W|Modrm, Reg|Mem, 0, 0},
+
+{"shld", 3, 0x0fa4, _, Modrm, Imm8, WordReg, WordReg|Mem},
+{"shld", 3, 0x0fa5, _, Modrm, ShiftCount, WordReg, WordReg|Mem},
+
+{"shr", 2, 0xd0, 5, W|Modrm, Imm1, Reg|Mem, 0},
+{"shr", 2, 0xc0, 5, W|Modrm, Imm8, Reg|Mem, 0},
+{"shr", 2, 0xd2, 5, W|Modrm, ShiftCount, Reg|Mem, 0},
+{"shr", 1, 0xd0, 5, W|Modrm, Reg|Mem, 0, 0},
+
+{"shrd", 3, 0x0fac, _, Modrm, Imm8, WordReg, WordReg|Mem},
+{"shrd", 3, 0x0fad, _, Modrm, ShiftCount, WordReg, WordReg|Mem},
+
+{"sar", 2, 0xd0, 7, W|Modrm, Imm1, Reg|Mem, 0},
+{"sar", 2, 0xc0, 7, W|Modrm, Imm8, Reg|Mem, 0},
+{"sar", 2, 0xd2, 7, W|Modrm, ShiftCount, Reg|Mem, 0},
+{"sar", 1, 0xd0, 7, W|Modrm, Reg|Mem, 0, 0},
+
+/* control transfer instructions */
+#define CALL_PC_RELATIVE 0xe8
+{"call", 1, 0xe8, _, JumpDword, Disp32, 0, 0},
+{"call", 1, 0xff, 2, Modrm, Reg|Mem|JumpAbsolute, 0, 0},
+#define CALL_FAR_IMMEDIATE 0x9a
+{"lcall", 2, 0x9a, _, JumpInterSegment, Imm16, Abs32, 0},
+{"lcall", 1, 0xff, 3, Modrm, Mem, 0, 0},
+
+#define JUMP_PC_RELATIVE 0xeb
+{"jmp", 1, 0xeb, _, Jump, Disp, 0, 0},
+{"jmp", 1, 0xff, 4, Modrm, Reg32|Mem|JumpAbsolute, 0, 0},
+#define JUMP_FAR_IMMEDIATE 0xea
+{"ljmp", 2, 0xea, _, JumpInterSegment, Imm16, Imm32, 0},
+{"ljmp", 1, 0xff, 5, Modrm, Mem, 0, 0},
+
+{"ret", 0, 0xc3, _, NoModrm, 0, 0, 0},
+{"ret", 1, 0xc2, _, NoModrm, Imm16, 0, 0},
+{"lret", 0, 0xcb, _, NoModrm, 0, 0, 0},
+{"lret", 1, 0xca, _, NoModrm, Imm16, 0, 0},
+{"enter", 2, 0xc8, _, NoModrm, Imm16, Imm8, 0},
+{"leave", 0, 0xc9, _, NoModrm, 0, 0, 0},
+
+/* conditional jumps */
+{"jo", 1, 0x70, _, Jump, Disp, 0, 0},
+
+{"jno", 1, 0x71, _, Jump, Disp, 0, 0},
+
+{"jb", 1, 0x72, _, Jump, Disp, 0, 0},
+{"jc", 1, 0x72, _, Jump, Disp, 0, 0},
+{"jnae", 1, 0x72, _, Jump, Disp, 0, 0},
+
+{"jnb", 1, 0x73, _, Jump, Disp, 0, 0},
+{"jnc", 1, 0x73, _, Jump, Disp, 0, 0},
+{"jae", 1, 0x73, _, Jump, Disp, 0, 0},
+
+{"je", 1, 0x74, _, Jump, Disp, 0, 0},
+{"jz", 1, 0x74, _, Jump, Disp, 0, 0},
+
+{"jne", 1, 0x75, _, Jump, Disp, 0, 0},
+{"jnz", 1, 0x75, _, Jump, Disp, 0, 0},
+
+{"jbe", 1, 0x76, _, Jump, Disp, 0, 0},
+{"jna", 1, 0x76, _, Jump, Disp, 0, 0},
+
+{"jnbe", 1, 0x77, _, Jump, Disp, 0, 0},
+{"ja", 1, 0x77, _, Jump, Disp, 0, 0},
+
+{"js", 1, 0x78, _, Jump, Disp, 0, 0},
+
+{"jns", 1, 0x79, _, Jump, Disp, 0, 0},
+
+{"jp", 1, 0x7a, _, Jump, Disp, 0, 0},
+{"jpe", 1, 0x7a, _, Jump, Disp, 0, 0},
+
+{"jnp", 1, 0x7b, _, Jump, Disp, 0, 0},
+{"jpo", 1, 0x7b, _, Jump, Disp, 0, 0},
+
+{"jl", 1, 0x7c, _, Jump, Disp, 0, 0},
+{"jnge", 1, 0x7c, _, Jump, Disp, 0, 0},
+
+{"jnl", 1, 0x7d, _, Jump, Disp, 0, 0},
+{"jge", 1, 0x7d, _, Jump, Disp, 0, 0},
+
+{"jle", 1, 0x7e, _, Jump, Disp, 0, 0},
+{"jng", 1, 0x7e, _, Jump, Disp, 0, 0},
+
+{"jnle", 1, 0x7f, _, Jump, Disp, 0, 0},
+{"jg", 1, 0x7f, _, Jump, Disp, 0, 0},
+
+/* these turn into pseudo operations when disp is larger than 8 bits */
+#define IS_JUMP_ON_CX_ZERO(o) \
+ (o == 0x67e3)
+#define IS_JUMP_ON_ECX_ZERO(o) \
+ (o == 0xe3)
+
+{"jcxz", 1, 0x67e3, _, JumpByte, Disp, 0, 0},
+{"jecxz", 1, 0xe3, _, JumpByte, Disp, 0, 0},
+
+#define IS_LOOP_ECX_TIMES(o) \
+ (o == 0xe2 || o == 0xe1 || o == 0xe0)
+
+{"loop", 1, 0xe2, _, JumpByte, Disp, 0, 0},
+
+{"loopz", 1, 0xe1, _, JumpByte, Disp, 0, 0},
+{"loope", 1, 0xe1, _, JumpByte, Disp, 0, 0},
+
+{"loopnz", 1, 0xe0, _, JumpByte, Disp, 0, 0},
+{"loopne", 1, 0xe0, _, JumpByte, Disp, 0, 0},
+
+/* set byte on flag instructions */
+{"seto", 1, 0x0f90, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setno", 1, 0x0f91, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setc", 1, 0x0f92, 0, Modrm, Reg8|Mem, 0, 0},
+{"setb", 1, 0x0f92, 0, Modrm, Reg8|Mem, 0, 0},
+{"setnae", 1, 0x0f92, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setnc", 1, 0x0f93, 0, Modrm, Reg8|Mem, 0, 0},
+{"setnb", 1, 0x0f93, 0, Modrm, Reg8|Mem, 0, 0},
+{"setae", 1, 0x0f93, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"sete", 1, 0x0f94, 0, Modrm, Reg8|Mem, 0, 0},
+{"setz", 1, 0x0f94, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setne", 1, 0x0f95, 0, Modrm, Reg8|Mem, 0, 0},
+{"setnz", 1, 0x0f95, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setbe", 1, 0x0f96, 0, Modrm, Reg8|Mem, 0, 0},
+{"setna", 1, 0x0f96, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setnbe", 1, 0x0f97, 0, Modrm, Reg8|Mem, 0, 0},
+{"seta", 1, 0x0f97, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"sets", 1, 0x0f98, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setns", 1, 0x0f99, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setp", 1, 0x0f9a, 0, Modrm, Reg8|Mem, 0, 0},
+{"setpe", 1, 0x0f9a, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setnp", 1, 0x0f9b, 0, Modrm, Reg8|Mem, 0, 0},
+{"setpo", 1, 0x0f9b, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setl", 1, 0x0f9c, 0, Modrm, Reg8|Mem, 0, 0},
+{"setnge", 1, 0x0f9c, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setnl", 1, 0x0f9d, 0, Modrm, Reg8|Mem, 0, 0},
+{"setge", 1, 0x0f9d, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setle", 1, 0x0f9e, 0, Modrm, Reg8|Mem, 0, 0},
+{"setng", 1, 0x0f9e, 0, Modrm, Reg8|Mem, 0, 0},
+
+{"setnle", 1, 0x0f9f, 0, Modrm, Reg8|Mem, 0, 0},
+{"setg", 1, 0x0f9f, 0, Modrm, Reg8|Mem, 0, 0},
+
+#define IS_STRING_INSTRUCTION(o) \
+ ((o) == 0xa6 || (o) == 0x6c || (o) == 0x6e || (o) == 0x6e || \
+ (o) == 0xac || (o) == 0xa4 || (o) == 0xae || (o) == 0xaa || \
+ (o) == 0xd7)
+
+/* string manipulation */
+{"cmps", 0, 0xa6, _, W|NoModrm, 0, 0, 0},
+{"scmp", 0, 0xa6, _, W|NoModrm, 0, 0, 0},
+{"ins", 0, 0x6c, _, W|NoModrm, 0, 0, 0},
+{"outs", 0, 0x6e, _, W|NoModrm, 0, 0, 0},
+{"lods", 0, 0xac, _, W|NoModrm, 0, 0, 0},
+{"slod", 0, 0xac, _, W|NoModrm, 0, 0, 0},
+{"movs", 0, 0xa4, _, W|NoModrm, 0, 0, 0},
+{"smov", 0, 0xa4, _, W|NoModrm, 0, 0, 0},
+{"scas", 0, 0xae, _, W|NoModrm, 0, 0, 0},
+{"ssca", 0, 0xae, _, W|NoModrm, 0, 0, 0},
+{"stos", 0, 0xaa, _, W|NoModrm, 0, 0, 0},
+{"ssto", 0, 0xaa, _, W|NoModrm, 0, 0, 0},
+{"xlat", 0, 0xd7, _, NoModrm, 0, 0, 0},
+
+/* bit manipulation */
+{"bsf", 2, 0x0fbc, _, Modrm|ReverseRegRegmem, Reg|Mem, Reg, 0},
+{"bsr", 2, 0x0fbd, _, Modrm|ReverseRegRegmem, Reg|Mem, Reg, 0},
+{"bt", 2, 0x0fa3, _, Modrm, Reg, Reg|Mem, 0},
+{"bt", 2, 0x0fba, 4, Modrm, Imm8, Reg|Mem, 0},
+{"btc", 2, 0x0fbb, _, Modrm, Reg, Reg|Mem, 0},
+{"btc", 2, 0x0fba, 7, Modrm, Imm8, Reg|Mem, 0},
+{"btr", 2, 0x0fb3, _, Modrm, Reg, Reg|Mem, 0},
+{"btr", 2, 0x0fba, 6, Modrm, Imm8, Reg|Mem, 0},
+{"bts", 2, 0x0fab, _, Modrm, Reg, Reg|Mem, 0},
+{"bts", 2, 0x0fba, 5, Modrm, Imm8, Reg|Mem, 0},
+
+/* interrupts & op. sys insns */
+/* See i386.c for conversion of 'int $3' into the special int 3 insn. */
+#define INT_OPCODE 0xcd
+#define INT3_OPCODE 0xcc
+{"int", 1, 0xcd, _, NoModrm, Imm8, 0, 0},
+{"int3", 0, 0xcc, _, NoModrm, 0, 0, 0},
+{"into", 0, 0xce, _, NoModrm, 0, 0, 0},
+{"iret", 0, 0xcf, _, NoModrm, 0, 0, 0},
+
+{"boundl", 2, 0x62, _, Modrm, Reg32, Mem, 0},
+{"boundw", 2, 0x6662, _, Modrm, Reg16, Mem, 0},
+
+{"hlt", 0, 0xf4, _, NoModrm, 0, 0, 0},
+{"wait", 0, 0x9b, _, NoModrm, 0, 0, 0},
+/* nop is actually 'xchgl %eax, %eax' */
+{"nop", 0, 0x90, _, NoModrm, 0, 0, 0},
+
+/* protection control */
+{"arpl", 2, 0x63, _, Modrm, Reg16, Reg16|Mem, 0},
+{"lar", 2, 0x0f02, _, Modrm|ReverseRegRegmem, WordReg|Mem, WordReg, 0},
+{"lgdt", 1, 0x0f01, 2, Modrm, Mem, 0, 0},
+{"lidt", 1, 0x0f01, 3, Modrm, Mem, 0, 0},
+{"lldt", 1, 0x0f00, 2, Modrm, WordReg|Mem, 0, 0},
+{"lmsw", 1, 0x0f01, 6, Modrm, WordReg|Mem, 0, 0},
+{"lsl", 2, 0x0f03, _, Modrm|ReverseRegRegmem, WordReg|Mem, WordReg, 0},
+{"ltr", 1, 0x0f00, 3, Modrm, WordReg|Mem, 0, 0},
+
+{"sgdt", 1, 0x0f01, 0, Modrm, Mem, 0, 0},
+{"sidt", 1, 0x0f01, 1, Modrm, Mem, 0, 0},
+{"sldt", 1, 0x0f00, 0, Modrm, WordReg|Mem, 0, 0},
+{"smsw", 1, 0x0f01, 4, Modrm, WordReg|Mem, 0, 0},
+{"str", 1, 0x0f00, 1, Modrm, Reg16|Mem, 0, 0},
+
+{"verr", 1, 0x0f00, 4, Modrm, WordReg|Mem, 0, 0},
+{"verw", 1, 0x0f00, 5, Modrm, WordReg|Mem, 0, 0},
+
+/* floating point instructions */
+
+/* load */
+{"fld", 1, 0xd9c0, _, ShortForm, FloatReg, 0, 0}, /* register */
+{"flds", 1, 0xd9, 0, Modrm, Mem, 0, 0}, /* %st0 <-- mem float */
+{"fildl", 1, 0xdb, 0, Modrm, Mem, 0, 0}, /* %st0 <-- mem word */
+{"fldl", 1, 0xdd, 0, Modrm, Mem, 0, 0}, /* %st0 <-- mem double */
+{"fldl", 1, 0xd9c0, _, ShortForm, FloatReg, 0, 0}, /* register */
+{"filds", 1, 0xdf, 0, Modrm, Mem, 0, 0}, /* %st0 <-- mem dword */
+{"fildq", 1, 0xdf, 5, Modrm, Mem, 0, 0}, /* %st0 <-- mem qword */
+{"fldt", 1, 0xdb, 5, Modrm, Mem, 0, 0}, /* %st0 <-- mem efloat */
+{"fbld", 1, 0xdf, 4, Modrm, Mem, 0, 0}, /* %st0 <-- mem bcd */
+
+/* store (no pop) */
+{"fst", 1, 0xddd0, _, ShortForm, FloatReg, 0, 0}, /* register */
+{"fsts", 1, 0xd9, 2, Modrm, Mem, 0, 0}, /* %st0 --> mem float */
+{"fistl", 1, 0xdb, 2, Modrm, Mem, 0, 0}, /* %st0 --> mem dword */
+{"fstl", 1, 0xdd, 2, Modrm, Mem, 0, 0}, /* %st0 --> mem double */
+{"fstl", 1, 0xddd0, _, ShortForm, FloatReg, 0, 0}, /* register */
+{"fists", 1, 0xdf, 2, Modrm, Mem, 0, 0}, /* %st0 --> mem word */
+
+/* store (with pop) */
+{"fstp", 1, 0xddd8, _, ShortForm, FloatReg, 0, 0}, /* register */
+{"fstps", 1, 0xd9, 3, Modrm, Mem, 0, 0}, /* %st0 --> mem float */
+{"fistpl", 1, 0xdb, 3, Modrm, Mem, 0, 0}, /* %st0 --> mem word */
+{"fstpl", 1, 0xdd, 3, Modrm, Mem, 0, 0}, /* %st0 --> mem double */
+{"fstpl", 1, 0xddd8, _, ShortForm, FloatReg, 0, 0}, /* register */
+{"fistps", 1, 0xdf, 3, Modrm, Mem, 0, 0}, /* %st0 --> mem dword */
+{"fistpq", 1, 0xdf, 7, Modrm, Mem, 0, 0}, /* %st0 --> mem qword */
+{"fstpt", 1, 0xdb, 7, Modrm, Mem, 0, 0}, /* %st0 --> mem efloat */
+{"fbstp", 1, 0xdf, 6, Modrm, Mem, 0, 0}, /* %st0 --> mem bcd */
+
+/* exchange %st<n> with %st0 */
+{"fxch", 1, 0xd9c8, _, ShortForm, FloatReg, 0, 0},
+
+/* comparison (without pop) */
+{"fcom", 1, 0xd8d0, _, ShortForm, FloatReg, 0, 0},
+{"fcoms", 1, 0xd8, 2, Modrm, Mem, 0, 0}, /* compare %st0, mem float */
+{"ficoml", 1, 0xda, 2, Modrm, Mem, 0, 0}, /* compare %st0, mem word */
+{"fcoml", 1, 0xdc, 2, Modrm, Mem, 0, 0}, /* compare %st0, mem double */
+{"fcoml", 1, 0xd8d0, _, ShortForm, FloatReg, 0, 0},
+{"ficoms", 1, 0xde, 2, Modrm, Mem, 0, 0}, /* compare %st0, mem dword */
+
+/* comparison (with pop) */
+{"fcomp", 1, 0xd8d8, _, ShortForm, FloatReg, 0, 0},
+{"fcomps", 1, 0xd8, 3, Modrm, Mem, 0, 0}, /* compare %st0, mem float */
+{"ficompl", 1, 0xda, 3, Modrm, Mem, 0, 0}, /* compare %st0, mem word */
+{"fcompl", 1, 0xdc, 3, Modrm, Mem, 0, 0}, /* compare %st0, mem double */
+{"fcompl", 1, 0xd8d8, _, ShortForm, FloatReg, 0, 0},
+{"ficomps", 1, 0xde, 3, Modrm, Mem, 0, 0}, /* compare %st0, mem dword */
+{"fcompp", 0, 0xded9, _, NoModrm, 0, 0, 0}, /* compare %st0, %st1 & pop twice */
+
+/* unordered comparison (with pop) */
+{"fucom", 1, 0xdde0, _, ShortForm, FloatReg, 0, 0},
+{"fucomp", 1, 0xdde8, _, ShortForm, FloatReg, 0, 0},
+{"fucompp", 0, 0xdae9, _, NoModrm, 0, 0, 0}, /* ucompare %st0, %st1 & pop twice */
+
+{"ftst", 0, 0xd9e4, _, NoModrm, 0, 0, 0}, /* test %st0 */
+{"fxam", 0, 0xd9e5, _, NoModrm, 0, 0, 0}, /* examine %st0 */
+
+/* load constants into %st0 */
+{"fld1", 0, 0xd9e8, _, NoModrm, 0, 0, 0}, /* %st0 <-- 1.0 */
+{"fldl2t", 0, 0xd9e9, _, NoModrm, 0, 0, 0}, /* %st0 <-- log2(10) */
+{"fldl2e", 0, 0xd9ea, _, NoModrm, 0, 0, 0}, /* %st0 <-- log2(e) */
+{"fldpi", 0, 0xd9eb, _, NoModrm, 0, 0, 0}, /* %st0 <-- pi */
+{"fldlg2", 0, 0xd9ec, _, NoModrm, 0, 0, 0}, /* %st0 <-- log10(2) */
+{"fldln2", 0, 0xd9ed, _, NoModrm, 0, 0, 0}, /* %st0 <-- ln(2) */
+{"fldz", 0, 0xd9ee, _, NoModrm, 0, 0, 0}, /* %st0 <-- 0.0 */
+
+/* arithmetic */
+
+/* add */
+{"fadd", 1, 0xd8c0, _, ShortForm, FloatReg, 0, 0},
+{"fadd", 2, 0xd8c0, _, ShortForm|FloatD, FloatReg, FloatAcc, 0},
+{"fadd", 0, 0xdcc1, _, NoModrm, 0, 0, 0}, /* alias for fadd %st, %st(1) */
+{"faddp", 1, 0xdec0, _, ShortForm, FloatReg, 0, 0},
+{"faddp", 2, 0xdac0, _, ShortForm|FloatD, FloatReg, FloatAcc, 0},
+{"faddp", 0, 0xdec1, _, NoModrm, 0, 0, 0}, /* alias for faddp %st, %st(1) */
+{"fadds", 1, 0xd8, 0, Modrm, Mem, 0, 0},
+{"fiaddl", 1, 0xda, 0, Modrm, Mem, 0, 0},
+{"faddl", 1, 0xdc, 0, Modrm, Mem, 0, 0},
+{"fiadds", 1, 0xde, 0, Modrm, Mem, 0, 0},
+
+/* sub */
+/* Note: intel has decided that certain of these operations are reversed
+ in assembler syntax. */
+{"fsub", 1, 0xd8e0, _, ShortForm, FloatReg, 0, 0},
+{"fsub", 2, 0xd8e0, _, ShortForm, FloatReg, FloatAcc, 0},
+#ifdef NON_BROKEN_OPCODES
+{"fsub", 2, 0xdce8, _, ShortForm, FloatAcc, FloatReg, 0},
+#else
+{"fsub", 2, 0xdce0, _, ShortForm, FloatAcc, FloatReg, 0},
+#endif
+{"fsub", 0, 0xdce1, _, NoModrm, 0, 0, 0},
+{"fsubp", 1, 0xdee0, _, ShortForm, FloatReg, 0, 0},
+{"fsubp", 2, 0xdee0, _, ShortForm, FloatReg, FloatAcc, 0},
+#ifdef NON_BROKEN_OPCODES
+{"fsubp", 2, 0xdee8, _, ShortForm, FloatAcc, FloatReg, 0},
+#else
+{"fsubp", 2, 0xdee0, _, ShortForm, FloatAcc, FloatReg, 0},
+#endif
+{"fsubp", 0, 0xdee1, _, NoModrm, 0, 0, 0},
+{"fsubs", 1, 0xd8, 4, Modrm, Mem, 0, 0},
+{"fisubl", 1, 0xda, 4, Modrm, Mem, 0, 0},
+{"fsubl", 1, 0xdc, 4, Modrm, Mem, 0, 0},
+{"fisubs", 1, 0xde, 4, Modrm, Mem, 0, 0},
+
+/* sub reverse */
+{"fsubr", 1, 0xd8e8, _, ShortForm, FloatReg, 0, 0},
+{"fsubr", 2, 0xd8e8, _, ShortForm, FloatReg, FloatAcc, 0},
+#ifdef NON_BROKEN_OPCODES
+{"fsubr", 2, 0xdce0, _, ShortForm, FloatAcc, FloatReg, 0},
+#else
+{"fsubr", 2, 0xdce8, _, ShortForm, FloatAcc, FloatReg, 0},
+#endif
+{"fsubr", 0, 0xdce9, _, NoModrm, 0, 0, 0},
+{"fsubrp", 1, 0xdee8, _, ShortForm, FloatReg, 0, 0},
+{"fsubrp", 2, 0xdee8, _, ShortForm, FloatReg, FloatAcc, 0},
+#ifdef NON_BROKEN_OPCODES
+{"fsubrp", 2, 0xdee0, _, ShortForm, FloatAcc, FloatReg, 0},
+#else
+{"fsubrp", 2, 0xdee8, _, ShortForm, FloatAcc, FloatReg, 0},
+#endif
+{"fsubrp", 0, 0xdee9, _, NoModrm, 0, 0, 0},
+{"fsubrs", 1, 0xd8, 5, Modrm, Mem, 0, 0},
+{"fisubrl", 1, 0xda, 5, Modrm, Mem, 0, 0},
+{"fsubrl", 1, 0xdc, 5, Modrm, Mem, 0, 0},
+{"fisubrs", 1, 0xde, 5, Modrm, Mem, 0, 0},
+
+/* mul */
+{"fmul", 1, 0xd8c8, _, ShortForm, FloatReg, 0, 0},
+{"fmul", 2, 0xd8c8, _, ShortForm|FloatD, FloatReg, FloatAcc, 0},
+{"fmul", 0, 0xdcc9, _, NoModrm, 0, 0, 0},
+{"fmulp", 1, 0xdec8, _, ShortForm, FloatReg, 0, 0},
+{"fmulp", 2, 0xdec8, _, ShortForm|FloatD, FloatReg, FloatAcc, 0},
+{"fmulp", 0, 0xdec9, _, NoModrm, 0, 0, 0},
+{"fmuls", 1, 0xd8, 1, Modrm, Mem, 0, 0},
+{"fimull", 1, 0xda, 1, Modrm, Mem, 0, 0},
+{"fmull", 1, 0xdc, 1, Modrm, Mem, 0, 0},
+{"fimuls", 1, 0xde, 1, Modrm, Mem, 0, 0},
+
+/* div */
+/* Note: intel has decided that certain of these operations are reversed
+ in assembler syntax. */
+{"fdiv", 1, 0xd8f0, _, ShortForm, FloatReg, 0, 0},
+{"fdiv", 2, 0xd8f0, _, ShortForm, FloatReg, FloatAcc, 0},
+#ifdef NON_BROKEN_OPCODES
+{"fdiv", 2, 0xdcf8, _, ShortForm, FloatAcc, FloatReg, 0},
+#else
+{"fdiv", 2, 0xdcf0, _, ShortForm, FloatAcc, FloatReg, 0},
+#endif
+{"fdiv", 0, 0xdcf1, _, NoModrm, 0, 0, 0},
+{"fdivp", 1, 0xdef0, _, ShortForm, FloatReg, 0, 0},
+{"fdivp", 2, 0xdef0, _, ShortForm, FloatReg, FloatAcc, 0},
+#ifdef NON_BROKEN_OPCODES
+{"fdivp", 2, 0xdef8, _, ShortForm, FloatAcc, FloatReg, 0},
+#else
+{"fdivp", 2, 0xdef0, _, ShortForm, FloatAcc, FloatReg, 0},
+#endif
+{"fdivp", 0, 0xdef1, _, NoModrm, 0, 0, 0},
+{"fdivs", 1, 0xd8, 6, Modrm, Mem, 0, 0},
+{"fidivl", 1, 0xda, 6, Modrm, Mem, 0, 0},
+{"fdivl", 1, 0xdc, 6, Modrm, Mem, 0, 0},
+{"fidivs", 1, 0xde, 6, Modrm, Mem, 0, 0},
+
+/* div reverse */
+{"fdivr", 1, 0xd8f8, _, ShortForm, FloatReg, 0, 0},
+{"fdivr", 2, 0xd8f8, _, ShortForm, FloatReg, FloatAcc, 0},
+#ifdef NON_BROKEN_OPCODES
+{"fdivr", 2, 0xdcf0, _, ShortForm, FloatAcc, FloatReg, 0},
+#else
+{"fdivr", 2, 0xdcf8, _, ShortForm, FloatAcc, FloatReg, 0},
+#endif
+{"fdivr", 0, 0xdcf9, _, NoModrm, 0, 0, 0},
+{"fdivrp", 1, 0xdef8, _, ShortForm, FloatReg, 0, 0},
+{"fdivrp", 2, 0xdef8, _, ShortForm, FloatReg, FloatAcc, 0},
+#ifdef NON_BROKEN_OPCODES
+{"fdivrp", 2, 0xdef0, _, ShortForm, FloatAcc, FloatReg, 0},
+#else
+{"fdivrp", 2, 0xdef8, _, ShortForm, FloatAcc, FloatReg, 0},
+#endif
+{"fdivrp", 0, 0xdef9, _, NoModrm, 0, 0, 0},
+{"fdivrs", 1, 0xd8, 7, Modrm, Mem, 0, 0},
+{"fidivrl", 1, 0xda, 7, Modrm, Mem, 0, 0},
+{"fdivrl", 1, 0xdc, 7, Modrm, Mem, 0, 0},
+{"fidivrs", 1, 0xde, 7, Modrm, Mem, 0, 0},
+
+{"f2xm1", 0, 0xd9f0, _, NoModrm, 0, 0, 0},
+{"fyl2x", 0, 0xd9f1, _, NoModrm, 0, 0, 0},
+{"fptan", 0, 0xd9f2, _, NoModrm, 0, 0, 0},
+{"fpatan", 0, 0xd9f3, _, NoModrm, 0, 0, 0},
+{"fxtract", 0, 0xd9f4, _, NoModrm, 0, 0, 0},
+{"fprem1", 0, 0xd9f5, _, NoModrm, 0, 0, 0},
+{"fdecstp", 0, 0xd9f6, _, NoModrm, 0, 0, 0},
+{"fincstp", 0, 0xd9f7, _, NoModrm, 0, 0, 0},
+{"fprem", 0, 0xd9f8, _, NoModrm, 0, 0, 0},
+{"fyl2xp1", 0, 0xd9f9, _, NoModrm, 0, 0, 0},
+{"fsqrt", 0, 0xd9fa, _, NoModrm, 0, 0, 0},
+{"fsincos", 0, 0xd9fb, _, NoModrm, 0, 0, 0},
+{"frndint", 0, 0xd9fc, _, NoModrm, 0, 0, 0},
+{"fscale", 0, 0xd9fd, _, NoModrm, 0, 0, 0},
+{"fsin", 0, 0xd9fe, _, NoModrm, 0, 0, 0},
+{"fcos", 0, 0xd9ff, _, NoModrm, 0, 0, 0},
+
+{"fchs", 0, 0xd9e0, _, NoModrm, 0, 0, 0},
+{"fabs", 0, 0xd9e1, _, NoModrm, 0, 0, 0},
+
+/* processor control */
+{"fninit", 0, 0xdbe3, _, NoModrm, 0, 0, 0},
+{"finit", 0, 0xdbe3, _, NoModrm, 0, 0, 0},
+{"fldcw", 1, 0xd9, 5, Modrm, Mem, 0, 0},
+{"fnstcw", 1, 0xd9, 7, Modrm, Mem, 0, 0},
+{"fstcw", 1, 0xd9, 7, Modrm, Mem, 0, 0},
+{"fnstsw", 1, 0xdfe0, _, NoModrm, Acc, 0, 0},
+{"fnstsw", 1, 0xdd, 7, Modrm, Mem, 0, 0},
+{"fnstsw", 0, 0xdfe0, _, NoModrm, 0, 0, 0},
+{"fstsw", 1, 0xdfe0, _, NoModrm, Acc, 0, 0},
+{"fstsw", 1, 0xdd, 7, Modrm, Mem, 0, 0},
+{"fstsw", 0, 0xdfe0, _, NoModrm, 0, 0, 0},
+{"fnclex", 0, 0xdbe2, _, NoModrm, 0, 0, 0},
+{"fclex", 0, 0xdbe2, _, NoModrm, 0, 0, 0},
+/*
+ We ignore the short format (287) versions of fstenv/fldenv & fsave/frstor
+ instructions; i'm not sure how to add them or how they are different.
+ My 386/387 book offers no details about this.
+*/
+{"fnstenv", 1, 0xd9, 6, Modrm, Mem, 0, 0},
+{"fstenv", 1, 0xd9, 6, Modrm, Mem, 0, 0},
+{"fldenv", 1, 0xd9, 4, Modrm, Mem, 0, 0},
+{"fnsave", 1, 0xdd, 6, Modrm, Mem, 0, 0},
+{"fsave", 1, 0xdd, 6, Modrm, Mem, 0, 0},
+{"frstor", 1, 0xdd, 4, Modrm, Mem, 0, 0},
+
+{"ffree", 1, 0xddc0, _, ShortForm, FloatReg, 0, 0},
+{"fnop", 0, 0xd9d0, _, NoModrm, 0, 0, 0},
+{"fwait", 0, 0x9b, _, NoModrm, 0, 0, 0},
+
+/*
+ opcode prefixes; we allow them as seperate insns too
+ (see prefix table below)
+*/
+{"aword", 0, 0x67, _, NoModrm, 0, 0, 0},
+{"addr16", 0, 0x67, _, NoModrm, 0, 0, 0},
+{"word", 0, 0x66, _, NoModrm, 0, 0, 0},
+{"data16", 0, 0x66, _, NoModrm, 0, 0, 0},
+{"lock", 0, 0xf0, _, NoModrm, 0, 0, 0},
+{"cs", 0, 0x2e, _, NoModrm, 0, 0, 0},
+{"ds", 0, 0x3e, _, NoModrm, 0, 0, 0},
+{"es", 0, 0x26, _, NoModrm, 0, 0, 0},
+{"fs", 0, 0x64, _, NoModrm, 0, 0, 0},
+{"gs", 0, 0x65, _, NoModrm, 0, 0, 0},
+{"ss", 0, 0x36, _, NoModrm, 0, 0, 0},
+{"rep", 0, 0xf3, _, NoModrm, 0, 0, 0},
+{"repe", 0, 0xf3, _, NoModrm, 0, 0, 0},
+{ "repne", 0, 0xf2, _, NoModrm, 0, 0, 0},
+{"repz", 0, 0xf3, _, NoModrm, 0, 0, 0},
+{ "repnz", 0, 0xf2, _, NoModrm, 0, 0, 0},
+
+/* 486 extensions */
+{"bswap", 1, 0x0fc8, _, ShortForm, Reg32,0,0 },
+{"xadd", 2, 0x0fc0, _, DW|Modrm, Reg, Reg|Mem, 0 },
+{"cmpxchg", 2, 0x0fb0, _, DW|Modrm, Reg, Reg|Mem, 0 },
+{"invd", 0, 0x0f08, _, NoModrm, 0, 0, 0},
+{"wbinvd", 0, 0x0f09, _, NoModrm, 0, 0, 0},
+{"invlpg", 1, 0x0f01, 7, Modrm, Mem, 0, 0},
+
+{"", 0, 0, 0, 0, 0, 0, 0} /* sentinal */
+};
+#undef _
+
+static const template *i386_optab_end
+ = i386_optab + sizeof (i386_optab)/sizeof(i386_optab[0]);
+
+/* 386 register table */
+
+static const reg_entry i386_regtab[] = {
+ /* 8 bit regs */
+ {"al", Reg8|Acc, 0}, {"cl", Reg8|ShiftCount, 1}, {"dl", Reg8, 2},
+ {"bl", Reg8, 3},
+ {"ah", Reg8, 4}, {"ch", Reg8, 5}, {"dh", Reg8, 6}, {"bh", Reg8, 7},
+ /* 16 bit regs */
+ {"ax", Reg16|Acc, 0}, {"cx", Reg16, 1}, {"dx", Reg16|InOutPortReg, 2}, {"bx", Reg16, 3},
+ {"sp", Reg16, 4}, {"bp", Reg16, 5}, {"si", Reg16, 6}, {"di", Reg16, 7},
+ /* 32 bit regs */
+ {"eax", Reg32|Acc, 0}, {"ecx", Reg32, 1}, {"edx", Reg32, 2}, {"ebx", Reg32, 3},
+ {"esp", Reg32, 4}, {"ebp", Reg32, 5}, {"esi", Reg32, 6}, {"edi", Reg32, 7},
+ /* segment registers */
+ {"es", SReg2, 0}, {"cs", SReg2, 1}, {"ss", SReg2, 2},
+ {"ds", SReg2, 3}, {"fs", SReg3, 4}, {"gs", SReg3, 5},
+ /* control registers */
+ {"cr0", Control, 0}, {"cr2", Control, 2}, {"cr3", Control, 3},
+ /* debug registers */
+ {"db0", Debug, 0}, {"db1", Debug, 1}, {"db2", Debug, 2},
+ {"db3", Debug, 3}, {"db6", Debug, 6}, {"db7", Debug, 7},
+ /* test registers */
+ {"tr6", Test, 6}, {"tr7", Test, 7},
+ /* float registers */
+ {"st(0)", FloatReg|FloatAcc, 0},
+ {"st", FloatReg|FloatAcc, 0},
+ {"st(1)", FloatReg, 1}, {"st(2)", FloatReg, 2},
+ {"st(3)", FloatReg, 3}, {"st(4)", FloatReg, 4}, {"st(5)", FloatReg, 5},
+ {"st(6)", FloatReg, 6}, {"st(7)", FloatReg, 7}
+};
+
+#define MAX_REG_NAME_SIZE 8 /* for parsing register names from input */
+
+static const reg_entry *i386_regtab_end
+ = i386_regtab + sizeof(i386_regtab)/sizeof(i386_regtab[0]);
+
+/* segment stuff */
+static const seg_entry cs = { "cs", 0x2e };
+static const seg_entry ds = { "ds", 0x3e };
+static const seg_entry ss = { "ss", 0x36 };
+static const seg_entry es = { "es", 0x26 };
+static const seg_entry fs = { "fs", 0x64 };
+static const seg_entry gs = { "gs", 0x65 };
+static const seg_entry null = { "", 0x0 };
+
+/*
+ This table is used to store the default segment register implied by all
+ possible memory addressing modes.
+ It is indexed by the mode & modrm entries of the modrm byte as follows:
+ index = (mode<<3) | modrm;
+*/
+static const seg_entry *one_byte_segment_defaults[] = {
+ /* mode 0 */
+ &ds, &ds, &ds, &ds, &null, &ds, &ds, &ds,
+ /* mode 1 */
+ &ds, &ds, &ds, &ds, &null, &ss, &ds, &ds,
+ /* mode 2 */
+ &ds, &ds, &ds, &ds, &null, &ss, &ds, &ds,
+ /* mode 3 --- not a memory reference; never referenced */
+};
+
+static const seg_entry *two_byte_segment_defaults[] = {
+ /* mode 0 */
+ &ds, &ds, &ds, &ds, &ss, &ds, &ds, &ds,
+ /* mode 1 */
+ &ds, &ds, &ds, &ds, &ss, &ds, &ds, &ds,
+ /* mode 2 */
+ &ds, &ds, &ds, &ds, &ss, &ds, &ds, &ds,
+ /* mode 3 --- not a memory reference; never referenced */
+};
+
+static const prefix_entry i386_prefixtab[] = {
+ { "addr16", 0x67 }, /* address size prefix ==> 16bit addressing
+ * (How is this useful?) */
+#define WORD_PREFIX_OPCODE 0x66
+ { "data16", 0x66 }, /* operand size prefix */
+ { "lock", 0xf0 }, /* bus lock prefix */
+ { "wait", 0x9b }, /* wait for coprocessor */
+ { "cs", 0x2e }, { "ds", 0x3e }, /* segment overrides ... */
+ { "es", 0x26 }, { "fs", 0x64 },
+ { "gs", 0x65 }, { "ss", 0x36 },
+/* REPE & REPNE used to detect rep/repne with a non-string instruction */
+#define REPNE 0xf2
+#define REPE 0xf3
+ { "rep", 0xf3 }, { "repe", 0xf3 }, { "repz", 0xf3 }, /* repeat string instructions */
+ { "repne", 0xf2 }, { "repnz", 0xf2 }
+};
+
+static const prefix_entry *i386_prefixtab_end
+ = i386_prefixtab + sizeof(i386_prefixtab)/sizeof(i386_prefixtab[0]);
+
+/* end of i386-opcode.h */
diff --git a/gnu/usr.bin/as/opcode/i860.h b/gnu/usr.bin/as/opcode/i860.h
new file mode 100644
index 0000000..0842786
--- /dev/null
+++ b/gnu/usr.bin/as/opcode/i860.h
@@ -0,0 +1,495 @@
+/* Table of opcodes for the i860.
+ Copyright (C) 1989 Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler, and GDB, the GNU disassembler.
+
+GAS/GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GAS/GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS or GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined(__STDC__) && !defined(const)
+#define const
+#endif
+
+/*
+ * Structure of an opcode table entry.
+ */
+struct i860_opcode
+{
+ const char *name;
+ unsigned long match; /* Bits that must be set. */
+ unsigned long lose; /* Bits that must not be set. */
+ const char *args;
+ /* Nonzero if this is a possible expand-instruction. */
+ char expand;
+};
+
+enum expand_type
+{
+ E_MOV = 1, E_ADDR, E_U32, E_AND, E_S32, E_DELAY
+};
+
+/*
+ All i860 opcodes are 32 bits, except for the pseudoinstructions
+ and the operations utilizing a 32-bit address expression, an
+ unsigned 32-bit constant, or a signed 32-bit constant.
+ These opcodes are expanded into a two-instruction sequence for
+ any situation where the immediate operand does not fit in 32 bits.
+ In the case of the add and subtract operations the expansion is
+ to a three-instruction sequence (ex: orh, or, adds). In cases
+ where the address is to be relocated, the instruction is
+ expanded to handle the worse case, this could be optimized at
+ the final link if the actual address were known.
+
+ The pseudoinstructions are: mov, fmov, pmov, nop, and fnop.
+ These instructions are implemented as a one or two instruction
+ sequence of other operations.
+
+ The match component is a mask saying which bits must match a
+ particular opcode in order for an instruction to be an instance
+ of that opcode.
+
+ The args component is a string containing one character
+ for each operand of the instruction.
+
+Kinds of operands:
+ # Number used by optimizer. It is ignored.
+ 1 src1 integer register.
+ 2 src2 integer register.
+ d dest register.
+ c ctrlreg control register.
+ i 16 bit immediate.
+ I 16 bit immediate, aligned.
+ 5 5 bit immediate.
+ l lbroff 26 bit PC relative immediate.
+ r sbroff 16 bit PC relative immediate.
+ s split 16 bit immediate.
+ S split 16 bit immediate, aligned.
+ e src1 floating point register.
+ f src2 floating point register.
+ g dest floating point register.
+
+*/
+
+/* The order of the opcodes in this table is significant:
+
+ * The assembler requires that all instances of the same mnemonic must be
+ consecutive. If they aren't, the assembler will bomb at runtime.
+
+ * The disassembler should not care about the order of the opcodes. */
+
+static struct i860_opcode i860_opcodes[] =
+{
+
+/* REG-Format Instructions */
+{ "ld.c", 0x30000000, 0xcc000000, "c,d", 0 }, /* ld.c csrc2,idest */
+{ "ld.b", 0x00000000, 0xfc000000, "1(2),d", 0 }, /* ld.b isrc1(isrc2),idest */
+{ "ld.b", 0x04000000, 0xf8000000, "I(2),d", E_ADDR }, /* ld.b #const(isrc2),idest */
+{ "ld.s", 0x10000000, 0xec000001, "1(2),d", 0 }, /* ld.s isrc1(isrc2),idest */
+{ "ld.s", 0x14000001, 0xe8000000, "I(2),d", E_ADDR }, /* ld.s #const(isrc2),idest */
+{ "ld.l", 0x10000001, 0xec000000, "1(2),d", 0 }, /* ld.l isrc1(isrc2),idest */
+{ "ld.l", 0x14000001, 0xe8000000, "I(2),d", E_ADDR }, /* ld.l #const(isrc2),idest */
+
+{ "st.c", 0x38000000, 0xc4000000, "1,c", 0 }, /* st.c isrc1ni,csrc2 */
+{ "st.b", 0x0c000000, 0xf0000000, "1,S(2)", E_ADDR }, /* st.b isrc1ni,#const(isrc2) */
+{ "st.s", 0x1c000000, 0xe0000000, "1,S(2)", E_ADDR }, /* st.s isrc1ni,#const(isrc2) */
+{ "st.l", 0x1c000001, 0xe0000000, "1,S(2)", E_ADDR }, /* st.l isrc1ni,#const(isrc2) */
+
+{ "ixfr", 0x08000000, 0xf4000000, "1,g", 0 }, /* ixfr isrc1ni,fdest */
+
+{ "fld.l", 0x20000002, 0xdc000001, "1(2),g", 0 }, /* fld.l isrc1(isrc2),fdest */
+{ "fld.l", 0x24000002, 0xd8000001, "i(2),g", E_ADDR }, /* fld.l #const(isrc2),fdest */
+{ "fld.l", 0x20000003, 0xdc000000, "1(2)++,g", 0 }, /* fld.l isrc1(isrc2)++,fdest */
+{ "fld.l", 0x24000003, 0xd8000000, "i(2)++,g", E_ADDR }, /* fld.l #const(isrc2)++,fdest */
+{ "fld.d", 0x20000000, 0xdc000007, "1(2),g", 0 }, /* fld.d isrc1(isrc2),fdest */
+{ "fld.d", 0x24000000, 0xd8000007, "i(2),g", E_ADDR }, /* fld.d #const(isrc2),fdest */
+{ "fld.d", 0x20000001, 0xdc000006, "1(2)++,g", 0 }, /* fld.d isrc1(isrc2)++,fdest */
+{ "fld.d", 0x24000001, 0xd8000006, "i(2)++,g", E_ADDR }, /* fld.d #const(isrc2)++,fdest */
+{ "fld.q", 0x20000004, 0xdc000003, "1(2),g", 0 }, /* fld.q isrc1(isrc2),fdest */
+{ "fld.q", 0x24000004, 0xd8000003, "i(2),g", E_ADDR }, /* fld.q #const(isrc2),fdest */
+{ "fld.q", 0x20000005, 0xdc000002, "1(2)++,g", 0 }, /* fld.q isrc1(isrc2)++,fdest */
+{ "fld.q", 0x24000005, 0xd8000002, "i(2)++,g", E_ADDR }, /* fld.q #const(isrc2)++,fdest */
+
+{ "pfld.l", 0x60000000, 0x9c000003, "1(2),g", 0 }, /* pfld.l isrc1(isrc2),fdest */
+{ "pfld.l", 0x64000000, 0x98000003, "i(2),g", E_ADDR }, /* pfld.l #const(isrc2),fdest */
+{ "pfld.l", 0x60000001, 0x9c000002, "1(2)++,g", 0 }, /* pfld.l isrc1(isrc2)++,fdest */
+{ "pfld.l", 0x64000001, 0x98000002, "i(2)++,g", E_ADDR }, /* pfld.l #const(isrc2)++,fdest */
+{ "pfld.d", 0x60000000, 0x9c000007, "1(2),g", 0 }, /* pfld.d isrc1(isrc2),fdest */
+{ "pfld.d", 0x64000000, 0x98000007, "i(2),g", E_ADDR }, /* pfld.d #const(isrc2),fdest */
+{ "pfld.d", 0x60000001, 0x9c000006, "1(2)++,g", 0 }, /* pfld.d isrc1(isrc2)++,fdest */
+{ "pfld.d", 0x64000001, 0x98000006, "i(2)++,g", E_ADDR }, /* pfld.d #const(isrc2)++,fdest */
+
+{ "fst.l", 0x28000002, 0xd4000001, "g,1(2)", 0 }, /* fst.l fdest,isrc1(isrc2) */
+{ "fst.l", 0x2c000002, 0xd0000001, "g,i(2)", E_ADDR }, /* fst.l fdest,#const(isrc2) */
+{ "fst.l", 0x28000003, 0xd4000000, "g,1(2)++", 0 }, /* fst.l fdest,isrc1(isrc2)++ */
+{ "fst.l", 0x2c000003, 0xd0000000, "g,i(2)++", E_ADDR }, /* fst.l fdest,#const(isrc2)++ */
+{ "fst.d", 0x28000000, 0xd4000007, "g,1(2)", 0 }, /* fst.d fdest,isrc1(isrc2) */
+{ "fst.d", 0x2c000000, 0xd0000007, "g,i(2)", E_ADDR }, /* fst.d fdest,#const(isrc2) */
+{ "fst.d", 0x28000001, 0xd4000006, "g,1(2)++", 0 }, /* fst.d fdest,isrc1(isrc2)++ */
+{ "fst.d", 0x2c000001, 0xd0000006, "g,i(2)++", E_ADDR }, /* fst.d fdest,#const(isrc2)++ */
+{ "fst.q", 0x28000004, 0xd4000003, "g,1(2)", 0 }, /* fst.q fdest,isrc1(isrc2) */
+{ "fst.q", 0x2c000004, 0xd0000003, "g,i(2)", E_ADDR }, /* fst.q fdest,#const(isrc2) */
+{ "fst.q", 0x28000005, 0xd4000002, "g,1(2)++", 0 }, /* fst.q fdest,isrc1(isrc2)++ */
+{ "fst.q", 0x2c000005, 0xd0000002, "g,i(2)++", E_ADDR }, /* fst.q fdest,#const(isrc2)++ */
+
+{ "pst.d", 0x3c000000, 0xc0000007, "g,i(2)", E_ADDR }, /* pst.d fdest,#const(isrc2) */
+{ "pst.d", 0x3c000001, 0xc0000006, "g,i(2)++", E_ADDR }, /* pst.d fdest,#const(isrc2)++ */
+
+{ "addu", 0x80000000, 0x7c000000, "1,2,d", 0 }, /* addu isrc1,isrc2,idest */
+{ "addu", 0x84000000, 0x78000000, "i,2,d", E_S32 }, /* addu #const,isrc2,idest */
+{ "adds", 0x90000000, 0x6c000000, "1,2,d", 0 }, /* adds isrc1,isrc2,idest */
+{ "adds", 0x94000000, 0x68000000, "i,2,d", E_S32 }, /* adds #const,isrc2,idest */
+{ "subu", 0x88000000, 0x74000000, "1,2,d", 0 }, /* subu isrc1,isrc2,idest */
+{ "subu", 0x8c000000, 0x70000000, "i,2,d", E_S32 }, /* subu #const,isrc2,idest */
+{ "subs", 0x98000000, 0x64000000, "1,2,d", 0 }, /* subs isrc1,isrc2,idest */
+{ "subs", 0x9c000000, 0x60000000, "i,2,d", E_S32 }, /* subs #const,isrc2,idest */
+
+{ "shl", 0xa0000000, 0x5c000000, "1,2,d", 0 }, /* shl isrc1,isrc2,idest */
+{ "shl", 0xa4000000, 0x58000000, "i,2,d", 0 }, /* shl #const,isrc2,idest */
+{ "shr", 0xa8000000, 0x54000000, "1,2,d", 0 }, /* shr isrc1,isrc2,idest */
+{ "shr", 0xac000000, 0x50000000, "i,2,d", 0 }, /* shr #const,isrc2,idest */
+{ "shrd", 0xb0000000, 0x4c000000, "1,2,d", 0 }, /* shrd isrc1,isrc2,idest */
+{ "shra", 0xb8000000, 0x44000000, "1,2,d", 0 }, /* shra isrc1,isrc2,idest */
+{ "shra", 0xbc000000, 0x40000000, "i,2,d", 0 }, /* shra #const,isrc2,idest */
+
+{ "mov", 0xa0000000, 0x5c00f800, "2,d", 0 }, /* shl r0,isrc2,idest */
+{ "mov", 0x94000000, 0x69e00000, "i,d", E_MOV }, /* adds #const,r0,idest */
+{ "nop", 0xa0000000, 0x5ffff800, "", 0 }, /* shl r0,r0,r0 */
+{ "fnop", 0xb0000000, 0x4ffff800, "", 0 }, /* shrd r0,r0,r0 */
+
+{ "trap", 0x44000000, 0xb8000000, "1,2,d", 0 }, /* trap isrc1ni,isrc2,idest */
+
+{ "flush", 0x34000000, 0xc81f0001, "i(2)", E_ADDR }, /* flush #const(isrc2) */
+{ "flush", 0x34000001, 0xc81f0000, "i(2)++", E_ADDR }, /* flush #const(isrc2)++ */
+
+{ "and", 0xc0000000, 0x3c000000, "1,2,d", 0 }, /* and isrc1,isrc2,idest */
+{ "and", 0xc4000000, 0x38000000, "i,2,d", E_AND }, /* and #const,isrc2,idest */
+{ "andh", 0xc8000000, 0x34000000, "1,2,d", 0 }, /* andh isrc1,isrc2,idest */
+{ "andh", 0xcc000000, 0x30000000, "i,2,d", 0 }, /* andh #const,isrc2,idest */
+{ "andnot", 0xd0000000, 0x2c000000, "1,2,d", 0 }, /* andnot isrc1,isrc2,idest */
+{ "andnot", 0xd4000000, 0x28000000, "i,2,d", E_U32 }, /* andnot #const,isrc2,idest */
+{ "andnoth", 0xd8000000, 0x24000000, "1,2,d", 0 }, /* andnoth isrc1,isrc2,idest */
+{ "andnoth", 0xdc000000, 0x20000000, "i,2,d", 0 }, /* andnoth #const,isrc2,idest */
+{ "or", 0xe0000000, 0x1c000000, "1,2,d", 0 }, /* or isrc1,isrc2,idest */
+{ "or", 0xe4000000, 0x18000000, "i,2,d", E_U32 }, /* or #const,isrc2,idest */
+{ "orh", 0xe8000000, 0x14000000, "1,2,d", 0 }, /* orh isrc1,isrc2,idest */
+{ "orh", 0xec000000, 0x10000000, "i,2,d", 0 }, /* orh #const,isrc2,idest */
+{ "xor", 0xf0000000, 0x0c000000, "1,2,d", 0 }, /* xor isrc1,isrc2,idest */
+{ "xor", 0xf4000000, 0x08000000, "i,2,d", E_U32 }, /* xor #const,isrc2,idest */
+{ "xorh", 0xf8000000, 0x04000000, "1,2,d", 0 }, /* xorh isrc1,isrc2,idest */
+{ "xorh", 0xfc000000, 0x00000000, "i,2,d", 0 }, /* xorh #const,isrc2,idest */
+
+{ "bte", 0x58000000, 0xa4000000, "1,2,s", 0 }, /* bte isrc1s,isrc2,sbroff */
+{ "bte", 0x5c000000, 0xa0000000, "5,2,s", 0 }, /* bte #const5,isrc2,sbroff */
+{ "btne", 0x50000000, 0xac000000, "1,2,s", 0 }, /* btne isrc1s,isrc2,sbroff */
+{ "btne", 0x54000000, 0xa8000000, "5,2,s", 0 }, /* btne #const5,isrc2,sbroff */
+{ "bla", 0xb4000000, 0x48000000, "1,2,s", E_DELAY }, /* bla isrc1s,isrc2,sbroff */
+{ "bri", 0x40000000, 0xbc000000, "1", E_DELAY }, /* bri isrc1ni */
+
+/* Core Escape Instruction Format */
+{ "lock", 0x4c000001, 0xb000001e, "", 0 }, /* lock set BL in dirbase */
+{ "calli", 0x4c000002, 0xb000001d, "1", E_DELAY }, /* calli isrc1ni */
+{ "intovr", 0x4c000004, 0xb000001b, "", 0 }, /* intovr trap on integer overflow */
+{ "unlock", 0x4c000007, 0xb0000018, "", 0 }, /* unlock clear BL in dirbase */
+
+/* CTRL-Format Instructions */
+{ "br", 0x68000000, 0x94000000, "l", E_DELAY }, /* br lbroff */
+{ "call", 0x6c000000, 0x90000000, "l", E_DELAY }, /* call lbroff */
+{ "bc", 0x70000000, 0x8c000000, "l", 0 }, /* bc lbroff */
+{ "bc.t", 0x74000000, 0x88000000, "l", E_DELAY }, /* bc.t lbroff */
+{ "bnc", 0x78000000, 0x84000000, "l", 0 }, /* bnc lbroff */
+{ "bnc.t", 0x7c000000, 0x80000000, "l", E_DELAY }, /* bnc.t lbroff */
+
+/* Floating Point Escape Instruction Format - pfam.p fsrc1,fsrc2,fdest */
+{ "r2p1.ss", 0x48000400, 0xb40003ff, "e,f,g", 0 },
+{ "r2p1.sd", 0x48000480, 0xb400037f, "e,f,g", 0 },
+{ "r2p1.dd", 0x48000580, 0xb400027f, "e,f,g", 0 },
+{ "r2pt.ss", 0x48000401, 0xb40003fe, "e,f,g", 0 },
+{ "r2pt.sd", 0x48000481, 0xb400037e, "e,f,g", 0 },
+{ "r2pt.dd", 0x48000581, 0xb400027e, "e,f,g", 0 },
+{ "r2ap1.ss", 0x48000402, 0xb40003fd, "e,f,g", 0 },
+{ "r2ap1.sd", 0x48000482, 0xb400037d, "e,f,g", 0 },
+{ "r2ap1.dd", 0x48000582, 0xb400027d, "e,f,g", 0 },
+{ "r2apt.ss", 0x48000403, 0xb40003fc, "e,f,g", 0 },
+{ "r2apt.sd", 0x48000483, 0xb400037c, "e,f,g", 0 },
+{ "r2apt.dd", 0x48000583, 0xb400027c, "e,f,g", 0 },
+{ "i2p1.ss", 0x48000404, 0xb40003fb, "e,f,g", 0 },
+{ "i2p1.sd", 0x48000484, 0xb400037b, "e,f,g", 0 },
+{ "i2p1.dd", 0x48000584, 0xb400027b, "e,f,g", 0 },
+{ "i2pt.ss", 0x48000405, 0xb40003fa, "e,f,g", 0 },
+{ "i2pt.sd", 0x48000485, 0xb400037a, "e,f,g", 0 },
+{ "i2pt.dd", 0x48000585, 0xb400027a, "e,f,g", 0 },
+{ "i2ap1.ss", 0x48000406, 0xb40003f9, "e,f,g", 0 },
+{ "i2ap1.sd", 0x48000486, 0xb4000379, "e,f,g", 0 },
+{ "i2ap1.dd", 0x48000586, 0xb4000279, "e,f,g", 0 },
+{ "i2apt.ss", 0x48000407, 0xb40003f8, "e,f,g", 0 },
+{ "i2apt.sd", 0x48000487, 0xb4000378, "e,f,g", 0 },
+{ "i2apt.dd", 0x48000587, 0xb4000278, "e,f,g", 0 },
+{ "rat1p2.ss", 0x48000408, 0xb40003f7, "e,f,g", 0 },
+{ "rat1p2.sd", 0x48000488, 0xb4000377, "e,f,g", 0 },
+{ "rat1p2.dd", 0x48000588, 0xb4000277, "e,f,g", 0 },
+{ "m12apm.ss", 0x48000409, 0xb40003f6, "e,f,g", 0 },
+{ "m12apm.sd", 0x48000489, 0xb4000376, "e,f,g", 0 },
+{ "m12apm.dd", 0x48000589, 0xb4000276, "e,f,g", 0 },
+{ "ra1p2.ss", 0x4800040a, 0xb40003f5, "e,f,g", 0 },
+{ "ra1p2.sd", 0x4800048a, 0xb4000375, "e,f,g", 0 },
+{ "ra1p2.dd", 0x4800058a, 0xb4000275, "e,f,g", 0 },
+{ "m12ttpa.ss", 0x4800040b, 0xb40003f4, "e,f,g", 0 },
+{ "m12ttpa.sd", 0x4800048b, 0xb4000374, "e,f,g", 0 },
+{ "m12ttpa.dd", 0x4800058b, 0xb4000274, "e,f,g", 0 },
+{ "iat1p2.ss", 0x4800040c, 0xb40003f3, "e,f,g", 0 },
+{ "iat1p2.sd", 0x4800048c, 0xb4000373, "e,f,g", 0 },
+{ "iat1p2.dd", 0x4800058c, 0xb4000273, "e,f,g", 0 },
+{ "m12tpm.ss", 0x4800040d, 0xb40003f2, "e,f,g", 0 },
+{ "m12tpm.sd", 0x4800048d, 0xb4000372, "e,f,g", 0 },
+{ "m12tpm.dd", 0x4800058d, 0xb4000272, "e,f,g", 0 },
+{ "ia1p2.ss", 0x4800040e, 0xb40003f1, "e,f,g", 0 },
+{ "ia1p2.sd", 0x4800048e, 0xb4000371, "e,f,g", 0 },
+{ "ia1p2.dd", 0x4800058e, 0xb4000271, "e,f,g", 0 },
+{ "m12tpa.ss", 0x4800040f, 0xb40003f0, "e,f,g", 0 },
+{ "m12tpa.sd", 0x4800048f, 0xb4000370, "e,f,g", 0 },
+{ "m12tpa.dd", 0x4800058f, 0xb4000270, "e,f,g", 0 },
+
+/* Floating Point Escape Instruction Format - pfsm.p fsrc1,fsrc2,fdest */
+{ "r2s1.ss", 0x48000410, 0xb40003ef, "e,f,g", 0 },
+{ "r2s1.sd", 0x48000490, 0xb400036f, "e,f,g", 0 },
+{ "r2s1.dd", 0x48000590, 0xb400026f, "e,f,g", 0 },
+{ "r2st.ss", 0x48000411, 0xb40003ee, "e,f,g", 0 },
+{ "r2st.sd", 0x48000491, 0xb400036e, "e,f,g", 0 },
+{ "r2st.dd", 0x48000591, 0xb400026e, "e,f,g", 0 },
+{ "r2as1.ss", 0x48000412, 0xb40003ed, "e,f,g", 0 },
+{ "r2as1.sd", 0x48000492, 0xb400036d, "e,f,g", 0 },
+{ "r2as1.dd", 0x48000592, 0xb400026d, "e,f,g", 0 },
+{ "r2ast.ss", 0x48000413, 0xb40003ec, "e,f,g", 0 },
+{ "r2ast.sd", 0x48000493, 0xb400036c, "e,f,g", 0 },
+{ "r2ast.dd", 0x48000593, 0xb400026c, "e,f,g", 0 },
+{ "i2s1.ss", 0x48000414, 0xb40003eb, "e,f,g", 0 },
+{ "i2s1.sd", 0x48000494, 0xb400036b, "e,f,g", 0 },
+{ "i2s1.dd", 0x48000594, 0xb400026b, "e,f,g", 0 },
+{ "i2st.ss", 0x48000415, 0xb40003ea, "e,f,g", 0 },
+{ "i2st.sd", 0x48000495, 0xb400036a, "e,f,g", 0 },
+{ "i2st.dd", 0x48000595, 0xb400026a, "e,f,g", 0 },
+{ "i2as1.ss", 0x48000416, 0xb40003e9, "e,f,g", 0 },
+{ "i2as1.sd", 0x48000496, 0xb4000369, "e,f,g", 0 },
+{ "i2as1.dd", 0x48000596, 0xb4000269, "e,f,g", 0 },
+{ "i2ast.ss", 0x48000417, 0xb40003e8, "e,f,g", 0 },
+{ "i2ast.sd", 0x48000497, 0xb4000368, "e,f,g", 0 },
+{ "i2ast.dd", 0x48000597, 0xb4000268, "e,f,g", 0 },
+{ "rat1s2.ss", 0x48000418, 0xb40003e7, "e,f,g", 0 },
+{ "rat1s2.sd", 0x48000498, 0xb4000367, "e,f,g", 0 },
+{ "rat1s2.dd", 0x48000598, 0xb4000267, "e,f,g", 0 },
+{ "m12asm.ss", 0x48000419, 0xb40003e6, "e,f,g", 0 },
+{ "m12asm.sd", 0x48000499, 0xb4000366, "e,f,g", 0 },
+{ "m12asm.dd", 0x48000599, 0xb4000266, "e,f,g", 0 },
+{ "ra1s2.ss", 0x4800041a, 0xb40003e5, "e,f,g", 0 },
+{ "ra1s2.sd", 0x4800049a, 0xb4000365, "e,f,g", 0 },
+{ "ra1s2.dd", 0x4800059a, 0xb4000265, "e,f,g", 0 },
+{ "m12ttsa.ss", 0x4800041b, 0xb40003e4, "e,f,g", 0 },
+{ "m12ttsa.sd", 0x4800049b, 0xb4000364, "e,f,g", 0 },
+{ "m12ttsa.dd", 0x4800059b, 0xb4000264, "e,f,g", 0 },
+{ "iat1s2.ss", 0x4800041c, 0xb40003e3, "e,f,g", 0 },
+{ "iat1s2.sd", 0x4800049c, 0xb4000363, "e,f,g", 0 },
+{ "iat1s2.dd", 0x4800059c, 0xb4000263, "e,f,g", 0 },
+{ "m12tsm.ss", 0x4800041d, 0xb40003e2, "e,f,g", 0 },
+{ "m12tsm.sd", 0x4800049d, 0xb4000362, "e,f,g", 0 },
+{ "m12tsm.dd", 0x4800059d, 0xb4000262, "e,f,g", 0 },
+{ "ia1s2.ss", 0x4800041e, 0xb40003e1, "e,f,g", 0 },
+{ "ia1s2.sd", 0x4800049e, 0xb4000361, "e,f,g", 0 },
+{ "ia1s2.dd", 0x4800059e, 0xb4000261, "e,f,g", 0 },
+{ "m12tsa.ss", 0x4800041f, 0xb40003e0, "e,f,g", 0 },
+{ "m12tsa.sd", 0x4800049f, 0xb4000360, "e,f,g", 0 },
+{ "m12tsa.dd", 0x4800059f, 0xb4000260, "e,f,g", 0 },
+
+/* Floating Point Escape Instruction Format - pfmam.p fsrc1,fsrc2,fdest */
+{ "mr2p1.ss", 0x48000000, 0xb40007ff, "e,f,g", 0 },
+{ "mr2p1.sd", 0x48000080, 0xb400077f, "e,f,g", 0 },
+{ "mr2p1.dd", 0x48000180, 0xb400067f, "e,f,g", 0 },
+{ "mr2pt.ss", 0x48000001, 0xb40007fe, "e,f,g", 0 },
+{ "mr2pt.sd", 0x48000081, 0xb400077e, "e,f,g", 0 },
+{ "mr2pt.dd", 0x48000181, 0xb400067e, "e,f,g", 0 },
+{ "mr2mp1.ss", 0x48000002, 0xb40007fd, "e,f,g", 0 },
+{ "mr2mp1.sd", 0x48000082, 0xb400077d, "e,f,g", 0 },
+{ "mr2mp1.dd", 0x48000182, 0xb400067d, "e,f,g", 0 },
+{ "mr2mpt.ss", 0x48000003, 0xb40007fc, "e,f,g", 0 },
+{ "mr2mpt.sd", 0x48000083, 0xb400077c, "e,f,g", 0 },
+{ "mr2mpt.dd", 0x48000183, 0xb400067c, "e,f,g", 0 },
+{ "mi2p1.ss", 0x48000004, 0xb40007fb, "e,f,g", 0 },
+{ "mi2p1.sd", 0x48000084, 0xb400077b, "e,f,g", 0 },
+{ "mi2p1.dd", 0x48000184, 0xb400067b, "e,f,g", 0 },
+{ "mi2pt.ss", 0x48000005, 0xb40007fa, "e,f,g", 0 },
+{ "mi2pt.sd", 0x48000085, 0xb400077a, "e,f,g", 0 },
+{ "mi2pt.dd", 0x48000185, 0xb400067a, "e,f,g", 0 },
+{ "mi2mp1.ss", 0x48000006, 0xb40007f9, "e,f,g", 0 },
+{ "mi2mp1.sd", 0x48000086, 0xb4000779, "e,f,g", 0 },
+{ "mi2mp1.dd", 0x48000186, 0xb4000679, "e,f,g", 0 },
+{ "mi2mpt.ss", 0x48000007, 0xb40007f8, "e,f,g", 0 },
+{ "mi2mpt.sd", 0x48000087, 0xb4000778, "e,f,g", 0 },
+{ "mi2mpt.dd", 0x48000187, 0xb4000678, "e,f,g", 0 },
+{ "mrmt1p2.ss", 0x48000008, 0xb40007f7, "e,f,g", 0 },
+{ "mrmt1p2.sd", 0x48000088, 0xb4000777, "e,f,g", 0 },
+{ "mrmt1p2.dd", 0x48000188, 0xb4000677, "e,f,g", 0 },
+{ "mm12mpm.ss", 0x48000009, 0xb40007f6, "e,f,g", 0 },
+{ "mm12mpm.sd", 0x48000089, 0xb4000776, "e,f,g", 0 },
+{ "mm12mpm.dd", 0x48000189, 0xb4000676, "e,f,g", 0 },
+{ "mrm1p2.ss", 0x4800000a, 0xb40007f5, "e,f,g", 0 },
+{ "mrm1p2.sd", 0x4800008a, 0xb4000775, "e,f,g", 0 },
+{ "mrm1p2.dd", 0x4800018a, 0xb4000675, "e,f,g", 0 },
+{ "mm12ttpm.ss",0x4800000b, 0xb40007f4, "e,f,g", 0 },
+{ "mm12ttpm.sd",0x4800008b, 0xb4000774, "e,f,g", 0 },
+{ "mm12ttpm.dd",0x4800018b, 0xb4000674, "e,f,g", 0 },
+{ "mimt1p2.ss", 0x4800000c, 0xb40007f3, "e,f,g", 0 },
+{ "mimt1p2.sd", 0x4800008c, 0xb4000773, "e,f,g", 0 },
+{ "mimt1p2.dd", 0x4800018c, 0xb4000673, "e,f,g", 0 },
+{ "mm12tpm.ss", 0x4800000d, 0xb40007f2, "e,f,g", 0 },
+{ "mm12tpm.sd", 0x4800008d, 0xb4000772, "e,f,g", 0 },
+{ "mm12tpm.dd", 0x4800018d, 0xb4000672, "e,f,g", 0 },
+{ "mim1p2.ss", 0x4800000e, 0xb40007f1, "e,f,g", 0 },
+{ "mim1p2.sd", 0x4800008e, 0xb4000771, "e,f,g", 0 },
+{ "mim1p2.dd", 0x4800018e, 0xb4000671, "e,f,g", 0 },
+
+/* Floating Point Escape Instruction Format - pfmsm.p fsrc1,fsrc2,fdest */
+{ "mr2s1.ss", 0x48000010, 0xb40007ef, "e,f,g", 0 },
+{ "mr2s1.sd", 0x48000090, 0xb400076f, "e,f,g", 0 },
+{ "mr2s1.dd", 0x48000190, 0xb400066f, "e,f,g", 0 },
+{ "mr2st.ss", 0x48000011, 0xb40007ee, "e,f,g", 0 },
+{ "mr2st.sd", 0x48000091, 0xb400076e, "e,f,g", 0 },
+{ "mr2st.dd", 0x48000191, 0xb400066e, "e,f,g", 0 },
+{ "mr2ms1.ss", 0x48000012, 0xb40007ed, "e,f,g", 0 },
+{ "mr2ms1.sd", 0x48000092, 0xb400076d, "e,f,g", 0 },
+{ "mr2ms1.dd", 0x48000192, 0xb400066d, "e,f,g", 0 },
+{ "mr2mst.ss", 0x48000013, 0xb40007ec, "e,f,g", 0 },
+{ "mr2mst.sd", 0x48000093, 0xb400076c, "e,f,g", 0 },
+{ "mr2mst.dd", 0x48000193, 0xb400066c, "e,f,g", 0 },
+{ "mi2s1.ss", 0x48000014, 0xb40007eb, "e,f,g", 0 },
+{ "mi2s1.sd", 0x48000094, 0xb400076b, "e,f,g", 0 },
+{ "mi2s1.dd", 0x48000194, 0xb400066b, "e,f,g", 0 },
+{ "mi2st.ss", 0x48000015, 0xb40007ea, "e,f,g", 0 },
+{ "mi2st.sd", 0x48000095, 0xb400076a, "e,f,g", 0 },
+{ "mi2st.dd", 0x48000195, 0xb400066a, "e,f,g", 0 },
+{ "mi2ms1.ss", 0x48000016, 0xb40007e9, "e,f,g", 0 },
+{ "mi2ms1.sd", 0x48000096, 0xb4000769, "e,f,g", 0 },
+{ "mi2ms1.dd", 0x48000196, 0xb4000669, "e,f,g", 0 },
+{ "mi2mst.ss", 0x48000017, 0xb40007e8, "e,f,g", 0 },
+{ "mi2mst.sd", 0x48000097, 0xb4000768, "e,f,g", 0 },
+{ "mi2mst.dd", 0x48000197, 0xb4000668, "e,f,g", 0 },
+{ "mrmt1s2.ss", 0x48000018, 0xb40007e7, "e,f,g", 0 },
+{ "mrmt1s2.sd", 0x48000098, 0xb4000767, "e,f,g", 0 },
+{ "mrmt1s2.dd", 0x48000198, 0xb4000667, "e,f,g", 0 },
+{ "mm12msm.ss", 0x48000019, 0xb40007e6, "e,f,g", 0 },
+{ "mm12msm.sd", 0x48000099, 0xb4000766, "e,f,g", 0 },
+{ "mm12msm.dd", 0x48000199, 0xb4000666, "e,f,g", 0 },
+{ "mrm1s2.ss", 0x4800001a, 0xb40007e5, "e,f,g", 0 },
+{ "mrm1s2.sd", 0x4800009a, 0xb4000765, "e,f,g", 0 },
+{ "mrm1s2.dd", 0x4800019a, 0xb4000665, "e,f,g", 0 },
+{ "mm12ttsm.ss",0x4800001b, 0xb40007e4, "e,f,g", 0 },
+{ "mm12ttsm.sd",0x4800009b, 0xb4000764, "e,f,g", 0 },
+{ "mm12ttsm.dd",0x4800019b, 0xb4000664, "e,f,g", 0 },
+{ "mimt1s2.ss", 0x4800001c, 0xb40007e3, "e,f,g", 0 },
+{ "mimt1s2.sd", 0x4800009c, 0xb4000763, "e,f,g", 0 },
+{ "mimt1s2.dd", 0x4800019c, 0xb4000663, "e,f,g", 0 },
+{ "mm12tsm.ss", 0x4800001d, 0xb40007e2, "e,f,g", 0 },
+{ "mm12tsm.sd", 0x4800009d, 0xb4000762, "e,f,g", 0 },
+{ "mm12tsm.dd", 0x4800019d, 0xb4000662, "e,f,g", 0 },
+{ "mim1s2.ss", 0x4800001e, 0xb40007e1, "e,f,g", 0 },
+{ "mim1s2.sd", 0x4800009e, 0xb4000761, "e,f,g", 0 },
+{ "mim1s2.dd", 0x4800019e, 0xb4000661, "e,f,g", 0 },
+
+
+{ "fmul.ss", 0x48000020, 0xb40007df, "e,f,g", 0 }, /* fmul.p fsrc1,fsrc2,fdest */
+{ "fmul.sd", 0x480000a0, 0xb400075f, "e,f,g", 0 }, /* fmul.p fsrc1,fsrc2,fdest */
+{ "fmul.dd", 0x480001a0, 0xb400065f, "e,f,g", 0 }, /* fmul.p fsrc1,fsrc2,fdest */
+{ "pfmul.ss", 0x48000420, 0xb40003df, "e,f,g", 0 }, /* pfmul.p fsrc1,fsrc2,fdest */
+{ "pfmul.sd", 0x480004a0, 0xb400035f, "e,f,g", 0 }, /* pfmul.p fsrc1,fsrc2,fdest */
+{ "pfmul.dd", 0x480005a0, 0xb400025f, "e,f,g", 0 }, /* pfmul.p fsrc1,fsrc2,fdest */
+{ "pfmul3.dd", 0x480005a4, 0xb400025b, "e,f,g", 0 }, /* pfmul3.p fsrc1,fsrc2,fdest */
+{ "fmlow.dd", 0x480001a1, 0xb400065e, "e,f,g", 0 }, /* fmlow.dd fsrc1,fsrc2,fdest */
+{ "frcp.ss", 0x48000022, 0xb40007dd, "f,g", 0 }, /* frcp.p fsrc2,fdest */
+{ "frcp.sd", 0x480000a2, 0xb400075d, "f,g", 0 }, /* frcp.p fsrc2,fdest */
+{ "frcp.dd", 0x480001a2, 0xb400065d, "f,g", 0 }, /* frcp.p fsrc2,fdest */
+{ "frsqr.ss", 0x48000023, 0xb40007dc, "f,g", 0 }, /* frsqr.p fsrc2,fdest */
+{ "frsqr.sd", 0x480000a3, 0xb400075c, "f,g", 0 }, /* frsqr.p fsrc2,fdest */
+{ "frsqr.dd", 0x480001a3, 0xb400065c, "f,g", 0 }, /* frsqr.p fsrc2,fdest */
+{ "fadd.ss", 0x48000030, 0xb40007cf, "e,f,g", 0 }, /* fadd.p fsrc1,fsrc2,fdest */
+{ "fadd.sd", 0x480000b0, 0xb400074f, "e,f,g", 0 }, /* fadd.p fsrc1,fsrc2,fdest */
+{ "fadd.dd", 0x480001b0, 0xb400064f, "e,f,g", 0 }, /* fadd.p fsrc1,fsrc2,fdest */
+{ "pfadd.ss", 0x48000430, 0xb40003cf, "e,f,g", 0 }, /* pfadd.p fsrc1,fsrc2,fdest */
+{ "pfadd.sd", 0x480004b0, 0xb400034f, "e,f,g", 0 }, /* pfadd.p fsrc1,fsrc2,fdest */
+{ "pfadd.dd", 0x480005b0, 0xb400024f, "e,f,g", 0 }, /* pfadd.p fsrc1,fsrc2,fdest */
+{ "fsub.ss", 0x48000031, 0xb40007ce, "e,f,g", 0 }, /* fsub.p fsrc1,fsrc2,fdest */
+{ "fsub.sd", 0x480000b1, 0xb400074e, "e,f,g", 0 }, /* fsub.p fsrc1,fsrc2,fdest */
+{ "fsub.dd", 0x480001b1, 0xb400064e, "e,f,g", 0 }, /* fsub.p fsrc1,fsrc2,fdest */
+{ "pfsub.ss", 0x48000431, 0xb40003ce, "e,f,g", 0 }, /* pfsub.p fsrc1,fsrc2,fdest */
+{ "pfsub.sd", 0x480004b1, 0xb400034e, "e,f,g", 0 }, /* pfsub.p fsrc1,fsrc2,fdest */
+{ "pfsub.dd", 0x480005b1, 0xb400024e, "e,f,g", 0 }, /* pfsub.p fsrc1,fsrc2,fdest */
+{ "fix.ss", 0x48000032, 0xb40007cd, "e,g", 0 }, /* fix.p fsrc1,fdest */
+{ "fix.sd", 0x480000b2, 0xb400074d, "e,g", 0 }, /* fix.p fsrc1,fdest */
+{ "fix.dd", 0x480001b2, 0xb400064d, "e,g", 0 }, /* fix.p fsrc1,fdest */
+{ "pfix.ss", 0x48000432, 0xb40003cd, "e,g", 0 }, /* pfix.p fsrc1,fdest */
+{ "pfix.sd", 0x480004b2, 0xb400034d, "e,g", 0 }, /* pfix.p fsrc1,fdest */
+{ "pfix.dd", 0x480005b2, 0xb400024d, "e,g", 0 }, /* pfix.p fsrc1,fdest */
+{ "famov.ss", 0x48000033, 0xb40007cc, "e,g", 0 }, /* famov.p fsrc1,fdest */
+{ "famov.ds", 0x48000133, 0xb40006cc, "e,g", 0 }, /* famov.p fsrc1,fdest */
+{ "famov.sd", 0x480000b3, 0xb400074c, "e,g", 0 }, /* famov.p fsrc1,fdest */
+{ "famov.dd", 0x480001b3, 0xb400064c, "e,g", 0 }, /* famov.p fsrc1,fdest */
+{ "pfamov.ss", 0x48000433, 0xb40003cc, "e,g", 0 }, /* pfamov.p fsrc1,fdest */
+{ "pfamov.ds", 0x48000533, 0xb40002cc, "e,g", 0 }, /* pfamov.p fsrc1,fdest */
+{ "pfamov.sd", 0x480004b3, 0xb400034c, "e,g", 0 }, /* pfamov.p fsrc1,fdest */
+{ "pfamov.dd", 0x480005b3, 0xb400024c, "e,g", 0 }, /* pfamov.p fsrc1,fdest */
+/* pfgt has R bit cleared; pfle has R bit set */
+{ "pfgt.ss", 0x48000434, 0xb40003cb, "e,f,g", 0 }, /* pfgt.p fsrc1,fsrc2,fdest */
+{ "pfgt.sd", 0x48000434, 0xb40003cb, "e,f,g", 0 }, /* pfgt.p fsrc1,fsrc2,fdest */
+{ "pfgt.dd", 0x48000534, 0xb40002cb, "e,f,g", 0 }, /* pfgt.p fsrc1,fsrc2,fdest */
+/* pfgt has R bit cleared; pfle has R bit set */
+{ "pfle.ss", 0x480004b4, 0xb400034b, "e,f,g", 0 }, /* pfle.p fsrc1,fsrc2,fdest */
+{ "pfle.sd", 0x480004b4, 0xb400034b, "e,f,g", 0 }, /* pfle.p fsrc1,fsrc2,fdest */
+{ "pfle.dd", 0x480005b4, 0xb400024b, "e,f,g", 0 }, /* pfle.p fsrc1,fsrc2,fdest */
+{ "ftrunc.ss", 0x4800003a, 0xb40007c5, "e,g", 0 }, /* ftrunc.p fsrc1,fdest */
+{ "ftrunc.sd", 0x480000ba, 0xb4000745, "e,g", 0 }, /* ftrunc.p fsrc1,fdest */
+{ "ftrunc.dd", 0x480001ba, 0xb4000645, "e,g", 0 }, /* ftrunc.p fsrc1,fdest */
+{ "pftrunc.ss", 0x4800043a, 0xb40003c5, "e,g", 0 }, /* pftrunc.p fsrc1,fdest */
+{ "pftrunc.sd", 0x480004ba, 0xb4000345, "e,g", 0 }, /* pftrunc.p fsrc1,fdest */
+{ "pftrunc.dd", 0x480005ba, 0xb4000245, "e,g", 0 }, /* pftrunc.p fsrc1,fdest */
+{ "fxfr", 0x48000040, 0xb40007bf, "e,d", 0 }, /* fxfr fsrc1,idest */
+{ "fiadd.ss", 0x48000049, 0xb40007b6, "e,f,g", 0 }, /* fiadd.w fsrc1,fsrc2,fdest */
+{ "fiadd.dd", 0x480001c9, 0xb4000636, "e,f,g", 0 }, /* fiadd.w fsrc1,fsrc2,fdest */
+{ "pfiadd.ss", 0x48000449, 0xb40003b6, "e,f,g", 0 }, /* pfiadd.w fsrc1,fsrc2,fdest */
+{ "pfiadd.dd", 0x480005c9, 0xb4000236, "e,f,g", 0 }, /* pfiadd.w fsrc1,fsrc2,fdest */
+{ "fisub.ss", 0x4800004d, 0xb40007b2, "e,f,g", 0 }, /* fisub.w fsrc1,fsrc2,fdest */
+{ "fisub.dd", 0x480001cd, 0xb4000632, "e,f,g", 0 }, /* fisub.w fsrc1,fsrc2,fdest */
+{ "pfisub.ss", 0x4800044d, 0xb40003b2, "e,f,g", 0 }, /* pfisub.w fsrc1,fsrc2,fdest */
+{ "pfisub.dd", 0x480005cd, 0xb4000232, "e,f,g", 0 }, /* pfisub.w fsrc1,fsrc2,fdest */
+{ "fzchkl", 0x48000057, 0xb40007a8, "e,f,g", 0 }, /* fzchkl fsrc1,fsrc2,fdest */
+{ "pfzchkl", 0x48000457, 0xb40003a8, "e,f,g", 0 }, /* pfzchkl fsrc1,fsrc2,fdest */
+{ "fzchks", 0x4800005f, 0xb40007a0, "e,f,g", 0 }, /* fzchks fsrc1,fsrc2,fdest */
+{ "pfzchks", 0x4800045f, 0xb40003a0, "e,f,g", 0 }, /* pfzchks fsrc1,fsrc2,fdest */
+{ "faddp", 0x48000050, 0xb40007af, "e,f,g", 0 }, /* faddp fsrc1,fsrc2,fdest */
+{ "pfaddp", 0x48000450, 0xb40003af, "e,f,g", 0 }, /* pfaddp fsrc1,fsrc2,fdest */
+{ "faddz", 0x48000051, 0xb40007ae, "e,f,g", 0 }, /* faddz fsrc1,fsrc2,fdest */
+{ "pfaddz", 0x48000451, 0xb40003ae, "e,f,g", 0 }, /* pfaddz fsrc1,fsrc2,fdest */
+{ "form", 0x4800005a, 0xb40007a5, "e,g", 0 }, /* form fsrc1,fdest */
+{ "pform", 0x4800045a, 0xb40003a5, "e,g", 0 }, /* pform fsrc1,fdest */
+
+/* Floating point pseudo-instructions */
+{ "fmov.ss", 0x48000049, 0xb7e007b6, "e,g", 0 }, /* fiadd.ss fsrc1,f0,fdest */
+{ "fmov.dd", 0x480001c9, 0xb7e00636, "e,g", 0 }, /* fiadd.dd fsrc1,f0,fdest */
+{ "fmov.sd", 0x480000b0, 0xb7e0074f, "e,g", 0 }, /* fadd.sd fsrc1,f0,fdest */
+{ "fmov.ds", 0x48000130, 0xb7e006cf, "e,g", 0 }, /* fadd.ds fsrc1,f0,fdest */
+{ "pfmov.ds", 0x48000530, 0xb73002cf, "e,g", 0 }, /* pfadd.ds fsrc1,f0,fdest */
+{ "pfmov.dd", 0x480005c9, 0xb7e00236, "e,g", 0 }, /* pfiadd.dd fsrc1,f0,fdest */
+
+
+};
+
+#define NUMOPCODES ((sizeof i860_opcodes)/(sizeof i860_opcodes[0]))
+
+
diff --git a/gnu/usr.bin/as/opcode/i960.h b/gnu/usr.bin/as/opcode/i960.h
new file mode 100644
index 0000000..e9c8cb4
--- /dev/null
+++ b/gnu/usr.bin/as/opcode/i960.h
@@ -0,0 +1,434 @@
+/* Basic 80960 instruction formats.
+ *
+ * The 'COJ' instructions are actually COBR instructions with the 'b' in
+ * the mnemonic replaced by a 'j'; they are ALWAYS "de-optimized" if necessary:
+ * if the displacement will not fit in 13 bits, the assembler will replace them
+ * with the corresponding compare and branch instructions.
+ *
+ * All of the 'MEMn' instructions are the same format; the 'n' in the name
+ * indicates the default index scale factor (the size of the datum operated on).
+ *
+ * The FBRA formats are not actually an instruction format. They are the
+ * "convenience directives" for branching on floating-point comparisons,
+ * each of which generates 2 instructions (a 'bno' and one other branch).
+ *
+ * The CALLJ format is not actually an instruction format. It indicates that
+ * the instruction generated (a CTRL-format 'call') should have its relocation
+ * specially flagged for link-time replacement with a 'bal' or 'calls' if
+ * appropriate.
+ */
+
+/* $Id: i960.h,v 1.1 1993/10/02 21:00:44 pk Exp $ */
+
+#define CTRL 0
+#define COBR 1
+#define COJ 2
+#define REG 3
+#define MEM1 4
+#define MEM2 5
+#define MEM4 6
+#define MEM8 7
+#define MEM12 8
+#define MEM16 9
+#define FBRA 10
+#define CALLJ 11
+
+/* Masks for the mode bits in REG format instructions */
+#define M1 0x0800
+#define M2 0x1000
+#define M3 0x2000
+
+/* Generate the 12-bit opcode for a REG format instruction by placing the
+ * high 8 bits in instruction bits 24-31, the low 4 bits in instruction bits
+ * 7-10.
+ */
+
+#define REG_OPC(opc) ((opc & 0xff0) << 20) | ((opc & 0xf) << 7)
+
+/* Generate a template for a REG format instruction: place the opcode bits
+ * in the appropriate fields and OR in mode bits for the operands that will not
+ * be used. I.e.,
+ * set m1=1, if src1 will not be used
+ * set m2=1, if src2 will not be used
+ * set m3=1, if dst will not be used
+ *
+ * Setting the "unused" mode bits to 1 speeds up instruction execution(!).
+ * The information is also useful to us because some 1-operand REG instructions
+ * use the src1 field, others the dst field; and some 2-operand REG instructions
+ * use src1/src2, others src1/dst. The set mode bits enable us to distinguish.
+ */
+#define R_0(opc) ( REG_OPC(opc) | M1 | M2 | M3 ) /* No operands */
+#define R_1(opc) ( REG_OPC(opc) | M2 | M3 ) /* 1 operand: src1 */
+#define R_1D(opc) ( REG_OPC(opc) | M1 | M2 ) /* 1 operand: dst */
+#define R_2(opc) ( REG_OPC(opc) | M3 ) /* 2 ops: src1/src2 */
+#define R_2D(opc) ( REG_OPC(opc) | M2 ) /* 2 ops: src1/dst */
+#define R_3(opc) ( REG_OPC(opc) ) /* 3 operands */
+
+/* DESCRIPTOR BYTES FOR REGISTER OPERANDS
+ *
+ * Interpret names as follows:
+ * R: global or local register only
+ * RS: global, local, or (if target allows) special-function register only
+ * RL: global or local register, or integer literal
+ * RSL: global, local, or (if target allows) special-function register;
+ * or integer literal
+ * F: global, local, or floating-point register
+ * FL: global, local, or floating-point register; or literal (including
+ * floating point)
+ *
+ * A number appended to a name indicates that registers must be aligned,
+ * as follows:
+ * 2: register number must be multiple of 2
+ * 4: register number must be multiple of 4
+ */
+
+#define SFR 0x10 /* Mask for the "sfr-OK" bit */
+#define LIT 0x08 /* Mask for the "literal-OK" bit */
+#define FP 0x04 /* Mask for "floating-point-OK" bit */
+
+/* This macro ors the bits together. Note that 'align' is a mask
+ * for the low 0, 1, or 2 bits of the register number, as appropriate.
+ */
+#define OP(align,lit,fp,sfr) ( align | lit | fp | sfr )
+
+#define R OP( 0, 0, 0, 0 )
+#define RS OP( 0, 0, 0, SFR )
+#define RL OP( 0, LIT, 0, 0 )
+#define RSL OP( 0, LIT, 0, SFR )
+#define F OP( 0, 0, FP, 0 )
+#define FL OP( 0, LIT, FP, 0 )
+#define R2 OP( 1, 0, 0, 0 )
+#define RL2 OP( 1, LIT, 0, 0 )
+#define F2 OP( 1, 0, FP, 0 )
+#define FL2 OP( 1, LIT, FP, 0 )
+#define R4 OP( 3, 0, 0, 0 )
+#define RL4 OP( 3, LIT, 0, 0 )
+#define F4 OP( 3, 0, FP, 0 )
+#define FL4 OP( 3, LIT, FP, 0 )
+
+#define M 0x7f /* Memory operand (MEMA & MEMB format instructions) */
+
+/* Macros to extract info from the register operand descriptor byte 'od'.
+ */
+#define SFR_OK(od) (od & SFR) /* TRUE if sfr operand allowed */
+#define LIT_OK(od) (od & LIT) /* TRUE if literal operand allowed */
+#define FP_OK(od) (od & FP) /* TRUE if floating-point op allowed */
+#define REG_ALIGN(od,n) ((od & 0x3 & n) == 0)
+ /* TRUE if reg #n is properly aligned */
+#define MEMOP(od) (od == M) /* TRUE if operand is a memory operand*/
+
+/* Description of a single i80960 instruction */
+struct i960_opcode {
+ long opcode; /* 32 bits, constant fields filled in, rest zeroed */
+ char *name; /* Assembler mnemonic */
+ short iclass; /* Class: see #defines below */
+ char format; /* REG, COBR, CTRL, MEMn, COJ, FBRA, or CALLJ */
+ char num_ops; /* Number of operands */
+ char operand[3];/* Operand descriptors; same order as assembler instr */
+};
+
+/* Classes of 960 intructions:
+ * - each instruction falls into one class.
+ * - each target architecture supports one or more classes.
+ *
+ * EACH CONSTANT MUST CONTAIN 1 AND ONLY 1 SET BIT!: see targ_has_iclass().
+ */
+#define I_BASE 0x01 /* 80960 base instruction set */
+#define I_CX 0x02 /* 80960Cx instruction */
+#define I_DEC 0x04 /* Decimal instruction */
+#define I_FP 0x08 /* Floating point instruction */
+#define I_KX 0x10 /* 80960Kx instruction */
+#define I_MIL 0x20 /* Military instruction */
+#define I_CASIM 0x40 /* CA simulator instruction */
+
+/******************************************************************************
+ *
+ * TABLE OF i960 INSTRUCTION DESCRIPTIONS
+ *
+ ******************************************************************************/
+
+const struct i960_opcode i960_opcodes[] = {
+
+ /* if a CTRL instruction has an operand, it's always a displacement */
+
+ { 0x09000000, "callj", I_BASE, CALLJ, 1 },/*default=='call'*/
+ { 0x08000000, "b", I_BASE, CTRL, 1 },
+ { 0x09000000, "call", I_BASE, CTRL, 1 },
+ { 0x0a000000, "ret", I_BASE, CTRL, 0 },
+ { 0x0b000000, "bal", I_BASE, CTRL, 1 },
+ { 0x10000000, "bno", I_BASE, CTRL, 1 },
+ { 0x10000000, "bf", I_BASE, CTRL, 1 }, /* same as bno */
+ { 0x10000000, "bru", I_BASE, CTRL, 1 }, /* same as bno */
+ { 0x11000000, "bg", I_BASE, CTRL, 1 },
+ { 0x11000000, "brg", I_BASE, CTRL, 1 }, /* same as bg */
+ { 0x12000000, "be", I_BASE, CTRL, 1 },
+ { 0x12000000, "bre", I_BASE, CTRL, 1 }, /* same as be */
+ { 0x13000000, "bge", I_BASE, CTRL, 1 },
+ { 0x13000000, "brge", I_BASE, CTRL, 1 }, /* same as bge */
+ { 0x14000000, "bl", I_BASE, CTRL, 1 },
+ { 0x14000000, "brl", I_BASE, CTRL, 1 }, /* same as bl */
+ { 0x15000000, "bne", I_BASE, CTRL, 1 },
+ { 0x15000000, "brlg", I_BASE, CTRL, 1 }, /* same as bne */
+ { 0x16000000, "ble", I_BASE, CTRL, 1 },
+ { 0x16000000, "brle", I_BASE, CTRL, 1 }, /* same as ble */
+ { 0x17000000, "bo", I_BASE, CTRL, 1 },
+ { 0x17000000, "bt", I_BASE, CTRL, 1 }, /* same as bo */
+ { 0x17000000, "bro", I_BASE, CTRL, 1 }, /* same as bo */
+ { 0x18000000, "faultno", I_BASE, CTRL, 0 },
+ { 0x18000000, "faultf", I_BASE, CTRL, 0 }, /*same as faultno*/
+ { 0x19000000, "faultg", I_BASE, CTRL, 0 },
+ { 0x1a000000, "faulte", I_BASE, CTRL, 0 },
+ { 0x1b000000, "faultge", I_BASE, CTRL, 0 },
+ { 0x1c000000, "faultl", I_BASE, CTRL, 0 },
+ { 0x1d000000, "faultne", I_BASE, CTRL, 0 },
+ { 0x1e000000, "faultle", I_BASE, CTRL, 0 },
+ { 0x1f000000, "faulto", I_BASE, CTRL, 0 },
+ { 0x1f000000, "faultt", I_BASE, CTRL, 0 }, /* syn for faulto */
+
+ { 0x01000000, "syscall", I_CASIM,CTRL, 0 },
+
+ /* If a COBR (or COJ) has 3 operands, the last one is always a
+ * displacement and does not appear explicitly in the table.
+ */
+
+ { 0x20000000, "testno", I_BASE, COBR, 1, R },
+ { 0x21000000, "testg", I_BASE, COBR, 1, R },
+ { 0x22000000, "teste", I_BASE, COBR, 1, R },
+ { 0x23000000, "testge", I_BASE, COBR, 1, R },
+ { 0x24000000, "testl", I_BASE, COBR, 1, R },
+ { 0x25000000, "testne", I_BASE, COBR, 1, R },
+ { 0x26000000, "testle", I_BASE, COBR, 1, R },
+ { 0x27000000, "testo", I_BASE, COBR, 1, R },
+ { 0x30000000, "bbc", I_BASE, COBR, 3, RL, RS },
+ { 0x31000000, "cmpobg", I_BASE, COBR, 3, RL, RS },
+ { 0x32000000, "cmpobe", I_BASE, COBR, 3, RL, RS },
+ { 0x33000000, "cmpobge", I_BASE, COBR, 3, RL, RS },
+ { 0x34000000, "cmpobl", I_BASE, COBR, 3, RL, RS },
+ { 0x35000000, "cmpobne", I_BASE, COBR, 3, RL, RS },
+ { 0x36000000, "cmpoble", I_BASE, COBR, 3, RL, RS },
+ { 0x37000000, "bbs", I_BASE, COBR, 3, RL, RS },
+ { 0x38000000, "cmpibno", I_BASE, COBR, 3, RL, RS },
+ { 0x39000000, "cmpibg", I_BASE, COBR, 3, RL, RS },
+ { 0x3a000000, "cmpibe", I_BASE, COBR, 3, RL, RS },
+ { 0x3b000000, "cmpibge", I_BASE, COBR, 3, RL, RS },
+ { 0x3c000000, "cmpibl", I_BASE, COBR, 3, RL, RS },
+ { 0x3d000000, "cmpibne", I_BASE, COBR, 3, RL, RS },
+ { 0x3e000000, "cmpible", I_BASE, COBR, 3, RL, RS },
+ { 0x3f000000, "cmpibo", I_BASE, COBR, 3, RL, RS },
+ { 0x31000000, "cmpojg", I_BASE, COJ, 3, RL, RS },
+ { 0x32000000, "cmpoje", I_BASE, COJ, 3, RL, RS },
+ { 0x33000000, "cmpojge", I_BASE, COJ, 3, RL, RS },
+ { 0x34000000, "cmpojl", I_BASE, COJ, 3, RL, RS },
+ { 0x35000000, "cmpojne", I_BASE, COJ, 3, RL, RS },
+ { 0x36000000, "cmpojle", I_BASE, COJ, 3, RL, RS },
+ { 0x38000000, "cmpijno", I_BASE, COJ, 3, RL, RS },
+ { 0x39000000, "cmpijg", I_BASE, COJ, 3, RL, RS },
+ { 0x3a000000, "cmpije", I_BASE, COJ, 3, RL, RS },
+ { 0x3b000000, "cmpijge", I_BASE, COJ, 3, RL, RS },
+ { 0x3c000000, "cmpijl", I_BASE, COJ, 3, RL, RS },
+ { 0x3d000000, "cmpijne", I_BASE, COJ, 3, RL, RS },
+ { 0x3e000000, "cmpijle", I_BASE, COJ, 3, RL, RS },
+ { 0x3f000000, "cmpijo", I_BASE, COJ, 3, RL, RS },
+
+ { 0x80000000, "ldob", I_BASE, MEM1, 2, M, R },
+ { 0x82000000, "stob", I_BASE, MEM1, 2, R , M },
+ { 0x84000000, "bx", I_BASE, MEM1, 1, M },
+ { 0x85000000, "balx", I_BASE, MEM1, 2, M, R },
+ { 0x86000000, "callx", I_BASE, MEM1, 1, M },
+ { 0x88000000, "ldos", I_BASE, MEM2, 2, M, R },
+ { 0x8a000000, "stos", I_BASE, MEM2, 2, R , M },
+ { 0x8c000000, "lda", I_BASE, MEM1, 2, M, R },
+ { 0x90000000, "ld", I_BASE, MEM4, 2, M, R },
+ { 0x92000000, "st", I_BASE, MEM4, 2, R , M },
+ { 0x98000000, "ldl", I_BASE, MEM8, 2, M, R2 },
+ { 0x9a000000, "stl", I_BASE, MEM8, 2, R2 ,M },
+ { 0xa0000000, "ldt", I_BASE, MEM12, 2, M, R4 },
+ { 0xa2000000, "stt", I_BASE, MEM12, 2, R4 ,M },
+ { 0xb0000000, "ldq", I_BASE, MEM16, 2, M, R4 },
+ { 0xb2000000, "stq", I_BASE, MEM16, 2, R4 ,M },
+ { 0xc0000000, "ldib", I_BASE, MEM1, 2, M, R },
+ { 0xc2000000, "stib", I_BASE, MEM1, 2, R , M },
+ { 0xc8000000, "ldis", I_BASE, MEM2, 2, M, R },
+ { 0xca000000, "stis", I_BASE, MEM2, 2, R , M },
+
+ { R_3(0x580), "notbit", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x581), "and", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x582), "andnot", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x583), "setbit", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x584), "notand", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x586), "xor", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x587), "or", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x588), "nor", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x589), "xnor", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_2D(0x58a), "not", I_BASE, REG, 2, RSL,RS },
+ { R_3(0x58b), "ornot", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x58c), "clrbit", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x58d), "notor", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x58e), "nand", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x58f), "alterbit", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x590), "addo", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x591), "addi", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x592), "subo", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x593), "subi", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x598), "shro", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x59a), "shrdi", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x59b), "shri", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x59c), "shlo", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x59d), "rotate", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x59e), "shli", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_2(0x5a0), "cmpo", I_BASE, REG, 2, RSL,RSL },
+ { R_2(0x5a1), "cmpi", I_BASE, REG, 2, RSL,RSL },
+ { R_2(0x5a2), "concmpo", I_BASE, REG, 2, RSL,RSL },
+ { R_2(0x5a3), "concmpi", I_BASE, REG, 2, RSL,RSL },
+ { R_3(0x5a4), "cmpinco", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x5a5), "cmpinci", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x5a6), "cmpdeco", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x5a7), "cmpdeci", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_2(0x5ac), "scanbyte", I_BASE, REG, 2, RSL,RSL },
+ { R_2(0x5ae), "chkbit", I_BASE, REG, 2, RSL,RSL },
+ { R_3(0x5b0), "addc", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x5b2), "subc", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_2D(0x5cc), "mov", I_BASE, REG, 2, RSL,RS },
+ { R_2D(0x5dc), "movl", I_BASE, REG, 2, RL2,R2 },
+ { R_2D(0x5ec), "movt", I_BASE, REG, 2, RL4,R4 },
+ { R_2D(0x5fc), "movq", I_BASE, REG, 2, RL4,R4 },
+ { R_3(0x610), "atmod", I_BASE, REG, 3, RS, RSL,R },
+ { R_3(0x612), "atadd", I_BASE, REG, 3, RS, RSL,RS },
+ { R_2D(0x640), "spanbit", I_BASE, REG, 2, RSL,RS },
+ { R_2D(0x641), "scanbit", I_BASE, REG, 2, RSL,RS },
+ { R_3(0x645), "modac", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x650), "modify", I_BASE, REG, 3, RSL,RSL,R },
+ { R_3(0x651), "extract", I_BASE, REG, 3, RSL,RSL,R },
+ { R_3(0x654), "modtc", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x655), "modpc", I_BASE, REG, 3, RSL,RSL,R },
+ { R_1(0x660), "calls", I_BASE, REG, 1, RSL },
+ { R_0(0x66b), "mark", I_BASE, REG, 0, },
+ { R_0(0x66c), "fmark", I_BASE, REG, 0, },
+ { R_0(0x66d), "flushreg", I_BASE, REG, 0, },
+ { R_0(0x66f), "syncf", I_BASE, REG, 0, },
+ { R_3(0x670), "emul", I_BASE, REG, 3, RSL,RSL,R2 },
+ { R_3(0x671), "ediv", I_BASE, REG, 3, RSL,RL2,RS },
+ { R_2D(0x672), "cvtadr", I_CASIM,REG, 2, RL, R2 },
+ { R_3(0x701), "mulo", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x708), "remo", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x70b), "divo", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x741), "muli", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x748), "remi", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x749), "modi", I_BASE, REG, 3, RSL,RSL,RS },
+ { R_3(0x74b), "divi", I_BASE, REG, 3, RSL,RSL,RS },
+
+ /* Floating-point instructions */
+
+ { R_2D(0x674), "cvtir", I_FP, REG, 2, RL, F },
+ { R_2D(0x675), "cvtilr", I_FP, REG, 2, RL, F },
+ { R_3(0x676), "scalerl", I_FP, REG, 3, RL, FL2,F2 },
+ { R_3(0x677), "scaler", I_FP, REG, 3, RL, FL, F },
+ { R_3(0x680), "atanr", I_FP, REG, 3, FL, FL, F },
+ { R_3(0x681), "logepr", I_FP, REG, 3, FL, FL, F },
+ { R_3(0x682), "logr", I_FP, REG, 3, FL, FL, F },
+ { R_3(0x683), "remr", I_FP, REG, 3, FL, FL, F },
+ { R_2(0x684), "cmpor", I_FP, REG, 2, FL, FL },
+ { R_2(0x685), "cmpr", I_FP, REG, 2, FL, FL },
+ { R_2D(0x688), "sqrtr", I_FP, REG, 2, FL, F },
+ { R_2D(0x689), "expr", I_FP, REG, 2, FL, F },
+ { R_2D(0x68a), "logbnr", I_FP, REG, 2, FL, F },
+ { R_2D(0x68b), "roundr", I_FP, REG, 2, FL, F },
+ { R_2D(0x68c), "sinr", I_FP, REG, 2, FL, F },
+ { R_2D(0x68d), "cosr", I_FP, REG, 2, FL, F },
+ { R_2D(0x68e), "tanr", I_FP, REG, 2, FL, F },
+ { R_1(0x68f), "classr", I_FP, REG, 1, FL },
+ { R_3(0x690), "atanrl", I_FP, REG, 3, FL2,FL2,F2 },
+ { R_3(0x691), "logeprl", I_FP, REG, 3, FL2,FL2,F2 },
+ { R_3(0x692), "logrl", I_FP, REG, 3, FL2,FL2,F2 },
+ { R_3(0x693), "remrl", I_FP, REG, 3, FL2,FL2,F2 },
+ { R_2(0x694), "cmporl", I_FP, REG, 2, FL2,FL2 },
+ { R_2(0x695), "cmprl", I_FP, REG, 2, FL2,FL2 },
+ { R_2D(0x698), "sqrtrl", I_FP, REG, 2, FL2,F2 },
+ { R_2D(0x699), "exprl", I_FP, REG, 2, FL2,F2 },
+ { R_2D(0x69a), "logbnrl", I_FP, REG, 2, FL2,F2 },
+ { R_2D(0x69b), "roundrl", I_FP, REG, 2, FL2,F2 },
+ { R_2D(0x69c), "sinrl", I_FP, REG, 2, FL2,F2 },
+ { R_2D(0x69d), "cosrl", I_FP, REG, 2, FL2,F2 },
+ { R_2D(0x69e), "tanrl", I_FP, REG, 2, FL2,F2 },
+ { R_1(0x69f), "classrl", I_FP, REG, 1, FL2 },
+ { R_2D(0x6c0), "cvtri", I_FP, REG, 2, FL, R },
+ { R_2D(0x6c1), "cvtril", I_FP, REG, 2, FL, R2 },
+ { R_2D(0x6c2), "cvtzri", I_FP, REG, 2, FL, R },
+ { R_2D(0x6c3), "cvtzril", I_FP, REG, 2, FL, R2 },
+ { R_2D(0x6c9), "movr", I_FP, REG, 2, FL, F },
+ { R_2D(0x6d9), "movrl", I_FP, REG, 2, FL2,F2 },
+ { R_2D(0x6e1), "movre", I_FP, REG, 2, FL4,F4 },
+ { R_3(0x6e2), "cpysre", I_FP, REG, 3, FL4,FL4,F4 },
+ { R_3(0x6e3), "cpyrsre", I_FP, REG, 3, FL4,FL4,F4 },
+ { R_3(0x78b), "divr", I_FP, REG, 3, FL, FL, F },
+ { R_3(0x78c), "mulr", I_FP, REG, 3, FL, FL, F },
+ { R_3(0x78d), "subr", I_FP, REG, 3, FL, FL, F },
+ { R_3(0x78f), "addr", I_FP, REG, 3, FL, FL, F },
+ { R_3(0x79b), "divrl", I_FP, REG, 3, FL2,FL2,F2 },
+ { R_3(0x79c), "mulrl", I_FP, REG, 3, FL2,FL2,F2 },
+ { R_3(0x79d), "subrl", I_FP, REG, 3, FL2,FL2,F2 },
+ { R_3(0x79f), "addrl", I_FP, REG, 3, FL2,FL2,F2 },
+
+ /* These are the floating point branch instructions. Each actually
+ * generates 2 branch instructions: the first a CTRL instruction with
+ * the indicated opcode, and the second a 'bno'.
+ */
+
+ { 0x12000000, "brue", I_FP, FBRA, 1 },
+ { 0x11000000, "brug", I_FP, FBRA, 1 },
+ { 0x13000000, "bruge", I_FP, FBRA, 1 },
+ { 0x14000000, "brul", I_FP, FBRA, 1 },
+ { 0x16000000, "brule", I_FP, FBRA, 1 },
+ { 0x15000000, "brulg", I_FP, FBRA, 1 },
+
+
+ /* Decimal instructions */
+
+ { R_3(0x642), "daddc", I_DEC, REG, 3, RSL,RSL,RS },
+ { R_3(0x643), "dsubc", I_DEC, REG, 3, RSL,RSL,RS },
+ { R_2D(0x644), "dmovt", I_DEC, REG, 2, RSL,RS },
+
+
+ /* KX extensions */
+
+ { R_2(0x600), "synmov", I_KX, REG, 2, R, R },
+ { R_2(0x601), "synmovl", I_KX, REG, 2, R, R },
+ { R_2(0x602), "synmovq", I_KX, REG, 2, R, R },
+ { R_2D(0x615), "synld", I_KX, REG, 2, R, R },
+
+
+ /* MC extensions */
+
+ { R_3(0x603), "cmpstr", I_MIL, REG, 3, R, R, RL },
+ { R_3(0x604), "movqstr", I_MIL, REG, 3, R, R, RL },
+ { R_3(0x605), "movstr", I_MIL, REG, 3, R, R, RL },
+ { R_2D(0x613), "inspacc", I_MIL, REG, 2, R, R },
+ { R_2D(0x614), "ldphy", I_MIL, REG, 2, R, R },
+ { R_3(0x617), "fill", I_MIL, REG, 3, R, RL, RL },
+ { R_2D(0x646), "condrec", I_MIL, REG, 2, R, R },
+ { R_2D(0x656), "receive", I_MIL, REG, 2, R, R },
+ { R_3(0x662), "send", I_MIL, REG, 3, R, RL, R },
+ { R_1(0x663), "sendserv", I_MIL, REG, 1, R },
+ { R_1(0x664), "resumprcs", I_MIL, REG, 1, R },
+ { R_1(0x665), "schedprcs", I_MIL, REG, 1, R },
+ { R_0(0x666), "saveprcs", I_MIL, REG, 0, },
+ { R_1(0x668), "condwait", I_MIL, REG, 1, R },
+ { R_1(0x669), "wait", I_MIL, REG, 1, R },
+ { R_1(0x66a), "signal", I_MIL, REG, 1, R },
+ { R_1D(0x673), "ldtime", I_MIL, REG, 1, R2 },
+
+
+ /* CX extensions */
+
+ { R_3(0x5d8), "eshro", I_CX, REG, 3, RSL,RSL,RS },
+ { R_3(0x630), "sdma", I_CX, REG, 3, RSL,RSL,RL },
+ { R_3(0x631), "udma", I_CX, REG, 0 },
+ { R_3(0x659), "sysctl", I_CX, REG, 3, RSL,RSL,RL },
+
+
+ /* END OF TABLE */
+
+ { 0, NULL, 0, 0 }
+};
+
+ /* end of i960-opcode.h */
diff --git a/gnu/usr.bin/as/opcode/m68k.h b/gnu/usr.bin/as/opcode/m68k.h
new file mode 100644
index 0000000..dda8337
--- /dev/null
+++ b/gnu/usr.bin/as/opcode/m68k.h
@@ -0,0 +1,1996 @@
+/* Opcode table for m680[01234]0/m6888[12]/m68851.
+ Copyright (C) 1989, 1991 Free Software Foundation.
+
+This file is part of GDB, the GNU Debugger and GAS, the GNU Assembler.
+
+Both GDB and GAS are free software; you can redistribute and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB and GAS are distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB or GAS; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* These are used as bit flags for arch below. */
+
+enum m68k_architecture {
+ m68000 = 0x01,
+ m68008 = m68000, /* synonym for -m68000. otherwise unused. */
+ m68010 = 0x02,
+ m68020 = 0x04,
+ m68030 = 0x08,
+ m68040 = 0x10,
+ m68881 = 0x20,
+ m68882 = m68881, /* synonym for -m68881. otherwise unused. */
+ m68851 = 0x40,
+
+ /* handy aliases */
+ m68040up = m68040,
+ m68030up = (m68030 | m68040up),
+ m68020up = (m68020 | m68030up),
+ m68010up = (m68010 | m68020up),
+ m68000up = (m68000 | m68010up),
+
+ mfloat = (m68881 | m68882 | m68040),
+ mmmu = (m68851 | m68030 | m68040)
+}; /* enum m68k_architecture */
+
+ /* note that differences in addressing modes that aren't distinguished
+ in the following table are handled explicitly by gas. */
+
+struct m68k_opcode {
+ char *name;
+ unsigned long opcode;
+ unsigned long match;
+ char *args;
+ enum m68k_architecture arch;
+};
+
+/* We store four bytes of opcode for all opcodes because that
+ is the most any of them need. The actual length of an instruction
+ is always at least 2 bytes, and is as much longer as necessary to
+ hold the operands it has.
+
+ The match component is a mask saying which bits must match
+ particular opcode in order for an instruction to be an instance
+ of that opcode.
+
+ The args component is a string containing two characters
+ for each operand of the instruction. The first specifies
+ the kind of operand; the second, the place it is stored. */
+
+/* Kinds of operands:
+ D data register only. Stored as 3 bits.
+ A address register only. Stored as 3 bits.
+ a address register indirect only. Stored as 3 bits.
+ R either kind of register. Stored as 4 bits.
+ F floating point coprocessor register only. Stored as 3 bits.
+ O an offset (or width): immediate data 0-31 or data register.
+ Stored as 6 bits in special format for BF... insns.
+ + autoincrement only. Stored as 3 bits (number of the address register).
+ - autodecrement only. Stored as 3 bits (number of the address register).
+ Q quick immediate data. Stored as 3 bits.
+ This matches an immediate operand only when value is in range 1 .. 8.
+ M moveq immediate data. Stored as 8 bits.
+ This matches an immediate operand only when value is in range -128..127
+ T trap vector immediate data. Stored as 4 bits.
+
+ k K-factor for fmove.p instruction. Stored as a 7-bit constant or
+ a three bit register offset, depending on the field type.
+
+ # immediate data. Stored in special places (b, w or l)
+ which say how many bits to store.
+ ^ immediate data for floating point instructions. Special places
+ are offset by 2 bytes from '#'...
+ B pc-relative address, converted to an offset
+ that is treated as immediate data.
+ d displacement and register. Stores the register as 3 bits
+ and stores the displacement in the entire second word.
+
+ C the CCR. No need to store it; this is just for filtering validity.
+ S the SR. No need to store, just as with CCR.
+ U the USP. No need to store, just as with CCR.
+
+ I Coprocessor ID. Not printed if 1. The Coprocessor ID is always
+ extracted from the 'd' field of word one, which means that an extended
+ coprocessor opcode can be skipped using the 'i' place, if needed.
+
+ s System Control register for the floating point coprocessor.
+ S List of system control registers for floating point coprocessor.
+
+ J Misc register for movec instruction, stored in 'j' format.
+ Possible values:
+ 0x000 SFC Source Function Code reg [40, 30, 20, 10]
+ 0x001 DFC Data Function Code reg [40, 30, 20, 10]
+ 0x002 CACR Cache Control Register [40, 30, 20]
+ 0x800 USP User Stack Pointer [40, 30, 20, 10]
+ 0x801 VBR Vector Base reg [40, 30, 20, 10]
+ 0x802 CAAR Cache Address Register [ 30, 20]
+ 0x803 MSP Master Stack Pointer [40, 30, 20]
+ 0x804 ISP Interrupt Stack Pointer [40, 30, 20]
+ 0x003 TC MMU Translation Control [40]
+ 0x004 ITT0 Instruction Transparent
+ Translation reg 0 [40]
+ 0x005 ITT1 Instruction Transparent
+ Translation reg 1 [40]
+ 0x006 DTT0 Data Transparent
+ Translation reg 0 [40]
+ 0x007 DTT1 Data Transparent
+ Translation reg 1 [40]
+ 0x805 MMUSR MMU Status reg [40]
+ 0x806 URP User Root Pointer [40]
+ 0x807 SRP Supervisor Root Pointer [40]
+
+ L Register list of the type d0-d7/a0-a7 etc.
+ (New! Improved! Can also hold fp0-fp7, as well!)
+ The assembler tries to see if the registers match the insn by
+ looking at where the insn wants them stored.
+
+ l Register list like L, but with all the bits reversed.
+ Used for going the other way. . .
+
+ c cache identifier which may be "nc" for no cache, "ic"
+ for instruction cache, "dc" for data cache, or "bc"
+ for both caches. Used in cinv and cpush. Always
+ stored in position "d".
+
+ They are all stored as 6 bits using an address mode and a register number;
+ they differ in which addressing modes they match.
+
+ * all (modes 0-6,7.*)
+ ~ alterable memory (modes 2-6,7.0,7.1)(not 0,1,7.~)
+ % alterable (modes 0-6,7.0,7.1)(not 7.~)
+ ; data (modes 0,2-6,7.*)(not 1)
+ @ data, but not immediate (modes 0,2-6,7.? ? ?)(not 1,7.?) This may really be ;, the 68020 book says it is
+ ! control (modes 2,5,6,7.*-)(not 0,1,3,4,7.4)
+ & alterable control (modes 2,5,6,7.0,7.1)(not 0,1,7.? ? ?)
+ $ alterable data (modes 0,2-6,7.0,7.1)(not 1,7.~)
+ ? alterable control, or data register (modes 0,2,5,6,7.0,7.1)(not 1,3,4,7.~)
+ / control, or data register (modes 0,2,5,6,7.0,7.1,7.2,7.3)(not 1,3,4,7.4)
+*/
+
+/* JF: for the 68851 */
+/*
+ I didn't use much imagination in choosing the
+ following codes, so many of them aren't very
+ mnemonic. -rab
+
+ P pmmu register
+ Possible values:
+ 000 TC Translation Control reg
+ 100 CAL Current Access Level
+ 101 VAL Validate Access Level
+ 110 SCC Stack Change Control
+ 111 AC Access Control
+
+ W wide pmmu registers
+ Possible values:
+ 001 DRP Dma Root Pointer
+ 010 SRP Supervisor Root Pointer
+ 011 CRP Cpu Root Pointer
+
+ f function code register
+ 0 SFC
+ 1 DFC
+
+ V VAL register only
+
+ X BADx, BACx
+ 100 BAD Breakpoint Acknowledge Data
+ 101 BAC Breakpoint Acknowledge Control
+
+ Y PSR
+ Z PCSR
+
+ | memory (modes 2-6, 7.*)
+
+*/
+
+/* Places to put an operand, for non-general operands:
+ s source, low bits of first word.
+ d dest, shifted 9 in first word
+ 1 second word, shifted 12
+ 2 second word, shifted 6
+ 3 second word, shifted 0
+ 4 third word, shifted 12
+ 5 third word, shifted 6
+ 6 third word, shifted 0
+ 7 second word, shifted 7
+ 8 second word, shifted 10
+ D store in both place 1 and place 3; for divul and divsl.
+ B first word, low byte, for branch displacements
+ W second word (entire), for branch displacements
+ L second and third words (entire), for branch displacements (also overloaded for move16)
+ b second word, low byte
+ w second word (entire) [variable word/long branch offset for dbra]
+ l second and third word (entire)
+ g variable branch offset for bra and similar instructions.
+ The place to store depends on the magnitude of offset.
+ t store in both place 7 and place 8; for floating point operations
+ c branch offset for cpBcc operations.
+ The place to store is word two if bit six of word one is zero,
+ and words two and three if bit six of word one is one.
+ i Increment by two, to skip over coprocessor extended operands. Only
+ works with the 'I' format.
+ k Dynamic K-factor field. Bits 6-4 of word 2, used as a register number.
+ Also used for dynamic fmovem instruction.
+ C floating point coprocessor constant - 7 bits. Also used for static
+ K-factors...
+ j Movec register #, stored in 12 low bits of second word.
+
+ Places to put operand, for general operands:
+ d destination, shifted 6 bits in first word
+ b source, at low bit of first word, and immediate uses one byte
+ w source, at low bit of first word, and immediate uses two bytes
+ l source, at low bit of first word, and immediate uses four bytes
+ s source, at low bit of first word.
+ Used sometimes in contexts where immediate is not allowed anyway.
+ f single precision float, low bit of 1st word, immediate uses 4 bytes
+ F double precision float, low bit of 1st word, immediate uses 8 bytes
+ x extended precision float, low bit of 1st word, immediate uses 12 bytes
+ p packed float, low bit of 1st word, immediate uses 12 bytes
+*/
+
+#define one(x) ((x) << 16)
+#define two(x, y) (((x) << 16) + y)
+
+/*
+ *** DANGER WILL ROBINSON ***
+
+ The assembler requires that all instances of the same mnemonic must be
+ consecutive. If they aren't, the assembler will bomb at runtime
+ */
+struct m68k_opcode m68k_opcodes[] =
+{
+{"abcd", one(0140400), one(0170770), "DsDd", m68000up },
+{"abcd", one(0140410), one(0170770), "-s-d", m68000up },
+
+ /* Add instructions */
+{"addal", one(0150700), one(0170700), "*lAd", m68000up },
+{"addaw", one(0150300), one(0170700), "*wAd", m68000up },
+{"addib", one(0003000), one(0177700), "#b$b", m68000up },
+{"addil", one(0003200), one(0177700), "#l$l", m68000up },
+{"addiw", one(0003100), one(0177700), "#w$w", m68000up },
+{"addqb", one(0050000), one(0170700), "Qd$b", m68000up },
+{"addql", one(0050200), one(0170700), "Qd%l", m68000up },
+{"addqw", one(0050100), one(0170700), "Qd%w", m68000up },
+
+{"addb", one(0050000), one(0170700), "Qd$b", m68000up }, /* addq written as add */
+{"addb", one(0003000), one(0177700), "#b$b", m68000up }, /* addi written as add */
+{"addb", one(0150000), one(0170700), ";bDd", m68000up }, /* addb <ea>, Dd */
+{"addb", one(0150400), one(0170700), "Dd~b", m68000up }, /* addb Dd, <ea> */
+
+{"addw", one(0050100), one(0170700), "Qd%w", m68000up }, /* addq written as add */
+{"addw", one(0003100), one(0177700), "#w$w", m68000up }, /* addi written as add */
+{"addw", one(0150300), one(0170700), "*wAd", m68000up }, /* adda written as add */
+{"addw", one(0150100), one(0170700), "*wDd", m68000up }, /* addw <ea>, Dd */
+{"addw", one(0150500), one(0170700), "Dd~w", m68000up }, /* addw Dd, <ea> */
+
+{"addl", one(0050200), one(0170700), "Qd%l", m68000up }, /* addq written as add */
+{"addl", one(0003200), one(0177700), "#l$l", m68000up }, /* addi written as add */
+{"addl", one(0150700), one(0170700), "*lAd", m68000up }, /* adda written as add */
+{"addl", one(0150200), one(0170700), "*lDd", m68000up }, /* addl <ea>, Dd */
+{"addl", one(0150600), one(0170700), "Dd~l", m68000up }, /* addl Dd, <ea> */
+
+{"addxb", one(0150400), one(0170770), "DsDd", m68000up },
+{"addxb", one(0150410), one(0170770), "-s-d", m68000up },
+{"addxl", one(0150600), one(0170770), "DsDd", m68000up },
+{"addxl", one(0150610), one(0170770), "-s-d", m68000up },
+{"addxw", one(0150500), one(0170770), "DsDd", m68000up },
+{"addxw", one(0150510), one(0170770), "-s-d", m68000up },
+
+{"andib", one(0001000), one(0177700), "#b$b", m68000up },
+{"andib", one(0001074), one(0177777), "#bCb", m68000up }, /* andi to ccr */
+{"andiw", one(0001100), one(0177700), "#w$w", m68000up },
+{"andiw", one(0001174), one(0177777), "#wSw", m68000up }, /* andi to sr */
+{"andil", one(0001200), one(0177700), "#l$l", m68000up },
+
+{"andb", one(0001000), one(0177700), "#b$b", m68000up }, /* andi written as or */
+{"andb", one(0001074), one(0177777), "#bCb", m68000up }, /* andi to ccr */
+{"andb", one(0140000), one(0170700), ";bDd", m68000up }, /* memory to register */
+{"andb", one(0140400), one(0170700), "Dd~b", m68000up }, /* register to memory */
+{"andw", one(0001100), one(0177700), "#w$w", m68000up }, /* andi written as or */
+{"andw", one(0001174), one(0177777), "#wSw", m68000up }, /* andi to sr */
+{"andw", one(0140100), one(0170700), ";wDd", m68000up }, /* memory to register */
+{"andw", one(0140500), one(0170700), "Dd~w", m68000up }, /* register to memory */
+{"andl", one(0001200), one(0177700), "#l$l", m68000up }, /* andi written as or */
+{"andl", one(0140200), one(0170700), ";lDd", m68000up }, /* memory to register */
+{"andl", one(0140600), one(0170700), "Dd~l", m68000up }, /* register to memory */
+
+{"aslb", one(0160400), one(0170770), "QdDs", m68000up },
+{"aslb", one(0160440), one(0170770), "DdDs", m68000up },
+{"asll", one(0160600), one(0170770), "QdDs", m68000up },
+{"asll", one(0160640), one(0170770), "DdDs", m68000up },
+{"aslw", one(0160500), one(0170770), "QdDs", m68000up },
+{"aslw", one(0160540), one(0170770), "DdDs", m68000up },
+{"aslw", one(0160700), one(0177700), "~s", m68000up }, /* Shift memory */
+{"asrb", one(0160000), one(0170770), "QdDs", m68000up },
+{"asrb", one(0160040), one(0170770), "DdDs", m68000up },
+{"asrl", one(0160200), one(0170770), "QdDs", m68000up },
+{"asrl", one(0160240), one(0170770), "DdDs", m68000up },
+{"asrw", one(0160100), one(0170770), "QdDs", m68000up },
+{"asrw", one(0160140), one(0170770), "DdDs", m68000up },
+{"asrw", one(0160300), one(0177700), "~s", m68000up }, /* Shift memory */
+
+/* Fixed-size branches with 16-bit offsets */
+
+{"bhi", one(0061000), one(0177777), "BW", m68000up },
+{"bls", one(0061400), one(0177777), "BW", m68000up },
+{"bcc", one(0062000), one(0177777), "BW", m68000up },
+{"bcs", one(0062400), one(0177777), "BW", m68000up },
+{"bne", one(0063000), one(0177777), "BW", m68000up },
+{"beq", one(0063400), one(0177777), "BW", m68000up },
+{"bvc", one(0064000), one(0177777), "BW", m68000up },
+{"bvs", one(0064400), one(0177777), "BW", m68000up },
+{"bpl", one(0065000), one(0177777), "BW", m68000up },
+{"bmi", one(0065400), one(0177777), "BW", m68000up },
+{"bge", one(0066000), one(0177777), "BW", m68000up },
+{"blt", one(0066400), one(0177777), "BW", m68000up },
+{"bgt", one(0067000), one(0177777), "BW", m68000up },
+{"ble", one(0067400), one(0177777), "BW", m68000up },
+{"bra", one(0060000), one(0177777), "BW", m68000up },
+{"bsr", one(0060400), one(0177777), "BW", m68000up },
+
+/* Fixed-size branches with short (byte) offsets */
+
+{"bhis", one(0061000), one(0177400), "BB", m68000up },
+{"blss", one(0061400), one(0177400), "BB", m68000up },
+{"bccs", one(0062000), one(0177400), "BB", m68000up },
+{"bcss", one(0062400), one(0177400), "BB", m68000up },
+{"bnes", one(0063000), one(0177400), "BB", m68000up },
+{"beqs", one(0063400), one(0177400), "BB", m68000up },
+{"bvcs", one(0064000), one(0177400), "BB", m68000up },
+{"bvss", one(0064400), one(0177400), "BB", m68000up },
+{"bpls", one(0065000), one(0177400), "BB", m68000up },
+{"bmis", one(0065400), one(0177400), "BB", m68000up },
+{"bges", one(0066000), one(0177400), "BB", m68000up },
+{"blts", one(0066400), one(0177400), "BB", m68000up },
+{"bgts", one(0067000), one(0177400), "BB", m68000up },
+{"bles", one(0067400), one(0177400), "BB", m68000up },
+{"bras", one(0060000), one(0177400), "BB", m68000up },
+{"bsrs", one(0060400), one(0177400), "BB", m68000up },
+
+/* Fixed-size branches with long (32-bit) offsets */
+
+{"bhil", one(0061377), one(0177777), "BL", m68020up },
+{"blsl", one(0061777), one(0177777), "BL", m68020up },
+{"bccl", one(0062377), one(0177777), "BL", m68020up },
+{"bcsl", one(0062777), one(0177777), "BL", m68020up },
+{"bnel", one(0063377), one(0177777), "BL", m68020up },
+{"beql", one(0063777), one(0177777), "BL", m68020up },
+{"bvcl", one(0064377), one(0177777), "BL", m68020up },
+{"bvsl", one(0064777), one(0177777), "BL", m68020up },
+{"bpll", one(0065377), one(0177777), "BL", m68020up },
+{"bmil", one(0065777), one(0177777), "BL", m68020up },
+{"bgel", one(0066377), one(0177777), "BL", m68020up },
+{"bltl", one(0066777), one(0177777), "BL", m68020up },
+{"bgtl", one(0067377), one(0177777), "BL", m68020up },
+{"blel", one(0067777), one(0177777), "BL", m68020up },
+{"bral", one(0060377), one(0177777), "BL", m68020up },
+{"bsrl", one(0060777), one(0177777), "BL", m68020up },
+
+/* We now return you to our regularly scheduled instruction set */
+
+{"bchg", one(0000500), one(0170700), "Dd$s", m68000up },
+{"bchg", one(0004100), one(0177700), "#b$s", m68000up },
+{"bclr", one(0000600), one(0170700), "Dd$s", m68000up },
+{"bclr", one(0004200), one(0177700), "#b$s", m68000up },
+
+{"bfchg", two(0165300, 0), two(0177700, 0170000), "?sO2O3", m68020up },
+{"bfclr", two(0166300, 0), two(0177700, 0170000), "?sO2O3", m68020up },
+{"bfexts", two(0165700, 0), two(0177700, 0100000), "/sO2O3D1", m68020up },
+{"bfextu", two(0164700, 0), two(0177700, 0100000), "/sO2O3D1", m68020up },
+{"bfffo", two(0166700, 0), two(0177700, 0100000), "/sO2O3D1", m68020up },
+{"bfins", two(0167700, 0), two(0177700, 0100000), "D1?sO2O3", m68020up },
+{"bfset", two(0167300, 0), two(0177700, 0170000), "?sO2O3", m68020up },
+{"bftst", two(0164300, 0), two(0177700, 0170000), "/sO2O3", m68020up },
+{"bkpt", one(0044110), one(0177770), "Qs", m68020up },
+
+{"bset", one(0000700), one(0170700), "Dd$s", m68000up },
+{"bset", one(0004300), one(0177700), "#b$s", m68000up },
+{"btst", one(0000400), one(0170700), "Dd@s", m68000up },
+{"btst", one(0004000), one(0177700), "#b@s", m68000up },
+
+
+{"callm", one(0003300), one(0177700), "#b!s", m68020 },
+
+{"cas2l", two(0007374, 0), two(0177777, 0107070), "D3D6D2D5R1R4", m68020up }, /* JF FOO really a 3 word ins */
+{"cas2w", two(0006374, 0), two(0177777, 0107070), "D3D6D2D5R1R4", m68020up }, /* JF ditto */
+{"casb", two(0005300, 0), two(0177700, 0177070), "D3D2~s", m68020up },
+{"casl", two(0007300, 0), two(0177700, 0177070), "D3D2~s", m68020up },
+{"casw", two(0006300, 0), two(0177700, 0177070), "D3D2~s", m68020up },
+
+/* {"chk", one(0040600), one(0170700), ";wDd"}, JF FOO this looks wrong */
+{"chk2b", two(0000300, 0004000), two(0177700, 07777), "!sR1", m68020up },
+{"chk2l", two(0002300, 0004000), two(0177700, 07777), "!sR1", m68020up },
+{"chk2w", two(0001300, 0004000), two(0177700, 07777), "!sR1", m68020up },
+{"chkl", one(0040400), one(0170700), ";lDd", m68000up },
+{"chkw", one(0040600), one(0170700), ";wDd", m68000up },
+
+#define SCOPE_LINE (0x1 << 3)
+#define SCOPE_PAGE (0x2 << 3)
+#define SCOPE_ALL (0x3 << 3)
+
+{"cinva", one(0xf400|SCOPE_ALL), one(0xff20), "ce", m68040 },
+{"cinvl", one(0xf400|SCOPE_LINE), one(0xff20), "ceas", m68040 },
+{"cinvp", one(0xf400|SCOPE_PAGE), one(0xff20), "ceas", m68040 },
+
+{"cpusha", one(0xf420|SCOPE_ALL), one(0xff20), "ce", m68040 },
+{"cpushl", one(0xf420|SCOPE_LINE), one(0xff20), "ceas", m68040 },
+{"cpushp", one(0xf420|SCOPE_PAGE), one(0xff20), "ceas", m68040 },
+
+#undef SCOPE_LINE
+#undef SCOPE_PAGE
+#undef SCOPE_ALL
+
+{"clrb", one(0041000), one(0177700), "$s", m68000up },
+{"clrl", one(0041200), one(0177700), "$s", m68000up },
+{"clrw", one(0041100), one(0177700), "$s", m68000up },
+
+{"cmp2b", two(0000300, 0), two(0177700, 07777), "!sR1", m68020up },
+{"cmp2l", two(0002300, 0), two(0177700, 07777), "!sR1", m68020up },
+{"cmp2w", two(0001300, 0), two(0177700, 07777), "!sR1", m68020up },
+{"cmpal", one(0130700), one(0170700), "*lAd", m68000up },
+{"cmpaw", one(0130300), one(0170700), "*wAd", m68000up },
+{"cmpib", one(0006000), one(0177700), "#b;b", m68000up },
+{"cmpil", one(0006200), one(0177700), "#l;l", m68000up },
+{"cmpiw", one(0006100), one(0177700), "#w;w", m68000up },
+{"cmpb", one(0006000), one(0177700), "#b;b", m68000up }, /* cmpi written as cmp */
+{"cmpb", one(0130000), one(0170700), ";bDd", m68000up },
+{"cmpw", one(0006100), one(0177700), "#w;w", m68000up },
+{"cmpw", one(0130100), one(0170700), "*wDd", m68000up },
+{"cmpw", one(0130300), one(0170700), "*wAd", m68000up }, /* cmpa written as cmp */
+{"cmpl", one(0006200), one(0177700), "#l;l", m68000up },
+{"cmpl", one(0130200), one(0170700), "*lDd", m68000up },
+{"cmpl", one(0130700), one(0170700), "*lAd", m68000up },
+{"cmpmb", one(0130410), one(0170770), "+s+d", m68000up },
+{"cmpml", one(0130610), one(0170770), "+s+d", m68000up },
+{"cmpmw", one(0130510), one(0170770), "+s+d", m68000up },
+
+{"dbcc", one(0052310), one(0177770), "DsBw", m68000up },
+{"dbcs", one(0052710), one(0177770), "DsBw", m68000up },
+{"dbeq", one(0053710), one(0177770), "DsBw", m68000up },
+{"dbf", one(0050710), one(0177770), "DsBw", m68000up },
+{"dbge", one(0056310), one(0177770), "DsBw", m68000up },
+{"dbgt", one(0057310), one(0177770), "DsBw", m68000up },
+{"dbhi", one(0051310), one(0177770), "DsBw", m68000up },
+{"dble", one(0057710), one(0177770), "DsBw", m68000up },
+{"dbls", one(0051710), one(0177770), "DsBw", m68000up },
+{"dblt", one(0056710), one(0177770), "DsBw", m68000up },
+{"dbmi", one(0055710), one(0177770), "DsBw", m68000up },
+{"dbne", one(0053310), one(0177770), "DsBw", m68000up },
+{"dbpl", one(0055310), one(0177770), "DsBw", m68000up },
+{"dbra", one(0050710), one(0177770), "DsBw", m68000up },
+{"dbt", one(0050310), one(0177770), "DsBw", m68000up },
+{"dbvc", one(0054310), one(0177770), "DsBw", m68000up },
+{"dbvs", one(0054710), one(0177770), "DsBw", m68000up },
+
+{"divsl", two(0046100, 0006000), two(0177700, 0107770), ";lD3D1", m68020up },
+{"divsl", two(0046100, 0004000), two(0177700, 0107770), ";lDD", m68020up },
+{"divsll", two(0046100, 0004000), two(0177700, 0107770), ";lD3D1", m68020up },
+{"divsw", one(0100700), one(0170700), ";wDd", m68000up },
+{"divs", one(0100700), one(0170700), ";wDd", m68000up },
+{"divul", two(0046100, 0002000), two(0177700, 0107770), ";lD3D1", m68020up },
+{"divul", two(0046100, 0000000), two(0177700, 0107770), ";lDD", m68020up },
+{"divull", two(0046100, 0000000), two(0177700, 0107770), ";lD3D1", m68020up },
+{"divuw", one(0100300), one(0170700), ";wDd", m68000up },
+{"divu", one(0100300), one(0170700), ";wDd", m68000up },
+{"eorb", one(0005000), one(0177700), "#b$s", m68000up }, /* eori written as or */
+{"eorb", one(0005074), one(0177777), "#bCs", m68000up }, /* eori to ccr */
+{"eorb", one(0130400), one(0170700), "Dd$s", m68000up }, /* register to memory */
+{"eorib", one(0005000), one(0177700), "#b$s", m68000up },
+{"eorib", one(0005074), one(0177777), "#bCs", m68000up }, /* eori to ccr */
+{"eoril", one(0005200), one(0177700), "#l$s", m68000up },
+{"eoriw", one(0005100), one(0177700), "#w$s", m68000up },
+{"eoriw", one(0005174), one(0177777), "#wSs", m68000up }, /* eori to sr */
+{"eorl", one(0005200), one(0177700), "#l$s", m68000up },
+{"eorl", one(0130600), one(0170700), "Dd$s", m68000up },
+{"eorw", one(0005100), one(0177700), "#w$s", m68000up },
+{"eorw", one(0005174), one(0177777), "#wSs", m68000up }, /* eori to sr */
+{"eorw", one(0130500), one(0170700), "Dd$s", m68000up },
+
+{"exg", one(0140500), one(0170770), "DdDs", m68000up },
+{"exg", one(0140510), one(0170770), "AdAs", m68000up },
+{"exg", one(0140610), one(0170770), "DdAs", m68000up },
+{"exg", one(0140610), one(0170770), "AsDd", m68000up },
+
+{"extw", one(0044200), one(0177770), "Ds", m68000up },
+{"extl", one(0044300), one(0177770), "Ds", m68000up },
+{"extbl", one(0044700), one(0177770), "Ds", m68020up },
+{"extb.l", one(0044700), one(0177770), "Ds", m68020up }, /* Not sure we should support this one */
+
+/* float stuff starts here */
+{"fabsb", two(0xF000, 0x5818), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fabsd", two(0xF000, 0x5418), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fabsl", two(0xF000, 0x4018), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fabsp", two(0xF000, 0x4C18), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fabss", two(0xF000, 0x4418), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fabsw", two(0xF000, 0x5018), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fabsx", two(0xF000, 0x0018), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fabsx", two(0xF000, 0x4818), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fabsx", two(0xF000, 0x0018), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+/* FIXME-NOW: The '040 book that I have claims that these should be
+ coded exactly like fadd. In fact, the table of opmodes calls them
+ fadd, fsadd, fdadd. That can't be right. If someone can give me the
+ right encoding, I'll fix it. By induction, I *think* the right
+ encoding is 38 & 3c, but I'm not sure.
+
+ in the mean time, if you know the encoding for the opmode field, you
+ can replace all of the "38),"'s and "3c),"'s below with the corrected
+ values and these guys should then just work. xoxorich. 31Aug91 */
+
+#ifdef comment
+{"fsabsb", two(0xF000, 0x5838), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040 },
+{"fsabsd", two(0xF000, 0x5438), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040 },
+{"fsabsl", two(0xF000, 0x4038), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040 },
+{"fsabsp", two(0xF000, 0x4C38), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040 },
+{"fsabss", two(0xF000, 0x4438), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040 },
+{"fsabsw", two(0xF000, 0x5038), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040 },
+{"fsabsx", two(0xF000, 0x0038), two(0xF1C0, 0xE07F), "IiF8F7", m68040 },
+{"fsabsx", two(0xF000, 0x4838), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040 },
+{"fsabsx", two(0xF000, 0x0038), two(0xF1C0, 0xE07F), "IiFt", m68040 },
+
+{"fdabsb", two(0xF000, 0x583c), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040},
+{"fdabsd", two(0xF000, 0x543c), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040},
+{"fdabsl", two(0xF000, 0x403c), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040},
+{"fdabsp", two(0xF000, 0x4C3c), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040},
+{"fdabss", two(0xF000, 0x443c), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040},
+{"fdabsw", two(0xF000, 0x503c), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040},
+{"fdabsx", two(0xF000, 0x003c), two(0xF1C0, 0xE07F), "IiF8F7", m68040},
+{"fdabsx", two(0xF000, 0x483c), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040},
+{"fdabsx", two(0xF000, 0x003c), two(0xF1C0, 0xE07F), "IiFt", m68040},
+#endif /* comment */
+
+{"facosb", two(0xF000, 0x581C), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"facosd", two(0xF000, 0x541C), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"facosl", two(0xF000, 0x401C), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"facosp", two(0xF000, 0x4C1C), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"facoss", two(0xF000, 0x441C), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"facosw", two(0xF000, 0x501C), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"facosx", two(0xF000, 0x001C), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"facosx", two(0xF000, 0x481C), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"facosx", two(0xF000, 0x001C), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"faddb", two(0xF000, 0x5822), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"faddd", two(0xF000, 0x5422), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"faddl", two(0xF000, 0x4022), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"faddp", two(0xF000, 0x4C22), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fadds", two(0xF000, 0x4422), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"faddw", two(0xF000, 0x5022), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"faddx", two(0xF000, 0x0022), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"faddx", two(0xF000, 0x4822), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+/* {"faddx", two(0xF000, 0x0022), two(0xF1C0, 0xE07F), "IiFt", mfloat }, JF removed */
+
+{"fsaddb", two(0xF000, 0x5832), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040 },
+{"fsaddd", two(0xF000, 0x5432), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040 },
+{"fsaddl", two(0xF000, 0x4032), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040 },
+{"fsaddp", two(0xF000, 0x4C32), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040 },
+{"fsadds", two(0xF000, 0x4432), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040 },
+{"fsaddw", two(0xF000, 0x5032), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040 },
+{"fsaddx", two(0xF000, 0x0032), two(0xF1C0, 0xE07F), "IiF8F7", m68040 },
+{"fsaddx", two(0xF000, 0x4832), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040 },
+/* {"fsaddx", two(0xF000, 0x0032), two(0xF1C0, 0xE07F), "IiFt", m68040 }, JF removed */
+
+{"fdaddb", two(0xF000, 0x5836), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040 },
+{"fdaddd", two(0xF000, 0x5436), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040 },
+{"fdaddl", two(0xF000, 0x4036), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040 },
+{"fdaddp", two(0xF000, 0x4C36), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040 },
+{"fdadds", two(0xF000, 0x4436), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040 },
+{"fdaddw", two(0xF000, 0x5036), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040 },
+{"fdaddx", two(0xF000, 0x0036), two(0xF1C0, 0xE07F), "IiF8F7", m68040 },
+{"fdaddx", two(0xF000, 0x4836), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040 },
+/* {"faddx", two(0xF000, 0x0036), two(0xF1C0, 0xE07F), "IiFt", m68040 }, JF removed */
+
+{"fasinb", two(0xF000, 0x580C), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fasind", two(0xF000, 0x540C), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fasinl", two(0xF000, 0x400C), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fasinp", two(0xF000, 0x4C0C), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fasins", two(0xF000, 0x440C), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fasinw", two(0xF000, 0x500C), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fasinx", two(0xF000, 0x000C), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fasinx", two(0xF000, 0x480C), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fasinx", two(0xF000, 0x000C), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fatanb", two(0xF000, 0x580A), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fatand", two(0xF000, 0x540A), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fatanl", two(0xF000, 0x400A), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fatanp", two(0xF000, 0x4C0A), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fatans", two(0xF000, 0x440A), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fatanw", two(0xF000, 0x500A), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fatanx", two(0xF000, 0x000A), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fatanx", two(0xF000, 0x480A), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fatanx", two(0xF000, 0x000A), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fatanhb", two(0xF000, 0x580D), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fatanhd", two(0xF000, 0x540D), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fatanhl", two(0xF000, 0x400D), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fatanhp", two(0xF000, 0x4C0D), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fatanhs", two(0xF000, 0x440D), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fatanhw", two(0xF000, 0x500D), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fatanhx", two(0xF000, 0x000D), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fatanhx", two(0xF000, 0x480D), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fatanhx", two(0xF000, 0x000D), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+/* Fixed-size Float branches */
+
+{"fbeq", one(0xF081), one(0xF1BF), "IdBW", mfloat },
+{"fbf", one(0xF080), one(0xF1BF), "IdBW", mfloat },
+{"fbge", one(0xF093), one(0xF1BF), "IdBW", mfloat },
+{"fbgl", one(0xF096), one(0xF1BF), "IdBW", mfloat },
+{"fbgle", one(0xF097), one(0xF1BF), "IdBW", mfloat },
+{"fbgt", one(0xF092), one(0xF1BF), "IdBW", mfloat },
+{"fble", one(0xF095), one(0xF1BF), "IdBW", mfloat },
+{"fblt", one(0xF094), one(0xF1BF), "IdBW", mfloat },
+{"fbne", one(0xF08E), one(0xF1BF), "IdBW", mfloat },
+{"fbnge", one(0xF09C), one(0xF1BF), "IdBW", mfloat },
+{"fbngl", one(0xF099), one(0xF1BF), "IdBW", mfloat },
+{"fbngle", one(0xF098), one(0xF1BF), "IdBW", mfloat },
+{"fbngt", one(0xF09D), one(0xF1BF), "IdBW", mfloat },
+{"fbnle", one(0xF09A), one(0xF1BF), "IdBW", mfloat },
+{"fbnlt", one(0xF09B), one(0xF1BF), "IdBW", mfloat },
+{"fboge", one(0xF083), one(0xF1BF), "IdBW", mfloat },
+{"fbogl", one(0xF086), one(0xF1BF), "IdBW", mfloat },
+{"fbogt", one(0xF082), one(0xF1BF), "IdBW", mfloat },
+{"fbole", one(0xF085), one(0xF1BF), "IdBW", mfloat },
+{"fbolt", one(0xF084), one(0xF1BF), "IdBW", mfloat },
+{"fbor", one(0xF087), one(0xF1BF), "IdBW", mfloat },
+{"fbseq", one(0xF091), one(0xF1BF), "IdBW", mfloat },
+{"fbsf", one(0xF090), one(0xF1BF), "IdBW", mfloat },
+{"fbsne", one(0xF09E), one(0xF1BF), "IdBW", mfloat },
+{"fbst", one(0xF09F), one(0xF1BF), "IdBW", mfloat },
+{"fbt", one(0xF08F), one(0xF1BF), "IdBW", mfloat },
+{"fbueq", one(0xF089), one(0xF1BF), "IdBW", mfloat },
+{"fbuge", one(0xF08B), one(0xF1BF), "IdBW", mfloat },
+{"fbugt", one(0xF08A), one(0xF1BF), "IdBW", mfloat },
+{"fbule", one(0xF08D), one(0xF1BF), "IdBW", mfloat },
+{"fbult", one(0xF08C), one(0xF1BF), "IdBW", mfloat },
+{"fbun", one(0xF088), one(0xF1BF), "IdBW", mfloat },
+
+/* Float branches -- long (32-bit) displacements */
+
+{"fbeql", one(0xF081), one(0xF1BF), "IdBC", mfloat },
+{"fbfl", one(0xF080), one(0xF1BF), "IdBC", mfloat },
+{"fbgel", one(0xF093), one(0xF1BF), "IdBC", mfloat },
+{"fbgll", one(0xF096), one(0xF1BF), "IdBC", mfloat },
+{"fbglel", one(0xF097), one(0xF1BF), "IdBC", mfloat },
+{"fbgtl", one(0xF092), one(0xF1BF), "IdBC", mfloat },
+{"fblel", one(0xF095), one(0xF1BF), "IdBC", mfloat },
+{"fbltl", one(0xF094), one(0xF1BF), "IdBC", mfloat },
+{"fbnel", one(0xF08E), one(0xF1BF), "IdBC", mfloat },
+{"fbngel", one(0xF09C), one(0xF1BF), "IdBC", mfloat },
+{"fbngll", one(0xF099), one(0xF1BF), "IdBC", mfloat },
+{"fbnglel", one(0xF098), one(0xF1BF), "IdBC", mfloat },
+{"fbngtl", one(0xF09D), one(0xF1BF), "IdBC", mfloat },
+{"fbnlel", one(0xF09A), one(0xF1BF), "IdBC", mfloat },
+{"fbnltl", one(0xF09B), one(0xF1BF), "IdBC", mfloat },
+{"fbogel", one(0xF083), one(0xF1BF), "IdBC", mfloat },
+{"fbogll", one(0xF086), one(0xF1BF), "IdBC", mfloat },
+{"fbogtl", one(0xF082), one(0xF1BF), "IdBC", mfloat },
+{"fbolel", one(0xF085), one(0xF1BF), "IdBC", mfloat },
+{"fboltl", one(0xF084), one(0xF1BF), "IdBC", mfloat },
+{"fborl", one(0xF087), one(0xF1BF), "IdBC", mfloat },
+{"fbseql", one(0xF091), one(0xF1BF), "IdBC", mfloat },
+{"fbsfl", one(0xF090), one(0xF1BF), "IdBC", mfloat },
+{"fbsnel", one(0xF09E), one(0xF1BF), "IdBC", mfloat },
+{"fbstl", one(0xF09F), one(0xF1BF), "IdBC", mfloat },
+{"fbtl", one(0xF08F), one(0xF1BF), "IdBC", mfloat },
+{"fbueql", one(0xF089), one(0xF1BF), "IdBC", mfloat },
+{"fbugel", one(0xF08B), one(0xF1BF), "IdBC", mfloat },
+{"fbugtl", one(0xF08A), one(0xF1BF), "IdBC", mfloat },
+{"fbulel", one(0xF08D), one(0xF1BF), "IdBC", mfloat },
+{"fbultl", one(0xF08C), one(0xF1BF), "IdBC", mfloat },
+{"fbunl", one(0xF088), one(0xF1BF), "IdBC", mfloat },
+
+{"fcmpb", two(0xF000, 0x5838), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fcmpd", two(0xF000, 0x5438), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fcmpl", two(0xF000, 0x4038), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fcmpp", two(0xF000, 0x4C38), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fcmps", two(0xF000, 0x4438), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fcmpw", two(0xF000, 0x5038), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fcmpx", two(0xF000, 0x0038), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fcmpx", two(0xF000, 0x4838), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+/* {"fcmpx", two(0xF000, 0x0038), two(0xF1C0, 0xE07F), "IiFt", mfloat }, JF removed */
+
+{"fcosb", two(0xF000, 0x581D), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fcosd", two(0xF000, 0x541D), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fcosl", two(0xF000, 0x401D), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fcosp", two(0xF000, 0x4C1D), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fcoss", two(0xF000, 0x441D), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fcosw", two(0xF000, 0x501D), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fcosx", two(0xF000, 0x001D), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fcosx", two(0xF000, 0x481D), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fcosx", two(0xF000, 0x001D), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fcoshb", two(0xF000, 0x5819), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fcoshd", two(0xF000, 0x5419), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fcoshl", two(0xF000, 0x4019), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fcoshp", two(0xF000, 0x4C19), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fcoshs", two(0xF000, 0x4419), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fcoshw", two(0xF000, 0x5019), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fcoshx", two(0xF000, 0x0019), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fcoshx", two(0xF000, 0x4819), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fcoshx", two(0xF000, 0x0019), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fdbeq", two(0xF048, 0x0001), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbf", two(0xF048, 0x0000), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbge", two(0xF048, 0x0013), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbgl", two(0xF048, 0x0016), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbgle", two(0xF048, 0x0017), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbgt", two(0xF048, 0x0012), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdble", two(0xF048, 0x0015), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdblt", two(0xF048, 0x0014), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbne", two(0xF048, 0x000E), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbnge", two(0xF048, 0x001C), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbngl", two(0xF048, 0x0019), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbngle", two(0xF048, 0x0018), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbngt", two(0xF048, 0x001D), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbnle", two(0xF048, 0x001A), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbnlt", two(0xF048, 0x001B), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdboge", two(0xF048, 0x0003), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbogl", two(0xF048, 0x0006), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbogt", two(0xF048, 0x0002), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbole", two(0xF048, 0x0005), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbolt", two(0xF048, 0x0004), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbor", two(0xF048, 0x0007), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbseq", two(0xF048, 0x0011), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbsf", two(0xF048, 0x0010), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbsne", two(0xF048, 0x001E), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbst", two(0xF048, 0x001F), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbt", two(0xF048, 0x000F), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbueq", two(0xF048, 0x0009), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbuge", two(0xF048, 0x000B), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbugt", two(0xF048, 0x000A), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbule", two(0xF048, 0x000D), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbult", two(0xF048, 0x000C), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbun", two(0xF048, 0x0008), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+
+{"fdivb", two(0xF000, 0x5820), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fdivd", two(0xF000, 0x5420), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fdivl", two(0xF000, 0x4020), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fdivp", two(0xF000, 0x4C20), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fdivs", two(0xF000, 0x4420), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fdivw", two(0xF000, 0x5020), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fdivx", two(0xF000, 0x0020), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fdivx", two(0xF000, 0x4820), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+/* {"fdivx", two(0xF000, 0x0020), two(0xF1C0, 0xE07F), "IiFt", mfloat }, JF */
+
+{"fsdivb", two(0xF000, 0x5830), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040 },
+{"fsdivd", two(0xF000, 0x5430), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040 },
+{"fsdivl", two(0xF000, 0x4030), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040 },
+{"fsdivp", two(0xF000, 0x4C30), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040 },
+{"fsdivs", two(0xF000, 0x4430), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040 },
+{"fsdivw", two(0xF000, 0x5030), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040 },
+{"fsdivx", two(0xF000, 0x0030), two(0xF1C0, 0xE07F), "IiF8F7", m68040 },
+{"fsdivx", two(0xF000, 0x4830), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040 },
+/* {"fsdivx", two(0xF000, 0x0030), two(0xF1C0, 0xE07F), "IiFt", m68040 }, JF */
+
+{"fddivb", two(0xF000, 0x5834), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040 },
+{"fddivd", two(0xF000, 0x5434), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040 },
+{"fddivl", two(0xF000, 0x4034), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040 },
+{"fddivp", two(0xF000, 0x4C34), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040 },
+{"fddivs", two(0xF000, 0x4434), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040 },
+{"fddivw", two(0xF000, 0x5034), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040 },
+{"fddivx", two(0xF000, 0x0034), two(0xF1C0, 0xE07F), "IiF8F7", m68040 },
+{"fddivx", two(0xF000, 0x4834), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040 },
+/* {"fddivx", two(0xF000, 0x0034), two(0xF1C0, 0xE07F), "IiFt", m68040 }, JF */
+
+{"fetoxb", two(0xF000, 0x5810), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fetoxd", two(0xF000, 0x5410), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fetoxl", two(0xF000, 0x4010), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fetoxp", two(0xF000, 0x4C10), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fetoxs", two(0xF000, 0x4410), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fetoxw", two(0xF000, 0x5010), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fetoxx", two(0xF000, 0x0010), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fetoxx", two(0xF000, 0x4810), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fetoxx", two(0xF000, 0x0010), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fetoxm1b", two(0xF000, 0x5808), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fetoxm1d", two(0xF000, 0x5408), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fetoxm1l", two(0xF000, 0x4008), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fetoxm1p", two(0xF000, 0x4C08), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fetoxm1s", two(0xF000, 0x4408), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fetoxm1w", two(0xF000, 0x5008), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fetoxm1x", two(0xF000, 0x0008), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fetoxm1x", two(0xF000, 0x4808), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fetoxm1x", two(0xF000, 0x0008), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fgetexpb", two(0xF000, 0x581E), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fgetexpd", two(0xF000, 0x541E), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fgetexpl", two(0xF000, 0x401E), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fgetexpp", two(0xF000, 0x4C1E), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fgetexps", two(0xF000, 0x441E), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fgetexpw", two(0xF000, 0x501E), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fgetexpx", two(0xF000, 0x001E), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fgetexpx", two(0xF000, 0x481E), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fgetexpx", two(0xF000, 0x001E), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fgetmanb", two(0xF000, 0x581F), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fgetmand", two(0xF000, 0x541F), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fgetmanl", two(0xF000, 0x401F), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fgetmanp", two(0xF000, 0x4C1F), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fgetmans", two(0xF000, 0x441F), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fgetmanw", two(0xF000, 0x501F), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fgetmanx", two(0xF000, 0x001F), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fgetmanx", two(0xF000, 0x481F), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fgetmanx", two(0xF000, 0x001F), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fintb", two(0xF000, 0x5801), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fintd", two(0xF000, 0x5401), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fintl", two(0xF000, 0x4001), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fintp", two(0xF000, 0x4C01), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fints", two(0xF000, 0x4401), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fintw", two(0xF000, 0x5001), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fintx", two(0xF000, 0x0001), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fintx", two(0xF000, 0x4801), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fintx", two(0xF000, 0x0001), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fintrzb", two(0xF000, 0x5803), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fintrzd", two(0xF000, 0x5403), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fintrzl", two(0xF000, 0x4003), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fintrzp", two(0xF000, 0x4C03), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fintrzs", two(0xF000, 0x4403), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fintrzw", two(0xF000, 0x5003), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fintrzx", two(0xF000, 0x0003), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fintrzx", two(0xF000, 0x4803), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fintrzx", two(0xF000, 0x0003), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"flog10b", two(0xF000, 0x5815), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"flog10d", two(0xF000, 0x5415), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"flog10l", two(0xF000, 0x4015), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"flog10p", two(0xF000, 0x4C15), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"flog10s", two(0xF000, 0x4415), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"flog10w", two(0xF000, 0x5015), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"flog10x", two(0xF000, 0x0015), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"flog10x", two(0xF000, 0x4815), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"flog10x", two(0xF000, 0x0015), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"flog2b", two(0xF000, 0x5816), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"flog2d", two(0xF000, 0x5416), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"flog2l", two(0xF000, 0x4016), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"flog2p", two(0xF000, 0x4C16), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"flog2s", two(0xF000, 0x4416), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"flog2w", two(0xF000, 0x5016), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"flog2x", two(0xF000, 0x0016), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"flog2x", two(0xF000, 0x4816), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"flog2x", two(0xF000, 0x0016), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"flognb", two(0xF000, 0x5814), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"flognd", two(0xF000, 0x5414), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"flognl", two(0xF000, 0x4014), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"flognp", two(0xF000, 0x4C14), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"flogns", two(0xF000, 0x4414), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"flognw", two(0xF000, 0x5014), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"flognx", two(0xF000, 0x0014), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"flognx", two(0xF000, 0x4814), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"flognx", two(0xF000, 0x0014), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"flognp1b", two(0xF000, 0x5806), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"flognp1d", two(0xF000, 0x5406), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"flognp1l", two(0xF000, 0x4006), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"flognp1p", two(0xF000, 0x4C06), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"flognp1s", two(0xF000, 0x4406), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"flognp1w", two(0xF000, 0x5006), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"flognp1x", two(0xF000, 0x0006), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"flognp1x", two(0xF000, 0x4806), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"flognp1x", two(0xF000, 0x0006), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fmodb", two(0xF000, 0x5821), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fmodd", two(0xF000, 0x5421), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fmodl", two(0xF000, 0x4021), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fmodp", two(0xF000, 0x4C21), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fmods", two(0xF000, 0x4421), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fmodw", two(0xF000, 0x5021), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fmodx", two(0xF000, 0x0021), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fmodx", two(0xF000, 0x4821), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+/* {"fmodx", two(0xF000, 0x0021), two(0xF1C0, 0xE07F), "IiFt", mfloat }, JF */
+
+{"fmoveb", two(0xF000, 0x5800), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat }, /* fmove from <ea> to fp<n> */
+{"fmoveb", two(0xF000, 0x7800), two(0xF1C0, 0xFC7F), "IiF7@b", mfloat }, /* fmove from fp<n> to <ea> */
+{"fmoved", two(0xF000, 0x5400), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat }, /* fmove from <ea> to fp<n> */
+{"fmoved", two(0xF000, 0x7400), two(0xF1C0, 0xFC7F), "IiF7@F", mfloat }, /* fmove from fp<n> to <ea> */
+{"fmovel", two(0xF000, 0x4000), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat }, /* fmove from <ea> to fp<n> */
+{"fmovel", two(0xF000, 0x6000), two(0xF1C0, 0xFC7F), "IiF7@l", mfloat }, /* fmove from fp<n> to <ea> */
+/* Warning: The addressing modes on these are probably not right:
+ esp, Areg direct is only allowed for FPI */
+ /* fmove.l from/to system control registers: */
+{"fmovel", two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "Iis8@s", mfloat },
+{"fmovel", two(0xF000, 0x8000), two(0xF1C0, 0xE3FF), "Ii*ls8", mfloat },
+
+/* {"fmovel", two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "Iis8@s", mfloat },
+{"fmovel", two(0xF000, 0x8000), two(0xF2C0, 0xE3FF), "Ii*ss8", mfloat }, */
+
+{"fmovep", two(0xF000, 0x4C00), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat }, /* fmove from <ea> to fp<n> */
+{"fmovep", two(0xF000, 0x6C00), two(0xF1C0, 0xFC00), "IiF7@pkC", mfloat }, /* fmove.p with k-factors: */
+{"fmovep", two(0xF000, 0x7C00), two(0xF1C0, 0xFC0F), "IiF7@pDk", mfloat }, /* fmove.p with k-factors: */
+
+{"fmoves", two(0xF000, 0x4400), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat }, /* fmove from <ea> to fp<n> */
+{"fmoves", two(0xF000, 0x6400), two(0xF1C0, 0xFC7F), "IiF7@f", mfloat }, /* fmove from fp<n> to <ea> */
+{"fmovew", two(0xF000, 0x5000), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat }, /* fmove from <ea> to fp<n> */
+{"fmovew", two(0xF000, 0x7000), two(0xF1C0, 0xFC7F), "IiF7@w", mfloat }, /* fmove from fp<n> to <ea> */
+{"fmovex", two(0xF000, 0x0000), two(0xF1C0, 0xE07F), "IiF8F7", mfloat }, /* fmove from <ea> to fp<n> */
+{"fmovex", two(0xF000, 0x4800), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat }, /* fmove from <ea> to fp<n> */
+{"fmovex", two(0xF000, 0x6800), two(0xF1C0, 0xFC7F), "IiF7@x", mfloat }, /* fmove from fp<n> to <ea> */
+/* JF removed {"fmovex", two(0xF000, 0x0000), two(0xF1C0, 0xE07F), "IiFt", mfloat }, / * fmove from <ea> to fp<n> */
+
+{"fsmoveb", two(0xF000, 0x5800), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040 }, /* fmove from <ea> to fp<n> */
+{"fsmoved", two(0xF000, 0x5400), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040 }, /* fmove from <ea> to fp<n> */
+{"fsmovel", two(0xF000, 0x4000), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040 }, /* fmove from <ea> to fp<n> */
+{"fsmoves", two(0xF000, 0x4400), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040 }, /* fmove from <ea> to fp<n> */
+{"fsmovew", two(0xF000, 0x5000), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040 }, /* fmove from <ea> to fp<n> */
+{"fsmovex", two(0xF000, 0x0000), two(0xF1C0, 0xE07F), "IiF8F7", m68040 }, /* fmove from <ea> to fp<n> */
+{"fsmovex", two(0xF000, 0x4800), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040 }, /* fmove from <ea> to fp<n> */
+/* JF removed {"fsmovex", two(0xF000, 0x0000), two(0xF1C0, 0xE07F), "IiFt", m68040 }, / * fmove from <ea> to fp<n> */
+
+{"fdmoveb", two(0xF000, 0x5800), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040 }, /* fmove from <ea> to fp<n> */
+{"fdmoved", two(0xF000, 0x5400), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040 }, /* fmove from <ea> to fp<n> */
+{"fdmovel", two(0xF000, 0x4000), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040 }, /* fmove from <ea> to fp<n> */
+{"fdmoves", two(0xF000, 0x4400), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040 }, /* fmove from <ea> to fp<n> */
+{"fdmovew", two(0xF000, 0x5000), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040 }, /* fmove from <ea> to fp<n> */
+{"fdmovex", two(0xF000, 0x0000), two(0xF1C0, 0xE07F), "IiF8F7", m68040 }, /* fmove from <ea> to fp<n> */
+{"fdmovex", two(0xF000, 0x4800), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040 }, /* fmove from <ea> to fp<n> */
+/* JF removed {"fdmovex", two(0xF000, 0x0000), two(0xF1C0, 0xE07F), "IiFt", m68040 }, / * fmove from <ea> to fp<n> */
+
+{"fmovecrx", two(0xF000, 0x5C00), two(0xF1FF, 0xFC00), "Ii#CF7", mfloat }, /* fmovecr.x #ccc, FPn */
+{"fmovecr", two(0xF000, 0x5C00), two(0xF1FF, 0xFC00), "Ii#CF7", mfloat },
+
+/* Other fmovemx. */
+{"fmovemx", two(0xF000, 0xF800), two(0xF1C0, 0xFF8F), "IiDk&s", mfloat }, /* reg to control, static and dynamic: */
+{"fmovemx", two(0xF000, 0xD800), two(0xF1C0, 0xFF8F), "Ii&sDk", mfloat }, /* from control to reg, static and dynamic: */
+
+{"fmovemx", two(0xF000, 0xF000), two(0xF1C0, 0xFF00), "Idl3&s", mfloat }, /* to control, static and dynamic: */
+{"fmovemx", two(0xF000, 0xF000), two(0xF1C0, 0xFF00), "Id#3&s", mfloat }, /* to control, static and dynamic: */
+
+{"fmovemx", two(0xF000, 0xD000), two(0xF1C0, 0xFF00), "Id&sl3", mfloat }, /* from control, static and dynamic: */
+{"fmovemx", two(0xF000, 0xD000), two(0xF1C0, 0xFF00), "Id&s#3", mfloat }, /* from control, static and dynamic: */
+
+{"fmovemx", two(0xF020, 0xE800), two(0xF1F8, 0xFF8F), "IiDk-s", mfloat }, /* reg to autodecrement, static and dynamic */
+{"fmovemx", two(0xF020, 0xE000), two(0xF1F8, 0xFF00), "IdL3-s", mfloat }, /* to autodecrement, static and dynamic */
+{"fmovemx", two(0xF020, 0xE000), two(0xF1F8, 0xFF00), "Id#3-s", mfloat }, /* to autodecrement, static and dynamic */
+
+{"fmovemx", two(0xF018, 0xD800), two(0xF1F8, 0xFF8F), "Ii+sDk", mfloat }, /* from autoinc to reg, static and dynamic: */
+{"fmovemx", two(0xF018, 0xD000), two(0xF1F8, 0xFF00), "Id+sl3", mfloat }, /* from autoincrement, static and dynamic: */
+{"fmovemx", two(0xF018, 0xD000), two(0xF1F8, 0xFF00), "Id+s#3", mfloat }, /* from autoincrement, static and dynamic: */
+
+{"fmoveml", two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "IiL8@s", mfloat },
+{"fmoveml", two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "Ii#8@s", mfloat },
+{"fmoveml", two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "Iis8@s", mfloat },
+
+{"fmoveml", two(0xF000, 0x8000), two(0xF2C0, 0xE3FF), "Ii*sL8", mfloat },
+{"fmoveml", two(0xF000, 0x8000), two(0xF1C0, 0xE3FF), "Ii*s#8", mfloat },
+{"fmoveml", two(0xF000, 0x8000), two(0xF1C0, 0xE3FF), "Ii*ss8", mfloat },
+
+/* fmovemx with register lists */
+{"fmovem", two(0xF020, 0xE000), two(0xF1F8, 0xFF00), "IdL3-s", mfloat }, /* to autodec, static & dynamic */
+{"fmovem", two(0xF000, 0xF000), two(0xF1C0, 0xFF00), "Idl3&s", mfloat }, /* to control, static and dynamic */
+{"fmovem", two(0xF018, 0xD000), two(0xF1F8, 0xFF00), "Id+sl3", mfloat }, /* from autoinc, static & dynamic */
+{"fmovem", two(0xF000, 0xD000), two(0xF1C0, 0xFF00), "Id&sl3", mfloat }, /* from control, static and dynamic */
+
+ /* Alternate mnemonics for GNU as and GNU CC */
+{"fmovem", two(0xF020, 0xE000), two(0xF1F8, 0xFF00), "Id#3-s", mfloat }, /* to autodecrement, static and dynamic */
+{"fmovem", two(0xF020, 0xE800), two(0xF1F8, 0xFF8F), "IiDk-s", mfloat }, /* to autodecrement, static and dynamic */
+
+{"fmovem", two(0xF000, 0xF000), two(0xF1C0, 0xFF00), "Id#3&s", mfloat }, /* to control, static and dynamic: */
+{"fmovem", two(0xF000, 0xF800), two(0xF1C0, 0xFF8F), "IiDk&s", mfloat }, /* to control, static and dynamic: */
+
+{"fmovem", two(0xF018, 0xD000), two(0xF1F8, 0xFF00), "Id+s#3", mfloat }, /* from autoincrement, static and dynamic: */
+{"fmovem", two(0xF018, 0xD800), two(0xF1F8, 0xFF8F), "Ii+sDk", mfloat }, /* from autoincrement, static and dynamic: */
+
+{"fmovem", two(0xF000, 0xD000), two(0xF1C0, 0xFF00), "Id&s#3", mfloat }, /* from control, static and dynamic: */
+{"fmovem", two(0xF000, 0xD800), two(0xF1C0, 0xFF8F), "Ii&sDk", mfloat }, /* from control, static and dynamic: */
+
+/* fmoveml a FP-control register */
+{"fmovem", two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "Iis8@s", mfloat },
+{"fmovem", two(0xF000, 0x8000), two(0xF1C0, 0xE3FF), "Ii*ss8", mfloat },
+
+/* fmoveml a FP-control reglist */
+{"fmovem", two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "IiL8@s", mfloat },
+{"fmovem", two(0xF000, 0x8000), two(0xF2C0, 0xE3FF), "Ii*sL8", mfloat },
+
+{"fmulb", two(0xF000, 0x5823), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fmuld", two(0xF000, 0x5423), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fmull", two(0xF000, 0x4023), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fmulp", two(0xF000, 0x4C23), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fmuls", two(0xF000, 0x4423), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fmulw", two(0xF000, 0x5023), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fmulx", two(0xF000, 0x0023), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fmulx", two(0xF000, 0x4823), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+/* {"fmulx", two(0xF000, 0x0023), two(0xF1C0, 0xE07F), "IiFt", mfloat }, JF */
+
+{"fsmulb", two(0xF000, 0x5833), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040 },
+{"fsmuld", two(0xF000, 0x5433), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040 },
+{"fsmull", two(0xF000, 0x4033), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040 },
+{"fsmulp", two(0xF000, 0x4C33), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040 },
+{"fsmuls", two(0xF000, 0x4433), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040 },
+{"fsmulw", two(0xF000, 0x5033), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040 },
+{"fsmulx", two(0xF000, 0x0033), two(0xF1C0, 0xE07F), "IiF8F7", m68040 },
+{"fsmulx", two(0xF000, 0x4833), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040 },
+/* {"fsmulx", two(0xF000, 0x0033), two(0xF1C0, 0xE07F), "IiFt", m68040 }, JF */
+
+{"fdmulb", two(0xF000, 0x5837), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040 },
+{"fdmuld", two(0xF000, 0x5437), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040 },
+{"fdmull", two(0xF000, 0x4037), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040 },
+{"fdmulp", two(0xF000, 0x4C37), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040 },
+{"fdmuls", two(0xF000, 0x4437), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040 },
+{"fdmulw", two(0xF000, 0x5037), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040 },
+{"fdmulx", two(0xF000, 0x0037), two(0xF1C0, 0xE07F), "IiF8F7", m68040 },
+{"fdmulx", two(0xF000, 0x4837), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040 },
+/* {"dfmulx", two(0xF000, 0x0037), two(0xF1C0, 0xE07F), "IiFt", m68040 }, JF */
+
+{"fnegb", two(0xF000, 0x581A), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fnegd", two(0xF000, 0x541A), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fnegl", two(0xF000, 0x401A), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fnegp", two(0xF000, 0x4C1A), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fnegs", two(0xF000, 0x441A), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fnegw", two(0xF000, 0x501A), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fnegx", two(0xF000, 0x001A), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fnegx", two(0xF000, 0x481A), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fnegx", two(0xF000, 0x001A), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fsnegb", two(0xF000, 0x585A), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040 },
+{"fsnegd", two(0xF000, 0x545A), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040 },
+{"fsnegl", two(0xF000, 0x405A), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040 },
+{"fsnegp", two(0xF000, 0x4C5A), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040 },
+{"fsnegs", two(0xF000, 0x445A), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040 },
+{"fsnegw", two(0xF000, 0x505A), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040 },
+{"fsnegx", two(0xF000, 0x005A), two(0xF1C0, 0xE07F), "IiF8F7", m68040 },
+{"fsnegx", two(0xF000, 0x485A), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040 },
+{"fsnegx", two(0xF000, 0x005A), two(0xF1C0, 0xE07F), "IiFt", m68040 },
+
+{"fdnegb", two(0xF000, 0x585E), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040 },
+{"fdnegd", two(0xF000, 0x545E), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040 },
+{"fdnegl", two(0xF000, 0x405E), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040 },
+{"fdnegp", two(0xF000, 0x4C5E), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040 },
+{"fdnegs", two(0xF000, 0x445E), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040 },
+{"fdnegw", two(0xF000, 0x505E), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040 },
+{"fdnegx", two(0xF000, 0x005E), two(0xF1C0, 0xE07F), "IiF8F7", m68040 },
+{"fdnegx", two(0xF000, 0x485E), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040 },
+{"fdnegx", two(0xF000, 0x005E), two(0xF1C0, 0xE07F), "IiFt", m68040 },
+
+{"fnop", two(0xF280, 0x0000), two(0xFFFF, 0xFFFF), "Ii", mfloat },
+
+{"fremb", two(0xF000, 0x5825), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fremd", two(0xF000, 0x5425), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"freml", two(0xF000, 0x4025), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fremp", two(0xF000, 0x4C25), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"frems", two(0xF000, 0x4425), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fremw", two(0xF000, 0x5025), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fremx", two(0xF000, 0x0025), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fremx", two(0xF000, 0x4825), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+/* {"fremx", two(0xF000, 0x0025), two(0xF1C0, 0xE07F), "IiFt", mfloat }, JF */
+
+{"frestore", one(0xF140), one(0xF1C0), "Id&s", mfloat },
+{"frestore", one(0xF158), one(0xF1F8), "Id+s", mfloat },
+{"fsave", one(0xF100), one(0xF1C0), "Id&s", mfloat },
+{"fsave", one(0xF120), one(0xF1F8), "Id-s", mfloat },
+
+{"fscaleb", two(0xF000, 0x5826), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fscaled", two(0xF000, 0x5426), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fscalel", two(0xF000, 0x4026), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fscalep", two(0xF000, 0x4C26), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fscales", two(0xF000, 0x4426), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fscalew", two(0xF000, 0x5026), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fscalex", two(0xF000, 0x0026), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fscalex", two(0xF000, 0x4826), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+/* {"fscalex", two(0xF000, 0x0026), two(0xF1C0, 0xE07F), "IiFt", mfloat }, JF */
+
+/* $ is necessary to prevent the assembler from using PC-relative.
+ If @ were used, "label: fseq label" could produce "ftrapeq",
+ because "label" became "pc@label". */
+{"fseq", two(0xF040, 0x0001), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsf", two(0xF040, 0x0000), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsge", two(0xF040, 0x0013), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsgl", two(0xF040, 0x0016), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsgle", two(0xF040, 0x0017), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsgt", two(0xF040, 0x0012), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsle", two(0xF040, 0x0015), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fslt", two(0xF040, 0x0014), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsne", two(0xF040, 0x000E), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsnge", two(0xF040, 0x001C), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsngl", two(0xF040, 0x0019), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsngle", two(0xF040, 0x0018), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsngt", two(0xF040, 0x001D), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsnle", two(0xF040, 0x001A), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsnlt", two(0xF040, 0x001B), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsoge", two(0xF040, 0x0003), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsogl", two(0xF040, 0x0006), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsogt", two(0xF040, 0x0002), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsole", two(0xF040, 0x0005), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsolt", two(0xF040, 0x0004), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsor", two(0xF040, 0x0007), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsseq", two(0xF040, 0x0011), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fssf", two(0xF040, 0x0010), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fssne", two(0xF040, 0x001E), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsst", two(0xF040, 0x001F), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fst", two(0xF040, 0x000F), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsueq", two(0xF040, 0x0009), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsuge", two(0xF040, 0x000B), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsugt", two(0xF040, 0x000A), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsule", two(0xF040, 0x000D), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsult", two(0xF040, 0x000C), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsun", two(0xF040, 0x0008), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+
+{"fsgldivb", two(0xF000, 0x5824), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fsgldivd", two(0xF000, 0x5424), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fsgldivl", two(0xF000, 0x4024), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fsgldivp", two(0xF000, 0x4C24), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fsgldivs", two(0xF000, 0x4424), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fsgldivw", two(0xF000, 0x5024), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fsgldivx", two(0xF000, 0x0024), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fsgldivx", two(0xF000, 0x4824), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fsgldivx", two(0xF000, 0x0024), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fsglmulb", two(0xF000, 0x5827), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fsglmuld", two(0xF000, 0x5427), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fsglmull", two(0xF000, 0x4027), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fsglmulp", two(0xF000, 0x4C27), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fsglmuls", two(0xF000, 0x4427), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fsglmulw", two(0xF000, 0x5027), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fsglmulx", two(0xF000, 0x0027), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fsglmulx", two(0xF000, 0x4827), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fsglmulx", two(0xF000, 0x0027), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fsinb", two(0xF000, 0x580E), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fsind", two(0xF000, 0x540E), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fsinl", two(0xF000, 0x400E), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fsinp", two(0xF000, 0x4C0E), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fsins", two(0xF000, 0x440E), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fsinw", two(0xF000, 0x500E), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fsinx", two(0xF000, 0x000E), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fsinx", two(0xF000, 0x480E), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fsinx", two(0xF000, 0x000E), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fsinhb", two(0xF000, 0x5802), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fsinhd", two(0xF000, 0x5402), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fsinhl", two(0xF000, 0x4002), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fsinhp", two(0xF000, 0x4C02), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fsinhs", two(0xF000, 0x4402), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fsinhw", two(0xF000, 0x5002), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fsinhx", two(0xF000, 0x0002), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fsinhx", two(0xF000, 0x4802), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fsinhx", two(0xF000, 0x0002), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fsincosb", two(0xF000, 0x5830), two(0xF1C0, 0xFC78), "Ii;bF3F7", mfloat },
+{"fsincosd", two(0xF000, 0x5430), two(0xF1C0, 0xFC78), "Ii;FF3F7", mfloat },
+{"fsincosl", two(0xF000, 0x4030), two(0xF1C0, 0xFC78), "Ii;lF3F7", mfloat },
+{"fsincosp", two(0xF000, 0x4C30), two(0xF1C0, 0xFC78), "Ii;pF3F7", mfloat },
+{"fsincoss", two(0xF000, 0x4430), two(0xF1C0, 0xFC78), "Ii;fF3F7", mfloat },
+{"fsincosw", two(0xF000, 0x5030), two(0xF1C0, 0xFC78), "Ii;wF3F7", mfloat },
+{"fsincosx", two(0xF000, 0x0030), two(0xF1C0, 0xE078), "IiF8F3F7", mfloat },
+{"fsincosx", two(0xF000, 0x4830), two(0xF1C0, 0xFC78), "Ii;xF3F7", mfloat },
+
+{"fsqrtb", two(0xF000, 0x5804), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fsqrtd", two(0xF000, 0x5404), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fsqrtl", two(0xF000, 0x4004), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fsqrtp", two(0xF000, 0x4C04), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fsqrts", two(0xF000, 0x4404), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fsqrtw", two(0xF000, 0x5004), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fsqrtx", two(0xF000, 0x0004), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fsqrtx", two(0xF000, 0x4804), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fsqrtx", two(0xF000, 0x0004), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fssqrtb", two(0xF000, 0x5841), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040 },
+{"fssqrtd", two(0xF000, 0x5441), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040 },
+{"fssqrtl", two(0xF000, 0x4041), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040 },
+{"fssqrtp", two(0xF000, 0x4C41), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040 },
+{"fssqrts", two(0xF000, 0x4441), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040 },
+{"fssqrtw", two(0xF000, 0x5041), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040 },
+{"fssqrtx", two(0xF000, 0x0041), two(0xF1C0, 0xE07F), "IiF8F7", m68040 },
+{"fssqrtx", two(0xF000, 0x4841), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040 },
+{"fssqrtx", two(0xF000, 0x0041), two(0xF1C0, 0xE07F), "IiFt", m68040 },
+
+{"fdsqrtb", two(0xF000, 0x5845), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040 },
+{"fdsqrtd", two(0xF000, 0x5445), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040 },
+{"fdsqrtl", two(0xF000, 0x4045), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040 },
+{"fdsqrtp", two(0xF000, 0x4C45), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040 },
+{"fdsqrts", two(0xF000, 0x4445), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040 },
+{"fdsqrtw", two(0xF000, 0x5045), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040 },
+{"fdsqrtx", two(0xF000, 0x0045), two(0xF1C0, 0xE07F), "IiF8F7", m68040 },
+{"fdsqrtx", two(0xF000, 0x4845), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040 },
+{"fdsqrtx", two(0xF000, 0x0045), two(0xF1C0, 0xE07F), "IiFt", m68040 },
+
+{"fsubb", two(0xF000, 0x5828), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fsubd", two(0xF000, 0x5428), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fsubl", two(0xF000, 0x4028), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fsubp", two(0xF000, 0x4C28), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fsubs", two(0xF000, 0x4428), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fsubw", two(0xF000, 0x5028), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fsubx", two(0xF000, 0x0028), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fsubx", two(0xF000, 0x4828), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fsubx", two(0xF000, 0x0028), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fssubb", two(0xF000, 0x5838), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040 },
+{"fssubd", two(0xF000, 0x5438), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040 },
+{"fssubl", two(0xF000, 0x4038), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040 },
+{"fssubp", two(0xF000, 0x4C38), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040 },
+{"fssubs", two(0xF000, 0x4438), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040 },
+{"fssubw", two(0xF000, 0x5038), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040 },
+{"fssubx", two(0xF000, 0x0038), two(0xF1C0, 0xE07F), "IiF8F7", m68040 },
+{"fssubx", two(0xF000, 0x4838), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040 },
+{"fssubx", two(0xF000, 0x0038), two(0xF1C0, 0xE07F), "IiFt", m68040 },
+
+{"fdsubb", two(0xF000, 0x583c), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040 },
+{"fdsubd", two(0xF000, 0x543c), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040 },
+{"fdsubl", two(0xF000, 0x403c), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040 },
+{"fdsubp", two(0xF000, 0x4C3c), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040 },
+{"fdsubs", two(0xF000, 0x443c), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040 },
+{"fdsubw", two(0xF000, 0x503c), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040 },
+{"fdsubx", two(0xF000, 0x003c), two(0xF1C0, 0xE07F), "IiF8F7", m68040 },
+{"fdsubx", two(0xF000, 0x483c), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040 },
+{"fdsubx", two(0xF000, 0x003c), two(0xF1C0, 0xE07F), "IiFt", m68040 },
+
+{"ftanb", two(0xF000, 0x580F), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"ftand", two(0xF000, 0x540F), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"ftanl", two(0xF000, 0x400F), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"ftanp", two(0xF000, 0x4C0F), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"ftans", two(0xF000, 0x440F), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"ftanw", two(0xF000, 0x500F), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"ftanx", two(0xF000, 0x000F), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"ftanx", two(0xF000, 0x480F), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"ftanx", two(0xF000, 0x000F), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"ftanhb", two(0xF000, 0x5809), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"ftanhd", two(0xF000, 0x5409), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"ftanhl", two(0xF000, 0x4009), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"ftanhp", two(0xF000, 0x4C09), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"ftanhs", two(0xF000, 0x4409), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"ftanhw", two(0xF000, 0x5009), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"ftanhx", two(0xF000, 0x0009), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"ftanhx", two(0xF000, 0x4809), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"ftanhx", two(0xF000, 0x0009), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"ftentoxb", two(0xF000, 0x5812), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"ftentoxd", two(0xF000, 0x5412), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"ftentoxl", two(0xF000, 0x4012), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"ftentoxp", two(0xF000, 0x4C12), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"ftentoxs", two(0xF000, 0x4412), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"ftentoxw", two(0xF000, 0x5012), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"ftentoxx", two(0xF000, 0x0012), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"ftentoxx", two(0xF000, 0x4812), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"ftentoxx", two(0xF000, 0x0012), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"ftrapeq", two(0xF07C, 0x0001), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapf", two(0xF07C, 0x0000), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapge", two(0xF07C, 0x0013), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapgl", two(0xF07C, 0x0016), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapgle", two(0xF07C, 0x0017), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapgt", two(0xF07C, 0x0012), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftraple", two(0xF07C, 0x0015), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftraplt", two(0xF07C, 0x0014), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapne", two(0xF07C, 0x000E), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapnge", two(0xF07C, 0x001C), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapngl", two(0xF07C, 0x0019), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapngle", two(0xF07C, 0x0018), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapngt", two(0xF07C, 0x001D), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapnle", two(0xF07C, 0x001A), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapnlt", two(0xF07C, 0x001B), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapoge", two(0xF07C, 0x0003), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapogl", two(0xF07C, 0x0006), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapogt", two(0xF07C, 0x0002), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapole", two(0xF07C, 0x0005), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapolt", two(0xF07C, 0x0004), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapor", two(0xF07C, 0x0007), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapseq", two(0xF07C, 0x0011), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapsf", two(0xF07C, 0x0010), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapsne", two(0xF07C, 0x001E), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapst", two(0xF07C, 0x001F), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapt", two(0xF07C, 0x000F), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapueq", two(0xF07C, 0x0009), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapuge", two(0xF07C, 0x000B), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapugt", two(0xF07C, 0x000A), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapule", two(0xF07C, 0x000D), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapult", two(0xF07C, 0x000C), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapun", two(0xF07C, 0x0008), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+
+{"ftrapeqw", two(0xF07A, 0x0001), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapfw", two(0xF07A, 0x0000), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapgew", two(0xF07A, 0x0013), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapglw", two(0xF07A, 0x0016), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapglew", two(0xF07A, 0x0017), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapgtw", two(0xF07A, 0x0012), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftraplew", two(0xF07A, 0x0015), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapltw", two(0xF07A, 0x0014), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapnew", two(0xF07A, 0x000E), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapngew", two(0xF07A, 0x001C), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapnglw", two(0xF07A, 0x0019), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapnglew", two(0xF07A, 0x0018), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapngtw", two(0xF07A, 0x001D), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapnlew", two(0xF07A, 0x001A), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapnltw", two(0xF07A, 0x001B), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapogew", two(0xF07A, 0x0003), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapoglw", two(0xF07A, 0x0006), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapogtw", two(0xF07A, 0x0002), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapolew", two(0xF07A, 0x0005), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapoltw", two(0xF07A, 0x0004), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftraporw", two(0xF07A, 0x0007), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapseqw", two(0xF07A, 0x0011), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapsfw", two(0xF07A, 0x0010), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapsnew", two(0xF07A, 0x001E), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapstw", two(0xF07A, 0x001F), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftraptw", two(0xF07A, 0x000F), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapueqw", two(0xF07A, 0x0009), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapugew", two(0xF07A, 0x000B), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapugtw", two(0xF07A, 0x000A), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapulew", two(0xF07A, 0x000D), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapultw", two(0xF07A, 0x000C), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapunw", two(0xF07A, 0x0008), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+
+{"ftrapeql", two(0xF07B, 0x0001), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapfl", two(0xF07B, 0x0000), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapgel", two(0xF07B, 0x0013), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapgll", two(0xF07B, 0x0016), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapglel", two(0xF07B, 0x0017), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapgtl", two(0xF07B, 0x0012), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftraplel", two(0xF07B, 0x0015), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapltl", two(0xF07B, 0x0014), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapnel", two(0xF07B, 0x000E), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapngel", two(0xF07B, 0x001C), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapngll", two(0xF07B, 0x0019), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapnglel", two(0xF07B, 0x0018), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapngtl", two(0xF07B, 0x001D), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapnlel", two(0xF07B, 0x001A), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapnltl", two(0xF07B, 0x001B), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapogel", two(0xF07B, 0x0003), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapogll", two(0xF07B, 0x0006), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapogtl", two(0xF07B, 0x0002), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapolel", two(0xF07B, 0x0005), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapoltl", two(0xF07B, 0x0004), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftraporl", two(0xF07B, 0x0007), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapseql", two(0xF07B, 0x0011), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapsfl", two(0xF07B, 0x0010), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapsnel", two(0xF07B, 0x001E), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapstl", two(0xF07B, 0x001F), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftraptl", two(0xF07B, 0x000F), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapueql", two(0xF07B, 0x0009), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapugel", two(0xF07B, 0x000B), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapugtl", two(0xF07B, 0x000A), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapulel", two(0xF07B, 0x000D), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapultl", two(0xF07B, 0x000C), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapunl", two(0xF07B, 0x0008), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+
+{"ftstb", two(0xF000, 0x583A), two(0xF1C0, 0xFC7F), "Ii;b", mfloat },
+{"ftstd", two(0xF000, 0x543A), two(0xF1C0, 0xFC7F), "Ii;F", mfloat },
+{"ftstl", two(0xF000, 0x403A), two(0xF1C0, 0xFC7F), "Ii;l", mfloat },
+{"ftstp", two(0xF000, 0x4C3A), two(0xF1C0, 0xFC7F), "Ii;p", mfloat },
+{"ftsts", two(0xF000, 0x443A), two(0xF1C0, 0xFC7F), "Ii;f", mfloat },
+{"ftstw", two(0xF000, 0x503A), two(0xF1C0, 0xFC7F), "Ii;w", mfloat },
+{"ftstx", two(0xF000, 0x003A), two(0xF1C0, 0xE07F), "IiF8", mfloat },
+{"ftstx", two(0xF000, 0x483A), two(0xF1C0, 0xFC7F), "Ii;x", mfloat },
+
+{"ftwotoxb", two(0xF000, 0x5811), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"ftwotoxd", two(0xF000, 0x5411), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"ftwotoxl", two(0xF000, 0x4011), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"ftwotoxp", two(0xF000, 0x4C11), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"ftwotoxs", two(0xF000, 0x4411), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"ftwotoxw", two(0xF000, 0x5011), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"ftwotoxx", two(0xF000, 0x0011), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"ftwotoxx", two(0xF000, 0x4811), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"ftwotoxx", two(0xF000, 0x0011), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+/* Variable-sized float branches */
+
+{"fjeq", one(0xF081), one(0xF1FF), "IdBc", mfloat },
+{"fjf", one(0xF080), one(0xF1FF), "IdBc", mfloat },
+{"fjge", one(0xF093), one(0xF1FF), "IdBc", mfloat },
+{"fjgl", one(0xF096), one(0xF1FF), "IdBc", mfloat },
+{"fjgle", one(0xF097), one(0xF1FF), "IdBc", mfloat },
+{"fjgt", one(0xF092), one(0xF1FF), "IdBc", mfloat },
+{"fjle", one(0xF095), one(0xF1FF), "IdBc", mfloat },
+{"fjlt", one(0xF094), one(0xF1FF), "IdBc", mfloat },
+{"fjne", one(0xF08E), one(0xF1FF), "IdBc", mfloat },
+{"fjnge", one(0xF09C), one(0xF1FF), "IdBc", mfloat },
+{"fjngl", one(0xF099), one(0xF1FF), "IdBc", mfloat },
+{"fjngle", one(0xF098), one(0xF1FF), "IdBc", mfloat },
+{"fjngt", one(0xF09D), one(0xF1FF), "IdBc", mfloat },
+{"fjnle", one(0xF09A), one(0xF1FF), "IdBc", mfloat },
+{"fjnlt", one(0xF09B), one(0xF1FF), "IdBc", mfloat },
+{"fjoge", one(0xF083), one(0xF1FF), "IdBc", mfloat },
+{"fjogl", one(0xF086), one(0xF1FF), "IdBc", mfloat },
+{"fjogt", one(0xF082), one(0xF1FF), "IdBc", mfloat },
+{"fjole", one(0xF085), one(0xF1FF), "IdBc", mfloat },
+{"fjolt", one(0xF084), one(0xF1FF), "IdBc", mfloat },
+{"fjor", one(0xF087), one(0xF1FF), "IdBc", mfloat },
+{"fjseq", one(0xF091), one(0xF1FF), "IdBc", mfloat },
+{"fjsf", one(0xF090), one(0xF1FF), "IdBc", mfloat },
+{"fjsne", one(0xF09E), one(0xF1FF), "IdBc", mfloat },
+{"fjst", one(0xF09F), one(0xF1FF), "IdBc", mfloat },
+{"fjt", one(0xF08F), one(0xF1FF), "IdBc", mfloat },
+{"fjueq", one(0xF089), one(0xF1FF), "IdBc", mfloat },
+{"fjuge", one(0xF08B), one(0xF1FF), "IdBc", mfloat },
+{"fjugt", one(0xF08A), one(0xF1FF), "IdBc", mfloat },
+{"fjule", one(0xF08D), one(0xF1FF), "IdBc", mfloat },
+{"fjult", one(0xF08C), one(0xF1FF), "IdBc", mfloat },
+{"fjun", one(0xF088), one(0xF1FF), "IdBc", mfloat },
+/* float stuff ends here */
+
+{"illegal", one(0045374), one(0177777), "", m68000up },
+{"jmp", one(0047300), one(0177700), "!s", m68000up },
+{"jsr", one(0047200), one(0177700), "!s", m68000up },
+{"lea", one(0040700), one(0170700), "!sAd", m68000up },
+{"linkw", one(0047120), one(0177770), "As#w", m68000up },
+{"linkl", one(0044010), one(0177770), "As#l", m68020up },
+{"link", one(0047120), one(0177770), "As#w", m68000up },
+{"link", one(0044010), one(0177770), "As#l", m68020up },
+
+{"lslb", one(0160410), one(0170770), "QdDs", m68000up }, /* lsrb #Q, Ds */
+{"lslb", one(0160450), one(0170770), "DdDs", m68000up }, /* lsrb Dd, Ds */
+{"lslw", one(0160510), one(0170770), "QdDs", m68000up }, /* lsrb #Q, Ds */
+{"lslw", one(0160550), one(0170770), "DdDs", m68000up }, /* lsrb Dd, Ds */
+{"lslw", one(0161700), one(0177700), "~s", m68000up }, /* Shift memory */
+{"lsll", one(0160610), one(0170770), "QdDs", m68000up }, /* lsrb #Q, Ds */
+{"lsll", one(0160650), one(0170770), "DdDs", m68000up }, /* lsrb Dd, Ds */
+
+{"lsrb", one(0160010), one(0170770), "QdDs", m68000up }, /* lsrb #Q, Ds */
+{"lsrb", one(0160050), one(0170770), "DdDs", m68000up }, /* lsrb Dd, Ds */
+{"lsrl", one(0160210), one(0170770), "QdDs", m68000up }, /* lsrb #Q, Ds */
+{"lsrl", one(0160250), one(0170770), "DdDs", m68000up }, /* lsrb #Q, Ds */
+{"lsrw", one(0160110), one(0170770), "QdDs", m68000up }, /* lsrb #Q, Ds */
+{"lsrw", one(0160150), one(0170770), "DdDs", m68000up }, /* lsrb #Q, Ds */
+{"lsrw", one(0161300), one(0177700), "~s", m68000up }, /* Shift memory */
+
+{"moveal", one(0020100), one(0170700), "*lAd", m68000up },
+{"moveaw", one(0030100), one(0170700), "*wAd", m68000up },
+{"moveb", one(0010000), one(0170000), ";b$d", m68000up }, /* move */
+{"movel", one(0070000), one(0170400), "MsDd", m68000up }, /* moveq written as move */
+{"movel", one(0020000), one(0170000), "*l$d", m68000up },
+{"movel", one(0020100), one(0170700), "*lAd", m68000up },
+{"movel", one(0047140), one(0177770), "AsUd", m68000up }, /* move to USP */
+{"movel", one(0047150), one(0177770), "UdAs", m68000up }, /* move from USP */
+
+{"movec", one(0047173), one(0177777), "R1Jj", m68010up },
+{"movec", one(0047173), one(0177777), "R1#j", m68010up },
+{"movec", one(0047172), one(0177777), "JjR1", m68010up },
+{"movec", one(0047172), one(0177777), "#jR1", m68010up },
+
+/* JF added these next four for the assembler */
+{"moveml", one(0044300), one(0177700), "Lw&s", m68000up }, /* movem reg to mem. */
+{"moveml", one(0044340), one(0177770), "lw-s", m68000up }, /* movem reg to autodecrement. */
+{"moveml", one(0046300), one(0177700), "!sLw", m68000up }, /* movem mem to reg. */
+{"moveml", one(0046330), one(0177770), "+sLw", m68000up }, /* movem autoinc to reg. */
+
+{"moveml", one(0044300), one(0177700), "#w&s", m68000up }, /* movem reg to mem. */
+{"moveml", one(0044340), one(0177770), "#w-s", m68000up }, /* movem reg to autodecrement. */
+{"moveml", one(0046300), one(0177700), "!s#w", m68000up }, /* movem mem to reg. */
+{"moveml", one(0046330), one(0177770), "+s#w", m68000up }, /* movem autoinc to reg. */
+
+/* JF added these next four for the assembler */
+{"movemw", one(0044200), one(0177700), "Lw&s", m68000up }, /* movem reg to mem. */
+{"movemw", one(0044240), one(0177770), "lw-s", m68000up }, /* movem reg to autodecrement. */
+{"movemw", one(0046200), one(0177700), "!sLw", m68000up }, /* movem mem to reg. */
+{"movemw", one(0046230), one(0177770), "+sLw", m68000up }, /* movem autoinc to reg. */
+
+{"movemw", one(0044200), one(0177700), "#w&s", m68000up }, /* movem reg to mem. */
+{"movemw", one(0044240), one(0177770), "#w-s", m68000up }, /* movem reg to autodecrement. */
+{"movemw", one(0046200), one(0177700), "!s#w", m68000up }, /* movem mem to reg. */
+{"movemw", one(0046230), one(0177770), "+s#w", m68000up }, /* movem autoinc to reg. */
+
+{"movepl", one(0000510), one(0170770), "dsDd", m68000up }, /* memory to register */
+{"movepl", one(0000710), one(0170770), "Ddds", m68000up }, /* register to memory */
+{"movepw", one(0000410), one(0170770), "dsDd", m68000up }, /* memory to register */
+{"movepw", one(0000610), one(0170770), "Ddds", m68000up }, /* register to memory */
+{"moveq", one(0070000), one(0170400), "MsDd", m68000up },
+{"movew", one(0030000), one(0170000), "*w$d", m68000up },
+{"movew", one(0030100), one(0170700), "*wAd", m68000up }, /* movea, written as move */
+{"movew", one(0040300), one(0177700), "Ss$s", m68000up }, /* Move from sr */
+{"movew", one(0041300), one(0177700), "Cs$s", m68010up }, /* Move from ccr */
+{"movew", one(0042300), one(0177700), ";wCd", m68000up }, /* move to ccr */
+{"movew", one(0043300), one(0177700), ";wSd", m68000up }, /* move to sr */
+
+{"movesb", two(0007000, 0), two(0177700, 07777), "~sR1", m68010up }, /* moves from memory */
+{"movesb", two(0007000, 04000), two(0177700, 07777), "R1~s", m68010up }, /* moves to memory */
+{"movesl", two(0007200, 0), two(0177700, 07777), "~sR1", m68010up }, /* moves from memory */
+{"movesl", two(0007200, 04000), two(0177700, 07777), "R1~s", m68010up }, /* moves to memory */
+{"movesw", two(0007100, 0), two(0177700, 07777), "~sR1", m68010up }, /* moves from memory */
+{"movesw", two(0007100, 04000), two(0177700, 07777), "R1~s", m68010up }, /* moves to memory */
+
+{"move16", two(0xf620, 0x8000), two(0xfff8, 0x8fff), "+s+1", m68040 },
+{"move16", one(0xf600), one(0xfff8), "+s_L", m68040 },
+{"move16", one(0xf608), one(0xfff8), "_L+s", m68040 },
+{"move16", one(0xf610), one(0xfff8), "as_L", m68040 },
+{"move16", one(0xf618), one(0xfff8), "_Las", m68040 },
+
+{"mulsl", two(0046000, 004000), two(0177700, 0107770), ";lD1", m68020up },
+{"mulsl", two(0046000, 006000), two(0177700, 0107770), ";lD3D1", m68020up },
+{"mulsw", one(0140700), one(0170700), ";wDd", m68000up },
+{"muls", one(0140700), one(0170700), ";wDd", m68000up },
+{"mulul", two(0046000, 000000), two(0177700, 0107770), ";lD1", m68020up },
+{"mulul", two(0046000, 002000), two(0177700, 0107770), ";lD3D1", m68020up },
+{"muluw", one(0140300), one(0170700), ";wDd", m68000up },
+{"mulu", one(0140300), one(0170700), ";wDd", m68000up },
+{"nbcd", one(0044000), one(0177700), "$s", m68000up },
+{"negb", one(0042000), one(0177700), "$s", m68000up },
+{"negl", one(0042200), one(0177700), "$s", m68000up },
+{"negw", one(0042100), one(0177700), "$s", m68000up },
+{"negxb", one(0040000), one(0177700), "$s", m68000up },
+{"negxl", one(0040200), one(0177700), "$s", m68000up },
+{"negxw", one(0040100), one(0177700), "$s", m68000up },
+{"nop", one(0047161), one(0177777), "", m68000up },
+{"notb", one(0043000), one(0177700), "$s", m68000up },
+{"notl", one(0043200), one(0177700), "$s", m68000up },
+{"notw", one(0043100), one(0177700), "$s", m68000up },
+
+{"orb", one(0000000), one(0177700), "#b$s", m68000up }, /* ori written as or */
+{"orb", one(0000074), one(0177777), "#bCs", m68000up }, /* ori to ccr */
+{"orb", one(0100000), one(0170700), ";bDd", m68000up }, /* memory to register */
+{"orb", one(0100400), one(0170700), "Dd~s", m68000up }, /* register to memory */
+{"orib", one(0000000), one(0177700), "#b$s", m68000up },
+{"orib", one(0000074), one(0177777), "#bCs", m68000up }, /* ori to ccr */
+{"oril", one(0000200), one(0177700), "#l$s", m68000up },
+{"oriw", one(0000100), one(0177700), "#w$s", m68000up },
+{"oriw", one(0000174), one(0177777), "#wSs", m68000up }, /* ori to sr */
+{"orl", one(0000200), one(0177700), "#l$s", m68000up },
+{"orl", one(0100200), one(0170700), ";lDd", m68000up }, /* memory to register */
+{"orl", one(0100600), one(0170700), "Dd~s", m68000up }, /* register to memory */
+{"orw", one(0000100), one(0177700), "#w$s", m68000up },
+{"orw", one(0000174), one(0177777), "#wSs", m68000up }, /* ori to sr */
+{"orw", one(0100100), one(0170700), ";wDd", m68000up }, /* memory to register */
+{"orw", one(0100500), one(0170700), "Dd~s", m68000up }, /* register to memory */
+
+{"pack", one(0100500), one(0170770), "DsDd#w", m68020up }, /* pack Ds, Dd, #w */
+{"pack", one(0100510), one(0170770), "-s-d#w", m68020up }, /* pack -(As), -(Ad), #w */
+
+#ifndef NO_68851
+{"pbac", one(0xf0c7), one(0xffbf), "Bc", m68851 },
+{"pbacw", one(0xf087), one(0xffbf), "Bc", m68851 },
+{"pbas", one(0xf0c6), one(0xffbf), "Bc", m68851 },
+{"pbasw", one(0xf086), one(0xffbf), "Bc", m68851 },
+{"pbbc", one(0xf0c1), one(0xffbf), "Bc", m68851 },
+{"pbbcw", one(0xf081), one(0xffbf), "Bc", m68851 },
+{"pbbs", one(0xf0c0), one(0xffbf), "Bc", m68851 },
+{"pbbsw", one(0xf080), one(0xffbf), "Bc", m68851 },
+{"pbcc", one(0xf0cf), one(0xffbf), "Bc", m68851 },
+{"pbccw", one(0xf08f), one(0xffbf), "Bc", m68851 },
+{"pbcs", one(0xf0ce), one(0xffbf), "Bc", m68851 },
+{"pbcsw", one(0xf08e), one(0xffbf), "Bc", m68851 },
+{"pbgc", one(0xf0cd), one(0xffbf), "Bc", m68851 },
+{"pbgcw", one(0xf08d), one(0xffbf), "Bc", m68851 },
+{"pbgs", one(0xf0cc), one(0xffbf), "Bc", m68851 },
+{"pbgsw", one(0xf08c), one(0xffbf), "Bc", m68851 },
+{"pbic", one(0xf0cb), one(0xffbf), "Bc", m68851 },
+{"pbicw", one(0xf08b), one(0xffbf), "Bc", m68851 },
+{"pbis", one(0xf0ca), one(0xffbf), "Bc", m68851 },
+{"pbisw", one(0xf08a), one(0xffbf), "Bc", m68851 },
+{"pblc", one(0xf0c3), one(0xffbf), "Bc", m68851 },
+{"pblcw", one(0xf083), one(0xffbf), "Bc", m68851 },
+{"pbls", one(0xf0c2), one(0xffbf), "Bc", m68851 },
+{"pblsw", one(0xf082), one(0xffbf), "Bc", m68851 },
+{"pbsc", one(0xf0c5), one(0xffbf), "Bc", m68851 },
+{"pbscw", one(0xf085), one(0xffbf), "Bc", m68851 },
+{"pbss", one(0xf0c4), one(0xffbf), "Bc", m68851 },
+{"pbssw", one(0xf084), one(0xffbf), "Bc", m68851 },
+{"pbwc", one(0xf0c9), one(0xffbf), "Bc", m68851 },
+{"pbwcw", one(0xf089), one(0xffbf), "Bc", m68851 },
+{"pbws", one(0xf0c8), one(0xffbf), "Bc", m68851 },
+{"pbwsw", one(0xf088), one(0xffbf), "Bc", m68851 },
+
+{"pdbac", two(0xf048, 0x0007), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbas", two(0xf048, 0x0006), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbbc", two(0xf048, 0x0001), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbbs", two(0xf048, 0x0000), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbcc", two(0xf048, 0x000f), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbcs", two(0xf048, 0x000e), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbgc", two(0xf048, 0x000d), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbgs", two(0xf048, 0x000c), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbic", two(0xf048, 0x000b), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbis", two(0xf048, 0x000a), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdblc", two(0xf048, 0x0003), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbls", two(0xf048, 0x0002), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbsc", two(0xf048, 0x0005), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbss", two(0xf048, 0x0004), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbwc", two(0xf048, 0x0009), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbws", two(0xf048, 0x0008), two(0xfff8, 0xffff), "DsBw", m68851 },
+#endif /* NO_68851 */
+
+{"pea", one(0044100), one(0177700), "!s", m68000up },
+
+#ifndef NO_68851
+{"pflusha", two(0xf000, 0x2400), two(0xffff, 0xffff), "", m68030 | m68851 },
+{"pflusha", one(0xf510), one(0xfff8), "", m68040 },
+
+{"pflush", two(0xf000, 0x3010), two(0xffc0, 0xfe10), "T3T9", m68030 | m68851 },
+{"pflush", two(0xf000, 0x3810), two(0xffc0, 0xfe10), "T3T9&s", m68030 | m68851 },
+{"pflush", two(0xf000, 0x3008), two(0xffc0, 0xfe18), "D3T9", m68030 | m68851 },
+{"pflush", two(0xf000, 0x3808), two(0xffc0, 0xfe18), "D3T9&s", m68030 | m68851 },
+{"pflush", two(0xf000, 0x3000), two(0xffc0, 0xfe1e), "f3T9", m68030 | m68851 },
+{"pflush", two(0xf000, 0x3800), two(0xffc0, 0xfe1e), "f3T9&s", m68030 | m68851 },
+{"pflush", one(0xf500), one(0xfff8), "As", m68040 },
+
+{"pflushan", one(0xf518), one(0xfff8), "", m68040 },
+{"pflushn", one(0xf508), one(0xfff8), "As", m68040 },
+
+{"pflushr", two(0xf000, 0xa000), two(0xffc0, 0xffff), "|s", m68851 },
+
+{"pflushs", two(0xf000, 0x3410), two(0xfff8, 0xfe10), "T3T9", m68851 },
+{"pflushs", two(0xf000, 0x3c10), two(0xfff8, 0xfe00), "T3T9&s", m68851 },
+{"pflushs", two(0xf000, 0x3408), two(0xfff8, 0xfe18), "D3T9", m68851 },
+{"pflushs", two(0xf000, 0x3c08), two(0xfff8, 0xfe18), "D3T9&s", m68851 },
+{"pflushs", two(0xf000, 0x3400), two(0xfff8, 0xfe1e), "f3T9", m68851 },
+{"pflushs", two(0xf000, 0x3c00), two(0xfff8, 0xfe1e), "f3T9&s", m68851 },
+
+{"ploadr", two(0xf000, 0x2210), two(0xffc0, 0xfff0), "T3&s", m68030 | m68851 },
+{"ploadr", two(0xf000, 0x2208), two(0xffc0, 0xfff8), "D3&s", m68030 | m68851 },
+{"ploadr", two(0xf000, 0x2200), two(0xffc0, 0xfffe), "f3&s", m68030 | m68851 },
+{"ploadw", two(0xf000, 0x2010), two(0xffc0, 0xfff0), "T3&s", m68030 | m68851 },
+{"ploadw", two(0xf000, 0x2008), two(0xffc0, 0xfff8), "D3&s", m68030 | m68851 },
+{"ploadw", two(0xf000, 0x2000), two(0xffc0, 0xfffe), "f3&s", m68030 | m68851 },
+
+/* TC, CRP, DRP, SRP, CAL, VAL, SCC, AC */
+{"pmove", two(0xf000, 0x4000), two(0xffc0, 0xe3ff), "*sP8", m68030 | m68851 },
+{"pmove", two(0xf000, 0x4200), two(0xffc0, 0xe3ff), "P8%s", m68030 | m68851 },
+{"pmove", two(0xf000, 0x4000), two(0xffc0, 0xe3ff), "|sW8", m68030 | m68851 },
+{"pmove", two(0xf000, 0x4200), two(0xffc0, 0xe3ff), "W8~s", m68030 | m68851 },
+
+/* BADx, BACx */
+{"pmove", two(0xf000, 0x6200), two(0xffc0, 0xe3e3), "*sX3", m68030 | m68851 },
+{"pmove", two(0xf000, 0x6000), two(0xffc0, 0xe3e3), "X3%s", m68030 | m68851 },
+
+/* PSR, PCSR */
+/* {"pmove", two(0xf000, 0x6100), two(oxffc0, oxffff), "*sZ8", m68030 | m68851 }, */
+{"pmove", two(0xf000, 0x6000), two(0xffc0, 0xffff), "*sY8", m68030 | m68851 },
+{"pmove", two(0xf000, 0x6200), two(0xffc0, 0xffff), "Y8%s", m68030 | m68851 },
+{"pmove", two(0xf000, 0x6600), two(0xffc0, 0xffff), "Z8%s", m68030 | m68851 },
+
+{"prestore", one(0xf140), one(0xffc0), "&s", m68851 },
+{"prestore", one(0xf158), one(0xfff8), "+s", m68851 },
+{"psave", one(0xf100), one(0xffc0), "&s", m68851 },
+{"psave", one(0xf100), one(0xffc0), "+s", m68851 },
+
+{"psac", two(0xf040, 0x0007), two(0xffc0, 0xffff), "@s", m68851 },
+{"psas", two(0xf040, 0x0006), two(0xffc0, 0xffff), "@s", m68851 },
+{"psbc", two(0xf040, 0x0001), two(0xffc0, 0xffff), "@s", m68851 },
+{"psbs", two(0xf040, 0x0000), two(0xffc0, 0xffff), "@s", m68851 },
+{"pscc", two(0xf040, 0x000f), two(0xffc0, 0xffff), "@s", m68851 },
+{"pscs", two(0xf040, 0x000e), two(0xffc0, 0xffff), "@s", m68851 },
+{"psgc", two(0xf040, 0x000d), two(0xffc0, 0xffff), "@s", m68851 },
+{"psgs", two(0xf040, 0x000c), two(0xffc0, 0xffff), "@s", m68851 },
+{"psic", two(0xf040, 0x000b), two(0xffc0, 0xffff), "@s", m68851 },
+{"psis", two(0xf040, 0x000a), two(0xffc0, 0xffff), "@s", m68851 },
+{"pslc", two(0xf040, 0x0003), two(0xffc0, 0xffff), "@s", m68851 },
+{"psls", two(0xf040, 0x0002), two(0xffc0, 0xffff), "@s", m68851 },
+{"pssc", two(0xf040, 0x0005), two(0xffc0, 0xffff), "@s", m68851 },
+{"psss", two(0xf040, 0x0004), two(0xffc0, 0xffff), "@s", m68851 },
+{"pswc", two(0xf040, 0x0009), two(0xffc0, 0xffff), "@s", m68851 },
+{"psws", two(0xf040, 0x0008), two(0xffc0, 0xffff), "@s", m68851 },
+
+{"ptestr", two(0xf000, 0x8210), two(0xffc0, 0xe3f0), "T3&sQ8", m68030 | m68851 },
+{"ptestr", two(0xf000, 0x8310), two(0xffc0, 0xe310), "T3&sQ8A9", m68030 | m68851 },
+{"ptestr", two(0xf000, 0x8208), two(0xffc0, 0xe3f8), "D3&sQ8", m68030 | m68851 },
+{"ptestr", two(0xf000, 0x8308), two(0xffc0, 0xe318), "D3&sQ8A9", m68030 | m68851 },
+{"ptestr", two(0xf000, 0x8200), two(0xffc0, 0xe3fe), "f3&sQ8", m68030 | m68851 },
+{"ptestr", two(0xf000, 0x8300), two(0xffc0, 0xe31e), "f3&sQ8A9", m68030 | m68851 },
+
+{"ptestr", one(0xf568), one(0xfff8), "As", m68040 },
+
+{"ptestw", two(0xf000, 0x8010), two(0xffc0, 0xe3f0), "T3&sQ8", m68030 | m68851 },
+{"ptestw", two(0xf000, 0x8110), two(0xffc0, 0xe310), "T3&sQ8A9", m68030 | m68851 },
+{"ptestw", two(0xf000, 0x8008), two(0xffc0, 0xe3f8), "D3&sQ8", m68030 | m68851 },
+{"ptestw", two(0xf000, 0x8108), two(0xffc0, 0xe318), "D3&sQ8A9", m68030 | m68851 },
+{"ptestw", two(0xf000, 0x8000), two(0xffc0, 0xe3fe), "f3&sQ8", m68030 | m68851 },
+{"ptestw", two(0xf000, 0x8100), two(0xffc0, 0xe31e), "f3&sQ8A9", m68030 | m68851 },
+
+{"ptestw", one(0xf548), one(0xfff8), "As", m68040 },
+
+{"ptrapacw", two(0xf07a, 0x0007), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapacl", two(0xf07b, 0x0007), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapac", two(0xf07c, 0x0007), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapasw", two(0xf07a, 0x0006), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapasl", two(0xf07b, 0x0006), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapas", two(0xf07c, 0x0006), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapbcw", two(0xf07a, 0x0001), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapbcl", two(0xf07b, 0x0001), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapbc", two(0xf07c, 0x0001), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapbsw", two(0xf07a, 0x0000), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapbsl", two(0xf07b, 0x0000), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapbs", two(0xf07c, 0x0000), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapccw", two(0xf07a, 0x000f), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapccl", two(0xf07b, 0x000f), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapcc", two(0xf07c, 0x000f), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapcsw", two(0xf07a, 0x000e), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapcsl", two(0xf07b, 0x000e), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapcs", two(0xf07c, 0x000e), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapgcw", two(0xf07a, 0x000d), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapgcl", two(0xf07b, 0x000d), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapgc", two(0xf07c, 0x000d), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapgsw", two(0xf07a, 0x000c), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapgsl", two(0xf07b, 0x000c), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapgs", two(0xf07c, 0x000c), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapicw", two(0xf07a, 0x000b), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapicl", two(0xf07b, 0x000b), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapic", two(0xf07c, 0x000b), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapisw", two(0xf07a, 0x000a), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapisl", two(0xf07b, 0x000a), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapis", two(0xf07c, 0x000a), two(0xffff, 0xffff), "", m68851 },
+
+{"ptraplcw", two(0xf07a, 0x0003), two(0xffff, 0xffff), "#w", m68851 },
+{"ptraplcl", two(0xf07b, 0x0003), two(0xffff, 0xffff), "#l", m68851 },
+{"ptraplc", two(0xf07c, 0x0003), two(0xffff, 0xffff), "", m68851 },
+
+{"ptraplsw", two(0xf07a, 0x0002), two(0xffff, 0xffff), "#w", m68851 },
+{"ptraplsl", two(0xf07b, 0x0002), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapls", two(0xf07c, 0x0002), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapscw", two(0xf07a, 0x0005), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapscl", two(0xf07b, 0x0005), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapsc", two(0xf07c, 0x0005), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapssw", two(0xf07a, 0x0004), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapssl", two(0xf07b, 0x0004), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapss", two(0xf07c, 0x0004), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapwcw", two(0xf07a, 0x0009), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapwcl", two(0xf07b, 0x0009), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapwc", two(0xf07c, 0x0009), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapwsw", two(0xf07a, 0x0008), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapwsl", two(0xf07b, 0x0008), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapws", two(0xf07c, 0x0008), two(0xffff, 0xffff), "", m68851 },
+
+{"pvalid", two(0xf000, 0x2800), two(0xffc0, 0xffff), "Vs&s", m68851 },
+{"pvalid", two(0xf000, 0x2c00), two(0xffc0, 0xfff8), "A3&s", m68851 },
+
+#endif /* NO_68851 */
+
+{"reset", one(0047160), one(0177777), "", m68000up },
+
+{"rolb", one(0160430), one(0170770), "QdDs", m68000up }, /* rorb #Q, Ds */
+{"rolb", one(0160470), one(0170770), "DdDs", m68000up }, /* rorb Dd, Ds */
+{"roll", one(0160630), one(0170770), "QdDs", m68000up }, /* rorb #Q, Ds */
+{"roll", one(0160670), one(0170770), "DdDs", m68000up }, /* rorb Dd, Ds */
+{"rolw", one(0160530), one(0170770), "QdDs", m68000up }, /* rorb #Q, Ds */
+{"rolw", one(0160570), one(0170770), "DdDs", m68000up }, /* rorb Dd, Ds */
+{"rolw", one(0163700), one(0177700), "~s", m68000up }, /* Rotate memory */
+{"rorb", one(0160030), one(0170770), "QdDs", m68000up }, /* rorb #Q, Ds */
+{"rorb", one(0160070), one(0170770), "DdDs", m68000up }, /* rorb Dd, Ds */
+{"rorl", one(0160230), one(0170770), "QdDs", m68000up }, /* rorb #Q, Ds */
+{"rorl", one(0160270), one(0170770), "DdDs", m68000up }, /* rorb Dd, Ds */
+{"rorw", one(0160130), one(0170770), "QdDs", m68000up }, /* rorb #Q, Ds */
+{"rorw", one(0160170), one(0170770), "DdDs", m68000up }, /* rorb Dd, Ds */
+{"rorw", one(0163300), one(0177700), "~s", m68000up }, /* Rotate memory */
+
+{"roxlb", one(0160420), one(0170770), "QdDs", m68000up }, /* roxrb #Q, Ds */
+{"roxlb", one(0160460), one(0170770), "DdDs", m68000up }, /* roxrb Dd, Ds */
+{"roxll", one(0160620), one(0170770), "QdDs", m68000up }, /* roxrb #Q, Ds */
+{"roxll", one(0160660), one(0170770), "DdDs", m68000up }, /* roxrb Dd, Ds */
+{"roxlw", one(0160520), one(0170770), "QdDs", m68000up }, /* roxrb #Q, Ds */
+{"roxlw", one(0160560), one(0170770), "DdDs", m68000up }, /* roxrb Dd, Ds */
+{"roxlw", one(0162700), one(0177700), "~s", m68000up }, /* Rotate memory */
+{"roxrb", one(0160020), one(0170770), "QdDs", m68000up }, /* roxrb #Q, Ds */
+{"roxrb", one(0160060), one(0170770), "DdDs", m68000up }, /* roxrb Dd, Ds */
+{"roxrl", one(0160220), one(0170770), "QdDs", m68000up }, /* roxrb #Q, Ds */
+{"roxrl", one(0160260), one(0170770), "DdDs", m68000up }, /* roxrb Dd, Ds */
+{"roxrw", one(0160120), one(0170770), "QdDs", m68000up }, /* roxrb #Q, Ds */
+{"roxrw", one(0160160), one(0170770), "DdDs", m68000up }, /* roxrb Dd, Ds */
+{"roxrw", one(0162300), one(0177700), "~s", m68000up }, /* Rotate memory */
+
+{"rtd", one(0047164), one(0177777), "#w", m68010up },
+{"rte", one(0047163), one(0177777), "", m68000up },
+{"rtm", one(0003300), one(0177760), "Rs", m68020 },
+{"rtr", one(0047167), one(0177777), "", m68000up },
+{"rts", one(0047165), one(0177777), "", m68000up },
+
+{"sbcd", one(0100400), one(0170770), "DsDd", m68000up },
+{"sbcd", one(0100410), one(0170770), "-s-d", m68000up },
+
+{"scc", one(0052300), one(0177700), "$s", m68000up },
+{"scs", one(0052700), one(0177700), "$s", m68000up },
+{"seq", one(0053700), one(0177700), "$s", m68000up },
+{"sf", one(0050700), one(0177700), "$s", m68000up },
+{"sge", one(0056300), one(0177700), "$s", m68000up },
+{"sfge", one(0056300), one(0177700), "$s", m68000up },
+{"sgt", one(0057300), one(0177700), "$s", m68000up },
+{"sfgt", one(0057300), one(0177700), "$s", m68000up },
+{"shi", one(0051300), one(0177700), "$s", m68000up },
+{"sle", one(0057700), one(0177700), "$s", m68000up },
+{"sfle", one(0057700), one(0177700), "$s", m68000up },
+{"sls", one(0051700), one(0177700), "$s", m68000up },
+{"slt", one(0056700), one(0177700), "$s", m68000up },
+{"sflt", one(0056700), one(0177700), "$s", m68000up },
+{"smi", one(0055700), one(0177700), "$s", m68000up },
+{"sne", one(0053300), one(0177700), "$s", m68000up },
+{"sfneq", one(0053300), one(0177700), "$s", m68000up },
+{"spl", one(0055300), one(0177700), "$s", m68000up },
+{"st", one(0050300), one(0177700), "$s", m68000up },
+{"svc", one(0054300), one(0177700), "$s", m68000up },
+{"svs", one(0054700), one(0177700), "$s", m68000up },
+
+{"stop", one(0047162), one(0177777), "#w", m68000up },
+
+{"subal", one(0110700), one(0170700), "*lAd", m68000up },
+{"subaw", one(0110300), one(0170700), "*wAd", m68000up },
+{"subb", one(0050400), one(0170700), "Qd%s", m68000up }, /* subq written as sub */
+{"subb", one(0002000), one(0177700), "#b$s", m68000up }, /* subi written as sub */
+{"subb", one(0110000), one(0170700), ";bDd", m68000up }, /* subb ? ?, Dd */
+{"subb", one(0110400), one(0170700), "Dd~s", m68000up }, /* subb Dd, ? ? */
+{"subib", one(0002000), one(0177700), "#b$s", m68000up },
+{"subil", one(0002200), one(0177700), "#l$s", m68000up },
+{"subiw", one(0002100), one(0177700), "#w$s", m68000up },
+{"subl", one(0050600), one(0170700), "Qd%s", m68000up },
+{"subl", one(0002200), one(0177700), "#l$s", m68000up },
+{"subl", one(0110700), one(0170700), "*lAd", m68000up },
+{"subl", one(0110200), one(0170700), "*lDd", m68000up },
+{"subl", one(0110600), one(0170700), "Dd~s", m68000up },
+{"subqb", one(0050400), one(0170700), "Qd%s", m68000up },
+{"subql", one(0050600), one(0170700), "Qd%s", m68000up },
+{"subqw", one(0050500), one(0170700), "Qd%s", m68000up },
+{"subw", one(0050500), one(0170700), "Qd%s", m68000up },
+{"subw", one(0002100), one(0177700), "#w$s", m68000up },
+{"subw", one(0110100), one(0170700), "*wDd", m68000up },
+{"subw", one(0110300), one(0170700), "*wAd", m68000up }, /* suba written as sub */
+{"subw", one(0110500), one(0170700), "Dd~s", m68000up },
+{"subxb", one(0110400), one(0170770), "DsDd", m68000up }, /* subxb Ds, Dd */
+{"subxb", one(0110410), one(0170770), "-s-d", m68000up }, /* subxb -(As), -(Ad) */
+{"subxl", one(0110600), one(0170770), "DsDd", m68000up },
+{"subxl", one(0110610), one(0170770), "-s-d", m68000up },
+{"subxw", one(0110500), one(0170770), "DsDd", m68000up },
+{"subxw", one(0110510), one(0170770), "-s-d", m68000up },
+
+{"swap", one(0044100), one(0177770), "Ds", m68000up },
+
+{"tas", one(0045300), one(0177700), "$s", m68000up },
+{"trap", one(0047100), one(0177760), "Ts", m68000up },
+
+{"trapcc", one(0052374), one(0177777), "", m68020up },
+{"trapcs", one(0052774), one(0177777), "", m68020up },
+{"trapeq", one(0053774), one(0177777), "", m68020up },
+{"trapf", one(0050774), one(0177777), "", m68020up },
+{"trapge", one(0056374), one(0177777), "", m68020up },
+{"trapgt", one(0057374), one(0177777), "", m68020up },
+{"traphi", one(0051374), one(0177777), "", m68020up },
+{"traple", one(0057774), one(0177777), "", m68020up },
+{"trapls", one(0051774), one(0177777), "", m68020up },
+{"traplt", one(0056774), one(0177777), "", m68020up },
+{"trapmi", one(0055774), one(0177777), "", m68020up },
+{"trapne", one(0053374), one(0177777), "", m68020up },
+{"trappl", one(0055374), one(0177777), "", m68020up },
+{"trapt", one(0050374), one(0177777), "", m68020up },
+{"trapvc", one(0054374), one(0177777), "", m68020up },
+{"trapvs", one(0054774), one(0177777), "", m68020up },
+
+{"trapcc.w", one(0052372), one(0177777), "", m68020up },
+{"trapcs.w", one(0052772), one(0177777), "", m68020up },
+{"trapeq.w", one(0053772), one(0177777), "", m68020up },
+{"trapf.w", one(0050772), one(0177777), "", m68020up },
+{"trapge.w", one(0056372), one(0177777), "", m68020up },
+{"trapgt.w", one(0057372), one(0177777), "", m68020up },
+{"traphi.w", one(0051372), one(0177777), "", m68020up },
+{"traple.w", one(0057772), one(0177777), "", m68020up },
+{"trapls.w", one(0051772), one(0177777), "", m68020up },
+{"traplt.w", one(0056772), one(0177777), "", m68020up },
+{"trapmi.w", one(0055772), one(0177777), "", m68020up },
+{"trapne.w", one(0053372), one(0177777), "", m68020up },
+{"trappl.w", one(0055372), one(0177777), "", m68020up },
+{"trapt.w", one(0050372), one(0177777), "", m68020up },
+{"trapvc.w", one(0054372), one(0177777), "", m68020up },
+{"trapvs.w", one(0054772), one(0177777), "", m68020up },
+
+{"trapcc.l", one(0052373), one(0177777), "", m68020up },
+{"trapcs.l", one(0052773), one(0177777), "", m68020up },
+{"trapeq.l", one(0053773), one(0177777), "", m68020up },
+{"trapf.l", one(0050773), one(0177777), "", m68020up },
+{"trapge.l", one(0056373), one(0177777), "", m68020up },
+{"trapgt.l", one(0057373), one(0177777), "", m68020up },
+{"traphi.l", one(0051373), one(0177777), "", m68020up },
+{"traple.l", one(0057773), one(0177777), "", m68020up },
+{"trapls.l", one(0051773), one(0177777), "", m68020up },
+{"traplt.l", one(0056773), one(0177777), "", m68020up },
+{"trapmi.l", one(0055773), one(0177777), "", m68020up },
+{"trapne.l", one(0053373), one(0177777), "", m68020up },
+{"trappl.l", one(0055373), one(0177777), "", m68020up },
+{"trapt.l", one(0050373), one(0177777), "", m68020up },
+{"trapvc.l", one(0054373), one(0177777), "", m68020up },
+{"trapvs.l", one(0054773), one(0177777), "", m68020up },
+
+{"trapv", one(0047166), one(0177777), "", m68000up },
+
+{"tstb", one(0045000), one(0177700), ";b", m68000up },
+{"tstw", one(0045100), one(0177700), "*w", m68000up },
+{"tstl", one(0045200), one(0177700), "*l", m68000up },
+
+{"unlk", one(0047130), one(0177770), "As", m68000up },
+{"unpk", one(0100600), one(0170770), "DsDd#w", m68020up },
+{"unpk", one(0100610), one(0170770), "-s-d#w", m68020up },
+
+/* Variable-sized branches */
+
+{"jbsr", one(0060400), one(0177400), "Bg", m68000up },
+{"jbsr", one(0047200), one(0177700), "!s", m68000up },
+{"jra", one(0060000), one(0177400), "Bg", m68000up },
+{"jra", one(0047300), one(0177700), "!s", m68000up },
+
+{"jhi", one(0061000), one(0177400), "Bg", m68000up },
+{"jls", one(0061400), one(0177400), "Bg", m68000up },
+{"jcc", one(0062000), one(0177400), "Bg", m68000up },
+{"jfnlt", one(0062000), one(0177400), "Bg", m68000up }, /* apparently a sun alias */
+{"jcs", one(0062400), one(0177400), "Bg", m68000up },
+{"jne", one(0063000), one(0177400), "Bg", m68000up },
+{"jeq", one(0063400), one(0177400), "Bg", m68000up },
+{"jfeq", one(0063400), one(0177400), "Bg", m68000up }, /* apparently a sun alias */
+{"jvc", one(0064000), one(0177400), "Bg", m68000up },
+{"jvs", one(0064400), one(0177400), "Bg", m68000up },
+{"jpl", one(0065000), one(0177400), "Bg", m68000up },
+{"jmi", one(0065400), one(0177400), "Bg", m68000up },
+{"jge", one(0066000), one(0177400), "Bg", m68000up },
+{"jlt", one(0066400), one(0177400), "Bg", m68000up },
+{"jgt", one(0067000), one(0177400), "Bg", m68000up },
+{"jle", one(0067400), one(0177400), "Bg", m68000up },
+{"jfngt", one(0067400), one(0177400), "Bg", m68000up }, /* apparently a sun alias */
+
+/* aliases */
+
+{"movql", one(0070000), one(0170400), "MsDd", m68000up },
+{"moveql", one(0070000), one(0170400), "MsDd", m68000up },
+{"moval", one(0020100), one(0170700), "*lAd", m68000up },
+{"movaw", one(0030100), one(0170700), "*wAd", m68000up },
+{"movb", one(0010000), one(0170000), ";b$d", m68000up }, /* mov */
+{"movl", one(0070000), one(0170400), "MsDd", m68000up }, /* movq written as mov */
+{"movl", one(0020000), one(0170000), "*l$d", m68000up },
+{"movl", one(0020100), one(0170700), "*lAd", m68000up },
+{"movl", one(0047140), one(0177770), "AsUd", m68000up }, /* mov to USP */
+{"movl", one(0047150), one(0177770), "UdAs", m68000up }, /* mov from USP */
+{"movc", one(0047173), one(0177777), "R1Jj", m68010up },
+{"movc", one(0047173), one(0177777), "R1#j", m68010up },
+{"movc", one(0047172), one(0177777), "JjR1", m68010up },
+{"movc", one(0047172), one(0177777), "#jR1", m68010up },
+{"movml", one(0044300), one(0177700), "#w&s", m68000up }, /* movm reg to mem. */
+{"movml", one(0044340), one(0177770), "#w-s", m68000up }, /* movm reg to autodecrement. */
+{"movml", one(0046300), one(0177700), "!s#w", m68000up }, /* movm mem to reg. */
+{"movml", one(0046330), one(0177770), "+s#w", m68000up }, /* movm autoinc to reg. */
+{"movml", one(0044300), one(0177700), "Lw&s", m68000up }, /* movm reg to mem. */
+{"movml", one(0044340), one(0177770), "lw-s", m68000up }, /* movm reg to autodecrement. */
+{"movml", one(0046300), one(0177700), "!sLw", m68000up }, /* movm mem to reg. */
+{"movml", one(0046330), one(0177770), "+sLw", m68000up }, /* movm autoinc to reg. */
+{"movmw", one(0044200), one(0177700), "#w&s", m68000up }, /* movm reg to mem. */
+{"movmw", one(0044240), one(0177770), "#w-s", m68000up }, /* movm reg to autodecrement. */
+{"movmw", one(0046200), one(0177700), "!s#w", m68000up }, /* movm mem to reg. */
+{"movmw", one(0046230), one(0177770), "+s#w", m68000up }, /* movm autoinc to reg. */
+{"movmw", one(0044200), one(0177700), "Lw&s", m68000up }, /* movm reg to mem. */
+{"movmw", one(0044240), one(0177770), "lw-s", m68000up }, /* movm reg to autodecrement. */
+{"movmw", one(0046200), one(0177700), "!sLw", m68000up }, /* movm mem to reg. */
+{"movmw", one(0046230), one(0177770), "+sLw", m68000up }, /* movm autoinc to reg. */
+{"movpl", one(0000510), one(0170770), "dsDd", m68000up }, /* memory to register */
+{"movpl", one(0000710), one(0170770), "Ddds", m68000up }, /* register to memory */
+{"movpw", one(0000410), one(0170770), "dsDd", m68000up }, /* memory to register */
+{"movpw", one(0000610), one(0170770), "Ddds", m68000up }, /* register to memory */
+{"movq", one(0070000), one(0170400), "MsDd", m68000up },
+{"movw", one(0030000), one(0170000), "*w$d", m68000up },
+{"movw", one(0030100), one(0170700), "*wAd", m68000up }, /* mova, written as mov */
+{"movw", one(0040300), one(0177700), "Ss$s", m68000up }, /* Move from sr */
+{"movw", one(0041300), one(0177700), "Cs$s", m68010up }, /* Move from ccr */
+{"movw", one(0042300), one(0177700), ";wCd", m68000up }, /* mov to ccr */
+{"movw", one(0043300), one(0177700), ";wSd", m68000up }, /* mov to sr */
+
+{"movsb", two(0007000, 0), two(0177700, 07777), "~sR1", m68010up },
+{"movsb", two(0007000, 04000), two(0177700, 07777), "R1~s", m68010up },
+{"movsl", two(0007200, 0), two(0177700, 07777), "~sR1", m68010up },
+{"movsl", two(0007200, 04000), two(0177700, 07777), "R1~s", m68010up },
+{"movsw", two(0007100, 0), two(0177700, 07777), "~sR1", m68010up },
+{"movsw", two(0007100, 04000), two(0177700, 07777), "R1~s", m68010up },
+
+};
+
+int numopcodes=sizeof(m68k_opcodes)/sizeof(m68k_opcodes[0]);
+
+struct m68k_opcode *endop = m68k_opcodes+sizeof(m68k_opcodes)/sizeof(m68k_opcodes[0]);
+
+/*
+ * Local Variables:
+ * fill-column: 131
+ * End:
+ */
+
+/* end of m68k-opcode.h */
diff --git a/gnu/usr.bin/as/opcode/m88k.h b/gnu/usr.bin/as/opcode/m88k.h
new file mode 100644
index 0000000..5f685b9
--- /dev/null
+++ b/gnu/usr.bin/as/opcode/m88k.h
@@ -0,0 +1,282 @@
+/* m88k-opcode.h -- Instruction information for the Motorola 88000
+ Contributed by Devon Bowen of Buffalo University
+ and Torbjorn Granlund of the Swedish Institute of Computer Science.
+ Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined(__STDC__) && !defined(const)
+#define const
+#endif
+
+/*
+ Character codes for op_spec field below.
+ Reserved for direct matching: x , [ ]
+
+ d = GRF Destination register (21:5)
+ 1 = Source register 1 (16:5)
+ 2 = Source register 2 (0:5)
+ 3 = Both source registers (same value) (0:5 and 16:5)
+ I = IMM16 (0:16)
+ b = bit field spec. (0:10)
+ p = 16 bit pc displ. (0:16)
+ P = 26 bit pc displ. (0:26)
+ B = bb0/bb1 condition (21:5)
+ M = bcnd condition (21:5)
+ f = fcr (5:6)
+ c = cr (5:6)
+ V = VEC9 (0:9)
+ ? = Give warning for this insn/operand combination
+ */
+
+/* instruction descriptor structure */
+
+struct m88k_opcode
+{
+ unsigned int opcode;
+ char *name;
+ char *op_spec;
+};
+
+/* and introducing... the Motorola 88100 instruction sets... */
+
+/* These macros may seem silly, but they are in preparation
+ for future versions of the 88000 family. */
+
+#define _MC88100(OPCODE,MNEM,OP_SPEC) {OPCODE,MNEM,OP_SPEC},
+#define _MC88xxx(OPCODE,MNEM,OP_SPEC) {OPCODE,MNEM,OP_SPEC},
+
+/* Equal mnemonics must be adjacent.
+ More specific operand specification must go before more general.
+ For example, "d,1,2" must go before "d,1,I" as a register for s2
+ would otherwise be considered a variable name. */
+
+static struct m88k_opcode m88k_opcodes[] =
+{
+ /* Opcode Mnemonic Opspec */
+
+ _MC88xxx(0xf4007000, "add", "d,1,2")
+ _MC88xxx(0x70000000, "add", "d,1,I")
+ _MC88xxx(0xf4007200, "add.ci", "d,1,2")
+ _MC88xxx(0xf4007300, "add.cio", "d,1,2")
+ _MC88xxx(0xf4007100, "add.co", "d,1,2")
+ _MC88xxx(0xf4006000, "addu", "d,1,2")
+ _MC88xxx(0x60000000, "addu", "d,1,I")
+ _MC88xxx(0xf4006200, "addu.ci", "d,1,2")
+ _MC88xxx(0xf4006300, "addu.cio", "d,1,2")
+ _MC88xxx(0xf4006100, "addu.co", "d,1,2")
+ _MC88xxx(0xf4004000, "and", "d,1,2")
+ _MC88xxx(0x40000000, "and", "d,1,I")
+ _MC88xxx(0xf4004400, "and.c", "d,1,2")
+ _MC88xxx(0x44000000, "and.u", "d,1,I")
+ _MC88xxx(0xd0000000, "bb0", "B,1,p")
+ _MC88xxx(0xd4000000, "bb0.n", "B,1,p")
+ _MC88xxx(0xd8000000, "bb1", "B,1,p")
+ _MC88xxx(0xdc000000, "bb1.n", "B,1,p")
+ _MC88xxx(0xe8000000, "bcnd", "M,1,p")
+ _MC88xxx(0xec000000, "bcnd.n", "M,1,p")
+ _MC88xxx(0xc0000000, "br", "P")
+ _MC88xxx(0xc4000000, "br.n", "P")
+ _MC88xxx(0xc8000000, "bsr", "P")
+ _MC88xxx(0xcc000000, "bsr.n", "P")
+ _MC88xxx(0xf4008000, "clr", "d,1,2")
+ _MC88xxx(0xf0008000, "clr", "d,1,b")
+ _MC88xxx(0xf4007c00, "cmp", "d,1,2")
+ _MC88xxx(0x7c000000, "cmp", "d,1,I")
+ _MC88xxx(0xf4007800, "div", "d,1,2")
+ _MC88xxx(0x78000000, "div", "d,1,I")
+ _MC88xxx(0xf4007800, "divs", "d,1,2")
+ _MC88xxx(0x78000000, "divs", "d,1,I")
+ _MC88xxx(0xf4006800, "divu", "d,1,2")
+ _MC88xxx(0x68000000, "divu", "d,1,I")
+ _MC88xxx(0xf4009000, "ext", "d,1,2")
+ _MC88xxx(0xf0009000, "ext", "d,1,b")
+ _MC88xxx(0xf4009800, "extu", "d,1,2")
+ _MC88xxx(0xf0009800, "extu", "d,1,b")
+ _MC88xxx(0x84002800, "fadd.sss", "d,1,2")
+ _MC88xxx(0x84002880, "fadd.ssd", "d,1,2")
+ _MC88xxx(0x84002a00, "fadd.sds", "d,1,2")
+ _MC88xxx(0x84002a80, "fadd.sdd", "d,1,2")
+ _MC88xxx(0x84002820, "fadd.dss", "d,1,2")
+ _MC88xxx(0x840028a0, "fadd.dsd", "d,1,2")
+ _MC88xxx(0x84002a20, "fadd.dds", "d,1,2")
+ _MC88xxx(0x84002aa0, "fadd.ddd", "d,1,2")
+ _MC88xxx(0x84003a80, "fcmp.sdd", "d,1,2")
+ _MC88xxx(0x84003a00, "fcmp.sds", "d,1,2")
+ _MC88xxx(0x84003880, "fcmp.ssd", "d,1,2")
+ _MC88xxx(0x84003800, "fcmp.sss", "d,1,2")
+ _MC88xxx(0x84007000, "fdiv.sss", "d,1,2")
+ _MC88xxx(0x84007080, "fdiv.ssd", "d,1,2")
+ _MC88xxx(0x84007200, "fdiv.sds", "d,1,2")
+ _MC88xxx(0x84007280, "fdiv.sdd", "d,1,2")
+ _MC88xxx(0x84007020, "fdiv.dss", "d,1,2")
+ _MC88xxx(0x840070a0, "fdiv.dsd", "d,1,2")
+ _MC88xxx(0x84007220, "fdiv.dds", "d,1,2")
+ _MC88xxx(0x840072a0, "fdiv.ddd", "d,1,2")
+ _MC88xxx(0xf400ec00, "ff0", "d,2")
+ _MC88xxx(0xf400e800, "ff1", "d,2")
+ _MC88xxx(0x80004800, "fldcr", "d,f")
+ _MC88xxx(0x84002020, "flt.ds", "d,2")
+ _MC88xxx(0x84002000, "flt.ss", "d,2")
+ _MC88xxx(0x84000000, "fmul.sss", "d,1,2")
+ _MC88xxx(0x84000080, "fmul.ssd", "d,1,2")
+ _MC88xxx(0x84000200, "fmul.sds", "d,1,2")
+ _MC88xxx(0x84000280, "fmul.sdd", "d,1,2")
+ _MC88xxx(0x84000020, "fmul.dss", "d,1,2")
+ _MC88xxx(0x840000a0, "fmul.dsd", "d,1,2")
+ _MC88xxx(0x84000220, "fmul.dds", "d,1,2")
+ _MC88xxx(0x840002a0, "fmul.ddd", "d,1,2")
+ _MC88xxx(0x80008800, "fstcr", "3,f")
+ _MC88xxx(0x84003000, "fsub.sss", "d,1,2")
+ _MC88xxx(0x84003080, "fsub.ssd", "d,1,2")
+ _MC88xxx(0x84003200, "fsub.sds", "d,1,2")
+ _MC88xxx(0x84003280, "fsub.sdd", "d,1,2")
+ _MC88xxx(0x84003020, "fsub.dss", "d,1,2")
+ _MC88xxx(0x840030a0, "fsub.dsd", "d,1,2")
+ _MC88xxx(0x84003220, "fsub.dds", "d,1,2")
+ _MC88xxx(0x840032a0, "fsub.ddd", "d,1,2")
+ _MC88xxx(0x8000c800, "fxcr", "d,3,f")
+ _MC88xxx(0x8400fc01, "illop1", "")
+ _MC88xxx(0x8400fc02, "illop2", "")
+ _MC88xxx(0x8400fc03, "illop3", "")
+ _MC88xxx(0x84004880, "int.sd", "d,2")
+ _MC88xxx(0x84004800, "int.ss", "d,2")
+ _MC88xxx(0xf400c000, "jmp", "2")
+ _MC88xxx(0xf400c400, "jmp.n", "2")
+ _MC88xxx(0xf400c800, "jsr", "2")
+ _MC88xxx(0xf400cc00, "jsr.n", "2")
+ _MC88xxx(0xf4001400, "ld", "d,1,2")
+ _MC88xxx(0xf4001600, "ld", "d,1[2]")
+ _MC88xxx(0x14000000, "ld", "d,1,I")
+ _MC88xxx(0xf4001e00, "ld.b", "d,1[2]")
+ _MC88xxx(0xf4001c00, "ld.b", "d,1,2")
+ _MC88xxx(0x1c000000, "ld.b", "d,1,I")
+ _MC88xxx(0xf4001d00, "ld.b.usr", "d,1,2")
+ _MC88xxx(0xf4001f00, "ld.b.usr", "d,1[2]")
+ _MC88xxx(0xf4000e00, "ld.bu", "d,1[2]")
+ _MC88xxx(0xf4000c00, "ld.bu", "d,1,2")
+ _MC88xxx(0x0c000000, "ld.bu", "d,1,I")
+ _MC88xxx(0xf4000d00, "ld.bu.usr", "d,1,2")
+ _MC88xxx(0xf4000f00, "ld.bu.usr", "d,1[2]")
+ _MC88xxx(0xf4001200, "ld.d", "d,1[2]")
+ _MC88xxx(0xf4001000, "ld.d", "d,1,2")
+ _MC88xxx(0x10000000, "ld.d", "d,1,I")
+ _MC88xxx(0xf4001100, "ld.d.usr", "d,1,2")
+ _MC88xxx(0xf4001300, "ld.d.usr", "d,1[2]")
+ _MC88xxx(0xf4001a00, "ld.h", "d,1[2]")
+ _MC88xxx(0xf4001800, "ld.h", "d,1,2")
+ _MC88xxx(0x18000000, "ld.h", "d,1,I")
+ _MC88xxx(0xf4001900, "ld.h.usr", "d,1,2")
+ _MC88xxx(0xf4001b00, "ld.h.usr", "d,1[2]")
+ _MC88xxx(0xf4000a00, "ld.hu", "d,1[2]")
+ _MC88xxx(0xf4000800, "ld.hu", "d,1,2")
+ _MC88xxx(0x08000000, "ld.hu", "d,1,I")
+ _MC88xxx(0xf4000900, "ld.hu.usr", "d,1,2")
+ _MC88xxx(0xf4000b00, "ld.hu.usr", "d,1[2]")
+ _MC88xxx(0xf4001500, "ld.usr", "d,1,2")
+ _MC88xxx(0xf4001700, "ld.usr", "d,1[2]")
+ _MC88xxx(0xf4003600, "lda", "d,1[2]")
+ _MC88xxx(0xf4006000, "lda", "?d,1,2") /* Output addu */
+ _MC88xxx(0x60000000, "lda", "?d,1,I") /* Output addu */
+ _MC88xxx(0xf4006000, "lda.b", "?d,1[2]") /* Output addu */
+ _MC88xxx(0xf4006000, "lda.b", "?d,1,2") /* Output addu */
+ _MC88xxx(0x60000000, "lda.b", "?d,1,I") /* Output addu */
+ _MC88xxx(0xf4003200, "lda.d", "d,1[2]")
+ _MC88xxx(0xf4006000, "lda.d", "?d,1,2") /* Output addu */
+ _MC88xxx(0x60000000, "lda.d", "?d,1,I") /* Output addu */
+ _MC88xxx(0xf4003a00, "lda.h", "d,1[2]")
+ _MC88xxx(0xf4006000, "lda.h", "?d,1,2") /* Output addu */
+ _MC88xxx(0x60000000, "lda.h", "?d,1,I") /* Output addu */
+ _MC88xxx(0x80004000, "ldcr", "d,c")
+ _MC88xxx(0xf400a000, "mak", "d,1,2")
+ _MC88xxx(0xf000a000, "mak", "d,1,b")
+ _MC88xxx(0x48000000, "mask", "d,1,I")
+ _MC88xxx(0x4c000000, "mask.u", "d,1,I")
+ _MC88xxx(0xf4006c00, "mul", "d,1,2")
+ _MC88xxx(0x6c000000, "mul", "d,1,I")
+ _MC88xxx(0xf4006c00, "mulu", "d,1,2") /* synonym for mul */
+ _MC88xxx(0x6c000000, "mulu", "d,1,I") /* synonym for mul */
+ _MC88xxx(0x84005080, "nint.sd", "d,2")
+ _MC88xxx(0x84005000, "nint.ss", "d,2")
+ _MC88xxx(0xf4005800, "or", "d,1,2")
+ _MC88xxx(0x58000000, "or", "d,1,I")
+ _MC88xxx(0xf4005c00, "or.c", "d,1,2")
+ _MC88xxx(0x5c000000, "or.u", "d,1,I")
+ _MC88xxx(0xf000a800, "rot", "d,1,b")
+ _MC88xxx(0xf400a800, "rot", "d,1,2")
+ _MC88xxx(0xf400fc00, "rte", "")
+ _MC88xxx(0xf4008800, "set", "d,1,2")
+ _MC88xxx(0xf0008800, "set", "d,1,b")
+ _MC88xxx(0xf4002600, "st", "d,1[2]")
+ _MC88xxx(0xf4002400, "st", "d,1,2")
+ _MC88xxx(0x24000000, "st", "d,1,I")
+ _MC88xxx(0xf4002e00, "st.b", "d,1[2]")
+ _MC88xxx(0xf4002c00, "st.b", "d,1,2")
+ _MC88xxx(0x2c000000, "st.b", "d,1,I")
+ _MC88xxx(0xf4002d00, "st.b.usr", "d,1,2")
+ _MC88xxx(0xf4002f00, "st.b.usr", "d,1[2]")
+ _MC88xxx(0xf4002200, "st.d", "d,1[2]")
+ _MC88xxx(0xf4002000, "st.d", "d,1,2")
+ _MC88xxx(0x20000000, "st.d", "d,1,I")
+ _MC88xxx(0xf4002100, "st.d.usr", "d,1,2")
+ _MC88xxx(0xf4002300, "st.d.usr", "d,1[2]")
+ _MC88xxx(0xf4002a00, "st.h", "d,1[2]")
+ _MC88xxx(0xf4002800, "st.h", "d,1,2")
+ _MC88xxx(0x28000000, "st.h", "d,1,I")
+ _MC88xxx(0xf4002900, "st.h.usr", "d,1,2")
+ _MC88xxx(0xf4002b00, "st.h.usr", "d,1[2]")
+ _MC88xxx(0xf4002500, "st.usr", "d,1,2")
+ _MC88xxx(0xf4002700, "st.usr", "d,1[2]")
+ _MC88xxx(0x80008000, "stcr", "3,c")
+ _MC88xxx(0xf4007400, "sub", "d,1,2")
+ _MC88xxx(0x74000000, "sub", "d,1,I")
+ _MC88xxx(0xf4007600, "sub.ci", "d,1,2")
+ _MC88xxx(0xf4007700, "sub.cio", "d,1,2")
+ _MC88xxx(0xf4007500, "sub.co", "d,1,2")
+ _MC88xxx(0xf4006400, "subu", "d,1,2")
+ _MC88xxx(0x64000000, "subu", "d,1,I")
+ _MC88xxx(0xf4006600, "subu.ci", "d,1,2")
+ _MC88xxx(0xf4006700, "subu.cio", "d,1,2")
+ _MC88xxx(0xf4006500, "subu.co", "d,1,2")
+ _MC88xxx(0xf000d000, "tb0", "B,1,V")
+ _MC88xxx(0xf000d800, "tb1", "B,1,V")
+ _MC88xxx(0xf400f800, "tbnd", "1,2")
+ _MC88xxx(0xf8000000, "tbnd", "1,I")
+ _MC88xxx(0xf000e800, "tcnd", "M,1,V")
+ _MC88xxx(0x84005880, "trnc.sd", "d,2")
+ _MC88xxx(0x84005800, "trnc.ss", "d,2")
+ _MC88xxx(0x8000c000, "xcr", "d,1,c")
+ _MC88xxx(0xf4000600, "xmem", "d,1[2]")
+ _MC88xxx(0xf4000400, "xmem", "d,1,2")
+ _MC88100(0x04000000, "xmem", "?d,1,I")
+ _MC88xxx(0xf4000200, "xmem.bu", "d,1[2]")
+ _MC88xxx(0xf4000000, "xmem.bu", "d,1,2")
+ _MC88100(0x00000000, "xmem.bu", "?d,1,I")
+ _MC88xxx(0xf4000300, "xmem.bu.usr", "d,1[2]")
+ _MC88xxx(0xf4000100, "xmem.bu.usr", "d,1,2")
+ _MC88100(0x00000100, "xmem.bu.usr", "?d,1,I")
+ _MC88xxx(0xf4000700, "xmem.usr", "d,1[2]")
+ _MC88xxx(0xf4000500, "xmem.usr", "d,1,2")
+ _MC88100(0x04000100, "xmem.usr", "?d,1,I")
+ _MC88xxx(0xf4005000, "xor", "d,1,2")
+ _MC88xxx(0x50000000, "xor", "d,1,I")
+ _MC88xxx(0xf4005400, "xor.c", "d,1,2")
+ _MC88xxx(0x54000000, "xor.u", "d,1,I")
+ _MC88xxx(0x00000000, "", 0)
+};
+
+#define NUMOPCODES ((sizeof m88k_opcodes)/(sizeof m88k_opcodes[0]))
diff --git a/gnu/usr.bin/as/opcode/mips.h b/gnu/usr.bin/as/opcode/mips.h
new file mode 100644
index 0000000..a65678a
--- /dev/null
+++ b/gnu/usr.bin/as/opcode/mips.h
@@ -0,0 +1,363 @@
+/* Mips opcde list for GDB, the GNU debugger.
+ Copyright (C) 1989 Free Software Foundation, Inc.
+ Contributed by Nobuyuki Hikichi(hikichi@sra.junet)
+ Made to work for little-endian machines, and debugged
+ by Per Bothner (bothner@cs.wisc.edu).
+ Many fixes contributed by Frank Yellin (fy@lucid.com).
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if BITS_BIG_ENDIAN
+#define BIT_FIELDS_2(a,b) a;b;
+#define BIT_FIELDS_4(a,b,c,d) a;b;c;d;
+#define BIT_FIELDS_6(a,b,c,d,e,f) a;b;c;d;e;f;
+#else
+#define BIT_FIELDS_2(a,b) b;a;
+#define BIT_FIELDS_4(a,b,c,d) d;c;b;a;
+#define BIT_FIELDS_6(a,b,c,d,e,f) f;e;d;c;b;a;
+#endif
+
+struct op_i_fmt
+{
+BIT_FIELDS_4(
+ unsigned op : 6,
+ unsigned rs : 5,
+ unsigned rt : 5,
+ unsigned immediate : 16)
+};
+
+struct op_j_fmt
+{
+BIT_FIELDS_2(
+ unsigned op : 6,
+ unsigned target : 26)
+};
+
+struct op_r_fmt
+{
+BIT_FIELDS_6(
+ unsigned op : 6,
+ unsigned rs : 5,
+ unsigned rt : 5,
+ unsigned rd : 5,
+ unsigned shamt : 5,
+ unsigned funct : 6)
+};
+
+
+struct fop_i_fmt
+{
+BIT_FIELDS_4(
+ unsigned op : 6,
+ unsigned rs : 5,
+ unsigned rt : 5,
+ unsigned immediate : 16)
+};
+
+struct op_b_fmt
+{
+BIT_FIELDS_4(
+ unsigned op : 6,
+ unsigned rs : 5,
+ unsigned rt : 5,
+ short delta : 16)
+};
+
+struct fop_r_fmt
+{
+BIT_FIELDS_6(
+ unsigned op : 6,
+ unsigned fmt : 5,
+ unsigned ft : 5,
+ unsigned fs : 5,
+ unsigned fd : 5,
+ unsigned funct : 6)
+};
+
+struct mips_opcode
+{
+ char *name;
+ unsigned long opcode;
+ unsigned long match;
+ char *args;
+ int bdelay; /* Nonzero if delayed branch. */
+};
+
+/* args format;
+
+ "s" rs: source register specifier
+ "t" rt: target register
+ "i" immediate
+ "a" target address
+ "c" branch condition
+ "d" rd: destination register specifier
+ "h" shamt: shift amount
+ "f" funct: function field
+
+ for fpu
+ "S" fs source 1 register
+ "T" ft source 2 register
+ "D" distination register
+*/
+
+#define one(x) (x << 26)
+#define op_func(x, y) ((x << 26) | y)
+#define op_cond(x, y) ((x << 26) | (y << 16))
+#define op_rs_func(x, y, z) ((x << 26) | (y << 21) | z)
+#define op_rs_b11(x, y, z) ((x << 26) | (y << 21) | z)
+#define op_o16(x, y) ((x << 26) | (y << 16))
+#define op_bc(x, y, z) ((x << 26) | (y << 21) | (z << 16))
+
+struct mips_opcode mips_opcodes[] =
+{
+/* These first opcodes are special cases of the ones in the comments */
+ {"nop", 0, 0xffffffff, /*li*/ "", 0},
+ {"li", op_bc(9,0,0), op_bc(0x3f,31,0), /*addiu*/ "t,j", 0},
+ {"b", one(4), 0xffff0000, /*beq*/ "b", 1},
+ {"move", op_func(0, 33), op_cond(0x3f,31)|0x7ff,/*addu*/ "d,s", 0},
+
+ {"sll", op_func(0, 0), op_func(0x3f, 0x3f), "d,t,h", 0},
+ {"srl", op_func(0, 2), op_func(0x3f, 0x3f), "d,t,h", 0},
+ {"sra", op_func(0, 3), op_func(0x3f, 0x3f), "d,t,h", 0},
+ {"sllv", op_func(0, 4), op_func(0x3f, 0x7ff), "d,t,s", 0},
+ {"srlv", op_func(0, 6), op_func(0x3f, 0x7ff), "d,t,s", 0},
+ {"srav", op_func(0, 7), op_func(0x3f, 0x7ff), "d,t,s", 0},
+ {"jr", op_func(0, 8), op_func(0x3f, 0x1fffff), "s", 1},
+ {"jalr", op_func(0, 9), op_func(0x3f, 0x1f07ff), "d,s", 1},
+ {"syscall", op_func(0, 12), op_func(0x3f, 0x3f), "", 0},
+ {"break", op_func(0, 13), op_func(0x3f, 0x3f), "", 0},
+ {"mfhi", op_func(0, 16), op_func(0x3f, 0x03ff07ff), "d", 0},
+ {"mthi", op_func(0, 17), op_func(0x3f, 0x1fffff), "s", 0},
+ {"mflo", op_func(0, 18), op_func(0x3f, 0x03ff07ff), "d", 0},
+ {"mtlo", op_func(0, 19), op_func(0x3f, 0x1fffff), "s", 0},
+ {"mult", op_func(0, 24), op_func(0x3f, 0xffff), "s,t", 0},
+ {"multu", op_func(0, 25), op_func(0x3f, 0xffff), "s,t", 0},
+ {"div", op_func(0, 26), op_func(0x3f, 0xffff), "s,t", 0},
+ {"divu", op_func(0, 27), op_func(0x3f, 0xffff), "s,t", 0},
+ {"add", op_func(0, 32), op_func(0x3f, 0x7ff), "d,s,t", 0},
+ {"addu", op_func(0, 33), op_func(0x3f, 0x7ff), "d,s,t", 0},
+ {"sub", op_func(0, 34), op_func(0x3f, 0x7ff), "d,s,t", 0},
+ {"subu", op_func(0, 35), op_func(0x3f, 0x7ff), "d,s,t", 0},
+ {"and", op_func(0, 36), op_func(0x3f, 0x7ff), "d,s,t", 0},
+ {"or", op_func(0, 37), op_func(0x3f, 0x7ff), "d,s,t", 0},
+ {"xor", op_func(0, 38), op_func(0x3f, 0x7ff), "d,s,t", 0},
+ {"nor", op_func(0, 39), op_func(0x3f, 0x7ff), "d,s,t", 0},
+ {"slt", op_func(0, 42), op_func(0x3f, 0x7ff), "d,s,t", 0},
+ {"sltu", op_func(0, 43), op_func(0x3f, 0x7ff), "d,s,t", 0},
+
+ {"bltz", op_cond (1, 0), op_cond(0x3f, 0x1f), "s,b", 1},
+ {"bgez", op_cond (1, 1), op_cond(0x3f, 0x1f), "s,b", 1},
+ {"bltzal", op_cond (1, 16),op_cond(0x3f, 0x1f), "s,b", 1},
+ {"bgezal", op_cond (1, 17),op_cond(0x3f, 0x1f), "s,b", 1},
+
+
+ {"j", one(2), one(0x3f), "a", 1},
+ {"jal", one(3), one(0x3f), "a", 1},
+ {"beq", one(4), one(0x3f), "s,t,b", 1},
+ {"bne", one(5), one(0x3f), "s,t,b", 1},
+ {"blez", one(6), one(0x3f) | 0x1f0000, "s,b", 1},
+ {"bgtz", one(7), one(0x3f) | 0x1f0000, "s,b", 1},
+ {"addi", one(8), one(0x3f), "t,s,j", 0},
+ {"addiu", one(9), one(0x3f), "t,s,j", 0},
+ {"slti", one(10), one(0x3f), "t,s,j", 0},
+ {"sltiu", one(11), one(0x3f), "t,s,j", 0},
+ {"andi", one(12), one(0x3f), "t,s,i", 0},
+ {"ori", one(13), one(0x3f), "t,s,i", 0},
+ {"xori", one(14), one(0x3f), "t,s,i", 0},
+ /* rs field is don't care field? */
+ {"lui", one(15), one(0x3f), "t,i", 0},
+
+/* co processor 0 instruction */
+ {"mfc0", op_rs_b11 (16, 0, 0), op_rs_b11(0x3f, 0x1f, 0x1ffff), "t,d", 0},
+ {"cfc0", op_rs_b11 (16, 2, 0), op_rs_b11(0x3f, 0x1f, 0x1ffff), "t,d", 0},
+ {"mtc0", op_rs_b11 (16, 4, 0), op_rs_b11(0x3f, 0x1f, 0x1ffff), "t,d", 0},
+ {"ctc0", op_rs_b11 (16, 6, 0), op_rs_b11(0x3f, 0x1f, 0x1ffff), "t,d", 0},
+
+ {"bc0f", op_o16(16, 0x100), op_o16(0x3f, 0x3ff), "b", 1},
+ {"bc0f", op_o16(16, 0x180), op_o16(0x3f, 0x3ff), "b", 1},
+ {"bc0t", op_o16(16, 0x101), op_o16(0x3f, 0x3ff), "b", 1},
+ {"bc0t", op_o16(16, 0x181), op_o16(0x3f, 0x3ff), "b", 1},
+
+ {"tlbr", op_rs_func(16, 0x10, 1), ~0, "", 0},
+ {"tlbwi", op_rs_func(16, 0x10, 2), ~0, "", 0},
+ {"tlbwr", op_rs_func(16, 0x10, 6), ~0, "", 0},
+ {"tlbp", op_rs_func(16, 0x10, 8), ~0, "", 0},
+ {"rfe", op_rs_func(16, 0x10, 16), ~0, "", 0},
+
+ {"mfc1", op_rs_b11 (17, 0, 0), op_rs_b11(0x3f, 0x1f, 0),"t,S", 0},
+ {"cfc1", op_rs_b11 (17, 2, 0), op_rs_b11(0x3f, 0x1f, 0),"t,S", 0},
+ {"mtc1", op_rs_b11 (17, 4, 0), op_rs_b11(0x3f, 0x1f, 0),"t,S", 0},
+ {"ctc1", op_rs_b11 (17, 6, 0), op_rs_b11(0x3f, 0x1f, 0),"t,S", 0},
+
+ {"bc1f", op_o16(17, 0x100), op_o16(0x3f, 0x3ff), "b", 1},
+ {"bc1f", op_o16(17, 0x180), op_o16(0x3f, 0x3ff), "b", 1},
+ {"bc1t", op_o16(17, 0x101), op_o16(0x3f, 0x3ff), "b", 1},
+ {"bc1t", op_o16(17, 0x181), op_o16(0x3f, 0x3ff), "b", 1},
+
+/* fpu instruction */
+ {"add.s", op_rs_func(17, 0x10, 0),
+ op_rs_func(0x3f, 0x1f, 0x3f), "D,S,T", 0},
+ {"add.d", op_rs_func(17, 0x11, 0),
+ op_rs_func(0x3f, 0x1f, 0x3f), "D,S,T", 0},
+ {"sub.s", op_rs_func(17, 0x10, 1),
+ op_rs_func(0x3f, 0x1f, 0x3f), "D,S,T", 0},
+ {"sub.d", op_rs_func(17, 0x11, 1),
+ op_rs_func(0x3f, 0x1f, 0x3f), "D,S,T", 0},
+ {"mul.s", op_rs_func(17, 0x10, 2),
+ op_rs_func(0x3f, 0x1f, 0x3f), "D,S,T", 0},
+ {"mul.d", op_rs_func(17, 0x11, 2),
+ op_rs_func(0x3f, 0x1f, 0x3f), "D,S,T", 0},
+ {"div.s", op_rs_func(17, 0x10, 3),
+ op_rs_func(0x3f, 0x1f, 0x3f), "D,S,T", 0},
+ {"div.d", op_rs_func(17, 0x11, 3),
+ op_rs_func(0x3f, 0x1f, 0x3f), "D,S,T", 0},
+ {"abs.s", op_rs_func(17, 0x10, 5),
+ op_rs_func(0x3f, 0x1f, 0x1f003f), "D,S", 0},
+ {"abs.d", op_rs_func(17, 0x11, 5),
+ op_rs_func(0x3f, 0x1f, 0x1f003f), "D,S", 0},
+ {"mov.s", op_rs_func(17, 0x10, 6),
+ op_rs_func(0x3f, 0x1f, 0x1f003f), "D,S", 0},
+ {"mov.d", op_rs_func(17, 0x11, 6),
+ op_rs_func(0x3f, 0x1f, 0x1f003f), "D,S", 0},
+ {"neg.s", op_rs_func(17, 0x10, 7),
+ op_rs_func(0x3f, 0x1f, 0x1f003f), "D,S", 0},
+ {"neg.d", op_rs_func(17, 0x11, 7),
+ op_rs_func(0x3f, 0x1f, 0x1f003f), "D,S", 0},
+ {"cvt.s.s", op_rs_func(17, 0x10, 32),
+ op_rs_func(0x3f, 0x1f, 0x1f003f), "D,S", 0},
+ {"cvt.s.d", op_rs_func(17, 0x11, 32),
+ op_rs_func(0x3f, 0x1f, 0x1f003f), "D,S", 0},
+ {"cvt.s.w", op_rs_func(17, 0x14, 32),
+ op_rs_func(0x3f, 0x1f, 0x1f003f), "D,S", 0},
+ {"cvt.d.s", op_rs_func(17, 0x10, 33),
+ op_rs_func(0x3f, 0x1f, 0x1f003f), "D,S", 0},
+ {"cvt.d.d", op_rs_func(17, 0x11, 33),
+ op_rs_func(0x3f, 0x1f, 0x1f003f), "D,S", 0},
+ {"cvt.d.w", op_rs_func(17, 0x14, 33),
+ op_rs_func(0x3f, 0x1f, 0x1f003f), "D,S", 0},
+ {"cvt.w.s", op_rs_func(17, 0x10, 36),
+ op_rs_func(0x3f, 0x1f, 0x1f003f), "D,S", 0},
+ {"cvt.w.d", op_rs_func(17, 0x11, 36),
+ op_rs_func(0x3f, 0x1f, 0x1f003f), "D,S", 0},
+ {"c.f.s", op_rs_func(17, 0x10, 48),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.f.d", op_rs_func(17, 0x11, 48),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.un.s", op_rs_func(17, 0x10, 49),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.un.d", op_rs_func(17, 0x11, 49),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.eq.s", op_rs_func(17, 0x10, 50),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.eq.d", op_rs_func(17, 0x11, 50),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.ueq.s", op_rs_func(17, 0x10, 51),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.ueq.d", op_rs_func(17, 0x11, 51),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.olt.s", op_rs_func(17, 0x10, 52),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.olt.d", op_rs_func(17, 0x11, 52),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.ult.s", op_rs_func(17, 0x10, 53),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.ult.d", op_rs_func(17, 0x11, 53),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.ole.s", op_rs_func(17, 0x10, 54),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.ole.d", op_rs_func(17, 0x11, 54),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.ule.s", op_rs_func(17, 0x10, 55),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.ule.d", op_rs_func(17, 0x11, 55),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.sf.s", op_rs_func(17, 0x10, 56),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.sf.d", op_rs_func(17, 0x11, 56),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.ngle.s", op_rs_func(17, 0x10, 57),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.ngle.d", op_rs_func(17, 0x11, 57),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.seq.s", op_rs_func(17, 0x10, 58),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.seq.d", op_rs_func(17, 0x11, 58),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.ngl.s", op_rs_func(17, 0x10, 59),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.ngl.d", op_rs_func(17, 0x11, 59),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.lt.s", op_rs_func(17, 0x10, 60),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.lt.d", op_rs_func(17, 0x11, 60),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.nge.s", op_rs_func(17, 0x10, 61),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.nge.d", op_rs_func(17, 0x11, 61),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.le.s", op_rs_func(17, 0x10, 62),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.le.d", op_rs_func(17, 0x11, 62),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.ngt.s", op_rs_func(17, 0x10, 63),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+ {"c.ngt.d", op_rs_func(17, 0x11, 63),
+ op_rs_func(0x3f, 0x1f, 0x7ff), "S,T", 0},
+
+/* co processor 2 instruction */
+ {"mfc2", op_rs_b11 (18, 0, 0), op_rs_b11(0x3f, 0x1f, 0x1ffff), "t,d", 0},
+ {"cfc2", op_rs_b11 (18, 2, 0), op_rs_b11(0x3f, 0x1f, 0x1ffff), "t,d", 0},
+ {"mtc2", op_rs_b11 (18, 4, 0), op_rs_b11(0x3f, 0x1f, 0x1ffff), "t,d", 0},
+ {"ctc2", op_rs_b11 (18, 6, 0), op_rs_b11(0x3f, 0x1f, 0x1ffff), "t,d", 0},
+ {"bc2f", op_o16(18, 0x100), op_o16(0x3f, 0x3ff), "b", 1},
+ {"bc2f", op_o16(18, 0x180), op_o16(0x3f, 0x3ff), "b", 1},
+ {"bc2f", op_o16(18, 0x101), op_o16(0x3f, 0x3ff), "b", 1},
+ {"bc2t", op_o16(18, 0x181), op_o16(0x3f, 0x3ff), "b", 1},
+
+/* co processor 3 instruction */
+ {"mtc3", op_rs_b11 (19, 0, 0), op_rs_b11(0x3f, 0x1f, 0x1ffff), "t,d", 0},
+ {"cfc3", op_rs_b11 (19, 2, 0), op_rs_b11(0x3f, 0x1f, 0x1ffff), "t,d", 0},
+ {"mtc3", op_rs_b11 (19, 4, 0), op_rs_b11(0x3f, 0x1f, 0x1ffff), "t,d", 0},
+ {"ctc3", op_rs_b11 (19, 6, 0), op_rs_b11(0x3f, 0x1f, 0x1ffff), "t,d", 0},
+ {"bc3f", op_o16(19, 0x100), op_o16(0x3f, 0x3ff), "b", 1},
+ {"bc3f", op_o16(19, 0x180), op_o16(0x3f, 0x3ff), "b", 1},
+ {"bc3t", op_o16(19, 0x101), op_o16(0x3f, 0x3ff), "b", 1},
+ {"bc3t", op_o16(19, 0x181), op_o16(0x3f, 0x3ff), "b", 1},
+
+ {"lb", one(32), one(0x3f), "t,j(s)", 0},
+ {"lh", one(33), one(0x3f), "t,j(s)", 0},
+ {"lwl", one(34), one(0x3f), "t,j(s)", 0},
+ {"lw", one(35), one(0x3f), "t,j(s)", 0},
+ {"lbu", one(36), one(0x3f), "t,j(s)", 0},
+ {"lhu", one(37), one(0x3f), "t,j(s)", 0},
+ {"lwr", one(38), one(0x3f), "t,j(s)", 0},
+ {"sb", one(40), one(0x3f), "t,j(s)", 0},
+ {"sh", one(41), one(0x3f), "t,j(s)", 0},
+ {"swl", one(42), one(0x3f), "t,j(s)", 0},
+ {"swr", one(46), one(0x3f), "t,j(s)", 0},
+ {"sw", one(43), one(0x3f), "t,j(s)", 0},
+ {"lwc0", one(48), one(0x3f), "t,j(s)", 0},
+/* for fpu */
+ {"lwc1", one(49), one(0x3f), "T,j(s)", 0},
+ {"lwc2", one(50), one(0x3f), "t,j(s)", 0},
+ {"lwc3", one(51), one(0x3f), "t,j(s)", 0},
+ {"swc0", one(56), one(0x3f), "t,j(s)", 0},
+/* for fpu */
+ {"swc1", one(57), one(0x3f), "T,j(s)", 0},
+ {"swc2", one(58), one(0x3f), "t,j(s)", 0},
+ {"swc3", one(59), one(0x3f), "t,j(s)", 0},
+};
diff --git a/gnu/usr.bin/as/opcode/np1.h b/gnu/usr.bin/as/opcode/np1.h
new file mode 100644
index 0000000..6546825
--- /dev/null
+++ b/gnu/usr.bin/as/opcode/np1.h
@@ -0,0 +1,422 @@
+/* Print GOULD NPL instructions for GDB, the GNU debugger.
+ Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+struct gld_opcode
+{
+ char *name;
+ unsigned long opcode;
+ unsigned long mask;
+ char *args;
+ int length;
+};
+
+/* We store four bytes of opcode for all opcodes because that
+ is the most any of them need. The actual length of an instruction
+ is always at least 2 bytes, and at most four. The length of the
+ instruction is based on the opcode.
+
+ The mask component is a mask saying which bits must match
+ particular opcode in order for an instruction to be an instance
+ of that opcode.
+
+ The args component is a string containing characters
+ that are used to format the arguments to the instruction. */
+
+/* Kinds of operands:
+ r Register in first field
+ R Register in second field
+ b Base register in first field
+ B Base register in second field
+ v Vector register in first field
+ V Vector register in first field
+ A Optional address register (base register)
+ X Optional index register
+ I Immediate data (16bits signed)
+ O Offset field (16bits signed)
+ h Offset field (15bits signed)
+ d Offset field (14bits signed)
+ S Shift count field
+
+ any other characters are printed as is...
+*/
+
+/* The assembler requires that this array be sorted as follows:
+ all instances of the same mnemonic must be consecutive.
+ All instances of the same mnemonic with the same number of operands
+ must be consecutive.
+ */
+struct gld_opcode gld_opcodes[] =
+{
+{ "lb", 0xb4080000, 0xfc080000, "r,xOA,X", 4 },
+{ "lnb", 0xb8080000, 0xfc080000, "r,xOA,X", 4 },
+{ "lbs", 0xec080000, 0xfc080000, "r,xOA,X", 4 },
+{ "lh", 0xb4000001, 0xfc080001, "r,xOA,X", 4 },
+{ "lnh", 0xb8000001, 0xfc080001, "r,xOA,X", 4 },
+{ "lw", 0xb4000000, 0xfc080000, "r,xOA,X", 4 },
+{ "lnw", 0xb8000000, 0xfc080000, "r,xOA,X", 4 },
+{ "ld", 0xb4000002, 0xfc080002, "r,xOA,X", 4 },
+{ "lnd", 0xb8000002, 0xfc080002, "r,xOA,X", 4 },
+{ "li", 0xf8000000, 0xfc7f0000, "r,I", 4 },
+{ "lpa", 0x50080000, 0xfc080000, "r,xOA,X", 4 },
+{ "la", 0x50000000, 0xfc080000, "r,xOA,X", 4 },
+{ "labr", 0x58080000, 0xfc080000, "b,xOA,X", 4 },
+{ "lbp", 0x90080000, 0xfc080000, "r,xOA,X", 4 },
+{ "lhp", 0x90000001, 0xfc080001, "r,xOA,X", 4 },
+{ "lwp", 0x90000000, 0xfc080000, "r,xOA,X", 4 },
+{ "ldp", 0x90000002, 0xfc080002, "r,xOA,X", 4 },
+{ "suabr", 0x58000000, 0xfc080000, "b,xOA,X", 4 },
+{ "lf", 0xbc000000, 0xfc080000, "r,xOA,X", 4 },
+{ "lfbr", 0xbc080000, 0xfc080000, "b,xOA,X", 4 },
+{ "lwbr", 0x5c000000, 0xfc080000, "b,xOA,X", 4 },
+{ "stb", 0xd4080000, 0xfc080000, "r,xOA,X", 4 },
+{ "sth", 0xd4000001, 0xfc080001, "r,xOA,X", 4 },
+{ "stw", 0xd4000000, 0xfc080000, "r,xOA,X", 4 },
+{ "std", 0xd4000002, 0xfc080002, "r,xOA,X", 4 },
+{ "stf", 0xdc000000, 0xfc080000, "r,xOA,X", 4 },
+{ "stfbr", 0xdc080000, 0xfc080000, "b,xOA,X", 4 },
+{ "stwbr", 0x54000000, 0xfc080000, "b,xOA,X", 4 },
+{ "zmb", 0xd8080000, 0xfc080000, "r,xOA,X", 4 },
+{ "zmh", 0xd8000001, 0xfc080001, "r,xOA,X", 4 },
+{ "zmw", 0xd8000000, 0xfc080000, "r,xOA,X", 4 },
+{ "zmd", 0xd8000002, 0xfc080002, "r,xOA,X", 4 },
+{ "stbp", 0x94080000, 0xfc080000, "r,xOA,X", 4 },
+{ "sthp", 0x94000001, 0xfc080001, "r,xOA,X", 4 },
+{ "stwp", 0x94000000, 0xfc080000, "r,xOA,X", 4 },
+{ "stdp", 0x94000002, 0xfc080002, "r,xOA,X", 4 },
+{ "lil", 0xf80b0000, 0xfc7f0000, "r,D", 4 },
+{ "lwsl1", 0xec000000, 0xfc080000, "r,xOA,X", 4 },
+{ "lwsl2", 0xfc000000, 0xfc080000, "r,xOA,X", 4 },
+{ "lwsl3", 0xfc080000, 0xfc080000, "r,xOA,X", 4 },
+
+{ "lvb", 0xb0080000, 0xfc080000, "v,xOA,X", 4 },
+{ "lvh", 0xb0000001, 0xfc080001, "v,xOA,X", 4 },
+{ "lvw", 0xb0000000, 0xfc080000, "v,xOA,X", 4 },
+{ "lvd", 0xb0000002, 0xfc080002, "v,xOA,X", 4 },
+{ "liv", 0x3c040000, 0xfc0f0000, "v,R", 2 },
+{ "livf", 0x3c080000, 0xfc0f0000, "v,R", 2 },
+{ "stvb", 0xd0080000, 0xfc080000, "v,xOA,X", 4 },
+{ "stvh", 0xd0000001, 0xfc080001, "v,xOA,X", 4 },
+{ "stvw", 0xd0000000, 0xfc080000, "v,xOA,X", 4 },
+{ "stvd", 0xd0000002, 0xfc080002, "v,xOA,X", 4 },
+
+{ "trr", 0x2c000000, 0xfc0f0000, "r,R", 2 },
+{ "trn", 0x2c040000, 0xfc0f0000, "r,R", 2 },
+{ "trnd", 0x2c0c0000, 0xfc0f0000, "r,R", 2 },
+{ "trabs", 0x2c010000, 0xfc0f0000, "r,R", 2 },
+{ "trabsd", 0x2c090000, 0xfc0f0000, "r,R", 2 },
+{ "trc", 0x2c030000, 0xfc0f0000, "r,R", 2 },
+{ "xcr", 0x28040000, 0xfc0f0000, "r,R", 2 },
+{ "cxcr", 0x2c060000, 0xfc0f0000, "r,R", 2 },
+{ "cxcrd", 0x2c0e0000, 0xfc0f0000, "r,R", 2 },
+{ "tbrr", 0x2c020000, 0xfc0f0000, "r,B", 2 },
+{ "trbr", 0x28030000, 0xfc0f0000, "b,R", 2 },
+{ "xcbr", 0x28020000, 0xfc0f0000, "b,B", 2 },
+{ "tbrbr", 0x28010000, 0xfc0f0000, "b,B", 2 },
+
+{ "trvv", 0x28050000, 0xfc0f0000, "v,V", 2 },
+{ "trvvn", 0x2c050000, 0xfc0f0000, "v,V", 2 },
+{ "trvvnd", 0x2c0d0000, 0xfc0f0000, "v,V", 2 },
+{ "trvab", 0x2c070000, 0xfc0f0000, "v,V", 2 },
+{ "trvabd", 0x2c0f0000, 0xfc0f0000, "v,V", 2 },
+{ "cmpv", 0x14060000, 0xfc0f0000, "v,V", 2 },
+{ "expv", 0x14070000, 0xfc0f0000, "v,V", 2 },
+{ "mrvvlt", 0x10030000, 0xfc0f0000, "v,V", 2 },
+{ "mrvvle", 0x10040000, 0xfc0f0000, "v,V", 2 },
+{ "mrvvgt", 0x14030000, 0xfc0f0000, "v,V", 2 },
+{ "mrvvge", 0x14040000, 0xfc0f0000, "v,V", 2 },
+{ "mrvveq", 0x10050000, 0xfc0f0000, "v,V", 2 },
+{ "mrvvne", 0x10050000, 0xfc0f0000, "v,V", 2 },
+{ "mrvrlt", 0x100d0000, 0xfc0f0000, "v,R", 2 },
+{ "mrvrle", 0x100e0000, 0xfc0f0000, "v,R", 2 },
+{ "mrvrgt", 0x140d0000, 0xfc0f0000, "v,R", 2 },
+{ "mrvrge", 0x140e0000, 0xfc0f0000, "v,R", 2 },
+{ "mrvreq", 0x100f0000, 0xfc0f0000, "v,R", 2 },
+{ "mrvrne", 0x140f0000, 0xfc0f0000, "v,R", 2 },
+{ "trvr", 0x140b0000, 0xfc0f0000, "r,V", 2 },
+{ "trrv", 0x140c0000, 0xfc0f0000, "v,R", 2 },
+
+{ "bu", 0x40000000, 0xff880000, "xOA,X", 4 },
+{ "bns", 0x70080000, 0xff880000, "xOA,X", 4 },
+{ "bnco", 0x70880000, 0xff880000, "xOA,X", 4 },
+{ "bge", 0x71080000, 0xff880000, "xOA,X", 4 },
+{ "bne", 0x71880000, 0xff880000, "xOA,X", 4 },
+{ "bunge", 0x72080000, 0xff880000, "xOA,X", 4 },
+{ "bunle", 0x72880000, 0xff880000, "xOA,X", 4 },
+{ "bgt", 0x73080000, 0xff880000, "xOA,X", 4 },
+{ "bnany", 0x73880000, 0xff880000, "xOA,X", 4 },
+{ "bs" , 0x70000000, 0xff880000, "xOA,X", 4 },
+{ "bco", 0x70800000, 0xff880000, "xOA,X", 4 },
+{ "blt", 0x71000000, 0xff880000, "xOA,X", 4 },
+{ "beq", 0x71800000, 0xff880000, "xOA,X", 4 },
+{ "buge", 0x72000000, 0xff880000, "xOA,X", 4 },
+{ "bult", 0x72800000, 0xff880000, "xOA,X", 4 },
+{ "ble", 0x73000000, 0xff880000, "xOA,X", 4 },
+{ "bany", 0x73800000, 0xff880000, "xOA,X", 4 },
+{ "brlnk", 0x44000000, 0xfc080000, "r,xOA,X", 4 },
+{ "bib", 0x48000000, 0xfc080000, "r,xOA,X", 4 },
+{ "bih", 0x48080000, 0xfc080000, "r,xOA,X", 4 },
+{ "biw", 0x4c000000, 0xfc080000, "r,xOA,X", 4 },
+{ "bid", 0x4c080000, 0xfc080000, "r,xOA,X", 4 },
+{ "bivb", 0x60000000, 0xfc080000, "r,xOA,X", 4 },
+{ "bivh", 0x60080000, 0xfc080000, "r,xOA,X", 4 },
+{ "bivw", 0x64000000, 0xfc080000, "r,xOA,X", 4 },
+{ "bivd", 0x64080000, 0xfc080000, "r,xOA,X", 4 },
+{ "bvsb", 0x68000000, 0xfc080000, "r,xOA,X", 4 },
+{ "bvsh", 0x68080000, 0xfc080000, "r,xOA,X", 4 },
+{ "bvsw", 0x6c000000, 0xfc080000, "r,xOA,X", 4 },
+{ "bvsd", 0x6c080000, 0xfc080000, "r,xOA,X", 4 },
+
+{ "camb", 0x80080000, 0xfc080000, "r,xOA,X", 4 },
+{ "camh", 0x80000001, 0xfc080001, "r,xOA,X", 4 },
+{ "camw", 0x80000000, 0xfc080000, "r,xOA,X", 4 },
+{ "camd", 0x80000002, 0xfc080002, "r,xOA,X", 4 },
+{ "car", 0x10000000, 0xfc0f0000, "r,R", 2 },
+{ "card", 0x14000000, 0xfc0f0000, "r,R", 2 },
+{ "ci", 0xf8050000, 0xfc7f0000, "r,I", 4 },
+{ "chkbnd", 0x5c080000, 0xfc080000, "r,xOA,X", 4 },
+
+{ "cavv", 0x10010000, 0xfc0f0000, "v,V", 2 },
+{ "cavr", 0x10020000, 0xfc0f0000, "v,R", 2 },
+{ "cavvd", 0x10090000, 0xfc0f0000, "v,V", 2 },
+{ "cavrd", 0x100b0000, 0xfc0f0000, "v,R", 2 },
+
+{ "anmb", 0x84080000, 0xfc080000, "r,xOA,X", 4 },
+{ "anmh", 0x84000001, 0xfc080001, "r,xOA,X", 4 },
+{ "anmw", 0x84000000, 0xfc080000, "r,xOA,X", 4 },
+{ "anmd", 0x84000002, 0xfc080002, "r,xOA,X", 4 },
+{ "anr", 0x04000000, 0xfc0f0000, "r,R", 2 },
+{ "ani", 0xf8080000, 0xfc7f0000, "r,I", 4 },
+{ "ormb", 0xb8080000, 0xfc080000, "r,xOA,X", 4 },
+{ "ormh", 0xb8000001, 0xfc080001, "r,xOA,X", 4 },
+{ "ormw", 0xb8000000, 0xfc080000, "r,xOA,X", 4 },
+{ "ormd", 0xb8000002, 0xfc080002, "r,xOA,X", 4 },
+{ "orr", 0x08000000, 0xfc0f0000, "r,R", 2 },
+{ "oi", 0xf8090000, 0xfc7f0000, "r,I", 4 },
+{ "eomb", 0x8c080000, 0xfc080000, "r,xOA,X", 4 },
+{ "eomh", 0x8c000001, 0xfc080001, "r,xOA,X", 4 },
+{ "eomw", 0x8c000000, 0xfc080000, "r,xOA,X", 4 },
+{ "eomd", 0x8c000002, 0xfc080002, "r,xOA,X", 4 },
+{ "eor", 0x0c000000, 0xfc0f0000, "r,R", 2 },
+{ "eoi", 0xf80a0000, 0xfc7f0000, "r,I", 4 },
+
+{ "anvv", 0x04010000, 0xfc0f0000, "v,V", 2 },
+{ "anvr", 0x04020000, 0xfc0f0000, "v,R", 2 },
+{ "orvv", 0x08010000, 0xfc0f0000, "v,V", 2 },
+{ "orvr", 0x08020000, 0xfc0f0000, "v,R", 2 },
+{ "eovv", 0x0c010000, 0xfc0f0000, "v,V", 2 },
+{ "eovr", 0x0c020000, 0xfc0f0000, "v,R", 2 },
+
+{ "sacz", 0x100c0000, 0xfc0f0000, "r,R", 2 },
+{ "sla", 0x1c400000, 0xfc600000, "r,S", 2 },
+{ "sll", 0x1c600000, 0xfc600000, "r,S", 2 },
+{ "slc", 0x24400000, 0xfc600000, "r,S", 2 },
+{ "slad", 0x20400000, 0xfc600000, "r,S", 2 },
+{ "slld", 0x20600000, 0xfc600000, "r,S", 2 },
+{ "sra", 0x1c000000, 0xfc600000, "r,S", 2 },
+{ "srl", 0x1c200000, 0xfc600000, "r,S", 2 },
+{ "src", 0x24000000, 0xfc600000, "r,S", 2 },
+{ "srad", 0x20000000, 0xfc600000, "r,S", 2 },
+{ "srld", 0x20200000, 0xfc600000, "r,S", 2 },
+{ "sda", 0x3c030000, 0xfc0f0000, "r,R", 2 },
+{ "sdl", 0x3c020000, 0xfc0f0000, "r,R", 2 },
+{ "sdc", 0x3c010000, 0xfc0f0000, "r,R", 2 },
+{ "sdad", 0x3c0b0000, 0xfc0f0000, "r,R", 2 },
+{ "sdld", 0x3c0a0000, 0xfc0f0000, "r,R", 2 },
+
+{ "svda", 0x3c070000, 0xfc0f0000, "v,R", 2 },
+{ "svdl", 0x3c060000, 0xfc0f0000, "v,R", 2 },
+{ "svdc", 0x3c050000, 0xfc0f0000, "v,R", 2 },
+{ "svdad", 0x3c0e0000, 0xfc0f0000, "v,R", 2 },
+{ "svdld", 0x3c0d0000, 0xfc0f0000, "v,R", 2 },
+
+{ "sbm", 0xac080000, 0xfc080000, "f,xOA,X", 4 },
+{ "zbm", 0xac000000, 0xfc080000, "f,xOA,X", 4 },
+{ "tbm", 0xa8080000, 0xfc080000, "f,xOA,X", 4 },
+{ "incmb", 0xa0000000, 0xfc080000, "xOA,X", 4 },
+{ "incmh", 0xa0080000, 0xfc080000, "xOA,X", 4 },
+{ "incmw", 0xa4000000, 0xfc080000, "xOA,X", 4 },
+{ "incmd", 0xa4080000, 0xfc080000, "xOA,X", 4 },
+{ "sbmd", 0x7c080000, 0xfc080000, "r,xOA,X", 4 },
+{ "zbmd", 0x7c000000, 0xfc080000, "r,xOA,X", 4 },
+{ "tbmd", 0x78080000, 0xfc080000, "r,xOA,X", 4 },
+
+{ "ssm", 0x9c080000, 0xfc080000, "f,xOA,X", 4 },
+{ "zsm", 0x9c000000, 0xfc080000, "f,xOA,X", 4 },
+{ "tsm", 0x98080000, 0xfc080000, "f,xOA,X", 4 },
+
+{ "admb", 0xc8080000, 0xfc080000, "r,xOA,X", 4 },
+{ "admh", 0xc8000001, 0xfc080001, "r,xOA,X", 4 },
+{ "admw", 0xc8000000, 0xfc080000, "r,xOA,X", 4 },
+{ "admd", 0xc8000002, 0xfc080002, "r,xOA,X", 4 },
+{ "adr", 0x38000000, 0xfc0f0000, "r,R", 2 },
+{ "armb", 0xe8080000, 0xfc080000, "r,xOA,X", 4 },
+{ "armh", 0xe8000001, 0xfc080001, "r,xOA,X", 4 },
+{ "armw", 0xe8000000, 0xfc080000, "r,xOA,X", 4 },
+{ "armd", 0xe8000002, 0xfc080002, "r,xOA,X", 4 },
+{ "adi", 0xf8010000, 0xfc0f0000, "r,I", 4 },
+{ "sumb", 0xcc080000, 0xfc080000, "r,xOA,X", 4 },
+{ "sumh", 0xcc000001, 0xfc080001, "r,xOA,X", 4 },
+{ "sumw", 0xcc000000, 0xfc080000, "r,xOA,X", 4 },
+{ "sumd", 0xcc000002, 0xfc080002, "r,xOA,X", 4 },
+{ "sur", 0x3c000000, 0xfc0f0000, "r,R", 2 },
+{ "sui", 0xf8020000, 0xfc0f0000, "r,I", 4 },
+{ "mpmb", 0xc0080000, 0xfc080000, "r,xOA,X", 4 },
+{ "mpmh", 0xc0000001, 0xfc080001, "r,xOA,X", 4 },
+{ "mpmw", 0xc0000000, 0xfc080000, "r,xOA,X", 4 },
+{ "mpr", 0x38020000, 0xfc0f0000, "r,R", 2 },
+{ "mprd", 0x3c0f0000, 0xfc0f0000, "r,R", 2 },
+{ "mpi", 0xf8030000, 0xfc0f0000, "r,I", 4 },
+{ "dvmb", 0xc4080000, 0xfc080000, "r,xOA,X", 4 },
+{ "dvmh", 0xc4000001, 0xfc080001, "r,xOA,X", 4 },
+{ "dvmw", 0xc4000000, 0xfc080000, "r,xOA,X", 4 },
+{ "dvr", 0x380a0000, 0xfc0f0000, "r,R", 2 },
+{ "dvi", 0xf8040000, 0xfc0f0000, "r,I", 4 },
+{ "exs", 0x38080000, 0xfc0f0000, "r,R", 2 },
+
+{ "advv", 0x30000000, 0xfc0f0000, "v,V", 2 },
+{ "advvd", 0x30080000, 0xfc0f0000, "v,V", 2 },
+{ "adrv", 0x34000000, 0xfc0f0000, "v,R", 2 },
+{ "adrvd", 0x34080000, 0xfc0f0000, "v,R", 2 },
+{ "suvv", 0x30010000, 0xfc0f0000, "v,V", 2 },
+{ "suvvd", 0x30090000, 0xfc0f0000, "v,V", 2 },
+{ "surv", 0x34010000, 0xfc0f0000, "v,R", 2 },
+{ "survd", 0x34090000, 0xfc0f0000, "v,R", 2 },
+{ "mpvv", 0x30020000, 0xfc0f0000, "v,V", 2 },
+{ "mprv", 0x34020000, 0xfc0f0000, "v,R", 2 },
+
+{ "adfw", 0xe0080000, 0xfc080000, "r,xOA,X", 4 },
+{ "adfd", 0xe0080002, 0xfc080002, "r,xOA,X", 4 },
+{ "adrfw", 0x38010000, 0xfc0f0000, "r,R", 2 },
+{ "adrfd", 0x38090000, 0xfc0f0000, "r,R", 2 },
+{ "surfw", 0xe0000000, 0xfc080000, "r,xOA,X", 4 },
+{ "surfd", 0xe0000002, 0xfc080002, "r,xOA,X", 4 },
+{ "surfw", 0x38030000, 0xfc0f0000, "r,R", 2 },
+{ "surfd", 0x380b0000, 0xfc0f0000, "r,R", 2 },
+{ "mpfw", 0xe4080000, 0xfc080000, "r,xOA,X", 4 },
+{ "mpfd", 0xe4080002, 0xfc080002, "r,xOA,X", 4 },
+{ "mprfw", 0x38060000, 0xfc0f0000, "r,R", 2 },
+{ "mprfd", 0x380e0000, 0xfc0f0000, "r,R", 2 },
+{ "rfw", 0xe4000000, 0xfc080000, "r,xOA,X", 4 },
+{ "rfd", 0xe4000002, 0xfc080002, "r,xOA,X", 4 },
+{ "rrfw", 0x0c0e0000, 0xfc0f0000, "r", 2 },
+{ "rrfd", 0x0c0f0000, 0xfc0f0000, "r", 2 },
+
+{ "advvfw", 0x30040000, 0xfc0f0000, "v,V", 2 },
+{ "advvfd", 0x300c0000, 0xfc0f0000, "v,V", 2 },
+{ "adrvfw", 0x34040000, 0xfc0f0000, "v,R", 2 },
+{ "adrvfd", 0x340c0000, 0xfc0f0000, "v,R", 2 },
+{ "suvvfw", 0x30050000, 0xfc0f0000, "v,V", 2 },
+{ "suvvfd", 0x300d0000, 0xfc0f0000, "v,V", 2 },
+{ "survfw", 0x34050000, 0xfc0f0000, "v,R", 2 },
+{ "survfd", 0x340d0000, 0xfc0f0000, "v,R", 2 },
+{ "mpvvfw", 0x30060000, 0xfc0f0000, "v,V", 2 },
+{ "mpvvfd", 0x300e0000, 0xfc0f0000, "v,V", 2 },
+{ "mprvfw", 0x34060000, 0xfc0f0000, "v,R", 2 },
+{ "mprvfd", 0x340e0000, 0xfc0f0000, "v,R", 2 },
+{ "rvfw", 0x30070000, 0xfc0f0000, "v", 2 },
+{ "rvfd", 0x300f0000, 0xfc0f0000, "v", 2 },
+
+{ "fltw", 0x38070000, 0xfc0f0000, "r,R", 2 },
+{ "fltd", 0x380f0000, 0xfc0f0000, "r,R", 2 },
+{ "fixw", 0x38050000, 0xfc0f0000, "r,R", 2 },
+{ "fixd", 0x380d0000, 0xfc0f0000, "r,R", 2 },
+{ "cfpds", 0x3c090000, 0xfc0f0000, "r,R", 2 },
+
+{ "fltvw", 0x080d0000, 0xfc0f0000, "v,V", 2 },
+{ "fltvd", 0x080f0000, 0xfc0f0000, "v,V", 2 },
+{ "fixvw", 0x080c0000, 0xfc0f0000, "v,V", 2 },
+{ "fixvd", 0x080e0000, 0xfc0f0000, "v,V", 2 },
+{ "cfpvds", 0x0c0d0000, 0xfc0f0000, "v,V", 2 },
+
+{ "orvrn", 0x000a0000, 0xfc0f0000, "r,V", 2 },
+{ "andvrn", 0x00080000, 0xfc0f0000, "r,V", 2 },
+{ "frsteq", 0x04090000, 0xfc0f0000, "r,V", 2 },
+{ "sigma", 0x0c080000, 0xfc0f0000, "r,V", 2 },
+{ "sigmad", 0x0c0a0000, 0xfc0f0000, "r,V", 2 },
+{ "sigmf", 0x08080000, 0xfc0f0000, "r,V", 2 },
+{ "sigmfd", 0x080a0000, 0xfc0f0000, "r,V", 2 },
+{ "prodf", 0x04080000, 0xfc0f0000, "r,V", 2 },
+{ "prodfd", 0x040a0000, 0xfc0f0000, "r,V", 2 },
+{ "maxv", 0x10080000, 0xfc0f0000, "r,V", 2 },
+{ "maxvd", 0x100a0000, 0xfc0f0000, "r,V", 2 },
+{ "minv", 0x14080000, 0xfc0f0000, "r,V", 2 },
+{ "minvd", 0x140a0000, 0xfc0f0000, "r,V", 2 },
+
+{ "lpsd", 0xf0000000, 0xfc080000, "xOA,X", 4 },
+{ "ldc", 0xf0080000, 0xfc080000, "xOA,X", 4 },
+{ "spm", 0x040c0000, 0xfc0f0000, "r", 2 },
+{ "rpm", 0x040d0000, 0xfc0f0000, "r", 2 },
+{ "tritr", 0x00070000, 0xfc0f0000, "r", 2 },
+{ "trrit", 0x00060000, 0xfc0f0000, "r", 2 },
+{ "rpswt", 0x04080000, 0xfc0f0000, "r", 2 },
+{ "exr", 0xf8070000, 0xfc0f0000, "", 4 },
+{ "halt", 0x00000000, 0xfc0f0000, "", 2 },
+{ "wait", 0x00010000, 0xfc0f0000, "", 2 },
+{ "nop", 0x00020000, 0xfc0f0000, "", 2 },
+{ "eiae", 0x00030000, 0xfc0f0000, "", 2 },
+{ "efae", 0x000d0000, 0xfc0f0000, "", 2 },
+{ "diae", 0x000e0000, 0xfc0f0000, "", 2 },
+{ "dfae", 0x000f0000, 0xfc0f0000, "", 2 },
+{ "spvc", 0xf8060000, 0xfc0f0000, "r,T,N", 4 },
+{ "rdsts", 0x00090000, 0xfc0f0000, "r", 2 },
+{ "setcpu", 0x000c0000, 0xfc0f0000, "r", 2 },
+{ "cmc", 0x000b0000, 0xfc0f0000, "r", 2 },
+{ "trrcu", 0x00040000, 0xfc0f0000, "r", 2 },
+{ "attnio", 0x00050000, 0xfc0f0000, "", 2 },
+{ "fudit", 0x28080000, 0xfc0f0000, "", 2 },
+{ "break", 0x28090000, 0xfc0f0000, "", 2 },
+{ "frzss", 0x280a0000, 0xfc0f0000, "", 2 },
+{ "ripi", 0x04040000, 0xfc0f0000, "r,R", 2 },
+{ "xcp", 0x04050000, 0xfc0f0000, "r", 2 },
+{ "block", 0x04060000, 0xfc0f0000, "", 2 },
+{ "unblock", 0x04070000, 0xfc0f0000, "", 2 },
+{ "trsc", 0x08060000, 0xfc0f0000, "r,R", 2 },
+{ "tscr", 0x08070000, 0xfc0f0000, "r,R", 2 },
+{ "fq", 0x04080000, 0xfc0f0000, "r", 2 },
+{ "flupte", 0x2c080000, 0xfc0f0000, "r", 2 },
+{ "rviu", 0x040f0000, 0xfc0f0000, "", 2 },
+{ "ldel", 0x280c0000, 0xfc0f0000, "r,R", 2 },
+{ "ldu", 0x280d0000, 0xfc0f0000, "r,R", 2 },
+{ "stdecc", 0x280b0000, 0xfc0f0000, "r,R", 2 },
+{ "trpc", 0x08040000, 0xfc0f0000, "r", 2 },
+{ "tpcr", 0x08050000, 0xfc0f0000, "r", 2 },
+{ "ghalt", 0x0c050000, 0xfc0f0000, "r", 2 },
+{ "grun", 0x0c040000, 0xfc0f0000, "", 2 },
+{ "tmpr", 0x2c0a0000, 0xfc0f0000, "r,R", 2 },
+{ "trmp", 0x2c0b0000, 0xfc0f0000, "r,R", 2 },
+
+{ "trrve", 0x28060000, 0xfc0f0000, "r", 2 },
+{ "trver", 0x28070000, 0xfc0f0000, "r", 2 },
+{ "trvlr", 0x280f0000, 0xfc0f0000, "r", 2 },
+
+{ "linkfl", 0x18000000, 0xfc0f0000, "r,R", 2 },
+{ "linkbl", 0x18020000, 0xfc0f0000, "r,R", 2 },
+{ "linkfp", 0x18010000, 0xfc0f0000, "r,R", 2 },
+{ "linkbp", 0x18030000, 0xfc0f0000, "r,R", 2 },
+{ "linkpl", 0x18040000, 0xfc0f0000, "r,R", 2 },
+{ "ulinkl", 0x18080000, 0xfc0f0000, "r,R", 2 },
+{ "ulinkp", 0x18090000, 0xfc0f0000, "r,R", 2 },
+{ "ulinktl", 0x180a0000, 0xfc0f0000, "r,R", 2 },
+{ "ulinktp", 0x180b0000, 0xfc0f0000, "r,R", 2 },
+};
+
+int numopcodes = sizeof(gld_opcodes) / sizeof(gld_opcodes[0]);
+
+struct gld_opcode *endop = gld_opcodes + sizeof(gld_opcodes) /
+ sizeof(gld_opcodes[0]);
diff --git a/gnu/usr.bin/as/opcode/ns32k.h b/gnu/usr.bin/as/opcode/ns32k.h
new file mode 100644
index 0000000..2a7621a
--- /dev/null
+++ b/gnu/usr.bin/as/opcode/ns32k.h
@@ -0,0 +1,491 @@
+/* ns32k-opcode.h -- Opcode table for National Semi 32k processor
+ Copyright (C) 1987 Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#ifdef TE_SEQUENT
+#define DEF_MODEC 20
+#define DEF_MODEL 21
+#endif
+
+#ifndef DEF_MODEC
+#define DEF_MODEC 20
+#endif
+
+#ifndef DEF_MODEL
+#define DEF_MODEL 20
+#endif
+/*
+ After deciding the instruction entry (via hash.c) the instruction parser
+ will try to match the operands after the instruction to the required set
+ given in the entry operandfield. Every operand will result in a change in
+ the opcode or the addition of data to the opcode.
+ The operands in the source instruction are checked for inconsistent
+ semantics.
+
+ F : 32 bit float general form
+ L : 64 bit float "
+ B : byte "
+ W : word "
+ D : double-word "
+ Q : quad-word "
+ A : double-word gen-address-form ie no regs allowed
+ d : displacement
+ b : displacement - pc relative addressing acb
+ p : displacement - pc relative addressing br bcond bsr cxp
+ q : quick
+ i : immediate (8 bits)
+ This is not a standard ns32k operandtype, it is used to build
+ instructions like svc arg1,arg2
+ Svc is the instruction SuperVisorCall and is sometimes used to
+ call OS-routines from usermode. Some args might be handy!
+ r : register number (3 bits)
+ O : setcfg instruction optionslist
+ C : cinv instruction optionslist
+ S : stringinstruction optionslist
+ U : registerlist save,enter
+ u : registerlist restore,exit
+ M : mmu register
+ P : cpu register
+ g : 3:rd operand of inss or exts instruction
+ G : 4:th operand of inss or exts instruction
+ Those operands are encoded in the same byte.
+ This byte is placed last in the instruction.
+ f : operand of sfsr
+ H : sequent-hack for bsr (Warning)
+
+column 1 instructions
+ 2 number of bits in opcode.
+ 3 number of bits in opcode explicitly
+ determined by the instruction type.
+ 4 opcodeseed, the number we build our opcode
+ from.
+ 5 operandtypes, used by operandparser.
+ 6 size in bytes of immediate
+*/
+struct ns32k_opcode {
+ char *name;
+ unsigned char opcode_id_size; /* not used by the assembler */
+ unsigned char opcode_size;
+ unsigned long opcode_seed;
+ char *operands;
+ unsigned char im_size; /* not used by dissassembler */
+ char *default_args; /* default to those args when none given */
+ char default_modec; /* default to this addr-mode when ambigous
+ ie when the argument of a general addr-mode
+ is a plain constant */
+ char default_model; /* is a plain label */
+};
+
+#ifdef comment
+/* This section was from the gdb version of this file. */
+
+#ifndef ns32k_opcodeT
+#define ns32k_opcodeT int
+#endif /* no ns32k_opcodeT */
+
+struct not_wot /* ns32k opcode table: wot to do with this */
+ /* particular opcode */
+{
+ int obits; /* number of opcode bits */
+ int ibits; /* number of instruction bits */
+ ns32k_opcodeT code; /* op-code (may be > 8 bits!) */
+ char *args; /* how to compile said opcode */
+};
+
+struct not /* ns32k opcode text */
+{
+ char * name; /* opcode name: lowercase string [key] */
+ struct not_wot detail; /* rest of opcode table [datum] */
+};
+
+/* Instructions look like this:
+
+ basic instruction--1, 2, or 3 bytes
+ index byte for operand A, if operand A is indexed--1 byte
+ index byte for operand B, if operand B is indexed--1 byte
+ addressing extension for operand A
+ addressing extension for operand B
+ implied operands
+
+ Operand A is the operand listed first in the following opcode table.
+ Operand B is the operand listed second in the following opcode table.
+ All instructions have at most 2 general operands, so this is enough.
+ The implied operands are associated with operands other than A and B.
+
+ Each operand has a digit and a letter.
+
+ The digit gives the position in the assembly language. The letter,
+ one of the following, tells us what kind of operand it is. */
+
+/* F : 32 bit float
+ * L : 64 bit float
+ * B : byte
+ * W : word
+ * D : double-word
+ * Q : quad-word
+ * d : displacement
+ * q : quick
+ * i : immediate (8 bits)
+ * r : register number (3 bits)
+ * p : displacement - pc relative addressing
+*/
+
+
+#endif /* comment */
+
+static const struct ns32k_opcode ns32k_opcodes[]=
+{
+ { "absf", 14,24, 0x35be, "1F2F", 4, "", DEF_MODEC,DEF_MODEL },
+ { "absl", 14,24, 0x34be, "1L2L", 8, "", DEF_MODEC,DEF_MODEL },
+ { "absb", 14,24, 0x304e, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "absw", 14,24, 0x314e, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "absd", 14,24, 0x334e, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "acbb", 7,16, 0x4c, "2B1q3p", 1, "", DEF_MODEC,DEF_MODEL },
+ { "acbw", 7,16, 0x4d, "2W1q3p", 2, "", DEF_MODEC,DEF_MODEL },
+ { "acbd", 7,16, 0x4f, "2D1q3p", 4, "", DEF_MODEC,DEF_MODEL },
+ { "addf", 14,24, 0x01be, "1F2F", 4, "", DEF_MODEC,DEF_MODEL },
+ { "addl", 14,24, 0x00be, "1L2L", 8, "", DEF_MODEC,DEF_MODEL },
+ { "addb", 6,16, 0x00, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "addw", 6,16, 0x01, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "addd", 6,16, 0x03, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "addcb", 6,16, 0x10, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "addcw", 6,16, 0x11, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "addcd", 6,16, 0x13, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "addpb", 14,24, 0x3c4e, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "addpw", 14,24, 0x3d4e, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "addpd", 14,24, 0x3f4e, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "addqb", 7,16, 0x0c, "2B1q", 1, "", DEF_MODEC,DEF_MODEL },
+ { "addqw", 7,16, 0x0d, "2W1q", 2, "", DEF_MODEC,DEF_MODEL },
+ { "addqd", 7,16, 0x0f, "2D1q", 4, "", DEF_MODEC,DEF_MODEL },
+ { "addr", 6,16, 0x27, "1A2D", 4, "", 21,21 },
+ { "adjspb", 11,16, 0x057c, "1B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "adjspw", 11,16, 0x057d, "1W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "adjspd", 11,16, 0x057f, "1D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "andb", 6,16, 0x28, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "andw", 6,16, 0x29, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "andd", 6,16, 0x2b, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "ashb", 14,24, 0x044e, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "ashw", 14,24, 0x054e, "1B2W", 1, "", DEF_MODEC,DEF_MODEL },
+ { "ashd", 14,24, 0x074e, "1B2D", 1, "", DEF_MODEC,DEF_MODEL },
+ { "beq", 8,8, 0x0a, "1p", 0, "", 21,21 },
+ { "bne", 8,8, 0x1a, "1p", 0, "", 21,21 },
+ { "bcs", 8,8, 0x2a, "1p", 0, "", 21,21 },
+ { "bcc", 8,8, 0x3a, "1p", 0, "", 21,21 },
+ { "bhi", 8,8, 0x4a, "1p", 0, "", 21,21 },
+ { "bls", 8,8, 0x5a, "1p", 0, "", 21,21 },
+ { "bgt", 8,8, 0x6a, "1p", 0, "", 21,21 },
+ { "ble", 8,8, 0x7a, "1p", 0, "", 21,21 },
+ { "bfs", 8,8, 0x8a, "1p", 0, "", 21,21 },
+ { "bfc", 8,8, 0x9a, "1p", 0, "", 21,21 },
+ { "blo", 8,8, 0xaa, "1p", 0, "", 21,21 },
+ { "bhs", 8,8, 0xba, "1p", 0, "", 21,21 },
+ { "blt", 8,8, 0xca, "1p", 0, "", 21,21 },
+ { "bge", 8,8, 0xda, "1p", 0, "", 21,21 },
+ { "but", 8,8, 0xea, "1p", 0, "", 21,21 },
+ { "buf", 8,8, 0xfa, "1p", 0, "", 21,21 },
+ { "bicb", 6,16, 0x08, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "bicw", 6,16, 0x09, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "bicd", 6,16, 0x0b, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "bicpsrb", 11,16, 0x17c, "1B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "bicpsrw", 11,16, 0x17d, "1W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "bispsrb", 11,16, 0x37c, "1B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "bispsrw", 11,16, 0x37d, "1W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "bpt", 8,8, 0xf2, "", 0, "", DEF_MODEC,DEF_MODEL },
+ { "br", 8,8, 0xea, "1p", 0, "", 21,21 },
+#ifdef TE_SEQUENT
+ { "bsr", 8,8, 0x02, "1H", 0, "", 21,21 },
+#else
+ { "bsr", 8,8, 0x02, "1p", 0, "", 21,21 },
+#endif
+ { "caseb", 11,16, 0x77c, "1B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "casew", 11,16, 0x77d, "1W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "cased", 11,16, 0x77f, "1D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "cbitb", 14,24, 0x084e, "1B2D", 1, "", DEF_MODEC,DEF_MODEL },
+ { "cbitw", 14,24, 0x094e, "1W2D", 2, "", DEF_MODEC,DEF_MODEL },
+ { "cbitd", 14,24, 0x0b4e, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "cbitib", 14,24, 0x0c4e, "1B2D", 1, "", DEF_MODEC,DEF_MODEL },
+ { "cbitiw", 14,24, 0x0d4e, "1W2D", 2, "", DEF_MODEC,DEF_MODEL },
+ { "cbitid", 14,24, 0x0f4e, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "checkb", 11,24, 0x0ee, "2A3B1r", 1, "", DEF_MODEC,DEF_MODEL },
+ { "checkw", 11,24, 0x1ee, "2A3W1r", 2, "", DEF_MODEC,DEF_MODEL },
+ { "checkd", 11,24, 0x3ee, "2A3D1r", 4, "", DEF_MODEC,DEF_MODEL },
+ { "cinv", 14,24, 0x271e, "2D1C", 4, "", DEF_MODEC,DEF_MODEL },
+ { "cmpf", 14,24, 0x09be, "1F2F", 4, "", DEF_MODEC,DEF_MODEL },
+ { "cmpl", 14,24, 0x08be, "1L2L", 8, "", DEF_MODEC,DEF_MODEL },
+ { "cmpb", 6,16, 0x04, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "cmpw", 6,16, 0x05, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "cmpd", 6,16, 0x07, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "cmpmb", 14,24, 0x04ce, "1A2A3b", 1, "", DEF_MODEC,DEF_MODEL },
+ { "cmpmw", 14,24, 0x05ce, "1A2A3b", 2, "", DEF_MODEC,DEF_MODEL },
+ { "cmpmd", 14,24, 0x07ce, "1A2A3b", 4, "", DEF_MODEC,DEF_MODEL },
+ { "cmpqb", 7,16, 0x1c, "2B1q", 1, "", DEF_MODEC,DEF_MODEL },
+ { "cmpqw", 7,16, 0x1d, "2W1q", 2, "", DEF_MODEC,DEF_MODEL },
+ { "cmpqd", 7,16, 0x1f, "2D1q", 4, "", DEF_MODEC,DEF_MODEL },
+ { "cmpsb", 16,24, 0x040e, "1S", 0, "[]", DEF_MODEC,DEF_MODEL },
+ { "cmpsw", 16,24, 0x050e, "1S", 0, "[]", DEF_MODEC,DEF_MODEL },
+ { "cmpsd", 16,24, 0x070e, "1S", 0, "[]", DEF_MODEC,DEF_MODEL },
+ { "cmpst", 16,24, 0x840e, "1S", 0, "[]", DEF_MODEC,DEF_MODEL },
+ { "comb", 14,24, 0x344e, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "comw", 14,24, 0x354e, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "comd", 14,24, 0x374e, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "cvtp", 11,24, 0x036e, "2A3D1r", 4, "", DEF_MODEC,DEF_MODEL },
+ { "cxp", 8,8, 0x22, "1p", 0, "", 21,21 },
+ { "cxpd", 11,16, 0x07f, "1A", 4, "", DEF_MODEC,DEF_MODEL },
+ { "deib", 14,24, 0x2cce, "1B2W", 1, "", DEF_MODEC,DEF_MODEL },
+ { "deiw", 14,24, 0x2dce, "1W2D", 2, "", DEF_MODEC,DEF_MODEL },
+ { "deid", 14,24, 0x2fce, "1D2Q", 4, "", DEF_MODEC,DEF_MODEL },
+ { "dia", 8,8, 0xc2, "", 1, "", DEF_MODEC,DEF_MODEL },
+ { "divf", 14,24, 0x21be, "1F2F", 4, "", DEF_MODEC,DEF_MODEL },
+ { "divl", 14,24, 0x20be, "1L2L", 8, "", DEF_MODEC,DEF_MODEL },
+ { "divb", 14,24, 0x3cce, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "divw", 14,24, 0x3dce, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "divd", 14,24, 0x3fce, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "enter", 8,8, 0x82, "1U2d", 0, "", DEF_MODEC,DEF_MODEL },
+ { "exit", 8,8, 0x92, "1u", 0, "", DEF_MODEC,DEF_MODEL },
+ { "extb", 11,24, 0x02e, "2D3B1r4d", 1, "", DEF_MODEC,DEF_MODEL },
+ { "extw", 11,24, 0x12e, "2D3W1r4d", 2, "", DEF_MODEC,DEF_MODEL },
+ { "extd", 11,24, 0x32e, "2D3D1r4d", 4, "", DEF_MODEC,DEF_MODEL },
+ { "extsb", 14,24, 0x0cce, "1D2B3g4G", 1, "", DEF_MODEC,DEF_MODEL },
+ { "extsw", 14,24, 0x0dce, "1D2W3g4G", 2, "", DEF_MODEC,DEF_MODEL },
+ { "extsd", 14,24, 0x0fce, "1D2D3g4G", 4, "", DEF_MODEC,DEF_MODEL },
+ { "ffsb", 14,24, 0x046e, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "ffsw", 14,24, 0x056e, "1W2B", 2, "", DEF_MODEC,DEF_MODEL },
+ { "ffsd", 14,24, 0x076e, "1D2B", 4, "", DEF_MODEC,DEF_MODEL },
+ { "flag", 8,8, 0xd2, "", 0, "", DEF_MODEC,DEF_MODEL },
+ { "floorfb", 14,24, 0x3c3e, "1F2B", 4, "", DEF_MODEC,DEF_MODEL },
+ { "floorfw", 14,24, 0x3d3e, "1F2W", 4, "", DEF_MODEC,DEF_MODEL },
+ { "floorfd", 14,24, 0x3f3e, "1F2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "floorlb", 14,24, 0x383e, "1L2B", 8, "", DEF_MODEC,DEF_MODEL },
+ { "floorlw", 14,24, 0x393e, "1L2W", 8, "", DEF_MODEC,DEF_MODEL },
+ { "floorld", 14,24, 0x3b3e, "1L2D", 8, "", DEF_MODEC,DEF_MODEL },
+ { "ibitb", 14,24, 0x384e, "1B2D", 1, "", DEF_MODEC,DEF_MODEL },
+ { "ibitw", 14,24, 0x394e, "1W2D", 2, "", DEF_MODEC,DEF_MODEL },
+ { "ibitd", 14,24, 0x3b4e, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "indexb", 11,24, 0x42e, "2B3B1r", 1, "", DEF_MODEC,DEF_MODEL },
+ { "indexw", 11,24, 0x52e, "2W3W1r", 2, "", DEF_MODEC,DEF_MODEL },
+ { "indexd", 11,24, 0x72e, "2D3D1r", 4, "", DEF_MODEC,DEF_MODEL },
+ { "insb", 11,24, 0x0ae, "2B3B1r4d", 1, "", DEF_MODEC,DEF_MODEL },
+ { "insw", 11,24, 0x1ae, "2W3W1r4d", 2, "", DEF_MODEC,DEF_MODEL },
+ { "insd", 11,24, 0x3ae, "2D3D1r4d", 4, "", DEF_MODEC,DEF_MODEL },
+ { "inssb", 14,24, 0x08ce, "1B2D3g4G", 1, "", DEF_MODEC,DEF_MODEL },
+ { "inssw", 14,24, 0x09ce, "1W2D3g4G", 2, "", DEF_MODEC,DEF_MODEL },
+ { "inssd", 14,24, 0x0bce, "1D2D3g4G", 4, "", DEF_MODEC,DEF_MODEL },
+ { "jsr", 11,16, 0x67f, "1A", 4, "", 21,21 },
+ { "jump", 11,16, 0x27f, "1A", 4, "", 21,21 },
+ { "lfsr", 19,24, 0x00f3e,"1D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "lmr", 15,24, 0x0b1e, "2D1M", 4, "", DEF_MODEC,DEF_MODEL },
+ { "lprb", 7,16, 0x6c, "2B1P", 1, "", DEF_MODEC,DEF_MODEL },
+ { "lprw", 7,16, 0x6d, "2W1P", 2, "", DEF_MODEC,DEF_MODEL },
+ { "lprd", 7,16, 0x6f, "2D1P", 4, "", DEF_MODEC,DEF_MODEL },
+ { "lshb", 14,24, 0x144e, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "lshw", 14,24, 0x154e, "1B2W", 1, "", DEF_MODEC,DEF_MODEL },
+ { "lshd", 14,24, 0x174e, "1B2D", 1, "", DEF_MODEC,DEF_MODEL },
+ { "meib", 14,24, 0x24ce, "1B2W", 1, "", DEF_MODEC,DEF_MODEL },
+ { "meiw", 14,24, 0x25ce, "1W2D", 2, "", DEF_MODEC,DEF_MODEL },
+ { "meid", 14,24, 0x27ce, "1D2Q", 4, "", DEF_MODEC,DEF_MODEL },
+ { "modb", 14,24, 0x38ce, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "modw", 14,24, 0x39ce, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "modd", 14,24, 0x3bce, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "movf", 14,24, 0x05be, "1F2F", 4, "", DEF_MODEC,DEF_MODEL },
+ { "movl", 14,24, 0x04be, "1L2L", 8, "", DEF_MODEC,DEF_MODEL },
+ { "movb", 6,16, 0x14, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "movw", 6,16, 0x15, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "movd", 6,16, 0x17, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "movbf", 14,24, 0x043e, "1B2F", 1, "", DEF_MODEC,DEF_MODEL },
+ { "movwf", 14,24, 0x053e, "1W2F", 2, "", DEF_MODEC,DEF_MODEL },
+ { "movdf", 14,24, 0x073e, "1D2F", 4, "", DEF_MODEC,DEF_MODEL },
+ { "movbl", 14,24, 0x003e, "1B2L", 1, "", DEF_MODEC,DEF_MODEL },
+ { "movwl", 14,24, 0x013e, "1W2L", 2, "", DEF_MODEC,DEF_MODEL },
+ { "movdl", 14,24, 0x033e, "1D2L", 4, "", DEF_MODEC,DEF_MODEL },
+ { "movfl", 14,24, 0x1b3e, "1F2L", 4, "", DEF_MODEC,DEF_MODEL },
+ { "movlf", 14,24, 0x163e, "1L2F", 8, "", DEF_MODEC,DEF_MODEL },
+ { "movmb", 14,24, 0x00ce, "1A2A3b", 1, "", DEF_MODEC,DEF_MODEL },
+ { "movmw", 14,24, 0x01ce, "1A2A3b", 2, "", DEF_MODEC,DEF_MODEL },
+ { "movmd", 14,24, 0x03ce, "1A2A3b", 4, "", DEF_MODEC,DEF_MODEL },
+ { "movqb", 7,16, 0x5c, "2B1q", 1, "", DEF_MODEC,DEF_MODEL },
+ { "movqw", 7,16, 0x5d, "2B1q", 2, "", DEF_MODEC,DEF_MODEL },
+ { "movqd", 7,16, 0x5f, "2B1q", 4, "", DEF_MODEC,DEF_MODEL },
+ { "movsb", 16,24, 0x000e, "1S", 0, "[]", DEF_MODEC,DEF_MODEL },
+ { "movsw", 16,24, 0x010e, "1S", 0, "[]", DEF_MODEC,DEF_MODEL },
+ { "movsd", 16,24, 0x030e, "1S", 0, "[]", DEF_MODEC,DEF_MODEL },
+ { "movst", 16,24, 0x800e, "1S", 0, "[]", DEF_MODEC,DEF_MODEL },
+ { "movsub", 14,24, 0x0cae, "1A2A", 1, "", DEF_MODEC,DEF_MODEL },
+ { "movsuw", 14,24, 0x0dae, "1A2A", 2, "", DEF_MODEC,DEF_MODEL },
+ { "movsud", 14,24, 0x0fae, "1A2A", 4, "", DEF_MODEC,DEF_MODEL },
+ { "movusb", 14,24, 0x1cae, "1A2A", 1, "", DEF_MODEC,DEF_MODEL },
+ { "movusw", 14,24, 0x1dae, "1A2A", 2, "", DEF_MODEC,DEF_MODEL },
+ { "movusd", 14,24, 0x1fae, "1A2A", 4, "", DEF_MODEC,DEF_MODEL },
+ { "movxbd", 14,24, 0x1cce, "1B2D", 1, "", DEF_MODEC,DEF_MODEL },
+ { "movxwd", 14,24, 0x1dce, "1W2D", 2, "", DEF_MODEC,DEF_MODEL },
+ { "movxbw", 14,24, 0x10ce, "1B2W", 1, "", DEF_MODEC,DEF_MODEL },
+ { "movzbd", 14,24, 0x18ce, "1B2D", 1, "", DEF_MODEC,DEF_MODEL },
+ { "movzwd", 14,24, 0x19ce, "1W2D", 2, "", DEF_MODEC,DEF_MODEL },
+ { "movzbw", 14,24, 0x14ce, "1B2W", 1, "", DEF_MODEC,DEF_MODEL },
+ { "mulf", 14,24, 0x31be, "1F2F", 4, "", DEF_MODEC,DEF_MODEL },
+ { "mull", 14,24, 0x30be, "1L2L", 8, "", DEF_MODEC,DEF_MODEL },
+ { "mulb", 14,24, 0x20ce, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "mulw", 14,24, 0x21ce, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "muld", 14,24, 0x23ce, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "negf", 14,24, 0x15be, "1F2F", 4, "", DEF_MODEC,DEF_MODEL },
+ { "negl", 14,24, 0x14be, "1L2L", 8, "", DEF_MODEC,DEF_MODEL },
+ { "negb", 14,24, 0x204e, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "negw", 14,24, 0x214e, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "negd", 14,24, 0x234e, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "nop", 8,8, 0xa2, "", 0, "", DEF_MODEC,DEF_MODEL },
+ { "notb", 14,24, 0x244e, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "notw", 14,24, 0x254e, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "notd", 14,24, 0x274e, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "orb", 6,16, 0x18, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "orw", 6,16, 0x19, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "ord", 6,16, 0x1b, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "quob", 14,24, 0x30ce, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "quow", 14,24, 0x31ce, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "quod", 14,24, 0x33ce, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "rdval", 19,24, 0x0031e,"1A", 4, "", DEF_MODEC,DEF_MODEL },
+ { "remb", 14,24, 0x34ce, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "remw", 14,24, 0x35ce, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "remd", 14,24, 0x37ce, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "restore", 8,8, 0x72, "1u", 0, "", DEF_MODEC,DEF_MODEL },
+ { "ret", 8,8, 0x12, "1d", 0, "", DEF_MODEC,DEF_MODEL },
+ { "reti", 8,8, 0x52, "", 0, "", DEF_MODEC,DEF_MODEL },
+ { "rett", 8,8, 0x42, "1d", 0, "", DEF_MODEC,DEF_MODEL },
+ { "rotb", 14,24, 0x004e, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "rotw", 14,24, 0x014e, "1B2W", 1, "", DEF_MODEC,DEF_MODEL },
+ { "rotd", 14,24, 0x034e, "1B2D", 1, "", DEF_MODEC,DEF_MODEL },
+ { "roundfb", 14,24, 0x243e, "1F2B", 4, "", DEF_MODEC,DEF_MODEL },
+ { "roundfw", 14,24, 0x253e, "1F2W", 4, "", DEF_MODEC,DEF_MODEL },
+ { "roundfd", 14,24, 0x273e, "1F2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "roundlb", 14,24, 0x203e, "1L2B", 8, "", DEF_MODEC,DEF_MODEL },
+ { "roundlw", 14,24, 0x213e, "1L2W", 8, "", DEF_MODEC,DEF_MODEL },
+ { "roundld", 14,24, 0x233e, "1L2D", 8, "", DEF_MODEC,DEF_MODEL },
+ { "rxp", 8,8, 0x32, "1d", 0, "", DEF_MODEC,DEF_MODEL },
+ { "seqb", 11,16, 0x3c, "1B", 0, "", DEF_MODEC,DEF_MODEL },
+ { "seqw", 11,16, 0x3d, "1W", 0, "", DEF_MODEC,DEF_MODEL },
+ { "seqd", 11,16, 0x3f, "1D", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sneb", 11,16, 0xbc, "1B", 0, "", DEF_MODEC,DEF_MODEL },
+ { "snew", 11,16, 0xbd, "1W", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sned", 11,16, 0xbf, "1D", 0, "", DEF_MODEC,DEF_MODEL },
+ { "scsb", 11,16, 0x13c, "1B", 0, "", DEF_MODEC,DEF_MODEL },
+ { "scsw", 11,16, 0x13d, "1W", 0, "", DEF_MODEC,DEF_MODEL },
+ { "scsd", 11,16, 0x13f, "1D", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sccb", 11,16, 0x1bc, "1B", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sccw", 11,16, 0x1bd, "1W", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sccd", 11,16, 0x1bf, "1D", 0, "", DEF_MODEC,DEF_MODEL },
+ { "shib", 11,16, 0x23c, "1B", 0, "", DEF_MODEC,DEF_MODEL },
+ { "shiw", 11,16, 0x23d, "1W", 0, "", DEF_MODEC,DEF_MODEL },
+ { "shid", 11,16, 0x23f, "1D", 0, "", DEF_MODEC,DEF_MODEL },
+ { "slsb", 11,16, 0x2bc, "1B", 0, "", DEF_MODEC,DEF_MODEL },
+ { "slsw", 11,16, 0x2bd, "1W", 0, "", DEF_MODEC,DEF_MODEL },
+ { "slsd", 11,16, 0x2bf, "1D", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sgtb", 11,16, 0x33c, "1B", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sgtw", 11,16, 0x33d, "1W", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sgtd", 11,16, 0x33f, "1D", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sleb", 11,16, 0x3bc, "1B", 0, "", DEF_MODEC,DEF_MODEL },
+ { "slew", 11,16, 0x3bd, "1W", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sled", 11,16, 0x3bf, "1D", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sfsb", 11,16, 0x43c, "1B", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sfsw", 11,16, 0x43d, "1W", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sfsd", 11,16, 0x43f, "1D", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sfcb", 11,16, 0x4bc, "1B", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sfcw", 11,16, 0x4bd, "1W", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sfcd", 11,16, 0x4bf, "1D", 0, "", DEF_MODEC,DEF_MODEL },
+ { "slob", 11,16, 0x53c, "1B", 0, "", DEF_MODEC,DEF_MODEL },
+ { "slow", 11,16, 0x53d, "1W", 0, "", DEF_MODEC,DEF_MODEL },
+ { "slod", 11,16, 0x53f, "1D", 0, "", DEF_MODEC,DEF_MODEL },
+ { "shsb", 11,16, 0x5bc, "1B", 0, "", DEF_MODEC,DEF_MODEL },
+ { "shsw", 11,16, 0x5bd, "1W", 0, "", DEF_MODEC,DEF_MODEL },
+ { "shsd", 11,16, 0x5bf, "1D", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sltb", 11,16, 0x63c, "1B", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sltw", 11,16, 0x63d, "1W", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sltd", 11,16, 0x63f, "1D", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sgeb", 11,16, 0x6bc, "1B", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sgew", 11,16, 0x6bd, "1W", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sged", 11,16, 0x6bf, "1D", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sutb", 11,16, 0x73c, "1B", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sutw", 11,16, 0x73d, "1W", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sutd", 11,16, 0x73f, "1D", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sufb", 11,16, 0x7bc, "1B", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sufw", 11,16, 0x7bd, "1W", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sufd", 11,16, 0x7bf, "1D", 0, "", DEF_MODEC,DEF_MODEL },
+ { "save", 8,8, 0x62, "1U", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sbitb", 14,24, 0x184e, "1B2A", 1, "", DEF_MODEC,DEF_MODEL },
+ { "sbitw", 14,24, 0x194e, "1W2A", 2, "", DEF_MODEC,DEF_MODEL },
+ { "sbitd", 14,24, 0x1b4e, "1D2A", 4, "", DEF_MODEC,DEF_MODEL },
+ { "sbitib", 14,24, 0x1c4e, "1B2A", 1, "", DEF_MODEC,DEF_MODEL },
+ { "sbitiw", 14,24, 0x1d4e, "1W2A", 2, "", DEF_MODEC,DEF_MODEL },
+ { "sbitid", 14,24, 0x1f4e, "1D2A", 4, "", DEF_MODEC,DEF_MODEL },
+ { "setcfg", 15,24, 0x0b0e, "1O", 0, "", DEF_MODEC,DEF_MODEL },
+ { "sfsr", 14,24, 0x373e, "1f", 0, "", DEF_MODEC,DEF_MODEL },
+ { "skpsb", 16,24, 0x0c0e, "1S", 0, "[]", DEF_MODEC,DEF_MODEL },
+ { "skpsw", 16,24, 0x0d0e, "1S", 0, "[]", DEF_MODEC,DEF_MODEL },
+ { "skpsd", 16,24, 0x0f0e, "1S", 0, "[]", DEF_MODEC,DEF_MODEL },
+ { "skpst", 16,24, 0x8c0e, "1S", 0, "[]", DEF_MODEC,DEF_MODEL },
+ { "smr", 15,24, 0x0f1e, "2D1M", 4, "", DEF_MODEC,DEF_MODEL },
+ { "sprb", 7,16, 0x2c, "2B1P", 1, "", DEF_MODEC,DEF_MODEL },
+ { "sprw", 7,16, 0x2d, "2W1P", 2, "", DEF_MODEC,DEF_MODEL },
+ { "sprd", 7,16, 0x2f, "2D1P", 4, "", DEF_MODEC,DEF_MODEL },
+ { "subf", 14,24, 0x11be, "1F2F", 4, "", DEF_MODEC,DEF_MODEL },
+ { "subl", 14,24, 0x10be, "1L2L", 8, "", DEF_MODEC,DEF_MODEL },
+ { "subb", 6,16, 0x20, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "subw", 6,16, 0x21, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "subd", 6,16, 0x23, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "subcb", 6,16, 0x30, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "subcw", 6,16, 0x31, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "subcd", 6,16, 0x33, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "subpb", 14,24, 0x2c4e, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "subpw", 14,24, 0x2d4e, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "subpd", 14,24, 0x2f4e, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+#ifdef NS32K_SVC_IMMED_OPERANDS
+ { "svc", 8,8, 0xe2, "2i1i", 1, "", DEF_MODEC,DEF_MODEL }, /* not really, but unix uses it */
+#else
+ { "svc", 8,8, 0xe2, "", 0, "", DEF_MODEC,DEF_MODEL },
+#endif
+ { "tbitb", 6,16, 0x34, "1B2A", 1, "", DEF_MODEC,DEF_MODEL },
+ { "tbitw", 6,16, 0x35, "1W2A", 2, "", DEF_MODEC,DEF_MODEL },
+ { "tbitd", 6,16, 0x37, "1D2A", 4, "", DEF_MODEC,DEF_MODEL },
+ { "truncfb", 14,24, 0x2c3e, "1F2B", 4, "", DEF_MODEC,DEF_MODEL },
+ { "truncfw", 14,24, 0x2d3e, "1F2W", 4, "", DEF_MODEC,DEF_MODEL },
+ { "truncfd", 14,24, 0x2f3e, "1F2D", 4, "", DEF_MODEC,DEF_MODEL },
+ { "trunclb", 14,24, 0x283e, "1L2B", 8, "", DEF_MODEC,DEF_MODEL },
+ { "trunclw", 14,24, 0x293e, "1L2W", 8, "", DEF_MODEC,DEF_MODEL },
+ { "truncld", 14,24, 0x2b3e, "1L2D", 8, "", DEF_MODEC,DEF_MODEL },
+ { "wait", 8,8, 0xb2, "", 0, "", DEF_MODEC,DEF_MODEL },
+ { "wrval", 19,24, 0x0071e,"1A", 0, "", DEF_MODEC,DEF_MODEL },
+ { "xorb", 6,16, 0x38, "1B2B", 1, "", DEF_MODEC,DEF_MODEL },
+ { "xorw", 6,16, 0x39, "1W2W", 2, "", DEF_MODEC,DEF_MODEL },
+ { "xord", 6,16, 0x3b, "1D2D", 4, "", DEF_MODEC,DEF_MODEL },
+#if defined(NS32381) /* I'm not too sure of these */
+ { "dotf", 14,24, 0x0dfe, "1F2F", 4, "", DEF_MODEC,DEF_MODEL },
+ { "dotl", 14,24, 0x0cfe, "1L2L", 8, "", DEF_MODEC,DEF_MODEL },
+ { "logbf", 14,24, 0x15fe, "1F2F", 4, "", DEF_MODEC,DEF_MODEL },
+ { "logbl", 14,24, 0x14fe, "1L2L", 8, "", DEF_MODEC,DEF_MODEL },
+ { "polyf", 14,24, 0x09fe, "1F2F", 4, "", DEF_MODEC,DEF_MODEL },
+ { "polyl", 14,24, 0x08fe, "1L2L", 8, "", DEF_MODEC,DEF_MODEL },
+ { "scalbf", 14,24, 0x11fe, "1F2F", 4, "", DEF_MODEC,DEF_MODEL },
+ { "scalbl", 14,24, 0x10fe, "1L2L", 8, "", DEF_MODEC,DEF_MODEL },
+#endif
+};
+
+static const int numopcodes=sizeof(ns32k_opcodes)/sizeof(ns32k_opcodes[0]);
+
+static const struct ns32k_opcode *endop = ns32k_opcodes+sizeof(ns32k_opcodes)/sizeof(ns32k_opcodes[0]);
+
+#define MAX_ARGS 4
+#define ARG_LEN 50
+
diff --git a/gnu/usr.bin/as/opcode/pn.h b/gnu/usr.bin/as/opcode/pn.h
new file mode 100644
index 0000000..fde4764
--- /dev/null
+++ b/gnu/usr.bin/as/opcode/pn.h
@@ -0,0 +1,282 @@
+/* Print GOULD PN (PowerNode) instructions for GDB, the GNU debugger.
+ Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+struct gld_opcode
+{
+ char *name;
+ unsigned long opcode;
+ unsigned long mask;
+ char *args;
+ int length;
+};
+
+/* We store four bytes of opcode for all opcodes because that
+ is the most any of them need. The actual length of an instruction
+ is always at least 2 bytes, and at most four. The length of the
+ instruction is based on the opcode.
+
+ The mask component is a mask saying which bits must match
+ particular opcode in order for an instruction to be an instance
+ of that opcode.
+
+ The args component is a string containing characters
+ that are used to format the arguments to the instruction. */
+
+/* Kinds of operands:
+ r Register in first field
+ R Register in second field
+ b Base register in first field
+ B Base register in second field
+ v Vector register in first field
+ V Vector register in first field
+ A Optional address register (base register)
+ X Optional index register
+ I Immediate data (16bits signed)
+ O Offset field (16bits signed)
+ h Offset field (15bits signed)
+ d Offset field (14bits signed)
+ S Shift count field
+
+ any other characters are printed as is...
+*/
+
+/* The assembler requires that this array be sorted as follows:
+ all instances of the same mnemonic must be consecutive.
+ All instances of the same mnemonic with the same number of operands
+ must be consecutive.
+ */
+struct gld_opcode gld_opcodes[] =
+{
+{ "abm", 0xa0080000, 0xfc080000, "f,xOA,X", 4 },
+{ "abr", 0x18080000, 0xfc0c0000, "r,f", 2 },
+{ "aci", 0xfc770000, 0xfc7f8000, "r,I", 4 },
+{ "adfd", 0xe0080002, 0xfc080002, "r,xOA,X", 4 },
+{ "adfw", 0xe0080000, 0xfc080000, "r,xOA,X", 4 },
+{ "adi", 0xc8010000, 0xfc7f0000, "r,I", 4 },
+{ "admb", 0xb8080000, 0xfc080000, "r,xOA,X", 4 },
+{ "admd", 0xb8000002, 0xfc080002, "r,xOA,X", 4 },
+{ "admh", 0xb8000001, 0xfc080001, "r,xOA,X", 4 },
+{ "admw", 0xb8000000, 0xfc080000, "r,xOA,X", 4 },
+{ "adr", 0x38000000, 0xfc0f0000, "r,R", 2 },
+{ "adrfd", 0x38090000, 0xfc0f0000, "r,R", 2 },
+{ "adrfw", 0x38010000, 0xfc0f0000, "r,R", 2 },
+{ "adrm", 0x38080000, 0xfc0f0000, "r,R", 2 },
+{ "ai", 0xfc030000, 0xfc07ffff, "I", 4 },
+{ "anmb", 0x84080000, 0xfc080000, "r,xOA,X", 4 },
+{ "anmd", 0x84000002, 0xfc080002, "r,xOA,X", 4 },
+{ "anmh", 0x84000001, 0xfc080001, "r,xOA,X", 4 },
+{ "anmw", 0x84000000, 0xfc080000, "r,xOA,X", 4 },
+{ "anr", 0x04000000, 0xfc0f0000, "r,R", 2 },
+{ "armb", 0xe8080000, 0xfc080000, "r,xOA,X", 4 },
+{ "armd", 0xe8000002, 0xfc080002, "r,xOA,X", 4 },
+{ "armh", 0xe8000001, 0xfc080001, "r,xOA,X", 4 },
+{ "armw", 0xe8000000, 0xfc080000, "r,xOA,X", 4 },
+{ "bcf", 0xf0000000, 0xfc080000, "I,xOA,X", 4 },
+{ "bct", 0xec000000, 0xfc080000, "I,xOA,X", 4 },
+{ "bei", 0x00060000, 0xffff0000, "", 2 },
+{ "bft", 0xf0000000, 0xff880000, "xOA,X", 4 },
+{ "bib", 0xf4000000, 0xfc780000, "r,xOA", 4 },
+{ "bid", 0xf4600000, 0xfc780000, "r,xOA", 4 },
+{ "bih", 0xf4200000, 0xfc780000, "r,xOA", 4 },
+{ "biw", 0xf4400000, 0xfc780000, "r,xOA", 4 },
+{ "bl", 0xf8800000, 0xff880000, "xOA,X", 4 },
+{ "bsub", 0x5c080000, 0xff8f0000, "", 2 },
+{ "bsubm", 0x28080000, 0xfc080000, "", 4 },
+{ "bu", 0xec000000, 0xff880000, "xOA,X", 4 },
+{ "call", 0x28080000, 0xfc0f0000, "", 2 },
+{ "callm", 0x5c080000, 0xff880000, "", 4 },
+{ "camb", 0x90080000, 0xfc080000, "r,xOA,X", 4 },
+{ "camd", 0x90000002, 0xfc080002, "r,xOA,X", 4 },
+{ "camh", 0x90000001, 0xfc080001, "r,xOA,X", 4 },
+{ "camw", 0x90000000, 0xfc080000, "r.xOA,X", 4 },
+{ "car", 0x10000000, 0xfc0f0000, "r,R", 2 },
+{ "cd", 0xfc060000, 0xfc070000, "r,f", 4 },
+{ "cea", 0x000f0000, 0xffff0000, "", 2 },
+{ "ci", 0xc8050000, 0xfc7f0000, "r,I", 4 },
+{ "cmc", 0x040a0000, 0xfc7f0000, "r", 2 },
+{ "cmmb", 0x94080000, 0xfc080000, "r,xOA,X", 4 },
+{ "cmmd", 0x94000002, 0xfc080002, "r,xOA,X", 4 },
+{ "cmmh", 0x94000001, 0xfc080001, "r,xOA,X", 4 },
+{ "cmmw", 0x94000000, 0xfc080000, "r,xOA,X", 4 },
+{ "cmr", 0x14000000, 0xfc0f0000, "r,R", 2 },
+{ "daci", 0xfc7f0000, 0xfc7f8000, "r,I", 4 },
+{ "dae", 0x000e0000, 0xffff0000, "", 2 },
+{ "dai", 0xfc040000, 0xfc07ffff, "I", 4 },
+{ "dci", 0xfc6f0000, 0xfc7f8000, "r,I", 4 },
+{ "di", 0xfc010000, 0xfc07ffff, "I", 4 },
+{ "dvfd", 0xe4000002, 0xfc080002, "r,xOA,X", 4 },
+{ "dvfw", 0xe4000000, 0xfc080000, "r,xOA,X", 4 },
+{ "dvi", 0xc8040000, 0xfc7f0000, "r,I", 4 },
+{ "dvmb", 0xc4080000, 0xfc080000, "r,xOA,X", 4 },
+{ "dvmh", 0xc4000001, 0xfc080001, "r,xOA,X", 4 },
+{ "dvmw", 0xc4000000, 0xfc080000, "r,xOA,X", 4 },
+{ "dvr", 0x380a0000, 0xfc0f0000, "r,R", 2 },
+{ "dvrfd", 0x380c0000, 0xfc0f0000, "r,R", 4 },
+{ "dvrfw", 0x38040000, 0xfc0f0000, "r,xOA,X", 4 },
+{ "eae", 0x00080000, 0xffff0000, "", 2 },
+{ "eci", 0xfc670000, 0xfc7f8080, "r,I", 4 },
+{ "ecwcs", 0xfc4f0000, 0xfc7f8000, "", 4 },
+{ "ei", 0xfc000000, 0xfc07ffff, "I", 4 },
+{ "eomb", 0x8c080000, 0xfc080000, "r,xOA,X", 4 },
+{ "eomd", 0x8c000002, 0xfc080002, "r,xOA,X", 4 },
+{ "eomh", 0x8c000001, 0xfc080001, "r,xOA,X", 4 },
+{ "eomw", 0x8c000000, 0xfc080000, "r,xOA,X", 4 },
+{ "eor", 0x0c000000, 0xfc0f0000, "r,R", 2 },
+{ "eorm", 0x0c080000, 0xfc0f0000, "r,R", 2 },
+{ "es", 0x00040000, 0xfc7f0000, "r", 2 },
+{ "exm", 0xa8000000, 0xff880000, "xOA,X", 4 },
+{ "exr", 0xc8070000, 0xfc7f0000, "r", 2 },
+{ "exrr", 0xc8070002, 0xfc7f0002, "r", 2 },
+{ "fixd", 0x380d0000, 0xfc0f0000, "r,R", 2 },
+{ "fixw", 0x38050000, 0xfc0f0000, "r,R", 2 },
+{ "fltd", 0x380f0000, 0xfc0f0000, "r,R", 2 },
+{ "fltw", 0x38070000, 0xfc0f0000, "r,R", 2 },
+{ "grio", 0xfc3f0000, 0xfc7f8000, "r,I", 4 },
+{ "halt", 0x00000000, 0xffff0000, "", 2 },
+{ "hio", 0xfc370000, 0xfc7f8000, "r,I", 4 },
+{ "jwcs", 0xfa080000, 0xff880000, "xOA,X", 4 },
+{ "la", 0x50000000, 0xfc000000, "r,xOA,X", 4 },
+{ "labr", 0x58080000, 0xfc080000, "b,xOA,X", 4 },
+{ "lb", 0xac080000, 0xfc080000, "r,xOA,X", 4 },
+{ "lcs", 0x00030000, 0xfc7f0000, "r", 2 },
+{ "ld", 0xac000002, 0xfc080002, "r,xOA,X", 4 },
+{ "lear", 0x80000000, 0xfc080000, "r,xOA,X", 4 },
+{ "lf", 0xcc000000, 0xfc080000, "r,xOA,X", 4 },
+{ "lfbr", 0xcc080000, 0xfc080000, "b,xOA,X", 4 },
+{ "lh", 0xac000001, 0xfc080001, "r,xOA,X", 4 },
+{ "li", 0xc8000000, 0xfc7f0000, "r,I", 4 },
+{ "lmap", 0x2c070000, 0xfc7f0000, "r", 2 },
+{ "lmb", 0xb0080000, 0xfc080000, "r,xOA,X", 4 },
+{ "lmd", 0xb0000002, 0xfc080002, "r,xOA,X", 4 },
+{ "lmh", 0xb0000001, 0xfc080001, "r,xOA,X", 4 },
+{ "lmw", 0xb0000000, 0xfc080000, "r,xOA,X", 4 },
+{ "lnb", 0xb4080000, 0xfc080000, "r,xOA,X", 4 },
+{ "lnd", 0xb4000002, 0xfc080002, "r,xOA,X", 4 },
+{ "lnh", 0xb4000001, 0xfc080001, "r,xOA,X", 4 },
+{ "lnw", 0xb4000000, 0xfc080000, "r,xOA,X", 4 },
+{ "lpsd", 0xf9800000, 0xff880000, "r,xOA,X", 4 },
+{ "lpsdcm", 0xfa800000, 0xff880000, "r,xOA,X", 4 },
+{ "lw", 0xac000000, 0xfc080000, "r,xOA,X", 4 },
+{ "lwbr", 0x5c000000, 0xfc080000, "b,xOA,X", 4 },
+{ "mpfd", 0xe4080002, 0xfc080002, "r,xOA,X", 4 },
+{ "mpfw", 0xe4080000, 0xfc080000, "r,xOA,X", 4 },
+{ "mpi", 0xc8030000, 0xfc7f0000, "r,I", 4 },
+{ "mpmb", 0xc0080000, 0xfc080000, "r,xOA,X", 4 },
+{ "mpmh", 0xc0000001, 0xfc080001, "r,xOA,X", 4 },
+{ "mpmw", 0xc0000000, 0xfc080000, "r,xOA,X", 4 },
+{ "mpr", 0x38020000, 0xfc0f0000, "r,R", 2 },
+{ "mprfd", 0x380e0000, 0xfc0f0000, "r,R", 2 },
+{ "mprfw", 0x38060000, 0xfc0f0000, "r,R", 2 },
+{ "nop", 0x00020000, 0xffff0000, "", 2 },
+{ "ormb", 0x88080000, 0xfc080000, "r,xOA,X", 4 },
+{ "ormd", 0x88000002, 0xfc080002, "r,xOA,X", 4 },
+{ "ormh", 0x88000001, 0xfc080001, "r,xOA,X", 4 },
+{ "ormw", 0x88000000, 0xfc080000, "r,xOA,X", 4 },
+{ "orr", 0x08000000, 0xfc0f0000, "r,R", 2 },
+{ "orrm", 0x08080000, 0xfc0f0000, "r,R", 2 },
+{ "rdsts", 0x00090000, 0xfc7f0000, "r", 2 },
+{ "return", 0x280e0000, 0xfc7f0000, "", 2 },
+{ "ri", 0xfc020000, 0xfc07ffff, "I", 4 },
+{ "rnd", 0x00050000, 0xfc7f0000, "r", 2 },
+{ "rpswt", 0x040b0000, 0xfc7f0000, "r", 2 },
+{ "rschnl", 0xfc2f0000, 0xfc7f8000, "r,I", 4 },
+{ "rsctl", 0xfc470000, 0xfc7f8000, "r,I", 4 },
+{ "rwcs", 0x000b0000, 0xfc0f0000, "r,R", 2 },
+{ "sacz", 0x10080000, 0xfc0f0000, "r,R", 2 },
+{ "sbm", 0x98080000, 0xfc080000, "f,xOA,X", 4 },
+{ "sbr", 0x18000000, 0xfc0c0000, "r,f", 4 },
+{ "sea", 0x000d0000, 0xffff0000, "", 2 },
+{ "setcpu", 0x2c090000, 0xfc7f0000, "r", 2 },
+{ "sio", 0xfc170000, 0xfc7f8000, "r,I", 4 },
+{ "sipu", 0x000a0000, 0xffff0000, "", 2 },
+{ "sla", 0x1c400000, 0xfc600000, "r,S", 2 },
+{ "slad", 0x20400000, 0xfc600000, "r,S", 2 },
+{ "slc", 0x24400000, 0xfc600000, "r,S", 2 },
+{ "sll", 0x1c600000, 0xfc600000, "r,S", 2 },
+{ "slld", 0x20600000, 0xfc600000, "r,S", 2 },
+{ "smc", 0x04070000, 0xfc070000, "", 2 },
+{ "sra", 0x1c000000, 0xfc600000, "r,S", 2 },
+{ "srad", 0x20000000, 0xfc600000, "r,S", 2 },
+{ "src", 0x24000000, 0xfc600000, "r,S", 2 },
+{ "srl", 0x1c200000, 0xfc600000, "r,S", 2 },
+{ "srld", 0x20200000, 0xfc600000, "r,S", 2 },
+{ "stb", 0xd4080000, 0xfc080000, "r,xOA,X", 4 },
+{ "std", 0xd4000002, 0xfc080002, "r,xOA,X", 4 },
+{ "stf", 0xdc000000, 0xfc080000, "r,xOA,X", 4 },
+{ "stfbr", 0x54000000, 0xfc080000, "b,xOA,X", 4 },
+{ "sth", 0xd4000001, 0xfc080001, "r,xOA,X", 4 },
+{ "stmb", 0xd8080000, 0xfc080000, "r,xOA,X", 4 },
+{ "stmd", 0xd8000002, 0xfc080002, "r,xOA,X", 4 },
+{ "stmh", 0xd8000001, 0xfc080001, "r,xOA,X", 4 },
+{ "stmw", 0xd8000000, 0xfc080000, "r,xOA,X", 4 },
+{ "stpio", 0xfc270000, 0xfc7f8000, "r,I", 4 },
+{ "stw", 0xd4000000, 0xfc080000, "r,xOA,X", 4 },
+{ "stwbr", 0x54000000, 0xfc080000, "b,xOA,X", 4 },
+{ "suabr", 0x58000000, 0xfc080000, "b,xOA,X", 4 },
+{ "sufd", 0xe0000002, 0xfc080002, "r,xOA,X", 4 },
+{ "sufw", 0xe0000000, 0xfc080000, "r,xOA,X", 4 },
+{ "sui", 0xc8020000, 0xfc7f0000, "r,I", 4 },
+{ "sumb", 0xbc080000, 0xfc080000, "r,xOA,X", 4 },
+{ "sumd", 0xbc000002, 0xfc080002, "r,xOA,X", 4 },
+{ "sumh", 0xbc000001, 0xfc080001, "r,xOA,X", 4 },
+{ "sumw", 0xbc000000, 0xfc080000, "r,xOA,X", 4 },
+{ "sur", 0x3c000000, 0xfc0f0000, "r,R", 2 },
+{ "surfd", 0x380b0000, 0xfc0f0000, "r,xOA,X", 4 },
+{ "surfw", 0x38030000, 0xfc0f0000, "r,R", 2 },
+{ "surm", 0x3c080000, 0xfc0f0000, "r,R", 2 },
+{ "svc", 0xc8060000, 0xffff0000, "", 4 },
+{ "tbm", 0xa4080000, 0xfc080000, "f,xOA,X", 4 },
+{ "tbr", 0x180c0000, 0xfc0c0000, "r,f", 2 },
+{ "tbrr", 0x2c020000, 0xfc0f0000, "r,B", 2 },
+{ "tccr", 0x28040000, 0xfc7f0000, "", 2 },
+{ "td", 0xfc050000, 0xfc070000, "r,f", 4 },
+{ "tio", 0xfc1f0000, 0xfc7f8000, "r,I", 4 },
+{ "tmapr", 0x2c0a0000, 0xfc0f0000, "r,R", 2 },
+{ "tpcbr", 0x280c0000, 0xfc7f0000, "r", 2 },
+{ "trbr", 0x2c010000, 0xfc0f0000, "b,R", 2 },
+{ "trc", 0x2c030000, 0xfc0f0000, "r,R", 2 },
+{ "trcc", 0x28050000, 0xfc7f0000, "", 2 },
+{ "trcm", 0x2c0b0000, 0xfc0f0000, "r,R", 2 },
+{ "trn", 0x2c040000, 0xfc0f0000, "r,R", 2 },
+{ "trnm", 0x2c0c0000, 0xfc0f0000, "r,R", 2 },
+{ "trr", 0x2c000000, 0xfc0f0000, "r,R", 2 },
+{ "trrm", 0x2c080000, 0xfc0f0000, "r,R", 2 },
+{ "trsc", 0x2c0e0000, 0xfc0f0000, "r,R", 2 },
+{ "trsw", 0x28000000, 0xfc7f0000, "r", 2 },
+{ "tscr", 0x2c0f0000, 0xfc0f0000, "r,R", 2 },
+{ "uei", 0x00070000, 0xffff0000, "", 2 },
+{ "wait", 0x00010000, 0xffff0000, "", 2 },
+{ "wcwcs", 0xfc5f0000, 0xfc7f8000, "", 4 },
+{ "wwcs", 0x000c0000, 0xfc0f0000, "r,R", 2 },
+{ "xcbr", 0x28020000, 0xfc0f0000, "b,B", 2 },
+{ "xcr", 0x2c050000, 0xfc0f0000, "r,R", 2 },
+{ "xcrm", 0x2c0d0000, 0xfc0f0000, "r,R", 2 },
+{ "zbm", 0x9c080000, 0xfc080000, "f,xOA,X", 4 },
+{ "zbr", 0x18040000, 0xfc0c0000, "r,f", 2 },
+{ "zmb", 0xf8080000, 0xfc080000, "r,xOA,X", 4 },
+{ "zmd", 0xf8000002, 0xfc080002, "r,xOA,X", 4 },
+{ "zmh", 0xf8000001, 0xfc080001, "r,xOA,X", 4 },
+{ "zmw", 0xf8000000, 0xfc080000, "r,xOA,X", 4 },
+{ "zr", 0x0c000000, 0xfc0f0000, "r", 2 },
+};
+
+int numopcodes = sizeof(gld_opcodes) / sizeof(gld_opcodes[0]);
+
+struct gld_opcode *endop = gld_opcodes + sizeof(gld_opcodes) /
+ sizeof(gld_opcodes[0]);
diff --git a/gnu/usr.bin/as/opcode/pyr.h b/gnu/usr.bin/as/opcode/pyr.h
new file mode 100644
index 0000000..06632b8
--- /dev/null
+++ b/gnu/usr.bin/as/opcode/pyr.h
@@ -0,0 +1,287 @@
+/* pyramid.opcode.h -- gdb initial attempt. */
+
+/* pyramid opcode table: wot to do with this
+ particular opcode */
+
+struct pyr_datum
+{
+ char nargs;
+ char * args; /* how to compile said opcode */
+ unsigned long mask; /* Bit vector: which operand modes are valid
+ for this opcode */
+ unsigned char code; /* op-code (always 6(?) bits */
+};
+
+typedef struct pyr_insn_format {
+ unsigned int mode :4;
+ unsigned int operator :8;
+ unsigned int index_scale :2;
+ unsigned int index_reg :6;
+ unsigned int operand_1 :6;
+ unsigned int operand_2:6;
+} pyr_insn_format;
+
+
+/* We store four bytes of opcode for all opcodes.
+ Pyramid is sufficiently RISCy that:
+ - insns are always an integral number of words;
+ - the length of any insn can be told from the first word of
+ the insn. (ie, if there are zero, one, or two words of
+ immediate operand/offset).
+
+
+ The args component is a string containing two characters for each
+ operand of the instruction. The first specifies the kind of operand;
+ the second, the place it is stored. */
+
+/* Kinds of operands:
+ mask assembler syntax description
+ 0x0001: movw Rn,Rn register to register
+ 0x0002: movw K,Rn quick immediate to register
+ 0x0004: movw I,Rn long immediate to register
+ 0x0008: movw (Rn),Rn register indirect to register
+ movw (Rn)[x],Rn register indirect to register
+ 0x0010: movw I(Rn),Rn offset register indirect to register
+ movw I(Rn)[x],Rn offset register indirect, indexed, to register
+
+ 0x0020: movw Rn,(Rn) register to register indirect
+ 0x0040: movw K,(Rn) quick immediate to register indirect
+ 0x0080: movw I,(Rn) long immediate to register indirect
+ 0x0100: movw (Rn),(Rn) register indirect to-register indirect
+ 0x0100: movw (Rn),(Rn) register indirect to-register indirect
+ 0x0200: movw I(Rn),(Rn) register indirect+offset to register indirect
+ 0x0200: movw I(Rn),(Rn) register indirect+offset to register indirect
+
+ 0x0400: movw Rn,I(Rn) register to register indirect+offset
+ 0x0800: movw K,I(Rn) quick immediate to register indirect+offset
+ 0x1000: movw I,I(Rn) long immediate to register indirect+offset
+ 0x1000: movw (Rn),I(Rn) register indirect to-register indirect+offset
+ 0x1000: movw I(Rn),I(Rn) register indirect+offset to register indirect
+ +offset
+ 0x0000: (irregular) ???
+
+
+ Each insn has a four-bit field encoding the type(s) of its operands.
+*/
+
+/* Some common combinations
+ */
+
+/* the first 5,(0x1|0x2|0x4|0x8|0x10) ie (1|2|4|8|16), ie ( 32 -1)*/
+#define GEN_TO_REG (31)
+
+#define UNKNOWN ((unsigned long)-1)
+#define ANY (GEN_TO_REG | (GEN_TO_REG << 5) | (GEN_TO_REG << 15))
+
+#define CONVERT (1|8|0x10|0x20|0x200)
+
+#define K_TO_REG (2)
+#define I_TO_REG (4)
+#define NOTK_TO_REG (GEN_TO_REG & ~K_TO_REG)
+#define NOTI_TO_REG (GEN_TO_REG & ~I_TO_REG)
+
+/* The assembler requires that this array be sorted as follows:
+ all instances of the same mnemonic must be consecutive.
+ All instances of the same mnemonic with the same number of operands
+ must be consecutive.
+ */
+
+struct pyr_opcode /* pyr opcode text */
+{
+ char * name; /* opcode name: lowercase string [key] */
+ struct pyr_datum datum; /* rest of opcode table [datum] */
+};
+
+#define pyr_how args
+#define pyr_nargs nargs
+#define pyr_mask mask
+#define pyr_name name
+
+struct pyr_opcode pyr_opcodes[] =
+{
+ {"movb", { 2, "", UNKNOWN, 0x11}, },
+ {"movh", { 2, "", UNKNOWN, 0x12} },
+ {"movw", { 2, "", ANY, 0x10} },
+ {"movl", { 2, "", ANY, 0x13} },
+ {"mnegw", { 2, "", (0x1|0x8|0x10), 0x14} },
+ {"mnegf", { 2, "", 0x1, 0x15} },
+ {"mnegd", { 2, "", 0x1, 0x16} },
+ {"mcomw", { 2, "", (0x1|0x8|0x10), 0x17} },
+ {"mabsw", { 2, "", (0x1|0x8|0x10), 0x18} },
+ {"mabsf", { 2, "", 0x1, 0x19} },
+ {"mabsd", { 2, "", 0x1, 0x1a} },
+ {"mtstw", { 2, "", (0x1|0x8|0x10), 0x1c} },
+ {"mtstf", { 2, "", 0x1, 0x1d} },
+ {"mtstd", { 2, "", 0x1, 0x1e} },
+ {"mova", { 2, "", 0x8|0x10, 0x1f} },
+ {"movzbw", { 2, "", (0x1|0x8|0x10), 0x20} },
+ {"movzhw", { 2, "", (0x1|0x8|0x10), 0x21} },
+ /* 2 insns out of order here */
+ {"movbl", { 2, "", 1, 0x4f} },
+ {"filbl", { 2, "", 1, 0x4e} },
+
+ {"cvtbw", { 2, "", CONVERT, 0x22} },
+ {"cvthw", { 2, "", CONVERT, 0x23} },
+ {"cvtwb", { 2, "", CONVERT, 0x24} },
+ {"cvtwh", { 2, "", CONVERT, 0x25} },
+ {"cvtwf", { 2, "", CONVERT, 0x26} },
+ {"cvtwd", { 2, "", CONVERT, 0x27} },
+ {"cvtfw", { 2, "", CONVERT, 0x28} },
+ {"cvtfd", { 2, "", CONVERT, 0x29} },
+ {"cvtdw", { 2, "", CONVERT, 0x2a} },
+ {"cvtdf", { 2, "", CONVERT, 0x2b} },
+
+ {"addw", { 2, "", GEN_TO_REG, 0x40} },
+ {"addwc", { 2, "", GEN_TO_REG, 0x41} },
+ {"subw", { 2, "", GEN_TO_REG, 0x42} },
+ {"subwb", { 2, "", GEN_TO_REG, 0x43} },
+ {"rsubw", { 2, "", GEN_TO_REG, 0x44} },
+ {"mulw", { 2, "", GEN_TO_REG, 0x45} },
+ {"emul", { 2, "", GEN_TO_REG, 0x47} },
+ {"umulw", { 2, "", GEN_TO_REG, 0x46} },
+ {"divw", { 2, "", GEN_TO_REG, 0x48} },
+ {"ediv", { 2, "", GEN_TO_REG, 0x4a} },
+ {"rdivw", { 2, "", GEN_TO_REG, 0x4b} },
+ {"udivw", { 2, "", GEN_TO_REG, 0x49} },
+ {"modw", { 2, "", GEN_TO_REG, 0x4c} },
+ {"umodw", { 2, "", GEN_TO_REG, 0x4d} },
+
+
+ {"addf", { 2, "", 1, 0x50} },
+ {"addd", { 2, "", 1, 0x51} },
+ {"subf", { 2, "", 1, 0x52} },
+ {"subd", { 2, "", 1, 0x53} },
+ {"mulf", { 2, "", 1, 0x56} },
+ {"muld", { 2, "", 1, 0x57} },
+ {"divf", { 2, "", 1, 0x58} },
+ {"divd", { 2, "", 1, 0x59} },
+
+
+ {"cmpb", { 2, "", UNKNOWN, 0x61} },
+ {"cmph", { 2, "", UNKNOWN, 0x62} },
+ {"cmpw", { 2, "", UNKNOWN, 0x60} },
+ {"ucmpb", { 2, "", UNKNOWN, 0x66} },
+ /* WHY no "ucmph"??? */
+ {"ucmpw", { 2, "", UNKNOWN, 0x65} },
+ {"xchw", { 2, "", UNKNOWN, 0x0f} },
+
+
+ {"andw", { 2, "", GEN_TO_REG, 0x30} },
+ {"orw", { 2, "", GEN_TO_REG, 0x31} },
+ {"xorw", { 2, "", GEN_TO_REG, 0x32} },
+ {"bicw", { 2, "", GEN_TO_REG, 0x33} },
+ {"lshlw", { 2, "", GEN_TO_REG, 0x38} },
+ {"ashlw", { 2, "", GEN_TO_REG, 0x3a} },
+ {"ashll", { 2, "", GEN_TO_REG, 0x3c} },
+ {"ashrw", { 2, "", GEN_TO_REG, 0x3b} },
+ {"ashrl", { 2, "", GEN_TO_REG, 0x3d} },
+ {"rotlw", { 2, "", GEN_TO_REG, 0x3e} },
+ {"rotrw", { 2, "", GEN_TO_REG, 0x3f} },
+
+ /* push and pop insns are "going away next release". */
+ {"pushw", { 2, "", GEN_TO_REG, 0x0c} },
+ {"popw", { 2, "", (0x1|0x8|0x10), 0x0d} },
+ {"pusha", { 2, "", (0x8|0x10), 0x0e} },
+
+ {"bitsw", { 2, "", UNKNOWN, 0x35} },
+ {"bitcw", { 2, "", UNKNOWN, 0x36} },
+ /* some kind of ibra/dbra insns??*/
+ {"icmpw", { 2, "", UNKNOWN, 0x67} },
+ {"dcmpw", { 2, "", (1|4|0x20|0x80|0x400|0x1000), 0x69} },/*FIXME*/
+ {"acmpw", { 2, "", 1, 0x6b} },
+
+ /* Call is written as a 1-op insn, but is always (dis)assembled as a 2-op
+ insn with a 2nd op of tr14. The assembler will have to grok this. */
+ {"call", { 2, "", GEN_TO_REG, 0x04} },
+ {"call", { 1, "", GEN_TO_REG, 0x04} },
+
+ {"callk", { 1, "", UNKNOWN, 0x06} },/* system call?*/
+ /* Ret is usually written as a 0-op insn, but gets disassembled as a
+ 1-op insn. The operand is always tr15. */
+ {"ret", { 0, "", UNKNOWN, 0x09} },
+ {"ret", { 1, "", UNKNOWN, 0x09} },
+ {"adsf", { 2, "", (1|2|4), 0x08} },
+ {"retd", { 2, "", UNKNOWN, 0x0a} },
+ {"btc", { 2, "", UNKNOWN, 0x01} },
+ {"bfc", { 2, "", UNKNOWN, 0x02} },
+ /* Careful: halt is 0x00000000. Jump must have some other (mode?)bit set?? */
+ {"jump", { 1, "", UNKNOWN, 0x00} },
+ {"btp", { 2, "", UNKNOWN, 0xf00} },
+ /* read control-stack pointer is another 1-or-2 operand insn. */
+ {"rcsp", { 2, "", UNKNOWN, 0x01f} },
+ {"rcsp", { 1, "", UNKNOWN, 0x01f} }
+};
+
+/* end: pyramid.opcode.h */
+/* One day I will have to take the time to find out what operands
+ are valid for these insns, and guess at what they mean.
+
+ I can't imagine what the "I???" insns (iglob, etc) do.
+
+ the arithmetic-sounding insns ending in "p" sound awfully like BCD
+ arithmetic insns:
+ dshlp -> Decimal SHift Left Packed
+ dshrp -> Decimal SHift Right Packed
+ and cvtlp would be convert long to packed.
+ I have no idea how the operands are interpreted; but having them be
+ a long register with (address, length) of an in-memory packed BCD operand
+ would not be surprising.
+ They are unlikely to be a packed bcd string: 64 bits of long give
+ is only 15 digits+sign, which isn't enough for COBOL.
+ */
+#if 0
+ {"wcsp", { 2, "", UNKNOWN, 0x00} }, /*write csp?*/
+ /* The OSx Operating System Porting Guide claims SSL does things
+ with tr12 (a register reserved to it) to do with static block-structure
+ references. SSL=Set Static Link? It's "Going away next release". */
+ {"ssl", { 2, "", UNKNOWN, 0x00} },
+ {"ccmps", { 2, "", UNKNOWN, 0x00} },
+ {"lcd", { 2, "", UNKNOWN, 0x00} },
+ {"uemul", { 2, "", UNKNOWN, 0x00} }, /*unsigned emul*/
+ {"srf", { 2, "", UNKNOWN, 0x00} }, /*Gidget time???*/
+ {"mnegp", { 2, "", UNKNOWN, 0x00} }, /move-neg phys?*/
+ {"ldp", { 2, "", UNKNOWN, 0x00} }, /*load phys?*/
+ {"ldti", { 2, "", UNKNOWN, 0x00} },
+ {"ldb", { 2, "", UNKNOWN, 0x00} },
+ {"stp", { 2, "", UNKNOWN, 0x00} },
+ {"stti", { 2, "", UNKNOWN, 0x00} },
+ {"stb", { 2, "", UNKNOWN, 0x00} },
+ {"stu", { 2, "", UNKNOWN, 0x00} },
+ {"addp", { 2, "", UNKNOWN, 0x00} },
+ {"subp", { 2, "", UNKNOWN, 0x00} },
+ {"mulp", { 2, "", UNKNOWN, 0x00} },
+ {"divp", { 2, "", UNKNOWN, 0x00} },
+ {"dshlp", { 2, "", UNKNOWN, 0x00} }, /* dec shl packed? */
+ {"dshrp", { 2, "", UNKNOWN, 0x00} }, /* dec shr packed? */
+ {"movs", { 2, "", UNKNOWN, 0x00} }, /*move (string?)?*/
+ {"cmpp", { 2, "", UNKNOWN, 0x00} }, /* cmp phys?*/
+ {"cmps", { 2, "", UNKNOWN, 0x00} }, /* cmp (string?)?*/
+ {"cvtlp", { 2, "", UNKNOWN, 0x00} }, /* cvt long to p??*/
+ {"cvtpl", { 2, "", UNKNOWN, 0x00} }, /* cvt p to l??*/
+ {"dintr", { 2, "", UNKNOWN, 0x00} }, /* ?? intr ?*/
+ {"rphysw", { 2, "", UNKNOWN, 0x00} }, /* read phys word?*/
+ {"wphysw", { 2, "", UNKNOWN, 0x00} }, /* write phys word?*/
+ {"cmovs", { 2, "", UNKNOWN, 0x00} },
+ {"rsubw", { 2, "", UNKNOWN, 0x00} },
+ {"bicpsw", { 2, "", UNKNOWN, 0x00} }, /* clr bit in psw? */
+ {"bispsw", { 2, "", UNKNOWN, 0x00} }, /* set bit in psw? */
+ {"eio", { 2, "", UNKNOWN, 0x00} }, /* ?? ?io ? */
+ {"callp", { 2, "", UNKNOWN, 0x00} }, /* call phys?*/
+ {"callr", { 2, "", UNKNOWN, 0x00} },
+ {"lpcxt", { 2, "", UNKNOWN, 0x00} }, /*load proc context*/
+ {"rei", { 2, "", UNKNOWN, 0x00} }, /*ret from intrpt*/
+ {"rport", { 2, "", UNKNOWN, 0x00} }, /*read-port?*/
+ {"rtod", { 2, "", UNKNOWN, 0x00} }, /*read-time-of-day?*/
+ {"ssi", { 2, "", UNKNOWN, 0x00} },
+ {"vtpa", { 2, "", UNKNOWN, 0x00} }, /*virt-to-phys-addr?*/
+ {"wicl", { 2, "", UNKNOWN, 0x00} }, /* write icl ? */
+ {"wport", { 2, "", UNKNOWN, 0x00} }, /*write-port?*/
+ {"wtod", { 2, "", UNKNOWN, 0x00} }, /*write-time-of-day?*/
+ {"flic", { 2, "", UNKNOWN, 0x00} },
+ {"iglob", { 2, "", UNKNOWN, 0x00} }, /* I global? */
+ {"iphys", { 2, "", UNKNOWN, 0x00} }, /* I physical? */
+ {"ipid", { 2, "", UNKNOWN, 0x00} }, /* I pid? */
+ {"ivect", { 2, "", UNKNOWN, 0x00} }, /* I vector? */
+ {"lamst", { 2, "", UNKNOWN, 0x00} },
+ {"tio", { 2, "", UNKNOWN, 0x00} },
+#endif
diff --git a/gnu/usr.bin/as/opcode/sparc.h b/gnu/usr.bin/as/opcode/sparc.h
new file mode 100644
index 0000000..3c52464
--- /dev/null
+++ b/gnu/usr.bin/as/opcode/sparc.h
@@ -0,0 +1,871 @@
+
+/* Table of opcodes for the sparc.
+ Copyright 1989, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler, GDB, the GNU debugger, and
+the GNU Binutils.
+
+GAS/GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GAS/GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS or GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * $Id: sparc.h,v 1.1 1993/10/02 21:00:55 pk Exp $
+ */
+
+ /* FIXME-someday: perhaps the ,a's and such should be embedded in the
+ instruction's name rather than the args. This would make gas faster, pinsn
+ slower, but would mess up some macros a bit. xoxorich. */
+
+#if !defined(__STDC__) && !defined(const)
+#define const
+#endif
+
+/*
+ * Structure of an opcode table entry.
+ */
+enum sparc_architecture {
+ v6 = 0,
+ v7,
+ v8,
+};
+
+static const char *architecture_pname[] = {
+ "v6",
+ "v7",
+ "v8",
+ NULL,
+};
+
+struct sparc_opcode {
+ const char *name;
+ unsigned long match; /* Bits that must be set. */
+ unsigned long lose; /* Bits that must not be set. */
+ const char *args;
+ /* This was called "delayed" in versions before the flags. */
+ char flags;
+ enum sparc_architecture architecture;
+};
+
+#define F_DELAYED 1 /* Delayed branch */
+#define F_ALIAS 2 /* Alias for a "real" instruction */
+
+/*
+
+All sparc opcodes are 32 bits, except for the `set' instruction (really a
+macro), which is 64 bits. It is handled as a special case.
+
+The match component is a mask saying which bits must match a particular
+opcode in order for an instruction to be an instance of that opcode.
+
+The args component is a string containing one character for each operand of the
+instruction.
+
+Kinds of operands:
+ # Number used by optimizer. It is ignored.
+ 1 rs1 register.
+ 2 rs2 register.
+ d rd register.
+ e frs1 floating point register.
+ v frs1 floating point register (double/even).
+ V frs1 floating point register (quad/multiple of 4).
+ f frs2 floating point register.
+ B frs2 floating point register (double/even).
+ R frs2 floating point register (quad/multiple of 4).
+ g frsd floating point register.
+ H frsd floating point register (double/even).
+ J frsd floating point register (quad/multiple of 4).
+ b crs1 coprocessor register
+ c crs2 coprocessor register
+ D crsd coprocessor register
+ m alternate space register (asr) in rd
+ M alternate space register (asr) in rs1
+ h 22 high bits.
+ i 13 bit Immediate.
+ n 22 bit immediate.
+ l 22 bit PC relative immediate.
+ L 30 bit PC relative immediate.
+ a Annul. The annul bit is set.
+ A Alternate address space. Stored as 8 bits.
+ C Coprocessor state register.
+ F floating point state register.
+ p Processor state register.
+ q Floating point queue.
+ r Single register that is both rs1 and rsd.
+ Q Coprocessor queue.
+ S Special case.
+ x Single register that is both rs2 and rsd.
+ t Trap base register.
+ w Window invalid mask register.
+ y Y register.
+
+The following chars are unused: (note: ,[] are used as punctuation)
+[oOX3450]
+
+*/
+
+/* The order of the opcodes in this table is significant:
+
+ * The assembler requires that all instances of the same mnemonic must
+ be consecutive. If they aren't, the assembler will bomb at runtime.
+
+ * The disassembler should not care about the order of the opcodes.
+
+*/
+
+#define OP2(x) (((x)&0x7) << 22) /* op2 field of format2 insns */
+#define OP3(x) (((x)&0x3f) << 19) /* op3 field of format3 insns */
+#define OP(x) (((x)&0x3) << 30) /* op field of all insns */
+#define OPF(x) (((x)&0x1ff) << 5) /* opf field of float insns */
+#define F3F(x, y, z) (OP(x) | OP3(y) | OPF(z)) /* format3 float insns */
+#define F3I(x) (((x)&0x1) << 13) /* immediate field of format 3 insns */
+#define F2(x, y) (OP(x) | OP2(y)) /* format 2 insns */
+#define F3(x, y, z) (OP(x) | OP3(y) | F3I(z)) /* format3 insns */
+#define F1(x) (OP(x))
+#define DISP30(x) ((x)&0x3fffffff)
+#define ASI(x) (((x)&0xff) << 5) /* asi field of format3 insns */
+#define RS2(x) ((x)&0x1f) /* rs2 field */
+#define SIMM13(x) ((x)&0x1fff) /* simm13 field */
+#define RD(x) (((x)&0x1f) << 25) /* destination register field */
+#define RS1(x) (((x)&0x1f) << 14) /* rs1 field */
+#define ASI_RS2(x) (SIMM13(x))
+
+#define ANNUL (1<<29)
+#define IMMED F3I(1)
+#define RD_G0 RD(~0)
+#define RS1_G0 RS1(~0)
+#define RS2_G0 RS2(~0)
+
+#define COND(x) (((x)&0xf)<<25)
+
+#define CONDA (COND(0x8))
+#define CONDCC (COND(0xd))
+#define CONDCS (COND(0x5))
+#define CONDE (COND(0x1))
+#define CONDG (COND(0xa))
+#define CONDGE (COND(0xb))
+#define CONDGU (COND(0xc))
+#define CONDL (COND(0x3))
+#define CONDLE (COND(0x2))
+#define CONDLEU (COND(0x4))
+#define CONDN (COND(0x0))
+#define CONDNE (COND(0x9))
+#define CONDNEG (COND(0x6))
+#define CONDPOS (COND(0xe))
+#define CONDVC (COND(0xf))
+#define CONDVS (COND(0x7))
+
+#define CONDNZ CONDNE
+#define CONDZ CONDE
+#define CONDGEU CONDCC
+#define CONDLU CONDCS
+
+#define FCONDA (COND(0x8))
+#define FCONDE (COND(0x9))
+#define FCONDG (COND(0x6))
+#define FCONDGE (COND(0xb))
+#define FCONDL (COND(0x4))
+#define FCONDLE (COND(0xd))
+#define FCONDLG (COND(0x2))
+#define FCONDN (COND(0x0))
+#define FCONDNE (COND(0x1))
+#define FCONDO (COND(0xf))
+#define FCONDU (COND(0x7))
+#define FCONDUE (COND(0xa))
+#define FCONDUG (COND(0x5))
+#define FCONDUGE (COND(0xc))
+#define FCONDUL (COND(0x3))
+#define FCONDULE (COND(0xe))
+
+#define FCONDNZ FCONDNE
+#define FCONDZ FCONDE
+
+
+static const struct sparc_opcode sparc_opcodes[] = {
+
+{ "ld", F3(3, 0x00, 0), F3(~3, ~0x00, ~0), "[1+2],d", 0, v6 },
+{ "ld", F3(3, 0x00, 0), F3(~3, ~0x00, ~0)|RS2_G0, "[1],d", 0, v6 }, /* ld [rs1+%g0],d */
+{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1), "[1+i],d", 0, v6 },
+{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1), "[i+1],d", 0, v6 },
+{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ld [rs1+0],d */
+{ "ld", F3(3, 0x20, 0), F3(~3, ~0x20, ~0), "[1+2],g", 0, v6 },
+{ "ld", F3(3, 0x20, 0), F3(~3, ~0x20, ~0)|RS2_G0, "[1],g", 0, v6 }, /* ld [rs1+%g0],d */
+{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1), "[1+i],g", 0, v6 },
+{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1), "[i+1],g", 0, v6 },
+{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1)|RS1_G0, "[i],g", 0, v6 },
+{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1)|SIMM13(~0), "[1],g", 0, v6 }, /* ld [rs1+0],d */
+{ "ld", F3(3, 0x21, 0), F3(~3, ~0x21, ~0), "[1+2],F", 0, v6 },
+{ "ld", F3(3, 0x21, 0), F3(~3, ~0x21, ~0)|RS2_G0, "[1],F", 0, v6 }, /* ld [rs1+%g0],d */
+{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1), "[1+i],F", 0, v6 },
+{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1), "[i+1],F", 0, v6 },
+{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1)|RS1_G0, "[i],F", 0, v6 },
+{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1)|SIMM13(~0), "[1],F", 0, v6 }, /* ld [rs1+0],d */
+{ "ld", F3(3, 0x30, 0), F3(~3, ~0x30, ~0), "[1+2],D", 0, v6 },
+{ "ld", F3(3, 0x30, 0), F3(~3, ~0x30, ~0)|RS2_G0, "[1],D", 0, v6 }, /* ld [rs1+%g0],d */
+{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1), "[1+i],D", 0, v6 },
+{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1), "[i+1],D", 0, v6 },
+{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1)|RS1_G0, "[i],D", 0, v6 },
+{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1)|SIMM13(~0), "[1],D", 0, v6 }, /* ld [rs1+0],d */
+{ "ld", F3(3, 0x31, 0), F3(~3, ~0x31, ~0), "[1+2],C", 0, v6 },
+{ "ld", F3(3, 0x31, 0), F3(~3, ~0x31, ~0)|RS2_G0, "[1],C", 0, v6 }, /* ld [rs1+%g0],d */
+{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1), "[1+i],C", 0, v6 },
+{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1), "[i+1],C", 0, v6 },
+{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1)|RS1_G0, "[i],C", 0, v6 },
+{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1)|SIMM13(~0), "[1],C", 0, v6 }, /* ld [rs1+0],d */
+
+
+
+{ "lda", F3(3, 0x10, 0), F3(~3, ~0x10, ~0), "[1+2]A,d", 0, v6 },
+{ "lda", F3(3, 0x10, 0), F3(~3, ~0x10, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* lda [rs1+%g0],d */
+
+{ "ldd", F3(3, 0x03, 0), F3(~3, ~0x03, ~0)|ASI(~0), "[1+2],d", 0, v6 },
+{ "ldd", F3(3, 0x03, 0), F3(~3, ~0x03, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldd [rs1+%g0],d */
+{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1), "[1+i],d", 0, v6 },
+{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1), "[i+1],d", 0, v6 },
+{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldd [rs1+0],d */
+{ "ldd", F3(3, 0x23, 0), F3(~3, ~0x23, ~0)|ASI(~0), "[1+2],g", 0, v6 },
+{ "ldd", F3(3, 0x23, 0), F3(~3, ~0x23, ~0)|ASI_RS2(~0), "[1],g", 0, v6 }, /* ldd [rs1+%g0],d */
+{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1), "[1+i],g", 0, v6 },
+{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1), "[i+1],g", 0, v6 },
+{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1)|RS1_G0, "[i],g", 0, v6 },
+{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1)|SIMM13(~0), "[1],g", 0, v6 }, /* ldd [rs1+0],d */
+{ "ldd", F3(3, 0x33, 0), F3(~3, ~0x33, ~0)|ASI(~0), "[1+2],D", 0, v6 },
+{ "ldd", F3(3, 0x33, 0), F3(~3, ~0x33, ~0)|ASI_RS2(~0), "[1],D", 0, v6 }, /* ldd [rs1+%g0],d */
+{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1), "[1+i],D", 0, v6 },
+{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1), "[i+1],D", 0, v6 },
+{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1)|RS1_G0, "[i],D", 0, v6 },
+{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1)|SIMM13(~0), "[1],D", 0, v6 }, /* ldd [rs1+0],d */
+{ "ldsb", F3(3, 0x09, 0), F3(~3, ~0x09, ~0)|ASI(~0), "[1+2],d", 0, v6 },
+{ "ldsb", F3(3, 0x09, 0), F3(~3, ~0x09, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldsb [rs1+%g0],d */
+{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1), "[1+i],d", 0, v6 },
+{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1), "[i+1],d", 0, v6 },
+{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldsb [rs1+0],d */
+{ "ldsh", F3(3, 0x0a, 0), F3(~3, ~0x0a, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldsh [rs1+%g0],d */
+{ "ldsh", F3(3, 0x0a, 0), F3(~3, ~0x0a, ~0)|ASI(~0), "[1+2],d", 0, v6 },
+{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1), "[1+i],d", 0, v6 },
+{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1), "[i+1],d", 0, v6 },
+{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldsh [rs1+0],d */
+{ "ldstub", F3(3, 0x0d, 0), F3(~3, ~0x0d, ~0)|ASI(~0), "[1+2],d", 0, v6 },
+{ "ldstub", F3(3, 0x0d, 0), F3(~3, ~0x0d, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldstub [rs1+%g0],d */
+{ "ldstub", F3(3, 0x0d, 1), F3(~3, ~0x0d, ~1), "[1+i],d", 0, v6 },
+{ "ldstub", F3(3, 0x0d, 1), F3(~3, ~0x0d, ~1), "[i+1],d", 0, v6 },
+{ "ldstub", F3(3, 0x0d, 1), F3(~3, ~0x0d, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "ldub", F3(3, 0x01, 0), F3(~3, ~0x01, ~0)|ASI(~0), "[1+2],d", 0, v6 },
+{ "ldub", F3(3, 0x01, 0), F3(~3, ~0x01, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldub [rs1+%g0],d */
+{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1), "[1+i],d", 0, v6 },
+{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1), "[i+1],d", 0, v6 },
+{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldub [rs1+0],d */
+{ "lduh", F3(3, 0x02, 0), F3(~3, ~0x02, ~0)|ASI(~0), "[1+2],d", 0, v6 },
+{ "lduh", F3(3, 0x02, 0), F3(~3, ~0x02, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* lduh [rs1+%g0],d */
+{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1), "[1+i],d", 0, v6 },
+{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1), "[i+1],d", 0, v6 },
+{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* lduh [rs1+0],d */
+
+
+{ "ldda", F3(3, 0x13, 0), F3(~3, ~0x13, ~0), "[1+2]A,d", 0, v6 },
+{ "ldda", F3(3, 0x13, 0), F3(~3, ~0x13, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldda [rs1+%g0],d */
+{ "ldsba", F3(3, 0x19, 0), F3(~3, ~0x19, ~0), "[1+2]A,d", 0, v6 },
+{ "ldsba", F3(3, 0x19, 0), F3(~3, ~0x19, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldsba [rs1+%g0],d */
+{ "ldsha", F3(3, 0x1a, 0), F3(~3, ~0x1a, ~0), "[1+2]A,d", 0, v6 },
+{ "ldsha", F3(3, 0x1a, 0), F3(~3, ~0x1a, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldsha [rs1+%g0],d */
+{ "ldstuba", F3(3, 0x1d, 0), F3(~3, ~0x1d, ~0), "[1+2]A,d", 0, v6 },
+{ "ldstuba", F3(3, 0x1d, 0), F3(~3, ~0x1d, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldstuba [rs1+%g0],d */
+{ "lduba", F3(3, 0x11, 0), F3(~3, ~0x11, ~0), "[1+2]A,d", 0, v6 },
+{ "lduba", F3(3, 0x11, 0), F3(~3, ~0x11, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* lduba [rs1+%g0],d */
+{ "lduha", F3(3, 0x12, 0), F3(~3, ~0x12, ~0), "[1+2]A,d", 0, v6 },
+{ "lduha", F3(3, 0x12, 0), F3(~3, ~0x12, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* lduha [rs1+%g0],d */
+
+{ "st", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI(~0), "d,[1+2]", 0, v6 },
+{ "st", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* st d,[rs1+%g0] */
+{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[1+i]", 0, v6 },
+{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[i+1]", 0, v6 },
+{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RS1_G0, "d,[i]", 0, v6 },
+{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* st d,[rs1+0] */
+{ "st", F3(3, 0x24, 0), F3(~3, ~0x24, ~0)|ASI(~0), "g,[1+2]", 0, v6 },
+{ "st", F3(3, 0x24, 0), F3(~3, ~0x24, ~0)|ASI_RS2(~0), "g,[1]", 0, v6 }, /* st d[rs1+%g0] */
+{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1), "g,[1+i]", 0, v6 },
+{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1), "g,[i+1]", 0, v6 },
+{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1)|RS1_G0, "g,[i]", 0, v6 },
+{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1)|SIMM13(~0), "g,[1]", 0, v6 }, /* st d,[rs1+0] */
+{ "st", F3(3, 0x34, 0), F3(~3, ~0x34, ~0)|ASI(~0), "D,[1+2]", 0, v6 },
+{ "st", F3(3, 0x34, 0), F3(~3, ~0x34, ~0)|ASI_RS2(~0), "D,[1]", 0, v6 }, /* st d,[rs1+%g0] */
+{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1), "D,[1+i]", 0, v6 },
+{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1), "D,[i+1]", 0, v6 },
+{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1)|RS1_G0, "D,[i]", 0, v6 },
+{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1)|SIMM13(~0), "D,[1]", 0, v6 }, /* st d,[rs1+0] */
+{ "st", F3(3, 0x35, 0), F3(~3, ~0x35, ~0)|ASI(~0), "C,[1+2]", 0, v6 },
+{ "st", F3(3, 0x35, 0), F3(~3, ~0x35, ~0)|ASI_RS2(~0), "C,[1]", 0, v6 }, /* st d,[rs1+%g0] */
+{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1), "C,[1+i]", 0, v6 },
+{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1), "C,[i+1]", 0, v6 },
+{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1)|RS1_G0, "C,[i]", 0, v6 },
+{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1)|SIMM13(~0), "C,[1]", 0, v6 }, /* st d,[rs1+0] */
+
+{ "st", F3(3, 0x25, 0), F3(~3, ~0x25, ~0)|RD_G0|ASI(~0), "F,[1+2]", 0, v6 },
+{ "st", F3(3, 0x25, 0), F3(~3, ~0x25, ~0)|RD_G0|ASI_RS2(~0), "F,[1]", 0, v6 }, /* st d,[rs1+%g0] */
+{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|RD_G0, "F,[1+i]", 0, v6 },
+{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|RD_G0, "F,[i+1]", 0, v6 },
+{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|RD_G0|RS1_G0, "F,[i]", 0, v6 },
+{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|SIMM13(~0), "F,[1]", 0, v6 }, /* st d,[rs1+0] */
+
+
+
+
+{ "sta", F3(3, 0x14, 0), F3(~3, ~0x14, ~0), "d,[1+2]A", 0, v6 },
+{ "sta", F3(3, 0x14, 0), F3(~3, ~0x14, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* sta d,[rs1+%g0] */
+
+
+
+
+{ "stb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI(~0), "d,[1+2]", 0, v6 },
+{ "stb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* stb d,[rs1+%g0] */
+{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[1+i]", 0, v6 },
+{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[i+1]", 0, v6 },
+{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RS1_G0, "d,[i]", 0, v6 },
+{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* stb d,[rs1+0] */
+
+
+
+{ "stba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0), "d,[1+2]A", 0, v6 },
+{ "stba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* stba d,[rs1+%g0] */
+
+
+
+{ "std", F3(3, 0x07, 0), F3(~3, ~0x07, ~0)|ASI(~0), "d,[1+2]", 0, v6 },
+{ "std", F3(3, 0x07, 0), F3(~3, ~0x07, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* std d,[rs1+%g0] */
+{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1), "d,[1+i]", 0, v6 },
+{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1), "d,[i+1]", 0, v6 },
+{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1)|RS1_G0, "d,[i]", 0, v6 },
+{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* std d,[rs1+0] */
+{ "std", F3(3, 0x26, 0), F3(~3, ~0x26, ~0)|ASI(~0), "q,[1+2]", 0, v6 },
+{ "std", F3(3, 0x26, 0), F3(~3, ~0x26, ~0)|ASI_RS2(~0), "q,[1]", 0, v6 }, /* std d,[rs1+%g0] */
+{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1), "q,[1+i]", 0, v6 },
+{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1), "q,[i+1]", 0, v6 },
+{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1)|RS1_G0, "q,[i]", 0, v6 },
+{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1)|SIMM13(~0), "q,[1]", 0, v6 }, /* std d,[rs1+0] */
+{ "std", F3(3, 0x27, 0), F3(~3, ~0x27, ~0)|ASI(~0), "g,[1+2]", 0, v6 },
+{ "std", F3(3, 0x27, 0), F3(~3, ~0x27, ~0)|ASI_RS2(~0), "g,[1]", 0, v6 }, /* std d,[rs1+%g0] */
+{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1), "g,[1+i]", 0, v6 },
+{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1), "g,[i+1]", 0, v6 },
+{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1)|RS1_G0, "g,[i]", 0, v6 },
+{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1)|SIMM13(~0), "g,[1]", 0, v6 }, /* std d,[rs1+0] */
+{ "std", F3(3, 0x36, 0), F3(~3, ~0x36, ~0)|ASI(~0), "Q,[1+2]", 0, v6 },
+{ "std", F3(3, 0x36, 0), F3(~3, ~0x36, ~0)|ASI_RS2(~0), "Q,[1]", 0, v6 }, /* std d,[rs1+%g0] */
+{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1), "Q,[1+i]", 0, v6 },
+{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1), "Q,[i+1]", 0, v6 },
+{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1)|RS1_G0, "Q,[i]", 0, v6 },
+{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1)|SIMM13(~0), "Q,[1]", 0, v6 }, /* std d,[rs1+0] */
+{ "std", F3(3, 0x37, 0), F3(~3, ~0x37, ~0)|ASI(~0), "D,[1+2]", 0, v6 },
+{ "std", F3(3, 0x37, 0), F3(~3, ~0x37, ~0)|ASI_RS2(~0), "D,[1]", 0, v6 }, /* std d,[rs1+%g0] */
+{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1), "D,[1+i]", 0, v6 },
+{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1), "D,[i+1]", 0, v6 },
+{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1)|RS1_G0, "D,[i]", 0, v6 },
+{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1)|SIMM13(~0), "D,[1]", 0, v6 }, /* std d,[rs1+0] */
+
+{ "stda", F3(3, 0x17, 0), F3(~3, ~0x17, ~0), "d,[1+2]A", 0, v6 },
+{ "stda", F3(3, 0x17, 0), F3(~3, ~0x17, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* stda d,[rs1+%g0] */
+
+{ "sth", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI(~0), "d,[1+2]", 0, v6 },
+{ "sth", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* sth d,[rs1+%g0] */
+{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[1+i]", 0, v6 },
+{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[i+1]", 0, v6 },
+{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RS1_G0, "d,[i]", 0, v6 },
+{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* sth d,[+] */
+
+
+
+{ "stha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0), "d,[1+2]A", 0, v6 },
+{ "stha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* stha ,[+%] */
+
+
+
+
+
+{ "swap", F3(3, 0x0f, 0), F3(~3, ~0x0f, ~0)|ASI(~0), "[1+2],d", 0, v7 },
+{ "swap", F3(3, 0x0f, 0), F3(~3, ~0x0f, ~0)|ASI_RS2(~0), "[1],d", 0, v7 }, /* swap [rs1+%g0],d */
+{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1), "[1+i],d", 0, v7 },
+{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1), "[i+1],d", 0, v7 },
+{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1)|RS1_G0, "[i],d", 0, v7 },
+{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1)|SIMM13(~0), "[1],d", 0, v7 }, /* swap [rs1+0],d */
+
+{ "swapa", F3(3, 0x1f, 0), F3(~3, ~0x1f, ~0), "[1+2]A,d", 0, v7 },
+{ "swapa", F3(3, 0x1f, 0), F3(~3, ~0x1f, ~0)|RS2(~0), "[1]A,d", 0, v7 }, /* swapa [rs1+%g0],d */
+
+{ "restore", F3(2, 0x3d, 0), F3(~2, ~0x3d, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "restore", F3(2, 0x3d, 0), F3(~2, ~0x3d, ~0)|RD_G0|RS1_G0|ASI_RS2(~0), "", 0, v6 }, /* restore %g0,%g0,%g0 */
+{ "restore", F3(2, 0x3d, 1), F3(~2, ~0x3d, ~1), "1,i,d", 0, v6 },
+{ "restore", F3(2, 0x3d, 1), F3(~2, ~0x3d, ~1)|RD_G0|RS1_G0|SIMM13(~0), "", 0, v6 }, /* restore %g0,0,%g0 */
+
+{ "rett", F3(2, 0x39, 0), F3(~2, ~0x39, ~0)|RD_G0|ASI(~0), "1+2", F_DELAYED, v6 }, /* rett rs1+rs2 */
+{ "rett", F3(2, 0x39, 0), F3(~2, ~0x39, ~0)|RD_G0|ASI_RS2(~0), "1", F_DELAYED, v6 }, /* rett rs1,%g0 */
+{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0, "1+i", F_DELAYED, v6 }, /* rett rs1+X */
+{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0, "i+1", F_DELAYED, v6 }, /* rett X+rs1 */
+{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0|RS1_G0,"i", F_DELAYED, v6 }, /* rett X+rs1 */
+{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0|RS1_G0, "i", F_DELAYED, v6 }, /* rett X */
+{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0|SIMM13(~0), "1", F_DELAYED, v6 }, /* rett rs1+0 */
+
+{ "save", F3(2, 0x3c, 0), F3(~2, ~0x3c, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "save", F3(2, 0x3c, 1), F3(~2, ~0x3c, ~1), "1,i,d", 0, v6 },
+
+{ "ret", F3(2, 0x38, 1)|RS1(0x1f)|SIMM13(8), F3(~2, ~0x38, ~1)|SIMM13(~8), "", F_DELAYED, v6 }, /* jmpl %i7+8,%g0 */
+{ "retl", F3(2, 0x38, 1)|RS1(0x0f)|SIMM13(8), F3(~2, ~0x38, ~1)|RS1(~0x0f)|SIMM13(~8), "", F_DELAYED, v6 }, /* jmpl %o7+8,%g0 */
+
+{ "jmpl", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|ASI(~0), "1+2,d", F_DELAYED, v6 },
+{ "jmpl", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|ASI_RS2(~0), "1,d", F_DELAYED, v6 }, /* jmpl rs1+%g0,d */
+{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|SIMM13(~0), "1,d", F_DELAYED, v6 }, /* jmpl rs1+0,d */
+{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RS1_G0, "i,d", F_DELAYED, v6 }, /* jmpl %g0+i,d */
+{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1), "1+i,d", F_DELAYED, v6 },
+{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1), "i+1,d", F_DELAYED, v6 },
+{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RS1_G0, "i,d", F_DELAYED, v6 },
+
+ /* The 1<<12 is a long story. It is necessary. For more info, please contact rich@cygnus.com */
+{ "sll", F3(2, 0x25, 0), F3(~2, ~0x25, ~0)|(1<<12)|ASI(~0), "1,2,d", 0, v6 },
+{ "sll", F3(2, 0x25, 1), F3(~2, ~0x25, ~1)|(1<<12), "1,i,d", 0, v6 },
+{ "sra", F3(2, 0x27, 0), F3(~2, ~0x27, ~0)|(1<<12)|ASI(~0), "1,2,d", 0, v6 },
+{ "sra", F3(2, 0x27, 1), F3(~2, ~0x27, ~1)|(1<<12), "1,i,d", 0, v6 },
+{ "srl", F3(2, 0x26, 0), F3(~2, ~0x26, ~0)|(1<<12)|ASI(~0), "1,2,d", 0, v6 },
+{ "srl", F3(2, 0x26, 1), F3(~2, ~0x26, ~1)|(1<<12), "1,i,d", 0, v6 },
+
+
+
+{ "mulscc", F3(2, 0x24, 0), F3(~2, ~0x24, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "mulscc", F3(2, 0x24, 1), F3(~2, ~0x24, ~1), "1,i,d", 0, v6 },
+
+{ "clr", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|RD_G0|RS1_G0|ASI_RS2(~0), "d", F_ALIAS, v6 }, /* or %g0,%g0,d */
+{ "clr", F3(2, 0x02, 1), F3(~2, ~0x02, ~1)|RS1_G0|SIMM13(~0), "d", F_ALIAS, v6 }, /* or %g0,0,d */
+{ "clr", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|RD_G0|ASI(~0), "[1+2]", F_ALIAS, v6 },
+{ "clr", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|RD_G0|ASI_RS2(~0), "[1]", F_ALIAS, v6 }, /* st %g0,[rs1+%g0] */
+{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0, "[1+i]", F_ALIAS, v6 },
+{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0, "[i+1]", F_ALIAS, v6 },
+{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0|RS1_G0, "[i]", F_ALIAS, v6 },
+{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0|SIMM13(~0), "[1]", F_ALIAS, v6 }, /* st %g0,[rs1+0] */
+
+{ "clrb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|RD_G0|ASI(~0), "[1+2]", F_ALIAS, v6 },
+{ "clrb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|RD_G0|ASI_RS2(~0), "[1]", F_ALIAS, v6 }, /* stb %g0,[rs1+%g0] */
+{ "clrb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RD_G0, "[1+i]", F_ALIAS, v6 },
+{ "clrb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RD_G0, "[i+1]", F_ALIAS, v6 },
+{ "clrb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RD_G0|RS1_G0, "[i]", F_ALIAS, v6 },
+
+{ "clrh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|RD_G0|ASI(~0), "[1+2]", F_ALIAS, v6 },
+{ "clrh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|RD_G0|ASI_RS2(~0), "[1]", F_ALIAS, v6 }, /* sth %g0,[rs1+%g0] */
+{ "clrh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RD_G0, "[1+i]", F_ALIAS, v6 },
+{ "clrh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RD_G0, "[i+1]", F_ALIAS, v6 },
+{ "clrh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RD_G0|RS1_G0, "[i]", F_ALIAS, v6 },
+
+{ "orcc", F3(2, 0x12, 0), F3(~2, ~0x12, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "orcc", F3(2, 0x12, 1), F3(~2, ~0x12, ~1), "1,i,d", 0, v6 },
+{ "orcc", F3(2, 0x12, 1), F3(~2, ~0x12, ~1), "i,1,d", 0, v6 },
+
+{ "orncc", F3(2, 0x16, 0), F3(~2, ~0x16, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "orncc", F3(2, 0x16, 1), F3(~2, ~0x16, ~1), "1,i,d", 0, v6 },
+{ "orncc", F3(2, 0x16, 1), F3(~2, ~0x16, ~1), "i,1,d", 0, v6 },
+
+{ "orn", F3(2, 0x06, 0), F3(~2, ~0x06, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "orn", F3(2, 0x06, 1), F3(~2, ~0x06, ~1), "1,i,d", 0, v6 },
+{ "orn", F3(2, 0x06, 1), F3(~2, ~0x06, ~1), "i,1,d", 0, v6 },
+
+{ "tst", F3(2, 0x12, 0), F3(~2, ~0x12, ~0)|RD_G0|ASI_RS2(~0), "1", 0, v6 }, /* orcc rs1, %g0, %g0 */
+{ "tst", F3(2, 0x12, 0), F3(~2, ~0x12, ~0)|RD_G0|RS1_G0|ASI(~0), "2", 0, v6 }, /* orcc %g0, rs2, %g0 */
+{ "tst", F3(2, 0x12, 1), F3(~2, ~0x12, ~1)|RD_G0|SIMM13(~0), "1", 0, v6 }, /* orcc rs1, 0, %g0 */
+
+{ "wr", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|ASI(~0), "1,2,m", 0, v8 }, /* wr r,r,%asrX */
+{ "wr", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|RD_G0|ASI(~0), "1,2,y", 0, v6 }, /* wr r,r,%y */
+{ "wr", F3(2, 0x30, 1), F3(~2, ~0x30, ~1), "1,i,m", 0, v8 }, /* wr r,i,%asrX */
+{ "wr", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|RD_G0, "1,i,y", 0, v6 }, /* wr r,i,%y */
+{ "wr", F3(2, 0x31, 0), F3(~2, ~0x31, ~0)|RD_G0|ASI(~0), "1,2,p", 0, v6 }, /* wr r,r,%psr */
+{ "wr", F3(2, 0x31, 1), F3(~2, ~0x31, ~1)|RD_G0, "1,i,p", 0, v6 }, /* wr r,i,%psr */
+{ "wr", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|RD_G0|ASI(~0), "1,2,w", 0, v6 }, /* wr r,r,%wim */
+{ "wr", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RD_G0, "1,i,w", 0, v6 }, /* wr r,i,%wim */
+{ "wr", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|RD_G0|ASI(~0), "1,2,t", 0, v6 }, /* wr r,r,%tbr */
+{ "wr", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RD_G0, "1,i,t", 0, v6 }, /* wr r,i,%tbr */
+
+
+{ "rd", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|SIMM13(~0), "M,d", 0, v8 }, /* rd %asr1,r */
+{ "rd", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|RS1_G0|SIMM13(~0), "y,d", 0, v6 }, /* rd %y,r */
+{ "rd", F3(2, 0x2b, 0), F3(~2, ~0x2b, ~0)|RS1_G0|SIMM13(~0), "t,d", 0, v6 }, /* rd %tbr,r */
+
+{ "rd", F3(2, 0x29, 0), F3(~2, ~0x29, ~0)|RS1_G0|SIMM13(~0), "p,d", 0, v6 }, /* rd %psr,r */
+{ "rd", F3(2, 0x2a, 0), F3(~2, ~0x2a, ~0)|RS1_G0|SIMM13(~0), "w,d", 0, v6 }, /* rd %wim,r */
+
+{ "mov", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|ASI(~0), "1,2,m", F_ALIAS, v8 }, /* wr r,r,%asrX */
+{ "mov", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|RD_G0|ASI(~0), "1,2,y", F_ALIAS, v6 }, /* wr r,r,%y */
+{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1), "1,i,m", F_ALIAS, v8 }, /* wr r,i,%asrX */
+{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|RD_G0, "1,i,y", F_ALIAS, v6 }, /* wr r,i,%y */
+{ "mov", F3(2, 0x31, 0), F3(~2, ~0x31, ~0)|RD_G0|ASI(~0), "1,2,p", F_ALIAS, v6 }, /* wr r,r,%psr */
+{ "mov", F3(2, 0x31, 1), F3(~2, ~0x31, ~1)|RD_G0, "1,i,p", F_ALIAS, v6 }, /* wr r,i,%psr */
+{ "mov", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|RD_G0|ASI(~0), "1,2,w", F_ALIAS, v6 }, /* wr r,r,%wim */
+{ "mov", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RD_G0, "1,i,w", F_ALIAS, v6 }, /* wr r,i,%wim */
+{ "mov", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|RD_G0|ASI(~0), "1,2,t", F_ALIAS, v6 }, /* wr r,r,%tbr */
+{ "mov", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RD_G0, "1,i,t", F_ALIAS, v6 }, /* wr r,i,%tbr */
+
+{ "mov", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|SIMM13(~0), "M,d", F_ALIAS, v8 }, /* rd %asr1,r */
+{ "mov", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|RS1_G0|SIMM13(~0), "y,d", F_ALIAS, v6 }, /* rd %y,r */
+{ "mov", F3(2, 0x29, 0), F3(~2, ~0x29, ~0)|RS1_G0|SIMM13(~0), "p,d", F_ALIAS, v6 }, /* rd %psr,r */
+{ "mov", F3(2, 0x2a, 0), F3(~2, ~0x2a, ~0)|RS1_G0|SIMM13(~0), "w,d", F_ALIAS, v6 }, /* rd %wim,r */
+{ "mov", F3(2, 0x2b, 0), F3(~2, ~0x2b, ~0)|RS1_G0|SIMM13(~0), "t,d", F_ALIAS, v6 }, /* rd %tbr,r */
+
+{ "mov", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|ASI_RS2(~0), "1,y", F_ALIAS, v6 }, /* wr rs1,%g0,%y */
+{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1), "i,y", F_ALIAS, v6 },
+{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|SIMM13(~0), "1,y", F_ALIAS, v6 }, /* wr rs1,0,%y */
+{ "mov", F3(2, 0x31, 0), F3(~2, ~0x31, ~0)|ASI_RS2(~0), "1,p", F_ALIAS, v6 }, /* wr rs1,%g0,%psr */
+{ "mov", F3(2, 0x31, 1), F3(~2, ~0x31, ~1), "i,p", F_ALIAS, v6 },
+{ "mov", F3(2, 0x31, 1), F3(~2, ~0x31, ~1)|SIMM13(~0), "1,p", F_ALIAS, v6 }, /* wr rs1,0,%psr */
+{ "mov", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|ASI_RS2(~0), "1,w", F_ALIAS, v6 }, /* wr rs1,%g0,%wim */
+{ "mov", F3(2, 0x32, 1), F3(~2, ~0x32, ~1), "i,w", F_ALIAS, v6 },
+{ "mov", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|SIMM13(~0), "1,w", F_ALIAS, v6 }, /* wr rs1,0,%wim */
+{ "mov", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|ASI_RS2(~0), "1,t", F_ALIAS, v6 }, /* wr rs1,%g0,%tbr */
+{ "mov", F3(2, 0x33, 1), F3(~2, ~0x33, ~1), "i,t", F_ALIAS, v6 },
+{ "mov", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|SIMM13(~0), "1,t", F_ALIAS, v6 }, /* wr rs1,0,%tbr */
+
+{ "mov", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|RS1_G0|ASI(~0), "2,d", 0, v6 }, /* or %g0,rs2,d */
+{ "mov", F3(2, 0x02, 1), F3(~2, ~0x02, ~1)|RS1_G0, "i,d", 0, v6 }, /* or %g0,i,d */
+{ "mov", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|ASI_RS2(~0), "1,d", 0, v6 }, /* or rs1,%g0,d */
+{ "mov", F3(2, 0x02, 1), F3(~2, ~0x02, ~1)|SIMM13(~0), "1,d", 0, v6 }, /* or rs1,0,d */
+
+{ "or", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "or", F3(2, 0x02, 1), F3(~2, ~0x02, ~1), "1,i,d", 0, v6 },
+{ "or", F3(2, 0x02, 1), F3(~2, ~0x02, ~1), "i,1,d", 0, v6 },
+
+{ "bset", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|ASI(~0), "2,r", F_ALIAS, v6 }, /* or rd,rs2,rd */
+{ "bset", F3(2, 0x02, 1), F3(~2, ~0x02, ~1), "i,r", F_ALIAS, v6 }, /* or rd,i,rd */
+
+{ "andn", F3(2, 0x05, 0), F3(~2, ~0x05, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "andn", F3(2, 0x05, 1), F3(~2, ~0x05, ~1), "1,i,d", 0, v6 },
+{ "andn", F3(2, 0x05, 1), F3(~2, ~0x05, ~1), "i,1,d", 0, v6 },
+
+{ "andncc", F3(2, 0x15, 0), F3(~2, ~0x15, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "andncc", F3(2, 0x15, 1), F3(~2, ~0x15, ~1), "1,i,d", 0, v6 },
+{ "andncc", F3(2, 0x15, 1), F3(~2, ~0x15, ~1), "i,1,d", 0, v6 },
+
+{ "bclr", F3(2, 0x05, 0), F3(~2, ~0x05, ~0)|ASI(~0), "2,r", F_ALIAS, v6 }, /* andn rd,rs2,rd */
+{ "bclr", F3(2, 0x05, 1), F3(~2, ~0x05, ~1), "i,r", F_ALIAS, v6 }, /* andn rd,i,rd */
+
+{ "cmp", F3(2, 0x14, 0), F3(~2, ~0x14, ~0)|RD_G0|ASI(~0), "1,2", 0, v6 }, /* subcc rs1,rs2,%g0 */
+{ "cmp", F3(2, 0x14, 1), F3(~2, ~0x14, ~1)|RD_G0, "1,i", 0, v6 }, /* subcc rs1,i,%g0 */
+
+{ "sub", F3(2, 0x04, 0), F3(~2, ~0x04, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "sub", F3(2, 0x04, 1), F3(~2, ~0x04, ~1), "1,i,d", 0, v6 },
+
+{ "subcc", F3(2, 0x14, 0), F3(~2, ~0x14, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "subcc", F3(2, 0x14, 1), F3(~2, ~0x14, ~1), "1,i,d", 0, v6 },
+
+{ "subx", F3(2, 0x0c, 0), F3(~2, ~0x0c, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "subx", F3(2, 0x0c, 1), F3(~2, ~0x0c, ~1), "1,i,d", 0, v6 },
+
+{ "subxcc", F3(2, 0x1c, 0), F3(~2, ~0x1c, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "subxcc", F3(2, 0x1c, 1), F3(~2, ~0x1c, ~1), "1,i,d", 0, v6 },
+
+{ "and", F3(2, 0x01, 0), F3(~2, ~0x01, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "and", F3(2, 0x01, 1), F3(~2, ~0x01, ~1), "1,i,d", 0, v6 },
+{ "and", F3(2, 0x01, 1), F3(~2, ~0x01, ~1), "i,1,d", 0, v6 },
+
+{ "andcc", F3(2, 0x11, 0), F3(~2, ~0x11, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "andcc", F3(2, 0x11, 1), F3(~2, ~0x11, ~1), "1,i,d", 0, v6 },
+{ "andcc", F3(2, 0x11, 1), F3(~2, ~0x11, ~1), "i,1,d", 0, v6 },
+
+{ "dec", F3(2, 0x04, 1)|SIMM13(0x1), F3(~2, ~0x04, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* sub rd,1,rd */
+{ "dec", F3(2, 0x04, 1), F3(~2, ~0x04, ~1), "i,r", F_ALIAS, v6 }, /* sub rd,i,rd */
+{ "deccc", F3(2, 0x14, 1)|SIMM13(0x1), F3(~2, ~0x14, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* subcc rd,1,rd */
+{ "deccc", F3(2, 0x14, 1), F3(~2, ~0x14, ~1), "i,r", F_ALIAS, v6 }, /* subcc rd,i,rd */
+{ "inc", F3(2, 0x00, 1)|SIMM13(0x1), F3(~2, ~0x00, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* add rs1,1,rsd */
+{ "inc", F3(2, 0x00, 1), F3(~2, ~0x00, ~1), "i,r", F_ALIAS, v6 }, /* add rs1,i,rsd */
+{ "inccc", F3(2, 0x10, 1)|SIMM13(0x1), F3(~2, ~0x10, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* addcc rd,1,rd */
+{ "inccc", F3(2, 0x10, 1), F3(~2, ~0x10, ~1), "i,r", F_ALIAS, v6 }, /* addcc rd,i,rd */
+
+{ "btst", F3(2, 0x11, 0), F3(~2, ~0x11, ~0)|RD_G0|ASI(~0), "1,2", F_ALIAS, v6 }, /* andcc rs1,rs2,%g0 */
+{ "btst", F3(2, 0x11, 1), F3(~2, ~0x11, ~1)|RD_G0, "i,1", F_ALIAS, v6 }, /* andcc rs1,i,%g0 */
+
+{ "neg", F3(2, 0x04, 0), F3(~2, ~0x04, ~0)|RS1_G0|ASI(~0), "2,d", F_ALIAS, v6 }, /* sub %g0,rs2,rd */
+{ "neg", F3(2, 0x04, 0), F3(~2, ~0x04, ~0)|RS1_G0|ASI(~0), "x", F_ALIAS, v6 }, /* sub %g0,rd,rd */
+
+{ "add", F3(2, 0x00, 0), F3(~2, ~0x00, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "add", F3(2, 0x00, 1), F3(~2, ~0x00, ~1), "1,i,d", 0, v6 },
+{ "add", F3(2, 0x00, 1), F3(~2, ~0x00, ~1), "i,1,d", 0, v6 },
+{ "addcc", F3(2, 0x10, 0), F3(~2, ~0x10, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "addcc", F3(2, 0x10, 1), F3(~2, ~0x10, ~1), "1,i,d", 0, v6 },
+{ "addcc", F3(2, 0x10, 1), F3(~2, ~0x10, ~1), "i,1,d", 0, v6 },
+{ "addx", F3(2, 0x08, 0), F3(~2, ~0x08, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "addx", F3(2, 0x08, 1), F3(~2, ~0x08, ~1), "1,i,d", 0, v6 },
+{ "addx", F3(2, 0x08, 1), F3(~2, ~0x08, ~1), "i,1,d", 0, v6 },
+{ "addxcc", F3(2, 0x18, 0), F3(~2, ~0x18, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "addxcc", F3(2, 0x18, 1), F3(~2, ~0x18, ~1), "1,i,d", 0, v6 },
+{ "addxcc", F3(2, 0x18, 1), F3(~2, ~0x18, ~1), "i,1,d", 0, v6 },
+
+{ "smul", F3(2, 0x0b, 0), F3(~2, ~0x0b, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "smul", F3(2, 0x0b, 1), F3(~2, ~0x0b, ~1), "1,i,d", 0, v8 },
+{ "smul", F3(2, 0x0b, 1), F3(~2, ~0x0b, ~1), "i,1,d", 0, v8 },
+{ "smulcc", F3(2, 0x1b, 0), F3(~2, ~0x1b, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "smulcc", F3(2, 0x1b, 1), F3(~2, ~0x1b, ~1), "1,i,d", 0, v8 },
+{ "smulcc", F3(2, 0x1b, 1), F3(~2, ~0x1b, ~1), "i,1,d", 0, v8 },
+{ "umul", F3(2, 0x0a, 0), F3(~2, ~0x0a, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "umul", F3(2, 0x0a, 1), F3(~2, ~0x0a, ~1), "1,i,d", 0, v8 },
+{ "umul", F3(2, 0x0a, 1), F3(~2, ~0x0a, ~1), "i,1,d", 0, v8 },
+{ "umulcc", F3(2, 0x1a, 0), F3(~2, ~0x1a, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "umulcc", F3(2, 0x1a, 1), F3(~2, ~0x1a, ~1), "1,i,d", 0, v8 },
+{ "umulcc", F3(2, 0x1a, 1), F3(~2, ~0x1a, ~1), "i,1,d", 0, v8 },
+{ "sdiv", F3(2, 0x0f, 0), F3(~2, ~0x0f, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "sdiv", F3(2, 0x0f, 1), F3(~2, ~0x0f, ~1), "1,i,d", 0, v8 },
+{ "sdiv", F3(2, 0x0f, 1), F3(~2, ~0x0f, ~1), "i,1,d", 0, v8 },
+{ "sdivcc", F3(2, 0x1f, 0), F3(~2, ~0x1f, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "sdivcc", F3(2, 0x1f, 1), F3(~2, ~0x1f, ~1), "1,i,d", 0, v8 },
+{ "sdivcc", F3(2, 0x1f, 1), F3(~2, ~0x1f, ~1), "i,1,d", 0, v8 },
+{ "udiv", F3(2, 0x0e, 0), F3(~2, ~0x0e, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "udiv", F3(2, 0x0e, 1), F3(~2, ~0x0e, ~1), "1,i,d", 0, v8 },
+{ "udiv", F3(2, 0x0e, 1), F3(~2, ~0x0e, ~1), "i,1,d", 0, v8 },
+{ "udivcc", F3(2, 0x1e, 0), F3(~2, ~0x1e, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "udivcc", F3(2, 0x1e, 1), F3(~2, ~0x1e, ~1), "1,i,d", 0, v8 },
+{ "udivcc", F3(2, 0x1e, 1), F3(~2, ~0x1e, ~1), "i,1,d", 0, v8 },
+
+
+{ "call", F1(0x1), F1(~0x1), "L", F_DELAYED, v6 },
+{ "call", F1(0x1), F1(~0x1), "L,#", F_DELAYED, v6 },
+{ "call", F3(2, 0x38, 0)|RD(0xf), F3(~2, ~0x38, ~0)|RD(~0xf)|ASI_RS2(~0), "1", F_DELAYED, v6 }, /* jmpl rs1+%g0, %o7 */
+{ "call", F3(2, 0x38, 0)|RD(0xf), F3(~2, ~0x38, ~0)|RD(~0xf)|ASI_RS2(~0), "1,#", F_DELAYED, v6 },
+
+/* Conditional instructions.
+
+ Because this part of the table was such a mess earlier, I have
+ macrofied it so that all the branches and traps are generated from
+ a single-line description of each condition value. John Gilmore. */
+
+/* Define branches -- one annulled, one without, etc. */
+#define br(opcode, mask, lose, flags) \
+ { opcode, (mask)|ANNUL, (lose), ",a l", (flags), v6 }, \
+ { opcode, (mask) , (lose)|ANNUL, "l", (flags), v6 }
+
+
+/* Define four traps: reg+reg, reg + immediate, immediate alone, reg alone. */
+#define tr(opcode, mask, lose, flags) \
+ { opcode, (mask)|IMMED, (lose)|RS1_G0, "i", (flags), v6 }, /* %g0 + imm */ \
+ { opcode, (mask)|IMMED, (lose), "1+i", (flags), v6 }, /* rs1 + imm */ \
+ { opcode, (mask), IMMED|(lose), "1+2", (flags), v6 }, /* rs1 + rs2 */ \
+ { opcode, (mask), IMMED|(lose)|RS2_G0, "1", (flags), v6 } /* rs1 + %g0 */
+
+/* Define both branches and traps based on condition mask */
+#define cond(bop, top, mask, flags) \
+ br(bop, F2(0, 2)|(mask), F2(~0, ~2)|((~mask)&COND(~0)), F_DELAYED|(flags)), \
+ tr(top, F3(2, 0x3a, 0)|(mask), F3(~2, ~0x3a, 0)|((~mask)&COND(~0)), (flags))
+
+/* Define all the conditions, all the branches, all the traps. */
+
+cond ("b", "t", CONDA, 0),
+cond ("ba", "ta", CONDA, F_ALIAS), /* for nothing */
+cond ("bcc", "tcc", CONDCC, 0),
+cond ("bcs", "tcs", CONDCS, 0),
+cond ("be", "te", CONDE, 0),
+cond ("bg", "tg", CONDG, 0),
+cond ("bgt", "tgt", CONDG, F_ALIAS),
+cond ("bge", "tge", CONDGE, 0),
+cond ("bgeu", "tgeu", CONDGEU, F_ALIAS), /* for cc */
+cond ("bgu", "tgu", CONDGU, 0),
+cond ("bl", "tl", CONDL, 0),
+cond ("blt", "tlt", CONDL, F_ALIAS),
+cond ("ble", "tle", CONDLE, 0),
+cond ("bleu", "tleu", CONDLEU, 0),
+cond ("blu", "tlu", CONDLU, F_ALIAS), /* for cs */
+cond ("bn", "tn", CONDN, 0),
+cond ("bne", "tne", CONDNE, 0),
+cond ("bneg", "tneg", CONDNEG, 0),
+cond ("bnz", "tnz", CONDNZ, F_ALIAS), /* for ne */
+cond ("bpos", "tpos", CONDPOS, 0),
+cond ("bvc", "tvc", CONDVC, 0),
+cond ("bvs", "tvs", CONDVS, 0),
+cond ("bz", "tz", CONDZ, F_ALIAS), /* for e */
+
+#undef cond
+#undef br
+#undef tr
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#define brfc(opcode, mask, lose, flags) \
+ { opcode, (mask), ANNUL|(lose), "l", flags|F_DELAYED, v6 }, \
+ { opcode, (mask)|ANNUL, (lose), ",a l", flags|F_DELAYED, v6 }
+
+
+#define condfc(fop, cop, mask, flags) \
+ brfc(fop, F2(0, 6)|COND(mask), F2(~0, ~6)|COND(~(mask)), flags), \
+ brfc(cop, F2(0, 7)|COND(mask), F2(~0, ~7)|COND(~(mask)), flags) \
+
+condfc("fb", "cb", 0x8, 0),
+condfc("fba", "cba", 0x8, F_ALIAS),
+condfc("fbe", "cb0", 0x9, 0),
+condfc("fbg", "cb2", 0x6, 0),
+condfc("fbge", "cb02", 0xb, 0),
+condfc("fbl", "cb1", 0x4, 0),
+condfc("fble", "cb01", 0xd, 0),
+condfc("fblg", "cb12", 0x2, 0),
+condfc("fbn", "cbn", 0x0, 0),
+condfc("fbne", "cb123", 0x1, 0),
+condfc("fbo", "cb012", 0xf, 0),
+condfc("fbu", "cb3", 0x7, 0),
+condfc("fbue", "cb03", 0xa, 0),
+condfc("fbug", "cb23", 0x5, 0),
+condfc("fbuge", "cb023", 0xc, 0),
+condfc("fbul", "cb13", 0x3, 0),
+condfc("fbule", "cb013", 0xe, 0),
+
+#undef condfc
+#undef brfc
+
+{ "jmp", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|RD_G0|ASI(~0), "1+2", F_DELAYED, v6 }, /* jmpl rs1+rs2,%g0 */
+{ "jmp", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|RD_G0|ASI_RS2(~0), "1", F_DELAYED, v6 }, /* jmpl rs1+%g0,%g0 */
+{ "jmp", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RD_G0, "1+i", F_DELAYED, v6 }, /* jmpl rs1+i,%g0 */
+{ "jmp", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RD_G0, "i+1", F_DELAYED, v6 }, /* jmpl i+rs1,%g0 */
+{ "jmp", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RD_G0|RS1_G0, "i", F_DELAYED, v6 }, /* jmpl %g0+i,%g0 */
+
+{ "nop", F2(0, 4), 0xfeffffff, "", 0, v6 }, /* sethi 0, %g0 */
+
+{ "set", F2(0x0, 0x4), F2(~0x0, ~0x4), "Sh,d", F_ALIAS, v6 },
+
+{ "sethi", F2(0x0, 0x4), F2(~0x0, ~0x4), "h,d", 0, v6 },
+
+{ "taddcc", F3(2, 0x20, 0), F3(~2, ~0x20, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "taddcc", F3(2, 0x20, 1), F3(~2, ~0x20, ~1), "1,i,d", 0, v6 },
+{ "taddcc", F3(2, 0x20, 1), F3(~2, ~0x20, ~1), "i,1,d", 0, v6 },
+{ "taddcctv", F3(2, 0x22, 0), F3(~2, ~0x22, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "taddcctv", F3(2, 0x22, 1), F3(~2, ~0x22, ~1), "1,i,d", 0, v6 },
+{ "taddcctv", F3(2, 0x22, 1), F3(~2, ~0x22, ~1), "i,1,d", 0, v6 },
+
+{ "tsubcc", F3(2, 0x21, 0), F3(~2, ~0x21, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "tsubcc", F3(2, 0x21, 1), F3(~2, ~0x21, ~1), "1,i,d", 0, v6 },
+{ "tsubcctv", F3(2, 0x23, 0), F3(~2, ~0x23, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "tsubcctv", F3(2, 0x23, 1), F3(~2, ~0x23, ~1), "1,i,d", 0, v6 },
+
+{ "unimp", F2(0x0, 0x0), 0xffc00000, "n", 0, v6 },
+
+{ "iflush", F3(2, 0x3b, 0), F3(~2, ~0x3b, ~0)|ASI(~0), "1+2", 0, v6 },
+{ "iflush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1), "1+i", 0, v6 },
+{ "iflush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1), "i+1", 0, v6 },
+{ "iflush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1)|RS1_G0, "i", 0, v6 },
+
+{ "xnor", F3(2, 0x07, 0), F3(~2, ~0x07, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "xnor", F3(2, 0x07, 1), F3(~2, ~0x07, ~1), "1,i,d", 0, v6 },
+{ "xnor", F3(2, 0x07, 1), F3(~2, ~0x07, ~1), "i,1,d", 0, v6 },
+{ "xnorcc", F3(2, 0x17, 0), F3(~2, ~0x17, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "xnorcc", F3(2, 0x17, 1), F3(~2, ~0x17, ~1), "1,i,d", 0, v6 },
+{ "xnorcc", F3(2, 0x17, 1), F3(~2, ~0x17, ~1), "i,1,d", 0, v6 },
+{ "xor", F3(2, 0x03, 0), F3(~2, ~0x03, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "xor", F3(2, 0x03, 1), F3(~2, ~0x03, ~1), "1,i,d", 0, v6 },
+{ "xor", F3(2, 0x03, 1), F3(~2, ~0x03, ~1), "i,1,d", 0, v6 },
+{ "xorcc", F3(2, 0x13, 0), F3(~2, ~0x13, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "xorcc", F3(2, 0x13, 1), F3(~2, ~0x13, ~1), "1,i,d", 0, v6 },
+{ "xorcc", F3(2, 0x13, 1), F3(~2, ~0x13, ~1), "i,1,d", 0, v6 },
+
+{ "not", F3(2, 0x07, 0), F3(~2, ~0x07, ~0)|ASI(~0), "1,d", F_ALIAS, v6 }, /* xnor rs1,%0,rd */
+{ "not", F3(2, 0x07, 0), F3(~2, ~0x07, ~0)|ASI(~0), "r", F_ALIAS, v6 }, /* xnor rd,%0,rd */
+
+{ "btog", F3(2, 0x03, 0), F3(~2, ~0x03, ~0)|ASI(~0), "2,r", F_ALIAS, v6 }, /* xor rd,rs2,rd */
+{ "btog", F3(2, 0x03, 1), F3(~2, ~0x03, ~1), "i,r", F_ALIAS, v6 }, /* xor rd,i,rd */
+
+{ "fpop1", F3F(2, 0x34, 0), F3F(~2, ~0x34, ~1), "[1+2],d", 0, v6 },
+{ "fpop2", F3F(2, 0x35, 0), F3F(~2, ~0x35, ~1), "[1+2],d", 0, v6 },
+
+/* float-start */
+{ "fdtoi", F3F(2, 0x34, 0x0d2), F3F(~2, ~0x34, ~0x0d2)|RS1_G0, "B,g", 0, v6 },
+{ "fstoi", F3F(2, 0x34, 0x0d1), F3F(~2, ~0x34, ~0x0d1)|RS1_G0, "f,g", 0, v6 },
+
+ /* all of these conversions are confused and probably wrong. */
+{ "fitod", F3F(2, 0x34, 0x0c8), F3F(~2, ~0x34, ~0x0c8)|RS1_G0, "f,H", 0, v6 },
+{ "fitos", F3F(2, 0x34, 0x0c4), F3F(~2, ~0x34, ~0x0c4)|RS1_G0, "f,g", 0, v6 },
+
+{ "fitoq", F3F(2, 0x34, 0x0cc), F3F(~2, ~0x34, ~0x0cc)|RS1_G0, "f,J", 0, v8 },
+
+
+{ "fdtoq", F3F(2, 0x34, 0x0ce), F3F(~2, ~0x34, ~0x0ce)|RS1_G0, "B,J", 0, v8 },
+{ "fdtos", F3F(2, 0x34, 0x0c6), F3F(~2, ~0x34, ~0x0c6)|RS1_G0, "B,g", 0, v6 },
+{ "fqtod", F3F(2, 0x34, 0x0cb), F3F(~2, ~0x34, ~0x0cb)|RS1_G0, "R,H", 0, v8 },
+{ "fqtos", F3F(2, 0x34, 0x0c7), F3F(~2, ~0x34, ~0x0c7)|RS1_G0, "R,g", 0, v8 },
+{ "fstod", F3F(2, 0x34, 0x0c9), F3F(~2, ~0x34, ~0x0c9)|RS1_G0, "f,H", 0, v6 },
+{ "fstoq", F3F(2, 0x34, 0x0cd), F3F(~2, ~0x34, ~0x0cd)|RS1_G0, "f,J", 0, v8 },
+
+
+
+
+
+{ "fqtoi", F3F(2, 0x34, 0x0d3), F3F(~2, ~0x34, ~0x0d3)|RS1_G0, "R,g", 0, v8 },
+
+
+{ "fdivd", F3F(2, 0x34, 0x04e), F3F(~2, ~0x34, ~0x04e), "v,B,H", 0, v6 },
+{ "fdivq", F3F(2, 0x34, 0x04f), F3F(~2, ~0x34, ~0x04f), "V,R,J", 0, v8 },
+{ "fdivs", F3F(2, 0x34, 0x04d), F3F(~2, ~0x34, ~0x04d), "e,f,g", 0, v6 },
+{ "fmuld", F3F(2, 0x34, 0x04a), F3F(~2, ~0x34, ~0x04a), "v,B,H", 0, v6 },
+{ "fmulq", F3F(2, 0x34, 0x04b), F3F(~2, ~0x34, ~0x04b), "V,R,J", 0, v8 },
+{ "fmuls", F3F(2, 0x34, 0x049), F3F(~2, ~0x34, ~0x049), "e,f,g", 0, v6 },
+
+{ "fdmulq", F3F(2, 0x34, 0x06e), F3F(~2, ~0x34, ~0x06e), "v,B,J", 0, v8 },
+{ "fsmuld", F3F(2, 0x34, 0x069), F3F(~2, ~0x34, ~0x069), "e,f,H", 0, v8 },
+
+{ "fsqrtd", F3F(2, 0x34, 0x02a), F3F(~2, ~0x34, ~0x02a)|RS1_G0, "B,H", 0, v7 },
+{ "fsqrtq", F3F(2, 0x34, 0x02b), F3F(~2, ~0x34, ~0x02b)|RS1_G0, "R,J", 0, v8 },
+{ "fsqrts", F3F(2, 0x34, 0x029), F3F(~2, ~0x34, ~0x029)|RS1_G0, "f,g", 0, v7 },
+
+{ "fabsq", F3F(2, 0x34, 0x00b), F3F(~2, ~0x34, ~0x00b)|RS1_G0, "R,J", 0, v6 },
+{ "fabss", F3F(2, 0x34, 0x009), F3F(~2, ~0x34, ~0x009)|RS1_G0, "f,g", 0, v6 },
+{ "fmovq", F3F(2, 0x34, 0x003), F3F(~2, ~0x34, ~0x003)|RS1_G0, "R,J", 0, v6 },
+{ "fmovs", F3F(2, 0x34, 0x001), F3F(~2, ~0x34, ~0x001)|RS1_G0, "f,g", 0, v6 },
+{ "fnegq", F3F(2, 0x34, 0x007), F3F(~2, ~0x34, ~0x007)|RS1_G0, "R,J", 0, v6 },
+{ "fnegs", F3F(2, 0x34, 0x005), F3F(~2, ~0x34, ~0x005)|RS1_G0, "f,g", 0, v6 },
+
+
+{ "faddd", F3F(2, 0x34, 0x042), F3F(~2, ~0x34, ~0x042), "v,B,H", 0, v6 },
+{ "faddq", F3F(2, 0x34, 0x043), F3F(~2, ~0x34, ~0x043), "V,R,J", 0, v8 },
+{ "fadds", F3F(2, 0x34, 0x041), F3F(~2, ~0x34, ~0x041), "e,f,g", 0, v6 },
+{ "fsubd", F3F(2, 0x34, 0x046), F3F(~2, ~0x34, ~0x046), "v,B,H", 0, v6 },
+{ "fsubq", F3F(2, 0x34, 0x047), F3F(~2, ~0x34, ~0x047), "V,R,J", 0, v8 },
+{ "fsubs", F3F(2, 0x34, 0x045), F3F(~2, ~0x34, ~0x045), "e,f,g", 0, v6 },
+
+#define CMPFCC(x) (((x)&0x3)<<25)
+
+{ "fcmpd", F3F(2, 0x35, 0x052), F3F(~2, ~0x35, ~0x052)|RS1_G0, "v,B", 0, v6 },
+{ "fcmped", F3F(2, 0x35, 0x056), F3F(~2, ~0x35, ~0x056)|RS1_G0, "v,B", 0, v6 },
+{ "fcmpeq", F3F(2, 0x34, 0x057), F3F(~2, ~0x34, ~0x057), "V,R", 0, v8 },
+{ "fcmpes", F3F(2, 0x35, 0x055), F3F(~2, ~0x35, ~0x055)|RS1_G0, "e,f", 0, v6 },
+{ "fcmpq", F3F(2, 0x34, 0x053), F3F(~2, ~0x34, ~0x053), "V,R", 0, v8 },
+{ "fcmps", F3F(2, 0x35, 0x051), F3F(~2, ~0x35, ~0x051)|RS1_G0, "e,f", 0, v6 },
+
+{ "cpop1", F3(2, 0x36, 0), F3(~2, ~0x36, ~1), "[1+2],d", 0, v6 },
+{ "cpop2", F3(2, 0x37, 0), F3(~2, ~0x37, ~1), "[1+2],d", 0, v6 },
+
+
+
+};
+
+#define NUMOPCODES ((sizeof sparc_opcodes)/(sizeof sparc_opcodes[0]))
+
+/*
+ * Local Variables:
+ * fill-column: 131
+ * comment-column: 0
+ * End:
+ */
+
+/* end of sparc-opcode.h */
diff --git a/gnu/usr.bin/as/opcode/tahoe.h b/gnu/usr.bin/as/opcode/tahoe.h
new file mode 100644
index 0000000..27099a4
--- /dev/null
+++ b/gnu/usr.bin/as/opcode/tahoe.h
@@ -0,0 +1,247 @@
+/* tahoe-opcode.h - tahoe-specific
+ * Not part of GAS yet
+ *
+ * Ported by the State University of New York at Buffalo by the Distributed
+ * Computer Systems Lab, Department of Computer Science, 1991. (And by Dale
+ * Wiles who was unemployed at the time.)
+ */
+
+struct tot_wot /* tahoe opcode table: wot to do with this */
+ /* particular opcode */
+{
+ char *args; /* how to compile said opcode */
+ tahoe_opcodeT code; /* The opcode. */
+};
+
+struct tot /* tahoe opcode text */
+{
+ char *name; /* opcode name: lowercase string [key] */
+ struct tot_wot detail; /* rest of opcode table [datum] */
+};
+
+static struct tot
+totstrs[] =
+{
+{ "halt", {"", 0x00 } },
+{ "sinf", {"", 0x05 } },
+{ "ldf", {"rl", 0x06 } },
+{ "ldd", {"rq", 0x07 } },
+{ "addb2", {"rbmb", 0x08 } },
+{ "movb", {"rbwb", 0x09 } },
+{ "addw2", {"rwmw", 0x0a } },
+{ "movw", {"rwww", 0x0b } },
+{ "addl2", {"rlml", 0x0c } },
+{ "movl", {"rlwl", 0x0d } },
+{ "bbs", {"rlvlbw", 0x0e } },
+{ "nop", {"", 0x10 } },
+{ "brb", {"bb", 0x11 } },
+{ "brw", {"bw", 0x13 } },
+{ "cosf", {"", 0x15 } },
+{ "lnf", {"rl", 0x16 } },
+{ "lnd", {"rq", 0x17 } },
+{ "addb3", {"rbrbwb", 0x18 } },
+ /* cmpb is wrong in the "offical" (what a joke!) text. It's not "rbwb" */
+{ "cmpb", {"rbrb", 0x19 } },
+{ "addw3", {"rwrwww", 0x1a } },
+ /* cmpw is wrong in the "offical" text. It's not "rwww" */
+{ "cmpw", {"rwrw", 0x1b } },
+{ "addl3", {"rlrlwl", 0x1c } },
+ /* cmpl is wrong in the "offical" text. It's not "rlwl" */
+{ "cmpl", {"rlrl", 0x1d } },
+{ "bbc", {"rlvlbw", 0x1e } },
+{ "rei", {"", 0x20 } },
+{ "bneq", {"bb", 0x21 } },
+{ "bnequ", {"bb", 0x21 } },
+{ "cvtwl", {"rwwl", 0x23 } },
+{ "stf", {"wl", 0x26 } },
+{ "std", {"wq", 0x27 } },
+{ "subb2", {"rbmb", 0x28 } },
+{ "mcomb", {"rbwb", 0x29 } },
+{ "subw2", {"rwmw", 0x2a } },
+{ "mcomw", {"rwww", 0x2b } },
+{ "subl2", {"rlml", 0x2c } },
+{ "mcoml", {"rlwl", 0x2d } },
+{ "emul", {"rlrlrlwq", 0x2e } },
+{ "aoblss", {"rlmlbw", 0x2f } },
+{ "bpt", {"", 0x30 } },
+{ "beql", {"bb", 0x31 } },
+{ "beqlu", {"bb", 0x31 } },
+{ "cvtwb", {"rwwb", 0x33 } },
+{ "logf", {"", 0x35 } },
+{ "cmpf", {"rl", 0x36 } },
+{ "cmpd", {"rq", 0x37 } },
+{ "subb3", {"rbrbwb", 0x38 } },
+{ "bitb", {"rbrb", 0x39 } },
+{ "subw3", {"rwrwww", 0x3a } },
+{ "bitw", {"rwrw", 0x3b } },
+{ "subl3", {"rlrlwl", 0x3c } },
+{ "bitl", {"rlrl", 0x3d } },
+{ "ediv", {"rlrqwlwl", 0x3e } },
+{ "aobleq", {"rlmlbw", 0x3f } },
+{ "ret", {"", 0x40 } },
+{ "bgtr", {"bb", 0x41 } },
+{ "sqrtf", {"", 0x45 } },
+{ "cmpf2", {"rlrl", 0x46 } },
+{ "cmpd2", {"rqrq", 0x47 } },
+{ "shll", {"rbrlwl", 0x48 } },
+{ "clrb", {"wb", 0x49 } },
+{ "shlq", {"rbrqwq", 0x4a } },
+{ "clrw", {"ww", 0x4b } },
+{ "mull2", {"rlml", 0x4c } },
+{ "clrl", {"wl", 0x4d } },
+{ "shal", {"rbrlwl", 0x4e } },
+{ "bleq", {"bb", 0x51 } },
+{ "expf", {"", 0x55 } },
+{ "tstf", {"", 0x56 } },
+{ "tstd", {"", 0x57 } },
+{ "shrl", {"rbrlwl", 0x58 } },
+{ "tstb", {"rb", 0x59 } },
+{ "shrq", {"rbrqwq", 0x5a } },
+{ "tstw", {"rw", 0x5b } },
+{ "mull3", {"rlrlwl", 0x5c } },
+{ "tstl", {"rl", 0x5d } },
+{ "shar", {"rbrlwl", 0x5e } },
+{ "bbssi", {"rlmlbw", 0x5f } },
+{ "ldpctx", {"", 0x60 } },
+{ "pushd", {"", 0x67 } },
+{ "incb", {"mb", 0x69 } },
+{ "incw", {"mw", 0x6b } },
+{ "divl2", {"rlml", 0x6c } },
+{ "incl", {"ml", 0x6d } },
+{ "cvtlb", {"rlwb", 0x6f } },
+{ "svpctx", {"", 0x70 } },
+{ "jmp", {"ab", 0x71 } },
+{ "cvlf", {"rl", 0x76 } },
+{ "cvld", {"rl", 0x77 } },
+{ "decb", {"mb", 0x79 } },
+{ "decw", {"mw", 0x7b } },
+{ "divl3", {"rlrlwl", 0x7c } },
+{ "decl", {"ml", 0x7d } },
+{ "cvtlw", {"rlww", 0x7f } },
+{ "bgeq", {"bb", 0x81 } },
+{ "movs2", {"abab", 0x82 } },
+{ "cvfl", {"wl", 0x86 } },
+{ "cvdl", {"wl", 0x87 } },
+{ "orb2", {"rbmb", 0x88 } },
+{ "cvtbl", {"rbwl", 0x89 } },
+{ "orw2", {"rwmw", 0x8a } },
+{ "bispsw", {"rw", 0x8b } },
+{ "orl2", {"rlml", 0x8c } },
+{ "adwc", {"rlml", 0x8d } },
+{ "adda", {"rlml", 0x8e } },
+{ "blss", {"bb", 0x91 } },
+{ "cmps2", {"abab", 0x92 } },
+{ "ldfd", {"rl", 0x97 } },
+{ "orb3", {"rbrbwb", 0x98 } },
+{ "cvtbw", {"rbww", 0x99 } },
+{ "orw3", {"rwrwww", 0x9a } },
+{ "bicpsw", {"rw", 0x9b } },
+{ "orl3", {"rlrlwl", 0x9c } },
+{ "sbwc", {"rlml", 0x9d } },
+{ "suba", {"rlml", 0x9e } },
+{ "bgtru", {"bb", 0xa1 } },
+{ "cvdf", {"", 0xa6 } },
+{ "andb2", {"rbmb", 0xa8 } },
+{ "movzbl", {"rbwl", 0xa9 } },
+{ "andw2", {"rwmw", 0xaa } },
+{ "loadr", {"rwal", 0xab } },
+{ "andl2", {"rlml", 0xac } },
+{ "mtpr", {"rlrl", 0xad } },
+{ "ffs", {"rlwl", 0xae } },
+{ "blequ", {"bb", 0xb1 } },
+{ "negf", {"", 0xb6 } },
+{ "negd", {"", 0xb7 } },
+{ "andb3", {"rbrbwb", 0xb8 } },
+{ "movzbw", {"rbww", 0xb9 } },
+{ "andw3", {"rwrwww", 0xba } },
+{ "storer", {"rwal", 0xbb } },
+{ "andl3", {"rlrlwl", 0xbc } },
+{ "mfpr", {"rlwl", 0xbd } },
+{ "ffc", {"rlwl", 0xbe } },
+{ "calls", {"rbab", 0xbf } },
+{ "prober", {"rbabrl", 0xc0 } },
+{ "bvc", {"bb", 0xc1 } },
+{ "movs3", {"ababrw", 0xc2 } },
+{ "movzwl", {"rwwl", 0xc3 } },
+{ "addf", {"rl", 0xc6 } },
+{ "addd", {"rq", 0xc7 } },
+{ "xorb2", {"rbmb", 0xc8 } },
+{ "movob", {"rbwb", 0xc9 } },
+{ "xorw2", {"rwmw", 0xca } },
+{ "movow", {"rwww", 0xcb } },
+{ "xorl2", {"rlml", 0xcc } },
+{ "movpsl", {"wl", 0xcd } },
+{ "kcall", {"rw", 0xcf } },
+{ "probew", {"rbabrl", 0xd0 } },
+{ "bvs", {"bb", 0xd1 } },
+{ "cmps3", {"ababrw", 0xd2 } },
+{ "subf", {"rq", 0xd6 } },
+{ "subd", {"rq", 0xd7 } },
+{ "xorb3", {"rbrbwb", 0xd8 } },
+{ "pushb", {"rb", 0xd9 } },
+{ "xorw3", {"rwrwww", 0xda } },
+{ "pushw", {"rw", 0xdb } },
+{ "xorl3", {"rlrlwl", 0xdc } },
+{ "pushl", {"rl", 0xdd } },
+{ "insque", {"abab", 0xe0 } },
+{ "bcs", {"bb", 0xe1 } },
+{ "bgequ", {"bb", 0xe1 } },
+{ "mulf", {"rq", 0xe6 } },
+{ "muld", {"rq", 0xe7 } },
+{ "mnegb", {"rbwb", 0xe8 } },
+{ "movab", {"abwl", 0xe9 } },
+{ "mnegw", {"rwww", 0xea } },
+{ "movaw", {"awwl", 0xeb } },
+{ "mnegl", {"rlwl", 0xec } },
+{ "moval", {"alwl", 0xed } },
+{ "remque", {"ab", 0xf0 } },
+{ "bcc", {"bb", 0xf1 } },
+{ "blssu", {"bb", 0xf1 } },
+{ "divf", {"rq", 0xf6 } },
+{ "divd", {"rq", 0xf7 } },
+ /* movblk is really "alalrw" but 'as' won't accept it,
+ 'cc' and 'gcc' also produce code this way. */
+{ "movblk", {"", 0xf8 } },
+{ "pushab", {"ab", 0xf9 } },
+{ "pushaw", {"aw", 0xfb } },
+{ "casel", {"rlrlrl", 0xfc } },
+{ "pushal", {"al", 0xfd } },
+{ "callf", {"rbab", 0xfe } },
+{ "", "" } /* empty is end sentinel */
+};
+
+/* These are synthetic instructions, where the assembler will munge
+ the addressings modes for you. */
+static struct tot
+synthetic_totstrs[] =
+{
+{ "jr", {"b-", 0x11 } },
+{ "jbr", {"b-", 0x11 } },
+
+{ "jneq", {"b?", 0x21 } },
+{ "jnequ", {"b?", 0x21 } },
+{ "jeql", {"b?", 0x31 } },
+{ "jeqlu", {"b?", 0x31 } },
+{ "jgtr", {"b?", 0x41 } },
+{ "jleq", {"b?", 0x51 } },
+{ "jgeq", {"b?", 0x81 } },
+{ "jlss", {"b?", 0x91 } },
+{ "jgtru", {"b?", 0xa1 } },
+{ "jlequ", {"b?", 0xb1 } },
+{ "jvc", {"b?", 0xc1 } },
+{ "jvs", {"b?", 0xd1 } },
+{ "jcs", {"b?", 0xe1 } },
+{ "jgequ", {"b?", 0xe1 } },
+{ "jcc", {"b?", 0xf1 } },
+{ "jlssu", {"b?", 0xf1 } },
+
+{ "jbs", {"rlvlb!", 0x0e } },
+{ "jbc", {"rlvlb!", 0x1e } },
+
+{ "aojlss", {"rlmlb:", 0x2f } },
+{ "jaoblss", {"rlmlb:", 0x2f } },
+{ "aojleq", {"rlmlb:", 0x3f } },
+{ "jaobleq", {"rlmlb:", 0x3f } },
+{ "jbssi", {"rlmlb:", 0x5f } },
+ { "", "" } /* empty is end sentinel */
+};
diff --git a/gnu/usr.bin/as/opcode/vax.h b/gnu/usr.bin/as/opcode/vax.h
new file mode 100644
index 0000000..d604e3f
--- /dev/null
+++ b/gnu/usr.bin/as/opcode/vax.h
@@ -0,0 +1,382 @@
+/* Vax opcde list.
+ Copyright (C) 1989, Free Software Foundation, Inc.
+
+This file is part of GDB and GAS.
+
+GDB and GAS are free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB and GAS are distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB or GAS; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef vax_opcodeT
+#define vax_opcodeT int
+#endif /* no vax_opcodeT */
+
+struct vot_wot /* vax opcode table: wot to do with this */
+ /* particular opcode */
+{
+ char * args; /* how to compile said opcode */
+ vax_opcodeT code; /* op-code (may be > 8 bits!) */
+};
+
+struct vot /* vax opcode text */
+{
+ char * name; /* opcode name: lowercase string [key] */
+ struct vot_wot detail; /* rest of opcode table [datum] */
+};
+
+#define vot_how args
+#define vot_code code
+#define vot_detail detail
+#define vot_name name
+
+static const struct vot
+votstrs[] =
+{
+{ "halt", {"", 0x00 } },
+{ "nop", {"", 0x01 } },
+{ "rei", {"", 0x02 } },
+{ "bpt", {"", 0x03 } },
+{ "ret", {"", 0x04 } },
+{ "rsb", {"", 0x05 } },
+{ "ldpctx", {"", 0x06 } },
+{ "svpctx", {"", 0x07 } },
+{ "cvtps", {"rwabrwab", 0x08 } },
+{ "cvtsp", {"rwabrwab", 0x09 } },
+{ "index", {"rlrlrlrlrlwl", 0x0a } },
+{ "crc", {"abrlrwab", 0x0b } },
+{ "prober", {"rbrwab", 0x0c } },
+{ "probew", {"rbrwab", 0x0d } },
+{ "insque", {"abab", 0x0e } },
+{ "remque", {"abwl", 0x0f } },
+{ "bsbb", {"bb", 0x10 } },
+{ "brb", {"bb", 0x11 } },
+{ "bneq", {"bb", 0x12 } },
+{ "bnequ", {"bb", 0x12 } },
+{ "beql", {"bb", 0x13 } },
+{ "beqlu", {"bb", 0x13 } },
+{ "bgtr", {"bb", 0x14 } },
+{ "bleq", {"bb", 0x15 } },
+{ "jsb", {"ab", 0x16 } },
+{ "jmp", {"ab", 0x17 } },
+{ "bgeq", {"bb", 0x18 } },
+{ "blss", {"bb", 0x19 } },
+{ "bgtru", {"bb", 0x1a } },
+{ "blequ", {"bb", 0x1b } },
+{ "bvc", {"bb", 0x1c } },
+{ "bvs", {"bb", 0x1d } },
+{ "bcc", {"bb", 0x1e } },
+{ "bgequ", {"bb", 0x1e } },
+{ "blssu", {"bb", 0x1f } },
+{ "bcs", {"bb", 0x1f } },
+{ "addp4", {"rwabrwab", 0x20 } },
+{ "addp6", {"rwabrwabrwab", 0x21 } },
+{ "subp4", {"rwabrwab", 0x22 } },
+{ "subp6", {"rwabrwabrwab", 0x23 } },
+{ "cvtpt", {"rwababrwab", 0x24 } },
+{ "mulp", {"rwabrwabrwab", 0x25 } },
+{ "cvttp", {"rwababrwab", 0x26 } },
+{ "divp", {"rwabrwabrwab", 0x27 } },
+{ "movc3", {"rwabab", 0x28 } },
+{ "cmpc3", {"rwabab", 0x29 } },
+{ "scanc", {"rwababrb", 0x2a } },
+{ "spanc", {"rwababrb", 0x2b } },
+{ "movc5", {"rwabrbrwab", 0x2c } },
+{ "cmpc5", {"rwabrbrwab", 0x2d } },
+{ "movtc", {"rwabrbabrwab", 0x2e } },
+{ "movtuc", {"rwabrbabrwab", 0x2f } },
+{ "bsbw", {"bw", 0x30 } },
+{ "brw", {"bw", 0x31 } },
+{ "cvtwl", {"rwwl", 0x32 } },
+{ "cvtwb", {"rwwb", 0x33 } },
+{ "movp", {"rwabab", 0x34 } },
+{ "cmpp3", {"rwabab", 0x35 } },
+{ "cvtpl", {"rwabwl", 0x36 } },
+{ "cmpp4", {"rwabrwab", 0x37 } },
+{ "editpc", {"rwababab", 0x38 } },
+{ "matchc", {"rwabrwab", 0x39 } },
+{ "locc", {"rbrwab", 0x3a } },
+{ "skpc", {"rbrwab", 0x3b } },
+{ "movzwl", {"rwwl", 0x3c } },
+{ "acbw", {"rwrwmwbw", 0x3d } },
+{ "movaw", {"awwl", 0x3e } },
+{ "pushaw", {"aw", 0x3f } },
+{ "addf2", {"rfmf", 0x40 } },
+{ "addf3", {"rfrfwf", 0x41 } },
+{ "subf2", {"rfmf", 0x42 } },
+{ "subf3", {"rfrfwf", 0x43 } },
+{ "mulf2", {"rfmf", 0x44 } },
+{ "mulf3", {"rfrfwf", 0x45 } },
+{ "divf2", {"rfmf", 0x46 } },
+{ "divf3", {"rfrfwf", 0x47 } },
+{ "cvtfb", {"rfwb", 0x48 } },
+{ "cvtfw", {"rfww", 0x49 } },
+{ "cvtfl", {"rfwl", 0x4a } },
+{ "cvtrfl", {"rfwl", 0x4b } },
+{ "cvtbf", {"rbwf", 0x4c } },
+{ "cvtwf", {"rwwf", 0x4d } },
+{ "cvtlf", {"rlwf", 0x4e } },
+{ "acbf", {"rfrfmfbw", 0x4f } },
+{ "movf", {"rfwf", 0x50 } },
+{ "cmpf", {"rfrf", 0x51 } },
+{ "mnegf", {"rfwf", 0x52 } },
+{ "tstf", {"rf", 0x53 } },
+{ "emodf", {"rfrbrfwlwf", 0x54 } },
+{ "polyf", {"rfrwab", 0x55 } },
+{ "cvtfd", {"rfwd", 0x56 } },
+ /* opcode 57 is not defined yet */
+{ "adawi", {"rwmw", 0x58 } },
+ /* opcode 59 is not defined yet */
+ /* opcode 5a is not defined yet */
+ /* opcode 5b is not defined yet */
+{ "insqhi", {"abaq", 0x5c } },
+{ "insqti", {"abaq", 0x5d } },
+{ "remqhi", {"aqwl", 0x5e } },
+{ "remqti", {"aqwl", 0x5f } },
+{ "addd2", {"rdmd", 0x60 } },
+{ "addd3", {"rdrdwd", 0x61 } },
+{ "subd2", {"rdmd", 0x62 } },
+{ "subd3", {"rdrdwd", 0x63 } },
+{ "muld2", {"rdmd", 0x64 } },
+{ "muld3", {"rdrdwd", 0x65 } },
+{ "divd2", {"rdmd", 0x66 } },
+{ "divd3", {"rdrdwd", 0x67 } },
+{ "cvtdb", {"rdwb", 0x68 } },
+{ "cvtdw", {"rdww", 0x69 } },
+{ "cvtdl", {"rdwl", 0x6a } },
+{ "cvtrdl", {"rdwl", 0x6b } },
+{ "cvtbd", {"rbwd", 0x6c } },
+{ "cvtwd", {"rwwd", 0x6d } },
+{ "cvtld", {"rlwd", 0x6e } },
+{ "acbd", {"rdrdmdbw", 0x6f } },
+{ "movd", {"rdwd", 0x70 } },
+{ "cmpd", {"rdrd", 0x71 } },
+{ "mnegd", {"rdwd", 0x72 } },
+{ "tstd", {"rd", 0x73 } },
+{ "emodd", {"rdrbrdwlwd", 0x74 } },
+{ "polyd", {"rdrwab", 0x75 } },
+{ "cvtdf", {"rdwf", 0x76 } },
+ /* opcode 77 is not defined yet */
+{ "ashl", {"rbrlwl", 0x78 } },
+{ "ashq", {"rbrqwq", 0x79 } },
+{ "emul", {"rlrlrlwq", 0x7a } },
+{ "ediv", {"rlrqwlwl", 0x7b } },
+{ "clrd", {"wd", 0x7c } },
+{ "clrg", {"wg", 0x7c } },
+{ "clrq", {"wd", 0x7c } },
+{ "movq", {"rqwq", 0x7d } },
+{ "movaq", {"aqwl", 0x7e } },
+{ "movad", {"adwl", 0x7e } },
+{ "pushaq", {"aq", 0x7f } },
+{ "pushad", {"ad", 0x7f } },
+{ "addb2", {"rbmb", 0x80 } },
+{ "addb3", {"rbrbwb", 0x81 } },
+{ "subb2", {"rbmb", 0x82 } },
+{ "subb3", {"rbrbwb", 0x83 } },
+{ "mulb2", {"rbmb", 0x84 } },
+{ "mulb3", {"rbrbwb", 0x85 } },
+{ "divb2", {"rbmb", 0x86 } },
+{ "divb3", {"rbrbwb", 0x87 } },
+{ "bisb2", {"rbmb", 0x88 } },
+{ "bisb3", {"rbrbwb", 0x89 } },
+{ "bicb2", {"rbmb", 0x8a } },
+{ "bicb3", {"rbrbwb", 0x8b } },
+{ "xorb2", {"rbmb", 0x8c } },
+{ "xorb3", {"rbrbwb", 0x8d } },
+{ "mnegb", {"rbwb", 0x8e } },
+{ "caseb", {"rbrbrb", 0x8f } },
+{ "movb", {"rbwb", 0x90 } },
+{ "cmpb", {"rbrb", 0x91 } },
+{ "mcomb", {"rbwb", 0x92 } },
+{ "bitb", {"rbrb", 0x93 } },
+{ "clrb", {"wb", 0x94 } },
+{ "tstb", {"rb", 0x95 } },
+{ "incb", {"mb", 0x96 } },
+{ "decb", {"mb", 0x97 } },
+{ "cvtbl", {"rbwl", 0x98 } },
+{ "cvtbw", {"rbww", 0x99 } },
+{ "movzbl", {"rbwl", 0x9a } },
+{ "movzbw", {"rbww", 0x9b } },
+{ "rotl", {"rbrlwl", 0x9c } },
+{ "acbb", {"rbrbmbbw", 0x9d } },
+{ "movab", {"abwl", 0x9e } },
+{ "pushab", {"ab", 0x9f } },
+{ "addw2", {"rwmw", 0xa0 } },
+{ "addw3", {"rwrwww", 0xa1 } },
+{ "subw2", {"rwmw", 0xa2 } },
+{ "subw3", {"rwrwww", 0xa3 } },
+{ "mulw2", {"rwmw", 0xa4 } },
+{ "mulw3", {"rwrwww", 0xa5 } },
+{ "divw2", {"rwmw", 0xa6 } },
+{ "divw3", {"rwrwww", 0xa7 } },
+{ "bisw2", {"rwmw", 0xa8 } },
+{ "bisw3", {"rwrwww", 0xa9 } },
+{ "bicw2", {"rwmw", 0xaa } },
+{ "bicw3", {"rwrwww", 0xab } },
+{ "xorw2", {"rwmw", 0xac } },
+{ "xorw3", {"rwrwww", 0xad } },
+{ "mnegw", {"rwww", 0xae } },
+{ "casew", {"rwrwrw", 0xaf } },
+{ "movw", {"rwww", 0xb0 } },
+{ "cmpw", {"rwrw", 0xb1 } },
+{ "mcomw", {"rwww", 0xb2 } },
+{ "bitw", {"rwrw", 0xb3 } },
+{ "clrw", {"ww", 0xb4 } },
+{ "tstw", {"rw", 0xb5 } },
+{ "incw", {"mw", 0xb6 } },
+{ "decw", {"mw", 0xb7 } },
+{ "bispsw", {"rw", 0xb8 } },
+{ "bicpsw", {"rw", 0xb9 } },
+{ "popr", {"rw", 0xba } },
+{ "pushr", {"rw", 0xbb } },
+{ "chmk", {"rw", 0xbc } },
+{ "chme", {"rw", 0xbd } },
+{ "chms", {"rw", 0xbe } },
+{ "chmu", {"rw", 0xbf } },
+{ "addl2", {"rlml", 0xc0 } },
+{ "addl3", {"rlrlwl", 0xc1 } },
+{ "subl2", {"rlml", 0xc2 } },
+{ "subl3", {"rlrlwl", 0xc3 } },
+{ "mull2", {"rlml", 0xc4 } },
+{ "mull3", {"rlrlwl", 0xc5 } },
+{ "divl2", {"rlml", 0xc6 } },
+{ "divl3", {"rlrlwl", 0xc7 } },
+{ "bisl2", {"rlml", 0xc8 } },
+{ "bisl3", {"rlrlwl", 0xc9 } },
+{ "bicl2", {"rlml", 0xca } },
+{ "bicl3", {"rlrlwl", 0xcb } },
+{ "xorl2", {"rlml", 0xcc } },
+{ "xorl3", {"rlrlwl", 0xcd } },
+{ "mnegl", {"rlwl", 0xce } },
+{ "casel", {"rlrlrl", 0xcf } },
+{ "movl", {"rlwl", 0xd0 } },
+{ "cmpl", {"rlrl", 0xd1 } },
+{ "mcoml", {"rlwl", 0xd2 } },
+{ "bitl", {"rlrl", 0xd3 } },
+{ "clrf", {"wf", 0xd4 } },
+{ "clrl", {"wl", 0xd4 } },
+{ "tstl", {"rl", 0xd5 } },
+{ "incl", {"ml", 0xd6 } },
+{ "decl", {"ml", 0xd7 } },
+{ "adwc", {"rlml", 0xd8 } },
+{ "sbwc", {"rlml", 0xd9 } },
+{ "mtpr", {"rlrl", 0xda } },
+{ "mfpr", {"rlwl", 0xdb } },
+{ "movpsl", {"wl", 0xdc } },
+{ "pushl", {"rl", 0xdd } },
+{ "moval", {"alwl", 0xde } },
+{ "movaf", {"afwl", 0xde } },
+{ "pushal", {"al", 0xdf } },
+{ "pushaf", {"af", 0xdf } },
+{ "bbs", {"rlabbb", 0xe0 } },
+{ "bbc", {"rlabbb", 0xe1 } },
+{ "bbss", {"rlabbb", 0xe2 } },
+{ "bbcs", {"rlabbb", 0xe3 } },
+{ "bbsc", {"rlabbb", 0xe4 } },
+{ "bbcc", {"rlabbb", 0xe5 } },
+{ "bbssi", {"rlabbb", 0xe6 } },
+{ "bbcci", {"rlabbb", 0xe7 } },
+{ "blbs", {"rlbb", 0xe8 } },
+{ "blbc", {"rlbb", 0xe9 } },
+{ "ffs", {"rlrbvbwl", 0xea } },
+{ "ffc", {"rlrbvbwl", 0xeb } },
+{ "cmpv", {"rlrbvbrl", 0xec } },
+{ "cmpzv", {"rlrbvbrl", 0xed } },
+{ "extv", {"rlrbvbwl", 0xee } },
+{ "extzv", {"rlrbvbwl", 0xef } },
+{ "insv", {"rlrlrbvb", 0xf0 } },
+{ "acbl", {"rlrlmlbw", 0xf1 } },
+{ "aoblss", {"rlmlbb", 0xf2 } },
+{ "aobleq", {"rlmlbb", 0xf3 } },
+{ "sobgeq", {"mlbb", 0xf4 } },
+{ "sobgtr", {"mlbb", 0xf5 } },
+{ "cvtlb", {"rlwb", 0xf6 } },
+{ "cvtlw", {"rlww", 0xf7 } },
+{ "ashp", {"rbrwabrbrwab", 0xf8 } },
+{ "cvtlp", {"rlrwab", 0xf9 } },
+{ "callg", {"abab", 0xfa } },
+{ "calls", {"rlab", 0xfb } },
+{ "xfc", {"", 0xfc } },
+ /* undefined opcodes here */
+{ "cvtdh", {"rdwh", 0x32fd } },
+{ "cvtgf", {"rgwh", 0x33fd } },
+{ "addg2", {"rgmg", 0x40fd } },
+{ "addg3", {"rgrgwg", 0x41fd } },
+{ "subg2", {"rgmg", 0x42fd } },
+{ "subg3", {"rgrgwg", 0x43fd } },
+{ "mulg2", {"rgmg", 0x44fd } },
+{ "mulg3", {"rgrgwg", 0x45fd } },
+{ "divg2", {"rgmg", 0x46fd } },
+{ "divg3", {"rgrgwg", 0x47fd } },
+{ "cvtgb", {"rgwb", 0x48fd } },
+{ "cvtgw", {"rgww", 0x49fd } },
+{ "cvtgl", {"rgwl", 0x4afd } },
+{ "cvtrgl", {"rgwl", 0x4bfd } },
+{ "cvtbg", {"rbwg", 0x4cfd } },
+{ "cvtwg", {"rwwg", 0x4dfd } },
+{ "cvtlg", {"rlwg", 0x4efd } },
+{ "acbg", {"rgrgmgbw", 0x4ffd } },
+{ "movg", {"rgwg", 0x50fd } },
+{ "cmpg", {"rgrg", 0x51fd } },
+{ "mnegg", {"rgwg", 0x52fd } },
+{ "tstg", {"rg", 0x53fd } },
+{ "emodg", {"rgrwrgwlwg", 0x54fd } },
+{ "polyg", {"rgrwab", 0x55fd } },
+{ "cvtgh", {"rgwh", 0x56fd } },
+ /* undefined opcodes here */
+{ "addh2", {"rhmh", 0x60fd } },
+{ "addh3", {"rhrhwh", 0x61fd } },
+{ "subh2", {"rhmh", 0x62fd } },
+{ "subh3", {"rhrhwh", 0x63fd } },
+{ "mulh2", {"rhmh", 0x64fd } },
+{ "mulh3", {"rhrhwh", 0x65fd } },
+{ "divh2", {"rhmh", 0x66fd } },
+{ "divh3", {"rhrhwh", 0x67fd } },
+{ "cvthb", {"rhwb", 0x68fd } },
+{ "cvthw", {"rhww", 0x69fd } },
+{ "cvthl", {"rhwl", 0x6afd } },
+{ "cvtrhl", {"rhwl", 0x6bfd } },
+{ "cvtbh", {"rbwh", 0x6cfd } },
+{ "cvtwh", {"rwwh", 0x6dfd } },
+{ "cvtlh", {"rlwh", 0x6efd } },
+{ "acbh", {"rhrhmhbw", 0x6ffd } },
+{ "movh", {"rhwh", 0x70fd } },
+{ "cmph", {"rhrh", 0x71fd } },
+{ "mnegh", {"rhwh", 0x72fd } },
+{ "tsth", {"rh", 0x73fd } },
+{ "emodh", {"rhrwrhwlwh", 0x74fd } },
+{ "polyh", {"rhrwab", 0x75fd } },
+{ "cvthg", {"rhwg", 0x76fd } },
+ /* undefined opcodes here */
+{ "clrh", {"wh", 0x7cfd } },
+{ "clro", {"wo", 0x7cfd } },
+{ "movo", {"rowo", 0x7dfd } },
+{ "movah", {"ahwl", 0x7efd } },
+{ "movao", {"aowl", 0x7efd } },
+{ "pushah", {"ah", 0x7ffd } },
+{ "pushao", {"ao", 0x7ffd } },
+ /* undefined opcodes here */
+{ "cvtfh", {"rfwh", 0x98fd } },
+{ "cvtfg", {"rfwg", 0x99fd } },
+ /* undefined opcodes here */
+{ "cvthf", {"rhwf", 0xf6fd } },
+{ "cvthd", {"rhwd", 0xf7fd } },
+ /* undefined opcodes here */
+{ "bugl", {"rl", 0xfdff } },
+{ "bugw", {"rw", 0xfeff } },
+ /* undefined opcodes here */
+
+{ "" , "" } /* empty is end sentinel */
+
+}; /* votstrs */
+
+/* end: vax.opcode.h */
diff --git a/gnu/usr.bin/as/output-file.c b/gnu/usr.bin/as/output-file.c
new file mode 100644
index 0000000..7439e86
--- /dev/null
+++ b/gnu/usr.bin/as/output-file.c
@@ -0,0 +1,122 @@
+/* output-file.c - Deal with the output file
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * Confines all details of emitting object bytes to this module.
+ * All O/S specific crocks should live here.
+ * What we lose in "efficiency" we gain in modularity.
+ * Note we don't need to #include the "as.h" file. No common coupling!
+ */
+
+/* note that we do need config info. xoxorich. */
+
+#ifndef lint
+static char rcsid[] = "$Id: output-file.c,v 1.3 1993/10/02 20:57:49 pk Exp $";
+#endif
+
+#include <stdio.h>
+
+#include "as.h"
+
+#include "output-file.h"
+#ifdef BFD_HEADERS
+#include "bfd.h"
+bfd *stdoutput;
+void output_file_create(name)
+char *name;
+{
+ if (name[0] == '-' && name[1] == '\0') {
+ as_perror("FATAL: Can't open a bfd on stdout %s ", name);
+ }
+ else if ( ! (stdoutput = bfd_openw( name, TARGET_FORMAT )) )
+ {
+ as_perror ("FATAL: Can't create %s", name);
+ exit(42);
+ }
+ bfd_set_format(stdoutput, bfd_object);
+}
+/* output_file_create() */
+
+
+void output_file_close(filename)
+char *filename;
+{
+ /* Close the bfd without getting bfd to write out anything by itself */
+ if ( bfd_close_all_done( stdoutput ) == 0 )
+ {
+ as_perror ("FATAL: Can't close %s\n", filename);
+ exit(42);
+ }
+ stdoutput = NULL; /* Trust nobody! */
+} /* output_file_close() */
+
+void output_file_append(where, length, filename)
+char *where;
+long length;
+char *filename;
+{
+ abort(); /* Never do this */
+}
+
+#else
+
+static FILE *stdoutput;
+
+void output_file_create(name)
+char *name;
+{
+ if (name[0] == '-' && name[1] == '\0')
+ stdoutput=stdout;
+ else if (!(stdoutput = fopen(name, "wb"))) {
+ as_perror("FATAL: Can't create %s", name);
+ exit(42);
+ }
+} /* output_file_create() */
+
+
+
+void output_file_close(filename)
+char *filename;
+{
+ if (EOF == fclose(stdoutput)) {
+ as_perror ("FATAL: Can't close %s", filename);
+ exit(42);
+ }
+ stdoutput = NULL; /* Trust nobody! */
+} /* output_file_close() */
+
+void output_file_append(where, length, filename)
+char *where;
+long length;
+char *filename;
+{
+
+ for (; length; length--, where++) {
+ (void) putc(*where, stdoutput);
+ if (ferror(stdoutput))
+ /* if ( EOF == (putc( *where, stdoutput )) ) */
+ {
+ as_perror("Failed to emit an object byte", filename);
+ as_fatal("Can't continue");
+ }
+ }
+} /* output_file_append() */
+#endif
+
+/* end of output-file.c */
diff --git a/gnu/usr.bin/as/output-file.h b/gnu/usr.bin/as/output-file.h
new file mode 100644
index 0000000..e7d7b97
--- /dev/null
+++ b/gnu/usr.bin/as/output-file.h
@@ -0,0 +1,40 @@
+/* This file is output-file.h
+
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
+ * $Id: output-file.h,v 1.1 1993/10/02 20:57:49 pk Exp $
+ */
+
+
+#ifdef __STDC__
+
+void output_file_append(char *where, long length, char *filename);
+void output_file_close(char *filename);
+void output_file_create(char *name);
+
+#else /* __STDC__ */
+
+void output_file_append();
+void output_file_close();
+void output_file_create();
+
+#endif /* __STDC__ */
+
+
+/* end of output-file.h */
diff --git a/gnu/usr.bin/as/read.c b/gnu/usr.bin/as/read.c
new file mode 100644
index 0000000..cf3c416
--- /dev/null
+++ b/gnu/usr.bin/as/read.c
@@ -0,0 +1,2347 @@
+/* read.c - read a source file -
+
+ Copyright (C) 1986, 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef lint
+static char rcsid[] = "$Id: read.c,v 1.3 1993/11/30 20:55:43 jkh Exp $";
+#endif
+
+#define MASK_CHAR (0xFF) /* If your chars aren't 8 bits, you will
+ change this a bit. But then, GNU isn't
+ spozed to run on your machine anyway.
+ (RMS is so shortsighted sometimes.)
+ */
+
+#define MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT (16)
+/* This is the largest known floating point */
+/* format (for now). It will grow when we */
+/* do 4361 style flonums. */
+
+
+/* Routines that read assembler source text to build spagetti in memory. */
+/* Another group of these functions is in the as-expr.c module */
+
+#include "as.h"
+
+#include "obstack.h"
+
+char *input_line_pointer; /*->next char of source file to parse. */
+
+#ifndef NOP_OPCODE
+# define NOP_OPCODE 0x00
+#endif
+
+#if BITS_PER_CHAR != 8
+The following table is indexed by [ (char) ] and will break if
+ a char does not have exactly 256 states (hopefully 0:255!) !
+#endif
+
+#ifdef ALLOW_ATSIGN
+#define AT 2
+#else
+#define AT 0
+#endif
+
+#ifndef PIC
+ const
+#endif
+ char /* used by is_... macros. our ctype[] */
+ lex_type[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ABCDEFGHIJKLMNO */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* PQRSTUVWXYZ[\]^_ */
+ 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, /* _!"#$%&'()*+,-./ */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0123456789:;<=>? */
+ AT, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* @ABCDEFGHIJKLMNO */
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, /* PQRSTUVWXYZ[\]^_ */
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* `abcdefghijklmno */
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, /* pqrstuvwxyz{|}~. */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+
+/*
+ * In: a character.
+ * Out: 1 if this character ends a line.
+ */
+#define _ (0)
+char is_end_of_line[256] = {
+#ifdef CR_EOL
+ _, _, _, _, _, _, _, _, _, _,99, _, _, 99, _, _,/* @abcdefghijklmno */
+#else
+ _, _, _, _, _, _, _, _, _, _,99, _, _, _, _, _, /* @abcdefghijklmno */
+#endif
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */
+ _, _, _, _, _, _, _, _, _, _, _,99, _, _, _, _, /* 0123456789:;<=>? */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _ /* */
+ };
+#undef _
+
+/* Functions private to this file. */
+
+extern const char line_comment_chars[];
+const char line_separator_chars[1];
+
+static char *buffer; /* 1st char of each buffer of lines is here. */
+static char *buffer_limit; /*->1 + last char in buffer. */
+
+static char *bignum_low; /* Lowest char of bignum. */
+static char *bignum_limit; /* 1st illegal address of bignum. */
+static char *bignum_high; /* Highest char of bignum. */
+/* May point to (bignum_start-1). */
+/* Never >= bignum_limit. */
+static char *old_buffer = 0; /* JF a hack */
+static char *old_input;
+static char *old_limit;
+
+/* Variables for handling include file directory list. */
+
+char **include_dirs; /* List of pointers to directories to
+ search for .include's */
+int include_dir_count; /* How many are in the list */
+int include_dir_maxlen = 1; /* Length of longest in list */
+
+#ifndef WORKING_DOT_WORD
+struct broken_word *broken_words;
+int new_broken_words = 0;
+#endif
+
+#if __STDC__ == 1
+
+static char *demand_copy_string(int *lenP);
+int is_it_end_of_statement(void);
+unsigned int next_char_of_string(void);
+static segT get_known_segmented_expression(expressionS *expP);
+static void grow_bignum(void);
+static void pobegin(void);
+void stringer(int append_zero);
+
+#else /* __STDC__ */
+
+static char *demand_copy_string();
+int is_it_end_of_statement();
+unsigned int next_char_of_string();
+static segT get_known_segmented_expression();
+static void grow_bignum();
+static void pobegin();
+void stringer();
+
+#endif /* not __STDC__ */
+
+extern int listing;
+
+
+void
+ read_begin()
+{
+ const char *p;
+
+ pobegin();
+ obj_read_begin_hook();
+
+ obstack_begin(&notes, 5000);
+ obstack_begin(&cond_obstack, 960);
+
+#define BIGNUM_BEGIN_SIZE (16)
+ bignum_low = xmalloc((long)BIGNUM_BEGIN_SIZE);
+ bignum_limit = bignum_low + BIGNUM_BEGIN_SIZE;
+
+ /* Use machine dependent syntax */
+ for (p = line_separator_chars; *p; p++)
+ is_end_of_line[*p] = 1;
+ /* Use more. FIXME-SOMEDAY. */
+}
+
+/* set up pseudo-op tables */
+
+struct hash_control *
+ po_hash = NULL; /* use before set up: NULL->address error */
+
+static const pseudo_typeS
+ potable[] =
+{
+ { "abort", s_abort, 0 },
+ { "align", s_align_ptwo, 0 },
+ { "ascii", stringer, 0 },
+ { "asciz", stringer, 1 },
+ /* block */
+ { "byte", cons, 1 },
+ { "comm", s_comm, 0 },
+ { "data", s_data, 0 },
+ /* dim */
+ { "double", float_cons, 'd' },
+ /* dsect */
+#ifdef NO_LISTING
+ { "eject", s_ignore, 0 }, /* Formfeed listing */
+#else
+ { "eject", listing_eject, 0 }, /* Formfeed listing */
+#endif /* NO_LISTING */
+ { "else", s_else, 0 },
+ { "end", s_end, 0 },
+ { "endif", s_endif, 0 },
+ /* endef */
+ { "equ", s_set, 0 },
+ /* err */
+ /* extend */
+ { "extern", s_ignore, 0 }, /* We treat all undef as ext */
+ { "app-file", s_app_file, 0 },
+ { "file", s_app_file, 0 },
+ { "fill", s_fill, 0 },
+ { "float", float_cons, 'f' },
+ { "global", s_globl, 0 },
+ { "globl", s_globl, 0 },
+ { "hword", cons, 2 },
+ { "if", s_if, 0 },
+ { "ifdef", s_ifdef, 0 },
+ { "ifeqs", s_ifeqs, 0 },
+ { "ifndef", s_ifdef, 1 },
+ { "ifnes", s_ifeqs, 1 },
+ { "ifnotdef", s_ifdef, 1 },
+ { "include", s_include, 0 },
+ { "int", cons, 4 },
+ { "lcomm", s_lcomm, 0 },
+#ifdef NO_LISTING
+ { "lflags", s_ignore, 0 }, /* Listing flags */
+ { "list", s_ignore, 1 }, /* Turn listing on */
+#else
+ { "lflags", listing_flags, 0 }, /* Listing flags */
+ { "list", listing_list, 1 }, /* Turn listing on */
+#endif /* NO_LISTING */
+ { "long", cons, 4 },
+ { "lsym", s_lsym, 0 },
+#ifdef NO_LISTING
+ { "nolist", s_ignore, 0 }, /* Turn listing off */
+#else
+ { "nolist", listing_list, 0 }, /* Turn listing off */
+#endif /* NO_LISTING */
+ { "octa", big_cons, 16 },
+ { "org", s_org, 0 },
+#ifdef NO_LISTING
+ { "psize", s_ignore, 0 }, /* set paper size */
+#else
+ { "psize", listing_psize, 0 }, /* set paper size */
+#endif /* NO_LISTING */
+ /* print */
+ { "quad", big_cons, 8 },
+#ifdef NO_LISTING
+ { "sbttl", s_ignore, 1 }, /* Subtitle of listing */
+#else
+ { "sbttl", listing_title, 1 }, /* Subtitle of listing */
+#endif /* NO_LISTING */
+ /* scl */
+ /* sect */
+#ifndef TC_M88K
+ { "set", s_set, 0 },
+#endif /* TC_M88K */
+ { "short", cons, 2 },
+ { "single", float_cons, 'f' },
+ /* size */
+ { "space", s_space, 0 },
+ /* tag */
+ { "text", s_text, 0 },
+#ifdef NO_LISTING
+ { "title", s_ignore, 0 }, /* Listing title */
+#else
+ { "title", listing_title, 0 }, /* Listing title */
+#endif /* NO_LISTING */
+ /* type */
+ /* use */
+ /* val */
+ { "word", cons, 2 },
+ { NULL} /* end sentinel */
+};
+
+static void pobegin() {
+ char *errtxt; /* error text */
+ const pseudo_typeS * pop;
+
+ po_hash = hash_new();
+
+ /* Do the target-specific pseudo ops. */
+ for (pop = md_pseudo_table; pop->poc_name; pop++) {
+ errtxt = hash_insert(po_hash, pop->poc_name, (char *)pop);
+ if (errtxt && *errtxt) {
+ as_fatal("error constructing md pseudo-op table");
+ } /* on error */
+ } /* for each op */
+
+ /* Now object specific. Skip any that were in the target table. */
+ for (pop = obj_pseudo_table; pop->poc_name; pop++) {
+ errtxt = hash_insert(po_hash, pop->poc_name, (char *) pop);
+ if (errtxt && *errtxt) {
+ if (!strcmp(errtxt, "exists")) {
+#ifdef DIE_ON_OVERRIDES
+ as_fatal("pseudo op \".%s\" overridden.\n", pop->poc_name);
+#endif /* DIE_ON_OVERRIDES */
+ continue; /* OK if target table overrides. */
+ } else {
+ as_fatal("error constructing obj pseudo-op table");
+ } /* if overridden */
+ } /* on error */
+ } /* for each op */
+
+ /* Now portable ones. Skip any that we've seen already. */
+ for (pop = potable; pop->poc_name; pop++) {
+ errtxt = hash_insert(po_hash, pop->poc_name, (char *) pop);
+ if (errtxt && *errtxt) {
+ if (!strcmp (errtxt, "exists")) {
+#ifdef DIE_ON_OVERRIDES
+ as_fatal("pseudo op \".%s\" overridden.\n", pop->poc_name);
+#endif /* DIE_ON_OVERRIDES */
+ continue; /* OK if target table overrides. */
+ } else {
+ as_fatal("error constructing obj pseudo-op table");
+ } /* if overridden */
+ } /* on error */
+ } /* for each op */
+
+ return;
+} /* pobegin() */
+
+#define HANDLE_CONDITIONAL_ASSEMBLY() \
+ if (ignore_input ()) \
+{ \
+ while (! is_end_of_line[*input_line_pointer++]) \
+ if (input_line_pointer == buffer_limit) \
+ break; \
+ continue; \
+ }
+
+
+/* read_a_source_file()
+ *
+ * We read the file, putting things into a web that
+ * represents what we have been reading.
+ */
+void read_a_source_file(name)
+char *name;
+{
+ register char c;
+ register char * s; /* string of symbol, '\0' appended */
+ register int temp;
+ pseudo_typeS *pop = NULL;
+
+ buffer = input_scrub_new_file(name);
+
+ listing_file(name);
+ listing_newline("");
+
+ while ((buffer_limit = input_scrub_next_buffer(&input_line_pointer)) != 0) { /* We have another line to parse. */
+ know(buffer_limit[-1] == '\n'); /* Must have a sentinel. */
+ contin: /* JF this goto is my fault I admit it. Someone brave please re-write
+ the whole input section here? Pleeze??? */
+ while (input_line_pointer < buffer_limit) { /* We have more of this buffer to parse. */
+
+ /*
+ * We now have input_line_pointer->1st char of next line.
+ * If input_line_pointer[-1] == '\n' then we just
+ * scanned another line: so bump line counters.
+ */
+ if (input_line_pointer[-1] == '\n') {
+ bump_line_counters();
+ } /* just passed a newline */
+
+
+
+ /*
+ * We are at the begining of a line, or similar place.
+ * We expect a well-formed assembler statement.
+ * A "symbol-name:" is a statement.
+ *
+ * Depending on what compiler is used, the order of these tests
+ * may vary to catch most common case 1st.
+ * Each test is independent of all other tests at the (top) level.
+ * PLEASE make a compiler that doesn't use this assembler.
+ * It is crufty to waste a compiler's time encoding things for this
+ * assembler, which then wastes more time decoding it.
+ * (And communicating via (linear) files is silly!
+ * If you must pass stuff, please pass a tree!)
+ */
+ if ((c = *input_line_pointer++) == '\t' || c == ' ' || c == '\f' || c == 0) {
+ c = *input_line_pointer++;
+ }
+ know(c != ' '); /* No further leading whitespace. */
+ LISTING_NEWLINE();
+ /*
+ * C is the 1st significant character.
+ * Input_line_pointer points after that character.
+ */
+ if (is_name_beginner(c)) { /* want user-defined label or pseudo/opcode */
+ HANDLE_CONDITIONAL_ASSEMBLY();
+
+ s = input_line_pointer - 1;
+ c = get_symbol_end(); /* name's delimiter */
+ /*
+ * C is character after symbol.
+ * That character's place in the input line is now '\0'.
+ * S points to the beginning of the symbol.
+ * [In case of pseudo-op, s->'.'.]
+ * Input_line_pointer->'\0' where c was.
+ */
+ if (c == ':') {
+ colon(s); /* user-defined label */
+ * input_line_pointer ++ = ':'; /* Put ':' back for error messages' sake. */
+ /* Input_line_pointer->after ':'. */
+ SKIP_WHITESPACE();
+
+
+ } else if (c == '=' || input_line_pointer[1] == '=') { /* JF deal with FOO=BAR */
+ equals(s);
+ demand_empty_rest_of_line();
+ } else { /* expect pseudo-op or machine instruction */
+ if (*s == '.'
+#ifdef NO_DOT_PSEUDOS
+ || (pop= (pseudo_typeS *) hash_find(po_hash, s))
+#endif
+ ) {
+ /*
+ * PSEUDO - OP.
+ *
+ * WARNING: c has next char, which may be end-of-line.
+ * We lookup the pseudo-op table with s+1 because we
+ * already know that the pseudo-op begins with a '.'.
+ */
+
+#ifdef NO_DOT_PSEUDOS
+ if (*s == '.')
+#endif
+ pop = (pseudo_typeS *) hash_find(po_hash, s+1);
+
+ /* Print the error msg now, while we still can */
+ if (!pop) {
+ as_bad("Unknown pseudo-op: `%s'",s);
+ *input_line_pointer = c;
+ s_ignore(0);
+ break;
+ }
+
+ /* Put it back for error messages etc. */
+ *input_line_pointer = c;
+ /* The following skip of whitespace is compulsory. */
+ /* A well shaped space is sometimes all that separates keyword from operands. */
+ if (c == ' ' || c == '\t') {
+ input_line_pointer++;
+ } /* Skip seperator after keyword. */
+ /*
+ * Input_line is restored.
+ * Input_line_pointer->1st non-blank char
+ * after pseudo-operation.
+ */
+ if (!pop) {
+ ignore_rest_of_line();
+ break;
+ } else {
+ (*pop->poc_handler)(pop->poc_val);
+ } /* if we have one */
+ } else { /* machine instruction */
+ /* WARNING: c has char, which may be end-of-line. */
+ /* Also: input_line_pointer->`\0` where c was. */
+ * input_line_pointer = c;
+ while (!is_end_of_line[*input_line_pointer]) {
+ input_line_pointer++;
+ }
+ c = *input_line_pointer;
+ *input_line_pointer = '\0';
+ md_assemble(s); /* Assemble 1 instruction. */
+ *input_line_pointer++ = c;
+ /* We resume loop AFTER the end-of-line from this instruction */
+ } /* if (*s == '.') */
+
+ } /* if c == ':' */
+ continue;
+ } /* if (is_name_beginner(c) */
+
+
+ if (is_end_of_line[c]) {
+ continue;
+ } /* empty statement */
+
+
+ if (isdigit(c)) { /* local label ("4:") */
+ HANDLE_CONDITIONAL_ASSEMBLY ();
+
+ temp = c - '0';
+#ifdef LOCAL_LABELS_DOLLAR
+ if (*input_line_pointer == '$')
+ input_line_pointer++;
+#endif
+ if (*input_line_pointer++ == ':') {
+ local_colon (temp);
+ } else {
+ as_bad("Spurious digit %d.", temp);
+ input_line_pointer -- ;
+ ignore_rest_of_line();
+ }
+ continue;
+ } /* local label ("4:") */
+
+ if (c && strchr(line_comment_chars, c)) { /* Its a comment. Better say APP or NO_APP */
+ char *ends;
+ char *new_buf;
+ char *new_tmp;
+ int new_length;
+ char *tmp_buf = 0;
+ extern char *scrub_string, *scrub_last_string;
+
+ bump_line_counters();
+ s = input_line_pointer;
+ if (strncmp(s,"APP\n",4))
+ continue; /* We ignore it */
+ s += 4;
+
+ ends = strstr(s,"#NO_APP\n");
+
+ if (!ends) {
+ int tmp_len;
+ int num;
+
+ /* The end of the #APP wasn't in this buffer. We
+ keep reading in buffers until we find the #NO_APP
+ that goes with this #APP There is one. The specs
+ guarentee it... */
+ tmp_len = buffer_limit - s;
+ tmp_buf = xmalloc(tmp_len);
+ memcpy(tmp_buf, s, tmp_len);
+ do {
+ new_tmp = input_scrub_next_buffer(&buffer);
+ if (!new_tmp)
+ break;
+ else
+ buffer_limit = new_tmp;
+ input_line_pointer = buffer;
+ ends = strstr(buffer,"#NO_APP\n");
+ if (ends)
+ num = ends - buffer;
+ else
+ num = buffer_limit - buffer;
+
+ tmp_buf = xrealloc(tmp_buf, tmp_len + num);
+ memcpy(tmp_buf + tmp_len, buffer, num);
+ tmp_len += num;
+ } while (!ends);
+
+ input_line_pointer = ends ? ends + 8 : NULL;
+
+ s = tmp_buf;
+ ends = s + tmp_len;
+
+ } else {
+ input_line_pointer = ends + 8;
+ }
+ new_buf=xmalloc(100);
+ new_length=100;
+ new_tmp=new_buf;
+
+ scrub_string = s;
+ scrub_last_string = ends;
+ for (;;) {
+ int ch;
+
+ ch = do_scrub_next_char(scrub_from_string, scrub_to_string);
+ if (ch == EOF) break;
+ *new_tmp++ = ch;
+ if (new_tmp == new_buf + new_length) {
+ new_buf = xrealloc(new_buf, new_length + 100);
+ new_tmp = new_buf + new_length;
+ new_length += 100;
+ }
+ }
+
+ if (tmp_buf)
+ free(tmp_buf);
+ old_buffer = buffer;
+ old_input = input_line_pointer;
+ old_limit = buffer_limit;
+ buffer = new_buf;
+ input_line_pointer = new_buf;
+ buffer_limit = new_tmp;
+ continue;
+ }
+
+ HANDLE_CONDITIONAL_ASSEMBLY();
+
+ /* as_warn("Junk character %d.",c); Now done by ignore_rest */
+ input_line_pointer--; /* Report unknown char as ignored. */
+ ignore_rest_of_line();
+ } /* while (input_line_pointer<buffer_limit) */
+ if (old_buffer) {
+ bump_line_counters();
+ if (old_input != 0) {
+ buffer=old_buffer;
+ input_line_pointer=old_input;
+ buffer_limit=old_limit;
+ old_buffer = 0;
+ goto contin;
+ }
+ }
+ } /* while (more buffers to scan) */
+ input_scrub_close(); /* Close the input file */
+
+} /* read_a_source_file() */
+
+void s_abort() {
+ as_fatal(".abort detected. Abandoning ship.");
+} /* s_abort() */
+
+/* For machines where ".align 4" means align to a 4 byte boundary. */
+void s_align_bytes(arg)
+int arg;
+{
+ register unsigned int temp;
+ register long temp_fill;
+ unsigned int i = 0;
+ unsigned long max_alignment = 1 << 15;
+
+ if (is_end_of_line[*input_line_pointer])
+ temp = arg; /* Default value from pseudo-op table */
+ else
+ temp = get_absolute_expression();
+
+ if (temp > max_alignment) {
+ as_bad("Alignment too large: %d. assumed.", temp = max_alignment);
+ }
+
+ /*
+ * For the sparc, `.align (1<<n)' actually means `.align n'
+ * so we have to convert it.
+ */
+ if (temp != 0) {
+ for (i = 0; (temp & 1) == 0; temp >>= 1, ++i)
+ ;
+ }
+ if (temp != 1)
+ as_bad("Alignment not a power of 2");
+
+ temp = i;
+ if (*input_line_pointer == ',') {
+ input_line_pointer ++;
+ temp_fill = get_absolute_expression();
+ } else {
+ temp_fill = NOP_OPCODE;
+ }
+ /* Only make a frag if we HAVE to... */
+ if (temp && ! need_pass_2)
+ frag_align(temp, (int)temp_fill);
+
+ demand_empty_rest_of_line();
+} /* s_align_bytes() */
+
+/* For machines where ".align 4" means align to 2**4 boundary. */
+void s_align_ptwo() {
+ register int temp;
+ register long temp_fill;
+ long max_alignment = 15;
+
+ temp = get_absolute_expression();
+ if (temp > max_alignment)
+ as_bad("Alignment too large: %d. assumed.", temp = max_alignment);
+ else if (temp < 0) {
+ as_bad("Alignment negative. 0 assumed.");
+ temp = 0;
+ }
+ if (*input_line_pointer == ',') {
+ input_line_pointer ++;
+ temp_fill = get_absolute_expression();
+ } else
+ temp_fill = NOP_OPCODE;
+ /* Only make a frag if we HAVE to... */
+ if (temp && ! need_pass_2)
+ frag_align (temp, (int)temp_fill);
+
+ record_alignment(now_seg, temp);
+
+ demand_empty_rest_of_line();
+} /* s_align_ptwo() */
+
+void s_comm() {
+ register char *name;
+ register char c;
+ register char *p;
+ register int temp;
+ register symbolS *symbolP;
+
+ name = input_line_pointer;
+ c = get_symbol_end();
+ /* just after name is now '\0' */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE();
+ if (*input_line_pointer != ',') {
+ as_bad("Expected comma after symbol-name: rest of line ignored.");
+ ignore_rest_of_line();
+ return;
+ }
+ input_line_pointer ++; /* skip ',' */
+ if ((temp = get_absolute_expression()) < 0) {
+ as_warn(".COMMon length (%d.) <0! Ignored.", temp);
+ ignore_rest_of_line();
+ return;
+ }
+ *p = 0;
+ symbolP = symbol_find_or_make(name);
+ *p = c;
+ if (S_IS_DEFINED(symbolP)) {
+ as_bad("Ignoring attempt to re-define symbol");
+ ignore_rest_of_line();
+ return;
+ }
+ if (S_GET_VALUE(symbolP)) {
+ if (S_GET_VALUE(symbolP) != temp)
+ as_bad("Length of .comm \"%s\" is already %d. Not changed to %d.",
+ S_GET_NAME(symbolP),
+ S_GET_VALUE(symbolP),
+ temp);
+ } else {
+ S_SET_VALUE(symbolP, temp);
+ S_SET_EXTERNAL(symbolP);
+ }
+#ifdef OBJ_VMS
+ if ( (!temp) || !flagseen['1'])
+ S_GET_OTHER(symbolP) = const_flag;
+#endif /* not OBJ_VMS */
+ know(symbolP->sy_frag == &zero_address_frag);
+ demand_empty_rest_of_line();
+} /* s_comm() */
+
+void
+ s_data()
+{
+ register int temp;
+
+ temp = get_absolute_expression();
+#ifdef MANY_SEGMENTS
+ subseg_new (SEG_E1, (subsegT)temp);
+#else
+ subseg_new (SEG_DATA, (subsegT)temp);
+#endif
+
+#ifdef OBJ_VMS
+ const_flag = 0;
+#endif /* not OBJ_VMS */
+ demand_empty_rest_of_line();
+}
+
+void s_app_file() {
+ register char *s;
+ int length;
+
+ /* Some assemblers tolerate immediately following '"' */
+ if ((s = demand_copy_string(&length)) != 0) {
+ new_logical_line(s, -1);
+ demand_empty_rest_of_line();
+ }
+#ifdef OBJ_COFF
+ c_dot_file_symbol(s);
+#endif /* OBJ_COFF */
+} /* s_app_file() */
+
+void s_fill() {
+ long temp_repeat;
+ long temp_size;
+ register long temp_fill;
+ char *p;
+
+ if (get_absolute_expression_and_terminator(& temp_repeat) != ',') {
+ input_line_pointer --; /* Backup over what was not a ','. */
+ as_bad("Expect comma after rep-size in .fill:");
+ ignore_rest_of_line();
+ return;
+ }
+ if (get_absolute_expression_and_terminator(& temp_size) != ',') {
+ input_line_pointer --; /* Backup over what was not a ','. */
+ as_bad("Expected comma after size in .fill");
+ ignore_rest_of_line();
+ return;
+ }
+ /*
+ * This is to be compatible with BSD 4.2 AS, not for any rational reason.
+ */
+#define BSD_FILL_SIZE_CROCK_8 (8)
+ if (temp_size > BSD_FILL_SIZE_CROCK_8) {
+ as_bad(".fill size clamped to %d.", BSD_FILL_SIZE_CROCK_8);
+ temp_size = BSD_FILL_SIZE_CROCK_8 ;
+ } if (temp_size < 0) {
+ as_warn("Size negative: .fill ignored.");
+ temp_size = 0;
+ } else if (temp_repeat <= 0) {
+ as_warn("Repeat < 0, .fill ignored");
+ temp_size = 0;
+ }
+ temp_fill = get_absolute_expression();
+ if (temp_size && !need_pass_2) {
+ p = frag_var(rs_fill, (int)temp_size, (int)temp_size, (relax_substateT)0, (symbolS *)0, temp_repeat, (char *)0);
+ memset(p, '\0', (int) temp_size);
+ /*
+ * The magic number BSD_FILL_SIZE_CROCK_4 is from BSD 4.2 VAX flavoured AS.
+ * The following bizzare behaviour is to be compatible with above.
+ * I guess they tried to take up to 8 bytes from a 4-byte expression
+ * and they forgot to sign extend. Un*x Sux.
+ */
+#define BSD_FILL_SIZE_CROCK_4 (4)
+ md_number_to_chars (p, temp_fill, temp_size > BSD_FILL_SIZE_CROCK_4 ? BSD_FILL_SIZE_CROCK_4 : (int)temp_size);
+ /*
+ * Note: .fill (),0 emits no frag (since we are asked to .fill 0 bytes)
+ * but emits no error message because it seems a legal thing to do.
+ * It is a degenerate case of .fill but could be emitted by a compiler.
+ */
+ }
+ demand_empty_rest_of_line();
+}
+
+void s_globl() {
+ register char *name;
+ register int c;
+ register symbolS * symbolP;
+
+ do {
+ name = input_line_pointer;
+ c = get_symbol_end();
+ symbolP = symbol_find_or_make(name);
+ * input_line_pointer = c;
+ SKIP_WHITESPACE();
+ S_SET_EXTERNAL(symbolP);
+ if (c == ',') {
+ input_line_pointer++;
+ SKIP_WHITESPACE();
+ if (*input_line_pointer == '\n')
+ c='\n';
+ }
+ } while (c == ',');
+ demand_empty_rest_of_line();
+} /* s_globl() */
+
+void s_lcomm(needs_align)
+int needs_align; /* 1 if this was a ".bss" directive, which may require
+ * a 3rd argument (alignment).
+ * 0 if it was an ".lcomm" (2 args only)
+ */
+{
+ register char *name;
+ register char c;
+ register char *p;
+ register int temp;
+ register symbolS * symbolP;
+ const int max_alignment = 15;
+ int align = 0;
+
+ name = input_line_pointer;
+ c = get_symbol_end();
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE();
+ if (*input_line_pointer != ',') {
+ as_bad("Expected comma after name");
+ ignore_rest_of_line();
+ return;
+ }
+
+ ++input_line_pointer;
+
+ if (*input_line_pointer == '\n') {
+ as_bad("Missing size expression");
+ return;
+ }
+
+ if ((temp = get_absolute_expression()) < 0) {
+ as_warn("BSS length (%d.) <0! Ignored.", temp);
+ ignore_rest_of_line();
+ return;
+ }
+
+ if (needs_align) {
+ align = 0;
+ SKIP_WHITESPACE();
+ if (*input_line_pointer != ',') {
+ as_bad("Expected comma after size");
+ ignore_rest_of_line();
+ return;
+ }
+ input_line_pointer++;
+ SKIP_WHITESPACE();
+ if (*input_line_pointer == '\n') {
+ as_bad("Missing alignment");
+ return;
+ }
+ align = get_absolute_expression();
+ if (align > max_alignment){
+ align = max_alignment;
+ as_warn("Alignment too large: %d. assumed.", align);
+ } else if (align < 0) {
+ align = 0;
+ as_warn("Alignment negative. 0 assumed.");
+ }
+#ifdef MANY_SEGMENTS
+#define SEG_BSS SEG_E2
+ record_alignment(SEG_E2, align);
+#else
+ record_alignment(SEG_BSS, align);
+#endif
+ } /* if needs align */
+
+ *p = 0;
+ symbolP = symbol_find_or_make(name);
+ *p = c;
+
+ if (
+#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
+ S_GET_OTHER(symbolP) == 0 &&
+ S_GET_DESC(symbolP) == 0 &&
+#endif /* OBJ_AOUT or OBJ_BOUT */
+ (((S_GET_SEGMENT(symbolP) == SEG_BSS) && (S_GET_VALUE(symbolP) == local_bss_counter))
+ || (!S_IS_DEFINED(symbolP) && S_GET_VALUE(symbolP) == 0))) {
+ if (needs_align){
+ /* Align */
+ align = ~ ((~0) << align); /* Convert to a mask */
+ local_bss_counter =
+ (local_bss_counter + align) & (~align);
+ }
+
+ S_SET_VALUE(symbolP, local_bss_counter);
+ S_SET_SEGMENT(symbolP, SEG_BSS);
+#ifdef OBJ_COFF
+ /* The symbol may already have been created with a preceding
+ * ".globl" directive -- be careful not to step on storage
+ * class in that case. Otherwise, set it to static.
+ */
+ if (S_GET_STORAGE_CLASS(symbolP) != C_EXT){
+ S_SET_STORAGE_CLASS(symbolP, C_STAT);
+ }
+#endif /* OBJ_COFF */
+ symbolP->sy_frag = &bss_address_frag;
+ local_bss_counter += temp;
+ } else {
+ as_bad("Ignoring attempt to re-define symbol from %d. to %d.",
+ S_GET_VALUE(symbolP), local_bss_counter);
+ }
+ demand_empty_rest_of_line();
+
+ return;
+} /* s_lcomm() */
+
+void
+ s_long()
+{
+ cons(4);
+}
+
+void
+ s_int()
+{
+ cons(4);
+}
+
+void s_lsym() {
+ register char *name;
+ register char c;
+ register char *p;
+ register segT segment;
+ expressionS exp;
+ register symbolS *symbolP;
+
+ /* we permit ANY defined expression: BSD4.2 demands constants */
+ name = input_line_pointer;
+ c = get_symbol_end();
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE();
+ if (* input_line_pointer != ',') {
+ *p = 0;
+ as_bad("Expected comma after name \"%s\"", name);
+ *p = c;
+ ignore_rest_of_line();
+ return;
+ }
+ input_line_pointer ++;
+ segment = expression(& exp);
+ if (segment != SEG_ABSOLUTE
+#ifdef MANY_SEGMENTS
+ && ! ( segment >= SEG_E0 && segment <= SEG_UNKNOWN)
+#else
+ && segment != SEG_DATA
+ && segment != SEG_TEXT
+ && segment != SEG_BSS
+#endif
+ && segment != SEG_REGISTER) {
+ as_bad("Bad expression: %s", segment_name(segment));
+ ignore_rest_of_line();
+ return;
+ }
+ *p = 0;
+ symbolP = symbol_find_or_make(name);
+
+ /* FIXME-SOON I pulled a (&& symbolP->sy_other == 0
+ && symbolP->sy_desc == 0) out of this test
+ because coff doesn't have those fields, and I
+ can't see when they'd ever be tripped. I don't
+ think I understand why they were here so I may
+ have introduced a bug. As recently as 1.37 didn't
+ have this test anyway. xoxorich. */
+
+ if (S_GET_SEGMENT(symbolP) == SEG_UNKNOWN
+ && S_GET_VALUE(symbolP) == 0) {
+ /* The name might be an undefined .global symbol; be
+ sure to keep the "external" bit. */
+ S_SET_SEGMENT(symbolP, segment);
+ S_SET_VALUE(symbolP, (valueT)(exp.X_add_number));
+ } else {
+ as_bad("Symbol %s already defined", name);
+ }
+ *p = c;
+ demand_empty_rest_of_line();
+} /* s_lsym() */
+
+void s_org() {
+ register segT segment;
+ expressionS exp;
+ register long temp_fill;
+ register char *p;
+ /*
+ * Don't believe the documentation of BSD 4.2 AS.
+ * There is no such thing as a sub-segment-relative origin.
+ * Any absolute origin is given a warning, then assumed to be segment-relative.
+ * Any segmented origin expression ("foo+42") had better be in the right
+ * segment or the .org is ignored.
+ *
+ * BSD 4.2 AS warns if you try to .org backwards. We cannot because we
+ * never know sub-segment sizes when we are reading code.
+ * BSD will crash trying to emit -ve numbers of filler bytes in certain
+ * .orgs. We don't crash, but see as-write for that code.
+ */
+ /*
+ * Don't make frag if need_pass_2 == 1.
+ */
+ segment = get_known_segmented_expression(&exp);
+ if (*input_line_pointer == ',') {
+ input_line_pointer ++;
+ temp_fill = get_absolute_expression();
+ } else
+ temp_fill = 0;
+ if (! need_pass_2) {
+ if (segment != now_seg && segment != SEG_ABSOLUTE)
+ as_bad("Invalid segment \"%s\". Segment \"%s\" assumed.",
+ segment_name(segment), segment_name(now_seg));
+ p = frag_var (rs_org, 1, 1, (relax_substateT)0, exp.X_add_symbol,
+ exp.X_add_number, (char *)0);
+ * p = temp_fill;
+ } /* if (ok to make frag) */
+ demand_empty_rest_of_line();
+} /* s_org() */
+
+void s_set() {
+ register char *name;
+ register char delim;
+ register char *end_name;
+ register symbolS *symbolP;
+
+ /*
+ * Especial apologies for the random logic:
+ * this just grew, and could be parsed much more simply!
+ * Dean in haste.
+ */
+ name = input_line_pointer;
+ delim = get_symbol_end();
+ end_name = input_line_pointer;
+ *end_name = delim;
+ SKIP_WHITESPACE();
+
+ if (*input_line_pointer != ',') {
+ *end_name = 0;
+ as_bad("Expected comma after name \"%s\"", name);
+ *end_name = delim;
+ ignore_rest_of_line();
+ return;
+ }
+
+ input_line_pointer ++;
+ *end_name = 0;
+
+ if (name[0] == '.' && name[1] == '\0') {
+ /* Turn '. = mumble' into a .org mumble */
+ register segT segment;
+ expressionS exp;
+ register char *ptr;
+
+ segment = get_known_segmented_expression(& exp);
+
+ if (!need_pass_2) {
+ if (segment != now_seg && segment != SEG_ABSOLUTE)
+ as_bad("Invalid segment \"%s\". Segment \"%s\" assumed.",
+ segment_name(segment),
+ segment_name (now_seg));
+ ptr = frag_var(rs_org, 1, 1, (relax_substateT)0, exp.X_add_symbol,
+ exp.X_add_number, (char *)0);
+ *ptr= 0;
+ } /* if (ok to make frag) */
+
+ *end_name = delim;
+ return;
+ }
+
+ if ((symbolP = symbol_find(name)) == NULL
+ && (symbolP = md_undefined_symbol(name)) == NULL) {
+ symbolP = symbol_new(name,
+ SEG_UNKNOWN,
+ 0,
+ &zero_address_frag);
+#ifdef OBJ_COFF
+ /* "set" symbols are local unless otherwise specified. */
+ SF_SET_LOCAL(symbolP);
+#endif /* OBJ_COFF */
+
+ } /* make a new symbol */
+
+ symbol_table_insert(symbolP);
+
+ *end_name = delim;
+ pseudo_set(symbolP);
+ demand_empty_rest_of_line();
+} /* s_set() */
+
+void s_size() {
+ register char *name;
+ register char c;
+ register char *p;
+ register int temp;
+ register symbolS *symbolP;
+ expressionS *exp;
+ segT seg;
+
+ SKIP_WHITESPACE();
+ name = input_line_pointer;
+ c = get_symbol_end();
+ /* just after name is now '\0' */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE();
+ if (*input_line_pointer != ',') {
+ as_bad("Expected comma after symbol-name: rest of line ignored.");
+ ignore_rest_of_line();
+ return;
+ }
+ input_line_pointer ++; /* skip ',' */
+ if ((exp = (expressionS *)malloc(sizeof(expressionS))) == NULL) {
+ as_bad("Virtual memory exhausted");
+ return;
+ }
+ switch (get_known_segmented_expression(exp)) {
+ case SEG_ABSOLUTE:
+ break;
+ case SEG_DIFFERENCE:
+ if (exp->X_add_symbol == NULL || exp->X_subtract_symbol == NULL
+ || S_GET_SEGMENT(exp->X_add_symbol) !=
+ S_GET_SEGMENT(exp->X_subtract_symbol)) {
+ as_bad("Illegal .size expression");
+ ignore_rest_of_line();
+ return;
+ }
+ break;
+ default:
+ as_bad("Illegal .size expression");
+ ignore_rest_of_line();
+ return;
+ }
+ *p = 0;
+ symbolP = symbol_find_or_make(name);
+ *p = c;
+ if (symbolP->sy_sizexp) {
+ as_warn("\"%s\" already has a size", S_GET_NAME(symbolP));
+ } else
+ symbolP->sy_sizexp = (void *)exp;
+
+ demand_empty_rest_of_line();
+} /* s_size() */
+
+void s_type() {
+ register char *name, *type;
+ register char c, c1;
+ register char *p;
+ register symbolS *symbolP;
+ int aux;
+
+ SKIP_WHITESPACE();
+ name = input_line_pointer;
+ c = get_symbol_end();
+ /* just after name is now '\0' */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE();
+ if (*input_line_pointer != ',') {
+ as_bad("Expected comma after symbol-name: rest of line ignored.");
+ ignore_rest_of_line();
+ return;
+ }
+ input_line_pointer ++; /* skip ',' */
+ SKIP_WHITESPACE();
+ if (*input_line_pointer != TYPE_OPERAND_FMT) {
+ as_bad("Expected `%c' as start of operand: rest of line ignored.", TYPE_OPERAND_FMT);
+ ignore_rest_of_line();
+ return;
+ }
+ input_line_pointer ++; /* skip '@' */
+ type = input_line_pointer;
+ c1 = get_symbol_end();
+ if (strcmp(type, "function") == 0) {
+ aux = AUX_FUNC;
+ } else if (strcmp(type, "object") == 0) {
+ aux = AUX_OBJECT;
+ } else {
+ as_warn("Unrecognized .type operand: \"%s\": rest of line ignored.",
+ type);
+ ignore_rest_of_line();
+ return;
+ }
+ *input_line_pointer = c1;
+
+ *p = 0;
+ symbolP = symbol_find_or_make(name);
+ *p = c;
+
+ if (symbolP->sy_aux && symbolP->sy_aux != aux) {
+ as_bad("Type of \"%s\" is already %d. Not changed to %d.",
+ S_GET_NAME(symbolP), symbolP->sy_aux, aux);
+ } else
+ symbolP->sy_aux = aux;
+
+ demand_empty_rest_of_line();
+} /* s_type() */
+
+void s_space() {
+ long temp_repeat;
+ register long temp_fill;
+ register char *p;
+
+ /* Just like .fill, but temp_size = 1 */
+ if (get_absolute_expression_and_terminator(& temp_repeat) == ',') {
+ temp_fill = get_absolute_expression();
+ } else {
+ input_line_pointer --; /* Backup over what was not a ','. */
+ temp_fill = 0;
+ }
+ if (temp_repeat <= 0) {
+ as_warn("Repeat < 0, .space ignored");
+ ignore_rest_of_line();
+ return;
+ }
+ if (! need_pass_2) {
+ p = frag_var (rs_fill, 1, 1, (relax_substateT)0, (symbolS *)0,
+ temp_repeat, (char *)0);
+ * p = temp_fill;
+ }
+ demand_empty_rest_of_line();
+} /* s_space() */
+
+void
+ s_text()
+{
+ register int temp;
+
+ temp = get_absolute_expression();
+#ifdef MANY_SEGMENTS
+ subseg_new (SEG_E0, (subsegT)temp);
+#else
+ subseg_new (SEG_TEXT, (subsegT)temp);
+#endif
+ demand_empty_rest_of_line();
+} /* s_text() */
+
+
+/*(JF was static, but can't be if machine dependent pseudo-ops are to use it */
+
+void demand_empty_rest_of_line() {
+ SKIP_WHITESPACE();
+ if (is_end_of_line[*input_line_pointer]) {
+ input_line_pointer++;
+ } else {
+ ignore_rest_of_line();
+ }
+ /* Return having already swallowed end-of-line. */
+} /* Return pointing just after end-of-line. */
+
+void
+ ignore_rest_of_line() /* For suspect lines: gives warning. */
+{
+ if (!is_end_of_line[*input_line_pointer])
+ {
+ if (isprint(*input_line_pointer))
+ as_bad("Rest of line ignored. First ignored character is `%c'.",
+ *input_line_pointer);
+ else
+ as_bad("Rest of line ignored. First ignored character valued 0x%x.",
+ *input_line_pointer);
+ while (input_line_pointer < buffer_limit
+ && !is_end_of_line[*input_line_pointer])
+ {
+ input_line_pointer ++;
+ }
+ }
+ input_line_pointer ++; /* Return pointing just after end-of-line. */
+ know(is_end_of_line[input_line_pointer[-1]]);
+}
+
+/*
+ * pseudo_set()
+ *
+ * In: Pointer to a symbol.
+ * Input_line_pointer->expression.
+ *
+ * Out: Input_line_pointer->just after any whitespace after expression.
+ * Tried to set symbol to value of expression.
+ * Will change symbols type, value, and frag;
+ * May set need_pass_2 == 1.
+ */
+void
+ pseudo_set (symbolP)
+symbolS * symbolP;
+{
+ expressionS exp;
+ register segT segment;
+#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
+ int ext;
+#endif /* OBJ_AOUT or OBJ_BOUT */
+
+ know(symbolP); /* NULL pointer is logic error. */
+#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
+ ext=S_IS_EXTERNAL(symbolP);
+#endif /* OBJ_AOUT or OBJ_BOUT */
+
+ if ((segment = expression(& exp)) == SEG_ABSENT)
+ {
+ as_bad("Missing expression: absolute 0 assumed");
+ exp.X_seg = SEG_ABSOLUTE;
+ exp.X_add_number = 0;
+ }
+
+ switch (segment)
+ {
+ case SEG_BIG:
+ as_bad("%s number invalid. Absolute 0 assumed.",
+ exp.X_add_number > 0 ? "Bignum" : "Floating-Point");
+ S_SET_SEGMENT(symbolP, SEG_ABSOLUTE);
+#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
+ ext ? S_SET_EXTERNAL(symbolP) :
+ S_CLEAR_EXTERNAL(symbolP);
+#endif /* OBJ_AOUT or OBJ_BOUT */
+ S_SET_VALUE(symbolP, 0);
+ symbolP->sy_frag = & zero_address_frag;
+ break;
+
+ case SEG_ABSENT:
+ as_warn("No expression: Using absolute 0");
+ S_SET_SEGMENT(symbolP, SEG_ABSOLUTE);
+#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
+ ext ? S_SET_EXTERNAL(symbolP) :
+ S_CLEAR_EXTERNAL(symbolP);
+#endif /* OBJ_AOUT or OBJ_BOUT */
+ S_SET_VALUE(symbolP, 0);
+ symbolP->sy_frag = & zero_address_frag;
+ break;
+
+ case SEG_DIFFERENCE:
+ if (exp.X_add_symbol && exp.X_subtract_symbol
+ && (S_GET_SEGMENT(exp.X_add_symbol) ==
+ S_GET_SEGMENT(exp.X_subtract_symbol))) {
+ if (exp.X_add_symbol->sy_frag != exp.X_subtract_symbol->sy_frag) {
+ as_bad("Unknown expression: symbols %s and %s are in different frags.",
+ S_GET_NAME(exp.X_add_symbol), S_GET_NAME(exp.X_subtract_symbol));
+ need_pass_2++;
+ }
+ exp.X_add_number+=S_GET_VALUE(exp.X_add_symbol) -
+ S_GET_VALUE(exp.X_subtract_symbol);
+ } else
+ as_bad("Complex expression. Absolute segment assumed.");
+ case SEG_ABSOLUTE:
+ S_SET_SEGMENT(symbolP, SEG_ABSOLUTE);
+#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
+ ext ? S_SET_EXTERNAL(symbolP) :
+ S_CLEAR_EXTERNAL(symbolP);
+#endif /* OBJ_AOUT or OBJ_BOUT */
+ S_SET_VALUE(symbolP, exp.X_add_number);
+ symbolP->sy_frag = & zero_address_frag;
+ break;
+
+ default:
+#ifdef MANY_SEGMENTS
+ S_SET_SEGMENT(symbolP, segment);
+#else
+ switch (segment) {
+ case SEG_DATA: S_SET_SEGMENT(symbolP, SEG_DATA); break;
+ case SEG_TEXT: S_SET_SEGMENT(symbolP, SEG_TEXT); break;
+ case SEG_BSS: S_SET_SEGMENT(symbolP, SEG_BSS); break;
+ default: as_fatal("failed sanity check.");
+ } /* switch on segment */
+#endif
+#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
+ if (ext) {
+ S_SET_EXTERNAL(symbolP);
+ } else {
+ S_CLEAR_EXTERNAL(symbolP);
+ } /* if external */
+#endif /* OBJ_AOUT or OBJ_BOUT */
+
+ S_SET_VALUE(symbolP, exp.X_add_number + S_GET_VALUE(exp.X_add_symbol));
+ symbolP->sy_frag = exp.X_add_symbol->sy_frag;
+ break;
+
+ case SEG_PASS1: /* Not an error. Just try another pass. */
+ symbolP->sy_forward=exp.X_add_symbol;
+ as_bad("Unknown expression");
+ know(need_pass_2 == 1);
+ break;
+
+ case SEG_UNKNOWN:
+ symbolP->sy_forward=exp.X_add_symbol;
+ /* as_warn("unknown symbol"); */
+ /* need_pass_2 = 1; */
+ break;
+
+
+
+ }
+}
+
+/*
+ * cons()
+ *
+ * CONStruct more frag of .bytes, or .words etc.
+ * Should need_pass_2 be 1 then emit no frag(s).
+ * This understands EXPRESSIONS, as opposed to big_cons().
+ *
+ * Bug (?)
+ *
+ * This has a split personality. We use expression() to read the
+ * value. We can detect if the value won't fit in a byte or word.
+ * But we can't detect if expression() discarded significant digits
+ * in the case of a long. Not worth the crocks required to fix it.
+ */
+
+/* worker to do .byte etc statements */
+/* clobbers input_line_pointer, checks */
+/* end-of-line. */
+void cons(nbytes)
+register unsigned int nbytes; /* 1=.byte, 2=.word, 4=.long */
+{
+ register char c;
+ register long mask; /* High-order bits we will left-truncate, */
+ /* but includes sign bit also. */
+ register long get; /* what we get */
+ register long use; /* get after truncation. */
+ register long unmask; /* what bits we will store */
+ register char * p;
+ register segT segment;
+ expressionS exp;
+
+ /*
+ * Input_line_pointer->1st char after pseudo-op-code and could legally
+ * be a end-of-line. (Or, less legally an eof - which we cope with.)
+ */
+ /* JF << of >= number of bits in the object is undefined. In particular
+ SPARC (Sun 4) has problems */
+
+ if (nbytes >= sizeof(long)) {
+ mask = 0;
+ } else {
+ mask = ~0 << (BITS_PER_CHAR * nbytes); /* Don't store these bits. */
+ } /* bigger than a long */
+
+ unmask = ~mask; /* Do store these bits. */
+
+#ifdef NEVER
+ "Do this mod if you want every overflow check to assume SIGNED 2's complement data.";
+ mask = ~ (unmask >> 1); /* Includes sign bit now. */
+#endif
+
+ /*
+ * The following awkward logic is to parse ZERO or more expressions,
+ * comma seperated. Recall an expression includes its leading &
+ * trailing blanks. We fake a leading ',' if there is (supposed to
+ * be) a 1st expression, and keep demanding 1 expression for each ','.
+ */
+ if (is_it_end_of_statement()) {
+ c = 0; /* Skip loop. */
+ input_line_pointer++; /* Matches end-of-loop 'correction'. */
+ } else {
+ c = ',';
+ } /* if the end else fake it */
+
+ /* Do loop. */
+ while (c == ',') {
+#ifdef WANT_BITFIELDS
+ unsigned int bits_available = BITS_PER_CHAR * nbytes;
+ /* used for error messages and rescanning */
+ char *hold = input_line_pointer;
+#endif /* WANT_BITFIELDS */
+
+ /* At least scan over the expression. */
+ segment = expression(&exp);
+
+#ifdef WANT_BITFIELDS
+ /* Some other assemblers, (eg, asm960), allow
+ bitfields after ".byte" as w:x,y:z, where w and
+ y are bitwidths and x and y are values. They
+ then pack them all together. We do a little
+ better in that we allow them in words, longs,
+ etc. and we'll pack them in target byte order
+ for you.
+
+ The rules are: pack least significat bit first,
+ if a field doesn't entirely fit, put it in the
+ next unit. Overflowing the bitfield is
+ explicitly *not* even a warning. The bitwidth
+ should be considered a "mask".
+
+ FIXME-SOMEDAY: If this is considered generally
+ useful, this logic should probably be reworked.
+ xoxorich. */
+
+ if (*input_line_pointer == ':') { /* bitfields */
+ long value = 0;
+
+ for (;;) {
+ unsigned long width;
+
+ if (*input_line_pointer != ':') {
+ input_line_pointer = hold;
+ break;
+ } /* next piece is not a bitfield */
+
+ /* In the general case, we can't allow
+ full expressions with symbol
+ differences and such. The relocation
+ entries for symbols not defined in this
+ assembly would require arbitrary field
+ widths, positions, and masks which most
+ of our current object formats don't
+ support.
+
+ In the specific case where a symbol
+ *is* defined in this assembly, we
+ *could* build fixups and track it, but
+ this could lead to confusion for the
+ backends. I'm lazy. I'll take any
+ SEG_ABSOLUTE. I think that means that
+ you can use a previous .set or
+ .equ type symbol. xoxorich. */
+
+ if (segment == SEG_ABSENT) {
+ as_warn("Using a bit field width of zero.");
+ exp.X_add_number = 0;
+ segment = SEG_ABSOLUTE;
+ } /* implied zero width bitfield */
+
+ if (segment != SEG_ABSOLUTE) {
+ *input_line_pointer = '\0';
+ as_bad("Field width \"%s\" too complex for a bitfield.\n", hold);
+ *input_line_pointer = ':';
+ demand_empty_rest_of_line();
+ return;
+ } /* too complex */
+
+ if ((width = exp.X_add_number) > (BITS_PER_CHAR * nbytes)) {
+ as_warn("Field width %d too big to fit in %d bytes: truncated to %d bits.",
+ width, nbytes, (BITS_PER_CHAR * nbytes));
+ width = BITS_PER_CHAR * nbytes;
+ } /* too big */
+
+ if (width > bits_available) {
+ /* FIXME-SOMEDAY: backing up and
+ reparsing is wasteful */
+ input_line_pointer = hold;
+ exp.X_add_number = value;
+ break;
+ } /* won't fit */
+
+ hold = ++input_line_pointer; /* skip ':' */
+
+ if ((segment = expression(&exp)) != SEG_ABSOLUTE) {
+ char cache = *input_line_pointer;
+
+ *input_line_pointer = '\0';
+ as_bad("Field value \"%s\" too complex for a bitfield.\n", hold);
+ *input_line_pointer = cache;
+ demand_empty_rest_of_line();
+ return;
+ } /* too complex */
+
+ value |= (~(-1 << width) & exp.X_add_number)
+ << ((BITS_PER_CHAR * nbytes) - bits_available);
+
+ if ((bits_available -= width) == 0
+ || is_it_end_of_statement()
+ || *input_line_pointer != ',') {
+ break;
+ } /* all the bitfields we're gonna get */
+
+ hold = ++input_line_pointer;
+ segment = expression(&exp);
+ } /* forever loop */
+
+ exp.X_add_number = value;
+ segment = SEG_ABSOLUTE;
+ } /* if looks like a bitfield */
+#endif /* WANT_BITFIELDS */
+
+ if (!need_pass_2) { /* Still worthwhile making frags. */
+
+ /* Don't call this if we are going to junk this pass anyway! */
+ know(segment != SEG_PASS1);
+
+ if (segment == SEG_DIFFERENCE && exp.X_add_symbol == NULL) {
+ as_bad("Subtracting symbol \"%s\"(segment\"%s\") is too hard. Absolute segment assumed.",
+ S_GET_NAME(exp.X_subtract_symbol),
+ segment_name(S_GET_SEGMENT(exp.X_subtract_symbol)));
+ segment = SEG_ABSOLUTE;
+ /* Leave exp.X_add_number alone. */
+ }
+
+ p = frag_more(nbytes);
+
+ switch (segment) {
+ case SEG_BIG:
+ as_bad("%s number invalid. Absolute 0 assumed.",
+ exp.X_add_number > 0 ? "Bignum" : "Floating-Point");
+ md_number_to_chars (p, (long)0, nbytes);
+ break;
+
+ case SEG_ABSENT:
+ as_warn("0 assumed for missing expression");
+ exp.X_add_number = 0;
+ know(exp.X_add_symbol == NULL);
+ /* fall into SEG_ABSOLUTE */
+ case SEG_ABSOLUTE:
+ get = exp.X_add_number;
+ use = get & unmask;
+ if ((get & mask) && (get & mask) != mask)
+ { /* Leading bits contain both 0s & 1s. */
+ as_warn("Value 0x%x truncated to 0x%x.", get, use);
+ }
+ md_number_to_chars (p, use, nbytes); /* put bytes in right order. */
+ break;
+
+ case SEG_DIFFERENCE:
+#ifndef WORKING_DOT_WORD
+ if (nbytes == 2) {
+ struct broken_word *x;
+
+ x = (struct broken_word *) xmalloc(sizeof(struct broken_word));
+ x->next_broken_word = broken_words;
+ broken_words = x;
+ x->frag = frag_now;
+ x->word_goes_here = p;
+ x->dispfrag = 0;
+ x->add = exp.X_add_symbol;
+ x->sub = exp.X_subtract_symbol;
+ x->addnum = exp.X_add_number;
+ x->added = 0;
+ new_broken_words++;
+ break;
+ }
+ /* Else Fall through into... */
+#endif
+ default:
+ case SEG_UNKNOWN:
+#ifdef TC_NS32K
+ fix_new_ns32k(frag_now, p - frag_now->fr_literal, nbytes,
+ exp.X_add_symbol, exp.X_subtract_symbol,
+ exp.X_add_number, 0, 0, 2, 0, 0);
+#else
+#ifdef PIC
+ fix_new(frag_now, p - frag_now->fr_literal, nbytes,
+ exp.X_add_symbol, exp.X_subtract_symbol,
+ exp.X_add_number, 0, RELOC_32,
+ exp.X_got_symbol);
+#else
+ fix_new(frag_now, p - frag_now->fr_literal, nbytes,
+ exp.X_add_symbol, exp.X_subtract_symbol,
+ exp.X_add_number, 0, RELOC_32);
+#endif
+#endif /* TC_NS32K */
+ break;
+ } /* switch (segment) */
+ } /* if (!need_pass_2) */
+ c = *input_line_pointer++;
+ } /* while (c == ',') */
+ input_line_pointer--; /* Put terminator back into stream. */
+ demand_empty_rest_of_line();
+} /* cons() */
+
+/*
+ * big_cons()
+ *
+ * CONStruct more frag(s) of .quads, or .octa etc.
+ * Makes 0 or more new frags.
+ * If need_pass_2 == 1, generate no frag.
+ * This understands only bignums, not expressions. Cons() understands
+ * expressions.
+ *
+ * Constants recognised are '0...'(octal) '0x...'(hex) '...'(decimal).
+ *
+ * This creates objects with struct obstack_control objs, destroying
+ * any context objs held about a partially completed object. Beware!
+ *
+ *
+ * I think it sucks to have 2 different types of integers, with 2
+ * routines to read them, store them etc.
+ * It would be nicer to permit bignums in expressions and only
+ * complain if the result overflowed. However, due to "efficiency"...
+ */
+/* worker to do .quad etc statements */
+/* clobbers input_line_pointer, checks */
+/* end-of-line. */
+/* 8=.quad 16=.octa ... */
+
+void big_cons(nbytes)
+register int nbytes;
+{
+ register char c; /* input_line_pointer->c. */
+ register int radix;
+ register long length; /* Number of chars in an object. */
+ register int digit; /* Value of 1 digit. */
+ register int carry; /* For multi-precision arithmetic. */
+ register int work; /* For multi-precision arithmetic. */
+ register char * p; /* For multi-precision arithmetic. */
+
+ extern const char hex_value[]; /* In hex_value.c. */
+
+ /*
+ * The following awkward logic is to parse ZERO or more strings,
+ * comma seperated. Recall an expression includes its leading &
+ * trailing blanks. We fake a leading ',' if there is (supposed to
+ * be) a 1st expression, and keep demanding 1 expression for each ','.
+ */
+ if (is_it_end_of_statement())
+ {
+ c = 0; /* Skip loop. */
+ }
+ else
+ {
+ c = ','; /* Do loop. */
+ -- input_line_pointer;
+ }
+ while (c == ',')
+ {
+ ++ input_line_pointer;
+ SKIP_WHITESPACE();
+ c = * input_line_pointer;
+ /* C contains 1st non-blank character of what we hope is a number. */
+ if (c == '0')
+ {
+ c = * ++ input_line_pointer;
+ if (c == 'x' || c == 'X')
+ {
+ c = * ++ input_line_pointer;
+ radix = 16;
+ }
+ else
+ {
+ radix = 8;
+ }
+ }
+ else
+ {
+ radix = 10;
+ }
+ /*
+ * This feature (?) is here to stop people worrying about
+ * mysterious zero constants: which is what they get when
+ * they completely omit digits.
+ */
+ if (hex_value[c] >= radix) {
+ as_bad("Missing digits. 0 assumed.");
+ }
+ bignum_high = bignum_low - 1; /* Start constant with 0 chars. */
+ for (; (digit = hex_value[c]) < radix; c = *++input_line_pointer)
+ {
+ /* Multiply existing number by radix, then add digit. */
+ carry = digit;
+ for (p=bignum_low; p <= bignum_high; p++)
+ {
+ work = (*p & MASK_CHAR) * radix + carry;
+ *p = work & MASK_CHAR;
+ carry = work >> BITS_PER_CHAR;
+ }
+ if (carry)
+ {
+ grow_bignum();
+ * bignum_high = carry & MASK_CHAR;
+ know((carry & ~ MASK_CHAR) == 0);
+ }
+ }
+ length = bignum_high - bignum_low + 1;
+ if (length > nbytes)
+ {
+ as_warn("Most significant bits truncated in integer constant.");
+ }
+ else
+ {
+ register long leading_zeroes;
+
+ for (leading_zeroes = nbytes - length;
+ leading_zeroes;
+ leading_zeroes --)
+ {
+ grow_bignum();
+ * bignum_high = 0;
+ }
+ }
+ if (! need_pass_2)
+ {
+ p = frag_more (nbytes);
+ memcpy(p, bignum_low, (int) nbytes);
+ }
+ /* C contains character after number. */
+ SKIP_WHITESPACE();
+ c = *input_line_pointer;
+ /* C contains 1st non-blank character after number. */
+ }
+ demand_empty_rest_of_line();
+} /* big_cons() */
+
+/* Extend bignum by 1 char. */
+static void grow_bignum() {
+ register long length;
+
+ bignum_high ++;
+ if (bignum_high >= bignum_limit)
+ {
+ length = bignum_limit - bignum_low;
+ bignum_low = xrealloc(bignum_low, length + length);
+ bignum_high = bignum_low + length;
+ bignum_limit = bignum_low + length + length;
+ }
+} /* grow_bignum(); */
+
+/*
+ * float_cons()
+ *
+ * CONStruct some more frag chars of .floats .ffloats etc.
+ * Makes 0 or more new frags.
+ * If need_pass_2 == 1, no frags are emitted.
+ * This understands only floating literals, not expressions. Sorry.
+ *
+ * A floating constant is defined by atof_generic(), except it is preceded
+ * by 0d 0f 0g or 0h. After observing the STRANGE way my BSD AS does its
+ * reading, I decided to be incompatible. This always tries to give you
+ * rounded bits to the precision of the pseudo-op. Former AS did premature
+ * truncatation, restored noisy bits instead of trailing 0s AND gave you
+ * a choice of 2 flavours of noise according to which of 2 floating-point
+ * scanners you directed AS to use.
+ *
+ * In: input_line_pointer->whitespace before, or '0' of flonum.
+ *
+ */
+
+void /* JF was static, but can't be if VAX.C is goning to use it */
+ float_cons(float_type) /* Worker to do .float etc statements. */
+/* Clobbers input_line-pointer, checks end-of-line. */
+register int float_type; /* 'f':.ffloat ... 'F':.float ... */
+{
+ register char * p;
+ register char c;
+ int length; /* Number of chars in an object. */
+ register char * err; /* Error from scanning floating literal. */
+ char temp[MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT];
+
+ /*
+ * The following awkward logic is to parse ZERO or more strings,
+ * comma seperated. Recall an expression includes its leading &
+ * trailing blanks. We fake a leading ',' if there is (supposed to
+ * be) a 1st expression, and keep demanding 1 expression for each ','.
+ */
+ if (is_it_end_of_statement())
+ {
+ c = 0; /* Skip loop. */
+ ++ input_line_pointer; /*->past termintor. */
+ }
+ else
+ {
+ c = ','; /* Do loop. */
+ }
+ while (c == ',') {
+ /* input_line_pointer->1st char of a flonum (we hope!). */
+ SKIP_WHITESPACE();
+ /* Skip any 0{letter} that may be present. Don't even check if the
+ * letter is legal. Someone may invent a "z" format and this routine
+ * has no use for such information. Lusers beware: you get
+ * diagnostics if your input is ill-conditioned.
+ */
+
+ if (input_line_pointer[0] == '0' && isalpha(input_line_pointer[1]))
+ input_line_pointer+=2;
+
+ err = md_atof (float_type, temp, &length);
+ know(length <= MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT);
+ know(length > 0);
+ if (* err) {
+ as_bad("Bad floating literal: %s", err);
+ ignore_rest_of_line();
+ /* Input_line_pointer->just after end-of-line. */
+ c = 0; /* Break out of loop. */
+ } else {
+ if (! need_pass_2) {
+ p = frag_more (length);
+ memcpy(p, temp, length);
+ }
+ SKIP_WHITESPACE();
+ c = *input_line_pointer++;
+ /* C contains 1st non-white character after number. */
+ /* input_line_pointer->just after terminator (c). */
+ }
+ }
+ --input_line_pointer; /*->terminator (is not ','). */
+ demand_empty_rest_of_line();
+} /* float_cons() */
+
+/*
+ * stringer() Worker to do .ascii etc statements. Checks end-of-line.
+ *
+ * We read 0 or more ',' seperated, double-quoted strings.
+ *
+ * Caller should have checked need_pass_2 is FALSE because we don't check it.
+ */
+
+void stringer(append_zero)
+int append_zero; /* 0: don't append '\0', else 1 */
+{
+ unsigned int c;
+
+ /*
+ * The following awkward logic is to parse ZERO or more strings,
+ * comma seperated. Recall a string expression includes spaces
+ * before the opening '\"' and spaces after the closing '\"'.
+ * We fake a leading ',' if there is (supposed to be)
+ * a 1st, expression. We keep demanding expressions for each
+ * ','.
+ */
+ if (is_it_end_of_statement()) {
+ c = 0; /* Skip loop. */
+ ++ input_line_pointer; /* Compensate for end of loop. */
+ } else {
+ c = ','; /* Do loop. */
+ }
+
+ while (c == ',' || c == '<' || c == '"' || ('0' <= c && c <= '9')) {
+ int i;
+
+ SKIP_WHITESPACE();
+ switch (*input_line_pointer) {
+ case '\"':
+ ++input_line_pointer; /* ->1st char of string. */
+ while (is_a_char(c = next_char_of_string())) {
+ FRAG_APPEND_1_CHAR(c);
+ }
+ if (append_zero) {
+ FRAG_APPEND_1_CHAR(0);
+ }
+ know(input_line_pointer[-1] == '\"');
+ break;
+
+ case '<':
+ input_line_pointer++;
+ c = get_single_number();
+ FRAG_APPEND_1_CHAR(c);
+ if (*input_line_pointer != '>') {
+ as_bad("Expected <nn>");
+ }
+ input_line_pointer++;
+ break;
+
+ case ',':
+ input_line_pointer++;
+ break;
+
+ default:
+ i = get_absolute_expression();
+ FRAG_APPEND_1_CHAR(i);
+ break;
+ } /* switch on next char */
+
+ SKIP_WHITESPACE();
+ c = *input_line_pointer;
+ }
+
+ demand_empty_rest_of_line();
+} /* stringer() */
+
+/* FIXME-SOMEDAY: I had trouble here on characters with the
+ high bits set. We'll probably also have trouble with
+ multibyte chars, wide chars, etc. Also be careful about
+ returning values bigger than 1 byte. xoxorich. */
+
+unsigned int next_char_of_string() {
+ register unsigned int c;
+
+ c = *input_line_pointer++ & CHAR_MASK;
+ switch (c) {
+ case '\"':
+ c = NOT_A_CHAR;
+ break;
+
+ case '\\':
+ switch (c = *input_line_pointer++) {
+ case 'b':
+ c = '\b';
+ break;
+
+ case 'f':
+ c = '\f';
+ break;
+
+ case 'n':
+ c = '\n';
+ break;
+
+ case 'r':
+ c = '\r';
+ break;
+
+ case 't':
+ c = '\t';
+ break;
+
+#ifdef BACKSLASH_V
+ case 'v':
+ c = '\013';
+ break;
+#endif
+
+ case '\\':
+ case '"':
+ break; /* As itself. */
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': {
+ long number;
+
+ for (number = 0; isdigit(c); c = *input_line_pointer++) {
+ number = number * 8 + c - '0';
+ }
+ c = number & 0xff;
+ }
+ --input_line_pointer;
+ break;
+
+ case '\n':
+ /* To be compatible with BSD 4.2 as: give the luser a linefeed!! */
+ as_warn("Unterminated string: Newline inserted.");
+ c = '\n';
+ break;
+
+ default:
+
+#ifdef ONLY_STANDARD_ESCAPES
+ as_bad("Bad escaped character in string, '?' assumed");
+ c = '?';
+#endif /* ONLY_STANDARD_ESCAPES */
+
+ break;
+ } /* switch on escaped char */
+ break;
+
+ default:
+ break;
+ } /* switch on char */
+ return(c);
+} /* next_char_of_string() */
+
+static segT
+ get_segmented_expression (expP)
+register expressionS * expP;
+{
+ register segT retval;
+
+ if ((retval = expression(expP)) == SEG_PASS1 || retval == SEG_ABSENT || retval == SEG_BIG)
+ {
+ as_bad("Expected address expression: absolute 0 assumed");
+ retval = expP->X_seg = SEG_ABSOLUTE;
+ expP->X_add_number = 0;
+ expP->X_add_symbol = expP->X_subtract_symbol = 0;
+ }
+ return (retval); /* SEG_ ABSOLUTE,UNKNOWN,DATA,TEXT,BSS */
+}
+
+static segT get_known_segmented_expression(expP)
+register expressionS *expP;
+{
+ register segT retval;
+ register char * name1;
+ register char * name2;
+
+ if ((retval = get_segmented_expression (expP)) == SEG_UNKNOWN)
+ {
+ name1 = expP->X_add_symbol ? S_GET_NAME(expP->X_add_symbol) : "";
+ name2 = expP->X_subtract_symbol ?
+ S_GET_NAME(expP->X_subtract_symbol) :
+ "";
+ if (name1 && name2)
+ {
+ as_warn("Symbols \"%s\" \"%s\" are undefined: absolute 0 assumed.",
+ name1, name2);
+ }
+ else
+ {
+ as_warn("Symbol \"%s\" undefined: absolute 0 assumed.",
+ name1 ? name1 : name2);
+ }
+ retval = expP->X_seg = SEG_ABSOLUTE;
+ expP->X_add_number = 0;
+ expP->X_add_symbol = expP->X_subtract_symbol = NULL;
+ }
+#ifndef MANY_SEGMENTS
+ know(retval == SEG_ABSOLUTE || retval == SEG_DATA || retval == SEG_TEXT || retval == SEG_BSS || retval == SEG_DIFFERENCE);
+#endif
+ return (retval);
+
+} /* get_known_segmented_expression() */
+
+
+
+/* static */ long /* JF was static, but can't be if the MD pseudos are to use it */
+ get_absolute_expression()
+{
+ expressionS exp;
+ register segT s;
+
+ if ((s = expression(&exp)) != SEG_ABSOLUTE) {
+ if (s != SEG_ABSENT) {
+ as_bad("Bad Absolute Expression, absolute 0 assumed.");
+ }
+ exp.X_add_number = 0;
+ }
+ return(exp.X_add_number);
+} /* get_absolute_expression() */
+
+char /* return terminator */
+ get_absolute_expression_and_terminator(val_pointer)
+long * val_pointer; /* return value of expression */
+{
+ *val_pointer = get_absolute_expression();
+ return (*input_line_pointer++);
+}
+
+/*
+ * demand_copy_C_string()
+ *
+ * Like demand_copy_string, but return NULL if the string contains any '\0's.
+ * Give a warning if that happens.
+ */
+char *
+ demand_copy_C_string (len_pointer)
+int *len_pointer;
+{
+ register char *s;
+
+ if ((s = demand_copy_string(len_pointer)) != 0) {
+ register int len;
+
+ for (len = *len_pointer;
+ len > 0;
+ len--) {
+ if (*s == 0) {
+ s = 0;
+ len = 1;
+ *len_pointer = 0;
+ as_bad("This string may not contain \'\\0\'");
+ }
+ }
+ }
+ return(s);
+}
+
+/*
+ * demand_copy_string()
+ *
+ * Demand string, but return a safe (=private) copy of the string.
+ * Return NULL if we can't read a string here.
+ */
+static char *demand_copy_string(lenP)
+int *lenP;
+{
+ register unsigned int c;
+ register int len;
+ char *retval;
+
+ len = 0;
+ SKIP_WHITESPACE();
+ if (*input_line_pointer == '\"') {
+ input_line_pointer++; /* Skip opening quote. */
+
+ while (is_a_char(c = next_char_of_string())) {
+ obstack_1grow(&notes, c);
+ len ++;
+ }
+ /* JF this next line is so demand_copy_C_string will return a null
+ termanated string. */
+ obstack_1grow(&notes,'\0');
+ retval=obstack_finish(&notes);
+ } else {
+ as_warn("Missing string");
+ retval = NULL;
+ ignore_rest_of_line();
+ }
+ *lenP = len;
+ return(retval);
+} /* demand_copy_string() */
+
+/*
+ * is_it_end_of_statement()
+ *
+ * In: Input_line_pointer->next character.
+ *
+ * Do: Skip input_line_pointer over all whitespace.
+ *
+ * Out: 1 if input_line_pointer->end-of-line.
+ */
+int is_it_end_of_statement() {
+ SKIP_WHITESPACE();
+ return(is_end_of_line[*input_line_pointer]);
+} /* is_it_end_of_statement() */
+
+void equals(sym_name)
+char *sym_name;
+{
+ register symbolS *symbolP; /* symbol we are working with */
+
+ input_line_pointer++;
+ if (*input_line_pointer == '=')
+ input_line_pointer++;
+
+ while (*input_line_pointer == ' ' || *input_line_pointer == '\t')
+ input_line_pointer++;
+
+ if (sym_name[0] == '.' && sym_name[1] == '\0') {
+ /* Turn '. = mumble' into a .org mumble */
+ register segT segment;
+ expressionS exp;
+ register char *p;
+
+ segment = get_known_segmented_expression(& exp);
+ if (! need_pass_2) {
+ if (segment != now_seg && segment != SEG_ABSOLUTE)
+ as_warn("Illegal segment \"%s\". Segment \"%s\" assumed.",
+ segment_name(segment),
+ segment_name(now_seg));
+ p = frag_var(rs_org, 1, 1, (relax_substateT)0, exp.X_add_symbol,
+ exp.X_add_number, (char *)0);
+ * p = 0;
+ } /* if (ok to make frag) */
+ } else {
+ symbolP=symbol_find_or_make(sym_name);
+ pseudo_set(symbolP);
+ }
+} /* equals() */
+
+/* .include -- include a file at this point. */
+
+/* ARGSUSED */
+void s_include(arg)
+int arg;
+{
+ char *newbuf;
+ char *filename;
+ int i;
+ FILE *try;
+ char *path;
+
+ filename = demand_copy_string(&i);
+ demand_empty_rest_of_line();
+ path = xmalloc(i + include_dir_maxlen + 5 /* slop */);
+ for (i = 0; i < include_dir_count; i++) {
+ strcpy(path, include_dirs[i]);
+ strcat(path, "/");
+ strcat(path, filename);
+ if (0 != (try = fopen(path, "r")))
+ {
+ fclose (try);
+ goto gotit;
+ }
+ }
+ free(path);
+ path = filename;
+ gotit:
+ /* malloc Storage leak when file is found on path. FIXME-SOMEDAY. */
+ newbuf = input_scrub_include_file (path, input_line_pointer);
+ buffer_limit = input_scrub_next_buffer (&input_line_pointer);
+} /* s_include() */
+
+void add_include_dir(path)
+char *path;
+{
+ int i;
+
+ if (include_dir_count == 0)
+ {
+ include_dirs = (char **)xmalloc (2 * sizeof (*include_dirs));
+ include_dirs[0] = "."; /* Current dir */
+ include_dir_count = 2;
+ }
+ else
+ {
+ include_dir_count++;
+ include_dirs = (char **) realloc(include_dirs,
+ include_dir_count*sizeof (*include_dirs));
+ }
+
+ include_dirs[include_dir_count-1] = path; /* New one */
+
+ i = strlen (path);
+ if (i > include_dir_maxlen)
+ include_dir_maxlen = i;
+} /* add_include_dir() */
+
+void s_ignore(arg)
+int arg;
+{
+ while (!is_end_of_line[*input_line_pointer]) {
+ ++input_line_pointer;
+ }
+ ++input_line_pointer;
+
+ return;
+} /* s_ignore() */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of read.c */
diff --git a/gnu/usr.bin/as/read.h b/gnu/usr.bin/as/read.h
new file mode 100644
index 0000000..b03fbab
--- /dev/null
+++ b/gnu/usr.bin/as/read.h
@@ -0,0 +1,149 @@
+/* read.h - of read.c
+
+ Copyright (C) 1986, 1990, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
+ * $Id: read.h,v 1.2 1993/11/03 00:52:16 paul Exp $
+ */
+
+
+extern char *input_line_pointer; /* -> char we are parsing now. */
+
+#define PERMIT_WHITESPACE /* Define to make whitespace be allowed in */
+/* many syntactically unnecessary places. */
+/* Normally undefined. For compatibility */
+/* with ancient GNU cc. */
+/* #undef PERMIT_WHITESPACE */
+
+#ifdef PERMIT_WHITESPACE
+#define SKIP_WHITESPACE() {if (* input_line_pointer == ' ') ++ input_line_pointer;}
+#else
+#define SKIP_WHITESPACE() know(*input_line_pointer != ' ' )
+#endif
+
+
+#define LEX_NAME (1) /* may continue a name */
+#define LEX_BEGIN_NAME (2) /* may begin a name */
+
+#define is_name_beginner(c) ( lex_type[c] & LEX_BEGIN_NAME )
+#define is_part_of_name(c) ( lex_type[c] & LEX_NAME )
+
+#ifndef is_a_char
+#define CHAR_MASK (0xff)
+#define NOT_A_CHAR (CHAR_MASK+1)
+#define is_a_char(c) (((unsigned)(c)) <= CHAR_MASK)
+#endif /* is_a_char() */
+
+#ifdef PIC
+/* We change some of the entries in lex_type on some archs */
+extern char lex_type[];
+#else
+extern const char lex_type[];
+#endif
+extern char is_end_of_line[];
+
+#if __STDC__ == 1
+
+char *demand_copy_C_string(int *len_pointer);
+char get_absolute_expression_and_terminator(long *val_pointer);
+long get_absolute_expression(void);
+void add_include_dir(char *path);
+void big_cons(int nbytes);
+void cons(unsigned int nbytes);
+void demand_empty_rest_of_line(void);
+void equals(char *sym_name);
+void float_cons(int float_type);
+void ignore_rest_of_line(void);
+void pseudo_set(symbolS *symbolP);
+void read_a_source_file(char *name);
+void read_begin(void);
+void s_abort(void);
+void s_align_bytes(int arg);
+void s_align_ptwo(void);
+void s_app_file(void);
+void s_comm(void);
+void s_data(void);
+void s_else(int arg);
+void s_end(int arg);
+void s_endif(int arg);
+void s_fill(void);
+void s_globl(void);
+void s_if(int arg);
+void s_ifdef(int arg);
+void s_ifeqs(int arg);
+void s_ignore(int arg);
+void s_include(int arg);
+void s_lcomm(int needs_align);
+void s_lsym(void);
+void s_org(void);
+void s_set(void);
+void s_size(void);
+void s_space(void);
+void s_text(void);
+void s_type(void);
+
+#else /* not __STDC__ */
+
+char *demand_copy_C_string();
+char get_absolute_expression_and_terminator();
+long get_absolute_expression();
+void add_include_dir();
+void big_cons();
+void cons();
+void demand_empty_rest_of_line();
+void equals();
+void float_cons();
+void ignore_rest_of_line();
+void pseudo_set();
+void read_a_source_file();
+void read_begin();
+void s_abort();
+void s_align_bytes();
+void s_align_ptwo();
+void s_app_file();
+void s_comm();
+void s_data();
+void s_else();
+void s_end();
+void s_endif();
+void s_fill();
+void s_globl();
+void s_if();
+void s_ifdef();
+void s_ifeqs();
+void s_ignore();
+void s_include();
+void s_lcomm();
+void s_lsym();
+void s_org();
+void s_set();
+void s_size();
+void s_space();
+void s_text();
+void s_type();
+
+#endif /* not __STDC__ */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of read.h */
diff --git a/gnu/usr.bin/as/struc-symbol.h b/gnu/usr.bin/as/struc-symbol.h
new file mode 100644
index 0000000..e0030cd
--- /dev/null
+++ b/gnu/usr.bin/as/struc-symbol.h
@@ -0,0 +1,132 @@
+/* struct_symbol.h - Internal symbol structure
+ Copyright (C) 1987, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ oYou should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
+ * $Id: struc-symbol.h,v 1.2 1993/11/03 00:52:18 paul Exp $
+ */
+
+
+#ifndef __struc_symbol_h__
+#define __struc_symbol_h__
+
+struct symbol /* our version of an nlist node */
+{
+ obj_symbol_type sy_symbol; /* what we write in .o file (if permitted) */
+ unsigned long sy_name_offset; /* 4-origin position of sy_name in symbols */
+ /* part of object file. */
+ /* 0 for (nameless) .stabd symbols. */
+ /* Not used until write_object_file() time. */
+ long sy_number; /* 24 bit symbol number. */
+ /* Symbol numbers start at 0 and are */
+ /* unsigned. */
+ struct symbol *sy_next; /* forward chain, or NULL */
+#ifdef SYMBOLS_NEED_BACKPOINTERS
+ struct symbol *sy_previous; /* backward chain, or NULL */
+#endif /* SYMBOLS_NEED_BACKPOINTERS */
+ struct frag *sy_frag; /* NULL or -> frag this symbol attaches to. */
+ struct symbol *sy_forward; /* value is really that of this other symbol */
+ /* We will probably want to add a sy_segment here soon. */
+
+#ifdef PIC
+ /* Force symbol into symbol table, even if local */
+ int sy_forceout;
+#endif
+ /* Size of symbol as given by the .size directive */
+ void *sy_sizexp; /* (expressionS *) */
+
+ /* Auxiliary type information as given by the .type directive */
+ int sy_aux;
+#define AUX_OBJECT 1
+#define AUX_FUNC 2
+};
+
+typedef struct symbol symbolS;
+
+#ifdef PIC
+symbolS *GOT_symbol; /* Pre-defined "__GLOBAL_OFFSET_TABLE" */
+int got_referenced;
+#endif
+
+typedef unsigned valueT; /* The type of n_value. Helps casting. */
+
+#ifndef WORKING_DOT_WORD
+struct broken_word {
+ struct broken_word *next_broken_word;/* One of these strucs per .word x-y */
+ fragS *frag; /* Which frag its in */
+ char *word_goes_here;/* Where in the frag it is */
+ fragS *dispfrag; /* where to add the break */
+ symbolS *add; /* symbol_x */
+ symbolS *sub; /* - symbol_y */
+ long addnum; /* + addnum */
+ int added; /* nasty thing happend yet? */
+ /* 1: added and has a long-jump */
+ /* 2: added but uses someone elses long-jump */
+ struct broken_word *use_jump; /* points to broken_word with a similar
+ long-jump */
+};
+extern struct broken_word *broken_words;
+#endif /* ndef WORKING_DOT_WORD */
+
+#define SEGMENT_TO_SYMBOL_TYPE(seg) (seg_N_TYPE[(int) (seg)])
+extern const short seg_N_TYPE[]; /* subseg.c */
+
+#define N_REGISTER 30 /* Fake N_TYPE value for SEG_REGISTER */
+
+#ifdef SYMBOLS_NEED_BACKPOINTERS
+#if __STDC__ == 1
+
+void symbol_clear_list_pointers(symbolS *symbolP);
+void symbol_insert(symbolS *addme, symbolS *target, symbolS **rootP, symbolS **lastP);
+void symbol_remove(symbolS *symbolP, symbolS **rootP, symbolS **lastP);
+void verify_symbol_chain(symbolS *rootP, symbolS *lastP);
+
+#else /* not __STDC__ */
+
+void symbol_clear_list_pointers();
+void symbol_insert();
+void symbol_remove();
+void verify_symbol_chain();
+
+#endif /* not __STDC__ */
+
+#define symbol_previous(s) ((s)->sy_previous)
+
+#else /* SYMBOLS_NEED_BACKPOINTERS */
+
+#define symbol_clear_list_pointers(clearme) {clearme->sy_next = NULL;}
+
+#endif /* SYMBOLS_NEED_BACKPOINTERS */
+
+#if __STDC__ == 1
+void symbol_append(symbolS *addme, symbolS *target, symbolS **rootP, symbolS **lastP);
+#else /* not __STDC__ */
+void symbol_append();
+#endif /* not __STDC__ */
+
+#define symbol_next(s) ((s)->sy_next)
+
+#endif /* __struc_symbol_h__ */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of struc-symbol.h */
diff --git a/gnu/usr.bin/as/subsegs.c b/gnu/usr.bin/as/subsegs.c
new file mode 100644
index 0000000..af2d4e7
--- /dev/null
+++ b/gnu/usr.bin/as/subsegs.c
@@ -0,0 +1,308 @@
+/* subsegs.c - subsegments -
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * Segments & sub-segments.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: subsegs.c,v 1.3 1993/10/02 20:57:54 pk Exp $";
+#endif
+
+#include "as.h"
+
+#include "subsegs.h"
+#include "obstack.h"
+
+#ifdef MANY_SEGMENTS
+segment_info_type segment_info[SEG_MAXIMUM_ORDINAL];
+
+frchainS* frchain_root,
+ * frchain_now;
+
+#else
+frchainS* frchain_root,
+ * frchain_now, /* Commented in "subsegs.h". */
+ * data0_frchainP;
+
+#endif
+char * const /* in: segT out: char* */
+ seg_name[] = {
+ "absolute",
+#ifdef MANY_SEGMENTS
+ "e0","e1","e2","e3","e4","e5","e6","e7","e8","e9",
+#else
+ "text",
+ "data",
+ "bss",
+#endif
+ "unknown",
+ "absent",
+ "pass1",
+ "ASSEMBLER-INTERNAL-LOGIC-ERROR!",
+ "bignum/flonum",
+ "difference",
+ "debug",
+ "transfert vector preload",
+ "transfert vector postload",
+ "register",
+ "",
+ }; /* Used by error reporters, dumpers etc. */
+
+
+void
+ subsegs_begin()
+{
+ /* Check table(s) seg_name[], seg_N_TYPE[] is in correct order */
+#ifdef MANY_SEGMENTS
+#else
+ know(SEG_ABSOLUTE == 0);
+ know(SEG_TEXT == 1);
+ know(SEG_DATA == 2);
+ know(SEG_BSS == 3);
+ know(SEG_UNKNOWN == 4);
+ know(SEG_ABSENT == 5);
+ know(SEG_PASS1 == 6);
+ know(SEG_GOOF == 7);
+ know(SEG_BIG == 8);
+ know(SEG_DIFFERENCE == 9);
+ know(SEG_DEBUG == 10);
+ know(SEG_NTV == 11);
+ know(SEG_PTV == 12);
+ know(SEG_REGISTER == 13);
+ know(SEG_MAXIMUM_ORDINAL == SEG_REGISTER );
+ /* know(segment_name(SEG_MAXIMUM_ORDINAL + 1)[0] == 0);*/
+#endif
+
+ obstack_begin(&frags, 5000);
+ frchain_root = NULL;
+ frchain_now = NULL; /* Warn new_subseg() that we are booting. */
+ /* Fake up 1st frag. */
+ /* It won't be used=> is ok if obstack... */
+ /* pads the end of it for alignment. */
+ frag_now=(fragS *)obstack_alloc(&frags,SIZEOF_STRUCT_FRAG);
+ memset(frag_now, SIZEOF_STRUCT_FRAG, 0);
+ /* This 1st frag will not be in any frchain. */
+ /* We simply give subseg_new somewhere to scribble. */
+ now_subseg = 42; /* Lie for 1st call to subseg_new. */
+#ifdef MANY_SEGMENTS
+ {
+ int i;
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++) {
+ subseg_new(i, 0);
+ segment_info[i].frchainP = frchain_now;
+ }
+ }
+#else
+ subseg_new(SEG_DATA, 0); /* .data 0 */
+ data0_frchainP = frchain_now;
+#endif
+
+}
+
+/*
+ * subseg_change()
+ *
+ * Change the subsegment we are in, BUT DO NOT MAKE A NEW FRAG for the
+ * subsegment. If we are already in the correct subsegment, change nothing.
+ * This is used eg as a worker for subseg_new [which does make a new frag_now]
+ * and for changing segments after we have read the source. We construct eg
+ * fixSs even after the source file is read, so we do have to keep the
+ * segment context correct.
+ */
+void
+ subseg_change (seg, subseg)
+register segT seg;
+register int subseg;
+{
+ now_seg = seg;
+ now_subseg = subseg;
+#ifdef MANY_SEGMENTS
+ seg_fix_rootP = &segment_info[seg].fix_root;
+ seg_fix_tailP = &segment_info[seg].fix_tail;
+#else
+ if (seg == SEG_DATA) {
+ seg_fix_rootP = &data_fix_root;
+ seg_fix_tailP = &data_fix_tail;
+ } else {
+ know (seg == SEG_TEXT);
+ seg_fix_rootP = &text_fix_root;
+ seg_fix_tailP = &text_fix_tail;
+ }
+#endif
+}
+
+/*
+ * subseg_new()
+ *
+ * If you attempt to change to the current subsegment, nothing happens.
+ *
+ * In: segT, subsegT code for new subsegment.
+ * frag_now -> incomplete frag for current subsegment.
+ * If frag_now == NULL, then there is no old, incomplete frag, so
+ * the old frag is not closed off.
+ *
+ * Out: now_subseg, now_seg updated.
+ * Frchain_now points to the (possibly new) struct frchain for this
+ * sub-segment.
+ * Frchain_root updated if needed.
+ */
+
+void
+ subseg_new (seg, subseg) /* begin assembly for a new sub-segment */
+register segT seg; /* SEG_DATA or SEG_TEXT */
+register subsegT subseg;
+{
+ long tmp; /* JF for obstack alignment hacking */
+#ifndef MANY_SEGMENTS
+ know(seg == SEG_DATA || seg == SEG_TEXT);
+#endif
+ if (seg != now_seg || subseg != now_subseg)
+ { /* we just changed sub-segments */
+ register frchainS * frcP; /* crawl frchain chain */
+ register frchainS** lastPP; /* address of last pointer */
+ frchainS *newP; /* address of new frchain */
+ register fragS *former_last_fragP;
+ register fragS *new_fragP;
+
+ if (frag_now) /* If not bootstrapping. */
+ {
+ frag_now->fr_fix = obstack_next_free(& frags) - frag_now->fr_literal;
+ frag_wane(frag_now); /* Close off any frag in old subseg. */
+ }
+ /*
+ * It would be nice to keep an obstack for each subsegment, if we swap
+ * subsegments a lot. Hence we would have much fewer frag_wanes().
+ */
+ {
+
+ obstack_finish( &frags);
+ /*
+ * If we don't do the above, the next object we put on obstack frags
+ * will appear to start at the fr_literal of the current frag.
+ * Also, above ensures that the next object will begin on a
+ * address that is aligned correctly for the engine that runs
+ * this program.
+ */
+ }
+ subseg_change (seg, (int)subseg);
+ /*
+ * Attempt to find or make a frchain for that sub seg.
+ * Crawl along chain of frchainSs, begins @ frchain_root.
+ * If we need to make a frchainS, link it into correct
+ * position of chain rooted in frchain_root.
+ */
+ for (frcP = * (lastPP = & frchain_root);
+ frcP
+ && (int)(frcP->frch_seg) <= (int)seg;
+ frcP = * ( lastPP = & frcP->frch_next)
+ )
+ {
+ if ( (int)(frcP->frch_seg) == (int)seg
+ && frcP->frch_subseg >= subseg)
+ {
+ break;
+ }
+ }
+ /*
+ * frcP: Address of the 1st frchainS in correct segment with
+ * frch_subseg >= subseg.
+ * We want to either use this frchainS, or we want
+ * to insert a new frchainS just before it.
+ *
+ * If frcP == NULL, then we are at the end of the chain
+ * of frchainS-s. A NULL frcP means we fell off the end
+ * of the chain looking for a
+ * frch_subseg >= subseg, so we
+ * must make a new frchainS.
+ *
+ * If we ever maintain a pointer to
+ * the last frchainS in the chain, we change that pointer
+ * ONLY when frcP == NULL.
+ *
+ * lastPP: Address of the pointer with value frcP;
+ * Never NULL.
+ * May point to frchain_root.
+ *
+ */
+ if ( ! frcP
+ || ( (int)(frcP->frch_seg) > (int)seg
+ || frcP->frch_subseg > subseg)) /* Kinky logic only works with 2 segments. */
+ {
+ /*
+ * This should be the only code that creates a frchainS.
+ */
+ newP=(frchainS *)obstack_alloc(&frags,sizeof(frchainS));
+ memset(newP, sizeof(frchainS), 0);
+ /* This begines on a good boundary */
+ /* because a obstack_done() preceeded it. */
+ /* It implies an obstack_done(), so we */
+ /* expect the next object allocated to */
+ /* begin on a correct boundary. */
+ *lastPP = newP;
+ newP->frch_next = frcP; /* perhaps NULL */
+ (frcP = newP)->frch_subseg = subseg;
+ newP->frch_seg = seg;
+ newP->frch_last = NULL;
+ }
+ /*
+ * Here with frcP->ing to the frchainS for subseg.
+ */
+ frchain_now = frcP;
+ /*
+ * Make a fresh frag for the subsegment.
+ */
+ /* We expect this to happen on a correct */
+ /* boundary since it was proceeded by a */
+ /* obstack_done(). */
+ tmp=obstack_alignment_mask(&frags); /* JF disable alignment */
+ obstack_alignment_mask(&frags)=0;
+ frag_now=(fragS *)obstack_alloc(&frags,SIZEOF_STRUCT_FRAG);
+ obstack_alignment_mask(&frags)=tmp;
+ /* know(frags.obstack_c_next_free == frag_now->fr_literal); */
+ /* But we want any more chars to come */
+ /* immediately after the structure we just made. */
+ new_fragP = frag_now;
+ new_fragP->fr_next = NULL;
+ /*
+ * Append new frag to current frchain.
+ */
+ former_last_fragP = frcP->frch_last;
+ if (former_last_fragP)
+ {
+ know( former_last_fragP->fr_next == NULL );
+ know( frchain_now->frch_root );
+ former_last_fragP->fr_next = new_fragP;
+ }
+ else
+ {
+ frcP->frch_root = new_fragP;
+ }
+ frcP->frch_last = new_fragP;
+ } /* if (changing subsegments) */
+} /* subseg_new() */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of subsegs.c */
diff --git a/gnu/usr.bin/as/subsegs.h b/gnu/usr.bin/as/subsegs.h
new file mode 100644
index 0000000..4b8e3ce
--- /dev/null
+++ b/gnu/usr.bin/as/subsegs.h
@@ -0,0 +1,93 @@
+/* subsegs.h -> subsegs.c
+
+ Copyright (C) 1987, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
+ * $Id: subsegs.h,v 1.3 1993/10/02 20:57:55 pk Exp $
+ */
+
+
+/*
+ * For every sub-segment the user mentions in the ASsembler program,
+ * we make one struct frchain. Each sub-segment has exactly one struct frchain
+ * and vice versa.
+ *
+ * Struct frchain's are forward chained (in ascending order of sub-segment
+ * code number). The chain runs through frch_next of each subsegment.
+ * This makes it hard to find a subsegment's frags
+ * if programmer uses a lot of them. Most programs only use text0 and
+ * data0, so they don't suffer. At least this way:
+ * (1) There are no "arbitrary" restrictions on how many subsegments
+ * can be programmed;
+ * (2) Subsegments' frchain-s are (later) chained together in the order in
+ * which they are emitted for object file viz text then data.
+ *
+ * From each struct frchain dangles a chain of struct frags. The frags
+ * represent code fragments, for that sub-segment, forward chained.
+ */
+
+struct frchain /* control building of a frag chain */
+{ /* FRCH = FRagment CHain control */
+ struct frag * frch_root; /* 1st struct frag in chain, or NULL */
+ struct frag * frch_last; /* last struct frag in chain, or NULL */
+ struct frchain * frch_next; /* next in chain of struct frchain-s */
+ segT frch_seg; /* SEG_TEXT or SEG_DATA. */
+ subsegT frch_subseg; /* subsegment number of this chain */
+};
+
+typedef struct frchain frchainS;
+
+extern frchainS * frchain_root; /* NULL means no frchains yet. */
+/* all subsegments' chains hang off here */
+
+extern frchainS * frchain_now;
+/* Frchain we are assembling into now */
+/* That is, the current segment's frag */
+/* chain, even if it contains no (complete) */
+/* frags. */
+
+
+#ifdef MANY_SEGMENTS
+typedef struct
+{
+ frchainS *frchainP;
+ int hadone;
+ int user_stuff;
+ /* struct frag *frag_root;*/
+ /* struct frag *last_frag;*/
+ fixS *fix_root;
+ fixS *fix_tail;
+ struct internal_scnhdr scnhdr;
+ symbolS *dot;
+
+ struct lineno_list *lineno_list_head;
+ struct lineno_list *lineno_list_tail;
+
+} segment_info_type;
+segment_info_type segment_info[];
+#else
+extern frchainS * data0_frchainP;
+extern frchainS * bss0_frchainP;
+/* Sentinel for frchain crawling. */
+/* Points to the 1st data-segment frchain. */
+/* (Which is pointed to by the last text- */
+/* segment frchain.) */
+
+#endif
+
+/* end of subsegs.h */
diff --git a/gnu/usr.bin/as/symbols.c b/gnu/usr.bin/as/symbols.c
new file mode 100644
index 0000000..066f54e
--- /dev/null
+++ b/gnu/usr.bin/as/symbols.c
@@ -0,0 +1,658 @@
+/* symbols.c -symbol table-
+
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef lint
+static char rcsid[] = "$Id: symbols.c,v 1.3 1993/10/02 20:57:56 pk Exp $";
+#endif
+
+#include "as.h"
+
+#include "obstack.h" /* For "symbols.h" */
+#include "subsegs.h"
+
+#ifndef WORKING_DOT_WORD
+extern int new_broken_words;
+#endif
+
+static
+ struct hash_control *
+ sy_hash; /* symbol-name => struct symbol pointer */
+
+/* Below are commented in "symbols.h". */
+unsigned int local_bss_counter;
+symbolS * symbol_rootP;
+symbolS * symbol_lastP;
+symbolS abs_symbol;
+
+symbolS* dot_text_symbol;
+symbolS* dot_data_symbol;
+symbolS* dot_bss_symbol;
+
+struct obstack notes;
+
+/*
+ * Un*x idea of local labels. They are made by "n:" where n
+ * is any decimal digit. Refer to them with
+ * "nb" for previous (backward) n:
+ * or "nf" for next (forward) n:.
+ *
+ * Like Un*x AS, we have one set of local label counters for entire assembly,
+ * not one set per (sub)segment like in most assemblers. This implies that
+ * one can refer to a label in another segment, and indeed some crufty
+ * compilers have done just that.
+ *
+ * I document the symbol names here to save duplicating words elsewhere.
+ * The mth occurence of label n: is turned into the symbol "Ln^Am" where
+ * n is a digit and m is a decimal number. "L" makes it a label discarded
+ * unless debugging and "^A"('\1') ensures no ordinary symbol SHOULD get the
+ * same name as a local label symbol. The first "4:" is "L4^A1" - the m
+ * numbers begin at 1.
+ */
+
+typedef short unsigned int
+ local_label_countT;
+
+static local_label_countT
+ local_label_counter[10];
+
+static /* Returned to caller, then copied. */
+ char symbol_name_build[12]; /* used for created names ("4f") */
+
+#ifdef LOCAL_LABELS_DOLLAR
+int local_label_defined[10];
+#endif
+
+
+void
+ symbol_begin()
+{
+ symbol_lastP = NULL;
+ symbol_rootP = NULL; /* In case we have 0 symbols (!!) */
+ sy_hash = hash_new();
+ memset((char *)(& abs_symbol), '\0', sizeof(abs_symbol));
+ S_SET_SEGMENT(&abs_symbol, SEG_ABSOLUTE); /* Can't initialise a union. Sigh. */
+ memset((char *)(local_label_counter), '\0', sizeof(local_label_counter) );
+ local_bss_counter = 0;
+}
+
+/*
+ * local_label_name()
+ *
+ * Caller must copy returned name: we re-use the area for the next name.
+ */
+
+char * /* Return local label name. */
+ local_label_name(n, augend)
+register int n; /* we just saw "n:", "nf" or "nb" : n a digit */
+register int augend; /* 0 for nb, 1 for n:, nf */
+{
+ register char * p;
+ register char * q;
+ char symbol_name_temporary[10]; /* build up a number, BACKWARDS */
+
+ know( n >= 0 );
+ know( augend == 0 || augend == 1 );
+ p = symbol_name_build;
+ * p ++ = 1; /* ^A */
+ * p ++ = 'L';
+ * p ++ = n + '0'; /* Make into ASCII */
+ n = local_label_counter[ n ] + augend;
+ /* version number of this local label */
+ /*
+ * Next code just does sprintf( {}, "%d", n);
+ * It is more elegant to do the next part recursively, but a procedure
+ * call for each digit emitted is considered too costly.
+ */
+ q = symbol_name_temporary;
+ for (*q++=0; n; q++) /* emits NOTHING if n starts as 0 */
+ {
+ know(n>0); /* We expect n > 0 always */
+ *q = n % 10 + '0';
+ n /= 10;
+ }
+ while (( * p ++ = * -- q ) != '\0') ;;
+
+ /* The label, as a '\0' ended string, starts at symbol_name_build. */
+ return(symbol_name_build);
+} /* local_label_name() */
+
+
+void local_colon (n)
+int n; /* just saw "n:" */
+{
+ local_label_counter[n] ++;
+#ifdef LOCAL_LABELS_DOLLAR
+ local_label_defined[n]=1;
+#endif
+ colon (local_label_name (n, 0));
+}
+
+/*
+ * symbol_new()
+ *
+ * Return a pointer to a new symbol.
+ * Die if we can't make a new symbol.
+ * Fill in the symbol's values.
+ * Add symbol to end of symbol chain.
+ *
+ *
+ * Please always call this to create a new symbol.
+ *
+ * Changes since 1985: Symbol names may not contain '\0'. Sigh.
+ * 2nd argument is now a SEG rather than a TYPE. The mapping between
+ * segments and types is mostly encapsulated herein (actually, we inherit it
+ * from macros in struc-symbol.h).
+ */
+
+symbolS *symbol_new(name, segment, value, frag)
+char *name; /* It is copied, the caller can destroy/modify */
+segT segment; /* Segment identifier (SEG_<something>) */
+long value; /* Symbol value */
+fragS *frag; /* Associated fragment */
+{
+ unsigned int name_length;
+ char *preserved_copy_of_name;
+ symbolS *symbolP;
+
+ name_length = strlen(name) + 1; /* +1 for \0 */
+ obstack_grow(&notes, name, name_length);
+ preserved_copy_of_name = obstack_finish(&notes);
+ symbolP = (symbolS *) obstack_alloc(&notes, sizeof(symbolS));
+
+ /* symbol must be born in some fixed state. This seems as good as any. */
+ memset(symbolP, 0, sizeof(symbolS));
+
+#ifdef STRIP_UNDERSCORE
+ S_SET_NAME(symbolP, (*preserved_copy_of_name == '_'
+ ? preserved_copy_of_name + 1
+ : preserved_copy_of_name));
+#else /* STRIP_UNDERSCORE */
+ S_SET_NAME(symbolP, preserved_copy_of_name);
+#endif /* STRIP_UNDERSCORE */
+
+ S_SET_SEGMENT(symbolP, segment);
+ S_SET_VALUE(symbolP, value);
+ /* symbol_clear_list_pointers(symbolP); uneeded if symbol is born zeroed. */
+
+ symbolP->sy_frag = frag;
+ /* krm: uneeded if symbol is born zeroed.
+ symbolP->sy_forward = NULL; */ /* JF */
+ symbolP->sy_number = ~0;
+ symbolP->sy_name_offset = ~0;
+
+ /*
+ * Link to end of symbol chain.
+ */
+ symbol_append(symbolP, symbol_lastP, &symbol_rootP, &symbol_lastP);
+
+ obj_symbol_new_hook(symbolP);
+
+#ifdef DEBUG
+ /* verify_symbol_chain(symbol_rootP, symbol_lastP); */
+#endif /* DEBUG */
+
+ return(symbolP);
+} /* symbol_new() */
+
+
+/*
+ * colon()
+ *
+ * We have just seen "<name>:".
+ * Creates a struct symbol unless it already exists.
+ *
+ * Gripes if we are redefining a symbol incompatibly (and ignores it).
+ *
+ */
+void colon(sym_name) /* just seen "x:" - rattle symbols & frags */
+register char * sym_name; /* symbol name, as a cannonical string */
+/* We copy this string: OK to alter later. */
+{
+ register symbolS * symbolP; /* symbol we are working with */
+
+#ifdef LOCAL_LABELS_DOLLAR
+ /* Sun local labels go out of scope whenever a non-local symbol is defined. */
+
+ if (*sym_name != 'L')
+ memset((void *) local_label_defined, '\0', sizeof(local_label_defined));
+#endif
+
+#ifndef WORKING_DOT_WORD
+ if (new_broken_words) {
+ struct broken_word *a;
+ int possible_bytes;
+ fragS *frag_tmp;
+ char *frag_opcode;
+
+ extern const md_short_jump_size;
+ extern const md_long_jump_size;
+ possible_bytes=md_short_jump_size + new_broken_words * md_long_jump_size;
+
+ frag_tmp=frag_now;
+ frag_opcode=frag_var(rs_broken_word,
+ possible_bytes,
+ possible_bytes,
+ (relax_substateT) 0,
+ (symbolS *) broken_words,
+ 0L,
+ NULL);
+
+ /* We want to store the pointer to where to insert the jump table in the
+ fr_opcode of the rs_broken_word frag. This requires a little hackery */
+ while (frag_tmp && (frag_tmp->fr_type != rs_broken_word || frag_tmp->fr_opcode))
+ frag_tmp=frag_tmp->fr_next;
+ know(frag_tmp);
+ frag_tmp->fr_opcode=frag_opcode;
+ new_broken_words = 0;
+
+ for (a=broken_words;a && a->dispfrag == 0;a=a->next_broken_word)
+ a->dispfrag=frag_tmp;
+ }
+#endif
+ if ((symbolP = symbol_find(sym_name)) != 0) {
+#ifdef OBJ_VMS
+ /*
+ * If the new symbol is .comm AND it has a size of zero,
+ * we ignore it (i.e. the old symbol overrides it)
+ */
+ if ((SEGMENT_TO_SYMBOL_TYPE((int) now_seg) == (N_UNDF | N_EXT)) &&
+ ((obstack_next_free(& frags) - frag_now->fr_literal) == 0))
+ return;
+ /*
+ * If the old symbol is .comm and it has a size of zero,
+ * we override it with the new symbol value.
+ */
+ if (S_IS_EXTERNAL(symbolP) && S_IS_DEFINED(symbolP)
+ && (S_GET_VALUE(symbolP) == 0)) {
+ symbolP->sy_frag = frag_now;
+ S_GET_OTHER(symbolP) = const_flag;
+ S_SET_VALUE(symbolP, obstack_next_free(& frags) - frag_now->fr_literal);
+ symbolP->sy_symbol.n_type |=
+ SEGMENT_TO_SYMBOL_TYPE((int) now_seg); /* keep N_EXT bit */
+ return;
+ }
+#endif /* OBJ_VMS */
+ /*
+ * Now check for undefined symbols
+ */
+ if (!S_IS_DEFINED(symbolP)) {
+ if (S_GET_VALUE(symbolP) == 0) {
+ symbolP->sy_frag = frag_now;
+#ifdef OBJ_VMS
+ S_GET_OTHER(symbolP) = const_flag;
+#endif
+ S_SET_VALUE(symbolP, obstack_next_free(&frags) - frag_now->fr_literal);
+ S_SET_SEGMENT(symbolP, now_seg);
+#ifdef N_UNDF
+ know(N_UNDF == 0);
+#endif /* if we have one, it better be zero. */
+
+ } else {
+ /*
+ * There are still several cases to check:
+ * A .comm/.lcomm symbol being redefined as
+ * initialized data is OK
+ * A .comm/.lcomm symbol being redefined with
+ * a larger size is also OK
+ *
+ * This only used to be allowed on VMS gas, but Sun cc
+ * on the sparc also depends on it.
+ */
+ /* char New_Type = SEGMENT_TO_SYMBOL_TYPE((int) now_seg); */
+#ifdef MANY_SEGMENTS
+#define SEG_BSS SEG_E2
+#define SEG_DATA SEG_E1
+#endif
+
+ if (((!S_IS_DEBUG(symbolP) && !S_IS_DEFINED(symbolP) && S_IS_EXTERNAL(symbolP))
+ || (S_GET_SEGMENT(symbolP) == SEG_BSS))
+ && ((now_seg == SEG_DATA)
+ || (now_seg == S_GET_SEGMENT(symbolP)))) {
+ /*
+ * Select which of the 2 cases this is
+ */
+ if (now_seg != SEG_DATA) {
+ /*
+ * New .comm for prev .comm symbol.
+ * If the new size is larger we just
+ * change its value. If the new size
+ * is smaller, we ignore this symbol
+ */
+ if (S_GET_VALUE(symbolP)
+ < ((unsigned) (obstack_next_free(& frags) - frag_now->fr_literal))) {
+ S_SET_VALUE(symbolP,
+ obstack_next_free(& frags) -
+ frag_now->fr_literal);
+ }
+ } else {
+ /*
+ * It is a .comm/.lcomm being converted
+ * to initialized data.
+ */
+ symbolP->sy_frag = frag_now;
+#ifdef OBJ_VMS
+ S_GET_OTHER(symbolP) = const_flag;
+#endif /* OBJ_VMS */
+ S_SET_VALUE(symbolP, obstack_next_free(& frags) - frag_now->fr_literal);
+ S_SET_SEGMENT(symbolP, now_seg); /* keep N_EXT bit */
+ }
+ } else {
+#ifdef OBJ_COFF
+ as_fatal("Symbol \"%s\" is already defined as \"%s\"/%d.",
+ sym_name,
+ segment_name(S_GET_SEGMENT(symbolP)),
+ S_GET_VALUE(symbolP));
+#else /* OBJ_COFF */
+ as_fatal("Symbol \"%s\" is already defined as \"%s\"/%d.%d.%d.",
+ sym_name,
+ segment_name(S_GET_SEGMENT(symbolP)),
+ S_GET_OTHER(symbolP), S_GET_DESC(symbolP),
+ S_GET_VALUE(symbolP));
+#endif /* OBJ_COFF */
+ }
+ } /* if the undefined symbol has no value */
+ } else
+ {
+ /* Don't blow up if the definition is the same */
+ if (!(frag_now == symbolP->sy_frag
+ && S_GET_VALUE(symbolP) == obstack_next_free(&frags) - frag_now->fr_literal
+ && S_GET_SEGMENT(symbolP) == now_seg) )
+ as_fatal("Symbol %s already defined.", sym_name);
+ } /* if this symbol is not yet defined */
+
+ } else {
+ symbolP = symbol_new(sym_name,
+ now_seg,
+ (valueT)(obstack_next_free(&frags)-frag_now->fr_literal),
+ frag_now);
+#ifdef OBJ_VMS
+ S_SET_OTHER(symbolP, const_flag);
+#endif /* OBJ_VMS */
+
+ symbol_table_insert(symbolP);
+ } /* if we have seen this symbol before */
+
+ return;
+} /* colon() */
+
+
+/*
+ * symbol_table_insert()
+ *
+ * Die if we can't insert the symbol.
+ *
+ */
+
+void symbol_table_insert(symbolP)
+symbolS *symbolP;
+{
+ register char *error_string;
+
+ know(symbolP);
+ know(S_GET_NAME(symbolP));
+
+ if (*(error_string = hash_jam(sy_hash, S_GET_NAME(symbolP), (char *)symbolP))) {
+ as_fatal("Inserting \"%s\" into symbol table failed: %s",
+ S_GET_NAME(symbolP), error_string);
+ } /* on error */
+} /* symbol_table_insert() */
+
+/*
+ * symbol_find_or_make()
+ *
+ * If a symbol name does not exist, create it as undefined, and insert
+ * it into the symbol table. Return a pointer to it.
+ */
+symbolS *symbol_find_or_make(name)
+char *name;
+{
+ register symbolS *symbolP;
+
+ symbolP = symbol_find(name);
+
+ if (symbolP == NULL) {
+ symbolP = symbol_make(name);
+
+ symbol_table_insert(symbolP);
+ } /* if symbol wasn't found */
+
+ return(symbolP);
+} /* symbol_find_or_make() */
+
+symbolS *symbol_make(name)
+char *name;
+{
+ symbolS *symbolP;
+
+ /* Let the machine description default it, e.g. for register names. */
+ symbolP = md_undefined_symbol(name);
+
+ if (!symbolP) {
+ symbolP = symbol_new(name,
+ SEG_UNKNOWN,
+ 0,
+ &zero_address_frag);
+ } /* if md didn't build us a symbol */
+
+ return(symbolP);
+} /* symbol_make() */
+
+/*
+ * symbol_find()
+ *
+ * Implement symbol table lookup.
+ * In: A symbol's name as a string: '\0' can't be part of a symbol name.
+ * Out: NULL if the name was not in the symbol table, else the address
+ * of a struct symbol associated with that name.
+ */
+
+symbolS *symbol_find(name)
+char *name;
+{
+#ifdef STRIP_UNDERSCORE
+ return(symbol_find_base(name, 1));
+#else /* STRIP_UNDERSCORE */
+ return(symbol_find_base(name, 0));
+#endif /* STRIP_UNDERSCORE */
+} /* symbol_find() */
+
+symbolS *symbol_find_base(name, strip_underscore)
+char *name;
+int strip_underscore;
+{
+ if (strip_underscore && *name == '_') name++;
+ return ( (symbolS *) hash_find( sy_hash, name ));
+}
+
+/*
+ * Once upon a time, symbols were kept in a singly linked list. At
+ * least coff needs to be able to rearrange them from time to time, for
+ * which a doubly linked list is much more convenient. Loic did these
+ * as macros which seemed dangerous to me so they're now functions.
+ * xoxorich.
+ */
+
+/* Link symbol ADDME after symbol TARGET in the chain. */
+void symbol_append(addme, target, rootPP, lastPP)
+symbolS *addme;
+symbolS *target;
+symbolS **rootPP;
+symbolS **lastPP;
+{
+ if (target == NULL) {
+ know(*rootPP == NULL);
+ know(*lastPP == NULL);
+ *rootPP = addme;
+ *lastPP = addme;
+ return;
+ } /* if the list is empty */
+
+ if (target->sy_next != NULL) {
+#ifdef SYMBOLS_NEED_BACKPOINTERS
+ target->sy_next->sy_previous = addme;
+#endif /* SYMBOLS_NEED_BACKPOINTERS */
+ } else {
+ know(*lastPP == target);
+ *lastPP = addme;
+ } /* if we have a next */
+
+ addme->sy_next = target->sy_next;
+ target->sy_next = addme;
+
+#ifdef SYMBOLS_NEED_BACKPOINTERS
+ addme->sy_previous = target;
+#endif /* SYMBOLS_NEED_BACKPOINTERS */
+
+#ifdef DEBUG
+ /* verify_symbol_chain(*rootPP, *lastPP); */
+#endif /* DEBUG */
+
+ return;
+} /* symbol_append() */
+
+#ifdef SYMBOLS_NEED_BACKPOINTERS
+/* Remove SYMBOLP from the list. */
+void symbol_remove(symbolP, rootPP, lastPP)
+symbolS *symbolP;
+symbolS **rootPP;
+symbolS **lastPP;
+{
+ if (symbolP == *rootPP) {
+ *rootPP = symbolP->sy_next;
+ } /* if it was the root */
+
+ if (symbolP == *lastPP) {
+ *lastPP = symbolP->sy_previous;
+ } /* if it was the tail */
+
+ if (symbolP->sy_next != NULL) {
+ symbolP->sy_next->sy_previous = symbolP->sy_previous;
+ } /* if not last */
+
+ if (symbolP->sy_previous != NULL) {
+ symbolP->sy_previous->sy_next = symbolP->sy_next;
+ } /* if not first */
+
+#ifdef DEBUG
+ verify_symbol_chain(*rootPP, *lastPP);
+#endif /* DEBUG */
+
+ return;
+} /* symbol_remove() */
+
+/* Set the chain pointers of SYMBOL to null. */
+void symbol_clear_list_pointers(symbolP)
+symbolS *symbolP;
+{
+ symbolP->sy_next = NULL;
+ symbolP->sy_previous = NULL;
+} /* symbol_clear_list_pointers() */
+
+/* Link symbol ADDME before symbol TARGET in the chain. */
+void symbol_insert(addme, target, rootPP, lastPP)
+symbolS *addme;
+symbolS *target;
+symbolS **rootPP;
+symbolS **lastPP;
+{
+ if (target->sy_previous != NULL) {
+ target->sy_previous->sy_next = addme;
+ } else {
+ know(*rootPP == target);
+ *rootPP = addme;
+ } /* if not first */
+
+ addme->sy_previous = target->sy_previous;
+ target->sy_previous = addme;
+ addme->sy_next = target;
+
+#ifdef DEBUG
+ verify_symbol_chain(*rootPP, *lastPP);
+#endif /* DEBUG */
+
+ return;
+} /* symbol_insert() */
+#endif /* SYMBOLS_NEED_BACKPOINTERS */
+
+void verify_symbol_chain(rootP, lastP)
+symbolS *rootP;
+symbolS *lastP;
+{
+ symbolS *symbolP = rootP;
+
+ if (symbolP == NULL) {
+ return;
+ } /* empty chain */
+
+ for ( ; symbol_next(symbolP) != NULL; symbolP = symbol_next(symbolP)) {
+#ifdef SYMBOLS_NEED_BACKPOINTERS
+ /*$if (symbolP->sy_previous) {
+ know(symbolP->sy_previous->sy_next == symbolP);
+ } else {
+ know(symbolP == rootP);
+ }$*/ /* both directions */
+ know(symbolP->sy_next->sy_previous == symbolP);
+#else /* SYMBOLS_NEED_BACKPOINTERS */
+ ;
+#endif /* SYMBOLS_NEED_BACKPOINTERS */
+ } /* verify pointers */
+
+ know(lastP == symbolP);
+
+ return;
+} /* verify_symbol_chain() */
+
+
+/*
+ * decode name that may have been generated by local_label_name() above. If
+ * the name wasn't generated by local_label_name(), then return it unaltered.
+ * This is used for error messages.
+ */
+
+char *decode_local_label_name(s)
+char *s;
+{
+ char *symbol_decode;
+ int label_number;
+ /* int label_version; */
+ char *message_format = "\"%d\" (instance number %s of a local label)";
+
+ if (s[0] != 'L'
+ || s[2] != 1) {
+ return(s);
+ } /* not a local_label_name() generated name. */
+
+ label_number = s[1] - '0';
+
+ (void) sprintf(symbol_decode = obstack_alloc(&notes, strlen(s + 3) + strlen(message_format) + 10),
+ message_format, label_number, s + 3);
+
+ return(symbol_decode);
+} /* decode_local_label_name() */
+
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of symbols.c */
diff --git a/gnu/usr.bin/as/symbols.h b/gnu/usr.bin/as/symbols.h
new file mode 100644
index 0000000..535933f
--- /dev/null
+++ b/gnu/usr.bin/as/symbols.h
@@ -0,0 +1,82 @@
+/* symbols.h -
+
+ Copyright (C) 1987, 1990, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
+ * $Id: symbols.h,v 1.3 1993/10/02 20:57:57 pk Exp $
+ */
+
+
+extern struct obstack notes; /* eg FixS live here. */
+
+extern struct obstack cond_obstack; /* this is where we track .ifdef/.endif
+ (if we do that at all). */
+
+extern unsigned int local_bss_counter; /* Zeroed before a pass. */
+/* Only used by .lcomm directive. */
+
+extern symbolS *symbol_rootP; /* all the symbol nodes */
+extern symbolS *symbol_lastP; /* last struct symbol we made, or NULL */
+
+extern symbolS abs_symbol;
+
+extern symbolS *dot_text_symbol;
+extern symbolS *dot_data_symbol;
+extern symbolS *dot_bss_symbol;
+
+#if __STDC__ == 1
+
+char *decode_local_label_name(char *s);
+char *local_label_name(int n, int augend);
+symbolS *symbol_find(char *name);
+symbolS *symbol_find_base(char *name, int strip_underscore);
+symbolS *symbol_find_or_make(char *name);
+symbolS *symbol_make(char *name);
+symbolS *symbol_new(char *name, segT segment, long value, fragS *frag);
+void colon(char *sym_name);
+void local_colon(int n);
+void symbol_begin(void);
+void symbol_table_insert(symbolS *symbolP);
+void verify_symbol_chain(symbolS *rootP, symbolS *lastP);
+
+#else /* not __STDC__ */
+
+char *decode_local_label_name();
+char *local_label_name();
+symbolS *symbol_find();
+symbolS *symbol_find_base();
+symbolS *symbol_find_or_make();
+symbolS *symbol_make();
+symbolS *symbol_new();
+void colon();
+void local_colon();
+void symbol_begin();
+void symbol_table_insert();
+void verify_symbol_chain();
+
+#endif /* not __STDC__ */
+
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of symbols.h */
diff --git a/gnu/usr.bin/as/tc.h b/gnu/usr.bin/as/tc.h
new file mode 100644
index 0000000..f36629c
--- /dev/null
+++ b/gnu/usr.bin/as/tc.h
@@ -0,0 +1,112 @@
+/* tc.h - target cpu dependent
+
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
+ * $Id: tc.h,v 1.1 1993/10/02 20:57:58 pk Exp $
+ */
+
+
+/* In theory (mine, at least!) the machine dependent part of the assembler
+ should only have to include one file. This one. -- JF */
+
+extern const pseudo_typeS md_pseudo_table[];
+
+/* JF moved this here from as.h under the theory that nobody except MACHINE.c
+ and write.c care about it anyway. */
+
+typedef struct
+{
+ long rlx_forward; /* Forward reach. Signed number. > 0. */
+ long rlx_backward; /* Backward reach. Signed number. < 0. */
+ unsigned char rlx_length; /* Bytes length of this address. */
+ relax_substateT rlx_more; /* Next longer relax-state. */
+ /* 0 means there is no 'next' relax-state. */
+}
+relax_typeS;
+
+extern const relax_typeS md_relax_table[]; /* Define it in MACHINE.c */
+
+extern int md_reloc_size; /* Size of a relocation record */
+
+extern void (*md_emit_relocations)();
+
+#if __STDC__ == 1
+
+char *md_atof(int what_statement_type, char *literalP, int *sizeP);
+int md_estimate_size_before_relax(fragS *fragP, segT segment);
+int md_parse_option(char **argP, int *cntP, char ***vecP);
+long md_pcrel_from(fixS *fixP);
+long md_section_align(segT seg, long align);
+short tc_coff_fix2rtype(fixS *fixP);
+symbolS *md_undefined_symbol(char *name);
+void md_apply_fix(fixS *fixP, long val);
+void md_assemble(char *str);
+void md_begin(void);
+void md_convert_frag(object_headers *headers, fragS *fragP);
+void md_create_long_jump(char *ptr, long from_addr, long to_addr, fragS *frag, symbolS *to_symbol);
+void md_create_short_jump(char *ptr, long from_addr, long to_addr, fragS *frag, symbolS *to_symbol);
+void md_end(void);
+void md_number_to_chars(char *buf, long val, int n);
+void md_operand(expressionS *expressionP);
+
+#ifndef tc_crawl_symbol_chain
+void tc_crawl_symbol_chain(object_headers *headers);
+#endif /* tc_crawl_symbol_chain */
+
+#ifndef tc_headers_hook
+void tc_headers_hook(object_headers *headers);
+#endif /* tc_headers_hook */
+
+#else
+
+char *md_atof();
+int md_estimate_size_before_relax();
+int md_parse_option();
+long md_pcrel_from();
+long md_section_align();
+short tc_coff_fix2rtype();
+symbolS *md_undefined_symbol();
+void md_apply_fix();
+void md_assemble();
+void md_begin();
+void md_convert_frag();
+void md_create_long_jump();
+void md_create_short_jump();
+void md_end();
+void md_number_to_chars();
+void md_operand();
+
+#ifndef tc_headers_hook
+void tc_headers_hook();
+#endif /* tc_headers_hook */
+
+#ifndef tc_crawl_symbol_chain
+void tc_crawl_symbol_chain();
+#endif /* tc_crawl_symbol_chain */
+
+#endif /* __STDC_ */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of tc.h */
diff --git a/gnu/usr.bin/as/testscripts/doboth b/gnu/usr.bin/as/testscripts/doboth
new file mode 100755
index 0000000..6b46a03
--- /dev/null
+++ b/gnu/usr.bin/as/testscripts/doboth
@@ -0,0 +1,20 @@
+#!/bin/sh
+# $Id: doboth,v 1.1 1993/10/02 21:01:07 pk Exp $
+
+x=$1 ; shift
+y=$1 ; shift
+
+rm tmp.0 > /dev/null 2>&1
+ln -s $x tmp.0
+$* tmp.0 > tmp.1
+
+rm tmp.0
+ln -s $y tmp.0
+$* tmp.0 > tmp.2
+
+rm tmp.0
+
+diff -c tmp.1 tmp.2
+exit
+
+#eof
diff --git a/gnu/usr.bin/as/testscripts/doobjcmp b/gnu/usr.bin/as/testscripts/doobjcmp
new file mode 100755
index 0000000..6c90cf9
--- /dev/null
+++ b/gnu/usr.bin/as/testscripts/doobjcmp
@@ -0,0 +1,89 @@
+#!/bin/sh
+# $Id: doobjcmp,v 1.1 1993/10/02 21:01:08 pk Exp $
+# compare two object files, in depth.
+
+x=$1
+y=$2
+BOTH="$1 $2"
+
+
+# if they cmp, we're fine.
+if (cmp $BOTH > /dev/null)
+then
+ exit 0
+fi
+
+# otherwise, we must look closer.
+if (doboth $BOTH size)
+then
+ echo Sizes ok.
+else
+ echo Sizes differ:
+ size $BOTH
+# exit 1
+fi
+
+if (doboth $BOTH objdump +header)
+then
+ echo Headers ok.
+else
+ echo Header differences.
+# exit 1
+fi
+
+if (doboth $BOTH objdump +text > /dev/null)
+then
+ echo Text ok.
+else
+ echo Text differences.
+# doboth $BOTH objdump +text
+# exit 1
+fi
+
+if (doboth $BOTH objdump +data > /dev/null)
+then
+ echo Data ok.
+else
+ echo Data differences.
+# doboth $BOTH objdump +data
+# exit 1
+fi
+
+if (doboth $BOTH objdump +symbols > /dev/null)
+then
+ echo Symbols ok.
+else
+ echo -n Symbol differences...
+
+ if (doboth $BOTH dounsortsymbols)
+ then
+ echo but symbols are simply ordered differently.
+# echo Now what to do about relocs'?'
+# exit 1
+ else
+ echo and symbols differ in content.
+ exit 1
+ fi
+fi
+
+# of course, if there were symbol diffs, then the reloc symbol indexes
+# will be off.
+
+if (doboth $BOTH objdump -r > /dev/null)
+then
+ echo Reloc ok.
+else
+ echo -n Reloc differences...
+
+ if (doboth $BOTH dounsortreloc)
+ then
+ echo but relocs are simply ordered differently.
+ else
+ echo and relocs differ in content.
+ exit 1
+ fi
+fi
+
+exit
+
+# eof
diff --git a/gnu/usr.bin/as/testscripts/dostriptest b/gnu/usr.bin/as/testscripts/dostriptest
new file mode 100755
index 0000000..aa734c0
--- /dev/null
+++ b/gnu/usr.bin/as/testscripts/dostriptest
@@ -0,0 +1,15 @@
+#!/bin/sh
+# $Id: dostriptest,v 1.1 1993/10/02 21:01:09 pk Exp $
+
+x=striptest.xx.$$
+y=striptest.yy.$$
+
+cp $1 $x
+strip $x
+cp $2 $y
+strip $y
+
+doobjcmp $x $y
+exit
+
+#eof
diff --git a/gnu/usr.bin/as/testscripts/dotest b/gnu/usr.bin/as/testscripts/dotest
new file mode 100755
index 0000000..051ee11e
--- /dev/null
+++ b/gnu/usr.bin/as/testscripts/dotest
@@ -0,0 +1,44 @@
+#!/bin/sh
+# ad hoc debug tool
+# $Id: dotest,v 1.1 1993/10/02 21:01:10 pk Exp $
+
+x=$1
+y=$2
+
+xout=`basename $x`.xxx.$$
+yout=`basename $x`.yyy.$$
+
+mkdir $xout
+mkdir $yout
+
+for i in *.s
+do
+ echo Testing $i...
+ object=`basename $i .s`.o
+ $x $i -o $xout/$object
+ $y $i -o $yout/$object
+
+# if they cmp, we're ok. Otherwise we have to look closer.
+
+ if (cmp $xout/$object $yout/$object)
+ then
+ echo $i is ok.
+ else
+ if (doobjcmp $xout/$object $yout/$object)
+ then
+ echo Not the same but objcmp ok.
+ else
+ exit 1
+ fi
+ fi
+
+ echo
+done
+
+rm -rf $xout $yout
+
+exit 0
+
+# EOF
+
+
diff --git a/gnu/usr.bin/as/testscripts/dounsortreloc b/gnu/usr.bin/as/testscripts/dounsortreloc
new file mode 100755
index 0000000..0a4771c
--- /dev/null
+++ b/gnu/usr.bin/as/testscripts/dounsortreloc
@@ -0,0 +1,9 @@
+#!/bin/sh
+# $Id: dounsortreloc,v 1.1 1993/10/02 21:01:11 pk Exp $
+# objdump the reloc table, but strip off the headings and reloc
+# numbers and sort the result. Intended for use in comparing reloc
+# tables that may not be in the same order.
+
+objdump +reloc +omit-relocation-numbers +omit-symbol-numbers $1 \
+ | sort
+#eof
diff --git a/gnu/usr.bin/as/testscripts/dounsortsymbols b/gnu/usr.bin/as/testscripts/dounsortsymbols
new file mode 100755
index 0000000..2dc5acd
--- /dev/null
+++ b/gnu/usr.bin/as/testscripts/dounsortsymbols
@@ -0,0 +1,9 @@
+#!/bin/sh
+# $Id: dounsortsymbols,v 1.1 1993/10/02 21:01:12 pk Exp $
+# objdump the symbol table, but strip off the headings and symbol
+# numbers and sort the result. Intended for use in comparing symbol
+# tables that may not be in the same order.
+
+objdump +symbols +omit-symbol-numbers $1 \
+ | sort
+#eof
diff --git a/gnu/usr.bin/as/version.c b/gnu/usr.bin/as/version.c
new file mode 100644
index 0000000..b3711c4
--- /dev/null
+++ b/gnu/usr.bin/as/version.c
@@ -0,0 +1,30 @@
+#if (__STDC__ == 1) || defined(const)
+const
+#endif
+
+/* DO NOT PUT COMMENTS ABOUT CHANGES IN THIS FILE.
+
+ This file exists only to define `version_string'.
+
+ Log changes in ChangeLog. The easiest way to do this is with
+ the Emacs command `add-change-log-entry'. If you don't use Emacs,
+ add entries of the form:
+
+ Thu Jan 1 00:00:00 1970 Dennis Ritchie (dmr at alice)
+
+ universe.c (temporal_reality): Began Time.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: version.c,v 1.2 1993/11/03 00:52:27 paul Exp $";
+#endif
+
+char version_string[] = "GNU assembler version 1.92.3, FreeBSD $Revision: 1.2 $\n";
+
+#ifdef HO_VMS
+dummy3()
+{
+}
+#endif
+
+/* end of version.c */
diff --git a/gnu/usr.bin/as/write.c b/gnu/usr.bin/as/write.c
new file mode 100644
index 0000000..dd139f7
--- /dev/null
+++ b/gnu/usr.bin/as/write.c
@@ -0,0 +1,1213 @@
+/* write.c - emit .o file
+
+ Copyright (C) 1986, 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This thing should be set up to do byteordering correctly. But... */
+
+#ifndef lint
+static char rcsid[] = "$Id: write.c,v 1.4 1993/12/12 17:01:24 jkh Exp $";
+#endif
+
+#include "as.h"
+#include "subsegs.h"
+#include "obstack.h"
+#include "output-file.h"
+
+/* The NOP_OPCODE is for the alignment fill value.
+ * fill it a nop instruction so that the disassembler does not choke
+ * on it
+ */
+#ifndef NOP_OPCODE
+#define NOP_OPCODE 0x00
+#endif
+
+#ifndef MANY_SEGMENTS
+static struct frag *text_frag_root;
+static struct frag *data_frag_root;
+
+static struct frag *text_last_frag; /* Last frag in segment. */
+static struct frag *data_last_frag; /* Last frag in segment. */
+#endif
+
+#ifndef WORKING_DOT_WORD
+extern const int md_short_jump_size;
+extern const int md_long_jump_size;
+#endif
+
+static object_headers headers;
+
+long string_byte_count;
+
+static char *the_object_file;
+
+char *next_object_file_charP; /* Tracks object file bytes. */
+
+/* static long length; JF unused */ /* String length, including trailing '\0'. */
+
+
+#if __STDC__ == 1
+
+static int is_dnrange(struct frag *f1, struct frag *f2);
+static long fixup_segment(fixS *fixP, segT this_segment_type);
+static relax_addressT relax_align(relax_addressT address, long alignment);
+void relax_segment(struct frag *segment_frag_root, segT segment_type);
+
+#else
+
+static int is_dnrange();
+static long fixup_segment();
+static relax_addressT relax_align();
+void relax_segment();
+
+#endif /* not __STDC__ */
+
+/*
+ * fix_new()
+ *
+ * Create a fixS in obstack 'notes'.
+ */
+#ifdef PIC
+fixS *fix_new(frag, where, size, add_symbol, sub_symbol, offset, pcrel, r_type, got_symbol)
+#else
+fixS *fix_new(frag, where, size, add_symbol, sub_symbol, offset, pcrel, r_type)
+#endif
+fragS *frag; /* Which frag? */
+int where; /* Where in that frag? */
+short int size; /* 1, 2, or 4 usually. */
+symbolS *add_symbol; /* X_add_symbol. */
+symbolS *sub_symbol; /* X_subtract_symbol. */
+#ifdef PIC
+symbolS *got_symbol; /* X_got. */
+#endif
+long offset; /* X_add_number. */
+int pcrel; /* TRUE if PC-relative relocation. */
+enum reloc_type r_type; /* Relocation type */
+{
+ fixS *fixP;
+
+ fixP = (fixS *) obstack_alloc(&notes, sizeof(fixS));
+
+ fixP->fx_frag = frag;
+ fixP->fx_where = where;
+ fixP->fx_size = size;
+ fixP->fx_addsy = add_symbol;
+ fixP->fx_subsy = sub_symbol;
+#ifdef PIC
+ fixP->fx_gotsy = got_symbol;
+ if (got_symbol)
+ pcrel = 1;
+#endif
+ fixP->fx_offset = offset;
+ fixP->fx_pcrel = pcrel;
+ fixP->fx_r_type = r_type;
+
+ /* JF these 'cuz of the NS32K stuff */
+ fixP->fx_im_disp = 0;
+ fixP->fx_pcrel_adjust = 0;
+ fixP->fx_bsr = 0;
+ fixP->fx_bit_fixP = 0;
+
+ /* usually, we want relocs sorted numerically, but while
+ comparing to older versions of gas that have relocs
+ reverse sorted, it is convenient to have this compile
+ time option. xoxorich. */
+
+#ifdef REVERSE_SORT_RELOCS
+
+ fixP->fx_next = *seg_fix_rootP;
+ *seg_fix_rootP = fixP;
+
+#else /* REVERSE_SORT_RELOCS */
+
+ fixP->fx_next = NULL;
+
+ if (*seg_fix_tailP)
+ (*seg_fix_tailP)->fx_next = fixP;
+ else
+ *seg_fix_rootP = fixP;
+ *seg_fix_tailP = fixP;
+
+#endif /* REVERSE_SORT_RELOCS */
+
+ fixP->fx_callj = 0;
+ return(fixP);
+} /* fix_new() */
+
+#ifndef BFD
+void write_object_file()
+{
+ register struct frchain * frchainP; /* Track along all frchains. */
+ register fragS * fragP; /* Track along all frags. */
+ register struct frchain * next_frchainP;
+ register fragS * * prev_fragPP;
+ /* register char * name; */
+ /* symbolS *symbolP; */
+ /* register symbolS ** symbolPP; */
+ /* register fixS * fixP; JF unused */
+ unsigned int data_siz;
+
+ long object_file_size;
+
+#ifdef OBJ_VMS
+ /*
+ * Under VMS we try to be compatible with VAX-11 "C". Thus, we
+ * call a routine to check for the definition of the procedure
+ * "_main", and if so -- fix it up so that it can be program
+ * entry point.
+ */
+ VMS_Check_For_Main();
+#endif /* OBJ_VMS */
+ /*
+ * After every sub-segment, we fake an ".align ...". This conforms to BSD4.2
+ * brane-damage. We then fake ".fill 0" because that is the kind of frag
+ * that requires least thought. ".align" frags like to have a following
+ * frag since that makes calculating their intended length trivial.
+ */
+#ifndef SUB_SEGMENT_ALIGN
+#define SUB_SEGMENT_ALIGN (2)
+#endif
+ for (frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next) {
+#ifdef OBJ_VMS
+ /*
+ * Under VAX/VMS, the linker (and PSECT specifications)
+ * take care of correctly aligning the segments.
+ * Doing the alignment here (on initialized data) can
+ * mess up the calculation of global data PSECT sizes.
+ */
+#undef SUB_SEGMENT_ALIGN
+#define SUB_SEGMENT_ALIGN ((frchainP->frch_seg != SEG_DATA) ? 2 : 0)
+#endif /* OBJ_VMS */
+ subseg_new (frchainP->frch_seg, frchainP->frch_subseg);
+ frag_align (SUB_SEGMENT_ALIGN, NOP_OPCODE);
+ /* frag_align will have left a new frag. */
+ /* Use this last frag for an empty ".fill". */
+ /*
+ * For this segment ...
+ * Create a last frag. Do not leave a "being filled in frag".
+ */
+ frag_wane (frag_now);
+ frag_now->fr_fix = 0;
+ know( frag_now->fr_next == NULL );
+ /* know(frags.obstack_c_base == frags.obstack_c_next_free); */
+ /* Above shows we haven't left a half-completed object on obstack. */
+ } /* walk the frag chain */
+
+ /*
+ * From now on, we don't care about sub-segments.
+ * Build one frag chain for each segment. Linked thru fr_next.
+ * We know that there is at least 1 text frchain & at least 1 data frchain.
+ */
+ prev_fragPP = &text_frag_root;
+ for (frchainP = frchain_root; frchainP; frchainP = next_frchainP) {
+ know( frchainP->frch_root );
+ *prev_fragPP = frchainP->frch_root;
+ prev_fragPP = & frchainP->frch_last->fr_next;
+
+ if (((next_frchainP = frchainP->frch_next) == NULL)
+ || next_frchainP == data0_frchainP) {
+ prev_fragPP = &data_frag_root;
+ if (next_frchainP) {
+ text_last_frag = frchainP->frch_last;
+ } else {
+ data_last_frag = frchainP->frch_last;
+ }
+ }
+ } /* walk the frag chain */
+
+ /*
+ * We have two segments. If user gave -R flag, then we must put the
+ * data frags into the text segment. Do this before relaxing so
+ * we know to take advantage of -R and make shorter addresses.
+ */
+ if (flagseen[ 'R' ]) {
+ fixS *tmp;
+
+ text_last_frag->fr_next = data_frag_root;
+ text_last_frag = data_last_frag;
+ data_last_frag = NULL;
+ data_frag_root = NULL;
+ if (text_fix_root) {
+ for (tmp = text_fix_root; tmp->fx_next; tmp = tmp->fx_next) ;;
+ tmp->fx_next = data_fix_root;
+ } else
+ text_fix_root = data_fix_root;
+ data_fix_root = NULL;
+ }
+
+ relax_segment(text_frag_root, SEG_TEXT);
+ relax_segment(data_frag_root, SEG_DATA);
+ /*
+ * Now the addresses of frags are correct within the segment.
+ */
+
+ know(text_last_frag->fr_type == rs_fill && text_last_frag->fr_offset == 0);
+ H_SET_TEXT_SIZE(&headers, text_last_frag->fr_address);
+ text_last_frag->fr_address = H_GET_TEXT_SIZE(&headers);
+
+ /*
+ * Join the 2 segments into 1 huge segment.
+ * To do this, re-compute every rn_address in the SEG_DATA frags.
+ * Then join the data frags after the text frags.
+ *
+ * Determine a_data [length of data segment].
+ */
+ if (data_frag_root) {
+ register relax_addressT slide;
+
+ know((text_last_frag->fr_type == rs_fill) && (text_last_frag->fr_offset == 0));
+
+ H_SET_DATA_SIZE(&headers, data_last_frag->fr_address);
+ data_last_frag->fr_address = H_GET_DATA_SIZE(&headers);
+ slide = H_GET_TEXT_SIZE(&headers); /* & in file of the data segment. */
+
+ for (fragP = data_frag_root; fragP; fragP = fragP->fr_next) {
+ fragP->fr_address += slide;
+ } /* for each data frag */
+
+ know(text_last_frag != 0);
+ text_last_frag->fr_next = data_frag_root;
+ } else {
+ H_SET_DATA_SIZE(&headers,0);
+ data_siz = 0;
+ }
+
+ bss_address_frag.fr_address = (H_GET_TEXT_SIZE(&headers) +
+ H_GET_DATA_SIZE(&headers));
+
+ H_SET_BSS_SIZE(&headers,local_bss_counter);
+
+ /*
+ *
+ * Crawl the symbol chain.
+ *
+ * For each symbol whose value depends on a frag, take the address of
+ * that frag and subsume it into the value of the symbol.
+ * After this, there is just one way to lookup a symbol value.
+ * Values are left in their final state for object file emission.
+ * We adjust the values of 'L' local symbols, even if we do
+ * not intend to emit them to the object file, because their values
+ * are needed for fix-ups.
+ *
+ * Unless we saw a -L flag, remove all symbols that begin with 'L'
+ * from the symbol chain. (They are still pointed to by the fixes.)
+ *
+ * Count the remaining symbols.
+ * Assign a symbol number to each symbol.
+ * Count the number of string-table chars we will emit.
+ * Put this info into the headers as appropriate.
+ *
+ */
+ know(zero_address_frag.fr_address == 0);
+ string_byte_count = sizeof(string_byte_count);
+
+ obj_crawl_symbol_chain(&headers);
+
+ if (string_byte_count == sizeof(string_byte_count)) {
+ string_byte_count = 0;
+ } /* if no strings, then no count. */
+
+ H_SET_STRING_SIZE(&headers, string_byte_count);
+
+ /*
+ * Addresses of frags now reflect addresses we use in the object file.
+ * Symbol values are correct.
+ * Scan the frags, converting any ".org"s and ".align"s to ".fill"s.
+ * Also converting any machine-dependent frags using md_convert_frag();
+ */
+ subseg_change(SEG_TEXT, 0);
+
+ for (fragP = text_frag_root; fragP; fragP = fragP->fr_next) {
+ switch (fragP->fr_type) {
+ case rs_align:
+ case rs_org:
+ fragP->fr_type = rs_fill;
+ know(fragP->fr_var == 1);
+ know(fragP->fr_next != NULL);
+
+ fragP->fr_offset = (fragP->fr_next->fr_address
+ - fragP->fr_address
+ - fragP->fr_fix);
+ break;
+
+ case rs_fill:
+ break;
+
+ case rs_machine_dependent:
+ md_convert_frag(&headers, fragP);
+
+ know((fragP->fr_next == NULL) || ((fragP->fr_next->fr_address - fragP->fr_address) == fragP->fr_fix));
+
+ /*
+ * After md_convert_frag, we make the frag into a ".space 0".
+ * Md_convert_frag() should set up any fixSs and constants
+ * required.
+ */
+ frag_wane(fragP);
+ break;
+
+#ifndef WORKING_DOT_WORD
+ case rs_broken_word: {
+ struct broken_word *lie;
+
+ if (fragP->fr_subtype) {
+ fragP->fr_fix+=md_short_jump_size;
+ for (lie=(struct broken_word *)(fragP->fr_symbol);lie && lie->dispfrag == fragP;lie=lie->next_broken_word)
+ if (lie->added == 1)
+ fragP->fr_fix+=md_long_jump_size;
+ }
+ frag_wane(fragP);
+ }
+ break;
+#endif
+
+ default:
+ BAD_CASE( fragP->fr_type );
+ break;
+ } /* switch (fr_type) */
+
+ know((fragP->fr_next == NULL)
+ || ((fragP->fr_next->fr_address - fragP->fr_address)
+ == (fragP->fr_fix + (fragP->fr_offset * fragP->fr_var))));
+ } /* for each frag. */
+
+#ifndef WORKING_DOT_WORD
+ {
+ struct broken_word *lie;
+ struct broken_word **prevP;
+
+ prevP = &broken_words;
+ for (lie = broken_words; lie; lie = lie->next_broken_word)
+ if (!lie->added) {
+#ifdef TC_NS32K
+ fix_new_ns32k(lie->frag,
+ lie->word_goes_here - lie->frag->fr_literal,
+ 2,
+ lie->add,
+ lie->sub,
+ lie->addnum,
+ 0, 0, 2, 0, 0);
+#else /* TC_NS32K */
+#ifdef PIC
+ fix_new(lie->frag, lie->word_goes_here - lie->frag->fr_literal,
+ 2, lie->add,
+ lie->sub, lie->addnum,
+ 0, NO_RELOC, (symbolS *)0);
+#else
+ fix_new(lie->frag, lie->word_goes_here - lie->frag->fr_literal,
+ 2, lie->add,
+ lie->sub, lie->addnum,
+ 0, NO_RELOC);
+#endif
+#endif /* TC_NS32K */
+ /* md_number_to_chars(lie->word_goes_here,
+ S_GET_VALUE(lie->add)
+ + lie->addnum
+ - S_GET_VALUE(lie->sub),
+ 2); */
+ *prevP = lie->next_broken_word;
+ } else
+ prevP = &(lie->next_broken_word);
+
+ for (lie = broken_words; lie;) {
+ struct broken_word *untruth;
+ char *table_ptr;
+ long table_addr;
+ long from_addr,
+ to_addr;
+ int n,
+ m;
+
+ fragP = lie->dispfrag;
+
+ /* Find out how many broken_words go here */
+ n=0;
+ for (untruth = lie; untruth && untruth->dispfrag == fragP; untruth = untruth->next_broken_word)
+ if (untruth->added == 1)
+ n++;
+
+ table_ptr = lie->dispfrag->fr_opcode;
+ table_addr = lie->dispfrag->fr_address + (table_ptr - lie->dispfrag->fr_literal);
+ /* Create the jump around the long jumps */
+ /* This is a short jump from table_ptr+0 to table_ptr+n*long_jump_size */
+ from_addr = table_addr;
+ to_addr = table_addr + md_short_jump_size + n * md_long_jump_size;
+ md_create_short_jump(table_ptr, from_addr, to_addr, lie->dispfrag, lie->add);
+ table_ptr += md_short_jump_size;
+ table_addr += md_short_jump_size;
+
+ for (m = 0; lie && lie->dispfrag == fragP; m++, lie = lie->next_broken_word) {
+ if (lie->added == 2)
+ continue;
+ /* Patch the jump table */
+ /* This is the offset from ??? to table_ptr+0 */
+ to_addr = table_addr
+ - S_GET_VALUE(lie->sub);
+ md_number_to_chars(lie->word_goes_here, to_addr, 2);
+ for (untruth = lie->next_broken_word;
+ untruth && untruth->dispfrag == fragP;
+ untruth = untruth->next_broken_word) {
+ if (untruth->use_jump == lie)
+ md_number_to_chars(untruth->word_goes_here, to_addr, 2);
+ }
+
+ /* Install the long jump */
+ /* this is a long jump from table_ptr+0 to the final target */
+ from_addr = table_addr;
+ to_addr = S_GET_VALUE(lie->add) + lie->addnum;
+ md_create_long_jump(table_ptr, from_addr, to_addr, lie->dispfrag, lie->add);
+ table_ptr += md_long_jump_size;
+ table_addr += md_long_jump_size;
+ }
+ }
+ }
+#endif /* not WORKING_DOT_WORD */
+
+#ifndef OBJ_VMS
+ { /* not vms */
+ /*
+ * Scan every FixS performing fixups. We had to wait until now to do
+ * this because md_convert_frag() may have made some fixSs.
+ */
+
+ H_SET_RELOCATION_SIZE(&headers,
+ md_reloc_size * fixup_segment(text_fix_root, SEG_TEXT),
+ md_reloc_size * fixup_segment(data_fix_root, SEG_DATA));
+
+
+ obj_pre_write_hook(&headers);
+
+ if ((had_warnings() && flagseen['Z'])
+ || had_errors() > 0) {
+ if (flagseen['Z']) {
+ as_warn("%d error%s, %d warning%s, generating bad object file.\n",
+ had_errors(), had_errors() == 1 ? "" : "s",
+ had_warnings(), had_warnings() == 1 ? "" : "s");
+ } else {
+ as_fatal("%d error%s, %d warning%s, no object file generated.\n",
+ had_errors(), had_errors() == 1 ? "" : "s",
+ had_warnings(), had_warnings() == 1 ? "" : "s");
+ } /* on want output */
+ } /* on error condition */
+
+ object_file_size = H_GET_FILE_SIZE(&headers);
+ next_object_file_charP = the_object_file = xmalloc(object_file_size);
+
+ output_file_create(out_file_name);
+
+ obj_header_append(&next_object_file_charP, &headers);
+
+ know((next_object_file_charP - the_object_file) == H_GET_HEADER_SIZE(&headers));
+
+ /*
+ * Emit code.
+ */
+ for (fragP = text_frag_root; fragP; fragP = fragP->fr_next) {
+ register long count;
+ register char *fill_literal;
+ register long fill_size;
+
+ know(fragP->fr_type == rs_fill);
+ append(&next_object_file_charP, fragP->fr_literal, (unsigned long) fragP->fr_fix);
+ fill_literal = fragP->fr_literal + fragP->fr_fix;
+ fill_size = fragP->fr_var;
+ know(fragP->fr_offset >= 0);
+
+ for (count = fragP->fr_offset; count; count--) {
+ append(&next_object_file_charP, fill_literal, (unsigned long) fill_size);
+ } /* for each */
+
+ } /* for each code frag. */
+
+ know((next_object_file_charP - the_object_file) == (H_GET_HEADER_SIZE(&headers) + H_GET_TEXT_SIZE(&headers) + H_GET_DATA_SIZE(&headers)));
+
+ /*
+ * Emit relocations.
+ */
+ obj_emit_relocations(&next_object_file_charP, text_fix_root, (relax_addressT)0);
+ know((next_object_file_charP - the_object_file) == (H_GET_HEADER_SIZE(&headers) + H_GET_TEXT_SIZE(&headers) + H_GET_DATA_SIZE(&headers) + H_GET_TEXT_RELOCATION_SIZE(&headers)));
+#ifdef TC_I960
+ /* Make addresses in data relocation directives relative to beginning of
+ * first data fragment, not end of last text fragment: alignment of the
+ * start of the data segment may place a gap between the segments.
+ */
+ obj_emit_relocations(&next_object_file_charP, data_fix_root, data0_frchainP->frch_root->fr_address);
+#else /* TC_I960 */
+ obj_emit_relocations(&next_object_file_charP, data_fix_root, text_last_frag->fr_address);
+#endif /* TC_I960 */
+
+ know((next_object_file_charP - the_object_file) == (H_GET_HEADER_SIZE(&headers) + H_GET_TEXT_SIZE(&headers) + H_GET_DATA_SIZE(&headers) + H_GET_TEXT_RELOCATION_SIZE(&headers) + H_GET_DATA_RELOCATION_SIZE(&headers)));
+
+ /*
+ * Emit line number entries.
+ */
+ OBJ_EMIT_LINENO(&next_object_file_charP, lineno_rootP, the_object_file);
+ know((next_object_file_charP - the_object_file) == (H_GET_HEADER_SIZE(&headers) + H_GET_TEXT_SIZE(&headers) + H_GET_DATA_SIZE(&headers) + H_GET_TEXT_RELOCATION_SIZE(&headers) + H_GET_DATA_RELOCATION_SIZE(&headers) + H_GET_LINENO_SIZE(&headers)));
+
+ /*
+ * Emit symbols.
+ */
+ obj_emit_symbols(&next_object_file_charP, symbol_rootP);
+ know((next_object_file_charP - the_object_file) == (H_GET_HEADER_SIZE(&headers) + H_GET_TEXT_SIZE(&headers) + H_GET_DATA_SIZE(&headers) + H_GET_TEXT_RELOCATION_SIZE(&headers) + H_GET_DATA_RELOCATION_SIZE(&headers) + H_GET_LINENO_SIZE(&headers) + H_GET_SYMBOL_TABLE_SIZE(&headers)));
+
+ /*
+ * Emit strings.
+ */
+
+ if (string_byte_count > 0) {
+ obj_emit_strings(&next_object_file_charP);
+ } /* only if we have a string table */
+
+ /* know((next_object_file_charP - the_object_file) == (H_GET_HEADER_SIZE(&headers) + H_GET_TEXT_SIZE(&headers) + H_GET_DATA_SIZE(&headers) + H_GET_TEXT_RELOCATION_SIZE(&headers) + H_GET_DATA_RELOCATION_SIZE(&headers) + H_GET_LINENO_SIZE(&headers) + H_GET_SYMBOL_TABLE_SIZE(&headers) + H_GET_STRING_SIZE(&headers)));
+ */
+ /* know(next_object_file_charP == the_object_file + object_file_size);*/
+
+#ifdef BFD_HEADERS
+ bfd_seek(stdoutput, 0, 0);
+ bfd_write(the_object_file, 1, object_file_size, stdoutput);
+#else
+
+ /* Write the data to the file */
+ output_file_append(the_object_file, object_file_size, out_file_name);
+#endif
+
+ output_file_close(out_file_name);
+ } /* non vms output */
+#else /* OBJ_VMS */
+ /*
+ * Now do the VMS-dependent part of writing the object file
+ */
+ VMS_write_object_file(H_GET_TEXT_SIZE(&headers), H_GET_DATA_SIZE(&headers),
+ text_frag_root, data_frag_root);
+#endif /* OBJ_VMS */
+} /* write_object_file() */
+#else
+#endif
+
+/*
+ * relax_segment()
+ *
+ * Now we have a segment, not a crowd of sub-segments, we can make fr_address
+ * values.
+ *
+ * Relax the frags.
+ *
+ * After this, all frags in this segment have addresses that are correct
+ * within the segment. Since segments live in different file addresses,
+ * these frag addresses may not be the same as final object-file addresses.
+ */
+
+
+
+void relax_segment(segment_frag_root, segment)
+struct frag * segment_frag_root;
+segT segment; /* SEG_DATA or SEG_TEXT */
+{
+ register struct frag * fragP;
+ register relax_addressT address;
+ /* register relax_addressT old_address; JF unused */
+ /* register relax_addressT new_address; JF unused */
+#ifndef MANY_SEGMENTS
+ know(segment == SEG_DATA || segment == SEG_TEXT);
+#endif
+ /* In case md_estimate_size_before_relax() wants to make fixSs. */
+ subseg_change(segment, 0);
+
+ /*
+ * For each frag in segment: count and store (a 1st guess of) fr_address.
+ */
+ address = 0;
+ for (fragP = segment_frag_root; fragP; fragP = fragP->fr_next) {
+ fragP->fr_address = address;
+ address += fragP->fr_fix;
+
+ switch (fragP->fr_type) {
+ case rs_fill:
+ address += fragP->fr_offset * fragP->fr_var ;
+ break;
+
+ case rs_align:
+ address += relax_align(address, fragP->fr_offset);
+ break;
+
+ case rs_org:
+ /*
+ * Assume .org is nugatory. It will grow with 1st relax.
+ */
+ break;
+
+ case rs_machine_dependent:
+ address += md_estimate_size_before_relax(fragP, segment);
+ break;
+
+#ifndef WORKING_DOT_WORD
+ /* Broken words don't concern us yet */
+ case rs_broken_word:
+ break;
+#endif
+
+ default:
+ BAD_CASE(fragP->fr_type);
+ break;
+ } /* switch (fr_type) */
+ } /* for each frag in the segment */
+
+ /*
+ * Do relax().
+ */
+ {
+ register long stretch; /* May be any size, 0 or negative. */
+ /* Cumulative number of addresses we have */
+ /* relaxed this pass. */
+ /* We may have relaxed more than one address. */
+ register long stretched; /* Have we stretched on this pass? */
+ /* This is 'cuz stretch may be zero, when,
+ in fact some piece of code grew, and
+ another shrank. If a branch instruction
+ doesn't fit anymore, we could be scrod */
+
+ do {
+ stretch = stretched = 0;
+ for (fragP = segment_frag_root; fragP; fragP = fragP->fr_next) {
+ register long growth = 0;
+ register unsigned long was_address;
+ register long offset;
+ register symbolS *symbolP;
+ register long target;
+ register long after;
+ register long aim;
+
+ was_address = fragP->fr_address;
+ address = fragP->fr_address += stretch;
+ symbolP = fragP->fr_symbol;
+ offset = fragP->fr_offset;
+
+ switch (fragP->fr_type) {
+ case rs_fill: /* .fill never relaxes. */
+ growth = 0;
+ break;
+
+#ifndef WORKING_DOT_WORD
+ /* JF: This is RMS's idea. I do *NOT* want to be blamed
+ for it I do not want to write it. I do not want to have
+ anything to do with it. This is not the proper way to
+ implement this misfeature. */
+ case rs_broken_word: {
+ struct broken_word *lie;
+ struct broken_word *untruth;
+
+ /* Yes this is ugly (storing the broken_word pointer
+ in the symbol slot). Still, this whole chunk of
+ code is ugly, and I don't feel like doing anything
+ about it. Think of it as stubbornness in action */
+ growth=0;
+ for (lie=(struct broken_word *)(fragP->fr_symbol);
+ lie && lie->dispfrag == fragP;
+ lie=lie->next_broken_word) {
+
+ if (lie->added)
+ continue;
+
+ offset = lie->add->sy_frag->fr_address+ S_GET_VALUE(lie->add) + lie->addnum -
+ (lie->sub->sy_frag->fr_address+ S_GET_VALUE(lie->sub));
+ if (offset <= -32768 || offset >= 32767) {
+ if (flagseen['K'])
+ as_warn(".word %s-%s+%ld didn't fit",
+ S_GET_NAME(lie->add),
+ S_GET_NAME(lie->sub),
+ lie->addnum);
+ lie->added=1;
+ if (fragP->fr_subtype == 0) {
+ fragP->fr_subtype++;
+ growth+=md_short_jump_size;
+ }
+ for (untruth=lie->next_broken_word;untruth && untruth->dispfrag == lie->dispfrag;untruth=untruth->next_broken_word)
+ if ((untruth->add->sy_frag == lie->add->sy_frag)
+ && S_GET_VALUE(untruth->add) == S_GET_VALUE(lie->add)) {
+ untruth->added=2;
+ untruth->use_jump=lie;
+ }
+ growth += md_long_jump_size;
+ }
+ }
+
+ break;
+ } /* case rs_broken_word */
+#endif
+ case rs_align:
+ growth = relax_align((relax_addressT) (address + fragP->fr_fix), offset)
+ - relax_align((relax_addressT) (was_address + fragP->fr_fix), offset);
+ break;
+
+ case rs_org:
+ target = offset;
+
+ if (symbolP) {
+#ifdef MANY_SEGMENTS
+#else
+ know((S_GET_SEGMENT(symbolP) == SEG_ABSOLUTE) || (S_GET_SEGMENT(symbolP) == SEG_DATA) || (S_GET_SEGMENT(symbolP) == SEG_TEXT));
+ know(symbolP->sy_frag);
+ know(!(S_GET_SEGMENT(symbolP) == SEG_ABSOLUTE) || (symbolP->sy_frag == &zero_address_frag));
+#endif
+ target += S_GET_VALUE(symbolP)
+ + symbolP->sy_frag->fr_address;
+ } /* if we have a symbol */
+
+ know(fragP->fr_next);
+ after = fragP->fr_next->fr_address;
+ growth = ((target - after ) > 0) ? (target - after) : 0;
+ /* Growth may be -ve, but variable part */
+ /* of frag cannot have < 0 chars. */
+ /* That is, we can't .org backwards. */
+
+ growth -= stretch; /* This is an absolute growth factor */
+ break;
+
+ case rs_machine_dependent: {
+ register const relax_typeS * this_type;
+ register const relax_typeS * start_type;
+ register relax_substateT next_state;
+ register relax_substateT this_state;
+
+ start_type = this_type = md_relax_table + (this_state = fragP->fr_subtype);
+ target = offset;
+
+ if (symbolP) {
+#ifndef MANY_SEGMENTS
+ know((S_GET_SEGMENT(symbolP) == SEG_ABSOLUTE) || (S_GET_SEGMENT(symbolP) == SEG_DATA) || (S_GET_SEGMENT(symbolP) == SEG_TEXT));
+#endif
+ know(symbolP->sy_frag);
+ know(!(S_GET_SEGMENT(symbolP) == SEG_ABSOLUTE) || symbolP->sy_frag == &zero_address_frag );
+ target +=
+ S_GET_VALUE(symbolP)
+ + symbolP->sy_frag->fr_address;
+
+ /* If frag has yet to be reached on this pass,
+ assume it will move by STRETCH just as we did.
+ If this is not so, it will be because some frag
+ between grows, and that will force another pass. */
+
+ /* JF was just address */
+ /* JF also added is_dnrange hack */
+ /* There's gotta be a better/faster/etc way
+ to do this... */
+ /* gnu@cygnus.com: I changed this from > to >=
+ because I ran into a zero-length frag (fr_fix=0)
+ which was created when the obstack needed a new
+ chunk JUST AFTER the opcode of a branch. Since
+ fr_fix is zero, fr_address of this frag is the same
+ as fr_address of the next frag. This
+ zero-length frag was variable and jumped to .+2
+ (in the next frag), but since the > comparison
+ below failed (the two were =, not >), "stretch"
+ was not added to the target. Stretch was 178, so
+ the offset appeared to be .-176 instead, which did
+ not fit into a byte branch, so the assembler
+ relaxed the branch to a word. This didn't compare
+ with what happened when the same source file was
+ assembled on other machines, which is how I found it.
+ You might want to think about what other places have
+ trouble with zero length frags... */
+
+ if (symbolP->sy_frag->fr_address >= was_address
+ && is_dnrange(fragP,symbolP->sy_frag)) {
+ target += stretch;
+ } /* */
+
+ } /* if there's a symbol attached */
+
+ aim = target - address - fragP->fr_fix;
+ /* The displacement is affected by the instruction size
+ * for the 32k architecture. I think we ought to be able
+ * to add fragP->fr_pcrel_adjust in all cases (it should be
+ * zero if not used), but just in case it breaks something
+ * else we'll put this inside #ifdef NS32K ... #endif
+ */
+#ifdef TC_NS32K
+ aim += fragP->fr_pcrel_adjust;
+#endif /* TC_NS32K */
+
+ if (aim < 0) {
+ /* Look backwards. */
+ for (next_state = this_type->rlx_more; next_state; ) {
+ if (aim >= this_type->rlx_backward) {
+ next_state = 0;
+ } else { /* Grow to next state. */
+ this_type = md_relax_table + (this_state = next_state);
+ next_state = this_type->rlx_more;
+ }
+ }
+ } else {
+#ifdef DONTDEF
+ /* JF these next few lines of code are for the mc68020 which can't handle short
+ offsets of zero in branch instructions. What a kludge! */
+ if (aim == 0 && this_state == (1<<2+0)) { /* FOO hard encoded from m.c */
+ aim=this_type->rlx_forward+1; /* Force relaxation into word mode */
+ }
+#endif
+#ifdef M68K_AIM_KLUDGE
+ M68K_AIM_KLUDGE(aim, this_state, this_type);
+#endif
+ /* JF end of 68020 code */
+ /* Look forwards. */
+ for (next_state = this_type->rlx_more; next_state; ) {
+ if (aim <= this_type->rlx_forward) {
+ next_state = 0;
+ } else { /* Grow to next state. */
+ this_type = md_relax_table + (this_state = next_state);
+ next_state = this_type->rlx_more;
+ }
+ }
+ }
+
+ if ((growth = this_type->rlx_length - start_type->rlx_length) != 0)
+ fragP->fr_subtype = this_state;
+
+ break;
+ } /* case rs_machine_dependent */
+
+ default:
+ BAD_CASE( fragP->fr_type );
+ break;
+ }
+ if (growth) {
+ stretch += growth;
+ stretched++;
+ }
+ } /* For each frag in the segment. */
+ } while (stretched); /* Until nothing further to relax. */
+ } /* do_relax */
+
+ /*
+ * We now have valid fr_address'es for each frag.
+ */
+
+ /*
+ * All fr_address's are correct, relative to their own segment.
+ * We have made all the fixS we will ever make.
+ */
+} /* relax_segment() */
+
+/*
+ * Relax_align. Advance location counter to next address that has 'alignment'
+ * lowest order bits all 0s.
+ */
+
+/* How many addresses does the .align take? */
+static relax_addressT relax_align(address, alignment)
+register relax_addressT address; /* Address now. */
+register long alignment; /* Alignment (binary). */
+{
+ relax_addressT mask;
+ relax_addressT new_address;
+
+ mask = ~ ( (~0) << alignment );
+ new_address = (address + mask) & (~ mask);
+ return (new_address - address);
+} /* relax_align() */
+
+/* fixup_segment()
+
+ Go through all the fixS's in a segment and see which ones can be
+ handled now. (These consist of fixS where we have since discovered
+ the value of a symbol, or the address of the frag involved.)
+ For each one, call md_apply_fix to put the fix into the frag data.
+
+ Result is a count of how many relocation structs will be needed to
+ handle the remaining fixS's that we couldn't completely handle here.
+ These will be output later by emit_relocations(). */
+
+static long fixup_segment(fixP, this_segment_type)
+register fixS *fixP;
+segT this_segment_type; /* N_TYPE bits for segment. */
+{
+ register long seg_reloc_count;
+ register symbolS *add_symbolP;
+ register symbolS *sub_symbolP;
+ register long add_number;
+ register int size;
+ register char *place;
+ register long where;
+ register char pcrel;
+ register fragS *fragP;
+ register segT add_symbol_segment = SEG_ABSOLUTE;
+
+ /* FIXME: remove this line */ /* fixS *orig = fixP; */
+ seg_reloc_count = 0;
+
+ for ( ; fixP; fixP = fixP->fx_next) {
+ fragP = fixP->fx_frag;
+ know(fragP);
+ where = fixP->fx_where;
+ place = fragP->fr_literal + where;
+ size = fixP->fx_size;
+ add_symbolP = fixP->fx_addsy;
+#ifdef TC_I960
+ if (fixP->fx_callj && TC_S_IS_CALLNAME(add_symbolP)) {
+ /* Relocation should be done via the
+ associated 'bal' entry point
+ symbol. */
+
+ if (!TC_S_IS_BALNAME(tc_get_bal_of_call(add_symbolP))) {
+ as_bad("No 'bal' entry point for leafproc %s",
+ S_GET_NAME(add_symbolP));
+ continue;
+ }
+ fixP->fx_addsy = add_symbolP = tc_get_bal_of_call(add_symbolP);
+ } /* callj relocation */
+#endif
+ sub_symbolP = fixP->fx_subsy;
+ add_number = fixP->fx_offset;
+ pcrel = fixP->fx_pcrel;
+
+ if (add_symbolP) {
+ add_symbol_segment = S_GET_SEGMENT(add_symbolP);
+ } /* if there is an addend */
+
+ if (sub_symbolP) {
+ if (!add_symbolP) {
+ /* Its just -sym */
+ if (S_GET_SEGMENT(sub_symbolP) != SEG_ABSOLUTE) {
+ as_bad("Negative of non-absolute symbol %s", S_GET_NAME(sub_symbolP));
+ } /* not absolute */
+
+ add_number -= S_GET_VALUE(sub_symbolP);
+
+ /* if sub_symbol is in the same segment that add_symbol
+ and add_symbol is either in DATA, TEXT, BSS or ABSOLUTE */
+ } else if ((S_GET_SEGMENT(sub_symbolP) == add_symbol_segment)
+ && (SEG_NORMAL(add_symbol_segment)
+ || (add_symbol_segment == SEG_ABSOLUTE))) {
+ /* Difference of 2 symbols from same segment. */
+ /* Can't make difference of 2 undefineds: 'value' means */
+ /* something different for N_UNDF. */
+#ifdef TC_I960
+ /* Makes no sense to use the difference of 2 arbitrary symbols
+ * as the target of a call instruction.
+ */
+ if (fixP->fx_callj) {
+ as_bad("callj to difference of 2 symbols");
+ }
+#endif /* TC_I960 */
+ add_number += S_GET_VALUE(add_symbolP) -
+ S_GET_VALUE(sub_symbolP);
+
+ add_symbolP = NULL;
+ fixP->fx_addsy = NULL;
+ } else {
+ /* Different segments in subtraction. */
+ know(!(S_IS_EXTERNAL(sub_symbolP) && (S_GET_SEGMENT(sub_symbolP) == SEG_ABSOLUTE)));
+
+ if ((S_GET_SEGMENT(sub_symbolP) == SEG_ABSOLUTE)) {
+ add_number -= S_GET_VALUE(sub_symbolP);
+ } else {
+ as_bad("Can't emit reloc {- %s-seg symbol \"%s\"} @ file address %d.",
+ segment_name(S_GET_SEGMENT(sub_symbolP)),
+ S_GET_NAME(sub_symbolP), fragP->fr_address + where);
+ } /* if absolute */
+ }
+ } /* if sub_symbolP */
+
+#ifdef PIC
+ /*
+ * Bring _GLOBAL_OFFSET_TABLE_ forward, now we've had the
+ * chance to collapse any accompanying symbols into a number.
+ * This is the sequel of the hack in expr.c to parse operands
+ * of the form `_GLOBAL_OFFSET_TABLE_+(L1-L2)'. Note that
+ * _GLOBAL_OFFSET_TABLE_ can only be an "add symbol".
+ */
+ if (add_symbolP == NULL && fixP->fx_gotsy != NULL) {
+ add_symbolP = fixP->fx_addsy = fixP->fx_gotsy;
+ add_symbol_segment = S_GET_SEGMENT(add_symbolP);
+ }
+#endif
+
+ if (add_symbolP) {
+ if (add_symbol_segment == this_segment_type && pcrel) {
+ /*
+ * This fixup was made when the symbol's segment was
+ * SEG_UNKNOWN, but it is now in the local segment.
+ * So we know how to do the address without relocation.
+ */
+#ifdef TC_I960
+ /* reloc_callj() may replace a 'call' with a 'calls' or a 'bal',
+ * in which cases it modifies *fixP as appropriate. In the case
+ * of a 'calls', no further work is required, and *fixP has been
+ * set up to make the rest of the code below a no-op.
+ */
+ reloc_callj(fixP);
+#endif /* TC_I960 */
+
+ add_number += S_GET_VALUE(add_symbolP);
+ add_number -= md_pcrel_from(fixP);
+ pcrel = 0; /* Lie. Don't want further pcrel processing. */
+ fixP->fx_addsy = NULL; /* No relocations please. */
+ } else {
+ switch (add_symbol_segment) {
+ case SEG_ABSOLUTE:
+#ifdef TC_I960
+ reloc_callj(fixP); /* See comment about reloc_callj() above*/
+#endif /* TC_I960 */
+ add_number += S_GET_VALUE(add_symbolP);
+ fixP->fx_addsy = NULL;
+ add_symbolP = NULL;
+ break;
+ default:
+ seg_reloc_count ++;
+#ifdef PIC
+ /*
+ * Do not fixup refs to global data
+ * even if defined here.
+ */
+ if (!flagseen['k'] ||
+ (fixP->fx_r_type != RELOC_GLOB_DAT &&
+#ifdef TC_I386
+/* XXX - This must be rationalized */
+ fixP->fx_r_type != RELOC_GOT &&
+ fixP->fx_r_type != RELOC_GOTOFF &&
+#endif
+ (fixP->fx_r_type != RELOC_32 ||
+ !S_IS_EXTERNAL(add_symbolP))))
+#endif
+ add_number += S_GET_VALUE(add_symbolP);
+ break;
+
+ case SEG_UNKNOWN:
+#ifdef TC_I960
+ if ((int)fixP->fx_bit_fixP == 13) {
+ /* This is a COBR instruction. They have only a
+ * 13-bit displacement and are only to be used
+ * for local branches: flag as error, don't generate
+ * relocation.
+ */
+ as_bad("can't use COBR format with external label");
+ fixP->fx_addsy = NULL; /* No relocations please. */
+ continue;
+ } /* COBR */
+#endif /* TC_I960 */
+
+#ifdef OBJ_COFF
+#ifdef TE_I386AIX
+ if (S_IS_COMMON(add_symbolP))
+ add_number += S_GET_VALUE(add_symbolP);
+#endif /* TE_I386AIX */
+#endif /* OBJ_COFF */
+ ++seg_reloc_count;
+
+ break;
+
+
+ } /* switch on symbol seg */
+ } /* if not in local seg */
+ } /* if there was a + symbol */
+
+ if (pcrel) {
+ add_number -= md_pcrel_from(fixP);
+ if (add_symbolP == 0) {
+ fixP->fx_addsy = & abs_symbol;
+ ++seg_reloc_count;
+ } /* if there's an add_symbol */
+ } /* if pcrel */
+
+ if (!fixP->fx_bit_fixP) {
+ if ((size == 1 &&
+ (add_number& ~0xFF) && (add_number & ~0xFF != (-1 & ~0xFF))) ||
+ (size == 2 &&
+ (add_number& ~0xFFFF) && (add_number & ~0xFFFF != (-1 & ~0xFFFF)))) {
+ as_bad("Value of %d too large for field of %d bytes at 0x%x",
+ add_number, size, fragP->fr_address + where);
+ } /* generic error checking */
+ } /* not a bit fix */
+
+ md_apply_fix(fixP, add_number);
+ } /* For each fixS in this segment. */
+
+#ifdef OBJ_COFF
+#ifdef TC_I960
+ {
+ fixS *topP = fixP;
+
+ /* two relocs per callj under coff. */
+ for (fixP = topP; fixP; fixP = fixP->fx_next) {
+ if (fixP->fx_callj && fixP->fx_addsy != 0) {
+ ++seg_reloc_count;
+ } /* if callj and not already fixed. */
+ } /* for each fix */
+ }
+#endif /* TC_I960 */
+
+#endif /* OBJ_COFF */
+ return(seg_reloc_count);
+} /* fixup_segment() */
+
+
+static int is_dnrange(f1,f2)
+struct frag *f1;
+struct frag *f2;
+{
+ while (f1) {
+ if (f1->fr_next == f2)
+ return 1;
+ f1=f1->fr_next;
+ }
+ return 0;
+} /* is_dnrange() */
+
+/* Append a string onto another string, bumping the pointer along. */
+void
+ append (charPP, fromP, length)
+char **charPP;
+char *fromP;
+unsigned long length;
+{
+ if (length) { /* Don't trust memcpy() of 0 chars. */
+ memcpy(*charPP, fromP, (int) length);
+ *charPP += length;
+ }
+}
+
+int section_alignment[SEG_MAXIMUM_ORDINAL];
+
+/*
+ * This routine records the largest alignment seen for each segment.
+ * If the beginning of the segment is aligned on the worst-case
+ * boundary, all of the other alignments within it will work. At
+ * least one object format really uses this info.
+ */
+void record_alignment(seg, align)
+segT seg; /* Segment to which alignment pertains */
+int align; /* Alignment, as a power of 2
+ * (e.g., 1 => 2-byte boundary, 2 => 4-byte boundary, etc.)
+ */
+{
+
+ if ( align > section_alignment[(int) seg] ){
+ section_alignment[(int) seg] = align;
+ } /* if highest yet */
+
+ return;
+} /* record_alignment() */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of write.c */
diff --git a/gnu/usr.bin/as/write.h b/gnu/usr.bin/as/write.h
new file mode 100644
index 0000000..01e7272
--- /dev/null
+++ b/gnu/usr.bin/as/write.h
@@ -0,0 +1,120 @@
+/* write.h
+
+ Copyright (C) 1987, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
+ * write.h,v 1.3 1993/10/02 20:58:02 pk Exp
+ */
+
+
+#ifndef TC_I960
+#ifdef hpux
+#define EXEC_MACHINE_TYPE HP9000S200_ID
+#endif
+#endif /* TC_I960 */
+
+#ifndef LOCAL_LABEL
+#ifdef DOT_LABEL_PREFIX
+#define LOCAL_LABEL(name) (name[0] == '.' \
+ && (name[1] == 'L' || name[1] == '.'))
+#else /* not defined DOT_LABEL_PREFIX */
+#define LOCAL_LABEL(name) (name[0] == 'L')
+#endif /* not defined DOT_LABEL_PREFIX */
+#endif /* LOCAL_LABEL */
+
+#define S_LOCAL_NAME(s) (LOCAL_LABEL(S_GET_NAME(s)))
+
+#include "bit_fix.h"
+
+/*
+ * FixSs may be built up in any order.
+ */
+
+struct fix {
+ fragS *fx_frag; /* Which frag? */
+ long fx_where; /* Where is the 1st byte to fix up? */
+ symbolS *fx_addsy; /* NULL or Symbol whose value we add in. */
+ symbolS *fx_subsy; /* NULL or Symbol whose value we subtract. */
+#ifdef PIC
+ symbolS *fx_gotsy; /* NULL or __GLOBAL_OFFSET_TABLE_ . */
+#endif
+ long fx_offset; /* Absolute number we add in. */
+ struct fix *fx_next; /* NULL or -> next fixS. */
+ short int fx_size; /* How many bytes are involved? */
+ char fx_pcrel; /* TRUE: pc-relative. */
+ char fx_pcrel_adjust; /* pc-relative offset adjust */
+ char fx_im_disp; /* TRUE: value is a displacement */
+ bit_fixS *fx_bit_fixP; /* IF NULL no bitfix's to do */
+ char fx_bsr; /* sequent-hack */
+ enum reloc_type fx_r_type; /* Sparc hacks */
+ char fx_callj; /* TRUE if target is a 'callj' (used by i960) */
+ long fx_addnumber;
+};
+
+typedef struct fix fixS;
+
+COMMON char *next_object_file_charP;
+
+#ifndef MANY_SEGMENTS
+COMMON fixS *text_fix_root, *text_fix_tail; /* Chains fixSs. */
+COMMON fixS *data_fix_root, *data_fix_tail; /* Chains fixSs. */
+COMMON fixS *bss_fix_root, *bss_fix_tail; /* Chains fixSs. */
+#endif
+COMMON fixS **seg_fix_rootP, **seg_fix_tailP; /* -> one of above. */
+extern long string_byte_count;
+extern int section_alignment[];
+
+#if __STDC__ == 1
+
+bit_fixS *bit_fix_new(int size, int offset, long base_type, long base_adj, long min, long max, long add);
+void append(char **charPP, char *fromP, unsigned long length);
+void record_alignment(segT seg, int align);
+void write_object_file(void);
+
+fixS *fix_new(fragS *frag,
+ int where,
+ int size,
+ symbolS *add_symbol,
+ symbolS *sub_symbol,
+ long offset,
+ int pcrel,
+ enum reloc_type r_type
+#ifdef PIC
+ ,symbolS *got_symbol);
+#else
+ );
+#endif
+
+#else /* not __STDC__ */
+
+bit_fixS *bit_fix_new();
+fixS *fix_new();
+void append();
+void record_alignment();
+void write_object_file();
+
+#endif /* not __STDC__ */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of write.h */
diff --git a/gnu/usr.bin/as/xmalloc.c b/gnu/usr.bin/as/xmalloc.c
new file mode 100644
index 0000000..6962484
--- /dev/null
+++ b/gnu/usr.bin/as/xmalloc.c
@@ -0,0 +1,75 @@
+/* xmalloc.c - get memory or bust
+
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ NAME
+ xmalloc() - get memory or bust
+ INDEX
+ xmalloc() uses malloc()
+
+ SYNOPSIS
+ char * my_memory;
+
+ my_memory = xmalloc(42); / * my_memory gets address of 42 chars * /
+
+ DESCRIPTION
+
+ Use xmalloc() as an "error-free" malloc(). It does almost the same job.
+ When it cannot honour your request for memory it BOMBS your program
+ with a "virtual memory exceeded" message. Malloc() returns NULL and
+ does not bomb your program.
+
+ SEE ALSO
+ malloc()
+
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: xmalloc.c,v 1.3 1993/10/02 20:58:02 pk Exp $";
+#endif
+
+#include <stdio.h>
+
+#if __STDC__ == 1
+#include <stdlib.h>
+#else
+#ifdef USG
+#include <malloc.h>
+#else
+char * malloc();
+#endif /* USG */
+#endif /* not __STDC__ */
+
+#define error as_fatal
+
+char * xmalloc(n)
+long n;
+{
+ char * retval;
+ void error();
+
+ if ((retval = malloc ((unsigned)n)) == NULL)
+ {
+ error("virtual memory exceeded");
+ }
+ return (retval);
+}
+
+/* end of xmalloc.c */
diff --git a/gnu/usr.bin/as/xrealloc.c b/gnu/usr.bin/as/xrealloc.c
new file mode 100644
index 0000000..1b26f43
--- /dev/null
+++ b/gnu/usr.bin/as/xrealloc.c
@@ -0,0 +1,74 @@
+/* xrealloc.c - new memory or bust
+
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/*
+
+ NAME
+ xrealloc () - get more memory or bust
+ INDEX
+ xrealloc () uses realloc ()
+ SYNOPSIS
+ char *my_memory;
+
+ my_memory = xrealloc (my_memory, 42);
+ / * my_memory gets (perhaps new) address of 42 chars * /
+
+ DESCRIPTION
+
+ Use xrealloc () as an "error-free" realloc ().It does almost the same
+ job. When it cannot honour your request for memory it BOMBS your
+ program with a "virtual memory exceeded" message. Realloc() returns
+ NULL and does not bomb your program.
+
+ SEE ALSO
+ realloc ()
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: xrealloc.c,v 1.3 1993/10/02 20:58:03 pk Exp $";
+#endif
+
+
+#if __STDC__ == 1
+#include <stdlib.h>
+#else
+#ifdef USG
+#include <malloc.h>
+#else
+char *realloc ();
+#endif /* USG */
+#endif /* not __STDC__ */
+
+#define error as_fatal
+
+char *
+ xrealloc (ptr, n)
+register char *ptr;
+long n;
+{
+ void error();
+
+ if ((ptr = realloc (ptr, (unsigned)n)) == 0)
+ error ("virtual memory exceeded");
+ return (ptr);
+}
+
+/* end of xrealloc.c */
diff --git a/gnu/usr.bin/awk/ACKNOWLEDGMENT b/gnu/usr.bin/awk/ACKNOWLEDGMENT
new file mode 100644
index 0000000..cb4021f
--- /dev/null
+++ b/gnu/usr.bin/awk/ACKNOWLEDGMENT
@@ -0,0 +1,25 @@
+The current developers of Gawk would like to thank and acknowledge the
+many people who have contributed to the development through bug reports
+and fixes and suggestions. Unfortunately, we have not been organized
+enough to keep track of all the names -- for that we apologize.
+
+Another group of people have assisted even more by porting Gawk to new
+platforms and providing a great deal of feedback. They are:
+
+ Hal Peterson <hrp@pecan.cray.com> (Cray)
+ Pat Rankin <gawk.rankin@EQL.Caltech.Edu> (VMS)
+ Michal Jaegermann <michal@gortel.phys.UAlberta.CA> (Atari, NeXT, DEC 3100)
+ Mike Lijewski <mjlx@eagle.cnsf.cornell.edu> (IBM RS6000)
+ Scott Deifik <scottd@amgen.com> (MSDOS 2.14 and 2.15)
+ Kent Williams (MSDOS 2.11)
+ Conrad Kwok (MSDOS earlier versions)
+ Scott Garfinkle (MSDOS earlier versions)
+ Kai Uwe Rommel <rommel@ars.muc.de> (OS/2)
+ Darrel Hankerson <hankedr@mail.auburn.edu> (OS/2)
+ Mark Moraes <Mark-Moraes@deshaw.com> (Code Center, Purify)
+ Kaveh Ghazi <ghazi@noc.rutgers.edu> (Lots of Unix variants)
+
+Last, but far from least, we would like to thank Brian Kernighan who
+has helped to clear up many dark corners of the language and provided a
+restraining touch when we have been overly tempted by "feeping
+creaturism".
diff --git a/gnu/usr.bin/awk/COPYING b/gnu/usr.bin/awk/COPYING
new file mode 100644
index 0000000..3358a7b
--- /dev/null
+++ b/gnu/usr.bin/awk/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
diff --git a/gnu/usr.bin/awk/FUTURES b/gnu/usr.bin/awk/FUTURES
new file mode 100644
index 0000000..6250584
--- /dev/null
+++ b/gnu/usr.bin/awk/FUTURES
@@ -0,0 +1,117 @@
+This file lists future projects and enhancements for gawk. Items are listed
+in roughly the order they will be done for a given release. This file is
+mainly for use by the developer(s) to help keep themselves on track, please
+don't bug us too much about schedules or what all this really means.
+
+(An `x' indicates that some progress has been made, but that the feature is
+not complete yet.)
+
+For 2.16
+========
+x Move to autoconf-based configure system.
+
+x Research awk `fflush' function.
+
+x Generalize IGNORECASE
+ any value makes it work, not just numeric non-zero
+ make it apply to *all* string comparisons
+
+x Fix FILENAME to have an initial value of "", not "-"
+
+In 2.17
+=======
+x Allow RS to be a regexp.
+
+ RT variable to hold text of record terminator
+
+ RECLEN variable for fixed length records
+
+ Feedback alloca.s changes to FSF
+
+x Split() with null string as third arg to split up strings
+
+x Analogously, setting FS="" would split the input record into individual
+ characters.
+
+x Clean up code by isolating system-specific functions in separate files.
+
+ Undertake significant directory reorganization.
+
+x Extensive manual cleanup:
+ Use of texinfo 2.0 features
+ Lots more examples
+ Document all of the above.
+
+x Go to POSIX regexps
+
+ Make regex + dfa less dependant on gawk header file includes
+
+ Additional manual features:
+ Document posix regexps
+ Document use of dbm arrays
+ ? Add an error messages section to the manual
+ ? A section on where gawk is bounded
+ regex
+ i/o
+ sun fp conversions
+
+For 2.18
+========
+ DBM storage of awk arrays. Try to allow multiple dbm packages
+
+ General sub functions:
+ edit(line, pat, sub) and gedit(line, pat, sub)
+ that return the substituted strings and allow \1 etc. in the sub
+ string.
+
+ ? Have strftime() pay attention to the value of ENVIRON["TZ"]
+
+For 2.19
+========
+ Add chdir and stat built-in functions.
+
+ Add function pointers as valid variable types.
+
+ Add an `ftw' built-in function that takes a function pointer.
+
+ Do an optimization pass over parse tree?
+
+For 2.20 or later:
+==================
+Add variables similar to C's __FILE__ and __LINE__ for better diagnostics
+from within awk programs.
+
+Add an explicit concatenation operator and assignment version.
+
+? Add a switch statement
+
+Add the ability to seek on an open file and retrieve the current file position.
+
+Add lint checking everywhere, including check for use of builtin vars.
+only in new awk.
+
+"restart" keyword
+
+Add |&
+
+Make awk '/foo/' files... run at egrep speeds
+
+Do a reference card
+
+Allow OFMT and CONVFMT to be other than a floating point format.
+
+Allow redefining of builtin functions?
+
+Make it faster and smaller.
+
+For 3.x:
+========
+
+Create a gawk compiler?
+
+Create a gawk-to-C translator? (or C++??)
+
+Provide awk profiling and debugging.
+
+
+
diff --git a/gnu/usr.bin/awk/LIMITATIONS b/gnu/usr.bin/awk/LIMITATIONS
new file mode 100644
index 0000000..64eab85
--- /dev/null
+++ b/gnu/usr.bin/awk/LIMITATIONS
@@ -0,0 +1,16 @@
+This file describes limits of gawk on a Unix system (although it
+is variable even then). Non-Unix systems may have other limits.
+
+# of fields in a record: MAX_INT
+Length of input record: MAX_INT
+Length of output record: unlimited
+Size of a field: MAX_INT
+Size of a printf string: MAX_INT
+Size of a literal string: MAX_INT
+Characters in a character class: 2^(# of bits per byte)
+# of file redirections: unlimited
+# of pipe redirections: min(# of processes per user, # of open files)
+double-precision floating point
+Length of source line: unlimited
+Number of input records in one file: MAX_LONG
+Number of input records total: MAX_LONG
diff --git a/gnu/usr.bin/awk/Makefile b/gnu/usr.bin/awk/Makefile
new file mode 100644
index 0000000..dad9f57
--- /dev/null
+++ b/gnu/usr.bin/awk/Makefile
@@ -0,0 +1,13 @@
+PROG= awk
+SRCS= main.c eval.c builtin.c msg.c iop.c io.c field.c getopt1.c \
+ getopt.c array.c \
+ node.c version.c re.c awk.c regex.c dfa.c
+DPADD= ${LIBM}
+LDADD= -lm
+CFLAGS+=-I${.CURDIR} -DGAWK
+CLEANFILES+=awk.c y.tab.h
+
+MAN1= awk.1
+
+.include <bsd.prog.mk>
+#.include "../../usr.bin/Makefile.inc"
diff --git a/gnu/usr.bin/awk/NEWS b/gnu/usr.bin/awk/NEWS
new file mode 100644
index 0000000..4df69e7
--- /dev/null
+++ b/gnu/usr.bin/awk/NEWS
@@ -0,0 +1,1480 @@
+Changes from 2.15.4 to 2.15.5
+-----------------------------
+
+FUTURES file updated and re-arranged some with more rational schedule.
+
+Many prototypes handled better for ANSI C in protos.h.
+
+getopt.c updated somewhat.
+
+test/Makefile now removes junk directory, `bardargtest' renamed `badargs.'
+
+Bug fix in iop.c for RS = "". Eat trailing newlines off of record separator.
+
+Bug fix in Makefile.bsd44, use leading tab in actions.
+
+Fix in field.c:set_FS for FS == "\\" and IGNORECASE != 0.
+
+Config files updated or added:
+ cray60, DEC OSF/1 2.0, Utek, sgi405, next21, next30, atari/config.h,
+ sco.
+
+Fix in io.c for ENFILE as well as EMFILE, update decl of groupset to
+include OSF/1.
+
+Rationalized printing as integers if numbers are outside the range of a long.
+Changes to node.c:force_string and builtin.c.
+
+Made internal NF, NR, and FNR variables longs instead of ints.
+
+Add LIMITS_H_MISSING stuff to config.in and awk.h, and default defs for
+INT_MAX and LONG_MAX, if no limits.h file. Add a standard decl of
+the time() function for __STDC__. From ghazi@noc.rutgers.edu.
+
+Fix tree_eval in awk.h and r_tree_eval in eval.c to deal better with
+function parameters, particularly ones that are arrays.
+
+Fix eval.c to print out array names of arrays used in scalar contexts.
+
+Fix eval.c in interpret to zero out source and sourceline initially. This
+does a better job of providing source file and line number information.
+
+Fix to re_parse_field in field.c to not use isspace when RS = "", but rather
+to explicitly look for blank and tab.
+
+Fix to sc_parse_field in field.c to catch the case of the FS character at the
+end of a record.
+
+Lots of miscellanious bug fixes for memory leaks, courtesy Mark Moraes,
+also fixes for arrays.
+
+io.c fixed to warn about lack of explicit closes if --lint.
+
+Updated missing/strftime.c to match posted strftime 6.2.
+
+Bug fix in builtin.c, in case of non-match in sub_common.
+
+Updated constant used for division in builtin.c:do_rand for DEC Alpha
+and CRAY Y-MP.
+
+POSIXLY_CORRECT in the environment turns on --posix (fixed in main.c).
+
+Updated srandom prototype and calls in builtin.c.
+
+Fix awk.y to enforce posix semantics of unary +: result is numeric.
+
+Fix array.c to not rearrange the hash chain upon finding an index in
+the array. This messed things up in cases like:
+ for (index1 in array) {
+ blah
+ if (index2 in array) # blew away the for
+ stuff
+ }
+
+Fixed spelling errors in the man page.
+
+Fixes in awk.y so that
+ gawk '' /path/to/file
+will work without core dumping or finding parse errors.
+
+Fix main.c so that --lint will fuss about an empty program.
+Yet another fix for argument parsing in the case of unrecognized options.
+
+Bug fix in dfa.c to not attempt to free null pointers.
+
+Bug fix in builtin.c to only use DEFAULT_G_PRECISION for %g or %G.
+
+Bug fix in field.c to achieve call by value semantics for split.
+
+Changes from 2.15.3 to 2.15.4
+-----------------------------
+
+Lots of lint fixes, and do_sprintf made mostly ANSI C compatible.
+
+Man page updated and edited.
+
+Copyrights updated.
+
+Arrays now grow dynamically, initially scaling up by an order of magnitude
+ and then doubling, up to ~ 64K. This should keep gawk's performance
+ graceful under heavy load.
+
+New `delete array' feature added. Only documented in the man page.
+
+Switched to dfa and regex suites from grep-2.0. These offer the ability to
+ move to POSIX regexps in the next release.
+
+Disabled GNU regex ops.
+
+Research awk -m option now recognized. It does nothing in gawk, since gawk
+ has no static limits. Only documented in the man page.
+
+New bionic (faster, better, stronger than before) hashing function.
+
+Bug fix in argument handling. `gawk -X' now notices there was no program.
+ Additional bug fixes to make --compat and --lint work again.
+
+Many changes for systems where sizeof(int) != sizeof(void *).
+
+Add explicit alloca(0) in io.c to recover space from C alloca.
+
+Fixed file descriptor leak in io.c.
+
+The --version option now follows the GNU coding standards and exits.
+
+Fixed several prototypes in protos.h.
+
+Several tests updated. On Solaris, warn that the out? tests will fail.
+
+Configuration files for SunOS with cc and Solaris 2.x added.
+
+Improved error messages in awk.y on gawk extensions if do_unix or do_compat.
+
+INSTALL file added.
+
+Fixed Atari Makefile and several VMS specific changes.
+
+Better conversion of numbers to strings on systems with broken sprintfs.
+
+Changes from 2.15.2 to 2.15.3
+-----------------------------
+
+Increased HASHSIZE to a decent number, 127 was way too small.
+
+FILENAME is now the null string in a BEGIN rule.
+
+Argument processing fixed for invalid options and missing arguments.
+
+This version will build on VMS. This included a fix to close all files
+ and pipes opened with redirections before closing stdout and stderr.
+
+More getpgrp() defines.
+
+Changes for BSD44: <sys/param.h> in io.c and Makefile.bsd44.
+
+All directories in the distribution are now writable.
+
+Separated LDFLAGS and CFLAGS in Makefile. CFLAGS can now be overridden by
+ user.
+
+Make dist now builds compressed archives ending in .gz and runs doschk.
+
+Amiga port.
+
+New getopt.c fixes Alpha OSF/1 problem.
+
+Make clean now removes possible test output.
+
+Improved algorithm for multiple adjacent string concatenations leads to
+ performance improvements.
+
+Fix nasty bug whereby command-line assignments, both with -v and at run time,
+ could create variables with syntactically illegal names.
+
+Fix obscure bug in printf with %0 flag and filling.
+
+Add a lint check for substr if provided length exceeds remaining characters
+ in string.
+
+Update atari support.
+
+PC support enhanced to include support for both DOS and OS/2. (Lots more
+ #ifdefs. Sigh.)
+
+Config files for Hitachi Unix and OSF/1, courtesy of Yoko Morishita
+ (morisita@sra.co.jp)
+
+Changes from 2.15.1 to 2.15.2
+-----------------------------
+
+Additions to the FUTURES file.
+
+Document undefined order of output when using both standard output
+ and /dev/stdout or any of the /dev output files that gawk emulates in
+ the absence of OS support.
+
+Clean up the distribution generation in Makefile.in: the info files are
+ now included, the distributed files are marked read-only and patched
+ distributions are now unpacked in a directory named with the patch level.
+
+Changes from 2.15 to 2.15.1
+---------------------------
+
+Close stdout and stderr before all redirections on program exit. This allows
+ detection of write errors and also fixes the messages test on Solaris 2.x.
+
+Removed YYMAXDEPTH define in awk.y which was limiting the parser stack depth.
+
+Changes to config/bsd44, Makefile.bsd44 and configure to bring it into line
+ with the BSD4.4 release.
+
+Changed Makefile to use prefix, exec_prefix, bindir etc.
+
+make install now installs info files.
+
+make install now sets permissions on installed files.
+
+Make targets added: uninstall, distclean, mostlyclean and realclean.
+
+Added config.h to cleaner and clobber make targets.
+
+Changes to config/{hpux8x,sysv3,sysv4,ultrix41} to deal with alloca().
+
+Change to getopt.h for portability.
+
+Added more special cases to the getpgrp() call.
+
+Added README.ibmrt-aos and config/ibmrt-aos.
+
+Changes from 2.14 to 2.15
+---------------------------
+
+Command-line source can now be mixed with library functions.
+
+ARGIND variable tracks index in ARGV of FILENAME.
+
+GNU style long options in addition to short options.
+
+Plan 9 style special files interpreted by gawk:
+ /dev/pid
+ /dev/ppid
+ /dev/pgrpid
+ /dev/user
+ $1 = getuid
+ $2 = geteuid
+ $3 = getgid
+ $4 = getegid
+ $5 ... $NF = getgroups if supported
+
+ERRNO variable contains error string if getline or close fails.
+
+Very old options -a and -e have gone away.
+
+Inftest has been removed from the default target in test/Makefile -- the
+ results were too machine specific and resulted in too many false alarms.
+
+A README.amiga has been added.
+
+The "too many arguments supplied for format string" warning message is only
+ in effect under the lint option.
+
+Code improvements in dfa.c.
+
+Fixed all reported bugs:
+
+ Writes are checked for failure (such as full filesystem).
+
+ Stopped (at least some) runaway error messages.
+
+ gsub(/^/, "x") does the right thing for $0 of 0, 1, or more length.
+
+ close() on a command being piped to a getline now works properly.
+
+ The input record will no longer be freed upon an explicit close()
+ of the input file.
+
+ A NUL character in FS now works.
+
+ In a substitute, \\& now means a literal backslash followed by what
+ was matched.
+
+ Integer overflow of substring length in substr() is caught.
+
+ An input record without a newline termination is handled properly.
+
+ In io.c, check is against only EMFILE so that system file table
+ is not filled.
+
+ Renamed all files with names longer than 14 characters.
+
+ Escaped characters in regular expressions were being lost when
+ IGNORECASE was used.
+
+ Long source lines were not being handled properly.
+
+ Sourcefiles that ended in a tab but no newline were bombing.
+
+ Patterns that could match zero characters in split() were not working
+ properly.
+
+ The parsedebug option was not working.
+
+ The grammar was being a bit too lenient, allowing some very dubious
+ programs to pass.
+
+ Compilation with DEBUG defined now works.
+
+ A variable read in with getline was not being treated as a potential
+ number.
+
+ Array subscripts were not always of string type.
+
+
+Changes from 2.13.2 to 2.14
+---------------------------
+
+Updated manual!
+
+Added "next file" to skip efficiently to the next input file.
+
+Fixed potential of overflowing buffer in do_sprintf().
+
+Plugged small memory leak in sub_common().
+
+EOF on a redirect is now "sticky" -- it can only be cleared by close()ing
+ the pipe or file.
+
+Now works if used via a #! /bin/gawk line at the top of an executable file
+ when that line ends with whitespace.
+
+Added some checks to the grammar to catch redefinition of builtin functions.
+ This could eventually be the basis for an extension to allow redefining
+ functions, but in the mean time it's a good error catching facility.
+
+Negative integer exponents now work.
+
+Modified do_system() to make sure it had a non-null string to be passed
+ to system(3). Thus, system("") will flush any pending output but not go
+ through the overhead of forking an un-needed shell.
+
+A fix to floating point comparisons so that NaNs compare right on IEEE systems.
+
+Added code to make sure we're not opening directories for reading and such.
+
+Added code to do better diagnoses of weird or null file names.
+
+Allow continue outside of a loop, unless in strict posix mode. Lint option
+ will issue warning.
+
+New missing/strftime.c. There has been one change that affects gawk. Posix
+ now defines a %V conversion so the vms conversion has been changed to %v.
+ If this version is used with gawk -Wlint and they use %V in a call to
+ strftime, they'll get a warning.
+
+Error messages now conform to GNU standard (I hope).
+
+Changed comparisons to conform to the description found in the file POSIX.
+ This is inconsistent with the current POSIX draft, but that is broken.
+ Hopefully the final POSIX standard will conform to this version.
+ (Alas, this will have to wait for 1003.2b, which will be a revision to
+ the 1003.2 standard. That standard has been frozen with the broken
+ comparison rules.)
+
+The length of a string was a short and now is a size_t.
+
+Updated VMS help.
+
+Added quite a few new tests to the test suite and deleted many due to lack of
+ written releases. Test output is only removed if it is identical to the
+ "good" output.
+
+Fixed a couple of bugs for reference to $0 when $0 is "" -- particularly in
+ a BEGIN block.
+
+Fixed premature freeing in construct "$0 = $0".
+
+Removed the call to wait_any() in gawk_popen(), since on at least some systems,
+ if gawk's input was from a pipe, the predecessor process in the pipe was a
+ child of gawk and this caused a deadlock.
+
+Regexp can (once again) match a newline, if given explicitly.
+
+nextopen() makes sure file name is null terminated.
+
+Fixed VMS pipe simulation. Improved VMS I/O performance.
+
+Catch . used in variable names.
+
+Fixed bug in getline without redirect from a file -- it was quitting after the
+ first EOF, rather than trying the next file.
+
+Fixed bug in treatment of backslash at the end of a string -- it was bombing
+ rather than doing something sensible. It is not clear what this should mean,
+ but for now I issue a warning and take it as a literal backslash.
+
+Moved setting of regexp syntax to before the option parsing in main(), to
+ handle things like -v FS='[.,;]'
+
+Fixed bug when NF is set by user -- fields_arr must be expanded if necessary
+ and "new" fields must be initialized.
+
+Fixed several bugs in [g]sub() for no match found or the match is 0-length.
+
+Fixed bug where in gsub() a pattern anchored at the beginning would still
+ substitute throughout the string.
+
+make test does not assume the . is in PATH.
+
+Fixed bug when a field beyond the end of the record was requested after
+ $0 was altered (directly or indirectly).
+
+Fixed bug for assignment to field beyond end of record -- the assigned value
+ was not found on subsequent reference to that field.
+
+Fixed bug for FS a regexp and it matches at the end of a record.
+
+Fixed memory leak for an array local to a function.
+
+Fixed hanging of pipe redirection to getline
+
+Fixed coredump on access to $0 inside BEGIN block.
+
+Fixed treatment of RS = "". It now parses the fields correctly and strips
+ leading whitespace from a record if FS is a space.
+
+Fixed faking of /dev/stdin.
+
+Fixed problem with x += x
+
+Use of scalar as array and vice versa is now detected.
+
+IGNORECASE now obeyed for FS (even if FS is a single alphabetic character).
+
+Switch to GPL version 2.
+
+Renamed awk.tab.c to awktab.c for MSDOS and VMS tar programs.
+
+Renamed this file (CHANGES) to NEWS.
+
+Use fmod() instead of modf() and provide FMOD_MISSING #define to undo
+ this change.
+
+Correct the volatile declarations in eval.c.
+
+Avoid errant closing of the file descriptors for stdin, stdout and stderr.
+
+Be more flexible about where semi-colons can occur in programs.
+
+Check for write errors on all output, not just on close().
+
+Eliminate the need for missing/{strtol.c,vprintf.c}.
+
+Use GNU getopt and eliminate missing/getopt.c.
+
+More "lint" checking.
+
+
+Changes from 2.13.1 to 2.13.2
+-----------------------------
+
+Toward conformity with GNU standards, configure is a link to mkconf, the latter
+ to disappear in the next major release.
+
+Update to config/bsd43.
+
+Added config/apollo, config/msc60, config/cray2-50, config/interactive2.2
+
+sgi33.cc added for compilation using cc rather than gcc.
+
+Ultrix41 now propagates to config.h properly -- as part of a general
+ mechanism in configure for kludges -- #define anything from a config file
+ just gets tacked onto the end of config.h -- to be used sparingly.
+
+Got rid of an unnecessary and troublesome declaration of vprintf().
+
+Small improvement in locality of error messages.
+
+Try to diagnose use of array as scalar and vice versa -- to be improved in
+ the future.
+
+Fix for last bug fix for Cray division code--sigh.
+
+More changes to test suite to explicitly use sh. Also get rid of
+ a few generated files.
+
+Fixed off-by-one bug in string concatenation code.
+
+Fix for use of array that is passed in from a previous function parameter.
+ Addition to test suite for above.
+
+A number of changes associated with changing NF and access to fields
+ beyond the end of the current record.
+
+Change to missing/memcmp.c to avoid seg. fault on zero length input.
+
+Updates to test suite (including some inadvertently left out of the last patch)
+ to invoke sh explicitly (rather than rely on #!/bin/sh) and remove some
+ junk files. test/chem/good updated to correspond to bug fixes.
+
+Changes from 2.13.0 to 2.13.1
+-----------------------------
+
+More configs and PORTS.
+
+Fixed bug wherein a simple division produced an erroneous FPE, caused by
+ the Cray division workaround -- that code is now #ifdef'd only for
+ Cray *and* fixed.
+
+Fixed bug in modulus implementation -- it was very close to the above
+ code, so I noticed it.
+
+Fixed portability problem with limits.h in missing.c
+
+Fixed portability problem with tzname and daylight -- define TZNAME_MISSING
+ if strftime() is missing and tzname is also.
+
+Better support for Latin-1 character set.
+
+Fixed portability problem in test Makefile.
+
+Updated PROBLEMS file.
+
+=============================== gawk-2.13 released =========================
+Changes from 2.12.42 to 2.12.43
+-------------------------------
+
+Typo in awk.y
+
+Fixed up strftime.3 and added doc. for %V.
+
+Changes from 2.12.41 to 2.12.42
+-------------------------------
+
+Fixed bug in devopen() -- if you had write permission in /dev,
+ it would just create /dev/stdout etc.!!
+
+Final (?) VMS update.
+
+Make NeXT use GFMT_WORKAROUND
+
+Fixed bug in sub_common() for substitute on zero-length match. Improved the
+ code a bit while I was at it.
+
+Fixed grammar so that $i++ parses as ($i)++
+
+Put support/* back in the distribution (didn't I already do this?!)
+
+Changes from 2.12.40 to 2.12.41
+-------------------------------
+
+VMS workaround for broken %g format.
+
+Changes from 2.12.39 to 2.12.40
+-------------------------------
+
+Minor man page update.
+
+Fixed latent bug in redirect().
+
+Changes from 2.12.38 to 2.12.39
+-------------------------------
+
+Updates to test suite -- remove dependence on changing gawk.1 man page.
+
+Changes from 2.12.37 to 2.12.38
+-------------------------------
+
+Fixed bug in use of *= without whitespace following.
+
+VMS update.
+
+Updates to man page.
+
+Option handling updates in main.c
+
+test/manyfiles redone and added to bigtest.
+
+Fixed latent (on Sun) bug in handling of save_fs.
+
+Changes from 2.12.36 to 2.12.37
+-------------------------------
+
+Update REL in Makefile-dist. Incorporate test suite into main distribution.
+
+Minor fix in regtest.
+
+Changes from 2.12.35 to 2.12.36
+-------------------------------
+
+Release takes on dual personality -- 2.12.36 and 2.13.0 -- any further
+ patches before public release won't count for 2.13, although they will for
+ 2.12 -- be careful to avoid confusion! patchlevel.h will be the last thing
+ to change.
+
+Cray updates to deal with arithmetic problems.
+
+Minor test suite updates.
+
+Fixed latent bug in parser (freeing memory).
+
+Changes from 2.12.34 to 2.12.35
+-------------------------------
+
+VMS updates.
+
+Flush stdout at top of err() and stderr at bottom.
+
+Fixed bug in eval_condition() -- it wasn't testing for MAYBE_NUM and
+ doing the force_number().
+
+Included the missing manyfiles.awk and a new test to catch the above bug which
+ I am amazed wasn't already caught by the test suite -- it's pretty basic.
+
+Changes from 2.12.33 to 2.12.34
+-------------------------------
+
+Atari updates -- including bug fix.
+
+More VMS updates -- also nuke vms/version.com.
+
+Fixed bug in handling of large numbers of redirections -- it was probably never
+ tested before (blush!).
+
+Minor rearrangement of code in r_force_number().
+
+Made chem and regtest tests a bit more portable (Ultrix again).
+
+Added another test -- manyfiles -- not invoked under any other test -- very Unix
+ specific.
+
+Rough beginning of LIMITATIONS file -- need my AWK book to complete it.
+
+Changes from 2.12.32 to 2.12.33
+-------------------------------
+
+Expunge debug.? from various files.
+
+Remove vestiges of Floor and Ceil kludge.
+
+Special case integer division -- mainly for Cray, but maybe someone else
+ will benefit.
+
+Workaround for iop_close closing an output pipe descriptor on Cray --
+ not conditional since I think it may fix a bug on SGI as well and I don't
+ think it can hurt elsewhere.
+
+Fixed memory leak in assoc_lookup().
+
+Small cleanup in test suite.
+
+Changes from 2.12.31 to 2.12.32
+-------------------------------
+
+Nuked debug.c and debugging flag -- there are better ways.
+
+Nuked version.sh and version.c in subdirectories.
+
+Fixed bug in handling of IGNORECASE.
+
+Fixed bug when FIELDWIDTHS was set via -v option.
+
+Fixed (obscure) bug when $0 is assigned a numerical value.
+
+Fixed so that escape sequences in command-line assignments work (as it already
+ said in the comment).
+
+Added a few cases to test suite.
+
+Moved support/* back into distribution.
+
+VMS updates.
+
+Changes from 2.12.30 to 2.12.31
+-------------------------------
+
+Cosmetic manual page changes.
+
+Updated sunos3 config.
+
+Small changes in test suite including renaming files over 14 chars. in length.
+
+Changes from 2.12.29 to 2.12.30
+-------------------------------
+
+Bug fix for many string concatenations in a row.
+
+Changes from 2.12.28 to 2.12.29
+-------------------------------
+
+Minor cleanup in awk.y
+
+Minor VMS update.
+
+Minor atari update.
+
+Changes from 2.12.27 to 2.12.28
+-------------------------------
+
+Got rid of the debugging goop in eval.c -- there are better ways.
+
+Sequent port.
+
+VMS changes left out of the last patch -- sigh! config/vms.h renamed
+ to config/vms-conf.h.
+
+Fixed missing/tzset.c
+
+Removed use of gcvt() and GCVT_MISSING -- turns out it was no faster than
+ sprintf("%g") and caused all sorts of portability headaches.
+
+Tuned get_field() -- it was unnecessarily parsing the whole record on reference
+ to $0.
+
+Tuned interpret() a bit in the rule_node loop.
+
+In r_force_number(), worked around bug in Uglix strtod() and got rid of
+ ugly do{}while(0) at Michal's urging.
+
+Replaced do_deref() and deref with unref(node) -- much cleaner and a bit faster.
+
+Got rid of assign_number() -- contrary to comment, it was no faster than
+ just making a new node and freeing the old one.
+
+Replaced make_number() and tmp_number() with macros that call mk_number().
+
+Changed freenode() and newnode() into macros -- the latter is getnode()
+ which calls more_nodes() as necessary.
+
+Changes from 2.12.26 to 2.12.27
+-------------------------------
+
+Completion of Cray 2 port (includes a kludge for floor() and ceil()
+ that may go or be changed -- I think that it may just be working around
+ a bug in chem that is being tweaked on the Cray).
+
+More VMS updates.
+
+Moved kludge over yacc's insertion of malloc and realloc declarations
+ from protos.h to the Makefile.
+
+Added a lisp interpreter in awk to the test suite. (Invoked under
+ bigtest.)
+
+Cleanup in r_force_number() -- I had never gotten around to a thorough
+ profile of the cache code and it turns out to be not worth it.
+
+Performance boost -- do lazy force_number()'ing for fields etc. i.e.
+ flag them (MAYBE_NUM) and call force_number only as necessary.
+
+Changes from 2.12.25 to 2.12.26
+-------------------------------
+
+Rework of regexp stuff so that dynamic regexps have reasonable
+ performance -- string used for compiled regexp is stored and
+ compared to new string -- if same, no recompilation is necessary.
+ Also, very dynamic regexps cause dfa-based searching to be turned
+ off.
+
+Code in dev_open() is back to returning fileno(std*) rather than
+ dup()ing it. This will be documented. Sorry for the run-around
+ on this.
+
+Minor atari updates.
+
+Minor vms update.
+
+Missing file from MSDOS port.
+
+Added warning (under lint) if third arg. of [g]sub is a constant and
+ handle it properly in the code (i.e. return how many matches).
+
+Changes from 2.12.24 to 2.12.25
+-------------------------------
+
+MSDOS port.
+
+Non-consequential changes to regexp variables in preparation for
+ a more serious change to fix a serious performance problem.
+
+Changes from 2.12.23 to 2.12.24
+-------------------------------
+
+Fixed bug in output flushing introduced a few patches back. This caused
+ serious performance losses.
+
+Changes from 2.12.22 to 2.12.23
+-------------------------------
+
+Accidentally left config/cray2-60 out of last patch.
+
+Added some missing dependencies to Makefile.
+
+Cleaned up mkconf a bit; made yacc the default parser (no alloca needed,
+ right?); added rs6000 hook for signed characters.
+
+Made regex.c with NO_ALLOCA undefined work.
+
+Fixed bug in dfa.c for systems where free(NULL) bombs.
+
+Deleted a few cant_happen()'s that *really* can't hapen.
+
+Changes from 2.12.21 to 2.12.22
+-------------------------------
+
+Added to config stuff the ability to choose YACC rather than bison.
+
+Fixed CHAR_UNSIGNED in config.h-dist.
+
+Second arg. of strtod() is char ** rather than const char **.
+
+stackb is now initially malloc()'ed since it may be realloc()'ed.
+
+VMS updates.
+
+Added SIZE_T_MISSING to config stuff and a default typedef to awk.h.
+ (Maybe it is not needed on any current systems??)
+
+re_compile_pattern()'s size is now size_t unconditionally.
+
+Changes from 2.12.20 to 2.12.21
+-------------------------------
+
+Corrected missing/gcvt.c.
+
+Got rid of use of dup2() and thus DUP_MISSING.
+
+Updated config/sgi33.
+
+Turned on (and fixed) in cmp_nodes() the behaviour that I *hope* will be in
+ POSIX 1003.2 for relational comparisons.
+
+Small updates to test suite.
+
+Changes from 2.12.19 to 2.12.20
+-------------------------------
+
+Sloppy, sloppy, sloppy!! I didn't even try to compile the last two
+ patches. This one fixes goofs in regex.c.
+
+Changes from 2.12.18 to 2.12.19
+-------------------------------
+
+Cleanup of last patch.
+
+Changes from 2.12.17 to 2.12.18
+-------------------------------
+
+Makefile renamed to Makefile-dist.
+
+Added alloca() configuration to mkconf. (A bit kludgey.) Just
+ add a single line containing ALLOCA_PW, ALLOCA_S or ALLOCA_C
+ to the appropriate config file to have Makefile-dist edited
+ accordingly.
+
+Reorganized output flushing to correspond with new semantics of
+ devopen() on "/dev/std*" etc.
+
+Fixed rest of last goof!!
+
+Save and restore errno in do_pathopen().
+
+Miscellaneous atari updates.
+
+Get rid of the trailing comma in the NODETYPE definition (Cray
+ compiler won't take it).
+
+Try to make the use of `const' consistent since Cray compiler is
+ fussy about that. See the changes to `basename' and `myname'.
+
+It turns out that, according to section 3.8.3 (Macro Replacement)
+ of the ANSI Standard: ``If there are sequences of preprocessing
+ tokens within the list of arguments that would otherwise act as
+ preprocessing directives, the behavior is undefined.'' That means
+ that you cannot count on the behavior of the declaration of
+ re_compile_pattern in awk.h, and indeed the Cray compiler chokes on it.
+
+Replaced alloca with malloc/realloc/free in regex.c. It was much simpler
+ than expected. (Inside NO_ALLOCA for now -- by default no alloca.)
+
+Added a configuration file, config/cray60, for Unicos-6.0.
+
+Changes from 2.12.16 to 2.12.17
+-------------------------------
+
+Ooops. Goofed signal use in last patch.
+
+Changes from 2.12.15 to 2.12.16
+-------------------------------
+
+RENAMED *_dir to just * (e.g. missing_dir).
+
+Numerous VMS changes.
+
+Proper inclusion of atari and vms files.
+
+Added experimental (ifdef'd out) RELAXED_CONTINUATION and DEFAULT_FILETYPE
+ -- please comment on these!
+
+Moved pathopen() to io.c (sigh).
+
+Put local directory ahead in default AWKPATH.
+
+Added facility in mkconf to echo comments on stdout: lines beginning
+ with "#echo " will have the remainder of the line echoed when mkconf is run.
+ Any lines starting with "#" will otherwise be treated as comments. The
+ intent is to be able to say:
+ "#echo Make sure you uncomment alloca.c in the Makefile"
+ or the like.
+
+Prototype fix for V.4
+
+Fixed version_string to not print leading @(#).
+
+Fixed FIELDWIDTHS to work with strict (turned out to be easy).
+
+Fixed conf for V.2.
+
+Changed semantics of /dev/fd/n to be like on real /dev/fd.
+
+Several configuration and updates in the makefile.
+
+Updated manpage.
+
+Include tzset.c and system.c from missing_dir that were accidently left out of
+ the last patch.
+
+Fixed bug in cmdline variable assignment -- arg was getting freed(!) in
+ call to variable.
+
+Backed out of parse-time constant folding for now, until I can figure out
+ how to do it right.
+
+Fixed devopen() so that getline <"-" works.
+
+Changes from 2.12.14 to 2.12.15
+-------------------------------
+
+Changed config/* to a condensed form that can be used with mkconf to generate
+ a config.h from config.h-dist -- much easier to maintain. Please check
+ carefully against what you had before for a particular system and report
+ any problems. vms.h remains separate since the stuff at the bottom
+ didn't quite fit the mkconf model -- hopefully cleared up later.
+
+Fixed bug in grammar -- didn't allow function definition to be separated from
+ other rules by a semi-colon.
+
+VMS fix to #includes in missing.c -- should we just be including awk.h?
+
+Updated README for texinfo.tex version.
+
+Updating of copyright in all .[chy] files.
+
+Added but commented out Michal's fix to strftime.
+
+Added tzset() emulation based on Rick Adams' code. Added TZSET_MISSING to
+ config.h-dist.
+
+Added strftime.3 man page for missing_dir
+
+More posix: func, **, **= don't work in -W posix
+
+More lint: ^, ^= not in old awk
+
+gawk.1: removed ref to -DNO_DEV_FD, other minor updating.
+
+Style change: pushbak becomes pushback() in yylex().
+
+Changes from 2.12.13 to 2.12.14
+-------------------------------
+
+Better (?) organization of awk.h -- attempt to keep all system dependencies
+ near the top and move some of the non-general things out of the config.h
+ files.
+
+Change to handling of SYSTEM_MISSING.
+
+Small change to ultrix config.
+
+Do "/dev/fd/*" etc. checking at runtime.
+
+First pass at VMS port.
+
+Improvements to error handling (when lexeme spans buffers).
+
+Fixed backslash handling -- why didn't I notice this sooner?
+
+Added programs from book to test suite and new target "bigtest" to Makefile.
+
+Changes from 2.12.12 to 2.12.13
+-------------------------------
+
+Recognize OFS and ORS specially so that OFS = 9 works without efficiency hit.
+ Took advantage of opportunity to tune do_print*() for about 10% win on a
+ print with 5 args (i.e. small but significant).
+
+Somewhat pervasive changes to reconcile CONVFMT vs. OFMT.
+
+Better initialization of builtin vars.
+
+Make config/* consistent wrt STRTOL_MISSING.
+
+Small portability improvement to alloca.s
+
+Improvements to lint code in awk.y
+
+Replaced strtol() with a better one by Chris Torek.
+
+Changes from 2.12.11 to 2.12.12
+-------------------------------
+
+Added PORTS file to record successful ports.
+
+Added #define const to nothing if not STDC and added const to strtod() header.
+
+Added * to printf capabilities and partially implemented ' ' and '+' (has an
+ effect for %d only, silently ignored for other formats). I'm afraid that's
+ as far as I want to go before I look at a complete replacement for
+ do_sprintf().
+
+Added warning for /regexp/ on LHS of MATCHOP.
+
+Changes from 2.12.10 to 2.12.11
+-------------------------------
+
+Small Makefile improvements.
+
+Some remaining nits from the NeXT port.
+
+Got rid of bcopy() define in awk.h -- not needed anymore (??)
+
+Changed private in builtin.c -- it is special on Sequent.
+
+Added subset implementation of strtol() and STRTOL_MISSING.
+
+A little bit of cleanup in debug.c, dfa.c.
+
+Changes from 2.12.9 to 2.12.10
+------------------------------
+
+Redid compatability checking and checking for # of args.
+
+Removed all references to variables[] from outside awk.y, in preparation
+ for a more abstract interface to the symbol table.
+
+Got rid of a remaining use of bcopy() in regex.c.
+
+Changes from 2.12.8 to 2.12.9
+-----------------------------
+
+Portability improvements for atari, next and decstation.
+
+Bug fix in substr() -- wasn't handling 3rd arg. of -1 properly.
+
+Manpage updates.
+
+Moved support from src release to doc release.
+
+Updated FUTURES file.
+
+Added some "lint" warnings.
+
+Changes from 2.12.7 to 2.12.8
+-----------------------------
+
+Changed time() to systime().
+
+Changed warning() in snode() to fatal().
+
+strftime() now defaults second arg. to current time.
+
+Changes from 2.12.6 to 2.12.7
+-----------------------------
+
+Fixed bug in sub_common() involving inadequate allocation of a buffer.
+
+Added some missing files to the Makefile.
+
+Changes from 2.12.5 to 2.12.6
+-----------------------------
+
+Fixed bug wherein non-redirected getline could call iop_close() just
+ prior to a call from do_input().
+
+Fixed bug in handling of /dev/stdout and /dev/stderr.
+
+Changes from 2.12.4 to 2.12.5
+-----------------------------
+
+Updated README and support directory.
+
+Changes from 2.12.3 to 2.12.4
+-----------------------------
+
+Updated CHANGES and TODO (should have been done in previous 2 patches).
+
+Changes from 2.12.2 to 2.12.3
+-----------------------------
+
+Brought regex.c and alloca.s into line with current FSF versions.
+
+Changes from 2.12.1 to 2.12.2
+-----------------------------
+
+Portability improvements; mostly moving system prototypes out of awk.h
+
+Introduction of strftime.
+
+Use of CONVFMT.
+
+Changes from 2.12 to 2.12.1
+-----------------------------
+
+Consolidated treatment of command-line assignments (thus correcting the
+-v treatment).
+
+Rationalized builtin-variable handling into a table-driven process, thus
+simplifying variable() and eliminating spc_var().
+
+Fixed bug in handling of command-line source that ended in a newline.
+
+Simplified install() and lookup().
+
+Did away with double-mallocing of identifiers and now free second and later
+instances of a name, after the first gets installed into the symbol table.
+
+Treat IGNORECASE specially, simplifying a lot of code, and allowing
+checking against strict conformance only on setting it, rather than on each
+pattern match.
+
+Fixed regexp matching when IGNORECASE is non-zero (broken when dfa.c was
+added).
+
+Fixed bug where $0 was not being marked as valid, even after it was rebuilt.
+This caused mangling of $0.
+
+
+Changes from 2.11.1 to 2.12
+-----------------------------
+
+Makefile:
+
+Portability improvements in Makefile.
+Move configuration stuff into config.h
+
+FSF files:
+
+Synchronized alloca.[cs] and regex.[ch] with FSF.
+
+array.c:
+
+Rationalized hash routines into one with a different algorithm.
+delete() now works if the array is a local variable.
+Changed interface of assoc_next() and avoided dereferencing past the end of the
+ array.
+
+awk.h:
+
+Merged non-prototype and prototype declarations in awk.h.
+Expanded tree_eval #define to short-circuit more calls of r_tree_eval().
+
+awk.y:
+
+Delinted some of the code in the grammar.
+Fixed and improved some of the error message printing.
+Changed to accomodate unlimited length source lines.
+Line continuation now works as advertised.
+Source lines can be arbitrarily long.
+Refined grammar hacks so that /= assignment works. Regular expressions
+ starting with /= are recognized at the beginning of a line, after && or ||
+ and after ~ or !~. More contexts can be added if necessary.
+Fixed IGNORECASE (multiple scans for backslash).
+Condensed expression_lists in array references.
+Detect and warn for correct # args in builtin functions -- call most of them
+ with a fixed number (i.e. fill in defaults at parse-time rather than at
+ run-time).
+Load ENVIRON only if it is referenced (detected at parse-time).
+Treat NF, FS, RS, NR, FNR specially at parse time, to improve run time.
+Fold constant expressions at parse time.
+Do make_regexp() on third arg. of split() at parse tiem if it is a constant.
+
+builtin.c:
+
+srand() returns 0 the first time called.
+Replaced alloca() with malloc() in do_sprintf().
+Fixed setting of RSTART and RLENGTH in do_match().
+Got rid of get_{one,two,three} and allowance for variable # of args. at
+ run-time -- this is now done at parse-time.
+Fixed latent bug in [g]sub whereby changes to $0 would never get made.
+Rewrote much of sub_common() for simplicity and performance.
+Added ctime() and time() builtin functions (unless -DSTRICT). ctime() returns
+ a time string like the C function, given the number of seconds since the epoch
+ and time() returns the current time in seconds.
+do_sprintf() now checks for mismatch between format string and number of
+ arguments supplied.
+
+dfa.c
+
+This is borrowed (almost unmodified) from GNU grep to provide faster searches.
+
+eval.c
+
+Node_var, Node_var_array and Node_param_list handled from macro rather
+ than in r_tree_eval().
+Changed cmp_nodes() to not do a force_number() -- this, combined with a
+ force_number() on ARGV[] and ENVIRON[] brings it into line with other awks
+Greatly simplified cmp_nodes().
+Separated out Node_NF, Node_FS, Node_RS, Node_NR and Node_FNR in get_lhs().
+All adjacent string concatenations now done at once.
+
+field.c
+
+Added support for FIELDWIDTHS.
+Fixed bug in get_field() whereby changes to a field were not always
+ properly reflected in $0.
+Reordered tests in parse_field() so that reference off the end of the buffer
+ doesn't happen.
+set_FS() now sets *parse_field i.e. routine to call depending on type of FS.
+It also does make_regexp() for FS if needed. get_field() passes FS_regexp
+ to re_parse_field(), as does do_split().
+Changes to set_field() and set_record() to avoid malloc'ing and free'ing the
+ field nodes repeatedly. The fields now just point into $0 unless they are
+ assigned to another variable or changed. force_number() on the field is
+ *only* done when the field is needed.
+
+gawk.1
+
+Fixed troff formatting problem on .TP lines.
+
+io.c
+
+Moved some code out into iop.c.
+Output from pipes and system() calls is properly synchronized.
+Status from pipe close properly returned.
+Bug in getline with no redirect fixed.
+
+iop.c
+
+This file contains a totally revamped get_a_record and associated code.
+
+main.c
+
+Command line programs no longer use a temporary file.
+Therefore, tmpnam() no longer required.
+Deprecated -a and -e options -- they will go away in the next release,
+ but for now they cause a warning.
+Moved -C, -V, -c options to -W ala posix.
+Added -W posix option: throw out \x
+Added -W lint option.
+
+
+node.c
+
+force_number() now allows pure numerics to have leading whitespace.
+Added make_string facility to optimize case of adding an already malloc'd
+ string.
+Cleaned up and simplified do_deref().
+Fixed bug in handling of stref==255 in do_deref().
+
+re.c
+
+contains the interface to regexp code
+
+Changes from 2.11.1 to FSF version of same
+------------------------------------------
+Thu Jan 4 14:19:30 1990 Jim Kingdon (kingdon at albert)
+
+ * Makefile (YACC): Add -y to bison part.
+
+ * missing.c: Add #include <stdio.h>.
+
+Sun Dec 24 16:16:05 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu)
+
+ * * Makefile: Add (commented out) default defines for Sony News.
+
+ * awk.h: Move declaration of vprintf so it will compile when
+ -DVPRINTF_MISSING is defined.
+
+Mon Nov 13 18:54:08 1989 Robert J. Chassell (bob at apple-gunkies.ai.mit.edu)
+
+ * gawk.texinfo: changed @-commands that are not part of the
+ standard, currently released texinfmt.el to those that are.
+ Otherwise, only people with the as-yet unreleased makeinfo.c can
+ format this file.
+
+Changes from 2.11beta to 2.11.1 (production)
+--------------------------------------------
+
+Went from "beta" to production status!!!
+
+Now flushes stdout before closing pipes or redirected files to
+synchronize output.
+
+MS-DOS changes added in.
+
+Signal handler return type parameterized in Makefile and awk.h and
+some lint removed. debug.c cleaned up.
+
+Fixed FS splitting to never match null strings, per book.
+
+Correction to the manual's description of FS.
+
+Some compilers break on char *foo = "string" + 4 so fixed version.sh and
+main.c.
+
+Changes from 2.10beta to 2.11beta
+---------------------------------
+
+This release fixes all reported bugs that we could reproduce. Probably
+some of the changes are not documented here.
+
+The next release will probably not be a beta release!
+
+The most important change is the addition of the -nostalgia option. :-)
+
+The documentation has been improved and brought up-to-date.
+
+There has been a lot of general cleaning up of the code that is not otherwise
+documented here. There has been a movement toward using standard-conforming
+library routines and providing them (in missing.d) for systems lacking them.
+Improved (hopefully) configuration through Makfile modifications and missing.c.
+In particular, straightened out confusion over vprintf #defines, declarations
+etc.
+
+Deleted RCS log comments from source, to reduce source size by about one third.
+Most of them were horribly out-of-date, anyway.
+
+Renamed source files to reflect (for the most part) their contents.
+
+More and improved error messages. Cleanup and fixes to yyerror().
+String constants are not altered in input buffer, so error messages come out
+better. Fixed usage message. Make use of ANSI C strerror() function
+(provided).
+
+Plugged many more memory leaks. The memory consumption is now quite
+reasonable over a wide range of programs.
+
+Uses volatile declaration if STDC > 0 to avoid problems due to longjmp.
+
+New -a and -e options to use awk or egrep style regexps, respectively,
+since POSIX says awk should use egrep regexps. Default is -a.
+
+Added -v option for setting variables before the first file is encountered.
+Version information now uses -V and copyleft uses -C.
+
+Added a patchlevel.h file and its use for -V and -C.
+
+Append_right() optimized for major improvement to programs with a *lot*
+of statements.
+
+Operator precedence has been corrected to match draft Posix.
+
+Tightened up grammar for builtin functions so that only length
+may be called without arguments or parentheses.
+
+/regex/ is now a normal expression that can appear in any expression
+context.
+
+Allow /= to begin a regexp. Allow ..[../..].. in a regexp.
+
+Allow empty compound statements ({}).
+
+Made return and next illegal outside a function and in BEGIN/END respectively.
+
+Division by zero is now illegal and causes a fatal error.
+
+Fixed exponentiation so that x ^ 0 and x ^= 0 both return 1.
+
+Fixed do_sqrt, do_log, and do_exp to do argument/return checking and
+print an error message, per the manual.
+
+Fixed main to catch SIGSEGV to get source and data file line numbers.
+
+Fixed yyerror to print the ^ at the beginning of the bad token, not the end.
+
+Fix to substr() builtin: it was failing if the arguments
+weren't already strings.
+
+Added new node value flag NUMERIC to indicate that a variable is
+purely a number as opposed to type NUM which indicates that
+the node's numeric value is valid. This is set in make_number(),
+tmp_number and r_force_number() when appropriate and used in
+cmp_nodes(). This fixed a bug in comparison of variables that had
+numeric prefixes. The new code uses strtod() and eliminates is_a_number().
+A simple strtod() is provided for systems lacking one. It does no
+overflow checking, so could be improved.
+
+Simplification and efficiency improvement in force_string.
+
+Added performance tweak in r_force_number().
+
+Fixed a bug with nested loops and break/continue in functions.
+
+Fixed inconsistency in handling of empty fields when $0 has to be rebuilt.
+Happens to simplify rebuild_record().
+
+Cleaned up the code associated with opening a pipe for reading. Gawk
+now has its own popen routine (gawk_popen) that allocates an IOBUF
+and keeps track of the pid of the child process. gawk_pclose
+marks the appropriate child as defunct in the right struct redirect.
+
+Cleaned up and fixed close_redir().
+
+Fixed an obscure bug to do with redirection. Intermingled ">" and ">>"
+redirects did not output in a predictable order.
+
+Improved handling of output buffering: now all print[f]s redirected to a tty
+or pipe are flushed immediately and non-redirected output to a tty is flushed
+before the next input record is read.
+
+Fixed a bug in get_a_record() where bcopy() could have copied over
+a random pointer.
+
+Fixed a bug when RS="" and records separated by multiple blank lines.
+
+Got rid of SLOWIO code which was out-of-date anyway.
+
+Fix in get_field() for case where $0 is changed and then $(n) are
+changed and then $0 is used.
+
+Fixed infinite loop on failure to open file for reading from getline.
+Now handles redirect file open failures properly.
+
+Filenames such as /dev/stdin now allowed on the command line as well as
+in redirects.
+
+Fixed so that gawk '$1' where $1 is a zero tests false.
+
+Fixed parsing so that `RLENGTH -1' parses the same as `RLENGTH - 1',
+for example.
+
+The return from a user-defined function now defaults to the Null node.
+This fixes a core-dump-causing bug when the return value of a function
+is used and that function returns no value.
+
+Now catches floating point exceptions to avoid core dumps.
+
+Bug fix for deleting elements of an array -- under some conditions, it was
+deleting more than one element at a time.
+
+Fix in AWKPATH code for running off the end of the string.
+
+Fixed handling of precision in *printf calls. %0.2d now works properly,
+as does %c. [s]printf now recognizes %i and %X.
+
+Fixed a bug in printing of very large (>240) strings.
+
+Cleaned up erroneous behaviour for RS == "".
+
+Added IGNORECASE support to index().
+
+Simplified and fixed newnode/freenode.
+
+Fixed reference to $(anything) in a BEGIN block.
+
+Eliminated use of USG rand48().
+
+Bug fix in force_string for machines with 16-bit ints.
+
+Replaced use of mktemp() with tmpnam() and provided a partial implementation of
+the latter for systems that don't have it.
+
+Added a portability check for includes in io.c.
+
+Minor portability fix in alloc.c plus addition of xmalloc().
+
+Portability fix: on UMAX4.2, st_blksize is zero for a pipe, thus breaking
+iop_alloc() -- fixed.
+
+Workaround for compiler bug on Sun386i in do_sprintf.
+
+More and improved prototypes in awk.h.
+
+Consolidated C escape parsing code into one place.
+
+strict flag is now turned on only when invoked with compatability option.
+It now applies to fewer things.
+
+Changed cast of f._ptr in vprintf.c from (unsigned char *) to (char *).
+Hopefully this is right for the systems that use this code (I don't).
+
+Support for pipes under MSDOS added.
diff --git a/gnu/usr.bin/awk/PORTS b/gnu/usr.bin/awk/PORTS
new file mode 100644
index 0000000..5087a43
--- /dev/null
+++ b/gnu/usr.bin/awk/PORTS
@@ -0,0 +1,35 @@
+A recent version of gawk has been successfully compiled and run "make test"
+on the following:
+
+Sun 4/490 running 4.1
+NeXT running 2.0
+DECstation 3100 running Ultrix 4.0 or Ultrix 3.1 (different config)
+AtariST (16-bit ints, gcc compiler, byacc, running under TOS)
+ESIX V.3.2 Rev D (== System V Release 3.2), the 386. compiler was gcc + bison
+IBM RS/6000 (see README.rs6000)
+486 running SVR4, using cc and bison
+SGI running IRIX 3.3 using gcc (fails with cc)
+Sequent Balance running Dynix V3.1
+Cray Y-MP8 running Unicos 6.0.11
+Cray 2 running Unicos 6.1 (modulo trailing zeroes in chem)
+VAX/VMS V5.x (should also work on 4.6 and 4.7)
+VMS POSIX V1.0, V1.1
+OpenVMS AXP V1.0
+MSDOS - Microsoft C 5.1, compiles and runs very simple testing
+BSD 4.4alpha
+
+From: ghazi@noc.rutgers.edu (Kaveh R. Ghazi):
+
+arch configured as:
+---- --------------
+Dec Alpha OSF 1.3 osf1
+Hpux 9.0 hpux8x
+NeXTStep 2.0 next20
+Sgi Irix 4.0.5 (/bin/cc) sgi405.cc
+Stardent Titan 1500 OSv2.5 sysv3
+Stardent Vistra (i860) SVR4 sysv4
+Solaris 2.3 solaris2.cc
+SunOS 4.1.3 sunos41
+Tektronix XD88 (UTekV 3.2e) sysv3
+Tektronix 4300 (UTek 4.0) utek
+Ultrix 4.2 ultrix41
diff --git a/gnu/usr.bin/awk/POSIX b/gnu/usr.bin/awk/POSIX
new file mode 100644
index 0000000..f240542
--- /dev/null
+++ b/gnu/usr.bin/awk/POSIX
@@ -0,0 +1,95 @@
+Right now, the numeric vs. string comparisons are screwed up in draft
+11.2. What prompted me to check it out was the note in gnu.bug.utils
+which observed that gawk was doing the comparison $1 == "000"
+numerically. I think that we can agree that intuitively, this should
+be done as a string comparison. Version 2.13.2 of gawk follows the
+current POSIX draft. Following is how I (now) think this
+stuff should be done.
+
+1. A numeric literal or the result of a numeric operation has the NUMERIC
+ attribute.
+
+2. A string literal or the result of a string operation has the STRING
+ attribute.
+
+3. Fields, getline input, FILENAME, ARGV elements, ENVIRON elements and the
+ elements of an array created by split() that are numeric strings
+ have the STRNUM attribute. Otherwise, they have the STRING attribute.
+ Uninitialized variables also have the STRNUM attribute.
+
+4. Attributes propagate across assignments, but are not changed by
+ any use. (Although a use may cause the entity to acquire an additional
+ value such that it has both a numeric and string value -- this leaves the
+ attribute unchanged.)
+
+When two operands are compared, either string comparison or numeric comparison
+may be used, depending on the attributes of the operands, according to the
+following (symmetric) matrix:
+
+ +----------------------------------------------
+ | STRING NUMERIC STRNUM
+--------+----------------------------------------------
+ |
+STRING | string string string
+ |
+NUMERIC | string numeric numeric
+ |
+STRNUM | string numeric numeric
+--------+----------------------------------------------
+
+So, the following program should print all OKs.
+
+echo '0e2 0a 0 0b
+0e2 0a 0 0b' |
+$AWK '
+NR == 1 {
+ num = 0
+ str = "0e2"
+
+ print ++test ": " ( (str == "0e2") ? "OK" : "OOPS" )
+ print ++test ": " ( ("0e2" != 0) ? "OK" : "OOPS" )
+ print ++test ": " ( ("0" != $2) ? "OK" : "OOPS" )
+ print ++test ": " ( ("0e2" == $1) ? "OK" : "OOPS" )
+
+ print ++test ": " ( (0 == "0") ? "OK" : "OOPS" )
+ print ++test ": " ( (0 == num) ? "OK" : "OOPS" )
+ print ++test ": " ( (0 != $2) ? "OK" : "OOPS" )
+ print ++test ": " ( (0 == $1) ? "OK" : "OOPS" )
+
+ print ++test ": " ( ($1 != "0") ? "OK" : "OOPS" )
+ print ++test ": " ( ($1 == num) ? "OK" : "OOPS" )
+ print ++test ": " ( ($2 != 0) ? "OK" : "OOPS" )
+ print ++test ": " ( ($2 != $1) ? "OK" : "OOPS" )
+ print ++test ": " ( ($3 == 0) ? "OK" : "OOPS" )
+ print ++test ": " ( ($3 == $1) ? "OK" : "OOPS" )
+ print ++test ": " ( ($2 != $4) ? "OK" : "OOPS" ) # 15
+}
+{
+ a = "+2"
+ b = 2
+ if (NR % 2)
+ c = a + b
+ print ++test ": " ( (a != b) ? "OK" : "OOPS" ) # 16 and 22
+
+ d = "2a"
+ b = 2
+ if (NR % 2)
+ c = d + b
+ print ++test ": " ( (d != b) ? "OK" : "OOPS" )
+
+ print ++test ": " ( (d + 0 == b) ? "OK" : "OOPS" )
+
+ e = "2"
+ print ++test ": " ( (e == b "") ? "OK" : "OOPS" )
+
+ a = "2.13"
+ print ++test ": " ( (a == 2.13) ? "OK" : "OOPS" )
+
+ a = "2.130000"
+ print ++test ": " ( (a != 2.13) ? "OK" : "OOPS" )
+
+ if (NR == 2) {
+ CONVFMT = "%.6f"
+ print ++test ": " ( (a == 2.13) ? "OK" : "OOPS" )
+ }
+}'
diff --git a/gnu/usr.bin/awk/PROBLEMS b/gnu/usr.bin/awk/PROBLEMS
new file mode 100644
index 0000000..a436180
--- /dev/null
+++ b/gnu/usr.bin/awk/PROBLEMS
@@ -0,0 +1,10 @@
+This is a list of known problems in gawk 2.15.
+Hopefully they will all be fixed in the next major release of gawk.
+
+Please keep in mind that the code is still undergoing significant evolution.
+
+1. The interactions with the lexer and yyerror need reworking. It is possible
+ to get line numbers that are one line off if --compat or --posix is
+ true and either `next file' or `delete array' are used.
+
+ Really the whole lexical analysis stuff needs reworking.
diff --git a/gnu/usr.bin/awk/README b/gnu/usr.bin/awk/README
new file mode 100644
index 0000000..90ed9c2
--- /dev/null
+++ b/gnu/usr.bin/awk/README
@@ -0,0 +1,125 @@
+README:
+
+This is GNU Awk 2.15. It should be upwardly compatible with the System
+V Release 4 awk. It is almost completely compliant with POSIX 1003.2.
+
+This release adds new features -- see NEWS for details.
+
+See the installation instructions, below.
+
+Known problems are given in the PROBLEMS file. Work to be done is
+described briefly in the FUTURES file. Verified ports are listed in
+the PORTS file. Changes in this version are summarized in the NEWS file.
+Please read the LIMITATIONS and ACKNOWLEDGMENT files.
+
+Read the file POSIX for a discussion of how the standard says comparisons
+should be done vs. how they really should be done and how gawk does them.
+
+To format the documentation with TeX, you must use texinfo.tex 2.53
+or later. Otherwise footnotes look unacceptable.
+
+If you wish to remake the Info files, you should use makeinfo. The 2.15
+version of makeinfo works with no errors.
+
+The man page is up to date.
+
+INSTALLATION:
+
+Check whether there is a system-specific README file for your system.
+
+A quick overview of the installation process is in the file INSTALL.
+
+Makefile.in may need some tailoring. The only changes necessary should
+be to change installation targets or to change compiler flags.
+The changes to make in Makefile.in are commented and should be obvious.
+
+All other changes should be made in a config file. Samples for
+various systems are included in the config directory. Starting with
+2.11, our intent has been to make the code conform to standards (ANSI,
+POSIX, SVID, in that order) whenever possible, and to not penalize
+standard conforming systems. We have included substitute versions of
+routines not universally available. Simply add the appropriate define
+for the missing feature(s) on your system.
+
+If you have neither bison nor yacc, use the awktab.c file here. It was
+generated with bison, and should have no AT&T code in it. (Note that
+modifying awk.y without bison or yacc will be difficult, at best. You might
+want to get a copy of bison from the FSF too.)
+
+If no config file is included for your system, start by copying one
+for a similar system. One way of determining the defines needed is to
+try to load gawk with nothing defined and see what routines are
+unresolved by the loader. This should give you a good idea of how to
+proceed.
+
+The next release will use the FSF autoconfig program, so we are no longer
+soliciting new config files.
+
+If you have an MS-DOS or OS/2 system, use the stuff in the pc directory.
+For an Atari there is an atari directory and similarly one for VMS.
+
+Chapter 16 of The GAWK Manual discusses configuration in detail.
+(However, it does not discuss OS/2 configuration, see README.pc for
+the details. The manual is being massively revised for 2.16.)
+
+After successful compilation, do 'make test' to run a small test
+suite. There should be no output from the 'cmp' invocations except in
+the cases where there are small differences in floating point values.
+If there are other differences, please investigate and report the
+problem.
+
+PRINTING THE MANUAL
+
+The 'support' directory contains texinfo.tex 2.115, which will be necessary
+for printing the manual, and the texindex.c program from the texinfo
+distribution which is also necessary. See the makefile for the steps needed
+to get a DVI file from the manual.
+
+CAVEATS
+
+The existence of a patchlevel.h file does *N*O*T* imply a commitment on
+our part to issue bug fixes or patches. It is there in case we should
+decide to do so.
+
+BUG REPORTS AND FIXES (Un*x systems):
+
+Please coordinate changes through David Trueman and/or Arnold Robbins.
+
+David Trueman
+Department of Mathematics, Statistics and Computing Science,
+Dalhousie University, Halifax, Nova Scotia, Canada
+
+UUCP: {uunet utai watmath}!dalcs!david
+INTERNET: david@cs.dal.ca
+
+Arnold Robbins
+1736 Reindeer Drive
+Atlanta, GA, 30329-3528, USA
+
+INTERNET: arnold@skeeve.atl.ga.us
+UUCP: { gatech, emory, emoryu1 }!skeeve!arnold
+
+BUG REPORTS AND FIXES (non-Unix ports):
+
+MS-DOS:
+ Scott Deifik
+ AMGEN Inc.
+ Amgen Center, Bldg.17-Dept.393
+ Thousand Oaks, CA 91320-1789
+ Tel-805-499-5725 ext.4677
+ Fax-805-498-0358
+ scottd@amgen.com
+
+VMS:
+ Pat Rankin
+ rankin@eql.caltech.edu (e-mail only)
+
+Atari ST:
+ Michal Jaegermann
+ michal@gortel.phys.ualberta.ca (e-mail only)
+
+OS/2:
+ Kai Uwe Rommel
+ rommel@ars.muc.de (e-mail only)
+ Darrel Hankerson
+ hankedr@mail.auburn.edu (e-mail only)
diff --git a/gnu/usr.bin/awk/array.c b/gnu/usr.bin/awk/array.c
new file mode 100644
index 0000000..d42f9a6
--- /dev/null
+++ b/gnu/usr.bin/awk/array.c
@@ -0,0 +1,515 @@
+/*
+ * array.c - routines for associative arrays.
+ */
+
+/*
+ * Copyright (C) 1986, 1988, 1989, 1991, 1992, 1993 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Progamming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GAWK; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Tree walks (``for (iggy in foo)'') and array deletions use expensive
+ * linear searching. So what we do is start out with small arrays and
+ * grow them as needed, so that our arrays are hopefully small enough,
+ * most of the time, that they're pretty full and we're not looking at
+ * wasted space.
+ *
+ * The decision is made to grow the array if the average chain length is
+ * ``too big''. This is defined as the total number of entries in the table
+ * divided by the size of the array being greater than some constant.
+ */
+
+#define AVG_CHAIN_MAX 10 /* don't want to linear search more than this */
+
+#include "awk.h"
+
+static NODE *assoc_find P((NODE *symbol, NODE *subs, int hash1));
+static void grow_table P((NODE *symbol));
+
+NODE *
+concat_exp(tree)
+register NODE *tree;
+{
+ register NODE *r;
+ char *str;
+ char *s;
+ size_t len;
+ int offset;
+ size_t subseplen;
+ char *subsep;
+
+ if (tree->type != Node_expression_list)
+ return force_string(tree_eval(tree));
+ r = force_string(tree_eval(tree->lnode));
+ if (tree->rnode == NULL)
+ return r;
+ subseplen = SUBSEP_node->lnode->stlen;
+ subsep = SUBSEP_node->lnode->stptr;
+ len = r->stlen + subseplen + 2;
+ emalloc(str, char *, len, "concat_exp");
+ memcpy(str, r->stptr, r->stlen+1);
+ s = str + r->stlen;
+ free_temp(r);
+ tree = tree->rnode;
+ while (tree) {
+ if (subseplen == 1)
+ *s++ = *subsep;
+ else {
+ memcpy(s, subsep, subseplen+1);
+ s += subseplen;
+ }
+ r = force_string(tree_eval(tree->lnode));
+ len += r->stlen + subseplen;
+ offset = s - str;
+ erealloc(str, char *, len, "concat_exp");
+ s = str + offset;
+ memcpy(s, r->stptr, r->stlen+1);
+ s += r->stlen;
+ free_temp(r);
+ tree = tree->rnode;
+ }
+ r = make_str_node(str, s - str, ALREADY_MALLOCED);
+ r->flags |= TEMP;
+ return r;
+}
+
+/* Flush all the values in symbol[] before doing a split() */
+void
+assoc_clear(symbol)
+NODE *symbol;
+{
+ int i;
+ NODE *bucket, *next;
+
+ if (symbol->var_array == 0)
+ return;
+ for (i = 0; i < symbol->array_size; i++) {
+ for (bucket = symbol->var_array[i]; bucket; bucket = next) {
+ next = bucket->ahnext;
+ unref(bucket->ahname);
+ unref(bucket->ahvalue);
+ freenode(bucket);
+ }
+ symbol->var_array[i] = 0;
+ }
+ free(symbol->var_array);
+ symbol->var_array = NULL;
+ symbol->array_size = symbol->table_size = 0;
+ symbol->flags &= ~ARRAYMAXED;
+}
+
+/*
+ * calculate the hash function of the string in subs
+ */
+unsigned int
+hash(s, len, hsize)
+register const char *s;
+register size_t len;
+unsigned long hsize;
+{
+ register unsigned long h = 0;
+
+#ifdef this_is_really_slow
+
+ register unsigned long g;
+
+ while (len--) {
+ h = (h << 4) + *s++;
+ g = (h & 0xf0000000);
+ if (g) {
+ h = h ^ (g >> 24);
+ h = h ^ g;
+ }
+ }
+
+#else /* this_is_really_slow */
+/*
+ * This is INCREDIBLY ugly, but fast. We break the string up into 8 byte
+ * units. On the first time through the loop we get the "leftover bytes"
+ * (strlen % 8). On every other iteration, we perform 8 HASHC's so we handle
+ * all 8 bytes. Essentially, this saves us 7 cmp & branch instructions. If
+ * this routine is heavily used enough, it's worth the ugly coding.
+ *
+ * OZ's original sdbm hash, copied from Margo Seltzers db package.
+ *
+ */
+
+/* Even more speed: */
+/* #define HASHC h = *s++ + 65599 * h */
+/* Because 65599 = pow(2,6) + pow(2,16) - 1 we multiply by shifts */
+#define HASHC htmp = (h << 6); \
+ h = *s++ + htmp + (htmp << 10) - h
+
+ unsigned long htmp;
+
+ h = 0;
+
+#if defined(VAXC)
+/*
+ * [This was an implementation of "Duff's Device", but it has been
+ * redone, separating the switch for extra iterations from the loop.
+ * This is necessary because the DEC VAX-C compiler is STOOPID.]
+ */
+ switch (len & (8 - 1)) {
+ case 7: HASHC;
+ case 6: HASHC;
+ case 5: HASHC;
+ case 4: HASHC;
+ case 3: HASHC;
+ case 2: HASHC;
+ case 1: HASHC;
+ default: break;
+ }
+
+ if (len > (8 - 1)) {
+ register size_t loop = len >> 3;
+ do {
+ HASHC;
+ HASHC;
+ HASHC;
+ HASHC;
+ HASHC;
+ HASHC;
+ HASHC;
+ HASHC;
+ } while (--loop);
+ }
+#else /* !VAXC */
+ /* "Duff's Device" for those who can handle it */
+ if (len > 0) {
+ register size_t loop = (len + 8 - 1) >> 3;
+
+ switch (len & (8 - 1)) {
+ case 0:
+ do { /* All fall throughs */
+ HASHC;
+ case 7: HASHC;
+ case 6: HASHC;
+ case 5: HASHC;
+ case 4: HASHC;
+ case 3: HASHC;
+ case 2: HASHC;
+ case 1: HASHC;
+ } while (--loop);
+ }
+ }
+#endif /* !VAXC */
+#endif /* this_is_really_slow - not */
+
+ if (h >= hsize)
+ h %= hsize;
+ return h;
+}
+
+/*
+ * locate symbol[subs]
+ */
+static NODE * /* NULL if not found */
+assoc_find(symbol, subs, hash1)
+NODE *symbol;
+register NODE *subs;
+int hash1;
+{
+ register NODE *bucket, *prev = 0;
+
+ for (bucket = symbol->var_array[hash1]; bucket; bucket = bucket->ahnext) {
+ if (cmp_nodes(bucket->ahname, subs) == 0) {
+#if 0
+ /*
+ * Disable this code for now. It screws things up if we have
+ * a ``for (iggy in foo)'' in progress. Interestingly enough,
+ * this was not a problem in 2.15.3, only in 2.15.4. I'm not
+ * sure why it works in 2.15.3.
+ */
+ if (prev) { /* move found to front of chain */
+ prev->ahnext = bucket->ahnext;
+ bucket->ahnext = symbol->var_array[hash1];
+ symbol->var_array[hash1] = bucket;
+ }
+#endif
+ return bucket;
+ } else
+ prev = bucket; /* save previous list entry */
+ }
+ return NULL;
+}
+
+/*
+ * test whether the array element symbol[subs] exists or not
+ */
+int
+in_array(symbol, subs)
+NODE *symbol, *subs;
+{
+ register int hash1;
+
+ if (symbol->type == Node_param_list)
+ symbol = stack_ptr[symbol->param_cnt];
+ if (symbol->var_array == 0)
+ return 0;
+ subs = concat_exp(subs); /* concat_exp returns a string node */
+ hash1 = hash(subs->stptr, subs->stlen, (unsigned long) symbol->array_size);
+ if (assoc_find(symbol, subs, hash1) == NULL) {
+ free_temp(subs);
+ return 0;
+ } else {
+ free_temp(subs);
+ return 1;
+ }
+}
+
+/*
+ * SYMBOL is the address of the node (or other pointer) being dereferenced.
+ * SUBS is a number or string used as the subscript.
+ *
+ * Find SYMBOL[SUBS] in the assoc array. Install it with value "" if it
+ * isn't there. Returns a pointer ala get_lhs to where its value is stored
+ */
+NODE **
+assoc_lookup(symbol, subs)
+NODE *symbol, *subs;
+{
+ register int hash1;
+ register NODE *bucket;
+
+ (void) force_string(subs);
+
+ if (symbol->var_array == 0) {
+ symbol->type = Node_var_array;
+ symbol->array_size = symbol->table_size = 0; /* sanity */
+ symbol->flags &= ~ARRAYMAXED;
+ grow_table(symbol);
+ hash1 = hash(subs->stptr, subs->stlen,
+ (unsigned long) symbol->array_size);
+ } else {
+ hash1 = hash(subs->stptr, subs->stlen,
+ (unsigned long) symbol->array_size);
+ bucket = assoc_find(symbol, subs, hash1);
+ if (bucket != NULL) {
+ free_temp(subs);
+ return &(bucket->ahvalue);
+ }
+ }
+
+ /* It's not there, install it. */
+ if (do_lint && subs->stlen == 0)
+ warning("subscript of array `%s' is null string",
+ symbol->vname);
+
+ /* first see if we would need to grow the array, before installing */
+ symbol->table_size++;
+ if ((symbol->flags & ARRAYMAXED) == 0
+ && symbol->table_size/symbol->array_size > AVG_CHAIN_MAX) {
+ grow_table(symbol);
+ /* have to recompute hash value for new size */
+ hash1 = hash(subs->stptr, subs->stlen,
+ (unsigned long) symbol->array_size);
+ }
+
+ getnode(bucket);
+ bucket->type = Node_ahash;
+ if (subs->flags & TEMP)
+ bucket->ahname = dupnode(subs);
+ else {
+ unsigned int saveflags = subs->flags;
+
+ subs->flags &= ~MALLOC;
+ bucket->ahname = dupnode(subs);
+ subs->flags = saveflags;
+ }
+ free_temp(subs);
+
+ /* array subscripts are strings */
+ bucket->ahname->flags &= ~NUMBER;
+ bucket->ahname->flags |= STRING;
+ bucket->ahvalue = Nnull_string;
+ bucket->ahnext = symbol->var_array[hash1];
+ symbol->var_array[hash1] = bucket;
+ return &(bucket->ahvalue);
+}
+
+void
+do_delete(symbol, tree)
+NODE *symbol, *tree;
+{
+ register int hash1;
+ register NODE *bucket, *last;
+ NODE *subs;
+
+ if (symbol->type == Node_param_list)
+ symbol = stack_ptr[symbol->param_cnt];
+ if (symbol->var_array == 0)
+ return;
+ subs = concat_exp(tree); /* concat_exp returns string node */
+ hash1 = hash(subs->stptr, subs->stlen, (unsigned long) symbol->array_size);
+
+ last = NULL;
+ for (bucket = symbol->var_array[hash1]; bucket; last = bucket, bucket = bucket->ahnext)
+ if (cmp_nodes(bucket->ahname, subs) == 0)
+ break;
+ free_temp(subs);
+ if (bucket == NULL)
+ return;
+ if (last)
+ last->ahnext = bucket->ahnext;
+ else
+ symbol->var_array[hash1] = bucket->ahnext;
+ unref(bucket->ahname);
+ unref(bucket->ahvalue);
+ freenode(bucket);
+ symbol->table_size--;
+ if (symbol->table_size <= 0) {
+ memset(symbol->var_array, '\0',
+ sizeof(NODE *) * symbol->array_size);
+ symbol->table_size = symbol->array_size = 0;
+ symbol->flags &= ~ARRAYMAXED;
+ free((char *) symbol->var_array);
+ symbol->var_array = NULL;
+ }
+}
+
+void
+assoc_scan(symbol, lookat)
+NODE *symbol;
+struct search *lookat;
+{
+ lookat->sym = symbol;
+ lookat->idx = 0;
+ lookat->bucket = NULL;
+ lookat->retval = NULL;
+ if (symbol->var_array != NULL)
+ assoc_next(lookat);
+}
+
+void
+assoc_next(lookat)
+struct search *lookat;
+{
+ register NODE *symbol = lookat->sym;
+
+ if (symbol == NULL)
+ fatal("null symbol in assoc_next");
+ if (symbol->var_array == NULL || lookat->idx > symbol->array_size) {
+ lookat->retval = NULL;
+ return;
+ }
+ /*
+ * This is theoretically unsafe. The element bucket might have
+ * been freed if the body of the scan did a delete on the next
+ * element of the bucket. The only way to do that is by array
+ * reference, which is unlikely. Basically, if the user is doing
+ * anything other than an operation on the current element of an
+ * assoc array while walking through it sequentially, all bets are
+ * off. (The safe way is to register all search structs on an
+ * array with the array, and update all of them on a delete or
+ * insert)
+ */
+ if (lookat->bucket != NULL) {
+ lookat->retval = lookat->bucket->ahname;
+ lookat->bucket = lookat->bucket->ahnext;
+ return;
+ }
+ for (; lookat->idx < symbol->array_size; lookat->idx++) {
+ NODE *bucket;
+
+ if ((bucket = symbol->var_array[lookat->idx]) != NULL) {
+ lookat->retval = bucket->ahname;
+ lookat->bucket = bucket->ahnext;
+ lookat->idx++;
+ return;
+ }
+ }
+ lookat->retval = NULL;
+ lookat->bucket = NULL;
+ return;
+}
+
+/* grow_table --- grow a hash table */
+
+static void
+grow_table(symbol)
+NODE *symbol;
+{
+ NODE **old, **new, *chain, *next;
+ int i, j;
+ unsigned long hash1;
+ unsigned long oldsize, newsize;
+ /*
+ * This is an array of primes. We grow the table by an order of
+ * magnitude each time (not just doubling) so that growing is a
+ * rare operation. We expect, on average, that it won't happen
+ * more than twice. The final size is also chosen to be small
+ * enough so that MS-DOG mallocs can handle it. When things are
+ * very large (> 8K), we just double more or less, instead of
+ * just jumping from 8K to 64K.
+ */
+ static long sizes[] = { 13, 127, 1021, 8191, 16381, 32749, 65497 };
+
+ /* find next biggest hash size */
+ oldsize = symbol->array_size;
+ newsize = 0;
+ for (i = 0, j = sizeof(sizes)/sizeof(sizes[0]); i < j; i++) {
+ if (oldsize < sizes[i]) {
+ newsize = sizes[i];
+ break;
+ }
+ }
+
+ if (newsize == oldsize) { /* table already at max (!) */
+ symbol->flags |= ARRAYMAXED;
+ return;
+ }
+
+ /* allocate new table */
+ emalloc(new, NODE **, newsize * sizeof(NODE *), "grow_table");
+ memset(new, '\0', newsize * sizeof(NODE *));
+
+ /* brand new hash table, set things up and return */
+ if (symbol->var_array == NULL) {
+ symbol->table_size = 0;
+ goto done;
+ }
+
+ /* old hash table there, move stuff to new, free old */
+ old = symbol->var_array;
+ for (i = 0; i < oldsize; i++) {
+ if (old[i] == NULL)
+ continue;
+
+ for (chain = old[i]; chain != NULL; chain = next) {
+ next = chain->ahnext;
+ hash1 = hash(chain->ahname->stptr,
+ chain->ahname->stlen, newsize);
+
+ /* remove from old list, add to new */
+ chain->ahnext = new[hash1];
+ new[hash1] = chain;
+
+ }
+ }
+ free(old);
+
+done:
+ /*
+ * note that symbol->table_size does not change if an old array,
+ * and is explicitly set to 0 if a new one.
+ */
+ symbol->var_array = new;
+ symbol->array_size = newsize;
+}
diff --git a/gnu/usr.bin/awk/awk.1 b/gnu/usr.bin/awk/awk.1
new file mode 100644
index 0000000..a98c99d
--- /dev/null
+++ b/gnu/usr.bin/awk/awk.1
@@ -0,0 +1,1969 @@
+.ds PX \s-1POSIX\s+1
+.ds UX \s-1UNIX\s+1
+.ds AN \s-1ANSI\s+1
+.TH GAWK 1 "Apr 18 1994" "Free Software Foundation" "Utility Commands"
+.SH NAME
+gawk \- pattern scanning and processing language
+.SH SYNOPSIS
+.B gawk
+[ POSIX or GNU style options ]
+.B \-f
+.I program-file
+[
+.B \-\^\-
+] file .\^.\^.
+.br
+.B gawk
+[ POSIX or GNU style options ]
+[
+.B \-\^\-
+]
+.I program-text
+file .\^.\^.
+.SH DESCRIPTION
+.I Gawk
+is the GNU Project's implementation of the AWK programming language.
+It conforms to the definition of the language in
+the \*(PX 1003.2 Command Language And Utilities Standard.
+This version in turn is based on the description in
+.IR "The AWK Programming Language" ,
+by Aho, Kernighan, and Weinberger,
+with the additional features defined in the System V Release 4 version
+of \*(UX
+.IR awk .
+.I Gawk
+also provides some GNU-specific extensions.
+.PP
+The command line consists of options to
+.I gawk
+itself, the AWK program text (if not supplied via the
+.B \-f
+or
+.B \-\^\-file
+options), and values to be made
+available in the
+.B ARGC
+and
+.B ARGV
+pre-defined AWK variables.
+.SH OPTIONS
+.PP
+.I Gawk
+options may be either the traditional \*(PX one letter options,
+or the GNU style long options. \*(PX style options start with a single ``\-'',
+while GNU long options start with ``\-\^\-''.
+GNU style long options are provided for both GNU-specific features and
+for \*(PX mandated features. Other implementations of the AWK language
+are likely to only accept the traditional one letter options.
+.PP
+Following the \*(PX standard,
+.IR gawk -specific
+options are supplied via arguments to the
+.B \-W
+option. Multiple
+.B \-W
+options may be supplied, or multiple arguments may be supplied together
+if they are separated by commas, or enclosed in quotes and separated
+by white space.
+Case is ignored in arguments to the
+.B \-W
+option.
+Each
+.B \-W
+option has a corresponding GNU style long option, as detailed below.
+Arguments to GNU style long options are either joined with the option
+by an
+.B =
+sign, with no intervening spaces, or they may be provided in the
+next command line argument.
+.PP
+.I Gawk
+accepts the following options.
+.TP
+.PD 0
+.BI \-F " fs"
+.TP
+.PD
+.BI \-\^\-field-separator= fs
+Use
+.I fs
+for the input field separator (the value of the
+.B FS
+predefined
+variable).
+.TP
+.PD 0
+\fB\-v\fI var\fB\^=\^\fIval\fR
+.TP
+.PD
+\fB\-\^\-assign=\fIvar\fB\^=\^\fIval\fR
+Assign the value
+.IR val ,
+to the variable
+.IR var ,
+before execution of the program begins.
+Such variable values are available to the
+.B BEGIN
+block of an AWK program.
+.TP
+.PD 0
+.BI \-f " program-file"
+.TP
+.PD
+.BI \-\^\-file= program-file
+Read the AWK program source from the file
+.IR program-file ,
+instead of from the first command line argument.
+Multiple
+.B \-f
+(or
+.BR \-\^\-file )
+options may be used.
+.TP
+.PD 0
+.BI \-mf= NNN
+.TP
+.BI \-mr= NNN
+Set various memory limits to the value
+.IR NNN .
+The
+.B f
+flag sets the maximum number of fields, and the
+.B r
+flag sets the maximum record size. These two flags and the
+.B \-m
+option are from the AT&T Bell Labs research version of \*(UX
+.IR awk .
+They are ignored by
+.IR gawk ,
+since
+.I gawk
+has no pre-defined limits.
+.TP \w'\fB\-\^\-copyright\fR'u+1n
+.PD 0
+.B "\-W compat"
+.TP
+.PD
+.B \-\^\-compat
+Run in
+.I compatibility
+mode. In compatibility mode,
+.I gawk
+behaves identically to \*(UX
+.IR awk ;
+none of the GNU-specific extensions are recognized.
+See
+.BR "GNU EXTENSIONS" ,
+below, for more information.
+.TP
+.PD 0
+.B "\-W copyleft"
+.TP
+.PD 0
+.B "\-W copyright"
+.TP
+.PD 0
+.B \-\^\-copyleft
+.TP
+.PD
+.B \-\^\-copyright
+Print the short version of the GNU copyright information message on
+the error output.
+.TP
+.PD 0
+.B "\-W help"
+.TP
+.PD 0
+.B "\-W usage"
+.TP
+.PD 0
+.B \-\^\-help
+.TP
+.PD
+.B \-\^\-usage
+Print a relatively short summary of the available options on
+the error output.
+Per the GNU Coding Standards, these options cause an immediate,
+successful exit.
+.TP
+.PD 0
+.B "\-W lint"
+.TP
+.PD 0
+.B \-\^\-lint
+Provide warnings about constructs that are
+dubious or non-portable to other AWK implementations.
+.ig
+.\" This option is left undocumented, on purpose.
+.TP
+.PD 0
+.B "\-W nostalgia"
+.TP
+.PD
+.B \-\^\-nostalgia
+Provide a moment of nostalgia for long time
+.I awk
+users.
+..
+.TP
+.PD 0
+.B "\-W posix"
+.TP
+.PD
+.B \-\^\-posix
+This turns on
+.I compatibility
+mode, with the following additional restrictions:
+.RS
+.TP \w'\(bu'u+1n
+\(bu
+.B \ex
+escape sequences are not recognized.
+.TP
+\(bu
+The synonym
+.B func
+for the keyword
+.B function
+is not recognized.
+.TP
+\(bu
+The operators
+.B **
+and
+.B **=
+cannot be used in place of
+.B ^
+and
+.BR ^= .
+.RE
+.TP
+.PD 0
+.BI "\-W source=" program-text
+.TP
+.PD
+.BI \-\^\-source= program-text
+Use
+.I program-text
+as AWK program source code.
+This option allows the easy intermixing of library functions (used via the
+.B \-f
+and
+.B \-\^\-file
+options) with source code entered on the command line.
+It is intended primarily for medium to large size AWK programs used
+in shell scripts.
+.sp .5
+The
+.B "\-W source="
+form of this option uses the rest of the command line argument for
+.IR program-text ;
+no other options to
+.B \-W
+will be recognized in the same argument.
+.TP
+.PD 0
+.B "\-W version"
+.TP
+.PD
+.B \-\^\-version
+Print version information for this particular copy of
+.I gawk
+on the error output.
+This is useful mainly for knowing if the current copy of
+.I gawk
+on your system
+is up to date with respect to whatever the Free Software Foundation
+is distributing.
+Per the GNU Coding Standards, these options cause an immediate,
+successful exit.
+.TP
+.B \-\^\-
+Signal the end of options. This is useful to allow further arguments to the
+AWK program itself to start with a ``\-''.
+This is mainly for consistency with the argument parsing convention used
+by most other \*(PX programs.
+.PP
+In compatibility mode,
+any other options are flagged as illegal, but are otherwise ignored.
+In normal operation, as long as program text has been supplied, unknown
+options are passed on to the AWK program in the
+.B ARGV
+array for processing. This is particularly useful for running AWK
+programs via the ``#!'' executable interpreter mechanism.
+.SH AWK PROGRAM EXECUTION
+.PP
+An AWK program consists of a sequence of pattern-action statements
+and optional function definitions.
+.RS
+.PP
+\fIpattern\fB { \fIaction statements\fB }\fR
+.br
+\fBfunction \fIname\fB(\fIparameter list\fB) { \fIstatements\fB }\fR
+.RE
+.PP
+.I Gawk
+first reads the program source from the
+.IR program-file (s)
+if specified,
+from arguments to
+.BR "\-W source=" ,
+or from the first non-option argument on the command line.
+The
+.B \-f
+and
+.B "\-W source="
+options may be used multiple times on the command line.
+.I Gawk
+will read the program text as if all the
+.IR program-file s
+and command line source texts
+had been concatenated together. This is useful for building libraries
+of AWK functions, without having to include them in each new AWK
+program that uses them. It also provides the ability to mix library
+functions with command line programs.
+.PP
+The environment variable
+.B AWKPATH
+specifies a search path to use when finding source files named with
+the
+.B \-f
+option. If this variable does not exist, the default path is
+\fB".:/usr/lib/awk:/usr/local/lib/awk"\fR.
+If a file name given to the
+.B \-f
+option contains a ``/'' character, no path search is performed.
+.PP
+.I Gawk
+executes AWK programs in the following order.
+First,
+all variable assignments specified via the
+.B \-v
+option are performed.
+Next,
+.I gawk
+compiles the program into an internal form.
+Then,
+.I gawk
+executes the code in the
+.B BEGIN
+block(s) (if any),
+and then proceeds to read
+each file named in the
+.B ARGV
+array.
+If there are no files named on the command line,
+.I gawk
+reads the standard input.
+.PP
+If a filename on the command line has the form
+.IB var = val
+it is treated as a variable assignment. The variable
+.I var
+will be assigned the value
+.IR val .
+(This happens after any
+.B BEGIN
+block(s) have been run.)
+Command line variable assignment
+is most useful for dynamically assigning values to the variables
+AWK uses to control how input is broken into fields and records. It
+is also useful for controlling state if multiple passes are needed over
+a single data file.
+.PP
+If the value of a particular element of
+.B ARGV
+is empty (\fB""\fR),
+.I gawk
+skips over it.
+.PP
+For each line in the input,
+.I gawk
+tests to see if it matches any
+.I pattern
+in the AWK program.
+For each pattern that the line matches, the associated
+.I action
+is executed.
+The patterns are tested in the order they occur in the program.
+.PP
+Finally, after all the input is exhausted,
+.I gawk
+executes the code in the
+.B END
+block(s) (if any).
+.SH VARIABLES AND FIELDS
+AWK variables are dynamic; they come into existence when they are
+first used. Their values are either floating-point numbers or strings,
+or both,
+depending upon how they are used. AWK also has one dimensional
+arrays; arrays with multiple dimensions may be simulated.
+Several pre-defined variables are set as a program
+runs; these will be described as needed and summarized below.
+.SS Fields
+.PP
+As each input line is read,
+.I gawk
+splits the line into
+.IR fields ,
+using the value of the
+.B FS
+variable as the field separator.
+If
+.B FS
+is a single character, fields are separated by that character.
+Otherwise,
+.B FS
+is expected to be a full regular expression.
+In the special case that
+.B FS
+is a single blank, fields are separated
+by runs of blanks and/or tabs.
+Note that the value of
+.B IGNORECASE
+(see below) will also affect how fields are split when
+.B FS
+is a regular expression.
+.PP
+If the
+.B FIELDWIDTHS
+variable is set to a space separated list of numbers, each field is
+expected to have fixed width, and
+.I gawk
+will split up the record using the specified widths. The value of
+.B FS
+is ignored.
+Assigning a new value to
+.B FS
+overrides the use of
+.BR FIELDWIDTHS ,
+and restores the default behavior.
+.PP
+Each field in the input line may be referenced by its position,
+.BR $1 ,
+.BR $2 ,
+and so on.
+.B $0
+is the whole line. The value of a field may be assigned to as well.
+Fields need not be referenced by constants:
+.RS
+.PP
+.ft B
+n = 5
+.br
+print $n
+.ft R
+.RE
+.PP
+prints the fifth field in the input line.
+The variable
+.B NF
+is set to the total number of fields in the input line.
+.PP
+References to non-existent fields (i.e. fields after
+.BR $NF )
+produce the null-string. However, assigning to a non-existent field
+(e.g.,
+.BR "$(NF+2) = 5" )
+will increase the value of
+.BR NF ,
+create any intervening fields with the null string as their value, and
+cause the value of
+.B $0
+to be recomputed, with the fields being separated by the value of
+.BR OFS .
+References to negative numbered fields cause a fatal error.
+.SS Built-in Variables
+.PP
+AWK's built-in variables are:
+.PP
+.TP \w'\fBFIELDWIDTHS\fR'u+1n
+.B ARGC
+The number of command line arguments (does not include options to
+.IR gawk ,
+or the program source).
+.TP
+.B ARGIND
+The index in
+.B ARGV
+of the current file being processed.
+.TP
+.B ARGV
+Array of command line arguments. The array is indexed from
+0 to
+.B ARGC
+\- 1.
+Dynamically changing the contents of
+.B ARGV
+can control the files used for data.
+.TP
+.B CONVFMT
+The conversion format for numbers, \fB"%.6g"\fR, by default.
+.TP
+.B ENVIRON
+An array containing the values of the current environment.
+The array is indexed by the environment variables, each element being
+the value of that variable (e.g., \fBENVIRON["HOME"]\fP might be
+.BR /u/arnold ).
+Changing this array does not affect the environment seen by programs which
+.I gawk
+spawns via redirection or the
+.B system()
+function.
+(This may change in a future version of
+.IR gawk .)
+.\" but don't hold your breath...
+.TP
+.B ERRNO
+If a system error occurs either doing a redirection for
+.BR getline ,
+during a read for
+.BR getline ,
+or during a
+.BR close() ,
+then
+.B ERRNO
+will contain
+a string describing the error.
+.TP
+.B FIELDWIDTHS
+A white-space separated list of fieldwidths. When set,
+.I gawk
+parses the input into fields of fixed width, instead of using the
+value of the
+.B FS
+variable as the field separator.
+The fixed field width facility is still experimental; expect the
+semantics to change as
+.I gawk
+evolves over time.
+.TP
+.B FILENAME
+The name of the current input file.
+If no files are specified on the command line, the value of
+.B FILENAME
+is ``\-''.
+However,
+.B FILENAME
+is undefined inside the
+.B BEGIN
+block.
+.TP
+.B FNR
+The input record number in the current input file.
+.TP
+.B FS
+The input field separator, a blank by default.
+.TP
+.B IGNORECASE
+Controls the case-sensitivity of all regular expression operations. If
+.B IGNORECASE
+has a non-zero value, then pattern matching in rules,
+field splitting with
+.BR FS ,
+regular expression
+matching with
+.B ~
+and
+.BR !~ ,
+and the
+.BR gsub() ,
+.BR index() ,
+.BR match() ,
+.BR split() ,
+and
+.B sub()
+pre-defined functions will all ignore case when doing regular expression
+operations. Thus, if
+.B IGNORECASE
+is not equal to zero,
+.B /aB/
+matches all of the strings \fB"ab"\fP, \fB"aB"\fP, \fB"Ab"\fP,
+and \fB"AB"\fP.
+As with all AWK variables, the initial value of
+.B IGNORECASE
+is zero, so all regular expression operations are normally case-sensitive.
+.TP
+.B NF
+The number of fields in the current input record.
+.TP
+.B NR
+The total number of input records seen so far.
+.TP
+.B OFMT
+The output format for numbers, \fB"%.6g"\fR, by default.
+.TP
+.B OFS
+The output field separator, a blank by default.
+.TP
+.B ORS
+The output record separator, by default a newline.
+.TP
+.B RS
+The input record separator, by default a newline.
+.B RS
+is exceptional in that only the first character of its string
+value is used for separating records.
+(This will probably change in a future release of
+.IR gawk .)
+If
+.B RS
+is set to the null string, then records are separated by
+blank lines.
+When
+.B RS
+is set to the null string, then the newline character always acts as
+a field separator, in addition to whatever value
+.B FS
+may have.
+.TP
+.B RSTART
+The index of the first character matched by
+.BR match() ;
+0 if no match.
+.TP
+.B RLENGTH
+The length of the string matched by
+.BR match() ;
+\-1 if no match.
+.TP
+.B SUBSEP
+The character used to separate multiple subscripts in array
+elements, by default \fB"\e034"\fR.
+.SS Arrays
+.PP
+Arrays are subscripted with an expression between square brackets
+.RB ( [ " and " ] ).
+If the expression is an expression list
+.RI ( expr ", " expr " ...)"
+then the array subscript is a string consisting of the
+concatenation of the (string) value of each expression,
+separated by the value of the
+.B SUBSEP
+variable.
+This facility is used to simulate multiply dimensioned
+arrays. For example:
+.PP
+.RS
+.ft B
+i = "A" ;\^ j = "B" ;\^ k = "C"
+.br
+x[i, j, k] = "hello, world\en"
+.ft R
+.RE
+.PP
+assigns the string \fB"hello, world\en"\fR to the element of the array
+.B x
+which is indexed by the string \fB"A\e034B\e034C"\fR. All arrays in AWK
+are associative, i.e. indexed by string values.
+.PP
+The special operator
+.B in
+may be used in an
+.B if
+or
+.B while
+statement to see if an array has an index consisting of a particular
+value.
+.PP
+.RS
+.ft B
+.nf
+if (val in array)
+ print array[val]
+.fi
+.ft
+.RE
+.PP
+If the array has multiple subscripts, use
+.BR "(i, j) in array" .
+.PP
+The
+.B in
+construct may also be used in a
+.B for
+loop to iterate over all the elements of an array.
+.PP
+An element may be deleted from an array using the
+.B delete
+statement.
+The
+.B delete
+statement may also be used to delete the entire contents of an array.
+.SS Variable Typing And Conversion
+.PP
+Variables and fields
+may be (floating point) numbers, or strings, or both. How the
+value of a variable is interpreted depends upon its context. If used in
+a numeric expression, it will be treated as a number, if used as a string
+it will be treated as a string.
+.PP
+To force a variable to be treated as a number, add 0 to it; to force it
+to be treated as a string, concatenate it with the null string.
+.PP
+When a string must be converted to a number, the conversion is accomplished
+using
+.IR atof (3).
+A number is converted to a string by using the value of
+.B CONVFMT
+as a format string for
+.IR sprintf (3),
+with the numeric value of the variable as the argument.
+However, even though all numbers in AWK are floating-point,
+integral values are
+.I always
+converted as integers. Thus, given
+.PP
+.RS
+.ft B
+.nf
+CONVFMT = "%2.2f"
+a = 12
+b = a ""
+.fi
+.ft R
+.RE
+.PP
+the variable
+.B b
+has a string value of \fB"12"\fR and not \fB"12.00"\fR.
+.PP
+.I Gawk
+performs comparisons as follows:
+If two variables are numeric, they are compared numerically.
+If one value is numeric and the other has a string value that is a
+``numeric string,'' then comparisons are also done numerically.
+Otherwise, the numeric value is converted to a string and a string
+comparison is performed.
+Two strings are compared, of course, as strings.
+According to the \*(PX standard, even if two strings are
+numeric strings, a numeric comparison is performed. However, this is
+clearly incorrect, and
+.I gawk
+does not do this.
+.PP
+Uninitialized variables have the numeric value 0 and the string value ""
+(the null, or empty, string).
+.SH PATTERNS AND ACTIONS
+AWK is a line oriented language. The pattern comes first, and then the
+action. Action statements are enclosed in
+.B {
+and
+.BR } .
+Either the pattern may be missing, or the action may be missing, but,
+of course, not both. If the pattern is missing, the action will be
+executed for every single line of input.
+A missing action is equivalent to
+.RS
+.PP
+.B "{ print }"
+.RE
+.PP
+which prints the entire line.
+.PP
+Comments begin with the ``#'' character, and continue until the
+end of the line.
+Blank lines may be used to separate statements.
+Normally, a statement ends with a newline, however, this is not the
+case for lines ending in
+a ``,'', ``{'', ``?'', ``:'', ``&&'', or ``||''.
+Lines ending in
+.B do
+or
+.B else
+also have their statements automatically continued on the following line.
+In other cases, a line can be continued by ending it with a ``\e'',
+in which case the newline will be ignored.
+.PP
+Multiple statements may
+be put on one line by separating them with a ``;''.
+This applies to both the statements within the action part of a
+pattern-action pair (the usual case),
+and to the pattern-action statements themselves.
+.SS Patterns
+AWK patterns may be one of the following:
+.PP
+.RS
+.nf
+.B BEGIN
+.B END
+.BI / "regular expression" /
+.I "relational expression"
+.IB pattern " && " pattern
+.IB pattern " || " pattern
+.IB pattern " ? " pattern " : " pattern
+.BI ( pattern )
+.BI ! " pattern"
+.IB pattern1 ", " pattern2
+.fi
+.RE
+.PP
+.B BEGIN
+and
+.B END
+are two special kinds of patterns which are not tested against
+the input.
+The action parts of all
+.B BEGIN
+patterns are merged as if all the statements had
+been written in a single
+.B BEGIN
+block. They are executed before any
+of the input is read. Similarly, all the
+.B END
+blocks are merged,
+and executed when all the input is exhausted (or when an
+.B exit
+statement is executed).
+.B BEGIN
+and
+.B END
+patterns cannot be combined with other patterns in pattern expressions.
+.B BEGIN
+and
+.B END
+patterns cannot have missing action parts.
+.PP
+For
+.BI / "regular expression" /
+patterns, the associated statement is executed for each input line that matches
+the regular expression.
+Regular expressions are the same as those in
+.IR egrep (1),
+and are summarized below.
+.PP
+A
+.I "relational expression"
+may use any of the operators defined below in the section on actions.
+These generally test whether certain fields match certain regular expressions.
+.PP
+The
+.BR && ,
+.BR || ,
+and
+.B !
+operators are logical AND, logical OR, and logical NOT, respectively, as in C.
+They do short-circuit evaluation, also as in C, and are used for combining
+more primitive pattern expressions. As in most languages, parentheses
+may be used to change the order of evaluation.
+.PP
+The
+.B ?\^:
+operator is like the same operator in C. If the first pattern is true
+then the pattern used for testing is the second pattern, otherwise it is
+the third. Only one of the second and third patterns is evaluated.
+.PP
+The
+.IB pattern1 ", " pattern2
+form of an expression is called a
+.IR "range pattern" .
+It matches all input records starting with a line that matches
+.IR pattern1 ,
+and continuing until a record that matches
+.IR pattern2 ,
+inclusive. It does not combine with any other sort of pattern expression.
+.SS Regular Expressions
+Regular expressions are the extended kind found in
+.IR egrep .
+They are composed of characters as follows:
+.TP \w'\fB[^\fIabc...\fB]\fR'u+2n
+.I c
+matches the non-metacharacter
+.IR c .
+.TP
+.I \ec
+matches the literal character
+.IR c .
+.TP
+.B .
+matches any character except newline.
+.TP
+.B ^
+matches the beginning of a line or a string.
+.TP
+.B $
+matches the end of a line or a string.
+.TP
+.BI [ abc... ]
+character class, matches any of the characters
+.IR abc... .
+.TP
+.BI [^ abc... ]
+negated character class, matches any character except
+.I abc...
+and newline.
+.TP
+.IB r1 | r2
+alternation: matches either
+.I r1
+or
+.IR r2 .
+.TP
+.I r1r2
+concatenation: matches
+.IR r1 ,
+and then
+.IR r2 .
+.TP
+.IB r +
+matches one or more
+.IR r 's.
+.TP
+.IB r *
+matches zero or more
+.IR r 's.
+.TP
+.IB r ?
+matches zero or one
+.IR r 's.
+.TP
+.BI ( r )
+grouping: matches
+.IR r .
+.PP
+The escape sequences that are valid in string constants (see below)
+are also legal in regular expressions.
+.SS Actions
+Action statements are enclosed in braces,
+.B {
+and
+.BR } .
+Action statements consist of the usual assignment, conditional, and looping
+statements found in most languages. The operators, control statements,
+and input/output statements
+available are patterned after those in C.
+.SS Operators
+.PP
+The operators in AWK, in order of increasing precedence, are
+.PP
+.TP "\w'\fB*= /= %= ^=\fR'u+1n"
+.PD 0
+.B "= += \-="
+.TP
+.PD
+.B "*= /= %= ^="
+Assignment. Both absolute assignment
+.BI ( var " = " value )
+and operator-assignment (the other forms) are supported.
+.TP
+.B ?:
+The C conditional expression. This has the form
+.IB expr1 " ? " expr2 " : " expr3\c
+\&. If
+.I expr1
+is true, the value of the expression is
+.IR expr2 ,
+otherwise it is
+.IR expr3 .
+Only one of
+.I expr2
+and
+.I expr3
+is evaluated.
+.TP
+.B ||
+Logical OR.
+.TP
+.B &&
+Logical AND.
+.TP
+.B "~ !~"
+Regular expression match, negated match.
+.B NOTE:
+Do not use a constant regular expression
+.RB ( /foo/ )
+on the left-hand side of a
+.B ~
+or
+.BR !~ .
+Only use one on the right-hand side. The expression
+.BI "/foo/ ~ " exp
+has the same meaning as \fB(($0 ~ /foo/) ~ \fIexp\fB)\fR.
+This is usually
+.I not
+what was intended.
+.TP
+.PD 0
+.B "< >"
+.TP
+.PD 0
+.B "<= >="
+.TP
+.PD
+.B "!= =="
+The regular relational operators.
+.TP
+.I blank
+String concatenation.
+.TP
+.B "+ \-"
+Addition and subtraction.
+.TP
+.B "* / %"
+Multiplication, division, and modulus.
+.TP
+.B "+ \- !"
+Unary plus, unary minus, and logical negation.
+.TP
+.B ^
+Exponentiation (\fB**\fR may also be used, and \fB**=\fR for
+the assignment operator).
+.TP
+.B "++ \-\^\-"
+Increment and decrement, both prefix and postfix.
+.TP
+.B $
+Field reference.
+.SS Control Statements
+.PP
+The control statements are
+as follows:
+.PP
+.RS
+.nf
+\fBif (\fIcondition\fB) \fIstatement\fR [ \fBelse\fI statement \fR]
+\fBwhile (\fIcondition\fB) \fIstatement \fR
+\fBdo \fIstatement \fBwhile (\fIcondition\fB)\fR
+\fBfor (\fIexpr1\fB; \fIexpr2\fB; \fIexpr3\fB) \fIstatement\fR
+\fBfor (\fIvar \fBin\fI array\fB) \fIstatement\fR
+\fBbreak\fR
+\fBcontinue\fR
+\fBdelete \fIarray\^\fB[\^\fIindex\^\fB]\fR
+\fBdelete \fIarray\^\fR
+\fBexit\fR [ \fIexpression\fR ]
+\fB{ \fIstatements \fB}
+.fi
+.RE
+.SS "I/O Statements"
+.PP
+The input/output statements are as follows:
+.PP
+.TP "\w'\fBprintf \fIfmt, expr-list\fR'u+1n"
+.BI close( filename )
+Close file (or pipe, see below).
+.TP
+.B getline
+Set
+.B $0
+from next input record; set
+.BR NF ,
+.BR NR ,
+.BR FNR .
+.TP
+.BI "getline <" file
+Set
+.B $0
+from next record of
+.IR file ;
+set
+.BR NF .
+.TP
+.BI getline " var"
+Set
+.I var
+from next input record; set
+.BR NF ,
+.BR FNR .
+.TP
+.BI getline " var" " <" file
+Set
+.I var
+from next record of
+.IR file .
+.TP
+.B next
+Stop processing the current input record. The next input record
+is read and processing starts over with the first pattern in the
+AWK program. If the end of the input data is reached, the
+.B END
+block(s), if any, are executed.
+.TP
+.B "next file"
+Stop processing the current input file. The next input record read
+comes from the next input file.
+.B FILENAME
+is updated,
+.B FNR
+is reset to 1, and processing starts over with the first pattern in the
+AWK program. If the end of the input data is reached, the
+.B END
+block(s), if any, are executed.
+.TP
+.B print
+Prints the current record.
+.TP
+.BI print " expr-list"
+Prints expressions.
+Each expression is separated by the value of the
+.B OFS
+variable. The output record is terminated with the value of the
+.B ORS
+variable.
+.TP
+.BI print " expr-list" " >" file
+Prints expressions on
+.IR file .
+Each expression is separated by the value of the
+.B OFS
+variable. The output record is terminated with the value of the
+.B ORS
+variable.
+.TP
+.BI printf " fmt, expr-list"
+Format and print.
+.TP
+.BI printf " fmt, expr-list" " >" file
+Format and print on
+.IR file .
+.TP
+.BI system( cmd-line )
+Execute the command
+.IR cmd-line ,
+and return the exit status.
+(This may not be available on non-\*(PX systems.)
+.PP
+Other input/output redirections are also allowed. For
+.B print
+and
+.BR printf ,
+.BI >> file
+appends output to the
+.IR file ,
+while
+.BI | " command"
+writes on a pipe.
+In a similar fashion,
+.IB command " | getline"
+pipes into
+.BR getline .
+The
+.BR getline
+command will return 0 on end of file, and \-1 on an error.
+.SS The \fIprintf\fP\^ Statement
+.PP
+The AWK versions of the
+.B printf
+statement and
+.B sprintf()
+function
+(see below)
+accept the following conversion specification formats:
+.TP
+.B %c
+An \s-1ASCII\s+1 character.
+If the argument used for
+.B %c
+is numeric, it is treated as a character and printed.
+Otherwise, the argument is assumed to be a string, and the only first
+character of that string is printed.
+.TP
+.B %d
+A decimal number (the integer part).
+.TP
+.B %i
+Just like
+.BR %d .
+.TP
+.B %e
+A floating point number of the form
+.BR [\-]d.ddddddE[+\^\-]dd .
+.TP
+.B %f
+A floating point number of the form
+.BR [\-]ddd.dddddd .
+.TP
+.B %g
+Use
+.B e
+or
+.B f
+conversion, whichever is shorter, with nonsignificant zeros suppressed.
+.TP
+.B %o
+An unsigned octal number (again, an integer).
+.TP
+.B %s
+A character string.
+.TP
+.B %x
+An unsigned hexadecimal number (an integer).
+.TP
+.B %X
+Like
+.BR %x ,
+but using
+.B ABCDEF
+instead of
+.BR abcdef .
+.TP
+.B %%
+A single
+.B %
+character; no argument is converted.
+.PP
+There are optional, additional parameters that may lie between the
+.B %
+and the control letter:
+.TP
+.B \-
+The expression should be left-justified within its field.
+.TP
+.I width
+The field should be padded to this width. If the number has a leading
+zero, then the field will be padded with zeros.
+Otherwise it is padded with blanks.
+This applies even to the non-numeric output formats.
+.TP
+.BI . prec
+A number indicating the maximum width of strings or digits to the right
+of the decimal point.
+.PP
+The dynamic
+.I width
+and
+.I prec
+capabilities of the \*(AN C
+.B printf()
+routines are supported.
+A
+.B *
+in place of either the
+.B width
+or
+.B prec
+specifications will cause their values to be taken from
+the argument list to
+.B printf
+or
+.BR sprintf() .
+.SS Special File Names
+.PP
+When doing I/O redirection from either
+.B print
+or
+.B printf
+into a file,
+or via
+.B getline
+from a file,
+.I gawk
+recognizes certain special filenames internally. These filenames
+allow access to open file descriptors inherited from
+.IR gawk 's
+parent process (usually the shell).
+Other special filenames provide access information about the running
+.B gawk
+process.
+The filenames are:
+.TP \w'\fB/dev/stdout\fR'u+1n
+.B /dev/pid
+Reading this file returns the process ID of the current process,
+in decimal, terminated with a newline.
+.TP
+.B /dev/ppid
+Reading this file returns the parent process ID of the current process,
+in decimal, terminated with a newline.
+.TP
+.B /dev/pgrpid
+Reading this file returns the process group ID of the current process,
+in decimal, terminated with a newline.
+.TP
+.B /dev/user
+Reading this file returns a single record terminated with a newline.
+The fields are separated with blanks.
+.B $1
+is the value of the
+.IR getuid (2)
+system call,
+.B $2
+is the value of the
+.IR geteuid (2)
+system call,
+.B $3
+is the value of the
+.IR getgid (2)
+system call, and
+.B $4
+is the value of the
+.IR getegid (2)
+system call.
+If there are any additional fields, they are the group IDs returned by
+.IR getgroups (2).
+Multiple groups may not be supported on all systems.
+.TP
+.B /dev/stdin
+The standard input.
+.TP
+.B /dev/stdout
+The standard output.
+.TP
+.B /dev/stderr
+The standard error output.
+.TP
+.BI /dev/fd/\^ n
+The file associated with the open file descriptor
+.IR n .
+.PP
+These are particularly useful for error messages. For example:
+.PP
+.RS
+.ft B
+print "You blew it!" > "/dev/stderr"
+.ft R
+.RE
+.PP
+whereas you would otherwise have to use
+.PP
+.RS
+.ft B
+print "You blew it!" | "cat 1>&2"
+.ft R
+.RE
+.PP
+These file names may also be used on the command line to name data files.
+.SS Numeric Functions
+.PP
+AWK has the following pre-defined arithmetic functions:
+.PP
+.TP \w'\fBsrand(\^\fIexpr\^\fB)\fR'u+1n
+.BI atan2( y , " x" )
+returns the arctangent of
+.I y/x
+in radians.
+.TP
+.BI cos( expr )
+returns the cosine in radians.
+.TP
+.BI exp( expr )
+the exponential function.
+.TP
+.BI int( expr )
+truncates to integer.
+.TP
+.BI log( expr )
+the natural logarithm function.
+.TP
+.B rand()
+returns a random number between 0 and 1.
+.TP
+.BI sin( expr )
+returns the sine in radians.
+.TP
+.BI sqrt( expr )
+the square root function.
+.TP
+.BI srand( expr )
+use
+.I expr
+as a new seed for the random number generator. If no
+.I expr
+is provided, the time of day will be used.
+The return value is the previous seed for the random
+number generator.
+.SS String Functions
+.PP
+AWK has the following pre-defined string functions:
+.PP
+.TP "\w'\fBsprintf(\^\fIfmt\fB\^, \fIexpr-list\^\fB)\fR'u+1n"
+\fBgsub(\fIr\fB, \fIs\fB, \fIt\fB)\fR
+for each substring matching the regular expression
+.I r
+in the string
+.IR t ,
+substitute the string
+.IR s ,
+and return the number of substitutions.
+If
+.I t
+is not supplied, use
+.BR $0 .
+.TP
+.BI index( s , " t" )
+returns the index of the string
+.I t
+in the string
+.IR s ,
+or 0 if
+.I t
+is not present.
+.TP
+.BI length( s )
+returns the length of the string
+.IR s ,
+or the length of
+.B $0
+if
+.I s
+is not supplied.
+.TP
+.BI match( s , " r" )
+returns the position in
+.I s
+where the regular expression
+.I r
+occurs, or 0 if
+.I r
+is not present, and sets the values of
+.B RSTART
+and
+.BR RLENGTH .
+.TP
+\fBsplit(\fIs\fB, \fIa\fB, \fIr\fB)\fR
+splits the string
+.I s
+into the array
+.I a
+on the regular expression
+.IR r ,
+and returns the number of fields. If
+.I r
+is omitted,
+.B FS
+is used instead.
+The array
+.I a
+is cleared first.
+.TP
+.BI sprintf( fmt , " expr-list" )
+prints
+.I expr-list
+according to
+.IR fmt ,
+and returns the resulting string.
+.TP
+\fBsub(\fIr\fB, \fIs\fB, \fIt\fB)\fR
+just like
+.BR gsub() ,
+but only the first matching substring is replaced.
+.TP
+\fBsubstr(\fIs\fB, \fIi\fB, \fIn\fB)\fR
+returns the
+.IR n -character
+substring of
+.I s
+starting at
+.IR i .
+If
+.I n
+is omitted, the rest of
+.I s
+is used.
+.TP
+.BI tolower( str )
+returns a copy of the string
+.IR str ,
+with all the upper-case characters in
+.I str
+translated to their corresponding lower-case counterparts.
+Non-alphabetic characters are left unchanged.
+.TP
+.BI toupper( str )
+returns a copy of the string
+.IR str ,
+with all the lower-case characters in
+.I str
+translated to their corresponding upper-case counterparts.
+Non-alphabetic characters are left unchanged.
+.SS Time Functions
+.PP
+Since one of the primary uses of AWK programs is processing log files
+that contain time stamp information,
+.I gawk
+provides the following two functions for obtaining time stamps and
+formatting them.
+.PP
+.TP "\w'\fBsystime()\fR'u+1n"
+.B systime()
+returns the current time of day as the number of seconds since the Epoch
+(Midnight UTC, January 1, 1970 on \*(PX systems).
+.TP
+\fBstrftime(\fIformat\fR, \fItimestamp\fB)\fR
+formats
+.I timestamp
+according to the specification in
+.IR format.
+The
+.I timestamp
+should be of the same form as returned by
+.BR systime() .
+If
+.I timestamp
+is missing, the current time of day is used.
+See the specification for the
+.B strftime()
+function in \*(AN C for the format conversions that are
+guaranteed to be available.
+A public-domain version of
+.IR strftime (3)
+and a man page for it are shipped with
+.IR gawk ;
+if that version was used to build
+.IR gawk ,
+then all of the conversions described in that man page are available to
+.IR gawk.
+.SS String Constants
+.PP
+String constants in AWK are sequences of characters enclosed
+between double quotes (\fB"\fR). Within strings, certain
+.I "escape sequences"
+are recognized, as in C. These are:
+.PP
+.TP \w'\fB\e\^\fIddd\fR'u+1n
+.B \e\e
+A literal backslash.
+.TP
+.B \ea
+The ``alert'' character; usually the \s-1ASCII\s+1 \s-1BEL\s+1 character.
+.TP
+.B \eb
+backspace.
+.TP
+.B \ef
+form-feed.
+.TP
+.B \en
+new line.
+.TP
+.B \er
+carriage return.
+.TP
+.B \et
+horizontal tab.
+.TP
+.B \ev
+vertical tab.
+.TP
+.BI \ex "\^hex digits"
+The character represented by the string of hexadecimal digits following
+the
+.BR \ex .
+As in \*(AN C, all following hexadecimal digits are considered part of
+the escape sequence.
+(This feature should tell us something about language design by committee.)
+E.g., \fB"\ex1B"\fR is the \s-1ASCII\s+1 \s-1ESC\s+1 (escape) character.
+.TP
+.BI \e ddd
+The character represented by the 1-, 2-, or 3-digit sequence of octal
+digits. E.g. \fB"\e033"\fR is the \s-1ASCII\s+1 \s-1ESC\s+1 (escape) character.
+.TP
+.BI \e c
+The literal character
+.IR c\^ .
+.PP
+The escape sequences may also be used inside constant regular expressions
+(e.g.,
+.B "/[\ \et\ef\en\er\ev]/"
+matches whitespace characters).
+.SH FUNCTIONS
+Functions in AWK are defined as follows:
+.PP
+.RS
+\fBfunction \fIname\fB(\fIparameter list\fB) { \fIstatements \fB}\fR
+.RE
+.PP
+Functions are executed when called from within the action parts of regular
+pattern-action statements. Actual parameters supplied in the function
+call are used to instantiate the formal parameters declared in the function.
+Arrays are passed by reference, other variables are passed by value.
+.PP
+Since functions were not originally part of the AWK language, the provision
+for local variables is rather clumsy: They are declared as extra parameters
+in the parameter list. The convention is to separate local variables from
+real parameters by extra spaces in the parameter list. For example:
+.PP
+.RS
+.ft B
+.nf
+function f(p, q, a, b) { # a & b are local
+ ..... }
+
+/abc/ { ... ; f(1, 2) ; ... }
+.fi
+.ft R
+.RE
+.PP
+The left parenthesis in a function call is required
+to immediately follow the function name,
+without any intervening white space.
+This is to avoid a syntactic ambiguity with the concatenation operator.
+This restriction does not apply to the built-in functions listed above.
+.PP
+Functions may call each other and may be recursive.
+Function parameters used as local variables are initialized
+to the null string and the number zero upon function invocation.
+.PP
+The word
+.B func
+may be used in place of
+.BR function .
+.SH EXAMPLES
+.nf
+Print and sort the login names of all users:
+
+.ft B
+ BEGIN { FS = ":" }
+ { print $1 | "sort" }
+
+.ft R
+Count lines in a file:
+
+.ft B
+ { nlines++ }
+ END { print nlines }
+
+.ft R
+Precede each line by its number in the file:
+
+.ft B
+ { print FNR, $0 }
+
+.ft R
+Concatenate and line number (a variation on a theme):
+
+.ft B
+ { print NR, $0 }
+.ft R
+.fi
+.SH SEE ALSO
+.IR egrep (1),
+.IR getpid (2),
+.IR getppid (2),
+.IR getpgrp (2),
+.IR getuid (2),
+.IR geteuid (2),
+.IR getgid (2),
+.IR getegid (2),
+.IR getgroups (2)
+.PP
+.IR "The AWK Programming Language" ,
+Alfred V. Aho, Brian W. Kernighan, Peter J. Weinberger,
+Addison-Wesley, 1988. ISBN 0-201-07981-X.
+.PP
+.IR "The GAWK Manual" ,
+Edition 0.15, published by the Free Software Foundation, 1993.
+.SH POSIX COMPATIBILITY
+A primary goal for
+.I gawk
+is compatibility with the \*(PX standard, as well as with the
+latest version of \*(UX
+.IR awk .
+To this end,
+.I gawk
+incorporates the following user visible
+features which are not described in the AWK book,
+but are part of
+.I awk
+in System V Release 4, and are in the \*(PX standard.
+.PP
+The
+.B \-v
+option for assigning variables before program execution starts is new.
+The book indicates that command line variable assignment happens when
+.I awk
+would otherwise open the argument as a file, which is after the
+.B BEGIN
+block is executed. However, in earlier implementations, when such an
+assignment appeared before any file names, the assignment would happen
+.I before
+the
+.B BEGIN
+block was run. Applications came to depend on this ``feature.''
+When
+.I awk
+was changed to match its documentation, this option was added to
+accommodate applications that depended upon the old behavior.
+(This feature was agreed upon by both the AT&T and GNU developers.)
+.PP
+The
+.B \-W
+option for implementation specific features is from the \*(PX standard.
+.PP
+When processing arguments,
+.I gawk
+uses the special option ``\fB\-\^\-\fP'' to signal the end of
+arguments.
+In compatibility mode, it will warn about, but otherwise ignore,
+undefined options.
+In normal operation, such arguments are passed on to the AWK program for
+it to process.
+.PP
+The AWK book does not define the return value of
+.BR srand() .
+The System V Release 4 version of \*(UX
+.I awk
+(and the \*(PX standard)
+has it return the seed it was using, to allow keeping track
+of random number sequences. Therefore
+.B srand()
+in
+.I gawk
+also returns its current seed.
+.PP
+Other new features are:
+The use of multiple
+.B \-f
+options (from MKS
+.IR awk );
+the
+.B ENVIRON
+array; the
+.BR \ea ,
+and
+.BR \ev
+escape sequences (done originally in
+.I gawk
+and fed back into AT&T's); the
+.B tolower()
+and
+.B toupper()
+built-in functions (from AT&T); and the \*(AN C conversion specifications in
+.B printf
+(done first in AT&T's version).
+.SH GNU EXTENSIONS
+.I Gawk
+has some extensions to \*(PX
+.IR awk .
+They are described in this section. All the extensions described here
+can be disabled by
+invoking
+.I gawk
+with the
+.B "\-W compat"
+option.
+.PP
+The following features of
+.I gawk
+are not available in
+\*(PX
+.IR awk .
+.RS
+.TP \w'\(bu'u+1n
+\(bu
+The
+.B \ex
+escape sequence.
+.TP
+\(bu
+The
+.B systime()
+and
+.B strftime()
+functions.
+.TP
+\(bu
+The special file names available for I/O redirection are not recognized.
+.TP
+\(bu
+The
+.B ARGIND
+and
+.B ERRNO
+variables are not special.
+.TP
+\(bu
+The
+.B IGNORECASE
+variable and its side-effects are not available.
+.TP
+\(bu
+The
+.B FIELDWIDTHS
+variable and fixed width field splitting.
+.TP
+\(bu
+No path search is performed for files named via the
+.B \-f
+option. Therefore the
+.B AWKPATH
+environment variable is not special.
+.TP
+\(bu
+The use of
+.B "next file"
+to abandon processing of the current input file.
+.TP
+\(bu
+The use of
+.BI delete " array"
+to delete the entire contents of an array.
+.RE
+.PP
+The AWK book does not define the return value of the
+.B close()
+function.
+.IR Gawk\^ 's
+.B close()
+returns the value from
+.IR fclose (3),
+or
+.IR pclose (3),
+when closing a file or pipe, respectively.
+.PP
+When
+.I gawk
+is invoked with the
+.B "\-W compat"
+option,
+if the
+.I fs
+argument to the
+.B \-F
+option is ``t'', then
+.B FS
+will be set to the tab character.
+Since this is a rather ugly special case, it is not the default behavior.
+This behavior also does not occur if
+.B "\-W posix"
+has been specified.
+.ig
+.PP
+If
+.I gawk
+was compiled for debugging, it will
+accept the following additional options:
+.TP
+.PD 0
+.B \-Wparsedebug
+.TP
+.PD
+.B \-\^\-parsedebug
+Turn on
+.IR yacc (1)
+or
+.IR bison (1)
+debugging output during program parsing.
+This option should only be of interest to the
+.I gawk
+maintainers, and may not even be compiled into
+.IR gawk .
+..
+.SH HISTORICAL FEATURES
+There are two features of historical AWK implementations that
+.I gawk
+supports.
+First, it is possible to call the
+.B length()
+built-in function not only with no argument, but even without parentheses!
+Thus,
+.RS
+.PP
+.ft B
+a = length
+.ft R
+.RE
+.PP
+is the same as either of
+.RS
+.PP
+.ft B
+a = length()
+.br
+a = length($0)
+.ft R
+.RE
+.PP
+This feature is marked as ``deprecated'' in the \*(PX standard, and
+.I gawk
+will issue a warning about its use if
+.B "\-W lint"
+is specified on the command line.
+.PP
+The other feature is the use of the
+.B continue
+statement outside the body of a
+.BR while ,
+.BR for ,
+or
+.B do
+loop. Traditional AWK implementations have treated such usage as
+equivalent to the
+.B next
+statement.
+.I Gawk
+will support this usage if
+.B "\-W posix"
+has not been specified.
+.SH ENVIRONMENT VARIABLES
+If
+.B POSIXLY_CORRECT
+exists in the environment, then
+.I gawk
+behaves exactly as if
+.B \-\-posix
+had been specified on the command line.
+If
+.B \-\-lint
+has been specified,
+.I gawk
+will issue a warning message to this effect.
+.SH BUGS
+The
+.B \-F
+option is not necessary given the command line variable assignment feature;
+it remains only for backwards compatibility.
+.PP
+If your system actually has support for
+.B /dev/fd
+and the associated
+.BR /dev/stdin ,
+.BR /dev/stdout ,
+and
+.B /dev/stderr
+files, you may get different output from
+.I gawk
+than you would get on a system without those files. When
+.I gawk
+interprets these files internally, it synchronizes output to the standard
+output with output to
+.BR /dev/stdout ,
+while on a system with those files, the output is actually to different
+open files.
+Caveat Emptor.
+.SH VERSION INFORMATION
+This man page documents
+.IR gawk ,
+version 2.15.
+.PP
+Starting with the 2.15 version of
+.IR gawk ,
+the
+.BR \-c ,
+.BR \-V ,
+.BR \-C ,
+.ig
+.BR \-D ,
+..
+.BR \-a ,
+and
+.B \-e
+options of the 2.11 version are no longer recognized.
+This fact will not even be documented in the manual page for version 2.16.
+.SH AUTHORS
+The original version of \*(UX
+.I awk
+was designed and implemented by Alfred Aho,
+Peter Weinberger, and Brian Kernighan of AT&T Bell Labs. Brian Kernighan
+continues to maintain and enhance it.
+.PP
+Paul Rubin and Jay Fenlason,
+of the Free Software Foundation, wrote
+.IR gawk ,
+to be compatible with the original version of
+.I awk
+distributed in Seventh Edition \*(UX.
+John Woods contributed a number of bug fixes.
+David Trueman, with contributions
+from Arnold Robbins, made
+.I gawk
+compatible with the new version of \*(UX
+.IR awk .
+.PP
+The initial DOS port was done by Conrad Kwok and Scott Garfinkle.
+Scott Deifik is the current DOS maintainer. Pat Rankin did the
+port to VMS, and Michal Jaegermann did the port to the Atari ST.
+The port to OS/2 was done by Kai Uwe Rommel, with contributions and
+help from Darrel Hankerson.
+.SH ACKNOWLEDGEMENTS
+Brian Kernighan of Bell Labs
+provided valuable assistance during testing and debugging.
+We thank him.
diff --git a/gnu/usr.bin/awk/awk.h b/gnu/usr.bin/awk/awk.h
new file mode 100644
index 0000000..066bf44
--- /dev/null
+++ b/gnu/usr.bin/awk/awk.h
@@ -0,0 +1,790 @@
+/*
+ * awk.h -- Definitions for gawk.
+ */
+
+/*
+ * Copyright (C) 1986, 1988, 1989, 1991, 1992, 1993 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Progamming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GAWK; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* ------------------------------ Includes ------------------------------ */
+#include "config.h"
+
+#include <stdio.h>
+#ifndef LIMITS_H_MISSING
+#include <limits.h>
+#endif
+#include <ctype.h>
+#include <setjmp.h>
+#include <varargs.h>
+#include <time.h>
+#include <errno.h>
+#if !defined(errno) && !defined(MSDOS) && !defined(OS2)
+extern int errno;
+#endif
+#ifdef __GNU_LIBRARY__
+#ifndef linux
+#include <signum.h>
+#endif
+#endif
+
+/* ----------------- System dependencies (with more includes) -----------*/
+
+#if defined(__FreeBSD__)
+# include <floatingpoint.h>
+#endif
+
+#if !defined(VMS) || (!defined(VAXC) && !defined(__DECC))
+#include <sys/types.h>
+#include <sys/stat.h>
+#else /* VMS w/ VAXC or DECC */
+#include <types.h>
+#include <stat.h>
+#include <file.h> /* avoid <fcntl.h> in io.c */
+#endif
+
+#include <signal.h>
+
+#ifdef __STDC__
+#define P(s) s
+#define MALLOC_ARG_T size_t
+#else
+#define P(s) ()
+#define MALLOC_ARG_T unsigned
+#define volatile
+#define const
+#endif
+
+#ifndef SIGTYPE
+#define SIGTYPE void
+#endif
+
+#ifdef SIZE_T_MISSING
+typedef unsigned int size_t;
+#endif
+
+#ifndef SZTC
+#define SZTC
+#define INTC
+#endif
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#include <string.h>
+#ifdef NeXT
+#include <libc.h>
+#undef atof
+#else
+#if defined(atarist) || defined(VMS)
+#include <unixlib.h>
+#else /* atarist || VMS */
+#if !defined(MSDOS) && !defined(_MSC_VER)
+#include <unistd.h>
+#endif /* MSDOS */
+#endif /* atarist || VMS */
+#endif /* Next */
+#else /* STDC_HEADERS */
+#include "protos.h"
+#endif /* STDC_HEADERS */
+
+#if defined(ultrix) && !defined(Ultrix41)
+extern char * getenv P((char *name));
+extern double atof P((char *s));
+#endif
+
+#ifndef __GNUC__
+#ifdef sparc
+/* nasty nasty SunOS-ism */
+#include <alloca.h>
+#ifdef lint
+extern char *alloca();
+#endif
+#else /* not sparc */
+#if !defined(alloca) && !defined(ALLOCA_PROTO)
+#if defined(_MSC_VER)
+#include <malloc.h>
+#else
+extern char *alloca();
+#endif /* _MSC_VER */
+#endif
+#endif /* sparc */
+#endif /* __GNUC__ */
+
+#ifdef HAVE_UNDERSCORE_SETJMP
+/* nasty nasty berkelixm */
+#define setjmp _setjmp
+#define longjmp _longjmp
+#endif
+
+/*
+ * if you don't have vprintf, try this and cross your fingers.
+ */
+#if defined(VPRINTF_MISSING)
+#define vfprintf(fp,fmt,arg) _doprnt((fmt), (arg), (fp))
+#endif
+
+#ifdef VMS
+/* some macros to redirect to code in vms/vms_misc.c */
+#define exit vms_exit
+#define open vms_open
+#define strerror vms_strerror
+#define strdup vms_strdup
+extern void exit P((int));
+extern int open P((const char *,int,...));
+extern char *strerror P((int));
+extern char *strdup P((const char *str));
+extern int vms_devopen P((const char *,int));
+# ifndef NO_TTY_FWRITE
+#define fwrite tty_fwrite
+#define fclose tty_fclose
+extern size_t fwrite P((const void *,size_t,size_t,FILE *));
+extern int fclose P((FILE *));
+# endif
+extern FILE *popen P((const char *,const char *));
+extern int pclose P((FILE *));
+extern void vms_arg_fixup P((int *,char ***));
+/* some things not in STDC_HEADERS */
+extern size_t gnu_strftime P((char *,size_t,const char *,const struct tm *));
+extern int unlink P((const char *));
+extern int getopt P((int,char **,char *));
+extern int isatty P((int));
+#ifndef fileno
+extern int fileno P((FILE *));
+#endif
+extern int close(), dup(), dup2(), fstat(), read(), stat();
+extern int getpgrp P((void));
+#endif /*VMS*/
+
+#define GNU_REGEX
+#ifdef GNU_REGEX
+#include "regex.h"
+#include "dfa.h"
+typedef struct Regexp {
+ struct re_pattern_buffer pat;
+ struct re_registers regs;
+ struct dfa dfareg;
+ int dfa;
+} Regexp;
+#define RESTART(rp,s) (rp)->regs.start[0]
+#define REEND(rp,s) (rp)->regs.end[0]
+#else /* GNU_REGEX */
+#endif /* GNU_REGEX */
+
+#ifdef atarist
+#define read _text_read /* we do not want all these CR's to mess our input */
+extern int _text_read (int, char *, int);
+#ifndef __MINT__
+#undef NGROUPS_MAX
+#endif /* __MINT__ */
+#endif
+
+#ifndef DEFPATH
+#define DEFPATH ".:/usr/local/lib/awk:/usr/lib/awk"
+#endif
+
+#ifndef ENVSEP
+#define ENVSEP ':'
+#endif
+
+extern double double_to_int P((double d));
+
+/* ------------------ Constants, Structures, Typedefs ------------------ */
+#define AWKNUM double
+
+typedef enum {
+ /* illegal entry == 0 */
+ Node_illegal,
+
+ /* binary operators lnode and rnode are the expressions to work on */
+ Node_times,
+ Node_quotient,
+ Node_mod,
+ Node_plus,
+ Node_minus,
+ Node_cond_pair, /* conditional pair (see Node_line_range) */
+ Node_subscript,
+ Node_concat,
+ Node_exp,
+
+ /* unary operators subnode is the expression to work on */
+/*10*/ Node_preincrement,
+ Node_predecrement,
+ Node_postincrement,
+ Node_postdecrement,
+ Node_unary_minus,
+ Node_field_spec,
+
+ /* assignments lnode is the var to assign to, rnode is the exp */
+ Node_assign,
+ Node_assign_times,
+ Node_assign_quotient,
+ Node_assign_mod,
+/*20*/ Node_assign_plus,
+ Node_assign_minus,
+ Node_assign_exp,
+
+ /* boolean binaries lnode and rnode are expressions */
+ Node_and,
+ Node_or,
+
+ /* binary relationals compares lnode and rnode */
+ Node_equal,
+ Node_notequal,
+ Node_less,
+ Node_greater,
+ Node_leq,
+/*30*/ Node_geq,
+ Node_match,
+ Node_nomatch,
+
+ /* unary relationals works on subnode */
+ Node_not,
+
+ /* program structures */
+ Node_rule_list, /* lnode is a rule, rnode is rest of list */
+ Node_rule_node, /* lnode is pattern, rnode is statement */
+ Node_statement_list, /* lnode is statement, rnode is more list */
+ Node_if_branches, /* lnode is to run on true, rnode on false */
+ Node_expression_list, /* lnode is an exp, rnode is more list */
+ Node_param_list, /* lnode is a variable, rnode is more list */
+
+ /* keywords */
+/*40*/ Node_K_if, /* lnode is conditonal, rnode is if_branches */
+ Node_K_while, /* lnode is condtional, rnode is stuff to run */
+ Node_K_for, /* lnode is for_struct, rnode is stuff to run */
+ Node_K_arrayfor, /* lnode is for_struct, rnode is stuff to run */
+ Node_K_break, /* no subs */
+ Node_K_continue, /* no stuff */
+ Node_K_print, /* lnode is exp_list, rnode is redirect */
+ Node_K_printf, /* lnode is exp_list, rnode is redirect */
+ Node_K_next, /* no subs */
+ Node_K_exit, /* subnode is return value, or NULL */
+/*50*/ Node_K_do, /* lnode is conditional, rnode stuff to run */
+ Node_K_return,
+ Node_K_delete,
+ Node_K_getline,
+ Node_K_function, /* lnode is statement list, rnode is params */
+
+ /* I/O redirection for print statements */
+ Node_redirect_output, /* subnode is where to redirect */
+ Node_redirect_append, /* subnode is where to redirect */
+ Node_redirect_pipe, /* subnode is where to redirect */
+ Node_redirect_pipein, /* subnode is where to redirect */
+ Node_redirect_input, /* subnode is where to redirect */
+
+ /* Variables */
+/*60*/ Node_var, /* rnode is value, lnode is array stuff */
+ Node_var_array, /* array is ptr to elements, asize num of
+ * eles */
+ Node_val, /* node is a value - type in flags */
+
+ /* Builtins subnode is explist to work on, proc is func to call */
+ Node_builtin,
+
+ /*
+ * pattern: conditional ',' conditional ; lnode of Node_line_range
+ * is the two conditionals (Node_cond_pair), other word (rnode place)
+ * is a flag indicating whether or not this range has been entered.
+ */
+ Node_line_range,
+
+ /*
+ * boolean test of membership in array lnode is string-valued
+ * expression rnode is array name
+ */
+ Node_in_array,
+
+ Node_func, /* lnode is param. list, rnode is body */
+ Node_func_call, /* lnode is name, rnode is argument list */
+
+ Node_cond_exp, /* lnode is conditonal, rnode is if_branches */
+ Node_regex,
+/*70*/ Node_hashnode,
+ Node_ahash,
+ Node_NF,
+ Node_NR,
+ Node_FNR,
+ Node_FS,
+ Node_RS,
+ Node_FIELDWIDTHS,
+ Node_IGNORECASE,
+ Node_OFS,
+ Node_ORS,
+ Node_OFMT,
+ Node_CONVFMT,
+ Node_K_nextfile
+} NODETYPE;
+
+/*
+ * NOTE - this struct is a rather kludgey -- it is packed to minimize
+ * space usage, at the expense of cleanliness. Alter at own risk.
+ */
+typedef struct exp_node {
+ union {
+ struct {
+ union {
+ struct exp_node *lptr;
+ char *param_name;
+ long ll;
+ } l;
+ union {
+ struct exp_node *rptr;
+ struct exp_node *(*pptr) ();
+ Regexp *preg;
+ struct for_loop_header *hd;
+ struct exp_node **av;
+ int r_ent; /* range entered */
+ } r;
+ union {
+ char *name;
+ struct exp_node *extra;
+ long xl;
+ } x;
+ short number;
+ unsigned char reflags;
+# define CASE 1
+# define CONST 2
+# define FS_DFLT 4
+ } nodep;
+ struct {
+ AWKNUM fltnum; /* this is here for optimal packing of
+ * the structure on many machines
+ */
+ char *sp;
+ size_t slen;
+ unsigned char sref;
+ char idx;
+ } val;
+ struct {
+ struct exp_node *next;
+ char *name;
+ size_t length;
+ struct exp_node *value;
+ } hash;
+#define hnext sub.hash.next
+#define hname sub.hash.name
+#define hlength sub.hash.length
+#define hvalue sub.hash.value
+ struct {
+ struct exp_node *next;
+ struct exp_node *name;
+ struct exp_node *value;
+ } ahash;
+#define ahnext sub.ahash.next
+#define ahname sub.ahash.name
+#define ahvalue sub.ahash.value
+ } sub;
+ NODETYPE type;
+ unsigned short flags;
+# define MALLOC 1 /* can be free'd */
+# define TEMP 2 /* should be free'd */
+# define PERM 4 /* can't be free'd */
+# define STRING 8 /* assigned as string */
+# define STR 16 /* string value is current */
+# define NUM 32 /* numeric value is current */
+# define NUMBER 64 /* assigned as number */
+# define MAYBE_NUM 128 /* user input: if NUMERIC then
+ * a NUMBER */
+# define ARRAYMAXED 256 /* array is at max size */
+ char *vname; /* variable's name */
+} NODE;
+
+#define lnode sub.nodep.l.lptr
+#define nextp sub.nodep.l.lptr
+#define rnode sub.nodep.r.rptr
+#define source_file sub.nodep.x.name
+#define source_line sub.nodep.number
+#define param_cnt sub.nodep.number
+#define param sub.nodep.l.param_name
+
+#define subnode lnode
+#define proc sub.nodep.r.pptr
+
+#define re_reg sub.nodep.r.preg
+#define re_flags sub.nodep.reflags
+#define re_text lnode
+#define re_exp sub.nodep.x.extra
+#define re_cnt sub.nodep.number
+
+#define forsub lnode
+#define forloop rnode->sub.nodep.r.hd
+
+#define stptr sub.val.sp
+#define stlen sub.val.slen
+#define stref sub.val.sref
+#define stfmt sub.val.idx
+
+#define numbr sub.val.fltnum
+
+#define var_value lnode
+#define var_array sub.nodep.r.av
+#define array_size sub.nodep.l.ll
+#define table_size sub.nodep.x.xl
+
+#define condpair lnode
+#define triggered sub.nodep.r.r_ent
+
+#ifdef DONTDEF
+int primes[] = {31, 61, 127, 257, 509, 1021, 2053, 4099, 8191, 16381};
+#endif
+
+typedef struct for_loop_header {
+ NODE *init;
+ NODE *cond;
+ NODE *incr;
+} FOR_LOOP_HEADER;
+
+/* for "for(iggy in foo) {" */
+struct search {
+ NODE *sym;
+ size_t idx;
+ NODE *bucket;
+ NODE *retval;
+};
+
+/* for faster input, bypass stdio */
+typedef struct iobuf {
+ int fd;
+ char *buf;
+ char *off;
+ char *end;
+ size_t size; /* this will be determined by an fstat() call */
+ int cnt;
+ long secsiz;
+ int flag;
+# define IOP_IS_TTY 1
+# define IOP_IS_INTERNAL 2
+# define IOP_NO_FREE 4
+} IOBUF;
+
+typedef void (*Func_ptr)();
+
+/*
+ * structure used to dynamically maintain a linked-list of open files/pipes
+ */
+struct redirect {
+ unsigned int flag;
+# define RED_FILE 1
+# define RED_PIPE 2
+# define RED_READ 4
+# define RED_WRITE 8
+# define RED_APPEND 16
+# define RED_NOBUF 32
+# define RED_USED 64
+# define RED_EOF 128
+ char *value;
+ FILE *fp;
+ IOBUF *iop;
+ int pid;
+ int status;
+ struct redirect *prev;
+ struct redirect *next;
+};
+
+/* structure for our source, either a command line string or a source file */
+struct src {
+ enum srctype { CMDLINE = 1, SOURCEFILE } stype;
+ char *val;
+};
+
+/* longjmp return codes, must be nonzero */
+/* Continue means either for loop/while continue, or next input record */
+#define TAG_CONTINUE 1
+/* Break means either for/while break, or stop reading input */
+#define TAG_BREAK 2
+/* Return means return from a function call; leave value in ret_node */
+#define TAG_RETURN 3
+
+#ifndef INT_MAX
+#define INT_MAX (~(1 << (sizeof (int) * 8 - 1)))
+#endif
+#ifndef LONG_MAX
+#define LONG_MAX (~(1 << (sizeof (long) * 8 - 1)))
+#endif
+#ifndef ULONG_MAX
+#define ULONG_MAX (~(unsigned long)0)
+#endif
+#ifndef LONG_MIN
+#define LONG_MIN (-LONG_MAX - 1)
+#endif
+#define HUGE INT_MAX
+
+/* -------------------------- External variables -------------------------- */
+/* gawk builtin variables */
+extern long NF;
+extern long NR;
+extern long FNR;
+extern int IGNORECASE;
+extern char *RS;
+extern char *OFS;
+extern int OFSlen;
+extern char *ORS;
+extern int ORSlen;
+extern char *OFMT;
+extern char *CONVFMT;
+extern int CONVFMTidx;
+extern int OFMTidx;
+extern NODE *FS_node, *NF_node, *RS_node, *NR_node;
+extern NODE *FILENAME_node, *OFS_node, *ORS_node, *OFMT_node;
+extern NODE *CONVFMT_node;
+extern NODE *FNR_node, *RLENGTH_node, *RSTART_node, *SUBSEP_node;
+extern NODE *IGNORECASE_node;
+extern NODE *FIELDWIDTHS_node;
+
+extern NODE **stack_ptr;
+extern NODE *Nnull_string;
+extern NODE **fields_arr;
+extern int sourceline;
+extern char *source;
+extern NODE *expression_value;
+
+extern NODE *_t; /* used as temporary in tree_eval */
+
+extern const char *myname;
+
+extern NODE *nextfree;
+extern int field0_valid;
+extern int do_unix;
+extern int do_posix;
+extern int do_lint;
+extern int in_begin_rule;
+extern int in_end_rule;
+
+/* ------------------------- Pseudo-functions ------------------------- */
+
+#define is_identchar(c) (isalnum(c) || (c) == '_')
+
+
+#ifndef MPROF
+#define getnode(n) if (nextfree) n = nextfree, nextfree = nextfree->nextp;\
+ else n = more_nodes()
+#define freenode(n) ((n)->nextp = nextfree, nextfree = (n))
+#else
+#define getnode(n) emalloc(n, NODE *, sizeof(NODE), "getnode")
+#define freenode(n) free(n)
+#endif
+
+#ifdef DEBUG
+#define tree_eval(t) r_tree_eval(t)
+#define get_lhs(p, a) r_get_lhs((p), (a))
+#undef freenode
+#else
+#define get_lhs(p, a) ((p)->type == Node_var ? (&(p)->var_value) : \
+ r_get_lhs((p), (a)))
+#define tree_eval(t) (_t = (t),_t == NULL ? Nnull_string : \
+ (_t->type == Node_param_list ? r_tree_eval(_t) : \
+ (_t->type == Node_val ? _t : \
+ (_t->type == Node_var ? _t->var_value : \
+ r_tree_eval(_t)))))
+#endif
+
+#define make_number(x) mk_number((x), (unsigned int)(MALLOC|NUM|NUMBER))
+#define tmp_number(x) mk_number((x), (unsigned int)(MALLOC|TEMP|NUM|NUMBER))
+
+#define free_temp(n) do {if ((n)->flags&TEMP) { unref(n); }} while (0)
+#define make_string(s,l) make_str_node((s), SZTC (l),0)
+#define SCAN 1
+#define ALREADY_MALLOCED 2
+
+#define cant_happen() fatal("internal error line %d, file: %s", \
+ __LINE__, __FILE__);
+
+#if defined(__STDC__) && !defined(NO_TOKEN_PASTING)
+#define emalloc(var,ty,x,str) (void)((var=(ty)malloc((MALLOC_ARG_T)(x))) ||\
+ (fatal("%s: %s: can't allocate memory (%s)",\
+ (str), #var, strerror(errno)),0))
+#define erealloc(var,ty,x,str) (void)((var=(ty)realloc((char *)var,\
+ (MALLOC_ARG_T)(x))) ||\
+ (fatal("%s: %s: can't allocate memory (%s)",\
+ (str), #var, strerror(errno)),0))
+#else /* __STDC__ */
+#define emalloc(var,ty,x,str) (void)((var=(ty)malloc((MALLOC_ARG_T)(x))) ||\
+ (fatal("%s: %s: can't allocate memory (%s)",\
+ (str), "var", strerror(errno)),0))
+#define erealloc(var,ty,x,str) (void)((var=(ty)realloc((char *)var,\
+ (MALLOC_ARG_T)(x))) ||\
+ (fatal("%s: %s: can't allocate memory (%s)",\
+ (str), "var", strerror(errno)),0))
+#endif /* __STDC__ */
+
+#ifdef DEBUG
+#define force_number r_force_number
+#define force_string r_force_string
+#else /* not DEBUG */
+#ifdef lint
+extern AWKNUM force_number();
+#endif
+#ifdef MSDOS
+extern double _msc51bug;
+#define force_number(n) (_msc51bug=(_t = (n),(_t->flags & NUM) ? _t->numbr : r_force_number(_t)))
+#else /* not MSDOS */
+#define force_number(n) (_t = (n),(_t->flags & NUM) ? _t->numbr : r_force_number(_t))
+#endif /* MSDOS */
+#define force_string(s) (_t = (s),(_t->flags & STR) ? _t : r_force_string(_t))
+#endif /* not DEBUG */
+
+#define STREQ(a,b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+#define STREQN(a,b,n) ((n)&& *(a)== *(b) && strncmp((a), (b), SZTC (n)) == 0)
+
+/* ------------- Function prototypes or defs (as appropriate) ------------- */
+
+/* array.c */
+extern NODE *concat_exp P((NODE *tree));
+extern void assoc_clear P((NODE *symbol));
+extern unsigned int hash P((const char *s, size_t len, unsigned long hsize));
+extern int in_array P((NODE *symbol, NODE *subs));
+extern NODE **assoc_lookup P((NODE *symbol, NODE *subs));
+extern void do_delete P((NODE *symbol, NODE *tree));
+extern void assoc_scan P((NODE *symbol, struct search *lookat));
+extern void assoc_next P((struct search *lookat));
+/* awk.tab.c */
+extern char *tokexpand P((void));
+extern char nextc P((void));
+extern NODE *node P((NODE *left, NODETYPE op, NODE *right));
+extern NODE *install P((char *name, NODE *value));
+extern NODE *lookup P((const char *name));
+extern NODE *variable P((char *name, int can_free));
+extern int yyparse P((void));
+/* builtin.c */
+extern NODE *do_exp P((NODE *tree));
+extern NODE *do_index P((NODE *tree));
+extern NODE *do_int P((NODE *tree));
+extern NODE *do_length P((NODE *tree));
+extern NODE *do_log P((NODE *tree));
+extern NODE *do_sprintf P((NODE *tree));
+extern void do_printf P((NODE *tree));
+extern void print_simple P((NODE *tree, FILE *fp));
+extern NODE *do_sqrt P((NODE *tree));
+extern NODE *do_substr P((NODE *tree));
+extern NODE *do_strftime P((NODE *tree));
+extern NODE *do_systime P((NODE *tree));
+extern NODE *do_system P((NODE *tree));
+extern void do_print P((NODE *tree));
+extern NODE *do_tolower P((NODE *tree));
+extern NODE *do_toupper P((NODE *tree));
+extern NODE *do_atan2 P((NODE *tree));
+extern NODE *do_sin P((NODE *tree));
+extern NODE *do_cos P((NODE *tree));
+extern NODE *do_rand P((NODE *tree));
+extern NODE *do_srand P((NODE *tree));
+extern NODE *do_match P((NODE *tree));
+extern NODE *do_gsub P((NODE *tree));
+extern NODE *do_sub P((NODE *tree));
+/* eval.c */
+extern int interpret P((NODE *volatile tree));
+extern NODE *r_tree_eval P((NODE *tree));
+extern int cmp_nodes P((NODE *t1, NODE *t2));
+extern NODE **r_get_lhs P((NODE *ptr, Func_ptr *assign));
+extern void set_IGNORECASE P((void));
+void set_OFS P((void));
+void set_ORS P((void));
+void set_OFMT P((void));
+void set_CONVFMT P((void));
+/* field.c */
+extern void init_fields P((void));
+extern void set_record P((char *buf, int cnt, int freeold));
+extern void reset_record P((void));
+extern void set_NF P((void));
+extern NODE **get_field P((int num, Func_ptr *assign));
+extern NODE *do_split P((NODE *tree));
+extern void set_FS P((void));
+extern void set_RS P((void));
+extern void set_FIELDWIDTHS P((void));
+/* io.c */
+extern void set_FNR P((void));
+extern void set_NR P((void));
+extern void do_input P((void));
+extern struct redirect *redirect P((NODE *tree, int *errflg));
+extern NODE *do_close P((NODE *tree));
+extern int flush_io P((void));
+extern int close_io P((void));
+extern int devopen P((const char *name, const char *mode));
+extern int pathopen P((const char *file));
+extern NODE *do_getline P((NODE *tree));
+extern void do_nextfile P((void));
+/* iop.c */
+extern int optimal_bufsize P((int fd));
+extern IOBUF *iop_alloc P((int fd));
+extern int get_a_record P((char **out, IOBUF *iop, int rs, int *errcode));
+/* main.c */
+extern int main P((int argc, char **argv));
+extern Regexp *mk_re_parse P((char *s, int ignorecase));
+extern void load_environ P((void));
+extern char *arg_assign P((char *arg));
+extern SIGTYPE catchsig P((int sig, int code));
+/* msg.c */
+extern void err P((const char *s, const char *emsg, va_list argp));
+#if _MSC_VER == 510
+extern void msg P((va_list va_alist, ...));
+extern void warning P((va_list va_alist, ...));
+extern void fatal P((va_list va_alist, ...));
+#else
+extern void msg ();
+extern void warning ();
+extern void fatal ();
+#endif
+/* node.c */
+extern AWKNUM r_force_number P((NODE *n));
+extern NODE *r_force_string P((NODE *s));
+extern NODE *dupnode P((NODE *n));
+extern NODE *mk_number P((AWKNUM x, unsigned int flags));
+extern NODE *make_str_node P((char *s, size_t len, int scan ));
+extern NODE *tmp_string P((char *s, size_t len ));
+extern NODE *more_nodes P((void));
+#ifdef DEBUG
+extern void freenode P((NODE *it));
+#endif
+extern void unref P((NODE *tmp));
+extern int parse_escape P((char **string_ptr));
+/* re.c */
+extern Regexp *make_regexp P((char *s, size_t len, int ignorecase, int dfa));
+extern int research P((Regexp *rp, char *str, int start,
+ size_t len, int need_start));
+extern void refree P((Regexp *rp));
+extern void reg_error P((const char *s));
+extern Regexp *re_update P((NODE *t));
+extern void resyntax P((int syntax));
+extern void resetup P((void));
+
+/* strcase.c */
+extern int strcasecmp P((const char *s1, const char *s2));
+extern int strncasecmp P((const char *s1, const char *s2, register size_t n));
+
+#ifdef atarist
+/* atari/tmpnam.c */
+extern char *tmpnam P((char *buf));
+extern char *tempnam P((const char *path, const char *base));
+#endif
+
+/* Figure out what '\a' really is. */
+#ifdef __STDC__
+#define BELL '\a' /* sure makes life easy, don't it? */
+#else
+# if 'z' - 'a' == 25 /* ascii */
+# if 'a' != 97 /* machine is dumb enough to use mark parity */
+# define BELL '\207'
+# else
+# define BELL '\07'
+# endif
+# else
+# define BELL '\057'
+# endif
+#endif
+
+extern char casetable[]; /* for case-independent regexp matching */
diff --git a/gnu/usr.bin/awk/awk.y b/gnu/usr.bin/awk/awk.y
new file mode 100644
index 0000000..175cea9
--- /dev/null
+++ b/gnu/usr.bin/awk/awk.y
@@ -0,0 +1,1868 @@
+/*
+ * awk.y --- yacc/bison parser
+ */
+
+/*
+ * Copyright (C) 1986, 1988, 1989, 1991, 1992, 1993 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Progamming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GAWK; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+%{
+#ifdef DEBUG
+#define YYDEBUG 12
+#endif
+
+#include "awk.h"
+
+static void yyerror (); /* va_alist */
+static char *get_src_buf P((void));
+static int yylex P((void));
+static NODE *node_common P((NODETYPE op));
+static NODE *snode P((NODE *subn, NODETYPE op, int sindex));
+static NODE *mkrangenode P((NODE *cpair));
+static NODE *make_for_loop P((NODE *init, NODE *cond, NODE *incr));
+static NODE *append_right P((NODE *list, NODE *new));
+static void func_install P((NODE *params, NODE *def));
+static void pop_var P((NODE *np, int freeit));
+static void pop_params P((NODE *params));
+static NODE *make_param P((char *name));
+static NODE *mk_rexp P((NODE *exp));
+
+static int want_assign; /* lexical scanning kludge */
+static int want_regexp; /* lexical scanning kludge */
+static int can_return; /* lexical scanning kludge */
+static int io_allowed = 1; /* lexical scanning kludge */
+static char *lexptr; /* pointer to next char during parsing */
+static char *lexend;
+static char *lexptr_begin; /* keep track of where we were for error msgs */
+static char *lexeme; /* beginning of lexeme for debugging */
+static char *thisline = NULL;
+#define YYDEBUG_LEXER_TEXT (lexeme)
+static int param_counter;
+static char *tokstart = NULL;
+static char *tok = NULL;
+static char *tokend;
+
+#define HASHSIZE 1021 /* this constant only used here */
+NODE *variables[HASHSIZE];
+
+extern char *source;
+extern int sourceline;
+extern struct src *srcfiles;
+extern int numfiles;
+extern int errcount;
+extern NODE *begin_block;
+extern NODE *end_block;
+%}
+
+%union {
+ long lval;
+ AWKNUM fval;
+ NODE *nodeval;
+ NODETYPE nodetypeval;
+ char *sval;
+ NODE *(*ptrval)();
+}
+
+%type <nodeval> function_prologue function_body
+%type <nodeval> rexp exp start program rule simp_exp
+%type <nodeval> non_post_simp_exp
+%type <nodeval> pattern
+%type <nodeval> action variable param_list
+%type <nodeval> rexpression_list opt_rexpression_list
+%type <nodeval> expression_list opt_expression_list
+%type <nodeval> statements statement if_statement opt_param_list
+%type <nodeval> opt_exp opt_variable regexp
+%type <nodeval> input_redir output_redir
+%type <nodetypeval> print
+%type <sval> func_name
+%type <lval> lex_builtin
+
+%token <sval> FUNC_CALL NAME REGEXP
+%token <lval> ERROR
+%token <nodeval> YNUMBER YSTRING
+%token <nodetypeval> RELOP APPEND_OP
+%token <nodetypeval> ASSIGNOP MATCHOP NEWLINE CONCAT_OP
+%token <nodetypeval> LEX_BEGIN LEX_END LEX_IF LEX_ELSE LEX_RETURN LEX_DELETE
+%token <nodetypeval> LEX_WHILE LEX_DO LEX_FOR LEX_BREAK LEX_CONTINUE
+%token <nodetypeval> LEX_PRINT LEX_PRINTF LEX_NEXT LEX_EXIT LEX_FUNCTION
+%token <nodetypeval> LEX_GETLINE
+%token <nodetypeval> LEX_IN
+%token <lval> LEX_AND LEX_OR INCREMENT DECREMENT
+%token <lval> LEX_BUILTIN LEX_LENGTH
+
+/* these are just yylval numbers */
+
+/* Lowest to highest */
+%right ASSIGNOP
+%right '?' ':'
+%left LEX_OR
+%left LEX_AND
+%left LEX_GETLINE
+%nonassoc LEX_IN
+%left FUNC_CALL LEX_BUILTIN LEX_LENGTH
+%nonassoc ','
+%nonassoc MATCHOP
+%nonassoc RELOP '<' '>' '|' APPEND_OP
+%left CONCAT_OP
+%left YSTRING YNUMBER
+%left '+' '-'
+%left '*' '/' '%'
+%right '!' UNARY
+%right '^'
+%left INCREMENT DECREMENT
+%left '$'
+%left '(' ')'
+%%
+
+start
+ : opt_nls program opt_nls
+ { expression_value = $2; }
+ ;
+
+program
+ : rule
+ {
+ if ($1 != NULL)
+ $$ = $1;
+ else
+ $$ = NULL;
+ yyerrok;
+ }
+ | program rule
+ /* add the rule to the tail of list */
+ {
+ if ($2 == NULL)
+ $$ = $1;
+ else if ($1 == NULL)
+ $$ = $2;
+ else {
+ if ($1->type != Node_rule_list)
+ $1 = node($1, Node_rule_list,
+ (NODE*)NULL);
+ $$ = append_right ($1,
+ node($2, Node_rule_list,(NODE *) NULL));
+ }
+ yyerrok;
+ }
+ | error { $$ = NULL; }
+ | program error { $$ = NULL; }
+ | /* empty */ { $$ = NULL; }
+ ;
+
+rule
+ : LEX_BEGIN { io_allowed = 0; }
+ action
+ {
+ if (begin_block) {
+ if (begin_block->type != Node_rule_list)
+ begin_block = node(begin_block, Node_rule_list,
+ (NODE *)NULL);
+ (void) append_right (begin_block, node(
+ node((NODE *)NULL, Node_rule_node, $3),
+ Node_rule_list, (NODE *)NULL) );
+ } else
+ begin_block = node((NODE *)NULL, Node_rule_node, $3);
+ $$ = NULL;
+ io_allowed = 1;
+ yyerrok;
+ }
+ | LEX_END { io_allowed = 0; }
+ action
+ {
+ if (end_block) {
+ if (end_block->type != Node_rule_list)
+ end_block = node(end_block, Node_rule_list,
+ (NODE *)NULL);
+ (void) append_right (end_block, node(
+ node((NODE *)NULL, Node_rule_node, $3),
+ Node_rule_list, (NODE *)NULL));
+ } else
+ end_block = node((NODE *)NULL, Node_rule_node, $3);
+ $$ = NULL;
+ io_allowed = 1;
+ yyerrok;
+ }
+ | LEX_BEGIN statement_term
+ {
+ warning("BEGIN blocks must have an action part");
+ errcount++;
+ yyerrok;
+ }
+ | LEX_END statement_term
+ {
+ warning("END blocks must have an action part");
+ errcount++;
+ yyerrok;
+ }
+ | pattern action
+ { $$ = node ($1, Node_rule_node, $2); yyerrok; }
+ | action
+ { $$ = node ((NODE *)NULL, Node_rule_node, $1); yyerrok; }
+ | pattern statement_term
+ {
+ $$ = node ($1,
+ Node_rule_node,
+ node(node(node(make_number(0.0),
+ Node_field_spec,
+ (NODE *) NULL),
+ Node_expression_list,
+ (NODE *) NULL),
+ Node_K_print,
+ (NODE *) NULL));
+ yyerrok;
+ }
+ | function_prologue function_body
+ {
+ func_install($1, $2);
+ $$ = NULL;
+ yyerrok;
+ }
+ ;
+
+func_name
+ : NAME
+ { $$ = $1; }
+ | FUNC_CALL
+ { $$ = $1; }
+ | lex_builtin
+ {
+ yyerror("%s() is a built-in function, it cannot be redefined",
+ tokstart);
+ errcount++;
+ /* yyerrok; */
+ }
+ ;
+
+lex_builtin
+ : LEX_BUILTIN
+ | LEX_LENGTH
+ ;
+
+function_prologue
+ : LEX_FUNCTION
+ {
+ param_counter = 0;
+ }
+ func_name '(' opt_param_list r_paren opt_nls
+ {
+ $$ = append_right(make_param($3), $5);
+ can_return = 1;
+ }
+ ;
+
+function_body
+ : l_brace statements r_brace opt_semi
+ {
+ $$ = $2;
+ can_return = 0;
+ }
+ ;
+
+
+pattern
+ : exp
+ { $$ = $1; }
+ | exp ',' exp
+ { $$ = mkrangenode ( node($1, Node_cond_pair, $3) ); }
+ ;
+
+regexp
+ /*
+ * In this rule, want_regexp tells yylex that the next thing
+ * is a regexp so it should read up to the closing slash.
+ */
+ : '/'
+ { ++want_regexp; }
+ REGEXP '/'
+ {
+ NODE *n;
+ size_t len;
+
+ getnode(n);
+ n->type = Node_regex;
+ len = strlen($3);
+ n->re_exp = make_string($3, len);
+ n->re_reg = make_regexp($3, len, 0, 1);
+ n->re_text = NULL;
+ n->re_flags = CONST;
+ n->re_cnt = 1;
+ $$ = n;
+ }
+ ;
+
+action
+ : l_brace statements r_brace opt_semi opt_nls
+ { $$ = $2 ; }
+ | l_brace r_brace opt_semi opt_nls
+ { $$ = NULL; }
+ ;
+
+statements
+ : statement
+ { $$ = $1; }
+ | statements statement
+ {
+ if ($1 == NULL || $1->type != Node_statement_list)
+ $1 = node($1, Node_statement_list,(NODE *)NULL);
+ $$ = append_right($1,
+ node( $2, Node_statement_list, (NODE *)NULL));
+ yyerrok;
+ }
+ | error
+ { $$ = NULL; }
+ | statements error
+ { $$ = NULL; }
+ ;
+
+statement_term
+ : nls
+ | semi opt_nls
+ ;
+
+statement
+ : semi opt_nls
+ { $$ = NULL; }
+ | l_brace r_brace
+ { $$ = NULL; }
+ | l_brace statements r_brace
+ { $$ = $2; }
+ | if_statement
+ { $$ = $1; }
+ | LEX_WHILE '(' exp r_paren opt_nls statement
+ { $$ = node ($3, Node_K_while, $6); }
+ | LEX_DO opt_nls statement LEX_WHILE '(' exp r_paren opt_nls
+ { $$ = node ($6, Node_K_do, $3); }
+ | LEX_FOR '(' NAME LEX_IN NAME r_paren opt_nls statement
+ {
+ $$ = node ($8, Node_K_arrayfor, make_for_loop(variable($3,1),
+ (NODE *)NULL, variable($5,1)));
+ }
+ | LEX_FOR '(' opt_exp semi exp semi opt_exp r_paren opt_nls statement
+ {
+ $$ = node($10, Node_K_for, (NODE *)make_for_loop($3, $5, $7));
+ }
+ | LEX_FOR '(' opt_exp semi semi opt_exp r_paren opt_nls statement
+ {
+ $$ = node ($9, Node_K_for,
+ (NODE *)make_for_loop($3, (NODE *)NULL, $6));
+ }
+ | LEX_BREAK statement_term
+ /* for break, maybe we'll have to remember where to break to */
+ { $$ = node ((NODE *)NULL, Node_K_break, (NODE *)NULL); }
+ | LEX_CONTINUE statement_term
+ /* similarly */
+ { $$ = node ((NODE *)NULL, Node_K_continue, (NODE *)NULL); }
+ | print '(' expression_list r_paren output_redir statement_term
+ { $$ = node ($3, $1, $5); }
+ | print opt_rexpression_list output_redir statement_term
+ {
+ if ($1 == Node_K_print && $2 == NULL)
+ $2 = node(node(make_number(0.0),
+ Node_field_spec,
+ (NODE *) NULL),
+ Node_expression_list,
+ (NODE *) NULL);
+
+ $$ = node ($2, $1, $3);
+ }
+ | LEX_NEXT opt_exp statement_term
+ { NODETYPE type;
+
+ if ($2 && $2 == lookup("file")) {
+ if (do_lint)
+ warning("`next file' is a gawk extension");
+ if (do_unix || do_posix) {
+ /*
+ * can't use yyerror, since may have overshot
+ * the source line
+ */
+ errcount++;
+ msg("`next file' is a gawk extension");
+ }
+ if (! io_allowed) {
+ /* same thing */
+ errcount++;
+ msg("`next file' used in BEGIN or END action");
+ }
+ type = Node_K_nextfile;
+ } else {
+ if (! io_allowed)
+ yyerror("next used in BEGIN or END action");
+ type = Node_K_next;
+ }
+ $$ = node ((NODE *)NULL, type, (NODE *)NULL);
+ }
+ | LEX_EXIT opt_exp statement_term
+ { $$ = node ($2, Node_K_exit, (NODE *)NULL); }
+ | LEX_RETURN
+ { if (! can_return) yyerror("return used outside function context"); }
+ opt_exp statement_term
+ { $$ = node ($3, Node_K_return, (NODE *)NULL); }
+ | LEX_DELETE NAME '[' expression_list ']' statement_term
+ { $$ = node (variable($2,1), Node_K_delete, $4); }
+ | LEX_DELETE NAME statement_term
+ {
+ if (do_lint)
+ warning("`delete array' is a gawk extension");
+ if (do_unix || do_posix) {
+ /*
+ * can't use yyerror, since may have overshot
+ * the source line
+ */
+ errcount++;
+ msg("`delete array' is a gawk extension");
+ }
+ $$ = node (variable($2,1), Node_K_delete, (NODE *) NULL);
+ }
+ | exp statement_term
+ { $$ = $1; }
+ ;
+
+print
+ : LEX_PRINT
+ { $$ = $1; }
+ | LEX_PRINTF
+ { $$ = $1; }
+ ;
+
+if_statement
+ : LEX_IF '(' exp r_paren opt_nls statement
+ {
+ $$ = node($3, Node_K_if,
+ node($6, Node_if_branches, (NODE *)NULL));
+ }
+ | LEX_IF '(' exp r_paren opt_nls statement
+ LEX_ELSE opt_nls statement
+ { $$ = node ($3, Node_K_if,
+ node ($6, Node_if_branches, $9)); }
+ ;
+
+nls
+ : NEWLINE
+ { want_assign = 0; }
+ | nls NEWLINE
+ ;
+
+opt_nls
+ : /* empty */
+ | nls
+ ;
+
+input_redir
+ : /* empty */
+ { $$ = NULL; }
+ | '<' simp_exp
+ { $$ = node ($2, Node_redirect_input, (NODE *)NULL); }
+ ;
+
+output_redir
+ : /* empty */
+ { $$ = NULL; }
+ | '>' exp
+ { $$ = node ($2, Node_redirect_output, (NODE *)NULL); }
+ | APPEND_OP exp
+ { $$ = node ($2, Node_redirect_append, (NODE *)NULL); }
+ | '|' exp
+ { $$ = node ($2, Node_redirect_pipe, (NODE *)NULL); }
+ ;
+
+opt_param_list
+ : /* empty */
+ { $$ = NULL; }
+ | param_list
+ { $$ = $1; }
+ ;
+
+param_list
+ : NAME
+ { $$ = make_param($1); }
+ | param_list comma NAME
+ { $$ = append_right($1, make_param($3)); yyerrok; }
+ | error
+ { $$ = NULL; }
+ | param_list error
+ { $$ = NULL; }
+ | param_list comma error
+ { $$ = NULL; }
+ ;
+
+/* optional expression, as in for loop */
+opt_exp
+ : /* empty */
+ { $$ = NULL; }
+ | exp
+ { $$ = $1; }
+ ;
+
+opt_rexpression_list
+ : /* empty */
+ { $$ = NULL; }
+ | rexpression_list
+ { $$ = $1; }
+ ;
+
+rexpression_list
+ : rexp
+ { $$ = node ($1, Node_expression_list, (NODE *)NULL); }
+ | rexpression_list comma rexp
+ {
+ $$ = append_right($1,
+ node( $3, Node_expression_list, (NODE *)NULL));
+ yyerrok;
+ }
+ | error
+ { $$ = NULL; }
+ | rexpression_list error
+ { $$ = NULL; }
+ | rexpression_list error rexp
+ { $$ = NULL; }
+ | rexpression_list comma error
+ { $$ = NULL; }
+ ;
+
+opt_expression_list
+ : /* empty */
+ { $$ = NULL; }
+ | expression_list
+ { $$ = $1; }
+ ;
+
+expression_list
+ : exp
+ { $$ = node ($1, Node_expression_list, (NODE *)NULL); }
+ | expression_list comma exp
+ {
+ $$ = append_right($1,
+ node( $3, Node_expression_list, (NODE *)NULL));
+ yyerrok;
+ }
+ | error
+ { $$ = NULL; }
+ | expression_list error
+ { $$ = NULL; }
+ | expression_list error exp
+ { $$ = NULL; }
+ | expression_list comma error
+ { $$ = NULL; }
+ ;
+
+/* Expressions, not including the comma operator. */
+exp : variable ASSIGNOP
+ { want_assign = 0; }
+ exp
+ {
+ if (do_lint && $4->type == Node_regex)
+ warning("Regular expression on left of assignment.");
+ $$ = node ($1, $2, $4);
+ }
+ | '(' expression_list r_paren LEX_IN NAME
+ { $$ = node (variable($5,1), Node_in_array, $2); }
+ | exp '|' LEX_GETLINE opt_variable
+ {
+ $$ = node ($4, Node_K_getline,
+ node ($1, Node_redirect_pipein, (NODE *)NULL));
+ }
+ | LEX_GETLINE opt_variable input_redir
+ {
+ if (do_lint && ! io_allowed && $3 == NULL)
+ warning("non-redirected getline undefined inside BEGIN or END action");
+ $$ = node ($2, Node_K_getline, $3);
+ }
+ | exp LEX_AND exp
+ { $$ = node ($1, Node_and, $3); }
+ | exp LEX_OR exp
+ { $$ = node ($1, Node_or, $3); }
+ | exp MATCHOP exp
+ {
+ if ($1->type == Node_regex)
+ warning("Regular expression on left of MATCH operator.");
+ $$ = node ($1, $2, mk_rexp($3));
+ }
+ | regexp
+ { $$ = $1; }
+ | '!' regexp %prec UNARY
+ {
+ $$ = node(node(make_number(0.0),
+ Node_field_spec,
+ (NODE *) NULL),
+ Node_nomatch,
+ $2);
+ }
+ | exp LEX_IN NAME
+ { $$ = node (variable($3,1), Node_in_array, $1); }
+ | exp RELOP exp
+ {
+ if (do_lint && $3->type == Node_regex)
+ warning("Regular expression on left of comparison.");
+ $$ = node ($1, $2, $3);
+ }
+ | exp '<' exp
+ { $$ = node ($1, Node_less, $3); }
+ | exp '>' exp
+ { $$ = node ($1, Node_greater, $3); }
+ | exp '?' exp ':' exp
+ { $$ = node($1, Node_cond_exp, node($3, Node_if_branches, $5));}
+ | simp_exp
+ { $$ = $1; }
+ | exp simp_exp %prec CONCAT_OP
+ { $$ = node ($1, Node_concat, $2); }
+ ;
+
+rexp
+ : variable ASSIGNOP
+ { want_assign = 0; }
+ rexp
+ { $$ = node ($1, $2, $4); }
+ | rexp LEX_AND rexp
+ { $$ = node ($1, Node_and, $3); }
+ | rexp LEX_OR rexp
+ { $$ = node ($1, Node_or, $3); }
+ | LEX_GETLINE opt_variable input_redir
+ {
+ if (do_lint && ! io_allowed && $3 == NULL)
+ warning("non-redirected getline undefined inside BEGIN or END action");
+ $$ = node ($2, Node_K_getline, $3);
+ }
+ | regexp
+ { $$ = $1; }
+ | '!' regexp %prec UNARY
+ { $$ = node((NODE *) NULL, Node_nomatch, $2); }
+ | rexp MATCHOP rexp
+ { $$ = node ($1, $2, mk_rexp($3)); }
+ | rexp LEX_IN NAME
+ { $$ = node (variable($3,1), Node_in_array, $1); }
+ | rexp RELOP rexp
+ { $$ = node ($1, $2, $3); }
+ | rexp '?' rexp ':' rexp
+ { $$ = node($1, Node_cond_exp, node($3, Node_if_branches, $5));}
+ | simp_exp
+ { $$ = $1; }
+ | rexp simp_exp %prec CONCAT_OP
+ { $$ = node ($1, Node_concat, $2); }
+ ;
+
+simp_exp
+ : non_post_simp_exp
+ /* Binary operators in order of decreasing precedence. */
+ | simp_exp '^' simp_exp
+ { $$ = node ($1, Node_exp, $3); }
+ | simp_exp '*' simp_exp
+ { $$ = node ($1, Node_times, $3); }
+ | simp_exp '/' simp_exp
+ { $$ = node ($1, Node_quotient, $3); }
+ | simp_exp '%' simp_exp
+ { $$ = node ($1, Node_mod, $3); }
+ | simp_exp '+' simp_exp
+ { $$ = node ($1, Node_plus, $3); }
+ | simp_exp '-' simp_exp
+ { $$ = node ($1, Node_minus, $3); }
+ | variable INCREMENT
+ { $$ = node ($1, Node_postincrement, (NODE *)NULL); }
+ | variable DECREMENT
+ { $$ = node ($1, Node_postdecrement, (NODE *)NULL); }
+ ;
+
+non_post_simp_exp
+ : '!' simp_exp %prec UNARY
+ { $$ = node ($2, Node_not,(NODE *) NULL); }
+ | '(' exp r_paren
+ { $$ = $2; }
+ | LEX_BUILTIN
+ '(' opt_expression_list r_paren
+ { $$ = snode ($3, Node_builtin, (int) $1); }
+ | LEX_LENGTH '(' opt_expression_list r_paren
+ { $$ = snode ($3, Node_builtin, (int) $1); }
+ | LEX_LENGTH
+ {
+ if (do_lint)
+ warning("call of `length' without parentheses is not portable");
+ $$ = snode ((NODE *)NULL, Node_builtin, (int) $1);
+ if (do_posix)
+ warning( "call of `length' without parentheses is deprecated by POSIX");
+ }
+ | FUNC_CALL '(' opt_expression_list r_paren
+ {
+ $$ = node ($3, Node_func_call, make_string($1, strlen($1)));
+ }
+ | variable
+ | INCREMENT variable
+ { $$ = node ($2, Node_preincrement, (NODE *)NULL); }
+ | DECREMENT variable
+ { $$ = node ($2, Node_predecrement, (NODE *)NULL); }
+ | YNUMBER
+ { $$ = $1; }
+ | YSTRING
+ { $$ = $1; }
+
+ | '-' simp_exp %prec UNARY
+ { if ($2->type == Node_val) {
+ $2->numbr = -(force_number($2));
+ $$ = $2;
+ } else
+ $$ = node ($2, Node_unary_minus, (NODE *)NULL);
+ }
+ | '+' simp_exp %prec UNARY
+ {
+ /* was: $$ = $2 */
+ /* POSIX semantics: force a conversion to numeric type */
+ $$ = node (make_number(0.0), Node_plus, $2);
+ }
+ ;
+
+opt_variable
+ : /* empty */
+ { $$ = NULL; }
+ | variable
+ { $$ = $1; }
+ ;
+
+variable
+ : NAME
+ { $$ = variable($1,1); }
+ | NAME '[' expression_list ']'
+ {
+ if ($3->rnode == NULL) {
+ $$ = node (variable($1,1), Node_subscript, $3->lnode);
+ freenode($3);
+ } else
+ $$ = node (variable($1,1), Node_subscript, $3);
+ }
+ | '$' non_post_simp_exp
+ { $$ = node ($2, Node_field_spec, (NODE *)NULL); }
+ ;
+
+l_brace
+ : '{' opt_nls
+ ;
+
+r_brace
+ : '}' opt_nls { yyerrok; }
+ ;
+
+r_paren
+ : ')' { yyerrok; }
+ ;
+
+opt_semi
+ : /* empty */
+ | semi
+ ;
+
+semi
+ : ';' { yyerrok; want_assign = 0; }
+ ;
+
+comma : ',' opt_nls { yyerrok; }
+ ;
+
+%%
+
+struct token {
+ const char *operator; /* text to match */
+ NODETYPE value; /* node type */
+ int class; /* lexical class */
+ unsigned flags; /* # of args. allowed and compatability */
+# define ARGS 0xFF /* 0, 1, 2, 3 args allowed (any combination */
+# define A(n) (1<<(n))
+# define VERSION 0xFF00 /* old awk is zero */
+# define NOT_OLD 0x0100 /* feature not in old awk */
+# define NOT_POSIX 0x0200 /* feature not in POSIX */
+# define GAWKX 0x0400 /* gawk extension */
+ NODE *(*ptr) (); /* function that implements this keyword */
+};
+
+extern NODE
+ *do_exp(), *do_getline(), *do_index(), *do_length(),
+ *do_sqrt(), *do_log(), *do_sprintf(), *do_substr(),
+ *do_split(), *do_system(), *do_int(), *do_close(),
+ *do_atan2(), *do_sin(), *do_cos(), *do_rand(),
+ *do_srand(), *do_match(), *do_tolower(), *do_toupper(),
+ *do_sub(), *do_gsub(), *do_strftime(), *do_systime();
+
+/* Tokentab is sorted ascii ascending order, so it can be binary searched. */
+
+static struct token tokentab[] = {
+{"BEGIN", Node_illegal, LEX_BEGIN, 0, 0},
+{"END", Node_illegal, LEX_END, 0, 0},
+{"atan2", Node_builtin, LEX_BUILTIN, NOT_OLD|A(2), do_atan2},
+{"break", Node_K_break, LEX_BREAK, 0, 0},
+{"close", Node_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_close},
+{"continue", Node_K_continue, LEX_CONTINUE, 0, 0},
+{"cos", Node_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_cos},
+{"delete", Node_K_delete, LEX_DELETE, NOT_OLD, 0},
+{"do", Node_K_do, LEX_DO, NOT_OLD, 0},
+{"else", Node_illegal, LEX_ELSE, 0, 0},
+{"exit", Node_K_exit, LEX_EXIT, 0, 0},
+{"exp", Node_builtin, LEX_BUILTIN, A(1), do_exp},
+{"for", Node_K_for, LEX_FOR, 0, 0},
+{"func", Node_K_function, LEX_FUNCTION, NOT_POSIX|NOT_OLD, 0},
+{"function", Node_K_function, LEX_FUNCTION, NOT_OLD, 0},
+{"getline", Node_K_getline, LEX_GETLINE, NOT_OLD, 0},
+{"gsub", Node_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), do_gsub},
+{"if", Node_K_if, LEX_IF, 0, 0},
+{"in", Node_illegal, LEX_IN, 0, 0},
+{"index", Node_builtin, LEX_BUILTIN, A(2), do_index},
+{"int", Node_builtin, LEX_BUILTIN, A(1), do_int},
+{"length", Node_builtin, LEX_LENGTH, A(0)|A(1), do_length},
+{"log", Node_builtin, LEX_BUILTIN, A(1), do_log},
+{"match", Node_builtin, LEX_BUILTIN, NOT_OLD|A(2), do_match},
+{"next", Node_K_next, LEX_NEXT, 0, 0},
+{"print", Node_K_print, LEX_PRINT, 0, 0},
+{"printf", Node_K_printf, LEX_PRINTF, 0, 0},
+{"rand", Node_builtin, LEX_BUILTIN, NOT_OLD|A(0), do_rand},
+{"return", Node_K_return, LEX_RETURN, NOT_OLD, 0},
+{"sin", Node_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_sin},
+{"split", Node_builtin, LEX_BUILTIN, A(2)|A(3), do_split},
+{"sprintf", Node_builtin, LEX_BUILTIN, 0, do_sprintf},
+{"sqrt", Node_builtin, LEX_BUILTIN, A(1), do_sqrt},
+{"srand", Node_builtin, LEX_BUILTIN, NOT_OLD|A(0)|A(1), do_srand},
+{"strftime", Node_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2), do_strftime},
+{"sub", Node_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), do_sub},
+{"substr", Node_builtin, LEX_BUILTIN, A(2)|A(3), do_substr},
+{"system", Node_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_system},
+{"systime", Node_builtin, LEX_BUILTIN, GAWKX|A(0), do_systime},
+{"tolower", Node_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_tolower},
+{"toupper", Node_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_toupper},
+{"while", Node_K_while, LEX_WHILE, 0, 0},
+};
+
+/* VARARGS0 */
+static void
+yyerror(va_alist)
+va_dcl
+{
+ va_list args;
+ const char *mesg = NULL;
+ register char *bp, *cp;
+ char *scan;
+ char buf[120];
+ static char end_of_file_line[] = "(END OF FILE)";
+
+ errcount++;
+ /* Find the current line in the input file */
+ if (lexptr && lexeme) {
+ if (!thisline) {
+ cp = lexeme;
+ if (*cp == '\n') {
+ cp--;
+ mesg = "unexpected newline";
+ }
+ for ( ; cp != lexptr_begin && *cp != '\n'; --cp)
+ continue;
+ if (*cp == '\n')
+ cp++;
+ thisline = cp;
+ }
+ /* NL isn't guaranteed */
+ bp = lexeme;
+ while (bp < lexend && *bp && *bp != '\n')
+ bp++;
+ } else {
+ thisline = end_of_file_line;
+ bp = thisline + strlen(thisline);
+ }
+ msg("%.*s", (int) (bp - thisline), thisline);
+ bp = buf;
+ cp = buf + sizeof(buf) - 24; /* 24 more than longest msg. input */
+ if (lexptr) {
+ scan = thisline;
+ while (bp < cp && scan < lexeme)
+ if (*scan++ == '\t')
+ *bp++ = '\t';
+ else
+ *bp++ = ' ';
+ *bp++ = '^';
+ *bp++ = ' ';
+ }
+ va_start(args);
+ if (mesg == NULL)
+ mesg = va_arg(args, char *);
+ strcpy(bp, mesg);
+ err("", buf, args);
+ va_end(args);
+ exit(2);
+}
+
+static char *
+get_src_buf()
+{
+ static int samefile = 0;
+ static int nextfile = 0;
+ static char *buf = NULL;
+ static int fd;
+ int n;
+ register char *scan;
+ static int len = 0;
+ static int did_newline = 0;
+# define SLOP 128 /* enough space to hold most source lines */
+
+again:
+ if (nextfile > numfiles)
+ return NULL;
+
+ if (srcfiles[nextfile].stype == CMDLINE) {
+ if (len == 0) {
+ len = strlen(srcfiles[nextfile].val);
+ if (len == 0) {
+ /*
+ * Yet Another Special case:
+ * gawk '' /path/name
+ * Sigh.
+ */
+ ++nextfile;
+ goto again;
+ }
+ sourceline = 1;
+ lexptr = lexptr_begin = srcfiles[nextfile].val;
+ lexend = lexptr + len;
+ } else if (!did_newline && *(lexptr-1) != '\n') {
+ /*
+ * The following goop is to ensure that the source
+ * ends with a newline and that the entire current
+ * line is available for error messages.
+ */
+ int offset;
+
+ did_newline = 1;
+ offset = lexptr - lexeme;
+ for (scan = lexeme; scan > lexptr_begin; scan--)
+ if (*scan == '\n') {
+ scan++;
+ break;
+ }
+ len = lexptr - scan;
+ emalloc(buf, char *, len+1, "get_src_buf");
+ memcpy(buf, scan, len);
+ thisline = buf;
+ lexptr = buf + len;
+ *lexptr = '\n';
+ lexeme = lexptr - offset;
+ lexptr_begin = buf;
+ lexend = lexptr + 1;
+ } else {
+ len = 0;
+ lexeme = lexptr = lexptr_begin = NULL;
+ }
+ if (lexptr == NULL && ++nextfile <= numfiles)
+ return get_src_buf();
+ return lexptr;
+ }
+ if (!samefile) {
+ source = srcfiles[nextfile].val;
+ if (source == NULL) {
+ if (buf) {
+ free(buf);
+ buf = NULL;
+ }
+ len = 0;
+ return lexeme = lexptr = lexptr_begin = NULL;
+ }
+ fd = pathopen(source);
+ if (fd == -1)
+ fatal("can't open source file \"%s\" for reading (%s)",
+ source, strerror(errno));
+ len = optimal_bufsize(fd);
+ if (buf)
+ free(buf);
+ emalloc(buf, char *, len + SLOP, "get_src_buf");
+ lexptr_begin = buf + SLOP;
+ samefile = 1;
+ sourceline = 1;
+ } else {
+ /*
+ * Here, we retain the current source line (up to length SLOP)
+ * in the beginning of the buffer that was overallocated above
+ */
+ int offset;
+ int linelen;
+
+ offset = lexptr - lexeme;
+ for (scan = lexeme; scan > lexptr_begin; scan--)
+ if (*scan == '\n') {
+ scan++;
+ break;
+ }
+ linelen = lexptr - scan;
+ if (linelen > SLOP)
+ linelen = SLOP;
+ thisline = buf + SLOP - linelen;
+ memcpy(thisline, scan, linelen);
+ lexeme = buf + SLOP - offset;
+ lexptr_begin = thisline;
+ }
+ n = read(fd, buf + SLOP, len);
+ if (n == -1)
+ fatal("can't read sourcefile \"%s\" (%s)",
+ source, strerror(errno));
+ if (n == 0) {
+ samefile = 0;
+ nextfile++;
+ *lexeme = '\0';
+ len = 0;
+ return get_src_buf();
+ }
+ lexptr = buf + SLOP;
+ lexend = lexptr + n;
+ return buf;
+}
+
+#define tokadd(x) (*tok++ = (x), tok == tokend ? tokexpand() : tok)
+
+char *
+tokexpand()
+{
+ static int toksize = 60;
+ int tokoffset;
+
+ tokoffset = tok - tokstart;
+ toksize *= 2;
+ if (tokstart)
+ erealloc(tokstart, char *, toksize, "tokexpand");
+ else
+ emalloc(tokstart, char *, toksize, "tokexpand");
+ tokend = tokstart + toksize;
+ tok = tokstart + tokoffset;
+ return tok;
+}
+
+#if DEBUG
+char
+nextc() {
+ if (lexptr && lexptr < lexend)
+ return *lexptr++;
+ else if (get_src_buf())
+ return *lexptr++;
+ else
+ return '\0';
+}
+#else
+#define nextc() ((lexptr && lexptr < lexend) ? \
+ *lexptr++ : \
+ (get_src_buf() ? *lexptr++ : '\0') \
+ )
+#endif
+#define pushback() (lexptr && lexptr > lexptr_begin ? lexptr-- : lexptr)
+
+/*
+ * Read the input and turn it into tokens.
+ */
+
+static int
+yylex()
+{
+ register int c;
+ int seen_e = 0; /* These are for numbers */
+ int seen_point = 0;
+ int esc_seen; /* for literal strings */
+ int low, mid, high;
+ static int did_newline = 0;
+ char *tokkey;
+
+ if (!nextc())
+ return 0;
+ pushback();
+#ifdef OS2
+ /*
+ * added for OS/2's extproc feature of cmd.exe
+ * (like #! in BSD sh)
+ */
+ if (strncasecmp(lexptr, "extproc ", 8) == 0) {
+ while (*lexptr && *lexptr != '\n')
+ lexptr++;
+ }
+#endif
+ lexeme = lexptr;
+ thisline = NULL;
+ if (want_regexp) {
+ int in_brack = 0;
+
+ want_regexp = 0;
+ tok = tokstart;
+ while ((c = nextc()) != 0) {
+ switch (c) {
+ case '[':
+ in_brack = 1;
+ break;
+ case ']':
+ in_brack = 0;
+ break;
+ case '\\':
+ if ((c = nextc()) == '\0') {
+ yyerror("unterminated regexp ends with \\ at end of file");
+ } else if (c == '\n') {
+ sourceline++;
+ continue;
+ } else
+ tokadd('\\');
+ break;
+ case '/': /* end of the regexp */
+ if (in_brack)
+ break;
+
+ pushback();
+ tokadd('\0');
+ yylval.sval = tokstart;
+ return REGEXP;
+ case '\n':
+ pushback();
+ yyerror("unterminated regexp");
+ case '\0':
+ yyerror("unterminated regexp at end of file");
+ }
+ tokadd(c);
+ }
+ }
+retry:
+ while ((c = nextc()) == ' ' || c == '\t')
+ continue;
+
+ lexeme = lexptr ? lexptr - 1 : lexptr;
+ thisline = NULL;
+ tok = tokstart;
+ yylval.nodetypeval = Node_illegal;
+
+ switch (c) {
+ case 0:
+ return 0;
+
+ case '\n':
+ sourceline++;
+ return NEWLINE;
+
+ case '#': /* it's a comment */
+ while ((c = nextc()) != '\n') {
+ if (c == '\0')
+ return 0;
+ }
+ sourceline++;
+ return NEWLINE;
+
+ case '\\':
+#ifdef RELAXED_CONTINUATION
+ /*
+ * This code puports to allow comments and/or whitespace
+ * after the `\' at the end of a line used for continuation.
+ * Use it at your own risk. We think it's a bad idea, which
+ * is why it's not on by default.
+ */
+ if (!do_unix) {
+ /* strip trailing white-space and/or comment */
+ while ((c = nextc()) == ' ' || c == '\t')
+ continue;
+ if (c == '#')
+ while ((c = nextc()) != '\n')
+ if (c == '\0')
+ break;
+ pushback();
+ }
+#endif /* RELAXED_CONTINUATION */
+ if (nextc() == '\n') {
+ sourceline++;
+ goto retry;
+ } else
+ yyerror("backslash not last character on line");
+ break;
+
+ case '$':
+ want_assign = 1;
+ return '$';
+
+ case ')':
+ case ']':
+ case '(':
+ case '[':
+ case ';':
+ case ':':
+ case '?':
+ case '{':
+ case ',':
+ return c;
+
+ case '*':
+ if ((c = nextc()) == '=') {
+ yylval.nodetypeval = Node_assign_times;
+ return ASSIGNOP;
+ } else if (do_posix) {
+ pushback();
+ return '*';
+ } else if (c == '*') {
+ /* make ** and **= aliases for ^ and ^= */
+ static int did_warn_op = 0, did_warn_assgn = 0;
+
+ if (nextc() == '=') {
+ if (do_lint && ! did_warn_assgn) {
+ did_warn_assgn = 1;
+ warning("**= is not allowed by POSIX");
+ }
+ yylval.nodetypeval = Node_assign_exp;
+ return ASSIGNOP;
+ } else {
+ pushback();
+ if (do_lint && ! did_warn_op) {
+ did_warn_op = 1;
+ warning("** is not allowed by POSIX");
+ }
+ return '^';
+ }
+ }
+ pushback();
+ return '*';
+
+ case '/':
+ if (want_assign) {
+ if (nextc() == '=') {
+ yylval.nodetypeval = Node_assign_quotient;
+ return ASSIGNOP;
+ }
+ pushback();
+ }
+ return '/';
+
+ case '%':
+ if (nextc() == '=') {
+ yylval.nodetypeval = Node_assign_mod;
+ return ASSIGNOP;
+ }
+ pushback();
+ return '%';
+
+ case '^':
+ {
+ static int did_warn_op = 0, did_warn_assgn = 0;
+
+ if (nextc() == '=') {
+
+ if (do_lint && ! did_warn_assgn) {
+ did_warn_assgn = 1;
+ warning("operator `^=' is not supported in old awk");
+ }
+ yylval.nodetypeval = Node_assign_exp;
+ return ASSIGNOP;
+ }
+ pushback();
+ if (do_lint && ! did_warn_op) {
+ did_warn_op = 1;
+ warning("operator `^' is not supported in old awk");
+ }
+ return '^';
+ }
+
+ case '+':
+ if ((c = nextc()) == '=') {
+ yylval.nodetypeval = Node_assign_plus;
+ return ASSIGNOP;
+ }
+ if (c == '+')
+ return INCREMENT;
+ pushback();
+ return '+';
+
+ case '!':
+ if ((c = nextc()) == '=') {
+ yylval.nodetypeval = Node_notequal;
+ return RELOP;
+ }
+ if (c == '~') {
+ yylval.nodetypeval = Node_nomatch;
+ want_assign = 0;
+ return MATCHOP;
+ }
+ pushback();
+ return '!';
+
+ case '<':
+ if (nextc() == '=') {
+ yylval.nodetypeval = Node_leq;
+ return RELOP;
+ }
+ yylval.nodetypeval = Node_less;
+ pushback();
+ return '<';
+
+ case '=':
+ if (nextc() == '=') {
+ yylval.nodetypeval = Node_equal;
+ return RELOP;
+ }
+ yylval.nodetypeval = Node_assign;
+ pushback();
+ return ASSIGNOP;
+
+ case '>':
+ if ((c = nextc()) == '=') {
+ yylval.nodetypeval = Node_geq;
+ return RELOP;
+ } else if (c == '>') {
+ yylval.nodetypeval = Node_redirect_append;
+ return APPEND_OP;
+ }
+ yylval.nodetypeval = Node_greater;
+ pushback();
+ return '>';
+
+ case '~':
+ yylval.nodetypeval = Node_match;
+ want_assign = 0;
+ return MATCHOP;
+
+ case '}':
+ /*
+ * Added did newline stuff. Easier than
+ * hacking the grammar
+ */
+ if (did_newline) {
+ did_newline = 0;
+ return c;
+ }
+ did_newline++;
+ --lexptr; /* pick up } next time */
+ return NEWLINE;
+
+ case '"':
+ esc_seen = 0;
+ while ((c = nextc()) != '"') {
+ if (c == '\n') {
+ pushback();
+ yyerror("unterminated string");
+ }
+ if (c == '\\') {
+ c = nextc();
+ if (c == '\n') {
+ sourceline++;
+ continue;
+ }
+ esc_seen = 1;
+ tokadd('\\');
+ }
+ if (c == '\0') {
+ pushback();
+ yyerror("unterminated string");
+ }
+ tokadd(c);
+ }
+ yylval.nodeval = make_str_node(tokstart,
+ tok - tokstart, esc_seen ? SCAN : 0);
+ yylval.nodeval->flags |= PERM;
+ return YSTRING;
+
+ case '-':
+ if ((c = nextc()) == '=') {
+ yylval.nodetypeval = Node_assign_minus;
+ return ASSIGNOP;
+ }
+ if (c == '-')
+ return DECREMENT;
+ pushback();
+ return '-';
+
+ case '.':
+ c = nextc();
+ pushback();
+ if (!isdigit(c))
+ return '.';
+ else
+ c = '.'; /* FALL THROUGH */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ /* It's a number */
+ for (;;) {
+ int gotnumber = 0;
+
+ tokadd(c);
+ switch (c) {
+ case '.':
+ if (seen_point) {
+ gotnumber++;
+ break;
+ }
+ ++seen_point;
+ break;
+ case 'e':
+ case 'E':
+ if (seen_e) {
+ gotnumber++;
+ break;
+ }
+ ++seen_e;
+ if ((c = nextc()) == '-' || c == '+')
+ tokadd(c);
+ else
+ pushback();
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ break;
+ default:
+ gotnumber++;
+ }
+ if (gotnumber)
+ break;
+ c = nextc();
+ }
+ pushback();
+ yylval.nodeval = make_number(atof(tokstart));
+ yylval.nodeval->flags |= PERM;
+ return YNUMBER;
+
+ case '&':
+ if ((c = nextc()) == '&') {
+ yylval.nodetypeval = Node_and;
+ for (;;) {
+ c = nextc();
+ if (c == '\0')
+ break;
+ if (c == '#') {
+ while ((c = nextc()) != '\n' && c != '\0')
+ continue;
+ if (c == '\0')
+ break;
+ }
+ if (c == '\n')
+ sourceline++;
+ if (! isspace(c)) {
+ pushback();
+ break;
+ }
+ }
+ want_assign = 0;
+ return LEX_AND;
+ }
+ pushback();
+ return '&';
+
+ case '|':
+ if ((c = nextc()) == '|') {
+ yylval.nodetypeval = Node_or;
+ for (;;) {
+ c = nextc();
+ if (c == '\0')
+ break;
+ if (c == '#') {
+ while ((c = nextc()) != '\n' && c != '\0')
+ continue;
+ if (c == '\0')
+ break;
+ }
+ if (c == '\n')
+ sourceline++;
+ if (! isspace(c)) {
+ pushback();
+ break;
+ }
+ }
+ want_assign = 0;
+ return LEX_OR;
+ }
+ pushback();
+ return '|';
+ }
+
+ if (c != '_' && ! isalpha(c))
+ yyerror("Invalid char '%c' in expression\n", c);
+
+ /* it's some type of name-type-thing. Find its length */
+ tok = tokstart;
+ while (is_identchar(c)) {
+ tokadd(c);
+ c = nextc();
+ }
+ tokadd('\0');
+ emalloc(tokkey, char *, tok - tokstart, "yylex");
+ memcpy(tokkey, tokstart, tok - tokstart);
+ pushback();
+
+ /* See if it is a special token. */
+ low = 0;
+ high = (sizeof (tokentab) / sizeof (tokentab[0])) - 1;
+ while (low <= high) {
+ int i/* , c */;
+
+ mid = (low + high) / 2;
+ c = *tokstart - tokentab[mid].operator[0];
+ i = c ? c : strcmp (tokstart, tokentab[mid].operator);
+
+ if (i < 0) { /* token < mid */
+ high = mid - 1;
+ } else if (i > 0) { /* token > mid */
+ low = mid + 1;
+ } else {
+ if (do_lint) {
+ if (tokentab[mid].flags & GAWKX)
+ warning("%s() is a gawk extension",
+ tokentab[mid].operator);
+ if (tokentab[mid].flags & NOT_POSIX)
+ warning("POSIX does not allow %s",
+ tokentab[mid].operator);
+ if (tokentab[mid].flags & NOT_OLD)
+ warning("%s is not supported in old awk",
+ tokentab[mid].operator);
+ }
+ if ((do_unix && (tokentab[mid].flags & GAWKX))
+ || (do_posix && (tokentab[mid].flags & NOT_POSIX)))
+ break;
+ if (tokentab[mid].class == LEX_BUILTIN
+ || tokentab[mid].class == LEX_LENGTH
+ )
+ yylval.lval = mid;
+ else
+ yylval.nodetypeval = tokentab[mid].value;
+
+ free(tokkey);
+ return tokentab[mid].class;
+ }
+ }
+
+ yylval.sval = tokkey;
+ if (*lexptr == '(')
+ return FUNC_CALL;
+ else {
+ want_assign = 1;
+ return NAME;
+ }
+}
+
+static NODE *
+node_common(op)
+NODETYPE op;
+{
+ register NODE *r;
+
+ getnode(r);
+ r->type = op;
+ r->flags = MALLOC;
+ /* if lookahead is NL, lineno is 1 too high */
+ if (lexeme && *lexeme == '\n')
+ r->source_line = sourceline - 1;
+ else
+ r->source_line = sourceline;
+ r->source_file = source;
+ return r;
+}
+
+/*
+ * This allocates a node with defined lnode and rnode.
+ */
+NODE *
+node(left, op, right)
+NODE *left, *right;
+NODETYPE op;
+{
+ register NODE *r;
+
+ r = node_common(op);
+ r->lnode = left;
+ r->rnode = right;
+ return r;
+}
+
+/*
+ * This allocates a node with defined subnode and proc for builtin functions
+ * Checks for arg. count and supplies defaults where possible.
+ */
+static NODE *
+snode(subn, op, idx)
+NODETYPE op;
+int idx;
+NODE *subn;
+{
+ register NODE *r;
+ register NODE *n;
+ int nexp = 0;
+ int args_allowed;
+
+ r = node_common(op);
+
+ /* traverse expression list to see how many args. given */
+ for (n= subn; n; n= n->rnode) {
+ nexp++;
+ if (nexp > 3)
+ break;
+ }
+
+ /* check against how many args. are allowed for this builtin */
+ args_allowed = tokentab[idx].flags & ARGS;
+ if (args_allowed && !(args_allowed & A(nexp)))
+ fatal("%s() cannot have %d argument%c",
+ tokentab[idx].operator, nexp, nexp == 1 ? ' ' : 's');
+
+ r->proc = tokentab[idx].ptr;
+
+ /* special case processing for a few builtins */
+ if (nexp == 0 && r->proc == do_length) {
+ subn = node(node(make_number(0.0),Node_field_spec,(NODE *)NULL),
+ Node_expression_list,
+ (NODE *) NULL);
+ } else if (r->proc == do_match) {
+ if (subn->rnode->lnode->type != Node_regex)
+ subn->rnode->lnode = mk_rexp(subn->rnode->lnode);
+ } else if (r->proc == do_sub || r->proc == do_gsub) {
+ if (subn->lnode->type != Node_regex)
+ subn->lnode = mk_rexp(subn->lnode);
+ if (nexp == 2)
+ append_right(subn, node(node(make_number(0.0),
+ Node_field_spec,
+ (NODE *) NULL),
+ Node_expression_list,
+ (NODE *) NULL));
+ else if (do_lint && subn->rnode->rnode->lnode->type == Node_val)
+ warning("string literal as last arg of substitute");
+ } else if (r->proc == do_split) {
+ if (nexp == 2)
+ append_right(subn,
+ node(FS_node, Node_expression_list, (NODE *) NULL));
+ n = subn->rnode->rnode->lnode;
+ if (n->type != Node_regex)
+ subn->rnode->rnode->lnode = mk_rexp(n);
+ if (nexp == 2)
+ subn->rnode->rnode->lnode->re_flags |= FS_DFLT;
+ }
+
+ r->subnode = subn;
+ return r;
+}
+
+/*
+ * This allocates a Node_line_range node with defined condpair and
+ * zeroes the trigger word to avoid the temptation of assuming that calling
+ * 'node( foo, Node_line_range, 0)' will properly initialize 'triggered'.
+ */
+/* Otherwise like node() */
+static NODE *
+mkrangenode(cpair)
+NODE *cpair;
+{
+ register NODE *r;
+
+ getnode(r);
+ r->type = Node_line_range;
+ r->condpair = cpair;
+ r->triggered = 0;
+ return r;
+}
+
+/* Build a for loop */
+static NODE *
+make_for_loop(init, cond, incr)
+NODE *init, *cond, *incr;
+{
+ register FOR_LOOP_HEADER *r;
+ NODE *n;
+
+ emalloc(r, FOR_LOOP_HEADER *, sizeof(FOR_LOOP_HEADER), "make_for_loop");
+ getnode(n);
+ n->type = Node_illegal;
+ r->init = init;
+ r->cond = cond;
+ r->incr = incr;
+ n->sub.nodep.r.hd = r;
+ return n;
+}
+
+/*
+ * Install a name in the symbol table, even if it is already there.
+ * Caller must check against redefinition if that is desired.
+ */
+NODE *
+install(name, value)
+char *name;
+NODE *value;
+{
+ register NODE *hp;
+ register size_t len;
+ register int bucket;
+
+ len = strlen(name);
+ bucket = hash(name, len, (unsigned long) HASHSIZE);
+ getnode(hp);
+ hp->type = Node_hashnode;
+ hp->hnext = variables[bucket];
+ variables[bucket] = hp;
+ hp->hlength = len;
+ hp->hvalue = value;
+ hp->hname = name;
+ hp->hvalue->vname = name;
+ return hp->hvalue;
+}
+
+/* find the most recent hash node for name installed by install */
+NODE *
+lookup(name)
+const char *name;
+{
+ register NODE *bucket;
+ register size_t len;
+
+ len = strlen(name);
+ bucket = variables[hash(name, len, (unsigned long) HASHSIZE)];
+ while (bucket) {
+ if (bucket->hlength == len && STREQN(bucket->hname, name, len))
+ return bucket->hvalue;
+ bucket = bucket->hnext;
+ }
+ return NULL;
+}
+
+/*
+ * Add new to the rightmost branch of LIST. This uses n^2 time, so we make
+ * a simple attempt at optimizing it.
+ */
+static NODE *
+append_right(list, new)
+NODE *list, *new;
+{
+ register NODE *oldlist;
+ static NODE *savefront = NULL, *savetail = NULL;
+
+ oldlist = list;
+ if (savefront == oldlist) {
+ savetail = savetail->rnode = new;
+ return oldlist;
+ } else
+ savefront = oldlist;
+ while (list->rnode != NULL)
+ list = list->rnode;
+ savetail = list->rnode = new;
+ return oldlist;
+}
+
+/*
+ * check if name is already installed; if so, it had better have Null value,
+ * in which case def is added as the value. Otherwise, install name with def
+ * as value.
+ */
+static void
+func_install(params, def)
+NODE *params;
+NODE *def;
+{
+ NODE *r;
+
+ pop_params(params->rnode);
+ pop_var(params, 0);
+ r = lookup(params->param);
+ if (r != NULL) {
+ fatal("function name `%s' previously defined", params->param);
+ } else
+ (void) install(params->param, node(params, Node_func, def));
+}
+
+static void
+pop_var(np, freeit)
+NODE *np;
+int freeit;
+{
+ register NODE *bucket, **save;
+ register size_t len;
+ char *name;
+
+ name = np->param;
+ len = strlen(name);
+ save = &(variables[hash(name, len, (unsigned long) HASHSIZE)]);
+ for (bucket = *save; bucket; bucket = bucket->hnext) {
+ if (len == bucket->hlength && STREQN(bucket->hname, name, len)) {
+ *save = bucket->hnext;
+ freenode(bucket);
+ if (freeit)
+ free(np->param);
+ return;
+ }
+ save = &(bucket->hnext);
+ }
+}
+
+static void
+pop_params(params)
+NODE *params;
+{
+ register NODE *np;
+
+ for (np = params; np != NULL; np = np->rnode)
+ pop_var(np, 1);
+}
+
+static NODE *
+make_param(name)
+char *name;
+{
+ NODE *r;
+
+ getnode(r);
+ r->type = Node_param_list;
+ r->rnode = NULL;
+ r->param = name;
+ r->param_cnt = param_counter++;
+ return (install(name, r));
+}
+
+/* Name points to a variable name. Make sure its in the symbol table */
+NODE *
+variable(name, can_free)
+char *name;
+int can_free;
+{
+ register NODE *r;
+ static int env_loaded = 0;
+
+ if (!env_loaded && STREQ(name, "ENVIRON")) {
+ load_environ();
+ env_loaded = 1;
+ }
+ if ((r = lookup(name)) == NULL)
+ r = install(name, node(Nnull_string, Node_var, (NODE *) NULL));
+ else if (can_free)
+ free(name);
+ return r;
+}
+
+static NODE *
+mk_rexp(exp)
+NODE *exp;
+{
+ if (exp->type == Node_regex)
+ return exp;
+ else {
+ NODE *n;
+
+ getnode(n);
+ n->type = Node_regex;
+ n->re_exp = exp;
+ n->re_text = NULL;
+ n->re_reg = NULL;
+ n->re_flags = 0;
+ n->re_cnt = 1;
+ return n;
+ }
+}
diff --git a/gnu/usr.bin/awk/builtin.c b/gnu/usr.bin/awk/builtin.c
new file mode 100644
index 0000000..96411de
--- /dev/null
+++ b/gnu/usr.bin/awk/builtin.c
@@ -0,0 +1,1239 @@
+/*
+ * builtin.c - Builtin functions and various utility procedures
+ */
+
+/*
+ * Copyright (C) 1986, 1988, 1989, 1991, 1992, 1993 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Progamming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GAWK; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include "awk.h"
+
+#ifndef SRANDOM_PROTO
+extern void srandom P((unsigned int seed));
+#endif
+#ifndef linux
+extern char *initstate P((unsigned seed, char *state, int n));
+extern char *setstate P((char *state));
+extern long random P((void));
+#endif
+
+extern NODE **fields_arr;
+extern int output_is_tty;
+
+static NODE *sub_common P((NODE *tree, int global));
+NODE *format_tree P((const char *, int, NODE *));
+
+#ifdef _CRAY
+/* Work around a problem in conversion of doubles to exact integers. */
+#include <float.h>
+#define Floor(n) floor((n) * (1.0 + DBL_EPSILON))
+#define Ceil(n) ceil((n) * (1.0 + DBL_EPSILON))
+
+/* Force the standard C compiler to use the library math functions. */
+extern double exp(double);
+double (*Exp)() = exp;
+#define exp(x) (*Exp)(x)
+extern double log(double);
+double (*Log)() = log;
+#define log(x) (*Log)(x)
+#else
+#define Floor(n) floor(n)
+#define Ceil(n) ceil(n)
+#endif
+
+#define DEFAULT_G_PRECISION 6
+
+#ifdef GFMT_WORKAROUND
+/* semi-temporary hack, mostly to gracefully handle VMS */
+static void sgfmt P((char *buf, const char *format, int alt,
+ int fwidth, int precision, double value));
+#endif /* GFMT_WORKAROUND */
+
+/*
+ * On the alpha, LONG_MAX is too big for doing rand().
+ * On the Cray (Y-MP, anyway), ints and longs are 64 bits, but
+ * random() does things in terms of 32 bits. So we have to chop
+ * LONG_MAX down.
+ */
+#if (defined(__alpha) && defined(__osf__)) || defined(_CRAY)
+#define GAWK_RANDOM_MAX (LONG_MAX & 0x7fffffff)
+#else
+#define GAWK_RANDOM_MAX LONG_MAX
+#endif
+
+static void efwrite P((const void *ptr, size_t size, size_t count, FILE *fp,
+ const char *from, struct redirect *rp,int flush));
+
+static void
+efwrite(ptr, size, count, fp, from, rp, flush)
+const void *ptr;
+size_t size, count;
+FILE *fp;
+const char *from;
+struct redirect *rp;
+int flush;
+{
+ errno = 0;
+ if (fwrite(ptr, size, count, fp) != count)
+ goto wrerror;
+ if (flush
+ && ((fp == stdout && output_is_tty)
+ || (rp && (rp->flag & RED_NOBUF)))) {
+ fflush(fp);
+ if (ferror(fp))
+ goto wrerror;
+ }
+ return;
+
+ wrerror:
+ fatal("%s to \"%s\" failed (%s)", from,
+ rp ? rp->value : "standard output",
+ errno ? strerror(errno) : "reason unknown");
+}
+
+/* Builtin functions */
+NODE *
+do_exp(tree)
+NODE *tree;
+{
+ NODE *tmp;
+ double d, res;
+#ifndef exp
+ double exp P((double));
+#endif
+
+ tmp= tree_eval(tree->lnode);
+ d = force_number(tmp);
+ free_temp(tmp);
+ errno = 0;
+ res = exp(d);
+ if (errno == ERANGE)
+ warning("exp argument %g is out of range", d);
+ return tmp_number((AWKNUM) res);
+}
+
+NODE *
+do_index(tree)
+NODE *tree;
+{
+ NODE *s1, *s2;
+ register char *p1, *p2;
+ register size_t l1, l2;
+ long ret;
+
+
+ s1 = tree_eval(tree->lnode);
+ s2 = tree_eval(tree->rnode->lnode);
+ force_string(s1);
+ force_string(s2);
+ p1 = s1->stptr;
+ p2 = s2->stptr;
+ l1 = s1->stlen;
+ l2 = s2->stlen;
+ ret = 0;
+ if (IGNORECASE) {
+ while (l1) {
+ if (l2 > l1)
+ break;
+ if (casetable[(int)*p1] == casetable[(int)*p2]
+ && (l2 == 1 || strncasecmp(p1, p2, l2) == 0)) {
+ ret = 1 + s1->stlen - l1;
+ break;
+ }
+ l1--;
+ p1++;
+ }
+ } else {
+ while (l1) {
+ if (l2 > l1)
+ break;
+ if (*p1 == *p2
+ && (l2 == 1 || STREQN(p1, p2, l2))) {
+ ret = 1 + s1->stlen - l1;
+ break;
+ }
+ l1--;
+ p1++;
+ }
+ }
+ free_temp(s1);
+ free_temp(s2);
+ return tmp_number((AWKNUM) ret);
+}
+
+double
+double_to_int(d)
+double d;
+{
+ double floor P((double));
+ double ceil P((double));
+
+ if (d >= 0)
+ d = Floor(d);
+ else
+ d = Ceil(d);
+ return d;
+}
+
+NODE *
+do_int(tree)
+NODE *tree;
+{
+ NODE *tmp;
+ double d;
+
+ tmp = tree_eval(tree->lnode);
+ d = force_number(tmp);
+ d = double_to_int(d);
+ free_temp(tmp);
+ return tmp_number((AWKNUM) d);
+}
+
+NODE *
+do_length(tree)
+NODE *tree;
+{
+ NODE *tmp;
+ size_t len;
+
+ tmp = tree_eval(tree->lnode);
+ len = force_string(tmp)->stlen;
+ free_temp(tmp);
+ return tmp_number((AWKNUM) len);
+}
+
+NODE *
+do_log(tree)
+NODE *tree;
+{
+ NODE *tmp;
+#ifndef log
+ double log P((double));
+#endif
+ double d, arg;
+
+ tmp = tree_eval(tree->lnode);
+ arg = (double) force_number(tmp);
+ if (arg < 0.0)
+ warning("log called with negative argument %g", arg);
+ d = log(arg);
+ free_temp(tmp);
+ return tmp_number((AWKNUM) d);
+}
+
+/*
+ * format_tree() formats nodes of a tree, starting with a left node,
+ * and accordingly to a fmt_string providing a format like in
+ * printf family from C library. Returns a string node which value
+ * is a formatted string. Called by sprintf function.
+ *
+ * It is one of the uglier parts of gawk. Thanks to Michal Jaegermann
+ * for taming this beast and making it compatible with ANSI C.
+ */
+
+NODE *
+format_tree(fmt_string, n0, carg)
+const char *fmt_string;
+int n0;
+register NODE *carg;
+{
+/* copy 'l' bytes from 's' to 'obufout' checking for space in the process */
+/* difference of pointers should be of ptrdiff_t type, but let us be kind */
+#define bchunk(s,l) if(l) {\
+ while((l)>ofre) {\
+ long olen = obufout - obuf;\
+ erealloc(obuf, char *, osiz*2, "format_tree");\
+ ofre+=osiz;\
+ osiz*=2;\
+ obufout = obuf + olen;\
+ }\
+ memcpy(obufout,s,(size_t)(l));\
+ obufout+=(l);\
+ ofre-=(l);\
+ }
+/* copy one byte from 's' to 'obufout' checking for space in the process */
+#define bchunk_one(s) {\
+ if(ofre <= 0) {\
+ long olen = obufout - obuf;\
+ erealloc(obuf, char *, osiz*2, "format_tree");\
+ ofre+=osiz;\
+ osiz*=2;\
+ obufout = obuf + olen;\
+ }\
+ *obufout++ = *s;\
+ --ofre;\
+ }
+
+ /* Is there space for something L big in the buffer? */
+#define chksize(l) if((l)>ofre) {\
+ long olen = obufout - obuf;\
+ erealloc(obuf, char *, osiz*2, "format_tree");\
+ obufout = obuf + olen;\
+ ofre+=osiz;\
+ osiz*=2;\
+ }
+
+ /*
+ * Get the next arg to be formatted. If we've run out of args,
+ * return "" (Null string)
+ */
+#define parse_next_arg() {\
+ if(!carg) { toofew = 1; break; }\
+ else {\
+ arg=tree_eval(carg->lnode);\
+ carg=carg->rnode;\
+ }\
+ }
+
+ NODE *r;
+ int toofew = 0;
+ char *obuf, *obufout;
+ size_t osiz, ofre;
+ char *chbuf;
+ const char *s0, *s1;
+ int cs1;
+ NODE *arg;
+ long fw, prec;
+ int lj, alt, big, have_prec;
+ long *cur;
+ long val;
+#ifdef sun386 /* Can't cast unsigned (int/long) from ptr->value */
+ long tmp_uval; /* on 386i 4.0.1 C compiler -- it just hangs */
+#endif
+ unsigned long uval;
+ int sgn;
+ int base = 0;
+ char cpbuf[30]; /* if we have numbers bigger than 30 */
+ char *cend = &cpbuf[30];/* chars, we lose, but seems unlikely */
+ char *cp;
+ char *fill;
+ double tmpval;
+ char signchar = 0;
+ size_t len;
+ static char sp[] = " ";
+ static char zero_string[] = "0";
+ static char lchbuf[] = "0123456789abcdef";
+ static char Uchbuf[] = "0123456789ABCDEF";
+
+ emalloc(obuf, char *, 120, "format_tree");
+ obufout = obuf;
+ osiz = 120;
+ ofre = osiz - 1;
+
+ s0 = s1 = fmt_string;
+ while (n0-- > 0) {
+ if (*s1 != '%') {
+ s1++;
+ continue;
+ }
+ bchunk(s0, s1 - s0);
+ s0 = s1;
+ cur = &fw;
+ fw = 0;
+ prec = 0;
+ have_prec = 0;
+ lj = alt = big = 0;
+ fill = sp;
+ cp = cend;
+ chbuf = lchbuf;
+ s1++;
+
+retry:
+ --n0;
+ switch (cs1 = *s1++) {
+ case (-1): /* dummy case to allow for checking */
+check_pos:
+ if (cur != &fw)
+ break; /* reject as a valid format */
+ goto retry;
+ case '%':
+ bchunk_one("%");
+ s0 = s1;
+ break;
+
+ case '0':
+ if (lj)
+ goto retry;
+ if (cur == &fw)
+ fill = zero_string; /* FALL through */
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (cur == 0)
+ /* goto lose; */
+ break;
+ if (prec >= 0)
+ *cur = cs1 - '0';
+ /* with a negative precision *cur is already set */
+ /* to -1, so it will remain negative, but we have */
+ /* to "eat" precision digits in any case */
+ while (n0 > 0 && *s1 >= '0' && *s1 <= '9') {
+ --n0;
+ *cur = *cur * 10 + *s1++ - '0';
+ }
+ if (prec < 0) /* negative precision is discarded */
+ have_prec = 0;
+ if (cur == &prec)
+ cur = 0;
+ goto retry;
+ case '*':
+ if (cur == 0)
+ /* goto lose; */
+ break;
+ parse_next_arg();
+ *cur = force_number(arg);
+ free_temp(arg);
+ if (cur == &prec)
+ cur = 0;
+ goto retry;
+ case ' ': /* print ' ' or '-' */
+ /* 'space' flag is ignored */
+ /* if '+' already present */
+ if (signchar != 0)
+ goto check_pos;
+ /* FALL THROUGH */
+ case '+': /* print '+' or '-' */
+ signchar = cs1;
+ goto check_pos;
+ case '-':
+ if (prec < 0)
+ break;
+ if (cur == &prec) {
+ prec = -1;
+ goto retry;
+ }
+ fill = sp; /* if left justified then other */
+ lj++; /* filling is ignored */
+ goto check_pos;
+ case '.':
+ if (cur != &fw)
+ break;
+ cur = &prec;
+ have_prec++;
+ goto retry;
+ case '#':
+ alt++;
+ goto check_pos;
+ case 'l':
+ if (big)
+ break;
+ big++;
+ goto check_pos;
+ case 'c':
+ parse_next_arg();
+ if (arg->flags & NUMBER) {
+#ifdef sun386
+ tmp_uval = arg->numbr;
+ uval= (unsigned long) tmp_uval;
+#else
+ uval = (unsigned long) arg->numbr;
+#endif
+ cpbuf[0] = uval;
+ prec = 1;
+ cp = cpbuf;
+ goto pr_tail;
+ }
+ if (have_prec == 0)
+ prec = 1;
+ else if (prec > arg->stlen)
+ prec = arg->stlen;
+ cp = arg->stptr;
+ goto pr_tail;
+ case 's':
+ parse_next_arg();
+ arg = force_string(arg);
+ if (have_prec == 0 || prec > arg->stlen)
+ prec = arg->stlen;
+ cp = arg->stptr;
+ goto pr_tail;
+ case 'd':
+ case 'i':
+ parse_next_arg();
+ tmpval = force_number(arg);
+ if (tmpval > LONG_MAX || tmpval < LONG_MIN) {
+ /* out of range - emergency use of %g format */
+ cs1 = 'g';
+ goto format_float;
+ }
+ val = (long) tmpval;
+
+ if (val < 0) {
+ sgn = 1;
+ if (val > LONG_MIN)
+ uval = (unsigned long) -val;
+ else
+ uval = (unsigned long)(-(LONG_MIN + 1))
+ + (unsigned long)1;
+ } else {
+ sgn = 0;
+ uval = (unsigned long) val;
+ }
+ do {
+ *--cp = (char) ('0' + uval % 10);
+ uval /= 10;
+ } while (uval);
+ if (sgn)
+ *--cp = '-';
+ else if (signchar)
+ *--cp = signchar;
+ if (have_prec != 0) /* ignore '0' flag if */
+ fill = sp; /* precision given */
+ if (prec > fw)
+ fw = prec;
+ prec = cend - cp;
+ if (fw > prec && ! lj && fill != sp
+ && (*cp == '-' || signchar)) {
+ bchunk_one(cp);
+ cp++;
+ prec--;
+ fw--;
+ }
+ goto pr_tail;
+ case 'X':
+ chbuf = Uchbuf; /* FALL THROUGH */
+ case 'x':
+ base += 6; /* FALL THROUGH */
+ case 'u':
+ base += 2; /* FALL THROUGH */
+ case 'o':
+ base += 8;
+ parse_next_arg();
+ tmpval = force_number(arg);
+ if (tmpval > ULONG_MAX || tmpval < LONG_MIN) {
+ /* out of range - emergency use of %g format */
+ cs1 = 'g';
+ goto format_float;
+ }
+ uval = (unsigned long)tmpval;
+ if (have_prec != 0) /* ignore '0' flag if */
+ fill = sp; /* precision given */
+ do {
+ *--cp = chbuf[uval % base];
+ uval /= base;
+ } while (uval);
+ if (alt) {
+ if (base == 16) {
+ *--cp = cs1;
+ *--cp = '0';
+ if (fill != sp) {
+ bchunk(cp, 2);
+ cp += 2;
+ fw -= 2;
+ }
+ } else if (base == 8)
+ *--cp = '0';
+ }
+ base = 0;
+ prec = cend - cp;
+ pr_tail:
+ if (! lj) {
+ while (fw > prec) {
+ bchunk_one(fill);
+ fw--;
+ }
+ }
+ bchunk(cp, (int) prec);
+ while (fw > prec) {
+ bchunk_one(fill);
+ fw--;
+ }
+ s0 = s1;
+ free_temp(arg);
+ break;
+ case 'g':
+ case 'G':
+ case 'e':
+ case 'f':
+ case 'E':
+ parse_next_arg();
+ tmpval = force_number(arg);
+ format_float:
+ free_temp(arg);
+ if (have_prec == 0)
+ prec = DEFAULT_G_PRECISION;
+ chksize(fw + prec + 9); /* 9==slop */
+
+ cp = cpbuf;
+ *cp++ = '%';
+ if (lj)
+ *cp++ = '-';
+ if (signchar)
+ *cp++ = signchar;
+ if (alt)
+ *cp++ = '#';
+ if (fill != sp)
+ *cp++ = '0';
+ cp = strcpy(cp, "*.*") + 3;
+ *cp++ = cs1;
+ *cp = '\0';
+#ifndef GFMT_WORKAROUND
+ (void) sprintf(obufout, cpbuf,
+ (int) fw, (int) prec, (double) tmpval);
+#else /* GFMT_WORKAROUND */
+ if (cs1 == 'g' || cs1 == 'G')
+ sgfmt(obufout, cpbuf, (int) alt,
+ (int) fw, (int) prec, (double) tmpval);
+ else
+ (void) sprintf(obufout, cpbuf,
+ (int) fw, (int) prec, (double) tmpval);
+#endif /* GFMT_WORKAROUND */
+ len = strlen(obufout);
+ ofre -= len;
+ obufout += len;
+ s0 = s1;
+ break;
+ default:
+ break;
+ }
+ if (toofew)
+ fatal("%s\n\t%s\n\t%*s%s",
+ "not enough arguments to satisfy format string",
+ fmt_string, s1 - fmt_string - 2, "",
+ "^ ran out for this one"
+ );
+ }
+ if (do_lint && carg != NULL)
+ warning("too many arguments supplied for format string");
+ bchunk(s0, s1 - s0);
+ r = make_str_node(obuf, obufout - obuf, ALREADY_MALLOCED);
+ r->flags |= TEMP;
+ return r;
+}
+
+NODE *
+do_sprintf(tree)
+NODE *tree;
+{
+ NODE *r;
+ NODE *sfmt = force_string(tree_eval(tree->lnode));
+
+ r = format_tree(sfmt->stptr, sfmt->stlen, tree->rnode);
+ free_temp(sfmt);
+ return r;
+}
+
+
+void
+do_printf(tree)
+register NODE *tree;
+{
+ struct redirect *rp = NULL;
+ register FILE *fp;
+
+ if (tree->rnode) {
+ int errflg; /* not used, sigh */
+
+ rp = redirect(tree->rnode, &errflg);
+ if (rp) {
+ fp = rp->fp;
+ if (!fp)
+ return;
+ } else
+ return;
+ } else
+ fp = stdout;
+ tree = do_sprintf(tree->lnode);
+ efwrite(tree->stptr, sizeof(char), tree->stlen, fp, "printf", rp , 1);
+ free_temp(tree);
+}
+
+NODE *
+do_sqrt(tree)
+NODE *tree;
+{
+ NODE *tmp;
+ double arg;
+ extern double sqrt P((double));
+
+ tmp = tree_eval(tree->lnode);
+ arg = (double) force_number(tmp);
+ free_temp(tmp);
+ if (arg < 0.0)
+ warning("sqrt called with negative argument %g", arg);
+ return tmp_number((AWKNUM) sqrt(arg));
+}
+
+NODE *
+do_substr(tree)
+NODE *tree;
+{
+ NODE *t1, *t2, *t3;
+ NODE *r;
+ register int indx;
+ size_t length;
+ int is_long;
+
+ t1 = tree_eval(tree->lnode);
+ t2 = tree_eval(tree->rnode->lnode);
+ if (tree->rnode->rnode == NULL) /* third arg. missing */
+ length = t1->stlen;
+ else {
+ t3 = tree_eval(tree->rnode->rnode->lnode);
+ length = (size_t) force_number(t3);
+ free_temp(t3);
+ }
+ indx = (int) force_number(t2) - 1;
+ free_temp(t2);
+ t1 = force_string(t1);
+ if (indx < 0)
+ indx = 0;
+ if (indx >= t1->stlen || (long) length <= 0) {
+ free_temp(t1);
+ return Nnull_string;
+ }
+ if ((is_long = (indx + length > t1->stlen)) || LONG_MAX - indx < length) {
+ length = t1->stlen - indx;
+ if (do_lint && is_long)
+ warning("substr: length %d at position %d exceeds length of first argument",
+ length, indx+1);
+ }
+ r = tmp_string(t1->stptr + indx, length);
+ free_temp(t1);
+ return r;
+}
+
+NODE *
+do_strftime(tree)
+NODE *tree;
+{
+ NODE *t1, *t2;
+ struct tm *tm;
+ time_t fclock;
+ char buf[100];
+
+ t1 = force_string(tree_eval(tree->lnode));
+
+ if (tree->rnode == NULL) /* second arg. missing, default */
+ (void) time(&fclock);
+ else {
+ t2 = tree_eval(tree->rnode->lnode);
+ fclock = (time_t) force_number(t2);
+ free_temp(t2);
+ }
+ tm = localtime(&fclock);
+
+ return tmp_string(buf, strftime(buf, 100, t1->stptr, tm));
+}
+
+NODE *
+do_systime(tree)
+NODE *tree;
+{
+ time_t lclock;
+
+ (void) time(&lclock);
+ return tmp_number((AWKNUM) lclock);
+}
+
+NODE *
+do_system(tree)
+NODE *tree;
+{
+ NODE *tmp;
+ int ret = 0;
+ char *cmd;
+ char save;
+
+ (void) flush_io (); /* so output is synchronous with gawk's */
+ tmp = tree_eval(tree->lnode);
+ cmd = force_string(tmp)->stptr;
+
+ if (cmd && *cmd) {
+ /* insure arg to system is zero-terminated */
+
+ /*
+ * From: David Trueman <emory!cs.dal.ca!david>
+ * To: arnold@cc.gatech.edu (Arnold Robbins)
+ * Date: Wed, 3 Nov 1993 12:49:41 -0400
+ *
+ * It may not be necessary to save the character, but
+ * I'm not sure. It would normally be the field
+ * separator. If the parse has not yet gone beyond
+ * that, it could mess up (although I doubt it). If
+ * FIELDWIDTHS is being used, it might be the first
+ * character of the next field. Unless someone wants
+ * to check it out exhaustively, I suggest saving it
+ * for now...
+ */
+ save = cmd[tmp->stlen];
+ cmd[tmp->stlen] = '\0';
+
+ ret = system(cmd);
+ ret = (ret >> 8) & 0xff;
+
+ cmd[tmp->stlen] = save;
+ }
+ free_temp(tmp);
+ return tmp_number((AWKNUM) ret);
+}
+
+extern NODE **fmt_list; /* declared in eval.c */
+
+void
+do_print(tree)
+register NODE *tree;
+{
+ register NODE *t1;
+ struct redirect *rp = NULL;
+ register FILE *fp;
+ register char *s;
+
+ if (tree->rnode) {
+ int errflg; /* not used, sigh */
+
+ rp = redirect(tree->rnode, &errflg);
+ if (rp) {
+ fp = rp->fp;
+ if (!fp)
+ return;
+ } else
+ return;
+ } else
+ fp = stdout;
+ tree = tree->lnode;
+ while (tree) {
+ t1 = tree_eval(tree->lnode);
+ if (t1->flags & NUMBER) {
+ if (OFMTidx == CONVFMTidx)
+ (void) force_string(t1);
+ else {
+#ifndef GFMT_WORKAROUND
+ char buf[100];
+
+ (void) sprintf(buf, OFMT, t1->numbr);
+ free_temp(t1);
+ t1 = tmp_string(buf, strlen(buf));
+#else /* GFMT_WORKAROUND */
+ free_temp(t1);
+ t1 = format_tree(OFMT,
+ fmt_list[OFMTidx]->stlen,
+ tree);
+#endif /* GFMT_WORKAROUND */
+ }
+ }
+ efwrite(t1->stptr, sizeof(char), t1->stlen, fp, "print", rp, 0);
+ free_temp(t1);
+ tree = tree->rnode;
+ if (tree) {
+ s = OFS;
+ if (OFSlen)
+ efwrite(s, sizeof(char), (size_t)OFSlen,
+ fp, "print", rp, 0);
+ }
+ }
+ s = ORS;
+ if (ORSlen)
+ efwrite(s, sizeof(char), (size_t)ORSlen, fp, "print", rp, 1);
+}
+
+NODE *
+do_tolower(tree)
+NODE *tree;
+{
+ NODE *t1, *t2;
+ register char *cp, *cp2;
+
+ t1 = tree_eval(tree->lnode);
+ t1 = force_string(t1);
+ t2 = tmp_string(t1->stptr, t1->stlen);
+ for (cp = t2->stptr, cp2 = t2->stptr + t2->stlen; cp < cp2; cp++)
+ if (isupper(*cp))
+ *cp = tolower(*cp);
+ free_temp(t1);
+ return t2;
+}
+
+NODE *
+do_toupper(tree)
+NODE *tree;
+{
+ NODE *t1, *t2;
+ register char *cp;
+
+ t1 = tree_eval(tree->lnode);
+ t1 = force_string(t1);
+ t2 = tmp_string(t1->stptr, t1->stlen);
+ for (cp = t2->stptr; cp < t2->stptr + t2->stlen; cp++)
+ if (islower(*cp))
+ *cp = toupper(*cp);
+ free_temp(t1);
+ return t2;
+}
+
+NODE *
+do_atan2(tree)
+NODE *tree;
+{
+ NODE *t1, *t2;
+ extern double atan2 P((double, double));
+ double d1, d2;
+
+ t1 = tree_eval(tree->lnode);
+ t2 = tree_eval(tree->rnode->lnode);
+ d1 = force_number(t1);
+ d2 = force_number(t2);
+ free_temp(t1);
+ free_temp(t2);
+ return tmp_number((AWKNUM) atan2(d1, d2));
+}
+
+NODE *
+do_sin(tree)
+NODE *tree;
+{
+ NODE *tmp;
+ extern double sin P((double));
+ double d;
+
+ tmp = tree_eval(tree->lnode);
+ d = sin((double)force_number(tmp));
+ free_temp(tmp);
+ return tmp_number((AWKNUM) d);
+}
+
+NODE *
+do_cos(tree)
+NODE *tree;
+{
+ NODE *tmp;
+ extern double cos P((double));
+ double d;
+
+ tmp = tree_eval(tree->lnode);
+ d = cos((double)force_number(tmp));
+ free_temp(tmp);
+ return tmp_number((AWKNUM) d);
+}
+
+static int firstrand = 1;
+static char state[512];
+
+/* ARGSUSED */
+NODE *
+do_rand(tree)
+NODE *tree;
+{
+ if (firstrand) {
+ (void) initstate((unsigned) 1, state, sizeof state);
+ srandom(1);
+ firstrand = 0;
+ }
+ return tmp_number((AWKNUM) random() / GAWK_RANDOM_MAX);
+}
+
+NODE *
+do_srand(tree)
+NODE *tree;
+{
+ NODE *tmp;
+ static long save_seed = 0;
+ long ret = save_seed; /* SVR4 awk srand returns previous seed */
+
+ if (firstrand)
+ (void) initstate((unsigned) 1, state, sizeof state);
+ else
+ (void) setstate(state);
+
+ if (!tree)
+ srandom((unsigned int) (save_seed = (long) time((time_t *) 0)));
+ else {
+ tmp = tree_eval(tree->lnode);
+ srandom((unsigned int) (save_seed = (long) force_number(tmp)));
+ free_temp(tmp);
+ }
+ firstrand = 0;
+ return tmp_number((AWKNUM) ret);
+}
+
+NODE *
+do_match(tree)
+NODE *tree;
+{
+ NODE *t1;
+ int rstart;
+ AWKNUM rlength;
+ Regexp *rp;
+
+ t1 = force_string(tree_eval(tree->lnode));
+ tree = tree->rnode->lnode;
+ rp = re_update(tree);
+ rstart = research(rp, t1->stptr, 0, t1->stlen, 1);
+ if (rstart >= 0) { /* match succeded */
+ rstart++; /* 1-based indexing */
+ rlength = REEND(rp, t1->stptr) - RESTART(rp, t1->stptr);
+ } else { /* match failed */
+ rstart = 0;
+ rlength = -1.0;
+ }
+ free_temp(t1);
+ unref(RSTART_node->var_value);
+ RSTART_node->var_value = make_number((AWKNUM) rstart);
+ unref(RLENGTH_node->var_value);
+ RLENGTH_node->var_value = make_number(rlength);
+ return tmp_number((AWKNUM) rstart);
+}
+
+static NODE *
+sub_common(tree, global)
+NODE *tree;
+int global;
+{
+ register char *scan;
+ register char *bp, *cp;
+ char *buf;
+ size_t buflen;
+ register char *matchend;
+ register size_t len;
+ char *matchstart;
+ char *text;
+ size_t textlen;
+ char *repl;
+ char *replend;
+ size_t repllen;
+ int sofar;
+ int ampersands;
+ int matches = 0;
+ Regexp *rp;
+ NODE *s; /* subst. pattern */
+ NODE *t; /* string to make sub. in; $0 if none given */
+ NODE *tmp;
+ NODE **lhs = &tree; /* value not used -- just different from NULL */
+ int priv = 0;
+ Func_ptr after_assign = NULL;
+
+ tmp = tree->lnode;
+ rp = re_update(tmp);
+
+ tree = tree->rnode;
+ s = tree->lnode;
+
+ tree = tree->rnode;
+ tmp = tree->lnode;
+ t = force_string(tree_eval(tmp));
+
+ /* do the search early to avoid work on non-match */
+ if (research(rp, t->stptr, 0, t->stlen, 1) == -1 ||
+ RESTART(rp, t->stptr) > t->stlen) {
+ free_temp(t);
+ return tmp_number((AWKNUM) 0.0);
+ }
+
+ if (tmp->type == Node_val)
+ lhs = NULL;
+ else
+ lhs = get_lhs(tmp, &after_assign);
+ t->flags |= STRING;
+ /*
+ * create a private copy of the string
+ */
+ if (t->stref > 1 || (t->flags & PERM)) {
+ unsigned int saveflags;
+
+ saveflags = t->flags;
+ t->flags &= ~MALLOC;
+ tmp = dupnode(t);
+ t->flags = saveflags;
+ t = tmp;
+ priv = 1;
+ }
+ text = t->stptr;
+ textlen = t->stlen;
+ buflen = textlen + 2;
+
+ s = force_string(tree_eval(s));
+ repl = s->stptr;
+ replend = repl + s->stlen;
+ repllen = replend - repl;
+ emalloc(buf, char *, buflen + 2, "do_sub");
+ buf[buflen] = '\0';
+ buf[buflen + 1] = '\0';
+ ampersands = 0;
+ for (scan = repl; scan < replend; scan++) {
+ if (*scan == '&') {
+ repllen--;
+ ampersands++;
+ } else if (*scan == '\\' && *(scan+1) == '&') {
+ repllen--;
+ scan++;
+ }
+ }
+
+ bp = buf;
+ for (;;) {
+ matches++;
+ matchstart = t->stptr + RESTART(rp, t->stptr);
+ matchend = t->stptr + REEND(rp, t->stptr);
+
+ /*
+ * create the result, copying in parts of the original
+ * string
+ */
+ len = matchstart - text + repllen
+ + ampersands * (matchend - matchstart);
+ sofar = bp - buf;
+ while ((long)(buflen - sofar - len - 1) < 0) {
+ buflen *= 2;
+ erealloc(buf, char *, buflen, "do_sub");
+ bp = buf + sofar;
+ }
+ for (scan = text; scan < matchstart; scan++)
+ *bp++ = *scan;
+ for (scan = repl; scan < replend; scan++)
+ if (*scan == '&')
+ for (cp = matchstart; cp < matchend; cp++)
+ *bp++ = *cp;
+ else if (*scan == '\\' && *(scan+1) == '&') {
+ scan++;
+ *bp++ = *scan;
+ } else
+ *bp++ = *scan;
+
+ /* catch the case of gsub(//, "blah", whatever), i.e. empty regexp */
+ if (global && matchstart == matchend && matchend < text + textlen) {
+ *bp++ = *matchend;
+ matchend++;
+ }
+ textlen = text + textlen - matchend;
+ text = matchend;
+ if (!global || (long)textlen <= 0 ||
+ research(rp, t->stptr, text-t->stptr, textlen, 1) == -1)
+ break;
+ }
+ sofar = bp - buf;
+ if (buflen - sofar - textlen - 1) {
+ buflen = sofar + textlen + 2;
+ erealloc(buf, char *, buflen, "do_sub");
+ bp = buf + sofar;
+ }
+ for (scan = matchend; scan < text + textlen; scan++)
+ *bp++ = *scan;
+ *bp = '\0';
+ textlen = bp - buf;
+ free(t->stptr);
+ t->stptr = buf;
+ t->stlen = textlen;
+
+ free_temp(s);
+ if (matches > 0 && lhs) {
+ if (priv) {
+ unref(*lhs);
+ *lhs = t;
+ }
+ if (after_assign)
+ (*after_assign)();
+ t->flags &= ~(NUM|NUMBER);
+ }
+ return tmp_number((AWKNUM) matches);
+}
+
+NODE *
+do_gsub(tree)
+NODE *tree;
+{
+ return sub_common(tree, 1);
+}
+
+NODE *
+do_sub(tree)
+NODE *tree;
+{
+ return sub_common(tree, 0);
+}
+
+#ifdef GFMT_WORKAROUND
+/*
+ * printf's %g format [can't rely on gcvt()]
+ * caveat: don't use as argument to *printf()!
+ * 'format' string HAS to be of "<flags>*.*g" kind, or we bomb!
+ */
+static void
+sgfmt(buf, format, alt, fwidth, prec, g)
+char *buf; /* return buffer; assumed big enough to hold result */
+const char *format;
+int alt; /* use alternate form flag */
+int fwidth; /* field width in a format */
+int prec; /* indicates desired significant digits, not decimal places */
+double g; /* value to format */
+{
+ char dform[40];
+ register char *gpos;
+ register char *d, *e, *p;
+ int again = 0;
+
+ strncpy(dform, format, sizeof dform - 1);
+ dform[sizeof dform - 1] = '\0';
+ gpos = strrchr(dform, '.');
+
+ if (g == 0.0 && alt == 0) { /* easy special case */
+ *gpos++ = 'd';
+ *gpos = '\0';
+ (void) sprintf(buf, dform, fwidth, 0);
+ return;
+ }
+ gpos += 2; /* advance to location of 'g' in the format */
+
+ if (prec <= 0) /* negative precision is ignored */
+ prec = (prec < 0 ? DEFAULT_G_PRECISION : 1);
+
+ if (*gpos == 'G')
+ again = 1;
+ /* start with 'e' format (it'll provide nice exponent) */
+ *gpos = 'e';
+ prec -= 1;
+ (void) sprintf(buf, dform, fwidth, prec, g);
+ if ((e = strrchr(buf, 'e')) != NULL) { /* find exponent */
+ int exp = atoi(e+1); /* fetch exponent */
+ if (exp >= -4 && exp <= prec) { /* per K&R2, B1.2 */
+ /* switch to 'f' format and re-do */
+ *gpos = 'f';
+ prec -= exp; /* decimal precision */
+ (void) sprintf(buf, dform, fwidth, prec, g);
+ e = buf + strlen(buf);
+ while (*--e == ' ')
+ continue;
+ e += 1;
+ }
+ else if (again != 0)
+ *gpos = 'E';
+
+ /* if 'alt' in force, then trailing zeros are not removed */
+ if (alt == 0 && (d = strrchr(buf, '.')) != NULL) {
+ /* throw away an excess of precision */
+ for (p = e; p > d && *--p == '0'; )
+ prec -= 1;
+ if (d == p)
+ prec -= 1;
+ if (prec < 0)
+ prec = 0;
+ /* and do that once again */
+ again = 1;
+ }
+ if (again != 0)
+ (void) sprintf(buf, dform, fwidth, prec, g);
+ }
+}
+#endif /* GFMT_WORKAROUND */
diff --git a/gnu/usr.bin/awk/config.h b/gnu/usr.bin/awk/config.h
new file mode 100644
index 0000000..0b3cca1
--- /dev/null
+++ b/gnu/usr.bin/awk/config.h
@@ -0,0 +1,306 @@
+/*
+ * config.h -- configuration definitions for gawk.
+ *
+ * For generic 4.4 alpha
+ */
+
+/*
+ * Copyright (C) 1991, 1992, 1993 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Progamming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GAWK; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This file isolates configuration dependencies for gnu awk.
+ * You should know something about your system, perhaps by having
+ * a manual handy, when you edit this file. You should copy config.h-dist
+ * to config.h, and edit config.h. Do not modify config.h-dist, so that
+ * it will be easy to apply any patches that may be distributed.
+ *
+ * The general idea is that systems conforming to the various standards
+ * should need to do the least amount of changing. Definining the various
+ * items in ths file usually means that your system is missing that
+ * particular feature.
+ *
+ * The order of preference in standard conformance is ANSI C, POSIX,
+ * and the SVID.
+ *
+ * If you have no clue as to what's going on with your system, try
+ * compiling gawk without editing this file and see what shows up
+ * missing in the link stage. From there, you can probably figure out
+ * which defines to turn on.
+ */
+
+/**************************/
+/* Miscellanious features */
+/**************************/
+
+/*
+ * BLKSIZE_MISSING
+ *
+ * Check your /usr/include/sys/stat.h file. If the stat structure
+ * does not have a member named st_blksize, define this. (This will
+ * most likely be the case on most System V systems prior to V.4.)
+ */
+/* #define BLKSIZE_MISSING 1 */
+
+/*
+ * SIGTYPE
+ *
+ * The return type of the routines passed to the signal function.
+ * Modern systems use `void', older systems use `int'.
+ * If left undefined, it will default to void.
+ */
+/* #define SIGTYPE int */
+
+/*
+ * SIZE_T_MISSING
+ *
+ * If your system has no typedef for size_t, define this to get a default
+ */
+/* #define SIZE_T_MISSING 1 */
+
+/*
+ * CHAR_UNSIGNED
+ *
+ * If your machine uses unsigned characters (IBM RT and RS/6000 and others)
+ * then define this for use in regex.c
+ */
+/* #define CHAR_UNSIGNED 1 */
+
+/*
+ * HAVE_UNDERSCORE_SETJMP
+ *
+ * Check in your /usr/include/setjmp.h file. If there are routines
+ * there named _setjmp and _longjmp, then you should define this.
+ * Typically only systems derived from Berkeley Unix have this.
+ */
+#define HAVE_UNDERSCORE_SETJMP 1
+
+/*
+ * LIMITS_H_MISSING
+ *
+ * You don't have a <limits.h> include file.
+ */
+/* #define LIMITS_H_MISSING 1 */
+
+/***********************************************/
+/* Missing library subroutines or system calls */
+/***********************************************/
+
+/*
+ * MEMCMP_MISSING
+ * MEMCPY_MISSING
+ * MEMSET_MISSING
+ *
+ * These three routines are for manipulating blocks of memory. Most
+ * likely they will either all three be present or all three be missing,
+ * so they're grouped together.
+ */
+/* #define MEMCMP_MISSING 1 */
+/* #define MEMCPY_MISSING 1 */
+/* #define MEMSET_MISSING 1 */
+
+/*
+ * RANDOM_MISSING
+ *
+ * Your system does not have the random(3) suite of random number
+ * generating routines. These are different than the old rand(3)
+ * routines!
+ */
+/* #define RANDOM_MISSING 1 */
+
+/*
+ * STRCASE_MISSING
+ *
+ * Your system does not have the strcasemp() and strncasecmp()
+ * routines that originated in Berkeley Unix.
+ */
+/* #define STRCASE_MISSING 1 */
+
+/*
+ * STRCHR_MISSING
+ *
+ * Your system does not have the strchr() and strrchr() functions.
+ */
+/* #define STRCHR_MISSING 1 */
+
+/*
+ * STRERROR_MISSING
+ *
+ * Your system lacks the ANSI C strerror() routine for returning the
+ * strings associated with errno values.
+ */
+/* #define STRERROR_MISSING 1 */
+
+/*
+ * STRTOD_MISSING
+ *
+ * Your system does not have the strtod() routine for converting
+ * strings to double precision floating point values.
+ */
+/* #define STRTOD_MISSING 1 */
+
+/*
+ * STRFTIME_MISSING
+ *
+ * Your system lacks the ANSI C strftime() routine for formatting
+ * broken down time values.
+ */
+/* #define STRFTIME_MISSING 1 */
+
+/*
+ * TZSET_MISSING
+ *
+ * If you have a 4.2 BSD vintage system, then the strftime() routine
+ * supplied in the missing directory won't be enough, because it relies on the
+ * tzset() routine from System V / Posix. Fortunately, there is an
+ * emulation for tzset() too that should do the trick. If you don't
+ * have tzset(), define this.
+ */
+/* #define TZSET_MISSING 1 */
+
+/*
+ * TZNAME_MISSING
+ *
+ * Some systems do not support the external variables tzname and daylight.
+ * If this is the case *and* strftime() is missing, define this.
+ */
+/* #define TZNAME_MISSING 1 */
+
+/*
+ * TM_ZONE_MISSING
+ *
+ * Your "struct tm" is missing the tm_zone field.
+ * If this is the case *and* strftime() is missing *and* tzname is missing,
+ * define this.
+ */
+/* #define TM_ZONE_MISSING 1 */
+
+/*
+ * STDC_HEADERS
+ *
+ * If your system does have ANSI compliant header files that
+ * provide prototypes for library routines, then define this.
+ */
+#define STDC_HEADERS 1
+
+/*
+ * NO_TOKEN_PASTING
+ *
+ * If your compiler define's __STDC__ but does not support token
+ * pasting (tok##tok), then define this.
+ */
+/* #define NO_TOKEN_PASTING 1 */
+
+/*****************************************************************/
+/* Stuff related to the Standard I/O Library. */
+/*****************************************************************/
+/* Much of this is (still, unfortunately) black magic in nature. */
+/* You may have to use some or all of these together to get gawk */
+/* to work correctly. */
+/*****************************************************************/
+
+/*
+ * NON_STD_SPRINTF
+ *
+ * Look in your /usr/include/stdio.h file. If the return type of the
+ * sprintf() function is NOT `int', define this.
+ */
+/* #define NON_STD_SPRINTF 1 */
+
+/*
+ * VPRINTF_MISSING
+ *
+ * Define this if your system lacks vprintf() and the other routines
+ * that go with it. This will trigger an attempt to use _doprnt().
+ * If you don't have that, this attempt will fail and you are on your own.
+ */
+/* #define VPRINTF_MISSING 1 */
+
+/*
+ * Casts from size_t to int and back. These will become unnecessary
+ * at some point in the future, but for now are required where the
+ * two types are a different representation.
+ */
+/* #define SZTC */
+/* #define INTC */
+
+/*
+ * SYSTEM_MISSING
+ *
+ * Define this if your library does not provide a system function
+ * or you are not entirely happy with it and would rather use
+ * a provided replacement (atari only).
+ */
+/* #define SYSTEM_MISSING 1 */
+
+/*
+ * FMOD_MISSING
+ *
+ * Define this if your system lacks the fmod() function and modf() will
+ * be used instead.
+ */
+/* #define FMOD_MISSING 1 */
+
+
+/*******************************/
+/* Gawk configuration options. */
+/*******************************/
+
+/*
+ * DEFPATH
+ *
+ * The default search path for the -f option of gawk. It is used
+ * if the AWKPATH environment variable is undefined. The default
+ * definition is provided here. Most likely you should not change
+ * this.
+ */
+
+/* #define DEFPATH ".:/usr/lib/awk:/usr/local/lib/awk" */
+/* #define ENVSEP ':' */
+
+/*
+ * alloca already has a prototype defined - don't redefine it
+ */
+#define ALLOCA_PROTO 1
+
+/*
+ * srandom already has a prototype defined - don't redefine it
+ */
+#define SRANDOM_PROTO 1
+
+/*
+ * getpgrp() in sysvr4 and POSIX takes no argument
+ */
+/* #define GETPGRP_NOARG 0 */
+
+/*
+ * define const to nothing if not __STDC__
+ */
+#ifndef __STDC__
+#define const
+#endif
+
+/* If svr4 and not gcc */
+/* #define SVR4 0 */
+#ifdef SVR4
+#define __svr4__ 1
+#endif
+
+/* anything that follows is for system-specific short-term kludges */
diff --git a/gnu/usr.bin/awk/dfa.c b/gnu/usr.bin/awk/dfa.c
new file mode 100644
index 0000000..47ad35e
--- /dev/null
+++ b/gnu/usr.bin/awk/dfa.c
@@ -0,0 +1,2588 @@
+/* dfa.c - deterministic extended regexp routines for GNU
+ Copyright (C) 1988 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written June, 1988 by Mike Haertel
+ Modified July, 1988 by Arthur David Olson to assist BMG speedups */
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+#include <sys/types.h>
+extern char *calloc(), *malloc(), *realloc();
+extern void free();
+#endif
+
+#if defined(HAVE_STRING_H) || defined(STDC_HEADERS)
+#include <string.h>
+#undef index
+#define index strchr
+#else
+#include <strings.h>
+#endif
+
+#ifndef DEBUG /* use the same approach as regex.c */
+#undef assert
+#define assert(e)
+#endif /* DEBUG */
+
+#ifndef isgraph
+#define isgraph(C) (isprint(C) && !isspace(C))
+#endif
+
+#ifdef isascii
+#define ISALPHA(C) (isascii(C) && isalpha(C))
+#define ISUPPER(C) (isascii(C) && isupper(C))
+#define ISLOWER(C) (isascii(C) && islower(C))
+#define ISDIGIT(C) (isascii(C) && isdigit(C))
+#define ISXDIGIT(C) (isascii(C) && isxdigit(C))
+#define ISSPACE(C) (isascii(C) && isspace(C))
+#define ISPUNCT(C) (isascii(C) && ispunct(C))
+#define ISALNUM(C) (isascii(C) && isalnum(C))
+#define ISPRINT(C) (isascii(C) && isprint(C))
+#define ISGRAPH(C) (isascii(C) && isgraph(C))
+#define ISCNTRL(C) (isascii(C) && iscntrl(C))
+#else
+#define ISALPHA(C) isalpha(C)
+#define ISUPPER(C) isupper(C)
+#define ISLOWER(C) islower(C)
+#define ISDIGIT(C) isdigit(C)
+#define ISXDIGIT(C) isxdigit(C)
+#define ISSPACE(C) isspace(C)
+#define ISPUNCT(C) ispunct(C)
+#define ISALNUM(C) isalnum(C)
+#define ISPRINT(C) isprint(C)
+#define ISGRAPH(C) isgraph(C)
+#define ISCNTRL(C) iscntrl(C)
+#endif
+
+#include "regex.h"
+#include "dfa.h"
+
+#ifdef __STDC__
+typedef void *ptr_t;
+#else
+typedef char *ptr_t;
+#ifndef const
+#define const
+#endif
+#endif
+
+static void dfamust _RE_ARGS((struct dfa *dfa));
+
+static ptr_t xcalloc _RE_ARGS((size_t n, size_t s));
+static ptr_t xmalloc _RE_ARGS((size_t n));
+static ptr_t xrealloc _RE_ARGS((ptr_t p, size_t n));
+#ifdef DEBUG
+static void prtok _RE_ARGS((token t));
+#endif
+static int tstbit _RE_ARGS((int b, charclass c));
+static void setbit _RE_ARGS((int b, charclass c));
+static void clrbit _RE_ARGS((int b, charclass c));
+static void copyset _RE_ARGS((charclass src, charclass dst));
+static void zeroset _RE_ARGS((charclass s));
+static void notset _RE_ARGS((charclass s));
+static int equal _RE_ARGS((charclass s1, charclass s2));
+static int charclass_index _RE_ARGS((charclass s));
+static int looking_at _RE_ARGS((const char *s));
+static token lex _RE_ARGS((void));
+static void addtok _RE_ARGS((token t));
+static void atom _RE_ARGS((void));
+static int nsubtoks _RE_ARGS((int tindex));
+static void copytoks _RE_ARGS((int tindex, int ntokens));
+static void closure _RE_ARGS((void));
+static void branch _RE_ARGS((void));
+static void regexp _RE_ARGS((int toplevel));
+static void copy _RE_ARGS((position_set *src, position_set *dst));
+static void insert _RE_ARGS((position p, position_set *s));
+static void merge _RE_ARGS((position_set *s1, position_set *s2, position_set *m));
+static void delete _RE_ARGS((position p, position_set *s));
+static int state_index _RE_ARGS((struct dfa *d, position_set *s,
+ int newline, int letter));
+static void build_state _RE_ARGS((int s, struct dfa *d));
+static void build_state_zero _RE_ARGS((struct dfa *d));
+static char *icatalloc _RE_ARGS((char *old, char *new));
+static char *icpyalloc _RE_ARGS((char *string));
+static char *istrstr _RE_ARGS((char *lookin, char *lookfor));
+static void ifree _RE_ARGS((char *cp));
+static void freelist _RE_ARGS((char **cpp));
+static char **enlist _RE_ARGS((char **cpp, char *new, size_t len));
+static char **comsubs _RE_ARGS((char *left, char *right));
+static char **addlists _RE_ARGS((char **old, char **new));
+static char **inboth _RE_ARGS((char **left, char **right));
+
+static ptr_t
+xcalloc(n, s)
+ size_t n;
+ size_t s;
+{
+ ptr_t r = calloc(n, s);
+
+ if (!r)
+ dfaerror("Memory exhausted");
+ return r;
+}
+
+static ptr_t
+xmalloc(n)
+ size_t n;
+{
+ ptr_t r = malloc(n);
+
+ assert(n != 0);
+ if (!r)
+ dfaerror("Memory exhausted");
+ return r;
+}
+
+static ptr_t
+xrealloc(p, n)
+ ptr_t p;
+ size_t n;
+{
+ ptr_t r = realloc(p, n);
+
+ assert(n != 0);
+ if (!r)
+ dfaerror("Memory exhausted");
+ return r;
+}
+
+#define CALLOC(p, t, n) ((p) = (t *) xcalloc((size_t)(n), sizeof (t)))
+#define MALLOC(p, t, n) ((p) = (t *) xmalloc((n) * sizeof (t)))
+#define REALLOC(p, t, n) ((p) = (t *) xrealloc((ptr_t) (p), (n) * sizeof (t)))
+
+/* Reallocate an array of type t if nalloc is too small for index. */
+#define REALLOC_IF_NECESSARY(p, t, nalloc, index) \
+ if ((index) >= (nalloc)) \
+ { \
+ while ((index) >= (nalloc)) \
+ (nalloc) *= 2; \
+ REALLOC(p, t, nalloc); \
+ }
+
+#ifdef DEBUG
+
+static void
+prtok(t)
+ token t;
+{
+ char *s;
+
+ if (t < 0)
+ fprintf(stderr, "END");
+ else if (t < NOTCHAR)
+ fprintf(stderr, "%c", t);
+ else
+ {
+ switch (t)
+ {
+ case EMPTY: s = "EMPTY"; break;
+ case BACKREF: s = "BACKREF"; break;
+ case BEGLINE: s = "BEGLINE"; break;
+ case ENDLINE: s = "ENDLINE"; break;
+ case BEGWORD: s = "BEGWORD"; break;
+ case ENDWORD: s = "ENDWORD"; break;
+ case LIMWORD: s = "LIMWORD"; break;
+ case NOTLIMWORD: s = "NOTLIMWORD"; break;
+ case QMARK: s = "QMARK"; break;
+ case STAR: s = "STAR"; break;
+ case PLUS: s = "PLUS"; break;
+ case CAT: s = "CAT"; break;
+ case OR: s = "OR"; break;
+ case ORTOP: s = "ORTOP"; break;
+ case LPAREN: s = "LPAREN"; break;
+ case RPAREN: s = "RPAREN"; break;
+ default: s = "CSET"; break;
+ }
+ fprintf(stderr, "%s", s);
+ }
+}
+#endif /* DEBUG */
+
+/* Stuff pertaining to charclasses. */
+
+static int
+tstbit(b, c)
+ int b;
+ charclass c;
+{
+ return c[b / INTBITS] & 1 << b % INTBITS;
+}
+
+static void
+setbit(b, c)
+ int b;
+ charclass c;
+{
+ c[b / INTBITS] |= 1 << b % INTBITS;
+}
+
+static void
+clrbit(b, c)
+ int b;
+ charclass c;
+{
+ c[b / INTBITS] &= ~(1 << b % INTBITS);
+}
+
+static void
+copyset(src, dst)
+ charclass src;
+ charclass dst;
+{
+ int i;
+
+ for (i = 0; i < CHARCLASS_INTS; ++i)
+ dst[i] = src[i];
+}
+
+static void
+zeroset(s)
+ charclass s;
+{
+ int i;
+
+ for (i = 0; i < CHARCLASS_INTS; ++i)
+ s[i] = 0;
+}
+
+static void
+notset(s)
+ charclass s;
+{
+ int i;
+
+ for (i = 0; i < CHARCLASS_INTS; ++i)
+ s[i] = ~s[i];
+}
+
+static int
+equal(s1, s2)
+ charclass s1;
+ charclass s2;
+{
+ int i;
+
+ for (i = 0; i < CHARCLASS_INTS; ++i)
+ if (s1[i] != s2[i])
+ return 0;
+ return 1;
+}
+
+/* A pointer to the current dfa is kept here during parsing. */
+static struct dfa *dfa;
+
+/* Find the index of charclass s in dfa->charclasses, or allocate a new charclass. */
+static int
+charclass_index(s)
+ charclass s;
+{
+ int i;
+
+ for (i = 0; i < dfa->cindex; ++i)
+ if (equal(s, dfa->charclasses[i]))
+ return i;
+ REALLOC_IF_NECESSARY(dfa->charclasses, charclass, dfa->calloc, dfa->cindex);
+ ++dfa->cindex;
+ copyset(s, dfa->charclasses[i]);
+ return i;
+}
+
+/* Syntax bits controlling the behavior of the lexical analyzer. */
+static reg_syntax_t syntax_bits, syntax_bits_set;
+
+/* Flag for case-folding letters into sets. */
+static int case_fold;
+
+/* Entry point to set syntax options. */
+void
+dfasyntax(bits, fold)
+ reg_syntax_t bits;
+ int fold;
+{
+ syntax_bits_set = 1;
+ syntax_bits = bits;
+ case_fold = fold;
+}
+
+/* Lexical analyzer. All the dross that deals with the obnoxious
+ GNU Regex syntax bits is located here. The poor, suffering
+ reader is referred to the GNU Regex documentation for the
+ meaning of the @#%!@#%^!@ syntax bits. */
+
+static char *lexstart; /* Pointer to beginning of input string. */
+static char *lexptr; /* Pointer to next input character. */
+static lexleft; /* Number of characters remaining. */
+static token lasttok; /* Previous token returned; initially END. */
+static int laststart; /* True if we're separated from beginning or (, |
+ only by zero-width characters. */
+static int parens; /* Count of outstanding left parens. */
+static int minrep, maxrep; /* Repeat counts for {m,n}. */
+
+/* Note that characters become unsigned here. */
+#define FETCH(c, eoferr) \
+ { \
+ if (! lexleft) \
+ if (eoferr != 0) \
+ dfaerror(eoferr); \
+ else \
+ return lasttok = END; \
+ (c) = (unsigned char) *lexptr++; \
+ --lexleft; \
+ }
+
+#ifdef __STDC__
+#define FUNC(F, P) static int F(int c) { return P(c); }
+#else
+#define FUNC(F, P) static int F(c) int c; { return P(c); }
+#endif
+
+FUNC(is_alpha, ISALPHA)
+FUNC(is_upper, ISUPPER)
+FUNC(is_lower, ISLOWER)
+FUNC(is_digit, ISDIGIT)
+FUNC(is_xdigit, ISXDIGIT)
+FUNC(is_space, ISSPACE)
+FUNC(is_punct, ISPUNCT)
+FUNC(is_alnum, ISALNUM)
+FUNC(is_print, ISPRINT)
+FUNC(is_graph, ISGRAPH)
+FUNC(is_cntrl, ISCNTRL)
+
+/* The following list maps the names of the Posix named character classes
+ to predicate functions that determine whether a given character is in
+ the class. The leading [ has already been eaten by the lexical analyzer. */
+static struct {
+ const char *name;
+ int (*pred) _RE_ARGS((int));
+} prednames[] = {
+ { ":alpha:]", is_alpha },
+ { ":upper:]", is_upper },
+ { ":lower:]", is_lower },
+ { ":digit:]", is_digit },
+ { ":xdigit:]", is_xdigit },
+ { ":space:]", is_space },
+ { ":punct:]", is_punct },
+ { ":alnum:]", is_alnum },
+ { ":print:]", is_print },
+ { ":graph:]", is_graph },
+ { ":cntrl:]", is_cntrl },
+ { 0 }
+};
+
+static int
+looking_at(s)
+ const char *s;
+{
+ size_t len;
+
+ len = strlen(s);
+ if (lexleft < len)
+ return 0;
+ return strncmp(s, lexptr, len) == 0;
+}
+
+static token
+lex()
+{
+ token c, c1, c2;
+ int backslash = 0, invert;
+ charclass ccl;
+ int i;
+
+ /* Basic plan: We fetch a character. If it's a backslash,
+ we set the backslash flag and go through the loop again.
+ On the plus side, this avoids having a duplicate of the
+ main switch inside the backslash case. On the minus side,
+ it means that just about every case begins with
+ "if (backslash) ...". */
+ for (i = 0; i < 2; ++i)
+ {
+ FETCH(c, 0);
+ switch (c)
+ {
+ case '\\':
+ if (backslash)
+ goto normal_char;
+ if (lexleft == 0)
+ dfaerror("Unfinished \\ escape");
+ backslash = 1;
+ break;
+
+ case '^':
+ if (backslash)
+ goto normal_char;
+ if (syntax_bits & RE_CONTEXT_INDEP_ANCHORS
+ || lasttok == END
+ || lasttok == LPAREN
+ || lasttok == OR)
+ return lasttok = BEGLINE;
+ goto normal_char;
+
+ case '$':
+ if (backslash)
+ goto normal_char;
+ if (syntax_bits & RE_CONTEXT_INDEP_ANCHORS
+ || lexleft == 0
+ || (syntax_bits & RE_NO_BK_PARENS
+ ? lexleft > 0 && *lexptr == ')'
+ : lexleft > 1 && lexptr[0] == '\\' && lexptr[1] == ')')
+ || (syntax_bits & RE_NO_BK_VBAR
+ ? lexleft > 0 && *lexptr == '|'
+ : lexleft > 1 && lexptr[0] == '\\' && lexptr[1] == '|')
+ || ((syntax_bits & RE_NEWLINE_ALT)
+ && lexleft > 0 && *lexptr == '\n'))
+ return lasttok = ENDLINE;
+ goto normal_char;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (backslash && !(syntax_bits & RE_NO_BK_REFS))
+ {
+ laststart = 0;
+ return lasttok = BACKREF;
+ }
+ goto normal_char;
+
+ case '<':
+ if (syntax_bits & RE_NO_GNU_OPS)
+ goto normal_char;
+ if (backslash)
+ return lasttok = BEGWORD;
+ goto normal_char;
+
+ case '>':
+ if (syntax_bits & RE_NO_GNU_OPS)
+ goto normal_char;
+ if (backslash)
+ return lasttok = ENDWORD;
+ goto normal_char;
+
+ case 'b':
+ if (syntax_bits & RE_NO_GNU_OPS)
+ goto normal_char;
+ if (backslash)
+ return lasttok = LIMWORD;
+ goto normal_char;
+
+ case 'B':
+ if (syntax_bits & RE_NO_GNU_OPS)
+ goto normal_char;
+ if (backslash)
+ return lasttok = NOTLIMWORD;
+ goto normal_char;
+
+ case '?':
+ if (syntax_bits & RE_LIMITED_OPS)
+ goto normal_char;
+ if (backslash != ((syntax_bits & RE_BK_PLUS_QM) != 0))
+ goto normal_char;
+ if (!(syntax_bits & RE_CONTEXT_INDEP_OPS) && laststart)
+ goto normal_char;
+ return lasttok = QMARK;
+
+ case '*':
+ if (backslash)
+ goto normal_char;
+ if (!(syntax_bits & RE_CONTEXT_INDEP_OPS) && laststart)
+ goto normal_char;
+ return lasttok = STAR;
+
+ case '+':
+ if (syntax_bits & RE_LIMITED_OPS)
+ goto normal_char;
+ if (backslash != ((syntax_bits & RE_BK_PLUS_QM) != 0))
+ goto normal_char;
+ if (!(syntax_bits & RE_CONTEXT_INDEP_OPS) && laststart)
+ goto normal_char;
+ return lasttok = PLUS;
+
+ case '{':
+ if (!(syntax_bits & RE_INTERVALS))
+ goto normal_char;
+ if (backslash != ((syntax_bits & RE_NO_BK_BRACES) == 0))
+ goto normal_char;
+ minrep = maxrep = 0;
+ /* Cases:
+ {M} - exact count
+ {M,} - minimum count, maximum is infinity
+ {,M} - 0 through M
+ {M,N} - M through N */
+ FETCH(c, "unfinished repeat count");
+ if (ISDIGIT(c))
+ {
+ minrep = c - '0';
+ for (;;)
+ {
+ FETCH(c, "unfinished repeat count");
+ if (!ISDIGIT(c))
+ break;
+ minrep = 10 * minrep + c - '0';
+ }
+ }
+ else if (c != ',')
+ dfaerror("malformed repeat count");
+ if (c == ',')
+ for (;;)
+ {
+ FETCH(c, "unfinished repeat count");
+ if (!ISDIGIT(c))
+ break;
+ maxrep = 10 * maxrep + c - '0';
+ }
+ else
+ maxrep = minrep;
+ if (!(syntax_bits & RE_NO_BK_BRACES))
+ {
+ if (c != '\\')
+ dfaerror("malformed repeat count");
+ FETCH(c, "unfinished repeat count");
+ }
+ if (c != '}')
+ dfaerror("malformed repeat count");
+ laststart = 0;
+ return lasttok = REPMN;
+
+ case '|':
+ if (syntax_bits & RE_LIMITED_OPS)
+ goto normal_char;
+ if (backslash != ((syntax_bits & RE_NO_BK_VBAR) == 0))
+ goto normal_char;
+ laststart = 1;
+ return lasttok = OR;
+
+ case '\n':
+ if (syntax_bits & RE_LIMITED_OPS
+ || backslash
+ || !(syntax_bits & RE_NEWLINE_ALT))
+ goto normal_char;
+ laststart = 1;
+ return lasttok = OR;
+
+ case '(':
+ if (backslash != ((syntax_bits & RE_NO_BK_PARENS) == 0))
+ goto normal_char;
+ ++parens;
+ laststart = 1;
+ return lasttok = LPAREN;
+
+ case ')':
+ if (backslash != ((syntax_bits & RE_NO_BK_PARENS) == 0))
+ goto normal_char;
+ if (parens == 0 && syntax_bits & RE_UNMATCHED_RIGHT_PAREN_ORD)
+ goto normal_char;
+ --parens;
+ laststart = 0;
+ return lasttok = RPAREN;
+
+ case '.':
+ if (backslash)
+ goto normal_char;
+ zeroset(ccl);
+ notset(ccl);
+ if (!(syntax_bits & RE_DOT_NEWLINE))
+ clrbit('\n', ccl);
+ if (syntax_bits & RE_DOT_NOT_NULL)
+ clrbit('\0', ccl);
+ laststart = 0;
+ return lasttok = CSET + charclass_index(ccl);
+
+ case 'w':
+ case 'W':
+ if (!backslash || (syntax_bits & RE_NO_GNU_OPS))
+ goto normal_char;
+ zeroset(ccl);
+ for (c2 = 0; c2 < NOTCHAR; ++c2)
+ if (ISALNUM(c2))
+ setbit(c2, ccl);
+ if (c == 'W')
+ notset(ccl);
+ laststart = 0;
+ return lasttok = CSET + charclass_index(ccl);
+
+ case '[':
+ if (backslash)
+ goto normal_char;
+ zeroset(ccl);
+ FETCH(c, "Unbalanced [");
+ if (c == '^')
+ {
+ FETCH(c, "Unbalanced [");
+ invert = 1;
+ }
+ else
+ invert = 0;
+ do
+ {
+ /* Nobody ever said this had to be fast. :-)
+ Note that if we're looking at some other [:...:]
+ construct, we just treat it as a bunch of ordinary
+ characters. We can do this because we assume
+ regex has checked for syntax errors before
+ dfa is ever called. */
+ if (c == '[' && (syntax_bits & RE_CHAR_CLASSES))
+ for (c1 = 0; prednames[c1].name; ++c1)
+ if (looking_at(prednames[c1].name))
+ {
+ for (c2 = 0; c2 < NOTCHAR; ++c2)
+ if ((*prednames[c1].pred)(c2))
+ setbit(c2, ccl);
+ lexptr += strlen(prednames[c1].name);
+ lexleft -= strlen(prednames[c1].name);
+ FETCH(c1, "Unbalanced [");
+ goto skip;
+ }
+ if (c == '\\' && (syntax_bits & RE_BACKSLASH_ESCAPE_IN_LISTS))
+ FETCH(c, "Unbalanced [");
+ FETCH(c1, "Unbalanced [");
+ if (c1 == '-')
+ {
+ FETCH(c2, "Unbalanced [");
+ if (c2 == ']')
+ {
+ /* In the case [x-], the - is an ordinary hyphen,
+ which is left in c1, the lookahead character. */
+ --lexptr;
+ ++lexleft;
+ c2 = c;
+ }
+ else
+ {
+ if (c2 == '\\'
+ && (syntax_bits & RE_BACKSLASH_ESCAPE_IN_LISTS))
+ FETCH(c2, "Unbalanced [");
+ FETCH(c1, "Unbalanced [");
+ }
+ }
+ else
+ c2 = c;
+ while (c <= c2)
+ {
+ setbit(c, ccl);
+ if (case_fold)
+ if (ISUPPER(c))
+ setbit(tolower(c), ccl);
+ else if (ISLOWER(c))
+ setbit(toupper(c), ccl);
+ ++c;
+ }
+ skip:
+ ;
+ }
+ while ((c = c1) != ']');
+ if (invert)
+ {
+ notset(ccl);
+ if (syntax_bits & RE_HAT_LISTS_NOT_NEWLINE)
+ clrbit('\n', ccl);
+ }
+ laststart = 0;
+ return lasttok = CSET + charclass_index(ccl);
+
+ default:
+ normal_char:
+ laststart = 0;
+ if (case_fold && ISALPHA(c))
+ {
+ zeroset(ccl);
+ setbit(c, ccl);
+ if (isupper(c))
+ setbit(tolower(c), ccl);
+ else
+ setbit(toupper(c), ccl);
+ return lasttok = CSET + charclass_index(ccl);
+ }
+ return c;
+ }
+ }
+
+ /* The above loop should consume at most a backslash
+ and some other character. */
+ abort();
+}
+
+/* Recursive descent parser for regular expressions. */
+
+static token tok; /* Lookahead token. */
+static depth; /* Current depth of a hypothetical stack
+ holding deferred productions. This is
+ used to determine the depth that will be
+ required of the real stack later on in
+ dfaanalyze(). */
+
+/* Add the given token to the parse tree, maintaining the depth count and
+ updating the maximum depth if necessary. */
+static void
+addtok(t)
+ token t;
+{
+ REALLOC_IF_NECESSARY(dfa->tokens, token, dfa->talloc, dfa->tindex);
+ dfa->tokens[dfa->tindex++] = t;
+
+ switch (t)
+ {
+ case QMARK:
+ case STAR:
+ case PLUS:
+ break;
+
+ case CAT:
+ case OR:
+ case ORTOP:
+ --depth;
+ break;
+
+ default:
+ ++dfa->nleaves;
+ case EMPTY:
+ ++depth;
+ break;
+ }
+ if (depth > dfa->depth)
+ dfa->depth = depth;
+}
+
+/* The grammar understood by the parser is as follows.
+
+ regexp:
+ regexp OR branch
+ branch
+
+ branch:
+ branch closure
+ closure
+
+ closure:
+ closure QMARK
+ closure STAR
+ closure PLUS
+ atom
+
+ atom:
+ <normal character>
+ CSET
+ BACKREF
+ BEGLINE
+ ENDLINE
+ BEGWORD
+ ENDWORD
+ LIMWORD
+ NOTLIMWORD
+ <empty>
+
+ The parser builds a parse tree in postfix form in an array of tokens. */
+
+static void
+atom()
+{
+ if ((tok >= 0 && tok < NOTCHAR) || tok >= CSET || tok == BACKREF
+ || tok == BEGLINE || tok == ENDLINE || tok == BEGWORD
+ || tok == ENDWORD || tok == LIMWORD || tok == NOTLIMWORD)
+ {
+ addtok(tok);
+ tok = lex();
+ }
+ else if (tok == LPAREN)
+ {
+ tok = lex();
+ regexp(0);
+ if (tok != RPAREN)
+ dfaerror("Unbalanced (");
+ tok = lex();
+ }
+ else
+ addtok(EMPTY);
+}
+
+/* Return the number of tokens in the given subexpression. */
+static int
+nsubtoks(tindex)
+int tindex;
+{
+ int ntoks1;
+
+ switch (dfa->tokens[tindex - 1])
+ {
+ default:
+ return 1;
+ case QMARK:
+ case STAR:
+ case PLUS:
+ return 1 + nsubtoks(tindex - 1);
+ case CAT:
+ case OR:
+ case ORTOP:
+ ntoks1 = nsubtoks(tindex - 1);
+ return 1 + ntoks1 + nsubtoks(tindex - 1 - ntoks1);
+ }
+}
+
+/* Copy the given subexpression to the top of the tree. */
+static void
+copytoks(tindex, ntokens)
+ int tindex, ntokens;
+{
+ int i;
+
+ for (i = 0; i < ntokens; ++i)
+ addtok(dfa->tokens[tindex + i]);
+}
+
+static void
+closure()
+{
+ int tindex, ntokens, i;
+
+ atom();
+ while (tok == QMARK || tok == STAR || tok == PLUS || tok == REPMN)
+ if (tok == REPMN)
+ {
+ ntokens = nsubtoks(dfa->tindex);
+ tindex = dfa->tindex - ntokens;
+ if (maxrep == 0)
+ addtok(PLUS);
+ if (minrep == 0)
+ addtok(QMARK);
+ for (i = 1; i < minrep; ++i)
+ {
+ copytoks(tindex, ntokens);
+ addtok(CAT);
+ }
+ for (; i < maxrep; ++i)
+ {
+ copytoks(tindex, ntokens);
+ addtok(QMARK);
+ addtok(CAT);
+ }
+ tok = lex();
+ }
+ else
+ {
+ addtok(tok);
+ tok = lex();
+ }
+}
+
+static void
+branch()
+{
+ closure();
+ while (tok != RPAREN && tok != OR && tok >= 0)
+ {
+ closure();
+ addtok(CAT);
+ }
+}
+
+static void
+regexp(toplevel)
+ int toplevel;
+{
+ branch();
+ while (tok == OR)
+ {
+ tok = lex();
+ branch();
+ if (toplevel)
+ addtok(ORTOP);
+ else
+ addtok(OR);
+ }
+}
+
+/* Main entry point for the parser. S is a string to be parsed, len is the
+ length of the string, so s can include NUL characters. D is a pointer to
+ the struct dfa to parse into. */
+void
+dfaparse(s, len, d)
+ char *s;
+ size_t len;
+ struct dfa *d;
+
+{
+ dfa = d;
+ lexstart = lexptr = s;
+ lexleft = len;
+ lasttok = END;
+ laststart = 1;
+ parens = 0;
+
+ if (! syntax_bits_set)
+ dfaerror("No syntax specified");
+
+ tok = lex();
+ depth = d->depth;
+
+ regexp(1);
+
+ if (tok != END)
+ dfaerror("Unbalanced )");
+
+ addtok(END - d->nregexps);
+ addtok(CAT);
+
+ if (d->nregexps)
+ addtok(ORTOP);
+
+ ++d->nregexps;
+}
+
+/* Some primitives for operating on sets of positions. */
+
+/* Copy one set to another; the destination must be large enough. */
+static void
+copy(src, dst)
+ position_set *src;
+ position_set *dst;
+{
+ int i;
+
+ for (i = 0; i < src->nelem; ++i)
+ dst->elems[i] = src->elems[i];
+ dst->nelem = src->nelem;
+}
+
+/* Insert a position in a set. Position sets are maintained in sorted
+ order according to index. If position already exists in the set with
+ the same index then their constraints are logically or'd together.
+ S->elems must point to an array large enough to hold the resulting set. */
+static void
+insert(p, s)
+ position p;
+ position_set *s;
+{
+ int i;
+ position t1, t2;
+
+ for (i = 0; i < s->nelem && p.index < s->elems[i].index; ++i)
+ continue;
+ if (i < s->nelem && p.index == s->elems[i].index)
+ s->elems[i].constraint |= p.constraint;
+ else
+ {
+ t1 = p;
+ ++s->nelem;
+ while (i < s->nelem)
+ {
+ t2 = s->elems[i];
+ s->elems[i++] = t1;
+ t1 = t2;
+ }
+ }
+}
+
+/* Merge two sets of positions into a third. The result is exactly as if
+ the positions of both sets were inserted into an initially empty set. */
+static void
+merge(s1, s2, m)
+ position_set *s1;
+ position_set *s2;
+ position_set *m;
+{
+ int i = 0, j = 0;
+
+ m->nelem = 0;
+ while (i < s1->nelem && j < s2->nelem)
+ if (s1->elems[i].index > s2->elems[j].index)
+ m->elems[m->nelem++] = s1->elems[i++];
+ else if (s1->elems[i].index < s2->elems[j].index)
+ m->elems[m->nelem++] = s2->elems[j++];
+ else
+ {
+ m->elems[m->nelem] = s1->elems[i++];
+ m->elems[m->nelem++].constraint |= s2->elems[j++].constraint;
+ }
+ while (i < s1->nelem)
+ m->elems[m->nelem++] = s1->elems[i++];
+ while (j < s2->nelem)
+ m->elems[m->nelem++] = s2->elems[j++];
+}
+
+/* Delete a position from a set. */
+static void
+delete(p, s)
+ position p;
+ position_set *s;
+{
+ int i;
+
+ for (i = 0; i < s->nelem; ++i)
+ if (p.index == s->elems[i].index)
+ break;
+ if (i < s->nelem)
+ for (--s->nelem; i < s->nelem; ++i)
+ s->elems[i] = s->elems[i + 1];
+}
+
+/* Find the index of the state corresponding to the given position set with
+ the given preceding context, or create a new state if there is no such
+ state. Newline and letter tell whether we got here on a newline or
+ letter, respectively. */
+static int
+state_index(d, s, newline, letter)
+ struct dfa *d;
+ position_set *s;
+ int newline;
+ int letter;
+{
+ int hash = 0;
+ int constraint;
+ int i, j;
+
+ newline = newline ? 1 : 0;
+ letter = letter ? 1 : 0;
+
+ for (i = 0; i < s->nelem; ++i)
+ hash ^= s->elems[i].index + s->elems[i].constraint;
+
+ /* Try to find a state that exactly matches the proposed one. */
+ for (i = 0; i < d->sindex; ++i)
+ {
+ if (hash != d->states[i].hash || s->nelem != d->states[i].elems.nelem
+ || newline != d->states[i].newline || letter != d->states[i].letter)
+ continue;
+ for (j = 0; j < s->nelem; ++j)
+ if (s->elems[j].constraint
+ != d->states[i].elems.elems[j].constraint
+ || s->elems[j].index != d->states[i].elems.elems[j].index)
+ break;
+ if (j == s->nelem)
+ return i;
+ }
+
+ /* We'll have to create a new state. */
+ REALLOC_IF_NECESSARY(d->states, dfa_state, d->salloc, d->sindex);
+ d->states[i].hash = hash;
+ MALLOC(d->states[i].elems.elems, position, s->nelem);
+ copy(s, &d->states[i].elems);
+ d->states[i].newline = newline;
+ d->states[i].letter = letter;
+ d->states[i].backref = 0;
+ d->states[i].constraint = 0;
+ d->states[i].first_end = 0;
+ for (j = 0; j < s->nelem; ++j)
+ if (d->tokens[s->elems[j].index] < 0)
+ {
+ constraint = s->elems[j].constraint;
+ if (SUCCEEDS_IN_CONTEXT(constraint, newline, 0, letter, 0)
+ || SUCCEEDS_IN_CONTEXT(constraint, newline, 0, letter, 1)
+ || SUCCEEDS_IN_CONTEXT(constraint, newline, 1, letter, 0)
+ || SUCCEEDS_IN_CONTEXT(constraint, newline, 1, letter, 1))
+ d->states[i].constraint |= constraint;
+ if (! d->states[i].first_end)
+ d->states[i].first_end = d->tokens[s->elems[j].index];
+ }
+ else if (d->tokens[s->elems[j].index] == BACKREF)
+ {
+ d->states[i].constraint = NO_CONSTRAINT;
+ d->states[i].backref = 1;
+ }
+
+ ++d->sindex;
+
+ return i;
+}
+
+/* Find the epsilon closure of a set of positions. If any position of the set
+ contains a symbol that matches the empty string in some context, replace
+ that position with the elements of its follow labeled with an appropriate
+ constraint. Repeat exhaustively until no funny positions are left.
+ S->elems must be large enough to hold the result. */
+static void epsclosure _RE_ARGS((position_set *s, struct dfa *d));
+
+static void
+epsclosure(s, d)
+ position_set *s;
+ struct dfa *d;
+{
+ int i, j;
+ int *visited;
+ position p, old;
+
+ MALLOC(visited, int, d->tindex);
+ for (i = 0; i < d->tindex; ++i)
+ visited[i] = 0;
+
+ for (i = 0; i < s->nelem; ++i)
+ if (d->tokens[s->elems[i].index] >= NOTCHAR
+ && d->tokens[s->elems[i].index] != BACKREF
+ && d->tokens[s->elems[i].index] < CSET)
+ {
+ old = s->elems[i];
+ p.constraint = old.constraint;
+ delete(s->elems[i], s);
+ if (visited[old.index])
+ {
+ --i;
+ continue;
+ }
+ visited[old.index] = 1;
+ switch (d->tokens[old.index])
+ {
+ case BEGLINE:
+ p.constraint &= BEGLINE_CONSTRAINT;
+ break;
+ case ENDLINE:
+ p.constraint &= ENDLINE_CONSTRAINT;
+ break;
+ case BEGWORD:
+ p.constraint &= BEGWORD_CONSTRAINT;
+ break;
+ case ENDWORD:
+ p.constraint &= ENDWORD_CONSTRAINT;
+ break;
+ case LIMWORD:
+ p.constraint &= LIMWORD_CONSTRAINT;
+ break;
+ case NOTLIMWORD:
+ p.constraint &= NOTLIMWORD_CONSTRAINT;
+ break;
+ default:
+ break;
+ }
+ for (j = 0; j < d->follows[old.index].nelem; ++j)
+ {
+ p.index = d->follows[old.index].elems[j].index;
+ insert(p, s);
+ }
+ /* Force rescan to start at the beginning. */
+ i = -1;
+ }
+
+ free(visited);
+}
+
+/* Perform bottom-up analysis on the parse tree, computing various functions.
+ Note that at this point, we're pretending constructs like \< are real
+ characters rather than constraints on what can follow them.
+
+ Nullable: A node is nullable if it is at the root of a regexp that can
+ match the empty string.
+ * EMPTY leaves are nullable.
+ * No other leaf is nullable.
+ * A QMARK or STAR node is nullable.
+ * A PLUS node is nullable if its argument is nullable.
+ * A CAT node is nullable if both its arguments are nullable.
+ * An OR node is nullable if either argument is nullable.
+
+ Firstpos: The firstpos of a node is the set of positions (nonempty leaves)
+ that could correspond to the first character of a string matching the
+ regexp rooted at the given node.
+ * EMPTY leaves have empty firstpos.
+ * The firstpos of a nonempty leaf is that leaf itself.
+ * The firstpos of a QMARK, STAR, or PLUS node is the firstpos of its
+ argument.
+ * The firstpos of a CAT node is the firstpos of the left argument, union
+ the firstpos of the right if the left argument is nullable.
+ * The firstpos of an OR node is the union of firstpos of each argument.
+
+ Lastpos: The lastpos of a node is the set of positions that could
+ correspond to the last character of a string matching the regexp at
+ the given node.
+ * EMPTY leaves have empty lastpos.
+ * The lastpos of a nonempty leaf is that leaf itself.
+ * The lastpos of a QMARK, STAR, or PLUS node is the lastpos of its
+ argument.
+ * The lastpos of a CAT node is the lastpos of its right argument, union
+ the lastpos of the left if the right argument is nullable.
+ * The lastpos of an OR node is the union of the lastpos of each argument.
+
+ Follow: The follow of a position is the set of positions that could
+ correspond to the character following a character matching the node in
+ a string matching the regexp. At this point we consider special symbols
+ that match the empty string in some context to be just normal characters.
+ Later, if we find that a special symbol is in a follow set, we will
+ replace it with the elements of its follow, labeled with an appropriate
+ constraint.
+ * Every node in the firstpos of the argument of a STAR or PLUS node is in
+ the follow of every node in the lastpos.
+ * Every node in the firstpos of the second argument of a CAT node is in
+ the follow of every node in the lastpos of the first argument.
+
+ Because of the postfix representation of the parse tree, the depth-first
+ analysis is conveniently done by a linear scan with the aid of a stack.
+ Sets are stored as arrays of the elements, obeying a stack-like allocation
+ scheme; the number of elements in each set deeper in the stack can be
+ used to determine the address of a particular set's array. */
+void
+dfaanalyze(d, searchflag)
+ struct dfa *d;
+ int searchflag;
+{
+ int *nullable; /* Nullable stack. */
+ int *nfirstpos; /* Element count stack for firstpos sets. */
+ position *firstpos; /* Array where firstpos elements are stored. */
+ int *nlastpos; /* Element count stack for lastpos sets. */
+ position *lastpos; /* Array where lastpos elements are stored. */
+ int *nalloc; /* Sizes of arrays allocated to follow sets. */
+ position_set tmp; /* Temporary set for merging sets. */
+ position_set merged; /* Result of merging sets. */
+ int wants_newline; /* True if some position wants newline info. */
+ int *o_nullable;
+ int *o_nfirst, *o_nlast;
+ position *o_firstpos, *o_lastpos;
+ int i, j;
+ position *pos;
+
+#ifdef DEBUG
+ fprintf(stderr, "dfaanalyze:\n");
+ for (i = 0; i < d->tindex; ++i)
+ {
+ fprintf(stderr, " %d:", i);
+ prtok(d->tokens[i]);
+ }
+ putc('\n', stderr);
+#endif
+
+ d->searchflag = searchflag;
+
+ MALLOC(nullable, int, d->depth);
+ o_nullable = nullable;
+ MALLOC(nfirstpos, int, d->depth);
+ o_nfirst = nfirstpos;
+ MALLOC(firstpos, position, d->nleaves);
+ o_firstpos = firstpos, firstpos += d->nleaves;
+ MALLOC(nlastpos, int, d->depth);
+ o_nlast = nlastpos;
+ MALLOC(lastpos, position, d->nleaves);
+ o_lastpos = lastpos, lastpos += d->nleaves;
+ MALLOC(nalloc, int, d->tindex);
+ for (i = 0; i < d->tindex; ++i)
+ nalloc[i] = 0;
+ MALLOC(merged.elems, position, d->nleaves);
+
+ CALLOC(d->follows, position_set, d->tindex);
+
+ for (i = 0; i < d->tindex; ++i)
+#ifdef DEBUG
+ { /* Nonsyntactic #ifdef goo... */
+#endif
+ switch (d->tokens[i])
+ {
+ case EMPTY:
+ /* The empty set is nullable. */
+ *nullable++ = 1;
+
+ /* The firstpos and lastpos of the empty leaf are both empty. */
+ *nfirstpos++ = *nlastpos++ = 0;
+ break;
+
+ case STAR:
+ case PLUS:
+ /* Every element in the firstpos of the argument is in the follow
+ of every element in the lastpos. */
+ tmp.nelem = nfirstpos[-1];
+ tmp.elems = firstpos;
+ pos = lastpos;
+ for (j = 0; j < nlastpos[-1]; ++j)
+ {
+ merge(&tmp, &d->follows[pos[j].index], &merged);
+ REALLOC_IF_NECESSARY(d->follows[pos[j].index].elems, position,
+ nalloc[pos[j].index], merged.nelem - 1);
+ copy(&merged, &d->follows[pos[j].index]);
+ }
+
+ case QMARK:
+ /* A QMARK or STAR node is automatically nullable. */
+ if (d->tokens[i] != PLUS)
+ nullable[-1] = 1;
+ break;
+
+ case CAT:
+ /* Every element in the firstpos of the second argument is in the
+ follow of every element in the lastpos of the first argument. */
+ tmp.nelem = nfirstpos[-1];
+ tmp.elems = firstpos;
+ pos = lastpos + nlastpos[-1];
+ for (j = 0; j < nlastpos[-2]; ++j)
+ {
+ merge(&tmp, &d->follows[pos[j].index], &merged);
+ REALLOC_IF_NECESSARY(d->follows[pos[j].index].elems, position,
+ nalloc[pos[j].index], merged.nelem - 1);
+ copy(&merged, &d->follows[pos[j].index]);
+ }
+
+ /* The firstpos of a CAT node is the firstpos of the first argument,
+ union that of the second argument if the first is nullable. */
+ if (nullable[-2])
+ nfirstpos[-2] += nfirstpos[-1];
+ else
+ firstpos += nfirstpos[-1];
+ --nfirstpos;
+
+ /* The lastpos of a CAT node is the lastpos of the second argument,
+ union that of the first argument if the second is nullable. */
+ if (nullable[-1])
+ nlastpos[-2] += nlastpos[-1];
+ else
+ {
+ pos = lastpos + nlastpos[-2];
+ for (j = nlastpos[-1] - 1; j >= 0; --j)
+ pos[j] = lastpos[j];
+ lastpos += nlastpos[-2];
+ nlastpos[-2] = nlastpos[-1];
+ }
+ --nlastpos;
+
+ /* A CAT node is nullable if both arguments are nullable. */
+ nullable[-2] = nullable[-1] && nullable[-2];
+ --nullable;
+ break;
+
+ case OR:
+ case ORTOP:
+ /* The firstpos is the union of the firstpos of each argument. */
+ nfirstpos[-2] += nfirstpos[-1];
+ --nfirstpos;
+
+ /* The lastpos is the union of the lastpos of each argument. */
+ nlastpos[-2] += nlastpos[-1];
+ --nlastpos;
+
+ /* An OR node is nullable if either argument is nullable. */
+ nullable[-2] = nullable[-1] || nullable[-2];
+ --nullable;
+ break;
+
+ default:
+ /* Anything else is a nonempty position. (Note that special
+ constructs like \< are treated as nonempty strings here;
+ an "epsilon closure" effectively makes them nullable later.
+ Backreferences have to get a real position so we can detect
+ transitions on them later. But they are nullable. */
+ *nullable++ = d->tokens[i] == BACKREF;
+
+ /* This position is in its own firstpos and lastpos. */
+ *nfirstpos++ = *nlastpos++ = 1;
+ --firstpos, --lastpos;
+ firstpos->index = lastpos->index = i;
+ firstpos->constraint = lastpos->constraint = NO_CONSTRAINT;
+
+ /* Allocate the follow set for this position. */
+ nalloc[i] = 1;
+ MALLOC(d->follows[i].elems, position, nalloc[i]);
+ break;
+ }
+#ifdef DEBUG
+ /* ... balance the above nonsyntactic #ifdef goo... */
+ fprintf(stderr, "node %d:", i);
+ prtok(d->tokens[i]);
+ putc('\n', stderr);
+ fprintf(stderr, nullable[-1] ? " nullable: yes\n" : " nullable: no\n");
+ fprintf(stderr, " firstpos:");
+ for (j = nfirstpos[-1] - 1; j >= 0; --j)
+ {
+ fprintf(stderr, " %d:", firstpos[j].index);
+ prtok(d->tokens[firstpos[j].index]);
+ }
+ fprintf(stderr, "\n lastpos:");
+ for (j = nlastpos[-1] - 1; j >= 0; --j)
+ {
+ fprintf(stderr, " %d:", lastpos[j].index);
+ prtok(d->tokens[lastpos[j].index]);
+ }
+ putc('\n', stderr);
+ }
+#endif
+
+ /* For each follow set that is the follow set of a real position, replace
+ it with its epsilon closure. */
+ for (i = 0; i < d->tindex; ++i)
+ if (d->tokens[i] < NOTCHAR || d->tokens[i] == BACKREF
+ || d->tokens[i] >= CSET)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "follows(%d:", i);
+ prtok(d->tokens[i]);
+ fprintf(stderr, "):");
+ for (j = d->follows[i].nelem - 1; j >= 0; --j)
+ {
+ fprintf(stderr, " %d:", d->follows[i].elems[j].index);
+ prtok(d->tokens[d->follows[i].elems[j].index]);
+ }
+ putc('\n', stderr);
+#endif
+ copy(&d->follows[i], &merged);
+ epsclosure(&merged, d);
+ if (d->follows[i].nelem < merged.nelem)
+ REALLOC(d->follows[i].elems, position, merged.nelem);
+ copy(&merged, &d->follows[i]);
+ }
+
+ /* Get the epsilon closure of the firstpos of the regexp. The result will
+ be the set of positions of state 0. */
+ merged.nelem = 0;
+ for (i = 0; i < nfirstpos[-1]; ++i)
+ insert(firstpos[i], &merged);
+ epsclosure(&merged, d);
+
+ /* Check if any of the positions of state 0 will want newline context. */
+ wants_newline = 0;
+ for (i = 0; i < merged.nelem; ++i)
+ if (PREV_NEWLINE_DEPENDENT(merged.elems[i].constraint))
+ wants_newline = 1;
+
+ /* Build the initial state. */
+ d->salloc = 1;
+ d->sindex = 0;
+ MALLOC(d->states, dfa_state, d->salloc);
+ state_index(d, &merged, wants_newline, 0);
+
+ free(o_nullable);
+ free(o_nfirst);
+ free(o_firstpos);
+ free(o_nlast);
+ free(o_lastpos);
+ free(nalloc);
+ free(merged.elems);
+}
+
+/* Find, for each character, the transition out of state s of d, and store
+ it in the appropriate slot of trans.
+
+ We divide the positions of s into groups (positions can appear in more
+ than one group). Each group is labeled with a set of characters that
+ every position in the group matches (taking into account, if necessary,
+ preceding context information of s). For each group, find the union
+ of the its elements' follows. This set is the set of positions of the
+ new state. For each character in the group's label, set the transition
+ on this character to be to a state corresponding to the set's positions,
+ and its associated backward context information, if necessary.
+
+ If we are building a searching matcher, we include the positions of state
+ 0 in every state.
+
+ The collection of groups is constructed by building an equivalence-class
+ partition of the positions of s.
+
+ For each position, find the set of characters C that it matches. Eliminate
+ any characters from C that fail on grounds of backward context.
+
+ Search through the groups, looking for a group whose label L has nonempty
+ intersection with C. If L - C is nonempty, create a new group labeled
+ L - C and having the same positions as the current group, and set L to
+ the intersection of L and C. Insert the position in this group, set
+ C = C - L, and resume scanning.
+
+ If after comparing with every group there are characters remaining in C,
+ create a new group labeled with the characters of C and insert this
+ position in that group. */
+void
+dfastate(s, d, trans)
+ int s;
+ struct dfa *d;
+ int trans[];
+{
+ position_set grps[NOTCHAR]; /* As many as will ever be needed. */
+ charclass labels[NOTCHAR]; /* Labels corresponding to the groups. */
+ int ngrps = 0; /* Number of groups actually used. */
+ position pos; /* Current position being considered. */
+ charclass matches; /* Set of matching characters. */
+ int matchesf; /* True if matches is nonempty. */
+ charclass intersect; /* Intersection with some label set. */
+ int intersectf; /* True if intersect is nonempty. */
+ charclass leftovers; /* Stuff in the label that didn't match. */
+ int leftoversf; /* True if leftovers is nonempty. */
+ static charclass letters; /* Set of characters considered letters. */
+ static charclass newline; /* Set of characters that aren't newline. */
+ position_set follows; /* Union of the follows of some group. */
+ position_set tmp; /* Temporary space for merging sets. */
+ int state; /* New state. */
+ int wants_newline; /* New state wants to know newline context. */
+ int state_newline; /* New state on a newline transition. */
+ int wants_letter; /* New state wants to know letter context. */
+ int state_letter; /* New state on a letter transition. */
+ static initialized; /* Flag for static initialization. */
+ int i, j, k;
+
+ /* Initialize the set of letters, if necessary. */
+ if (! initialized)
+ {
+ initialized = 1;
+ for (i = 0; i < NOTCHAR; ++i)
+ if (ISALNUM(i))
+ setbit(i, letters);
+ setbit('\n', newline);
+ }
+
+ zeroset(matches);
+
+ for (i = 0; i < d->states[s].elems.nelem; ++i)
+ {
+ pos = d->states[s].elems.elems[i];
+ if (d->tokens[pos.index] >= 0 && d->tokens[pos.index] < NOTCHAR)
+ setbit(d->tokens[pos.index], matches);
+ else if (d->tokens[pos.index] >= CSET)
+ copyset(d->charclasses[d->tokens[pos.index] - CSET], matches);
+ else
+ continue;
+
+ /* Some characters may need to be eliminated from matches because
+ they fail in the current context. */
+ if (pos.constraint != 0xFF)
+ {
+ if (! MATCHES_NEWLINE_CONTEXT(pos.constraint,
+ d->states[s].newline, 1))
+ clrbit('\n', matches);
+ if (! MATCHES_NEWLINE_CONTEXT(pos.constraint,
+ d->states[s].newline, 0))
+ for (j = 0; j < CHARCLASS_INTS; ++j)
+ matches[j] &= newline[j];
+ if (! MATCHES_LETTER_CONTEXT(pos.constraint,
+ d->states[s].letter, 1))
+ for (j = 0; j < CHARCLASS_INTS; ++j)
+ matches[j] &= ~letters[j];
+ if (! MATCHES_LETTER_CONTEXT(pos.constraint,
+ d->states[s].letter, 0))
+ for (j = 0; j < CHARCLASS_INTS; ++j)
+ matches[j] &= letters[j];
+
+ /* If there are no characters left, there's no point in going on. */
+ for (j = 0; j < CHARCLASS_INTS && !matches[j]; ++j)
+ continue;
+ if (j == CHARCLASS_INTS)
+ continue;
+ }
+
+ for (j = 0; j < ngrps; ++j)
+ {
+ /* If matches contains a single character only, and the current
+ group's label doesn't contain that character, go on to the
+ next group. */
+ if (d->tokens[pos.index] >= 0 && d->tokens[pos.index] < NOTCHAR
+ && !tstbit(d->tokens[pos.index], labels[j]))
+ continue;
+
+ /* Check if this group's label has a nonempty intersection with
+ matches. */
+ intersectf = 0;
+ for (k = 0; k < CHARCLASS_INTS; ++k)
+ (intersect[k] = matches[k] & labels[j][k]) ? (intersectf = 1) : 0;
+ if (! intersectf)
+ continue;
+
+ /* It does; now find the set differences both ways. */
+ leftoversf = matchesf = 0;
+ for (k = 0; k < CHARCLASS_INTS; ++k)
+ {
+ /* Even an optimizing compiler can't know this for sure. */
+ int match = matches[k], label = labels[j][k];
+
+ (leftovers[k] = ~match & label) ? (leftoversf = 1) : 0;
+ (matches[k] = match & ~label) ? (matchesf = 1) : 0;
+ }
+
+ /* If there were leftovers, create a new group labeled with them. */
+ if (leftoversf)
+ {
+ copyset(leftovers, labels[ngrps]);
+ copyset(intersect, labels[j]);
+ MALLOC(grps[ngrps].elems, position, d->nleaves);
+ copy(&grps[j], &grps[ngrps]);
+ ++ngrps;
+ }
+
+ /* Put the position in the current group. Note that there is no
+ reason to call insert() here. */
+ grps[j].elems[grps[j].nelem++] = pos;
+
+ /* If every character matching the current position has been
+ accounted for, we're done. */
+ if (! matchesf)
+ break;
+ }
+
+ /* If we've passed the last group, and there are still characters
+ unaccounted for, then we'll have to create a new group. */
+ if (j == ngrps)
+ {
+ copyset(matches, labels[ngrps]);
+ zeroset(matches);
+ MALLOC(grps[ngrps].elems, position, d->nleaves);
+ grps[ngrps].nelem = 1;
+ grps[ngrps].elems[0] = pos;
+ ++ngrps;
+ }
+ }
+
+ MALLOC(follows.elems, position, d->nleaves);
+ MALLOC(tmp.elems, position, d->nleaves);
+
+ /* If we are a searching matcher, the default transition is to a state
+ containing the positions of state 0, otherwise the default transition
+ is to fail miserably. */
+ if (d->searchflag)
+ {
+ wants_newline = 0;
+ wants_letter = 0;
+ for (i = 0; i < d->states[0].elems.nelem; ++i)
+ {
+ if (PREV_NEWLINE_DEPENDENT(d->states[0].elems.elems[i].constraint))
+ wants_newline = 1;
+ if (PREV_LETTER_DEPENDENT(d->states[0].elems.elems[i].constraint))
+ wants_letter = 1;
+ }
+ copy(&d->states[0].elems, &follows);
+ state = state_index(d, &follows, 0, 0);
+ if (wants_newline)
+ state_newline = state_index(d, &follows, 1, 0);
+ else
+ state_newline = state;
+ if (wants_letter)
+ state_letter = state_index(d, &follows, 0, 1);
+ else
+ state_letter = state;
+ for (i = 0; i < NOTCHAR; ++i)
+ if (i == '\n')
+ trans[i] = state_newline;
+ else if (ISALNUM(i))
+ trans[i] = state_letter;
+ else
+ trans[i] = state;
+ }
+ else
+ for (i = 0; i < NOTCHAR; ++i)
+ trans[i] = -1;
+
+ for (i = 0; i < ngrps; ++i)
+ {
+ follows.nelem = 0;
+
+ /* Find the union of the follows of the positions of the group.
+ This is a hideously inefficient loop. Fix it someday. */
+ for (j = 0; j < grps[i].nelem; ++j)
+ for (k = 0; k < d->follows[grps[i].elems[j].index].nelem; ++k)
+ insert(d->follows[grps[i].elems[j].index].elems[k], &follows);
+
+ /* If we are building a searching matcher, throw in the positions
+ of state 0 as well. */
+ if (d->searchflag)
+ for (j = 0; j < d->states[0].elems.nelem; ++j)
+ insert(d->states[0].elems.elems[j], &follows);
+
+ /* Find out if the new state will want any context information. */
+ wants_newline = 0;
+ if (tstbit('\n', labels[i]))
+ for (j = 0; j < follows.nelem; ++j)
+ if (PREV_NEWLINE_DEPENDENT(follows.elems[j].constraint))
+ wants_newline = 1;
+
+ wants_letter = 0;
+ for (j = 0; j < CHARCLASS_INTS; ++j)
+ if (labels[i][j] & letters[j])
+ break;
+ if (j < CHARCLASS_INTS)
+ for (j = 0; j < follows.nelem; ++j)
+ if (PREV_LETTER_DEPENDENT(follows.elems[j].constraint))
+ wants_letter = 1;
+
+ /* Find the state(s) corresponding to the union of the follows. */
+ state = state_index(d, &follows, 0, 0);
+ if (wants_newline)
+ state_newline = state_index(d, &follows, 1, 0);
+ else
+ state_newline = state;
+ if (wants_letter)
+ state_letter = state_index(d, &follows, 0, 1);
+ else
+ state_letter = state;
+
+ /* Set the transitions for each character in the current label. */
+ for (j = 0; j < CHARCLASS_INTS; ++j)
+ for (k = 0; k < INTBITS; ++k)
+ if (labels[i][j] & 1 << k)
+ {
+ int c = j * INTBITS + k;
+
+ if (c == '\n')
+ trans[c] = state_newline;
+ else if (ISALNUM(c))
+ trans[c] = state_letter;
+ else if (c < NOTCHAR)
+ trans[c] = state;
+ }
+ }
+
+ for (i = 0; i < ngrps; ++i)
+ free(grps[i].elems);
+ free(follows.elems);
+ free(tmp.elems);
+}
+
+/* Some routines for manipulating a compiled dfa's transition tables.
+ Each state may or may not have a transition table; if it does, and it
+ is a non-accepting state, then d->trans[state] points to its table.
+ If it is an accepting state then d->fails[state] points to its table.
+ If it has no table at all, then d->trans[state] is NULL.
+ TODO: Improve this comment, get rid of the unnecessary redundancy. */
+
+static void
+build_state(s, d)
+ int s;
+ struct dfa *d;
+{
+ int *trans; /* The new transition table. */
+ int i;
+
+ /* Set an upper limit on the number of transition tables that will ever
+ exist at once. 1024 is arbitrary. The idea is that the frequently
+ used transition tables will be quickly rebuilt, whereas the ones that
+ were only needed once or twice will be cleared away. */
+ if (d->trcount >= 1024)
+ {
+ for (i = 0; i < d->tralloc; ++i)
+ if (d->trans[i])
+ {
+ free((ptr_t) d->trans[i]);
+ d->trans[i] = NULL;
+ }
+ else if (d->fails[i])
+ {
+ free((ptr_t) d->fails[i]);
+ d->fails[i] = NULL;
+ }
+ d->trcount = 0;
+ }
+
+ ++d->trcount;
+
+ /* Set up the success bits for this state. */
+ d->success[s] = 0;
+ if (ACCEPTS_IN_CONTEXT(d->states[s].newline, 1, d->states[s].letter, 0,
+ s, *d))
+ d->success[s] |= 4;
+ if (ACCEPTS_IN_CONTEXT(d->states[s].newline, 0, d->states[s].letter, 1,
+ s, *d))
+ d->success[s] |= 2;
+ if (ACCEPTS_IN_CONTEXT(d->states[s].newline, 0, d->states[s].letter, 0,
+ s, *d))
+ d->success[s] |= 1;
+
+ MALLOC(trans, int, NOTCHAR);
+ dfastate(s, d, trans);
+
+ /* Now go through the new transition table, and make sure that the trans
+ and fail arrays are allocated large enough to hold a pointer for the
+ largest state mentioned in the table. */
+ for (i = 0; i < NOTCHAR; ++i)
+ if (trans[i] >= d->tralloc)
+ {
+ int oldalloc = d->tralloc;
+
+ while (trans[i] >= d->tralloc)
+ d->tralloc *= 2;
+ REALLOC(d->realtrans, int *, d->tralloc + 1);
+ d->trans = d->realtrans + 1;
+ REALLOC(d->fails, int *, d->tralloc);
+ REALLOC(d->success, int, d->tralloc);
+ REALLOC(d->newlines, int, d->tralloc);
+ while (oldalloc < d->tralloc)
+ {
+ d->trans[oldalloc] = NULL;
+ d->fails[oldalloc++] = NULL;
+ }
+ }
+
+ /* Keep the newline transition in a special place so we can use it as
+ a sentinel. */
+ d->newlines[s] = trans['\n'];
+ trans['\n'] = -1;
+
+ if (ACCEPTING(s, *d))
+ d->fails[s] = trans;
+ else
+ d->trans[s] = trans;
+}
+
+static void
+build_state_zero(d)
+ struct dfa *d;
+{
+ d->tralloc = 1;
+ d->trcount = 0;
+ CALLOC(d->realtrans, int *, d->tralloc + 1);
+ d->trans = d->realtrans + 1;
+ CALLOC(d->fails, int *, d->tralloc);
+ MALLOC(d->success, int, d->tralloc);
+ MALLOC(d->newlines, int, d->tralloc);
+ build_state(0, d);
+}
+
+/* Search through a buffer looking for a match to the given struct dfa.
+ Find the first occurrence of a string matching the regexp in the buffer,
+ and the shortest possible version thereof. Return a pointer to the first
+ character after the match, or NULL if none is found. Begin points to
+ the beginning of the buffer, and end points to the first character after
+ its end. We store a newline in *end to act as a sentinel, so end had
+ better point somewhere valid. Newline is a flag indicating whether to
+ allow newlines to be in the matching string. If count is non-
+ NULL it points to a place we're supposed to increment every time we
+ see a newline. Finally, if backref is non-NULL it points to a place
+ where we're supposed to store a 1 if backreferencing happened and the
+ match needs to be verified by a backtracking matcher. Otherwise
+ we store a 0 in *backref. */
+char *
+dfaexec(d, begin, end, newline, count, backref)
+ struct dfa *d;
+ char *begin;
+ char *end;
+ int newline;
+ int *count;
+ int *backref;
+{
+ register s, s1, tmp; /* Current state. */
+ register unsigned char *p; /* Current input character. */
+ register **trans, *t; /* Copy of d->trans so it can be optimized
+ into a register. */
+ static sbit[NOTCHAR]; /* Table for anding with d->success. */
+ static sbit_init;
+
+ if (! sbit_init)
+ {
+ int i;
+
+ sbit_init = 1;
+ for (i = 0; i < NOTCHAR; ++i)
+ if (i == '\n')
+ sbit[i] = 4;
+ else if (ISALNUM(i))
+ sbit[i] = 2;
+ else
+ sbit[i] = 1;
+ }
+
+ if (! d->tralloc)
+ build_state_zero(d);
+
+ s = s1 = 0;
+ p = (unsigned char *) begin;
+ trans = d->trans;
+ *end = '\n';
+
+ for (;;)
+ {
+ /* The dreaded inner loop. */
+ if ((t = trans[s]) != 0)
+ do
+ {
+ s1 = t[*p++];
+ if (! (t = trans[s1]))
+ goto last_was_s;
+ s = t[*p++];
+ }
+ while ((t = trans[s]) != 0);
+ goto last_was_s1;
+ last_was_s:
+ tmp = s, s = s1, s1 = tmp;
+ last_was_s1:
+
+ if (s >= 0 && p <= (unsigned char *) end && d->fails[s])
+ {
+ if (d->success[s] & sbit[*p])
+ {
+ if (backref)
+ if (d->states[s].backref)
+ *backref = 1;
+ else
+ *backref = 0;
+ return (char *) p;
+ }
+
+ s1 = s;
+ s = d->fails[s][*p++];
+ continue;
+ }
+
+ /* If the previous character was a newline, count it. */
+ if (count && (char *) p <= end && p[-1] == '\n')
+ ++*count;
+
+ /* Check if we've run off the end of the buffer. */
+ if ((char *) p > end)
+ return NULL;
+
+ if (s >= 0)
+ {
+ build_state(s, d);
+ trans = d->trans;
+ continue;
+ }
+
+ if (p[-1] == '\n' && newline)
+ {
+ s = d->newlines[s1];
+ continue;
+ }
+
+ s = 0;
+ }
+}
+
+/* Initialize the components of a dfa that the other routines don't
+ initialize for themselves. */
+void
+dfainit(d)
+ struct dfa *d;
+{
+ d->calloc = 1;
+ MALLOC(d->charclasses, charclass, d->calloc);
+ d->cindex = 0;
+
+ d->talloc = 1;
+ MALLOC(d->tokens, token, d->talloc);
+ d->tindex = d->depth = d->nleaves = d->nregexps = 0;
+
+ d->searchflag = 0;
+ d->tralloc = 0;
+
+ d->musts = 0;
+}
+
+/* Parse and analyze a single string of the given length. */
+void
+dfacomp(s, len, d, searchflag)
+ char *s;
+ size_t len;
+ struct dfa *d;
+ int searchflag;
+{
+ if (case_fold) /* dummy folding in service of dfamust() */
+ {
+ char *lcopy;
+ int i;
+
+ lcopy = malloc(len);
+ if (!lcopy)
+ dfaerror("out of memory");
+
+ /* This is a kludge. */
+ case_fold = 0;
+ for (i = 0; i < len; ++i)
+ if (ISUPPER(s[i]))
+ lcopy[i] = tolower(s[i]);
+ else
+ lcopy[i] = s[i];
+
+ dfainit(d);
+ dfaparse(lcopy, len, d);
+ free(lcopy);
+ dfamust(d);
+ d->cindex = d->tindex = d->depth = d->nleaves = d->nregexps = 0;
+ case_fold = 1;
+ dfaparse(s, len, d);
+ dfaanalyze(d, searchflag);
+ }
+ else
+ {
+ dfainit(d);
+ dfaparse(s, len, d);
+ dfamust(d);
+ dfaanalyze(d, searchflag);
+ }
+}
+
+/* Free the storage held by the components of a dfa. */
+void
+dfafree(d)
+ struct dfa *d;
+{
+ int i;
+ struct dfamust *dm, *ndm;
+
+ free((ptr_t) d->charclasses);
+ free((ptr_t) d->tokens);
+ for (i = 0; i < d->sindex; ++i)
+ free((ptr_t) d->states[i].elems.elems);
+ free((ptr_t) d->states);
+ for (i = 0; i < d->tindex; ++i)
+ if (d->follows[i].elems)
+ free((ptr_t) d->follows[i].elems);
+ free((ptr_t) d->follows);
+ for (i = 0; i < d->tralloc; ++i)
+ if (d->trans[i])
+ free((ptr_t) d->trans[i]);
+ else if (d->fails[i])
+ free((ptr_t) d->fails[i]);
+ if (d->realtrans) free((ptr_t) d->realtrans);
+ if (d->fails) free((ptr_t) d->fails);
+ if (d->newlines) free((ptr_t) d->newlines);
+ for (dm = d->musts; dm; dm = ndm)
+ {
+ ndm = dm->next;
+ free(dm->must);
+ free((ptr_t) dm);
+ }
+}
+
+/* Having found the postfix representation of the regular expression,
+ try to find a long sequence of characters that must appear in any line
+ containing the r.e.
+ Finding a "longest" sequence is beyond the scope here;
+ we take an easy way out and hope for the best.
+ (Take "(ab|a)b"--please.)
+
+ We do a bottom-up calculation of sequences of characters that must appear
+ in matches of r.e.'s represented by trees rooted at the nodes of the postfix
+ representation:
+ sequences that must appear at the left of the match ("left")
+ sequences that must appear at the right of the match ("right")
+ lists of sequences that must appear somewhere in the match ("in")
+ sequences that must constitute the match ("is")
+
+ When we get to the root of the tree, we use one of the longest of its
+ calculated "in" sequences as our answer. The sequence we find is returned in
+ d->must (where "d" is the single argument passed to "dfamust");
+ the length of the sequence is returned in d->mustn.
+
+ The sequences calculated for the various types of node (in pseudo ANSI c)
+ are shown below. "p" is the operand of unary operators (and the left-hand
+ operand of binary operators); "q" is the right-hand operand of binary
+ operators.
+
+ "ZERO" means "a zero-length sequence" below.
+
+ Type left right is in
+ ---- ---- ----- -- --
+ char c # c # c # c # c
+
+ CSET ZERO ZERO ZERO ZERO
+
+ STAR ZERO ZERO ZERO ZERO
+
+ QMARK ZERO ZERO ZERO ZERO
+
+ PLUS p->left p->right ZERO p->in
+
+ CAT (p->is==ZERO)? (q->is==ZERO)? (p->is!=ZERO && p->in plus
+ p->left : q->right : q->is!=ZERO) ? q->in plus
+ p->is##q->left p->right##q->is p->is##q->is : p->right##q->left
+ ZERO
+
+ OR longest common longest common (do p->is and substrings common to
+ leading trailing q->is have same p->in and q->in
+ (sub)sequence (sub)sequence length and
+ of p->left of p->right content) ?
+ and q->left and q->right p->is : NULL
+
+ If there's anything else we recognize in the tree, all four sequences get set
+ to zero-length sequences. If there's something we don't recognize in the tree,
+ we just return a zero-length sequence.
+
+ Break ties in favor of infrequent letters (choosing 'zzz' in preference to
+ 'aaa')?
+
+ And. . .is it here or someplace that we might ponder "optimizations" such as
+ egrep 'psi|epsilon' -> egrep 'psi'
+ egrep 'pepsi|epsilon' -> egrep 'epsi'
+ (Yes, we now find "epsi" as a "string
+ that must occur", but we might also
+ simplify the *entire* r.e. being sought)
+ grep '[c]' -> grep 'c'
+ grep '(ab|a)b' -> grep 'ab'
+ grep 'ab*' -> grep 'a'
+ grep 'a*b' -> grep 'b'
+
+ There are several issues:
+
+ Is optimization easy (enough)?
+
+ Does optimization actually accomplish anything,
+ or is the automaton you get from "psi|epsilon" (for example)
+ the same as the one you get from "psi" (for example)?
+
+ Are optimizable r.e.'s likely to be used in real-life situations
+ (something like 'ab*' is probably unlikely; something like is
+ 'psi|epsilon' is likelier)? */
+
+static char *
+icatalloc(old, new)
+ char *old;
+ char *new;
+{
+ char *result;
+ size_t oldsize, newsize;
+
+ newsize = (new == NULL) ? 0 : strlen(new);
+ if (old == NULL)
+ oldsize = 0;
+ else if (newsize == 0)
+ return old;
+ else oldsize = strlen(old);
+ if (old == NULL)
+ result = (char *) malloc(newsize + 1);
+ else
+ result = (char *) realloc((void *) old, oldsize + newsize + 1);
+ if (result != NULL && new != NULL)
+ (void) strcpy(result + oldsize, new);
+ return result;
+}
+
+static char *
+icpyalloc(string)
+ char *string;
+{
+ return icatalloc((char *) NULL, string);
+}
+
+static char *
+istrstr(lookin, lookfor)
+ char *lookin;
+ char *lookfor;
+{
+ char *cp;
+ size_t len;
+
+ len = strlen(lookfor);
+ for (cp = lookin; *cp != '\0'; ++cp)
+ if (strncmp(cp, lookfor, len) == 0)
+ return cp;
+ return NULL;
+}
+
+static void
+ifree(cp)
+ char *cp;
+{
+ if (cp != NULL)
+ free(cp);
+}
+
+static void
+freelist(cpp)
+ char **cpp;
+{
+ int i;
+
+ if (cpp == NULL)
+ return;
+ for (i = 0; cpp[i] != NULL; ++i)
+ {
+ free(cpp[i]);
+ cpp[i] = NULL;
+ }
+}
+
+static char **
+enlist(cpp, new, len)
+ char **cpp;
+ char *new;
+ size_t len;
+{
+ int i, j;
+
+ if (cpp == NULL)
+ return NULL;
+ if ((new = icpyalloc(new)) == NULL)
+ {
+ freelist(cpp);
+ return NULL;
+ }
+ new[len] = '\0';
+ /* Is there already something in the list that's new (or longer)? */
+ for (i = 0; cpp[i] != NULL; ++i)
+ if (istrstr(cpp[i], new) != NULL)
+ {
+ free(new);
+ return cpp;
+ }
+ /* Eliminate any obsoleted strings. */
+ j = 0;
+ while (cpp[j] != NULL)
+ if (istrstr(new, cpp[j]) == NULL)
+ ++j;
+ else
+ {
+ free(cpp[j]);
+ if (--i == j)
+ break;
+ cpp[j] = cpp[i];
+ cpp[i] = NULL;
+ }
+ /* Add the new string. */
+ cpp = (char **) realloc((char *) cpp, (i + 2) * sizeof *cpp);
+ if (cpp == NULL)
+ return NULL;
+ cpp[i] = new;
+ cpp[i + 1] = NULL;
+ return cpp;
+}
+
+/* Given pointers to two strings, return a pointer to an allocated
+ list of their distinct common substrings. Return NULL if something
+ seems wild. */
+static char **
+comsubs(left, right)
+ char *left;
+ char *right;
+{
+ char **cpp;
+ char *lcp;
+ char *rcp;
+ size_t i, len;
+
+ if (left == NULL || right == NULL)
+ return NULL;
+ cpp = (char **) malloc(sizeof *cpp);
+ if (cpp == NULL)
+ return NULL;
+ cpp[0] = NULL;
+ for (lcp = left; *lcp != '\0'; ++lcp)
+ {
+ len = 0;
+ rcp = index(right, *lcp);
+ while (rcp != NULL)
+ {
+ for (i = 1; lcp[i] != '\0' && lcp[i] == rcp[i]; ++i)
+ continue;
+ if (i > len)
+ len = i;
+ rcp = index(rcp + 1, *lcp);
+ }
+ if (len == 0)
+ continue;
+ if ((cpp = enlist(cpp, lcp, len)) == NULL)
+ break;
+ }
+ return cpp;
+}
+
+static char **
+addlists(old, new)
+char **old;
+char **new;
+{
+ int i;
+
+ if (old == NULL || new == NULL)
+ return NULL;
+ for (i = 0; new[i] != NULL; ++i)
+ {
+ old = enlist(old, new[i], strlen(new[i]));
+ if (old == NULL)
+ break;
+ }
+ return old;
+}
+
+/* Given two lists of substrings, return a new list giving substrings
+ common to both. */
+static char **
+inboth(left, right)
+ char **left;
+ char **right;
+{
+ char **both;
+ char **temp;
+ int lnum, rnum;
+
+ if (left == NULL || right == NULL)
+ return NULL;
+ both = (char **) malloc(sizeof *both);
+ if (both == NULL)
+ return NULL;
+ both[0] = NULL;
+ for (lnum = 0; left[lnum] != NULL; ++lnum)
+ {
+ for (rnum = 0; right[rnum] != NULL; ++rnum)
+ {
+ temp = comsubs(left[lnum], right[rnum]);
+ if (temp == NULL)
+ {
+ freelist(both);
+ return NULL;
+ }
+ both = addlists(both, temp);
+ freelist(temp);
+ if (both == NULL)
+ return NULL;
+ }
+ }
+ return both;
+}
+
+typedef struct
+{
+ char **in;
+ char *left;
+ char *right;
+ char *is;
+} must;
+
+static void
+resetmust(mp)
+must *mp;
+{
+ mp->left[0] = mp->right[0] = mp->is[0] = '\0';
+ freelist(mp->in);
+}
+
+static void
+dfamust(dfa)
+struct dfa *dfa;
+{
+ must *musts;
+ must *mp;
+ char *result;
+ int ri;
+ int i;
+ int exact;
+ token t;
+ static must must0;
+ struct dfamust *dm;
+ static char empty_string[] = "";
+
+ result = empty_string;
+ exact = 0;
+ musts = (must *) malloc((dfa->tindex + 1) * sizeof *musts);
+ if (musts == NULL)
+ return;
+ mp = musts;
+ for (i = 0; i <= dfa->tindex; ++i)
+ mp[i] = must0;
+ for (i = 0; i <= dfa->tindex; ++i)
+ {
+ mp[i].in = (char **) malloc(sizeof *mp[i].in);
+ mp[i].left = malloc(2);
+ mp[i].right = malloc(2);
+ mp[i].is = malloc(2);
+ if (mp[i].in == NULL || mp[i].left == NULL ||
+ mp[i].right == NULL || mp[i].is == NULL)
+ goto done;
+ mp[i].left[0] = mp[i].right[0] = mp[i].is[0] = '\0';
+ mp[i].in[0] = NULL;
+ }
+#ifdef DEBUG
+ fprintf(stderr, "dfamust:\n");
+ for (i = 0; i < dfa->tindex; ++i)
+ {
+ fprintf(stderr, " %d:", i);
+ prtok(dfa->tokens[i]);
+ }
+ putc('\n', stderr);
+#endif
+ for (ri = 0; ri < dfa->tindex; ++ri)
+ {
+ switch (t = dfa->tokens[ri])
+ {
+ case LPAREN:
+ case RPAREN:
+ goto done; /* "cannot happen" */
+ case EMPTY:
+ case BEGLINE:
+ case ENDLINE:
+ case BEGWORD:
+ case ENDWORD:
+ case LIMWORD:
+ case NOTLIMWORD:
+ case BACKREF:
+ resetmust(mp);
+ break;
+ case STAR:
+ case QMARK:
+ if (mp <= musts)
+ goto done; /* "cannot happen" */
+ --mp;
+ resetmust(mp);
+ break;
+ case OR:
+ case ORTOP:
+ if (mp < &musts[2])
+ goto done; /* "cannot happen" */
+ {
+ char **new;
+ must *lmp;
+ must *rmp;
+ int j, ln, rn, n;
+
+ rmp = --mp;
+ lmp = --mp;
+ /* Guaranteed to be. Unlikely, but. . . */
+ if (strcmp(lmp->is, rmp->is) != 0)
+ lmp->is[0] = '\0';
+ /* Left side--easy */
+ i = 0;
+ while (lmp->left[i] != '\0' && lmp->left[i] == rmp->left[i])
+ ++i;
+ lmp->left[i] = '\0';
+ /* Right side */
+ ln = strlen(lmp->right);
+ rn = strlen(rmp->right);
+ n = ln;
+ if (n > rn)
+ n = rn;
+ for (i = 0; i < n; ++i)
+ if (lmp->right[ln - i - 1] != rmp->right[rn - i - 1])
+ break;
+ for (j = 0; j < i; ++j)
+ lmp->right[j] = lmp->right[(ln - i) + j];
+ lmp->right[j] = '\0';
+ new = inboth(lmp->in, rmp->in);
+ if (new == NULL)
+ goto done;
+ freelist(lmp->in);
+ free((char *) lmp->in);
+ lmp->in = new;
+ }
+ break;
+ case PLUS:
+ if (mp <= musts)
+ goto done; /* "cannot happen" */
+ --mp;
+ mp->is[0] = '\0';
+ break;
+ case END:
+ if (mp != &musts[1])
+ goto done; /* "cannot happen" */
+ for (i = 0; musts[0].in[i] != NULL; ++i)
+ if (strlen(musts[0].in[i]) > strlen(result))
+ result = musts[0].in[i];
+ if (strcmp(result, musts[0].is) == 0)
+ exact = 1;
+ goto done;
+ case CAT:
+ if (mp < &musts[2])
+ goto done; /* "cannot happen" */
+ {
+ must *lmp;
+ must *rmp;
+
+ rmp = --mp;
+ lmp = --mp;
+ /* In. Everything in left, plus everything in
+ right, plus catenation of
+ left's right and right's left. */
+ lmp->in = addlists(lmp->in, rmp->in);
+ if (lmp->in == NULL)
+ goto done;
+ if (lmp->right[0] != '\0' &&
+ rmp->left[0] != '\0')
+ {
+ char *tp;
+
+ tp = icpyalloc(lmp->right);
+ if (tp == NULL)
+ goto done;
+ tp = icatalloc(tp, rmp->left);
+ if (tp == NULL)
+ goto done;
+ lmp->in = enlist(lmp->in, tp,
+ strlen(tp));
+ free(tp);
+ if (lmp->in == NULL)
+ goto done;
+ }
+ /* Left-hand */
+ if (lmp->is[0] != '\0')
+ {
+ lmp->left = icatalloc(lmp->left,
+ rmp->left);
+ if (lmp->left == NULL)
+ goto done;
+ }
+ /* Right-hand */
+ if (rmp->is[0] == '\0')
+ lmp->right[0] = '\0';
+ lmp->right = icatalloc(lmp->right, rmp->right);
+ if (lmp->right == NULL)
+ goto done;
+ /* Guaranteed to be */
+ if (lmp->is[0] != '\0' && rmp->is[0] != '\0')
+ {
+ lmp->is = icatalloc(lmp->is, rmp->is);
+ if (lmp->is == NULL)
+ goto done;
+ }
+ else
+ lmp->is[0] = '\0';
+ }
+ break;
+ default:
+ if (t < END)
+ {
+ /* "cannot happen" */
+ goto done;
+ }
+ else if (t == '\0')
+ {
+ /* not on *my* shift */
+ goto done;
+ }
+ else if (t >= CSET)
+ {
+ /* easy enough */
+ resetmust(mp);
+ }
+ else
+ {
+ /* plain character */
+ resetmust(mp);
+ mp->is[0] = mp->left[0] = mp->right[0] = t;
+ mp->is[1] = mp->left[1] = mp->right[1] = '\0';
+ mp->in = enlist(mp->in, mp->is, (size_t)1);
+ if (mp->in == NULL)
+ goto done;
+ }
+ break;
+ }
+#ifdef DEBUG
+ fprintf(stderr, " node: %d:", ri);
+ prtok(dfa->tokens[ri]);
+ fprintf(stderr, "\n in:");
+ for (i = 0; mp->in[i]; ++i)
+ fprintf(stderr, " \"%s\"", mp->in[i]);
+ fprintf(stderr, "\n is: \"%s\"\n", mp->is);
+ fprintf(stderr, " left: \"%s\"\n", mp->left);
+ fprintf(stderr, " right: \"%s\"\n", mp->right);
+#endif
+ ++mp;
+ }
+ done:
+ if (strlen(result))
+ {
+ dm = (struct dfamust *) malloc(sizeof (struct dfamust));
+ dm->exact = exact;
+ dm->must = malloc(strlen(result) + 1);
+ strcpy(dm->must, result);
+ dm->next = dfa->musts;
+ dfa->musts = dm;
+ }
+ mp = musts;
+ for (i = 0; i <= dfa->tindex; ++i)
+ {
+ freelist(mp[i].in);
+ ifree((char *) mp[i].in);
+ ifree(mp[i].left);
+ ifree(mp[i].right);
+ ifree(mp[i].is);
+ }
+ free((char *) mp);
+}
diff --git a/gnu/usr.bin/awk/dfa.h b/gnu/usr.bin/awk/dfa.h
new file mode 100644
index 0000000..cc27d7a
--- /dev/null
+++ b/gnu/usr.bin/awk/dfa.h
@@ -0,0 +1,360 @@
+/* dfa.h - declarations for GNU deterministic regexp compiler
+ Copyright (C) 1988 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written June, 1988 by Mike Haertel */
+
+/* FIXME:
+ 2. We should not export so much of the DFA internals.
+ In addition to clobbering modularity, we eat up valuable
+ name space. */
+
+/* Number of bits in an unsigned char. */
+#define CHARBITS 8
+
+/* First integer value that is greater than any character code. */
+#define NOTCHAR (1 << CHARBITS)
+
+/* INTBITS need not be exact, just a lower bound. */
+#define INTBITS (CHARBITS * sizeof (int))
+
+/* Number of ints required to hold a bit for every character. */
+#define CHARCLASS_INTS ((NOTCHAR + INTBITS - 1) / INTBITS)
+
+/* Sets of unsigned characters are stored as bit vectors in arrays of ints. */
+typedef int charclass[CHARCLASS_INTS];
+
+/* The regexp is parsed into an array of tokens in postfix form. Some tokens
+ are operators and others are terminal symbols. Most (but not all) of these
+ codes are returned by the lexical analyzer. */
+
+typedef enum
+{
+ END = -1, /* END is a terminal symbol that matches the
+ end of input; any value of END or less in
+ the parse tree is such a symbol. Accepting
+ states of the DFA are those that would have
+ a transition on END. */
+
+ /* Ordinary character values are terminal symbols that match themselves. */
+
+ EMPTY = NOTCHAR, /* EMPTY is a terminal symbol that matches
+ the empty string. */
+
+ BACKREF, /* BACKREF is generated by \<digit>; it
+ it not completely handled. If the scanner
+ detects a transition on backref, it returns
+ a kind of "semi-success" indicating that
+ the match will have to be verified with
+ a backtracking matcher. */
+
+ BEGLINE, /* BEGLINE is a terminal symbol that matches
+ the empty string if it is at the beginning
+ of a line. */
+
+ ENDLINE, /* ENDLINE is a terminal symbol that matches
+ the empty string if it is at the end of
+ a line. */
+
+ BEGWORD, /* BEGWORD is a terminal symbol that matches
+ the empty string if it is at the beginning
+ of a word. */
+
+ ENDWORD, /* ENDWORD is a terminal symbol that matches
+ the empty string if it is at the end of
+ a word. */
+
+ LIMWORD, /* LIMWORD is a terminal symbol that matches
+ the empty string if it is at the beginning
+ or the end of a word. */
+
+ NOTLIMWORD, /* NOTLIMWORD is a terminal symbol that
+ matches the empty string if it is not at
+ the beginning or end of a word. */
+
+ QMARK, /* QMARK is an operator of one argument that
+ matches zero or one occurences of its
+ argument. */
+
+ STAR, /* STAR is an operator of one argument that
+ matches the Kleene closure (zero or more
+ occurrences) of its argument. */
+
+ PLUS, /* PLUS is an operator of one argument that
+ matches the positive closure (one or more
+ occurrences) of its argument. */
+
+ REPMN, /* REPMN is a lexical token corresponding
+ to the {m,n} construct. REPMN never
+ appears in the compiled token vector. */
+
+ CAT, /* CAT is an operator of two arguments that
+ matches the concatenation of its
+ arguments. CAT is never returned by the
+ lexical analyzer. */
+
+ OR, /* OR is an operator of two arguments that
+ matches either of its arguments. */
+
+ ORTOP, /* OR at the toplevel in the parse tree.
+ This is used for a boyer-moore heuristic. */
+
+ LPAREN, /* LPAREN never appears in the parse tree,
+ it is only a lexeme. */
+
+ RPAREN, /* RPAREN never appears in the parse tree. */
+
+ CSET /* CSET and (and any value greater) is a
+ terminal symbol that matches any of a
+ class of characters. */
+} token;
+
+/* Sets are stored in an array in the compiled dfa; the index of the
+ array corresponding to a given set token is given by SET_INDEX(t). */
+#define SET_INDEX(t) ((t) - CSET)
+
+/* Sometimes characters can only be matched depending on the surrounding
+ context. Such context decisions depend on what the previous character
+ was, and the value of the current (lookahead) character. Context
+ dependent constraints are encoded as 8 bit integers. Each bit that
+ is set indicates that the constraint succeeds in the corresponding
+ context.
+
+ bit 7 - previous and current are newlines
+ bit 6 - previous was newline, current isn't
+ bit 5 - previous wasn't newline, current is
+ bit 4 - neither previous nor current is a newline
+ bit 3 - previous and current are word-constituents
+ bit 2 - previous was word-constituent, current isn't
+ bit 1 - previous wasn't word-constituent, current is
+ bit 0 - neither previous nor current is word-constituent
+
+ Word-constituent characters are those that satisfy isalnum().
+
+ The macro SUCCEEDS_IN_CONTEXT determines whether a a given constraint
+ succeeds in a particular context. Prevn is true if the previous character
+ was a newline, currn is true if the lookahead character is a newline.
+ Prevl and currl similarly depend upon whether the previous and current
+ characters are word-constituent letters. */
+#define MATCHES_NEWLINE_CONTEXT(constraint, prevn, currn) \
+ ((constraint) & 1 << (((prevn) ? 2 : 0) + ((currn) ? 1 : 0) + 4))
+#define MATCHES_LETTER_CONTEXT(constraint, prevl, currl) \
+ ((constraint) & 1 << (((prevl) ? 2 : 0) + ((currl) ? 1 : 0)))
+#define SUCCEEDS_IN_CONTEXT(constraint, prevn, currn, prevl, currl) \
+ (MATCHES_NEWLINE_CONTEXT(constraint, prevn, currn) \
+ && MATCHES_LETTER_CONTEXT(constraint, prevl, currl))
+
+/* The following macros give information about what a constraint depends on. */
+#define PREV_NEWLINE_DEPENDENT(constraint) \
+ (((constraint) & 0xc0) >> 2 != ((constraint) & 0x30))
+#define PREV_LETTER_DEPENDENT(constraint) \
+ (((constraint) & 0x0c) >> 2 != ((constraint) & 0x03))
+
+/* Tokens that match the empty string subject to some constraint actually
+ work by applying that constraint to determine what may follow them,
+ taking into account what has gone before. The following values are
+ the constraints corresponding to the special tokens previously defined. */
+#define NO_CONSTRAINT 0xff
+#define BEGLINE_CONSTRAINT 0xcf
+#define ENDLINE_CONSTRAINT 0xaf
+#define BEGWORD_CONSTRAINT 0xf2
+#define ENDWORD_CONSTRAINT 0xf4
+#define LIMWORD_CONSTRAINT 0xf6
+#define NOTLIMWORD_CONSTRAINT 0xf9
+
+/* States of the recognizer correspond to sets of positions in the parse
+ tree, together with the constraints under which they may be matched.
+ So a position is encoded as an index into the parse tree together with
+ a constraint. */
+typedef struct
+{
+ unsigned index; /* Index into the parse array. */
+ unsigned constraint; /* Constraint for matching this position. */
+} position;
+
+/* Sets of positions are stored as arrays. */
+typedef struct
+{
+ position *elems; /* Elements of this position set. */
+ int nelem; /* Number of elements in this set. */
+} position_set;
+
+/* A state of the dfa consists of a set of positions, some flags,
+ and the token value of the lowest-numbered position of the state that
+ contains an END token. */
+typedef struct
+{
+ int hash; /* Hash of the positions of this state. */
+ position_set elems; /* Positions this state could match. */
+ char newline; /* True if previous state matched newline. */
+ char letter; /* True if previous state matched a letter. */
+ char backref; /* True if this state matches a \<digit>. */
+ unsigned char constraint; /* Constraint for this state to accept. */
+ int first_end; /* Token value of the first END in elems. */
+} dfa_state;
+
+/* Element of a list of strings, at least one of which is known to
+ appear in any R.E. matching the DFA. */
+struct dfamust
+{
+ int exact;
+ char *must;
+ struct dfamust *next;
+};
+
+/* A compiled regular expression. */
+struct dfa
+{
+ /* Stuff built by the scanner. */
+ charclass *charclasses; /* Array of character sets for CSET tokens. */
+ int cindex; /* Index for adding new charclasses. */
+ int calloc; /* Number of charclasses currently allocated. */
+
+ /* Stuff built by the parser. */
+ token *tokens; /* Postfix parse array. */
+ int tindex; /* Index for adding new tokens. */
+ int talloc; /* Number of tokens currently allocated. */
+ int depth; /* Depth required of an evaluation stack
+ used for depth-first traversal of the
+ parse tree. */
+ int nleaves; /* Number of leaves on the parse tree. */
+ int nregexps; /* Count of parallel regexps being built
+ with dfaparse(). */
+
+ /* Stuff owned by the state builder. */
+ dfa_state *states; /* States of the dfa. */
+ int sindex; /* Index for adding new states. */
+ int salloc; /* Number of states currently allocated. */
+
+ /* Stuff built by the structure analyzer. */
+ position_set *follows; /* Array of follow sets, indexed by position
+ index. The follow of a position is the set
+ of positions containing characters that
+ could conceivably follow a character
+ matching the given position in a string
+ matching the regexp. Allocated to the
+ maximum possible position index. */
+ int searchflag; /* True if we are supposed to build a searching
+ as opposed to an exact matcher. A searching
+ matcher finds the first and shortest string
+ matching a regexp anywhere in the buffer,
+ whereas an exact matcher finds the longest
+ string matching, but anchored to the
+ beginning of the buffer. */
+
+ /* Stuff owned by the executor. */
+ int tralloc; /* Number of transition tables that have
+ slots so far. */
+ int trcount; /* Number of transition tables that have
+ actually been built. */
+ int **trans; /* Transition tables for states that can
+ never accept. If the transitions for a
+ state have not yet been computed, or the
+ state could possibly accept, its entry in
+ this table is NULL. */
+ int **realtrans; /* Trans always points to realtrans + 1; this
+ is so trans[-1] can contain NULL. */
+ int **fails; /* Transition tables after failing to accept
+ on a state that potentially could do so. */
+ int *success; /* Table of acceptance conditions used in
+ dfaexec and computed in build_state. */
+ int *newlines; /* Transitions on newlines. The entry for a
+ newline in any transition table is always
+ -1 so we can count lines without wasting
+ too many cycles. The transition for a
+ newline is stored separately and handled
+ as a special case. Newline is also used
+ as a sentinel at the end of the buffer. */
+ struct dfamust *musts; /* List of strings, at least one of which
+ is known to appear in any r.e. matching
+ the dfa. */
+};
+
+/* Some macros for user access to dfa internals. */
+
+/* ACCEPTING returns true if s could possibly be an accepting state of r. */
+#define ACCEPTING(s, r) ((r).states[s].constraint)
+
+/* ACCEPTS_IN_CONTEXT returns true if the given state accepts in the
+ specified context. */
+#define ACCEPTS_IN_CONTEXT(prevn, currn, prevl, currl, state, dfa) \
+ SUCCEEDS_IN_CONTEXT((dfa).states[state].constraint, \
+ prevn, currn, prevl, currl)
+
+/* FIRST_MATCHING_REGEXP returns the index number of the first of parallel
+ regexps that a given state could accept. Parallel regexps are numbered
+ starting at 1. */
+#define FIRST_MATCHING_REGEXP(state, dfa) (-(dfa).states[state].first_end)
+
+/* Entry points. */
+
+#ifdef __STDC__
+
+/* dfasyntax() takes two arguments; the first sets the syntax bits described
+ earlier in this file, and the second sets the case-folding flag. */
+extern void dfasyntax(reg_syntax_t, int);
+
+/* Compile the given string of the given length into the given struct dfa.
+ Final argument is a flag specifying whether to build a searching or an
+ exact matcher. */
+extern void dfacomp(char *, size_t, struct dfa *, int);
+
+/* Execute the given struct dfa on the buffer of characters. The
+ first char * points to the beginning, and the second points to the
+ first character after the end of the buffer, which must be a writable
+ place so a sentinel end-of-buffer marker can be stored there. The
+ second-to-last argument is a flag telling whether to allow newlines to
+ be part of a string matching the regexp. The next-to-last argument,
+ if non-NULL, points to a place to increment every time we see a
+ newline. The final argument, if non-NULL, points to a flag that will
+ be set if further examination by a backtracking matcher is needed in
+ order to verify backreferencing; otherwise the flag will be cleared.
+ Returns NULL if no match is found, or a pointer to the first
+ character after the first & shortest matching string in the buffer. */
+extern char *dfaexec(struct dfa *, char *, char *, int, int *, int *);
+
+/* Free the storage held by the components of a struct dfa. */
+extern void dfafree(struct dfa *);
+
+/* Entry points for people who know what they're doing. */
+
+/* Initialize the components of a struct dfa. */
+extern void dfainit(struct dfa *);
+
+/* Incrementally parse a string of given length into a struct dfa. */
+extern void dfaparse(char *, size_t, struct dfa *);
+
+/* Analyze a parsed regexp; second argument tells whether to build a searching
+ or an exact matcher. */
+extern void dfaanalyze(struct dfa *, int);
+
+/* Compute, for each possible character, the transitions out of a given
+ state, storing them in an array of integers. */
+extern void dfastate(int, struct dfa *, int []);
+
+/* Error handling. */
+
+/* dfaerror() is called by the regexp routines whenever an error occurs. It
+ takes a single argument, a NUL-terminated string describing the error.
+ The default dfaerror() prints the error message to stderr and exits.
+ The user can provide a different dfafree() if so desired. */
+extern void dfaerror(const char *);
+
+#else /* ! __STDC__ */
+extern void dfasyntax(), dfacomp(), dfafree(), dfainit(), dfaparse();
+extern void dfaanalyze(), dfastate(), dfaerror();
+extern char *dfaexec();
+#endif /* ! __STDC__ */
diff --git a/gnu/usr.bin/awk/eval.c b/gnu/usr.bin/awk/eval.c
new file mode 100644
index 0000000..18f67fd
--- /dev/null
+++ b/gnu/usr.bin/awk/eval.c
@@ -0,0 +1,1260 @@
+/*
+ * eval.c - gawk parse tree interpreter
+ */
+
+/*
+ * Copyright (C) 1986, 1988, 1989, 1991, 1992, 1993 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Progamming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GAWK; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "awk.h"
+
+extern double pow P((double x, double y));
+extern double modf P((double x, double *yp));
+extern double fmod P((double x, double y));
+
+static int eval_condition P((NODE *tree));
+static NODE *op_assign P((NODE *tree));
+static NODE *func_call P((NODE *name, NODE *arg_list));
+static NODE *match_op P((NODE *tree));
+
+NODE *_t; /* used as a temporary in macros */
+#ifdef MSDOS
+double _msc51bug; /* to get around a bug in MSC 5.1 */
+#endif
+NODE *ret_node;
+int OFSlen;
+int ORSlen;
+int OFMTidx;
+int CONVFMTidx;
+
+/* Macros and variables to save and restore function and loop bindings */
+/*
+ * the val variable allows return/continue/break-out-of-context to be
+ * caught and diagnosed
+ */
+#define PUSH_BINDING(stack, x, val) (memcpy ((char *)(stack), (char *)(x), sizeof (jmp_buf)), val++)
+#define RESTORE_BINDING(stack, x, val) (memcpy ((char *)(x), (char *)(stack), sizeof (jmp_buf)), val--)
+
+static jmp_buf loop_tag; /* always the current binding */
+static int loop_tag_valid = 0; /* nonzero when loop_tag valid */
+static int func_tag_valid = 0;
+static jmp_buf func_tag;
+extern int exiting, exit_val;
+
+/*
+ * This table is used by the regexp routines to do case independant
+ * matching. Basically, every ascii character maps to itself, except
+ * uppercase letters map to lower case ones. This table has 256
+ * entries, which may be overkill. Note also that if the system this
+ * is compiled on doesn't use 7-bit ascii, casetable[] should not be
+ * defined to the linker, so gawk should not load.
+ *
+ * Do NOT make this array static, it is used in several spots, not
+ * just in this file.
+ */
+#if 'a' == 97 /* it's ascii */
+char casetable[] = {
+ '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
+ '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
+ '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
+ '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
+ /* ' ' '!' '"' '#' '$' '%' '&' ''' */
+ '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
+ /* '(' ')' '*' '+' ',' '-' '.' '/' */
+ '\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
+ /* '0' '1' '2' '3' '4' '5' '6' '7' */
+ '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
+ /* '8' '9' ':' ';' '<' '=' '>' '?' */
+ '\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
+ /* '@' 'A' 'B' 'C' 'D' 'E' 'F' 'G' */
+ '\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+ /* 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' */
+ '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+ /* 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' */
+ '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+ /* 'X' 'Y' 'Z' '[' '\' ']' '^' '_' */
+ '\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
+ /* '`' 'a' 'b' 'c' 'd' 'e' 'f' 'g' */
+ '\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+ /* 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' */
+ '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+ /* 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' */
+ '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+ /* 'x' 'y' 'z' '{' '|' '}' '~' */
+ '\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
+ '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
+ '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
+ '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
+ '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
+ '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
+ '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
+ '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
+ '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
+ '\300', '\301', '\302', '\303', '\304', '\305', '\306', '\307',
+ '\310', '\311', '\312', '\313', '\314', '\315', '\316', '\317',
+ '\320', '\321', '\322', '\323', '\324', '\325', '\326', '\327',
+ '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337',
+ '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+ '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
+ '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
+ '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
+};
+#else
+#include "You lose. You will need a translation table for your character set."
+#endif
+
+/*
+ * Tree is a bunch of rules to run. Returns zero if it hit an exit()
+ * statement
+ */
+int
+interpret(tree)
+register NODE *volatile tree;
+{
+ jmp_buf volatile loop_tag_stack; /* shallow binding stack for loop_tag */
+ static jmp_buf rule_tag; /* tag the rule currently being run, for NEXT
+ * and EXIT statements. It is static because
+ * there are no nested rules */
+ register NODE *volatile t = NULL; /* temporary */
+ NODE **volatile lhs; /* lhs == Left Hand Side for assigns, etc */
+ NODE *volatile stable_tree;
+ int volatile traverse = 1; /* True => loop thru tree (Node_rule_list) */
+
+ /* avoid false source indications */
+ source = NULL;
+ sourceline = 0;
+
+ if (tree == NULL)
+ return 1;
+ sourceline = tree->source_line;
+ source = tree->source_file;
+ switch (tree->type) {
+ case Node_rule_node:
+ traverse = 0; /* False => one for-loop iteration only */
+ /* FALL THROUGH */
+ case Node_rule_list:
+ for (t = tree; t != NULL; t = t->rnode) {
+ if (traverse)
+ tree = t->lnode;
+ sourceline = tree->source_line;
+ source = tree->source_file;
+ switch (setjmp(rule_tag)) {
+ case 0: /* normal non-jump */
+ /* test pattern, if any */
+ if (tree->lnode == NULL ||
+ eval_condition(tree->lnode))
+ (void) interpret(tree->rnode);
+ break;
+ case TAG_CONTINUE: /* NEXT statement */
+ return 1;
+ case TAG_BREAK:
+ return 0;
+ default:
+ cant_happen();
+ }
+ if (!traverse) /* case Node_rule_node */
+ break; /* don't loop */
+ }
+ break;
+
+ case Node_statement_list:
+ for (t = tree; t != NULL; t = t->rnode)
+ (void) interpret(t->lnode);
+ break;
+
+ case Node_K_if:
+ if (eval_condition(tree->lnode)) {
+ (void) interpret(tree->rnode->lnode);
+ } else {
+ (void) interpret(tree->rnode->rnode);
+ }
+ break;
+
+ case Node_K_while:
+ PUSH_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
+
+ stable_tree = tree;
+ while (eval_condition(stable_tree->lnode)) {
+ switch (setjmp(loop_tag)) {
+ case 0: /* normal non-jump */
+ (void) interpret(stable_tree->rnode);
+ break;
+ case TAG_CONTINUE: /* continue statement */
+ break;
+ case TAG_BREAK: /* break statement */
+ RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
+ return 1;
+ default:
+ cant_happen();
+ }
+ }
+ RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
+ break;
+
+ case Node_K_do:
+ PUSH_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
+ stable_tree = tree;
+ do {
+ switch (setjmp(loop_tag)) {
+ case 0: /* normal non-jump */
+ (void) interpret(stable_tree->rnode);
+ break;
+ case TAG_CONTINUE: /* continue statement */
+ break;
+ case TAG_BREAK: /* break statement */
+ RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
+ return 1;
+ default:
+ cant_happen();
+ }
+ } while (eval_condition(stable_tree->lnode));
+ RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
+ break;
+
+ case Node_K_for:
+ PUSH_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
+ (void) interpret(tree->forloop->init);
+ stable_tree = tree;
+ while (eval_condition(stable_tree->forloop->cond)) {
+ switch (setjmp(loop_tag)) {
+ case 0: /* normal non-jump */
+ (void) interpret(stable_tree->lnode);
+ /* fall through */
+ case TAG_CONTINUE: /* continue statement */
+ (void) interpret(stable_tree->forloop->incr);
+ break;
+ case TAG_BREAK: /* break statement */
+ RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
+ return 1;
+ default:
+ cant_happen();
+ }
+ }
+ RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
+ break;
+
+ case Node_K_arrayfor:
+ {
+ volatile struct search l; /* For array_for */
+ Func_ptr after_assign = NULL;
+
+#define hakvar forloop->init
+#define arrvar forloop->incr
+ PUSH_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
+ lhs = get_lhs(tree->hakvar, &after_assign);
+ t = tree->arrvar;
+ if (t->type == Node_param_list)
+ t = stack_ptr[t->param_cnt];
+ stable_tree = tree;
+ for (assoc_scan(t, (struct search *)&l);
+ l.retval;
+ assoc_next((struct search *)&l)) {
+ unref(*((NODE **) lhs));
+ *lhs = dupnode(l.retval);
+ if (after_assign)
+ (*after_assign)();
+ switch (setjmp(loop_tag)) {
+ case 0:
+ (void) interpret(stable_tree->lnode);
+ case TAG_CONTINUE:
+ break;
+
+ case TAG_BREAK:
+ RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
+ return 1;
+ default:
+ cant_happen();
+ }
+ }
+ RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
+ break;
+ }
+
+ case Node_K_break:
+ if (loop_tag_valid == 0)
+ fatal("unexpected break");
+ longjmp(loop_tag, TAG_BREAK);
+ break;
+
+ case Node_K_continue:
+ if (loop_tag_valid == 0) {
+ /*
+ * AT&T nawk treats continue outside of loops like
+ * next. Allow it if not posix, and complain if
+ * lint.
+ */
+ static int warned = 0;
+
+ if (do_lint && ! warned) {
+ warning("use of `continue' outside of loop is not portable");
+ warned = 1;
+ }
+ if (do_posix)
+ fatal("use of `continue' outside of loop is not allowed");
+ longjmp(rule_tag, TAG_CONTINUE);
+ } else
+ longjmp(loop_tag, TAG_CONTINUE);
+ break;
+
+ case Node_K_print:
+ do_print(tree);
+ break;
+
+ case Node_K_printf:
+ do_printf(tree);
+ break;
+
+ case Node_K_delete:
+ if (tree->rnode != NULL)
+ do_delete(tree->lnode, tree->rnode);
+ else
+ assoc_clear(tree->lnode);
+ break;
+
+ case Node_K_next:
+ longjmp(rule_tag, TAG_CONTINUE);
+ break;
+
+ case Node_K_nextfile:
+ do_nextfile();
+ break;
+
+ case Node_K_exit:
+ /*
+ * In A,K,&W, p. 49, it says that an exit statement "...
+ * causes the program to behave as if the end of input had
+ * occurred; no more input is read, and the END actions, if
+ * any are executed." This implies that the rest of the rules
+ * are not done. So we immediately break out of the main loop.
+ */
+ exiting = 1;
+ if (tree) {
+ t = tree_eval(tree->lnode);
+ exit_val = (int) force_number(t);
+ }
+ free_temp(t);
+ longjmp(rule_tag, TAG_BREAK);
+ break;
+
+ case Node_K_return:
+ t = tree_eval(tree->lnode);
+ ret_node = dupnode(t);
+ free_temp(t);
+ longjmp(func_tag, TAG_RETURN);
+ break;
+
+ default:
+ /*
+ * Appears to be an expression statement. Throw away the
+ * value.
+ */
+ if (do_lint && tree->type == Node_var)
+ warning("statement has no effect");
+ t = tree_eval(tree);
+ free_temp(t);
+ break;
+ }
+ return 1;
+}
+
+/* evaluate a subtree */
+
+NODE *
+r_tree_eval(tree)
+register NODE *tree;
+{
+ register NODE *r, *t1, *t2; /* return value & temporary subtrees */
+ register NODE **lhs;
+ register int di;
+ AWKNUM x, x1, x2;
+ long lx;
+#ifdef _CRAY
+ long lx2;
+#endif
+
+#ifdef DEBUG
+ if (tree == NULL)
+ return Nnull_string;
+ if (tree->type == Node_val) {
+ if ((char)tree->stref <= 0) cant_happen();
+ return tree;
+ }
+ if (tree->type == Node_var) {
+ if ((char)tree->var_value->stref <= 0) cant_happen();
+ return tree->var_value;
+ }
+#endif
+
+ if (tree->type == Node_param_list) {
+ tree = stack_ptr[tree->param_cnt];
+ if (tree == NULL)
+ return Nnull_string;
+ }
+
+ switch (tree->type) {
+ case Node_var:
+ return tree->var_value;
+
+ case Node_and:
+ return tmp_number((AWKNUM) (eval_condition(tree->lnode)
+ && eval_condition(tree->rnode)));
+
+ case Node_or:
+ return tmp_number((AWKNUM) (eval_condition(tree->lnode)
+ || eval_condition(tree->rnode)));
+
+ case Node_not:
+ return tmp_number((AWKNUM) ! eval_condition(tree->lnode));
+
+ /* Builtins */
+ case Node_builtin:
+ return ((*tree->proc) (tree->subnode));
+
+ case Node_K_getline:
+ return (do_getline(tree));
+
+ case Node_in_array:
+ return tmp_number((AWKNUM) in_array(tree->lnode, tree->rnode));
+
+ case Node_func_call:
+ return func_call(tree->rnode, tree->lnode);
+
+ /* unary operations */
+ case Node_NR:
+ case Node_FNR:
+ case Node_NF:
+ case Node_FIELDWIDTHS:
+ case Node_FS:
+ case Node_RS:
+ case Node_field_spec:
+ case Node_subscript:
+ case Node_IGNORECASE:
+ case Node_OFS:
+ case Node_ORS:
+ case Node_OFMT:
+ case Node_CONVFMT:
+ lhs = get_lhs(tree, (Func_ptr *)0);
+ return *lhs;
+
+ case Node_var_array:
+ fatal("attempt to use array `%s' in a scalar context", tree->vname);
+
+ case Node_unary_minus:
+ t1 = tree_eval(tree->subnode);
+ x = -force_number(t1);
+ free_temp(t1);
+ return tmp_number(x);
+
+ case Node_cond_exp:
+ if (eval_condition(tree->lnode))
+ return tree_eval(tree->rnode->lnode);
+ return tree_eval(tree->rnode->rnode);
+
+ case Node_match:
+ case Node_nomatch:
+ case Node_regex:
+ return match_op(tree);
+
+ case Node_func:
+ fatal("function `%s' called with space between name and (,\n%s",
+ tree->lnode->param,
+ "or used in other expression context");
+
+ /* assignments */
+ case Node_assign:
+ {
+ Func_ptr after_assign = NULL;
+
+ r = tree_eval(tree->rnode);
+ lhs = get_lhs(tree->lnode, &after_assign);
+ if (r != *lhs) {
+ NODE *save;
+
+ save = *lhs;
+ *lhs = dupnode(r);
+ unref(save);
+ }
+ free_temp(r);
+ if (after_assign)
+ (*after_assign)();
+ return *lhs;
+ }
+
+ case Node_concat:
+ {
+#define STACKSIZE 10
+ NODE *treelist[STACKSIZE+1];
+ NODE *strlist[STACKSIZE+1];
+ register NODE **treep;
+ register NODE **strp;
+ register size_t len;
+ char *str;
+ register char *dest;
+
+ /*
+ * This is an efficiency hack for multiple adjacent string
+ * concatenations, to avoid recursion and string copies.
+ *
+ * Node_concat trees grow downward to the left, so
+ * descend to lowest (first) node, accumulating nodes
+ * to evaluate to strings as we go.
+ */
+ treep = treelist;
+ while (tree->type == Node_concat) {
+ *treep++ = tree->rnode;
+ tree = tree->lnode;
+ if (treep == &treelist[STACKSIZE])
+ break;
+ }
+ *treep = tree;
+ /*
+ * Now, evaluate to strings in LIFO order, accumulating
+ * the string length, so we can do a single malloc at the
+ * end.
+ */
+ strp = strlist;
+ len = 0;
+ while (treep >= treelist) {
+ *strp = force_string(tree_eval(*treep--));
+ len += (*strp)->stlen;
+ strp++;
+ }
+ *strp = NULL;
+ emalloc(str, char *, len+2, "tree_eval");
+ str[len] = str[len+1] = '\0'; /* for good measure */
+ dest = str;
+ strp = strlist;
+ while (*strp) {
+ memcpy(dest, (*strp)->stptr, (*strp)->stlen);
+ dest += (*strp)->stlen;
+ free_temp(*strp);
+ strp++;
+ }
+ r = make_str_node(str, len, ALREADY_MALLOCED);
+ r->flags |= TEMP;
+ }
+ return r;
+
+ /* other assignment types are easier because they are numeric */
+ case Node_preincrement:
+ case Node_predecrement:
+ case Node_postincrement:
+ case Node_postdecrement:
+ case Node_assign_exp:
+ case Node_assign_times:
+ case Node_assign_quotient:
+ case Node_assign_mod:
+ case Node_assign_plus:
+ case Node_assign_minus:
+ return op_assign(tree);
+ default:
+ break; /* handled below */
+ }
+
+ /* evaluate subtrees in order to do binary operation, then keep going */
+ t1 = tree_eval(tree->lnode);
+ t2 = tree_eval(tree->rnode);
+
+ switch (tree->type) {
+ case Node_geq:
+ case Node_leq:
+ case Node_greater:
+ case Node_less:
+ case Node_notequal:
+ case Node_equal:
+ di = cmp_nodes(t1, t2);
+ free_temp(t1);
+ free_temp(t2);
+ switch (tree->type) {
+ case Node_equal:
+ return tmp_number((AWKNUM) (di == 0));
+ case Node_notequal:
+ return tmp_number((AWKNUM) (di != 0));
+ case Node_less:
+ return tmp_number((AWKNUM) (di < 0));
+ case Node_greater:
+ return tmp_number((AWKNUM) (di > 0));
+ case Node_leq:
+ return tmp_number((AWKNUM) (di <= 0));
+ case Node_geq:
+ return tmp_number((AWKNUM) (di >= 0));
+ default:
+ cant_happen();
+ }
+ break;
+ default:
+ break; /* handled below */
+ }
+
+ x1 = force_number(t1);
+ free_temp(t1);
+ x2 = force_number(t2);
+ free_temp(t2);
+ switch (tree->type) {
+ case Node_exp:
+ if ((lx = x2) == x2 && lx >= 0) { /* integer exponent */
+ if (lx == 0)
+ x = 1;
+ else if (lx == 1)
+ x = x1;
+ else {
+ /* doing it this way should be more precise */
+ for (x = x1; --lx; )
+ x *= x1;
+ }
+ } else
+ x = pow((double) x1, (double) x2);
+ return tmp_number(x);
+
+ case Node_times:
+ return tmp_number(x1 * x2);
+
+ case Node_quotient:
+ if (x2 == 0)
+ fatal("division by zero attempted");
+#ifdef _CRAY
+ /*
+ * special case for integer division, put in for Cray
+ */
+ lx2 = x2;
+ if (lx2 == 0)
+ return tmp_number(x1 / x2);
+ lx = (long) x1 / lx2;
+ if (lx * x2 == x1)
+ return tmp_number((AWKNUM) lx);
+ else
+#endif
+ return tmp_number(x1 / x2);
+
+ case Node_mod:
+ if (x2 == 0)
+ fatal("division by zero attempted in mod");
+#ifndef FMOD_MISSING
+ return tmp_number(fmod (x1, x2));
+#else
+ (void) modf(x1 / x2, &x);
+ return tmp_number(x1 - x * x2);
+#endif
+
+ case Node_plus:
+ return tmp_number(x1 + x2);
+
+ case Node_minus:
+ return tmp_number(x1 - x2);
+
+ case Node_var_array:
+ fatal("attempt to use array `%s' in a scalar context", tree->vname);
+
+ default:
+ fatal("illegal type (%d) in tree_eval", tree->type);
+ }
+ return 0;
+}
+
+/* Is TREE true or false? Returns 0==false, non-zero==true */
+static int
+eval_condition(tree)
+register NODE *tree;
+{
+ register NODE *t1;
+ register int ret;
+
+ if (tree == NULL) /* Null trees are the easiest kinds */
+ return 1;
+ if (tree->type == Node_line_range) {
+ /*
+ * Node_line_range is kind of like Node_match, EXCEPT: the
+ * lnode field (more properly, the condpair field) is a node
+ * of a Node_cond_pair; whether we evaluate the lnode of that
+ * node or the rnode depends on the triggered word. More
+ * precisely: if we are not yet triggered, we tree_eval the
+ * lnode; if that returns true, we set the triggered word.
+ * If we are triggered (not ELSE IF, note), we tree_eval the
+ * rnode, clear triggered if it succeeds, and perform our
+ * action (regardless of success or failure). We want to be
+ * able to begin and end on a single input record, so this
+ * isn't an ELSE IF, as noted above.
+ */
+ if (!tree->triggered)
+ if (!eval_condition(tree->condpair->lnode))
+ return 0;
+ else
+ tree->triggered = 1;
+ /* Else we are triggered */
+ if (eval_condition(tree->condpair->rnode))
+ tree->triggered = 0;
+ return 1;
+ }
+
+ /*
+ * Could just be J.random expression. in which case, null and 0 are
+ * false, anything else is true
+ */
+
+ t1 = tree_eval(tree);
+ if (t1->flags & MAYBE_NUM)
+ (void) force_number(t1);
+ if (t1->flags & NUMBER)
+ ret = t1->numbr != 0.0;
+ else
+ ret = t1->stlen != 0;
+ free_temp(t1);
+ return ret;
+}
+
+/*
+ * compare two nodes, returning negative, 0, positive
+ */
+int
+cmp_nodes(t1, t2)
+register NODE *t1, *t2;
+{
+ register int ret;
+ register size_t len1, len2;
+
+ if (t1 == t2)
+ return 0;
+ if (t1->flags & MAYBE_NUM)
+ (void) force_number(t1);
+ if (t2->flags & MAYBE_NUM)
+ (void) force_number(t2);
+ if ((t1->flags & NUMBER) && (t2->flags & NUMBER)) {
+ if (t1->numbr == t2->numbr) return 0;
+ else if (t1->numbr - t2->numbr < 0) return -1;
+ else return 1;
+ }
+ (void) force_string(t1);
+ (void) force_string(t2);
+ len1 = t1->stlen;
+ len2 = t2->stlen;
+ if (len1 == 0 || len2 == 0)
+ return len1 - len2;
+ ret = memcmp(t1->stptr, t2->stptr, len1 <= len2 ? len1 : len2);
+ return ret == 0 ? len1-len2 : ret;
+}
+
+static NODE *
+op_assign(tree)
+register NODE *tree;
+{
+ AWKNUM rval, lval;
+ NODE **lhs;
+ AWKNUM t1, t2;
+ long ltemp;
+ NODE *tmp;
+ Func_ptr after_assign = NULL;
+
+ lhs = get_lhs(tree->lnode, &after_assign);
+ lval = force_number(*lhs);
+
+ /*
+ * Can't unref *lhs until we know the type; doing so
+ * too early breaks x += x sorts of things.
+ */
+ switch(tree->type) {
+ case Node_preincrement:
+ case Node_predecrement:
+ unref(*lhs);
+ *lhs = make_number(lval +
+ (tree->type == Node_preincrement ? 1.0 : -1.0));
+ if (after_assign)
+ (*after_assign)();
+ return *lhs;
+
+ case Node_postincrement:
+ case Node_postdecrement:
+ unref(*lhs);
+ *lhs = make_number(lval +
+ (tree->type == Node_postincrement ? 1.0 : -1.0));
+ if (after_assign)
+ (*after_assign)();
+ return tmp_number(lval);
+ default:
+ break; /* handled below */
+ }
+
+ tmp = tree_eval(tree->rnode);
+ rval = force_number(tmp);
+ free_temp(tmp);
+ unref(*lhs);
+ switch(tree->type) {
+ case Node_assign_exp:
+ if ((ltemp = rval) == rval) { /* integer exponent */
+ if (ltemp == 0)
+ *lhs = make_number((AWKNUM) 1);
+ else if (ltemp == 1)
+ *lhs = make_number(lval);
+ else {
+ /* doing it this way should be more precise */
+ for (t1 = t2 = lval; --ltemp; )
+ t1 *= t2;
+ *lhs = make_number(t1);
+ }
+ } else
+ *lhs = make_number((AWKNUM) pow((double) lval, (double) rval));
+ break;
+
+ case Node_assign_times:
+ *lhs = make_number(lval * rval);
+ break;
+
+ case Node_assign_quotient:
+ if (rval == (AWKNUM) 0)
+ fatal("division by zero attempted in /=");
+#ifdef _CRAY
+ /*
+ * special case for integer division, put in for Cray
+ */
+ ltemp = rval;
+ if (ltemp == 0) {
+ *lhs = make_number(lval / rval);
+ break;
+ }
+ ltemp = (long) lval / ltemp;
+ if (ltemp * lval == rval)
+ *lhs = make_number((AWKNUM) ltemp);
+ else
+#endif
+ *lhs = make_number(lval / rval);
+ break;
+
+ case Node_assign_mod:
+ if (rval == (AWKNUM) 0)
+ fatal("division by zero attempted in %=");
+#ifndef FMOD_MISSING
+ *lhs = make_number(fmod(lval, rval));
+#else
+ (void) modf(lval / rval, &t1);
+ t2 = lval - rval * t1;
+ *lhs = make_number(t2);
+#endif
+ break;
+
+ case Node_assign_plus:
+ *lhs = make_number(lval + rval);
+ break;
+
+ case Node_assign_minus:
+ *lhs = make_number(lval - rval);
+ break;
+ default:
+ cant_happen();
+ }
+ if (after_assign)
+ (*after_assign)();
+ return *lhs;
+}
+
+NODE **stack_ptr;
+
+static NODE *
+func_call(name, arg_list)
+NODE *name; /* name is a Node_val giving function name */
+NODE *arg_list; /* Node_expression_list of calling args. */
+{
+ register NODE *arg, *argp, *r;
+ NODE *n, *f;
+ jmp_buf volatile func_tag_stack;
+ jmp_buf volatile loop_tag_stack;
+ int volatile save_loop_tag_valid = 0;
+ NODE **volatile save_stack, *save_ret_node;
+ NODE **volatile local_stack = NULL, **sp;
+ int count;
+ extern NODE *ret_node;
+
+ /*
+ * retrieve function definition node
+ */
+ f = lookup(name->stptr);
+ if (!f || f->type != Node_func)
+ fatal("function `%s' not defined", name->stptr);
+#ifdef FUNC_TRACE
+ fprintf(stderr, "function %s called\n", name->stptr);
+#endif
+ count = f->lnode->param_cnt;
+ if (count)
+ emalloc(local_stack, NODE **, count*sizeof(NODE *), "func_call");
+ sp = local_stack;
+
+ /*
+ * for each calling arg. add NODE * on stack
+ */
+ for (argp = arg_list; count && argp != NULL; argp = argp->rnode) {
+ arg = argp->lnode;
+ getnode(r);
+ r->type = Node_var;
+ /*
+ * call by reference for arrays; see below also
+ */
+ if (arg->type == Node_param_list)
+ arg = stack_ptr[arg->param_cnt];
+ if (arg->type == Node_var_array)
+ *r = *arg;
+ else {
+ n = tree_eval(arg);
+ r->lnode = dupnode(n);
+ r->rnode = (NODE *) NULL;
+ free_temp(n);
+ }
+ *sp++ = r;
+ count--;
+ }
+ if (argp != NULL) /* left over calling args. */
+ warning(
+ "function `%s' called with more arguments than declared",
+ name->stptr);
+ /*
+ * add remaining params. on stack with null value
+ */
+ while (count-- > 0) {
+ getnode(r);
+ r->type = Node_var;
+ r->lnode = Nnull_string;
+ r->rnode = (NODE *) NULL;
+ *sp++ = r;
+ }
+
+ /*
+ * Execute function body, saving context, as a return statement
+ * will longjmp back here.
+ *
+ * Have to save and restore the loop_tag stuff so that a return
+ * inside a loop in a function body doesn't scrog any loops going
+ * on in the main program. We save the necessary info in variables
+ * local to this function so that function nesting works OK.
+ * We also only bother to save the loop stuff if we're in a loop
+ * when the function is called.
+ */
+ if (loop_tag_valid) {
+ int junk = 0;
+
+ save_loop_tag_valid = (volatile int) loop_tag_valid;
+ PUSH_BINDING(loop_tag_stack, loop_tag, junk);
+ loop_tag_valid = 0;
+ }
+ save_stack = stack_ptr;
+ stack_ptr = local_stack;
+ PUSH_BINDING(func_tag_stack, func_tag, func_tag_valid);
+ save_ret_node = ret_node;
+ ret_node = Nnull_string; /* default return value */
+ if (setjmp(func_tag) == 0)
+ (void) interpret(f->rnode);
+
+ r = ret_node;
+ ret_node = (NODE *) save_ret_node;
+ RESTORE_BINDING(func_tag_stack, func_tag, func_tag_valid);
+ stack_ptr = (NODE **) save_stack;
+
+ /*
+ * here, we pop each parameter and check whether
+ * it was an array. If so, and if the arg. passed in was
+ * a simple variable, then the value should be copied back.
+ * This achieves "call-by-reference" for arrays.
+ */
+ sp = local_stack;
+ count = f->lnode->param_cnt;
+ for (argp = arg_list; count > 0 && argp != NULL; argp = argp->rnode) {
+ arg = argp->lnode;
+ if (arg->type == Node_param_list)
+ arg = stack_ptr[arg->param_cnt];
+ n = *sp++;
+ if ((arg->type == Node_var || arg->type == Node_var_array)
+ && n->type == Node_var_array) {
+ /* should we free arg->var_value ? */
+ arg->var_array = n->var_array;
+ arg->type = Node_var_array;
+ arg->array_size = n->array_size;
+ arg->table_size = n->table_size;
+ arg->flags = n->flags;
+ }
+ /* n->lnode overlays the array size, don't unref it if array */
+ if (n->type != Node_var_array)
+ unref(n->lnode);
+ freenode(n);
+ count--;
+ }
+ while (count-- > 0) {
+ n = *sp++;
+ /* if n is an (local) array, all the elements should be freed */
+ if (n->type == Node_var_array)
+ assoc_clear(n);
+ unref(n->lnode);
+ freenode(n);
+ }
+ if (local_stack)
+ free((char *) local_stack);
+
+ /* Restore the loop_tag stuff if necessary. */
+ if (save_loop_tag_valid) {
+ int junk = 0;
+
+ loop_tag_valid = (int) save_loop_tag_valid;
+ RESTORE_BINDING(loop_tag_stack, loop_tag, junk);
+ }
+
+ if (!(r->flags & PERM))
+ r->flags |= TEMP;
+ return r;
+}
+
+/*
+ * This returns a POINTER to a node pointer. get_lhs(ptr) is the current
+ * value of the var, or where to store the var's new value
+ */
+
+NODE **
+r_get_lhs(ptr, assign)
+register NODE *ptr;
+Func_ptr *assign;
+{
+ register NODE **aptr = NULL;
+ register NODE *n;
+
+ switch (ptr->type) {
+ case Node_var_array:
+ fatal("attempt to use array `%s' in a scalar context", ptr->vname);
+ case Node_var:
+ aptr = &(ptr->var_value);
+#ifdef DEBUG
+ if ((char)ptr->var_value->stref <= 0)
+ cant_happen();
+#endif
+ break;
+
+ case Node_FIELDWIDTHS:
+ aptr = &(FIELDWIDTHS_node->var_value);
+ if (assign)
+ *assign = set_FIELDWIDTHS;
+ break;
+
+ case Node_RS:
+ aptr = &(RS_node->var_value);
+ if (assign)
+ *assign = set_RS;
+ break;
+
+ case Node_FS:
+ aptr = &(FS_node->var_value);
+ if (assign)
+ *assign = set_FS;
+ break;
+
+ case Node_FNR:
+ unref(FNR_node->var_value);
+ FNR_node->var_value = make_number((AWKNUM) FNR);
+ aptr = &(FNR_node->var_value);
+ if (assign)
+ *assign = set_FNR;
+ break;
+
+ case Node_NR:
+ unref(NR_node->var_value);
+ NR_node->var_value = make_number((AWKNUM) NR);
+ aptr = &(NR_node->var_value);
+ if (assign)
+ *assign = set_NR;
+ break;
+
+ case Node_NF:
+ if (NF == -1)
+ (void) get_field(HUGE-1, assign); /* parse record */
+ unref(NF_node->var_value);
+ NF_node->var_value = make_number((AWKNUM) NF);
+ aptr = &(NF_node->var_value);
+ if (assign)
+ *assign = set_NF;
+ break;
+
+ case Node_IGNORECASE:
+ unref(IGNORECASE_node->var_value);
+ IGNORECASE_node->var_value = make_number((AWKNUM) IGNORECASE);
+ aptr = &(IGNORECASE_node->var_value);
+ if (assign)
+ *assign = set_IGNORECASE;
+ break;
+
+ case Node_OFMT:
+ aptr = &(OFMT_node->var_value);
+ if (assign)
+ *assign = set_OFMT;
+ break;
+
+ case Node_CONVFMT:
+ aptr = &(CONVFMT_node->var_value);
+ if (assign)
+ *assign = set_CONVFMT;
+ break;
+
+ case Node_ORS:
+ aptr = &(ORS_node->var_value);
+ if (assign)
+ *assign = set_ORS;
+ break;
+
+ case Node_OFS:
+ aptr = &(OFS_node->var_value);
+ if (assign)
+ *assign = set_OFS;
+ break;
+
+ case Node_param_list:
+ aptr = &(stack_ptr[ptr->param_cnt]->var_value);
+ break;
+
+ case Node_field_spec:
+ {
+ int field_num;
+
+ n = tree_eval(ptr->lnode);
+ field_num = (int) force_number(n);
+ free_temp(n);
+ if (field_num < 0)
+ fatal("attempt to access field %d", field_num);
+ if (field_num == 0 && field0_valid) { /* short circuit */
+ aptr = &fields_arr[0];
+ if (assign)
+ *assign = reset_record;
+ break;
+ }
+ aptr = get_field(field_num, assign);
+ break;
+ }
+ case Node_subscript:
+ n = ptr->lnode;
+ if (n->type == Node_param_list)
+ n = stack_ptr[n->param_cnt];
+ aptr = assoc_lookup(n, concat_exp(ptr->rnode));
+ break;
+
+ case Node_func:
+ fatal ("`%s' is a function, assignment is not allowed",
+ ptr->lnode->param);
+ default:
+ cant_happen();
+ }
+ return aptr;
+}
+
+static NODE *
+match_op(tree)
+register NODE *tree;
+{
+ register NODE *t1;
+ register Regexp *rp;
+ int i;
+ int match = 1;
+
+ if (tree->type == Node_nomatch)
+ match = 0;
+ if (tree->type == Node_regex)
+ t1 = *get_field(0, (Func_ptr *) 0);
+ else {
+ t1 = force_string(tree_eval(tree->lnode));
+ tree = tree->rnode;
+ }
+ rp = re_update(tree);
+ i = research(rp, t1->stptr, 0, t1->stlen, 0);
+ i = (i == -1) ^ (match == 1);
+ free_temp(t1);
+ return tmp_number((AWKNUM) i);
+}
+
+void
+set_IGNORECASE()
+{
+ static int warned = 0;
+
+ if ((do_lint || do_unix) && ! warned) {
+ warned = 1;
+ warning("IGNORECASE not supported in compatibility mode");
+ }
+ IGNORECASE = (force_number(IGNORECASE_node->var_value) != 0.0);
+ set_FS();
+}
+
+void
+set_OFS()
+{
+ OFS = force_string(OFS_node->var_value)->stptr;
+ OFSlen = OFS_node->var_value->stlen;
+ OFS[OFSlen] = '\0';
+}
+
+void
+set_ORS()
+{
+ ORS = force_string(ORS_node->var_value)->stptr;
+ ORSlen = ORS_node->var_value->stlen;
+ ORS[ORSlen] = '\0';
+}
+
+NODE **fmt_list = NULL;
+static int fmt_ok P((NODE *n));
+static int fmt_index P((NODE *n));
+
+static int
+fmt_ok(n)
+NODE *n;
+{
+ /* to be done later */
+ return 1;
+}
+
+static int
+fmt_index(n)
+NODE *n;
+{
+ register int ix = 0;
+ static int fmt_num = 4;
+ static int fmt_hiwater = 0;
+
+ if (fmt_list == NULL)
+ emalloc(fmt_list, NODE **, fmt_num*sizeof(*fmt_list), "fmt_index");
+ (void) force_string(n);
+ while (ix < fmt_hiwater) {
+ if (cmp_nodes(fmt_list[ix], n) == 0)
+ return ix;
+ ix++;
+ }
+ /* not found */
+ n->stptr[n->stlen] = '\0';
+ if (!fmt_ok(n))
+ warning("bad FMT specification");
+ if (fmt_hiwater >= fmt_num) {
+ fmt_num *= 2;
+ emalloc(fmt_list, NODE **, fmt_num, "fmt_index");
+ }
+ fmt_list[fmt_hiwater] = dupnode(n);
+ return fmt_hiwater++;
+}
+
+void
+set_OFMT()
+{
+ OFMTidx = fmt_index(OFMT_node->var_value);
+ OFMT = fmt_list[OFMTidx]->stptr;
+}
+
+void
+set_CONVFMT()
+{
+ CONVFMTidx = fmt_index(CONVFMT_node->var_value);
+ CONVFMT = fmt_list[CONVFMTidx]->stptr;
+}
diff --git a/gnu/usr.bin/awk/field.c b/gnu/usr.bin/awk/field.c
new file mode 100644
index 0000000..17dce9b
--- /dev/null
+++ b/gnu/usr.bin/awk/field.c
@@ -0,0 +1,678 @@
+/*
+ * field.c - routines for dealing with fields and record parsing
+ */
+
+/*
+ * Copyright (C) 1986, 1988, 1989, 1991, 1992, 1993 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Progamming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GAWK; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "awk.h"
+
+typedef void (* Setfunc) P((int, char*, int, NODE *));
+
+static long (*parse_field) P((int, char **, int, NODE *,
+ Regexp *, Setfunc, NODE *));
+static void rebuild_record P((void));
+static long re_parse_field P((int, char **, int, NODE *,
+ Regexp *, Setfunc, NODE *));
+static long def_parse_field P((int, char **, int, NODE *,
+ Regexp *, Setfunc, NODE *));
+static long sc_parse_field P((int, char **, int, NODE *,
+ Regexp *, Setfunc, NODE *));
+static long fw_parse_field P((int, char **, int, NODE *,
+ Regexp *, Setfunc, NODE *));
+static void set_element P((int, char *, int, NODE *));
+static void grow_fields_arr P((long num));
+static void set_field P((int num, char *str, int len, NODE *dummy));
+
+
+static Regexp *FS_regexp = NULL;
+static char *parse_extent; /* marks where to restart parse of record */
+static long parse_high_water=0; /* field number that we have parsed so far */
+static long nf_high_water = 0; /* size of fields_arr */
+static int resave_fs;
+static NODE *save_FS; /* save current value of FS when line is read,
+ * to be used in deferred parsing
+ */
+
+NODE **fields_arr; /* array of pointers to the field nodes */
+int field0_valid; /* $(>0) has not been changed yet */
+int default_FS;
+static NODE **nodes; /* permanent repository of field nodes */
+static int *FIELDWIDTHS = NULL;
+
+void
+init_fields()
+{
+ NODE *n;
+
+ emalloc(fields_arr, NODE **, sizeof(NODE *), "init_fields");
+ emalloc(nodes, NODE **, sizeof(NODE *), "init_fields");
+ getnode(n);
+ *n = *Nnull_string;
+ fields_arr[0] = nodes[0] = n;
+ parse_extent = fields_arr[0]->stptr;
+ save_FS = dupnode(FS_node->var_value);
+ field0_valid = 1;
+}
+
+
+static void
+grow_fields_arr(num)
+long num;
+{
+ register int t;
+ register NODE *n;
+
+ erealloc(fields_arr, NODE **, (num + 1) * sizeof(NODE *), "set_field");
+ erealloc(nodes, NODE **, (num+1) * sizeof(NODE *), "set_field");
+ for (t = nf_high_water+1; t <= num; t++) {
+ getnode(n);
+ *n = *Nnull_string;
+ fields_arr[t] = nodes[t] = n;
+ }
+ nf_high_water = num;
+}
+
+/*ARGSUSED*/
+static void
+set_field(num, str, len, dummy)
+int num;
+char *str;
+int len;
+NODE *dummy; /* not used -- just to make interface same as set_element */
+{
+ register NODE *n;
+
+ if (num > nf_high_water)
+ grow_fields_arr(num);
+ n = nodes[num];
+ n->stptr = str;
+ n->stlen = len;
+ n->flags = (PERM|STR|STRING|MAYBE_NUM);
+ fields_arr[num] = n;
+}
+
+/* Someone assigned a value to $(something). Fix up $0 to be right */
+static void
+rebuild_record()
+{
+ register size_t tlen;
+ register NODE *tmp;
+ NODE *ofs;
+ char *ops;
+ register char *cops;
+ register NODE **ptr;
+ register size_t ofslen;
+
+ tlen = 0;
+ ofs = force_string(OFS_node->var_value);
+ ofslen = ofs->stlen;
+ ptr = &fields_arr[NF];
+ while (ptr > &fields_arr[0]) {
+ tmp = force_string(*ptr);
+ tlen += tmp->stlen;
+ ptr--;
+ }
+ tlen += (NF - 1) * ofslen;
+ if ((long)tlen < 0)
+ tlen = 0;
+ emalloc(ops, char *, tlen + 2, "rebuild_record");
+ cops = ops;
+ ops[0] = '\0';
+ for (ptr = &fields_arr[1]; ptr <= &fields_arr[NF]; ptr++) {
+ tmp = *ptr;
+ if (tmp->stlen == 1)
+ *cops++ = tmp->stptr[0];
+ else if (tmp->stlen != 0) {
+ memcpy(cops, tmp->stptr, tmp->stlen);
+ cops += tmp->stlen;
+ }
+ if (ptr != &fields_arr[NF]) {
+ if (ofslen == 1)
+ *cops++ = ofs->stptr[0];
+ else if (ofslen != 0) {
+ memcpy(cops, ofs->stptr, ofslen);
+ cops += ofslen;
+ }
+ }
+ }
+ tmp = make_str_node(ops, tlen, ALREADY_MALLOCED);
+ unref(fields_arr[0]);
+ fields_arr[0] = tmp;
+ field0_valid = 1;
+}
+
+/*
+ * setup $0, but defer parsing rest of line until reference is made to $(>0)
+ * or to NF. At that point, parse only as much as necessary.
+ */
+void
+set_record(buf, cnt, freeold)
+char *buf;
+int cnt;
+int freeold;
+{
+ register int i;
+
+ NF = -1;
+ for (i = 1; i <= parse_high_water; i++) {
+ unref(fields_arr[i]);
+ }
+ parse_high_water = 0;
+ if (freeold) {
+ unref(fields_arr[0]);
+ if (resave_fs) {
+ resave_fs = 0;
+ unref(save_FS);
+ save_FS = dupnode(FS_node->var_value);
+ }
+ nodes[0]->stptr = buf;
+ nodes[0]->stlen = cnt;
+ nodes[0]->stref = 1;
+ nodes[0]->flags = (STRING|STR|PERM|MAYBE_NUM);
+ fields_arr[0] = nodes[0];
+ }
+ fields_arr[0]->flags |= MAYBE_NUM;
+ field0_valid = 1;
+}
+
+void
+reset_record()
+{
+ (void) force_string(fields_arr[0]);
+ set_record(fields_arr[0]->stptr, fields_arr[0]->stlen, 0);
+}
+
+void
+set_NF()
+{
+ register int i;
+
+ NF = (long) force_number(NF_node->var_value);
+ if (NF > nf_high_water)
+ grow_fields_arr(NF);
+ for (i = parse_high_water + 1; i <= NF; i++) {
+ unref(fields_arr[i]);
+ fields_arr[i] = Nnull_string;
+ }
+ field0_valid = 0;
+}
+
+/*
+ * this is called both from get_field() and from do_split()
+ * via (*parse_field)(). This variation is for when FS is a regular
+ * expression -- either user-defined or because RS=="" and FS==" "
+ */
+static long
+re_parse_field(up_to, buf, len, fs, rp, set, n)
+int up_to; /* parse only up to this field number */
+char **buf; /* on input: string to parse; on output: point to start next */
+int len;
+NODE *fs;
+Regexp *rp;
+Setfunc set; /* routine to set the value of the parsed field */
+NODE *n;
+{
+ register char *scan = *buf;
+ register int nf = parse_high_water;
+ register char *field;
+ register char *end = scan + len;
+
+ if (up_to == HUGE)
+ nf = 0;
+ if (len == 0)
+ return nf;
+
+ if (*RS == 0 && default_FS)
+ while (scan < end && (*scan == ' ' || *scan == '\t' || *scan == '\n'))
+ scan++;
+ field = scan;
+ while (scan < end
+ && research(rp, scan, 0, (end - scan), 1) != -1
+ && nf < up_to) {
+ if (REEND(rp, scan) == RESTART(rp, scan)) { /* null match */
+ scan++;
+ if (scan == end) {
+ (*set)(++nf, field, (int)(scan - field), n);
+ up_to = nf;
+ break;
+ }
+ continue;
+ }
+ (*set)(++nf, field,
+ (int)(scan + RESTART(rp, scan) - field), n);
+ scan += REEND(rp, scan);
+ field = scan;
+ if (scan == end) /* FS at end of record */
+ (*set)(++nf, field, 0, n);
+ }
+ if (nf != up_to && scan < end) {
+ (*set)(++nf, scan, (int)(end - scan), n);
+ scan = end;
+ }
+ *buf = scan;
+ return (nf);
+}
+
+/*
+ * this is called both from get_field() and from do_split()
+ * via (*parse_field)(). This variation is for when FS is a single space
+ * character.
+ */
+static long
+def_parse_field(up_to, buf, len, fs, rp, set, n)
+int up_to; /* parse only up to this field number */
+char **buf; /* on input: string to parse; on output: point to start next */
+int len;
+NODE *fs;
+Regexp *rp;
+Setfunc set; /* routine to set the value of the parsed field */
+NODE *n;
+{
+ register char *scan = *buf;
+ register int nf = parse_high_water;
+ register char *field;
+ register char *end = scan + len;
+ char sav;
+
+ if (up_to == HUGE)
+ nf = 0;
+ if (len == 0)
+ return nf;
+
+ /* before doing anything save the char at *end */
+ sav = *end;
+ /* because it will be destroyed now: */
+
+ *end = ' '; /* sentinel character */
+ for (; nf < up_to; scan++) {
+ /*
+ * special case: fs is single space, strip leading whitespace
+ */
+ while (scan < end && (*scan == ' ' || *scan == '\t'))
+ scan++;
+ if (scan >= end)
+ break;
+ field = scan;
+ while (*scan != ' ' && *scan != '\t')
+ scan++;
+ (*set)(++nf, field, (int)(scan - field), n);
+ if (scan == end)
+ break;
+ }
+
+ /* everything done, restore original char at *end */
+ *end = sav;
+
+ *buf = scan;
+ return nf;
+}
+
+/*
+ * this is called both from get_field() and from do_split()
+ * via (*parse_field)(). This variation is for when FS is a single character
+ * other than space.
+ */
+static long
+sc_parse_field(up_to, buf, len, fs, rp, set, n)
+int up_to; /* parse only up to this field number */
+char **buf; /* on input: string to parse; on output: point to start next */
+int len;
+NODE *fs;
+Regexp *rp;
+Setfunc set; /* routine to set the value of the parsed field */
+NODE *n;
+{
+ register char *scan = *buf;
+ register char fschar;
+ register int nf = parse_high_water;
+ register char *field;
+ register char *end = scan + len;
+ char sav;
+
+ if (up_to == HUGE)
+ nf = 0;
+ if (len == 0)
+ return nf;
+
+ if (*RS == 0 && fs->stlen == 0)
+ fschar = '\n';
+ else
+ fschar = fs->stptr[0];
+
+ /* before doing anything save the char at *end */
+ sav = *end;
+ /* because it will be destroyed now: */
+ *end = fschar; /* sentinel character */
+
+ for (; nf < up_to;) {
+ field = scan;
+ while (*scan != fschar)
+ scan++;
+ (*set)(++nf, field, (int)(scan - field), n);
+ if (scan == end)
+ break;
+ scan++;
+ if (scan == end) { /* FS at end of record */
+ (*set)(++nf, field, 0, n);
+ break;
+ }
+ }
+
+ /* everything done, restore original char at *end */
+ *end = sav;
+
+ *buf = scan;
+ return nf;
+}
+
+/*
+ * this is called both from get_field() and from do_split()
+ * via (*parse_field)(). This variation is for fields are fixed widths.
+ */
+static long
+fw_parse_field(up_to, buf, len, fs, rp, set, n)
+int up_to; /* parse only up to this field number */
+char **buf; /* on input: string to parse; on output: point to start next */
+int len;
+NODE *fs;
+Regexp *rp;
+Setfunc set; /* routine to set the value of the parsed field */
+NODE *n;
+{
+ register char *scan = *buf;
+ register long nf = parse_high_water;
+ register char *end = scan + len;
+
+ if (up_to == HUGE)
+ nf = 0;
+ if (len == 0)
+ return nf;
+ for (; nf < up_to && (len = FIELDWIDTHS[nf+1]) != -1; ) {
+ if (len > end - scan)
+ len = end - scan;
+ (*set)(++nf, scan, len, n);
+ scan += len;
+ }
+ if (len == -1)
+ *buf = end;
+ else
+ *buf = scan;
+ return nf;
+}
+
+NODE **
+get_field(requested, assign)
+register int requested;
+Func_ptr *assign; /* this field is on the LHS of an assign */
+{
+ /*
+ * if requesting whole line but some other field has been altered,
+ * then the whole line must be rebuilt
+ */
+ if (requested == 0) {
+ if (!field0_valid) {
+ /* first, parse remainder of input record */
+ if (NF == -1) {
+ NF = (*parse_field)(HUGE-1, &parse_extent,
+ fields_arr[0]->stlen -
+ (parse_extent - fields_arr[0]->stptr),
+ save_FS, FS_regexp, set_field,
+ (NODE *)NULL);
+ parse_high_water = NF;
+ }
+ rebuild_record();
+ }
+ if (assign)
+ *assign = reset_record;
+ return &fields_arr[0];
+ }
+
+ /* assert(requested > 0); */
+
+ if (assign)
+ field0_valid = 0; /* $0 needs reconstruction */
+
+ if (requested <= parse_high_water) /* already parsed this field */
+ return &fields_arr[requested];
+
+ if (NF == -1) { /* have not yet parsed to end of record */
+ /*
+ * parse up to requested fields, calling set_field() for each,
+ * saving in parse_extent the point where the parse left off
+ */
+ if (parse_high_water == 0) /* starting at the beginning */
+ parse_extent = fields_arr[0]->stptr;
+ parse_high_water = (*parse_field)(requested, &parse_extent,
+ fields_arr[0]->stlen - (parse_extent-fields_arr[0]->stptr),
+ save_FS, FS_regexp, set_field, (NODE *)NULL);
+
+ /*
+ * if we reached the end of the record, set NF to the number of
+ * fields so far. Note that requested might actually refer to
+ * a field that is beyond the end of the record, but we won't
+ * set NF to that value at this point, since this is only a
+ * reference to the field and NF only gets set if the field
+ * is assigned to -- this case is handled below
+ */
+ if (parse_extent == fields_arr[0]->stptr + fields_arr[0]->stlen)
+ NF = parse_high_water;
+ if (requested == HUGE-1) /* HUGE-1 means set NF */
+ requested = parse_high_water;
+ }
+ if (parse_high_water < requested) { /* requested beyond end of record */
+ if (assign) { /* expand record */
+ register int i;
+
+ if (requested > nf_high_water)
+ grow_fields_arr(requested);
+
+ /* fill in fields that don't exist */
+ for (i = parse_high_water + 1; i <= requested; i++)
+ fields_arr[i] = Nnull_string;
+
+ NF = requested;
+ parse_high_water = requested;
+ } else
+ return &Nnull_string;
+ }
+
+ return &fields_arr[requested];
+}
+
+static void
+set_element(num, s, len, n)
+int num;
+char *s;
+int len;
+NODE *n;
+{
+ register NODE *it;
+
+ it = make_string(s, len);
+ it->flags |= MAYBE_NUM;
+ *assoc_lookup(n, tmp_number((AWKNUM) (num))) = it;
+}
+
+NODE *
+do_split(tree)
+NODE *tree;
+{
+ NODE *t1, *t2, *t3, *tmp;
+ NODE *fs;
+ char *s;
+ long (*parseit)P((int, char **, int, NODE *,
+ Regexp *, Setfunc, NODE *));
+ Regexp *rp = NULL;
+
+
+ /*
+ * do dupnode(), to avoid problems like
+ * x = split(a[1], a, "blah")
+ * since we assoc_clear the array. gack.
+ * this also gives up complete call by value semantics.
+ */
+ tmp = tree_eval(tree->lnode);
+ t1 = dupnode(tmp);
+ free_temp(tmp);
+
+ t2 = tree->rnode->lnode;
+ t3 = tree->rnode->rnode->lnode;
+
+ (void) force_string(t1);
+
+ if (t2->type == Node_param_list)
+ t2 = stack_ptr[t2->param_cnt];
+ if (t2->type != Node_var && t2->type != Node_var_array)
+ fatal("second argument of split is not a variable");
+ assoc_clear(t2);
+
+ if (t3->re_flags & FS_DFLT) {
+ parseit = parse_field;
+ fs = force_string(FS_node->var_value);
+ rp = FS_regexp;
+ } else {
+ tmp = force_string(tree_eval(t3->re_exp));
+ if (tmp->stlen == 1) {
+ if (tmp->stptr[0] == ' ')
+ parseit = def_parse_field;
+ else
+ parseit = sc_parse_field;
+ } else {
+ parseit = re_parse_field;
+ rp = re_update(t3);
+ }
+ fs = tmp;
+ }
+
+ s = t1->stptr;
+ tmp = tmp_number((AWKNUM) (*parseit)(HUGE, &s, (int)t1->stlen,
+ fs, rp, set_element, t2));
+ unref(t1);
+ free_temp(t3);
+ return tmp;
+}
+
+void
+set_FS()
+{
+ char buf[10];
+ NODE *fs;
+
+ /*
+ * If changing the way fields are split, obey least-suprise
+ * semantics, and force $0 to be split totally.
+ */
+ if (fields_arr != NULL)
+ (void) get_field(HUGE - 1, 0);
+
+ buf[0] = '\0';
+ default_FS = 0;
+ if (FS_regexp) {
+ refree(FS_regexp);
+ FS_regexp = NULL;
+ }
+ fs = force_string(FS_node->var_value);
+ if (fs->stlen > 1)
+ parse_field = re_parse_field;
+ else if (*RS == 0) {
+ parse_field = sc_parse_field;
+ if (fs->stlen == 1) {
+ if (fs->stptr[0] == ' ') {
+ default_FS = 1;
+ strcpy(buf, "[ \t\n]+");
+ } else if (fs->stptr[0] != '\n')
+ sprintf(buf, "[%c\n]", fs->stptr[0]);
+ }
+ } else {
+ parse_field = def_parse_field;
+ if (fs->stptr[0] == ' ' && fs->stlen == 1)
+ default_FS = 1;
+ else if (fs->stptr[0] != ' ' && fs->stlen == 1) {
+ if (IGNORECASE == 0)
+ parse_field = sc_parse_field;
+ else if (fs->stptr[0] == '\\')
+ /* yet another special case */
+ strcpy(buf, "[\\\\]");
+ else
+ sprintf(buf, "[%c]", fs->stptr[0]);
+ }
+ }
+ if (buf[0]) {
+ FS_regexp = make_regexp(buf, strlen(buf), IGNORECASE, 1);
+ parse_field = re_parse_field;
+ } else if (parse_field == re_parse_field) {
+ FS_regexp = make_regexp(fs->stptr, fs->stlen, IGNORECASE, 1);
+ } else
+ FS_regexp = NULL;
+ resave_fs = 1;
+}
+
+void
+set_RS()
+{
+ (void) force_string(RS_node->var_value);
+ RS = RS_node->var_value->stptr;
+ set_FS();
+}
+
+void
+set_FIELDWIDTHS()
+{
+ register char *scan;
+ char *end;
+ register int i;
+ static int fw_alloc = 1;
+ static int warned = 0;
+ extern double strtod();
+
+ if (do_lint && ! warned) {
+ warned = 1;
+ warning("use of FIELDWIDTHS is a gawk extension");
+ }
+ if (do_unix) /* quick and dirty, does the trick */
+ return;
+
+ /*
+ * If changing the way fields are split, obey least-suprise
+ * semantics, and force $0 to be split totally.
+ */
+ if (fields_arr != NULL)
+ (void) get_field(HUGE - 1, 0);
+
+ parse_field = fw_parse_field;
+ scan = force_string(FIELDWIDTHS_node->var_value)->stptr;
+ end = scan + 1;
+ if (FIELDWIDTHS == NULL)
+ emalloc(FIELDWIDTHS, int *, fw_alloc * sizeof(int), "set_FIELDWIDTHS");
+ FIELDWIDTHS[0] = 0;
+ for (i = 1; ; i++) {
+ if (i >= fw_alloc) {
+ fw_alloc *= 2;
+ erealloc(FIELDWIDTHS, int *, fw_alloc * sizeof(int), "set_FIELDWIDTHS");
+ }
+ FIELDWIDTHS[i] = (int) strtod(scan, &end);
+ if (end == scan)
+ break;
+ scan = end;
+ }
+ FIELDWIDTHS[i] = -1;
+}
diff --git a/gnu/usr.bin/awk/gawk.texi b/gnu/usr.bin/awk/gawk.texi
new file mode 100644
index 0000000..b280262
--- /dev/null
+++ b/gnu/usr.bin/awk/gawk.texi
@@ -0,0 +1,11270 @@
+\input texinfo @c -*-texinfo-*-
+@c %**start of header (This is for running Texinfo on a region.)
+@setfilename gawk.info
+@settitle The GAWK Manual
+@c @smallbook
+@c %**end of header (This is for running Texinfo on a region.)
+
+@ifinfo
+@synindex fn cp
+@synindex vr cp
+@end ifinfo
+@iftex
+@syncodeindex fn cp
+@syncodeindex vr cp
+@end iftex
+
+@c If "finalout" is commented out, the printed output will show
+@c black boxes that mark lines that are too long. Thus, it is
+@c unwise to comment it out when running a master in case there are
+@c overfulls which are deemed okay.
+
+@iftex
+@finalout
+@end iftex
+
+@c ===> NOTE! <==
+@c Determine the edition number in *four* places by hand:
+@c 1. First ifinfo section 2. title page 3. copyright page 4. top node
+@c To find the locations, search for !!set
+
+@ifinfo
+This file documents @code{awk}, a program that you can use to select
+particular records in a file and perform operations upon them.
+
+This is Edition 0.15 of @cite{The GAWK Manual}, @*
+for the 2.15 version of the GNU implementation @*
+of AWK.
+
+Copyright (C) 1989, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+@ignore
+Permission is granted to process this file through TeX and print the
+results, provided the printed document carries copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+@end ignore
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the entire
+resulting derived work is distributed under the terms of a permission
+notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation approved
+by the Foundation.
+@end ifinfo
+
+@setchapternewpage odd
+
+@c !!set edition, date, version
+@titlepage
+@title The GAWK Manual
+@subtitle Edition 0.15
+@subtitle April 1993
+@author Diane Barlow Close
+@author Arnold D. Robbins
+@author Paul H. Rubin
+@author Richard Stallman
+
+@c Include the Distribution inside the titlepage environment so
+@c that headings are turned off. Headings on and off do not work.
+
+@page
+@vskip 0pt plus 1filll
+Copyright @copyright{} 1989, 1991, 1992, 1993 Free Software Foundation, Inc.
+@sp 2
+
+@c !!set edition, date, version
+This is Edition 0.15 of @cite{The GAWK Manual}, @*
+for the 2.15 version of the GNU implementation @*
+of AWK.
+
+@sp 2
+Published by the Free Software Foundation @*
+675 Massachusetts Avenue @*
+Cambridge, MA 02139 USA @*
+Printed copies are available for $20 each.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the entire
+resulting derived work is distributed under the terms of a permission
+notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation approved
+by the Foundation.
+@end titlepage
+
+@ifinfo
+@node Top, Preface, (dir), (dir)
+@comment node-name, next, previous, up
+@top General Introduction
+@c Preface or Licensing nodes should come right after the Top
+@c node, in `unnumbered' sections, then the chapter, `What is gawk'.
+
+This file documents @code{awk}, a program that you can use to select
+particular records in a file and perform operations upon them.
+
+@c !!set edition, date, version
+This is Edition 0.15 of @cite{The GAWK Manual}, @*
+for the 2.15 version of the GNU implementation @*
+of AWK.
+
+@end ifinfo
+
+@menu
+* Preface:: What you can do with @code{awk}; brief history
+ and acknowledgements.
+* Copying:: Your right to copy and distribute @code{gawk}.
+* This Manual:: Using this manual.
+ Includes sample input files that you can use.
+* Getting Started:: A basic introduction to using @code{awk}.
+ How to run an @code{awk} program.
+ Command line syntax.
+* Reading Files:: How to read files and manipulate fields.
+* Printing:: How to print using @code{awk}. Describes the
+ @code{print} and @code{printf} statements.
+ Also describes redirection of output.
+* One-liners:: Short, sample @code{awk} programs.
+* Patterns:: The various types of patterns
+ explained in detail.
+* Actions:: The various types of actions are
+ introduced here. Describes
+ expressions and the various operators in
+ detail. Also describes comparison expressions.
+* Expressions:: Expressions are the basic building
+ blocks of statements.
+* Statements:: The various control statements are
+ described in detail.
+* Arrays:: The description and use of arrays.
+ Also includes array-oriented control
+ statements.
+* Built-in:: The built-in functions are summarized here.
+* User-defined:: User-defined functions are described in detail.
+* Built-in Variables:: Built-in Variables
+* Command Line:: How to run @code{gawk}.
+* Language History:: The evolution of the @code{awk} language.
+* Installation:: Installing @code{gawk} under
+ various operating systems.
+* Gawk Summary:: @code{gawk} Options and Language Summary.
+* Sample Program:: A sample @code{awk} program with a
+ complete explanation.
+* Bugs:: Reporting Problems and Bugs.
+* Notes:: Something about the
+ implementation of @code{gawk}.
+* Glossary:: An explanation of some unfamiliar terms.
+* Index::
+@end menu
+
+@node Preface, Copying, Top, Top
+@comment node-name, next, previous, up
+@unnumbered Preface
+
+@iftex
+@cindex what is @code{awk}
+@end iftex
+If you are like many computer users, you would frequently like to make
+changes in various text files wherever certain patterns appear, or
+extract data from parts of certain lines while discarding the rest. To
+write a program to do this in a language such as C or Pascal is a
+time-consuming inconvenience that may take many lines of code. The job
+may be easier with @code{awk}.
+
+The @code{awk} utility interprets a special-purpose programming language
+that makes it possible to handle simple data-reformatting jobs easily
+with just a few lines of code.
+
+The GNU implementation of @code{awk} is called @code{gawk}; it is fully
+upward compatible with the System V Release 4 version of
+@code{awk}. @code{gawk} is also upward compatible with the @sc{posix}
+(draft) specification of the @code{awk} language. This means that all
+properly written @code{awk} programs should work with @code{gawk}.
+Thus, we usually don't distinguish between @code{gawk} and other @code{awk}
+implementations in this manual.@refill
+
+@cindex uses of @code{awk}
+This manual teaches you what @code{awk} does and how you can use
+@code{awk} effectively. You should already be familiar with basic
+system commands such as @code{ls}. Using @code{awk} you can: @refill
+
+@itemize @bullet
+@item
+manage small, personal databases
+
+@item
+generate reports
+
+@item
+validate data
+@item
+produce indexes, and perform other document preparation tasks
+
+@item
+even experiment with algorithms that can be adapted later to other computer
+languages
+@end itemize
+
+@iftex
+This manual has the difficult task of being both tutorial and reference.
+If you are a novice, feel free to skip over details that seem too complex.
+You should also ignore the many cross references; they are for the
+expert user, and for the on-line Info version of the manual.
+@end iftex
+
+@menu
+* History:: The history of @code{gawk} and
+ @code{awk}. Acknowledgements.
+@end menu
+
+@node History, , Preface, Preface
+@comment node-name, next, previous, up
+@unnumberedsec History of @code{awk} and @code{gawk}
+
+@cindex acronym
+@cindex history of @code{awk}
+The name @code{awk} comes from the initials of its designers: Alfred V.
+Aho, Peter J. Weinberger, and Brian W. Kernighan. The original version of
+@code{awk} was written in 1977. In 1985 a new version made the programming
+language more powerful, introducing user-defined functions, multiple input
+streams, and computed regular expressions.
+This new version became generally available with System V Release 3.1.
+The version in System V Release 4 added some new features and also cleaned
+up the behavior in some of the ``dark corners'' of the language.
+The specification for @code{awk} in the @sc{posix} Command Language
+and Utilities standard further clarified the language based on feedback
+from both the @code{gawk} designers, and the original @code{awk}
+designers.@refill
+
+The GNU implementation, @code{gawk}, was written in 1986 by Paul Rubin
+and Jay Fenlason, with advice from Richard Stallman. John Woods
+contributed parts of the code as well. In 1988 and 1989, David Trueman, with
+help from Arnold Robbins, thoroughly reworked @code{gawk} for compatibility
+with the newer @code{awk}. Current development (1992) focuses on bug fixes,
+performance improvements, and standards compliance.
+
+We need to thank many people for their assistance in producing this
+manual. Jay Fenlason contributed many ideas and sample programs. Richard
+Mlynarik and Robert J. Chassell gave helpful comments on early drafts of this
+manual. The paper @cite{A Supplemental Document for @code{awk}} by John W.
+Pierce of the Chemistry Department at UC San Diego, pinpointed several
+issues relevant both to @code{awk} implementation and to this manual, that
+would otherwise have escaped us. David Trueman, Pat Rankin, and Michal
+Jaegermann also contributed sections of the manual.@refill
+
+The following people provided many helpful comments on this edition of
+the manual: Rick Adams, Michael Brennan, Rich Burridge, Diane Close,
+Christopher (``Topher'') Eliot, Michael Lijewski, Pat Rankin, Miriam Robbins,
+and Michal Jaegermann. Robert J. Chassell provided much valuable advice on
+the use of Texinfo.
+
+Finally, we would like to thank Brian Kernighan of Bell Labs for invaluable
+assistance during the testing and debugging of @code{gawk}, and for
+help in clarifying numerous points about the language.@refill
+
+@node Copying, This Manual, Preface, Top
+@unnumbered GNU GENERAL PUBLIC LICENSE
+@center Version 2, June 1991
+
+@display
+Copyright @copyright{} 1989, 1991 Free Software Foundation, Inc.
+675 Mass Ave, Cambridge, MA 02139, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+@end display
+
+@c fakenode --- for prepinfo
+@unnumberedsec Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software---to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+@iftex
+@c fakenode --- for prepinfo
+@unnumberedsec TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+@end iftex
+@ifinfo
+@center TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+@end ifinfo
+
+@enumerate
+@item
+This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The ``Program'', below,
+refers to any such program or work, and a ``work based on the Program''
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term ``modification''.) Each licensee is addressed as ``you''.
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+@item
+You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+@item
+You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+@enumerate a
+@item
+You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+@item
+You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+@item
+If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+@end enumerate
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+@item
+You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+@enumerate a
+@item
+Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+@item
+Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+@item
+Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+@end enumerate
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+@item
+You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+@item
+You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+@item
+Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+@item
+If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+@item
+If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+@item
+The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and ``any
+later version'', you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+@item
+If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+@iftex
+@c fakenode --- for prepinfo
+@heading NO WARRANTY
+@end iftex
+@ifinfo
+@center NO WARRANTY
+@end ifinfo
+
+@item
+BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM ``AS IS'' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+@item
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+@end enumerate
+
+@iftex
+@c fakenode --- for prepinfo
+@heading END OF TERMS AND CONDITIONS
+@end iftex
+@ifinfo
+@center END OF TERMS AND CONDITIONS
+@end ifinfo
+
+@page
+@c fakenode --- for prepinfo
+@unnumberedsec How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the ``copyright'' line and a pointer to where the full notice is found.
+
+@smallexample
+@var{one line to give the program's name and a brief idea of what it does.}
+Copyright (C) 19@var{yy} @var{name of author}
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+@end smallexample
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+@smallexample
+Gnomovision version 69, Copyright (C) 19@var{yy} @var{name of author}
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
+type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+@end smallexample
+
+The hypothetical commands @samp{show w} and @samp{show c} should show
+the appropriate parts of the General Public License. Of course, the
+commands you use may be called something other than @samp{show w} and
+@samp{show c}; they could even be mouse-clicks or menu items---whatever
+suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a ``copyright disclaimer'' for the program, if
+necessary. Here is a sample; alter the names:
+
+@smallexample
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+@var{signature of Ty Coon}, 1 April 1989
+Ty Coon, President of Vice
+@end smallexample
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
+@node This Manual, Getting Started, Copying, Top
+@chapter Using this Manual
+@cindex manual, using this
+@cindex using this manual
+@cindex language, @code{awk}
+@cindex program, @code{awk}
+@cindex @code{awk} language
+@cindex @code{awk} program
+
+The term @code{awk} refers to a particular program, and to the language you
+use to tell this program what to do. When we need to be careful, we call
+the program ``the @code{awk} utility'' and the language ``the @code{awk}
+language.'' The term @code{gawk} refers to a version of @code{awk} developed
+as part the GNU project. The purpose of this manual is to explain
+both the
+@code{awk} language and how to run the @code{awk} utility.@refill
+
+While concentrating on the features of @code{gawk}, the manual will also
+attempt to describe important differences between @code{gawk} and other
+@code{awk} implementations. In particular, any features that are not
+in the @sc{posix} standard for @code{awk} will be noted. @refill
+
+The term @dfn{@code{awk} program} refers to a program written by you in
+the @code{awk} programming language.@refill
+
+@xref{Getting Started, ,Getting Started with @code{awk}}, for the bare
+essentials you need to know to start using @code{awk}.
+
+Some useful ``one-liners'' are included to give you a feel for the
+@code{awk} language (@pxref{One-liners, ,Useful ``One-liners''}).
+
+@ignore
+@strong{I deleted four paragraphs here because they would confuse the
+beginner more than help him. They mention terms such as ``field,''
+``pattern,'' ``action,'' ``built-in function'' which the beginner
+doesn't know.}
+
+@strong{If you can find a way to introduce several of these concepts here,
+enough to give the reader a map of what is to follow, that might
+be useful. I'm not sure that can be done without taking up more
+space than ought to be used here. There may be no way to win.}
+
+@strong{ADR: I'd like to tackle this in phase 2 of my editing.}
+@end ignore
+
+A sample @code{awk} program has been provided for you
+(@pxref{Sample Program}).@refill
+
+If you find terms that you aren't familiar with, try looking them
+up in the glossary (@pxref{Glossary}).@refill
+
+The entire @code{awk} language is summarized for quick reference in
+@ref{Gawk Summary, ,@code{gawk} Summary}. Look there if you just need
+to refresh your memory about a particular feature.@refill
+
+Most of the time complete @code{awk} programs are used as examples, but in
+some of the more advanced sections, only the part of the @code{awk} program
+that illustrates the concept being described is shown.@refill
+
+@menu
+* Sample Data Files:: Sample data files for use in the @code{awk}
+ programs illustrated in this manual.
+@end menu
+
+@node Sample Data Files, , This Manual, This Manual
+@section Data Files for the Examples
+
+@cindex input file, sample
+@cindex sample input file
+@cindex @file{BBS-list} file
+Many of the examples in this manual take their input from two sample
+data files. The first, called @file{BBS-list}, represents a list of
+computer bulletin board systems together with information about those systems.
+The second data file, called @file{inventory-shipped}, contains
+information about shipments on a monthly basis. Each line of these
+files is one @dfn{record}.
+
+In the file @file{BBS-list}, each record contains the name of a computer
+bulletin board, its phone number, the board's baud rate, and a code for
+the number of hours it is operational. An @samp{A} in the last column
+means the board operates 24 hours a day. A @samp{B} in the last
+column means the board operates evening and weekend hours, only. A
+@samp{C} means the board operates only on weekends.
+
+@example
+aardvark 555-5553 1200/300 B
+alpo-net 555-3412 2400/1200/300 A
+barfly 555-7685 1200/300 A
+bites 555-1675 2400/1200/300 A
+camelot 555-0542 300 C
+core 555-2912 1200/300 C
+fooey 555-1234 2400/1200/300 B
+foot 555-6699 1200/300 B
+macfoo 555-6480 1200/300 A
+sdace 555-3430 2400/1200/300 A
+sabafoo 555-2127 1200/300 C
+@end example
+
+@cindex @file{inventory-shipped} file
+The second data file, called @file{inventory-shipped}, represents
+information about shipments during the year.
+Each record contains the month of the year, the number
+of green crates shipped, the number of red boxes shipped, the number of
+orange bags shipped, and the number of blue packages shipped,
+respectively. There are 16 entries, covering the 12 months of one year
+and 4 months of the next year.@refill
+
+@example
+Jan 13 25 15 115
+Feb 15 32 24 226
+Mar 15 24 34 228
+Apr 31 52 63 420
+May 16 34 29 208
+Jun 31 42 75 492
+Jul 24 34 67 436
+Aug 15 34 47 316
+Sep 13 55 37 277
+Oct 29 54 68 525
+Nov 20 87 82 577
+Dec 17 35 61 401
+
+Jan 21 36 64 620
+Feb 26 58 80 652
+Mar 24 75 70 495
+Apr 21 70 74 514
+@end example
+
+@ifinfo
+If you are reading this in GNU Emacs using Info, you can copy the regions
+of text showing these sample files into your own test files. This way you
+can try out the examples shown in the remainder of this document. You do
+this by using the command @kbd{M-x write-region} to copy text from the Info
+file into a file for use with @code{awk}
+(@xref{Misc File Ops, , , emacs, GNU Emacs Manual},
+for more information). Using this information, create your own
+@file{BBS-list} and @file{inventory-shipped} files, and practice what you
+learn in this manual.
+@end ifinfo
+
+@node Getting Started, Reading Files, This Manual, Top
+@chapter Getting Started with @code{awk}
+@cindex script, definition of
+@cindex rule, definition of
+@cindex program, definition of
+@cindex basic function of @code{gawk}
+
+The basic function of @code{awk} is to search files for lines (or other
+units of text) that contain certain patterns. When a line matches one
+of the patterns, @code{awk} performs specified actions on that line.
+@code{awk} keeps processing input lines in this way until the end of the
+input file is reached.@refill
+
+When you run @code{awk}, you specify an @code{awk} @dfn{program} which
+tells @code{awk} what to do. The program consists of a series of
+@dfn{rules}. (It may also contain @dfn{function definitions}, but that
+is an advanced feature, so we will ignore it for now.
+@xref{User-defined, ,User-defined Functions}.) Each rule specifies one
+pattern to search for, and one action to perform when that pattern is found.
+
+Syntactically, a rule consists of a pattern followed by an action. The
+action is enclosed in curly braces to separate it from the pattern.
+Rules are usually separated by newlines. Therefore, an @code{awk}
+program looks like this:
+
+@example
+@var{pattern} @{ @var{action} @}
+@var{pattern} @{ @var{action} @}
+@dots{}
+@end example
+
+@menu
+* Very Simple:: A very simple example.
+* Two Rules:: A less simple one-line example with two rules.
+* More Complex:: A more complex example.
+* Running gawk:: How to run @code{gawk} programs;
+ includes command line syntax.
+* Comments:: Adding documentation to @code{gawk} programs.
+* Statements/Lines:: Subdividing or combining statements into lines.
+* When:: When to use @code{gawk} and
+ when to use other things.
+@end menu
+
+@node Very Simple, Two Rules, Getting Started, Getting Started
+@section A Very Simple Example
+
+@cindex @samp{print $0}
+The following command runs a simple @code{awk} program that searches the
+input file @file{BBS-list} for the string of characters: @samp{foo}. (A
+string of characters is usually called, a @dfn{string}.
+The term @dfn{string} is perhaps based on similar usage in English, such
+as ``a string of pearls,'' or, ``a string of cars in a train.'')
+
+@example
+awk '/foo/ @{ print $0 @}' BBS-list
+@end example
+
+@noindent
+When lines containing @samp{foo} are found, they are printed, because
+@w{@samp{print $0}} means print the current line. (Just @samp{print} by
+itself means the same thing, so we could have written that
+instead.)
+
+You will notice that slashes, @samp{/}, surround the string @samp{foo}
+in the actual @code{awk} program. The slashes indicate that @samp{foo}
+is a pattern to search for. This type of pattern is called a
+@dfn{regular expression}, and is covered in more detail later
+(@pxref{Regexp, ,Regular Expressions as Patterns}). There are
+single-quotes around the @code{awk} program so that the shell won't
+interpret any of it as special shell characters.@refill
+
+Here is what this program prints:
+
+@example
+@group
+fooey 555-1234 2400/1200/300 B
+foot 555-6699 1200/300 B
+macfoo 555-6480 1200/300 A
+sabafoo 555-2127 1200/300 C
+@end group
+@end example
+
+@cindex action, default
+@cindex pattern, default
+@cindex default action
+@cindex default pattern
+In an @code{awk} rule, either the pattern or the action can be omitted,
+but not both. If the pattern is omitted, then the action is performed
+for @emph{every} input line. If the action is omitted, the default
+action is to print all lines that match the pattern.
+
+Thus, we could leave out the action (the @code{print} statement and the curly
+braces) in the above example, and the result would be the same: all
+lines matching the pattern @samp{foo} would be printed. By comparison,
+omitting the @code{print} statement but retaining the curly braces makes an
+empty action that does nothing; then no lines would be printed.
+
+@node Two Rules, More Complex, Very Simple, Getting Started
+@section An Example with Two Rules
+@cindex how @code{awk} works
+
+The @code{awk} utility reads the input files one line at a
+time. For each line, @code{awk} tries the patterns of each of the rules.
+If several patterns match then several actions are run, in the order in
+which they appear in the @code{awk} program. If no patterns match, then
+no actions are run.
+
+After processing all the rules (perhaps none) that match the line,
+@code{awk} reads the next line (however,
+@pxref{Next Statement, ,The @code{next} Statement}). This continues
+until the end of the file is reached.@refill
+
+For example, the @code{awk} program:
+
+@example
+/12/ @{ print $0 @}
+/21/ @{ print $0 @}
+@end example
+
+@noindent
+contains two rules. The first rule has the string @samp{12} as the
+pattern and @samp{print $0} as the action. The second rule has the
+string @samp{21} as the pattern and also has @samp{print $0} as the
+action. Each rule's action is enclosed in its own pair of braces.
+
+This @code{awk} program prints every line that contains the string
+@samp{12} @emph{or} the string @samp{21}. If a line contains both
+strings, it is printed twice, once by each rule.
+
+If we run this program on our two sample data files, @file{BBS-list} and
+@file{inventory-shipped}, as shown here:
+
+@example
+awk '/12/ @{ print $0 @}
+ /21/ @{ print $0 @}' BBS-list inventory-shipped
+@end example
+
+@noindent
+we get the following output:
+
+@example
+aardvark 555-5553 1200/300 B
+alpo-net 555-3412 2400/1200/300 A
+barfly 555-7685 1200/300 A
+bites 555-1675 2400/1200/300 A
+core 555-2912 1200/300 C
+fooey 555-1234 2400/1200/300 B
+foot 555-6699 1200/300 B
+macfoo 555-6480 1200/300 A
+sdace 555-3430 2400/1200/300 A
+sabafoo 555-2127 1200/300 C
+sabafoo 555-2127 1200/300 C
+Jan 21 36 64 620
+Apr 21 70 74 514
+@end example
+
+@noindent
+Note how the line in @file{BBS-list} beginning with @samp{sabafoo}
+was printed twice, once for each rule.
+
+@node More Complex, Running gawk, Two Rules, Getting Started
+@comment node-name, next, previous, up
+@section A More Complex Example
+
+Here is an example to give you an idea of what typical @code{awk}
+programs do. This example shows how @code{awk} can be used to
+summarize, select, and rearrange the output of another utility. It uses
+features that haven't been covered yet, so don't worry if you don't
+understand all the details.
+
+@example
+ls -l | awk '$5 == "Nov" @{ sum += $4 @}
+ END @{ print sum @}'
+@end example
+
+This command prints the total number of bytes in all the files in the
+current directory that were last modified in November (of any year).
+(In the C shell you would need to type a semicolon and then a backslash
+at the end of the first line; in a @sc{posix}-compliant shell, such as the
+Bourne shell or the Bourne-Again shell, you can type the example as shown.)
+
+The @w{@samp{ls -l}} part of this example is a command that gives you a
+listing of the files in a directory, including file size and date.
+Its output looks like this:@refill
+
+@example
+-rw-r--r-- 1 close 1933 Nov 7 13:05 Makefile
+-rw-r--r-- 1 close 10809 Nov 7 13:03 gawk.h
+-rw-r--r-- 1 close 983 Apr 13 12:14 gawk.tab.h
+-rw-r--r-- 1 close 31869 Jun 15 12:20 gawk.y
+-rw-r--r-- 1 close 22414 Nov 7 13:03 gawk1.c
+-rw-r--r-- 1 close 37455 Nov 7 13:03 gawk2.c
+-rw-r--r-- 1 close 27511 Dec 9 13:07 gawk3.c
+-rw-r--r-- 1 close 7989 Nov 7 13:03 gawk4.c
+@end example
+
+@noindent
+The first field contains read-write permissions, the second field contains
+the number of links to the file, and the third field identifies the owner of
+the file. The fourth field contains the size of the file in bytes. The
+fifth, sixth, and seventh fields contain the month, day, and time,
+respectively, that the file was last modified. Finally, the eighth field
+contains the name of the file.
+
+The @code{$5 == "Nov"} in our @code{awk} program is an expression that
+tests whether the fifth field of the output from @w{@samp{ls -l}}
+matches the string @samp{Nov}. Each time a line has the string
+@samp{Nov} in its fifth field, the action @samp{@{ sum += $4 @}} is
+performed. This adds the fourth field (the file size) to the variable
+@code{sum}. As a result, when @code{awk} has finished reading all the
+input lines, @code{sum} is the sum of the sizes of files whose
+lines matched the pattern. (This works because @code{awk} variables
+are automatically initialized to zero.)@refill
+
+After the last line of output from @code{ls} has been processed, the
+@code{END} rule is executed, and the value of @code{sum} is
+printed. In this example, the value of @code{sum} would be 80600.@refill
+
+These more advanced @code{awk} techniques are covered in later sections
+(@pxref{Actions, ,Overview of Actions}). Before you can move on to more
+advanced @code{awk} programming, you have to know how @code{awk} interprets
+your input and displays your output. By manipulating fields and using
+@code{print} statements, you can produce some very useful and spectacular
+looking reports.@refill
+
+@node Running gawk, Comments, More Complex, Getting Started
+@section How to Run @code{awk} Programs
+
+@ignore
+Date: Mon, 26 Aug 91 09:48:10 +0200
+From: gatech!vsoc07.cern.ch!matheys (Jean-Pol Matheys (CERN - ECP Division))
+To: uunet.UU.NET!skeeve!arnold
+Subject: RE: status check
+
+The introduction of Chapter 2 (i.e. before 2.1) should include
+the whole of section 2.4 - it's better to tell people how to run awk programs
+before giving any examples
+
+ADR --- he's right. but for now, don't do this because the rest of the
+chapter would need some rewriting.
+@end ignore
+
+@cindex command line formats
+@cindex running @code{awk} programs
+There are several ways to run an @code{awk} program. If the program is
+short, it is easiest to include it in the command that runs @code{awk},
+like this:
+
+@example
+awk '@var{program}' @var{input-file1} @var{input-file2} @dots{}
+@end example
+
+@noindent
+where @var{program} consists of a series of patterns and actions, as
+described earlier.
+
+When the program is long, it is usually more convenient to put it in a file
+and run it with a command like this:
+
+@example
+awk -f @var{program-file} @var{input-file1} @var{input-file2} @dots{}
+@end example
+
+@menu
+* One-shot:: Running a short throw-away @code{awk} program.
+* Read Terminal:: Using no input files (input from
+ terminal instead).
+* Long:: Putting permanent @code{awk} programs in files.
+* Executable Scripts:: Making self-contained @code{awk} programs.
+@end menu
+
+@node One-shot, Read Terminal, Running gawk, Running gawk
+@subsection One-shot Throw-away @code{awk} Programs
+
+Once you are familiar with @code{awk}, you will often type simple
+programs at the moment you want to use them. Then you can write the
+program as the first argument of the @code{awk} command, like this:
+
+@example
+awk '@var{program}' @var{input-file1} @var{input-file2} @dots{}
+@end example
+
+@noindent
+where @var{program} consists of a series of @var{patterns} and
+@var{actions}, as described earlier.
+
+@cindex single quotes, why needed
+This command format instructs the shell to start @code{awk} and use the
+@var{program} to process records in the input file(s). There are single
+quotes around @var{program} so that the shell doesn't interpret any
+@code{awk} characters as special shell characters. They also cause the
+shell to treat all of @var{program} as a single argument for
+@code{awk} and allow @var{program} to be more than one line long.@refill
+
+This format is also useful for running short or medium-sized @code{awk}
+programs from shell scripts, because it avoids the need for a separate
+file for the @code{awk} program. A self-contained shell script is more
+reliable since there are no other files to misplace.
+
+@node Read Terminal, Long, One-shot, Running gawk
+@subsection Running @code{awk} without Input Files
+
+@cindex standard input
+@cindex input, standard
+You can also run @code{awk} without any input files. If you type the
+command line:@refill
+
+@example
+awk '@var{program}'
+@end example
+
+@noindent
+then @code{awk} applies the @var{program} to the @dfn{standard input},
+which usually means whatever you type on the terminal. This continues
+until you indicate end-of-file by typing @kbd{Control-d}.
+
+For example, if you execute this command:
+
+@example
+awk '/th/'
+@end example
+
+@noindent
+whatever you type next is taken as data for that @code{awk}
+program. If you go on to type the following data:
+
+@example
+Kathy
+Ben
+Tom
+Beth
+Seth
+Karen
+Thomas
+@kbd{Control-d}
+@end example
+
+@noindent
+then @code{awk} prints this output:
+
+@example
+Kathy
+Beth
+Seth
+@end example
+
+@noindent
+@cindex case sensitivity
+@cindex pattern, case sensitive
+as matching the pattern @samp{th}. Notice that it did not recognize
+@samp{Thomas} as matching the pattern. The @code{awk} language is
+@dfn{case sensitive}, and matches patterns exactly. (However, you can
+override this with the variable @code{IGNORECASE}.
+@xref{Case-sensitivity, ,Case-sensitivity in Matching}.)
+
+@node Long, Executable Scripts, Read Terminal, Running gawk
+@subsection Running Long Programs
+
+@cindex running long programs
+@cindex @samp{-f} option
+@cindex program file
+@cindex file, @code{awk} program
+Sometimes your @code{awk} programs can be very long. In this case it is
+more convenient to put the program into a separate file. To tell
+@code{awk} to use that file for its program, you type:@refill
+
+@example
+awk -f @var{source-file} @var{input-file1} @var{input-file2} @dots{}
+@end example
+
+The @samp{-f} instructs the @code{awk} utility to get the @code{awk} program
+from the file @var{source-file}. Any file name can be used for
+@var{source-file}. For example, you could put the program:@refill
+
+@example
+/th/
+@end example
+
+@noindent
+into the file @file{th-prog}. Then this command:
+
+@example
+awk -f th-prog
+@end example
+
+@noindent
+does the same thing as this one:
+
+@example
+awk '/th/'
+@end example
+
+@noindent
+which was explained earlier (@pxref{Read Terminal, ,Running @code{awk} without Input Files}).
+Note that you don't usually need single quotes around the file name that you
+specify with @samp{-f}, because most file names don't contain any of the shell's
+special characters. Notice that in @file{th-prog}, the @code{awk}
+program did not have single quotes around it. The quotes are only needed
+for programs that are provided on the @code{awk} command line.
+
+If you want to identify your @code{awk} program files clearly as such,
+you can add the extension @file{.awk} to the file name. This doesn't
+affect the execution of the @code{awk} program, but it does make
+``housekeeping'' easier.
+
+@node Executable Scripts, , Long, Running gawk
+@c node-name, next, previous, up
+@subsection Executable @code{awk} Programs
+@cindex executable scripts
+@cindex scripts, executable
+@cindex self contained programs
+@cindex program, self contained
+@cindex @samp{#!}
+
+Once you have learned @code{awk}, you may want to write self-contained
+@code{awk} scripts, using the @samp{#!} script mechanism. You can do
+this on many Unix systems @footnote{The @samp{#!} mechanism works on
+Unix systems derived from Berkeley Unix, System V Release 4, and some System
+V Release 3 systems.} (and someday on GNU).@refill
+
+For example, you could create a text file named @file{hello}, containing
+the following (where @samp{BEGIN} is a feature we have not yet
+discussed):
+
+@example
+#! /bin/awk -f
+
+# a sample awk program
+BEGIN @{ print "hello, world" @}
+@end example
+
+@noindent
+After making this file executable (with the @code{chmod} command), you
+can simply type:
+
+@example
+hello
+@end example
+
+@noindent
+at the shell, and the system will arrange to run @code{awk} @footnote{The
+line beginning with @samp{#!} lists the full pathname of an interpreter
+to be run, and an optional initial command line argument to pass to that
+interpreter. The operating system then runs the interpreter with the given
+argument and the full argument list of the executed program. The first argument
+in the list is the full pathname of the @code{awk} program. The rest of the
+argument list will either be options to @code{awk}, or data files,
+or both.} as if you had typed:@refill
+
+@example
+awk -f hello
+@end example
+
+@noindent
+Self-contained @code{awk} scripts are useful when you want to write a
+program which users can invoke without knowing that the program is
+written in @code{awk}.
+
+@cindex shell scripts
+@cindex scripts, shell
+If your system does not support the @samp{#!} mechanism, you can get a
+similar effect using a regular shell script. It would look something
+like this:
+
+@example
+: The colon makes sure this script is executed by the Bourne shell.
+awk '@var{program}' "$@@"
+@end example
+
+Using this technique, it is @emph{vital} to enclose the @var{program} in
+single quotes to protect it from interpretation by the shell. If you
+omit the quotes, only a shell wizard can predict the results.
+
+The @samp{"$@@"} causes the shell to forward all the command line
+arguments to the @code{awk} program, without interpretation. The first
+line, which starts with a colon, is used so that this shell script will
+work even if invoked by a user who uses the C shell.
+@c Someday: (See @cite{The Bourne Again Shell}, by ??.)
+
+@node Comments, Statements/Lines, Running gawk, Getting Started
+@section Comments in @code{awk} Programs
+@cindex @samp{#}
+@cindex comments
+@cindex use of comments
+@cindex documenting @code{awk} programs
+@cindex programs, documenting
+
+A @dfn{comment} is some text that is included in a program for the sake
+of human readers, and that is not really part of the program. Comments
+can explain what the program does, and how it works. Nearly all
+programming languages have provisions for comments, because programs are
+typically hard to understand without their extra help.
+
+In the @code{awk} language, a comment starts with the sharp sign
+character, @samp{#}, and continues to the end of the line. The
+@code{awk} language ignores the rest of a line following a sharp sign.
+For example, we could have put the following into @file{th-prog}:@refill
+
+@smallexample
+# This program finds records containing the pattern @samp{th}. This is how
+# you continue comments on additional lines.
+/th/
+@end smallexample
+
+You can put comment lines into keyboard-composed throw-away @code{awk}
+programs also, but this usually isn't very useful; the purpose of a
+comment is to help you or another person understand the program at
+a later time.@refill
+
+@node Statements/Lines, When, Comments, Getting Started
+@section @code{awk} Statements versus Lines
+
+Most often, each line in an @code{awk} program is a separate statement or
+separate rule, like this:
+
+@example
+awk '/12/ @{ print $0 @}
+ /21/ @{ print $0 @}' BBS-list inventory-shipped
+@end example
+
+But sometimes statements can be more than one line, and lines can
+contain several statements. You can split a statement into multiple
+lines by inserting a newline after any of the following:@refill
+
+@example
+, @{ ? : || && do else
+@end example
+
+@noindent
+A newline at any other point is considered the end of the statement.
+(Splitting lines after @samp{?} and @samp{:} is a minor @code{gawk}
+extension. The @samp{?} and @samp{:} referred to here is the
+three operand conditional expression described in
+@ref{Conditional Exp, ,Conditional Expressions}.)@refill
+
+@cindex backslash continuation
+@cindex continuation of lines
+If you would like to split a single statement into two lines at a point
+where a newline would terminate it, you can @dfn{continue} it by ending the
+first line with a backslash character, @samp{\}. This is allowed
+absolutely anywhere in the statement, even in the middle of a string or
+regular expression. For example:
+
+@example
+awk '/This program is too long, so continue it\
+ on the next line/ @{ print $1 @}'
+@end example
+
+@noindent
+We have generally not used backslash continuation in the sample programs in
+this manual. Since in @code{gawk} there is no limit on the length of a line,
+it is never strictly necessary; it just makes programs prettier. We have
+preferred to make them even more pretty by keeping the statements short.
+Backslash continuation is most useful when your @code{awk} program is in a
+separate source file, instead of typed in on the command line. You should
+also note that many @code{awk} implementations are more picky about where
+you may use backslash continuation. For maximal portability of your @code{awk}
+programs, it is best not to split your lines in the middle of a regular
+expression or a string.@refill
+
+@strong{Warning: backslash continuation does not work as described above
+with the C shell.} Continuation with backslash works for @code{awk}
+programs in files, and also for one-shot programs @emph{provided} you
+are using a @sc{posix}-compliant shell, such as the Bourne shell or the
+Bourne-again shell. But the C shell used on Berkeley Unix behaves
+differently! There, you must use two backslashes in a row, followed by
+a newline.@refill
+
+@cindex multiple statements on one line
+When @code{awk} statements within one rule are short, you might want to put
+more than one of them on a line. You do this by separating the statements
+with a semicolon, @samp{;}.
+This also applies to the rules themselves.
+Thus, the previous program could have been written:@refill
+
+@example
+/12/ @{ print $0 @} ; /21/ @{ print $0 @}
+@end example
+
+@noindent
+@strong{Note:} the requirement that rules on the same line must be
+separated with a semicolon is a recent change in the @code{awk}
+language; it was done for consistency with the treatment of statements
+within an action.
+
+@node When, , Statements/Lines, Getting Started
+@section When to Use @code{awk}
+
+@cindex when to use @code{awk}
+@cindex applications of @code{awk}
+You might wonder how @code{awk} might be useful for you. Using additional
+utility programs, more advanced patterns, field separators, arithmetic
+statements, and other selection criteria, you can produce much more
+complex output. The @code{awk} language is very useful for producing
+reports from large amounts of raw data, such as summarizing information
+from the output of other utility programs like @code{ls}.
+(@xref{More Complex, ,A More Complex Example}.)
+
+Programs written with @code{awk} are usually much smaller than they would
+be in other languages. This makes @code{awk} programs easy to compose and
+use. Often @code{awk} programs can be quickly composed at your terminal,
+used once, and thrown away. Since @code{awk} programs are interpreted, you
+can avoid the usually lengthy edit-compile-test-debug cycle of software
+development.
+
+Complex programs have been written in @code{awk}, including a complete
+retargetable assembler for 8-bit microprocessors (@pxref{Glossary}, for
+more information) and a microcode assembler for a special purpose Prolog
+computer. However, @code{awk}'s capabilities are strained by tasks of
+such complexity.
+
+If you find yourself writing @code{awk} scripts of more than, say, a few
+hundred lines, you might consider using a different programming
+language. Emacs Lisp is a good choice if you need sophisticated string
+or pattern matching capabilities. The shell is also good at string and
+pattern matching; in addition, it allows powerful use of the system
+utilities. More conventional languages, such as C, C++, and Lisp, offer
+better facilities for system programming and for managing the complexity
+of large programs. Programs in these languages may require more lines
+of source code than the equivalent @code{awk} programs, but they are
+easier to maintain and usually run more efficiently.@refill
+
+@node Reading Files, Printing, Getting Started, Top
+@chapter Reading Input Files
+
+@cindex reading files
+@cindex input
+@cindex standard input
+@vindex FILENAME
+In the typical @code{awk} program, all input is read either from the
+standard input (by default the keyboard, but often a pipe from another
+command) or from files whose names you specify on the @code{awk} command
+line. If you specify input files, @code{awk} reads them in order, reading
+all the data from one before going on to the next. The name of the current
+input file can be found in the built-in variable @code{FILENAME}
+(@pxref{Built-in Variables}).@refill
+
+The input is read in units called records, and processed by the
+rules one record at a time. By default, each record is one line. Each
+record is split automatically into fields, to make it more
+convenient for a rule to work on its parts.
+
+On rare occasions you will need to use the @code{getline} command,
+which can do explicit input from any number of files
+(@pxref{Getline, ,Explicit Input with @code{getline}}).@refill
+
+@menu
+* Records:: Controlling how data is split into records.
+* Fields:: An introduction to fields.
+* Non-Constant Fields:: Non-constant Field Numbers.
+* Changing Fields:: Changing the Contents of a Field.
+* Field Separators:: The field separator and how to change it.
+* Constant Size:: Reading constant width data.
+* Multiple Line:: Reading multi-line records.
+* Getline:: Reading files under explicit program control
+ using the @code{getline} function.
+* Close Input:: Closing an input file (so you can read from
+ the beginning once more).
+@end menu
+
+@node Records, Fields, Reading Files, Reading Files
+@section How Input is Split into Records
+
+@cindex record separator
+The @code{awk} language divides its input into records and fields.
+Records are separated by a character called the @dfn{record separator}.
+By default, the record separator is the newline character, defining
+a record to be a single line of text.@refill
+
+@iftex
+@cindex changing the record separator
+@end iftex
+@vindex RS
+Sometimes you may want to use a different character to separate your
+records. You can use a different character by changing the built-in
+variable @code{RS}. The value of @code{RS} is a string that says how
+to separate records; the default value is @code{"\n"}, the string containing
+just a newline character. This is why records are, by default, single lines.
+
+@code{RS} can have any string as its value, but only the first character
+of the string is used as the record separator. The other characters are
+ignored. @code{RS} is exceptional in this regard; @code{awk} uses the
+full value of all its other built-in variables.@refill
+
+@ignore
+Someday this should be true!
+
+The value of @code{RS} is not limited to a one-character string. It can
+be any regular expression (@pxref{Regexp, ,Regular Expressions as Patterns}).
+In general, each record
+ends at the next string that matches the regular expression; the next
+record starts at the end of the matching string. This general rule is
+actually at work in the usual case, where @code{RS} contains just a
+newline: a record ends at the beginning of the next matching string (the
+next newline in the input) and the following record starts just after
+the end of this string (at the first character of the following line).
+The newline, since it matches @code{RS}, is not part of either record.@refill
+@end ignore
+
+You can change the value of @code{RS} in the @code{awk} program with the
+assignment operator, @samp{=} (@pxref{Assignment Ops, ,Assignment Expressions}).
+The new record-separator character should be enclosed in quotation marks to make
+a string constant. Often the right time to do this is at the beginning
+of execution, before any input has been processed, so that the very
+first record will be read with the proper separator. To do this, use
+the special @code{BEGIN} pattern
+(@pxref{BEGIN/END, ,@code{BEGIN} and @code{END} Special Patterns}). For
+example:@refill
+
+@example
+awk 'BEGIN @{ RS = "/" @} ; @{ print $0 @}' BBS-list
+@end example
+
+@noindent
+changes the value of @code{RS} to @code{"/"}, before reading any input.
+This is a string whose first character is a slash; as a result, records
+are separated by slashes. Then the input file is read, and the second
+rule in the @code{awk} program (the action with no pattern) prints each
+record. Since each @code{print} statement adds a newline at the end of
+its output, the effect of this @code{awk} program is to copy the input
+with each slash changed to a newline.
+
+Another way to change the record separator is on the command line,
+using the variable-assignment feature
+(@pxref{Command Line, ,Invoking @code{awk}}).@refill
+
+@example
+awk '@{ print $0 @}' RS="/" BBS-list
+@end example
+
+@noindent
+This sets @code{RS} to @samp{/} before processing @file{BBS-list}.
+
+Reaching the end of an input file terminates the current input record,
+even if the last character in the file is not the character in @code{RS}.
+
+@ignore
+@c merge the preceding paragraph and this stuff into one paragraph
+@c and put it in an `expert info' section.
+This produces correct behavior in the vast majority of cases, although
+the following (extreme) pipeline prints a surprising @samp{1}. (There
+is one field, consisting of a newline.)
+
+@example
+echo | awk 'BEGIN @{ RS = "a" @} ; @{ print NF @}'
+@end example
+
+@end ignore
+
+The empty string, @code{""} (a string of no characters), has a special meaning
+as the value of @code{RS}: it means that records are separated only
+by blank lines. @xref{Multiple Line, ,Multiple-Line Records}, for more details.
+
+@cindex number of records, @code{NR} or @code{FNR}
+@vindex NR
+@vindex FNR
+The @code{awk} utility keeps track of the number of records that have
+been read so far from the current input file. This value is stored in a
+built-in variable called @code{FNR}. It is reset to zero when a new
+file is started. Another built-in variable, @code{NR}, is the total
+number of input records read so far from all files. It starts at zero
+but is never automatically reset to zero.
+
+If you change the value of @code{RS} in the middle of an @code{awk} run,
+the new value is used to delimit subsequent records, but the record
+currently being processed (and records already processed) are not
+affected.
+
+@node Fields, Non-Constant Fields, Records, Reading Files
+@section Examining Fields
+
+@cindex examining fields
+@cindex fields
+@cindex accessing fields
+When @code{awk} reads an input record, the record is
+automatically separated or @dfn{parsed} by the interpreter into chunks
+called @dfn{fields}. By default, fields are separated by whitespace,
+like words in a line.
+Whitespace in @code{awk} means any string of one or more spaces and/or
+tabs; other characters such as newline, formfeed, and so on, that are
+considered whitespace by other languages are @emph{not} considered
+whitespace by @code{awk}.@refill
+
+The purpose of fields is to make it more convenient for you to refer to
+these pieces of the record. You don't have to use them---you can
+operate on the whole record if you wish---but fields are what make
+simple @code{awk} programs so powerful.
+
+@cindex @code{$} (field operator)
+@cindex operators, @code{$}
+To refer to a field in an @code{awk} program, you use a dollar-sign,
+@samp{$}, followed by the number of the field you want. Thus, @code{$1}
+refers to the first field, @code{$2} to the second, and so on. For
+example, suppose the following is a line of input:@refill
+
+@example
+This seems like a pretty nice example.
+@end example
+
+@noindent
+Here the first field, or @code{$1}, is @samp{This}; the second field, or
+@code{$2}, is @samp{seems}; and so on. Note that the last field,
+@code{$7}, is @samp{example.}. Because there is no space between the
+@samp{e} and the @samp{.}, the period is considered part of the seventh
+field.@refill
+
+No matter how many fields there are, the last field in a record can be
+represented by @code{$NF}. So, in the example above, @code{$NF} would
+be the same as @code{$7}, which is @samp{example.}. Why this works is
+explained below (@pxref{Non-Constant Fields, ,Non-constant Field Numbers}).
+If you try to refer to a field beyond the last one, such as @code{$8}
+when the record has only 7 fields, you get the empty string.@refill
+
+@vindex NF
+@cindex number of fields, @code{NF}
+Plain @code{NF}, with no @samp{$}, is a built-in variable whose value
+is the number of fields in the current record.
+
+@code{$0}, which looks like an attempt to refer to the zeroth field, is
+a special case: it represents the whole input record. This is what you
+would use if you weren't interested in fields.
+
+Here are some more examples:
+
+@example
+awk '$1 ~ /foo/ @{ print $0 @}' BBS-list
+@end example
+
+@noindent
+This example prints each record in the file @file{BBS-list} whose first
+field contains the string @samp{foo}. The operator @samp{~} is called a
+@dfn{matching operator} (@pxref{Comparison Ops, ,Comparison Expressions});
+it tests whether a string (here, the field @code{$1}) matches a given regular
+expression.@refill
+
+By contrast, the following example:
+
+@example
+awk '/foo/ @{ print $1, $NF @}' BBS-list
+@end example
+
+@noindent
+looks for @samp{foo} in @emph{the entire record} and prints the first
+field and the last field for each input record containing a
+match.@refill
+
+@node Non-Constant Fields, Changing Fields, Fields, Reading Files
+@section Non-constant Field Numbers
+
+The number of a field does not need to be a constant. Any expression in
+the @code{awk} language can be used after a @samp{$} to refer to a
+field. The value of the expression specifies the field number. If the
+value is a string, rather than a number, it is converted to a number.
+Consider this example:@refill
+
+@example
+awk '@{ print $NR @}'
+@end example
+
+@noindent
+Recall that @code{NR} is the number of records read so far: 1 in the
+first record, 2 in the second, etc. So this example prints the first
+field of the first record, the second field of the second record, and so
+on. For the twentieth record, field number 20 is printed; most likely,
+the record has fewer than 20 fields, so this prints a blank line.
+
+Here is another example of using expressions as field numbers:
+
+@example
+awk '@{ print $(2*2) @}' BBS-list
+@end example
+
+The @code{awk} language must evaluate the expression @code{(2*2)} and use
+its value as the number of the field to print. The @samp{*} sign
+represents multiplication, so the expression @code{2*2} evaluates to 4.
+The parentheses are used so that the multiplication is done before the
+@samp{$} operation; they are necessary whenever there is a binary
+operator in the field-number expression. This example, then, prints the
+hours of operation (the fourth field) for every line of the file
+@file{BBS-list}.@refill
+
+If the field number you compute is zero, you get the entire record.
+Thus, @code{$(2-2)} has the same value as @code{$0}. Negative field
+numbers are not allowed.
+
+The number of fields in the current record is stored in the built-in
+variable @code{NF} (@pxref{Built-in Variables}). The expression
+@code{$NF} is not a special feature: it is the direct consequence of
+evaluating @code{NF} and using its value as a field number.
+
+@node Changing Fields, Field Separators, Non-Constant Fields, Reading Files
+@section Changing the Contents of a Field
+
+@cindex field, changing contents of
+@cindex changing contents of a field
+@cindex assignment to fields
+You can change the contents of a field as seen by @code{awk} within an
+@code{awk} program; this changes what @code{awk} perceives as the
+current input record. (The actual input is untouched: @code{awk} never
+modifies the input file.)
+
+Consider this example:
+
+@smallexample
+awk '@{ $3 = $2 - 10; print $2, $3 @}' inventory-shipped
+@end smallexample
+
+@noindent
+The @samp{-} sign represents subtraction, so this program reassigns
+field three, @code{$3}, to be the value of field two minus ten,
+@code{$2 - 10}. (@xref{Arithmetic Ops, ,Arithmetic Operators}.)
+Then field two, and the new value for field three, are printed.
+
+In order for this to work, the text in field @code{$2} must make sense
+as a number; the string of characters must be converted to a number in
+order for the computer to do arithmetic on it. The number resulting
+from the subtraction is converted back to a string of characters which
+then becomes field three.
+@xref{Conversion, ,Conversion of Strings and Numbers}.@refill
+
+When you change the value of a field (as perceived by @code{awk}), the
+text of the input record is recalculated to contain the new field where
+the old one was. Therefore, @code{$0} changes to reflect the altered
+field. Thus,
+
+@smallexample
+awk '@{ $2 = $2 - 10; print $0 @}' inventory-shipped
+@end smallexample
+
+@noindent
+prints a copy of the input file, with 10 subtracted from the second
+field of each line.
+
+You can also assign contents to fields that are out of range. For
+example:
+
+@smallexample
+awk '@{ $6 = ($5 + $4 + $3 + $2) ; print $6 @}' inventory-shipped
+@end smallexample
+
+@noindent
+We've just created @code{$6}, whose value is the sum of fields
+@code{$2}, @code{$3}, @code{$4}, and @code{$5}. The @samp{+} sign
+represents addition. For the file @file{inventory-shipped}, @code{$6}
+represents the total number of parcels shipped for a particular month.
+
+Creating a new field changes the internal @code{awk} copy of the current
+input record---the value of @code{$0}. Thus, if you do @samp{print $0}
+after adding a field, the record printed includes the new field, with
+the appropriate number of field separators between it and the previously
+existing fields.
+
+This recomputation affects and is affected by several features not yet
+discussed, in particular, the @dfn{output field separator}, @code{OFS},
+which is used to separate the fields (@pxref{Output Separators}), and
+@code{NF} (the number of fields; @pxref{Fields, ,Examining Fields}).
+For example, the value of @code{NF} is set to the number of the highest
+field you create.@refill
+
+Note, however, that merely @emph{referencing} an out-of-range field
+does @emph{not} change the value of either @code{$0} or @code{NF}.
+Referencing an out-of-range field merely produces a null string. For
+example:@refill
+
+@smallexample
+if ($(NF+1) != "")
+ print "can't happen"
+else
+ print "everything is normal"
+@end smallexample
+
+@noindent
+should print @samp{everything is normal}, because @code{NF+1} is certain
+to be out of range. (@xref{If Statement, ,The @code{if} Statement},
+for more information about @code{awk}'s @code{if-else} statements.)@refill
+
+It is important to note that assigning to a field will change the
+value of @code{$0}, but will not change the value of @code{NF},
+even when you assign the null string to a field. For example:
+
+@smallexample
+echo a b c d | awk '@{ OFS = ":"; $2 = "" ; print ; print NF @}'
+@end smallexample
+
+@noindent
+prints
+
+@smallexample
+a::c:d
+4
+@end smallexample
+
+@noindent
+The field is still there, it just has an empty value. You can tell
+because there are two colons in a row.
+
+@node Field Separators, Constant Size, Changing Fields, Reading Files
+@section Specifying how Fields are Separated
+@vindex FS
+@cindex fields, separating
+@cindex field separator, @code{FS}
+@cindex @samp{-F} option
+
+(This section is rather long; it describes one of the most fundamental
+operations in @code{awk}. If you are a novice with @code{awk}, we
+recommend that you re-read this section after you have studied the
+section on regular expressions, @ref{Regexp, ,Regular Expressions as Patterns}.)
+
+The way @code{awk} splits an input record into fields is controlled by
+the @dfn{field separator}, which is a single character or a regular
+expression. @code{awk} scans the input record for matches for the
+separator; the fields themselves are the text between the matches. For
+example, if the field separator is @samp{oo}, then the following line:
+
+@smallexample
+moo goo gai pan
+@end smallexample
+
+@noindent
+would be split into three fields: @samp{m}, @samp{@ g} and @samp{@ gai@
+pan}.
+
+The field separator is represented by the built-in variable @code{FS}.
+Shell programmers take note! @code{awk} does not use the name @code{IFS}
+which is used by the shell.@refill
+
+You can change the value of @code{FS} in the @code{awk} program with the
+assignment operator, @samp{=} (@pxref{Assignment Ops, ,Assignment Expressions}).
+Often the right time to do this is at the beginning of execution,
+before any input has been processed, so that the very first record
+will be read with the proper separator. To do this, use the special
+@code{BEGIN} pattern
+(@pxref{BEGIN/END, ,@code{BEGIN} and @code{END} Special Patterns}).
+For example, here we set the value of @code{FS} to the string
+@code{","}:@refill
+
+@smallexample
+awk 'BEGIN @{ FS = "," @} ; @{ print $2 @}'
+@end smallexample
+
+@noindent
+Given the input line,
+
+@smallexample
+John Q. Smith, 29 Oak St., Walamazoo, MI 42139
+@end smallexample
+
+@noindent
+this @code{awk} program extracts the string @samp{@ 29 Oak St.}.
+
+@cindex field separator, choice of
+@cindex regular expressions as field separators
+Sometimes your input data will contain separator characters that don't
+separate fields the way you thought they would. For instance, the
+person's name in the example we've been using might have a title or
+suffix attached, such as @samp{John Q. Smith, LXIX}. From input
+containing such a name:
+
+@smallexample
+John Q. Smith, LXIX, 29 Oak St., Walamazoo, MI 42139
+@end smallexample
+
+@noindent
+the previous sample program would extract @samp{@ LXIX}, instead of
+@samp{@ 29 Oak St.}. If you were expecting the program to print the
+address, you would be surprised. So choose your data layout and
+separator characters carefully to prevent such problems.
+
+As you know, by default, fields are separated by whitespace sequences
+(spaces and tabs), not by single spaces: two spaces in a row do not
+delimit an empty field. The default value of the field separator is a
+string @w{@code{" "}} containing a single space. If this value were
+interpreted in the usual way, each space character would separate
+fields, so two spaces in a row would make an empty field between them.
+The reason this does not happen is that a single space as the value of
+@code{FS} is a special case: it is taken to specify the default manner
+of delimiting fields.
+
+If @code{FS} is any other single character, such as @code{","}, then
+each occurrence of that character separates two fields. Two consecutive
+occurrences delimit an empty field. If the character occurs at the
+beginning or the end of the line, that too delimits an empty field. The
+space character is the only single character which does not follow these
+rules.
+
+More generally, the value of @code{FS} may be a string containing any
+regular expression. Then each match in the record for the regular
+expression separates fields. For example, the assignment:@refill
+
+@smallexample
+FS = ", \t"
+@end smallexample
+
+@noindent
+makes every area of an input line that consists of a comma followed by a
+space and a tab, into a field separator. (@samp{\t} stands for a
+tab.)@refill
+
+For a less trivial example of a regular expression, suppose you want
+single spaces to separate fields the way single commas were used above.
+You can set @code{FS} to @w{@code{"[@ ]"}}. This regular expression
+matches a single space and nothing else.
+
+@c the following index entry is an overfull hbox. --mew 30jan1992
+@cindex field separator: on command line
+@cindex command line, setting @code{FS} on
+@code{FS} can be set on the command line. You use the @samp{-F} argument to
+do so. For example:
+
+@smallexample
+awk -F, '@var{program}' @var{input-files}
+@end smallexample
+
+@noindent
+sets @code{FS} to be the @samp{,} character. Notice that the argument uses
+a capital @samp{F}. Contrast this with @samp{-f}, which specifies a file
+containing an @code{awk} program. Case is significant in command options:
+the @samp{-F} and @samp{-f} options have nothing to do with each other.
+You can use both options at the same time to set the @code{FS} argument
+@emph{and} get an @code{awk} program from a file.@refill
+
+@c begin expert info
+The value used for the argument to @samp{-F} is processed in exactly the
+same way as assignments to the built-in variable @code{FS}. This means that
+if the field separator contains special characters, they must be escaped
+appropriately. For example, to use a @samp{\} as the field separator, you
+would have to type:
+
+@smallexample
+# same as FS = "\\"
+awk -F\\\\ '@dots{}' files @dots{}
+@end smallexample
+
+@noindent
+Since @samp{\} is used for quoting in the shell, @code{awk} will see
+@samp{-F\\}. Then @code{awk} processes the @samp{\\} for escape
+characters (@pxref{Constants, ,Constant Expressions}), finally yielding
+a single @samp{\} to be used for the field separator.
+@c end expert info
+
+As a special case, in compatibility mode
+(@pxref{Command Line, ,Invoking @code{awk}}), if the
+argument to @samp{-F} is @samp{t}, then @code{FS} is set to the tab
+character. (This is because if you type @samp{-F\t}, without the quotes,
+at the shell, the @samp{\} gets deleted, so @code{awk} figures that you
+really want your fields to be separated with tabs, and not @samp{t}s.
+Use @samp{-v FS="t"} on the command line if you really do want to separate
+your fields with @samp{t}s.)@refill
+
+For example, let's use an @code{awk} program file called @file{baud.awk}
+that contains the pattern @code{/300/}, and the action @samp{print $1}.
+Here is the program:
+
+@smallexample
+/300/ @{ print $1 @}
+@end smallexample
+
+Let's also set @code{FS} to be the @samp{-} character, and run the
+program on the file @file{BBS-list}. The following command prints a
+list of the names of the bulletin boards that operate at 300 baud and
+the first three digits of their phone numbers:@refill
+
+@smallexample
+awk -F- -f baud.awk BBS-list
+@end smallexample
+
+@noindent
+It produces this output:
+
+@smallexample
+aardvark 555
+alpo
+barfly 555
+bites 555
+camelot 555
+core 555
+fooey 555
+foot 555
+macfoo 555
+sdace 555
+sabafoo 555
+@end smallexample
+
+@noindent
+Note the second line of output. If you check the original file, you will
+see that the second line looked like this:
+
+@smallexample
+alpo-net 555-3412 2400/1200/300 A
+@end smallexample
+
+The @samp{-} as part of the system's name was used as the field
+separator, instead of the @samp{-} in the phone number that was
+originally intended. This demonstrates why you have to be careful in
+choosing your field and record separators.
+
+The following program searches the system password file, and prints
+the entries for users who have no password:
+
+@smallexample
+awk -F: '$2 == ""' /etc/passwd
+@end smallexample
+
+@noindent
+Here we use the @samp{-F} option on the command line to set the field
+separator. Note that fields in @file{/etc/passwd} are separated by
+colons. The second field represents a user's encrypted password, but if
+the field is empty, that user has no password.
+
+@c begin expert info
+According to the @sc{posix} standard, @code{awk} is supposed to behave
+as if each record is split into fields at the time that it is read.
+In particular, this means that you can change the value of @code{FS}
+after a record is read, but before any of the fields are referenced.
+The value of the fields (i.e. how they were split) should reflect the
+old value of @code{FS}, not the new one.
+
+However, many implementations of @code{awk} do not do this. Instead,
+they defer splitting the fields until a field reference actually happens,
+using the @emph{current} value of @code{FS}! This behavior can be difficult
+to diagnose. The following example illustrates the results of the two methods.
+(The @code{sed} command prints just the first line of @file{/etc/passwd}.)
+
+@smallexample
+sed 1q /etc/passwd | awk '@{ FS = ":" ; print $1 @}'
+@end smallexample
+
+@noindent
+will usually print
+
+@smallexample
+root
+@end smallexample
+
+@noindent
+on an incorrect implementation of @code{awk}, while @code{gawk}
+will print something like
+
+@smallexample
+root:nSijPlPhZZwgE:0:0:Root:/:
+@end smallexample
+@c end expert info
+
+@c begin expert info
+There is an important difference between the two cases of @samp{FS = @w{" "}}
+(a single blank) and @samp{FS = @w{"[ \t]+"}} (which is a regular expression
+matching one or more blanks or tabs). For both values of @code{FS}, fields
+are separated by runs of blanks and/or tabs. However, when the value of
+@code{FS} is @code{" "}, @code{awk} will strip leading and trailing whitespace
+from the record, and then decide where the fields are.
+
+For example, the following expression prints @samp{b}:
+
+@smallexample
+echo ' a b c d ' | awk '@{ print $2 @}'
+@end smallexample
+
+@noindent
+However, the following prints @samp{a}:
+
+@smallexample
+echo ' a b c d ' | awk 'BEGIN @{ FS = "[ \t]+" @} ; @{ print $2 @}'
+@end smallexample
+
+@noindent
+In this case, the first field is null.
+
+The stripping of leading and trailing whitespace also comes into
+play whenever @code{$0} is recomputed. For instance, this pipeline
+
+@smallexample
+echo ' a b c d' | awk '@{ print; $2 = $2; print @}'
+@end smallexample
+
+@noindent
+produces this output:
+
+@smallexample
+ a b c d
+a b c d
+@end smallexample
+
+@noindent
+The first @code{print} statement prints the record as it was read,
+with leading whitespace intact. The assignment to @code{$2} rebuilds
+@code{$0} by concatenating @code{$1} through @code{$NF} together,
+separated by the value of @code{OFS}. Since the leading whitespace
+was ignored when finding @code{$1}, it is not part of the new @code{$0}.
+Finally, the last @code{print} statement prints the new @code{$0}.
+@c end expert info
+
+The following table summarizes how fields are split, based on the
+value of @code{FS}.
+
+@table @code
+@item FS == " "
+Fields are separated by runs of whitespace. Leading and trailing
+whitespace are ignored. This is the default.
+
+@item FS == @var{any single character}
+Fields are separated by each occurrence of the character. Multiple
+successive occurrences delimit empty fields, as do leading and
+trailing occurrences.
+
+@item FS == @var{regexp}
+Fields are separated by occurrences of characters that match @var{regexp}.
+Leading and trailing matches of @var{regexp} delimit empty fields.
+@end table
+
+@node Constant Size, Multiple Line, Field Separators, Reading Files
+@section Reading Fixed-width Data
+
+(This section discusses an advanced, experimental feature. If you are
+a novice @code{awk} user, you may wish to skip it on the first reading.)
+
+@code{gawk} 2.13 introduced a new facility for dealing with fixed-width fields
+with no distinctive field separator. Data of this nature arises typically
+in one of at least two ways: the input for old FORTRAN programs where
+numbers are run together, and the output of programs that did not anticipate
+the use of their output as input for other programs.
+
+An example of the latter is a table where all the columns are lined up by
+the use of a variable number of spaces and @emph{empty fields are just
+spaces}. Clearly, @code{awk}'s normal field splitting based on @code{FS}
+will not work well in this case. (Although a portable @code{awk} program
+can use a series of @code{substr} calls on @code{$0}, this is awkward and
+inefficient for a large number of fields.)@refill
+
+The splitting of an input record into fixed-width fields is specified by
+assigning a string containing space-separated numbers to the built-in
+variable @code{FIELDWIDTHS}. Each number specifies the width of the field
+@emph{including} columns between fields. If you want to ignore the columns
+between fields, you can specify the width as a separate field that is
+subsequently ignored.
+
+The following data is the output of the @code{w} utility. It is useful
+to illustrate the use of @code{FIELDWIDTHS}.
+
+@smallexample
+ 10:06pm up 21 days, 14:04, 23 users
+User tty login@ idle JCPU PCPU what
+hzuo ttyV0 8:58pm 9 5 vi p24.tex
+hzang ttyV3 6:37pm 50 -csh
+eklye ttyV5 9:53pm 7 1 em thes.tex
+dportein ttyV6 8:17pm 1:47 -csh
+gierd ttyD3 10:00pm 1 elm
+dave ttyD4 9:47pm 4 4 w
+brent ttyp0 26Jun91 4:46 26:46 4:41 bash
+dave ttyq4 26Jun9115days 46 46 wnewmail
+@end smallexample
+
+The following program takes the above input, converts the idle time to
+number of seconds and prints out the first two fields and the calculated
+idle time. (This program uses a number of @code{awk} features that
+haven't been introduced yet.)@refill
+
+@smallexample
+BEGIN @{ FIELDWIDTHS = "9 6 10 6 7 7 35" @}
+NR > 2 @{
+ idle = $4
+ sub(/^ */, "", idle) # strip leading spaces
+ if (idle == "") idle = 0
+ if (idle ~ /:/) @{ split(idle, t, ":"); idle = t[1] * 60 + t[2] @}
+ if (idle ~ /days/) @{ idle *= 24 * 60 * 60 @}
+
+ print $1, $2, idle
+@}
+@end smallexample
+
+Here is the result of running the program on the data:
+
+@smallexample
+hzuo ttyV0 0
+hzang ttyV3 50
+eklye ttyV5 0
+dportein ttyV6 107
+gierd ttyD3 1
+dave ttyD4 0
+brent ttyp0 286
+dave ttyq4 1296000
+@end smallexample
+
+Another (possibly more practical) example of fixed-width input data
+would be the input from a deck of balloting cards. In some parts of
+the United States, voters make their choices by punching holes in computer
+cards. These cards are then processed to count the votes for any particular
+candidate or on any particular issue. Since a voter may choose not to
+vote on some issue, any column on the card may be empty. An @code{awk}
+program for processing such data could use the @code{FIELDWIDTHS} feature
+to simplify reading the data.@refill
+
+@c of course, getting gawk to run on a system with card readers is
+@c another story!
+
+This feature is still experimental, and will likely evolve over time.
+
+@node Multiple Line, Getline, Constant Size, Reading Files
+@section Multiple-Line Records
+
+@cindex multiple line records
+@cindex input, multiple line records
+@cindex reading files, multiple line records
+@cindex records, multiple line
+In some data bases, a single line cannot conveniently hold all the
+information in one entry. In such cases, you can use multi-line
+records.
+
+The first step in doing this is to choose your data format: when records
+are not defined as single lines, how do you want to define them?
+What should separate records?
+
+One technique is to use an unusual character or string to separate
+records. For example, you could use the formfeed character (written
+@code{\f} in @code{awk}, as in C) to separate them, making each record
+a page of the file. To do this, just set the variable @code{RS} to
+@code{"\f"} (a string containing the formfeed character). Any
+other character could equally well be used, as long as it won't be part
+of the data in a record.@refill
+
+@ignore
+Another technique is to have blank lines separate records. The string
+@code{"^\n+"} is a regular expression that matches any sequence of
+newlines starting at the beginning of a line---in other words, it
+matches a sequence of blank lines. If you set @code{RS} to this string,
+a record always ends at the first blank line encountered. In
+addition, a regular expression always matches the longest possible
+sequence when there is a choice. So the next record doesn't start until
+the first nonblank line that follows---no matter how many blank lines
+appear in a row, they are considered one record-separator.
+@end ignore
+
+Another technique is to have blank lines separate records. By a special
+dispensation, a null string as the value of @code{RS} indicates that
+records are separated by one or more blank lines. If you set @code{RS}
+to the null string, a record always ends at the first blank line
+encountered. And the next record doesn't start until the first nonblank
+line that follows---no matter how many blank lines appear in a row, they
+are considered one record-separator. (End of file is also considered
+a record separator.)@refill
+@c !!! This use of `end of file' is confusing. Needs to be clarified.
+
+The second step is to separate the fields in the record. One way to do
+this is to put each field on a separate line: to do this, just set the
+variable @code{FS} to the string @code{"\n"}. (This simple regular
+expression matches a single newline.)
+
+Another way to separate fields is to divide each of the lines into fields
+in the normal manner. This happens by default as a result of a special
+feature: when @code{RS} is set to the null string, the newline character
+@emph{always} acts as a field separator. This is in addition to whatever
+field separations result from @code{FS}.
+
+The original motivation for this special exception was probably so that
+you get useful behavior in the default case (i.e., @w{@code{FS == " "}}).
+This feature can be a problem if you really don't want the
+newline character to separate fields, since there is no way to
+prevent it. However, you can work around this by using the @code{split}
+function to break up the record manually
+(@pxref{String Functions, ,Built-in Functions for String Manipulation}).@refill
+
+@ignore
+Here are two ways to use records separated by blank lines and break each
+line into fields normally:
+
+@example
+awk 'BEGIN @{ RS = ""; FS = "[ \t\n]+" @} @{ print $1 @}' BBS-list
+
+@exdent @r{or}
+
+awk 'BEGIN @{ RS = "^\n+"; FS = "[ \t\n]+" @} @{ print $1 @}' BBS-list
+@end example
+@end ignore
+
+@ignore
+Here is how to use records separated by blank lines and break each
+line into fields normally:
+
+@example
+awk 'BEGIN @{ RS = ""; FS = "[ \t\n]+" @} ; @{ print $1 @}' BBS-list
+@end example
+@end ignore
+
+@node Getline, Close Input, Multiple Line, Reading Files
+@section Explicit Input with @code{getline}
+
+@findex getline
+@cindex input, explicit
+@cindex explicit input
+@cindex input, @code{getline} command
+@cindex reading files, @code{getline} command
+So far we have been getting our input files from @code{awk}'s main
+input stream---either the standard input (usually your terminal) or the
+files specified on the command line. The @code{awk} language has a
+special built-in command called @code{getline} that
+can be used to read input under your explicit control.@refill
+
+This command is quite complex and should @emph{not} be used by
+beginners. It is covered here because this is the chapter on input.
+The examples that follow the explanation of the @code{getline} command
+include material that has not been covered yet. Therefore, come back
+and study the @code{getline} command @emph{after} you have reviewed the
+rest of this manual and have a good knowledge of how @code{awk} works.
+
+@vindex ERRNO
+@cindex differences: @code{gawk} and @code{awk}
+@code{getline} returns 1 if it finds a record, and 0 if the end of the
+file is encountered. If there is some error in getting a record, such
+as a file that cannot be opened, then @code{getline} returns @minus{}1.
+In this case, @code{gawk} sets the variable @code{ERRNO} to a string
+describing the error that occurred.
+
+In the following examples, @var{command} stands for a string value that
+represents a shell command.
+
+@table @code
+@item getline
+The @code{getline} command can be used without arguments to read input
+from the current input file. All it does in this case is read the next
+input record and split it up into fields. This is useful if you've
+finished processing the current record, but you want to do some special
+processing @emph{right now} on the next record. Here's an
+example:@refill
+
+@example
+awk '@{
+ if (t = index($0, "/*")) @{
+ if (t > 1)
+ tmp = substr($0, 1, t - 1)
+ else
+ tmp = ""
+ u = index(substr($0, t + 2), "*/")
+ while (u == 0) @{
+ getline
+ t = -1
+ u = index($0, "*/")
+ @}
+ if (u <= length($0) - 2)
+ $0 = tmp substr($0, t + u + 3)
+ else
+ $0 = tmp
+ @}
+ print $0
+@}'
+@end example
+
+This @code{awk} program deletes all C-style comments, @samp{/* @dots{}
+*/}, from the input. By replacing the @samp{print $0} with other
+statements, you could perform more complicated processing on the
+decommented input, like searching for matches of a regular
+expression. (This program has a subtle problem---can you spot it?)
+
+@c the program to remove comments doesn't work if one
+@c comment ends and another begins on the same line. (Your
+@c idea for restart would be useful here). --- brennan@boeing.com
+
+This form of the @code{getline} command sets @code{NF} (the number of
+fields; @pxref{Fields, ,Examining Fields}), @code{NR} (the number of
+records read so far; @pxref{Records, ,How Input is Split into Records}),
+@code{FNR} (the number of records read from this input file), and the
+value of @code{$0}.
+
+@strong{Note:} the new value of @code{$0} is used in testing
+the patterns of any subsequent rules. The original value
+of @code{$0} that triggered the rule which executed @code{getline}
+is lost. By contrast, the @code{next} statement reads a new record
+but immediately begins processing it normally, starting with the first
+rule in the program. @xref{Next Statement, ,The @code{next} Statement}.
+
+@item getline @var{var}
+This form of @code{getline} reads a record into the variable @var{var}.
+This is useful when you want your program to read the next record from
+the current input file, but you don't want to subject the record to the
+normal input processing.
+
+For example, suppose the next line is a comment, or a special string,
+and you want to read it, but you must make certain that it won't trigger
+any rules. This version of @code{getline} allows you to read that line
+and store it in a variable so that the main
+read-a-line-and-check-each-rule loop of @code{awk} never sees it.
+
+The following example swaps every two lines of input. For example, given:
+
+@example
+wan
+tew
+free
+phore
+@end example
+
+@noindent
+it outputs:
+
+@example
+tew
+wan
+phore
+free
+@end example
+
+@noindent
+Here's the program:
+
+@example
+@group
+awk '@{
+ if ((getline tmp) > 0) @{
+ print tmp
+ print $0
+ @} else
+ print $0
+@}'
+@end group
+@end example
+
+The @code{getline} function used in this way sets only the variables
+@code{NR} and @code{FNR} (and of course, @var{var}). The record is not
+split into fields, so the values of the fields (including @code{$0}) and
+the value of @code{NF} do not change.@refill
+
+@item getline < @var{file}
+@cindex input redirection
+@cindex redirection of input
+This form of the @code{getline} function takes its input from the file
+@var{file}. Here @var{file} is a string-valued expression that
+specifies the file name. @samp{< @var{file}} is called a @dfn{redirection}
+since it directs input to come from a different place.
+
+This form is useful if you want to read your input from a particular
+file, instead of from the main input stream. For example, the following
+program reads its input record from the file @file{foo.input} when it
+encounters a first field with a value equal to 10 in the current input
+file.@refill
+
+@example
+awk '@{
+ if ($1 == 10) @{
+ getline < "foo.input"
+ print
+ @} else
+ print
+@}'
+@end example
+
+Since the main input stream is not used, the values of @code{NR} and
+@code{FNR} are not changed. But the record read is split into fields in
+the normal manner, so the values of @code{$0} and other fields are
+changed. So is the value of @code{NF}.
+
+This does not cause the record to be tested against all the patterns
+in the @code{awk} program, in the way that would happen if the record
+were read normally by the main processing loop of @code{awk}. However
+the new record is tested against any subsequent rules, just as when
+@code{getline} is used without a redirection.
+
+@item getline @var{var} < @var{file}
+This form of the @code{getline} function takes its input from the file
+@var{file} and puts it in the variable @var{var}. As above, @var{file}
+is a string-valued expression that specifies the file from which to read.
+
+In this version of @code{getline}, none of the built-in variables are
+changed, and the record is not split into fields. The only variable
+changed is @var{var}.
+
+For example, the following program copies all the input files to the
+output, except for records that say @w{@samp{@@include @var{filename}}}.
+Such a record is replaced by the contents of the file
+@var{filename}.@refill
+
+@example
+awk '@{
+ if (NF == 2 && $1 == "@@include") @{
+ while ((getline line < $2) > 0)
+ print line
+ close($2)
+ @} else
+ print
+@}'
+@end example
+
+Note here how the name of the extra input file is not built into
+the program; it is taken from the data, from the second field on
+the @samp{@@include} line.@refill
+
+The @code{close} function is called to ensure that if two identical
+@samp{@@include} lines appear in the input, the entire specified file is
+included twice. @xref{Close Input, ,Closing Input Files and Pipes}.@refill
+
+One deficiency of this program is that it does not process nested
+@samp{@@include} statements the way a true macro preprocessor would.
+
+@item @var{command} | getline
+You can @dfn{pipe} the output of a command into @code{getline}. A pipe is
+simply a way to link the output of one program to the input of another. In
+this case, the string @var{command} is run as a shell command and its output
+is piped into @code{awk} to be used as input. This form of @code{getline}
+reads one record from the pipe.
+
+For example, the following program copies input to output, except for lines
+that begin with @samp{@@execute}, which are replaced by the output produced by
+running the rest of the line as a shell command:
+
+@example
+awk '@{
+ if ($1 == "@@execute") @{
+ tmp = substr($0, 10)
+ while ((tmp | getline) > 0)
+ print
+ close(tmp)
+ @} else
+ print
+@}'
+@end example
+
+@noindent
+The @code{close} function is called to ensure that if two identical
+@samp{@@execute} lines appear in the input, the command is run for
+each one. @xref{Close Input, ,Closing Input Files and Pipes}.
+
+Given the input:
+
+@example
+foo
+bar
+baz
+@@execute who
+bletch
+@end example
+
+@noindent
+the program might produce:
+
+@example
+foo
+bar
+baz
+hack ttyv0 Jul 13 14:22
+hack ttyp0 Jul 13 14:23 (gnu:0)
+hack ttyp1 Jul 13 14:23 (gnu:0)
+hack ttyp2 Jul 13 14:23 (gnu:0)
+hack ttyp3 Jul 13 14:23 (gnu:0)
+bletch
+@end example
+
+@noindent
+Notice that this program ran the command @code{who} and printed the result.
+(If you try this program yourself, you will get different results, showing
+you who is logged in on your system.)
+
+This variation of @code{getline} splits the record into fields, sets the
+value of @code{NF} and recomputes the value of @code{$0}. The values of
+@code{NR} and @code{FNR} are not changed.
+
+@item @var{command} | getline @var{var}
+The output of the command @var{command} is sent through a pipe to
+@code{getline} and into the variable @var{var}. For example, the
+following program reads the current date and time into the variable
+@code{current_time}, using the @code{date} utility, and then
+prints it.@refill
+
+@example
+awk 'BEGIN @{
+ "date" | getline current_time
+ close("date")
+ print "Report printed on " current_time
+@}'
+@end example
+
+In this version of @code{getline}, none of the built-in variables are
+changed, and the record is not split into fields.
+@end table
+
+@node Close Input, , Getline, Reading Files
+@section Closing Input Files and Pipes
+@cindex closing input files and pipes
+@findex close
+
+If the same file name or the same shell command is used with
+@code{getline} more than once during the execution of an @code{awk}
+program, the file is opened (or the command is executed) only the first time.
+At that time, the first record of input is read from that file or command.
+The next time the same file or command is used in @code{getline}, another
+record is read from it, and so on.
+
+This implies that if you want to start reading the same file again from
+the beginning, or if you want to rerun a shell command (rather than
+reading more output from the command), you must take special steps.
+What you must do is use the @code{close} function, as follows:
+
+@example
+close(@var{filename})
+@end example
+
+@noindent
+or
+
+@example
+close(@var{command})
+@end example
+
+The argument @var{filename} or @var{command} can be any expression. Its
+value must exactly equal the string that was used to open the file or
+start the command---for example, if you open a pipe with this:
+
+@example
+"sort -r names" | getline foo
+@end example
+
+@noindent
+then you must close it with this:
+
+@example
+close("sort -r names")
+@end example
+
+Once this function call is executed, the next @code{getline} from that
+file or command will reopen the file or rerun the command.
+
+@iftex
+@vindex ERRNO
+@cindex differences: @code{gawk} and @code{awk}
+@end iftex
+@code{close} returns a value of zero if the close succeeded.
+Otherwise, the value will be non-zero.
+In this case, @code{gawk} sets the variable @code{ERRNO} to a string
+describing the error that occurred.
+
+@node Printing, One-liners, Reading Files, Top
+@chapter Printing Output
+
+@cindex printing
+@cindex output
+One of the most common things that actions do is to output or @dfn{print}
+some or all of the input. For simple output, use the @code{print}
+statement. For fancier formatting use the @code{printf} statement.
+Both are described in this chapter.
+
+@menu
+* Print:: The @code{print} statement.
+* Print Examples:: Simple examples of @code{print} statements.
+* Output Separators:: The output separators and how to change them.
+* OFMT:: Controlling Numeric Output With @code{print}.
+* Printf:: The @code{printf} statement.
+* Redirection:: How to redirect output to multiple
+ files and pipes.
+* Special Files:: File name interpretation in @code{gawk}.
+ @code{gawk} allows access to
+ inherited file descriptors.
+@end menu
+
+@node Print, Print Examples, Printing, Printing
+@section The @code{print} Statement
+@cindex @code{print} statement
+
+The @code{print} statement does output with simple, standardized
+formatting. You specify only the strings or numbers to be printed, in a
+list separated by commas. They are output, separated by single spaces,
+followed by a newline. The statement looks like this:
+
+@example
+print @var{item1}, @var{item2}, @dots{}
+@end example
+
+@noindent
+The entire list of items may optionally be enclosed in parentheses. The
+parentheses are necessary if any of the item expressions uses a
+relational operator; otherwise it could be confused with a redirection
+(@pxref{Redirection, ,Redirecting Output of @code{print} and @code{printf}}).
+The relational operators are @samp{==},
+@samp{!=}, @samp{<}, @samp{>}, @samp{>=}, @samp{<=}, @samp{~} and
+@samp{!~} (@pxref{Comparison Ops, ,Comparison Expressions}).@refill
+
+The items printed can be constant strings or numbers, fields of the
+current record (such as @code{$1}), variables, or any @code{awk}
+expressions. The @code{print} statement is completely general for
+computing @emph{what} values to print. With two exceptions,
+you cannot specify @emph{how} to print them---how many
+columns, whether to use exponential notation or not, and so on.
+(@xref{Output Separators}, and
+@ref{OFMT, ,Controlling Numeric Output with @code{print}}.)
+For that, you need the @code{printf} statement
+(@pxref{Printf, ,Using @code{printf} Statements for Fancier Printing}).@refill
+
+The simple statement @samp{print} with no items is equivalent to
+@samp{print $0}: it prints the entire current record. To print a blank
+line, use @samp{print ""}, where @code{""} is the null, or empty,
+string.
+
+To print a fixed piece of text, use a string constant such as
+@w{@code{"Hello there"}} as one item. If you forget to use the
+double-quote characters, your text will be taken as an @code{awk}
+expression, and you will probably get an error. Keep in mind that a
+space is printed between any two items.
+
+Most often, each @code{print} statement makes one line of output. But it
+isn't limited to one line. If an item value is a string that contains a
+newline, the newline is output along with the rest of the string. A
+single @code{print} can make any number of lines this way.
+
+@node Print Examples, Output Separators, Print, Printing
+@section Examples of @code{print} Statements
+
+Here is an example of printing a string that contains embedded newlines:
+
+@example
+awk 'BEGIN @{ print "line one\nline two\nline three" @}'
+@end example
+
+@noindent
+produces output like this:
+
+@example
+line one
+line two
+line three
+@end example
+
+Here is an example that prints the first two fields of each input record,
+with a space between them:
+
+@example
+awk '@{ print $1, $2 @}' inventory-shipped
+@end example
+
+@noindent
+Its output looks like this:
+
+@example
+Jan 13
+Feb 15
+Mar 15
+@dots{}
+@end example
+
+A common mistake in using the @code{print} statement is to omit the comma
+between two items. This often has the effect of making the items run
+together in the output, with no space. The reason for this is that
+juxtaposing two string expressions in @code{awk} means to concatenate
+them. For example, without the comma:
+
+@example
+awk '@{ print $1 $2 @}' inventory-shipped
+@end example
+
+@noindent
+prints:
+
+@example
+@group
+Jan13
+Feb15
+Mar15
+@dots{}
+@end group
+@end example
+
+Neither example's output makes much sense to someone unfamiliar with the
+file @file{inventory-shipped}. A heading line at the beginning would make
+it clearer. Let's add some headings to our table of months (@code{$1}) and
+green crates shipped (@code{$2}). We do this using the @code{BEGIN} pattern
+(@pxref{BEGIN/END, ,@code{BEGIN} and @code{END} Special Patterns}) to force the headings to be printed only once:
+
+@example
+awk 'BEGIN @{ print "Month Crates"
+ print "----- ------" @}
+ @{ print $1, $2 @}' inventory-shipped
+@end example
+
+@noindent
+Did you already guess what happens? This program prints the following:
+
+@example
+@group
+Month Crates
+----- ------
+Jan 13
+Feb 15
+Mar 15
+@dots{}
+@end group
+@end example
+
+@noindent
+The headings and the table data don't line up! We can fix this by printing
+some spaces between the two fields:
+
+@example
+awk 'BEGIN @{ print "Month Crates"
+ print "----- ------" @}
+ @{ print $1, " ", $2 @}' inventory-shipped
+@end example
+
+You can imagine that this way of lining up columns can get pretty
+complicated when you have many columns to fix. Counting spaces for two
+or three columns can be simple, but more than this and you can get
+``lost'' quite easily. This is why the @code{printf} statement was
+created (@pxref{Printf, ,Using @code{printf} Statements for Fancier Printing});
+one of its specialties is lining up columns of data.@refill
+
+@node Output Separators, OFMT, Print Examples, Printing
+@section Output Separators
+
+@cindex output field separator, @code{OFS}
+@vindex OFS
+@vindex ORS
+@cindex output record separator, @code{ORS}
+As mentioned previously, a @code{print} statement contains a list
+of items, separated by commas. In the output, the items are normally
+separated by single spaces. But they do not have to be spaces; a
+single space is only the default. You can specify any string of
+characters to use as the @dfn{output field separator} by setting the
+built-in variable @code{OFS}. The initial value of this variable
+is the string @w{@code{" "}}, that is, just a single space.@refill
+
+The output from an entire @code{print} statement is called an
+@dfn{output record}. Each @code{print} statement outputs one output
+record and then outputs a string called the @dfn{output record separator}.
+The built-in variable @code{ORS} specifies this string. The initial
+value of the variable is the string @code{"\n"} containing a newline
+character; thus, normally each @code{print} statement makes a separate line.
+
+You can change how output fields and records are separated by assigning
+new values to the variables @code{OFS} and/or @code{ORS}. The usual
+place to do this is in the @code{BEGIN} rule
+(@pxref{BEGIN/END, ,@code{BEGIN} and @code{END} Special Patterns}), so
+that it happens before any input is processed. You may also do this
+with assignments on the command line, before the names of your input
+files.@refill
+
+The following example prints the first and second fields of each input
+record separated by a semicolon, with a blank line added after each
+line:@refill
+
+@example
+@group
+awk 'BEGIN @{ OFS = ";"; ORS = "\n\n" @}
+ @{ print $1, $2 @}' BBS-list
+@end group
+@end example
+
+If the value of @code{ORS} does not contain a newline, all your output
+will be run together on a single line, unless you output newlines some
+other way.
+
+@node OFMT, Printf, Output Separators, Printing
+@section Controlling Numeric Output with @code{print}
+@vindex OFMT
+When you use the @code{print} statement to print numeric values,
+@code{awk} internally converts the number to a string of characters,
+and prints that string. @code{awk} uses the @code{sprintf} function
+to do this conversion. For now, it suffices to say that the @code{sprintf}
+function accepts a @dfn{format specification} that tells it how to format
+numbers (or strings), and that there are a number of different ways that
+numbers can be formatted. The different format specifications are discussed
+more fully in
+@ref{Printf, ,Using @code{printf} Statements for Fancier Printing}.@refill
+
+The built-in variable @code{OFMT} contains the default format specification
+that @code{print} uses with @code{sprintf} when it wants to convert a
+number to a string for printing. By supplying different format specifications
+as the value of @code{OFMT}, you can change how @code{print} will print
+your numbers. As a brief example:
+
+@example
+@group
+awk 'BEGIN @{ OFMT = "%d" # print numbers as integers
+ print 17.23 @}'
+@end group
+@end example
+
+@noindent
+will print @samp{17}.
+
+@node Printf, Redirection, OFMT, Printing
+@section Using @code{printf} Statements for Fancier Printing
+@cindex formatted output
+@cindex output, formatted
+
+If you want more precise control over the output format than
+@code{print} gives you, use @code{printf}. With @code{printf} you can
+specify the width to use for each item, and you can specify various
+stylistic choices for numbers (such as what radix to use, whether to
+print an exponent, whether to print a sign, and how many digits to print
+after the decimal point). You do this by specifying a string, called
+the @dfn{format string}, which controls how and where to print the other
+arguments.
+
+@menu
+* Basic Printf:: Syntax of the @code{printf} statement.
+* Control Letters:: Format-control letters.
+* Format Modifiers:: Format-specification modifiers.
+* Printf Examples:: Several examples.
+@end menu
+
+@node Basic Printf, Control Letters, Printf, Printf
+@subsection Introduction to the @code{printf} Statement
+
+@cindex @code{printf} statement, syntax of
+The @code{printf} statement looks like this:@refill
+
+@example
+printf @var{format}, @var{item1}, @var{item2}, @dots{}
+@end example
+
+@noindent
+The entire list of arguments may optionally be enclosed in parentheses. The
+parentheses are necessary if any of the item expressions uses a
+relational operator; otherwise it could be confused with a redirection
+(@pxref{Redirection, ,Redirecting Output of @code{print} and @code{printf}}).
+The relational operators are @samp{==},
+@samp{!=}, @samp{<}, @samp{>}, @samp{>=}, @samp{<=}, @samp{~} and
+@samp{!~} (@pxref{Comparison Ops, ,Comparison Expressions}).@refill
+
+@cindex format string
+The difference between @code{printf} and @code{print} is the argument
+@var{format}. This is an expression whose value is taken as a string; it
+specifies how to output each of the other arguments. It is called
+the @dfn{format string}.
+
+The format string is the same as in the @sc{ansi} C library function
+@code{printf}. Most of @var{format} is text to be output verbatim.
+Scattered among this text are @dfn{format specifiers}, one per item.
+Each format specifier says to output the next item at that place in the
+format.@refill
+
+The @code{printf} statement does not automatically append a newline to its
+output. It outputs only what the format specifies. So if you want
+a newline, you must include one in the format. The output separator
+variables @code{OFS} and @code{ORS} have no effect on @code{printf}
+statements.@refill
+
+@node Control Letters, Format Modifiers, Basic Printf, Printf
+@subsection Format-Control Letters
+@cindex @code{printf}, format-control characters
+@cindex format specifier
+
+A format specifier starts with the character @samp{%} and ends with a
+@dfn{format-control letter}; it tells the @code{printf} statement how
+to output one item. (If you actually want to output a @samp{%}, write
+@samp{%%}.) The format-control letter specifies what kind of value to
+print. The rest of the format specifier is made up of optional
+@dfn{modifiers} which are parameters such as the field width to use.@refill
+
+Here is a list of the format-control letters:
+
+@table @samp
+@item c
+This prints a number as an ASCII character. Thus, @samp{printf "%c",
+65} outputs the letter @samp{A}. The output for a string value is
+the first character of the string.
+
+@item d
+This prints a decimal integer.
+
+@item i
+This also prints a decimal integer.
+
+@item e
+This prints a number in scientific (exponential) notation.
+For example,
+
+@example
+printf "%4.3e", 1950
+@end example
+
+@noindent
+prints @samp{1.950e+03}, with a total of four significant figures of
+which three follow the decimal point. The @samp{4.3} are @dfn{modifiers},
+discussed below.
+
+@item f
+This prints a number in floating point notation.
+
+@item g
+This prints a number in either scientific notation or floating point
+notation, whichever uses fewer characters.
+@ignore
+From: gatech!ames!elroy!cit-vax!EQL.Caltech.Edu!rankin (Pat Rankin)
+
+In the description of printf formats (p.43), the information for %g
+is incorrect (mainly, it's too much of an oversimplification). It's
+wrong in the AWK book too, and in the gawk man page. I suggested to
+David Trueman before 2.13 was released that the latter be revised, so
+that it matched gawk's behavior (rather than trying to change gawk to
+match the docs ;-). The documented description is nice and simple, but
+it doesn't match the actual underlying behavior of %g in the various C
+run-time libraries that gawk relies on. The precision value for g format
+is different than for f and e formats, so it's inaccurate to say 'g' is
+the shorter of 'e' or 'f'. For 'g', precision represents the number of
+significant digits rather than the number of decimal places, and it has
+special rules about how to format numbers with range between 10E-1 and
+10E-4. All in all, it's pretty messy, and I had to add that clumsy
+GFMT_WORKAROUND code because the VMS run-time library doesn't conform to
+the ANSI-C specifications.
+@end ignore
+
+@item o
+This prints an unsigned octal integer.
+
+@item s
+This prints a string.
+
+@item x
+This prints an unsigned hexadecimal integer.
+
+@item X
+This prints an unsigned hexadecimal integer. However, for the values 10
+through 15, it uses the letters @samp{A} through @samp{F} instead of
+@samp{a} through @samp{f}.
+
+@item %
+This isn't really a format-control letter, but it does have a meaning
+when used after a @samp{%}: the sequence @samp{%%} outputs one
+@samp{%}. It does not consume an argument.
+@end table
+
+@node Format Modifiers, Printf Examples, Control Letters, Printf
+@subsection Modifiers for @code{printf} Formats
+
+@cindex @code{printf}, modifiers
+@cindex modifiers (in format specifiers)
+A format specification can also include @dfn{modifiers} that can control
+how much of the item's value is printed and how much space it gets. The
+modifiers come between the @samp{%} and the format-control letter. Here
+are the possible modifiers, in the order in which they may appear:
+
+@table @samp
+@item -
+The minus sign, used before the width modifier, says to left-justify
+the argument within its specified width. Normally the argument
+is printed right-justified in the specified width. Thus,
+
+@example
+printf "%-4s", "foo"
+@end example
+
+@noindent
+prints @samp{foo }.
+
+@item @var{width}
+This is a number representing the desired width of a field. Inserting any
+number between the @samp{%} sign and the format control character forces the
+field to be expanded to this width. The default way to do this is to
+pad with spaces on the left. For example,
+
+@example
+printf "%4s", "foo"
+@end example
+
+@noindent
+prints @samp{ foo}.
+
+The value of @var{width} is a minimum width, not a maximum. If the item
+value requires more than @var{width} characters, it can be as wide as
+necessary. Thus,
+
+@example
+printf "%4s", "foobar"
+@end example
+
+@noindent
+prints @samp{foobar}.
+
+Preceding the @var{width} with a minus sign causes the output to be
+padded with spaces on the right, instead of on the left.
+
+@item .@var{prec}
+This is a number that specifies the precision to use when printing.
+This specifies the number of digits you want printed to the right of the
+decimal point. For a string, it specifies the maximum number of
+characters from the string that should be printed.
+@end table
+
+The C library @code{printf}'s dynamic @var{width} and @var{prec}
+capability (for example, @code{"%*.*s"}) is supported. Instead of
+supplying explicit @var{width} and/or @var{prec} values in the format
+string, you pass them in the argument list. For example:@refill
+
+@example
+w = 5
+p = 3
+s = "abcdefg"
+printf "<%*.*s>\n", w, p, s
+@end example
+
+@noindent
+is exactly equivalent to
+
+@example
+s = "abcdefg"
+printf "<%5.3s>\n", s
+@end example
+
+@noindent
+Both programs output @samp{@w{<@bullet{}@bullet{}abc>}}. (We have
+used the bullet symbol ``@bullet{}'' to represent a space, to clearly
+show you that there are two spaces in the output.)@refill
+
+Earlier versions of @code{awk} did not support this capability. You may
+simulate it by using concatenation to build up the format string,
+like so:@refill
+
+@example
+w = 5
+p = 3
+s = "abcdefg"
+printf "<%" w "." p "s>\n", s
+@end example
+
+@noindent
+This is not particularly easy to read, however.
+
+@node Printf Examples, , Format Modifiers, Printf
+@subsection Examples of Using @code{printf}
+
+Here is how to use @code{printf} to make an aligned table:
+
+@example
+awk '@{ printf "%-10s %s\n", $1, $2 @}' BBS-list
+@end example
+
+@noindent
+prints the names of bulletin boards (@code{$1}) of the file
+@file{BBS-list} as a string of 10 characters, left justified. It also
+prints the phone numbers (@code{$2}) afterward on the line. This
+produces an aligned two-column table of names and phone numbers:@refill
+
+@example
+@group
+aardvark 555-5553
+alpo-net 555-3412
+barfly 555-7685
+bites 555-1675
+camelot 555-0542
+core 555-2912
+fooey 555-1234
+foot 555-6699
+macfoo 555-6480
+sdace 555-3430
+sabafoo 555-2127
+@end group
+@end example
+
+Did you notice that we did not specify that the phone numbers be printed
+as numbers? They had to be printed as strings because the numbers are
+separated by a dash. This dash would be interpreted as a minus sign if
+we had tried to print the phone numbers as numbers. This would have led
+to some pretty confusing results.
+
+We did not specify a width for the phone numbers because they are the
+last things on their lines. We don't need to put spaces after them.
+
+We could make our table look even nicer by adding headings to the tops
+of the columns. To do this, use the @code{BEGIN} pattern
+(@pxref{BEGIN/END, ,@code{BEGIN} and @code{END} Special Patterns})
+to force the header to be printed only once, at the beginning of
+the @code{awk} program:@refill
+
+@example
+@group
+awk 'BEGIN @{ print "Name Number"
+ print "---- ------" @}
+ @{ printf "%-10s %s\n", $1, $2 @}' BBS-list
+@end group
+@end example
+
+Did you notice that we mixed @code{print} and @code{printf} statements in
+the above example? We could have used just @code{printf} statements to get
+the same results:
+
+@example
+@group
+awk 'BEGIN @{ printf "%-10s %s\n", "Name", "Number"
+ printf "%-10s %s\n", "----", "------" @}
+ @{ printf "%-10s %s\n", $1, $2 @}' BBS-list
+@end group
+@end example
+
+@noindent
+By outputting each column heading with the same format specification
+used for the elements of the column, we have made sure that the headings
+are aligned just like the columns.
+
+The fact that the same format specification is used three times can be
+emphasized by storing it in a variable, like this:
+
+@example
+awk 'BEGIN @{ format = "%-10s %s\n"
+ printf format, "Name", "Number"
+ printf format, "----", "------" @}
+ @{ printf format, $1, $2 @}' BBS-list
+@end example
+
+See if you can use the @code{printf} statement to line up the headings and
+table data for our @file{inventory-shipped} example covered earlier in the
+section on the @code{print} statement
+(@pxref{Print, ,The @code{print} Statement}).@refill
+
+@node Redirection, Special Files, Printf, Printing
+@section Redirecting Output of @code{print} and @code{printf}
+
+@cindex output redirection
+@cindex redirection of output
+So far we have been dealing only with output that prints to the standard
+output, usually your terminal. Both @code{print} and @code{printf} can
+also send their output to other places.
+This is called @dfn{redirection}.@refill
+
+A redirection appears after the @code{print} or @code{printf} statement.
+Redirections in @code{awk} are written just like redirections in shell
+commands, except that they are written inside the @code{awk} program.
+
+@menu
+* File/Pipe Redirection:: Redirecting Output to Files and Pipes.
+* Close Output:: How to close output files and pipes.
+@end menu
+
+@node File/Pipe Redirection, Close Output, Redirection, Redirection
+@subsection Redirecting Output to Files and Pipes
+
+Here are the three forms of output redirection. They are all shown for
+the @code{print} statement, but they work identically for @code{printf}
+also.@refill
+
+@table @code
+@item print @var{items} > @var{output-file}
+This type of redirection prints the items onto the output file
+@var{output-file}. The file name @var{output-file} can be any
+expression. Its value is changed to a string and then used as a
+file name (@pxref{Expressions, ,Expressions as Action Statements}).@refill
+
+When this type of redirection is used, the @var{output-file} is erased
+before the first output is written to it. Subsequent writes do not
+erase @var{output-file}, but append to it. If @var{output-file} does
+not exist, then it is created.@refill
+
+For example, here is how one @code{awk} program can write a list of
+BBS names to a file @file{name-list} and a list of phone numbers to a
+file @file{phone-list}. Each output file contains one name or number
+per line.
+
+@smallexample
+awk '@{ print $2 > "phone-list"
+ print $1 > "name-list" @}' BBS-list
+@end smallexample
+
+@item print @var{items} >> @var{output-file}
+This type of redirection prints the items onto the output file
+@var{output-file}. The difference between this and the
+single-@samp{>} redirection is that the old contents (if any) of
+@var{output-file} are not erased. Instead, the @code{awk} output is
+appended to the file.
+
+@cindex pipes for output
+@cindex output, piping
+@item print @var{items} | @var{command}
+It is also possible to send output through a @dfn{pipe} instead of into a
+file. This type of redirection opens a pipe to @var{command} and writes
+the values of @var{items} through this pipe, to another process created
+to execute @var{command}.@refill
+
+The redirection argument @var{command} is actually an @code{awk}
+expression. Its value is converted to a string, whose contents give the
+shell command to be run.
+
+For example, this produces two files, one unsorted list of BBS names
+and one list sorted in reverse alphabetical order:
+
+@smallexample
+awk '@{ print $1 > "names.unsorted"
+ print $1 | "sort -r > names.sorted" @}' BBS-list
+@end smallexample
+
+Here the unsorted list is written with an ordinary redirection while
+the sorted list is written by piping through the @code{sort} utility.
+
+Here is an example that uses redirection to mail a message to a mailing
+list @samp{bug-system}. This might be useful when trouble is encountered
+in an @code{awk} script run periodically for system maintenance.
+
+@smallexample
+report = "mail bug-system"
+print "Awk script failed:", $0 | report
+print "at record number", FNR, "of", FILENAME | report
+close(report)
+@end smallexample
+
+We call the @code{close} function here because it's a good idea to close
+the pipe as soon as all the intended output has been sent to it.
+@xref{Close Output, ,Closing Output Files and Pipes}, for more information
+on this. This example also illustrates the use of a variable to represent
+a @var{file} or @var{command}: it is not necessary to always
+use a string constant. Using a variable is generally a good idea,
+since @code{awk} requires you to spell the string value identically
+every time.
+@end table
+
+Redirecting output using @samp{>}, @samp{>>}, or @samp{|} asks the system
+to open a file or pipe only if the particular @var{file} or @var{command}
+you've specified has not already been written to by your program, or if
+it has been closed since it was last written to.@refill
+
+@node Close Output, , File/Pipe Redirection, Redirection
+@subsection Closing Output Files and Pipes
+@cindex closing output files and pipes
+@findex close
+
+When a file or pipe is opened, the file name or command associated with
+it is remembered by @code{awk} and subsequent writes to the same file or
+command are appended to the previous writes. The file or pipe stays
+open until @code{awk} exits. This is usually convenient.
+
+Sometimes there is a reason to close an output file or pipe earlier
+than that. To do this, use the @code{close} function, as follows:
+
+@example
+close(@var{filename})
+@end example
+
+@noindent
+or
+
+@example
+close(@var{command})
+@end example
+
+The argument @var{filename} or @var{command} can be any expression.
+Its value must exactly equal the string used to open the file or pipe
+to begin with---for example, if you open a pipe with this:
+
+@example
+print $1 | "sort -r > names.sorted"
+@end example
+
+@noindent
+then you must close it with this:
+
+@example
+close("sort -r > names.sorted")
+@end example
+
+Here are some reasons why you might need to close an output file:
+
+@itemize @bullet
+@item
+To write a file and read it back later on in the same @code{awk}
+program. Close the file when you are finished writing it; then
+you can start reading it with @code{getline}
+(@pxref{Getline, ,Explicit Input with @code{getline}}).@refill
+
+@item
+To write numerous files, successively, in the same @code{awk}
+program. If you don't close the files, eventually you may exceed a
+system limit on the number of open files in one process. So close
+each one when you are finished writing it.
+
+@item
+To make a command finish. When you redirect output through a pipe,
+the command reading the pipe normally continues to try to read input
+as long as the pipe is open. Often this means the command cannot
+really do its work until the pipe is closed. For example, if you
+redirect output to the @code{mail} program, the message is not
+actually sent until the pipe is closed.
+
+@item
+To run the same program a second time, with the same arguments.
+This is not the same thing as giving more input to the first run!
+
+For example, suppose you pipe output to the @code{mail} program. If you
+output several lines redirected to this pipe without closing it, they make
+a single message of several lines. By contrast, if you close the pipe
+after each line of output, then each line makes a separate message.
+@end itemize
+
+@iftex
+@vindex ERRNO
+@cindex differences: @code{gawk} and @code{awk}
+@end iftex
+@code{close} returns a value of zero if the close succeeded.
+Otherwise, the value will be non-zero.
+In this case, @code{gawk} sets the variable @code{ERRNO} to a string
+describing the error that occurred.
+
+@node Special Files, , Redirection, Printing
+@section Standard I/O Streams
+@cindex standard input
+@cindex standard output
+@cindex standard error output
+@cindex file descriptors
+
+Running programs conventionally have three input and output streams
+already available to them for reading and writing. These are known as
+the @dfn{standard input}, @dfn{standard output}, and @dfn{standard error
+output}. These streams are, by default, terminal input and output, but
+they are often redirected with the shell, via the @samp{<}, @samp{<<},
+@samp{>}, @samp{>>}, @samp{>&} and @samp{|} operators. Standard error
+is used only for writing error messages; the reason we have two separate
+streams, standard output and standard error, is so that they can be
+redirected separately.
+
+@iftex
+@cindex differences: @code{gawk} and @code{awk}
+@end iftex
+In other implementations of @code{awk}, the only way to write an error
+message to standard error in an @code{awk} program is as follows:
+
+@smallexample
+print "Serious error detected!\n" | "cat 1>&2"
+@end smallexample
+
+@noindent
+This works by opening a pipeline to a shell command which can access the
+standard error stream which it inherits from the @code{awk} process.
+This is far from elegant, and is also inefficient, since it requires a
+separate process. So people writing @code{awk} programs have often
+neglected to do this. Instead, they have sent the error messages to the
+terminal, like this:
+
+@smallexample
+@group
+NF != 4 @{
+ printf("line %d skipped: doesn't have 4 fields\n", FNR) > "/dev/tty"
+@}
+@end group
+@end smallexample
+
+@noindent
+This has the same effect most of the time, but not always: although the
+standard error stream is usually the terminal, it can be redirected, and
+when that happens, writing to the terminal is not correct. In fact, if
+@code{awk} is run from a background job, it may not have a terminal at all.
+Then opening @file{/dev/tty} will fail.
+
+@code{gawk} provides special file names for accessing the three standard
+streams. When you redirect input or output in @code{gawk}, if the file name
+matches one of these special names, then @code{gawk} directly uses the
+stream it stands for.
+
+@cindex @file{/dev/stdin}
+@cindex @file{/dev/stdout}
+@cindex @file{/dev/stderr}
+@cindex @file{/dev/fd/}
+@table @file
+@item /dev/stdin
+The standard input (file descriptor 0).
+
+@item /dev/stdout
+The standard output (file descriptor 1).
+
+@item /dev/stderr
+The standard error output (file descriptor 2).
+
+@item /dev/fd/@var{N}
+The file associated with file descriptor @var{N}. Such a file must have
+been opened by the program initiating the @code{awk} execution (typically
+the shell). Unless you take special pains, only descriptors 0, 1 and 2
+are available.
+@end table
+
+The file names @file{/dev/stdin}, @file{/dev/stdout}, and @file{/dev/stderr}
+are aliases for @file{/dev/fd/0}, @file{/dev/fd/1}, and @file{/dev/fd/2},
+respectively, but they are more self-explanatory.
+
+The proper way to write an error message in a @code{gawk} program
+is to use @file{/dev/stderr}, like this:
+
+@smallexample
+NF != 4 @{
+ printf("line %d skipped: doesn't have 4 fields\n", FNR) > "/dev/stderr"
+@}
+@end smallexample
+
+@code{gawk} also provides special file names that give access to information
+about the running @code{gawk} process. Each of these ``files'' provides
+a single record of information. To read them more than once, you must
+first close them with the @code{close} function
+(@pxref{Close Input, ,Closing Input Files and Pipes}).
+The filenames are:
+
+@cindex @file{/dev/pid}
+@cindex @file{/dev/pgrpid}
+@cindex @file{/dev/ppid}
+@cindex @file{/dev/user}
+@table @file
+@item /dev/pid
+Reading this file returns the process ID of the current process,
+in decimal, terminated with a newline.
+
+@item /dev/ppid
+Reading this file returns the parent process ID of the current process,
+in decimal, terminated with a newline.
+
+@item /dev/pgrpid
+Reading this file returns the process group ID of the current process,
+in decimal, terminated with a newline.
+
+@item /dev/user
+Reading this file returns a single record terminated with a newline.
+The fields are separated with blanks. The fields represent the
+following information:
+
+@table @code
+@item $1
+The value of the @code{getuid} system call.
+
+@item $2
+The value of the @code{geteuid} system call.
+
+@item $3
+The value of the @code{getgid} system call.
+
+@item $4
+The value of the @code{getegid} system call.
+@end table
+
+If there are any additional fields, they are the group IDs returned by
+@code{getgroups} system call.
+(Multiple groups may not be supported on all systems.)@refill
+@end table
+
+These special file names may be used on the command line as data
+files, as well as for I/O redirections within an @code{awk} program.
+They may not be used as source files with the @samp{-f} option.
+
+Recognition of these special file names is disabled if @code{gawk} is in
+compatibility mode (@pxref{Command Line, ,Invoking @code{awk}}).
+
+@quotation
+@strong{Caution}: Unless your system actually has a @file{/dev/fd} directory
+(or any of the other above listed special files),
+the interpretation of these file names is done by @code{gawk} itself.
+For example, using @samp{/dev/fd/4} for output will actually write on
+file descriptor 4, and not on a new file descriptor that was @code{dup}'ed
+from file descriptor 4. Most of the time this does not matter; however, it
+is important to @emph{not} close any of the files related to file descriptors
+0, 1, and 2. If you do close one of these files, unpredictable behavior
+will result.
+@end quotation
+
+@node One-liners, Patterns, Printing, Top
+@chapter Useful ``One-liners''
+
+@cindex one-liners
+Useful @code{awk} programs are often short, just a line or two. Here is a
+collection of useful, short programs to get you started. Some of these
+programs contain constructs that haven't been covered yet. The description
+of the program will give you a good idea of what is going on, but please
+read the rest of the manual to become an @code{awk} expert!
+
+@c Per suggestions from Michal Jaegermann
+@ifinfo
+Since you are reading this in Info, each line of the example code is
+enclosed in quotes, to represent text that you would type literally.
+The examples themselves represent shell commands that use single quotes
+to keep the shell from interpreting the contents of the program.
+When reading the examples, focus on the text between the open and close
+quotes.
+@end ifinfo
+
+@table @code
+@item awk '@{ if (NF > max) max = NF @}
+@itemx @ @ @ @ @ END @{ print max @}'
+This program prints the maximum number of fields on any input line.
+
+@item awk 'length($0) > 80'
+This program prints every line longer than 80 characters. The sole
+rule has a relational expression as its pattern, and has no action (so the
+default action, printing the record, is used).
+
+@item awk 'NF > 0'
+This program prints every line that has at least one field. This is an
+easy way to delete blank lines from a file (or rather, to create a new
+file similar to the old file but from which the blank lines have been
+deleted).
+
+@item awk '@{ if (NF > 0) print @}'
+This program also prints every line that has at least one field. Here we
+allow the rule to match every line, then decide in the action whether
+to print.
+
+@item awk@ 'BEGIN@ @{@ for (i = 1; i <= 7; i++)
+@itemx @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ print int(101 * rand()) @}'
+This program prints 7 random numbers from 0 to 100, inclusive.
+
+@item ls -l @var{files} | awk '@{ x += $4 @} ; END @{ print "total bytes: " x @}'
+This program prints the total number of bytes used by @var{files}.
+
+@item expand@ @var{file}@ |@ awk@ '@{ if (x < length()) x = length() @}
+@itemx @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ END @{ print "maximum line length is " x @}'
+This program prints the maximum line length of @var{file}. The input
+is piped through the @code{expand} program to change tabs into spaces,
+so the widths compared are actually the right-margin columns.
+
+@item awk 'BEGIN @{ FS = ":" @}
+@itemx @ @ @ @ @ @{ print $1 | "sort" @}' /etc/passwd
+This program prints a sorted list of the login names of all users.
+
+@item awk '@{ nlines++ @}
+@itemx @ @ @ @ @ END@ @{ print nlines @}'
+This programs counts lines in a file.
+
+@item awk 'END @{ print NR @}'
+This program also counts lines in a file, but lets @code{awk} do the work.
+
+@item awk '@{ print NR, $0 @}'
+This program adds line numbers to all its input files,
+similar to @samp{cat -n}.
+@end table
+
+@node Patterns, Actions, One-liners, Top
+@chapter Patterns
+@cindex pattern, definition of
+
+Patterns in @code{awk} control the execution of rules: a rule is
+executed when its pattern matches the current input record. This
+chapter tells all about how to write patterns.
+
+@menu
+* Kinds of Patterns:: A list of all kinds of patterns.
+ The following subsections describe
+ them in detail.
+* Regexp:: Regular expressions such as @samp{/foo/}.
+* Comparison Patterns:: Comparison expressions such as @code{$1 > 10}.
+* Boolean Patterns:: Combining comparison expressions.
+* Expression Patterns:: Any expression can be used as a pattern.
+* Ranges:: Pairs of patterns specify record ranges.
+* BEGIN/END:: Specifying initialization and cleanup rules.
+* Empty:: The empty pattern, which matches every record.
+@end menu
+
+@node Kinds of Patterns, Regexp, Patterns, Patterns
+@section Kinds of Patterns
+@cindex patterns, types of
+
+Here is a summary of the types of patterns supported in @code{awk}.
+@c At the next rewrite, check to see that this order matches the
+@c order in the text. It might not matter to a reader, but it's good
+@c style. Also, it might be nice to mention all the topics of sections
+@c that follow in this list; that way people can scan and know when to
+@c expect a specific topic. Specifically please also make an entry
+@c for Boolean operators as patterns in the right place. --mew
+
+@table @code
+@item /@var{regular expression}/
+A regular expression as a pattern. It matches when the text of the
+input record fits the regular expression.
+(@xref{Regexp, ,Regular Expressions as Patterns}.)@refill
+
+@item @var{expression}
+A single expression. It matches when its value, converted to a number,
+is nonzero (if a number) or nonnull (if a string).
+(@xref{Expression Patterns, ,Expressions as Patterns}.)@refill
+
+@item @var{pat1}, @var{pat2}
+A pair of patterns separated by a comma, specifying a range of records.
+(@xref{Ranges, ,Specifying Record Ranges with Patterns}.)
+
+@item BEGIN
+@itemx END
+Special patterns to supply start-up or clean-up information to
+@code{awk}. (@xref{BEGIN/END, ,@code{BEGIN} and @code{END} Special Patterns}.)
+
+@item @var{null}
+The empty pattern matches every input record.
+(@xref{Empty, ,The Empty Pattern}.)@refill
+@end table
+
+
+@node Regexp, Comparison Patterns, Kinds of Patterns, Patterns
+@section Regular Expressions as Patterns
+@cindex pattern, regular expressions
+@cindex regexp
+@cindex regular expressions as patterns
+
+A @dfn{regular expression}, or @dfn{regexp}, is a way of describing a
+class of strings. A regular expression enclosed in slashes (@samp{/})
+is an @code{awk} pattern that matches every input record whose text
+belongs to that class.
+
+The simplest regular expression is a sequence of letters, numbers, or
+both. Such a regexp matches any string that contains that sequence.
+Thus, the regexp @samp{foo} matches any string containing @samp{foo}.
+Therefore, the pattern @code{/foo/} matches any input record containing
+@samp{foo}. Other kinds of regexps let you specify more complicated
+classes of strings.
+
+@menu
+* Regexp Usage:: How to Use Regular Expressions
+* Regexp Operators:: Regular Expression Operators
+* Case-sensitivity:: How to do case-insensitive matching.
+@end menu
+
+@node Regexp Usage, Regexp Operators, Regexp, Regexp
+@subsection How to Use Regular Expressions
+
+A regular expression can be used as a pattern by enclosing it in
+slashes. Then the regular expression is matched against the
+entire text of each record. (Normally, it only needs
+to match some part of the text in order to succeed.) For example, this
+prints the second field of each record that contains @samp{foo} anywhere:
+
+@example
+awk '/foo/ @{ print $2 @}' BBS-list
+@end example
+
+@cindex regular expression matching operators
+@cindex string-matching operators
+@cindex operators, string-matching
+@cindex operators, regexp matching
+@cindex regexp search operators
+Regular expressions can also be used in comparison expressions. Then
+you can specify the string to match against; it need not be the entire
+current input record. These comparison expressions can be used as
+patterns or in @code{if}, @code{while}, @code{for}, and @code{do} statements.
+
+@table @code
+@item @var{exp} ~ /@var{regexp}/
+This is true if the expression @var{exp} (taken as a character string)
+is matched by @var{regexp}. The following example matches, or selects,
+all input records with the upper-case letter @samp{J} somewhere in the
+first field:@refill
+
+@example
+awk '$1 ~ /J/' inventory-shipped
+@end example
+
+So does this:
+
+@example
+awk '@{ if ($1 ~ /J/) print @}' inventory-shipped
+@end example
+
+@item @var{exp} !~ /@var{regexp}/
+This is true if the expression @var{exp} (taken as a character string)
+is @emph{not} matched by @var{regexp}. The following example matches,
+or selects, all input records whose first field @emph{does not} contain
+the upper-case letter @samp{J}:@refill
+
+@example
+awk '$1 !~ /J/' inventory-shipped
+@end example
+@end table
+
+@cindex computed regular expressions
+@cindex regular expressions, computed
+@cindex dynamic regular expressions
+The right hand side of a @samp{~} or @samp{!~} operator need not be a
+constant regexp (i.e., a string of characters between slashes). It may
+be any expression. The expression is evaluated, and converted if
+necessary to a string; the contents of the string are used as the
+regexp. A regexp that is computed in this way is called a @dfn{dynamic
+regexp}. For example:
+
+@example
+identifier_regexp = "[A-Za-z_][A-Za-z_0-9]+"
+$0 ~ identifier_regexp
+@end example
+
+@noindent
+sets @code{identifier_regexp} to a regexp that describes @code{awk}
+variable names, and tests if the input record matches this regexp.
+
+@node Regexp Operators, Case-sensitivity, Regexp Usage, Regexp
+@subsection Regular Expression Operators
+@cindex metacharacters
+@cindex regular expression metacharacters
+
+You can combine regular expressions with the following characters,
+called @dfn{regular expression operators}, or @dfn{metacharacters}, to
+increase the power and versatility of regular expressions.
+
+Here is a table of metacharacters. All characters not listed in the
+table stand for themselves.
+
+@table @code
+@item ^
+This matches the beginning of the string or the beginning of a line
+within the string. For example:
+
+@example
+^@@chapter
+@end example
+
+@noindent
+matches the @samp{@@chapter} at the beginning of a string, and can be used
+to identify chapter beginnings in Texinfo source files.
+
+@item $
+This is similar to @samp{^}, but it matches only at the end of a string
+or the end of a line within the string. For example:
+
+@example
+p$
+@end example
+
+@noindent
+matches a record that ends with a @samp{p}.
+
+@item .
+This matches any single character except a newline. For example:
+
+@example
+.P
+@end example
+
+@noindent
+matches any single character followed by a @samp{P} in a string. Using
+concatenation we can make regular expressions like @samp{U.A}, which
+matches any three-character sequence that begins with @samp{U} and ends
+with @samp{A}.
+
+@item [@dots{}]
+This is called a @dfn{character set}. It matches any one of the
+characters that are enclosed in the square brackets. For example:
+
+@example
+[MVX]
+@end example
+
+@noindent
+matches any one of the characters @samp{M}, @samp{V}, or @samp{X} in a
+string.@refill
+
+Ranges of characters are indicated by using a hyphen between the beginning
+and ending characters, and enclosing the whole thing in brackets. For
+example:@refill
+
+@example
+[0-9]
+@end example
+
+@noindent
+matches any digit.
+
+To include the character @samp{\}, @samp{]}, @samp{-} or @samp{^} in a
+character set, put a @samp{\} in front of it. For example:
+
+@example
+[d\]]
+@end example
+
+@noindent
+matches either @samp{d}, or @samp{]}.@refill
+
+This treatment of @samp{\} is compatible with other @code{awk}
+implementations, and is also mandated by the @sc{posix} Command Language
+and Utilities standard. The regular expressions in @code{awk} are a superset
+of the @sc{posix} specification for Extended Regular Expressions (EREs).
+@sc{posix} EREs are based on the regular expressions accepted by the
+traditional @code{egrep} utility.
+
+In @code{egrep} syntax, backslash is not syntactically special within
+square brackets. This means that special tricks have to be used to
+represent the characters @samp{]}, @samp{-} and @samp{^} as members of a
+character set.
+
+In @code{egrep} syntax, to match @samp{-}, write it as @samp{---},
+which is a range containing only @w{@samp{-}.} You may also give @samp{-}
+as the first or last character in the set. To match @samp{^}, put it
+anywhere except as the first character of a set. To match a @samp{]},
+make it the first character in the set. For example:@refill
+
+@example
+[]d^]
+@end example
+
+@noindent
+matches either @samp{]}, @samp{d} or @samp{^}.@refill
+
+@item [^ @dots{}]
+This is a @dfn{complemented character set}. The first character after
+the @samp{[} @emph{must} be a @samp{^}. It matches any characters
+@emph{except} those in the square brackets (or newline). For example:
+
+@example
+[^0-9]
+@end example
+
+@noindent
+matches any character that is not a digit.
+
+@item |
+This is the @dfn{alternation operator} and it is used to specify
+alternatives. For example:
+
+@example
+^P|[0-9]
+@end example
+
+@noindent
+matches any string that matches either @samp{^P} or @samp{[0-9]}. This
+means it matches any string that contains a digit or starts with @samp{P}.
+
+The alternation applies to the largest possible regexps on either side.
+@item (@dots{})
+Parentheses are used for grouping in regular expressions as in
+arithmetic. They can be used to concatenate regular expressions
+containing the alternation operator, @samp{|}.
+
+@item *
+This symbol means that the preceding regular expression is to be
+repeated as many times as possible to find a match. For example:
+
+@example
+ph*
+@end example
+
+@noindent
+applies the @samp{*} symbol to the preceding @samp{h} and looks for matches
+to one @samp{p} followed by any number of @samp{h}s. This will also match
+just @samp{p} if no @samp{h}s are present.
+
+The @samp{*} repeats the @emph{smallest} possible preceding expression.
+(Use parentheses if you wish to repeat a larger expression.) It finds
+as many repetitions as possible. For example:
+
+@example
+awk '/\(c[ad][ad]*r x\)/ @{ print @}' sample
+@end example
+
+@noindent
+prints every record in the input containing a string of the form
+@samp{(car x)}, @samp{(cdr x)}, @samp{(cadr x)}, and so on.@refill
+
+@item +
+This symbol is similar to @samp{*}, but the preceding expression must be
+matched at least once. This means that:
+
+@example
+wh+y
+@end example
+
+@noindent
+would match @samp{why} and @samp{whhy} but not @samp{wy}, whereas
+@samp{wh*y} would match all three of these strings. This is a simpler
+way of writing the last @samp{*} example:
+
+@example
+awk '/\(c[ad]+r x\)/ @{ print @}' sample
+@end example
+
+@item ?
+This symbol is similar to @samp{*}, but the preceding expression can be
+matched once or not at all. For example:
+
+@example
+fe?d
+@end example
+
+@noindent
+will match @samp{fed} and @samp{fd}, but nothing else.@refill
+
+@item \
+This is used to suppress the special meaning of a character when
+matching. For example:
+
+@example
+\$
+@end example
+
+@noindent
+matches the character @samp{$}.
+
+The escape sequences used for string constants
+(@pxref{Constants, ,Constant Expressions}) are
+valid in regular expressions as well; they are also introduced by a
+@samp{\}.@refill
+@end table
+
+In regular expressions, the @samp{*}, @samp{+}, and @samp{?} operators have
+the highest precedence, followed by concatenation, and finally by @samp{|}.
+As in arithmetic, parentheses can change how operators are grouped.@refill
+
+@node Case-sensitivity, , Regexp Operators, Regexp
+@subsection Case-sensitivity in Matching
+
+Case is normally significant in regular expressions, both when matching
+ordinary characters (i.e., not metacharacters), and inside character
+sets. Thus a @samp{w} in a regular expression matches only a lower case
+@samp{w} and not an upper case @samp{W}.
+
+The simplest way to do a case-independent match is to use a character
+set: @samp{[Ww]}. However, this can be cumbersome if you need to use it
+often; and it can make the regular expressions harder for humans to
+read. There are two other alternatives that you might prefer.
+
+One way to do a case-insensitive match at a particular point in the
+program is to convert the data to a single case, using the
+@code{tolower} or @code{toupper} built-in string functions (which we
+haven't discussed yet;
+@pxref{String Functions, ,Built-in Functions for String Manipulation}).
+For example:@refill
+
+@example
+tolower($1) ~ /foo/ @{ @dots{} @}
+@end example
+
+@noindent
+converts the first field to lower case before matching against it.
+
+Another method is to set the variable @code{IGNORECASE} to a nonzero
+value (@pxref{Built-in Variables}). When @code{IGNORECASE} is not zero,
+@emph{all} regexp operations ignore case. Changing the value of
+@code{IGNORECASE} dynamically controls the case sensitivity of your
+program as it runs. Case is significant by default because
+@code{IGNORECASE} (like most variables) is initialized to zero.
+
+@example
+x = "aB"
+if (x ~ /ab/) @dots{} # this test will fail
+
+IGNORECASE = 1
+if (x ~ /ab/) @dots{} # now it will succeed
+@end example
+
+In general, you cannot use @code{IGNORECASE} to make certain rules
+case-insensitive and other rules case-sensitive, because there is no way
+to set @code{IGNORECASE} just for the pattern of a particular rule. To
+do this, you must use character sets or @code{tolower}. However, one
+thing you can do only with @code{IGNORECASE} is turn case-sensitivity on
+or off dynamically for all the rules at once.@refill
+
+@code{IGNORECASE} can be set on the command line, or in a @code{BEGIN}
+rule. Setting @code{IGNORECASE} from the command line is a way to make
+a program case-insensitive without having to edit it.
+
+The value of @code{IGNORECASE} has no effect if @code{gawk} is in
+compatibility mode (@pxref{Command Line, ,Invoking @code{awk}}).
+Case is always significant in compatibility mode.@refill
+
+@node Comparison Patterns, Boolean Patterns, Regexp, Patterns
+@section Comparison Expressions as Patterns
+@cindex comparison expressions as patterns
+@cindex pattern, comparison expressions
+@cindex relational operators
+@cindex operators, relational
+
+@dfn{Comparison patterns} test relationships such as equality between
+two strings or numbers. They are a special case of expression patterns
+(@pxref{Expression Patterns, ,Expressions as Patterns}). They are written
+with @dfn{relational operators}, which are a superset of those in C.
+Here is a table of them:@refill
+
+@table @code
+@item @var{x} < @var{y}
+True if @var{x} is less than @var{y}.
+
+@item @var{x} <= @var{y}
+True if @var{x} is less than or equal to @var{y}.
+
+@item @var{x} > @var{y}
+True if @var{x} is greater than @var{y}.
+
+@item @var{x} >= @var{y}
+True if @var{x} is greater than or equal to @var{y}.
+
+@item @var{x} == @var{y}
+True if @var{x} is equal to @var{y}.
+
+@item @var{x} != @var{y}
+True if @var{x} is not equal to @var{y}.
+
+@item @var{x} ~ @var{y}
+True if @var{x} matches the regular expression described by @var{y}.
+
+@item @var{x} !~ @var{y}
+True if @var{x} does not match the regular expression described by @var{y}.
+@end table
+
+The operands of a relational operator are compared as numbers if they
+are both numbers. Otherwise they are converted to, and compared as,
+strings (@pxref{Conversion, ,Conversion of Strings and Numbers},
+for the detailed rules). Strings are compared by comparing the first
+character of each, then the second character of each,
+and so on, until there is a difference. If the two strings are equal until
+the shorter one runs out, the shorter one is considered to be less than the
+longer one. Thus, @code{"10"} is less than @code{"9"}, and @code{"abc"}
+is less than @code{"abcd"}.@refill
+
+The left operand of the @samp{~} and @samp{!~} operators is a string.
+The right operand is either a constant regular expression enclosed in
+slashes (@code{/@var{regexp}/}), or any expression, whose string value
+is used as a dynamic regular expression
+(@pxref{Regexp Usage, ,How to Use Regular Expressions}).@refill
+
+The following example prints the second field of each input record
+whose first field is precisely @samp{foo}.
+
+@example
+awk '$1 == "foo" @{ print $2 @}' BBS-list
+@end example
+
+@noindent
+Contrast this with the following regular expression match, which would
+accept any record with a first field that contains @samp{foo}:
+
+@example
+awk '$1 ~ "foo" @{ print $2 @}' BBS-list
+@end example
+
+@noindent
+or, equivalently, this one:
+
+@example
+awk '$1 ~ /foo/ @{ print $2 @}' BBS-list
+@end example
+
+@node Boolean Patterns, Expression Patterns, Comparison Patterns, Patterns
+@section Boolean Operators and Patterns
+@cindex patterns, boolean
+@cindex boolean patterns
+
+A @dfn{boolean pattern} is an expression which combines other patterns
+using the @dfn{boolean operators} ``or'' (@samp{||}), ``and''
+(@samp{&&}), and ``not'' (@samp{!}). Whether the boolean pattern
+matches an input record depends on whether its subpatterns match.
+
+For example, the following command prints all records in the input file
+@file{BBS-list} that contain both @samp{2400} and @samp{foo}.@refill
+
+@example
+awk '/2400/ && /foo/' BBS-list
+@end example
+
+The following command prints all records in the input file
+@file{BBS-list} that contain @emph{either} @samp{2400} or @samp{foo}, or
+both.@refill
+
+@example
+awk '/2400/ || /foo/' BBS-list
+@end example
+
+The following command prints all records in the input file
+@file{BBS-list} that do @emph{not} contain the string @samp{foo}.
+
+@example
+awk '! /foo/' BBS-list
+@end example
+
+Note that boolean patterns are a special case of expression patterns
+(@pxref{Expression Patterns, ,Expressions as Patterns}); they are
+expressions that use the boolean operators.
+@xref{Boolean Ops, ,Boolean Expressions}, for complete information
+on the boolean operators.@refill
+
+The subpatterns of a boolean pattern can be constant regular
+expressions, comparisons, or any other @code{awk} expressions. Range
+patterns are not expressions, so they cannot appear inside boolean
+patterns. Likewise, the special patterns @code{BEGIN} and @code{END},
+which never match any input record, are not expressions and cannot
+appear inside boolean patterns.
+
+@node Expression Patterns, Ranges, Boolean Patterns, Patterns
+@section Expressions as Patterns
+
+Any @code{awk} expression is also valid as an @code{awk} pattern.
+Then the pattern ``matches'' if the expression's value is nonzero (if a
+number) or nonnull (if a string).
+
+The expression is reevaluated each time the rule is tested against a new
+input record. If the expression uses fields such as @code{$1}, the
+value depends directly on the new input record's text; otherwise, it
+depends only on what has happened so far in the execution of the
+@code{awk} program, but that may still be useful.
+
+Comparison patterns are actually a special case of this. For
+example, the expression @code{$5 == "foo"} has the value 1 when the
+value of @code{$5} equals @code{"foo"}, and 0 otherwise; therefore, this
+expression as a pattern matches when the two values are equal.
+
+Boolean patterns are also special cases of expression patterns.
+
+A constant regexp as a pattern is also a special case of an expression
+pattern. @code{/foo/} as an expression has the value 1 if @samp{foo}
+appears in the current input record; thus, as a pattern, @code{/foo/}
+matches any record containing @samp{foo}.
+
+Other implementations of @code{awk} that are not yet @sc{posix} compliant
+are less general than @code{gawk}: they allow comparison expressions, and
+boolean combinations thereof (optionally with parentheses), but not
+necessarily other kinds of expressions.
+
+@node Ranges, BEGIN/END, Expression Patterns, Patterns
+@section Specifying Record Ranges with Patterns
+
+@cindex range pattern
+@cindex patterns, range
+A @dfn{range pattern} is made of two patterns separated by a comma, of
+the form @code{@var{begpat}, @var{endpat}}. It matches ranges of
+consecutive input records. The first pattern @var{begpat} controls
+where the range begins, and the second one @var{endpat} controls where
+it ends. For example,@refill
+
+@example
+awk '$1 == "on", $1 == "off"'
+@end example
+
+@noindent
+prints every record between @samp{on}/@samp{off} pairs, inclusive.
+
+A range pattern starts out by matching @var{begpat}
+against every input record; when a record matches @var{begpat}, the
+range pattern becomes @dfn{turned on}. The range pattern matches this
+record. As long as it stays turned on, it automatically matches every
+input record read. It also matches @var{endpat} against
+every input record; when that succeeds, the range pattern is turned
+off again for the following record. Now it goes back to checking
+@var{begpat} against each record.
+
+The record that turns on the range pattern and the one that turns it
+off both match the range pattern. If you don't want to operate on
+these records, you can write @code{if} statements in the rule's action
+to distinguish them.
+
+It is possible for a pattern to be turned both on and off by the same
+record, if both conditions are satisfied by that record. Then the action is
+executed for just that record.
+
+@node BEGIN/END, Empty, Ranges, Patterns
+@section @code{BEGIN} and @code{END} Special Patterns
+
+@cindex @code{BEGIN} special pattern
+@cindex patterns, @code{BEGIN}
+@cindex @code{END} special pattern
+@cindex patterns, @code{END}
+@code{BEGIN} and @code{END} are special patterns. They are not used to
+match input records. Rather, they are used for supplying start-up or
+clean-up information to your @code{awk} script. A @code{BEGIN} rule is
+executed, once, before the first input record has been read. An @code{END}
+rule is executed, once, after all the input has been read. For
+example:@refill
+
+@example
+awk 'BEGIN @{ print "Analysis of `foo'" @}
+ /foo/ @{ ++foobar @}
+ END @{ print "`foo' appears " foobar " times." @}' BBS-list
+@end example
+
+This program finds the number of records in the input file @file{BBS-list}
+that contain the string @samp{foo}. The @code{BEGIN} rule prints a title
+for the report. There is no need to use the @code{BEGIN} rule to
+initialize the counter @code{foobar} to zero, as @code{awk} does this
+for us automatically (@pxref{Variables}).
+
+The second rule increments the variable @code{foobar} every time a
+record containing the pattern @samp{foo} is read. The @code{END} rule
+prints the value of @code{foobar} at the end of the run.@refill
+
+The special patterns @code{BEGIN} and @code{END} cannot be used in ranges
+or with boolean operators (indeed, they cannot be used with any operators).
+
+An @code{awk} program may have multiple @code{BEGIN} and/or @code{END}
+rules. They are executed in the order they appear, all the @code{BEGIN}
+rules at start-up and all the @code{END} rules at termination.
+
+Multiple @code{BEGIN} and @code{END} sections are useful for writing
+library functions, since each library can have its own @code{BEGIN} or
+@code{END} rule to do its own initialization and/or cleanup. Note that
+the order in which library functions are named on the command line
+controls the order in which their @code{BEGIN} and @code{END} rules are
+executed. Therefore you have to be careful to write such rules in
+library files so that the order in which they are executed doesn't matter.
+@xref{Command Line, ,Invoking @code{awk}}, for more information on
+using library functions.
+
+If an @code{awk} program only has a @code{BEGIN} rule, and no other
+rules, then the program exits after the @code{BEGIN} rule has been run.
+(Older versions of @code{awk} used to keep reading and ignoring input
+until end of file was seen.) However, if an @code{END} rule exists as
+well, then the input will be read, even if there are no other rules in
+the program. This is necessary in case the @code{END} rule checks the
+@code{NR} variable.
+
+@code{BEGIN} and @code{END} rules must have actions; there is no default
+action for these rules since there is no current record when they run.
+
+@node Empty, , BEGIN/END, Patterns
+@comment node-name, next, previous, up
+@section The Empty Pattern
+
+@cindex empty pattern
+@cindex pattern, empty
+An empty pattern is considered to match @emph{every} input record. For
+example, the program:@refill
+
+@example
+awk '@{ print $1 @}' BBS-list
+@end example
+
+@noindent
+prints the first field of every record.
+
+@node Actions, Expressions, Patterns, Top
+@chapter Overview of Actions
+@cindex action, definition of
+@cindex curly braces
+@cindex action, curly braces
+@cindex action, separating statements
+
+An @code{awk} program or script consists of a series of
+rules and function definitions, interspersed. (Functions are
+described later. @xref{User-defined, ,User-defined Functions}.)
+
+A rule contains a pattern and an action, either of which may be
+omitted. The purpose of the @dfn{action} is to tell @code{awk} what to do
+once a match for the pattern is found. Thus, the entire program
+looks somewhat like this:
+
+@example
+@r{[}@var{pattern}@r{]} @r{[}@{ @var{action} @}@r{]}
+@r{[}@var{pattern}@r{]} @r{[}@{ @var{action} @}@r{]}
+@dots{}
+function @var{name} (@var{args}) @{ @dots{} @}
+@dots{}
+@end example
+
+An action consists of one or more @code{awk} @dfn{statements}, enclosed
+in curly braces (@samp{@{} and @samp{@}}). Each statement specifies one
+thing to be done. The statements are separated by newlines or
+semicolons.
+
+The curly braces around an action must be used even if the action
+contains only one statement, or even if it contains no statements at
+all. However, if you omit the action entirely, omit the curly braces as
+well. (An omitted action is equivalent to @samp{@{ print $0 @}}.)
+
+Here are the kinds of statements supported in @code{awk}:
+
+@itemize @bullet
+@item
+Expressions, which can call functions or assign values to variables
+(@pxref{Expressions, ,Expressions as Action Statements}). Executing
+this kind of statement simply computes the value of the expression and
+then ignores it. This is useful when the expression has side effects
+(@pxref{Assignment Ops, ,Assignment Expressions}).@refill
+
+@item
+Control statements, which specify the control flow of @code{awk}
+programs. The @code{awk} language gives you C-like constructs
+(@code{if}, @code{for}, @code{while}, and so on) as well as a few
+special ones (@pxref{Statements, ,Control Statements in Actions}).@refill
+
+@item
+Compound statements, which consist of one or more statements enclosed in
+curly braces. A compound statement is used in order to put several
+statements together in the body of an @code{if}, @code{while}, @code{do}
+or @code{for} statement.
+
+@item
+Input control, using the @code{getline} command
+(@pxref{Getline, ,Explicit Input with @code{getline}}), and the @code{next}
+statement (@pxref{Next Statement, ,The @code{next} Statement}).
+
+@item
+Output statements, @code{print} and @code{printf}.
+@xref{Printing, ,Printing Output}.@refill
+
+@item
+Deletion statements, for deleting array elements.
+@xref{Delete, ,The @code{delete} Statement}.@refill
+@end itemize
+
+@iftex
+The next two chapters cover in detail expressions and control
+statements, respectively. We go on to treat arrays and built-in
+functions, both of which are used in expressions. Then we proceed
+to discuss how to define your own functions.
+@end iftex
+
+@node Expressions, Statements, Actions, Top
+@chapter Expressions as Action Statements
+@cindex expression
+
+Expressions are the basic building block of @code{awk} actions. An
+expression evaluates to a value, which you can print, test, store in a
+variable or pass to a function. But beyond that, an expression can assign a new value to a variable
+or a field, with an assignment operator.
+
+An expression can serve as a statement on its own. Most other kinds of
+statements contain one or more expressions which specify data to be
+operated on. As in other languages, expressions in @code{awk} include
+variables, array references, constants, and function calls, as well as
+combinations of these with various operators.
+
+@menu
+* Constants:: String, numeric, and regexp constants.
+* Variables:: Variables give names to values for later use.
+* Arithmetic Ops:: Arithmetic operations (@samp{+}, @samp{-}, etc.)
+* Concatenation:: Concatenating strings.
+* Comparison Ops:: Comparison of numbers and strings
+ with @samp{<}, etc.
+* Boolean Ops:: Combining comparison expressions
+ using boolean operators
+ @samp{||} (``or''), @samp{&&} (``and'') and @samp{!} (``not'').
+
+* Assignment Ops:: Changing the value of a variable or a field.
+* Increment Ops:: Incrementing the numeric value of a variable.
+
+* Conversion:: The conversion of strings to numbers
+ and vice versa.
+* Values:: The whole truth about numbers and strings.
+* Conditional Exp:: Conditional expressions select
+ between two subexpressions under control
+ of a third subexpression.
+* Function Calls:: A function call is an expression.
+* Precedence:: How various operators nest.
+@end menu
+
+@node Constants, Variables, Expressions, Expressions
+@section Constant Expressions
+@cindex constants, types of
+@cindex string constants
+
+The simplest type of expression is the @dfn{constant}, which always has
+the same value. There are three types of constants: numeric constants,
+string constants, and regular expression constants.
+
+@cindex numeric constant
+@cindex numeric value
+A @dfn{numeric constant} stands for a number. This number can be an
+integer, a decimal fraction, or a number in scientific (exponential)
+notation. Note that all numeric values are represented within
+@code{awk} in double-precision floating point. Here are some examples
+of numeric constants, which all have the same value:
+
+@example
+105
+1.05e+2
+1050e-1
+@end example
+
+A string constant consists of a sequence of characters enclosed in
+double-quote marks. For example:
+
+@example
+"parrot"
+@end example
+
+@noindent
+@iftex
+@cindex differences between @code{gawk} and @code{awk}
+@end iftex
+represents the string whose contents are @samp{parrot}. Strings in
+@code{gawk} can be of any length and they can contain all the possible
+8-bit ASCII characters including ASCII NUL. Other @code{awk}
+implementations may have difficulty with some character codes.@refill
+
+@cindex escape sequence notation
+Some characters cannot be included literally in a string constant. You
+represent them instead with @dfn{escape sequences}, which are character
+sequences beginning with a backslash (@samp{\}).
+
+One use of an escape sequence is to include a double-quote character in
+a string constant. Since a plain double-quote would end the string, you
+must use @samp{\"} to represent a single double-quote character as a
+part of the string.
+The
+backslash character itself is another character that cannot be
+included normally; you write @samp{\\} to put one backslash in the
+string. Thus, the string whose contents are the two characters
+@samp{"\} must be written @code{"\"\\"}.
+
+Another use of backslash is to represent unprintable characters
+such as newline. While there is nothing to stop you from writing most
+of these characters directly in a string constant, they may look ugly.
+
+Here is a table of all the escape sequences used in @code{awk}:
+
+@table @code
+@item \\
+Represents a literal backslash, @samp{\}.
+
+@item \a
+Represents the ``alert'' character, control-g, ASCII code 7.
+
+@item \b
+Represents a backspace, control-h, ASCII code 8.
+
+@item \f
+Represents a formfeed, control-l, ASCII code 12.
+
+@item \n
+Represents a newline, control-j, ASCII code 10.
+
+@item \r
+Represents a carriage return, control-m, ASCII code 13.
+
+@item \t
+Represents a horizontal tab, control-i, ASCII code 9.
+
+@item \v
+Represents a vertical tab, control-k, ASCII code 11.
+
+@item \@var{nnn}
+Represents the octal value @var{nnn}, where @var{nnn} are one to three
+digits between 0 and 7. For example, the code for the ASCII ESC
+(escape) character is @samp{\033}.@refill
+
+@item \x@var{hh}@dots{}
+Represents the hexadecimal value @var{hh}, where @var{hh} are hexadecimal
+digits (@samp{0} through @samp{9} and either @samp{A} through @samp{F} or
+@samp{a} through @samp{f}). Like the same construct in @sc{ansi} C, the escape
+sequence continues until the first non-hexadecimal digit is seen. However,
+using more than two hexadecimal digits produces undefined results. (The
+@samp{\x} escape sequence is not allowed in @sc{posix} @code{awk}.)@refill
+@end table
+
+A @dfn{constant regexp} is a regular expression description enclosed in
+slashes, such as @code{/^beginning and end$/}. Most regexps used in
+@code{awk} programs are constant, but the @samp{~} and @samp{!~}
+operators can also match computed or ``dynamic'' regexps
+(@pxref{Regexp Usage, ,How to Use Regular Expressions}).@refill
+
+Constant regexps may be used like simple expressions. When a
+constant regexp is not on the right hand side of the @samp{~} or
+@samp{!~} operators, it has the same meaning as if it appeared
+in a pattern, i.e. @samp{($0 ~ /foo/)}
+(@pxref{Expression Patterns, ,Expressions as Patterns}).
+This means that the two code segments,@refill
+
+@example
+if ($0 ~ /barfly/ || $0 ~ /camelot/)
+ print "found"
+@end example
+
+@noindent
+and
+
+@example
+if (/barfly/ || /camelot/)
+ print "found"
+@end example
+
+@noindent
+are exactly equivalent. One rather bizarre consequence of this rule is
+that the following boolean expression is legal, but does not do what the user
+intended:@refill
+
+@example
+if (/foo/ ~ $1) print "found foo"
+@end example
+
+This code is ``obviously'' testing @code{$1} for a match against the regexp
+@code{/foo/}. But in fact, the expression @code{(/foo/ ~ $1)} actually means
+@code{(($0 ~ /foo/) ~ $1)}. In other words, first match the input record
+against the regexp @code{/foo/}. The result will be either a 0 or a 1,
+depending upon the success or failure of the match. Then match that result
+against the first field in the record.@refill
+
+Since it is unlikely that you would ever really wish to make this kind of
+test, @code{gawk} will issue a warning when it sees this construct in
+a program.@refill
+
+Another consequence of this rule is that the assignment statement
+
+@example
+matches = /foo/
+@end example
+
+@noindent
+will assign either 0 or 1 to the variable @code{matches}, depending
+upon the contents of the current input record.
+
+Constant regular expressions are also used as the first argument for
+the @code{sub} and @code{gsub} functions
+(@pxref{String Functions, ,Built-in Functions for String Manipulation}).@refill
+
+This feature of the language was never well documented until the
+@sc{posix} specification.
+
+You may be wondering, when is
+
+@example
+$1 ~ /foo/ @{ @dots{} @}
+@end example
+
+@noindent
+preferable to
+
+@example
+$1 ~ "foo" @{ @dots{} @}
+@end example
+
+Since the right-hand sides of both @samp{~} operators are constants,
+it is more efficient to use the @samp{/foo/} form: @code{awk} can note
+that you have supplied a regexp and store it internally in a form that
+makes pattern matching more efficient. In the second form, @code{awk}
+must first convert the string into this internal form, and then perform
+the pattern matching. The first form is also better style; it shows
+clearly that you intend a regexp match.
+
+@node Variables, Arithmetic Ops, Constants, Expressions
+@section Variables
+@cindex variables, user-defined
+@cindex user-defined variables
+@c there should be more than one subsection, ideally. Not a big deal.
+@c But usually there are supposed to be at least two. One way to get
+@c around this is to write the info in the subsection as the info in the
+@c section itself and not have any subsections.. --mew
+
+Variables let you give names to values and refer to them later. You have
+already seen variables in many of the examples. The name of a variable
+must be a sequence of letters, digits and underscores, but it may not begin
+with a digit. Case is significant in variable names; @code{a} and @code{A}
+are distinct variables.
+
+A variable name is a valid expression by itself; it represents the
+variable's current value. Variables are given new values with
+@dfn{assignment operators} and @dfn{increment operators}.
+@xref{Assignment Ops, ,Assignment Expressions}.
+
+A few variables have special built-in meanings, such as @code{FS}, the
+field separator, and @code{NF}, the number of fields in the current
+input record. @xref{Built-in Variables}, for a list of them. These
+built-in variables can be used and assigned just like all other
+variables, but their values are also used or changed automatically by
+@code{awk}. Each built-in variable's name is made entirely of upper case
+letters.
+
+Variables in @code{awk} can be assigned either numeric or string
+values. By default, variables are initialized to the null string, which
+is effectively zero if converted to a number. There is no need to
+``initialize'' each variable explicitly in @code{awk}, the way you would in C or most other traditional languages.
+
+@menu
+* Assignment Options:: Setting variables on the command line
+ and a summary of command line syntax.
+ This is an advanced method of input.
+@end menu
+
+@node Assignment Options, , Variables, Variables
+@subsection Assigning Variables on the Command Line
+
+You can set any @code{awk} variable by including a @dfn{variable assignment}
+among the arguments on the command line when you invoke @code{awk}
+(@pxref{Command Line, ,Invoking @code{awk}}). Such an assignment has
+this form:@refill
+
+@example
+@var{variable}=@var{text}
+@end example
+
+@noindent
+With it, you can set a variable either at the beginning of the
+@code{awk} run or in between input files.
+
+If you precede the assignment with the @samp{-v} option, like this:
+
+@example
+-v @var{variable}=@var{text}
+@end example
+
+@noindent
+then the variable is set at the very beginning, before even the
+@code{BEGIN} rules are run. The @samp{-v} option and its assignment
+must precede all the file name arguments, as well as the program text.
+
+Otherwise, the variable assignment is performed at a time determined by
+its position among the input file arguments: after the processing of the
+preceding input file argument. For example:
+
+@example
+awk '@{ print $n @}' n=4 inventory-shipped n=2 BBS-list
+@end example
+
+@noindent
+prints the value of field number @code{n} for all input records. Before
+the first file is read, the command line sets the variable @code{n}
+equal to 4. This causes the fourth field to be printed in lines from
+the file @file{inventory-shipped}. After the first file has finished,
+but before the second file is started, @code{n} is set to 2, so that the
+second field is printed in lines from @file{BBS-list}.
+
+Command line arguments are made available for explicit examination by
+the @code{awk} program in an array named @code{ARGV}
+(@pxref{Built-in Variables}).@refill
+
+@code{awk} processes the values of command line assignments for escape
+sequences (@pxref{Constants, ,Constant Expressions}).
+
+@node Arithmetic Ops, Concatenation, Variables, Expressions
+@section Arithmetic Operators
+@cindex arithmetic operators
+@cindex operators, arithmetic
+@cindex addition
+@cindex subtraction
+@cindex multiplication
+@cindex division
+@cindex remainder
+@cindex quotient
+@cindex exponentiation
+
+The @code{awk} language uses the common arithmetic operators when
+evaluating expressions. All of these arithmetic operators follow normal
+precedence rules, and work as you would expect them to. This example
+divides field three by field four, adds field two, stores the result
+into field one, and prints the resulting altered input record:
+
+@example
+awk '@{ $1 = $2 + $3 / $4; print @}' inventory-shipped
+@end example
+
+The arithmetic operators in @code{awk} are:
+
+@table @code
+@item @var{x} + @var{y}
+Addition.
+
+@item @var{x} - @var{y}
+Subtraction.
+
+@item - @var{x}
+Negation.
+
+@item + @var{x}
+Unary plus. No real effect on the expression.
+
+@item @var{x} * @var{y}
+Multiplication.
+
+@item @var{x} / @var{y}
+Division. Since all numbers in @code{awk} are double-precision
+floating point, the result is not rounded to an integer: @code{3 / 4}
+has the value 0.75.
+
+@item @var{x} % @var{y}
+@iftex
+@cindex differences between @code{gawk} and @code{awk}
+@end iftex
+Remainder. The quotient is rounded toward zero to an integer,
+multiplied by @var{y} and this result is subtracted from @var{x}.
+This operation is sometimes known as ``trunc-mod.'' The following
+relation always holds:
+
+@example
+b * int(a / b) + (a % b) == a
+@end example
+
+One possibly undesirable effect of this definition of remainder is that
+@code{@var{x} % @var{y}} is negative if @var{x} is negative. Thus,
+
+@example
+-17 % 8 = -1
+@end example
+
+In other @code{awk} implementations, the signedness of the remainder
+may be machine dependent.
+
+@item @var{x} ^ @var{y}
+@itemx @var{x} ** @var{y}
+Exponentiation: @var{x} raised to the @var{y} power. @code{2 ^ 3} has
+the value 8. The character sequence @samp{**} is equivalent to
+@samp{^}. (The @sc{posix} standard only specifies the use of @samp{^}
+for exponentiation.)
+@end table
+
+@node Concatenation, Comparison Ops, Arithmetic Ops, Expressions
+@section String Concatenation
+
+@cindex string operators
+@cindex operators, string
+@cindex concatenation
+There is only one string operation: concatenation. It does not have a
+specific operator to represent it. Instead, concatenation is performed by
+writing expressions next to one another, with no operator. For example:
+
+@example
+awk '@{ print "Field number one: " $1 @}' BBS-list
+@end example
+
+@noindent
+produces, for the first record in @file{BBS-list}:
+
+@example
+Field number one: aardvark
+@end example
+
+Without the space in the string constant after the @samp{:}, the line
+would run together. For example:
+
+@example
+awk '@{ print "Field number one:" $1 @}' BBS-list
+@end example
+
+@noindent
+produces, for the first record in @file{BBS-list}:
+
+@example
+Field number one:aardvark
+@end example
+
+Since string concatenation does not have an explicit operator, it is
+often necessary to insure that it happens where you want it to by
+enclosing the items to be concatenated in parentheses. For example, the
+following code fragment does not concatenate @code{file} and @code{name}
+as you might expect:
+
+@example
+file = "file"
+name = "name"
+print "something meaningful" > file name
+@end example
+
+@noindent
+It is necessary to use the following:
+
+@example
+print "something meaningful" > (file name)
+@end example
+
+We recommend you use parentheses around concatenation in all but the
+most common contexts (such as in the right-hand operand of @samp{=}).
+
+@ignore
+@code{gawk} actually now allows a concatenation on the right hand
+side of a @code{>} redirection, but other @code{awk}s don't. So for
+now we won't mention that fact.
+@end ignore
+
+@node Comparison Ops, Boolean Ops, Concatenation, Expressions
+@section Comparison Expressions
+@cindex comparison expressions
+@cindex expressions, comparison
+@cindex relational operators
+@cindex operators, relational
+@cindex regexp operators
+
+@dfn{Comparison expressions} compare strings or numbers for
+relationships such as equality. They are written using @dfn{relational
+operators}, which are a superset of those in C. Here is a table of
+them:
+
+@table @code
+@item @var{x} < @var{y}
+True if @var{x} is less than @var{y}.
+
+@item @var{x} <= @var{y}
+True if @var{x} is less than or equal to @var{y}.
+
+@item @var{x} > @var{y}
+True if @var{x} is greater than @var{y}.
+
+@item @var{x} >= @var{y}
+True if @var{x} is greater than or equal to @var{y}.
+
+@item @var{x} == @var{y}
+True if @var{x} is equal to @var{y}.
+
+@item @var{x} != @var{y}
+True if @var{x} is not equal to @var{y}.
+
+@item @var{x} ~ @var{y}
+True if the string @var{x} matches the regexp denoted by @var{y}.
+
+@item @var{x} !~ @var{y}
+True if the string @var{x} does not match the regexp denoted by @var{y}.
+
+@item @var{subscript} in @var{array}
+True if array @var{array} has an element with the subscript @var{subscript}.
+@end table
+
+Comparison expressions have the value 1 if true and 0 if false.
+
+The rules @code{gawk} uses for performing comparisons are based on those
+in draft 11.2 of the @sc{posix} standard. The @sc{posix} standard introduced
+the concept of a @dfn{numeric string}, which is simply a string that looks
+like a number, for example, @code{@w{" +2"}}.
+
+@vindex CONVFMT
+When performing a relational operation, @code{gawk} considers the type of an
+operand to be the type it received on its last @emph{assignment}, rather
+than the type of its last @emph{use}
+(@pxref{Values, ,Numeric and String Values}).
+This type is @emph{unknown} when the operand is from an ``external'' source:
+field variables, command line arguments, array elements resulting from a
+@code{split} operation, and the value of an @code{ENVIRON} element.
+In this case only, if the operand is a numeric string, then it is
+considered to be of both string type and numeric type. If at least one
+operand of a comparison is of string type only, then a string
+comparison is performed. Any numeric operand will be converted to a
+string using the value of @code{CONVFMT}
+(@pxref{Conversion, ,Conversion of Strings and Numbers}).
+If one operand of a comparison is numeric, and the other operand is
+either numeric or both numeric and string, then @code{gawk} does a
+numeric comparison. If both operands have both types, then the
+comparison is numeric. Strings are compared
+by comparing the first character of each, then the second character of each,
+and so on. Thus @code{"10"} is less than @code{"9"}. If there are two
+strings where one is a prefix of the other, the shorter string is less than
+the longer one. Thus @code{"abc"} is less than @code{"abcd"}.@refill
+
+Here are some sample expressions, how @code{gawk} compares them, and what
+the result of the comparison is.
+
+@table @code
+@item 1.5 <= 2.0
+numeric comparison (true)
+
+@item "abc" >= "xyz"
+string comparison (false)
+
+@item 1.5 != " +2"
+string comparison (true)
+
+@item "1e2" < "3"
+string comparison (true)
+
+@item a = 2; b = "2"
+@itemx a == b
+string comparison (true)
+@end table
+
+@example
+echo 1e2 3 | awk '@{ print ($1 < $2) ? "true" : "false" @}'
+@end example
+
+@noindent
+prints @samp{false} since both @code{$1} and @code{$2} are numeric
+strings and thus have both string and numeric types, thus dictating
+a numeric comparison.
+
+The purpose of the comparison rules and the use of numeric strings is
+to attempt to produce the behavior that is ``least surprising,'' while
+still ``doing the right thing.''
+
+String comparisons and regular expression comparisons are very different.
+For example,
+
+@example
+$1 == "foo"
+@end example
+
+@noindent
+has the value of 1, or is true, if the first field of the current input
+record is precisely @samp{foo}. By contrast,
+
+@example
+$1 ~ /foo/
+@end example
+
+@noindent
+has the value 1 if the first field contains @samp{foo}, such as @samp{foobar}.
+
+The right hand operand of the @samp{~} and @samp{!~} operators may be
+either a constant regexp (@code{/@dots{}/}), or it may be an ordinary
+expression, in which case the value of the expression as a string is a
+dynamic regexp (@pxref{Regexp Usage, ,How to Use Regular Expressions}).
+
+@cindex regexp as expression
+In very recent implementations of @code{awk}, a constant regular
+expression in slashes by itself is also an expression. The regexp
+@code{/@var{regexp}/} is an abbreviation for this comparison expression:
+
+@example
+$0 ~ /@var{regexp}/
+@end example
+
+In some contexts it may be necessary to write parentheses around the
+regexp to avoid confusing the @code{gawk} parser. For example,
+@code{(/x/ - /y/) > threshold} is not allowed, but @code{((/x/) - (/y/))
+> threshold} parses properly.
+
+One special place where @code{/foo/} is @emph{not} an abbreviation for
+@code{$0 ~ /foo/} is when it is the right-hand operand of @samp{~} or
+@samp{!~}! @xref{Constants, ,Constant Expressions}, where this is
+discussed in more detail.
+
+@node Boolean Ops, Assignment Ops, Comparison Ops, Expressions
+@section Boolean Expressions
+@cindex expressions, boolean
+@cindex boolean expressions
+@cindex operators, boolean
+@cindex boolean operators
+@cindex logical operations
+@cindex and operator
+@cindex or operator
+@cindex not operator
+
+A @dfn{boolean expression} is a combination of comparison expressions or
+matching expressions, using the boolean operators ``or''
+(@samp{||}), ``and'' (@samp{&&}), and ``not'' (@samp{!}), along with
+parentheses to control nesting. The truth of the boolean expression is
+computed by combining the truth values of the component expressions.
+
+Boolean expressions can be used wherever comparison and matching
+expressions can be used. They can be used in @code{if}, @code{while}
+@code{do} and @code{for} statements. They have numeric values (1 if true,
+0 if false), which come into play if the result of the boolean expression
+is stored in a variable, or used in arithmetic.@refill
+
+In addition, every boolean expression is also a valid boolean pattern, so
+you can use it as a pattern to control the execution of rules.
+
+Here are descriptions of the three boolean operators, with an example of
+each. It may be instructive to compare these examples with the
+analogous examples of boolean patterns
+(@pxref{Boolean Patterns, ,Boolean Operators and Patterns}), which
+use the same boolean operators in patterns instead of expressions.@refill
+
+@table @code
+@item @var{boolean1} && @var{boolean2}
+True if both @var{boolean1} and @var{boolean2} are true. For example,
+the following statement prints the current input record if it contains
+both @samp{2400} and @samp{foo}.@refill
+
+@smallexample
+if ($0 ~ /2400/ && $0 ~ /foo/) print
+@end smallexample
+
+The subexpression @var{boolean2} is evaluated only if @var{boolean1}
+is true. This can make a difference when @var{boolean2} contains
+expressions that have side effects: in the case of @code{$0 ~ /foo/ &&
+($2 == bar++)}, the variable @code{bar} is not incremented if there is
+no @samp{foo} in the record.
+
+@item @var{boolean1} || @var{boolean2}
+True if at least one of @var{boolean1} or @var{boolean2} is true.
+For example, the following command prints all records in the input
+file @file{BBS-list} that contain @emph{either} @samp{2400} or
+@samp{foo}, or both.@refill
+
+@smallexample
+awk '@{ if ($0 ~ /2400/ || $0 ~ /foo/) print @}' BBS-list
+@end smallexample
+
+The subexpression @var{boolean2} is evaluated only if @var{boolean1}
+is false. This can make a difference when @var{boolean2} contains
+expressions that have side effects.
+
+@item !@var{boolean}
+True if @var{boolean} is false. For example, the following program prints
+all records in the input file @file{BBS-list} that do @emph{not} contain the
+string @samp{foo}.
+
+@smallexample
+awk '@{ if (! ($0 ~ /foo/)) print @}' BBS-list
+@end smallexample
+@end table
+
+@node Assignment Ops, Increment Ops, Boolean Ops, Expressions
+@section Assignment Expressions
+@cindex assignment operators
+@cindex operators, assignment
+@cindex expressions, assignment
+
+An @dfn{assignment} is an expression that stores a new value into a
+variable. For example, let's assign the value 1 to the variable
+@code{z}:@refill
+
+@example
+z = 1
+@end example
+
+After this expression is executed, the variable @code{z} has the value 1.
+Whatever old value @code{z} had before the assignment is forgotten.
+
+Assignments can store string values also. For example, this would store
+the value @code{"this food is good"} in the variable @code{message}:
+
+@example
+thing = "food"
+predicate = "good"
+message = "this " thing " is " predicate
+@end example
+
+@noindent
+(This also illustrates concatenation of strings.)
+
+The @samp{=} sign is called an @dfn{assignment operator}. It is the
+simplest assignment operator because the value of the right-hand
+operand is stored unchanged.
+
+@cindex side effect
+Most operators (addition, concatenation, and so on) have no effect
+except to compute a value. If you ignore the value, you might as well
+not use the operator. An assignment operator is different; it does
+produce a value, but even if you ignore the value, the assignment still
+makes itself felt through the alteration of the variable. We call this
+a @dfn{side effect}.
+
+@cindex lvalue
+The left-hand operand of an assignment need not be a variable
+(@pxref{Variables}); it can also be a field
+(@pxref{Changing Fields, ,Changing the Contents of a Field}) or
+an array element (@pxref{Arrays, ,Arrays in @code{awk}}).
+These are all called @dfn{lvalues},
+which means they can appear on the left-hand side of an assignment operator.
+The right-hand operand may be any expression; it produces the new value
+which the assignment stores in the specified variable, field or array
+element.@refill
+
+It is important to note that variables do @emph{not} have permanent types.
+The type of a variable is simply the type of whatever value it happens
+to hold at the moment. In the following program fragment, the variable
+@code{foo} has a numeric value at first, and a string value later on:
+
+@example
+foo = 1
+print foo
+foo = "bar"
+print foo
+@end example
+
+@noindent
+When the second assignment gives @code{foo} a string value, the fact that
+it previously had a numeric value is forgotten.
+
+An assignment is an expression, so it has a value: the same value that
+is assigned. Thus, @code{z = 1} as an expression has the value 1.
+One consequence of this is that you can write multiple assignments together:
+
+@example
+x = y = z = 0
+@end example
+
+@noindent
+stores the value 0 in all three variables. It does this because the
+value of @code{z = 0}, which is 0, is stored into @code{y}, and then
+the value of @code{y = z = 0}, which is 0, is stored into @code{x}.
+
+You can use an assignment anywhere an expression is called for. For
+example, it is valid to write @code{x != (y = 1)} to set @code{y} to 1
+and then test whether @code{x} equals 1. But this style tends to make
+programs hard to read; except in a one-shot program, you should
+rewrite it to get rid of such nesting of assignments. This is never very
+hard.
+
+Aside from @samp{=}, there are several other assignment operators that
+do arithmetic with the old value of the variable. For example, the
+operator @samp{+=} computes a new value by adding the right-hand value
+to the old value of the variable. Thus, the following assignment adds
+5 to the value of @code{foo}:
+
+@example
+foo += 5
+@end example
+
+@noindent
+This is precisely equivalent to the following:
+
+@example
+foo = foo + 5
+@end example
+
+@noindent
+Use whichever one makes the meaning of your program clearer.
+
+Here is a table of the arithmetic assignment operators. In each
+case, the right-hand operand is an expression whose value is converted
+to a number.
+
+@table @code
+@item @var{lvalue} += @var{increment}
+Adds @var{increment} to the value of @var{lvalue} to make the new value
+of @var{lvalue}.
+
+@item @var{lvalue} -= @var{decrement}
+Subtracts @var{decrement} from the value of @var{lvalue}.
+
+@item @var{lvalue} *= @var{coefficient}
+Multiplies the value of @var{lvalue} by @var{coefficient}.
+
+@item @var{lvalue} /= @var{quotient}
+Divides the value of @var{lvalue} by @var{quotient}.
+
+@item @var{lvalue} %= @var{modulus}
+Sets @var{lvalue} to its remainder by @var{modulus}.
+
+@item @var{lvalue} ^= @var{power}
+@itemx @var{lvalue} **= @var{power}
+Raises @var{lvalue} to the power @var{power}.
+(Only the @code{^=} operator is specified by @sc{posix}.)
+@end table
+
+@ignore
+From: gatech!ames!elroy!cit-vax!EQL.Caltech.Edu!rankin (Pat Rankin)
+ In the discussion of assignment operators, it states that
+``foo += 5'' "is precisely equivalent to" ``foo = foo + 5'' (p.77). That
+may be true for simple variables, but it's not true for expressions with
+side effects, like array references. For proof, try
+ BEGIN {
+ foo[rand()] += 5; for (x in foo) print x, foo[x]
+ bar[rand()] = bar[rand()] + 5; for (x in bar) print x, bar[x]
+ }
+I suspect that the original statement is simply untrue--that '+=' is more
+efficient in all cases.
+
+ADR --- Try to add something about this here for the next go 'round.
+@end ignore
+
+@node Increment Ops, Conversion, Assignment Ops, Expressions
+@section Increment Operators
+
+@cindex increment operators
+@cindex operators, increment
+@dfn{Increment operators} increase or decrease the value of a variable
+by 1. You could do the same thing with an assignment operator, so
+the increment operators add no power to the @code{awk} language; but they
+are convenient abbreviations for something very common.
+
+The operator to add 1 is written @samp{++}. It can be used to increment
+a variable either before or after taking its value.
+
+To pre-increment a variable @var{v}, write @code{++@var{v}}. This adds
+1 to the value of @var{v} and that new value is also the value of this
+expression. The assignment expression @code{@var{v} += 1} is completely
+equivalent.
+
+Writing the @samp{++} after the variable specifies post-increment. This
+increments the variable value just the same; the difference is that the
+value of the increment expression itself is the variable's @emph{old}
+value. Thus, if @code{foo} has the value 4, then the expression @code{foo++}
+has the value 4, but it changes the value of @code{foo} to 5.
+
+The post-increment @code{foo++} is nearly equivalent to writing @code{(foo
++= 1) - 1}. It is not perfectly equivalent because all numbers in
+@code{awk} are floating point: in floating point, @code{foo + 1 - 1} does
+not necessarily equal @code{foo}. But the difference is minute as
+long as you stick to numbers that are fairly small (less than a trillion).
+
+Any lvalue can be incremented. Fields and array elements are incremented
+just like variables. (Use @samp{$(i++)} when you wish to do a field reference
+and a variable increment at the same time. The parentheses are necessary
+because of the precedence of the field reference operator, @samp{$}.)
+@c expert information in the last parenthetical remark
+
+The decrement operator @samp{--} works just like @samp{++} except that
+it subtracts 1 instead of adding. Like @samp{++}, it can be used before
+the lvalue to pre-decrement or after it to post-decrement.
+
+Here is a summary of increment and decrement expressions.
+
+@table @code
+@item ++@var{lvalue}
+This expression increments @var{lvalue} and the new value becomes the
+value of this expression.
+
+@item @var{lvalue}++
+This expression causes the contents of @var{lvalue} to be incremented.
+The value of the expression is the @emph{old} value of @var{lvalue}.
+
+@item --@var{lvalue}
+Like @code{++@var{lvalue}}, but instead of adding, it subtracts. It
+decrements @var{lvalue} and delivers the value that results.
+
+@item @var{lvalue}--
+Like @code{@var{lvalue}++}, but instead of adding, it subtracts. It
+decrements @var{lvalue}. The value of the expression is the @emph{old}
+value of @var{lvalue}.
+@end table
+
+@node Conversion, Values, Increment Ops, Expressions
+@section Conversion of Strings and Numbers
+
+@cindex conversion of strings and numbers
+Strings are converted to numbers, and numbers to strings, if the context
+of the @code{awk} program demands it. For example, if the value of
+either @code{foo} or @code{bar} in the expression @code{foo + bar}
+happens to be a string, it is converted to a number before the addition
+is performed. If numeric values appear in string concatenation, they
+are converted to strings. Consider this:@refill
+
+@example
+two = 2; three = 3
+print (two three) + 4
+@end example
+
+@noindent
+This eventually prints the (numeric) value 27. The numeric values of
+the variables @code{two} and @code{three} are converted to strings and
+concatenated together, and the resulting string is converted back to the
+number 23, to which 4 is then added.
+
+If, for some reason, you need to force a number to be converted to a
+string, concatenate the null string with that number. To force a string
+to be converted to a number, add zero to that string.
+
+A string is converted to a number by interpreting a numeric prefix
+of the string as numerals:
+@code{"2.5"} converts to 2.5, @code{"1e3"} converts to 1000, and @code{"25fix"}
+has a numeric value of 25.
+Strings that can't be interpreted as valid numbers are converted to
+zero.
+
+@vindex CONVFMT
+The exact manner in which numbers are converted into strings is controlled
+by the @code{awk} built-in variable @code{CONVFMT} (@pxref{Built-in Variables}).
+Numbers are converted using a special version of the @code{sprintf} function
+(@pxref{Built-in, ,Built-in Functions}) with @code{CONVFMT} as the format
+specifier.@refill
+
+@code{CONVFMT}'s default value is @code{"%.6g"}, which prints a value with
+at least six significant digits. For some applications you will want to
+change it to specify more precision. Double precision on most modern
+machines gives you 16 or 17 decimal digits of precision.
+
+Strange results can happen if you set @code{CONVFMT} to a string that doesn't
+tell @code{sprintf} how to format floating point numbers in a useful way.
+For example, if you forget the @samp{%} in the format, all numbers will be
+converted to the same constant string.@refill
+
+As a special case, if a number is an integer, then the result of converting
+it to a string is @emph{always} an integer, no matter what the value of
+@code{CONVFMT} may be. Given the following code fragment:
+
+@example
+CONVFMT = "%2.2f"
+a = 12
+b = a ""
+@end example
+
+@noindent
+@code{b} has the value @code{"12"}, not @code{"12.00"}.
+
+@ignore
+For the 2.14 version, describe the ``stickyness'' of conversions. Right now
+the manual assumes everywhere that variables are either numbers or strings;
+in fact both kinds of values may be valid. If both happen to be valid, a
+conversion isn't necessary and isn't done. Revising the manual to be
+consistent with this, though, is too big a job to tackle at the moment.
+
+7/92: This has sort of been done, only the section isn't completely right!
+ What to do?
+7/92: Pretty much fixed, at least for the short term, thanks to text
+ from David.
+@end ignore
+
+@vindex OFMT
+Prior to the @sc{posix} standard, @code{awk} specified that the value
+of @code{OFMT} was used for converting numbers to strings. @code{OFMT}
+specifies the output format to use when printing numbers with @code{print}.
+@code{CONVFMT} was introduced in order to separate the semantics of
+conversions from the semantics of printing. Both @code{CONVFMT} and
+@code{OFMT} have the same default value: @code{"%.6g"}. In the vast majority
+of cases, old @code{awk} programs will not change their behavior.
+However, this use of @code{OFMT} is something to keep in mind if you must
+port your program to other implementations of @code{awk}; we recommend
+that instead of changing your programs, you just port @code{gawk} itself!@refill
+
+@node Values, Conditional Exp, Conversion, Expressions
+@section Numeric and String Values
+@cindex conversion of strings and numbers
+
+Through most of this manual, we present @code{awk} values (such as constants,
+fields, or variables) as @emph{either} numbers @emph{or} strings. This is
+a convenient way to think about them, since typically they are used in only
+one way, or the other.
+
+In truth though, @code{awk} values can be @emph{both} string and
+numeric, at the same time. Internally, @code{awk} represents values
+with a string, a (floating point) number, and an indication that one,
+the other, or both representations of the value are valid.
+
+Keeping track of both kinds of values is important for execution
+efficiency: a variable can acquire a string value the first time it
+is used as a string, and then that string value can be used until the
+variable is assigned a new value. Thus, if a variable with only a numeric
+value is used in several concatenations in a row, it only has to be given
+a string representation once. The numeric value remains valid, so that
+no conversion back to a number is necessary if the variable is later used
+in an arithmetic expression.
+
+Tracking both kinds of values is also important for precise numerical
+calculations. Consider the following:
+
+@smallexample
+a = 123.321
+CONVFMT = "%3.1f"
+b = a " is a number"
+c = a + 1.654
+@end smallexample
+
+@noindent
+The variable @code{a} receives a string value in the concatenation and
+assignment to @code{b}. The string value of @code{a} is @code{"123.3"}.
+If the numeric value was lost when it was converted to a string, then the
+numeric use of @code{a} in the last statement would lose information.
+@code{c} would be assigned the value 124.954 instead of 124.975.
+Such errors accumulate rapidly, and very adversely affect numeric
+computations.@refill
+
+Once a numeric value acquires a corresponding string value, it stays valid
+until a new assignment is made. If @code{CONVFMT}
+(@pxref{Conversion, ,Conversion of Strings and Numbers}) changes in the
+meantime, the old string value will still be used. For example:@refill
+
+@smallexample
+BEGIN @{
+ CONVFMT = "%2.2f"
+ a = 123.456
+ b = a "" # force `a' to have string value too
+ printf "a = %s\n", a
+ CONVFMT = "%.6g"
+ printf "a = %s\n", a
+ a += 0 # make `a' numeric only again
+ printf "a = %s\n", a # use `a' as string
+@}
+@end smallexample
+
+@noindent
+This program prints @samp{a = 123.46} twice, and then prints
+@samp{a = 123.456}.
+
+@xref{Conversion, ,Conversion of Strings and Numbers}, for the rules that
+specify how string values are made from numeric values.
+
+@node Conditional Exp, Function Calls, Values, Expressions
+@section Conditional Expressions
+@cindex conditional expression
+@cindex expression, conditional
+
+A @dfn{conditional expression} is a special kind of expression with
+three operands. It allows you to use one expression's value to select
+one of two other expressions.
+
+The conditional expression looks the same as in the C language:
+
+@example
+@var{selector} ? @var{if-true-exp} : @var{if-false-exp}
+@end example
+
+@noindent
+There are three subexpressions. The first, @var{selector}, is always
+computed first. If it is ``true'' (not zero and not null) then
+@var{if-true-exp} is computed next and its value becomes the value of
+the whole expression. Otherwise, @var{if-false-exp} is computed next
+and its value becomes the value of the whole expression.@refill
+
+For example, this expression produces the absolute value of @code{x}:
+
+@example
+x > 0 ? x : -x
+@end example
+
+Each time the conditional expression is computed, exactly one of
+@var{if-true-exp} and @var{if-false-exp} is computed; the other is ignored.
+This is important when the expressions contain side effects. For example,
+this conditional expression examines element @code{i} of either array
+@code{a} or array @code{b}, and increments @code{i}.
+
+@example
+x == y ? a[i++] : b[i++]
+@end example
+
+@noindent
+This is guaranteed to increment @code{i} exactly once, because each time
+one or the other of the two increment expressions is executed,
+and the other is not.
+
+@node Function Calls, Precedence, Conditional Exp, Expressions
+@section Function Calls
+@cindex function call
+@cindex calling a function
+
+A @dfn{function} is a name for a particular calculation. Because it has
+a name, you can ask for it by name at any point in the program. For
+example, the function @code{sqrt} computes the square root of a number.
+
+A fixed set of functions are @dfn{built-in}, which means they are
+available in every @code{awk} program. The @code{sqrt} function is one
+of these. @xref{Built-in, ,Built-in Functions}, for a list of built-in
+functions and their descriptions. In addition, you can define your own
+functions in the program for use elsewhere in the same program.
+@xref{User-defined, ,User-defined Functions}, for how to do this.@refill
+
+@cindex arguments in function call
+The way to use a function is with a @dfn{function call} expression,
+which consists of the function name followed by a list of
+@dfn{arguments} in parentheses. The arguments are expressions which
+give the raw materials for the calculation that the function will do.
+When there is more than one argument, they are separated by commas. If
+there are no arguments, write just @samp{()} after the function name.
+Here are some examples:
+
+@example
+sqrt(x^2 + y^2) # @r{One argument}
+atan2(y, x) # @r{Two arguments}
+rand() # @r{No arguments}
+@end example
+
+@strong{Do not put any space between the function name and the
+open-parenthesis!} A user-defined function name looks just like the name of
+a variable, and space would make the expression look like concatenation
+of a variable with an expression inside parentheses. Space before the
+parenthesis is harmless with built-in functions, but it is best not to get
+into the habit of using space to avoid mistakes with user-defined
+functions.
+
+Each function expects a particular number of arguments. For example, the
+@code{sqrt} function must be called with a single argument, the number
+to take the square root of:
+
+@example
+sqrt(@var{argument})
+@end example
+
+Some of the built-in functions allow you to omit the final argument.
+If you do so, they use a reasonable default.
+@xref{Built-in, ,Built-in Functions}, for full details. If arguments
+are omitted in calls to user-defined functions, then those arguments are
+treated as local variables, initialized to the null string
+(@pxref{User-defined, ,User-defined Functions}).@refill
+
+Like every other expression, the function call has a value, which is
+computed by the function based on the arguments you give it. In this
+example, the value of @code{sqrt(@var{argument})} is the square root of the
+argument. A function can also have side effects, such as assigning the
+values of certain variables or doing I/O.
+
+Here is a command to read numbers, one number per line, and print the
+square root of each one:
+
+@example
+awk '@{ print "The square root of", $1, "is", sqrt($1) @}'
+@end example
+
+@node Precedence, , Function Calls, Expressions
+@section Operator Precedence (How Operators Nest)
+@cindex precedence
+@cindex operator precedence
+
+@dfn{Operator precedence} determines how operators are grouped, when
+different operators appear close by in one expression. For example,
+@samp{*} has higher precedence than @samp{+}; thus, @code{a + b * c}
+means to multiply @code{b} and @code{c}, and then add @code{a} to the
+product (i.e., @code{a + (b * c)}).
+
+You can overrule the precedence of the operators by using parentheses.
+You can think of the precedence rules as saying where the
+parentheses are assumed if you do not write parentheses yourself. In
+fact, it is wise to always use parentheses whenever you have an unusual
+combination of operators, because other people who read the program may
+not remember what the precedence is in this case. You might forget,
+too; then you could make a mistake. Explicit parentheses will help prevent
+any such mistake.
+
+When operators of equal precedence are used together, the leftmost
+operator groups first, except for the assignment, conditional and
+exponentiation operators, which group in the opposite order.
+Thus, @code{a - b + c} groups as @code{(a - b) + c};
+@code{a = b = c} groups as @code{a = (b = c)}.@refill
+
+The precedence of prefix unary operators does not matter as long as only
+unary operators are involved, because there is only one way to parse
+them---innermost first. Thus, @code{$++i} means @code{$(++i)} and
+@code{++$x} means @code{++($x)}. However, when another operator follows
+the operand, then the precedence of the unary operators can matter.
+Thus, @code{$x^2} means @code{($x)^2}, but @code{-x^2} means
+@code{-(x^2)}, because @samp{-} has lower precedence than @samp{^}
+while @samp{$} has higher precedence.
+
+Here is a table of the operators of @code{awk}, in order of increasing
+precedence:
+
+@table @asis
+@item assignment
+@samp{=}, @samp{+=}, @samp{-=}, @samp{*=}, @samp{/=}, @samp{%=},
+@samp{^=}, @samp{**=}. These operators group right-to-left.
+(The @samp{**=} operator is not specified by @sc{posix}.)
+
+@item conditional
+@samp{?:}. This operator groups right-to-left.
+
+@item logical ``or''.
+@samp{||}.
+
+@item logical ``and''.
+@samp{&&}.
+
+@item array membership
+@samp{in}.
+
+@item matching
+@samp{~}, @samp{!~}.
+
+@item relational, and redirection
+The relational operators and the redirections have the same precedence
+level. Characters such as @samp{>} serve both as relationals and as
+redirections; the context distinguishes between the two meanings.
+
+The relational operators are @samp{<}, @samp{<=}, @samp{==}, @samp{!=},
+@samp{>=} and @samp{>}.
+
+The I/O redirection operators are @samp{<}, @samp{>}, @samp{>>} and
+@samp{|}.
+
+Note that I/O redirection operators in @code{print} and @code{printf}
+statements belong to the statement level, not to expressions. The
+redirection does not produce an expression which could be the operand of
+another operator. As a result, it does not make sense to use a
+redirection operator near another operator of lower precedence, without
+parentheses. Such combinations, for example @samp{print foo > a ? b :
+c}, result in syntax errors.
+
+@item concatenation
+No special token is used to indicate concatenation.
+The operands are simply written side by side.
+
+@item add, subtract
+@samp{+}, @samp{-}.
+
+@item multiply, divide, mod
+@samp{*}, @samp{/}, @samp{%}.
+
+@item unary plus, minus, ``not''
+@samp{+}, @samp{-}, @samp{!}.
+
+@item exponentiation
+@samp{^}, @samp{**}. These operators group right-to-left.
+(The @samp{**} operator is not specified by @sc{posix}.)
+
+@item increment, decrement
+@samp{++}, @samp{--}.
+
+@item field
+@samp{$}.
+@end table
+
+@node Statements, Arrays, Expressions, Top
+@chapter Control Statements in Actions
+@cindex control statement
+
+@dfn{Control statements} such as @code{if}, @code{while}, and so on
+control the flow of execution in @code{awk} programs. Most of the
+control statements in @code{awk} are patterned on similar statements in
+C.
+
+All the control statements start with special keywords such as @code{if}
+and @code{while}, to distinguish them from simple expressions.
+
+Many control statements contain other statements; for example, the
+@code{if} statement contains another statement which may or may not be
+executed. The contained statement is called the @dfn{body}. If you
+want to include more than one statement in the body, group them into a
+single compound statement with curly braces, separating them with
+newlines or semicolons.
+
+@menu
+* If Statement:: Conditionally execute
+ some @code{awk} statements.
+* While Statement:: Loop until some condition is satisfied.
+* Do Statement:: Do specified action while looping until some
+ condition is satisfied.
+* For Statement:: Another looping statement, that provides
+ initialization and increment clauses.
+* Break Statement:: Immediately exit the innermost enclosing loop.
+* Continue Statement:: Skip to the end of the innermost
+ enclosing loop.
+* Next Statement:: Stop processing the current input record.
+* Next File Statement:: Stop processing the current file.
+* Exit Statement:: Stop execution of @code{awk}.
+@end menu
+
+@node If Statement, While Statement, Statements, Statements
+@section The @code{if} Statement
+
+@cindex @code{if} statement
+The @code{if}-@code{else} statement is @code{awk}'s decision-making
+statement. It looks like this:@refill
+
+@example
+if (@var{condition}) @var{then-body} @r{[}else @var{else-body}@r{]}
+@end example
+
+@noindent
+@var{condition} is an expression that controls what the rest of the
+statement will do. If @var{condition} is true, @var{then-body} is
+executed; otherwise, @var{else-body} is executed (assuming that the
+@code{else} clause is present). The @code{else} part of the statement is
+optional. The condition is considered false if its value is zero or
+the null string, and true otherwise.@refill
+
+Here is an example:
+
+@example
+if (x % 2 == 0)
+ print "x is even"
+else
+ print "x is odd"
+@end example
+
+In this example, if the expression @code{x % 2 == 0} is true (that is,
+the value of @code{x} is divisible by 2), then the first @code{print}
+statement is executed, otherwise the second @code{print} statement is
+performed.@refill
+
+If the @code{else} appears on the same line as @var{then-body}, and
+@var{then-body} is not a compound statement (i.e., not surrounded by
+curly braces), then a semicolon must separate @var{then-body} from
+@code{else}. To illustrate this, let's rewrite the previous example:
+
+@example
+awk '@{ if (x % 2 == 0) print "x is even"; else
+ print "x is odd" @}'
+@end example
+
+@noindent
+If you forget the @samp{;}, @code{awk} won't be able to parse the
+statement, and you will get a syntax error.
+
+We would not actually write this example this way, because a human
+reader might fail to see the @code{else} if it were not the first thing
+on its line.
+
+@node While Statement, Do Statement, If Statement, Statements
+@section The @code{while} Statement
+@cindex @code{while} statement
+@cindex loop
+@cindex body of a loop
+
+In programming, a @dfn{loop} means a part of a program that is (or at least can
+be) executed two or more times in succession.
+
+The @code{while} statement is the simplest looping statement in
+@code{awk}. It repeatedly executes a statement as long as a condition is
+true. It looks like this:
+
+@example
+while (@var{condition})
+ @var{body}
+@end example
+
+@noindent
+Here @var{body} is a statement that we call the @dfn{body} of the loop,
+and @var{condition} is an expression that controls how long the loop
+keeps running.
+
+The first thing the @code{while} statement does is test @var{condition}.
+If @var{condition} is true, it executes the statement @var{body}.
+(@var{condition} is true when the value
+is not zero and not a null string.) After @var{body} has been executed,
+@var{condition} is tested again, and if it is still true, @var{body} is
+executed again. This process repeats until @var{condition} is no longer
+true. If @var{condition} is initially false, the body of the loop is
+never executed.@refill
+
+This example prints the first three fields of each record, one per line.
+
+@example
+awk '@{ i = 1
+ while (i <= 3) @{
+ print $i
+ i++
+ @}
+@}'
+@end example
+
+@noindent
+Here the body of the loop is a compound statement enclosed in braces,
+containing two statements.
+
+The loop works like this: first, the value of @code{i} is set to 1.
+Then, the @code{while} tests whether @code{i} is less than or equal to
+three. This is the case when @code{i} equals one, so the @code{i}-th
+field is printed. Then the @code{i++} increments the value of @code{i}
+and the loop repeats. The loop terminates when @code{i} reaches 4.
+
+As you can see, a newline is not required between the condition and the
+body; but using one makes the program clearer unless the body is a
+compound statement or is very simple. The newline after the open-brace
+that begins the compound statement is not required either, but the
+program would be hard to read without it.
+
+@node Do Statement, For Statement, While Statement, Statements
+@section The @code{do}-@code{while} Statement
+
+The @code{do} loop is a variation of the @code{while} looping statement.
+The @code{do} loop executes the @var{body} once, then repeats @var{body}
+as long as @var{condition} is true. It looks like this:
+
+@example
+do
+ @var{body}
+while (@var{condition})
+@end example
+
+Even if @var{condition} is false at the start, @var{body} is executed at
+least once (and only once, unless executing @var{body} makes
+@var{condition} true). Contrast this with the corresponding
+@code{while} statement:
+
+@example
+while (@var{condition})
+ @var{body}
+@end example
+
+@noindent
+This statement does not execute @var{body} even once if @var{condition}
+is false to begin with.
+
+Here is an example of a @code{do} statement:
+
+@example
+awk '@{ i = 1
+ do @{
+ print $0
+ i++
+ @} while (i <= 10)
+@}'
+@end example
+
+@noindent
+prints each input record ten times. It isn't a very realistic example,
+since in this case an ordinary @code{while} would do just as well. But
+this reflects actual experience; there is only occasionally a real use
+for a @code{do} statement.@refill
+
+@node For Statement, Break Statement, Do Statement, Statements
+@section The @code{for} Statement
+@cindex @code{for} statement
+
+The @code{for} statement makes it more convenient to count iterations of a
+loop. The general form of the @code{for} statement looks like this:@refill
+
+@example
+for (@var{initialization}; @var{condition}; @var{increment})
+ @var{body}
+@end example
+
+@noindent
+This statement starts by executing @var{initialization}. Then, as long
+as @var{condition} is true, it repeatedly executes @var{body} and then
+@var{increment}. Typically @var{initialization} sets a variable to
+either zero or one, @var{increment} adds 1 to it, and @var{condition}
+compares it against the desired number of iterations.
+
+Here is an example of a @code{for} statement:
+
+@example
+@group
+awk '@{ for (i = 1; i <= 3; i++)
+ print $i
+@}'
+@end group
+@end example
+
+@noindent
+This prints the first three fields of each input record, one field per
+line.
+
+In the @code{for} statement, @var{body} stands for any statement, but
+@var{initialization}, @var{condition} and @var{increment} are just
+expressions. You cannot set more than one variable in the
+@var{initialization} part unless you use a multiple assignment statement
+such as @code{x = y = 0}, which is possible only if all the initial values
+are equal. (But you can initialize additional variables by writing
+their assignments as separate statements preceding the @code{for} loop.)
+
+The same is true of the @var{increment} part; to increment additional
+variables, you must write separate statements at the end of the loop.
+The C compound expression, using C's comma operator, would be useful in
+this context, but it is not supported in @code{awk}.
+
+Most often, @var{increment} is an increment expression, as in the
+example above. But this is not required; it can be any expression
+whatever. For example, this statement prints all the powers of 2
+between 1 and 100:
+
+@example
+for (i = 1; i <= 100; i *= 2)
+ print i
+@end example
+
+Any of the three expressions in the parentheses following the @code{for} may
+be omitted if there is nothing to be done there. Thus, @w{@samp{for (;x
+> 0;)}} is equivalent to @w{@samp{while (x > 0)}}. If the
+@var{condition} is omitted, it is treated as @var{true}, effectively
+yielding an @dfn{infinite loop} (i.e., a loop that will never
+terminate).@refill
+
+In most cases, a @code{for} loop is an abbreviation for a @code{while}
+loop, as shown here:
+
+@example
+@var{initialization}
+while (@var{condition}) @{
+ @var{body}
+ @var{increment}
+@}
+@end example
+
+@noindent
+The only exception is when the @code{continue} statement
+(@pxref{Continue Statement, ,The @code{continue} Statement}) is used
+inside the loop; changing a @code{for} statement to a @code{while}
+statement in this way can change the effect of the @code{continue}
+statement inside the loop.@refill
+
+There is an alternate version of the @code{for} loop, for iterating over
+all the indices of an array:
+
+@example
+for (i in array)
+ @var{do something with} array[i]
+@end example
+
+@noindent
+@xref{Arrays, ,Arrays in @code{awk}}, for more information on this
+version of the @code{for} loop.
+
+The @code{awk} language has a @code{for} statement in addition to a
+@code{while} statement because often a @code{for} loop is both less work to
+type and more natural to think of. Counting the number of iterations is
+very common in loops. It can be easier to think of this counting as part
+of looping rather than as something to do inside the loop.
+
+The next section has more complicated examples of @code{for} loops.
+
+@node Break Statement, Continue Statement, For Statement, Statements
+@section The @code{break} Statement
+@cindex @code{break} statement
+@cindex loops, exiting
+
+The @code{break} statement jumps out of the innermost @code{for},
+@code{while}, or @code{do}-@code{while} loop that encloses it. The
+following example finds the smallest divisor of any integer, and also
+identifies prime numbers:@refill
+
+@smallexample
+awk '# find smallest divisor of num
+ @{ num = $1
+ for (div = 2; div*div <= num; div++)
+ if (num % div == 0)
+ break
+ if (num % div == 0)
+ printf "Smallest divisor of %d is %d\n", num, div
+ else
+ printf "%d is prime\n", num @}'
+@end smallexample
+
+When the remainder is zero in the first @code{if} statement, @code{awk}
+immediately @dfn{breaks out} of the containing @code{for} loop. This means
+that @code{awk} proceeds immediately to the statement following the loop
+and continues processing. (This is very different from the @code{exit}
+statement which stops the entire @code{awk} program.
+@xref{Exit Statement, ,The @code{exit} Statement}.)@refill
+
+Here is another program equivalent to the previous one. It illustrates how
+the @var{condition} of a @code{for} or @code{while} could just as well be
+replaced with a @code{break} inside an @code{if}:
+
+@smallexample
+@group
+awk '# find smallest divisor of num
+ @{ num = $1
+ for (div = 2; ; div++) @{
+ if (num % div == 0) @{
+ printf "Smallest divisor of %d is %d\n", num, div
+ break
+ @}
+ if (div*div > num) @{
+ printf "%d is prime\n", num
+ break
+ @}
+ @}
+@}'
+@end group
+@end smallexample
+
+@node Continue Statement, Next Statement, Break Statement, Statements
+@section The @code{continue} Statement
+
+@cindex @code{continue} statement
+The @code{continue} statement, like @code{break}, is used only inside
+@code{for}, @code{while}, and @code{do}-@code{while} loops. It skips
+over the rest of the loop body, causing the next cycle around the loop
+to begin immediately. Contrast this with @code{break}, which jumps out
+of the loop altogether. Here is an example:@refill
+
+@example
+# print names that don't contain the string "ignore"
+
+# first, save the text of each line
+@{ names[NR] = $0 @}
+
+# print what we're interested in
+END @{
+ for (x in names) @{
+ if (names[x] ~ /ignore/)
+ continue
+ print names[x]
+ @}
+@}
+@end example
+
+If one of the input records contains the string @samp{ignore}, this
+example skips the print statement for that record, and continues back to
+the first statement in the loop.
+
+This is not a practical example of @code{continue}, since it would be
+just as easy to write the loop like this:
+
+@example
+for (x in names)
+ if (names[x] !~ /ignore/)
+ print names[x]
+@end example
+
+@ignore
+from brennan@boeing.com:
+
+page 90, section 9.6. The example is too artificial as
+the one line program
+
+ !/ignore/
+
+does the same thing.
+@end ignore
+@c ADR --- he's right, but don't worry about this for now
+
+The @code{continue} statement in a @code{for} loop directs @code{awk} to
+skip the rest of the body of the loop, and resume execution with the
+increment-expression of the @code{for} statement. The following program
+illustrates this fact:@refill
+
+@example
+awk 'BEGIN @{
+ for (x = 0; x <= 20; x++) @{
+ if (x == 5)
+ continue
+ printf ("%d ", x)
+ @}
+ print ""
+@}'
+@end example
+
+@noindent
+This program prints all the numbers from 0 to 20, except for 5, for
+which the @code{printf} is skipped. Since the increment @code{x++}
+is not skipped, @code{x} does not remain stuck at 5. Contrast the
+@code{for} loop above with the @code{while} loop:
+
+@example
+awk 'BEGIN @{
+ x = 0
+ while (x <= 20) @{
+ if (x == 5)
+ continue
+ printf ("%d ", x)
+ x++
+ @}
+ print ""
+@}'
+@end example
+
+@noindent
+This program loops forever once @code{x} gets to 5.
+
+As described above, the @code{continue} statement has no meaning when
+used outside the body of a loop. However, although it was never documented,
+historical implementations of @code{awk} have treated the @code{continue}
+statement outside of a loop as if it were a @code{next} statement
+(@pxref{Next Statement, ,The @code{next} Statement}).
+By default, @code{gawk} silently supports this usage. However, if
+@samp{-W posix} has been specified on the command line
+(@pxref{Command Line, ,Invoking @code{awk}}),
+it will be treated as an error, since the @sc{posix} standard specifies
+that @code{continue} should only be used inside the body of a loop.@refill
+
+@node Next Statement, Next File Statement, Continue Statement, Statements
+@section The @code{next} Statement
+@cindex @code{next} statement
+
+The @code{next} statement forces @code{awk} to immediately stop processing
+the current record and go on to the next record. This means that no
+further rules are executed for the current record. The rest of the
+current rule's action is not executed either.
+
+Contrast this with the effect of the @code{getline} function
+(@pxref{Getline, ,Explicit Input with @code{getline}}). That too causes
+@code{awk} to read the next record immediately, but it does not alter the
+flow of control in any way. So the rest of the current action executes
+with a new input record.
+
+At the highest level, @code{awk} program execution is a loop that reads
+an input record and then tests each rule's pattern against it. If you
+think of this loop as a @code{for} statement whose body contains the
+rules, then the @code{next} statement is analogous to a @code{continue}
+statement: it skips to the end of the body of this implicit loop, and
+executes the increment (which reads another record).
+
+For example, if your @code{awk} program works only on records with four
+fields, and you don't want it to fail when given bad input, you might
+use this rule near the beginning of the program:
+
+@smallexample
+NF != 4 @{
+ printf("line %d skipped: doesn't have 4 fields", FNR) > "/dev/stderr"
+ next
+@}
+@end smallexample
+
+@noindent
+so that the following rules will not see the bad record. The error
+message is redirected to the standard error output stream, as error
+messages should be. @xref{Special Files, ,Standard I/O Streams}.
+
+According to the @sc{posix} standard, the behavior is undefined if
+the @code{next} statement is used in a @code{BEGIN} or @code{END} rule.
+@code{gawk} will treat it as a syntax error.
+
+If the @code{next} statement causes the end of the input to be reached,
+then the code in the @code{END} rules, if any, will be executed.
+@xref{BEGIN/END, ,@code{BEGIN} and @code{END} Special Patterns}.
+
+@node Next File Statement, Exit Statement, Next Statement, Statements
+@section The @code{next file} Statement
+
+@cindex @code{next file} statement
+The @code{next file} statement is similar to the @code{next} statement.
+However, instead of abandoning processing of the current record, the
+@code{next file} statement instructs @code{awk} to stop processing the
+current data file.
+
+Upon execution of the @code{next file} statement, @code{FILENAME} is
+updated to the name of the next data file listed on the command line,
+@code{FNR} is reset to 1, and processing starts over with the first
+rule in the progam. @xref{Built-in Variables}.
+
+If the @code{next file} statement causes the end of the input to be reached,
+then the code in the @code{END} rules, if any, will be executed.
+@xref{BEGIN/END, ,@code{BEGIN} and @code{END} Special Patterns}.
+
+The @code{next file} statement is a @code{gawk} extension; it is not
+(currently) available in any other @code{awk} implementation. You can
+simulate its behavior by creating a library file named @file{nextfile.awk},
+with the following contents. (This sample program uses user-defined
+functions, a feature that has not been presented yet.
+@xref{User-defined, ,User-defined Functions},
+for more information.)@refill
+
+@smallexample
+# nextfile --- function to skip remaining records in current file
+
+# this should be read in before the "main" awk program
+
+function nextfile() @{ _abandon_ = FILENAME; next @}
+
+_abandon_ == FILENAME && FNR > 1 @{ next @}
+_abandon_ == FILENAME && FNR == 1 @{ _abandon_ = "" @}
+@end smallexample
+
+The @code{nextfile} function simply sets a ``private'' variable@footnote{Since
+all variables in @code{awk} are global, this program uses the common
+practice of prefixing the variable name with an underscore. In fact, it
+also suffixes the variable name with an underscore, as extra insurance
+against using a variable name that might be used in some other library
+file.} to the name of the current data file, and then retrieves the next
+record. Since this file is read before the main @code{awk} program,
+the rules that follows the function definition will be executed before the
+rules in the main program. The first rule continues to skip records as long as
+the name of the input file has not changed, and this is not the first
+record in the file. This rule is sufficient most of the time. But what if
+the @emph{same} data file is named twice in a row on the command line?
+This rule would not process the data file the second time. The second rule
+catches this case: If the data file name is what was being skipped, but
+@code{FNR} is 1, then this is the second time the file is being processed,
+and it should not be skipped.
+
+The @code{next file} statement would be useful if you have many data
+files to process, and due to the nature of the data, you expect that you
+would not want to process every record in the file. In order to move on to
+the next data file, you would have to continue scanning the unwanted
+records (as described above). The @code{next file} statement accomplishes
+this much more efficiently.
+
+@ignore
+Would it make sense down the road to nuke `next file' in favor of
+semantics that would make this work?
+
+ function nextfile() { ARGIND++ ; next }
+@end ignore
+
+@node Exit Statement, , Next File Statement, Statements
+@section The @code{exit} Statement
+
+@cindex @code{exit} statement
+The @code{exit} statement causes @code{awk} to immediately stop
+executing the current rule and to stop processing input; any remaining input
+is ignored.@refill
+
+If an @code{exit} statement is executed from a @code{BEGIN} rule the
+program stops processing everything immediately. No input records are
+read. However, if an @code{END} rule is present, it is executed
+(@pxref{BEGIN/END, ,@code{BEGIN} and @code{END} Special Patterns}).
+
+If @code{exit} is used as part of an @code{END} rule, it causes
+the program to stop immediately.
+
+An @code{exit} statement that is part of an ordinary rule (that is, not part
+of a @code{BEGIN} or @code{END} rule) stops the execution of any further
+automatic rules, but the @code{END} rule is executed if there is one.
+If you do not want the @code{END} rule to do its job in this case, you
+can set a variable to nonzero before the @code{exit} statement, and check
+that variable in the @code{END} rule.
+
+If an argument is supplied to @code{exit}, its value is used as the exit
+status code for the @code{awk} process. If no argument is supplied,
+@code{exit} returns status zero (success).@refill
+
+For example, let's say you've discovered an error condition you really
+don't know how to handle. Conventionally, programs report this by
+exiting with a nonzero status. Your @code{awk} program can do this
+using an @code{exit} statement with a nonzero argument. Here's an
+example of this:@refill
+
+@example
+@group
+BEGIN @{
+ if (("date" | getline date_now) < 0) @{
+ print "Can't get system date" > "/dev/stderr"
+ exit 4
+ @}
+@}
+@end group
+@end example
+
+@node Arrays, Built-in, Statements, Top
+@chapter Arrays in @code{awk}
+
+An @dfn{array} is a table of values, called @dfn{elements}. The
+elements of an array are distinguished by their indices. @dfn{Indices}
+may be either numbers or strings. Each array has a name, which looks
+like a variable name, but must not be in use as a variable name in the
+same @code{awk} program.
+
+@menu
+* Array Intro:: Introduction to Arrays
+* Reference to Elements:: How to examine one element of an array.
+* Assigning Elements:: How to change an element of an array.
+* Array Example:: Basic Example of an Array
+* Scanning an Array:: A variation of the @code{for} statement.
+ It loops through the indices of
+ an array's existing elements.
+* Delete:: The @code{delete} statement removes
+ an element from an array.
+* Numeric Array Subscripts:: How to use numbers as subscripts in @code{awk}.
+* Multi-dimensional:: Emulating multi-dimensional arrays in @code{awk}.
+* Multi-scanning:: Scanning multi-dimensional arrays.
+@end menu
+
+@node Array Intro, Reference to Elements, Arrays, Arrays
+@section Introduction to Arrays
+
+@cindex arrays
+The @code{awk} language has one-dimensional @dfn{arrays} for storing groups
+of related strings or numbers.
+
+Every @code{awk} array must have a name. Array names have the same
+syntax as variable names; any valid variable name would also be a valid
+array name. But you cannot use one name in both ways (as an array and
+as a variable) in one @code{awk} program.
+
+Arrays in @code{awk} superficially resemble arrays in other programming
+languages; but there are fundamental differences. In @code{awk}, you
+don't need to specify the size of an array before you start to use it.
+Additionally, any number or string in @code{awk} may be used as an
+array index.
+
+In most other languages, you have to @dfn{declare} an array and specify
+how many elements or components it contains. In such languages, the
+declaration causes a contiguous block of memory to be allocated for that
+many elements. An index in the array must be a positive integer; for
+example, the index 0 specifies the first element in the array, which is
+actually stored at the beginning of the block of memory. Index 1
+specifies the second element, which is stored in memory right after the
+first element, and so on. It is impossible to add more elements to the
+array, because it has room for only as many elements as you declared.
+
+A contiguous array of four elements might look like this,
+conceptually, if the element values are @code{8}, @code{"foo"},
+@code{""} and @code{30}:@refill
+
+@example
++---------+---------+--------+---------+
+| 8 | "foo" | "" | 30 | @r{value}
++---------+---------+--------+---------+
+ 0 1 2 3 @r{index}
+@end example
+
+@noindent
+Only the values are stored; the indices are implicit from the order of
+the values. @code{8} is the value at index 0, because @code{8} appears in the
+position with 0 elements before it.
+
+@cindex arrays, definition of
+@cindex associative arrays
+Arrays in @code{awk} are different: they are @dfn{associative}. This means
+that each array is a collection of pairs: an index, and its corresponding
+array element value:
+
+@example
+@r{Element} 4 @r{Value} 30
+@r{Element} 2 @r{Value} "foo"
+@r{Element} 1 @r{Value} 8
+@r{Element} 3 @r{Value} ""
+@end example
+
+@noindent
+We have shown the pairs in jumbled order because their order is irrelevant.
+
+One advantage of an associative array is that new pairs can be added
+at any time. For example, suppose we add to the above array a tenth element
+whose value is @w{@code{"number ten"}}. The result is this:
+
+@example
+@r{Element} 10 @r{Value} "number ten"
+@r{Element} 4 @r{Value} 30
+@r{Element} 2 @r{Value} "foo"
+@r{Element} 1 @r{Value} 8
+@r{Element} 3 @r{Value} ""
+@end example
+
+@noindent
+Now the array is @dfn{sparse} (i.e., some indices are missing): it has
+elements 1--4 and 10, but doesn't have elements 5, 6, 7, 8, or 9.@refill
+
+Another consequence of associative arrays is that the indices don't
+have to be positive integers. Any number, or even a string, can be
+an index. For example, here is an array which translates words from
+English into French:
+
+@example
+@r{Element} "dog" @r{Value} "chien"
+@r{Element} "cat" @r{Value} "chat"
+@r{Element} "one" @r{Value} "un"
+@r{Element} 1 @r{Value} "un"
+@end example
+
+@noindent
+Here we decided to translate the number 1 in both spelled-out and
+numeric form---thus illustrating that a single array can have both
+numbers and strings as indices.
+
+When @code{awk} creates an array for you, e.g., with the @code{split}
+built-in function,
+that array's indices are consecutive integers starting at 1.
+(@xref{String Functions, ,Built-in Functions for String Manipulation}.)
+
+@node Reference to Elements, Assigning Elements, Array Intro, Arrays
+@section Referring to an Array Element
+@cindex array reference
+@cindex element of array
+@cindex reference to array
+
+The principal way of using an array is to refer to one of its elements.
+An array reference is an expression which looks like this:
+
+@example
+@var{array}[@var{index}]
+@end example
+
+@noindent
+Here, @var{array} is the name of an array. The expression @var{index} is
+the index of the element of the array that you want.
+
+The value of the array reference is the current value of that array
+element. For example, @code{foo[4.3]} is an expression for the element
+of array @code{foo} at index 4.3.
+
+If you refer to an array element that has no recorded value, the value
+of the reference is @code{""}, the null string. This includes elements
+to which you have not assigned any value, and elements that have been
+deleted (@pxref{Delete, ,The @code{delete} Statement}). Such a reference
+automatically creates that array element, with the null string as its value.
+(In some cases, this is unfortunate, because it might waste memory inside
+@code{awk}).
+
+@cindex arrays, presence of elements
+You can find out if an element exists in an array at a certain index with
+the expression:
+
+@example
+@var{index} in @var{array}
+@end example
+
+@noindent
+This expression tests whether or not the particular index exists,
+without the side effect of creating that element if it is not present.
+The expression has the value 1 (true) if @code{@var{array}[@var{index}]}
+exists, and 0 (false) if it does not exist.@refill
+
+For example, to test whether the array @code{frequencies} contains the
+index @code{"2"}, you could write this statement:@refill
+
+@smallexample
+if ("2" in frequencies) print "Subscript \"2\" is present."
+@end smallexample
+
+Note that this is @emph{not} a test of whether or not the array
+@code{frequencies} contains an element whose @emph{value} is @code{"2"}.
+(There is no way to do that except to scan all the elements.) Also, this
+@emph{does not} create @code{frequencies["2"]}, while the following
+(incorrect) alternative would do so:@refill
+
+@smallexample
+if (frequencies["2"] != "") print "Subscript \"2\" is present."
+@end smallexample
+
+@node Assigning Elements, Array Example, Reference to Elements, Arrays
+@section Assigning Array Elements
+@cindex array assignment
+@cindex element assignment
+
+Array elements are lvalues: they can be assigned values just like
+@code{awk} variables:
+
+@example
+@var{array}[@var{subscript}] = @var{value}
+@end example
+
+@noindent
+Here @var{array} is the name of your array. The expression
+@var{subscript} is the index of the element of the array that you want
+to assign a value. The expression @var{value} is the value you are
+assigning to that element of the array.@refill
+
+@node Array Example, Scanning an Array, Assigning Elements, Arrays
+@section Basic Example of an Array
+
+The following program takes a list of lines, each beginning with a line
+number, and prints them out in order of line number. The line numbers are
+not in order, however, when they are first read: they are scrambled. This
+program sorts the lines by making an array using the line numbers as
+subscripts. It then prints out the lines in sorted order of their numbers.
+It is a very simple program, and gets confused if it encounters repeated
+numbers, gaps, or lines that don't begin with a number.@refill
+
+@example
+@{
+ if ($1 > max)
+ max = $1
+ arr[$1] = $0
+@}
+
+END @{
+ for (x = 1; x <= max; x++)
+ print arr[x]
+@}
+@end example
+
+The first rule keeps track of the largest line number seen so far;
+it also stores each line into the array @code{arr}, at an index that
+is the line's number.
+
+The second rule runs after all the input has been read, to print out
+all the lines.
+
+When this program is run with the following input:
+
+@example
+5 I am the Five man
+2 Who are you? The new number two!
+4 . . . And four on the floor
+1 Who is number one?
+3 I three you.
+@end example
+
+@noindent
+its output is this:
+
+@example
+1 Who is number one?
+2 Who are you? The new number two!
+3 I three you.
+4 . . . And four on the floor
+5 I am the Five man
+@end example
+
+If a line number is repeated, the last line with a given number overrides
+the others.
+
+Gaps in the line numbers can be handled with an easy improvement to the
+program's @code{END} rule:
+
+@example
+END @{
+ for (x = 1; x <= max; x++)
+ if (x in arr)
+ print arr[x]
+@}
+@end example
+
+@node Scanning an Array, Delete, Array Example, Arrays
+@section Scanning all Elements of an Array
+@cindex @code{for (x in @dots{})}
+@cindex arrays, special @code{for} statement
+@cindex scanning an array
+
+In programs that use arrays, often you need a loop that executes
+once for each element of an array. In other languages, where arrays are
+contiguous and indices are limited to positive integers, this is
+easy: the largest index is one less than the length of the array, and you can
+find all the valid indices by counting from zero up to that value. This
+technique won't do the job in @code{awk}, since any number or string
+may be an array index. So @code{awk} has a special kind of @code{for}
+statement for scanning an array:
+
+@example
+for (@var{var} in @var{array})
+ @var{body}
+@end example
+
+@noindent
+This loop executes @var{body} once for each different value that your
+program has previously used as an index in @var{array}, with the
+variable @var{var} set to that index.@refill
+
+Here is a program that uses this form of the @code{for} statement. The
+first rule scans the input records and notes which words appear (at
+least once) in the input, by storing a 1 into the array @code{used} with
+the word as index. The second rule scans the elements of @code{used} to
+find all the distinct words that appear in the input. It prints each
+word that is more than 10 characters long, and also prints the number of
+such words. @xref{Built-in, ,Built-in Functions}, for more information
+on the built-in function @code{length}.
+
+@smallexample
+# Record a 1 for each word that is used at least once.
+@{
+ for (i = 1; i <= NF; i++)
+ used[$i] = 1
+@}
+
+# Find number of distinct words more than 10 characters long.
+END @{
+ for (x in used)
+ if (length(x) > 10) @{
+ ++num_long_words
+ print x
+ @}
+ print num_long_words, "words longer than 10 characters"
+@}
+@end smallexample
+
+@noindent
+@xref{Sample Program}, for a more detailed example of this type.
+
+The order in which elements of the array are accessed by this statement
+is determined by the internal arrangement of the array elements within
+@code{awk} and cannot be controlled or changed. This can lead to
+problems if new elements are added to @var{array} by statements in
+@var{body}; you cannot predict whether or not the @code{for} loop will
+reach them. Similarly, changing @var{var} inside the loop can produce
+strange results. It is best to avoid such things.@refill
+
+@node Delete, Numeric Array Subscripts, Scanning an Array, Arrays
+@section The @code{delete} Statement
+@cindex @code{delete} statement
+@cindex deleting elements of arrays
+@cindex removing elements of arrays
+@cindex arrays, deleting an element
+
+You can remove an individual element of an array using the @code{delete}
+statement:
+
+@example
+delete @var{array}[@var{index}]
+@end example
+
+You can not refer to an array element after it has been deleted;
+it is as if you had never referred
+to it and had never given it any value. You can no longer obtain any
+value the element once had.
+
+Here is an example of deleting elements in an array:
+
+@example
+for (i in frequencies)
+ delete frequencies[i]
+@end example
+
+@noindent
+This example removes all the elements from the array @code{frequencies}.
+
+If you delete an element, a subsequent @code{for} statement to scan the array
+will not report that element, and the @code{in} operator to check for
+the presence of that element will return 0:
+
+@example
+delete foo[4]
+if (4 in foo)
+ print "This will never be printed"
+@end example
+
+It is not an error to delete an element which does not exist.
+
+@node Numeric Array Subscripts, Multi-dimensional, Delete, Arrays
+@section Using Numbers to Subscript Arrays
+
+An important aspect of arrays to remember is that array subscripts
+are @emph{always} strings. If you use a numeric value as a subscript,
+it will be converted to a string value before it is used for subscripting
+(@pxref{Conversion, ,Conversion of Strings and Numbers}).
+
+@cindex conversions, during subscripting
+@cindex numbers, used as subscripts
+@vindex CONVFMT
+This means that the value of the @code{CONVFMT} can potentially
+affect how your program accesses elements of an array. For example:
+
+@example
+a = b = 12.153
+data[a] = 1
+CONVFMT = "%2.2f"
+if (b in data)
+ printf "%s is in data", b
+else
+ printf "%s is not in data", b
+@end example
+
+@noindent
+should print @samp{12.15 is not in data}. The first statement gives
+both @code{a} and @code{b} the same numeric value. Assigning to
+@code{data[a]} first gives @code{a} the string value @code{"12.153"}
+(using the default conversion value of @code{CONVFMT}, @code{"%.6g"}),
+and then assigns 1 to @code{data["12.153"]}. The program then changes
+the value of @code{CONVFMT}. The test @samp{(b in data)} forces @code{b}
+to be converted to a string, this time @code{"12.15"}, since the value of
+@code{CONVFMT} only allows two significant digits. This test fails,
+since @code{"12.15"} is a different string from @code{"12.153"}.@refill
+
+According to the rules for conversions
+(@pxref{Conversion, ,Conversion of Strings and Numbers}), integer
+values are always converted to strings as integers, no matter what the
+value of @code{CONVFMT} may happen to be. So the usual case of@refill
+
+@example
+for (i = 1; i <= maxsub; i++)
+ @i{do something with} array[i]
+@end example
+
+@noindent
+will work, no matter what the value of @code{CONVFMT}.
+
+Like many things in @code{awk}, the majority of the time things work
+as you would expect them to work. But it is useful to have a precise
+knowledge of the actual rules, since sometimes they can have a subtle
+effect on your programs.
+
+@node Multi-dimensional, Multi-scanning, Numeric Array Subscripts, Arrays
+@section Multi-dimensional Arrays
+
+@c the following index entry is an overfull hbox. --mew 30jan1992
+@cindex subscripts in arrays
+@cindex arrays, multi-dimensional subscripts
+@cindex multi-dimensional subscripts
+A multi-dimensional array is an array in which an element is identified
+by a sequence of indices, not a single index. For example, a
+two-dimensional array requires two indices. The usual way (in most
+languages, including @code{awk}) to refer to an element of a
+two-dimensional array named @code{grid} is with
+@code{grid[@var{x},@var{y}]}.
+
+@vindex SUBSEP
+Multi-dimensional arrays are supported in @code{awk} through
+concatenation of indices into one string. What happens is that
+@code{awk} converts the indices into strings
+(@pxref{Conversion, ,Conversion of Strings and Numbers}) and
+concatenates them together, with a separator between them. This creates
+a single string that describes the values of the separate indices. The
+combined string is used as a single index into an ordinary,
+one-dimensional array. The separator used is the value of the built-in
+variable @code{SUBSEP}.@refill
+
+For example, suppose we evaluate the expression @code{foo[5,12]="value"}
+when the value of @code{SUBSEP} is @code{"@@"}. The numbers 5 and 12 are
+converted to strings and
+concatenated with an @samp{@@} between them, yielding @code{"5@@12"}; thus,
+the array element @code{foo["5@@12"]} is set to @code{"value"}.@refill
+
+Once the element's value is stored, @code{awk} has no record of whether
+it was stored with a single index or a sequence of indices. The two
+expressions @code{foo[5,12]} and @w{@code{foo[5 SUBSEP 12]}} always have
+the same value.
+
+The default value of @code{SUBSEP} is the string @code{"\034"},
+which contains a nonprinting character that is unlikely to appear in an
+@code{awk} program or in the input data.
+
+The usefulness of choosing an unlikely character comes from the fact
+that index values that contain a string matching @code{SUBSEP} lead to
+combined strings that are ambiguous. Suppose that @code{SUBSEP} were
+@code{"@@"}; then @w{@code{foo["a@@b", "c"]}} and @w{@code{foo["a",
+"b@@c"]}} would be indistinguishable because both would actually be
+stored as @code{foo["a@@b@@c"]}. Because @code{SUBSEP} is
+@code{"\034"}, such confusion can arise only when an index
+contains the character with ASCII code 034, which is a rare
+event.@refill
+
+You can test whether a particular index-sequence exists in a
+``multi-dimensional'' array with the same operator @code{in} used for single
+dimensional arrays. Instead of a single index as the left-hand operand,
+write the whole sequence of indices, separated by commas, in
+parentheses:@refill
+
+@example
+(@var{subscript1}, @var{subscript2}, @dots{}) in @var{array}
+@end example
+
+The following example treats its input as a two-dimensional array of
+fields; it rotates this array 90 degrees clockwise and prints the
+result. It assumes that all lines have the same number of
+elements.
+
+@example
+awk '@{
+ if (max_nf < NF)
+ max_nf = NF
+ max_nr = NR
+ for (x = 1; x <= NF; x++)
+ vector[x, NR] = $x
+@}
+
+END @{
+ for (x = 1; x <= max_nf; x++) @{
+ for (y = max_nr; y >= 1; --y)
+ printf("%s ", vector[x, y])
+ printf("\n")
+ @}
+@}'
+@end example
+
+@noindent
+When given the input:
+
+@example
+@group
+1 2 3 4 5 6
+2 3 4 5 6 1
+3 4 5 6 1 2
+4 5 6 1 2 3
+@end group
+@end example
+
+@noindent
+it produces:
+
+@example
+@group
+4 3 2 1
+5 4 3 2
+6 5 4 3
+1 6 5 4
+2 1 6 5
+3 2 1 6
+@end group
+@end example
+
+@node Multi-scanning, , Multi-dimensional, Arrays
+@section Scanning Multi-dimensional Arrays
+
+There is no special @code{for} statement for scanning a
+``multi-dimensional'' array; there cannot be one, because in truth there
+are no multi-dimensional arrays or elements; there is only a
+multi-dimensional @emph{way of accessing} an array.
+
+However, if your program has an array that is always accessed as
+multi-dimensional, you can get the effect of scanning it by combining
+the scanning @code{for} statement
+(@pxref{Scanning an Array, ,Scanning all Elements of an Array}) with the
+@code{split} built-in function
+(@pxref{String Functions, ,Built-in Functions for String Manipulation}).
+It works like this:@refill
+
+@example
+for (combined in @var{array}) @{
+ split(combined, separate, SUBSEP)
+ @dots{}
+@}
+@end example
+
+@noindent
+This finds each concatenated, combined index in the array, and splits it
+into the individual indices by breaking it apart where the value of
+@code{SUBSEP} appears. The split-out indices become the elements of
+the array @code{separate}.
+
+Thus, suppose you have previously stored in @code{@var{array}[1,
+"foo"]}; then an element with index @code{"1\034foo"} exists in
+@var{array}. (Recall that the default value of @code{SUBSEP} contains
+the character with code 034.) Sooner or later the @code{for} statement
+will find that index and do an iteration with @code{combined} set to
+@code{"1\034foo"}. Then the @code{split} function is called as
+follows:
+
+@example
+split("1\034foo", separate, "\034")
+@end example
+
+@noindent
+The result of this is to set @code{separate[1]} to 1 and @code{separate[2]}
+to @code{"foo"}. Presto, the original sequence of separate indices has
+been recovered.
+
+@node Built-in, User-defined, Arrays, Top
+@chapter Built-in Functions
+
+@cindex built-in functions
+@dfn{Built-in} functions are functions that are always available for
+your @code{awk} program to call. This chapter defines all the built-in
+functions in @code{awk}; some of them are mentioned in other sections,
+but they are summarized here for your convenience. (You can also define
+new functions yourself. @xref{User-defined, ,User-defined Functions}.)
+
+@menu
+* Calling Built-in:: How to call built-in functions.
+* Numeric Functions:: Functions that work with numbers,
+ including @code{int}, @code{sin} and @code{rand}.
+* String Functions:: Functions for string manipulation,
+ such as @code{split}, @code{match}, and @code{sprintf}.
+* I/O Functions:: Functions for files and shell commands.
+* Time Functions:: Functions for dealing with time stamps.
+@end menu
+
+@node Calling Built-in, Numeric Functions, Built-in, Built-in
+@section Calling Built-in Functions
+
+To call a built-in function, write the name of the function followed
+by arguments in parentheses. For example, @code{atan2(y + z, 1)}
+is a call to the function @code{atan2}, with two arguments.
+
+Whitespace is ignored between the built-in function name and the
+open-parenthesis, but we recommend that you avoid using whitespace
+there. User-defined functions do not permit whitespace in this way, and
+you will find it easier to avoid mistakes by following a simple
+convention which always works: no whitespace after a function name.
+
+Each built-in function accepts a certain number of arguments. In most
+cases, any extra arguments given to built-in functions are ignored. The
+defaults for omitted arguments vary from function to function and are
+described under the individual functions.
+
+When a function is called, expressions that create the function's actual
+parameters are evaluated completely before the function call is performed.
+For example, in the code fragment:
+
+@example
+i = 4
+j = sqrt(i++)
+@end example
+
+@noindent
+the variable @code{i} is set to 5 before @code{sqrt} is called
+with a value of 4 for its actual parameter.
+
+@node Numeric Functions, String Functions, Calling Built-in, Built-in
+@section Numeric Built-in Functions
+@c I didn't make all the examples small because a couple of them were
+@c short already. --mew 29jan1992
+
+Here is a full list of built-in functions that work with numbers:
+
+@table @code
+@item int(@var{x})
+This gives you the integer part of @var{x}, truncated toward 0. This
+produces the nearest integer to @var{x}, located between @var{x} and 0.
+
+For example, @code{int(3)} is 3, @code{int(3.9)} is 3, @code{int(-3.9)}
+is @minus{}3, and @code{int(-3)} is @minus{}3 as well.@refill
+
+@item sqrt(@var{x})
+This gives you the positive square root of @var{x}. It reports an error
+if @var{x} is negative. Thus, @code{sqrt(4)} is 2.@refill
+
+@item exp(@var{x})
+This gives you the exponential of @var{x}, or reports an error if
+@var{x} is out of range. The range of values @var{x} can have depends
+on your machine's floating point representation.@refill
+
+@item log(@var{x})
+This gives you the natural logarithm of @var{x}, if @var{x} is positive;
+otherwise, it reports an error.@refill
+
+@item sin(@var{x})
+This gives you the sine of @var{x}, with @var{x} in radians.
+
+@item cos(@var{x})
+This gives you the cosine of @var{x}, with @var{x} in radians.
+
+@item atan2(@var{y}, @var{x})
+This gives you the arctangent of @code{@var{y} / @var{x}} in radians.
+
+@item rand()
+This gives you a random number. The values of @code{rand} are
+uniformly-distributed between 0 and 1. The value is never 0 and never
+1.
+
+Often you want random integers instead. Here is a user-defined function
+you can use to obtain a random nonnegative integer less than @var{n}:
+
+@example
+function randint(n) @{
+ return int(n * rand())
+@}
+@end example
+
+@noindent
+The multiplication produces a random real number greater than 0 and less
+than @var{n}. We then make it an integer (using @code{int}) between 0
+and @code{@var{n} @minus{} 1}.
+
+Here is an example where a similar function is used to produce
+random integers between 1 and @var{n}. Note that this program will
+print a new random number for each input record.
+
+@smallexample
+awk '
+# Function to roll a simulated die.
+function roll(n) @{ return 1 + int(rand() * n) @}
+
+# Roll 3 six-sided dice and print total number of points.
+@{
+ printf("%d points\n", roll(6)+roll(6)+roll(6))
+@}'
+@end smallexample
+
+@strong{Note:} @code{rand} starts generating numbers from the same
+point, or @dfn{seed}, each time you run @code{awk}. This means that
+a program will produce the same results each time you run it.
+The numbers are random within one @code{awk} run, but predictable
+from run to run. This is convenient for debugging, but if you want
+a program to do different things each time it is used, you must change
+the seed to a value that will be different in each run. To do this,
+use @code{srand}.
+
+@item srand(@var{x})
+The function @code{srand} sets the starting point, or @dfn{seed},
+for generating random numbers to the value @var{x}.
+
+Each seed value leads to a particular sequence of ``random'' numbers.
+Thus, if you set the seed to the same value a second time, you will get
+the same sequence of ``random'' numbers again.
+
+If you omit the argument @var{x}, as in @code{srand()}, then the current
+date and time of day are used for a seed. This is the way to get random
+numbers that are truly unpredictable.
+
+The return value of @code{srand} is the previous seed. This makes it
+easy to keep track of the seeds for use in consistently reproducing
+sequences of random numbers.
+@end table
+
+@node String Functions, I/O Functions, Numeric Functions, Built-in
+@section Built-in Functions for String Manipulation
+
+The functions in this section look at or change the text of one or more
+strings.
+
+@table @code
+@item index(@var{in}, @var{find})
+@findex match
+This searches the string @var{in} for the first occurrence of the string
+@var{find}, and returns the position in characters where that occurrence
+begins in the string @var{in}. For example:@refill
+
+@smallexample
+awk 'BEGIN @{ print index("peanut", "an") @}'
+@end smallexample
+
+@noindent
+prints @samp{3}. If @var{find} is not found, @code{index} returns 0.
+(Remember that string indices in @code{awk} start at 1.)
+
+@item length(@var{string})
+@findex length
+This gives you the number of characters in @var{string}. If
+@var{string} is a number, the length of the digit string representing
+that number is returned. For example, @code{length("abcde")} is 5. By
+contrast, @code{length(15 * 35)} works out to 3. How? Well, 15 * 35 =
+525, and 525 is then converted to the string @samp{"525"}, which has
+three characters.
+
+If no argument is supplied, @code{length} returns the length of @code{$0}.
+
+In older versions of @code{awk}, you could call the @code{length} function
+without any parentheses. Doing so is marked as ``deprecated'' in the
+@sc{posix} standard. This means that while you can do this in your
+programs, it is a feature that can eventually be removed from a future
+version of the standard. Therefore, for maximal portability of your
+@code{awk} programs you should always supply the parentheses.
+
+@item match(@var{string}, @var{regexp})
+@findex match
+The @code{match} function searches the string, @var{string}, for the
+longest, leftmost substring matched by the regular expression,
+@var{regexp}. It returns the character position, or @dfn{index}, of
+where that substring begins (1, if it starts at the beginning of
+@var{string}). If no match if found, it returns 0.
+
+@vindex RSTART
+@vindex RLENGTH
+The @code{match} function sets the built-in variable @code{RSTART} to
+the index. It also sets the built-in variable @code{RLENGTH} to the
+length in characters of the matched substring. If no match is found,
+@code{RSTART} is set to 0, and @code{RLENGTH} to @minus{}1.
+
+For example:
+
+@smallexample
+awk '@{
+ if ($1 == "FIND")
+ regex = $2
+ else @{
+ where = match($0, regex)
+ if (where)
+ print "Match of", regex, "found at", where, "in", $0
+ @}
+@}'
+@end smallexample
+
+@noindent
+This program looks for lines that match the regular expression stored in
+the variable @code{regex}. This regular expression can be changed. If the
+first word on a line is @samp{FIND}, @code{regex} is changed to be the
+second word on that line. Therefore, given:
+
+@smallexample
+FIND fo*bar
+My program was a foobar
+But none of it would doobar
+FIND Melvin
+JF+KM
+This line is property of The Reality Engineering Co.
+This file created by Melvin.
+@end smallexample
+
+@noindent
+@code{awk} prints:
+
+@smallexample
+Match of fo*bar found at 18 in My program was a foobar
+Match of Melvin found at 26 in This file created by Melvin.
+@end smallexample
+
+@item split(@var{string}, @var{array}, @var{fieldsep})
+@findex split
+This divides @var{string} into pieces separated by @var{fieldsep},
+and stores the pieces in @var{array}. The first piece is stored in
+@code{@var{array}[1]}, the second piece in @code{@var{array}[2]}, and so
+forth. The string value of the third argument, @var{fieldsep}, is
+a regexp describing where to split @var{string} (much as @code{FS} can
+be a regexp describing where to split input records). If
+the @var{fieldsep} is omitted, the value of @code{FS} is used.
+@code{split} returns the number of elements created.@refill
+
+The @code{split} function, then, splits strings into pieces in a
+manner similar to the way input lines are split into fields. For example:
+
+@smallexample
+split("auto-da-fe", a, "-")
+@end smallexample
+
+@noindent
+splits the string @samp{auto-da-fe} into three fields using @samp{-} as the
+separator. It sets the contents of the array @code{a} as follows:
+
+@smallexample
+a[1] = "auto"
+a[2] = "da"
+a[3] = "fe"
+@end smallexample
+
+@noindent
+The value returned by this call to @code{split} is 3.
+
+As with input field-splitting, when the value of @var{fieldsep} is
+@code{" "}, leading and trailing whitespace is ignored, and the elements
+are separated by runs of whitespace.
+
+@item sprintf(@var{format}, @var{expression1},@dots{})
+@findex sprintf
+This returns (without printing) the string that @code{printf} would
+have printed out with the same arguments
+(@pxref{Printf, ,Using @code{printf} Statements for Fancier Printing}).
+For example:@refill
+
+@smallexample
+sprintf("pi = %.2f (approx.)", 22/7)
+@end smallexample
+
+@noindent
+returns the string @w{@code{"pi = 3.14 (approx.)"}}.
+
+@item sub(@var{regexp}, @var{replacement}, @var{target})
+@findex sub
+The @code{sub} function alters the value of @var{target}.
+It searches this value, which should be a string, for the
+leftmost substring matched by the regular expression, @var{regexp},
+extending this match as far as possible. Then the entire string is
+changed by replacing the matched text with @var{replacement}.
+The modified string becomes the new value of @var{target}.
+
+This function is peculiar because @var{target} is not simply
+used to compute a value, and not just any expression will do: it
+must be a variable, field or array reference, so that @code{sub} can
+store a modified value there. If this argument is omitted, then the
+default is to use and alter @code{$0}.
+
+For example:@refill
+
+@smallexample
+str = "water, water, everywhere"
+sub(/at/, "ith", str)
+@end smallexample
+
+@noindent
+sets @code{str} to @w{@code{"wither, water, everywhere"}}, by replacing the
+leftmost, longest occurrence of @samp{at} with @samp{ith}.
+
+The @code{sub} function returns the number of substitutions made (either
+one or zero).
+
+If the special character @samp{&} appears in @var{replacement}, it
+stands for the precise substring that was matched by @var{regexp}. (If
+the regexp can match more than one string, then this precise substring
+may vary.) For example:@refill
+
+@smallexample
+awk '@{ sub(/candidate/, "& and his wife"); print @}'
+@end smallexample
+
+@noindent
+changes the first occurrence of @samp{candidate} to @samp{candidate
+and his wife} on each input line.
+
+Here is another example:
+
+@smallexample
+awk 'BEGIN @{
+ str = "daabaaa"
+ sub(/a*/, "c&c", str)
+ print str
+@}'
+@end smallexample
+
+@noindent
+prints @samp{dcaacbaaa}. This show how @samp{&} can represent a non-constant
+string, and also illustrates the ``leftmost, longest'' rule.
+
+The effect of this special character (@samp{&}) can be turned off by putting a
+backslash before it in the string. As usual, to insert one backslash in
+the string, you must write two backslashes. Therefore, write @samp{\\&}
+in a string constant to include a literal @samp{&} in the replacement.
+For example, here is how to replace the first @samp{|} on each line with
+an @samp{&}:@refill
+
+@smallexample
+awk '@{ sub(/\|/, "\\&"); print @}'
+@end smallexample
+
+@strong{Note:} as mentioned above, the third argument to @code{sub} must
+be an lvalue. Some versions of @code{awk} allow the third argument to
+be an expression which is not an lvalue. In such a case, @code{sub}
+would still search for the pattern and return 0 or 1, but the result of
+the substitution (if any) would be thrown away because there is no place
+to put it. Such versions of @code{awk} accept expressions like
+this:@refill
+
+@smallexample
+sub(/USA/, "United States", "the USA and Canada")
+@end smallexample
+
+@noindent
+But that is considered erroneous in @code{gawk}.
+
+@item gsub(@var{regexp}, @var{replacement}, @var{target})
+@findex gsub
+This is similar to the @code{sub} function, except @code{gsub} replaces
+@emph{all} of the longest, leftmost, @emph{nonoverlapping} matching
+substrings it can find. The @samp{g} in @code{gsub} stands for
+``global,'' which means replace everywhere. For example:@refill
+
+@smallexample
+awk '@{ gsub(/Britain/, "United Kingdom"); print @}'
+@end smallexample
+
+@noindent
+replaces all occurrences of the string @samp{Britain} with @samp{United
+Kingdom} for all input records.@refill
+
+The @code{gsub} function returns the number of substitutions made. If
+the variable to be searched and altered, @var{target}, is
+omitted, then the entire input record, @code{$0}, is used.@refill
+
+As in @code{sub}, the characters @samp{&} and @samp{\} are special, and
+the third argument must be an lvalue.
+
+@item substr(@var{string}, @var{start}, @var{length})
+@findex substr
+This returns a @var{length}-character-long substring of @var{string},
+starting at character number @var{start}. The first character of a
+string is character number one. For example,
+@code{substr("washington", 5, 3)} returns @code{"ing"}.@refill
+
+If @var{length} is not present, this function returns the whole suffix of
+@var{string} that begins at character number @var{start}. For example,
+@code{substr("washington", 5)} returns @code{"ington"}. This is also
+the case if @var{length} is greater than the number of characters remaining
+in the string, counting from character number @var{start}.
+
+@item tolower(@var{string})
+@findex tolower
+This returns a copy of @var{string}, with each upper-case character
+in the string replaced with its corresponding lower-case character.
+Nonalphabetic characters are left unchanged. For example,
+@code{tolower("MiXeD cAsE 123")} returns @code{"mixed case 123"}.
+
+@item toupper(@var{string})
+@findex toupper
+This returns a copy of @var{string}, with each lower-case character
+in the string replaced with its corresponding upper-case character.
+Nonalphabetic characters are left unchanged. For example,
+@code{toupper("MiXeD cAsE 123")} returns @code{"MIXED CASE 123"}.
+@end table
+
+@node I/O Functions, Time Functions, String Functions, Built-in
+@section Built-in Functions for Input/Output
+
+@table @code
+@item close(@var{filename})
+Close the file @var{filename}, for input or output. The argument may
+alternatively be a shell command that was used for redirecting to or
+from a pipe; then the pipe is closed.
+
+@xref{Close Input, ,Closing Input Files and Pipes}, regarding closing
+input files and pipes. @xref{Close Output, ,Closing Output Files and Pipes},
+regarding closing output files and pipes.@refill
+
+@item system(@var{command})
+@findex system
+@c the following index entry is an overfull hbox. --mew 30jan1992
+@cindex interaction, @code{awk} and other programs
+The system function allows the user to execute operating system commands
+and then return to the @code{awk} program. The @code{system} function
+executes the command given by the string @var{command}. It returns, as
+its value, the status returned by the command that was executed.
+
+For example, if the following fragment of code is put in your @code{awk}
+program:
+
+@smallexample
+END @{
+ system("mail -s 'awk run done' operator < /dev/null")
+@}
+@end smallexample
+
+@noindent
+the system operator will be sent mail when the @code{awk} program
+finishes processing input and begins its end-of-input processing.
+
+Note that much the same result can be obtained by redirecting
+@code{print} or @code{printf} into a pipe. However, if your @code{awk}
+program is interactive, @code{system} is useful for cranking up large
+self-contained programs, such as a shell or an editor.@refill
+
+Some operating systems cannot implement the @code{system} function.
+@code{system} causes a fatal error if it is not supported.
+@end table
+
+@c fakenode --- for prepinfo
+@subheading Controlling Output Buffering with @code{system}
+@cindex flushing buffers
+@cindex buffers, flushing
+@cindex buffering output
+@cindex output, buffering
+
+Many utility programs will @dfn{buffer} their output; they save information
+to be written to a disk file or terminal in memory, until there is enough
+to be written in one operation. This is often more efficient than writing
+every little bit of information as soon as it is ready. However, sometimes
+it is necessary to force a program to @dfn{flush} its buffers; that is,
+write the information to its destination, even if a buffer is not full.
+You can do this from your @code{awk} program by calling @code{system}
+with a null string as its argument:
+
+@example
+system("") # flush output
+@end example
+
+@noindent
+@code{gawk} treats this use of the @code{system} function as a special
+case, and is smart enough not to run a shell (or other command
+interpreter) with the empty command. Therefore, with @code{gawk}, this
+idiom is not only useful, it is efficient. While this idiom should work
+with other @code{awk} implementations, it will not necessarily avoid
+starting an unnecessary shell.
+@ignore
+Need a better explanation, perhaps in a separate paragraph. Explain that
+for
+
+awk 'BEGIN { print "hi"
+ system("echo hello")
+ print "howdy" }'
+
+that the output had better be
+
+ hi
+ hello
+ howdy
+
+and not
+
+ hello
+ hi
+ howdy
+
+which it would be if awk did not flush its buffers before calling system.
+@end ignore
+
+@node Time Functions, , I/O Functions, Built-in
+@section Functions for Dealing with Time Stamps
+
+@cindex time stamps
+@cindex time of day
+A common use for @code{awk} programs is the processing of log files.
+Log files often contain time stamp information, indicating when a
+particular log record was written. Many programs log their time stamp
+in the form returned by the @code{time} system call, which is the
+number of seconds since a particular epoch. On @sc{posix} systems,
+it is the number of seconds since Midnight, January 1, 1970, @sc{utc}.
+
+In order to make it easier to process such log files, and to easily produce
+useful reports, @code{gawk} provides two functions for working with time
+stamps. Both of these are @code{gawk} extensions; they are not specified
+in the @sc{posix} standard, nor are they in any other known version
+of @code{awk}.
+
+@table @code
+@item systime()
+@findex systime
+This function returns the current time as the number of seconds since
+the system epoch. On @sc{posix} systems, this is the number of seconds
+since Midnight, January 1, 1970, @sc{utc}. It may be a different number on
+other systems.
+
+@item strftime(@var{format}, @var{timestamp})
+@findex strftime
+This function returns a string. It is similar to the function of the
+same name in the @sc{ansi} C standard library. The time specified by
+@var{timestamp} is used to produce a string, based on the contents
+of the @var{format} string.
+@end table
+
+The @code{systime} function allows you to compare a time stamp from a
+log file with the current time of day. In particular, it is easy to
+determine how long ago a particular record was logged. It also allows
+you to produce log records using the ``seconds since the epoch'' format.
+
+The @code{strftime} function allows you to easily turn a time stamp
+into human-readable information. It is similar in nature to the @code{sprintf}
+function, copying non-format specification characters verbatim to the
+returned string, and substituting date and time values for format
+specifications in the @var{format} string. If no @var{timestamp} argument
+is supplied, @code{gawk} will use the current time of day as the
+time stamp.@refill
+
+@code{strftime} is guaranteed by the @sc{ansi} C standard to support
+the following date format specifications:
+
+@table @code
+@item %a
+The locale's abbreviated weekday name.
+
+@item %A
+The locale's full weekday name.
+
+@item %b
+The locale's abbreviated month name.
+
+@item %B
+The locale's full month name.
+
+@item %c
+The locale's ``appropriate'' date and time representation.
+
+@item %d
+The day of the month as a decimal number (01--31).
+
+@item %H
+The hour (24-hour clock) as a decimal number (00--23).
+
+@item %I
+The hour (12-hour clock) as a decimal number (01--12).
+
+@item %j
+The day of the year as a decimal number (001--366).
+
+@item %m
+The month as a decimal number (01--12).
+
+@item %M
+The minute as a decimal number (00--59).
+
+@item %p
+The locale's equivalent of the AM/PM designations associated
+with a 12-hour clock.
+
+@item %S
+The second as a decimal number (00--61). (Occasionally there are
+minutes in a year with one or two leap seconds, which is why the
+seconds can go from 0 all the way to 61.)
+
+@item %U
+The week number of the year (the first Sunday as the first day of week 1)
+as a decimal number (00--53).
+
+@item %w
+The weekday as a decimal number (0--6). Sunday is day 0.
+
+@item %W
+The week number of the year (the first Monday as the first day of week 1)
+as a decimal number (00--53).
+
+@item %x
+The locale's ``appropriate'' date representation.
+
+@item %X
+The locale's ``appropriate'' time representation.
+
+@item %y
+The year without century as a decimal number (00--99).
+
+@item %Y
+The year with century as a decimal number.
+
+@item %Z
+The time zone name or abbreviation, or no characters if
+no time zone is determinable.
+
+@item %%
+A literal @samp{%}.
+@end table
+
+@c The parenthetical remark here should really be a footnote, but
+@c it gave formatting problems at the FSF. So for now put it in
+@c parentheses.
+If a conversion specifier is not one of the above, the behavior is
+undefined. (This is because the @sc{ansi} standard for C leaves the
+behavior of the C version of @code{strftime} undefined, and @code{gawk}
+will use the system's version of @code{strftime} if it's there.
+Typically, the conversion specifier will either not appear in the
+returned string, or it will appear literally.)
+
+Informally, a @dfn{locale} is the geographic place in which a program
+is meant to run. For example, a common way to abbreviate the date
+September 4, 1991 in the United States would be ``9/4/91''.
+In many countries in Europe, however, it would be abbreviated ``4.9.91''.
+Thus, the @samp{%x} specification in a @code{"US"} locale might produce
+@samp{9/4/91}, while in a @code{"EUROPE"} locale, it might produce
+@samp{4.9.91}. The @sc{ansi} C standard defines a default @code{"C"}
+locale, which is an environment that is typical of what most C programmers
+are used to.
+
+A public-domain C version of @code{strftime} is shipped with @code{gawk}
+for systems that are not yet fully @sc{ansi}-compliant. If that version is
+used to compile @code{gawk} (@pxref{Installation, ,Installing @code{gawk}}),
+then the following additional format specifications are available:@refill
+
+@table @code
+@item %D
+Equivalent to specifying @samp{%m/%d/%y}.
+
+@item %e
+The day of the month, padded with a blank if it is only one digit.
+
+@item %h
+Equivalent to @samp{%b}, above.
+
+@item %n
+A newline character (ASCII LF).
+
+@item %r
+Equivalent to specifying @samp{%I:%M:%S %p}.
+
+@item %R
+Equivalent to specifying @samp{%H:%M}.
+
+@item %T
+Equivalent to specifying @samp{%H:%M:%S}.
+
+@item %t
+A TAB character.
+
+@item %k
+is replaced by the hour (24-hour clock) as a decimal number (0-23).
+Single digit numbers are padded with a blank.
+
+@item %l
+is replaced by the hour (12-hour clock) as a decimal number (1-12).
+Single digit numbers are padded with a blank.
+
+@item %C
+The century, as a number between 00 and 99.
+
+@item %u
+is replaced by the weekday as a decimal number
+[1 (Monday)--7].
+
+@item %V
+is replaced by the week number of the year (the first Monday as the first
+day of week 1) as a decimal number (01--53).
+The method for determining the week number is as specified by ISO 8601
+(to wit: if the week containing January 1 has four or more days in the
+new year, then it is week 1, otherwise it is week 53 of the previous year
+and the next week is week 1).@refill
+
+@item %Ec %EC %Ex %Ey %EY %Od %Oe %OH %OI
+@itemx %Om %OM %OS %Ou %OU %OV %Ow %OW %Oy
+These are ``alternate representations'' for the specifications
+that use only the second letter (@samp{%c}, @samp{%C}, and so on).
+They are recognized, but their normal representations are used.
+(These facilitate compliance with the @sc{posix} @code{date}
+utility.)@refill
+
+@item %v
+The date in VMS format (e.g. 20-JUN-1991).
+@end table
+
+Here are two examples that use @code{strftime}. The first is an
+@code{awk} version of the C @code{ctime} function. (This is a
+user defined function, which we have not discussed yet.
+@xref{User-defined, ,User-defined Functions}, for more information.)
+
+@smallexample
+# ctime.awk
+#
+# awk version of C ctime(3) function
+
+function ctime(ts, format)
+@{
+ format = "%a %b %e %H:%M:%S %Z %Y"
+ if (ts == 0)
+ ts = systime() # use current time as default
+ return strftime(format, ts)
+@}
+@end smallexample
+
+This next example is an @code{awk} implementation of the @sc{posix}
+@code{date} utility. Normally, the @code{date} utility prints the
+current date and time of day in a well known format. However, if you
+provide an argument to it that begins with a @samp{+}, @code{date}
+will copy non-format specifier characters to the standard output, and
+will interpret the current time according to the format specifiers in
+the string. For example:
+
+@smallexample
+date '+Today is %A, %B %d, %Y.'
+@end smallexample
+
+@noindent
+might print
+
+@smallexample
+Today is Thursday, July 11, 1991.
+@end smallexample
+
+Here is the @code{awk} version of the @code{date} utility.
+
+@smallexample
+#! /usr/bin/gawk -f
+#
+# date --- implement the P1003.2 Draft 11 'date' command
+#
+# Bug: does not recognize the -u argument.
+
+BEGIN \
+@{
+ format = "%a %b %e %H:%M:%S %Z %Y"
+ exitval = 0
+
+ if (ARGC > 2)
+ exitval = 1
+ else if (ARGC == 2) @{
+ format = ARGV[1]
+ if (format ~ /^\+/)
+ format = substr(format, 2) # remove leading +
+ @}
+ print strftime(format)
+ exit exitval
+@}
+@end smallexample
+
+@node User-defined, Built-in Variables, Built-in, Top
+@chapter User-defined Functions
+
+@cindex user-defined functions
+@cindex functions, user-defined
+Complicated @code{awk} programs can often be simplified by defining
+your own functions. User-defined functions can be called just like
+built-in ones (@pxref{Function Calls}), but it is up to you to define
+them---to tell @code{awk} what they should do.
+
+@menu
+* Definition Syntax:: How to write definitions and what they mean.
+* Function Example:: An example function definition and
+ what it does.
+* Function Caveats:: Things to watch out for.
+* Return Statement:: Specifying the value a function returns.
+@end menu
+
+@node Definition Syntax, Function Example, User-defined, User-defined
+@section Syntax of Function Definitions
+@cindex defining functions
+@cindex function definition
+
+Definitions of functions can appear anywhere between the rules of the
+@code{awk} program. Thus, the general form of an @code{awk} program is
+extended to include sequences of rules @emph{and} user-defined function
+definitions.
+
+The definition of a function named @var{name} looks like this:
+
+@example
+function @var{name} (@var{parameter-list}) @{
+ @var{body-of-function}
+@}
+@end example
+
+@noindent
+@var{name} is the name of the function to be defined. A valid function
+name is like a valid variable name: a sequence of letters, digits and
+underscores, not starting with a digit. Functions share the same pool
+of names as variables and arrays.
+
+@var{parameter-list} is a list of the function's arguments and local
+variable names, separated by commas. When the function is called,
+the argument names are used to hold the argument values given in
+the call. The local variables are initialized to the null string.
+
+The @var{body-of-function} consists of @code{awk} statements. It is the
+most important part of the definition, because it says what the function
+should actually @emph{do}. The argument names exist to give the body a
+way to talk about the arguments; local variables, to give the body
+places to keep temporary values.
+
+Argument names are not distinguished syntactically from local variable
+names; instead, the number of arguments supplied when the function is
+called determines how many argument variables there are. Thus, if three
+argument values are given, the first three names in @var{parameter-list}
+are arguments, and the rest are local variables.
+
+It follows that if the number of arguments is not the same in all calls
+to the function, some of the names in @var{parameter-list} may be
+arguments on some occasions and local variables on others. Another
+way to think of this is that omitted arguments default to the
+null string.
+
+Usually when you write a function you know how many names you intend to
+use for arguments and how many you intend to use as locals. By
+convention, you should write an extra space between the arguments and
+the locals, so other people can follow how your function is
+supposed to be used.
+
+During execution of the function body, the arguments and local variable
+values hide or @dfn{shadow} any variables of the same names used in the
+rest of the program. The shadowed variables are not accessible in the
+function definition, because there is no way to name them while their
+names have been taken away for the local variables. All other variables
+used in the @code{awk} program can be referenced or set normally in the
+function definition.
+
+The arguments and local variables last only as long as the function body
+is executing. Once the body finishes, the shadowed variables come back.
+
+The function body can contain expressions which call functions. They
+can even call this function, either directly or by way of another
+function. When this happens, we say the function is @dfn{recursive}.
+
+There is no need in @code{awk} to put the definition of a function
+before all uses of the function. This is because @code{awk} reads the
+entire program before starting to execute any of it.
+
+In many @code{awk} implementations, the keyword @code{function} may be
+abbreviated @code{func}. However, @sc{posix} only specifies the use of
+the keyword @code{function}. This actually has some practical implications.
+If @code{gawk} is in @sc{posix}-compatibility mode
+(@pxref{Command Line, ,Invoking @code{awk}}), then the following
+statement will @emph{not} define a function:@refill
+
+@example
+func foo() @{ a = sqrt($1) ; print a @}
+@end example
+
+@noindent
+Instead it defines a rule that, for each record, concatenates the value
+of the variable @samp{func} with the return value of the function @samp{foo},
+and based on the truth value of the result, executes the corresponding action.
+This is probably not what was desired. (@code{awk} accepts this input as
+syntactically valid, since functions may be used before they are defined
+in @code{awk} programs.)
+
+@node Function Example, Function Caveats, Definition Syntax, User-defined
+@section Function Definition Example
+
+Here is an example of a user-defined function, called @code{myprint}, that
+takes a number and prints it in a specific format.
+
+@example
+function myprint(num)
+@{
+ printf "%6.3g\n", num
+@}
+@end example
+
+@noindent
+To illustrate, here is an @code{awk} rule which uses our @code{myprint}
+function:
+
+@example
+$3 > 0 @{ myprint($3) @}
+@end example
+
+@noindent
+This program prints, in our special format, all the third fields that
+contain a positive number in our input. Therefore, when given:
+
+@example
+ 1.2 3.4 5.6 7.8
+ 9.10 11.12 -13.14 15.16
+17.18 19.20 21.22 23.24
+@end example
+
+@noindent
+this program, using our function to format the results, prints:
+
+@example
+ 5.6
+ 21.2
+@end example
+
+Here is a rather contrived example of a recursive function. It prints a
+string backwards:
+
+@example
+function rev (str, len) @{
+ if (len == 0) @{
+ printf "\n"
+ return
+ @}
+ printf "%c", substr(str, len, 1)
+ rev(str, len - 1)
+@}
+@end example
+
+@node Function Caveats, Return Statement, Function Example, User-defined
+@section Calling User-defined Functions
+
+@dfn{Calling a function} means causing the function to run and do its job.
+A function call is an expression, and its value is the value returned by
+the function.
+
+A function call consists of the function name followed by the arguments
+in parentheses. What you write in the call for the arguments are
+@code{awk} expressions; each time the call is executed, these
+expressions are evaluated, and the values are the actual arguments. For
+example, here is a call to @code{foo} with three arguments (the first
+being a string concatenation):
+
+@example
+foo(x y, "lose", 4 * z)
+@end example
+
+@quotation
+@strong{Caution:} whitespace characters (spaces and tabs) are not allowed
+between the function name and the open-parenthesis of the argument list.
+If you write whitespace by mistake, @code{awk} might think that you mean
+to concatenate a variable with an expression in parentheses. However, it
+notices that you used a function name and not a variable name, and reports
+an error.
+@end quotation
+
+@cindex call by value
+When a function is called, it is given a @emph{copy} of the values of
+its arguments. This is called @dfn{call by value}. The caller may use
+a variable as the expression for the argument, but the called function
+does not know this: it only knows what value the argument had. For
+example, if you write this code:
+
+@example
+foo = "bar"
+z = myfunc(foo)
+@end example
+
+@noindent
+then you should not think of the argument to @code{myfunc} as being
+``the variable @code{foo}.'' Instead, think of the argument as the
+string value, @code{"bar"}.
+
+If the function @code{myfunc} alters the values of its local variables,
+this has no effect on any other variables. In particular, if @code{myfunc}
+does this:
+
+@example
+function myfunc (win) @{
+ print win
+ win = "zzz"
+ print win
+@}
+@end example
+
+@noindent
+to change its first argument variable @code{win}, this @emph{does not}
+change the value of @code{foo} in the caller. The role of @code{foo} in
+calling @code{myfunc} ended when its value, @code{"bar"}, was computed.
+If @code{win} also exists outside of @code{myfunc}, the function body
+cannot alter this outer value, because it is shadowed during the
+execution of @code{myfunc} and cannot be seen or changed from there.
+
+@cindex call by reference
+However, when arrays are the parameters to functions, they are @emph{not}
+copied. Instead, the array itself is made available for direct manipulation
+by the function. This is usually called @dfn{call by reference}.
+Changes made to an array parameter inside the body of a function @emph{are}
+visible outside that function.
+@ifinfo
+This can be @strong{very} dangerous if you do not watch what you are
+doing. For example:@refill
+@end ifinfo
+@iftex
+@emph{This can be very dangerous if you do not watch what you are
+doing.} For example:@refill
+@end iftex
+
+@example
+function changeit (array, ind, nvalue) @{
+ array[ind] = nvalue
+@}
+
+BEGIN @{
+ a[1] = 1 ; a[2] = 2 ; a[3] = 3
+ changeit(a, 2, "two")
+ printf "a[1] = %s, a[2] = %s, a[3] = %s\n", a[1], a[2], a[3]
+ @}
+@end example
+
+@noindent
+prints @samp{a[1] = 1, a[2] = two, a[3] = 3}, because calling
+@code{changeit} stores @code{"two"} in the second element of @code{a}.
+
+@node Return Statement, , Function Caveats, User-defined
+@section The @code{return} Statement
+@cindex @code{return} statement
+
+The body of a user-defined function can contain a @code{return} statement.
+This statement returns control to the rest of the @code{awk} program. It
+can also be used to return a value for use in the rest of the @code{awk}
+program. It looks like this:@refill
+
+@example
+return @var{expression}
+@end example
+
+The @var{expression} part is optional. If it is omitted, then the returned
+value is undefined and, therefore, unpredictable.
+
+A @code{return} statement with no value expression is assumed at the end of
+every function definition. So if control reaches the end of the function
+body, then the function returns an unpredictable value. @code{awk}
+will not warn you if you use the return value of such a function; you will
+simply get unpredictable or unexpected results.
+
+Here is an example of a user-defined function that returns a value
+for the largest number among the elements of an array:@refill
+
+@example
+@group
+function maxelt (vec, i, ret) @{
+ for (i in vec) @{
+ if (ret == "" || vec[i] > ret)
+ ret = vec[i]
+ @}
+ return ret
+@}
+@end group
+@end example
+
+@noindent
+You call @code{maxelt} with one argument, which is an array name. The local
+variables @code{i} and @code{ret} are not intended to be arguments;
+while there is nothing to stop you from passing two or three arguments
+to @code{maxelt}, the results would be strange. The extra space before
+@code{i} in the function parameter list is to indicate that @code{i} and
+@code{ret} are not supposed to be arguments. This is a convention which
+you should follow when you define functions.
+
+Here is a program that uses our @code{maxelt} function. It loads an
+array, calls @code{maxelt}, and then reports the maximum number in that
+array:@refill
+
+@example
+@group
+awk '
+function maxelt (vec, i, ret) @{
+ for (i in vec) @{
+ if (ret == "" || vec[i] > ret)
+ ret = vec[i]
+ @}
+ return ret
+@}
+@end group
+
+@group
+# Load all fields of each record into nums.
+@{
+ for(i = 1; i <= NF; i++)
+ nums[NR, i] = $i
+@}
+
+END @{
+ print maxelt(nums)
+@}'
+@end group
+@end example
+
+Given the following input:
+
+@example
+@group
+ 1 5 23 8 16
+44 3 5 2 8 26
+256 291 1396 2962 100
+-6 467 998 1101
+99385 11 0 225
+@end group
+@end example
+
+@noindent
+our program tells us (predictably) that:
+
+@example
+99385
+@end example
+
+@noindent
+is the largest number in our array.
+
+@node Built-in Variables, Command Line, User-defined, Top
+@chapter Built-in Variables
+@cindex built-in variables
+
+Most @code{awk} variables are available for you to use for your own
+purposes; they never change except when your program assigns values to
+them, and never affect anything except when your program examines them.
+
+A few variables have special built-in meanings. Some of them @code{awk}
+examines automatically, so that they enable you to tell @code{awk} how
+to do certain things. Others are set automatically by @code{awk}, so
+that they carry information from the internal workings of @code{awk} to
+your program.
+
+This chapter documents all the built-in variables of @code{gawk}. Most
+of them are also documented in the chapters where their areas of
+activity are described.
+
+@menu
+* User-modified:: Built-in variables that you change
+ to control @code{awk}.
+* Auto-set:: Built-in variables where @code{awk}
+ gives you information.
+@end menu
+
+@node User-modified, Auto-set, Built-in Variables, Built-in Variables
+@section Built-in Variables that Control @code{awk}
+@cindex built-in variables, user modifiable
+
+This is a list of the variables which you can change to control how
+@code{awk} does certain things.
+
+@table @code
+@iftex
+@vindex CONVFMT
+@end iftex
+@item CONVFMT
+This string is used by @code{awk} to control conversion of numbers to
+strings (@pxref{Conversion, ,Conversion of Strings and Numbers}).
+It works by being passed, in effect, as the first argument to the
+@code{sprintf} function. Its default value is @code{"%.6g"}.
+@code{CONVFMT} was introduced by the @sc{posix} standard.@refill
+
+@iftex
+@vindex FIELDWIDTHS
+@end iftex
+@item FIELDWIDTHS
+This is a space separated list of columns that tells @code{gawk}
+how to manage input with fixed, columnar boundaries. It is an
+experimental feature that is still evolving. Assigning to @code{FIELDWIDTHS}
+overrides the use of @code{FS} for field splitting.
+@xref{Constant Size, ,Reading Fixed-width Data}, for more information.@refill
+
+If @code{gawk} is in compatibility mode
+(@pxref{Command Line, ,Invoking @code{awk}}), then @code{FIELDWIDTHS}
+has no special meaning, and field splitting operations are done based
+exclusively on the value of @code{FS}.@refill
+
+@iftex
+@vindex FS
+@end iftex
+@item FS
+@code{FS} is the input field separator
+(@pxref{Field Separators, ,Specifying how Fields are Separated}).
+The value is a single-character string or a multi-character regular
+expression that matches the separations between fields in an input
+record.@refill
+
+The default value is @w{@code{" "}}, a string consisting of a single
+space. As a special exception, this value actually means that any
+sequence of spaces and tabs is a single separator. It also causes
+spaces and tabs at the beginning or end of a line to be ignored.
+
+You can set the value of @code{FS} on the command line using the
+@samp{-F} option:
+
+@example
+awk -F, '@var{program}' @var{input-files}
+@end example
+
+If @code{gawk} is using @code{FIELDWIDTHS} for field-splitting,
+assigning a value to @code{FS} will cause @code{gawk} to return to
+the normal, regexp-based, field splitting.
+
+@item IGNORECASE
+@iftex
+@vindex IGNORECASE
+@end iftex
+If @code{IGNORECASE} is nonzero, then @emph{all} regular expression
+matching is done in a case-independent fashion. In particular, regexp
+matching with @samp{~} and @samp{!~}, and the @code{gsub} @code{index},
+@code{match}, @code{split} and @code{sub} functions all ignore case when
+doing their particular regexp operations. @strong{Note:} since field
+splitting with the value of the @code{FS} variable is also a regular
+expression operation, that too is done with case ignored.
+@xref{Case-sensitivity, ,Case-sensitivity in Matching}.
+
+If @code{gawk} is in compatibility mode
+(@pxref{Command Line, ,Invoking @code{awk}}), then @code{IGNORECASE} has
+no special meaning, and regexp operations are always case-sensitive.@refill
+
+@item OFMT
+@iftex
+@vindex OFMT
+@end iftex
+This string is used by @code{awk} to control conversion of numbers to
+strings (@pxref{Conversion, ,Conversion of Strings and Numbers}) for
+printing with the @code{print} statement.
+It works by being passed, in effect, as the first argument to the
+@code{sprintf} function. Its default value is @code{"%.6g"}.
+Earlier versions of @code{awk} also used @code{OFMT} to specify the
+format for converting numbers to strings in general expressions; this
+has been taken over by @code{CONVFMT}.@refill
+
+@item OFS
+@iftex
+@vindex OFS
+@end iftex
+This is the output field separator (@pxref{Output Separators}). It is
+output between the fields output by a @code{print} statement. Its
+default value is @w{@code{" "}}, a string consisting of a single space.
+
+@item ORS
+@iftex
+@vindex ORS
+@end iftex
+This is the output record separator. It is output at the end of every
+@code{print} statement. Its default value is a string containing a
+single newline character, which could be written as @code{"\n"}.
+(@xref{Output Separators}.)@refill
+
+@item RS
+@iftex
+@vindex RS
+@end iftex
+This is @code{awk}'s input record separator. Its default value is a string
+containing a single newline character, which means that an input record
+consists of a single line of text.
+(@xref{Records, ,How Input is Split into Records}.)@refill
+
+@item SUBSEP
+@iftex
+@vindex SUBSEP
+@end iftex
+@code{SUBSEP} is the subscript separator. It has the default value of
+@code{"\034"}, and is used to separate the parts of the name of a
+multi-dimensional array. Thus, if you access @code{foo[12,3]}, it
+really accesses @code{foo["12\0343"]}
+(@pxref{Multi-dimensional, ,Multi-dimensional Arrays}).@refill
+@end table
+
+@node Auto-set, , User-modified, Built-in Variables
+@section Built-in Variables that Convey Information
+
+This is a list of the variables that are set automatically by @code{awk}
+on certain occasions so as to provide information to your program.
+
+@table @code
+@item ARGC
+@itemx ARGV
+@iftex
+@vindex ARGC
+@vindex ARGV
+@end iftex
+The command-line arguments available to @code{awk} programs are stored in
+an array called @code{ARGV}. @code{ARGC} is the number of command-line
+arguments present. @xref{Command Line, ,Invoking @code{awk}}.
+@code{ARGV} is indexed from zero to @w{@code{ARGC - 1}}. For example:@refill
+
+@example
+awk 'BEGIN @{
+ for (i = 0; i < ARGC; i++)
+ print ARGV[i]
+ @}' inventory-shipped BBS-list
+@end example
+
+@noindent
+In this example, @code{ARGV[0]} contains @code{"awk"}, @code{ARGV[1]}
+contains @code{"inventory-shipped"}, and @code{ARGV[2]} contains
+@code{"BBS-list"}. The value of @code{ARGC} is 3, one more than the
+index of the last element in @code{ARGV} since the elements are numbered
+from zero.@refill
+
+The names @code{ARGC} and @code{ARGV}, as well the convention of indexing
+the array from 0 to @w{@code{ARGC - 1}}, are derived from the C language's
+method of accessing command line arguments.@refill
+
+Notice that the @code{awk} program is not entered in @code{ARGV}. The
+other special command line options, with their arguments, are also not
+entered. But variable assignments on the command line @emph{are}
+treated as arguments, and do show up in the @code{ARGV} array.
+
+Your program can alter @code{ARGC} and the elements of @code{ARGV}.
+Each time @code{awk} reaches the end of an input file, it uses the next
+element of @code{ARGV} as the name of the next input file. By storing a
+different string there, your program can change which files are read.
+You can use @code{"-"} to represent the standard input. By storing
+additional elements and incrementing @code{ARGC} you can cause
+additional files to be read.
+
+If you decrease the value of @code{ARGC}, that eliminates input files
+from the end of the list. By recording the old value of @code{ARGC}
+elsewhere, your program can treat the eliminated arguments as
+something other than file names.
+
+To eliminate a file from the middle of the list, store the null string
+(@code{""}) into @code{ARGV} in place of the file's name. As a
+special feature, @code{awk} ignores file names that have been
+replaced with the null string.
+
+@ignore
+see getopt.awk in the examples...
+@end ignore
+
+@item ARGIND
+@vindex ARGIND
+The index in @code{ARGV} of the current file being processed.
+Every time @code{gawk} opens a new data file for processing, it sets
+@code{ARGIND} to the index in @code{ARGV} of the file name. Thus, the
+condition @samp{FILENAME == ARGV[ARGIND]} is always true.
+
+This variable is useful in file processing; it allows you to tell how far
+along you are in the list of data files, and to distinguish between
+multiple successive instances of the same filename on the command line.
+
+While you can change the value of @code{ARGIND} within your @code{awk}
+program, @code{gawk} will automatically set it to a new value when the
+next file is opened.
+
+This variable is a @code{gawk} extension; in other @code{awk} implementations
+it is not special.
+
+@item ENVIRON
+@vindex ENVIRON
+This is an array that contains the values of the environment. The array
+indices are the environment variable names; the values are the values of
+the particular environment variables. For example,
+@code{ENVIRON["HOME"]} might be @file{/u/close}. Changing this array
+does not affect the environment passed on to any programs that
+@code{awk} may spawn via redirection or the @code{system} function.
+(In a future version of @code{gawk}, it may do so.)
+
+Some operating systems may not have environment variables.
+On such systems, the array @code{ENVIRON} is empty.
+
+@item ERRNO
+@iftex
+@vindex ERRNO
+@end iftex
+If a system error occurs either doing a redirection for @code{getline},
+during a read for @code{getline}, or during a @code{close} operation,
+then @code{ERRNO} will contain a string describing the error.
+
+This variable is a @code{gawk} extension; in other @code{awk} implementations
+it is not special.
+
+@item FILENAME
+@iftex
+@vindex FILENAME
+@end iftex
+This is the name of the file that @code{awk} is currently reading.
+If @code{awk} is reading from the standard input (in other words,
+there are no files listed on the command line),
+@code{FILENAME} is set to @code{"-"}.
+@code{FILENAME} is changed each time a new file is read
+(@pxref{Reading Files, ,Reading Input Files}).@refill
+
+@item FNR
+@iftex
+@vindex FNR
+@end iftex
+@code{FNR} is the current record number in the current file. @code{FNR} is
+incremented each time a new record is read
+(@pxref{Getline, ,Explicit Input with @code{getline}}). It is reinitialized
+to 0 each time a new input file is started.@refill
+
+@item NF
+@iftex
+@vindex NF
+@end iftex
+@code{NF} is the number of fields in the current input record.
+@code{NF} is set each time a new record is read, when a new field is
+created, or when @code{$0} changes (@pxref{Fields, ,Examining Fields}).@refill
+
+@item NR
+@iftex
+@vindex NR
+@end iftex
+This is the number of input records @code{awk} has processed since
+the beginning of the program's execution.
+(@pxref{Records, ,How Input is Split into Records}).
+@code{NR} is set each time a new record is read.@refill
+
+@item RLENGTH
+@iftex
+@vindex RLENGTH
+@end iftex
+@code{RLENGTH} is the length of the substring matched by the
+@code{match} function
+(@pxref{String Functions, ,Built-in Functions for String Manipulation}).
+@code{RLENGTH} is set by invoking the @code{match} function. Its value
+is the length of the matched string, or @minus{}1 if no match was found.@refill
+
+@item RSTART
+@iftex
+@vindex RSTART
+@end iftex
+@code{RSTART} is the start-index in characters of the substring matched by the
+@code{match} function
+(@pxref{String Functions, ,Built-in Functions for String Manipulation}).
+@code{RSTART} is set by invoking the @code{match} function. Its value
+is the position of the string where the matched substring starts, or 0
+if no match was found.@refill
+@end table
+
+@node Command Line, Language History, Built-in Variables, Top
+@c node-name, next, previous, up
+@chapter Invoking @code{awk}
+@cindex command line
+@cindex invocation of @code{gawk}
+@cindex arguments, command line
+@cindex options, command line
+@cindex long options
+@cindex options, long
+
+There are two ways to run @code{awk}: with an explicit program, or with
+one or more program files. Here are templates for both of them; items
+enclosed in @samp{@r{[}@dots{}@r{]}} in these templates are optional.
+
+Besides traditional one-letter @sc{posix}-style options, @code{gawk} also
+supports GNU long named options.
+
+@example
+awk @r{[@var{POSIX or GNU style options}]} -f progfile @r{[@code{--}]} @var{file} @dots{}
+awk @r{[@var{POSIX or GNU style options}]} @r{[@code{--}]} '@var{program}' @var{file} @dots{}
+@end example
+
+@menu
+* Options:: Command line options and their meanings.
+* Other Arguments:: Input file names and variable assignments.
+* AWKPATH Variable:: Searching directories for @code{awk} programs.
+* Obsolete:: Obsolete Options and/or features.
+* Undocumented:: Undocumented Options and Features.
+@end menu
+
+@node Options, Other Arguments, Command Line, Command Line
+@section Command Line Options
+
+Options begin with a minus sign, and consist of a single character.
+GNU style long named options consist of two minus signs and
+a keyword that can be abbreviated if the abbreviation allows the option
+to be uniquely identified. If the option takes an argument, then the
+keyword is immediately followed by an equals sign (@samp{=}) and the
+argument's value. For brevity, the discussion below only refers to the
+traditional short options; however the long and short options are
+interchangeable in all contexts.
+
+Each long named option for @code{gawk} has a corresponding
+@sc{posix}-style option. The options and their meanings are as follows:
+
+@table @code
+@item -F @var{fs}
+@itemx --field-separator=@var{fs}
+@iftex
+@cindex @code{-F} option
+@end iftex
+@cindex @code{--field-separator} option
+Sets the @code{FS} variable to @var{fs}
+(@pxref{Field Separators, ,Specifying how Fields are Separated}).@refill
+
+@item -f @var{source-file}
+@itemx --file=@var{source-file}
+@iftex
+@cindex @code{-f} option
+@end iftex
+@cindex @code{--file} option
+Indicates that the @code{awk} program is to be found in @var{source-file}
+instead of in the first non-option argument.
+
+@item -v @var{var}=@var{val}
+@itemx --assign=@var{var}=@var{val}
+@cindex @samp{-v} option
+@cindex @code{--assign} option
+Sets the variable @var{var} to the value @var{val} @emph{before}
+execution of the program begins. Such variable values are available
+inside the @code{BEGIN} rule (see below for a fuller explanation).
+
+The @samp{-v} option can only set one variable, but you can use
+it more than once, setting another variable each time, like this:
+@samp{@w{-v foo=1} @w{-v bar=2}}.
+
+@item -W @var{gawk-opt}
+@cindex @samp{-W} option
+Following the @sc{posix} standard, options that are implementation
+specific are supplied as arguments to the @samp{-W} option. With @code{gawk},
+these arguments may be separated by commas, or quoted and separated by
+whitespace. Case is ignored when processing these options. These options
+also have corresponding GNU style long named options. The following
+@code{gawk}-specific options are available:
+
+@table @code
+@item -W compat
+@itemx --compat
+@cindex @code{--compat} option
+Specifies @dfn{compatibility mode}, in which the GNU extensions in
+@code{gawk} are disabled, so that @code{gawk} behaves just like Unix
+@code{awk}.
+@xref{POSIX/GNU, ,Extensions in @code{gawk} not in POSIX @code{awk}},
+which summarizes the extensions. Also see
+@ref{Compatibility Mode, ,Downward Compatibility and Debugging}.@refill
+
+@item -W copyleft
+@itemx -W copyright
+@itemx --copyleft
+@itemx --copyright
+@cindex @code{--copyleft} option
+@cindex @code{--copyright} option
+Print the short version of the General Public License.
+This option may disappear in a future version of @code{gawk}.
+
+@item -W help
+@itemx -W usage
+@itemx --help
+@itemx --usage
+@cindex @code{--help} option
+@cindex @code{--usage} option
+Print a ``usage'' message summarizing the short and long style options
+that @code{gawk} accepts, and then exit.
+
+@item -W lint
+@itemx --lint
+@cindex @code{--lint} option
+Provide warnings about constructs that are dubious or non-portable to
+other @code{awk} implementations.
+Some warnings are issued when @code{gawk} first reads your program. Others
+are issued at run-time, as your program executes.
+
+@item -W posix
+@itemx --posix
+@cindex @code{--posix} option
+Operate in strict @sc{posix} mode. This disables all @code{gawk}
+extensions (just like @code{-W compat}), and adds the following additional
+restrictions:
+
+@itemize @bullet{}
+@item
+@code{\x} escape sequences are not recognized
+(@pxref{Constants, ,Constant Expressions}).@refill
+
+@item
+The synonym @code{func} for the keyword @code{function} is not
+recognized (@pxref{Definition Syntax, ,Syntax of Function Definitions}).
+
+@item
+The operators @samp{**} and @samp{**=} cannot be used in
+place of @samp{^} and @samp{^=} (@pxref{Arithmetic Ops, ,Arithmetic Operators},
+and also @pxref{Assignment Ops, ,Assignment Expressions}).@refill
+
+@item
+Specifying @samp{-Ft} on the command line does not set the value
+of @code{FS} to be a single tab character
+(@pxref{Field Separators, ,Specifying how Fields are Separated}).@refill
+@end itemize
+
+Although you can supply both @samp{-W compat} and @samp{-W posix} on the
+command line, @samp{-W posix} will take precedence.
+
+@item -W source=@var{program-text}
+@itemx --source=@var{program-text}
+@cindex @code{--source} option
+Program source code is taken from the @var{program-text}. This option
+allows you to mix @code{awk} source code in files with program source
+code that you would enter on the command line. This is particularly useful
+when you have library functions that you wish to use from your command line
+programs (@pxref{AWKPATH Variable, ,The @code{AWKPATH} Environment Variable}).
+
+@item -W version
+@itemx --version
+@cindex @code{--version} option
+Prints version information for this particular copy of @code{gawk}.
+This is so you can determine if your copy of @code{gawk} is up to date
+with respect to whatever the Free Software Foundation is currently
+distributing. This option may disappear in a future version of @code{gawk}.
+@end table
+
+@item --
+Signals the end of the command line options. The following arguments
+are not treated as options even if they begin with @samp{-}. This
+interpretation of @samp{--} follows the @sc{posix} argument parsing
+conventions.
+
+This is useful if you have file names that start with @samp{-},
+or in shell scripts, if you have file names that will be specified
+by the user which could start with @samp{-}.
+@end table
+
+Any other options are flagged as invalid with a warning message, but
+are otherwise ignored.
+
+In compatibility mode, as a special case, if the value of @var{fs} supplied
+to the @samp{-F} option is @samp{t}, then @code{FS} is set to the tab
+character (@code{"\t"}). This is only true for @samp{-W compat}, and not
+for @samp{-W posix}
+(@pxref{Field Separators, ,Specifying how Fields are Separated}).@refill
+
+If the @samp{-f} option is @emph{not} used, then the first non-option
+command line argument is expected to be the program text.
+
+The @samp{-f} option may be used more than once on the command line.
+If it is, @code{awk} reads its program source from all of the named files, as
+if they had been concatenated together into one big file. This is
+useful for creating libraries of @code{awk} functions. Useful functions
+can be written once, and then retrieved from a standard place, instead
+of having to be included into each individual program. You can still
+type in a program at the terminal and use library functions, by specifying
+@samp{-f /dev/tty}. @code{awk} will read a file from the terminal
+to use as part of the @code{awk} program. After typing your program,
+type @kbd{Control-d} (the end-of-file character) to terminate it.
+(You may also use @samp{-f -} to read program source from the standard
+input, but then you will not be able to also use the standard input as a
+source of data.)
+
+Because it is clumsy using the standard @code{awk} mechanisms to mix source
+file and command line @code{awk} programs, @code{gawk} provides the
+@samp{--source} option. This does not require you to pre-empt the standard
+input for your source code, and allows you to easily mix command line
+and library source code
+(@pxref{AWKPATH Variable, ,The @code{AWKPATH} Environment Variable}).
+
+If no @samp{-f} or @samp{--source} option is specified, then @code{gawk}
+will use the first non-option command line argument as the text of the
+program source code.
+
+@node Other Arguments, AWKPATH Variable, Options, Command Line
+@section Other Command Line Arguments
+
+Any additional arguments on the command line are normally treated as
+input files to be processed in the order specified. However, an
+argument that has the form @code{@var{var}=@var{value}}, means to assign
+the value @var{value} to the variable @var{var}---it does not specify a
+file at all.
+
+@vindex ARGV
+All these arguments are made available to your @code{awk} program in the
+@code{ARGV} array (@pxref{Built-in Variables}). Command line options
+and the program text (if present) are omitted from the @code{ARGV}
+array. All other arguments, including variable assignments, are
+included.
+
+The distinction between file name arguments and variable-assignment
+arguments is made when @code{awk} is about to open the next input file.
+At that point in execution, it checks the ``file name'' to see whether
+it is really a variable assignment; if so, @code{awk} sets the variable
+instead of reading a file.
+
+Therefore, the variables actually receive the specified values after all
+previously specified files have been read. In particular, the values of
+variables assigned in this fashion are @emph{not} available inside a
+@code{BEGIN} rule
+(@pxref{BEGIN/END, ,@code{BEGIN} and @code{END} Special Patterns}),
+since such rules are run before @code{awk} begins scanning the argument list.
+The values given on the command line are processed for escape sequences
+(@pxref{Constants, ,Constant Expressions}).@refill
+
+In some earlier implementations of @code{awk}, when a variable assignment
+occurred before any file names, the assignment would happen @emph{before}
+the @code{BEGIN} rule was executed. Some applications came to depend
+upon this ``feature.'' When @code{awk} was changed to be more consistent,
+the @samp{-v} option was added to accommodate applications that depended
+upon this old behavior.
+
+The variable assignment feature is most useful for assigning to variables
+such as @code{RS}, @code{OFS}, and @code{ORS}, which control input and
+output formats, before scanning the data files. It is also useful for
+controlling state if multiple passes are needed over a data file. For
+example:@refill
+
+@cindex multiple passes over data
+@cindex passes, multiple
+@smallexample
+awk 'pass == 1 @{ @var{pass 1 stuff} @}
+ pass == 2 @{ @var{pass 2 stuff} @}' pass=1 datafile pass=2 datafile
+@end smallexample
+
+Given the variable assignment feature, the @samp{-F} option is not
+strictly necessary. It remains for historical compatibility.
+
+@node AWKPATH Variable, Obsolete, Other Arguments, Command Line
+@section The @code{AWKPATH} Environment Variable
+@cindex @code{AWKPATH} environment variable
+@cindex search path
+@cindex directory search
+@cindex path, search
+@iftex
+@cindex differences between @code{gawk} and @code{awk}
+@end iftex
+
+The previous section described how @code{awk} program files can be named
+on the command line with the @samp{-f} option. In some @code{awk}
+implementations, you must supply a precise path name for each program
+file, unless the file is in the current directory.
+
+But in @code{gawk}, if the file name supplied in the @samp{-f} option
+does not contain a @samp{/}, then @code{gawk} searches a list of
+directories (called the @dfn{search path}), one by one, looking for a
+file with the specified name.
+
+The search path is actually a string consisting of directory names
+separated by colons. @code{gawk} gets its search path from the
+@code{AWKPATH} environment variable. If that variable does not exist,
+@code{gawk} uses the default path, which is
+@samp{.:/usr/lib/awk:/usr/local/lib/awk}. (Programs written by
+system administrators should use an @code{AWKPATH} variable that
+does not include the current directory, @samp{.}.)@refill
+
+The search path feature is particularly useful for building up libraries
+of useful @code{awk} functions. The library files can be placed in a
+standard directory that is in the default path, and then specified on
+the command line with a short file name. Otherwise, the full file name
+would have to be typed for each file.
+
+By combining the @samp{--source} and @samp{-f} options, your command line
+@code{awk} programs can use facilities in @code{awk} library files.
+
+Path searching is not done if @code{gawk} is in compatibility mode.
+This is true for both @samp{-W compat} and @samp{-W posix}.
+@xref{Options, ,Command Line Options}.
+
+@strong{Note:} if you want files in the current directory to be found,
+you must include the current directory in the path, either by writing
+@file{.} as an entry in the path, or by writing a null entry in the
+path. (A null entry is indicated by starting or ending the path with a
+colon, or by placing two colons next to each other (@samp{::}).) If the
+current directory is not included in the path, then files cannot be
+found in the current directory. This path search mechanism is identical
+to the shell's.
+@c someday, @cite{The Bourne Again Shell}....
+
+@node Obsolete, Undocumented, AWKPATH Variable, Command Line
+@section Obsolete Options and/or Features
+
+@cindex deprecated options
+@cindex obsolete options
+@cindex deprecated features
+@cindex obsolete features
+This section describes features and/or command line options from the
+previous release of @code{gawk} that are either not available in the
+current version, or that are still supported but deprecated (meaning that
+they will @emph{not} be in the next release).
+
+@c update this section for each release!
+
+For version 2.15 of @code{gawk}, the following command line options
+from version 2.11.1 are no longer recognized.
+
+@table @samp
+@ignore
+@item -nostalgia
+Use @samp{-W nostalgia} instead.
+@end ignore
+
+@item -c
+Use @samp{-W compat} instead.
+
+@item -V
+Use @samp{-W version} instead.
+
+@item -C
+Use @samp{-W copyright} instead.
+
+@item -a
+@itemx -e
+These options produce an ``unrecognized option'' error message but have
+no effect on the execution of @code{gawk}. The @sc{posix} standard now
+specifies traditional @code{awk} regular expressions for the @code{awk} utility.
+@end table
+
+The public-domain version of @code{strftime} that is distributed with
+@code{gawk} changed for the 2.14 release. The @samp{%V} conversion specifier
+that used to generate the date in VMS format was changed to @samp{%v}.
+This is because the @sc{posix} standard for the @code{date} utility now
+specifies a @samp{%V} conversion specifier.
+@xref{Time Functions, ,Functions for Dealing with Time Stamps}, for details.
+
+@node Undocumented, , Obsolete, Command Line
+@section Undocumented Options and Features
+
+This section intentionally left blank.
+
+@c Read The Source, Luke!
+
+@ignore
+@c If these came out in the Info file or TeX manual, then they wouldn't
+@c be undocumented, would they?
+
+@code{gawk} has one undocumented option:
+
+@table @samp
+@item -W nostalgia
+Print the message @code{"awk: bailing out near line 1"} and dump core.
+This option was inspired by the common behavior of very early versions of
+Unix @code{awk}, and by a t--shirt.
+@end table
+
+Early versions of @code{awk} used to not require any separator (either
+a newline or @samp{;}) between the rules in @code{awk} programs. Thus,
+it was common to see one-line programs like:
+
+@example
+awk '@{ sum += $1 @} END @{ print sum @}'
+@end example
+
+@code{gawk} actually supports this, but it is purposely undocumented
+since it is considered bad style. The correct way to write such a program
+is either
+
+@example
+awk '@{ sum += $1 @} ; END @{ print sum @}'
+@end example
+
+@noindent
+or
+
+@example
+awk '@{ sum += $1 @}
+ END @{ print sum @}' data
+@end example
+
+@noindent
+@xref{Statements/Lines, ,@code{awk} Statements versus Lines}, for a fuller
+explanation.@refill
+
+As an accident of the implementation of the original Unix @code{awk}, if
+a built-in function used @code{$0} as its default argument, it was possible
+to call that function without the parentheses. In particular, it was
+common practice to use the @code{length} function in this fashion.
+For example, the pipeline:
+
+@example
+echo abcdef | awk '@{ print length @}'
+@end example
+
+@noindent
+would print @samp{6}.
+
+For backwards compatibility with old programs, @code{gawk} supports
+this usage, but only for the @code{length} function. New programs should
+@emph{not} call the @code{length} function this way. In particular,
+this usage will not be portable to other @sc{posix} compliant versions
+of @code{awk}. It is also poor style.
+
+@end ignore
+
+@node Language History, Installation, Command Line, Top
+@chapter The Evolution of the @code{awk} Language
+
+This manual describes the GNU implementation of @code{awk}, which is patterned
+after the @sc{posix} specification. Many @code{awk} users are only familiar
+with the original @code{awk} implementation in Version 7 Unix, which is also
+the basis for the version in Berkeley Unix (through 4.3--Reno). This chapter
+briefly describes the evolution of the @code{awk} language.
+
+@menu
+* V7/S5R3.1:: The major changes between V7 and
+ System V Release 3.1.
+* S5R4:: Minor changes between System V
+ Releases 3.1 and 4.
+* POSIX:: New features from the @sc{posix} standard.
+* POSIX/GNU:: The extensions in @code{gawk}
+ not in @sc{posix} @code{awk}.
+@end menu
+
+@node V7/S5R3.1, S5R4, Language History, Language History
+@section Major Changes between V7 and S5R3.1
+
+The @code{awk} language evolved considerably between the release of
+Version 7 Unix (1978) and the new version first made widely available in
+System V Release 3.1 (1987). This section summarizes the changes, with
+cross-references to further details.
+
+@itemize @bullet
+@item
+The requirement for @samp{;} to separate rules on a line
+(@pxref{Statements/Lines, ,@code{awk} Statements versus Lines}).
+
+@item
+User-defined functions, and the @code{return} statement
+(@pxref{User-defined, ,User-defined Functions}).
+
+@item
+The @code{delete} statement (@pxref{Delete, ,The @code{delete} Statement}).
+
+@item
+The @code{do}-@code{while} statement
+(@pxref{Do Statement, ,The @code{do}-@code{while} Statement}).@refill
+
+@item
+The built-in functions @code{atan2}, @code{cos}, @code{sin}, @code{rand} and
+@code{srand} (@pxref{Numeric Functions, ,Numeric Built-in Functions}).
+
+@item
+The built-in functions @code{gsub}, @code{sub}, and @code{match}
+(@pxref{String Functions, ,Built-in Functions for String Manipulation}).
+
+@item
+The built-in functions @code{close}, which closes an open file, and
+@code{system}, which allows the user to execute operating system
+commands (@pxref{I/O Functions, ,Built-in Functions for Input/Output}).@refill
+@c Does the above verbiage prevents an overfull hbox? --mew, rjc 24jan1992
+
+@item
+The @code{ARGC}, @code{ARGV}, @code{FNR}, @code{RLENGTH}, @code{RSTART},
+and @code{SUBSEP} built-in variables (@pxref{Built-in Variables}).
+
+@item
+The conditional expression using the operators @samp{?} and @samp{:}
+(@pxref{Conditional Exp, ,Conditional Expressions}).@refill
+
+@item
+The exponentiation operator @samp{^}
+(@pxref{Arithmetic Ops, ,Arithmetic Operators}) and its assignment operator
+form @samp{^=} (@pxref{Assignment Ops, ,Assignment Expressions}).@refill
+
+@item
+C-compatible operator precedence, which breaks some old @code{awk}
+programs (@pxref{Precedence, ,Operator Precedence (How Operators Nest)}).
+
+@item
+Regexps as the value of @code{FS}
+(@pxref{Field Separators, ,Specifying how Fields are Separated}), and as the
+third argument to the @code{split} function
+(@pxref{String Functions, ,Built-in Functions for String Manipulation}).@refill
+
+@item
+Dynamic regexps as operands of the @samp{~} and @samp{!~} operators
+(@pxref{Regexp Usage, ,How to Use Regular Expressions}).
+
+@item
+Escape sequences (@pxref{Constants, ,Constant Expressions}) in regexps.@refill
+
+@item
+The escape sequences @samp{\b}, @samp{\f}, and @samp{\r}
+(@pxref{Constants, ,Constant Expressions}).
+
+@item
+Redirection of input for the @code{getline} function
+(@pxref{Getline, ,Explicit Input with @code{getline}}).@refill
+
+@item
+Multiple @code{BEGIN} and @code{END} rules
+(@pxref{BEGIN/END, ,@code{BEGIN} and @code{END} Special Patterns}).@refill
+
+@item
+Simulated multi-dimensional arrays
+(@pxref{Multi-dimensional, ,Multi-dimensional Arrays}).@refill
+@end itemize
+
+@node S5R4, POSIX, V7/S5R3.1, Language History
+@section Changes between S5R3.1 and S5R4
+
+The System V Release 4 version of Unix @code{awk} added these features
+(some of which originated in @code{gawk}):
+
+@itemize @bullet
+@item
+The @code{ENVIRON} variable (@pxref{Built-in Variables}).
+
+@item
+Multiple @samp{-f} options on the command line
+(@pxref{Command Line, ,Invoking @code{awk}}).@refill
+
+@item
+The @samp{-v} option for assigning variables before program execution begins
+(@pxref{Command Line, ,Invoking @code{awk}}).@refill
+
+@item
+The @samp{--} option for terminating command line options.
+
+@item
+The @samp{\a}, @samp{\v}, and @samp{\x} escape sequences
+(@pxref{Constants, ,Constant Expressions}).@refill
+
+@item
+A defined return value for the @code{srand} built-in function
+(@pxref{Numeric Functions, ,Numeric Built-in Functions}).
+
+@item
+The @code{toupper} and @code{tolower} built-in string functions
+for case translation
+(@pxref{String Functions, ,Built-in Functions for String Manipulation}).@refill
+
+@item
+A cleaner specification for the @samp{%c} format-control letter in the
+@code{printf} function
+(@pxref{Printf, ,Using @code{printf} Statements for Fancier Printing}).@refill
+
+@item
+The ability to dynamically pass the field width and precision (@code{"%*.*d"})
+in the argument list of the @code{printf} function
+(@pxref{Printf, ,Using @code{printf} Statements for Fancier Printing}).@refill
+
+@item
+The use of constant regexps such as @code{/foo/} as expressions, where
+they are equivalent to use of the matching operator, as in @code{$0 ~
+/foo/} (@pxref{Constants, ,Constant Expressions}).
+@end itemize
+
+@node POSIX, POSIX/GNU, S5R4, Language History
+@section Changes between S5R4 and POSIX @code{awk}
+
+The @sc{posix} Command Language and Utilities standard for @code{awk}
+introduced the following changes into the language:
+
+@itemize @bullet{}
+@item
+The use of @samp{-W} for implementation-specific options.
+
+@item
+The use of @code{CONVFMT} for controlling the conversion of numbers
+to strings (@pxref{Conversion, ,Conversion of Strings and Numbers}).
+
+@item
+The concept of a numeric string, and tighter comparison rules to go
+with it (@pxref{Comparison Ops, ,Comparison Expressions}).
+
+@item
+More complete documentation of many of the previously undocumented
+features of the language.
+@end itemize
+
+@node POSIX/GNU, , POSIX, Language History
+@section Extensions in @code{gawk} not in POSIX @code{awk}
+
+The GNU implementation, @code{gawk}, adds these features:
+
+@itemize @bullet
+@item
+The @code{AWKPATH} environment variable for specifying a path search for
+the @samp{-f} command line option
+(@pxref{Command Line, ,Invoking @code{awk}}).@refill
+
+@item
+The various @code{gawk} specific features available via the @samp{-W}
+command line option (@pxref{Command Line, ,Invoking @code{awk}}).
+
+@item
+The @code{ARGIND} variable, that tracks the movement of @code{FILENAME}
+through @code{ARGV}. (@pxref{Built-in Variables}).
+
+@item
+The @code{ERRNO} variable, that contains the system error message when
+@code{getline} returns @minus{}1, or when @code{close} fails.
+(@pxref{Built-in Variables}).
+
+@item
+The @code{IGNORECASE} variable and its effects
+(@pxref{Case-sensitivity, ,Case-sensitivity in Matching}).@refill
+
+@item
+The @code{FIELDWIDTHS} variable and its effects
+(@pxref{Constant Size, ,Reading Fixed-width Data}).@refill
+
+@item
+The @code{next file} statement for skipping to the next data file
+(@pxref{Next File Statement, ,The @code{next file} Statement}).@refill
+
+@item
+The @code{systime} and @code{strftime} built-in functions for obtaining
+and printing time stamps
+(@pxref{Time Functions, ,Functions for Dealing with Time Stamps}).@refill
+
+@item
+The @file{/dev/stdin}, @file{/dev/stdout}, @file{/dev/stderr}, and
+@file{/dev/fd/@var{n}} file name interpretation
+(@pxref{Special Files, ,Standard I/O Streams}).@refill
+
+@item
+The @samp{-W compat} option to turn off these extensions
+(@pxref{Command Line, ,Invoking @code{awk}}).@refill
+
+@item
+The @samp{-W posix} option for full @sc{posix} compliance
+(@pxref{Command Line, ,Invoking @code{awk}}).@refill
+
+@end itemize
+
+@node Installation, Gawk Summary, Language History, Top
+@chapter Installing @code{gawk}
+
+This chapter provides instructions for installing @code{gawk} on the
+various platforms that are supported by the developers. The primary
+developers support Unix (and one day, GNU), while the other ports were
+contributed. The file @file{ACKNOWLEDGMENT} in the @code{gawk}
+distribution lists the electronic mail addresses of the people who did
+the respective ports.@refill
+
+@menu
+* Gawk Distribution:: What is in the @code{gawk} distribution.
+* Unix Installation:: Installing @code{gawk} under various versions
+ of Unix.
+* VMS Installation:: Installing @code{gawk} on VMS.
+* MS-DOS Installation:: Installing @code{gawk} on MS-DOS.
+* Atari Installation:: Installing @code{gawk} on the Atari ST.
+@end menu
+
+@node Gawk Distribution, Unix Installation, Installation, Installation
+@section The @code{gawk} Distribution
+
+This section first describes how to get and extract the @code{gawk}
+distribution, and then discusses what is in the various files and
+subdirectories.
+
+@menu
+* Extracting:: How to get and extract the distribution.
+* Distribution contents:: What is in the distribution.
+@end menu
+
+@node Extracting, Distribution contents, Gawk Distribution, Gawk Distribution
+@subsection Getting the @code{gawk} Distribution
+
+@cindex getting gawk
+@cindex anonymous ftp
+@cindex anonymous uucp
+@cindex ftp, anonymous
+@cindex uucp, anonymous
+@code{gawk} is distributed as a @code{tar} file compressed with the
+GNU Zip program, @code{gzip}. You can
+get it via anonymous @code{ftp} to the Internet host @code{prep.ai.mit.edu}.
+Like all GNU software, it will be archived at other well known systems,
+from which it will be possible to use some sort of anonymous @code{uucp} to
+obtain the distribution as well.
+You can also order @code{gawk} on tape or CD-ROM directly from the
+Free Software Foundation. (The address is on the copyright page.)
+Doing so directly contributes to the support of the foundation and to
+the production of more free software.
+
+Once you have the distribution (for example,
+@file{gawk-2.15.0.tar.z}), first use @code{gzip} to expand the
+file, and then use @code{tar} to extract it. You can use the following
+pipeline to produce the @code{gawk} distribution:
+
+@example
+# Under System V, add 'o' to the tar flags
+gzip -d -c gawk-2.15.0.tar.z | tar -xvpf -
+@end example
+
+@noindent
+This will create a directory named @file{gawk-2.15} in the current
+directory.
+
+The distribution file name is of the form @file{gawk-2.15.@var{n}.tar.Z}.
+The @var{n} represents a @dfn{patchlevel}, meaning that minor bugs have
+been fixed in the major release. The current patchlevel is 0, but when
+retrieving distributions, you should get the version with the highest
+patchlevel.@refill
+
+If you are not on a Unix system, you will need to make other arrangements
+for getting and extracting the @code{gawk} distribution. You should consult
+a local expert.
+
+@node Distribution contents, , Extracting, Gawk Distribution
+@subsection Contents of the @code{gawk} Distribution
+
+@code{gawk} has a number of C source files, documentation files,
+subdirectories and files related to the configuration process
+(@pxref{Unix Installation, ,Compiling and Installing @code{gawk} on Unix}),
+and several subdirectories related to different, non-Unix,
+operating systems.@refill
+
+@table @asis
+@item various @samp{.c}, @samp{.y}, and @samp{.h} files
+
+The C and YACC source files are the actual @code{gawk} source code.
+@end table
+
+@table @file
+@item README
+@itemx README.VMS
+@itemx README.dos
+@itemx README.rs6000
+@itemx README.ultrix
+Descriptive files: @file{README} for @code{gawk} under Unix, and the
+rest for the various hardware and software combinations.
+
+@item PORTS
+A list of systems to which @code{gawk} has been ported, and which
+have successfully run the test suite.
+
+@item ACKNOWLEDGMENT
+A list of the people who contributed major parts of the code or documentation.
+
+@item NEWS
+A list of changes to @code{gawk} since the last release or patch.
+
+@item COPYING
+The GNU General Public License.
+
+@item FUTURES
+A brief list of features and/or changes being contemplated for future
+releases, with some indication of the time frame for the feature, based
+on its difficulty.
+
+@item LIMITATIONS
+A list of those factors that limit @code{gawk}'s performance.
+Most of these depend on the hardware or operating system software, and
+are not limits in @code{gawk} itself.@refill
+
+@item PROBLEMS
+A file describing known problems with the current release.
+
+@item gawk.1
+The @code{troff} source for a manual page describing @code{gawk}.
+
+@item gawk.texinfo
+@ifinfo
+The @code{texinfo} source file for this Info file.
+It should be processed with @TeX{} to produce a printed manual, and
+with @code{makeinfo} to produce the Info file.@refill
+@end ifinfo
+@iftex
+The @code{texinfo} source file for this manual.
+It should be processed with @TeX{} to produce a printed manual, and
+with @code{makeinfo} to produce the Info file.@refill
+@end iftex
+
+@item Makefile.in
+@itemx config
+@itemx config.in
+@itemx configure
+@itemx missing
+@itemx mungeconf
+These files and subdirectories are used when configuring @code{gawk}
+for various Unix systems. They are explained in detail in
+@ref{Unix Installation, ,Compiling and Installing @code{gawk} on Unix}.@refill
+
+@item atari
+Files needed for building @code{gawk} on an Atari ST.
+@xref{Atari Installation, ,Installing @code{gawk} on the Atari ST}, for details.
+
+@item pc
+Files needed for building @code{gawk} under MS-DOS.
+@xref{MS-DOS Installation, ,Installing @code{gawk} on MS-DOS}, for details.
+
+@item vms
+Files needed for building @code{gawk} under VMS.
+@xref{VMS Installation, ,Compiling Installing and Running @code{gawk} on VMS}, for details.
+
+@item test
+Many interesting @code{awk} programs, provided as a test suite for
+@code{gawk}. You can use @samp{make test} from the top level @code{gawk}
+directory to run your version of @code{gawk} against the test suite.
+@c There are many programs here that are useful in their own right.
+If @code{gawk} successfully passes @samp{make test} then you can
+be confident of a successful port.@refill
+@end table
+
+@node Unix Installation, VMS Installation, Gawk Distribution, Installation
+@section Compiling and Installing @code{gawk} on Unix
+
+Often, you can compile and install @code{gawk} by typing only two
+commands. However, if you do not use a supported system, you may need
+to configure @code{gawk} for your system yourself.
+
+@menu
+* Quick Installation:: Compiling @code{gawk} on a
+ supported Unix version.
+* Configuration Philosophy:: How it's all supposed to work.
+* New Configurations:: What to do if there is no supplied
+ configuration for your system.
+@end menu
+
+@node Quick Installation, Configuration Philosophy, Unix Installation, Unix Installation
+@subsection Compiling @code{gawk} for a Supported Unix Version
+
+@cindex installation, unix
+After you have extracted the @code{gawk} distribution, @code{cd}
+to @file{gawk-2.15}. Look in the @file{config} subdirectory for a
+file that matches your hardware/software combination. In general,
+only the software is relevant; for example @code{sunos41} is used
+for SunOS 4.1, on both Sun 3 and Sun 4 hardware.@refill
+
+If you find such a file, run the command:
+
+@example
+# assume you have SunOS 4.1
+./configure sunos41
+@end example
+
+This produces a @file{Makefile} and @file{config.h} tailored to your
+system. You may wish to edit the @file{Makefile} to use a different
+C compiler, such as @code{gcc}, the GNU C compiler, if you have it.
+You may also wish to change the @code{CFLAGS} variable, which controls
+the command line options that are passed to the C compiler (such as
+optimization levels, or compiling for debugging).@refill
+
+After you have configured @file{Makefile} and @file{config.h}, type:
+
+@example
+make
+@end example
+
+@noindent
+and shortly thereafter, you should have an executable version of @code{gawk}.
+That's all there is to it!
+
+@node Configuration Philosophy, New Configurations, Quick Installation, Unix Installation
+@subsection The Configuration Process
+
+(This section is of interest only if you know something about using the
+C language and the Unix operating system.)
+
+The source code for @code{gawk} generally attempts to adhere to industry
+standards wherever possible. This means that @code{gawk} uses library
+routines that are specified by the @sc{ansi} C standard and by the @sc{posix}
+operating system interface standard. When using an @sc{ansi} C compiler,
+function prototypes are provided to help improve the compile-time checking.
+
+Many older Unix systems do not support all of either the @sc{ansi} or the
+@sc{posix} standards. The @file{missing} subdirectory in the @code{gawk}
+distribution contains replacement versions of those subroutines that are
+most likely to be missing.
+
+The @file{config.h} file that is created by the @code{configure} program
+contains definitions that describe features of the particular operating
+system where you are attempting to compile @code{gawk}. For the most
+part, it lists which standard subroutines are @emph{not} available.
+For example, if your system lacks the @samp{getopt} routine, then
+@samp{GETOPT_MISSING} would be defined.
+
+@file{config.h} also defines constants that describe facts about your
+variant of Unix. For example, there may not be an @samp{st_blksize}
+element in the @code{stat} structure. In this case @samp{BLKSIZE_MISSING}
+would be defined.
+
+Based on the list in @file{config.h} of standard subroutines that are
+missing, @file{missing.c} will do a @samp{#include} of the appropriate
+file(s) from the @file{missing} subdirectory.@refill
+
+Conditionally compiled code in the other source files relies on the
+other definitions in the @file{config.h} file.
+
+Besides creating @file{config.h}, @code{configure} produces a @file{Makefile}
+from @file{Makefile.in}. There are a number of lines in @file{Makefile.in}
+that are system or feature specific. For example, there is line that begins
+with @samp{##MAKE_ALLOCA_C##}. This is normally a comment line, since
+it starts with @samp{#}. If a configuration file has @samp{MAKE_ALLOCA_C}
+in it, then @code{configure} will delete the @samp{##MAKE_ALLOCA_C##}
+from the beginning of the line. This will enable the rules in the
+@file{Makefile} that use a C version of @samp{alloca}. There are several
+similar features that work in this fashion.@refill
+
+@node New Configurations, , Configuration Philosophy, Unix Installation
+@subsection Configuring @code{gawk} for a New System
+
+(This section is of interest only if you know something about using the
+C language and the Unix operating system, and if you have to install
+@code{gawk} on a system that is not supported by the @code{gawk} distribution.
+If you are a C or Unix novice, get help from a local expert.)
+
+If you need to configure @code{gawk} for a Unix system that is not
+supported in the distribution, first see
+@ref{Configuration Philosophy, ,The Configuration Process}.
+Then, copy @file{config.in} to @file{config.h}, and copy
+@file{Makefile.in} to @file{Makefile}.@refill
+
+Next, edit both files. Both files are liberally commented, and the
+necessary changes should be straightforward.
+
+While editing @file{config.h}, you need to determine what library
+routines you do or do not have by consulting your system documentation, or
+by perusing your actual libraries using the @code{ar} or @code{nm} utilities.
+In the worst case, simply do not define @emph{any} of the macros for missing
+subroutines. When you compile @code{gawk}, the final link-editing step
+will fail. The link editor will provide you with a list of unresolved external
+references---these are the missing subroutines. Edit @file{config.h} again
+and recompile, and you should be set.@refill
+
+Editing the @file{Makefile} should also be straightforward. Enable or
+disable the lines that begin with @samp{##MAKE_@var{whatever}##}, as
+appropriate. Select the correct C compiler and @code{CFLAGS} for it.
+Then run @code{make}.
+
+Getting a correct configuration is likely to be an iterative process.
+Do not be discouraged if it takes you several tries. If you have no
+luck whatsoever, please report your system type, and the steps you took.
+Once you do have a working configuration, please send it to the maintainers
+so that support for your system can be added to the official release.
+
+@xref{Bugs, ,Reporting Problems and Bugs}, for information on how to report
+problems in configuring @code{gawk}. You may also use the same mechanisms
+for sending in new configurations.@refill
+
+@node VMS Installation, MS-DOS Installation, Unix Installation, Installation
+@section Compiling, Installing, and Running @code{gawk} on VMS
+
+@c based on material from
+@c Pat Rankin <rankin@eql.caltech.edu>
+
+@cindex installation, vms
+This section describes how to compile and install @code{gawk} under VMS.
+
+@menu
+* VMS Compilation:: How to compile @code{gawk} under VMS.
+* VMS Installation Details:: How to install @code{gawk} under VMS.
+* VMS Running:: How to run @code{gawk} under VMS.
+* VMS POSIX:: Alternate instructions for VMS POSIX.
+@end menu
+
+@node VMS Compilation, VMS Installation Details, VMS Installation, VMS Installation
+@subsection Compiling @code{gawk} under VMS
+
+To compile @code{gawk} under VMS, there is a @code{DCL} command procedure that
+will issue all the necessary @code{CC} and @code{LINK} commands, and there is
+also a @file{Makefile} for use with the @code{MMS} utility. From the source
+directory, use either
+
+@smallexample
+$ @@[.VMS]VMSBUILD.COM
+@end smallexample
+
+@noindent
+or
+
+@smallexample
+$ MMS/DESCRIPTION=[.VMS]DECSRIP.MMS GAWK
+@end smallexample
+
+Depending upon which C compiler you are using, follow one of the sets
+of instructions in this table:
+
+@table @asis
+@item VAX C V3.x
+Use either @file{vmsbuild.com} or @file{descrip.mms} as is. These use
+@code{CC/OPTIMIZE=NOLINE}, which is essential for Version 3.0.
+
+@item VAX C V2.x
+You must have Version 2.3 or 2.4; older ones won't work. Edit either
+@file{vmsbuild.com} or @file{descrip.mms} according to the comments in them.
+For @file{vmsbuild.com}, this just entails removing two @samp{!} delimiters.
+Also edit @file{config.h} (which is a copy of file @file{[.config]vms-conf.h})
+and comment out or delete the two lines @samp{#define __STDC__ 0} and
+@samp{#define VAXC_BUILTINS} near the end.@refill
+
+@item GNU C
+Edit @file{vmsbuild.com} or @file{descrip.mms}; the changes are different
+from those for VAX C V2.x, but equally straightforward. No changes to
+@file{config.h} should be needed.
+
+@item DEC C
+Edit @file{vmsbuild.com} or @file{descrip.mms} according to their comments.
+No changes to @file{config.h} should be needed.
+@end table
+
+@code{gawk} 2.15 has been tested under VAX/VMS 5.5-1 using VAX C V3.2,
+GNU C 1.40 and 2.3. It should work without modifications for VMS V4.6 and up.
+
+@node VMS Installation Details, VMS Running, VMS Compilation, VMS Installation
+@subsection Installing @code{gawk} on VMS
+
+To install @code{gawk}, all you need is a ``foreign'' command, which is
+a @code{DCL} symbol whose value begins with a dollar sign.
+
+@smallexample
+$ GAWK :== $device:[directory]GAWK
+@end smallexample
+
+@noindent
+(Substitute the actual location of @code{gawk.exe} for
+@samp{device:[directory]}.) The symbol should be placed in the
+@file{login.com} of any user who wishes to run @code{gawk},
+so that it will be defined every time the user logs on.
+Alternatively, the symbol may be placed in the system-wide
+@file{sylogin.com} procedure, which will allow all users
+to run @code{gawk}.@refill
+
+Optionally, the help entry can be loaded into a VMS help library:
+
+@smallexample
+$ LIBRARY/HELP SYS$HELP:HELPLIB [.VMS]GAWK.HLP
+@end smallexample
+
+@noindent
+(You may want to substitute a site-specific help library rather than
+the standard VMS library @samp{HELPLIB}.) After loading the help text,
+
+@c this is so tiny, but `should' be smallexample for consistency sake...
+@c I didn't because it was so short. --mew 29jan1992
+@example
+$ HELP GAWK
+@end example
+
+@noindent
+will provide information about both the @code{gawk} implementation and the
+@code{awk} programming language.
+
+The logical name @samp{AWK_LIBRARY} can designate a default location
+for @code{awk} program files. For the @samp{-f} option, if the specified
+filename has no device or directory path information in it, @code{gawk}
+will look in the current directory first, then in the directory specified
+by the translation of @samp{AWK_LIBRARY} if the file was not found.
+If after searching in both directories, the file still is not found,
+then @code{gawk} appends the suffix @samp{.awk} to the filename and the
+file search will be re-tried. If @samp{AWK_LIBRARY} is not defined, that
+portion of the file search will fail benignly.@refill
+
+@node VMS Running, VMS POSIX, VMS Installation Details, VMS Installation
+@subsection Running @code{gawk} on VMS
+
+Command line parsing and quoting conventions are significantly different
+on VMS, so examples in this manual or from other sources often need minor
+changes. They @emph{are} minor though, and all @code{awk} programs
+should run correctly.
+
+Here are a couple of trivial tests:
+
+@smallexample
+$ gawk -- "BEGIN @{print ""Hello, World!""@}"
+$ gawk -"W" version ! could also be -"W version" or "-W version"
+@end smallexample
+
+@noindent
+Note that upper-case and mixed-case text must be quoted.
+
+The VMS port of @code{gawk} includes a @code{DCL}-style interface in addition
+to the original shell-style interface (see the help entry for details).
+One side-effect of dual command line parsing is that if there is only a
+single parameter (as in the quoted string program above), the command
+becomes ambiguous. To work around this, the normally optional @samp{--}
+flag is required to force Unix style rather than @code{DCL} parsing. If any
+other dash-type options (or multiple parameters such as data files to be
+processed) are present, there is no ambiguity and @samp{--} can be omitted.
+
+The default search path when looking for @code{awk} program files specified
+by the @samp{-f} option is @code{"SYS$DISK:[],AWK_LIBRARY:"}. The logical
+name @samp{AWKPATH} can be used to override this default. The format
+of @samp{AWKPATH} is a comma-separated list of directory specifications.
+When defining it, the value should be quoted so that it retains a single
+translation, and not a multi-translation @code{RMS} searchlist.
+
+@node VMS POSIX, , VMS Running, VMS Installation
+@subsection Building and using @code{gawk} under VMS POSIX
+
+Ignore the instructions above, although @file{vms/gawk.hlp} should still
+be made available in a help library. Make sure that the two scripts,
+@file{configure} and @file{mungeconf}, are executable; use @samp{chmod +x}
+on them if necessary. Then execute the following commands:
+
+@smallexample
+$ POSIX
+psx> configure vms-posix
+psx> make awktab.c gawk
+@end smallexample
+
+@noindent
+The first command will construct files @file{config.h} and @file{Makefile}
+out of templates. The second command will compile and link @code{gawk}.
+Due to a @code{make} bug in VMS POSIX V1.0 and V1.1,
+the file @file{awktab.c} must be given as an explicit target or it will
+not be built and the final link step will fail. Ignore the warning
+@samp{"Could not find lib m in lib list"}; it is harmless, caused by the
+explicit use of @samp{-lm} as a linker option which is not needed
+under VMS POSIX. Under V1.1 (but not V1.0) a problem with the @code{yacc}
+skeleton @file{/etc/yyparse.c} will cause a compiler warning for
+@file{awktab.c}, followed by a linker warning about compilation warnings
+in the resulting object module. These warnings can be ignored.@refill
+
+Once built, @code{gawk} will work like any other shell utility. Unlike
+the normal VMS port of @code{gawk}, no special command line manipulation is
+needed in the VMS POSIX environment.
+
+@node MS-DOS Installation, Atari Installation, VMS Installation, Installation
+@section Installing @code{gawk} on MS-DOS
+
+@cindex installation, ms-dos
+The first step is to get all the files in the @code{gawk} distribution
+onto your PC. Move all the files from the @file{pc} directory into
+the main directory where the other files are. Edit the file
+@file{make.bat} so that it will be an acceptable MS-DOS batch file.
+This means making sure that all lines are terminated with the ASCII
+carriage return and line feed characters.
+restrictions.
+
+@code{gawk} has only been compiled with version 5.1 of the Microsoft
+C compiler. The file @file{make.bat} from the @file{pc} directory
+assumes that you have this compiler.
+
+Copy the file @file{setargv.obj} from the library directory where it
+resides to the @code{gawk} source code directory.
+
+Run @file{make.bat}. This will compile @code{gawk} for you, and link it.
+That's all there is to it!
+
+@node Atari Installation, , MS-DOS Installation, Installation
+@section Installing @code{gawk} on the Atari ST
+
+@c based on material from
+@c Michal Jaegermann <ntomczak@vm.ucs.ualberta.ca>
+
+@cindex installation, atari
+This section assumes that you are running TOS. It applies to other Atari
+models (STe, TT) as well.
+
+In order to use @code{gawk}, you need to have a shell, either text or
+graphics, that does not map all the characters of a command line to
+upper case. Maintaining case distinction in option flags is very
+important (@pxref{Command Line, ,Invoking @code{awk}}). Popular shells
+like @code{gulam} or @code{gemini} will work, as will newer versions of
+@code{desktop}. Support for I/O redirection is necessary to make it easy
+to import @code{awk} programs from other environments. Pipes are nice to have,
+but not vital.
+
+If you have received an executable version of @code{gawk}, place it,
+as usual, anywhere in your @code{PATH} where your shell will find it.
+
+While executing, @code{gawk} creates a number of temporary files.
+@code{gawk} looks for either of the environment variables @code{TEMP}
+or @code{TMPDIR}, in that order. If either one is found, its value
+is assumed to be a directory for temporary files. This directory
+must exist, and if you can spare the memory, it is a good idea to
+put it on a @sc{ram} drive. If neither @code{TEMP} nor @code{TMPDIR}
+are found, then @code{gawk} uses the current directory for its
+temporary files.
+
+The ST version of @code{gawk} searches for its program files as
+described in @ref{AWKPATH Variable, ,The @code{AWKPATH} Environment Variable}.
+On the ST, the default value for the @code{AWKPATH} variable is
+@code{@w{".,c:\lib\awk,c:\gnu\lib\awk"}}.
+The search path can be modified by explicitly setting @code{AWKPATH} to
+whatever you wish. Note that colons cannot be used on the ST to separate
+elements in the @code{AWKPATH} variable, since they have another, reserved,
+meaning. Instead, you must use a comma to separate elements in the path.
+If you are recompiling @code{gawk} on the ST, then you can choose a new
+default search path, by setting the value of @samp{DEFPATH} in the file
+@file{...\config\atari}. You may choose a different separator character
+by setting the value of @samp{ENVSEP} in the same file. The new values will
+be used when creating the header file @file{config.h}.@refill
+
+@ignore
+As a last resort, small
+adjustments can be made directly on the executable version of @code{gawk}
+using a binary editor.@refill
+@end ignore
+
+Although @code{awk} allows great flexibility in doing I/O redirections
+from within a program, this facility should be used with care on the ST.
+In some circumstances the OS routines for file handle pool processing
+lose track of certain events, causing the computer to crash, and requiring
+a reboot. Often a warm reboot is sufficient. Fortunately, this happens
+infrequently, and in rather esoteric situations. In particular, avoid
+having one part of an @code{awk} program using @code{print}
+statements explicitly redirected to @code{"/dev/stdout"}, while other
+@code{print} statements use the default standard output, and a
+calling shell has redirected standard output to a file.@refill
+@c whew!
+
+When @code{gawk} is compiled with the ST version of @code{gcc} and its
+usual libraries, it will accept both @samp{/} and @samp{\} as path separators.
+While this is convenient, it should be remembered that this removes one,
+technically legal, character (@samp{/}) from your file names, and that
+it may create problems for external programs, called via the @code{system()}
+function, which may not support this convention. Whenever it is possible
+that a file created by @code{gawk} will be used by some other program,
+use only backslashes. Also remember that in @code{awk}, backslashes in
+strings have to be doubled in order to get literal backslashes.
+
+The initial port of @code{gawk} to the ST was done with @code{gcc}.
+If you wish to recompile @code{gawk} from scratch, you will need to use
+a compiler that accepts @sc{ansi} standard C (such as @code{gcc}, Turbo C,
+or Prospero C). If @code{sizeof(int) != @w{sizeof(int *)}}, the correctness
+of the generated code depends heavily on the fact that all function calls
+have function prototypes in the current scope. If your compiler does
+not accept function prototypes, you will probably have to add a
+number of casts to the code.@refill
+
+If you are using @code{gcc}, make sure that you have up-to-date libraries.
+Older versions have problems with some library functions (@code{atan2()},
+@code{strftime()}, the @samp{%g} conversion in @code{sprintf()}) which
+may affect the operation of @code{gawk}.
+
+In the @file{atari} subdirectory of the @code{gawk} distribution is
+a version of the @code{system()} function that has been tested with
+@code{gulam} and @code{msh}; it should work with other shells as well.
+With @code{gulam}, it passes the string to be executed without spawning
+an extra copy of a shell. It is possible to replace this version of
+@code{system()} with a similar function from a library or from some other
+source if that version would be a better choice for the shell you prefer.
+
+The files needed to recompile @code{gawk} on the ST can be found in
+the @file{atari} directory. The provided files and instructions below
+assume that you have the GNU C compiler (@code{gcc}), the @code{gulam} shell,
+and an ST version of @code{sed}. The @file{Makefile} is set up to use
+@file{byacc} as a @file{yacc} replacement. With a different set of tools some
+adjustments and/or editing will be needed.@refill
+
+@code{cd} to the @file{atari} directory. Copy @file{Makefile.st} to
+@file{makefile} in the source (parent) directory. Possibly adjust
+@file{../config/atari} to suit your system. Execute the script @file{mkconf.g}
+which will create the header file @file{../config.h}. Go back to the source
+directory. If you are not using @code{gcc}, check the file @file{missing.c}.
+It may be necessary to change forward slashes in the references to files
+from the @file{atari} subdirectory into backslashes. Type @code{make} and
+enjoy.@refill
+
+Compilation with @code{gcc} of some of the bigger modules, like
+@file{awk_tab.c}, may require a full four megabytes of memory. On smaller
+machines you would need to cut down on optimizations, or you would have to
+switch to another, less memory hungry, compiler.@refill
+
+@node Gawk Summary, Sample Program, Installation, Top
+@appendix @code{gawk} Summary
+
+This appendix provides a brief summary of the @code{gawk} command line and the
+@code{awk} language. It is designed to serve as ``quick reference.'' It is
+therefore terse, but complete.
+
+@menu
+* Command Line Summary:: Recapitulation of the command line.
+* Language Summary:: A terse review of the language.
+* Variables/Fields:: Variables, fields, and arrays.
+* Rules Summary:: Patterns and Actions, and their
+ component parts.
+* Functions Summary:: Defining and calling functions.
+* Historical Features:: Some undocumented but supported ``features''.
+@end menu
+
+@node Command Line Summary, Language Summary, Gawk Summary, Gawk Summary
+@appendixsec Command Line Options Summary
+
+The command line consists of options to @code{gawk} itself, the
+@code{awk} program text (if not supplied via the @samp{-f} option), and
+values to be made available in the @code{ARGC} and @code{ARGV}
+predefined @code{awk} variables:
+
+@example
+awk @r{[@var{POSIX or GNU style options}]} -f source-file @r{[@code{--}]} @var{file} @dots{}
+awk @r{[@var{POSIX or GNU style options}]} @r{[@code{--}]} '@var{program}' @var{file} @dots{}
+@end example
+
+The options that @code{gawk} accepts are:
+
+@table @code
+@item -F @var{fs}
+@itemx --field-separator=@var{fs}
+Use @var{fs} for the input field separator (the value of the @code{FS}
+predefined variable).
+
+@item -f @var{program-file}
+@itemx --file=@var{program-file}
+Read the @code{awk} program source from the file @var{program-file}, instead
+of from the first command line argument.
+
+@item -v @var{var}=@var{val}
+@itemx --assign=@var{var}=@var{val}
+Assign the variable @var{var} the value @var{val} before program execution
+begins.
+
+@item -W compat
+@itemx --compat
+Specifies compatibility mode, in which @code{gawk} extensions are turned
+off.
+
+@item -W copyleft
+@itemx -W copyright
+@itemx --copyleft
+@itemx --copyright
+Print the short version of the General Public License on the error
+output. This option may disappear in a future version of @code{gawk}.
+
+@item -W help
+@itemx -W usage
+@itemx --help
+@itemx --usage
+Print a relatively short summary of the available options on the error output.
+
+@item -W lint
+@itemx --lint
+Give warnings about dubious or non-portable @code{awk} constructs.
+
+@item -W posix
+@itemx --posix
+Specifies @sc{posix} compatibility mode, in which @code{gawk} extensions
+are turned off and additional restrictions apply.
+
+@item -W source=@var{program-text}
+@itemx --source=@var{program-text}
+Use @var{program-text} as @code{awk} program source code. This option allows
+mixing command line source code with source code from files, and is
+particularly useful for mixing command line programs with library functions.
+
+@item -W version
+@itemx --version
+Print version information for this particular copy of @code{gawk} on the error
+output. This option may disappear in a future version of @code{gawk}.
+
+@item --
+Signal the end of options. This is useful to allow further arguments to the
+@code{awk} program itself to start with a @samp{-}. This is mainly for
+consistency with the argument parsing conventions of @sc{posix}.
+@end table
+
+Any other options are flagged as invalid, but are otherwise ignored.
+@xref{Command Line, ,Invoking @code{awk}}, for more details.
+
+@node Language Summary, Variables/Fields, Command Line Summary, Gawk Summary
+@appendixsec Language Summary
+
+An @code{awk} program consists of a sequence of pattern-action statements
+and optional function definitions.
+
+@example
+@var{pattern} @{ @var{action statements} @}
+
+function @var{name}(@var{parameter list}) @{ @var{action statements} @}
+@end example
+
+@code{gawk} first reads the program source from the
+@var{program-file}(s) if specified, or from the first non-option
+argument on the command line. The @samp{-f} option may be used multiple
+times on the command line. @code{gawk} reads the program text from all
+the @var{program-file} files, effectively concatenating them in the
+order they are specified. This is useful for building libraries of
+@code{awk} functions, without having to include them in each new
+@code{awk} program that uses them. To use a library function in a file
+from a program typed in on the command line, specify @samp{-f /dev/tty};
+then type your program, and end it with a @kbd{Control-d}.
+@xref{Command Line, ,Invoking @code{awk}}.@refill
+
+The environment variable @code{AWKPATH} specifies a search path to use
+when finding source files named with the @samp{-f} option. The default
+path, which is
+@samp{.:/usr/lib/awk:/usr/local/lib/awk} is used if @code{AWKPATH} is not set.
+If a file name given to the @samp{-f} option contains a @samp{/} character,
+no path search is performed.
+@xref{AWKPATH Variable, ,The @code{AWKPATH} Environment Variable},
+for a full description of the @code{AWKPATH} environment variable.@refill
+
+@code{gawk} compiles the program into an internal form, and then proceeds to
+read each file named in the @code{ARGV} array. If there are no files named
+on the command line, @code{gawk} reads the standard input.
+
+If a ``file'' named on the command line has the form
+@samp{@var{var}=@var{val}}, it is treated as a variable assignment: the
+variable @var{var} is assigned the value @var{val}.
+If any of the files have a value that is the null string, that
+element in the list is skipped.@refill
+
+For each line in the input, @code{gawk} tests to see if it matches any
+@var{pattern} in the @code{awk} program. For each pattern that the line
+matches, the associated @var{action} is executed.
+
+@node Variables/Fields, Rules Summary, Language Summary, Gawk Summary
+@appendixsec Variables and Fields
+
+@code{awk} variables are dynamic; they come into existence when they are
+first used. Their values are either floating-point numbers or strings.
+@code{awk} also has one-dimension arrays; multiple-dimensional arrays
+may be simulated. There are several predefined variables that
+@code{awk} sets as a program runs; these are summarized below.
+
+@menu
+* Fields Summary:: Input field splitting.
+* Built-in Summary:: @code{awk}'s built-in variables.
+* Arrays Summary:: Using arrays.
+* Data Type Summary:: Values in @code{awk} are numbers or strings.
+@end menu
+
+@node Fields Summary, Built-in Summary, Variables/Fields, Variables/Fields
+@appendixsubsec Fields
+
+As each input line is read, @code{gawk} splits the line into
+@var{fields}, using the value of the @code{FS} variable as the field
+separator. If @code{FS} is a single character, fields are separated by
+that character. Otherwise, @code{FS} is expected to be a full regular
+expression. In the special case that @code{FS} is a single blank,
+fields are separated by runs of blanks and/or tabs. Note that the value
+of @code{IGNORECASE} (@pxref{Case-sensitivity, ,Case-sensitivity in Matching})
+also affects how fields are split when @code{FS} is a regular expression.@refill
+
+Each field in the input line may be referenced by its position, @code{$1},
+@code{$2}, and so on. @code{$0} is the whole line. The value of a field may
+be assigned to as well. Field numbers need not be constants:
+
+@example
+n = 5
+print $n
+@end example
+
+@noindent
+prints the fifth field in the input line. The variable @code{NF} is set to
+the total number of fields in the input line.
+
+References to nonexistent fields (i.e., fields after @code{$NF}) return
+the null-string. However, assigning to a nonexistent field (e.g.,
+@code{$(NF+2) = 5}) increases the value of @code{NF}, creates any
+intervening fields with the null string as their value, and causes the
+value of @code{$0} to be recomputed, with the fields being separated by
+the value of @code{OFS}.@refill
+
+@xref{Reading Files, ,Reading Input Files}, for a full description of the
+way @code{awk} defines and uses fields.
+
+@node Built-in Summary, Arrays Summary, Fields Summary, Variables/Fields
+@appendixsubsec Built-in Variables
+
+@code{awk}'s built-in variables are:
+
+@table @code
+@item ARGC
+The number of command line arguments (not including options or the
+@code{awk} program itself).
+
+@item ARGIND
+The index in @code{ARGV} of the current file being processed.
+It is always true that @samp{FILENAME == ARGV[ARGIND]}.
+
+@item ARGV
+The array of command line arguments. The array is indexed from 0 to
+@code{ARGC} @minus{} 1. Dynamically changing the contents of @code{ARGV}
+can control the files used for data.@refill
+
+@item CONVFMT
+The conversion format to use when converting numbers to strings.
+
+@item FIELDWIDTHS
+A space separated list of numbers describing the fixed-width input data.
+
+@item ENVIRON
+An array containing the values of the environment variables. The array
+is indexed by variable name, each element being the value of that
+variable. Thus, the environment variable @code{HOME} would be in
+@code{ENVIRON["HOME"]}. Its value might be @file{/u/close}.
+
+Changing this array does not affect the environment seen by programs
+which @code{gawk} spawns via redirection or the @code{system} function.
+(This may change in a future version of @code{gawk}.)
+
+Some operating systems do not have environment variables.
+The array @code{ENVIRON} is empty when running on these systems.
+
+@item ERRNO
+The system error message when an error occurs using @code{getline}
+or @code{close}.
+
+@item FILENAME
+The name of the current input file. If no files are specified on the command
+line, the value of @code{FILENAME} is @samp{-}.
+
+@item FNR
+The input record number in the current input file.
+
+@item FS
+The input field separator, a blank by default.
+
+@item IGNORECASE
+The case-sensitivity flag for regular expression operations. If
+@code{IGNORECASE} has a nonzero value, then pattern matching in rules,
+field splitting with @code{FS}, regular expression matching with
+@samp{~} and @samp{!~}, and the @code{gsub}, @code{index}, @code{match},
+@code{split} and @code{sub} predefined functions all ignore case
+when doing regular expression operations.@refill
+
+@item NF
+The number of fields in the current input record.
+
+@item NR
+The total number of input records seen so far.
+
+@item OFMT
+The output format for numbers for the @code{print} statement,
+@code{"%.6g"} by default.
+
+@item OFS
+The output field separator, a blank by default.
+
+@item ORS
+The output record separator, by default a newline.
+
+@item RS
+The input record separator, by default a newline. @code{RS} is exceptional
+in that only the first character of its string value is used for separating
+records. If @code{RS} is set to the null string, then records are separated by
+blank lines. When @code{RS} is set to the null string, then the newline
+character always acts as a field separator, in addition to whatever value
+@code{FS} may have.@refill
+
+@item RSTART
+The index of the first character matched by @code{match}; 0 if no match.
+
+@item RLENGTH
+The length of the string matched by @code{match}; @minus{}1 if no match.
+
+@item SUBSEP
+The string used to separate multiple subscripts in array elements, by
+default @code{"\034"}.
+@end table
+
+@xref{Built-in Variables}, for more information.
+
+@node Arrays Summary, Data Type Summary, Built-in Summary, Variables/Fields
+@appendixsubsec Arrays
+
+Arrays are subscripted with an expression between square brackets
+(@samp{[} and @samp{]}). Array subscripts are @emph{always} strings;
+numbers are converted to strings as necessary, following the standard
+conversion rules
+(@pxref{Conversion, ,Conversion of Strings and Numbers}).@refill
+
+If you use multiple expressions separated by commas inside the square
+brackets, then the array subscript is a string consisting of the
+concatenation of the individual subscript values, converted to strings,
+separated by the subscript separator (the value of @code{SUBSEP}).
+
+The special operator @code{in} may be used in an @code{if} or
+@code{while} statement to see if an array has an index consisting of a
+particular value.
+
+@example
+if (val in array)
+ print array[val]
+@end example
+
+If the array has multiple subscripts, use @code{(i, j, @dots{}) in array}
+to test for existence of an element.
+
+The @code{in} construct may also be used in a @code{for} loop to iterate
+over all the elements of an array.
+@xref{Scanning an Array, ,Scanning all Elements of an Array}.@refill
+
+An element may be deleted from an array using the @code{delete} statement.
+
+@xref{Arrays, ,Arrays in @code{awk}}, for more detailed information.
+
+@node Data Type Summary, , Arrays Summary, Variables/Fields
+@appendixsubsec Data Types
+
+The value of an @code{awk} expression is always either a number
+or a string.
+
+Certain contexts (such as arithmetic operators) require numeric
+values. They convert strings to numbers by interpreting the text
+of the string as a numeral. If the string does not look like a
+numeral, it converts to 0.
+
+Certain contexts (such as concatenation) require string values.
+They convert numbers to strings by effectively printing them
+with @code{sprintf}.
+@xref{Conversion, ,Conversion of Strings and Numbers}, for the details.@refill
+
+To force conversion of a string value to a number, simply add 0
+to it. If the value you start with is already a number, this
+does not change it.
+
+To force conversion of a numeric value to a string, concatenate it with
+the null string.
+
+The @code{awk} language defines comparisons as being done numerically if
+both operands are numeric, or if one is numeric and the other is a numeric
+string. Otherwise one or both operands are converted to strings and a
+string comparison is performed.
+
+Uninitialized variables have the string value @code{""} (the null, or
+empty, string). In contexts where a number is required, this is
+equivalent to 0.
+
+@xref{Variables}, for more information on variable naming and initialization;
+@pxref{Conversion, ,Conversion of Strings and Numbers}, for more information
+on how variable values are interpreted.@refill
+
+@node Rules Summary, Functions Summary, Variables/Fields, Gawk Summary
+@appendixsec Patterns and Actions
+
+@menu
+* Pattern Summary:: Quick overview of patterns.
+* Regexp Summary:: Quick overview of regular expressions.
+* Actions Summary:: Quick overview of actions.
+@end menu
+
+An @code{awk} program is mostly composed of rules, each consisting of a
+pattern followed by an action. The action is enclosed in @samp{@{} and
+@samp{@}}. Either the pattern may be missing, or the action may be
+missing, but, of course, not both. If the pattern is missing, the
+action is executed for every single line of input. A missing action is
+equivalent to this action,
+
+@example
+@{ print @}
+@end example
+
+@noindent
+which prints the entire line.
+
+Comments begin with the @samp{#} character, and continue until the end of the
+line. Blank lines may be used to separate statements. Normally, a statement
+ends with a newline, however, this is not the case for lines ending in a
+@samp{,}, @samp{@{}, @samp{?}, @samp{:}, @samp{&&}, or @samp{||}. Lines
+ending in @code{do} or @code{else} also have their statements automatically
+continued on the following line. In other cases, a line can be continued by
+ending it with a @samp{\}, in which case the newline is ignored.@refill
+
+Multiple statements may be put on one line by separating them with a @samp{;}.
+This applies to both the statements within the action part of a rule (the
+usual case), and to the rule statements.
+
+@xref{Comments, ,Comments in @code{awk} Programs}, for information on
+@code{awk}'s commenting convention;
+@pxref{Statements/Lines, ,@code{awk} Statements versus Lines}, for a
+description of the line continuation mechanism in @code{awk}.@refill
+
+@node Pattern Summary, Regexp Summary, Rules Summary, Rules Summary
+@appendixsubsec Patterns
+
+@code{awk} patterns may be one of the following:
+
+@example
+/@var{regular expression}/
+@var{relational expression}
+@var{pattern} && @var{pattern}
+@var{pattern} || @var{pattern}
+@var{pattern} ? @var{pattern} : @var{pattern}
+(@var{pattern})
+! @var{pattern}
+@var{pattern1}, @var{pattern2}
+BEGIN
+END
+@end example
+
+@code{BEGIN} and @code{END} are two special kinds of patterns that are not
+tested against the input. The action parts of all @code{BEGIN} rules are
+merged as if all the statements had been written in a single @code{BEGIN}
+rule. They are executed before any of the input is read. Similarly, all the
+@code{END} rules are merged, and executed when all the input is exhausted (or
+when an @code{exit} statement is executed). @code{BEGIN} and @code{END}
+patterns cannot be combined with other patterns in pattern expressions.
+@code{BEGIN} and @code{END} rules cannot have missing action parts.@refill
+
+For @samp{/@var{regular-expression}/} patterns, the associated statement is
+executed for each input line that matches the regular expression. Regular
+expressions are extensions of those in @code{egrep}, and are summarized below.
+
+A @var{relational expression} may use any of the operators defined below in
+the section on actions. These generally test whether certain fields match
+certain regular expressions.
+
+The @samp{&&}, @samp{||}, and @samp{!} operators are logical ``and,''
+logical ``or,'' and logical ``not,'' respectively, as in C. They do
+short-circuit evaluation, also as in C, and are used for combining more
+primitive pattern expressions. As in most languages, parentheses may be
+used to change the order of evaluation.
+
+The @samp{?:} operator is like the same operator in C. If the first
+pattern matches, then the second pattern is matched against the input
+record; otherwise, the third is matched. Only one of the second and
+third patterns is matched.
+
+The @samp{@var{pattern1}, @var{pattern2}} form of a pattern is called a
+range pattern. It matches all input lines starting with a line that
+matches @var{pattern1}, and continuing until a line that matches
+@var{pattern2}, inclusive. A range pattern cannot be used as an operand
+to any of the pattern operators.
+
+@xref{Patterns}, for a full description of the pattern part of @code{awk}
+rules.
+
+@node Regexp Summary, Actions Summary, Pattern Summary, Rules Summary
+@appendixsubsec Regular Expressions
+
+Regular expressions are the extended kind found in @code{egrep}.
+They are composed of characters as follows:
+
+@table @code
+@item @var{c}
+matches the character @var{c} (assuming @var{c} is a character with no
+special meaning in regexps).
+
+@item \@var{c}
+matches the literal character @var{c}.
+
+@item .
+matches any character except newline.
+
+@item ^
+matches the beginning of a line or a string.
+
+@item $
+matches the end of a line or a string.
+
+@item [@var{abc}@dots{}]
+matches any of the characters @var{abc}@dots{} (character class).
+
+@item [^@var{abc}@dots{}]
+matches any character except @var{abc}@dots{} and newline (negated
+character class).
+
+@item @var{r1}|@var{r2}
+matches either @var{r1} or @var{r2} (alternation).
+
+@item @var{r1r2}
+matches @var{r1}, and then @var{r2} (concatenation).
+
+@item @var{r}+
+matches one or more @var{r}'s.
+
+@item @var{r}*
+matches zero or more @var{r}'s.
+
+@item @var{r}?
+matches zero or one @var{r}'s.
+
+@item (@var{r})
+matches @var{r} (grouping).
+@end table
+
+@xref{Regexp, ,Regular Expressions as Patterns}, for a more detailed
+explanation of regular expressions.
+
+The escape sequences allowed in string constants are also valid in
+regular expressions (@pxref{Constants, ,Constant Expressions}).
+
+@node Actions Summary, , Regexp Summary, Rules Summary
+@appendixsubsec Actions
+
+Action statements are enclosed in braces, @samp{@{} and @samp{@}}.
+Action statements consist of the usual assignment, conditional, and looping
+statements found in most languages. The operators, control statements,
+and input/output statements available are patterned after those in C.
+
+@menu
+* Operator Summary:: @code{awk} operators.
+* Control Flow Summary:: The control statements.
+* I/O Summary:: The I/O statements.
+* Printf Summary:: A summary of @code{printf}.
+* Special File Summary:: Special file names interpreted internally.
+* Numeric Functions Summary:: Built-in numeric functions.
+* String Functions Summary:: Built-in string functions.
+* Time Functions Summary:: Built-in time functions.
+* String Constants Summary:: Escape sequences in strings.
+@end menu
+
+@node Operator Summary, Control Flow Summary, Actions Summary, Actions Summary
+@appendixsubsubsec Operators
+
+The operators in @code{awk}, in order of increasing precedence, are:
+
+@table @code
+@item = += -= *= /= %= ^=
+Assignment. Both absolute assignment (@code{@var{var}=@var{value}})
+and operator assignment (the other forms) are supported.
+
+@item ?:
+A conditional expression, as in C. This has the form @code{@var{expr1} ?
+@var{expr2} : @var{expr3}}. If @var{expr1} is true, the value of the
+expression is @var{expr2}; otherwise it is @var{expr3}. Only one of
+@var{expr2} and @var{expr3} is evaluated.@refill
+
+@item ||
+Logical ``or''.
+
+@item &&
+Logical ``and''.
+
+@item ~ !~
+Regular expression match, negated match.
+
+@item < <= > >= != ==
+The usual relational operators.
+
+@item @var{blank}
+String concatenation.
+
+@item + -
+Addition and subtraction.
+
+@item * / %
+Multiplication, division, and modulus.
+
+@item + - !
+Unary plus, unary minus, and logical negation.
+
+@item ^
+Exponentiation (@samp{**} may also be used, and @samp{**=} for the assignment
+operator, but they are not specified in the @sc{posix} standard).
+
+@item ++ --
+Increment and decrement, both prefix and postfix.
+
+@item $
+Field reference.
+@end table
+
+@xref{Expressions, ,Expressions as Action Statements}, for a full
+description of all the operators listed above.
+@xref{Fields, ,Examining Fields}, for a description of the field
+reference operator.@refill
+
+@node Control Flow Summary, I/O Summary, Operator Summary, Actions Summary
+@appendixsubsubsec Control Statements
+
+The control statements are as follows:
+
+@example
+if (@var{condition}) @var{statement} @r{[} else @var{statement} @r{]}
+while (@var{condition}) @var{statement}
+do @var{statement} while (@var{condition})
+for (@var{expr1}; @var{expr2}; @var{expr3}) @var{statement}
+for (@var{var} in @var{array}) @var{statement}
+break
+continue
+delete @var{array}[@var{index}]
+exit @r{[} @var{expression} @r{]}
+@{ @var{statements} @}
+@end example
+
+@xref{Statements, ,Control Statements in Actions}, for a full description
+of all the control statements listed above.
+
+@node I/O Summary, Printf Summary, Control Flow Summary, Actions Summary
+@appendixsubsubsec I/O Statements
+
+The input/output statements are as follows:
+
+@table @code
+@item getline
+Set @code{$0} from next input record; set @code{NF}, @code{NR}, @code{FNR}.
+
+@item getline <@var{file}
+Set @code{$0} from next record of @var{file}; set @code{NF}.
+
+@item getline @var{var}
+Set @var{var} from next input record; set @code{NF}, @code{FNR}.
+
+@item getline @var{var} <@var{file}
+Set @var{var} from next record of @var{file}.
+
+@item next
+Stop processing the current input record. The next input record is read and
+processing starts over with the first pattern in the @code{awk} program.
+If the end of the input data is reached, the @code{END} rule(s), if any,
+are executed.
+
+@item next file
+Stop processing the current input file. The next input record read comes
+from the next input file. @code{FILENAME} is updated, @code{FNR} is set to 1,
+and processing starts over with the first pattern in the @code{awk} program.
+If the end of the input data is reached, the @code{END} rule(s), if any,
+are executed.
+
+@item print
+Prints the current record.
+
+@item print @var{expr-list}
+Prints expressions.
+
+@item print @var{expr-list} > @var{file}
+Prints expressions on @var{file}.
+
+@item printf @var{fmt, expr-list}
+Format and print.
+
+@item printf @var{fmt, expr-list} > file
+Format and print on @var{file}.
+@end table
+
+Other input/output redirections are also allowed. For @code{print} and
+@code{printf}, @samp{>> @var{file}} appends output to the @var{file},
+and @samp{| @var{command}} writes on a pipe. In a similar fashion,
+@samp{@var{command} | getline} pipes input into @code{getline}.
+@code{getline} returns 0 on end of file, and @minus{}1 on an error.@refill
+
+@xref{Getline, ,Explicit Input with @code{getline}}, for a full description
+of the @code{getline} statement.
+@xref{Printing, ,Printing Output}, for a full description of @code{print} and
+@code{printf}. Finally, @pxref{Next Statement, ,The @code{next} Statement},
+for a description of how the @code{next} statement works.@refill
+
+@node Printf Summary, Special File Summary, I/O Summary, Actions Summary
+@appendixsubsubsec @code{printf} Summary
+
+The @code{awk} @code{printf} statement and @code{sprintf} function
+accept the following conversion specification formats:
+
+@table @code
+@item %c
+An ASCII character. If the argument used for @samp{%c} is numeric, it is
+treated as a character and printed. Otherwise, the argument is assumed to
+be a string, and the only first character of that string is printed.
+
+@item %d
+@itemx %i
+A decimal number (the integer part).
+
+@item %e
+A floating point number of the form
+@samp{@r{[}-@r{]}d.ddddddE@r{[}+-@r{]}dd}.@refill
+
+@item %f
+A floating point number of the form
+@r{[}@code{-}@r{]}@code{ddd.dddddd}.
+
+@item %g
+Use @samp{%e} or @samp{%f} conversion, whichever produces a shorter string,
+with nonsignificant zeros suppressed.
+
+@item %o
+An unsigned octal number (again, an integer).
+
+@item %s
+A character string.
+
+@item %x
+An unsigned hexadecimal number (an integer).
+
+@item %X
+Like @samp{%x}, except use @samp{A} through @samp{F} instead of @samp{a}
+through @samp{f} for decimal 10 through 15.@refill
+
+@item %%
+A single @samp{%} character; no argument is converted.
+@end table
+
+There are optional, additional parameters that may lie between the @samp{%}
+and the control letter:
+
+@table @code
+@item -
+The expression should be left-justified within its field.
+
+@item @var{width}
+The field should be padded to this width. If @var{width} has a leading zero,
+then the field is padded with zeros. Otherwise it is padded with blanks.
+
+@item .@var{prec}
+A number indicating the maximum width of strings or digits to the right
+of the decimal point.
+@end table
+
+Either or both of the @var{width} and @var{prec} values may be specified
+as @samp{*}. In that case, the particular value is taken from the argument
+list.
+
+@xref{Printf, ,Using @code{printf} Statements for Fancier Printing}, for
+examples and for a more detailed description.
+
+@node Special File Summary, Numeric Functions Summary, Printf Summary, Actions Summary
+@appendixsubsubsec Special File Names
+
+When doing I/O redirection from either @code{print} or @code{printf} into a
+file, or via @code{getline} from a file, @code{gawk} recognizes certain special
+file names internally. These file names allow access to open file descriptors
+inherited from @code{gawk}'s parent process (usually the shell). The
+file names are:
+
+@table @file
+@item /dev/stdin
+The standard input.
+
+@item /dev/stdout
+The standard output.
+
+@item /dev/stderr
+The standard error output.
+
+@item /dev/fd/@var{n}
+The file denoted by the open file descriptor @var{n}.
+@end table
+
+In addition the following files provide process related information
+about the running @code{gawk} program.
+
+@table @file
+@item /dev/pid
+Reading this file returns the process ID of the current process,
+in decimal, terminated with a newline.
+
+@item /dev/ppid
+Reading this file returns the parent process ID of the current process,
+in decimal, terminated with a newline.
+
+@item /dev/pgrpid
+Reading this file returns the process group ID of the current process,
+in decimal, terminated with a newline.
+
+@item /dev/user
+Reading this file returns a single record terminated with a newline.
+The fields are separated with blanks. The fields represent the
+following information:
+
+@table @code
+@item $1
+The value of the @code{getuid} system call.
+
+@item $2
+The value of the @code{geteuid} system call.
+
+@item $3
+The value of the @code{getgid} system call.
+
+@item $4
+The value of the @code{getegid} system call.
+@end table
+
+If there are any additional fields, they are the group IDs returned by
+@code{getgroups} system call.
+(Multiple groups may not be supported on all systems.)@refill
+@end table
+
+@noindent
+These file names may also be used on the command line to name data files.
+These file names are only recognized internally if you do not
+actually have files by these names on your system.
+
+@xref{Special Files, ,Standard I/O Streams}, for a longer description that
+provides the motivation for this feature.
+
+@node Numeric Functions Summary, String Functions Summary, Special File Summary, Actions Summary
+@appendixsubsubsec Numeric Functions
+
+@code{awk} has the following predefined arithmetic functions:
+
+@table @code
+@item atan2(@var{y}, @var{x})
+returns the arctangent of @var{y/x} in radians.
+
+@item cos(@var{expr})
+returns the cosine in radians.
+
+@item exp(@var{expr})
+the exponential function.
+
+@item int(@var{expr})
+truncates to integer.
+
+@item log(@var{expr})
+the natural logarithm function.
+
+@item rand()
+returns a random number between 0 and 1.
+
+@item sin(@var{expr})
+returns the sine in radians.
+
+@item sqrt(@var{expr})
+the square root function.
+
+@item srand(@var{expr})
+use @var{expr} as a new seed for the random number generator. If no @var{expr}
+is provided, the time of day is used. The return value is the previous
+seed for the random number generator.
+@end table
+
+@node String Functions Summary, Time Functions Summary, Numeric Functions Summary, Actions Summary
+@appendixsubsubsec String Functions
+
+@code{awk} has the following predefined string functions:
+
+@table @code
+@item gsub(@var{r}, @var{s}, @var{t})
+for each substring matching the regular expression @var{r} in the string
+@var{t}, substitute the string @var{s}, and return the number of substitutions.
+If @var{t} is not supplied, use @code{$0}.
+
+@item index(@var{s}, @var{t})
+returns the index of the string @var{t} in the string @var{s}, or 0 if
+@var{t} is not present.
+
+@item length(@var{s})
+returns the length of the string @var{s}. The length of @code{$0}
+is returned if no argument is supplied.
+
+@item match(@var{s}, @var{r})
+returns the position in @var{s} where the regular expression @var{r}
+occurs, or 0 if @var{r} is not present, and sets the values of @code{RSTART}
+and @code{RLENGTH}.
+
+@item split(@var{s}, @var{a}, @var{r})
+splits the string @var{s} into the array @var{a} on the regular expression
+@var{r}, and returns the number of fields. If @var{r} is omitted, @code{FS}
+is used instead.
+
+@item sprintf(@var{fmt}, @var{expr-list})
+prints @var{expr-list} according to @var{fmt}, and returns the resulting string.
+
+@item sub(@var{r}, @var{s}, @var{t})
+this is just like @code{gsub}, but only the first matching substring is
+replaced.
+
+@item substr(@var{s}, @var{i}, @var{n})
+returns the @var{n}-character substring of @var{s} starting at @var{i}.
+If @var{n} is omitted, the rest of @var{s} is used.
+
+@item tolower(@var{str})
+returns a copy of the string @var{str}, with all the upper-case characters in
+@var{str} translated to their corresponding lower-case counterparts.
+Nonalphabetic characters are left unchanged.
+
+@item toupper(@var{str})
+returns a copy of the string @var{str}, with all the lower-case characters in
+@var{str} translated to their corresponding upper-case counterparts.
+Nonalphabetic characters are left unchanged.
+
+@item system(@var{cmd-line})
+Execute the command @var{cmd-line}, and return the exit status.
+@end table
+
+@node Time Functions Summary, String Constants Summary, String Functions Summary, Actions Summary
+@appendixsubsubsec Built-in time functions
+
+The following two functions are available for getting the current
+time of day, and for formatting time stamps.
+
+@table @code
+@item systime()
+returns the current time of day as the number of seconds since a particular
+epoch (Midnight, January 1, 1970 @sc{utc}, on @sc{posix} systems).
+
+@item strftime(@var{format}, @var{timestamp})
+formats @var{timestamp} according to the specification in @var{format}.
+The current time of day is used if no @var{timestamp} is supplied.
+@xref{Time Functions, ,Functions for Dealing with Time Stamps}, for the
+details on the conversion specifiers that @code{strftime} accepts.@refill
+@end table
+
+@iftex
+@xref{Built-in, ,Built-in Functions}, for a description of all of
+@code{awk}'s built-in functions.
+@end iftex
+
+@node String Constants Summary, , Time Functions Summary, Actions Summary
+@appendixsubsubsec String Constants
+
+String constants in @code{awk} are sequences of characters enclosed
+between double quotes (@code{"}). Within strings, certain @dfn{escape sequences}
+are recognized, as in C. These are:
+
+@table @code
+@item \\
+A literal backslash.
+
+@item \a
+The ``alert'' character; usually the ASCII BEL character.
+
+@item \b
+Backspace.
+
+@item \f
+Formfeed.
+
+@item \n
+Newline.
+
+@item \r
+Carriage return.
+
+@item \t
+Horizontal tab.
+
+@item \v
+Vertical tab.
+
+@item \x@var{hex digits}
+The character represented by the string of hexadecimal digits following
+the @samp{\x}. As in @sc{ansi} C, all following hexadecimal digits are
+considered part of the escape sequence. (This feature should tell us
+something about language design by committee.) E.g., @code{"\x1B"} is a
+string containing the ASCII ESC (escape) character. (The @samp{\x}
+escape sequence is not in @sc{posix} @code{awk}.)
+
+@item \@var{ddd}
+The character represented by the 1-, 2-, or 3-digit sequence of octal
+digits. Thus, @code{"\033"} is also a string containing the ASCII ESC
+(escape) character.
+
+@item \@var{c}
+The literal character @var{c}.
+@end table
+
+The escape sequences may also be used inside constant regular expressions
+(e.g., the regexp @code{@w{/[@ \t\f\n\r\v]/}} matches whitespace
+characters).@refill
+
+@xref{Constants, ,Constant Expressions}.
+
+@node Functions Summary, Historical Features, Rules Summary, Gawk Summary
+@appendixsec Functions
+
+Functions in @code{awk} are defined as follows:
+
+@example
+function @var{name}(@var{parameter list}) @{ @var{statements} @}
+@end example
+
+Actual parameters supplied in the function call are used to instantiate
+the formal parameters declared in the function. Arrays are passed by
+reference, other variables are passed by value.
+
+If there are fewer arguments passed than there are names in @var{parameter-list},
+the extra names are given the null string as value. Extra names have the
+effect of local variables.
+
+The open-parenthesis in a function call of a user-defined function must
+immediately follow the function name, without any intervening white space.
+This is to avoid a syntactic ambiguity with the concatenation operator.
+
+The word @code{func} may be used in place of @code{function} (but not in
+@sc{posix} @code{awk}).
+
+Use the @code{return} statement to return a value from a function.
+
+@xref{User-defined, ,User-defined Functions}, for a more complete description.
+
+@node Historical Features, , Functions Summary, Gawk Summary
+@appendixsec Historical Features
+
+There are two features of historical @code{awk} implementations that
+@code{gawk} supports. First, it is possible to call the @code{length}
+built-in function not only with no arguments, but even without parentheses!
+
+@example
+a = length
+@end example
+
+@noindent
+is the same as either of
+
+@example
+a = length()
+a = length($0)
+@end example
+
+@noindent
+This feature is marked as ``deprecated'' in the @sc{posix} standard, and
+@code{gawk} will issue a warning about its use if @samp{-W lint} is
+specified on the command line.
+
+The other feature is the use of the @code{continue} statement outside the
+body of a @code{while}, @code{for}, or @code{do} loop. Traditional
+@code{awk} implementations have treated such usage as equivalent to the
+@code{next} statement. @code{gawk} will support this usage if @samp{-W posix}
+has not been specified.
+
+@node Sample Program, Bugs, Gawk Summary, Top
+@appendix Sample Program
+
+The following example is a complete @code{awk} program, which prints
+the number of occurrences of each word in its input. It illustrates the
+associative nature of @code{awk} arrays by using strings as subscripts. It
+also demonstrates the @samp{for @var{x} in @var{array}} construction.
+Finally, it shows how @code{awk} can be used in conjunction with other
+utility programs to do a useful task of some complexity with a minimum of
+effort. Some explanations follow the program listing.@refill
+
+@example
+awk '
+# Print list of word frequencies
+@{
+ for (i = 1; i <= NF; i++)
+ freq[$i]++
+@}
+
+END @{
+ for (word in freq)
+ printf "%s\t%d\n", word, freq[word]
+@}'
+@end example
+
+The first thing to notice about this program is that it has two rules. The
+first rule, because it has an empty pattern, is executed on every line of
+the input. It uses @code{awk}'s field-accessing mechanism
+(@pxref{Fields, ,Examining Fields}) to pick out the individual words from
+the line, and the built-in variable @code{NF} (@pxref{Built-in Variables})
+to know how many fields are available.@refill
+
+For each input word, an element of the array @code{freq} is incremented to
+reflect that the word has been seen an additional time.@refill
+
+The second rule, because it has the pattern @code{END}, is not executed
+until the input has been exhausted. It prints out the contents of the
+@code{freq} table that has been built up inside the first action.@refill
+
+Note that this program has several problems that would prevent it from being
+useful by itself on real text files:@refill
+
+@itemize @bullet
+@item
+Words are detected using the @code{awk} convention that fields are
+separated by whitespace and that other characters in the input (except
+newlines) don't have any special meaning to @code{awk}. This means that
+punctuation characters count as part of words.@refill
+
+@item
+The @code{awk} language considers upper and lower case characters to be
+distinct. Therefore, @samp{foo} and @samp{Foo} are not treated by this
+program as the same word. This is undesirable since in normal text, words
+are capitalized if they begin sentences, and a frequency analyzer should not
+be sensitive to that.@refill
+
+@item
+The output does not come out in any useful order. You're more likely to be
+interested in which words occur most frequently, or having an alphabetized
+table of how frequently each word occurs.@refill
+@end itemize
+
+The way to solve these problems is to use some of the more advanced
+features of the @code{awk} language. First, we use @code{tolower} to remove
+case distinctions. Next, we use @code{gsub} to remove punctuation
+characters. Finally, we use the system @code{sort} utility to process the
+output of the @code{awk} script. First, here is the new version of
+the program:@refill
+
+@example
+awk '
+# Print list of word frequencies
+@{
+ $0 = tolower($0) # remove case distinctions
+ gsub(/[^a-z0-9_ \t]/, "", $0) # remove punctuation
+ for (i = 1; i <= NF; i++)
+ freq[$i]++
+@}
+
+END @{
+ for (word in freq)
+ printf "%s\t%d\n", word, freq[word]
+@}'
+@end example
+
+Assuming we have saved this program in a file named @file{frequency.awk},
+and that the data is in @file{file1}, the following pipeline
+
+@example
+awk -f frequency.awk file1 | sort +1 -nr
+@end example
+
+@noindent
+produces a table of the words appearing in @file{file1} in order of
+decreasing frequency.
+
+The @code{awk} program suitably massages the data and produces a word
+frequency table, which is not ordered.
+
+The @code{awk} script's output is then sorted by the @code{sort} command and
+printed on the terminal. The options given to @code{sort} in this example
+specify to sort using the second field of each input line (skipping one field),
+that the sort keys should be treated as numeric quantities (otherwise
+@samp{15} would come before @samp{5}), and that the sorting should be done
+in descending (reverse) order.@refill
+
+We could have even done the @code{sort} from within the program, by
+changing the @code{END} action to:
+
+@example
+END @{
+ sort = "sort +1 -nr"
+ for (word in freq)
+ printf "%s\t%d\n", word, freq[word] | sort
+ close(sort)
+@}'
+@end example
+
+See the general operating system documentation for more information on how
+to use the @code{sort} command.@refill
+
+@ignore
+@strong{ADR: I have some more substantial programs courtesy of Rick Adams
+at UUNET. I am planning on incorporating those either in addition to or
+instead of this program.}
+
+@strong{I would also like to incorporate the general @code{translate}
+function that I have written.}
+
+@strong{I have a ton of other sample programs to include too.}
+@end ignore
+
+@node Bugs, Notes, Sample Program, Top
+@appendix Reporting Problems and Bugs
+
+@c This chapter stolen shamelessly from the GNU m4 manual.
+@c This chapter has been unshamelessly altered to emulate changes made to
+@c make.texi from whence it was originally shamelessly stolen! :-} --mew
+
+If you have problems with @code{gawk} or think that you have found a bug,
+please report it to the developers; we cannot promise to do anything
+but we might well want to fix it.
+
+Before reporting a bug, make sure you have actually found a real bug.
+Carefully reread the documentation and see if it really says you can do
+what you're trying to do. If it's not clear whether you should be able
+to do something or not, report that too; it's a bug in the documentation!
+
+Before reporting a bug or trying to fix it yourself, try to isolate it
+to the smallest possible @code{awk} program and input data file that
+reproduces the problem. Then send us the program and data file,
+some idea of what kind of Unix system you're using, and the exact results
+@code{gawk} gave you. Also say what you expected to occur; this will help
+us decide whether the problem was really in the documentation.
+
+Once you have a precise problem, send e-mail to (Internet)
+@samp{bug-gnu-utils@@prep.ai.mit.edu} or (UUCP)
+@samp{mit-eddie!prep.ai.mit.edu!bug-gnu-utils}. Please include the
+version number of @code{gawk} you are using. You can get this information
+with the command @samp{gawk -W version '@{@}' /dev/null}.
+You should send carbon copies of your mail to David Trueman at
+@samp{david@@cs.dal.ca}, and to Arnold Robbins, who can be reached at
+@samp{arnold@@skeeve.atl.ga.us}. David is most likely to fix code
+problems, while Arnold is most likely to fix documentation problems.@refill
+
+Non-bug suggestions are always welcome as well. If you have questions
+about things that are unclear in the documentation or are just obscure
+features, ask Arnold Robbins; he will try to help you out, although he
+may not have the time to fix the problem. You can send him electronic mail at the Internet address
+above.
+
+If you find bugs in one of the non-Unix ports of @code{gawk}, please send
+an electronic mail message to the person who maintains that port. They
+are listed below, and also in the @file{README} file in the @code{gawk}
+distribution. Information in the @code{README} file should be considered
+authoritative if it conflicts with this manual.
+
+The people maintaining the non-Unix ports of @code{gawk} are:
+
+@table @asis
+@item MS-DOS
+The port to MS-DOS is maintained by Scott Deifik.
+His electronic mail address is @samp{scottd@@amgen.com}.
+
+@item VMS
+The port to VAX VMS is maintained by Pat Rankin.
+His electronic mail address is @samp{rankin@@eql.caltech.edu}.
+
+@item Atari ST
+The port to the Atari ST is maintained by Michal Jaegermann.
+His electronic mail address is @samp{ntomczak@@vm.ucs.ualberta.ca}.
+
+@end table
+
+If your bug is also reproducible under Unix, please send copies of your
+report to the general GNU bug list, as well as to Arnold Robbins and David
+Trueman, at the addresses listed above.
+
+@node Notes, Glossary, Bugs, Top
+@appendix Implementation Notes
+
+This appendix contains information mainly of interest to implementors and
+maintainers of @code{gawk}. Everything in it applies specifically to
+@code{gawk}, and not to other implementations.
+
+@menu
+* Compatibility Mode:: How to disable certain @code{gawk} extensions.
+* Future Extensions:: New features we may implement soon.
+* Improvements:: Suggestions for improvements by volunteers.
+@end menu
+
+@node Compatibility Mode, Future Extensions, Notes, Notes
+@appendixsec Downward Compatibility and Debugging
+
+@xref{POSIX/GNU, ,Extensions in @code{gawk} not in POSIX @code{awk}},
+for a summary of the GNU extensions to the @code{awk} language and program.
+All of these features can be turned off by invoking @code{gawk} with the
+@samp{-W compat} option, or with the @samp{-W posix} option.@refill
+
+If @code{gawk} is compiled for debugging with @samp{-DDEBUG}, then there
+is one more option available on the command line:
+
+@table @samp
+@item -W parsedebug
+Print out the parse stack information as the program is being parsed.
+@end table
+
+This option is intended only for serious @code{gawk} developers,
+and not for the casual user. It probably has not even been compiled into
+your version of @code{gawk}, since it slows down execution.
+
+@node Future Extensions, Improvements, Compatibility Mode, Notes
+@appendixsec Probable Future Extensions
+
+This section briefly lists extensions that indicate the directions we are
+currently considering for @code{gawk}. The file @file{FUTURES} in the
+@code{gawk} distributions lists these extensions, as well as several others.
+
+@table @asis
+@item @code{RS} as a regexp
+The meaning of @code{RS} may be generalized along the lines of @code{FS}.
+
+@item Control of subprocess environment
+Changes made in @code{gawk} to the array @code{ENVIRON} may be
+propagated to subprocesses run by @code{gawk}.
+
+@item Databases
+It may be possible to map a GDBM/NDBM/SDBM file into an @code{awk} array.
+
+@item Single-character fields
+The null string, @code{""}, as a field separator, will cause field
+splitting and the @code{split} function to separate individual characters.
+Thus, @code{split(a, "abcd", "")} would yield @code{a[1] == "a"},
+@code{a[2] == "b"}, and so on.
+
+@item More @code{lint} warnings
+There are more things that could be checked for portability.
+
+@item @code{RECLEN} variable for fixed length records
+Along with @code{FIELDWIDTHS}, this would speed up the processing of
+fixed-length records.
+
+@item @code{RT} variable to hold the record terminator
+It is occasionally useful to have access to the actual string of
+characters that matched the @code{RS} variable. The @code{RT}
+variable would hold these characters.
+
+@item A @code{restart} keyword
+After modifying @code{$0}, @code{restart} would restart the pattern
+matching loop, without reading a new record from the input.
+
+@item A @samp{|&} redirection
+The @samp{|&} redirection, in place of @samp{|}, would open a two-way
+pipeline for communication with a sub-process (via @code{getline} and
+@code{print} and @code{printf}).
+
+@item @code{IGNORECASE} affecting all comparisons
+The effects of the @code{IGNORECASE} variable may be generalized to
+all string comparisons, and not just regular expression operations.
+
+@item A way to mix command line source code and library files
+There may be a new option that would make it possible to easily use library
+functions from a program entered on the command line.
+@c probably a @samp{-s} option...
+
+@item GNU-style long options
+We will add GNU-style long options
+to @code{gawk} for compatibility with other GNU programs.
+(For example, @samp{--field-separator=:} would be equivalent to
+@samp{-F:}.)@refill
+
+@c this is @emph{very} long term --- not worth including right now.
+@ignore
+@item The C Comma Operator
+We may add the C comma operator, which takes the form
+@code{@var{expr1},@var{expr2}}. The first expression is evaluated, and the
+result is thrown away. The value of the full expression is the value of
+@var{expr2}.@refill
+@end ignore
+@end table
+
+@node Improvements, , Future Extensions, Notes
+@appendixsec Suggestions for Improvements
+
+Here are some projects that would-be @code{gawk} hackers might like to take
+on. They vary in size from a few days to a few weeks of programming,
+depending on which one you choose and how fast a programmer you are. Please
+send any improvements you write to the maintainers at the GNU
+project.@refill
+
+@enumerate
+@item
+Compilation of @code{awk} programs: @code{gawk} uses a Bison (YACC-like)
+parser to convert the script given it into a syntax tree; the syntax
+tree is then executed by a simple recursive evaluator. This method incurs
+a lot of overhead, since the recursive evaluator performs many procedure
+calls to do even the simplest things.@refill
+
+It should be possible for @code{gawk} to convert the script's parse tree
+into a C program which the user would then compile, using the normal
+C compiler and a special @code{gawk} library to provide all the needed
+functions (regexps, fields, associative arrays, type coercion, and so
+on).@refill
+
+An easier possibility might be for an intermediate phase of @code{awk} to
+convert the parse tree into a linear byte code form like the one used
+in GNU Emacs Lisp. The recursive evaluator would then be replaced by
+a straight line byte code interpreter that would be intermediate in speed
+between running a compiled program and doing what @code{gawk} does
+now.@refill
+
+This may actually happen for the 3.0 version of @code{gawk}.
+
+@item
+An error message section has not been included in this version of the
+manual. Perhaps some nice beta testers will document some of the messages
+for the future.
+
+@item
+The programs in the test suite could use documenting in this manual.
+
+@item
+The programs and data files in the manual should be available in
+separate files to facilitate experimentation.
+
+@item
+See the @file{FUTURES} file for more ideas. Contact us if you would
+seriously like to tackle any of the items listed there.
+@end enumerate
+
+@node Glossary, Index, Notes, Top
+@appendix Glossary
+
+@table @asis
+@item Action
+A series of @code{awk} statements attached to a rule. If the rule's
+pattern matches an input record, the @code{awk} language executes the
+rule's action. Actions are always enclosed in curly braces.
+@xref{Actions, ,Overview of Actions}.@refill
+
+@item Amazing @code{awk} Assembler
+Henry Spencer at the University of Toronto wrote a retargetable assembler
+completely as @code{awk} scripts. It is thousands of lines long, including
+machine descriptions for several 8-bit microcomputers.
+@c It is distributed with @code{gawk} (as part of the test suite) and
+It is a good example of a
+program that would have been better written in another language.@refill
+
+@item @sc{ansi}
+The American National Standards Institute. This organization produces
+many standards, among them the standard for the C programming language.
+
+@item Assignment
+An @code{awk} expression that changes the value of some @code{awk}
+variable or data object. An object that you can assign to is called an
+@dfn{lvalue}. @xref{Assignment Ops, ,Assignment Expressions}.@refill
+
+@item @code{awk} Language
+The language in which @code{awk} programs are written.
+
+@item @code{awk} Program
+An @code{awk} program consists of a series of @dfn{patterns} and
+@dfn{actions}, collectively known as @dfn{rules}. For each input record
+given to the program, the program's rules are all processed in turn.
+@code{awk} programs may also contain function definitions.@refill
+
+@item @code{awk} Script
+Another name for an @code{awk} program.
+
+@item Built-in Function
+The @code{awk} language provides built-in functions that perform various
+numerical, time stamp related, and string computations. Examples are
+@code{sqrt} (for the square root of a number) and @code{substr} (for a
+substring of a string). @xref{Built-in, ,Built-in Functions}.@refill
+
+@item Built-in Variable
+@code{ARGC}, @code{ARGIND}, @code{ARGV}, @code{CONVFMT}, @code{ENVIRON},
+@code{ERRNO}, @code{FIELDWIDTHS}, @code{FILENAME}, @code{FNR}, @code{FS},
+@code{IGNORECASE}, @code{NF}, @code{NR}, @code{OFMT}, @code{OFS}, @code{ORS},
+@code{RLENGTH}, @code{RSTART}, @code{RS}, and @code{SUBSEP},
+are the variables that have special
+meaning to @code{awk}. Changing some of them affects @code{awk}'s running
+environment. @xref{Built-in Variables}.@refill
+
+@item Braces
+See ``Curly Braces.''
+
+@item C
+The system programming language that most GNU software is written in. The
+@code{awk} programming language has C-like syntax, and this manual
+points out similarities between @code{awk} and C when appropriate.@refill
+
+@item CHEM
+A preprocessor for @code{pic} that reads descriptions of molecules
+and produces @code{pic} input for drawing them. It was written by
+Brian Kernighan, and is available from @code{netlib@@research.att.com}.@refill
+
+@item Compound Statement
+A series of @code{awk} statements, enclosed in curly braces. Compound
+statements may be nested.
+@xref{Statements, ,Control Statements in Actions}.@refill
+
+@item Concatenation
+Concatenating two strings means sticking them together, one after another,
+giving a new string. For example, the string @samp{foo} concatenated with
+the string @samp{bar} gives the string @samp{foobar}.
+@xref{Concatenation, ,String Concatenation}.@refill
+
+@item Conditional Expression
+An expression using the @samp{?:} ternary operator, such as
+@code{@var{expr1} ? @var{expr2} : @var{expr3}}. The expression
+@var{expr1} is evaluated; if the result is true, the value of the whole
+expression is the value of @var{expr2} otherwise the value is
+@var{expr3}. In either case, only one of @var{expr2} and @var{expr3}
+is evaluated. @xref{Conditional Exp, ,Conditional Expressions}.@refill
+
+@item Constant Regular Expression
+A constant regular expression is a regular expression written within
+slashes, such as @samp{/foo/}. This regular expression is chosen
+when you write the @code{awk} program, and cannot be changed doing
+its execution. @xref{Regexp Usage, ,How to Use Regular Expressions}.
+
+@item Comparison Expression
+A relation that is either true or false, such as @code{(a < b)}.
+Comparison expressions are used in @code{if}, @code{while}, and @code{for}
+statements, and in patterns to select which input records to process.
+@xref{Comparison Ops, ,Comparison Expressions}.@refill
+
+@item Curly Braces
+The characters @samp{@{} and @samp{@}}. Curly braces are used in
+@code{awk} for delimiting actions, compound statements, and function
+bodies.@refill
+
+@item Data Objects
+These are numbers and strings of characters. Numbers are converted into
+strings and vice versa, as needed.
+@xref{Conversion, ,Conversion of Strings and Numbers}.@refill
+
+@item Dynamic Regular Expression
+A dynamic regular expression is a regular expression written as an
+ordinary expression. It could be a string constant, such as
+@code{"foo"}, but it may also be an expression whose value may vary.
+@xref{Regexp Usage, ,How to Use Regular Expressions}.
+
+@item Escape Sequences
+A special sequence of characters used for describing nonprinting
+characters, such as @samp{\n} for newline, or @samp{\033} for the ASCII
+ESC (escape) character. @xref{Constants, ,Constant Expressions}.
+
+@item Field
+When @code{awk} reads an input record, it splits the record into pieces
+separated by whitespace (or by a separator regexp which you can
+change by setting the built-in variable @code{FS}). Such pieces are
+called fields. If the pieces are of fixed length, you can use the built-in
+variable @code{FIELDWIDTHS} to describe their lengths.
+@xref{Records, ,How Input is Split into Records}.@refill
+
+@item Format
+Format strings are used to control the appearance of output in the
+@code{printf} statement. Also, data conversions from numbers to strings
+are controlled by the format string contained in the built-in variable
+@code{CONVFMT}. @xref{Control Letters, ,Format-Control Letters}.@refill
+
+@item Function
+A specialized group of statements often used to encapsulate general
+or program-specific tasks. @code{awk} has a number of built-in
+functions, and also allows you to define your own.
+@xref{Built-in, ,Built-in Functions}.
+Also, see @ref{User-defined, ,User-defined Functions}.@refill
+
+@item @code{gawk}
+The GNU implementation of @code{awk}.
+
+@item GNU
+``GNU's not Unix''. An on-going project of the Free Software Foundation
+to create a complete, freely distributable, @sc{posix}-compliant computing
+environment.
+
+@item Input Record
+A single chunk of data read in by @code{awk}. Usually, an @code{awk} input
+record consists of one line of text.
+@xref{Records, ,How Input is Split into Records}.@refill
+
+@item Keyword
+In the @code{awk} language, a keyword is a word that has special
+meaning. Keywords are reserved and may not be used as variable names.
+
+@code{awk}'s keywords are:
+@code{if},
+@code{else},
+@code{while},
+@code{do@dots{}while},
+@code{for},
+@code{for@dots{}in},
+@code{break},
+@code{continue},
+@code{delete},
+@code{next},
+@code{function},
+@code{func},
+and @code{exit}.@refill
+
+@item Lvalue
+An expression that can appear on the left side of an assignment
+operator. In most languages, lvalues can be variables or array
+elements. In @code{awk}, a field designator can also be used as an
+lvalue.@refill
+
+@item Number
+A numeric valued data object. The @code{gawk} implementation uses double
+precision floating point to represent numbers.@refill
+
+@item Pattern
+Patterns tell @code{awk} which input records are interesting to which
+rules.
+
+A pattern is an arbitrary conditional expression against which input is
+tested. If the condition is satisfied, the pattern is said to @dfn{match}
+the input record. A typical pattern might compare the input record against
+a regular expression. @xref{Patterns}.@refill
+
+@item @sc{posix}
+The name for a series of standards being developed by the @sc{ieee}
+that specify a Portable Operating System interface. The ``IX'' denotes
+the Unix heritage of these standards. The main standard of interest for
+@code{awk} users is P1003.2, the Command Language and Utilities standard.
+
+@item Range (of input lines)
+A sequence of consecutive lines from the input file. A pattern
+can specify ranges of input lines for @code{awk} to process, or it can
+specify single lines. @xref{Patterns}.@refill
+
+@item Recursion
+When a function calls itself, either directly or indirectly.
+If this isn't clear, refer to the entry for ``recursion.''
+
+@item Redirection
+Redirection means performing input from other than the standard input
+stream, or output to other than the standard output stream.
+
+You can redirect the output of the @code{print} and @code{printf} statements
+to a file or a system command, using the @samp{>}, @samp{>>}, and @samp{|}
+operators. You can redirect input to the @code{getline} statement using
+the @samp{<} and @samp{|} operators.
+@xref{Redirection, ,Redirecting Output of @code{print} and @code{printf}}.@refill
+
+@item Regular Expression
+See ``regexp.''
+
+@item Regexp
+Short for @dfn{regular expression}. A regexp is a pattern that denotes a
+set of strings, possibly an infinite set. For example, the regexp
+@samp{R.*xp} matches any string starting with the letter @samp{R}
+and ending with the letters @samp{xp}. In @code{awk}, regexps are
+used in patterns and in conditional expressions. Regexps may contain
+escape sequences. @xref{Regexp, ,Regular Expressions as Patterns}.@refill
+
+@item Rule
+A segment of an @code{awk} program, that specifies how to process single
+input records. A rule consists of a @dfn{pattern} and an @dfn{action}.
+@code{awk} reads an input record; then, for each rule, if the input record
+satisfies the rule's pattern, @code{awk} executes the rule's action.
+Otherwise, the rule does nothing for that input record.@refill
+
+@item Side Effect
+A side effect occurs when an expression has an effect aside from merely
+producing a value. Assignment expressions, increment expressions and
+function calls have side effects. @xref{Assignment Ops, ,Assignment Expressions}.
+
+@item Special File
+A file name interpreted internally by @code{gawk}, instead of being handed
+directly to the underlying operating system. For example, @file{/dev/stdin}.
+@xref{Special Files, ,Standard I/O Streams}.
+
+@item Stream Editor
+A program that reads records from an input stream and processes them one
+or more at a time. This is in contrast with batch programs, which may
+expect to read their input files in entirety before starting to do
+anything, and with interactive programs, which require input from the
+user.@refill
+
+@item String
+A datum consisting of a sequence of characters, such as @samp{I am a
+string}. Constant strings are written with double-quotes in the
+@code{awk} language, and may contain escape sequences.
+@xref{Constants, ,Constant Expressions}.
+
+@item Whitespace
+A sequence of blank or tab characters occurring inside an input record or a
+string.@refill
+@end table
+
+@node Index, , Glossary, Top
+@unnumbered Index
+@printindex cp
+
+@summarycontents
+@contents
+@bye
+
+Unresolved Issues:
+------------------
+1. From: ntomczak@vm.ucs.ualberta.ca (Michal Jaegermann)
+ Examples of usage tend to suggest that /../ and ".." delimiters
+ can be used for regular expressions, even if definition is consistently
+ using /../. I am not sure what the real rules are and in particular
+ what of the following is a bug and what is a feature:
+ # This program matches everything
+ '"\(" { print }'
+ # This one complains about mismatched parenthesis
+ '$0 ~ "\(" { print }'
+ # This one behaves in an expected manner
+ '/\(/ { print }'
+ You may also try to use "\(" as an argument to match() to see what
+ will happen.
+
+2. From ADR.
+
+ The posix (and original Unix!) notion of awk values as both number
+ and string values needs to be put into the manual. This involves
+ major and minor rewrites of most of the manual, but should help in
+ clarifying many of the weirder points of the language.
+
+3. From ADR.
+
+ The manual should be reorganized. Expressions should be introduced
+ early, building up to regexps as expressions, and from there to their
+ use as patterns and then in actions. Built-in vars should come earlier
+ in the manual too. The 'expert info' sections marked with comments
+ should get their own sections or subsections with nodes and titles.
+ The manual should be gone over thoroughly for indexing.
+
+4. From ADR.
+
+ Robert J. Chassell points out that awk programs should have some indication
+ of how to use them. It would be useful to perhaps have a "programming
+ style" section of the manual that would include this and other tips.
+
+5. From ADR in response to moraes@uunet.ca
+ (This would make the beginnings of a good "puzzles" section...)
+
+ Date: Mon, 2 Dec 91 10:08:05 EST
+ From: gatech!cc!arnold (Arnold Robbins)
+ To: cs.dal.ca!david, uunet.ca!moraes
+ Subject: redirecting to /dev/stderr
+ Cc: skeeve!arnold, boeing.com!brennan, research.att.com!bwk
+
+ In 2.13.3 the following program no longer dumps core:
+
+ BEGIN { print "hello" > /dev/stderr ; exit(1) }
+
+ Instead, it creates a file named `0' with the word `hello' in it. AWK
+ semantics strikes again. The meaning of the statement is
+
+ print "hello" > (($0 ~ /dev/) stderr)
+
+ /dev/ tests $0 for the pattern `dev'. This yields a 0. The variable stderr,
+ having never been used, has a null string in it. The concatenation yields
+ a string value of "0" which is used as the file name. Sigh.
+
+ I think with some more time I can come up with a decent fix, but it will
+ probably only print a diagnostic with -Wlint.
+
+ Arnold
+
diff --git a/gnu/usr.bin/awk/getopt.c b/gnu/usr.bin/awk/getopt.c
new file mode 100644
index 0000000..fd142f5
--- /dev/null
+++ b/gnu/usr.bin/awk/getopt.c
@@ -0,0 +1,757 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 1994
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef HAVE_CONFIG_H
+#if defined (emacs) || defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
+#ifndef __STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#if defined(__GNU_LIBRARY__) || defined(STDC_HEADERS)
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+#include <stdlib.h>
+#else
+extern char *getenv ();
+#endif /* __GNU_LIBRARY || STDC_HEADERS */
+
+/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
+ long-named option. Because this is not POSIX.2 compliant, it is
+ being phased out. */
+/* #define GETOPT_COMPAT */
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = 0;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* XXX 1003.2 says this must be 1 before any call. */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return EOF with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+#if defined(__GNU_LIBRARY__) || defined(STDC_HEADERS)
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+#include <string.h>
+#define my_index strchr
+#else
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+static char *
+my_index (str, chr)
+ const char *str;
+ int chr;
+{
+ while (*str)
+ {
+ if (*str == chr)
+ return (char *) str;
+ str++;
+ }
+ return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+ If not using GCC, it is ok not to declare it.
+ (Supposedly there are some machines where it might get a warning,
+ but changing this conditional to __STDC__ is too risky.) */
+#ifdef __GNUC__
+#ifdef IN_GCC
+#include "gstddef.h"
+#else
+#include <stddef.h>
+#endif
+extern size_t strlen (const char *);
+#endif
+
+#endif /* __GNU_LIBRARY__ || STDC_HEADERS */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int bottom = first_nonopt;
+ int middle = last_nonopt;
+ int top = optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
+
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns `EOF'.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ int option_index;
+
+ optarg = 0;
+
+ /* Initialize the internal data when the first call is made.
+ Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ if (optind == 0)
+ {
+ first_nonopt = last_nonopt = optind = 1;
+
+ nextchar = NULL;
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (getenv ("POSIXLY_CORRECT") != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+ }
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Now skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc
+ && (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* Special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return EOF;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if ((argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ {
+ if (ordering == REQUIRE_ORDER)
+ return EOF;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Start decoding its characters. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ if (longopts != NULL
+ && ((argv[optind][0] == '-'
+ && (argv[optind][1] == '-' || long_only))
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ ))
+ {
+ const struct option *p;
+ char *s = nextchar;
+ int exact = 0;
+ int ambig = 0;
+ const struct option *pfound = NULL;
+ int indfound;
+
+ while (*s && *s != '=')
+ s++;
+
+ /* Test all options for either exact match or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name;
+ p++, option_index++)
+ if (!strncmp (p->name, nextchar, s - nextchar))
+ {
+ if (s - nextchar == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' is ambiguous\n",
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*s)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = s + 1;
+ else
+ {
+ if (opterr)
+ {
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ "%s: option `--%s' doesn't allow an argument\n",
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ "%s: option `%c%s' doesn't allow an argument\n",
+ argv[0], argv[optind - 1][0], pfound->name);
+ }
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' requires an argument\n",
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, "%s: unrecognized option `--%s'\n",
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+#if 0
+ if (c < 040 || c >= 0177)
+ fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
+ argv[0], c);
+ else
+ fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
+#endif
+ }
+ optopt = c;
+ return '?';
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = 0;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+#if 0
+ fprintf (stderr, "%s: option `-%c' requires an argument\n",
+ argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: option requires an argument -- %c\n",
+ argv[0], c);
+#endif
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/gnu/usr.bin/awk/getopt.h b/gnu/usr.bin/awk/getopt.h
new file mode 100644
index 0000000..b0fc4ff
--- /dev/null
+++ b/gnu/usr.bin/awk/getopt.h
@@ -0,0 +1,129 @@
+/* Declarations for getopt.
+ Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized. */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+#ifdef __STDC__
+ const char *name;
+#else
+ char *name;
+#endif
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+#ifdef __STDC__
+#if defined(__GNU_LIBRARY__)
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* not __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind);
+
+/* Internal only. Users should not call this directly. */
+extern int _getopt_internal (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* not __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/gnu/usr.bin/awk/getopt1.c b/gnu/usr.bin/awk/getopt1.c
new file mode 100644
index 0000000..7739b51
--- /dev/null
+++ b/gnu/usr.bin/awk/getopt1.c
@@ -0,0 +1,187 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef HAVE_CONFIG_H
+#if defined (emacs) || defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
+#include "getopt.h"
+
+#ifndef __STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#if defined(__GNU_LIBRARY__) || defined(OS2) || defined(MSDOS) || defined(atarist)
+#include <stdlib.h>
+#else
+char *getenv ();
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/gnu/usr.bin/awk/io.c b/gnu/usr.bin/awk/io.c
new file mode 100644
index 0000000..7fe21ec
--- /dev/null
+++ b/gnu/usr.bin/awk/io.c
@@ -0,0 +1,1282 @@
+/*
+ * io.c --- routines for dealing with input and output and records
+ */
+
+/*
+ * Copyright (C) 1986, 1988, 1989, 1991, 1992, 1993 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Progamming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GAWK; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#if !defined(VMS) && !defined(VMS_POSIX) && !defined(_MSC_VER)
+#include <sys/param.h>
+#endif
+#include "awk.h"
+
+#ifndef O_RDONLY
+#include <fcntl.h>
+#endif
+
+#if !defined(S_ISDIR) && defined(S_IFDIR)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+
+#ifndef ENFILE
+#define ENFILE EMFILE
+#endif
+
+#ifndef atarist
+#define INVALID_HANDLE (-1)
+#else
+#define INVALID_HANDLE (__SMALLEST_VALID_HANDLE - 1)
+#endif
+
+#if defined(MSDOS) || defined(OS2) || defined(atarist)
+#define PIPES_SIMULATED
+#endif
+
+static IOBUF *nextfile P((int skipping));
+static int inrec P((IOBUF *iop));
+static int iop_close P((IOBUF *iop));
+struct redirect *redirect P((NODE *tree, int *errflg));
+static void close_one P((void));
+static int close_redir P((struct redirect *rp, int exitwarn));
+#ifndef PIPES_SIMULATED
+static int wait_any P((int interesting));
+#endif
+static IOBUF *gawk_popen P((char *cmd, struct redirect *rp));
+static IOBUF *iop_open P((const char *file, const char *how));
+static int gawk_pclose P((struct redirect *rp));
+static int do_pathopen P((const char *file));
+static int str2mode P((const char *mode));
+static void spec_setup P((IOBUF *iop, int len, int allocate));
+static int specfdopen P((IOBUF *iop, const char *name, const char *mode));
+static int pidopen P((IOBUF *iop, const char *name, const char *mode));
+static int useropen P((IOBUF *iop, const char *name, const char *mode));
+
+extern FILE *fdopen();
+
+#if defined (MSDOS)
+#include "popen.h"
+#define popen(c,m) os_popen(c,m)
+#define pclose(f) os_pclose(f)
+#elif defined (OS2) /* OS/2, but not family mode */
+#if defined (_MSC_VER)
+#define popen(c,m) _popen(c,m)
+#define pclose(f) _pclose(f)
+#endif
+#else
+extern FILE *popen();
+#endif
+
+static struct redirect *red_head = NULL;
+
+extern int output_is_tty;
+extern NODE *ARGC_node;
+extern NODE *ARGV_node;
+extern NODE *ARGIND_node;
+extern NODE *ERRNO_node;
+extern NODE **fields_arr;
+
+static jmp_buf filebuf; /* for do_nextfile() */
+
+/* do_nextfile --- implement gawk "next file" extension */
+
+void
+do_nextfile()
+{
+ (void) nextfile(1);
+ longjmp(filebuf, 1);
+}
+
+static IOBUF *
+nextfile(skipping)
+int skipping;
+{
+ static int i = 1;
+ static int files = 0;
+ NODE *arg;
+ static IOBUF *curfile = NULL;
+
+ if (skipping) {
+ if (curfile != NULL)
+ iop_close(curfile);
+ curfile = NULL;
+ return NULL;
+ }
+ if (curfile != NULL) {
+ if (curfile->cnt == EOF) {
+ (void) iop_close(curfile);
+ curfile = NULL;
+ } else
+ return curfile;
+ }
+ for (; i < (int) (ARGC_node->lnode->numbr); i++) {
+ arg = *assoc_lookup(ARGV_node, tmp_number((AWKNUM) i));
+ if (arg->stptr[0] == '\0')
+ continue;
+ arg->stptr[arg->stlen] = '\0';
+ if (! do_unix) {
+ ARGIND_node->var_value->numbr = i;
+ ARGIND_node->var_value->flags = NUM|NUMBER;
+ }
+ if (!arg_assign(arg->stptr)) {
+ files++;
+ curfile = iop_open(arg->stptr, "r");
+ if (curfile == NULL)
+ fatal("cannot open file `%s' for reading (%s)",
+ arg->stptr, strerror(errno));
+ /* NOTREACHED */
+ /* This is a kludge. */
+ unref(FILENAME_node->var_value);
+ FILENAME_node->var_value = dupnode(arg);
+ FNR = 0;
+ i++;
+ break;
+ }
+ }
+ if (files == 0) {
+ files++;
+ /* no args. -- use stdin */
+ /* FNR is init'ed to 0 */
+ FILENAME_node->var_value = make_string("-", 1);
+ curfile = iop_alloc(fileno(stdin));
+ }
+ return curfile;
+}
+
+void
+set_FNR()
+{
+ FNR = (long) FNR_node->var_value->numbr;
+}
+
+void
+set_NR()
+{
+ NR = (long) NR_node->var_value->numbr;
+}
+
+/*
+ * This reads in a record from the input file
+ */
+static int
+inrec(iop)
+IOBUF *iop;
+{
+ char *begin;
+ register int cnt;
+ int retval = 0;
+
+ cnt = get_a_record(&begin, iop, *RS, NULL);
+ if (cnt == EOF) {
+ cnt = 0;
+ retval = 1;
+ } else {
+ NR += 1;
+ FNR += 1;
+ }
+ set_record(begin, cnt, 1);
+
+ return retval;
+}
+
+static int
+iop_close(iop)
+IOBUF *iop;
+{
+ int ret;
+
+ if (iop == NULL)
+ return 0;
+ errno = 0;
+
+#ifdef _CRAY
+ /* Work around bug in UNICOS popen */
+ if (iop->fd < 3)
+ ret = 0;
+ else
+#endif
+ /* save these for re-use; don't free the storage */
+ if ((iop->flag & IOP_IS_INTERNAL) != 0) {
+ iop->off = iop->buf;
+ iop->end = iop->buf + strlen(iop->buf);
+ iop->cnt = 0;
+ iop->secsiz = 0;
+ return 0;
+ }
+
+ /* Don't close standard files or else crufty code elsewhere will lose */
+ if (iop->fd == fileno(stdin) ||
+ iop->fd == fileno(stdout) ||
+ iop->fd == fileno(stderr))
+ ret = 0;
+ else
+ ret = close(iop->fd);
+ if (ret == -1)
+ warning("close of fd %d failed (%s)", iop->fd, strerror(errno));
+ if ((iop->flag & IOP_NO_FREE) == 0) {
+ /*
+ * be careful -- $0 may still reference the buffer even though
+ * an explicit close is being done; in the future, maybe we
+ * can do this a bit better
+ */
+ if (iop->buf) {
+ if ((fields_arr[0]->stptr >= iop->buf)
+ && (fields_arr[0]->stptr < iop->end)) {
+ NODE *t;
+
+ t = make_string(fields_arr[0]->stptr,
+ fields_arr[0]->stlen);
+ unref(fields_arr[0]);
+ fields_arr [0] = t;
+ reset_record ();
+ }
+ free(iop->buf);
+ }
+ free((char *)iop);
+ }
+ return ret == -1 ? 1 : 0;
+}
+
+void
+do_input()
+{
+ IOBUF *iop;
+ extern int exiting;
+
+ (void) setjmp(filebuf);
+
+ while ((iop = nextfile(0)) != NULL) {
+ if (inrec(iop) == 0)
+ while (interpret(expression_value) && inrec(iop) == 0)
+ continue;
+ /* recover any space from C based alloca */
+ (void) alloca(0);
+
+ if (exiting)
+ break;
+ }
+}
+
+/* Redirection for printf and print commands */
+struct redirect *
+redirect(tree, errflg)
+NODE *tree;
+int *errflg;
+{
+ register NODE *tmp;
+ register struct redirect *rp;
+ register char *str;
+ int tflag = 0;
+ int outflag = 0;
+ const char *direction = "to";
+ const char *mode;
+ int fd;
+ const char *what = NULL;
+
+ switch (tree->type) {
+ case Node_redirect_append:
+ tflag = RED_APPEND;
+ /* FALL THROUGH */
+ case Node_redirect_output:
+ outflag = (RED_FILE|RED_WRITE);
+ tflag |= outflag;
+ if (tree->type == Node_redirect_output)
+ what = ">";
+ else
+ what = ">>";
+ break;
+ case Node_redirect_pipe:
+ tflag = (RED_PIPE|RED_WRITE);
+ what = "|";
+ break;
+ case Node_redirect_pipein:
+ tflag = (RED_PIPE|RED_READ);
+ what = "|";
+ break;
+ case Node_redirect_input:
+ tflag = (RED_FILE|RED_READ);
+ what = "<";
+ break;
+ default:
+ fatal ("invalid tree type %d in redirect()", tree->type);
+ break;
+ }
+ tmp = tree_eval(tree->subnode);
+ if (do_lint && ! (tmp->flags & STR))
+ warning("expression in `%s' redirection only has numeric value",
+ what);
+ tmp = force_string(tmp);
+ str = tmp->stptr;
+ if (str == NULL || *str == '\0')
+ fatal("expression for `%s' redirection has null string value",
+ what);
+ if (do_lint
+ && (STREQN(str, "0", tmp->stlen) || STREQN(str, "1", tmp->stlen)))
+ warning("filename `%s' for `%s' redirection may be result of logical expression", str, what);
+ for (rp = red_head; rp != NULL; rp = rp->next)
+ if (strlen(rp->value) == tmp->stlen
+ && STREQN(rp->value, str, tmp->stlen)
+ && ((rp->flag & ~(RED_NOBUF|RED_EOF)) == tflag
+ || (outflag
+ && (rp->flag & (RED_FILE|RED_WRITE)) == outflag)))
+ break;
+ if (rp == NULL) {
+ emalloc(rp, struct redirect *, sizeof(struct redirect),
+ "redirect");
+ emalloc(str, char *, tmp->stlen+1, "redirect");
+ memcpy(str, tmp->stptr, tmp->stlen);
+ str[tmp->stlen] = '\0';
+ rp->value = str;
+ rp->flag = tflag;
+ rp->fp = NULL;
+ rp->iop = NULL;
+ rp->pid = 0; /* unlikely that we're worried about init */
+ rp->status = 0;
+ /* maintain list in most-recently-used first order */
+ if (red_head)
+ red_head->prev = rp;
+ rp->prev = NULL;
+ rp->next = red_head;
+ red_head = rp;
+ }
+ while (rp->fp == NULL && rp->iop == NULL) {
+ if (rp->flag & RED_EOF)
+ /* encountered EOF on file or pipe -- must be cleared
+ * by explicit close() before reading more
+ */
+ return rp;
+ mode = NULL;
+ errno = 0;
+ switch (tree->type) {
+ case Node_redirect_output:
+ mode = "w";
+ if (rp->flag & RED_USED)
+ mode = "a";
+ break;
+ case Node_redirect_append:
+ mode = "a";
+ break;
+ case Node_redirect_pipe:
+ if ((rp->fp = popen(str, "w")) == NULL)
+ fatal("can't open pipe (\"%s\") for output (%s)",
+ str, strerror(errno));
+ rp->flag |= RED_NOBUF;
+ break;
+ case Node_redirect_pipein:
+ direction = "from";
+ if (gawk_popen(str, rp) == NULL)
+ fatal("can't open pipe (\"%s\") for input (%s)",
+ str, strerror(errno));
+ break;
+ case Node_redirect_input:
+ direction = "from";
+ rp->iop = iop_open(str, "r");
+ break;
+ default:
+ cant_happen();
+ }
+ if (mode != NULL) {
+ fd = devopen(str, mode);
+ if (fd > INVALID_HANDLE) {
+ if (fd == fileno(stdin))
+ rp->fp = stdin;
+ else if (fd == fileno(stdout))
+ rp->fp = stdout;
+ else if (fd == fileno(stderr))
+ rp->fp = stderr;
+ else {
+ rp->fp = fdopen(fd, (char *) mode);
+ /* don't leak file descriptors */
+ if (rp->fp == NULL)
+ close(fd);
+ }
+ if (rp->fp != NULL && isatty(fd))
+ rp->flag |= RED_NOBUF;
+ }
+ }
+ if (rp->fp == NULL && rp->iop == NULL) {
+ /* too many files open -- close one and try again */
+ if (errno == EMFILE || errno == ENFILE)
+ close_one();
+ else {
+ /*
+ * Some other reason for failure.
+ *
+ * On redirection of input from a file,
+ * just return an error, so e.g. getline
+ * can return -1. For output to file,
+ * complain. The shell will complain on
+ * a bad command to a pipe.
+ */
+ *errflg = errno;
+ if (tree->type == Node_redirect_output
+ || tree->type == Node_redirect_append)
+ fatal("can't redirect %s `%s' (%s)",
+ direction, str, strerror(errno));
+ else {
+ free_temp(tmp);
+ return NULL;
+ }
+ }
+ }
+ }
+ free_temp(tmp);
+ return rp;
+}
+
+static void
+close_one()
+{
+ register struct redirect *rp;
+ register struct redirect *rplast = NULL;
+
+ /* go to end of list first, to pick up least recently used entry */
+ for (rp = red_head; rp != NULL; rp = rp->next)
+ rplast = rp;
+ /* now work back up through the list */
+ for (rp = rplast; rp != NULL; rp = rp->prev)
+ if (rp->fp && (rp->flag & RED_FILE)) {
+ rp->flag |= RED_USED;
+ errno = 0;
+ if (fclose(rp->fp))
+ warning("close of \"%s\" failed (%s).",
+ rp->value, strerror(errno));
+ rp->fp = NULL;
+ break;
+ }
+ if (rp == NULL)
+ /* surely this is the only reason ??? */
+ fatal("too many pipes or input files open");
+}
+
+NODE *
+do_close(tree)
+NODE *tree;
+{
+ NODE *tmp;
+ register struct redirect *rp;
+
+ tmp = force_string(tree_eval(tree->subnode));
+ for (rp = red_head; rp != NULL; rp = rp->next) {
+ if (strlen(rp->value) == tmp->stlen
+ && STREQN(rp->value, tmp->stptr, tmp->stlen))
+ break;
+ }
+ free_temp(tmp);
+ if (rp == NULL) /* no match */
+ return tmp_number((AWKNUM) 0.0);
+ fflush(stdout); /* synchronize regular output */
+ tmp = tmp_number((AWKNUM)close_redir(rp, 0));
+ rp = NULL;
+ return tmp;
+}
+
+static int
+close_redir(rp, exitwarn)
+register struct redirect *rp;
+int exitwarn;
+{
+ int status = 0;
+ char *what;
+
+ if (rp == NULL)
+ return 0;
+ if (rp->fp == stdout || rp->fp == stderr)
+ return 0;
+ errno = 0;
+ if ((rp->flag & (RED_PIPE|RED_WRITE)) == (RED_PIPE|RED_WRITE))
+ status = pclose(rp->fp);
+ else if (rp->fp)
+ status = fclose(rp->fp);
+ else if (rp->iop) {
+ if (rp->flag & RED_PIPE)
+ status = gawk_pclose(rp);
+ else {
+ status = iop_close(rp->iop);
+ rp->iop = NULL;
+ }
+ }
+
+ what = (rp->flag & RED_PIPE) ? "pipe" : "file";
+
+ if (exitwarn)
+ warning("no explicit close of %s \"%s\" provided",
+ what, rp->value);
+
+ /* SVR4 awk checks and warns about status of close */
+ if (status) {
+ char *s = strerror(errno);
+
+ warning("failure status (%d) on %s close of \"%s\" (%s)",
+ status, what, rp->value, s);
+
+ if (! do_unix) {
+ /* set ERRNO too so that program can get at it */
+ unref(ERRNO_node->var_value);
+ ERRNO_node->var_value = make_string(s, strlen(s));
+ }
+ }
+ if (rp->next)
+ rp->next->prev = rp->prev;
+ if (rp->prev)
+ rp->prev->next = rp->next;
+ else
+ red_head = rp->next;
+ free(rp->value);
+ free((char *)rp);
+ return status;
+}
+
+int
+flush_io ()
+{
+ register struct redirect *rp;
+ int status = 0;
+
+ errno = 0;
+ if (fflush(stdout)) {
+ warning("error writing standard output (%s).", strerror(errno));
+ status++;
+ }
+ if (fflush(stderr)) {
+ warning("error writing standard error (%s).", strerror(errno));
+ status++;
+ }
+ for (rp = red_head; rp != NULL; rp = rp->next)
+ /* flush both files and pipes, what the heck */
+ if ((rp->flag & RED_WRITE) && rp->fp != NULL) {
+ if (fflush(rp->fp)) {
+ warning("%s flush of \"%s\" failed (%s).",
+ (rp->flag & RED_PIPE) ? "pipe" :
+ "file", rp->value, strerror(errno));
+ status++;
+ }
+ }
+ return status;
+}
+
+int
+close_io ()
+{
+ register struct redirect *rp;
+ register struct redirect *next;
+ int status = 0;
+
+ errno = 0;
+ for (rp = red_head; rp != NULL; rp = next) {
+ next = rp->next;
+ /* close_redir() will print a message if needed */
+ /* if do_lint, warn about lack of explicit close */
+ if (close_redir(rp, do_lint))
+ status++;
+ rp = NULL;
+ }
+ /*
+ * Some of the non-Unix os's have problems doing an fclose
+ * on stdout and stderr. Since we don't really need to close
+ * them, we just flush them, and do that across the board.
+ */
+ if (fflush(stdout)) {
+ warning("error writing standard output (%s).", strerror(errno));
+ status++;
+ }
+ if (fflush(stderr)) {
+ warning("error writing standard error (%s).", strerror(errno));
+ status++;
+ }
+ return status;
+}
+
+/* str2mode --- convert a string mode to an integer mode */
+
+static int
+str2mode(mode)
+const char *mode;
+{
+ int ret;
+
+ switch(mode[0]) {
+ case 'r':
+ ret = O_RDONLY;
+ break;
+
+ case 'w':
+ ret = O_WRONLY|O_CREAT|O_TRUNC;
+ break;
+
+ case 'a':
+ ret = O_WRONLY|O_APPEND|O_CREAT;
+ break;
+
+ default:
+ ret = 0; /* lint */
+ cant_happen();
+ }
+ return ret;
+}
+
+/* devopen --- handle /dev/std{in,out,err}, /dev/fd/N, regular files */
+
+/*
+ * This separate version is still needed for output, since file and pipe
+ * output is done with stdio. iop_open() handles input with IOBUFs of
+ * more "special" files. Those files are not handled here since it makes
+ * no sense to use them for output.
+ */
+
+int
+devopen(name, mode)
+const char *name, *mode;
+{
+ int openfd = INVALID_HANDLE;
+ const char *cp, *ptr;
+ int flag = 0;
+ struct stat buf;
+ extern double strtod();
+
+ flag = str2mode(mode);
+
+ if (do_unix)
+ goto strictopen;
+
+#ifdef VMS
+ if ((openfd = vms_devopen(name, flag)) >= 0)
+ return openfd;
+#endif /* VMS */
+
+ if (STREQ(name, "-"))
+ openfd = fileno(stdin);
+ else if (STREQN(name, "/dev/", 5) && stat((char *) name, &buf) == -1) {
+ cp = name + 5;
+
+ if (STREQ(cp, "stdin") && (flag & O_RDONLY) == O_RDONLY)
+ openfd = fileno(stdin);
+ else if (STREQ(cp, "stdout") && (flag & O_WRONLY) == O_WRONLY)
+ openfd = fileno(stdout);
+ else if (STREQ(cp, "stderr") && (flag & O_WRONLY) == O_WRONLY)
+ openfd = fileno(stderr);
+ else if (STREQN(cp, "fd/", 3)) {
+ cp += 3;
+ openfd = (int)strtod(cp, &ptr);
+ if (openfd <= INVALID_HANDLE || ptr == cp)
+ openfd = INVALID_HANDLE;
+ }
+ }
+
+strictopen:
+ if (openfd == INVALID_HANDLE)
+ openfd = open(name, flag, 0666);
+ if (openfd != INVALID_HANDLE && fstat(openfd, &buf) > 0)
+ if (S_ISDIR(buf.st_mode))
+ fatal("file `%s' is a directory", name);
+ return openfd;
+}
+
+
+/* spec_setup --- setup an IOBUF for a special internal file */
+
+static void
+spec_setup(iop, len, allocate)
+IOBUF *iop;
+int len;
+int allocate;
+{
+ char *cp;
+
+ if (allocate) {
+ emalloc(cp, char *, len+2, "spec_setup");
+ iop->buf = cp;
+ } else {
+ len = strlen(iop->buf);
+ iop->buf[len++] = '\n'; /* get_a_record clobbered it */
+ iop->buf[len] = '\0'; /* just in case */
+ }
+ iop->off = iop->buf;
+ iop->cnt = 0;
+ iop->secsiz = 0;
+ iop->size = len;
+ iop->end = iop->buf + len;
+ iop->fd = -1;
+ iop->flag = IOP_IS_INTERNAL;
+}
+
+/* specfdopen --- open a fd special file */
+
+static int
+specfdopen(iop, name, mode)
+IOBUF *iop;
+const char *name, *mode;
+{
+ int fd;
+ IOBUF *tp;
+
+ fd = devopen(name, mode);
+ if (fd == INVALID_HANDLE)
+ return INVALID_HANDLE;
+ tp = iop_alloc(fd);
+ if (tp == NULL)
+ return INVALID_HANDLE;
+ *iop = *tp;
+ iop->flag |= IOP_NO_FREE;
+ free(tp);
+ return 0;
+}
+
+/*
+ * Following mess will improve in 2.16; this is written to avoid
+ * long lines, avoid splitting #if with backslash, and avoid #elif
+ * to maximize portability.
+ */
+#ifndef GETPGRP_NOARG
+#if defined(__svr4__) || defined(BSD4_4) || defined(_POSIX_SOURCE)
+#define GETPGRP_NOARG
+#else
+#if defined(i860) || defined(_AIX) || defined(hpux) || defined(VMS)
+#define GETPGRP_NOARG
+#else
+#if defined(OS2) || defined(MSDOS) || defined(AMIGA) || defined(atarist)
+#define GETPGRP_NOARG
+#endif
+#endif
+#endif
+#endif
+
+#ifdef GETPGRP_NOARG
+#define getpgrp_ARG /* nothing */
+#else
+#define getpgrp_ARG getpid()
+#endif
+
+/* pidopen --- "open" /dev/pid, /dev/ppid, and /dev/pgrpid */
+
+static int
+pidopen(iop, name, mode)
+IOBUF *iop;
+const char *name, *mode;
+{
+ char tbuf[BUFSIZ];
+ int i;
+
+ if (name[6] == 'g')
+ sprintf(tbuf, "%d\n", getpgrp( getpgrp_ARG ));
+ else if (name[6] == 'i')
+ sprintf(tbuf, "%d\n", getpid());
+ else
+ sprintf(tbuf, "%d\n", getppid());
+ i = strlen(tbuf);
+ spec_setup(iop, i, 1);
+ strcpy(iop->buf, tbuf);
+ return 0;
+}
+
+/* useropen --- "open" /dev/user */
+
+/*
+ * /dev/user creates a record as follows:
+ * $1 = getuid()
+ * $2 = geteuid()
+ * $3 = getgid()
+ * $4 = getegid()
+ * If multiple groups are supported, the $5 through $NF are the
+ * supplementary group set.
+ */
+
+static int
+useropen(iop, name, mode)
+IOBUF *iop;
+const char *name, *mode;
+{
+ char tbuf[BUFSIZ], *cp;
+ int i;
+#if defined(NGROUPS_MAX) && NGROUPS_MAX > 0
+#if defined(atarist) || defined(__svr4__) || defined(__osf__)
+ gid_t groupset[NGROUPS_MAX];
+#else
+ int groupset[NGROUPS_MAX];
+#endif
+ int ngroups;
+#endif
+
+ sprintf(tbuf, "%d %d %d %d", getuid(), geteuid(), getgid(), getegid());
+
+ cp = tbuf + strlen(tbuf);
+#if defined(NGROUPS_MAX) && NGROUPS_MAX > 0
+ ngroups = getgroups(NGROUPS_MAX, groupset);
+ if (ngroups == -1)
+ fatal("could not find groups: %s", strerror(errno));
+
+ for (i = 0; i < ngroups; i++) {
+ *cp++ = ' ';
+ sprintf(cp, "%d", (int)groupset[i]);
+ cp += strlen(cp);
+ }
+#endif
+ *cp++ = '\n';
+ *cp++ = '\0';
+
+
+ i = strlen(tbuf);
+ spec_setup(iop, i, 1);
+ strcpy(iop->buf, tbuf);
+ return 0;
+}
+
+/* iop_open --- handle special and regular files for input */
+
+static IOBUF *
+iop_open(name, mode)
+const char *name, *mode;
+{
+ int openfd = INVALID_HANDLE;
+ int flag = 0;
+ struct stat buf;
+ IOBUF *iop;
+ static struct internal {
+ const char *name;
+ int compare;
+ int (*fp) P((IOBUF*,const char *,const char *));
+ IOBUF iob;
+ } table[] = {
+ { "/dev/fd/", 8, specfdopen },
+ { "/dev/stdin", 10, specfdopen },
+ { "/dev/stdout", 11, specfdopen },
+ { "/dev/stderr", 11, specfdopen },
+ { "/dev/pid", 8, pidopen },
+ { "/dev/ppid", 9, pidopen },
+ { "/dev/pgrpid", 11, pidopen },
+ { "/dev/user", 9, useropen },
+ };
+ int devcount = sizeof(table) / sizeof(table[0]);
+
+ flag = str2mode(mode);
+
+ if (do_unix)
+ goto strictopen;
+
+ if (STREQ(name, "-"))
+ openfd = fileno(stdin);
+ else if (STREQN(name, "/dev/", 5) && stat((char *) name, &buf) == -1) {
+ int i;
+
+ for (i = 0; i < devcount; i++) {
+ if (STREQN(name, table[i].name, table[i].compare)) {
+ iop = & table[i].iob;
+
+ if (iop->buf != NULL) {
+ spec_setup(iop, 0, 0);
+ return iop;
+ } else if ((*table[i].fp)(iop, name, mode) == 0)
+ return iop;
+ else {
+ warning("could not open %s, mode `%s'",
+ name, mode);
+ return NULL;
+ }
+ }
+ }
+ }
+
+strictopen:
+ if (openfd == INVALID_HANDLE)
+ openfd = open(name, flag, 0666);
+ if (openfd != INVALID_HANDLE && fstat(openfd, &buf) > 0)
+ if ((buf.st_mode & S_IFMT) == S_IFDIR)
+ fatal("file `%s' is a directory", name);
+ iop = iop_alloc(openfd);
+ return iop;
+}
+
+#ifndef PIPES_SIMULATED
+ /* real pipes */
+static int
+wait_any(interesting)
+int interesting; /* pid of interest, if any */
+{
+ SIGTYPE (*hstat)(), (*istat)(), (*qstat)();
+ int pid;
+ int status = 0;
+ struct redirect *redp;
+ extern int errno;
+
+ hstat = signal(SIGHUP, SIG_IGN);
+ istat = signal(SIGINT, SIG_IGN);
+ qstat = signal(SIGQUIT, SIG_IGN);
+ for (;;) {
+#ifdef NeXT
+ pid = wait((union wait *)&status);
+#else
+ pid = wait(&status);
+#endif /* NeXT */
+ if (interesting && pid == interesting) {
+ break;
+ } else if (pid != -1) {
+ for (redp = red_head; redp != NULL; redp = redp->next)
+ if (pid == redp->pid) {
+ redp->pid = -1;
+ redp->status = status;
+ if (redp->fp) {
+ pclose(redp->fp);
+ redp->fp = 0;
+ }
+ if (redp->iop) {
+ (void) iop_close(redp->iop);
+ redp->iop = 0;
+ }
+ break;
+ }
+ }
+ if (pid == -1 && errno == ECHILD)
+ break;
+ }
+ signal(SIGHUP, hstat);
+ signal(SIGINT, istat);
+ signal(SIGQUIT, qstat);
+ return(status);
+}
+
+static IOBUF *
+gawk_popen(cmd, rp)
+char *cmd;
+struct redirect *rp;
+{
+ int p[2];
+ register int pid;
+
+ /* used to wait for any children to synchronize input and output,
+ * but this could cause gawk to hang when it is started in a pipeline
+ * and thus has a child process feeding it input (shell dependant)
+ */
+ /*(void) wait_any(0);*/ /* wait for outstanding processes */
+
+ if (pipe(p) < 0)
+ fatal("cannot open pipe \"%s\" (%s)", cmd, strerror(errno));
+ if ((pid = fork()) == 0) {
+ if (close(1) == -1)
+ fatal("close of stdout in child failed (%s)",
+ strerror(errno));
+ if (dup(p[1]) != 1)
+ fatal("dup of pipe failed (%s)", strerror(errno));
+ if (close(p[0]) == -1 || close(p[1]) == -1)
+ fatal("close of pipe failed (%s)", strerror(errno));
+ if (close(0) == -1)
+ fatal("close of stdin in child failed (%s)",
+ strerror(errno));
+ execl("/bin/sh", "sh", "-c", cmd, 0);
+ _exit(127);
+ }
+ if (pid == -1)
+ fatal("cannot fork for \"%s\" (%s)", cmd, strerror(errno));
+ rp->pid = pid;
+ if (close(p[1]) == -1)
+ fatal("close of pipe failed (%s)", strerror(errno));
+ return (rp->iop = iop_alloc(p[0]));
+}
+
+static int
+gawk_pclose(rp)
+struct redirect *rp;
+{
+ (void) iop_close(rp->iop);
+ rp->iop = NULL;
+
+ /* process previously found, return stored status */
+ if (rp->pid == -1)
+ return (rp->status >> 8) & 0xFF;
+ rp->status = wait_any(rp->pid);
+ rp->pid = -1;
+ return (rp->status >> 8) & 0xFF;
+}
+
+#else /* PIPES_SIMULATED */
+ /* use temporary file rather than pipe */
+ /* except if popen() provides real pipes too */
+
+#if defined(VMS) || defined(OS2) || defined (MSDOS)
+static IOBUF *
+gawk_popen(cmd, rp)
+char *cmd;
+struct redirect *rp;
+{
+ FILE *current;
+
+ if ((current = popen(cmd, "r")) == NULL)
+ return NULL;
+ return (rp->iop = iop_alloc(fileno(current)));
+}
+
+static int
+gawk_pclose(rp)
+struct redirect *rp;
+{
+ int rval, aval, fd = rp->iop->fd;
+ FILE *kludge = fdopen(fd, (char *) "r"); /* pclose needs FILE* w/ right fileno */
+
+ rp->iop->fd = dup(fd); /* kludge to allow close() + pclose() */
+ rval = iop_close(rp->iop);
+ rp->iop = NULL;
+ aval = pclose(kludge);
+ return (rval < 0 ? rval : aval);
+}
+#else /* VMS || OS2 || MSDOS */
+
+static
+struct {
+ char *command;
+ char *name;
+} pipes[_NFILE];
+
+static IOBUF *
+gawk_popen(cmd, rp)
+char *cmd;
+struct redirect *rp;
+{
+ extern char *strdup(const char *);
+ int current;
+ char *name;
+ static char cmdbuf[256];
+
+ /* get a name to use. */
+ if ((name = tempnam(".", "pip")) == NULL)
+ return NULL;
+ sprintf(cmdbuf,"%s > %s", cmd, name);
+ system(cmdbuf);
+ if ((current = open(name,O_RDONLY)) == INVALID_HANDLE)
+ return NULL;
+ pipes[current].name = name;
+ pipes[current].command = strdup(cmd);
+ rp->iop = iop_alloc(current);
+ return (rp->iop = iop_alloc(current));
+}
+
+static int
+gawk_pclose(rp)
+struct redirect *rp;
+{
+ int cur = rp->iop->fd;
+ int rval;
+
+ rval = iop_close(rp->iop);
+ rp->iop = NULL;
+
+ /* check for an open file */
+ if (pipes[cur].name == NULL)
+ return -1;
+ unlink(pipes[cur].name);
+ free(pipes[cur].name);
+ pipes[cur].name = NULL;
+ free(pipes[cur].command);
+ return rval;
+}
+#endif /* VMS || OS2 || MSDOS */
+
+#endif /* PIPES_SIMULATED */
+
+NODE *
+do_getline(tree)
+NODE *tree;
+{
+ struct redirect *rp = NULL;
+ IOBUF *iop;
+ int cnt = EOF;
+ char *s = NULL;
+ int errcode;
+
+ while (cnt == EOF) {
+ if (tree->rnode == NULL) { /* no redirection */
+ iop = nextfile(0);
+ if (iop == NULL) /* end of input */
+ return tmp_number((AWKNUM) 0.0);
+ } else {
+ int redir_error = 0;
+
+ rp = redirect(tree->rnode, &redir_error);
+ if (rp == NULL && redir_error) { /* failed redirect */
+ if (! do_unix) {
+ s = strerror(redir_error);
+
+ unref(ERRNO_node->var_value);
+ ERRNO_node->var_value =
+ make_string(s, strlen(s));
+ }
+ return tmp_number((AWKNUM) -1.0);
+ }
+ iop = rp->iop;
+ if (iop == NULL) /* end of input */
+ return tmp_number((AWKNUM) 0.0);
+ }
+ errcode = 0;
+ cnt = get_a_record(&s, iop, *RS, & errcode);
+ if (! do_unix && errcode != 0) {
+ s = strerror(errcode);
+
+ unref(ERRNO_node->var_value);
+ ERRNO_node->var_value = make_string(s, strlen(s));
+ return tmp_number((AWKNUM) -1.0);
+ }
+ if (cnt == EOF) {
+ if (rp) {
+ /*
+ * Don't do iop_close() here if we are
+ * reading from a pipe; otherwise
+ * gawk_pclose will not be called.
+ */
+ if (!(rp->flag & RED_PIPE)) {
+ (void) iop_close(iop);
+ rp->iop = NULL;
+ }
+ rp->flag |= RED_EOF; /* sticky EOF */
+ return tmp_number((AWKNUM) 0.0);
+ } else
+ continue; /* try another file */
+ }
+ if (!rp) {
+ NR += 1;
+ FNR += 1;
+ }
+ if (tree->lnode == NULL) /* no optional var. */
+ set_record(s, cnt, 1);
+ else { /* assignment to variable */
+ Func_ptr after_assign = NULL;
+ NODE **lhs;
+
+ lhs = get_lhs(tree->lnode, &after_assign);
+ unref(*lhs);
+ *lhs = make_string(s, strlen(s));
+ (*lhs)->flags |= MAYBE_NUM;
+ /* we may have to regenerate $0 here! */
+ if (after_assign)
+ (*after_assign)();
+ }
+ }
+ return tmp_number((AWKNUM) 1.0);
+}
+
+int
+pathopen (file)
+const char *file;
+{
+ int fd = do_pathopen(file);
+
+#ifdef DEFAULT_FILETYPE
+ if (! do_unix && fd <= INVALID_HANDLE) {
+ char *file_awk;
+ int save = errno;
+#ifdef VMS
+ int vms_save = vaxc$errno;
+#endif
+
+ /* append ".awk" and try again */
+ emalloc(file_awk, char *, strlen(file) +
+ sizeof(DEFAULT_FILETYPE) + 1, "pathopen");
+ sprintf(file_awk, "%s%s", file, DEFAULT_FILETYPE);
+ fd = do_pathopen(file_awk);
+ free(file_awk);
+ if (fd <= INVALID_HANDLE) {
+ errno = save;
+#ifdef VMS
+ vaxc$errno = vms_save;
+#endif
+ }
+ }
+#endif /*DEFAULT_FILETYPE*/
+
+ return fd;
+}
+
+static int
+do_pathopen (file)
+const char *file;
+{
+ static const char *savepath = DEFPATH; /* defined in config.h */
+ static int first = 1;
+ const char *awkpath;
+ char *cp, trypath[BUFSIZ];
+ int fd;
+
+ if (STREQ(file, "-"))
+ return (0);
+
+ if (do_unix)
+ return (devopen(file, "r"));
+
+ if (first) {
+ first = 0;
+ if ((awkpath = getenv ("AWKPATH")) != NULL && *awkpath)
+ savepath = awkpath; /* used for restarting */
+ }
+ awkpath = savepath;
+
+ /* some kind of path name, no search */
+#ifdef VMS /* (strchr not equal implies either or both not NULL) */
+ if (strchr(file, ':') != strchr(file, ']')
+ || strchr(file, '>') != strchr(file, '/'))
+#else /*!VMS*/
+#if defined(MSDOS) || defined(OS2)
+ if (strchr(file, '/') != strchr(file, '\\')
+ || strchr(file, ':') != NULL)
+#else
+ if (strchr(file, '/') != NULL)
+#endif /*MSDOS*/
+#endif /*VMS*/
+ return (devopen(file, "r"));
+
+#if defined(MSDOS) || defined(OS2)
+ _searchenv(file, "AWKPATH", trypath);
+ if (trypath[0] == '\0')
+ _searchenv(file, "PATH", trypath);
+ return (trypath[0] == '\0') ? 0 : devopen(trypath, "r");
+#else
+ do {
+ trypath[0] = '\0';
+ /* this should take into account limits on size of trypath */
+ for (cp = trypath; *awkpath && *awkpath != ENVSEP; )
+ *cp++ = *awkpath++;
+
+ if (cp != trypath) { /* nun-null element in path */
+ /* add directory punctuation only if needed */
+#ifdef VMS
+ if (strchr(":]>/", *(cp-1)) == NULL)
+#else
+#if defined(MSDOS) || defined(OS2)
+ if (strchr(":\\/", *(cp-1)) == NULL)
+#else
+ if (*(cp-1) != '/')
+#endif
+#endif
+ *cp++ = '/';
+ /* append filename */
+ strcpy (cp, file);
+ } else
+ strcpy (trypath, file);
+ if ((fd = devopen(trypath, "r")) >= 0)
+ return (fd);
+
+ /* no luck, keep going */
+ if(*awkpath == ENVSEP && awkpath[1] != '\0')
+ awkpath++; /* skip colon */
+ } while (*awkpath);
+ /*
+ * You might have one of the awk
+ * paths defined, WITHOUT the current working directory in it.
+ * Therefore try to open the file in the current directory.
+ */
+ return (devopen(file, "r"));
+#endif
+}
diff --git a/gnu/usr.bin/awk/iop.c b/gnu/usr.bin/awk/iop.c
new file mode 100644
index 0000000..897daef
--- /dev/null
+++ b/gnu/usr.bin/awk/iop.c
@@ -0,0 +1,321 @@
+/*
+ * iop.c - do i/o related things.
+ */
+
+/*
+ * Copyright (C) 1986, 1988, 1989, 1991, 1992, 1993 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Progamming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GAWK; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "awk.h"
+
+#ifndef atarist
+#define INVALID_HANDLE (-1)
+#else
+#include <stddef.h>
+#include <fcntl.h>
+#define INVALID_HANDLE (__SMALLEST_VALID_HANDLE - 1)
+#endif /* atarist */
+
+
+#ifdef TEST
+int bufsize = 8192;
+
+void
+fatal(s)
+char *s;
+{
+ printf("%s\n", s);
+ exit(1);
+}
+#endif
+
+int
+optimal_bufsize(fd)
+int fd;
+{
+ struct stat stb;
+
+#ifdef VMS
+ /*
+ * These values correspond with the RMS multi-block count used by
+ * vms_open() in vms/vms_misc.c.
+ */
+ if (isatty(fd) > 0)
+ return BUFSIZ;
+ else if (fstat(fd, &stb) < 0)
+ return 8*512; /* conservative in case of DECnet access */
+ else
+ return 32*512;
+
+#else
+ /*
+ * System V doesn't have the file system block size in the
+ * stat structure. So we have to make some sort of reasonable
+ * guess. We use stdio's BUFSIZ, since that is what it was
+ * meant for in the first place.
+ */
+#ifdef BLKSIZE_MISSING
+#define DEFBLKSIZE BUFSIZ
+#else
+#define DEFBLKSIZE (stb.st_blksize ? stb.st_blksize : BUFSIZ)
+#endif
+
+#ifdef TEST
+ return bufsize;
+#else
+#ifndef atarist
+ if (isatty(fd))
+#else
+ /*
+ * On ST redirected stdin does not have a name attached
+ * (this could be hard to do to) and fstat would fail
+ */
+ if (0 == fd || isatty(fd))
+#endif /*atarist */
+ return BUFSIZ;
+#ifndef BLKSIZE_MISSING
+ /* VMS POSIX 1.0: st_blksize is never assigned a value, so zero it */
+ stb.st_blksize = 0;
+#endif
+ if (fstat(fd, &stb) == -1)
+ fatal("can't stat fd %d (%s)", fd, strerror(errno));
+ if (lseek(fd, (off_t)0, 0) == -1)
+ return DEFBLKSIZE;
+ return ((int) (stb.st_size < DEFBLKSIZE ? stb.st_size : DEFBLKSIZE));
+#endif /*! TEST */
+#endif /*! VMS */
+}
+
+IOBUF *
+iop_alloc(fd)
+int fd;
+{
+ IOBUF *iop;
+
+ if (fd == INVALID_HANDLE)
+ return NULL;
+ emalloc(iop, IOBUF *, sizeof(IOBUF), "iop_alloc");
+ iop->flag = 0;
+ if (isatty(fd))
+ iop->flag |= IOP_IS_TTY;
+ iop->size = optimal_bufsize(fd);
+ iop->secsiz = -2;
+ errno = 0;
+ iop->fd = fd;
+ iop->off = iop->buf = NULL;
+ iop->cnt = 0;
+ return iop;
+}
+
+/*
+ * Get the next record. Uses a "split buffer" where the latter part is
+ * the normal read buffer and the head part is an "overflow" area that is used
+ * when a record spans the end of the normal buffer, in which case the first
+ * part of the record is copied into the overflow area just before the
+ * normal buffer. Thus, the eventual full record can be returned as a
+ * contiguous area of memory with a minimum of copying. The overflow area
+ * is expanded as needed, so that records are unlimited in length.
+ * We also mark both the end of the buffer and the end of the read() with
+ * a sentinel character (the current record separator) so that the inside
+ * loop can run as a single test.
+ */
+int
+get_a_record(out, iop, grRS, errcode)
+char **out;
+IOBUF *iop;
+register int grRS;
+int *errcode;
+{
+ register char *bp = iop->off;
+ char *bufend;
+ char *start = iop->off; /* beginning of record */
+ char rs;
+ int saw_newline = 0, eat_whitespace = 0; /* used iff grRS==0 */
+
+ if (iop->cnt == EOF) { /* previous read hit EOF */
+ *out = NULL;
+ return EOF;
+ }
+
+ if (grRS == 0) { /* special case: grRS == "" */
+ rs = '\n';
+ } else
+ rs = (char) grRS;
+
+ /* set up sentinel */
+ if (iop->buf) {
+ bufend = iop->buf + iop->size + iop->secsiz;
+ *bufend = rs;
+ } else
+ bufend = NULL;
+
+ for (;;) { /* break on end of record, read error or EOF */
+
+ /* Following code is entered on the first call of this routine
+ * for a new iop, or when we scan to the end of the buffer.
+ * In the latter case, we copy the current partial record to
+ * the space preceding the normal read buffer. If necessary,
+ * we expand this space. This is done so that we can return
+ * the record as a contiguous area of memory.
+ */
+ if ((iop->flag & IOP_IS_INTERNAL) == 0 && bp >= bufend) {
+ char *oldbuf = NULL;
+ char *oldsplit = iop->buf + iop->secsiz;
+ long len; /* record length so far */
+
+ len = bp - start;
+ if (len > iop->secsiz) {
+ /* expand secondary buffer */
+ if (iop->secsiz == -2)
+ iop->secsiz = 256;
+ while (len > iop->secsiz)
+ iop->secsiz *= 2;
+ oldbuf = iop->buf;
+ emalloc(iop->buf, char *,
+ iop->size+iop->secsiz+2, "get_a_record");
+ bufend = iop->buf + iop->size + iop->secsiz;
+ *bufend = rs;
+ }
+ if (len > 0) {
+ char *newsplit = iop->buf + iop->secsiz;
+
+ if (start < oldsplit) {
+ memcpy(newsplit - len, start,
+ oldsplit - start);
+ memcpy(newsplit - (bp - oldsplit),
+ oldsplit, bp - oldsplit);
+ } else
+ memcpy(newsplit - len, start, len);
+ }
+ bp = iop->end = iop->off = iop->buf + iop->secsiz;
+ start = bp - len;
+ if (oldbuf) {
+ free(oldbuf);
+ oldbuf = NULL;
+ }
+ }
+ /* Following code is entered whenever we have no more data to
+ * scan. In most cases this will read into the beginning of
+ * the main buffer, but in some cases (terminal, pipe etc.)
+ * we may be doing smallish reads into more advanced positions.
+ */
+ if (bp >= iop->end) {
+ if ((iop->flag & IOP_IS_INTERNAL) != 0) {
+ iop->cnt = EOF;
+ break;
+ }
+ iop->cnt = read(iop->fd, iop->end, bufend - iop->end);
+ if (iop->cnt == -1) {
+ if (! do_unix && errcode != NULL) {
+ *errcode = errno;
+ iop->cnt = EOF;
+ break;
+ } else
+ fatal("error reading input: %s",
+ strerror(errno));
+ } else if (iop->cnt == 0) {
+ iop->cnt = EOF;
+ break;
+ }
+ iop->end += iop->cnt;
+ *iop->end = rs;
+ }
+ if (grRS == 0) {
+ extern int default_FS;
+
+ if (default_FS && (bp == start || eat_whitespace)) {
+ while (bp < iop->end
+ && (*bp == ' ' || *bp == '\t' || *bp == '\n'))
+ bp++;
+ if (bp == iop->end) {
+ eat_whitespace = 1;
+ continue;
+ } else
+ eat_whitespace = 0;
+ }
+ if (saw_newline && *bp == rs) {
+ bp++;
+ break;
+ }
+ saw_newline = 0;
+ }
+
+ while (*bp++ != rs)
+ ;
+
+ if (bp <= iop->end) {
+ if (grRS == 0)
+ saw_newline = 1;
+ else
+ break;
+ } else
+ bp--;
+
+ if ((iop->flag & IOP_IS_INTERNAL) != 0)
+ iop->cnt = bp - start;
+ }
+ if (iop->cnt == EOF
+ && (((iop->flag & IOP_IS_INTERNAL) != 0) || start == bp)) {
+ *out = NULL;
+ return EOF;
+ }
+
+ iop->off = bp;
+ bp--;
+ if (*bp != rs)
+ bp++;
+ *bp = '\0';
+ if (grRS == 0) {
+ /* there could be more newlines left, clean 'em out now */
+ while (*(iop->off) == rs && iop->off <= iop->end)
+ (iop->off)++;
+
+ if (*--bp == rs)
+ *bp = '\0';
+ else
+ bp++;
+ }
+
+ *out = start;
+ return bp - start;
+}
+
+#ifdef TEST
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ IOBUF *iop;
+ char *out;
+ int cnt;
+ char rs[2];
+
+ rs[0] = 0;
+ if (argc > 1)
+ bufsize = atoi(argv[1]);
+ if (argc > 2)
+ rs[0] = *argv[2];
+ iop = iop_alloc(0);
+ while ((cnt = get_a_record(&out, iop, rs[0], NULL)) > 0) {
+ fwrite(out, 1, cnt, stdout);
+ fwrite(rs, 1, 1, stdout);
+ }
+}
+#endif
diff --git a/gnu/usr.bin/awk/main.c b/gnu/usr.bin/awk/main.c
new file mode 100644
index 0000000..a14efff
--- /dev/null
+++ b/gnu/usr.bin/awk/main.c
@@ -0,0 +1,817 @@
+/*
+ * main.c -- Expression tree constructors and main program for gawk.
+ */
+
+/*
+ * Copyright (C) 1986, 1988, 1989, 1991, 1992, 1993 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Progamming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GAWK; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "getopt.h"
+#include "awk.h"
+#include "patchlevel.h"
+
+static void usage P((int exitval));
+static void copyleft P((void));
+static void cmdline_fs P((char *str));
+static void init_args P((int argc0, int argc, char *argv0, char **argv));
+static void init_vars P((void));
+static void pre_assign P((char *v));
+SIGTYPE catchsig P((int sig, int code));
+static void gawk_option P((char *optstr));
+static void nostalgia P((void));
+static void version P((void));
+char *gawk_name P((char *filespec));
+
+#ifdef MSDOS
+extern int isatty P((int));
+#endif
+
+extern void resetup P((void));
+
+/* These nodes store all the special variables AWK uses */
+NODE *FS_node, *NF_node, *RS_node, *NR_node;
+NODE *FILENAME_node, *OFS_node, *ORS_node, *OFMT_node;
+NODE *CONVFMT_node;
+NODE *ERRNO_node;
+NODE *FNR_node, *RLENGTH_node, *RSTART_node, *SUBSEP_node;
+NODE *ENVIRON_node, *IGNORECASE_node;
+NODE *ARGC_node, *ARGV_node, *ARGIND_node;
+NODE *FIELDWIDTHS_node;
+
+long NF;
+long NR;
+long FNR;
+int IGNORECASE;
+char *RS;
+char *OFS;
+char *ORS;
+char *OFMT;
+char *CONVFMT;
+
+/*
+ * The parse tree and field nodes are stored here. Parse_end is a dummy item
+ * used to free up unneeded fields without freeing the program being run
+ */
+int errcount = 0; /* error counter, used by yyerror() */
+
+/* The global null string */
+NODE *Nnull_string;
+
+/* The name the program was invoked under, for error messages */
+const char *myname;
+
+/* A block of AWK code to be run before running the program */
+NODE *begin_block = 0;
+
+/* A block of AWK code to be run after the last input file */
+NODE *end_block = 0;
+
+int exiting = 0; /* Was an "exit" statement executed? */
+int exit_val = 0; /* optional exit value */
+
+#if defined(YYDEBUG) || defined(DEBUG)
+extern int yydebug;
+#endif
+
+struct src *srcfiles = NULL; /* source file name(s) */
+int numfiles = -1; /* how many source files */
+
+int do_unix = 0; /* turn off gnu extensions */
+int do_posix = 0; /* turn off gnu and unix extensions */
+int do_lint = 0; /* provide warnings about questionable stuff */
+int do_nostalgia = 0; /* provide a blast from the past */
+
+int in_begin_rule = 0; /* we're in a BEGIN rule */
+int in_end_rule = 0; /* we're in a END rule */
+
+int output_is_tty = 0; /* control flushing of output */
+
+extern char *version_string; /* current version, for printing */
+
+NODE *expression_value;
+
+static struct option optab[] = {
+ { "compat", no_argument, & do_unix, 1 },
+ { "lint", no_argument, & do_lint, 1 },
+ { "posix", no_argument, & do_posix, 1 },
+ { "nostalgia", no_argument, & do_nostalgia, 1 },
+ { "copyleft", no_argument, NULL, 'C' },
+ { "copyright", no_argument, NULL, 'C' },
+ { "field-separator", required_argument, NULL, 'F' },
+ { "file", required_argument, NULL, 'f' },
+ { "assign", required_argument, NULL, 'v' },
+ { "version", no_argument, NULL, 'V' },
+ { "usage", no_argument, NULL, 'u' },
+ { "help", no_argument, NULL, 'u' },
+ { "source", required_argument, NULL, 's' },
+#ifdef DEBUG
+ { "parsedebug", no_argument, NULL, 'D' },
+#endif
+ { 0, 0, 0, 0 }
+};
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ int c;
+ char *scan;
+ /* the + on the front tells GNU getopt not to rearrange argv */
+ const char *optlist = "+F:f:v:W:m:";
+ int stopped_early = 0;
+ int old_optind;
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+
+#ifdef __EMX__
+ _response(&argc, &argv);
+ _wildcard(&argc, &argv);
+ setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
+#endif
+
+ (void) signal(SIGFPE, (SIGTYPE (*) P((int))) catchsig);
+ (void) signal(SIGSEGV, (SIGTYPE (*) P((int))) catchsig);
+#ifdef SIGBUS
+ (void) signal(SIGBUS, (SIGTYPE (*) P((int))) catchsig);
+#endif
+
+ myname = gawk_name(argv[0]);
+ argv[0] = (char *)myname;
+#ifdef VMS
+ vms_arg_fixup(&argc, &argv); /* emulate redirection, expand wildcards */
+#endif
+
+ /* remove sccs gunk */
+ if (strncmp(version_string, "@(#)", 4) == 0)
+ version_string += 4;
+
+ if (argc < 2)
+ usage(1);
+
+ /* initialize the null string */
+ Nnull_string = make_string("", 0);
+ Nnull_string->numbr = 0.0;
+ Nnull_string->type = Node_val;
+ Nnull_string->flags = (PERM|STR|STRING|NUM|NUMBER);
+
+ /* Set up the special variables */
+ /*
+ * Note that this must be done BEFORE arg parsing else -F
+ * breaks horribly
+ */
+ init_vars();
+
+ /* worst case */
+ emalloc(srcfiles, struct src *, argc * sizeof(struct src), "main");
+ memset(srcfiles, '\0', argc * sizeof(struct src));
+
+ /* Tell the regex routines how they should work. . . */
+ resetup();
+
+#ifdef fpsetmask
+ fpsetmask(~0xff);
+#endif
+ /* we do error messages ourselves on invalid options */
+ opterr = 0;
+
+ /* option processing. ready, set, go! */
+ for (optopt = 0, old_optind = 1;
+ (c = getopt_long(argc, argv, optlist, optab, NULL)) != EOF;
+ optopt = 0, old_optind = optind) {
+ if (do_posix)
+ opterr = 1;
+ switch (c) {
+ case 'F':
+ cmdline_fs(optarg);
+ break;
+
+ case 'f':
+ /*
+ * a la MKS awk, allow multiple -f options.
+ * this makes function libraries real easy.
+ * most of the magic is in the scanner.
+ */
+ /* The following is to allow for whitespace at the end
+ * of a #! /bin/gawk line in an executable file
+ */
+ scan = optarg;
+ while (isspace(*scan))
+ scan++;
+ ++numfiles;
+ srcfiles[numfiles].stype = SOURCEFILE;
+ if (*scan == '\0')
+ srcfiles[numfiles].val = argv[optind++];
+ else
+ srcfiles[numfiles].val = optarg;
+ break;
+
+ case 'v':
+ pre_assign(optarg);
+ break;
+
+ case 'm':
+ /*
+ * Research awk extension.
+ * -mf=nnn set # fields, gawk ignores
+ * -mr=nnn set record length, ditto
+ */
+ if (do_lint)
+ warning("-m[fr] option irrelevant");
+ if ((optarg[0] != 'r' && optarg[0] != 'f')
+ || optarg[1] != '=')
+ warning("-m option usage: -m[fn]=nnn");
+ break;
+
+ case 'W': /* gawk specific options */
+ gawk_option(optarg);
+ break;
+
+ /* These can only come from long form options */
+ case 'V':
+ version();
+ break;
+
+ case 'C':
+ copyleft();
+ break;
+
+ case 'u':
+ usage(0);
+ break;
+
+ case 's':
+ if (optarg[0] == '\0')
+ warning("empty argument to --source ignored");
+ else {
+ srcfiles[++numfiles].stype = CMDLINE;
+ srcfiles[numfiles].val = optarg;
+ }
+ break;
+
+#ifdef DEBUG
+ case 'D':
+ yydebug = 2;
+ break;
+#endif
+
+ case 0:
+ /*
+ * getopt_long found an option that sets a variable
+ * instead of returning a letter. Do nothing, just
+ * cycle around for the next one.
+ */
+ break;
+
+ case '?':
+ default:
+ /*
+ * New behavior. If not posix, an unrecognized
+ * option stops argument processing so that it can
+ * go into ARGV for the awk program to see. This
+ * makes use of ``#! /bin/gawk -f'' easier.
+ *
+ * However, it's never simple. If optopt is set,
+ * an option that requires an argument didn't get the
+ * argument. We care because if opterr is 0, then
+ * getopt_long won't print the error message for us.
+ */
+ if (! do_posix
+ && (optopt == 0 || strchr(optlist, optopt) == NULL)) {
+ /*
+ * can't just do optind--. In case of an
+ * option with >=2 letters, getopt_long
+ * won't have incremented optind.
+ */
+ optind = old_optind;
+ stopped_early = 1;
+ goto out;
+ } else if (optopt)
+ /* Use 1003.2 required message format */
+ fprintf (stderr,
+ "%s: option requires an argument -- %c\n",
+ myname, optopt);
+ /* else
+ let getopt print error message for us */
+ break;
+ }
+ }
+out:
+
+ if (do_nostalgia)
+ nostalgia();
+
+ /* check for POSIXLY_CORRECT environment variable */
+ if (! do_posix && getenv("POSIXLY_CORRECT") != NULL) {
+ do_posix = 1;
+ if (do_lint)
+ warning(
+ "environment variable `POSIXLY_CORRECT' set: turning on --posix");
+ }
+
+ /* POSIX compliance also implies no Unix extensions either */
+ if (do_posix)
+ do_unix = 1;
+
+#ifdef DEBUG
+ setbuf(stdout, (char *) NULL); /* make debugging easier */
+#endif
+ if (isatty(fileno(stdout)))
+ output_is_tty = 1;
+ /* No -f or --source options, use next arg */
+ if (numfiles == -1) {
+ if (optind > argc - 1 || stopped_early) /* no args left or no program */
+ usage(1);
+ srcfiles[++numfiles].stype = CMDLINE;
+ srcfiles[numfiles].val = argv[optind];
+ optind++;
+ }
+ init_args(optind, argc, (char *) myname, argv);
+ (void) tokexpand();
+
+ /* Read in the program */
+ if (yyparse() || errcount)
+ exit(1);
+
+ /* Set up the field variables */
+ init_fields();
+
+ if (do_lint && begin_block == NULL && expression_value == NULL
+ && end_block == NULL)
+ warning("no program");
+
+ if (begin_block) {
+ in_begin_rule = 1;
+ (void) interpret(begin_block);
+ }
+ in_begin_rule = 0;
+ if (!exiting && (expression_value || end_block))
+ do_input();
+ if (end_block) {
+ in_end_rule = 1;
+ (void) interpret(end_block);
+ }
+ in_end_rule = 0;
+ if (close_io() != 0 && exit_val == 0)
+ exit_val = 1;
+ exit(exit_val); /* more portable */
+ return exit_val; /* to suppress warnings */
+}
+
+/* usage --- print usage information and exit */
+
+static void
+usage(exitval)
+int exitval;
+{
+ const char *opt1 = " -f progfile [--]";
+#if defined(MSDOS) || defined(OS2) || defined(VMS)
+ const char *opt2 = " [--] \"program\"";
+#else
+ const char *opt2 = " [--] 'program'";
+#endif
+ const char *regops = " [POSIX or GNU style options]";
+
+ fprintf(stderr, "Usage:\t%s%s%s file ...\n\t%s%s%s file ...\n",
+ myname, regops, opt1, myname, regops, opt2);
+
+ /* GNU long options info. Gack. */
+ fputs("POSIX options:\t\tGNU long options:\n", stderr);
+ fputs("\t-f progfile\t\t--file=progfile\n", stderr);
+ fputs("\t-F fs\t\t\t--field-separator=fs\n", stderr);
+ fputs("\t-v var=val\t\t--assign=var=val\n", stderr);
+ fputs("\t-m[fr]=val\n", stderr);
+ fputs("\t-W compat\t\t--compat\n", stderr);
+ fputs("\t-W copyleft\t\t--copyleft\n", stderr);
+ fputs("\t-W copyright\t\t--copyright\n", stderr);
+ fputs("\t-W help\t\t\t--help\n", stderr);
+ fputs("\t-W lint\t\t\t--lint\n", stderr);
+#ifdef NOSTALGIA
+ fputs("\t-W nostalgia\t\t--nostalgia\n", stderr);
+#endif
+#ifdef DEBUG
+ fputs("\t-W parsedebug\t\t--parsedebug\n", stderr);
+#endif
+ fputs("\t-W posix\t\t--posix\n", stderr);
+ fputs("\t-W source=program-text\t--source=program-text\n", stderr);
+ fputs("\t-W usage\t\t--usage\n", stderr);
+ fputs("\t-W version\t\t--version\n", stderr);
+ exit(exitval);
+}
+
+static void
+copyleft ()
+{
+ static char blurb_part1[] =
+"Copyright (C) 1989, 1991, 1992, Free Software Foundation.\n\
+\n\
+This program is free software; you can redistribute it and/or modify\n\
+it under the terms of the GNU General Public License as published by\n\
+the Free Software Foundation; either version 2 of the License, or\n\
+(at your option) any later version.\n\
+\n";
+ static char blurb_part2[] =
+"This program is distributed in the hope that it will be useful,\n\
+but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
+GNU General Public License for more details.\n\
+\n";
+ static char blurb_part3[] =
+"You should have received a copy of the GNU General Public License\n\
+along with this program; if not, write to the Free Software\n\
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n";
+
+ fputs(blurb_part1, stderr);
+ fputs(blurb_part2, stderr);
+ fputs(blurb_part3, stderr);
+ fflush(stderr);
+}
+
+static void
+cmdline_fs(str)
+char *str;
+{
+ register NODE **tmp;
+ /* int len = strlen(str); *//* don't do that - we want to
+ avoid mismatched types */
+
+ tmp = get_lhs(FS_node, (Func_ptr *) 0);
+ unref(*tmp);
+ /*
+ * Only if in full compatibility mode check for the stupid special
+ * case so -F\t works as documented in awk even though the shell
+ * hands us -Ft. Bleah!
+ *
+ * Thankfully, Posix didn't propogate this "feature".
+ */
+ if (str[0] == 't' && str[1] == '\0') {
+ if (do_lint)
+ warning("-Ft does not set FS to tab in POSIX awk");
+ if (do_unix && ! do_posix)
+ str[0] = '\t';
+ }
+ *tmp = make_str_node(str, strlen(str), SCAN); /* do process escapes */
+ set_FS();
+}
+
+static void
+init_args(argc0, argc, argv0, argv)
+int argc0, argc;
+char *argv0;
+char **argv;
+{
+ int i, j;
+ NODE **aptr;
+
+ ARGV_node = install("ARGV", node(Nnull_string, Node_var, (NODE *)NULL));
+ aptr = assoc_lookup(ARGV_node, tmp_number(0.0));
+ *aptr = make_string(argv0, strlen(argv0));
+ (*aptr)->flags |= MAYBE_NUM;
+ for (i = argc0, j = 1; i < argc; i++) {
+ aptr = assoc_lookup(ARGV_node, tmp_number((AWKNUM) j));
+ *aptr = make_string(argv[i], strlen(argv[i]));
+ (*aptr)->flags |= MAYBE_NUM;
+ j++;
+ }
+ ARGC_node = install("ARGC",
+ node(make_number((AWKNUM) j), Node_var, (NODE *) NULL));
+}
+
+/*
+ * Set all the special variables to their initial values.
+ */
+struct varinit {
+ NODE **spec;
+ const char *name;
+ NODETYPE type;
+ const char *strval;
+ AWKNUM numval;
+ Func_ptr assign;
+};
+static struct varinit varinit[] = {
+{&NF_node, "NF", Node_NF, 0, -1, set_NF },
+{&FIELDWIDTHS_node, "FIELDWIDTHS", Node_FIELDWIDTHS, "", 0, 0 },
+{&NR_node, "NR", Node_NR, 0, 0, set_NR },
+{&FNR_node, "FNR", Node_FNR, 0, 0, set_FNR },
+{&FS_node, "FS", Node_FS, " ", 0, 0 },
+{&RS_node, "RS", Node_RS, "\n", 0, set_RS },
+{&IGNORECASE_node, "IGNORECASE", Node_IGNORECASE, 0, 0, set_IGNORECASE },
+{&FILENAME_node, "FILENAME", Node_var, "", 0, 0 },
+{&OFS_node, "OFS", Node_OFS, " ", 0, set_OFS },
+{&ORS_node, "ORS", Node_ORS, "\n", 0, set_ORS },
+{&OFMT_node, "OFMT", Node_OFMT, "%.6g", 0, set_OFMT },
+{&CONVFMT_node, "CONVFMT", Node_CONVFMT, "%.6g", 0, set_CONVFMT },
+{&RLENGTH_node, "RLENGTH", Node_var, 0, 0, 0 },
+{&RSTART_node, "RSTART", Node_var, 0, 0, 0 },
+{&SUBSEP_node, "SUBSEP", Node_var, "\034", 0, 0 },
+{&ARGIND_node, "ARGIND", Node_var, 0, 0, 0 },
+{&ERRNO_node, "ERRNO", Node_var, 0, 0, 0 },
+{0, 0, Node_illegal, 0, 0, 0 },
+};
+
+static void
+init_vars()
+{
+ register struct varinit *vp;
+
+ for (vp = varinit; vp->name; vp++) {
+ *(vp->spec) = install((char *) vp->name,
+ node(vp->strval == 0 ? make_number(vp->numval)
+ : make_string((char *) vp->strval,
+ strlen(vp->strval)),
+ vp->type, (NODE *) NULL));
+ if (vp->assign)
+ (*(vp->assign))();
+ }
+}
+
+void
+load_environ()
+{
+#if !defined(MSDOS) && !defined(OS2) && !(defined(VMS) && defined(__DECC))
+ extern char **environ;
+#endif
+ register char *var, *val;
+ NODE **aptr;
+ register int i;
+
+ ENVIRON_node = install("ENVIRON",
+ node(Nnull_string, Node_var, (NODE *) NULL));
+ for (i = 0; environ[i]; i++) {
+ static char nullstr[] = "";
+
+ var = environ[i];
+ val = strchr(var, '=');
+ if (val)
+ *val++ = '\0';
+ else
+ val = nullstr;
+ aptr = assoc_lookup(ENVIRON_node, tmp_string(var, strlen (var)));
+ *aptr = make_string(val, strlen (val));
+ (*aptr)->flags |= MAYBE_NUM;
+
+ /* restore '=' so that system() gets a valid environment */
+ if (val != nullstr)
+ *--val = '=';
+ }
+}
+
+/* Process a command-line assignment */
+char *
+arg_assign(arg)
+char *arg;
+{
+ char *cp, *cp2;
+ int badvar;
+ Func_ptr after_assign = NULL;
+ NODE *var;
+ NODE *it;
+ NODE **lhs;
+
+ cp = strchr(arg, '=');
+ if (cp != NULL) {
+ *cp++ = '\0';
+ /* first check that the variable name has valid syntax */
+ badvar = 0;
+ if (! isalpha(arg[0]) && arg[0] != '_')
+ badvar = 1;
+ else
+ for (cp2 = arg+1; *cp2; cp2++)
+ if (! isalnum(*cp2) && *cp2 != '_') {
+ badvar = 1;
+ break;
+ }
+ if (badvar)
+ fatal("illegal name `%s' in variable assignment", arg);
+
+ /*
+ * Recent versions of nawk expand escapes inside assignments.
+ * This makes sense, so we do it too.
+ */
+ it = make_str_node(cp, strlen(cp), SCAN);
+ it->flags |= MAYBE_NUM;
+ var = variable(arg, 0);
+ lhs = get_lhs(var, &after_assign);
+ unref(*lhs);
+ *lhs = it;
+ if (after_assign)
+ (*after_assign)();
+ *--cp = '='; /* restore original text of ARGV */
+ }
+ return cp;
+}
+
+static void
+pre_assign(v)
+char *v;
+{
+ if (!arg_assign(v)) {
+ fprintf (stderr,
+ "%s: '%s' argument to -v not in 'var=value' form\n",
+ myname, v);
+ usage(1);
+ }
+}
+
+SIGTYPE
+catchsig(sig, code)
+int sig, code;
+{
+#ifdef lint
+ code = 0; sig = code; code = sig;
+#endif
+ if (sig == SIGFPE) {
+ fatal("floating point exception");
+ } else if (sig == SIGSEGV
+#ifdef SIGBUS
+ || sig == SIGBUS
+#endif
+ ) {
+ msg("fatal error: internal error");
+ /* fatal won't abort() if not compiled for debugging */
+ abort();
+ } else
+ cant_happen();
+ /* NOTREACHED */
+}
+
+/* gawk_option --- do gawk specific things */
+
+static void
+gawk_option(optstr)
+char *optstr;
+{
+ char *cp;
+
+ for (cp = optstr; *cp; cp++) {
+ switch (*cp) {
+ case ' ':
+ case '\t':
+ case ',':
+ break;
+ case 'v':
+ case 'V':
+ /* print version */
+ if (strncasecmp(cp, "version", 7) != 0)
+ goto unknown;
+ else
+ cp += 6;
+ version();
+ break;
+ case 'c':
+ case 'C':
+ if (strncasecmp(cp, "copyright", 9) == 0) {
+ cp += 8;
+ copyleft();
+ } else if (strncasecmp(cp, "copyleft", 8) == 0) {
+ cp += 7;
+ copyleft();
+ } else if (strncasecmp(cp, "compat", 6) == 0) {
+ cp += 5;
+ do_unix = 1;
+ } else
+ goto unknown;
+ break;
+ case 'n':
+ case 'N':
+ /*
+ * Undocumented feature,
+ * inspired by nostalgia, and a T-shirt
+ */
+ if (strncasecmp(cp, "nostalgia", 9) != 0)
+ goto unknown;
+ nostalgia();
+ break;
+ case 'p':
+ case 'P':
+#ifdef DEBUG
+ if (strncasecmp(cp, "parsedebug", 10) == 0) {
+ cp += 9;
+ yydebug = 2;
+ break;
+ }
+#endif
+ if (strncasecmp(cp, "posix", 5) != 0)
+ goto unknown;
+ cp += 4;
+ do_posix = do_unix = 1;
+ break;
+ case 'l':
+ case 'L':
+ if (strncasecmp(cp, "lint", 4) != 0)
+ goto unknown;
+ cp += 3;
+ do_lint = 1;
+ break;
+ case 'H':
+ case 'h':
+ if (strncasecmp(cp, "help", 4) != 0)
+ goto unknown;
+ cp += 3;
+ usage(0);
+ break;
+ case 'U':
+ case 'u':
+ if (strncasecmp(cp, "usage", 5) != 0)
+ goto unknown;
+ cp += 4;
+ usage(0);
+ break;
+ case 's':
+ case 'S':
+ if (strncasecmp(cp, "source=", 7) != 0)
+ goto unknown;
+ cp += 7;
+ if (cp[0] == '\0')
+ warning("empty argument to -Wsource ignored");
+ else {
+ srcfiles[++numfiles].stype = CMDLINE;
+ srcfiles[numfiles].val = cp;
+ return;
+ }
+ break;
+ default:
+ unknown:
+ fprintf(stderr, "'%c' -- unknown option, ignored\n",
+ *cp);
+ break;
+ }
+ }
+}
+
+/* nostalgia --- print the famous error message and die */
+
+static void
+nostalgia()
+{
+ fprintf(stderr, "awk: bailing out near line 1\n");
+ abort();
+}
+
+/* version --- print version message */
+
+static void
+version()
+{
+ fprintf(stderr, "%s, patchlevel %d\n", version_string, PATCHLEVEL);
+ /* per GNU coding standards, exit successfully, do nothing else */
+ exit(0);
+}
+
+/* this mess will improve in 2.16 */
+char *
+gawk_name(filespec)
+char *filespec;
+{
+ char *p;
+
+#ifdef VMS /* "device:[root.][directory.subdir]GAWK.EXE;n" -> "GAWK" */
+ char *q;
+
+ p = strrchr(filespec, ']'); /* directory punctuation */
+ q = strrchr(filespec, '>'); /* alternate <international> punct */
+
+ if (p == NULL || q > p) p = q;
+ p = strdup(p == NULL ? filespec : (p + 1));
+ if ((q = strrchr(p, '.')) != NULL) *q = '\0'; /* strip .typ;vers */
+
+ return p;
+#endif /*VMS*/
+
+#if defined(MSDOS) || defined(OS2) || defined(atarist)
+ char *q;
+
+ for (p = filespec; (p = strchr(p, '\\')); *p = '/')
+ ;
+ p = filespec;
+ if ((q = strrchr(p, '/')))
+ p = q + 1;
+ if ((q = strchr(p, '.')))
+ *q = '\0';
+ strlwr(p);
+
+ return (p == NULL ? filespec : p);
+#endif /* MSDOS || atarist */
+
+ /* "path/name" -> "name" */
+ p = strrchr(filespec, '/');
+ return (p == NULL ? filespec : p + 1);
+}
diff --git a/gnu/usr.bin/awk/msg.c b/gnu/usr.bin/awk/msg.c
new file mode 100644
index 0000000..4bd9f90
--- /dev/null
+++ b/gnu/usr.bin/awk/msg.c
@@ -0,0 +1,107 @@
+/*
+ * msg.c - routines for error messages
+ */
+
+/*
+ * Copyright (C) 1986, 1988, 1989, 1991, 1992, 1993 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Progamming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GAWK; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "awk.h"
+
+int sourceline = 0;
+char *source = NULL;
+
+/* VARARGS2 */
+void
+err(s, emsg, argp)
+const char *s;
+const char *emsg;
+va_list argp;
+{
+ char *file;
+
+ (void) fflush(stdout);
+ (void) fprintf(stderr, "%s: ", myname);
+ if (sourceline) {
+ if (source)
+ (void) fprintf(stderr, "%s:", source);
+ else
+ (void) fprintf(stderr, "cmd. line:");
+
+ (void) fprintf(stderr, "%d: ", sourceline);
+ }
+ if (FNR) {
+ file = FILENAME_node->var_value->stptr;
+ (void) putc('(', stderr);
+ if (file)
+ (void) fprintf(stderr, "FILENAME=%s ", file);
+ (void) fprintf(stderr, "FNR=%ld) ", FNR);
+ }
+ (void) fprintf(stderr, s);
+ vfprintf(stderr, emsg, argp);
+ (void) fprintf(stderr, "\n");
+ (void) fflush(stderr);
+}
+
+/*VARARGS0*/
+void
+msg(va_alist)
+va_dcl
+{
+ va_list args;
+ char *mesg;
+
+ va_start(args);
+ mesg = va_arg(args, char *);
+ err("", mesg, args);
+ va_end(args);
+}
+
+/*VARARGS0*/
+void
+warning(va_alist)
+va_dcl
+{
+ va_list args;
+ char *mesg;
+
+ va_start(args);
+ mesg = va_arg(args, char *);
+ err("warning: ", mesg, args);
+ va_end(args);
+}
+
+/*VARARGS0*/
+void
+fatal(va_alist)
+va_dcl
+{
+ va_list args;
+ char *mesg;
+
+ va_start(args);
+ mesg = va_arg(args, char *);
+ err("fatal: ", mesg, args);
+ va_end(args);
+#ifdef DEBUG
+ abort();
+#endif
+ exit(2);
+}
diff --git a/gnu/usr.bin/awk/node.c b/gnu/usr.bin/awk/node.c
new file mode 100644
index 0000000..dca4ad1
--- /dev/null
+++ b/gnu/usr.bin/awk/node.c
@@ -0,0 +1,463 @@
+/*
+ * node.c -- routines for node management
+ */
+
+/*
+ * Copyright (C) 1986, 1988, 1989, 1991, 1992, 1993 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Progamming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GAWK; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "awk.h"
+
+extern double strtod();
+
+AWKNUM
+r_force_number(n)
+register NODE *n;
+{
+ register char *cp;
+ register char *cpend;
+ char save;
+ char *ptr;
+ unsigned int newflags;
+
+#ifdef DEBUG
+ if (n == NULL)
+ cant_happen();
+ if (n->type != Node_val)
+ cant_happen();
+ if(n->flags == 0)
+ cant_happen();
+ if (n->flags & NUM)
+ return n->numbr;
+#endif
+
+ /* all the conditionals are an attempt to avoid the expensive strtod */
+
+ n->numbr = 0.0;
+ n->flags |= NUM;
+
+ if (n->stlen == 0)
+ return 0.0;
+
+ cp = n->stptr;
+ if (isalpha(*cp))
+ return 0.0;
+
+ cpend = cp + n->stlen;
+ while (cp < cpend && isspace(*cp))
+ cp++;
+ if (cp == cpend || isalpha(*cp))
+ return 0.0;
+
+ if (n->flags & MAYBE_NUM) {
+ newflags = NUMBER;
+ n->flags &= ~MAYBE_NUM;
+ } else
+ newflags = 0;
+ if (cpend - cp == 1) {
+ if (isdigit(*cp)) {
+ n->numbr = (AWKNUM)(*cp - '0');
+ n->flags |= newflags;
+ }
+ return n->numbr;
+ }
+
+ errno = 0;
+ save = *cpend;
+ *cpend = '\0';
+ n->numbr = (AWKNUM) strtod((const char *)cp, &ptr);
+
+ /* POSIX says trailing space is OK for NUMBER */
+ while (isspace(*ptr))
+ ptr++;
+ *cpend = save;
+ /* the >= should be ==, but for SunOS 3.5 strtod() */
+ if (errno == 0 && ptr >= cpend)
+ n->flags |= newflags;
+ else
+ errno = 0;
+
+ return n->numbr;
+}
+
+/*
+ * the following lookup table is used as an optimization in force_string
+ * (more complicated) variations on this theme didn't seem to pay off, but
+ * systematic testing might be in order at some point
+ */
+static const char *values[] = {
+ "0",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+};
+#define NVAL (sizeof(values)/sizeof(values[0]))
+
+NODE *
+r_force_string(s)
+register NODE *s;
+{
+ char buf[128];
+ register char *sp = buf;
+ double val;
+
+#ifdef DEBUG
+ if (s == NULL) cant_happen();
+ if (s->type != Node_val) cant_happen();
+ if (s->flags & STR) return s;
+ if (!(s->flags & NUM)) cant_happen();
+ if (s->stref != 0) ; /*cant_happen();*/
+#endif
+
+ /* not an integral value, or out of range */
+ if ((val = double_to_int(s->numbr)) != s->numbr
+ || val < LONG_MIN || val > LONG_MAX) {
+#ifdef GFMT_WORKAROUND
+ NODE *dummy, *r;
+ unsigned short oflags;
+ extern NODE *format_tree P((const char *, int, NODE *));
+ extern NODE **fmt_list; /* declared in eval.c */
+
+ /* create dummy node for a sole use of format_tree */
+ getnode(dummy);
+ dummy->lnode = s;
+ dummy->rnode = NULL;
+ oflags = s->flags;
+ s->flags |= PERM; /* prevent from freeing by format_tree() */
+ r = format_tree(CONVFMT, fmt_list[CONVFMTidx]->stlen, dummy);
+ s->flags = oflags;
+ s->stfmt = (char)CONVFMTidx;
+ s->stlen = r->stlen;
+ s->stptr = r->stptr;
+ freenode(r); /* Do not free_temp(r)! We want */
+ freenode(dummy); /* to keep s->stptr == r->stpr. */
+
+ goto no_malloc;
+#else
+ /*
+ * no need for a "replacement" formatting by gawk,
+ * just use sprintf
+ */
+ sprintf(sp, CONVFMT, s->numbr);
+ s->stlen = strlen(sp);
+ s->stfmt = (char)CONVFMTidx;
+#endif /* GFMT_WORKAROUND */
+ } else {
+ /* integral value */
+ /* force conversion to long only once */
+ register long num = (long) val;
+ if (num < NVAL && num >= 0) {
+ sp = (char *) values[num];
+ s->stlen = 1;
+ } else {
+ (void) sprintf(sp, "%ld", num);
+ s->stlen = strlen(sp);
+ }
+ s->stfmt = -1;
+ }
+ emalloc(s->stptr, char *, s->stlen + 2, "force_string");
+ memcpy(s->stptr, sp, s->stlen+1);
+no_malloc:
+ s->stref = 1;
+ s->flags |= STR;
+ return s;
+}
+
+/*
+ * Duplicate a node. (For strings, "duplicate" means crank up the
+ * reference count.)
+ */
+NODE *
+dupnode(n)
+NODE *n;
+{
+ register NODE *r;
+
+ if (n->flags & TEMP) {
+ n->flags &= ~TEMP;
+ n->flags |= MALLOC;
+ return n;
+ }
+ if ((n->flags & (MALLOC|STR)) == (MALLOC|STR)) {
+ if (n->stref < 255)
+ n->stref++;
+ return n;
+ }
+ getnode(r);
+ *r = *n;
+ r->flags &= ~(PERM|TEMP);
+ r->flags |= MALLOC;
+ if (n->type == Node_val && (n->flags & STR)) {
+ r->stref = 1;
+ emalloc(r->stptr, char *, r->stlen + 2, "dupnode");
+ memcpy(r->stptr, n->stptr, r->stlen);
+ r->stptr[r->stlen] = '\0';
+ }
+ return r;
+}
+
+/* this allocates a node with defined numbr */
+NODE *
+mk_number(x, flags)
+AWKNUM x;
+unsigned int flags;
+{
+ register NODE *r;
+
+ getnode(r);
+ r->type = Node_val;
+ r->numbr = x;
+ r->flags = flags;
+#ifdef DEBUG
+ r->stref = 1;
+ r->stptr = 0;
+ r->stlen = 0;
+#endif
+ return r;
+}
+
+/*
+ * Make a string node.
+ */
+NODE *
+make_str_node(s, len, flags)
+char *s;
+size_t len;
+int flags;
+{
+ register NODE *r;
+
+ getnode(r);
+ r->type = Node_val;
+ r->flags = (STRING|STR|MALLOC);
+ if (flags & ALREADY_MALLOCED)
+ r->stptr = s;
+ else {
+ emalloc(r->stptr, char *, len + 2, s);
+ memcpy(r->stptr, s, len);
+ }
+ r->stptr[len] = '\0';
+
+ if (flags & SCAN) { /* scan for escape sequences */
+ char *pf;
+ register char *ptm;
+ register int c;
+ register char *end;
+
+ end = &(r->stptr[len]);
+ for (pf = ptm = r->stptr; pf < end;) {
+ c = *pf++;
+ if (c == '\\') {
+ c = parse_escape(&pf);
+ if (c < 0) {
+ if (do_lint)
+ warning("backslash at end of string");
+ c = '\\';
+ }
+ *ptm++ = c;
+ } else
+ *ptm++ = c;
+ }
+ len = ptm - r->stptr;
+ erealloc(r->stptr, char *, len + 1, "make_str_node");
+ r->stptr[len] = '\0';
+ r->flags |= PERM;
+ }
+ r->stlen = len;
+ r->stref = 1;
+ r->stfmt = -1;
+
+ return r;
+}
+
+NODE *
+tmp_string(s, len)
+char *s;
+size_t len;
+{
+ register NODE *r;
+
+ r = make_string(s, len);
+ r->flags |= TEMP;
+ return r;
+}
+
+
+#define NODECHUNK 100
+
+NODE *nextfree = NULL;
+
+NODE *
+more_nodes()
+{
+ register NODE *np;
+
+ /* get more nodes and initialize list */
+ emalloc(nextfree, NODE *, NODECHUNK * sizeof(NODE), "newnode");
+ for (np = nextfree; np < &nextfree[NODECHUNK - 1]; np++) {
+ np->flags = 0;
+ np->nextp = np + 1;
+ }
+ np->nextp = NULL;
+ np = nextfree;
+ nextfree = nextfree->nextp;
+ return np;
+}
+
+#ifdef DEBUG
+void
+freenode(it)
+NODE *it;
+{
+#ifdef MPROF
+ it->stref = 0;
+ free((char *) it);
+#else /* not MPROF */
+ /* add it to head of freelist */
+ it->nextp = nextfree;
+ nextfree = it;
+#endif /* not MPROF */
+}
+#endif /* DEBUG */
+
+void
+unref(tmp)
+register NODE *tmp;
+{
+ if (tmp == NULL)
+ return;
+ if (tmp->flags & PERM)
+ return;
+ if (tmp->flags & (MALLOC|TEMP)) {
+ tmp->flags &= ~TEMP;
+ if (tmp->flags & STR) {
+ if (tmp->stref > 1) {
+ if (tmp->stref != 255)
+ tmp->stref--;
+ return;
+ }
+ free(tmp->stptr);
+ }
+ freenode(tmp);
+ }
+}
+
+/*
+ * Parse a C escape sequence. STRING_PTR points to a variable containing a
+ * pointer to the string to parse. That pointer is updated past the
+ * characters we use. The value of the escape sequence is returned.
+ *
+ * A negative value means the sequence \ newline was seen, which is supposed to
+ * be equivalent to nothing at all.
+ *
+ * If \ is followed by a null character, we return a negative value and leave
+ * the string pointer pointing at the null character.
+ *
+ * If \ is followed by 000, we return 0 and leave the string pointer after the
+ * zeros. A value of 0 does not mean end of string.
+ *
+ * Posix doesn't allow \x.
+ */
+
+int
+parse_escape(string_ptr)
+char **string_ptr;
+{
+ register int c = *(*string_ptr)++;
+ register int i;
+ register int count;
+
+ switch (c) {
+ case 'a':
+ return BELL;
+ case 'b':
+ return '\b';
+ case 'f':
+ return '\f';
+ case 'n':
+ return '\n';
+ case 'r':
+ return '\r';
+ case 't':
+ return '\t';
+ case 'v':
+ return '\v';
+ case '\n':
+ return -2;
+ case 0:
+ (*string_ptr)--;
+ return -1;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ i = c - '0';
+ count = 0;
+ while (++count < 3) {
+ if ((c = *(*string_ptr)++) >= '0' && c <= '7') {
+ i *= 8;
+ i += c - '0';
+ } else {
+ (*string_ptr)--;
+ break;
+ }
+ }
+ return i;
+ case 'x':
+ if (do_lint) {
+ static int didwarn;
+
+ if (! didwarn) {
+ didwarn = 1;
+ warning("Posix does not allow \"\\x\" escapes");
+ }
+ }
+ if (do_posix)
+ return ('x');
+ i = 0;
+ while (1) {
+ if (isxdigit((c = *(*string_ptr)++))) {
+ i *= 16;
+ if (isdigit(c))
+ i += c - '0';
+ else if (isupper(c))
+ i += c - 'A' + 10;
+ else
+ i += c - 'a' + 10;
+ } else {
+ (*string_ptr)--;
+ break;
+ }
+ }
+ return i;
+ default:
+ return c;
+ }
+}
diff --git a/gnu/usr.bin/awk/patchlevel.h b/gnu/usr.bin/awk/patchlevel.h
new file mode 100644
index 0000000..c80ca15
--- /dev/null
+++ b/gnu/usr.bin/awk/patchlevel.h
@@ -0,0 +1 @@
+#define PATCHLEVEL 5
diff --git a/gnu/usr.bin/awk/protos.h b/gnu/usr.bin/awk/protos.h
new file mode 100644
index 0000000..1d4ac99
--- /dev/null
+++ b/gnu/usr.bin/awk/protos.h
@@ -0,0 +1,122 @@
+/*
+ * protos.h -- function prototypes for when the headers don't have them.
+ */
+
+/*
+ * Copyright (C) 1991, 1992, 1993 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Progamming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GAWK; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifdef __STDC__
+#define aptr_t void * /* arbitrary pointer type */
+#else
+#define aptr_t char *
+#endif
+extern aptr_t malloc P((MALLOC_ARG_T));
+extern aptr_t realloc P((aptr_t, MALLOC_ARG_T));
+extern aptr_t calloc P((MALLOC_ARG_T, MALLOC_ARG_T));
+
+#if !defined(sun) && !defined(__sun__)
+extern void free P((aptr_t));
+#endif
+extern char *getenv P((const char *));
+
+extern char *strcpy P((char *, const char *));
+extern char *strcat P((char *, const char *));
+extern int strcmp P((const char *, const char *));
+extern char *strncpy P((char *, const char *, size_t));
+extern int strncmp P((const char *, const char *, size_t));
+#ifndef VMS
+extern char *strerror P((int));
+#else
+extern char *strerror P((int,...));
+#endif
+extern char *strchr P((const char *, int));
+extern char *strrchr P((const char *, int));
+extern char *strstr P((const char *s1, const char *s2));
+extern size_t strlen P((const char *));
+extern long strtol P((const char *, char **, int));
+#if !defined(_MSC_VER) && !defined(__GNU_LIBRARY__)
+extern size_t strftime P((char *, size_t, const char *, const struct tm *));
+#endif
+#ifdef __STDC__
+extern time_t time P((time_t *));
+#else
+extern long time();
+#endif
+extern aptr_t memset P((aptr_t, int, size_t));
+extern aptr_t memcpy P((aptr_t, const aptr_t, size_t));
+extern aptr_t memmove P((aptr_t, const aptr_t, size_t));
+extern aptr_t memchr P((const aptr_t, int, size_t));
+extern int memcmp P((const aptr_t, const aptr_t, size_t));
+
+extern int fprintf P((FILE *, const char *, ...));
+#if !defined(MSDOS) && !defined(__GNU_LIBRARY__)
+#ifdef __STDC__
+extern size_t fwrite P((const aptr_t, size_t, size_t, FILE *));
+#else
+extern int fwrite();
+#endif
+extern int fputs P((const char *, FILE *));
+extern int unlink P((const char *));
+#endif
+extern int fflush P((FILE *));
+extern int fclose P((FILE *));
+extern FILE *popen P((const char *, const char *));
+extern int pclose P((FILE *));
+extern void abort P(());
+extern int isatty P((int));
+extern void exit P((int));
+extern int system P((const char *));
+extern int sscanf P((const char *, const char *, ...));
+#ifndef toupper
+extern int toupper P((int));
+#endif
+#ifndef tolower
+extern int tolower P((int));
+#endif
+
+extern double pow P((double x, double y));
+extern double atof P((const char *));
+extern double strtod P((const char *, char **));
+extern int fstat P((int, struct stat *));
+extern int stat P((const char *, struct stat *));
+extern off_t lseek P((int, off_t, int));
+extern int fseek P((FILE *, long, int));
+extern int close P((int));
+extern int creat P((const char *, mode_t));
+extern int open P((const char *, int, ...));
+extern int pipe P((int *));
+extern int dup P((int));
+extern int dup2 P((int,int));
+extern int fork P(());
+extern int execl P((/* char *, char *, ... */));
+#ifndef __STDC__
+extern int read P((int, char *, int));
+#endif
+extern int wait P((int *));
+extern void _exit P((int));
+
+#ifdef NON_STD_SPRINTF
+extern char *sprintf P((char *, const char*, ...));
+#else
+extern int sprintf P((char *, const char*, ...));
+#endif /* SPRINTF_INT */
+
+#undef aptr_t
diff --git a/gnu/usr.bin/awk/re.c b/gnu/usr.bin/awk/re.c
new file mode 100644
index 0000000..4ea22c2
--- /dev/null
+++ b/gnu/usr.bin/awk/re.c
@@ -0,0 +1,212 @@
+/*
+ * re.c - compile regular expressions.
+ */
+
+/*
+ * Copyright (C) 1991, 1992, 1993 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Progamming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GAWK; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "awk.h"
+
+/* Generate compiled regular expressions */
+
+Regexp *
+make_regexp(s, len, ignorecase, dfa)
+char *s;
+size_t len;
+int ignorecase;
+int dfa;
+{
+ Regexp *rp;
+ const char *rerr;
+ char *src = s;
+ char *temp;
+ char *end = s + len;
+ register char *dest;
+ register int c;
+
+ /* Handle escaped characters first. */
+
+ /* Build a copy of the string (in dest) with the
+ escaped characters translated, and generate the regex
+ from that.
+ */
+ emalloc(dest, char *, len + 2, "make_regexp");
+ temp = dest;
+
+ while (src < end) {
+ if (*src == '\\') {
+ c = *++src;
+ switch (c) {
+ case 'a':
+ case 'b':
+ case 'f':
+ case 'n':
+ case 'r':
+ case 't':
+ case 'v':
+ case 'x':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ c = parse_escape(&src);
+ if (c < 0)
+ cant_happen();
+ *dest++ = (char)c;
+ break;
+ default:
+ *dest++ = '\\';
+ *dest++ = (char)c;
+ src++;
+ break;
+ } /* switch */
+ } else {
+ *dest++ = *src++; /* not '\\' */
+ }
+ } /* for */
+
+ *dest = '\0' ; /* Only necessary if we print dest ? */
+ emalloc(rp, Regexp *, sizeof(*rp), "make_regexp");
+ memset((char *) rp, 0, sizeof(*rp));
+ emalloc(rp->pat.buffer, unsigned char *, 16, "make_regexp");
+ rp->pat.allocated = 16;
+ emalloc(rp->pat.fastmap, char *, 256, "make_regexp");
+
+ if (ignorecase)
+ rp->pat.translate = casetable;
+ else
+ rp->pat.translate = NULL;
+ len = dest - temp;
+ if ((rerr = re_compile_pattern(temp, len, &(rp->pat))) != NULL)
+ fatal("%s: /%s/", rerr, temp);
+ if (dfa && !ignorecase) {
+ dfacomp(temp, len, &(rp->dfareg), 1);
+ rp->dfa = 1;
+ } else
+ rp->dfa = 0;
+
+ free(temp);
+ return rp;
+}
+
+int
+research(rp, str, start, len, need_start)
+Regexp *rp;
+register char *str;
+int start;
+register size_t len;
+int need_start;
+{
+ char *ret = str;
+
+ if (rp->dfa) {
+ char save;
+ int count = 0;
+ int try_backref;
+
+ /*
+ * dfa likes to stick a '\n' right after the matched
+ * text. So we just save and restore the character.
+ */
+ save = str[start+len];
+ ret = dfaexec(&(rp->dfareg), str+start, str+start+len, 1,
+ &count, &try_backref);
+ str[start+len] = save;
+ }
+ if (ret) {
+ if (need_start || rp->dfa == 0)
+ return re_search(&(rp->pat), str, start+len, start,
+ len, &(rp->regs));
+ else
+ return 1;
+ } else
+ return -1;
+}
+
+void
+refree(rp)
+Regexp *rp;
+{
+ free(rp->pat.buffer);
+ free(rp->pat.fastmap);
+ if (rp->dfa)
+ dfafree(&(rp->dfareg));
+ free(rp);
+}
+
+void
+dfaerror(s)
+const char *s;
+{
+ fatal(s);
+}
+
+Regexp *
+re_update(t)
+NODE *t;
+{
+ NODE *t1;
+
+# define CASE 1
+ if ((t->re_flags & CASE) == IGNORECASE) {
+ if (t->re_flags & CONST)
+ return t->re_reg;
+ t1 = force_string(tree_eval(t->re_exp));
+ if (t->re_text) {
+ if (cmp_nodes(t->re_text, t1) == 0) {
+ free_temp(t1);
+ return t->re_reg;
+ }
+ unref(t->re_text);
+ }
+ t->re_text = dupnode(t1);
+ free_temp(t1);
+ }
+ if (t->re_reg)
+ refree(t->re_reg);
+ if (t->re_cnt)
+ t->re_cnt++;
+ if (t->re_cnt > 10)
+ t->re_cnt = 0;
+ if (!t->re_text) {
+ t1 = force_string(tree_eval(t->re_exp));
+ t->re_text = dupnode(t1);
+ free_temp(t1);
+ }
+ t->re_reg = make_regexp(t->re_text->stptr, t->re_text->stlen,
+ IGNORECASE, t->re_cnt);
+ t->re_flags &= ~CASE;
+ t->re_flags |= IGNORECASE;
+ return t->re_reg;
+}
+
+void
+resetup()
+{
+ reg_syntax_t syn = RE_SYNTAX_AWK;
+
+ (void) re_set_syntax(syn);
+ dfasyntax(syn, 0);
+}
diff --git a/gnu/usr.bin/awk/regex.c b/gnu/usr.bin/awk/regex.c
new file mode 100644
index 0000000..6a36f3d
--- /dev/null
+++ b/gnu/usr.bin/awk/regex.c
@@ -0,0 +1,5070 @@
+/* Extended regular expression matching and search library,
+ version 0.12.
+ (Implements POSIX draft P10003.2/D11.2, except for
+ internationalization features.)
+
+ Copyright (C) 1993 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* AIX requires this to be the first thing in the file. */
+#if defined (_AIX) && !defined (REGEX_MALLOC)
+ #pragma alloca
+#endif
+
+#define _GNU_SOURCE
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if defined(STDC_HEADERS) && !defined(emacs)
+#include <stddef.h>
+#else
+/* We need this for `regex.h', and perhaps for the Emacs include files. */
+#include <sys/types.h>
+#endif
+
+/* The `emacs' switch turns on certain matching commands
+ that make sense only in Emacs. */
+#ifdef emacs
+
+#include "lisp.h"
+#include "buffer.h"
+#include "syntax.h"
+
+/* Emacs uses `NULL' as a predicate. */
+#undef NULL
+
+#else /* not emacs */
+
+/* We used to test for `BSTRING' here, but only GCC and Emacs define
+ `BSTRING', as far as I know, and neither of them use this code. */
+#if HAVE_STRING_H || STDC_HEADERS
+#include <string.h>
+#ifndef bcmp
+#define bcmp(s1, s2, n) memcmp ((s1), (s2), (n))
+#endif
+#ifndef bcopy
+#define bcopy(s, d, n) memcpy ((d), (s), (n))
+#endif
+#ifndef bzero
+#define bzero(s, n) memset ((s), 0, (n))
+#endif
+#else
+#include <strings.h>
+#endif
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+char *malloc ();
+char *realloc ();
+#endif
+
+
+/* Define the syntax stuff for \<, \>, etc. */
+
+/* This must be nonzero for the wordchar and notwordchar pattern
+ commands in re_match_2. */
+#ifndef Sword
+#define Sword 1
+#endif
+
+#ifdef SYNTAX_TABLE
+
+extern char *re_syntax_table;
+
+#else /* not SYNTAX_TABLE */
+
+/* How many characters in the character set. */
+#define CHAR_SET_SIZE 256
+
+static char re_syntax_table[CHAR_SET_SIZE];
+
+static void
+init_syntax_once ()
+{
+ register int c;
+ static int done = 0;
+
+ if (done)
+ return;
+
+ bzero (re_syntax_table, sizeof re_syntax_table);
+
+ for (c = 'a'; c <= 'z'; c++)
+ re_syntax_table[c] = Sword;
+
+ for (c = 'A'; c <= 'Z'; c++)
+ re_syntax_table[c] = Sword;
+
+ for (c = '0'; c <= '9'; c++)
+ re_syntax_table[c] = Sword;
+
+ re_syntax_table['_'] = Sword;
+
+ done = 1;
+}
+
+#endif /* not SYNTAX_TABLE */
+
+#define SYNTAX(c) re_syntax_table[c]
+
+#endif /* not emacs */
+
+/* Get the interface, including the syntax bits. */
+#include "regex.h"
+
+/* isalpha etc. are used for the character classes. */
+#include <ctype.h>
+
+/* Jim Meyering writes:
+
+ "... Some ctype macros are valid only for character codes that
+ isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when
+ using /bin/cc or gcc but without giving an ansi option). So, all
+ ctype uses should be through macros like ISPRINT... If
+ STDC_HEADERS is defined, then autoconf has verified that the ctype
+ macros don't need to be guarded with references to isascii. ...
+ Defining isascii to 1 should let any compiler worth its salt
+ eliminate the && through constant folding." */
+#if ! defined (isascii) || defined (STDC_HEADERS)
+#undef isascii
+#define isascii(c) 1
+#endif
+
+#ifdef isblank
+#define ISBLANK(c) (isascii (c) && isblank (c))
+#else
+#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+#ifdef isgraph
+#define ISGRAPH(c) (isascii (c) && isgraph (c))
+#else
+#define ISGRAPH(c) (isascii (c) && isprint (c) && !isspace (c))
+#endif
+
+#define ISPRINT(c) (isascii (c) && isprint (c))
+#define ISDIGIT(c) (isascii (c) && isdigit (c))
+#define ISALNUM(c) (isascii (c) && isalnum (c))
+#define ISALPHA(c) (isascii (c) && isalpha (c))
+#define ISCNTRL(c) (isascii (c) && iscntrl (c))
+#define ISLOWER(c) (isascii (c) && islower (c))
+#define ISPUNCT(c) (isascii (c) && ispunct (c))
+#define ISSPACE(c) (isascii (c) && isspace (c))
+#define ISUPPER(c) (isascii (c) && isupper (c))
+#define ISXDIGIT(c) (isascii (c) && isxdigit (c))
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/* We remove any previous definition of `SIGN_EXTEND_CHAR',
+ since ours (we hope) works properly with all combinations of
+ machines, compilers, `char' and `unsigned char' argument types.
+ (Per Bothner suggested the basic approach.) */
+#undef SIGN_EXTEND_CHAR
+#if __STDC__
+#define SIGN_EXTEND_CHAR(c) ((signed char) (c))
+#else /* not __STDC__ */
+/* As in Harbison and Steele. */
+#define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128)
+#endif
+
+/* Should we use malloc or alloca? If REGEX_MALLOC is not defined, we
+ use `alloca' instead of `malloc'. This is because using malloc in
+ re_search* or re_match* could cause memory leaks when C-g is used in
+ Emacs; also, malloc is slower and causes storage fragmentation. On
+ the other hand, malloc is more portable, and easier to debug.
+
+ Because we sometimes use alloca, some routines have to be macros,
+ not functions -- `alloca'-allocated space disappears at the end of the
+ function it is called in. */
+
+#ifdef REGEX_MALLOC
+
+#define REGEX_ALLOCATE malloc
+#define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize)
+
+#else /* not REGEX_MALLOC */
+
+/* Emacs already defines alloca, sometimes. */
+#ifndef alloca
+
+/* Make alloca work the best possible way. */
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not __GNUC__ */
+#if HAVE_ALLOCA_H
+#include <alloca.h>
+#else /* not __GNUC__ or HAVE_ALLOCA_H */
+#ifndef _AIX /* Already did AIX, up at the top. */
+char *alloca ();
+#endif /* not _AIX */
+#endif /* not HAVE_ALLOCA_H */
+#endif /* not __GNUC__ */
+
+#endif /* not alloca */
+
+#define REGEX_ALLOCATE alloca
+
+/* Assumes a `char *destination' variable. */
+#define REGEX_REALLOCATE(source, osize, nsize) \
+ (destination = (char *) alloca (nsize), \
+ bcopy (source, destination, osize), \
+ destination)
+
+#endif /* not REGEX_MALLOC */
+
+
+/* True if `size1' is non-NULL and PTR is pointing anywhere inside
+ `string1' or just past its end. This works if PTR is NULL, which is
+ a good thing. */
+#define FIRST_STRING_P(ptr) \
+ (size1 && string1 <= (ptr) && (ptr) <= string1 + size1)
+
+/* (Re)Allocate N items of type T using malloc, or fail. */
+#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t)))
+#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t)))
+#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t)))
+
+#define BYTEWIDTH 8 /* In bits. */
+
+#define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+typedef char boolean;
+#define false 0
+#define true 1
+
+/* These are the command codes that appear in compiled regular
+ expressions. Some opcodes are followed by argument bytes. A
+ command code can specify any interpretation whatsoever for its
+ arguments. Zero bytes may appear in the compiled regular expression.
+
+ The value of `exactn' is needed in search.c (search_buffer) in Emacs.
+ So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of
+ `exactn' we use here must also be 1. */
+
+typedef enum
+{
+ no_op = 0,
+
+ /* Followed by one byte giving n, then by n literal bytes. */
+ exactn = 1,
+
+ /* Matches any (more or less) character. */
+ anychar,
+
+ /* Matches any one char belonging to specified set. First
+ following byte is number of bitmap bytes. Then come bytes
+ for a bitmap saying which chars are in. Bits in each byte
+ are ordered low-bit-first. A character is in the set if its
+ bit is 1. A character too large to have a bit in the map is
+ automatically not in the set. */
+ charset,
+
+ /* Same parameters as charset, but match any character that is
+ not one of those specified. */
+ charset_not,
+
+ /* Start remembering the text that is matched, for storing in a
+ register. Followed by one byte with the register number, in
+ the range 0 to one less than the pattern buffer's re_nsub
+ field. Then followed by one byte with the number of groups
+ inner to this one. (This last has to be part of the
+ start_memory only because we need it in the on_failure_jump
+ of re_match_2.) */
+ start_memory,
+
+ /* Stop remembering the text that is matched and store it in a
+ memory register. Followed by one byte with the register
+ number, in the range 0 to one less than `re_nsub' in the
+ pattern buffer, and one byte with the number of inner groups,
+ just like `start_memory'. (We need the number of inner
+ groups here because we don't have any easy way of finding the
+ corresponding start_memory when we're at a stop_memory.) */
+ stop_memory,
+
+ /* Match a duplicate of something remembered. Followed by one
+ byte containing the register number. */
+ duplicate,
+
+ /* Fail unless at beginning of line. */
+ begline,
+
+ /* Fail unless at end of line. */
+ endline,
+
+ /* Succeeds if at beginning of buffer (if emacs) or at beginning
+ of string to be matched (if not). */
+ begbuf,
+
+ /* Analogously, for end of buffer/string. */
+ endbuf,
+
+ /* Followed by two byte relative address to which to jump. */
+ jump,
+
+ /* Same as jump, but marks the end of an alternative. */
+ jump_past_alt,
+
+ /* Followed by two-byte relative address of place to resume at
+ in case of failure. */
+ on_failure_jump,
+
+ /* Like on_failure_jump, but pushes a placeholder instead of the
+ current string position when executed. */
+ on_failure_keep_string_jump,
+
+ /* Throw away latest failure point and then jump to following
+ two-byte relative address. */
+ pop_failure_jump,
+
+ /* Change to pop_failure_jump if know won't have to backtrack to
+ match; otherwise change to jump. This is used to jump
+ back to the beginning of a repeat. If what follows this jump
+ clearly won't match what the repeat does, such that we can be
+ sure that there is no use backtracking out of repetitions
+ already matched, then we change it to a pop_failure_jump.
+ Followed by two-byte address. */
+ maybe_pop_jump,
+
+ /* Jump to following two-byte address, and push a dummy failure
+ point. This failure point will be thrown away if an attempt
+ is made to use it for a failure. A `+' construct makes this
+ before the first repeat. Also used as an intermediary kind
+ of jump when compiling an alternative. */
+ dummy_failure_jump,
+
+ /* Push a dummy failure point and continue. Used at the end of
+ alternatives. */
+ push_dummy_failure,
+
+ /* Followed by two-byte relative address and two-byte number n.
+ After matching N times, jump to the address upon failure. */
+ succeed_n,
+
+ /* Followed by two-byte relative address, and two-byte number n.
+ Jump to the address N times, then fail. */
+ jump_n,
+
+ /* Set the following two-byte relative address to the
+ subsequent two-byte number. The address *includes* the two
+ bytes of number. */
+ set_number_at,
+
+ wordchar, /* Matches any word-constituent character. */
+ notwordchar, /* Matches any char that is not a word-constituent. */
+
+ wordbeg, /* Succeeds if at word beginning. */
+ wordend, /* Succeeds if at word end. */
+
+ wordbound, /* Succeeds if at a word boundary. */
+ notwordbound /* Succeeds if not at a word boundary. */
+
+#ifdef emacs
+ ,before_dot, /* Succeeds if before point. */
+ at_dot, /* Succeeds if at point. */
+ after_dot, /* Succeeds if after point. */
+
+ /* Matches any character whose syntax is specified. Followed by
+ a byte which contains a syntax code, e.g., Sword. */
+ syntaxspec,
+
+ /* Matches any character whose syntax is not that specified. */
+ notsyntaxspec
+#endif /* emacs */
+} re_opcode_t;
+
+/* Common operations on the compiled pattern. */
+
+/* Store NUMBER in two contiguous bytes starting at DESTINATION. */
+
+#define STORE_NUMBER(destination, number) \
+ do { \
+ (destination)[0] = (number) & 0377; \
+ (destination)[1] = (number) >> 8; \
+ } while (0)
+
+/* Same as STORE_NUMBER, except increment DESTINATION to
+ the byte after where the number is stored. Therefore, DESTINATION
+ must be an lvalue. */
+
+#define STORE_NUMBER_AND_INCR(destination, number) \
+ do { \
+ STORE_NUMBER (destination, number); \
+ (destination) += 2; \
+ } while (0)
+
+/* Put into DESTINATION a number stored in two contiguous bytes starting
+ at SOURCE. */
+
+#define EXTRACT_NUMBER(destination, source) \
+ do { \
+ (destination) = *(source) & 0377; \
+ (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8; \
+ } while (0)
+
+#ifdef DEBUG
+static void extract_number _RE_ARGS((int *dest, unsigned char *source));
+static void
+extract_number (dest, source)
+ int *dest;
+ unsigned char *source;
+{
+ int temp = SIGN_EXTEND_CHAR (*(source + 1));
+ *dest = *source & 0377;
+ *dest += temp << 8;
+}
+
+#ifndef EXTRACT_MACROS /* To debug the macros. */
+#undef EXTRACT_NUMBER
+#define EXTRACT_NUMBER(dest, src) extract_number (&dest, src)
+#endif /* not EXTRACT_MACROS */
+
+#endif /* DEBUG */
+
+/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number.
+ SOURCE must be an lvalue. */
+
+#define EXTRACT_NUMBER_AND_INCR(destination, source) \
+ do { \
+ EXTRACT_NUMBER (destination, source); \
+ (source) += 2; \
+ } while (0)
+
+#ifdef DEBUG
+static void extract_number_and_incr _RE_ARGS((int *destination,
+ unsigned char **source));
+static void
+extract_number_and_incr (destination, source)
+ int *destination;
+ unsigned char **source;
+{
+ extract_number (destination, *source);
+ *source += 2;
+}
+
+#ifndef EXTRACT_MACROS
+#undef EXTRACT_NUMBER_AND_INCR
+#define EXTRACT_NUMBER_AND_INCR(dest, src) \
+ extract_number_and_incr (&dest, &src)
+#endif /* not EXTRACT_MACROS */
+
+#endif /* DEBUG */
+
+/* If DEBUG is defined, Regex prints many voluminous messages about what
+ it is doing (if the variable `debug' is nonzero). If linked with the
+ main program in `iregex.c', you can enter patterns and strings
+ interactively. And if linked with the main program in `main.c' and
+ the other test files, you can run the already-written tests. */
+
+#ifdef DEBUG
+
+/* We use standard I/O for debugging. */
+#include <stdio.h>
+
+/* It is useful to test things that ``must'' be true when debugging. */
+#include <assert.h>
+
+static int debug = 0;
+
+#define DEBUG_STATEMENT(e) e
+#define DEBUG_PRINT1(x) if (debug) printf (x)
+#define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2)
+#define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3)
+#define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4)
+#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) \
+ if (debug) print_partial_compiled_pattern (s, e)
+#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) \
+ if (debug) print_double_string (w, s1, sz1, s2, sz2)
+
+
+extern void printchar ();
+
+/* Print the fastmap in human-readable form. */
+
+void
+print_fastmap (fastmap)
+ char *fastmap;
+{
+ unsigned was_a_range = 0;
+ unsigned i = 0;
+
+ while (i < (1 << BYTEWIDTH))
+ {
+ if (fastmap[i++])
+ {
+ was_a_range = 0;
+ printchar (i - 1);
+ while (i < (1 << BYTEWIDTH) && fastmap[i])
+ {
+ was_a_range = 1;
+ i++;
+ }
+ if (was_a_range)
+ {
+ printf ("-");
+ printchar (i - 1);
+ }
+ }
+ }
+ putchar ('\n');
+}
+
+
+/* Print a compiled pattern string in human-readable form, starting at
+ the START pointer into it and ending just before the pointer END. */
+
+void
+print_partial_compiled_pattern (start, end)
+ unsigned char *start;
+ unsigned char *end;
+{
+ int mcnt, mcnt2;
+ unsigned char *p = start;
+ unsigned char *pend = end;
+
+ if (start == NULL)
+ {
+ printf ("(null)\n");
+ return;
+ }
+
+ /* Loop over pattern commands. */
+ while (p < pend)
+ {
+ printf ("%d:\t", p - start);
+
+ switch ((re_opcode_t) *p++)
+ {
+ case no_op:
+ printf ("/no_op");
+ break;
+
+ case exactn:
+ mcnt = *p++;
+ printf ("/exactn/%d", mcnt);
+ do
+ {
+ putchar ('/');
+ printchar (*p++);
+ }
+ while (--mcnt);
+ break;
+
+ case start_memory:
+ mcnt = *p++;
+ printf ("/start_memory/%d/%d", mcnt, *p++);
+ break;
+
+ case stop_memory:
+ mcnt = *p++;
+ printf ("/stop_memory/%d/%d", mcnt, *p++);
+ break;
+
+ case duplicate:
+ printf ("/duplicate/%d", *p++);
+ break;
+
+ case anychar:
+ printf ("/anychar");
+ break;
+
+ case charset:
+ case charset_not:
+ {
+ register int c, last = -100;
+ register int in_range = 0;
+
+ printf ("/charset [%s",
+ (re_opcode_t) *(p - 1) == charset_not ? "^" : "");
+
+ assert (p + *p < pend);
+
+ for (c = 0; c < 256; c++)
+ if (c / 8 < *p
+ && (p[1 + (c/8)] & (1 << (c % 8))))
+ {
+ /* Are we starting a range? */
+ if (last + 1 == c && ! in_range)
+ {
+ putchar ('-');
+ in_range = 1;
+ }
+ /* Have we broken a range? */
+ else if (last + 1 != c && in_range)
+ {
+ printchar (last);
+ in_range = 0;
+ }
+
+ if (! in_range)
+ printchar (c);
+
+ last = c;
+ }
+
+ if (in_range)
+ printchar (last);
+
+ putchar (']');
+
+ p += 1 + *p;
+ }
+ break;
+
+ case begline:
+ printf ("/begline");
+ break;
+
+ case endline:
+ printf ("/endline");
+ break;
+
+ case on_failure_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/on_failure_jump to %d", p + mcnt - start);
+ break;
+
+ case on_failure_keep_string_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/on_failure_keep_string_jump to %d", p + mcnt - start);
+ break;
+
+ case dummy_failure_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/dummy_failure_jump to %d", p + mcnt - start);
+ break;
+
+ case push_dummy_failure:
+ printf ("/push_dummy_failure");
+ break;
+
+ case maybe_pop_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/maybe_pop_jump to %d", p + mcnt - start);
+ break;
+
+ case pop_failure_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/pop_failure_jump to %d", p + mcnt - start);
+ break;
+
+ case jump_past_alt:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/jump_past_alt to %d", p + mcnt - start);
+ break;
+
+ case jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/jump to %d", p + mcnt - start);
+ break;
+
+ case succeed_n:
+ extract_number_and_incr (&mcnt, &p);
+ extract_number_and_incr (&mcnt2, &p);
+ printf ("/succeed_n to %d, %d times", p + mcnt - start, mcnt2);
+ break;
+
+ case jump_n:
+ extract_number_and_incr (&mcnt, &p);
+ extract_number_and_incr (&mcnt2, &p);
+ printf ("/jump_n to %d, %d times", p + mcnt - start, mcnt2);
+ break;
+
+ case set_number_at:
+ extract_number_and_incr (&mcnt, &p);
+ extract_number_and_incr (&mcnt2, &p);
+ printf ("/set_number_at location %d to %d", p + mcnt - start, mcnt2);
+ break;
+
+ case wordbound:
+ printf ("/wordbound");
+ break;
+
+ case notwordbound:
+ printf ("/notwordbound");
+ break;
+
+ case wordbeg:
+ printf ("/wordbeg");
+ break;
+
+ case wordend:
+ printf ("/wordend");
+
+#ifdef emacs
+ case before_dot:
+ printf ("/before_dot");
+ break;
+
+ case at_dot:
+ printf ("/at_dot");
+ break;
+
+ case after_dot:
+ printf ("/after_dot");
+ break;
+
+ case syntaxspec:
+ printf ("/syntaxspec");
+ mcnt = *p++;
+ printf ("/%d", mcnt);
+ break;
+
+ case notsyntaxspec:
+ printf ("/notsyntaxspec");
+ mcnt = *p++;
+ printf ("/%d", mcnt);
+ break;
+#endif /* emacs */
+
+ case wordchar:
+ printf ("/wordchar");
+ break;
+
+ case notwordchar:
+ printf ("/notwordchar");
+ break;
+
+ case begbuf:
+ printf ("/begbuf");
+ break;
+
+ case endbuf:
+ printf ("/endbuf");
+ break;
+
+ default:
+ printf ("?%d", *(p-1));
+ }
+
+ putchar ('\n');
+ }
+
+ printf ("%d:\tend of pattern.\n", p - start);
+}
+
+
+void
+print_compiled_pattern (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ unsigned char *buffer = bufp->buffer;
+
+ print_partial_compiled_pattern (buffer, buffer + bufp->used);
+ printf ("%d bytes used/%d bytes allocated.\n", bufp->used, bufp->allocated);
+
+ if (bufp->fastmap_accurate && bufp->fastmap)
+ {
+ printf ("fastmap: ");
+ print_fastmap (bufp->fastmap);
+ }
+
+ printf ("re_nsub: %d\t", bufp->re_nsub);
+ printf ("regs_alloc: %d\t", bufp->regs_allocated);
+ printf ("can_be_null: %d\t", bufp->can_be_null);
+ printf ("newline_anchor: %d\n", bufp->newline_anchor);
+ printf ("no_sub: %d\t", bufp->no_sub);
+ printf ("not_bol: %d\t", bufp->not_bol);
+ printf ("not_eol: %d\t", bufp->not_eol);
+ printf ("syntax: %d\n", bufp->syntax);
+ /* Perhaps we should print the translate table? */
+}
+
+
+void
+print_double_string (where, string1, size1, string2, size2)
+ const char *where;
+ const char *string1;
+ const char *string2;
+ int size1;
+ int size2;
+{
+ unsigned this_char;
+
+ if (where == NULL)
+ printf ("(null)");
+ else
+ {
+ if (FIRST_STRING_P (where))
+ {
+ for (this_char = where - string1; this_char < size1; this_char++)
+ printchar (string1[this_char]);
+
+ where = string2;
+ }
+
+ for (this_char = where - string2; this_char < size2; this_char++)
+ printchar (string2[this_char]);
+ }
+}
+
+#else /* not DEBUG */
+
+#undef assert
+#define assert(e)
+
+#define DEBUG_STATEMENT(e)
+#define DEBUG_PRINT1(x)
+#define DEBUG_PRINT2(x1, x2)
+#define DEBUG_PRINT3(x1, x2, x3)
+#define DEBUG_PRINT4(x1, x2, x3, x4)
+#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)
+#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)
+
+#endif /* not DEBUG */
+
+/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can
+ also be assigned to arbitrarily: each pattern buffer stores its own
+ syntax, so it can be changed between regex compilations. */
+reg_syntax_t re_syntax_options = RE_SYNTAX_EMACS;
+
+
+/* Specify the precise syntax of regexps for compilation. This provides
+ for compatibility for various utilities which historically have
+ different, incompatible syntaxes.
+
+ The argument SYNTAX is a bit mask comprised of the various bits
+ defined in regex.h. We return the old syntax. */
+
+reg_syntax_t
+re_set_syntax (syntax)
+ reg_syntax_t syntax;
+{
+ reg_syntax_t ret = re_syntax_options;
+
+ re_syntax_options = syntax;
+ return ret;
+}
+
+/* This table gives an error message for each of the error codes listed
+ in regex.h. Obviously the order here has to be same as there. */
+
+static const char *re_error_msg[] =
+ { NULL, /* REG_NOERROR */
+ "No match", /* REG_NOMATCH */
+ "Invalid regular expression", /* REG_BADPAT */
+ "Invalid collation character", /* REG_ECOLLATE */
+ "Invalid character class name", /* REG_ECTYPE */
+ "Trailing backslash", /* REG_EESCAPE */
+ "Invalid back reference", /* REG_ESUBREG */
+ "Unmatched [ or [^", /* REG_EBRACK */
+ "Unmatched ( or \\(", /* REG_EPAREN */
+ "Unmatched \\{", /* REG_EBRACE */
+ "Invalid content of \\{\\}", /* REG_BADBR */
+ "Invalid range end", /* REG_ERANGE */
+ "Memory exhausted", /* REG_ESPACE */
+ "Invalid preceding regular expression", /* REG_BADRPT */
+ "Premature end of regular expression", /* REG_EEND */
+ "Regular expression too big", /* REG_ESIZE */
+ "Unmatched ) or \\)", /* REG_ERPAREN */
+ };
+
+/* Subroutine declarations and macros for regex_compile. */
+
+static reg_errcode_t regex_compile _RE_ARGS((const char *pattern, size_t size,
+ reg_syntax_t syntax,
+ struct re_pattern_buffer *bufp));
+static void store_op1 _RE_ARGS((re_opcode_t op, unsigned char *loc, int arg));
+static void store_op2 _RE_ARGS((re_opcode_t op, unsigned char *loc,
+ int arg1, int arg2));
+static void insert_op1 _RE_ARGS((re_opcode_t op, unsigned char *loc,
+ int arg, unsigned char *end));
+static void insert_op2 _RE_ARGS((re_opcode_t op, unsigned char *loc,
+ int arg1, int arg2, unsigned char *end));
+static boolean at_begline_loc_p _RE_ARGS((const char *pattern, const char *p,
+ reg_syntax_t syntax));
+static boolean at_endline_loc_p _RE_ARGS((const char *p, const char *pend,
+ reg_syntax_t syntax));
+static reg_errcode_t compile_range _RE_ARGS((const char **p_ptr,
+ const char *pend,
+ char *translate,
+ reg_syntax_t syntax,
+ unsigned char *b));
+
+/* Fetch the next character in the uncompiled pattern---translating it
+ if necessary. Also cast from a signed character in the constant
+ string passed to us by the user to an unsigned char that we can use
+ as an array index (in, e.g., `translate'). */
+#define PATFETCH(c) \
+ do {if (p == pend) return REG_EEND; \
+ c = (unsigned char) *p++; \
+ if (translate) c = translate[c]; \
+ } while (0)
+
+/* Fetch the next character in the uncompiled pattern, with no
+ translation. */
+#define PATFETCH_RAW(c) \
+ do {if (p == pend) return REG_EEND; \
+ c = (unsigned char) *p++; \
+ } while (0)
+
+/* Go backwards one character in the pattern. */
+#define PATUNFETCH p--
+
+
+/* If `translate' is non-null, return translate[D], else just D. We
+ cast the subscript to translate because some data is declared as
+ `char *', to avoid warnings when a string constant is passed. But
+ when we use a character as a subscript we must make it unsigned. */
+#define TRANSLATE(d) (translate ? translate[(unsigned char) (d)] : (d))
+
+
+/* Macros for outputting the compiled pattern into `buffer'. */
+
+/* If the buffer isn't allocated when it comes in, use this. */
+#define INIT_BUF_SIZE 32
+
+/* Make sure we have at least N more bytes of space in buffer. */
+#define GET_BUFFER_SPACE(n) \
+ while (b - bufp->buffer + (n) > bufp->allocated) \
+ EXTEND_BUFFER ()
+
+/* Make sure we have one more byte of buffer space and then add C to it. */
+#define BUF_PUSH(c) \
+ do { \
+ GET_BUFFER_SPACE (1); \
+ *b++ = (unsigned char) (c); \
+ } while (0)
+
+
+/* Ensure we have two more bytes of buffer space and then append C1 and C2. */
+#define BUF_PUSH_2(c1, c2) \
+ do { \
+ GET_BUFFER_SPACE (2); \
+ *b++ = (unsigned char) (c1); \
+ *b++ = (unsigned char) (c2); \
+ } while (0)
+
+
+/* As with BUF_PUSH_2, except for three bytes. */
+#define BUF_PUSH_3(c1, c2, c3) \
+ do { \
+ GET_BUFFER_SPACE (3); \
+ *b++ = (unsigned char) (c1); \
+ *b++ = (unsigned char) (c2); \
+ *b++ = (unsigned char) (c3); \
+ } while (0)
+
+
+/* Store a jump with opcode OP at LOC to location TO. We store a
+ relative address offset by the three bytes the jump itself occupies. */
+#define STORE_JUMP(op, loc, to) \
+ store_op1 (op, loc, (int)((to) - (loc) - 3))
+
+/* Likewise, for a two-argument jump. */
+#define STORE_JUMP2(op, loc, to, arg) \
+ store_op2 (op, loc, (int)((to) - (loc) - 3), arg)
+
+/* Like `STORE_JUMP', but for inserting. Assume `b' is the buffer end. */
+#define INSERT_JUMP(op, loc, to) \
+ insert_op1 (op, loc, (int)((to) - (loc) - 3), b)
+
+/* Like `STORE_JUMP2', but for inserting. Assume `b' is the buffer end. */
+#define INSERT_JUMP2(op, loc, to, arg) \
+ insert_op2 (op, loc, (int)((to) - (loc) - 3), arg, b)
+
+
+/* This is not an arbitrary limit: the arguments which represent offsets
+ into the pattern are two bytes long. So if 2^16 bytes turns out to
+ be too small, many things would have to change. */
+/* Any other compiler which, like MSC, has allocation limit below 2^16
+ bytes will have to use approach similar to what was done below for
+ MSC and drop MAX_BUF_SIZE a bit. Otherwise you may end up
+ reallocating to 0 bytes. Such thing is not going to work too well.
+ You have been warned!! */
+#ifdef _MSC_VER
+/* Microsoft C 16-bit versions limit malloc to approx 65512 bytes.
+ The REALLOC define eliminates a flurry of conversion warnings,
+ but is not required. */
+#define MAX_BUF_SIZE 65500L
+#define REALLOC(p,s) realloc((p), (size_t) (s))
+#else
+#define MAX_BUF_SIZE (1L << 16)
+#define REALLOC realloc
+#endif
+
+/* Extend the buffer by twice its current size via realloc and
+ reset the pointers that pointed into the old block to point to the
+ correct places in the new one. If extending the buffer results in it
+ being larger than MAX_BUF_SIZE, then flag memory exhausted. */
+#define EXTEND_BUFFER() \
+ do { \
+ unsigned char *old_buffer = bufp->buffer; \
+ if (bufp->allocated == MAX_BUF_SIZE) \
+ return REG_ESIZE; \
+ bufp->allocated <<= 1; \
+ if (bufp->allocated > MAX_BUF_SIZE) \
+ bufp->allocated = MAX_BUF_SIZE; \
+ bufp->buffer = (unsigned char *) REALLOC(bufp->buffer, bufp->allocated);\
+ if (bufp->buffer == NULL) \
+ return REG_ESPACE; \
+ /* If the buffer moved, move all the pointers into it. */ \
+ if (old_buffer != bufp->buffer) \
+ { \
+ b = (b - old_buffer) + bufp->buffer; \
+ begalt = (begalt - old_buffer) + bufp->buffer; \
+ if (fixup_alt_jump) \
+ fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\
+ if (laststart) \
+ laststart = (laststart - old_buffer) + bufp->buffer; \
+ if (pending_exact) \
+ pending_exact = (pending_exact - old_buffer) + bufp->buffer; \
+ } \
+ } while (0)
+
+
+/* Since we have one byte reserved for the register number argument to
+ {start,stop}_memory, the maximum number of groups we can report
+ things about is what fits in that byte. */
+#define MAX_REGNUM 255
+
+/* But patterns can have more than `MAX_REGNUM' registers. We just
+ ignore the excess. */
+typedef unsigned regnum_t;
+
+
+/* Macros for the compile stack. */
+
+/* Since offsets can go either forwards or backwards, this type needs to
+ be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1. */
+/* int may be not enough when sizeof(int) == 2 */
+typedef long pattern_offset_t;
+
+typedef struct
+{
+ pattern_offset_t begalt_offset;
+ pattern_offset_t fixup_alt_jump;
+ pattern_offset_t inner_group_offset;
+ pattern_offset_t laststart_offset;
+ regnum_t regnum;
+} compile_stack_elt_t;
+
+
+typedef struct
+{
+ compile_stack_elt_t *stack;
+ unsigned size;
+ unsigned avail; /* Offset of next open position. */
+} compile_stack_type;
+
+
+#define INIT_COMPILE_STACK_SIZE 32
+
+#define COMPILE_STACK_EMPTY (compile_stack.avail == 0)
+#define COMPILE_STACK_FULL (compile_stack.avail == compile_stack.size)
+
+/* The next available element. */
+#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail])
+
+
+/* Set the bit for character C in a list. */
+#define SET_LIST_BIT(c) \
+ (b[((unsigned char) (c)) / BYTEWIDTH] \
+ |= 1 << (((unsigned char) c) % BYTEWIDTH))
+
+
+/* Get the next unsigned number in the uncompiled pattern. */
+#define GET_UNSIGNED_NUMBER(num) \
+ { if (p != pend) \
+ { \
+ PATFETCH (c); \
+ while (ISDIGIT (c)) \
+ { \
+ if (num < 0) \
+ num = 0; \
+ num = num * 10 + c - '0'; \
+ if (p == pend) \
+ break; \
+ PATFETCH (c); \
+ } \
+ } \
+ }
+
+#define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */
+
+#define IS_CHAR_CLASS(string) \
+ (STREQ (string, "alpha") || STREQ (string, "upper") \
+ || STREQ (string, "lower") || STREQ (string, "digit") \
+ || STREQ (string, "alnum") || STREQ (string, "xdigit") \
+ || STREQ (string, "space") || STREQ (string, "print") \
+ || STREQ (string, "punct") || STREQ (string, "graph") \
+ || STREQ (string, "cntrl") || STREQ (string, "blank"))
+
+static boolean group_in_compile_stack _RE_ARGS((compile_stack_type
+ compile_stack,
+ regnum_t regnum));
+
+/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX.
+ Returns one of error codes defined in `regex.h', or zero for success.
+
+ Assumes the `allocated' (and perhaps `buffer') and `translate'
+ fields are set in BUFP on entry.
+
+ If it succeeds, results are put in BUFP (if it returns an error, the
+ contents of BUFP are undefined):
+ `buffer' is the compiled pattern;
+ `syntax' is set to SYNTAX;
+ `used' is set to the length of the compiled pattern;
+ `fastmap_accurate' is zero;
+ `re_nsub' is the number of subexpressions in PATTERN;
+ `not_bol' and `not_eol' are zero;
+
+ The `fastmap' and `newline_anchor' fields are neither
+ examined nor set. */
+
+static reg_errcode_t
+regex_compile (pattern, size, syntax, bufp)
+ const char *pattern;
+ size_t size;
+ reg_syntax_t syntax;
+ struct re_pattern_buffer *bufp;
+{
+ /* We fetch characters from PATTERN here. Even though PATTERN is
+ `char *' (i.e., signed), we declare these variables as unsigned, so
+ they can be reliably used as array indices. */
+ register unsigned char c, c1;
+
+ /* A random tempory spot in PATTERN. */
+ const char *p1;
+
+ /* Points to the end of the buffer, where we should append. */
+ register unsigned char *b;
+
+ /* Keeps track of unclosed groups. */
+ compile_stack_type compile_stack;
+
+ /* Points to the current (ending) position in the pattern. */
+ const char *p = pattern;
+ const char *pend = pattern + size;
+
+ /* How to translate the characters in the pattern. */
+ char *translate = bufp->translate;
+
+ /* Address of the count-byte of the most recently inserted `exactn'
+ command. This makes it possible to tell if a new exact-match
+ character can be added to that command or if the character requires
+ a new `exactn' command. */
+ unsigned char *pending_exact = 0;
+
+ /* Address of start of the most recently finished expression.
+ This tells, e.g., postfix * where to find the start of its
+ operand. Reset at the beginning of groups and alternatives. */
+ unsigned char *laststart = 0;
+
+ /* Address of beginning of regexp, or inside of last group. */
+ unsigned char *begalt;
+
+ /* Place in the uncompiled pattern (i.e., the {) to
+ which to go back if the interval is invalid. */
+ const char *beg_interval;
+
+ /* Address of the place where a forward jump should go to the end of
+ the containing expression. Each alternative of an `or' -- except the
+ last -- ends with a forward jump of this sort. */
+ unsigned char *fixup_alt_jump = 0;
+
+ /* Counts open-groups as they are encountered. Remembered for the
+ matching close-group on the compile stack, so the same register
+ number is put in the stop_memory as the start_memory. */
+ regnum_t regnum = 0;
+
+#ifdef DEBUG
+ DEBUG_PRINT1 ("\nCompiling pattern: ");
+ if (debug)
+ {
+ unsigned debug_count;
+
+ for (debug_count = 0; debug_count < size; debug_count++)
+ printchar (pattern[debug_count]);
+ putchar ('\n');
+ }
+#endif /* DEBUG */
+
+ /* Initialize the compile stack. */
+ compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t);
+ if (compile_stack.stack == NULL)
+ return REG_ESPACE;
+
+ compile_stack.size = INIT_COMPILE_STACK_SIZE;
+ compile_stack.avail = 0;
+
+ /* Initialize the pattern buffer. */
+ bufp->syntax = syntax;
+ bufp->fastmap_accurate = 0;
+ bufp->not_bol = bufp->not_eol = 0;
+
+ /* Set `used' to zero, so that if we return an error, the pattern
+ printer (for debugging) will think there's no pattern. We reset it
+ at the end. */
+ bufp->used = 0;
+
+ /* Always count groups, whether or not bufp->no_sub is set. */
+ bufp->re_nsub = 0;
+
+#if !defined (emacs) && !defined (SYNTAX_TABLE)
+ /* Initialize the syntax table. */
+ init_syntax_once ();
+#endif
+
+ if (bufp->allocated == 0)
+ {
+ if (bufp->buffer)
+ { /* If zero allocated, but buffer is non-null, try to realloc
+ enough space. This loses if buffer's address is bogus, but
+ that is the user's responsibility. */
+ RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char);
+ }
+ else
+ { /* Caller did not allocate a buffer. Do it for them. */
+ bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char);
+ }
+ if (!bufp->buffer) return REG_ESPACE;
+
+ bufp->allocated = INIT_BUF_SIZE;
+ }
+
+ begalt = b = bufp->buffer;
+
+ /* Loop through the uncompiled pattern until we're at the end. */
+ while (p != pend)
+ {
+ PATFETCH (c);
+
+ switch (c)
+ {
+ case '^':
+ {
+ if ( /* If at start of pattern, it's an operator. */
+ p == pattern + 1
+ /* If context independent, it's an operator. */
+ || syntax & RE_CONTEXT_INDEP_ANCHORS
+ /* Otherwise, depends on what's come before. */
+ || at_begline_loc_p (pattern, p, syntax))
+ BUF_PUSH (begline);
+ else
+ goto normal_char;
+ }
+ break;
+
+
+ case '$':
+ {
+ if ( /* If at end of pattern, it's an operator. */
+ p == pend
+ /* If context independent, it's an operator. */
+ || syntax & RE_CONTEXT_INDEP_ANCHORS
+ /* Otherwise, depends on what's next. */
+ || at_endline_loc_p (p, pend, syntax))
+ BUF_PUSH (endline);
+ else
+ goto normal_char;
+ }
+ break;
+
+
+ case '+':
+ case '?':
+ if ((syntax & RE_BK_PLUS_QM)
+ || (syntax & RE_LIMITED_OPS))
+ goto normal_char;
+ handle_plus:
+ case '*':
+ /* If there is no previous pattern... */
+ if (!laststart)
+ {
+ if (syntax & RE_CONTEXT_INVALID_OPS)
+ return REG_BADRPT;
+ else if (!(syntax & RE_CONTEXT_INDEP_OPS))
+ goto normal_char;
+ }
+
+ {
+ /* Are we optimizing this jump? */
+ boolean keep_string_p = false;
+
+ /* 1 means zero (many) matches is allowed. */
+ char zero_times_ok = 0, many_times_ok = 0;
+
+ /* If there is a sequence of repetition chars, collapse it
+ down to just one (the right one). We can't combine
+ interval operators with these because of, e.g., `a{2}*',
+ which should only match an even number of `a's. */
+
+ for (;;)
+ {
+ zero_times_ok |= c != '+';
+ many_times_ok |= c != '?';
+
+ if (p == pend)
+ break;
+
+ PATFETCH (c);
+
+ if (c == '*'
+ || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?')))
+ ;
+
+ else if (syntax & RE_BK_PLUS_QM && c == '\\')
+ {
+ if (p == pend) return REG_EESCAPE;
+
+ PATFETCH (c1);
+ if (!(c1 == '+' || c1 == '?'))
+ {
+ PATUNFETCH;
+ PATUNFETCH;
+ break;
+ }
+
+ c = c1;
+ }
+ else
+ {
+ PATUNFETCH;
+ break;
+ }
+
+ /* If we get here, we found another repeat character. */
+ }
+
+ /* Star, etc. applied to an empty pattern is equivalent
+ to an empty pattern. */
+ if (!laststart)
+ break;
+
+ /* Now we know whether or not zero matches is allowed
+ and also whether or not two or more matches is allowed. */
+ if (many_times_ok)
+ { /* More than one repetition is allowed, so put in at the
+ end a backward relative jump from `b' to before the next
+ jump we're going to put in below (which jumps from
+ laststart to after this jump).
+
+ But if we are at the `*' in the exact sequence `.*\n',
+ insert an unconditional jump backwards to the .,
+ instead of the beginning of the loop. This way we only
+ push a failure point once, instead of every time
+ through the loop. */
+ assert (p - 1 > pattern);
+
+ /* Allocate the space for the jump. */
+ GET_BUFFER_SPACE (3);
+
+ /* We know we are not at the first character of the pattern,
+ because laststart was nonzero. And we've already
+ incremented `p', by the way, to be the character after
+ the `*'. Do we have to do something analogous here
+ for null bytes, because of RE_DOT_NOT_NULL? */
+ if (TRANSLATE (*(p - 2)) == TRANSLATE ('.')
+ && zero_times_ok
+ && p < pend && TRANSLATE (*p) == TRANSLATE ('\n')
+ && !(syntax & RE_DOT_NEWLINE))
+ { /* We have .*\n. */
+ STORE_JUMP (jump, b, laststart);
+ keep_string_p = true;
+ }
+ else
+ /* Anything else. */
+ STORE_JUMP (maybe_pop_jump, b, laststart - 3);
+
+ /* We've added more stuff to the buffer. */
+ b += 3;
+ }
+
+ /* On failure, jump from laststart to b + 3, which will be the
+ end of the buffer after this jump is inserted. */
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump
+ : on_failure_jump,
+ laststart, b + 3);
+ pending_exact = 0;
+ b += 3;
+
+ if (!zero_times_ok)
+ {
+ /* At least one repetition is required, so insert a
+ `dummy_failure_jump' before the initial
+ `on_failure_jump' instruction of the loop. This
+ effects a skip over that instruction the first time
+ we hit that loop. */
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6);
+ b += 3;
+ }
+ }
+ break;
+
+
+ case '.':
+ laststart = b;
+ BUF_PUSH (anychar);
+ break;
+
+
+ case '[':
+ {
+ boolean had_char_class = false;
+
+ if (p == pend) return REG_EBRACK;
+
+ /* Ensure that we have enough space to push a charset: the
+ opcode, the length count, and the bitset; 34 bytes in all. */
+ GET_BUFFER_SPACE (34);
+
+ laststart = b;
+
+ /* We test `*p == '^' twice, instead of using an if
+ statement, so we only need one BUF_PUSH. */
+ BUF_PUSH (*p == '^' ? charset_not : charset);
+ if (*p == '^')
+ p++;
+
+ /* Remember the first position in the bracket expression. */
+ p1 = p;
+
+ /* Push the number of bytes in the bitmap. */
+ BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
+
+ /* Clear the whole map. */
+ bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH);
+
+ /* charset_not matches newline according to a syntax bit. */
+ if ((re_opcode_t) b[-2] == charset_not
+ && (syntax & RE_HAT_LISTS_NOT_NEWLINE))
+ SET_LIST_BIT ('\n');
+
+ /* Read in characters and ranges, setting map bits. */
+ for (;;)
+ {
+ if (p == pend) return REG_EBRACK;
+
+ PATFETCH (c);
+
+ /* \ might escape characters inside [...] and [^...]. */
+ if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\')
+ {
+ if (p == pend) return REG_EESCAPE;
+
+ PATFETCH (c1);
+ SET_LIST_BIT (c1);
+ continue;
+ }
+
+ /* Could be the end of the bracket expression. If it's
+ not (i.e., when the bracket expression is `[]' so
+ far), the ']' character bit gets set way below. */
+ if (c == ']' && p != p1 + 1)
+ break;
+
+ /* Look ahead to see if it's a range when the last thing
+ was a character class. */
+ if (had_char_class && c == '-' && *p != ']')
+ return REG_ERANGE;
+
+ /* Look ahead to see if it's a range when the last thing
+ was a character: if this is a hyphen not at the
+ beginning or the end of a list, then it's the range
+ operator. */
+ if (c == '-'
+ && !(p - 2 >= pattern && p[-2] == '[')
+ && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^')
+ && *p != ']')
+ {
+ reg_errcode_t ret
+ = compile_range (&p, pend, translate, syntax, b);
+ if (ret != REG_NOERROR) return ret;
+ }
+
+ else if (p[0] == '-' && p[1] != ']')
+ { /* This handles ranges made up of characters only. */
+ reg_errcode_t ret;
+
+ /* Move past the `-'. */
+ PATFETCH (c1);
+
+ ret = compile_range (&p, pend, translate, syntax, b);
+ if (ret != REG_NOERROR) return ret;
+ }
+
+ /* See if we're at the beginning of a possible character
+ class. */
+
+ else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':')
+ { /* Leave room for the null. */
+ char str[CHAR_CLASS_MAX_LENGTH + 1];
+
+ PATFETCH (c);
+ c1 = 0;
+
+ /* If pattern is `[[:'. */
+ if (p == pend) return REG_EBRACK;
+
+ for (;;)
+ {
+ PATFETCH (c);
+ if (c == ':' || c == ']' || p == pend
+ || c1 == CHAR_CLASS_MAX_LENGTH)
+ break;
+ str[c1++] = c;
+ }
+ str[c1] = '\0';
+
+ /* If isn't a word bracketed by `[:' and:`]':
+ undo the ending character, the letters, and leave
+ the leading `:' and `[' (but set bits for them). */
+ if (c == ':' && *p == ']')
+ {
+ int ch;
+ boolean is_alnum = STREQ (str, "alnum");
+ boolean is_alpha = STREQ (str, "alpha");
+ boolean is_blank = STREQ (str, "blank");
+ boolean is_cntrl = STREQ (str, "cntrl");
+ boolean is_digit = STREQ (str, "digit");
+ boolean is_graph = STREQ (str, "graph");
+ boolean is_lower = STREQ (str, "lower");
+ boolean is_print = STREQ (str, "print");
+ boolean is_punct = STREQ (str, "punct");
+ boolean is_space = STREQ (str, "space");
+ boolean is_upper = STREQ (str, "upper");
+ boolean is_xdigit = STREQ (str, "xdigit");
+
+ if (!IS_CHAR_CLASS (str)) return REG_ECTYPE;
+
+ /* Throw away the ] at the end of the character
+ class. */
+ PATFETCH (c);
+
+ if (p == pend) return REG_EBRACK;
+
+ for (ch = 0; ch < 1 << BYTEWIDTH; ch++)
+ {
+ if ( (is_alnum && ISALNUM (ch))
+ || (is_alpha && ISALPHA (ch))
+ || (is_blank && ISBLANK (ch))
+ || (is_cntrl && ISCNTRL (ch))
+ || (is_digit && ISDIGIT (ch))
+ || (is_graph && ISGRAPH (ch))
+ || (is_lower && ISLOWER (ch))
+ || (is_print && ISPRINT (ch))
+ || (is_punct && ISPUNCT (ch))
+ || (is_space && ISSPACE (ch))
+ || (is_upper && ISUPPER (ch))
+ || (is_xdigit && ISXDIGIT (ch)))
+ SET_LIST_BIT (ch);
+ }
+ had_char_class = true;
+ }
+ else
+ {
+ c1++;
+ while (c1--)
+ PATUNFETCH;
+ SET_LIST_BIT ('[');
+ SET_LIST_BIT (':');
+ had_char_class = false;
+ }
+ }
+ else
+ {
+ had_char_class = false;
+ SET_LIST_BIT (c);
+ }
+ }
+
+ /* Discard any (non)matching list bytes that are all 0 at the
+ end of the map. Decrease the map-length byte too. */
+ while ((int) b[-1] > 0 && b[b[-1] - 1] == 0)
+ b[-1]--;
+ b += b[-1];
+ }
+ break;
+
+
+ case '(':
+ if (syntax & RE_NO_BK_PARENS)
+ goto handle_open;
+ else
+ goto normal_char;
+
+
+ case ')':
+ if (syntax & RE_NO_BK_PARENS)
+ goto handle_close;
+ else
+ goto normal_char;
+
+
+ case '\n':
+ if (syntax & RE_NEWLINE_ALT)
+ goto handle_alt;
+ else
+ goto normal_char;
+
+
+ case '|':
+ if (syntax & RE_NO_BK_VBAR)
+ goto handle_alt;
+ else
+ goto normal_char;
+
+
+ case '{':
+ if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES)
+ goto handle_interval;
+ else
+ goto normal_char;
+
+
+ case '\\':
+ if (p == pend) return REG_EESCAPE;
+
+ /* Do not translate the character after the \, so that we can
+ distinguish, e.g., \B from \b, even if we normally would
+ translate, e.g., B to b. */
+ PATFETCH_RAW (c);
+
+ switch (c)
+ {
+ case '(':
+ if (syntax & RE_NO_BK_PARENS)
+ goto normal_backslash;
+
+ handle_open:
+ bufp->re_nsub++;
+ regnum++;
+
+ if (COMPILE_STACK_FULL)
+ {
+ RETALLOC (compile_stack.stack, compile_stack.size << 1,
+ compile_stack_elt_t);
+ if (compile_stack.stack == NULL) return REG_ESPACE;
+
+ compile_stack.size <<= 1;
+ }
+
+ /* These are the values to restore when we hit end of this
+ group. They are all relative offsets, so that if the
+ whole pattern moves because of realloc, they will still
+ be valid. */
+ COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer;
+ COMPILE_STACK_TOP.fixup_alt_jump
+ = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0;
+ COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer;
+ COMPILE_STACK_TOP.regnum = regnum;
+
+ /* We will eventually replace the 0 with the number of
+ groups inner to this one. But do not push a
+ start_memory for groups beyond the last one we can
+ represent in the compiled pattern. */
+ if (regnum <= MAX_REGNUM)
+ {
+ COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2;
+ BUF_PUSH_3 (start_memory, regnum, 0);
+ }
+
+ compile_stack.avail++;
+
+ fixup_alt_jump = 0;
+ laststart = 0;
+ begalt = b;
+ /* If we've reached MAX_REGNUM groups, then this open
+ won't actually generate any code, so we'll have to
+ clear pending_exact explicitly. */
+ pending_exact = 0;
+ break;
+
+
+ case ')':
+ if (syntax & RE_NO_BK_PARENS) goto normal_backslash;
+
+ if (COMPILE_STACK_EMPTY)
+ if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+ goto normal_backslash;
+ else
+ return REG_ERPAREN;
+
+ handle_close:
+ if (fixup_alt_jump)
+ { /* Push a dummy failure point at the end of the
+ alternative for a possible future
+ `pop_failure_jump' to pop. See comments at
+ `push_dummy_failure' in `re_match_2'. */
+ BUF_PUSH (push_dummy_failure);
+
+ /* We allocated space for this jump when we assigned
+ to `fixup_alt_jump', in the `handle_alt' case below. */
+ STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1);
+ }
+
+ /* See similar code for backslashed left paren above. */
+ if (COMPILE_STACK_EMPTY)
+ if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+ goto normal_char;
+ else
+ return REG_ERPAREN;
+
+ /* Since we just checked for an empty stack above, this
+ ``can't happen''. */
+ assert (compile_stack.avail != 0);
+ {
+ /* We don't just want to restore into `regnum', because
+ later groups should continue to be numbered higher,
+ as in `(ab)c(de)' -- the second group is #2. */
+ regnum_t this_group_regnum;
+
+ compile_stack.avail--;
+ begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset;
+ fixup_alt_jump
+ = COMPILE_STACK_TOP.fixup_alt_jump
+ ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1
+ : 0;
+ laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset;
+ this_group_regnum = COMPILE_STACK_TOP.regnum;
+ /* If we've reached MAX_REGNUM groups, then this open
+ won't actually generate any code, so we'll have to
+ clear pending_exact explicitly. */
+ pending_exact = 0;
+
+ /* We're at the end of the group, so now we know how many
+ groups were inside this one. */
+ if (this_group_regnum <= MAX_REGNUM)
+ {
+ unsigned char *inner_group_loc
+ = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset;
+
+ *inner_group_loc = regnum - this_group_regnum;
+ BUF_PUSH_3 (stop_memory, this_group_regnum,
+ regnum - this_group_regnum);
+ }
+ }
+ break;
+
+
+ case '|': /* `\|'. */
+ if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR)
+ goto normal_backslash;
+ handle_alt:
+ if (syntax & RE_LIMITED_OPS)
+ goto normal_char;
+
+ /* Insert before the previous alternative a jump which
+ jumps to this alternative if the former fails. */
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (on_failure_jump, begalt, b + 6);
+ pending_exact = 0;
+ b += 3;
+
+ /* The alternative before this one has a jump after it
+ which gets executed if it gets matched. Adjust that
+ jump so it will jump to this alternative's analogous
+ jump (put in below, which in turn will jump to the next
+ (if any) alternative's such jump, etc.). The last such
+ jump jumps to the correct final destination. A picture:
+ _____ _____
+ | | | |
+ | v | v
+ a | b | c
+
+ If we are at `b', then fixup_alt_jump right now points to a
+ three-byte space after `a'. We'll put in the jump, set
+ fixup_alt_jump to right after `b', and leave behind three
+ bytes which we'll fill in when we get to after `c'. */
+
+ if (fixup_alt_jump)
+ STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
+
+ /* Mark and leave space for a jump after this alternative,
+ to be filled in later either by next alternative or
+ when know we're at the end of a series of alternatives. */
+ fixup_alt_jump = b;
+ GET_BUFFER_SPACE (3);
+ b += 3;
+
+ laststart = 0;
+ begalt = b;
+ break;
+
+
+ case '{':
+ /* If \{ is a literal. */
+ if (!(syntax & RE_INTERVALS)
+ /* If we're at `\{' and it's not the open-interval
+ operator. */
+ || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ || (p - 2 == pattern && p == pend))
+ goto normal_backslash;
+
+ handle_interval:
+ {
+ /* If got here, then the syntax allows intervals. */
+
+ /* At least (most) this many matches must be made. */
+ int lower_bound = -1, upper_bound = -1;
+
+ beg_interval = p - 1;
+
+ if (p == pend)
+ {
+ if (syntax & RE_NO_BK_BRACES)
+ goto unfetch_interval;
+ else
+ return REG_EBRACE;
+ }
+
+ GET_UNSIGNED_NUMBER (lower_bound);
+
+ if (c == ',')
+ {
+ GET_UNSIGNED_NUMBER (upper_bound);
+ if (upper_bound < 0) upper_bound = RE_DUP_MAX;
+ }
+ else
+ /* Interval such as `{1}' => match exactly once. */
+ upper_bound = lower_bound;
+
+ if (lower_bound < 0 || upper_bound > RE_DUP_MAX
+ || lower_bound > upper_bound)
+ {
+ if (syntax & RE_NO_BK_BRACES)
+ goto unfetch_interval;
+ else
+ return REG_BADBR;
+ }
+
+ if (!(syntax & RE_NO_BK_BRACES))
+ {
+ if (c != '\\') return REG_EBRACE;
+
+ PATFETCH (c);
+ }
+
+ if (c != '}')
+ {
+ if (syntax & RE_NO_BK_BRACES)
+ goto unfetch_interval;
+ else
+ return REG_BADBR;
+ }
+
+ /* We just parsed a valid interval. */
+
+ /* If it's invalid to have no preceding re. */
+ if (!laststart)
+ {
+ if (syntax & RE_CONTEXT_INVALID_OPS)
+ return REG_BADRPT;
+ else if (syntax & RE_CONTEXT_INDEP_OPS)
+ laststart = b;
+ else
+ goto unfetch_interval;
+ }
+
+ /* If the upper bound is zero, don't want to succeed at
+ all; jump from `laststart' to `b + 3', which will be
+ the end of the buffer after we insert the jump. */
+ if (upper_bound == 0)
+ {
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (jump, laststart, b + 3);
+ b += 3;
+ }
+
+ /* Otherwise, we have a nontrivial interval. When
+ we're all done, the pattern will look like:
+ set_number_at <jump count> <upper bound>
+ set_number_at <succeed_n count> <lower bound>
+ succeed_n <after jump addr> <succed_n count>
+ <body of loop>
+ jump_n <succeed_n addr> <jump count>
+ (The upper bound and `jump_n' are omitted if
+ `upper_bound' is 1, though.) */
+ else
+ { /* If the upper bound is > 1, we need to insert
+ more at the end of the loop. */
+ unsigned nbytes = 10 + (upper_bound > 1) * 10;
+
+ GET_BUFFER_SPACE (nbytes);
+
+ /* Initialize lower bound of the `succeed_n', even
+ though it will be set during matching by its
+ attendant `set_number_at' (inserted next),
+ because `re_compile_fastmap' needs to know.
+ Jump to the `jump_n' we might insert below. */
+ INSERT_JUMP2 (succeed_n, laststart,
+ b + 5 + (upper_bound > 1) * 5,
+ lower_bound);
+ b += 5;
+
+ /* Code to initialize the lower bound. Insert
+ before the `succeed_n'. The `5' is the last two
+ bytes of this `set_number_at', plus 3 bytes of
+ the following `succeed_n'. */
+ insert_op2 (set_number_at, laststart, 5, lower_bound, b);
+ b += 5;
+
+ if (upper_bound > 1)
+ { /* More than one repetition is allowed, so
+ append a backward jump to the `succeed_n'
+ that starts this interval.
+
+ When we've reached this during matching,
+ we'll have matched the interval once, so
+ jump back only `upper_bound - 1' times. */
+ STORE_JUMP2 (jump_n, b, laststart + 5,
+ upper_bound - 1);
+ b += 5;
+
+ /* The location we want to set is the second
+ parameter of the `jump_n'; that is `b-2' as
+ an absolute address. `laststart' will be
+ the `set_number_at' we're about to insert;
+ `laststart+3' the number to set, the source
+ for the relative address. But we are
+ inserting into the middle of the pattern --
+ so everything is getting moved up by 5.
+ Conclusion: (b - 2) - (laststart + 3) + 5,
+ i.e., b - laststart.
+
+ We insert this at the beginning of the loop
+ so that if we fail during matching, we'll
+ reinitialize the bounds. */
+ insert_op2 (set_number_at, laststart, b - laststart,
+ upper_bound - 1, b);
+ b += 5;
+ }
+ }
+ pending_exact = 0;
+ beg_interval = NULL;
+ }
+ break;
+
+ unfetch_interval:
+ /* If an invalid interval, match the characters as literals. */
+ assert (beg_interval);
+ p = beg_interval;
+ beg_interval = NULL;
+
+ /* normal_char and normal_backslash need `c'. */
+ PATFETCH (c);
+
+ if (!(syntax & RE_NO_BK_BRACES))
+ {
+ if (p > pattern && p[-1] == '\\')
+ goto normal_backslash;
+ }
+ goto normal_char;
+
+#ifdef emacs
+ /* There is no way to specify the before_dot and after_dot
+ operators. rms says this is ok. --karl */
+ case '=':
+ BUF_PUSH (at_dot);
+ break;
+
+ case 's':
+ laststart = b;
+ PATFETCH (c);
+ BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]);
+ break;
+
+ case 'S':
+ laststart = b;
+ PATFETCH (c);
+ BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]);
+ break;
+#endif /* emacs */
+
+
+ case 'w':
+ if (re_syntax_options & RE_NO_GNU_OPS)
+ goto normal_char;
+ laststart = b;
+ BUF_PUSH (wordchar);
+ break;
+
+
+ case 'W':
+ if (re_syntax_options & RE_NO_GNU_OPS)
+ goto normal_char;
+ laststart = b;
+ BUF_PUSH (notwordchar);
+ break;
+
+
+ case '<':
+ if (re_syntax_options & RE_NO_GNU_OPS)
+ goto normal_char;
+ BUF_PUSH (wordbeg);
+ break;
+
+ case '>':
+ if (re_syntax_options & RE_NO_GNU_OPS)
+ goto normal_char;
+ BUF_PUSH (wordend);
+ break;
+
+ case 'b':
+ if (re_syntax_options & RE_NO_GNU_OPS)
+ goto normal_char;
+ BUF_PUSH (wordbound);
+ break;
+
+ case 'B':
+ if (re_syntax_options & RE_NO_GNU_OPS)
+ goto normal_char;
+ BUF_PUSH (notwordbound);
+ break;
+
+ case '`':
+ if (re_syntax_options & RE_NO_GNU_OPS)
+ goto normal_char;
+ BUF_PUSH (begbuf);
+ break;
+
+ case '\'':
+ if (re_syntax_options & RE_NO_GNU_OPS)
+ goto normal_char;
+ BUF_PUSH (endbuf);
+ break;
+
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ if (syntax & RE_NO_BK_REFS)
+ goto normal_char;
+
+ c1 = c - '0';
+
+ if (c1 > regnum)
+ return REG_ESUBREG;
+
+ /* Can't back reference to a subexpression if inside of it. */
+ if (group_in_compile_stack (compile_stack, (regnum_t)c1))
+ goto normal_char;
+
+ laststart = b;
+ BUF_PUSH_2 (duplicate, c1);
+ break;
+
+
+ case '+':
+ case '?':
+ if (syntax & RE_BK_PLUS_QM)
+ goto handle_plus;
+ else
+ goto normal_backslash;
+
+ default:
+ normal_backslash:
+ /* You might think it would be useful for \ to mean
+ not to translate; but if we don't translate it
+ it will never match anything. */
+ c = TRANSLATE (c);
+ goto normal_char;
+ }
+ break;
+
+
+ default:
+ /* Expects the character in `c'. */
+ normal_char:
+ /* If no exactn currently being built. */
+ if (!pending_exact
+
+ /* If last exactn not at current position. */
+ || pending_exact + *pending_exact + 1 != b
+
+ /* We have only one byte following the exactn for the count. */
+ || *pending_exact == (1 << BYTEWIDTH) - 1
+
+ /* If followed by a repetition operator. */
+ || *p == '*' || *p == '^'
+ || ((syntax & RE_BK_PLUS_QM)
+ ? *p == '\\' && (p[1] == '+' || p[1] == '?')
+ : (*p == '+' || *p == '?'))
+ || ((syntax & RE_INTERVALS)
+ && ((syntax & RE_NO_BK_BRACES)
+ ? *p == '{'
+ : (p[0] == '\\' && p[1] == '{'))))
+ {
+ /* Start building a new exactn. */
+
+ laststart = b;
+
+ BUF_PUSH_2 (exactn, 0);
+ pending_exact = b - 1;
+ }
+
+ BUF_PUSH (c);
+ (*pending_exact)++;
+ break;
+ } /* switch (c) */
+ } /* while p != pend */
+
+
+ /* Through the pattern now. */
+
+ if (fixup_alt_jump)
+ STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
+
+ if (!COMPILE_STACK_EMPTY)
+ return REG_EPAREN;
+
+ free (compile_stack.stack);
+
+ /* We have succeeded; set the length of the buffer. */
+ bufp->used = b - bufp->buffer;
+
+#ifdef DEBUG
+ if (debug)
+ {
+ DEBUG_PRINT1 ("\nCompiled pattern: \n");
+ print_compiled_pattern (bufp);
+ }
+#endif /* DEBUG */
+
+ return REG_NOERROR;
+} /* regex_compile */
+
+/* Subroutines for `regex_compile'. */
+
+/* Store OP at LOC followed by two-byte integer parameter ARG. */
+
+static void
+store_op1 (op, loc, arg)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg;
+{
+ *loc = (unsigned char) op;
+ STORE_NUMBER (loc + 1, arg);
+}
+
+
+/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2. */
+
+static void
+store_op2 (op, loc, arg1, arg2)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg1, arg2;
+{
+ *loc = (unsigned char) op;
+ STORE_NUMBER (loc + 1, arg1);
+ STORE_NUMBER (loc + 3, arg2);
+}
+
+
+/* Copy the bytes from LOC to END to open up three bytes of space at LOC
+ for OP followed by two-byte integer parameter ARG. */
+
+static void
+insert_op1 (op, loc, arg, end)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg;
+ unsigned char *end;
+{
+ register unsigned char *pfrom = end;
+ register unsigned char *pto = end + 3;
+
+ while (pfrom != loc)
+ *--pto = *--pfrom;
+
+ store_op1 (op, loc, arg);
+}
+
+
+/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2. */
+
+static void
+insert_op2 (op, loc, arg1, arg2, end)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg1, arg2;
+ unsigned char *end;
+{
+ register unsigned char *pfrom = end;
+ register unsigned char *pto = end + 5;
+
+ while (pfrom != loc)
+ *--pto = *--pfrom;
+
+ store_op2 (op, loc, arg1, arg2);
+}
+
+
+/* P points to just after a ^ in PATTERN. Return true if that ^ comes
+ after an alternative or a begin-subexpression. We assume there is at
+ least one character before the ^. */
+
+static boolean
+at_begline_loc_p (pattern, p, syntax)
+ const char *pattern, *p;
+ reg_syntax_t syntax;
+{
+ const char *prev = p - 2;
+ boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\';
+
+ return
+ /* After a subexpression? */
+ (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash))
+ /* After an alternative? */
+ || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash));
+}
+
+
+/* The dual of at_begline_loc_p. This one is for $. We assume there is
+ at least one character after the $, i.e., `P < PEND'. */
+
+static boolean
+at_endline_loc_p (p, pend, syntax)
+ const char *p, *pend;
+ reg_syntax_t syntax;
+{
+ const char *next = p;
+ boolean next_backslash = *next == '\\';
+ const char *next_next = p + 1 < pend ? p + 1 : NULL;
+
+ return
+ /* Before a subexpression? */
+ (syntax & RE_NO_BK_PARENS ? *next == ')'
+ : next_backslash && next_next && *next_next == ')')
+ /* Before an alternative? */
+ || (syntax & RE_NO_BK_VBAR ? *next == '|'
+ : next_backslash && next_next && *next_next == '|');
+}
+
+
+/* Returns true if REGNUM is in one of COMPILE_STACK's elements and
+ false if it's not. */
+
+static boolean
+group_in_compile_stack (compile_stack, regnum)
+ compile_stack_type compile_stack;
+ regnum_t regnum;
+{
+ int this_element;
+
+ for (this_element = compile_stack.avail - 1;
+ this_element >= 0;
+ this_element--)
+ if (compile_stack.stack[this_element].regnum == regnum)
+ return true;
+
+ return false;
+}
+
+
+/* Read the ending character of a range (in a bracket expression) from the
+ uncompiled pattern *P_PTR (which ends at PEND). We assume the
+ starting character is in `P[-2]'. (`P[-1]' is the character `-'.)
+ Then we set the translation of all bits between the starting and
+ ending characters (inclusive) in the compiled pattern B.
+
+ Return an error code.
+
+ We use these short variable names so we can use the same macros as
+ `regex_compile' itself. */
+
+static reg_errcode_t
+compile_range (p_ptr, pend, translate, syntax, b)
+ const char **p_ptr, *pend;
+ char *translate;
+ reg_syntax_t syntax;
+ unsigned char *b;
+{
+ unsigned this_char;
+
+ const char *p = *p_ptr;
+ int range_start, range_end;
+
+ if (p == pend)
+ return REG_ERANGE;
+
+ /* Even though the pattern is a signed `char *', we need to fetch
+ with unsigned char *'s; if the high bit of the pattern character
+ is set, the range endpoints will be negative if we fetch using a
+ signed char *.
+
+ We also want to fetch the endpoints without translating them; the
+ appropriate translation is done in the bit-setting loop below. */
+ range_start = ((unsigned char *) p)[-2];
+ range_end = ((unsigned char *) p)[0];
+
+ /* Have to increment the pointer into the pattern string, so the
+ caller isn't still at the ending character. */
+ (*p_ptr)++;
+
+ /* If the start is after the end, the range is empty. */
+ if (range_start > range_end)
+ return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR;
+
+ /* Here we see why `this_char' has to be larger than an `unsigned
+ char' -- the range is inclusive, so if `range_end' == 0xff
+ (assuming 8-bit characters), we would otherwise go into an infinite
+ loop, since all characters <= 0xff. */
+ for (this_char = range_start; this_char <= range_end; this_char++)
+ {
+ SET_LIST_BIT (TRANSLATE (this_char));
+ }
+
+ return REG_NOERROR;
+}
+
+/* Failure stack declarations and macros; both re_compile_fastmap and
+ re_match_2 use a failure stack. These have to be macros because of
+ REGEX_ALLOCATE. */
+
+
+/* Number of failure points for which to initially allocate space
+ when matching. If this number is exceeded, we allocate more
+ space, so it is not a hard limit. */
+#ifndef INIT_FAILURE_ALLOC
+#define INIT_FAILURE_ALLOC 5
+#endif
+
+/* Roughly the maximum number of failure points on the stack. Would be
+ exactly that if always used MAX_FAILURE_SPACE each time we failed.
+ This is a variable only so users of regex can assign to it; we never
+ change it ourselves. */
+int re_max_failures = 2000;
+
+typedef const unsigned char *fail_stack_elt_t;
+
+typedef struct
+{
+ fail_stack_elt_t *stack;
+ unsigned size;
+ unsigned avail; /* Offset of next open position. */
+} fail_stack_type;
+
+#define FAIL_STACK_EMPTY() (fail_stack.avail == 0)
+#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0)
+#define FAIL_STACK_FULL() (fail_stack.avail == fail_stack.size)
+#define FAIL_STACK_TOP() (fail_stack.stack[fail_stack.avail])
+
+
+/* Initialize `fail_stack'. Do `return -2' if the alloc fails. */
+
+#define INIT_FAIL_STACK() \
+ do { \
+ fail_stack.stack = (fail_stack_elt_t *) \
+ REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \
+ \
+ if (fail_stack.stack == NULL) \
+ return -2; \
+ \
+ fail_stack.size = INIT_FAILURE_ALLOC; \
+ fail_stack.avail = 0; \
+ } while (0)
+
+
+/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items.
+
+ Return 1 if succeeds, and 0 if either ran out of memory
+ allocating space for it or it was already too large.
+
+ REGEX_REALLOCATE requires `destination' be declared. */
+
+#define DOUBLE_FAIL_STACK(fail_stack) \
+ ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS \
+ ? 0 \
+ : ((fail_stack).stack = (fail_stack_elt_t *) \
+ REGEX_REALLOCATE ((fail_stack).stack, \
+ (fail_stack).size * sizeof (fail_stack_elt_t), \
+ ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)), \
+ \
+ (fail_stack).stack == NULL \
+ ? 0 \
+ : ((fail_stack).size <<= 1, \
+ 1)))
+
+
+/* Push PATTERN_OP on FAIL_STACK.
+
+ Return 1 if was able to do so and 0 if ran out of memory allocating
+ space to do so. */
+#define PUSH_PATTERN_OP(pattern_op, fail_stack) \
+ ((FAIL_STACK_FULL () \
+ && !DOUBLE_FAIL_STACK (fail_stack)) \
+ ? 0 \
+ : ((fail_stack).stack[(fail_stack).avail++] = pattern_op, \
+ 1))
+
+/* This pushes an item onto the failure stack. Must be a four-byte
+ value. Assumes the variable `fail_stack'. Probably should only
+ be called from within `PUSH_FAILURE_POINT'. */
+#define PUSH_FAILURE_ITEM(item) \
+ fail_stack.stack[fail_stack.avail++] = (fail_stack_elt_t) item
+
+/* The complement operation. Assumes `fail_stack' is nonempty. */
+#define POP_FAILURE_ITEM() fail_stack.stack[--fail_stack.avail]
+
+/* Used to omit pushing failure point id's when we're not debugging. */
+#ifdef DEBUG
+#define DEBUG_PUSH PUSH_FAILURE_ITEM
+#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_ITEM ()
+#else
+#define DEBUG_PUSH(item)
+#define DEBUG_POP(item_addr)
+#endif
+
+
+/* Push the information about the state we will need
+ if we ever fail back to it.
+
+ Requires variables fail_stack, regstart, regend, reg_info, and
+ num_regs be declared. DOUBLE_FAIL_STACK requires `destination' be
+ declared.
+
+ Does `return FAILURE_CODE' if runs out of memory. */
+
+#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \
+ do { \
+ char *destination; \
+ /* Must be int, so when we don't save any registers, the arithmetic \
+ of 0 + -1 isn't done as unsigned. */ \
+ /* Can't be int, since there is not a shred of a guarantee that int \
+ is wide enough to hold a value of something to which pointer can \
+ be assigned */ \
+ s_reg_t this_reg; \
+ \
+ DEBUG_STATEMENT (failure_id++); \
+ DEBUG_STATEMENT (nfailure_points_pushed++); \
+ DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \
+ DEBUG_PRINT2 (" Before push, next avail: %d\n", (fail_stack).avail);\
+ DEBUG_PRINT2 (" size: %d\n", (fail_stack).size);\
+ \
+ DEBUG_PRINT2 (" slots needed: %d\n", NUM_FAILURE_ITEMS); \
+ DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \
+ \
+ /* Ensure we have enough space allocated for what we will push. */ \
+ while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \
+ { \
+ if (!DOUBLE_FAIL_STACK (fail_stack)) \
+ return failure_code; \
+ \
+ DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \
+ (fail_stack).size); \
+ DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\
+ }
+
+#define PUSH_FAILURE_POINT2(pattern_place, string_place, failure_code) \
+ /* Push the info, starting with the registers. */ \
+ DEBUG_PRINT1 ("\n"); \
+ \
+ PUSH_FAILURE_POINT_LOOP (); \
+ \
+ DEBUG_PRINT2 (" Pushing low active reg: %d\n", lowest_active_reg);\
+ PUSH_FAILURE_ITEM (lowest_active_reg); \
+ \
+ DEBUG_PRINT2 (" Pushing high active reg: %d\n", highest_active_reg);\
+ PUSH_FAILURE_ITEM (highest_active_reg); \
+ \
+ DEBUG_PRINT2 (" Pushing pattern 0x%x: ", pattern_place); \
+ DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \
+ PUSH_FAILURE_ITEM (pattern_place); \
+ \
+ DEBUG_PRINT2 (" Pushing string 0x%x: `", string_place); \
+ DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \
+ size2); \
+ DEBUG_PRINT1 ("'\n"); \
+ PUSH_FAILURE_ITEM (string_place); \
+ \
+ DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \
+ DEBUG_PUSH (failure_id); \
+ } while (0)
+
+/* Pulled out of PUSH_FAILURE_POINT() to shorten the definition
+ of that macro. (for VAX C) */
+#define PUSH_FAILURE_POINT_LOOP() \
+ for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \
+ this_reg++) \
+ { \
+ DEBUG_PRINT2 (" Pushing reg: %d\n", this_reg); \
+ DEBUG_STATEMENT (num_regs_pushed++); \
+ \
+ DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \
+ PUSH_FAILURE_ITEM (regstart[this_reg]); \
+ \
+ DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \
+ PUSH_FAILURE_ITEM (regend[this_reg]); \
+ \
+ DEBUG_PRINT2 (" info: 0x%x\n ", reg_info[this_reg]); \
+ DEBUG_PRINT2 (" match_null=%d", \
+ REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" matched_something=%d", \
+ MATCHED_SOMETHING (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" ever_matched=%d", \
+ EVER_MATCHED_SOMETHING (reg_info[this_reg])); \
+ DEBUG_PRINT1 ("\n"); \
+ PUSH_FAILURE_ITEM (reg_info[this_reg].word); \
+ }
+
+/* This is the number of items that are pushed and popped on the stack
+ for each register. */
+#define NUM_REG_ITEMS 3
+
+/* Individual items aside from the registers. */
+#ifdef DEBUG
+#define NUM_NONREG_ITEMS 5 /* Includes failure point id. */
+#else
+#define NUM_NONREG_ITEMS 4
+#endif
+
+/* We push at most this many items on the stack. */
+#define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS)
+
+/* We actually push this many items. */
+#define NUM_FAILURE_ITEMS \
+ ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS \
+ + NUM_NONREG_ITEMS)
+
+/* How many items can still be added to the stack without overflowing it. */
+#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail)
+
+
+/* Pops what PUSH_FAIL_STACK pushes.
+
+ We restore into the parameters, all of which should be lvalues:
+ STR -- the saved data position.
+ PAT -- the saved pattern position.
+ LOW_REG, HIGH_REG -- the highest and lowest active registers.
+ REGSTART, REGEND -- arrays of string positions.
+ REG_INFO -- array of information about each subexpression.
+
+ Also assumes the variables `fail_stack' and (if debugging), `bufp',
+ `pend', `string1', `size1', `string2', and `size2'. */
+
+#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\
+{ \
+ DEBUG_STATEMENT (fail_stack_elt_t failure_id;) \
+ s_reg_t this_reg; \
+ const unsigned char *string_temp; \
+ \
+ assert (!FAIL_STACK_EMPTY ()); \
+ \
+ /* Remove failure points and point to how many regs pushed. */ \
+ DEBUG_PRINT1 ("POP_FAILURE_POINT:\n"); \
+ DEBUG_PRINT2 (" Before pop, next avail: %d\n", fail_stack.avail); \
+ DEBUG_PRINT2 (" size: %d\n", fail_stack.size); \
+ \
+ assert (fail_stack.avail >= NUM_NONREG_ITEMS); \
+ \
+ DEBUG_POP (&failure_id); \
+ DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id); \
+ \
+ /* If the saved string location is NULL, it came from an \
+ on_failure_keep_string_jump opcode, and we want to throw away the \
+ saved NULL, thus retaining our current position in the string. */ \
+ string_temp = POP_FAILURE_ITEM (); \
+ if (string_temp != NULL) \
+ str = (const char *) string_temp; \
+ \
+ DEBUG_PRINT2 (" Popping string 0x%x: `", str); \
+ DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \
+ DEBUG_PRINT1 ("'\n"); \
+ \
+ pat = (unsigned char *) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" Popping pattern 0x%x: ", pat); \
+ DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \
+ \
+ POP_FAILURE_POINT2 (low_reg, high_reg, regstart, regend, reg_info);
+
+/* Pulled out of POP_FAILURE_POINT() to shorten the definition
+ of that macro. (for MSC 5.1) */
+#define POP_FAILURE_POINT2(low_reg, high_reg, regstart, regend, reg_info) \
+ \
+ /* Restore register info. */ \
+ high_reg = (active_reg_t) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" Popping high active reg: %d\n", high_reg); \
+ \
+ low_reg = (active_reg_t) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" Popping low active reg: %d\n", low_reg); \
+ \
+ for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \
+ { \
+ DEBUG_PRINT2 (" Popping reg: %d\n", this_reg); \
+ \
+ reg_info[this_reg].word = POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" info: 0x%x\n", reg_info[this_reg]); \
+ \
+ regend[this_reg] = (const char *) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \
+ \
+ regstart[this_reg] = (const char *) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \
+ } \
+ \
+ DEBUG_STATEMENT (nfailure_points_popped++); \
+} /* POP_FAILURE_POINT */
+
+
+/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in
+ BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible
+ characters can start a string that matches the pattern. This fastmap
+ is used by re_search to skip quickly over impossible starting points.
+
+ The caller must supply the address of a (1 << BYTEWIDTH)-byte data
+ area as BUFP->fastmap.
+
+ We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in
+ the pattern buffer.
+
+ Returns 0 if we succeed, -2 if an internal error. */
+
+int
+re_compile_fastmap (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ int j, k;
+ fail_stack_type fail_stack;
+#ifndef REGEX_MALLOC
+ char *destination;
+#endif
+ /* We don't push any register information onto the failure stack. */
+ unsigned num_regs = 0;
+
+ register char *fastmap = bufp->fastmap;
+ unsigned char *pattern = bufp->buffer;
+ const unsigned char *p = pattern;
+ register unsigned char *pend = pattern + bufp->used;
+
+ /* Assume that each path through the pattern can be null until
+ proven otherwise. We set this false at the bottom of switch
+ statement, to which we get only if a particular path doesn't
+ match the empty string. */
+ boolean path_can_be_null = true;
+
+ /* We aren't doing a `succeed_n' to begin with. */
+ boolean succeed_n_p = false;
+
+ assert (fastmap != NULL && p != NULL);
+
+ INIT_FAIL_STACK ();
+ bzero (fastmap, 1 << BYTEWIDTH); /* Assume nothing's valid. */
+ bufp->fastmap_accurate = 1; /* It will be when we're done. */
+ bufp->can_be_null = 0;
+
+ while (p != pend || !FAIL_STACK_EMPTY ())
+ {
+ if (p == pend)
+ {
+ bufp->can_be_null |= path_can_be_null;
+
+ /* Reset for next path. */
+ path_can_be_null = true;
+
+ p = fail_stack.stack[--fail_stack.avail];
+ }
+
+ /* We should never be about to go beyond the end of the pattern. */
+ assert (p < pend);
+
+#ifdef SWITCH_ENUM_BUG
+ switch ((int) ((re_opcode_t) *p++))
+#else
+ switch ((re_opcode_t) *p++)
+#endif
+ {
+
+ /* I guess the idea here is to simply not bother with a fastmap
+ if a backreference is used, since it's too hard to figure out
+ the fastmap for the corresponding group. Setting
+ `can_be_null' stops `re_search_2' from using the fastmap, so
+ that is all we do. */
+ case duplicate:
+ bufp->can_be_null = 1;
+ return 0;
+
+
+ /* Following are the cases which match a character. These end
+ with `break'. */
+
+ case exactn:
+ fastmap[p[1]] = 1;
+ break;
+
+
+ case charset:
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
+ fastmap[j] = 1;
+ break;
+
+
+ case charset_not:
+ /* Chars beyond end of map must be allowed. */
+ for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
+ fastmap[j] = 1;
+
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
+ fastmap[j] = 1;
+ break;
+
+
+ case wordchar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) == Sword)
+ fastmap[j] = 1;
+ break;
+
+
+ case notwordchar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) != Sword)
+ fastmap[j] = 1;
+ break;
+
+
+ case anychar:
+ /* `.' matches anything ... */
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ fastmap[j] = 1;
+
+ /* ... except perhaps newline. */
+ if (!(bufp->syntax & RE_DOT_NEWLINE))
+ fastmap['\n'] = 0;
+
+ /* Return if we have already set `can_be_null'; if we have,
+ then the fastmap is irrelevant. Something's wrong here. */
+ else if (bufp->can_be_null)
+ return 0;
+
+ /* Otherwise, have to check alternative paths. */
+ break;
+
+
+#ifdef emacs
+ case syntaxspec:
+ k = *p++;
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) == (enum syntaxcode) k)
+ fastmap[j] = 1;
+ break;
+
+
+ case notsyntaxspec:
+ k = *p++;
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) != (enum syntaxcode) k)
+ fastmap[j] = 1;
+ break;
+
+
+ /* All cases after this match the empty string. These end with
+ `continue'. */
+
+
+ case before_dot:
+ case at_dot:
+ case after_dot:
+ continue;
+#endif /* not emacs */
+
+
+ case no_op:
+ case begline:
+ case endline:
+ case begbuf:
+ case endbuf:
+ case wordbound:
+ case notwordbound:
+ case wordbeg:
+ case wordend:
+ case push_dummy_failure:
+ continue;
+
+
+ case jump_n:
+ case pop_failure_jump:
+ case maybe_pop_jump:
+ case jump:
+ case jump_past_alt:
+ case dummy_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (j, p);
+ p += j;
+ if (j > 0)
+ continue;
+
+ /* Jump backward implies we just went through the body of a
+ loop and matched nothing. Opcode jumped to should be
+ `on_failure_jump' or `succeed_n'. Just treat it like an
+ ordinary jump. For a * loop, it has pushed its failure
+ point already; if so, discard that as redundant. */
+ if ((re_opcode_t) *p != on_failure_jump
+ && (re_opcode_t) *p != succeed_n)
+ continue;
+
+ p++;
+ EXTRACT_NUMBER_AND_INCR (j, p);
+ p += j;
+
+ /* If what's on the stack is where we are now, pop it. */
+ if (!FAIL_STACK_EMPTY ()
+ && fail_stack.stack[fail_stack.avail - 1] == p)
+ fail_stack.avail--;
+
+ continue;
+
+
+ case on_failure_jump:
+ case on_failure_keep_string_jump:
+ handle_on_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (j, p);
+
+ /* For some patterns, e.g., `(a?)?', `p+j' here points to the
+ end of the pattern. We don't want to push such a point,
+ since when we restore it above, entering the switch will
+ increment `p' past the end of the pattern. We don't need
+ to push such a point since we obviously won't find any more
+ fastmap entries beyond `pend'. Such a pattern can match
+ the null string, though. */
+ if (p + j < pend)
+ {
+ if (!PUSH_PATTERN_OP (p + j, fail_stack))
+ return -2;
+ }
+ else
+ bufp->can_be_null = 1;
+
+ if (succeed_n_p)
+ {
+ EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */
+ succeed_n_p = false;
+ }
+
+ continue;
+
+
+ case succeed_n:
+ /* Get to the number of times to succeed. */
+ p += 2;
+
+ /* Increment p past the n for when k != 0. */
+ EXTRACT_NUMBER_AND_INCR (k, p);
+ if (k == 0)
+ {
+ p -= 4;
+ succeed_n_p = true; /* Spaghetti code alert. */
+ goto handle_on_failure_jump;
+ }
+ continue;
+
+
+ case set_number_at:
+ p += 4;
+ continue;
+
+
+ case start_memory:
+ case stop_memory:
+ p += 2;
+ continue;
+
+
+ default:
+ abort (); /* We have listed all the cases. */
+ } /* switch *p++ */
+
+ /* Getting here means we have found the possible starting
+ characters for one path of the pattern -- and that the empty
+ string does not match. We need not follow this path further.
+ Instead, look at the next alternative (remembered on the
+ stack), or quit if no more. The test at the top of the loop
+ does these things. */
+ path_can_be_null = false;
+ p = pend;
+ } /* while p */
+
+ /* Set `can_be_null' for the last path (also the first path, if the
+ pattern is empty). */
+ bufp->can_be_null |= path_can_be_null;
+ return 0;
+} /* re_compile_fastmap */
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+ ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use
+ this memory for recording register information. STARTS and ENDS
+ must be allocated using the malloc library routine, and must each
+ be at least NUM_REGS * sizeof (regoff_t) bytes long.
+
+ If NUM_REGS == 0, then subsequent matches should allocate their own
+ register data.
+
+ Unless this function is called, the first search or match using
+ PATTERN_BUFFER will allocate its own register data, without
+ freeing the old data. */
+
+void
+re_set_registers (bufp, regs, num_regs, starts, ends)
+ struct re_pattern_buffer *bufp;
+ struct re_registers *regs;
+ unsigned num_regs;
+ regoff_t *starts, *ends;
+{
+ if (num_regs)
+ {
+ bufp->regs_allocated = REGS_REALLOCATE;
+ regs->num_regs = num_regs;
+ regs->start = starts;
+ regs->end = ends;
+ }
+ else
+ {
+ bufp->regs_allocated = REGS_UNALLOCATED;
+ regs->num_regs = 0;
+ regs->start = regs->end = 0;
+ }
+}
+
+/* Searching routines. */
+
+/* Like re_search_2, below, but only one string is specified, and
+ doesn't let you say where to stop matching. */
+
+int
+re_search (bufp, string, size, startpos, range, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ int size, startpos, range;
+ struct re_registers *regs;
+{
+ return re_search_2 (bufp, NULL, 0, string, size, startpos, range,
+ regs, size);
+}
+
+
+/* Using the compiled pattern in BUFP->buffer, first tries to match the
+ virtual concatenation of STRING1 and STRING2, starting first at index
+ STARTPOS, then at STARTPOS + 1, and so on.
+
+ STRING1 and STRING2 have length SIZE1 and SIZE2, respectively.
+
+ RANGE is how far to scan while trying to match. RANGE = 0 means try
+ only at STARTPOS; in general, the last start tried is STARTPOS +
+ RANGE.
+
+ In REGS, return the indices of the virtual concatenation of STRING1
+ and STRING2 that matched the entire BUFP->buffer and its contained
+ subexpressions.
+
+ Do not consider matching one past the index STOP in the virtual
+ concatenation of STRING1 and STRING2.
+
+ We return either the position in the strings at which the match was
+ found, -1 if no match, or -2 if error (such as failure
+ stack overflow). */
+
+int
+re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ int size1, size2;
+ int startpos;
+ int range;
+ struct re_registers *regs;
+ int stop;
+{
+ int val;
+ register char *fastmap = bufp->fastmap;
+ register char *translate = bufp->translate;
+ int total_size = size1 + size2;
+ int endpos = startpos + range;
+
+ /* Check for out-of-range STARTPOS. */
+ if (startpos < 0 || startpos > total_size)
+ return -1;
+
+ /* Fix up RANGE if it might eventually take us outside
+ the virtual concatenation of STRING1 and STRING2. */
+ if (endpos < -1)
+ range = -1 - startpos;
+ else if (endpos > total_size)
+ range = total_size - startpos;
+
+ /* If the search isn't to be a backwards one, don't waste time in a
+ search for a pattern that must be anchored. */
+ if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf && range > 0)
+ {
+ if (startpos > 0)
+ return -1;
+ else
+ range = 1;
+ }
+
+ /* Update the fastmap now if not correct already. */
+ if (fastmap && !bufp->fastmap_accurate)
+ if (re_compile_fastmap (bufp) == -2)
+ return -2;
+
+ /* Loop through the string, looking for a place to start matching. */
+ for (;;)
+ {
+ /* If a fastmap is supplied, skip quickly over characters that
+ cannot be the start of a match. If the pattern can match the
+ null string, however, we don't need to skip characters; we want
+ the first null string. */
+ if (fastmap && startpos < total_size && !bufp->can_be_null)
+ {
+ if (range > 0) /* Searching forwards. */
+ {
+ register const char *d;
+ register int lim = 0;
+ int irange = range;
+
+ if (startpos < size1 && startpos + range >= size1)
+ lim = range - (size1 - startpos);
+
+ d = (startpos >= size1 ? string2 - size1 : string1) + startpos;
+
+ /* Written out as an if-else to avoid testing `translate'
+ inside the loop. */
+ if (translate)
+ while (range > lim
+ && !fastmap[(unsigned char)
+ translate[(unsigned char) *d++]])
+ range--;
+ else
+ while (range > lim && !fastmap[(unsigned char) *d++])
+ range--;
+
+ startpos += irange - range;
+ }
+ else /* Searching backwards. */
+ {
+ register char c = (size1 == 0 || startpos >= size1
+ ? string2[startpos - size1]
+ : string1[startpos]);
+
+ if (!fastmap[(unsigned char) TRANSLATE (c)])
+ goto advance;
+ }
+ }
+
+ /* If can't match the null string, and that's all we have left, fail. */
+ if (range >= 0 && startpos == total_size && fastmap
+ && !bufp->can_be_null)
+ return -1;
+
+ val = re_match_2 (bufp, string1, size1, string2, size2,
+ startpos, regs, stop);
+ if (val >= 0)
+ return startpos;
+
+ if (val == -2)
+ return -2;
+
+ advance:
+ if (!range)
+ break;
+ else if (range > 0)
+ {
+ range--;
+ startpos++;
+ }
+ else
+ {
+ range++;
+ startpos--;
+ }
+ }
+ return -1;
+} /* re_search_2 */
+
+/* Structure for per-register (a.k.a. per-group) information.
+ This must not be longer than one word, because we push this value
+ onto the failure stack. Other register information, such as the
+ starting and ending positions (which are addresses), and the list of
+ inner groups (which is a bits list) are maintained in separate
+ variables.
+
+ We are making a (strictly speaking) nonportable assumption here: that
+ the compiler will pack our bit fields into something that fits into
+ the type of `word', i.e., is something that fits into one item on the
+ failure stack. */
+
+/* Declarations and macros for re_match_2. */
+
+typedef union
+{
+ fail_stack_elt_t word;
+ struct
+ {
+ /* This field is one if this group can match the empty string,
+ zero if not. If not yet determined, `MATCH_NULL_UNSET_VALUE'. */
+#define MATCH_NULL_UNSET_VALUE 3
+ unsigned match_null_string_p : 2;
+ unsigned is_active : 1;
+ unsigned matched_something : 1;
+ unsigned ever_matched_something : 1;
+ } bits;
+} register_info_type;
+
+#define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p)
+#define IS_ACTIVE(R) ((R).bits.is_active)
+#define MATCHED_SOMETHING(R) ((R).bits.matched_something)
+#define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something)
+
+static boolean group_match_null_string_p _RE_ARGS((unsigned char **p,
+ unsigned char *end,
+ register_info_type *reg_info));
+static boolean alt_match_null_string_p _RE_ARGS((unsigned char *p,
+ unsigned char *end,
+ register_info_type *reg_info));
+static boolean common_op_match_null_string_p _RE_ARGS((unsigned char **p,
+ unsigned char *end,
+ register_info_type *reg_info));
+static int bcmp_translate _RE_ARGS((const char *s1, const char *s2,
+ int len, char *translate));
+
+/* Call this when have matched a real character; it sets `matched' flags
+ for the subexpressions which we are currently inside. Also records
+ that those subexprs have matched. */
+#define SET_REGS_MATCHED() \
+ do \
+ { \
+ active_reg_t r; \
+ for (r = lowest_active_reg; r <= highest_active_reg; r++) \
+ { \
+ MATCHED_SOMETHING (reg_info[r]) \
+ = EVER_MATCHED_SOMETHING (reg_info[r]) \
+ = 1; \
+ } \
+ } \
+ while (0)
+
+
+/* This converts PTR, a pointer into one of the search strings `string1'
+ and `string2' into an offset from the beginning of that string. */
+#define POINTER_TO_OFFSET(ptr) \
+ (FIRST_STRING_P (ptr) ? (ptr) - string1 : (ptr) - string2 + size1)
+
+/* Registers are set to a sentinel when they haven't yet matched. */
+#define REG_UNSET_VALUE ((char *) -1)
+#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
+
+
+/* Macros for dealing with the split strings in re_match_2. */
+
+#define MATCHING_IN_FIRST_STRING (dend == end_match_1)
+
+/* Call before fetching a character with *d. This switches over to
+ string2 if necessary. */
+#define PREFETCH() \
+ while (d == dend) \
+ { \
+ /* End of string2 => fail. */ \
+ if (dend == end_match_2) \
+ goto fail; \
+ /* End of string1 => advance to string2. */ \
+ d = string2; \
+ dend = end_match_2; \
+ }
+
+
+/* Test if at very beginning or at very end of the virtual concatenation
+ of `string1' and `string2'. If only one string, it's `string2'. */
+#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2)
+#define AT_STRINGS_END(d) ((d) == end2)
+
+
+/* Test if D points to a character which is word-constituent. We have
+ two special cases to check for: if past the end of string1, look at
+ the first character in string2; and if before the beginning of
+ string2, look at the last character in string1. */
+#define WORDCHAR_P(d) \
+ (SYNTAX ((d) == end1 ? *string2 \
+ : (d) == string2 - 1 ? *(end1 - 1) : *(d)) \
+ == Sword)
+
+/* Test if the character before D and the one at D differ with respect
+ to being word-constituent. */
+#define AT_WORD_BOUNDARY(d) \
+ (AT_STRINGS_BEG (d) || AT_STRINGS_END (d) \
+ || WORDCHAR_P (d - 1) != WORDCHAR_P (d))
+
+
+/* Free everything we malloc. */
+#ifdef REGEX_MALLOC
+#define FREE_VAR(var) if (var) free (var); var = NULL
+#define FREE_VARIABLES() \
+ do { \
+ FREE_VAR (fail_stack.stack); \
+ FREE_VAR (regstart); \
+ FREE_VAR (regend); \
+ FREE_VAR (old_regstart); \
+ FREE_VAR (old_regend); \
+ FREE_VAR (best_regstart); \
+ FREE_VAR (best_regend); \
+ FREE_VAR (reg_info); \
+ FREE_VAR (reg_dummy); \
+ FREE_VAR (reg_info_dummy); \
+ } while (0)
+#else /* not REGEX_MALLOC */
+/* Some MIPS systems (at least) want this to free alloca'd storage. */
+#define FREE_VARIABLES() alloca (0)
+#endif /* not REGEX_MALLOC */
+
+
+/* These values must meet several constraints. They must not be valid
+ register values; since we have a limit of 255 registers (because
+ we use only one byte in the pattern for the register number), we can
+ use numbers larger than 255. They must differ by 1, because of
+ NUM_FAILURE_ITEMS above. And the value for the lowest register must
+ be larger than the value for the highest register, so we do not try
+ to actually save any registers when none are active. */
+#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH)
+#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1)
+
+/* Matching routines. */
+
+#ifndef emacs /* Emacs never uses this. */
+/* re_match is like re_match_2 except it takes only a single string. */
+
+int
+re_match (bufp, string, size, pos, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ int size, pos;
+ struct re_registers *regs;
+ {
+ return re_match_2 (bufp, NULL, 0, string, size, pos, regs, size);
+}
+#endif /* not emacs */
+
+
+/* re_match_2 matches the compiled pattern in BUFP against the
+ the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
+ and SIZE2, respectively). We start matching at POS, and stop
+ matching at STOP.
+
+ If REGS is non-null and the `no_sub' field of BUFP is nonzero, we
+ store offsets for the substring each group matched in REGS. See the
+ documentation for exactly how many groups we fill.
+
+ We return -1 if no match, -2 if an internal error (such as the
+ failure stack overflowing). Otherwise, we return the length of the
+ matched substring. */
+
+int
+re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ int size1, size2;
+ int pos;
+ struct re_registers *regs;
+ int stop;
+{
+ /* General temporaries. */
+ int mcnt;
+ unsigned char *p1;
+
+ /* Just past the end of the corresponding string. */
+ const char *end1, *end2;
+
+ /* Pointers into string1 and string2, just past the last characters in
+ each to consider matching. */
+ const char *end_match_1, *end_match_2;
+
+ /* Where we are in the data, and the end of the current string. */
+ const char *d, *dend;
+
+ /* Where we are in the pattern, and the end of the pattern. */
+ unsigned char *p = bufp->buffer;
+ register unsigned char *pend = p + bufp->used;
+
+ /* We use this to map every character in the string. */
+ char *translate = bufp->translate;
+
+ /* Failure point stack. Each place that can handle a failure further
+ down the line pushes a failure point on this stack. It consists of
+ restart, regend, and reg_info for all registers corresponding to
+ the subexpressions we're currently inside, plus the number of such
+ registers, and, finally, two char *'s. The first char * is where
+ to resume scanning the pattern; the second one is where to resume
+ scanning the strings. If the latter is zero, the failure point is
+ a ``dummy''; if a failure happens and the failure point is a dummy,
+ it gets discarded and the next next one is tried. */
+ fail_stack_type fail_stack;
+#ifdef DEBUG
+ static unsigned failure_id = 0;
+ unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0;
+#endif
+
+ /* We fill all the registers internally, independent of what we
+ return, for use in backreferences. The number here includes
+ an element for register zero. */
+ size_t num_regs = bufp->re_nsub + 1;
+
+ /* The currently active registers. */
+ active_reg_t lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+ active_reg_t highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+
+ /* Information on the contents of registers. These are pointers into
+ the input strings; they record just what was matched (on this
+ attempt) by a subexpression part of the pattern, that is, the
+ regnum-th regstart pointer points to where in the pattern we began
+ matching and the regnum-th regend points to right after where we
+ stopped matching the regnum-th subexpression. (The zeroth register
+ keeps track of what the whole pattern matches.) */
+ const char **regstart = 0, **regend = 0;
+
+ /* If a group that's operated upon by a repetition operator fails to
+ match anything, then the register for its start will need to be
+ restored because it will have been set to wherever in the string we
+ are when we last see its open-group operator. Similarly for a
+ register's end. */
+ const char **old_regstart = 0, **old_regend = 0;
+
+ /* The is_active field of reg_info helps us keep track of which (possibly
+ nested) subexpressions we are currently in. The matched_something
+ field of reg_info[reg_num] helps us tell whether or not we have
+ matched any of the pattern so far this time through the reg_num-th
+ subexpression. These two fields get reset each time through any
+ loop their register is in. */
+ register_info_type *reg_info = 0;
+
+ /* The following record the register info as found in the above
+ variables when we find a match better than any we've seen before.
+ This happens as we backtrack through the failure points, which in
+ turn happens only if we have not yet matched the entire string. */
+ unsigned best_regs_set = false;
+ const char **best_regstart = 0, **best_regend = 0;
+
+ /* Logically, this is `best_regend[0]'. But we don't want to have to
+ allocate space for that if we're not allocating space for anything
+ else (see below). Also, we never need info about register 0 for
+ any of the other register vectors, and it seems rather a kludge to
+ treat `best_regend' differently than the rest. So we keep track of
+ the end of the best match so far in a separate variable. We
+ initialize this to NULL so that when we backtrack the first time
+ and need to test it, it's not garbage. */
+ const char *match_end = NULL;
+
+ /* Used when we pop values we don't care about. */
+ const char **reg_dummy = 0;
+ register_info_type *reg_info_dummy = 0;
+
+#ifdef DEBUG
+ /* Counts the total number of registers pushed. */
+ unsigned num_regs_pushed = 0;
+#endif
+
+ DEBUG_PRINT1 ("\n\nEntering re_match_2.\n");
+
+ INIT_FAIL_STACK ();
+
+ /* Do not bother to initialize all the register variables if there are
+ no groups in the pattern, as it takes a fair amount of time. If
+ there are groups, we include space for register 0 (the whole
+ pattern), even though we never use it, since it simplifies the
+ array indexing. We should fix this. */
+ if (bufp->re_nsub)
+ {
+ regstart = REGEX_TALLOC (num_regs, const char *);
+ regend = REGEX_TALLOC (num_regs, const char *);
+ old_regstart = REGEX_TALLOC (num_regs, const char *);
+ old_regend = REGEX_TALLOC (num_regs, const char *);
+ best_regstart = REGEX_TALLOC (num_regs, const char *);
+ best_regend = REGEX_TALLOC (num_regs, const char *);
+ reg_info = REGEX_TALLOC (num_regs, register_info_type);
+ reg_dummy = REGEX_TALLOC (num_regs, const char *);
+ reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type);
+
+ if (!(regstart && regend && old_regstart && old_regend && reg_info
+ && best_regstart && best_regend && reg_dummy && reg_info_dummy))
+ {
+ FREE_VARIABLES ();
+ return -2;
+ }
+ }
+#ifdef REGEX_MALLOC
+ else
+ {
+ /* We must initialize all our variables to NULL, so that
+ `FREE_VARIABLES' doesn't try to free them. */
+ regstart = regend = old_regstart = old_regend = best_regstart
+ = best_regend = reg_dummy = NULL;
+ reg_info = reg_info_dummy = (register_info_type *) NULL;
+ }
+#endif /* REGEX_MALLOC */
+
+ /* The starting position is bogus. */
+ if (pos < 0 || pos > size1 + size2)
+ {
+ FREE_VARIABLES ();
+ return -1;
+ }
+
+ /* Initialize subexpression text positions to -1 to mark ones that no
+ start_memory/stop_memory has been seen for. Also initialize the
+ register information struct. */
+ for (mcnt = 1; mcnt < num_regs; mcnt++)
+ {
+ regstart[mcnt] = regend[mcnt]
+ = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE;
+
+ REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE;
+ IS_ACTIVE (reg_info[mcnt]) = 0;
+ MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+ EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+ }
+
+ /* We move `string1' into `string2' if the latter's empty -- but not if
+ `string1' is null. */
+ if (size2 == 0 && string1 != NULL)
+ {
+ string2 = string1;
+ size2 = size1;
+ string1 = 0;
+ size1 = 0;
+ }
+ end1 = string1 + size1;
+ end2 = string2 + size2;
+
+ /* Compute where to stop matching, within the two strings. */
+ if (stop <= size1)
+ {
+ end_match_1 = string1 + stop;
+ end_match_2 = string2;
+ }
+ else
+ {
+ end_match_1 = end1;
+ end_match_2 = string2 + stop - size1;
+ }
+
+ /* `p' scans through the pattern as `d' scans through the data.
+ `dend' is the end of the input string that `d' points within. `d'
+ is advanced into the following input string whenever necessary, but
+ this happens before fetching; therefore, at the beginning of the
+ loop, `d' can be pointing at the end of a string, but it cannot
+ equal `string2'. */
+ if (size1 > 0 && pos <= size1)
+ {
+ d = string1 + pos;
+ dend = end_match_1;
+ }
+ else
+ {
+ d = string2 + pos - size1;
+ dend = end_match_2;
+ }
+
+ DEBUG_PRINT1 ("The compiled pattern is: ");
+ DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend);
+ DEBUG_PRINT1 ("The string to match is: `");
+ DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2);
+ DEBUG_PRINT1 ("'\n");
+
+ /* This loops over pattern commands. It exits by returning from the
+ function if the match is complete, or it drops through if the match
+ fails at this starting point in the input data. */
+ for (;;)
+ {
+ DEBUG_PRINT2 ("\n0x%x: ", p);
+
+ if (p == pend)
+ { /* End of pattern means we might have succeeded. */
+ DEBUG_PRINT1 ("end of pattern ... ");
+
+ /* If we haven't matched the entire string, and we want the
+ longest match, try backtracking. */
+ if (d != end_match_2)
+ {
+ DEBUG_PRINT1 ("backtracking.\n");
+
+ if (!FAIL_STACK_EMPTY ())
+ { /* More failure points to try. */
+ boolean same_str_p = (FIRST_STRING_P (match_end)
+ == MATCHING_IN_FIRST_STRING);
+
+ /* If exceeds best match so far, save it. */
+ if (!best_regs_set
+ || (same_str_p && d > match_end)
+ || (!same_str_p && !MATCHING_IN_FIRST_STRING))
+ {
+ best_regs_set = true;
+ match_end = d;
+
+ DEBUG_PRINT1 ("\nSAVING match as best so far.\n");
+
+ for (mcnt = 1; mcnt < num_regs; mcnt++)
+ {
+ best_regstart[mcnt] = regstart[mcnt];
+ best_regend[mcnt] = regend[mcnt];
+ }
+ }
+ goto fail;
+ }
+
+ /* If no failure points, don't restore garbage. */
+ else if (best_regs_set)
+ {
+ restore_best_regs:
+ /* Restore best match. It may happen that `dend ==
+ end_match_1' while the restored d is in string2.
+ For example, the pattern `x.*y.*z' against the
+ strings `x-' and `y-z-', if the two strings are
+ not consecutive in memory. */
+ DEBUG_PRINT1 ("Restoring best registers.\n");
+
+ d = match_end;
+ dend = ((d >= string1 && d <= end1)
+ ? end_match_1 : end_match_2);
+
+ for (mcnt = 1; mcnt < num_regs; mcnt++)
+ {
+ regstart[mcnt] = best_regstart[mcnt];
+ regend[mcnt] = best_regend[mcnt];
+ }
+ }
+ } /* d != end_match_2 */
+
+ DEBUG_PRINT1 ("Accepting match.\n");
+
+ /* If caller wants register contents data back, do it. */
+ if (regs && !bufp->no_sub)
+ {
+ /* Have the register data arrays been allocated? */
+ if (bufp->regs_allocated == REGS_UNALLOCATED)
+ { /* No. So allocate them with malloc. We need one
+ extra element beyond `num_regs' for the `-1' marker
+ GNU code uses. */
+ regs->num_regs = MAX (RE_NREGS, num_regs + 1);
+ regs->start = TALLOC (regs->num_regs, regoff_t);
+ regs->end = TALLOC (regs->num_regs, regoff_t);
+ if (regs->start == NULL || regs->end == NULL)
+ return -2;
+ bufp->regs_allocated = REGS_REALLOCATE;
+ }
+ else if (bufp->regs_allocated == REGS_REALLOCATE)
+ { /* Yes. If we need more elements than were already
+ allocated, reallocate them. If we need fewer, just
+ leave it alone. */
+ if (regs->num_regs < num_regs + 1)
+ {
+ regs->num_regs = num_regs + 1;
+ RETALLOC (regs->start, regs->num_regs, regoff_t);
+ RETALLOC (regs->end, regs->num_regs, regoff_t);
+ if (regs->start == NULL || regs->end == NULL)
+ return -2;
+ }
+ }
+ else
+ {
+ /* These braces fend off a "empty body in an else-statement"
+ warning under GCC when assert expands to nothing. */
+ assert (bufp->regs_allocated == REGS_FIXED);
+ }
+
+ /* Convert the pointer data in `regstart' and `regend' to
+ indices. Register zero has to be set differently,
+ since we haven't kept track of any info for it. */
+ if (regs->num_regs > 0)
+ {
+ regs->start[0] = pos;
+ regs->end[0] = (MATCHING_IN_FIRST_STRING ? d - string1
+ : d - string2 + size1);
+ }
+
+ /* Go through the first `min (num_regs, regs->num_regs)'
+ registers, since that is all we initialized. */
+ for (mcnt = 1; mcnt < MIN (num_regs, regs->num_regs); mcnt++)
+ {
+ if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt]))
+ regs->start[mcnt] = regs->end[mcnt] = -1;
+ else
+ {
+ regs->start[mcnt] = POINTER_TO_OFFSET (regstart[mcnt]);
+ regs->end[mcnt] = POINTER_TO_OFFSET (regend[mcnt]);
+ }
+ }
+
+ /* If the regs structure we return has more elements than
+ were in the pattern, set the extra elements to -1. If
+ we (re)allocated the registers, this is the case,
+ because we always allocate enough to have at least one
+ -1 at the end. */
+ for (mcnt = num_regs; mcnt < regs->num_regs; mcnt++)
+ regs->start[mcnt] = regs->end[mcnt] = -1;
+ } /* regs && !bufp->no_sub */
+
+ FREE_VARIABLES ();
+ DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n",
+ nfailure_points_pushed, nfailure_points_popped,
+ nfailure_points_pushed - nfailure_points_popped);
+ DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed);
+
+ mcnt = d - pos - (MATCHING_IN_FIRST_STRING
+ ? string1
+ : string2 - size1);
+
+ DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt);
+
+ return mcnt;
+ }
+
+ /* Otherwise match next pattern command. */
+#ifdef SWITCH_ENUM_BUG
+ switch ((int) ((re_opcode_t) *p++))
+#else
+ switch ((re_opcode_t) *p++)
+#endif
+ {
+ /* Ignore these. Used to ignore the n of succeed_n's which
+ currently have n == 0. */
+ case no_op:
+ DEBUG_PRINT1 ("EXECUTING no_op.\n");
+ break;
+
+
+ /* Match the next n pattern characters exactly. The following
+ byte in the pattern defines n, and the n bytes after that
+ are the characters to match. */
+ case exactn:
+ mcnt = *p++;
+ DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt);
+
+ /* This is written out as an if-else so we don't waste time
+ testing `translate' inside the loop. */
+ if (translate)
+ {
+ do
+ {
+ PREFETCH ();
+ if (translate[(unsigned char) *d++] != (char) *p++)
+ goto fail;
+ }
+ while (--mcnt);
+ }
+ else
+ {
+ do
+ {
+ PREFETCH ();
+ if (*d++ != (char) *p++) goto fail;
+ }
+ while (--mcnt);
+ }
+ SET_REGS_MATCHED ();
+ break;
+
+
+ /* Match any character except possibly a newline or a null. */
+ case anychar:
+ DEBUG_PRINT1 ("EXECUTING anychar.\n");
+
+ PREFETCH ();
+
+ if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n')
+ || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000'))
+ goto fail;
+
+ SET_REGS_MATCHED ();
+ DEBUG_PRINT2 (" Matched `%d'.\n", *d);
+ d++;
+ break;
+
+
+ case charset:
+ case charset_not:
+ {
+ register unsigned char c;
+ boolean not = (re_opcode_t) *(p - 1) == charset_not;
+
+ DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : "");
+
+ PREFETCH ();
+ c = TRANSLATE (*d); /* The character to match. */
+
+ /* Cast to `unsigned' instead of `unsigned char' in case the
+ bit list is a full 32 bytes long. */
+ if (c < (unsigned) (*p * BYTEWIDTH)
+ && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+ not = !not;
+
+ p += 1 + *p;
+
+ if (!not) goto fail;
+
+ SET_REGS_MATCHED ();
+ d++;
+ break;
+ }
+
+
+ /* The beginning of a group is represented by start_memory.
+ The arguments are the register number in the next byte, and the
+ number of groups inner to this one in the next. The text
+ matched within the group is recorded (in the internal
+ registers data structure) under the register number. */
+ case start_memory:
+ DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]);
+
+ /* Find out if this group can match the empty string. */
+ p1 = p; /* To send to group_match_null_string_p. */
+
+ if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE)
+ REG_MATCH_NULL_STRING_P (reg_info[*p])
+ = group_match_null_string_p (&p1, pend, reg_info);
+
+ /* Save the position in the string where we were the last time
+ we were at this open-group operator in case the group is
+ operated upon by a repetition operator, e.g., with `(a*)*b'
+ against `ab'; then we want to ignore where we are now in
+ the string in case this attempt to match fails. */
+ old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+ ? REG_UNSET (regstart[*p]) ? d : regstart[*p]
+ : regstart[*p];
+ DEBUG_PRINT2 (" old_regstart: %d\n",
+ POINTER_TO_OFFSET (old_regstart[*p]));
+
+ regstart[*p] = d;
+ DEBUG_PRINT2 (" regstart: %d\n", POINTER_TO_OFFSET (regstart[*p]));
+
+ IS_ACTIVE (reg_info[*p]) = 1;
+ MATCHED_SOMETHING (reg_info[*p]) = 0;
+
+ /* This is the new highest active register. */
+ highest_active_reg = *p;
+
+ /* If nothing was active before, this is the new lowest active
+ register. */
+ if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+ lowest_active_reg = *p;
+
+ /* Move past the register number and inner group count. */
+ p += 2;
+ break;
+
+
+ /* The stop_memory opcode represents the end of a group. Its
+ arguments are the same as start_memory's: the register
+ number, and the number of inner groups. */
+ case stop_memory:
+ DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]);
+
+ /* We need to save the string position the last time we were at
+ this close-group operator in case the group is operated
+ upon by a repetition operator, e.g., with `((a*)*(b*)*)*'
+ against `aba'; then we want to ignore where we are now in
+ the string in case this attempt to match fails. */
+ old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+ ? REG_UNSET (regend[*p]) ? d : regend[*p]
+ : regend[*p];
+ DEBUG_PRINT2 (" old_regend: %d\n",
+ POINTER_TO_OFFSET (old_regend[*p]));
+
+ regend[*p] = d;
+ DEBUG_PRINT2 (" regend: %d\n", POINTER_TO_OFFSET (regend[*p]));
+
+ /* This register isn't active anymore. */
+ IS_ACTIVE (reg_info[*p]) = 0;
+
+ /* If this was the only register active, nothing is active
+ anymore. */
+ if (lowest_active_reg == highest_active_reg)
+ {
+ lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+ highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+ }
+ else
+ { /* We must scan for the new highest active register, since
+ it isn't necessarily one less than now: consider
+ (a(b)c(d(e)f)g). When group 3 ends, after the f), the
+ new highest active register is 1. */
+ unsigned char r = *p - 1;
+ while (r > 0 && !IS_ACTIVE (reg_info[r]))
+ r--;
+
+ /* If we end up at register zero, that means that we saved
+ the registers as the result of an `on_failure_jump', not
+ a `start_memory', and we jumped to past the innermost
+ `stop_memory'. For example, in ((.)*) we save
+ registers 1 and 2 as a result of the *, but when we pop
+ back to the second ), we are at the stop_memory 1.
+ Thus, nothing is active. */
+ if (r == 0)
+ {
+ lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+ highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+ }
+ else
+ highest_active_reg = r;
+ }
+
+ /* If just failed to match something this time around with a
+ group that's operated on by a repetition operator, try to
+ force exit from the ``loop'', and restore the register
+ information for this group that we had before trying this
+ last match. */
+ if ((!MATCHED_SOMETHING (reg_info[*p])
+ || (re_opcode_t) p[-3] == start_memory)
+ && (p + 2) < pend)
+ {
+ boolean is_a_jump_n = false;
+
+ p1 = p + 2;
+ mcnt = 0;
+ switch ((re_opcode_t) *p1++)
+ {
+ case jump_n:
+ is_a_jump_n = true;
+ case pop_failure_jump:
+ case maybe_pop_jump:
+ case jump:
+ case dummy_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ if (is_a_jump_n)
+ p1 += 2;
+ break;
+
+ default:
+ /* do nothing */ ;
+ }
+ p1 += mcnt;
+
+ /* If the next operation is a jump backwards in the pattern
+ to an on_failure_jump right before the start_memory
+ corresponding to this stop_memory, exit from the loop
+ by forcing a failure after pushing on the stack the
+ on_failure_jump's jump in the pattern, and d. */
+ if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump
+ && (re_opcode_t) p1[3] == start_memory && p1[4] == *p)
+ {
+ /* If this group ever matched anything, then restore
+ what its registers were before trying this last
+ failed match, e.g., with `(a*)*b' against `ab' for
+ regstart[1], and, e.g., with `((a*)*(b*)*)*'
+ against `aba' for regend[3].
+
+ Also restore the registers for inner groups for,
+ e.g., `((a*)(b*))*' against `aba' (register 3 would
+ otherwise get trashed). */
+
+ if (EVER_MATCHED_SOMETHING (reg_info[*p]))
+ {
+ unsigned r;
+
+ EVER_MATCHED_SOMETHING (reg_info[*p]) = 0;
+
+ /* Restore this and inner groups' (if any) registers. */
+ for (r = *p; r < *p + *(p + 1); r++)
+ {
+ regstart[r] = old_regstart[r];
+
+ /* xx why this test? */
+ if ((s_reg_t) old_regend[r] >= (s_reg_t) regstart[r])
+ regend[r] = old_regend[r];
+ }
+ }
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ PUSH_FAILURE_POINT (p1 + mcnt, d, -2);
+ PUSH_FAILURE_POINT2(p1 + mcnt, d, -2);
+
+ goto fail;
+ }
+ }
+
+ /* Move past the register number and the inner group count. */
+ p += 2;
+ break;
+
+
+ /* \<digit> has been turned into a `duplicate' command which is
+ followed by the numeric value of <digit> as the register number. */
+ case duplicate:
+ {
+ register const char *d2, *dend2;
+ int regno = *p++; /* Get which register to match against. */
+ DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno);
+
+ /* Can't back reference a group which we've never matched. */
+ if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno]))
+ goto fail;
+
+ /* Where in input to try to start matching. */
+ d2 = regstart[regno];
+
+ /* Where to stop matching; if both the place to start and
+ the place to stop matching are in the same string, then
+ set to the place to stop, otherwise, for now have to use
+ the end of the first string. */
+
+ dend2 = ((FIRST_STRING_P (regstart[regno])
+ == FIRST_STRING_P (regend[regno]))
+ ? regend[regno] : end_match_1);
+ for (;;)
+ {
+ /* If necessary, advance to next segment in register
+ contents. */
+ while (d2 == dend2)
+ {
+ if (dend2 == end_match_2) break;
+ if (dend2 == regend[regno]) break;
+
+ /* End of string1 => advance to string2. */
+ d2 = string2;
+ dend2 = regend[regno];
+ }
+ /* At end of register contents => success */
+ if (d2 == dend2) break;
+
+ /* If necessary, advance to next segment in data. */
+ PREFETCH ();
+
+ /* How many characters left in this segment to match. */
+ mcnt = dend - d;
+
+ /* Want how many consecutive characters we can match in
+ one shot, so, if necessary, adjust the count. */
+ if (mcnt > dend2 - d2)
+ mcnt = dend2 - d2;
+
+ /* Compare that many; failure if mismatch, else move
+ past them. */
+ if (translate
+ ? bcmp_translate (d, d2, mcnt, translate)
+ : bcmp (d, d2, mcnt))
+ goto fail;
+ d += mcnt, d2 += mcnt;
+ }
+ }
+ break;
+
+
+ /* begline matches the empty string at the beginning of the string
+ (unless `not_bol' is set in `bufp'), and, if
+ `newline_anchor' is set, after newlines. */
+ case begline:
+ DEBUG_PRINT1 ("EXECUTING begline.\n");
+
+ if (AT_STRINGS_BEG (d))
+ {
+ if (!bufp->not_bol) break;
+ }
+ else if (d[-1] == '\n' && bufp->newline_anchor)
+ {
+ break;
+ }
+ /* In all other cases, we fail. */
+ goto fail;
+
+
+ /* endline is the dual of begline. */
+ case endline:
+ DEBUG_PRINT1 ("EXECUTING endline.\n");
+
+ if (AT_STRINGS_END (d))
+ {
+ if (!bufp->not_eol) break;
+ }
+
+ /* We have to ``prefetch'' the next character. */
+ else if ((d == end1 ? *string2 : *d) == '\n'
+ && bufp->newline_anchor)
+ {
+ break;
+ }
+ goto fail;
+
+
+ /* Match at the very beginning of the data. */
+ case begbuf:
+ DEBUG_PRINT1 ("EXECUTING begbuf.\n");
+ if (AT_STRINGS_BEG (d))
+ break;
+ goto fail;
+
+
+ /* Match at the very end of the data. */
+ case endbuf:
+ DEBUG_PRINT1 ("EXECUTING endbuf.\n");
+ if (AT_STRINGS_END (d))
+ break;
+ goto fail;
+
+
+ /* on_failure_keep_string_jump is used to optimize `.*\n'. It
+ pushes NULL as the value for the string on the stack. Then
+ `pop_failure_point' will keep the current value for the
+ string, instead of restoring it. To see why, consider
+ matching `foo\nbar' against `.*\n'. The .* matches the foo;
+ then the . fails against the \n. But the next thing we want
+ to do is match the \n against the \n; if we restored the
+ string value, we would be back at the foo.
+
+ Because this is used only in specific cases, we don't need to
+ check all the things that `on_failure_jump' does, to make
+ sure the right things get saved on the stack. Hence we don't
+ share its code. The only reason to push anything on the
+ stack at all is that otherwise we would have to change
+ `anychar's code to do something besides goto fail in this
+ case; that seems worse than this. */
+ case on_failure_keep_string_jump:
+ DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump");
+
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt);
+
+ PUSH_FAILURE_POINT (p + mcnt, NULL, -2);
+ PUSH_FAILURE_POINT2(p + mcnt, NULL, -2);
+ break;
+
+
+ /* Uses of on_failure_jump:
+
+ Each alternative starts with an on_failure_jump that points
+ to the beginning of the next alternative. Each alternative
+ except the last ends with a jump that in effect jumps past
+ the rest of the alternatives. (They really jump to the
+ ending jump of the following alternative, because tensioning
+ these jumps is a hassle.)
+
+ Repeats start with an on_failure_jump that points past both
+ the repetition text and either the following jump or
+ pop_failure_jump back to this on_failure_jump. */
+ case on_failure_jump:
+ on_failure:
+ DEBUG_PRINT1 ("EXECUTING on_failure_jump");
+
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt);
+
+ /* If this on_failure_jump comes right before a group (i.e.,
+ the original * applied to a group), save the information
+ for that group and all inner ones, so that if we fail back
+ to this point, the group's information will be correct.
+ For example, in \(a*\)*\1, we need the preceding group,
+ and in \(\(a*\)b*\)\2, we need the inner group. */
+
+ /* We can't use `p' to check ahead because we push
+ a failure point to `p + mcnt' after we do this. */
+ p1 = p;
+
+ /* We need to skip no_op's before we look for the
+ start_memory in case this on_failure_jump is happening as
+ the result of a completed succeed_n, as in \(a\)\{1,3\}b\1
+ against aba. */
+ while (p1 < pend && (re_opcode_t) *p1 == no_op)
+ p1++;
+
+ if (p1 < pend && (re_opcode_t) *p1 == start_memory)
+ {
+ /* We have a new highest active register now. This will
+ get reset at the start_memory we are about to get to,
+ but we will have saved all the registers relevant to
+ this repetition op, as described above. */
+ highest_active_reg = *(p1 + 1) + *(p1 + 2);
+ if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+ lowest_active_reg = *(p1 + 1);
+ }
+
+ DEBUG_PRINT1 (":\n");
+ PUSH_FAILURE_POINT (p + mcnt, d, -2);
+ PUSH_FAILURE_POINT2(p + mcnt, d, -2);
+ break;
+
+
+ /* A smart repeat ends with `maybe_pop_jump'.
+ We change it to either `pop_failure_jump' or `jump'. */
+ case maybe_pop_jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt);
+ {
+ register unsigned char *p2 = p;
+
+ /* Compare the beginning of the repeat with what in the
+ pattern follows its end. If we can establish that there
+ is nothing that they would both match, i.e., that we
+ would have to backtrack because of (as in, e.g., `a*a')
+ then we can change to pop_failure_jump, because we'll
+ never have to backtrack.
+
+ This is not true in the case of alternatives: in
+ `(a|ab)*' we do need to backtrack to the `ab' alternative
+ (e.g., if the string was `ab'). But instead of trying to
+ detect that here, the alternative has put on a dummy
+ failure point which is what we will end up popping. */
+
+ /* Skip over open/close-group commands. */
+ while (p2 + 2 < pend
+ && ((re_opcode_t) *p2 == stop_memory
+ || (re_opcode_t) *p2 == start_memory))
+ p2 += 3; /* Skip over args, too. */
+
+ /* If we're at the end of the pattern, we can change. */
+ if (p2 == pend)
+ {
+ /* Consider what happens when matching ":\(.*\)"
+ against ":/". I don't really understand this code
+ yet. */
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT1
+ (" End of pattern: change to `pop_failure_jump'.\n");
+ }
+
+ else if ((re_opcode_t) *p2 == exactn
+ || (bufp->newline_anchor && (re_opcode_t) *p2 == endline))
+ {
+ register unsigned char c
+ = *p2 == (unsigned char) endline ? '\n' : p2[2];
+ p1 = p + mcnt;
+
+ /* p1[0] ... p1[2] are the `on_failure_jump' corresponding
+ to the `maybe_finalize_jump' of this case. Examine what
+ follows. */
+ if ((re_opcode_t) p1[3] == exactn && p1[5] != c)
+ {
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n",
+ c, p1[5]);
+ }
+
+ else if ((re_opcode_t) p1[3] == charset
+ || (re_opcode_t) p1[3] == charset_not)
+ {
+ int not = (re_opcode_t) p1[3] == charset_not;
+
+ if (c < (unsigned char) (p1[4] * BYTEWIDTH)
+ && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+ not = !not;
+
+ /* `not' is equal to 1 if c would match, which means
+ that we can't change to pop_failure_jump. */
+ if (!not)
+ {
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT1 (" No match => pop_failure_jump.\n");
+ }
+ }
+ }
+ }
+ p -= 2; /* Point at relative address again. */
+ if ((re_opcode_t) p[-1] != pop_failure_jump)
+ {
+ p[-1] = (unsigned char) jump;
+ DEBUG_PRINT1 (" Match => jump.\n");
+ goto unconditional_jump;
+ }
+ /* Note fall through. */
+
+
+ /* The end of a simple repeat has a pop_failure_jump back to
+ its matching on_failure_jump, where the latter will push a
+ failure point. The pop_failure_jump takes off failure
+ points put on by this pop_failure_jump's matching
+ on_failure_jump; we got through the pattern to here from the
+ matching on_failure_jump, so didn't fail. */
+ case pop_failure_jump:
+ {
+ /* We need to pass separate storage for the lowest and
+ highest registers, even though we don't care about the
+ actual values. Otherwise, we will restore only one
+ register from the stack, since lowest will == highest in
+ `pop_failure_point'. */
+ active_reg_t dummy_low_reg, dummy_high_reg;
+ unsigned char *pdummy;
+ const char *sdummy;
+
+ DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n");
+ POP_FAILURE_POINT (sdummy, pdummy,
+ dummy_low_reg, dummy_high_reg,
+ reg_dummy, reg_dummy, reg_info_dummy);
+ }
+ /* Note fall through. */
+
+
+ /* Unconditionally jump (without popping any failure points). */
+ case jump:
+ unconditional_jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p); /* Get the amount to jump. */
+ DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt);
+ p += mcnt; /* Do the jump. */
+ DEBUG_PRINT2 ("(to 0x%x).\n", p);
+ break;
+
+
+ /* We need this opcode so we can detect where alternatives end
+ in `group_match_null_string_p' et al. */
+ case jump_past_alt:
+ DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n");
+ goto unconditional_jump;
+
+
+ /* Normally, the on_failure_jump pushes a failure point, which
+ then gets popped at pop_failure_jump. We will end up at
+ pop_failure_jump, also, and with a pattern of, say, `a+', we
+ are skipping over the on_failure_jump, so we have to push
+ something meaningless for pop_failure_jump to pop. */
+ case dummy_failure_jump:
+ DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n");
+ /* It doesn't matter what we push for the string here. What
+ the code at `fail' tests is the value for the pattern. */
+ PUSH_FAILURE_POINT (0, 0, -2);
+ PUSH_FAILURE_POINT2(0, 0, -2);
+ goto unconditional_jump;
+
+
+ /* At the end of an alternative, we need to push a dummy failure
+ point in case we are followed by a `pop_failure_jump', because
+ we don't want the failure point for the alternative to be
+ popped. For example, matching `(a|ab)*' against `aab'
+ requires that we match the `ab' alternative. */
+ case push_dummy_failure:
+ DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n");
+ /* See comments just above at `dummy_failure_jump' about the
+ two zeroes. */
+ PUSH_FAILURE_POINT (0, 0, -2);
+ PUSH_FAILURE_POINT2(0, 0, -2);
+ break;
+
+ /* Have to succeed matching what follows at least n times.
+ After that, handle like `on_failure_jump'. */
+ case succeed_n:
+ EXTRACT_NUMBER (mcnt, p + 2);
+ DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt);
+
+ assert (mcnt >= 0);
+ /* Originally, this is how many times we HAVE to succeed. */
+ if (mcnt > 0)
+ {
+ mcnt--;
+ p += 2;
+ STORE_NUMBER_AND_INCR (p, mcnt);
+ DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p, mcnt);
+ }
+ else if (mcnt == 0)
+ {
+ DEBUG_PRINT2 (" Setting two bytes from 0x%x to no_op.\n", p+2);
+ p[2] = (unsigned char) no_op;
+ p[3] = (unsigned char) no_op;
+ goto on_failure;
+ }
+ break;
+
+ case jump_n:
+ EXTRACT_NUMBER (mcnt, p + 2);
+ DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt);
+
+ /* Originally, this is how many times we CAN jump. */
+ if (mcnt)
+ {
+ mcnt--;
+ STORE_NUMBER (p + 2, mcnt);
+ goto unconditional_jump;
+ }
+ /* If don't have to jump any more, skip over the rest of command. */
+ else
+ p += 4;
+ break;
+
+ case set_number_at:
+ {
+ DEBUG_PRINT1 ("EXECUTING set_number_at.\n");
+
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ p1 = p + mcnt;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p1, mcnt);
+ STORE_NUMBER (p1, mcnt);
+ break;
+ }
+
+ case wordbound:
+ DEBUG_PRINT1 ("EXECUTING wordbound.\n");
+ if (AT_WORD_BOUNDARY (d))
+ break;
+ goto fail;
+
+ case notwordbound:
+ DEBUG_PRINT1 ("EXECUTING notwordbound.\n");
+ if (AT_WORD_BOUNDARY (d))
+ goto fail;
+ break;
+
+ case wordbeg:
+ DEBUG_PRINT1 ("EXECUTING wordbeg.\n");
+ if (WORDCHAR_P (d) && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1)))
+ break;
+ goto fail;
+
+ case wordend:
+ DEBUG_PRINT1 ("EXECUTING wordend.\n");
+ if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1)
+ && (!WORDCHAR_P (d) || AT_STRINGS_END (d)))
+ break;
+ goto fail;
+
+#ifdef emacs
+#ifdef emacs19
+ case before_dot:
+ DEBUG_PRINT1 ("EXECUTING before_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) >= point)
+ goto fail;
+ break;
+
+ case at_dot:
+ DEBUG_PRINT1 ("EXECUTING at_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) != point)
+ goto fail;
+ break;
+
+ case after_dot:
+ DEBUG_PRINT1 ("EXECUTING after_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) <= point)
+ goto fail;
+ break;
+#else /* not emacs19 */
+ case at_dot:
+ DEBUG_PRINT1 ("EXECUTING at_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) + 1 != point)
+ goto fail;
+ break;
+#endif /* not emacs19 */
+
+ case syntaxspec:
+ DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt);
+ mcnt = *p++;
+ goto matchsyntax;
+
+ case wordchar:
+ DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n");
+ mcnt = (int) Sword;
+ matchsyntax:
+ PREFETCH ();
+ if (SYNTAX (*d++) != (enum syntaxcode) mcnt)
+ goto fail;
+ SET_REGS_MATCHED ();
+ break;
+
+ case notsyntaxspec:
+ DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt);
+ mcnt = *p++;
+ goto matchnotsyntax;
+
+ case notwordchar:
+ DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n");
+ mcnt = (int) Sword;
+ matchnotsyntax:
+ PREFETCH ();
+ if (SYNTAX (*d++) == (enum syntaxcode) mcnt)
+ goto fail;
+ SET_REGS_MATCHED ();
+ break;
+
+#else /* not emacs */
+ case wordchar:
+ DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n");
+ PREFETCH ();
+ if (!WORDCHAR_P (d))
+ goto fail;
+ SET_REGS_MATCHED ();
+ d++;
+ break;
+
+ case notwordchar:
+ DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n");
+ PREFETCH ();
+ if (WORDCHAR_P (d))
+ goto fail;
+ SET_REGS_MATCHED ();
+ d++;
+ break;
+#endif /* not emacs */
+
+ default:
+ abort ();
+ }
+ continue; /* Successfully executed one pattern command; keep going. */
+
+
+ /* We goto here if a matching operation fails. */
+ fail:
+ if (!FAIL_STACK_EMPTY ())
+ { /* A restart point is known. Restore to that state. */
+ DEBUG_PRINT1 ("\nFAIL:\n");
+ POP_FAILURE_POINT (d, p,
+ lowest_active_reg, highest_active_reg,
+ regstart, regend, reg_info);
+
+ /* If this failure point is a dummy, try the next one. */
+ if (!p)
+ goto fail;
+
+ /* If we failed to the end of the pattern, don't examine *p. */
+ assert (p <= pend);
+ if (p < pend)
+ {
+ boolean is_a_jump_n = false;
+
+ /* If failed to a backwards jump that's part of a repetition
+ loop, need to pop this failure point and use the next one. */
+ switch ((re_opcode_t) *p)
+ {
+ case jump_n:
+ is_a_jump_n = true;
+ case maybe_pop_jump:
+ case pop_failure_jump:
+ case jump:
+ p1 = p + 1;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ p1 += mcnt;
+
+ if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n)
+ || (!is_a_jump_n
+ && (re_opcode_t) *p1 == on_failure_jump))
+ goto fail;
+ break;
+ default:
+ /* do nothing */ ;
+ }
+ }
+
+ if (d >= string1 && d <= end1)
+ dend = end_match_1;
+ }
+ else
+ break; /* Matching at this starting point really fails. */
+ } /* for (;;) */
+
+ if (best_regs_set)
+ goto restore_best_regs;
+
+ FREE_VARIABLES ();
+
+ return -1; /* Failure to match. */
+} /* re_match_2 */
+
+/* Subroutine definitions for re_match_2. */
+
+
+/* We are passed P pointing to a register number after a start_memory.
+
+ Return true if the pattern up to the corresponding stop_memory can
+ match the empty string, and false otherwise.
+
+ If we find the matching stop_memory, sets P to point to one past its number.
+ Otherwise, sets P to an undefined byte less than or equal to END.
+
+ We don't handle duplicates properly (yet). */
+
+static boolean
+group_match_null_string_p (p, end, reg_info)
+ unsigned char **p, *end;
+ register_info_type *reg_info;
+{
+ int mcnt;
+ /* Point to after the args to the start_memory. */
+ unsigned char *p1 = *p + 2;
+
+ while (p1 < end)
+ {
+ /* Skip over opcodes that can match nothing, and return true or
+ false, as appropriate, when we get to one that can't, or to the
+ matching stop_memory. */
+
+ switch ((re_opcode_t) *p1)
+ {
+ /* Could be either a loop or a series of alternatives. */
+ case on_failure_jump:
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+
+ /* If the next operation is not a jump backwards in the
+ pattern. */
+
+ if (mcnt >= 0)
+ {
+ /* Go through the on_failure_jumps of the alternatives,
+ seeing if any of the alternatives cannot match nothing.
+ The last alternative starts with only a jump,
+ whereas the rest start with on_failure_jump and end
+ with a jump, e.g., here is the pattern for `a|b|c':
+
+ /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6
+ /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3
+ /exactn/1/c
+
+ So, we have to first go through the first (n-1)
+ alternatives and then deal with the last one separately. */
+
+
+ /* Deal with the first (n-1) alternatives, which start
+ with an on_failure_jump (see above) that jumps to right
+ past a jump_past_alt. */
+
+ while ((re_opcode_t) p1[mcnt-3] == jump_past_alt)
+ {
+ /* `mcnt' holds how many bytes long the alternative
+ is, including the ending `jump_past_alt' and
+ its number. */
+
+ if (!alt_match_null_string_p (p1, p1 + mcnt - 3,
+ reg_info))
+ return false;
+
+ /* Move to right after this alternative, including the
+ jump_past_alt. */
+ p1 += mcnt;
+
+ /* Break if it's the beginning of an n-th alternative
+ that doesn't begin with an on_failure_jump. */
+ if ((re_opcode_t) *p1 != on_failure_jump)
+ break;
+
+ /* Still have to check that it's not an n-th
+ alternative that starts with an on_failure_jump. */
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ if ((re_opcode_t) p1[mcnt-3] != jump_past_alt)
+ {
+ /* Get to the beginning of the n-th alternative. */
+ p1 -= 3;
+ break;
+ }
+ }
+
+ /* Deal with the last alternative: go back and get number
+ of the `jump_past_alt' just before it. `mcnt' contains
+ the length of the alternative. */
+ EXTRACT_NUMBER (mcnt, p1 - 2);
+
+ if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info))
+ return false;
+
+ p1 += mcnt; /* Get past the n-th alternative. */
+ } /* if mcnt > 0 */
+ break;
+
+
+ case stop_memory:
+ assert (p1[1] == **p);
+ *p = p1 + 2;
+ return true;
+
+
+ default:
+ if (!common_op_match_null_string_p (&p1, end, reg_info))
+ return false;
+ }
+ } /* while p1 < end */
+
+ return false;
+} /* group_match_null_string_p */
+
+
+/* Similar to group_match_null_string_p, but doesn't deal with alternatives:
+ It expects P to be the first byte of a single alternative and END one
+ byte past the last. The alternative can contain groups. */
+
+static boolean
+alt_match_null_string_p (p, end, reg_info)
+ unsigned char *p, *end;
+ register_info_type *reg_info;
+{
+ int mcnt;
+ unsigned char *p1 = p;
+
+ while (p1 < end)
+ {
+ /* Skip over opcodes that can match nothing, and break when we get
+ to one that can't. */
+
+ switch ((re_opcode_t) *p1)
+ {
+ /* It's a loop. */
+ case on_failure_jump:
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ p1 += mcnt;
+ break;
+
+ default:
+ if (!common_op_match_null_string_p (&p1, end, reg_info))
+ return false;
+ }
+ } /* while p1 < end */
+
+ return true;
+} /* alt_match_null_string_p */
+
+
+/* Deals with the ops common to group_match_null_string_p and
+ alt_match_null_string_p.
+
+ Sets P to one after the op and its arguments, if any. */
+
+static boolean
+common_op_match_null_string_p (p, end, reg_info)
+ unsigned char **p, *end;
+ register_info_type *reg_info;
+{
+ int mcnt;
+ boolean ret;
+ int reg_no;
+ unsigned char *p1 = *p;
+
+ switch ((re_opcode_t) *p1++)
+ {
+ case no_op:
+ case begline:
+ case endline:
+ case begbuf:
+ case endbuf:
+ case wordbeg:
+ case wordend:
+ case wordbound:
+ case notwordbound:
+#ifdef emacs
+ case before_dot:
+ case at_dot:
+ case after_dot:
+#endif
+ break;
+
+ case start_memory:
+ reg_no = *p1;
+ assert (reg_no > 0 && reg_no <= MAX_REGNUM);
+ ret = group_match_null_string_p (&p1, end, reg_info);
+
+ /* Have to set this here in case we're checking a group which
+ contains a group and a back reference to it. */
+
+ if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE)
+ REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret;
+
+ if (!ret)
+ return false;
+ break;
+
+ /* If this is an optimized succeed_n for zero times, make the jump. */
+ case jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ if (mcnt >= 0)
+ p1 += mcnt;
+ else
+ return false;
+ break;
+
+ case succeed_n:
+ /* Get to the number of times to succeed. */
+ p1 += 2;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+
+ if (mcnt == 0)
+ {
+ p1 -= 4;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ p1 += mcnt;
+ }
+ else
+ return false;
+ break;
+
+ case duplicate:
+ if (!REG_MATCH_NULL_STRING_P (reg_info[*p1]))
+ return false;
+ break;
+
+ case set_number_at:
+ p1 += 4;
+
+ default:
+ /* All other opcodes mean we cannot match the empty string. */
+ return false;
+ }
+
+ *p = p1;
+ return true;
+} /* common_op_match_null_string_p */
+
+
+/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN
+ bytes; nonzero otherwise. */
+
+static int
+bcmp_translate (s1, s2, len, translate)
+ const char *s1, *s2;
+ register int len;
+ char *translate;
+{
+ register const unsigned char *p1 = (const unsigned char *) s1,
+ *p2 = (const unsigned char *) s2;
+ while (len)
+ {
+ if (translate[*p1++] != translate[*p2++]) return 1;
+ len--;
+ }
+ return 0;
+}
+
+/* Entry points for GNU code. */
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+ compiles PATTERN (of length SIZE) and puts the result in BUFP.
+ Returns 0 if the pattern was valid, otherwise an error string.
+
+ Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+ are set in BUFP on entry.
+
+ We call regex_compile to do the actual compilation. */
+
+const char *
+re_compile_pattern (pattern, length, bufp)
+ const char *pattern;
+ size_t length;
+ struct re_pattern_buffer *bufp;
+{
+ reg_errcode_t ret;
+
+ /* GNU code is written to assume at least RE_NREGS registers will be set
+ (and at least one extra will be -1). */
+ bufp->regs_allocated = REGS_UNALLOCATED;
+
+ /* And GNU code determines whether or not to get register information
+ by passing null for the REGS argument to re_match, etc., not by
+ setting no_sub. */
+ bufp->no_sub = 0;
+
+ /* Match anchors at newline. */
+ bufp->newline_anchor = 1;
+
+ ret = regex_compile (pattern, length, re_syntax_options, bufp);
+
+ return re_error_msg[(int) ret];
+}
+
+/* Entry points compatible with 4.2 BSD regex library. We don't define
+ them if this is an Emacs or POSIX compilation. */
+
+#if !defined (emacs) && !defined (_POSIX_SOURCE)
+
+/* BSD has one and only one pattern buffer. */
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+re_comp (s)
+ const char *s;
+{
+ reg_errcode_t ret;
+
+ if (!s)
+ {
+ if (!re_comp_buf.buffer)
+ return "No previous regular expression";
+ return 0;
+ }
+
+ if (!re_comp_buf.buffer)
+ {
+ re_comp_buf.buffer = (unsigned char *) malloc (200);
+ if (re_comp_buf.buffer == NULL)
+ return "Memory exhausted";
+ re_comp_buf.allocated = 200;
+
+ re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH);
+ if (re_comp_buf.fastmap == NULL)
+ return "Memory exhausted";
+ }
+
+ /* Since `re_exec' always passes NULL for the `regs' argument, we
+ don't need to initialize the pattern buffer fields which affect it. */
+
+ /* Match anchors at newlines. */
+ re_comp_buf.newline_anchor = 1;
+
+ ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf);
+
+ /* Yes, we're discarding `const' here. */
+ return (char *) re_error_msg[(int) ret];
+}
+
+
+int
+re_exec (s)
+ const char *s;
+{
+ const int len = strlen (s);
+ return
+ 0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0);
+}
+#endif /* not emacs and not _POSIX_SOURCE */
+
+/* POSIX.2 functions. Don't define these for Emacs. */
+
+#ifndef emacs
+
+/* regcomp takes a regular expression as a string and compiles it.
+
+ PREG is a regex_t *. We do not expect any fields to be initialized,
+ since POSIX says we shouldn't. Thus, we set
+
+ `buffer' to the compiled pattern;
+ `used' to the length of the compiled pattern;
+ `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+ REG_EXTENDED bit in CFLAGS is set; otherwise, to
+ RE_SYNTAX_POSIX_BASIC;
+ `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+ `fastmap' and `fastmap_accurate' to zero;
+ `re_nsub' to the number of subexpressions in PATTERN.
+
+ PATTERN is the address of the pattern string.
+
+ CFLAGS is a series of bits which affect compilation.
+
+ If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+ use POSIX basic syntax.
+
+ If REG_NEWLINE is set, then . and [^...] don't match newline.
+ Also, regexec will try a match beginning after every newline.
+
+ If REG_ICASE is set, then we considers upper- and lowercase
+ versions of letters to be equivalent when matching.
+
+ If REG_NOSUB is set, then when PREG is passed to regexec, that
+ routine will report only success or failure, and nothing about the
+ registers.
+
+ It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for
+ the return codes and their meanings.) */
+
+int
+regcomp (preg, pattern, cflags)
+ regex_t *preg;
+ const char *pattern;
+ int cflags;
+{
+ reg_errcode_t ret;
+ reg_syntax_t syntax
+ = (cflags & REG_EXTENDED) ?
+ RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC;
+
+ /* regex_compile will allocate the space for the compiled pattern. */
+ preg->buffer = 0;
+ preg->allocated = 0;
+ preg->used = 0;
+
+ /* Don't bother to use a fastmap when searching. This simplifies the
+ REG_NEWLINE case: if we used a fastmap, we'd have to put all the
+ characters after newlines into the fastmap. This way, we just try
+ every character. */
+ preg->fastmap = 0;
+
+ if (cflags & REG_ICASE)
+ {
+ unsigned i;
+
+ preg->translate = (char *) malloc (CHAR_SET_SIZE);
+ if (preg->translate == NULL)
+ return (int) REG_ESPACE;
+
+ /* Map uppercase characters to corresponding lowercase ones. */
+ for (i = 0; i < CHAR_SET_SIZE; i++)
+ preg->translate[i] = ISUPPER (i) ? tolower (i) : i;
+ }
+ else
+ preg->translate = NULL;
+
+ /* If REG_NEWLINE is set, newlines are treated differently. */
+ if (cflags & REG_NEWLINE)
+ { /* REG_NEWLINE implies neither . nor [^...] match newline. */
+ syntax &= ~RE_DOT_NEWLINE;
+ syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+ /* It also changes the matching behavior. */
+ preg->newline_anchor = 1;
+ }
+ else
+ preg->newline_anchor = 0;
+
+ preg->no_sub = !!(cflags & REG_NOSUB);
+
+ /* POSIX says a null character in the pattern terminates it, so we
+ can use strlen here in compiling the pattern. */
+ ret = regex_compile (pattern, strlen (pattern), syntax, preg);
+
+ /* POSIX doesn't distinguish between an unmatched open-group and an
+ unmatched close-group: both are REG_EPAREN. */
+ if (ret == REG_ERPAREN) ret = REG_EPAREN;
+
+ return (int) ret;
+}
+
+
+/* regexec searches for a given pattern, specified by PREG, in the
+ string STRING.
+
+ If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+ `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at
+ least NMATCH elements, and we set them to the offsets of the
+ corresponding matched substrings.
+
+ EFLAGS specifies `execution flags' which affect matching: if
+ REG_NOTBOL is set, then ^ does not match at the beginning of the
+ string; if REG_NOTEOL is set, then $ does not match at the end.
+
+ We return 0 if we find a match and REG_NOMATCH if not. */
+
+int
+regexec (preg, string, nmatch, pmatch, eflags)
+ const regex_t *preg;
+ const char *string;
+ size_t nmatch;
+ regmatch_t pmatch[];
+ int eflags;
+{
+ int ret;
+ struct re_registers regs;
+ regex_t private_preg;
+ int len = strlen (string);
+ boolean want_reg_info = !preg->no_sub && nmatch > 0;
+
+ private_preg = *preg;
+
+ private_preg.not_bol = !!(eflags & REG_NOTBOL);
+ private_preg.not_eol = !!(eflags & REG_NOTEOL);
+
+ /* The user has told us exactly how many registers to return
+ information about, via `nmatch'. We have to pass that on to the
+ matching routines. */
+ private_preg.regs_allocated = REGS_FIXED;
+
+ if (want_reg_info)
+ {
+ regs.num_regs = nmatch;
+ regs.start = TALLOC (nmatch, regoff_t);
+ regs.end = TALLOC (nmatch, regoff_t);
+ if (regs.start == NULL || regs.end == NULL)
+ return (int) REG_NOMATCH;
+ }
+
+ /* Perform the searching operation. */
+ ret = re_search (&private_preg, string, len,
+ /* start: */ 0, /* range: */ len,
+ want_reg_info ? &regs : (struct re_registers *) 0);
+
+ /* Copy the register information to the POSIX structure. */
+ if (want_reg_info)
+ {
+ if (ret >= 0)
+ {
+ unsigned r;
+
+ for (r = 0; r < nmatch; r++)
+ {
+ pmatch[r].rm_so = regs.start[r];
+ pmatch[r].rm_eo = regs.end[r];
+ }
+ }
+
+ /* If we needed the temporary register info, free the space now. */
+ free (regs.start);
+ free (regs.end);
+ }
+
+ /* We want zero return to mean success, unlike `re_search'. */
+ return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH;
+}
+
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+ from either regcomp or regexec. We don't use PREG here. */
+
+size_t
+regerror (errcode, preg, errbuf, errbuf_size)
+ int errcode;
+ const regex_t *preg;
+ char *errbuf;
+ size_t errbuf_size;
+{
+ const char *msg;
+ size_t msg_size;
+
+ if (errcode < 0
+ || errcode >= (sizeof (re_error_msg) / sizeof (re_error_msg[0])))
+ /* Only error codes returned by the rest of the code should be passed
+ to this routine. If we are given anything else, or if other regex
+ code generates an invalid error code, then the program has a bug.
+ Dump core so we can fix it. */
+ abort ();
+
+ msg = re_error_msg[errcode];
+
+ /* POSIX doesn't require that we do anything in this case, but why
+ not be nice. */
+ if (! msg)
+ msg = "Success";
+
+ msg_size = strlen (msg) + 1; /* Includes the null. */
+
+ if (errbuf_size != 0)
+ {
+ if (msg_size > errbuf_size)
+ {
+ strncpy (errbuf, msg, errbuf_size - 1);
+ errbuf[errbuf_size - 1] = 0;
+ }
+ else
+ strcpy (errbuf, msg);
+ }
+
+ return msg_size;
+}
+
+
+/* Free dynamically allocated space used by PREG. */
+
+void
+regfree (preg)
+ regex_t *preg;
+{
+ if (preg->buffer != NULL)
+ free (preg->buffer);
+ preg->buffer = NULL;
+
+ preg->allocated = 0;
+ preg->used = 0;
+
+ if (preg->fastmap != NULL)
+ free (preg->fastmap);
+ preg->fastmap = NULL;
+ preg->fastmap_accurate = 0;
+
+ if (preg->translate != NULL)
+ free (preg->translate);
+ preg->translate = NULL;
+}
+
+#endif /* not emacs */
+
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
diff --git a/gnu/usr.bin/awk/regex.h b/gnu/usr.bin/awk/regex.h
new file mode 100644
index 0000000..757dbac
--- /dev/null
+++ b/gnu/usr.bin/awk/regex.h
@@ -0,0 +1,505 @@
+/* Definitions for data structures and routines for the regular
+ expression library, version 0.12.
+
+ Copyright (C) 1985, 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __REGEXP_LIBRARY_H__
+#define __REGEXP_LIBRARY_H__
+
+/* POSIX says that <sys/types.h> must be included (by the caller) before
+ <regex.h>. */
+
+#ifdef VMS
+/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it
+ should be there. */
+#include <stddef.h>
+#endif
+
+
+/* The following two types have to be signed and unsigned integer type
+ wide enough to hold a value of a pointer. For most ANSI compilers
+ ptrdiff_t and size_t should be likely OK. Still size of these two
+ types is 2 for Microsoft C. Ugh... */
+typedef long s_reg_t;
+typedef unsigned long active_reg_t;
+
+/* The following bits are used to determine the regexp syntax we
+ recognize. The set/not-set meanings are chosen so that Emacs syntax
+ remains the value 0. The bits are given in alphabetical order, and
+ the definitions shifted by one from the previous bit; thus, when we
+ add or remove a bit, only one other definition need change. */
+typedef unsigned long reg_syntax_t;
+
+/* If this bit is not set, then \ inside a bracket expression is literal.
+ If set, then such a \ quotes the following character. */
+#define RE_BACKSLASH_ESCAPE_IN_LISTS (1L)
+
+/* If this bit is not set, then + and ? are operators, and \+ and \? are
+ literals.
+ If set, then \+ and \? are operators and + and ? are literals. */
+#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
+
+/* If this bit is set, then character classes are supported. They are:
+ [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
+ [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+ If not set, then character classes are not supported. */
+#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
+
+/* If this bit is set, then ^ and $ are always anchors (outside bracket
+ expressions, of course).
+ If this bit is not set, then it depends:
+ ^ is an anchor if it is at the beginning of a regular
+ expression or after an open-group or an alternation operator;
+ $ is an anchor if it is at the end of a regular expression, or
+ before a close-group or an alternation operator.
+
+ This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
+ POSIX draft 11.2 says that * etc. in leading positions is undefined.
+ We already implemented a previous draft which made those constructs
+ invalid, though, so we haven't changed the code back. */
+#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
+
+/* If this bit is set, then special characters are always special
+ regardless of where they are in the pattern.
+ If this bit is not set, then special characters are special only in
+ some contexts; otherwise they are ordinary. Specifically,
+ * + ? and intervals are only special when not after the beginning,
+ open-group, or alternation operator. */
+#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
+
+/* If this bit is set, then *, +, ?, and { cannot be first in an re or
+ immediately after an alternation or begin-group operator. */
+#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
+
+/* If this bit is set, then . matches newline.
+ If not set, then it doesn't. */
+#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
+
+/* If this bit is set, then . doesn't match NUL.
+ If not set, then it does. */
+#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
+
+/* If this bit is set, nonmatching lists [^...] do not match newline.
+ If not set, they do. */
+#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
+
+/* If this bit is set, either \{...\} or {...} defines an
+ interval, depending on RE_NO_BK_BRACES.
+ If not set, \{, \}, {, and } are literals. */
+#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
+
+/* If this bit is set, +, ? and | aren't recognized as operators.
+ If not set, they are. */
+#define RE_LIMITED_OPS (RE_INTERVALS << 1)
+
+/* If this bit is set, newline is an alternation operator.
+ If not set, newline is literal. */
+#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
+
+/* If this bit is set, then `{...}' defines an interval, and \{ and \}
+ are literals.
+ If not set, then `\{...\}' defines an interval. */
+#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
+
+/* If this bit is set, (...) defines a group, and \( and \) are literals.
+ If not set, \(...\) defines a group, and ( and ) are literals. */
+#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
+
+/* If this bit is set, then \<digit> matches <digit>.
+ If not set, then \<digit> is a back-reference. */
+#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
+
+/* If this bit is set, then | is an alternation operator, and \| is literal.
+ If not set, then \| is an alternation operator, and | is literal. */
+#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
+
+/* If this bit is set, then an ending range point collating higher
+ than the starting range point, as in [z-a], is invalid.
+ If not set, then when ending range point collates higher than the
+ starting range point, the range is ignored. */
+#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
+
+/* If this bit is set, then an unmatched ) is ordinary.
+ If not set, then an unmatched ) is invalid. */
+#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+
+/* If this bit is set, do not process the GNU regex operators.
+ IF not set, then the GNU regex operators are recognized. */
+#define RE_NO_GNU_OPS (RE_UNMATCHED_RIGHT_PAREN_ORD << 1)
+
+/* This global variable defines the particular regexp syntax to use (for
+ some interfaces). When a regexp is compiled, the syntax used is
+ stored in the pattern buffer, so changing this does not affect
+ already-compiled regexps. */
+extern reg_syntax_t re_syntax_options;
+
+/* Define combinations of the above bits for the standard possibilities.
+ (The [[[ comments delimit what gets put into the Texinfo file, so
+ don't delete them!) */
+/* [[[begin syntaxes]]] */
+#define RE_SYNTAX_EMACS 0
+
+#define RE_SYNTAX_AWK \
+ (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS \
+ | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \
+ | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS)
+
+#define RE_SYNTAX_GNU_AWK \
+ (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS)
+
+#define RE_SYNTAX_POSIX_AWK \
+ (RE_SYNTAX_GNU_AWK | RE_NO_GNU_OPS)
+
+#define RE_SYNTAX_GREP \
+ (RE_BK_PLUS_QM | RE_CHAR_CLASSES \
+ | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \
+ | RE_NEWLINE_ALT)
+
+#define RE_SYNTAX_EGREP \
+ (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \
+ | RE_NEWLINE_ALT | RE_NO_BK_PARENS \
+ | RE_NO_BK_VBAR)
+
+#define RE_SYNTAX_POSIX_EGREP \
+ (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
+
+/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */
+#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
+
+#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
+
+/* Syntax bits common to both basic and extended POSIX regex syntax. */
+#define _RE_SYNTAX_POSIX_COMMON \
+ (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \
+ | RE_INTERVALS | RE_NO_EMPTY_RANGES)
+
+#define RE_SYNTAX_POSIX_BASIC \
+ (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM)
+
+/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
+ RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this
+ isn't minimal, since other operators, such as \`, aren't disabled. */
+#define RE_SYNTAX_POSIX_MINIMAL_BASIC \
+ (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
+
+#define RE_SYNTAX_POSIX_EXTENDED \
+ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \
+ | RE_NO_BK_PARENS | RE_NO_BK_VBAR \
+ | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS
+ replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added. */
+#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \
+ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS \
+ | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD)
+/* [[[end syntaxes]]] */
+
+/* Maximum number of duplicates an interval can allow. Some systems
+ (erroneously) define this in other header files, but we want our
+ value, so remove any previous define. */
+#ifdef RE_DUP_MAX
+#undef RE_DUP_MAX
+#endif
+/* if sizeof(int) == 2, then ((1 << 15) - 1) overflows */
+#define RE_DUP_MAX (0x7fff)
+
+
+/* POSIX `cflags' bits (i.e., information for `regcomp'). */
+
+/* If this bit is set, then use extended regular expression syntax.
+ If not set, then use basic regular expression syntax. */
+#define REG_EXTENDED 1
+
+/* If this bit is set, then ignore case when matching.
+ If not set, then case is significant. */
+#define REG_ICASE (REG_EXTENDED << 1)
+
+/* If this bit is set, then anchors do not match at newline
+ characters in the string.
+ If not set, then anchors do match at newlines. */
+#define REG_NEWLINE (REG_ICASE << 1)
+
+/* If this bit is set, then report only success or fail in regexec.
+ If not set, then returns differ between not matching and errors. */
+#define REG_NOSUB (REG_NEWLINE << 1)
+
+
+/* POSIX `eflags' bits (i.e., information for regexec). */
+
+/* If this bit is set, then the beginning-of-line operator doesn't match
+ the beginning of the string (presumably because it's not the
+ beginning of a line).
+ If not set, then the beginning-of-line operator does match the
+ beginning of the string. */
+#define REG_NOTBOL 1
+
+/* Like REG_NOTBOL, except for the end-of-line. */
+#define REG_NOTEOL (1 << 1)
+
+
+/* If any error codes are removed, changed, or added, update the
+ `re_error_msg' table in regex.c. */
+typedef enum
+{
+ REG_NOERROR = 0, /* Success. */
+ REG_NOMATCH, /* Didn't find a match (for regexec). */
+
+ /* POSIX regcomp return error codes. (In the order listed in the
+ standard.) */
+ REG_BADPAT, /* Invalid pattern. */
+ REG_ECOLLATE, /* Not implemented. */
+ REG_ECTYPE, /* Invalid character class name. */
+ REG_EESCAPE, /* Trailing backslash. */
+ REG_ESUBREG, /* Invalid back reference. */
+ REG_EBRACK, /* Unmatched left bracket. */
+ REG_EPAREN, /* Parenthesis imbalance. */
+ REG_EBRACE, /* Unmatched \{. */
+ REG_BADBR, /* Invalid contents of \{\}. */
+ REG_ERANGE, /* Invalid range end. */
+ REG_ESPACE, /* Ran out of memory. */
+ REG_BADRPT, /* No preceding re for repetition op. */
+
+ /* Error codes we've added. */
+ REG_EEND, /* Premature end. */
+ REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */
+ REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */
+} reg_errcode_t;
+
+/* This data structure represents a compiled pattern. Before calling
+ the pattern compiler, the fields `buffer', `allocated', `fastmap',
+ `translate', and `no_sub' can be set. After the pattern has been
+ compiled, the `re_nsub' field is available. All other fields are
+ private to the regex routines. */
+
+struct re_pattern_buffer
+{
+/* [[[begin pattern_buffer]]] */
+ /* Space that holds the compiled pattern. It is declared as
+ `unsigned char *' because its elements are
+ sometimes used as array indexes. */
+ unsigned char *buffer;
+
+ /* Number of bytes to which `buffer' points. */
+ unsigned long allocated;
+
+ /* Number of bytes actually used in `buffer'. */
+ unsigned long used;
+
+ /* Syntax setting with which the pattern was compiled. */
+ reg_syntax_t syntax;
+
+ /* Pointer to a fastmap, if any, otherwise zero. re_search uses
+ the fastmap, if there is one, to skip over impossible
+ starting points for matches. */
+ char *fastmap;
+
+ /* Either a translate table to apply to all characters before
+ comparing them, or zero for no translation. The translation
+ is applied to a pattern when it is compiled and to a string
+ when it is matched. */
+ char *translate;
+
+ /* Number of subexpressions found by the compiler. */
+ size_t re_nsub;
+
+ /* Zero if this pattern cannot match the empty string, one else.
+ Well, in truth it's used only in `re_search_2', to see
+ whether or not we should use the fastmap, so we don't set
+ this absolutely perfectly; see `re_compile_fastmap' (the
+ `duplicate' case). */
+ unsigned can_be_null : 1;
+
+ /* If REGS_UNALLOCATED, allocate space in the `regs' structure
+ for `max (RE_NREGS, re_nsub + 1)' groups.
+ If REGS_REALLOCATE, reallocate space if necessary.
+ If REGS_FIXED, use what's there. */
+#define REGS_UNALLOCATED 0
+#define REGS_REALLOCATE 1
+#define REGS_FIXED 2
+ unsigned regs_allocated : 2;
+
+ /* Set to zero when `regex_compile' compiles a pattern; set to one
+ by `re_compile_fastmap' if it updates the fastmap. */
+ unsigned fastmap_accurate : 1;
+
+ /* If set, `re_match_2' does not return information about
+ subexpressions. */
+ unsigned no_sub : 1;
+
+ /* If set, a beginning-of-line anchor doesn't match at the
+ beginning of the string. */
+ unsigned not_bol : 1;
+
+ /* Similarly for an end-of-line anchor. */
+ unsigned not_eol : 1;
+
+ /* If true, an anchor at a newline matches. */
+ unsigned newline_anchor : 1;
+
+/* [[[end pattern_buffer]]] */
+};
+
+typedef struct re_pattern_buffer regex_t;
+
+
+/* search.c (search_buffer) in Emacs needs this one opcode value. It is
+ defined both in `regex.c' and here. */
+#define RE_EXACTN_VALUE 1
+
+/* Type for byte offsets within the string. POSIX mandates this. */
+typedef int regoff_t;
+
+
+/* This is the structure we store register match data in. See
+ regex.texinfo for a full description of what registers match. */
+struct re_registers
+{
+ unsigned num_regs;
+ regoff_t *start;
+ regoff_t *end;
+};
+
+
+/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
+ `re_match_2' returns information about at least this many registers
+ the first time a `regs' structure is passed. */
+#ifndef RE_NREGS
+#define RE_NREGS 30
+#endif
+
+
+/* POSIX specification for registers. Aside from the different names than
+ `re_registers', POSIX uses an array of structures, instead of a
+ structure of arrays. */
+typedef struct
+{
+ regoff_t rm_so; /* Byte offset from string's start to substring's start. */
+ regoff_t rm_eo; /* Byte offset from string's start to substring's end. */
+} regmatch_t;
+
+/* Declarations for routines. */
+
+/* To avoid duplicating every routine declaration -- once with a
+ prototype (if we are ANSI), and once without (if we aren't) -- we
+ use the following macro to declare argument types. This
+ unfortunately clutters up the declarations a bit, but I think it's
+ worth it. */
+
+#ifdef __STDC__
+
+#define _RE_ARGS(args) args
+
+#else /* not __STDC__ */
+
+#define _RE_ARGS(args) ()
+
+#endif /* not __STDC__ */
+
+/* Sets the current default syntax to SYNTAX, and return the old syntax.
+ You can also simply assign to the `re_syntax_options' variable. */
+extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));
+
+/* Compile the regular expression PATTERN, with length LENGTH
+ and syntax given by the global `re_syntax_options', into the buffer
+ BUFFER. Return NULL if successful, and an error string if not. */
+extern const char *re_compile_pattern
+ _RE_ARGS ((const char *pattern, size_t length,
+ struct re_pattern_buffer *buffer));
+
+
+/* Compile a fastmap for the compiled pattern in BUFFER; used to
+ accelerate searches. Return 0 if successful and -2 if was an
+ internal error. */
+extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
+
+
+/* Search in the string STRING (with length LENGTH) for the pattern
+ compiled into BUFFER. Start searching at position START, for RANGE
+ characters. Return the starting position of the match, -1 for no
+ match, or -2 for an internal error. Also return register
+ information in REGS (if REGS and BUFFER->no_sub are nonzero). */
+extern int re_search
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+ int length, int start, int range, struct re_registers *regs));
+
+
+/* Like `re_search', but search in the concatenation of STRING1 and
+ STRING2. Also, stop searching at index START + STOP. */
+extern int re_search_2
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+ int length1, const char *string2, int length2,
+ int start, int range, struct re_registers *regs, int stop));
+
+
+/* Like `re_search', but return how many characters in STRING the regexp
+ in BUFFER matched, starting at position START. */
+extern int re_match
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+ int length, int start, struct re_registers *regs));
+
+
+/* Relates to `re_match' as `re_search_2' relates to `re_search'. */
+extern int re_match_2
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+ int length1, const char *string2, int length2,
+ int start, struct re_registers *regs, int stop));
+
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+ ENDS. Subsequent matches using BUFFER and REGS will use this memory
+ for recording register information. STARTS and ENDS must be
+ allocated with malloc, and must each be at least `NUM_REGS * sizeof
+ (regoff_t)' bytes long.
+
+ If NUM_REGS == 0, then subsequent matches should allocate their own
+ register data.
+
+ Unless this function is called, the first search or match using
+ PATTERN_BUFFER will allocate its own register data, without
+ freeing the old data. */
+extern void re_set_registers
+ _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
+ unsigned num_regs, regoff_t *starts, regoff_t *ends));
+
+/* 4.2 bsd compatibility. */
+extern char *re_comp _RE_ARGS ((const char *));
+extern int re_exec _RE_ARGS ((const char *));
+
+/* POSIX compatibility. */
+extern int regcomp _RE_ARGS ((regex_t *preg, const char *pattern, int cflags));
+extern int regexec
+ _RE_ARGS ((const regex_t *preg, const char *string, size_t nmatch,
+ regmatch_t pmatch[], int eflags));
+extern size_t regerror
+ _RE_ARGS ((int errcode, const regex_t *preg, char *errbuf,
+ size_t errbuf_size));
+extern void regfree _RE_ARGS ((regex_t *preg));
+
+#endif /* not __REGEXP_LIBRARY_H__ */
+
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
diff --git a/gnu/usr.bin/awk/version.c b/gnu/usr.bin/awk/version.c
new file mode 100644
index 0000000..89b6cc0
--- /dev/null
+++ b/gnu/usr.bin/awk/version.c
@@ -0,0 +1,47 @@
+char *version_string = "@(#)Gnu Awk (gawk) 2.15";
+
+/* 1.02 fixed /= += *= etc to return the new Left Hand Side instead
+ of the Right Hand Side */
+
+/* 1.03 Fixed split() to treat strings of space and tab as FS if
+ the split char is ' '.
+
+ Added -v option to print version number
+
+ Fixed bug that caused rounding when printing large numbers */
+
+/* 2.00beta Incorporated the functionality of the "new" awk as described
+ the book (reference not handy). Extensively tested, but no
+ doubt still buggy. Badly needs tuning and cleanup, in
+ particular in memory management which is currently almost
+ non-existent. */
+
+/* 2.01 JF: Modified to compile under GCC, and fixed a few
+ bugs while I was at it. I hope I didn't add any more.
+ I modified parse.y to reduce the number of reduce/reduce
+ conflicts. There are still a few left. */
+
+/* 2.02 Fixed JF's bugs; improved memory management, still needs
+ lots of work. */
+
+/* 2.10 Major grammar rework and lots of bug fixes from David.
+ Major changes for performance enhancements from David.
+ A number of minor bug fixes and new features from Arnold.
+ Changes for MSDOS from Conrad Kwok and Scott Garfinkle.
+ The gawk.texinfo and info files included! */
+
+/* 2.11 Bug fix release to 2.10. Lots of changes for portability,
+ speed, and configurability. */
+
+/* 2.12 Lots of changes for portability, speed, and configurability.
+ Several bugs fixed. POSIX compliance. Removal of last set
+ of hard-wired limits. Atari and VMS ports added. */
+
+/* 2.13 Public release of 2.12 */
+
+/* 2.14 Mostly bug fixes. */
+
+/* 2.15 Bug fixes plus intermixing of command-line source and files,
+ GNU long options, ARGIND, ERRNO and Plan 9 style /dev/ files.
+ `delete array'. OS/2 port added. */
+
diff --git a/gnu/usr.bin/bc/COPYING b/gnu/usr.bin/bc/COPYING
new file mode 100644
index 0000000..86cf81a
--- /dev/null
+++ b/gnu/usr.bin/bc/COPYING
@@ -0,0 +1,341 @@
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
diff --git a/gnu/usr.bin/bc/Makefile b/gnu/usr.bin/bc/Makefile
new file mode 100644
index 0000000..fac7437
--- /dev/null
+++ b/gnu/usr.bin/bc/Makefile
@@ -0,0 +1,6 @@
+PROG= bc
+SRCS= bc.c global.c scan.c util.c main.c number.c storage.c load.c execute.c
+MAN1= bc.1
+CFLAGS+= -D_POSIX_SOURCE -I$(.CURDIR)
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/bc/bc.1 b/gnu/usr.bin/bc/bc.1
new file mode 100644
index 0000000..b387f92
--- /dev/null
+++ b/gnu/usr.bin/bc/bc.1
@@ -0,0 +1,730 @@
+.\"
+.\" bc.1 - the *roff document processor source for the bc manual
+.\"
+.\" This file is part of bc written for MINIX.
+.\" Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License , or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; see the file COPYING. If not, write to
+.\" the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\" You may contact the author by:
+.\" e-mail: phil@cs.wwu.edu
+.\" us-mail: Philip A. Nelson
+.\" Computer Science Department, 9062
+.\" Western Washington University
+.\" Bellingham, WA 98226-9062
+.\"
+.\"
+.TH bc 1 .\" "Command Manual" v1.02 "Feb 3, 1992"
+.SH NAME
+bc - An arbitrary precision calculator language
+.SH SYNTAX
+\fBbc\fR [ \fB-lws\fR ] [ \fI file ...\fR ]
+.SH VERSION
+This man page documents GNU bc version 1.02.
+.SH DESCRIPTION
+\fBbc\fR is a language that supports arbitrary precision numbers
+with interactive execution of statements. There are some similarities
+in the syntax to the C programming language.
+A standard math library is available by command line option.
+If requested, the math library is defined before processing any files.
+\fBbc\fR starts by processing code from all the files listed
+on the command line in the order listed. After all files have been
+processed, \fBbc\fR reads from the standard input. All code is
+executed as it is read. (If a file contains a command to halt the
+processor, \fBbc\fR will never read from the standard input.)
+.PP
+This version of \fBbc\fR contains several extensions beyond
+traditional \fBbc\fR implementations and the POSIX draft standard.
+Command line options can cause these extensions to print a warning
+or to be rejected. This
+document describes the language accepted by this processor.
+Extensions will be identified as such.
+.SS OPTIONS
+.IP -l
+Define the standard math library.
+.IP -w
+Give warnings for extensions to POSIX \fBbc\fR.
+.IP -s
+Process exactly the POSIX \fBbc\fR language.
+.SS NUMBERS
+The most basic element in \fBbc\fR is the number. Numbers are
+arbitrary precision numbers. This precision is both in the integer
+part and the fractional part. All numbers are represented internally
+in decimal and all computation is done in decimal. (This version
+truncates results from divide and multiply operations.) There are two
+attributes of numbers, the length and the scale. The length is the
+total number of significant decimal digits in a number and the scale
+is the total number of decimal digits after the decimal point. For
+example:
+.nf
+.RS
+ .000001 has a length of 6 and scale of 6.
+ 1935.000 has a length of 7 and a scale of 3.
+.RE
+.fi
+.SS VARIABLES
+Numbers are stored in two types of variables, simple variables and
+arrays. Both simple variables and array variables are named. Names
+begin with a letter followed by any number of letters, digits and
+underscores. All letters must be lower case. (Full alpha-numeric
+names are an extension. In POSIX \fBbc\fR all names are a single
+lower case letter.) The type of variable is clear by the context
+because all array variable names will be followed by brackets ([]).
+.PP
+There are four special variables, \fBscale, ibase, obase,\fR and
+\fBlast\fR. \fBscale\fR defines how some operations use digits after the
+decimal point. The default value of \fBscale\fR is 0. \fBibase\fR
+and \fBobase\fR define the conversion base for input and output
+numbers. The default for both input and output is base 10.
+\fBlast\fR (an extension) is a variable that has the value of the last
+printed number. These will be discussed in further detail where
+appropriate. All of these variables may have values assigned to them
+as well as used in expressions.
+.SS COMMENTS
+Comments in \fBbc\fR start with the characters \fB/*\fR and end with
+the characters \fB*/\fR. Comments may start anywhere and appear as a
+single space in the input. (This causes comments to delimit other
+input items. For example, a comment can not be found in the middle of
+a variable name.) Comments include any newlines (end of line) between
+the start and the end of the comment.
+.SS EXPRESSIONS
+The numbers are manipulated by expressions and statements. Since
+the language was designed to be interactive, statements and expressions
+are executed as soon as possible. There is no "main" program. Instead,
+code is executed as it is encountered. (Functions, discussed in
+detail later, are defined when encountered.)
+.PP
+A simple expression is just a constant. \fBbc\fR converts constants
+into internal decimal numbers using the current input base, specified
+by the variable \fBibase\fR. (There is an exception in functions.)
+The legal values of \fBibase\fR are 2 through 16 (F). Assigning a
+value outside this range to \fBibase\fR will result in a value of 2
+or 16. Input numbers may contain the characters 0-9 and A-F. (Note:
+They must be capitals. Lower case letters are variable names.)
+Single digit numbers always have the value of the digit regardless of
+the value of \fBibase\fR. (i.e. A = 10.) For multi-digit numbers,
+\fBbc\fR changes all input digits greater or equal to ibase to the
+value of \fBibase\fR-1. This makes the number \fBFFF\fR always be
+the largest 3 digit number of the input base.
+.PP
+Full expressions are similar to many other high level languages.
+Since there is only one kind of number, there are no rules for mixing
+types. Instead, there are rules on the scale of expressions. Every
+expression has a scale. This is derived from the scale of original
+numbers, the operation performed and in many cases, the value of the
+variable \fBscale\fR. Legal values of the variable \fBscale\fR are
+0 to the maximum number representable by a C integer.
+.PP
+In the following descriptions of legal expressions, "expr" refers to a
+complete expression and "var" refers to a simple or an array variable.
+A simple variable is just a
+.RS
+\fIname\fR
+.RE
+and an array variable is specified as
+.RS
+\fIname\fR[\fIexpr\fR]
+.RE
+Unless specifically
+mentioned the scale of the result is the maximum scale of the
+expressions involved.
+.IP "- expr"
+The result is the negation of the expression.
+.IP "++ var"
+The variable is incremented by one and the new value is the result of
+the expression.
+.IP "-- var"
+The variable
+is decremented by one and the new value is the result of the
+expression.
+.IP "var ++"
+ The result of the expression is the value of
+the variable and then the variable is incremented by one.
+.IP "var --"
+The result of the expression is the value of the variable and then
+the variable is decremented by one.
+.IP "expr + expr"
+The result of the expression is the sum of the two expressions.
+.IP "expr - expr"
+The result of the expression is the difference of the two expressions.
+.IP "expr * expr"
+The result of the expression is the product of the two expressions.
+.IP "expr / expr"
+The result of the expression is the quotient of the two expressions.
+The scale of the result is the value of the variable \fBscale\fR.
+.IP "expr % expr"
+The result of the expression is the "remainder" and it is computed in the
+following way. To compute a%b, first a/b is computed to \fBscale\fR
+digits. That result is used to compute a-(a/b)*b to the scale of the
+maximum of \fBscale\fR+scale(b) and scale(a). If \fBscale\fR is set
+to zero and both expressions are integers this expression is the
+integer remainder function.
+.IP "expr ^ expr"
+The result of the expression is the value of the first raised to the
+second. The second expression must be an integer. (If the second
+expression is not an integer, a warning is generated and the
+expression is truncated to get an integer value.) The scale of the
+result is \fBscale\fR if the exponent is negative. If the exponent
+is positive the scale of the result is the minimum of the scale of the
+first expression times the value of the exponent and the maximum of
+\fBscale\fR and the scale of the first expression. (e.g. scale(a^b)
+= min(scale(a)*b, max( \fBscale,\fR scale(a))).) It should be noted
+that expr^0 will always return the value of 1.
+.IP "( expr )"
+This alters the standard precedence to force the evaluation of the
+expression.
+.IP "var = expr"
+The variable is assigned the value of the expression.
+.IP "var <op>= expr"
+This is equivalent to "var = var <op> expr" with the exception that
+the "var" part is evaluated only once. This can make a difference if
+"var" is an array.
+.PP
+ Relational expressions are a special kind of expression
+that always evaluate to 0 or 1, 0 if the relation is false and 1 if
+the relation is true. These may appear in any legal expression.
+(POSIX bc requires that relational expressions are used only in if,
+while, and for statements and that only one relational test may be
+done in them.) The relational operators are
+.IP "expr1 < expr2"
+The result is 1 if expr1 is strictly less than expr2.
+.IP "expr1 <= expr2"
+The result is 1 if expr1 is less than or equal to expr2.
+.IP "expr1 > expr2"
+The result is 1 if expr1 is strictly greater than expr2.
+.IP "expr1 >= expr2"
+The result is 1 if expr1 is greater than or equal to expr2.
+.IP "expr1 == expr2"
+The result is 1 if expr1 is equal to expr2.
+.IP "expr1 != expr2"
+The result is 1 if expr1 is not equal to expr2.
+.PP
+Boolean operations are also legal. (POSIX \fBbc\fR does NOT have
+boolean operations). The result of all boolean operations are 0 and 1
+(for false and true) as in relational expressions. The boolean
+operators are:
+.IP "!expr"
+The result is 1 if expr is 0.
+.IP "expr && expr"
+The result is 1 if both expressions are non-zero.
+.IP "expr || expr"
+The result is 1 if either expression is non-zero.
+.PP
+The expression precedence is as follows: (lowest to highest)
+.nf
+.RS
+|| operator, left associative
+&& operator, left associative
+! operator, nonassociative
+Relational operators, left associative
+Assignment operator, right associative
++ and - operators, left associative
+*, / and % operators, left associative
+^ operator, right associative
+unary - operator, nonassociative
+++ and -- operators, nonassociative
+.RE
+.fi
+.PP
+This precedence was chosen so that POSIX compliant \fBbc\fR programs
+will run correctly. This will cause the use of the relational and
+logical operators to have some unusual behavior when used with
+assignment expressions. Consider the expression:
+.RS
+a = 3 < 5
+.RE
+.PP
+Most C programmers would assume this would assign the result of "3 <
+5" (the value 1) to the variable "a". What this does in \fBbc\fR is
+assign the value 3 to the variable "a" and then compare 3 to 5. It is
+best to use parenthesis when using relational and logical operators
+with the assignment operators.
+.PP
+There are a few more special expressions that are provided in \fBbc\fR.
+These have to do with user defined functions and standard
+functions. They all appear as "\fIname\fB(\fIparameters\fB)\fR".
+See the section on functions for user defined functions. The standard
+functions are:
+.IP "length ( expression )"
+The value of the length function is the number of significant digits in the
+expression.
+.IP "read ( )"
+The read function (an extension) will read a number from the standard
+input, regardless of where the function occurs. Beware, this can
+cause problems with the mixing of data and program in the standard input.
+The best use for this function is in a previously written program that
+needs input from the user, but never allows program code to be input
+from the user. The value of the read function is the number read from
+the standard input using the current value of the variable
+\fBibase\fR for the conversion base.
+.IP "scale ( expression )"
+The value of the scale function is the number of digits after the decimal
+point in the expression.
+.IP "sqrt ( expression )"
+The value of the sqrt function is the square root of the expression. If
+the expression is negative, a run time error is generated.
+.SS STATEMENTS
+Statements (as in most algebraic languages) provide the sequencing of
+expression evaluation. In \fBbc\fR statements are executed "as soon
+as possible." Execution happens when a newline in encountered and
+there is one or more complete statements. Due to this immediate
+execution, newlines are very important in \fBbc\fR. In fact, both a
+semicolon and a newline are used as statement separators. An
+improperly placed newline will cause a syntax error. Because newlines
+are statement separators, it is possible to hide a newline by using
+the backslash character. The sequence "\e<nl>", where <nl> is the
+newline appears to \fBbc\fR as whitespace instead of a newline. A
+statement list is a series of statements separated by semicolons and
+newlines. The following is a list of \fBbc\fR statements and what
+they do: (Things enclosed in brackets ([]) are optional parts of the
+statement.)
+.IP "expression"
+This statement does one of two things. If the expression starts with
+"<variable> <assignment> ...", it is considered to be an assignment
+statement. If the expression is not an assignment statement, the
+expression is evaluated and printed to the output. After the number
+is printed, a newline is printed. For example, "a=1" is an assignment
+statement and "(a=1)" is an expression that has an embedded
+assignment. All numbers that are printed are printed in the base
+specified by the variable \fBobase\fR. The legal values for \fB
+obase\fR are 2 through BC_BASE_MAX. (See the section LIMITS.) For
+bases 2 through 16, the usual method of writing numbers is used. For
+bases greater than 16, \fBbc\fR uses a multi-character digit method
+of printing the numbers where each higher base digit is printed as a
+base 10 number. The multi-character digits are separated by spaces.
+Each digit contains the number of characters required to represent the
+base ten value of "obase-1". Since numbers are of arbitrary
+precision, some numbers may not be printable on a single output line.
+These long numbers will be split across lines using the "\e" as the
+last character on a line. The maximum number of characters printed
+per line is 70. Due to the interactive nature of \fBbc\fR printing
+a number cause the side effect of assigning the printed value the the
+special variable \fBlast\fR. This allows the user to recover the
+last value printed without having to retype the expression that
+printed the number. Assigning to \fBlast\fR is legal and will
+overwrite the last printed value with the assigned value. The newly
+assigned value will remain until the next number is printed or another
+value is assigned to \fBlast\fR.
+.IP "string"
+The string is printed to the output. Strings start with a double quote
+character and contain all characters until the next double quote character.
+All characters are take literally, including any newline. No newline
+character is printed after the string.
+.IP "\fBprint\fR list"
+The print statement (an extension) provides another method of output.
+The "list" is a list of strings and expressions separated by commas.
+Each string or expression is printed in the order of the list. No
+terminating newline is printed. Expressions are evaluated and their
+value is printed and assigned the the variable \fBlast\fR. Strings
+in the print statement are printed to the output and may contain
+special characters. Special characters start with the backslash
+character (\e). The special characters recognized by \fBbc\fR are
+"b" (bell), "f" (form feed), "n" (newline), "r" (carriage return), "t"
+(tab), and "\e" (backslash). Any other character following the
+backslash will be ignored. This still does not allow the double quote
+character to be part of any string.
+.IP "{ statement_list }"
+This is the compound statement. It allows multiple statements to be
+grouped together for execution.
+.IP "\fBif\fR ( expression ) \fBthen\fR statement1 [\fBelse\fR statement2]"
+The if statement evaluates the expression and executes statement1 or
+statement2 depending on the value of the expression. If the expression
+is non-zero, statement1 is executed. If statement2 is present and
+the value of the expression is 0, then statement2 is executed. (The
+else clause is an extension.)
+.IP "\fBwhile\fR ( expression ) statement"
+The while statement will execute the statement while the expression
+is non-zero. It evaluates the expression before each execution of
+the statement. Termination of the loop is caused by a zero
+expression value or the execution of a break statement.
+.IP "\fBfor\fR ( [expression1] ; [expression2] ; [expression3] ) statement"
+The for statement controls repeated execution of the statement.
+Expression1 is evaluated before the loop. Expression2 is evaluated
+before each execution of the statement. If it is non-zero, the statement
+is evaluated. If it is zero, the loop is terminated. After each
+execution of the statement, expression3 is evaluated before the reevaluation
+of expression2. If expression1 or expression3 are missing, nothing is
+evaluated at the point they would be evaluated.
+If expression2 is missing, it is the same as substituting
+the value 1 for expression2. (The optional expressions are an
+extension. POSIX \fBbc\fR requires all three expressions.)
+The following is equivalent code for the for statement:
+.nf
+.RS
+expression1;
+while (expression2) {
+ statement;
+ expression3;
+}
+.RE
+.fi
+.IP "\fBbreak\fR"
+This statement causes a forced exit of the most recent enclosing while
+statement or for statement.
+.IP "\fBcontinue\fR"
+The continue statement (an extension) causes the most recent enclosing
+for statement to start the next iteration.
+.IP "\fBhalt\fR"
+The halt statement (an extension) is an executed statement that causes
+the \fBbc\fR processor to quit only when it is executed. For example,
+"if (0 == 1) halt" will not cause \fBbc\fR to terminate because the halt is
+not executed.
+.IP "\fBreturn\fR"
+Return the value 0 from a function. (See the section on functions.)
+.IP "\fBreturn\fR ( expression )"
+Return the value of the expression from a function. (See the section on
+functions.)
+.SS PSEUDO STATEMENTS
+These statements are not statements in the traditional sense. They are
+not executed statements. Their function is performed at "compile" time.
+.IP "\fBlimits\fR"
+Print the local limits enforced by the local version of \fBbc\fR. This
+is an extension.
+.IP "\fBquit\fR"
+When the quit statement is read, the \fBbc\fR processor
+is terminated, regardless of where the quit statement is found. For
+example, "if (0 == 1) quit" will cause \fBbc\fR to terminate.
+.IP "\fBwarranty\fR"
+Print a longer warranty notice. This is an extension.
+.SS FUNCTIONS
+Functions provide a method of defining a computation that can be executed
+later. Functions in
+.B bc
+always compute a value and return it to the caller. Function definitions
+are "dynamic" in the sense that a function is undefined until a definition
+is encountered in the input. That definition is then used until another
+definition function for the same name is encountered. The new definition
+then replaces the older definition. A function is defined as follows:
+.nf
+.RS
+\fBdefine \fIname \fB( \fIparameters \fB) { \fInewline
+\fI auto_list statement_list \fB}\fR
+.RE
+.fi
+A function call is just an expression of the form
+"\fIname\fB(\fIparameters\fB)\fR".
+.PP
+Parameters are numbers or arrays (an extension). In the function definition,
+zero or more parameters are defined by listing their names separated by
+commas. Numbers are only call by value parameters. Arrays are only
+call by variable. Arrays are specified in the parameter definition by
+the notation "\fIname\fB[]\fR". In the function call, actual parameters
+are full expressions for number parameters. The same notation is used
+for passing arrays as for defining array parameters. The named array is
+passed by variable to the function. Since function definitions are dynamic,
+parameter numbers and types are checked when a function is called. Any
+mismatch in number or types of parameters will cause a runtime error.
+A runtime error will also occur for the call to an undefined function.
+.PP
+The \fIauto_list\fR is an optional list of variables that are for
+"local" use. The syntax of the auto list (if present) is "\fBauto
+\fIname\fR, ... ;". (The semicolon is optional.) Each \fIname\fR is
+the name of an auto variable. Arrays may be specified by using the
+same notation as used in parameters. These variables have their
+values pushed onto a stack at the start of the function. The
+variables are then initialized to zero and used throughout the
+execution of the function. At function exit, these variables are
+popped so that the original value (at the time of the function call)
+of these variables are restored. The parameters are really auto
+variables that are initialized to a value provided in the function
+call. Auto variables are different than traditional local variables
+in the fact that if function A calls function B, B may access function
+A's auto variables by just using the same name, unless function B has
+called them auto variables. Due to the fact that auto variables and
+parameters are pushed onto a stack, \fBbc\fR supports recursive functions.
+.PP
+The function body is a list of \fBbc\fR statements. Again, statements
+are separated by semicolons or newlines. Return statements cause the
+termination of a function and the return of a value. There are two
+versions of the return statement. The first form, "\fBreturn\fR", returns
+the value 0 to the calling expression. The second form,
+"\fBreturn ( \fIexpression \fB)\fR", computes the value of the expression
+and returns that value to the calling expression. There is an implied
+"\fBreturn (0)\fR" at the end of every function. This allows a function
+to terminate and return 0 without an explicit return statement.
+.PP
+Functions also change the usage of the variable \fBibase\fR. All
+constants in the function body will be converted using the value of
+\fBibase\fR at the time of the function call. Changes of \fBibase\fR
+will be ignored during the execution of the function except for the
+standard function \fBread\fR, which will always use the current value
+of \fBibase\fR for conversion of numbers.
+.SS MATH LIBRARY
+If \fBbc\fR is invoked with the \fB-l\fR option, a math library is preloaded
+and the default scale is set to 20. The math functions will calculate their
+results to the scale set at the time of their call.
+The math library defines the following functions:
+.IP "s (\fIx\fR)"
+The sine of x in radians.
+.IP "c (\fIx\fR)"
+The cosine of x in radians.
+.IP "a (\fIx\fR)"
+The arctangent of x.
+.IP "l (\fIx\fR)"
+The natural logarithm of x.
+.IP "e (\fIx\fR)"
+The exponential function of raising e to the value x.
+.IP "j (\fIn,x\fR)"
+The bessel function of integer order n of x.
+.SS EXAMPLES
+In /bin/sh, the following will assign the value of "pi" to the shell
+variable \fBpi\fR.
+.RS
+\f(CW
+pi=$(echo "scale=10; 4*a(1)" | bc -l)
+\fR
+.RE
+.PP
+The following is the definition of the exponential function used in the
+math library. This function is written in POSIX \fBbc\fR.
+.nf
+.RS
+\f(CW
+scale = 20
+
+/* Uses the fact that e^x = (e^(x/2))^2
+ When x is small enough, we use the series:
+ e^x = 1 + x + x^2/2! + x^3/3! + ...
+*/
+
+define e(x) {
+ auto a, d, e, f, i, m, v, z
+
+ /* Check the sign of x. */
+ if (x<0) {
+ m = 1
+ x = -x
+ }
+
+ /* Precondition x. */
+ z = scale;
+ scale = 4 + z + .44*x;
+ while (x > 1) {
+ f += 1;
+ x /= 2;
+ }
+
+ /* Initialize the variables. */
+ v = 1+x
+ a = x
+ d = 1
+
+ for (i=2; 1; i++) {
+ e = (a *= x) / (d *= i)
+ if (e == 0) {
+ if (f>0) while (f--) v = v*v;
+ scale = z
+ if (m) return (1/v);
+ return (v/1);
+ }
+ v += e
+ }
+}
+\fR
+.RE
+.fi
+.PP
+The following is code that uses the extended features of \fBbc\fR to
+implement a simple program for calculating checkbook balances. This
+program is best kept in a file so that it can be used many times
+without having to retype it at every use.
+.nf
+.RS
+\f(CW
+scale=2
+print "\enCheck book program!\en"
+print " Remember, deposits are negative transactions.\en"
+print " Exit by a 0 transaction.\en\en"
+
+print "Initial balance? "; bal = read()
+bal /= 1
+print "\en"
+while (1) {
+ "current balance = "; bal
+ "transaction? "; trans = read()
+ if (trans == 0) break;
+ bal -= trans
+ bal /= 1
+}
+quit
+\fR
+.RE
+.fi
+.PP
+The following is the definition of the recursive factorial function.
+.nf
+.RS
+\f(CW
+define f (x) {
+ if (x <= 1) return (1);
+ return (f(x-1) * x);
+}
+\fR
+.RE
+.fi
+.SS DIFFERENCES
+This version of
+.B bc
+was implemented from the POSIX P1003.2/D11 draft and contains
+several differences and extensions relative to the draft and
+traditional implementations.
+It is not implemented in the traditional way using
+.I dc(1).
+This version is a single process which parses and runs a byte code
+translation of the program. There is an "undocumented" option (-c)
+that causes the program to output the byte code to
+the standard output instead of running it. It was mainly used for
+debugging the parser and preparing the math library.
+.PP
+A major source of differences is
+extensions, where a feature is extended to add more functionality and
+additions, where new features are added.
+The following is the list of differences and extensions.
+.IP LANG environment
+This version does not conform to the POSIX standard in the processing
+of the LANG environment variable and all environment variables starting
+with LC_.
+.IP names
+Traditional and POSIX
+.B bc
+have single letter names for functions, variables and arrays. They have
+been extended to be multi-character names that start with a letter and
+may contain letters, numbers and the underscore character.
+.IP Strings
+Strings are not allowed to contain NUL characters. POSIX says all characters
+must be included in strings.
+.IP last
+POSIX \fBbc\fR does not have a \fBlast\fR variable. Some implementations
+of \fBbc\fR use the period (.) in a similar way.
+.IP comparisons
+POSIX \fBbc\fR allows comparisons only in the if statement, the while
+statement, and the second expression of the for statement. Also, only
+one relational operation is allowed in each of those statements.
+.IP "if statement, else clause"
+POSIX \fBbc\fR does not have an else clause.
+.IP "for statement"
+POSIX \fBbc\fR requires all expressions to be present in the for statement.
+.IP "&&, ||, !"
+POSIX \fBbc\fR does not have the logical operators.
+.IP "read function"
+POSIX \fBbc\fR does not have a read function.
+.IP "print statement"
+POSIX \fBbc\fR does not have a print statement .
+.IP "continue statement"
+POSIX \fBbc\fR does not have a continue statement.
+.IP "array parameters"
+POSIX \fBbc\fR does not have array parameters. Other implementations
+of \fBbc\fR may have call by value array parameters.
+.IP "=+, =-, =*, =/, =%, =^"
+POSIX \fBbc\fR does not require these "old style" assignment operators to
+be defined. This version may allow these "old style" assignments. Use
+the limits statement to see if the installed version supports them. If
+it does support the "old style" assignment operators, the statement
+"a =- 1" will decrement \fBa\fR by 1 instead of setting \fBa\fR to the
+value -1.
+.IP "spaces in numbers"
+Other implementations of \fBbc\fR allow spaces in numbers. For example,
+"x=1 3" would assign the value 13 to the variable x. The same statement
+would cause a syntax error in this version of \fBbc\fR.
+.IP "errors and execution"
+This implementation varies from other implementations in terms of what
+code will be executed when syntax and other errors are found in the
+program. If a syntax error is found in a function definition, error
+recovery tries to find the beginning of a statement and continue to
+parse the function. Once a syntax error is found in the function, the
+function will not be callable and becomes undefined.
+Syntax errors in the interactive execution code will invalidate the
+current execution block. The execution block is terminated by an
+end of line that appears after a complete sequence of statements.
+For example,
+.nf
+.RS
+a = 1
+b = 2
+.RE
+.fi
+has two execution blocks and
+.nf
+.RS
+{ a = 1
+ b = 2 }
+.RE
+.fi
+has one execution block. Any runtime error will terminate the execution
+of the current execution block. A runtime warning will not terminate the
+current execution block.
+.IP "Interrupts"
+During an interactive session, the SIGINT signal (usually generated by
+the control-C character from the terminal) will cause execution of the
+current execution block to be interrupted. It will display a "runtime"
+error indicating which function was interrupted. After all runtime
+structures have been cleaned up, a message will be printed to notify the
+user that \fBbc\fR is ready for more input. All previously defined functions
+remain defined and the value of all non-auto variables are the value at
+the point of interruption. All auto variables and function parameters
+are removed during the
+clean up process. During a non-interactive
+session, the SIGINT signal will terminate the entire run of \fBbc\fR.
+.SS LIMITS
+The following are the limits currently in place for this
+.B bc
+processor. Some of them may have been changed by an installation.
+Use the limits statement to see the actual values.
+.IP BC_BASE_MAX
+The maximum output base is currently set at 999. The maximum input base
+is 16.
+.IP BC_DIM_MAX
+This is currently an arbitrary limit of 65535 as distributed. Your
+installation may be different.
+.IP BC_SCALE_MAX
+The number of digits after the decimal point is limited to INT_MAX digits.
+Also, the number of digits before the decimal point is limited to INT_MAX
+digits.
+.IP BC_STRING_MAX
+The limit on the number of characters in a string is INT_MAX characters.
+.IP exponent
+The value of the exponent in the raise operation (^) is limited to LONG_MAX.
+.IP multiply
+The multiply routine may yield incorrect results if a number
+has more than LONG_MAX / 90 total digits. For 32 bit longs, this number is
+23,860,929 digits.
+.IP "code size"
+Each function and the "main" program are limited to 10240 bytes of
+compiled byte code each. This limit (BC_MAX_SEGS) can be easily changed
+to have more than 10 segments of 1024 bytes.
+.IP "variable names"
+The current limit on the number of unique names is 32767 for each of
+simple variables, arrays and functions.
+.SH FILES
+In most installations, \fBbc\fR is completely self-contained.
+Where executable size is of importance or the C compiler does
+not deal with very long strings, \fBbc\fR will read
+the standard math library from the file /usr/local/lib/libmath.b.
+(The actual location may vary. It may be /lib/libmath.b.)
+.SH DIAGNOSTICS
+If any file on the command line can not be opened, \fBbc\fR will report
+that the file is unavailable and terminate. Also, there are compile
+and run time diagnostics that should be self-explanatory.
+.SH BUGS
+Error recovery is not very good yet.
+.SH AUTHOR
+.nf
+Philip A. Nelson
+phil@cs.wwu.edu
+.fi
+.SH ACKNOWLEDGEMENTS
+The author would like to thank Steve Sommars (sesv@iwtsf.att.com) for
+his extensive help in testing the implementation. Many great suggestions
+were given. This is a much better product due to his involvement.
diff --git a/gnu/usr.bin/bc/bc.c b/gnu/usr.bin/bc/bc.c
new file mode 100644
index 0000000..923215e
--- /dev/null
+++ b/gnu/usr.bin/bc/bc.c
@@ -0,0 +1,1369 @@
+#ifndef lint
+static char yysccsid[] = "@(#)yaccpar 1.8 (Berkeley) 01/20/90";
+#endif
+#define YYBYACC 1
+#line 2 "bc.y"
+/* bc.y: The grammar for a POSIX compatable bc processor with some
+ extensions to the language. */
+
+/* This file is part of bc written for MINIX.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License , or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ You may contact the author by:
+ e-mail: phil@cs.wwu.edu
+ us-mail: Philip A. Nelson
+ Computer Science Department, 9062
+ Western Washington University
+ Bellingham, WA 98226-9062
+
+*************************************************************************/
+
+#include "bcdefs.h"
+#include "global.h"
+#include "proto.h"
+#line 38 "bc.y"
+typedef union {
+ char *s_value;
+ char c_value;
+ int i_value;
+ arg_list *a_value;
+ } YYSTYPE;
+#line 46 "y.tab.c"
+#define NEWLINE 257
+#define AND 258
+#define OR 259
+#define NOT 260
+#define STRING 261
+#define NAME 262
+#define NUMBER 263
+#define MUL_OP 264
+#define ASSIGN_OP 265
+#define REL_OP 266
+#define INCR_DECR 267
+#define Define 268
+#define Break 269
+#define Quit 270
+#define Length 271
+#define Return 272
+#define For 273
+#define If 274
+#define While 275
+#define Sqrt 276
+#define Else 277
+#define Scale 278
+#define Ibase 279
+#define Obase 280
+#define Auto 281
+#define Read 282
+#define Warranty 283
+#define Halt 284
+#define Last 285
+#define Continue 286
+#define Print 287
+#define Limits 288
+#define UNARY_MINUS 289
+#define YYERRCODE 256
+short yylhs[] = { -1,
+ 0, 0, 10, 10, 10, 11, 11, 11, 11, 12,
+ 12, 12, 12, 12, 12, 15, 15, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 16, 17, 18,
+ 19, 13, 20, 13, 22, 23, 13, 13, 25, 13,
+ 24, 24, 26, 26, 21, 27, 21, 28, 14, 5,
+ 5, 6, 6, 6, 7, 7, 7, 7, 8, 8,
+ 9, 9, 9, 9, 4, 4, 2, 2, 29, 1,
+ 30, 1, 31, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 3, 3, 3, 3, 3, 3,
+};
+short yylen[] = { 2,
+ 0, 2, 2, 1, 2, 0, 1, 3, 2, 0,
+ 1, 2, 3, 2, 3, 1, 2, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 4, 0, 0, 0,
+ 0, 13, 0, 7, 0, 0, 7, 3, 0, 3,
+ 1, 3, 1, 1, 0, 0, 3, 0, 12, 0,
+ 1, 0, 3, 3, 1, 3, 3, 5, 0, 1,
+ 1, 3, 3, 5, 0, 1, 0, 1, 0, 4,
+ 0, 4, 0, 4, 2, 3, 3, 3, 3, 3,
+ 2, 1, 1, 3, 4, 2, 2, 4, 4, 4,
+ 3, 1, 4, 1, 1, 1, 1,
+};
+short yydefred[] = { 1,
+ 0, 0, 0, 21, 0, 83, 0, 0, 22, 24,
+ 0, 0, 28, 0, 35, 0, 0, 94, 95, 0,
+ 18, 25, 97, 23, 39, 19, 0, 0, 0, 0,
+ 0, 2, 0, 16, 4, 7, 5, 17, 0, 0,
+ 0, 0, 96, 86, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 81, 0, 0, 0, 11, 71,
+ 73, 0, 0, 0, 0, 0, 69, 87, 3, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 91, 43, 0, 40, 0, 84,
+ 0, 0, 38, 0, 0, 0, 0, 0, 0, 0,
+ 0, 8, 0, 85, 0, 93, 0, 0, 0, 88,
+ 27, 0, 0, 33, 0, 89, 90, 0, 13, 15,
+ 0, 0, 0, 62, 0, 0, 0, 0, 0, 29,
+ 0, 0, 42, 0, 56, 0, 0, 0, 0, 0,
+ 64, 0, 0, 0, 46, 34, 37, 0, 48, 58,
+ 30, 0, 0, 0, 0, 47, 53, 54, 0, 0,
+ 0, 31, 49, 0, 32,
+};
+short yydgoto[] = { 1,
+ 30, 79, 31, 113, 108, 149, 109, 73, 74, 32,
+ 33, 58, 34, 35, 59, 48, 138, 155, 164, 131,
+ 146, 50, 132, 88, 54, 89, 152, 154, 101, 94,
+ 95,
+};
+short yysindex[] = { 0,
+ -7, 58, 212, 0, -22, 0, -233, -241, 0, 0,
+ -8, -5, 0, -4, 0, 2, 4, 0, 0, 9,
+ 0, 0, 0, 0, 0, 0, 212, 212, 91, 725,
+ -240, 0, -29, 0, 0, 0, 0, 0, 84, 245,
+ 212, -57, 0, 0, 10, 212, 212, 14, 212, 16,
+ 212, 212, 23, 156, 0, 549, 127, -52, 0, 0,
+ 0, 212, 212, 212, 212, 212, 0, 0, 0, 91,
+ -17, 725, 24, -3, 578, -205, 562, 725, 27, 212,
+ 606, 212, 669, 716, 0, 0, 725, 0, 19, 0,
+ 91, 127, 0, 212, 212, -36, -39, -91, -91, -36,
+ 212, 0, 166, 0, 277, 0, -21, 36, 40, 0,
+ 0, 725, 28, 0, 725, 0, 0, 156, 0, 0,
+ 84, 540, -39, 0, -9, 725, -2, -37, -174, 0,
+ 127, 48, 0, 346, 0, -167, 3, 212, -185, 127,
+ 0, -188, 6, 37, 0, 0, 0, -205, 0, 0,
+ 0, 127, -42, 91, 212, 0, 0, 0, -20, 54,
+ 26, 0, 0, 127, 0,
+};
+short yyrindex[] = { 0,
+ -16, 0, 0, 0, 409, 0, 0, 0, 0, 0,
+ 0, -58, 0, 0, 0, 0, 426, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, -50, 46,
+ 470, 0, 0, 0, 0, 0, 0, 0, 661, 56,
+ 0, 525, 0, 0, 0, 0, 59, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, -6,
+ 705, 7, 0, 60, 0, 61, 0, 63, 0, 49,
+ 0, 0, 0, 0, 0, 0, 17, 0, 78, 0,
+ -47, -45, 0, 0, 0, 537, 440, 620, 637, 594,
+ 0, 0, 0, 0, 0, 0, -33, 0, 66, 0,
+ 0, -19, 0, 0, 68, 0, 0, 0, 0, 0,
+ 667, 680, 508, 0, 705, 18, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, -31, 49, -44, 0,
+ 0, -40, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 69, 0, 0, 0, 0, 0,
+ 13, 0, 0, 0, 0,
+};
+short yygindex[] = { 0,
+ 958, 0, 104, -118, 0, 0, -35, 0, 0, 0,
+ 0, -34, 22, 0, 15, 0, 0, 0, 0, 0,
+ 0, 0, 0, -1, 0, 0, 0, 0, 0, 0,
+ 0,
+};
+#define YYTABLESIZE 1113
+short yytable[] = { 52,
+ 26, 129, 66, 64, 52, 65, 92, 55, 10, 57,
+ 55, 12, 57, 14, 45, 36, 158, 40, 52, 144,
+ 45, 66, 40, 38, 67, 55, 68, 57, 42, 70,
+ 40, 46, 28, 41, 47, 49, 160, 27, 92, 66,
+ 105, 51, 6, 52, 43, 18, 19, 61, 53, 76,
+ 61, 23, 9, 80, 66, 82, 107, 66, 63, 10,
+ 44, 63, 118, 85, 104, 28, 26, 111, 41, 127,
+ 27, 12, 93, 103, 10, 44, 128, 12, 38, 14,
+ 45, 134, 52, 129, 102, 136, 130, 137, 140, 142,
+ 135, 145, 148, 143, 162, 151, 59, 28, 150, 67,
+ 60, 50, 27, 68, 20, 119, 51, 65, 36, 65,
+ 44, 0, 153, 120, 0, 29, 133, 0, 0, 159,
+ 0, 0, 0, 0, 0, 0, 64, 0, 65, 0,
+ 28, 0, 0, 0, 0, 27, 41, 0, 0, 0,
+ 0, 44, 0, 0, 0, 0, 0, 0, 29, 0,
+ 163, 0, 139, 0, 0, 0, 0, 0, 0, 0,
+ 0, 147, 0, 0, 0, 0, 28, 0, 0, 0,
+ 20, 27, 62, 156, 0, 119, 0, 66, 0, 0,
+ 29, 0, 0, 0, 0, 165, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 28, 0, 0, 26, 0,
+ 27, 0, 41, 0, 91, 28, 10, 0, 0, 12,
+ 27, 14, 45, 29, 157, 52, 52, 0, 26, 52,
+ 52, 52, 52, 55, 62, 57, 52, 69, 52, 52,
+ 52, 52, 52, 52, 52, 52, 161, 52, 52, 52,
+ 6, 52, 52, 52, 52, 52, 52, 52, 2, 29,
+ 9, 28, 3, 4, 5, 6, 27, 10, 124, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 12,
+ 17, 18, 19, 44, 20, 21, 22, 23, 24, 25,
+ 26, 57, 0, 0, 28, 3, 4, 5, 6, 27,
+ 0, 0, 7, 44, 9, 10, 11, 12, 13, 14,
+ 15, 16, 20, 17, 18, 19, 0, 20, 21, 22,
+ 23, 24, 25, 26, 37, 0, 28, 3, 4, 5,
+ 6, 27, 20, 0, 7, 0, 9, 10, 11, 12,
+ 13, 14, 15, 16, 41, 17, 18, 19, 0, 20,
+ 21, 22, 23, 24, 25, 26, 57, 62, 0, 63,
+ 3, 4, 5, 6, 41, 0, 0, 7, 0, 9,
+ 10, 11, 12, 13, 14, 15, 16, 0, 17, 18,
+ 19, 0, 20, 21, 22, 23, 24, 25, 26, 0,
+ 0, 0, 0, 0, 0, 28, 3, 4, 5, 6,
+ 27, 0, 0, 7, 0, 9, 10, 11, 12, 13,
+ 14, 15, 16, 0, 17, 18, 19, 0, 20, 21,
+ 22, 23, 24, 25, 26, 3, 86, 5, 6, 0,
+ 0, 0, 7, 0, 0, 3, 11, 5, 6, 0,
+ 0, 16, 7, 17, 18, 19, 11, 20, 141, 0,
+ 23, 16, 0, 17, 18, 19, 0, 20, 0, 92,
+ 23, 92, 92, 92, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 96, 92, 96, 96,
+ 96, 3, 0, 5, 6, 0, 0, 0, 7, 0,
+ 76, 0, 11, 76, 96, 0, 0, 16, 0, 17,
+ 18, 19, 0, 20, 0, 0, 23, 0, 76, 0,
+ 0, 92, 92, 0, 3, 0, 71, 6, 0, 0,
+ 82, 7, 82, 82, 82, 11, 0, 0, 96, 96,
+ 16, 0, 17, 18, 19, 0, 20, 0, 82, 23,
+ 0, 0, 76, 92, 0, 0, 3, 0, 125, 6,
+ 0, 0, 0, 7, 0, 0, 0, 11, 70, 0,
+ 96, 70, 16, 0, 17, 18, 19, 0, 20, 0,
+ 0, 23, 82, 82, 76, 92, 70, 92, 92, 92,
+ 0, 0, 0, 0, 0, 0, 0, 79, 0, 79,
+ 79, 79, 64, 92, 65, 0, 0, 0, 0, 90,
+ 0, 64, 0, 65, 82, 79, 0, 0, 0, 0,
+ 70, 0, 110, 0, 64, 3, 65, 5, 6, 0,
+ 0, 0, 7, 0, 0, 0, 11, 92, 92, 0,
+ 64, 16, 65, 17, 18, 19, 0, 20, 0, 79,
+ 23, 0, 70, 66, 80, 0, 80, 80, 80, 0,
+ 0, 0, 66, 0, 0, 0, 114, 0, 64, 92,
+ 65, 0, 80, 0, 0, 66, 0, 0, 0, 0,
+ 77, 79, 77, 77, 77, 92, 92, 92, 0, 0,
+ 106, 66, 92, 92, 92, 92, 0, 78, 77, 78,
+ 78, 78, 96, 96, 96, 92, 80, 0, 0, 96,
+ 96, 96, 96, 0, 0, 78, 76, 76, 76, 66,
+ 0, 75, 96, 0, 75, 76, 0, 72, 0, 116,
+ 72, 64, 77, 65, 0, 0, 76, 0, 80, 75,
+ 74, 0, 0, 74, 0, 72, 82, 82, 82, 78,
+ 0, 0, 0, 82, 0, 82, 0, 0, 74, 0,
+ 0, 0, 0, 0, 77, 92, 82, 92, 92, 92,
+ 0, 0, 0, 75, 0, 0, 117, 0, 64, 72,
+ 65, 78, 66, 0, 70, 70, 70, 64, 0, 65,
+ 0, 0, 74, 70, 0, 0, 0, 0, 0, 0,
+ 0, 92, 92, 92, 70, 75, 0, 0, 92, 0,
+ 92, 72, 0, 79, 79, 79, 0, 60, 92, 0,
+ 79, 92, 79, 62, 74, 63, 60, 61, 0, 66,
+ 0, 0, 62, 79, 63, 0, 0, 0, 66, 60,
+ 61, 0, 0, 0, 0, 62, 0, 63, 0, 0,
+ 0, 0, 0, 0, 0, 60, 61, 0, 0, 0,
+ 0, 62, 0, 63, 0, 0, 0, 0, 0, 0,
+ 80, 80, 80, 0, 0, 0, 0, 80, 0, 80,
+ 0, 0, 0, 60, 61, 0, 0, 0, 0, 62,
+ 80, 63, 0, 0, 0, 0, 77, 77, 77, 0,
+ 0, 0, 0, 0, 0, 77, 0, 0, 0, 0,
+ 0, 0, 0, 78, 78, 78, 77, 0, 0, 0,
+ 0, 0, 78, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 78, 0, 0, 0, 75, 75, 75,
+ 0, 0, 0, 72, 72, 72, 60, 61, 0, 0,
+ 0, 0, 62, 0, 63, 0, 74, 75, 74, 0,
+ 0, 0, 0, 72, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 74, 0, 0, 0,
+ 39, 0, 92, 92, 0, 0, 0, 0, 92, 92,
+ 92, 92, 0, 60, 61, 0, 0, 0, 0, 62,
+ 0, 63, 60, 61, 55, 56, 0, 0, 62, 0,
+ 63, 0, 0, 0, 0, 0, 0, 72, 75, 0,
+ 0, 0, 0, 77, 78, 0, 81, 0, 83, 84,
+ 0, 87, 0, 0, 0, 0, 0, 0, 0, 96,
+ 97, 98, 99, 100, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 112, 0, 115,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 121, 122, 0, 0, 0, 0, 0, 123, 0,
+ 75, 0, 126, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 87, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 75, 0, 0, 0, 112, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 112,
+};
+short yycheck[] = { 40,
+ 59, 44, 94, 43, 45, 45, 59, 41, 59, 41,
+ 44, 59, 44, 59, 59, 1, 59, 40, 59, 138,
+ 262, 41, 40, 2, 265, 59, 267, 59, 262, 59,
+ 40, 40, 40, 91, 40, 40, 155, 45, 59, 59,
+ 44, 40, 59, 40, 278, 279, 280, 41, 40, 40,
+ 44, 285, 59, 40, 94, 40, 262, 94, 41, 59,
+ 44, 44, 44, 41, 41, 40, 125, 41, 91, 91,
+ 45, 59, 125, 91, 125, 59, 41, 125, 57, 125,
+ 125, 91, 123, 44, 70, 123, 59, 262, 41, 257,
+ 93, 277, 281, 91, 41, 59, 41, 40, 93, 41,
+ 41, 41, 45, 41, 59, 91, 41, 59, 41, 41,
+ 7, -1, 148, 92, -1, 123, 118, -1, -1, 154,
+ -1, -1, -1, -1, -1, -1, 43, -1, 45, -1,
+ 40, -1, -1, -1, -1, 45, 59, -1, -1, -1,
+ -1, 125, -1, -1, -1, -1, -1, -1, 123, -1,
+ 125, -1, 131, -1, -1, -1, -1, -1, -1, -1,
+ -1, 140, -1, -1, -1, -1, 40, -1, -1, -1,
+ 125, 45, 264, 152, -1, 161, -1, 94, -1, -1,
+ 123, -1, -1, -1, -1, 164, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 40, -1, -1, 257, -1,
+ 45, -1, 125, -1, 257, 40, 257, -1, -1, 257,
+ 45, 257, 257, 123, 257, 256, 257, -1, 277, 260,
+ 261, 262, 263, 257, 264, 257, 267, 257, 269, 270,
+ 271, 272, 273, 274, 275, 276, 257, 278, 279, 280,
+ 257, 282, 283, 284, 285, 286, 287, 288, 256, 123,
+ 257, 40, 260, 261, 262, 263, 45, 257, 93, 267,
+ 268, 269, 270, 271, 272, 273, 274, 275, 276, 257,
+ 278, 279, 280, 257, 282, 283, 284, 285, 286, 287,
+ 288, 256, -1, -1, 40, 260, 261, 262, 263, 45,
+ -1, -1, 267, 277, 269, 270, 271, 272, 273, 274,
+ 275, 276, 257, 278, 279, 280, -1, 282, 283, 284,
+ 285, 286, 287, 288, 257, -1, 40, 260, 261, 262,
+ 263, 45, 277, -1, 267, -1, 269, 270, 271, 272,
+ 273, 274, 275, 276, 257, 278, 279, 280, -1, 282,
+ 283, 284, 285, 286, 287, 288, 256, 264, -1, 266,
+ 260, 261, 262, 263, 277, -1, -1, 267, -1, 269,
+ 270, 271, 272, 273, 274, 275, 276, -1, 278, 279,
+ 280, -1, 282, 283, 284, 285, 286, 287, 288, -1,
+ -1, -1, -1, -1, -1, 40, 260, 261, 262, 263,
+ 45, -1, -1, 267, -1, 269, 270, 271, 272, 273,
+ 274, 275, 276, -1, 278, 279, 280, -1, 282, 283,
+ 284, 285, 286, 287, 288, 260, 261, 262, 263, -1,
+ -1, -1, 267, -1, -1, 260, 271, 262, 263, -1,
+ -1, 276, 267, 278, 279, 280, 271, 282, 93, -1,
+ 285, 276, -1, 278, 279, 280, -1, 282, -1, 41,
+ 285, 43, 44, 45, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 41, 59, 43, 44,
+ 45, 260, -1, 262, 263, -1, -1, -1, 267, -1,
+ 41, -1, 271, 44, 59, -1, -1, 276, -1, 278,
+ 279, 280, -1, 282, -1, -1, 285, -1, 59, -1,
+ -1, 93, 94, -1, 260, -1, 262, 263, -1, -1,
+ 41, 267, 43, 44, 45, 271, -1, -1, 93, 94,
+ 276, -1, 278, 279, 280, -1, 282, -1, 59, 285,
+ -1, -1, 93, 125, -1, -1, 260, -1, 262, 263,
+ -1, -1, -1, 267, -1, -1, -1, 271, 41, -1,
+ 125, 44, 276, -1, 278, 279, 280, -1, 282, -1,
+ -1, 285, 93, 94, 125, 41, 59, 43, 44, 45,
+ -1, -1, -1, -1, -1, -1, -1, 41, -1, 43,
+ 44, 45, 43, 59, 45, -1, -1, -1, -1, 41,
+ -1, 43, -1, 45, 125, 59, -1, -1, -1, -1,
+ 93, -1, 41, -1, 43, 260, 45, 262, 263, -1,
+ -1, -1, 267, -1, -1, -1, 271, 93, 94, -1,
+ 43, 276, 45, 278, 279, 280, -1, 282, -1, 93,
+ 285, -1, 125, 94, 41, -1, 43, 44, 45, -1,
+ -1, -1, 94, -1, -1, -1, 41, -1, 43, 125,
+ 45, -1, 59, -1, -1, 94, -1, -1, -1, -1,
+ 41, 125, 43, 44, 45, 257, 258, 259, -1, -1,
+ 93, 94, 264, 265, 266, 267, -1, 41, 59, 43,
+ 44, 45, 257, 258, 259, 277, 93, -1, -1, 264,
+ 265, 266, 267, -1, -1, 59, 257, 258, 259, 94,
+ -1, 41, 277, -1, 44, 266, -1, 41, -1, 41,
+ 44, 43, 93, 45, -1, -1, 277, -1, 125, 59,
+ 41, -1, -1, 44, -1, 59, 257, 258, 259, 93,
+ -1, -1, -1, 264, -1, 266, -1, -1, 59, -1,
+ -1, -1, -1, -1, 125, 41, 277, 43, 44, 45,
+ -1, -1, -1, 93, -1, -1, 41, -1, 43, 93,
+ 45, 125, 94, -1, 257, 258, 259, 43, -1, 45,
+ -1, -1, 93, 266, -1, -1, -1, -1, -1, -1,
+ -1, 257, 258, 259, 277, 125, -1, -1, 264, -1,
+ 266, 125, -1, 257, 258, 259, -1, 258, 94, -1,
+ 264, 277, 266, 264, 125, 266, 258, 259, -1, 94,
+ -1, -1, 264, 277, 266, -1, -1, -1, 94, 258,
+ 259, -1, -1, -1, -1, 264, -1, 266, -1, -1,
+ -1, -1, -1, -1, -1, 258, 259, -1, -1, -1,
+ -1, 264, -1, 266, -1, -1, -1, -1, -1, -1,
+ 257, 258, 259, -1, -1, -1, -1, 264, -1, 266,
+ -1, -1, -1, 258, 259, -1, -1, -1, -1, 264,
+ 277, 266, -1, -1, -1, -1, 257, 258, 259, -1,
+ -1, -1, -1, -1, -1, 266, -1, -1, -1, -1,
+ -1, -1, -1, 257, 258, 259, 277, -1, -1, -1,
+ -1, -1, 266, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 277, -1, -1, -1, 257, 258, 259,
+ -1, -1, -1, 257, 258, 259, 258, 259, -1, -1,
+ -1, -1, 264, -1, 266, -1, 257, 277, 259, -1,
+ -1, -1, -1, 277, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 277, -1, -1, -1,
+ 3, -1, 258, 259, -1, -1, -1, -1, 264, 265,
+ 266, 267, -1, 258, 259, -1, -1, -1, -1, 264,
+ -1, 266, 258, 259, 27, 28, -1, -1, 264, -1,
+ 266, -1, -1, -1, -1, -1, -1, 40, 41, -1,
+ -1, -1, -1, 46, 47, -1, 49, -1, 51, 52,
+ -1, 54, -1, -1, -1, -1, -1, -1, -1, 62,
+ 63, 64, 65, 66, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 80, -1, 82,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 94, 95, -1, -1, -1, -1, -1, 101, -1,
+ 103, -1, 105, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 118, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 134, -1, -1, -1, 138, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 155,
+};
+#define YYFINAL 1
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYMAXTOKEN 289
+#if YYDEBUG
+char *yyname[] = {
+"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,"'('","')'",0,"'+'","','","'-'",0,0,0,0,0,0,0,0,0,0,0,0,0,"';'",0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'['",0,"']'","'^'",0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'{'",0,"'}'",0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,"NEWLINE","AND","OR","NOT","STRING","NAME","NUMBER","MUL_OP",
+"ASSIGN_OP","REL_OP","INCR_DECR","Define","Break","Quit","Length","Return",
+"For","If","While","Sqrt","Else","Scale","Ibase","Obase","Auto","Read",
+"Warranty","Halt","Last","Continue","Print","Limits","UNARY_MINUS",
+};
+char *yyrule[] = {
+"$accept : program",
+"program :",
+"program : program input_item",
+"input_item : semicolon_list NEWLINE",
+"input_item : function",
+"input_item : error NEWLINE",
+"semicolon_list :",
+"semicolon_list : statement_or_error",
+"semicolon_list : semicolon_list ';' statement_or_error",
+"semicolon_list : semicolon_list ';'",
+"statement_list :",
+"statement_list : statement_or_error",
+"statement_list : statement_list NEWLINE",
+"statement_list : statement_list NEWLINE statement_or_error",
+"statement_list : statement_list ';'",
+"statement_list : statement_list ';' statement",
+"statement_or_error : statement",
+"statement_or_error : error statement",
+"statement : Warranty",
+"statement : Limits",
+"statement : expression",
+"statement : STRING",
+"statement : Break",
+"statement : Continue",
+"statement : Quit",
+"statement : Halt",
+"statement : Return",
+"statement : Return '(' return_expression ')'",
+"$$1 :",
+"$$2 :",
+"$$3 :",
+"$$4 :",
+"statement : For $$1 '(' opt_expression ';' $$2 opt_expression ';' $$3 opt_expression ')' $$4 statement",
+"$$5 :",
+"statement : If '(' expression ')' $$5 statement opt_else",
+"$$6 :",
+"$$7 :",
+"statement : While $$6 '(' expression $$7 ')' statement",
+"statement : '{' statement_list '}'",
+"$$8 :",
+"statement : Print $$8 print_list",
+"print_list : print_element",
+"print_list : print_element ',' print_list",
+"print_element : STRING",
+"print_element : expression",
+"opt_else :",
+"$$9 :",
+"opt_else : Else $$9 statement",
+"$$10 :",
+"function : Define NAME '(' opt_parameter_list ')' '{' NEWLINE opt_auto_define_list $$10 statement_list NEWLINE '}'",
+"opt_parameter_list :",
+"opt_parameter_list : define_list",
+"opt_auto_define_list :",
+"opt_auto_define_list : Auto define_list NEWLINE",
+"opt_auto_define_list : Auto define_list ';'",
+"define_list : NAME",
+"define_list : NAME '[' ']'",
+"define_list : define_list ',' NAME",
+"define_list : define_list ',' NAME '[' ']'",
+"opt_argument_list :",
+"opt_argument_list : argument_list",
+"argument_list : expression",
+"argument_list : NAME '[' ']'",
+"argument_list : argument_list ',' expression",
+"argument_list : argument_list ',' NAME '[' ']'",
+"opt_expression :",
+"opt_expression : expression",
+"return_expression :",
+"return_expression : expression",
+"$$11 :",
+"expression : named_expression ASSIGN_OP $$11 expression",
+"$$12 :",
+"expression : expression AND $$12 expression",
+"$$13 :",
+"expression : expression OR $$13 expression",
+"expression : NOT expression",
+"expression : expression REL_OP expression",
+"expression : expression '+' expression",
+"expression : expression '-' expression",
+"expression : expression MUL_OP expression",
+"expression : expression '^' expression",
+"expression : '-' expression",
+"expression : named_expression",
+"expression : NUMBER",
+"expression : '(' expression ')'",
+"expression : NAME '(' opt_argument_list ')'",
+"expression : INCR_DECR named_expression",
+"expression : named_expression INCR_DECR",
+"expression : Length '(' expression ')'",
+"expression : Sqrt '(' expression ')'",
+"expression : Scale '(' expression ')'",
+"expression : Read '(' ')'",
+"named_expression : NAME",
+"named_expression : NAME '[' expression ']'",
+"named_expression : Ibase",
+"named_expression : Obase",
+"named_expression : Scale",
+"named_expression : Last",
+};
+#endif
+#define yyclearin (yychar=(-1))
+#define yyerrok (yyerrflag=0)
+#ifdef YYSTACKSIZE
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH YYSTACKSIZE
+#endif
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 500
+#define YYMAXDEPTH 500
+#endif
+#endif
+int yydebug;
+int yynerrs;
+int yyerrflag;
+int yychar;
+short *yyssp;
+YYSTYPE *yyvsp;
+YYSTYPE yyval;
+YYSTYPE yylval;
+short yyss[YYSTACKSIZE];
+YYSTYPE yyvs[YYSTACKSIZE];
+#define yystacksize YYSTACKSIZE
+#define YYABORT goto yyabort
+#define YYACCEPT goto yyaccept
+#define YYERROR goto yyerrlab
+int
+yyparse()
+{
+ register int yym, yyn, yystate;
+#if YYDEBUG
+ register char *yys;
+ extern char *getenv();
+
+ if (yys = getenv("YYDEBUG"))
+ {
+ yyn = *yys;
+ if (yyn >= '0' && yyn <= '9')
+ yydebug = yyn - '0';
+ }
+#endif
+
+ yynerrs = 0;
+ yyerrflag = 0;
+ yychar = (-1);
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+ *yyssp = yystate = 0;
+
+yyloop:
+ if (yyn = yydefred[yystate]) goto yyreduce;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("yydebug: state %d, reading %d (%s)\n", yystate,
+ yychar, yys);
+ }
+#endif
+ }
+ if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("yydebug: state %d, shifting to state %d\n",
+ yystate, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ yychar = (-1);
+ if (yyerrflag > 0) --yyerrflag;
+ goto yyloop;
+ }
+ if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+ yyn = yytable[yyn];
+ goto yyreduce;
+ }
+ if (yyerrflag) goto yyinrecovery;
+#ifdef lint
+ goto yynewerror;
+#endif
+yynewerror:
+ yyerror("syntax error");
+#ifdef lint
+ goto yyerrlab;
+#endif
+yyerrlab:
+ ++yynerrs;
+yyinrecovery:
+ if (yyerrflag < 3)
+ {
+ yyerrflag = 3;
+ for (;;)
+ {
+ if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("yydebug: state %d, error recovery shifting\
+ to state %d\n", *yyssp, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ goto yyloop;
+ }
+ else
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("yydebug: error recovery discarding state %d\n",
+ *yyssp);
+#endif
+ if (yyssp <= yyss) goto yyabort;
+ --yyssp;
+ --yyvsp;
+ }
+ }
+ }
+ else
+ {
+ if (yychar == 0) goto yyabort;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("yydebug: state %d, error recovery discards token %d (%s)\n",
+ yystate, yychar, yys);
+ }
+#endif
+ yychar = (-1);
+ goto yyloop;
+ }
+yyreduce:
+#if YYDEBUG
+ if (yydebug)
+ printf("yydebug: state %d, reducing by rule %d (%s)\n",
+ yystate, yyn, yyrule[yyn]);
+#endif
+ yym = yylen[yyn];
+ yyval = yyvsp[1-yym];
+ switch (yyn)
+ {
+case 1:
+#line 106 "bc.y"
+{
+ yyval.i_value = 0;
+ if (interactive)
+ {
+ printf ("%s\n", BC_VERSION);
+ welcome ();
+ }
+ }
+break;
+case 3:
+#line 117 "bc.y"
+{ run_code (); }
+break;
+case 4:
+#line 119 "bc.y"
+{ run_code (); }
+break;
+case 5:
+#line 121 "bc.y"
+{
+ yyerrok;
+ init_gen ();
+ }
+break;
+case 6:
+#line 127 "bc.y"
+{ yyval.i_value = 0; }
+break;
+case 10:
+#line 133 "bc.y"
+{ yyval.i_value = 0; }
+break;
+case 17:
+#line 142 "bc.y"
+{ yyval.i_value = yyvsp[0].i_value; }
+break;
+case 18:
+#line 145 "bc.y"
+{ warranty (""); }
+break;
+case 19:
+#line 147 "bc.y"
+{ limits (); }
+break;
+case 20:
+#line 149 "bc.y"
+{
+ if (yyvsp[0].i_value & 2)
+ warn ("comparison in expression");
+ if (yyvsp[0].i_value & 1)
+ generate ("W");
+ else
+ generate ("p");
+ }
+break;
+case 21:
+#line 158 "bc.y"
+{
+ yyval.i_value = 0;
+ generate ("w");
+ generate (yyvsp[0].s_value);
+ free (yyvsp[0].s_value);
+ }
+break;
+case 22:
+#line 165 "bc.y"
+{
+ if (break_label == 0)
+ yyerror ("Break outside a for/while");
+ else
+ {
+ sprintf (genstr, "J%1d:", break_label);
+ generate (genstr);
+ }
+ }
+break;
+case 23:
+#line 175 "bc.y"
+{
+ warn ("Continue statement");
+ if (continue_label == 0)
+ yyerror ("Continue outside a for");
+ else
+ {
+ sprintf (genstr, "J%1d:", continue_label);
+ generate (genstr);
+ }
+ }
+break;
+case 24:
+#line 186 "bc.y"
+{ exit (0); }
+break;
+case 25:
+#line 188 "bc.y"
+{ generate ("h"); }
+break;
+case 26:
+#line 190 "bc.y"
+{ generate ("0R"); }
+break;
+case 27:
+#line 192 "bc.y"
+{ generate ("R"); }
+break;
+case 28:
+#line 194 "bc.y"
+{
+ yyvsp[0].i_value = break_label;
+ break_label = next_label++;
+ }
+break;
+case 29:
+#line 199 "bc.y"
+{
+ if (yyvsp[-1].i_value > 1)
+ warn ("Comparison in first for expression");
+ yyvsp[-1].i_value = next_label++;
+ if (yyvsp[-1].i_value < 0)
+ sprintf (genstr, "N%1d:", yyvsp[-1].i_value);
+ else
+ sprintf (genstr, "pN%1d:", yyvsp[-1].i_value);
+ generate (genstr);
+ }
+break;
+case 30:
+#line 210 "bc.y"
+{
+ if (yyvsp[-1].i_value < 0) generate ("1");
+ yyvsp[-1].i_value = next_label++;
+ sprintf (genstr, "B%1d:J%1d:", yyvsp[-1].i_value, break_label);
+ generate (genstr);
+ yyval.i_value = continue_label;
+ continue_label = next_label++;
+ sprintf (genstr, "N%1d:", continue_label);
+ generate (genstr);
+ }
+break;
+case 31:
+#line 221 "bc.y"
+{
+ if (yyvsp[-1].i_value > 1)
+ warn ("Comparison in third for expression");
+ if (yyvsp[-1].i_value < 0)
+ sprintf (genstr, "J%1d:N%1d:", yyvsp[-7].i_value, yyvsp[-4].i_value);
+ else
+ sprintf (genstr, "pJ%1d:N%1d:", yyvsp[-7].i_value, yyvsp[-4].i_value);
+ generate (genstr);
+ }
+break;
+case 32:
+#line 231 "bc.y"
+{
+ sprintf (genstr, "J%1d:N%1d:",
+ continue_label, break_label);
+ generate (genstr);
+ break_label = yyvsp[-12].i_value;
+ continue_label = yyvsp[-4].i_value;
+ }
+break;
+case 33:
+#line 239 "bc.y"
+{
+ yyvsp[-1].i_value = if_label;
+ if_label = next_label++;
+ sprintf (genstr, "Z%1d:", if_label);
+ generate (genstr);
+ }
+break;
+case 34:
+#line 246 "bc.y"
+{
+ sprintf (genstr, "N%1d:", if_label);
+ generate (genstr);
+ if_label = yyvsp[-4].i_value;
+ }
+break;
+case 35:
+#line 252 "bc.y"
+{
+ yyvsp[0].i_value = next_label++;
+ sprintf (genstr, "N%1d:", yyvsp[0].i_value);
+ generate (genstr);
+ }
+break;
+case 36:
+#line 258 "bc.y"
+{
+ yyvsp[0].i_value = break_label;
+ break_label = next_label++;
+ sprintf (genstr, "Z%1d:", break_label);
+ generate (genstr);
+ }
+break;
+case 37:
+#line 265 "bc.y"
+{
+ sprintf (genstr, "J%1d:N%1d:", yyvsp[-6].i_value, break_label);
+ generate (genstr);
+ break_label = yyvsp[-3].i_value;
+ }
+break;
+case 38:
+#line 271 "bc.y"
+{ yyval.i_value = 0; }
+break;
+case 39:
+#line 273 "bc.y"
+{ warn ("print statement"); }
+break;
+case 43:
+#line 280 "bc.y"
+{
+ generate ("O");
+ generate (yyvsp[0].s_value);
+ free (yyvsp[0].s_value);
+ }
+break;
+case 44:
+#line 286 "bc.y"
+{ generate ("P"); }
+break;
+case 46:
+#line 290 "bc.y"
+{
+ warn ("else clause in if statement");
+ yyvsp[0].i_value = next_label++;
+ sprintf (genstr, "J%d:N%1d:", yyvsp[0].i_value, if_label);
+ generate (genstr);
+ if_label = yyvsp[0].i_value;
+ }
+break;
+case 48:
+#line 300 "bc.y"
+{
+ /* Check auto list against parameter list? */
+ check_params (yyvsp[-4].a_value,yyvsp[0].a_value);
+ sprintf (genstr, "F%d,%s.%s[", lookup(yyvsp[-6].s_value,FUNCT),
+ arg_str (yyvsp[-4].a_value,TRUE), arg_str (yyvsp[0].a_value,TRUE));
+ generate (genstr);
+ free_args (yyvsp[-4].a_value);
+ free_args (yyvsp[0].a_value);
+ yyvsp[-7].i_value = next_label;
+ next_label = 0;
+ }
+break;
+case 49:
+#line 312 "bc.y"
+{
+ generate ("0R]");
+ next_label = yyvsp[-11].i_value;
+ }
+break;
+case 50:
+#line 318 "bc.y"
+{ yyval.a_value = NULL; }
+break;
+case 52:
+#line 322 "bc.y"
+{ yyval.a_value = NULL; }
+break;
+case 53:
+#line 324 "bc.y"
+{ yyval.a_value = yyvsp[-1].a_value; }
+break;
+case 54:
+#line 326 "bc.y"
+{ yyval.a_value = yyvsp[-1].a_value; }
+break;
+case 55:
+#line 329 "bc.y"
+{ yyval.a_value = nextarg (NULL, lookup (yyvsp[0].s_value,SIMPLE)); }
+break;
+case 56:
+#line 331 "bc.y"
+{ yyval.a_value = nextarg (NULL, lookup (yyvsp[-2].s_value,ARRAY)); }
+break;
+case 57:
+#line 333 "bc.y"
+{ yyval.a_value = nextarg (yyvsp[-2].a_value, lookup (yyvsp[0].s_value,SIMPLE)); }
+break;
+case 58:
+#line 335 "bc.y"
+{ yyval.a_value = nextarg (yyvsp[-4].a_value, lookup (yyvsp[-2].s_value,ARRAY)); }
+break;
+case 59:
+#line 338 "bc.y"
+{ yyval.a_value = NULL; }
+break;
+case 61:
+#line 342 "bc.y"
+{
+ if (yyvsp[0].i_value > 1) warn ("comparison in argument");
+ yyval.a_value = nextarg (NULL,0);
+ }
+break;
+case 62:
+#line 347 "bc.y"
+{
+ sprintf (genstr, "K%d:", -lookup (yyvsp[-2].s_value,ARRAY));
+ generate (genstr);
+ yyval.a_value = nextarg (NULL,1);
+ }
+break;
+case 63:
+#line 353 "bc.y"
+{
+ if (yyvsp[0].i_value > 1) warn ("comparison in argument");
+ yyval.a_value = nextarg (yyvsp[-2].a_value,0);
+ }
+break;
+case 64:
+#line 358 "bc.y"
+{
+ sprintf (genstr, "K%d:", -lookup (yyvsp[-2].s_value,ARRAY));
+ generate (genstr);
+ yyval.a_value = nextarg (yyvsp[-4].a_value,1);
+ }
+break;
+case 65:
+#line 365 "bc.y"
+{
+ yyval.i_value = -1;
+ warn ("Missing expression in for statement");
+ }
+break;
+case 67:
+#line 372 "bc.y"
+{
+ yyval.i_value = 0;
+ generate ("0");
+ }
+break;
+case 68:
+#line 377 "bc.y"
+{
+ if (yyvsp[0].i_value > 1)
+ warn ("comparison in return expresion");
+ }
+break;
+case 69:
+#line 383 "bc.y"
+{
+ if (yyvsp[0].c_value != '=')
+ {
+ if (yyvsp[-1].i_value < 0)
+ sprintf (genstr, "DL%d:", -yyvsp[-1].i_value);
+ else
+ sprintf (genstr, "l%d:", yyvsp[-1].i_value);
+ generate (genstr);
+ }
+ }
+break;
+case 70:
+#line 394 "bc.y"
+{
+ if (yyvsp[0].i_value > 1) warn("comparison in assignment");
+ if (yyvsp[-2].c_value != '=')
+ {
+ sprintf (genstr, "%c", yyvsp[-2].c_value);
+ generate (genstr);
+ }
+ if (yyvsp[-3].i_value < 0)
+ sprintf (genstr, "S%d:", -yyvsp[-3].i_value);
+ else
+ sprintf (genstr, "s%d:", yyvsp[-3].i_value);
+ generate (genstr);
+ yyval.i_value = 0;
+ }
+break;
+case 71:
+#line 410 "bc.y"
+{
+ warn("&& operator");
+ yyvsp[0].i_value = next_label++;
+ sprintf (genstr, "DZ%d:p", yyvsp[0].i_value);
+ generate (genstr);
+ }
+break;
+case 72:
+#line 417 "bc.y"
+{
+ sprintf (genstr, "DZ%d:p1N%d:", yyvsp[-2].i_value, yyvsp[-2].i_value);
+ generate (genstr);
+ yyval.i_value = yyvsp[-3].i_value | yyvsp[0].i_value;
+ }
+break;
+case 73:
+#line 423 "bc.y"
+{
+ warn("|| operator");
+ yyvsp[0].i_value = next_label++;
+ sprintf (genstr, "B%d:", yyvsp[0].i_value);
+ generate (genstr);
+ }
+break;
+case 74:
+#line 430 "bc.y"
+{
+ int tmplab;
+ tmplab = next_label++;
+ sprintf (genstr, "B%d:0J%d:N%d:1N%d:",
+ yyvsp[-2].i_value, tmplab, yyvsp[-2].i_value, tmplab);
+ generate (genstr);
+ yyval.i_value = yyvsp[-3].i_value | yyvsp[0].i_value;
+ }
+break;
+case 75:
+#line 439 "bc.y"
+{
+ yyval.i_value = yyvsp[0].i_value;
+ warn("! operator");
+ generate ("!");
+ }
+break;
+case 76:
+#line 445 "bc.y"
+{
+ yyval.i_value = 3;
+ switch (*(yyvsp[-1].s_value))
+ {
+ case '=':
+ generate ("=");
+ break;
+
+ case '!':
+ generate ("#");
+ break;
+
+ case '<':
+ if (yyvsp[-1].s_value[1] == '=')
+ generate ("{");
+ else
+ generate ("<");
+ break;
+
+ case '>':
+ if (yyvsp[-1].s_value[1] == '=')
+ generate ("}");
+ else
+ generate (">");
+ break;
+ }
+ }
+break;
+case 77:
+#line 473 "bc.y"
+{
+ generate ("+");
+ yyval.i_value = yyvsp[-2].i_value | yyvsp[0].i_value;
+ }
+break;
+case 78:
+#line 478 "bc.y"
+{
+ generate ("-");
+ yyval.i_value = yyvsp[-2].i_value | yyvsp[0].i_value;
+ }
+break;
+case 79:
+#line 483 "bc.y"
+{
+ genstr[0] = yyvsp[-1].c_value;
+ genstr[1] = 0;
+ generate (genstr);
+ yyval.i_value = yyvsp[-2].i_value | yyvsp[0].i_value;
+ }
+break;
+case 80:
+#line 490 "bc.y"
+{
+ generate ("^");
+ yyval.i_value = yyvsp[-2].i_value | yyvsp[0].i_value;
+ }
+break;
+case 81:
+#line 495 "bc.y"
+{
+ generate ("n");
+ yyval.i_value = yyvsp[0].i_value;
+ }
+break;
+case 82:
+#line 500 "bc.y"
+{
+ yyval.i_value = 1;
+ if (yyvsp[0].i_value < 0)
+ sprintf (genstr, "L%d:", -yyvsp[0].i_value);
+ else
+ sprintf (genstr, "l%d:", yyvsp[0].i_value);
+ generate (genstr);
+ }
+break;
+case 83:
+#line 509 "bc.y"
+{
+ int len = strlen(yyvsp[0].s_value);
+ yyval.i_value = 1;
+ if (len == 1 && *yyvsp[0].s_value == '0')
+ generate ("0");
+ else if (len == 1 && *yyvsp[0].s_value == '1')
+ generate ("1");
+ else
+ {
+ generate ("K");
+ generate (yyvsp[0].s_value);
+ generate (":");
+ }
+ free (yyvsp[0].s_value);
+ }
+break;
+case 84:
+#line 525 "bc.y"
+{ yyval.i_value = yyvsp[-1].i_value | 1; }
+break;
+case 85:
+#line 527 "bc.y"
+{
+ yyval.i_value = 1;
+ if (yyvsp[-1].a_value != NULL)
+ {
+ sprintf (genstr, "C%d,%s:",
+ lookup (yyvsp[-3].s_value,FUNCT),
+ arg_str (yyvsp[-1].a_value,FALSE));
+ free_args (yyvsp[-1].a_value);
+ }
+ else
+ {
+ sprintf (genstr, "C%d:", lookup (yyvsp[-3].s_value,FUNCT));
+ }
+ generate (genstr);
+ }
+break;
+case 86:
+#line 543 "bc.y"
+{
+ yyval.i_value = 1;
+ if (yyvsp[0].i_value < 0)
+ {
+ if (yyvsp[-1].c_value == '+')
+ sprintf (genstr, "DA%d:L%d:", -yyvsp[0].i_value, -yyvsp[0].i_value);
+ else
+ sprintf (genstr, "DM%d:L%d:", -yyvsp[0].i_value, -yyvsp[0].i_value);
+ }
+ else
+ {
+ if (yyvsp[-1].c_value == '+')
+ sprintf (genstr, "i%d:l%d:", yyvsp[0].i_value, yyvsp[0].i_value);
+ else
+ sprintf (genstr, "d%d:l%d:", yyvsp[0].i_value, yyvsp[0].i_value);
+ }
+ generate (genstr);
+ }
+break;
+case 87:
+#line 562 "bc.y"
+{
+ yyval.i_value = 1;
+ if (yyvsp[-1].i_value < 0)
+ {
+ sprintf (genstr, "DL%d:x", -yyvsp[-1].i_value);
+ generate (genstr);
+ if (yyvsp[0].c_value == '+')
+ sprintf (genstr, "A%d:", -yyvsp[-1].i_value);
+ else
+ sprintf (genstr, "M%d:", -yyvsp[-1].i_value);
+ }
+ else
+ {
+ sprintf (genstr, "l%d:", yyvsp[-1].i_value);
+ generate (genstr);
+ if (yyvsp[0].c_value == '+')
+ sprintf (genstr, "i%d:", yyvsp[-1].i_value);
+ else
+ sprintf (genstr, "d%d:", yyvsp[-1].i_value);
+ }
+ generate (genstr);
+ }
+break;
+case 88:
+#line 585 "bc.y"
+{ generate ("cL"); yyval.i_value = 1;}
+break;
+case 89:
+#line 587 "bc.y"
+{ generate ("cR"); yyval.i_value = 1;}
+break;
+case 90:
+#line 589 "bc.y"
+{ generate ("cS"); yyval.i_value = 1;}
+break;
+case 91:
+#line 591 "bc.y"
+{
+ warn ("read function");
+ generate ("cI"); yyval.i_value = 1;
+ }
+break;
+case 92:
+#line 597 "bc.y"
+{ yyval.i_value = lookup(yyvsp[0].s_value,SIMPLE); }
+break;
+case 93:
+#line 599 "bc.y"
+{
+ if (yyvsp[-1].i_value > 1) warn("comparison in subscript");
+ yyval.i_value = lookup(yyvsp[-3].s_value,ARRAY);
+ }
+break;
+case 94:
+#line 604 "bc.y"
+{ yyval.i_value = 0; }
+break;
+case 95:
+#line 606 "bc.y"
+{ yyval.i_value = 1; }
+break;
+case 96:
+#line 608 "bc.y"
+{ yyval.i_value = 2; }
+break;
+case 97:
+#line 610 "bc.y"
+{ yyval.i_value = 3; }
+break;
+#line 1314 "y.tab.c"
+ }
+ yyssp -= yym;
+ yystate = *yyssp;
+ yyvsp -= yym;
+ yym = yylhs[yyn];
+ if (yystate == 0 && yym == 0)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("yydebug: after reduction, shifting from state 0 to\
+ state %d\n", YYFINAL);
+#endif
+ yystate = YYFINAL;
+ *++yyssp = YYFINAL;
+ *++yyvsp = yyval;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("yydebug: state %d, reading %d (%s)\n",
+ YYFINAL, yychar, yys);
+ }
+#endif
+ }
+ if (yychar == 0) goto yyaccept;
+ goto yyloop;
+ }
+ if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yystate)
+ yystate = yytable[yyn];
+ else
+ yystate = yydgoto[yym];
+#if YYDEBUG
+ if (yydebug)
+ printf("yydebug: after reduction, shifting from state %d \
+to state %d\n", *yyssp, yystate);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate;
+ *++yyvsp = yyval;
+ goto yyloop;
+yyoverflow:
+ yyerror("yacc stack overflow");
+yyabort:
+ return (1);
+yyaccept:
+ return (0);
+}
diff --git a/gnu/usr.bin/bc/bcdefs.h b/gnu/usr.bin/bc/bcdefs.h
new file mode 100644
index 0000000..a9d2176
--- /dev/null
+++ b/gnu/usr.bin/bc/bcdefs.h
@@ -0,0 +1,154 @@
+/* bcdefs.h: The single file to include all constants and type definitions. */
+
+/* This file is part of bc written for MINIX.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License , or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ You may contact the author by:
+ e-mail: phil@cs.wwu.edu
+ us-mail: Philip A. Nelson
+ Computer Science Department, 9062
+ Western Washington University
+ Bellingham, WA 98226-9062
+
+*************************************************************************/
+
+/* Include the configuration file. */
+#include "config.h"
+
+/* Standard includes for all files. */
+#include <stdio.h>
+#include <sys/types.h>
+#include <ctype.h>
+#ifdef STRINGS_H
+#include <strings.h>
+#else
+#include <string.h>
+#endif
+#ifndef NO_LIMITS
+#include <limits.h>
+#endif
+
+/* Include the other definitions. */
+#include "const.h"
+#include "number.h"
+
+
+/* These definitions define all the structures used in
+ code and data storage. This includes the representation of
+ labels. The "guiding" principle is to make structures that
+ take a minimum of space when unused but can be built to contain
+ the full structures. */
+
+/* Labels are first. Labels are generated sequentially in functions
+ and full code. They just "point" to a single bye in the code. The
+ "address" is the byte number. The byte number is used to get an
+ actual character pointer. */
+
+typedef struct bc_label_group
+ {
+ long l_adrs [ BC_LABEL_GROUP ];
+ struct bc_label_group *l_next;
+ } bc_label_group;
+
+
+/* Each function has its own code segments and labels. There can be
+ no jumps between functions so labels are unique to a function. */
+
+typedef struct arg_list
+ {
+ int av_name;
+ struct arg_list *next;
+ } arg_list;
+
+typedef struct
+ {
+ char f_defined; /* Is this function defined yet. */
+ char *f_body[BC_MAX_SEGS];
+ int f_code_size;
+ bc_label_group *f_label;
+ arg_list *f_params;
+ arg_list *f_autos;
+ } bc_function;
+
+/* Code addresses. */
+typedef struct {
+ int pc_func;
+ int pc_addr;
+ } program_counter;
+
+
+/* Variables are "pushable" (auto) and thus we need a stack mechanism.
+ This is built into the variable record. */
+
+typedef struct bc_var
+ {
+ bc_num v_value;
+ struct bc_var *v_next;
+ } bc_var;
+
+
+/* bc arrays can also be "auto" variables and thus need the same
+ kind of stacking mechanisms. */
+
+typedef struct bc_array_node
+ {
+ union
+ {
+ bc_num n_num [NODE_SIZE];
+ struct bc_array_node *n_down [NODE_SIZE];
+ } n_items;
+ } bc_array_node;
+
+typedef struct bc_array
+ {
+ bc_array_node *a_tree;
+ short a_depth;
+ } bc_array;
+
+typedef struct bc_var_array
+ {
+ bc_array *a_value;
+ char a_param;
+ struct bc_var_array *a_next;
+ } bc_var_array;
+
+
+/* For the stacks, execution and function, we need records to allow
+ for arbitrary size. */
+
+typedef struct estack_rec {
+ bc_num s_num;
+ struct estack_rec *s_next;
+} estack_rec;
+
+typedef struct fstack_rec {
+ int s_val;
+ struct fstack_rec *s_next;
+} fstack_rec;
+
+
+/* The following are for the name tree. */
+
+typedef struct id_rec {
+ char *id; /* The program name. */
+ /* A name == 0 => nothing assigned yet. */
+ int a_name; /* The array variable name (number). */
+ int f_name; /* The function name (number). */
+ int v_name; /* The variable name (number). */
+ short balance; /* For the balanced tree. */
+ struct id_rec *left, *right; /* Tree pointers. */
+} id_rec;
diff --git a/gnu/usr.bin/bc/config.h b/gnu/usr.bin/bc/config.h
new file mode 100644
index 0000000..a9bd0be
--- /dev/null
+++ b/gnu/usr.bin/bc/config.h
@@ -0,0 +1,4 @@
+/* config.h */
+#ifndef __STDC__
+#define VARARGS
+#endif
diff --git a/gnu/usr.bin/bc/const.h b/gnu/usr.bin/bc/const.h
new file mode 100644
index 0000000..ea91eaf
--- /dev/null
+++ b/gnu/usr.bin/bc/const.h
@@ -0,0 +1,87 @@
+/* const.h: Constants for bc. */
+
+/* This file is part of bc written for MINIX.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License , or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ You may contact the author by:
+ e-mail: phil@cs.wwu.edu
+ us-mail: Philip A. Nelson
+ Computer Science Department, 9062
+ Western Washington University
+ Bellingham, WA 98226-9062
+
+*************************************************************************/
+
+
+/* Define INT_MAX and LONG_MAX if not defined. Assuming 32 bits... */
+
+#ifdef NO_LIMITS
+#define INT_MAX 0x7FFFFFFF
+#define LONG_MAX 0x7FFFFFFF
+#endif
+
+
+/* Define constants in some reasonable size. The next 4 constants are
+ POSIX constants. */
+#if !defined(_POSIX_SOURCE)
+#define BC_BASE_MAX INT_MAX
+#define BC_SCALE_MAX INT_MAX
+#define BC_STRING_MAX INT_MAX
+
+/* Definitions for arrays. */
+
+#define BC_DIM_MAX 65535 /* this should be NODE_SIZE^NODE_DEPTH-1 */
+#endif
+
+#define NODE_SIZE 16 /* Must be a power of 2. */
+#define NODE_MASK 0xf /* Must be NODE_SIZE-1. */
+#define NODE_SHIFT 4 /* Number of 1 bits in NODE_MASK. */
+#define NODE_DEPTH 4
+
+
+/* Other BC limits defined but not part of POSIX. */
+
+#define BC_LABEL_GROUP 64
+#define BC_LABEL_LOG 6
+#define BC_MAX_SEGS 16 /* Code segments. */
+#define BC_SEG_SIZE 1024
+#define BC_SEG_LOG 10
+
+/* Maximum number of variables, arrays and functions and the
+ allocation increment for the dynamic arrays. */
+
+#define MAX_STORE 32767
+#define STORE_INCR 32
+
+/* Other interesting constants. */
+
+#define FALSE 0
+#define TRUE 1
+#define SIMPLE 0
+#define ARRAY 1
+#define FUNCT 2
+#define EXTERN extern
+#ifdef __STDC__
+#define CONST const
+#define VOID void
+#else
+#define CONST
+#define VOID
+#endif
+
+/* Include the version definition. */
+#include "version.h"
diff --git a/gnu/usr.bin/bc/execute.c b/gnu/usr.bin/bc/execute.c
new file mode 100644
index 0000000..a7bc9c7
--- /dev/null
+++ b/gnu/usr.bin/bc/execute.c
@@ -0,0 +1,783 @@
+/* execute.c - run a bc program. */
+
+/* This file is part of bc written for MINIX.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License , or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ You may contact the author by:
+ e-mail: phil@cs.wwu.edu
+ us-mail: Philip A. Nelson
+ Computer Science Department, 9062
+ Western Washington University
+ Bellingham, WA 98226-9062
+
+*************************************************************************/
+
+#include "bcdefs.h"
+#include <signal.h>
+#include "global.h"
+#include "proto.h"
+
+
+/* The SIGINT interrupt handling routine. */
+
+int had_sigint;
+
+void
+stop_execution (sig)
+ int sig;
+{
+ had_sigint = TRUE;
+ printf ("\n");
+ rt_error ("interrupted execution");
+}
+
+
+/* Get the current byte and advance the PC counter. */
+
+unsigned char
+byte (pc)
+ program_counter *pc;
+{
+ int seg, offset;
+
+ seg = pc->pc_addr >> BC_SEG_LOG;
+ offset = pc->pc_addr++ % BC_SEG_SIZE;
+ return (functions[pc->pc_func].f_body[seg][offset]);
+}
+
+
+/* The routine that actually runs the machine. */
+
+void
+execute ()
+{
+ int label_num, l_gp, l_off;
+ bc_label_group *gp;
+
+ char inst, ch;
+ int new_func;
+ int var_name;
+
+ int const_base;
+
+ bc_num temp_num;
+ arg_list *auto_list;
+
+ /* Initialize this run... */
+ pc.pc_func = 0;
+ pc.pc_addr = 0;
+ runtime_error = FALSE;
+ init_num (&temp_num);
+
+ /* Set up the interrupt mechanism for an interactive session. */
+ if (interactive)
+ {
+ signal (SIGINT, stop_execution);
+ had_sigint = FALSE;
+ }
+
+ while (pc.pc_addr < functions[pc.pc_func].f_code_size && !runtime_error)
+ {
+ inst = byte(&pc);
+
+#if DEBUG > 3
+ { /* Print out address and the stack before each instruction.*/
+ int depth; estack_rec *temp = ex_stack;
+
+ printf ("func=%d addr=%d inst=%c\n",pc.pc_func, pc.pc_addr, inst);
+ if (temp == NULL) printf ("empty stack.\n", inst);
+ else
+ {
+ depth = 1;
+ while (temp != NULL)
+ {
+ printf (" %d = ", depth);
+ out_num (temp->s_num, 10, out_char);
+ depth++;
+ temp = temp->s_next;
+ }
+ }
+ }
+#endif
+
+ switch ( inst )
+ {
+
+ case 'A' : /* increment array variable (Add one). */
+ var_name = byte(&pc);
+ if ((var_name & 0x80) != 0)
+ var_name = ((var_name << 8) & 0x7f) + byte(&pc);
+ incr_array (var_name);
+ break;
+
+ case 'B' : /* Branch to a label if TOS != 0. Remove value on TOS. */
+ case 'Z' : /* Branch to a label if TOS == 0. Remove value on TOS. */
+ c_code = !is_zero (ex_stack->s_num);
+ pop ();
+ case 'J' : /* Jump to a label. */
+ label_num = byte(&pc); /* Low order bits first. */
+ label_num += byte(&pc) << 8;
+ if (inst == 'J' || (inst == 'B' && c_code)
+ || (inst == 'Z' && !c_code)) {
+ gp = functions[pc.pc_func].f_label;
+ l_gp = label_num >> BC_LABEL_LOG;
+ l_off = label_num % BC_LABEL_GROUP;
+ while (l_gp-- > 0) gp = gp->l_next;
+ pc.pc_addr = gp->l_adrs[l_off];
+ }
+ break;
+
+ case 'C' : /* Call a function. */
+ /* Get the function number. */
+ new_func = byte(&pc);
+ if ((new_func & 0x80) != 0)
+ new_func = ((new_func << 8) & 0x7f) + byte(&pc);
+
+ /* Check to make sure it is defined. */
+ if (!functions[new_func].f_defined)
+ {
+ rt_error ("Function %s not defined.", f_names[new_func]);
+ break;
+ }
+
+ /* Check and push parameters. */
+ process_params (&pc, new_func);
+
+ /* Push auto variables. */
+ for (auto_list = functions[new_func].f_autos;
+ auto_list != NULL;
+ auto_list = auto_list->next)
+ auto_var (auto_list->av_name);
+
+ /* Push pc and ibase. */
+ fpush (pc.pc_func);
+ fpush (pc.pc_addr);
+ fpush (i_base);
+
+ /* Reset pc to start of function. */
+ pc.pc_func = new_func;
+ pc.pc_addr = 0;
+ break;
+
+ case 'D' : /* Duplicate top of stack */
+ push_copy (ex_stack->s_num);
+ break;
+
+ case 'K' : /* Push a constant */
+ /* Get the input base and convert it to a bc number. */
+ if (pc.pc_func == 0)
+ const_base = i_base;
+ else
+ const_base = fn_stack->s_val;
+ if (const_base == 10)
+ push_b10_const (&pc);
+ else
+ push_constant (prog_char, const_base);
+ break;
+
+ case 'L' : /* load array variable */
+ var_name = byte(&pc);
+ if ((var_name & 0x80) != 0)
+ var_name = ((var_name << 8) & 0x7f) + byte(&pc);
+ load_array (var_name);
+ break;
+
+ case 'M' : /* decrement array variable (Minus!) */
+ var_name = byte(&pc);
+ if ((var_name & 0x80) != 0)
+ var_name = ((var_name << 8) & 0x7f) + byte(&pc);
+ decr_array (var_name);
+ break;
+
+ case 'O' : /* Write a string to the output with processing. */
+ while ((ch = byte(&pc)) != '"')
+ if (ch != '\\')
+ out_char (ch);
+ else
+ {
+ ch = byte(&pc);
+ if (ch == '"') break;
+ switch (ch)
+ {
+ case 'n': out_char ('\n'); break;
+ case 't': out_char ('\t'); break;
+ case 'r': out_char ('\r'); break;
+ case 'b': out_char (007); break;
+ case 'f': out_char ('\f'); break;
+ case '\\': out_char ('\\'); break;
+ default: break;
+ }
+ }
+ if (interactive) fflush (stdout);
+ break;
+
+ case 'R' : /* Return from function */
+ if (pc.pc_func != 0)
+ {
+ /* "Pop" autos and parameters. */
+ pop_vars(functions[pc.pc_func].f_autos);
+ pop_vars(functions[pc.pc_func].f_params);
+ /* reset the pc. */
+ fpop ();
+ pc.pc_addr = fpop ();
+ pc.pc_func = fpop ();
+ }
+ else
+ rt_error ("Return from main program.");
+ break;
+
+ case 'S' : /* store array variable */
+ var_name = byte(&pc);
+ if ((var_name & 0x80) != 0)
+ var_name = ((var_name << 8) & 0x7f) + byte(&pc);
+ store_array (var_name);
+ break;
+
+ case 'T' : /* Test tos for zero */
+ c_code = is_zero (ex_stack->s_num);
+ assign (c_code);
+ break;
+
+ case 'W' : /* Write the value on the top of the stack. */
+ case 'P' : /* Write the value on the top of the stack. No newline. */
+ out_num (ex_stack->s_num, o_base, out_char);
+ if (inst == 'W') out_char ('\n');
+ store_var (3); /* Special variable "last". */
+ if (interactive) fflush (stdout);
+ break;
+
+ case 'c' : /* Call special function. */
+ new_func = byte(&pc);
+
+ switch (new_func)
+ {
+ case 'L': /* Length function. */
+ /* For the number 0.xxxx, 0 is not significant. */
+ if (ex_stack->s_num->n_len == 1 &&
+ ex_stack->s_num->n_scale != 0 &&
+ ex_stack->s_num->n_value[0] == 0 )
+ int2num (&ex_stack->s_num, ex_stack->s_num->n_scale);
+ else
+ int2num (&ex_stack->s_num, ex_stack->s_num->n_len
+ + ex_stack->s_num->n_scale);
+ break;
+
+ case 'S': /* Scale function. */
+ int2num (&ex_stack->s_num, ex_stack->s_num->n_scale);
+ break;
+
+ case 'R': /* Square Root function. */
+ if (!bc_sqrt (&ex_stack->s_num, scale))
+ rt_error ("Square root of a negative number");
+ break;
+
+ case 'I': /* Read function. */
+ push_constant (input_char, i_base);
+ break;
+ }
+ break;
+
+ case 'd' : /* Decrement number */
+ var_name = byte(&pc);
+ if ((var_name & 0x80) != 0)
+ var_name = ((var_name << 8) & 0x7f) + byte(&pc);
+ decr_var (var_name);
+ break;
+
+ case 'h' : /* Halt the machine. */
+ exit (0);
+
+ case 'i' : /* increment number */
+ var_name = byte(&pc);
+ if ((var_name & 0x80) != 0)
+ var_name = ((var_name << 8) & 0x7f) + byte(&pc);
+ incr_var (var_name);
+ break;
+
+ case 'l' : /* load variable */
+ var_name = byte(&pc);
+ if ((var_name & 0x80) != 0)
+ var_name = ((var_name << 8) & 0x7f) + byte(&pc);
+ load_var (var_name);
+ break;
+
+ case 'n' : /* Negate top of stack. */
+ bc_sub (_zero_, ex_stack->s_num, &ex_stack->s_num);
+ break;
+
+ case 'p' : /* Pop the execution stack. */
+ pop ();
+ break;
+
+ case 's' : /* store variable */
+ var_name = byte(&pc);
+ if ((var_name & 0x80) != 0)
+ var_name = ((var_name << 8) & 0x7f) + byte(&pc);
+ store_var (var_name);
+ break;
+
+ case 'w' : /* Write a string to the output. */
+ while ((ch = byte(&pc)) != '"') out_char (ch);
+ if (interactive) fflush (stdout);
+ break;
+
+ case 'x' : /* Exchange Top of Stack with the one under the tos. */
+ if (check_stack(2)) {
+ bc_num temp = ex_stack->s_num;
+ ex_stack->s_num = ex_stack->s_next->s_num;
+ ex_stack->s_next->s_num = temp;
+ }
+ break;
+
+ case '0' : /* Load Constant 0. */
+ push_copy (_zero_);
+ break;
+
+ case '1' : /* Load Constant 0. */
+ push_copy (_one_);
+ break;
+
+ case '!' : /* Negate the boolean value on top of the stack. */
+ c_code = is_zero (ex_stack->s_num);
+ assign (c_code);
+ break;
+
+ case '&' : /* compare greater than */
+ if (check_stack(2))
+ {
+ c_code = !is_zero (ex_stack->s_next->s_num)
+ && !is_zero (ex_stack->s_num);
+ pop ();
+ assign (c_code);
+ }
+ break;
+
+ case '|' : /* compare greater than */
+ if (check_stack(2))
+ {
+ c_code = !is_zero (ex_stack->s_next->s_num)
+ || !is_zero (ex_stack->s_num);
+ pop ();
+ assign (c_code);
+ }
+ break;
+
+ case '+' : /* add */
+ if (check_stack(2))
+ {
+ bc_add (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num);
+ pop();
+ pop();
+ push_num (temp_num);
+ init_num (&temp_num);
+ }
+ break;
+
+ case '-' : /* subtract */
+ if (check_stack(2))
+ {
+ bc_sub (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num);
+ pop();
+ pop();
+ push_num (temp_num);
+ init_num (&temp_num);
+ }
+ break;
+
+ case '*' : /* multiply */
+ if (check_stack(2))
+ {
+ bc_multiply (ex_stack->s_next->s_num, ex_stack->s_num,
+ &temp_num, scale);
+ pop();
+ pop();
+ push_num (temp_num);
+ init_num (&temp_num);
+ }
+ break;
+
+ case '/' : /* divide */
+ if (check_stack(2))
+ {
+ if (bc_divide (ex_stack->s_next->s_num,
+ ex_stack->s_num, &temp_num, scale) == 0)
+ {
+ pop();
+ pop();
+ push_num (temp_num);
+ init_num (&temp_num);
+ }
+ else
+ rt_error ("Divide by zero");
+ }
+ break;
+
+ case '%' : /* remainder */
+ if (check_stack(2))
+ {
+ if (is_zero (ex_stack->s_num))
+ rt_error ("Modulo by zero");
+ else
+ {
+ bc_modulo (ex_stack->s_next->s_num,
+ ex_stack->s_num, &temp_num, scale);
+ pop();
+ pop();
+ push_num (temp_num);
+ init_num (&temp_num);
+ }
+ }
+ break;
+
+ case '^' : /* raise */
+ if (check_stack(2))
+ {
+ bc_raise (ex_stack->s_next->s_num,
+ ex_stack->s_num, &temp_num, scale);
+ if (is_zero (ex_stack->s_next->s_num) && is_neg (ex_stack->s_num))
+ rt_error ("divide by zero");
+ pop();
+ pop();
+ push_num (temp_num);
+ init_num (&temp_num);
+ }
+ break;
+
+ case '=' : /* compare equal */
+ if (check_stack(2))
+ {
+ c_code = bc_compare (ex_stack->s_next->s_num,
+ ex_stack->s_num) == 0;
+ pop ();
+ assign (c_code);
+ }
+ break;
+
+ case '#' : /* compare not equal */
+ if (check_stack(2))
+ {
+ c_code = bc_compare (ex_stack->s_next->s_num,
+ ex_stack->s_num) != 0;
+ pop ();
+ assign (c_code);
+ }
+ break;
+
+ case '<' : /* compare less than */
+ if (check_stack(2))
+ {
+ c_code = bc_compare (ex_stack->s_next->s_num,
+ ex_stack->s_num) == -1;
+ pop ();
+ assign (c_code);
+ }
+ break;
+
+ case '{' : /* compare less than or equal */
+ if (check_stack(2))
+ {
+ c_code = bc_compare (ex_stack->s_next->s_num,
+ ex_stack->s_num) <= 0;
+ pop ();
+ assign (c_code);
+ }
+ break;
+
+ case '>' : /* compare greater than */
+ if (check_stack(2))
+ {
+ c_code = bc_compare (ex_stack->s_next->s_num,
+ ex_stack->s_num) == 1;
+ pop ();
+ assign (c_code);
+ }
+ break;
+
+ case '}' : /* compare greater than or equal */
+ if (check_stack(2))
+ {
+ c_code = bc_compare (ex_stack->s_next->s_num,
+ ex_stack->s_num) >= 0;
+ pop ();
+ assign (c_code);
+ }
+ break;
+
+ default : /* error! */
+ rt_error ("bad instruction: inst=%c", inst);
+ }
+ }
+
+ /* Clean up the function stack and pop all autos/parameters. */
+ while (pc.pc_func != 0)
+ {
+ pop_vars(functions[pc.pc_func].f_autos);
+ pop_vars(functions[pc.pc_func].f_params);
+ fpop ();
+ pc.pc_addr = fpop ();
+ pc.pc_func = fpop ();
+ }
+
+ /* Clean up the execution stack. */
+ while (ex_stack != NULL) pop();
+
+ /* Clean up the interrupt stuff. */
+ if (interactive)
+ {
+ signal (SIGINT, use_quit);
+ if (had_sigint)
+ printf ("Interruption completed.\n");
+ }
+}
+
+
+/* Prog_char gets another byte from the program. It is used for
+ conversion of text constants in the code to numbers. */
+
+char
+prog_char ()
+{
+ return byte(&pc);
+}
+
+
+/* Read a character from the standard input. This function is used
+ by the "read" function. */
+
+char
+input_char ()
+{
+ char in_ch;
+
+ /* Get a character from the standard input for the read function. */
+ in_ch = getchar();
+
+ /* Check for a \ quoted newline. */
+ if (in_ch == '\\')
+ {
+ in_ch = getchar();
+ if (in_ch == '\n')
+ in_ch = getchar();
+ }
+
+ /* Classify and preprocess the input character. */
+ if (isdigit(in_ch))
+ return (in_ch - '0');
+ if (in_ch >= 'A' && in_ch <= 'F')
+ return (in_ch + 10 - 'A');
+ if (in_ch >= 'a' && in_ch <= 'f')
+ return (in_ch + 10 - 'a');
+ if (in_ch == '.' || in_ch == '+' || in_ch == '-')
+ return (in_ch);
+ if (in_ch <= ' ')
+ return (' ');
+
+ return (':');
+}
+
+
+/* Push_constant converts a sequence of input characters as returned
+ by IN_CHAR into a number. The number is pushed onto the execution
+ stack. The number is converted as a number in base CONV_BASE. */
+
+void
+push_constant (in_char, conv_base)
+ char (*in_char)(VOID);
+ int conv_base;
+{
+ int digits;
+ bc_num build, temp, result, mult, divisor;
+ char in_ch, first_ch;
+ char negative;
+
+ /* Initialize all bc numbers */
+ init_num (&temp);
+ init_num (&result);
+ init_num (&mult);
+ build = copy_num (_zero_);
+ negative = FALSE;
+
+ /* The conversion base. */
+ int2num (&mult, conv_base);
+
+ /* Get things ready. */
+ in_ch = in_char();
+ while (in_ch == ' ')
+ in_ch = in_char();
+
+ if (in_ch == '+')
+ in_ch = in_char();
+ else
+ if (in_ch == '-')
+ {
+ negative = TRUE;
+ in_ch = in_char();
+ }
+
+ /* Check for the special case of a single digit. */
+ if (in_ch < 16)
+ {
+ first_ch = in_ch;
+ in_ch = in_char();
+ if (in_ch < 16 && first_ch >= conv_base)
+ first_ch = conv_base - 1;
+ int2num (&build, (int) first_ch);
+ }
+
+ /* Convert the integer part. */
+ while (in_ch < 16)
+ {
+ if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1;
+ bc_multiply (build, mult, &result, 0);
+ int2num (&temp, (int) in_ch);
+ bc_add (result, temp, &build);
+ in_ch = in_char();
+ }
+ if (in_ch == '.')
+ {
+ in_ch = in_char();
+ if (in_ch >= conv_base) in_ch = conv_base-1;
+ free_num (&result);
+ free_num (&temp);
+ divisor = copy_num (_one_);
+ result = copy_num (_zero_);
+ digits = 0;
+ while (in_ch < 16)
+ {
+ bc_multiply (result, mult, &result, 0);
+ int2num (&temp, (int) in_ch);
+ bc_add (result, temp, &result);
+ bc_multiply (divisor, mult, &divisor, 0);
+ digits++;
+ in_ch = in_char();
+ if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1;
+ }
+ bc_divide (result, divisor, &result, digits);
+ bc_add (build, result, &build);
+ }
+
+ /* Final work. */
+ if (negative)
+ bc_sub (_zero_, build, &build);
+
+ push_num (build);
+ free_num (&temp);
+ free_num (&result);
+ free_num (&mult);
+}
+
+
+/* When converting base 10 constants from the program, we use this
+ more efficient way to convert them to numbers. PC tells where
+ the constant starts and is expected to be advanced to after
+ the constant. */
+
+void
+push_b10_const (pc)
+ program_counter *pc;
+{
+ bc_num build;
+ program_counter look_pc;
+ int kdigits, kscale;
+ char inchar;
+ char *ptr;
+
+ /* Count the digits and get things ready. */
+ look_pc = *pc;
+ kdigits = 0;
+ kscale = 0;
+ inchar = byte (&look_pc);
+ while (inchar != '.' && inchar != ':')
+ {
+ kdigits++;
+ inchar = byte(&look_pc);
+ }
+ if (inchar == '.' )
+ {
+ inchar = byte(&look_pc);
+ while (inchar != ':')
+ {
+ kscale++;
+ inchar = byte(&look_pc);
+ }
+ }
+
+ /* Get the first character again and move the pc. */
+ inchar = byte(pc);
+
+ /* Secial cases of 0, 1, and A-F single inputs. */
+ if (kdigits == 1 && kscale == 0)
+ {
+ if (inchar == 0)
+ {
+ push_copy (_zero_);
+ inchar = byte(pc);
+ return;
+ }
+ if (inchar == 1) {
+ push_copy (_one_);
+ inchar = byte(pc);
+ return;
+ }
+ if (inchar > 9)
+ {
+ init_num (&build);
+ int2num (&build, inchar);
+ push_num (build);
+ inchar = byte(pc);
+ return;
+ }
+ }
+
+ /* Build the new number. */
+ if (kdigits == 0)
+ {
+ build = new_num (1,kscale);
+ ptr = build->n_value;
+ *ptr++ = 0;
+ }
+ else
+ {
+ build = new_num (kdigits,kscale);
+ ptr = build->n_value;
+ }
+
+ while (inchar != ':')
+ {
+ if (inchar != '.')
+ if (inchar > 9)
+ *ptr++ = 9;
+ else
+ *ptr++ = inchar;
+ inchar = byte(pc);
+ }
+ push_num (build);
+}
+
+
+/* Put the correct value on the stack for C_CODE. Frees TOS num. */
+
+void
+assign (c_code)
+ char c_code;
+{
+ free_num (&ex_stack->s_num);
+ if (c_code)
+ ex_stack->s_num = copy_num (_one_);
+ else
+ ex_stack->s_num = copy_num (_zero_);
+}
diff --git a/gnu/usr.bin/bc/global.c b/gnu/usr.bin/bc/global.c
new file mode 100644
index 0000000..1e7dc1c
--- /dev/null
+++ b/gnu/usr.bin/bc/global.c
@@ -0,0 +1,42 @@
+/* global.c: This defines the global variables. */
+
+/* This file is part of bc written for MINIX.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License , or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ You may contact the author by:
+ e-mail: phil@cs.wwu.edu
+ us-mail: Philip A. Nelson
+ Computer Science Department, 9062
+ Western Washington University
+ Bellingham, WA 98226-9062
+
+*************************************************************************/
+
+#include "bcdefs.h"
+
+/* Since we want to define them here, we use the following define. */
+#undef EXTERN
+#define EXTERN
+
+/* Define all the global variables for bc. */
+#include "global.h"
+
+#ifndef BC_MATH_FILE
+CONST char libmath[] =
+#include "math.h"
+;
+#endif
diff --git a/gnu/usr.bin/bc/global.h b/gnu/usr.bin/bc/global.h
new file mode 100644
index 0000000..e2b6007
--- /dev/null
+++ b/gnu/usr.bin/bc/global.h
@@ -0,0 +1,108 @@
+/* global.h: The global variables for bc. */
+
+/* This file is part of bc written for MINIX.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License , or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ You may contact the author by:
+ e-mail: phil@cs.wwu.edu
+ us-mail: Philip A. Nelson
+ Computer Science Department, 9062
+ Western Washington University
+ Bellingham, WA 98226-9062
+
+*************************************************************************/
+
+
+/* For the current "break level" and if statements. */
+EXTERN int break_label;
+EXTERN int if_label;
+EXTERN int continue_label;
+
+/* Label numbers. */
+EXTERN int next_label;
+
+/* Used for "code" generation. */
+EXTERN char genstr[80];
+EXTERN int out_count;
+EXTERN char did_gen;
+
+/* Interactive and other flags. */
+EXTERN char interactive;
+EXTERN char compile_only;
+EXTERN char use_math;
+EXTERN char warn_not_std;
+EXTERN char std_only;
+
+/* global variables for the bc machine. All will be dynamic in size.*/
+/* Function storage. main is (0) and functions (1-f_count) */
+
+EXTERN bc_function *functions;
+EXTERN char **f_names;
+EXTERN int f_count;
+
+/* Variable stoarge and reverse names. */
+
+EXTERN bc_var **variables;
+EXTERN char **v_names;
+EXTERN int v_count;
+
+/* Array Variable storage and reverse names. */
+
+EXTERN bc_var_array **arrays;
+EXTERN char **a_names;
+EXTERN int a_count;
+
+/* Execution stack. */
+EXTERN estack_rec *ex_stack;
+
+/* Function return stack. */
+EXTERN fstack_rec *fn_stack;
+
+/* Other "storage". */
+EXTERN int i_base;
+EXTERN int o_base;
+EXTERN int scale;
+EXTERN char c_code;
+EXTERN int out_col;
+EXTERN char runtime_error;
+EXTERN program_counter pc;
+
+/* Input Line numbers and other error information. */
+EXTERN int line_no;
+EXTERN int had_error;
+
+/* For larger identifiers, a tree, and how many "storage" locations
+ have been allocated. */
+
+EXTERN int next_array;
+EXTERN int next_func;
+EXTERN int next_var;
+
+EXTERN id_rec *name_tree;
+
+/* For error message production */
+EXTERN char **g_argv;
+EXTERN int g_argc;
+EXTERN char is_std_in;
+
+/* defined in number.c */
+extern bc_num _zero_;
+extern bc_num _one_;
+
+/* For use with getopt. Do not declare them here.*/
+extern int optind;
+
diff --git a/gnu/usr.bin/bc/libmath.b b/gnu/usr.bin/bc/libmath.b
new file mode 100644
index 0000000..30d9532
--- /dev/null
+++ b/gnu/usr.bin/bc/libmath.b
@@ -0,0 +1,255 @@
+/* libmath.b for bc for minix. */
+
+/* This file is part of bc written for MINIX.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License , or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ You may contact the author by:
+ e-mail: phil@cs.wwu.edu
+ us-mail: Philip A. Nelson
+ Computer Science Department, 9062
+ Western Washington University
+ Bellingham, WA 98226-9062
+
+*************************************************************************/
+
+
+scale = 20
+
+/* Uses the fact that e^x = (e^(x/2))^2
+ When x is small enough, we use the series:
+ e^x = 1 + x + x^2/2! + x^3/3! + ...
+*/
+
+define e(x) {
+ auto a, d, e, f, i, m, v, z
+
+ /* Check the sign of x. */
+ if (x<0) {
+ m = 1
+ x = -x
+ }
+
+ /* Precondition x. */
+ z = scale;
+ scale = 4 + z + .44*x;
+ while (x > 1) {
+ f += 1;
+ x /= 2;
+ }
+
+ /* Initialize the variables. */
+ v = 1+x
+ a = x
+ d = 1
+
+ for (i=2; 1; i++) {
+ e = (a *= x) / (d *= i)
+ if (e == 0) {
+ if (f>0) while (f--) v = v*v;
+ scale = z
+ if (m) return (1/v);
+ return (v/1);
+ }
+ v += e
+ }
+}
+
+/* Natural log. Uses the fact that ln(x^2) = 2*ln(x)
+ The series used is:
+ ln(x) = 2(a+a^3/3+a^5/5+...) where a=(x-1)/(x+1)
+*/
+
+define l(x) {
+ auto e, f, i, m, n, v, z
+
+ /* return something for the special case. */
+ if (x <= 0) return (1 - 10^scale)
+
+ /* Precondition x to make .5 < x < 2.0. */
+ z = scale;
+ scale += 4;
+ f = 2;
+ i=0
+ while (x >= 2) { /* for large numbers */
+ f *= 2;
+ x = sqrt(x);
+ }
+ while (x <= .5) { /* for small numbers */
+ f *= 2;
+ x = sqrt(x);
+ }
+
+ /* Set up the loop. */
+ v = n = (x-1)/(x+1)
+ m = n*n
+
+ /* Sum the series. */
+ for (i=3; 1; i+=2) {
+ e = (n *= m) / i
+ if (e == 0) {
+ v = f*v
+ scale = z
+ return (v/1)
+ }
+ v += e
+ }
+}
+
+/* Sin(x) uses the standard series:
+ sin(x) = x - x^3/3! + x^5/5! - x^7/7! ... */
+
+define s(x) {
+ auto e, i, m, n, s, v, z
+
+ /* precondition x. */
+ z = scale
+ scale = 1.1*z + 1;
+ v = a(1)
+ if (x < 0) {
+ m = 1;
+ x = -x;
+ }
+ scale = 0
+ n = (x / v + 2 )/4
+ x = x - 4*n*v
+ if (n%2) x = -x
+
+ /* Do the loop. */
+ scale = z + 2;
+ v = e = x
+ s = -x*x
+ for (i=3; 1; i+=2) {
+ e *= s/(i*(i-1))
+ if (e == 0) {
+ scale = z
+ if (m) return (-v/1);
+ return (v/1);
+ }
+ v += e
+ }
+}
+
+/* Cosine : cos(x) = sin(x+pi/2) */
+define c(x) {
+ auto v;
+ scale += 1;
+ v = s(x+a(1)*2);
+ scale -= 1;
+ return (v/1);
+}
+
+/* Arctan: Using the formula:
+ atan(x) = atan(c) + atan((x-c)/(1+xc)) for a small c (.2 here)
+ For under .2, use the series:
+ atan(x) = x - x^3/3 + x^5/5 - x^7/7 + ... */
+
+define a(x) {
+ auto a, e, f, i, m, n, s, v, z
+
+ /* Special case and for fast answers */
+ if (x==1) {
+ if (scale <= 25) return (.7853981633974483096156608/1)
+ if (scale <= 40) return (.7853981633974483096156608458198757210492/1)
+ if (scale <= 60) \
+ return (.785398163397448309615660845819875721049292349843776455243736/1)
+ }
+ if (x==.2) {
+ if (scale <= 25) return (.1973955598498807583700497/1)
+ if (scale <= 40) return (.1973955598498807583700497651947902934475/1)
+ if (scale <= 60) \
+ return (.197395559849880758370049765194790293447585103787852101517688/1)
+ }
+
+ /* Negative x? */
+ if (x<0) {
+ m = 1;
+ x = -x;
+ }
+
+ /* Save the scale. */
+ z = scale;
+
+ /* Note: a and f are known to be zero due to being auto vars. */
+ /* Calculate atan of a known number. */
+ if (x > .2) {
+ scale = z+4;
+ a = a(.2);
+ }
+
+ /* Precondition x. */
+ scale = z+2;
+ while (x > .2) {
+ f += 1;
+ x = (x-.2) / (1+x*.2);
+ }
+
+ /* Initialize the series. */
+ v = n = x;
+ s = -x*x;
+
+ /* Calculate the series. */
+ for (i=3; 1; i+=2) {
+ e = (n *= s) / i;
+ if (e == 0) {
+ scale = z;
+ if (m) return ((f*a+v)/-1);
+ return ((f*a+v)/1);
+ }
+ v += e
+ }
+}
+
+
+/* Bessel function of integer order. Uses the following:
+ j(-n,x) = (-1)^n*j(n,x)
+ j(n,x) = x^n/(2^n*n!) * (1 - x^2/(2^2*1!*(n+1)) + x^4/(2^4*2!*(n+1)*(n+2))
+ - x^6/(2^6*3!*(n+1)*(n+2)*(n+3)) .... )
+*/
+define j(n,x) {
+ auto a, d, e, f, i, m, s, v, z
+
+ /* Make n an integer and check for negative n. */
+ z = scale;
+ scale = 0;
+ n = n/1;
+ if (n<0) {
+ n = -n;
+ if (n%2 == 1) m = 1;
+ }
+
+ /* Compute the factor of x^n/(2^n*n!) */
+ f = 1;
+ for (i=2; i<=n; i++) f = f*i;
+ scale = 1.5*z;
+ f = x^n / 2^n / f;
+
+ /* Initialize the loop .*/
+ v = e = 1;
+ s = -x*x/4
+ scale = 1.5*z
+
+ /* The Loop.... */
+ for (i=1; 1; i++) {
+ e = e * s / i / (n+i);
+ if (e == 0) {
+ scale = z
+ if (m) return (-f*v/1);
+ return (f*v/1);
+ }
+ v += e;
+ }
+}
diff --git a/gnu/usr.bin/bc/load.c b/gnu/usr.bin/bc/load.c
new file mode 100644
index 0000000..be4ab3a
--- /dev/null
+++ b/gnu/usr.bin/bc/load.c
@@ -0,0 +1,333 @@
+/* load.c: This code "loads" code into the code segments. */
+
+/* This file is part of bc written for MINIX.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License , or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ You may contact the author by:
+ e-mail: phil@cs.wwu.edu
+ us-mail: Philip A. Nelson
+ Computer Science Department, 9062
+ Western Washington University
+ Bellingham, WA 98226-9062
+
+*************************************************************************/
+
+#include "bcdefs.h"
+#include "global.h"
+#include "proto.h"
+
+/* Load variables. */
+
+program_counter load_adr;
+char load_str;
+char load_const;
+
+/* Initialize the load sequence. */
+void
+init_load ()
+{
+ clear_func(0);
+ load_adr.pc_func = 0;
+ load_adr.pc_addr = 0;
+ load_str = FALSE;
+ load_const = FALSE;
+}
+
+/* addbyte adds one BYTE to the current code segment. */
+void
+addbyte (byte)
+ char byte;
+{
+ int seg, offset, func;
+
+ /* If there was an error, don't continue. */
+ if (had_error) return;
+
+ /* Calculate the segment and offset. */
+ seg = load_adr.pc_addr >> BC_SEG_LOG;
+ offset = load_adr.pc_addr++ % BC_SEG_SIZE;
+ func = load_adr.pc_func;
+
+ if (seg >= BC_MAX_SEGS)
+ {
+ yyerror ("Function too big.");
+ return;
+ }
+
+ if (functions[func].f_body[seg] == NULL)
+ functions[func].f_body[seg] = (char *) bc_malloc (BC_SEG_SIZE);
+
+ /* Store the byte. */
+ functions[func].f_body[seg][offset] = byte;
+ functions[func].f_code_size++;
+}
+
+
+/* Define a label LAB to be the current program counter. */
+
+void
+def_label (lab)
+ long lab;
+{
+ bc_label_group *temp;
+ int group, offset, func;
+
+ /* Get things ready. */
+ group = lab >> BC_LABEL_LOG;
+ offset = lab % BC_LABEL_GROUP;
+ func = load_adr.pc_func;
+
+ /* Make sure there is at least one label group. */
+ if (functions[func].f_label == NULL)
+ {
+ functions[func].f_label =
+ (bc_label_group *) bc_malloc (sizeof(bc_label_group));
+ functions[func].f_label->l_next = NULL;
+ }
+
+ /* Add the label group. */
+ temp = functions[func].f_label;
+ while (group > 0)
+ {
+ if (temp->l_next == NULL)
+ {
+ temp->l_next = (bc_label_group *) bc_malloc (sizeof(bc_label_group));
+ temp->l_next->l_next = NULL;
+ }
+ temp = temp->l_next;
+ group --;
+ }
+
+ /* Define it! */
+ temp->l_adrs [offset] = load_adr.pc_addr;
+}
+
+/* Several instructions have integers in the code. They
+ are all known to be legal longs. So, no error code
+ is added. STR is the pointer to the load string and
+ must be moved to the last non-digit character. */
+
+long
+long_val (str)
+ char **str;
+{ int val = 0;
+ char neg = FALSE;
+
+ if (**str == '-')
+ {
+ neg = TRUE;
+ (*str)++;
+ }
+ while (isdigit(**str))
+ val = val*10 + *(*str)++ - '0';
+
+ if (neg)
+ return -val;
+ else
+ return val;
+}
+
+
+/* load_code loads the CODE into the machine. */
+
+void
+load_code (code)
+ char *code;
+{
+ char *str;
+ long ap_name; /* auto or parameter name. */
+ long label_no;
+ long vaf_name; /* variable, array or function number. */
+ long func;
+ program_counter save_adr;
+
+ /* Initialize. */
+ str = code;
+
+ /* Scan the code. */
+ while (*str != 0)
+ {
+ /* If there was an error, don't continue. */
+ if (had_error) return;
+
+ if (load_str)
+ {
+ if (*str == '"') load_str = FALSE;
+ addbyte (*str++);
+ }
+ else
+ if (load_const)
+ {
+ if (*str == '\n')
+ str++;
+ else
+ {
+ if (*str == ':')
+ {
+ load_const = FALSE;
+ addbyte (*str++);
+ }
+ else
+ if (*str == '.')
+ addbyte (*str++);
+ else
+ if (*str >= 'A')
+ addbyte (*str++ + 10 - 'A');
+ else
+ addbyte (*str++ - '0');
+ }
+ }
+ else
+ {
+ switch (*str)
+ {
+
+ case '"': /* Starts a string. */
+ load_str = TRUE;
+ break;
+
+ case 'N': /* A label */
+ str++;
+ label_no = long_val (&str);
+ def_label (label_no);
+ break;
+
+ case 'B': /* Branch to label. */
+ case 'J': /* Jump to label. */
+ case 'Z': /* Branch Zero to label. */
+ addbyte(*str++);
+ label_no = long_val (&str);
+ if (label_no > 65535L)
+ { /* Better message? */
+ fprintf (stderr,"Program too big.\n");
+ exit(1);
+ }
+ addbyte ( (char) label_no & 0xFF);
+ addbyte ( (char) label_no >> 8);
+ break;
+
+ case 'F': /* A function, get the name and initialize it. */
+ str++;
+ func = long_val (&str);
+ clear_func (func);
+#if DEBUG > 2
+ printf ("Loading function number %d\n", func);
+#endif
+ /* get the parameters */
+ while (*str++ != '.')
+ {
+ if (*str == '.')
+ {
+ str++;
+ break;
+ }
+ ap_name = long_val (&str);
+#if DEBUG > 2
+ printf ("parameter number %d\n", ap_name);
+#endif
+ functions[(int)func].f_params =
+ nextarg (functions[(int)func].f_params, ap_name);
+ }
+
+ /* get the auto vars */
+ while (*str != '[')
+ {
+ if (*str == ',') str++;
+ ap_name = long_val (&str);
+#if DEBUG > 2
+ printf ("auto number %d\n", ap_name);
+#endif
+ functions[(int)func].f_autos =
+ nextarg (functions[(int)func].f_autos, ap_name);
+ }
+ save_adr = load_adr;
+ load_adr.pc_func = func;
+ load_adr.pc_addr = 0;
+ break;
+
+ case ']': /* A function end */
+ functions[load_adr.pc_func].f_defined = TRUE;
+ load_adr = save_adr;
+ break;
+
+ case 'C': /* Call a function. */
+ addbyte (*str++);
+ func = long_val (&str);
+ if (func < 128)
+ addbyte ( (char) func);
+ else
+ {
+ addbyte ((func >> 8) & 0xff | 0x80);
+ addbyte (func & 0xff);
+ }
+ if (*str == ',') str++;
+ while (*str != ':')
+ addbyte (*str++);
+ addbyte (':');
+ break;
+
+ case 'c': /* Call a special function. */
+ addbyte (*str++);
+ addbyte (*str);
+ break;
+
+ case 'K': /* A constant.... may have an "F" in it. */
+ addbyte (*str);
+ load_const = TRUE;
+ break;
+
+ case 'd': /* Decrement. */
+ case 'i': /* Increment. */
+ case 'l': /* Load. */
+ case 's': /* Store. */
+ case 'A': /* Array Increment */
+ case 'M': /* Array Decrement */
+ case 'L': /* Array Load */
+ case 'S': /* Array Store */
+ addbyte (*str++);
+ vaf_name = long_val (&str);
+ if (vaf_name < 128)
+ addbyte (vaf_name);
+ else
+ {
+ addbyte ((vaf_name >> 8) & 0xff | 0x80);
+ addbyte (vaf_name & 0xff);
+ }
+ break;
+
+ case '@': /* A command! */
+ switch (*(++str))
+ {
+ case 'i':
+ init_load ();
+ break;
+ case 'r':
+ execute ();
+ break;
+ }
+ break;
+
+ case '\n': /* Ignore the newlines */
+ break;
+
+ default: /* Anything else */
+ addbyte (*str);
+ }
+ str++;
+ }
+ }
+}
diff --git a/gnu/usr.bin/bc/main.c b/gnu/usr.bin/bc/main.c
new file mode 100644
index 0000000..33827cc
--- /dev/null
+++ b/gnu/usr.bin/bc/main.c
@@ -0,0 +1,204 @@
+/* main.c: The main program for bc. */
+
+/* This file is part of bc written for MINIX.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License , or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ You may contact the author by:
+ e-mail: phil@cs.wwu.edu
+ us-mail: Philip A. Nelson
+ Computer Science Department, 9062
+ Western Washington University
+ Bellingham, WA 98226-9062
+
+*************************************************************************/
+
+#include "bcdefs.h"
+#include <signal.h>
+#include "global.h"
+#include "proto.h"
+
+/* Variables for processing multiple files. */
+char first_file;
+extern FILE *yyin;
+
+
+/* The main program for bc. */
+int
+main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+
+ /* Initialize many variables. */
+ compile_only = FALSE;
+ use_math = FALSE;
+ warn_not_std = FALSE;
+ std_only = FALSE;
+ if (isatty(0) && isatty(1))
+ interactive = TRUE;
+ else
+ interactive = FALSE;
+
+ /* Parse the command line */
+ ch = getopt (argc, argv, "lcisvw");
+ while (ch != EOF)
+ {
+ switch (ch)
+ {
+ case 'c': /* compile only */
+ compile_only = TRUE;
+ break;
+ case 'l': /* math lib */
+ use_math = TRUE;
+ break;
+ case 'i': /* force interactive */
+ interactive = TRUE;
+ break;
+ case 'w': /* Non standard features give warnings. */
+ warn_not_std = TRUE;
+ break;
+ case 's': /* Non standard features give errors. */
+ std_only = TRUE;
+ break;
+ case 'v': /* Print the version. */
+ printf ("%s\n", BC_VERSION);
+ break;
+ }
+ ch = getopt (argc, argv, "lcisvw");
+ }
+
+ /* Initialize the machine. */
+ init_storage();
+ init_load();
+
+ /* Set up interrupts to print a message. */
+ if (interactive)
+ signal (SIGINT, use_quit);
+
+ /* Initialize the front end. */
+ init_tree();
+ init_gen ();
+ g_argv = argv;
+ g_argc = argc;
+ is_std_in = FALSE;
+ first_file = TRUE;
+ if (!open_new_file ())
+ exit (1);
+
+ /* Do the parse. */
+ yyparse ();
+
+ /* End the compile only output with a newline. */
+ if (compile_only)
+ printf ("\n");
+
+ exit (0);
+}
+
+
+/* This is the function that opens all the files.
+ It returns TRUE if the file was opened, otherwise
+ it returns FALSE. */
+
+int
+open_new_file ()
+{
+ FILE *new_file;
+
+ /* Set the line number. */
+ line_no = 1;
+
+ /* Check to see if we are done. */
+ if (is_std_in) return (FALSE);
+
+ /* Open the other files. */
+ if (use_math && first_file)
+ {
+#ifdef BC_MATH_FILE
+ /* Make the first file be the math library. */
+ new_file = fopen (BC_MATH_FILE, "r");
+ use_math = FALSE;
+ if (new_file != NULL)
+ {
+ new_yy_file (new_file);
+ return TRUE;
+ }
+ else
+ {
+ fprintf (stderr, "Math Library unavailable.\n");
+ exit (1);
+ }
+#else
+ /* Load the code from a precompiled version of the math libarary. */
+ extern char libmath[];
+ char tmp;
+ /* These MUST be in the order of first mention of each function.
+ That is why "a" comes before "c" even though "a" is defined after
+ after "c". "a" is used in "s"! */
+ tmp = lookup ("e", FUNCT);
+ tmp = lookup ("l", FUNCT);
+ tmp = lookup ("s", FUNCT);
+ tmp = lookup ("a", FUNCT);
+ tmp = lookup ("c", FUNCT);
+ tmp = lookup ("j", FUNCT);
+ load_code (libmath);
+#endif
+ }
+
+ /* One of the argv values. */
+ while (optind < g_argc)
+ {
+ new_file = fopen (g_argv[optind], "r");
+ if (new_file != NULL)
+ {
+ new_yy_file (new_file);
+ optind++;
+ return TRUE;
+ }
+ fprintf (stderr, "File %s is unavailable.\n", g_argv[optind++]);
+ exit (1);
+ }
+
+ /* If we fall through to here, we should return stdin. */
+ new_yy_file (stdin);
+ is_std_in = TRUE;
+ return TRUE;
+}
+
+
+/* Set yyin to the new file. */
+
+void
+new_yy_file (file)
+ FILE *file;
+{
+ if (!first_file) fclose (yyin);
+ yyin = file;
+ first_file = FALSE;
+}
+
+
+/* Message to use quit. */
+
+void
+use_quit (sig)
+ int sig;
+{
+ printf ("\n(interrupt) use quit to exit.\n");
+ signal (SIGINT, use_quit);
+}
diff --git a/gnu/usr.bin/bc/math.h b/gnu/usr.bin/bc/math.h
new file mode 100644
index 0000000..a1fc146
--- /dev/null
+++ b/gnu/usr.bin/bc/math.h
@@ -0,0 +1,40 @@
+"@iK20:s2:p@r\
+@iF1,4.5,6,7,8,9,10,11,12[l4:0<Z0:1s10:pl4:ns4:pN0:l2:s12:pK4\
+:l12:+K.44:l4:*+s2:pN1:l4:1>Z2:l8:1+s8:pl4:K2:/s4:pJ1:N2:1l4:\
++s11:pl4:s5:p1s6:pK2:s9:pN4:1B5:J3:N6:l9:i9:pJ4:N5:l5:l4:*s5:\
+l6:l9:*s6:/s7:pl7:0=Z7:l8:0>Z8:N9:l8:d8:Z10:l11:l11:*s11:pJ9:N10:\
+N8:l12:s2:pl10:Z11:1l11:/RN11:l11:1/RN7:l11:l7:+s11:pJ6:N3:0R]\
+@r\
+@iF2,4.7,8,9,10,13,11,12[l4:0{Z0:1K10:l2:^-RN0:l2:s12:pl2:K4:\
++s2:pK2:s8:p0s9:pN1:l4:K2:}Z2:l8:K2:*s8:pl4:cRs4:pJ1:N2:N3:l4:\
+K.5:{Z4:l8:K2:*s8:pl4:cRs4:pJ3:N4:l4:1-l4:1+/s13:s11:pl13:l13:\
+*s10:pK3:s9:pN6:1B7:J5:N8:l9:K2:+s9:pJ6:N7:l13:l10:*s13:l9:/s7:\
+pl7:0=Z9:l8:l11:*s11:pl12:s2:pl11:1/RN9:l11:l7:+s11:pJ8:N5:0R]\
+@r\
+@iF3,4.7,9,10,13,14,11,12[l2:s12:pK1.1:l12:*1+s2:p1C4,0:s11:p\
+l4:0<Z0:1s10:pl4:ns4:pN0:0s2:pl4:l11:/K2:+K4:/s13:pl4:K4:l13:\
+*l11:*-s4:pl13:K2:%Z1:l4:ns4:pN1:l12:K2:+s2:pl4:s7:s11:pl4:nl4:\
+*s14:pK3:s9:pN3:1B4:J2:N5:l9:K2:+s9:pJ3:N4:l7:l14:l9:l9:1-*/*\
+s7:pl7:0=Z6:l12:s2:pl10:Z7:l11:n1/RN7:l11:1/RN6:l11:l7:+s11:p\
+J5:N2:0R]@r\
+@iF5,4.11[l2:1+s2:pl4:1C4,0:K2:*+C3,0:s11:pl2:1-s2:pl11:1/R0R]\
+@r\
+@iF4,4.5,7,8,9,10,13,14,11,12[l4:1=Z0:l2:K25:{Z1:K.7853981633974483096156608\
+:1/RN1:l2:K40:{Z2:K.7853981633974483096156608458198757210492:\
+1/RN2:l2:K60:{Z3:K.785398163397448309615660845819875721049292349843776455243736\
+:1/RN3:N0:l4:K.2:=Z4:l2:K25:{Z5:K.1973955598498807583700497:1\
+/RN5:l2:K40:{Z6:K.1973955598498807583700497651947902934475:1/\
+RN6:l2:K60:{Z7:K.197395559849880758370049765194790293447585103787852101517688\
+:1/RN7:N4:l4:0<Z8:1s10:pl4:ns4:pN8:l2:s12:pl4:K.2:>Z9:l12:K4:\
++s2:pK.2:C4,0:s5:pN9:l12:K2:+s2:pN10:l4:K.2:>Z11:l8:1+s8:pl4:\
+K.2:-1l4:K.2:*+/s4:pJ10:N11:l4:s13:s11:pl4:nl4:*s14:pK3:s9:pN13:\
+1B14:J12:N15:l9:K2:+s9:pJ13:N14:l13:l14:*s13:l9:/s7:pl7:0=Z16:\
+l12:s2:pl10:Z17:l8:l5:*l11:+1n/RN17:l8:l5:*l11:+1/RN16:l11:l7:\
++s11:pJ15:N12:0R]@r\
+@iF6,13,4.5,6,7,8,9,10,14,11,12[l2:s12:p0s2:pl13:1/s13:pl13:0\
+<Z0:l13:ns13:pl13:K2:%1=Z1:1s10:pN1:N0:1s8:pK2:s9:pN3:l9:l13:\
+{B4:J2:N5:l9:i9:pJ3:N4:l8:l9:*s8:pJ5:N2:K1.5:l12:*s2:pl4:l13:\
+^K2:l13:^/l8:/s8:p1s7:s11:pl4:nl4:*K4:/s14:pK1.5:l12:*s2:p1s9:\
+pN7:1B8:J6:N9:l9:i9:pJ7:N8:l7:l14:*l9:/l13:l9:+/s7:pl7:0=Z10:\
+l12:s2:pl10:Z11:l8:nl11:*1/RN11:l8:l11:*1/RN10:l11:l7:+s11:pJ9:N6:\
+0R]@r"
diff --git a/gnu/usr.bin/bc/number.c b/gnu/usr.bin/bc/number.c
new file mode 100644
index 0000000..346b44a
--- /dev/null
+++ b/gnu/usr.bin/bc/number.c
@@ -0,0 +1,1405 @@
+/* number.c: Implements arbitrary precision numbers. */
+
+/* This file is part of bc written for MINIX.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License , or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ You may contact the author by:
+ e-mail: phil@cs.wwu.edu
+ us-mail: Philip A. Nelson
+ Computer Science Department, 9062
+ Western Washington University
+ Bellingham, WA 98226-9062
+
+*************************************************************************/
+
+#include "bcdefs.h"
+#include "proto.h"
+
+/* Storage used for special numbers. */
+bc_num _zero_;
+bc_num _one_;
+bc_num _two_;
+
+
+/* "Frees" a bc_num NUM. Actually decreases reference count and only
+ frees the storage if reference count is zero. */
+
+void
+free_num (num)
+ bc_num *num;
+{
+ if (*num == NULL) return;
+ (*num)->n_refs--;
+ if ((*num)->n_refs == 0) free(*num);
+ *num = NULL;
+}
+
+
+/* new_num allocates a number and sets fields to known values. */
+
+bc_num
+new_num (length, scale)
+ int length, scale;
+{
+ bc_num temp;
+
+ temp = (bc_num) malloc (sizeof(bc_struct)+length+scale);
+ if (temp == NULL) out_of_memory ();
+ temp->n_sign = PLUS;
+ temp->n_len = length;
+ temp->n_scale = scale;
+ temp->n_refs = 1;
+ temp->n_value[0] = 0;
+ return temp;
+}
+
+
+/* Intitialize the number package! */
+
+void
+init_numbers ()
+{
+ _zero_ = new_num (1,0);
+ _one_ = new_num (1,0);
+ _one_->n_value[0] = 1;
+ _two_ = new_num (1,0);
+ _two_->n_value[0] = 2;
+}
+
+
+/* Make a copy of a number! Just increments the reference count! */
+
+bc_num
+copy_num (num)
+ bc_num num;
+{
+ num->n_refs++;
+ return num;
+}
+
+
+/* Initialize a number NUM by making it a copy of zero. */
+
+void
+init_num (num)
+ bc_num *num;
+{
+ *num = copy_num (_zero_);
+}
+
+
+/* Convert an integer VAL to a bc number NUM. */
+
+void
+int2num (num, val)
+ bc_num *num;
+ int val;
+{
+ char buffer[30];
+ char *bptr, *vptr;
+ int ix = 1;
+ char neg = 0;
+
+ /* Sign. */
+ if (val < 0)
+ {
+ neg = 1;
+ val = -val;
+ }
+
+ /* Get things going. */
+ bptr = buffer;
+ *bptr++ = val % 10;
+ val = val / 10;
+
+ /* Extract remaining digits. */
+ while (val != 0)
+ {
+ *bptr++ = val % 10;
+ val = val / 10;
+ ix++; /* Count the digits. */
+ }
+
+ /* Make the number. */
+ free_num (num);
+ *num = new_num (ix, 0);
+ if (neg) (*num)->n_sign = MINUS;
+
+ /* Assign the digits. */
+ vptr = (*num)->n_value;
+ while (ix-- > 0)
+ *vptr++ = *--bptr;
+}
+
+
+/* Convert a number NUM to a long. The function returns only the integer
+ part of the number. For numbers that are too large to represent as
+ a long, this function returns a zero. This can be detected by checking
+ the NUM for zero after having a zero returned. */
+
+long
+num2long (num)
+ bc_num num;
+{
+ long val;
+ char *nptr;
+ int index;
+
+ /* Extract the int value, ignore the fraction. */
+ val = 0;
+ nptr = num->n_value;
+ for (index=num->n_len; (index>0) && (val<=(LONG_MAX/10)); index--)
+ val = val*10 + *nptr++;
+
+ /* Check for overflow. If overflow, return zero. */
+ if (index>0) val = 0;
+ if (val < 0) val = 0;
+
+ /* Return the value. */
+ if (num->n_sign == PLUS)
+ return (val);
+ else
+ return (-val);
+}
+
+
+/* The following are some math routines for numbers. */
+_PROTOTYPE(static int _do_compare, (bc_num n1, bc_num n2, int use_sign,
+ int ignore_last));
+_PROTOTYPE(static void _rm_leading_zeros, (bc_num num));
+_PROTOTYPE(static bc_num _do_add, (bc_num n1, bc_num n2));
+_PROTOTYPE(static bc_num _do_sub, (bc_num n1, bc_num n2));
+_PROTOTYPE(static void _one_mult, (unsigned char *num, int size, int digit,
+ unsigned char *result));
+
+
+
+/* Compare two bc numbers. Return value is 0 if equal, -1 if N1 is less
+ than N2 and +1 if N1 is greater than N2. If USE_SIGN is false, just
+ compare the magnitudes. */
+
+static int
+_do_compare (n1, n2, use_sign, ignore_last)
+ bc_num n1, n2;
+ int use_sign;
+ int ignore_last;
+{
+ char *n1ptr, *n2ptr;
+ int count;
+
+ /* First, compare signs. */
+ if (use_sign && n1->n_sign != n2->n_sign)
+ {
+ if (n1->n_sign == PLUS)
+ return (1); /* Positive N1 > Negative N2 */
+ else
+ return (-1); /* Negative N1 < Positive N1 */
+ }
+
+ /* Now compare the magnitude. */
+ if (n1->n_len != n2->n_len)
+ {
+ if (n1->n_len > n2->n_len)
+ {
+ /* Magnitude of n1 > n2. */
+ if (!use_sign || n1->n_sign == PLUS)
+ return (1);
+ else
+ return (-1);
+ }
+ else
+ {
+ /* Magnitude of n1 < n2. */
+ if (!use_sign || n1->n_sign == PLUS)
+ return (-1);
+ else
+ return (1);
+ }
+ }
+
+ /* If we get here, they have the same number of integer digits.
+ check the integer part and the equal length part of the fraction. */
+ count = n1->n_len + MIN (n1->n_scale, n2->n_scale);
+ n1ptr = n1->n_value;
+ n2ptr = n2->n_value;
+
+ while ((count > 0) && (*n1ptr == *n2ptr))
+ {
+ n1ptr++;
+ n2ptr++;
+ count--;
+ }
+ if (ignore_last && count == 1 && n1->n_scale == n2->n_scale)
+ return (0);
+ if (count != 0)
+ {
+ if (*n1ptr > *n2ptr)
+ {
+ /* Magnitude of n1 > n2. */
+ if (!use_sign || n1->n_sign == PLUS)
+ return (1);
+ else
+ return (-1);
+ }
+ else
+ {
+ /* Magnitude of n1 < n2. */
+ if (!use_sign || n1->n_sign == PLUS)
+ return (-1);
+ else
+ return (1);
+ }
+ }
+
+ /* They are equal up to the last part of the equal part of the fraction. */
+ if (n1->n_scale != n2->n_scale)
+ if (n1->n_scale > n2->n_scale)
+ {
+ for (count = n1->n_scale-n2->n_scale; count>0; count--)
+ if (*n1ptr++ != 0)
+ {
+ /* Magnitude of n1 > n2. */
+ if (!use_sign || n1->n_sign == PLUS)
+ return (1);
+ else
+ return (-1);
+ }
+ }
+ else
+ {
+ for (count = n2->n_scale-n1->n_scale; count>0; count--)
+ if (*n2ptr++ != 0)
+ {
+ /* Magnitude of n1 < n2. */
+ if (!use_sign || n1->n_sign == PLUS)
+ return (-1);
+ else
+ return (1);
+ }
+ }
+
+ /* They must be equal! */
+ return (0);
+}
+
+
+/* This is the "user callable" routine to compare numbers N1 and N2. */
+
+int
+bc_compare (n1, n2)
+ bc_num n1, n2;
+{
+ return _do_compare (n1, n2, TRUE, FALSE);
+}
+
+
+/* In some places we need to check if the number NUM is zero. */
+
+char
+is_zero (num)
+ bc_num num;
+{
+ int count;
+ char *nptr;
+
+ /* Quick check. */
+ if (num == _zero_) return TRUE;
+
+ /* Initialize */
+ count = num->n_len + num->n_scale;
+ nptr = num->n_value;
+
+ /* The check */
+ while ((count > 0) && (*nptr++ == 0)) count--;
+
+ if (count != 0)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+
+/* In some places we need to check if the number is negative. */
+
+char
+is_neg (num)
+ bc_num num;
+{
+ return num->n_sign == MINUS;
+}
+
+
+/* For many things, we may have leading zeros in a number NUM.
+ _rm_leading_zeros just moves the data to the correct
+ place and adjusts the length. */
+
+static void
+_rm_leading_zeros (num)
+ bc_num num;
+{
+ int bytes;
+ char *dst, *src;
+
+ /* Do a quick check to see if we need to do it. */
+ if (*num->n_value != 0) return;
+
+ /* The first digit is 0, find the first non-zero digit in the 10's or
+ greater place. */
+ bytes = num->n_len;
+ src = num->n_value;
+ while (bytes > 1 && *src == 0) src++, bytes--;
+ num->n_len = bytes;
+ bytes += num->n_scale;
+ dst = num->n_value;
+ while (bytes-- > 0) *dst++ = *src++;
+
+}
+
+
+/* Perform addition: N1 is added to N2 and the value is
+ returned. The signs of N1 and N2 are ignored. */
+
+static bc_num
+_do_add (n1, n2)
+ bc_num n1, n2;
+{
+ bc_num sum;
+ int sum_scale, sum_digits;
+ char *n1ptr, *n2ptr, *sumptr;
+ int carry, n1bytes, n2bytes;
+
+ /* Prepare sum. */
+ sum_scale = MAX (n1->n_scale, n2->n_scale);
+ sum_digits = MAX (n1->n_len, n2->n_len) + 1;
+ sum = new_num (sum_digits,sum_scale);
+
+ /* Start with the fraction part. Initialize the pointers. */
+ n1bytes = n1->n_scale;
+ n2bytes = n2->n_scale;
+ n1ptr = (char *) (n1->n_value + n1->n_len + n1bytes - 1);
+ n2ptr = (char *) (n2->n_value + n2->n_len + n2bytes - 1);
+ sumptr = (char *) (sum->n_value + sum_scale + sum_digits - 1);
+
+ /* Add the fraction part. First copy the longer fraction.*/
+ if (n1bytes != n2bytes)
+ {
+ if (n1bytes > n2bytes)
+ while (n1bytes>n2bytes)
+ { *sumptr-- = *n1ptr--; n1bytes--;}
+ else
+ while (n2bytes>n1bytes)
+ { *sumptr-- = *n2ptr--; n2bytes--;}
+ }
+
+ /* Now add the remaining fraction part and equal size integer parts. */
+ n1bytes += n1->n_len;
+ n2bytes += n2->n_len;
+ carry = 0;
+ while ((n1bytes > 0) && (n2bytes > 0))
+ {
+ *sumptr = *n1ptr-- + *n2ptr-- + carry;
+ if (*sumptr > 9)
+ {
+ carry = 1;
+ *sumptr -= 10;
+ }
+ else
+ carry = 0;
+ sumptr--;
+ n1bytes--;
+ n2bytes--;
+ }
+
+ /* Now add carry the longer integer part. */
+ if (n1bytes == 0)
+ { n1bytes = n2bytes; n1ptr = n2ptr; }
+ while (n1bytes-- > 0)
+ {
+ *sumptr = *n1ptr-- + carry;
+ if (*sumptr > 9)
+ {
+ carry = 1;
+ *sumptr -= 10;
+ }
+ else
+ carry = 0;
+ sumptr--;
+ }
+
+ /* Set final carry. */
+ if (carry == 1)
+ *sumptr += 1;
+
+ /* Adjust sum and return. */
+ _rm_leading_zeros (sum);
+ return sum;
+}
+
+
+/* Perform subtraction: N2 is subtracted from N1 and the value is
+ returned. The signs of N1 and N2 are ignored. Also, N1 is
+ assumed to be larger than N2. */
+
+static bc_num
+_do_sub (n1, n2)
+ bc_num n1, n2;
+{
+ bc_num diff;
+ int diff_scale, diff_len;
+ int min_scale, min_len;
+ char *n1ptr, *n2ptr, *diffptr;
+ int borrow, count, val;
+
+ /* Allocate temporary storage. */
+ diff_len = MAX (n1->n_len, n2->n_len);
+ diff_scale = MAX (n1->n_scale, n2->n_scale);
+ min_len = MIN (n1->n_len, n2->n_len);
+ min_scale = MIN (n1->n_scale, n2->n_scale);
+ diff = new_num (diff_len, diff_scale);
+
+ /* Initialize the subtract. */
+ n1ptr = (char *) (n1->n_value + n1->n_len + n1->n_scale -1);
+ n2ptr = (char *) (n2->n_value + n2->n_len + n2->n_scale -1);
+ diffptr = (char *) (diff->n_value + diff_len + diff_scale -1);
+
+ /* Subtract the numbers. */
+ borrow = 0;
+
+ /* Take care of the longer scaled number. */
+ if (n1->n_scale != min_scale)
+ {
+ /* n1 has the longer scale */
+ for (count = n1->n_scale - min_scale; count > 0; count--)
+ *diffptr-- = *n1ptr--;
+ }
+ else
+ {
+ /* n2 has the longer scale */
+ for (count = n2->n_scale - min_scale; count > 0; count--)
+ {
+ val = - *n2ptr-- - borrow;
+ if (val < 0)
+ {
+ val += 10;
+ borrow = 1;
+ }
+ else
+ borrow = 0;
+ *diffptr-- = val;
+ }
+ }
+
+ /* Now do the equal length scale and integer parts. */
+
+ for (count = 0; count < min_len + min_scale; count++)
+ {
+ val = *n1ptr-- - *n2ptr-- - borrow;
+ if (val < 0)
+ {
+ val += 10;
+ borrow = 1;
+ }
+ else
+ borrow = 0;
+ *diffptr-- = val;
+ }
+
+ /* If n1 has more digits then n2, we now do that subtract. */
+ if (diff_len != min_len)
+ {
+ for (count = diff_len - min_len; count > 0; count--)
+ {
+ val = *n1ptr-- - borrow;
+ if (val < 0)
+ {
+ val += 10;
+ borrow = 1;
+ }
+ else
+ borrow = 0;
+ *diffptr-- = val;
+ }
+ }
+
+ /* Clean up and return. */
+ _rm_leading_zeros (diff);
+ return diff;
+}
+
+
+/* Here is the full add routine that takes care of negative numbers.
+ N1 is added to N2 and the result placed into RESULT. */
+
+void
+bc_add ( n1, n2, result)
+ bc_num n1, n2, *result;
+{
+ bc_num sum;
+ int cmp_res;
+
+ if (n1->n_sign == n2->n_sign)
+ {
+ sum = _do_add (n1, n2);
+ sum->n_sign = n1->n_sign;
+ }
+ else
+ {
+ /* subtraction must be done. */
+ cmp_res = _do_compare (n1, n2, FALSE, FALSE); /* Compare magnitudes. */
+ switch (cmp_res)
+ {
+ case -1:
+ /* n1 is less than n2, subtract n1 from n2. */
+ sum = _do_sub (n2, n1);
+ sum->n_sign = n2->n_sign;
+ break;
+ case 0:
+ /* They are equal! return zero! */
+ sum = copy_num (_zero_);
+ break;
+ case 1:
+ /* n2 is less than n1, subtract n2 from n1. */
+ sum = _do_sub (n1, n2);
+ sum->n_sign = n1->n_sign;
+ }
+ }
+
+ /* Clean up and return. */
+ free_num (result);
+ *result = sum;
+}
+
+
+/* Here is the full subtract routine that takes care of negative numbers.
+ N2 is subtracted from N1 and the result placed in RESULT. */
+
+void
+bc_sub ( n1, n2, result)
+ bc_num n1, n2, *result;
+{
+ bc_num diff;
+ int cmp_res;
+
+ if (n1->n_sign != n2->n_sign)
+ {
+ diff = _do_add (n1, n2);
+ diff->n_sign = n1->n_sign;
+ }
+ else
+ {
+ /* subtraction must be done. */
+ cmp_res = _do_compare (n1, n2, FALSE, FALSE); /* Compare magnitudes. */
+ switch (cmp_res)
+ {
+ case -1:
+ /* n1 is less than n2, subtract n1 from n2. */
+ diff = _do_sub (n2, n1);
+ diff->n_sign = (n2->n_sign == PLUS ? MINUS : PLUS);
+ break;
+ case 0:
+ /* They are equal! return zero! */
+ diff = copy_num (_zero_);
+ break;
+ case 1:
+ /* n2 is less than n1, subtract n2 from n1. */
+ diff = _do_sub (n1, n2);
+ diff->n_sign = n1->n_sign;
+ break;
+ }
+ }
+
+ /* Clean up and return. */
+ free_num (result);
+ *result = diff;
+}
+
+
+/* The multiply routine. N2 time N1 is put int PROD with the scale of
+ the result being MIN(N2 scale+N1 scale, MAX (SCALE, N2 scale, N1 scale)).
+ */
+
+void
+bc_multiply (n1, n2, prod, scale)
+ bc_num n1, n2, *prod;
+ int scale;
+{
+ bc_num pval; /* For the working storage. */
+ char *n1ptr, *n2ptr, *pvptr; /* Work pointers. */
+ char *n1end, *n2end; /* To the end of n1 and n2. */
+
+ int indx;
+ int len1, len2, total_digits;
+ long sum;
+ int full_scale, prod_scale;
+ int toss;
+
+ /* Initialize things. */
+ len1 = n1->n_len + n1->n_scale;
+ len2 = n2->n_len + n2->n_scale;
+ total_digits = len1 + len2;
+ full_scale = n1->n_scale + n2->n_scale;
+ prod_scale = MIN(full_scale,MAX(scale,MAX(n1->n_scale,n2->n_scale)));
+ toss = full_scale - prod_scale;
+ pval = new_num (total_digits-full_scale, prod_scale);
+ pval->n_sign = ( n1->n_sign == n2->n_sign ? PLUS : MINUS );
+ n1end = (char *) (n1->n_value + len1 - 1);
+ n2end = (char *) (n2->n_value + len2 - 1);
+ pvptr = (char *) (pval->n_value + total_digits - toss - 1);
+ sum = 0;
+
+ /* Here are the loops... */
+ for (indx = 0; indx < toss; indx++)
+ {
+ n1ptr = (char *) (n1end - MAX(0, indx-len2+1));
+ n2ptr = (char *) (n2end - MIN(indx, len2-1));
+ while ((n1ptr >= n1->n_value) && (n2ptr <= n2end))
+ sum += *n1ptr-- * *n2ptr++;
+ sum = sum / 10;
+ }
+ for ( ; indx < total_digits-1; indx++)
+ {
+ n1ptr = (char *) (n1end - MAX(0, indx-len2+1));
+ n2ptr = (char *) (n2end - MIN(indx, len2-1));
+ while ((n1ptr >= n1->n_value) && (n2ptr <= n2end))
+ sum += *n1ptr-- * *n2ptr++;
+ *pvptr-- = sum % 10;
+ sum = sum / 10;
+ }
+ *pvptr-- = sum;
+
+ /* Assign to prod and clean up the number. */
+ free_num (prod);
+ *prod = pval;
+ _rm_leading_zeros (*prod);
+ if (is_zero (*prod))
+ (*prod)->n_sign = PLUS;
+}
+
+
+/* Some utility routines for the divide: First a one digit multiply.
+ NUM (with SIZE digits) is multiplied by DIGIT and the result is
+ placed into RESULT. It is written so that NUM and RESULT can be
+ the same pointers. */
+
+static void
+_one_mult (num, size, digit, result)
+ unsigned char *num;
+ int size, digit;
+ unsigned char *result;
+{
+ int carry, value;
+ unsigned char *nptr, *rptr;
+
+ if (digit == 0)
+ memset (result, 0, size);
+ else
+ {
+ if (digit == 1)
+ memcpy (result, num, size);
+ else
+ {
+ /* Initialize */
+ nptr = (unsigned char *) (num+size-1);
+ rptr = (unsigned char *) (result+size-1);
+ carry = 0;
+
+ while (size-- > 0)
+ {
+ value = *nptr-- * digit + carry;
+ *rptr-- = value % 10;
+ carry = value / 10;
+ }
+
+ if (carry != 0) *rptr = carry;
+ }
+ }
+}
+
+
+/* The full division routine. This computes N1 / N2. It returns
+ 0 if the division is ok and the result is in QUOT. The number of
+ digits after the decimal point is SCALE. It returns -1 if division
+ by zero is tried. The algorithm is found in Knuth Vol 2. p237. */
+
+int
+bc_divide (n1, n2, quot, scale)
+ bc_num n1, n2, *quot;
+ int scale;
+{
+ bc_num qval;
+ unsigned char *num1, *num2;
+ unsigned char *ptr1, *ptr2, *n2ptr, *qptr;
+ int scale1, val;
+ unsigned int len1, len2, scale2, qdigits, extra, count;
+ unsigned int qdig, qguess, borrow, carry;
+ unsigned char *mval;
+ char zero;
+ unsigned int norm;
+
+ /* Test for divide by zero. */
+ if (is_zero (n2)) return -1;
+
+ /* Test for divide by 1. If it is we must truncate. */
+ if (n2->n_scale == 0)
+ {
+ if (n2->n_len == 1 && *n2->n_value == 1)
+ {
+ qval = new_num (n1->n_len, scale);
+ qval->n_sign = (n1->n_sign == n2->n_sign ? PLUS : MINUS);
+ memset (&qval->n_value[n1->n_len],0,scale);
+ memcpy (qval->n_value, n1->n_value,
+ n1->n_len + MIN(n1->n_scale,scale));
+ free_num (quot);
+ *quot = qval;
+ }
+ }
+
+ /* Set up the divide. Move the decimal point on n1 by n2's scale.
+ Remember, zeros on the end of num2 are wasted effort for dividing. */
+ scale2 = n2->n_scale;
+ n2ptr = (unsigned char *) n2->n_value+n2->n_len+scale2-1;
+ while ((scale2 > 0) && (*n2ptr-- == 0)) scale2--;
+
+ len1 = n1->n_len + scale2;
+ scale1 = n1->n_scale - scale2;
+ if (scale1 < scale)
+ extra = scale - scale1;
+ else
+ extra = 0;
+ num1 = (unsigned char *) malloc (n1->n_len+n1->n_scale+extra+2);
+ if (num1 == NULL) out_of_memory();
+ memset (num1, 0, n1->n_len+n1->n_scale+extra+2);
+ memcpy (num1+1, n1->n_value, n1->n_len+n1->n_scale);
+
+ len2 = n2->n_len + scale2;
+ num2 = (unsigned char *) malloc (len2+1);
+ if (num2 == NULL) out_of_memory();
+ memcpy (num2, n2->n_value, len2);
+ *(num2+len2) = 0;
+ n2ptr = num2;
+ while (*n2ptr == 0)
+ {
+ n2ptr++;
+ len2--;
+ }
+
+ /* Calculate the number of quotient digits. */
+ if (len2 > len1+scale)
+ {
+ qdigits = scale+1;
+ zero = TRUE;
+ }
+ else
+ {
+ zero = FALSE;
+ if (len2>len1)
+ qdigits = scale+1; /* One for the zero integer part. */
+ else
+ qdigits = len1-len2+scale+1;
+ }
+
+ /* Allocate and zero the storage for the quotient. */
+ qval = new_num (qdigits-scale,scale);
+ memset (qval->n_value, 0, qdigits);
+
+ /* Allocate storage for the temporary storage mval. */
+ mval = (unsigned char *) malloc (len2+1);
+ if (mval == NULL) out_of_memory ();
+
+ /* Now for the full divide algorithm. */
+ if (!zero)
+ {
+ /* Normalize */
+ norm = 10 / ((int)*n2ptr + 1);
+ if (norm != 1)
+ {
+ _one_mult (num1, len1+scale1+extra+1, norm, num1);
+ _one_mult (n2ptr, len2, norm, n2ptr);
+ }
+
+ /* Initialize divide loop. */
+ qdig = 0;
+ if (len2 > len1)
+ qptr = (unsigned char *) qval->n_value+len2-len1;
+ else
+ qptr = (unsigned char *) qval->n_value;
+
+ /* Loop */
+ while (qdig <= len1+scale-len2)
+ {
+ /* Calculate the quotient digit guess. */
+ if (*n2ptr == num1[qdig])
+ qguess = 9;
+ else
+ qguess = (num1[qdig]*10 + num1[qdig+1]) / *n2ptr;
+
+ /* Test qguess. */
+ if (n2ptr[1]*qguess >
+ (num1[qdig]*10 + num1[qdig+1] - *n2ptr*qguess)*10
+ + num1[qdig+2])
+ {
+ qguess--;
+ /* And again. */
+ if (n2ptr[1]*qguess >
+ (num1[qdig]*10 + num1[qdig+1] - *n2ptr*qguess)*10
+ + num1[qdig+2])
+ qguess--;
+ }
+
+ /* Multiply and subtract. */
+ borrow = 0;
+ if (qguess != 0)
+ {
+ *mval = 0;
+ _one_mult (n2ptr, len2, qguess, mval+1);
+ ptr1 = (unsigned char *) num1+qdig+len2;
+ ptr2 = (unsigned char *) mval+len2;
+ for (count = 0; count < len2+1; count++)
+ {
+ val = (int) *ptr1 - (int) *ptr2-- - borrow;
+ if (val < 0)
+ {
+ val += 10;
+ borrow = 1;
+ }
+ else
+ borrow = 0;
+ *ptr1-- = val;
+ }
+ }
+
+ /* Test for negative result. */
+ if (borrow == 1)
+ {
+ qguess--;
+ ptr1 = (unsigned char *) num1+qdig+len2;
+ ptr2 = (unsigned char *) n2ptr+len2-1;
+ carry = 0;
+ for (count = 0; count < len2; count++)
+ {
+ val = (int) *ptr1 + (int) *ptr2-- + carry;
+ if (val > 9)
+ {
+ val -= 10;
+ carry = 1;
+ }
+ else
+ carry = 0;
+ *ptr1-- = val;
+ }
+ if (carry == 1) *ptr1 = (*ptr1 + 1) % 10;
+ }
+
+ /* We now know the quotient digit. */
+ *qptr++ = qguess;
+ qdig++;
+ }
+ }
+
+ /* Clean up and return the number. */
+ qval->n_sign = ( n1->n_sign == n2->n_sign ? PLUS : MINUS );
+ if (is_zero (qval)) qval->n_sign = PLUS;
+ _rm_leading_zeros (qval);
+ free_num (quot);
+ *quot = qval;
+
+ /* Clean up temporary storage. */
+ free (mval);
+ free (num1);
+ free (num2);
+
+ return 0; /* Everything is OK. */
+}
+
+
+/* Modulo for numbers. This computes NUM1 % NUM2 and puts the
+ result in RESULT. */
+
+int
+bc_modulo (num1, num2, result, scale)
+ bc_num num1, num2, *result;
+ int scale;
+{
+ bc_num temp;
+ int rscale;
+
+ /* Check for correct numbers. */
+ if (is_zero (num2)) return -1;
+
+ /* Calculate final scale. */
+ rscale = MAX (num1->n_scale, num2->n_scale+scale);
+ init_num (&temp);
+
+ /* Calculate it. */
+ bc_divide (num1, num2, &temp, scale);
+ bc_multiply (temp, num2, &temp, rscale);
+ bc_sub (num1, temp, result);
+ free_num (&temp);
+
+ return 0; /* Everything is OK. */
+}
+
+
+/* Raise NUM1 to the NUM2 power. The result is placed in RESULT.
+ Maximum exponent is LONG_MAX. If a NUM2 is not an integer,
+ only the integer part is used. */
+
+void
+bc_raise (num1, num2, result, scale)
+ bc_num num1, num2, *result;
+ int scale;
+{
+ bc_num temp, power;
+ long exponent;
+ int rscale;
+ char neg;
+
+ /* Check the exponent for scale digits and convert to a long. */
+ if (num2->n_scale != 0)
+ rt_warn ("non-zero scale in exponent");
+ exponent = num2long (num2);
+ if (exponent == 0 && (num2->n_len > 1 || num2->n_value[0] != 0))
+ rt_error ("exponent too large in raise");
+
+ /* Special case if exponent is a zero. */
+ if (exponent == 0)
+ {
+ free_num (result);
+ *result = copy_num (_one_);
+ return;
+ }
+
+ /* Other initializations. */
+ if (exponent < 0)
+ {
+ neg = TRUE;
+ exponent = -exponent;
+ rscale = scale;
+ }
+ else
+ {
+ neg = FALSE;
+ rscale = MIN (num1->n_scale*exponent, MAX(scale, num1->n_scale));
+ }
+ temp = copy_num (_one_);
+ power = copy_num (num1);
+
+ /* Do the calculation. */
+ while (exponent != 0)
+ {
+ if (exponent & 1 != 0)
+ bc_multiply (temp, power, &temp, rscale);
+ bc_multiply (power, power, &power, rscale);
+ exponent = exponent >> 1;
+ }
+
+ /* Assign the value. */
+ if (neg)
+ {
+ bc_divide (_one_, temp, result, rscale);
+ free_num (&temp);
+ }
+ else
+ {
+ free_num (result);
+ *result = temp;
+ }
+ free_num (&power);
+}
+
+
+/* Take the square root NUM and return it in NUM with SCALE digits
+ after the decimal place. */
+
+int
+bc_sqrt (num, scale)
+ bc_num *num;
+ int scale;
+{
+ int rscale, cmp_res, done;
+ int cscale;
+ bc_num guess, guess1, point5;
+
+ /* Initial checks. */
+ cmp_res = bc_compare (*num, _zero_);
+ if (cmp_res < 0)
+ return 0; /* error */
+ else
+ {
+ if (cmp_res == 0)
+ {
+ free_num (num);
+ *num = copy_num (_zero_);
+ return 1;
+ }
+ }
+ cmp_res = bc_compare (*num, _one_);
+ if (cmp_res == 0)
+ {
+ free_num (num);
+ *num = copy_num (_one_);
+ return 1;
+ }
+
+ /* Initialize the variables. */
+ rscale = MAX (scale, (*num)->n_scale);
+ cscale = rscale + 2;
+ init_num (&guess);
+ init_num (&guess1);
+ point5 = new_num (1,1);
+ point5->n_value[1] = 5;
+
+
+ /* Calculate the initial guess. */
+ if (cmp_res < 0)
+ /* The number is between 0 and 1. Guess should start at 1. */
+ guess = copy_num (_one_);
+ else
+ {
+ /* The number is greater than 1. Guess should start at 10^(exp/2). */
+ int2num (&guess,10);
+ int2num (&guess1,(*num)->n_len);
+ bc_multiply (guess1, point5, &guess1, rscale);
+ guess1->n_scale = 0;
+ bc_raise (guess, guess1, &guess, rscale);
+ free_num (&guess1);
+ }
+
+ /* Find the square root using Newton's algorithm. */
+ done = FALSE;
+ while (!done)
+ {
+ free_num (&guess1);
+ guess1 = copy_num (guess);
+ bc_divide (*num,guess,&guess,cscale);
+ bc_add (guess,guess1,&guess);
+ bc_multiply (guess,point5,&guess,cscale);
+ cmp_res = _do_compare (guess,guess1,FALSE,TRUE);
+ if (cmp_res == 0) done = TRUE;
+ }
+
+ /* Assign the number and clean up. */
+ free_num (num);
+ bc_divide (guess,_one_,num,rscale);
+ free_num (&guess);
+ free_num (&guess1);
+ free_num (&point5);
+ return 1;
+}
+
+
+/* The following routines provide output for bcd numbers package
+ using the rules of POSIX bc for output. */
+
+/* This structure is used for saving digits in the conversion process. */
+typedef struct stk_rec {
+ long digit;
+ struct stk_rec *next;
+} stk_rec;
+
+/* The reference string for digits. */
+char ref_str[] = "0123456789ABCDEF";
+
+
+/* A special output routine for "multi-character digits." Exactly
+ SIZE characters must be output for the value VAL. If SPACE is
+ non-zero, we must output one space before the number. OUT_CHAR
+ is the actual routine for writing the characters. */
+
+void
+out_long (val, size, space, out_char)
+ long val;
+ int size, space;
+#ifdef __STDC__
+ void (*out_char)(int);
+#else
+ void (*out_char)();
+#endif
+{
+ char digits[40];
+ int len, ix;
+
+ if (space) (*out_char) (' ');
+ sprintf (digits, "%ld", val);
+ len = strlen (digits);
+ while (size > len)
+ {
+ (*out_char) ('0');
+ size--;
+ }
+ for (ix=0; ix < len; ix++)
+ (*out_char) (digits[ix]);
+}
+
+/* Output of a bcd number. NUM is written in base O_BASE using OUT_CHAR
+ as the routine to do the actual output of the characters. */
+
+void
+out_num (num, o_base, out_char)
+ bc_num num;
+ int o_base;
+#ifdef __STDC__
+ void (*out_char)(int);
+#else
+ void (*out_char)();
+#endif
+{
+ char *nptr;
+ int index, fdigit, pre_space;
+ stk_rec *digits, *temp;
+ bc_num int_part, frac_part, base, cur_dig, t_num, max_o_digit;
+
+ /* The negative sign if needed. */
+ if (num->n_sign == MINUS) (*out_char) ('-');
+
+ /* Output the number. */
+ if (is_zero (num))
+ (*out_char) ('0');
+ else
+ if (o_base == 10)
+ {
+ /* The number is in base 10, do it the fast way. */
+ nptr = num->n_value;
+ if (num->n_len > 1 || *nptr != 0)
+ for (index=num->n_len; index>0; index--)
+ (*out_char) (BCD_CHAR(*nptr++));
+ else
+ nptr++;
+
+ /* Now the fraction. */
+ if (num->n_scale > 0)
+ {
+ (*out_char) ('.');
+ for (index=0; index<num->n_scale; index++)
+ (*out_char) (BCD_CHAR(*nptr++));
+ }
+ }
+ else
+ {
+ /* The number is some other base. */
+ digits = NULL;
+ init_num (&int_part);
+ bc_divide (num, _one_, &int_part, 0);
+ init_num (&frac_part);
+ init_num (&cur_dig);
+ init_num (&base);
+ bc_sub (num, int_part, &frac_part);
+ int2num (&base, o_base);
+ init_num (&max_o_digit);
+ int2num (&max_o_digit, o_base-1);
+
+
+ /* Get the digits of the integer part and push them on a stack. */
+ while (!is_zero (int_part))
+ {
+ bc_modulo (int_part, base, &cur_dig, 0);
+ temp = (stk_rec *) malloc (sizeof(stk_rec));
+ if (temp == NULL) out_of_memory();
+ temp->digit = num2long (cur_dig);
+ temp->next = digits;
+ digits = temp;
+ bc_divide (int_part, base, &int_part, 0);
+ }
+
+ /* Print the digits on the stack. */
+ if (digits != NULL)
+ {
+ /* Output the digits. */
+ while (digits != NULL)
+ {
+ temp = digits;
+ digits = digits->next;
+ if (o_base <= 16)
+ (*out_char) (ref_str[ (int) temp->digit]);
+ else
+ out_long (temp->digit, max_o_digit->n_len, 1, out_char);
+ free (temp);
+ }
+ }
+
+ /* Get and print the digits of the fraction part. */
+ if (num->n_scale > 0)
+ {
+ (*out_char) ('.');
+ pre_space = 0;
+ t_num = copy_num (_one_);
+ while (t_num->n_len <= num->n_scale) {
+ bc_multiply (frac_part, base, &frac_part, num->n_scale);
+ fdigit = num2long (frac_part);
+ int2num (&int_part, fdigit);
+ bc_sub (frac_part, int_part, &frac_part);
+ if (o_base <= 16)
+ (*out_char) (ref_str[fdigit]);
+ else {
+ out_long (fdigit, max_o_digit->n_len, pre_space, out_char);
+ pre_space = 1;
+ }
+ bc_multiply (t_num, base, &t_num, 0);
+ }
+ }
+
+ /* Clean up. */
+ free_num (&int_part);
+ free_num (&frac_part);
+ free_num (&base);
+ free_num (&cur_dig);
+ }
+}
+
+
+#if DEBUG > 0
+
+/* Debugging procedures. Some are just so one can call them from the
+ debugger. */
+
+/* p_n prints the number NUM in base 10. */
+
+void
+p_n (num)
+ bc_num num;
+{
+ out_num (num, 10, out_char);
+ return 0;
+}
+
+
+/* p_b prints a character array as if it was a string of bcd digits. */
+void
+p_v (name, num, len)
+ char *name;
+ unsigned char *num;
+ int len;
+{
+ int i;
+ printf ("%s=", name);
+ for (i=0; i<len; i++) printf ("%c",BCD_CHAR(num[i]));
+ printf ("\n");
+}
+
+
+/* Convert strings to bc numbers. Base 10 only.*/
+
+void
+str2num (num, str, scale)
+ bc_num *num;
+ char *str;
+ int scale;
+{
+ int digits, strscale;
+ char *ptr, *nptr;
+ char zero_int;
+
+ /* Prepare num. */
+ free_num (num);
+
+ /* Check for valid number and count digits. */
+ ptr = str;
+ digits = 0;
+ strscale = 0;
+ zero_int = FALSE;
+ if ( (*ptr == '+') || (*ptr == '-')) ptr++; /* Sign */
+ while (*ptr == '0') ptr++; /* Skip leading zeros. */
+ while (isdigit(*ptr)) ptr++, digits++; /* digits */
+ if (*ptr == '.') ptr++; /* decimal point */
+ while (isdigit(*ptr)) ptr++, strscale++; /* digits */
+ if ((*ptr != '\0') || (digits+strscale == 0))
+ {
+ *num = copy_num (_zero_);
+ return;
+ }
+
+ /* Adjust numbers and allocate storage and initialize fields. */
+ strscale = MIN(strscale, scale);
+ if (digits == 0)
+ {
+ zero_int = TRUE;
+ digits = 1;
+ }
+ *num = new_num (digits, strscale);
+
+ /* Build the whole number. */
+ ptr = str;
+ if (*ptr == '-')
+ {
+ (*num)->n_sign = MINUS;
+ ptr++;
+ }
+ else
+ {
+ (*num)->n_sign = PLUS;
+ if (*ptr == '+') ptr++;
+ }
+ while (*ptr == '0') ptr++; /* Skip leading zeros. */
+ nptr = (*num)->n_value;
+ if (zero_int)
+ {
+ *nptr++ = 0;
+ digits = 0;
+ }
+ for (;digits > 0; digits--)
+ *nptr++ = CH_VAL(*ptr++);
+
+
+ /* Build the fractional part. */
+ if (strscale > 0)
+ {
+ ptr++; /* skip the decimal point! */
+ for (;strscale > 0; strscale--)
+ *nptr++ = CH_VAL(*ptr++);
+ }
+}
+
+/* Convert a numbers to a string. Base 10 only.*/
+
+char
+*num2str (num)
+ bc_num num;
+{
+ char *str, *sptr;
+ char *nptr;
+ int index, signch;
+
+ /* Allocate the string memory. */
+ signch = ( num->n_sign == PLUS ? 0 : 1 ); /* Number of sign chars. */
+ if (num->n_scale > 0)
+ str = (char *) malloc (num->n_len + num->n_scale + 2 + signch);
+ else
+ str = (char *) malloc (num->n_len + 1 + signch);
+ if (str == NULL) out_of_memory();
+
+ /* The negative sign if needed. */
+ sptr = str;
+ if (signch) *sptr++ = '-';
+
+ /* Load the whole number. */
+ nptr = num->n_value;
+ for (index=num->n_len; index>0; index--)
+ *sptr++ = BCD_CHAR(*nptr++);
+
+ /* Now the fraction. */
+ if (num->n_scale > 0)
+ {
+ *sptr++ = '.';
+ for (index=0; index<num->n_scale; index++)
+ *sptr++ = BCD_CHAR(*nptr++);
+ }
+
+ /* Terminate the string and return it! */
+ *sptr = '\0';
+ return (str);
+}
+#endif
diff --git a/gnu/usr.bin/bc/number.h b/gnu/usr.bin/bc/number.h
new file mode 100644
index 0000000..9938515
--- /dev/null
+++ b/gnu/usr.bin/bc/number.h
@@ -0,0 +1,60 @@
+/* number.h: Arbitrary precision numbers header file. */
+
+/* This file is part of bc written for MINIX.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License , or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ You may contact the author by:
+ e-mail: phil@cs.wwu.edu
+ us-mail: Philip A. Nelson
+ Computer Science Department, 9062
+ Western Washington University
+ Bellingham, WA 98226-9062
+
+*************************************************************************/
+
+
+typedef enum {PLUS, MINUS} sign;
+
+typedef struct
+ {
+ sign n_sign;
+ int n_len; /* The number of digits before the decimal point. */
+ int n_scale; /* The number of digits after the decimal point. */
+ int n_refs; /* The number of pointers to this number. */
+ char n_value[1]; /* The storage. Not zero char terminated. It is
+ allocated with all other fields. */
+ } bc_struct;
+
+typedef bc_struct *bc_num;
+
+/* Some useful macros and constants. */
+
+#define CH_VAL(c) (c - '0')
+#define BCD_CHAR(d) (d + '0')
+
+#ifdef MIN
+#undef MIN
+#undef MAX
+#endif
+#define MAX(a,b) (a>b?a:b)
+#define MIN(a,b) (a>b?b:a)
+#define ODD(a) (a&1)
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
diff --git a/gnu/usr.bin/bc/proto.h b/gnu/usr.bin/bc/proto.h
new file mode 100644
index 0000000..fea9405
--- /dev/null
+++ b/gnu/usr.bin/bc/proto.h
@@ -0,0 +1,165 @@
+/* proto.h: Prototype function definitions for "external" functions. */
+
+/* This file is part of bc written for MINIX.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License , or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ You may contact the author by:
+ e-mail: phil@cs.wwu.edu
+ us-mail: Philip A. Nelson
+ Computer Science Department, 9062
+ Western Washington University
+ Bellingham, WA 98226-9062
+
+*************************************************************************/
+
+/* For the pc version using k&r ACK. (minix1.5 and earlier.) */
+#ifdef SHORTNAMES
+#define init_numbers i_numbers
+#define push_constant push__constant
+#define load_const in_load_const
+#define yy_get_next_buffer yyget_next_buffer
+#define yy_init_buffer yyinit_buffer
+#define yy_last_accepting_state yylast_accepting_state
+#define arglist1 arg1list
+#endif
+
+/* Include the standard library header files. */
+#ifndef NO_UNISTD
+#include <unistd.h>
+#endif
+#ifndef NO_STDLIB
+#ifdef __STDC__
+#include <stdlib.h>
+#endif
+#endif
+
+/* Define the _PROTOTYPE macro if it is needed. */
+
+#ifndef _PROTOTYPE
+#ifdef __STDC__
+#define _PROTOTYPE(func, args) func args
+#else
+#define _PROTOTYPE(func, args) func()
+#endif
+#endif
+
+/* From execute.c */
+_PROTOTYPE(void stop_execution, (int));
+_PROTOTYPE(unsigned char byte, (program_counter *pc));
+_PROTOTYPE(void execute, (void));
+_PROTOTYPE(char prog_char, (void));
+_PROTOTYPE(char input_char, (void));
+_PROTOTYPE(void push_constant, (char (*in_char)(void), int conv_base));
+_PROTOTYPE(void push_b10_const, (program_counter *pc));
+_PROTOTYPE(void assign, (int c_code));
+
+/* From util.c */
+_PROTOTYPE(char *strcopyof, (char *str));
+_PROTOTYPE(arg_list *nextarg, (arg_list *args, int val));
+_PROTOTYPE(char *arg_str, (arg_list *args, int));
+_PROTOTYPE(void free_args, (arg_list *args));
+_PROTOTYPE(void check_params, (arg_list *params, arg_list *autos));
+_PROTOTYPE(void init_gen, (void));
+_PROTOTYPE(void generate, (char *str));
+_PROTOTYPE(void run_code, (void));
+_PROTOTYPE(void out_char, (int ch));
+_PROTOTYPE(id_rec *find_id, (id_rec *tree, char *id));
+_PROTOTYPE(int insert_id_rec, (id_rec **root, id_rec *new_id));
+_PROTOTYPE(void init_tree, (void));
+_PROTOTYPE(int lookup, (char *name, int namekind));
+_PROTOTYPE(char *bc_malloc, (int));
+_PROTOTYPE(void out_of_memory, (void));
+_PROTOTYPE(void welcome, (void));
+_PROTOTYPE(void warranty, (char *));
+_PROTOTYPE(void limits, (void));
+_PROTOTYPE(void yyerror, (char *str ,...));
+_PROTOTYPE(void warn, (char *mesg ,...));
+_PROTOTYPE(void rt_error, (char *mesg ,...));
+_PROTOTYPE(void rt_warn, (char *mesg ,...));
+
+/* From load.c */
+_PROTOTYPE(void init_load, (void));
+_PROTOTYPE(void addbyte, (int byte));
+_PROTOTYPE(void def_label, (long lab));
+_PROTOTYPE(long long_val, (char **str));
+_PROTOTYPE(void load_code, (char *code));
+
+/* From main.c */
+_PROTOTYPE(int main, (int argc , char *argv []));
+_PROTOTYPE(int open_new_file, (void));
+_PROTOTYPE(void new_yy_file, (FILE *file));
+_PROTOTYPE(void use_quit, (int));
+
+/* From number.c */
+_PROTOTYPE(void free_num, (bc_num *num));
+_PROTOTYPE(bc_num new_num, (int length, int scale));
+_PROTOTYPE(void init_numbers, (void));
+_PROTOTYPE(bc_num copy_num, (bc_num num));
+_PROTOTYPE(void init_num, (bc_num *num));
+_PROTOTYPE(void str2num, (bc_num *num, char *str, int scale));
+_PROTOTYPE(char *num2str, (bc_num num));
+_PROTOTYPE(void int2num, (bc_num *num, int val));
+_PROTOTYPE(long num2long, (bc_num num));
+_PROTOTYPE(int bc_compare, (bc_num n1, bc_num n2));
+_PROTOTYPE(char is_zero, (bc_num num));
+_PROTOTYPE(char is_neg, (bc_num num));
+_PROTOTYPE(void bc_add, (bc_num n1, bc_num n2, bc_num *result));
+_PROTOTYPE(void bc_sub, (bc_num n1, bc_num n2, bc_num *result));
+_PROTOTYPE(void bc_multiply, (bc_num n1, bc_num n2, bc_num *prod, int scale));
+_PROTOTYPE(int bc_divide, (bc_num n1, bc_num n2, bc_num *quot, int scale));
+_PROTOTYPE(int bc_modulo, (bc_num num1, bc_num num2, bc_num *result, int scale));
+_PROTOTYPE(void bc_raise, (bc_num num1, bc_num num2, bc_num *result, int scale));
+_PROTOTYPE(int bc_sqrt, (bc_num *num, int scale));
+_PROTOTYPE(void out_long, (long val, int size, int space,
+ void (*out_char)(int)));
+_PROTOTYPE(void out_num, (bc_num num, int o_base, void (* out_char)(int)));
+
+
+/* From storage.c */
+_PROTOTYPE(void init_storage, (void));
+_PROTOTYPE(void more_functions, (void));
+_PROTOTYPE(void more_variables, (void));
+_PROTOTYPE(void more_arrays, (void));
+_PROTOTYPE(void clear_func, (int func ));
+_PROTOTYPE(int fpop, (void));
+_PROTOTYPE(void fpush, (int val ));
+_PROTOTYPE(void pop, (void));
+_PROTOTYPE(void push_copy, (bc_num num ));
+_PROTOTYPE(void push_num, (bc_num num ));
+_PROTOTYPE(char check_stack, (int depth ));
+_PROTOTYPE(bc_var *get_var, (int var_name ));
+_PROTOTYPE(bc_num *get_array_num, (int var_index, long index ));
+_PROTOTYPE(void store_var, (int var_name ));
+_PROTOTYPE(void store_array, (int var_name ));
+_PROTOTYPE(void load_var, (int var_name ));
+_PROTOTYPE(void load_array, (int var_name ));
+_PROTOTYPE(void decr_var, (int var_name ));
+_PROTOTYPE(void decr_array, (int var_name ));
+_PROTOTYPE(void incr_var, (int var_name ));
+_PROTOTYPE(void incr_array, (int var_name ));
+_PROTOTYPE(void auto_var, (int name ));
+_PROTOTYPE(void free_a_tree, (bc_array_node *root, int depth ));
+_PROTOTYPE(void pop_vars, (arg_list *list ));
+_PROTOTYPE(void process_params, (program_counter *pc, int func ));
+
+/* For the scanner and parser.... */
+_PROTOTYPE(int yyparse, (void));
+_PROTOTYPE(int yylex, (void));
+
+/* Other things... */
+_PROTOTYPE (int getopt, (int, char *[], CONST char *));
+
diff --git a/gnu/usr.bin/bc/scan.c b/gnu/usr.bin/bc/scan.c
new file mode 100644
index 0000000..59aa6e5
--- /dev/null
+++ b/gnu/usr.bin/bc/scan.c
@@ -0,0 +1,1368 @@
+/* A lexical scanner generated by flex */
+
+/* scanner skeleton version:
+ * $Header: /usr/fsys/odin/a/vern/flex/RCS/flex.skel,v 2.16 90/08/03 14:09:36 vern Exp $
+ */
+
+#define FLEX_SCANNER
+
+#include <stdio.h>
+
+
+/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+
+
+#ifdef __cplusplus
+
+#include <stdlib.h>
+#include <osfcn.h>
+
+/* use prototypes in function declarations */
+#define YY_USE_PROTOS
+
+/* the "const" storage-class-modifier is valid */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+#ifdef __STDC__
+
+#ifdef __GNUC__
+#include <stddef.h>
+void *malloc( size_t );
+void free( void* );
+#else
+#include <stdlib.h>
+#endif /* __GNUC__ */
+
+#define YY_USE_PROTOS
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+
+#ifdef __TURBOC__
+#define YY_USE_CONST
+#endif
+
+
+#ifndef YY_USE_CONST
+#define const
+#endif
+
+
+#ifdef YY_USE_PROTOS
+#define YY_PROTO(proto) proto
+#else
+#define YY_PROTO(proto) ()
+/* we can't get here if it's an ANSI C compiler, or a C++ compiler,
+ * so it's got to be a K&R compiler, and therefore there's no standard
+ * place from which to include these definitions
+ */
+/* char *malloc();
+int free(); */
+int read();
+#endif
+
+
+/* amount of stuff to slurp up with each read */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* returned upon end-of-file */
+#define YY_END_TOK 0
+
+/* copy whatever the last rule matched to the standard output */
+
+/* cast to (char *) is because for 8-bit chars, yytext is (unsigned char *) */
+/* this used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite()
+ */
+#define ECHO (void) fwrite( (char *) yytext, yyleng, 1, yyout )
+
+/* gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#define YY_INPUT(buf,result,max_size) \
+ if ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \
+ YY_FATAL_ERROR( "read() in flex scanner failed" );
+#define YY_NULL 0
+
+/* no semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#define yyterminate() return ( YY_NULL )
+
+/* report a fatal error */
+
+/* The funky do-while is used to turn this macro definition into
+ * a single C statement (which needs a semi-colon terminator).
+ * This avoids problems with code like:
+ *
+ * if ( something_happens )
+ * YY_FATAL_ERROR( "oops, the something happened" );
+ * else
+ * everything_okay();
+ *
+ * Prior to using the do-while the compiler would get upset at the
+ * "else" because it interpreted the "if" statement as being all
+ * done when it reached the ';' after the YY_FATAL_ERROR() call.
+ */
+
+#define YY_FATAL_ERROR(msg) \
+ do \
+ { \
+ (void) fputs( msg, stderr ); \
+ (void) putc( '\n', stderr ); \
+ exit( 1 ); \
+ } \
+ while ( 0 )
+
+/* default yywrap function - always treat EOF as an EOF */
+#define yywrap() 1
+
+/* enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN
+ */
+#define BEGIN yy_start = 1 + 2 *
+
+/* action number for EOF rule of a given start state */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* special action meaning "start processing a new file" */
+#define YY_NEW_FILE \
+ do \
+ { \
+ yy_init_buffer( yy_current_buffer, yyin ); \
+ yy_load_buffer_state(); \
+ } \
+ while ( 0 )
+
+/* default declaration of generated scanner - a define so the user can
+ * easily add parameters
+ */
+#define YY_DECL int yylex YY_PROTO(( void ))
+
+/* code executed at the end of each rule */
+#define YY_BREAK break;
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE (YY_READ_BUF_SIZE * 2) /* size of default input buffer */
+#endif
+
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+
+#define YY_CHAR unsigned char
+# line 1 "scan.l"
+#define INITIAL 0
+# line 2 "scan.l"
+/* scan.l: the (f)lex description file for the scanner. */
+
+/* This file is part of bc written for MINIX.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License , or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ You may contact the author by:
+ e-mail: phil@cs.wwu.edu
+ us-mail: Philip A. Nelson
+ Computer Science Department, 9062
+ Western Washington University
+ Bellingham, WA 98226-9062
+
+*************************************************************************/
+
+#include "bcdefs.h"
+#include "y.tab.h"
+#include "global.h"
+#include "proto.h"
+
+/* Using flex, we can ask for a smaller input buffer. With lex, this
+ does nothing! */
+
+#ifdef SMALL_BUF
+#undef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 512
+#endif
+
+/* We want to define our own yywrap. */
+#undef yywrap
+_PROTOTYPE(int yywrap, (void));
+
+/* MINIX returns from read with < 0 if SIGINT is encountered.
+ In flex, we can redefine YY_INPUT to the following. In lex, this
+ does nothing! */
+#include <errno.h>
+#undef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ while ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \
+ if (errno != EINTR) \
+ YY_FATAL_ERROR( "read() in flex scanner failed" );
+
+# line 60 "scan.l"
+
+/* done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext
+ */
+#define YY_DO_BEFORE_ACTION \
+ yytext = yy_bp; \
+ yyleng = yy_cp - yy_bp; \
+ yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yy_c_buf_p = yy_cp;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+/* return all but the first 'n' matched characters back to the input stream */
+#define yyless(n) \
+ do \
+ { \
+ /* undo effects of setting up yytext */ \
+ *yy_cp = yy_hold_char; \
+ yy_c_buf_p = yy_cp = yy_bp + n; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, yytext )
+
+
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ YY_CHAR *yy_ch_buf; /* input buffer */
+ YY_CHAR *yy_buf_pos; /* current position in input buffer */
+
+ /* size of input buffer in bytes, not including room for EOB characters*/
+ int yy_buf_size;
+
+ /* number of characters read into yy_ch_buf, not including EOB characters */
+ int yy_n_chars;
+
+ int yy_eof_status; /* whether we've seen an EOF on this buffer */
+#define EOF_NOT_SEEN 0
+ /* "pending" happens when the EOF has been seen but there's still
+ * some text process
+ */
+#define EOF_PENDING 1
+#define EOF_DONE 2
+ };
+
+static YY_BUFFER_STATE yy_current_buffer;
+
+/* we provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state"
+ */
+#define YY_CURRENT_BUFFER yy_current_buffer
+
+
+/* yy_hold_char holds the character lost when yytext is formed */
+static YY_CHAR yy_hold_char;
+
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+
+
+
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+#ifndef YY_USER_INIT
+#define YY_USER_INIT
+#endif
+
+extern YY_CHAR *yytext;
+extern int yyleng;
+extern FILE *yyin, *yyout;
+
+YY_CHAR *yytext;
+int yyleng;
+
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+
+#define YY_END_OF_BUFFER 40
+typedef int yy_state_type;
+static const short int yy_accept[144] =
+ { 0,
+ 0, 0, 40, 38, 33, 31, 25, 38, 26, 38,
+ 22, 26, 22, 22, 38, 26, 37, 29, 27, 29,
+ 38, 22, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 38, 33,
+ 29, 0, 36, 27, 23, 30, 37, 0, 34, 37,
+ 37, 0, 28, 32, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 7, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 24, 37, 0, 0, 37,
+ 0, 35, 35, 35, 35, 35, 6, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+
+ 35, 13, 35, 35, 35, 14, 16, 35, 17, 35,
+ 35, 35, 35, 3, 15, 35, 35, 9, 35, 35,
+ 2, 35, 35, 11, 35, 35, 12, 20, 35, 10,
+ 35, 8, 35, 1, 4, 21, 5, 35, 35, 35,
+ 19, 18, 0
+ } ;
+
+static const YY_CHAR yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 4, 5, 1, 1, 6, 7, 1, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 1, 17, 18,
+ 19, 20, 1, 1, 21, 21, 21, 21, 21, 21,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 22, 23, 24, 25, 26, 1, 27, 28, 29, 30,
+
+ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 36, 48, 36,
+ 49, 36, 50, 51, 52, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static const YY_CHAR yy_meta[53] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 2, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
+ 1, 1
+ } ;
+
+static const short int yy_base[146] =
+ { 0,
+ 0, 0, 193, 194, 190, 194, 172, 185, 170, 181,
+ 194, 168, 42, 41, 41, 46, 52, 167, 61, 166,
+ 181, 164, 135, 137, 139, 148, 140, 136, 0, 149,
+ 27, 50, 147, 130, 126, 141, 40, 36, 120, 168,
+ 194, 164, 194, 194, 194, 194, 66, 165, 194, 72,
+ 76, 164, 194, 194, 0, 120, 134, 124, 131, 117,
+ 117, 122, 132, 0, 113, 117, 117, 128, 119, 118,
+ 52, 125, 107, 106, 114, 194, 80, 145, 84, 88,
+ 144, 105, 118, 98, 108, 111, 0, 95, 95, 93,
+ 105, 102, 91, 95, 88, 103, 85, 93, 84, 85,
+
+ 90, 0, 90, 91, 85, 0, 0, 93, 0, 77,
+ 76, 90, 74, 0, 0, 75, 87, 0, 90, 85,
+ 0, 75, 83, 0, 76, 63, 0, 0, 66, 0,
+ 62, 0, 47, 0, 0, 0, 0, 45, 53, 29,
+ 0, 0, 194, 111, 56
+ } ;
+
+static const short int yy_def[146] =
+ { 0,
+ 143, 1, 143, 143, 143, 143, 143, 144, 143, 143,
+ 143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
+ 143, 143, 145, 145, 145, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 145, 145, 145, 143, 143,
+ 143, 144, 143, 143, 143, 143, 143, 143, 143, 143,
+ 143, 143, 143, 143, 145, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 143, 143, 143, 143, 143,
+ 143, 145, 145, 145, 145, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
+
+ 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
+ 145, 145, 0, 143, 143
+ } ;
+
+static const short int yy_nxt[247] =
+ { 0,
+ 4, 5, 6, 7, 8, 9, 10, 11, 11, 12,
+ 13, 11, 14, 15, 16, 17, 11, 18, 19, 20,
+ 17, 11, 21, 11, 22, 4, 23, 24, 25, 26,
+ 27, 28, 29, 30, 31, 29, 29, 32, 29, 29,
+ 33, 34, 35, 36, 37, 29, 29, 38, 29, 11,
+ 39, 11, 46, 46, 63, 49, 47, 55, 64, 44,
+ 44, 47, 74, 48, 44, 50, 53, 51, 72, 75,
+ 53, 53, 51, 53, 52, 53, 65, 142, 96, 41,
+ 66, 77, 73, 141, 67, 53, 77, 80, 78, 50,
+ 140, 51, 80, 139, 81, 77, 51, 97, 52, 47,
+
+ 77, 138, 78, 80, 47, 137, 48, 136, 80, 135,
+ 81, 42, 42, 134, 133, 132, 131, 130, 129, 128,
+ 127, 126, 125, 124, 123, 122, 121, 120, 119, 118,
+ 117, 116, 115, 114, 113, 112, 111, 110, 109, 108,
+ 107, 106, 105, 104, 103, 102, 80, 77, 101, 100,
+ 99, 98, 95, 94, 93, 92, 91, 90, 89, 88,
+ 87, 86, 85, 84, 83, 82, 51, 79, 43, 40,
+ 76, 71, 70, 69, 68, 62, 61, 60, 59, 58,
+ 57, 56, 44, 54, 41, 41, 44, 45, 44, 43,
+ 41, 40, 143, 3, 143, 143, 143, 143, 143, 143,
+
+ 143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
+ 143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
+ 143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
+ 143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
+ 143, 143, 143, 143, 143, 143
+ } ;
+
+static const short int yy_chk[247] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 13, 14, 31, 16, 15, 145, 31, 14,
+ 13, 15, 38, 15, 16, 17, 19, 17, 37, 38,
+ 19, 19, 17, 19, 17, 19, 32, 140, 71, 19,
+ 32, 47, 37, 139, 32, 19, 47, 50, 47, 51,
+ 138, 51, 50, 133, 50, 77, 51, 71, 51, 79,
+
+ 77, 131, 77, 80, 79, 129, 79, 126, 80, 125,
+ 80, 144, 144, 123, 122, 120, 119, 117, 116, 113,
+ 112, 111, 110, 108, 105, 104, 103, 101, 100, 99,
+ 98, 97, 96, 95, 94, 93, 92, 91, 90, 89,
+ 88, 86, 85, 84, 83, 82, 81, 78, 75, 74,
+ 73, 72, 70, 69, 68, 67, 66, 65, 63, 62,
+ 61, 60, 59, 58, 57, 56, 52, 48, 42, 40,
+ 39, 36, 35, 34, 33, 30, 28, 27, 26, 25,
+ 24, 23, 22, 21, 20, 18, 12, 10, 9, 8,
+ 7, 5, 3, 143, 143, 143, 143, 143, 143, 143,
+
+ 143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
+ 143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
+ 143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
+ 143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
+ 143, 143, 143, 143, 143, 143
+ } ;
+
+static yy_state_type yy_last_accepting_state;
+static YY_CHAR *yy_last_accepting_cpos;
+
+/* the intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+
+/* these variables are all declared out here so that section 3 code can
+ * manipulate them
+ */
+/* points to current character in buffer */
+static YY_CHAR *yy_c_buf_p = (YY_CHAR *) 0;
+static int yy_init = 1; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+static yy_state_type yy_get_previous_state YY_PROTO(( void ));
+static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state ));
+static int yy_get_next_buffer YY_PROTO(( void ));
+static void yyunput YY_PROTO(( YY_CHAR c, YY_CHAR *buf_ptr ));
+void yyrestart YY_PROTO(( FILE *input_file ));
+void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer ));
+void yy_load_buffer_state YY_PROTO(( void ));
+YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size ));
+void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file ));
+
+#define yy_new_buffer yy_create_buffer
+
+#ifdef __cplusplus
+static int yyinput YY_PROTO(( void ));
+#else
+static int input YY_PROTO(( void ));
+#endif
+
+YY_DECL
+ {
+ register yy_state_type yy_current_state;
+ register YY_CHAR *yy_cp, *yy_bp;
+ register int yy_act;
+
+
+
+ if ( yy_init )
+ {
+ YY_USER_INIT;
+
+ if ( ! yy_start )
+ yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( yy_current_buffer )
+ yy_init_buffer( yy_current_buffer, yyin );
+ else
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE );
+
+ yy_load_buffer_state();
+
+ yy_init = 0;
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = yy_c_buf_p;
+
+ /* support of yytext */
+ *yy_cp = yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of the
+ * current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yy_start;
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[*yy_cp];
+ if ( yy_accept[yy_current_state] )
+ {
+ yy_last_accepting_state = yy_current_state;
+ yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = yy_def[yy_current_state];
+ if ( yy_current_state >= 144 )
+ yy_c = yy_meta[yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 194 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+
+ YY_DO_BEFORE_ACTION;
+ YY_USER_ACTION;
+
+do_action: /* this label is used only to access EOF actions */
+
+
+ switch ( yy_act )
+ {
+ case 0: /* must backtrack */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = yy_hold_char;
+ yy_cp = yy_last_accepting_cpos;
+ yy_current_state = yy_last_accepting_state;
+ goto yy_find_action;
+
+case 1:
+# line 61 "scan.l"
+return(Define);
+ YY_BREAK
+case 2:
+# line 62 "scan.l"
+return(Break);
+ YY_BREAK
+case 3:
+# line 63 "scan.l"
+return(Quit);
+ YY_BREAK
+case 4:
+# line 64 "scan.l"
+return(Length);
+ YY_BREAK
+case 5:
+# line 65 "scan.l"
+return(Return);
+ YY_BREAK
+case 6:
+# line 66 "scan.l"
+return(For);
+ YY_BREAK
+case 7:
+# line 67 "scan.l"
+return(If);
+ YY_BREAK
+case 8:
+# line 68 "scan.l"
+return(While);
+ YY_BREAK
+case 9:
+# line 69 "scan.l"
+return(Sqrt);
+ YY_BREAK
+case 10:
+# line 70 "scan.l"
+return(Scale);
+ YY_BREAK
+case 11:
+# line 71 "scan.l"
+return(Ibase);
+ YY_BREAK
+case 12:
+# line 72 "scan.l"
+return(Obase);
+ YY_BREAK
+case 13:
+# line 73 "scan.l"
+return(Auto);
+ YY_BREAK
+case 14:
+# line 74 "scan.l"
+return(Else);
+ YY_BREAK
+case 15:
+# line 75 "scan.l"
+return(Read);
+ YY_BREAK
+case 16:
+# line 76 "scan.l"
+return(Halt);
+ YY_BREAK
+case 17:
+# line 77 "scan.l"
+return(Last);
+ YY_BREAK
+case 18:
+# line 78 "scan.l"
+return(Warranty);
+ YY_BREAK
+case 19:
+# line 79 "scan.l"
+return(Continue);
+ YY_BREAK
+case 20:
+# line 80 "scan.l"
+return(Print);
+ YY_BREAK
+case 21:
+# line 81 "scan.l"
+return(Limits);
+ YY_BREAK
+case 22:
+# line 82 "scan.l"
+{ yylval.c_value = yytext[0];
+ return((int)yytext[0]); }
+ YY_BREAK
+case 23:
+# line 84 "scan.l"
+{ return(AND); }
+ YY_BREAK
+case 24:
+# line 85 "scan.l"
+{ return(OR); }
+ YY_BREAK
+case 25:
+# line 86 "scan.l"
+{ return(NOT); }
+ YY_BREAK
+case 26:
+# line 87 "scan.l"
+{ yylval.c_value = yytext[0]; return(MUL_OP); }
+ YY_BREAK
+case 27:
+# line 88 "scan.l"
+{ yylval.c_value = yytext[0]; return(ASSIGN_OP); }
+ YY_BREAK
+case 28:
+# line 89 "scan.l"
+{
+#ifdef OLD_EQ_OP
+ char warn_save;
+ warn_save = warn_not_std;
+ warn_not_std = TRUE;
+ warn ("Old fashioned =<op>");
+ warn_not_std = warn_save;
+ yylval.c_value = yytext[1];
+#else
+ yylval.c_value = '=';
+ yyless (1);
+#endif
+ return(ASSIGN_OP);
+ }
+ YY_BREAK
+case 29:
+# line 103 "scan.l"
+{ yylval.s_value = strcopyof(yytext); return(REL_OP); }
+ YY_BREAK
+case 30:
+# line 104 "scan.l"
+{ yylval.c_value = yytext[0]; return(INCR_DECR); }
+ YY_BREAK
+case 31:
+# line 105 "scan.l"
+{ line_no++; return(NEWLINE); }
+ YY_BREAK
+case 32:
+# line 106 "scan.l"
+{ line_no++; /* ignore a "quoted" newline */ }
+ YY_BREAK
+case 33:
+# line 107 "scan.l"
+{ /* ignore spaces and tabs */ }
+ YY_BREAK
+case 34:
+# line 108 "scan.l"
+{
+ int c;
+
+ for (;;)
+ {
+ while ( ((c=input()) != '*') && (c != EOF))
+ /* eat it */
+ if (c == '\n') line_no++;
+ if (c == '*')
+ {
+ while ( (c=input()) == '*') /* eat it*/;
+ if (c == '/') break; /* at end of comment */
+ if (c == '\n') line_no++;
+ }
+ if (c == EOF)
+ {
+ fprintf (stderr,"EOF encountered in a comment.\n");
+ break;
+ }
+ }
+ }
+ YY_BREAK
+case 35:
+# line 129 "scan.l"
+{ yylval.s_value = strcopyof(yytext); return(NAME); }
+ YY_BREAK
+case 36:
+# line 130 "scan.l"
+{
+ unsigned char *look;
+ int count = 0;
+ yylval.s_value = strcopyof(yytext);
+ for (look = yytext; *look != 0; look++)
+ {
+ if (*look == '\n') line_no++;
+ if (*look == '"') count++;
+ }
+ if (count != 2) yyerror ("NUL character in string.");
+ return(STRING);
+ }
+ YY_BREAK
+case 37:
+# line 142 "scan.l"
+{
+ unsigned char *src, *dst;
+ int len;
+ /* remove a trailing decimal point. */
+ len = strlen(yytext);
+ if (yytext[len-1] == '.')
+ yytext[len-1] = 0;
+ /* remove leading zeros. */
+ src = yytext;
+ dst = yytext;
+ while (*src == '0') src++;
+ if (*src == 0) src--;
+ /* Copy strings removing the newlines. */
+ while (*src != 0)
+ {
+ if (*src == '\\')
+ {
+ src++; src++;
+ line_no++;
+ }
+ else
+ *dst++ = *src++;
+ }
+ *dst = 0;
+ yylval.s_value = strcopyof(yytext);
+ return(NUMBER);
+ }
+ YY_BREAK
+case 38:
+# line 169 "scan.l"
+{
+ if (yytext[0] < ' ')
+ yyerror ("illegal character: ^%c",yytext[0] + '@');
+ else
+ if (yytext[0] > '~')
+ yyerror ("illegal character: \\%3d", (int) yytext[0]);
+ else
+ yyerror ("illegal character: %s",yytext);
+ }
+ YY_BREAK
+case 39:
+# line 178 "scan.l"
+ECHO;
+ YY_BREAK
+case YY_STATE_EOF(INITIAL):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* amount of text matched not including the EOB char */
+ int yy_amount_of_matched_text = yy_cp - yytext - 1;
+
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = yy_hold_char;
+
+ /* note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the end-
+ * of-buffer state). Contrast this with the test in yyinput().
+ */
+ if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+ /* this was really a NUL */
+ {
+ yy_state_type yy_next_state;
+
+ yy_c_buf_p = yytext + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state();
+
+ /* okay, we're now positioned to make the
+ * NUL transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we
+ * don't want to build jamming into it because
+ * then it will run more slowly)
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = yytext + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* consume the NUL */
+ yy_cp = ++yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer() )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yy_did_buffer_switch_on_eof = 0;
+
+ if ( yywrap() )
+ {
+ /* note: because we've taken care in
+ * yy_get_next_buffer() to have set up yytext,
+ * we can now set up yy_c_buf_p so that if some
+ * total hoser (like flex itself) wants
+ * to call the scanner after we return the
+ * YY_NULL, it'll still work - another YY_NULL
+ * will get returned.
+ */
+ yy_c_buf_p = yytext + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF((yy_start - 1) / 2);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ }
+ break;
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yy_c_buf_p = yytext + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state();
+
+ yy_cp = yy_c_buf_p;
+ yy_bp = yytext + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yy_c_buf_p =
+ &yy_current_buffer->yy_ch_buf[yy_n_chars];
+
+ yy_current_state = yy_get_previous_state();
+
+ yy_cp = yy_c_buf_p;
+ yy_bp = yytext + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+#ifdef FLEX_DEBUG
+ printf( "action # %d\n", yy_act );
+#endif
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ }
+ }
+ }
+
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * synopsis
+ * int yy_get_next_buffer();
+ *
+ * returns a code representing an action
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+
+static int yy_get_next_buffer()
+
+ {
+ register YY_CHAR *dest = yy_current_buffer->yy_ch_buf;
+ register YY_CHAR *source = yytext - 1; /* copy prev. char, too */
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ /* try to read more data */
+
+ /* first move last chars to start of buffer */
+ number_to_move = yy_c_buf_p - yytext;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( yy_current_buffer->yy_eof_status != EOF_NOT_SEEN )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read = yy_current_buffer->yy_buf_size - number_to_move - 1;
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ else if ( num_to_read <= 0 )
+ YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" );
+
+ /* read in more data */
+ YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]),
+ yy_n_chars, num_to_read );
+ }
+
+ if ( yy_n_chars == 0 )
+ {
+ if ( number_to_move == 1 )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yy_current_buffer->yy_eof_status = EOF_DONE;
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ yy_current_buffer->yy_eof_status = EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ yy_n_chars += number_to_move;
+ yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ /* yytext begins at the second character in yy_ch_buf; the first
+ * character is the one which preceded it before reading in the latest
+ * buffer; it needs to be kept around in case it's a newline, so
+ * yy_get_previous_state() will have with '^' rules active
+ */
+
+ yytext = &yy_current_buffer->yy_ch_buf[1];
+
+ return ( ret_val );
+ }
+
+
+/* yy_get_previous_state - get the state just before the EOB char was reached
+ *
+ * synopsis
+ * yy_state_type yy_get_previous_state();
+ */
+
+static yy_state_type yy_get_previous_state()
+
+ {
+ register yy_state_type yy_current_state;
+ register YY_CHAR *yy_cp;
+
+ yy_current_state = yy_start;
+
+ for ( yy_cp = yytext + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[*yy_cp] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ yy_last_accepting_state = yy_current_state;
+ yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = yy_def[yy_current_state];
+ if ( yy_current_state >= 144 )
+ yy_c = yy_meta[yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+ }
+
+ return ( yy_current_state );
+ }
+
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+
+#ifdef YY_USE_PROTOS
+static yy_state_type yy_try_NUL_trans( register yy_state_type yy_current_state )
+#else
+static yy_state_type yy_try_NUL_trans( yy_current_state )
+register yy_state_type yy_current_state;
+#endif
+
+ {
+ register int yy_is_jam;
+ register YY_CHAR *yy_cp = yy_c_buf_p;
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ yy_last_accepting_state = yy_current_state;
+ yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = yy_def[yy_current_state];
+ if ( yy_current_state >= 144 )
+ yy_c = yy_meta[yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+ yy_is_jam = (yy_current_state == 143);
+
+ return ( yy_is_jam ? 0 : yy_current_state );
+ }
+
+
+#ifdef YY_USE_PROTOS
+static void yyunput( YY_CHAR c, register YY_CHAR *yy_bp )
+#else
+static void yyunput( c, yy_bp )
+YY_CHAR c;
+register YY_CHAR *yy_bp;
+#endif
+
+ {
+ register YY_CHAR *yy_cp = yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yy_hold_char;
+
+ if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ register int number_to_move = yy_n_chars + 2; /* +2 for EOB chars */
+ register YY_CHAR *dest =
+ &yy_current_buffer->yy_ch_buf[yy_current_buffer->yy_buf_size + 2];
+ register YY_CHAR *source =
+ &yy_current_buffer->yy_ch_buf[number_to_move];
+
+ while ( source > yy_current_buffer->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += dest - source;
+ yy_bp += dest - source;
+ yy_n_chars = yy_current_buffer->yy_buf_size;
+
+ if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ if ( yy_cp > yy_bp && yy_cp[-1] == '\n' )
+ yy_cp[-2] = '\n';
+
+ *--yy_cp = c;
+
+ /* note: the formal parameter *must* be called "yy_bp" for this
+ * macro to now work correctly
+ */
+ YY_DO_BEFORE_ACTION; /* set up yytext again */
+ }
+
+
+#ifdef __cplusplus
+static int yyinput()
+#else
+static int input()
+#endif
+
+ {
+ int c;
+ YY_CHAR *yy_cp = yy_c_buf_p;
+
+ *yy_cp = yy_hold_char;
+
+ if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+ /* this was really a NUL */
+ *yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ yytext = yy_c_buf_p;
+ ++yy_c_buf_p;
+
+ switch ( yy_get_next_buffer() )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap() )
+ {
+ yy_c_buf_p = yytext + YY_MORE_ADJ;
+ return ( EOF );
+ }
+
+ YY_NEW_FILE;
+
+#ifdef __cplusplus
+ return ( yyinput() );
+#else
+ return ( input() );
+#endif
+ }
+ break;
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yy_c_buf_p = yytext + YY_MORE_ADJ;
+ break;
+
+ case EOB_ACT_LAST_MATCH:
+#ifdef __cplusplus
+ YY_FATAL_ERROR( "unexpected last match in yyinput()" );
+#else
+ YY_FATAL_ERROR( "unexpected last match in input()" );
+#endif
+ }
+ }
+ }
+
+ c = *yy_c_buf_p;
+ yy_hold_char = *++yy_c_buf_p;
+
+ return ( c );
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yyrestart( FILE *input_file )
+#else
+void yyrestart( input_file )
+FILE *input_file;
+#endif
+
+ {
+ yy_init_buffer( yy_current_buffer, input_file );
+ yy_load_buffer_state();
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+#else
+void yy_switch_to_buffer( new_buffer )
+YY_BUFFER_STATE new_buffer;
+#endif
+
+ {
+ if ( yy_current_buffer == new_buffer )
+ return;
+
+ if ( yy_current_buffer )
+ {
+ /* flush out information for old buffer */
+ *yy_c_buf_p = yy_hold_char;
+ yy_current_buffer->yy_buf_pos = yy_c_buf_p;
+ yy_current_buffer->yy_n_chars = yy_n_chars;
+ }
+
+ yy_current_buffer = new_buffer;
+ yy_load_buffer_state();
+
+ /* we don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yy_did_buffer_switch_on_eof = 1;
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_load_buffer_state( void )
+#else
+void yy_load_buffer_state()
+#endif
+
+ {
+ yy_n_chars = yy_current_buffer->yy_n_chars;
+ yytext = yy_c_buf_p = yy_current_buffer->yy_buf_pos;
+ yyin = yy_current_buffer->yy_input_file;
+ yy_hold_char = *yy_c_buf_p;
+ }
+
+
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
+#else
+YY_BUFFER_STATE yy_create_buffer( file, size )
+FILE *file;
+int size;
+#endif
+
+ {
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) malloc( sizeof( struct yy_buffer_state ) );
+
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (YY_CHAR *) malloc( (unsigned) (b->yy_buf_size + 2) );
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ yy_init_buffer( b, file );
+
+ return ( b );
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_delete_buffer( YY_BUFFER_STATE b )
+#else
+void yy_delete_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+
+ {
+ if ( b == yy_current_buffer )
+ yy_current_buffer = (YY_BUFFER_STATE) 0;
+
+ free( (char *) b->yy_ch_buf );
+ free( (char *) b );
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_init_buffer( YY_BUFFER_STATE b, FILE *file )
+#else
+void yy_init_buffer( b, file )
+YY_BUFFER_STATE b;
+FILE *file;
+#endif
+
+ {
+ b->yy_input_file = file;
+
+ /* we put in the '\n' and start reading from [1] so that an
+ * initial match-at-newline will be true.
+ */
+
+ b->yy_ch_buf[0] = '\n';
+ b->yy_n_chars = 1;
+
+ /* we always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[2] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[1];
+
+ b->yy_eof_status = EOF_NOT_SEEN;
+ }
+# line 178 "scan.l"
+
+
+
+
+/* This is the way to get multiple files input into lex. */
+
+int
+yywrap()
+{
+ if (!open_new_file ()) return (1); /* EOF on standard in. */
+ return (0); /* We have more input. */
+}
diff --git a/gnu/usr.bin/bc/storage.c b/gnu/usr.bin/bc/storage.c
new file mode 100644
index 0000000..1edd6e2
--- /dev/null
+++ b/gnu/usr.bin/bc/storage.c
@@ -0,0 +1,967 @@
+/* storage.c: Code and data storage manipulations. This includes labels. */
+
+/* This file is part of bc written for MINIX.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License , or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ You may contact the author by:
+ e-mail: phil@cs.wwu.edu
+ us-mail: Philip A. Nelson
+ Computer Science Department, 9062
+ Western Washington University
+ Bellingham, WA 98226-9062
+
+*************************************************************************/
+
+#include "bcdefs.h"
+#include "global.h"
+#include "proto.h"
+
+
+/* Initialize the storage at the beginning of the run. */
+
+void
+init_storage ()
+{
+
+ /* Functions: we start with none and ask for more. */
+ f_count = 0;
+ more_functions ();
+ f_names[0] = "(main)";
+
+ /* Variables. */
+ v_count = 0;
+ more_variables ();
+
+ /* Arrays. */
+ a_count = 0;
+ more_arrays ();
+
+ /* Other things... */
+ ex_stack = NULL;
+ fn_stack = NULL;
+ i_base = 10;
+ o_base = 10;
+ scale = 0;
+ c_code = FALSE;
+ init_numbers();
+}
+
+/* Three functions for increasing the number of functions, variables, or
+ arrays that are needed. This adds another 32 of the requested object. */
+
+void
+more_functions (VOID)
+{
+ int old_count;
+ int indx1, indx2;
+ bc_function *old_f;
+ bc_function *f;
+ char **old_names;
+
+ /* Save old information. */
+ old_count = f_count;
+ old_f = functions;
+ old_names = f_names;
+
+ /* Add a fixed amount and allocate new space. */
+ f_count += STORE_INCR;
+ functions = (bc_function *) bc_malloc (f_count*sizeof (bc_function));
+ f_names = (char **) bc_malloc (f_count*sizeof (char *));
+
+ /* Copy old ones. */
+ for (indx1 = 0; indx1 < old_count; indx1++)
+ {
+ functions[indx1] = old_f[indx1];
+ f_names[indx1] = old_names[indx1];
+ }
+
+ /* Initialize the new ones. */
+ for (; indx1 < f_count; indx1++)
+ {
+ f = &functions[indx1];
+ f->f_defined = FALSE;
+ for (indx2 = 0; indx2 < BC_MAX_SEGS; indx2++)
+ f->f_body [indx2] = NULL;
+ f->f_code_size = 0;
+ f->f_label = NULL;
+ f->f_autos = NULL;
+ f->f_params = NULL;
+ }
+
+ /* Free the old elements. */
+ if (old_count != 0)
+ {
+ free (old_f);
+ free (old_names);
+ }
+}
+
+void
+more_variables ()
+{
+ int indx;
+ int old_count;
+ bc_var **old_var;
+ char **old_names;
+
+ /* Save the old values. */
+ old_count = v_count;
+ old_var = variables;
+ old_names = v_names;
+
+ /* Increment by a fixed amount and allocate. */
+ v_count += STORE_INCR;
+ variables = (bc_var **) bc_malloc (v_count*sizeof(bc_var *));
+ v_names = (char **) bc_malloc (v_count*sizeof(char *));
+
+ /* Copy the old variables. */
+ for (indx = 3; indx < old_count; indx++)
+ variables[indx] = old_var[indx];
+
+ /* Initialize the new elements. */
+ for (; indx < v_count; indx++)
+ variables[indx] = NULL;
+
+ /* Free the old elements. */
+ if (old_count != 0)
+ {
+ free (old_var);
+ free (old_names);
+ }
+}
+
+void
+more_arrays ()
+{
+ int indx;
+ int old_count;
+ bc_var_array **old_ary;
+ char **old_names;
+
+ /* Save the old values. */
+ old_count = a_count;
+ old_ary = arrays;
+ old_names = a_names;
+
+ /* Increment by a fixed amount and allocate. */
+ a_count += STORE_INCR;
+ arrays = (bc_var_array **) bc_malloc (a_count*sizeof(bc_var_array *));
+ a_names = (char **) bc_malloc (a_count*sizeof(char *));
+
+ /* Copy the old arrays. */
+ for (indx = 1; indx < old_count; indx++)
+ arrays[indx] = old_ary[indx];
+
+
+ /* Initialize the new elements. */
+ for (; indx < v_count; indx++)
+ arrays[indx] = NULL;
+
+ /* Free the old elements. */
+ if (old_count != 0)
+ {
+ free (old_ary);
+ free (old_names);
+ }
+}
+
+
+/* clear_func clears out function FUNC and makes it ready to redefine. */
+
+void
+clear_func (func)
+ char func;
+{
+ bc_function *f;
+ int indx;
+ bc_label_group *lg;
+
+ /* Set the pointer to the function. */
+ f = &functions[func];
+ f->f_defined = FALSE;
+
+ /* Clear the code segments. */
+ for (indx = 0; indx < BC_MAX_SEGS; indx++)
+ {
+ if (f->f_body[indx] != NULL)
+ {
+ free (f->f_body[indx]);
+ f->f_body[indx] = NULL;
+ }
+ }
+
+ f->f_code_size = 0;
+ if (f->f_autos != NULL)
+ {
+ free_args (f->f_autos);
+ f->f_autos = NULL;
+ }
+ if (f->f_params != NULL)
+ {
+ free_args (f->f_params);
+ f->f_params = NULL;
+ }
+ while (f->f_label != NULL)
+ {
+ lg = f->f_label->l_next;
+ free (f->f_label);
+ f->f_label = lg;
+ }
+}
+
+
+/* Pop the function execution stack and return the top. */
+
+int
+fpop()
+{
+ fstack_rec *temp;
+ int retval;
+
+ if (fn_stack != NULL)
+ {
+ temp = fn_stack;
+ fn_stack = temp->s_next;
+ retval = temp->s_val;
+ free (temp);
+ }
+ return (retval);
+}
+
+
+/* Push VAL on to the function stack. */
+
+void
+fpush (val)
+ int val;
+{
+ fstack_rec *temp;
+
+ temp = (fstack_rec *) bc_malloc (sizeof (fstack_rec));
+ temp->s_next = fn_stack;
+ temp->s_val = val;
+ fn_stack = temp;
+}
+
+
+/* Pop and discard the top element of the regular execution stack. */
+
+void
+pop ()
+{
+ estack_rec *temp;
+
+ if (ex_stack != NULL)
+ {
+ temp = ex_stack;
+ ex_stack = temp->s_next;
+ free_num (&temp->s_num);
+ free (temp);
+ }
+}
+
+
+/* Push a copy of NUM on to the regular execution stack. */
+
+void
+push_copy (num)
+ bc_num num;
+{
+ estack_rec *temp;
+
+ temp = (estack_rec *) bc_malloc (sizeof (estack_rec));
+ temp->s_num = copy_num (num);
+ temp->s_next = ex_stack;
+ ex_stack = temp;
+}
+
+
+/* Push NUM on to the regular execution stack. Do NOT push a copy. */
+
+void
+push_num (num)
+ bc_num num;
+{
+ estack_rec *temp;
+
+ temp = (estack_rec *) bc_malloc (sizeof (estack_rec));
+ temp->s_num = num;
+ temp->s_next = ex_stack;
+ ex_stack = temp;
+}
+
+
+/* Make sure the ex_stack has at least DEPTH elements on it.
+ Return TRUE if it has at least DEPTH elements, otherwise
+ return FALSE. */
+
+char
+check_stack (depth)
+ int depth;
+{
+ estack_rec *temp;
+
+ temp = ex_stack;
+ while ((temp != NULL) && (depth > 0))
+ {
+ temp = temp->s_next;
+ depth--;
+ }
+ if (depth > 0)
+ {
+ rt_error ("Stack error.");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/* The following routines manipulate simple variables and
+ array variables. */
+
+/* get_var returns a pointer to the variable VAR_NAME. If one does not
+ exist, one is created. */
+
+bc_var *
+get_var (var_name)
+ int var_name;
+{
+ bc_var *var_ptr;
+
+ var_ptr = variables[var_name];
+ if (var_ptr == NULL)
+ {
+ var_ptr = variables[var_name] = (bc_var *) bc_malloc (sizeof (bc_var));
+ init_num (&var_ptr->v_value);
+ }
+ return var_ptr;
+}
+
+
+/* get_array_num returns the address of the bc_num in the array
+ structure. If more structure is requried to get to the index,
+ this routine does the work to create that structure. VAR_INDEX
+ is a zero based index into the arrays storage array. INDEX is
+ the index into the bc array. */
+
+bc_num *
+get_array_num (var_index, index)
+ int var_index;
+ long index;
+{
+ bc_var_array *ary_ptr;
+ bc_array *a_var;
+ bc_array_node *temp;
+ int log, ix, ix1;
+ int sub [NODE_DEPTH];
+
+ /* Get the array entry. */
+ ary_ptr = arrays[var_index];
+ if (ary_ptr == NULL)
+ {
+ ary_ptr = arrays[var_index] =
+ (bc_var_array *) bc_malloc (sizeof (bc_var_array));
+ ary_ptr->a_value = NULL;
+ ary_ptr->a_next = NULL;
+ ary_ptr->a_param = FALSE;
+ }
+
+ a_var = ary_ptr->a_value;
+ if (a_var == NULL) {
+ a_var = ary_ptr->a_value = (bc_array *) bc_malloc (sizeof (bc_array));
+ a_var->a_tree = NULL;
+ a_var->a_depth = 0;
+ }
+
+ /* Get the index variable. */
+ sub[0] = index & NODE_MASK;
+ ix = index >> NODE_SHIFT;
+ log = 1;
+ while (ix > 0 || log < a_var->a_depth)
+ {
+ sub[log] = ix & NODE_MASK;
+ ix >>= NODE_SHIFT;
+ log++;
+ }
+
+ /* Build any tree that is necessary. */
+ while (log > a_var->a_depth)
+ {
+ temp = (bc_array_node *) bc_malloc (sizeof(bc_array_node));
+ if (a_var->a_depth != 0)
+ {
+ temp->n_items.n_down[0] = a_var->a_tree;
+ for (ix=1; ix < NODE_SIZE; ix++)
+ temp->n_items.n_down[ix] = NULL;
+ }
+ else
+ {
+ for (ix=0; ix < NODE_SIZE; ix++)
+ temp->n_items.n_num[ix] = copy_num(_zero_);
+ }
+ a_var->a_tree = temp;
+ a_var->a_depth++;
+ }
+
+ /* Find the indexed variable. */
+ temp = a_var->a_tree;
+ while ( log-- > 1)
+ {
+ ix1 = sub[log];
+ if (temp->n_items.n_down[ix1] == NULL)
+ {
+ temp->n_items.n_down[ix1] =
+ (bc_array_node *) bc_malloc (sizeof(bc_array_node));
+ temp = temp->n_items.n_down[ix1];
+ if (log > 1)
+ for (ix=0; ix < NODE_SIZE; ix++)
+ temp->n_items.n_down[ix] = NULL;
+ else
+ for (ix=0; ix < NODE_SIZE; ix++)
+ temp->n_items.n_num[ix] = copy_num(_zero_);
+ }
+ else
+ temp = temp->n_items.n_down[ix1];
+ }
+
+ /* Return the address of the indexed variable. */
+ return &(temp->n_items.n_num[sub[0]]);
+}
+
+
+/* Store the top of the execution stack into VAR_NAME.
+ This includes the special variables ibase, obase, and scale. */
+
+void
+store_var (var_name)
+ int var_name;
+{
+ bc_var *var_ptr;
+ long temp;
+ char toobig;
+
+ if (var_name > 2)
+ {
+ /* It is a simple variable. */
+ var_ptr = get_var (var_name);
+ if (var_ptr != NULL)
+ {
+ free_num(&var_ptr->v_value);
+ var_ptr->v_value = copy_num (ex_stack->s_num);
+ }
+ }
+ else
+ {
+ /* It is a special variable... */
+ toobig = FALSE;
+ if (is_neg (ex_stack->s_num))
+ {
+ switch (var_name)
+ {
+ case 0:
+ rt_warn ("negative ibase, set to 2");
+ temp = 2;
+ break;
+ case 1:
+ rt_warn ("negative obase, set to 2");
+ temp = 2;
+ break;
+ case 2:
+ rt_warn ("negative scale, set to 0");
+ temp = 0;
+ break;
+ }
+ }
+ else
+ {
+ temp = num2long (ex_stack->s_num);
+ if (!is_zero (ex_stack->s_num) && temp == 0)
+ toobig = TRUE;
+ }
+ switch (var_name)
+ {
+ case 0:
+ if (temp < 2 && !toobig)
+ {
+ i_base = 2;
+ rt_warn ("ibase too small, set to 2");
+ }
+ else
+ if (temp > 16 || toobig)
+ {
+ i_base = 16;
+ rt_warn ("ibase too large, set to 16");
+ }
+ else
+ i_base = (int) temp;
+ break;
+
+ case 1:
+ if (temp < 2 && !toobig)
+ {
+ o_base = 2;
+ rt_warn ("obase too small, set to 2");
+ }
+ else
+ if (temp > BC_BASE_MAX || toobig)
+ {
+ o_base = BC_BASE_MAX;
+ rt_warn ("obase too large, set to %d", BC_BASE_MAX);
+ }
+ else
+ o_base = (int) temp;
+ break;
+
+ case 2:
+ /* WARNING: The following if statement may generate a compiler
+ warning if INT_MAX == LONG_MAX. This is NOT a problem. */
+ if (temp > BC_SCALE_MAX || toobig )
+ {
+ scale = BC_SCALE_MAX;
+ rt_warn ("scale too large, set to %d", BC_SCALE_MAX);
+ }
+ else
+ scale = (int) temp;
+ }
+ }
+}
+
+
+/* Store the top of the execution stack into array VAR_NAME.
+ VAR_NAME is the name of an array, and the next to the top
+ of stack for the index into the array. */
+
+void
+store_array (var_name)
+ int var_name;
+{
+ bc_num *num_ptr;
+ long index;
+
+ if (!check_stack(2)) return;
+ index = num2long (ex_stack->s_next->s_num);
+ if (index < 0 || index > BC_DIM_MAX ||
+ (index == 0 && !is_zero(ex_stack->s_next->s_num)))
+ rt_error ("Array %s subscript out of bounds.", a_names[var_name]);
+ else
+ {
+ num_ptr = get_array_num (var_name, index);
+ if (num_ptr != NULL)
+ {
+ free_num (num_ptr);
+ *num_ptr = copy_num (ex_stack->s_num);
+ free_num (&ex_stack->s_next->s_num);
+ ex_stack->s_next->s_num = ex_stack->s_num;
+ init_num (&ex_stack->s_num);
+ pop();
+ }
+ }
+}
+
+
+/* Load a copy of VAR_NAME on to the execution stack. This includes
+ the special variables ibase, obase and scale. */
+
+void
+load_var (var_name)
+ int var_name;
+{
+ bc_var *var_ptr;
+
+ switch (var_name)
+ {
+
+ case 0:
+ /* Special variable ibase. */
+ push_copy (_zero_);
+ int2num (&ex_stack->s_num, i_base);
+ break;
+
+ case 1:
+ /* Special variable obase. */
+ push_copy (_zero_);
+ int2num (&ex_stack->s_num, o_base);
+ break;
+
+ case 2:
+ /* Special variable scale. */
+ push_copy (_zero_);
+ int2num (&ex_stack->s_num, scale);
+ break;
+
+ default:
+ /* It is a simple variable. */
+ var_ptr = variables[var_name];
+ if (var_ptr != NULL)
+ push_copy (var_ptr->v_value);
+ else
+ push_copy (_zero_);
+ }
+}
+
+
+/* Load a copy of VAR_NAME on to the execution stack. This includes
+ the special variables ibase, obase and scale. */
+
+void
+load_array (var_name)
+ int var_name;
+{
+ bc_num *num_ptr;
+ long index;
+
+ if (!check_stack(1)) return;
+ index = num2long (ex_stack->s_num);
+ if (index < 0 || index > BC_DIM_MAX ||
+ (index == 0 && !is_zero(ex_stack->s_num)))
+ rt_error ("Array %s subscript out of bounds.", a_names[var_name]);
+ else
+ {
+ num_ptr = get_array_num (var_name, index);
+ if (num_ptr != NULL)
+ {
+ pop();
+ push_copy (*num_ptr);
+ }
+ }
+}
+
+
+/* Decrement VAR_NAME by one. This includes the special variables
+ ibase, obase, and scale. */
+
+void
+decr_var (var_name)
+ int var_name;
+{
+ bc_var *var_ptr;
+
+ switch (var_name)
+ {
+
+ case 0: /* ibase */
+ if (i_base > 2)
+ i_base--;
+ else
+ rt_warn ("ibase too small in --");
+ break;
+
+ case 1: /* obase */
+ if (o_base > 2)
+ o_base--;
+ else
+ rt_warn ("obase too small in --");
+ break;
+
+ case 2: /* scale */
+ if (scale > 0)
+ scale--;
+ else
+ rt_warn ("scale can not be negative in -- ");
+ break;
+
+ default: /* It is a simple variable. */
+ var_ptr = get_var (var_name);
+ if (var_ptr != NULL)
+ bc_sub (var_ptr->v_value,_one_,&var_ptr->v_value);
+ }
+}
+
+
+/* Decrement VAR_NAME by one. VAR_NAME is an array, and the top of
+ the execution stack is the index and it is popped off the stack. */
+
+void
+decr_array (var_name)
+ char var_name;
+{
+ bc_num *num_ptr;
+ long index;
+
+ /* It is an array variable. */
+ if (!check_stack (1)) return;
+ index = num2long (ex_stack->s_num);
+ if (index < 0 || index > BC_DIM_MAX ||
+ (index == 0 && !is_zero (ex_stack->s_num)))
+ rt_error ("Array %s subscript out of bounds.", a_names[var_name]);
+ else
+ {
+ num_ptr = get_array_num (var_name, index);
+ if (num_ptr != NULL)
+ {
+ pop ();
+ bc_sub (*num_ptr, _one_, num_ptr);
+ }
+ }
+}
+
+
+/* Increment VAR_NAME by one. This includes the special variables
+ ibase, obase, and scale. */
+
+void
+incr_var (var_name)
+ int var_name;
+{
+ bc_var *var_ptr;
+
+ switch (var_name)
+ {
+
+ case 0: /* ibase */
+ if (i_base < 16)
+ i_base++;
+ else
+ rt_warn ("ibase too big in ++");
+ break;
+
+ case 1: /* obase */
+ if (o_base < BC_BASE_MAX)
+ o_base++;
+ else
+ rt_warn ("obase too big in ++");
+ break;
+
+ case 2:
+ if (scale < BC_SCALE_MAX)
+ scale++;
+ else
+ rt_warn ("Scale too big in ++");
+ break;
+
+ default: /* It is a simple variable. */
+ var_ptr = get_var (var_name);
+ if (var_ptr != NULL)
+ bc_add (var_ptr->v_value, _one_, &var_ptr->v_value);
+
+ }
+}
+
+
+/* Increment VAR_NAME by one. VAR_NAME is an array and top of
+ execution stack is the index and is popped off the stack. */
+
+void
+incr_array (var_name)
+ int var_name;
+{
+ bc_num *num_ptr;
+ long index;
+
+ if (!check_stack (1)) return;
+ index = num2long (ex_stack->s_num);
+ if (index < 0 || index > BC_DIM_MAX ||
+ (index == 0 && !is_zero (ex_stack->s_num)))
+ rt_error ("Array %s subscript out of bounds.", a_names[var_name]);
+ else
+ {
+ num_ptr = get_array_num (var_name, index);
+ if (num_ptr != NULL)
+ {
+ pop ();
+ bc_add (*num_ptr, _one_, num_ptr);
+ }
+ }
+}
+
+
+/* Routines for processing autos variables and parameters. */
+
+/* NAME is an auto variable that needs to be pushed on its stack. */
+
+void
+auto_var (name)
+ int name;
+{
+ bc_var *v_temp;
+ bc_var_array *a_temp;
+ int ix;
+
+ if (name > 0)
+ {
+ /* A simple variable. */
+ ix = name;
+ v_temp = (bc_var *) bc_malloc (sizeof (bc_var));
+ v_temp->v_next = variables[ix];
+ init_num (&v_temp->v_value);
+ variables[ix] = v_temp;
+ }
+ else
+ {
+ /* An array variable. */
+ ix = -name;
+ a_temp = (bc_var_array *) bc_malloc (sizeof (bc_var_array));
+ a_temp->a_next = arrays[ix];
+ a_temp->a_value = NULL;
+ a_temp->a_param = FALSE;
+ arrays[ix] = a_temp;
+ }
+}
+
+
+/* Free_a_tree frees everything associated with an array variable tree.
+ This is used when popping an array variable off its auto stack. */
+
+void
+free_a_tree ( root, depth )
+ bc_array_node *root;
+ int depth;
+{
+ int ix;
+
+ if (root != NULL)
+ {
+ if (depth > 1)
+ for (ix = 0; ix < NODE_SIZE; ix++)
+ free_a_tree (root->n_items.n_down[ix], depth-1);
+ else
+ for (ix = 0; ix < NODE_SIZE; ix++)
+ free_num ( &(root->n_items.n_num[ix]));
+ free (root);
+ }
+}
+
+
+/* LIST is an NULL terminated list of varible names that need to be
+ popped off their auto stacks. */
+
+void
+pop_vars (list)
+ arg_list *list;
+{
+ bc_var *v_temp;
+ bc_var_array *a_temp;
+ int ix;
+
+ while (list != NULL)
+ {
+ ix = list->av_name;
+ if (ix > 0)
+ {
+ /* A simple variable. */
+ v_temp = variables[ix];
+ if (v_temp != NULL)
+ {
+ variables[ix] = v_temp->v_next;
+ free_num (&v_temp->v_value);
+ free (v_temp);
+ }
+ }
+ else
+ {
+ /* An array variable. */
+ ix = -ix;
+ a_temp = arrays[ix];
+ if (a_temp != NULL)
+ {
+ arrays[ix] = a_temp->a_next;
+ if (!a_temp->a_param && a_temp->a_value != NULL)
+ {
+ free_a_tree (a_temp->a_value->a_tree,
+ a_temp->a_value->a_depth);
+ free (a_temp->a_value);
+ }
+ free (a_temp);
+ }
+ }
+ list = list->next;
+ }
+}
+
+
+/* A call is being made to FUNC. The call types are at PC. Process
+ the parameters by doing an auto on the parameter variable and then
+ store the value at the new variable or put a pointer the the array
+ variable. */
+
+void
+process_params (pc, func)
+ program_counter *pc;
+ int func;
+{
+ char ch;
+ arg_list *params;
+ char warned = FALSE;
+ int ix, ix1;
+ bc_var *v_temp;
+ bc_var_array *a_src, *a_dest;
+ bc_num *n_temp;
+
+ /* Get the parameter names from the function. */
+ params = functions[func].f_params;
+
+ while ((ch = byte(pc)) != ':')
+ {
+ if (params != NULL)
+ {
+ if ((ch == '0') && params->av_name > 0)
+ {
+ /* A simple variable. */
+ ix = params->av_name;
+ v_temp = (bc_var *) bc_malloc (sizeof(bc_var));
+ v_temp->v_next = variables[ix];
+ v_temp->v_value = ex_stack->s_num;
+ init_num (&ex_stack->s_num);
+ variables[ix] = v_temp;
+ }
+ else
+ if ((ch == '1') && (params->av_name < 0))
+ {
+ /* The variables is an array variable. */
+
+ /* Compute source index and make sure some structure exists. */
+ ix = (int) num2long (ex_stack->s_num);
+ n_temp = get_array_num (ix, 0);
+
+ /* Push a new array and Compute Destination index */
+ auto_var (params->av_name);
+ ix1 = -params->av_name;
+
+ /* Set up the correct pointers in the structure. */
+ if (ix == ix1)
+ a_src = arrays[ix]->a_next;
+ else
+ a_src = arrays[ix];
+ a_dest = arrays[ix1];
+ a_dest->a_param = TRUE;
+ a_dest->a_value = a_src->a_value;
+ }
+ else
+ {
+ if (params->av_name < 0)
+ rt_error ("Parameter type mismatch parameter %s.",
+ a_names[-params->av_name]);
+ else
+ rt_error ("Parameter type mismatch, parameter %s.",
+ v_names[params->av_name]);
+ params++;
+ }
+ pop ();
+ }
+ else
+ {
+ if (!warned)
+ {
+ rt_error ("Parameter number mismatch");
+ warned = TRUE;
+ }
+ }
+ params = params->next;
+ }
+ if (params != NULL)
+ rt_error ("Parameter number mismatch");
+}
diff --git a/gnu/usr.bin/bc/util.c b/gnu/usr.bin/bc/util.c
new file mode 100644
index 0000000..954c719
--- /dev/null
+++ b/gnu/usr.bin/bc/util.c
@@ -0,0 +1,794 @@
+/* util.c: Utility routines for bc. */
+
+/* This file is part of bc written for MINIX.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License , or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ You may contact the author by:
+ e-mail: phil@cs.wwu.edu
+ us-mail: Philip A. Nelson
+ Computer Science Department, 9062
+ Western Washington University
+ Bellingham, WA 98226-9062
+
+*************************************************************************/
+
+
+#include "bcdefs.h"
+#ifndef VARARGS
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include "global.h"
+#include "proto.h"
+
+
+/* strcopyof mallocs new memory and copies a string to to the new
+ memory. */
+
+char *
+strcopyof (str)
+ char *str;
+{
+ char *temp;
+
+ temp = (char *) bc_malloc (strlen (str)+1);
+ return (strcpy (temp,str));
+}
+
+
+/* nextarg adds another value to the list of arguments. */
+
+arg_list *
+nextarg (args, val)
+ arg_list *args;
+ char val;
+{ arg_list *temp;
+
+ temp = (arg_list *) bc_malloc (sizeof (arg_list));
+ temp->av_name = val;
+ temp->next = args;
+
+ return (temp);
+}
+
+
+/* For generate, we must produce a string in the form
+ "val,val,...,val". We also need a couple of static variables
+ for retaining old generated strings. It also uses a recursive
+ function that builds the string. */
+
+static char *arglist1 = NULL, *arglist2 = NULL;
+
+
+/* make_arg_str does the actual construction of the argument string.
+ ARGS is the pointer to the list and LEN is the maximum number of
+ characters needed. 1 char is the minimum needed. COMMAS tells
+ if each number should be seperated by commas.*/
+
+_PROTOTYPE (static char *make_arg_str, (arg_list *args, int len, int commas));
+
+static char *
+make_arg_str (args, len, commas)
+ arg_list *args;
+ int len;
+ int commas;
+{
+ char *temp;
+ char sval[20];
+
+ /* Recursive call. */
+ if (args != NULL)
+ temp = make_arg_str (args->next, len+11, commas);
+ else
+ {
+ temp = (char *) bc_malloc (len);
+ *temp = 0;
+ return temp;
+ }
+
+ /* Add the current number to the end of the string. */
+ if (len != 1 && commas)
+ sprintf (sval, "%d,", args->av_name);
+ else
+ sprintf (sval, "%d", args->av_name);
+ temp = strcat (temp, sval);
+ return (temp);
+}
+
+char *
+arg_str (args, commas)
+ arg_list *args;
+ int commas;
+{
+ if (arglist2 != NULL)
+ free (arglist2);
+ arglist2 = arglist1;
+ arglist1 = make_arg_str (args, 1, commas);
+ return (arglist1);
+}
+
+
+/* free_args frees an argument list ARGS. */
+
+void
+free_args (args)
+ arg_list *args;
+{
+ arg_list *temp;
+
+ temp = args;
+ while (temp != NULL)
+ {
+ args = args->next;
+ free (temp);
+ temp = args;
+ }
+}
+
+
+/* Check for valid parameter (PARAMS) and auto (AUTOS) lists.
+ There must be no duplicates any where. Also, this is where
+ warnings are generated for array parameters. */
+
+void
+check_params ( params, autos )
+ arg_list *params, *autos;
+{
+ arg_list *tmp1, *tmp2;
+
+ /* Check for duplicate parameters. */
+ if (params != NULL)
+ {
+ tmp1 = params;
+ while (tmp1 != NULL)
+ {
+ tmp2 = tmp1->next;
+ while (tmp2 != NULL)
+ {
+ if (tmp2->av_name == tmp1->av_name)
+ yyerror ("duplicate parameter names");
+ tmp2 = tmp2->next;
+ }
+ if (tmp1->av_name < 0)
+ warn ("Array parameter");
+ tmp1 = tmp1->next;
+ }
+ }
+
+ /* Check for duplicate autos. */
+ if (autos != NULL)
+ {
+ tmp1 = autos;
+ while (tmp1 != NULL)
+ {
+ tmp2 = tmp1->next;
+ while (tmp2 != NULL)
+ {
+ if (tmp2->av_name == tmp1->av_name)
+ yyerror ("duplicate auto variable names");
+ tmp2 = tmp2->next;
+ }
+ tmp1 = tmp1->next;
+ }
+ }
+
+ /* Check for duplicate between parameters and autos. */
+ if ((params != NULL) && (autos != NULL))
+ {
+ tmp1 = params;
+ while (tmp1 != NULL)
+ {
+ tmp2 = autos;
+ while (tmp2 != NULL)
+ {
+ if (tmp2->av_name == tmp1->av_name)
+ yyerror ("variable in both parameter and auto lists");
+ tmp2 = tmp2->next;
+ }
+ tmp1 = tmp1->next;
+ }
+ }
+}
+
+
+/* Initialize the code generator the parser. */
+
+void
+init_gen ()
+{
+ /* Get things ready. */
+ break_label = 0;
+ continue_label = 0;
+ next_label = 1;
+ out_count = 2;
+ if (compile_only)
+ printf ("@i");
+ else
+ init_load ();
+ had_error = FALSE;
+ did_gen = FALSE;
+}
+
+
+/* generate code STR for the machine. */
+
+void
+generate (str)
+ char *str;
+{
+ did_gen = TRUE;
+ if (compile_only)
+ {
+ printf ("%s",str);
+ out_count += strlen(str);
+ if (out_count > 60)
+ {
+ printf ("\n");
+ out_count = 0;
+ }
+ }
+ else
+ load_code (str);
+}
+
+
+/* Execute the current code as loaded. */
+
+void
+run_code()
+{
+ /* If no compile errors run the current code. */
+ if (!had_error && did_gen)
+ {
+ if (compile_only)
+ {
+ printf ("@r\n");
+ out_count = 0;
+ }
+ else
+ execute ();
+ }
+
+ /* Reinitialize the code generation and machine. */
+ if (did_gen)
+ init_gen();
+ else
+ had_error = FALSE;
+}
+
+
+/* Output routines: Write a character CH to the standard output.
+ It keeps track of the number of characters output and may
+ break the output with a "\<cr>". */
+
+void
+out_char (ch)
+ char ch;
+{
+ if (ch == '\n')
+ {
+ out_col = 0;
+ putchar ('\n');
+ }
+ else
+ {
+ out_col++;
+ if (out_col == 70)
+ {
+ putchar ('\\');
+ putchar ('\n');
+ out_col = 1;
+ }
+ putchar (ch);
+ }
+}
+
+
+/* The following are "Symbol Table" routines for the parser. */
+
+/* find_id returns a pointer to node in TREE that has the correct
+ ID. If there is no node in TREE with ID, NULL is returned. */
+
+id_rec *
+find_id (tree, id)
+ id_rec *tree;
+ char *id;
+{
+ int cmp_result;
+
+ /* Check for an empty tree. */
+ if (tree == NULL)
+ return NULL;
+
+ /* Recursively search the tree. */
+ cmp_result = strcmp (id, tree->id);
+ if (cmp_result == 0)
+ return tree; /* This is the item. */
+ else if (cmp_result < 0)
+ return find_id (tree->left, id);
+ else
+ return find_id (tree->right, id);
+}
+
+
+/* insert_id_rec inserts a NEW_ID rec into the tree whose ROOT is
+ provided. insert_id_rec returns TRUE if the tree height from
+ ROOT down is increased otherwise it returns FALSE. This is a
+ recursive balanced binary tree insertion algorithm. */
+
+int insert_id_rec (root, new_id)
+ id_rec **root;
+ id_rec *new_id;
+{
+ id_rec *A, *B;
+
+ /* If root is NULL, this where it is to be inserted. */
+ if (*root == NULL)
+ {
+ *root = new_id;
+ new_id->left = NULL;
+ new_id->right = NULL;
+ new_id->balance = 0;
+ return (TRUE);
+ }
+
+ /* We need to search for a leaf. */
+ if (strcmp (new_id->id, (*root)->id) < 0)
+ {
+ /* Insert it on the left. */
+ if (insert_id_rec (&((*root)->left), new_id))
+ {
+ /* The height increased. */
+ (*root)->balance --;
+
+ switch ((*root)->balance)
+ {
+ case 0: /* no height increase. */
+ return (FALSE);
+ case -1: /* height increase. */
+ return (FALSE);
+ case -2: /* we need to do a rebalancing act. */
+ A = *root;
+ B = (*root)->left;
+ if (B->balance <= 0)
+ {
+ /* Single Rotate. */
+ A->left = B->right;
+ B->right = A;
+ *root = B;
+ A->balance = 0;
+ B->balance = 0;
+ }
+ else
+ {
+ /* Double Rotate. */
+ *root = B->right;
+ B->right = (*root)->left;
+ A->left = (*root)->right;
+ (*root)->left = B;
+ (*root)->right = A;
+ switch ((*root)->balance)
+ {
+ case -1:
+ A->balance = 1;
+ B->balance = 0;
+ break;
+ case 0:
+ A->balance = 0;
+ B->balance = 0;
+ break;
+ case 1:
+ A->balance = 0;
+ B->balance = -1;
+ break;
+ }
+ (*root)->balance = 0;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Insert it on the right. */
+ if (insert_id_rec (&((*root)->right), new_id))
+ {
+ /* The height increased. */
+ (*root)->balance ++;
+ switch ((*root)->balance)
+ {
+ case 0: /* no height increase. */
+ return (FALSE);
+ case 1: /* height increase. */
+ return (FALSE);
+ case 2: /* we need to do a rebalancing act. */
+ A = *root;
+ B = (*root)->right;
+ if (B->balance >= 0)
+ {
+ /* Single Rotate. */
+ A->right = B->left;
+ B->left = A;
+ *root = B;
+ A->balance = 0;
+ B->balance = 0;
+ }
+ else
+ {
+ /* Double Rotate. */
+ *root = B->left;
+ B->left = (*root)->right;
+ A->right = (*root)->left;
+ (*root)->left = A;
+ (*root)->right = B;
+ switch ((*root)->balance)
+ {
+ case -1:
+ A->balance = 0;
+ B->balance = 1;
+ break;
+ case 0:
+ A->balance = 0;
+ B->balance = 0;
+ break;
+ case 1:
+ A->balance = -1;
+ B->balance = 0;
+ break;
+ }
+ (*root)->balance = 0;
+ }
+ }
+ }
+ }
+
+ /* If we fall through to here, the tree did not grow in height. */
+ return (FALSE);
+}
+
+
+/* Initialize variables for the symbol table tree. */
+
+void
+init_tree()
+{
+ name_tree = NULL;
+ next_array = 1;
+ next_func = 1;
+ next_var = 4; /* 0 => ibase, 1 => obase, 2 => scale, 3 => last. */
+}
+
+
+/* Lookup routines for symbol table names. */
+
+int
+lookup (name, namekind)
+ char *name;
+ int namekind;
+{
+ id_rec *id;
+
+ /* Warn about non-standard name. */
+ if (strlen(name) != 1)
+ warn ("multiple letter name - %s", name);
+
+ /* Look for the id. */
+ id = find_id (name_tree, name);
+ if (id == NULL)
+ {
+ /* We need to make a new item. */
+ id = (id_rec *) bc_malloc (sizeof (id_rec));
+ id->id = strcopyof (name);
+ id->a_name = 0;
+ id->f_name = 0;
+ id->v_name = 0;
+ insert_id_rec (&name_tree, id);
+ }
+
+ /* Return the correct value. */
+ switch (namekind)
+ {
+
+ case ARRAY:
+ /* ARRAY variable numbers are returned as negative numbers. */
+ if (id->a_name != 0)
+ {
+ free (name);
+ return (-id->a_name);
+ }
+ id->a_name = next_array++;
+ a_names[id->a_name] = name;
+ if (id->a_name < MAX_STORE)
+ {
+ if (id->a_name >= a_count)
+ more_arrays ();
+ return (-id->a_name);
+ }
+ yyerror ("Too many array variables");
+ exit (1);
+
+ case FUNCT:
+ if (id->f_name != 0)
+ {
+ free(name);
+ return (id->f_name);
+ }
+ id->f_name = next_func++;
+ f_names[id->f_name] = name;
+ if (id->f_name < MAX_STORE)
+ {
+ if (id->f_name >= f_count)
+ more_functions ();
+ return (id->f_name);
+ }
+ yyerror ("Too many functions");
+ exit (1);
+
+ case SIMPLE:
+ if (id->v_name != 0)
+ {
+ free(name);
+ return (id->v_name);
+ }
+ id->v_name = next_var++;
+ v_names[id->v_name - 1] = name;
+ if (id->v_name <= MAX_STORE)
+ {
+ if (id->v_name >= v_count)
+ more_variables ();
+ return (id->v_name);
+ }
+ yyerror ("Too many variables");
+ exit (1);
+ }
+}
+
+
+/* Print the welcome banner. */
+
+void
+welcome()
+{
+ printf ("This is free software with ABSOLUTELY NO WARRANTY.\n");
+ printf ("For details type `warranty'. \n");
+}
+
+
+/* Print out the warranty information. */
+
+void
+warranty(prefix)
+ char *prefix;
+{
+ printf ("\n%s%s\n\n", prefix, BC_VERSION);
+ printf ("%s%s%s%s%s%s%s%s%s%s%s",
+" This program is free software; you can redistribute it and/or modify\n",
+" it under the terms of the GNU General Public License as published by\n",
+" the Free Software Foundation; either version 2 of the License , or\n",
+" (at your option) any later version.\n\n",
+" This program is distributed in the hope that it will be useful,\n",
+" but WITHOUT ANY WARRANTY; without even the implied warranty of\n",
+" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n",
+" GNU General Public License for more details.\n\n",
+" You should have received a copy of the GNU General Public License\n",
+" along with this program. If not, write to the Free Software\n",
+" Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.\n\n");
+}
+
+/* Print out the limits of this program. */
+
+void
+limits()
+{
+ printf ("BC_BASE_MAX = %d\n", BC_BASE_MAX);
+ printf ("BC_DIM_MAX = %ld\n", (long) BC_DIM_MAX);
+ printf ("BC_SCALE_MAX = %d\n", BC_SCALE_MAX);
+ printf ("BC_STRING_MAX = %d\n", BC_STRING_MAX);
+ printf ("MAX Exponent = %ld\n", (long) LONG_MAX);
+ printf ("MAX code = %ld\n", (long) BC_MAX_SEGS * (long) BC_SEG_SIZE);
+ printf ("multiply digits = %ld\n", (long) LONG_MAX / (long) 90);
+ printf ("Number of vars = %ld\n", (long) MAX_STORE);
+#ifdef OLD_EQ_OP
+ printf ("Old assignment operatiors are valid. (=-, =+, ...)\n");
+#endif
+}
+
+/* bc_malloc will check the return value so all other places do not
+ have to do it! SIZE is the number of types to allocate. */
+
+char *
+bc_malloc (size)
+ int size;
+{
+ char *ptr;
+
+ ptr = (char *) malloc (size);
+ if (ptr == NULL)
+ out_of_memory ();
+
+ return ptr;
+}
+
+
+/* The following routines are error routines for various problems. */
+
+/* Malloc could not get enought memory. */
+
+void
+out_of_memory()
+{
+ fprintf (stderr, "Fatal error: Out of memory for malloc.\n");
+ exit (1);
+}
+
+
+
+/* The standard yyerror routine. Built with variable number of argumnets. */
+
+#ifndef VARARGS
+#ifdef __STDC__
+void
+yyerror (char *str, ...)
+#else
+void
+yyerror (str)
+ char *str;
+#endif
+#else
+void
+yyerror (str, va_alist)
+ char *str;
+#endif
+{
+ char *name;
+ va_list args;
+
+#ifndef VARARGS
+ va_start (args, str);
+#else
+ va_start (args);
+#endif
+ if (is_std_in)
+ name = "(standard_in)";
+ else
+ name = g_argv[optind-1];
+ fprintf (stderr,"%s %d: ",name,line_no);
+ vfprintf (stderr, str, args);
+ fprintf (stderr, "\n");
+ had_error = TRUE;
+ va_end (args);
+}
+
+
+/* The routine to produce warnings about non-standard features
+ found during parsing. */
+
+#ifndef VARARGS
+#ifdef __STDC__
+void
+warn (char *mesg, ...)
+#else
+void
+warn (mesg)
+ char *mesg;
+#endif
+#else
+void
+warn (mesg, va_alist)
+ char *mesg;
+#endif
+{
+ char *name;
+ va_list args;
+
+#ifndef VARARGS
+ va_start (args, mesg);
+#else
+ va_start (args);
+#endif
+ if (std_only)
+ {
+ if (is_std_in)
+ name = "(standard_in)";
+ else
+ name = g_argv[optind-1];
+ fprintf (stderr,"%s %d: ",name,line_no);
+ vfprintf (stderr, mesg, args);
+ fprintf (stderr, "\n");
+ had_error = TRUE;
+ }
+ else
+ if (warn_not_std)
+ {
+ if (is_std_in)
+ name = "(standard_in)";
+ else
+ name = g_argv[optind-1];
+ fprintf (stderr,"%s %d: (Warning) ",name,line_no);
+ vfprintf (stderr, mesg, args);
+ fprintf (stderr, "\n");
+ }
+ va_end (args);
+}
+
+/* Runtime error will print a message and stop the machine. */
+
+#ifndef VARARGS
+#ifdef __STDC__
+void
+rt_error (char *mesg, ...)
+#else
+void
+rt_error (mesg)
+ char *mesg;
+#endif
+#else
+void
+rt_error (mesg, va_alist)
+ char *mesg;
+#endif
+{
+ va_list args;
+ char error_mesg [255];
+
+#ifndef VARARGS
+ va_start (args, mesg);
+#else
+ va_start (args);
+#endif
+ vsprintf (error_mesg, mesg, args);
+ va_end (args);
+
+ fprintf (stderr, "Runtime error (func=%s, adr=%d): %s\n",
+ f_names[pc.pc_func], pc.pc_addr, error_mesg);
+ runtime_error = TRUE;
+}
+
+
+/* A runtime warning tells of some action taken by the processor that
+ may change the program execution but was not enough of a problem
+ to stop the execution. */
+
+#ifndef VARARGS
+#ifdef __STDC__
+void
+rt_warn (char *mesg, ...)
+#else
+void
+rt_warn (mesg)
+ char *mesg;
+#endif
+#else
+void
+rt_warn (mesg, va_alist)
+ char *mesg;
+#endif
+{
+ va_list args;
+ char error_mesg [255];
+
+#ifndef VARARGS
+ va_start (args, mesg);
+#else
+ va_start (args);
+#endif
+ vsprintf (error_mesg, mesg, args);
+ va_end (args);
+
+ fprintf (stderr, "Runtime warning (func=%s, adr=%d): %s\n",
+ f_names[pc.pc_func], pc.pc_addr, error_mesg);
+}
diff --git a/gnu/usr.bin/bc/version.h b/gnu/usr.bin/bc/version.h
new file mode 100644
index 0000000..6b3f909
--- /dev/null
+++ b/gnu/usr.bin/bc/version.h
@@ -0,0 +1,3 @@
+#define BC_VERSION \
+ "bc 1.02 (Mar 3, 92) Copyright (C) 1991, 1992 Free Software Foundation, Inc."
+
diff --git a/gnu/usr.bin/bc/y.tab.h b/gnu/usr.bin/bc/y.tab.h
new file mode 100644
index 0000000..9e65a2f
--- /dev/null
+++ b/gnu/usr.bin/bc/y.tab.h
@@ -0,0 +1,40 @@
+#define NEWLINE 257
+#define AND 258
+#define OR 259
+#define NOT 260
+#define STRING 261
+#define NAME 262
+#define NUMBER 263
+#define MUL_OP 264
+#define ASSIGN_OP 265
+#define REL_OP 266
+#define INCR_DECR 267
+#define Define 268
+#define Break 269
+#define Quit 270
+#define Length 271
+#define Return 272
+#define For 273
+#define If 274
+#define While 275
+#define Sqrt 276
+#define Else 277
+#define Scale 278
+#define Ibase 279
+#define Obase 280
+#define Auto 281
+#define Read 282
+#define Warranty 283
+#define Halt 284
+#define Last 285
+#define Continue 286
+#define Print 287
+#define Limits 288
+#define UNARY_MINUS 289
+typedef union {
+ char *s_value;
+ char c_value;
+ int i_value;
+ arg_list *a_value;
+ } YYSTYPE;
+extern YYSTYPE yylval;
diff --git a/gnu/usr.bin/binutils/gdb/Makefile b/gnu/usr.bin/binutils/gdb/Makefile
new file mode 100644
index 0000000..5f134e2
--- /dev/null
+++ b/gnu/usr.bin/binutils/gdb/Makefile
@@ -0,0 +1,72 @@
+PROG = gdb
+BINDIR= /usr/bin
+SRCS = main.c blockframe.c breakpoint.c findvar.c stack.c thread.c \
+ source.c values.c eval.c valops.c valarith.c valprint.c printcmd.c \
+ symtab.c symfile.c symmisc.c infcmd.c infrun.c command.c utils.c \
+ expprint.c environ.c gdbtypes.c copying.c i386-tdep.c i386-pinsn.c \
+ freebsd-solib.c ser-unix.c exec.c fork-child.c infptrace.c inftarg.c \
+ corelow.c coredep.c freebsd-nat.c remote.c dcache.c remote-utils.c \
+ mem-break.c target.c putenv.c parse.c language.c buildsym.c \
+ objfiles.c minsyms.c maint.c demangle.c dbxread.c coffread.c \
+ elfread.c dwarfread.c mipsread.c stabsread.c core.c c-lang.c \
+ ch-lang.c m2-lang.c complaints.c typeprint.c c-typeprint.c \
+ ch-typeprint.c m2-typeprint.c c-valprint.c cp-valprint.c ch-valprint.c \
+ m2-valprint.c nlmread.c serial.c inflow.c regex.c init.c \
+ c-exp.tab.c ch-exp.tab.c m2-exp.tab.c version.c i386-dis.c dis-buf.c
+
+c-exp.tab.c: $(.CURDIR)/c-exp.y
+ yacc -d -p c_ $(.CURDIR)/c-exp.y
+ sed -e '/extern.*malloc/d' -e '/extern.*realloc/d' -e '/extern.*free/d' \
+ -e '/include.*malloc.h/d' -e 's/malloc/xmalloc/g' \
+ -e 's/realloc/xrealloc/g' < y.tab.c > c-exp.new
+ rm y.tab.c
+ mv c-exp.new ./c-exp.tab.c
+
+ch-exp.tab.c: $(.CURDIR)/ch-exp.y
+ yacc -d -p ch_ $(.CURDIR)/ch-exp.y
+ sed -e '/extern.*malloc/d' -e '/extern.*realloc/d' -e '/extern.*free/d' \
+ -e '/include.*malloc.h/d' -e 's/malloc/xmalloc/g' \
+ -e 's/realloc/xrealloc/g' < y.tab.c > ch-exp.new
+ rm y.tab.c
+ mv ch-exp.new ./ch-exp.tab.c
+
+m2-exp.tab.c: $(.CURDIR)/m2-exp.y
+ yacc -d -p m2_ $(.CURDIR)/m2-exp.y
+ sed -e '/extern.*malloc/d' -e '/extern.*realloc/d' -e '/extern.*free/d' \
+ -e '/include.*malloc.h/d' -e 's/malloc/xmalloc/g' \
+ -e 's/realloc/xrealloc/g' < y.tab.c > m2-exp.new
+ rm y.tab.c
+ mv m2-exp.new ./m2-exp.tab.c
+
+
+
+CFLAGS+= -I$(.CURDIR)/. -I/usr/include/readline -I$(.CURDIR)/../bfd
+DPADD+= ${LIBREADLINE} ${LIBTERMCAP}
+LDADD+= -lreadline -ltermcap
+
+.if exists(${.CURDIR}/../libiberty/obj)
+LDADD+= -L${.CURDIR}/../libiberty/obj -liberty
+DPADD+= ${.CURDIR}/../libiberty/obj/libiberty.a
+.else
+LDADD+= -L${.CURDIR}/../libiberty/ -liberty
+DPADD+= ${.CURDIR}/../libiberty/libiberty.a
+.endif
+
+.if exists(${.CURDIR}/../bfd/obj)
+LDADD+= -L${.CURDIR}/../bfd/obj -lbfd
+DPADD+= ${.CURDIR}/../bfd/obj/libbfd.a
+.else
+LDADD+= -L${.CURDIR}/../bfd/ -lbfd
+DPADD+= ${.CURDIR}/../bfd/libbfd.a
+.endif
+
+.if exists(${.CURDIR}/../mmalloc/obj)
+LDADD+= -L${.CURDIR}/../mmalloc/obj -lmmalloc
+DPADD+= ${.CURDIR}/../mmalloc/obj/libmmalloc.a
+.else
+LDADD+= -L${.CURDIR}/../mmalloc/ -lmmalloc
+DPADD+= ${.CURDIR}/../mmalloc/libmmalloc.a
+.endif
+
+LDADD+= -lcompat
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/binutils/gdb/gdb.1 b/gnu/usr.bin/binutils/gdb/gdb.1
new file mode 100644
index 0000000..ccb216e
--- /dev/null
+++ b/gnu/usr.bin/binutils/gdb/gdb.1
@@ -0,0 +1,371 @@
+.\" Copyright (c) 1991 Free Software Foundation
+.\" See section COPYING for conditions for redistribution
+.\" $Id: gdb.1,v 1.1.1.1 1993/10/30 21:59:13 jkh Exp $
+.TH gdb 1 "4nov1991" "GNU Tools" "GNU Tools"
+.SH NAME
+gdb \- The GNU Debugger
+.SH SYNOPSIS
+.na
+.TP
+.B gdb
+.RB "[\|" \-help "\|]"
+.RB "[\|" \-nx "\|]"
+.RB "[\|" \-q "\|]"
+.RB "[\|" \-batch "\|]"
+.RB "[\|" \-cd=\c
+.I dir\c
+\|]
+.RB "[\|" \-f "\|]"
+.RB "[\|" "\-b\ "\c
+.IR bps "\|]"
+.RB "[\|" "\-tty="\c
+.IR dev "\|]"
+.RB "[\|" "\-s "\c
+.I symfile\c
+\&\|]
+.RB "[\|" "\-e "\c
+.I prog\c
+\&\|]
+.RB "[\|" "\-se "\c
+.I prog\c
+\&\|]
+.RB "[\|" "\-c "\c
+.I core\c
+\&\|]
+.RB "[\|" "\-x "\c
+.I cmds\c
+\&\|]
+.RB "[\|" "\-d "\c
+.I dir\c
+\&\|]
+.RB "[\|" \c
+.I prog\c
+.RB "[\|" \c
+.IR core \||\| procID\c
+\&\|]\&\|]
+.ad b
+.SH DESCRIPTION
+The purpose of a debugger such as GDB is to allow you to see what is
+going on ``inside'' another program while it executes\(em\&or what another
+program was doing at the moment it crashed.
+
+GDB can do four main kinds of things (plus other things in support of
+these) to help you catch bugs in the act:
+
+.TP
+\ \ \ \(bu
+Start your program, specifying anything that might affect its behavior.
+
+.TP
+\ \ \ \(bu
+Make your program stop on specified conditions.
+
+.TP
+\ \ \ \(bu
+Examine what has happened, when your program has stopped.
+
+.TP
+\ \ \ \(bu
+Change things in your program, so you can experiment with correcting the
+effects of one bug and go on to learn about another.
+.PP
+
+You can use GDB to debug programs written in C, C++, and Modula-2.
+Fortran support will be added when a GNU Fortran compiler is ready.
+
+GDB is invoked with the shell command \c
+.B gdb\c
+\&. Once started, it reads
+commands from the terminal until you tell it to exit with the GDB
+command \c
+.B quit\c
+\&. You can get online help from \c
+.B gdb\c
+\& itself
+by using the command \c
+.B help\c
+\&.
+
+You can run \c
+.B gdb\c
+\& with no arguments or options; but the most
+usual way to start GDB is with one argument or two, specifying an
+executable program as the argument:
+.sp
+.br
+gdb\ program
+.br
+.sp
+
+You can also start with both an executable program and a core file specified:
+.sp
+.br
+gdb\ program\ core
+.br
+.sp
+
+You can, instead, specify a process ID as a second argument, if you want
+to debug a running process:
+.sp
+.br
+gdb\ program\ 1234
+.br
+.sp
+
+would attach GDB to process \c
+.B 1234\c
+\& (unless you also have a file
+named `\|\c
+.B 1234\c
+\&\|'; GDB does check for a core file first).
+
+Here are some of the most frequently needed GDB commands:
+.TP
+.B break \fR[\|\fIfile\fB:\fR\|]\fIfunction
+\&
+Set a breakpoint at \c
+.I function\c
+\& (in \c
+.I file\c
+\&).
+.TP
+.B run \fR[\|\fIarglist\fR\|]
+Start your program (with \c
+.I arglist\c
+\&, if specified).
+.TP
+.B bt
+Backtrace: display the program stack.
+.TP
+.BI print " expr"\c
+\&
+Display the value of an expression.
+.TP
+.B c
+Continue running your program (after stopping, e.g. at a breakpoint).
+.TP
+.B next
+Execute next program line (after stopping); step \c
+.I over\c
+\& any
+function calls in the line.
+.TP
+.B step
+Execute next program line (after stopping); step \c
+.I into\c
+\& any
+function calls in the line.
+.TP
+.B help \fR[\|\fIname\fR\|]
+Show information about GDB command \c
+.I name\c
+\&, or general information
+about using GDB.
+.TP
+.B quit
+Exit from GDB.
+.PP
+For full details on GDB, see \c
+.I
+Using GDB: A Guide to the GNU Source-Level Debugger\c
+\&, by Richard M. Stallman and Roland H. Pesch. The same text is available online
+as the \c
+.B gdb\c
+\& entry in the \c
+.B info\c
+\& program.
+.SH OPTIONS
+Any arguments other than options specify an executable
+file and core file (or process ID); that is, the first argument
+encountered with no
+associated option flag is equivalent to a `\|\c
+.B \-se\c
+\&\|' option, and the
+second, if any, is equivalent to a `\|\c
+.B \-c\c
+\&\|' option if it's the name of a file. Many options have
+both long and short forms; both are shown here. The long forms are also
+recognized if you truncate them, so long as enough of the option is
+present to be unambiguous. (If you prefer, you can flag option
+arguments with `\|\c
+.B +\c
+\&\|' rather than `\|\c
+.B \-\c
+\&\|', though we illustrate the
+more usual convention.)
+
+All the options and command line arguments you give are processed
+in sequential order. The order makes a difference when the
+`\|\c
+.B \-x\c
+\&\|' option is used.
+
+.TP
+.B \-help
+.TP
+.B \-h
+List all options, with brief explanations.
+
+.TP
+.BI "\-symbols=" "file"\c
+.TP
+.BI "\-s " "file"\c
+\&
+Read symbol table from file \c
+.I file\c
+\&.
+
+.TP
+.BI "\-exec=" "file"\c
+.TP
+.BI "\-e " "file"\c
+\&
+Use file \c
+.I file\c
+\& as the executable file to execute when
+appropriate, and for examining pure data in conjunction with a core
+dump.
+
+.TP
+.BI "\-se=" "file"\c
+\&
+Read symbol table from file \c
+.I file\c
+\& and use it as the executable
+file.
+
+.TP
+.BI "\-core=" "file"\c
+.TP
+.BI "\-c " "file"\c
+\&
+Use file \c
+.I file\c
+\& as a core dump to examine.
+
+.TP
+.BI "\-command=" "file"\c
+.TP
+.BI "\-x " "file"\c
+\&
+Execute GDB commands from file \c
+.I file\c
+\&.
+
+.TP
+.BI "\-directory=" "directory"\c
+.TP
+.BI "\-d " "directory"\c
+\&
+Add \c
+.I directory\c
+\& to the path to search for source files.
+.PP
+
+.TP
+.B \-nx
+.TP
+.B \-n
+Do not execute commands from any `\|\c
+.B .gdbinit\c
+\&\|' initialization files.
+Normally, the commands in these files are executed after all the
+command options and arguments have been processed.
+
+
+.TP
+.B \-quiet
+.TP
+.B \-q
+``Quiet''. Do not print the introductory and copyright messages. These
+messages are also suppressed in batch mode.
+
+.TP
+.B \-batch
+Run in batch mode. Exit with status \c
+.B 0\c
+\& after processing all the command
+files specified with `\|\c
+.B \-x\c
+\&\|' (and `\|\c
+.B .gdbinit\c
+\&\|', if not inhibited).
+Exit with nonzero status if an error occurs in executing the GDB
+commands in the command files.
+
+Batch mode may be useful for running GDB as a filter, for example to
+download and run a program on another computer; in order to make this
+more useful, the message
+.sp
+.br
+Program\ exited\ normally.
+.br
+.sp
+
+(which is ordinarily issued whenever a program running under GDB control
+terminates) is not issued when running in batch mode.
+
+.TP
+.BI "\-cd=" "directory"\c
+\&
+Run GDB using \c
+.I directory\c
+\& as its working directory,
+instead of the current directory.
+
+.TP
+.B \-fullname
+.TP
+.B \-f
+Emacs sets this option when it runs GDB as a subprocess. It tells GDB
+to output the full file name and line number in a standard,
+recognizable fashion each time a stack frame is displayed (which
+includes each time the program stops). This recognizable format looks
+like two `\|\c
+.B \032\c
+\&\|' characters, followed by the file name, line number
+and character position separated by colons, and a newline. The
+Emacs-to-GDB interface program uses the two `\|\c
+.B \032\c
+\&\|' characters as
+a signal to display the source code for the frame.
+
+.TP
+.BI "\-b " "bps"\c
+\&
+Set the line speed (baud rate or bits per second) of any serial
+interface used by GDB for remote debugging.
+
+.TP
+.BI "\-tty=" "device"\c
+\&
+Run using \c
+.I device\c
+\& for your program's standard input and output.
+.PP
+
+.SH "SEE ALSO"
+.RB "`\|" gdb "\|'"
+entry in
+.B info\c
+\&;
+.I
+Using GDB: A Guide to the GNU Source-Level Debugger\c
+, Richard M. Stallman and Roland H. Pesch, July 1991.
+.SH COPYING
+Copyright (c) 1991 Free Software Foundation, Inc.
+.PP
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+.PP
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+.PP
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
diff --git a/gnu/usr.bin/binutils/gdb/i386/freebsd-nat.c b/gnu/usr.bin/binutils/gdb/i386/freebsd-nat.c
new file mode 100644
index 0000000..deb68eb
--- /dev/null
+++ b/gnu/usr.bin/binutils/gdb/i386/freebsd-nat.c
@@ -0,0 +1,323 @@
+/* Native-dependent code for BSD Unix running on i386's, for GDB.
+ Copyright 1988, 1989, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include <machine/reg.h>
+
+/* this table must line up with REGISTER_NAMES in tm-i386.h */
+/* symbols like 'tEAX' come from <machine/reg.h> */
+static int tregmap[] =
+{
+ tEAX, tECX, tEDX, tEBX,
+ tESP, tEBP, tESI, tEDI,
+ tEIP, tEFLAGS, tCS, tSS
+};
+#ifdef sEAX
+static int sregmap[] =
+{
+ sEAX, sECX, sEDX, sEBX,
+ sESP, sEBP, sESI, sEDI,
+ sEIP, sEFLAGS, sCS, sSS
+};
+#endif
+/* blockend is the value of u.u_ar0, and points to the
+ place where ES is stored. */
+
+int
+i386_register_u_addr (blockend, regnum)
+ int blockend;
+ int regnum;
+{
+ /* The following condition is a kludge to get at the proper register map
+ depending upon the state of pcb_flag.
+ The proper condition would be
+ if (u.u_pcb.pcb_flag & FM_TRAP)
+ but that would require a ptrace call here and wouldn't work
+ for corefiles. */
+
+#ifdef sEAX
+ if (blockend < 0x1fcc)
+ return (blockend + 4 * tregmap[regnum]);
+ else
+ return (blockend + 4 * sregmap[regnum]);
+#else
+ return (blockend + 4 * tregmap[regnum]);
+#endif
+}
+
+#ifdef FLOAT_INFO
+#include <sys/param.h>
+#include <sys/dir.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#include <a.out.h>
+
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/uio.h>
+#define curpcb Xcurpcb /* XXX avoid leaking declaration from pcb.h */
+#include <sys/user.h>
+#undef curpcb
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/ptrace.h>
+
+#define fpstate save87
+#define U_FPSTATE(u) u.u_pcb.pcb_savefpu
+
+i387_to_double (from, to)
+ char *from;
+ char *to;
+{
+ long *lp;
+ /* push extended mode on 387 stack, then pop in double mode
+ *
+ * first, set exception masks so no error is generated -
+ * number will be rounded to inf or 0, if necessary
+ */
+ asm ("pushl %eax"); /* grab a stack slot */
+ asm ("fstcw (%esp)"); /* get 387 control word */
+ asm ("movl (%esp),%eax"); /* save old value */
+ asm ("orl $0x3f,%eax"); /* mask all exceptions */
+ asm ("pushl %eax");
+ asm ("fldcw (%esp)"); /* load new value into 387 */
+
+ asm ("movl 8(%ebp),%eax");
+ asm ("fldt (%eax)"); /* push extended number on 387 stack */
+ asm ("fwait");
+ asm ("movl 12(%ebp),%eax");
+ asm ("fstpl (%eax)"); /* pop double */
+ asm ("fwait");
+
+ asm ("popl %eax"); /* flush modified control word */
+ asm ("fnclex"); /* clear exceptions */
+ asm ("fldcw (%esp)"); /* restore original control word */
+ asm ("popl %eax"); /* flush saved copy */
+}
+
+double_to_i387 (from, to)
+ char *from;
+ char *to;
+{
+ /* push double mode on 387 stack, then pop in extended mode
+ * no errors are possible because every 64-bit pattern
+ * can be converted to an extended
+ */
+ asm ("movl 8(%ebp),%eax");
+ asm ("fldl (%eax)");
+ asm ("fwait");
+ asm ("movl 12(%ebp),%eax");
+ asm ("fstpt (%eax)");
+ asm ("fwait");
+}
+
+struct env387
+{
+ unsigned short control;
+ unsigned short r0;
+ unsigned short status;
+ unsigned short r1;
+ unsigned short tag;
+ unsigned short r2;
+ unsigned long eip;
+ unsigned short code_seg;
+ unsigned short opcode;
+ unsigned long operand;
+ unsigned short operand_seg;
+ unsigned short r3;
+ unsigned char regs[8][10];
+};
+
+void
+print_387_control_word (control)
+unsigned int control;
+{
+ printf ("control 0x%04x: ", control);
+ printf ("compute to ");
+ switch ((control >> 8) & 3)
+ {
+ case 0: printf ("24 bits; "); break;
+ case 1: printf ("(bad); "); break;
+ case 2: printf ("53 bits; "); break;
+ case 3: printf ("64 bits; "); break;
+ }
+ printf ("round ");
+ switch ((control >> 10) & 3)
+ {
+ case 0: printf ("NEAREST; "); break;
+ case 1: printf ("DOWN; "); break;
+ case 2: printf ("UP; "); break;
+ case 3: printf ("CHOP; "); break;
+ }
+ if (control & 0x3f)
+ {
+ printf ("mask:");
+ if (control & 0x0001) printf (" INVALID");
+ if (control & 0x0002) printf (" DENORM");
+ if (control & 0x0004) printf (" DIVZ");
+ if (control & 0x0008) printf (" OVERF");
+ if (control & 0x0010) printf (" UNDERF");
+ if (control & 0x0020) printf (" LOS");
+ printf (";");
+ }
+ printf ("\n");
+ if (control & 0xe080) printf ("warning: reserved bits on 0x%x\n",
+ control & 0xe080);
+}
+
+void
+print_387_status_word (status)
+ unsigned int status;
+{
+ printf ("status 0x%04x: ", status);
+ if (status & 0xff)
+ {
+ printf ("exceptions:");
+ if (status & 0x0001) printf (" INVALID");
+ if (status & 0x0002) printf (" DENORM");
+ if (status & 0x0004) printf (" DIVZ");
+ if (status & 0x0008) printf (" OVERF");
+ if (status & 0x0010) printf (" UNDERF");
+ if (status & 0x0020) printf (" LOS");
+ if (status & 0x0040) printf (" FPSTACK");
+ printf ("; ");
+ }
+ printf ("flags: %d%d%d%d; ",
+ (status & 0x4000) != 0,
+ (status & 0x0400) != 0,
+ (status & 0x0200) != 0,
+ (status & 0x0100) != 0);
+
+ printf ("top %d\n", (status >> 11) & 7);
+}
+
+static
+print_387_status (status, ep)
+ unsigned short status;
+ struct env387 *ep;
+{
+ int i;
+ int bothstatus;
+ int top;
+ int fpreg;
+ unsigned char *p;
+
+ bothstatus = ((status != 0) && (ep->status != 0));
+ if (status != 0)
+ {
+ if (bothstatus)
+ printf ("u: ");
+ print_387_status_word ((unsigned int)status);
+ }
+
+ if (ep->status != 0)
+ {
+ if (bothstatus)
+ printf ("e: ");
+ print_387_status_word ((unsigned int)ep->status);
+ }
+
+ print_387_control_word ((unsigned int)ep->control);
+ printf ("last exception: ");
+ printf ("opcode 0x%x; ", ep->opcode);
+ printf ("pc 0x%x:0x%x; ", ep->code_seg, ep->eip);
+ printf ("operand 0x%x:0x%x\n", ep->operand_seg, ep->operand);
+
+ top = (ep->status >> 11) & 7;
+
+ printf (" regno tag msb lsb value\n");
+ for (fpreg = 7; fpreg >= 0; fpreg--)
+ {
+ int st_regno;
+ double val;
+
+ /* The physical regno `fpreg' is only relevant as an index into the
+ * tag word. Logical `%st' numbers are required for indexing `p->regs.
+ */
+ st_regno = (fpreg + 8 - top) & 0x7;
+
+ printf ("%%st(%d) %s ", st_regno, fpreg == top ? "=>" : " ");
+
+ switch ((ep->tag >> (fpreg * 2)) & 3)
+ {
+ case 0: printf ("valid "); break;
+ case 1: printf ("zero "); break;
+ case 2: printf ("trap "); break;
+ case 3: printf ("empty "); break;
+ }
+ for (i = 9; i >= 0; i--)
+ printf ("%02x", ep->regs[st_regno][i]);
+
+ i387_to_double (ep->regs[st_regno], (char *)&val);
+ printf (" %g\n", val);
+ }
+}
+
+i386_float_info ()
+{
+ struct user u; /* just for address computations */
+ int i;
+ /* fpstate defined in <sys/user.h> */
+ struct fpstate *fpstatep;
+ char buf[sizeof (struct fpstate) + 2 * sizeof (int)];
+ unsigned int uaddr;
+ char fpvalid;
+ unsigned int rounded_addr;
+ unsigned int rounded_size;
+ /*extern int corechan;*/
+ int skip;
+ extern int inferior_pid;
+
+ uaddr = (char *)&U_FPSTATE(u) - (char *)&u;
+ if (inferior_pid)
+ {
+ int *ip;
+
+ rounded_addr = uaddr & -sizeof (int);
+ rounded_size = (((uaddr + sizeof (struct fpstate)) - uaddr) +
+ sizeof (int) - 1) / sizeof (int);
+ skip = uaddr - rounded_addr;
+
+ ip = (int *)buf;
+ for (i = 0; i < rounded_size; i++)
+ {
+ *ip++ = ptrace (PT_READ_U, inferior_pid, (caddr_t)rounded_addr, 0);
+ rounded_addr += sizeof (int);
+ }
+ }
+ else
+ {
+ printf("float info: can't do a core file (yet)\n");
+
+ return;
+#if 0
+ if (lseek (corechan, uaddr, 0) < 0)
+ perror_with_name ("seek on core file");
+ if (myread (corechan, buf, sizeof (struct fpstate)) < 0)
+ perror_with_name ("read from core file");
+ skip = 0;
+#endif
+ }
+
+ print_387_status (0, (struct env387 *)buf);
+}
+
+#endif
diff --git a/gnu/usr.bin/binutils/gdb/i386/nm.h b/gnu/usr.bin/binutils/gdb/i386/nm.h
new file mode 100644
index 0000000..a7af00f
--- /dev/null
+++ b/gnu/usr.bin/binutils/gdb/i386/nm.h
@@ -0,0 +1,44 @@
+/* Native-dependent definitions for Intel 386 running BSD Unix, for GDB.
+ Copyright 1986, 1987, 1989, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef NM_FREEBSD_H
+#define NM_FREEBSD_H
+
+/* Be shared lib aware */
+#include "solib.h"
+
+/* This is the amount to subtract from u.u_ar0
+ to get the offset in the core file of the register values. */
+
+#include <machine/vmparam.h>
+#define KERNEL_U_ADDR USRSTACK
+
+/* #undef FLOAT_INFO /* No float info yet */
+#define FLOAT_INFO extern i386_float_info (); \
+ i386_float_info ()
+
+#define REGISTER_U_ADDR(addr, blockend, regno) \
+ (addr) = i386_register_u_addr ((blockend),(regno));
+
+extern int
+i386_register_u_addr PARAMS ((int, int));
+
+#define PTRACE_ARG3_TYPE char*
+
+#endif /* NM_FREEBSD_H */
diff --git a/gnu/usr.bin/binutils/gdb/i386/tm.h b/gnu/usr.bin/binutils/gdb/i386/tm.h
new file mode 100644
index 0000000..25b66c7
--- /dev/null
+++ b/gnu/usr.bin/binutils/gdb/i386/tm.h
@@ -0,0 +1,76 @@
+/* Macro definitions for i386 running under BSD Unix.
+ Copyright 1986, 1987, 1989, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Override number of expected traps from sysv. */
+#define START_INFERIOR_TRAPS_EXPECTED 2
+
+/* Most definitions from sysv could be used. */
+#include "tm-i386v.h"
+
+/* 386BSD cannot handle the segment registers. */
+/* BSDI can't handle them either. */
+#undef NUM_REGS
+#define NUM_REGS 10
+
+/* On 386 bsd, sigtramp is above the user stack and immediately below
+ the user area. Using constants here allows for cross debugging.
+ These are tested for BSDI but should work on 386BSD. */
+#define SIGTRAMP_START 0xfdbfdfc0
+#define SIGTRAMP_END 0xfdbfe000
+
+/* The following redefines make backtracing through sigtramp work.
+ They manufacture a fake sigtramp frame and obtain the saved pc in sigtramp
+ from the sigcontext structure which is pushed by the kernel on the
+ user stack, along with a pointer to it. */
+
+/* FRAME_CHAIN takes a frame's nominal address and produces the frame's
+ chain-pointer.
+ In the case of the i386, the frame's nominal address
+ is the address of a 4-byte word containing the calling frame's address. */
+#undef FRAME_CHAIN
+#define FRAME_CHAIN(thisframe) \
+ (thisframe->signal_handler_caller \
+ ? thisframe->frame \
+ : (!inside_entry_file ((thisframe)->pc) \
+ ? read_memory_integer ((thisframe)->frame, 4) \
+ : 0))
+
+/* A macro that tells us whether the function invocation represented
+ by FI does not have a frame on the stack associated with it. If it
+ does not, FRAMELESS is set to 1, else 0. */
+#undef FRAMELESS_FUNCTION_INVOCATION
+#define FRAMELESS_FUNCTION_INVOCATION(FI, FRAMELESS) \
+ do { \
+ if ((FI)->signal_handler_caller) \
+ (FRAMELESS) = 0; \
+ else \
+ (FRAMELESS) = frameless_look_for_prologue(FI); \
+ } while (0)
+
+/* Saved Pc. Get it from sigcontext if within sigtramp. */
+
+/* Offset to saved PC in sigcontext, from <sys/signal.h>. */
+#define SIGCONTEXT_PC_OFFSET 20
+
+#undef FRAME_SAVED_PC(FRAME)
+#define FRAME_SAVED_PC(FRAME) \
+ (((FRAME)->signal_handler_caller \
+ ? sigtramp_saved_pc (FRAME) \
+ : read_memory_integer ((FRAME)->frame + 4, 4)) \
+ )
diff --git a/gnu/usr.bin/binutils/gdb/i386/version.c b/gnu/usr.bin/binutils/gdb/i386/version.c
new file mode 100644
index 0000000..d32e958
--- /dev/null
+++ b/gnu/usr.bin/binutils/gdb/i386/version.c
@@ -0,0 +1,3 @@
+char *version = "4.11";
+char *host_canonical = "i386-unknown-freebsd";
+char *target_canonical = "i386-unknown-freebsd";
diff --git a/gnu/usr.bin/binutils/gdb/i386/xm.h b/gnu/usr.bin/binutils/gdb/i386/xm.h
new file mode 100644
index 0000000..8d28df0
--- /dev/null
+++ b/gnu/usr.bin/binutils/gdb/i386/xm.h
@@ -0,0 +1,31 @@
+/* Host-dependent definitions for Intel 386 running BSD Unix, for GDB.
+ Copyright 1986, 1987, 1989, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define HOST_BYTE_ORDER LITTLE_ENDIAN
+
+#include <machine/limits.h> /* for INT_MIN, to avoid "INT_MIN
+ redefined" warnings from defs.h */
+
+/* psignal() is in <signal.h>. */
+
+#define PSIGNAL_IN_SIGNAL_H
+
+/* Get rid of any system-imposed stack limit if possible. */
+
+#define SET_STACK_LIMIT_HUGE
diff --git a/gnu/usr.bin/cc/Makefile b/gnu/usr.bin/cc/Makefile
new file mode 100644
index 0000000..74e88b8
--- /dev/null
+++ b/gnu/usr.bin/cc/Makefile
@@ -0,0 +1,8 @@
+#
+# $FreeBSD$
+#
+
+PGMDIR= cc_int cpp cc1 cc cc1plus c++ libgcc
+SUBDIR= $(PGMDIR)
+
+.include <bsd.subdir.mk>
diff --git a/gnu/usr.bin/cc/Makefile.inc b/gnu/usr.bin/cc/Makefile.inc
new file mode 100644
index 0000000..d3a8b22
--- /dev/null
+++ b/gnu/usr.bin/cc/Makefile.inc
@@ -0,0 +1,22 @@
+#
+# $FreeBSD$
+#
+
+CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../include
+CFLAGS+= -Dbsd4_4
+CFLAGS+= -DGCC_INCLUDE_DIR=\"FOO\"
+CFLAGS+= -DDEFAULT_TARGET_VERSION=\"2.6.0\"
+CFLAGS+= -DDEFAULT_TARGET_MACHINE=\"i386--freebsd\"
+CFLAGS+= -DMD_EXEC_PREFIX=\"/usr/libexec/\"
+CFLAGS+= -DSTANDARD_STARTFILE_PREFIX=\"/usr/lib\"
+CFLAGS+= -DGCC_NAME=\"cc\"
+
+.if exists(${.CURDIR}/../cc_int/obj)
+LIBDESTDIR= ${.CURDIR}/../cc_int/obj
+.else
+LIBDESTDIR= ${.CURDIR}/../cc_int
+.endif
+
+# XXX LDDESTDIR isn't a directory and there is no standard name for the dir
+LDDESTDIR= -L${LIBDESTDIR}
+LIBCC_INT= ${LIBDESTDIR}/libcc_int.a
diff --git a/gnu/usr.bin/cc/README b/gnu/usr.bin/cc/README
new file mode 100644
index 0000000..01303ea
--- /dev/null
+++ b/gnu/usr.bin/cc/README
@@ -0,0 +1,16 @@
+
+$FreeBSD$
+
+This directory contains gcc in a form that uses "bmake" makefiles.
+This is not the place you want to start, if you want to hack gcc.
+we have included everything here which is part of the source-code
+of gcc, but still, don't use this as a hacking-base.
+
+If you suspect a problem with gcc, or just want to hack it in general,
+get a complete gcc-X.Y.Z.tar.gz from somewhere, and use that.
+
+Please look in the directory src/gnu/gnu2bmake to find the tools
+to generate these files.
+
+Thankyou.
+
diff --git a/gnu/usr.bin/cc/c++/Makefile b/gnu/usr.bin/cc/c++/Makefile
new file mode 100644
index 0000000..6b400b6
--- /dev/null
+++ b/gnu/usr.bin/cc/c++/Makefile
@@ -0,0 +1,12 @@
+#
+# $FreeBSD$
+#
+
+PROG = c++
+SRCS = g++.c
+BINDIR= /usr/bin
+NOMAN= 1
+DPADD+= ${LIBCC_INT}
+LDADD+= -lcc_int
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/cc/c++/g++.c b/gnu/usr.bin/cc/c++/g++.c
new file mode 100644
index 0000000..fcd1029
--- /dev/null
+++ b/gnu/usr.bin/cc/c++/g++.c
@@ -0,0 +1,535 @@
+/* G++ preliminary semantic processing for the compiler driver.
+ Copyright (C) 1993, 1994 Free Software Foundation, Inc.
+ Contributed by Brendan Kehoe (brendan@cygnus.com).
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This program is a wrapper to the main `gcc' driver. For GNU C++,
+ we need to do two special things: a) append `-lg++' in situations
+ where it's appropriate, to link in libg++, and b) add `-xc++'..`-xnone'
+ around file arguments named `foo.c' or `foo.i'. So, we do all of
+ this semantic processing then just exec gcc with the new argument
+ list.
+
+ We used to do all of this in a small shell script, but many users
+ found the performance of this as a shell script to be unacceptable.
+ In situations where your PATH has a lot of NFS-mounted directories,
+ using a script that runs sed and other things would be a nasty
+ performance hit. With this program, we never search the PATH at all. */
+
+#include "config.h"
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/file.h> /* May get R_OK, etc. on some systems. */
+
+/* Defined to the name of the compiler; if using a cross compiler, the
+ Makefile should compile this file with the proper name
+ (e.g., "i386-aout-gcc"). */
+#ifndef GCC_NAME
+#define GCC_NAME "gcc"
+#endif
+
+/* This bit is set if we saw a `-xfoo' language specification. */
+#define LANGSPEC (1<<1)
+/* This bit is set if they did `-lm' or `-lmath'. */
+#define MATHLIB (1<<2)
+
+/* On MSDOS, write temp files in current dir
+ because there's no place else we can expect to use. */
+#ifdef __MSDOS__
+#ifndef P_tmpdir
+#define P_tmpdir "."
+#endif
+#ifndef R_OK
+#define R_OK 4
+#define W_OK 2
+#define X_OK 1
+#endif
+#endif
+
+#ifndef VPROTO
+#ifdef __STDC__
+#define PVPROTO(ARGS) ARGS
+#define VPROTO(ARGS) ARGS
+#define VA_START(va_list,var) va_start(va_list,var)
+#else
+#define PVPROTO(ARGS) ()
+#define VPROTO(ARGS) (va_alist) va_dcl
+#define VA_START(va_list,var) va_start(va_list)
+#endif
+#endif
+
+extern int errno, sys_nerr;
+#if defined(bsd4_4) || defined(__NetBSD__)
+extern const char *const sys_errlist[];
+#else
+extern char *sys_errlist[];
+#endif
+
+/* Name with which this program was invoked. */
+static char *programname;
+
+#ifdef HAVE_VPRINTF
+/* Output an error message and exit */
+
+static void
+fatal VPROTO((char *format, ...))
+{
+#ifndef __STDC__
+ char *format;
+#endif
+ va_list ap;
+
+ VA_START (ap, format);
+
+#ifndef __STDC__
+ format = va_arg (ap, char*);
+#endif
+
+ fprintf (stderr, "%s: ", programname);
+ vfprintf (stderr, format, ap);
+ va_end (ap);
+ fprintf (stderr, "\n");
+#if 0
+ /* XXX Not needed for g++ driver. */
+ delete_temp_files ();
+#endif
+ exit (1);
+}
+
+static void
+error VPROTO((char *format, ...))
+{
+#ifndef __STDC__
+ char *format;
+#endif
+ va_list ap;
+
+ VA_START (ap, format);
+
+#ifndef __STDC__
+ format = va_arg (ap, char*);
+#endif
+
+ fprintf (stderr, "%s: ", programname);
+ vfprintf (stderr, format, ap);
+ va_end (ap);
+
+ fprintf (stderr, "\n");
+}
+
+#else /* not HAVE_VPRINTF */
+
+static void
+error (msg, arg1, arg2)
+ char *msg, *arg1, *arg2;
+{
+ fprintf (stderr, "%s: ", programname);
+ fprintf (stderr, msg, arg1, arg2);
+ fprintf (stderr, "\n");
+}
+
+static void
+fatal (msg, arg1, arg2)
+ char *msg, *arg1, *arg2;
+{
+ error (msg, arg1, arg2);
+#if 0
+ /* XXX Not needed for g++ driver. */
+ delete_temp_files ();
+#endif
+ exit (1);
+}
+
+#endif /* not HAVE_VPRINTF */
+
+/* More 'friendly' abort that prints the line and file.
+ config.h can #define abort fancy_abort if you like that sort of thing. */
+
+void
+fancy_abort ()
+{
+ fatal ("Internal g++ abort.");
+}
+
+char *
+xmalloc (size)
+ unsigned size;
+{
+ register char *value = (char *) malloc (size);
+ if (value == 0)
+ fatal ("virtual memory exhausted");
+ return value;
+}
+
+/* Return a newly-allocated string whose contents concatenate those
+ of s1, s2, s3. */
+static char *
+concat (s1, s2, s3)
+ char *s1, *s2, *s3;
+{
+ int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
+ char *result = xmalloc (len1 + len2 + len3 + 1);
+
+ strcpy (result, s1);
+ strcpy (result + len1, s2);
+ strcpy (result + len1 + len2, s3);
+ *(result + len1 + len2 + len3) = 0;
+
+ return result;
+}
+
+static void
+pfatal_with_name (name)
+ char *name;
+{
+ char *s;
+
+ if (errno < sys_nerr)
+ s = concat ("%s: ", sys_errlist[errno], "");
+ else
+ s = "cannot open %s";
+ fatal (s, name);
+}
+
+#ifdef __MSDOS__
+/* This is the common prefix we use to make temp file names. */
+char *temp_filename;
+
+/* Length of the prefix. */
+int temp_filename_length;
+
+/* Compute a string to use as the base of all temporary file names. */
+static char *
+choose_temp_base_try (try, base)
+char *try;
+char *base;
+{
+ char *rv;
+ if (base)
+ rv = base;
+ else if (try == (char *)0)
+ rv = 0;
+ else if (access (try, R_OK | W_OK) != 0)
+ rv = 0;
+ else
+ rv = try;
+ return rv;
+}
+
+static void
+choose_temp_base ()
+{
+ char *base = 0;
+ int len;
+
+ base = choose_temp_base_try (getenv ("TMPDIR"), base);
+ base = choose_temp_base_try (getenv ("TMP"), base);
+ base = choose_temp_base_try (getenv ("TEMP"), base);
+
+#ifdef P_tmpdir
+ base = choose_temp_base_try (P_tmpdir, base);
+#endif
+
+ base = choose_temp_base_try ("/usr/tmp", base);
+ base = choose_temp_base_try ("/tmp", base);
+
+ /* If all else fails, use the current directory! */
+ if (base == (char *)0)
+ base = "./";
+
+ len = strlen (base);
+ temp_filename = xmalloc (len + sizeof("/ccXXXXXX"));
+ strcpy (temp_filename, base);
+ if (len > 0 && temp_filename[len-1] != '/')
+ temp_filename[len++] = '/';
+ strcpy (temp_filename + len, "ccXXXXXX");
+
+ mktemp (temp_filename);
+ temp_filename_length = strlen (temp_filename);
+ if (temp_filename_length == 0)
+ abort ();
+}
+
+static void
+perror_exec (name)
+ char *name;
+{
+ char *s;
+
+ if (errno < sys_nerr)
+ s = concat ("installation problem, cannot exec %s: ",
+ sys_errlist[errno], "");
+ else
+ s = "installation problem, cannot exec %s";
+ error (s, name);
+}
+
+/* This is almost exactly what's in gcc.c:pexecute for MSDOS. */
+void
+run_dos (program, argv)
+ char *program;
+ char *argv[];
+{
+ char *scmd, *rf;
+ FILE *argfile;
+ int i;
+
+ choose_temp_base (); /* not in gcc.c */
+
+ scmd = (char *) malloc (strlen (program) + strlen (temp_filename) + 10);
+ rf = scmd + strlen (program) + 6;
+ sprintf (scmd, "%s.exe @%s.gp", program, temp_filename);
+
+ argfile = fopen (rf, "w");
+ if (argfile == 0)
+ pfatal_with_name (rf);
+
+ for (i=1; argv[i]; i++)
+ {
+ char *cp;
+ for (cp = argv[i]; *cp; cp++)
+ {
+ if (*cp == '"' || *cp == '\'' || *cp == '\\' || isspace (*cp))
+ fputc ('\\', argfile);
+ fputc (*cp, argfile);
+ }
+ fputc ('\n', argfile);
+ }
+ fclose (argfile);
+
+ i = system (scmd);
+
+ remove (rf);
+
+ if (i == -1)
+ perror_exec (program);
+}
+#endif /* __MSDOS__ */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ register int i, j = 0;
+ register char *p;
+ int verbose = 0;
+
+ /* This will be NULL if we encounter a situation where we should not
+ link in libg++. */
+ char *library = "-lg++";
+
+ /* Used to track options that take arguments, so we don't go wrapping
+ those with -xc++/-xnone. */
+ char *quote = NULL;
+
+ /* The new argument list will be contained in this. */
+ char **arglist;
+
+ /* The name of the compiler we will want to run---by default, it
+ will be the definition of `GCC_NAME', e.g., `gcc'. */
+ char *gcc = GCC_NAME;
+
+ /* Non-zero if we saw a `-xfoo' language specification on the
+ command line. Used to avoid adding our own -xc++ if the user
+ already gave a language for the file. */
+ int saw_speclang = 0;
+
+ /* Non-zero if we saw `-lm' or `-lmath' on the command line. */
+ int saw_math = 0;
+
+ /* The number of arguments being added to what's in argv. By
+ default it's one new argument (adding `-lg++'). We use this
+ to track the number of times we've inserted -xc++/-xnone as well. */
+ int added = 1;
+
+ /* An array used to flag each argument that needs a bit set for
+ LANGSPEC or MATHLIB. */
+ int *args;
+
+ p = argv[0] + strlen (argv[0]);
+ while (p != argv[0] && p[-1] != '/')
+ --p;
+ programname = p;
+
+ if (argc == 1)
+ fatal ("No input files specified.\n");
+
+#ifndef __MSDOS__
+ /* We do a little magic to find out where the main gcc executable
+ is. If they ran us as /usr/local/bin/g++, then we will look
+ for /usr/local/bin/gcc; similarly, if they just ran us as `g++',
+ we'll just look for `gcc'. */
+ if (p != argv[0])
+ {
+ *--p = '\0';
+ gcc = (char *) malloc ((strlen (argv[0]) + 1 + strlen (GCC_NAME) + 1)
+ * sizeof (char));
+ sprintf (gcc, "%s/%s", argv[0], GCC_NAME);
+ }
+#endif
+
+ args = (int *) malloc (argc * sizeof (int));
+ bzero (args, argc * sizeof (int));
+
+ for (i = 1; i < argc; i++)
+ {
+ /* If the previous option took an argument, we swallow it here. */
+ if (quote)
+ {
+ quote = NULL;
+ continue;
+ }
+
+ if (argv[i][0] == '\0' || argv[i][1] == '\0')
+ continue;
+
+ if (argv[i][0] == '-')
+ {
+ if (strcmp (argv[i], "-nostdlib") == 0)
+ {
+ added--;
+ library = NULL;
+ }
+ else if (strcmp (argv[i], "-lm") == 0
+ || strcmp (argv[i], "-lmath") == 0)
+ args[i] |= MATHLIB;
+ else if (strcmp (argv[i], "-v") == 0)
+ {
+ verbose = 1;
+ if (argc == 2)
+ {
+ /* If they only gave us `-v', don't try to link
+ in libg++. */
+ added--;
+ library = NULL;
+ }
+ }
+ else if (strncmp (argv[i], "-x", 2) == 0)
+ saw_speclang = 1;
+ else if (((argv[i][2] == '\0'
+ && (char *)strchr ("bBVDUoeTuIYmLiA", argv[i][1]) != NULL)
+ || strcmp (argv[i], "-Tdata") == 0))
+ quote = argv[i];
+ else if (((argv[i][2] == '\0'
+ && (char *) strchr ("cSEM", argv[i][1]) != NULL)
+ || strcmp (argv[i], "-MM") == 0))
+ {
+ /* Don't specify libraries if we won't link, since that would
+ cause a warning. */
+ added--;
+ library = NULL;
+ }
+ else
+ /* Pass other options through. */
+ continue;
+ }
+ else
+ {
+ int len;
+
+ if (saw_speclang)
+ continue;
+
+ /* If the filename ends in .c or .i, put options around it.
+ But not if a specified -x option is currently active. */
+ len = strlen (argv[i]);
+ if (len > 2
+ && (argv[i][len - 1] == 'c' || argv[i][len - 1] == 'i')
+ && argv[i][len - 2] == '.')
+ {
+ args[i] |= LANGSPEC;
+ added += 2;
+ }
+ }
+ }
+
+ if (quote)
+ fatal ("argument to `%s' missing\n", quote);
+
+ if (added)
+ {
+ arglist = (char **) malloc ((argc + added + 1) * sizeof (char *));
+
+ for (i = 1, j = 1; i < argc; i++, j++)
+ {
+ arglist[j] = argv[i];
+
+ /* Make sure -lg++ is before the math library, since libg++
+ itself uses those math routines. */
+ if (!saw_math && (args[i] & MATHLIB) && library)
+ {
+ saw_math = 1;
+ arglist[j] = library;
+ arglist[++j] = argv[i];
+ }
+
+ /* Wrap foo.c and foo.i files in a language specification to
+ force the gcc compiler driver to run cc1plus on them. */
+ if (args[i] & LANGSPEC)
+ {
+ int len = strlen (argv[i]);
+ if (argv[i][len - 1] == 'i')
+ arglist[j++] = "-xc++-cpp-output";
+ else
+ arglist[j++] = "-xc++";
+ arglist[j++] = argv[i];
+ arglist[j] = "-xnone";
+ }
+ }
+
+ /* Add `-lg++' if we haven't already done so. */
+ if (library && !saw_math)
+ arglist[j++] = library;
+
+ arglist[j] = NULL;
+ }
+ else
+ /* No need to copy 'em all. */
+ arglist = argv;
+
+ arglist[0] = gcc;
+
+ if (verbose)
+ {
+ if (j == 0)
+ j = argc;
+
+ for (i = 0; i < j; i++)
+ fprintf (stderr, " %s", arglist[i]);
+ fprintf (stderr, "\n");
+ }
+#ifndef OS2
+#ifdef __MSDOS__
+ run_dos (gcc, arglist);
+#else /* !__MSDOS__ */
+ if (execvp (gcc, arglist) < 0)
+ pfatal_with_name (gcc);
+#endif /* __MSDOS__ */
+#else /* OS2 */
+ if (spawnvp (gcc, arglist) < 0)
+ pfatal_with_name (gcc);
+#endif
+
+ return 0;
+}
diff --git a/gnu/usr.bin/cc/cc/Makefile b/gnu/usr.bin/cc/cc/Makefile
new file mode 100644
index 0000000..44fabc5
--- /dev/null
+++ b/gnu/usr.bin/cc/cc/Makefile
@@ -0,0 +1,11 @@
+#
+# $FreeBSD$
+#
+
+PROG = cc
+SRCS = gcc.c
+BINDIR= /usr/bin
+DPADD+= ${LIBCC_INT}
+LDADD+= -lcc_int
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/cc/cc/cc.1 b/gnu/usr.bin/cc/cc/cc.1
new file mode 100644
index 0000000..223115c
--- /dev/null
+++ b/gnu/usr.bin/cc/cc/cc.1
@@ -0,0 +1,4111 @@
+.\" Copyright (c) 1991, 1992, 1993, 1994 Free Software Foundation -*-Text-*-
+.\" See section COPYING for conditions for redistribution
+.\"
+.\" Set up \*(lq, \*(rq if -man hasn't already set it up.
+.if @@\*(lq@ \{\
+. ds lq "
+. if t .ds lq ``
+. if !@@\(lq@ .ds lq "\(lq
+.\}
+.if @@\*(rq@ \{\
+. ds rq "
+. if t .ds rq ''
+. if !@@\(rq@ .ds rq "\(rq
+.\}
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.de Sp
+.if n .sp
+.if t .sp 0.4
+..
+.Id $Id: gcc.1,v 1.4 1993/10/13 23:19:12 pesch Exp $
+.TH GCC 1 "\*(Dt" "GNU Tools" "GNU Tools"
+.SH NAME
+gcc, g++ \- GNU project C and C++ Compiler (v2.6)
+.SH SYNOPSIS
+.B gcc
+.RI "[ " option " | " filename " ].\|.\|."
+.br
+.B g++
+.RI "[ " option " | " filename " ].\|.\|."
+.SH WARNING
+The information in this man page is an extract from the full
+documentation of the GNU C compiler, and is limited to the meaning of
+the options.
+.PP
+This man page is not kept up to date except when volunteers want to
+maintain it. If you find a discrepancy between the man page and the
+software, please check the Info file, which is the authoritative
+documentation.
+.PP
+If we find that the things in this man page that are out of date cause
+significant confusion or complaints, we will stop distributing the man
+page. The alternative, updating the man page when we update the Info
+file, is impossible because the rest of the work of maintaining GNU CC
+leaves us no time for that. The GNU project regards man pages as
+obsolete and should not let them take time away from other things.
+.PP
+For complete and current documentation, refer to the Info file `\|\c
+.B gcc\c
+\&\|' or the manual
+.I
+Using and Porting GNU CC (for version 2.0)\c
+\&. Both are made from the Texinfo source file
+.BR gcc.texinfo .
+.SH DESCRIPTION
+The C and C++ compilers are integrated. Both process input files
+through one or more of four stages: preprocessing, compilation,
+assembly, and linking. Source filename suffixes identify the source
+language, but which name you use for the compiler governs default
+assumptions:
+.TP
+.B gcc
+assumes preprocessed (\c
+.B .i\c
+\&) files are C and assumes C style linking.
+.TP
+.B g++
+assumes preprocessed (\c
+.B .i\c
+\&) files are C++ and assumes C++ style linking.
+.PP
+Suffixes of source file names indicate the language and kind of
+processing to be done:
+.Sp
+.nf
+.ta \w'\fB.cxx\fP 'u
+\&\fB.c\fP C source; preprocess, compile, assemble
+\&\fB.C\fP C++ source; preprocess, compile, assemble
+\&\fB.cc\fP C++ source; preprocess, compile, assemble
+\&\fB.cxx\fP C++ source; preprocess, compile, assemble
+\&\fB.m\fP Objective-C source; preprocess, compile, assemble
+\&\fB.i\fP preprocessed C; compile, assemble
+\&\fB.ii\fP preprocessed C++; compile, assemble
+\&\fB.s\fP Assembler source; assemble
+\&\fB.S\fP Assembler source; preprocess, assemble
+\&\fB.h\fP Preprocessor file; not usually named on command line
+.Sp
+.fi
+Files with other suffixes are passed to the linker. Common cases include:
+.Sp
+.nf
+\&\fB.o\fP Object file
+\&\fB.a\fP Archive file
+.br
+.fi
+.Sp
+Linking is always the last stage unless you use one of the
+.BR \-c ,
+.BR \-S ,
+or
+.B \-E
+options to avoid it (or unless compilation errors stop the whole
+process). For the link stage, all
+.B .o
+files corresponding to source files,
+.B \-l
+libraries, unrecognized filenames (including named
+.B .o
+object files and
+.B .a
+archives)
+are passed to the linker in command-line order.
+.SH OPTIONS
+Options must be separate: `\|\c
+.B \-dr\c
+\&\|' is quite different from `\|\c
+.B \-d \-r
+\&\|'.
+.PP
+Most `\|\c
+.B \-f\c
+\&\|' and `\|\c
+.B \-W\c
+\&\|' options have two contrary forms:
+.BI \-f name
+and
+.BI \-fno\- name\c
+\& (or
+.BI \-W name
+and
+.BI \-Wno\- name\c
+\&). Only the non-default forms are shown here.
+.PP
+Here is a summary of all the options, grouped by type. Explanations are
+in the following sections.
+.hy 0
+.na
+.TP
+.B Overall Options
+.br
+\-c
+\-S
+\-E
+.RI "\-o " file
+\-pipe
+\-v
+.RI "\-x " language
+.TP
+.B Language Options
+\-ansi
+\-fall\-virtual
+\-fcond\-mismatch
+\-fdollars\-in\-identifiers
+\-fenum\-int\-equiv
+\-fexternal\-templates
+\-fno\-asm
+\-fno\-builtin
+\-fno\-strict\-prototype
+\-fsigned\-bitfields
+\-fsigned\-char
+\-fthis\-is\-variable
+\-funsigned\-bitfields
+\-funsigned\-char
+\-fwritable\-strings
+\-traditional
+\-traditional\-cpp
+\-trigraphs
+.TP
+.B Warning Options
+\-fsyntax\-only
+\-pedantic
+\-pedantic\-errors
+\-w
+\-W
+\-Wall
+\-Waggregate\-return
+\-Wcast\-align
+\-Wcast\-qual
+\-Wchar\-subscript
+\-Wcomment
+\-Wconversion
+\-Wenum\-clash
+\-Werror
+\-Wformat
+.RI \-Wid\-clash\- len
+\-Wimplicit
+\-Winline
+\-Wmissing\-prototypes
+\-Wmissing\-declarations
+\-Wnested\-externs
+\-Wno\-import
+\-Wparentheses
+\-Wpointer\-arith
+\-Wredundant\-decls
+\-Wreturn\-type
+\-Wshadow
+\-Wstrict\-prototypes
+\-Wswitch
+\-Wtemplate\-debugging
+\-Wtraditional
+\-Wtrigraphs
+\-Wuninitialized
+\-Wunused
+\-Wwrite\-strings
+.TP
+.B Debugging Options
+\-a
+.RI \-d letters
+\-fpretend\-float
+\-g
+.RI \-g level
+\-gcoff
+\-gxcoff
+\-gxcoff+
+\-gdwarf
+\-gdwarf+
+\-gstabs
+\-gstabs+
+\-ggdb
+\-p
+\-pg
+\-save\-temps
+.RI \-print\-file\-name= library
+\-print\-libgcc\-file\-name
+.RI \-print\-prog\-name= program
+.TP
+.B Optimization Options
+\-fcaller\-saves
+\-fcse\-follow\-jumps
+\-fcse\-skip\-blocks
+\-fdelayed\-branch
+\-felide\-constructors
+\-fexpensive\-optimizations
+\-ffast\-math
+\-ffloat\-store
+\-fforce\-addr
+\-fforce\-mem
+\-finline\-functions
+\-fkeep\-inline\-functions
+\-fmemoize\-lookups
+\-fno\-default\-inline
+\-fno\-defer\-pop
+\-fno\-function\-cse
+\-fno\-inline
+\-fno\-peephole
+\-fomit\-frame\-pointer
+\-frerun\-cse\-after\-loop
+\-fschedule\-insns
+\-fschedule\-insns2
+\-fstrength\-reduce
+\-fthread\-jumps
+\-funroll\-all\-loops
+\-funroll\-loops
+\-O
+\-O2
+.TP
+.B Preprocessor Options
+.RI \-A assertion
+\-C
+\-dD
+\-dM
+\-dN
+.RI \-D macro [\|= defn \|]
+\-E
+\-H
+.RI "\-idirafter " dir
+.RI "\-include " file
+.RI "\-imacros " file
+.RI "\-iprefix " file
+.RI "\-iwithprefix " dir
+\-M
+\-MD
+\-MM
+\-MMD
+\-nostdinc
+\-P
+.RI \-U macro
+\-undef
+.TP
+.B Assembler Option
+.RI \-Wa, option
+.TP
+.B Linker Options
+.RI \-l library
+\-nostartfiles
+\-nostdlib
+\-static
+\-shared
+\-symbolic
+.RI "\-Xlinker\ " option
+.RI \-Wl, option
+.RI "\-u " symbol
+.TP
+.B Directory Options
+.RI \-B prefix
+.RI \-I dir
+\-I\-
+.RI \-L dir
+.TP
+.B Target Options
+.RI "\-b " machine
+.RI "\-V " version
+.TP
+.B Configuration Dependent Options
+.I M680x0\ Options
+.br
+\-m68000
+\-m68020
+\-m68020\-40
+\-m68030
+\-m68040
+\-m68881
+\-mbitfield
+\-mc68000
+\-mc68020
+\-mfpa
+\-mnobitfield
+\-mrtd
+\-mshort
+\-msoft\-float
+.Sp
+.I VAX Options
+.br
+\-mg
+\-mgnu
+\-munix
+.Sp
+.I SPARC Options
+.br
+\-mepilogue
+\-mfpu
+\-mhard\-float
+\-mno\-fpu
+\-mno\-epilogue
+\-msoft\-float
+\-msparclite
+\-mv8
+\-msupersparc
+\-mcypress
+.Sp
+.I Convex Options
+.br
+\-margcount
+\-mc1
+\-mc2
+\-mnoargcount
+.Sp
+.I AMD29K Options
+.br
+\-m29000
+\-m29050
+\-mbw
+\-mdw
+\-mkernel\-registers
+\-mlarge
+\-mnbw
+\-mnodw
+\-msmall
+\-mstack\-check
+\-muser\-registers
+.Sp
+.I M88K Options
+.br
+\-m88000
+\-m88100
+\-m88110
+\-mbig\-pic
+\-mcheck\-zero\-division
+\-mhandle\-large\-shift
+\-midentify\-revision
+\-mno\-check\-zero\-division
+\-mno\-ocs\-debug\-info
+\-mno\-ocs\-frame\-position
+\-mno\-optimize\-arg\-area
+\-mno\-seriazlize\-volatile
+\-mno\-underscores
+\-mocs\-debug\-info
+\-mocs\-frame\-position
+\-moptimize\-arg\-area
+\-mserialize\-volatile
+.RI \-mshort\-data\- num
+\-msvr3
+\-msvr4
+\-mtrap\-large\-shift
+\-muse\-div\-instruction
+\-mversion\-03.00
+\-mwarn\-passed\-structs
+.Sp
+.I RS6000 Options
+.br
+\-mfp\-in\-toc
+\-mno\-fop\-in\-toc
+.Sp
+.I RT Options
+.br
+\-mcall\-lib\-mul
+\-mfp\-arg\-in\-fpregs
+\-mfp\-arg\-in\-gregs
+\-mfull\-fp\-blocks
+\-mhc\-struct\-return
+\-min\-line\-mul
+\-mminimum\-fp\-blocks
+\-mnohc\-struct\-return
+.Sp
+.I MIPS Options
+.br
+\-mcpu=\fIcpu type\fP
+\-mips2
+\-mips3
+\-mint64
+\-mlong64
+\-mlonglong128
+\-mmips\-as
+\-mgas
+\-mrnames
+\-mno\-rnames
+\-mgpopt
+\-mno\-gpopt
+\-mstats
+\-mno\-stats
+\-mmemcpy
+\-mno\-memcpy
+\-mno\-mips\-tfile
+\-mmips\-tfile
+\-msoft\-float
+\-mhard\-float
+\-mabicalls
+\-mno\-abicalls
+\-mhalf\-pic
+\-mno\-half\-pic
+\-G \fInum\fP
+\-nocpp
+.Sp
+.I i386 Options
+.br
+\-m486
+\-mno\-486
+\-msoft\-float
+\-mno\-fp\-ret\-in\-387
+.Sp
+.I HPPA Options
+.br
+\-mpa\-risc\-1\-0
+\-mpa\-risc\-1\-1
+\-mkernel
+\-mshared\-libs
+\-mno\-shared\-libs
+\-mlong\-calls
+\-mdisable\-fpregs
+\-mdisable\-indexing
+\-mtrailing\-colon
+.Sp
+.I i960 Options
+.br
+\-m\fIcpu-type\fP
+\-mnumerics
+\-msoft\-float
+\-mleaf\-procedures
+\-mno\-leaf\-procedures
+\-mtail\-call
+\-mno\-tail\-call
+\-mcomplex\-addr
+\-mno\-complex\-addr
+\-mcode\-align
+\-mno\-code\-align
+\-mic\-compat
+\-mic2.0\-compat
+\-mic3.0\-compat
+\-masm\-compat
+\-mintel\-asm
+\-mstrict\-align
+\-mno\-strict\-align
+\-mold\-align
+\-mno\-old\-align
+.Sp
+.I DEC Alpha Options
+.br
+\-mfp\-regs
+\-mno\-fp\-regs
+\-mno\-soft\-float
+\-msoft\-float
+.Sp
+.I System V Options
+.br
+\-G
+\-Qy
+\-Qn
+.RI \-YP, paths
+.RI \-Ym, dir
+.TP
+.B Code Generation Options
+.RI \-fcall\-saved\- reg
+.RI \-fcall\-used\- reg
+.RI \-ffixed\- reg
+\-finhibit\-size\-directive
+\-fnonnull\-objects
+\-fno\-common
+\-fno\-ident
+\-fno\-gnu\-linker
+\-fpcc\-struct\-return
+\-fpic
+\-fPIC
+\-freg\-struct\-returno
+\-fshared\-data
+\-fshort\-enums
+\-fshort\-double
+\-fvolatile
+\-fvolatile\-global
+\-fverbose\-asm
+.ad b
+.hy 1
+.SH OVERALL OPTIONS
+.TP
+.BI "\-x " "language"
+Specify explicitly the
+.I language\c
+\& for the following input files (rather than choosing a default based
+on the file name suffix) . This option applies to all following input
+files until the next `\|\c
+.B \-x\c
+\&\|' option. Possible values of \c
+.I language\c
+\& are
+`\|\c
+.B c\c
+\&\|', `\|\c
+.B objective\-c\c
+\&\|', `\|\c
+.B c\-header\c
+\&\|', `\|\c
+.B c++\c
+\&\|',
+`\|\c
+.B cpp\-output\c
+\&\|', `\|\c
+.B assembler\c
+\&\|', and `\|\c
+.B assembler\-with\-cpp\c
+\&\|'.
+.TP
+.B \-x none
+Turn off any specification of a language, so that subsequent files are
+handled according to their file name suffixes (as they are if `\|\c
+.B \-x\c
+\&\|'
+has not been used at all).
+.PP
+If you want only some of the four stages (preprocess, compile,
+assemble, link), you can use
+`\|\c
+.B \-x\c
+\&\|' (or filename suffixes) to tell \c
+.B gcc\c
+\& where to start, and
+one of the options `\|\c
+.B \-c\c
+\&\|', `\|\c
+.B \-S\c
+\&\|', or `\|\c
+.B \-E\c
+\&\|' to say where
+.B gcc\c
+\& is to stop. Note that some combinations (for example,
+`\|\c
+.B \-x cpp\-output \-E\c
+\&\|') instruct \c
+.B gcc\c
+\& to do nothing at all.
+.TP
+.B \-c
+Compile or assemble the source files, but do not link. The compiler
+output is an object file corresponding to each source file.
+.Sp
+By default, GCC makes the object file name for a source file by replacing
+the suffix `\|\c
+.B .c\c
+\&\|', `\|\c
+.B .i\c
+\&\|', `\|\c
+.B .s\c
+\&\|', etc., with `\|\c
+.B .o\c
+\&\|'. Use
+.B \-o\c
+\& to select another name.
+.Sp
+GCC ignores any unrecognized input files (those that do not require
+compilation or assembly) with the
+.B \-c
+option.
+.TP
+.B \-S
+Stop after the stage of compilation proper; do not assemble. The output
+is an assembler code file for each non-assembler input
+file specified.
+.Sp
+By default, GCC makes the assembler file name for a source file by
+replacing the suffix `\|\c
+.B .c\c
+\&\|', `\|\c
+.B .i\c
+\&\|', etc., with `\|\c
+.B .s\c
+\&\|'. Use
+.B \-o\c
+\& to select another name.
+.Sp
+GCC ignores any input files that don't require compilation.
+.TP
+.B \-E
+Stop after the preprocessing stage; do not run the compiler proper. The
+output is preprocessed source code, which is sent to the
+standard output.
+.Sp
+GCC ignores input files which don't require preprocessing.
+.TP
+.BI "\-o " file
+Place output in file \c
+.I file\c
+\&. This applies regardless to whatever
+sort of output GCC is producing, whether it be an executable file,
+an object file, an assembler file or preprocessed C code.
+.Sp
+Since only one output file can be specified, it does not make sense to
+use `\|\c
+.B \-o\c
+\&\|' when compiling more than one input file, unless you are
+producing an executable file as output.
+.Sp
+If you do not specify `\|\c
+.B \-o\c
+\&\|', the default is to put an executable file
+in `\|\c
+.B a.out\c
+\&\|', the object file for `\|\c
+.I source\c
+.B \&.\c
+.I suffix\c
+\&\c
+\&\|' in
+`\|\c
+.I source\c
+.B \&.o\c
+\&\|', its assembler file in `\|\c
+.I source\c
+.B \&.s\c
+\&\|', and
+all preprocessed C source on standard output.
+.TP
+.B \-v
+Print (on standard error output) the commands executed to run the stages
+of compilation. Also print the version number of the compiler driver
+program and of the preprocessor and the compiler proper.
+.TP
+.B \-pipe
+Use pipes rather than temporary files for communication between the
+various stages of compilation. This fails to work on some systems where
+the assembler cannot read from a pipe; but the GNU assembler has
+no trouble.
+.PP
+.SH LANGUAGE OPTIONS
+The following options control the dialect of C that the compiler
+accepts:
+.TP
+.B \-ansi
+Support all ANSI standard C programs.
+.Sp
+This turns off certain features of GNU C that are incompatible with
+ANSI C, such as the \c
+.B asm\c
+\&, \c
+.B inline\c
+\& and \c
+.B typeof
+keywords, and predefined macros such as \c
+.B unix\c
+\& and \c
+.B vax
+that identify the type of system you are using. It also enables the
+undesirable and rarely used ANSI trigraph feature, and disallows `\|\c
+.B $\c
+\&\|' as part of identifiers.
+.Sp
+The alternate keywords \c
+.B _\|_asm_\|_\c
+\&, \c
+.B _\|_extension_\|_\c
+\&,
+.B _\|_inline_\|_\c
+\& and \c
+.B _\|_typeof_\|_\c
+\& continue to work despite
+`\|\c
+.B \-ansi\c
+\&\|'. You would not want to use them in an ANSI C program, of
+course, but it is useful to put them in header files that might be included
+in compilations done with `\|\c
+.B \-ansi\c
+\&\|'. Alternate predefined macros
+such as \c
+.B _\|_unix_\|_\c
+\& and \c
+.B _\|_vax_\|_\c
+\& are also available, with or
+without `\|\c
+.B \-ansi\c
+\&\|'.
+.Sp
+The `\|\c
+.B \-ansi\c
+\&\|' option does not cause non-ANSI programs to be
+rejected gratuitously. For that, `\|\c
+.B \-pedantic\c
+\&\|' is required in
+addition to `\|\c
+.B \-ansi\c
+\&\|'.
+.Sp
+The preprocessor predefines a macro \c
+.B _\|_STRICT_ANSI_\|_\c
+\& when you use the `\|\c
+.B \-ansi\c
+\&\|'
+option. Some header files may notice this macro and refrain
+from declaring certain functions or defining certain macros that the
+ANSI standard doesn't call for; this is to avoid interfering with any
+programs that might use these names for other things.
+.TP
+.B \-fno\-asm
+Do not recognize \c
+.B asm\c
+\&, \c
+.B inline\c
+\& or \c
+.B typeof\c
+\& as a
+keyword. These words may then be used as identifiers. You can
+use \c
+.B _\|_asm_\|_\c
+\&, \c
+.B _\|_inline_\|_\c
+\& and \c
+.B _\|_typeof_\|_\c
+\& instead.
+`\|\c
+.B \-ansi\c
+\&\|' implies `\|\c
+.B \-fno\-asm\c
+\&\|'.
+.TP
+.B \-fno\-builtin
+Don't recognize built-in functions that do not begin with two leading
+underscores. Currently, the functions affected include \c
+.B _exit\c
+\&,
+.B abort\c
+\&, \c
+.B abs\c
+\&, \c
+.B alloca\c
+\&, \c
+.B cos\c
+\&, \c
+.B exit\c
+\&,
+.B fabs\c
+\&, \c
+.B labs\c
+\&, \c
+.B memcmp\c
+\&, \c
+.B memcpy\c
+\&, \c
+.B sin\c
+\&,
+.B sqrt\c
+\&, \c
+.B strcmp\c
+\&, \c
+.B strcpy\c
+\&, and \c
+.B strlen\c
+\&.
+.Sp
+The `\|\c
+.B \-ansi\c
+\&\|' option prevents \c
+.B alloca\c
+\& and \c
+.B _exit\c
+\& from
+being builtin functions.
+.TP
+.B \-fno\-strict\-prototype
+Treat a function declaration with no arguments, such as `\|\c
+.B int foo
+();\c
+\&\|', as C would treat it\(em\&as saying nothing about the number of
+arguments or their types (C++ only). Normally, such a declaration in
+C++ means that the function \c
+.B foo\c
+\& takes no arguments.
+.TP
+.B \-trigraphs
+Support ANSI C trigraphs. The `\|\c
+.B \-ansi\c
+\&\|' option implies `\|\c
+.B \-trigraphs\c
+\&\|'.
+.TP
+.B \-traditional
+Attempt to support some aspects of traditional C compilers.
+For details, see the GNU C Manual; the duplicate list here
+has been deleted so that we won't get complaints when it
+is out of date.
+.Sp
+But one note about C++ programs only (not C). `\|\c
+.B \-traditional\c
+\&\|' has one additional effect for C++: assignment to
+.B this
+is permitted. This is the same as the effect of `\|\c
+.B \-fthis\-is\-variable\c
+\&\|'.
+.TP
+.B \-traditional\-cpp
+Attempt to support some aspects of traditional C preprocessors.
+This includes the items that specifically mention the preprocessor above,
+but none of the other effects of `\|\c
+.B \-traditional\c
+\&\|'.
+.TP
+.B \-fdollars\-in\-identifiers
+Permit the use of `\|\c
+.B $\c
+\&\|' in identifiers (C++ only). You can also use
+`\|\c
+.B \-fno\-dollars\-in\-identifiers\c
+\&\|' to explicitly prohibit use of
+`\|\c
+.B $\c
+\&\|'. (GNU C++ allows `\|\c
+.B $\c
+\&\|' by default on some target systems
+but not others.)
+.TP
+.B \-fenum\-int\-equiv
+Permit implicit conversion of \c
+.B int\c
+\& to enumeration types (C++
+only). Normally GNU C++ allows conversion of \c
+.B enum\c
+\& to \c
+.B int\c
+\&,
+but not the other way around.
+.TP
+.B \-fexternal\-templates
+Produce smaller code for template declarations, by generating only a
+single copy of each template function where it is defined (C++ only).
+To use this option successfully, you must also mark all files that
+use templates with either `\|\c
+.B #pragma implementation\c
+\&\|' (the definition) or
+`\|\c
+.B #pragma interface\c
+\&\|' (declarations).
+
+When your code is compiled with `\|\c
+.B \-fexternal\-templates\c
+\&\|', all
+template instantiations are external. You must arrange for all
+necessary instantiations to appear in the implementation file; you can
+do this with a \c
+.B typedef\c
+\& that references each instantiation needed.
+Conversely, when you compile using the default option
+`\|\c
+.B \-fno\-external\-templates\c
+\&\|', all template instantiations are
+explicitly internal.
+.TP
+.B \-fall\-virtual
+Treat all possible member functions as virtual, implicitly. All
+member functions (except for constructor functions and
+.B new
+or
+.B delete
+member operators) are treated as virtual functions of the class where
+they appear.
+.Sp
+This does not mean that all calls to these member functions will be
+made through the internal table of virtual functions. Under some
+circumstances, the compiler can determine that a call to a given
+virtual function can be made directly; in these cases the calls are
+direct in any case.
+.TP
+.B \-fcond\-mismatch
+Allow conditional expressions with mismatched types in the second and
+third arguments. The value of such an expression is void.
+.TP
+.B \-fthis\-is\-variable
+Permit assignment to \c
+.B this\c
+\& (C++ only). The incorporation of
+user-defined free store management into C++ has made assignment to
+`\|\c
+.B this\c
+\&\|' an anachronism. Therefore, by default it is invalid to
+assign to \c
+.B this\c
+\& within a class member function. However, for
+backwards compatibility, you can make it valid with
+`\|\c
+.B \-fthis-is-variable\c
+\&\|'.
+.TP
+.B \-funsigned\-char
+Let the type \c
+.B char\c
+\& be unsigned, like \c
+.B unsigned char\c
+\&.
+.Sp
+Each kind of machine has a default for what \c
+.B char\c
+\& should
+be. It is either like \c
+.B unsigned char\c
+\& by default or like
+.B signed char\c
+\& by default.
+.Sp
+Ideally, a portable program should always use \c
+.B signed char\c
+\& or
+.B unsigned char\c
+\& when it depends on the signedness of an object.
+But many programs have been written to use plain \c
+.B char\c
+\& and
+expect it to be signed, or expect it to be unsigned, depending on the
+machines they were written for. This option, and its inverse, let you
+make such a program work with the opposite default.
+.Sp
+The type \c
+.B char\c
+\& is always a distinct type from each of
+.B signed char\c
+\& and \c
+.B unsigned char\c
+\&, even though its behavior
+is always just like one of those two.
+.TP
+.B \-fsigned\-char
+Let the type \c
+.B char\c
+\& be signed, like \c
+.B signed char\c
+\&.
+.Sp
+Note that this is equivalent to `\|\c
+.B \-fno\-unsigned\-char\c
+\&\|', which is
+the negative form of `\|\c
+.B \-funsigned\-char\c
+\&\|'. Likewise,
+`\|\c
+.B \-fno\-signed\-char\c
+\&\|' is equivalent to `\|\c
+.B \-funsigned\-char\c
+\&\|'.
+.TP
+.B \-fsigned\-bitfields
+.TP
+.B \-funsigned\-bitfields
+.TP
+.B \-fno\-signed\-bitfields
+.TP
+.B \-fno\-unsigned\-bitfields
+These options control whether a bitfield is
+signed or unsigned, when declared with no explicit `\|\c
+.B signed\c
+\&\|' or `\|\c
+.B unsigned\c
+\&\|' qualifier. By default, such a bitfield is
+signed, because this is consistent: the basic integer types such as
+.B int\c
+\& are signed types.
+.Sp
+However, when you specify `\|\c
+.B \-traditional\c
+\&\|', bitfields are all unsigned
+no matter what.
+.TP
+.B \-fwritable\-strings
+Store string constants in the writable data segment and don't uniquize
+them. This is for compatibility with old programs which assume they
+can write into string constants. `\|\c
+.B \-traditional\c
+\&\|' also has this
+effect.
+.Sp
+Writing into string constants is a very bad idea; \*(lqconstants\*(rq should
+be constant.
+.SH PREPROCESSOR OPTIONS
+These options control the C preprocessor, which is run on each C source
+file before actual compilation.
+.PP
+If you use the `\|\c
+.B \-E\c
+\&\|' option, GCC does nothing except preprocessing.
+Some of these options make sense only together with `\|\c
+.B \-E\c
+\&\|' because
+they cause the preprocessor output to be unsuitable for actual
+compilation.
+.TP
+.BI "\-include " "file"
+Process \c
+.I file\c
+\& as input before processing the regular input file.
+In effect, the contents of \c
+.I file\c
+\& are compiled first. Any `\|\c
+.B \-D\c
+\&\|'
+and `\|\c
+.B \-U\c
+\&\|' options on the command line are always processed before
+`\|\c
+.B \-include \c
+.I file\c
+\&\c
+\&\|', regardless of the order in which they are
+written. All the `\|\c
+.B \-include\c
+\&\|' and `\|\c
+.B \-imacros\c
+\&\|' options are
+processed in the order in which they are written.
+.TP
+.BI "\-imacros " file
+Process \c
+.I file\c
+\& as input, discarding the resulting output, before
+processing the regular input file. Because the output generated from
+.I file\c
+\& is discarded, the only effect of `\|\c
+.B \-imacros \c
+.I file\c
+\&\c
+\&\|' is to
+make the macros defined in \c
+.I file\c
+\& available for use in the main
+input. The preprocessor evaluates any `\|\c
+.B \-D\c
+\&\|' and `\|\c
+.B \-U\c
+\&\|' options
+on the command line before processing `\|\c
+.B \-imacros\c
+.I file\c
+\&\|', regardless of the order in
+which they are written. All the `\|\c
+.B \-include\c
+\&\|' and `\|\c
+.B \-imacros\c
+\&\|'
+options are processed in the order in which they are written.
+.TP
+.BI "\-idirafter " "dir"
+Add the directory \c
+.I dir\c
+\& to the second include path. The directories
+on the second include path are searched when a header file is not found
+in any of the directories in the main include path (the one that
+`\|\c
+.B \-I\c
+\&\|' adds to).
+.TP
+.BI "\-iprefix " "prefix"
+Specify \c
+.I prefix\c
+\& as the prefix for subsequent `\|\c
+.B \-iwithprefix\c
+\&\|'
+options.
+.TP
+.BI "\-iwithprefix " "dir"
+Add a directory to the second include path. The directory's name is
+made by concatenating \c
+.I prefix\c
+\& and \c
+.I dir\c
+\&, where \c
+.I prefix
+was specified previously with `\|\c
+.B \-iprefix\c
+\&\|'.
+.TP
+.B \-nostdinc
+Do not search the standard system directories for header files. Only
+the directories you have specified with `\|\c
+.B \-I\c
+\&\|' options (and the
+current directory, if appropriate) are searched.
+.Sp
+By using both `\|\c
+.B \-nostdinc\c
+\&\|' and `\|\c
+.B \-I\-\c
+\&\|', you can limit the include-file search file to only those
+directories you specify explicitly.
+.TP
+.B \-nostdinc++
+Do not search for header files in the C++\-specific standard directories,
+but do still search the other standard directories.
+(This option is used when building `\|\c
+.B libg++\c
+\&\|'.)
+.TP
+.B \-undef
+Do not predefine any nonstandard macros. (Including architecture flags).
+.TP
+.B \-E
+Run only the C preprocessor. Preprocess all the C source files
+specified and output the results to standard output or to the
+specified output file.
+.TP
+.B \-C
+Tell the preprocessor not to discard comments. Used with the
+`\|\c
+.B \-E\c
+\&\|' option.
+.TP
+.B \-P
+Tell the preprocessor not to generate `\|\c
+.B #line\c
+\&\|' commands.
+Used with the `\|\c
+.B \-E\c
+\&\|' option.
+.TP
+.B \-M\ [ \-MG ]
+Tell the preprocessor to output a rule suitable for \c
+.B make
+describing the dependencies of each object file. For each source file,
+the preprocessor outputs one \c
+.B make\c
+\&-rule whose target is the object
+file name for that source file and whose dependencies are all the files
+`\|\c
+.B #include\c
+\&\|'d in it. This rule may be a single line or may be
+continued with `\|\c
+.B \e\c
+\&\|'-newline if it is long. The list of rules is
+printed on standard output instead of the preprocessed C program.
+.Sp
+`\|\c
+.B \-M\c
+\&\|' implies `\|\c
+.B \-E\c
+\&\|'.
+.Sp
+`\|\c
+.B \-MG\c
+\&\|' says to treat missing header files as generated files and assume \c
+they live in the same directory as the source file. It must be specified \c
+in addition to `\|\c
+.B \-M\c
+\&\|'.
+.TP
+.B \-MM\ [ \-MG ]
+Like `\|\c
+.B \-M\c
+\&\|' but the output mentions only the user header files
+included with `\|\c
+.B #include "\c
+.I file\c
+\&"\c
+\&\|'. System header files
+included with `\|\c
+.B #include <\c
+.I file\c
+\&>\c
+\&\|' are omitted.
+.TP
+.B \-MD
+Like `\|\c
+.B \-M\c
+\&\|' but the dependency information is written to files with
+names made by replacing `\|\c
+.B .o\c
+\&\|' with `\|\c
+.B .d\c
+\&\|' at the end of the
+output file names. This is in addition to compiling the file as
+specified\(em\&`\|\c
+.B \-MD\c
+\&\|' does not inhibit ordinary compilation the way
+`\|\c
+.B \-M\c
+\&\|' does.
+.Sp
+The Mach utility `\|\c
+.B md\c
+\&\|' can be used to merge the `\|\c
+.B .d\c
+\&\|' files
+into a single dependency file suitable for using with the `\|\c
+.B make\c
+\&\|'
+command.
+.TP
+.B \-MMD
+Like `\|\c
+.B \-MD\c
+\&\|' except mention only user header files, not system
+header files.
+.TP
+.B \-H
+Print the name of each header file used, in addition to other normal
+activities.
+.TP
+.BI "\-A" "question" ( answer )
+Assert the answer
+.I answer
+for
+.I question\c
+\&, in case it is tested
+with a preprocessor conditional such as `\|\c
+.BI "#if #" question ( answer )\c
+\&\|'. `\|\c
+.B \-A\-\c
+\&\|' disables the standard
+assertions that normally describe the target machine.
+.TP
+.BI "\-A" "question"\c
+\&(\c
+.I answer\c
+\&)
+Assert the answer \c
+.I answer\c
+\& for \c
+.I question\c
+\&, in case it is tested
+with a preprocessor conditional such as `\|\c
+.B #if
+#\c
+.I question\c
+\&(\c
+.I answer\c
+\&)\c
+\&\|'. `\|\c
+.B \-A-\c
+\&\|' disables the standard
+assertions that normally describe the target machine.
+.TP
+.BI \-D macro
+Define macro \c
+.I macro\c
+\& with the string `\|\c
+.B 1\c
+\&\|' as its definition.
+.TP
+.BI \-D macro = defn
+Define macro \c
+.I macro\c
+\& as \c
+.I defn\c
+\&. All instances of `\|\c
+.B \-D\c
+\&\|' on
+the command line are processed before any `\|\c
+.B \-U\c
+\&\|' options.
+.TP
+.BI \-U macro
+Undefine macro \c
+.I macro\c
+\&. `\|\c
+.B \-U\c
+\&\|' options are evaluated after all `\|\c
+.B \-D\c
+\&\|' options, but before any `\|\c
+.B \-include\c
+\&\|' and `\|\c
+.B \-imacros\c
+\&\|' options.
+.TP
+.B \-dM
+Tell the preprocessor to output only a list of the macro definitions
+that are in effect at the end of preprocessing. Used with the `\|\c
+.B \-E\c
+\&\|'
+option.
+.TP
+.B \-dD
+Tell the preprocessor to pass all macro definitions into the output, in
+their proper sequence in the rest of the output.
+.TP
+.B \-dN
+Like `\|\c
+.B \-dD\c
+\&\|' except that the macro arguments and contents are omitted.
+Only `\|\c
+.B #define \c
+.I name\c
+\&\c
+\&\|' is included in the output.
+.SH ASSEMBLER OPTION
+.TP
+.BI "\-Wa," "option"
+Pass \c
+.I option\c
+\& as an option to the assembler. If \c
+.I option
+contains commas, it is split into multiple options at the commas.
+.SH LINKER OPTIONS
+These options come into play when the compiler links object files into
+an executable output file. They are meaningless if the compiler is
+not doing a link step.
+.TP
+.I object-file-name
+A file name that does not end in a special recognized suffix is
+considered to name an object file or library. (Object files are
+distinguished from libraries by the linker according to the file
+contents.) If GCC does a link step, these object files are used as input
+to the linker.
+.TP
+.BI \-l library
+Use the library named \c
+.I library\c
+\& when linking.
+.Sp
+The linker searches a standard list of directories for the library,
+which is actually a file named `\|\c
+.B lib\c
+.I library\c
+\&.a\c
+\&\|'. The linker
+then uses this file as if it had been specified precisely by name.
+.Sp
+The directories searched include several standard system directories
+plus any that you specify with `\|\c
+.B \-L\c
+\&\|'.
+.Sp
+Normally the files found this way are library files\(em\&archive files
+whose members are object files. The linker handles an archive file by
+scanning through it for members which define symbols that have so far
+been referenced but not defined. However, if the linker finds an
+ordinary object file rather than a library, the object file is linked
+in the usual fashion. The only difference between using an `\|\c
+.B \-l\c
+\&\|' option and specifying a file
+name is that `\|\c
+.B \-l\c
+\&\|' surrounds
+.I library
+with `\|\c
+.B lib\c
+\&\|' and `\|\c
+.B .a\c
+\&\|' and searches several directories.
+.TP
+.B \-lobjc
+You need this special case of the
+.B \-l
+option in order to link an Objective C program.
+.TP
+.B \-nostartfiles
+Do not use the standard system startup files when linking.
+The standard libraries are used normally.
+.TP
+.B \-nostdlib
+Don't use the standard system libraries and startup files when linking.
+Only the files you specify will be passed to the linker.
+.TP
+.B \-static
+On systems that support dynamic linking, this prevents linking with the shared
+libraries. On other systems, this option has no effect.
+.TP
+.B \-shared
+Produce a shared object which can then be linked with other objects to
+form an executable. Only a few systems support this option.
+.TP
+.B \-symbolic
+Bind references to global symbols when building a shared object. Warn
+about any unresolved references (unless overridden by the link editor
+option `\|\c
+.B
+\-Xlinker \-z \-Xlinker defs\c
+\&\|'). Only a few systems support
+this option.
+.TP
+.BI "\-Xlinker " "option"
+Pass \c
+.I option
+as an option to the linker. You can use this to
+supply system-specific linker options which GNU CC does not know how to
+recognize.
+.Sp
+If you want to pass an option that takes an argument, you must use
+`\|\c
+.B \-Xlinker\c
+\&\|' twice, once for the option and once for the argument.
+For example, to pass `\|\c
+.B
+\-assert definitions\c
+\&\|', you must write
+`\|\c
+.B
+\-Xlinker \-assert \-Xlinker definitions\c
+\&\|'. It does not work to write
+`\|\c
+.B
+\-Xlinker "\-assert definitions"\c
+\&\|', because this passes the entire
+string as a single argument, which is not what the linker expects.
+.TP
+.BI "\-Wl," "option"
+Pass \c
+.I option\c
+\& as an option to the linker. If \c
+.I option\c
+\& contains
+commas, it is split into multiple options at the commas.
+.TP
+.BI "\-u " "symbol"
+Pretend the symbol
+.I symbol
+is undefined, to force linking of
+library modules to define it. You can use `\|\c
+.B \-u\c
+\&\|' multiple times with
+different symbols to force loading of additional library modules.
+.SH DIRECTORY OPTIONS
+These options specify directories to search for header files, for
+libraries and for parts of the compiler:
+.TP
+.BI "\-I" "dir"
+Append directory \c
+.I dir\c
+\& to the list of directories searched for include files.
+.TP
+.B \-I\-
+Any directories you specify with `\|\c
+.B \-I\c
+\&\|' options before the `\|\c
+.B \-I\-\c
+\&\|'
+option are searched only for the case of `\|\c
+.B
+#include "\c
+.I file\c
+.B
+\&"\c
+\&\|';
+they are not searched for `\|\c
+.B #include <\c
+.I file\c
+\&>\c
+\&\|'.
+.Sp
+If additional directories are specified with `\|\c
+.B \-I\c
+\&\|' options after
+the `\|\c
+.B \-I\-\c
+\&\|', these directories are searched for all `\|\c
+.B #include\c
+\&\|'
+directives. (Ordinarily \c
+.I all\c
+\& `\|\c
+.B \-I\c
+\&\|' directories are used
+this way.)
+.Sp
+In addition, the `\|\c
+.B \-I\-\c
+\&\|' option inhibits the use of the current
+directory (where the current input file came from) as the first search
+directory for `\|\c
+.B
+#include "\c
+.I file\c
+.B
+\&"\c
+\&\|'. There is no way to
+override this effect of `\|\c
+.B \-I\-\c
+\&\|'. With `\|\c
+.B \-I.\c
+\&\|' you can specify
+searching the directory which was current when the compiler was
+invoked. That is not exactly the same as what the preprocessor does
+by default, but it is often satisfactory.
+.Sp
+`\|\c
+.B \-I\-\c
+\&\|' does not inhibit the use of the standard system directories
+for header files. Thus, `\|\c
+.B \-I\-\c
+\&\|' and `\|\c
+.B \-nostdinc\c
+\&\|' are
+independent.
+.TP
+.BI "\-L" "dir"
+Add directory \c
+.I dir\c
+\& to the list of directories to be searched
+for `\|\c
+.B \-l\c
+\&\|'.
+.TP
+.BI "\-B" "prefix"
+This option specifies where to find the executables, libraries and
+data files of the compiler itself.
+.Sp
+The compiler driver program runs one or more of the subprograms
+`\|\c
+.B cpp\c
+\&\|', `\|\c
+.B cc1\c
+\&\|' (or, for C++, `\|\c
+.B cc1plus\c
+\&\|'), `\|\c
+.B as\c
+\&\|' and `\|\c
+.B ld\c
+\&\|'. It tries
+.I prefix\c
+\& as a prefix for each program it tries to run, both with and
+without `\|\c
+.I machine\c
+.B /\c
+.I version\c
+.B /\c
+\&\|'.
+.Sp
+For each subprogram to be run, the compiler driver first tries the
+`\|\c
+.B \-B\c
+\&\|' prefix, if any. If that name is not found, or if `\|\c
+.B \-B\c
+\&\|'
+was not specified, the driver tries two standard prefixes, which are
+`\|\c
+.B /usr/lib/gcc/\c
+\&\|' and `\|\c
+.B /usr/local/lib/gcc-lib/\c
+\&\|'. If neither of
+those results in a file name that is found, the compiler driver
+searches for the unmodified program
+name, using the directories specified in your
+`\|\c
+.B PATH\c
+\&\|' environment variable.
+.Sp
+The run-time support file `\|\c
+.B libgcc.a\c
+\&\|' is also searched for using the
+`\|\c
+.B \-B\c
+\&\|' prefix, if needed. If it is not found there, the two
+standard prefixes above are tried, and that is all. The file is left
+out of the link if it is not found by those means. Most of the time,
+on most machines, `\|\c
+.B libgcc.a\c
+\&\|' is not actually necessary.
+.Sp
+You can get a similar result from the environment variable
+.B GCC_EXEC_PREFIX\c
+\&; if it is defined, its value is used as a prefix
+in the same way. If both the `\|\c
+.B \-B\c
+\&\|' option and the
+.B GCC_EXEC_PREFIX\c
+\& variable are present, the `\|\c
+.B \-B\c
+\&\|' option is
+used first and the environment variable value second.
+.SH WARNING OPTIONS
+Warnings are diagnostic messages that report constructions which
+are not inherently erroneous but which are risky or suggest there
+may have been an error.
+.Sp
+These options control the amount and kinds of warnings produced by GNU
+CC:
+.TP
+.B \-fsyntax\-only
+Check the code for syntax errors, but don't emit any output.
+.TP
+.B \-w
+Inhibit all warning messages.
+.TP
+.B \-Wno\-import
+Inhibit warning messages about the use of
+.BR #import .
+.TP
+.B \-pedantic
+Issue all the warnings demanded by strict ANSI standard C; reject
+all programs that use forbidden extensions.
+.Sp
+Valid ANSI standard C programs should compile properly with or without
+this option (though a rare few will require `\|\c
+.B \-ansi\c
+\&\|'). However,
+without this option, certain GNU extensions and traditional C features
+are supported as well. With this option, they are rejected. There is
+no reason to \c
+.I use\c
+\& this option; it exists only to satisfy pedants.
+.Sp
+`\|\c
+.B \-pedantic\c
+\&\|' does not cause warning messages for use of the
+alternate keywords whose names begin and end with `\|\c
+.B _\|_\c
+\&\|'. Pedantic
+warnings are also disabled in the expression that follows
+.B _\|_extension_\|_\c
+\&. However, only system header files should use
+these escape routes; application programs should avoid them.
+.TP
+.B \-pedantic\-errors
+Like `\|\c
+.B \-pedantic\c
+\&\|', except that errors are produced rather than
+warnings.
+.TP
+.B \-W
+Print extra warning messages for these events:
+.TP
+\ \ \ \(bu
+A nonvolatile automatic variable might be changed by a call to
+.B longjmp\c
+\&. These warnings are possible only in
+optimizing compilation.
+.Sp
+The compiler sees only the calls to \c
+.B setjmp\c
+\&. It cannot know
+where \c
+.B longjmp\c
+\& will be called; in fact, a signal handler could
+call it at any point in the code. As a result, you may get a warning
+even when there is in fact no problem because \c
+.B longjmp\c
+\& cannot
+in fact be called at the place which would cause a problem.
+.TP
+\ \ \ \(bu
+A function can return either with or without a value. (Falling
+off the end of the function body is considered returning without
+a value.) For example, this function would evoke such a
+warning:
+.Sp
+.nf
+foo (a)
+{
+ if (a > 0)
+ return a;
+}
+.Sp
+.fi
+Spurious warnings can occur because GNU CC does not realize that
+certain functions (including \c
+.B abort\c
+\& and \c
+.B longjmp\c
+\&)
+will never return.
+.TP
+\ \ \ \(bu
+An expression-statement contains no side effects.
+.TP
+\ \ \ \(bu
+An unsigned value is compared against zero with `\|\c
+.B >\c
+\&\|' or `\|\c
+.B <=\c
+\&\|'.
+.PP
+.TP
+.B \-Wimplicit
+Warn whenever a function or parameter is implicitly declared.
+.TP
+.B \-Wreturn\-type
+Warn whenever a function is defined with a return-type that defaults
+to \c
+.B int\c
+\&. Also warn about any \c
+.B return\c
+\& statement with no
+return-value in a function whose return-type is not \c
+.B void\c
+\&.
+.TP
+.B \-Wunused
+Warn whenever a local variable is unused aside from its declaration,
+whenever a function is declared static but never defined, and whenever
+a statement computes a result that is explicitly not used.
+.TP
+.B \-Wswitch
+Warn whenever a \c
+.B switch\c
+\& statement has an index of enumeral type
+and lacks a \c
+.B case\c
+\& for one or more of the named codes of that
+enumeration. (The presence of a \c
+.B default\c
+\& label prevents this
+warning.) \c
+.B case\c
+\& labels outside the enumeration range also
+provoke warnings when this option is used.
+.TP
+.B \-Wcomment
+Warn whenever a comment-start sequence `\|\c
+.B /\(**\c
+\&\|' appears in a comment.
+.TP
+.B \-Wtrigraphs
+Warn if any trigraphs are encountered (assuming they are enabled).
+.TP
+.B \-Wformat
+Check calls to \c
+.B printf\c
+\& and \c
+.B scanf\c
+\&, etc., to make sure that
+the arguments supplied have types appropriate to the format string
+specified.
+.TP
+.B \-Wchar\-subscripts
+Warn if an array subscript has type
+.BR char .
+This is a common cause of error, as programmers often forget that this
+type is signed on some machines.
+.TP
+.B \-Wuninitialized
+An automatic variable is used without first being initialized.
+.Sp
+These warnings are possible only in optimizing compilation,
+because they require data flow information that is computed only
+when optimizing. If you don't specify `\|\c
+.B \-O\c
+\&\|', you simply won't
+get these warnings.
+.Sp
+These warnings occur only for variables that are candidates for
+register allocation. Therefore, they do not occur for a variable that
+is declared \c
+.B volatile\c
+\&, or whose address is taken, or whose size
+is other than 1, 2, 4 or 8 bytes. Also, they do not occur for
+structures, unions or arrays, even when they are in registers.
+.Sp
+Note that there may be no warning about a variable that is used only
+to compute a value that itself is never used, because such
+computations may be deleted by data flow analysis before the warnings
+are printed.
+.Sp
+These warnings are made optional because GNU CC is not smart
+enough to see all the reasons why the code might be correct
+despite appearing to have an error. Here is one example of how
+this can happen:
+.Sp
+.nf
+{
+ int x;
+ switch (y)
+ {
+ case 1: x = 1;
+ break;
+ case 2: x = 4;
+ break;
+ case 3: x = 5;
+ }
+ foo (x);
+}
+.Sp
+.fi
+If the value of \c
+.B y\c
+\& is always 1, 2 or 3, then \c
+.B x\c
+\& is
+always initialized, but GNU CC doesn't know this. Here is
+another common case:
+.Sp
+.nf
+{
+ int save_y;
+ if (change_y) save_y = y, y = new_y;
+ .\|.\|.
+ if (change_y) y = save_y;
+}
+.Sp
+.fi
+This has no bug because \c
+.B save_y\c
+\& is used only if it is set.
+.Sp
+Some spurious warnings can be avoided if you declare as
+.B volatile\c
+\& all the functions you use that never return.
+.TP
+.B \-Wparentheses
+Warn if parentheses are omitted in certain contexts.
+.TP
+.B \-Wtemplate\-debugging
+When using templates in a C++ program, warn if debugging is not yet
+fully available (C++ only).
+.TP
+.B \-Wall
+All of the above `\|\c
+.B \-W\c
+\&\|' options combined. These are all the
+options which pertain to usage that we recommend avoiding and that we
+believe is easy to avoid, even in conjunction with macros.
+.PP
+The remaining `\|\c
+.B \-W.\|.\|.\c
+\&\|' options are not implied by `\|\c
+.B \-Wall\c
+\&\|'
+because they warn about constructions that we consider reasonable to
+use, on occasion, in clean programs.
+.TP
+.B \-Wtraditional
+Warn about certain constructs that behave differently in traditional and
+ANSI C.
+.TP
+\ \ \ \(bu
+Macro arguments occurring within string constants in the macro body.
+These would substitute the argument in traditional C, but are part of
+the constant in ANSI C.
+.TP
+\ \ \ \(bu
+A function declared external in one block and then used after the end of
+the block.
+.TP
+\ \ \ \(bu
+A \c
+.B switch\c
+\& statement has an operand of type \c
+.B long\c
+\&.
+.PP
+.TP
+.B \-Wshadow
+Warn whenever a local variable shadows another local variable.
+.TP
+.BI "\-Wid\-clash\-" "len"
+Warn whenever two distinct identifiers match in the first \c
+.I len
+characters. This may help you prepare a program that will compile
+with certain obsolete, brain-damaged compilers.
+.TP
+.B \-Wpointer\-arith
+Warn about anything that depends on the \*(lqsize of\*(rq a function type or
+of \c
+.B void\c
+\&. GNU C assigns these types a size of 1, for
+convenience in calculations with \c
+.B void \(**\c
+\& pointers and pointers
+to functions.
+.TP
+.B \-Wcast\-qual
+Warn whenever a pointer is cast so as to remove a type qualifier from
+the target type. For example, warn if a \c
+.B const char \(**\c
+\& is cast
+to an ordinary \c
+.B char \(**\c
+\&.
+.TP
+.B \-Wcast\-align
+Warn whenever a pointer is cast such that the required alignment of the
+target is increased. For example, warn if a \c
+.B char \(**\c
+\& is cast to
+an \c
+.B int \(**\c
+\& on machines where integers can only be accessed at
+two- or four-byte boundaries.
+.TP
+.B \-Wwrite\-strings
+Give string constants the type \c
+.B const char[\c
+.I length\c
+.B ]\c
+\& so that
+copying the address of one into a non-\c
+.B const\c
+\& \c
+.B char \(**
+pointer will get a warning. These warnings will help you find at
+compile time code that can try to write into a string constant, but
+only if you have been very careful about using \c
+.B const\c
+\& in
+declarations and prototypes. Otherwise, it will just be a nuisance;
+this is why we did not make `\|\c
+.B \-Wall\c
+\&\|' request these warnings.
+.TP
+.B \-Wconversion
+Warn if a prototype causes a type conversion that is different from what
+would happen to the same argument in the absence of a prototype. This
+includes conversions of fixed point to floating and vice versa, and
+conversions changing the width or signedness of a fixed point argument
+except when the same as the default promotion.
+.TP
+.B \-Waggregate\-return
+Warn if any functions that return structures or unions are defined or
+called. (In languages where you can return an array, this also elicits
+a warning.)
+.TP
+.B \-Wstrict\-prototypes
+Warn if a function is declared or defined without specifying the
+argument types. (An old-style function definition is permitted without
+a warning if preceded by a declaration which specifies the argument
+types.)
+.TP
+.B \-Wmissing\-prototypes
+Warn if a global function is defined without a previous prototype
+declaration. This warning is issued even if the definition itself
+provides a prototype. The aim is to detect global functions that fail
+to be declared in header files.
+.TP
+.B \-Wmissing\-declarations
+Warn if a global function is defined without a previous declaration.
+Do so even if the definition itself provides a prototype.
+Use this option to detect global functions that are not declared in
+header files.
+.TP
+.B \-Wredundant-decls
+Warn if anything is declared more than once in the same scope, even in
+cases where multiple declaration is valid and changes nothing.
+.TP
+.B \-Wnested-externs
+Warn if an \c
+.B extern\c
+\& declaration is encountered within an function.
+.TP
+.B \-Wenum\-clash
+Warn about conversion between different enumeration types (C++ only).
+.TP
+.B \-Woverloaded\-virtual
+(C++ only.)
+In a derived class, the definitions of virtual functions must match
+the type signature of a virtual function declared in the base class.
+Use this option to request warnings when a derived class declares a
+function that may be an erroneous attempt to define a virtual
+function: that is, warn when a function with the same name as a
+virtual function in the base class, but with a type signature that
+doesn't match any virtual functions from the base class.
+.TP
+.B \-Winline
+Warn if a function can not be inlined, and either it was declared as inline,
+or else the
+.B \-finline\-functions
+option was given.
+.TP
+.B \-Werror
+Treat warnings as errors; abort compilation after any warning.
+.SH DEBUGGING OPTIONS
+GNU CC has various special options that are used for debugging
+either your program or GCC:
+.TP
+.B \-g
+Produce debugging information in the operating system's native format
+(stabs, COFF, XCOFF, or DWARF). GDB can work with this debugging
+information.
+.Sp
+On most systems that use stabs format, `\|\c
+.B \-g\c
+\&\|' enables use of extra
+debugging information that only GDB can use; this extra information
+makes debugging work better in GDB but will probably make other debuggers
+crash or
+refuse to read the program. If you want to control for certain whether
+to generate the extra information, use `\|\c
+.B \-gstabs+\c
+\&\|', `\|\c
+.B \-gstabs\c
+\&\|',
+`\|\c
+.B \-gxcoff+\c
+\&\|', `\|\c
+.B \-gxcoff\c
+\&\|', `\|\c
+.B \-gdwarf+\c
+\&\|', or `\|\c
+.B \-gdwarf\c
+\&\|'
+(see below).
+.Sp
+Unlike most other C compilers, GNU CC allows you to use `\|\c
+.B \-g\c
+\&\|' with
+`\|\c
+.B \-O\c
+\&\|'. The shortcuts taken by optimized code may occasionally
+produce surprising results: some variables you declared may not exist
+at all; flow of control may briefly move where you did not expect it;
+some statements may not be executed because they compute constant
+results or their values were already at hand; some statements may
+execute in different places because they were moved out of loops.
+.Sp
+Nevertheless it proves possible to debug optimized output. This makes
+it reasonable to use the optimizer for programs that might have bugs.
+.PP
+The following options are useful when GNU CC is generated with the
+capability for more than one debugging format.
+.TP
+.B \-ggdb
+Produce debugging information in the native format (if that is supported),
+including GDB extensions if at all possible.
+.TP
+.B \-gstabs
+Produce debugging information in stabs format (if that is supported),
+without GDB extensions. This is the format used by DBX on most BSD
+systems.
+.TP
+.B \-gstabs+
+Produce debugging information in stabs format (if that is supported),
+using GNU extensions understood only by the GNU debugger (GDB). The
+use of these extensions is likely to make other debuggers crash or
+refuse to read the program.
+.TP
+.B \-gcoff
+Produce debugging information in COFF format (if that is supported).
+This is the format used by SDB on most System V systems prior to
+System V Release 4.
+.TP
+.B \-gxcoff
+Produce debugging information in XCOFF format (if that is supported).
+This is the format used by the DBX debugger on IBM RS/6000 systems.
+.TP
+.B \-gxcoff+
+Produce debugging information in XCOFF format (if that is supported),
+using GNU extensions understood only by the GNU debugger (GDB). The
+use of these extensions is likely to make other debuggers crash or
+refuse to read the program.
+.TP
+.B \-gdwarf
+Produce debugging information in DWARF format (if that is supported).
+This is the format used by SDB on most System V Release 4 systems.
+.TP
+.B \-gdwarf+
+Produce debugging information in DWARF format (if that is supported),
+using GNU extensions understood only by the GNU debugger (GDB). The
+use of these extensions is likely to make other debuggers crash or
+refuse to read the program.
+.PP
+.BI "\-g" "level"
+.br
+.BI "\-ggdb" "level"
+.br
+.BI "\-gstabs" "level"
+.br
+.BI "\-gcoff" "level"
+.BI "\-gxcoff" "level"
+.TP
+.BI "\-gdwarf" "level"
+Request debugging information and also use \c
+.I level\c
+\& to specify how
+much information. The default level is 2.
+.Sp
+Level 1 produces minimal information, enough for making backtraces in
+parts of the program that you don't plan to debug. This includes
+descriptions of functions and external variables, but no information
+about local variables and no line numbers.
+.Sp
+Level 3 includes extra information, such as all the macro definitions
+present in the program. Some debuggers support macro expansion when
+you use `\|\c
+.B \-g3\c
+\&\|'.
+.TP
+.B \-p
+Generate extra code to write profile information suitable for the
+analysis program \c
+.B prof\c
+\&.
+.TP
+.B \-pg
+Generate extra code to write profile information suitable for the
+analysis program \c
+.B gprof\c
+\&.
+.TP
+.B \-a
+Generate extra code to write profile information for basic blocks,
+which will record the number of times each basic block is executed.
+This data could be analyzed by a program like \c
+.B tcov\c
+\&. Note,
+however, that the format of the data is not what \c
+.B tcov\c
+\& expects.
+Eventually GNU \c
+.B gprof\c
+\& should be extended to process this data.
+.TP
+.BI "\-d" "letters"
+Says to make debugging dumps during compilation at times specified by
+.I letters\c
+\&. This is used for debugging the compiler. The file names
+for most of the dumps are made by appending a word to the source file
+name (e.g. `\|\c
+.B foo.c.rtl\c
+\&\|' or `\|\c
+.B foo.c.jump\c
+\&\|').
+.TP
+.B \-dM
+Dump all macro definitions, at the end of preprocessing, and write no
+output.
+.TP
+.B \-dN
+Dump all macro names, at the end of preprocessing.
+.TP
+.B \-dD
+Dump all macro definitions, at the end of preprocessing, in addition to
+normal output.
+.TP
+.B \-dy
+Dump debugging information during parsing, to standard error.
+.TP
+.B \-dr
+Dump after RTL generation, to `\|\c
+.I file\c
+.B \&.rtl\c
+\&\|'.
+.TP
+.B \-dx
+Just generate RTL for a function instead of compiling it. Usually used
+with `\|\c
+.B r\c
+\&\|'.
+.TP
+.B \-dj
+Dump after first jump optimization, to `\|\c
+.I file\c
+.B \&.jump\c
+\&\|'.
+.TP
+.B \-ds
+Dump after CSE (including the jump optimization that sometimes
+follows CSE), to `\|\c
+.I file\c
+.B \&.cse\c
+\&\|'.
+.TP
+.B \-dL
+Dump after loop optimization, to `\|\c
+.I file\c
+.B \&.loop\c
+\&\|'.
+.TP
+.B \-dt
+Dump after the second CSE pass (including the jump optimization that
+sometimes follows CSE), to `\|\c
+.I file\c
+.B \&.cse2\c
+\&\|'.
+.TP
+.B \-df
+Dump after flow analysis, to `\|\c
+.I file\c
+.B \&.flow\c
+\&\|'.
+.TP
+.B \-dc
+Dump after instruction combination, to `\|\c
+.I file\c
+.B \&.combine\c
+\&\|'.
+.TP
+.B \-dS
+Dump after the first instruction scheduling pass, to
+`\|\c
+.I file\c
+.B \&.sched\c
+\&\|'.
+.TP
+.B \-dl
+Dump after local register allocation, to `\|\c
+.I file\c
+.B \&.lreg\c
+\&\|'.
+.TP
+.B \-dg
+Dump after global register allocation, to `\|\c
+.I file\c
+.B \&.greg\c
+\&\|'.
+.TP
+.B \-dR
+Dump after the second instruction scheduling pass, to
+`\|\c
+.I file\c
+.B \&.sched2\c
+\&\|'.
+.TP
+.B \-dJ
+Dump after last jump optimization, to `\|\c
+.I file\c
+.B \&.jump2\c
+\&\|'.
+.TP
+.B \-dd
+Dump after delayed branch scheduling, to `\|\c
+.I file\c
+.B \&.dbr\c
+\&\|'.
+.TP
+.B \-dk
+Dump after conversion from registers to stack, to `\|\c
+.I file\c
+.B \&.stack\c
+\&\|'.
+.TP
+.B \-da
+Produce all the dumps listed above.
+.TP
+.B \-dm
+Print statistics on memory usage, at the end of the run, to
+standard error.
+.TP
+.B \-dp
+Annotate the assembler output with a comment indicating which
+pattern and alternative was used.
+.TP
+.B \-fpretend\-float
+When running a cross-compiler, pretend that the target machine uses the
+same floating point format as the host machine. This causes incorrect
+output of the actual floating constants, but the actual instruction
+sequence will probably be the same as GNU CC would make when running on
+the target machine.
+.TP
+.B \-save\-temps
+Store the usual \*(lqtemporary\*(rq intermediate files permanently; place them
+in the current directory and name them based on the source file. Thus,
+compiling `\|\c
+.B foo.c\c
+\&\|' with `\|\c
+.B \-c \-save\-temps\c
+\&\|' would produce files
+`\|\c
+.B foo.cpp\c
+\&\|' and `\|\c
+.B foo.s\c
+\&\|', as well as `\|\c
+.B foo.o\c
+\&\|'.
+.TP
+.BI "\-print\-file\-name=" "library"
+Print the full absolute name of the library file \|\c
+.nh
+.I library
+.hy
+\&\| that
+would be used when linking\(em\&and do not do anything else. With this
+option, GNU CC does not compile or link anything; it just prints the
+file name.
+.TP
+.B \-print\-libgcc\-file\-name
+Same as `\|\c
+.B \-print\-file\-name=libgcc.a\c
+\&\|'.
+.TP
+.BI "\-print\-prog\-name=" "program"
+Like `\|\c
+.B \-print\-file\-name\c
+\&\|', but searches for a program such as `\|\c
+cpp\c
+\&\|'.
+.SH OPTIMIZATION OPTIONS
+These options control various sorts of optimizations:
+.TP
+.B \-O
+.TP
+.B \-O1
+Optimize. Optimizing compilation takes somewhat more time, and a lot
+more memory for a large function.
+.Sp
+Without `\|\c
+.B \-O\c
+\&\|', the compiler's goal is to reduce the cost of
+compilation and to make debugging produce the expected results.
+Statements are independent: if you stop the program with a breakpoint
+between statements, you can then assign a new value to any variable or
+change the program counter to any other statement in the function and
+get exactly the results you would expect from the source code.
+.Sp
+Without `\|\c
+.B \-O\c
+\&\|', only variables declared \c
+.B register\c
+\& are
+allocated in registers. The resulting compiled code is a little worse
+than produced by PCC without `\|\c
+.B \-O\c
+\&\|'.
+.Sp
+With `\|\c
+.B \-O\c
+\&\|', the compiler tries to reduce code size and execution
+time.
+.Sp
+When you specify `\|\c
+.B \-O\c
+\&\|', the two options `\|\c
+.B \-fthread\-jumps\c
+\&\|' and `\|\c
+.B \-fdefer\-pop\c
+\&\|' are turned on. On machines that have delay slots, the `\|\c
+.B \-fdelayed\-branch\c
+\&\|' option is turned on. For those machines that can support debugging even
+without a frame pointer, the `\|\c
+.B \-fomit\-frame\-pointer\c
+\&\|' option is turned on. On some machines other flags may also be turned on.
+.TP
+.B \-O2
+Optimize even more. Nearly all supported optimizations that do not
+involve a space-speed tradeoff are performed. Loop unrolling and function
+inlining are not done, for example. As compared to
+.B \-O\c
+\&,
+this option increases both compilation time and the performance of the
+generated code.
+.TP
+.B \-O3
+Optimize yet more. This turns on everything
+.B \-O2
+does, along with also turning on
+.B \-finline\-functions.
+.TP
+.B \-O0
+Do not optimize.
+.Sp
+If you use multiple
+.B \-O
+options, with or without level numbers, the last such option is the
+one that is effective.
+.PP
+Options of the form `\|\c
+.B \-f\c
+.I flag\c
+\&\c
+\&\|' specify machine-independent
+flags. Most flags have both positive and negative forms; the negative
+form of `\|\c
+.B \-ffoo\c
+\&\|' would be `\|\c
+.B \-fno\-foo\c
+\&\|'. The following list shows
+only one form\(em\&the one which is not the default.
+You can figure out the other form by either removing `\|\c
+.B no\-\c
+\&\|' or
+adding it.
+.TP
+.B \-ffloat\-store
+Do not store floating point variables in registers. This
+prevents undesirable excess precision on machines such as the
+68000 where the floating registers (of the 68881) keep more
+precision than a \c
+.B double\c
+\& is supposed to have.
+.Sp
+For most programs, the excess precision does only good, but a few
+programs rely on the precise definition of IEEE floating point.
+Use `\|\c
+.B \-ffloat\-store\c
+\&\|' for such programs.
+.TP
+.B \-fmemoize\-lookups
+.TP
+.B \-fsave\-memoized
+Use heuristics to compile faster (C++ only). These heuristics are not
+enabled by default, since they are only effective for certain input
+files. Other input files compile more slowly.
+.Sp
+The first time the compiler must build a call to a member function (or
+reference to a data member), it must (1) determine whether the class
+implements member functions of that name; (2) resolve which member
+function to call (which involves figuring out what sorts of type
+conversions need to be made); and (3) check the visibility of the member
+function to the caller. All of this adds up to slower compilation.
+Normally, the second time a call is made to that member function (or
+reference to that data member), it must go through the same lengthy
+process again. This means that code like this
+.Sp
+\& cout << "This " << p << " has " << n << " legs.\en";
+.Sp
+makes six passes through all three steps. By using a software cache,
+a \*(lqhit\*(rq significantly reduces this cost. Unfortunately, using the
+cache introduces another layer of mechanisms which must be implemented,
+and so incurs its own overhead. `\|\c
+.B \-fmemoize\-lookups\c
+\&\|' enables
+the software cache.
+.Sp
+Because access privileges (visibility) to members and member functions
+may differ from one function context to the next,
+.B g++
+may need to flush the cache. With the `\|\c
+.B \-fmemoize\-lookups\c
+\&\|' flag, the cache is flushed after every
+function that is compiled. The `\|\c
+\-fsave\-memoized\c
+\&\|' flag enables the same software cache, but when the compiler
+determines that the context of the last function compiled would yield
+the same access privileges of the next function to compile, it
+preserves the cache.
+This is most helpful when defining many member functions for the same
+class: with the exception of member functions which are friends of
+other classes, each member function has exactly the same access
+privileges as every other, and the cache need not be flushed.
+.TP
+.B \-fno\-default\-inline
+Don't make member functions inline by default merely because they are
+defined inside the class scope (C++ only).
+.TP
+.B \-fno\-defer\-pop
+Always pop the arguments to each function call as soon as that
+function returns. For machines which must pop arguments after a
+function call, the compiler normally lets arguments accumulate on the
+stack for several function calls and pops them all at once.
+.TP
+.B \-fforce\-mem
+Force memory operands to be copied into registers before doing
+arithmetic on them. This may produce better code by making all
+memory references potential common subexpressions. When they are
+not common subexpressions, instruction combination should
+eliminate the separate register-load. I am interested in hearing
+about the difference this makes.
+.TP
+.B \-fforce\-addr
+Force memory address constants to be copied into registers before
+doing arithmetic on them. This may produce better code just as
+`\|\c
+.B \-fforce\-mem\c
+\&\|' may. I am interested in hearing about the
+difference this makes.
+.TP
+.B \-fomit\-frame\-pointer
+Don't keep the frame pointer in a register for functions that
+don't need one. This avoids the instructions to save, set up and
+restore frame pointers; it also makes an extra register available
+in many functions. \c
+.I It also makes debugging impossible on
+most machines.
+.Sp
+On some machines, such as the Vax, this flag has no effect, because
+the standard calling sequence automatically handles the frame pointer
+and nothing is saved by pretending it doesn't exist. The
+machine-description macro \c
+.B FRAME_POINTER_REQUIRED\c
+\& controls
+whether a target machine supports this flag.
+.TP
+.B \-finline\-functions
+Integrate all simple functions into their callers. The compiler
+heuristically decides which functions are simple enough to be worth
+integrating in this way.
+.Sp
+If all calls to a given function are integrated, and the function is
+declared \c
+.B static\c
+\&, then GCC normally does not output the function as
+assembler code in its own right.
+.TP
+.B \-fcaller\-saves
+Enable values to be allocated in registers that will be clobbered by
+function calls, by emitting extra instructions to save and restore the
+registers around such calls. Such allocation is done only when it
+seems to result in better code than would otherwise be produced.
+.Sp
+This option is enabled by default on certain machines, usually those
+which have no call-preserved registers to use instead.
+.TP
+.B \-fkeep\-inline\-functions
+Even if all calls to a given function are integrated, and the function
+is declared \c
+.B static\c
+\&, nevertheless output a separate run-time
+callable version of the function.
+.TP
+.B \-fno\-function\-cse
+Do not put function addresses in registers; make each instruction that
+calls a constant function contain the function's address explicitly.
+.Sp
+This option results in less efficient code, but some strange hacks
+that alter the assembler output may be confused by the optimizations
+performed when this option is not used.
+.TP
+.B \-fno\-peephole
+Disable any machine-specific peephole optimizations.
+.TP
+.B \-ffast-math
+This option allows GCC to violate some ANSI or IEEE rules/specifications
+in the interest of optimizing code for speed. For example, it allows
+the compiler to assume arguments to the \c
+.B sqrt\c
+\& function are
+non-negative numbers.
+.Sp
+This option should never be turned on by any `\|\c
+.B \-O\c
+\&\|' option since
+it can result in incorrect output for programs which depend on
+an exact implementation of IEEE or ANSI rules/specifications for
+math functions.
+.PP
+The following options control specific optimizations. The `\|\c
+.B \-O2\c
+\&\|'
+option turns on all of these optimizations except `\|\c
+.B \-funroll\-loops\c
+\&\|'
+and `\|\c
+.B \-funroll\-all\-loops\c
+\&\|'.
+.PP
+The `\|\c
+.B \-O\c
+\&\|' option usually turns on
+the `\|\c
+.B \-fthread\-jumps\c
+\&\|' and `\|\c
+.B \-fdelayed\-branch\c
+\&\|' options, but
+specific machines may change the default optimizations.
+.PP
+You can use the following flags in the rare cases when \*(lqfine-tuning\*(rq
+of optimizations to be performed is desired.
+.TP
+.B \-fstrength\-reduce
+Perform the optimizations of loop strength reduction and
+elimination of iteration variables.
+.TP
+.B \-fthread\-jumps
+Perform optimizations where we check to see if a jump branches to a
+location where another comparison subsumed by the first is found. If
+so, the first branch is redirected to either the destination of the
+second branch or a point immediately following it, depending on whether
+the condition is known to be true or false.
+.TP
+.B \-funroll\-loops
+Perform the optimization of loop unrolling. This is only done for loops
+whose number of iterations can be determined at compile time or run time.
+.TP
+.B \-funroll\-all\-loops
+Perform the optimization of loop unrolling. This is done for all loops.
+This usually makes programs run more slowly.
+.TP
+.B \-fcse\-follow\-jumps
+In common subexpression elimination, scan through jump instructions
+when the target of the jump is not reached by any other path. For
+example, when CSE encounters an \c
+.B if\c
+\& statement with an
+.B else\c
+\& clause, CSE will follow the jump when the condition
+tested is false.
+.TP
+.B \-fcse\-skip\-blocks
+This is similar to `\|\c
+.B \-fcse\-follow\-jumps\c
+\&\|', but causes CSE to
+follow jumps which conditionally skip over blocks. When CSE
+encounters a simple \c
+.B if\c
+\& statement with no else clause,
+`\|\c
+.B \-fcse\-skip\-blocks\c
+\&\|' causes CSE to follow the jump around the
+body of the \c
+.B if\c
+\&.
+.TP
+.B \-frerun\-cse\-after\-loop
+Re-run common subexpression elimination after loop optimizations has been
+performed.
+.TP
+.B \-felide\-constructors
+Elide constructors when this seems plausible (C++ only). With this
+flag, GNU C++ initializes \c
+.B y\c
+\& directly from the call to \c
+.B foo
+without going through a temporary in the following code:
+.Sp
+A foo ();
+A y = foo ();
+.Sp
+Without this option, GNU C++ first initializes \c
+.B y\c
+\& by calling the
+appropriate constructor for type \c
+.B A\c
+\&; then assigns the result of
+.B foo\c
+\& to a temporary; and, finally, replaces the initial valyue of
+`\|\c
+.B y\c
+\&\|' with the temporary.
+.Sp
+The default behavior (`\|\c
+.B \-fno\-elide\-constructors\c
+\&\|') is specified by
+the draft ANSI C++ standard. If your program's constructors have side
+effects, using `\|\c
+.B \-felide-constructors\c
+\&\|' can make your program act
+differently, since some constructor calls may be omitted.
+.TP
+.B \-fexpensive\-optimizations
+Perform a number of minor optimizations that are relatively expensive.
+.TP
+.B \-fdelayed\-branch
+If supported for the target machine, attempt to reorder instructions
+to exploit instruction slots available after delayed branch
+instructions.
+.TP
+.B \-fschedule\-insns
+If supported for the target machine, attempt to reorder instructions to
+eliminate execution stalls due to required data being unavailable. This
+helps machines that have slow floating point or memory load instructions
+by allowing other instructions to be issued until the result of the load
+or floating point instruction is required.
+.TP
+.B \-fschedule\-insns2
+Similar to `\|\c
+.B \-fschedule\-insns\c
+\&\|', but requests an additional pass of
+instruction scheduling after register allocation has been done. This is
+especially useful on machines with a relatively small number of
+registers and where memory load instructions take more than one cycle.
+.SH TARGET OPTIONS
+By default, GNU CC compiles code for the same type of machine that you
+are using. However, it can also be installed as a cross-compiler, to
+compile for some other type of machine. In fact, several different
+configurations of GNU CC, for different target machines, can be
+installed side by side. Then you specify which one to use with the
+`\|\c
+.B \-b\c
+\&\|' option.
+.PP
+In addition, older and newer versions of GNU CC can be installed side
+by side. One of them (probably the newest) will be the default, but
+you may sometimes wish to use another.
+.TP
+.BI "\-b " "machine"
+The argument \c
+.I machine\c
+\& specifies the target machine for compilation.
+This is useful when you have installed GNU CC as a cross-compiler.
+.Sp
+The value to use for \c
+.I machine\c
+\& is the same as was specified as the
+machine type when configuring GNU CC as a cross-compiler. For
+example, if a cross-compiler was configured with `\|\c
+.B configure
+i386v\c
+\&\|', meaning to compile for an 80386 running System V, then you
+would specify `\|\c
+.B \-b i386v\c
+\&\|' to run that cross compiler.
+.Sp
+When you do not specify `\|\c
+.B \-b\c
+\&\|', it normally means to compile for
+the same type of machine that you are using.
+.TP
+.BI "\-V " "version"
+The argument \c
+.I version\c
+\& specifies which version of GNU CC to run.
+This is useful when multiple versions are installed. For example,
+.I version\c
+\& might be `\|\c
+.B 2.0\c
+\&\|', meaning to run GNU CC version 2.0.
+.Sp
+The default version, when you do not specify `\|\c
+.B \-V\c
+\&\|', is controlled
+by the way GNU CC is installed. Normally, it will be a version that
+is recommended for general use.
+.SH MACHINE DEPENDENT OPTIONS
+Each of the target machine types can have its own special options,
+starting with `\|\c
+.B \-m\c
+\&\|', to choose among various hardware models or
+configurations\(em\&for example, 68010 vs 68020, floating coprocessor or
+none. A single installed version of the compiler can compile for any
+model or configuration, according to the options specified.
+.PP
+Some configurations of the compiler also support additional special
+options, usually for command-line compatibility with other compilers on
+the same platform.
+.PP
+These are the `\|\c
+.B \-m\c
+\&\|' options defined for the 68000 series:
+.TP
+.B \-m68000
+.TP
+.B \-mc68000
+Generate output for a 68000. This is the default when the compiler is
+configured for 68000-based systems.
+.TP
+.B \-m68020
+.TP
+.B \-mc68020
+Generate output for a 68020 (rather than a 68000). This is the
+default when the compiler is configured for 68020-based systems.
+.TP
+.B \-m68881
+Generate output containing 68881 instructions for floating point.
+This is the default for most 68020-based systems unless
+.B \-nfp
+was specified when the compiler was configured.
+.TP
+.B \-m68030
+Generate output for a 68030. This is the default when the compiler is
+configured for 68030-based systems.
+.TP
+.B \-m68040
+Generate output for a 68040. This is the default when the compiler is
+configured for 68040-based systems.
+.TP
+.B \-m68020\-40
+Generate output for a 68040, without using any of the new instructions.
+This results in code which can run relatively efficiently on either a
+68020/68881 or a 68030 or a 68040.
+.TP
+.B \-mfpa
+Generate output containing Sun FPA instructions for floating point.
+.TP
+.B \-msoft\-float
+Generate output containing library calls for floating point.
+.I
+WARNING:
+the requisite libraries are not part of GNU CC. Normally the
+facilities of the machine's usual C compiler are used, but this can't
+be done directly in cross-compilation. You must make your own
+arrangements to provide suitable library functions for cross-compilation.
+.TP
+.B \-mshort
+Consider type \c
+.B int\c
+\& to be 16 bits wide, like \c
+.B short int\c
+\&.
+.TP
+.B \-mnobitfield
+Do not use the bit-field instructions. `\|\c
+.B \-m68000\c
+\&\|' implies
+`\|\c
+.B \-mnobitfield\c
+\&\|'.
+.TP
+.B \-mbitfield
+Do use the bit-field instructions. `\|\c
+.B \-m68020\c
+\&\|' implies
+`\|\c
+.B \-mbitfield\c
+\&\|'. This is the default if you use the unmodified
+sources.
+.TP
+.B \-mrtd
+Use a different function-calling convention, in which functions
+that take a fixed number of arguments return with the \c
+.B rtd
+instruction, which pops their arguments while returning. This
+saves one instruction in the caller since there is no need to pop
+the arguments there.
+.Sp
+This calling convention is incompatible with the one normally
+used on Unix, so you cannot use it if you need to call libraries
+compiled with the Unix compiler.
+.Sp
+Also, you must provide function prototypes for all functions that
+take variable numbers of arguments (including \c
+.B printf\c
+\&);
+otherwise incorrect code will be generated for calls to those
+functions.
+.Sp
+In addition, seriously incorrect code will result if you call a
+function with too many arguments. (Normally, extra arguments are
+harmlessly ignored.)
+.Sp
+The \c
+.B rtd\c
+\& instruction is supported by the 68010 and 68020
+processors, but not by the 68000.
+.PP
+These `\|\c
+.B \-m\c
+\&\|' options are defined for the Vax:
+.TP
+.B \-munix
+Do not output certain jump instructions (\c
+.B aobleq\c
+\& and so on)
+that the Unix assembler for the Vax cannot handle across long
+ranges.
+.TP
+.B \-mgnu
+Do output those jump instructions, on the assumption that you
+will assemble with the GNU assembler.
+.TP
+.B \-mg
+Output code for g-format floating point numbers instead of d-format.
+.PP
+These `\|\c
+.B \-m\c
+\&\|' switches are supported on the SPARC:
+.PP
+.B \-mfpu
+.TP
+.B \-mhard\-float
+Generate output containing floating point instructions. This is the
+default.
+.PP
+.B \-mno\-fpu
+.TP
+.B \-msoft\-float
+Generate output containing library calls for floating point.
+.I Warning:
+there is no GNU floating-point library for SPARC.
+Normally the facilities of the machine's usual C compiler are used, but
+this cannot be done directly in cross-compilation. You must make your
+own arrangements to provide suitable library functions for
+cross-compilation.
+.Sp
+.B \-msoft\-float
+changes the calling convention in the output file;
+therefore, it is only useful if you compile
+.I all
+of a program with this option.
+.PP
+.B \-mno\-epilogue
+.TP
+.B \-mepilogue
+With
+.B \-mepilogue
+(the default), the compiler always emits code for
+function exit at the end of each function. Any function exit in
+the middle of the function (such as a return statement in C) will
+generate a jump to the exit code at the end of the function.
+.Sp
+With
+.BR \-mno\-epilogue ,
+the compiler tries to emit exit code inline at every function exit.
+.PP
+.B \-mno\-v8
+.TP
+.B \-mv8
+.TP
+.B \-msparclite
+These three options select variations on the SPARC architecture.
+.Sp
+By default (unless specifically configured for the Fujitsu SPARClite),
+GCC generates code for the v7 variant of the SPARC architecture.
+.Sp
+.B \-mv8
+will give you SPARC v8 code. The only difference from v7
+code is that the compiler emits the integer multiply and integer
+divide instructions which exist in SPARC v8 but not in SPARC v7.
+.Sp
+.B \-msparclite
+will give you SPARClite code. This adds the integer
+multiply, integer divide step and scan (ffs) instructions which
+exist in SPARClite but not in SPARC v7.
+.PP
+.B \-mcypress
+.TP
+.B \-msupersparc
+These two options select the processor for which the code is optimised.
+.Sp
+With
+.B \-mcypress
+(the default), the compiler optimises code for the Cypress CY7C602 chip, as
+used in the SparcStation/SparcSever 3xx series. This is also apropriate for
+the older SparcStation 1, 2, IPX etc.
+.Sp
+With
+.B \-msupersparc
+the compiler optimises code for the SuperSparc cpu, as used in the SparcStation
+10, 1000 and 2000 series. This flag also enables use of the full SPARC v8
+instruction set.
+.PP
+These `\|\c
+.B \-m\c
+\&\|' options are defined for the Convex:
+.TP
+.B \-mc1
+Generate output for a C1. This is the default when the compiler is
+configured for a C1.
+.TP
+.B \-mc2
+Generate output for a C2. This is the default when the compiler is
+configured for a C2.
+.TP
+.B \-margcount
+Generate code which puts an argument count in the word preceding each
+argument list. Some nonportable Convex and Vax programs need this word.
+(Debuggers don't, except for functions with variable-length argument
+lists; this info is in the symbol table.)
+.TP
+.B \-mnoargcount
+Omit the argument count word. This is the default if you use the
+unmodified sources.
+.PP
+These `\|\c
+.B \-m\c
+\&\|' options are defined for the AMD Am29000:
+.TP
+.B \-mdw
+Generate code that assumes the DW bit is set, i.e., that byte and
+halfword operations are directly supported by the hardware. This is the
+default.
+.TP
+.B \-mnodw
+Generate code that assumes the DW bit is not set.
+.TP
+.B \-mbw
+Generate code that assumes the system supports byte and halfword write
+operations. This is the default.
+.TP
+.B \-mnbw
+Generate code that assumes the systems does not support byte and
+halfword write operations. This implies `\|\c
+.B \-mnodw\c
+\&\|'.
+.TP
+.B \-msmall
+Use a small memory model that assumes that all function addresses are
+either within a single 256 KB segment or at an absolute address of less
+than 256K. This allows the \c
+.B call\c
+\& instruction to be used instead
+of a \c
+.B const\c
+\&, \c
+.B consth\c
+\&, \c
+.B calli\c
+\& sequence.
+.TP
+.B \-mlarge
+Do not assume that the \c
+.B call\c
+\& instruction can be used; this is the
+default.
+.TP
+.B \-m29050
+Generate code for the Am29050.
+.TP
+.B \-m29000
+Generate code for the Am29000. This is the default.
+.TP
+.B \-mkernel\-registers
+Generate references to registers \c
+.B gr64-gr95\c
+\& instead of
+.B gr96-gr127\c
+\&. This option can be used when compiling kernel code
+that wants a set of global registers disjoint from that used by
+user-mode code.
+.Sp
+Note that when this option is used, register names in `\|\c
+.B \-f\c
+\&\|' flags
+must use the normal, user-mode, names.
+.TP
+.B \-muser\-registers
+Use the normal set of global registers, \c
+.B gr96-gr127\c
+\&. This is the
+default.
+.TP
+.B \-mstack\-check
+Insert a call to \c
+.B _\|_msp_check\c
+\& after each stack adjustment. This
+is often used for kernel code.
+.PP
+These `\|\c
+.B \-m\c
+\&\|' options are defined for Motorola 88K architectures:
+.TP
+.B \-m88000
+Generate code that works well on both the m88100 and the
+m88110.
+.TP
+.B \-m88100
+Generate code that works best for the m88100, but that also
+runs on the m88110.
+.TP
+.B \-m88110
+Generate code that works best for the m88110, and may not run
+on the m88100.
+.TP
+.B \-midentify\-revision
+Include an \c
+.B ident\c
+\& directive in the assembler output recording the
+source file name, compiler name and version, timestamp, and compilation
+flags used.
+.TP
+.B \-mno\-underscores
+In assembler output, emit symbol names without adding an underscore
+character at the beginning of each name. The default is to use an
+underscore as prefix on each name.
+.TP
+.B \-mno\-check\-zero\-division
+.TP
+.B \-mcheck\-zero\-division
+Early models of the 88K architecture had problems with division by zero;
+in particular, many of them didn't trap. Use these options to avoid
+including (or to include explicitly) additional code to detect division
+by zero and signal an exception. All GCC configurations for the 88K use
+`\|\c
+.B \-mcheck\-zero\-division\c
+\&\|' by default.
+.TP
+.B \-mocs\-debug\-info
+.TP
+.B \-mno\-ocs\-debug\-info
+Include (or omit) additional debugging information (about
+registers used in each stack frame) as specified in the 88Open Object
+Compatibility Standard, \*(lqOCS\*(rq. This extra information is not needed
+by GDB. The default for DG/UX, SVr4, and Delta 88 SVr3.2 is to
+include this information; other 88k configurations omit this information
+by default.
+.TP
+.B \-mocs\-frame\-position
+.TP
+.B \-mno\-ocs\-frame\-position
+Force (or do not require) register values to be stored in a particular
+place in stack frames, as specified in OCS. The DG/UX, Delta88 SVr3.2,
+and BCS configurations use `\|\c
+.B \-mocs\-frame\-position\c
+\&\|'; other 88k
+configurations have the default `\|\c
+.B \-mno\-ocs\-frame\-position\c
+\&\|'.
+.TP
+.B \-moptimize\-arg\-area
+.TP
+.B \-mno\-optimize\-arg\-area
+Control how to store function arguments in stack frames.
+`\|\c
+.B \-moptimize\-arg\-area\c
+\&\|' saves space, but may break some
+debuggers (not GDB). `\|\c
+.B \-mno\-optimize\-arg\-area\c
+\&\|' conforms better to
+standards. By default GCC does not optimize the argument area.
+.TP
+.BI "\-mshort\-data\-" "num"
+.I num
+Generate smaller data references by making them relative to \c
+.B r0\c
+\&,
+which allows loading a value using a single instruction (rather than the
+usual two). You control which data references are affected by
+specifying \c
+.I num\c
+\& with this option. For example, if you specify
+`\|\c
+.B \-mshort\-data\-512\c
+\&\|', then the data references affected are those
+involving displacements of less than 512 bytes.
+`\|\c
+.B \-mshort\-data\-\c
+.I num\c
+\&\c
+\&\|' is not effective for \c
+.I num\c
+\& greater
+than 64K.
+.PP
+.B \-mserialize-volatile
+.TP
+.B \-mno-serialize-volatile
+Do, or do not, generate code to guarantee sequential consistency of
+volatile memory references.
+.Sp
+GNU CC always guarantees consistency by default, for the preferred
+processor submodel. How this is done depends on the submodel.
+.Sp
+The m88100 processor does not reorder memory references and so always
+provides sequential consistency. If you use `\|\c
+.B \-m88100\c
+\&\|', GNU CC does
+not generate any special instructions for sequential consistency.
+.Sp
+The order of memory references made by the m88110 processor does not
+always match the order of the instructions requesting those references.
+In particular, a load instruction may execute before a preceding store
+instruction. Such reordering violates sequential consistency of
+volatile memory references, when there are multiple processors. When
+you use `\|\c
+.B \-m88000\c
+\&\|' or `\|\c
+.B \-m88110\c
+\&\|', GNU CC generates special
+instructions when appropriate, to force execution in the proper order.
+.Sp
+The extra code generated to guarantee consistency may affect the
+performance of your application. If you know that you can safely forgo
+this guarantee, you may use the option `\|\c
+.B \-mno-serialize-volatile\c
+\&\|'.
+.Sp
+If you use the `\|\c
+.B \-m88100\c
+\&\|' option but require sequential consistency
+when running on the m88110 processor, you should use
+`\|\c
+.B \-mserialize-volatile\c
+\&\|'.
+.PP
+.B \-msvr4
+.TP
+.B \-msvr3
+Turn on (`\|\c
+.B \-msvr4\c
+\&\|') or off (`\|\c
+.B \-msvr3\c
+\&\|') compiler extensions
+related to System V release 4 (SVr4). This controls the following:
+.TP
+\ \ \ \(bu
+Which variant of the assembler syntax to emit (which you can select
+independently using `\|\c
+.B \-mversion\-03.00\c
+\&\|').
+.TP
+\ \ \ \(bu
+`\|\c
+.B \-msvr4\c
+\&\|' makes the C preprocessor recognize `\|\c
+.B #pragma weak\c
+\&\|'
+.TP
+\ \ \ \(bu
+`\|\c
+.B \-msvr4\c
+\&\|' makes GCC issue additional declaration directives used in
+SVr4.
+.PP
+`\|\c
+.B \-msvr3\c
+\&\|' is the default for all m88K configurations except
+the SVr4 configuration.
+.TP
+.B \-mtrap\-large\-shift
+.TP
+.B \-mhandle\-large\-shift
+Include code to detect bit-shifts of more than 31 bits; respectively,
+trap such shifts or emit code to handle them properly. By default GCC
+makes no special provision for large bit shifts.
+.TP
+.B \-muse\-div\-instruction
+Very early models of the 88K architecture didn't have a divide
+instruction, so GCC avoids that instruction by default. Use this option
+to specify that it's safe to use the divide instruction.
+.TP
+.B \-mversion\-03.00
+In the DG/UX configuration, there are two flavors of SVr4. This option
+modifies
+.B \-msvr4
+to select whether the hybrid-COFF or real-ELF
+flavor is used. All other configurations ignore this option.
+.TP
+.B \-mwarn\-passed\-structs
+Warn when a function passes a struct as an argument or result.
+Structure-passing conventions have changed during the evolution of the C
+language, and are often the source of portability problems. By default,
+GCC issues no such warning.
+.PP
+These options are defined for the IBM RS6000:
+.PP
+.B \-mfp\-in\-toc
+.TP
+.B \-mno\-fp\-in\-toc
+Control whether or not floating-point constants go in the Table of
+Contents (TOC), a table of all global variable and function addresses. By
+default GCC puts floating-point constants there; if the TOC overflows,
+`\|\c
+.B \-mno\-fp\-in\-toc\c
+\&\|' will reduce the size of the TOC, which may avoid
+the overflow.
+.PP
+These `\|\c
+.B \-m\c
+\&\|' options are defined for the IBM RT PC:
+.TP
+.B \-min\-line\-mul
+Use an in-line code sequence for integer multiplies. This is the
+default.
+.TP
+.B \-mcall\-lib\-mul
+Call \c
+.B lmul$$\c
+\& for integer multiples.
+.TP
+.B \-mfull\-fp\-blocks
+Generate full-size floating point data blocks, including the minimum
+amount of scratch space recommended by IBM. This is the default.
+.TP
+.B \-mminimum\-fp\-blocks
+Do not include extra scratch space in floating point data blocks. This
+results in smaller code, but slower execution, since scratch space must
+be allocated dynamically.
+.TP
+.B \-mfp\-arg\-in\-fpregs
+Use a calling sequence incompatible with the IBM calling convention in
+which floating point arguments are passed in floating point registers.
+Note that \c
+.B varargs.h\c
+\& and \c
+.B stdargs.h\c
+\& will not work with
+floating point operands if this option is specified.
+.TP
+.B \-mfp\-arg\-in\-gregs
+Use the normal calling convention for floating point arguments. This is
+the default.
+.TP
+.B \-mhc\-struct\-return
+Return structures of more than one word in memory, rather than in a
+register. This provides compatibility with the MetaWare HighC (hc)
+compiler. Use `\|\c
+.B \-fpcc\-struct\-return\c
+\&\|' for compatibility with the
+Portable C Compiler (pcc).
+.TP
+.B \-mnohc\-struct\-return
+Return some structures of more than one word in registers, when
+convenient. This is the default. For compatibility with the
+IBM-supplied compilers, use either `\|\c
+.B \-fpcc\-struct\-return\c
+\&\|' or
+`\|\c
+.B \-mhc\-struct\-return\c
+\&\|'.
+.PP
+These `\|\c
+.B \-m\c
+\&\|' options are defined for the MIPS family of computers:
+.TP
+.BI "\-mcpu=" "cpu-type"
+Assume the defaults for the machine type
+.I cpu-type
+when
+scheduling instructions. The default
+.I cpu-type
+is
+.BR default ,
+which picks the longest cycles times for any of the machines, in order
+that the code run at reasonable rates on all MIPS cpu's. Other
+choices for
+.I cpu-type
+are
+.BR r2000 ,
+.BR r3000 ,
+.BR r4000 ,
+and
+.BR r6000 .
+While picking a specific
+.I cpu-type
+will schedule things appropriately for that particular chip, the
+compiler will not generate any code that does not meet level 1 of the
+MIPS ISA (instruction set architecture) without the
+.B \-mips2
+or
+.B \-mips3
+switches being used.
+.TP
+.B \-mips2
+Issue instructions from level 2 of the MIPS ISA (branch likely, square
+root instructions). The
+.B \-mcpu=r4000
+or
+.B \-mcpu=r6000
+switch must be used in conjunction with
+.BR \-mips2 .
+.TP
+.B \-mips3
+Issue instructions from level 3 of the MIPS ISA (64 bit instructions).
+The
+.B \-mcpu=r4000
+switch must be used in conjunction with
+.BR \-mips2 .
+.TP
+.B \-mint64
+.TP
+.B \-mlong64
+.TP
+.B \-mlonglong128
+These options don't work at present.
+.TP
+.B \-mmips\-as
+Generate code for the MIPS assembler, and invoke
+.B mips\-tfile
+to add normal debug information. This is the default for all
+platforms except for the OSF/1 reference platform, using the OSF/rose
+object format. If any of the
+.BR \-ggdb ,
+.BR \-gstabs ,
+or
+.B \-gstabs+
+switches are used, the
+.B mips\-tfile
+program will encapsulate the stabs within MIPS ECOFF.
+.TP
+.B \-mgas
+Generate code for the GNU assembler. This is the default on the OSF/1
+reference platform, using the OSF/rose object format.
+.TP
+.B \-mrnames
+.TP
+.B \-mno\-rnames
+The
+.B \-mrnames
+switch says to output code using the MIPS software names for the
+registers, instead of the hardware names (ie,
+.B a0
+instead of
+.BR $4 ).
+The GNU assembler does not support the
+.B \-mrnames
+switch, and the MIPS assembler will be instructed to run the MIPS C
+preprocessor over the source file. The
+.B \-mno\-rnames
+switch is default.
+.TP
+.B \-mgpopt
+.TP
+.B \-mno\-gpopt
+The
+.B \-mgpopt
+switch says to write all of the data declarations before the
+instructions in the text section, to all the MIPS assembler to
+generate one word memory references instead of using two words for
+short global or static data items. This is on by default if
+optimization is selected.
+.TP
+.B \-mstats
+.TP
+.B \-mno\-stats
+For each non-inline function processed, the
+.B \-mstats
+switch causes the compiler to emit one line to the standard error file
+to print statistics about the program (number of registers saved,
+stack size, etc.).
+.TP
+.B \-mmemcpy
+.TP
+.B \-mno\-memcpy
+The
+.B \-mmemcpy
+switch makes all block moves call the appropriate string function
+.RB ( memcpy
+or
+.BR bcopy )
+instead of possibly generating inline code.
+.TP
+.B \-mmips\-tfile
+.TP
+.B \-mno\-mips\-tfile
+The
+.B \-mno\-mips\-tfile
+switch causes the compiler not postprocess the object file with the
+.B mips\-tfile
+program, after the MIPS assembler has generated it to add debug
+support. If
+.B mips\-tfile
+is not run, then no local variables will be available to the debugger.
+In addition,
+.B stage2
+and
+.B stage3
+objects will have the temporary file names passed to the assembler
+embedded in the object file, which means the objects will not compare
+the same.
+.TP
+.B \-msoft\-float
+Generate output containing library calls for floating point.
+.I
+WARNING:
+the requisite libraries are not part of GNU CC. Normally the
+facilities of the machine's usual C compiler are used, but this can't
+be done directly in cross-compilation. You must make your own
+arrangements to provide suitable library functions for cross-compilation.
+.TP
+.B \-mhard\-float
+Generate output containing floating point instructions. This is the
+default if you use the unmodified sources.
+.TP
+.B \-mfp64
+Assume that the
+.B FR
+bit in the status word is on, and that there are 32 64-bit floating
+point registers, instead of 32 32-bit floating point registers. You
+must also specify the
+.B \-mcpu=r4000
+and
+.B \-mips3
+switches.
+.TP
+.B \-mfp32
+Assume that there are 32 32-bit floating point registers. This is the
+default.
+.PP
+.B \-mabicalls
+.TP
+.B \-mno\-abicalls
+Emit (or do not emit) the
+.BR \&.abicalls ,
+.BR \&.cpload ,
+and
+.B \&.cprestore
+pseudo operations that some System V.4 ports use for position
+independent code.
+.TP
+.B \-mhalf\-pic
+.TP
+.B \-mno\-half\-pic
+The
+.B \-mhalf\-pic
+switch says to put pointers to extern references into the data section
+and load them up, rather than put the references in the text section.
+This option does not work at present.
+.B
+.BI \-G num
+Put global and static items less than or equal to
+.I num
+bytes into the small data or bss sections instead of the normal data
+or bss section. This allows the assembler to emit one word memory
+reference instructions based on the global pointer
+.RB ( gp
+or
+.BR $28 ),
+instead of the normal two words used. By default,
+.I num
+is 8 when the MIPS assembler is used, and 0 when the GNU
+assembler is used. The
+.BI \-G num
+switch is also passed to the assembler and linker. All modules should
+be compiled with the same
+.BI \-G num
+value.
+.TP
+.B \-nocpp
+Tell the MIPS assembler to not run it's preprocessor over user
+assembler files (with a `\|\c
+.B .s\c
+\&\|' suffix) when assembling them.
+.PP
+These `\|\c
+.B \-m\c
+\&\|' options are defined for the Intel 80386 family of computers:
+.B \-m486
+.TP
+.B \-mno\-486
+Control whether or not code is optimized for a 486 instead of an
+386. Code generated for a 486 will run on a 386 and vice versa.
+.TP
+.B \-msoft\-float
+Generate output containing library calls for floating point.
+.I Warning:
+the requisite libraries are not part of GNU CC.
+Normally the facilities of the machine's usual C compiler are used, but
+this can't be done directly in cross-compilation. You must make your
+own arrangements to provide suitable library functions for
+cross-compilation.
+.Sp
+On machines where a function returns floating point results in the 80387
+register stack, some floating point opcodes may be emitted even if
+`\|\c
+.B \-msoft-float\c
+\&\|' is used.
+.TP
+.B \-mno-fp-ret-in-387
+Do not use the FPU registers for return values of functions.
+.Sp
+The usual calling convention has functions return values of types
+.B float\c
+\& and \c
+.B double\c
+\& in an FPU register, even if there
+is no FPU. The idea is that the operating system should emulate
+an FPU.
+.Sp
+The option `\|\c
+.B \-mno-fp-ret-in-387\c
+\&\|' causes such values to be returned
+in ordinary CPU registers instead.
+.PP
+These `\|\c
+.B \-m\c
+\&\|' options are defined for the HPPA family of computers:
+.TP
+.B \-mpa-risc-1-0
+Generate code for a PA 1.0 processor.
+.TP
+.B \-mpa-risc-1-1
+Generate code for a PA 1.1 processor.
+.TP
+.B \-mkernel
+Generate code which is suitable for use in kernels. Specifically, avoid
+.B add\c
+\& instructions in which one of the arguments is the DP register;
+generate \c
+.B addil\c
+\& instructions instead. This avoids a rather serious
+bug in the HP-UX linker.
+.TP
+.B \-mshared-libs
+Generate code that can be linked against HP-UX shared libraries. This option
+is not fully function yet, and is not on by default for any PA target. Using
+this option can cause incorrect code to be generated by the compiler.
+.TP
+.B \-mno-shared-libs
+Don't generate code that will be linked against shared libraries. This is
+the default for all PA targets.
+.TP
+.B \-mlong-calls
+Generate code which allows calls to functions greater than 256K away from
+the caller when the caller and callee are in the same source file. Do
+not turn this option on unless code refuses to link with \*(lqbranch out of
+range errors\*('' from the linker.
+.TP
+.B \-mdisable-fpregs
+Prevent floating point registers from being used in any manner. This is
+necessary for compiling kernels which perform lazy context switching of
+floating point registers. If you use this option and attempt to perform
+floating point operations, the compiler will abort.
+.TP
+.B \-mdisable-indexing
+Prevent the compiler from using indexing address modes. This avoids some
+rather obscure problems when compiling MIG generated code under MACH.
+.TP
+.B \-mtrailing-colon
+Add a colon to the end of label definitions (for ELF assemblers).
+.PP
+These `\|\c
+.B \-m\c
+\&\|' options are defined for the Intel 80960 family of computers:
+.TP
+.BI "\-m" "cpu-type"
+Assume the defaults for the machine type
+.I cpu-type
+for instruction and addressing-mode availability and alignment.
+The default
+.I cpu-type
+is
+.BR kb ;
+other choices are
+.BR ka ,
+.BR mc ,
+.BR ca ,
+.BR cf ,
+.BR sa ,
+and
+.BR sb .
+.TP
+.B \-mnumerics
+.TP
+.B \-msoft\-float
+The
+.B \-mnumerics
+option indicates that the processor does support
+floating-point instructions. The
+.B \-msoft\-float
+option indicates
+that floating-point support should not be assumed.
+.TP
+.B \-mleaf\-procedures
+.TP
+.B \-mno\-leaf\-procedures
+Do (or do not) attempt to alter leaf procedures to be callable with the
+.I bal
+instruction as well as
+.IR call .
+This will result in more
+efficient code for explicit calls when the
+.I bal
+instruction can be
+substituted by the assembler or linker, but less efficient code in other
+cases, such as calls via function pointers, or using a linker that doesn't
+support this optimization.
+.TP
+.B \-mtail\-call
+.TP
+.B \-mno\-tail\-call
+Do (or do not) make additional attempts (beyond those of the
+machine-independent portions of the compiler) to optimize tail-recursive
+calls into branches. You may not want to do this because the detection of
+cases where this is not valid is not totally complete. The default is
+.BR \-mno\-tail\-call .
+.TP
+.B \-mcomplex\-addr
+.TP
+.B \-mno\-complex\-addr
+Assume (or do not assume) that the use of a complex addressing mode is a
+win on this implementation of the i960. Complex addressing modes may not
+be worthwhile on the K-series, but they definitely are on the C-series.
+The default is currently
+.B \-mcomplex\-addr
+for all processors except
+the CB and CC.
+.TP
+.B \-mcode\-align
+.TP
+.B \-mno\-code\-align
+Align code to 8-byte boundaries for faster fetching (or don't bother).
+Currently turned on by default for C-series implementations only.
+.TP
+.B \-mic\-compat
+.TP
+.B \-mic2.0\-compat
+.TP
+.B \-mic3.0\-compat
+Enable compatibility with iC960 v2.0 or v3.0.
+.TP
+.B \-masm\-compat
+.TP
+.B \-mintel\-asm
+Enable compatibility with the iC960 assembler.
+.TP
+.B \-mstrict\-align
+.TP
+.B \-mno\-strict\-align
+Do not permit (do permit) unaligned accesses.
+.TP
+.B \-mold\-align
+Enable structure-alignment compatibility with Intel's gcc release version
+1.3 (based on gcc 1.37). Currently this is buggy in that
+.B #pragma align 1
+is always assumed as well, and cannot be turned off.
+.PP
+These `\|\c
+.B \-m\c
+\&\|' options are defined for the DEC Alpha implementations:
+.TP
+.B \-mno-soft-float
+.TP
+.B \-msoft-float
+Use (do not use) the hardware floating-point instructions for
+floating-point operations. When \c
+.B \-msoft-float\c
+\& is specified,
+functions in `\|\c
+.B libgcc1.c\c
+\&\|' will be used to perform floating-point
+operations. Unless they are replaced by routines that emulate the
+floating-point operations, or compiled in such a way as to call such
+emulations routines, these routines will issue floating-point
+operations. If you are compiling for an Alpha without floating-point
+operations, you must ensure that the library is built so as not to call
+them.
+.Sp
+Note that Alpha implementations without floating-point operations are
+required to have floating-point registers.
+.TP
+.B \-mfp-reg
+.TP
+.B \-mno-fp-regs
+Generate code that uses (does not use) the floating-point register set.
+.B \-mno-fp-regs\c
+\& implies \c
+.B \-msoft-float\c
+\&. If the floating-point
+register set is not used, floating point operands are passed in integer
+registers as if they were integers and floating-point results are passed
+in $0 instead of $f0. This is a non-standard calling sequence, so any
+function with a floating-point argument or return value called by code
+compiled with \c
+.B \-mno-fp-regs\c
+\& must also be compiled with that
+option.
+.Sp
+A typical use of this option is building a kernel that does not use,
+and hence need not save and restore, any floating-point registers.
+.PP
+These additional options are available on System V Release 4 for
+compatibility with other compilers on those systems:
+.TP
+.B \-G
+On SVr4 systems, \c
+.B gcc\c
+\& accepts the option `\|\c
+.B \-G\c
+\&\|' (and passes
+it to the system linker), for compatibility with other compilers.
+However, we suggest you use `\|\c
+.B \-symbolic\c
+\&\|' or `\|\c
+.B \-shared\c
+\&\|' as
+appropriate, instead of supplying linker options on the \c
+.B gcc
+command line.
+.TP
+.B \-Qy
+Identify the versions of each tool used by the compiler, in a
+.B .ident\c
+\& assembler directive in the output.
+.TP
+.B \-Qn
+Refrain from adding \c
+.B .ident\c
+\& directives to the output file (this is
+the default).
+.TP
+.BI "\-YP," "dirs"
+Search the directories \c
+.I dirs\c
+\&, and no others, for libraries
+specified with `\|\c
+.B \-l\c
+\&\|'. You can separate directory entries in
+.I dirs\c
+\& from one another with colons.
+.TP
+.BI "\-Ym," "dir"
+Look in the directory \c
+.I dir\c
+\& to find the M4 preprocessor.
+The assembler uses this option.
+.SH CODE GENERATION OPTIONS
+These machine-independent options control the interface conventions
+used in code generation.
+.PP
+Most of them begin with `\|\c
+\-f\c
+\&\|'. These options have both positive and negative forms; the negative form
+of `\|\c
+.B \-ffoo\c
+\&\|' would be `\|\c
+.B \-fno\-foo\c
+\&\|'. In the table below, only
+one of the forms is listed\(em\&the one which is not the default. You
+can figure out the other form by either removing `\|\c
+.B no\-\c
+\&\|' or adding
+it.
+.TP
+.B \-fnonnull\-objects
+Assume that objects reached through references are not null
+(C++ only).
+.Sp
+Normally, GNU C++ makes conservative assumptions about objects reached
+through references. For example, the compiler must check that \c
+.B a
+is not null in code like the following:
+.Sp
+obj &a = g ();
+a.f (2);
+.Sp
+Checking that references of this sort have non-null values requires
+extra code, however, and it is unnecessary for many programs. You can
+use `\|\c
+.B \-fnonnull-objects\c
+\&\|' to omit the checks for null, if your
+program doesn't require checking.
+.TP
+.B \-fpcc\-struct\-return
+Use the same convention for returning \c
+.B struct\c
+\& and \c
+.B union
+values that is used by the usual C compiler on your system. This
+convention is less efficient for small structures, and on many
+machines it fails to be reentrant; but it has the advantage of
+allowing intercallability between GCC-compiled code and PCC-compiled
+code.
+.TP
+.B \-freg\-struct\-return
+Use the convention that
+.B struct
+and
+.B union
+values are returned in registers when possible. This is more
+efficient for small structures than
+.BR \-fpcc\-struct\-return .
+.Sp
+If you specify neither
+.B \-fpcc\-struct\-return
+nor
+.BR \-freg\-struct\-return ,
+GNU CC defaults to whichever convention is standard for the target.
+If there is no standard convention, GNU CC defaults to
+.BR \-fpcc\-struct\-return .
+.TP
+.B \-fshort\-enums
+Allocate to an \c
+.B enum\c
+\& type only as many bytes as it needs for the
+declared range of possible values. Specifically, the \c
+.B enum\c
+\& type
+will be equivalent to the smallest integer type which has enough room.
+.TP
+.B \-fshort\-double
+Use the same size for
+.B double
+as for
+.B float
+\&.
+.TP
+.B \-fshared\-data
+Requests that the data and non-\c
+.B const\c
+\& variables of this
+compilation be shared data rather than private data. The distinction
+makes sense only on certain operating systems, where shared data is
+shared between processes running the same program, while private data
+exists in one copy per process.
+.TP
+.B \-fno\-common
+Allocate even uninitialized global variables in the bss section of the
+object file, rather than generating them as common blocks. This has the
+effect that if the same variable is declared (without \c
+.B extern\c
+\&) in
+two different compilations, you will get an error when you link them.
+The only reason this might be useful is if you wish to verify that the
+program will work on other systems which always work this way.
+.TP
+.B \-fno\-ident
+Ignore the `\|\c
+.B #ident\c
+\&\|' directive.
+.TP
+.B \-fno\-gnu\-linker
+Do not output global initializations (such as C++ constructors and
+destructors) in the form used by the GNU linker (on systems where the GNU
+linker is the standard method of handling them). Use this option when
+you want to use a non-GNU linker, which also requires using the
+.B collect2\c
+\& program to make sure the system linker includes
+constructors and destructors. (\c
+.B collect2\c
+\& is included in the GNU CC
+distribution.) For systems which \c
+.I must\c
+\& use \c
+.B collect2\c
+\&, the
+compiler driver \c
+.B gcc\c
+\& is configured to do this automatically.
+.TP
+.B \-finhibit-size-directive
+Don't output a \c
+.B .size\c
+\& assembler directive, or anything else that
+would cause trouble if the function is split in the middle, and the
+two halves are placed at locations far apart in memory. This option is
+used when compiling `\|\c
+.B crtstuff.c\c
+\&\|'; you should not need to use it
+for anything else.
+.TP
+.B \-fverbose-asm
+Put extra commentary information in the generated assembly code to
+make it more readable. This option is generally only of use to those
+who actually need to read the generated assembly code (perhaps while
+debugging the compiler itself).
+.TP
+.B \-fvolatile
+Consider all memory references through pointers to be volatile.
+.TP
+.B \-fvolatile\-global
+Consider all memory references to extern and global data items to
+be volatile.
+.TP
+.B \-fpic
+If supported for the target machines, generate position-independent code,
+suitable for use in a shared library.
+.TP
+.B \-fPIC
+If supported for the target machine, emit position-independent code,
+suitable for dynamic linking, even if branches need large displacements.
+.TP
+.BI "\-ffixed\-" "reg"
+Treat the register named \c
+.I reg\c
+\& as a fixed register; generated code
+should never refer to it (except perhaps as a stack pointer, frame
+pointer or in some other fixed role).
+.Sp
+.I reg\c
+\& must be the name of a register. The register names accepted
+are machine-specific and are defined in the \c
+.B REGISTER_NAMES
+macro in the machine description macro file.
+.Sp
+This flag does not have a negative form, because it specifies a
+three-way choice.
+.TP
+.BI "\-fcall\-used\-" "reg"
+Treat the register named \c
+.I reg\c
+\& as an allocatable register that is
+clobbered by function calls. It may be allocated for temporaries or
+variables that do not live across a call. Functions compiled this way
+will not save and restore the register \c
+.I reg\c
+\&.
+.Sp
+Use of this flag for a register that has a fixed pervasive role in the
+machine's execution model, such as the stack pointer or frame pointer,
+will produce disastrous results.
+.Sp
+This flag does not have a negative form, because it specifies a
+three-way choice.
+.TP
+.BI "\-fcall\-saved\-" "reg"
+Treat the register named \c
+.I reg\c
+\& as an allocatable register saved by
+functions. It may be allocated even for temporaries or variables that
+live across a call. Functions compiled this way will save and restore
+the register \c
+.I reg\c
+\& if they use it.
+.Sp
+Use of this flag for a register that has a fixed pervasive role in the
+machine's execution model, such as the stack pointer or frame pointer,
+will produce disastrous results.
+.Sp
+A different sort of disaster will result from the use of this flag for
+a register in which function values may be returned.
+.Sp
+This flag does not have a negative form, because it specifies a
+three-way choice.
+.SH PRAGMAS
+Two `\|\c
+.B #pragma\c
+\&\|' directives are supported for GNU C++, to permit using the same
+header file for two purposes: as a definition of interfaces to a given
+object class, and as the full definition of the contents of that object class.
+.TP
+.B #pragma interface
+(C++ only.)
+Use this directive in header files that define object classes, to save
+space in most of the object files that use those classes. Normally,
+local copies of certain information (backup copies of inline member
+functions, debugging information, and the internal tables that
+implement virtual functions) must be kept in each object file that
+includes class definitions. You can use this pragma to avoid such
+duplication. When a header file containing `\|\c
+.B #pragma interface\c
+\&\|' is included in a compilation, this auxiliary information
+will not be generated (unless the main input source file itself uses
+`\|\c
+.B #pragma implementation\c
+\&\|'). Instead, the object files will contain references to be
+resolved at link time.
+.TP
+.B #pragma implementation
+.TP
+\fB#pragma implementation "\fP\fIobjects\fP\fB.h"\fP
+(C++ only.)
+Use this pragma in a main input file, when you want full output from
+included header files to be generated (and made globally visible).
+The included header file, in turn, should use `\|\c
+.B #pragma interface\c
+\&\|'.
+Backup copies of inline member functions, debugging information, and
+the internal tables used to implement virtual functions are all
+generated in implementation files.
+.Sp
+If you use `\|\c
+.B #pragma implementation\c
+\&\|' with no argument, it applies to an include file with the same
+basename as your source file; for example, in `\|\c
+.B allclass.cc\c
+\&\|', `\|\c
+.B #pragma implementation\c
+\&\|' by itself is equivalent to `\|\c
+.B
+#pragma implementation "allclass.h"\c
+\&\|'. Use the string argument if you want a single implementation
+file to include code from multiple header files.
+.Sp
+There is no way to split up the contents of a single header file into
+multiple implementation files.
+.SH FILES
+.nf
+.ta \w'LIBDIR/g++\-include 'u
+file.c C source file
+file.h C header (preprocessor) file
+file.i preprocessed C source file
+file.C C++ source file
+file.cc C++ source file
+file.cxx C++ source file
+file.m Objective-C source file
+file.s assembly language file
+file.o object file
+a.out link edited output
+\fITMPDIR\fR/cc\(** temporary files
+\fILIBDIR\fR/cpp preprocessor
+\fILIBDIR\fR/cc1 compiler for C
+\fILIBDIR\fR/cc1plus compiler for C++
+\fILIBDIR\fR/collect linker front end needed on some machines
+\fILIBDIR\fR/libgcc.a GCC subroutine library
+/lib/crt[01n].o start-up routine
+\fILIBDIR\fR/ccrt0 additional start-up routine for C++
+/lib/libc.a standard C library, see
+.IR intro (3)
+/usr/include standard directory for \fB#include\fP files
+\fILIBDIR\fR/include standard gcc directory for \fB#include\fP files
+\fILIBDIR\fR/g++\-include additional g++ directory for \fB#include\fP
+.Sp
+.fi
+.I LIBDIR
+is usually
+.B /usr/local/lib/\c
+.IR machine / version .
+.br
+.I TMPDIR
+comes from the environment variable
+.B TMPDIR
+(default
+.B /usr/tmp
+if available, else
+.B /tmp\c
+\&).
+.SH "SEE ALSO"
+cpp(1), as(1), ld(1), gdb(1), adb(1), dbx(1), sdb(1).
+.br
+.RB "`\|" gcc "\|', `\|" cpp \|',
+.RB "`\|" as "\|', `\|" ld \|',
+and
+.RB `\| gdb \|'
+entries in
+.B info\c
+\&.
+.br
+.I
+Using and Porting GNU CC (for version 2.0)\c
+, Richard M. Stallman;
+.I
+The C Preprocessor\c
+, Richard M. Stallman;
+.I
+Debugging with GDB: the GNU Source-Level Debugger\c
+, Richard M. Stallman and Roland H. Pesch;
+.I
+Using as: the GNU Assembler\c
+, Dean Elsner, Jay Fenlason & friends;
+.I
+ld: the GNU linker\c
+, Steve Chamberlain and Roland Pesch.
+.SH BUGS
+For instructions on reporting bugs, see the GCC manual.
+.SH COPYING
+Copyright
+.if t \(co
+1991, 1992, 1993 Free Software Foundation, Inc.
+.PP
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+.PP
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+.PP
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+.SH AUTHORS
+See the GNU CC Manual for the contributors to GNU CC.
diff --git a/gnu/usr.bin/cc/cc/gcc.c b/gnu/usr.bin/cc/cc/gcc.c
new file mode 100644
index 0000000..4f877ec
--- /dev/null
+++ b/gnu/usr.bin/cc/cc/gcc.c
@@ -0,0 +1,4896 @@
+/* Compiler driver program that can handle many languages.
+ Copyright (C) 1987, 1989, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+This paragraph is here to try to keep Sun CC from dying.
+The number of chars here seems crucial!!!! */
+
+/* This program is the user interface to the C compiler and possibly to
+other compilers. It is used because compilation is a complicated procedure
+which involves running several programs and passing temporary files between
+them, forwarding the users switches to those programs selectively,
+and deleting the temporary files at the end.
+
+CC recognizes how to compile each input file by suffixes in the file names.
+Once it knows which kind of compilation to perform, the procedure for
+compilation is specified by a string called a "spec". */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/file.h> /* May get R_OK, etc. on some systems. */
+
+#include "config.h"
+#include "obstack.h"
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <stdio.h>
+
+/* Include multi-lib information. */
+#include "multilib.h"
+
+#ifndef R_OK
+#define R_OK 4
+#define W_OK 2
+#define X_OK 1
+#endif
+
+/* Add prototype support. */
+#ifndef PROTO
+#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
+#define PROTO(ARGS) ARGS
+#else
+#define PROTO(ARGS) ()
+#endif
+#endif
+
+#ifndef VPROTO
+#ifdef __STDC__
+#define PVPROTO(ARGS) ARGS
+#define VPROTO(ARGS) ARGS
+#define VA_START(va_list,var) va_start(va_list,var)
+#else
+#define PVPROTO(ARGS) ()
+#define VPROTO(ARGS) (va_alist) va_dcl
+#define VA_START(va_list,var) va_start(va_list)
+#endif
+#endif
+
+/* Define a generic NULL if one hasn't already been defined. */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef GENERIC_PTR
+#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
+#define GENERIC_PTR void *
+#else
+#define GENERIC_PTR char *
+#endif
+#endif
+
+#ifndef NULL_PTR
+#define NULL_PTR ((GENERIC_PTR)0)
+#endif
+
+#ifdef USG
+#define vfork fork
+#endif /* USG */
+
+/* On MSDOS, write temp files in current dir
+ because there's no place else we can expect to use. */
+#ifdef __MSDOS__
+#ifndef P_tmpdir
+#define P_tmpdir "."
+#endif
+#endif
+
+/* Test if something is a normal file. */
+#ifndef S_ISREG
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+
+/* Test if something is a directory. */
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+
+/* By default there is no special suffix for executables. */
+#ifndef EXECUTABLE_SUFFIX
+#define EXECUTABLE_SUFFIX ""
+#endif
+
+/* By default, colon separates directories in a path. */
+#ifndef PATH_SEPARATOR
+#define PATH_SEPARATOR ':'
+#endif
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+extern void free ();
+extern char *getenv ();
+
+extern int errno, sys_nerr;
+#if defined(bsd4_4) || defined(__NetBSD__)
+extern const char *const sys_errlist[];
+#else
+extern char *sys_errlist[];
+#endif
+
+extern int execv (), execvp ();
+
+/* If a stage of compilation returns an exit status >= 1,
+ compilation of that file ceases. */
+
+#define MIN_FATAL_STATUS 1
+
+/* Flag saying to print the full filename of this file
+ as found through our usual search mechanism. */
+
+static char *print_file_name = NULL;
+
+/* As print_file_name, but search for executable file. */
+
+static char *print_prog_name = NULL;
+
+/* Flag saying to print the relative path we'd use to
+ find libgcc.a given the current compiler flags. */
+
+static int print_multi_directory;
+
+/* Flag saying to print the list of subdirectories and
+ compiler flags used to select them in a standard form. */
+
+static int print_multi_lib;
+
+/* Flag indicating whether we should print the command and arguments */
+
+static int verbose_flag;
+
+/* Nonzero means write "temp" files in source directory
+ and use the source file's name in them, and don't delete them. */
+
+static int save_temps_flag;
+
+/* The compiler version. */
+
+static char *compiler_version;
+
+/* The target version specified with -V */
+
+static char *spec_version = DEFAULT_TARGET_VERSION;
+
+/* The target machine specified with -b. */
+
+static char *spec_machine = DEFAULT_TARGET_MACHINE;
+
+/* Nonzero if cross-compiling.
+ When -b is used, the value comes from the `specs' file. */
+
+#ifdef CROSS_COMPILE
+static int cross_compile = 1;
+#else
+static int cross_compile = 0;
+#endif
+
+/* The number of errors that have occurred; the link phase will not be
+ run if this is non-zero. */
+static int error_count = 0;
+
+/* This is the obstack which we use to allocate many strings. */
+
+static struct obstack obstack;
+
+/* This is the obstack to build an environment variable to pass to
+ collect2 that describes all of the relevant switches of what to
+ pass the compiler in building the list of pointers to constructors
+ and destructors. */
+
+static struct obstack collect_obstack;
+
+extern char *version_string;
+
+/* Forward declaration for prototypes. */
+struct path_prefix;
+
+static void set_spec PROTO((char *, char *));
+static struct compiler *lookup_compiler PROTO((char *, int, char *));
+static char *find_a_file PROTO((struct path_prefix *, char *, int));
+static void add_prefix PROTO((struct path_prefix *, char *, int, int, int *));
+static char *skip_whitespace PROTO((char *));
+static void record_temp_file PROTO((char *, int, int));
+static void delete_if_ordinary PROTO((char *));
+static void delete_temp_files PROTO((void));
+static void delete_failure_queue PROTO((void));
+static void clear_failure_queue PROTO((void));
+static char *choose_temp_base_try PROTO((char *, char *));
+static void choose_temp_base PROTO((void));
+static int check_live_switch PROTO((int, int));
+static char *handle_braces PROTO((char *));
+static char *save_string PROTO((char *, int));
+static char *concat PROTO((char *, char *, char *));
+static int do_spec PROTO((char *));
+static int do_spec_1 PROTO((char *, int, char *));
+static char *find_file PROTO((char *));
+static int is_directory PROTO((char *, char *, int));
+static void validate_switches PROTO((char *));
+static void validate_all_switches PROTO((void));
+static void give_switch PROTO((int, int));
+static int used_arg PROTO((char *, int));
+static void set_multilib_dir PROTO((void));
+static void print_multilib_info PROTO((void));
+static void pfatal_with_name PROTO((char *));
+static void perror_with_name PROTO((char *));
+static void perror_exec PROTO((char *));
+#ifdef HAVE_VPRINTF
+static void fatal PVPROTO((char *, ...));
+static void error PVPROTO((char *, ...));
+#else
+/* We must not provide any prototype here, even if ANSI C. */
+static void fatal PROTO(());
+static void error PROTO(());
+#endif
+
+void fancy_abort ();
+char *xmalloc ();
+char *xrealloc ();
+
+/* Specs are strings containing lines, each of which (if not blank)
+is made up of a program name, and arguments separated by spaces.
+The program name must be exact and start from root, since no path
+is searched and it is unreliable to depend on the current working directory.
+Redirection of input or output is not supported; the subprograms must
+accept filenames saying what files to read and write.
+
+In addition, the specs can contain %-sequences to substitute variable text
+or for conditional text. Here is a table of all defined %-sequences.
+Note that spaces are not generated automatically around the results of
+expanding these sequences; therefore, you can concatenate them together
+or with constant text in a single argument.
+
+ %% substitute one % into the program name or argument.
+ %i substitute the name of the input file being processed.
+ %b substitute the basename of the input file being processed.
+ This is the substring up to (and not including) the last period
+ and not including the directory.
+ %g substitute the temporary-file-name-base. This is a string chosen
+ once per compilation. Different temporary file names are made by
+ concatenation of constant strings on the end, as in `%g.s'.
+ %g also has the same effect of %d.
+ %u like %g, but make the temporary file name unique.
+ %U returns the last file name generated with %u.
+ %d marks the argument containing or following the %d as a
+ temporary file name, so that that file will be deleted if CC exits
+ successfully. Unlike %g, this contributes no text to the argument.
+ %w marks the argument containing or following the %w as the
+ "output file" of this compilation. This puts the argument
+ into the sequence of arguments that %o will substitute later.
+ %W{...}
+ like %{...} but mark last argument supplied within
+ as a file to be deleted on failure.
+ %o substitutes the names of all the output files, with spaces
+ automatically placed around them. You should write spaces
+ around the %o as well or the results are undefined.
+ %o is for use in the specs for running the linker.
+ Input files whose names have no recognized suffix are not compiled
+ at all, but they are included among the output files, so they will
+ be linked.
+ %p substitutes the standard macro predefinitions for the
+ current target machine. Use this when running cpp.
+ %P like %p, but puts `__' before and after the name of each macro.
+ (Except macros that already have __.)
+ This is for ANSI C.
+ %I Substitute a -iprefix option made from GCC_EXEC_PREFIX.
+ %s current argument is the name of a library or startup file of some sort.
+ Search for that file in a standard list of directories
+ and substitute the full name found.
+ %eSTR Print STR as an error message. STR is terminated by a newline.
+ Use this when inconsistent options are detected.
+ %x{OPTION} Accumulate an option for %X.
+ %X Output the accumulated linker options specified by compilations.
+ %Y Output the accumulated assembler options specified by compilations.
+ %v1 Substitute the major version number of GCC.
+ (For version 2.5.n, this is 2.)
+ %v2 Substitute the minor version number of GCC.
+ (For version 2.5.n, this is 5.)
+ %a process ASM_SPEC as a spec.
+ This allows config.h to specify part of the spec for running as.
+ %A process ASM_FINAL_SPEC as a spec. A capital A is actually
+ used here. This can be used to run a post-processor after the
+ assembler has done it's job.
+ %D Dump out a -L option for each directory in startfile_prefix.
+ If multilib_dir is set, extra entries are generated with it affixed.
+ %l process LINK_SPEC as a spec.
+ %L process LIB_SPEC as a spec.
+ %S process STARTFILE_SPEC as a spec. A capital S is actually used here.
+ %E process ENDFILE_SPEC as a spec. A capital E is actually used here.
+ %c process SIGNED_CHAR_SPEC as a spec.
+ %C process CPP_SPEC as a spec. A capital C is actually used here.
+ %1 process CC1_SPEC as a spec.
+ %2 process CC1PLUS_SPEC as a spec.
+ %| output "-" if the input for the current command is coming from a pipe.
+ %* substitute the variable part of a matched option. (See below.)
+ Note that each comma in the substituted string is replaced by
+ a single space.
+ %{S} substitutes the -S switch, if that switch was given to CC.
+ If that switch was not specified, this substitutes nothing.
+ Here S is a metasyntactic variable.
+ %{S*} substitutes all the switches specified to CC whose names start
+ with -S. This is used for -o, -D, -I, etc; switches that take
+ arguments. CC considers `-o foo' as being one switch whose
+ name starts with `o'. %{o*} would substitute this text,
+ including the space; thus, two arguments would be generated.
+ %{S*:X} substitutes X if one or more switches whose names start with -S are
+ specified to CC. Note that the tail part of the -S option
+ (i.e. the part matched by the `*') will be substituted for each
+ occurrence of %* within X.
+ %{S:X} substitutes X, but only if the -S switch was given to CC.
+ %{!S:X} substitutes X, but only if the -S switch was NOT given to CC.
+ %{|S:X} like %{S:X}, but if no S switch, substitute `-'.
+ %{|!S:X} like %{!S:X}, but if there is an S switch, substitute `-'.
+ %{.S:X} substitutes X, but only if processing a file with suffix S.
+ %{!.S:X} substitutes X, but only if NOT processing a file with suffix S.
+ %(Spec) processes a specification defined in a specs file as *Spec:
+ %[Spec] as above, but put __ around -D arguments
+
+The conditional text X in a %{S:X} or %{!S:X} construct may contain
+other nested % constructs or spaces, or even newlines. They are
+processed as usual, as described above.
+
+The -O, -f, -m, and -W switches are handled specifically in these
+constructs. If another value of -O or the negated form of a -f, -m, or
+-W switch is found later in the command line, the earlier switch
+value is ignored, except with {S*} where S is just one letter; this
+passes all matching options.
+
+The character | is used to indicate that a command should be piped to
+the following command, but only if -pipe is specified.
+
+Note that it is built into CC which switches take arguments and which
+do not. You might think it would be useful to generalize this to
+allow each compiler's spec to say which switches take arguments. But
+this cannot be done in a consistent fashion. CC cannot even decide
+which input files have been specified without knowing which switches
+take arguments, and it must know which input files to compile in order
+to tell which compilers to run.
+
+CC also knows implicitly that arguments starting in `-l' are to be
+treated as compiler output files, and passed to the linker in their
+proper position among the other output files. */
+
+/* Define the macros used for specs %a, %l, %L, %S, %c, %C, %1. */
+
+/* config.h can define ASM_SPEC to provide extra args to the assembler
+ or extra switch-translations. */
+#ifndef ASM_SPEC
+#define ASM_SPEC ""
+#endif
+
+/* config.h can define ASM_FINAL_SPEC to run a post processor after
+ the assembler has run. */
+#ifndef ASM_FINAL_SPEC
+#define ASM_FINAL_SPEC ""
+#endif
+
+/* config.h can define CPP_SPEC to provide extra args to the C preprocessor
+ or extra switch-translations. */
+#ifndef CPP_SPEC
+#define CPP_SPEC ""
+#endif
+
+/* config.h can define CC1_SPEC to provide extra args to cc1 and cc1plus
+ or extra switch-translations. */
+#ifndef CC1_SPEC
+#define CC1_SPEC ""
+#endif
+
+/* config.h can define CC1PLUS_SPEC to provide extra args to cc1plus
+ or extra switch-translations. */
+#ifndef CC1PLUS_SPEC
+#define CC1PLUS_SPEC ""
+#endif
+
+/* config.h can define LINK_SPEC to provide extra args to the linker
+ or extra switch-translations. */
+#ifndef LINK_SPEC
+#define LINK_SPEC ""
+#endif
+
+/* config.h can define LIB_SPEC to override the default libraries. */
+#ifndef LIB_SPEC
+#define LIB_SPEC "%{g*:-lg} %{!p:%{!pg:-lc}}%{p:-lc_p}%{pg:-lc_p}"
+#endif
+
+/* config.h can define STARTFILE_SPEC to override the default crt0 files. */
+#ifndef STARTFILE_SPEC
+#define STARTFILE_SPEC \
+ "%{pg:gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt0.o%s}}"
+#endif
+
+/* config.h can define SWITCHES_NEED_SPACES to control passing -o and -L.
+ Make the string nonempty to require spaces there. */
+#ifndef SWITCHES_NEED_SPACES
+#define SWITCHES_NEED_SPACES ""
+#endif
+
+/* config.h can define ENDFILE_SPEC to override the default crtn files. */
+#ifndef ENDFILE_SPEC
+#define ENDFILE_SPEC ""
+#endif
+
+/* This spec is used for telling cpp whether char is signed or not. */
+#ifndef SIGNED_CHAR_SPEC
+/* Use #if rather than ?:
+ because MIPS C compiler rejects like ?: in initializers. */
+#if DEFAULT_SIGNED_CHAR
+#define SIGNED_CHAR_SPEC "%{funsigned-char:-D__CHAR_UNSIGNED__}"
+#else
+#define SIGNED_CHAR_SPEC "%{!fsigned-char:-D__CHAR_UNSIGNED__}"
+#endif
+#endif
+
+/* MULTILIB_SELECT comes from multilib.h. It gives a
+ string interpreted by set_multilib_dir to select a library
+ subdirectory based on the compiler options. */
+#ifndef MULTILIB_SELECT
+#define MULTILIB_SELECT ". ;"
+#endif
+
+static char *cpp_spec = CPP_SPEC;
+static char *cpp_predefines = CPP_PREDEFINES;
+static char *cc1_spec = CC1_SPEC;
+static char *cc1plus_spec = CC1PLUS_SPEC;
+static char *signed_char_spec = SIGNED_CHAR_SPEC;
+static char *asm_spec = ASM_SPEC;
+static char *asm_final_spec = ASM_FINAL_SPEC;
+static char *link_spec = LINK_SPEC;
+static char *lib_spec = LIB_SPEC;
+static char *endfile_spec = ENDFILE_SPEC;
+static char *startfile_spec = STARTFILE_SPEC;
+static char *switches_need_spaces = SWITCHES_NEED_SPACES;
+static char *multilib_select = MULTILIB_SELECT;
+
+/* This defines which switch letters take arguments. */
+
+#ifndef SWITCH_TAKES_ARG
+#define SWITCH_TAKES_ARG(CHAR) \
+ ((CHAR) == 'D' || (CHAR) == 'U' || (CHAR) == 'o' \
+ || (CHAR) == 'e' || (CHAR) == 'T' || (CHAR) == 'u' \
+ || (CHAR) == 'I' || (CHAR) == 'm' \
+ || (CHAR) == 'L' || (CHAR) == 'A')
+#endif
+
+/* This defines which multi-letter switches take arguments. */
+
+#define DEFAULT_WORD_SWITCH_TAKES_ARG(STR) \
+ (!strcmp (STR, "Tdata") || !strcmp (STR, "Ttext") \
+ || !strcmp (STR, "Tbss") || !strcmp (STR, "include") \
+ || !strcmp (STR, "imacros") || !strcmp (STR, "aux-info") \
+ || !strcmp (STR, "idirafter") || !strcmp (STR, "iprefix") \
+ || !strcmp (STR, "iwithprefix") || !strcmp (STR, "iwithprefixbefore") \
+ || !strcmp (STR, "isystem"))
+
+#ifndef WORD_SWITCH_TAKES_ARG
+#define WORD_SWITCH_TAKES_ARG(STR) DEFAULT_WORD_SWITCH_TAKES_ARG (STR)
+#endif
+
+/* Record the mapping from file suffixes for compilation specs. */
+
+struct compiler
+{
+ char *suffix; /* Use this compiler for input files
+ whose names end in this suffix. */
+
+ char *spec[4]; /* To use this compiler, concatenate these
+ specs and pass to do_spec. */
+};
+
+/* Pointer to a vector of `struct compiler' that gives the spec for
+ compiling a file, based on its suffix.
+ A file that does not end in any of these suffixes will be passed
+ unchanged to the loader and nothing else will be done to it.
+
+ An entry containing two 0s is used to terminate the vector.
+
+ If multiple entries match a file, the last matching one is used. */
+
+static struct compiler *compilers;
+
+/* Number of entries in `compilers', not counting the null terminator. */
+
+static int n_compilers;
+
+/* The default list of file name suffixes and their compilation specs. */
+
+static struct compiler default_compilers[] =
+{
+ {".c", "@c"},
+ {"@c",
+ "cpp -lang-c %{nostdinc*} %{C} %{v} %{A*} %{I*} %{P} %I\
+ %{C:%{!E:%eGNU C does not support -C without using -E}}\
+ %{M} %{MM} %{MD:-MD %b.d} %{MMD:-MMD %b.d} %{MG}\
+ -undef -D__GNUC__=%v1 -D__GNUC_MINOR__=%v2\
+ %{ansi:-trigraphs -$ -D__STRICT_ANSI__}\
+ %{!undef:%{!ansi:%p} %P} %{trigraphs} \
+ %c %{O*:%{!O0:-D__OPTIMIZE__}} %{traditional} %{ftraditional:-traditional}\
+ %{traditional-cpp:-traditional}\
+ %{g*} %{W*} %{w} %{pedantic*} %{H} %{d*} %C %{D*} %{U*} %{i*}\
+ %i %{!M:%{!MM:%{!E:%{!pipe:%g.i}}}}%{E:%W{o*}}%{M:%W{o*}}%{MM:%W{o*}} |\n",
+ "%{!M:%{!MM:%{!E:cc1 %{!pipe:%g.i} %1 \
+ %{!Q:-quiet} -dumpbase %b.c %{d*} %{m*} %{a}\
+ %{g*} %{O*} %{W*} %{w} %{pedantic*} %{ansi} \
+ %{traditional} %{v:-version} %{pg:-p} %{p} %{f*}\
+ %{aux-info*}\
+ %{pg:%{fomit-frame-pointer:%e-pg and -fomit-frame-pointer are incompatible}}\
+ %{S:%W{o*}%{!o*:-o %b.s}}%{!S:-o %{|!pipe:%g.s}} |\n\
+ %{!S:as %{R} %{j} %{J} %{h} %{d2} %a %Y\
+ %{c:%W{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%u.o}\
+ %{!pipe:%g.s} %A\n }}}}"},
+ {"-",
+ "%{E:cpp -lang-c %{nostdinc*} %{C} %{v} %{A*} %{I*} %{P} %I\
+ %{C:%{!E:%eGNU C does not support -C without using -E}}\
+ %{M} %{MM} %{MD:-MD %b.d} %{MMD:-MMD %b.d} %{MG}\
+ -undef -D__GNUC__=%v1 -D__GNUC_MINOR__=%v2\
+ %{ansi:-trigraphs -$ -D__STRICT_ANSI__}\
+ %{!undef:%{!ansi:%p} %P} %{trigraphs}\
+ %c %{O*:%{!O0:-D__OPTIMIZE__}} %{traditional} %{ftraditional:-traditional}\
+ %{traditional-cpp:-traditional}\
+ %{g*} %{W*} %{w} %{pedantic*} %{H} %{d*} %C %{D*} %{U*} %{i*}\
+ %i %W{o*}}\
+ %{!E:%e-E required when input is from standard input}"},
+ {".m", "@objective-c"},
+ {"@objective-c",
+ "cpp -lang-objc %{nostdinc*} %{C} %{v} %{A*} %{I*} %{P} %I\
+ %{C:%{!E:%eGNU C does not support -C without using -E}}\
+ %{M} %{MM} %{MD:-MD %b.d} %{MMD:-MMD %b.d} %{MG}\
+ -undef -D__OBJC__ -D__GNUC__=%v1 -D__GNUC_MINOR__=%v2\
+ %{ansi:-trigraphs -$ -D__STRICT_ANSI__}\
+ %{!undef:%{!ansi:%p} %P} %{trigraphs}\
+ %c %{O*:%{!O0:-D__OPTIMIZE__}} %{traditional} %{ftraditional:-traditional}\
+ %{traditional-cpp:-traditional}\
+ %{g*} %{W*} %{w} %{pedantic*} %{H} %{d*} %C %{D*} %{U*} %{i*}\
+ %i %{!M:%{!MM:%{!E:%{!pipe:%g.i}}}}%{E:%W{o*}}%{M:%W{o*}}%{MM:%W{o*}} |\n",
+ "%{!M:%{!MM:%{!E:cc1obj %{!pipe:%g.i} %1 \
+ %{!Q:-quiet} -dumpbase %b.m %{d*} %{m*} %{a}\
+ %{g*} %{O*} %{W*} %{w} %{pedantic*} %{ansi} \
+ %{traditional} %{v:-version} %{pg:-p} %{p} %{f*} \
+ -lang-objc %{gen-decls} \
+ %{aux-info*}\
+ %{pg:%{fomit-frame-pointer:%e-pg and -fomit-frame-pointer are incompatible}}\
+ %{S:%W{o*}%{!o*:-o %b.s}}%{!S:-o %{|!pipe:%g.s}} |\n\
+ %{!S:as %{R} %{j} %{J} %{h} %{d2} %a %Y\
+ %{c:%W{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%u.o}\
+ %{!pipe:%g.s} %A\n }}}}"},
+ {".h", "@c-header"},
+ {"@c-header",
+ "%{!E:%eCompilation of header file requested} \
+ cpp %{nostdinc*} %{C} %{v} %{A*} %{I*} %{P} %I\
+ %{C:%{!E:%eGNU C does not support -C without using -E}}\
+ %{M} %{MM} %{MD:-MD %b.d} %{MMD:-MMD %b.d} %{MG}\
+ -undef -D__GNUC__=%v1 -D__GNUC_MINOR__=%v2\
+ %{ansi:-trigraphs -$ -D__STRICT_ANSI__}\
+ %{!undef:%{!ansi:%p} %P} %{trigraphs}\
+ %c %{O*:%{!O0:-D__OPTIMIZE__}} %{traditional} %{ftraditional:-traditional}\
+ %{traditional-cpp:-traditional}\
+ %{g*} %{W*} %{w} %{pedantic*} %{H} %{d*} %C %{D*} %{U*} %{i*}\
+ %i %W{o*}"},
+ {".cc", "@c++"},
+ {".cxx", "@c++"},
+ {".cpp", "@c++"},
+ {".C", "@c++"},
+ {"@c++",
+ "cpp -lang-c++ %{nostdinc*} %{C} %{v} %{A*} %{I*} %{P} %I\
+ %{C:%{!E:%eGNU C++ does not support -C without using -E}}\
+ %{M} %{MM} %{MD:-MD %b.d} %{MMD:-MMD %b.d} %{MG}\
+ -undef -D__GNUC__=%v1 -D__GNUG__=%v1 -D__cplusplus -D__GNUC_MINOR__=%v2\
+ %{ansi:-trigraphs -$ -D__STRICT_ANSI__} %{!undef:%{!ansi:%p} %P}\
+ %c %{O*:%{!O0:-D__OPTIMIZE__}} %{traditional} %{ftraditional:-traditional}\
+ %{traditional-cpp:-traditional} %{trigraphs}\
+ %{g*} %{W*} %{w} %{pedantic*} %{H} %{d*} %C %{D*} %{U*} %{i*}\
+ %i %{!M:%{!MM:%{!E:%{!pipe:%g.ii}}}}%{E:%W{o*}}%{M:%W{o*}}%{MM:%W{o*}} |\n",
+ "%{!M:%{!MM:%{!E:cc1plus %{!pipe:%g.ii} %1 %2\
+ %{!Q:-quiet} -dumpbase %b.cc %{d*} %{m*} %{a}\
+ %{g*} %{O*} %{W*} %{w} %{pedantic*} %{ansi}\
+ %{traditional} %{v:-version} %{pg:-p} %{p}\
+ %{f*} %{+e*} %{aux-info*}\
+ %{pg:%{fomit-frame-pointer:%e-pg and -fomit-frame-pointer are incompatible}}\
+ %{S:%W{o*}%{!o*:-o %b.s}}%{!S:-o %{|!pipe:%g.s}}|\n\
+ %{!S:as %{R} %{j} %{J} %{h} %{d2} %a %Y\
+ %{c:%W{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%u.o}\
+ %{!pipe:%g.s} %A\n }}}}"},
+ {".i", "@cpp-output"},
+ {"@cpp-output",
+ "%{!M:%{!MM:%{!E:cc1 %i %1 %{!Q:-quiet} %{d*} %{m*} %{a}\
+ %{g*} %{O*} %{W*} %{w} %{pedantic*} %{ansi}\
+ %{traditional} %{v:-version} %{pg:-p} %{p} %{f*}\
+ %{aux-info*}\
+ %{pg:%{fomit-frame-pointer:%e-pg and -fomit-frame-pointer are incompatible}}\
+ %{S:%W{o*}%{!o*:-o %b.s}}%{!S:-o %{|!pipe:%g.s}} |\n\
+ %{!S:as %{R} %{j} %{J} %{h} %{d2} %a %Y\
+ %{c:%W{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%u.o}\
+ %{!pipe:%g.s} %A\n }}}}"},
+ {".ii", "@c++-cpp-output"},
+ {"@c++-cpp-output",
+ "%{!M:%{!MM:%{!E:cc1plus %i %1 %2 %{!Q:-quiet} %{d*} %{m*} %{a}\
+ %{g*} %{O*} %{W*} %{w} %{pedantic*} %{ansi}\
+ %{traditional} %{v:-version} %{pg:-p} %{p}\
+ %{f*} %{+e*} %{aux-info*}\
+ %{pg:%{fomit-frame-pointer:%e-pg and -fomit-frame-pointer are incompatible}}\
+ %{S:%W{o*}%{!o*:-o %b.s}}%{!S:-o %{|!pipe:%g.s}} |\n\
+ %{!S:as %{R} %{j} %{J} %{h} %{d2} %a %Y\
+ %{c:%W{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%u.o}\
+ %{!pipe:%g.s} %A\n }}}}"},
+ {".s", "@assembler"},
+ {"@assembler",
+ "%{!M:%{!MM:%{!E:%{!S:as %{R} %{j} %{J} %{h} %{d2} %a %Y\
+ %{c:%W{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%u.o}\
+ %i %A\n }}}}"},
+ {".S", "@assembler-with-cpp"},
+ {"@assembler-with-cpp",
+ "cpp -lang-asm %{nostdinc*} %{C} %{v} %{A*} %{I*} %{P} %I\
+ %{C:%{!E:%eGNU C does not support -C without using -E}}\
+ %{M} %{MM} %{MD:-MD %b.d} %{MMD:-MMD %b.d} %{MG} %{trigraphs}\
+ -undef -$ %{!undef:%p %P} -D__ASSEMBLER__ \
+ %c %{O*:%{!O0:-D__OPTIMIZE__}} %{traditional} %{ftraditional:-traditional}\
+ %{traditional-cpp:-traditional}\
+ %{g*} %{W*} %{w} %{pedantic*} %{H} %{d*} %C %{D*} %{U*} %{i*}\
+ %i %{!M:%{!MM:%{!E:%{!pipe:%g.s}}}}%{E:%W{o*}}%{M:%W{o*}}%{MM:%W{o*}} |\n",
+ "%{!M:%{!MM:%{!E:%{!S:as %{R} %{j} %{J} %{h} %{d2} %a %Y\
+ %{c:%W{o*}%{!o*:-o %w%b.o}}%{!c:-o %d%w%u.o}\
+ %{!pipe:%g.s} %A\n }}}}"},
+ {".ads", "@ada"},
+ {".adb", "@ada"},
+ {".ada", "@ada"},
+ {"@ada",
+ "%{!M:%{!MM:%{!E:gnat1 %{k8:-gnatk8} %{w:-gnatws} %{!Q:-quiet}\
+ -dumpbase %b.ada %{g*} %{O*} %{p} %{pg:-p} %{f*}\
+ %{d*}\
+ %{pg:%{fomit-frame-pointer:%e-pg and -fomit-frame-pointer are incompatible}}\
+ %i %{S:%W{o*}%{!o*:-o %b.s}}%{!S:-o %{|!pipe:%g.s}} |\n\
+ %{!S:%{!gnatc:%{!gnats:as %{R} %{j} %{J} %{h} %{d2} %a %Y\
+ %{c:%W{o*}%{!o*:-o %w%b.o}}\
+ %{!c:-o %d%w%u.o} %{!pipe:%g.s} %A\n}}}}}} "},
+ /* Mark end of table */
+ {0, 0}
+};
+
+/* Number of elements in default_compilers, not counting the terminator. */
+
+static int n_default_compilers
+ = (sizeof default_compilers / sizeof (struct compiler)) - 1;
+
+/* Here is the spec for running the linker, after compiling all files. */
+
+/* -u* was put back because both BSD and SysV seem to support it. */
+/* %{static:} simply prevents an error message if the target machine
+ doesn't handle -static. */
+/* We want %{T*} after %{L*} and %D so that it can be used to specify linker
+ scripts which exist in user specified directories, or in standard
+ directories. */
+#ifdef LINK_LIBGCC_SPECIAL_1
+/* Have gcc do the search for libgcc.a, but generate -L options as usual. */
+static char *link_command_spec = "\
+%{!fsyntax-only: \
+ %{!c:%{!M:%{!MM:%{!E:%{!S:ld %l %X %{o*} %{A} %{d} %{e*} %{m} %{N} %{n} \
+ %{r} %{s} %{t} %{u*} %{x} %{z} %{Z}\
+ %{!A:%{!nostartfiles:%{!nostdlib:%S}}} %{static:}\
+ %{L*} %D %{T*} %o %{!nostdlib:libgcc.a%s %L libgcc.a%s %{!A:%E}}\n }}}}}}";
+#else
+#ifdef LINK_LIBGCC_SPECIAL
+/* Have gcc do the search for libgcc.a, and don't generate -L options. */
+static char *link_command_spec = "\
+%{!fsyntax-only: \
+ %{!c:%{!M:%{!MM:%{!E:%{!S:ld %l %X %{o*} %{A} %{d} %{e*} %{m} %{N} %{n} \
+ %{r} %{s} %{t} %{u*} %{x} %{z} %{Z}\
+ %{!A:%{!nostartfiles:%{!nostdlib:%S}}} %{static:}\
+ %{L*} %{T*} %o %{!nostdlib:libgcc.a%s %L libgcc.a%s %{!A:%E}}\n }}}}}}";
+#else
+/* Use -L and have the linker do the search for -lgcc. */
+static char *link_command_spec = "\
+%{!fsyntax-only: \
+ %{!c:%{!M:%{!MM:%{!E:%{!S:ld %l %X %{o*} %{A} %{d} %{e*} %{m} %{N} %{n} \
+ %{r} %{s} %{t} %{u*} %{x} %{z} %{Z}\
+ %{!A:%{!nostartfiles:%{!nostdlib:%S}}} %{static:}\
+ %{L*} %D %{T*} %o %{!nostdlib:-lgcc %L -lgcc %{!A:%E}}\n }}}}}}";
+#endif
+#endif
+
+/* A vector of options to give to the linker.
+ These options are accumulated by -Xlinker and -Wl,
+ and substituted into the linker command with %X. */
+static int n_linker_options;
+static char **linker_options;
+
+/* A vector of options to give to the assembler.
+ These options are accumulated by -Wa,
+ and substituted into the assembler command with %X. */
+static int n_assembler_options;
+static char **assembler_options;
+
+/* Define how to map long options into short ones. */
+
+/* This structure describes one mapping. */
+struct option_map
+{
+ /* The long option's name. */
+ char *name;
+ /* The equivalent short option. */
+ char *equivalent;
+ /* Argument info. A string of flag chars; NULL equals no options.
+ a => argument required.
+ o => argument optional.
+ j => join argument to equivalent, making one word.
+ * => allow other text after NAME as an argument. */
+ char *arg_info;
+};
+
+/* This is the table of mappings. Mappings are tried sequentially
+ for each option encountered; the first one that matches, wins. */
+
+struct option_map option_map[] =
+ {
+ {"--profile-blocks", "-a", 0},
+ {"--target", "-b", "a"},
+ {"--compile", "-c", 0},
+ {"--dump", "-d", "a"},
+ {"--entry", "-e", 0},
+ {"--debug", "-g", "oj"},
+ {"--include", "-include", "a"},
+ {"--imacros", "-imacros", "a"},
+ {"--include-prefix", "-iprefix", "a"},
+ {"--include-directory-after", "-idirafter", "a"},
+ {"--include-with-prefix", "-iwithprefix", "a"},
+ {"--include-with-prefix-before", "-iwithprefixbefore", "a"},
+ {"--include-with-prefix-after", "-iwithprefix", "a"},
+ {"--machine-", "-m", "*j"},
+ {"--machine", "-m", "aj"},
+ {"--no-standard-includes", "-nostdinc", 0},
+ {"--no-standard-libraries", "-nostdlib", 0},
+ {"--no-precompiled-includes", "-noprecomp", 0},
+ {"--output", "-o", "a"},
+ {"--profile", "-p", 0},
+ {"--quiet", "-q", 0},
+ {"--silent", "-q", 0},
+ {"--force-link", "-u", "a"},
+ {"--verbose", "-v", 0},
+ {"--version", "-dumpversion", 0},
+ {"--no-warnings", "-w", 0},
+ {"--language", "-x", "a"},
+
+ {"--assert", "-A", "a"},
+ {"--prefix", "-B", "a"},
+ {"--comments", "-C", 0},
+ {"--define-macro", "-D", "a"},
+ {"--preprocess", "-E", 0},
+ {"--trace-includes", "-H", 0},
+ {"--include-directory", "-I", "a"},
+ {"--include-barrier", "-I-", 0},
+ {"--library-directory", "-L", "a"},
+ {"--dependencies", "-M", 0},
+ {"--user-dependencies", "-MM", 0},
+ {"--write-dependencies", "-MD", 0},
+ {"--write-user-dependencies", "-MMD", 0},
+ {"--print-missing-file-dependencies", "-MG", 0},
+ {"--optimize", "-O", "oj"},
+ {"--no-line-commands", "-P", 0},
+ {"--assemble", "-S", 0},
+ {"--undefine-macro", "-U", "a"},
+ {"--use-version", "-V", "a"},
+ {"--for-assembler", "-Wa", "a"},
+ {"--extra-warnings", "-W", 0},
+ {"--all-warnings", "-Wall", 0},
+ {"--warn-", "-W", "*j"},
+ {"--for-linker", "-Xlinker", "a"},
+
+ {"--ansi", "-ansi", 0},
+ {"--traditional", "-traditional", 0},
+ {"--traditional-cpp", "-traditional-cpp", 0},
+ {"--trigraphs", "-trigraphs", 0},
+ {"--pipe", "-pipe", 0},
+ {"--dumpbase", "-dumpbase", "a"},
+ {"--pedantic", "-pedantic", 0},
+ {"--pedantic-errors", "-pedantic-errors", 0},
+ {"--save-temps", "-save-temps", 0},
+ {"--print-libgcc-file-name", "-print-libgcc-file-name", 0},
+ {"--print-file-name", "-print-file-name=", "aj"},
+ {"--print-prog-name", "-print-prog-name=", "aj"},
+ {"--print-multi-lib", "-print-multi-lib", 0},
+ {"--print-multi-directory", "-print-multi-directory", 0},
+ {"--static", "-static", 0},
+ {"--shared", "-shared", 0},
+ {"--symbolic", "-symbolic", 0},
+ {"--", "-f", "*j"}
+ };
+
+/* Translate the options described by *ARGCP and *ARGVP.
+ Make a new vector and store it back in *ARGVP,
+ and store its length in *ARGVC. */
+
+static void
+translate_options (argcp, argvp)
+ int *argcp;
+ char ***argvp;
+{
+ int i, j;
+ int argc = *argcp;
+ char **argv = *argvp;
+ char **newv = (char **) xmalloc ((argc + 2) * 2 * sizeof (char *));
+ int newindex = 0;
+
+ i = 0;
+ newv[newindex++] = argv[i++];
+
+ while (i < argc)
+ {
+ /* Translate -- options. */
+ if (argv[i][0] == '-' && argv[i][1] == '-')
+ {
+ /* Find a mapping that applies to this option. */
+ for (j = 0; j < sizeof (option_map) / sizeof (option_map[0]); j++)
+ {
+ int optlen = strlen (option_map[j].name);
+ int complen = strlen (argv[i]);
+ char *arginfo = option_map[j].arg_info;
+
+ if (arginfo == 0)
+ arginfo = "";
+ if (complen > optlen)
+ complen = optlen;
+ if (!strncmp (argv[i], option_map[j].name, complen))
+ {
+ int extra = strlen (argv[i]) > optlen;
+ char *arg = 0;
+
+ if (extra)
+ {
+ /* If the option has an argument, accept that. */
+ if (argv[i][optlen] == '=')
+ arg = argv[i] + optlen + 1;
+ /* If this mapping allows extra text at end of name,
+ accept that as "argument". */
+ else if (index (arginfo, '*') != 0)
+ arg = argv[i] + optlen;
+ /* Otherwise, extra text at end means mismatch.
+ Try other mappings. */
+ else
+ continue;
+ }
+ else if (index (arginfo, '*') != 0)
+ error ("Incomplete `%s' option", option_map[j].name);
+
+ /* Handle arguments. */
+ if (index (arginfo, 'o') != 0)
+ {
+ if (arg == 0)
+ {
+ if (i + 1 == argc)
+ error ("Missing argument to `%s' option",
+ option_map[j].name);
+ arg = argv[++i];
+ }
+ }
+ else if (index (arginfo, '*') != 0)
+ ;
+ else if (index (arginfo, 'a') == 0)
+ {
+ if (arg != 0)
+ error ("Extraneous argument to `%s' option",
+ option_map[j].name);
+ arg = 0;
+ }
+
+ /* Store the translation as one argv elt or as two. */
+ if (arg != 0 && index (arginfo, 'j') != 0)
+ newv[newindex++] = concat (option_map[j].equivalent,
+ arg, "");
+ else if (arg != 0)
+ {
+ newv[newindex++] = option_map[j].equivalent;
+ newv[newindex++] = arg;
+ }
+ else
+ newv[newindex++] = option_map[j].equivalent;
+
+ break;
+ }
+ }
+ i++;
+ }
+ /* Handle old-fashioned options--just copy them through,
+ with their arguments. */
+ else if (argv[i][0] == '-')
+ {
+ char *p = argv[i] + 1;
+ int c = *p;
+ int nskip = 1;
+
+ if (SWITCH_TAKES_ARG (c) > (p[1] != 0))
+ nskip += SWITCH_TAKES_ARG (c) - (p[1] != 0);
+ else if (WORD_SWITCH_TAKES_ARG (p))
+ nskip += WORD_SWITCH_TAKES_ARG (p);
+ else if ((c == 'B' || c == 'b' || c == 'V' || c == 'x')
+ && p[1] == 0)
+ nskip += 1;
+ else if (! strcmp (p, "Xlinker"))
+ nskip += 1;
+
+ /* Watch out for an option at the end of the command line that
+ is missing arguments, and avoid skipping past the end of the
+ command line. */
+ if (nskip + i > argc)
+ nskip = argc - i;
+
+ while (nskip > 0)
+ {
+ newv[newindex++] = argv[i++];
+ nskip--;
+ }
+ }
+ else
+ /* Ordinary operands, or +e options. */
+ newv[newindex++] = argv[i++];
+ }
+
+ newv[newindex] = 0;
+
+ *argvp = newv;
+ *argcp = newindex;
+}
+
+/* Read compilation specs from a file named FILENAME,
+ replacing the default ones.
+
+ A suffix which starts with `*' is a definition for
+ one of the machine-specific sub-specs. The "suffix" should be
+ *asm, *cc1, *cpp, *link, *startfile, *signed_char, etc.
+ The corresponding spec is stored in asm_spec, etc.,
+ rather than in the `compilers' vector.
+
+ Anything invalid in the file is a fatal error. */
+
+static void
+read_specs (filename)
+ char *filename;
+{
+ int desc;
+ struct stat statbuf;
+ char *buffer;
+ register char *p;
+
+ if (verbose_flag)
+ fprintf (stderr, "Reading specs from %s\n", filename);
+
+ /* Open and stat the file. */
+ desc = open (filename, 0, 0);
+ if (desc < 0)
+ pfatal_with_name (filename);
+ if (stat (filename, &statbuf) < 0)
+ pfatal_with_name (filename);
+
+ /* Read contents of file into BUFFER. */
+ buffer = xmalloc ((unsigned) statbuf.st_size + 1);
+ read (desc, buffer, (unsigned) statbuf.st_size);
+ buffer[statbuf.st_size] = 0;
+ close (desc);
+
+ /* Scan BUFFER for specs, putting them in the vector. */
+ p = buffer;
+ while (1)
+ {
+ char *suffix;
+ char *spec;
+ char *in, *out, *p1, *p2;
+
+ /* Advance P in BUFFER to the next nonblank nocomment line. */
+ p = skip_whitespace (p);
+ if (*p == 0)
+ break;
+
+ /* Find the colon that should end the suffix. */
+ p1 = p;
+ while (*p1 && *p1 != ':' && *p1 != '\n') p1++;
+ /* The colon shouldn't be missing. */
+ if (*p1 != ':')
+ fatal ("specs file malformed after %d characters", p1 - buffer);
+ /* Skip back over trailing whitespace. */
+ p2 = p1;
+ while (p2 > buffer && (p2[-1] == ' ' || p2[-1] == '\t')) p2--;
+ /* Copy the suffix to a string. */
+ suffix = save_string (p, p2 - p);
+ /* Find the next line. */
+ p = skip_whitespace (p1 + 1);
+ if (p[1] == 0)
+ fatal ("specs file malformed after %d characters", p - buffer);
+ p1 = p;
+ /* Find next blank line. */
+ while (*p1 && !(*p1 == '\n' && p1[1] == '\n')) p1++;
+ /* Specs end at the blank line and do not include the newline. */
+ spec = save_string (p, p1 - p);
+ p = p1;
+
+ /* Delete backslash-newline sequences from the spec. */
+ in = spec;
+ out = spec;
+ while (*in != 0)
+ {
+ if (in[0] == '\\' && in[1] == '\n')
+ in += 2;
+ else if (in[0] == '#')
+ {
+ while (*in && *in != '\n') in++;
+ }
+ else
+ *out++ = *in++;
+ }
+ *out = 0;
+
+ if (suffix[0] == '*')
+ {
+ if (! strcmp (suffix, "*link_command"))
+ link_command_spec = spec;
+ else
+ set_spec (suffix + 1, spec);
+ }
+ else
+ {
+ /* Add this pair to the vector. */
+ compilers
+ = ((struct compiler *)
+ xrealloc (compilers, (n_compilers + 2) * sizeof (struct compiler)));
+ compilers[n_compilers].suffix = suffix;
+ bzero ((char *) compilers[n_compilers].spec,
+ sizeof compilers[n_compilers].spec);
+ compilers[n_compilers].spec[0] = spec;
+ n_compilers++;
+ bzero ((char *) &compilers[n_compilers],
+ sizeof compilers[n_compilers]);
+ }
+
+ if (*suffix == 0)
+ link_command_spec = spec;
+ }
+
+ if (link_command_spec == 0)
+ fatal ("spec file has no spec for linking");
+}
+
+static char *
+skip_whitespace (p)
+ char *p;
+{
+ while (1)
+ {
+ /* A fully-blank line is a delimiter in the SPEC file and shouldn't
+ be considered whitespace. */
+ if (p[0] == '\n' && p[1] == '\n' && p[2] == '\n')
+ return p + 1;
+ else if (*p == '\n' || *p == ' ' || *p == '\t')
+ p++;
+ else if (*p == '#')
+ {
+ while (*p != '\n') p++;
+ p++;
+ }
+ else
+ break;
+ }
+
+ return p;
+}
+
+/* Structure to keep track of the specs that have been defined so far. These
+ are accessed using %(specname) or %[specname] in a compiler or link spec. */
+
+struct spec_list
+{
+ char *name; /* Name of the spec. */
+ char *spec; /* The spec itself. */
+ struct spec_list *next; /* Next spec in linked list. */
+};
+
+/* List of specs that have been defined so far. */
+
+static struct spec_list *specs = (struct spec_list *) 0;
+
+/* Change the value of spec NAME to SPEC. If SPEC is empty, then the spec is
+ removed; If the spec starts with a + then SPEC is added to the end of the
+ current spec. */
+
+static void
+set_spec (name, spec)
+ char *name;
+ char *spec;
+{
+ struct spec_list *sl;
+ char *old_spec;
+
+ /* See if the spec already exists */
+ for (sl = specs; sl; sl = sl->next)
+ if (strcmp (sl->name, name) == 0)
+ break;
+
+ if (!sl)
+ {
+ /* Not found - make it */
+ sl = (struct spec_list *) xmalloc (sizeof (struct spec_list));
+ sl->name = save_string (name, strlen (name));
+ sl->spec = save_string ("", 0);
+ sl->next = specs;
+ specs = sl;
+ }
+
+ old_spec = sl->spec;
+ if (name && spec[0] == '+' && isspace (spec[1]))
+ sl->spec = concat (old_spec, spec + 1, "");
+ else
+ sl->spec = save_string (spec, strlen (spec));
+
+ if (! strcmp (name, "asm"))
+ asm_spec = sl->spec;
+ else if (! strcmp (name, "asm_final"))
+ asm_final_spec = sl->spec;
+ else if (! strcmp (name, "cc1"))
+ cc1_spec = sl->spec;
+ else if (! strcmp (name, "cc1plus"))
+ cc1plus_spec = sl->spec;
+ else if (! strcmp (name, "cpp"))
+ cpp_spec = sl->spec;
+ else if (! strcmp (name, "endfile"))
+ endfile_spec = sl->spec;
+ else if (! strcmp (name, "lib"))
+ lib_spec = sl->spec;
+ else if (! strcmp (name, "link"))
+ link_spec = sl->spec;
+ else if (! strcmp (name, "predefines"))
+ cpp_predefines = sl->spec;
+ else if (! strcmp (name, "signed_char"))
+ signed_char_spec = sl->spec;
+ else if (! strcmp (name, "startfile"))
+ startfile_spec = sl->spec;
+ else if (! strcmp (name, "switches_need_spaces"))
+ switches_need_spaces = sl->spec;
+ else if (! strcmp (name, "cross_compile"))
+ cross_compile = atoi (sl->spec);
+ else if (! strcmp (name, "multilib"))
+ multilib_select = sl->spec;
+ /* Free the old spec */
+ if (old_spec)
+ free (old_spec);
+}
+
+/* Accumulate a command (program name and args), and run it. */
+
+/* Vector of pointers to arguments in the current line of specifications. */
+
+static char **argbuf;
+
+/* Number of elements allocated in argbuf. */
+
+static int argbuf_length;
+
+/* Number of elements in argbuf currently in use (containing args). */
+
+static int argbuf_index;
+
+/* This is the list of suffixes and codes (%g/%u/%U) and the associated
+ temp file. Used only if MKTEMP_EACH_FILE. */
+
+static struct temp_name {
+ char *suffix; /* suffix associated with the code. */
+ int length; /* strlen (suffix). */
+ int unique; /* Indicates whether %g or %u/%U was used. */
+ char *filename; /* associated filename. */
+ int filename_length; /* strlen (filename). */
+ struct temp_name *next;
+} *temp_names;
+
+/* Number of commands executed so far. */
+
+static int execution_count;
+
+/* Number of commands that exited with a signal. */
+
+static int signal_count;
+
+/* Name with which this program was invoked. */
+
+static char *programname;
+
+/* Structures to keep track of prefixes to try when looking for files. */
+
+struct prefix_list
+{
+ char *prefix; /* String to prepend to the path. */
+ struct prefix_list *next; /* Next in linked list. */
+ int require_machine_suffix; /* Don't use without machine_suffix. */
+ /* 2 means try both machine_suffix and just_machine_suffix. */
+ int *used_flag_ptr; /* 1 if a file was found with this prefix. */
+};
+
+struct path_prefix
+{
+ struct prefix_list *plist; /* List of prefixes to try */
+ int max_len; /* Max length of a prefix in PLIST */
+ char *name; /* Name of this list (used in config stuff) */
+};
+
+/* List of prefixes to try when looking for executables. */
+
+static struct path_prefix exec_prefix = { 0, 0, "exec" };
+
+/* List of prefixes to try when looking for startup (crt0) files. */
+
+static struct path_prefix startfile_prefix = { 0, 0, "startfile" };
+
+/* List of prefixes to try when looking for include files. */
+
+static struct path_prefix include_prefix = { 0, 0, "include" };
+
+/* Suffix to attach to directories searched for commands.
+ This looks like `MACHINE/VERSION/'. */
+
+static char *machine_suffix = 0;
+
+/* Suffix to attach to directories searched for commands.
+ This is just `MACHINE/'. */
+
+static char *just_machine_suffix = 0;
+
+/* Adjusted value of GCC_EXEC_PREFIX envvar. */
+
+static char *gcc_exec_prefix;
+
+/* Default prefixes to attach to command names. */
+
+#ifdef CROSS_COMPILE /* Don't use these prefixes for a cross compiler. */
+#undef MD_EXEC_PREFIX
+#undef MD_STARTFILE_PREFIX
+#undef MD_STARTFILE_PREFIX_1
+#endif
+
+#ifndef STANDARD_EXEC_PREFIX
+#define STANDARD_EXEC_PREFIX "/usr/local/lib/gcc-lib/"
+#endif /* !defined STANDARD_EXEC_PREFIX */
+
+static char *standard_exec_prefix = STANDARD_EXEC_PREFIX;
+static char *standard_exec_prefix_1 = "/usr/lib/gcc/";
+#ifdef MD_EXEC_PREFIX
+static char *md_exec_prefix = MD_EXEC_PREFIX;
+#endif
+
+#ifndef STANDARD_STARTFILE_PREFIX
+#define STANDARD_STARTFILE_PREFIX "/usr/local/lib/"
+#endif /* !defined STANDARD_STARTFILE_PREFIX */
+
+#ifdef MD_STARTFILE_PREFIX
+static char *md_startfile_prefix = MD_STARTFILE_PREFIX;
+#endif
+#ifdef MD_STARTFILE_PREFIX_1
+static char *md_startfile_prefix_1 = MD_STARTFILE_PREFIX_1;
+#endif
+static char *standard_startfile_prefix = STANDARD_STARTFILE_PREFIX;
+static char *standard_startfile_prefix_1 = "/lib/";
+static char *standard_startfile_prefix_2 = "/usr/lib/";
+
+#ifndef TOOLDIR_BASE_PREFIX
+#define TOOLDIR_BASE_PREFIX "/usr/local/"
+#endif
+static char *tooldir_base_prefix = TOOLDIR_BASE_PREFIX;
+static char *tooldir_prefix;
+
+/* Subdirectory to use for locating libraries. Set by
+ set_multilib_dir based on the compilation options. */
+
+static char *multilib_dir;
+
+/* Clear out the vector of arguments (after a command is executed). */
+
+static void
+clear_args ()
+{
+ argbuf_index = 0;
+}
+
+/* Add one argument to the vector at the end.
+ This is done when a space is seen or at the end of the line.
+ If DELETE_ALWAYS is nonzero, the arg is a filename
+ and the file should be deleted eventually.
+ If DELETE_FAILURE is nonzero, the arg is a filename
+ and the file should be deleted if this compilation fails. */
+
+static void
+store_arg (arg, delete_always, delete_failure)
+ char *arg;
+ int delete_always, delete_failure;
+{
+ if (argbuf_index + 1 == argbuf_length)
+ {
+ argbuf = (char **) xrealloc (argbuf, (argbuf_length *= 2) * sizeof (char *));
+ }
+
+ argbuf[argbuf_index++] = arg;
+ argbuf[argbuf_index] = 0;
+
+ if (delete_always || delete_failure)
+ record_temp_file (arg, delete_always, delete_failure);
+}
+
+/* Record the names of temporary files we tell compilers to write,
+ and delete them at the end of the run. */
+
+/* This is the common prefix we use to make temp file names.
+ It is chosen once for each run of this program.
+ It is substituted into a spec by %g.
+ Thus, all temp file names contain this prefix.
+ In practice, all temp file names start with this prefix.
+
+ This prefix comes from the envvar TMPDIR if it is defined;
+ otherwise, from the P_tmpdir macro if that is defined;
+ otherwise, in /usr/tmp or /tmp. */
+
+static char *temp_filename;
+
+/* Length of the prefix. */
+
+static int temp_filename_length;
+
+/* Define the list of temporary files to delete. */
+
+struct temp_file
+{
+ char *name;
+ struct temp_file *next;
+};
+
+/* Queue of files to delete on success or failure of compilation. */
+static struct temp_file *always_delete_queue;
+/* Queue of files to delete on failure of compilation. */
+static struct temp_file *failure_delete_queue;
+
+/* Record FILENAME as a file to be deleted automatically.
+ ALWAYS_DELETE nonzero means delete it if all compilation succeeds;
+ otherwise delete it in any case.
+ FAIL_DELETE nonzero means delete it if a compilation step fails;
+ otherwise delete it in any case. */
+
+static void
+record_temp_file (filename, always_delete, fail_delete)
+ char *filename;
+ int always_delete;
+ int fail_delete;
+{
+ register char *name;
+ name = xmalloc (strlen (filename) + 1);
+ strcpy (name, filename);
+
+ if (always_delete)
+ {
+ register struct temp_file *temp;
+ for (temp = always_delete_queue; temp; temp = temp->next)
+ if (! strcmp (name, temp->name))
+ goto already1;
+ temp = (struct temp_file *) xmalloc (sizeof (struct temp_file));
+ temp->next = always_delete_queue;
+ temp->name = name;
+ always_delete_queue = temp;
+ already1:;
+ }
+
+ if (fail_delete)
+ {
+ register struct temp_file *temp;
+ for (temp = failure_delete_queue; temp; temp = temp->next)
+ if (! strcmp (name, temp->name))
+ goto already2;
+ temp = (struct temp_file *) xmalloc (sizeof (struct temp_file));
+ temp->next = failure_delete_queue;
+ temp->name = name;
+ failure_delete_queue = temp;
+ already2:;
+ }
+}
+
+/* Delete all the temporary files whose names we previously recorded. */
+
+static void
+delete_if_ordinary (name)
+ char *name;
+{
+ struct stat st;
+#ifdef DEBUG
+ int i, c;
+
+ printf ("Delete %s? (y or n) ", name);
+ fflush (stdout);
+ i = getchar ();
+ if (i != '\n')
+ while ((c = getchar ()) != '\n' && c != EOF) ;
+ if (i == 'y' || i == 'Y')
+#endif /* DEBUG */
+ if (stat (name, &st) >= 0 && S_ISREG (st.st_mode))
+ if (unlink (name) < 0)
+ if (verbose_flag)
+ perror_with_name (name);
+}
+
+static void
+delete_temp_files ()
+{
+ register struct temp_file *temp;
+
+ for (temp = always_delete_queue; temp; temp = temp->next)
+ delete_if_ordinary (temp->name);
+ always_delete_queue = 0;
+}
+
+/* Delete all the files to be deleted on error. */
+
+static void
+delete_failure_queue ()
+{
+ register struct temp_file *temp;
+
+ for (temp = failure_delete_queue; temp; temp = temp->next)
+ delete_if_ordinary (temp->name);
+}
+
+static void
+clear_failure_queue ()
+{
+ failure_delete_queue = 0;
+}
+
+/* Compute a string to use as the base of all temporary file names.
+ It is substituted for %g. */
+
+static char *
+choose_temp_base_try (try, base)
+ char *try;
+ char *base;
+{
+ char *rv;
+ if (base)
+ rv = base;
+ else if (try == (char *)0)
+ rv = 0;
+ else if (access (try, R_OK | W_OK) != 0)
+ rv = 0;
+ else
+ rv = try;
+ return rv;
+}
+
+static void
+choose_temp_base ()
+{
+ char *base = 0;
+ int len;
+
+ base = choose_temp_base_try (getenv ("TMPDIR"), base);
+ base = choose_temp_base_try (getenv ("TMP"), base);
+ base = choose_temp_base_try (getenv ("TEMP"), base);
+
+#ifdef P_tmpdir
+ base = choose_temp_base_try (P_tmpdir, base);
+#endif
+
+ base = choose_temp_base_try ("/usr/tmp", base);
+ base = choose_temp_base_try ("/tmp", base);
+
+ /* If all else fails, use the current directory! */
+ if (base == (char *)0)
+ base = "./";
+
+ len = strlen (base);
+ temp_filename = xmalloc (len + sizeof("/ccXXXXXX") + 1);
+ strcpy (temp_filename, base);
+ if (len > 0 && temp_filename[len-1] != '/')
+ temp_filename[len++] = '/';
+ strcpy (temp_filename + len, "ccXXXXXX");
+
+ mktemp (temp_filename);
+ temp_filename_length = strlen (temp_filename);
+ if (temp_filename_length == 0)
+ abort ();
+}
+
+
+/* Routine to add variables to the environment. We do this to pass
+ the pathname of the gcc driver, and the directories search to the
+ collect2 program, which is being run as ld. This way, we can be
+ sure of executing the right compiler when collect2 wants to build
+ constructors and destructors. Since the environment variables we
+ use come from an obstack, we don't have to worry about allocating
+ space for them. */
+
+#ifndef HAVE_PUTENV
+
+void
+putenv (str)
+ char *str;
+{
+#ifndef VMS /* nor about VMS */
+
+ extern char **environ;
+ char **old_environ = environ;
+ char **envp;
+ int num_envs = 0;
+ int name_len = 1;
+ int str_len = strlen (str);
+ char *p = str;
+ int ch;
+
+ while ((ch = *p++) != '\0' && ch != '=')
+ name_len++;
+
+ if (!ch)
+ abort ();
+
+ /* Search for replacing an existing environment variable, and
+ count the number of total environment variables. */
+ for (envp = old_environ; *envp; envp++)
+ {
+ num_envs++;
+ if (!strncmp (str, *envp, name_len))
+ {
+ *envp = str;
+ return;
+ }
+ }
+
+ /* Add a new environment variable */
+ environ = (char **) xmalloc (sizeof (char *) * (num_envs+2));
+ *environ = str;
+ bcopy ((char *) old_environ, (char *) (environ + 1),
+ sizeof (char *) * (num_envs+1));
+
+#endif /* VMS */
+}
+
+#endif /* HAVE_PUTENV */
+
+
+/* Rebuild the COMPILER_PATH and LIBRARY_PATH environment variables for collect. */
+
+static void
+putenv_from_prefixes (paths, env_var)
+ struct path_prefix *paths;
+ char *env_var;
+{
+ int suffix_len = (machine_suffix) ? strlen (machine_suffix) : 0;
+ int just_suffix_len
+ = (just_machine_suffix) ? strlen (just_machine_suffix) : 0;
+ int first_time = TRUE;
+ struct prefix_list *pprefix;
+
+ obstack_grow (&collect_obstack, env_var, strlen (env_var));
+
+ for (pprefix = paths->plist; pprefix != 0; pprefix = pprefix->next)
+ {
+ int len = strlen (pprefix->prefix);
+
+ if (machine_suffix
+ && is_directory (pprefix->prefix, machine_suffix, 0))
+ {
+ if (!first_time)
+ obstack_1grow (&collect_obstack, PATH_SEPARATOR);
+
+ first_time = FALSE;
+ obstack_grow (&collect_obstack, pprefix->prefix, len);
+ obstack_grow (&collect_obstack, machine_suffix, suffix_len);
+ }
+
+ if (just_machine_suffix
+ && pprefix->require_machine_suffix == 2
+ && is_directory (pprefix->prefix, just_machine_suffix, 0))
+ {
+ if (!first_time)
+ obstack_1grow (&collect_obstack, PATH_SEPARATOR);
+
+ first_time = FALSE;
+ obstack_grow (&collect_obstack, pprefix->prefix, len);
+ obstack_grow (&collect_obstack, just_machine_suffix,
+ just_suffix_len);
+ }
+
+ if (!pprefix->require_machine_suffix)
+ {
+ if (!first_time)
+ obstack_1grow (&collect_obstack, PATH_SEPARATOR);
+
+ first_time = FALSE;
+ obstack_grow (&collect_obstack, pprefix->prefix, len);
+ }
+ }
+ obstack_1grow (&collect_obstack, '\0');
+ putenv (obstack_finish (&collect_obstack));
+}
+
+
+/* Search for NAME using the prefix list PREFIXES. MODE is passed to
+ access to check permissions.
+ Return 0 if not found, otherwise return its name, allocated with malloc. */
+
+static char *
+find_a_file (pprefix, name, mode)
+ struct path_prefix *pprefix;
+ char *name;
+ int mode;
+{
+ char *temp;
+ char *file_suffix = ((mode & X_OK) != 0 ? EXECUTABLE_SUFFIX : "");
+ struct prefix_list *pl;
+ int len = pprefix->max_len + strlen (name) + strlen (file_suffix) + 1;
+
+ if (machine_suffix)
+ len += strlen (machine_suffix);
+
+ temp = xmalloc (len);
+
+ /* Determine the filename to execute (special case for absolute paths). */
+
+ if (*name == '/')
+ {
+ if (access (name, mode))
+ {
+ strcpy (temp, name);
+ return temp;
+ }
+ }
+ else
+ for (pl = pprefix->plist; pl; pl = pl->next)
+ {
+ if (machine_suffix)
+ {
+ /* Some systems have a suffix for executable files.
+ So try appending that first. */
+ if (file_suffix[0] != 0)
+ {
+ strcpy (temp, pl->prefix);
+ strcat (temp, machine_suffix);
+ strcat (temp, name);
+ strcat (temp, file_suffix);
+ if (access (temp, mode) == 0)
+ {
+ if (pl->used_flag_ptr != 0)
+ *pl->used_flag_ptr = 1;
+ return temp;
+ }
+ }
+
+ /* Now try just the name. */
+ strcpy (temp, pl->prefix);
+ strcat (temp, machine_suffix);
+ strcat (temp, name);
+ if (access (temp, mode) == 0)
+ {
+ if (pl->used_flag_ptr != 0)
+ *pl->used_flag_ptr = 1;
+ return temp;
+ }
+ }
+
+ /* Certain prefixes are tried with just the machine type,
+ not the version. This is used for finding as, ld, etc. */
+ if (just_machine_suffix && pl->require_machine_suffix == 2)
+ {
+ /* Some systems have a suffix for executable files.
+ So try appending that first. */
+ if (file_suffix[0] != 0)
+ {
+ strcpy (temp, pl->prefix);
+ strcat (temp, just_machine_suffix);
+ strcat (temp, name);
+ strcat (temp, file_suffix);
+ if (access (temp, mode) == 0)
+ {
+ if (pl->used_flag_ptr != 0)
+ *pl->used_flag_ptr = 1;
+ return temp;
+ }
+ }
+
+ strcpy (temp, pl->prefix);
+ strcat (temp, just_machine_suffix);
+ strcat (temp, name);
+ if (access (temp, mode) == 0)
+ {
+ if (pl->used_flag_ptr != 0)
+ *pl->used_flag_ptr = 1;
+ return temp;
+ }
+ }
+
+ /* Certain prefixes can't be used without the machine suffix
+ when the machine or version is explicitly specified. */
+ if (!pl->require_machine_suffix)
+ {
+ /* Some systems have a suffix for executable files.
+ So try appending that first. */
+ if (file_suffix[0] != 0)
+ {
+ strcpy (temp, pl->prefix);
+ strcat (temp, name);
+ strcat (temp, file_suffix);
+ if (access (temp, mode) == 0)
+ {
+ if (pl->used_flag_ptr != 0)
+ *pl->used_flag_ptr = 1;
+ return temp;
+ }
+ }
+
+ strcpy (temp, pl->prefix);
+ strcat (temp, name);
+ if (access (temp, mode) == 0)
+ {
+ if (pl->used_flag_ptr != 0)
+ *pl->used_flag_ptr = 1;
+ return temp;
+ }
+ }
+ }
+
+ free (temp);
+ return 0;
+}
+
+/* Add an entry for PREFIX in PLIST. If FIRST is set, it goes
+ at the start of the list, otherwise it goes at the end.
+
+ If WARN is nonzero, we will warn if no file is found
+ through this prefix. WARN should point to an int
+ which will be set to 1 if this entry is used.
+
+ REQUIRE_MACHINE_SUFFIX is 1 if this prefix can't be used without
+ the complete value of machine_suffix.
+ 2 means try both machine_suffix and just_machine_suffix. */
+
+static void
+add_prefix (pprefix, prefix, first, require_machine_suffix, warn)
+ struct path_prefix *pprefix;
+ char *prefix;
+ int first;
+ int require_machine_suffix;
+ int *warn;
+{
+ struct prefix_list *pl, **prev;
+ int len;
+
+ if (!first && pprefix->plist)
+ {
+ for (pl = pprefix->plist; pl->next; pl = pl->next)
+ ;
+ prev = &pl->next;
+ }
+ else
+ prev = &pprefix->plist;
+
+ /* Keep track of the longest prefix */
+
+ len = strlen (prefix);
+ if (len > pprefix->max_len)
+ pprefix->max_len = len;
+
+ pl = (struct prefix_list *) xmalloc (sizeof (struct prefix_list));
+ pl->prefix = save_string (prefix, len);
+ pl->require_machine_suffix = require_machine_suffix;
+ pl->used_flag_ptr = warn;
+ if (warn)
+ *warn = 0;
+
+ if (*prev)
+ pl->next = *prev;
+ else
+ pl->next = (struct prefix_list *) 0;
+ *prev = pl;
+}
+
+/* Print warnings for any prefixes in the list PPREFIX that were not used. */
+
+static void
+unused_prefix_warnings (pprefix)
+ struct path_prefix *pprefix;
+{
+ struct prefix_list *pl = pprefix->plist;
+
+ while (pl)
+ {
+ if (pl->used_flag_ptr != 0 && !*pl->used_flag_ptr)
+ {
+ error ("file path prefix `%s' never used",
+ pl->prefix);
+ /* Prevent duplicate warnings. */
+ *pl->used_flag_ptr = 1;
+ }
+ pl = pl->next;
+ }
+}
+
+/* Get rid of all prefixes built up so far in *PLISTP. */
+
+static void
+free_path_prefix (pprefix)
+ struct path_prefix *pprefix;
+{
+ struct prefix_list *pl = pprefix->plist;
+ struct prefix_list *temp;
+
+ while (pl)
+ {
+ temp = pl;
+ pl = pl->next;
+ free (temp->prefix);
+ free ((char *) temp);
+ }
+ pprefix->plist = (struct prefix_list *) 0;
+}
+
+/* stdin file number. */
+#define STDIN_FILE_NO 0
+
+/* stdout file number. */
+#define STDOUT_FILE_NO 1
+
+/* value of `pipe': port index for reading. */
+#define READ_PORT 0
+
+/* value of `pipe': port index for writing. */
+#define WRITE_PORT 1
+
+/* Pipe waiting from last process, to be used as input for the next one.
+ Value is STDIN_FILE_NO if no pipe is waiting
+ (i.e. the next command is the first of a group). */
+
+static int last_pipe_input;
+
+/* Fork one piped subcommand. FUNC is the system call to use
+ (either execv or execvp). ARGV is the arg vector to use.
+ NOT_LAST is nonzero if this is not the last subcommand
+ (i.e. its output should be piped to the next one.) */
+
+#ifndef OS2
+#ifdef __MSDOS__
+
+/* Declare these to avoid compilation error. They won't be called. */
+int execv(const char *a, const char **b){}
+int execvp(const char *a, const char **b){}
+
+static int
+pexecute (search_flag, program, argv, not_last)
+ int search_flag;
+ char *program;
+ char *argv[];
+ int not_last;
+{
+ char *scmd, *rf;
+ FILE *argfile;
+ int i, el = search_flag ? 0 : 4;
+
+ scmd = (char *)malloc (strlen (program) + strlen (temp_filename) + 6 + el);
+ rf = scmd + strlen(program) + 2 + el;
+ sprintf (scmd, "%s%s @%s.gp", program,
+ (search_flag ? "" : ".exe"), temp_filename);
+ argfile = fopen (rf, "w");
+ if (argfile == 0)
+ pfatal_with_name (rf);
+
+ for (i=1; argv[i]; i++)
+ {
+ char *cp;
+ for (cp = argv[i]; *cp; cp++)
+ {
+ if (*cp == '"' || *cp == '\'' || *cp == '\\' || isspace (*cp))
+ fputc ('\\', argfile);
+ fputc (*cp, argfile);
+ }
+ fputc ('\n', argfile);
+ }
+ fclose (argfile);
+
+ i = system (scmd);
+
+ remove (rf);
+
+ if (i == -1)
+ {
+ perror_exec (program);
+ return MIN_FATAL_STATUS << 8;
+ }
+
+ return i << 8;
+}
+
+#else /* not __MSDOS__ */
+
+static int
+pexecute (search_flag, program, argv, not_last)
+ int search_flag;
+ char *program;
+ char *argv[];
+ int not_last;
+{
+ int (*func)() = (search_flag ? execv : execvp);
+ int pid;
+ int pdes[2];
+ int input_desc = last_pipe_input;
+ int output_desc = STDOUT_FILE_NO;
+ int retries, sleep_interval;
+
+ /* If this isn't the last process, make a pipe for its output,
+ and record it as waiting to be the input to the next process. */
+
+ if (not_last)
+ {
+ if (pipe (pdes) < 0)
+ pfatal_with_name ("pipe");
+ output_desc = pdes[WRITE_PORT];
+ last_pipe_input = pdes[READ_PORT];
+ }
+ else
+ last_pipe_input = STDIN_FILE_NO;
+
+ /* Fork a subprocess; wait and retry if it fails. */
+ sleep_interval = 1;
+ for (retries = 0; retries < 4; retries++)
+ {
+ pid = vfork ();
+ if (pid >= 0)
+ break;
+ sleep (sleep_interval);
+ sleep_interval *= 2;
+ }
+
+ switch (pid)
+ {
+ case -1:
+#ifdef vfork
+ pfatal_with_name ("fork");
+#else
+ pfatal_with_name ("vfork");
+#endif
+ /* NOTREACHED */
+ return 0;
+
+ case 0: /* child */
+ /* Move the input and output pipes into place, if nec. */
+ if (input_desc != STDIN_FILE_NO)
+ {
+ close (STDIN_FILE_NO);
+ dup (input_desc);
+ close (input_desc);
+ }
+ if (output_desc != STDOUT_FILE_NO)
+ {
+ close (STDOUT_FILE_NO);
+ dup (output_desc);
+ close (output_desc);
+ }
+
+ /* Close the parent's descs that aren't wanted here. */
+ if (last_pipe_input != STDIN_FILE_NO)
+ close (last_pipe_input);
+
+ /* Exec the program. */
+ (*func) (program, argv);
+ perror_exec (program);
+ exit (-1);
+ /* NOTREACHED */
+ return 0;
+
+ default:
+ /* In the parent, after forking.
+ Close the descriptors that we made for this child. */
+ if (input_desc != STDIN_FILE_NO)
+ close (input_desc);
+ if (output_desc != STDOUT_FILE_NO)
+ close (output_desc);
+
+ /* Return child's process number. */
+ return pid;
+ }
+}
+
+#endif /* not __MSDOS__ */
+#else /* not OS2 */
+
+static int
+pexecute (search_flag, program, argv, not_last)
+ int search_flag;
+ char *program;
+ char *argv[];
+ int not_last;
+{
+ return (search_flag ? spawnv : spawnvp) (1, program, argv);
+}
+#endif /* not OS2 */
+
+/* Execute the command specified by the arguments on the current line of spec.
+ When using pipes, this includes several piped-together commands
+ with `|' between them.
+
+ Return 0 if successful, -1 if failed. */
+
+static int
+execute ()
+{
+ int i;
+ int n_commands; /* # of command. */
+ char *string;
+ struct command
+ {
+ char *prog; /* program name. */
+ char **argv; /* vector of args. */
+ int pid; /* pid of process for this command. */
+ };
+
+ struct command *commands; /* each command buffer with above info. */
+
+ /* Count # of piped commands. */
+ for (n_commands = 1, i = 0; i < argbuf_index; i++)
+ if (strcmp (argbuf[i], "|") == 0)
+ n_commands++;
+
+ /* Get storage for each command. */
+ commands
+ = (struct command *) alloca (n_commands * sizeof (struct command));
+
+ /* Split argbuf into its separate piped processes,
+ and record info about each one.
+ Also search for the programs that are to be run. */
+
+ commands[0].prog = argbuf[0]; /* first command. */
+ commands[0].argv = &argbuf[0];
+ string = find_a_file (&exec_prefix, commands[0].prog, X_OK);
+ if (string)
+ commands[0].argv[0] = string;
+
+ for (n_commands = 1, i = 0; i < argbuf_index; i++)
+ if (strcmp (argbuf[i], "|") == 0)
+ { /* each command. */
+#ifdef __MSDOS__
+ fatal ("-pipe not supported under MS-DOS");
+#endif
+ argbuf[i] = 0; /* termination of command args. */
+ commands[n_commands].prog = argbuf[i + 1];
+ commands[n_commands].argv = &argbuf[i + 1];
+ string = find_a_file (&exec_prefix, commands[n_commands].prog, X_OK);
+ if (string)
+ commands[n_commands].argv[0] = string;
+ n_commands++;
+ }
+
+ argbuf[argbuf_index] = 0;
+
+ /* If -v, print what we are about to do, and maybe query. */
+
+ if (verbose_flag)
+ {
+ /* Print each piped command as a separate line. */
+ for (i = 0; i < n_commands ; i++)
+ {
+ char **j;
+
+ for (j = commands[i].argv; *j; j++)
+ fprintf (stderr, " %s", *j);
+
+ /* Print a pipe symbol after all but the last command. */
+ if (i + 1 != n_commands)
+ fprintf (stderr, " |");
+ fprintf (stderr, "\n");
+ }
+ fflush (stderr);
+#ifdef DEBUG
+ fprintf (stderr, "\nGo ahead? (y or n) ");
+ fflush (stderr);
+ i = getchar ();
+ if (i != '\n')
+ while (getchar () != '\n') ;
+ if (i != 'y' && i != 'Y')
+ return 0;
+#endif /* DEBUG */
+ }
+
+ /* Run each piped subprocess. */
+
+ last_pipe_input = STDIN_FILE_NO;
+ for (i = 0; i < n_commands; i++)
+ {
+ char *string = commands[i].argv[0];
+
+ commands[i].pid = pexecute (string != commands[i].prog,
+ string, commands[i].argv,
+ i + 1 < n_commands);
+
+ if (string != commands[i].prog)
+ free (string);
+ }
+
+ execution_count++;
+
+ /* Wait for all the subprocesses to finish.
+ We don't care what order they finish in;
+ we know that N_COMMANDS waits will get them all. */
+
+ {
+ int ret_code = 0;
+
+ for (i = 0; i < n_commands; i++)
+ {
+ int status;
+ int pid;
+ char *prog = "unknown";
+
+#ifdef __MSDOS__
+ status = pid = commands[i].pid;
+#else
+ pid = wait (&status);
+#endif
+ if (pid < 0)
+ abort ();
+
+ if (status != 0)
+ {
+ int j;
+ for (j = 0; j < n_commands; j++)
+ if (commands[j].pid == pid)
+ prog = commands[j].prog;
+
+ if ((status & 0x7F) != 0)
+ {
+ fatal ("Internal compiler error: program %s got fatal signal %d",
+ prog, (status & 0x7F));
+ signal_count++;
+ }
+ if (((status & 0xFF00) >> 8) >= MIN_FATAL_STATUS)
+ ret_code = -1;
+ }
+ }
+ return ret_code;
+ }
+}
+
+/* Find all the switches given to us
+ and make a vector describing them.
+ The elements of the vector are strings, one per switch given.
+ If a switch uses following arguments, then the `part1' field
+ is the switch itself and the `args' field
+ is a null-terminated vector containing the following arguments.
+ The `live_cond' field is 1 if the switch is true in a conditional spec,
+ -1 if false (overridden by a later switch), and is initialized to zero.
+ The `valid' field is nonzero if any spec has looked at this switch;
+ if it remains zero at the end of the run, it must be meaningless. */
+
+struct switchstr
+{
+ char *part1;
+ char **args;
+ int live_cond;
+ int valid;
+};
+
+static struct switchstr *switches;
+
+static int n_switches;
+
+struct infile
+{
+ char *name;
+ char *language;
+};
+
+/* Also a vector of input files specified. */
+
+static struct infile *infiles;
+
+static int n_infiles;
+
+/* And a vector of corresponding output files is made up later. */
+
+static char **outfiles;
+
+/* Create the vector `switches' and its contents.
+ Store its length in `n_switches'. */
+
+static void
+process_command (argc, argv)
+ int argc;
+ char **argv;
+{
+ register int i;
+ char *temp;
+ char *spec_lang = 0;
+ int last_language_n_infiles;
+
+ gcc_exec_prefix = getenv ("GCC_EXEC_PREFIX");
+
+ n_switches = 0;
+ n_infiles = 0;
+
+ /* Figure compiler version from version string. */
+
+ compiler_version = save_string (version_string, strlen (version_string));
+ for (temp = compiler_version; *temp; ++temp)
+ {
+ if (*temp == ' ')
+ {
+ *temp = '\0';
+ break;
+ }
+ }
+
+ /* Set up the default search paths. */
+
+ if (gcc_exec_prefix)
+ {
+ add_prefix (&exec_prefix, gcc_exec_prefix, 0, 0, NULL_PTR);
+ add_prefix (&startfile_prefix, gcc_exec_prefix, 0, 0, NULL_PTR);
+ }
+
+ /* COMPILER_PATH and LIBRARY_PATH have values
+ that are lists of directory names with colons. */
+
+ temp = getenv ("COMPILER_PATH");
+ if (temp)
+ {
+ char *startp, *endp;
+ char *nstore = (char *) alloca (strlen (temp) + 3);
+
+ startp = endp = temp;
+ while (1)
+ {
+ if (*endp == PATH_SEPARATOR || *endp == 0)
+ {
+ strncpy (nstore, startp, endp-startp);
+ if (endp == startp)
+ {
+ strcpy (nstore, "./");
+ }
+ else if (endp[-1] != '/')
+ {
+ nstore[endp-startp] = '/';
+ nstore[endp-startp+1] = 0;
+ }
+ else
+ nstore[endp-startp] = 0;
+ add_prefix (&exec_prefix, nstore, 0, 0, NULL_PTR);
+ if (*endp == 0)
+ break;
+ endp = startp = endp + 1;
+ }
+ else
+ endp++;
+ }
+ }
+
+ temp = getenv ("LIBRARY_PATH");
+ if (temp)
+ {
+ char *startp, *endp;
+ char *nstore = (char *) alloca (strlen (temp) + 3);
+
+ startp = endp = temp;
+ while (1)
+ {
+ if (*endp == PATH_SEPARATOR || *endp == 0)
+ {
+ strncpy (nstore, startp, endp-startp);
+ if (endp == startp)
+ {
+ strcpy (nstore, "./");
+ }
+ else if (endp[-1] != '/')
+ {
+ nstore[endp-startp] = '/';
+ nstore[endp-startp+1] = 0;
+ }
+ else
+ nstore[endp-startp] = 0;
+ add_prefix (&startfile_prefix, nstore, 0, 0, NULL_PTR);
+ if (*endp == 0)
+ break;
+ endp = startp = endp + 1;
+ }
+ else
+ endp++;
+ }
+ }
+
+ /* Use LPATH like LIBRARY_PATH (for the CMU build program). */
+ temp = getenv ("LPATH");
+ if (temp)
+ {
+ char *startp, *endp;
+ char *nstore = (char *) alloca (strlen (temp) + 3);
+
+ startp = endp = temp;
+ while (1)
+ {
+ if (*endp == PATH_SEPARATOR || *endp == 0)
+ {
+ strncpy (nstore, startp, endp-startp);
+ if (endp == startp)
+ {
+ strcpy (nstore, "./");
+ }
+ else if (endp[-1] != '/')
+ {
+ nstore[endp-startp] = '/';
+ nstore[endp-startp+1] = 0;
+ }
+ else
+ nstore[endp-startp] = 0;
+ add_prefix (&startfile_prefix, nstore, 0, 0, NULL_PTR);
+ if (*endp == 0)
+ break;
+ endp = startp = endp + 1;
+ }
+ else
+ endp++;
+ }
+ }
+
+ /* Convert new-style -- options to old-style. */
+ translate_options (&argc, &argv);
+
+ /* Scan argv twice. Here, the first time, just count how many switches
+ there will be in their vector, and how many input files in theirs.
+ Here we also parse the switches that cc itself uses (e.g. -v). */
+
+ for (i = 1; i < argc; i++)
+ {
+ if (! strcmp (argv[i], "-dumpspecs"))
+ {
+ printf ("*asm:\n%s\n\n", asm_spec);
+ printf ("*asm_final:\n%s\n\n", asm_final_spec);
+ printf ("*cpp:\n%s\n\n", cpp_spec);
+ printf ("*cc1:\n%s\n\n", cc1_spec);
+ printf ("*cc1plus:\n%s\n\n", cc1plus_spec);
+ printf ("*endfile:\n%s\n\n", endfile_spec);
+ printf ("*link:\n%s\n\n", link_spec);
+ printf ("*lib:\n%s\n\n", lib_spec);
+ printf ("*startfile:\n%s\n\n", startfile_spec);
+ printf ("*switches_need_spaces:\n%s\n\n", switches_need_spaces);
+ printf ("*signed_char:\n%s\n\n", signed_char_spec);
+ printf ("*predefines:\n%s\n\n", cpp_predefines);
+ printf ("*cross_compile:\n%d\n\n", cross_compile);
+ printf ("*multilib:\n%s\n\n", multilib_select);
+
+ exit (0);
+ }
+ else if (! strcmp (argv[i], "-dumpversion"))
+ {
+ printf ("%s\n", version_string);
+ exit (0);
+ }
+ else if (! strcmp (argv[i], "-print-libgcc-file-name"))
+ print_file_name = "libgcc.a";
+ else if (! strncmp (argv[i], "-print-file-name=", 17))
+ print_file_name = argv[i] + 17;
+ else if (! strncmp (argv[i], "-print-prog-name=", 17))
+ print_prog_name = argv[i] + 17;
+ else if (! strcmp (argv[i], "-print-multi-lib"))
+ print_multi_lib = 1;
+ else if (! strcmp (argv[i], "-print-multi-directory"))
+ print_multi_directory = 1;
+ else if (! strcmp (argv[i], "-Xlinker"))
+ {
+ /* Pass the argument of this option to the linker when we link. */
+
+ if (i + 1 == argc)
+ fatal ("argument to `-Xlinker' is missing");
+
+ n_linker_options++;
+ if (!linker_options)
+ linker_options
+ = (char **) xmalloc (n_linker_options * sizeof (char **));
+ else
+ linker_options
+ = (char **) xrealloc (linker_options,
+ n_linker_options * sizeof (char **));
+
+ linker_options[n_linker_options - 1] = argv[++i];
+ }
+ else if (! strncmp (argv[i], "-Wl,", 4))
+ {
+ int prev, j;
+ /* Pass the rest of this option to the linker when we link. */
+
+ n_linker_options++;
+ if (!linker_options)
+ linker_options
+ = (char **) xmalloc (n_linker_options * sizeof (char **));
+ else
+ linker_options
+ = (char **) xrealloc (linker_options,
+ n_linker_options * sizeof (char **));
+
+ /* Split the argument at commas. */
+ prev = 4;
+ for (j = 4; argv[i][j]; j++)
+ if (argv[i][j] == ',')
+ {
+ linker_options[n_linker_options - 1]
+ = save_string (argv[i] + prev, j - prev);
+ n_linker_options++;
+ linker_options
+ = (char **) xrealloc (linker_options,
+ n_linker_options * sizeof (char **));
+ prev = j + 1;
+ }
+ /* Record the part after the last comma. */
+ linker_options[n_linker_options - 1] = argv[i] + prev;
+ }
+ else if (! strncmp (argv[i], "-Wa,", 4))
+ {
+ int prev, j;
+ /* Pass the rest of this option to the assembler. */
+
+ n_assembler_options++;
+ if (!assembler_options)
+ assembler_options
+ = (char **) xmalloc (n_assembler_options * sizeof (char **));
+ else
+ assembler_options
+ = (char **) xrealloc (assembler_options,
+ n_assembler_options * sizeof (char **));
+
+ /* Split the argument at commas. */
+ prev = 4;
+ for (j = 4; argv[i][j]; j++)
+ if (argv[i][j] == ',')
+ {
+ assembler_options[n_assembler_options - 1]
+ = save_string (argv[i] + prev, j - prev);
+ n_assembler_options++;
+ assembler_options
+ = (char **) xrealloc (assembler_options,
+ n_assembler_options * sizeof (char **));
+ prev = j + 1;
+ }
+ /* Record the part after the last comma. */
+ assembler_options[n_assembler_options - 1] = argv[i] + prev;
+ }
+ else if (argv[i][0] == '+' && argv[i][1] == 'e')
+ /* The +e options to the C++ front-end. */
+ n_switches++;
+ else if (argv[i][0] == '-' && argv[i][1] != 0 && argv[i][1] != 'l')
+ {
+ register char *p = &argv[i][1];
+ register int c = *p;
+
+ switch (c)
+ {
+ case 'b':
+ if (p[1] == 0 && i + 1 == argc)
+ fatal ("argument to `-b' is missing");
+ if (p[1] == 0)
+ spec_machine = argv[++i];
+ else
+ spec_machine = p + 1;
+ break;
+
+ case 'B':
+ {
+ int *temp = (int *) xmalloc (sizeof (int));
+ char *value;
+ if (p[1] == 0 && i + 1 == argc)
+ fatal ("argument to `-B' is missing");
+ if (p[1] == 0)
+ value = argv[++i];
+ else
+ value = p + 1;
+ add_prefix (&exec_prefix, value, 1, 0, temp);
+ add_prefix (&startfile_prefix, value, 1, 0, temp);
+ add_prefix (&include_prefix, concat (value, "include", ""),
+ 1, 0, 0);
+
+ /* As a kludge, if the arg is "[foo/]stageN/", just add
+ "[foo/]stageN/../include" to the include prefix. */
+ {
+ int len = strlen (value);
+ if ((len == 7 || (len > 7 && value[len - 8] == '/'))
+ && strncmp (value + len - 7, "stage", 5) == 0
+ && isdigit (value[len - 2])
+ && value[len - 1] == '/')
+ add_prefix (&include_prefix,
+ concat (value, "../include", ""), 1, 0, 0);
+ }
+ }
+ break;
+
+ case 'v': /* Print our subcommands and print versions. */
+ n_switches++;
+ /* If they do anything other than exactly `-v', don't set
+ verbose_flag; rather, continue on to give the error. */
+ if (p[1] != 0)
+ break;
+ verbose_flag++;
+ break;
+
+ case 'V':
+ if (p[1] == 0 && i + 1 == argc)
+ fatal ("argument to `-V' is missing");
+ if (p[1] == 0)
+ spec_version = argv[++i];
+ else
+ spec_version = p + 1;
+ compiler_version = spec_version;
+ break;
+
+ case 's':
+ if (!strcmp (p, "save-temps"))
+ {
+ save_temps_flag = 1;
+ n_switches++;
+ break;
+ }
+ default:
+ n_switches++;
+
+ if (SWITCH_TAKES_ARG (c) > (p[1] != 0))
+ i += SWITCH_TAKES_ARG (c) - (p[1] != 0);
+ else if (WORD_SWITCH_TAKES_ARG (p))
+ i += WORD_SWITCH_TAKES_ARG (p);
+ }
+ }
+ else
+ n_infiles++;
+ }
+
+ /* Set up the search paths before we go looking for config files. */
+
+ /* These come before the md prefixes so that we will find gcc's subcommands
+ (such as cpp) rather than those of the host system. */
+ /* Use 2 as fourth arg meaning try just the machine as a suffix,
+ as well as trying the machine and the version. */
+ add_prefix (&exec_prefix, standard_exec_prefix, 0, 2, NULL_PTR);
+ add_prefix (&exec_prefix, standard_exec_prefix_1, 0, 2, NULL_PTR);
+
+ add_prefix (&startfile_prefix, standard_exec_prefix, 0, 1, NULL_PTR);
+ add_prefix (&startfile_prefix, standard_exec_prefix_1, 0, 1, NULL_PTR);
+
+ tooldir_prefix = concat (tooldir_base_prefix, spec_machine, "/");
+
+ /* If tooldir is relative, base it on exec_prefix. A relative
+ tooldir lets us move the installed tree as a unit.
+
+ If GCC_EXEC_PREFIX is defined, then we want to add two relative
+ directories, so that we can search both the user specified directory
+ and the standard place. */
+
+ if (*tooldir_prefix != '/')
+ {
+ if (gcc_exec_prefix)
+ {
+ char *gcc_exec_tooldir_prefix
+ = concat (concat (gcc_exec_prefix, spec_machine, "/"),
+ concat (spec_version, "/", tooldir_prefix),
+ "");
+
+ add_prefix (&exec_prefix, concat (gcc_exec_tooldir_prefix, "bin", "/"),
+ 0, 0, NULL_PTR);
+ add_prefix (&startfile_prefix, concat (gcc_exec_tooldir_prefix, "lib", "/"),
+ 0, 0, NULL_PTR);
+ }
+
+ tooldir_prefix = concat (concat (standard_exec_prefix, spec_machine, "/"),
+ concat (spec_version, "/", tooldir_prefix),
+ "");
+ }
+
+ add_prefix (&exec_prefix, concat (tooldir_prefix, "bin", "/"),
+ 0, 0, NULL_PTR);
+ add_prefix (&startfile_prefix, concat (tooldir_prefix, "lib", "/"),
+ 0, 0, NULL_PTR);
+
+ /* More prefixes are enabled in main, after we read the specs file
+ and determine whether this is cross-compilation or not. */
+
+
+ /* Then create the space for the vectors and scan again. */
+
+ switches = ((struct switchstr *)
+ xmalloc ((n_switches + 1) * sizeof (struct switchstr)));
+ infiles = (struct infile *) xmalloc ((n_infiles + 1) * sizeof (struct infile));
+ n_switches = 0;
+ n_infiles = 0;
+ last_language_n_infiles = -1;
+
+ /* This, time, copy the text of each switch and store a pointer
+ to the copy in the vector of switches.
+ Store all the infiles in their vector. */
+
+ for (i = 1; i < argc; i++)
+ {
+ /* Just skip the switches that were handled by the preceding loop. */
+ if (!strcmp (argv[i], "-Xlinker"))
+ i++;
+ else if (! strncmp (argv[i], "-Wl,", 4))
+ ;
+ else if (! strncmp (argv[i], "-Wa,", 4))
+ ;
+ else if (! strcmp (argv[i], "-print-libgcc-file-name"))
+ ;
+ else if (! strncmp (argv[i], "-print-file-name=", 17))
+ ;
+ else if (! strncmp (argv[i], "-print-prog-name=", 17))
+ ;
+ else if (! strcmp (argv[i], "-print-multi-lib"))
+ ;
+ else if (! strcmp (argv[i], "-print-multi-directory"))
+ ;
+ else if (argv[i][0] == '+' && argv[i][1] == 'e')
+ {
+ /* Compensate for the +e options to the C++ front-end;
+ they're there simply for cfront call-compatibility. We do
+ some magic in default_compilers to pass them down properly.
+ Note we deliberately start at the `+' here, to avoid passing
+ -e0 or -e1 down into the linker. */
+ switches[n_switches].part1 = &argv[i][0];
+ switches[n_switches].args = 0;
+ switches[n_switches].live_cond = 0;
+ switches[n_switches].valid = 0;
+ n_switches++;
+ }
+ else if (argv[i][0] == '-' && argv[i][1] != 0 && argv[i][1] != 'l')
+ {
+ register char *p = &argv[i][1];
+ register int c = *p;
+
+ if (c == 'B' || c == 'b' || c == 'V')
+ {
+ /* Skip a separate arg, if any. */
+ if (p[1] == 0)
+ i++;
+ continue;
+ }
+ if (c == 'x')
+ {
+ if (p[1] == 0 && i + 1 == argc)
+ fatal ("argument to `-x' is missing");
+ if (p[1] == 0)
+ spec_lang = argv[++i];
+ else
+ spec_lang = p + 1;
+ if (! strcmp (spec_lang, "none"))
+ /* Suppress the warning if -xnone comes after the last input file,
+ because alternate command interfaces like g++ might find it
+ useful to place -xnone after each input file. */
+ spec_lang = 0;
+ else
+ last_language_n_infiles = n_infiles;
+ continue;
+ }
+ switches[n_switches].part1 = p;
+ /* Deal with option arguments in separate argv elements. */
+ if ((SWITCH_TAKES_ARG (c) > (p[1] != 0))
+ || WORD_SWITCH_TAKES_ARG (p))
+ {
+ int j = 0;
+ int n_args = WORD_SWITCH_TAKES_ARG (p);
+
+ if (n_args == 0)
+ {
+ /* Count only the option arguments in separate argv elements. */
+ n_args = SWITCH_TAKES_ARG (c) - (p[1] != 0);
+ }
+ if (i + n_args >= argc)
+ fatal ("argument to `-%s' is missing", p);
+ switches[n_switches].args
+ = (char **) xmalloc ((n_args + 1) * sizeof (char *));
+ while (j < n_args)
+ switches[n_switches].args[j++] = argv[++i];
+ /* Null-terminate the vector. */
+ switches[n_switches].args[j] = 0;
+ }
+ else if (*switches_need_spaces != 0 && (c == 'o' || c == 'L'))
+ {
+ /* On some systems, ld cannot handle -o or -L without space.
+ So split the -o or -L from its argument. */
+ switches[n_switches].part1 = (c == 'o' ? "o" : "L");
+ switches[n_switches].args = (char **) xmalloc (2 * sizeof (char *));
+ switches[n_switches].args[0] = xmalloc (strlen (p));
+ strcpy (switches[n_switches].args[0], &p[1]);
+ switches[n_switches].args[1] = 0;
+ }
+ else
+ switches[n_switches].args = 0;
+
+ switches[n_switches].live_cond = 0;
+ switches[n_switches].valid = 0;
+ /* This is always valid, since gcc.c itself understands it. */
+ if (!strcmp (p, "save-temps"))
+ switches[n_switches].valid = 1;
+ n_switches++;
+ }
+ else
+ {
+ if ((argv[i][0] != '-' || argv[i][1] != 'l')
+ && strcmp (argv[i], "-")
+ && access (argv[i], R_OK) < 0)
+ {
+ perror_with_name (argv[i]);
+ error_count++;
+ }
+ else
+ {
+ infiles[n_infiles].language = spec_lang;
+ infiles[n_infiles++].name = argv[i];
+ }
+ }
+ }
+
+ if (n_infiles == last_language_n_infiles && spec_lang != 0)
+ error ("Warning: `-x %s' after last input file has no effect", spec_lang);
+
+ switches[n_switches].part1 = 0;
+ infiles[n_infiles].name = 0;
+
+ /* If we have a GCC_EXEC_PREFIX envvar, modify it for cpp's sake. */
+ if (gcc_exec_prefix)
+ {
+ temp = (char *) xmalloc (strlen (gcc_exec_prefix) + strlen (spec_version)
+ + strlen (spec_machine) + 3);
+ strcpy (temp, gcc_exec_prefix);
+ strcat (temp, spec_machine);
+ strcat (temp, "/");
+ strcat (temp, spec_version);
+ strcat (temp, "/");
+ gcc_exec_prefix = temp;
+ }
+}
+
+/* Process a spec string, accumulating and running commands. */
+
+/* These variables describe the input file name.
+ input_file_number is the index on outfiles of this file,
+ so that the output file name can be stored for later use by %o.
+ input_basename is the start of the part of the input file
+ sans all directory names, and basename_length is the number
+ of characters starting there excluding the suffix .c or whatever. */
+
+static char *input_filename;
+static int input_file_number;
+static int input_filename_length;
+static int basename_length;
+static char *input_basename;
+static char *input_suffix;
+
+/* These are variables used within do_spec and do_spec_1. */
+
+/* Nonzero if an arg has been started and not yet terminated
+ (with space, tab or newline). */
+static int arg_going;
+
+/* Nonzero means %d or %g has been seen; the next arg to be terminated
+ is a temporary file name. */
+static int delete_this_arg;
+
+/* Nonzero means %w has been seen; the next arg to be terminated
+ is the output file name of this compilation. */
+static int this_is_output_file;
+
+/* Nonzero means %s has been seen; the next arg to be terminated
+ is the name of a library file and we should try the standard
+ search dirs for it. */
+static int this_is_library_file;
+
+/* Nonzero means that the input of this command is coming from a pipe. */
+static int input_from_pipe;
+
+/* Process the spec SPEC and run the commands specified therein.
+ Returns 0 if the spec is successfully processed; -1 if failed. */
+
+static int
+do_spec (spec)
+ char *spec;
+{
+ int value;
+
+ clear_args ();
+ arg_going = 0;
+ delete_this_arg = 0;
+ this_is_output_file = 0;
+ this_is_library_file = 0;
+ input_from_pipe = 0;
+
+ value = do_spec_1 (spec, 0, NULL_PTR);
+
+ /* Force out any unfinished command.
+ If -pipe, this forces out the last command if it ended in `|'. */
+ if (value == 0)
+ {
+ if (argbuf_index > 0 && !strcmp (argbuf[argbuf_index - 1], "|"))
+ argbuf_index--;
+
+ if (argbuf_index > 0)
+ value = execute ();
+ }
+
+ return value;
+}
+
+/* Process the sub-spec SPEC as a portion of a larger spec.
+ This is like processing a whole spec except that we do
+ not initialize at the beginning and we do not supply a
+ newline by default at the end.
+ INSWITCH nonzero means don't process %-sequences in SPEC;
+ in this case, % is treated as an ordinary character.
+ This is used while substituting switches.
+ INSWITCH nonzero also causes SPC not to terminate an argument.
+
+ Value is zero unless a line was finished
+ and the command on that line reported an error. */
+
+static int
+do_spec_1 (spec, inswitch, soft_matched_part)
+ char *spec;
+ int inswitch;
+ char *soft_matched_part;
+{
+ register char *p = spec;
+ register int c;
+ int i;
+ char *string;
+ int value;
+
+ while (c = *p++)
+ /* If substituting a switch, treat all chars like letters.
+ Otherwise, NL, SPC, TAB and % are special. */
+ switch (inswitch ? 'a' : c)
+ {
+ case '\n':
+ /* End of line: finish any pending argument,
+ then run the pending command if one has been started. */
+ if (arg_going)
+ {
+ obstack_1grow (&obstack, 0);
+ string = obstack_finish (&obstack);
+ if (this_is_library_file)
+ string = find_file (string);
+ store_arg (string, delete_this_arg, this_is_output_file);
+ if (this_is_output_file)
+ outfiles[input_file_number] = string;
+ }
+ arg_going = 0;
+
+ if (argbuf_index > 0 && !strcmp (argbuf[argbuf_index - 1], "|"))
+ {
+ int i;
+ for (i = 0; i < n_switches; i++)
+ if (!strcmp (switches[i].part1, "pipe"))
+ break;
+
+ /* A `|' before the newline means use a pipe here,
+ but only if -pipe was specified.
+ Otherwise, execute now and don't pass the `|' as an arg. */
+ if (i < n_switches)
+ {
+ input_from_pipe = 1;
+ switches[i].valid = 1;
+ break;
+ }
+ else
+ argbuf_index--;
+ }
+
+ if (argbuf_index > 0)
+ {
+ value = execute ();
+ if (value)
+ return value;
+ }
+ /* Reinitialize for a new command, and for a new argument. */
+ clear_args ();
+ arg_going = 0;
+ delete_this_arg = 0;
+ this_is_output_file = 0;
+ this_is_library_file = 0;
+ input_from_pipe = 0;
+ break;
+
+ case '|':
+ /* End any pending argument. */
+ if (arg_going)
+ {
+ obstack_1grow (&obstack, 0);
+ string = obstack_finish (&obstack);
+ if (this_is_library_file)
+ string = find_file (string);
+ store_arg (string, delete_this_arg, this_is_output_file);
+ if (this_is_output_file)
+ outfiles[input_file_number] = string;
+ }
+
+ /* Use pipe */
+ obstack_1grow (&obstack, c);
+ arg_going = 1;
+ break;
+
+ case '\t':
+ case ' ':
+ /* Space or tab ends an argument if one is pending. */
+ if (arg_going)
+ {
+ obstack_1grow (&obstack, 0);
+ string = obstack_finish (&obstack);
+ if (this_is_library_file)
+ string = find_file (string);
+ store_arg (string, delete_this_arg, this_is_output_file);
+ if (this_is_output_file)
+ outfiles[input_file_number] = string;
+ }
+ /* Reinitialize for a new argument. */
+ arg_going = 0;
+ delete_this_arg = 0;
+ this_is_output_file = 0;
+ this_is_library_file = 0;
+ break;
+
+ case '%':
+ switch (c = *p++)
+ {
+ case 0:
+ fatal ("Invalid specification! Bug in cc.");
+
+ case 'b':
+ obstack_grow (&obstack, input_basename, basename_length);
+ arg_going = 1;
+ break;
+
+ case 'd':
+ delete_this_arg = 2;
+ break;
+
+ /* Dump out the directories specified with LIBRARY_PATH,
+ followed by the absolute directories
+ that we search for startfiles. */
+ case 'D':
+ {
+ struct prefix_list *pl = startfile_prefix.plist;
+ int bufsize = 100;
+ char *buffer = (char *) xmalloc (bufsize);
+ int idx;
+
+ for (; pl; pl = pl->next)
+ {
+#ifdef RELATIVE_PREFIX_NOT_LINKDIR
+ /* Used on systems which record the specified -L dirs
+ and use them to search for dynamic linking. */
+ /* Relative directories always come from -B,
+ and it is better not to use them for searching
+ at run time. In particular, stage1 loses */
+ if (pl->prefix[0] != '/')
+ continue;
+#endif
+ /* Try subdirectory if there is one. */
+ if (multilib_dir != NULL)
+ {
+ if (machine_suffix)
+ {
+ if (strlen (pl->prefix) + strlen (machine_suffix)
+ >= bufsize)
+ bufsize = (strlen (pl->prefix)
+ + strlen (machine_suffix)) * 2 + 1;
+ buffer = (char *) xrealloc (buffer, bufsize);
+ strcpy (buffer, pl->prefix);
+ strcat (buffer, machine_suffix);
+ if (is_directory (buffer, multilib_dir, 1))
+ {
+ do_spec_1 ("-L", 0, NULL_PTR);
+#ifdef SPACE_AFTER_L_OPTION
+ do_spec_1 (" ", 0, NULL_PTR);
+#endif
+ do_spec_1 (buffer, 1, NULL_PTR);
+ do_spec_1 (multilib_dir, 1, NULL_PTR);
+ /* Make this a separate argument. */
+ do_spec_1 (" ", 0, NULL_PTR);
+ }
+ }
+ if (!pl->require_machine_suffix)
+ {
+ if (is_directory (pl->prefix, multilib_dir, 1))
+ {
+ do_spec_1 ("-L", 0, NULL_PTR);
+#ifdef SPACE_AFTER_L_OPTION
+ do_spec_1 (" ", 0, NULL_PTR);
+#endif
+ do_spec_1 (pl->prefix, 1, NULL_PTR);
+ do_spec_1 (multilib_dir, 1, NULL_PTR);
+ /* Make this a separate argument. */
+ do_spec_1 (" ", 0, NULL_PTR);
+ }
+ }
+ }
+ if (machine_suffix)
+ {
+ if (is_directory (pl->prefix, machine_suffix, 1))
+ {
+ do_spec_1 ("-L", 0, NULL_PTR);
+#ifdef SPACE_AFTER_L_OPTION
+ do_spec_1 (" ", 0, NULL_PTR);
+#endif
+ do_spec_1 (pl->prefix, 1, NULL_PTR);
+ /* Remove slash from machine_suffix. */
+ if (strlen (machine_suffix) >= bufsize)
+ bufsize = strlen (machine_suffix) * 2 + 1;
+ buffer = (char *) xrealloc (buffer, bufsize);
+ strcpy (buffer, machine_suffix);
+ idx = strlen (buffer);
+ if (buffer[idx - 1] == '/')
+ buffer[idx - 1] = 0;
+ do_spec_1 (buffer, 1, NULL_PTR);
+ /* Make this a separate argument. */
+ do_spec_1 (" ", 0, NULL_PTR);
+ }
+ }
+ if (!pl->require_machine_suffix)
+ {
+ if (is_directory (pl->prefix, "", 1))
+ {
+ do_spec_1 ("-L", 0, NULL_PTR);
+#ifdef SPACE_AFTER_L_OPTION
+ do_spec_1 (" ", 0, NULL_PTR);
+#endif
+ /* Remove slash from pl->prefix. */
+ if (strlen (pl->prefix) >= bufsize)
+ bufsize = strlen (pl->prefix) * 2 + 1;
+ buffer = (char *) xrealloc (buffer, bufsize);
+ strcpy (buffer, pl->prefix);
+ idx = strlen (buffer);
+ if (buffer[idx - 1] == '/')
+ buffer[idx - 1] = 0;
+ do_spec_1 (buffer, 1, NULL_PTR);
+ /* Make this a separate argument. */
+ do_spec_1 (" ", 0, NULL_PTR);
+ }
+ }
+ }
+ free (buffer);
+ }
+ break;
+
+ case 'e':
+ /* {...:%efoo} means report an error with `foo' as error message
+ and don't execute any more commands for this file. */
+ {
+ char *q = p;
+ char *buf;
+ while (*p != 0 && *p != '\n') p++;
+ buf = (char *) alloca (p - q + 1);
+ strncpy (buf, q, p - q);
+ buf[p - q] = 0;
+ error ("%s", buf);
+ return -1;
+ }
+ break;
+
+ case 'g':
+ case 'u':
+ case 'U':
+ if (save_temps_flag)
+ {
+ obstack_grow (&obstack, input_basename, basename_length);
+ delete_this_arg = 0;
+ }
+ else
+ {
+#ifdef MKTEMP_EACH_FILE
+ /* ??? This has a problem: the total number of
+ values mktemp can return is limited.
+ That matters for the names of object files.
+ In 2.4, do something about that. */
+ struct temp_name *t;
+ char *suffix = p;
+ while (*p == '.' || isalpha (*p))
+ p++;
+
+ /* See if we already have an association of %g/%u/%U and
+ suffix. */
+ for (t = temp_names; t; t = t->next)
+ if (t->length == p - suffix
+ && strncmp (t->suffix, suffix, p - suffix) == 0
+ && t->unique == (c != 'g'))
+ break;
+
+ /* Make a new association if needed. %u requires one. */
+ if (t == 0 || c == 'u')
+ {
+ if (t == 0)
+ {
+ t = (struct temp_name *) xmalloc (sizeof (struct temp_name));
+ t->next = temp_names;
+ temp_names = t;
+ }
+ t->length = p - suffix;
+ t->suffix = save_string (suffix, p - suffix);
+ t->unique = (c != 'g');
+ choose_temp_base ();
+ t->filename = temp_filename;
+ t->filename_length = temp_filename_length;
+ }
+
+ obstack_grow (&obstack, t->filename, t->filename_length);
+ delete_this_arg = 1;
+#else
+ obstack_grow (&obstack, temp_filename, temp_filename_length);
+ if (c == 'u' || c == 'U')
+ {
+ static int unique;
+ char buff[9];
+ if (c == 'u')
+ unique++;
+ sprintf (buff, "%d", unique);
+ obstack_grow (&obstack, buff, strlen (buff));
+ }
+#endif
+ delete_this_arg = 1;
+ }
+ arg_going = 1;
+ break;
+
+ case 'i':
+ obstack_grow (&obstack, input_filename, input_filename_length);
+ arg_going = 1;
+ break;
+
+ case 'I':
+ {
+ struct prefix_list *pl = include_prefix.plist;
+
+ if (gcc_exec_prefix)
+ {
+ do_spec_1 ("-iprefix", 1, NULL_PTR);
+ /* Make this a separate argument. */
+ do_spec_1 (" ", 0, NULL_PTR);
+ do_spec_1 (gcc_exec_prefix, 1, NULL_PTR);
+ do_spec_1 (" ", 0, NULL_PTR);
+ }
+
+ for (; pl; pl = pl->next)
+ {
+ do_spec_1 ("-isystem", 1, NULL_PTR);
+ /* Make this a separate argument. */
+ do_spec_1 (" ", 0, NULL_PTR);
+ do_spec_1 (pl->prefix, 1, NULL_PTR);
+ do_spec_1 (" ", 0, NULL_PTR);
+ }
+ }
+ break;
+
+ case 'o':
+ {
+ register int f;
+ for (f = 0; f < n_infiles; f++)
+ store_arg (outfiles[f], 0, 0);
+ }
+ break;
+
+ case 's':
+ this_is_library_file = 1;
+ break;
+
+ case 'w':
+ this_is_output_file = 1;
+ break;
+
+ case 'W':
+ {
+ int index = argbuf_index;
+ /* Handle the {...} following the %W. */
+ if (*p != '{')
+ abort ();
+ p = handle_braces (p + 1);
+ if (p == 0)
+ return -1;
+ /* If any args were output, mark the last one for deletion
+ on failure. */
+ if (argbuf_index != index)
+ record_temp_file (argbuf[argbuf_index - 1], 0, 1);
+ break;
+ }
+
+ /* %x{OPTION} records OPTION for %X to output. */
+ case 'x':
+ {
+ char *p1 = p;
+ char *string;
+
+ /* Skip past the option value and make a copy. */
+ if (*p != '{')
+ abort ();
+ while (*p++ != '}')
+ ;
+ string = save_string (p1 + 1, p - p1 - 2);
+
+ /* See if we already recorded this option. */
+ for (i = 0; i < n_linker_options; i++)
+ if (! strcmp (string, linker_options[i]))
+ {
+ free (string);
+ return 0;
+ }
+
+ /* This option is new; add it. */
+ n_linker_options++;
+ if (!linker_options)
+ linker_options
+ = (char **) xmalloc (n_linker_options * sizeof (char **));
+ else
+ linker_options
+ = (char **) xrealloc (linker_options,
+ n_linker_options * sizeof (char **));
+
+ linker_options[n_linker_options - 1] = string;
+ }
+ break;
+
+ /* Dump out the options accumulated previously using %x,
+ -Xlinker and -Wl,. */
+ case 'X':
+ for (i = 0; i < n_linker_options; i++)
+ {
+ do_spec_1 (linker_options[i], 1, NULL_PTR);
+ /* Make each accumulated option a separate argument. */
+ do_spec_1 (" ", 0, NULL_PTR);
+ }
+ break;
+
+ /* Dump out the options accumulated previously using -Wa,. */
+ case 'Y':
+ for (i = 0; i < n_assembler_options; i++)
+ {
+ do_spec_1 (assembler_options[i], 1, NULL_PTR);
+ /* Make each accumulated option a separate argument. */
+ do_spec_1 (" ", 0, NULL_PTR);
+ }
+ break;
+
+ /* Here are digits and numbers that just process
+ a certain constant string as a spec. */
+
+ case '1':
+ value = do_spec_1 (cc1_spec, 0, NULL_PTR);
+ if (value != 0)
+ return value;
+ break;
+
+ case '2':
+ value = do_spec_1 (cc1plus_spec, 0, NULL_PTR);
+ if (value != 0)
+ return value;
+ break;
+
+ case 'a':
+ value = do_spec_1 (asm_spec, 0, NULL_PTR);
+ if (value != 0)
+ return value;
+ break;
+
+ case 'A':
+ value = do_spec_1 (asm_final_spec, 0, NULL_PTR);
+ if (value != 0)
+ return value;
+ break;
+
+ case 'c':
+ value = do_spec_1 (signed_char_spec, 0, NULL_PTR);
+ if (value != 0)
+ return value;
+ break;
+
+ case 'C':
+ value = do_spec_1 (cpp_spec, 0, NULL_PTR);
+ if (value != 0)
+ return value;
+ break;
+
+ case 'E':
+ value = do_spec_1 (endfile_spec, 0, NULL_PTR);
+ if (value != 0)
+ return value;
+ break;
+
+ case 'l':
+ value = do_spec_1 (link_spec, 0, NULL_PTR);
+ if (value != 0)
+ return value;
+ break;
+
+ case 'L':
+ value = do_spec_1 (lib_spec, 0, NULL_PTR);
+ if (value != 0)
+ return value;
+ break;
+
+ case 'p':
+ {
+ char *x = (char *) alloca (strlen (cpp_predefines) + 1);
+ char *buf = x;
+ char *y;
+
+ /* Copy all of the -D options in CPP_PREDEFINES into BUF. */
+ y = cpp_predefines;
+ while (*y != 0)
+ {
+ if (! strncmp (y, "-D", 2))
+ /* Copy the whole option. */
+ while (*y && *y != ' ' && *y != '\t')
+ *x++ = *y++;
+ else if (*y == ' ' || *y == '\t')
+ /* Copy whitespace to the result. */
+ *x++ = *y++;
+ /* Don't copy other options. */
+ else
+ y++;
+ }
+
+ *x = 0;
+
+ value = do_spec_1 (buf, 0, NULL_PTR);
+ if (value != 0)
+ return value;
+ }
+ break;
+
+ case 'P':
+ {
+ char *x = (char *) alloca (strlen (cpp_predefines) * 4 + 1);
+ char *buf = x;
+ char *y;
+
+ /* Copy all of CPP_PREDEFINES into BUF,
+ but put __ after every -D and at the end of each arg. */
+ y = cpp_predefines;
+ while (*y != 0)
+ {
+ if (! strncmp (y, "-D", 2))
+ {
+ int flag = 0;
+
+ *x++ = *y++;
+ *x++ = *y++;
+
+ if (*y != '_'
+ || (*(y+1) != '_' && ! isupper (*(y+1))))
+ {
+ /* Stick __ at front of macro name. */
+ *x++ = '_';
+ *x++ = '_';
+ /* Arrange to stick __ at the end as well. */
+ flag = 1;
+ }
+
+ /* Copy the macro name. */
+ while (*y && *y != '=' && *y != ' ' && *y != '\t')
+ *x++ = *y++;
+
+ if (flag)
+ {
+ *x++ = '_';
+ *x++ = '_';
+ }
+
+ /* Copy the value given, if any. */
+ while (*y && *y != ' ' && *y != '\t')
+ *x++ = *y++;
+ }
+ else if (*y == ' ' || *y == '\t')
+ /* Copy whitespace to the result. */
+ *x++ = *y++;
+ /* Don't copy -A options */
+ else
+ y++;
+ }
+ *x++ = ' ';
+
+ /* Copy all of CPP_PREDEFINES into BUF,
+ but put __ after every -D. */
+ y = cpp_predefines;
+ while (*y != 0)
+ {
+ if (! strncmp (y, "-D", 2))
+ {
+ y += 2;
+
+ if (*y != '_'
+ || (*(y+1) != '_' && ! isupper (*(y+1))))
+ {
+ /* Stick -D__ at front of macro name. */
+ *x++ = '-';
+ *x++ = 'D';
+ *x++ = '_';
+ *x++ = '_';
+
+ /* Copy the macro name. */
+ while (*y && *y != '=' && *y != ' ' && *y != '\t')
+ *x++ = *y++;
+
+ /* Copy the value given, if any. */
+ while (*y && *y != ' ' && *y != '\t')
+ *x++ = *y++;
+ }
+ else
+ {
+ /* Do not copy this macro - we have just done it before */
+ while (*y && *y != ' ' && *y != '\t')
+ y++;
+ }
+ }
+ else if (*y == ' ' || *y == '\t')
+ /* Copy whitespace to the result. */
+ *x++ = *y++;
+ /* Don't copy -A options */
+ else
+ y++;
+ }
+ *x++ = ' ';
+
+ /* Copy all of the -A options in CPP_PREDEFINES into BUF. */
+ y = cpp_predefines;
+ while (*y != 0)
+ {
+ if (! strncmp (y, "-A", 2))
+ /* Copy the whole option. */
+ while (*y && *y != ' ' && *y != '\t')
+ *x++ = *y++;
+ else if (*y == ' ' || *y == '\t')
+ /* Copy whitespace to the result. */
+ *x++ = *y++;
+ /* Don't copy other options. */
+ else
+ y++;
+ }
+
+ *x = 0;
+
+ value = do_spec_1 (buf, 0, NULL_PTR);
+ if (value != 0)
+ return value;
+ }
+ break;
+
+ case 'S':
+ value = do_spec_1 (startfile_spec, 0, NULL_PTR);
+ if (value != 0)
+ return value;
+ break;
+
+ /* Here we define characters other than letters and digits. */
+
+ case '{':
+ p = handle_braces (p);
+ if (p == 0)
+ return -1;
+ break;
+
+ case '%':
+ obstack_1grow (&obstack, '%');
+ break;
+
+ case '*':
+ do_spec_1 (soft_matched_part, 1, NULL_PTR);
+ do_spec_1 (" ", 0, NULL_PTR);
+ break;
+
+ /* Process a string found as the value of a spec given by name.
+ This feature allows individual machine descriptions
+ to add and use their own specs.
+ %[...] modifies -D options the way %P does;
+ %(...) uses the spec unmodified. */
+ case '(':
+ case '[':
+ {
+ char *name = p;
+ struct spec_list *sl;
+ int len;
+
+ /* The string after the S/P is the name of a spec that is to be
+ processed. */
+ while (*p && *p != ')' && *p != ']')
+ p++;
+
+ /* See if it's in the list */
+ for (len = p - name, sl = specs; sl; sl = sl->next)
+ if (strncmp (sl->name, name, len) == 0 && !sl->name[len])
+ {
+ name = sl->spec;
+ break;
+ }
+
+ if (sl)
+ {
+ if (c == '(')
+ {
+ value = do_spec_1 (name, 0, NULL_PTR);
+ if (value != 0)
+ return value;
+ }
+ else
+ {
+ char *x = (char *) alloca (strlen (name) * 2 + 1);
+ char *buf = x;
+ char *y = name;
+
+ /* Copy all of NAME into BUF, but put __ after
+ every -D and at the end of each arg, */
+ while (1)
+ {
+ if (! strncmp (y, "-D", 2))
+ {
+ *x++ = '-';
+ *x++ = 'D';
+ *x++ = '_';
+ *x++ = '_';
+ y += 2;
+ }
+ else if (*y == ' ' || *y == 0)
+ {
+ *x++ = '_';
+ *x++ = '_';
+ if (*y == 0)
+ break;
+ else
+ *x++ = *y++;
+ }
+ else
+ *x++ = *y++;
+ }
+ *x = 0;
+
+ value = do_spec_1 (buf, 0, NULL_PTR);
+ if (value != 0)
+ return value;
+ }
+ }
+
+ /* Discard the closing paren or bracket. */
+ if (*p)
+ p++;
+ }
+ break;
+
+ case 'v':
+ {
+ int c1 = *p++; /* Select first or second version number. */
+ char *v = compiler_version;
+ char *q, *copy;
+ /* If desired, advance to second version number. */
+ if (c1 == '2')
+ {
+ /* Set P after the first period. */
+ while (*v != 0 && *v != ' ' && *v != '.')
+ v++;
+ if (*v == '.')
+ v++;
+ }
+ /* Set Q at the next period or at the end. */
+ q = v;
+ while (*q != 0 && *q != ' ' && *q != '.')
+ q++;
+ /* Empty string means zero. */
+ if (p == q)
+ {
+ v = "0";
+ q = v + 1;
+ }
+ /* Put that part into the command. */
+ obstack_grow (&obstack, v, q - v);
+ arg_going = 1;
+ }
+ break;
+
+ case '|':
+ if (input_from_pipe)
+ do_spec_1 ("-", 0, NULL_PTR);
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ case '\\':
+ /* Backslash: treat next character as ordinary. */
+ c = *p++;
+
+ /* fall through */
+ default:
+ /* Ordinary character: put it into the current argument. */
+ obstack_1grow (&obstack, c);
+ arg_going = 1;
+ }
+
+ return 0; /* End of string */
+}
+
+/* Return 0 if we call do_spec_1 and that returns -1. */
+
+static char *
+handle_braces (p)
+ register char *p;
+{
+ register char *q;
+ char *filter;
+ int pipe = 0;
+ int negate = 0;
+ int suffix = 0;
+
+ if (*p == '|')
+ /* A `|' after the open-brace means,
+ if the test fails, output a single minus sign rather than nothing.
+ This is used in %{|!pipe:...}. */
+ pipe = 1, ++p;
+
+ if (*p == '!')
+ /* A `!' after the open-brace negates the condition:
+ succeed if the specified switch is not present. */
+ negate = 1, ++p;
+
+ if (*p == '.')
+ /* A `.' after the open-brace means test against the current suffix. */
+ {
+ if (pipe)
+ abort ();
+
+ suffix = 1;
+ ++p;
+ }
+
+ filter = p;
+ while (*p != ':' && *p != '}') p++;
+ if (*p != '}')
+ {
+ register int count = 1;
+ q = p + 1;
+ while (count > 0)
+ {
+ if (*q == '{')
+ count++;
+ else if (*q == '}')
+ count--;
+ else if (*q == 0)
+ abort ();
+ q++;
+ }
+ }
+ else
+ q = p + 1;
+
+ if (suffix)
+ {
+ int found = (input_suffix != 0
+ && strlen (input_suffix) == p - filter
+ && strncmp (input_suffix, filter, p - filter) == 0);
+
+ if (p[0] == '}')
+ abort ();
+
+ if (negate != found
+ && do_spec_1 (save_string (p + 1, q - p - 2), 0, NULL_PTR) < 0)
+ return 0;
+
+ return q;
+ }
+ else if (p[-1] == '*' && p[0] == '}')
+ {
+ /* Substitute all matching switches as separate args. */
+ register int i;
+ --p;
+ for (i = 0; i < n_switches; i++)
+ if (!strncmp (switches[i].part1, filter, p - filter)
+ && check_live_switch (i, p - filter))
+ give_switch (i, 0);
+ }
+ else
+ {
+ /* Test for presence of the specified switch. */
+ register int i;
+ int present = 0;
+
+ /* If name specified ends in *, as in {x*:...},
+ check for %* and handle that case. */
+ if (p[-1] == '*' && !negate)
+ {
+ int substitution;
+ char *r = p;
+
+ /* First see whether we have %*. */
+ substitution = 0;
+ while (r < q)
+ {
+ if (*r == '%' && r[1] == '*')
+ substitution = 1;
+ r++;
+ }
+ /* If we do, handle that case. */
+ if (substitution)
+ {
+ /* Substitute all matching switches as separate args.
+ But do this by substituting for %*
+ in the text that follows the colon. */
+
+ unsigned hard_match_len = p - filter - 1;
+ char *string = save_string (p + 1, q - p - 2);
+
+ for (i = 0; i < n_switches; i++)
+ if (!strncmp (switches[i].part1, filter, hard_match_len)
+ && check_live_switch (i, -1))
+ {
+ do_spec_1 (string, 0, &switches[i].part1[hard_match_len]);
+ /* Pass any arguments this switch has. */
+ give_switch (i, 1);
+ }
+
+ return q;
+ }
+ }
+
+ /* If name specified ends in *, as in {x*:...},
+ check for presence of any switch name starting with x. */
+ if (p[-1] == '*')
+ {
+ for (i = 0; i < n_switches; i++)
+ {
+ unsigned hard_match_len = p - filter - 1;
+
+ if (!strncmp (switches[i].part1, filter, hard_match_len)
+ && check_live_switch (i, hard_match_len))
+ {
+ present = 1;
+ }
+ }
+ }
+ /* Otherwise, check for presence of exact name specified. */
+ else
+ {
+ for (i = 0; i < n_switches; i++)
+ {
+ if (!strncmp (switches[i].part1, filter, p - filter)
+ && switches[i].part1[p - filter] == 0
+ && check_live_switch (i, -1))
+ {
+ present = 1;
+ break;
+ }
+ }
+ }
+
+ /* If it is as desired (present for %{s...}, absent for %{-s...})
+ then substitute either the switch or the specified
+ conditional text. */
+ if (present != negate)
+ {
+ if (*p == '}')
+ {
+ give_switch (i, 0);
+ }
+ else
+ {
+ if (do_spec_1 (save_string (p + 1, q - p - 2), 0, NULL_PTR) < 0)
+ return 0;
+ }
+ }
+ else if (pipe)
+ {
+ /* Here if a %{|...} conditional fails: output a minus sign,
+ which means "standard output" or "standard input". */
+ do_spec_1 ("-", 0, NULL_PTR);
+ }
+ }
+
+ return q;
+}
+
+/* Return 0 iff switch number SWITCHNUM is obsoleted by a later switch
+ on the command line. PREFIX_LENGTH is the length of XXX in an {XXX*}
+ spec, or -1 if either exact match or %* is used.
+
+ A -O switch is obsoleted by a later -O switch. A -f, -m, or -W switch
+ whose value does not begin with "no-" is obsoleted by the same value
+ with the "no-", similarly for a switch with the "no-" prefix. */
+
+static int
+check_live_switch (switchnum, prefix_length)
+ int switchnum;
+ int prefix_length;
+{
+ char *name = switches[switchnum].part1;
+ int i;
+
+ /* In the common case of {<at-most-one-letter>*}, a negating
+ switch would always match, so ignore that case. We will just
+ send the conflicting switches to the compiler phase. */
+ if (prefix_length >= 0 && prefix_length <= 1)
+ return 1;
+
+ /* If we already processed this switch and determined if it was
+ live or not, return our past determination. */
+ if (switches[switchnum].live_cond != 0)
+ return switches[switchnum].live_cond > 0;
+
+ /* Now search for duplicate in a manner that depends on the name. */
+ switch (*name)
+ {
+ case 'O':
+ for (i = switchnum + 1; i < n_switches; i++)
+ if (switches[i].part1[0] == 'O')
+ {
+ switches[switchnum].valid = 1;
+ switches[switchnum].live_cond = -1;
+ return 0;
+ }
+ break;
+
+ case 'W': case 'f': case 'm':
+ if (! strncmp (name + 1, "no-", 3))
+ {
+ /* We have Xno-YYY, search for XYYY. */
+ for (i = switchnum + 1; i < n_switches; i++)
+ if (switches[i].part1[0] == name[0]
+ && ! strcmp (&switches[i].part1[1], &name[4]))
+ {
+ switches[switchnum].valid = 1;
+ switches[switchnum].live_cond = -1;
+ return 0;
+ }
+ }
+ else
+ {
+ /* We have XYYY, search for Xno-YYY. */
+ for (i = switchnum + 1; i < n_switches; i++)
+ if (switches[i].part1[0] == name[0]
+ && switches[i].part1[1] == 'n'
+ && switches[i].part1[2] == 'o'
+ && switches[i].part1[3] == '-'
+ && !strcmp (&switches[i].part1[4], &name[1]))
+ {
+ switches[switchnum].valid = 1;
+ switches[switchnum].live_cond = -1;
+ return 0;
+ }
+ }
+ break;
+ }
+
+ /* Otherwise the switch is live. */
+ switches[switchnum].live_cond = 1;
+ return 1;
+}
+
+/* Pass a switch to the current accumulating command
+ in the same form that we received it.
+ SWITCHNUM identifies the switch; it is an index into
+ the vector of switches gcc received, which is `switches'.
+ This cannot fail since it never finishes a command line.
+
+ If OMIT_FIRST_WORD is nonzero, then we omit .part1 of the argument. */
+
+static void
+give_switch (switchnum, omit_first_word)
+ int switchnum;
+ int omit_first_word;
+{
+ if (!omit_first_word)
+ {
+ do_spec_1 ("-", 0, NULL_PTR);
+ do_spec_1 (switches[switchnum].part1, 1, NULL_PTR);
+ }
+ do_spec_1 (" ", 0, NULL_PTR);
+ if (switches[switchnum].args != 0)
+ {
+ char **p;
+ for (p = switches[switchnum].args; *p; p++)
+ {
+ do_spec_1 (*p, 1, NULL_PTR);
+ do_spec_1 (" ", 0, NULL_PTR);
+ }
+ }
+ switches[switchnum].valid = 1;
+}
+
+/* Search for a file named NAME trying various prefixes including the
+ user's -B prefix and some standard ones.
+ Return the absolute file name found. If nothing is found, return NAME. */
+
+static char *
+find_file (name)
+ char *name;
+{
+ char *newname;
+
+ /* Try multilib_dir if it is defined. */
+ if (multilib_dir != NULL)
+ {
+ char *try;
+
+ try = (char *) alloca (strlen (multilib_dir) + strlen (name) + 2);
+ strcpy (try, multilib_dir);
+ strcat (try, "/");
+ strcat (try, name);
+
+ newname = find_a_file (&startfile_prefix, try, R_OK);
+
+ /* If we don't find it in the multi library dir, then fall
+ through and look for it in the normal places. */
+ if (newname != NULL)
+ return newname;
+ }
+
+ newname = find_a_file (&startfile_prefix, name, R_OK);
+ return newname ? newname : name;
+}
+
+/* Determine whether a directory exists. If LINKER, return 0 for
+ certain fixed names not needed by the linker. If not LINKER, it is
+ only important to return 0 if the host machine has a small ARG_MAX
+ limit. */
+
+static int
+is_directory (path1, path2, linker)
+ char *path1;
+ char *path2;
+ int linker;
+{
+ int len1 = strlen (path1);
+ int len2 = strlen (path2);
+ char *path = (char *) alloca (3 + len1 + len2);
+ char *cp;
+ struct stat st;
+
+#ifndef SMALL_ARG_MAX
+ if (! linker)
+ return 1;
+#endif
+
+ /* Construct the path from the two parts. Ensure the string ends with "/.".
+ The resulting path will be a directory even if the given path is a
+ symbolic link. */
+ bcopy (path1, path, len1);
+ bcopy (path2, path + len1, len2);
+ cp = path + len1 + len2;
+ if (cp[-1] != '/')
+ *cp++ = '/';
+ *cp++ = '.';
+ *cp = '\0';
+
+ /* Exclude directories that the linker is known to search. */
+ if (linker
+ && ((cp - path == 6 && strcmp (path, "/lib/.") == 0)
+ || (cp - path == 10 && strcmp (path, "/usr/lib/.") == 0)))
+ return 0;
+
+ return (stat (path, &st) >= 0 && S_ISDIR (st.st_mode));
+}
+
+/* On fatal signals, delete all the temporary files. */
+
+static void
+fatal_error (signum)
+ int signum;
+{
+ signal (signum, SIG_DFL);
+ delete_failure_queue ();
+ delete_temp_files ();
+ /* Get the same signal again, this time not handled,
+ so its normal effect occurs. */
+ kill (getpid (), signum);
+}
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ register int i;
+ int j;
+ int value;
+ int linker_was_run = 0;
+ char *explicit_link_files;
+ char *specs_file;
+ char *p;
+
+ p = argv[0] + strlen (argv[0]);
+ while (p != argv[0] && p[-1] != '/') --p;
+ programname = p;
+
+ if (signal (SIGINT, SIG_IGN) != SIG_IGN)
+ signal (SIGINT, fatal_error);
+#ifdef SIGHUP
+ if (signal (SIGHUP, SIG_IGN) != SIG_IGN)
+ signal (SIGHUP, fatal_error);
+#endif
+ if (signal (SIGTERM, SIG_IGN) != SIG_IGN)
+ signal (SIGTERM, fatal_error);
+#ifdef SIGPIPE
+ if (signal (SIGPIPE, SIG_IGN) != SIG_IGN)
+ signal (SIGPIPE, fatal_error);
+#endif
+
+ argbuf_length = 10;
+ argbuf = (char **) xmalloc (argbuf_length * sizeof (char *));
+
+ obstack_init (&obstack);
+
+ /* Set up to remember the pathname of gcc and any options
+ needed for collect. We use argv[0] instead of programname because
+ we need the complete pathname. */
+ obstack_init (&collect_obstack);
+ obstack_grow (&collect_obstack, "COLLECT_GCC=", sizeof ("COLLECT_GCC=")-1);
+ obstack_grow (&collect_obstack, argv[0], strlen (argv[0])+1);
+ putenv (obstack_finish (&collect_obstack));
+
+ /* Choose directory for temp files. */
+
+ choose_temp_base ();
+
+ /* Make a table of what switches there are (switches, n_switches).
+ Make a table of specified input files (infiles, n_infiles).
+ Decode switches that are handled locally. */
+
+ process_command (argc, argv);
+
+ /* Initialize the vector of specs to just the default.
+ This means one element containing 0s, as a terminator. */
+
+ compilers = (struct compiler *) xmalloc (sizeof default_compilers);
+ bcopy ((char *) default_compilers, (char *) compilers,
+ sizeof default_compilers);
+ n_compilers = n_default_compilers;
+
+ /* Read specs from a file if there is one. */
+
+ machine_suffix = concat (spec_machine, "/", concat (spec_version, "/", ""));
+ just_machine_suffix = concat (spec_machine, "/", "");
+
+ specs_file = find_a_file (&startfile_prefix, "specs", R_OK);
+ /* Read the specs file unless it is a default one. */
+ if (specs_file != 0 && strcmp (specs_file, "specs"))
+ read_specs (specs_file);
+
+ /* If not cross-compiling, look for startfiles in the standard places. */
+ /* The fact that these are done here, after reading the specs file,
+ means that it cannot be found in these directories.
+ But that's okay. It should never be there anyway. */
+ if (!cross_compile)
+ {
+#ifdef MD_EXEC_PREFIX
+ add_prefix (&exec_prefix, md_exec_prefix, 0, 0, NULL_PTR);
+ add_prefix (&startfile_prefix, md_exec_prefix, 0, 0, NULL_PTR);
+#endif
+
+#ifdef MD_STARTFILE_PREFIX
+ add_prefix (&startfile_prefix, md_startfile_prefix, 0, 0, NULL_PTR);
+#endif
+
+#ifdef MD_STARTFILE_PREFIX_1
+ add_prefix (&startfile_prefix, md_startfile_prefix_1, 0, 0, NULL_PTR);
+#endif
+
+ /* If standard_startfile_prefix is relative, base it on
+ standard_exec_prefix. This lets us move the installed tree
+ as a unit. If GCC_EXEC_PREFIX is defined, base
+ standard_startfile_prefix on that as well. */
+ if (*standard_startfile_prefix == '/')
+ add_prefix (&startfile_prefix, standard_startfile_prefix, 0, 0,
+ NULL_PTR);
+ else
+ {
+ if (gcc_exec_prefix)
+ add_prefix (&startfile_prefix,
+ concat (gcc_exec_prefix,
+ standard_startfile_prefix,
+ ""),
+ 0, 0, NULL_PTR);
+ add_prefix (&startfile_prefix,
+ concat (standard_exec_prefix,
+ machine_suffix,
+ standard_startfile_prefix),
+ 0, 0, NULL_PTR);
+ }
+
+ add_prefix (&startfile_prefix, standard_startfile_prefix_1, 0, 0,
+ NULL_PTR);
+ add_prefix (&startfile_prefix, standard_startfile_prefix_2, 0, 0,
+ NULL_PTR);
+#if 0 /* Can cause surprises, and one can use -B./ instead. */
+ add_prefix (&startfile_prefix, "./", 0, 1, NULL_PTR);
+#endif
+ }
+
+ /* Now we have the specs.
+ Set the `valid' bits for switches that match anything in any spec. */
+
+ validate_all_switches ();
+
+ /* Now that we have the switches and the specs, set
+ the subdirectory based on the options. */
+ set_multilib_dir ();
+
+ /* Warn about any switches that no pass was interested in. */
+
+ for (i = 0; i < n_switches; i++)
+ if (! switches[i].valid)
+ error ("unrecognized option `-%s'", switches[i].part1);
+
+ /* Obey some of the options. */
+
+ if (print_file_name)
+ {
+ printf ("%s\n", find_file (print_file_name));
+ exit (0);
+ }
+
+ if (print_prog_name)
+ {
+ char *newname = find_a_file (&exec_prefix, print_prog_name, X_OK);
+ printf ("%s\n", (newname ? newname : print_prog_name));
+ exit (0);
+ }
+
+ if (print_multi_lib)
+ {
+ print_multilib_info ();
+ exit (0);
+ }
+
+ if (print_multi_directory)
+ {
+ if (multilib_dir == NULL)
+ printf (".\n");
+ else
+ printf ("%s\n", multilib_dir);
+ exit (0);
+ }
+
+ if (verbose_flag)
+ {
+ fprintf (stderr, "gcc version %s\n", version_string);
+ if (n_infiles == 0)
+ exit (0);
+ }
+
+ if (n_infiles == 0)
+ fatal ("No input files");
+
+ /* Make a place to record the compiler output file names
+ that correspond to the input files. */
+
+ outfiles = (char **) xmalloc (n_infiles * sizeof (char *));
+ bzero ((char *) outfiles, n_infiles * sizeof (char *));
+
+ /* Record which files were specified explicitly as link input. */
+
+ explicit_link_files = xmalloc (n_infiles);
+ bzero (explicit_link_files, n_infiles);
+
+ for (i = 0; i < n_infiles; i++)
+ {
+ register struct compiler *cp = 0;
+ int this_file_error = 0;
+
+ /* Tell do_spec what to substitute for %i. */
+
+ input_filename = infiles[i].name;
+ input_filename_length = strlen (input_filename);
+ input_file_number = i;
+
+ /* Use the same thing in %o, unless cp->spec says otherwise. */
+
+ outfiles[i] = input_filename;
+
+ /* Figure out which compiler from the file's suffix. */
+
+ cp = lookup_compiler (infiles[i].name, input_filename_length,
+ infiles[i].language);
+
+ if (cp)
+ {
+ /* Ok, we found an applicable compiler. Run its spec. */
+ /* First say how much of input_filename to substitute for %b */
+ register char *p;
+ int len;
+
+ input_basename = input_filename;
+ for (p = input_filename; *p; p++)
+ if (*p == '/')
+ input_basename = p + 1;
+
+ /* Find a suffix starting with the last period,
+ and set basename_length to exclude that suffix. */
+ basename_length = strlen (input_basename);
+ p = input_basename + basename_length;
+ while (p != input_basename && *p != '.') --p;
+ if (*p == '.' && p != input_basename)
+ {
+ basename_length = p - input_basename;
+ input_suffix = p + 1;
+ }
+ else
+ input_suffix = "";
+
+ len = 0;
+ for (j = 0; j < sizeof cp->spec / sizeof cp->spec[0]; j++)
+ if (cp->spec[j])
+ len += strlen (cp->spec[j]);
+
+ p = (char *) xmalloc (len + 1);
+
+ len = 0;
+ for (j = 0; j < sizeof cp->spec / sizeof cp->spec[0]; j++)
+ if (cp->spec[j])
+ {
+ strcpy (p + len, cp->spec[j]);
+ len += strlen (cp->spec[j]);
+ }
+
+ value = do_spec (p);
+ free (p);
+ if (value < 0)
+ this_file_error = 1;
+ }
+
+ /* If this file's name does not contain a recognized suffix,
+ record it as explicit linker input. */
+
+ else
+ explicit_link_files[i] = 1;
+
+ /* Clear the delete-on-failure queue, deleting the files in it
+ if this compilation failed. */
+
+ if (this_file_error)
+ {
+ delete_failure_queue ();
+ error_count++;
+ }
+ /* If this compilation succeeded, don't delete those files later. */
+ clear_failure_queue ();
+ }
+
+ /* Run ld to link all the compiler output files. */
+
+ if (error_count == 0)
+ {
+ int tmp = execution_count;
+ int i;
+ int first_time;
+
+ /* Rebuild the COMPILER_PATH and LIBRARY_PATH environment variables
+ for collect. */
+ putenv_from_prefixes (&exec_prefix, "COMPILER_PATH=");
+ putenv_from_prefixes (&startfile_prefix, "LIBRARY_PATH=");
+
+ /* Build COLLECT_GCC_OPTIONS to have all of the options specified to
+ the compiler. */
+ obstack_grow (&collect_obstack, "COLLECT_GCC_OPTIONS=",
+ sizeof ("COLLECT_GCC_OPTIONS=")-1);
+
+ first_time = TRUE;
+ for (i = 0; i < n_switches; i++)
+ {
+ char **args;
+ if (!first_time)
+ obstack_grow (&collect_obstack, " ", 1);
+
+ first_time = FALSE;
+ obstack_grow (&collect_obstack, "-", 1);
+ obstack_grow (&collect_obstack, switches[i].part1,
+ strlen (switches[i].part1));
+
+ for (args = switches[i].args; args && *args; args++)
+ {
+ obstack_grow (&collect_obstack, " ", 1);
+ obstack_grow (&collect_obstack, *args, strlen (*args));
+ }
+ }
+ obstack_grow (&collect_obstack, "\0", 1);
+ putenv (obstack_finish (&collect_obstack));
+
+ value = do_spec (link_command_spec);
+ if (value < 0)
+ error_count = 1;
+ linker_was_run = (tmp != execution_count);
+ }
+
+ /* Warn if a -B option was specified but the prefix was never used. */
+ unused_prefix_warnings (&exec_prefix);
+ unused_prefix_warnings (&startfile_prefix);
+
+ /* If options said don't run linker,
+ complain about input files to be given to the linker. */
+
+ if (! linker_was_run && error_count == 0)
+ for (i = 0; i < n_infiles; i++)
+ if (explicit_link_files[i])
+ error ("%s: linker input file unused since linking not done",
+ outfiles[i]);
+
+ /* Delete some or all of the temporary files we made. */
+
+ if (error_count)
+ delete_failure_queue ();
+ delete_temp_files ();
+
+ exit (error_count > 0 ? (signal_count ? 2 : 1) : 0);
+ /* NOTREACHED */
+ return 0;
+}
+
+/* Find the proper compilation spec for the file name NAME,
+ whose length is LENGTH. LANGUAGE is the specified language,
+ or 0 if none specified. */
+
+static struct compiler *
+lookup_compiler (name, length, language)
+ char *name;
+ int length;
+ char *language;
+{
+ struct compiler *cp;
+
+ /* Look for the language, if one is spec'd. */
+ if (language != 0)
+ {
+ for (cp = compilers + n_compilers - 1; cp >= compilers; cp--)
+ {
+ if (language != 0)
+ {
+ if (cp->suffix[0] == '@'
+ && !strcmp (cp->suffix + 1, language))
+ return cp;
+ }
+ }
+ error ("language %s not recognized", language);
+ }
+
+ /* Look for a suffix. */
+ for (cp = compilers + n_compilers - 1; cp >= compilers; cp--)
+ {
+ if (/* The suffix `-' matches only the file name `-'. */
+ (!strcmp (cp->suffix, "-") && !strcmp (name, "-"))
+ ||
+ (strlen (cp->suffix) < length
+ /* See if the suffix matches the end of NAME. */
+ && !strcmp (cp->suffix,
+ name + length - strlen (cp->suffix))))
+ {
+ if (cp->spec[0][0] == '@')
+ {
+ struct compiler *new;
+ /* An alias entry maps a suffix to a language.
+ Search for the language; pass 0 for NAME and LENGTH
+ to avoid infinite recursion if language not found.
+ Construct the new compiler spec. */
+ language = cp->spec[0] + 1;
+ new = (struct compiler *) xmalloc (sizeof (struct compiler));
+ new->suffix = cp->suffix;
+ bcopy ((char *) lookup_compiler (NULL_PTR, 0, language)->spec,
+ (char *) new->spec, sizeof new->spec);
+ return new;
+ }
+ /* A non-alias entry: return it. */
+ return cp;
+ }
+ }
+
+ return 0;
+}
+
+char *
+xmalloc (size)
+ unsigned size;
+{
+ register char *value = (char *) malloc (size);
+ if (value == 0)
+ fatal ("virtual memory exhausted");
+ return value;
+}
+
+char *
+xrealloc (ptr, size)
+ char *ptr;
+ unsigned size;
+{
+ register char *value = (char *) realloc (ptr, size);
+ if (value == 0)
+ fatal ("virtual memory exhausted");
+ return value;
+}
+
+/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
+
+static char *
+concat (s1, s2, s3)
+ char *s1, *s2, *s3;
+{
+ int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
+ char *result = xmalloc (len1 + len2 + len3 + 1);
+
+ strcpy (result, s1);
+ strcpy (result + len1, s2);
+ strcpy (result + len1 + len2, s3);
+ *(result + len1 + len2 + len3) = 0;
+
+ return result;
+}
+
+static char *
+save_string (s, len)
+ char *s;
+ int len;
+{
+ register char *result = xmalloc (len + 1);
+
+ bcopy (s, result, len);
+ result[len] = 0;
+ return result;
+}
+
+static void
+pfatal_with_name (name)
+ char *name;
+{
+ char *s;
+
+ if (errno < sys_nerr)
+ s = concat ("%s: ", sys_errlist[errno], "");
+ else
+ s = "cannot open %s";
+ fatal (s, name);
+}
+
+static void
+perror_with_name (name)
+ char *name;
+{
+ char *s;
+
+ if (errno < sys_nerr)
+ s = concat ("%s: ", sys_errlist[errno], "");
+ else
+ s = "cannot open %s";
+ error (s, name);
+}
+
+static void
+perror_exec (name)
+ char *name;
+{
+ char *s;
+
+ if (errno < sys_nerr)
+ s = concat ("installation problem, cannot exec %s: ",
+ sys_errlist[errno], "");
+ else
+ s = "installation problem, cannot exec %s";
+ error (s, name);
+}
+
+/* More 'friendly' abort that prints the line and file.
+ config.h can #define abort fancy_abort if you like that sort of thing. */
+
+void
+fancy_abort ()
+{
+ fatal ("Internal gcc abort.");
+}
+
+#ifdef HAVE_VPRINTF
+
+/* Output an error message and exit */
+
+static void
+fatal VPROTO((char *format, ...))
+{
+#ifndef __STDC__
+ char *format;
+#endif
+ va_list ap;
+
+ VA_START (ap, format);
+
+#ifndef __STDC__
+ format = va_arg (ap, char*);
+#endif
+
+ fprintf (stderr, "%s: ", programname);
+ vfprintf (stderr, format, ap);
+ va_end (ap);
+ fprintf (stderr, "\n");
+ delete_temp_files ();
+ exit (1);
+}
+
+static void
+error VPROTO((char *format, ...))
+{
+#ifndef __STDC__
+ char *format;
+#endif
+ va_list ap;
+
+ VA_START (ap, format);
+
+#ifndef __STDC__
+ format = va_arg (ap, char*);
+#endif
+
+ fprintf (stderr, "%s: ", programname);
+ vfprintf (stderr, format, ap);
+ va_end (ap);
+
+ fprintf (stderr, "\n");
+}
+
+#else /* not HAVE_VPRINTF */
+
+static void
+fatal (msg, arg1, arg2)
+ char *msg, *arg1, *arg2;
+{
+ error (msg, arg1, arg2);
+ delete_temp_files ();
+ exit (1);
+}
+
+static void
+error (msg, arg1, arg2)
+ char *msg, *arg1, *arg2;
+{
+ fprintf (stderr, "%s: ", programname);
+ fprintf (stderr, msg, arg1, arg2);
+ fprintf (stderr, "\n");
+}
+
+#endif /* not HAVE_VPRINTF */
+
+
+static void
+validate_all_switches ()
+{
+ struct compiler *comp;
+ register char *p;
+ register char c;
+ struct spec_list *spec;
+
+ for (comp = compilers; comp->spec[0]; comp++)
+ {
+ int i;
+ for (i = 0; i < sizeof comp->spec / sizeof comp->spec[0] && comp->spec[i]; i++)
+ {
+ p = comp->spec[i];
+ while (c = *p++)
+ if (c == '%' && *p == '{')
+ /* We have a switch spec. */
+ validate_switches (p + 1);
+ }
+ }
+
+ /* look through the linked list of extra specs read from the specs file */
+ for (spec = specs; spec ; spec = spec->next)
+ {
+ p = spec->spec;
+ while (c = *p++)
+ if (c == '%' && *p == '{')
+ /* We have a switch spec. */
+ validate_switches (p + 1);
+ }
+
+ p = link_command_spec;
+ while (c = *p++)
+ if (c == '%' && *p == '{')
+ /* We have a switch spec. */
+ validate_switches (p + 1);
+
+ /* Now notice switches mentioned in the machine-specific specs. */
+
+ p = asm_spec;
+ while (c = *p++)
+ if (c == '%' && *p == '{')
+ /* We have a switch spec. */
+ validate_switches (p + 1);
+
+ p = asm_final_spec;
+ while (c = *p++)
+ if (c == '%' && *p == '{')
+ /* We have a switch spec. */
+ validate_switches (p + 1);
+
+ p = cpp_spec;
+ while (c = *p++)
+ if (c == '%' && *p == '{')
+ /* We have a switch spec. */
+ validate_switches (p + 1);
+
+ p = signed_char_spec;
+ while (c = *p++)
+ if (c == '%' && *p == '{')
+ /* We have a switch spec. */
+ validate_switches (p + 1);
+
+ p = cc1_spec;
+ while (c = *p++)
+ if (c == '%' && *p == '{')
+ /* We have a switch spec. */
+ validate_switches (p + 1);
+
+ p = cc1plus_spec;
+ while (c = *p++)
+ if (c == '%' && *p == '{')
+ /* We have a switch spec. */
+ validate_switches (p + 1);
+
+ p = link_spec;
+ while (c = *p++)
+ if (c == '%' && *p == '{')
+ /* We have a switch spec. */
+ validate_switches (p + 1);
+
+ p = lib_spec;
+ while (c = *p++)
+ if (c == '%' && *p == '{')
+ /* We have a switch spec. */
+ validate_switches (p + 1);
+
+ p = startfile_spec;
+ while (c = *p++)
+ if (c == '%' && *p == '{')
+ /* We have a switch spec. */
+ validate_switches (p + 1);
+}
+
+/* Look at the switch-name that comes after START
+ and mark as valid all supplied switches that match it. */
+
+static void
+validate_switches (start)
+ char *start;
+{
+ register char *p = start;
+ char *filter;
+ register int i;
+ int suffix = 0;
+
+ if (*p == '|')
+ ++p;
+
+ if (*p == '!')
+ ++p;
+
+ if (*p == '.')
+ suffix = 1, ++p;
+
+ filter = p;
+ while (*p != ':' && *p != '}') p++;
+
+ if (suffix)
+ ;
+ else if (p[-1] == '*')
+ {
+ /* Mark all matching switches as valid. */
+ --p;
+ for (i = 0; i < n_switches; i++)
+ if (!strncmp (switches[i].part1, filter, p - filter))
+ switches[i].valid = 1;
+ }
+ else
+ {
+ /* Mark an exact matching switch as valid. */
+ for (i = 0; i < n_switches; i++)
+ {
+ if (!strncmp (switches[i].part1, filter, p - filter)
+ && switches[i].part1[p - filter] == 0)
+ switches[i].valid = 1;
+ }
+ }
+}
+
+/* Check whether a particular argument was used. */
+
+static int
+used_arg (p, len)
+ char *p;
+ int len;
+{
+ int i;
+
+ for (i = 0; i < n_switches; i++)
+ if (! strncmp (switches[i].part1, p, len)
+ && strlen (switches[i].part1) == len)
+ return 1;
+ return 0;
+}
+
+/* Work out the subdirectory to use based on the
+ options. The format of multilib_select is a list of elements.
+ Each element is a subdirectory name followed by a list of options
+ followed by a semicolon. gcc will consider each line in turn. If
+ none of the options beginning with an exclamation point are
+ present, and all of the other options are present, that
+ subdirectory will be used. */
+
+static void
+set_multilib_dir ()
+{
+ char *p = multilib_select;
+ int this_path_len;
+ char *this_path, *this_arg;
+ int failed;
+
+ while (*p != '\0')
+ {
+ /* Ignore newlines. */
+ if (*p == '\n')
+ {
+ ++p;
+ continue;
+ }
+
+ /* Get the initial path. */
+ this_path = p;
+ while (*p != ' ')
+ {
+ if (*p == '\0')
+ abort ();
+ ++p;
+ }
+ this_path_len = p - this_path;
+
+ /* Check the arguments. */
+ failed = 0;
+ ++p;
+ while (*p != ';')
+ {
+ if (*p == '\0')
+ abort ();
+
+ if (failed)
+ {
+ ++p;
+ continue;
+ }
+
+ this_arg = p;
+ while (*p != ' ' && *p != ';')
+ {
+ if (*p == '\0')
+ abort ();
+ ++p;
+ }
+
+ if (*this_arg == '!')
+ failed = used_arg (this_arg + 1, p - (this_arg + 1));
+ else
+ failed = ! used_arg (this_arg, p - this_arg);
+
+ if (*p == ' ')
+ ++p;
+ }
+
+ if (! failed)
+ {
+ if (this_path_len != 1
+ || this_path[0] != '.')
+ {
+ multilib_dir = xmalloc (this_path_len + 1);
+ strncpy (multilib_dir, this_path, this_path_len);
+ multilib_dir[this_path_len] = '\0';
+ }
+ break;
+ }
+
+ ++p;
+ }
+}
+
+/* Print out the multiple library subdirectory selection
+ information. This prints out a series of lines. Each line looks
+ like SUBDIRECTORY;@OPTION@OPTION, with as many options as is
+ required. Only the desired options are printed out, the negative
+ matches. The options are print without a leading dash. There are
+ no spaces to make it easy to use the information in the shell.
+ Each subdirectory is printed only once. This assumes the ordering
+ generated by the genmultilib script. */
+
+static void
+print_multilib_info ()
+{
+ char *p = multilib_select;
+ char *last_path, *this_path;
+ int last_path_len, skip, use_arg;
+
+ while (*p != '\0')
+ {
+ /* Ignore newlines. */
+ if (*p == '\n')
+ {
+ ++p;
+ continue;
+ }
+
+ /* Get the initial path. */
+ this_path = p;
+ while (*p != ' ')
+ {
+ if (*p == '\0')
+ abort ();
+ ++p;
+ }
+
+ /* If this is a duplicate, skip it. */
+ skip = (p - this_path == last_path_len
+ && ! strncmp (last_path, this_path, last_path_len));
+
+ last_path = this_path;
+ last_path_len = p - this_path;
+
+ if (! skip)
+ {
+ char *p1;
+
+ for (p1 = last_path; p1 < p; p1++)
+ putchar (*p1);
+ putchar (';');
+ }
+
+ ++p;
+ while (*p != ';')
+ {
+ int use_arg;
+
+ if (*p == '\0')
+ abort ();
+
+ if (skip)
+ {
+ ++p;
+ continue;
+ }
+
+ use_arg = *p != '!';
+
+ if (use_arg)
+ putchar ('@');
+
+ while (*p != ' ' && *p != ';')
+ {
+ if (*p == '\0')
+ abort ();
+ if (use_arg)
+ putchar (*p);
+ ++p;
+ }
+
+ if (*p == ' ')
+ ++p;
+ }
+
+ if (! skip)
+ putchar ('\n');
+
+ ++p;
+ }
+}
diff --git a/gnu/usr.bin/cc/cc1/Makefile b/gnu/usr.bin/cc/cc1/Makefile
new file mode 100644
index 0000000..7cbe60b
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1/Makefile
@@ -0,0 +1,12 @@
+#
+# $FreeBSD$
+#
+
+PROG = cc1
+SRCS = c-aux-info.c c-convert.c c-decl.c c-iterate.c c-lang.c c-lex.c c-parse.c c-pragma.c c-typeck.c
+BINDIR= /usr/libexec
+NOMAN= 1
+DPADD+= ${LIBCC_INT} ${LIBGNUMALLOC}
+LDADD+= -lcc_int -lgnumalloc
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/cc/cc1/c-aux-info.c b/gnu/usr.bin/cc/cc1/c-aux-info.c
new file mode 100644
index 0000000..0e7df9b
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1/c-aux-info.c
@@ -0,0 +1,639 @@
+/* Generate information regarding function declarations and definitions based
+ on information stored in GCC's tree structure. This code implements the
+ -aux-info option.
+ Copyright (C) 1989, 1991, 1994 Free Software Foundation, Inc.
+ Contributed by Ron Guilmette (rfg@netcom.com).
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "config.h"
+#include "flags.h"
+#include "tree.h"
+#include "c-tree.h"
+
+extern char* xmalloc ();
+
+enum formals_style_enum {
+ ansi,
+ k_and_r_names,
+ k_and_r_decls
+};
+typedef enum formals_style_enum formals_style;
+
+
+static char* data_type;
+
+static char * concat ();
+static char * concat3 ();
+static char * gen_formal_list_for_type ();
+static int deserves_ellipsis ();
+static char * gen_formal_list_for_func_def ();
+static char * gen_type ();
+static char * gen_decl ();
+void gen_aux_info_record ();
+
+/* Take two strings and mash them together into a newly allocated area. */
+
+static char*
+concat (s1, s2)
+ char* s1;
+ char* s2;
+{
+ int size1, size2;
+ char* ret_val;
+
+ if (!s1)
+ s1 = "";
+ if (!s2)
+ s2 = "";
+
+ size1 = strlen (s1);
+ size2 = strlen (s2);
+ ret_val = xmalloc (size1 + size2 + 1);
+ strcpy (ret_val, s1);
+ strcpy (&ret_val[size1], s2);
+ return ret_val;
+}
+
+/* Take three strings and mash them together into a newly allocated area. */
+
+static char*
+concat3 (s1, s2, s3)
+ char* s1;
+ char* s2;
+ char* s3;
+{
+ int size1, size2, size3;
+ char* ret_val;
+
+ if (!s1)
+ s1 = "";
+ if (!s2)
+ s2 = "";
+ if (!s3)
+ s3 = "";
+
+ size1 = strlen (s1);
+ size2 = strlen (s2);
+ size3 = strlen (s3);
+ ret_val = xmalloc (size1 + size2 + size3 + 1);
+ strcpy (ret_val, s1);
+ strcpy (&ret_val[size1], s2);
+ strcpy (&ret_val[size1+size2], s3);
+ return ret_val;
+}
+
+/* Given a string representing an entire type or an entire declaration
+ which only lacks the actual "data-type" specifier (at its left end),
+ affix the data-type specifier to the left end of the given type
+ specification or object declaration.
+
+ Because of C language weirdness, the data-type specifier (which normally
+ goes in at the very left end) may have to be slipped in just to the
+ right of any leading "const" or "volatile" qualifiers (there may be more
+ than one). Actually this may not be strictly necessary because it seems
+ that GCC (at least) accepts `<data-type> const foo;' and treats it the
+ same as `const <data-type> foo;' but people are accustomed to seeing
+ `const char *foo;' and *not* `char const *foo;' so we try to create types
+ that look as expected. */
+
+static char*
+affix_data_type (type_or_decl)
+ char *type_or_decl;
+{
+ char *p = type_or_decl;
+ char *qualifiers_then_data_type;
+ char saved;
+
+ /* Skip as many leading const's or volatile's as there are. */
+
+ for (;;)
+ {
+ if (!strncmp (p, "volatile ", 9))
+ {
+ p += 9;
+ continue;
+ }
+ if (!strncmp (p, "const ", 6))
+ {
+ p += 6;
+ continue;
+ }
+ break;
+ }
+
+ /* p now points to the place where we can insert the data type. We have to
+ add a blank after the data-type of course. */
+
+ if (p == type_or_decl)
+ return concat3 (data_type, " ", type_or_decl);
+
+ saved = *p;
+ *p = '\0';
+ qualifiers_then_data_type = concat (type_or_decl, data_type);
+ *p = saved;
+ return concat3 (qualifiers_then_data_type, " ", p);
+}
+
+/* Given a tree node which represents some "function type", generate the
+ source code version of a formal parameter list (of some given style) for
+ this function type. Return the whole formal parameter list (including
+ a pair of surrounding parens) as a string. Note that if the style
+ we are currently aiming for is non-ansi, then we just return a pair
+ of empty parens here. */
+
+static char*
+gen_formal_list_for_type (fntype, style)
+ tree fntype;
+ formals_style style;
+{
+ char* formal_list = "";
+ tree formal_type;
+
+ if (style != ansi)
+ return "()";
+
+ formal_type = TYPE_ARG_TYPES (fntype);
+ while (formal_type && TREE_VALUE (formal_type) != void_type_node)
+ {
+ char* this_type;
+
+ if (*formal_list)
+ formal_list = concat (formal_list, ", ");
+
+ this_type = gen_type ("", TREE_VALUE (formal_type), ansi);
+ formal_list =
+ (strlen (this_type))
+ ? concat (formal_list, affix_data_type (this_type))
+ : concat (formal_list, data_type);
+
+ formal_type = TREE_CHAIN (formal_type);
+ }
+
+ /* If we got to here, then we are trying to generate an ANSI style formal
+ parameters list.
+
+ New style prototyped ANSI formal parameter lists should in theory always
+ contain some stuff between the opening and closing parens, even if it is
+ only "void".
+
+ The brutal truth though is that there is lots of old K&R code out there
+ which contains declarations of "pointer-to-function" parameters and
+ these almost never have fully specified formal parameter lists associated
+ with them. That is, the pointer-to-function parameters are declared
+ with just empty parameter lists.
+
+ In cases such as these, protoize should really insert *something* into
+ the vacant parameter lists, but what? It has no basis on which to insert
+ anything in particular.
+
+ Here, we make life easy for protoize by trying to distinguish between
+ K&R empty parameter lists and new-style prototyped parameter lists
+ that actually contain "void". In the latter case we (obviously) want
+ to output the "void" verbatim, and that what we do. In the former case,
+ we do our best to give protoize something nice to insert.
+
+ This "something nice" should be something that is still legal (when
+ re-compiled) but something that can clearly indicate to the user that
+ more typing information (for the parameter list) should be added (by
+ hand) at some convenient moment.
+
+ The string chosen here is a comment with question marks in it. */
+
+ if (!*formal_list)
+ {
+ if (TYPE_ARG_TYPES (fntype))
+ /* assert (TREE_VALUE (TYPE_ARG_TYPES (fntype)) == void_type_node); */
+ formal_list = "void";
+ else
+ formal_list = "/* ??? */";
+ }
+ else
+ {
+ /* If there were at least some parameters, and if the formals-types-list
+ petered out to a NULL (i.e. without being terminated by a
+ void_type_node) then we need to tack on an ellipsis. */
+ if (!formal_type)
+ formal_list = concat (formal_list, ", ...");
+ }
+
+ return concat3 (" (", formal_list, ")");
+}
+
+/* For the generation of an ANSI prototype for a function definition, we have
+ to look at the formal parameter list of the function's own "type" to
+ determine if the function's formal parameter list should end with an
+ ellipsis. Given a tree node, the following function will return non-zero
+ if the "function type" parameter list should end with an ellipsis. */
+
+static int
+deserves_ellipsis (fntype)
+ tree fntype;
+{
+ tree formal_type;
+
+ formal_type = TYPE_ARG_TYPES (fntype);
+ while (formal_type && TREE_VALUE (formal_type) != void_type_node)
+ formal_type = TREE_CHAIN (formal_type);
+
+ /* If there were at least some parameters, and if the formals-types-list
+ petered out to a NULL (i.e. without being terminated by a void_type_node)
+ then we need to tack on an ellipsis. */
+
+ return (!formal_type && TYPE_ARG_TYPES (fntype));
+}
+
+/* Generate a parameter list for a function definition (in some given style).
+
+ Note that this routine has to be separate (and different) from the code that
+ generates the prototype parameter lists for function declarations, because
+ in the case of a function declaration, all we have to go on is a tree node
+ representing the function's own "function type". This can tell us the types
+ of all of the formal parameters for the function, but it cannot tell us the
+ actual *names* of each of the formal parameters. We need to output those
+ parameter names for each function definition.
+
+ This routine gets a pointer to a tree node which represents the actual
+ declaration of the given function, and this DECL node has a list of formal
+ parameter (variable) declarations attached to it. These formal parameter
+ (variable) declaration nodes give us the actual names of the formal
+ parameters for the given function definition.
+
+ This routine returns a string which is the source form for the entire
+ function formal parameter list. */
+
+static char*
+gen_formal_list_for_func_def (fndecl, style)
+ tree fndecl;
+ formals_style style;
+{
+ char* formal_list = "";
+ tree formal_decl;
+
+ formal_decl = DECL_ARGUMENTS (fndecl);
+ while (formal_decl)
+ {
+ char *this_formal;
+
+ if (*formal_list && ((style == ansi) || (style == k_and_r_names)))
+ formal_list = concat (formal_list, ", ");
+ this_formal = gen_decl (formal_decl, 0, style);
+ if (style == k_and_r_decls)
+ formal_list = concat3 (formal_list, this_formal, "; ");
+ else
+ formal_list = concat (formal_list, this_formal);
+ formal_decl = TREE_CHAIN (formal_decl);
+ }
+ if (style == ansi)
+ {
+ if (!DECL_ARGUMENTS (fndecl))
+ formal_list = concat (formal_list, "void");
+ if (deserves_ellipsis (TREE_TYPE (fndecl)))
+ formal_list = concat (formal_list, ", ...");
+ }
+ if ((style == ansi) || (style == k_and_r_names))
+ formal_list = concat3 (" (", formal_list, ")");
+ return formal_list;
+}
+
+/* Generate a string which is the source code form for a given type (t). This
+ routine is ugly and complex because the C syntax for declarations is ugly
+ and complex. This routine is straightforward so long as *no* pointer types,
+ array types, or function types are involved.
+
+ In the simple cases, this routine will return the (string) value which was
+ passed in as the "ret_val" argument. Usually, this starts out either as an
+ empty string, or as the name of the declared item (i.e. the formal function
+ parameter variable).
+
+ This routine will also return with the global variable "data_type" set to
+ some string value which is the "basic" data-type of the given complete type.
+ This "data_type" string can be concatenated onto the front of the returned
+ string after this routine returns to its caller.
+
+ In complicated cases involving pointer types, array types, or function
+ types, the C declaration syntax requires an "inside out" approach, i.e. if
+ you have a type which is a "pointer-to-function" type, you need to handle
+ the "pointer" part first, but it also has to be "innermost" (relative to
+ the declaration stuff for the "function" type). Thus, is this case, you
+ must prepend a "(*" and append a ")" to the name of the item (i.e. formal
+ variable). Then you must append and prepend the other info for the
+ "function type" part of the overall type.
+
+ To handle the "innermost precedence" rules of complicated C declarators, we
+ do the following (in this routine). The input parameter called "ret_val"
+ is treated as a "seed". Each time gen_type is called (perhaps recursively)
+ some additional strings may be appended or prepended (or both) to the "seed"
+ string. If yet another (lower) level of the GCC tree exists for the given
+ type (as in the case of a pointer type, an array type, or a function type)
+ then the (wrapped) seed is passed to a (recursive) invocation of gen_type()
+ this recursive invocation may again "wrap" the (new) seed with yet more
+ declarator stuff, by appending, prepending (or both). By the time the
+ recursion bottoms out, the "seed value" at that point will have a value
+ which is (almost) the complete source version of the declarator (except
+ for the data_type info). Thus, this deepest "seed" value is simply passed
+ back up through all of the recursive calls until it is given (as the return
+ value) to the initial caller of the gen_type() routine. All that remains
+ to do at this point is for the initial caller to prepend the "data_type"
+ string onto the returned "seed". */
+
+static char*
+gen_type (ret_val, t, style)
+ char* ret_val;
+ tree t;
+ formals_style style;
+{
+ tree chain_p;
+
+ if (TYPE_NAME (t) && DECL_NAME (TYPE_NAME (t)))
+ data_type = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (t)));
+ else
+ {
+ switch (TREE_CODE (t))
+ {
+ case POINTER_TYPE:
+ if (TYPE_READONLY (t))
+ ret_val = concat ("const ", ret_val);
+ if (TYPE_VOLATILE (t))
+ ret_val = concat ("volatile ", ret_val);
+
+ ret_val = concat ("*", ret_val);
+
+ if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE || TREE_CODE (TREE_TYPE (t)) == FUNCTION_TYPE)
+ ret_val = concat3 ("(", ret_val, ")");
+
+ ret_val = gen_type (ret_val, TREE_TYPE (t), style);
+
+ return ret_val;
+
+ case ARRAY_TYPE:
+ if (TYPE_SIZE (t) == 0 || TREE_CODE (TYPE_SIZE (t)) != INTEGER_CST)
+ ret_val = gen_type (concat (ret_val, "[]"), TREE_TYPE (t), style);
+ else if (int_size_in_bytes (t) == 0)
+ ret_val = gen_type (concat (ret_val, "[0]"), TREE_TYPE (t), style);
+ else
+ {
+ int size = (int_size_in_bytes (t) / int_size_in_bytes (TREE_TYPE (t)));
+ char buff[10];
+ sprintf (buff, "[%d]", size);
+ ret_val = gen_type (concat (ret_val, buff),
+ TREE_TYPE (t), style);
+ }
+ break;
+
+ case FUNCTION_TYPE:
+ ret_val = gen_type (concat (ret_val, gen_formal_list_for_type (t, style)), TREE_TYPE (t), style);
+ break;
+
+ case IDENTIFIER_NODE:
+ data_type = IDENTIFIER_POINTER (t);
+ break;
+
+ /* The following three cases are complicated by the fact that a
+ user may do something really stupid, like creating a brand new
+ "anonymous" type specification in a formal argument list (or as
+ part of a function return type specification). For example:
+
+ int f (enum { red, green, blue } color);
+
+ In such cases, we have no name that we can put into the prototype
+ to represent the (anonymous) type. Thus, we have to generate the
+ whole darn type specification. Yuck! */
+
+ case RECORD_TYPE:
+ if (TYPE_NAME (t))
+ data_type = IDENTIFIER_POINTER (TYPE_NAME (t));
+ else
+ {
+ data_type = "";
+ chain_p = TYPE_FIELDS (t);
+ while (chain_p)
+ {
+ data_type = concat (data_type, gen_decl (chain_p, 0, ansi));
+ chain_p = TREE_CHAIN (chain_p);
+ data_type = concat (data_type, "; ");
+ }
+ data_type = concat3 ("{ ", data_type, "}");
+ }
+ data_type = concat ("struct ", data_type);
+ break;
+
+ case UNION_TYPE:
+ if (TYPE_NAME (t))
+ data_type = IDENTIFIER_POINTER (TYPE_NAME (t));
+ else
+ {
+ data_type = "";
+ chain_p = TYPE_FIELDS (t);
+ while (chain_p)
+ {
+ data_type = concat (data_type, gen_decl (chain_p, 0, ansi));
+ chain_p = TREE_CHAIN (chain_p);
+ data_type = concat (data_type, "; ");
+ }
+ data_type = concat3 ("{ ", data_type, "}");
+ }
+ data_type = concat ("union ", data_type);
+ break;
+
+ case ENUMERAL_TYPE:
+ if (TYPE_NAME (t))
+ data_type = IDENTIFIER_POINTER (TYPE_NAME (t));
+ else
+ {
+ data_type = "";
+ chain_p = TYPE_VALUES (t);
+ while (chain_p)
+ {
+ data_type = concat (data_type,
+ IDENTIFIER_POINTER (TREE_PURPOSE (chain_p)));
+ chain_p = TREE_CHAIN (chain_p);
+ if (chain_p)
+ data_type = concat (data_type, ", ");
+ }
+ data_type = concat3 ("{ ", data_type, " }");
+ }
+ data_type = concat ("enum ", data_type);
+ break;
+
+ case TYPE_DECL:
+ data_type = IDENTIFIER_POINTER (DECL_NAME (t));
+ break;
+
+ case INTEGER_TYPE:
+ data_type = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (t)));
+ /* Normally, `unsigned' is part of the deal. Not so if it comes
+ with `const' or `volatile'. */
+ if (TREE_UNSIGNED (t) && (TYPE_READONLY (t) || TYPE_VOLATILE (t)))
+ data_type = concat ("unsigned ", data_type);
+ break;
+
+ case REAL_TYPE:
+ data_type = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (t)));
+ break;
+
+ case VOID_TYPE:
+ data_type = "void";
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ if (TYPE_READONLY (t))
+ ret_val = concat ("const ", ret_val);
+ if (TYPE_VOLATILE (t))
+ ret_val = concat ("volatile ", ret_val);
+ return ret_val;
+}
+
+/* Generate a string (source) representation of an entire entity declaration
+ (using some particular style for function types).
+
+ The given entity may be either a variable or a function.
+
+ If the "is_func_definition" parameter is non-zero, assume that the thing
+ we are generating a declaration for is a FUNCTION_DECL node which is
+ associated with a function definition. In this case, we can assume that
+ an attached list of DECL nodes for function formal arguments is present. */
+
+static char*
+gen_decl (decl, is_func_definition, style)
+ tree decl;
+ int is_func_definition;
+ formals_style style;
+{
+ char* ret_val;
+
+ if (DECL_NAME (decl))
+ ret_val = IDENTIFIER_POINTER (DECL_NAME (decl));
+ else
+ ret_val = "";
+
+ /* If we are just generating a list of names of formal parameters, we can
+ simply return the formal parameter name (with no typing information
+ attached to it) now. */
+
+ if (style == k_and_r_names)
+ return ret_val;
+
+ /* Note that for the declaration of some entity (either a function or a
+ data object, like for instance a parameter) if the entity itself was
+ declared as either const or volatile, then const and volatile properties
+ are associated with just the declaration of the entity, and *not* with
+ the `type' of the entity. Thus, for such declared entities, we have to
+ generate the qualifiers here. */
+
+ if (TREE_THIS_VOLATILE (decl))
+ ret_val = concat ("volatile ", ret_val);
+ if (TREE_READONLY (decl))
+ ret_val = concat ("const ", ret_val);
+
+ data_type = "";
+
+ /* For FUNCTION_DECL nodes, there are two possible cases here. First, if
+ this FUNCTION_DECL node was generated from a function "definition", then
+ we will have a list of DECL_NODE's, one for each of the function's formal
+ parameters. In this case, we can print out not only the types of each
+ formal, but also each formal's name. In the second case, this
+ FUNCTION_DECL node came from an actual function declaration (and *not*
+ a definition). In this case, we do nothing here because the formal
+ argument type-list will be output later, when the "type" of the function
+ is added to the string we are building. Note that the ANSI-style formal
+ parameter list is considered to be a (suffix) part of the "type" of the
+ function. */
+
+ if (TREE_CODE (decl) == FUNCTION_DECL && is_func_definition)
+ {
+ ret_val = concat (ret_val, gen_formal_list_for_func_def (decl, ansi));
+
+ /* Since we have already added in the formals list stuff, here we don't
+ add the whole "type" of the function we are considering (which
+ would include its parameter-list info), rather, we only add in
+ the "type" of the "type" of the function, which is really just
+ the return-type of the function (and does not include the parameter
+ list info). */
+
+ ret_val = gen_type (ret_val, TREE_TYPE (TREE_TYPE (decl)), style);
+ }
+ else
+ ret_val = gen_type (ret_val, TREE_TYPE (decl), style);
+
+ ret_val = affix_data_type (ret_val);
+
+ if (DECL_REGISTER (decl))
+ ret_val = concat ("register ", ret_val);
+ if (TREE_PUBLIC (decl))
+ ret_val = concat ("extern ", ret_val);
+ if (TREE_CODE (decl) == FUNCTION_DECL && !TREE_PUBLIC (decl))
+ ret_val = concat ("static ", ret_val);
+
+ return ret_val;
+}
+
+extern FILE* aux_info_file;
+
+/* Generate and write a new line of info to the aux-info (.X) file. This
+ routine is called once for each function declaration, and once for each
+ function definition (even the implicit ones). */
+
+void
+gen_aux_info_record (fndecl, is_definition, is_implicit, is_prototyped)
+ tree fndecl;
+ int is_definition;
+ int is_implicit;
+ int is_prototyped;
+{
+ if (flag_gen_aux_info)
+ {
+ static int compiled_from_record = 0;
+
+ /* Each output .X file must have a header line. Write one now if we
+ have not yet done so. */
+
+ if (! compiled_from_record++)
+ {
+ /* The first line tells which directory file names are relative to.
+ Currently, -aux-info works only for files in the working
+ directory, so just use a `.' as a placeholder for now. */
+ fprintf (aux_info_file, "/* compiled from: . */\n");
+ }
+
+ /* Write the actual line of auxiliary info. */
+
+ fprintf (aux_info_file, "/* %s:%d:%c%c */ %s;",
+ DECL_SOURCE_FILE (fndecl),
+ DECL_SOURCE_LINE (fndecl),
+ (is_implicit) ? 'I' : (is_prototyped) ? 'N' : 'O',
+ (is_definition) ? 'F' : 'C',
+ gen_decl (fndecl, is_definition, ansi));
+
+ /* If this is an explicit function declaration, we need to also write
+ out an old-style (i.e. K&R) function header, just in case the user
+ wants to run unprotoize. */
+
+ if (is_definition)
+ {
+ fprintf (aux_info_file, " /*%s %s*/",
+ gen_formal_list_for_func_def (fndecl, k_and_r_names),
+ gen_formal_list_for_func_def (fndecl, k_and_r_decls));
+ }
+
+ fprintf (aux_info_file, "\n");
+ }
+}
diff --git a/gnu/usr.bin/cc/cc1/c-convert.c b/gnu/usr.bin/cc/cc1/c-convert.c
new file mode 100644
index 0000000..cfa590c
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1/c-convert.c
@@ -0,0 +1,95 @@
+/* Language-level data type conversion for GNU C.
+ Copyright (C) 1987, 1988, 1991 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file contains the functions for converting C expressions
+ to different data types. The only entry point is `convert'.
+ Every language front end must have a `convert' function
+ but what kind of conversions it does will depend on the language. */
+
+#include "config.h"
+#include "tree.h"
+#include "flags.h"
+#include "convert.h"
+
+/* Change of width--truncation and extension of integers or reals--
+ is represented with NOP_EXPR. Proper functioning of many things
+ assumes that no other conversions can be NOP_EXPRs.
+
+ Conversion between integer and pointer is represented with CONVERT_EXPR.
+ Converting integer to real uses FLOAT_EXPR
+ and real to integer uses FIX_TRUNC_EXPR.
+
+ Here is a list of all the functions that assume that widening and
+ narrowing is always done with a NOP_EXPR:
+ In convert.c, convert_to_integer.
+ In c-typeck.c, build_binary_op (boolean ops), and truthvalue_conversion.
+ In expr.c: expand_expr, for operands of a MULT_EXPR.
+ In fold-const.c: fold.
+ In tree.c: get_narrower and get_unwidened. */
+
+/* Subroutines of `convert'. */
+
+
+
+/* Create an expression whose value is that of EXPR,
+ converted to type TYPE. The TREE_TYPE of the value
+ is always TYPE. This function implements all reasonable
+ conversions; callers should filter out those that are
+ not permitted by the language being compiled. */
+
+tree
+convert (type, expr)
+ tree type, expr;
+{
+ register tree e = expr;
+ register enum tree_code code = TREE_CODE (type);
+
+ if (type == TREE_TYPE (expr)
+ || TREE_CODE (expr) == ERROR_MARK)
+ return expr;
+ if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (TREE_TYPE (expr)))
+ return fold (build1 (NOP_EXPR, type, expr));
+ if (TREE_CODE (TREE_TYPE (expr)) == ERROR_MARK)
+ return error_mark_node;
+ if (TREE_CODE (TREE_TYPE (expr)) == VOID_TYPE)
+ {
+ error ("void value not ignored as it ought to be");
+ return error_mark_node;
+ }
+ if (code == VOID_TYPE)
+ return build1 (CONVERT_EXPR, type, e);
+#if 0
+ /* This is incorrect. A truncation can't be stripped this way.
+ Extensions will be stripped by the use of get_unwidened. */
+ if (TREE_CODE (expr) == NOP_EXPR)
+ return convert (type, TREE_OPERAND (expr, 0));
+#endif
+ if (code == INTEGER_TYPE || code == ENUMERAL_TYPE)
+ return fold (convert_to_integer (type, e));
+ if (code == POINTER_TYPE)
+ return fold (convert_to_pointer (type, e));
+ if (code == REAL_TYPE)
+ return fold (convert_to_real (type, e));
+ if (code == COMPLEX_TYPE)
+ return fold (convert_to_complex (type, e));
+
+ error ("conversion to non-scalar type requested");
+ return error_mark_node;
+}
diff --git a/gnu/usr.bin/cc/cc1/c-decl.c b/gnu/usr.bin/cc/cc1/c-decl.c
new file mode 100644
index 0000000..c1a8dc9
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1/c-decl.c
@@ -0,0 +1,6797 @@
+/* Process declarations and variables for C compiler.
+ Copyright (C) 1988, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* Process declarations and symbol lookup for C front end.
+ Also constructs types; the standard scalar types at initialization,
+ and structure, union, array and enum types when they are declared. */
+
+/* ??? not all decl nodes are given the most useful possible
+ line numbers. For example, the CONST_DECLs for enum values. */
+
+#include "config.h"
+#include "tree.h"
+#include "flags.h"
+#include "c-tree.h"
+#include "c-lex.h"
+#include <stdio.h>
+
+/* In grokdeclarator, distinguish syntactic contexts of declarators. */
+enum decl_context
+{ NORMAL, /* Ordinary declaration */
+ FUNCDEF, /* Function definition */
+ PARM, /* Declaration of parm before function body */
+ FIELD, /* Declaration inside struct or union */
+ BITFIELD, /* Likewise but with specified width */
+ TYPENAME}; /* Typename (inside cast or sizeof) */
+
+#ifndef CHAR_TYPE_SIZE
+#define CHAR_TYPE_SIZE BITS_PER_UNIT
+#endif
+
+#ifndef SHORT_TYPE_SIZE
+#define SHORT_TYPE_SIZE (BITS_PER_UNIT * MIN ((UNITS_PER_WORD + 1) / 2, 2))
+#endif
+
+#ifndef INT_TYPE_SIZE
+#define INT_TYPE_SIZE BITS_PER_WORD
+#endif
+
+#ifndef LONG_TYPE_SIZE
+#define LONG_TYPE_SIZE BITS_PER_WORD
+#endif
+
+#ifndef LONG_LONG_TYPE_SIZE
+#define LONG_LONG_TYPE_SIZE (BITS_PER_WORD * 2)
+#endif
+
+#ifndef WCHAR_UNSIGNED
+#define WCHAR_UNSIGNED 0
+#endif
+
+#ifndef FLOAT_TYPE_SIZE
+#define FLOAT_TYPE_SIZE BITS_PER_WORD
+#endif
+
+#ifndef DOUBLE_TYPE_SIZE
+#define DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2)
+#endif
+
+#ifndef LONG_DOUBLE_TYPE_SIZE
+#define LONG_DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2)
+#endif
+
+/* We let tm.h override the types used here, to handle trivial differences
+ such as the choice of unsigned int or long unsigned int for size_t.
+ When machines start needing nontrivial differences in the size type,
+ it would be best to do something here to figure out automatically
+ from other information what type to use. */
+
+#ifndef SIZE_TYPE
+#define SIZE_TYPE "long unsigned int"
+#endif
+
+#ifndef PTRDIFF_TYPE
+#define PTRDIFF_TYPE "long int"
+#endif
+
+#ifndef WCHAR_TYPE
+#define WCHAR_TYPE "int"
+#endif
+
+/* a node which has tree code ERROR_MARK, and whose type is itself.
+ All erroneous expressions are replaced with this node. All functions
+ that accept nodes as arguments should avoid generating error messages
+ if this node is one of the arguments, since it is undesirable to get
+ multiple error messages from one error in the input. */
+
+tree error_mark_node;
+
+/* INTEGER_TYPE and REAL_TYPE nodes for the standard data types */
+
+tree short_integer_type_node;
+tree integer_type_node;
+tree long_integer_type_node;
+tree long_long_integer_type_node;
+
+tree short_unsigned_type_node;
+tree unsigned_type_node;
+tree long_unsigned_type_node;
+tree long_long_unsigned_type_node;
+
+tree ptrdiff_type_node;
+
+tree unsigned_char_type_node;
+tree signed_char_type_node;
+tree char_type_node;
+tree wchar_type_node;
+tree signed_wchar_type_node;
+tree unsigned_wchar_type_node;
+
+tree float_type_node;
+tree double_type_node;
+tree long_double_type_node;
+
+tree complex_integer_type_node;
+tree complex_float_type_node;
+tree complex_double_type_node;
+tree complex_long_double_type_node;
+
+tree intQI_type_node;
+tree intHI_type_node;
+tree intSI_type_node;
+tree intDI_type_node;
+
+tree unsigned_intQI_type_node;
+tree unsigned_intHI_type_node;
+tree unsigned_intSI_type_node;
+tree unsigned_intDI_type_node;
+
+/* a VOID_TYPE node. */
+
+tree void_type_node;
+
+/* Nodes for types `void *' and `const void *'. */
+
+tree ptr_type_node, const_ptr_type_node;
+
+/* Nodes for types `char *' and `const char *'. */
+
+tree string_type_node, const_string_type_node;
+
+/* Type `char[SOMENUMBER]'.
+ Used when an array of char is needed and the size is irrelevant. */
+
+tree char_array_type_node;
+
+/* Type `int[SOMENUMBER]' or something like it.
+ Used when an array of int needed and the size is irrelevant. */
+
+tree int_array_type_node;
+
+/* Type `wchar_t[SOMENUMBER]' or something like it.
+ Used when a wide string literal is created. */
+
+tree wchar_array_type_node;
+
+/* type `int ()' -- used for implicit declaration of functions. */
+
+tree default_function_type;
+
+/* function types `double (double)' and `double (double, double)', etc. */
+
+tree double_ftype_double, double_ftype_double_double;
+tree int_ftype_int, long_ftype_long;
+
+/* Function type `void (void *, void *, int)' and similar ones */
+
+tree void_ftype_ptr_ptr_int, int_ftype_ptr_ptr_int, void_ftype_ptr_int_int;
+
+/* Function type `char *(char *, char *)' and similar ones */
+tree string_ftype_ptr_ptr, int_ftype_string_string;
+
+/* Function type `int (const void *, const void *, size_t)' */
+tree int_ftype_cptr_cptr_sizet;
+
+/* Two expressions that are constants with value zero.
+ The first is of type `int', the second of type `void *'. */
+
+tree integer_zero_node;
+tree null_pointer_node;
+
+/* A node for the integer constant 1. */
+
+tree integer_one_node;
+
+/* Nonzero if we have seen an invalid cross reference
+ to a struct, union, or enum, but not yet printed the message. */
+
+tree pending_invalid_xref;
+/* File and line to appear in the eventual error message. */
+char *pending_invalid_xref_file;
+int pending_invalid_xref_line;
+
+/* While defining an enum type, this is 1 plus the last enumerator
+ constant value. Note that will do not have to save this or `enum_overflow'
+ around nested function definition since such a definition could only
+ occur in an enum value expression and we don't use these variables in
+ that case. */
+
+static tree enum_next_value;
+
+/* Nonzero means that there was overflow computing enum_next_value. */
+
+static int enum_overflow;
+
+/* Parsing a function declarator leaves a list of parameter names
+ or a chain or parameter decls here. */
+
+static tree last_function_parms;
+
+/* Parsing a function declarator leaves here a chain of structure
+ and enum types declared in the parmlist. */
+
+static tree last_function_parm_tags;
+
+/* After parsing the declarator that starts a function definition,
+ `start_function' puts here the list of parameter names or chain of decls.
+ `store_parm_decls' finds it here. */
+
+static tree current_function_parms;
+
+/* Similar, for last_function_parm_tags. */
+static tree current_function_parm_tags;
+
+/* Similar, for the file and line that the prototype came from if this is
+ an old-style definition. */
+static char *current_function_prototype_file;
+static int current_function_prototype_line;
+
+/* A list (chain of TREE_LIST nodes) of all LABEL_DECLs in the function
+ that have names. Here so we can clear out their names' definitions
+ at the end of the function. */
+
+static tree named_labels;
+
+/* A list of LABEL_DECLs from outer contexts that are currently shadowed. */
+
+static tree shadowed_labels;
+
+/* Nonzero when store_parm_decls is called indicates a varargs function.
+ Value not meaningful after store_parm_decls. */
+
+static int c_function_varargs;
+
+/* The FUNCTION_DECL for the function currently being compiled,
+ or 0 if between functions. */
+tree current_function_decl;
+
+/* Set to 0 at beginning of a function definition, set to 1 if
+ a return statement that specifies a return value is seen. */
+
+int current_function_returns_value;
+
+/* Set to 0 at beginning of a function definition, set to 1 if
+ a return statement with no argument is seen. */
+
+int current_function_returns_null;
+
+/* Set to nonzero by `grokdeclarator' for a function
+ whose return type is defaulted, if warnings for this are desired. */
+
+static int warn_about_return_type;
+
+/* Nonzero when starting a function declared `extern inline'. */
+
+static int current_extern_inline;
+
+/* For each binding contour we allocate a binding_level structure
+ * which records the names defined in that contour.
+ * Contours include:
+ * 0) the global one
+ * 1) one for each function definition,
+ * where internal declarations of the parameters appear.
+ * 2) one for each compound statement,
+ * to record its declarations.
+ *
+ * The current meaning of a name can be found by searching the levels from
+ * the current one out to the global one.
+ */
+
+/* Note that the information in the `names' component of the global contour
+ is duplicated in the IDENTIFIER_GLOBAL_VALUEs of all identifiers. */
+
+struct binding_level
+ {
+ /* A chain of _DECL nodes for all variables, constants, functions,
+ and typedef types. These are in the reverse of the order supplied.
+ */
+ tree names;
+
+ /* A list of structure, union and enum definitions,
+ * for looking up tag names.
+ * It is a chain of TREE_LIST nodes, each of whose TREE_PURPOSE is a name,
+ * or NULL_TREE; and whose TREE_VALUE is a RECORD_TYPE, UNION_TYPE,
+ * or ENUMERAL_TYPE node.
+ */
+ tree tags;
+
+ /* For each level, a list of shadowed outer-level local definitions
+ to be restored when this level is popped.
+ Each link is a TREE_LIST whose TREE_PURPOSE is an identifier and
+ whose TREE_VALUE is its old definition (a kind of ..._DECL node). */
+ tree shadowed;
+
+ /* For each level (except not the global one),
+ a chain of BLOCK nodes for all the levels
+ that were entered and exited one level down. */
+ tree blocks;
+
+ /* The BLOCK node for this level, if one has been preallocated.
+ If 0, the BLOCK is allocated (if needed) when the level is popped. */
+ tree this_block;
+
+ /* The binding level which this one is contained in (inherits from). */
+ struct binding_level *level_chain;
+
+ /* Nonzero for the level that holds the parameters of a function. */
+ char parm_flag;
+
+ /* Nonzero if this level "doesn't exist" for tags. */
+ char tag_transparent;
+
+ /* Nonzero if sublevels of this level "don't exist" for tags.
+ This is set in the parm level of a function definition
+ while reading the function body, so that the outermost block
+ of the function body will be tag-transparent. */
+ char subblocks_tag_transparent;
+
+ /* Nonzero means make a BLOCK for this level regardless of all else. */
+ char keep;
+
+ /* Nonzero means make a BLOCK if this level has any subblocks. */
+ char keep_if_subblocks;
+
+ /* Number of decls in `names' that have incomplete
+ structure or union types. */
+ int n_incomplete;
+
+ /* A list of decls giving the (reversed) specified order of parms,
+ not including any forward-decls in the parmlist.
+ This is so we can put the parms in proper order for assign_parms. */
+ tree parm_order;
+ };
+
+#define NULL_BINDING_LEVEL (struct binding_level *) NULL
+
+/* The binding level currently in effect. */
+
+static struct binding_level *current_binding_level;
+
+/* A chain of binding_level structures awaiting reuse. */
+
+static struct binding_level *free_binding_level;
+
+/* The outermost binding level, for names of file scope.
+ This is created when the compiler is started and exists
+ through the entire run. */
+
+static struct binding_level *global_binding_level;
+
+/* Binding level structures are initialized by copying this one. */
+
+static struct binding_level clear_binding_level
+ = {NULL, NULL, NULL, NULL, NULL, NULL_BINDING_LEVEL, 0, 0, 0, 0, 0, 0,
+ NULL};
+
+/* Nonzero means unconditionally make a BLOCK for the next level pushed. */
+
+static int keep_next_level_flag;
+
+/* Nonzero means make a BLOCK for the next level pushed
+ if it has subblocks. */
+
+static int keep_next_if_subblocks;
+
+/* The chain of outer levels of label scopes.
+ This uses the same data structure used for binding levels,
+ but it works differently: each link in the chain records
+ saved values of named_labels and shadowed_labels for
+ a label binding level outside the current one. */
+
+static struct binding_level *label_level_chain;
+
+/* Forward declarations. */
+
+static tree grokparms (), grokdeclarator ();
+tree pushdecl ();
+tree builtin_function ();
+void shadow_tag_warned ();
+
+static tree lookup_tag ();
+static tree lookup_tag_reverse ();
+tree lookup_name_current_level ();
+static char *redeclaration_error_message ();
+static void layout_array_type ();
+
+/* C-specific option variables. */
+
+/* Nonzero means allow type mismatches in conditional expressions;
+ just make their values `void'. */
+
+int flag_cond_mismatch;
+
+/* Nonzero means give `double' the same size as `float'. */
+
+int flag_short_double;
+
+/* Nonzero means don't recognize the keyword `asm'. */
+
+int flag_no_asm;
+
+/* Nonzero means don't recognize any builtin functions. */
+
+int flag_no_builtin;
+
+/* Nonzero means don't recognize the non-ANSI builtin functions.
+ -ansi sets this. */
+
+int flag_no_nonansi_builtin;
+
+/* Nonzero means do some things the same way PCC does. */
+
+int flag_traditional;
+
+/* Nonzero means to allow single precision math even if we're generally
+ being traditional. */
+int flag_allow_single_precision = 0;
+
+/* Nonzero means to treat bitfields as signed unless they say `unsigned'. */
+
+int flag_signed_bitfields = 1;
+int explicit_flag_signed_bitfields = 0;
+
+/* Nonzero means handle `#ident' directives. 0 means ignore them. */
+
+int flag_no_ident = 0;
+
+/* Nonzero means warn about implicit declarations. */
+
+int warn_implicit;
+
+/* Nonzero means give string constants the type `const char *'
+ to get extra warnings from them. These warnings will be too numerous
+ to be useful, except in thoroughly ANSIfied programs. */
+
+int warn_write_strings;
+
+/* Nonzero means warn about pointer casts that can drop a type qualifier
+ from the pointer target type. */
+
+int warn_cast_qual;
+
+/* Nonzero means warn when casting a function call to a type that does
+ not match the return type (e.g. (float)sqrt() or (anything*)malloc()
+ when there is no previous declaration of sqrt or malloc. */
+
+int warn_bad_function_cast;
+
+/* Warn about traditional constructs whose meanings changed in ANSI C. */
+
+int warn_traditional;
+
+/* Nonzero means warn about sizeof(function) or addition/subtraction
+ of function pointers. */
+
+int warn_pointer_arith;
+
+/* Nonzero means warn for non-prototype function decls
+ or non-prototyped defs without previous prototype. */
+
+int warn_strict_prototypes;
+
+/* Nonzero means warn for any global function def
+ without separate previous prototype decl. */
+
+int warn_missing_prototypes;
+
+/* Nonzero means warn for any global function def
+ without separate previous decl. */
+
+int warn_missing_declarations;
+
+/* Nonzero means warn about multiple (redundant) decls for the same single
+ variable or function. */
+
+int warn_redundant_decls = 0;
+
+/* Nonzero means warn about extern declarations of objects not at
+ file-scope level and about *all* declarations of functions (whether
+ extern or static) not at file-scope level. Note that we exclude
+ implicit function declarations. To get warnings about those, use
+ -Wimplicit. */
+
+int warn_nested_externs = 0;
+
+/* Warn about *printf or *scanf format/argument anomalies. */
+
+int warn_format;
+
+/* Warn about a subscript that has type char. */
+
+int warn_char_subscripts = 0;
+
+/* Warn if a type conversion is done that might have confusing results. */
+
+int warn_conversion;
+
+/* Warn if adding () is suggested. */
+
+int warn_parentheses;
+
+/* Warn if initializer is not completely bracketed. */
+
+int warn_missing_braces;
+
+/* Nonzero means `$' can be in an identifier.
+ See cccp.c for reasons why this breaks some obscure ANSI C programs. */
+
+#ifndef DOLLARS_IN_IDENTIFIERS
+#define DOLLARS_IN_IDENTIFIERS 1
+#endif
+int dollars_in_ident = DOLLARS_IN_IDENTIFIERS > 1;
+
+/* Decode the string P as a language-specific option for C.
+ Return 1 if it is recognized (and handle it);
+ return 0 if not recognized. */
+
+int
+c_decode_option (p)
+ char *p;
+{
+ if (!strcmp (p, "-ftraditional") || !strcmp (p, "-traditional"))
+ {
+ flag_traditional = 1;
+ flag_writable_strings = 1;
+#if DOLLARS_IN_IDENTIFIERS > 0
+ dollars_in_ident = 1;
+#endif
+ }
+ else if (!strcmp (p, "-fallow-single-precision"))
+ flag_allow_single_precision = 1;
+ else if (!strcmp (p, "-fnotraditional") || !strcmp (p, "-fno-traditional"))
+ {
+ flag_traditional = 0;
+ flag_writable_strings = 0;
+ dollars_in_ident = DOLLARS_IN_IDENTIFIERS > 1;
+ }
+ else if (!strcmp (p, "-fdollars-in-identifiers"))
+ {
+#if DOLLARS_IN_IDENTIFIERS > 0
+ dollars_in_ident = 1;
+#endif
+ }
+ else if (!strcmp (p, "-fno-dollars-in-identifiers"))
+ dollars_in_ident = 0;
+ else if (!strcmp (p, "-fsigned-char"))
+ flag_signed_char = 1;
+ else if (!strcmp (p, "-funsigned-char"))
+ flag_signed_char = 0;
+ else if (!strcmp (p, "-fno-signed-char"))
+ flag_signed_char = 0;
+ else if (!strcmp (p, "-fno-unsigned-char"))
+ flag_signed_char = 1;
+ else if (!strcmp (p, "-fsigned-bitfields")
+ || !strcmp (p, "-fno-unsigned-bitfields"))
+ {
+ flag_signed_bitfields = 1;
+ explicit_flag_signed_bitfields = 1;
+ }
+ else if (!strcmp (p, "-funsigned-bitfields")
+ || !strcmp (p, "-fno-signed-bitfields"))
+ {
+ flag_signed_bitfields = 0;
+ explicit_flag_signed_bitfields = 1;
+ }
+ else if (!strcmp (p, "-fshort-enums"))
+ flag_short_enums = 1;
+ else if (!strcmp (p, "-fno-short-enums"))
+ flag_short_enums = 0;
+ else if (!strcmp (p, "-fcond-mismatch"))
+ flag_cond_mismatch = 1;
+ else if (!strcmp (p, "-fno-cond-mismatch"))
+ flag_cond_mismatch = 0;
+ else if (!strcmp (p, "-fshort-double"))
+ flag_short_double = 1;
+ else if (!strcmp (p, "-fno-short-double"))
+ flag_short_double = 0;
+ else if (!strcmp (p, "-fasm"))
+ flag_no_asm = 0;
+ else if (!strcmp (p, "-fno-asm"))
+ flag_no_asm = 1;
+ else if (!strcmp (p, "-fbuiltin"))
+ flag_no_builtin = 0;
+ else if (!strcmp (p, "-fno-builtin"))
+ flag_no_builtin = 1;
+ else if (!strcmp (p, "-fno-ident"))
+ flag_no_ident = 1;
+ else if (!strcmp (p, "-fident"))
+ flag_no_ident = 0;
+ else if (!strcmp (p, "-ansi"))
+ flag_no_asm = 1, flag_no_nonansi_builtin = 1, dollars_in_ident = 0;
+ else if (!strcmp (p, "-Wimplicit"))
+ warn_implicit = 1;
+ else if (!strcmp (p, "-Wno-implicit"))
+ warn_implicit = 0;
+ else if (!strcmp (p, "-Wwrite-strings"))
+ warn_write_strings = 1;
+ else if (!strcmp (p, "-Wno-write-strings"))
+ warn_write_strings = 0;
+ else if (!strcmp (p, "-Wcast-qual"))
+ warn_cast_qual = 1;
+ else if (!strcmp (p, "-Wno-cast-qual"))
+ warn_cast_qual = 0;
+ else if (!strcmp (p, "-Wbad-function-cast"))
+ warn_bad_function_cast = 1;
+ else if (!strcmp (p, "-Wno-bad-function-cast"))
+ warn_bad_function_cast = 0;
+ else if (!strcmp (p, "-Wpointer-arith"))
+ warn_pointer_arith = 1;
+ else if (!strcmp (p, "-Wno-pointer-arith"))
+ warn_pointer_arith = 0;
+ else if (!strcmp (p, "-Wstrict-prototypes"))
+ warn_strict_prototypes = 1;
+ else if (!strcmp (p, "-Wno-strict-prototypes"))
+ warn_strict_prototypes = 0;
+ else if (!strcmp (p, "-Wmissing-prototypes"))
+ warn_missing_prototypes = 1;
+ else if (!strcmp (p, "-Wno-missing-prototypes"))
+ warn_missing_prototypes = 0;
+ else if (!strcmp (p, "-Wmissing-declarations"))
+ warn_missing_declarations = 1;
+ else if (!strcmp (p, "-Wno-missing-declarations"))
+ warn_missing_declarations = 0;
+ else if (!strcmp (p, "-Wredundant-decls"))
+ warn_redundant_decls = 1;
+ else if (!strcmp (p, "-Wno-redundant-decls"))
+ warn_redundant_decls = 0;
+ else if (!strcmp (p, "-Wnested-externs"))
+ warn_nested_externs = 1;
+ else if (!strcmp (p, "-Wno-nested-externs"))
+ warn_nested_externs = 0;
+ else if (!strcmp (p, "-Wtraditional"))
+ warn_traditional = 1;
+ else if (!strcmp (p, "-Wno-traditional"))
+ warn_traditional = 0;
+ else if (!strcmp (p, "-Wformat"))
+ warn_format = 1;
+ else if (!strcmp (p, "-Wno-format"))
+ warn_format = 0;
+ else if (!strcmp (p, "-Wchar-subscripts"))
+ warn_char_subscripts = 1;
+ else if (!strcmp (p, "-Wno-char-subscripts"))
+ warn_char_subscripts = 0;
+ else if (!strcmp (p, "-Wconversion"))
+ warn_conversion = 1;
+ else if (!strcmp (p, "-Wno-conversion"))
+ warn_conversion = 0;
+ else if (!strcmp (p, "-Wparentheses"))
+ warn_parentheses = 1;
+ else if (!strcmp (p, "-Wno-parentheses"))
+ warn_parentheses = 0;
+ else if (!strcmp (p, "-Wreturn-type"))
+ warn_return_type = 1;
+ else if (!strcmp (p, "-Wno-return-type"))
+ warn_return_type = 0;
+ else if (!strcmp (p, "-Wcomment"))
+ ; /* cpp handles this one. */
+ else if (!strcmp (p, "-Wno-comment"))
+ ; /* cpp handles this one. */
+ else if (!strcmp (p, "-Wcomments"))
+ ; /* cpp handles this one. */
+ else if (!strcmp (p, "-Wno-comments"))
+ ; /* cpp handles this one. */
+ else if (!strcmp (p, "-Wtrigraphs"))
+ ; /* cpp handles this one. */
+ else if (!strcmp (p, "-Wno-trigraphs"))
+ ; /* cpp handles this one. */
+ else if (!strcmp (p, "-Wimport"))
+ ; /* cpp handles this one. */
+ else if (!strcmp (p, "-Wno-import"))
+ ; /* cpp handles this one. */
+ else if (!strcmp (p, "-Wmissing-braces"))
+ warn_missing_braces = 1;
+ else if (!strcmp (p, "-Wno-missing-braces"))
+ warn_missing_braces = 0;
+ else if (!strcmp (p, "-Wall"))
+ {
+ extra_warnings = 1;
+ /* We save the value of warn_uninitialized, since if they put
+ -Wuninitialized on the command line, we need to generate a
+ warning about not using it without also specifying -O. */
+ if (warn_uninitialized != 1)
+ warn_uninitialized = 2;
+ warn_implicit = 1;
+ warn_return_type = 1;
+ warn_unused = 1;
+ warn_switch = 1;
+ warn_format = 1;
+ warn_char_subscripts = 1;
+ warn_parentheses = 1;
+ warn_missing_braces = 1;
+ }
+ else
+ return 0;
+
+ return 1;
+}
+
+/* Hooks for print_node. */
+
+void
+print_lang_decl (file, node, indent)
+ FILE *file;
+ tree node;
+ int indent;
+{
+}
+
+void
+print_lang_type (file, node, indent)
+ FILE *file;
+ tree node;
+ int indent;
+{
+}
+
+void
+print_lang_identifier (file, node, indent)
+ FILE *file;
+ tree node;
+ int indent;
+{
+ print_node (file, "global", IDENTIFIER_GLOBAL_VALUE (node), indent + 4);
+ print_node (file, "local", IDENTIFIER_LOCAL_VALUE (node), indent + 4);
+ print_node (file, "label", IDENTIFIER_LABEL_VALUE (node), indent + 4);
+ print_node (file, "implicit", IDENTIFIER_IMPLICIT_DECL (node), indent + 4);
+ print_node (file, "error locus", IDENTIFIER_ERROR_LOCUS (node), indent + 4);
+ print_node (file, "limbo value", IDENTIFIER_LIMBO_VALUE (node), indent + 4);
+}
+
+/* Hook called at end of compilation to assume 1 elt
+ for a top-level array decl that wasn't complete before. */
+
+void
+finish_incomplete_decl (decl)
+ tree decl;
+{
+ if (TREE_CODE (decl) == VAR_DECL && TREE_TYPE (decl) != error_mark_node)
+ {
+ tree type = TREE_TYPE (decl);
+ if (TREE_CODE (type) == ARRAY_TYPE
+ && TYPE_DOMAIN (type) == 0
+ && TREE_CODE (decl) != TYPE_DECL)
+ {
+ complete_array_type (type, NULL_TREE, 1);
+
+ layout_decl (decl, 0);
+ }
+ }
+}
+
+/* Create a new `struct binding_level'. */
+
+static
+struct binding_level *
+make_binding_level ()
+{
+ /* NOSTRICT */
+ return (struct binding_level *) xmalloc (sizeof (struct binding_level));
+}
+
+/* Nonzero if we are currently in the global binding level. */
+
+int
+global_bindings_p ()
+{
+ return current_binding_level == global_binding_level;
+}
+
+void
+keep_next_level ()
+{
+ keep_next_level_flag = 1;
+}
+
+/* Nonzero if the current level needs to have a BLOCK made. */
+
+int
+kept_level_p ()
+{
+ return ((current_binding_level->keep_if_subblocks
+ && current_binding_level->blocks != 0)
+ || current_binding_level->keep
+ || current_binding_level->names != 0
+ || (current_binding_level->tags != 0
+ && !current_binding_level->tag_transparent));
+}
+
+/* Identify this binding level as a level of parameters.
+ DEFINITION_FLAG is 1 for a definition, 0 for a declaration.
+ But it turns out there is no way to pass the right value for
+ DEFINITION_FLAG, so we ignore it. */
+
+void
+declare_parm_level (definition_flag)
+ int definition_flag;
+{
+ current_binding_level->parm_flag = 1;
+}
+
+/* Nonzero if currently making parm declarations. */
+
+int
+in_parm_level_p ()
+{
+ return current_binding_level->parm_flag;
+}
+
+/* Enter a new binding level.
+ If TAG_TRANSPARENT is nonzero, do so only for the name space of variables,
+ not for that of tags. */
+
+void
+pushlevel (tag_transparent)
+ int tag_transparent;
+{
+ register struct binding_level *newlevel = NULL_BINDING_LEVEL;
+
+ /* If this is the top level of a function,
+ just make sure that NAMED_LABELS is 0. */
+
+ if (current_binding_level == global_binding_level)
+ {
+ named_labels = 0;
+ }
+
+ /* Reuse or create a struct for this binding level. */
+
+ if (free_binding_level)
+ {
+ newlevel = free_binding_level;
+ free_binding_level = free_binding_level->level_chain;
+ }
+ else
+ {
+ newlevel = make_binding_level ();
+ }
+
+ /* Add this level to the front of the chain (stack) of levels that
+ are active. */
+
+ *newlevel = clear_binding_level;
+ newlevel->tag_transparent
+ = (tag_transparent
+ || (current_binding_level
+ ? current_binding_level->subblocks_tag_transparent
+ : 0));
+ newlevel->level_chain = current_binding_level;
+ current_binding_level = newlevel;
+ newlevel->keep = keep_next_level_flag;
+ keep_next_level_flag = 0;
+ newlevel->keep_if_subblocks = keep_next_if_subblocks;
+ keep_next_if_subblocks = 0;
+}
+
+/* Exit a binding level.
+ Pop the level off, and restore the state of the identifier-decl mappings
+ that were in effect when this level was entered.
+
+ If KEEP is nonzero, this level had explicit declarations, so
+ and create a "block" (a BLOCK node) for the level
+ to record its declarations and subblocks for symbol table output.
+
+ If FUNCTIONBODY is nonzero, this level is the body of a function,
+ so create a block as if KEEP were set and also clear out all
+ label names.
+
+ If REVERSE is nonzero, reverse the order of decls before putting
+ them into the BLOCK. */
+
+tree
+poplevel (keep, reverse, functionbody)
+ int keep;
+ int reverse;
+ int functionbody;
+{
+ register tree link;
+ /* The chain of decls was accumulated in reverse order.
+ Put it into forward order, just for cleanliness. */
+ tree decls;
+ tree tags = current_binding_level->tags;
+ tree subblocks = current_binding_level->blocks;
+ tree block = 0;
+ tree decl;
+ int block_previously_created;
+
+ keep |= current_binding_level->keep;
+
+ /* This warning is turned off because it causes warnings for
+ declarations like `extern struct foo *x'. */
+#if 0
+ /* Warn about incomplete structure types in this level. */
+ for (link = tags; link; link = TREE_CHAIN (link))
+ if (TYPE_SIZE (TREE_VALUE (link)) == 0)
+ {
+ tree type = TREE_VALUE (link);
+ char *errmsg;
+ switch (TREE_CODE (type))
+ {
+ case RECORD_TYPE:
+ errmsg = "`struct %s' incomplete in scope ending here";
+ break;
+ case UNION_TYPE:
+ errmsg = "`union %s' incomplete in scope ending here";
+ break;
+ case ENUMERAL_TYPE:
+ errmsg = "`enum %s' incomplete in scope ending here";
+ break;
+ }
+ if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
+ error (errmsg, IDENTIFIER_POINTER (TYPE_NAME (type)));
+ else
+ /* If this type has a typedef-name, the TYPE_NAME is a TYPE_DECL. */
+ error (errmsg, IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))));
+ }
+#endif /* 0 */
+
+ /* Get the decls in the order they were written.
+ Usually current_binding_level->names is in reverse order.
+ But parameter decls were previously put in forward order. */
+
+ if (reverse)
+ current_binding_level->names
+ = decls = nreverse (current_binding_level->names);
+ else
+ decls = current_binding_level->names;
+
+ /* Output any nested inline functions within this block
+ if they weren't already output. */
+
+ for (decl = decls; decl; decl = TREE_CHAIN (decl))
+ if (TREE_CODE (decl) == FUNCTION_DECL
+ && ! TREE_ASM_WRITTEN (decl)
+ && DECL_INITIAL (decl) != 0
+ && TREE_ADDRESSABLE (decl))
+ {
+ /* If this decl was copied from a file-scope decl
+ on account of a block-scope extern decl,
+ propagate TREE_ADDRESSABLE to the file-scope decl. */
+ if (DECL_ABSTRACT_ORIGIN (decl) != 0)
+ TREE_ADDRESSABLE (DECL_ABSTRACT_ORIGIN (decl)) = 1;
+ else
+ {
+ push_function_context ();
+ output_inline_function (decl);
+ pop_function_context ();
+ }
+ }
+
+ /* If there were any declarations or structure tags in that level,
+ or if this level is a function body,
+ create a BLOCK to record them for the life of this function. */
+
+ block = 0;
+ block_previously_created = (current_binding_level->this_block != 0);
+ if (block_previously_created)
+ block = current_binding_level->this_block;
+ else if (keep || functionbody
+ || (current_binding_level->keep_if_subblocks && subblocks != 0))
+ block = make_node (BLOCK);
+ if (block != 0)
+ {
+ BLOCK_VARS (block) = decls;
+ BLOCK_TYPE_TAGS (block) = tags;
+ BLOCK_SUBBLOCKS (block) = subblocks;
+ remember_end_note (block);
+ }
+
+ /* In each subblock, record that this is its superior. */
+
+ for (link = subblocks; link; link = TREE_CHAIN (link))
+ BLOCK_SUPERCONTEXT (link) = block;
+
+ /* Clear out the meanings of the local variables of this level. */
+
+ for (link = decls; link; link = TREE_CHAIN (link))
+ {
+ if (DECL_NAME (link) != 0)
+ {
+ /* If the ident. was used or addressed via a local extern decl,
+ don't forget that fact. */
+ if (DECL_EXTERNAL (link))
+ {
+ if (TREE_USED (link))
+ TREE_USED (DECL_NAME (link)) = 1;
+ if (TREE_ADDRESSABLE (link))
+ TREE_ADDRESSABLE (DECL_ASSEMBLER_NAME (link)) = 1;
+ }
+ IDENTIFIER_LOCAL_VALUE (DECL_NAME (link)) = 0;
+ }
+ }
+
+ /* Restore all name-meanings of the outer levels
+ that were shadowed by this level. */
+
+ for (link = current_binding_level->shadowed; link; link = TREE_CHAIN (link))
+ IDENTIFIER_LOCAL_VALUE (TREE_PURPOSE (link)) = TREE_VALUE (link);
+
+ /* If the level being exited is the top level of a function,
+ check over all the labels, and clear out the current
+ (function local) meanings of their names. */
+
+ if (functionbody)
+ {
+ /* If this is the top level block of a function,
+ the vars are the function's parameters.
+ Don't leave them in the BLOCK because they are
+ found in the FUNCTION_DECL instead. */
+
+ BLOCK_VARS (block) = 0;
+
+ /* Clear out the definitions of all label names,
+ since their scopes end here,
+ and add them to BLOCK_VARS. */
+
+ for (link = named_labels; link; link = TREE_CHAIN (link))
+ {
+ register tree label = TREE_VALUE (link);
+
+ if (DECL_INITIAL (label) == 0)
+ {
+ error_with_decl (label, "label `%s' used but not defined");
+ /* Avoid crashing later. */
+ define_label (input_filename, lineno,
+ DECL_NAME (label));
+ }
+ else if (warn_unused && !TREE_USED (label))
+ warning_with_decl (label, "label `%s' defined but not used");
+ IDENTIFIER_LABEL_VALUE (DECL_NAME (label)) = 0;
+
+ /* Put the labels into the "variables" of the
+ top-level block, so debugger can see them. */
+ TREE_CHAIN (label) = BLOCK_VARS (block);
+ BLOCK_VARS (block) = label;
+ }
+ }
+
+ /* Pop the current level, and free the structure for reuse. */
+
+ {
+ register struct binding_level *level = current_binding_level;
+ current_binding_level = current_binding_level->level_chain;
+
+ level->level_chain = free_binding_level;
+ free_binding_level = level;
+ }
+
+ /* Dispose of the block that we just made inside some higher level. */
+ if (functionbody)
+ DECL_INITIAL (current_function_decl) = block;
+ else if (block)
+ {
+ if (!block_previously_created)
+ current_binding_level->blocks
+ = chainon (current_binding_level->blocks, block);
+ }
+ /* If we did not make a block for the level just exited,
+ any blocks made for inner levels
+ (since they cannot be recorded as subblocks in that level)
+ must be carried forward so they will later become subblocks
+ of something else. */
+ else if (subblocks)
+ current_binding_level->blocks
+ = chainon (current_binding_level->blocks, subblocks);
+
+ /* Set the TYPE_CONTEXTs for all of the tagged types belonging to this
+ binding contour so that they point to the appropriate construct, i.e.
+ either to the current FUNCTION_DECL node, or else to the BLOCK node
+ we just constructed.
+
+ Note that for tagged types whose scope is just the formal parameter
+ list for some function type specification, we can't properly set
+ their TYPE_CONTEXTs here, because we don't have a pointer to the
+ appropriate FUNCTION_TYPE node readily available to us. For those
+ cases, the TYPE_CONTEXTs of the relevant tagged type nodes get set
+ in `grokdeclarator' as soon as we have created the FUNCTION_TYPE
+ node which will represent the "scope" for these "parameter list local"
+ tagged types.
+ */
+
+ if (functionbody)
+ for (link = tags; link; link = TREE_CHAIN (link))
+ TYPE_CONTEXT (TREE_VALUE (link)) = current_function_decl;
+ else if (block)
+ for (link = tags; link; link = TREE_CHAIN (link))
+ TYPE_CONTEXT (TREE_VALUE (link)) = block;
+
+ if (block)
+ TREE_USED (block) = 1;
+ return block;
+}
+
+/* Delete the node BLOCK from the current binding level.
+ This is used for the block inside a stmt expr ({...})
+ so that the block can be reinserted where appropriate. */
+
+void
+delete_block (block)
+ tree block;
+{
+ tree t;
+ if (current_binding_level->blocks == block)
+ current_binding_level->blocks = TREE_CHAIN (block);
+ for (t = current_binding_level->blocks; t;)
+ {
+ if (TREE_CHAIN (t) == block)
+ TREE_CHAIN (t) = TREE_CHAIN (block);
+ else
+ t = TREE_CHAIN (t);
+ }
+ TREE_CHAIN (block) = NULL;
+ /* Clear TREE_USED which is always set by poplevel.
+ The flag is set again if insert_block is called. */
+ TREE_USED (block) = 0;
+}
+
+/* Insert BLOCK at the end of the list of subblocks of the
+ current binding level. This is used when a BIND_EXPR is expanded,
+ to handle the BLOCK node inside the BIND_EXPR. */
+
+void
+insert_block (block)
+ tree block;
+{
+ TREE_USED (block) = 1;
+ current_binding_level->blocks
+ = chainon (current_binding_level->blocks, block);
+}
+
+/* Set the BLOCK node for the innermost scope
+ (the one we are currently in). */
+
+void
+set_block (block)
+ register tree block;
+{
+ current_binding_level->this_block = block;
+}
+
+void
+push_label_level ()
+{
+ register struct binding_level *newlevel;
+
+ /* Reuse or create a struct for this binding level. */
+
+ if (free_binding_level)
+ {
+ newlevel = free_binding_level;
+ free_binding_level = free_binding_level->level_chain;
+ }
+ else
+ {
+ newlevel = make_binding_level ();
+ }
+
+ /* Add this level to the front of the chain (stack) of label levels. */
+
+ newlevel->level_chain = label_level_chain;
+ label_level_chain = newlevel;
+
+ newlevel->names = named_labels;
+ newlevel->shadowed = shadowed_labels;
+ named_labels = 0;
+ shadowed_labels = 0;
+}
+
+void
+pop_label_level ()
+{
+ register struct binding_level *level = label_level_chain;
+ tree link, prev;
+
+ /* Clear out the definitions of the declared labels in this level.
+ Leave in the list any ordinary, non-declared labels. */
+ for (link = named_labels, prev = 0; link;)
+ {
+ if (C_DECLARED_LABEL_FLAG (TREE_VALUE (link)))
+ {
+ if (DECL_SOURCE_LINE (TREE_VALUE (link)) == 0)
+ {
+ error_with_decl (TREE_VALUE (link),
+ "label `%s' used but not defined");
+ /* Avoid crashing later. */
+ define_label (input_filename, lineno,
+ DECL_NAME (TREE_VALUE (link)));
+ }
+ else if (warn_unused && !TREE_USED (TREE_VALUE (link)))
+ warning_with_decl (TREE_VALUE (link),
+ "label `%s' defined but not used");
+ IDENTIFIER_LABEL_VALUE (DECL_NAME (TREE_VALUE (link))) = 0;
+
+ /* Delete this element from the list. */
+ link = TREE_CHAIN (link);
+ if (prev)
+ TREE_CHAIN (prev) = link;
+ else
+ named_labels = link;
+ }
+ else
+ {
+ prev = link;
+ link = TREE_CHAIN (link);
+ }
+ }
+
+ /* Bring back all the labels that were shadowed. */
+ for (link = shadowed_labels; link; link = TREE_CHAIN (link))
+ if (DECL_NAME (TREE_VALUE (link)) != 0)
+ IDENTIFIER_LABEL_VALUE (DECL_NAME (TREE_VALUE (link)))
+ = TREE_VALUE (link);
+
+ named_labels = chainon (named_labels, level->names);
+ shadowed_labels = level->shadowed;
+
+ /* Pop the current level, and free the structure for reuse. */
+ label_level_chain = label_level_chain->level_chain;
+ level->level_chain = free_binding_level;
+ free_binding_level = level;
+}
+
+/* Push a definition or a declaration of struct, union or enum tag "name".
+ "type" should be the type node.
+ We assume that the tag "name" is not already defined.
+
+ Note that the definition may really be just a forward reference.
+ In that case, the TYPE_SIZE will be zero. */
+
+void
+pushtag (name, type)
+ tree name, type;
+{
+ register struct binding_level *b;
+
+ /* Find the proper binding level for this type tag. */
+
+ for (b = current_binding_level; b->tag_transparent; b = b->level_chain)
+ continue;
+
+ if (name)
+ {
+ /* Record the identifier as the type's name if it has none. */
+
+ if (TYPE_NAME (type) == 0)
+ TYPE_NAME (type) = name;
+ }
+
+ if (b == global_binding_level)
+ b->tags = perm_tree_cons (name, type, b->tags);
+ else
+ b->tags = saveable_tree_cons (name, type, b->tags);
+
+ /* Create a fake NULL-named TYPE_DECL node whose TREE_TYPE will be the
+ tagged type we just added to the current binding level. This fake
+ NULL-named TYPE_DECL node helps dwarfout.c to know when it needs
+ to output a representation of a tagged type, and it also gives
+ us a convenient place to record the "scope start" address for the
+ tagged type. */
+
+ TYPE_STUB_DECL (type) = pushdecl (build_decl (TYPE_DECL, NULL_TREE, type));
+}
+
+/* Handle when a new declaration NEWDECL
+ has the same name as an old one OLDDECL
+ in the same binding contour.
+ Prints an error message if appropriate.
+
+ If safely possible, alter OLDDECL to look like NEWDECL, and return 1.
+ Otherwise, return 0. */
+
+static int
+duplicate_decls (newdecl, olddecl)
+ register tree newdecl, olddecl;
+{
+ int types_match = comptypes (TREE_TYPE (newdecl), TREE_TYPE (olddecl));
+ int new_is_definition = (TREE_CODE (newdecl) == FUNCTION_DECL
+ && DECL_INITIAL (newdecl) != 0);
+ tree oldtype = TREE_TYPE (olddecl);
+ tree newtype = TREE_TYPE (newdecl);
+
+ if (TREE_CODE (newtype) == ERROR_MARK
+ || TREE_CODE (oldtype) == ERROR_MARK)
+ types_match = 0;
+
+ /* New decl is completely inconsistent with the old one =>
+ tell caller to replace the old one.
+ This is always an error except in the case of shadowing a builtin. */
+ if (TREE_CODE (olddecl) != TREE_CODE (newdecl))
+ {
+ if (TREE_CODE (olddecl) == FUNCTION_DECL
+ && (DECL_BUILT_IN (olddecl)
+ || DECL_BUILT_IN_NONANSI (olddecl)))
+ {
+ /* If you declare a built-in or predefined function name as static,
+ the old definition is overridden,
+ but optionally warn this was a bad choice of name. */
+ if (!TREE_PUBLIC (newdecl))
+ {
+ if (!warn_shadow)
+ ;
+ else if (DECL_BUILT_IN (olddecl))
+ warning_with_decl (newdecl, "shadowing built-in function `%s'");
+ else
+ warning_with_decl (newdecl, "shadowing library function `%s'");
+ }
+ /* Likewise, if the built-in is not ansi, then programs can
+ override it even globally without an error. */
+ else if (! DECL_BUILT_IN (olddecl))
+ warning_with_decl (newdecl,
+ "library function `%s' declared as non-function");
+
+ else if (DECL_BUILT_IN_NONANSI (olddecl))
+ warning_with_decl (newdecl,
+ "built-in function `%s' declared as non-function");
+ else
+ warning_with_decl (newdecl,
+ "built-in function `%s' declared as non-function");
+ }
+ else
+ {
+ error_with_decl (newdecl, "`%s' redeclared as different kind of symbol");
+ error_with_decl (olddecl, "previous declaration of `%s'");
+ }
+
+ return 0;
+ }
+
+ /* For real parm decl following a forward decl,
+ return 1 so old decl will be reused. */
+ if (types_match && TREE_CODE (newdecl) == PARM_DECL
+ && TREE_ASM_WRITTEN (olddecl) && ! TREE_ASM_WRITTEN (newdecl))
+ return 1;
+
+ /* The new declaration is the same kind of object as the old one.
+ The declarations may partially match. Print warnings if they don't
+ match enough. Ultimately, copy most of the information from the new
+ decl to the old one, and keep using the old one. */
+
+ if (flag_traditional && TREE_CODE (newdecl) == FUNCTION_DECL
+ && IDENTIFIER_IMPLICIT_DECL (DECL_NAME (newdecl)) == olddecl
+ && DECL_INITIAL (olddecl) == 0)
+ /* If -traditional, avoid error for redeclaring fcn
+ after implicit decl. */
+ ;
+ else if (TREE_CODE (olddecl) == FUNCTION_DECL
+ && DECL_BUILT_IN (olddecl))
+ {
+ /* A function declaration for a built-in function. */
+ if (!TREE_PUBLIC (newdecl))
+ {
+ /* If you declare a built-in function name as static, the
+ built-in definition is overridden,
+ but optionally warn this was a bad choice of name. */
+ if (warn_shadow)
+ warning_with_decl (newdecl, "shadowing built-in function `%s'");
+ /* Discard the old built-in function. */
+ return 0;
+ }
+ else if (!types_match)
+ {
+ /* Accept the return type of the new declaration if same modes. */
+ tree oldreturntype = TREE_TYPE (TREE_TYPE (olddecl));
+ tree newreturntype = TREE_TYPE (TREE_TYPE (newdecl));
+ if (TYPE_MODE (oldreturntype) == TYPE_MODE (newreturntype))
+ {
+ /* Function types may be shared, so we can't just modify
+ the return type of olddecl's function type. */
+ tree newtype
+ = build_function_type (newreturntype,
+ TYPE_ARG_TYPES (TREE_TYPE (olddecl)));
+
+ types_match = comptypes (TREE_TYPE (newdecl), newtype);
+ if (types_match)
+ TREE_TYPE (olddecl) = newtype;
+ }
+ /* Accept harmless mismatch in first argument type also.
+ This is for ffs. */
+ if (TYPE_ARG_TYPES (TREE_TYPE (newdecl)) != 0
+ && TYPE_ARG_TYPES (TREE_TYPE (olddecl)) != 0
+ && TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (newdecl))) != 0
+ && TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (olddecl))) != 0
+ && (TYPE_MODE (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (newdecl))))
+ ==
+ TYPE_MODE (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (olddecl))))))
+ {
+ /* Function types may be shared, so we can't just modify
+ the return type of olddecl's function type. */
+ tree newtype
+ = build_function_type (TREE_TYPE (TREE_TYPE (olddecl)),
+ tree_cons (NULL_TREE,
+ TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (newdecl))),
+ TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (olddecl)))));
+
+ types_match = comptypes (TREE_TYPE (newdecl), newtype);
+ if (types_match)
+ TREE_TYPE (olddecl) = newtype;
+ }
+ }
+ if (!types_match)
+ {
+ /* If types don't match for a built-in, throw away the built-in. */
+ warning_with_decl (newdecl, "conflicting types for built-in function `%s'");
+ return 0;
+ }
+ }
+ else if (TREE_CODE (olddecl) == FUNCTION_DECL
+ && DECL_SOURCE_LINE (olddecl) == 0)
+ {
+ /* A function declaration for a predeclared function
+ that isn't actually built in. */
+ if (!TREE_PUBLIC (newdecl))
+ {
+ /* If you declare it as static, the
+ default definition is overridden. */
+ return 0;
+ }
+ else if (!types_match)
+ {
+ /* If the types don't match, preserve volatility indication.
+ Later on, we will discard everything else about the
+ default declaration. */
+ TREE_THIS_VOLATILE (newdecl) |= TREE_THIS_VOLATILE (olddecl);
+ }
+ }
+ /* Permit char *foo () to match void *foo (...) if not pedantic,
+ if one of them came from a system header file. */
+ else if (!types_match
+ && TREE_CODE (olddecl) == FUNCTION_DECL
+ && TREE_CODE (newdecl) == FUNCTION_DECL
+ && TREE_CODE (TREE_TYPE (oldtype)) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (newtype)) == POINTER_TYPE
+ && (DECL_IN_SYSTEM_HEADER (olddecl)
+ || DECL_IN_SYSTEM_HEADER (newdecl))
+ && ((TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (newtype))) == void_type_node
+ && TYPE_ARG_TYPES (oldtype) == 0
+ && self_promoting_args_p (TYPE_ARG_TYPES (newtype))
+ && TREE_TYPE (TREE_TYPE (oldtype)) == char_type_node)
+ ||
+ (TREE_TYPE (TREE_TYPE (newtype)) == char_type_node
+ && TYPE_ARG_TYPES (newtype) == 0
+ && self_promoting_args_p (TYPE_ARG_TYPES (oldtype))
+ && TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (oldtype))) == void_type_node)))
+ {
+ if (pedantic)
+ pedwarn_with_decl (newdecl, "conflicting types for `%s'");
+ /* Make sure we keep void * as ret type, not char *. */
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (oldtype))) == void_type_node)
+ TREE_TYPE (newdecl) = newtype = oldtype;
+
+ /* Set DECL_IN_SYSTEM_HEADER, so that if we see another declaration
+ we will come back here again. */
+ DECL_IN_SYSTEM_HEADER (newdecl) = 1;
+ }
+ else if (!types_match
+ /* Permit char *foo (int, ...); followed by char *foo ();
+ if not pedantic. */
+ && ! (TREE_CODE (olddecl) == FUNCTION_DECL
+ && ! pedantic
+ /* Return types must still match. */
+ && comptypes (TREE_TYPE (oldtype),
+ TREE_TYPE (newtype))
+ && TYPE_ARG_TYPES (newtype) == 0))
+ {
+ error_with_decl (newdecl, "conflicting types for `%s'");
+ /* Check for function type mismatch
+ involving an empty arglist vs a nonempty one. */
+ if (TREE_CODE (olddecl) == FUNCTION_DECL
+ && comptypes (TREE_TYPE (oldtype),
+ TREE_TYPE (newtype))
+ && ((TYPE_ARG_TYPES (oldtype) == 0
+ && DECL_INITIAL (olddecl) == 0)
+ ||
+ (TYPE_ARG_TYPES (newtype) == 0
+ && DECL_INITIAL (newdecl) == 0)))
+ {
+ /* Classify the problem further. */
+ register tree t = TYPE_ARG_TYPES (oldtype);
+ if (t == 0)
+ t = TYPE_ARG_TYPES (newtype);
+ for (; t; t = TREE_CHAIN (t))
+ {
+ register tree type = TREE_VALUE (t);
+
+ if (TREE_CHAIN (t) == 0
+ && TYPE_MAIN_VARIANT (type) != void_type_node)
+ {
+ error ("A parameter list with an ellipsis can't match");
+ error ("an empty parameter name list declaration.");
+ break;
+ }
+
+ if (TYPE_MAIN_VARIANT (type) == float_type_node
+ || C_PROMOTING_INTEGER_TYPE_P (type))
+ {
+ error ("An argument type that has a default promotion");
+ error ("can't match an empty parameter name list declaration.");
+ break;
+ }
+ }
+ }
+ error_with_decl (olddecl, "previous declaration of `%s'");
+ }
+ else
+ {
+ char *errmsg = redeclaration_error_message (newdecl, olddecl);
+ if (errmsg)
+ {
+ error_with_decl (newdecl, errmsg);
+ error_with_decl (olddecl,
+ ((DECL_INITIAL (olddecl)
+ && current_binding_level == global_binding_level)
+ ? "`%s' previously defined here"
+ : "`%s' previously declared here"));
+ }
+ else if (TREE_CODE (olddecl) == FUNCTION_DECL
+ && DECL_INITIAL (olddecl) != 0
+ && TYPE_ARG_TYPES (oldtype) == 0
+ && TYPE_ARG_TYPES (newtype) != 0)
+ {
+ register tree type, parm;
+ register int nargs;
+ /* Prototype decl follows defn w/o prototype. */
+
+ for (parm = TYPE_ACTUAL_ARG_TYPES (oldtype),
+ type = TYPE_ARG_TYPES (newtype),
+ nargs = 1;
+ (TYPE_MAIN_VARIANT (TREE_VALUE (parm)) != void_type_node
+ || TYPE_MAIN_VARIANT (TREE_VALUE (type)) != void_type_node);
+ parm = TREE_CHAIN (parm), type = TREE_CHAIN (type), nargs++)
+ {
+ if (TYPE_MAIN_VARIANT (TREE_VALUE (parm)) == void_type_node
+ || TYPE_MAIN_VARIANT (TREE_VALUE (type)) == void_type_node)
+ {
+ errmsg = "prototype for `%s' follows and number of arguments";
+ break;
+ }
+ /* Type for passing arg must be consistent
+ with that declared for the arg. */
+ if (! comptypes (TREE_VALUE (parm), TREE_VALUE (type))
+ /* If -traditional, allow `unsigned int' instead of `int'
+ in the prototype. */
+ && (! (flag_traditional
+ && TYPE_MAIN_VARIANT (TREE_VALUE (parm)) == integer_type_node
+ && TYPE_MAIN_VARIANT (TREE_VALUE (type)) == unsigned_type_node)))
+ {
+ errmsg = "prototype for `%s' follows and argument %d";
+ break;
+ }
+ }
+ if (errmsg)
+ {
+ error_with_decl (newdecl, errmsg, nargs);
+ error_with_decl (olddecl,
+ "doesn't match non-prototype definition here");
+ }
+ else
+ {
+ warning_with_decl (newdecl, "prototype for `%s' follows");
+ warning_with_decl (olddecl, "non-prototype definition here");
+ }
+ }
+ /* Warn about mismatches in various flags. */
+ else
+ {
+ /* Warn if function is now inline
+ but was previously declared not inline and has been called. */
+ if (TREE_CODE (olddecl) == FUNCTION_DECL
+ && ! DECL_INLINE (olddecl) && DECL_INLINE (newdecl)
+ && TREE_USED (olddecl))
+ warning_with_decl (newdecl,
+ "`%s' declared inline after being called");
+ if (TREE_CODE (olddecl) == FUNCTION_DECL
+ && ! DECL_INLINE (olddecl) && DECL_INLINE (newdecl)
+ && DECL_INITIAL (olddecl) != 0)
+ warning_with_decl (newdecl,
+ "`%s' declared inline after its definition");
+
+ /* If pedantic, warn when static declaration follows a non-static
+ declaration. Otherwise, do so only for functions. */
+ if ((pedantic || TREE_CODE (olddecl) == FUNCTION_DECL)
+ && TREE_PUBLIC (olddecl)
+ && !TREE_PUBLIC (newdecl))
+ warning_with_decl (newdecl, "static declaration for `%s' follows non-static");
+
+ /* Warn when const declaration follows a non-const
+ declaration, but not for functions. */
+ if (TREE_CODE (olddecl) != FUNCTION_DECL
+ && !TREE_READONLY (olddecl)
+ && TREE_READONLY (newdecl))
+ warning_with_decl (newdecl, "const declaration for `%s' follows non-const");
+ /* These bits are logically part of the type, for variables.
+ But not for functions
+ (where qualifiers are not valid ANSI anyway). */
+ else if (pedantic && TREE_CODE (olddecl) != FUNCTION_DECL
+ && (TREE_READONLY (newdecl) != TREE_READONLY (olddecl)
+ || TREE_THIS_VOLATILE (newdecl) != TREE_THIS_VOLATILE (olddecl)))
+ pedwarn_with_decl (newdecl, "type qualifiers for `%s' conflict with previous decl");
+ }
+ }
+
+ /* Optionally warn about more than one declaration for the same name. */
+ if (warn_redundant_decls && DECL_SOURCE_LINE (olddecl) != 0
+ /* Dont warn about a function declaration
+ followed by a definition. */
+ && !(TREE_CODE (newdecl) == FUNCTION_DECL && DECL_INITIAL (newdecl) != 0
+ && DECL_INITIAL (olddecl) == 0)
+ /* Don't warn about extern decl followed by (tentative) definition. */
+ && !(DECL_EXTERNAL (olddecl) && ! DECL_EXTERNAL (newdecl)))
+ {
+ warning_with_decl (newdecl, "redundant redeclaration of `%s' in same scope");
+ warning_with_decl (olddecl, "previous declaration of `%s'");
+ }
+
+ /* Copy all the DECL_... slots specified in the new decl
+ except for any that we copy here from the old type.
+
+ Past this point, we don't change OLDTYPE and NEWTYPE
+ even if we change the types of NEWDECL and OLDDECL. */
+
+ if (types_match)
+ {
+ /* Make sure we put the new type in the same obstack as the old ones.
+ If the old types are not both in the same obstack, use the permanent
+ one. */
+ if (TYPE_OBSTACK (oldtype) == TYPE_OBSTACK (newtype))
+ push_obstacks (TYPE_OBSTACK (oldtype), TYPE_OBSTACK (oldtype));
+ else
+ {
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+ }
+
+ /* Merge the data types specified in the two decls. */
+ if (TREE_CODE (newdecl) != FUNCTION_DECL || !DECL_BUILT_IN (olddecl))
+ TREE_TYPE (newdecl)
+ = TREE_TYPE (olddecl)
+ = common_type (newtype, oldtype);
+
+ /* Lay the type out, unless already done. */
+ if (oldtype != TREE_TYPE (newdecl))
+ {
+ if (TREE_TYPE (newdecl) != error_mark_node)
+ layout_type (TREE_TYPE (newdecl));
+ if (TREE_CODE (newdecl) != FUNCTION_DECL
+ && TREE_CODE (newdecl) != TYPE_DECL
+ && TREE_CODE (newdecl) != CONST_DECL)
+ layout_decl (newdecl, 0);
+ }
+ else
+ {
+ /* Since the type is OLDDECL's, make OLDDECL's size go with. */
+ DECL_SIZE (newdecl) = DECL_SIZE (olddecl);
+ if (TREE_CODE (olddecl) != FUNCTION_DECL)
+ if (DECL_ALIGN (olddecl) > DECL_ALIGN (newdecl))
+ DECL_ALIGN (newdecl) = DECL_ALIGN (olddecl);
+ }
+
+ /* Keep the old rtl since we can safely use it. */
+ DECL_RTL (newdecl) = DECL_RTL (olddecl);
+
+ /* Merge the type qualifiers. */
+ if (DECL_BUILT_IN_NONANSI (olddecl) && TREE_THIS_VOLATILE (olddecl)
+ && !TREE_THIS_VOLATILE (newdecl))
+ TREE_THIS_VOLATILE (olddecl) = 0;
+ if (TREE_READONLY (newdecl))
+ TREE_READONLY (olddecl) = 1;
+ if (TREE_THIS_VOLATILE (newdecl))
+ {
+ TREE_THIS_VOLATILE (olddecl) = 1;
+ if (TREE_CODE (newdecl) == VAR_DECL)
+ make_var_volatile (newdecl);
+ }
+
+ /* Keep source location of definition rather than declaration. */
+ if (DECL_INITIAL (newdecl) == 0 && DECL_INITIAL (olddecl) != 0)
+ {
+ DECL_SOURCE_LINE (newdecl) = DECL_SOURCE_LINE (olddecl);
+ DECL_SOURCE_FILE (newdecl) = DECL_SOURCE_FILE (olddecl);
+ }
+
+ /* Merge the unused-warning information. */
+ if (DECL_IN_SYSTEM_HEADER (olddecl))
+ DECL_IN_SYSTEM_HEADER (newdecl) = 1;
+ else if (DECL_IN_SYSTEM_HEADER (newdecl))
+ DECL_IN_SYSTEM_HEADER (olddecl) = 1;
+
+ /* Merge the initialization information. */
+ if (DECL_INITIAL (newdecl) == 0)
+ DECL_INITIAL (newdecl) = DECL_INITIAL (olddecl);
+
+ /* Merge the section attribute.
+ We want to issue an error if the sections conflict but that must be
+ done later in decl_attributes since we are called before attributes
+ are assigned. */
+ if (DECL_SECTION_NAME (newdecl) == NULL_TREE)
+ DECL_SECTION_NAME (newdecl) = DECL_SECTION_NAME (olddecl);
+
+ pop_obstacks ();
+ }
+ /* If cannot merge, then use the new type and qualifiers,
+ and don't preserve the old rtl. */
+ else
+ {
+ TREE_TYPE (olddecl) = TREE_TYPE (newdecl);
+ TREE_READONLY (olddecl) = TREE_READONLY (newdecl);
+ TREE_THIS_VOLATILE (olddecl) = TREE_THIS_VOLATILE (newdecl);
+ TREE_SIDE_EFFECTS (olddecl) = TREE_SIDE_EFFECTS (newdecl);
+ }
+
+ /* Merge the storage class information. */
+ /* For functions, static overrides non-static. */
+ if (TREE_CODE (newdecl) == FUNCTION_DECL)
+ {
+ TREE_PUBLIC (newdecl) &= TREE_PUBLIC (olddecl);
+ /* This is since we don't automatically
+ copy the attributes of NEWDECL into OLDDECL. */
+ TREE_PUBLIC (olddecl) = TREE_PUBLIC (newdecl);
+ /* If this clears `static', clear it in the identifier too. */
+ if (! TREE_PUBLIC (olddecl))
+ TREE_PUBLIC (DECL_NAME (olddecl)) = 0;
+ }
+ if (DECL_EXTERNAL (newdecl))
+ {
+ TREE_STATIC (newdecl) = TREE_STATIC (olddecl);
+ DECL_EXTERNAL (newdecl) = DECL_EXTERNAL (olddecl);
+ /* An extern decl does not override previous storage class. */
+ TREE_PUBLIC (newdecl) = TREE_PUBLIC (olddecl);
+ }
+ else
+ {
+ TREE_STATIC (olddecl) = TREE_STATIC (newdecl);
+ TREE_PUBLIC (olddecl) = TREE_PUBLIC (newdecl);
+ }
+
+ /* If either decl says `inline', this fn is inline,
+ unless its definition was passed already. */
+ if (DECL_INLINE (newdecl) && DECL_INITIAL (olddecl) == 0)
+ DECL_INLINE (olddecl) = 1;
+ DECL_INLINE (newdecl) = DECL_INLINE (olddecl);
+
+ /* Get rid of any built-in function if new arg types don't match it
+ or if we have a function definition. */
+ if (TREE_CODE (newdecl) == FUNCTION_DECL
+ && DECL_BUILT_IN (olddecl)
+ && (!types_match || new_is_definition))
+ {
+ TREE_TYPE (olddecl) = TREE_TYPE (newdecl);
+ DECL_BUILT_IN (olddecl) = 0;
+ }
+
+ /* If redeclaring a builtin function, and not a definition,
+ it stays built in.
+ Also preserve various other info from the definition. */
+ if (TREE_CODE (newdecl) == FUNCTION_DECL && !new_is_definition)
+ {
+ if (DECL_BUILT_IN (olddecl))
+ {
+ DECL_BUILT_IN (newdecl) = 1;
+ DECL_FUNCTION_CODE (newdecl) = DECL_FUNCTION_CODE (olddecl);
+ }
+ else
+ DECL_FRAME_SIZE (newdecl) = DECL_FRAME_SIZE (olddecl);
+
+ DECL_RESULT (newdecl) = DECL_RESULT (olddecl);
+ DECL_INITIAL (newdecl) = DECL_INITIAL (olddecl);
+ DECL_SAVED_INSNS (newdecl) = DECL_SAVED_INSNS (olddecl);
+ DECL_ARGUMENTS (newdecl) = DECL_ARGUMENTS (olddecl);
+ }
+
+ /* Copy most of the decl-specific fields of NEWDECL into OLDDECL.
+ But preserve OLDdECL's DECL_UID. */
+ {
+ register unsigned olddecl_uid = DECL_UID (olddecl);
+
+ bcopy ((char *) newdecl + sizeof (struct tree_common),
+ (char *) olddecl + sizeof (struct tree_common),
+ sizeof (struct tree_decl) - sizeof (struct tree_common));
+ DECL_UID (olddecl) = olddecl_uid;
+ }
+
+ return 1;
+}
+
+/* Record a decl-node X as belonging to the current lexical scope.
+ Check for errors (such as an incompatible declaration for the same
+ name already seen in the same scope).
+
+ Returns either X or an old decl for the same name.
+ If an old decl is returned, it may have been smashed
+ to agree with what X says. */
+
+tree
+pushdecl (x)
+ tree x;
+{
+ register tree t;
+ register tree name = DECL_NAME (x);
+ register struct binding_level *b = current_binding_level;
+
+ DECL_CONTEXT (x) = current_function_decl;
+ /* A local extern declaration for a function doesn't constitute nesting.
+ A local auto declaration does, since it's a forward decl
+ for a nested function coming later. */
+ if (TREE_CODE (x) == FUNCTION_DECL && DECL_INITIAL (x) == 0
+ && DECL_EXTERNAL (x))
+ DECL_CONTEXT (x) = 0;
+
+ if (warn_nested_externs && DECL_EXTERNAL (x) && b != global_binding_level
+ && x != IDENTIFIER_IMPLICIT_DECL (name)
+ /* Don't print error messages for __FUNCTION__ and __PRETTY_FUNCTION__ */
+ && !DECL_IN_SYSTEM_HEADER (x))
+ warning ("nested extern declaration of `%s'", IDENTIFIER_POINTER (name));
+
+ if (name)
+ {
+ char *file;
+ int line;
+
+ /* Don't type check externs here when -traditional. This is so that
+ code with conflicting declarations inside blocks will get warnings
+ not errors. X11 for instance depends on this. */
+ if (DECL_EXTERNAL (x) && TREE_PUBLIC (x) && ! flag_traditional)
+ t = lookup_name_current_level_global (name);
+ else
+ t = lookup_name_current_level (name);
+ if (t != 0 && t == error_mark_node)
+ /* error_mark_node is 0 for a while during initialization! */
+ {
+ t = 0;
+ error_with_decl (x, "`%s' used prior to declaration");
+ }
+
+ if (t != 0)
+ {
+ file = DECL_SOURCE_FILE (t);
+ line = DECL_SOURCE_LINE (t);
+ }
+
+ if (t != 0 && duplicate_decls (x, t))
+ {
+ if (TREE_CODE (t) == PARM_DECL)
+ {
+ /* Don't allow more than one "real" duplicate
+ of a forward parm decl. */
+ TREE_ASM_WRITTEN (t) = TREE_ASM_WRITTEN (x);
+ return t;
+ }
+ /* If this decl is `static' and an implicit decl was seen previously,
+ warn. But don't complain if -traditional,
+ since traditional compilers don't complain. */
+ if (!flag_traditional && TREE_PUBLIC (name)
+ && ! TREE_PUBLIC (x) && ! DECL_EXTERNAL (x)
+ /* We used to warn also for explicit extern followed by static,
+ but sometimes you need to do it that way. */
+ && IDENTIFIER_IMPLICIT_DECL (name) != 0)
+ {
+ pedwarn ("`%s' was declared implicitly `extern' and later `static'",
+ IDENTIFIER_POINTER (name));
+ pedwarn_with_file_and_line (file, line,
+ "previous declaration of `%s'",
+ IDENTIFIER_POINTER (name));
+ }
+
+ /* If this is a global decl, and there exists a conflicting local
+ decl in a parent block, then we can't return as yet, because we
+ need to register this decl in the current binding block. */
+ if (! DECL_EXTERNAL (x) || ! TREE_PUBLIC (x)
+ || lookup_name (name) == t)
+ return t;
+ }
+
+ /* If we are processing a typedef statement, generate a whole new
+ ..._TYPE node (which will be just an variant of the existing
+ ..._TYPE node with identical properties) and then install the
+ TYPE_DECL node generated to represent the typedef name as the
+ TYPE_NAME of this brand new (duplicate) ..._TYPE node.
+
+ The whole point here is to end up with a situation where each
+ and every ..._TYPE node the compiler creates will be uniquely
+ associated with AT MOST one node representing a typedef name.
+ This way, even though the compiler substitutes corresponding
+ ..._TYPE nodes for TYPE_DECL (i.e. "typedef name") nodes very
+ early on, later parts of the compiler can always do the reverse
+ translation and get back the corresponding typedef name. For
+ example, given:
+
+ typedef struct S MY_TYPE;
+ MY_TYPE object;
+
+ Later parts of the compiler might only know that `object' was of
+ type `struct S' if if were not for code just below. With this
+ code however, later parts of the compiler see something like:
+
+ struct S' == struct S
+ typedef struct S' MY_TYPE;
+ struct S' object;
+
+ And they can then deduce (from the node for type struct S') that
+ the original object declaration was:
+
+ MY_TYPE object;
+
+ Being able to do this is important for proper support of protoize,
+ and also for generating precise symbolic debugging information
+ which takes full account of the programmer's (typedef) vocabulary.
+
+ Obviously, we don't want to generate a duplicate ..._TYPE node if
+ the TYPE_DECL node that we are now processing really represents a
+ standard built-in type.
+
+ Since all standard types are effectively declared at line zero
+ in the source file, we can easily check to see if we are working
+ on a standard type by checking the current value of lineno. */
+
+ if (TREE_CODE (x) == TYPE_DECL)
+ {
+ if (DECL_SOURCE_LINE (x) == 0)
+ {
+ if (TYPE_NAME (TREE_TYPE (x)) == 0)
+ TYPE_NAME (TREE_TYPE (x)) = x;
+ }
+ else if (TREE_TYPE (x) != error_mark_node)
+ {
+ tree tt = TREE_TYPE (x);
+
+ tt = build_type_copy (tt);
+ TYPE_NAME (tt) = x;
+ TREE_TYPE (x) = tt;
+ }
+ }
+
+ /* Multiple external decls of the same identifier ought to match.
+ Check against both global declarations (when traditional) and out of
+ scope (limbo) block level declarations.
+
+ We get warnings about inline functions where they are defined.
+ Avoid duplicate warnings where they are used. */
+ if (TREE_PUBLIC (x) && ! DECL_INLINE (x))
+ {
+ tree decl;
+
+ if (flag_traditional && IDENTIFIER_GLOBAL_VALUE (name) != 0
+ && (DECL_EXTERNAL (IDENTIFIER_GLOBAL_VALUE (name))
+ || TREE_PUBLIC (IDENTIFIER_GLOBAL_VALUE (name))))
+ decl = IDENTIFIER_GLOBAL_VALUE (name);
+ else if (IDENTIFIER_LIMBO_VALUE (name) != 0)
+ /* Decls in limbo are always extern, so no need to check that. */
+ decl = IDENTIFIER_LIMBO_VALUE (name);
+ else
+ decl = 0;
+
+ if (decl && ! comptypes (TREE_TYPE (x), TREE_TYPE (decl))
+ /* If old decl is built-in, we already warned if we should. */
+ && !DECL_BUILT_IN (decl))
+ {
+ pedwarn_with_decl (x,
+ "type mismatch with previous external decl");
+ pedwarn_with_decl (decl, "previous external decl of `%s'");
+ }
+ }
+
+ /* If a function has had an implicit declaration, and then is defined,
+ make sure they are compatible. */
+
+ if (IDENTIFIER_IMPLICIT_DECL (name) != 0
+ && IDENTIFIER_GLOBAL_VALUE (name) == 0
+ && TREE_CODE (x) == FUNCTION_DECL
+ && ! comptypes (TREE_TYPE (x),
+ TREE_TYPE (IDENTIFIER_IMPLICIT_DECL (name))))
+ {
+ warning_with_decl (x, "type mismatch with previous implicit declaration");
+ warning_with_decl (IDENTIFIER_IMPLICIT_DECL (name),
+ "previous implicit declaration of `%s'");
+ }
+
+ /* In PCC-compatibility mode, extern decls of vars with no current decl
+ take effect at top level no matter where they are. */
+ if (flag_traditional && DECL_EXTERNAL (x)
+ && lookup_name (name) == 0)
+ {
+ tree type = TREE_TYPE (x);
+
+ /* But don't do this if the type contains temporary nodes. */
+ while (type)
+ {
+ if (type == error_mark_node)
+ break;
+ if (! TREE_PERMANENT (type))
+ {
+ warning_with_decl (x, "type of external `%s' is not global");
+ /* By exiting the loop early, we leave TYPE nonzero,
+ and thus prevent globalization of the decl. */
+ break;
+ }
+ else if (TREE_CODE (type) == FUNCTION_TYPE
+ && TYPE_ARG_TYPES (type) != 0)
+ /* The types might not be truly local,
+ but the list of arg types certainly is temporary.
+ Since prototypes are nontraditional,
+ ok not to do the traditional thing. */
+ break;
+ type = TREE_TYPE (type);
+ }
+
+ if (type == 0)
+ b = global_binding_level;
+ }
+
+ /* This name is new in its binding level.
+ Install the new declaration and return it. */
+ if (b == global_binding_level)
+ {
+ /* Install a global value. */
+
+ /* If the first global decl has external linkage,
+ warn if we later see static one. */
+ if (IDENTIFIER_GLOBAL_VALUE (name) == 0 && TREE_PUBLIC (x))
+ TREE_PUBLIC (name) = 1;
+
+ IDENTIFIER_GLOBAL_VALUE (name) = x;
+
+ /* We no longer care about any previous block level declarations. */
+ IDENTIFIER_LIMBO_VALUE (name) = 0;
+
+ /* Don't forget if the function was used via an implicit decl. */
+ if (IDENTIFIER_IMPLICIT_DECL (name)
+ && TREE_USED (IDENTIFIER_IMPLICIT_DECL (name)))
+ TREE_USED (x) = 1, TREE_USED (name) = 1;
+
+ /* Don't forget if its address was taken in that way. */
+ if (IDENTIFIER_IMPLICIT_DECL (name)
+ && TREE_ADDRESSABLE (IDENTIFIER_IMPLICIT_DECL (name)))
+ TREE_ADDRESSABLE (x) = 1;
+
+ /* Warn about mismatches against previous implicit decl. */
+ if (IDENTIFIER_IMPLICIT_DECL (name) != 0
+ /* If this real decl matches the implicit, don't complain. */
+ && ! (TREE_CODE (x) == FUNCTION_DECL
+ && (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (x)))
+ == integer_type_node)))
+ pedwarn ("`%s' was previously implicitly declared to return `int'",
+ IDENTIFIER_POINTER (name));
+
+ /* If this decl is `static' and an `extern' was seen previously,
+ that is erroneous. */
+ if (TREE_PUBLIC (name)
+ && ! TREE_PUBLIC (x) && ! DECL_EXTERNAL (x))
+ {
+ /* Okay to redeclare an ANSI built-in as static. */
+ if (t != 0 && DECL_BUILT_IN (t))
+ ;
+ /* Okay to declare a non-ANSI built-in as anything. */
+ else if (t != 0 && DECL_BUILT_IN_NONANSI (t))
+ ;
+ else if (IDENTIFIER_IMPLICIT_DECL (name))
+ pedwarn ("`%s' was declared implicitly `extern' and later `static'",
+ IDENTIFIER_POINTER (name));
+ else
+ pedwarn ("`%s' was declared `extern' and later `static'",
+ IDENTIFIER_POINTER (name));
+ }
+ }
+ else
+ {
+ /* Here to install a non-global value. */
+ tree oldlocal = IDENTIFIER_LOCAL_VALUE (name);
+ tree oldglobal = IDENTIFIER_GLOBAL_VALUE (name);
+ IDENTIFIER_LOCAL_VALUE (name) = x;
+
+ /* If this is an extern function declaration, see if we
+ have a global definition or declaration for the function. */
+ if (oldlocal == 0
+ && DECL_EXTERNAL (x) && !DECL_INLINE (x)
+ && oldglobal != 0
+ && TREE_CODE (x) == FUNCTION_DECL
+ && TREE_CODE (oldglobal) == FUNCTION_DECL)
+ {
+ /* We have one. Their types must agree. */
+ if (! comptypes (TREE_TYPE (x),
+ TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (name))))
+ pedwarn_with_decl (x, "extern declaration of `%s' doesn't match global one");
+ else
+ {
+ /* Inner extern decl is inline if global one is.
+ Copy enough to really inline it. */
+ if (DECL_INLINE (oldglobal))
+ {
+ DECL_INLINE (x) = DECL_INLINE (oldglobal);
+ DECL_INITIAL (x) = (current_function_decl == oldglobal
+ ? 0 : DECL_INITIAL (oldglobal));
+ DECL_SAVED_INSNS (x) = DECL_SAVED_INSNS (oldglobal);
+ DECL_FRAME_SIZE (x) = DECL_FRAME_SIZE (oldglobal);
+ DECL_ARGUMENTS (x) = DECL_ARGUMENTS (oldglobal);
+ DECL_RESULT (x) = DECL_RESULT (oldglobal);
+ TREE_ASM_WRITTEN (x) = TREE_ASM_WRITTEN (oldglobal);
+ DECL_ABSTRACT_ORIGIN (x) = oldglobal;
+ }
+ /* Inner extern decl is built-in if global one is. */
+ if (DECL_BUILT_IN (oldglobal))
+ {
+ DECL_BUILT_IN (x) = DECL_BUILT_IN (oldglobal);
+ DECL_FUNCTION_CODE (x) = DECL_FUNCTION_CODE (oldglobal);
+ }
+ /* Keep the arg types from a file-scope fcn defn. */
+ if (TYPE_ARG_TYPES (TREE_TYPE (oldglobal)) != 0
+ && DECL_INITIAL (oldglobal)
+ && TYPE_ARG_TYPES (TREE_TYPE (x)) == 0)
+ TREE_TYPE (x) = TREE_TYPE (oldglobal);
+ }
+ }
+
+#if 0 /* This case is probably sometimes the right thing to do. */
+ /* If we have a local external declaration,
+ then any file-scope declaration should not
+ have been static. */
+ if (oldlocal == 0 && oldglobal != 0
+ && !TREE_PUBLIC (oldglobal)
+ && DECL_EXTERNAL (x) && TREE_PUBLIC (x))
+ warning ("`%s' locally external but globally static",
+ IDENTIFIER_POINTER (name));
+#endif
+
+ /* If we have a local external declaration,
+ and no file-scope declaration has yet been seen,
+ then if we later have a file-scope decl it must not be static. */
+ if (oldlocal == 0
+ && oldglobal == 0
+ && DECL_EXTERNAL (x)
+ && TREE_PUBLIC (x))
+ {
+ TREE_PUBLIC (name) = 1;
+
+ /* Save this decl, so that we can do type checking against
+ other decls after it falls out of scope.
+
+ Only save it once. This prevents temporary decls created in
+ expand_inline_function from being used here, since this
+ will have been set when the inline function was parsed.
+ It also helps give slightly better warnings. */
+ if (IDENTIFIER_LIMBO_VALUE (name) == 0)
+ IDENTIFIER_LIMBO_VALUE (name) = x;
+ }
+
+ /* Warn if shadowing an argument at the top level of the body. */
+ if (oldlocal != 0 && !DECL_EXTERNAL (x)
+ /* This warning doesn't apply to the parms of a nested fcn. */
+ && ! current_binding_level->parm_flag
+ /* Check that this is one level down from the parms. */
+ && current_binding_level->level_chain->parm_flag
+ /* Check that the decl being shadowed
+ comes from the parm level, one level up. */
+ && chain_member (oldlocal, current_binding_level->level_chain->names))
+ {
+ if (TREE_CODE (oldlocal) == PARM_DECL)
+ pedwarn ("declaration of `%s' shadows a parameter",
+ IDENTIFIER_POINTER (name));
+ else
+ pedwarn ("declaration of `%s' shadows a symbol from the parameter list",
+ IDENTIFIER_POINTER (name));
+ }
+
+ /* Maybe warn if shadowing something else. */
+ else if (warn_shadow && !DECL_EXTERNAL (x)
+ /* No shadow warnings for internally generated vars. */
+ && DECL_SOURCE_LINE (x) != 0
+ /* No shadow warnings for vars made for inlining. */
+ && ! DECL_FROM_INLINE (x))
+ {
+ char *warnstring = 0;
+
+ if (TREE_CODE (x) == PARM_DECL
+ && current_binding_level->level_chain->parm_flag)
+ /* Don't warn about the parm names in function declarator
+ within a function declarator.
+ It would be nice to avoid warning in any function
+ declarator in a declaration, as opposed to a definition,
+ but there is no way to tell it's not a definition. */
+ ;
+ else if (oldlocal != 0 && TREE_CODE (oldlocal) == PARM_DECL)
+ warnstring = "declaration of `%s' shadows a parameter";
+ else if (oldlocal != 0)
+ warnstring = "declaration of `%s' shadows previous local";
+ else if (IDENTIFIER_GLOBAL_VALUE (name) != 0
+ && IDENTIFIER_GLOBAL_VALUE (name) != error_mark_node)
+ warnstring = "declaration of `%s' shadows global declaration";
+
+ if (warnstring)
+ warning (warnstring, IDENTIFIER_POINTER (name));
+ }
+
+ /* If storing a local value, there may already be one (inherited).
+ If so, record it for restoration when this binding level ends. */
+ if (oldlocal != 0)
+ b->shadowed = tree_cons (name, oldlocal, b->shadowed);
+ }
+
+ /* Keep count of variables in this level with incomplete type. */
+ if (TYPE_SIZE (TREE_TYPE (x)) == 0)
+ ++b->n_incomplete;
+ }
+
+ /* Put decls on list in reverse order.
+ We will reverse them later if necessary. */
+ TREE_CHAIN (x) = b->names;
+ b->names = x;
+
+ return x;
+}
+
+/* Like pushdecl, only it places X in GLOBAL_BINDING_LEVEL, if appropriate. */
+
+tree
+pushdecl_top_level (x)
+ tree x;
+{
+ register tree t;
+ register struct binding_level *b = current_binding_level;
+
+ current_binding_level = global_binding_level;
+ t = pushdecl (x);
+ current_binding_level = b;
+ return t;
+}
+
+/* Generate an implicit declaration for identifier FUNCTIONID
+ as a function of type int (). Print a warning if appropriate. */
+
+tree
+implicitly_declare (functionid)
+ tree functionid;
+{
+ register tree decl;
+ int traditional_warning = 0;
+ /* Only one "implicit declaration" warning per identifier. */
+ int implicit_warning;
+
+ /* Save the decl permanently so we can warn if definition follows. */
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+
+ /* We used to reuse an old implicit decl here,
+ but this loses with inline functions because it can clobber
+ the saved decl chains. */
+/* if (IDENTIFIER_IMPLICIT_DECL (functionid) != 0)
+ decl = IDENTIFIER_IMPLICIT_DECL (functionid);
+ else */
+ decl = build_decl (FUNCTION_DECL, functionid, default_function_type);
+
+ /* Warn of implicit decl following explicit local extern decl.
+ This is probably a program designed for traditional C. */
+ if (TREE_PUBLIC (functionid) && IDENTIFIER_GLOBAL_VALUE (functionid) == 0)
+ traditional_warning = 1;
+
+ /* Warn once of an implicit declaration. */
+ implicit_warning = (IDENTIFIER_IMPLICIT_DECL (functionid) == 0);
+
+ DECL_EXTERNAL (decl) = 1;
+ TREE_PUBLIC (decl) = 1;
+
+ /* Record that we have an implicit decl and this is it. */
+ IDENTIFIER_IMPLICIT_DECL (functionid) = decl;
+
+ /* ANSI standard says implicit declarations are in the innermost block.
+ So we record the decl in the standard fashion.
+ If flag_traditional is set, pushdecl does it top-level. */
+ pushdecl (decl);
+
+ /* This is a no-op in c-lang.c or something real in objc-actions.c. */
+ maybe_objc_check_decl (decl);
+
+ rest_of_decl_compilation (decl, NULL_PTR, 0, 0);
+
+ if (warn_implicit && implicit_warning)
+ warning ("implicit declaration of function `%s'",
+ IDENTIFIER_POINTER (functionid));
+ else if (warn_traditional && traditional_warning)
+ warning ("function `%s' was previously declared within a block",
+ IDENTIFIER_POINTER (functionid));
+
+ /* Write a record describing this implicit function declaration to the
+ prototypes file (if requested). */
+
+ gen_aux_info_record (decl, 0, 1, 0);
+
+ pop_obstacks ();
+
+ return decl;
+}
+
+/* Return zero if the declaration NEWDECL is valid
+ when the declaration OLDDECL (assumed to be for the same name)
+ has already been seen.
+ Otherwise return an error message format string with a %s
+ where the identifier should go. */
+
+static char *
+redeclaration_error_message (newdecl, olddecl)
+ tree newdecl, olddecl;
+{
+ if (TREE_CODE (newdecl) == TYPE_DECL)
+ {
+ if (flag_traditional && TREE_TYPE (newdecl) == TREE_TYPE (olddecl))
+ return 0;
+ return "redefinition of `%s'";
+ }
+ else if (TREE_CODE (newdecl) == FUNCTION_DECL)
+ {
+ /* Declarations of functions can insist on internal linkage
+ but they can't be inconsistent with internal linkage,
+ so there can be no error on that account.
+ However defining the same name twice is no good. */
+ if (DECL_INITIAL (olddecl) != 0 && DECL_INITIAL (newdecl) != 0
+ /* However, defining once as extern inline and a second
+ time in another way is ok. */
+ && !(DECL_INLINE (olddecl) && DECL_EXTERNAL (olddecl)
+ && !(DECL_INLINE (newdecl) && DECL_EXTERNAL (newdecl))))
+ return "redefinition of `%s'";
+ return 0;
+ }
+ else if (current_binding_level == global_binding_level)
+ {
+ /* Objects declared at top level: */
+ /* If at least one is a reference, it's ok. */
+ if (DECL_EXTERNAL (newdecl) || DECL_EXTERNAL (olddecl))
+ return 0;
+ /* Reject two definitions. */
+ if (DECL_INITIAL (olddecl) != 0 && DECL_INITIAL (newdecl) != 0)
+ return "redefinition of `%s'";
+ /* Now we have two tentative defs, or one tentative and one real def. */
+ /* Insist that the linkage match. */
+ if (TREE_PUBLIC (olddecl) != TREE_PUBLIC (newdecl))
+ return "conflicting declarations of `%s'";
+ return 0;
+ }
+ else if (current_binding_level->parm_flag
+ && TREE_ASM_WRITTEN (olddecl) && !TREE_ASM_WRITTEN (newdecl))
+ return 0;
+ else
+ {
+ /* Newdecl has block scope. If olddecl has block scope also, then
+ reject two definitions, and reject a definition together with an
+ external reference. Otherwise, it is OK, because newdecl must
+ be an extern reference to olddecl. */
+ if (!(DECL_EXTERNAL (newdecl) && DECL_EXTERNAL (olddecl))
+ && DECL_CONTEXT (newdecl) == DECL_CONTEXT (olddecl))
+ return "redeclaration of `%s'";
+ return 0;
+ }
+}
+
+/* Get the LABEL_DECL corresponding to identifier ID as a label.
+ Create one if none exists so far for the current function.
+ This function is called for both label definitions and label references. */
+
+tree
+lookup_label (id)
+ tree id;
+{
+ register tree decl = IDENTIFIER_LABEL_VALUE (id);
+
+ if (current_function_decl == 0)
+ {
+ error ("label %s referenced outside of any function",
+ IDENTIFIER_POINTER (id));
+ return 0;
+ }
+
+ /* Use a label already defined or ref'd with this name. */
+ if (decl != 0)
+ {
+ /* But not if it is inherited and wasn't declared to be inheritable. */
+ if (DECL_CONTEXT (decl) != current_function_decl
+ && ! C_DECLARED_LABEL_FLAG (decl))
+ return shadow_label (id);
+ return decl;
+ }
+
+ decl = build_decl (LABEL_DECL, id, void_type_node);
+
+ /* Make sure every label has an rtx. */
+ label_rtx (decl);
+
+ /* A label not explicitly declared must be local to where it's ref'd. */
+ DECL_CONTEXT (decl) = current_function_decl;
+
+ DECL_MODE (decl) = VOIDmode;
+
+ /* Say where one reference is to the label,
+ for the sake of the error if it is not defined. */
+ DECL_SOURCE_LINE (decl) = lineno;
+ DECL_SOURCE_FILE (decl) = input_filename;
+
+ IDENTIFIER_LABEL_VALUE (id) = decl;
+
+ named_labels = tree_cons (NULL_TREE, decl, named_labels);
+
+ return decl;
+}
+
+/* Make a label named NAME in the current function,
+ shadowing silently any that may be inherited from containing functions
+ or containing scopes.
+
+ Note that valid use, if the label being shadowed
+ comes from another scope in the same function,
+ requires calling declare_nonlocal_label right away. */
+
+tree
+shadow_label (name)
+ tree name;
+{
+ register tree decl = IDENTIFIER_LABEL_VALUE (name);
+
+ if (decl != 0)
+ {
+ shadowed_labels = tree_cons (NULL_TREE, decl, shadowed_labels);
+ IDENTIFIER_LABEL_VALUE (name) = decl = 0;
+ }
+
+ return lookup_label (name);
+}
+
+/* Define a label, specifying the location in the source file.
+ Return the LABEL_DECL node for the label, if the definition is valid.
+ Otherwise return 0. */
+
+tree
+define_label (filename, line, name)
+ char *filename;
+ int line;
+ tree name;
+{
+ tree decl = lookup_label (name);
+
+ /* If label with this name is known from an outer context, shadow it. */
+ if (decl != 0 && DECL_CONTEXT (decl) != current_function_decl)
+ {
+ shadowed_labels = tree_cons (NULL_TREE, decl, shadowed_labels);
+ IDENTIFIER_LABEL_VALUE (name) = 0;
+ decl = lookup_label (name);
+ }
+
+ if (DECL_INITIAL (decl) != 0)
+ {
+ error ("duplicate label `%s'", IDENTIFIER_POINTER (name));
+ return 0;
+ }
+ else
+ {
+ /* Mark label as having been defined. */
+ DECL_INITIAL (decl) = error_mark_node;
+ /* Say where in the source. */
+ DECL_SOURCE_FILE (decl) = filename;
+ DECL_SOURCE_LINE (decl) = line;
+ return decl;
+ }
+}
+
+/* Return the list of declarations of the current level.
+ Note that this list is in reverse order unless/until
+ you nreverse it; and when you do nreverse it, you must
+ store the result back using `storedecls' or you will lose. */
+
+tree
+getdecls ()
+{
+ return current_binding_level->names;
+}
+
+/* Return the list of type-tags (for structs, etc) of the current level. */
+
+tree
+gettags ()
+{
+ return current_binding_level->tags;
+}
+
+/* Store the list of declarations of the current level.
+ This is done for the parameter declarations of a function being defined,
+ after they are modified in the light of any missing parameters. */
+
+static void
+storedecls (decls)
+ tree decls;
+{
+ current_binding_level->names = decls;
+}
+
+/* Similarly, store the list of tags of the current level. */
+
+static void
+storetags (tags)
+ tree tags;
+{
+ current_binding_level->tags = tags;
+}
+
+/* Given NAME, an IDENTIFIER_NODE,
+ return the structure (or union or enum) definition for that name.
+ Searches binding levels from BINDING_LEVEL up to the global level.
+ If THISLEVEL_ONLY is nonzero, searches only the specified context
+ (but skips any tag-transparent contexts to find one that is
+ meaningful for tags).
+ CODE says which kind of type the caller wants;
+ it is RECORD_TYPE or UNION_TYPE or ENUMERAL_TYPE.
+ If the wrong kind of type is found, an error is reported. */
+
+static tree
+lookup_tag (code, name, binding_level, thislevel_only)
+ enum tree_code code;
+ struct binding_level *binding_level;
+ tree name;
+ int thislevel_only;
+{
+ register struct binding_level *level;
+
+ for (level = binding_level; level; level = level->level_chain)
+ {
+ register tree tail;
+ for (tail = level->tags; tail; tail = TREE_CHAIN (tail))
+ {
+ if (TREE_PURPOSE (tail) == name)
+ {
+ if (TREE_CODE (TREE_VALUE (tail)) != code)
+ {
+ /* Definition isn't the kind we were looking for. */
+ pending_invalid_xref = name;
+ pending_invalid_xref_file = input_filename;
+ pending_invalid_xref_line = lineno;
+ }
+ return TREE_VALUE (tail);
+ }
+ }
+ if (thislevel_only && ! level->tag_transparent)
+ return NULL_TREE;
+ }
+ return NULL_TREE;
+}
+
+/* Print an error message now
+ for a recent invalid struct, union or enum cross reference.
+ We don't print them immediately because they are not invalid
+ when used in the `struct foo;' construct for shadowing. */
+
+void
+pending_xref_error ()
+{
+ if (pending_invalid_xref != 0)
+ error_with_file_and_line (pending_invalid_xref_file,
+ pending_invalid_xref_line,
+ "`%s' defined as wrong kind of tag",
+ IDENTIFIER_POINTER (pending_invalid_xref));
+ pending_invalid_xref = 0;
+}
+
+/* Given a type, find the tag that was defined for it and return the tag name.
+ Otherwise return 0. */
+
+static tree
+lookup_tag_reverse (type)
+ tree type;
+{
+ register struct binding_level *level;
+
+ for (level = current_binding_level; level; level = level->level_chain)
+ {
+ register tree tail;
+ for (tail = level->tags; tail; tail = TREE_CHAIN (tail))
+ {
+ if (TREE_VALUE (tail) == type)
+ return TREE_PURPOSE (tail);
+ }
+ }
+ return NULL_TREE;
+}
+
+/* Look up NAME in the current binding level and its superiors
+ in the namespace of variables, functions and typedefs.
+ Return a ..._DECL node of some kind representing its definition,
+ or return 0 if it is undefined. */
+
+tree
+lookup_name (name)
+ tree name;
+{
+ register tree val;
+ if (current_binding_level != global_binding_level
+ && IDENTIFIER_LOCAL_VALUE (name))
+ val = IDENTIFIER_LOCAL_VALUE (name);
+ else
+ val = IDENTIFIER_GLOBAL_VALUE (name);
+ return val;
+}
+
+/* Similar to `lookup_name' but look only at current binding level. */
+
+tree
+lookup_name_current_level (name)
+ tree name;
+{
+ register tree t;
+
+ if (current_binding_level == global_binding_level)
+ return IDENTIFIER_GLOBAL_VALUE (name);
+
+ if (IDENTIFIER_LOCAL_VALUE (name) == 0)
+ return 0;
+
+ for (t = current_binding_level->names; t; t = TREE_CHAIN (t))
+ if (DECL_NAME (t) == name)
+ break;
+
+ return t;
+}
+
+/* Similar to `lookup_name_current_level' but also look at the global binding
+ level. */
+
+tree
+lookup_name_current_level_global (name)
+ tree name;
+{
+ register tree t = 0;
+
+ if (current_binding_level == global_binding_level)
+ return IDENTIFIER_GLOBAL_VALUE (name);
+
+ if (IDENTIFIER_LOCAL_VALUE (name) != 0)
+ for (t = current_binding_level->names; t; t = TREE_CHAIN (t))
+ if (DECL_NAME (t) == name)
+ break;
+
+ if (t == 0)
+ t = IDENTIFIER_GLOBAL_VALUE (name);
+
+ return t;
+}
+
+/* Create the predefined scalar types of C,
+ and some nodes representing standard constants (0, 1, (void *)0).
+ Initialize the global binding level.
+ Make definitions for built-in primitive functions. */
+
+void
+init_decl_processing ()
+{
+ register tree endlink;
+ /* Either char* or void*. */
+ tree traditional_ptr_type_node;
+ /* Data types of memcpy and strlen. */
+ tree memcpy_ftype, strlen_ftype;
+ tree void_ftype_any;
+ int wchar_type_size;
+ tree temp;
+ tree array_domain_type;
+
+ current_function_decl = NULL;
+ named_labels = NULL;
+ current_binding_level = NULL_BINDING_LEVEL;
+ free_binding_level = NULL_BINDING_LEVEL;
+ pushlevel (0); /* make the binding_level structure for global names */
+ global_binding_level = current_binding_level;
+
+ /* Define `int' and `char' first so that dbx will output them first. */
+
+ integer_type_node = make_signed_type (INT_TYPE_SIZE);
+ pushdecl (build_decl (TYPE_DECL, ridpointers[(int) RID_INT],
+ integer_type_node));
+
+ /* Define `char', which is like either `signed char' or `unsigned char'
+ but not the same as either. */
+
+ char_type_node
+ = (flag_signed_char
+ ? make_signed_type (CHAR_TYPE_SIZE)
+ : make_unsigned_type (CHAR_TYPE_SIZE));
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("char"),
+ char_type_node));
+
+ long_integer_type_node = make_signed_type (LONG_TYPE_SIZE);
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("long int"),
+ long_integer_type_node));
+
+ unsigned_type_node = make_unsigned_type (INT_TYPE_SIZE);
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("unsigned int"),
+ unsigned_type_node));
+
+ long_unsigned_type_node = make_unsigned_type (LONG_TYPE_SIZE);
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("long unsigned int"),
+ long_unsigned_type_node));
+
+ long_long_integer_type_node = make_signed_type (LONG_LONG_TYPE_SIZE);
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("long long int"),
+ long_long_integer_type_node));
+
+ long_long_unsigned_type_node = make_unsigned_type (LONG_LONG_TYPE_SIZE);
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("long long unsigned int"),
+ long_long_unsigned_type_node));
+
+ /* `unsigned long' is the standard type for sizeof.
+ Traditionally, use a signed type.
+ Note that stddef.h uses `unsigned long',
+ and this must agree, even of long and int are the same size. */
+ sizetype
+ = TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (get_identifier (SIZE_TYPE)));
+ if (flag_traditional && TREE_UNSIGNED (sizetype))
+ sizetype = signed_type (sizetype);
+
+ ptrdiff_type_node
+ = TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (get_identifier (PTRDIFF_TYPE)));
+
+ TREE_TYPE (TYPE_SIZE (integer_type_node)) = sizetype;
+ TREE_TYPE (TYPE_SIZE (char_type_node)) = sizetype;
+ TREE_TYPE (TYPE_SIZE (unsigned_type_node)) = sizetype;
+ TREE_TYPE (TYPE_SIZE (long_unsigned_type_node)) = sizetype;
+ TREE_TYPE (TYPE_SIZE (long_integer_type_node)) = sizetype;
+ TREE_TYPE (TYPE_SIZE (long_long_integer_type_node)) = sizetype;
+ TREE_TYPE (TYPE_SIZE (long_long_unsigned_type_node)) = sizetype;
+
+ error_mark_node = make_node (ERROR_MARK);
+ TREE_TYPE (error_mark_node) = error_mark_node;
+
+ short_integer_type_node = make_signed_type (SHORT_TYPE_SIZE);
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("short int"),
+ short_integer_type_node));
+
+ short_unsigned_type_node = make_unsigned_type (SHORT_TYPE_SIZE);
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("short unsigned int"),
+ short_unsigned_type_node));
+
+ /* Define both `signed char' and `unsigned char'. */
+ signed_char_type_node = make_signed_type (CHAR_TYPE_SIZE);
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("signed char"),
+ signed_char_type_node));
+
+ unsigned_char_type_node = make_unsigned_type (CHAR_TYPE_SIZE);
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("unsigned char"),
+ unsigned_char_type_node));
+
+ intQI_type_node = make_signed_type (GET_MODE_BITSIZE (QImode));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, intQI_type_node));
+
+ intHI_type_node = make_signed_type (GET_MODE_BITSIZE (HImode));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, intHI_type_node));
+
+ intSI_type_node = make_signed_type (GET_MODE_BITSIZE (SImode));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, intSI_type_node));
+
+ intDI_type_node = make_signed_type (GET_MODE_BITSIZE (DImode));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, intDI_type_node));
+
+ unsigned_intQI_type_node = make_unsigned_type (GET_MODE_BITSIZE (QImode));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, unsigned_intQI_type_node));
+
+ unsigned_intHI_type_node = make_unsigned_type (GET_MODE_BITSIZE (HImode));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, unsigned_intHI_type_node));
+
+ unsigned_intSI_type_node = make_unsigned_type (GET_MODE_BITSIZE (SImode));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, unsigned_intSI_type_node));
+
+ unsigned_intDI_type_node = make_unsigned_type (GET_MODE_BITSIZE (DImode));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, unsigned_intDI_type_node));
+
+ float_type_node = make_node (REAL_TYPE);
+ TYPE_PRECISION (float_type_node) = FLOAT_TYPE_SIZE;
+ pushdecl (build_decl (TYPE_DECL, ridpointers[(int) RID_FLOAT],
+ float_type_node));
+ layout_type (float_type_node);
+
+ double_type_node = make_node (REAL_TYPE);
+ if (flag_short_double)
+ TYPE_PRECISION (double_type_node) = FLOAT_TYPE_SIZE;
+ else
+ TYPE_PRECISION (double_type_node) = DOUBLE_TYPE_SIZE;
+ pushdecl (build_decl (TYPE_DECL, ridpointers[(int) RID_DOUBLE],
+ double_type_node));
+ layout_type (double_type_node);
+
+ long_double_type_node = make_node (REAL_TYPE);
+ TYPE_PRECISION (long_double_type_node) = LONG_DOUBLE_TYPE_SIZE;
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("long double"),
+ long_double_type_node));
+ layout_type (long_double_type_node);
+
+ complex_integer_type_node = make_node (COMPLEX_TYPE);
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("complex int"),
+ complex_integer_type_node));
+ TREE_TYPE (complex_integer_type_node) = integer_type_node;
+ layout_type (complex_integer_type_node);
+
+ complex_float_type_node = make_node (COMPLEX_TYPE);
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("complex float"),
+ complex_float_type_node));
+ TREE_TYPE (complex_float_type_node) = float_type_node;
+ layout_type (complex_float_type_node);
+
+ complex_double_type_node = make_node (COMPLEX_TYPE);
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("complex double"),
+ complex_double_type_node));
+ TREE_TYPE (complex_double_type_node) = double_type_node;
+ layout_type (complex_double_type_node);
+
+ complex_long_double_type_node = make_node (COMPLEX_TYPE);
+ pushdecl (build_decl (TYPE_DECL, get_identifier ("complex long double"),
+ complex_long_double_type_node));
+ TREE_TYPE (complex_long_double_type_node) = long_double_type_node;
+ layout_type (complex_long_double_type_node);
+
+ wchar_type_node
+ = TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (get_identifier (WCHAR_TYPE)));
+ wchar_type_size = TYPE_PRECISION (wchar_type_node);
+ signed_wchar_type_node = signed_type (wchar_type_node);
+ unsigned_wchar_type_node = unsigned_type (wchar_type_node);
+
+ integer_zero_node = build_int_2 (0, 0);
+ TREE_TYPE (integer_zero_node) = integer_type_node;
+ integer_one_node = build_int_2 (1, 0);
+ TREE_TYPE (integer_one_node) = integer_type_node;
+
+ size_zero_node = build_int_2 (0, 0);
+ TREE_TYPE (size_zero_node) = sizetype;
+ size_one_node = build_int_2 (1, 0);
+ TREE_TYPE (size_one_node) = sizetype;
+
+ void_type_node = make_node (VOID_TYPE);
+ pushdecl (build_decl (TYPE_DECL,
+ ridpointers[(int) RID_VOID], void_type_node));
+ layout_type (void_type_node); /* Uses integer_zero_node */
+ /* We are not going to have real types in C with less than byte alignment,
+ so we might as well not have any types that claim to have it. */
+ TYPE_ALIGN (void_type_node) = BITS_PER_UNIT;
+
+ null_pointer_node = build_int_2 (0, 0);
+ TREE_TYPE (null_pointer_node) = build_pointer_type (void_type_node);
+ layout_type (TREE_TYPE (null_pointer_node));
+
+ string_type_node = build_pointer_type (char_type_node);
+ const_string_type_node
+ = build_pointer_type (build_type_variant (char_type_node, 1, 0));
+
+ /* Make a type to be the domain of a few array types
+ whose domains don't really matter.
+ 200 is small enough that it always fits in size_t
+ and large enough that it can hold most function names for the
+ initializations of __FUNCTION__ and __PRETTY_FUNCTION__. */
+ array_domain_type = build_index_type (build_int_2 (200, 0));
+
+ /* make a type for arrays of characters.
+ With luck nothing will ever really depend on the length of this
+ array type. */
+ char_array_type_node
+ = build_array_type (char_type_node, array_domain_type);
+ /* Likewise for arrays of ints. */
+ int_array_type_node
+ = build_array_type (integer_type_node, array_domain_type);
+ /* This is for wide string constants. */
+ wchar_array_type_node
+ = build_array_type (wchar_type_node, array_domain_type);
+
+ default_function_type
+ = build_function_type (integer_type_node, NULL_TREE);
+
+ ptr_type_node = build_pointer_type (void_type_node);
+ const_ptr_type_node
+ = build_pointer_type (build_type_variant (void_type_node, 1, 0));
+
+ endlink = tree_cons (NULL_TREE, void_type_node, NULL_TREE);
+
+ void_ftype_any
+ = build_function_type (void_type_node, NULL_TREE);
+
+ double_ftype_double
+ = build_function_type (double_type_node,
+ tree_cons (NULL_TREE, double_type_node, endlink));
+
+ double_ftype_double_double
+ = build_function_type (double_type_node,
+ tree_cons (NULL_TREE, double_type_node,
+ tree_cons (NULL_TREE,
+ double_type_node, endlink)));
+
+ int_ftype_int
+ = build_function_type (integer_type_node,
+ tree_cons (NULL_TREE, integer_type_node, endlink));
+
+ long_ftype_long
+ = build_function_type (long_integer_type_node,
+ tree_cons (NULL_TREE,
+ long_integer_type_node, endlink));
+
+ void_ftype_ptr_ptr_int
+ = build_function_type (void_type_node,
+ tree_cons (NULL_TREE, ptr_type_node,
+ tree_cons (NULL_TREE, ptr_type_node,
+ tree_cons (NULL_TREE,
+ integer_type_node,
+ endlink))));
+
+ int_ftype_cptr_cptr_sizet
+ = build_function_type (integer_type_node,
+ tree_cons (NULL_TREE, const_ptr_type_node,
+ tree_cons (NULL_TREE, const_ptr_type_node,
+ tree_cons (NULL_TREE,
+ sizetype,
+ endlink))));
+
+ void_ftype_ptr_int_int
+ = build_function_type (void_type_node,
+ tree_cons (NULL_TREE, ptr_type_node,
+ tree_cons (NULL_TREE, integer_type_node,
+ tree_cons (NULL_TREE,
+ integer_type_node,
+ endlink))));
+
+ string_ftype_ptr_ptr /* strcpy prototype */
+ = build_function_type (string_type_node,
+ tree_cons (NULL_TREE, string_type_node,
+ tree_cons (NULL_TREE,
+ const_string_type_node,
+ endlink)));
+
+ int_ftype_string_string /* strcmp prototype */
+ = build_function_type (integer_type_node,
+ tree_cons (NULL_TREE, const_string_type_node,
+ tree_cons (NULL_TREE,
+ const_string_type_node,
+ endlink)));
+
+ strlen_ftype /* strlen prototype */
+ = build_function_type (flag_traditional ? integer_type_node : sizetype,
+ tree_cons (NULL_TREE, const_string_type_node,
+ endlink));
+
+ traditional_ptr_type_node
+ = (flag_traditional ? string_type_node : ptr_type_node);
+
+ memcpy_ftype /* memcpy prototype */
+ = build_function_type (traditional_ptr_type_node,
+ tree_cons (NULL_TREE, ptr_type_node,
+ tree_cons (NULL_TREE, const_ptr_type_node,
+ tree_cons (NULL_TREE,
+ sizetype,
+ endlink))));
+
+ builtin_function ("__builtin_constant_p", int_ftype_int,
+ BUILT_IN_CONSTANT_P, NULL_PTR);
+
+ builtin_function ("__builtin_return_address",
+ build_function_type (ptr_type_node,
+ tree_cons (NULL_TREE,
+ unsigned_type_node,
+ endlink)),
+ BUILT_IN_RETURN_ADDRESS, NULL_PTR);
+
+ builtin_function ("__builtin_frame_address",
+ build_function_type (ptr_type_node,
+ tree_cons (NULL_TREE,
+ unsigned_type_node,
+ endlink)),
+ BUILT_IN_FRAME_ADDRESS, NULL_PTR);
+
+ builtin_function ("__builtin_alloca",
+ build_function_type (ptr_type_node,
+ tree_cons (NULL_TREE,
+ sizetype,
+ endlink)),
+ BUILT_IN_ALLOCA, "alloca");
+ builtin_function ("__builtin_ffs", int_ftype_int, BUILT_IN_FFS, NULL_PTR);
+ /* Define alloca, ffs as builtins.
+ Declare _exit just to mark it as volatile. */
+ if (! flag_no_builtin && !flag_no_nonansi_builtin)
+ {
+ temp = builtin_function ("alloca",
+ build_function_type (ptr_type_node,
+ tree_cons (NULL_TREE,
+ sizetype,
+ endlink)),
+ BUILT_IN_ALLOCA, NULL_PTR);
+ /* Suppress error if redefined as a non-function. */
+ DECL_BUILT_IN_NONANSI (temp) = 1;
+ temp = builtin_function ("ffs", int_ftype_int, BUILT_IN_FFS, NULL_PTR);
+ /* Suppress error if redefined as a non-function. */
+ DECL_BUILT_IN_NONANSI (temp) = 1;
+ temp = builtin_function ("_exit", void_ftype_any, NOT_BUILT_IN,
+ NULL_PTR);
+ TREE_THIS_VOLATILE (temp) = 1;
+ TREE_SIDE_EFFECTS (temp) = 1;
+ /* Suppress error if redefined as a non-function. */
+ DECL_BUILT_IN_NONANSI (temp) = 1;
+ }
+
+ builtin_function ("__builtin_abs", int_ftype_int, BUILT_IN_ABS, NULL_PTR);
+ builtin_function ("__builtin_fabs", double_ftype_double, BUILT_IN_FABS,
+ NULL_PTR);
+ builtin_function ("__builtin_labs", long_ftype_long, BUILT_IN_LABS,
+ NULL_PTR);
+ builtin_function ("__builtin_saveregs",
+ build_function_type (ptr_type_node, NULL_TREE),
+ BUILT_IN_SAVEREGS, NULL_PTR);
+/* EXPAND_BUILTIN_VARARGS is obsolete. */
+#if 0
+ builtin_function ("__builtin_varargs",
+ build_function_type (ptr_type_node,
+ tree_cons (NULL_TREE,
+ integer_type_node,
+ endlink)),
+ BUILT_IN_VARARGS, NULL_PTR);
+#endif
+ builtin_function ("__builtin_classify_type", default_function_type,
+ BUILT_IN_CLASSIFY_TYPE, NULL_PTR);
+ builtin_function ("__builtin_next_arg",
+ build_function_type (ptr_type_node, NULL_TREE),
+ BUILT_IN_NEXT_ARG, NULL_PTR);
+ builtin_function ("__builtin_args_info",
+ build_function_type (integer_type_node,
+ tree_cons (NULL_TREE,
+ integer_type_node,
+ endlink)),
+ BUILT_IN_ARGS_INFO, NULL_PTR);
+
+ /* Untyped call and return. */
+ builtin_function ("__builtin_apply_args",
+ build_function_type (ptr_type_node, NULL_TREE),
+ BUILT_IN_APPLY_ARGS, NULL_PTR);
+
+ temp = tree_cons (NULL_TREE,
+ build_pointer_type (build_function_type (void_type_node,
+ NULL_TREE)),
+ tree_cons (NULL_TREE,
+ ptr_type_node,
+ tree_cons (NULL_TREE,
+ sizetype,
+ endlink)));
+ builtin_function ("__builtin_apply",
+ build_function_type (ptr_type_node, temp),
+ BUILT_IN_APPLY, NULL_PTR);
+ builtin_function ("__builtin_return",
+ build_function_type (void_type_node,
+ tree_cons (NULL_TREE,
+ ptr_type_node,
+ endlink)),
+ BUILT_IN_RETURN, NULL_PTR);
+
+ /* Currently under experimentation. */
+ builtin_function ("__builtin_memcpy", memcpy_ftype,
+ BUILT_IN_MEMCPY, "memcpy");
+ builtin_function ("__builtin_memcmp", int_ftype_cptr_cptr_sizet,
+ BUILT_IN_MEMCMP, "memcmp");
+ builtin_function ("__builtin_strcmp", int_ftype_string_string,
+ BUILT_IN_STRCMP, "strcmp");
+ builtin_function ("__builtin_strcpy", string_ftype_ptr_ptr,
+ BUILT_IN_STRCPY, "strcpy");
+ builtin_function ("__builtin_strlen", strlen_ftype,
+ BUILT_IN_STRLEN, "strlen");
+ builtin_function ("__builtin_fsqrt", double_ftype_double,
+ BUILT_IN_FSQRT, "sqrt");
+ builtin_function ("__builtin_sin", double_ftype_double,
+ BUILT_IN_SIN, "sin");
+ builtin_function ("__builtin_cos", double_ftype_double,
+ BUILT_IN_COS, "cos");
+
+ /* In an ANSI C program, it is okay to supply built-in meanings
+ for these functions, since applications cannot validly use them
+ with any other meaning.
+ However, honor the -fno-builtin option. */
+ if (!flag_no_builtin)
+ {
+ builtin_function ("abs", int_ftype_int, BUILT_IN_ABS, NULL_PTR);
+ builtin_function ("fabs", double_ftype_double, BUILT_IN_FABS, NULL_PTR);
+ builtin_function ("labs", long_ftype_long, BUILT_IN_LABS, NULL_PTR);
+ builtin_function ("memcpy", memcpy_ftype, BUILT_IN_MEMCPY, NULL_PTR);
+ builtin_function ("memcmp", int_ftype_cptr_cptr_sizet, BUILT_IN_MEMCMP,
+ NULL_PTR);
+ builtin_function ("strcmp", int_ftype_string_string, BUILT_IN_STRCMP,
+ NULL_PTR);
+ builtin_function ("strcpy", string_ftype_ptr_ptr, BUILT_IN_STRCPY,
+ NULL_PTR);
+ builtin_function ("strlen", strlen_ftype, BUILT_IN_STRLEN, NULL_PTR);
+ builtin_function ("sqrt", double_ftype_double, BUILT_IN_FSQRT, NULL_PTR);
+ builtin_function ("sin", double_ftype_double, BUILT_IN_SIN, NULL_PTR);
+ builtin_function ("cos", double_ftype_double, BUILT_IN_COS, NULL_PTR);
+
+ /* Declare these functions volatile
+ to avoid spurious "control drops through" warnings. */
+ /* Don't specify the argument types, to avoid errors
+ from certain code which isn't valid in ANSI but which exists. */
+ temp = builtin_function ("abort", void_ftype_any, NOT_BUILT_IN,
+ NULL_PTR);
+ TREE_THIS_VOLATILE (temp) = 1;
+ TREE_SIDE_EFFECTS (temp) = 1;
+ temp = builtin_function ("exit", void_ftype_any, NOT_BUILT_IN, NULL_PTR);
+ TREE_THIS_VOLATILE (temp) = 1;
+ TREE_SIDE_EFFECTS (temp) = 1;
+ }
+
+#if 0
+ /* Support for these has not been written in either expand_builtin
+ or build_function_call. */
+ builtin_function ("__builtin_div", default_ftype, BUILT_IN_DIV, NULL_PTR);
+ builtin_function ("__builtin_ldiv", default_ftype, BUILT_IN_LDIV, NULL_PTR);
+ builtin_function ("__builtin_ffloor", double_ftype_double, BUILT_IN_FFLOOR,
+ NULL_PTR);
+ builtin_function ("__builtin_fceil", double_ftype_double, BUILT_IN_FCEIL,
+ NULL_PTR);
+ builtin_function ("__builtin_fmod", double_ftype_double_double,
+ BUILT_IN_FMOD, NULL_PTR);
+ builtin_function ("__builtin_frem", double_ftype_double_double,
+ BUILT_IN_FREM, NULL_PTR);
+ builtin_function ("__builtin_memset", ptr_ftype_ptr_int_int,
+ BUILT_IN_MEMSET, NULL_PTR);
+ builtin_function ("__builtin_getexp", double_ftype_double, BUILT_IN_GETEXP,
+ NULL_PTR);
+ builtin_function ("__builtin_getman", double_ftype_double, BUILT_IN_GETMAN,
+ NULL_PTR);
+#endif
+
+ /* Create the global bindings for __FUNCTION__ and __PRETTY_FUNCTION__. */
+ declare_function_name ();
+
+ start_identifier_warnings ();
+
+ /* Prepare to check format strings against argument lists. */
+ init_function_format_info ();
+
+ init_iterators ();
+
+ incomplete_decl_finalize_hook = finish_incomplete_decl;
+}
+
+/* Return a definition for a builtin function named NAME and whose data type
+ is TYPE. TYPE should be a function type with argument types.
+ FUNCTION_CODE tells later passes how to compile calls to this function.
+ See tree.h for its possible values.
+
+ If LIBRARY_NAME is nonzero, use that for DECL_ASSEMBLER_NAME,
+ the name to be called if we can't opencode the function. */
+
+tree
+builtin_function (name, type, function_code, library_name)
+ char *name;
+ tree type;
+ enum built_in_function function_code;
+ char *library_name;
+{
+ tree decl = build_decl (FUNCTION_DECL, get_identifier (name), type);
+ DECL_EXTERNAL (decl) = 1;
+ TREE_PUBLIC (decl) = 1;
+ /* If -traditional, permit redefining a builtin function any way you like.
+ (Though really, if the program redefines these functions,
+ it probably won't work right unless compiled with -fno-builtin.) */
+ if (flag_traditional && name[0] != '_')
+ DECL_BUILT_IN_NONANSI (decl) = 1;
+ if (library_name)
+ DECL_ASSEMBLER_NAME (decl) = get_identifier (library_name);
+ make_decl_rtl (decl, NULL_PTR, 1);
+ pushdecl (decl);
+ if (function_code != NOT_BUILT_IN)
+ {
+ DECL_BUILT_IN (decl) = 1;
+ DECL_FUNCTION_CODE (decl) = function_code;
+ }
+ /* Warn if a function in the namespace for users
+ is used without an occasion to consider it declared. */
+ if (name[0] != '_' || name[1] != '_')
+ C_DECL_ANTICIPATED (decl) = 1;
+
+ return decl;
+}
+
+/* Called when a declaration is seen that contains no names to declare.
+ If its type is a reference to a structure, union or enum inherited
+ from a containing scope, shadow that tag name for the current scope
+ with a forward reference.
+ If its type defines a new named structure or union
+ or defines an enum, it is valid but we need not do anything here.
+ Otherwise, it is an error. */
+
+void
+shadow_tag (declspecs)
+ tree declspecs;
+{
+ shadow_tag_warned (declspecs, 0);
+}
+
+void
+shadow_tag_warned (declspecs, warned)
+ tree declspecs;
+ int warned;
+ /* 1 => we have done a pedwarn. 2 => we have done a warning, but
+ no pedwarn. */
+{
+ int found_tag = 0;
+ register tree link;
+
+ pending_invalid_xref = 0;
+
+ for (link = declspecs; link; link = TREE_CHAIN (link))
+ {
+ register tree value = TREE_VALUE (link);
+ register enum tree_code code = TREE_CODE (value);
+
+ if (code == RECORD_TYPE || code == UNION_TYPE || code == ENUMERAL_TYPE)
+ /* Used to test also that TYPE_SIZE (value) != 0.
+ That caused warning for `struct foo;' at top level in the file. */
+ {
+ register tree name = lookup_tag_reverse (value);
+ register tree t;
+
+ found_tag++;
+
+ if (name == 0)
+ {
+ if (warned != 1 && code != ENUMERAL_TYPE)
+ /* Empty unnamed enum OK */
+ {
+ pedwarn ("unnamed struct/union that defines no instances");
+ warned = 1;
+ }
+ }
+ else
+ {
+ t = lookup_tag (code, name, current_binding_level, 1);
+
+ if (t == 0)
+ {
+ t = make_node (code);
+ pushtag (name, t);
+ }
+ }
+ }
+ else
+ {
+ if (!warned)
+ {
+ warning ("useless keyword or type name in empty declaration");
+ warned = 2;
+ }
+ }
+ }
+
+ if (found_tag > 1)
+ error ("two types specified in one empty declaration");
+
+ if (warned != 1)
+ {
+ if (found_tag == 0)
+ pedwarn ("empty declaration");
+ }
+}
+
+/* Decode a "typename", such as "int **", returning a ..._TYPE node. */
+
+tree
+groktypename (typename)
+ tree typename;
+{
+ if (TREE_CODE (typename) != TREE_LIST)
+ return typename;
+ return grokdeclarator (TREE_VALUE (typename),
+ TREE_PURPOSE (typename),
+ TYPENAME, 0);
+}
+
+/* Return a PARM_DECL node for a given pair of specs and declarator. */
+
+tree
+groktypename_in_parm_context (typename)
+ tree typename;
+{
+ if (TREE_CODE (typename) != TREE_LIST)
+ return typename;
+ return grokdeclarator (TREE_VALUE (typename),
+ TREE_PURPOSE (typename),
+ PARM, 0);
+}
+
+/* Decode a declarator in an ordinary declaration or data definition.
+ This is called as soon as the type information and variable name
+ have been parsed, before parsing the initializer if any.
+ Here we create the ..._DECL node, fill in its type,
+ and put it on the list of decls for the current context.
+ The ..._DECL node is returned as the value.
+
+ Exception: for arrays where the length is not specified,
+ the type is left null, to be filled in by `finish_decl'.
+
+ Function definitions do not come here; they go to start_function
+ instead. However, external and forward declarations of functions
+ do go through here. Structure field declarations are done by
+ grokfield and not through here. */
+
+/* Set this to zero to debug not using the temporary obstack
+ to parse initializers. */
+int debug_temp_inits = 1;
+
+tree
+start_decl (declarator, declspecs, initialized)
+ tree declarator, declspecs;
+ int initialized;
+{
+ register tree decl = grokdeclarator (declarator, declspecs,
+ NORMAL, initialized);
+ register tree tem;
+ int init_written = initialized;
+
+ /* The corresponding pop_obstacks is in finish_decl. */
+ push_obstacks_nochange ();
+
+ if (initialized)
+ /* Is it valid for this decl to have an initializer at all?
+ If not, set INITIALIZED to zero, which will indirectly
+ tell `finish_decl' to ignore the initializer once it is parsed. */
+ switch (TREE_CODE (decl))
+ {
+ case TYPE_DECL:
+ /* typedef foo = bar means give foo the same type as bar.
+ We haven't parsed bar yet, so `finish_decl' will fix that up.
+ Any other case of an initialization in a TYPE_DECL is an error. */
+ if (pedantic || list_length (declspecs) > 1)
+ {
+ error ("typedef `%s' is initialized",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ initialized = 0;
+ }
+ break;
+
+ case FUNCTION_DECL:
+ error ("function `%s' is initialized like a variable",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ initialized = 0;
+ break;
+
+ case PARM_DECL:
+ /* DECL_INITIAL in a PARM_DECL is really DECL_ARG_TYPE. */
+ error ("parameter `%s' is initialized",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ initialized = 0;
+ break;
+
+ default:
+ /* Don't allow initializations for incomplete types
+ except for arrays which might be completed by the initialization. */
+ if (TYPE_SIZE (TREE_TYPE (decl)) != 0)
+ {
+ /* A complete type is ok if size is fixed. */
+
+ if (TREE_CODE (TYPE_SIZE (TREE_TYPE (decl))) != INTEGER_CST
+ || C_DECL_VARIABLE_SIZE (decl))
+ {
+ error ("variable-sized object may not be initialized");
+ initialized = 0;
+ }
+ }
+ else if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE)
+ {
+ error ("variable `%s' has initializer but incomplete type",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ initialized = 0;
+ }
+ else if (TYPE_SIZE (TREE_TYPE (TREE_TYPE (decl))) == 0)
+ {
+ error ("elements of array `%s' have incomplete type",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ initialized = 0;
+ }
+ }
+
+ if (initialized)
+ {
+#if 0 /* Seems redundant with grokdeclarator. */
+ if (current_binding_level != global_binding_level
+ && DECL_EXTERNAL (decl)
+ && TREE_CODE (decl) != FUNCTION_DECL)
+ warning ("declaration of `%s' has `extern' and is initialized",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+#endif
+ DECL_EXTERNAL (decl) = 0;
+ if (current_binding_level == global_binding_level)
+ TREE_STATIC (decl) = 1;
+
+ /* Tell `pushdecl' this is an initialized decl
+ even though we don't yet have the initializer expression.
+ Also tell `finish_decl' it may store the real initializer. */
+ DECL_INITIAL (decl) = error_mark_node;
+ }
+
+ /* If this is a function declaration, write a record describing it to the
+ prototypes file (if requested). */
+
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ gen_aux_info_record (decl, 0, 0, TYPE_ARG_TYPES (TREE_TYPE (decl)) != 0);
+
+ /* Add this decl to the current binding level.
+ TEM may equal DECL or it may be a previous decl of the same name. */
+ tem = pushdecl (decl);
+
+ /* For C and Obective-C, we by default put things in .common when
+ possible. */
+ DECL_COMMON (tem) = 1;
+
+ /* For a local variable, define the RTL now. */
+ if (current_binding_level != global_binding_level
+ /* But not if this is a duplicate decl
+ and we preserved the rtl from the previous one
+ (which may or may not happen). */
+ && DECL_RTL (tem) == 0)
+ {
+ if (TYPE_SIZE (TREE_TYPE (tem)) != 0)
+ expand_decl (tem);
+ else if (TREE_CODE (TREE_TYPE (tem)) == ARRAY_TYPE
+ && DECL_INITIAL (tem) != 0)
+ expand_decl (tem);
+ }
+
+ if (init_written)
+ {
+ /* When parsing and digesting the initializer,
+ use temporary storage. Do this even if we will ignore the value. */
+ if (current_binding_level == global_binding_level && debug_temp_inits)
+ temporary_allocation ();
+ }
+
+ return tem;
+}
+
+/* Finish processing of a declaration;
+ install its initial value.
+ If the length of an array type is not known before,
+ it must be determined now, from the initial value, or it is an error. */
+
+void
+finish_decl (decl, init, asmspec_tree)
+ tree decl, init;
+ tree asmspec_tree;
+{
+ register tree type = TREE_TYPE (decl);
+ int was_incomplete = (DECL_SIZE (decl) == 0);
+ int temporary = allocation_temporary_p ();
+ char *asmspec = 0;
+
+ /* If a name was specified, get the string. */
+ if (asmspec_tree)
+ asmspec = TREE_STRING_POINTER (asmspec_tree);
+
+ /* If `start_decl' didn't like having an initialization, ignore it now. */
+
+ if (init != 0 && DECL_INITIAL (decl) == 0)
+ init = 0;
+ /* Don't crash if parm is initialized. */
+ if (TREE_CODE (decl) == PARM_DECL)
+ init = 0;
+
+ if (ITERATOR_P (decl))
+ {
+ if (init == 0)
+ error_with_decl (decl, "iterator has no initial value");
+ else
+ init = save_expr (init);
+ }
+
+ if (init)
+ {
+ if (TREE_CODE (decl) != TYPE_DECL)
+ store_init_value (decl, init);
+ else
+ {
+ /* typedef foo = bar; store the type of bar as the type of foo. */
+ TREE_TYPE (decl) = TREE_TYPE (init);
+ DECL_INITIAL (decl) = init = 0;
+ }
+ }
+
+ /* Pop back to the obstack that is current for this binding level.
+ This is because MAXINDEX, rtl, etc. to be made below
+ must go in the permanent obstack. But don't discard the
+ temporary data yet. */
+ pop_obstacks ();
+#if 0 /* pop_obstacks was near the end; this is what was here. */
+ if (current_binding_level == global_binding_level && temporary)
+ end_temporary_allocation ();
+#endif
+
+ /* Deduce size of array from initialization, if not already known */
+
+ if (TREE_CODE (type) == ARRAY_TYPE
+ && TYPE_DOMAIN (type) == 0
+ && TREE_CODE (decl) != TYPE_DECL)
+ {
+ int do_default
+ = (TREE_STATIC (decl)
+ /* Even if pedantic, an external linkage array
+ may have incomplete type at first. */
+ ? pedantic && !TREE_PUBLIC (decl)
+ : !DECL_EXTERNAL (decl));
+ int failure
+ = complete_array_type (type, DECL_INITIAL (decl), do_default);
+
+ /* Get the completed type made by complete_array_type. */
+ type = TREE_TYPE (decl);
+
+ if (failure == 1)
+ error_with_decl (decl, "initializer fails to determine size of `%s'");
+
+ if (failure == 2)
+ {
+ if (do_default)
+ error_with_decl (decl, "array size missing in `%s'");
+ /* If a `static' var's size isn't known,
+ make it extern as well as static, so it does not get
+ allocated.
+ If it is not `static', then do not mark extern;
+ finish_incomplete_decl will give it a default size
+ and it will get allocated. */
+ else if (!pedantic && TREE_STATIC (decl) && ! TREE_PUBLIC (decl))
+ DECL_EXTERNAL (decl) = 1;
+ }
+
+ /* TYPE_MAX_VALUE is always one less than the number of elements
+ in the array, because we start counting at zero. Therefore,
+ warn only if the value is less than zero. */
+ if (pedantic && TYPE_DOMAIN (type) != 0
+ && tree_int_cst_sgn (TYPE_MAX_VALUE (TYPE_DOMAIN (type))) < 0)
+ error_with_decl (decl, "zero or negative size array `%s'");
+
+ layout_decl (decl, 0);
+ }
+
+ if (TREE_CODE (decl) == VAR_DECL)
+ {
+ if (DECL_SIZE (decl) == 0
+ && TYPE_SIZE (TREE_TYPE (decl)) != 0)
+ layout_decl (decl, 0);
+
+ if (DECL_SIZE (decl) == 0
+ && (TREE_STATIC (decl)
+ ?
+ /* A static variable with an incomplete type
+ is an error if it is initialized.
+ Also if it is not file scope.
+ Otherwise, let it through, but if it is not `extern'
+ then it may cause an error message later. */
+ (DECL_INITIAL (decl) != 0
+ || current_binding_level != global_binding_level)
+ :
+ /* An automatic variable with an incomplete type
+ is an error. */
+ !DECL_EXTERNAL (decl)))
+ {
+ error_with_decl (decl, "storage size of `%s' isn't known");
+ TREE_TYPE (decl) = error_mark_node;
+ }
+
+ if ((DECL_EXTERNAL (decl) || TREE_STATIC (decl))
+ && DECL_SIZE (decl) != 0)
+ {
+ if (TREE_CODE (DECL_SIZE (decl)) == INTEGER_CST)
+ constant_expression_warning (DECL_SIZE (decl));
+ else
+ error_with_decl (decl, "storage size of `%s' isn't constant");
+ }
+ }
+
+ /* If this is a function and an assembler name is specified, it isn't
+ builtin any more. Also reset DECL_RTL so we can give it its new
+ name. */
+ if (TREE_CODE (decl) == FUNCTION_DECL && asmspec)
+ {
+ DECL_BUILT_IN (decl) = 0;
+ DECL_RTL (decl) = 0;
+ }
+
+ /* Output the assembler code and/or RTL code for variables and functions,
+ unless the type is an undefined structure or union.
+ If not, it will get done when the type is completed. */
+
+ if (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ if ((flag_traditional || TREE_PERMANENT (decl))
+ && allocation_temporary_p ())
+ {
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+ /* This is a no-op in c-lang.c or something real in objc-actions.c. */
+ maybe_objc_check_decl (decl);
+ rest_of_decl_compilation (decl, asmspec,
+ current_binding_level == global_binding_level,
+ 0);
+ pop_obstacks ();
+ }
+ else
+ {
+ /* This is a no-op in c-lang.c or something real in objc-actions.c. */
+ maybe_objc_check_decl (decl);
+ rest_of_decl_compilation (decl, asmspec,
+ current_binding_level == global_binding_level,
+ 0);
+ }
+ if (current_binding_level != global_binding_level)
+ {
+ /* Recompute the RTL of a local array now
+ if it used to be an incomplete type. */
+ if (was_incomplete
+ && ! TREE_STATIC (decl) && ! DECL_EXTERNAL (decl))
+ {
+ /* If we used it already as memory, it must stay in memory. */
+ TREE_ADDRESSABLE (decl) = TREE_USED (decl);
+ /* If it's still incomplete now, no init will save it. */
+ if (DECL_SIZE (decl) == 0)
+ DECL_INITIAL (decl) = 0;
+ expand_decl (decl);
+ }
+ /* Compute and store the initial value. */
+ if (TREE_CODE (decl) != FUNCTION_DECL)
+ expand_decl_init (decl);
+ }
+ }
+
+ if (TREE_CODE (decl) == TYPE_DECL)
+ {
+ /* This is a no-op in c-lang.c or something real in objc-actions.c. */
+ maybe_objc_check_decl (decl);
+ rest_of_decl_compilation (decl, NULL_PTR,
+ current_binding_level == global_binding_level,
+ 0);
+ }
+
+ /* ??? After 2.3, test (init != 0) instead of TREE_CODE. */
+ /* This test used to include TREE_PERMANENT, however, we have the same
+ problem with initializers at the function level. Such initializers get
+ saved until the end of the function on the momentary_obstack. */
+ if (!(TREE_CODE (decl) == FUNCTION_DECL && DECL_INLINE (decl))
+ && temporary
+ /* DECL_INITIAL is not defined in PARM_DECLs, since it shares
+ space with DECL_ARG_TYPE. */
+ && TREE_CODE (decl) != PARM_DECL)
+ {
+ /* We need to remember that this array HAD an initialization,
+ but discard the actual temporary nodes,
+ since we can't have a permanent node keep pointing to them. */
+ /* We make an exception for inline functions, since it's
+ normal for a local extern redeclaration of an inline function
+ to have a copy of the top-level decl's DECL_INLINE. */
+ if (DECL_INITIAL (decl) != 0 && DECL_INITIAL (decl) != error_mark_node)
+ {
+ /* If this is a const variable, then preserve the
+ initializer instead of discarding it so that we can optimize
+ references to it. */
+ /* This test used to include TREE_STATIC, but this won't be set
+ for function level initializers. */
+ if (TREE_READONLY (decl))
+ {
+ preserve_initializer ();
+ /* Hack? Set the permanent bit for something that is permanent,
+ but not on the permenent obstack, so as to convince
+ output_constant_def to make its rtl on the permanent
+ obstack. */
+ TREE_PERMANENT (DECL_INITIAL (decl)) = 1;
+
+ /* The initializer and DECL must have the same (or equivalent
+ types), but if the initializer is a STRING_CST, its type
+ might not be on the right obstack, so copy the type
+ of DECL. */
+ TREE_TYPE (DECL_INITIAL (decl)) = type;
+ }
+ else
+ DECL_INITIAL (decl) = error_mark_node;
+ }
+ }
+
+ /* If requested, warn about definitions of large data objects. */
+
+ if (warn_larger_than
+ && (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == PARM_DECL)
+ && !DECL_EXTERNAL (decl))
+ {
+ register tree decl_size = DECL_SIZE (decl);
+
+ if (decl_size && TREE_CODE (decl_size) == INTEGER_CST)
+ {
+ unsigned units = TREE_INT_CST_LOW(decl_size) / BITS_PER_UNIT;
+
+ if (units > larger_than_size)
+ warning_with_decl (decl, "size of `%s' is %u bytes", units);
+ }
+ }
+
+#if 0
+ /* Resume permanent allocation, if not within a function. */
+ /* The corresponding push_obstacks_nochange is in start_decl,
+ and in push_parm_decl and in grokfield. */
+ pop_obstacks ();
+#endif
+
+ /* If we have gone back from temporary to permanent allocation,
+ actually free the temporary space that we no longer need. */
+ if (temporary && !allocation_temporary_p ())
+ permanent_allocation (0);
+
+ /* At the end of a declaration, throw away any variable type sizes
+ of types defined inside that declaration. There is no use
+ computing them in the following function definition. */
+ if (current_binding_level == global_binding_level)
+ get_pending_sizes ();
+}
+
+/* If DECL has a cleanup, build and return that cleanup here.
+ This is a callback called by expand_expr. */
+
+tree
+maybe_build_cleanup (decl)
+ tree decl;
+{
+ /* There are no cleanups in C. */
+ return NULL_TREE;
+}
+
+/* Given a parsed parameter declaration,
+ decode it into a PARM_DECL and push that on the current binding level.
+ Also, for the sake of forward parm decls,
+ record the given order of parms in `parm_order'. */
+
+void
+push_parm_decl (parm)
+ tree parm;
+{
+ tree decl;
+ int old_immediate_size_expand = immediate_size_expand;
+ /* Don't try computing parm sizes now -- wait till fn is called. */
+ immediate_size_expand = 0;
+
+ /* The corresponding pop_obstacks is in finish_decl. */
+ push_obstacks_nochange ();
+
+ decl = grokdeclarator (TREE_VALUE (parm), TREE_PURPOSE (parm), PARM, 0);
+
+#if 0
+ if (DECL_NAME (decl))
+ {
+ tree olddecl;
+ olddecl = lookup_name (DECL_NAME (decl));
+ if (pedantic && olddecl != 0 && TREE_CODE (olddecl) == TYPE_DECL)
+ pedwarn_with_decl (decl, "ANSI C forbids parameter `%s' shadowing typedef");
+ }
+#endif
+
+ decl = pushdecl (decl);
+
+ immediate_size_expand = old_immediate_size_expand;
+
+ current_binding_level->parm_order
+ = tree_cons (NULL_TREE, decl, current_binding_level->parm_order);
+
+ /* Add this decl to the current binding level. */
+ finish_decl (decl, NULL_TREE, NULL_TREE);
+}
+
+/* Clear the given order of parms in `parm_order'.
+ Used at start of parm list,
+ and also at semicolon terminating forward decls. */
+
+void
+clear_parm_order ()
+{
+ current_binding_level->parm_order = NULL_TREE;
+}
+
+/* Make TYPE a complete type based on INITIAL_VALUE.
+ Return 0 if successful, 1 if INITIAL_VALUE can't be deciphered,
+ 2 if there was no information (in which case assume 1 if DO_DEFAULT). */
+
+int
+complete_array_type (type, initial_value, do_default)
+ tree type;
+ tree initial_value;
+ int do_default;
+{
+ register tree maxindex = NULL_TREE;
+ int value = 0;
+
+ if (initial_value)
+ {
+ /* Note MAXINDEX is really the maximum index,
+ one less than the size. */
+ if (TREE_CODE (initial_value) == STRING_CST)
+ {
+ int eltsize
+ = int_size_in_bytes (TREE_TYPE (TREE_TYPE (initial_value)));
+ maxindex = build_int_2 ((TREE_STRING_LENGTH (initial_value)
+ / eltsize) - 1, 0);
+ }
+ else if (TREE_CODE (initial_value) == CONSTRUCTOR)
+ {
+ tree elts = CONSTRUCTOR_ELTS (initial_value);
+ maxindex = size_binop (MINUS_EXPR, integer_zero_node, size_one_node);
+ for (; elts; elts = TREE_CHAIN (elts))
+ {
+ if (TREE_PURPOSE (elts))
+ maxindex = TREE_PURPOSE (elts);
+ else
+ maxindex = size_binop (PLUS_EXPR, maxindex, size_one_node);
+ }
+ maxindex = copy_node (maxindex);
+ }
+ else
+ {
+ /* Make an error message unless that happened already. */
+ if (initial_value != error_mark_node)
+ value = 1;
+
+ /* Prevent further error messages. */
+ maxindex = build_int_2 (0, 0);
+ }
+ }
+
+ if (!maxindex)
+ {
+ if (do_default)
+ maxindex = build_int_2 (0, 0);
+ value = 2;
+ }
+
+ if (maxindex)
+ {
+ TYPE_DOMAIN (type) = build_index_type (maxindex);
+ if (!TREE_TYPE (maxindex))
+ TREE_TYPE (maxindex) = TYPE_DOMAIN (type);
+#if 0 /* I took out this change
+ together with the change in build_array_type. --rms */
+ change_main_variant (type,
+ build_array_type (TREE_TYPE (type),
+ TYPE_DOMAIN (type)));
+#endif
+ }
+
+ /* Lay out the type now that we can get the real answer. */
+
+ layout_type (type);
+
+ return value;
+}
+
+/* Given declspecs and a declarator,
+ determine the name and type of the object declared
+ and construct a ..._DECL node for it.
+ (In one case we can return a ..._TYPE node instead.
+ For invalid input we sometimes return 0.)
+
+ DECLSPECS is a chain of tree_list nodes whose value fields
+ are the storage classes and type specifiers.
+
+ DECL_CONTEXT says which syntactic context this declaration is in:
+ NORMAL for most contexts. Make a VAR_DECL or FUNCTION_DECL or TYPE_DECL.
+ FUNCDEF for a function definition. Like NORMAL but a few different
+ error messages in each case. Return value may be zero meaning
+ this definition is too screwy to try to parse.
+ PARM for a parameter declaration (either within a function prototype
+ or before a function body). Make a PARM_DECL, or return void_type_node.
+ TYPENAME if for a typename (in a cast or sizeof).
+ Don't make a DECL node; just return the ..._TYPE node.
+ FIELD for a struct or union field; make a FIELD_DECL.
+ BITFIELD for a field with specified width.
+ INITIALIZED is 1 if the decl has an initializer.
+
+ In the TYPENAME case, DECLARATOR is really an absolute declarator.
+ It may also be so in the PARM case, for a prototype where the
+ argument type is specified but not the name.
+
+ This function is where the complicated C meanings of `static'
+ and `extern' are interpreted. */
+
+static tree
+grokdeclarator (declarator, declspecs, decl_context, initialized)
+ tree declspecs;
+ tree declarator;
+ enum decl_context decl_context;
+ int initialized;
+{
+ int specbits = 0;
+ tree spec;
+ tree type = NULL_TREE;
+ int longlong = 0;
+ int constp;
+ int volatilep;
+ int inlinep;
+ int explicit_int = 0;
+ int explicit_char = 0;
+ int defaulted_int = 0;
+ tree typedef_decl = 0;
+ char *name;
+ tree typedef_type = 0;
+ int funcdef_flag = 0;
+ enum tree_code innermost_code = ERROR_MARK;
+ int bitfield = 0;
+ int size_varies = 0;
+
+ if (decl_context == BITFIELD)
+ bitfield = 1, decl_context = FIELD;
+
+ if (decl_context == FUNCDEF)
+ funcdef_flag = 1, decl_context = NORMAL;
+
+ push_obstacks_nochange ();
+
+ if (flag_traditional && allocation_temporary_p ())
+ end_temporary_allocation ();
+
+ /* Look inside a declarator for the name being declared
+ and get it as a string, for an error message. */
+ {
+ register tree decl = declarator;
+ name = 0;
+
+ while (decl)
+ switch (TREE_CODE (decl))
+ {
+ case ARRAY_REF:
+ case INDIRECT_REF:
+ case CALL_EXPR:
+ innermost_code = TREE_CODE (decl);
+ decl = TREE_OPERAND (decl, 0);
+ break;
+
+ case IDENTIFIER_NODE:
+ name = IDENTIFIER_POINTER (decl);
+ decl = 0;
+ break;
+
+ default:
+ abort ();
+ }
+ if (name == 0)
+ name = "type name";
+ }
+
+ /* A function definition's declarator must have the form of
+ a function declarator. */
+
+ if (funcdef_flag && innermost_code != CALL_EXPR)
+ return 0;
+
+ /* Anything declared one level down from the top level
+ must be one of the parameters of a function
+ (because the body is at least two levels down). */
+
+ /* If this looks like a function definition, make it one,
+ even if it occurs where parms are expected.
+ Then store_parm_decls will reject it and not use it as a parm. */
+ if (decl_context == NORMAL && !funcdef_flag
+ && current_binding_level->level_chain == global_binding_level)
+ decl_context = PARM;
+
+ /* Look through the decl specs and record which ones appear.
+ Some typespecs are defined as built-in typenames.
+ Others, the ones that are modifiers of other types,
+ are represented by bits in SPECBITS: set the bits for
+ the modifiers that appear. Storage class keywords are also in SPECBITS.
+
+ If there is a typedef name or a type, store the type in TYPE.
+ This includes builtin typedefs such as `int'.
+
+ Set EXPLICIT_INT or EXPLICIT_CHAR if the type is `int' or `char'
+ and did not come from a user typedef.
+
+ Set LONGLONG if `long' is mentioned twice. */
+
+ for (spec = declspecs; spec; spec = TREE_CHAIN (spec))
+ {
+ register int i;
+ register tree id = TREE_VALUE (spec);
+
+ if (id == ridpointers[(int) RID_INT])
+ explicit_int = 1;
+ if (id == ridpointers[(int) RID_CHAR])
+ explicit_char = 1;
+
+ if (TREE_CODE (id) == IDENTIFIER_NODE)
+ for (i = (int) RID_FIRST_MODIFIER; i < (int) RID_MAX; i++)
+ {
+ if (ridpointers[i] == id)
+ {
+ if (i == (int) RID_LONG && specbits & (1<<i))
+ {
+ if (longlong)
+ error ("`long long long' is too long for GCC");
+ else
+ {
+ if (pedantic && ! in_system_header)
+ pedwarn ("ANSI C does not support `long long'");
+ longlong = 1;
+ }
+ }
+ else if (specbits & (1 << i))
+ pedwarn ("duplicate `%s'", IDENTIFIER_POINTER (id));
+ specbits |= 1 << i;
+ goto found;
+ }
+ }
+ if (type)
+ error ("two or more data types in declaration of `%s'", name);
+ /* Actual typedefs come to us as TYPE_DECL nodes. */
+ else if (TREE_CODE (id) == TYPE_DECL)
+ {
+ type = TREE_TYPE (id);
+ typedef_decl = id;
+ }
+ /* Built-in types come as identifiers. */
+ else if (TREE_CODE (id) == IDENTIFIER_NODE)
+ {
+ register tree t = lookup_name (id);
+ if (TREE_TYPE (t) == error_mark_node)
+ ;
+ else if (!t || TREE_CODE (t) != TYPE_DECL)
+ error ("`%s' fails to be a typedef or built in type",
+ IDENTIFIER_POINTER (id));
+ else
+ {
+ type = TREE_TYPE (t);
+ typedef_decl = t;
+ }
+ }
+ else if (TREE_CODE (id) != ERROR_MARK)
+ type = id;
+
+ found: {}
+ }
+
+ typedef_type = type;
+ if (type)
+ size_varies = C_TYPE_VARIABLE_SIZE (type);
+
+ /* No type at all: default to `int', and set DEFAULTED_INT
+ because it was not a user-defined typedef. */
+
+ if (type == 0)
+ {
+ if (funcdef_flag && warn_return_type
+ && ! (specbits & ((1 << (int) RID_LONG) | (1 << (int) RID_SHORT)
+ | (1 << (int) RID_SIGNED) | (1 << (int) RID_UNSIGNED))))
+ warn_about_return_type = 1;
+ defaulted_int = 1;
+ type = integer_type_node;
+ }
+
+ /* Now process the modifiers that were specified
+ and check for invalid combinations. */
+
+ /* Long double is a special combination. */
+
+ if ((specbits & 1 << (int) RID_LONG)
+ && TYPE_MAIN_VARIANT (type) == double_type_node)
+ {
+ specbits &= ~ (1 << (int) RID_LONG);
+ type = long_double_type_node;
+ }
+
+ /* Check all other uses of type modifiers. */
+
+ if (specbits & ((1 << (int) RID_LONG) | (1 << (int) RID_SHORT)
+ | (1 << (int) RID_UNSIGNED) | (1 << (int) RID_SIGNED)))
+ {
+ int ok = 0;
+
+ if (TREE_CODE (type) != INTEGER_TYPE)
+ error ("long, short, signed or unsigned invalid for `%s'", name);
+ else if ((specbits & 1 << (int) RID_LONG)
+ && (specbits & 1 << (int) RID_SHORT))
+ error ("long and short specified together for `%s'", name);
+ else if (((specbits & 1 << (int) RID_LONG)
+ || (specbits & 1 << (int) RID_SHORT))
+ && explicit_char)
+ error ("long or short specified with char for `%s'", name);
+ else if (((specbits & 1 << (int) RID_LONG)
+ || (specbits & 1 << (int) RID_SHORT))
+ && TREE_CODE (type) == REAL_TYPE)
+ error ("long or short specified with floating type for `%s'", name);
+ else if ((specbits & 1 << (int) RID_SIGNED)
+ && (specbits & 1 << (int) RID_UNSIGNED))
+ error ("signed and unsigned given together for `%s'", name);
+ else
+ {
+ ok = 1;
+ if (!explicit_int && !defaulted_int && !explicit_char && pedantic)
+ {
+ pedwarn ("long, short, signed or unsigned used invalidly for `%s'",
+ name);
+ if (flag_pedantic_errors)
+ ok = 0;
+ }
+ }
+
+ /* Discard the type modifiers if they are invalid. */
+ if (! ok)
+ {
+ specbits &= ~((1 << (int) RID_LONG) | (1 << (int) RID_SHORT)
+ | (1 << (int) RID_UNSIGNED) | (1 << (int) RID_SIGNED));
+ longlong = 0;
+ }
+ }
+
+ if ((specbits & (1 << (int) RID_COMPLEX))
+ && TREE_CODE (type) != INTEGER_TYPE && TREE_CODE (type) != REAL_TYPE)
+ {
+ error ("complex invalid for `%s'", name);
+ specbits &= ~ (1 << (int) RID_COMPLEX);
+ }
+
+ /* Decide whether an integer type is signed or not.
+ Optionally treat bitfields as signed by default. */
+ if (specbits & 1 << (int) RID_UNSIGNED
+ /* Traditionally, all bitfields are unsigned. */
+ || (bitfield && flag_traditional
+ && (! explicit_flag_signed_bitfields || !flag_signed_bitfields))
+ || (bitfield && ! flag_signed_bitfields
+ && (explicit_int || defaulted_int || explicit_char
+ /* A typedef for plain `int' without `signed'
+ can be controlled just like plain `int'. */
+ || ! (typedef_decl != 0
+ && C_TYPEDEF_EXPLICITLY_SIGNED (typedef_decl)))
+ && TREE_CODE (type) != ENUMERAL_TYPE
+ && !(specbits & 1 << (int) RID_SIGNED)))
+ {
+ if (longlong)
+ type = long_long_unsigned_type_node;
+ else if (specbits & 1 << (int) RID_LONG)
+ type = long_unsigned_type_node;
+ else if (specbits & 1 << (int) RID_SHORT)
+ type = short_unsigned_type_node;
+ else if (type == char_type_node)
+ type = unsigned_char_type_node;
+ else if (typedef_decl)
+ type = unsigned_type (type);
+ else
+ type = unsigned_type_node;
+ }
+ else if ((specbits & 1 << (int) RID_SIGNED)
+ && type == char_type_node)
+ type = signed_char_type_node;
+ else if (longlong)
+ type = long_long_integer_type_node;
+ else if (specbits & 1 << (int) RID_LONG)
+ type = long_integer_type_node;
+ else if (specbits & 1 << (int) RID_SHORT)
+ type = short_integer_type_node;
+
+ if (specbits & 1 << (int) RID_COMPLEX)
+ {
+ /* If we just have "complex", it is equivalent to
+ "complex double", but if any modifiers at all are specified it is
+ the complex form of TYPE. E.g, "complex short" is
+ "complex short int". */
+
+ if (defaulted_int && ! longlong
+ && ! (specbits & ((1 << (int) RID_LONG) | (1 << (int) RID_SHORT)
+ | (1 << (int) RID_SIGNED)
+ | (1 << (int) RID_UNSIGNED))))
+ type = complex_double_type_node;
+ else if (type == integer_type_node)
+ type = complex_integer_type_node;
+ else if (type == float_type_node)
+ type = complex_float_type_node;
+ else if (type == double_type_node)
+ type = complex_double_type_node;
+ else if (type == long_double_type_node)
+ type = complex_long_double_type_node;
+ else
+ type = build_complex_type (type);
+ }
+
+ /* Set CONSTP if this declaration is `const', whether by
+ explicit specification or via a typedef.
+ Likewise for VOLATILEP. */
+
+ constp = !! (specbits & 1 << (int) RID_CONST) + TYPE_READONLY (type);
+ volatilep = !! (specbits & 1 << (int) RID_VOLATILE) + TYPE_VOLATILE (type);
+ inlinep = !! (specbits & (1 << (int) RID_INLINE));
+ if (constp > 1)
+ pedwarn ("duplicate `const'");
+ if (volatilep > 1)
+ pedwarn ("duplicate `volatile'");
+ if (! flag_gen_aux_info && (TYPE_READONLY (type) || TYPE_VOLATILE (type)))
+ type = TYPE_MAIN_VARIANT (type);
+
+ /* Warn if two storage classes are given. Default to `auto'. */
+
+ {
+ int nclasses = 0;
+
+ if (specbits & 1 << (int) RID_AUTO) nclasses++;
+ if (specbits & 1 << (int) RID_STATIC) nclasses++;
+ if (specbits & 1 << (int) RID_EXTERN) nclasses++;
+ if (specbits & 1 << (int) RID_REGISTER) nclasses++;
+ if (specbits & 1 << (int) RID_TYPEDEF) nclasses++;
+ if (specbits & 1 << (int) RID_ITERATOR) nclasses++;
+
+ /* Warn about storage classes that are invalid for certain
+ kinds of declarations (parameters, typenames, etc.). */
+
+ if (nclasses > 1)
+ error ("multiple storage classes in declaration of `%s'", name);
+ else if (funcdef_flag
+ && (specbits
+ & ((1 << (int) RID_REGISTER)
+ | (1 << (int) RID_AUTO)
+ | (1 << (int) RID_TYPEDEF))))
+ {
+ if (specbits & 1 << (int) RID_AUTO
+ && (pedantic || current_binding_level == global_binding_level))
+ pedwarn ("function definition declared `auto'");
+ if (specbits & 1 << (int) RID_REGISTER)
+ error ("function definition declared `register'");
+ if (specbits & 1 << (int) RID_TYPEDEF)
+ error ("function definition declared `typedef'");
+ specbits &= ~ ((1 << (int) RID_TYPEDEF) | (1 << (int) RID_REGISTER)
+ | (1 << (int) RID_AUTO));
+ }
+ else if (decl_context != NORMAL && nclasses > 0)
+ {
+ if (decl_context == PARM && specbits & 1 << (int) RID_REGISTER)
+ ;
+ else
+ {
+ error ((decl_context == FIELD
+ ? "storage class specified for structure field `%s'"
+ : (decl_context == PARM
+ ? "storage class specified for parameter `%s'"
+ : "storage class specified for typename")),
+ name);
+ specbits &= ~ ((1 << (int) RID_TYPEDEF) | (1 << (int) RID_REGISTER)
+ | (1 << (int) RID_AUTO) | (1 << (int) RID_STATIC)
+ | (1 << (int) RID_EXTERN));
+ }
+ }
+ else if (specbits & 1 << (int) RID_EXTERN && initialized && ! funcdef_flag)
+ {
+ /* `extern' with initialization is invalid if not at top level. */
+ if (current_binding_level == global_binding_level)
+ warning ("`%s' initialized and declared `extern'", name);
+ else
+ error ("`%s' has both `extern' and initializer", name);
+ }
+ else if (specbits & 1 << (int) RID_EXTERN && funcdef_flag
+ && current_binding_level != global_binding_level)
+ error ("nested function `%s' declared `extern'", name);
+ else if (current_binding_level == global_binding_level
+ && specbits & (1 << (int) RID_AUTO))
+ error ("top-level declaration of `%s' specifies `auto'", name);
+ else if ((specbits & 1 << (int) RID_ITERATOR)
+ && TREE_CODE (declarator) != IDENTIFIER_NODE)
+ {
+ error ("iterator `%s' has derived type", name);
+ type = error_mark_node;
+ }
+ else if ((specbits & 1 << (int) RID_ITERATOR)
+ && TREE_CODE (type) != INTEGER_TYPE)
+ {
+ error ("iterator `%s' has noninteger type", name);
+ type = error_mark_node;
+ }
+ }
+
+ /* Now figure out the structure of the declarator proper.
+ Descend through it, creating more complex types, until we reach
+ the declared identifier (or NULL_TREE, in an absolute declarator). */
+
+ while (declarator && TREE_CODE (declarator) != IDENTIFIER_NODE)
+ {
+ if (type == error_mark_node)
+ {
+ declarator = TREE_OPERAND (declarator, 0);
+ continue;
+ }
+
+ /* Each level of DECLARATOR is either an ARRAY_REF (for ...[..]),
+ an INDIRECT_REF (for *...),
+ a CALL_EXPR (for ...(...)),
+ an identifier (for the name being declared)
+ or a null pointer (for the place in an absolute declarator
+ where the name was omitted).
+ For the last two cases, we have just exited the loop.
+
+ At this point, TYPE is the type of elements of an array,
+ or for a function to return, or for a pointer to point to.
+ After this sequence of ifs, TYPE is the type of the
+ array or function or pointer, and DECLARATOR has had its
+ outermost layer removed. */
+
+ if (TREE_CODE (declarator) == ARRAY_REF)
+ {
+ register tree itype = NULL_TREE;
+ register tree size = TREE_OPERAND (declarator, 1);
+ /* An uninitialized decl with `extern' is a reference. */
+ int extern_ref = !initialized && (specbits & (1 << (int) RID_EXTERN));
+ /* The index is a signed object `sizetype' bits wide. */
+ tree index_type = signed_type (sizetype);
+
+ declarator = TREE_OPERAND (declarator, 0);
+
+ /* Check for some types that there cannot be arrays of. */
+
+ if (TYPE_MAIN_VARIANT (type) == void_type_node)
+ {
+ error ("declaration of `%s' as array of voids", name);
+ type = error_mark_node;
+ }
+
+ if (TREE_CODE (type) == FUNCTION_TYPE)
+ {
+ error ("declaration of `%s' as array of functions", name);
+ type = error_mark_node;
+ }
+
+ if (size == error_mark_node)
+ type = error_mark_node;
+
+ if (type == error_mark_node)
+ continue;
+
+ /* If this is a block level extern, it must live past the end
+ of the function so that we can check it against other extern
+ declarations (IDENTIFIER_LIMBO_VALUE). */
+ if (extern_ref && allocation_temporary_p ())
+ end_temporary_allocation ();
+
+ /* If size was specified, set ITYPE to a range-type for that size.
+ Otherwise, ITYPE remains null. finish_decl may figure it out
+ from an initial value. */
+
+ if (size)
+ {
+ /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
+ STRIP_TYPE_NOPS (size);
+
+ if (TREE_CODE (TREE_TYPE (size)) != INTEGER_TYPE
+ && TREE_CODE (TREE_TYPE (size)) != ENUMERAL_TYPE)
+ {
+ error ("size of array `%s' has non-integer type", name);
+ size = integer_one_node;
+ }
+
+ if (pedantic && integer_zerop (size))
+ pedwarn ("ANSI C forbids zero-size array `%s'", name);
+
+ if (TREE_CODE (size) == INTEGER_CST)
+ {
+ constant_expression_warning (size);
+ if (tree_int_cst_sgn (size) < 0)
+ {
+ error ("size of array `%s' is negative", name);
+ size = integer_one_node;
+ }
+ }
+ else
+ {
+ /* Make sure the array size remains visibly nonconstant
+ even if it is (eg) a const variable with known value. */
+ size_varies = 1;
+
+ if (pedantic)
+ {
+ if (TREE_CONSTANT (size))
+ pedwarn ("ANSI C forbids array `%s' whose size can't be evaluated", name);
+ else
+ pedwarn ("ANSI C forbids variable-size array `%s'", name);
+ }
+ }
+
+ /* Convert size to index_type, so that if it is a variable
+ the computations will be done in the proper mode. */
+ itype = fold (build (MINUS_EXPR, index_type,
+ convert (index_type, size),
+ convert (index_type, size_one_node)));
+
+ if (size_varies)
+ itype = variable_size (itype);
+ itype = build_index_type (itype);
+ }
+
+#if 0 /* This had bad results for pointers to arrays, as in
+ union incomplete (*foo)[4]; */
+ /* Complain about arrays of incomplete types, except in typedefs. */
+
+ if (TYPE_SIZE (type) == 0
+ /* Avoid multiple warnings for nested array types. */
+ && TREE_CODE (type) != ARRAY_TYPE
+ && !(specbits & (1 << (int) RID_TYPEDEF))
+ && !C_TYPE_BEING_DEFINED (type))
+ warning ("array type has incomplete element type");
+#endif
+
+#if 0 /* We shouldn't have a function type here at all!
+ Functions aren't allowed as array elements. */
+ if (pedantic && TREE_CODE (type) == FUNCTION_TYPE
+ && (constp || volatilep))
+ pedwarn ("ANSI C forbids const or volatile function types");
+#endif
+
+ /* Build the array type itself, then merge any constancy or
+ volatility into the target type. We must do it in this order
+ to ensure that the TYPE_MAIN_VARIANT field of the array type
+ is set correctly. */
+
+ type = build_array_type (type, itype);
+ if (constp || volatilep)
+ type = c_build_type_variant (type, constp, volatilep);
+
+#if 0 /* don't clear these; leave them set so that the array type
+ or the variable is itself const or volatile. */
+ constp = 0;
+ volatilep = 0;
+#endif
+
+ if (size_varies)
+ C_TYPE_VARIABLE_SIZE (type) = 1;
+ }
+ else if (TREE_CODE (declarator) == CALL_EXPR)
+ {
+ int extern_ref = (!(specbits & (1 << (int) RID_AUTO))
+ || current_binding_level == global_binding_level);
+ tree arg_types;
+
+ /* Declaring a function type.
+ Make sure we have a valid type for the function to return. */
+ if (type == error_mark_node)
+ continue;
+
+ size_varies = 0;
+
+ /* Warn about some types functions can't return. */
+
+ if (TREE_CODE (type) == FUNCTION_TYPE)
+ {
+ error ("`%s' declared as function returning a function", name);
+ type = integer_type_node;
+ }
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ error ("`%s' declared as function returning an array", name);
+ type = integer_type_node;
+ }
+
+#ifndef TRADITIONAL_RETURN_FLOAT
+ /* Traditionally, declaring return type float means double. */
+
+ if (flag_traditional && TYPE_MAIN_VARIANT (type) == float_type_node)
+ type = double_type_node;
+#endif /* TRADITIONAL_RETURN_FLOAT */
+
+ /* If this is a block level extern, it must live past the end
+ of the function so that we can check it against other extern
+ declarations (IDENTIFIER_LIMBO_VALUE). */
+ if (extern_ref && allocation_temporary_p ())
+ end_temporary_allocation ();
+
+ /* Construct the function type and go to the next
+ inner layer of declarator. */
+
+ arg_types = grokparms (TREE_OPERAND (declarator, 1),
+ funcdef_flag
+ /* Say it's a definition
+ only for the CALL_EXPR
+ closest to the identifier. */
+ && TREE_CODE (TREE_OPERAND (declarator, 0)) == IDENTIFIER_NODE);
+#if 0 /* This seems to be false. We turn off temporary allocation
+ above in this function if -traditional.
+ And this code caused inconsistent results with prototypes:
+ callers would ignore them, and pass arguments wrong. */
+
+ /* Omit the arg types if -traditional, since the arg types
+ and the list links might not be permanent. */
+ type = build_function_type (type,
+ flag_traditional
+ ? NULL_TREE : arg_types);
+#endif
+ /* ANSI seems to say that `const int foo ();'
+ does not make the function foo const. */
+ if (constp || volatilep)
+ type = c_build_type_variant (type, constp, volatilep);
+ constp = 0;
+ volatilep = 0;
+
+ type = build_function_type (type, arg_types);
+ declarator = TREE_OPERAND (declarator, 0);
+
+ /* Set the TYPE_CONTEXTs for each tagged type which is local to
+ the formal parameter list of this FUNCTION_TYPE to point to
+ the FUNCTION_TYPE node itself. */
+
+ {
+ register tree link;
+
+ for (link = current_function_parm_tags;
+ link;
+ link = TREE_CHAIN (link))
+ TYPE_CONTEXT (TREE_VALUE (link)) = type;
+ }
+ }
+ else if (TREE_CODE (declarator) == INDIRECT_REF)
+ {
+ /* Merge any constancy or volatility into the target type
+ for the pointer. */
+
+ if (pedantic && TREE_CODE (type) == FUNCTION_TYPE
+ && (constp || volatilep))
+ pedwarn ("ANSI C forbids const or volatile function types");
+ if (constp || volatilep)
+ type = c_build_type_variant (type, constp, volatilep);
+ constp = 0;
+ volatilep = 0;
+ size_varies = 0;
+
+ type = build_pointer_type (type);
+
+ /* Process a list of type modifier keywords
+ (such as const or volatile) that were given inside the `*'. */
+
+ if (TREE_TYPE (declarator))
+ {
+ register tree typemodlist;
+ int erred = 0;
+ for (typemodlist = TREE_TYPE (declarator); typemodlist;
+ typemodlist = TREE_CHAIN (typemodlist))
+ {
+ if (TREE_VALUE (typemodlist) == ridpointers[(int) RID_CONST])
+ constp++;
+ else if (TREE_VALUE (typemodlist) == ridpointers[(int) RID_VOLATILE])
+ volatilep++;
+ else if (!erred)
+ {
+ erred = 1;
+ error ("invalid type modifier within pointer declarator");
+ }
+ }
+ if (constp > 1)
+ pedwarn ("duplicate `const'");
+ if (volatilep > 1)
+ pedwarn ("duplicate `volatile'");
+ }
+
+ declarator = TREE_OPERAND (declarator, 0);
+ }
+ else
+ abort ();
+
+ }
+
+ /* Now TYPE has the actual type. */
+
+ /* If this is declaring a typedef name, return a TYPE_DECL. */
+
+ if (specbits & (1 << (int) RID_TYPEDEF))
+ {
+ tree decl;
+ /* Note that the grammar rejects storage classes
+ in typenames, fields or parameters */
+ if (pedantic && TREE_CODE (type) == FUNCTION_TYPE
+ && (constp || volatilep))
+ pedwarn ("ANSI C forbids const or volatile function types");
+ if (constp || volatilep)
+ type = c_build_type_variant (type, constp, volatilep);
+ pop_obstacks ();
+ decl = build_decl (TYPE_DECL, declarator, type);
+ if ((specbits & (1 << (int) RID_SIGNED))
+ || (typedef_decl && C_TYPEDEF_EXPLICITLY_SIGNED (typedef_decl)))
+ C_TYPEDEF_EXPLICITLY_SIGNED (decl) = 1;
+ return decl;
+ }
+
+ /* Detect the case of an array type of unspecified size
+ which came, as such, direct from a typedef name.
+ We must copy the type, so that each identifier gets
+ a distinct type, so that each identifier's size can be
+ controlled separately by its own initializer. */
+
+ if (type != 0 && typedef_type != 0
+ && TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (typedef_type)
+ && TREE_CODE (type) == ARRAY_TYPE && TYPE_DOMAIN (type) == 0)
+ {
+ type = build_array_type (TREE_TYPE (type), 0);
+ if (size_varies)
+ C_TYPE_VARIABLE_SIZE (type) = 1;
+ }
+
+ /* If this is a type name (such as, in a cast or sizeof),
+ compute the type and return it now. */
+
+ if (decl_context == TYPENAME)
+ {
+ /* Note that the grammar rejects storage classes
+ in typenames, fields or parameters */
+ if (pedantic && TREE_CODE (type) == FUNCTION_TYPE
+ && (constp || volatilep))
+ pedwarn ("ANSI C forbids const or volatile function types");
+ if (constp || volatilep)
+ type = c_build_type_variant (type, constp, volatilep);
+ pop_obstacks ();
+ return type;
+ }
+
+ /* Aside from typedefs and type names (handle above),
+ `void' at top level (not within pointer)
+ is allowed only in public variables.
+ We don't complain about parms either, but that is because
+ a better error message can be made later. */
+
+ if (TYPE_MAIN_VARIANT (type) == void_type_node && decl_context != PARM
+ && ! ((decl_context != FIELD && TREE_CODE (type) != FUNCTION_TYPE)
+ && ((specbits & (1 << (int) RID_EXTERN))
+ || (current_binding_level == global_binding_level
+ && !(specbits
+ & ((1 << (int) RID_STATIC) | (1 << (int) RID_REGISTER)))))))
+ {
+ error ("variable or field `%s' declared void",
+ IDENTIFIER_POINTER (declarator));
+ type = integer_type_node;
+ }
+
+ /* Now create the decl, which may be a VAR_DECL, a PARM_DECL
+ or a FUNCTION_DECL, depending on DECL_CONTEXT and TYPE. */
+
+ {
+ register tree decl;
+
+ if (decl_context == PARM)
+ {
+ tree type_as_written = type;
+ tree main_type;
+
+ /* A parameter declared as an array of T is really a pointer to T.
+ One declared as a function is really a pointer to a function. */
+
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ /* Transfer const-ness of array into that of type pointed to. */
+ type = TREE_TYPE (type);
+ if (constp || volatilep)
+ type = c_build_type_variant (type, constp, volatilep);
+ type = build_pointer_type (type);
+ volatilep = constp = 0;
+ size_varies = 0;
+ }
+ else if (TREE_CODE (type) == FUNCTION_TYPE)
+ {
+ if (pedantic && (constp || volatilep))
+ pedwarn ("ANSI C forbids const or volatile function types");
+ if (constp || volatilep)
+ type = c_build_type_variant (type, constp, volatilep);
+ type = build_pointer_type (type);
+ volatilep = constp = 0;
+ }
+
+ decl = build_decl (PARM_DECL, declarator, type);
+ if (size_varies)
+ C_DECL_VARIABLE_SIZE (decl) = 1;
+
+ /* Compute the type actually passed in the parmlist,
+ for the case where there is no prototype.
+ (For example, shorts and chars are passed as ints.)
+ When there is a prototype, this is overridden later. */
+
+ DECL_ARG_TYPE (decl) = type;
+ main_type = (type == error_mark_node
+ ? error_mark_node
+ : TYPE_MAIN_VARIANT (type));
+ if (main_type == float_type_node)
+ DECL_ARG_TYPE (decl) = double_type_node;
+ /* Don't use TYPE_PRECISION to decide whether to promote,
+ because we should convert short if it's the same size as int,
+ but we should not convert long if it's the same size as int. */
+ else if (TREE_CODE (main_type) != ERROR_MARK
+ && C_PROMOTING_INTEGER_TYPE_P (main_type))
+ {
+ if (TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node)
+ && TREE_UNSIGNED (type))
+ DECL_ARG_TYPE (decl) = unsigned_type_node;
+ else
+ DECL_ARG_TYPE (decl) = integer_type_node;
+ }
+
+ DECL_ARG_TYPE_AS_WRITTEN (decl) = type_as_written;
+ }
+ else if (decl_context == FIELD)
+ {
+ /* Structure field. It may not be a function. */
+
+ if (TREE_CODE (type) == FUNCTION_TYPE)
+ {
+ error ("field `%s' declared as a function",
+ IDENTIFIER_POINTER (declarator));
+ type = build_pointer_type (type);
+ }
+ else if (TREE_CODE (type) != ERROR_MARK && TYPE_SIZE (type) == 0)
+ {
+ error ("field `%s' has incomplete type",
+ IDENTIFIER_POINTER (declarator));
+ type = error_mark_node;
+ }
+ /* Move type qualifiers down to element of an array. */
+ if (TREE_CODE (type) == ARRAY_TYPE && (constp || volatilep))
+ {
+ type = build_array_type (c_build_type_variant (TREE_TYPE (type),
+ constp, volatilep),
+ TYPE_DOMAIN (type));
+#if 0 /* Leave the field const or volatile as well. */
+ constp = volatilep = 0;
+#endif
+ }
+ decl = build_decl (FIELD_DECL, declarator, type);
+ if (size_varies)
+ C_DECL_VARIABLE_SIZE (decl) = 1;
+ }
+ else if (TREE_CODE (type) == FUNCTION_TYPE)
+ {
+ /* Every function declaration is "external"
+ except for those which are inside a function body
+ in which `auto' is used.
+ That is a case not specified by ANSI C,
+ and we use it for forward declarations for nested functions. */
+ int extern_ref = (!(specbits & (1 << (int) RID_AUTO))
+ || current_binding_level == global_binding_level);
+
+ if (specbits & (1 << (int) RID_AUTO)
+ && (pedantic || current_binding_level == global_binding_level))
+ pedwarn ("invalid storage class for function `%s'",
+ IDENTIFIER_POINTER (declarator));
+ if (specbits & (1 << (int) RID_REGISTER))
+ error ("invalid storage class for function `%s'",
+ IDENTIFIER_POINTER (declarator));
+ /* Function declaration not at top level.
+ Storage classes other than `extern' are not allowed
+ and `extern' makes no difference. */
+ if (current_binding_level != global_binding_level
+ && (specbits & ((1 << (int) RID_STATIC) | (1 << (int) RID_INLINE)))
+ && pedantic)
+ pedwarn ("invalid storage class for function `%s'",
+ IDENTIFIER_POINTER (declarator));
+
+ /* If this is a block level extern, it must live past the end
+ of the function so that we can check it against other
+ extern declarations (IDENTIFIER_LIMBO_VALUE). */
+ if (extern_ref && allocation_temporary_p ())
+ end_temporary_allocation ();
+
+ decl = build_decl (FUNCTION_DECL, declarator, type);
+
+ if (pedantic && (constp || volatilep)
+ && ! DECL_IN_SYSTEM_HEADER (decl))
+ pedwarn ("ANSI C forbids const or volatile functions");
+
+ if (volatilep
+ && TREE_TYPE (TREE_TYPE (decl)) != void_type_node)
+ warning ("`noreturn' function returns non-void value");
+
+ if (extern_ref)
+ DECL_EXTERNAL (decl) = 1;
+ /* Record absence of global scope for `static' or `auto'. */
+ TREE_PUBLIC (decl)
+ = !(specbits & ((1 << (int) RID_STATIC) | (1 << (int) RID_AUTO)));
+ /* Record presence of `inline', if it is reasonable. */
+ if (inlinep)
+ {
+ tree last = tree_last (TYPE_ARG_TYPES (type));
+
+ if (! strcmp (IDENTIFIER_POINTER (declarator), "main"))
+ warning ("cannot inline function `main'");
+ else if (last && (TYPE_MAIN_VARIANT (TREE_VALUE (last))
+ != void_type_node))
+ warning ("inline declaration ignored for function with `...'");
+ else
+ /* Assume that otherwise the function can be inlined. */
+ DECL_INLINE (decl) = 1;
+
+ if (specbits & (1 << (int) RID_EXTERN))
+ current_extern_inline = 1;
+ }
+ }
+ else
+ {
+ /* It's a variable. */
+ /* An uninitialized decl with `extern' is a reference. */
+ int extern_ref = !initialized && (specbits & (1 << (int) RID_EXTERN));
+
+ /* Move type qualifiers down to element of an array. */
+ if (TREE_CODE (type) == ARRAY_TYPE && (constp || volatilep))
+ {
+ type = build_array_type (c_build_type_variant (TREE_TYPE (type),
+ constp, volatilep),
+ TYPE_DOMAIN (type));
+#if 0 /* Leave the variable const or volatile as well. */
+ constp = volatilep = 0;
+#endif
+ }
+
+ /* If this is a block level extern, it must live past the end
+ of the function so that we can check it against other
+ extern declarations (IDENTIFIER_LIMBO_VALUE). */
+ if (extern_ref && allocation_temporary_p ())
+ end_temporary_allocation ();
+
+ decl = build_decl (VAR_DECL, declarator, type);
+ if (size_varies)
+ C_DECL_VARIABLE_SIZE (decl) = 1;
+
+ if (inlinep)
+ pedwarn_with_decl (decl, "variable `%s' declared `inline'");
+
+ DECL_EXTERNAL (decl) = extern_ref;
+ /* At top level, the presence of a `static' or `register' storage
+ class specifier, or the absence of all storage class specifiers
+ makes this declaration a definition (perhaps tentative). Also,
+ the absence of both `static' and `register' makes it public. */
+ if (current_binding_level == global_binding_level)
+ {
+ TREE_PUBLIC (decl)
+ = !(specbits
+ & ((1 << (int) RID_STATIC) | (1 << (int) RID_REGISTER)));
+ TREE_STATIC (decl) = ! DECL_EXTERNAL (decl);
+ }
+ /* Not at top level, only `static' makes a static definition. */
+ else
+ {
+ TREE_STATIC (decl) = (specbits & (1 << (int) RID_STATIC)) != 0;
+ TREE_PUBLIC (decl) = DECL_EXTERNAL (decl);
+ }
+
+ if (specbits & 1 << (int) RID_ITERATOR)
+ ITERATOR_P (decl) = 1;
+ }
+
+ /* Record `register' declaration for warnings on &
+ and in case doing stupid register allocation. */
+
+ if (specbits & (1 << (int) RID_REGISTER))
+ DECL_REGISTER (decl) = 1;
+
+ /* Record constancy and volatility. */
+
+ if (constp)
+ TREE_READONLY (decl) = 1;
+ if (volatilep)
+ {
+ TREE_SIDE_EFFECTS (decl) = 1;
+ TREE_THIS_VOLATILE (decl) = 1;
+ }
+ /* If a type has volatile components, it should be stored in memory.
+ Otherwise, the fact that those components are volatile
+ will be ignored, and would even crash the compiler. */
+ if (C_TYPE_FIELDS_VOLATILE (TREE_TYPE (decl)))
+ mark_addressable (decl);
+
+ pop_obstacks ();
+
+ return decl;
+ }
+}
+
+/* Decode the parameter-list info for a function type or function definition.
+ The argument is the value returned by `get_parm_info' (or made in parse.y
+ if there is an identifier list instead of a parameter decl list).
+ These two functions are separate because when a function returns
+ or receives functions then each is called multiple times but the order
+ of calls is different. The last call to `grokparms' is always the one
+ that contains the formal parameter names of a function definition.
+
+ Store in `last_function_parms' a chain of the decls of parms.
+ Also store in `last_function_parm_tags' a chain of the struct, union,
+ and enum tags declared among the parms.
+
+ Return a list of arg types to use in the FUNCTION_TYPE for this function.
+
+ FUNCDEF_FLAG is nonzero for a function definition, 0 for
+ a mere declaration. A nonempty identifier-list gets an error message
+ when FUNCDEF_FLAG is zero. */
+
+static tree
+grokparms (parms_info, funcdef_flag)
+ tree parms_info;
+ int funcdef_flag;
+{
+ tree first_parm = TREE_CHAIN (parms_info);
+
+ last_function_parms = TREE_PURPOSE (parms_info);
+ last_function_parm_tags = TREE_VALUE (parms_info);
+
+ if (warn_strict_prototypes && first_parm == 0 && !funcdef_flag
+ && !in_system_header)
+ warning ("function declaration isn't a prototype");
+
+ if (first_parm != 0
+ && TREE_CODE (TREE_VALUE (first_parm)) == IDENTIFIER_NODE)
+ {
+ if (! funcdef_flag)
+ pedwarn ("parameter names (without types) in function declaration");
+
+ last_function_parms = first_parm;
+ return 0;
+ }
+ else
+ {
+ tree parm;
+ tree typelt;
+ /* We no longer test FUNCDEF_FLAG.
+ If the arg types are incomplete in a declaration,
+ they must include undefined tags.
+ These tags can never be defined in the scope of the declaration,
+ so the types can never be completed,
+ and no call can be compiled successfully. */
+#if 0
+ /* In a fcn definition, arg types must be complete. */
+ if (funcdef_flag)
+#endif
+ for (parm = last_function_parms, typelt = first_parm;
+ parm;
+ parm = TREE_CHAIN (parm))
+ /* Skip over any enumeration constants declared here. */
+ if (TREE_CODE (parm) == PARM_DECL)
+ {
+ /* Barf if the parameter itself has an incomplete type. */
+ tree type = TREE_VALUE (typelt);
+ if (TYPE_SIZE (type) == 0)
+ {
+ if (funcdef_flag && DECL_NAME (parm) != 0)
+ error ("parameter `%s' has incomplete type",
+ IDENTIFIER_POINTER (DECL_NAME (parm)));
+ else
+ warning ("parameter has incomplete type");
+ if (funcdef_flag)
+ {
+ TREE_VALUE (typelt) = error_mark_node;
+ TREE_TYPE (parm) = error_mark_node;
+ }
+ }
+#if 0 /* This has been replaced by parm_tags_warning
+ which uses a more accurate criterion for what to warn about. */
+ else
+ {
+ /* Now warn if is a pointer to an incomplete type. */
+ while (TREE_CODE (type) == POINTER_TYPE
+ || TREE_CODE (type) == REFERENCE_TYPE)
+ type = TREE_TYPE (type);
+ type = TYPE_MAIN_VARIANT (type);
+ if (TYPE_SIZE (type) == 0)
+ {
+ if (DECL_NAME (parm) != 0)
+ warning ("parameter `%s' points to incomplete type",
+ IDENTIFIER_POINTER (DECL_NAME (parm)));
+ else
+ warning ("parameter points to incomplete type");
+ }
+ }
+#endif
+ typelt = TREE_CHAIN (typelt);
+ }
+
+ /* Allocate the list of types the way we allocate a type. */
+ if (first_parm && ! TREE_PERMANENT (first_parm))
+ {
+ /* Construct a copy of the list of types
+ on the saveable obstack. */
+ tree result = NULL;
+ for (typelt = first_parm; typelt; typelt = TREE_CHAIN (typelt))
+ result = saveable_tree_cons (NULL_TREE, TREE_VALUE (typelt),
+ result);
+ return nreverse (result);
+ }
+ else
+ /* The list we have is permanent already. */
+ return first_parm;
+ }
+}
+
+
+/* Return a tree_list node with info on a parameter list just parsed.
+ The TREE_PURPOSE is a chain of decls of those parms.
+ The TREE_VALUE is a list of structure, union and enum tags defined.
+ The TREE_CHAIN is a list of argument types to go in the FUNCTION_TYPE.
+ This tree_list node is later fed to `grokparms'.
+
+ VOID_AT_END nonzero means append `void' to the end of the type-list.
+ Zero means the parmlist ended with an ellipsis so don't append `void'. */
+
+tree
+get_parm_info (void_at_end)
+ int void_at_end;
+{
+ register tree decl, t;
+ register tree types = 0;
+ int erred = 0;
+ tree tags = gettags ();
+ tree parms = getdecls ();
+ tree new_parms = 0;
+ tree order = current_binding_level->parm_order;
+
+ /* Just `void' (and no ellipsis) is special. There are really no parms. */
+ if (void_at_end && parms != 0
+ && TREE_CHAIN (parms) == 0
+ && TYPE_MAIN_VARIANT (TREE_TYPE (parms)) == void_type_node
+ && DECL_NAME (parms) == 0)
+ {
+ parms = NULL_TREE;
+ storedecls (NULL_TREE);
+ return saveable_tree_cons (NULL_TREE, NULL_TREE,
+ saveable_tree_cons (NULL_TREE, void_type_node, NULL_TREE));
+ }
+
+ /* Extract enumerator values and other non-parms declared with the parms.
+ Likewise any forward parm decls that didn't have real parm decls. */
+ for (decl = parms; decl; )
+ {
+ tree next = TREE_CHAIN (decl);
+
+ if (TREE_CODE (decl) != PARM_DECL)
+ {
+ TREE_CHAIN (decl) = new_parms;
+ new_parms = decl;
+ }
+ else if (TREE_ASM_WRITTEN (decl))
+ {
+ error_with_decl (decl, "parameter `%s' has just a forward declaration");
+ TREE_CHAIN (decl) = new_parms;
+ new_parms = decl;
+ }
+ decl = next;
+ }
+
+ /* Put the parm decls back in the order they were in in the parm list. */
+ for (t = order; t; t = TREE_CHAIN (t))
+ {
+ if (TREE_CHAIN (t))
+ TREE_CHAIN (TREE_VALUE (t)) = TREE_VALUE (TREE_CHAIN (t));
+ else
+ TREE_CHAIN (TREE_VALUE (t)) = 0;
+ }
+
+ new_parms = chainon (order ? nreverse (TREE_VALUE (order)) : 0,
+ new_parms);
+
+ /* Store the parmlist in the binding level since the old one
+ is no longer a valid list. (We have changed the chain pointers.) */
+ storedecls (new_parms);
+
+ for (decl = new_parms; decl; decl = TREE_CHAIN (decl))
+ /* There may also be declarations for enumerators if an enumeration
+ type is declared among the parms. Ignore them here. */
+ if (TREE_CODE (decl) == PARM_DECL)
+ {
+ /* Since there is a prototype,
+ args are passed in their declared types. */
+ tree type = TREE_TYPE (decl);
+ DECL_ARG_TYPE (decl) = type;
+#ifdef PROMOTE_PROTOTYPES
+ if ((TREE_CODE (type) == INTEGER_TYPE
+ || TREE_CODE (type) == ENUMERAL_TYPE)
+ && TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node))
+ DECL_ARG_TYPE (decl) = integer_type_node;
+#endif
+
+ types = saveable_tree_cons (NULL_TREE, TREE_TYPE (decl), types);
+ if (TYPE_MAIN_VARIANT (TREE_VALUE (types)) == void_type_node && ! erred
+ && DECL_NAME (decl) == 0)
+ {
+ error ("`void' in parameter list must be the entire list");
+ erred = 1;
+ }
+ }
+
+ if (void_at_end)
+ return saveable_tree_cons (new_parms, tags,
+ nreverse (saveable_tree_cons (NULL_TREE, void_type_node, types)));
+
+ return saveable_tree_cons (new_parms, tags, nreverse (types));
+}
+
+/* At end of parameter list, warn about any struct, union or enum tags
+ defined within. Do so because these types cannot ever become complete. */
+
+void
+parmlist_tags_warning ()
+{
+ tree elt;
+ static int already;
+
+ for (elt = current_binding_level->tags; elt; elt = TREE_CHAIN (elt))
+ {
+ enum tree_code code = TREE_CODE (TREE_VALUE (elt));
+ /* An anonymous union parm type is meaningful as a GNU extension.
+ So don't warn for that. */
+ if (code == UNION_TYPE && !pedantic)
+ continue;
+ if (TREE_PURPOSE (elt) != 0)
+ warning ("`%s %s' declared inside parameter list",
+ (code == RECORD_TYPE ? "struct"
+ : code == UNION_TYPE ? "union"
+ : "enum"),
+ IDENTIFIER_POINTER (TREE_PURPOSE (elt)));
+ else
+ warning ("anonymous %s declared inside parameter list",
+ (code == RECORD_TYPE ? "struct"
+ : code == UNION_TYPE ? "union"
+ : "enum"));
+
+ if (! already)
+ {
+ warning ("its scope is only this definition or declaration,");
+ warning ("which is probably not what you want.");
+ already = 1;
+ }
+ }
+}
+
+/* Get the struct, enum or union (CODE says which) with tag NAME.
+ Define the tag as a forward-reference if it is not defined. */
+
+tree
+xref_tag (code, name)
+ enum tree_code code;
+ tree name;
+{
+ int temporary = allocation_temporary_p ();
+
+ /* If a cross reference is requested, look up the type
+ already defined for this tag and return it. */
+
+ register tree ref = lookup_tag (code, name, current_binding_level, 0);
+ /* Even if this is the wrong type of tag, return what we found.
+ There will be an error message anyway, from pending_xref_error.
+ If we create an empty xref just for an invalid use of the type,
+ the main result is to create lots of superfluous error messages. */
+ if (ref)
+ return ref;
+
+ push_obstacks_nochange ();
+
+ if (current_binding_level == global_binding_level && temporary)
+ end_temporary_allocation ();
+
+ /* If no such tag is yet defined, create a forward-reference node
+ and record it as the "definition".
+ When a real declaration of this type is found,
+ the forward-reference will be altered into a real type. */
+
+ ref = make_node (code);
+ if (code == ENUMERAL_TYPE)
+ {
+ /* (In ANSI, Enums can be referred to only if already defined.) */
+ if (pedantic)
+ pedwarn ("ANSI C forbids forward references to `enum' types");
+ /* Give the type a default layout like unsigned int
+ to avoid crashing if it does not get defined. */
+ TYPE_MODE (ref) = TYPE_MODE (unsigned_type_node);
+ TYPE_ALIGN (ref) = TYPE_ALIGN (unsigned_type_node);
+ TREE_UNSIGNED (ref) = 1;
+ TYPE_PRECISION (ref) = TYPE_PRECISION (unsigned_type_node);
+ TYPE_MIN_VALUE (ref) = TYPE_MIN_VALUE (unsigned_type_node);
+ TYPE_MAX_VALUE (ref) = TYPE_MAX_VALUE (unsigned_type_node);
+ }
+
+ pushtag (name, ref);
+
+ pop_obstacks ();
+
+ return ref;
+}
+
+/* Make sure that the tag NAME is defined *in the current binding level*
+ at least as a forward reference.
+ CODE says which kind of tag NAME ought to be.
+
+ We also do a push_obstacks_nochange
+ whose matching pop is in finish_struct. */
+
+tree
+start_struct (code, name)
+ enum tree_code code;
+ tree name;
+{
+ /* If there is already a tag defined at this binding level
+ (as a forward reference), just return it. */
+
+ register tree ref = 0;
+
+ push_obstacks_nochange ();
+ if (current_binding_level == global_binding_level)
+ end_temporary_allocation ();
+
+ if (name != 0)
+ ref = lookup_tag (code, name, current_binding_level, 1);
+ if (ref && TREE_CODE (ref) == code)
+ {
+ C_TYPE_BEING_DEFINED (ref) = 1;
+ if (TYPE_FIELDS (ref))
+ error ((code == UNION_TYPE ? "redefinition of `union %s'"
+ : "redefinition of `struct %s'"),
+ IDENTIFIER_POINTER (name));
+
+ return ref;
+ }
+
+ /* Otherwise create a forward-reference just so the tag is in scope. */
+
+ ref = make_node (code);
+ pushtag (name, ref);
+ C_TYPE_BEING_DEFINED (ref) = 1;
+ return ref;
+}
+
+/* Process the specs, declarator (NULL if omitted) and width (NULL if omitted)
+ of a structure component, returning a FIELD_DECL node.
+ WIDTH is non-NULL for bit fields only, and is an INTEGER_CST node.
+
+ This is done during the parsing of the struct declaration.
+ The FIELD_DECL nodes are chained together and the lot of them
+ are ultimately passed to `build_struct' to make the RECORD_TYPE node. */
+
+tree
+grokfield (filename, line, declarator, declspecs, width)
+ char *filename;
+ int line;
+ tree declarator, declspecs, width;
+{
+ tree value;
+
+ /* The corresponding pop_obstacks is in finish_decl. */
+ push_obstacks_nochange ();
+
+ value = grokdeclarator (declarator, declspecs, width ? BITFIELD : FIELD, 0);
+
+ finish_decl (value, NULL_TREE, NULL_TREE);
+ DECL_INITIAL (value) = width;
+
+ maybe_objc_check_decl (value);
+ return value;
+}
+
+/* Function to help qsort sort FIELD_DECLs by name order. */
+
+static int
+field_decl_cmp (x, y)
+ tree *x, *y;
+{
+ return (long)DECL_NAME (*x) - (long)DECL_NAME (*y);
+}
+
+/* Fill in the fields of a RECORD_TYPE or UNION_TYPE node, T.
+ FIELDLIST is a chain of FIELD_DECL nodes for the fields.
+
+ We also do a pop_obstacks to match the push in start_struct. */
+
+tree
+finish_struct (t, fieldlist)
+ register tree t, fieldlist;
+{
+ register tree x;
+ int old_momentary;
+ int toplevel = global_binding_level == current_binding_level;
+
+ /* If this type was previously laid out as a forward reference,
+ make sure we lay it out again. */
+
+ TYPE_SIZE (t) = 0;
+
+ /* Nameless union parm types are useful as GCC extension. */
+ if (! (TREE_CODE (t) == UNION_TYPE && TYPE_NAME (t) == 0) && !pedantic)
+ /* Otherwise, warn about any struct or union def. in parmlist. */
+ if (in_parm_level_p ())
+ {
+ if (pedantic)
+ pedwarn ((TREE_CODE (t) == UNION_TYPE ? "union defined inside parms"
+ : "structure defined inside parms"));
+ else if (! flag_traditional)
+ warning ((TREE_CODE (t) == UNION_TYPE ? "union defined inside parms"
+ : "structure defined inside parms"));
+ }
+
+ old_momentary = suspend_momentary ();
+
+ if (fieldlist == 0 && pedantic)
+ pedwarn ((TREE_CODE (t) == UNION_TYPE ? "union has no members"
+ : "structure has no members"));
+
+ /* Install struct as DECL_CONTEXT of each field decl.
+ Also process specified field sizes.
+ Set DECL_FIELD_SIZE to the specified size, or 0 if none specified.
+ The specified size is found in the DECL_INITIAL.
+ Store 0 there, except for ": 0" fields (so we can find them
+ and delete them, below). */
+
+ for (x = fieldlist; x; x = TREE_CHAIN (x))
+ {
+ DECL_CONTEXT (x) = t;
+ DECL_FIELD_SIZE (x) = 0;
+
+ /* If any field is const, the structure type is pseudo-const. */
+ if (TREE_READONLY (x))
+ C_TYPE_FIELDS_READONLY (t) = 1;
+ else
+ {
+ /* A field that is pseudo-const makes the structure likewise. */
+ tree t1 = TREE_TYPE (x);
+ while (TREE_CODE (t1) == ARRAY_TYPE)
+ t1 = TREE_TYPE (t1);
+ if ((TREE_CODE (t1) == RECORD_TYPE || TREE_CODE (t1) == UNION_TYPE)
+ && C_TYPE_FIELDS_READONLY (t1))
+ C_TYPE_FIELDS_READONLY (t) = 1;
+ }
+
+ /* Any field that is volatile means variables of this type must be
+ treated in some ways as volatile. */
+ if (TREE_THIS_VOLATILE (x))
+ C_TYPE_FIELDS_VOLATILE (t) = 1;
+
+ /* Any field of nominal variable size implies structure is too. */
+ if (C_DECL_VARIABLE_SIZE (x))
+ C_TYPE_VARIABLE_SIZE (t) = 1;
+
+ /* Detect invalid nested redefinition. */
+ if (TREE_TYPE (x) == t)
+ error ("nested redefinition of `%s'",
+ IDENTIFIER_POINTER (TYPE_NAME (t)));
+
+ /* Detect invalid bit-field size. */
+ if (DECL_INITIAL (x))
+ STRIP_NOPS (DECL_INITIAL (x));
+ if (DECL_INITIAL (x))
+ {
+ if (TREE_CODE (DECL_INITIAL (x)) == INTEGER_CST)
+ constant_expression_warning (DECL_INITIAL (x));
+ else
+ {
+ error_with_decl (x, "bit-field `%s' width not an integer constant");
+ DECL_INITIAL (x) = NULL;
+ }
+ }
+
+ /* Detect invalid bit-field type. */
+ if (DECL_INITIAL (x)
+ && TREE_CODE (TREE_TYPE (x)) != INTEGER_TYPE
+ && TREE_CODE (TREE_TYPE (x)) != ENUMERAL_TYPE)
+ {
+ error_with_decl (x, "bit-field `%s' has invalid type");
+ DECL_INITIAL (x) = NULL;
+ }
+ if (DECL_INITIAL (x) && pedantic
+ && TYPE_MAIN_VARIANT (TREE_TYPE (x)) != integer_type_node
+ && TYPE_MAIN_VARIANT (TREE_TYPE (x)) != unsigned_type_node
+ /* Accept an enum that's equivalent to int or unsigned int. */
+ && !(TREE_CODE (TREE_TYPE (x)) == ENUMERAL_TYPE
+ && (TYPE_PRECISION (TREE_TYPE (x))
+ == TYPE_PRECISION (integer_type_node))))
+ pedwarn_with_decl (x, "bit-field `%s' type invalid in ANSI C");
+
+ /* Detect and ignore out of range field width. */
+ if (DECL_INITIAL (x))
+ {
+ unsigned HOST_WIDE_INT width = TREE_INT_CST_LOW (DECL_INITIAL (x));
+
+ if (tree_int_cst_sgn (DECL_INITIAL (x)) < 0)
+ {
+ DECL_INITIAL (x) = NULL;
+ error_with_decl (x, "negative width in bit-field `%s'");
+ }
+ else if (TREE_INT_CST_HIGH (DECL_INITIAL (x)) != 0
+ || width > TYPE_PRECISION (TREE_TYPE (x)))
+ {
+ DECL_INITIAL (x) = NULL;
+ pedwarn_with_decl (x, "width of `%s' exceeds its type");
+ }
+ else if (width == 0 && DECL_NAME (x) != 0)
+ {
+ error_with_decl (x, "zero width for bit-field `%s'");
+ DECL_INITIAL (x) = NULL;
+ }
+ }
+
+ /* Process valid field width. */
+ if (DECL_INITIAL (x))
+ {
+ register int width = TREE_INT_CST_LOW (DECL_INITIAL (x));
+
+ DECL_FIELD_SIZE (x) = width;
+ DECL_BIT_FIELD (x) = 1;
+ DECL_INITIAL (x) = NULL;
+
+ if (width == 0)
+ {
+ /* field size 0 => force desired amount of alignment. */
+#ifdef EMPTY_FIELD_BOUNDARY
+ DECL_ALIGN (x) = MAX (DECL_ALIGN (x), EMPTY_FIELD_BOUNDARY);
+#endif
+#ifdef PCC_BITFIELD_TYPE_MATTERS
+ DECL_ALIGN (x) = MAX (DECL_ALIGN (x),
+ TYPE_ALIGN (TREE_TYPE (x)));
+#endif
+ }
+ }
+ else
+ {
+ int min_align = (DECL_PACKED (x) ? BITS_PER_UNIT
+ : TYPE_ALIGN (TREE_TYPE (x)));
+ /* Non-bit-fields are aligned for their type, except packed
+ fields which require only BITS_PER_UNIT alignment. */
+ DECL_ALIGN (x) = MAX (DECL_ALIGN (x), min_align);
+ }
+ }
+
+ /* Now DECL_INITIAL is null on all members. */
+
+ /* Delete all duplicate fields from the fieldlist */
+ for (x = fieldlist; x && TREE_CHAIN (x);)
+ /* Anonymous fields aren't duplicates. */
+ if (DECL_NAME (TREE_CHAIN (x)) == 0)
+ x = TREE_CHAIN (x);
+ else
+ {
+ register tree y = fieldlist;
+
+ while (1)
+ {
+ if (DECL_NAME (y) == DECL_NAME (TREE_CHAIN (x)))
+ break;
+ if (y == x)
+ break;
+ y = TREE_CHAIN (y);
+ }
+ if (DECL_NAME (y) == DECL_NAME (TREE_CHAIN (x)))
+ {
+ error_with_decl (TREE_CHAIN (x), "duplicate member `%s'");
+ TREE_CHAIN (x) = TREE_CHAIN (TREE_CHAIN (x));
+ }
+ else x = TREE_CHAIN (x);
+ }
+
+ /* Now we have the nearly final fieldlist. Record it,
+ then lay out the structure or union (including the fields). */
+
+ TYPE_FIELDS (t) = fieldlist;
+
+ layout_type (t);
+
+ /* Delete all zero-width bit-fields from the front of the fieldlist */
+ while (fieldlist
+ && DECL_INITIAL (fieldlist))
+ fieldlist = TREE_CHAIN (fieldlist);
+ /* Delete all such members from the rest of the fieldlist */
+ for (x = fieldlist; x;)
+ {
+ if (TREE_CHAIN (x) && DECL_INITIAL (TREE_CHAIN (x)))
+ TREE_CHAIN (x) = TREE_CHAIN (TREE_CHAIN (x));
+ else x = TREE_CHAIN (x);
+ }
+
+ /* Now we have the truly final field list.
+ Store it in this type and in the variants. */
+
+ TYPE_FIELDS (t) = fieldlist;
+
+ /* If there are lots of fields, sort so we can look through them fast.
+ We arbitrarily consider 16 or more elts to be "a lot". */
+ {
+ int len = 0;
+
+ for (x = fieldlist; x; x = TREE_CHAIN (x))
+ {
+ if (len > 15)
+ break;
+ len += 1;
+ }
+ if (len > 15)
+ {
+ tree *field_array;
+ char *space;
+
+ len += list_length (x);
+ /* Use the same allocation policy here that make_node uses, to
+ ensure that this lives as long as the rest of the struct decl.
+ All decls in an inline function need to be saved. */
+ if (allocation_temporary_p ())
+ space = savealloc (sizeof (struct lang_type) + len * sizeof (tree));
+ else
+ space = oballoc (sizeof (struct lang_type) + len * sizeof (tree));
+
+ TYPE_LANG_SPECIFIC (t) = (struct lang_type *) space;
+ TYPE_LANG_SPECIFIC (t)->len = len;
+
+ field_array = &TYPE_LANG_SPECIFIC (t)->elts[0];
+ len = 0;
+ for (x = fieldlist; x; x = TREE_CHAIN (x))
+ field_array[len++] = x;
+
+ qsort (field_array, len, sizeof (tree), field_decl_cmp);
+ }
+ }
+
+ for (x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x))
+ {
+ TYPE_FIELDS (x) = TYPE_FIELDS (t);
+ TYPE_LANG_SPECIFIC (x) = TYPE_LANG_SPECIFIC (t);
+ TYPE_ALIGN (x) = TYPE_ALIGN (t);
+ }
+
+ /* Promote each bit-field's type to int if it is narrower than that. */
+ for (x = fieldlist; x; x = TREE_CHAIN (x))
+ if (DECL_BIT_FIELD (x)
+ && (C_PROMOTING_INTEGER_TYPE_P (TREE_TYPE (x))
+ || DECL_FIELD_SIZE (x) < TYPE_PRECISION (integer_type_node)))
+ {
+ tree type = TREE_TYPE (x);
+
+ /* Preserve unsignedness if traditional
+ or if not really getting any wider. */
+ if (TREE_UNSIGNED (type)
+ && (flag_traditional
+ ||
+ (TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node)
+ &&
+ DECL_FIELD_SIZE (x) == TYPE_PRECISION (integer_type_node))))
+ TREE_TYPE (x) = unsigned_type_node;
+ else
+ TREE_TYPE (x) = integer_type_node;
+ }
+
+ /* If this structure or union completes the type of any previous
+ variable declaration, lay it out and output its rtl. */
+
+ if (current_binding_level->n_incomplete != 0)
+ {
+ tree decl;
+ for (decl = current_binding_level->names; decl; decl = TREE_CHAIN (decl))
+ {
+ if (TREE_TYPE (decl) == t
+ && TREE_CODE (decl) != TYPE_DECL)
+ {
+ layout_decl (decl, 0);
+ /* This is a no-op in c-lang.c or something real in objc-actions.c. */
+ maybe_objc_check_decl (decl);
+ rest_of_decl_compilation (decl, NULL_PTR, toplevel, 0);
+ if (! toplevel)
+ expand_decl (decl);
+ --current_binding_level->n_incomplete;
+ }
+ else if (TYPE_SIZE (TREE_TYPE (decl)) == 0
+ && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
+ {
+ tree element = TREE_TYPE (decl);
+ while (TREE_CODE (element) == ARRAY_TYPE)
+ element = TREE_TYPE (element);
+ if (element == t)
+ layout_array_type (TREE_TYPE (decl));
+ }
+ }
+ }
+
+ resume_momentary (old_momentary);
+
+ /* Finish debugging output for this type. */
+ rest_of_type_compilation (t, toplevel);
+
+ /* The matching push is in start_struct. */
+ pop_obstacks ();
+
+ return t;
+}
+
+/* Lay out the type T, and its element type, and so on. */
+
+static void
+layout_array_type (t)
+ tree t;
+{
+ if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
+ layout_array_type (TREE_TYPE (t));
+ layout_type (t);
+}
+
+/* Begin compiling the definition of an enumeration type.
+ NAME is its name (or null if anonymous).
+ Returns the type object, as yet incomplete.
+ Also records info about it so that build_enumerator
+ may be used to declare the individual values as they are read. */
+
+tree
+start_enum (name)
+ tree name;
+{
+ register tree enumtype = 0;
+
+ /* If this is the real definition for a previous forward reference,
+ fill in the contents in the same object that used to be the
+ forward reference. */
+
+ if (name != 0)
+ enumtype = lookup_tag (ENUMERAL_TYPE, name, current_binding_level, 1);
+
+ /* The corresponding pop_obstacks is in finish_enum. */
+ push_obstacks_nochange ();
+ /* If these symbols and types are global, make them permanent. */
+ if (current_binding_level == global_binding_level)
+ end_temporary_allocation ();
+
+ if (enumtype == 0 || TREE_CODE (enumtype) != ENUMERAL_TYPE)
+ {
+ enumtype = make_node (ENUMERAL_TYPE);
+ pushtag (name, enumtype);
+ }
+
+ C_TYPE_BEING_DEFINED (enumtype) = 1;
+
+ if (TYPE_VALUES (enumtype) != 0)
+ {
+ /* This enum is a named one that has been declared already. */
+ error ("redeclaration of `enum %s'", IDENTIFIER_POINTER (name));
+
+ /* Completely replace its old definition.
+ The old enumerators remain defined, however. */
+ TYPE_VALUES (enumtype) = 0;
+ }
+
+ enum_next_value = integer_zero_node;
+ enum_overflow = 0;
+
+ return enumtype;
+}
+
+/* Return the minimum number of bits needed to represent VALUE in a
+ signed or unsigned type, UNSIGNEDP says which. */
+
+static int
+min_precision (value, unsignedp)
+ tree value;
+ int unsignedp;
+{
+ int log;
+
+ /* If the value is negative, compute its negative minus 1. The latter
+ adjustment is because the absolute value of the largest negative value
+ is one larger than the largest positive value. This is equivalent to
+ a bit-wise negation, so use that operation instead. */
+
+ if (tree_int_cst_sgn (value) < 0)
+ value = fold (build1 (BIT_NOT_EXPR, TREE_TYPE (value), value));
+
+ /* Return the number of bits needed, taking into account the fact
+ that we need one more bit for a signed than unsigned type. */
+
+ if (integer_zerop (value))
+ log = 0;
+ else if (TREE_INT_CST_HIGH (value) != 0)
+ log = HOST_BITS_PER_WIDE_INT + floor_log2 (TREE_INT_CST_HIGH (value));
+ else
+ log = floor_log2 (TREE_INT_CST_LOW (value));
+
+ return log + 1 + ! unsignedp;
+}
+
+/* After processing and defining all the values of an enumeration type,
+ install their decls in the enumeration type and finish it off.
+ ENUMTYPE is the type object and VALUES a list of decl-value pairs.
+ Returns ENUMTYPE. */
+
+tree
+finish_enum (enumtype, values)
+ register tree enumtype, values;
+{
+ register tree pair, tem;
+ tree minnode = 0, maxnode = 0;
+ int lowprec, highprec, precision;
+ int toplevel = global_binding_level == current_binding_level;
+
+ if (in_parm_level_p ())
+ warning ("enum defined inside parms");
+
+ /* Calculate the maximum value of any enumerator in this type. */
+
+ if (values == error_mark_node)
+ minnode = maxnode = integer_zero_node;
+ else
+ for (pair = values; pair; pair = TREE_CHAIN (pair))
+ {
+ tree value = TREE_VALUE (pair);
+ if (pair == values)
+ minnode = maxnode = TREE_VALUE (pair);
+ else
+ {
+ if (tree_int_cst_lt (maxnode, value))
+ maxnode = value;
+ if (tree_int_cst_lt (value, minnode))
+ minnode = value;
+ }
+ }
+
+ TYPE_MIN_VALUE (enumtype) = minnode;
+ TYPE_MAX_VALUE (enumtype) = maxnode;
+
+ /* An enum can have some negative values; then it is signed. */
+ TREE_UNSIGNED (enumtype) = tree_int_cst_sgn (minnode) >= 0;
+
+ /* Determine the precision this type needs. */
+
+ lowprec = min_precision (minnode, TREE_UNSIGNED (enumtype));
+ highprec = min_precision (maxnode, TREE_UNSIGNED (enumtype));
+ precision = MAX (lowprec, highprec);
+
+ if (flag_short_enums || precision > TYPE_PRECISION (integer_type_node))
+ /* Use the width of the narrowest normal C type which is wide enough. */
+ TYPE_PRECISION (enumtype) = TYPE_PRECISION (type_for_size (precision, 1));
+ else
+ TYPE_PRECISION (enumtype) = TYPE_PRECISION (integer_type_node);
+
+ TYPE_SIZE (enumtype) = 0;
+ layout_type (enumtype);
+
+ if (values != error_mark_node)
+ {
+ /* Change the type of the enumerators to be the enum type.
+ Formerly this was done only for enums that fit in an int,
+ but the comment said it was done only for enums wider than int.
+ It seems necessary to do this for wide enums,
+ and best not to change what's done for ordinary narrower ones. */
+ for (pair = values; pair; pair = TREE_CHAIN (pair))
+ {
+ TREE_TYPE (TREE_PURPOSE (pair)) = enumtype;
+ DECL_SIZE (TREE_PURPOSE (pair)) = TYPE_SIZE (enumtype);
+ if (TREE_CODE (TREE_PURPOSE (pair)) != FUNCTION_DECL)
+ DECL_ALIGN (TREE_PURPOSE (pair)) = TYPE_ALIGN (enumtype);
+ }
+
+ /* Replace the decl nodes in VALUES with their names. */
+ for (pair = values; pair; pair = TREE_CHAIN (pair))
+ TREE_PURPOSE (pair) = DECL_NAME (TREE_PURPOSE (pair));
+
+ TYPE_VALUES (enumtype) = values;
+ }
+
+ /* Fix up all variant types of this enum type. */
+ for (tem = TYPE_MAIN_VARIANT (enumtype); tem; tem = TYPE_NEXT_VARIANT (tem))
+ {
+ TYPE_VALUES (tem) = TYPE_VALUES (enumtype);
+ TYPE_MIN_VALUE (tem) = TYPE_MIN_VALUE (enumtype);
+ TYPE_MAX_VALUE (tem) = TYPE_MAX_VALUE (enumtype);
+ TYPE_SIZE (tem) = TYPE_SIZE (enumtype);
+ TYPE_MODE (tem) = TYPE_MODE (enumtype);
+ TYPE_PRECISION (tem) = TYPE_PRECISION (enumtype);
+ TYPE_ALIGN (tem) = TYPE_ALIGN (enumtype);
+ TREE_UNSIGNED (tem) = TREE_UNSIGNED (enumtype);
+ }
+
+ /* Finish debugging output for this type. */
+ rest_of_type_compilation (enumtype, toplevel);
+
+ /* This matches a push in start_enum. */
+ pop_obstacks ();
+
+ return enumtype;
+}
+
+/* Build and install a CONST_DECL for one value of the
+ current enumeration type (one that was begun with start_enum).
+ Return a tree-list containing the CONST_DECL and its value.
+ Assignment of sequential values by default is handled here. */
+
+tree
+build_enumerator (name, value)
+ tree name, value;
+{
+ register tree decl, type;
+
+ /* Validate and default VALUE. */
+
+ /* Remove no-op casts from the value. */
+ if (value)
+ STRIP_TYPE_NOPS (value);
+
+ if (value != 0)
+ {
+ if (TREE_CODE (value) == INTEGER_CST)
+ {
+ value = default_conversion (value);
+ constant_expression_warning (value);
+ }
+ else
+ {
+ error ("enumerator value for `%s' not integer constant",
+ IDENTIFIER_POINTER (name));
+ value = 0;
+ }
+ }
+
+ /* Default based on previous value. */
+ /* It should no longer be possible to have NON_LVALUE_EXPR
+ in the default. */
+ if (value == 0)
+ {
+ value = enum_next_value;
+ if (enum_overflow)
+ error ("overflow in enumeration values");
+ }
+
+ if (pedantic && ! int_fits_type_p (value, integer_type_node))
+ {
+ pedwarn ("ANSI C restricts enumerator values to range of `int'");
+ value = integer_zero_node;
+ }
+
+ /* Set basis for default for next value. */
+ enum_next_value = build_binary_op (PLUS_EXPR, value, integer_one_node, 0);
+ enum_overflow = tree_int_cst_lt (enum_next_value, value);
+
+ /* Now create a declaration for the enum value name. */
+
+ type = TREE_TYPE (value);
+ type = type_for_size (MAX (TYPE_PRECISION (type),
+ TYPE_PRECISION (integer_type_node)),
+ ((flag_traditional
+ || TYPE_PRECISION (type) >= TYPE_PRECISION (integer_type_node))
+ && TREE_UNSIGNED (type)));
+
+ decl = build_decl (CONST_DECL, name, type);
+ DECL_INITIAL (decl) = value;
+ TREE_TYPE (value) = type;
+ pushdecl (decl);
+
+ return saveable_tree_cons (decl, value, NULL_TREE);
+}
+
+/* Create the FUNCTION_DECL for a function definition.
+ DECLSPECS and DECLARATOR are the parts of the declaration;
+ they describe the function's name and the type it returns,
+ but twisted together in a fashion that parallels the syntax of C.
+
+ This function creates a binding context for the function body
+ as well as setting up the FUNCTION_DECL in current_function_decl.
+
+ Returns 1 on success. If the DECLARATOR is not suitable for a function
+ (it defines a datum instead), we return 0, which tells
+ yyparse to report a parse error.
+
+ NESTED is nonzero for a function nested within another function. */
+
+int
+start_function (declspecs, declarator, nested)
+ tree declarator, declspecs;
+ int nested;
+{
+ tree decl1, old_decl;
+ tree restype;
+
+ current_function_returns_value = 0; /* Assume, until we see it does. */
+ current_function_returns_null = 0;
+ warn_about_return_type = 0;
+ current_extern_inline = 0;
+ c_function_varargs = 0;
+ named_labels = 0;
+ shadowed_labels = 0;
+
+ decl1 = grokdeclarator (declarator, declspecs, FUNCDEF, 1);
+
+ /* If the declarator is not suitable for a function definition,
+ cause a syntax error. */
+ if (decl1 == 0)
+ return 0;
+
+ announce_function (decl1);
+
+ if (TYPE_SIZE (TREE_TYPE (TREE_TYPE (decl1))) == 0)
+ {
+ error ("return-type is an incomplete type");
+ /* Make it return void instead. */
+ TREE_TYPE (decl1)
+ = build_function_type (void_type_node,
+ TYPE_ARG_TYPES (TREE_TYPE (decl1)));
+ }
+
+ if (warn_about_return_type)
+ warning ("return-type defaults to `int'");
+
+ /* Save the parm names or decls from this function's declarator
+ where store_parm_decls will find them. */
+ current_function_parms = last_function_parms;
+ current_function_parm_tags = last_function_parm_tags;
+
+ /* Make the init_value nonzero so pushdecl knows this is not tentative.
+ error_mark_node is replaced below (in poplevel) with the BLOCK. */
+ DECL_INITIAL (decl1) = error_mark_node;
+
+ /* If this definition isn't a prototype and we had a prototype declaration
+ before, copy the arg type info from that prototype.
+ But not if what we had before was a builtin function. */
+ old_decl = lookup_name_current_level (DECL_NAME (decl1));
+ if (old_decl != 0 && TREE_CODE (TREE_TYPE (old_decl)) == FUNCTION_TYPE
+ && !DECL_BUILT_IN (old_decl)
+ && (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (decl1)))
+ == TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (old_decl))))
+ && TYPE_ARG_TYPES (TREE_TYPE (decl1)) == 0)
+ {
+ TREE_TYPE (decl1) = TREE_TYPE (old_decl);
+ current_function_prototype_file = DECL_SOURCE_FILE (old_decl);
+ current_function_prototype_line = DECL_SOURCE_LINE (old_decl);
+ }
+
+ /* Optionally warn of old-fashioned def with no previous prototype. */
+ if (warn_strict_prototypes
+ && TYPE_ARG_TYPES (TREE_TYPE (decl1)) == 0
+ && !(old_decl != 0 && TYPE_ARG_TYPES (TREE_TYPE (old_decl)) != 0))
+ warning ("function declaration isn't a prototype");
+ /* Optionally warn of any global def with no previous prototype. */
+ else if (warn_missing_prototypes
+ && TREE_PUBLIC (decl1)
+ && !(old_decl != 0 && TYPE_ARG_TYPES (TREE_TYPE (old_decl)) != 0)
+ && strcmp ("main", IDENTIFIER_POINTER (DECL_NAME (decl1))))
+ warning_with_decl (decl1, "no previous prototype for `%s'");
+ /* Optionally warn of any def with no previous prototype
+ if the function has already been used. */
+ else if (warn_missing_prototypes
+ && old_decl != 0 && TREE_USED (old_decl)
+ && !(old_decl != 0 && TYPE_ARG_TYPES (TREE_TYPE (old_decl)) != 0))
+ warning_with_decl (decl1,
+ "`%s' was used with no prototype before its definition");
+ /* Optionally warn of any global def with no previous declaration. */
+ else if (warn_missing_declarations
+ && TREE_PUBLIC (decl1)
+ && old_decl == 0
+ && strcmp ("main", IDENTIFIER_POINTER (DECL_NAME (decl1))))
+ warning_with_decl (decl1, "no previous declaration for `%s'");
+ /* Optionally warn of any def with no previous declaration
+ if the function has already been used. */
+ else if (warn_missing_declarations
+ && old_decl != 0 && TREE_USED (old_decl))
+ warning_with_decl (decl1,
+ "`%s' was used with no declaration before its definition");
+
+ /* This is a definition, not a reference.
+ So normally clear DECL_EXTERNAL.
+ However, `extern inline' acts like a declaration
+ except for defining how to inline. So set DECL_EXTERNAL in that case. */
+ DECL_EXTERNAL (decl1) = current_extern_inline;
+
+ /* This function exists in static storage.
+ (This does not mean `static' in the C sense!) */
+ TREE_STATIC (decl1) = 1;
+
+ /* A nested function is not global. */
+ if (current_function_decl != 0)
+ TREE_PUBLIC (decl1) = 0;
+
+ /* Record the decl so that the function name is defined.
+ If we already have a decl for this name, and it is a FUNCTION_DECL,
+ use the old decl. */
+
+ current_function_decl = pushdecl (decl1);
+
+ pushlevel (0);
+ declare_parm_level (1);
+ current_binding_level->subblocks_tag_transparent = 1;
+
+ make_function_rtl (current_function_decl);
+
+ restype = TREE_TYPE (TREE_TYPE (current_function_decl));
+ /* Promote the value to int before returning it. */
+ if (C_PROMOTING_INTEGER_TYPE_P (restype))
+ {
+ /* It retains unsignedness if traditional
+ or if not really getting wider. */
+ if (TREE_UNSIGNED (restype)
+ && (flag_traditional
+ || (TYPE_PRECISION (restype)
+ == TYPE_PRECISION (integer_type_node))))
+ restype = unsigned_type_node;
+ else
+ restype = integer_type_node;
+ }
+ DECL_RESULT (current_function_decl)
+ = build_decl (RESULT_DECL, NULL_TREE, restype);
+
+ if (!nested)
+ /* Allocate further tree nodes temporarily during compilation
+ of this function only. */
+ temporary_allocation ();
+
+ /* If this fcn was already referenced via a block-scope `extern' decl
+ (or an implicit decl), propagate certain information about the usage. */
+ if (TREE_ADDRESSABLE (DECL_ASSEMBLER_NAME (current_function_decl)))
+ TREE_ADDRESSABLE (current_function_decl) = 1;
+
+ return 1;
+}
+
+/* Record that this function is going to be a varargs function.
+ This is called before store_parm_decls, which is too early
+ to call mark_varargs directly. */
+
+void
+c_mark_varargs ()
+{
+ c_function_varargs = 1;
+}
+
+/* Store the parameter declarations into the current function declaration.
+ This is called after parsing the parameter declarations, before
+ digesting the body of the function.
+
+ For an old-style definition, modify the function's type
+ to specify at least the number of arguments. */
+
+void
+store_parm_decls ()
+{
+ register tree fndecl = current_function_decl;
+ register tree parm;
+
+ /* This is either a chain of PARM_DECLs (if a prototype was used)
+ or a list of IDENTIFIER_NODEs (for an old-fashioned C definition). */
+ tree specparms = current_function_parms;
+
+ /* This is a list of types declared among parms in a prototype. */
+ tree parmtags = current_function_parm_tags;
+
+ /* This is a chain of PARM_DECLs from old-style parm declarations. */
+ register tree parmdecls = getdecls ();
+
+ /* This is a chain of any other decls that came in among the parm
+ declarations. If a parm is declared with enum {foo, bar} x;
+ then CONST_DECLs for foo and bar are put here. */
+ tree nonparms = 0;
+
+ /* Nonzero if this definition is written with a prototype. */
+ int prototype = 0;
+
+ if (specparms != 0 && TREE_CODE (specparms) != TREE_LIST)
+ {
+ /* This case is when the function was defined with an ANSI prototype.
+ The parms already have decls, so we need not do anything here
+ except record them as in effect
+ and complain if any redundant old-style parm decls were written. */
+
+ register tree next;
+ tree others = 0;
+
+ prototype = 1;
+
+ if (parmdecls != 0)
+ {
+ tree decl, link;
+
+ error_with_decl (fndecl,
+ "parm types given both in parmlist and separately");
+ /* Get rid of the erroneous decls; don't keep them on
+ the list of parms, since they might not be PARM_DECLs. */
+ for (decl = current_binding_level->names;
+ decl; decl = TREE_CHAIN (decl))
+ if (DECL_NAME (decl))
+ IDENTIFIER_LOCAL_VALUE (DECL_NAME (decl)) = 0;
+ for (link = current_binding_level->shadowed;
+ link; link = TREE_CHAIN (link))
+ IDENTIFIER_LOCAL_VALUE (TREE_PURPOSE (link)) = TREE_VALUE (link);
+ current_binding_level->names = 0;
+ current_binding_level->shadowed = 0;
+ }
+
+ specparms = nreverse (specparms);
+ for (parm = specparms; parm; parm = next)
+ {
+ next = TREE_CHAIN (parm);
+ if (TREE_CODE (parm) == PARM_DECL)
+ {
+ if (DECL_NAME (parm) == 0)
+ error_with_decl (parm, "parameter name omitted");
+ else if (TYPE_MAIN_VARIANT (TREE_TYPE (parm)) == void_type_node)
+ {
+ error_with_decl (parm, "parameter `%s' declared void");
+ /* Change the type to error_mark_node so this parameter
+ will be ignored by assign_parms. */
+ TREE_TYPE (parm) = error_mark_node;
+ }
+ pushdecl (parm);
+ }
+ else
+ {
+ /* If we find an enum constant or a type tag,
+ put it aside for the moment. */
+ TREE_CHAIN (parm) = 0;
+ others = chainon (others, parm);
+ }
+ }
+
+ /* Get the decls in their original chain order
+ and record in the function. */
+ DECL_ARGUMENTS (fndecl) = getdecls ();
+
+#if 0
+ /* If this function takes a variable number of arguments,
+ add a phony parameter to the end of the parm list,
+ to represent the position of the first unnamed argument. */
+ if (TREE_VALUE (tree_last (TYPE_ARG_TYPES (TREE_TYPE (fndecl))))
+ != void_type_node)
+ {
+ tree dummy = build_decl (PARM_DECL, NULL_TREE, void_type_node);
+ /* Let's hope the address of the unnamed parm
+ won't depend on its type. */
+ TREE_TYPE (dummy) = integer_type_node;
+ DECL_ARG_TYPE (dummy) = integer_type_node;
+ DECL_ARGUMENTS (fndecl)
+ = chainon (DECL_ARGUMENTS (fndecl), dummy);
+ }
+#endif
+
+ /* Now pushdecl the enum constants. */
+ for (parm = others; parm; parm = next)
+ {
+ next = TREE_CHAIN (parm);
+ if (DECL_NAME (parm) == 0)
+ ;
+ else if (TYPE_MAIN_VARIANT (TREE_TYPE (parm)) == void_type_node)
+ ;
+ else if (TREE_CODE (parm) != PARM_DECL)
+ pushdecl (parm);
+ }
+
+ storetags (chainon (parmtags, gettags ()));
+ }
+ else
+ {
+ /* SPECPARMS is an identifier list--a chain of TREE_LIST nodes
+ each with a parm name as the TREE_VALUE.
+
+ PARMDECLS is a chain of declarations for parameters.
+ Warning! It can also contain CONST_DECLs which are not parameters
+ but are names of enumerators of any enum types
+ declared among the parameters.
+
+ First match each formal parameter name with its declaration.
+ Associate decls with the names and store the decls
+ into the TREE_PURPOSE slots. */
+
+ for (parm = parmdecls; parm; parm = TREE_CHAIN (parm))
+ DECL_RESULT (parm) = 0;
+
+ for (parm = specparms; parm; parm = TREE_CHAIN (parm))
+ {
+ register tree tail, found = NULL;
+
+ if (TREE_VALUE (parm) == 0)
+ {
+ error_with_decl (fndecl, "parameter name missing from parameter list");
+ TREE_PURPOSE (parm) = 0;
+ continue;
+ }
+
+ /* See if any of the parmdecls specifies this parm by name.
+ Ignore any enumerator decls. */
+ for (tail = parmdecls; tail; tail = TREE_CHAIN (tail))
+ if (DECL_NAME (tail) == TREE_VALUE (parm)
+ && TREE_CODE (tail) == PARM_DECL)
+ {
+ found = tail;
+ break;
+ }
+
+ /* If declaration already marked, we have a duplicate name.
+ Complain, and don't use this decl twice. */
+ if (found && DECL_RESULT (found) != 0)
+ {
+ error_with_decl (found, "multiple parameters named `%s'");
+ found = 0;
+ }
+
+ /* If the declaration says "void", complain and ignore it. */
+ if (found && TYPE_MAIN_VARIANT (TREE_TYPE (found)) == void_type_node)
+ {
+ error_with_decl (found, "parameter `%s' declared void");
+ TREE_TYPE (found) = integer_type_node;
+ DECL_ARG_TYPE (found) = integer_type_node;
+ layout_decl (found, 0);
+ }
+
+ /* Traditionally, a parm declared float is actually a double. */
+ if (found && flag_traditional
+ && TYPE_MAIN_VARIANT (TREE_TYPE (found)) == float_type_node)
+ {
+ TREE_TYPE (found) = double_type_node;
+ DECL_ARG_TYPE (found) = double_type_node;
+ layout_decl (found, 0);
+ }
+
+ /* If no declaration found, default to int. */
+ if (!found)
+ {
+ found = build_decl (PARM_DECL, TREE_VALUE (parm),
+ integer_type_node);
+ DECL_ARG_TYPE (found) = TREE_TYPE (found);
+ DECL_SOURCE_LINE (found) = DECL_SOURCE_LINE (fndecl);
+ DECL_SOURCE_FILE (found) = DECL_SOURCE_FILE (fndecl);
+ if (extra_warnings)
+ warning_with_decl (found, "type of `%s' defaults to `int'");
+ pushdecl (found);
+ }
+
+ TREE_PURPOSE (parm) = found;
+
+ /* Mark this decl as "already found" -- see test, above.
+ It is safe to use DECL_RESULT for this
+ since it is not used in PARM_DECLs or CONST_DECLs. */
+ DECL_RESULT (found) = error_mark_node;
+ }
+
+ /* Put anything which is on the parmdecls chain and which is
+ not a PARM_DECL onto the list NONPARMS. (The types of
+ non-parm things which might appear on the list include
+ enumerators and NULL-named TYPE_DECL nodes.) Complain about
+ any actual PARM_DECLs not matched with any names. */
+
+ nonparms = 0;
+ for (parm = parmdecls; parm; )
+ {
+ tree next = TREE_CHAIN (parm);
+ TREE_CHAIN (parm) = 0;
+
+ if (TREE_CODE (parm) != PARM_DECL)
+ nonparms = chainon (nonparms, parm);
+ else
+ {
+ /* Complain about args with incomplete types. */
+ if (TYPE_SIZE (TREE_TYPE (parm)) == 0)
+ {
+ error_with_decl (parm, "parameter `%s' has incomplete type");
+ TREE_TYPE (parm) = error_mark_node;
+ }
+
+ if (DECL_RESULT (parm) == 0)
+ {
+ error_with_decl (parm,
+ "declaration for parameter `%s' but no such parameter");
+ /* Pretend the parameter was not missing.
+ This gets us to a standard state and minimizes
+ further error messages. */
+ specparms
+ = chainon (specparms,
+ tree_cons (parm, NULL_TREE, NULL_TREE));
+ }
+ }
+
+ parm = next;
+ }
+
+ /* Chain the declarations together in the order of the list of names. */
+ /* Store that chain in the function decl, replacing the list of names. */
+ parm = specparms;
+ DECL_ARGUMENTS (fndecl) = 0;
+ {
+ register tree last;
+ for (last = 0; parm; parm = TREE_CHAIN (parm))
+ if (TREE_PURPOSE (parm))
+ {
+ if (last == 0)
+ DECL_ARGUMENTS (fndecl) = TREE_PURPOSE (parm);
+ else
+ TREE_CHAIN (last) = TREE_PURPOSE (parm);
+ last = TREE_PURPOSE (parm);
+ TREE_CHAIN (last) = 0;
+ }
+ }
+
+ /* If there was a previous prototype,
+ set the DECL_ARG_TYPE of each argument according to
+ the type previously specified, and report any mismatches. */
+
+ if (TYPE_ARG_TYPES (TREE_TYPE (fndecl)))
+ {
+ register tree type;
+ for (parm = DECL_ARGUMENTS (fndecl),
+ type = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+ parm || (type && (TYPE_MAIN_VARIANT (TREE_VALUE (type))
+ != void_type_node));
+ parm = TREE_CHAIN (parm), type = TREE_CHAIN (type))
+ {
+ if (parm == 0 || type == 0
+ || TYPE_MAIN_VARIANT (TREE_VALUE (type)) == void_type_node)
+ {
+ error ("number of arguments doesn't match prototype");
+ error_with_file_and_line (current_function_prototype_file,
+ current_function_prototype_line,
+ "prototype declaration");
+ break;
+ }
+ /* Type for passing arg must be consistent
+ with that declared for the arg. */
+ if (! comptypes (DECL_ARG_TYPE (parm), TREE_VALUE (type)))
+ {
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (parm))
+ == TYPE_MAIN_VARIANT (TREE_VALUE (type)))
+ {
+ /* Adjust argument to match prototype. E.g. a previous
+ `int foo(float);' prototype causes
+ `int foo(x) float x; {...}' to be treated like
+ `int foo(float x) {...}'. This is particularly
+ useful for argument types like uid_t. */
+ DECL_ARG_TYPE (parm) = TREE_TYPE (parm);
+#ifdef PROMOTE_PROTOTYPES
+ if ((TREE_CODE (TREE_TYPE (parm)) == INTEGER_TYPE
+ || TREE_CODE (TREE_TYPE (parm)) == ENUMERAL_TYPE)
+ && TYPE_PRECISION (TREE_TYPE (parm))
+ < TYPE_PRECISION (integer_type_node))
+ DECL_ARG_TYPE (parm) = integer_type_node;
+#endif
+ if (pedantic)
+ {
+ pedwarn ("promoted argument `%s' doesn't match prototype",
+ IDENTIFIER_POINTER (DECL_NAME (parm)));
+ warning_with_file_and_line
+ (current_function_prototype_file,
+ current_function_prototype_line,
+ "prototype declaration");
+ }
+ }
+ /* If -traditional, allow `int' argument to match
+ `unsigned' prototype. */
+ else if (! (flag_traditional
+ && TYPE_MAIN_VARIANT (TREE_TYPE (parm)) == integer_type_node
+ && TYPE_MAIN_VARIANT (TREE_VALUE (type)) == unsigned_type_node))
+ {
+ error ("argument `%s' doesn't match prototype",
+ IDENTIFIER_POINTER (DECL_NAME (parm)));
+ error_with_file_and_line (current_function_prototype_file,
+ current_function_prototype_line,
+ "prototype declaration");
+ }
+ }
+ }
+ TYPE_ACTUAL_ARG_TYPES (TREE_TYPE (fndecl)) = 0;
+ }
+
+ /* Otherwise, create a prototype that would match. */
+
+ else
+ {
+ tree actual = 0, last = 0, type;
+
+ for (parm = DECL_ARGUMENTS (fndecl); parm; parm = TREE_CHAIN (parm))
+ {
+ type = perm_tree_cons (NULL_TREE, DECL_ARG_TYPE (parm),
+ NULL_TREE);
+ if (last)
+ TREE_CHAIN (last) = type;
+ else
+ actual = type;
+ last = type;
+ }
+ type = perm_tree_cons (NULL_TREE, void_type_node, NULL_TREE);
+ if (last)
+ TREE_CHAIN (last) = type;
+ else
+ actual = type;
+
+ /* We are going to assign a new value for the TYPE_ACTUAL_ARG_TYPES
+ of the type of this function, but we need to avoid having this
+ affect the types of other similarly-typed functions, so we must
+ first force the generation of an identical (but separate) type
+ node for the relevant function type. The new node we create
+ will be a variant of the main variant of the original function
+ type. */
+
+ TREE_TYPE (fndecl) = build_type_copy (TREE_TYPE (fndecl));
+
+ TYPE_ACTUAL_ARG_TYPES (TREE_TYPE (fndecl)) = actual;
+ }
+
+ /* Now store the final chain of decls for the arguments
+ as the decl-chain of the current lexical scope.
+ Put the enumerators in as well, at the front so that
+ DECL_ARGUMENTS is not modified. */
+
+ storedecls (chainon (nonparms, DECL_ARGUMENTS (fndecl)));
+ }
+
+ /* Make sure the binding level for the top of the function body
+ gets a BLOCK if there are any in the function.
+ Otherwise, the dbx output is wrong. */
+
+ keep_next_if_subblocks = 1;
+
+ /* ??? This might be an improvement,
+ but needs to be thought about some more. */
+#if 0
+ keep_next_level_flag = 1;
+#endif
+
+ /* Write a record describing this function definition to the prototypes
+ file (if requested). */
+
+ gen_aux_info_record (fndecl, 1, 0, prototype);
+
+ /* Initialize the RTL code for the function. */
+
+ init_function_start (fndecl, input_filename, lineno);
+
+ /* If this is a varargs function, inform function.c. */
+
+ if (c_function_varargs)
+ mark_varargs ();
+
+ /* Declare __FUNCTION__ and __PRETTY_FUNCTION__ for this function. */
+
+ declare_function_name ();
+
+ /* Set up parameters and prepare for return, for the function. */
+
+ expand_function_start (fndecl, 0);
+
+ /* If this function is `main', emit a call to `__main'
+ to run global initializers, etc. */
+ if (DECL_NAME (fndecl)
+ && strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), "main") == 0
+ && DECL_CONTEXT (fndecl) == NULL_TREE)
+ expand_main_function ();
+}
+
+/* SPECPARMS is an identifier list--a chain of TREE_LIST nodes
+ each with a parm name as the TREE_VALUE. A null pointer as TREE_VALUE
+ stands for an ellipsis in the identifier list.
+
+ PARMLIST is the data returned by get_parm_info for the
+ parmlist that follows the semicolon.
+
+ We return a value of the same sort that get_parm_info returns,
+ except that it describes the combination of identifiers and parmlist. */
+
+tree
+combine_parm_decls (specparms, parmlist, void_at_end)
+ tree specparms, parmlist;
+ int void_at_end;
+{
+ register tree fndecl = current_function_decl;
+ register tree parm;
+
+ tree parmdecls = TREE_PURPOSE (parmlist);
+
+ /* This is a chain of any other decls that came in among the parm
+ declarations. They were separated already by get_parm_info,
+ so we just need to keep them separate. */
+ tree nonparms = TREE_VALUE (parmlist);
+
+ tree types = 0;
+
+ for (parm = parmdecls; parm; parm = TREE_CHAIN (parm))
+ DECL_RESULT (parm) = 0;
+
+ for (parm = specparms; parm; parm = TREE_CHAIN (parm))
+ {
+ register tree tail, found = NULL;
+
+ /* See if any of the parmdecls specifies this parm by name. */
+ for (tail = parmdecls; tail; tail = TREE_CHAIN (tail))
+ if (DECL_NAME (tail) == TREE_VALUE (parm))
+ {
+ found = tail;
+ break;
+ }
+
+ /* If declaration already marked, we have a duplicate name.
+ Complain, and don't use this decl twice. */
+ if (found && DECL_RESULT (found) != 0)
+ {
+ error_with_decl (found, "multiple parameters named `%s'");
+ found = 0;
+ }
+
+ /* If the declaration says "void", complain and ignore it. */
+ if (found && TYPE_MAIN_VARIANT (TREE_TYPE (found)) == void_type_node)
+ {
+ error_with_decl (found, "parameter `%s' declared void");
+ TREE_TYPE (found) = integer_type_node;
+ DECL_ARG_TYPE (found) = integer_type_node;
+ layout_decl (found, 0);
+ }
+
+ /* Traditionally, a parm declared float is actually a double. */
+ if (found && flag_traditional
+ && TYPE_MAIN_VARIANT (TREE_TYPE (found)) == float_type_node)
+ {
+ TREE_TYPE (found) = double_type_node;
+ DECL_ARG_TYPE (found) = double_type_node;
+ layout_decl (found, 0);
+ }
+
+ /* If no declaration found, default to int. */
+ if (!found)
+ {
+ found = build_decl (PARM_DECL, TREE_VALUE (parm),
+ integer_type_node);
+ DECL_ARG_TYPE (found) = TREE_TYPE (found);
+ DECL_SOURCE_LINE (found) = DECL_SOURCE_LINE (fndecl);
+ DECL_SOURCE_FILE (found) = DECL_SOURCE_FILE (fndecl);
+ error_with_decl (found, "type of parameter `%s' is not declared");
+ pushdecl (found);
+ }
+
+ TREE_PURPOSE (parm) = found;
+
+ /* Mark this decl as "already found" -- see test, above.
+ It is safe to use DECL_RESULT for this
+ since it is not used in PARM_DECLs or CONST_DECLs. */
+ DECL_RESULT (found) = error_mark_node;
+ }
+
+ /* Complain about any actual PARM_DECLs not matched with any names. */
+
+ for (parm = parmdecls; parm; )
+ {
+ tree next = TREE_CHAIN (parm);
+ TREE_CHAIN (parm) = 0;
+
+ /* Complain about args with incomplete types. */
+ if (TYPE_SIZE (TREE_TYPE (parm)) == 0)
+ {
+ error_with_decl (parm, "parameter `%s' has incomplete type");
+ TREE_TYPE (parm) = error_mark_node;
+ }
+
+ if (DECL_RESULT (parm) == 0)
+ {
+ error_with_decl (parm,
+ "declaration for parameter `%s' but no such parameter");
+ /* Pretend the parameter was not missing.
+ This gets us to a standard state and minimizes
+ further error messages. */
+ specparms
+ = chainon (specparms,
+ tree_cons (parm, NULL_TREE, NULL_TREE));
+ }
+
+ parm = next;
+ }
+
+ /* Chain the declarations together in the order of the list of names.
+ At the same time, build up a list of their types, in reverse order. */
+
+ parm = specparms;
+ parmdecls = 0;
+ {
+ register tree last;
+ for (last = 0; parm; parm = TREE_CHAIN (parm))
+ if (TREE_PURPOSE (parm))
+ {
+ if (last == 0)
+ parmdecls = TREE_PURPOSE (parm);
+ else
+ TREE_CHAIN (last) = TREE_PURPOSE (parm);
+ last = TREE_PURPOSE (parm);
+ TREE_CHAIN (last) = 0;
+
+ types = saveable_tree_cons (NULL_TREE, TREE_TYPE (parm), types);
+ }
+ }
+
+ if (void_at_end)
+ return saveable_tree_cons (parmdecls, nonparms,
+ nreverse (saveable_tree_cons (NULL_TREE, void_type_node, types)));
+
+ return saveable_tree_cons (parmdecls, nonparms, nreverse (types));
+}
+
+/* Finish up a function declaration and compile that function
+ all the way to assembler language output. The free the storage
+ for the function definition.
+
+ This is called after parsing the body of the function definition.
+
+ NESTED is nonzero if the function being finished is nested in another. */
+
+void
+finish_function (nested)
+ int nested;
+{
+ register tree fndecl = current_function_decl;
+
+/* TREE_READONLY (fndecl) = 1;
+ This caused &foo to be of type ptr-to-const-function
+ which then got a warning when stored in a ptr-to-function variable. */
+
+ poplevel (1, 0, 1);
+ BLOCK_SUPERCONTEXT (DECL_INITIAL (fndecl)) = fndecl;
+
+ /* Must mark the RESULT_DECL as being in this function. */
+
+ DECL_CONTEXT (DECL_RESULT (fndecl)) = fndecl;
+
+ /* Obey `register' declarations if `setjmp' is called in this fn. */
+ if (flag_traditional && current_function_calls_setjmp)
+ {
+ setjmp_protect (DECL_INITIAL (fndecl));
+ setjmp_protect_args ();
+ }
+
+#ifdef DEFAULT_MAIN_RETURN
+ if (! strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), "main"))
+ {
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (fndecl)))
+ != integer_type_node)
+ warning_with_decl (fndecl, "return type of `%s' is not `int'");
+ else
+ {
+ /* Make it so that `main' always returns success by default. */
+ DEFAULT_MAIN_RETURN;
+ }
+ }
+#endif
+
+ /* Generate rtl for function exit. */
+ expand_function_end (input_filename, lineno, 0);
+
+ /* So we can tell if jump_optimize sets it to 1. */
+ can_reach_end = 0;
+
+ /* Run the optimizers and output the assembler code for this function. */
+ rest_of_compilation (fndecl);
+
+ current_function_returns_null |= can_reach_end;
+
+ if (TREE_THIS_VOLATILE (fndecl) && current_function_returns_null)
+ warning ("`noreturn' function does return");
+ else if (warn_return_type && can_reach_end
+ && TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (fndecl))) != void_type_node)
+ /* If this function returns non-void and control can drop through,
+ complain. */
+ warning ("control reaches end of non-void function");
+ /* With just -W, complain only if function returns both with
+ and without a value. */
+ else if (extra_warnings
+ && current_function_returns_value && current_function_returns_null)
+ warning ("this function may return with or without a value");
+
+ /* If requested, warn about function definitions where the function will
+ return a value (usually of some struct or union type) which itself will
+ take up a lot of stack space. */
+
+ if (warn_larger_than && !DECL_EXTERNAL (fndecl) && TREE_TYPE (fndecl))
+ {
+ register tree ret_type = TREE_TYPE (TREE_TYPE (fndecl));
+
+ if (ret_type)
+ {
+ register tree ret_type_size = TYPE_SIZE (ret_type);
+
+ if (TREE_CODE (ret_type_size) == INTEGER_CST)
+ {
+ unsigned units
+ = TREE_INT_CST_LOW (ret_type_size) / BITS_PER_UNIT;
+
+ if (units > larger_than_size)
+ warning_with_decl (fndecl,
+ "size of return value of `%s' is %u bytes",
+ units);
+ }
+ }
+ }
+
+ /* Free all the tree nodes making up this function. */
+ /* Switch back to allocating nodes permanently
+ until we start another function. */
+ if (! nested)
+ permanent_allocation (1);
+
+ if (DECL_SAVED_INSNS (fndecl) == 0 && ! nested)
+ {
+ /* Stop pointing to the local nodes about to be freed. */
+ /* But DECL_INITIAL must remain nonzero so we know this
+ was an actual function definition. */
+ /* For a nested function, this is done in pop_c_function_context. */
+ /* If rest_of_compilation set this to 0, leave it 0. */
+ if (DECL_INITIAL (fndecl) != 0)
+ DECL_INITIAL (fndecl) = error_mark_node;
+ DECL_ARGUMENTS (fndecl) = 0;
+ }
+
+ if (! nested)
+ {
+ /* Let the error reporting routines know that we're outside a
+ function. For a nested function, this value is used in
+ pop_c_function_context and then reset via pop_function_context. */
+ current_function_decl = NULL;
+ }
+}
+
+/* Save and restore the variables in this file and elsewhere
+ that keep track of the progress of compilation of the current function.
+ Used for nested functions. */
+
+struct c_function
+{
+ struct c_function *next;
+ tree named_labels;
+ tree shadowed_labels;
+ int returns_value;
+ int returns_null;
+ int warn_about_return_type;
+ int extern_inline;
+ struct binding_level *binding_level;
+};
+
+struct c_function *c_function_chain;
+
+/* Save and reinitialize the variables
+ used during compilation of a C function. */
+
+void
+push_c_function_context ()
+{
+ struct c_function *p
+ = (struct c_function *) xmalloc (sizeof (struct c_function));
+
+ if (pedantic)
+ pedwarn ("ANSI C forbids nested functions");
+
+ push_function_context ();
+
+ p->next = c_function_chain;
+ c_function_chain = p;
+
+ p->named_labels = named_labels;
+ p->shadowed_labels = shadowed_labels;
+ p->returns_value = current_function_returns_value;
+ p->returns_null = current_function_returns_null;
+ p->warn_about_return_type = warn_about_return_type;
+ p->extern_inline = current_extern_inline;
+ p->binding_level = current_binding_level;
+}
+
+/* Restore the variables used during compilation of a C function. */
+
+void
+pop_c_function_context ()
+{
+ struct c_function *p = c_function_chain;
+ tree link;
+
+ /* Bring back all the labels that were shadowed. */
+ for (link = shadowed_labels; link; link = TREE_CHAIN (link))
+ if (DECL_NAME (TREE_VALUE (link)) != 0)
+ IDENTIFIER_LABEL_VALUE (DECL_NAME (TREE_VALUE (link)))
+ = TREE_VALUE (link);
+
+ if (DECL_SAVED_INSNS (current_function_decl) == 0)
+ {
+ /* Stop pointing to the local nodes about to be freed. */
+ /* But DECL_INITIAL must remain nonzero so we know this
+ was an actual function definition. */
+ DECL_INITIAL (current_function_decl) = error_mark_node;
+ DECL_ARGUMENTS (current_function_decl) = 0;
+ }
+
+ pop_function_context ();
+
+ c_function_chain = p->next;
+
+ named_labels = p->named_labels;
+ shadowed_labels = p->shadowed_labels;
+ current_function_returns_value = p->returns_value;
+ current_function_returns_null = p->returns_null;
+ warn_about_return_type = p->warn_about_return_type;
+ current_extern_inline = p->extern_inline;
+ current_binding_level = p->binding_level;
+
+ free (p);
+}
+
+/* integrate_decl_tree calls this function, but since we don't use the
+ DECL_LANG_SPECIFIC field, this is a no-op. */
+
+void
+copy_lang_decl (node)
+ tree node;
+{
+}
diff --git a/gnu/usr.bin/cc/cc1/c-iterate.c b/gnu/usr.bin/cc/cc1/c-iterate.c
new file mode 100644
index 0000000..99f9a79
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1/c-iterate.c
@@ -0,0 +1,595 @@
+/* Build expressions with type checking for C compiler.
+ Copyright (C) 1987, 1988, 1989, 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file is part of the C front end.
+ It is responsible for implementing iterators,
+ both their declarations and the expansion of statements using them. */
+
+#include "config.h"
+#include <stdio.h>
+#include "tree.h"
+#include "c-tree.h"
+#include "flags.h"
+#include "obstack.h"
+#include "rtl.h"
+
+static void expand_stmt_with_iterators_1 ();
+static tree collect_iterators ();
+static void iterator_loop_prologue ();
+static void iterator_loop_epilogue ();
+static void add_ixpansion ();
+static void delete_ixpansion();
+static int top_level_ixpansion_p ();
+static void istack_sublevel_to_current ();
+
+/* A special obstack, and a pointer to the start of
+ all the data in it (so we can free everything easily). */
+static struct obstack ixp_obstack;
+static char *ixp_firstobj;
+
+/*
+ KEEPING TRACK OF EXPANSIONS
+
+ In order to clean out expansions corresponding to statements inside
+ "{(...)}" constructs we have to keep track of all expansions. The
+ cleanup is needed when an automatic, or implicit, expansion on
+ iterator, say X, happens to a statement which contains a {(...)}
+ form with a statement already expanded on X. In this case we have
+ to go back and cleanup the inner expansion. This can be further
+ complicated by the fact that {(...)} can be nested.
+
+ To make this cleanup possible, we keep lists of all expansions, and
+ to make it work for nested constructs, we keep a stack. The list at
+ the top of the stack (ITER_STACK.CURRENT_LEVEL) corresponds to the
+ currently parsed level. All expansions of the levels below the
+ current one are kept in one list whose head is pointed to by
+ ITER_STACK.SUBLEVEL_FIRST (SUBLEVEL_LAST is there for making merges
+ easy). The process works as follows:
+
+ -- On "({" a new node is added to the stack by PUSH_ITERATOR_STACK.
+ The sublevel list is not changed at this point.
+
+ -- On "})" the list for the current level is appended to the sublevel
+ list.
+
+ -- On ";" sublevel lists are appended to the current level lists.
+ The reason is this: if they have not been superseded by the
+ expansion at the current level, they still might be
+ superseded later by the expansion on the higher level.
+ The levels do not have to distinguish levels below, so we
+ can merge the lists together. */
+
+struct ixpansion
+{
+ tree ixdecl; /* Iterator decl */
+ rtx ixprologue_start; /* First insn of epilogue. NULL means */
+ /* explicit (FOR) expansion*/
+ rtx ixprologue_end;
+ rtx ixepilogue_start;
+ rtx ixepilogue_end;
+ struct ixpansion *next; /* Next in the list */
+};
+
+struct iter_stack_node
+{
+ struct ixpansion *first; /* Head of list of ixpansions */
+ struct ixpansion *last; /* Last node in list of ixpansions */
+ struct iter_stack_node *next; /* Next level iterator stack node */
+};
+
+struct iter_stack_node *iter_stack;
+
+struct iter_stack_node sublevel_ixpansions;
+
+/* During collect_iterators, a list of SAVE_EXPRs already scanned. */
+static tree save_exprs;
+
+/* Initialize our obstack once per compilation. */
+
+void
+init_iterators ()
+{
+ gcc_obstack_init (&ixp_obstack);
+ ixp_firstobj = (char *) obstack_alloc (&ixp_obstack, 0);
+}
+
+/* Handle the start of an explicit `for' loop for iterator IDECL. */
+
+void
+iterator_for_loop_start (idecl)
+ tree idecl;
+{
+ ITERATOR_BOUND_P (idecl) = 1;
+ add_ixpansion (idecl, 0, 0, 0, 0);
+ iterator_loop_prologue (idecl, 0, 0);
+}
+
+/* Handle the end of an explicit `for' loop for iterator IDECL. */
+
+void
+iterator_for_loop_end (idecl)
+ tree idecl;
+{
+ iterator_loop_epilogue (idecl, 0, 0);
+ ITERATOR_BOUND_P (idecl) = 0;
+}
+
+/*
+ ITERATOR RTL EXPANSIONS
+
+ Expanding simple statements with iterators is straightforward:
+ collect the list of all free iterators in the statement, and
+ generate a loop for each of them.
+
+ An iterator is "free" if it has not been "bound" by a FOR
+ operator. The DECL_RTL of the iterator is the loop counter. */
+
+/* Expand a statement STMT, possibly containing iterator usage, into RTL. */
+
+void
+iterator_expand (stmt)
+ tree stmt;
+{
+ tree iter_list;
+ save_exprs = NULL_TREE;
+ iter_list = collect_iterators (stmt, NULL_TREE);
+ expand_stmt_with_iterators_1 (stmt, iter_list);
+ istack_sublevel_to_current ();
+}
+
+
+static void
+expand_stmt_with_iterators_1 (stmt, iter_list)
+ tree stmt, iter_list;
+{
+ if (iter_list == 0)
+ expand_expr_stmt (stmt);
+ else
+ {
+ tree current_iterator = TREE_VALUE (iter_list);
+ tree iter_list_tail = TREE_CHAIN (iter_list);
+ rtx p_start, p_end, e_start, e_end;
+
+ iterator_loop_prologue (current_iterator, &p_start, &p_end);
+ expand_stmt_with_iterators_1 (stmt, iter_list_tail);
+ iterator_loop_epilogue (current_iterator, &e_start, &e_end);
+
+ /** Delete all inner expansions based on current_iterator **/
+ /** before adding the outer one. **/
+
+ delete_ixpansion (current_iterator);
+ add_ixpansion (current_iterator, p_start, p_end, e_start, e_end);
+ }
+}
+
+
+/* Return a list containing all the free (i.e. not bound by a
+ containing `for' statement) iterators mentioned in EXP, plus those
+ in LIST. Do not add duplicate entries to the list. */
+
+static tree
+collect_iterators (exp, list)
+ tree exp, list;
+{
+ if (exp == 0) return list;
+
+ switch (TREE_CODE (exp))
+ {
+ case VAR_DECL:
+ if (! ITERATOR_P (exp) || ITERATOR_BOUND_P (exp))
+ return list;
+ if (value_member (exp, list))
+ return list;
+ return tree_cons (NULL_TREE, exp, list);
+
+ case TREE_LIST:
+ {
+ tree tail;
+ for (tail = exp; tail; tail = TREE_CHAIN (tail))
+ list = collect_iterators (TREE_VALUE (tail), list);
+ return list;
+ }
+
+ case SAVE_EXPR:
+ /* In each scan, scan a given save_expr only once. */
+ if (value_member (exp, save_exprs))
+ return list;
+
+ save_exprs = tree_cons (NULL_TREE, exp, save_exprs);
+ return collect_iterators (TREE_OPERAND (exp, 0), list);
+
+ /* we do not automatically iterate blocks -- one must */
+ /* use the FOR construct to do that */
+
+ case BLOCK:
+ return list;
+
+ default:
+ switch (TREE_CODE_CLASS (TREE_CODE (exp)))
+ {
+ case '1':
+ return collect_iterators (TREE_OPERAND (exp, 0), list);
+
+ case '2':
+ case '<':
+ return collect_iterators (TREE_OPERAND (exp, 0),
+ collect_iterators (TREE_OPERAND (exp, 1),
+ list));
+
+ case 'e':
+ case 'r':
+ {
+ int num_args = tree_code_length[(int) TREE_CODE (exp)];
+ int i;
+
+ /* Some tree codes have RTL, not trees, as operands. */
+ switch (TREE_CODE (exp))
+ {
+ case CALL_EXPR:
+ num_args = 2;
+ break;
+ case METHOD_CALL_EXPR:
+ num_args = 3;
+ break;
+ case WITH_CLEANUP_EXPR:
+ num_args = 1;
+ break;
+ case RTL_EXPR:
+ return list;
+ }
+
+ for (i = 0; i < num_args; i++)
+ list = collect_iterators (TREE_OPERAND (exp, i), list);
+ return list;
+ }
+ default:
+ return list;
+ }
+ }
+}
+
+/* Emit rtl for the start of a loop for iterator IDECL.
+
+ If necessary, create loop counter rtx and store it as DECL_RTL of IDECL.
+
+ The prologue normally starts and ends with notes, which are returned
+ by this function in *START_NOTE and *END_NODE.
+ If START_NOTE and END_NODE are 0, we don't make those notes. */
+
+static void
+iterator_loop_prologue (idecl, start_note, end_note)
+ tree idecl;
+ rtx *start_note, *end_note;
+{
+ tree expr;
+
+ /* Force the save_expr in DECL_INITIAL to be calculated
+ if it hasn't been calculated yet. */
+ expand_expr (DECL_INITIAL (idecl), const0_rtx, VOIDmode, 0);
+
+ if (DECL_RTL (idecl) == 0)
+ expand_decl (idecl);
+
+ if (start_note)
+ *start_note = emit_note (0, NOTE_INSN_DELETED);
+
+ /* Initialize counter. */
+ expr = build (MODIFY_EXPR, TREE_TYPE (idecl), idecl, integer_zero_node);
+ TREE_SIDE_EFFECTS (expr) = 1;
+ expand_expr (expr, const0_rtx, VOIDmode, 0);
+
+ expand_start_loop_continue_elsewhere (1);
+
+ ITERATOR_BOUND_P (idecl) = 1;
+
+ if (end_note)
+ *end_note = emit_note (0, NOTE_INSN_DELETED);
+}
+
+/* Similar to the previous function, but for the end of the loop.
+
+ DECL_RTL is zeroed unless we are inside "({...})". The reason for that is
+ described below.
+
+ When we create two (or more) loops based on the same IDECL, and
+ both inside the same "({...})" construct, we must be prepared to
+ delete both of the loops and create a single one on the level
+ above, i.e. enclosing the "({...})". The new loop has to use the
+ same counter rtl because the references to the iterator decl
+ (IDECL) have already been expanded as references to the counter
+ rtl.
+
+ It is incorrect to use the same counter reg in different functions,
+ and it is desirable to use different counters in disjoint loops
+ when we know there's no need to combine them (because then they can
+ get allocated separately). */
+
+static void
+iterator_loop_epilogue (idecl, start_note, end_note)
+ tree idecl;
+ rtx *start_note, *end_note;
+{
+ tree test, incr;
+
+ if (start_note)
+ *start_note = emit_note (0, NOTE_INSN_DELETED);
+ expand_loop_continue_here ();
+ incr = build_binary_op (PLUS_EXPR, idecl, integer_one_node, 0);
+ incr = build (MODIFY_EXPR, TREE_TYPE (idecl), idecl, incr);
+ TREE_SIDE_EFFECTS (incr) = 1;
+ expand_expr (incr, const0_rtx, VOIDmode, 0);
+ test = build_binary_op (LT_EXPR, idecl, DECL_INITIAL (idecl), 0);
+ expand_exit_loop_if_false (0, test);
+ expand_end_loop ();
+
+ ITERATOR_BOUND_P (idecl) = 0;
+ /* we can reset rtl since there is not chance that this expansion */
+ /* would be superceded by a higher level one */
+ if (top_level_ixpansion_p ())
+ DECL_RTL (idecl) = 0;
+ if (end_note)
+ *end_note = emit_note (0, NOTE_INSN_DELETED);
+}
+
+/* Return true if we are not currently inside a "({...})" construct. */
+
+static int
+top_level_ixpansion_p ()
+{
+ return iter_stack == 0;
+}
+
+/* Given two chains of iter_stack_nodes,
+ append the nodes in X into Y. */
+
+static void
+isn_append (x, y)
+ struct iter_stack_node *x, *y;
+{
+ if (x->first == 0)
+ return;
+
+ if (y->first == 0)
+ {
+ y->first = x->first;
+ y->last = x->last;
+ }
+ else
+ {
+ y->last->next = x->first;
+ y->last = x->last;
+ }
+}
+
+/** Make X empty **/
+
+#define ISN_ZERO(X) (X).first=(X).last=0
+
+/* Move the ixpansions in sublevel_ixpansions into the current
+ node on the iter_stack, or discard them if the iter_stack is empty.
+ We do this at the end of a statement. */
+
+static void
+istack_sublevel_to_current ()
+{
+ /* At the top level we can throw away sublevel's expansions **/
+ /* because there is nobody above us to ask for a cleanup **/
+ if (iter_stack != 0)
+ /** Merging with empty sublevel list is a no-op **/
+ if (sublevel_ixpansions.last)
+ isn_append (&sublevel_ixpansions, iter_stack);
+
+ if (iter_stack == 0)
+ obstack_free (&ixp_obstack, ixp_firstobj);
+
+ ISN_ZERO (sublevel_ixpansions);
+}
+
+/* Push a new node on the iter_stack, when we enter a ({...}). */
+
+void
+push_iterator_stack ()
+{
+ struct iter_stack_node *new_top
+ = (struct iter_stack_node*)
+ obstack_alloc (&ixp_obstack, sizeof (struct iter_stack_node));
+
+ new_top->first = 0;
+ new_top->last = 0;
+ new_top->next = iter_stack;
+ iter_stack = new_top;
+}
+
+/* Pop iter_stack, moving the ixpansions in the node being popped
+ into sublevel_ixpansions. */
+
+void
+pop_iterator_stack ()
+{
+ if (iter_stack == 0)
+ abort ();
+
+ isn_append (iter_stack, &sublevel_ixpansions);
+ /** Pop current level node: */
+ iter_stack = iter_stack->next;
+}
+
+
+/* Record an iterator expansion ("ixpansion") for IDECL.
+ The remaining paramters are the notes in the loop entry
+ and exit rtl. */
+
+static void
+add_ixpansion (idecl, pro_start, pro_end, epi_start, epi_end)
+ tree idecl;
+ rtx pro_start, pro_end, epi_start, epi_end;
+{
+ struct ixpansion* newix;
+
+ /* Do nothing if we are not inside "({...})",
+ as in that case this expansion can't need subsequent RTL modification. */
+ if (iter_stack == 0)
+ return;
+
+ newix = (struct ixpansion*) obstack_alloc (&ixp_obstack,
+ sizeof (struct ixpansion));
+ newix->ixdecl = idecl;
+ newix->ixprologue_start = pro_start;
+ newix->ixprologue_end = pro_end;
+ newix->ixepilogue_start = epi_start;
+ newix->ixepilogue_end = epi_end;
+
+ newix->next = iter_stack->first;
+ iter_stack->first = newix;
+ if (iter_stack->last == 0)
+ iter_stack->last = newix;
+}
+
+/* Delete the RTL for all ixpansions for iterator IDECL
+ in our sublevels. We do this when we make a larger
+ containing expansion for IDECL. */
+
+static void
+delete_ixpansion (idecl)
+ tree idecl;
+{
+ struct ixpansion* previx = 0, *ix;
+
+ for (ix = sublevel_ixpansions.first; ix; ix = ix->next)
+ if (ix->ixdecl == idecl)
+ {
+ /** zero means that this is a mark for FOR -- **/
+ /** we do not delete anything, just issue an error. **/
+
+ if (ix->ixprologue_start == 0)
+ error_with_decl (idecl,
+ "`for (%s)' appears within implicit iteration");
+ else
+ {
+ rtx insn;
+ /* We delete all insns, including notes because leaving loop */
+ /* notes and barriers produced by iterator expansion would */
+ /* be misleading to other phases */
+
+ for (insn = NEXT_INSN (ix->ixprologue_start);
+ insn != ix->ixprologue_end;
+ insn = NEXT_INSN (insn))
+ delete_insn (insn);
+ for (insn = NEXT_INSN (ix->ixepilogue_start);
+ insn != ix->ixepilogue_end;
+ insn = NEXT_INSN (insn))
+ delete_insn (insn);
+ }
+
+ /* Delete this ixpansion from sublevel_ixpansions. */
+ if (previx)
+ previx->next = ix->next;
+ else
+ sublevel_ixpansions.first = ix->next;
+ if (sublevel_ixpansions.last == ix)
+ sublevel_ixpansions.last = previx;
+ }
+ else
+ previx = ix;
+}
+
+#ifdef DEBUG_ITERATORS
+
+/* The functions below are for use from source level debugger.
+ They print short forms of iterator lists and the iterator stack. */
+
+/* Print the name of the iterator D. */
+
+void
+prdecl (d)
+ tree d;
+{
+ if (d)
+ {
+ if (TREE_CODE (d) == VAR_DECL)
+ {
+ tree tname = DECL_NAME (d);
+ char *dname = IDENTIFIER_POINTER (tname);
+ fprintf (stderr, dname);
+ }
+ else
+ fprintf (stderr, "<<Not a Decl!!!>>");
+ }
+ else
+ fprintf (stderr, "<<NULL!!>>");
+}
+
+/* Print Iterator List -- names only */
+
+tree
+pil (head)
+ tree head;
+{
+ tree current, next;
+ for (current = head; current; current = next)
+ {
+ tree node = TREE_VALUE (current);
+ prdecl (node);
+ next = TREE_CHAIN (current);
+ if (next) fprintf (stderr, ",");
+ }
+ fprintf (stderr, "\n");
+}
+
+/* Print IXpansion List */
+
+struct ixpansion *
+pixl (head)
+ struct ixpansion *head;
+{
+ struct ixpansion *current, *next;
+ fprintf (stderr, "> ");
+ if (head == 0)
+ fprintf (stderr, "(empty)");
+
+ for (current=head; current; current = next)
+ {
+ tree node = current->ixdecl;
+ prdecl (node);
+ next = current->next;
+ if (next)
+ fprintf (stderr, ",");
+ }
+ fprintf (stderr, "\n");
+ return head;
+}
+
+/* Print Iterator Stack*/
+
+void
+pis ()
+{
+ struct iter_stack_node *stack_node;
+
+ fprintf (stderr, "--SubLevel: ");
+ pixl (sublevel_ixpansions.first);
+ fprintf (stderr, "--Stack:--\n");
+ for (stack_node = iter_stack;
+ stack_node;
+ stack_node = stack_node->next)
+ pixl (stack_node->first);
+}
+
+#endif /* DEBUG_ITERATORS */
diff --git a/gnu/usr.bin/cc/cc1/c-lang.c b/gnu/usr.bin/cc/cc1/c-lang.c
new file mode 100644
index 0000000..8b46b3c
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1/c-lang.c
@@ -0,0 +1,129 @@
+/* Language-specific hook definitions for C front end.
+ Copyright (C) 1991 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#include "tree.h"
+#include <stdio.h>
+#include "input.h"
+
+/* Each of the functions defined here
+ is an alternative to a function in objc-actions.c. */
+
+int
+lang_decode_option (p)
+ char *p;
+{
+ return c_decode_option (p);
+}
+
+void
+lang_init ()
+{
+ /* the beginning of the file is a new line; check for # */
+ /* With luck, we discover the real source file's name from that
+ and put it in input_filename. */
+ ungetc (check_newline (), finput);
+}
+
+void
+lang_finish ()
+{
+}
+
+char *
+lang_identify ()
+{
+ return "c";
+}
+
+void
+print_lang_statistics ()
+{
+}
+
+/* Used by c-lex.c, but only for objc. */
+
+tree
+lookup_interface (arg)
+ tree arg;
+{
+ return 0;
+}
+
+tree
+is_class_name (arg)
+ tree arg;
+{
+ return 0;
+}
+
+void
+maybe_objc_check_decl (decl)
+ tree decl;
+{
+}
+
+int
+maybe_objc_comptypes (lhs, rhs, reflexive)
+ tree lhs, rhs;
+ int reflexive;
+{
+ return -1;
+}
+
+tree
+maybe_objc_method_name (decl)
+ tree decl;
+{
+ return 0;
+}
+
+tree
+maybe_building_objc_message_expr ()
+{
+ return 0;
+}
+
+int
+recognize_objc_keyword ()
+{
+ return 0;
+}
+
+tree
+build_objc_string (len, str)
+ int len;
+ char *str;
+{
+ abort ();
+ return NULL_TREE;
+}
+
+void
+GNU_xref_begin ()
+{
+ fatal ("GCC does not yet support XREF");
+}
+
+void
+GNU_xref_end ()
+{
+ fatal ("GCC does not yet support XREF");
+}
diff --git a/gnu/usr.bin/cc/cc1/c-lex.c b/gnu/usr.bin/cc/cc1/c-lex.c
new file mode 100644
index 0000000..17d50be
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1/c-lex.c
@@ -0,0 +1,1983 @@
+/* Lexical analyzer for C and Objective C.
+ Copyright (C) 1987, 1988, 1989, 1992, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <setjmp.h>
+
+#include "config.h"
+#include "rtl.h"
+#include "tree.h"
+#include "input.h"
+#include "c-lex.h"
+#include "c-tree.h"
+#include "flags.h"
+#include "c-parse.h"
+
+#include <ctype.h>
+
+#ifdef MULTIBYTE_CHARS
+#include <stdlib.h>
+#include <locale.h>
+#endif
+
+#ifndef errno
+extern int errno;
+#endif
+
+/* The elements of `ridpointers' are identifier nodes
+ for the reserved type names and storage classes.
+ It is indexed by a RID_... value. */
+tree ridpointers[(int) RID_MAX];
+
+/* Cause the `yydebug' variable to be defined. */
+#define YYDEBUG 1
+
+/* the declaration found for the last IDENTIFIER token read in.
+ yylex must look this up to detect typedefs, which get token type TYPENAME,
+ so it is left around in case the identifier is not a typedef but is
+ used in a context which makes it a reference to a variable. */
+tree lastiddecl;
+
+/* Nonzero enables objc features. */
+
+int doing_objc_thang;
+
+extern tree is_class_name ();
+
+extern int yydebug;
+
+/* File used for outputting assembler code. */
+extern FILE *asm_out_file;
+
+#ifndef WCHAR_TYPE_SIZE
+#ifdef INT_TYPE_SIZE
+#define WCHAR_TYPE_SIZE INT_TYPE_SIZE
+#else
+#define WCHAR_TYPE_SIZE BITS_PER_WORD
+#endif
+#endif
+
+/* Number of bytes in a wide character. */
+#define WCHAR_BYTES (WCHAR_TYPE_SIZE / BITS_PER_UNIT)
+
+static int maxtoken; /* Current nominal length of token buffer. */
+char *token_buffer; /* Pointer to token buffer.
+ Actual allocated length is maxtoken + 2.
+ This is not static because objc-parse.y uses it. */
+
+/* Nonzero if end-of-file has been seen on input. */
+static int end_of_file;
+
+/* Buffered-back input character; faster than using ungetc. */
+static int nextchar = -1;
+
+int check_newline ();
+
+/* Do not insert generated code into the source, instead, include it.
+ This allows us to build gcc automatically even for targets that
+ need to add or modify the reserved keyword lists. */
+#include "c-gperf.h"
+
+/* Return something to represent absolute declarators containing a *.
+ TARGET is the absolute declarator that the * contains.
+ TYPE_QUALS is a list of modifiers such as const or volatile
+ to apply to the pointer type, represented as identifiers.
+
+ We return an INDIRECT_REF whose "contents" are TARGET
+ and whose type is the modifier list. */
+
+tree
+make_pointer_declarator (type_quals, target)
+ tree type_quals, target;
+{
+ return build1 (INDIRECT_REF, type_quals, target);
+}
+
+void
+forget_protocol_qualifiers ()
+{
+ int i, n = sizeof wordlist / sizeof (struct resword);
+
+ for (i = 0; i < n; i++)
+ if ((int) wordlist[i].rid >= (int) RID_IN
+ && (int) wordlist[i].rid <= (int) RID_ONEWAY)
+ wordlist[i].name = "";
+}
+
+void
+remember_protocol_qualifiers ()
+{
+ int i, n = sizeof wordlist / sizeof (struct resword);
+
+ for (i = 0; i < n; i++)
+ if (wordlist[i].rid == RID_IN)
+ wordlist[i].name = "in";
+ else if (wordlist[i].rid == RID_OUT)
+ wordlist[i].name = "out";
+ else if (wordlist[i].rid == RID_INOUT)
+ wordlist[i].name = "inout";
+ else if (wordlist[i].rid == RID_BYCOPY)
+ wordlist[i].name = "bycopy";
+ else if (wordlist[i].rid == RID_ONEWAY)
+ wordlist[i].name = "oneway";
+}
+
+void
+init_lex ()
+{
+ /* Make identifier nodes long enough for the language-specific slots. */
+ set_identifier_size (sizeof (struct lang_identifier));
+
+ /* Start it at 0, because check_newline is called at the very beginning
+ and will increment it to 1. */
+ lineno = 0;
+
+#ifdef MULTIBYTE_CHARS
+ /* Change to the native locale for multibyte conversions. */
+ setlocale (LC_CTYPE, "");
+#endif
+
+ maxtoken = 40;
+ token_buffer = (char *) xmalloc (maxtoken + 2);
+
+ ridpointers[(int) RID_INT] = get_identifier ("int");
+ ridpointers[(int) RID_CHAR] = get_identifier ("char");
+ ridpointers[(int) RID_VOID] = get_identifier ("void");
+ ridpointers[(int) RID_FLOAT] = get_identifier ("float");
+ ridpointers[(int) RID_DOUBLE] = get_identifier ("double");
+ ridpointers[(int) RID_SHORT] = get_identifier ("short");
+ ridpointers[(int) RID_LONG] = get_identifier ("long");
+ ridpointers[(int) RID_UNSIGNED] = get_identifier ("unsigned");
+ ridpointers[(int) RID_SIGNED] = get_identifier ("signed");
+ ridpointers[(int) RID_INLINE] = get_identifier ("inline");
+ ridpointers[(int) RID_CONST] = get_identifier ("const");
+ ridpointers[(int) RID_VOLATILE] = get_identifier ("volatile");
+ ridpointers[(int) RID_AUTO] = get_identifier ("auto");
+ ridpointers[(int) RID_STATIC] = get_identifier ("static");
+ ridpointers[(int) RID_EXTERN] = get_identifier ("extern");
+ ridpointers[(int) RID_TYPEDEF] = get_identifier ("typedef");
+ ridpointers[(int) RID_REGISTER] = get_identifier ("register");
+ ridpointers[(int) RID_ITERATOR] = get_identifier ("iterator");
+ ridpointers[(int) RID_COMPLEX] = get_identifier ("complex");
+ ridpointers[(int) RID_ID] = get_identifier ("id");
+ ridpointers[(int) RID_IN] = get_identifier ("in");
+ ridpointers[(int) RID_OUT] = get_identifier ("out");
+ ridpointers[(int) RID_INOUT] = get_identifier ("inout");
+ ridpointers[(int) RID_BYCOPY] = get_identifier ("bycopy");
+ ridpointers[(int) RID_ONEWAY] = get_identifier ("oneway");
+ forget_protocol_qualifiers();
+
+ /* Some options inhibit certain reserved words.
+ Clear those words out of the hash table so they won't be recognized. */
+#define UNSET_RESERVED_WORD(STRING) \
+ do { struct resword *s = is_reserved_word (STRING, sizeof (STRING) - 1); \
+ if (s) s->name = ""; } while (0)
+
+ if (! doing_objc_thang)
+ UNSET_RESERVED_WORD ("id");
+
+ if (flag_traditional)
+ {
+ UNSET_RESERVED_WORD ("const");
+ UNSET_RESERVED_WORD ("volatile");
+ UNSET_RESERVED_WORD ("typeof");
+ UNSET_RESERVED_WORD ("signed");
+ UNSET_RESERVED_WORD ("inline");
+ UNSET_RESERVED_WORD ("iterator");
+ UNSET_RESERVED_WORD ("complex");
+ }
+ if (flag_no_asm)
+ {
+ UNSET_RESERVED_WORD ("asm");
+ UNSET_RESERVED_WORD ("typeof");
+ UNSET_RESERVED_WORD ("inline");
+ UNSET_RESERVED_WORD ("iterator");
+ UNSET_RESERVED_WORD ("complex");
+ }
+}
+
+void
+reinit_parse_for_function ()
+{
+}
+
+/* Function used when yydebug is set, to print a token in more detail. */
+
+void
+yyprint (file, yychar, yylval)
+ FILE *file;
+ int yychar;
+ YYSTYPE yylval;
+{
+ tree t;
+ switch (yychar)
+ {
+ case IDENTIFIER:
+ case TYPENAME:
+ case OBJECTNAME:
+ t = yylval.ttype;
+ if (IDENTIFIER_POINTER (t))
+ fprintf (file, " `%s'", IDENTIFIER_POINTER (t));
+ break;
+
+ case CONSTANT:
+ t = yylval.ttype;
+ if (TREE_CODE (t) == INTEGER_CST)
+ fprintf (file,
+#if HOST_BITS_PER_WIDE_INT == 64
+#if HOST_BITS_PER_WIDE_INT != HOST_BITS_PER_INT
+ " 0x%lx%016lx",
+#else
+ " 0x%x%016x",
+#endif
+#else
+#if HOST_BITS_PER_WIDE_INT != HOST_BITS_PER_INT
+ " 0x%lx%08lx",
+#else
+ " 0x%x%08x",
+#endif
+#endif
+ TREE_INT_CST_HIGH (t), TREE_INT_CST_LOW (t));
+ break;
+ }
+}
+
+
+/* If C is not whitespace, return C.
+ Otherwise skip whitespace and return first nonwhite char read. */
+
+static int
+skip_white_space (c)
+ register int c;
+{
+ static int newline_warning = 0;
+
+ for (;;)
+ {
+ switch (c)
+ {
+ /* We don't recognize comments here, because
+ cpp output can include / and * consecutively as operators.
+ Also, there's no need, since cpp removes all comments. */
+
+ case '\n':
+ c = check_newline ();
+ break;
+
+ case ' ':
+ case '\t':
+ case '\f':
+ case '\v':
+ case '\b':
+ c = getc (finput);
+ break;
+
+ case '\r':
+ /* ANSI C says the effects of a carriage return in a source file
+ are undefined. */
+ if (pedantic && !newline_warning)
+ {
+ warning ("carriage return in source file");
+ warning ("(we only warn about the first carriage return)");
+ newline_warning = 1;
+ }
+ c = getc (finput);
+ break;
+
+ case '\\':
+ c = getc (finput);
+ if (c == '\n')
+ lineno++;
+ else
+ error ("stray '\\' in program");
+ c = getc (finput);
+ break;
+
+ default:
+ return (c);
+ }
+ }
+}
+
+/* Skips all of the white space at the current location in the input file.
+ Must use and reset nextchar if it has the next character. */
+
+void
+position_after_white_space ()
+{
+ register int c;
+
+ if (nextchar != -1)
+ c = nextchar, nextchar = -1;
+ else
+ c = getc (finput);
+
+ ungetc (skip_white_space (c), finput);
+}
+
+/* Make the token buffer longer, preserving the data in it.
+ P should point to just beyond the last valid character in the old buffer.
+ The value we return is a pointer to the new buffer
+ at a place corresponding to P. */
+
+static char *
+extend_token_buffer (p)
+ char *p;
+{
+ int offset = p - token_buffer;
+
+ maxtoken = maxtoken * 2 + 10;
+ token_buffer = (char *) xrealloc (token_buffer, maxtoken + 2);
+
+ return token_buffer + offset;
+}
+
+/* At the beginning of a line, increment the line number
+ and process any #-directive on this line.
+ If the line is a #-directive, read the entire line and return a newline.
+ Otherwise, return the line's first non-whitespace character. */
+
+int
+check_newline ()
+{
+ register int c;
+ register int token;
+
+ lineno++;
+
+ /* Read first nonwhite char on the line. */
+
+ c = getc (finput);
+ while (c == ' ' || c == '\t')
+ c = getc (finput);
+
+ if (c != '#')
+ {
+ /* If not #, return it so caller will use it. */
+ return c;
+ }
+
+ /* Read first nonwhite char after the `#'. */
+
+ c = getc (finput);
+ while (c == ' ' || c == '\t')
+ c = getc (finput);
+
+ /* If a letter follows, then if the word here is `line', skip
+ it and ignore it; otherwise, ignore the line, with an error
+ if the word isn't `pragma', `ident', `define', or `undef'. */
+
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
+ {
+ if (c == 'p')
+ {
+ if (getc (finput) == 'r'
+ && getc (finput) == 'a'
+ && getc (finput) == 'g'
+ && getc (finput) == 'm'
+ && getc (finput) == 'a'
+ && ((c = getc (finput)) == ' ' || c == '\t' || c == '\n'))
+ {
+#ifdef HANDLE_SYSV_PRAGMA
+ return handle_sysv_pragma (finput, c);
+#else /* !HANDLE_SYSV_PRAGMA */
+#ifdef HANDLE_PRAGMA
+ HANDLE_PRAGMA (finput);
+#endif /* HANDLE_PRAGMA */
+ goto skipline;
+#endif /* !HANDLE_SYSV_PRAGMA */
+ }
+ }
+
+ else if (c == 'd')
+ {
+ if (getc (finput) == 'e'
+ && getc (finput) == 'f'
+ && getc (finput) == 'i'
+ && getc (finput) == 'n'
+ && getc (finput) == 'e'
+ && ((c = getc (finput)) == ' ' || c == '\t' || c == '\n'))
+ {
+#ifdef DWARF_DEBUGGING_INFO
+ if ((debug_info_level == DINFO_LEVEL_VERBOSE)
+ && (write_symbols == DWARF_DEBUG))
+ dwarfout_define (lineno, get_directive_line (finput));
+#endif /* DWARF_DEBUGGING_INFO */
+ goto skipline;
+ }
+ }
+ else if (c == 'u')
+ {
+ if (getc (finput) == 'n'
+ && getc (finput) == 'd'
+ && getc (finput) == 'e'
+ && getc (finput) == 'f'
+ && ((c = getc (finput)) == ' ' || c == '\t' || c == '\n'))
+ {
+#ifdef DWARF_DEBUGGING_INFO
+ if ((debug_info_level == DINFO_LEVEL_VERBOSE)
+ && (write_symbols == DWARF_DEBUG))
+ dwarfout_undef (lineno, get_directive_line (finput));
+#endif /* DWARF_DEBUGGING_INFO */
+ goto skipline;
+ }
+ }
+ else if (c == 'l')
+ {
+ if (getc (finput) == 'i'
+ && getc (finput) == 'n'
+ && getc (finput) == 'e'
+ && ((c = getc (finput)) == ' ' || c == '\t'))
+ goto linenum;
+ }
+ else if (c == 'i')
+ {
+ if (getc (finput) == 'd'
+ && getc (finput) == 'e'
+ && getc (finput) == 'n'
+ && getc (finput) == 't'
+ && ((c = getc (finput)) == ' ' || c == '\t'))
+ {
+ /* #ident. The pedantic warning is now in cccp.c. */
+
+ /* Here we have just seen `#ident '.
+ A string constant should follow. */
+
+ while (c == ' ' || c == '\t')
+ c = getc (finput);
+
+ /* If no argument, ignore the line. */
+ if (c == '\n')
+ return c;
+
+ ungetc (c, finput);
+ token = yylex ();
+ if (token != STRING
+ || TREE_CODE (yylval.ttype) != STRING_CST)
+ {
+ error ("invalid #ident");
+ goto skipline;
+ }
+
+ if (!flag_no_ident)
+ {
+#ifdef ASM_OUTPUT_IDENT
+ ASM_OUTPUT_IDENT (asm_out_file, TREE_STRING_POINTER (yylval.ttype));
+#endif
+ }
+
+ /* Skip the rest of this line. */
+ goto skipline;
+ }
+ }
+
+ error ("undefined or invalid # directive");
+ goto skipline;
+ }
+
+linenum:
+ /* Here we have either `#line' or `# <nonletter>'.
+ In either case, it should be a line number; a digit should follow. */
+
+ while (c == ' ' || c == '\t')
+ c = getc (finput);
+
+ /* If the # is the only nonwhite char on the line,
+ just ignore it. Check the new newline. */
+ if (c == '\n')
+ return c;
+
+ /* Something follows the #; read a token. */
+
+ ungetc (c, finput);
+ token = yylex ();
+
+ if (token == CONSTANT
+ && TREE_CODE (yylval.ttype) == INTEGER_CST)
+ {
+ int old_lineno = lineno;
+ int used_up = 0;
+ /* subtract one, because it is the following line that
+ gets the specified number */
+
+ int l = TREE_INT_CST_LOW (yylval.ttype) - 1;
+
+ /* Is this the last nonwhite stuff on the line? */
+ c = getc (finput);
+ while (c == ' ' || c == '\t')
+ c = getc (finput);
+ if (c == '\n')
+ {
+ /* No more: store the line number and check following line. */
+ lineno = l;
+ return c;
+ }
+ ungetc (c, finput);
+
+ /* More follows: it must be a string constant (filename). */
+
+ /* Read the string constant. */
+ token = yylex ();
+
+ if (token != STRING || TREE_CODE (yylval.ttype) != STRING_CST)
+ {
+ error ("invalid #line");
+ goto skipline;
+ }
+
+ input_filename
+ = (char *) permalloc (TREE_STRING_LENGTH (yylval.ttype) + 1);
+ strcpy (input_filename, TREE_STRING_POINTER (yylval.ttype));
+ lineno = l;
+
+ /* Each change of file name
+ reinitializes whether we are now in a system header. */
+ in_system_header = 0;
+
+ if (main_input_filename == 0)
+ main_input_filename = input_filename;
+
+ /* Is this the last nonwhite stuff on the line? */
+ c = getc (finput);
+ while (c == ' ' || c == '\t')
+ c = getc (finput);
+ if (c == '\n')
+ {
+ /* Update the name in the top element of input_file_stack. */
+ if (input_file_stack)
+ input_file_stack->name = input_filename;
+
+ return c;
+ }
+ ungetc (c, finput);
+
+ token = yylex ();
+ used_up = 0;
+
+ /* `1' after file name means entering new file.
+ `2' after file name means just left a file. */
+
+ if (token == CONSTANT
+ && TREE_CODE (yylval.ttype) == INTEGER_CST)
+ {
+ if (TREE_INT_CST_LOW (yylval.ttype) == 1)
+ {
+ /* Pushing to a new file. */
+ struct file_stack *p
+ = (struct file_stack *) xmalloc (sizeof (struct file_stack));
+ input_file_stack->line = old_lineno;
+ p->next = input_file_stack;
+ p->name = input_filename;
+ input_file_stack = p;
+ input_file_stack_tick++;
+#ifdef DWARF_DEBUGGING_INFO
+ if (debug_info_level == DINFO_LEVEL_VERBOSE
+ && write_symbols == DWARF_DEBUG)
+ dwarfout_start_new_source_file (input_filename);
+#endif /* DWARF_DEBUGGING_INFO */
+
+ used_up = 1;
+ }
+ else if (TREE_INT_CST_LOW (yylval.ttype) == 2)
+ {
+ /* Popping out of a file. */
+ if (input_file_stack->next)
+ {
+ struct file_stack *p = input_file_stack;
+ input_file_stack = p->next;
+ free (p);
+ input_file_stack_tick++;
+#ifdef DWARF_DEBUGGING_INFO
+ if (debug_info_level == DINFO_LEVEL_VERBOSE
+ && write_symbols == DWARF_DEBUG)
+ dwarfout_resume_previous_source_file (input_file_stack->line);
+#endif /* DWARF_DEBUGGING_INFO */
+ }
+ else
+ error ("#-lines for entering and leaving files don't match");
+
+ used_up = 1;
+ }
+ }
+
+ /* Now that we've pushed or popped the input stack,
+ update the name in the top element. */
+ if (input_file_stack)
+ input_file_stack->name = input_filename;
+
+ /* If we have handled a `1' or a `2',
+ see if there is another number to read. */
+ if (used_up)
+ {
+ /* Is this the last nonwhite stuff on the line? */
+ c = getc (finput);
+ while (c == ' ' || c == '\t')
+ c = getc (finput);
+ if (c == '\n')
+ return c;
+ ungetc (c, finput);
+
+ token = yylex ();
+ used_up = 0;
+ }
+
+ /* `3' after file name means this is a system header file. */
+
+ if (token == CONSTANT
+ && TREE_CODE (yylval.ttype) == INTEGER_CST
+ && TREE_INT_CST_LOW (yylval.ttype) == 3)
+ in_system_header = 1;
+ }
+ else
+ error ("invalid #-line");
+
+ /* skip the rest of this line. */
+ skipline:
+ if (c == '\n')
+ return c;
+ while ((c = getc (finput)) != EOF && c != '\n');
+ return c;
+}
+
+#ifdef HANDLE_SYSV_PRAGMA
+
+/* Handle a #pragma directive. INPUT is the current input stream,
+ and C is a character to reread. Processes the entire input line
+ and returns a character for the caller to reread: either \n or EOF. */
+
+/* This function has to be in this file, in order to get at
+ the token types. */
+
+int
+handle_sysv_pragma (input, c)
+ FILE *input;
+ int c;
+{
+ for (;;)
+ {
+ while (c == ' ' || c == '\t')
+ c = getc (input);
+ if (c == '\n' || c == EOF)
+ {
+ handle_pragma_token (0, 0);
+ return c;
+ }
+ ungetc (c, input);
+ switch (yylex ())
+ {
+ case IDENTIFIER:
+ case TYPENAME:
+ case STRING:
+ case CONSTANT:
+ handle_pragma_token (token_buffer, yylval.ttype);
+ break;
+ default:
+ handle_pragma_token (token_buffer, 0);
+ }
+ if (nextchar >= 0)
+ c = nextchar, nextchar = -1;
+ else
+ c = getc (input);
+ }
+}
+
+#endif /* HANDLE_SYSV_PRAGMA */
+
+#define ENDFILE -1 /* token that represents end-of-file */
+
+/* Read an escape sequence, returning its equivalent as a character,
+ or store 1 in *ignore_ptr if it is backslash-newline. */
+
+static int
+readescape (ignore_ptr)
+ int *ignore_ptr;
+{
+ register int c = getc (finput);
+ register int code;
+ register unsigned count;
+ unsigned firstdig = 0;
+ int nonnull;
+
+ switch (c)
+ {
+ case 'x':
+ if (warn_traditional)
+ warning ("the meaning of `\\x' varies with -traditional");
+
+ if (flag_traditional)
+ return c;
+
+ code = 0;
+ count = 0;
+ nonnull = 0;
+ while (1)
+ {
+ c = getc (finput);
+ if (!(c >= 'a' && c <= 'f')
+ && !(c >= 'A' && c <= 'F')
+ && !(c >= '0' && c <= '9'))
+ {
+ ungetc (c, finput);
+ break;
+ }
+ code *= 16;
+ if (c >= 'a' && c <= 'f')
+ code += c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ code += c - 'A' + 10;
+ if (c >= '0' && c <= '9')
+ code += c - '0';
+ if (code != 0 || count != 0)
+ {
+ if (count == 0)
+ firstdig = code;
+ count++;
+ }
+ nonnull = 1;
+ }
+ if (! nonnull)
+ error ("\\x used with no following hex digits");
+ else if (count == 0)
+ /* Digits are all 0's. Ok. */
+ ;
+ else if ((count - 1) * 4 >= TYPE_PRECISION (integer_type_node)
+ || (count > 1
+ && ((1 << (TYPE_PRECISION (integer_type_node) - (count - 1) * 4))
+ <= firstdig)))
+ pedwarn ("hex escape out of range");
+ return code;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7':
+ code = 0;
+ count = 0;
+ while ((c <= '7') && (c >= '0') && (count++ < 3))
+ {
+ code = (code * 8) + (c - '0');
+ c = getc (finput);
+ }
+ ungetc (c, finput);
+ return code;
+
+ case '\\': case '\'': case '"':
+ return c;
+
+ case '\n':
+ lineno++;
+ *ignore_ptr = 1;
+ return 0;
+
+ case 'n':
+ return TARGET_NEWLINE;
+
+ case 't':
+ return TARGET_TAB;
+
+ case 'r':
+ return TARGET_CR;
+
+ case 'f':
+ return TARGET_FF;
+
+ case 'b':
+ return TARGET_BS;
+
+ case 'a':
+ if (warn_traditional)
+ warning ("the meaning of `\\a' varies with -traditional");
+
+ if (flag_traditional)
+ return c;
+ return TARGET_BELL;
+
+ case 'v':
+#if 0 /* Vertical tab is present in common usage compilers. */
+ if (flag_traditional)
+ return c;
+#endif
+ return TARGET_VT;
+
+ case 'e':
+ case 'E':
+ if (pedantic)
+ pedwarn ("non-ANSI-standard escape sequence, `\\%c'", c);
+ return 033;
+
+ case '?':
+ return c;
+
+ /* `\(', etc, are used at beginning of line to avoid confusing Emacs. */
+ case '(':
+ case '{':
+ case '[':
+ /* `\%' is used to prevent SCCS from getting confused. */
+ case '%':
+ if (pedantic)
+ pedwarn ("non-ANSI escape sequence `\\%c'", c);
+ return c;
+ }
+ if (c >= 040 && c < 0177)
+ pedwarn ("unknown escape sequence `\\%c'", c);
+ else
+ pedwarn ("unknown escape sequence: `\\' followed by char code 0x%x", c);
+ return c;
+}
+
+void
+yyerror (string)
+ char *string;
+{
+ char buf[200];
+
+ strcpy (buf, string);
+
+ /* We can't print string and character constants well
+ because the token_buffer contains the result of processing escapes. */
+ if (end_of_file)
+ strcat (buf, " at end of input");
+ else if (token_buffer[0] == 0)
+ strcat (buf, " at null character");
+ else if (token_buffer[0] == '"')
+ strcat (buf, " before string constant");
+ else if (token_buffer[0] == '\'')
+ strcat (buf, " before character constant");
+ else if (token_buffer[0] < 040 || (unsigned char) token_buffer[0] >= 0177)
+ sprintf (buf + strlen (buf), " before character 0%o",
+ (unsigned char) token_buffer[0]);
+ else
+ strcat (buf, " before `%s'");
+
+ error (buf, token_buffer);
+}
+
+#if 0
+
+struct try_type
+{
+ tree *node_var;
+ char unsigned_flag;
+ char long_flag;
+ char long_long_flag;
+};
+
+struct try_type type_sequence[] =
+{
+ { &integer_type_node, 0, 0, 0},
+ { &unsigned_type_node, 1, 0, 0},
+ { &long_integer_type_node, 0, 1, 0},
+ { &long_unsigned_type_node, 1, 1, 0},
+ { &long_long_integer_type_node, 0, 1, 1},
+ { &long_long_unsigned_type_node, 1, 1, 1}
+};
+#endif /* 0 */
+
+int
+yylex ()
+{
+ register int c;
+ register char *p;
+ register int value;
+ int wide_flag = 0;
+ int objc_flag = 0;
+
+ if (nextchar >= 0)
+ c = nextchar, nextchar = -1;
+ else
+ c = getc (finput);
+
+ /* Effectively do c = skip_white_space (c)
+ but do it faster in the usual cases. */
+ while (1)
+ switch (c)
+ {
+ case ' ':
+ case '\t':
+ case '\f':
+ case '\v':
+ case '\b':
+ c = getc (finput);
+ break;
+
+ case '\r':
+ /* Call skip_white_space so we can warn if appropriate. */
+
+ case '\n':
+ case '/':
+ case '\\':
+ c = skip_white_space (c);
+ default:
+ goto found_nonwhite;
+ }
+ found_nonwhite:
+
+ token_buffer[0] = c;
+ token_buffer[1] = 0;
+
+/* yylloc.first_line = lineno; */
+
+ switch (c)
+ {
+ case EOF:
+ end_of_file = 1;
+ token_buffer[0] = 0;
+ value = ENDFILE;
+ break;
+
+ case '$':
+ if (dollars_in_ident)
+ goto letter;
+ return '$';
+
+ case 'L':
+ /* Capital L may start a wide-string or wide-character constant. */
+ {
+ register int c = getc (finput);
+ if (c == '\'')
+ {
+ wide_flag = 1;
+ goto char_constant;
+ }
+ if (c == '"')
+ {
+ wide_flag = 1;
+ goto string_constant;
+ }
+ ungetc (c, finput);
+ }
+ goto letter;
+
+ case '@':
+ if (!doing_objc_thang)
+ {
+ value = c;
+ break;
+ }
+ else
+ {
+ /* '@' may start a constant string object. */
+ register int c = getc(finput);
+ if (c == '"')
+ {
+ objc_flag = 1;
+ goto string_constant;
+ }
+ ungetc(c, finput);
+ /* Fall through to treat '@' as the start of an indentifier. */
+ }
+
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F': case 'G': case 'H': case 'I': case 'J':
+ case 'K': case 'M': case 'N': case 'O':
+ case 'P': case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X': case 'Y':
+ case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f': case 'g': case 'h': case 'i': case 'j':
+ case 'k': case 'l': case 'm': case 'n': case 'o':
+ case 'p': case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x': case 'y':
+ case 'z':
+ case '_':
+ letter:
+ p = token_buffer;
+ while (isalnum (c) || c == '_' || c == '$' || c == '@')
+ {
+ /* Make sure this char really belongs in an identifier. */
+ if (c == '@' && ! doing_objc_thang)
+ break;
+ if (c == '$' && ! dollars_in_ident)
+ break;
+
+ if (p >= token_buffer + maxtoken)
+ p = extend_token_buffer (p);
+
+ *p++ = c;
+ c = getc (finput);
+ }
+
+ *p = 0;
+ nextchar = c;
+
+ value = IDENTIFIER;
+ yylval.itype = 0;
+
+ /* Try to recognize a keyword. Uses minimum-perfect hash function */
+
+ {
+ register struct resword *ptr;
+
+ if (ptr = is_reserved_word (token_buffer, p - token_buffer))
+ {
+ if (ptr->rid)
+ yylval.ttype = ridpointers[(int) ptr->rid];
+ value = (int) ptr->token;
+
+ /* Only return OBJECTNAME if it is a typedef. */
+ if (doing_objc_thang && value == OBJECTNAME)
+ {
+ lastiddecl = lookup_name(yylval.ttype);
+
+ if (lastiddecl == NULL_TREE
+ || TREE_CODE (lastiddecl) != TYPE_DECL)
+ value = IDENTIFIER;
+ }
+
+ /* Even if we decided to recognize asm, still perhaps warn. */
+ if (pedantic
+ && (value == ASM_KEYWORD || value == TYPEOF
+ || ptr->rid == RID_INLINE)
+ && token_buffer[0] != '_')
+ pedwarn ("ANSI does not permit the keyword `%s'",
+ token_buffer);
+ }
+ }
+
+ /* If we did not find a keyword, look for an identifier
+ (or a typename). */
+
+ if (value == IDENTIFIER)
+ {
+ if (token_buffer[0] == '@')
+ error("invalid identifier `%s'", token_buffer);
+
+ yylval.ttype = get_identifier (token_buffer);
+ lastiddecl = lookup_name (yylval.ttype);
+
+ if (lastiddecl != 0 && TREE_CODE (lastiddecl) == TYPE_DECL)
+ value = TYPENAME;
+ /* A user-invisible read-only initialized variable
+ should be replaced by its value.
+ We handle only strings since that's the only case used in C. */
+ else if (lastiddecl != 0 && TREE_CODE (lastiddecl) == VAR_DECL
+ && DECL_IGNORED_P (lastiddecl)
+ && TREE_READONLY (lastiddecl)
+ && DECL_INITIAL (lastiddecl) != 0
+ && TREE_CODE (DECL_INITIAL (lastiddecl)) == STRING_CST)
+ {
+ tree stringval = DECL_INITIAL (lastiddecl);
+
+ /* Copy the string value so that we won't clobber anything
+ if we put something in the TREE_CHAIN of this one. */
+ yylval.ttype = build_string (TREE_STRING_LENGTH (stringval),
+ TREE_STRING_POINTER (stringval));
+ value = STRING;
+ }
+ else if (doing_objc_thang)
+ {
+ tree objc_interface_decl = is_class_name (yylval.ttype);
+
+ if (objc_interface_decl)
+ {
+ value = CLASSNAME;
+ yylval.ttype = objc_interface_decl;
+ }
+ }
+ }
+
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '.':
+ {
+ int base = 10;
+ int count = 0;
+ int largest_digit = 0;
+ int numdigits = 0;
+ /* for multi-precision arithmetic,
+ we actually store only HOST_BITS_PER_CHAR bits in each part.
+ The number of parts is chosen so as to be sufficient to hold
+ the enough bits to fit into the two HOST_WIDE_INTs that contain
+ the integer value (this is always at least as many bits as are
+ in a target `long long' value, but may be wider). */
+#define TOTAL_PARTS ((HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR) * 2 + 2)
+ int parts[TOTAL_PARTS];
+ int overflow = 0;
+
+ enum anon1 { NOT_FLOAT, AFTER_POINT, TOO_MANY_POINTS} floatflag
+ = NOT_FLOAT;
+
+ for (count = 0; count < TOTAL_PARTS; count++)
+ parts[count] = 0;
+
+ p = token_buffer;
+ *p++ = c;
+
+ if (c == '0')
+ {
+ *p++ = (c = getc (finput));
+ if ((c == 'x') || (c == 'X'))
+ {
+ base = 16;
+ *p++ = (c = getc (finput));
+ }
+ /* Leading 0 forces octal unless the 0 is the only digit. */
+ else if (c >= '0' && c <= '9')
+ {
+ base = 8;
+ numdigits++;
+ }
+ else
+ numdigits++;
+ }
+
+ /* Read all the digits-and-decimal-points. */
+
+ while (c == '.'
+ || (isalnum (c) && c != 'l' && c != 'L'
+ && c != 'u' && c != 'U'
+ && c != 'i' && c != 'I' && c != 'j' && c != 'J'
+ && (floatflag == NOT_FLOAT || ((c != 'f') && (c != 'F')))))
+ {
+ if (c == '.')
+ {
+ if (base == 16)
+ error ("floating constant may not be in radix 16");
+ if (floatflag == TOO_MANY_POINTS)
+ /* We have already emitted an error. Don't need another. */
+ ;
+ else if (floatflag == AFTER_POINT)
+ {
+ error ("malformed floating constant");
+ floatflag = TOO_MANY_POINTS;
+ /* Avoid another error from atof by forcing all characters
+ from here on to be ignored. */
+ p[-1] = '\0';
+ }
+ else
+ floatflag = AFTER_POINT;
+
+ base = 10;
+ *p++ = c = getc (finput);
+ /* Accept '.' as the start of a floating-point number
+ only when it is followed by a digit.
+ Otherwise, unread the following non-digit
+ and use the '.' as a structural token. */
+ if (p == token_buffer + 2 && !isdigit (c))
+ {
+ if (c == '.')
+ {
+ c = getc (finput);
+ if (c == '.')
+ {
+ *p++ = c;
+ *p = 0;
+ return ELLIPSIS;
+ }
+ error ("parse error at `..'");
+ }
+ ungetc (c, finput);
+ token_buffer[1] = 0;
+ value = '.';
+ goto done;
+ }
+ }
+ else
+ {
+ /* It is not a decimal point.
+ It should be a digit (perhaps a hex digit). */
+
+ if (isdigit (c))
+ {
+ c = c - '0';
+ }
+ else if (base <= 10)
+ {
+ if (c == 'e' || c == 'E')
+ {
+ base = 10;
+ floatflag = AFTER_POINT;
+ break; /* start of exponent */
+ }
+ error ("nondigits in number and not hexadecimal");
+ c = 0;
+ }
+ else if (c >= 'a')
+ {
+ c = c - 'a' + 10;
+ }
+ else
+ {
+ c = c - 'A' + 10;
+ }
+ if (c >= largest_digit)
+ largest_digit = c;
+ numdigits++;
+
+ for (count = 0; count < TOTAL_PARTS; count++)
+ {
+ parts[count] *= base;
+ if (count)
+ {
+ parts[count]
+ += (parts[count-1] >> HOST_BITS_PER_CHAR);
+ parts[count-1]
+ &= (1 << HOST_BITS_PER_CHAR) - 1;
+ }
+ else
+ parts[0] += c;
+ }
+
+ /* If the extra highest-order part ever gets anything in it,
+ the number is certainly too big. */
+ if (parts[TOTAL_PARTS - 1] != 0)
+ overflow = 1;
+
+ if (p >= token_buffer + maxtoken - 3)
+ p = extend_token_buffer (p);
+ *p++ = (c = getc (finput));
+ }
+ }
+
+ if (numdigits == 0)
+ error ("numeric constant with no digits");
+
+ if (largest_digit >= base)
+ error ("numeric constant contains digits beyond the radix");
+
+ /* Remove terminating char from the token buffer and delimit the string */
+ *--p = 0;
+
+ if (floatflag != NOT_FLOAT)
+ {
+ tree type = double_type_node;
+ int garbage_chars = 0, exceeds_double = 0;
+ int imag = 0;
+ REAL_VALUE_TYPE value;
+ jmp_buf handler;
+
+ /* Read explicit exponent if any, and put it in tokenbuf. */
+
+ if ((c == 'e') || (c == 'E'))
+ {
+ if (p >= token_buffer + maxtoken - 3)
+ p = extend_token_buffer (p);
+ *p++ = c;
+ c = getc (finput);
+ if ((c == '+') || (c == '-'))
+ {
+ *p++ = c;
+ c = getc (finput);
+ }
+ if (! isdigit (c))
+ error ("floating constant exponent has no digits");
+ while (isdigit (c))
+ {
+ if (p >= token_buffer + maxtoken - 3)
+ p = extend_token_buffer (p);
+ *p++ = c;
+ c = getc (finput);
+ }
+ }
+
+ *p = 0;
+ errno = 0;
+
+ /* Convert string to a double, checking for overflow. */
+ if (setjmp (handler))
+ {
+ error ("floating constant out of range");
+ value = dconst0;
+ }
+ else
+ {
+ int fflag = 0, lflag = 0;
+ /* Copy token_buffer now, while it has just the number
+ and not the suffixes; once we add `f' or `i',
+ REAL_VALUE_ATOF may not work any more. */
+ char *copy = (char *) alloca (p - token_buffer + 1);
+ bcopy (token_buffer, copy, p - token_buffer + 1);
+
+ set_float_handler (handler);
+
+ while (1)
+ {
+ int lose = 0;
+
+ /* Read the suffixes to choose a data type. */
+ switch (c)
+ {
+ case 'f': case 'F':
+ if (fflag)
+ error ("more than one `f' in numeric constant");
+ fflag = 1;
+ break;
+
+ case 'l': case 'L':
+ if (lflag)
+ error ("more than one `l' in numeric constant");
+ lflag = 1;
+ break;
+
+ case 'i': case 'I':
+ if (imag)
+ error ("more than one `i' or `j' in numeric constant");
+ else if (pedantic)
+ pedwarn ("ANSI C forbids imaginary numeric constants");
+ imag = 1;
+ break;
+
+ default:
+ lose = 1;
+ }
+
+ if (lose)
+ break;
+
+ if (p >= token_buffer + maxtoken - 3)
+ p = extend_token_buffer (p);
+ *p++ = c;
+ *p = 0;
+ c = getc (finput);
+ }
+
+ /* The second argument, machine_mode, of REAL_VALUE_ATOF
+ tells the desired precision of the binary result
+ of decimal-to-binary conversion. */
+
+ if (fflag)
+ {
+ if (lflag)
+ error ("both `f' and `l' in floating constant");
+
+ type = float_type_node;
+ value = REAL_VALUE_ATOF (copy, TYPE_MODE (type));
+ if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+ && REAL_VALUE_ISINF (value) && pedantic)
+ pedwarn ("floating point number exceeds range of `float'");
+ }
+ else if (lflag)
+ {
+ type = long_double_type_node;
+ value = REAL_VALUE_ATOF (copy, TYPE_MODE (type));
+ if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+ && REAL_VALUE_ISINF (value) && pedantic)
+ pedwarn ("floating point number exceeds range of `long double'");
+ }
+ else
+ {
+ value = REAL_VALUE_ATOF (copy, TYPE_MODE (type));
+ if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+ && REAL_VALUE_ISINF (value) && pedantic)
+ pedwarn ("floating point number exceeds range of `double'");
+ }
+
+ set_float_handler (NULL_PTR);
+ }
+#ifdef ERANGE
+ if (errno == ERANGE && !flag_traditional && pedantic)
+ {
+ /* ERANGE is also reported for underflow,
+ so test the value to distinguish overflow from that. */
+ if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+ && (REAL_VALUES_LESS (dconst1, value)
+ || REAL_VALUES_LESS (value, dconstm1)))
+ {
+ pedwarn ("floating point number exceeds range of `double'");
+ exceeds_double = 1;
+ }
+ }
+#endif
+ garbage_chars = 0;
+ while (isalnum (c) || c == '.' || c == '_'
+ || (!flag_traditional && (c == '+' || c == '-')
+ && (p[-1] == 'e' || p[-1] == 'E')))
+ {
+ if (p >= token_buffer + maxtoken - 3)
+ p = extend_token_buffer (p);
+ *p++ = c;
+ c = getc (finput);
+ garbage_chars++;
+ }
+ if (garbage_chars > 0)
+ error ("garbage at end of number");
+
+ /* If the result is not a number, assume it must have been
+ due to some error message above, so silently convert
+ it to a zero. */
+ if (REAL_VALUE_ISNAN (value))
+ value = dconst0;
+
+ /* Create a node with determined type and value. */
+ if (imag)
+ yylval.ttype = build_complex (convert (type, integer_zero_node),
+ build_real (type, value));
+ else
+ yylval.ttype = build_real (type, value);
+
+ ungetc (c, finput);
+ *p = 0;
+ }
+ else
+ {
+ tree traditional_type, ansi_type, type;
+ HOST_WIDE_INT high, low;
+ int spec_unsigned = 0;
+ int spec_long = 0;
+ int spec_long_long = 0;
+ int spec_imag = 0;
+ int bytes, warn, i;
+
+ while (1)
+ {
+ if (c == 'u' || c == 'U')
+ {
+ if (spec_unsigned)
+ error ("two `u's in integer constant");
+ spec_unsigned = 1;
+ }
+ else if (c == 'l' || c == 'L')
+ {
+ if (spec_long)
+ {
+ if (spec_long_long)
+ error ("three `l's in integer constant");
+ else if (pedantic)
+ pedwarn ("ANSI C forbids long long integer constants");
+ spec_long_long = 1;
+ }
+ spec_long = 1;
+ }
+ else if (c == 'i' || c == 'j' || c == 'I' || c == 'J')
+ {
+ if (spec_imag)
+ error ("more than one `i' or `j' in numeric constant");
+ else if (pedantic)
+ pedwarn ("ANSI C forbids imaginary numeric constants");
+ spec_imag = 1;
+ }
+ else
+ {
+ if (isalnum (c) || c == '.' || c == '_'
+ || (!flag_traditional && (c == '+' || c == '-')
+ && (p[-1] == 'e' || p[-1] == 'E')))
+ {
+ error ("garbage at end of number");
+ while (isalnum (c) || c == '.' || c == '_'
+ || (!flag_traditional && (c == '+' || c == '-')
+ && (p[-1] == 'e' || p[-1] == 'E')))
+ {
+ if (p >= token_buffer + maxtoken - 3)
+ p = extend_token_buffer (p);
+ *p++ = c;
+ c = getc (finput);
+ }
+ }
+ break;
+ }
+ if (p >= token_buffer + maxtoken - 3)
+ p = extend_token_buffer (p);
+ *p++ = c;
+ c = getc (finput);
+ }
+
+ ungetc (c, finput);
+
+ /* If the constant is not long long and it won't fit in an
+ unsigned long, or if the constant is long long and won't fit
+ in an unsigned long long, then warn that the constant is out
+ of range. */
+
+ /* ??? This assumes that long long and long integer types are
+ a multiple of 8 bits. This better than the original code
+ though which assumed that long was exactly 32 bits and long
+ long was exactly 64 bits. */
+
+ if (spec_long_long)
+ bytes = TYPE_PRECISION (long_long_integer_type_node) / 8;
+ else
+ bytes = TYPE_PRECISION (long_integer_type_node) / 8;
+
+ warn = overflow;
+ for (i = bytes; i < TOTAL_PARTS; i++)
+ if (parts[i])
+ warn = 1;
+ if (warn)
+ pedwarn ("integer constant out of range");
+
+ /* This is simplified by the fact that our constant
+ is always positive. */
+
+ high = low = 0;
+
+ for (i = 0; i < HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR; i++)
+ {
+ high |= ((HOST_WIDE_INT) parts[i + (HOST_BITS_PER_WIDE_INT
+ / HOST_BITS_PER_CHAR)]
+ << (i * HOST_BITS_PER_CHAR));
+ low |= (HOST_WIDE_INT) parts[i] << (i * HOST_BITS_PER_CHAR);
+ }
+
+ yylval.ttype = build_int_2 (low, high);
+ TREE_TYPE (yylval.ttype) = long_long_unsigned_type_node;
+
+ /* If warn_traditional, calculate both the ANSI type and the
+ traditional type, then see if they disagree.
+ Otherwise, calculate only the type for the dialect in use. */
+ if (warn_traditional || flag_traditional)
+ {
+ /* Calculate the traditional type. */
+ /* Traditionally, any constant is signed;
+ but if unsigned is specified explicitly, obey that.
+ Use the smallest size with the right number of bits,
+ except for one special case with decimal constants. */
+ if (! spec_long && base != 10
+ && int_fits_type_p (yylval.ttype, unsigned_type_node))
+ traditional_type = (spec_unsigned ? unsigned_type_node
+ : integer_type_node);
+ /* A decimal constant must be long
+ if it does not fit in type int.
+ I think this is independent of whether
+ the constant is signed. */
+ else if (! spec_long && base == 10
+ && int_fits_type_p (yylval.ttype, integer_type_node))
+ traditional_type = (spec_unsigned ? unsigned_type_node
+ : integer_type_node);
+ else if (! spec_long_long)
+ traditional_type = (spec_unsigned ? long_unsigned_type_node
+ : long_integer_type_node);
+ else
+ traditional_type = (spec_unsigned
+ ? long_long_unsigned_type_node
+ : long_long_integer_type_node);
+ }
+ if (warn_traditional || ! flag_traditional)
+ {
+ /* Calculate the ANSI type. */
+ if (! spec_long && ! spec_unsigned
+ && int_fits_type_p (yylval.ttype, integer_type_node))
+ ansi_type = integer_type_node;
+ else if (! spec_long && (base != 10 || spec_unsigned)
+ && int_fits_type_p (yylval.ttype, unsigned_type_node))
+ ansi_type = unsigned_type_node;
+ else if (! spec_unsigned && !spec_long_long
+ && int_fits_type_p (yylval.ttype, long_integer_type_node))
+ ansi_type = long_integer_type_node;
+ else if (! spec_long_long)
+ ansi_type = long_unsigned_type_node;
+ else if (! spec_unsigned
+ /* Verify value does not overflow into sign bit. */
+ && TREE_INT_CST_HIGH (yylval.ttype) >= 0
+ && int_fits_type_p (yylval.ttype,
+ long_long_integer_type_node))
+ ansi_type = long_long_integer_type_node;
+ else
+ ansi_type = long_long_unsigned_type_node;
+ }
+
+ type = flag_traditional ? traditional_type : ansi_type;
+
+ if (warn_traditional && traditional_type != ansi_type)
+ {
+ if (TYPE_PRECISION (traditional_type)
+ != TYPE_PRECISION (ansi_type))
+ warning ("width of integer constant changes with -traditional");
+ else if (TREE_UNSIGNED (traditional_type)
+ != TREE_UNSIGNED (ansi_type))
+ warning ("integer constant is unsigned in ANSI C, signed with -traditional");
+ else
+ warning ("width of integer constant may change on other systems with -traditional");
+ }
+
+ if (!flag_traditional && !int_fits_type_p (yylval.ttype, type)
+ && !warn)
+ pedwarn ("integer constant out of range");
+
+ if (base == 10 && ! spec_unsigned && TREE_UNSIGNED (type))
+ warning ("decimal constant is so large that it is unsigned");
+
+ if (spec_imag)
+ {
+ if (TYPE_PRECISION (type)
+ <= TYPE_PRECISION (integer_type_node))
+ yylval.ttype
+ = build_complex (integer_zero_node,
+ convert (integer_type_node, yylval.ttype));
+ else
+ error ("complex integer constant is too wide for `complex int'");
+ }
+ else if (flag_traditional && !int_fits_type_p (yylval.ttype, type))
+ /* The traditional constant 0x80000000 is signed
+ but doesn't fit in the range of int.
+ This will change it to -0x80000000, which does fit. */
+ {
+ TREE_TYPE (yylval.ttype) = unsigned_type (type);
+ yylval.ttype = convert (type, yylval.ttype);
+ TREE_OVERFLOW (yylval.ttype)
+ = TREE_CONSTANT_OVERFLOW (yylval.ttype) = 0;
+ }
+ else
+ TREE_TYPE (yylval.ttype) = type;
+
+ *p = 0;
+ }
+
+ value = CONSTANT; break;
+ }
+
+ case '\'':
+ char_constant:
+ {
+ register int result = 0;
+ register int num_chars = 0;
+ unsigned width = TYPE_PRECISION (char_type_node);
+ int max_chars;
+
+ if (wide_flag)
+ {
+ width = WCHAR_TYPE_SIZE;
+#ifdef MULTIBYTE_CHARS
+ max_chars = MB_CUR_MAX;
+#else
+ max_chars = 1;
+#endif
+ }
+ else
+ max_chars = TYPE_PRECISION (integer_type_node) / width;
+
+ while (1)
+ {
+ tryagain:
+
+ c = getc (finput);
+
+ if (c == '\'' || c == EOF)
+ break;
+
+ if (c == '\\')
+ {
+ int ignore = 0;
+ c = readescape (&ignore);
+ if (ignore)
+ goto tryagain;
+ if (width < HOST_BITS_PER_INT
+ && (unsigned) c >= (1 << width))
+ pedwarn ("escape sequence out of range for character");
+#ifdef MAP_CHARACTER
+ if (isprint (c))
+ c = MAP_CHARACTER (c);
+#endif
+ }
+ else if (c == '\n')
+ {
+ if (pedantic)
+ pedwarn ("ANSI C forbids newline in character constant");
+ lineno++;
+ }
+#ifdef MAP_CHARACTER
+ else
+ c = MAP_CHARACTER (c);
+#endif
+
+ num_chars++;
+ if (num_chars > maxtoken - 4)
+ extend_token_buffer (token_buffer);
+
+ token_buffer[num_chars] = c;
+
+ /* Merge character into result; ignore excess chars. */
+ if (num_chars < max_chars + 1)
+ {
+ if (width < HOST_BITS_PER_INT)
+ result = (result << width) | (c & ((1 << width) - 1));
+ else
+ result = c;
+ }
+ }
+
+ token_buffer[num_chars + 1] = '\'';
+ token_buffer[num_chars + 2] = 0;
+
+ if (c != '\'')
+ error ("malformatted character constant");
+ else if (num_chars == 0)
+ error ("empty character constant");
+ else if (num_chars > max_chars)
+ {
+ num_chars = max_chars;
+ error ("character constant too long");
+ }
+ else if (num_chars != 1 && ! flag_traditional)
+ warning ("multi-character character constant");
+
+ /* If char type is signed, sign-extend the constant. */
+ if (! wide_flag)
+ {
+ int num_bits = num_chars * width;
+ if (num_bits == 0)
+ /* We already got an error; avoid invalid shift. */
+ yylval.ttype = build_int_2 (0, 0);
+ else if (TREE_UNSIGNED (char_type_node)
+ || ((result >> (num_bits - 1)) & 1) == 0)
+ yylval.ttype
+ = build_int_2 (result & ((unsigned HOST_WIDE_INT) ~0
+ >> (HOST_BITS_PER_WIDE_INT - num_bits)),
+ 0);
+ else
+ yylval.ttype
+ = build_int_2 (result | ~((unsigned HOST_WIDE_INT) ~0
+ >> (HOST_BITS_PER_WIDE_INT - num_bits)),
+ -1);
+ TREE_TYPE (yylval.ttype) = integer_type_node;
+ }
+ else
+ {
+#ifdef MULTIBYTE_CHARS
+ /* Set the initial shift state and convert the next sequence. */
+ result = 0;
+ /* In all locales L'\0' is zero and mbtowc will return zero,
+ so don't use it. */
+ if (num_chars > 1
+ || (num_chars == 1 && token_buffer[1] != '\0'))
+ {
+ wchar_t wc;
+ (void) mbtowc (NULL_PTR, NULL_PTR, 0);
+ if (mbtowc (& wc, token_buffer + 1, num_chars) == num_chars)
+ result = wc;
+ else
+ warning ("Ignoring invalid multibyte character");
+ }
+#endif
+ yylval.ttype = build_int_2 (result, 0);
+ TREE_TYPE (yylval.ttype) = wchar_type_node;
+ }
+
+ value = CONSTANT;
+ break;
+ }
+
+ case '"':
+ string_constant:
+ {
+ c = getc (finput);
+ p = token_buffer + 1;
+
+ while (c != '"' && c >= 0)
+ {
+ if (c == '\\')
+ {
+ int ignore = 0;
+ c = readescape (&ignore);
+ if (ignore)
+ goto skipnewline;
+ if (!wide_flag
+ && TYPE_PRECISION (char_type_node) < HOST_BITS_PER_INT
+ && c >= (1 << TYPE_PRECISION (char_type_node)))
+ pedwarn ("escape sequence out of range for character");
+ }
+ else if (c == '\n')
+ {
+ if (pedantic)
+ pedwarn ("ANSI C forbids newline in string constant");
+ lineno++;
+ }
+
+ if (p == token_buffer + maxtoken)
+ p = extend_token_buffer (p);
+ *p++ = c;
+
+ skipnewline:
+ c = getc (finput);
+ }
+ *p = 0;
+
+ /* We have read the entire constant.
+ Construct a STRING_CST for the result. */
+
+ if (wide_flag)
+ {
+ /* If this is a L"..." wide-string, convert the multibyte string
+ to a wide character string. */
+ char *widep = (char *) alloca ((p - token_buffer) * WCHAR_BYTES);
+ int len;
+
+#ifdef MULTIBYTE_CHARS
+ len = mbstowcs ((wchar_t *) widep, token_buffer + 1, p - token_buffer);
+ if (len < 0 || len >= (p - token_buffer))
+ {
+ warning ("Ignoring invalid multibyte string");
+ len = 0;
+ }
+ bzero (widep + (len * WCHAR_BYTES), WCHAR_BYTES);
+#else
+ {
+ union { long l; char c[sizeof (long)]; } u;
+ int big_endian;
+ char *wp, *cp;
+
+ /* Determine whether host is little or big endian. */
+ u.l = 1;
+ big_endian = u.c[sizeof (long) - 1];
+ wp = widep + (big_endian ? WCHAR_BYTES - 1 : 0);
+
+ bzero (widep, (p - token_buffer) * WCHAR_BYTES);
+ for (cp = token_buffer + 1; cp < p; cp++)
+ *wp = *cp, wp += WCHAR_BYTES;
+ len = p - token_buffer - 1;
+ }
+#endif
+ yylval.ttype = build_string ((len + 1) * WCHAR_BYTES, widep);
+ TREE_TYPE (yylval.ttype) = wchar_array_type_node;
+ value = STRING;
+ }
+ else if (objc_flag)
+ {
+ extern tree build_objc_string();
+ /* Return an Objective-C @"..." constant string object. */
+ yylval.ttype = build_objc_string (p - token_buffer,
+ token_buffer + 1);
+ TREE_TYPE (yylval.ttype) = char_array_type_node;
+ value = OBJC_STRING;
+ }
+ else
+ {
+ yylval.ttype = build_string (p - token_buffer, token_buffer + 1);
+ TREE_TYPE (yylval.ttype) = char_array_type_node;
+ value = STRING;
+ }
+
+ *p++ = '"';
+ *p = 0;
+
+ break;
+ }
+
+ case '+':
+ case '-':
+ case '&':
+ case '|':
+ case '<':
+ case '>':
+ case '*':
+ case '/':
+ case '%':
+ case '^':
+ case '!':
+ case '=':
+ {
+ register int c1;
+
+ combine:
+
+ switch (c)
+ {
+ case '+':
+ yylval.code = PLUS_EXPR; break;
+ case '-':
+ yylval.code = MINUS_EXPR; break;
+ case '&':
+ yylval.code = BIT_AND_EXPR; break;
+ case '|':
+ yylval.code = BIT_IOR_EXPR; break;
+ case '*':
+ yylval.code = MULT_EXPR; break;
+ case '/':
+ yylval.code = TRUNC_DIV_EXPR; break;
+ case '%':
+ yylval.code = TRUNC_MOD_EXPR; break;
+ case '^':
+ yylval.code = BIT_XOR_EXPR; break;
+ case LSHIFT:
+ yylval.code = LSHIFT_EXPR; break;
+ case RSHIFT:
+ yylval.code = RSHIFT_EXPR; break;
+ case '<':
+ yylval.code = LT_EXPR; break;
+ case '>':
+ yylval.code = GT_EXPR; break;
+ }
+
+ token_buffer[1] = c1 = getc (finput);
+ token_buffer[2] = 0;
+
+ if (c1 == '=')
+ {
+ switch (c)
+ {
+ case '<':
+ value = ARITHCOMPARE; yylval.code = LE_EXPR; goto done;
+ case '>':
+ value = ARITHCOMPARE; yylval.code = GE_EXPR; goto done;
+ case '!':
+ value = EQCOMPARE; yylval.code = NE_EXPR; goto done;
+ case '=':
+ value = EQCOMPARE; yylval.code = EQ_EXPR; goto done;
+ }
+ value = ASSIGN; goto done;
+ }
+ else if (c == c1)
+ switch (c)
+ {
+ case '+':
+ value = PLUSPLUS; goto done;
+ case '-':
+ value = MINUSMINUS; goto done;
+ case '&':
+ value = ANDAND; goto done;
+ case '|':
+ value = OROR; goto done;
+ case '<':
+ c = LSHIFT;
+ goto combine;
+ case '>':
+ c = RSHIFT;
+ goto combine;
+ }
+ else if ((c == '-') && (c1 == '>'))
+ { value = POINTSAT; goto done; }
+ ungetc (c1, finput);
+ token_buffer[1] = 0;
+
+ if ((c == '<') || (c == '>'))
+ value = ARITHCOMPARE;
+ else value = c;
+ goto done;
+ }
+
+ case 0:
+ /* Don't make yyparse think this is eof. */
+ value = 1;
+ break;
+
+ default:
+ value = c;
+ }
+
+done:
+/* yylloc.last_line = lineno; */
+
+ return value;
+}
+
+/* Sets the value of the 'yydebug' variable to VALUE.
+ This is a function so we don't have to have YYDEBUG defined
+ in order to build the compiler. */
+
+void
+set_yydebug (value)
+ int value;
+{
+#if YYDEBUG != 0
+ yydebug = value;
+#else
+ warning ("YYDEBUG not defined.");
+#endif
+}
diff --git a/gnu/usr.bin/cc/cc1/c-parse.c b/gnu/usr.bin/cc/cc1/c-parse.c
new file mode 100644
index 0000000..5120144
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1/c-parse.c
@@ -0,0 +1,3530 @@
+
+/* A Bison parser, made from c-parse.y with Bison version GNU Bison version 1.22
+ */
+
+#define YYBISON 1 /* Identify Bison output. */
+
+#define IDENTIFIER 258
+#define TYPENAME 259
+#define SCSPEC 260
+#define TYPESPEC 261
+#define TYPE_QUAL 262
+#define CONSTANT 263
+#define STRING 264
+#define ELLIPSIS 265
+#define SIZEOF 266
+#define ENUM 267
+#define STRUCT 268
+#define UNION 269
+#define IF 270
+#define ELSE 271
+#define WHILE 272
+#define DO 273
+#define FOR 274
+#define SWITCH 275
+#define CASE 276
+#define DEFAULT 277
+#define BREAK 278
+#define CONTINUE 279
+#define RETURN 280
+#define GOTO 281
+#define ASM_KEYWORD 282
+#define TYPEOF 283
+#define ALIGNOF 284
+#define ALIGN 285
+#define ATTRIBUTE 286
+#define EXTENSION 287
+#define LABEL 288
+#define REALPART 289
+#define IMAGPART 290
+#define ASSIGN 291
+#define OROR 292
+#define ANDAND 293
+#define EQCOMPARE 294
+#define ARITHCOMPARE 295
+#define LSHIFT 296
+#define RSHIFT 297
+#define UNARY 298
+#define PLUSPLUS 299
+#define MINUSMINUS 300
+#define HYPERUNARY 301
+#define POINTSAT 302
+#define INTERFACE 303
+#define IMPLEMENTATION 304
+#define END 305
+#define SELECTOR 306
+#define DEFS 307
+#define ENCODE 308
+#define CLASSNAME 309
+#define PUBLIC 310
+#define PRIVATE 311
+#define PROTECTED 312
+#define PROTOCOL 313
+#define OBJECTNAME 314
+#define CLASS 315
+#define ALIAS 316
+#define OBJC_STRING 317
+
+#line 45 "c-parse.y"
+
+#include <stdio.h>
+#include <errno.h>
+#include <setjmp.h>
+
+#include "config.h"
+#include "tree.h"
+#include "input.h"
+#include "c-lex.h"
+#include "c-tree.h"
+#include "flags.h"
+
+#ifdef MULTIBYTE_CHARS
+#include <stdlib.h>
+#include <locale.h>
+#endif
+
+
+/* Since parsers are distinct for each language, put the language string
+ definition here. */
+char *language_string = "GNU C";
+
+#ifndef errno
+extern int errno;
+#endif
+
+void yyerror ();
+
+/* Like YYERROR but do call yyerror. */
+#define YYERROR1 { yyerror ("syntax error"); YYERROR; }
+
+/* Cause the `yydebug' variable to be defined. */
+#define YYDEBUG 1
+
+#line 82 "c-parse.y"
+typedef union {long itype; tree ttype; enum tree_code code;
+ char *filename; int lineno; } YYSTYPE;
+#line 194 "c-parse.y"
+
+/* Number of statements (loosely speaking) seen so far. */
+static int stmt_count;
+
+/* Input file and line number of the end of the body of last simple_if;
+ used by the stmt-rule immediately after simple_if returns. */
+static char *if_stmt_file;
+static int if_stmt_line;
+
+/* List of types and structure classes of the current declaration. */
+static tree current_declspecs;
+
+/* Stack of saved values of current_declspecs. */
+static tree declspec_stack;
+
+/* 1 if we explained undeclared var errors. */
+static int undeclared_variable_notice;
+
+
+/* Tell yyparse how to print a token's value, if yydebug is set. */
+
+#define YYPRINT(FILE,YYCHAR,YYLVAL) yyprint(FILE,YYCHAR,YYLVAL)
+extern void yyprint ();
+
+#ifndef YYLTYPE
+typedef
+ struct yyltype
+ {
+ int timestamp;
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+ char *text;
+ }
+ yyltype;
+
+#define YYLTYPE yyltype
+#endif
+
+#include <stdio.h>
+
+#ifndef __cplusplus
+#ifndef __STDC__
+#define const
+#endif
+#endif
+
+
+
+#define YYFINAL 626
+#define YYFLAG -32768
+#define YYNTBASE 85
+
+#define YYTRANSLATE(x) ((unsigned)(x) <= 317 ? yytranslate[x] : 225)
+
+static const char yytranslate[] = { 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 81, 2, 2, 2, 53, 44, 2, 60,
+ 77, 51, 49, 82, 50, 59, 52, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 39, 78, 2,
+ 37, 2, 38, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 61, 2, 84, 43, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 83, 42, 79, 80, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 1, 2, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 40, 41, 45, 46, 47, 48, 54, 55, 56,
+ 57, 58, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76
+};
+
+#if YYDEBUG != 0
+static const short yyprhs[] = { 0,
+ 0, 1, 3, 4, 7, 8, 12, 14, 16, 22,
+ 26, 31, 36, 39, 42, 45, 48, 50, 51, 52,
+ 60, 65, 66, 67, 75, 80, 81, 82, 89, 93,
+ 95, 97, 99, 101, 103, 105, 107, 109, 111, 113,
+ 114, 116, 118, 122, 124, 127, 128, 132, 135, 138,
+ 141, 146, 149, 154, 157, 160, 162, 167, 168, 176,
+ 178, 182, 186, 190, 194, 198, 202, 206, 210, 214,
+ 218, 222, 226, 230, 234, 240, 244, 248, 250, 252,
+ 254, 258, 262, 263, 268, 273, 278, 282, 286, 289,
+ 292, 294, 297, 298, 300, 303, 307, 309, 311, 314,
+ 317, 322, 327, 330, 333, 337, 339, 341, 344, 347,
+ 348, 353, 358, 362, 366, 369, 372, 375, 379, 380,
+ 383, 386, 388, 390, 393, 396, 399, 403, 404, 407,
+ 409, 411, 413, 418, 423, 425, 427, 429, 431, 435,
+ 437, 441, 442, 447, 448, 455, 459, 460, 467, 471,
+ 472, 474, 476, 479, 486, 488, 492, 493, 495, 500,
+ 507, 512, 514, 516, 518, 520, 522, 523, 528, 530,
+ 531, 534, 536, 540, 542, 543, 548, 550, 551, 560,
+ 561, 568, 569, 575, 576, 581, 582, 588, 589, 593,
+ 594, 598, 600, 602, 606, 610, 615, 619, 623, 625,
+ 629, 634, 638, 642, 644, 648, 652, 656, 661, 665,
+ 667, 668, 675, 680, 683, 684, 691, 696, 699, 700,
+ 708, 709, 716, 719, 720, 722, 723, 725, 727, 730,
+ 731, 735, 738, 742, 744, 748, 750, 752, 754, 758,
+ 763, 770, 776, 778, 782, 784, 786, 790, 793, 796,
+ 797, 799, 801, 804, 805, 808, 812, 816, 819, 823,
+ 828, 832, 835, 839, 842, 844, 847, 850, 851, 853,
+ 856, 857, 858, 860, 862, 865, 869, 871, 874, 877,
+ 884, 890, 896, 899, 902, 907, 908, 913, 914, 915,
+ 919, 924, 928, 930, 932, 934, 936, 939, 940, 945,
+ 947, 951, 952, 953, 961, 967, 970, 971, 972, 973,
+ 986, 987, 994, 997, 1000, 1003, 1007, 1014, 1023, 1034,
+ 1047, 1051, 1056, 1058, 1060, 1061, 1068, 1072, 1078, 1081,
+ 1084, 1085, 1087, 1088, 1090, 1091, 1093, 1095, 1099, 1104,
+ 1106, 1110, 1111, 1114, 1117, 1118, 1123, 1126, 1127, 1129,
+ 1131, 1135, 1137, 1141, 1144, 1147, 1150, 1153, 1156, 1157,
+ 1160, 1162, 1165, 1167, 1171, 1173
+};
+
+static const short yyrhs[] = { -1,
+ 86, 0, 0, 87, 89, 0, 0, 86, 88, 89,
+ 0, 91, 0, 90, 0, 27, 60, 100, 77, 78,
+ 0, 117, 127, 78, 0, 121, 117, 127, 78, 0,
+ 119, 117, 126, 78, 0, 121, 78, 0, 119, 78,
+ 0, 1, 78, 0, 1, 79, 0, 78, 0, 0,
+ 0, 119, 117, 154, 92, 111, 93, 184, 0, 119,
+ 117, 154, 1, 0, 0, 0, 121, 117, 157, 94,
+ 111, 95, 184, 0, 121, 117, 157, 1, 0, 0,
+ 0, 117, 157, 96, 111, 97, 184, 0, 117, 157,
+ 1, 0, 3, 0, 4, 0, 44, 0, 50, 0,
+ 49, 0, 55, 0, 56, 0, 80, 0, 81, 0,
+ 102, 0, 0, 102, 0, 107, 0, 102, 82, 107,
+ 0, 108, 0, 51, 105, 0, 0, 32, 104, 105,
+ 0, 99, 105, 0, 41, 98, 0, 11, 103, 0,
+ 11, 60, 172, 77, 0, 29, 103, 0, 29, 60,
+ 172, 77, 0, 34, 105, 0, 35, 105, 0, 103,
+ 0, 60, 172, 77, 105, 0, 0, 60, 172, 77,
+ 83, 106, 141, 79, 0, 105, 0, 107, 49, 107,
+ 0, 107, 50, 107, 0, 107, 51, 107, 0, 107,
+ 52, 107, 0, 107, 53, 107, 0, 107, 47, 107,
+ 0, 107, 48, 107, 0, 107, 46, 107, 0, 107,
+ 45, 107, 0, 107, 44, 107, 0, 107, 42, 107,
+ 0, 107, 43, 107, 0, 107, 41, 107, 0, 107,
+ 40, 107, 0, 107, 38, 208, 39, 107, 0, 107,
+ 37, 107, 0, 107, 36, 107, 0, 3, 0, 8,
+ 0, 110, 0, 60, 100, 77, 0, 60, 1, 77,
+ 0, 0, 60, 109, 185, 77, 0, 108, 60, 101,
+ 77, 0, 108, 61, 100, 84, 0, 108, 59, 98,
+ 0, 108, 58, 98, 0, 108, 55, 0, 108, 56,
+ 0, 9, 0, 110, 9, 0, 0, 113, 0, 113,
+ 10, 0, 190, 191, 114, 0, 112, 0, 179, 0,
+ 113, 112, 0, 112, 179, 0, 119, 117, 126, 78,
+ 0, 121, 117, 127, 78, 0, 119, 78, 0, 121,
+ 78, 0, 190, 191, 118, 0, 115, 0, 179, 0,
+ 116, 115, 0, 115, 179, 0, 0, 119, 117, 126,
+ 78, 0, 121, 117, 127, 78, 0, 119, 117, 150,
+ 0, 121, 117, 152, 0, 119, 78, 0, 121, 78,
+ 0, 124, 120, 0, 121, 124, 120, 0, 0, 120,
+ 125, 0, 120, 5, 0, 7, 0, 5, 0, 121,
+ 7, 0, 121, 5, 0, 124, 123, 0, 174, 124,
+ 123, 0, 0, 123, 125, 0, 6, 0, 158, 0,
+ 4, 0, 28, 60, 100, 77, 0, 28, 60, 172,
+ 77, 0, 6, 0, 7, 0, 158, 0, 129, 0,
+ 126, 82, 129, 0, 131, 0, 127, 82, 129, 0,
+ 0, 27, 60, 110, 77, 0, 0, 154, 128, 133,
+ 37, 130, 139, 0, 154, 128, 133, 0, 0, 157,
+ 128, 133, 37, 132, 139, 0, 157, 128, 133, 0,
+ 0, 134, 0, 135, 0, 134, 135, 0, 31, 60,
+ 60, 136, 77, 77, 0, 137, 0, 136, 82, 137,
+ 0, 0, 138, 0, 138, 60, 3, 77, 0, 138,
+ 60, 3, 82, 102, 77, 0, 138, 60, 102, 77,
+ 0, 98, 0, 5, 0, 6, 0, 7, 0, 107,
+ 0, 0, 83, 140, 141, 79, 0, 1, 0, 0,
+ 142, 163, 0, 143, 0, 142, 82, 143, 0, 107,
+ 0, 0, 83, 144, 141, 79, 0, 1, 0, 0,
+ 61, 107, 10, 107, 84, 37, 145, 143, 0, 0,
+ 61, 107, 84, 37, 146, 143, 0, 0, 61, 107,
+ 84, 147, 143, 0, 0, 98, 39, 148, 143, 0,
+ 0, 59, 98, 37, 149, 143, 0, 0, 154, 151,
+ 185, 0, 0, 157, 153, 185, 0, 155, 0, 157,
+ 0, 60, 155, 77, 0, 155, 60, 220, 0, 155,
+ 61, 100, 84, 0, 155, 61, 84, 0, 51, 175,
+ 155, 0, 4, 0, 156, 60, 220, 0, 156, 61,
+ 100, 84, 0, 156, 61, 84, 0, 51, 175, 156,
+ 0, 4, 0, 157, 60, 220, 0, 60, 157, 77,
+ 0, 51, 175, 157, 0, 157, 61, 100, 84, 0,
+ 157, 61, 84, 0, 3, 0, 0, 13, 98, 83,
+ 159, 165, 79, 0, 13, 83, 165, 79, 0, 13,
+ 98, 0, 0, 14, 98, 83, 160, 165, 79, 0,
+ 14, 83, 165, 79, 0, 14, 98, 0, 0, 12,
+ 98, 83, 161, 170, 164, 79, 0, 0, 12, 83,
+ 162, 170, 164, 79, 0, 12, 98, 0, 0, 82,
+ 0, 0, 82, 0, 166, 0, 166, 167, 0, 0,
+ 166, 167, 78, 0, 166, 78, 0, 122, 117, 168,
+ 0, 122, 0, 174, 117, 168, 0, 174, 0, 1,
+ 0, 169, 0, 168, 82, 169, 0, 190, 191, 154,
+ 133, 0, 190, 191, 154, 39, 107, 133, 0, 190,
+ 191, 39, 107, 133, 0, 171, 0, 170, 82, 171,
+ 0, 1, 0, 98, 0, 98, 37, 107, 0, 122,
+ 173, 0, 174, 173, 0, 0, 176, 0, 7, 0,
+ 174, 7, 0, 0, 175, 7, 0, 60, 176, 77,
+ 0, 51, 175, 176, 0, 51, 175, 0, 176, 60,
+ 213, 0, 176, 61, 100, 84, 0, 176, 61, 84,
+ 0, 60, 213, 0, 61, 100, 84, 0, 61, 84,
+ 0, 193, 0, 177, 193, 0, 177, 179, 0, 0,
+ 177, 0, 1, 78, 0, 0, 0, 182, 0, 183,
+ 0, 182, 183, 0, 33, 224, 78, 0, 185, 0,
+ 1, 185, 0, 83, 79, 0, 83, 180, 181, 116,
+ 178, 79, 0, 83, 180, 181, 1, 79, 0, 83,
+ 180, 181, 177, 79, 0, 187, 192, 0, 187, 1,
+ 0, 15, 60, 100, 77, 0, 0, 18, 189, 192,
+ 17, 0, 0, 0, 190, 191, 195, 0, 190, 191,
+ 206, 192, 0, 190, 191, 194, 0, 195, 0, 206,
+ 0, 185, 0, 203, 0, 100, 78, 0, 0, 186,
+ 16, 196, 192, 0, 186, 0, 186, 16, 1, 0,
+ 0, 0, 17, 197, 60, 100, 77, 198, 192, 0,
+ 188, 60, 100, 77, 78, 0, 188, 1, 0, 0,
+ 0, 0, 19, 60, 208, 78, 199, 208, 78, 200,
+ 208, 77, 201, 192, 0, 0, 20, 60, 100, 77,
+ 202, 192, 0, 23, 78, 0, 24, 78, 0, 25,
+ 78, 0, 25, 100, 78, 0, 27, 207, 60, 100,
+ 77, 78, 0, 27, 207, 60, 100, 39, 209, 77,
+ 78, 0, 27, 207, 60, 100, 39, 209, 39, 209,
+ 77, 78, 0, 27, 207, 60, 100, 39, 209, 39,
+ 209, 39, 212, 77, 78, 0, 26, 98, 78, 0,
+ 26, 51, 100, 78, 0, 78, 0, 204, 0, 0,
+ 19, 60, 108, 77, 205, 192, 0, 21, 107, 39,
+ 0, 21, 107, 10, 107, 39, 0, 22, 39, 0,
+ 98, 39, 0, 0, 7, 0, 0, 100, 0, 0,
+ 210, 0, 211, 0, 210, 82, 211, 0, 9, 60,
+ 100, 77, 0, 110, 0, 212, 82, 110, 0, 0,
+ 214, 215, 0, 217, 77, 0, 0, 218, 78, 216,
+ 215, 0, 1, 77, 0, 0, 10, 0, 218, 0,
+ 218, 82, 10, 0, 219, 0, 218, 82, 219, 0,
+ 119, 156, 0, 119, 157, 0, 119, 173, 0, 121,
+ 157, 0, 121, 173, 0, 0, 221, 222, 0, 215,
+ 0, 223, 77, 0, 3, 0, 223, 82, 3, 0,
+ 98, 0, 224, 82, 98, 0
+};
+
+#endif
+
+#if YYDEBUG != 0
+static const short yyrline[] = { 0,
+ 220, 224, 237, 239, 239, 240, 242, 244, 245, 255,
+ 261, 263, 265, 267, 269, 270, 271, 276, 282, 284,
+ 285, 287, 292, 294, 295, 297, 302, 304, 305, 309,
+ 311, 314, 316, 318, 320, 322, 324, 326, 330, 334,
+ 337, 340, 343, 347, 349, 352, 355, 358, 362, 388,
+ 393, 395, 397, 399, 401, 405, 407, 410, 414, 441,
+ 443, 445, 447, 449, 451, 453, 455, 457, 459, 461,
+ 463, 465, 467, 469, 471, 473, 476, 482, 581, 582,
+ 584, 590, 592, 606, 629, 631, 633, 637, 643, 645,
+ 650, 652, 657, 659, 660, 670, 675, 677, 678, 679,
+ 682, 687, 691, 694, 702, 707, 709, 710, 711, 718,
+ 726, 731, 735, 739, 743, 745, 753, 756, 760, 762,
+ 764, 775, 779, 781, 784, 797, 800, 804, 806, 814,
+ 815, 816, 820, 822, 828, 829, 830, 833, 835, 838,
+ 840, 843, 846, 852, 859, 862, 868, 875, 878, 885,
+ 888, 892, 895, 899, 904, 907, 911, 914, 916, 919,
+ 922, 929, 931, 932, 933, 938, 940, 945, 953, 958,
+ 962, 965, 967, 972, 975, 977, 979, 983, 986, 986,
+ 989, 989, 992, 992, 995, 995, 998, 1000, 1017, 1021,
+ 1038, 1045, 1047, 1052, 1055, 1060, 1062, 1064, 1066, 1074,
+ 1080, 1082, 1084, 1086, 1092, 1098, 1100, 1102, 1104, 1106,
+ 1109, 1114, 1118, 1121, 1123, 1125, 1127, 1130, 1132, 1135,
+ 1138, 1141, 1144, 1148, 1150, 1153, 1155, 1159, 1162, 1167,
+ 1169, 1171, 1185, 1191, 1196, 1201, 1206, 1210, 1212, 1216,
+ 1220, 1224, 1234, 1236, 1238, 1243, 1246, 1250, 1253, 1257,
+ 1260, 1263, 1266, 1270, 1273, 1277, 1281, 1283, 1285, 1287,
+ 1289, 1291, 1293, 1295, 1303, 1305, 1306, 1309, 1311, 1314,
+ 1317, 1328, 1330, 1335, 1337, 1340, 1354, 1357, 1360, 1362,
+ 1370, 1378, 1389, 1394, 1397, 1410, 1418, 1422, 1426, 1430,
+ 1436, 1440, 1445, 1447, 1458, 1461, 1462, 1479, 1484, 1487,
+ 1499, 1501, 1511, 1521, 1522, 1530, 1533, 1545, 1549, 1566,
+ 1576, 1585, 1590, 1595, 1600, 1604, 1608, 1619, 1626, 1633,
+ 1640, 1651, 1655, 1658, 1663, 1686, 1720, 1745, 1774, 1789,
+ 1800, 1804, 1808, 1811, 1816, 1818, 1821, 1823, 1827, 1832,
+ 1835, 1841, 1846, 1851, 1853, 1862, 1863, 1869, 1871, 1876,
+ 1878, 1882, 1885, 1891, 1894, 1896, 1898, 1900, 1907, 1912,
+ 1917, 1919, 1928, 1931, 1936, 1939
+};
+
+static const char * const yytname[] = { "$","error","$illegal.","IDENTIFIER",
+"TYPENAME","SCSPEC","TYPESPEC","TYPE_QUAL","CONSTANT","STRING","ELLIPSIS","SIZEOF",
+"ENUM","STRUCT","UNION","IF","ELSE","WHILE","DO","FOR","SWITCH","CASE","DEFAULT",
+"BREAK","CONTINUE","RETURN","GOTO","ASM_KEYWORD","TYPEOF","ALIGNOF","ALIGN",
+"ATTRIBUTE","EXTENSION","LABEL","REALPART","IMAGPART","ASSIGN","'='","'?'","':'",
+"OROR","ANDAND","'|'","'^'","'&'","EQCOMPARE","ARITHCOMPARE","LSHIFT","RSHIFT",
+"'+'","'-'","'*'","'/'","'%'","UNARY","PLUSPLUS","MINUSMINUS","HYPERUNARY","POINTSAT",
+"'.'","'('","'['","INTERFACE","IMPLEMENTATION","END","SELECTOR","DEFS","ENCODE",
+"CLASSNAME","PUBLIC","PRIVATE","PROTECTED","PROTOCOL","OBJECTNAME","CLASS","ALIAS",
+"OBJC_STRING","')'","';'","'}'","'~'","'!'","','","'{'","']'","program","extdefs",
+"@1","@2","extdef","datadef","fndef","@3","@4","@5","@6","@7","@8","identifier",
+"unop","expr","exprlist","nonnull_exprlist","unary_expr","@9","cast_expr","@10",
+"expr_no_commas","primary","@11","string","xdecls","lineno_datadecl","datadecls",
+"datadecl","lineno_decl","decls","setspecs","decl","typed_declspecs","reserved_declspecs",
+"declmods","typed_typespecs","reserved_typespecquals","typespec","typespecqual_reserved",
+"initdecls","notype_initdecls","maybeasm","initdcl","@12","notype_initdcl","@13",
+"maybe_attribute","attributes","attribute","attribute_list","attrib","any_word",
+"init","@14","initlist_maybe_comma","initlist1","initelt","@15","@16","@17",
+"@18","@19","@20","nested_function","@21","notype_nested_function","@22","declarator",
+"after_type_declarator","parm_declarator","notype_declarator","structsp","@23",
+"@24","@25","@26","maybecomma","maybecomma_warn","component_decl_list","component_decl_list2",
+"component_decl","components","component_declarator","enumlist","enumerator",
+"typename","absdcl","nonempty_type_quals","type_quals","absdcl1","stmts","xstmts",
+"errstmt","pushlevel","maybe_label_decls","label_decls","label_decl","compstmt_or_error",
+"compstmt","simple_if","if_prefix","do_stmt_start","@27","save_filename","save_lineno",
+"lineno_labeled_stmt","lineno_stmt_or_label","stmt_or_label","stmt","@28","@29",
+"@30","@31","@32","@33","@34","all_iter_stmt","all_iter_stmt_simple","@35","label",
+"maybe_type_qual","xexpr","asm_operands","nonnull_asm_operands","asm_operand",
+"asm_clobbers","parmlist","@36","parmlist_1","@37","parmlist_2","parms","parm",
+"parmlist_or_identifiers","@38","parmlist_or_identifiers_1","identifiers","identifiers_or_typenames",
+""
+};
+#endif
+
+static const short yyr1[] = { 0,
+ 85, 85, 87, 86, 88, 86, 89, 89, 89, 90,
+ 90, 90, 90, 90, 90, 90, 90, 92, 93, 91,
+ 91, 94, 95, 91, 91, 96, 97, 91, 91, 98,
+ 98, 99, 99, 99, 99, 99, 99, 99, 100, 101,
+ 101, 102, 102, 103, 103, 104, 103, 103, 103, 103,
+ 103, 103, 103, 103, 103, 105, 105, 106, 105, 107,
+ 107, 107, 107, 107, 107, 107, 107, 107, 107, 107,
+ 107, 107, 107, 107, 107, 107, 107, 108, 108, 108,
+ 108, 108, 109, 108, 108, 108, 108, 108, 108, 108,
+ 110, 110, 111, 111, 111, 112, 113, 113, 113, 113,
+ 114, 114, 114, 114, 115, 116, 116, 116, 116, 117,
+ 118, 118, 118, 118, 118, 118, 119, 119, 120, 120,
+ 120, 121, 121, 121, 121, 122, 122, 123, 123, 124,
+ 124, 124, 124, 124, 125, 125, 125, 126, 126, 127,
+ 127, 128, 128, 130, 129, 129, 132, 131, 131, 133,
+ 133, 134, 134, 135, 136, 136, 137, 137, 137, 137,
+ 137, 138, 138, 138, 138, 139, 140, 139, 139, 141,
+ 141, 142, 142, 143, 144, 143, 143, 145, 143, 146,
+ 143, 147, 143, 148, 143, 149, 143, 151, 150, 153,
+ 152, 154, 154, 155, 155, 155, 155, 155, 155, 156,
+ 156, 156, 156, 156, 157, 157, 157, 157, 157, 157,
+ 159, 158, 158, 158, 160, 158, 158, 158, 161, 158,
+ 162, 158, 158, 163, 163, 164, 164, 165, 165, 166,
+ 166, 166, 167, 167, 167, 167, 167, 168, 168, 169,
+ 169, 169, 170, 170, 170, 171, 171, 172, 172, 173,
+ 173, 174, 174, 175, 175, 176, 176, 176, 176, 176,
+ 176, 176, 176, 176, 177, 177, 177, 178, 178, 179,
+ 180, 181, 181, 182, 182, 183, 184, 184, 185, 185,
+ 185, 185, 186, 186, 187, 189, 188, 190, 191, 192,
+ 192, 193, 194, 194, 195, 195, 195, 196, 195, 195,
+ 195, 197, 198, 195, 195, 195, 199, 200, 201, 195,
+ 202, 195, 195, 195, 195, 195, 195, 195, 195, 195,
+ 195, 195, 195, 203, 205, 204, 206, 206, 206, 206,
+ 207, 207, 208, 208, 209, 209, 210, 210, 211, 212,
+ 212, 214, 213, 215, 216, 215, 215, 217, 217, 217,
+ 217, 218, 218, 219, 219, 219, 219, 219, 221, 220,
+ 222, 222, 223, 223, 224, 224
+};
+
+static const short yyr2[] = { 0,
+ 0, 1, 0, 2, 0, 3, 1, 1, 5, 3,
+ 4, 4, 2, 2, 2, 2, 1, 0, 0, 7,
+ 4, 0, 0, 7, 4, 0, 0, 6, 3, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+ 1, 1, 3, 1, 2, 0, 3, 2, 2, 2,
+ 4, 2, 4, 2, 2, 1, 4, 0, 7, 1,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 5, 3, 3, 1, 1, 1,
+ 3, 3, 0, 4, 4, 4, 3, 3, 2, 2,
+ 1, 2, 0, 1, 2, 3, 1, 1, 2, 2,
+ 4, 4, 2, 2, 3, 1, 1, 2, 2, 0,
+ 4, 4, 3, 3, 2, 2, 2, 3, 0, 2,
+ 2, 1, 1, 2, 2, 2, 3, 0, 2, 1,
+ 1, 1, 4, 4, 1, 1, 1, 1, 3, 1,
+ 3, 0, 4, 0, 6, 3, 0, 6, 3, 0,
+ 1, 1, 2, 6, 1, 3, 0, 1, 4, 6,
+ 4, 1, 1, 1, 1, 1, 0, 4, 1, 0,
+ 2, 1, 3, 1, 0, 4, 1, 0, 8, 0,
+ 6, 0, 5, 0, 4, 0, 5, 0, 3, 0,
+ 3, 1, 1, 3, 3, 4, 3, 3, 1, 3,
+ 4, 3, 3, 1, 3, 3, 3, 4, 3, 1,
+ 0, 6, 4, 2, 0, 6, 4, 2, 0, 7,
+ 0, 6, 2, 0, 1, 0, 1, 1, 2, 0,
+ 3, 2, 3, 1, 3, 1, 1, 1, 3, 4,
+ 6, 5, 1, 3, 1, 1, 3, 2, 2, 0,
+ 1, 1, 2, 0, 2, 3, 3, 2, 3, 4,
+ 3, 2, 3, 2, 1, 2, 2, 0, 1, 2,
+ 0, 0, 1, 1, 2, 3, 1, 2, 2, 6,
+ 5, 5, 2, 2, 4, 0, 4, 0, 0, 3,
+ 4, 3, 1, 1, 1, 1, 2, 0, 4, 1,
+ 3, 0, 0, 7, 5, 2, 0, 0, 0, 12,
+ 0, 6, 2, 2, 2, 3, 6, 8, 10, 12,
+ 3, 4, 1, 1, 0, 6, 3, 5, 2, 2,
+ 0, 1, 0, 1, 0, 1, 1, 3, 4, 1,
+ 3, 0, 2, 2, 0, 4, 2, 0, 1, 1,
+ 3, 1, 3, 2, 2, 2, 2, 2, 0, 2,
+ 1, 2, 1, 3, 1, 3
+};
+
+static const short yydefact[] = { 3,
+ 5, 0, 0, 0, 132, 123, 130, 122, 0, 0,
+ 0, 0, 0, 17, 4, 8, 7, 0, 110, 110,
+ 119, 131, 6, 15, 16, 30, 31, 221, 223, 230,
+ 214, 230, 218, 0, 0, 210, 254, 0, 0, 140,
+ 0, 14, 0, 125, 124, 13, 0, 119, 117, 0,
+ 219, 0, 0, 211, 0, 215, 78, 79, 91, 0,
+ 0, 46, 0, 0, 0, 32, 34, 33, 0, 35,
+ 36, 0, 37, 38, 0, 0, 39, 56, 60, 42,
+ 44, 80, 252, 0, 250, 128, 0, 250, 0, 0,
+ 10, 0, 29, 0, 359, 0, 0, 150, 199, 254,
+ 0, 0, 138, 0, 192, 193, 0, 0, 118, 121,
+ 135, 136, 120, 137, 245, 246, 226, 243, 0, 213,
+ 237, 232, 110, 229, 110, 230, 217, 230, 0, 50,
+ 0, 52, 0, 54, 55, 49, 45, 0, 0, 0,
+ 0, 48, 0, 0, 0, 0, 333, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 89, 90, 0, 0, 40, 0, 92, 133, 254,
+ 342, 0, 248, 251, 126, 134, 253, 128, 249, 255,
+ 207, 206, 141, 142, 0, 205, 0, 209, 0, 0,
+ 27, 0, 288, 98, 289, 0, 149, 151, 152, 0,
+ 0, 12, 0, 21, 0, 150, 359, 0, 11, 25,
+ 0, 0, 227, 0, 226, 288, 231, 288, 0, 0,
+ 0, 0, 47, 82, 81, 271, 0, 0, 9, 43,
+ 77, 76, 334, 0, 74, 73, 71, 72, 70, 69,
+ 68, 66, 67, 61, 62, 63, 64, 65, 88, 87,
+ 0, 41, 0, 258, 0, 262, 0, 264, 0, 342,
+ 0, 129, 127, 0, 0, 363, 349, 250, 250, 361,
+ 0, 350, 352, 360, 0, 208, 270, 0, 100, 95,
+ 99, 0, 0, 147, 153, 198, 194, 139, 19, 146,
+ 195, 197, 0, 23, 247, 244, 222, 0, 233, 238,
+ 289, 235, 212, 216, 51, 53, 279, 272, 84, 58,
+ 57, 0, 85, 86, 257, 256, 343, 263, 259, 261,
+ 0, 143, 347, 204, 254, 342, 354, 355, 356, 254,
+ 357, 358, 344, 345, 0, 362, 0, 0, 28, 277,
+ 96, 110, 110, 157, 0, 0, 144, 196, 0, 220,
+ 288, 0, 0, 0, 273, 274, 0, 75, 260, 258,
+ 359, 0, 258, 0, 351, 353, 364, 278, 103, 0,
+ 104, 0, 163, 164, 165, 162, 0, 155, 158, 169,
+ 167, 166, 148, 20, 0, 24, 239, 0, 150, 365,
+ 0, 0, 0, 288, 0, 107, 289, 265, 275, 177,
+ 78, 0, 0, 175, 0, 174, 0, 224, 172, 203,
+ 200, 202, 0, 346, 0, 0, 142, 0, 157, 0,
+ 0, 145, 150, 0, 240, 276, 0, 281, 109, 108,
+ 0, 0, 282, 267, 289, 266, 0, 0, 0, 0,
+ 184, 59, 0, 171, 201, 101, 102, 154, 156, 78,
+ 0, 0, 242, 150, 366, 280, 0, 132, 0, 302,
+ 286, 0, 0, 0, 0, 0, 0, 0, 0, 331,
+ 323, 0, 0, 105, 110, 110, 295, 300, 0, 0,
+ 292, 293, 296, 324, 294, 186, 0, 182, 0, 0,
+ 173, 159, 0, 161, 168, 241, 0, 0, 288, 333,
+ 0, 0, 329, 313, 314, 315, 0, 0, 0, 332,
+ 0, 330, 297, 115, 0, 116, 0, 0, 284, 289,
+ 283, 306, 0, 0, 0, 180, 0, 176, 185, 0,
+ 0, 0, 0, 44, 0, 0, 0, 327, 316, 0,
+ 321, 0, 0, 113, 142, 0, 114, 142, 301, 288,
+ 0, 0, 187, 0, 0, 183, 160, 285, 0, 287,
+ 325, 307, 311, 0, 322, 0, 111, 0, 112, 0,
+ 299, 290, 288, 0, 178, 181, 303, 288, 333, 288,
+ 328, 335, 0, 189, 191, 291, 305, 0, 288, 326,
+ 0, 312, 0, 0, 336, 337, 317, 179, 304, 308,
+ 0, 335, 0, 0, 333, 0, 0, 318, 338, 0,
+ 339, 0, 0, 309, 340, 0, 319, 288, 0, 0,
+ 310, 320, 341, 0, 0, 0
+};
+
+static const short yydefgoto[] = { 624,
+ 1, 2, 3, 15, 16, 17, 205, 346, 211, 349,
+ 97, 278, 405, 75, 233, 251, 77, 78, 133, 79,
+ 357, 80, 81, 140, 82, 191, 192, 193, 341, 393,
+ 394, 18, 474, 268, 49, 269, 85, 175, 21, 113,
+ 102, 39, 98, 103, 385, 40, 345, 197, 198, 199,
+ 377, 378, 379, 383, 421, 407, 408, 409, 440, 588,
+ 555, 527, 490, 524, 544, 568, 547, 570, 184, 105,
+ 327, 106, 22, 126, 128, 119, 50, 444, 214, 52,
+ 53, 124, 299, 300, 117, 118, 87, 173, 88, 89,
+ 174, 395, 432, 194, 308, 354, 355, 356, 339, 340,
+ 478, 479, 480, 499, 520, 282, 521, 398, 481, 482,
+ 550, 498, 589, 579, 605, 618, 580, 483, 484, 578,
+ 485, 511, 234, 594, 595, 596, 616, 256, 257, 270,
+ 364, 271, 272, 273, 186, 187, 274, 275, 391
+};
+
+static const short yypact[] = { 61,
+ 79, 401, 401, 192,-32768,-32768,-32768,-32768, 56, 63,
+ 67, 3, 36,-32768,-32768,-32768,-32768, 96, 20, 488,
+-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768, 40,-32768,
+ 66,-32768, 76, 1775, 1691,-32768,-32768, 96, 156,-32768,
+ 762,-32768, 69,-32768,-32768,-32768, 96,-32768, 404, 393,
+-32768, 109, 293,-32768, 111,-32768,-32768,-32768,-32768, 1788,
+ 1838,-32768, 1775, 1775, 330,-32768,-32768,-32768, 1775,-32768,
+-32768, 528,-32768,-32768, 1775, 98, 118,-32768,-32768, 1983,
+ 451, 193,-32768, 134, 172,-32768, 141, 1050, 258, 4,
+-32768, 69,-32768, 165,-32768, 1317, 236, 204,-32768,-32768,
+ 69, 191,-32768, 362, 331, 361, 232, 924, 404,-32768,
+-32768,-32768,-32768,-32768,-32768, 210, 162,-32768, 393,-32768,
+-32768,-32768, 360, 182, 1005,-32768,-32768,-32768, 528,-32768,
+ 528,-32768, 1775,-32768,-32768,-32768,-32768, 180, 200, 219,
+ 216,-32768, 233, 1775, 1775, 1775, 1775, 1775, 1775, 1775,
+ 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775,
+ 1775,-32768,-32768, 330, 330, 1775, 1775,-32768,-32768,-32768,
+ 172, 1330,-32768, 388, 419,-32768,-32768,-32768,-32768,-32768,
+ 361,-32768,-32768, 314, 311,-32768, 586,-32768, 264, 292,
+-32768, 179, 44,-32768,-32768, 322, 363, 204,-32768, 194,
+ 33,-32768, 69,-32768, 236, 204,-32768, 1384,-32768,-32768,
+ 236, 1775, 330, 319, 162,-32768,-32768,-32768, 340, 345,
+ 350, 359,-32768,-32768,-32768, 367, 364, 1633,-32768, 1983,
+ 1983, 1983,-32768, 416, 2012, 2024, 1877, 475, 2033, 1423,
+ 433, 496, 496, 406, 406,-32768,-32768,-32768,-32768,-32768,
+ 383, 118, 380, 265, 235,-32768, 846,-32768, 386,-32768,
+ 1397,-32768, 419, 31, 399,-32768,-32768, 148, 684,-32768,
+ 411, 249,-32768,-32768, 83,-32768,-32768, 48,-32768,-32768,
+-32768, 1484, 436,-32768,-32768, 331,-32768,-32768,-32768, 460,
+-32768,-32768, 415,-32768, 1983,-32768,-32768, 424, 423,-32768,
+-32768, 423,-32768,-32768,-32768,-32768,-32768, 480,-32768,-32768,
+-32768, 1775,-32768,-32768, 388,-32768,-32768,-32768,-32768,-32768,
+ 446,-32768,-32768,-32768,-32768, 161, 405, 361,-32768,-32768,
+ 361,-32768,-32768,-32768, 373,-32768, 511, 219,-32768,-32768,
+-32768, 465, 1278, 598, 1267, 48,-32768,-32768, 48,-32768,
+-32768, 53, 330, 702, 480,-32768, 1084, 1999,-32768, 102,
+-32768, 1451, 207, 846,-32768,-32768,-32768,-32768,-32768, 69,
+-32768, 96,-32768,-32768,-32768,-32768, 149,-32768, 478,-32768,
+-32768, 1983,-32768,-32768, 1267,-32768,-32768, 1775, 138,-32768,
+ 271, 390, 621, 471, 783,-32768,-32768,-32768,-32768,-32768,
+ 513, 330, 1775,-32768, 514, 1983, 476, 477,-32768, 405,
+-32768,-32768, 474,-32768, 280, 282, 26, 484, 598, 1851,
+ 1084,-32768, 1943, 1775,-32768,-32768, 330,-32768,-32768,-32768,
+ 864, 485,-32768,-32768,-32768,-32768, 1533, 531, 1898, 1084,
+-32768,-32768, 1145,-32768,-32768,-32768,-32768,-32768,-32768, 226,
+ 240, 486,-32768, 1943,-32768,-32768, 1583, 532, 515,-32768,
+-32768, 516, 520, 1775, 535, 503, 504, 1725, 168, 578,
+-32768, 547, 517,-32768, 519, 1643,-32768, 590, 945, 51,
+-32768,-32768,-32768,-32768,-32768,-32768, 1775, 570, 533, 1206,
+-32768,-32768, 1775,-32768,-32768,-32768, 1775, 550,-32768, 1775,
+ 1775, 1477,-32768,-32768,-32768,-32768, 537, 1775, 538,-32768,
+ 553,-32768,-32768,-32768, 69,-32768, 96, 1026,-32768,-32768,
+-32768,-32768, 1775, 1206, 1916,-32768, 1206,-32768,-32768, 247,
+ 541, 1775, 602, 296, 543, 546, 1775,-32768,-32768, 559,
+-32768, 1775, 283,-32768, 41, 306,-32768, 169,-32768,-32768,
+ 1583, 554,-32768, 614, 1206,-32768,-32768,-32768, 575,-32768,
+-32768,-32768,-32768, 1965,-32768, 38,-32768, 219,-32768, 219,
+-32768,-32768,-32768, 580,-32768,-32768,-32768,-32768, 1775,-32768,
+-32768, 650, 582,-32768,-32768,-32768,-32768, 1206,-32768,-32768,
+ 583,-32768, 604, 128, 584,-32768,-32768,-32768,-32768,-32768,
+ 1775, 650, 591, 650, 1775, 596, 143,-32768,-32768, 597,
+-32768, 311, 600,-32768, 193, 253,-32768,-32768, 601, 311,
+-32768,-32768, 193, 668, 675,-32768
+};
+
+static const short yypgoto[] = {-32768,
+-32768,-32768,-32768, 677,-32768,-32768,-32768,-32768,-32768,-32768,
+-32768,-32768, -7,-32768, -34,-32768, -157, 430,-32768, -33,
+-32768, 130, 183,-32768, -177, -122, 489,-32768,-32768, 290,
+-32768, -4,-32768, 10, 638, 16, 639, 555, -3, -129,
+ -342, -40, -94, -63,-32768,-32768,-32768, -195,-32768, 495,
+-32768, 275,-32768, 310,-32768, -366,-32768, -410,-32768,-32768,
+-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768, -37, -64,
+ 372, -13, -27,-32768,-32768,-32768,-32768,-32768, 523, 9,
+-32768,-32768, 521, 389, 622, 529, -28, -65, 694, -79,
+ -147, 354,-32768, -178,-32768,-32768,-32768, 394, -271, -114,
+-32768,-32768,-32768,-32768, -50, -281, -448, -347,-32768, 199,
+-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+ 203,-32768, -461, 153,-32768, 152,-32768, 499,-32768, -222,
+-32768,-32768,-32768, 425, -180,-32768,-32768,-32768,-32768
+};
+
+
+#define YYLAST 2086
+
+
+static const short yytable[] = { 76,
+ 84, 29, 31, 33, 41, 104, 107, 264, 252, 206,
+ 290, 19, 19, 279, 43, 47, 48, 20, 20, 352,
+ 200, 114, 179, 255, 90, 227, 291, 415, 183, 134,
+ 135, 86, 491, 108, 317, 137, 201, 139, 535, 168,
+ 55, 142, 116, 141, -94, 262, 195, 436, 338, 86,
+ 533, 522, 94, 280, 452, 36, 99, 136, 26, 27,
+ -1, 189, 34, 95, 96, 26, 27, 94, 86, 26,
+ 27, 36, 99, 489, 384, 181, 582, 386, -2, 529,
+ 182, 114, 289, 436, 178, 95, 96, 90, 294, 206,
+ 254, 388, 207, 208, 139, 35, 139, 42, 36, 223,
+ 221, 571, 222, 100, 36, 324, 315, 322, 180, 287,
+ 523, 116, 101, 553, 583, 437, 556, 591, 216, 100,
+ 218, 178, 51, -188, 586, 86, -94, 86, 101, 590,
+ 226, 592, 253, 262, 219, 286, 220, 259, 28, 288,
+ 599, 414, 195, 610, 576, 30, 37, 114, 54, 32,
+ 36, 324, 325, 457, 195, 38, 249, 250, 56, 336,
+ 195, 326, 172, 36, 337, 301, 602, 301, 196, 621,
+ 26, 27, 543, 293, 143, 396, 424, 598, 255, 190,
+ 411, 612, -97, -97, -97, -97, 181, 120, -97, 127,
+ -97, -97, -97, 425, 311, 94, 36, 99, 325, 144,
+ 180, 168, 329, 332, 603, 116, -97, 326, 172, 36,
+ 169, 330, 315, 180, 429, 315, 434, 176, 508, 613,
+ 326, 172, 170, 368, 185, 418, 321, 453, 95, 96,
+ 419, 171, 172, 91, 196, 114, 190, 92, 551, -288,
+ -288, -288, -288, 213, 100, 360, 212, -288, -288, -288,
+ 363, -190, 434, 101, 328, 331, 224, 330, 496, 217,
+ 36, -97, 451, -288, 180, 48, 326, 172, 202, 24,
+ 25, 180, 203, 230, 231, 232, 225, 235, 236, 237,
+ 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
+ 248, 342, 228, 121, 260, 261, 5, 343, 7, 83,
+ 301, 226, 492, 397, 9, 10, 11, 493, 37, 209,
+ 229, 316, 90, 92, 389, 170, 494, 38, -93, 59,
+ 13, 144, 477, 557, 171, 172, 334, 413, 144, 619,
+ 335, 416, 26, 27, 620, 530, 376, 370, 372, 48,
+ 94, 295, 477, 397, 435, 390, 181, 276, 426, 181,
+ 162, 163, 427, 164, 165, 166, 167, 446, 417, 447,
+ 567, 203, 204, 92, 203, -18, -18, -18, -18, 277,
+ 122, -228, 561, -18, -18, -18, 5, 6, 7, 8,
+ 435, 283, 365, 569, 9, 10, 11, 92, 94, -18,
+ 207, 208, -142, 115, 438, 26, 27, 297, -142, 284,
+ 13, 4, 473, -110, 5, 6, 7, 8, 110, 111,
+ 112, 376, 9, 10, 11, 9, 10, 11, 303, 455,
+ 95, 96, 473, 304, 111, 112, 305, 12, 13, 472,
+ 9, 10, 11, 507, 615, 306, 477, -234, -234, -142,
+ 309, 358, 623, -142, -18, 307, 475, 260, 261, 472,
+ 206, -110, 476, 584, 312, 585, 159, 160, 161, 313,
+ -110, 509, 531, 314, 361, 362, 536, 277, 428, 318,
+ 515, 517, 48, 540, 382, 323, 546, 545, 14, 155,
+ 156, 157, 158, 159, 160, 161, 406, 333, 552, 130,
+ 132, 5, 44, 7, 45, 344, 347, 559, 348, 9,
+ 10, 11, 350, 548, 351, 162, 163, 566, 164, 165,
+ 166, 167, 353, 367, 382, 13, 473, 423, 152, 153,
+ 154, 155, 156, 157, 158, 159, 160, 161, 138, 359,
+ 57, 5, 439, 7, 83, 58, 59, 420, 60, 9,
+ 10, 11, 369, 472, 157, 158, 159, 160, 161, -268,
+ 406, -30, 441, 454, 442, 13, 61, 445, 443, 62,
+ 448, 63, 64, 456, 495, 46, 606, 486, 65, 406,
+ -31, 66, 406, 503, 497, 500, 67, 68, 69, 501,
+ 504, 505, 70, 71, 510, 512, 265, 72, 266, 5,
+ 6, 7, 8, 502, 513, 267, 514, 9, 10, 11,
+ 26, 27, 373, 374, 375, 518, 526, 73, 74, 532,
+ -83, 528, 542, 13, 539, 541, 525, 558, 560, 406,
+ 562, 190, 563, -106, -106, -106, -106, -106, -106, -106,
+ 574, -106, -106, -106, -106, -106, 565, -106, -106, -106,
+ -106, -106, -106, -106, -106, -106, -106, -106, -106, -106,
+ 575, 577, -106, 406, -106, -106, 406, 587, 593, 597,
+ 600, -106, -348, 601, -106, 604, 564, 625, 608, -106,
+ -106, -106, 611, 614, 626, -106, -106, 617, 622, 23,
+ -106, 281, 534, 430, 406, 109, 36, 5, 44, 7,
+ 45, 123, 285, 449, 422, 9, 10, 11, -106, -106,
+ -106, -106, 392, -106, -288, -288, -288, -288, -288, -288,
+ -288, 13, -288, -288, -288, -288, -288, 406, -288, -288,
+ -288, -288, -288, -288, -288, -288, -288, -288, -288, -288,
+ -288, 410, 263, -288, 330, -288, -288, 298, 302, 387,
+ 215, 296, -288, 326, 172, -288, 125, 431, 399, 572,
+ -288, -288, -288, 573, 607, 609, -288, -288, 319, 366,
+ 0, -288, 93, 0, 0, -26, -26, -26, -26, 0,
+ 0, 0, 0, -26, -26, -26, 0, 0, 0, -288,
+ 0, -288, -288, 190, -288, -288, -288, 0, 94, -26,
+ -288, -288, -142, -288, 0, 0, 0, -288, -142, -288,
+ -288, -288, -288, -288, -288, -288, -288, -288, -288, -288,
+ 0, -288, 0, 0, -288, 0, -288, -288, 0, 0,
+ 0, 95, 96, -288, 0, 0, -288, 0, 0, 0,
+ 0, -288, -288, -288, 0, 0, 0, -288, -288, -142,
+ 0, 0, -288, -142, -26, 0, 265, 0, 0, 5,
+ 6, 7, 8, 0, 0, 267, 0, 9, 10, 11,
+ -288, 433, -288, -288, 190, -288, -288, -288, 0, 0,
+ 0, -288, -288, 13, -288, 0, 0, 0, -288, 0,
+ -288, -288, -288, -288, -288, -288, -288, -288, -288, -288,
+ -288, 0, -288, 0, 0, -288, 0, -288, -288, 0,
+ 0, 0, 0, 0, -288, 0, 0, -288, 0, 0,
+ 0, 0, -288, -288, -288, 0, 0, 0, -288, -288,
+ 0, 0, -348, -288, 210, 0, 0, -22, -22, -22,
+ -22, 0, 0, 0, 0, -22, -22, -22, 0, 0,
+ 0, -288, -269, -288, -288, 519, -288, -288, -288, 0,
+ 94, -22, -288, -288, -142, -288, 0, 0, 0, -288,
+ -142, -288, -288, -288, -288, -288, -288, -288, -288, -288,
+ -288, -288, 0, -288, 0, 0, -288, 0, -288, -288,
+ 0, 0, 0, 95, 96, -288, 0, 0, -288, 0,
+ 0, 0, 0, -288, -288, -288, 0, 0, 0, -288,
+ -288, -142, 0, 0, -288, -142, -22, 0, 5, 0,
+ 7, 177, 0, 0, 0, 0, 9, 10, 11, 0,
+ 0, 0, -288, 0, -288, -288, 549, -288, -298, -298,
+ 0, 0, 13, -298, -298, 0, -298, 0, 0, 0,
+ -298, 0, -298, -298, -298, -298, -298, -298, -298, -298,
+ -298, -298, -298, 5, -298, 7, 177, -298, 0, -298,
+ -298, 9, 10, 11, 0, 0, -298, 0, 0, -298,
+ 0, 0, 0, 0, -298, -298, -298, 13, 0, 0,
+ -298, -298, -236, -236, 400, -298, 401, 27, 0, 0,
+ 0, 58, 59, 0, 60, 0, 0, 0, 0, 0,
+ 170, 0, 0, -298, 0, -298, -298, 0, -298, 171,
+ 172, 0, 61, 0, 0, 62, 0, 63, 64, 0,
+ 0, 0, 0, 0, 65, 0, 0, 66, 0, 0,
+ 0, 0, 67, 68, 69, 0, 0, 0, 70, 71,
+ 0, 0, 402, 72, 403, 400, 0, 401, 27, 0,
+ 0, 0, 58, 59, 0, 60, 0, 0, 0, 0,
+ 0, 0, -170, 73, 74, 0, 404, 0, 0, 0,
+ 0, 0, 0, 61, 0, 0, 62, 0, 63, 64,
+ 0, 0, 0, 0, 0, 65, 0, 0, 66, 0,
+ 0, 0, 0, 67, 68, 69, 0, 0, 0, 70,
+ 71, 0, 0, 402, 72, 403, 400, 0, 401, 27,
+ 0, 0, 0, 58, 59, 0, 60, 0, 0, 0,
+ 0, 0, 0, -225, 73, 74, 0, 404, 0, 0,
+ 0, 0, 0, 0, 61, 0, 0, 62, 0, 63,
+ 64, 0, 0, 0, 0, 0, 65, 0, 0, 66,
+ 0, 0, 0, 0, 67, 68, 69, 0, 0, 0,
+ 70, 71, 0, 0, 402, 72, 403, 380, 0, 57,
+ 0, 0, 0, 0, 58, 59, 0, 60, 0, 0,
+ 0, 5, 44, 7, 45, 73, 74, 0, 404, 9,
+ 10, 11, 0, 0, 0, 61, 0, 0, 62, 0,
+ 63, 64, 0, 0, 0, 13, 0, 65, 0, 0,
+ 66, 0, 0, 0, 0, 67, 68, 69, 0, 57,
+ 0, 70, 71, 0, 58, 59, 72, 60, 0, 0,
+ 0, 0, 57, 0, 0, 0, 0, 58, 59, 0,
+ 60, 0, 0, 0, 0, 61, 73, 74, 62, 381,
+ 63, 64, 0, 0, 0, 371, 0, 65, 61, 0,
+ 66, 62, 0, 63, 64, 67, 68, 69, 0, 0,
+ 65, 70, 71, 66, 0, 0, 72, 0, 67, 68,
+ 69, 0, 0, 0, 70, 71, 57, 0, 0, 72,
+ 0, 58, 59, 0, 60, 0, 73, 74, 0, 57,
+ 188, 0, 0, 0, 58, 59, 0, 60, 0, 73,
+ 74, 0, 61, 258, 0, 62, 0, 63, 64, 0,
+ 0, 0, 0, 0, 65, 61, 0, 66, 62, 0,
+ 63, 64, 67, 68, 69, 0, 0, 65, 70, 71,
+ 66, 0, 0, 72, 0, 67, 68, 69, 0, 0,
+ 0, 70, 71, 57, 0, 0, 72, 0, 58, 59,
+ 0, 60, 0, 73, 74, 0, 0, 292, 154, 155,
+ 156, 157, 158, 159, 160, 161, 73, 74, 0, 61,
+ 320, 0, 62, 0, 63, 64, 537, 5, 6, 7,
+ 8, 65, 0, 0, 66, 9, 10, 11, 0, 67,
+ 68, 69, 0, 0, 0, 70, 71, 0, 0, 0,
+ 72, 13, 145, 146, 147, 538, 148, 149, 150, 151,
+ 152, 153, 154, 155, 156, 157, 158, 159, 160, 161,
+ 73, 74, 0, 0, 412, 401, 458, 6, 7, 8,
+ 58, 59, 0, 60, 9, 10, 11, 459, 0, 460,
+ 461, 462, 463, 464, 465, 466, 467, 468, 469, 470,
+ 13, 61, 0, 0, 62, 0, 63, 64, 0, 0,
+ 0, 0, 0, 65, 0, 0, 66, 0, 0, 0,
+ 0, 67, 68, 69, 0, 401, 27, 70, 71, 0,
+ 58, 59, 72, 60, 0, 0, 0, 459, 0, 460,
+ 461, 462, 463, 464, 465, 466, 467, 468, 469, 470,
+ 471, 61, 73, 74, 62, 226, 63, 64, 0, 0,
+ 0, 0, 0, 65, 0, 0, 66, 0, 0, 0,
+ 0, 67, 68, 69, 0, 57, 0, 70, 71, 0,
+ 58, 59, 72, 60, 0, 0, 5, 44, 7, 45,
+ 0, 0, 0, 0, 9, 10, 11, 0, 0, 0,
+ 471, 61, 73, 74, 62, 226, 63, 64, 0, 0,
+ 13, 0, 0, 65, 0, 0, 66, 0, 0, 0,
+ 0, 67, 68, 69, 0, 0, 0, 70, 71, 0,
+ 0, 0, 72, 57, 5, 0, 7, 83, 58, 59,
+ 0, 60, 9, 10, 11, 0, 0, 0, 0, 0,
+ 0, 0, 73, 74, 0, 310, 0, 0, 13, 61,
+ 516, 0, 62, 0, 63, 64, 0, 57, 0, 0,
+ 0, 65, 58, 59, 66, 60, 0, 0, 0, 67,
+ 68, 69, 0, 0, 0, 70, 71, 0, 0, 0,
+ 72, 0, 0, 61, 0, 0, 62, 0, 63, 64,
+ 0, 0, 0, 0, 0, 65, 0, 0, 66, 0,
+ 73, 74, 0, 67, 68, 69, 0, 57, 0, 70,
+ 71, 0, 58, 59, 72, 60, 0, 0, 0, 0,
+ 57, 0, 0, 0, 0, 58, 59, 0, 60, 0,
+ 0, 0, 506, 61, 73, 74, 62, 0, 63, 64,
+ 0, 0, 0, 0, 0, 65, 61, 0, 66, 62,
+ 0, 63, 64, 67, 68, 69, 0, 0, 65, 70,
+ 71, 66, 0, 0, 72, 0, 67, 68, 69, 0,
+ 57, 0, 70, 71, 0, 58, 59, 129, 60, 0,
+ 0, 0, 0, 450, 73, 74, 0, 0, 58, 59,
+ 0, 60, 0, 0, 0, 0, 61, 73, 74, 62,
+ 0, 63, 64, 0, 0, 0, 0, 0, 65, 61,
+ 0, 66, 62, 0, 63, 64, 67, 68, 69, 0,
+ 0, 65, 70, 71, 66, 0, 0, 131, 0, 67,
+ 68, 69, 0, 0, 0, 70, 71, 487, 0, 0,
+ 72, 0, 0, 0, 0, 0, 0, 73, 74, 151,
+ 152, 153, 154, 155, 156, 157, 158, 159, 160, 161,
+ 73, 74, 0, 145, 146, 147, 0, 148, 149, 150,
+ 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
+ 161, 145, 146, 147, 0, 148, 149, 150, 151, 152,
+ 153, 154, 155, 156, 157, 158, 159, 160, 161, 0,
+ 0, 0, 0, 196, 0, 0, 0, 0, 145, 146,
+ 147, 488, 148, 149, 150, 151, 152, 153, 154, 155,
+ 156, 157, 158, 159, 160, 161, 0, 0, 0, 554,
+ 145, 146, 147, 581, 148, 149, 150, 151, 152, 153,
+ 154, 155, 156, 157, 158, 159, 160, 161, 145, 146,
+ 147, 0, 148, 149, 150, 151, 152, 153, 154, 155,
+ 156, 157, 158, 159, 160, 161, 147, 0, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 149, 150, 151, 152, 153, 154, 155, 156,
+ 157, 158, 159, 160, 161, 150, 151, 152, 153, 154,
+ 155, 156, 157, 158, 159, 160, 161, 153, 154, 155,
+ 156, 157, 158, 159, 160, 161
+};
+
+static const short yycheck[] = { 34,
+ 35, 9, 10, 11, 18, 43, 47, 185, 166, 104,
+ 206, 2, 3, 192, 19, 20, 20, 2, 3, 301,
+ 100, 49, 88, 171, 38, 140, 207, 370, 92, 63,
+ 64, 35, 443, 47, 257, 69, 101, 72, 500, 9,
+ 32, 75, 50, 72, 1, 175, 97, 395, 1, 53,
+ 499, 1, 27, 10, 421, 3, 4, 65, 3, 4,
+ 0, 96, 60, 60, 61, 3, 4, 27, 72, 3,
+ 4, 3, 4, 440, 346, 89, 39, 349, 0, 490,
+ 77, 109, 205, 431, 88, 60, 61, 101, 211, 184,
+ 170, 39, 60, 61, 129, 60, 131, 78, 3, 133,
+ 129, 550, 131, 51, 3, 4, 254, 77, 7, 77,
+ 60, 119, 60, 524, 77, 397, 527, 579, 123, 51,
+ 125, 125, 83, 83, 573, 129, 83, 131, 60, 578,
+ 83, 580, 167, 263, 126, 200, 128, 172, 83, 203,
+ 589, 364, 193, 605, 555, 83, 51, 175, 83, 83,
+ 3, 4, 51, 435, 205, 60, 164, 165, 83, 77,
+ 211, 60, 61, 3, 82, 216, 39, 218, 31, 618,
+ 3, 4, 515, 208, 77, 354, 39, 588, 326, 1,
+ 361, 39, 4, 5, 6, 7, 200, 79, 10, 79,
+ 12, 13, 14, 389, 228, 27, 3, 4, 51, 82,
+ 7, 9, 268, 269, 77, 213, 28, 60, 61, 3,
+ 77, 51, 360, 7, 393, 363, 395, 77, 51, 77,
+ 60, 61, 51, 338, 60, 77, 261, 423, 60, 61,
+ 82, 60, 61, 78, 31, 263, 1, 82, 520, 4,
+ 5, 6, 7, 82, 51, 325, 37, 12, 13, 14,
+ 330, 83, 431, 60, 268, 269, 77, 51, 454, 78,
+ 3, 83, 420, 28, 7, 269, 60, 61, 78, 78,
+ 79, 7, 82, 144, 145, 146, 77, 148, 149, 150,
+ 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
+ 161, 282, 77, 1, 60, 61, 4, 282, 6, 7,
+ 351, 83, 77, 354, 12, 13, 14, 82, 51, 78,
+ 78, 77, 326, 82, 352, 51, 77, 60, 83, 9,
+ 28, 82, 437, 77, 60, 61, 78, 362, 82, 77,
+ 82, 372, 3, 4, 82, 493, 344, 342, 343, 343,
+ 27, 212, 457, 394, 395, 353, 360, 84, 78, 363,
+ 55, 56, 82, 58, 59, 60, 61, 78, 372, 78,
+ 78, 82, 1, 82, 82, 4, 5, 6, 7, 78,
+ 78, 79, 77, 12, 13, 14, 4, 5, 6, 7,
+ 431, 60, 10, 78, 12, 13, 14, 82, 27, 28,
+ 60, 61, 31, 1, 402, 3, 4, 79, 37, 37,
+ 28, 1, 437, 3, 4, 5, 6, 7, 5, 6,
+ 7, 419, 12, 13, 14, 12, 13, 14, 79, 427,
+ 60, 61, 457, 79, 6, 7, 77, 27, 28, 437,
+ 12, 13, 14, 468, 612, 77, 551, 78, 79, 78,
+ 77, 312, 620, 82, 83, 79, 437, 60, 61, 457,
+ 545, 51, 437, 568, 39, 570, 51, 52, 53, 77,
+ 60, 469, 497, 84, 60, 61, 501, 78, 79, 84,
+ 475, 476, 476, 508, 345, 77, 517, 515, 78, 47,
+ 48, 49, 50, 51, 52, 53, 357, 77, 523, 60,
+ 61, 4, 5, 6, 7, 60, 37, 532, 84, 12,
+ 13, 14, 79, 517, 82, 55, 56, 542, 58, 59,
+ 60, 61, 33, 3, 385, 28, 551, 388, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 53, 1, 84,
+ 3, 4, 403, 6, 7, 8, 9, 60, 11, 12,
+ 13, 14, 78, 551, 49, 50, 51, 52, 53, 79,
+ 421, 39, 39, 424, 79, 28, 29, 84, 82, 32,
+ 77, 34, 35, 79, 79, 78, 601, 37, 41, 440,
+ 39, 44, 443, 39, 60, 60, 49, 50, 51, 60,
+ 78, 78, 55, 56, 7, 39, 1, 60, 3, 4,
+ 5, 6, 7, 464, 78, 10, 78, 12, 13, 14,
+ 3, 4, 5, 6, 7, 16, 37, 80, 81, 60,
+ 83, 79, 60, 28, 78, 78, 487, 77, 17, 490,
+ 78, 1, 77, 3, 4, 5, 6, 7, 8, 9,
+ 77, 11, 12, 13, 14, 15, 78, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 37, 77, 32, 524, 34, 35, 527, 78, 9, 78,
+ 78, 41, 77, 60, 44, 82, 537, 0, 78, 49,
+ 50, 51, 77, 77, 0, 55, 56, 78, 78, 3,
+ 60, 193, 500, 394, 555, 48, 3, 4, 5, 6,
+ 7, 53, 198, 419, 385, 12, 13, 14, 78, 79,
+ 80, 81, 1, 83, 3, 4, 5, 6, 7, 8,
+ 9, 28, 11, 12, 13, 14, 15, 588, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+ 29, 360, 178, 32, 51, 34, 35, 215, 218, 351,
+ 119, 213, 41, 60, 61, 44, 53, 394, 355, 551,
+ 49, 50, 51, 551, 602, 604, 55, 56, 260, 335,
+ -1, 60, 1, -1, -1, 4, 5, 6, 7, -1,
+ -1, -1, -1, 12, 13, 14, -1, -1, -1, 78,
+ -1, 80, 81, 1, 83, 3, 4, -1, 27, 28,
+ 8, 9, 31, 11, -1, -1, -1, 15, 37, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ -1, 29, -1, -1, 32, -1, 34, 35, -1, -1,
+ -1, 60, 61, 41, -1, -1, 44, -1, -1, -1,
+ -1, 49, 50, 51, -1, -1, -1, 55, 56, 78,
+ -1, -1, 60, 82, 83, -1, 1, -1, -1, 4,
+ 5, 6, 7, -1, -1, 10, -1, 12, 13, 14,
+ 78, 79, 80, 81, 1, 83, 3, 4, -1, -1,
+ -1, 8, 9, 28, 11, -1, -1, -1, 15, -1,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
+ 27, -1, 29, -1, -1, 32, -1, 34, 35, -1,
+ -1, -1, -1, -1, 41, -1, -1, 44, -1, -1,
+ -1, -1, 49, 50, 51, -1, -1, -1, 55, 56,
+ -1, -1, 77, 60, 1, -1, -1, 4, 5, 6,
+ 7, -1, -1, -1, -1, 12, 13, 14, -1, -1,
+ -1, 78, 79, 80, 81, 1, 83, 3, 4, -1,
+ 27, 28, 8, 9, 31, 11, -1, -1, -1, 15,
+ 37, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, -1, 29, -1, -1, 32, -1, 34, 35,
+ -1, -1, -1, 60, 61, 41, -1, -1, 44, -1,
+ -1, -1, -1, 49, 50, 51, -1, -1, -1, 55,
+ 56, 78, -1, -1, 60, 82, 83, -1, 4, -1,
+ 6, 7, -1, -1, -1, -1, 12, 13, 14, -1,
+ -1, -1, 78, -1, 80, 81, 1, 83, 3, 4,
+ -1, -1, 28, 8, 9, -1, 11, -1, -1, -1,
+ 15, -1, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 4, 29, 6, 7, 32, -1, 34,
+ 35, 12, 13, 14, -1, -1, 41, -1, -1, 44,
+ -1, -1, -1, -1, 49, 50, 51, 28, -1, -1,
+ 55, 56, 78, 79, 1, 60, 3, 4, -1, -1,
+ -1, 8, 9, -1, 11, -1, -1, -1, -1, -1,
+ 51, -1, -1, 78, -1, 80, 81, -1, 83, 60,
+ 61, -1, 29, -1, -1, 32, -1, 34, 35, -1,
+ -1, -1, -1, -1, 41, -1, -1, 44, -1, -1,
+ -1, -1, 49, 50, 51, -1, -1, -1, 55, 56,
+ -1, -1, 59, 60, 61, 1, -1, 3, 4, -1,
+ -1, -1, 8, 9, -1, 11, -1, -1, -1, -1,
+ -1, -1, 79, 80, 81, -1, 83, -1, -1, -1,
+ -1, -1, -1, 29, -1, -1, 32, -1, 34, 35,
+ -1, -1, -1, -1, -1, 41, -1, -1, 44, -1,
+ -1, -1, -1, 49, 50, 51, -1, -1, -1, 55,
+ 56, -1, -1, 59, 60, 61, 1, -1, 3, 4,
+ -1, -1, -1, 8, 9, -1, 11, -1, -1, -1,
+ -1, -1, -1, 79, 80, 81, -1, 83, -1, -1,
+ -1, -1, -1, -1, 29, -1, -1, 32, -1, 34,
+ 35, -1, -1, -1, -1, -1, 41, -1, -1, 44,
+ -1, -1, -1, -1, 49, 50, 51, -1, -1, -1,
+ 55, 56, -1, -1, 59, 60, 61, 1, -1, 3,
+ -1, -1, -1, -1, 8, 9, -1, 11, -1, -1,
+ -1, 4, 5, 6, 7, 80, 81, -1, 83, 12,
+ 13, 14, -1, -1, -1, 29, -1, -1, 32, -1,
+ 34, 35, -1, -1, -1, 28, -1, 41, -1, -1,
+ 44, -1, -1, -1, -1, 49, 50, 51, -1, 3,
+ -1, 55, 56, -1, 8, 9, 60, 11, -1, -1,
+ -1, -1, 3, -1, -1, -1, -1, 8, 9, -1,
+ 11, -1, -1, -1, -1, 29, 80, 81, 32, 83,
+ 34, 35, -1, -1, -1, 78, -1, 41, 29, -1,
+ 44, 32, -1, 34, 35, 49, 50, 51, -1, -1,
+ 41, 55, 56, 44, -1, -1, 60, -1, 49, 50,
+ 51, -1, -1, -1, 55, 56, 3, -1, -1, 60,
+ -1, 8, 9, -1, 11, -1, 80, 81, -1, 3,
+ 84, -1, -1, -1, 8, 9, -1, 11, -1, 80,
+ 81, -1, 29, 84, -1, 32, -1, 34, 35, -1,
+ -1, -1, -1, -1, 41, 29, -1, 44, 32, -1,
+ 34, 35, 49, 50, 51, -1, -1, 41, 55, 56,
+ 44, -1, -1, 60, -1, 49, 50, 51, -1, -1,
+ -1, 55, 56, 3, -1, -1, 60, -1, 8, 9,
+ -1, 11, -1, 80, 81, -1, -1, 84, 46, 47,
+ 48, 49, 50, 51, 52, 53, 80, 81, -1, 29,
+ 84, -1, 32, -1, 34, 35, 10, 4, 5, 6,
+ 7, 41, -1, -1, 44, 12, 13, 14, -1, 49,
+ 50, 51, -1, -1, -1, 55, 56, -1, -1, -1,
+ 60, 28, 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ 80, 81, -1, -1, 84, 3, 4, 5, 6, 7,
+ 8, 9, -1, 11, 12, 13, 14, 15, -1, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, -1, -1, 32, -1, 34, 35, -1, -1,
+ -1, -1, -1, 41, -1, -1, 44, -1, -1, -1,
+ -1, 49, 50, 51, -1, 3, 4, 55, 56, -1,
+ 8, 9, 60, 11, -1, -1, -1, 15, -1, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ 78, 29, 80, 81, 32, 83, 34, 35, -1, -1,
+ -1, -1, -1, 41, -1, -1, 44, -1, -1, -1,
+ -1, 49, 50, 51, -1, 3, -1, 55, 56, -1,
+ 8, 9, 60, 11, -1, -1, 4, 5, 6, 7,
+ -1, -1, -1, -1, 12, 13, 14, -1, -1, -1,
+ 78, 29, 80, 81, 32, 83, 34, 35, -1, -1,
+ 28, -1, -1, 41, -1, -1, 44, -1, -1, -1,
+ -1, 49, 50, 51, -1, -1, -1, 55, 56, -1,
+ -1, -1, 60, 3, 4, -1, 6, 7, 8, 9,
+ -1, 11, 12, 13, 14, -1, -1, -1, -1, -1,
+ -1, -1, 80, 81, -1, 83, -1, -1, 28, 29,
+ 78, -1, 32, -1, 34, 35, -1, 3, -1, -1,
+ -1, 41, 8, 9, 44, 11, -1, -1, -1, 49,
+ 50, 51, -1, -1, -1, 55, 56, -1, -1, -1,
+ 60, -1, -1, 29, -1, -1, 32, -1, 34, 35,
+ -1, -1, -1, -1, -1, 41, -1, -1, 44, -1,
+ 80, 81, -1, 49, 50, 51, -1, 3, -1, 55,
+ 56, -1, 8, 9, 60, 11, -1, -1, -1, -1,
+ 3, -1, -1, -1, -1, 8, 9, -1, 11, -1,
+ -1, -1, 78, 29, 80, 81, 32, -1, 34, 35,
+ -1, -1, -1, -1, -1, 41, 29, -1, 44, 32,
+ -1, 34, 35, 49, 50, 51, -1, -1, 41, 55,
+ 56, 44, -1, -1, 60, -1, 49, 50, 51, -1,
+ 3, -1, 55, 56, -1, 8, 9, 60, 11, -1,
+ -1, -1, -1, 3, 80, 81, -1, -1, 8, 9,
+ -1, 11, -1, -1, -1, -1, 29, 80, 81, 32,
+ -1, 34, 35, -1, -1, -1, -1, -1, 41, 29,
+ -1, 44, 32, -1, 34, 35, 49, 50, 51, -1,
+ -1, 41, 55, 56, 44, -1, -1, 60, -1, 49,
+ 50, 51, -1, -1, -1, 55, 56, 10, -1, -1,
+ 60, -1, -1, -1, -1, -1, -1, 80, 81, 43,
+ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ 80, 81, -1, 36, 37, 38, -1, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 36, 37, 38, -1, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, -1,
+ -1, -1, -1, 31, -1, -1, -1, -1, 36, 37,
+ 38, 84, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, -1, -1, -1, 84,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 53, 36, 37,
+ 38, -1, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 38, -1, 40, 41,
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 53, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 42, 43, 44, 45, 46,
+ 47, 48, 49, 50, 51, 52, 53, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53
+};
+/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
+#line 3 "/usr/local/lib/bison.simple"
+
+/* Skeleton output parser for bison,
+ Copyright (C) 1984, 1989, 1990 Bob Corbett and Richard Stallman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#ifndef alloca
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not GNU C. */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi)
+#include <alloca.h>
+#else /* not sparc */
+#if defined (MSDOS) && !defined (__TURBOC__)
+#include <malloc.h>
+#else /* not MSDOS, or __TURBOC__ */
+#if defined(_AIX)
+#include <malloc.h>
+ #pragma alloca
+#else /* not MSDOS, __TURBOC__, or _AIX */
+#ifdef __hpux
+#ifdef __cplusplus
+extern "C" {
+void *alloca (unsigned int);
+};
+#else /* not __cplusplus */
+void *alloca ();
+#endif /* not __cplusplus */
+#endif /* __hpux */
+#endif /* not _AIX */
+#endif /* not MSDOS, or __TURBOC__ */
+#endif /* not sparc. */
+#endif /* not GNU C. */
+#endif /* alloca not defined. */
+
+/* This is the parser code that is written into each bison parser
+ when the %semantic_parser declaration is not specified in the grammar.
+ It was written by Richard Stallman by simplifying the hairy parser
+ used when %semantic_parser is specified. */
+
+/* Note: there must be only one dollar sign in this file.
+ It is replaced by the list of actions, each action
+ as one case of the switch. */
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY -2
+#define YYEOF 0
+#define YYACCEPT return(0)
+#define YYABORT return(1)
+#define YYERROR goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+ This remains here temporarily to ease the
+ transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+#define YYFAIL goto yyerrlab
+#define YYRECOVERING() (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { yychar = (token), yylval = (value); \
+ yychar1 = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { yyerror ("syntax error: cannot back up"); YYERROR; } \
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+#ifndef YYPURE
+#define YYLEX yylex()
+#endif
+
+#ifdef YYPURE
+#ifdef YYLSP_NEEDED
+#define YYLEX yylex(&yylval, &yylloc)
+#else
+#define YYLEX yylex(&yylval)
+#endif
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYPURE
+
+int yychar; /* the lookahead symbol */
+YYSTYPE yylval; /* the semantic value of the */
+ /* lookahead symbol */
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc; /* location data for the lookahead */
+ /* symbol */
+#endif
+
+int yynerrs; /* number of parse errors so far */
+#endif /* not YYPURE */
+
+#if YYDEBUG != 0
+int yydebug; /* nonzero means print parse trace */
+/* Since this is uninitialized, it does not stop multiple parsers
+ from coexisting. */
+#endif
+
+/* YYINITDEPTH indicates the initial size of the parser's stacks */
+
+#ifndef YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH is the maximum size the stacks can grow to
+ (effective only if the built-in stack extension method is used). */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+
+/* Prevent warning if -Wstrict-prototypes. */
+#ifdef __GNUC__
+int yyparse (void);
+#endif
+
+#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */
+#define __yy_bcopy(FROM,TO,COUNT) __builtin_memcpy(TO,FROM,COUNT)
+#else /* not GNU C or C++ */
+#ifndef __cplusplus
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_bcopy (from, to, count)
+ char *from;
+ char *to;
+ int count;
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#else /* __cplusplus */
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_bcopy (char *from, char *to, int count)
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#endif
+#endif
+
+#line 184 "/usr/local/lib/bison.simple"
+
+/* The user can define YYPARSE_PARAM as the name of an argument to be passed
+ into yyparse. The argument should have type void *.
+ It should actually point to an object.
+ Grammar actions can access the variable by casting it
+ to the proper pointer type. */
+
+#ifdef YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
+#else
+#define YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL
+#endif
+
+int
+yyparse(YYPARSE_PARAM)
+ YYPARSE_PARAM_DECL
+{
+ register int yystate;
+ register int yyn;
+ register short *yyssp;
+ register YYSTYPE *yyvsp;
+ int yyerrstatus; /* number of tokens to shift before error messages enabled */
+ int yychar1 = 0; /* lookahead token as an internal (translated) token number */
+
+ short yyssa[YYINITDEPTH]; /* the state stack */
+ YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
+
+ short *yyss = yyssa; /* refer to the stacks thru separate pointers */
+ YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */
+ YYLTYPE *yyls = yylsa;
+ YYLTYPE *yylsp;
+
+#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--)
+#else
+#define YYPOPSTACK (yyvsp--, yyssp--)
+#endif
+
+ int yystacksize = YYINITDEPTH;
+
+#ifdef YYPURE
+ int yychar;
+ YYSTYPE yylval;
+ int yynerrs;
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylloc;
+#endif
+#endif
+
+ YYSTYPE yyval; /* the variable used to return */
+ /* semantic values from the action */
+ /* routines */
+
+ int yylen;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Starting parse\n");
+#endif
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss - 1;
+ yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in yystate . */
+/* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks. */
+yynewstate:
+
+ *++yyssp = yystate;
+
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ /* Give user a chance to reallocate the stack */
+ /* Use copies of these so that the &'s don't force the real ones into memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+ YYLTYPE *yyls1 = yyls;
+#endif
+
+ /* Get the current used size of the three stacks, in elements. */
+ int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ /* Each stack pointer address is followed by the size of
+ the data in use in that stack, in bytes. */
+#ifdef YYLSP_NEEDED
+ /* This used to be a conditional around just the two extra args,
+ but that might be undefined if yyoverflow is a macro. */
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yyls1, size * sizeof (*yylsp),
+ &yystacksize);
+#else
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yystacksize);
+#endif
+
+ yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+ yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+ /* Extend the stack our own way. */
+ if (yystacksize >= YYMAXDEPTH)
+ {
+ yyerror("parser stack overflow");
+ return 2;
+ }
+ yystacksize *= 2;
+ if (yystacksize > YYMAXDEPTH)
+ yystacksize = YYMAXDEPTH;
+ yyss = (short *) alloca (yystacksize * sizeof (*yyssp));
+ __yy_bcopy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
+ yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp));
+ __yy_bcopy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+ yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp));
+ __yy_bcopy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + size - 1;
+ yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+ if (yyssp >= yyss + yystacksize - 1)
+ YYABORT;
+ }
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+ goto yybackup;
+ yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a lookahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* yychar is either YYEMPTY or YYEOF
+ or a valid token in external form. */
+
+ if (yychar == YYEMPTY)
+ {
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Reading a token: ");
+#endif
+ yychar = YYLEX;
+ }
+
+ /* Convert token to internal form (in yychar1) for indexing tables with */
+
+ if (yychar <= 0) /* This means end of input. */
+ {
+ yychar1 = 0;
+ yychar = YYEOF; /* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Now at end of input.\n");
+#endif
+ }
+ else
+ {
+ yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
+ /* Give the individual parser a way to print the precise meaning
+ of a token, for further debugging info. */
+#ifdef YYPRINT
+ YYPRINT (stderr, yychar, yylval);
+#endif
+ fprintf (stderr, ")\n");
+ }
+#endif
+ }
+
+ yyn += yychar1;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+ goto yydefault;
+
+ yyn = yytable[yyn];
+
+ /* yyn is what to do for this token type in this state.
+ Negative => reduce, -yyn is rule number.
+ Positive => shift, yyn is new state.
+ New state is final state => don't bother to shift,
+ just return success.
+ 0, or most negative number => error. */
+
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrlab;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the lookahead token. */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
+#endif
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ /* count tokens shifted since error; after three, turn off error status. */
+ if (yyerrstatus) yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+/* Do the default action for the current state. */
+yydefault:
+
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+
+/* Do a reduction. yyn is the number of a rule to reduce with. */
+yyreduce:
+ yylen = yyr2[yyn];
+ if (yylen > 0)
+ yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ int i;
+
+ fprintf (stderr, "Reducing via rule %d (line %d), ",
+ yyn, yyrline[yyn]);
+
+ /* Print the symbols being reduced, and their result. */
+ for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+ fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+ fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+ }
+#endif
+
+
+ switch (yyn) {
+
+case 1:
+#line 221 "c-parse.y"
+{ if (pedantic)
+ pedwarn ("ANSI C forbids an empty source file");
+ ;
+ break;}
+case 2:
+#line 225 "c-parse.y"
+{
+ /* In case there were missing closebraces,
+ get us back to the global binding level. */
+ while (! global_bindings_p ())
+ poplevel (0, 0, 0);
+ ;
+ break;}
+case 3:
+#line 238 "c-parse.y"
+{yyval.ttype = NULL_TREE; ;
+ break;}
+case 5:
+#line 239 "c-parse.y"
+{yyval.ttype = NULL_TREE; ;
+ break;}
+case 9:
+#line 246 "c-parse.y"
+{ STRIP_NOPS (yyvsp[-2].ttype);
+ if ((TREE_CODE (yyvsp[-2].ttype) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (yyvsp[-2].ttype, 0)) == STRING_CST)
+ || TREE_CODE (yyvsp[-2].ttype) == STRING_CST)
+ assemble_asm (yyvsp[-2].ttype);
+ else
+ error ("argument of `asm' is not a constant string"); ;
+ break;}
+case 10:
+#line 257 "c-parse.y"
+{ if (pedantic)
+ error ("ANSI C forbids data definition with no type or storage class");
+ else if (!flag_traditional)
+ warning ("data definition has no type or storage class"); ;
+ break;}
+case 11:
+#line 262 "c-parse.y"
+{;
+ break;}
+case 12:
+#line 264 "c-parse.y"
+{;
+ break;}
+case 13:
+#line 266 "c-parse.y"
+{ pedwarn ("empty declaration"); ;
+ break;}
+case 14:
+#line 268 "c-parse.y"
+{ shadow_tag (yyvsp[-1].ttype); ;
+ break;}
+case 17:
+#line 272 "c-parse.y"
+{ if (pedantic)
+ pedwarn ("ANSI C does not allow extra `;' outside of a function"); ;
+ break;}
+case 18:
+#line 278 "c-parse.y"
+{ if (! start_function (yyvsp[-2].ttype, yyvsp[0].ttype, 0))
+ YYERROR1;
+ reinit_parse_for_function (); ;
+ break;}
+case 19:
+#line 282 "c-parse.y"
+{ store_parm_decls (); ;
+ break;}
+case 20:
+#line 284 "c-parse.y"
+{ finish_function (0); ;
+ break;}
+case 21:
+#line 286 "c-parse.y"
+{ ;
+ break;}
+case 22:
+#line 288 "c-parse.y"
+{ if (! start_function (yyvsp[-2].ttype, yyvsp[0].ttype, 0))
+ YYERROR1;
+ reinit_parse_for_function (); ;
+ break;}
+case 23:
+#line 292 "c-parse.y"
+{ store_parm_decls (); ;
+ break;}
+case 24:
+#line 294 "c-parse.y"
+{ finish_function (0); ;
+ break;}
+case 25:
+#line 296 "c-parse.y"
+{ ;
+ break;}
+case 26:
+#line 298 "c-parse.y"
+{ if (! start_function (NULL_TREE, yyvsp[0].ttype, 0))
+ YYERROR1;
+ reinit_parse_for_function (); ;
+ break;}
+case 27:
+#line 302 "c-parse.y"
+{ store_parm_decls (); ;
+ break;}
+case 28:
+#line 304 "c-parse.y"
+{ finish_function (0); ;
+ break;}
+case 29:
+#line 306 "c-parse.y"
+{ ;
+ break;}
+case 32:
+#line 315 "c-parse.y"
+{ yyval.code = ADDR_EXPR; ;
+ break;}
+case 33:
+#line 317 "c-parse.y"
+{ yyval.code = NEGATE_EXPR; ;
+ break;}
+case 34:
+#line 319 "c-parse.y"
+{ yyval.code = CONVERT_EXPR; ;
+ break;}
+case 35:
+#line 321 "c-parse.y"
+{ yyval.code = PREINCREMENT_EXPR; ;
+ break;}
+case 36:
+#line 323 "c-parse.y"
+{ yyval.code = PREDECREMENT_EXPR; ;
+ break;}
+case 37:
+#line 325 "c-parse.y"
+{ yyval.code = BIT_NOT_EXPR; ;
+ break;}
+case 38:
+#line 327 "c-parse.y"
+{ yyval.code = TRUTH_NOT_EXPR; ;
+ break;}
+case 39:
+#line 331 "c-parse.y"
+{ yyval.ttype = build_compound_expr (yyvsp[0].ttype); ;
+ break;}
+case 40:
+#line 336 "c-parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 42:
+#line 342 "c-parse.y"
+{ yyval.ttype = build_tree_list (NULL_TREE, yyvsp[0].ttype); ;
+ break;}
+case 43:
+#line 344 "c-parse.y"
+{ chainon (yyvsp[-2].ttype, build_tree_list (NULL_TREE, yyvsp[0].ttype)); ;
+ break;}
+case 45:
+#line 350 "c-parse.y"
+{ yyval.ttype = build_indirect_ref (yyvsp[0].ttype, "unary *"); ;
+ break;}
+case 46:
+#line 353 "c-parse.y"
+{ yyvsp[0].itype = pedantic;
+ pedantic = 0; ;
+ break;}
+case 47:
+#line 356 "c-parse.y"
+{ yyval.ttype = yyvsp[0].ttype;
+ pedantic = yyvsp[-2].itype; ;
+ break;}
+case 48:
+#line 359 "c-parse.y"
+{ yyval.ttype = build_unary_op (yyvsp[-1].code, yyvsp[0].ttype, 0);
+ overflow_warning (yyval.ttype); ;
+ break;}
+case 49:
+#line 363 "c-parse.y"
+{ tree label = lookup_label (yyvsp[0].ttype);
+ if (label == 0)
+ yyval.ttype = null_pointer_node;
+ else
+ {
+ TREE_USED (label) = 1;
+ yyval.ttype = build1 (ADDR_EXPR, ptr_type_node, label);
+ TREE_CONSTANT (yyval.ttype) = 1;
+ }
+ ;
+ break;}
+case 50:
+#line 389 "c-parse.y"
+{ if (TREE_CODE (yyvsp[0].ttype) == COMPONENT_REF
+ && DECL_BIT_FIELD (TREE_OPERAND (yyvsp[0].ttype, 1)))
+ error ("`sizeof' applied to a bit-field");
+ yyval.ttype = c_sizeof (TREE_TYPE (yyvsp[0].ttype)); ;
+ break;}
+case 51:
+#line 394 "c-parse.y"
+{ yyval.ttype = c_sizeof (groktypename (yyvsp[-1].ttype)); ;
+ break;}
+case 52:
+#line 396 "c-parse.y"
+{ yyval.ttype = c_alignof_expr (yyvsp[0].ttype); ;
+ break;}
+case 53:
+#line 398 "c-parse.y"
+{ yyval.ttype = c_alignof (groktypename (yyvsp[-1].ttype)); ;
+ break;}
+case 54:
+#line 400 "c-parse.y"
+{ yyval.ttype = build_unary_op (REALPART_EXPR, yyvsp[0].ttype, 0); ;
+ break;}
+case 55:
+#line 402 "c-parse.y"
+{ yyval.ttype = build_unary_op (IMAGPART_EXPR, yyvsp[0].ttype, 0); ;
+ break;}
+case 57:
+#line 408 "c-parse.y"
+{ tree type = groktypename (yyvsp[-2].ttype);
+ yyval.ttype = build_c_cast (type, yyvsp[0].ttype); ;
+ break;}
+case 58:
+#line 411 "c-parse.y"
+{ start_init (NULL_TREE, NULL, 0);
+ yyvsp[-2].ttype = groktypename (yyvsp[-2].ttype);
+ really_start_incremental_init (yyvsp[-2].ttype); ;
+ break;}
+case 59:
+#line 415 "c-parse.y"
+{ char *name;
+ tree result = pop_init_level (0);
+ tree type = yyvsp[-5].ttype;
+ finish_init ();
+
+ if (pedantic)
+ pedwarn ("ANSI C forbids constructor expressions");
+ if (TYPE_NAME (type) != 0)
+ {
+ if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
+ name = IDENTIFIER_POINTER (TYPE_NAME (type));
+ else
+ name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)));
+ }
+ else
+ name = "";
+ yyval.ttype = result;
+ if (TREE_CODE (type) == ARRAY_TYPE && TYPE_SIZE (type) == 0)
+ {
+ int failure = complete_array_type (type, yyval.ttype, 1);
+ if (failure)
+ abort ();
+ }
+ ;
+ break;}
+case 61:
+#line 444 "c-parse.y"
+{ yyval.ttype = parser_build_binary_op (yyvsp[-1].code, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 62:
+#line 446 "c-parse.y"
+{ yyval.ttype = parser_build_binary_op (yyvsp[-1].code, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 63:
+#line 448 "c-parse.y"
+{ yyval.ttype = parser_build_binary_op (yyvsp[-1].code, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 64:
+#line 450 "c-parse.y"
+{ yyval.ttype = parser_build_binary_op (yyvsp[-1].code, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 65:
+#line 452 "c-parse.y"
+{ yyval.ttype = parser_build_binary_op (yyvsp[-1].code, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 66:
+#line 454 "c-parse.y"
+{ yyval.ttype = parser_build_binary_op (yyvsp[-1].code, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 67:
+#line 456 "c-parse.y"
+{ yyval.ttype = parser_build_binary_op (yyvsp[-1].code, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 68:
+#line 458 "c-parse.y"
+{ yyval.ttype = parser_build_binary_op (yyvsp[-1].code, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 69:
+#line 460 "c-parse.y"
+{ yyval.ttype = parser_build_binary_op (yyvsp[-1].code, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 70:
+#line 462 "c-parse.y"
+{ yyval.ttype = parser_build_binary_op (yyvsp[-1].code, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 71:
+#line 464 "c-parse.y"
+{ yyval.ttype = parser_build_binary_op (yyvsp[-1].code, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 72:
+#line 466 "c-parse.y"
+{ yyval.ttype = parser_build_binary_op (yyvsp[-1].code, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 73:
+#line 468 "c-parse.y"
+{ yyval.ttype = parser_build_binary_op (TRUTH_ANDIF_EXPR, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 74:
+#line 470 "c-parse.y"
+{ yyval.ttype = parser_build_binary_op (TRUTH_ORIF_EXPR, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 75:
+#line 472 "c-parse.y"
+{ yyval.ttype = build_conditional_expr (yyvsp[-4].ttype, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 76:
+#line 474 "c-parse.y"
+{ yyval.ttype = build_modify_expr (yyvsp[-2].ttype, NOP_EXPR, yyvsp[0].ttype);
+ C_SET_EXP_ORIGINAL_CODE (yyval.ttype, MODIFY_EXPR); ;
+ break;}
+case 77:
+#line 477 "c-parse.y"
+{ yyval.ttype = build_modify_expr (yyvsp[-2].ttype, yyvsp[-1].code, yyvsp[0].ttype);
+ /* This inhibits warnings in truthvalue_conversion. */
+ C_SET_EXP_ORIGINAL_CODE (yyval.ttype, ERROR_MARK); ;
+ break;}
+case 78:
+#line 484 "c-parse.y"
+{
+ yyval.ttype = lastiddecl;
+ if (!yyval.ttype || yyval.ttype == error_mark_node)
+ {
+ if (yychar == YYEMPTY)
+ yychar = YYLEX;
+ if (yychar == '(')
+ {
+ {
+ /* Ordinary implicit function declaration. */
+ yyval.ttype = implicitly_declare (yyvsp[0].ttype);
+ assemble_external (yyval.ttype);
+ TREE_USED (yyval.ttype) = 1;
+ }
+ }
+ else if (current_function_decl == 0)
+ {
+ error ("`%s' undeclared here (not in a function)",
+ IDENTIFIER_POINTER (yyvsp[0].ttype));
+ yyval.ttype = error_mark_node;
+ }
+ else
+ {
+ {
+ if (IDENTIFIER_GLOBAL_VALUE (yyvsp[0].ttype) != error_mark_node
+ || IDENTIFIER_ERROR_LOCUS (yyvsp[0].ttype) != current_function_decl)
+ {
+ error ("`%s' undeclared (first use this function)",
+ IDENTIFIER_POINTER (yyvsp[0].ttype));
+
+ if (! undeclared_variable_notice)
+ {
+ error ("(Each undeclared identifier is reported only once");
+ error ("for each function it appears in.)");
+ undeclared_variable_notice = 1;
+ }
+ }
+ yyval.ttype = error_mark_node;
+ /* Prevent repeated error messages. */
+ IDENTIFIER_GLOBAL_VALUE (yyvsp[0].ttype) = error_mark_node;
+ IDENTIFIER_ERROR_LOCUS (yyvsp[0].ttype) = current_function_decl;
+ }
+ }
+ }
+ else if (TREE_TYPE (yyval.ttype) == error_mark_node)
+ yyval.ttype = error_mark_node;
+ else if (C_DECL_ANTICIPATED (yyval.ttype))
+ {
+ /* The first time we see a build-in function used,
+ if it has not been declared. */
+ C_DECL_ANTICIPATED (yyval.ttype) = 0;
+ if (yychar == YYEMPTY)
+ yychar = YYLEX;
+ if (yychar == '(')
+ {
+ /* Omit the implicit declaration we
+ would ordinarily do, so we don't lose
+ the actual built in type.
+ But print a diagnostic for the mismatch. */
+ if (TREE_CODE (yyval.ttype) != FUNCTION_DECL)
+ error ("`%s' implicitly declared as function",
+ IDENTIFIER_POINTER (DECL_NAME (yyval.ttype)));
+ else if ((TYPE_MODE (TREE_TYPE (TREE_TYPE (yyval.ttype)))
+ != TYPE_MODE (integer_type_node))
+ && (TREE_TYPE (TREE_TYPE (yyval.ttype))
+ != void_type_node))
+ pedwarn ("type mismatch in implicit declaration for built-in function `%s'",
+ IDENTIFIER_POINTER (DECL_NAME (yyval.ttype)));
+ /* If it really returns void, change that to int. */
+ if (TREE_TYPE (TREE_TYPE (yyval.ttype)) == void_type_node)
+ TREE_TYPE (yyval.ttype)
+ = build_function_type (integer_type_node,
+ TYPE_ARG_TYPES (TREE_TYPE (yyval.ttype)));
+ }
+ else
+ pedwarn ("built-in function `%s' used without declaration",
+ IDENTIFIER_POINTER (DECL_NAME (yyval.ttype)));
+
+ /* Do what we would ordinarily do when a fn is used. */
+ assemble_external (yyval.ttype);
+ TREE_USED (yyval.ttype) = 1;
+ }
+ else
+ {
+ assemble_external (yyval.ttype);
+ TREE_USED (yyval.ttype) = 1;
+ }
+
+ if (TREE_CODE (yyval.ttype) == CONST_DECL)
+ {
+ yyval.ttype = DECL_INITIAL (yyval.ttype);
+ /* This is to prevent an enum whose value is 0
+ from being considered a null pointer constant. */
+ yyval.ttype = build1 (NOP_EXPR, TREE_TYPE (yyval.ttype), yyval.ttype);
+ TREE_CONSTANT (yyval.ttype) = 1;
+ }
+ ;
+ break;}
+case 80:
+#line 583 "c-parse.y"
+{ yyval.ttype = combine_strings (yyvsp[0].ttype); ;
+ break;}
+case 81:
+#line 585 "c-parse.y"
+{ char class = TREE_CODE_CLASS (TREE_CODE (yyvsp[-1].ttype));
+ if (class == 'e' || class == '1'
+ || class == '2' || class == '<')
+ C_SET_EXP_ORIGINAL_CODE (yyvsp[-1].ttype, ERROR_MARK);
+ yyval.ttype = yyvsp[-1].ttype; ;
+ break;}
+case 82:
+#line 591 "c-parse.y"
+{ yyval.ttype = error_mark_node; ;
+ break;}
+case 83:
+#line 593 "c-parse.y"
+{ if (current_function_decl == 0)
+ {
+ error ("braced-group within expression allowed only inside a function");
+ YYERROR;
+ }
+ /* We must force a BLOCK for this level
+ so that, if it is not expanded later,
+ there is a way to turn off the entire subtree of blocks
+ that are contained in it. */
+ keep_next_level ();
+ push_iterator_stack ();
+ push_label_level ();
+ yyval.ttype = expand_start_stmt_expr (); ;
+ break;}
+case 84:
+#line 607 "c-parse.y"
+{ tree rtl_exp;
+ if (pedantic)
+ pedwarn ("ANSI C forbids braced-groups within expressions");
+ pop_iterator_stack ();
+ pop_label_level ();
+ rtl_exp = expand_end_stmt_expr (yyvsp[-2].ttype);
+ /* The statements have side effects, so the group does. */
+ TREE_SIDE_EFFECTS (rtl_exp) = 1;
+
+ if (TREE_CODE (yyvsp[-1].ttype) == BLOCK)
+ {
+ /* Make a BIND_EXPR for the BLOCK already made. */
+ yyval.ttype = build (BIND_EXPR, TREE_TYPE (rtl_exp),
+ NULL_TREE, rtl_exp, yyvsp[-1].ttype);
+ /* Remove the block from the tree at this point.
+ It gets put back at the proper place
+ when the BIND_EXPR is expanded. */
+ delete_block (yyvsp[-1].ttype);
+ }
+ else
+ yyval.ttype = yyvsp[-1].ttype;
+ ;
+ break;}
+case 85:
+#line 630 "c-parse.y"
+{ yyval.ttype = build_function_call (yyvsp[-3].ttype, yyvsp[-1].ttype); ;
+ break;}
+case 86:
+#line 632 "c-parse.y"
+{ yyval.ttype = build_array_ref (yyvsp[-3].ttype, yyvsp[-1].ttype); ;
+ break;}
+case 87:
+#line 634 "c-parse.y"
+{
+ yyval.ttype = build_component_ref (yyvsp[-2].ttype, yyvsp[0].ttype);
+ ;
+ break;}
+case 88:
+#line 638 "c-parse.y"
+{
+ tree expr = build_indirect_ref (yyvsp[-2].ttype, "->");
+
+ yyval.ttype = build_component_ref (expr, yyvsp[0].ttype);
+ ;
+ break;}
+case 89:
+#line 644 "c-parse.y"
+{ yyval.ttype = build_unary_op (POSTINCREMENT_EXPR, yyvsp[-1].ttype, 0); ;
+ break;}
+case 90:
+#line 646 "c-parse.y"
+{ yyval.ttype = build_unary_op (POSTDECREMENT_EXPR, yyvsp[-1].ttype, 0); ;
+ break;}
+case 92:
+#line 653 "c-parse.y"
+{ yyval.ttype = chainon (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 95:
+#line 662 "c-parse.y"
+{ c_mark_varargs ();
+ if (pedantic)
+ pedwarn ("ANSI C does not permit use of `varargs.h'"); ;
+ break;}
+case 96:
+#line 672 "c-parse.y"
+{ ;
+ break;}
+case 101:
+#line 684 "c-parse.y"
+{ current_declspecs = TREE_VALUE (declspec_stack);
+ declspec_stack = TREE_CHAIN (declspec_stack);
+ resume_momentary (yyvsp[-2].itype); ;
+ break;}
+case 102:
+#line 688 "c-parse.y"
+{ current_declspecs = TREE_VALUE (declspec_stack);
+ declspec_stack = TREE_CHAIN (declspec_stack);
+ resume_momentary (yyvsp[-2].itype); ;
+ break;}
+case 103:
+#line 692 "c-parse.y"
+{ shadow_tag_warned (yyvsp[-1].ttype, 1);
+ pedwarn ("empty declaration"); ;
+ break;}
+case 104:
+#line 695 "c-parse.y"
+{ pedwarn ("empty declaration"); ;
+ break;}
+case 105:
+#line 704 "c-parse.y"
+{ ;
+ break;}
+case 110:
+#line 719 "c-parse.y"
+{ yyval.itype = suspend_momentary ();
+ pending_xref_error ();
+ declspec_stack = tree_cons (NULL_TREE, current_declspecs,
+ declspec_stack);
+ current_declspecs = yyvsp[0].ttype; ;
+ break;}
+case 111:
+#line 728 "c-parse.y"
+{ current_declspecs = TREE_VALUE (declspec_stack);
+ declspec_stack = TREE_CHAIN (declspec_stack);
+ resume_momentary (yyvsp[-2].itype); ;
+ break;}
+case 112:
+#line 732 "c-parse.y"
+{ current_declspecs = TREE_VALUE (declspec_stack);
+ declspec_stack = TREE_CHAIN (declspec_stack);
+ resume_momentary (yyvsp[-2].itype); ;
+ break;}
+case 113:
+#line 736 "c-parse.y"
+{ current_declspecs = TREE_VALUE (declspec_stack);
+ declspec_stack = TREE_CHAIN (declspec_stack);
+ resume_momentary (yyvsp[-1].itype); ;
+ break;}
+case 114:
+#line 740 "c-parse.y"
+{ current_declspecs = TREE_VALUE (declspec_stack);
+ declspec_stack = TREE_CHAIN (declspec_stack);
+ resume_momentary (yyvsp[-1].itype); ;
+ break;}
+case 115:
+#line 744 "c-parse.y"
+{ shadow_tag (yyvsp[-1].ttype); ;
+ break;}
+case 116:
+#line 746 "c-parse.y"
+{ pedwarn ("empty declaration"); ;
+ break;}
+case 117:
+#line 755 "c-parse.y"
+{ yyval.ttype = tree_cons (NULL_TREE, yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 118:
+#line 757 "c-parse.y"
+{ yyval.ttype = chainon (yyvsp[0].ttype, tree_cons (NULL_TREE, yyvsp[-1].ttype, yyvsp[-2].ttype)); ;
+ break;}
+case 119:
+#line 761 "c-parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 120:
+#line 763 "c-parse.y"
+{ yyval.ttype = tree_cons (NULL_TREE, yyvsp[0].ttype, yyvsp[-1].ttype); ;
+ break;}
+case 121:
+#line 765 "c-parse.y"
+{ if (extra_warnings)
+ warning ("`%s' is not at beginning of declaration",
+ IDENTIFIER_POINTER (yyvsp[0].ttype));
+ yyval.ttype = tree_cons (NULL_TREE, yyvsp[0].ttype, yyvsp[-1].ttype); ;
+ break;}
+case 122:
+#line 777 "c-parse.y"
+{ yyval.ttype = tree_cons (NULL_TREE, yyvsp[0].ttype, NULL_TREE);
+ TREE_STATIC (yyval.ttype) = 1; ;
+ break;}
+case 123:
+#line 780 "c-parse.y"
+{ yyval.ttype = tree_cons (NULL_TREE, yyvsp[0].ttype, NULL_TREE); ;
+ break;}
+case 124:
+#line 782 "c-parse.y"
+{ yyval.ttype = tree_cons (NULL_TREE, yyvsp[0].ttype, yyvsp[-1].ttype);
+ TREE_STATIC (yyval.ttype) = 1; ;
+ break;}
+case 125:
+#line 785 "c-parse.y"
+{ if (extra_warnings && TREE_STATIC (yyvsp[-1].ttype))
+ warning ("`%s' is not at beginning of declaration",
+ IDENTIFIER_POINTER (yyvsp[0].ttype));
+ yyval.ttype = tree_cons (NULL_TREE, yyvsp[0].ttype, yyvsp[-1].ttype);
+ TREE_STATIC (yyval.ttype) = TREE_STATIC (yyvsp[-1].ttype); ;
+ break;}
+case 126:
+#line 799 "c-parse.y"
+{ yyval.ttype = tree_cons (NULL_TREE, yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 127:
+#line 801 "c-parse.y"
+{ yyval.ttype = chainon (yyvsp[0].ttype, tree_cons (NULL_TREE, yyvsp[-1].ttype, yyvsp[-2].ttype)); ;
+ break;}
+case 128:
+#line 805 "c-parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 129:
+#line 807 "c-parse.y"
+{ yyval.ttype = tree_cons (NULL_TREE, yyvsp[0].ttype, yyvsp[-1].ttype); ;
+ break;}
+case 132:
+#line 817 "c-parse.y"
+{ /* For a typedef name, record the meaning, not the name.
+ In case of `foo foo, bar;'. */
+ yyval.ttype = lookup_name (yyvsp[0].ttype); ;
+ break;}
+case 133:
+#line 821 "c-parse.y"
+{ yyval.ttype = TREE_TYPE (yyvsp[-1].ttype); ;
+ break;}
+case 134:
+#line 823 "c-parse.y"
+{ yyval.ttype = groktypename (yyvsp[-1].ttype); ;
+ break;}
+case 142:
+#line 845 "c-parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 143:
+#line 847 "c-parse.y"
+{ if (TREE_CHAIN (yyvsp[-1].ttype)) yyvsp[-1].ttype = combine_strings (yyvsp[-1].ttype);
+ yyval.ttype = yyvsp[-1].ttype;
+ ;
+ break;}
+case 144:
+#line 854 "c-parse.y"
+{ yyval.ttype = start_decl (yyvsp[-3].ttype, current_declspecs, 1);
+ decl_attributes (yyval.ttype, yyvsp[-1].ttype);
+ start_init (yyval.ttype, yyvsp[-2].ttype, global_bindings_p ()); ;
+ break;}
+case 145:
+#line 859 "c-parse.y"
+{ finish_init ();
+ decl_attributes (yyvsp[-1].ttype, yyvsp[-3].ttype);
+ finish_decl (yyvsp[-1].ttype, yyvsp[0].ttype, yyvsp[-4].ttype); ;
+ break;}
+case 146:
+#line 863 "c-parse.y"
+{ tree d = start_decl (yyvsp[-2].ttype, current_declspecs, 0);
+ decl_attributes (d, yyvsp[0].ttype);
+ finish_decl (d, NULL_TREE, yyvsp[-1].ttype); ;
+ break;}
+case 147:
+#line 870 "c-parse.y"
+{ yyval.ttype = start_decl (yyvsp[-3].ttype, current_declspecs, 1);
+ decl_attributes (yyval.ttype, yyvsp[-1].ttype);
+ start_init (yyval.ttype, yyvsp[-2].ttype, global_bindings_p ()); ;
+ break;}
+case 148:
+#line 875 "c-parse.y"
+{ finish_init ();
+ decl_attributes (yyvsp[-1].ttype, yyvsp[-3].ttype);
+ finish_decl (yyvsp[-1].ttype, yyvsp[0].ttype, yyvsp[-4].ttype); ;
+ break;}
+case 149:
+#line 879 "c-parse.y"
+{ tree d = start_decl (yyvsp[-2].ttype, current_declspecs, 0);
+ decl_attributes (d, yyvsp[0].ttype);
+ finish_decl (d, NULL_TREE, yyvsp[-1].ttype); ;
+ break;}
+case 150:
+#line 887 "c-parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 151:
+#line 889 "c-parse.y"
+{ yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 152:
+#line 894 "c-parse.y"
+{ yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 153:
+#line 896 "c-parse.y"
+{ yyval.ttype = chainon (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 154:
+#line 901 "c-parse.y"
+{ yyval.ttype = yyvsp[-2].ttype; ;
+ break;}
+case 155:
+#line 906 "c-parse.y"
+{ yyval.ttype = build_tree_list (NULL_TREE, yyvsp[0].ttype); ;
+ break;}
+case 156:
+#line 908 "c-parse.y"
+{ yyval.ttype = chainon (yyvsp[-2].ttype, build_tree_list (NULL_TREE, yyvsp[0].ttype)); ;
+ break;}
+case 157:
+#line 913 "c-parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 158:
+#line 915 "c-parse.y"
+{ yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 159:
+#line 917 "c-parse.y"
+{ yyval.ttype = tree_cons (yyvsp[-3].ttype, NULL_TREE,
+ build_tree_list (NULL_TREE, yyvsp[-1].ttype)); ;
+ break;}
+case 160:
+#line 920 "c-parse.y"
+{ yyval.ttype = tree_cons (yyvsp[-5].ttype, NULL_TREE,
+ tree_cons (NULL_TREE, yyvsp[-3].ttype, yyvsp[-1].ttype)); ;
+ break;}
+case 161:
+#line 923 "c-parse.y"
+{ yyval.ttype = tree_cons (yyvsp[-3].ttype, NULL_TREE, yyvsp[-1].ttype); ;
+ break;}
+case 167:
+#line 941 "c-parse.y"
+{ really_start_incremental_init (NULL_TREE);
+ /* Note that the call to clear_momentary
+ is in process_init_element. */
+ push_momentary (); ;
+ break;}
+case 168:
+#line 946 "c-parse.y"
+{ yyval.ttype = pop_init_level (0);
+ if (yyval.ttype == error_mark_node
+ && ! (yychar == STRING || yychar == CONSTANT))
+ pop_momentary ();
+ else
+ pop_momentary_nofree (); ;
+ break;}
+case 169:
+#line 954 "c-parse.y"
+{ yyval.ttype = error_mark_node; ;
+ break;}
+case 170:
+#line 960 "c-parse.y"
+{ if (pedantic)
+ pedwarn ("ANSI C forbids empty initializer braces"); ;
+ break;}
+case 174:
+#line 974 "c-parse.y"
+{ process_init_element (yyvsp[0].ttype); ;
+ break;}
+case 175:
+#line 976 "c-parse.y"
+{ push_init_level (0); ;
+ break;}
+case 176:
+#line 978 "c-parse.y"
+{ process_init_element (pop_init_level (0)); ;
+ break;}
+case 178:
+#line 984 "c-parse.y"
+{ set_init_index (yyvsp[-4].ttype, yyvsp[-2].ttype); ;
+ break;}
+case 180:
+#line 987 "c-parse.y"
+{ set_init_index (yyvsp[-2].ttype, NULL_TREE); ;
+ break;}
+case 182:
+#line 990 "c-parse.y"
+{ set_init_index (yyvsp[-1].ttype, NULL_TREE); ;
+ break;}
+case 184:
+#line 993 "c-parse.y"
+{ set_init_label (yyvsp[-1].ttype); ;
+ break;}
+case 186:
+#line 996 "c-parse.y"
+{ set_init_label (yyvsp[-1].ttype); ;
+ break;}
+case 188:
+#line 1002 "c-parse.y"
+{ push_c_function_context ();
+ if (! start_function (current_declspecs, yyvsp[0].ttype, 1))
+ {
+ pop_c_function_context ();
+ YYERROR1;
+ }
+ reinit_parse_for_function ();
+ store_parm_decls (); ;
+ break;}
+case 189:
+#line 1017 "c-parse.y"
+{ finish_function (1);
+ pop_c_function_context (); ;
+ break;}
+case 190:
+#line 1023 "c-parse.y"
+{ push_c_function_context ();
+ if (! start_function (current_declspecs, yyvsp[0].ttype, 1))
+ {
+ pop_c_function_context ();
+ YYERROR1;
+ }
+ reinit_parse_for_function ();
+ store_parm_decls (); ;
+ break;}
+case 191:
+#line 1038 "c-parse.y"
+{ finish_function (1);
+ pop_c_function_context (); ;
+ break;}
+case 194:
+#line 1054 "c-parse.y"
+{ yyval.ttype = yyvsp[-1].ttype; ;
+ break;}
+case 195:
+#line 1056 "c-parse.y"
+{ yyval.ttype = build_nt (CALL_EXPR, yyvsp[-2].ttype, yyvsp[0].ttype, NULL_TREE); ;
+ break;}
+case 196:
+#line 1061 "c-parse.y"
+{ yyval.ttype = build_nt (ARRAY_REF, yyvsp[-3].ttype, yyvsp[-1].ttype); ;
+ break;}
+case 197:
+#line 1063 "c-parse.y"
+{ yyval.ttype = build_nt (ARRAY_REF, yyvsp[-2].ttype, NULL_TREE); ;
+ break;}
+case 198:
+#line 1065 "c-parse.y"
+{ yyval.ttype = make_pointer_declarator (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 200:
+#line 1076 "c-parse.y"
+{ yyval.ttype = build_nt (CALL_EXPR, yyvsp[-2].ttype, yyvsp[0].ttype, NULL_TREE); ;
+ break;}
+case 201:
+#line 1081 "c-parse.y"
+{ yyval.ttype = build_nt (ARRAY_REF, yyvsp[-3].ttype, yyvsp[-1].ttype); ;
+ break;}
+case 202:
+#line 1083 "c-parse.y"
+{ yyval.ttype = build_nt (ARRAY_REF, yyvsp[-2].ttype, NULL_TREE); ;
+ break;}
+case 203:
+#line 1085 "c-parse.y"
+{ yyval.ttype = make_pointer_declarator (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 205:
+#line 1094 "c-parse.y"
+{ yyval.ttype = build_nt (CALL_EXPR, yyvsp[-2].ttype, yyvsp[0].ttype, NULL_TREE); ;
+ break;}
+case 206:
+#line 1099 "c-parse.y"
+{ yyval.ttype = yyvsp[-1].ttype; ;
+ break;}
+case 207:
+#line 1101 "c-parse.y"
+{ yyval.ttype = make_pointer_declarator (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 208:
+#line 1103 "c-parse.y"
+{ yyval.ttype = build_nt (ARRAY_REF, yyvsp[-3].ttype, yyvsp[-1].ttype); ;
+ break;}
+case 209:
+#line 1105 "c-parse.y"
+{ yyval.ttype = build_nt (ARRAY_REF, yyvsp[-2].ttype, NULL_TREE); ;
+ break;}
+case 211:
+#line 1111 "c-parse.y"
+{ yyval.ttype = start_struct (RECORD_TYPE, yyvsp[-1].ttype);
+ /* Start scope of tag before parsing components. */
+ ;
+ break;}
+case 212:
+#line 1115 "c-parse.y"
+{ yyval.ttype = finish_struct (yyvsp[-2].ttype, yyvsp[-1].ttype);
+ /* Really define the structure. */
+ ;
+ break;}
+case 213:
+#line 1119 "c-parse.y"
+{ yyval.ttype = finish_struct (start_struct (RECORD_TYPE, NULL_TREE),
+ yyvsp[-1].ttype); ;
+ break;}
+case 214:
+#line 1122 "c-parse.y"
+{ yyval.ttype = xref_tag (RECORD_TYPE, yyvsp[0].ttype); ;
+ break;}
+case 215:
+#line 1124 "c-parse.y"
+{ yyval.ttype = start_struct (UNION_TYPE, yyvsp[-1].ttype); ;
+ break;}
+case 216:
+#line 1126 "c-parse.y"
+{ yyval.ttype = finish_struct (yyvsp[-2].ttype, yyvsp[-1].ttype); ;
+ break;}
+case 217:
+#line 1128 "c-parse.y"
+{ yyval.ttype = finish_struct (start_struct (UNION_TYPE, NULL_TREE),
+ yyvsp[-1].ttype); ;
+ break;}
+case 218:
+#line 1131 "c-parse.y"
+{ yyval.ttype = xref_tag (UNION_TYPE, yyvsp[0].ttype); ;
+ break;}
+case 219:
+#line 1133 "c-parse.y"
+{ yyvsp[0].itype = suspend_momentary ();
+ yyval.ttype = start_enum (yyvsp[-1].ttype); ;
+ break;}
+case 220:
+#line 1136 "c-parse.y"
+{ yyval.ttype = finish_enum (yyvsp[-3].ttype, nreverse (yyvsp[-2].ttype));
+ resume_momentary (yyvsp[-4].itype); ;
+ break;}
+case 221:
+#line 1139 "c-parse.y"
+{ yyvsp[0].itype = suspend_momentary ();
+ yyval.ttype = start_enum (NULL_TREE); ;
+ break;}
+case 222:
+#line 1142 "c-parse.y"
+{ yyval.ttype = finish_enum (yyvsp[-3].ttype, nreverse (yyvsp[-2].ttype));
+ resume_momentary (yyvsp[-4].itype); ;
+ break;}
+case 223:
+#line 1145 "c-parse.y"
+{ yyval.ttype = xref_tag (ENUMERAL_TYPE, yyvsp[0].ttype); ;
+ break;}
+case 227:
+#line 1156 "c-parse.y"
+{ if (pedantic) pedwarn ("comma at end of enumerator list"); ;
+ break;}
+case 228:
+#line 1161 "c-parse.y"
+{ yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 229:
+#line 1163 "c-parse.y"
+{ yyval.ttype = chainon (yyvsp[-1].ttype, yyvsp[0].ttype);
+ pedwarn ("no semicolon at end of struct or union"); ;
+ break;}
+case 230:
+#line 1168 "c-parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 231:
+#line 1170 "c-parse.y"
+{ yyval.ttype = chainon (yyvsp[-2].ttype, yyvsp[-1].ttype); ;
+ break;}
+case 232:
+#line 1172 "c-parse.y"
+{ if (pedantic)
+ pedwarn ("extra semicolon in struct or union specified"); ;
+ break;}
+case 233:
+#line 1187 "c-parse.y"
+{ yyval.ttype = yyvsp[0].ttype;
+ current_declspecs = TREE_VALUE (declspec_stack);
+ declspec_stack = TREE_CHAIN (declspec_stack);
+ resume_momentary (yyvsp[-1].itype); ;
+ break;}
+case 234:
+#line 1192 "c-parse.y"
+{ if (pedantic)
+ pedwarn ("ANSI C forbids member declarations with no members");
+ shadow_tag(yyvsp[0].ttype);
+ yyval.ttype = NULL_TREE; ;
+ break;}
+case 235:
+#line 1197 "c-parse.y"
+{ yyval.ttype = yyvsp[0].ttype;
+ current_declspecs = TREE_VALUE (declspec_stack);
+ declspec_stack = TREE_CHAIN (declspec_stack);
+ resume_momentary (yyvsp[-1].itype); ;
+ break;}
+case 236:
+#line 1202 "c-parse.y"
+{ if (pedantic)
+ pedwarn ("ANSI C forbids member declarations with no members");
+ shadow_tag(yyvsp[0].ttype);
+ yyval.ttype = NULL_TREE; ;
+ break;}
+case 237:
+#line 1207 "c-parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 239:
+#line 1213 "c-parse.y"
+{ yyval.ttype = chainon (yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 240:
+#line 1218 "c-parse.y"
+{ yyval.ttype = grokfield (yyvsp[-3].filename, yyvsp[-2].lineno, yyvsp[-1].ttype, current_declspecs, NULL_TREE);
+ decl_attributes (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 241:
+#line 1222 "c-parse.y"
+{ yyval.ttype = grokfield (yyvsp[-5].filename, yyvsp[-4].lineno, yyvsp[-3].ttype, current_declspecs, yyvsp[-1].ttype);
+ decl_attributes (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 242:
+#line 1225 "c-parse.y"
+{ yyval.ttype = grokfield (yyvsp[-4].filename, yyvsp[-3].lineno, NULL_TREE, current_declspecs, yyvsp[-1].ttype);
+ decl_attributes (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 244:
+#line 1237 "c-parse.y"
+{ yyval.ttype = chainon (yyvsp[0].ttype, yyvsp[-2].ttype); ;
+ break;}
+case 245:
+#line 1239 "c-parse.y"
+{ yyval.ttype = error_mark_node; ;
+ break;}
+case 246:
+#line 1245 "c-parse.y"
+{ yyval.ttype = build_enumerator (yyvsp[0].ttype, NULL_TREE); ;
+ break;}
+case 247:
+#line 1247 "c-parse.y"
+{ yyval.ttype = build_enumerator (yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 248:
+#line 1252 "c-parse.y"
+{ yyval.ttype = build_tree_list (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 249:
+#line 1254 "c-parse.y"
+{ yyval.ttype = build_tree_list (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 250:
+#line 1259 "c-parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 252:
+#line 1265 "c-parse.y"
+{ yyval.ttype = tree_cons (NULL_TREE, yyvsp[0].ttype, NULL_TREE); ;
+ break;}
+case 253:
+#line 1267 "c-parse.y"
+{ yyval.ttype = tree_cons (NULL_TREE, yyvsp[0].ttype, yyvsp[-1].ttype); ;
+ break;}
+case 254:
+#line 1272 "c-parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 255:
+#line 1274 "c-parse.y"
+{ yyval.ttype = tree_cons (NULL_TREE, yyvsp[0].ttype, yyvsp[-1].ttype); ;
+ break;}
+case 256:
+#line 1279 "c-parse.y"
+{ yyval.ttype = yyvsp[-1].ttype; ;
+ break;}
+case 257:
+#line 1282 "c-parse.y"
+{ yyval.ttype = make_pointer_declarator (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 258:
+#line 1284 "c-parse.y"
+{ yyval.ttype = make_pointer_declarator (yyvsp[0].ttype, NULL_TREE); ;
+ break;}
+case 259:
+#line 1286 "c-parse.y"
+{ yyval.ttype = build_nt (CALL_EXPR, yyvsp[-2].ttype, yyvsp[0].ttype, NULL_TREE); ;
+ break;}
+case 260:
+#line 1288 "c-parse.y"
+{ yyval.ttype = build_nt (ARRAY_REF, yyvsp[-3].ttype, yyvsp[-1].ttype); ;
+ break;}
+case 261:
+#line 1290 "c-parse.y"
+{ yyval.ttype = build_nt (ARRAY_REF, yyvsp[-2].ttype, NULL_TREE); ;
+ break;}
+case 262:
+#line 1292 "c-parse.y"
+{ yyval.ttype = build_nt (CALL_EXPR, NULL_TREE, yyvsp[0].ttype, NULL_TREE); ;
+ break;}
+case 263:
+#line 1294 "c-parse.y"
+{ yyval.ttype = build_nt (ARRAY_REF, NULL_TREE, yyvsp[-1].ttype); ;
+ break;}
+case 264:
+#line 1296 "c-parse.y"
+{ yyval.ttype = build_nt (ARRAY_REF, NULL_TREE, NULL_TREE); ;
+ break;}
+case 271:
+#line 1318 "c-parse.y"
+{ emit_line_note (input_filename, lineno);
+ pushlevel (0);
+ clear_last_expr ();
+ push_momentary ();
+ expand_start_bindings (0);
+ ;
+ break;}
+case 273:
+#line 1331 "c-parse.y"
+{ if (pedantic)
+ pedwarn ("ANSI C forbids label declarations"); ;
+ break;}
+case 276:
+#line 1342 "c-parse.y"
+{ tree link;
+ for (link = yyvsp[-1].ttype; link; link = TREE_CHAIN (link))
+ {
+ tree label = shadow_label (TREE_VALUE (link));
+ C_DECLARED_LABEL_FLAG (label) = 1;
+ declare_nonlocal_label (label);
+ }
+ ;
+ break;}
+case 277:
+#line 1356 "c-parse.y"
+{;
+ break;}
+case 279:
+#line 1361 "c-parse.y"
+{ yyval.ttype = convert (void_type_node, integer_zero_node); ;
+ break;}
+case 280:
+#line 1363 "c-parse.y"
+{ emit_line_note (input_filename, lineno);
+ expand_end_bindings (getdecls (), 1, 0);
+ yyval.ttype = poplevel (1, 1, 0);
+ if (yychar == CONSTANT || yychar == STRING)
+ pop_momentary_nofree ();
+ else
+ pop_momentary (); ;
+ break;}
+case 281:
+#line 1371 "c-parse.y"
+{ emit_line_note (input_filename, lineno);
+ expand_end_bindings (getdecls (), kept_level_p (), 0);
+ yyval.ttype = poplevel (kept_level_p (), 0, 0);
+ if (yychar == CONSTANT || yychar == STRING)
+ pop_momentary_nofree ();
+ else
+ pop_momentary (); ;
+ break;}
+case 282:
+#line 1379 "c-parse.y"
+{ emit_line_note (input_filename, lineno);
+ expand_end_bindings (getdecls (), kept_level_p (), 0);
+ yyval.ttype = poplevel (kept_level_p (), 0, 0);
+ if (yychar == CONSTANT || yychar == STRING)
+ pop_momentary_nofree ();
+ else
+ pop_momentary (); ;
+ break;}
+case 285:
+#line 1399 "c-parse.y"
+{ emit_line_note (yyvsp[-5].filename, yyvsp[-4].lineno);
+ expand_start_cond (truthvalue_conversion (yyvsp[-1].ttype), 0);
+ yyval.itype = stmt_count;
+ if_stmt_file = yyvsp[-5].filename;
+ if_stmt_line = yyvsp[-4].lineno;
+ position_after_white_space (); ;
+ break;}
+case 286:
+#line 1412 "c-parse.y"
+{ stmt_count++;
+ emit_line_note (yyvsp[-2].filename, yyvsp[-1].lineno);
+ /* See comment in `while' alternative, above. */
+ emit_nop ();
+ expand_start_loop_continue_elsewhere (1);
+ position_after_white_space (); ;
+ break;}
+case 287:
+#line 1419 "c-parse.y"
+{ expand_loop_continue_here (); ;
+ break;}
+case 288:
+#line 1423 "c-parse.y"
+{ yyval.filename = input_filename; ;
+ break;}
+case 289:
+#line 1427 "c-parse.y"
+{ yyval.lineno = lineno; ;
+ break;}
+case 290:
+#line 1432 "c-parse.y"
+{ ;
+ break;}
+case 291:
+#line 1437 "c-parse.y"
+{ ;
+ break;}
+case 292:
+#line 1442 "c-parse.y"
+{ ;
+ break;}
+case 294:
+#line 1448 "c-parse.y"
+{ int next;
+ position_after_white_space ();
+ next = getc (finput);
+ ungetc (next, finput);
+ if (pedantic && next == '}')
+ pedwarn ("ANSI C forbids label at end of compound statement");
+ ;
+ break;}
+case 295:
+#line 1460 "c-parse.y"
+{ stmt_count++; ;
+ break;}
+case 297:
+#line 1463 "c-parse.y"
+{ stmt_count++;
+ emit_line_note (yyvsp[-3].filename, yyvsp[-2].lineno);
+/* It appears that this should not be done--that a non-lvalue array
+ shouldn't get an error if the value isn't used.
+ Section 3.2.2.1 says that an array lvalue gets converted to a pointer
+ if it appears as a top-level expression,
+ but says nothing about non-lvalue arrays. */
+#if 0
+ /* Call default_conversion to get an error
+ on referring to a register array if pedantic. */
+ if (TREE_CODE (TREE_TYPE (yyvsp[-1].ttype)) == ARRAY_TYPE
+ || TREE_CODE (TREE_TYPE (yyvsp[-1].ttype)) == FUNCTION_TYPE)
+ yyvsp[-1].ttype = default_conversion (yyvsp[-1].ttype);
+#endif
+ iterator_expand (yyvsp[-1].ttype);
+ clear_momentary (); ;
+ break;}
+case 298:
+#line 1480 "c-parse.y"
+{ expand_start_else ();
+ yyvsp[-1].itype = stmt_count;
+ position_after_white_space (); ;
+ break;}
+case 299:
+#line 1484 "c-parse.y"
+{ expand_end_cond ();
+ if (extra_warnings && stmt_count == yyvsp[-3].itype)
+ warning ("empty body in an else-statement"); ;
+ break;}
+case 300:
+#line 1488 "c-parse.y"
+{ expand_end_cond ();
+ /* This warning is here instead of in simple_if, because we
+ do not want a warning if an empty if is followed by an
+ else statement. Increment stmt_count so we don't
+ give a second error if this is a nested `if'. */
+ if (extra_warnings && stmt_count++ == yyvsp[0].itype)
+ warning_with_file_and_line (if_stmt_file, if_stmt_line,
+ "empty body in an if-statement"); ;
+ break;}
+case 301:
+#line 1500 "c-parse.y"
+{ expand_end_cond (); ;
+ break;}
+case 302:
+#line 1502 "c-parse.y"
+{ stmt_count++;
+ emit_line_note (yyvsp[-2].filename, yyvsp[-1].lineno);
+ /* The emit_nop used to come before emit_line_note,
+ but that made the nop seem like part of the preceding line.
+ And that was confusing when the preceding line was
+ inside of an if statement and was not really executed.
+ I think it ought to work to put the nop after the line number.
+ We will see. --rms, July 15, 1991. */
+ emit_nop (); ;
+ break;}
+case 303:
+#line 1512 "c-parse.y"
+{ /* Don't start the loop till we have succeeded
+ in parsing the end test. This is to make sure
+ that we end every loop we start. */
+ expand_start_loop (1);
+ emit_line_note (input_filename, lineno);
+ expand_exit_loop_if_false (NULL_PTR,
+ truthvalue_conversion (yyvsp[-1].ttype));
+ position_after_white_space (); ;
+ break;}
+case 304:
+#line 1521 "c-parse.y"
+{ expand_end_loop (); ;
+ break;}
+case 305:
+#line 1524 "c-parse.y"
+{ emit_line_note (input_filename, lineno);
+ expand_exit_loop_if_false (NULL_PTR,
+ truthvalue_conversion (yyvsp[-2].ttype));
+ expand_end_loop ();
+ clear_momentary (); ;
+ break;}
+case 306:
+#line 1531 "c-parse.y"
+{ expand_end_loop ();
+ clear_momentary (); ;
+ break;}
+case 307:
+#line 1535 "c-parse.y"
+{ stmt_count++;
+ emit_line_note (yyvsp[-5].filename, yyvsp[-4].lineno);
+ /* See comment in `while' alternative, above. */
+ emit_nop ();
+ if (yyvsp[-1].ttype) c_expand_expr_stmt (yyvsp[-1].ttype);
+ /* Next step is to call expand_start_loop_continue_elsewhere,
+ but wait till after we parse the entire for (...).
+ Otherwise, invalid input might cause us to call that
+ fn without calling expand_end_loop. */
+ ;
+ break;}
+case 308:
+#line 1547 "c-parse.y"
+{ yyvsp[0].lineno = lineno;
+ yyval.filename = input_filename; ;
+ break;}
+case 309:
+#line 1550 "c-parse.y"
+{
+ /* Start the loop. Doing this after parsing
+ all the expressions ensures we will end the loop. */
+ expand_start_loop_continue_elsewhere (1);
+ /* Emit the end-test, with a line number. */
+ emit_line_note (yyvsp[-2].filename, yyvsp[-3].lineno);
+ if (yyvsp[-4].ttype)
+ expand_exit_loop_if_false (NULL_PTR,
+ truthvalue_conversion (yyvsp[-4].ttype));
+ /* Don't let the tree nodes for $9 be discarded by
+ clear_momentary during the parsing of the next stmt. */
+ push_momentary ();
+ yyvsp[-3].lineno = lineno;
+ yyvsp[-2].filename = input_filename;
+ position_after_white_space (); ;
+ break;}
+case 310:
+#line 1566 "c-parse.y"
+{ /* Emit the increment expression, with a line number. */
+ emit_line_note (yyvsp[-4].filename, yyvsp[-5].lineno);
+ expand_loop_continue_here ();
+ if (yyvsp[-3].ttype)
+ c_expand_expr_stmt (yyvsp[-3].ttype);
+ if (yychar == CONSTANT || yychar == STRING)
+ pop_momentary_nofree ();
+ else
+ pop_momentary ();
+ expand_end_loop (); ;
+ break;}
+case 311:
+#line 1577 "c-parse.y"
+{ stmt_count++;
+ emit_line_note (yyvsp[-5].filename, yyvsp[-4].lineno);
+ c_expand_start_case (yyvsp[-1].ttype);
+ /* Don't let the tree nodes for $3 be discarded by
+ clear_momentary during the parsing of the next stmt. */
+ push_momentary ();
+ position_after_white_space (); ;
+ break;}
+case 312:
+#line 1585 "c-parse.y"
+{ expand_end_case (yyvsp[-3].ttype);
+ if (yychar == CONSTANT || yychar == STRING)
+ pop_momentary_nofree ();
+ else
+ pop_momentary (); ;
+ break;}
+case 313:
+#line 1591 "c-parse.y"
+{ stmt_count++;
+ emit_line_note (yyvsp[-3].filename, yyvsp[-2].lineno);
+ if ( ! expand_exit_something ())
+ error ("break statement not within loop or switch"); ;
+ break;}
+case 314:
+#line 1596 "c-parse.y"
+{ stmt_count++;
+ emit_line_note (yyvsp[-3].filename, yyvsp[-2].lineno);
+ if (! expand_continue_loop (NULL_PTR))
+ error ("continue statement not within a loop"); ;
+ break;}
+case 315:
+#line 1601 "c-parse.y"
+{ stmt_count++;
+ emit_line_note (yyvsp[-3].filename, yyvsp[-2].lineno);
+ c_expand_return (NULL_TREE); ;
+ break;}
+case 316:
+#line 1605 "c-parse.y"
+{ stmt_count++;
+ emit_line_note (yyvsp[-4].filename, yyvsp[-3].lineno);
+ c_expand_return (yyvsp[-1].ttype); ;
+ break;}
+case 317:
+#line 1609 "c-parse.y"
+{ stmt_count++;
+ emit_line_note (yyvsp[-7].filename, yyvsp[-6].lineno);
+ STRIP_NOPS (yyvsp[-2].ttype);
+ if ((TREE_CODE (yyvsp[-2].ttype) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (yyvsp[-2].ttype, 0)) == STRING_CST)
+ || TREE_CODE (yyvsp[-2].ttype) == STRING_CST)
+ expand_asm (yyvsp[-2].ttype);
+ else
+ error ("argument of `asm' is not a constant string"); ;
+ break;}
+case 318:
+#line 1620 "c-parse.y"
+{ stmt_count++;
+ emit_line_note (yyvsp[-9].filename, yyvsp[-8].lineno);
+ c_expand_asm_operands (yyvsp[-4].ttype, yyvsp[-2].ttype, NULL_TREE, NULL_TREE,
+ yyvsp[-6].ttype == ridpointers[(int)RID_VOLATILE],
+ input_filename, lineno); ;
+ break;}
+case 319:
+#line 1627 "c-parse.y"
+{ stmt_count++;
+ emit_line_note (yyvsp[-11].filename, yyvsp[-10].lineno);
+ c_expand_asm_operands (yyvsp[-6].ttype, yyvsp[-4].ttype, yyvsp[-2].ttype, NULL_TREE,
+ yyvsp[-8].ttype == ridpointers[(int)RID_VOLATILE],
+ input_filename, lineno); ;
+ break;}
+case 320:
+#line 1635 "c-parse.y"
+{ stmt_count++;
+ emit_line_note (yyvsp[-13].filename, yyvsp[-12].lineno);
+ c_expand_asm_operands (yyvsp[-8].ttype, yyvsp[-6].ttype, yyvsp[-4].ttype, yyvsp[-2].ttype,
+ yyvsp[-10].ttype == ridpointers[(int)RID_VOLATILE],
+ input_filename, lineno); ;
+ break;}
+case 321:
+#line 1641 "c-parse.y"
+{ tree decl;
+ stmt_count++;
+ emit_line_note (yyvsp[-4].filename, yyvsp[-3].lineno);
+ decl = lookup_label (yyvsp[-1].ttype);
+ if (decl != 0)
+ {
+ TREE_USED (decl) = 1;
+ expand_goto (decl);
+ }
+ ;
+ break;}
+case 322:
+#line 1652 "c-parse.y"
+{ stmt_count++;
+ emit_line_note (yyvsp[-5].filename, yyvsp[-4].lineno);
+ expand_computed_goto (convert (ptr_type_node, yyvsp[-1].ttype)); ;
+ break;}
+case 325:
+#line 1665 "c-parse.y"
+{
+ /* The value returned by this action is */
+ /* 1 if everything is OK */
+ /* 0 in case of error or already bound iterator */
+
+ yyval.itype = 0;
+ if (TREE_CODE (yyvsp[-1].ttype) != VAR_DECL)
+ error ("invalid `for (ITERATOR)' syntax");
+ else if (! ITERATOR_P (yyvsp[-1].ttype))
+ error ("`%s' is not an iterator",
+ IDENTIFIER_POINTER (DECL_NAME (yyvsp[-1].ttype)));
+ else if (ITERATOR_BOUND_P (yyvsp[-1].ttype))
+ error ("`for (%s)' inside expansion of same iterator",
+ IDENTIFIER_POINTER (DECL_NAME (yyvsp[-1].ttype)));
+ else
+ {
+ yyval.itype = 1;
+ iterator_for_loop_start (yyvsp[-1].ttype);
+ }
+ ;
+ break;}
+case 326:
+#line 1686 "c-parse.y"
+{
+ if (yyvsp[-1].itype)
+ iterator_for_loop_end (yyvsp[-3].ttype);
+ ;
+ break;}
+case 327:
+#line 1721 "c-parse.y"
+{ register tree value = check_case_value (yyvsp[-1].ttype);
+ register tree label
+ = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+
+ stmt_count++;
+
+ if (value != error_mark_node)
+ {
+ tree duplicate;
+ int success = pushcase (value, convert_and_check,
+ label, &duplicate);
+ if (success == 1)
+ error ("case label not within a switch statement");
+ else if (success == 2)
+ {
+ error ("duplicate case value");
+ error_with_decl (duplicate, "this is the first entry for that value");
+ }
+ else if (success == 3)
+ warning ("case value out of range");
+ else if (success == 5)
+ error ("case label within scope of cleanup or variable array");
+ }
+ position_after_white_space (); ;
+ break;}
+case 328:
+#line 1746 "c-parse.y"
+{ register tree value1 = check_case_value (yyvsp[-3].ttype);
+ register tree value2 = check_case_value (yyvsp[-1].ttype);
+ register tree label
+ = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+
+ stmt_count++;
+
+ if (value1 != error_mark_node && value2 != error_mark_node)
+ {
+ tree duplicate;
+ int success = pushcase_range (value1, value2,
+ convert_and_check, label,
+ &duplicate);
+ if (success == 1)
+ error ("case label not within a switch statement");
+ else if (success == 2)
+ {
+ error ("duplicate case value");
+ error_with_decl (duplicate, "this is the first entry for that value");
+ }
+ else if (success == 3)
+ warning ("case value out of range");
+ else if (success == 4)
+ warning ("empty case range");
+ else if (success == 5)
+ error ("case label within scope of cleanup or variable array");
+ }
+ position_after_white_space (); ;
+ break;}
+case 329:
+#line 1775 "c-parse.y"
+{
+ tree duplicate;
+ register tree label
+ = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+ int success = pushcase (NULL_TREE, 0, label, &duplicate);
+ stmt_count++;
+ if (success == 1)
+ error ("default label not within a switch statement");
+ else if (success == 2)
+ {
+ error ("multiple default labels in one switch");
+ error_with_decl (duplicate, "this is the first default label");
+ }
+ position_after_white_space (); ;
+ break;}
+case 330:
+#line 1790 "c-parse.y"
+{ tree label = define_label (input_filename, lineno, yyvsp[-1].ttype);
+ stmt_count++;
+ emit_nop ();
+ if (label)
+ expand_label (label);
+ position_after_white_space (); ;
+ break;}
+case 331:
+#line 1802 "c-parse.y"
+{ emit_line_note (input_filename, lineno);
+ yyval.ttype = NULL_TREE; ;
+ break;}
+case 332:
+#line 1805 "c-parse.y"
+{ emit_line_note (input_filename, lineno); ;
+ break;}
+case 333:
+#line 1810 "c-parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 335:
+#line 1817 "c-parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 338:
+#line 1824 "c-parse.y"
+{ yyval.ttype = chainon (yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 339:
+#line 1829 "c-parse.y"
+{ yyval.ttype = build_tree_list (yyvsp[-3].ttype, yyvsp[-1].ttype); ;
+ break;}
+case 340:
+#line 1834 "c-parse.y"
+{ yyval.ttype = tree_cons (NULL_TREE, combine_strings (yyvsp[0].ttype), NULL_TREE); ;
+ break;}
+case 341:
+#line 1836 "c-parse.y"
+{ yyval.ttype = tree_cons (NULL_TREE, combine_strings (yyvsp[0].ttype), yyvsp[-2].ttype); ;
+ break;}
+case 342:
+#line 1842 "c-parse.y"
+{ pushlevel (0);
+ clear_parm_order ();
+ declare_parm_level (0); ;
+ break;}
+case 343:
+#line 1846 "c-parse.y"
+{ yyval.ttype = yyvsp[0].ttype;
+ parmlist_tags_warning ();
+ poplevel (0, 0, 0); ;
+ break;}
+case 345:
+#line 1854 "c-parse.y"
+{ tree parm;
+ if (pedantic)
+ pedwarn ("ANSI C forbids forward parameter declarations");
+ /* Mark the forward decls as such. */
+ for (parm = getdecls (); parm; parm = TREE_CHAIN (parm))
+ TREE_ASM_WRITTEN (parm) = 1;
+ clear_parm_order (); ;
+ break;}
+case 346:
+#line 1862 "c-parse.y"
+{ yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 347:
+#line 1864 "c-parse.y"
+{ yyval.ttype = tree_cons (NULL_TREE, NULL_TREE, NULL_TREE); ;
+ break;}
+case 348:
+#line 1870 "c-parse.y"
+{ yyval.ttype = get_parm_info (0); ;
+ break;}
+case 349:
+#line 1872 "c-parse.y"
+{ yyval.ttype = get_parm_info (0);
+ if (pedantic)
+ pedwarn ("ANSI C requires a named argument before `...'");
+ ;
+ break;}
+case 350:
+#line 1877 "c-parse.y"
+{ yyval.ttype = get_parm_info (1); ;
+ break;}
+case 351:
+#line 1879 "c-parse.y"
+{ yyval.ttype = get_parm_info (0); ;
+ break;}
+case 352:
+#line 1884 "c-parse.y"
+{ push_parm_decl (yyvsp[0].ttype); ;
+ break;}
+case 353:
+#line 1886 "c-parse.y"
+{ push_parm_decl (yyvsp[0].ttype); ;
+ break;}
+case 354:
+#line 1893 "c-parse.y"
+{ yyval.ttype = build_tree_list (yyvsp[-1].ttype, yyvsp[0].ttype) ; ;
+ break;}
+case 355:
+#line 1895 "c-parse.y"
+{ yyval.ttype = build_tree_list (yyvsp[-1].ttype, yyvsp[0].ttype) ; ;
+ break;}
+case 356:
+#line 1897 "c-parse.y"
+{ yyval.ttype = build_tree_list (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 357:
+#line 1899 "c-parse.y"
+{ yyval.ttype = build_tree_list (yyvsp[-1].ttype, yyvsp[0].ttype) ; ;
+ break;}
+case 358:
+#line 1901 "c-parse.y"
+{ yyval.ttype = build_tree_list (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 359:
+#line 1908 "c-parse.y"
+{ pushlevel (0);
+ clear_parm_order ();
+ declare_parm_level (1); ;
+ break;}
+case 360:
+#line 1912 "c-parse.y"
+{ yyval.ttype = yyvsp[0].ttype;
+ parmlist_tags_warning ();
+ poplevel (0, 0, 0); ;
+ break;}
+case 362:
+#line 1920 "c-parse.y"
+{ tree t;
+ for (t = yyvsp[-1].ttype; t; t = TREE_CHAIN (t))
+ if (TREE_VALUE (t) == NULL_TREE)
+ error ("`...' in old-style identifier list");
+ yyval.ttype = tree_cons (NULL_TREE, NULL_TREE, yyvsp[-1].ttype); ;
+ break;}
+case 363:
+#line 1930 "c-parse.y"
+{ yyval.ttype = build_tree_list (NULL_TREE, yyvsp[0].ttype); ;
+ break;}
+case 364:
+#line 1932 "c-parse.y"
+{ yyval.ttype = chainon (yyvsp[-2].ttype, build_tree_list (NULL_TREE, yyvsp[0].ttype)); ;
+ break;}
+case 365:
+#line 1938 "c-parse.y"
+{ yyval.ttype = build_tree_list (NULL_TREE, yyvsp[0].ttype); ;
+ break;}
+case 366:
+#line 1940 "c-parse.y"
+{ yyval.ttype = chainon (yyvsp[-2].ttype, build_tree_list (NULL_TREE, yyvsp[0].ttype)); ;
+ break;}
+}
+ /* the action file gets copied in in place of this dollarsign */
+#line 480 "/usr/local/lib/bison.simple"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+ yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+ *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+ yylsp++;
+ if (yylen == 0)
+ {
+ yylsp->first_line = yylloc.first_line;
+ yylsp->first_column = yylloc.first_column;
+ yylsp->last_line = (yylsp-1)->last_line;
+ yylsp->last_column = (yylsp-1)->last_column;
+ yylsp->text = 0;
+ }
+ else
+ {
+ yylsp->last_line = (yylsp+yylen-1)->last_line;
+ yylsp->last_column = (yylsp+yylen-1)->last_column;
+ }
+#endif
+
+ /* Now "shift" the result of the reduction.
+ Determine what state that goes to,
+ based on the state we popped back to
+ and the rule number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+ if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTBASE];
+
+ goto yynewstate;
+
+yyerrlab: /* here on detecting error */
+
+ if (! yyerrstatus)
+ /* If not already recovering from an error, report this error. */
+ {
+ ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (yyn > YYFLAG && yyn < YYLAST)
+ {
+ int size = 0;
+ char *msg;
+ int x, count;
+
+ count = 0;
+ /* Start X at -yyn if nec to avoid negative indexes in yycheck. */
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ size += strlen(yytname[x]) + 15, count++;
+ msg = (char *) malloc(size + 15);
+ if (msg != 0)
+ {
+ strcpy(msg, "parse error");
+
+ if (count < 5)
+ {
+ count = 0;
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ {
+ strcat(msg, count == 0 ? ", expecting `" : " or `");
+ strcat(msg, yytname[x]);
+ strcat(msg, "'");
+ count++;
+ }
+ }
+ yyerror(msg);
+ free(msg);
+ }
+ else
+ yyerror ("parse error; also virtual memory exceeded");
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror("parse error");
+ }
+
+ goto yyerrlab1;
+yyerrlab1: /* here on error raised explicitly by an action */
+
+ if (yyerrstatus == 3)
+ {
+ /* if just tried and failed to reuse lookahead token after an error, discard it. */
+
+ /* return failure if at end of input */
+ if (yychar == YYEOF)
+ YYABORT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#endif
+
+ yychar = YYEMPTY;
+ }
+
+ /* Else will try to reuse lookahead token
+ after shifting the error token. */
+
+ yyerrstatus = 3; /* Each real token shifted decrements this */
+
+ goto yyerrhandle;
+
+yyerrdefault: /* current state does not do anything special for the error token. */
+
+#if 0
+ /* This is wrong; only states that explicitly want error tokens
+ should shift them. */
+ yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
+ if (yyn) goto yydefault;
+#endif
+
+yyerrpop: /* pop the current state because it cannot handle the error token */
+
+ if (yyssp == yyss) YYABORT;
+ yyvsp--;
+ yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+ yylsp--;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "Error: state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+yyerrhandle:
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yyerrdefault;
+
+ yyn += YYTERROR;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+ goto yyerrdefault;
+
+ yyn = yytable[yyn];
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrpop;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrpop;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting error token, ");
+#endif
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ yystate = yyn;
+ goto yynewstate;
+}
+#line 1943 "c-parse.y"
+
diff --git a/gnu/usr.bin/cc/cc1/c-pragma.c b/gnu/usr.bin/cc/cc1/c-pragma.c
new file mode 100644
index 0000000..cdade3e
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1/c-pragma.c
@@ -0,0 +1,188 @@
+/* Handle #pragma, system V.4 style. Supports #pragma weak and #pragma pack.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "config.h"
+#include "tree.h"
+#include "function.h"
+#include "defaults.h"
+
+#ifdef HANDLE_SYSV_PRAGMA
+
+/* Support #pragma weak by default if WEAK_ASM_OP and ASM_OUTPUT_DEF
+ are defined. */
+#if !defined (HANDLE_PRAGMA_WEAK) && defined (WEAK_ASM_OP) && defined (ASM_OUTPUT_DEF)
+#define HANDLE_PRAGMA_WEAK 1
+#endif
+
+/* See varasm.c for an identical definition. */
+enum pragma_state
+{
+ ps_start,
+ ps_done,
+ ps_bad,
+ ps_weak,
+ ps_name,
+ ps_equals,
+ ps_value,
+ ps_pack,
+ ps_left,
+ ps_align,
+ ps_right
+};
+
+/* When structure field packing is in effect, this variable is the
+ number of bits to use as the maximum alignment. When packing is not
+ in effect, this is zero. */
+
+extern int maximum_field_alignment;
+
+/* File used for outputting assembler code. */
+extern FILE *asm_out_file;
+
+/* Handle one token of a pragma directive. TOKEN is the
+ current token, and STRING is its printable form. */
+
+void
+handle_pragma_token (string, token)
+ char *string;
+ tree token;
+{
+ static enum pragma_state state = ps_start, type;
+ static char *name;
+ static char *value;
+ static int align;
+
+ if (string == 0)
+ {
+ if (type == ps_pack)
+ {
+ if (state == ps_right)
+ maximum_field_alignment = align * 8;
+ else
+ warning ("malformed `#pragma pack'");
+ }
+ else if (type == ps_weak)
+ {
+#ifdef HANDLE_PRAGMA_WEAK
+ if (HANDLE_PRAGMA_WEAK)
+ handle_pragma_weak (state, asm_out_file, name, value);
+
+#endif /* HANDLE_PRAMA_WEAK */
+ }
+
+ type = state = ps_start;
+ return;
+ }
+
+ switch (state)
+ {
+ case ps_start:
+ if (token && TREE_CODE (token) == IDENTIFIER_NODE)
+ {
+ if (strcmp (IDENTIFIER_POINTER (token), "pack") == 0)
+ type = state = ps_pack;
+ else if (strcmp (IDENTIFIER_POINTER (token), "weak") == 0)
+ type = state = ps_weak;
+ else
+ type = state = ps_done;
+ }
+ else
+ type = state = ps_done;
+ break;
+
+ case ps_weak:
+ if (token && TREE_CODE (token) == IDENTIFIER_NODE)
+ {
+ name = IDENTIFIER_POINTER (token);
+ state = ps_name;
+ }
+ else
+ state = ps_bad;
+ break;
+
+ case ps_name:
+ state = (strcmp (string, "=") ? ps_bad : ps_equals);
+ break;
+
+ case ps_equals:
+ if (token && TREE_CODE (token) == IDENTIFIER_NODE)
+ {
+ value = IDENTIFIER_POINTER (token);
+ state = ps_value;
+ }
+ else
+ state = ps_bad;
+ break;
+
+ case ps_value:
+ state = ps_bad;
+ break;
+
+ case ps_pack:
+ if (strcmp (string, "(") == 0)
+ state = ps_left;
+ else
+ state = ps_bad;
+ break;
+
+ case ps_left:
+ if (token && TREE_CODE (token) == INTEGER_CST
+ && TREE_INT_CST_HIGH (token) == 0)
+ switch (TREE_INT_CST_LOW (token))
+ {
+ case 1:
+ case 2:
+ case 4:
+ align = TREE_INT_CST_LOW (token);
+ state = ps_align;
+ break;
+
+ default:
+ state = ps_bad;
+ }
+ else if (! token && strcmp (string, ")") == 0)
+ {
+ align = 0;
+ state = ps_right;
+ }
+ else
+ state = ps_bad;
+ break;
+
+ case ps_align:
+ if (strcmp (string, ")") == 0)
+ state = ps_right;
+ else
+ state = ps_bad;
+ break;
+
+ case ps_right:
+ state = ps_bad;
+ break;
+
+ case ps_bad:
+ case ps_done:
+ break;
+
+ default:
+ abort ();
+ }
+}
+#endif /* HANDLE_SYSV_PRAGMA */
diff --git a/gnu/usr.bin/cc/cc1/c-typeck.c b/gnu/usr.bin/cc/cc1/c-typeck.c
new file mode 100644
index 0000000..d5283c6
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1/c-typeck.c
@@ -0,0 +1,6384 @@
+/* Build expressions with type checking for C compiler.
+ Copyright (C) 1987, 88, 91, 92, 93, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file is part of the C front end.
+ It contains routines to build C expressions given their operands,
+ including computing the types of the result, C-specific error checks,
+ and some optimization.
+
+ There are also routines to build RETURN_STMT nodes and CASE_STMT nodes,
+ and to process initializations in declarations (since they work
+ like a strange sort of assignment). */
+
+#include "config.h"
+#include <stdio.h>
+#include "tree.h"
+#include "c-tree.h"
+#include "flags.h"
+
+/* Nonzero if we've already printed a "missing braces around initializer"
+ message within this initializer. */
+static int missing_braces_mentioned;
+
+extern char *index ();
+extern char *rindex ();
+
+static tree quality_type PROTO((tree, tree));
+static int comp_target_types PROTO((tree, tree));
+static int function_types_compatible_p PROTO((tree, tree));
+static int type_lists_compatible_p PROTO((tree, tree));
+static int self_promoting_type_p PROTO((tree));
+static tree decl_constant_value PROTO((tree));
+static tree lookup_field PROTO((tree, tree, tree *));
+static tree convert_arguments PROTO((tree, tree, tree, tree));
+static tree pointer_int_sum PROTO((enum tree_code, tree, tree));
+static tree pointer_diff PROTO((tree, tree));
+static tree unary_complex_lvalue PROTO((enum tree_code, tree));
+static void pedantic_lvalue_warning PROTO((enum tree_code));
+static tree internal_build_compound_expr PROTO((tree, int));
+static tree convert_for_assignment PROTO((tree, tree, char *, tree,
+ tree, int));
+static void warn_for_assignment PROTO((char *, char *, tree, int));
+static tree valid_compound_expr_initializer PROTO((tree, tree));
+static void push_string PROTO((char *));
+static void push_member_name PROTO((tree));
+static void push_array_bounds PROTO((int));
+static int spelling_length PROTO((void));
+static char *print_spelling PROTO((char *));
+static char *get_spelling PROTO((char *));
+static void warning_init PROTO((char *, char *,
+ char *));
+static tree digest_init PROTO((tree, tree, int, int));
+static void check_init_type_bitfields PROTO((tree));
+static void output_init_element PROTO((tree, tree, tree, int));
+static void output_pending_init_elements PROTO((int));
+
+/* Do `exp = require_complete_type (exp);' to make sure exp
+ does not have an incomplete type. (That includes void types.) */
+
+tree
+require_complete_type (value)
+ tree value;
+{
+ tree type = TREE_TYPE (value);
+
+ /* First, detect a valid value with a complete type. */
+ if (TYPE_SIZE (type) != 0
+ && type != void_type_node)
+ return value;
+
+ incomplete_type_error (value, type);
+ return error_mark_node;
+}
+
+/* Print an error message for invalid use of an incomplete type.
+ VALUE is the expression that was used (or 0 if that isn't known)
+ and TYPE is the type that was invalid. */
+
+void
+incomplete_type_error (value, type)
+ tree value;
+ tree type;
+{
+ char *errmsg;
+
+ /* Avoid duplicate error message. */
+ if (TREE_CODE (type) == ERROR_MARK)
+ return;
+
+ if (value != 0 && (TREE_CODE (value) == VAR_DECL
+ || TREE_CODE (value) == PARM_DECL))
+ error ("`%s' has an incomplete type",
+ IDENTIFIER_POINTER (DECL_NAME (value)));
+ else
+ {
+ retry:
+ /* We must print an error message. Be clever about what it says. */
+
+ switch (TREE_CODE (type))
+ {
+ case RECORD_TYPE:
+ errmsg = "invalid use of undefined type `struct %s'";
+ break;
+
+ case UNION_TYPE:
+ errmsg = "invalid use of undefined type `union %s'";
+ break;
+
+ case ENUMERAL_TYPE:
+ errmsg = "invalid use of undefined type `enum %s'";
+ break;
+
+ case VOID_TYPE:
+ error ("invalid use of void expression");
+ return;
+
+ case ARRAY_TYPE:
+ if (TYPE_DOMAIN (type))
+ {
+ type = TREE_TYPE (type);
+ goto retry;
+ }
+ error ("invalid use of array with unspecified bounds");
+ return;
+
+ default:
+ abort ();
+ }
+
+ if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
+ error (errmsg, IDENTIFIER_POINTER (TYPE_NAME (type)));
+ else
+ /* If this type has a typedef-name, the TYPE_NAME is a TYPE_DECL. */
+ error ("invalid use of incomplete typedef `%s'",
+ IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))));
+ }
+}
+
+/* Return a variant of TYPE which has all the type qualifiers of LIKE
+ as well as those of TYPE. */
+
+static tree
+qualify_type (type, like)
+ tree type, like;
+{
+ int constflag = TYPE_READONLY (type) || TYPE_READONLY (like);
+ int volflag = TYPE_VOLATILE (type) || TYPE_VOLATILE (like);
+ return c_build_type_variant (type, constflag, volflag);
+}
+
+/* Return the common type of two types.
+ We assume that comptypes has already been done and returned 1;
+ if that isn't so, this may crash. In particular, we assume that qualifiers
+ match.
+
+ This is the type for the result of most arithmetic operations
+ if the operands have the given two types. */
+
+tree
+common_type (t1, t2)
+ tree t1, t2;
+{
+ register enum tree_code code1;
+ register enum tree_code code2;
+ tree attributes;
+
+ /* Save time if the two types are the same. */
+
+ if (t1 == t2) return t1;
+
+ /* If one type is nonsense, use the other. */
+ if (t1 == error_mark_node)
+ return t2;
+ if (t2 == error_mark_node)
+ return t1;
+
+ /* Merge the attributes */
+
+ { register tree a1, a2;
+ a1 = TYPE_ATTRIBUTES (t1);
+ a2 = TYPE_ATTRIBUTES (t2);
+
+ /* Either one unset? Take the set one. */
+
+ if (!(attributes = a1))
+ attributes = a2;
+
+ /* One that completely contains the other? Take it. */
+
+ else if (a2 && !attribute_list_contained (a1, a2))
+ if (attribute_list_contained (a2, a1))
+ attributes = a2;
+ else
+ {
+ /* Pick the longest list, and hang on the other
+ list. */
+
+ if (list_length (a1) < list_length (a2))
+ attributes = a2, a2 = a1;
+
+ for (; a2; a2 = TREE_CHAIN (a2))
+ if (!value_member (attributes, a2))
+ {
+ a1 = copy_node (a2);
+ TREE_CHAIN (a1) = attributes;
+ attributes = a1;
+ }
+ }
+ }
+
+ /* Treat an enum type as the unsigned integer type of the same width. */
+
+ if (TREE_CODE (t1) == ENUMERAL_TYPE)
+ t1 = type_for_size (TYPE_PRECISION (t1), 1);
+ if (TREE_CODE (t2) == ENUMERAL_TYPE)
+ t2 = type_for_size (TYPE_PRECISION (t2), 1);
+
+ code1 = TREE_CODE (t1);
+ code2 = TREE_CODE (t2);
+
+ /* If one type is complex, form the common type of the non-complex
+ components, then make that complex. Use T1 or T2 if it is the
+ required type. */
+ if (code1 == COMPLEX_TYPE || code2 == COMPLEX_TYPE)
+ {
+ tree subtype1 = code1 == COMPLEX_TYPE ? TREE_TYPE (t1) : t1;
+ tree subtype2 = code2 == COMPLEX_TYPE ? TREE_TYPE (t2) : t2;
+ tree subtype = common_type (subtype1, subtype2);
+
+ if (code1 == COMPLEX_TYPE && TREE_TYPE (t1) == subtype)
+ return build_type_attribute_variant (t1, attributes);
+ else if (code2 == COMPLEX_TYPE && TREE_TYPE (t2) == subtype)
+ return build_type_attribute_variant (t2, attributes);
+ else
+ return build_type_attribute_variant (build_complex_type (subtype),
+ attributes);
+ }
+
+ switch (code1)
+ {
+ case INTEGER_TYPE:
+ case REAL_TYPE:
+ /* If only one is real, use it as the result. */
+
+ if (code1 == REAL_TYPE && code2 != REAL_TYPE)
+ return build_type_attribute_variant (t1, attributes);
+
+ if (code2 == REAL_TYPE && code1 != REAL_TYPE)
+ return build_type_attribute_variant (t2, attributes);
+
+ /* Both real or both integers; use the one with greater precision. */
+
+ if (TYPE_PRECISION (t1) > TYPE_PRECISION (t2))
+ return build_type_attribute_variant (t1, attributes);
+ else if (TYPE_PRECISION (t2) > TYPE_PRECISION (t1))
+ return build_type_attribute_variant (t2, attributes);
+
+ /* Same precision. Prefer longs to ints even when same size. */
+
+ if (TYPE_MAIN_VARIANT (t1) == long_unsigned_type_node
+ || TYPE_MAIN_VARIANT (t2) == long_unsigned_type_node)
+ return build_type_attribute_variant (long_unsigned_type_node,
+ attributes);
+
+ if (TYPE_MAIN_VARIANT (t1) == long_integer_type_node
+ || TYPE_MAIN_VARIANT (t2) == long_integer_type_node)
+ {
+ /* But preserve unsignedness from the other type,
+ since long cannot hold all the values of an unsigned int. */
+ if (TREE_UNSIGNED (t1) || TREE_UNSIGNED (t2))
+ t1 = long_unsigned_type_node;
+ else
+ t1 = long_integer_type_node;
+ return build_type_attribute_variant (t1, attributes);
+ }
+
+ /* Otherwise prefer the unsigned one. */
+
+ if (TREE_UNSIGNED (t1))
+ return build_type_attribute_variant (t1, attributes);
+ else
+ return build_type_attribute_variant (t2, attributes);
+
+ case POINTER_TYPE:
+ /* For two pointers, do this recursively on the target type,
+ and combine the qualifiers of the two types' targets. */
+ /* This code was turned off; I don't know why.
+ But ANSI C specifies doing this with the qualifiers.
+ So I turned it on again. */
+ {
+ tree target = common_type (TYPE_MAIN_VARIANT (TREE_TYPE (t1)),
+ TYPE_MAIN_VARIANT (TREE_TYPE (t2)));
+ int constp
+ = TYPE_READONLY (TREE_TYPE (t1)) || TYPE_READONLY (TREE_TYPE (t2));
+ int volatilep
+ = TYPE_VOLATILE (TREE_TYPE (t1)) || TYPE_VOLATILE (TREE_TYPE (t2));
+ t1 = build_pointer_type (c_build_type_variant (target, constp,
+ volatilep));
+ return build_type_attribute_variant (t1, attributes);
+ }
+#if 0
+ t1 = build_pointer_type (common_type (TREE_TYPE (t1), TREE_TYPE (t2)));
+ return build_type_attribute_variant (t1, attributes);
+#endif
+
+ case ARRAY_TYPE:
+ {
+ tree elt = common_type (TREE_TYPE (t1), TREE_TYPE (t2));
+ /* Save space: see if the result is identical to one of the args. */
+ if (elt == TREE_TYPE (t1) && TYPE_DOMAIN (t1))
+ return build_type_attribute_variant (t1, attributes);
+ if (elt == TREE_TYPE (t2) && TYPE_DOMAIN (t2))
+ return build_type_attribute_variant (t2, attributes);
+ /* Merge the element types, and have a size if either arg has one. */
+ t1 = build_array_type (elt, TYPE_DOMAIN (TYPE_DOMAIN (t1) ? t1 : t2));
+ return build_type_attribute_variant (t1, attributes);
+ }
+
+ case FUNCTION_TYPE:
+ /* Function types: prefer the one that specified arg types.
+ If both do, merge the arg types. Also merge the return types. */
+ {
+ tree valtype = common_type (TREE_TYPE (t1), TREE_TYPE (t2));
+ tree p1 = TYPE_ARG_TYPES (t1);
+ tree p2 = TYPE_ARG_TYPES (t2);
+ int len;
+ tree newargs, n;
+ int i;
+
+ /* Save space: see if the result is identical to one of the args. */
+ if (valtype == TREE_TYPE (t1) && ! TYPE_ARG_TYPES (t2))
+ return build_type_attribute_variant (t1, attributes);
+ if (valtype == TREE_TYPE (t2) && ! TYPE_ARG_TYPES (t1))
+ return build_type_attribute_variant (t2, attributes);
+
+ /* Simple way if one arg fails to specify argument types. */
+ if (TYPE_ARG_TYPES (t1) == 0)
+ {
+ t1 = build_function_type (valtype, TYPE_ARG_TYPES (t2));
+ return build_type_attribute_variant (t1, attributes);
+ }
+ if (TYPE_ARG_TYPES (t2) == 0)
+ {
+ t1 = build_function_type (valtype, TYPE_ARG_TYPES (t1));
+ return build_type_attribute_variant (t1, attributes);
+ }
+
+ /* If both args specify argument types, we must merge the two
+ lists, argument by argument. */
+
+ len = list_length (p1);
+ newargs = 0;
+
+ for (i = 0; i < len; i++)
+ newargs = tree_cons (NULL_TREE, NULL_TREE, newargs);
+
+ n = newargs;
+
+ for (; p1;
+ p1 = TREE_CHAIN (p1), p2 = TREE_CHAIN (p2), n = TREE_CHAIN (n))
+ {
+ /* A null type means arg type is not specified.
+ Take whatever the other function type has. */
+ if (TREE_VALUE (p1) == 0)
+ {
+ TREE_VALUE (n) = TREE_VALUE (p2);
+ goto parm_done;
+ }
+ if (TREE_VALUE (p2) == 0)
+ {
+ TREE_VALUE (n) = TREE_VALUE (p1);
+ goto parm_done;
+ }
+
+ /* Given wait (union {union wait *u; int *i} *)
+ and wait (union wait *),
+ prefer union wait * as type of parm. */
+ if (TREE_CODE (TREE_VALUE (p1)) == UNION_TYPE
+ && TREE_VALUE (p1) != TREE_VALUE (p2))
+ {
+ tree memb;
+ for (memb = TYPE_FIELDS (TREE_VALUE (p1));
+ memb; memb = TREE_CHAIN (memb))
+ if (comptypes (TREE_TYPE (memb), TREE_VALUE (p2)))
+ {
+ TREE_VALUE (n) = TREE_VALUE (p2);
+ if (pedantic)
+ pedwarn ("function types not truly compatible in ANSI C");
+ goto parm_done;
+ }
+ }
+ if (TREE_CODE (TREE_VALUE (p2)) == UNION_TYPE
+ && TREE_VALUE (p2) != TREE_VALUE (p1))
+ {
+ tree memb;
+ for (memb = TYPE_FIELDS (TREE_VALUE (p2));
+ memb; memb = TREE_CHAIN (memb))
+ if (comptypes (TREE_TYPE (memb), TREE_VALUE (p1)))
+ {
+ TREE_VALUE (n) = TREE_VALUE (p1);
+ if (pedantic)
+ pedwarn ("function types not truly compatible in ANSI C");
+ goto parm_done;
+ }
+ }
+ TREE_VALUE (n) = common_type (TREE_VALUE (p1), TREE_VALUE (p2));
+ parm_done: ;
+ }
+
+ t1 = build_function_type (valtype, newargs);
+ /* ... falls through ... */
+ }
+
+ default:
+ return build_type_attribute_variant (t1, attributes);
+ }
+
+}
+
+/* Return 1 if TYPE1 and TYPE2 are compatible types for assignment
+ or various other operations. Return 2 if they are compatible
+ but a warning may be needed if you use them together. */
+
+int
+comptypes (type1, type2)
+ tree type1, type2;
+{
+ register tree t1 = type1;
+ register tree t2 = type2;
+ int attrval, val;
+
+ /* Suppress errors caused by previously reported errors. */
+
+ if (t1 == t2 || TREE_CODE (t1) == ERROR_MARK || TREE_CODE (t2) == ERROR_MARK)
+ return 1;
+
+ /* Treat an enum type as the integer type of the same width and
+ signedness. */
+
+ if (TREE_CODE (t1) == ENUMERAL_TYPE)
+ t1 = type_for_size (TYPE_PRECISION (t1), TREE_UNSIGNED (t1));
+ if (TREE_CODE (t2) == ENUMERAL_TYPE)
+ t2 = type_for_size (TYPE_PRECISION (t2), TREE_UNSIGNED (t2));
+
+ if (t1 == t2)
+ return 1;
+
+ /* Different classes of types can't be compatible. */
+
+ if (TREE_CODE (t1) != TREE_CODE (t2)) return 0;
+
+ /* Qualifiers must match. */
+
+ if (TYPE_READONLY (t1) != TYPE_READONLY (t2))
+ return 0;
+ if (TYPE_VOLATILE (t1) != TYPE_VOLATILE (t2))
+ return 0;
+
+ /* Allow for two different type nodes which have essentially the same
+ definition. Note that we already checked for equality of the type
+ type qualifiers (just above). */
+
+ if (TYPE_MAIN_VARIANT (t1) == TYPE_MAIN_VARIANT (t2))
+ return 1;
+
+#ifndef COMP_TYPE_ATTRIBUTES
+#define COMP_TYPE_ATTRIBUTES(t1,t2) 1
+#endif
+
+ /* 1 if no need for warning yet, 2 if warning cause has been seen. */
+ if (! (attrval = COMP_TYPE_ATTRIBUTES (t1, t2)))
+ return 0;
+
+ /* 1 if no need for warning yet, 2 if warning cause has been seen. */
+ val = 0;
+
+ switch (TREE_CODE (t1))
+ {
+ case POINTER_TYPE:
+ val = (TREE_TYPE (t1) == TREE_TYPE (t2)
+ ? 1 : comptypes (TREE_TYPE (t1), TREE_TYPE (t2)));
+ break;
+
+ case FUNCTION_TYPE:
+ val = function_types_compatible_p (t1, t2);
+ break;
+
+ case ARRAY_TYPE:
+ {
+ tree d1 = TYPE_DOMAIN (t1);
+ tree d2 = TYPE_DOMAIN (t2);
+ val = 1;
+
+ /* Target types must match incl. qualifiers. */
+ if (TREE_TYPE (t1) != TREE_TYPE (t2)
+ && 0 == (val = comptypes (TREE_TYPE (t1), TREE_TYPE (t2))))
+ return 0;
+
+ /* Sizes must match unless one is missing or variable. */
+ if (d1 == 0 || d2 == 0 || d1 == d2
+ || TREE_CODE (TYPE_MIN_VALUE (d1)) != INTEGER_CST
+ || TREE_CODE (TYPE_MIN_VALUE (d2)) != INTEGER_CST
+ || TREE_CODE (TYPE_MAX_VALUE (d1)) != INTEGER_CST
+ || TREE_CODE (TYPE_MAX_VALUE (d2)) != INTEGER_CST)
+ break;
+
+ if (! ((TREE_INT_CST_LOW (TYPE_MIN_VALUE (d1))
+ == TREE_INT_CST_LOW (TYPE_MIN_VALUE (d2)))
+ && (TREE_INT_CST_HIGH (TYPE_MIN_VALUE (d1))
+ == TREE_INT_CST_HIGH (TYPE_MIN_VALUE (d2)))
+ && (TREE_INT_CST_LOW (TYPE_MAX_VALUE (d1))
+ == TREE_INT_CST_LOW (TYPE_MAX_VALUE (d2)))
+ && (TREE_INT_CST_HIGH (TYPE_MAX_VALUE (d1))
+ == TREE_INT_CST_HIGH (TYPE_MAX_VALUE (d2)))))
+ val = 0;
+ break;
+ }
+
+ case RECORD_TYPE:
+ if (maybe_objc_comptypes (t1, t2, 0) == 1)
+ val = 1;
+ break;
+ }
+ return attrval == 2 && val == 1 ? 2 : val;
+}
+
+/* Return 1 if TTL and TTR are pointers to types that are equivalent,
+ ignoring their qualifiers. */
+
+static int
+comp_target_types (ttl, ttr)
+ tree ttl, ttr;
+{
+ int val;
+
+ /* Give maybe_objc_comptypes a crack at letting these types through. */
+ if (val = maybe_objc_comptypes (ttl, ttr, 1) >= 0)
+ return val;
+
+ val = comptypes (TYPE_MAIN_VARIANT (TREE_TYPE (ttl)),
+ TYPE_MAIN_VARIANT (TREE_TYPE (ttr)));
+
+ if (val == 2 && pedantic)
+ pedwarn ("types are not quite compatible");
+ return val;
+}
+
+/* Subroutines of `comptypes'. */
+
+/* Return 1 if two function types F1 and F2 are compatible.
+ If either type specifies no argument types,
+ the other must specify a fixed number of self-promoting arg types.
+ Otherwise, if one type specifies only the number of arguments,
+ the other must specify that number of self-promoting arg types.
+ Otherwise, the argument types must match. */
+
+static int
+function_types_compatible_p (f1, f2)
+ tree f1, f2;
+{
+ tree args1, args2;
+ /* 1 if no need for warning yet, 2 if warning cause has been seen. */
+ int val = 1;
+ int val1;
+
+ if (!(TREE_TYPE (f1) == TREE_TYPE (f2)
+ || (val = comptypes (TREE_TYPE (f1), TREE_TYPE (f2)))))
+ return 0;
+
+ args1 = TYPE_ARG_TYPES (f1);
+ args2 = TYPE_ARG_TYPES (f2);
+
+ /* An unspecified parmlist matches any specified parmlist
+ whose argument types don't need default promotions. */
+
+ if (args1 == 0)
+ {
+ if (!self_promoting_args_p (args2))
+ return 0;
+ /* If one of these types comes from a non-prototype fn definition,
+ compare that with the other type's arglist.
+ If they don't match, ask for a warning (but no error). */
+ if (TYPE_ACTUAL_ARG_TYPES (f1)
+ && 1 != type_lists_compatible_p (args2, TYPE_ACTUAL_ARG_TYPES (f1)))
+ val = 2;
+ return val;
+ }
+ if (args2 == 0)
+ {
+ if (!self_promoting_args_p (args1))
+ return 0;
+ if (TYPE_ACTUAL_ARG_TYPES (f2)
+ && 1 != type_lists_compatible_p (args1, TYPE_ACTUAL_ARG_TYPES (f2)))
+ val = 2;
+ return val;
+ }
+
+ /* Both types have argument lists: compare them and propagate results. */
+ val1 = type_lists_compatible_p (args1, args2);
+ return val1 != 1 ? val1 : val;
+}
+
+/* Check two lists of types for compatibility,
+ returning 0 for incompatible, 1 for compatible,
+ or 2 for compatible with warning. */
+
+static int
+type_lists_compatible_p (args1, args2)
+ tree args1, args2;
+{
+ /* 1 if no need for warning yet, 2 if warning cause has been seen. */
+ int val = 1;
+ int newval = 0;
+
+ while (1)
+ {
+ if (args1 == 0 && args2 == 0)
+ return val;
+ /* If one list is shorter than the other,
+ they fail to match. */
+ if (args1 == 0 || args2 == 0)
+ return 0;
+ /* A null pointer instead of a type
+ means there is supposed to be an argument
+ but nothing is specified about what type it has.
+ So match anything that self-promotes. */
+ if (TREE_VALUE (args1) == 0)
+ {
+ if (! self_promoting_type_p (TREE_VALUE (args2)))
+ return 0;
+ }
+ else if (TREE_VALUE (args2) == 0)
+ {
+ if (! self_promoting_type_p (TREE_VALUE (args1)))
+ return 0;
+ }
+ else if (! (newval = comptypes (TREE_VALUE (args1), TREE_VALUE (args2))))
+ {
+ /* Allow wait (union {union wait *u; int *i} *)
+ and wait (union wait *) to be compatible. */
+ if (TREE_CODE (TREE_VALUE (args1)) == UNION_TYPE
+ && TYPE_NAME (TREE_VALUE (args1)) == 0
+ && TREE_CODE (TYPE_SIZE (TREE_VALUE (args1))) == INTEGER_CST
+ && tree_int_cst_equal (TYPE_SIZE (TREE_VALUE (args1)),
+ TYPE_SIZE (TREE_VALUE (args2))))
+ {
+ tree memb;
+ for (memb = TYPE_FIELDS (TREE_VALUE (args1));
+ memb; memb = TREE_CHAIN (memb))
+ if (comptypes (TREE_TYPE (memb), TREE_VALUE (args2)))
+ break;
+ if (memb == 0)
+ return 0;
+ }
+ else if (TREE_CODE (TREE_VALUE (args2)) == UNION_TYPE
+ && TYPE_NAME (TREE_VALUE (args2)) == 0
+ && TREE_CODE (TYPE_SIZE (TREE_VALUE (args2))) == INTEGER_CST
+ && tree_int_cst_equal (TYPE_SIZE (TREE_VALUE (args2)),
+ TYPE_SIZE (TREE_VALUE (args1))))
+ {
+ tree memb;
+ for (memb = TYPE_FIELDS (TREE_VALUE (args2));
+ memb; memb = TREE_CHAIN (memb))
+ if (comptypes (TREE_TYPE (memb), TREE_VALUE (args1)))
+ break;
+ if (memb == 0)
+ return 0;
+ }
+ else
+ return 0;
+ }
+
+ /* comptypes said ok, but record if it said to warn. */
+ if (newval > val)
+ val = newval;
+
+ args1 = TREE_CHAIN (args1);
+ args2 = TREE_CHAIN (args2);
+ }
+}
+
+/* Return 1 if PARMS specifies a fixed number of parameters
+ and none of their types is affected by default promotions. */
+
+int
+self_promoting_args_p (parms)
+ tree parms;
+{
+ register tree t;
+ for (t = parms; t; t = TREE_CHAIN (t))
+ {
+ register tree type = TREE_VALUE (t);
+
+ if (TREE_CHAIN (t) == 0 && type != void_type_node)
+ return 0;
+
+ if (type == 0)
+ return 0;
+
+ if (TYPE_MAIN_VARIANT (type) == float_type_node)
+ return 0;
+
+ if (C_PROMOTING_INTEGER_TYPE_P (type))
+ return 0;
+ }
+ return 1;
+}
+
+/* Return 1 if TYPE is not affected by default promotions. */
+
+static int
+self_promoting_type_p (type)
+ tree type;
+{
+ if (TYPE_MAIN_VARIANT (type) == float_type_node)
+ return 0;
+
+ if (C_PROMOTING_INTEGER_TYPE_P (type))
+ return 0;
+
+ return 1;
+}
+
+/* Return an unsigned type the same as TYPE in other respects. */
+
+tree
+unsigned_type (type)
+ tree type;
+{
+ tree type1 = TYPE_MAIN_VARIANT (type);
+ if (type1 == signed_char_type_node || type1 == char_type_node)
+ return unsigned_char_type_node;
+ if (type1 == integer_type_node)
+ return unsigned_type_node;
+ if (type1 == short_integer_type_node)
+ return short_unsigned_type_node;
+ if (type1 == long_integer_type_node)
+ return long_unsigned_type_node;
+ if (type1 == long_long_integer_type_node)
+ return long_long_unsigned_type_node;
+ return type;
+}
+
+/* Return a signed type the same as TYPE in other respects. */
+
+tree
+signed_type (type)
+ tree type;
+{
+ tree type1 = TYPE_MAIN_VARIANT (type);
+ if (type1 == unsigned_char_type_node || type1 == char_type_node)
+ return signed_char_type_node;
+ if (type1 == unsigned_type_node)
+ return integer_type_node;
+ if (type1 == short_unsigned_type_node)
+ return short_integer_type_node;
+ if (type1 == long_unsigned_type_node)
+ return long_integer_type_node;
+ if (type1 == long_long_unsigned_type_node)
+ return long_long_integer_type_node;
+ return type;
+}
+
+/* Return a type the same as TYPE except unsigned or
+ signed according to UNSIGNEDP. */
+
+tree
+signed_or_unsigned_type (unsignedp, type)
+ int unsignedp;
+ tree type;
+{
+ if (! INTEGRAL_TYPE_P (type))
+ return type;
+ if (TYPE_PRECISION (type) == TYPE_PRECISION (signed_char_type_node))
+ return unsignedp ? unsigned_char_type_node : signed_char_type_node;
+ if (TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node))
+ return unsignedp ? unsigned_type_node : integer_type_node;
+ if (TYPE_PRECISION (type) == TYPE_PRECISION (short_integer_type_node))
+ return unsignedp ? short_unsigned_type_node : short_integer_type_node;
+ if (TYPE_PRECISION (type) == TYPE_PRECISION (long_integer_type_node))
+ return unsignedp ? long_unsigned_type_node : long_integer_type_node;
+ if (TYPE_PRECISION (type) == TYPE_PRECISION (long_long_integer_type_node))
+ return (unsignedp ? long_long_unsigned_type_node
+ : long_long_integer_type_node);
+ return type;
+}
+
+/* Compute the value of the `sizeof' operator. */
+
+tree
+c_sizeof (type)
+ tree type;
+{
+ enum tree_code code = TREE_CODE (type);
+ tree t;
+
+ if (code == FUNCTION_TYPE)
+ {
+ if (pedantic || warn_pointer_arith)
+ pedwarn ("sizeof applied to a function type");
+ return size_int (1);
+ }
+ if (code == VOID_TYPE)
+ {
+ if (pedantic || warn_pointer_arith)
+ pedwarn ("sizeof applied to a void type");
+ return size_int (1);
+ }
+ if (code == ERROR_MARK)
+ return size_int (1);
+ if (TYPE_SIZE (type) == 0)
+ {
+ error ("sizeof applied to an incomplete type");
+ return size_int (0);
+ }
+
+ /* Convert in case a char is more than one unit. */
+ t = size_binop (CEIL_DIV_EXPR, TYPE_SIZE (type),
+ size_int (TYPE_PRECISION (char_type_node)));
+ /* size_binop does not put the constant in range, so do it now. */
+ if (TREE_CODE (t) == INTEGER_CST && force_fit_type (t, 0))
+ TREE_CONSTANT_OVERFLOW (t) = TREE_OVERFLOW (t) = 1;
+ return t;
+}
+
+tree
+c_sizeof_nowarn (type)
+ tree type;
+{
+ enum tree_code code = TREE_CODE (type);
+ tree t;
+
+ if (code == FUNCTION_TYPE
+ || code == VOID_TYPE
+ || code == ERROR_MARK)
+ return size_int (1);
+ if (TYPE_SIZE (type) == 0)
+ return size_int (0);
+
+ /* Convert in case a char is more than one unit. */
+ t = size_binop (CEIL_DIV_EXPR, TYPE_SIZE (type),
+ size_int (TYPE_PRECISION (char_type_node)));
+ force_fit_type (t, 0);
+ return t;
+}
+
+/* Compute the size to increment a pointer by. */
+
+tree
+c_size_in_bytes (type)
+ tree type;
+{
+ enum tree_code code = TREE_CODE (type);
+ tree t;
+
+ if (code == FUNCTION_TYPE)
+ return size_int (1);
+ if (code == VOID_TYPE)
+ return size_int (1);
+ if (code == ERROR_MARK)
+ return size_int (1);
+ if (TYPE_SIZE (type) == 0)
+ {
+ error ("arithmetic on pointer to an incomplete type");
+ return size_int (1);
+ }
+
+ /* Convert in case a char is more than one unit. */
+ t = size_binop (CEIL_DIV_EXPR, TYPE_SIZE (type),
+ size_int (BITS_PER_UNIT));
+ force_fit_type (t, 0);
+ return t;
+}
+
+/* Implement the __alignof keyword: Return the minimum required
+ alignment of TYPE, measured in bytes. */
+
+tree
+c_alignof (type)
+ tree type;
+{
+ enum tree_code code = TREE_CODE (type);
+
+ if (code == FUNCTION_TYPE)
+ return size_int (FUNCTION_BOUNDARY / BITS_PER_UNIT);
+
+ if (code == VOID_TYPE || code == ERROR_MARK)
+ return size_int (1);
+
+ return size_int (TYPE_ALIGN (type) / BITS_PER_UNIT);
+}
+
+/* Implement the __alignof keyword: Return the minimum required
+ alignment of EXPR, measured in bytes. For VAR_DECL's and
+ FIELD_DECL's return DECL_ALIGN (which can be set from an
+ "aligned" __attribute__ specification). */
+
+tree
+c_alignof_expr (expr)
+ tree expr;
+{
+ if (TREE_CODE (expr) == VAR_DECL)
+ return size_int (DECL_ALIGN (expr) / BITS_PER_UNIT);
+
+ if (TREE_CODE (expr) == COMPONENT_REF
+ && DECL_BIT_FIELD (TREE_OPERAND (expr, 1)))
+ {
+ error ("`__alignof' applied to a bit-field");
+ return size_int (1);
+ }
+ else if (TREE_CODE (expr) == COMPONENT_REF
+ && TREE_CODE (TREE_OPERAND (expr, 1)) == FIELD_DECL)
+ return size_int (DECL_ALIGN (TREE_OPERAND (expr, 1)) / BITS_PER_UNIT);
+
+ if (TREE_CODE (expr) == INDIRECT_REF)
+ {
+ tree t = TREE_OPERAND (expr, 0);
+ tree best = t;
+ int bestalign = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (t)));
+
+ while (TREE_CODE (t) == NOP_EXPR
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == POINTER_TYPE)
+ {
+ int thisalign;
+
+ t = TREE_OPERAND (t, 0);
+ thisalign = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (t)));
+ if (thisalign > bestalign)
+ best = t, bestalign = thisalign;
+ }
+ return c_alignof (TREE_TYPE (TREE_TYPE (best)));
+ }
+ else
+ return c_alignof (TREE_TYPE (expr));
+}
+/* Return either DECL or its known constant value (if it has one). */
+
+static tree
+decl_constant_value (decl)
+ tree decl;
+{
+ if (! TREE_PUBLIC (decl)
+ /* Don't change a variable array bound or initial value to a constant
+ in a place where a variable is invalid. */
+ && current_function_decl != 0
+ && ! pedantic
+ && ! TREE_THIS_VOLATILE (decl)
+ && TREE_READONLY (decl) && ! ITERATOR_P (decl)
+ && DECL_INITIAL (decl) != 0
+ && TREE_CODE (DECL_INITIAL (decl)) != ERROR_MARK
+ /* This is invalid if initial value is not constant.
+ If it has either a function call, a memory reference,
+ or a variable, then re-evaluating it could give different results. */
+ && TREE_CONSTANT (DECL_INITIAL (decl))
+ /* Check for cases where this is sub-optimal, even though valid. */
+ && TREE_CODE (DECL_INITIAL (decl)) != CONSTRUCTOR
+ && DECL_MODE (decl) != BLKmode)
+ return DECL_INITIAL (decl);
+ return decl;
+}
+
+/* Perform default promotions for C data used in expressions.
+ Arrays and functions are converted to pointers;
+ enumeral types or short or char, to int.
+ In addition, manifest constants symbols are replaced by their values. */
+
+tree
+default_conversion (exp)
+ tree exp;
+{
+ register tree type = TREE_TYPE (exp);
+ register enum tree_code code = TREE_CODE (type);
+
+ /* Constants can be used directly unless they're not loadable. */
+ if (TREE_CODE (exp) == CONST_DECL)
+ exp = DECL_INITIAL (exp);
+ /* Replace a nonvolatile const static variable with its value. */
+ else if (optimize && TREE_CODE (exp) == VAR_DECL)
+ {
+ exp = decl_constant_value (exp);
+ type = TREE_TYPE (exp);
+ }
+
+ /* Strip NON_LVALUE_EXPRs and no-op conversions, since we aren't using as
+ an lvalue. */
+ /* Do not use STRIP_NOPS here! It will remove conversions from pointer
+ to integer and cause infinite recursion. */
+ while (TREE_CODE (exp) == NON_LVALUE_EXPR
+ || (TREE_CODE (exp) == NOP_EXPR
+ && TREE_TYPE (TREE_OPERAND (exp, 0)) == TREE_TYPE (exp)))
+ exp = TREE_OPERAND (exp, 0);
+
+ /* Normally convert enums to int,
+ but convert wide enums to something wider. */
+ if (code == ENUMERAL_TYPE)
+ {
+ type = type_for_size (MAX (TYPE_PRECISION (type),
+ TYPE_PRECISION (integer_type_node)),
+ ((flag_traditional
+ || TYPE_PRECISION (type) >= TYPE_PRECISION (integer_type_node))
+ && TREE_UNSIGNED (type)));
+ return convert (type, exp);
+ }
+
+ if (C_PROMOTING_INTEGER_TYPE_P (type))
+ {
+ /* Traditionally, unsignedness is preserved in default promotions.
+ Also preserve unsignedness if not really getting any wider. */
+ if (TREE_UNSIGNED (type)
+ && (flag_traditional
+ || TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node)))
+ return convert (unsigned_type_node, exp);
+ return convert (integer_type_node, exp);
+ }
+ if (flag_traditional && !flag_allow_single_precision
+ && TYPE_MAIN_VARIANT (type) == float_type_node)
+ return convert (double_type_node, exp);
+ if (code == VOID_TYPE)
+ {
+ error ("void value not ignored as it ought to be");
+ return error_mark_node;
+ }
+ if (code == FUNCTION_TYPE)
+ {
+ return build_unary_op (ADDR_EXPR, exp, 0);
+ }
+ if (code == ARRAY_TYPE)
+ {
+ register tree adr;
+ tree restype = TREE_TYPE (type);
+ tree ptrtype;
+ int constp = 0;
+ int volatilep = 0;
+
+ if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'r'
+ || TREE_CODE_CLASS (TREE_CODE (exp)) == 'd')
+ {
+ constp = TREE_READONLY (exp);
+ volatilep = TREE_THIS_VOLATILE (exp);
+ }
+
+ if (TYPE_READONLY (type) || TYPE_VOLATILE (type)
+ || constp || volatilep)
+ restype = c_build_type_variant (restype,
+ TYPE_READONLY (type) || constp,
+ TYPE_VOLATILE (type) || volatilep);
+
+ if (TREE_CODE (exp) == INDIRECT_REF)
+ return convert (TYPE_POINTER_TO (restype),
+ TREE_OPERAND (exp, 0));
+
+ if (TREE_CODE (exp) == COMPOUND_EXPR)
+ {
+ tree op1 = default_conversion (TREE_OPERAND (exp, 1));
+ return build (COMPOUND_EXPR, TREE_TYPE (op1),
+ TREE_OPERAND (exp, 0), op1);
+ }
+
+ if (!lvalue_p (exp)
+ && ! (TREE_CODE (exp) == CONSTRUCTOR && TREE_STATIC (exp)))
+ {
+ error ("invalid use of non-lvalue array");
+ return error_mark_node;
+ }
+
+ ptrtype = build_pointer_type (restype);
+
+ if (TREE_CODE (exp) == VAR_DECL)
+ {
+ /* ??? This is not really quite correct
+ in that the type of the operand of ADDR_EXPR
+ is not the target type of the type of the ADDR_EXPR itself.
+ Question is, can this lossage be avoided? */
+ adr = build1 (ADDR_EXPR, ptrtype, exp);
+ if (mark_addressable (exp) == 0)
+ return error_mark_node;
+ TREE_CONSTANT (adr) = staticp (exp);
+ TREE_SIDE_EFFECTS (adr) = 0; /* Default would be, same as EXP. */
+ return adr;
+ }
+ /* This way is better for a COMPONENT_REF since it can
+ simplify the offset for a component. */
+ adr = build_unary_op (ADDR_EXPR, exp, 1);
+ return convert (ptrtype, adr);
+ }
+ return exp;
+}
+
+/* Look up component name in the structure type definition.
+
+ If this component name is found indirectly within an anonymous union,
+ store in *INDIRECT the component which directly contains
+ that anonymous union. Otherwise, set *INDIRECT to 0. */
+
+static tree
+lookup_field (type, component, indirect)
+ tree type, component;
+ tree *indirect;
+{
+ tree field;
+
+ /* If TYPE_LANG_SPECIFIC is set, then it is a sorted array of pointers
+ to the field elements. Use a binary search on this array to quickly
+ find the element. Otherwise, do a linear search. TYPE_LANG_SPECIFIC
+ will always be set for structures which have many elements. */
+
+ if (TYPE_LANG_SPECIFIC (type))
+ {
+ int bot, top, half;
+ tree *field_array = &TYPE_LANG_SPECIFIC (type)->elts[0];
+
+ field = TYPE_FIELDS (type);
+ bot = 0;
+ top = TYPE_LANG_SPECIFIC (type)->len;
+ while (top - bot > 1)
+ {
+ HOST_WIDE_INT cmp;
+
+ half = (top - bot + 1) >> 1;
+ field = field_array[bot+half];
+
+ if (DECL_NAME (field) == NULL_TREE)
+ {
+ /* Step through all anon unions in linear fashion. */
+ while (DECL_NAME (field_array[bot]) == NULL_TREE)
+ {
+ tree anon, junk;
+
+ field = field_array[bot++];
+ anon = lookup_field (TREE_TYPE (field), component, &junk);
+ if (anon != NULL_TREE)
+ {
+ *indirect = field;
+ return anon;
+ }
+ }
+
+ /* Entire record is only anon unions. */
+ if (bot > top)
+ return NULL_TREE;
+
+ /* Restart the binary search, with new lower bound. */
+ continue;
+ }
+
+ cmp = (HOST_WIDE_INT) DECL_NAME (field) - (HOST_WIDE_INT) component;
+ if (cmp == 0)
+ break;
+ if (cmp < 0)
+ bot += half;
+ else
+ top = bot + half;
+ }
+
+ if (DECL_NAME (field_array[bot]) == component)
+ field = field_array[bot];
+ else if (DECL_NAME (field) != component)
+ field = 0;
+ }
+ else
+ {
+ for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+ {
+ if (DECL_NAME (field) == NULL_TREE)
+ {
+ tree junk;
+ tree anon = lookup_field (TREE_TYPE (field), component, &junk);
+ if (anon != NULL_TREE)
+ {
+ *indirect = field;
+ return anon;
+ }
+ }
+
+ if (DECL_NAME (field) == component)
+ break;
+ }
+ }
+
+ *indirect = NULL_TREE;
+ return field;
+}
+
+/* Make an expression to refer to the COMPONENT field of
+ structure or union value DATUM. COMPONENT is an IDENTIFIER_NODE. */
+
+tree
+build_component_ref (datum, component)
+ tree datum, component;
+{
+ register tree type = TREE_TYPE (datum);
+ register enum tree_code code = TREE_CODE (type);
+ register tree field = NULL;
+ register tree ref;
+
+ /* If DATUM is a COMPOUND_EXPR or COND_EXPR, move our reference inside it
+ unless we are not to support things not strictly ANSI. */
+ switch (TREE_CODE (datum))
+ {
+ case COMPOUND_EXPR:
+ {
+ tree value = build_component_ref (TREE_OPERAND (datum, 1), component);
+ return build (COMPOUND_EXPR, TREE_TYPE (value),
+ TREE_OPERAND (datum, 0), value);
+ }
+ case COND_EXPR:
+ return build_conditional_expr
+ (TREE_OPERAND (datum, 0),
+ build_component_ref (TREE_OPERAND (datum, 1), component),
+ build_component_ref (TREE_OPERAND (datum, 2), component));
+ }
+
+ /* See if there is a field or component with name COMPONENT. */
+
+ if (code == RECORD_TYPE || code == UNION_TYPE)
+ {
+ tree indirect = 0;
+
+ if (TYPE_SIZE (type) == 0)
+ {
+ incomplete_type_error (NULL_TREE, type);
+ return error_mark_node;
+ }
+
+ field = lookup_field (type, component, &indirect);
+
+ if (!field)
+ {
+ error (code == RECORD_TYPE
+ ? "structure has no member named `%s'"
+ : "union has no member named `%s'",
+ IDENTIFIER_POINTER (component));
+ return error_mark_node;
+ }
+ if (TREE_TYPE (field) == error_mark_node)
+ return error_mark_node;
+
+ /* If FIELD was found buried within an anonymous union,
+ make one COMPONENT_REF to get that anonymous union,
+ then fall thru to make a second COMPONENT_REF to get FIELD. */
+ if (indirect != 0)
+ {
+ ref = build (COMPONENT_REF, TREE_TYPE (indirect), datum, indirect);
+ if (TREE_READONLY (datum) || TREE_READONLY (indirect))
+ TREE_READONLY (ref) = 1;
+ if (TREE_THIS_VOLATILE (datum) || TREE_THIS_VOLATILE (indirect))
+ TREE_THIS_VOLATILE (ref) = 1;
+ datum = ref;
+ }
+
+ ref = build (COMPONENT_REF, TREE_TYPE (field), datum, field);
+
+ if (TREE_READONLY (datum) || TREE_READONLY (field))
+ TREE_READONLY (ref) = 1;
+ if (TREE_THIS_VOLATILE (datum) || TREE_THIS_VOLATILE (field))
+ TREE_THIS_VOLATILE (ref) = 1;
+
+ return ref;
+ }
+ else if (code != ERROR_MARK)
+ error ("request for member `%s' in something not a structure or union",
+ IDENTIFIER_POINTER (component));
+
+ return error_mark_node;
+}
+
+/* Given an expression PTR for a pointer, return an expression
+ for the value pointed to.
+ ERRORSTRING is the name of the operator to appear in error messages. */
+
+tree
+build_indirect_ref (ptr, errorstring)
+ tree ptr;
+ char *errorstring;
+{
+ register tree pointer = default_conversion (ptr);
+ register tree type = TREE_TYPE (pointer);
+
+ if (TREE_CODE (type) == POINTER_TYPE)
+ {
+ if (TREE_CODE (pointer) == ADDR_EXPR
+ && !flag_volatile
+ && (TREE_TYPE (TREE_OPERAND (pointer, 0))
+ == TREE_TYPE (type)))
+ return TREE_OPERAND (pointer, 0);
+ else
+ {
+ tree t = TREE_TYPE (type);
+ register tree ref = build1 (INDIRECT_REF,
+ TYPE_MAIN_VARIANT (t), pointer);
+
+ if (TYPE_SIZE (t) == 0 && TREE_CODE (t) != ARRAY_TYPE)
+ {
+ error ("dereferencing pointer to incomplete type");
+ return error_mark_node;
+ }
+ if (TREE_CODE (t) == VOID_TYPE)
+ warning ("dereferencing `void *' pointer");
+
+ /* We *must* set TREE_READONLY when dereferencing a pointer to const,
+ so that we get the proper error message if the result is used
+ to assign to. Also, &* is supposed to be a no-op.
+ And ANSI C seems to specify that the type of the result
+ should be the const type. */
+ /* A de-reference of a pointer to const is not a const. It is valid
+ to change it via some other pointer. */
+ TREE_READONLY (ref) = TYPE_READONLY (t);
+ TREE_SIDE_EFFECTS (ref)
+ = TYPE_VOLATILE (t) || TREE_SIDE_EFFECTS (pointer) || flag_volatile;
+ TREE_THIS_VOLATILE (ref) = TYPE_VOLATILE (t);
+ return ref;
+ }
+ }
+ else if (TREE_CODE (pointer) != ERROR_MARK)
+ error ("invalid type argument of `%s'", errorstring);
+ return error_mark_node;
+}
+
+/* This handles expressions of the form "a[i]", which denotes
+ an array reference.
+
+ This is logically equivalent in C to *(a+i), but we may do it differently.
+ If A is a variable or a member, we generate a primitive ARRAY_REF.
+ This avoids forcing the array out of registers, and can work on
+ arrays that are not lvalues (for example, members of structures returned
+ by functions). */
+
+tree
+build_array_ref (array, index)
+ tree array, index;
+{
+ if (index == 0)
+ {
+ error ("subscript missing in array reference");
+ return error_mark_node;
+ }
+
+ if (TREE_TYPE (array) == error_mark_node
+ || TREE_TYPE (index) == error_mark_node)
+ return error_mark_node;
+
+ if (TREE_CODE (TREE_TYPE (array)) == ARRAY_TYPE
+ && TREE_CODE (array) != INDIRECT_REF)
+ {
+ tree rval, type;
+
+ /* Subscripting with type char is likely to lose
+ on a machine where chars are signed.
+ So warn on any machine, but optionally.
+ Don't warn for unsigned char since that type is safe.
+ Don't warn for signed char because anyone who uses that
+ must have done so deliberately. */
+ if (warn_char_subscripts
+ && TYPE_MAIN_VARIANT (TREE_TYPE (index)) == char_type_node)
+ warning ("array subscript has type `char'");
+
+ /* Apply default promotions *after* noticing character types. */
+ index = default_conversion (index);
+
+ /* Require integer *after* promotion, for sake of enums. */
+ if (TREE_CODE (TREE_TYPE (index)) != INTEGER_TYPE)
+ {
+ error ("array subscript is not an integer");
+ return error_mark_node;
+ }
+
+ /* An array that is indexed by a non-constant
+ cannot be stored in a register; we must be able to do
+ address arithmetic on its address.
+ Likewise an array of elements of variable size. */
+ if (TREE_CODE (index) != INTEGER_CST
+ || (TYPE_SIZE (TREE_TYPE (TREE_TYPE (array))) != 0
+ && TREE_CODE (TYPE_SIZE (TREE_TYPE (TREE_TYPE (array)))) != INTEGER_CST))
+ {
+ if (mark_addressable (array) == 0)
+ return error_mark_node;
+ }
+ /* An array that is indexed by a constant value which is not within
+ the array bounds cannot be stored in a register either; because we
+ would get a crash in store_bit_field/extract_bit_field when trying
+ to access a non-existent part of the register. */
+ if (TREE_CODE (index) == INTEGER_CST
+ && TYPE_VALUES (TREE_TYPE (array))
+ && ! int_fits_type_p (index, TYPE_VALUES (TREE_TYPE (array))))
+ {
+ if (mark_addressable (array) == 0)
+ return error_mark_node;
+ }
+
+ if (pedantic && !lvalue_p (array))
+ {
+ if (DECL_REGISTER (array))
+ pedwarn ("ANSI C forbids subscripting `register' array");
+ else
+ pedwarn ("ANSI C forbids subscripting non-lvalue array");
+ }
+
+ if (pedantic)
+ {
+ tree foo = array;
+ while (TREE_CODE (foo) == COMPONENT_REF)
+ foo = TREE_OPERAND (foo, 0);
+ if (TREE_CODE (foo) == VAR_DECL && DECL_REGISTER (foo))
+ pedwarn ("ANSI C forbids subscripting non-lvalue array");
+ }
+
+ type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (array)));
+ rval = build (ARRAY_REF, type, array, index);
+ /* Array ref is const/volatile if the array elements are
+ or if the array is. */
+ TREE_READONLY (rval)
+ |= (TYPE_READONLY (TREE_TYPE (TREE_TYPE (array)))
+ | TREE_READONLY (array));
+ TREE_SIDE_EFFECTS (rval)
+ |= (TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (array)))
+ | TREE_SIDE_EFFECTS (array));
+ TREE_THIS_VOLATILE (rval)
+ |= (TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (array)))
+ /* This was added by rms on 16 Nov 91.
+ It fixes vol struct foo *a; a->elts[1]
+ in an inline function.
+ Hope it doesn't break something else. */
+ | TREE_THIS_VOLATILE (array));
+ return require_complete_type (fold (rval));
+ }
+
+ {
+ tree ar = default_conversion (array);
+ tree ind = default_conversion (index);
+
+ /* Put the integer in IND to simplify error checking. */
+ if (TREE_CODE (TREE_TYPE (ar)) == INTEGER_TYPE)
+ {
+ tree temp = ar;
+ ar = ind;
+ ind = temp;
+ }
+
+ if (ar == error_mark_node)
+ return ar;
+
+ if (TREE_CODE (TREE_TYPE (ar)) != POINTER_TYPE)
+ {
+ error ("subscripted value is neither array nor pointer");
+ return error_mark_node;
+ }
+ if (TREE_CODE (TREE_TYPE (ind)) != INTEGER_TYPE)
+ {
+ error ("array subscript is not an integer");
+ return error_mark_node;
+ }
+
+ return build_indirect_ref (build_binary_op (PLUS_EXPR, ar, ind, 0),
+ "array indexing");
+ }
+}
+
+/* Build a function call to function FUNCTION with parameters PARAMS.
+ PARAMS is a list--a chain of TREE_LIST nodes--in which the
+ TREE_VALUE of each node is a parameter-expression.
+ FUNCTION's data type may be a function type or a pointer-to-function. */
+
+tree
+build_function_call (function, params)
+ tree function, params;
+{
+ register tree fntype, fundecl = 0;
+ register tree coerced_params;
+ tree name = NULL_TREE, assembler_name = NULL_TREE;
+
+ /* Strip NON_LVALUE_EXPRs, etc., since we aren't using as an lvalue. */
+ STRIP_TYPE_NOPS (function);
+
+ /* Convert anything with function type to a pointer-to-function. */
+ if (TREE_CODE (function) == FUNCTION_DECL)
+ {
+ name = DECL_NAME (function);
+ assembler_name = DECL_ASSEMBLER_NAME (function);
+
+ /* Differs from default_conversion by not setting TREE_ADDRESSABLE
+ (because calling an inline function does not mean the function
+ needs to be separately compiled). */
+ fntype = build_type_variant (TREE_TYPE (function),
+ TREE_READONLY (function),
+ TREE_THIS_VOLATILE (function));
+ fundecl = function;
+ function = build1 (ADDR_EXPR, build_pointer_type (fntype), function);
+ }
+ else
+ function = default_conversion (function);
+
+ fntype = TREE_TYPE (function);
+
+ if (TREE_CODE (fntype) == ERROR_MARK)
+ return error_mark_node;
+
+ if (!(TREE_CODE (fntype) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (fntype)) == FUNCTION_TYPE))
+ {
+ error ("called object is not a function");
+ return error_mark_node;
+ }
+
+ /* fntype now gets the type of function pointed to. */
+ fntype = TREE_TYPE (fntype);
+
+ /* Convert the parameters to the types declared in the
+ function prototype, or apply default promotions. */
+
+ coerced_params
+ = convert_arguments (TYPE_ARG_TYPES (fntype), params, name, fundecl);
+
+ /* Check for errors in format strings. */
+
+ if (warn_format && (name || assembler_name))
+ check_function_format (name, assembler_name, coerced_params);
+
+ /* Recognize certain built-in functions so we can make tree-codes
+ other than CALL_EXPR. We do this when it enables fold-const.c
+ to do something useful. */
+
+ if (TREE_CODE (function) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (function, 0)) == FUNCTION_DECL
+ && DECL_BUILT_IN (TREE_OPERAND (function, 0)))
+ switch (DECL_FUNCTION_CODE (TREE_OPERAND (function, 0)))
+ {
+ case BUILT_IN_ABS:
+ case BUILT_IN_LABS:
+ case BUILT_IN_FABS:
+ if (coerced_params == 0)
+ return integer_zero_node;
+ return build_unary_op (ABS_EXPR, TREE_VALUE (coerced_params), 0);
+ }
+
+ {
+ register tree result
+ = build (CALL_EXPR, TREE_TYPE (fntype),
+ function, coerced_params, NULL_TREE);
+
+ TREE_SIDE_EFFECTS (result) = 1;
+ if (TREE_TYPE (result) == void_type_node)
+ return result;
+ return require_complete_type (result);
+ }
+}
+
+/* Convert the argument expressions in the list VALUES
+ to the types in the list TYPELIST. The result is a list of converted
+ argument expressions.
+
+ If TYPELIST is exhausted, or when an element has NULL as its type,
+ perform the default conversions.
+
+ PARMLIST is the chain of parm decls for the function being called.
+ It may be 0, if that info is not available.
+ It is used only for generating error messages.
+
+ NAME is an IDENTIFIER_NODE or 0. It is used only for error messages.
+
+ This is also where warnings about wrong number of args are generated.
+
+ Both VALUES and the returned value are chains of TREE_LIST nodes
+ with the elements of the list in the TREE_VALUE slots of those nodes. */
+
+static tree
+convert_arguments (typelist, values, name, fundecl)
+ tree typelist, values, name, fundecl;
+{
+ register tree typetail, valtail;
+ register tree result = NULL;
+ int parmnum;
+
+ /* Scan the given expressions and types, producing individual
+ converted arguments and pushing them on RESULT in reverse order. */
+
+ for (valtail = values, typetail = typelist, parmnum = 0;
+ valtail;
+ valtail = TREE_CHAIN (valtail), parmnum++)
+ {
+ register tree type = typetail ? TREE_VALUE (typetail) : 0;
+ register tree val = TREE_VALUE (valtail);
+
+ if (type == void_type_node)
+ {
+ if (name)
+ error ("too many arguments to function `%s'",
+ IDENTIFIER_POINTER (name));
+ else
+ error ("too many arguments to function");
+ break;
+ }
+
+ /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
+ /* Do not use STRIP_NOPS here! We do not want an enumerator with value 0
+ to convert automatically to a pointer. */
+ if (TREE_CODE (val) == NON_LVALUE_EXPR)
+ val = TREE_OPERAND (val, 0);
+
+ if (TREE_CODE (TREE_TYPE (val)) == ARRAY_TYPE
+ || TREE_CODE (TREE_TYPE (val)) == FUNCTION_TYPE)
+ val = default_conversion (val);
+
+ val = require_complete_type (val);
+
+ if (type != 0)
+ {
+ /* Formal parm type is specified by a function prototype. */
+ tree parmval;
+
+ if (TYPE_SIZE (type) == 0)
+ {
+ error ("type of formal parameter %d is incomplete", parmnum + 1);
+ parmval = val;
+ }
+ else
+ {
+#if 0 /* This turns out not to win--there's no way to write a prototype
+ for a function whose arg type is a union with no tag. */
+ /* Nameless union automatically casts the types it contains. */
+ if (TREE_CODE (type) == UNION_TYPE && TYPE_NAME (type) == 0)
+ {
+ tree field;
+
+ for (field = TYPE_FIELDS (type); field;
+ field = TREE_CHAIN (field))
+ if (comptypes (TYPE_MAIN_VARIANT (TREE_TYPE (field)),
+ TYPE_MAIN_VARIANT (TREE_TYPE (val))))
+ break;
+
+ if (field)
+ val = build1 (CONVERT_EXPR, type, val);
+ }
+#endif
+
+ /* Optionally warn about conversions that
+ differ from the default conversions. */
+ if (warn_conversion)
+ {
+ int formal_prec = TYPE_PRECISION (type);
+
+ if (TREE_CODE (type) != REAL_TYPE
+ && TREE_CODE (TREE_TYPE (val)) == REAL_TYPE)
+ warn_for_assignment ("%s as integer rather than floating due to prototype", (char *) 0, name, parmnum + 1);
+ else if (TREE_CODE (type) == REAL_TYPE
+ && TREE_CODE (TREE_TYPE (val)) != REAL_TYPE)
+ warn_for_assignment ("%s as floating rather than integer due to prototype", (char *) 0, name, parmnum + 1);
+ else if (TREE_CODE (type) == REAL_TYPE
+ && TREE_CODE (TREE_TYPE (val)) == REAL_TYPE)
+ {
+ /* Warn if any argument is passed as `float',
+ since without a prototype it would be `double'. */
+ if (formal_prec == TYPE_PRECISION (float_type_node))
+ warn_for_assignment ("%s as `float' rather than `double' due to prototype", (char *) 0, name, parmnum + 1);
+ }
+ /* Detect integer changing in width or signedness. */
+ else if ((TREE_CODE (type) == INTEGER_TYPE
+ || TREE_CODE (type) == ENUMERAL_TYPE)
+ && (TREE_CODE (TREE_TYPE (val)) == INTEGER_TYPE
+ || TREE_CODE (TREE_TYPE (val)) == ENUMERAL_TYPE))
+ {
+ tree would_have_been = default_conversion (val);
+ tree type1 = TREE_TYPE (would_have_been);
+
+ if (TREE_CODE (type) == ENUMERAL_TYPE
+ && type == TREE_TYPE (val))
+ /* No warning if function asks for enum
+ and the actual arg is that enum type. */
+ ;
+ else if (formal_prec != TYPE_PRECISION (type1))
+ warn_for_assignment ("%s with different width due to prototype", (char *) 0, name, parmnum + 1);
+ else if (TREE_UNSIGNED (type) == TREE_UNSIGNED (type1))
+ ;
+ /* Don't complain if the formal parameter type
+ is an enum, because we can't tell now whether
+ the value was an enum--even the same enum. */
+ else if (TREE_CODE (type) == ENUMERAL_TYPE)
+ ;
+ else if (TREE_CODE (val) == INTEGER_CST
+ && int_fits_type_p (val, type))
+ /* Change in signedness doesn't matter
+ if a constant value is unaffected. */
+ ;
+ /* Likewise for a constant in a NOP_EXPR. */
+ else if (TREE_CODE (val) == NOP_EXPR
+ && TREE_CODE (TREE_OPERAND (val, 0)) == INTEGER_CST
+ && int_fits_type_p (TREE_OPERAND (val, 0), type))
+ ;
+#if 0 /* We never get such tree structure here. */
+ else if (TREE_CODE (TREE_TYPE (val)) == ENUMERAL_TYPE
+ && int_fits_type_p (TYPE_MIN_VALUE (TREE_TYPE (val)), type)
+ && int_fits_type_p (TYPE_MAX_VALUE (TREE_TYPE (val)), type))
+ /* Change in signedness doesn't matter
+ if an enum value is unaffected. */
+ ;
+#endif
+ /* If the value is extended from a narrower
+ unsigned type, it doesn't matter whether we
+ pass it as signed or unsigned; the value
+ certainly is the same either way. */
+ else if (TYPE_PRECISION (TREE_TYPE (val)) < TYPE_PRECISION (type)
+ && TREE_UNSIGNED (TREE_TYPE (val)))
+ ;
+ else if (TREE_UNSIGNED (type))
+ warn_for_assignment ("%s as unsigned due to prototype", (char *) 0, name, parmnum + 1);
+ else
+ warn_for_assignment ("%s as signed due to prototype", (char *) 0, name, parmnum + 1);
+ }
+ }
+
+ parmval = convert_for_assignment (type, val,
+ (char *)0, /* arg passing */
+ fundecl, name, parmnum + 1);
+
+#ifdef PROMOTE_PROTOTYPES
+ if ((TREE_CODE (type) == INTEGER_TYPE
+ || TREE_CODE (type) == ENUMERAL_TYPE)
+ && (TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node)))
+ parmval = default_conversion (parmval);
+#endif
+ }
+ result = tree_cons (NULL_TREE, parmval, result);
+ }
+ else if (TREE_CODE (TREE_TYPE (val)) == REAL_TYPE
+ && (TYPE_PRECISION (TREE_TYPE (val))
+ < TYPE_PRECISION (double_type_node)))
+ /* Convert `float' to `double'. */
+ result = tree_cons (NULL_TREE, convert (double_type_node, val), result);
+ else
+ /* Convert `short' and `char' to full-size `int'. */
+ result = tree_cons (NULL_TREE, default_conversion (val), result);
+
+ if (typetail)
+ typetail = TREE_CHAIN (typetail);
+ }
+
+ if (typetail != 0 && TREE_VALUE (typetail) != void_type_node)
+ {
+ if (name)
+ error ("too few arguments to function `%s'",
+ IDENTIFIER_POINTER (name));
+ else
+ error ("too few arguments to function");
+ }
+
+ return nreverse (result);
+}
+
+/* This is the entry point used by the parser
+ for binary operators in the input.
+ In addition to constructing the expression,
+ we check for operands that were written with other binary operators
+ in a way that is likely to confuse the user. */
+
+tree
+parser_build_binary_op (code, arg1, arg2)
+ enum tree_code code;
+ tree arg1, arg2;
+{
+ tree result = build_binary_op (code, arg1, arg2, 1);
+
+ char class;
+ char class1 = TREE_CODE_CLASS (TREE_CODE (arg1));
+ char class2 = TREE_CODE_CLASS (TREE_CODE (arg2));
+ enum tree_code code1 = ERROR_MARK;
+ enum tree_code code2 = ERROR_MARK;
+
+ if (class1 == 'e' || class1 == '1'
+ || class1 == '2' || class1 == '<')
+ code1 = C_EXP_ORIGINAL_CODE (arg1);
+ if (class2 == 'e' || class2 == '1'
+ || class2 == '2' || class2 == '<')
+ code2 = C_EXP_ORIGINAL_CODE (arg2);
+
+ /* Check for cases such as x+y<<z which users are likely
+ to misinterpret. If parens are used, C_EXP_ORIGINAL_CODE
+ is cleared to prevent these warnings. */
+ if (warn_parentheses)
+ {
+ if (code == LSHIFT_EXPR || code == RSHIFT_EXPR)
+ {
+ if (code1 == PLUS_EXPR || code1 == MINUS_EXPR
+ || code2 == PLUS_EXPR || code2 == MINUS_EXPR)
+ warning ("suggest parentheses around + or - inside shift");
+ }
+
+ if (code == TRUTH_ORIF_EXPR)
+ {
+ if (code1 == TRUTH_ANDIF_EXPR
+ || code2 == TRUTH_ANDIF_EXPR)
+ warning ("suggest parentheses around && within ||");
+ }
+
+ if (code == BIT_IOR_EXPR)
+ {
+ if (code1 == BIT_AND_EXPR || code1 == BIT_XOR_EXPR
+ || code1 == PLUS_EXPR || code1 == MINUS_EXPR
+ || code2 == BIT_AND_EXPR || code2 == BIT_XOR_EXPR
+ || code2 == PLUS_EXPR || code2 == MINUS_EXPR)
+ warning ("suggest parentheses around arithmetic in operand of |");
+ }
+
+ if (code == BIT_XOR_EXPR)
+ {
+ if (code1 == BIT_AND_EXPR
+ || code1 == PLUS_EXPR || code1 == MINUS_EXPR
+ || code2 == BIT_AND_EXPR
+ || code2 == PLUS_EXPR || code2 == MINUS_EXPR)
+ warning ("suggest parentheses around arithmetic in operand of ^");
+ }
+
+ if (code == BIT_AND_EXPR)
+ {
+ if (code1 == PLUS_EXPR || code1 == MINUS_EXPR
+ || code2 == PLUS_EXPR || code2 == MINUS_EXPR)
+ warning ("suggest parentheses around + or - in operand of &");
+ }
+ }
+
+ /* Similarly, check for cases like 1<=i<=10 that are probably errors. */
+ if (TREE_CODE_CLASS (code) == '<' && extra_warnings
+ && (TREE_CODE_CLASS (code1) == '<' || TREE_CODE_CLASS (code2) == '<'))
+ warning ("comparisons like X<=Y<=Z do not have their mathematical meaning");
+
+ unsigned_conversion_warning (result, arg1);
+ unsigned_conversion_warning (result, arg2);
+ overflow_warning (result);
+
+ class = TREE_CODE_CLASS (TREE_CODE (result));
+
+ /* Record the code that was specified in the source,
+ for the sake of warnings about confusing nesting. */
+ if (class == 'e' || class == '1'
+ || class == '2' || class == '<')
+ C_SET_EXP_ORIGINAL_CODE (result, code);
+ else
+ {
+ int flag = TREE_CONSTANT (result);
+ /* We used to use NOP_EXPR rather than NON_LVALUE_EXPR
+ so that convert_for_assignment wouldn't strip it.
+ That way, we got warnings for things like p = (1 - 1).
+ But it turns out we should not get those warnings. */
+ result = build1 (NON_LVALUE_EXPR, TREE_TYPE (result), result);
+ C_SET_EXP_ORIGINAL_CODE (result, code);
+ TREE_CONSTANT (result) = flag;
+ }
+
+ return result;
+}
+
+/* Build a binary-operation expression without default conversions.
+ CODE is the kind of expression to build.
+ This function differs from `build' in several ways:
+ the data type of the result is computed and recorded in it,
+ warnings are generated if arg data types are invalid,
+ special handling for addition and subtraction of pointers is known,
+ and some optimization is done (operations on narrow ints
+ are done in the narrower type when that gives the same result).
+ Constant folding is also done before the result is returned.
+
+ Note that the operands will never have enumeral types, or function
+ or array types, because either they will have the default conversions
+ performed or they have both just been converted to some other type in which
+ the arithmetic is to be done. */
+
+tree
+build_binary_op (code, orig_op0, orig_op1, convert_p)
+ enum tree_code code;
+ tree orig_op0, orig_op1;
+ int convert_p;
+{
+ tree type0, type1;
+ register enum tree_code code0, code1;
+ tree op0, op1;
+
+ /* Expression code to give to the expression when it is built.
+ Normally this is CODE, which is what the caller asked for,
+ but in some special cases we change it. */
+ register enum tree_code resultcode = code;
+
+ /* Data type in which the computation is to be performed.
+ In the simplest cases this is the common type of the arguments. */
+ register tree result_type = NULL;
+
+ /* Nonzero means operands have already been type-converted
+ in whatever way is necessary.
+ Zero means they need to be converted to RESULT_TYPE. */
+ int converted = 0;
+
+ /* Nonzero means after finally constructing the expression
+ give it this type. Otherwise, give it type RESULT_TYPE. */
+ tree final_type = 0;
+
+ /* Nonzero if this is an operation like MIN or MAX which can
+ safely be computed in short if both args are promoted shorts.
+ Also implies COMMON.
+ -1 indicates a bitwise operation; this makes a difference
+ in the exact conditions for when it is safe to do the operation
+ in a narrower mode. */
+ int shorten = 0;
+
+ /* Nonzero if this is a comparison operation;
+ if both args are promoted shorts, compare the original shorts.
+ Also implies COMMON. */
+ int short_compare = 0;
+
+ /* Nonzero if this is a right-shift operation, which can be computed on the
+ original short and then promoted if the operand is a promoted short. */
+ int short_shift = 0;
+
+ /* Nonzero means set RESULT_TYPE to the common type of the args. */
+ int common = 0;
+
+ if (convert_p)
+ {
+ op0 = default_conversion (orig_op0);
+ op1 = default_conversion (orig_op1);
+ }
+ else
+ {
+ op0 = orig_op0;
+ op1 = orig_op1;
+ }
+
+ type0 = TREE_TYPE (op0);
+ type1 = TREE_TYPE (op1);
+
+ /* The expression codes of the data types of the arguments tell us
+ whether the arguments are integers, floating, pointers, etc. */
+ code0 = TREE_CODE (type0);
+ code1 = TREE_CODE (type1);
+
+ /* Strip NON_LVALUE_EXPRs, etc., since we aren't using as an lvalue. */
+ STRIP_TYPE_NOPS (op0);
+ STRIP_TYPE_NOPS (op1);
+
+ /* If an error was already reported for one of the arguments,
+ avoid reporting another error. */
+
+ if (code0 == ERROR_MARK || code1 == ERROR_MARK)
+ return error_mark_node;
+
+ switch (code)
+ {
+ case PLUS_EXPR:
+ /* Handle the pointer + int case. */
+ if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE)
+ return pointer_int_sum (PLUS_EXPR, op0, op1);
+ else if (code1 == POINTER_TYPE && code0 == INTEGER_TYPE)
+ return pointer_int_sum (PLUS_EXPR, op1, op0);
+ else
+ common = 1;
+ break;
+
+ case MINUS_EXPR:
+ /* Subtraction of two similar pointers.
+ We must subtract them as integers, then divide by object size. */
+ if (code0 == POINTER_TYPE && code1 == POINTER_TYPE
+ && comp_target_types (type0, type1))
+ return pointer_diff (op0, op1);
+ /* Handle pointer minus int. Just like pointer plus int. */
+ else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE)
+ return pointer_int_sum (MINUS_EXPR, op0, op1);
+ else
+ common = 1;
+ break;
+
+ case MULT_EXPR:
+ common = 1;
+ break;
+
+ case TRUNC_DIV_EXPR:
+ case CEIL_DIV_EXPR:
+ case FLOOR_DIV_EXPR:
+ case ROUND_DIV_EXPR:
+ case EXACT_DIV_EXPR:
+ if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE
+ || code0 == COMPLEX_TYPE)
+ && (code1 == INTEGER_TYPE || code1 == REAL_TYPE
+ || code1 == COMPLEX_TYPE))
+ {
+ if (!(code0 == INTEGER_TYPE && code1 == INTEGER_TYPE))
+ resultcode = RDIV_EXPR;
+ else
+ {
+ /* Although it would be tempting to shorten always here, that
+ loses on some targets, since the modulo instruction is
+ undefined if the quotient can't be represented in the
+ computation mode. We shorten only if unsigned or if
+ dividing by something we know != -1. */
+ shorten = (TREE_UNSIGNED (TREE_TYPE (orig_op0))
+ || (TREE_CODE (op1) == INTEGER_CST
+ && (TREE_INT_CST_LOW (op1) != -1
+ || TREE_INT_CST_HIGH (op1) != -1)));
+ }
+ common = 1;
+ }
+ break;
+
+ case BIT_AND_EXPR:
+ case BIT_ANDTC_EXPR:
+ case BIT_IOR_EXPR:
+ case BIT_XOR_EXPR:
+ if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)
+ shorten = -1;
+ /* If one operand is a constant, and the other is a short type
+ that has been converted to an int,
+ really do the work in the short type and then convert the
+ result to int. If we are lucky, the constant will be 0 or 1
+ in the short type, making the entire operation go away. */
+ if (TREE_CODE (op0) == INTEGER_CST
+ && TREE_CODE (op1) == NOP_EXPR
+ && TYPE_PRECISION (type1) > TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (op1, 0)))
+ && TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (op1, 0))))
+ {
+ final_type = result_type;
+ op1 = TREE_OPERAND (op1, 0);
+ result_type = TREE_TYPE (op1);
+ }
+ if (TREE_CODE (op1) == INTEGER_CST
+ && TREE_CODE (op0) == NOP_EXPR
+ && TYPE_PRECISION (type0) > TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (op0, 0)))
+ && TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (op0, 0))))
+ {
+ final_type = result_type;
+ op0 = TREE_OPERAND (op0, 0);
+ result_type = TREE_TYPE (op0);
+ }
+ break;
+
+ case TRUNC_MOD_EXPR:
+ case FLOOR_MOD_EXPR:
+ if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)
+ {
+ /* Although it would be tempting to shorten always here, that loses
+ on some targets, since the modulo instruction is undefined if the
+ quotient can't be represented in the computation mode. We shorten
+ only if unsigned or if dividing by something we know != -1. */
+ shorten = (TREE_UNSIGNED (TREE_TYPE (orig_op0))
+ || (TREE_CODE (op1) == INTEGER_CST
+ && (TREE_INT_CST_LOW (op1) != -1
+ || TREE_INT_CST_HIGH (op1) != -1)));
+ common = 1;
+ }
+ break;
+
+ case TRUTH_ANDIF_EXPR:
+ case TRUTH_ORIF_EXPR:
+ case TRUTH_AND_EXPR:
+ case TRUTH_OR_EXPR:
+ case TRUTH_XOR_EXPR:
+ if ((code0 == INTEGER_TYPE || code0 == POINTER_TYPE
+ || code0 == REAL_TYPE || code0 == COMPLEX_TYPE)
+ && (code1 == INTEGER_TYPE || code1 == POINTER_TYPE
+ || code1 == REAL_TYPE || code1 == COMPLEX_TYPE))
+ {
+ /* Result of these operations is always an int,
+ but that does not mean the operands should be
+ converted to ints! */
+ result_type = integer_type_node;
+ op0 = truthvalue_conversion (op0);
+ op1 = truthvalue_conversion (op1);
+ converted = 1;
+ }
+ break;
+
+ /* Shift operations: result has same type as first operand;
+ always convert second operand to int.
+ Also set SHORT_SHIFT if shifting rightward. */
+
+ case RSHIFT_EXPR:
+ if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)
+ {
+ if (TREE_CODE (op1) == INTEGER_CST)
+ {
+ if (tree_int_cst_sgn (op1) < 0)
+ warning ("right shift count is negative");
+ else
+ {
+ if (TREE_INT_CST_LOW (op1) | TREE_INT_CST_HIGH (op1))
+ short_shift = 1;
+ if (TREE_INT_CST_HIGH (op1) != 0
+ || ((unsigned HOST_WIDE_INT) TREE_INT_CST_LOW (op1)
+ >= TYPE_PRECISION (type0)))
+ warning ("right shift count >= width of type");
+ }
+ }
+ /* Use the type of the value to be shifted.
+ This is what most traditional C compilers do. */
+ result_type = type0;
+ /* Unless traditional, convert the shift-count to an integer,
+ regardless of size of value being shifted. */
+ if (! flag_traditional)
+ {
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (op1)) != integer_type_node)
+ op1 = convert (integer_type_node, op1);
+ /* Avoid converting op1 to result_type later. */
+ converted = 1;
+ }
+ }
+ break;
+
+ case LSHIFT_EXPR:
+ if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)
+ {
+ if (TREE_CODE (op1) == INTEGER_CST)
+ {
+ if (tree_int_cst_sgn (op1) < 0)
+ warning ("left shift count is negative");
+ else if (TREE_INT_CST_HIGH (op1) != 0
+ || ((unsigned HOST_WIDE_INT) TREE_INT_CST_LOW (op1)
+ >= TYPE_PRECISION (type0)))
+ warning ("left shift count >= width of type");
+ }
+ /* Use the type of the value to be shifted.
+ This is what most traditional C compilers do. */
+ result_type = type0;
+ /* Unless traditional, convert the shift-count to an integer,
+ regardless of size of value being shifted. */
+ if (! flag_traditional)
+ {
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (op1)) != integer_type_node)
+ op1 = convert (integer_type_node, op1);
+ /* Avoid converting op1 to result_type later. */
+ converted = 1;
+ }
+ }
+ break;
+
+ case RROTATE_EXPR:
+ case LROTATE_EXPR:
+ if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)
+ {
+ if (TREE_CODE (op1) == INTEGER_CST)
+ {
+ if (tree_int_cst_sgn (op1) < 0)
+ warning ("shift count is negative");
+ else if (TREE_INT_CST_HIGH (op1) != 0
+ || ((unsigned HOST_WIDE_INT) TREE_INT_CST_LOW (op1)
+ >= TYPE_PRECISION (type0)))
+ warning ("shift count >= width of type");
+ }
+ /* Use the type of the value to be shifted.
+ This is what most traditional C compilers do. */
+ result_type = type0;
+ /* Unless traditional, convert the shift-count to an integer,
+ regardless of size of value being shifted. */
+ if (! flag_traditional)
+ {
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (op1)) != integer_type_node)
+ op1 = convert (integer_type_node, op1);
+ /* Avoid converting op1 to result_type later. */
+ converted = 1;
+ }
+ }
+ break;
+
+ case EQ_EXPR:
+ case NE_EXPR:
+ /* Result of comparison is always int,
+ but don't convert the args to int! */
+ result_type = integer_type_node;
+ converted = 1;
+ if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE
+ || code0 == COMPLEX_TYPE)
+ && (code1 == INTEGER_TYPE || code1 == REAL_TYPE
+ || code1 == COMPLEX_TYPE))
+ short_compare = 1;
+ else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE)
+ {
+ register tree tt0 = TREE_TYPE (type0);
+ register tree tt1 = TREE_TYPE (type1);
+ /* Anything compares with void *. void * compares with anything.
+ Otherwise, the targets must be compatible
+ and both must be object or both incomplete. */
+ if (comp_target_types (type0, type1))
+ ;
+ else if (TYPE_MAIN_VARIANT (tt0) == void_type_node)
+ {
+ /* op0 != orig_op0 detects the case of something
+ whose value is 0 but which isn't a valid null ptr const. */
+ if (pedantic && (!integer_zerop (op0) || op0 != orig_op0)
+ && TREE_CODE (tt1) == FUNCTION_TYPE)
+ pedwarn ("ANSI C forbids comparison of `void *' with function pointer");
+ }
+ else if (TYPE_MAIN_VARIANT (tt1) == void_type_node)
+ {
+ if (pedantic && (!integer_zerop (op1) || op1 != orig_op1)
+ && TREE_CODE (tt0) == FUNCTION_TYPE)
+ pedwarn ("ANSI C forbids comparison of `void *' with function pointer");
+ }
+ else
+ pedwarn ("comparison of distinct pointer types lacks a cast");
+ }
+ else if (code0 == POINTER_TYPE && TREE_CODE (op1) == INTEGER_CST
+ && integer_zerop (op1))
+ op1 = null_pointer_node;
+ else if (code1 == POINTER_TYPE && TREE_CODE (op0) == INTEGER_CST
+ && integer_zerop (op0))
+ op0 = null_pointer_node;
+ else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE)
+ {
+ if (! flag_traditional)
+ pedwarn ("comparison between pointer and integer");
+ op1 = convert (TREE_TYPE (op0), op1);
+ }
+ else if (code0 == INTEGER_TYPE && code1 == POINTER_TYPE)
+ {
+ if (! flag_traditional)
+ pedwarn ("comparison between pointer and integer");
+ op0 = convert (TREE_TYPE (op1), op0);
+ }
+ else
+ /* If args are not valid, clear out RESULT_TYPE
+ to cause an error message later. */
+ result_type = 0;
+ break;
+
+ case MAX_EXPR:
+ case MIN_EXPR:
+ if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE)
+ && (code1 == INTEGER_TYPE || code1 == REAL_TYPE))
+ shorten = 1;
+ else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE)
+ {
+ if (! comp_target_types (type0, type1))
+ pedwarn ("comparison of distinct pointer types lacks a cast");
+ else if (pedantic
+ && TREE_CODE (TREE_TYPE (type0)) == FUNCTION_TYPE)
+ pedwarn ("ANSI C forbids ordered comparisons of pointers to functions");
+ result_type = common_type (type0, type1);
+ }
+ break;
+
+ case LE_EXPR:
+ case GE_EXPR:
+ case LT_EXPR:
+ case GT_EXPR:
+ if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE)
+ && (code1 == INTEGER_TYPE || code1 == REAL_TYPE))
+ short_compare = 1;
+ else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE)
+ {
+ if (! comp_target_types (type0, type1))
+ pedwarn ("comparison of distinct pointer types lacks a cast");
+ else if ((TYPE_SIZE (TREE_TYPE (type0)) != 0)
+ != (TYPE_SIZE (TREE_TYPE (type1)) != 0))
+ pedwarn ("comparison of complete and incomplete pointers");
+ else if (pedantic
+ && TREE_CODE (TREE_TYPE (type0)) == FUNCTION_TYPE)
+ pedwarn ("ANSI C forbids ordered comparisons of pointers to functions");
+ result_type = integer_type_node;
+ }
+ else if (code0 == POINTER_TYPE && TREE_CODE (op1) == INTEGER_CST
+ && integer_zerop (op1))
+ {
+ result_type = integer_type_node;
+ op1 = null_pointer_node;
+ if (pedantic)
+ pedwarn ("ordered comparison of pointer with integer zero");
+ }
+ else if (code1 == POINTER_TYPE && TREE_CODE (op0) == INTEGER_CST
+ && integer_zerop (op0))
+ {
+ result_type = integer_type_node;
+ op0 = null_pointer_node;
+ if (pedantic)
+ pedwarn ("ordered comparison of pointer with integer zero");
+ }
+ else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE)
+ {
+ result_type = integer_type_node;
+ if (! flag_traditional)
+ pedwarn ("comparison between pointer and integer");
+ op1 = convert (TREE_TYPE (op0), op1);
+ }
+ else if (code0 == INTEGER_TYPE && code1 == POINTER_TYPE)
+ {
+ result_type = integer_type_node;
+ if (! flag_traditional)
+ pedwarn ("comparison between pointer and integer");
+ op0 = convert (TREE_TYPE (op1), op0);
+ }
+ converted = 1;
+ break;
+ }
+
+ if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE || code0 == COMPLEX_TYPE)
+ &&
+ (code1 == INTEGER_TYPE || code1 == REAL_TYPE || code1 == COMPLEX_TYPE))
+ {
+ int none_complex = (code0 != COMPLEX_TYPE && code1 != COMPLEX_TYPE);
+
+ if (shorten || common || short_compare)
+ result_type = common_type (type0, type1);
+
+ /* For certain operations (which identify themselves by shorten != 0)
+ if both args were extended from the same smaller type,
+ do the arithmetic in that type and then extend.
+
+ shorten !=0 and !=1 indicates a bitwise operation.
+ For them, this optimization is safe only if
+ both args are zero-extended or both are sign-extended.
+ Otherwise, we might change the result.
+ Eg, (short)-1 | (unsigned short)-1 is (int)-1
+ but calculated in (unsigned short) it would be (unsigned short)-1. */
+
+ if (shorten && none_complex)
+ {
+ int unsigned0, unsigned1;
+ tree arg0 = get_narrower (op0, &unsigned0);
+ tree arg1 = get_narrower (op1, &unsigned1);
+ /* UNS is 1 if the operation to be done is an unsigned one. */
+ int uns = TREE_UNSIGNED (result_type);
+ tree type;
+
+ final_type = result_type;
+
+ /* Handle the case that OP0 (or OP1) does not *contain* a conversion
+ but it *requires* conversion to FINAL_TYPE. */
+
+ if ((TYPE_PRECISION (TREE_TYPE (op0))
+ == TYPE_PRECISION (TREE_TYPE (arg0)))
+ && TREE_TYPE (op0) != final_type)
+ unsigned0 = TREE_UNSIGNED (TREE_TYPE (op0));
+ if ((TYPE_PRECISION (TREE_TYPE (op1))
+ == TYPE_PRECISION (TREE_TYPE (arg1)))
+ && TREE_TYPE (op1) != final_type)
+ unsigned1 = TREE_UNSIGNED (TREE_TYPE (op1));
+
+ /* Now UNSIGNED0 is 1 if ARG0 zero-extends to FINAL_TYPE. */
+
+ /* For bitwise operations, signedness of nominal type
+ does not matter. Consider only how operands were extended. */
+ if (shorten == -1)
+ uns = unsigned0;
+
+ /* Note that in all three cases below we refrain from optimizing
+ an unsigned operation on sign-extended args.
+ That would not be valid. */
+
+ /* Both args variable: if both extended in same way
+ from same width, do it in that width.
+ Do it unsigned if args were zero-extended. */
+ if ((TYPE_PRECISION (TREE_TYPE (arg0))
+ < TYPE_PRECISION (result_type))
+ && (TYPE_PRECISION (TREE_TYPE (arg1))
+ == TYPE_PRECISION (TREE_TYPE (arg0)))
+ && unsigned0 == unsigned1
+ && (unsigned0 || !uns))
+ result_type
+ = signed_or_unsigned_type (unsigned0,
+ common_type (TREE_TYPE (arg0), TREE_TYPE (arg1)));
+ else if (TREE_CODE (arg0) == INTEGER_CST
+ && (unsigned1 || !uns)
+ && (TYPE_PRECISION (TREE_TYPE (arg1))
+ < TYPE_PRECISION (result_type))
+ && (type = signed_or_unsigned_type (unsigned1,
+ TREE_TYPE (arg1)),
+ int_fits_type_p (arg0, type)))
+ result_type = type;
+ else if (TREE_CODE (arg1) == INTEGER_CST
+ && (unsigned0 || !uns)
+ && (TYPE_PRECISION (TREE_TYPE (arg0))
+ < TYPE_PRECISION (result_type))
+ && (type = signed_or_unsigned_type (unsigned0,
+ TREE_TYPE (arg0)),
+ int_fits_type_p (arg1, type)))
+ result_type = type;
+ }
+
+ /* Shifts can be shortened if shifting right. */
+
+ if (short_shift)
+ {
+ int unsigned_arg;
+ tree arg0 = get_narrower (op0, &unsigned_arg);
+
+ final_type = result_type;
+
+ if (arg0 == op0 && final_type == TREE_TYPE (op0))
+ unsigned_arg = TREE_UNSIGNED (TREE_TYPE (op0));
+
+ if (TYPE_PRECISION (TREE_TYPE (arg0)) < TYPE_PRECISION (result_type)
+ /* If arg is sign-extended and then unsigned-shifted,
+ we can simulate this with a signed shift in arg's type
+ only if the extended result is at least twice as wide
+ as the arg. Otherwise, the shift could use up all the
+ ones made by sign-extension and bring in zeros.
+ We can't optimize that case at all, but in most machines
+ it never happens because available widths are 2**N. */
+ && (!TREE_UNSIGNED (final_type)
+ || unsigned_arg
+ || 2 * TYPE_PRECISION (TREE_TYPE (arg0)) <= TYPE_PRECISION (result_type)))
+ {
+ /* Do an unsigned shift if the operand was zero-extended. */
+ result_type
+ = signed_or_unsigned_type (unsigned_arg,
+ TREE_TYPE (arg0));
+ /* Convert value-to-be-shifted to that type. */
+ if (TREE_TYPE (op0) != result_type)
+ op0 = convert (result_type, op0);
+ converted = 1;
+ }
+ }
+
+ /* Comparison operations are shortened too but differently.
+ They identify themselves by setting short_compare = 1. */
+
+ if (short_compare)
+ {
+ /* Don't write &op0, etc., because that would prevent op0
+ from being kept in a register.
+ Instead, make copies of the our local variables and
+ pass the copies by reference, then copy them back afterward. */
+ tree xop0 = op0, xop1 = op1, xresult_type = result_type;
+ enum tree_code xresultcode = resultcode;
+ tree val
+ = shorten_compare (&xop0, &xop1, &xresult_type, &xresultcode);
+ if (val != 0)
+ return val;
+ op0 = xop0, op1 = xop1, result_type = xresult_type;
+ resultcode = xresultcode;
+
+ if (extra_warnings)
+ {
+ tree op0_type = TREE_TYPE (orig_op0);
+ tree op1_type = TREE_TYPE (orig_op1);
+ int op0_unsigned = TREE_UNSIGNED (op0_type);
+ int op1_unsigned = TREE_UNSIGNED (op1_type);
+
+ /* Give warnings for comparisons between signed and unsigned
+ quantities that will fail. Do not warn if the signed quantity
+ is an unsuffixed integer literal (or some static constant
+ expression involving such literals) and it is positive.
+ Do not warn if the width of the unsigned quantity is less
+ than that of the signed quantity, since in this case all
+ values of the unsigned quantity fit in the signed quantity.
+ Do not warn if the signed type is the same size as the
+ result_type since sign extension does not cause trouble in
+ this case. */
+ /* Do the checking based on the original operand trees, so that
+ casts will be considered, but default promotions won't be. */
+ if (op0_unsigned != op1_unsigned
+ && ((op0_unsigned
+ && TYPE_PRECISION (op0_type) >= TYPE_PRECISION (op1_type)
+ && TYPE_PRECISION (op0_type) < TYPE_PRECISION (result_type)
+ && (TREE_CODE (op1) != INTEGER_CST
+ || (TREE_CODE (op1) == INTEGER_CST
+ && INT_CST_LT (op1, integer_zero_node))))
+ ||
+ (op1_unsigned
+ && TYPE_PRECISION (op1_type) >= TYPE_PRECISION (op0_type)
+ && TYPE_PRECISION (op1_type) < TYPE_PRECISION (result_type)
+ && (TREE_CODE (op0) != INTEGER_CST
+ || (TREE_CODE (op0) == INTEGER_CST
+ && INT_CST_LT (op0, integer_zero_node))))))
+ warning ("comparison between signed and unsigned");
+ }
+ }
+ }
+
+ /* At this point, RESULT_TYPE must be nonzero to avoid an error message.
+ If CONVERTED is zero, both args will be converted to type RESULT_TYPE.
+ Then the expression will be built.
+ It will be given type FINAL_TYPE if that is nonzero;
+ otherwise, it will be given type RESULT_TYPE. */
+
+ if (!result_type)
+ {
+ binary_op_error (code);
+ return error_mark_node;
+ }
+
+ if (! converted)
+ {
+ if (TREE_TYPE (op0) != result_type)
+ op0 = convert (result_type, op0);
+ if (TREE_TYPE (op1) != result_type)
+ op1 = convert (result_type, op1);
+ }
+
+ {
+ register tree result = build (resultcode, result_type, op0, op1);
+ register tree folded;
+
+ folded = fold (result);
+ if (folded == result)
+ TREE_CONSTANT (folded) = TREE_CONSTANT (op0) & TREE_CONSTANT (op1);
+ if (final_type != 0)
+ return convert (final_type, folded);
+ return folded;
+ }
+}
+
+/* Return a tree for the sum or difference (RESULTCODE says which)
+ of pointer PTROP and integer INTOP. */
+
+static tree
+pointer_int_sum (resultcode, ptrop, intop)
+ enum tree_code resultcode;
+ register tree ptrop, intop;
+{
+ tree size_exp;
+
+ register tree result;
+ register tree folded;
+
+ /* The result is a pointer of the same type that is being added. */
+
+ register tree result_type = TREE_TYPE (ptrop);
+
+ if (TREE_CODE (TREE_TYPE (result_type)) == VOID_TYPE)
+ {
+ if (pedantic || warn_pointer_arith)
+ pedwarn ("pointer of type `void *' used in arithmetic");
+ size_exp = integer_one_node;
+ }
+ else if (TREE_CODE (TREE_TYPE (result_type)) == FUNCTION_TYPE)
+ {
+ if (pedantic || warn_pointer_arith)
+ pedwarn ("pointer to a function used in arithmetic");
+ size_exp = integer_one_node;
+ }
+ else
+ size_exp = c_size_in_bytes (TREE_TYPE (result_type));
+
+ /* If what we are about to multiply by the size of the elements
+ contains a constant term, apply distributive law
+ and multiply that constant term separately.
+ This helps produce common subexpressions. */
+
+ if ((TREE_CODE (intop) == PLUS_EXPR || TREE_CODE (intop) == MINUS_EXPR)
+ && ! TREE_CONSTANT (intop)
+ && TREE_CONSTANT (TREE_OPERAND (intop, 1))
+ && TREE_CONSTANT (size_exp)
+ /* If the constant comes from pointer subtraction,
+ skip this optimization--it would cause an error. */
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (intop, 0))) == INTEGER_TYPE)
+ {
+ enum tree_code subcode = resultcode;
+ tree int_type = TREE_TYPE (intop);
+ if (TREE_CODE (intop) == MINUS_EXPR)
+ subcode = (subcode == PLUS_EXPR ? MINUS_EXPR : PLUS_EXPR);
+ /* Convert both subexpression types to the type of intop,
+ because weird cases involving pointer arithmetic
+ can result in a sum or difference with different type args. */
+ ptrop = build_binary_op (subcode, ptrop,
+ convert (int_type, TREE_OPERAND (intop, 1)), 1);
+ intop = convert (int_type, TREE_OPERAND (intop, 0));
+ }
+
+ /* Convert the integer argument to a type the same size as a pointer
+ so the multiply won't overflow spuriously. */
+
+ if (TYPE_PRECISION (TREE_TYPE (intop)) != POINTER_SIZE)
+ intop = convert (type_for_size (POINTER_SIZE, 0), intop);
+
+ /* Replace the integer argument with a suitable product by the object size.
+ Do this multiplication as signed, then convert to the appropriate
+ pointer type (actually unsigned integral). */
+
+ intop = convert (result_type,
+ build_binary_op (MULT_EXPR, intop,
+ convert (TREE_TYPE (intop), size_exp), 1));
+
+ /* Create the sum or difference. */
+
+ result = build (resultcode, result_type, ptrop, intop);
+
+ folded = fold (result);
+ if (folded == result)
+ TREE_CONSTANT (folded) = TREE_CONSTANT (ptrop) & TREE_CONSTANT (intop);
+ return folded;
+}
+
+/* Return a tree for the difference of pointers OP0 and OP1.
+ The resulting tree has type int. */
+
+static tree
+pointer_diff (op0, op1)
+ register tree op0, op1;
+{
+ register tree result, folded;
+ tree restype = ptrdiff_type_node;
+
+ tree target_type = TREE_TYPE (TREE_TYPE (op0));
+
+ if (pedantic || warn_pointer_arith)
+ {
+ if (TREE_CODE (target_type) == VOID_TYPE)
+ pedwarn ("pointer of type `void *' used in subtraction");
+ if (TREE_CODE (target_type) == FUNCTION_TYPE)
+ pedwarn ("pointer to a function used in subtraction");
+ }
+
+ /* First do the subtraction as integers;
+ then drop through to build the divide operator. */
+
+ op0 = build_binary_op (MINUS_EXPR, convert (restype, op0),
+ convert (restype, op1), 1);
+ /* This generates an error if op1 is pointer to incomplete type. */
+ if (TYPE_SIZE (TREE_TYPE (TREE_TYPE (op1))) == 0)
+ error ("arithmetic on pointer to an incomplete type");
+
+ /* This generates an error if op0 is pointer to incomplete type. */
+ op1 = c_size_in_bytes (target_type);
+
+ /* Divide by the size, in easiest possible way. */
+
+ result = build (EXACT_DIV_EXPR, restype, op0, convert (restype, op1));
+
+ folded = fold (result);
+ if (folded == result)
+ TREE_CONSTANT (folded) = TREE_CONSTANT (op0) & TREE_CONSTANT (op1);
+ return folded;
+}
+
+/* Construct and perhaps optimize a tree representation
+ for a unary operation. CODE, a tree_code, specifies the operation
+ and XARG is the operand. NOCONVERT nonzero suppresses
+ the default promotions (such as from short to int). */
+
+tree
+build_unary_op (code, xarg, noconvert)
+ enum tree_code code;
+ tree xarg;
+ int noconvert;
+{
+ /* No default_conversion here. It causes trouble for ADDR_EXPR. */
+ register tree arg = xarg;
+ register tree argtype = 0;
+ register enum tree_code typecode = TREE_CODE (TREE_TYPE (arg));
+ char *errstring = NULL;
+ tree val;
+
+ if (typecode == ERROR_MARK)
+ return error_mark_node;
+ if (typecode == ENUMERAL_TYPE)
+ typecode = INTEGER_TYPE;
+
+ switch (code)
+ {
+ case CONVERT_EXPR:
+ /* This is used for unary plus, because a CONVERT_EXPR
+ is enough to prevent anybody from looking inside for
+ associativity, but won't generate any code. */
+ if (!(typecode == INTEGER_TYPE || typecode == REAL_TYPE
+ || typecode == COMPLEX_TYPE))
+ errstring = "wrong type argument to unary plus";
+ else if (!noconvert)
+ arg = default_conversion (arg);
+ break;
+
+ case NEGATE_EXPR:
+ if (!(typecode == INTEGER_TYPE || typecode == REAL_TYPE
+ || typecode == COMPLEX_TYPE))
+ errstring = "wrong type argument to unary minus";
+ else if (!noconvert)
+ arg = default_conversion (arg);
+ break;
+
+ case BIT_NOT_EXPR:
+ if (typecode == COMPLEX_TYPE)
+ {
+ code = CONJ_EXPR;
+ if (!noconvert)
+ arg = default_conversion (arg);
+ }
+ else if (typecode != INTEGER_TYPE)
+ errstring = "wrong type argument to bit-complement";
+ else if (!noconvert)
+ arg = default_conversion (arg);
+ break;
+
+ case ABS_EXPR:
+ if (!(typecode == INTEGER_TYPE || typecode == REAL_TYPE
+ || typecode == COMPLEX_TYPE))
+ errstring = "wrong type argument to abs";
+ else if (!noconvert)
+ arg = default_conversion (arg);
+ break;
+
+ case CONJ_EXPR:
+ /* Conjugating a real value is a no-op, but allow it anyway. */
+ if (!(typecode == INTEGER_TYPE || typecode == REAL_TYPE
+ || typecode == COMPLEX_TYPE))
+ errstring = "wrong type argument to conjugation";
+ else if (!noconvert)
+ arg = default_conversion (arg);
+ break;
+
+ case TRUTH_NOT_EXPR:
+ if (typecode != INTEGER_TYPE
+ && typecode != REAL_TYPE && typecode != POINTER_TYPE
+ && typecode != COMPLEX_TYPE
+ /* These will convert to a pointer. */
+ && typecode != ARRAY_TYPE && typecode != FUNCTION_TYPE)
+ {
+ errstring = "wrong type argument to unary exclamation mark";
+ break;
+ }
+ arg = truthvalue_conversion (arg);
+ return invert_truthvalue (arg);
+
+ case NOP_EXPR:
+ break;
+
+ case REALPART_EXPR:
+ if (TREE_CODE (arg) == COMPLEX_CST)
+ return TREE_REALPART (arg);
+ else if (TREE_CODE (TREE_TYPE (arg)) == COMPLEX_TYPE)
+ return fold (build1 (REALPART_EXPR, TREE_TYPE (TREE_TYPE (arg)), arg));
+ else
+ return arg;
+
+ case IMAGPART_EXPR:
+ if (TREE_CODE (arg) == COMPLEX_CST)
+ return TREE_IMAGPART (arg);
+ else if (TREE_CODE (TREE_TYPE (arg)) == COMPLEX_TYPE)
+ return fold (build1 (IMAGPART_EXPR, TREE_TYPE (TREE_TYPE (arg)), arg));
+ else
+ return convert (TREE_TYPE (arg), integer_zero_node);
+
+ case PREINCREMENT_EXPR:
+ case POSTINCREMENT_EXPR:
+ case PREDECREMENT_EXPR:
+ case POSTDECREMENT_EXPR:
+ /* Handle complex lvalues (when permitted)
+ by reduction to simpler cases. */
+
+ val = unary_complex_lvalue (code, arg);
+ if (val != 0)
+ return val;
+
+ /* Increment or decrement the real part of the value,
+ and don't change the imaginary part. */
+ if (typecode == COMPLEX_TYPE)
+ {
+ tree real, imag;
+
+ arg = stabilize_reference (arg);
+ real = build_unary_op (REALPART_EXPR, arg, 1);
+ imag = build_unary_op (IMAGPART_EXPR, arg, 1);
+ return build (COMPLEX_EXPR, TREE_TYPE (arg),
+ build_unary_op (code, real, 1), imag);
+ }
+
+ /* Report invalid types. */
+
+ if (typecode != POINTER_TYPE
+ && typecode != INTEGER_TYPE && typecode != REAL_TYPE)
+ {
+ if (code == PREINCREMENT_EXPR || code == POSTINCREMENT_EXPR)
+ errstring ="wrong type argument to increment";
+ else
+ errstring ="wrong type argument to decrement";
+ break;
+ }
+
+ {
+ register tree inc;
+ tree result_type = TREE_TYPE (arg);
+
+ arg = get_unwidened (arg, 0);
+ argtype = TREE_TYPE (arg);
+
+ /* Compute the increment. */
+
+ if (typecode == POINTER_TYPE)
+ {
+ /* If pointer target is an undefined struct,
+ we just cannot know how to do the arithmetic. */
+ if (TYPE_SIZE (TREE_TYPE (result_type)) == 0)
+ error ("%s of pointer to unknown structure",
+ ((code == PREINCREMENT_EXPR
+ || code == POSTINCREMENT_EXPR)
+ ? "increment" : "decrement"));
+ else if ((pedantic || warn_pointer_arith)
+ && (TREE_CODE (TREE_TYPE (result_type)) == FUNCTION_TYPE
+ || TREE_CODE (TREE_TYPE (result_type)) == VOID_TYPE))
+ pedwarn ("wrong type argument to %s",
+ ((code == PREINCREMENT_EXPR
+ || code == POSTINCREMENT_EXPR)
+ ? "increment" : "decrement"));
+ inc = c_sizeof_nowarn (TREE_TYPE (result_type));
+ }
+ else
+ inc = integer_one_node;
+
+ inc = convert (argtype, inc);
+
+ /* Handle incrementing a cast-expression. */
+
+ while (1)
+ switch (TREE_CODE (arg))
+ {
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ case FLOAT_EXPR:
+ case FIX_TRUNC_EXPR:
+ case FIX_FLOOR_EXPR:
+ case FIX_ROUND_EXPR:
+ case FIX_CEIL_EXPR:
+ pedantic_lvalue_warning (CONVERT_EXPR);
+ /* If the real type has the same machine representation
+ as the type it is cast to, we can make better output
+ by adding directly to the inside of the cast. */
+ if ((TREE_CODE (TREE_TYPE (arg))
+ == TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 0))))
+ && (TYPE_MODE (TREE_TYPE (arg))
+ == TYPE_MODE (TREE_TYPE (TREE_OPERAND (arg, 0)))))
+ arg = TREE_OPERAND (arg, 0);
+ else
+ {
+ tree incremented, modify, value;
+ arg = stabilize_reference (arg);
+ if (code == PREINCREMENT_EXPR || code == PREDECREMENT_EXPR)
+ value = arg;
+ else
+ value = save_expr (arg);
+ incremented = build (((code == PREINCREMENT_EXPR
+ || code == POSTINCREMENT_EXPR)
+ ? PLUS_EXPR : MINUS_EXPR),
+ argtype, value, inc);
+ TREE_SIDE_EFFECTS (incremented) = 1;
+ modify = build_modify_expr (arg, NOP_EXPR, incremented);
+ value = build (COMPOUND_EXPR, TREE_TYPE (arg), modify, value);
+ TREE_USED (value) = 1;
+ return value;
+ }
+ break;
+
+ default:
+ goto give_up;
+ }
+ give_up:
+
+ /* Complain about anything else that is not a true lvalue. */
+ if (!lvalue_or_else (arg, ((code == PREINCREMENT_EXPR
+ || code == POSTINCREMENT_EXPR)
+ ? "increment" : "decrement")))
+ return error_mark_node;
+
+ /* Report a read-only lvalue. */
+ if (TREE_READONLY (arg))
+ readonly_warning (arg,
+ ((code == PREINCREMENT_EXPR
+ || code == POSTINCREMENT_EXPR)
+ ? "increment" : "decrement"));
+
+ val = build (code, TREE_TYPE (arg), arg, inc);
+ TREE_SIDE_EFFECTS (val) = 1;
+ val = convert (result_type, val);
+ if (TREE_CODE (val) != code)
+ TREE_NO_UNUSED_WARNING (val) = 1;
+ return val;
+ }
+
+ case ADDR_EXPR:
+ /* Note that this operation never does default_conversion
+ regardless of NOCONVERT. */
+
+ /* Let &* cancel out to simplify resulting code. */
+ if (TREE_CODE (arg) == INDIRECT_REF)
+ {
+ /* Don't let this be an lvalue. */
+ if (lvalue_p (TREE_OPERAND (arg, 0)))
+ return non_lvalue (TREE_OPERAND (arg, 0));
+ return TREE_OPERAND (arg, 0);
+ }
+
+ /* For &x[y], return x+y */
+ if (TREE_CODE (arg) == ARRAY_REF)
+ {
+ if (mark_addressable (TREE_OPERAND (arg, 0)) == 0)
+ return error_mark_node;
+ return build_binary_op (PLUS_EXPR, TREE_OPERAND (arg, 0),
+ TREE_OPERAND (arg, 1), 1);
+ }
+
+ /* Handle complex lvalues (when permitted)
+ by reduction to simpler cases. */
+ val = unary_complex_lvalue (code, arg);
+ if (val != 0)
+ return val;
+
+#if 0 /* Turned off because inconsistent;
+ float f; *&(int)f = 3.4 stores in int format
+ whereas (int)f = 3.4 stores in float format. */
+ /* Address of a cast is just a cast of the address
+ of the operand of the cast. */
+ switch (TREE_CODE (arg))
+ {
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ case FLOAT_EXPR:
+ case FIX_TRUNC_EXPR:
+ case FIX_FLOOR_EXPR:
+ case FIX_ROUND_EXPR:
+ case FIX_CEIL_EXPR:
+ if (pedantic)
+ pedwarn ("ANSI C forbids the address of a cast expression");
+ return convert (build_pointer_type (TREE_TYPE (arg)),
+ build_unary_op (ADDR_EXPR, TREE_OPERAND (arg, 0),
+ 0));
+ }
+#endif
+
+ /* Allow the address of a constructor if all the elements
+ are constant. */
+ if (TREE_CODE (arg) == CONSTRUCTOR && TREE_CONSTANT (arg))
+ ;
+ /* Anything not already handled and not a true memory reference
+ is an error. */
+ else if (typecode != FUNCTION_TYPE && !lvalue_or_else (arg, "unary `&'"))
+ return error_mark_node;
+
+ /* Ordinary case; arg is a COMPONENT_REF or a decl. */
+ argtype = TREE_TYPE (arg);
+ /* If the lvalue is const or volatile,
+ merge that into the type that the address will point to. */
+ if (TREE_CODE_CLASS (TREE_CODE (arg)) == 'd'
+ || TREE_CODE_CLASS (TREE_CODE (arg)) == 'r')
+ {
+ if (TREE_READONLY (arg) || TREE_THIS_VOLATILE (arg))
+ argtype = c_build_type_variant (argtype,
+ TREE_READONLY (arg),
+ TREE_THIS_VOLATILE (arg));
+ }
+
+ argtype = build_pointer_type (argtype);
+
+ if (mark_addressable (arg) == 0)
+ return error_mark_node;
+
+ {
+ tree addr;
+
+ if (TREE_CODE (arg) == COMPONENT_REF)
+ {
+ tree field = TREE_OPERAND (arg, 1);
+
+ addr = build_unary_op (ADDR_EXPR, TREE_OPERAND (arg, 0), 0);
+
+ if (DECL_BIT_FIELD (field))
+ {
+ error ("attempt to take address of bit-field structure member `%s'",
+ IDENTIFIER_POINTER (DECL_NAME (field)));
+ return error_mark_node;
+ }
+
+ addr = convert (argtype, addr);
+
+ if (! integer_zerop (DECL_FIELD_BITPOS (field)))
+ {
+ tree offset
+ = size_binop (EASY_DIV_EXPR, DECL_FIELD_BITPOS (field),
+ size_int (BITS_PER_UNIT));
+ int flag = TREE_CONSTANT (addr);
+ addr = fold (build (PLUS_EXPR, argtype,
+ addr, convert (argtype, offset)));
+ TREE_CONSTANT (addr) = flag;
+ }
+ }
+ else
+ addr = build1 (code, argtype, arg);
+
+ /* Address of a static or external variable or
+ file-scope function counts as a constant. */
+ if (staticp (arg)
+ && ! (TREE_CODE (arg) == FUNCTION_DECL
+ && DECL_CONTEXT (arg) != 0))
+ TREE_CONSTANT (addr) = 1;
+ return addr;
+ }
+ }
+
+ if (!errstring)
+ {
+ if (argtype == 0)
+ argtype = TREE_TYPE (arg);
+ return fold (build1 (code, argtype, arg));
+ }
+
+ error (errstring);
+ return error_mark_node;
+}
+
+#if 0
+/* If CONVERSIONS is a conversion expression or a nested sequence of such,
+ convert ARG with the same conversions in the same order
+ and return the result. */
+
+static tree
+convert_sequence (conversions, arg)
+ tree conversions;
+ tree arg;
+{
+ switch (TREE_CODE (conversions))
+ {
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ case FLOAT_EXPR:
+ case FIX_TRUNC_EXPR:
+ case FIX_FLOOR_EXPR:
+ case FIX_ROUND_EXPR:
+ case FIX_CEIL_EXPR:
+ return convert (TREE_TYPE (conversions),
+ convert_sequence (TREE_OPERAND (conversions, 0),
+ arg));
+
+ default:
+ return arg;
+ }
+}
+#endif /* 0 */
+
+/* Return nonzero if REF is an lvalue valid for this language.
+ Lvalues can be assigned, unless their type has TYPE_READONLY.
+ Lvalues can have their address taken, unless they have DECL_REGISTER. */
+
+int
+lvalue_p (ref)
+ tree ref;
+{
+ register enum tree_code code = TREE_CODE (ref);
+
+ switch (code)
+ {
+ case REALPART_EXPR:
+ case IMAGPART_EXPR:
+ case COMPONENT_REF:
+ return lvalue_p (TREE_OPERAND (ref, 0));
+
+ case STRING_CST:
+ return 1;
+
+ case INDIRECT_REF:
+ case ARRAY_REF:
+ case VAR_DECL:
+ case PARM_DECL:
+ case RESULT_DECL:
+ case ERROR_MARK:
+ if (TREE_CODE (TREE_TYPE (ref)) != FUNCTION_TYPE
+ && TREE_CODE (TREE_TYPE (ref)) != METHOD_TYPE)
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+/* Return nonzero if REF is an lvalue valid for this language;
+ otherwise, print an error message and return zero. */
+
+int
+lvalue_or_else (ref, string)
+ tree ref;
+ char *string;
+{
+ int win = lvalue_p (ref);
+ if (! win)
+ error ("invalid lvalue in %s", string);
+ return win;
+}
+
+/* Apply unary lvalue-demanding operator CODE to the expression ARG
+ for certain kinds of expressions which are not really lvalues
+ but which we can accept as lvalues.
+
+ If ARG is not a kind of expression we can handle, return zero. */
+
+static tree
+unary_complex_lvalue (code, arg)
+ enum tree_code code;
+ tree arg;
+{
+ /* Handle (a, b) used as an "lvalue". */
+ if (TREE_CODE (arg) == COMPOUND_EXPR)
+ {
+ tree real_result = build_unary_op (code, TREE_OPERAND (arg, 1), 0);
+ pedantic_lvalue_warning (COMPOUND_EXPR);
+ return build (COMPOUND_EXPR, TREE_TYPE (real_result),
+ TREE_OPERAND (arg, 0), real_result);
+ }
+
+ /* Handle (a ? b : c) used as an "lvalue". */
+ if (TREE_CODE (arg) == COND_EXPR)
+ {
+ pedantic_lvalue_warning (COND_EXPR);
+ return (build_conditional_expr
+ (TREE_OPERAND (arg, 0),
+ build_unary_op (code, TREE_OPERAND (arg, 1), 0),
+ build_unary_op (code, TREE_OPERAND (arg, 2), 0)));
+ }
+
+ return 0;
+}
+
+/* If pedantic, warn about improper lvalue. CODE is either COND_EXPR
+ COMPOUND_EXPR, or CONVERT_EXPR (for casts). */
+
+static void
+pedantic_lvalue_warning (code)
+ enum tree_code code;
+{
+ if (pedantic)
+ pedwarn ("ANSI C forbids use of %s expressions as lvalues",
+ code == COND_EXPR ? "conditional"
+ : code == COMPOUND_EXPR ? "compound" : "cast");
+}
+
+/* Warn about storing in something that is `const'. */
+
+void
+readonly_warning (arg, string)
+ tree arg;
+ char *string;
+{
+ char buf[80];
+ strcpy (buf, string);
+
+ /* Forbid assignments to iterators. */
+ if (TREE_CODE (arg) == VAR_DECL && ITERATOR_P (arg))
+ {
+ strcat (buf, " of iterator `%s'");
+ pedwarn (buf, IDENTIFIER_POINTER (DECL_NAME (arg)));
+ }
+
+ if (TREE_CODE (arg) == COMPONENT_REF)
+ {
+ if (TYPE_READONLY (TREE_TYPE (TREE_OPERAND (arg, 0))))
+ readonly_warning (TREE_OPERAND (arg, 0), string);
+ else
+ {
+ strcat (buf, " of read-only member `%s'");
+ pedwarn (buf, IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND (arg, 1))));
+ }
+ }
+ else if (TREE_CODE (arg) == VAR_DECL)
+ {
+ strcat (buf, " of read-only variable `%s'");
+ pedwarn (buf, IDENTIFIER_POINTER (DECL_NAME (arg)));
+ }
+ else
+ {
+ pedwarn ("%s of read-only location", buf);
+ }
+}
+
+/* Mark EXP saying that we need to be able to take the
+ address of it; it should not be allocated in a register.
+ Value is 1 if successful. */
+
+int
+mark_addressable (exp)
+ tree exp;
+{
+ register tree x = exp;
+ while (1)
+ switch (TREE_CODE (x))
+ {
+ case ADDR_EXPR:
+ case COMPONENT_REF:
+ case ARRAY_REF:
+ case REALPART_EXPR:
+ case IMAGPART_EXPR:
+ x = TREE_OPERAND (x, 0);
+ break;
+
+ case CONSTRUCTOR:
+ TREE_ADDRESSABLE (x) = 1;
+ return 1;
+
+ case VAR_DECL:
+ case CONST_DECL:
+ case PARM_DECL:
+ case RESULT_DECL:
+ if (DECL_REGISTER (x) && !TREE_ADDRESSABLE (x)
+ && DECL_NONLOCAL (x))
+ {
+ if (TREE_PUBLIC (x))
+ {
+ error ("global register variable `%s' used in nested function",
+ IDENTIFIER_POINTER (DECL_NAME (x)));
+ return 0;
+ }
+ pedwarn ("register variable `%s' used in nested function",
+ IDENTIFIER_POINTER (DECL_NAME (x)));
+ }
+ else if (DECL_REGISTER (x) && !TREE_ADDRESSABLE (x))
+ {
+ if (TREE_PUBLIC (x))
+ {
+ error ("address of global register variable `%s' requested",
+ IDENTIFIER_POINTER (DECL_NAME (x)));
+ return 0;
+ }
+ pedwarn ("address of register variable `%s' requested",
+ IDENTIFIER_POINTER (DECL_NAME (x)));
+ }
+ put_var_into_stack (x);
+
+ /* drops in */
+ case FUNCTION_DECL:
+ TREE_ADDRESSABLE (x) = 1;
+#if 0 /* poplevel deals with this now. */
+ if (DECL_CONTEXT (x) == 0)
+ TREE_ADDRESSABLE (DECL_ASSEMBLER_NAME (x)) = 1;
+#endif
+
+ default:
+ return 1;
+ }
+}
+
+/* Build and return a conditional expression IFEXP ? OP1 : OP2. */
+
+tree
+build_conditional_expr (ifexp, op1, op2)
+ tree ifexp, op1, op2;
+{
+ register tree type1;
+ register tree type2;
+ register enum tree_code code1;
+ register enum tree_code code2;
+ register tree result_type = NULL;
+ tree orig_op1 = op1, orig_op2 = op2;
+
+ /* If second operand is omitted, it is the same as the first one;
+ make sure it is calculated only once. */
+ if (op1 == 0)
+ {
+ if (pedantic)
+ pedwarn ("ANSI C forbids omitting the middle term of a ?: expression");
+ ifexp = op1 = save_expr (ifexp);
+ }
+
+ ifexp = truthvalue_conversion (default_conversion (ifexp));
+
+#if 0 /* Produces wrong result if within sizeof. */
+ /* Don't promote the operands separately if they promote
+ the same way. Return the unpromoted type and let the combined
+ value get promoted if necessary. */
+
+ if (TREE_TYPE (op1) == TREE_TYPE (op2)
+ && TREE_CODE (TREE_TYPE (op1)) != ARRAY_TYPE
+ && TREE_CODE (TREE_TYPE (op1)) != ENUMERAL_TYPE
+ && TREE_CODE (TREE_TYPE (op1)) != FUNCTION_TYPE)
+ {
+ if (TREE_CODE (ifexp) == INTEGER_CST)
+ return pedantic_non_lvalue (integer_zerop (ifexp) ? op2 : op1);
+
+ return fold (build (COND_EXPR, TREE_TYPE (op1), ifexp, op1, op2));
+ }
+#endif
+
+ /* Promote both alternatives. */
+
+ if (TREE_CODE (TREE_TYPE (op1)) != VOID_TYPE)
+ op1 = default_conversion (op1);
+ if (TREE_CODE (TREE_TYPE (op2)) != VOID_TYPE)
+ op2 = default_conversion (op2);
+
+ if (TREE_CODE (ifexp) == ERROR_MARK
+ || TREE_CODE (TREE_TYPE (op1)) == ERROR_MARK
+ || TREE_CODE (TREE_TYPE (op2)) == ERROR_MARK)
+ return error_mark_node;
+
+ type1 = TREE_TYPE (op1);
+ code1 = TREE_CODE (type1);
+ type2 = TREE_TYPE (op2);
+ code2 = TREE_CODE (type2);
+
+ /* Quickly detect the usual case where op1 and op2 have the same type
+ after promotion. */
+ if (TYPE_MAIN_VARIANT (type1) == TYPE_MAIN_VARIANT (type2))
+ {
+ if (type1 == type2)
+ result_type = type1;
+ else
+ result_type = TYPE_MAIN_VARIANT (type1);
+ }
+ else if ((code1 == INTEGER_TYPE || code1 == REAL_TYPE)
+ && (code2 == INTEGER_TYPE || code2 == REAL_TYPE))
+ {
+ result_type = common_type (type1, type2);
+ }
+ else if (code1 == VOID_TYPE || code2 == VOID_TYPE)
+ {
+ if (pedantic && (code1 != VOID_TYPE || code2 != VOID_TYPE))
+ pedwarn ("ANSI C forbids conditional expr with only one void side");
+ result_type = void_type_node;
+ }
+ else if (code1 == POINTER_TYPE && code2 == POINTER_TYPE)
+ {
+ if (comp_target_types (type1, type2))
+ result_type = common_type (type1, type2);
+ else if (integer_zerop (op1) && TREE_TYPE (type1) == void_type_node
+ && TREE_CODE (orig_op1) != NOP_EXPR)
+ result_type = qualify_type (type2, type1);
+ else if (integer_zerop (op2) && TREE_TYPE (type2) == void_type_node
+ && TREE_CODE (orig_op2) != NOP_EXPR)
+ result_type = qualify_type (type1, type2);
+ else if (TYPE_MAIN_VARIANT (TREE_TYPE (type1)) == void_type_node)
+ {
+ if (pedantic && TREE_CODE (TREE_TYPE (type2)) == FUNCTION_TYPE)
+ pedwarn ("ANSI C forbids conditional expr between `void *' and function pointer");
+ result_type = qualify_type (type1, type2);
+ }
+ else if (TYPE_MAIN_VARIANT (TREE_TYPE (type2)) == void_type_node)
+ {
+ if (pedantic && TREE_CODE (TREE_TYPE (type1)) == FUNCTION_TYPE)
+ pedwarn ("ANSI C forbids conditional expr between `void *' and function pointer");
+ result_type = qualify_type (type2, type1);
+ }
+ else
+ {
+ pedwarn ("pointer type mismatch in conditional expression");
+ result_type = build_pointer_type (void_type_node);
+ }
+ }
+ else if (code1 == POINTER_TYPE && code2 == INTEGER_TYPE)
+ {
+ if (! integer_zerop (op2))
+ pedwarn ("pointer/integer type mismatch in conditional expression");
+ else
+ {
+ op2 = null_pointer_node;
+#if 0 /* The spec seems to say this is permitted. */
+ if (pedantic && TREE_CODE (type1) == FUNCTION_TYPE)
+ pedwarn ("ANSI C forbids conditional expr between 0 and function pointer");
+#endif
+ }
+ result_type = type1;
+ }
+ else if (code2 == POINTER_TYPE && code1 == INTEGER_TYPE)
+ {
+ if (!integer_zerop (op1))
+ pedwarn ("pointer/integer type mismatch in conditional expression");
+ else
+ {
+ op1 = null_pointer_node;
+#if 0 /* The spec seems to say this is permitted. */
+ if (pedantic && TREE_CODE (type2) == FUNCTION_TYPE)
+ pedwarn ("ANSI C forbids conditional expr between 0 and function pointer");
+#endif
+ }
+ result_type = type2;
+ }
+
+ if (!result_type)
+ {
+ if (flag_cond_mismatch)
+ result_type = void_type_node;
+ else
+ {
+ error ("type mismatch in conditional expression");
+ return error_mark_node;
+ }
+ }
+
+ /* Merge const and volatile flags of the incoming types. */
+ result_type
+ = build_type_variant (result_type,
+ TREE_READONLY (op1) || TREE_READONLY (op2),
+ TREE_THIS_VOLATILE (op1) || TREE_THIS_VOLATILE (op2));
+
+ if (result_type != TREE_TYPE (op1))
+ op1 = convert_and_check (result_type, op1);
+ if (result_type != TREE_TYPE (op2))
+ op2 = convert_and_check (result_type, op2);
+
+#if 0
+ if (code1 == RECORD_TYPE || code1 == UNION_TYPE)
+ {
+ result_type = TREE_TYPE (op1);
+ if (TREE_CONSTANT (ifexp))
+ return pedantic_non_lvalue (integer_zerop (ifexp) ? op2 : op1);
+
+ if (TYPE_MODE (result_type) == BLKmode)
+ {
+ register tree tempvar
+ = build_decl (VAR_DECL, NULL_TREE, result_type);
+ register tree xop1 = build_modify_expr (tempvar, op1);
+ register tree xop2 = build_modify_expr (tempvar, op2);
+ register tree result = fold (build (COND_EXPR, result_type,
+ ifexp, xop1, xop2));
+
+ layout_decl (tempvar, TYPE_ALIGN (result_type));
+ /* No way to handle variable-sized objects here.
+ I fear that the entire handling of BLKmode conditional exprs
+ needs to be redone. */
+ if (TREE_CODE (DECL_SIZE (tempvar)) != INTEGER_CST)
+ abort ();
+ DECL_RTL (tempvar)
+ = assign_stack_local (DECL_MODE (tempvar),
+ (TREE_INT_CST_LOW (DECL_SIZE (tempvar))
+ + BITS_PER_UNIT - 1)
+ / BITS_PER_UNIT,
+ 0);
+
+ TREE_SIDE_EFFECTS (result)
+ = TREE_SIDE_EFFECTS (ifexp) | TREE_SIDE_EFFECTS (op1)
+ | TREE_SIDE_EFFECTS (op2);
+ return build (COMPOUND_EXPR, result_type, result, tempvar);
+ }
+ }
+#endif /* 0 */
+
+ if (TREE_CODE (ifexp) == INTEGER_CST)
+ return pedantic_non_lvalue (integer_zerop (ifexp) ? op2 : op1);
+
+ return fold (build (COND_EXPR, result_type, ifexp, op1, op2));
+}
+
+/* Given a list of expressions, return a compound expression
+ that performs them all and returns the value of the last of them. */
+
+tree
+build_compound_expr (list)
+ tree list;
+{
+ return internal_build_compound_expr (list, TRUE);
+}
+
+static tree
+internal_build_compound_expr (list, first_p)
+ tree list;
+ int first_p;
+{
+ register tree rest;
+
+ if (TREE_CHAIN (list) == 0)
+ {
+#if 0 /* If something inside inhibited lvalueness, we should not override. */
+ /* Consider (x, y+0), which is not an lvalue since y+0 is not. */
+
+ /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
+ if (TREE_CODE (list) == NON_LVALUE_EXPR)
+ list = TREE_OPERAND (list, 0);
+#endif
+
+ /* Don't let (0, 0) be null pointer constant. */
+ if (!first_p && integer_zerop (TREE_VALUE (list)))
+ return non_lvalue (TREE_VALUE (list));
+ return TREE_VALUE (list);
+ }
+
+ if (TREE_CHAIN (list) != 0 && TREE_CHAIN (TREE_CHAIN (list)) == 0)
+ {
+ /* Convert arrays to pointers when there really is a comma operator. */
+ if (TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (list)))) == ARRAY_TYPE)
+ TREE_VALUE (TREE_CHAIN (list))
+ = default_conversion (TREE_VALUE (TREE_CHAIN (list)));
+ }
+
+ rest = internal_build_compound_expr (TREE_CHAIN (list), FALSE);
+
+ /* When pedantic, a compound expression can be neither an lvalue
+ nor an integer constant expression. */
+ if (! TREE_SIDE_EFFECTS (TREE_VALUE (list)) && ! pedantic)
+ return rest;
+
+ return build (COMPOUND_EXPR, TREE_TYPE (rest), TREE_VALUE (list), rest);
+}
+
+/* Build an expression representing a cast to type TYPE of expression EXPR. */
+
+tree
+build_c_cast (type, expr)
+ register tree type;
+ tree expr;
+{
+ register tree value = expr;
+
+ if (type == error_mark_node || expr == error_mark_node)
+ return error_mark_node;
+ type = TYPE_MAIN_VARIANT (type);
+
+#if 0
+ /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
+ if (TREE_CODE (value) == NON_LVALUE_EXPR)
+ value = TREE_OPERAND (value, 0);
+#endif
+
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ error ("cast specifies array type");
+ return error_mark_node;
+ }
+
+ if (TREE_CODE (type) == FUNCTION_TYPE)
+ {
+ error ("cast specifies function type");
+ return error_mark_node;
+ }
+
+ if (type == TREE_TYPE (value))
+ {
+ if (pedantic)
+ {
+ if (TREE_CODE (type) == RECORD_TYPE
+ || TREE_CODE (type) == UNION_TYPE)
+ pedwarn ("ANSI C forbids casting nonscalar to the same type");
+ }
+ }
+ else if (TREE_CODE (type) == UNION_TYPE)
+ {
+ tree field;
+ if (TREE_CODE (TREE_TYPE (value)) == ARRAY_TYPE
+ || TREE_CODE (TREE_TYPE (value)) == FUNCTION_TYPE)
+ value = default_conversion (value);
+
+ for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+ if (comptypes (TYPE_MAIN_VARIANT (TREE_TYPE (field)),
+ TYPE_MAIN_VARIANT (TREE_TYPE (value))))
+ break;
+
+ if (field)
+ {
+ char *name;
+ tree t;
+
+ if (pedantic)
+ pedwarn ("ANSI C forbids casts to union type");
+ if (TYPE_NAME (type) != 0)
+ {
+ if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
+ name = IDENTIFIER_POINTER (TYPE_NAME (type));
+ else
+ name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)));
+ }
+ else
+ name = "";
+ t = digest_init (type, build (CONSTRUCTOR, type, NULL_TREE,
+ build_tree_list (field, value)),
+ 0, 0);
+ TREE_CONSTANT (t) = TREE_CONSTANT (value);
+ return t;
+ }
+ error ("cast to union type from type not present in union");
+ return error_mark_node;
+ }
+ else
+ {
+ tree otype, ovalue;
+
+ /* If casting to void, avoid the error that would come
+ from default_conversion in the case of a non-lvalue array. */
+ if (type == void_type_node)
+ return build1 (CONVERT_EXPR, type, value);
+
+ /* Convert functions and arrays to pointers,
+ but don't convert any other types. */
+ if (TREE_CODE (TREE_TYPE (value)) == FUNCTION_TYPE
+ || TREE_CODE (TREE_TYPE (value)) == ARRAY_TYPE)
+ value = default_conversion (value);
+ otype = TREE_TYPE (value);
+
+ /* Optionally warn about potentially worrisome casts. */
+
+ if (warn_cast_qual
+ && TREE_CODE (type) == POINTER_TYPE
+ && TREE_CODE (otype) == POINTER_TYPE)
+ {
+ if (TYPE_VOLATILE (TREE_TYPE (otype))
+ && ! TYPE_VOLATILE (TREE_TYPE (type)))
+ pedwarn ("cast discards `volatile' from pointer target type");
+ if (TYPE_READONLY (TREE_TYPE (otype))
+ && ! TYPE_READONLY (TREE_TYPE (type)))
+ pedwarn ("cast discards `const' from pointer target type");
+ }
+
+ /* Warn about possible alignment problems. */
+ if (STRICT_ALIGNMENT && warn_cast_align
+ && TREE_CODE (type) == POINTER_TYPE
+ && TREE_CODE (otype) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (otype)) != VOID_TYPE
+ && TREE_CODE (TREE_TYPE (otype)) != FUNCTION_TYPE
+ && TYPE_ALIGN (TREE_TYPE (type)) > TYPE_ALIGN (TREE_TYPE (otype)))
+ warning ("cast increases required alignment of target type");
+
+ if (TREE_CODE (type) == INTEGER_TYPE
+ && TREE_CODE (otype) == POINTER_TYPE
+ && TYPE_PRECISION (type) != TYPE_PRECISION (otype)
+ && !TREE_CONSTANT (value))
+ warning ("cast from pointer to integer of different size");
+
+ if (warn_bad_function_cast
+ && TREE_CODE (value) == CALL_EXPR
+ && TREE_CODE (type) != TREE_CODE (otype))
+ warning ("cast does not match function type");
+
+ if (TREE_CODE (type) == POINTER_TYPE
+ && TREE_CODE (otype) == INTEGER_TYPE
+ && TYPE_PRECISION (type) != TYPE_PRECISION (otype)
+#if 0
+ /* Don't warn about converting 0 to pointer,
+ provided the 0 was explicit--not cast or made by folding. */
+ && !(TREE_CODE (value) == INTEGER_CST && integer_zerop (value))
+#endif
+ /* Don't warn about converting any constant. */
+ && !TREE_CONSTANT (value))
+ warning ("cast to pointer from integer of different size");
+
+ ovalue = value;
+ value = convert (type, value);
+
+ /* Ignore any integer overflow caused by the cast. */
+ if (TREE_CODE (value) == INTEGER_CST)
+ {
+ TREE_OVERFLOW (value) = TREE_OVERFLOW (ovalue);
+ TREE_CONSTANT_OVERFLOW (value) = TREE_CONSTANT_OVERFLOW (ovalue);
+ }
+ }
+
+ /* Pedantically, don't ley (void *) (FOO *) 0 be a null pointer constant. */
+ if (pedantic && TREE_CODE (value) == INTEGER_CST
+ && TREE_CODE (expr) == INTEGER_CST
+ && TREE_CODE (TREE_TYPE (expr)) != INTEGER_TYPE)
+ value = non_lvalue (value);
+
+ /* If pedantic, don't let a cast be an lvalue. */
+ if (value == expr && pedantic)
+ value = non_lvalue (value);
+
+ return value;
+}
+
+/* Build an assignment expression of lvalue LHS from value RHS.
+ MODIFYCODE is the code for a binary operator that we use
+ to combine the old value of LHS with RHS to get the new value.
+ Or else MODIFYCODE is NOP_EXPR meaning do a simple assignment. */
+
+tree
+build_modify_expr (lhs, modifycode, rhs)
+ tree lhs, rhs;
+ enum tree_code modifycode;
+{
+ register tree result;
+ tree newrhs;
+ tree lhstype = TREE_TYPE (lhs);
+ tree olhstype = lhstype;
+
+ /* Types that aren't fully specified cannot be used in assignments. */
+ lhs = require_complete_type (lhs);
+
+ /* Avoid duplicate error messages from operands that had errors. */
+ if (TREE_CODE (lhs) == ERROR_MARK || TREE_CODE (rhs) == ERROR_MARK)
+ return error_mark_node;
+
+ /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
+ /* Do not use STRIP_NOPS here. We do not want an enumerator
+ whose value is 0 to count as a null pointer constant. */
+ if (TREE_CODE (rhs) == NON_LVALUE_EXPR)
+ rhs = TREE_OPERAND (rhs, 0);
+
+ newrhs = rhs;
+
+ /* Handle control structure constructs used as "lvalues". */
+
+ switch (TREE_CODE (lhs))
+ {
+ /* Handle (a, b) used as an "lvalue". */
+ case COMPOUND_EXPR:
+ pedantic_lvalue_warning (COMPOUND_EXPR);
+ newrhs = build_modify_expr (TREE_OPERAND (lhs, 1),
+ modifycode, rhs);
+ if (TREE_CODE (newrhs) == ERROR_MARK)
+ return error_mark_node;
+ return build (COMPOUND_EXPR, lhstype,
+ TREE_OPERAND (lhs, 0), newrhs);
+
+ /* Handle (a ? b : c) used as an "lvalue". */
+ case COND_EXPR:
+ pedantic_lvalue_warning (COND_EXPR);
+ rhs = save_expr (rhs);
+ {
+ /* Produce (a ? (b = rhs) : (c = rhs))
+ except that the RHS goes through a save-expr
+ so the code to compute it is only emitted once. */
+ tree cond
+ = build_conditional_expr (TREE_OPERAND (lhs, 0),
+ build_modify_expr (TREE_OPERAND (lhs, 1),
+ modifycode, rhs),
+ build_modify_expr (TREE_OPERAND (lhs, 2),
+ modifycode, rhs));
+ if (TREE_CODE (cond) == ERROR_MARK)
+ return cond;
+ /* Make sure the code to compute the rhs comes out
+ before the split. */
+ return build (COMPOUND_EXPR, TREE_TYPE (lhs),
+ /* But cast it to void to avoid an "unused" error. */
+ convert (void_type_node, rhs), cond);
+ }
+ }
+
+ /* If a binary op has been requested, combine the old LHS value with the RHS
+ producing the value we should actually store into the LHS. */
+
+ if (modifycode != NOP_EXPR)
+ {
+ lhs = stabilize_reference (lhs);
+ newrhs = build_binary_op (modifycode, lhs, rhs, 1);
+ }
+
+ /* Handle a cast used as an "lvalue".
+ We have already performed any binary operator using the value as cast.
+ Now convert the result to the cast type of the lhs,
+ and then true type of the lhs and store it there;
+ then convert result back to the cast type to be the value
+ of the assignment. */
+
+ switch (TREE_CODE (lhs))
+ {
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ case FLOAT_EXPR:
+ case FIX_TRUNC_EXPR:
+ case FIX_FLOOR_EXPR:
+ case FIX_ROUND_EXPR:
+ case FIX_CEIL_EXPR:
+ if (TREE_CODE (TREE_TYPE (newrhs)) == ARRAY_TYPE
+ || TREE_CODE (TREE_TYPE (newrhs)) == FUNCTION_TYPE)
+ newrhs = default_conversion (newrhs);
+ {
+ tree inner_lhs = TREE_OPERAND (lhs, 0);
+ tree result;
+ result = build_modify_expr (inner_lhs, NOP_EXPR,
+ convert (TREE_TYPE (inner_lhs),
+ convert (lhstype, newrhs)));
+ if (TREE_CODE (result) == ERROR_MARK)
+ return result;
+ pedantic_lvalue_warning (CONVERT_EXPR);
+ return convert (TREE_TYPE (lhs), result);
+ }
+ }
+
+ /* Now we have handled acceptable kinds of LHS that are not truly lvalues.
+ Reject anything strange now. */
+
+ if (!lvalue_or_else (lhs, "assignment"))
+ return error_mark_node;
+
+ /* Warn about storing in something that is `const'. */
+
+ if (TREE_READONLY (lhs) || TYPE_READONLY (lhstype)
+ || ((TREE_CODE (lhstype) == RECORD_TYPE
+ || TREE_CODE (lhstype) == UNION_TYPE)
+ && C_TYPE_FIELDS_READONLY (lhstype)))
+ readonly_warning (lhs, "assignment");
+
+ /* If storing into a structure or union member,
+ it has probably been given type `int'.
+ Compute the type that would go with
+ the actual amount of storage the member occupies. */
+
+ if (TREE_CODE (lhs) == COMPONENT_REF
+ && (TREE_CODE (lhstype) == INTEGER_TYPE
+ || TREE_CODE (lhstype) == REAL_TYPE
+ || TREE_CODE (lhstype) == ENUMERAL_TYPE))
+ lhstype = TREE_TYPE (get_unwidened (lhs, 0));
+
+ /* If storing in a field that is in actuality a short or narrower than one,
+ we must store in the field in its actual type. */
+
+ if (lhstype != TREE_TYPE (lhs))
+ {
+ lhs = copy_node (lhs);
+ TREE_TYPE (lhs) = lhstype;
+ }
+
+ /* Convert new value to destination type. */
+
+ newrhs = convert_for_assignment (lhstype, newrhs, "assignment",
+ NULL_TREE, NULL_TREE, 0);
+ if (TREE_CODE (newrhs) == ERROR_MARK)
+ return error_mark_node;
+
+ result = build (MODIFY_EXPR, lhstype, lhs, newrhs);
+ TREE_SIDE_EFFECTS (result) = 1;
+
+ /* If we got the LHS in a different type for storing in,
+ convert the result back to the nominal type of LHS
+ so that the value we return always has the same type
+ as the LHS argument. */
+
+ if (olhstype == TREE_TYPE (result))
+ return result;
+ return convert_for_assignment (olhstype, result, "assignment",
+ NULL_TREE, NULL_TREE, 0);
+}
+
+/* Convert value RHS to type TYPE as preparation for an assignment
+ to an lvalue of type TYPE.
+ The real work of conversion is done by `convert'.
+ The purpose of this function is to generate error messages
+ for assignments that are not allowed in C.
+ ERRTYPE is a string to use in error messages:
+ "assignment", "return", etc. If it is null, this is parameter passing
+ for a function call (and different error messages are output). Otherwise,
+ it may be a name stored in the spelling stack and interpreted by
+ get_spelling.
+
+ FUNNAME is the name of the function being called,
+ as an IDENTIFIER_NODE, or null.
+ PARMNUM is the number of the argument, for printing in error messages. */
+
+static tree
+convert_for_assignment (type, rhs, errtype, fundecl, funname, parmnum)
+ tree type, rhs;
+ char *errtype;
+ tree fundecl, funname;
+ int parmnum;
+{
+ register enum tree_code codel = TREE_CODE (type);
+ register tree rhstype;
+ register enum tree_code coder;
+
+ /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
+ /* Do not use STRIP_NOPS here. We do not want an enumerator
+ whose value is 0 to count as a null pointer constant. */
+ if (TREE_CODE (rhs) == NON_LVALUE_EXPR)
+ rhs = TREE_OPERAND (rhs, 0);
+
+ if (TREE_CODE (TREE_TYPE (rhs)) == ARRAY_TYPE
+ || TREE_CODE (TREE_TYPE (rhs)) == FUNCTION_TYPE)
+ rhs = default_conversion (rhs);
+ else if (optimize && TREE_CODE (rhs) == VAR_DECL)
+ rhs = decl_constant_value (rhs);
+
+ rhstype = TREE_TYPE (rhs);
+ coder = TREE_CODE (rhstype);
+
+ if (coder == ERROR_MARK)
+ return error_mark_node;
+
+ if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (rhstype))
+ {
+ overflow_warning (rhs);
+ /* Check for Objective-C protocols. This will issue a warning if
+ there are protocol violations. No need to use the return value. */
+ maybe_objc_comptypes (type, rhstype, 0);
+ return rhs;
+ }
+
+ if (coder == VOID_TYPE)
+ {
+ error ("void value not ignored as it ought to be");
+ return error_mark_node;
+ }
+ /* Arithmetic types all interconvert, and enum is treated like int. */
+ if ((codel == INTEGER_TYPE || codel == REAL_TYPE || codel == ENUMERAL_TYPE
+ || codel == COMPLEX_TYPE)
+ &&
+ (coder == INTEGER_TYPE || coder == REAL_TYPE || coder == ENUMERAL_TYPE
+ || coder == COMPLEX_TYPE))
+ return convert_and_check (type, rhs);
+ /* Conversion to a union from its member types. */
+ else if (codel == UNION_TYPE)
+ {
+ tree memb_types;
+ for (memb_types = TYPE_FIELDS (type); memb_types;
+ memb_types = TREE_CHAIN (memb_types))
+ {
+ if (comptypes (TREE_TYPE (memb_types), TREE_TYPE (rhs)))
+ {
+ if (pedantic
+ && !(fundecl != 0 && DECL_IN_SYSTEM_HEADER (fundecl)))
+ pedwarn ("ANSI C prohibits argument conversion to union type");
+ return build1 (NOP_EXPR, type, rhs);
+ }
+ else if (coder == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (memb_types)) == POINTER_TYPE)
+ {
+ tree memb_type = TREE_TYPE (memb_types);
+ register tree ttl = TREE_TYPE (memb_type);
+ register tree ttr = TREE_TYPE (rhstype);
+
+ /* Any non-function converts to a [const][volatile] void *
+ and vice versa; otherwise, targets must be the same.
+ Meanwhile, the lhs target must have all the qualifiers of the rhs. */
+ if (TYPE_MAIN_VARIANT (ttl) == void_type_node
+ || TYPE_MAIN_VARIANT (ttr) == void_type_node
+ || comp_target_types (memb_type, rhstype))
+ {
+ /* Const and volatile mean something different for function types,
+ so the usual warnings are not appropriate. */
+ if (TREE_CODE (ttr) != FUNCTION_TYPE
+ || TREE_CODE (ttl) != FUNCTION_TYPE)
+ {
+ if (! TYPE_READONLY (ttl) && TYPE_READONLY (ttr))
+ warn_for_assignment ("%s discards `const' from pointer target type",
+ get_spelling (errtype), funname, parmnum);
+ if (! TYPE_VOLATILE (ttl) && TYPE_VOLATILE (ttr))
+ warn_for_assignment ("%s discards `volatile' from pointer target type",
+ get_spelling (errtype), funname, parmnum);
+ }
+ else
+ {
+ /* Because const and volatile on functions are restrictions
+ that say the function will not do certain things,
+ it is okay to use a const or volatile function
+ where an ordinary one is wanted, but not vice-versa. */
+ if (TYPE_READONLY (ttl) && ! TYPE_READONLY (ttr))
+ warn_for_assignment ("%s makes `const *' function pointer from non-const",
+ get_spelling (errtype), funname, parmnum);
+ if (TYPE_VOLATILE (ttl) && ! TYPE_VOLATILE (ttr))
+ warn_for_assignment ("%s makes `volatile *' function pointer from non-volatile",
+ get_spelling (errtype), funname, parmnum);
+ }
+ if (pedantic
+ && !(fundecl != 0 && DECL_IN_SYSTEM_HEADER (fundecl)))
+ pedwarn ("ANSI C prohibits argument conversion to union type");
+ return build1 (NOP_EXPR, type, rhs);
+ }
+ }
+ }
+ }
+ /* Conversions among pointers */
+ else if (codel == POINTER_TYPE && coder == POINTER_TYPE)
+ {
+ register tree ttl = TREE_TYPE (type);
+ register tree ttr = TREE_TYPE (rhstype);
+
+ /* Any non-function converts to a [const][volatile] void *
+ and vice versa; otherwise, targets must be the same.
+ Meanwhile, the lhs target must have all the qualifiers of the rhs. */
+ if (TYPE_MAIN_VARIANT (ttl) == void_type_node
+ || TYPE_MAIN_VARIANT (ttr) == void_type_node
+ || comp_target_types (type, rhstype)
+ || (unsigned_type (TYPE_MAIN_VARIANT (ttl))
+ == unsigned_type (TYPE_MAIN_VARIANT (ttr))))
+ {
+ if (pedantic
+ && ((TYPE_MAIN_VARIANT (ttl) == void_type_node
+ && TREE_CODE (ttr) == FUNCTION_TYPE)
+ ||
+ (TYPE_MAIN_VARIANT (ttr) == void_type_node
+ /* Check TREE_CODE to catch cases like (void *) (char *) 0
+ which are not ANSI null ptr constants. */
+ && (!integer_zerop (rhs) || TREE_CODE (rhs) == NOP_EXPR)
+ && TREE_CODE (ttl) == FUNCTION_TYPE)))
+ warn_for_assignment ("ANSI forbids %s between function pointer and `void *'",
+ get_spelling (errtype), funname, parmnum);
+ /* Const and volatile mean something different for function types,
+ so the usual warnings are not appropriate. */
+ else if (TREE_CODE (ttr) != FUNCTION_TYPE
+ || TREE_CODE (ttl) != FUNCTION_TYPE)
+ {
+ if (! TYPE_READONLY (ttl) && TYPE_READONLY (ttr))
+ warn_for_assignment ("%s discards `const' from pointer target type",
+ get_spelling (errtype), funname, parmnum);
+ else if (! TYPE_VOLATILE (ttl) && TYPE_VOLATILE (ttr))
+ warn_for_assignment ("%s discards `volatile' from pointer target type",
+ get_spelling (errtype), funname, parmnum);
+ /* If this is not a case of ignoring a mismatch in signedness,
+ no warning. */
+ else if (TYPE_MAIN_VARIANT (ttl) == void_type_node
+ || TYPE_MAIN_VARIANT (ttr) == void_type_node
+ || comp_target_types (type, rhstype))
+ ;
+ /* If there is a mismatch, do warn. */
+ else if (pedantic)
+ warn_for_assignment ("pointer targets in %s differ in signedness",
+ get_spelling (errtype), funname, parmnum);
+ }
+ else
+ {
+ /* Because const and volatile on functions are restrictions
+ that say the function will not do certain things,
+ it is okay to use a const or volatile function
+ where an ordinary one is wanted, but not vice-versa. */
+ if (TYPE_READONLY (ttl) && ! TYPE_READONLY (ttr))
+ warn_for_assignment ("%s makes `const *' function pointer from non-const",
+ get_spelling (errtype), funname, parmnum);
+ if (TYPE_VOLATILE (ttl) && ! TYPE_VOLATILE (ttr))
+ warn_for_assignment ("%s makes `volatile *' function pointer from non-volatile",
+ get_spelling (errtype), funname, parmnum);
+ }
+ }
+ else
+ warn_for_assignment ("%s from incompatible pointer type",
+ get_spelling (errtype), funname, parmnum);
+ return convert (type, rhs);
+ }
+ else if (codel == POINTER_TYPE && coder == INTEGER_TYPE)
+ {
+ /* An explicit constant 0 can convert to a pointer,
+ or one that results from arithmetic, even including
+ a cast to integer type. */
+ if (! (TREE_CODE (rhs) == INTEGER_CST && integer_zerop (rhs))
+ &&
+ ! (TREE_CODE (rhs) == NOP_EXPR
+ && TREE_CODE (TREE_TYPE (rhs)) == INTEGER_TYPE
+ && TREE_CODE (TREE_OPERAND (rhs, 0)) == INTEGER_CST
+ && integer_zerop (TREE_OPERAND (rhs, 0))))
+ {
+ warn_for_assignment ("%s makes pointer from integer without a cast",
+ get_spelling (errtype), funname, parmnum);
+ return convert (type, rhs);
+ }
+ return null_pointer_node;
+ }
+ else if (codel == INTEGER_TYPE && coder == POINTER_TYPE)
+ {
+ warn_for_assignment ("%s makes integer from pointer without a cast",
+ get_spelling (errtype), funname, parmnum);
+ return convert (type, rhs);
+ }
+
+ if (!errtype)
+ {
+ if (funname)
+ {
+ tree selector = maybe_building_objc_message_expr ();
+
+ if (selector && parmnum > 2)
+ error ("incompatible type for argument %d of `%s'",
+ parmnum - 2, IDENTIFIER_POINTER (selector));
+ else
+ error ("incompatible type for argument %d of `%s'",
+ parmnum, IDENTIFIER_POINTER (funname));
+ }
+ else
+ error ("incompatible type for argument %d of indirect function call",
+ parmnum);
+ }
+ else
+ error ("incompatible types in %s", get_spelling (errtype));
+
+ return error_mark_node;
+}
+
+/* Print a warning using MSG.
+ It gets OPNAME as its one parameter.
+ If OPNAME is null, it is replaced by "passing arg ARGNUM of `FUNCTION'".
+ FUNCTION and ARGNUM are handled specially if we are building an
+ Objective-C selector. */
+
+static void
+warn_for_assignment (msg, opname, function, argnum)
+ char *msg;
+ char *opname;
+ tree function;
+ int argnum;
+{
+ static char argstring[] = "passing arg %d of `%s'";
+ static char argnofun[] = "passing arg %d";
+
+ if (opname == 0)
+ {
+ tree selector = maybe_building_objc_message_expr ();
+
+ if (selector && argnum > 2)
+ {
+ function = selector;
+ argnum -= 2;
+ }
+ if (function)
+ {
+ /* Function name is known; supply it. */
+ opname = (char *) alloca (IDENTIFIER_LENGTH (function)
+ + sizeof (argstring) + 25 /*%d*/ + 1);
+ sprintf (opname, argstring, argnum, IDENTIFIER_POINTER (function));
+ }
+ else
+ {
+ /* Function name unknown (call through ptr); just give arg number. */
+ opname = (char *) alloca (sizeof (argnofun) + 25 /*%d*/ + 1);
+ sprintf (opname, argnofun, argnum);
+ }
+ }
+ pedwarn (msg, opname);
+}
+
+/* Return nonzero if VALUE is a valid constant-valued expression
+ for use in initializing a static variable; one that can be an
+ element of a "constant" initializer.
+
+ Return null_pointer_node if the value is absolute;
+ if it is relocatable, return the variable that determines the relocation.
+ We assume that VALUE has been folded as much as possible;
+ therefore, we do not need to check for such things as
+ arithmetic-combinations of integers. */
+
+tree
+initializer_constant_valid_p (value, endtype)
+ tree value;
+ tree endtype;
+{
+ switch (TREE_CODE (value))
+ {
+ case CONSTRUCTOR:
+ if (TREE_CODE (TREE_TYPE (value)) == UNION_TYPE
+ && TREE_CONSTANT (value))
+ return
+ initializer_constant_valid_p (TREE_VALUE (CONSTRUCTOR_ELTS (value)),
+ endtype);
+
+ return TREE_STATIC (value) ? null_pointer_node : 0;
+
+ case INTEGER_CST:
+ case REAL_CST:
+ case STRING_CST:
+ case COMPLEX_CST:
+ return null_pointer_node;
+
+ case ADDR_EXPR:
+ return TREE_OPERAND (value, 0);
+
+ case NON_LVALUE_EXPR:
+ return initializer_constant_valid_p (TREE_OPERAND (value, 0), endtype);
+
+ case CONVERT_EXPR:
+ case NOP_EXPR:
+ /* Allow conversions between pointer types. */
+ if (TREE_CODE (TREE_TYPE (value)) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == POINTER_TYPE)
+ return initializer_constant_valid_p (TREE_OPERAND (value, 0), endtype);
+
+ /* Allow conversions between real types. */
+ if (TREE_CODE (TREE_TYPE (value)) == REAL_TYPE
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == REAL_TYPE)
+ return initializer_constant_valid_p (TREE_OPERAND (value, 0), endtype);
+
+ /* Allow length-preserving conversions between integer types. */
+ if (TREE_CODE (TREE_TYPE (value)) == INTEGER_TYPE
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == INTEGER_TYPE
+ && (TYPE_PRECISION (TREE_TYPE (value))
+ == TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (value, 0)))))
+ return initializer_constant_valid_p (TREE_OPERAND (value, 0), endtype);
+
+ /* Allow conversions between other integer types only if
+ explicit value. */
+ if (TREE_CODE (TREE_TYPE (value)) == INTEGER_TYPE
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == INTEGER_TYPE)
+ {
+ tree inner = initializer_constant_valid_p (TREE_OPERAND (value, 0),
+ endtype);
+ if (inner == null_pointer_node)
+ return null_pointer_node;
+ return 0;
+ }
+
+ /* Allow (int) &foo provided int is as wide as a pointer. */
+ if (TREE_CODE (TREE_TYPE (value)) == INTEGER_TYPE
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == POINTER_TYPE
+ && (TYPE_PRECISION (TREE_TYPE (value))
+ >= TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (value, 0)))))
+ return initializer_constant_valid_p (TREE_OPERAND (value, 0),
+ endtype);
+
+ /* Likewise conversions from int to pointers. */
+ if (TREE_CODE (TREE_TYPE (value)) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == INTEGER_TYPE
+ && (TYPE_PRECISION (TREE_TYPE (value))
+ <= TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (value, 0)))))
+ return initializer_constant_valid_p (TREE_OPERAND (value, 0),
+ endtype);
+
+ /* Allow conversions to union types if the value inside is okay. */
+ if (TREE_CODE (TREE_TYPE (value)) == UNION_TYPE)
+ return initializer_constant_valid_p (TREE_OPERAND (value, 0),
+ endtype);
+ return 0;
+
+ case PLUS_EXPR:
+ if (TREE_CODE (endtype) == INTEGER_TYPE
+ && TYPE_PRECISION (endtype) < POINTER_SIZE)
+ return 0;
+ {
+ tree valid0 = initializer_constant_valid_p (TREE_OPERAND (value, 0),
+ endtype);
+ tree valid1 = initializer_constant_valid_p (TREE_OPERAND (value, 1),
+ endtype);
+ /* If either term is absolute, use the other terms relocation. */
+ if (valid0 == null_pointer_node)
+ return valid1;
+ if (valid1 == null_pointer_node)
+ return valid0;
+ return 0;
+ }
+
+ case MINUS_EXPR:
+ if (TREE_CODE (endtype) == INTEGER_TYPE
+ && TYPE_PRECISION (endtype) < POINTER_SIZE)
+ return 0;
+ {
+ tree valid0 = initializer_constant_valid_p (TREE_OPERAND (value, 0),
+ endtype);
+ tree valid1 = initializer_constant_valid_p (TREE_OPERAND (value, 1),
+ endtype);
+ /* Win if second argument is absolute. */
+ if (valid1 == null_pointer_node)
+ return valid0;
+ /* Win if both arguments have the same relocation.
+ Then the value is absolute. */
+ if (valid0 == valid1)
+ return null_pointer_node;
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/* If VALUE is a compound expr all of whose expressions are constant, then
+ return its value. Otherwise, return error_mark_node.
+
+ This is for handling COMPOUND_EXPRs as initializer elements
+ which is allowed with a warning when -pedantic is specified. */
+
+static tree
+valid_compound_expr_initializer (value, endtype)
+ tree value;
+ tree endtype;
+{
+ if (TREE_CODE (value) == COMPOUND_EXPR)
+ {
+ if (valid_compound_expr_initializer (TREE_OPERAND (value, 0), endtype)
+ == error_mark_node)
+ return error_mark_node;
+ return valid_compound_expr_initializer (TREE_OPERAND (value, 1),
+ endtype);
+ }
+ else if (! TREE_CONSTANT (value)
+ && ! initializer_constant_valid_p (value, endtype))
+ return error_mark_node;
+ else
+ return value;
+}
+
+/* Perform appropriate conversions on the initial value of a variable,
+ store it in the declaration DECL,
+ and print any error messages that are appropriate.
+ If the init is invalid, store an ERROR_MARK. */
+
+void
+store_init_value (decl, init)
+ tree decl, init;
+{
+ register tree value, type;
+
+ /* If variable's type was invalidly declared, just ignore it. */
+
+ type = TREE_TYPE (decl);
+ if (TREE_CODE (type) == ERROR_MARK)
+ return;
+
+ /* Digest the specified initializer into an expression. */
+
+ value = digest_init (type, init, TREE_STATIC (decl),
+ TREE_STATIC (decl) || pedantic);
+
+ /* Store the expression if valid; else report error. */
+
+#if 0
+ /* Note that this is the only place we can detect the error
+ in a case such as struct foo bar = (struct foo) { x, y };
+ where there is one initial value which is a constructor expression. */
+ if (value == error_mark_node)
+ ;
+ else if (TREE_STATIC (decl) && ! TREE_CONSTANT (value))
+ {
+ error ("initializer for static variable is not constant");
+ value = error_mark_node;
+ }
+ else if (TREE_STATIC (decl)
+ && initializer_constant_valid_p (value, TREE_TYPE (value)) == 0)
+ {
+ error ("initializer for static variable uses complicated arithmetic");
+ value = error_mark_node;
+ }
+ else
+ {
+ if (pedantic && TREE_CODE (value) == CONSTRUCTOR)
+ {
+ if (! TREE_CONSTANT (value))
+ pedwarn ("aggregate initializer is not constant");
+ else if (! TREE_STATIC (value))
+ pedwarn ("aggregate initializer uses complicated arithmetic");
+ }
+ }
+#endif
+
+ DECL_INITIAL (decl) = value;
+
+ /* ANSI wants warnings about out-of-range constant initializers. */
+ STRIP_TYPE_NOPS (value);
+ constant_expression_warning (value);
+}
+
+/* Methods for storing and printing names for error messages. */
+
+/* Implement a spelling stack that allows components of a name to be pushed
+ and popped. Each element on the stack is this structure. */
+
+struct spelling
+{
+ int kind;
+ union
+ {
+ int i;
+ char *s;
+ } u;
+};
+
+#define SPELLING_STRING 1
+#define SPELLING_MEMBER 2
+#define SPELLING_BOUNDS 3
+
+static struct spelling *spelling; /* Next stack element (unused). */
+static struct spelling *spelling_base; /* Spelling stack base. */
+static int spelling_size; /* Size of the spelling stack. */
+
+/* Macros to save and restore the spelling stack around push_... functions.
+ Alternative to SAVE_SPELLING_STACK. */
+
+#define SPELLING_DEPTH() (spelling - spelling_base)
+#define RESTORE_SPELLING_DEPTH(depth) (spelling = spelling_base + depth)
+
+/* Save and restore the spelling stack around arbitrary C code. */
+
+#define SAVE_SPELLING_DEPTH(code) \
+{ \
+ int __depth = SPELLING_DEPTH (); \
+ code; \
+ RESTORE_SPELLING_DEPTH (__depth); \
+}
+
+/* Push an element on the spelling stack with type KIND and assign VALUE
+ to MEMBER. */
+
+#define PUSH_SPELLING(KIND, VALUE, MEMBER) \
+{ \
+ int depth = SPELLING_DEPTH (); \
+ \
+ if (depth >= spelling_size) \
+ { \
+ spelling_size += 10; \
+ if (spelling_base == 0) \
+ spelling_base \
+ = (struct spelling *) xmalloc (spelling_size * sizeof (struct spelling)); \
+ else \
+ spelling_base \
+ = (struct spelling *) xrealloc (spelling_base, \
+ spelling_size * sizeof (struct spelling)); \
+ RESTORE_SPELLING_DEPTH (depth); \
+ } \
+ \
+ spelling->kind = (KIND); \
+ spelling->MEMBER = (VALUE); \
+ spelling++; \
+}
+
+/* Push STRING on the stack. Printed literally. */
+
+static void
+push_string (string)
+ char *string;
+{
+ PUSH_SPELLING (SPELLING_STRING, string, u.s);
+}
+
+/* Push a member name on the stack. Printed as '.' STRING. */
+
+static void
+push_member_name (decl)
+ tree decl;
+
+{
+ char *string
+ = DECL_NAME (decl) ? IDENTIFIER_POINTER (DECL_NAME (decl)) : "<anonymous>";
+ PUSH_SPELLING (SPELLING_MEMBER, string, u.s);
+}
+
+/* Push an array bounds on the stack. Printed as [BOUNDS]. */
+
+static void
+push_array_bounds (bounds)
+ int bounds;
+{
+ PUSH_SPELLING (SPELLING_BOUNDS, bounds, u.i);
+}
+
+/* Compute the maximum size in bytes of the printed spelling. */
+
+static int
+spelling_length ()
+{
+ register int size = 0;
+ register struct spelling *p;
+
+ for (p = spelling_base; p < spelling; p++)
+ {
+ if (p->kind == SPELLING_BOUNDS)
+ size += 25;
+ else
+ size += strlen (p->u.s) + 1;
+ }
+
+ return size;
+}
+
+/* Print the spelling to BUFFER and return it. */
+
+static char *
+print_spelling (buffer)
+ register char *buffer;
+{
+ register char *d = buffer;
+ register char *s;
+ register struct spelling *p;
+
+ for (p = spelling_base; p < spelling; p++)
+ if (p->kind == SPELLING_BOUNDS)
+ {
+ sprintf (d, "[%d]", p->u.i);
+ d += strlen (d);
+ }
+ else
+ {
+ if (p->kind == SPELLING_MEMBER)
+ *d++ = '.';
+ for (s = p->u.s; *d = *s++; d++)
+ ;
+ }
+ *d++ = '\0';
+ return buffer;
+}
+
+/* Provide a means to pass component names derived from the spelling stack. */
+
+char initialization_message;
+
+/* Interpret the spelling of the given ERRTYPE message. */
+
+static char *
+get_spelling (errtype)
+ char *errtype;
+{
+ static char *buffer;
+ static int size = -1;
+
+ if (errtype == &initialization_message)
+ {
+ /* Avoid counting chars */
+ static char message[] = "initialization of `%s'";
+ register int needed = sizeof (message) + spelling_length () + 1;
+ char *temp;
+
+ if (size < 0)
+ buffer = (char *) xmalloc (size = needed);
+ if (needed > size)
+ buffer = (char *) xrealloc (buffer, size = needed);
+
+ temp = (char *) alloca (needed);
+ sprintf (buffer, message, print_spelling (temp));
+ return buffer;
+ }
+
+ return errtype;
+}
+
+/* Issue an error message for a bad initializer component.
+ FORMAT describes the message. OFWHAT is the name for the component.
+ LOCAL is a format string for formatting the insertion of the name
+ into the message.
+
+ If OFWHAT is null, the component name is stored on the spelling stack.
+ If the component name is a null string, then LOCAL is omitted entirely. */
+
+void
+error_init (format, local, ofwhat)
+ char *format, *local, *ofwhat;
+{
+ char *buffer;
+
+ if (ofwhat == 0)
+ ofwhat = print_spelling ((char *) alloca (spelling_length () + 1));
+ buffer = (char *) alloca (strlen (local) + strlen (ofwhat) + 2);
+
+ if (*ofwhat)
+ sprintf (buffer, local, ofwhat);
+ else
+ buffer[0] = 0;
+
+ error (format, buffer);
+}
+
+/* Issue a pedantic warning for a bad initializer component.
+ FORMAT describes the message. OFWHAT is the name for the component.
+ LOCAL is a format string for formatting the insertion of the name
+ into the message.
+
+ If OFWHAT is null, the component name is stored on the spelling stack.
+ If the component name is a null string, then LOCAL is omitted entirely. */
+
+void
+pedwarn_init (format, local, ofwhat)
+ char *format, *local, *ofwhat;
+{
+ char *buffer;
+
+ if (ofwhat == 0)
+ ofwhat = print_spelling ((char *) alloca (spelling_length () + 1));
+ buffer = (char *) alloca (strlen (local) + strlen (ofwhat) + 2);
+
+ if (*ofwhat)
+ sprintf (buffer, local, ofwhat);
+ else
+ buffer[0] = 0;
+
+ pedwarn (format, buffer);
+}
+
+/* Issue a warning for a bad initializer component.
+ FORMAT describes the message. OFWHAT is the name for the component.
+ LOCAL is a format string for formatting the insertion of the name
+ into the message.
+
+ If OFWHAT is null, the component name is stored on the spelling stack.
+ If the component name is a null string, then LOCAL is omitted entirely. */
+
+static void
+warning_init (format, local, ofwhat)
+ char *format, *local, *ofwhat;
+{
+ char *buffer;
+
+ if (ofwhat == 0)
+ ofwhat = print_spelling ((char *) alloca (spelling_length () + 1));
+ buffer = (char *) alloca (strlen (local) + strlen (ofwhat) + 2);
+
+ if (*ofwhat)
+ sprintf (buffer, local, ofwhat);
+ else
+ buffer[0] = 0;
+
+ warning (format, buffer);
+}
+
+/* Digest the parser output INIT as an initializer for type TYPE.
+ Return a C expression of type TYPE to represent the initial value.
+
+ The arguments REQUIRE_CONSTANT and CONSTRUCTOR_CONSTANT request errors
+ if non-constant initializers or elements are seen. CONSTRUCTOR_CONSTANT
+ applies only to elements of constructors. */
+
+static tree
+digest_init (type, init, require_constant, constructor_constant)
+ tree type, init;
+ int require_constant, constructor_constant;
+{
+ enum tree_code code = TREE_CODE (type);
+ tree inside_init = init;
+
+ if (init == error_mark_node)
+ return init;
+
+ /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
+ /* Do not use STRIP_NOPS here. We do not want an enumerator
+ whose value is 0 to count as a null pointer constant. */
+ if (TREE_CODE (init) == NON_LVALUE_EXPR)
+ inside_init = TREE_OPERAND (init, 0);
+
+ /* Initialization of an array of chars from a string constant
+ optionally enclosed in braces. */
+
+ if (code == ARRAY_TYPE)
+ {
+ tree typ1 = TYPE_MAIN_VARIANT (TREE_TYPE (type));
+ if ((typ1 == char_type_node
+ || typ1 == signed_char_type_node
+ || typ1 == unsigned_char_type_node
+ || typ1 == unsigned_wchar_type_node
+ || typ1 == signed_wchar_type_node)
+ && ((inside_init && TREE_CODE (inside_init) == STRING_CST)))
+ {
+ if (comptypes (TYPE_MAIN_VARIANT (TREE_TYPE (inside_init)),
+ TYPE_MAIN_VARIANT (type)))
+ return inside_init;
+
+ if ((TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (inside_init)))
+ != char_type_node)
+ && TYPE_PRECISION (typ1) == TYPE_PRECISION (char_type_node))
+ {
+ error_init ("char-array%s initialized from wide string",
+ " `%s'", NULL);
+ return error_mark_node;
+ }
+ if ((TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (inside_init)))
+ == char_type_node)
+ && TYPE_PRECISION (typ1) != TYPE_PRECISION (char_type_node))
+ {
+ error_init ("int-array%s initialized from non-wide string",
+ " `%s'", NULL);
+ return error_mark_node;
+ }
+
+ TREE_TYPE (inside_init) = type;
+ if (TYPE_DOMAIN (type) != 0
+ && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST)
+ {
+ register int size = TREE_INT_CST_LOW (TYPE_SIZE (type));
+ size = (size + BITS_PER_UNIT - 1) / BITS_PER_UNIT;
+ /* Subtract 1 (or sizeof (wchar_t))
+ because it's ok to ignore the terminating null char
+ that is counted in the length of the constant. */
+ if (size < TREE_STRING_LENGTH (inside_init)
+ - (TYPE_PRECISION (typ1) != TYPE_PRECISION (char_type_node)
+ ? TYPE_PRECISION (wchar_type_node) / BITS_PER_UNIT
+ : 1))
+ pedwarn_init (
+ "initializer-string for array of chars%s is too long",
+ " `%s'", NULL);
+ }
+ return inside_init;
+ }
+ }
+
+ /* Any type can be initialized
+ from an expression of the same type, optionally with braces. */
+
+ if (inside_init && TREE_TYPE (inside_init) != 0
+ && (comptypes (TYPE_MAIN_VARIANT (TREE_TYPE (inside_init)),
+ TYPE_MAIN_VARIANT (type))
+ || (code == ARRAY_TYPE
+ && comptypes (TREE_TYPE (inside_init), type))
+ || (code == POINTER_TYPE
+ && (TREE_CODE (TREE_TYPE (inside_init)) == ARRAY_TYPE
+ || TREE_CODE (TREE_TYPE (inside_init)) == FUNCTION_TYPE)
+ && comptypes (TREE_TYPE (TREE_TYPE (inside_init)),
+ TREE_TYPE (type)))))
+ {
+ if (code == POINTER_TYPE
+ && (TREE_CODE (TREE_TYPE (inside_init)) == ARRAY_TYPE
+ || TREE_CODE (TREE_TYPE (inside_init)) == FUNCTION_TYPE))
+ inside_init = default_conversion (inside_init);
+ else if (code == ARRAY_TYPE && TREE_CODE (inside_init) != STRING_CST
+ && TREE_CODE (inside_init) != CONSTRUCTOR)
+ {
+ error_init ("array%s initialized from non-constant array expression",
+ " `%s'", NULL);
+ return error_mark_node;
+ }
+
+ if (optimize && TREE_CODE (inside_init) == VAR_DECL)
+ inside_init = decl_constant_value (inside_init);
+
+ /* Compound expressions can only occur here if -pedantic or
+ -pedantic-errors is specified. In the later case, we always want
+ an error. In the former case, we simply want a warning. */
+ if (require_constant && pedantic
+ && TREE_CODE (inside_init) == COMPOUND_EXPR)
+ {
+ inside_init
+ = valid_compound_expr_initializer (inside_init,
+ TREE_TYPE (inside_init));
+ if (inside_init == error_mark_node)
+ error_init ("initializer element%s is not constant",
+ " for `%s'", NULL);
+ else
+ pedwarn_init ("initializer element%s is not constant",
+ " for `%s'", NULL);
+ if (flag_pedantic_errors)
+ inside_init = error_mark_node;
+ }
+ else if (require_constant && ! TREE_CONSTANT (inside_init))
+ {
+ error_init ("initializer element%s is not constant",
+ " for `%s'", NULL);
+ inside_init = error_mark_node;
+ }
+ else if (require_constant
+ && initializer_constant_valid_p (inside_init, TREE_TYPE (inside_init)) == 0)
+ {
+ error_init ("initializer element%s is not computable at load time",
+ " for `%s'", NULL);
+ inside_init = error_mark_node;
+ }
+
+ return inside_init;
+ }
+
+ /* Handle scalar types, including conversions. */
+
+ if (code == INTEGER_TYPE || code == REAL_TYPE || code == POINTER_TYPE
+ || code == ENUMERAL_TYPE || code == COMPLEX_TYPE)
+ {
+ /* Note that convert_for_assignment calls default_conversion
+ for arrays and functions. We must not call it in the
+ case where inside_init is a null pointer constant. */
+ inside_init
+ = convert_for_assignment (type, init, "initialization",
+ NULL_TREE, NULL_TREE, 0);
+
+ if (require_constant && ! TREE_CONSTANT (inside_init))
+ {
+ error_init ("initializer element%s is not constant",
+ " for `%s'", NULL);
+ inside_init = error_mark_node;
+ }
+ else if (require_constant
+ && initializer_constant_valid_p (inside_init, TREE_TYPE (inside_init)) == 0)
+ {
+ error_init ("initializer element%s is not computable at load time",
+ " for `%s'", NULL);
+ inside_init = error_mark_node;
+ }
+
+ return inside_init;
+ }
+
+ /* Come here only for records and arrays. */
+
+ if (TYPE_SIZE (type) && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+ {
+ error_init ("variable-sized object%s may not be initialized",
+ " `%s'", NULL);
+ return error_mark_node;
+ }
+
+ /* Traditionally, you can write struct foo x = 0;
+ and it initializes the first element of x to 0. */
+ if (flag_traditional)
+ {
+ tree top = 0, prev = 0;
+ while (TREE_CODE (type) == RECORD_TYPE
+ || TREE_CODE (type) == ARRAY_TYPE
+ || TREE_CODE (type) == QUAL_UNION_TYPE
+ || TREE_CODE (type) == UNION_TYPE)
+ {
+ tree temp = build (CONSTRUCTOR, type, NULL_TREE, NULL_TREE);
+ if (prev == 0)
+ top = temp;
+ else
+ TREE_OPERAND (prev, 1) = build_tree_list (NULL_TREE, temp);
+ prev = temp;
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ type = TREE_TYPE (type);
+ else if (TYPE_FIELDS (type))
+ type = TREE_TYPE (TYPE_FIELDS (type));
+ else
+ {
+ error_init ("invalid initializer%s", " for `%s'", NULL);
+ return error_mark_node;
+ }
+ }
+ TREE_OPERAND (prev, 1)
+ = build_tree_list (NULL_TREE,
+ digest_init (type, init, require_constant,
+ constructor_constant));
+ return top;
+ }
+ error_init ("invalid initializer%s", " for `%s'", NULL);
+ return error_mark_node;
+}
+
+/* Handle initializers that use braces. */
+
+/* Type of object we are accumulating a constructor for.
+ This type is always a RECORD_TYPE, UNION_TYPE or ARRAY_TYPE. */
+static tree constructor_type;
+
+/* For a RECORD_TYPE or UNION_TYPE, this is the chain of fields
+ left to fill. */
+static tree constructor_fields;
+
+/* For an ARRAY_TYPE, this is the specified index
+ at which to store the next element we get.
+ This is a special INTEGER_CST node that we modify in place. */
+static tree constructor_index;
+
+/* For an ARRAY_TYPE, this is the end index of the range
+ to intitialize with the next element, or NULL in the ordinary case
+ where the element is used just once. */
+static tree constructor_range_end;
+
+/* For an ARRAY_TYPE, this is the maximum index. */
+static tree constructor_max_index;
+
+/* For a RECORD_TYPE, this is the first field not yet written out. */
+static tree constructor_unfilled_fields;
+
+/* For an ARRAY_TYPE, this is the index of the first element
+ not yet written out.
+ This is a special INTEGER_CST node that we modify in place. */
+static tree constructor_unfilled_index;
+
+/* In a RECORD_TYPE, the byte index of the next consecutive field.
+ This is so we can generate gaps between fields, when appropriate.
+ This is a special INTEGER_CST node that we modify in place. */
+static tree constructor_bit_index;
+
+/* If we are saving up the elements rather than allocating them,
+ this is the list of elements so far (in reverse order,
+ most recent first). */
+static tree constructor_elements;
+
+/* 1 if so far this constructor's elements are all compile-time constants. */
+static int constructor_constant;
+
+/* 1 if so far this constructor's elements are all valid address constants. */
+static int constructor_simple;
+
+/* 1 if this constructor is erroneous so far. */
+static int constructor_erroneous;
+
+/* 1 if have called defer_addressed_constants. */
+static int constructor_subconstants_deferred;
+
+/* List of pending elements at this constructor level.
+ These are elements encountered out of order
+ which belong at places we haven't reached yet in actually
+ writing the output. */
+static tree constructor_pending_elts;
+
+/* The SPELLING_DEPTH of this constructor. */
+static int constructor_depth;
+
+/* 0 if implicitly pushing constructor levels is allowed. */
+int constructor_no_implicit = 0; /* 0 for C; 1 for some other languages. */
+
+/* 1 if this constructor level was entered implicitly. */
+static int constructor_implicit;
+
+static int require_constant_value;
+static int require_constant_elements;
+
+/* 1 if it is ok to output this constructor as we read it.
+ 0 means must accumulate a CONSTRUCTOR expression. */
+static int constructor_incremental;
+
+/* DECL node for which an initializer is being read.
+ 0 means we are reading a constructor expression
+ such as (struct foo) {...}. */
+static tree constructor_decl;
+
+/* start_init saves the ASMSPEC arg here for really_start_incremental_init. */
+static char *constructor_asmspec;
+
+/* Nonzero if this is an initializer for a top-level decl. */
+static int constructor_top_level;
+
+/* When we finish reading a constructor expression
+ (constructor_decl is 0), the CONSTRUCTOR goes here. */
+static tree constructor_result;
+
+/* This stack has a level for each implicit or explicit level of
+ structuring in the initializer, including the outermost one. It
+ saves the values of most of the variables above. */
+
+struct constructor_stack
+{
+ struct constructor_stack *next;
+ tree type;
+ tree fields;
+ tree index;
+ tree range_end;
+ tree max_index;
+ tree unfilled_index;
+ tree unfilled_fields;
+ tree bit_index;
+ tree elements;
+ int offset;
+ tree pending_elts;
+ int depth;
+ /* If nonzero, this value should replace the entire
+ constructor at this level. */
+ tree replacement_value;
+ char constant;
+ char simple;
+ char implicit;
+ char incremental;
+ char erroneous;
+ char outer;
+};
+
+struct constructor_stack *constructor_stack;
+
+/* This stack records separate initializers that are nested.
+ Nested initializers can't happen in ANSI C, but GNU C allows them
+ in cases like { ... (struct foo) { ... } ... }. */
+
+struct initializer_stack
+{
+ struct initializer_stack *next;
+ tree decl;
+ char *asmspec;
+ struct constructor_stack *constructor_stack;
+ tree elements;
+ struct spelling *spelling;
+ struct spelling *spelling_base;
+ int spelling_size;
+ char top_level;
+ char incremental;
+ char require_constant_value;
+ char require_constant_elements;
+ char deferred;
+};
+
+struct initializer_stack *initializer_stack;
+
+/* Prepare to parse and output the initializer for variable DECL. */
+
+void
+start_init (decl, asmspec_tree, top_level)
+ tree decl;
+ tree asmspec_tree;
+ int top_level;
+{
+ char *locus;
+ struct initializer_stack *p
+ = (struct initializer_stack *) xmalloc (sizeof (struct initializer_stack));
+ char *asmspec = 0;
+
+ if (asmspec_tree)
+ asmspec = TREE_STRING_POINTER (asmspec_tree);
+
+ p->decl = constructor_decl;
+ p->asmspec = constructor_asmspec;
+ p->incremental = constructor_incremental;
+ p->require_constant_value = require_constant_value;
+ p->require_constant_elements = require_constant_elements;
+ p->constructor_stack = constructor_stack;
+ p->elements = constructor_elements;
+ p->spelling = spelling;
+ p->spelling_base = spelling_base;
+ p->spelling_size = spelling_size;
+ p->deferred = constructor_subconstants_deferred;
+ p->top_level = constructor_top_level;
+ p->next = initializer_stack;
+ initializer_stack = p;
+
+ constructor_decl = decl;
+ constructor_incremental = top_level;
+ constructor_asmspec = asmspec;
+ constructor_subconstants_deferred = 0;
+ constructor_top_level = top_level;
+
+ if (decl != 0)
+ {
+ require_constant_value = TREE_STATIC (decl);
+ require_constant_elements
+ = ((TREE_STATIC (decl) || pedantic)
+ /* For a scalar, you can always use any value to initialize,
+ even within braces. */
+ && (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE
+ || TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE
+ || TREE_CODE (TREE_TYPE (decl)) == UNION_TYPE
+ || TREE_CODE (TREE_TYPE (decl)) == QUAL_UNION_TYPE));
+ locus = IDENTIFIER_POINTER (DECL_NAME (decl));
+ constructor_incremental |= TREE_STATIC (decl);
+ }
+ else
+ {
+ require_constant_value = 0;
+ require_constant_elements = 0;
+ locus = "(anonymous)";
+ }
+
+ constructor_stack = 0;
+
+ missing_braces_mentioned = 0;
+
+ spelling_base = 0;
+ spelling_size = 0;
+ RESTORE_SPELLING_DEPTH (0);
+
+ if (locus)
+ push_string (locus);
+}
+
+void
+finish_init ()
+{
+ struct initializer_stack *p = initializer_stack;
+
+ /* Output subconstants (string constants, usually)
+ that were referenced within this initializer and saved up.
+ Must do this if and only if we called defer_addressed_constants. */
+ if (constructor_subconstants_deferred)
+ output_deferred_addressed_constants ();
+
+ /* Free the whole constructor stack of this initializer. */
+ while (constructor_stack)
+ {
+ struct constructor_stack *q = constructor_stack;
+ constructor_stack = q->next;
+ free (q);
+ }
+
+ /* Pop back to the data of the outer initializer (if any). */
+ constructor_decl = p->decl;
+ constructor_asmspec = p->asmspec;
+ constructor_incremental = p->incremental;
+ require_constant_value = p->require_constant_value;
+ require_constant_elements = p->require_constant_elements;
+ constructor_stack = p->constructor_stack;
+ constructor_elements = p->elements;
+ spelling = p->spelling;
+ spelling_base = p->spelling_base;
+ spelling_size = p->spelling_size;
+ constructor_subconstants_deferred = p->deferred;
+ constructor_top_level = p->top_level;
+ initializer_stack = p->next;
+ free (p);
+}
+
+/* Call here when we see the initializer is surrounded by braces.
+ This is instead of a call to push_init_level;
+ it is matched by a call to pop_init_level.
+
+ TYPE is the type to initialize, for a constructor expression.
+ For an initializer for a decl, TYPE is zero. */
+
+void
+really_start_incremental_init (type)
+ tree type;
+{
+ struct constructor_stack *p
+ = (struct constructor_stack *) xmalloc (sizeof (struct constructor_stack));
+
+ if (type == 0)
+ type = TREE_TYPE (constructor_decl);
+
+ /* Turn off constructor_incremental if type is a struct with bitfields.
+ Do this before the first push, so that the corrected value
+ is available in finish_init. */
+ check_init_type_bitfields (type);
+
+ p->type = constructor_type;
+ p->fields = constructor_fields;
+ p->index = constructor_index;
+ p->range_end = constructor_range_end;
+ p->max_index = constructor_max_index;
+ p->unfilled_index = constructor_unfilled_index;
+ p->unfilled_fields = constructor_unfilled_fields;
+ p->bit_index = constructor_bit_index;
+ p->elements = constructor_elements;
+ p->constant = constructor_constant;
+ p->simple = constructor_simple;
+ p->erroneous = constructor_erroneous;
+ p->pending_elts = constructor_pending_elts;
+ p->depth = constructor_depth;
+ p->replacement_value = 0;
+ p->implicit = 0;
+ p->incremental = constructor_incremental;
+ p->outer = 0;
+ p->next = 0;
+ constructor_stack = p;
+
+ constructor_constant = 1;
+ constructor_simple = 1;
+ constructor_depth = SPELLING_DEPTH ();
+ constructor_elements = 0;
+ constructor_pending_elts = 0;
+ constructor_type = type;
+
+ if (TREE_CODE (constructor_type) == RECORD_TYPE
+ || TREE_CODE (constructor_type) == UNION_TYPE)
+ {
+ constructor_fields = TYPE_FIELDS (constructor_type);
+ /* Skip any nameless bit fields atthe beginning. */
+ while (constructor_fields != 0 && DECL_BIT_FIELD (constructor_fields)
+ && DECL_NAME (constructor_fields) == 0)
+ constructor_fields = TREE_CHAIN (constructor_fields);
+ constructor_unfilled_fields = constructor_fields;
+ constructor_bit_index = copy_node (integer_zero_node);
+ }
+ else if (TREE_CODE (constructor_type) == ARRAY_TYPE)
+ {
+ constructor_range_end = 0;
+ if (TYPE_DOMAIN (constructor_type))
+ {
+ constructor_max_index
+ = TYPE_MAX_VALUE (TYPE_DOMAIN (constructor_type));
+ constructor_index
+ = copy_node (TYPE_MIN_VALUE (TYPE_DOMAIN (constructor_type)));
+ }
+ else
+ constructor_index = copy_node (integer_zero_node);
+ constructor_unfilled_index = copy_node (constructor_index);
+ }
+ else
+ {
+ /* Handle the case of int x = {5}; */
+ constructor_fields = constructor_type;
+ constructor_unfilled_fields = constructor_type;
+ }
+
+ if (constructor_incremental)
+ {
+ int momentary = suspend_momentary ();
+ push_obstacks_nochange ();
+ if (TREE_PERMANENT (constructor_decl))
+ end_temporary_allocation ();
+ make_decl_rtl (constructor_decl, constructor_asmspec,
+ constructor_top_level);
+ assemble_variable (constructor_decl, constructor_top_level, 0, 1);
+ pop_obstacks ();
+ resume_momentary (momentary);
+ }
+
+ if (constructor_incremental)
+ {
+ defer_addressed_constants ();
+ constructor_subconstants_deferred = 1;
+ }
+}
+
+/* Push down into a subobject, for initialization.
+ If this is for an explicit set of braces, IMPLICIT is 0.
+ If it is because the next element belongs at a lower level,
+ IMPLICIT is 1. */
+
+void
+push_init_level (implicit)
+ int implicit;
+{
+ struct constructor_stack *p;
+
+ /* If we've exhausted any levels that didn't have braces,
+ pop them now. */
+ while (constructor_stack->implicit)
+ {
+ if ((TREE_CODE (constructor_type) == RECORD_TYPE
+ || TREE_CODE (constructor_type) == UNION_TYPE)
+ && constructor_fields == 0)
+ process_init_element (pop_init_level (1));
+ else if (TREE_CODE (constructor_type) == ARRAY_TYPE
+ && tree_int_cst_lt (constructor_max_index, constructor_index))
+ process_init_element (pop_init_level (1));
+ else
+ break;
+ }
+
+ /* Structure elements may require alignment. Do this now
+ if necessary for the subaggregate. */
+ if (constructor_incremental && TREE_CODE (constructor_type) == RECORD_TYPE
+ && constructor_fields)
+ {
+ /* Advance to offset of this element. */
+ if (! tree_int_cst_equal (constructor_bit_index,
+ DECL_FIELD_BITPOS (constructor_fields)))
+ {
+ int next = (TREE_INT_CST_LOW
+ (DECL_FIELD_BITPOS (constructor_fields))
+ / BITS_PER_UNIT);
+ int here = (TREE_INT_CST_LOW (constructor_bit_index)
+ / BITS_PER_UNIT);
+
+ assemble_zeros (next - here);
+ }
+ }
+
+ p = (struct constructor_stack *) xmalloc (sizeof (struct constructor_stack));
+ p->type = constructor_type;
+ p->fields = constructor_fields;
+ p->index = constructor_index;
+ p->range_end = constructor_range_end;
+ p->max_index = constructor_max_index;
+ p->unfilled_index = constructor_unfilled_index;
+ p->unfilled_fields = constructor_unfilled_fields;
+ p->bit_index = constructor_bit_index;
+ p->elements = constructor_elements;
+ p->constant = constructor_constant;
+ p->simple = constructor_simple;
+ p->erroneous = constructor_erroneous;
+ p->pending_elts = constructor_pending_elts;
+ p->depth = constructor_depth;
+ p->replacement_value = 0;
+ p->implicit = implicit;
+ p->incremental = constructor_incremental;
+ p->outer = 0;
+ p->next = constructor_stack;
+ constructor_stack = p;
+
+ constructor_constant = 1;
+ constructor_simple = 1;
+ constructor_depth = SPELLING_DEPTH ();
+ constructor_elements = 0;
+ constructor_pending_elts = 0;
+
+ /* Don't die if an entire brace-pair level is superfluous
+ in the containing level. */
+ if (constructor_type == 0)
+ ;
+ else if (TREE_CODE (constructor_type) == RECORD_TYPE
+ || TREE_CODE (constructor_type) == UNION_TYPE)
+ {
+ /* Don't die if there are extra init elts at the end. */
+ if (constructor_fields == 0)
+ constructor_type = 0;
+ else
+ {
+ constructor_type = TREE_TYPE (constructor_fields);
+ push_member_name (constructor_fields);
+ if (constructor_fields != constructor_unfilled_fields)
+ constructor_incremental = 0;
+ }
+ }
+ else if (TREE_CODE (constructor_type) == ARRAY_TYPE)
+ {
+ constructor_type = TREE_TYPE (constructor_type);
+ push_array_bounds (TREE_INT_CST_LOW (constructor_index));
+ if (! tree_int_cst_equal (constructor_index, constructor_unfilled_index)
+ || constructor_range_end != 0)
+ constructor_incremental = 0;
+ }
+
+ if (constructor_type == 0)
+ {
+ error_init ("extra brace group at end of initializer%s",
+ " for `%s'", NULL);
+ constructor_fields = 0;
+ constructor_unfilled_fields = 0;
+ return;
+ }
+
+ /* Turn off constructor_incremental if type is a struct with bitfields. */
+ check_init_type_bitfields (constructor_type);
+
+ if (implicit && warn_missing_braces && !missing_braces_mentioned)
+ {
+ missing_braces_mentioned = 1;
+ warning_init ("missing braces around initializer%s", " for `%s'", NULL);
+ }
+
+ if (TREE_CODE (constructor_type) == RECORD_TYPE
+ || TREE_CODE (constructor_type) == UNION_TYPE)
+ {
+ constructor_fields = TYPE_FIELDS (constructor_type);
+ /* Skip any nameless bit fields atthe beginning. */
+ while (constructor_fields != 0 && DECL_BIT_FIELD (constructor_fields)
+ && DECL_NAME (constructor_fields) == 0)
+ constructor_fields = TREE_CHAIN (constructor_fields);
+ constructor_unfilled_fields = constructor_fields;
+ constructor_bit_index = copy_node (integer_zero_node);
+ }
+ else if (TREE_CODE (constructor_type) == ARRAY_TYPE)
+ {
+ constructor_range_end = 0;
+ if (TYPE_DOMAIN (constructor_type))
+ {
+ constructor_max_index
+ = TYPE_MAX_VALUE (TYPE_DOMAIN (constructor_type));
+ constructor_index
+ = copy_node (TYPE_MIN_VALUE (TYPE_DOMAIN (constructor_type)));
+ }
+ else
+ constructor_index = copy_node (integer_zero_node);
+ constructor_unfilled_index = copy_node (constructor_index);
+ }
+ else
+ {
+ warning_init ("braces around scalar initializer%s", " for `%s'", NULL);
+ constructor_fields = constructor_type;
+ constructor_unfilled_fields = constructor_type;
+ }
+}
+
+/* Don't read a struct incrementally if it has any bitfields,
+ because the incremental reading code doesn't know how to
+ handle bitfields yet. */
+
+static void
+check_init_type_bitfields (type)
+ tree type;
+{
+ if (TREE_CODE (type) == RECORD_TYPE)
+ {
+ tree tail;
+ for (tail = TYPE_FIELDS (type); tail;
+ tail = TREE_CHAIN (tail))
+ {
+ if (DECL_BIT_FIELD (tail)
+ /* This catches cases like `int foo : 8;'. */
+ || DECL_MODE (tail) != TYPE_MODE (TREE_TYPE (tail)))
+ {
+ constructor_incremental = 0;
+ break;
+ }
+
+ check_init_type_bitfields (TREE_TYPE (tail));
+ }
+ }
+
+ else if (TREE_CODE (type) == ARRAY_TYPE)
+ check_init_type_bitfields (TREE_TYPE (type));
+}
+
+/* At the end of an implicit or explicit brace level,
+ finish up that level of constructor.
+ If we were outputting the elements as they are read, return 0
+ from inner levels (process_init_element ignores that),
+ but return error_mark_node from the outermost level
+ (that's what we want to put in DECL_INITIAL).
+ Otherwise, return a CONSTRUCTOR expression. */
+
+tree
+pop_init_level (implicit)
+ int implicit;
+{
+ struct constructor_stack *p;
+ int size = 0;
+ tree constructor = 0;
+
+ if (implicit == 0)
+ {
+ /* When we come to an explicit close brace,
+ pop any inner levels that didn't have explicit braces. */
+ while (constructor_stack->implicit)
+ process_init_element (pop_init_level (1));
+ }
+
+ p = constructor_stack;
+
+ if (constructor_type != 0)
+ size = int_size_in_bytes (constructor_type);
+
+ /* Now output all pending elements. */
+ output_pending_init_elements (1);
+
+#if 0 /* c-parse.in warns about {}. */
+ /* In ANSI, each brace level must have at least one element. */
+ if (! implicit && pedantic
+ && (TREE_CODE (constructor_type) == ARRAY_TYPE
+ ? integer_zerop (constructor_unfilled_index)
+ : constructor_unfilled_fields == TYPE_FIELDS (constructor_type)))
+ pedwarn_init ("empty braces in initializer%s", " for `%s'", NULL);
+#endif
+
+ /* Pad out the end of the structure. */
+
+ if (p->replacement_value)
+ {
+ /* If this closes a superfluous brace pair,
+ just pass out the element between them. */
+ constructor = p->replacement_value;
+ /* If this is the top level thing within the initializer,
+ and it's for a variable, then since we already called
+ assemble_variable, we must output the value now. */
+ if (p->next == 0 && constructor_decl != 0
+ && constructor_incremental)
+ {
+ constructor = digest_init (constructor_type, constructor,
+ 0, 0);
+
+ /* If initializing an array of unknown size,
+ determine the size now. */
+ if (TREE_CODE (constructor_type) == ARRAY_TYPE
+ && TYPE_DOMAIN (constructor_type) == 0)
+ {
+ int failure;
+ int momentary_p;
+
+ push_obstacks_nochange ();
+ if (TREE_PERMANENT (constructor_type))
+ end_temporary_allocation ();
+
+ momentary_p = suspend_momentary ();
+
+ /* We shouldn't have an incomplete array type within
+ some other type. */
+ if (constructor_stack->next)
+ abort ();
+
+ failure
+ = complete_array_type (constructor_type,
+ constructor, 0);
+ if (failure)
+ abort ();
+
+ size = int_size_in_bytes (constructor_type);
+ resume_momentary (momentary_p);
+ pop_obstacks ();
+ }
+
+ output_constant (constructor, size);
+ }
+ }
+ else if (constructor_type == 0)
+ ;
+ else if (TREE_CODE (constructor_type) != RECORD_TYPE
+ && TREE_CODE (constructor_type) != UNION_TYPE
+ && TREE_CODE (constructor_type) != ARRAY_TYPE
+ && ! constructor_incremental)
+ {
+ /* A nonincremental scalar initializer--just return
+ the element, after verifying there is just one. */
+ if (constructor_elements == 0)
+ {
+ error_init ("empty scalar initializer%s",
+ " for `%s'", NULL);
+ constructor = error_mark_node;
+ }
+ else if (TREE_CHAIN (constructor_elements) != 0)
+ {
+ error_init ("extra elements in scalar initializer%s",
+ " for `%s'", NULL);
+ constructor = TREE_VALUE (constructor_elements);
+ }
+ else
+ constructor = TREE_VALUE (constructor_elements);
+ }
+ else if (! constructor_incremental)
+ {
+ if (constructor_erroneous)
+ constructor = error_mark_node;
+ else
+ {
+ int momentary = suspend_momentary ();
+
+ constructor = build (CONSTRUCTOR, constructor_type, NULL_TREE,
+ nreverse (constructor_elements));
+ if (constructor_constant)
+ TREE_CONSTANT (constructor) = 1;
+ if (constructor_constant && constructor_simple)
+ TREE_STATIC (constructor) = 1;
+
+ resume_momentary (momentary);
+ }
+ }
+ else
+ {
+ tree filled;
+ int momentary = suspend_momentary ();
+
+ if (TREE_CODE (constructor_type) == RECORD_TYPE
+ || TREE_CODE (constructor_type) == UNION_TYPE)
+ {
+ /* Find the offset of the end of that field. */
+ filled = size_binop (CEIL_DIV_EXPR,
+ constructor_bit_index,
+ size_int (BITS_PER_UNIT));
+ }
+ else if (TREE_CODE (constructor_type) == ARRAY_TYPE)
+ {
+ /* If initializing an array of unknown size,
+ determine the size now. */
+ if (TREE_CODE (constructor_type) == ARRAY_TYPE
+ && TYPE_DOMAIN (constructor_type) == 0)
+ {
+ tree maxindex
+ = size_binop (MINUS_EXPR,
+ constructor_unfilled_index,
+ integer_one_node);
+
+ push_obstacks_nochange ();
+ if (TREE_PERMANENT (constructor_type))
+ end_temporary_allocation ();
+ maxindex = copy_node (maxindex);
+ TYPE_DOMAIN (constructor_type) = build_index_type (maxindex);
+ TREE_TYPE (maxindex) = TYPE_DOMAIN (constructor_type);
+
+ /* TYPE_MAX_VALUE is always one less than the number of elements
+ in the array, because we start counting at zero. Therefore,
+ warn only if the value is less than zero. */
+ if (pedantic
+ && (tree_int_cst_sgn (TYPE_MAX_VALUE (TYPE_DOMAIN (constructor_type)))
+ < 0))
+ error_with_decl (constructor_decl,
+ "zero or negative array size `%s'");
+ layout_type (constructor_type);
+ size = int_size_in_bytes (constructor_type);
+ pop_obstacks ();
+ }
+
+ filled = size_binop (MULT_EXPR, constructor_unfilled_index,
+ size_in_bytes (TREE_TYPE (constructor_type)));
+ }
+ else
+ filled = 0;
+
+ if (filled != 0)
+ assemble_zeros (size - TREE_INT_CST_LOW (filled));
+
+ resume_momentary (momentary);
+ }
+
+
+ constructor_type = p->type;
+ constructor_fields = p->fields;
+ constructor_index = p->index;
+ constructor_range_end = p->range_end;
+ constructor_max_index = p->max_index;
+ constructor_unfilled_index = p->unfilled_index;
+ constructor_unfilled_fields = p->unfilled_fields;
+ constructor_bit_index = p->bit_index;
+ constructor_elements = p->elements;
+ constructor_constant = p->constant;
+ constructor_simple = p->simple;
+ constructor_erroneous = p->erroneous;
+ constructor_pending_elts = p->pending_elts;
+ constructor_depth = p->depth;
+ constructor_incremental = p->incremental;
+ RESTORE_SPELLING_DEPTH (constructor_depth);
+
+ constructor_stack = p->next;
+ free (p);
+
+ if (constructor == 0)
+ {
+ if (constructor_stack == 0)
+ return error_mark_node;
+ return NULL_TREE;
+ }
+ return constructor;
+}
+
+/* Within an array initializer, specify the next index to be initialized.
+ FIRST is that index. If LAST is nonzero, then initialize a range
+ of indices, running from FIRST through LAST. */
+
+void
+set_init_index (first, last)
+ tree first, last;
+{
+ while ((TREE_CODE (first) == NOP_EXPR
+ || TREE_CODE (first) == CONVERT_EXPR
+ || TREE_CODE (first) == NON_LVALUE_EXPR)
+ && (TYPE_MODE (TREE_TYPE (first))
+ == TYPE_MODE (TREE_TYPE (TREE_OPERAND (first, 0)))))
+ (first) = TREE_OPERAND (first, 0);
+ if (last)
+ while ((TREE_CODE (last) == NOP_EXPR
+ || TREE_CODE (last) == CONVERT_EXPR
+ || TREE_CODE (last) == NON_LVALUE_EXPR)
+ && (TYPE_MODE (TREE_TYPE (last))
+ == TYPE_MODE (TREE_TYPE (TREE_OPERAND (last, 0)))))
+ (last) = TREE_OPERAND (last, 0);
+
+ if (TREE_CODE (first) != INTEGER_CST)
+ error_init ("nonconstant array index in initializer%s", " for `%s'", NULL);
+ else if (last != 0 && TREE_CODE (last) != INTEGER_CST)
+ error_init ("nonconstant array index in initializer%s", " for `%s'", NULL);
+ else if (tree_int_cst_lt (first, constructor_unfilled_index))
+ error_init ("duplicate array index in initializer%s", " for `%s'", NULL);
+ else
+ {
+ TREE_INT_CST_LOW (constructor_index)
+ = TREE_INT_CST_LOW (first);
+ TREE_INT_CST_HIGH (constructor_index)
+ = TREE_INT_CST_HIGH (first);
+
+ if (last != 0 && tree_int_cst_lt (last, first))
+ error_init ("empty index range in initializer%s", " for `%s'", NULL);
+ else
+ {
+ if (pedantic)
+ pedwarn ("ANSI C forbids specifying element to initialize");
+ constructor_range_end = last;
+ }
+ }
+}
+
+/* Within a struct initializer, specify the next field to be initialized. */
+
+void
+set_init_label (fieldname)
+ tree fieldname;
+{
+ tree tail;
+ int passed = 0;
+
+ for (tail = TYPE_FIELDS (constructor_type); tail;
+ tail = TREE_CHAIN (tail))
+ {
+ if (tail == constructor_unfilled_fields)
+ passed = 1;
+ if (DECL_NAME (tail) == fieldname)
+ break;
+ }
+
+ if (tail == 0)
+ error ("unknown field `%s' specified in initializer",
+ IDENTIFIER_POINTER (fieldname));
+ else if (!passed)
+ error ("field `%s' already initialized",
+ IDENTIFIER_POINTER (fieldname));
+ else
+ {
+ constructor_fields = tail;
+ if (pedantic)
+ pedwarn ("ANSI C forbids specifying structure member to initialize");
+ }
+}
+
+/* "Output" the next constructor element.
+ At top level, really output it to assembler code now.
+ Otherwise, collect it in a list from which we will make a CONSTRUCTOR.
+ TYPE is the data type that the containing data type wants here.
+ FIELD is the field (a FIELD_DECL) or the index that this element fills.
+
+ PENDING if non-nil means output pending elements that belong
+ right after this element. (PENDING is normally 1;
+ it is 0 while outputting pending elements, to avoid recursion.) */
+
+static void
+output_init_element (value, type, field, pending)
+ tree value, type, field;
+ int pending;
+{
+ int duplicate = 0;
+
+ if (TREE_CODE (TREE_TYPE (value)) == FUNCTION_TYPE
+ || (TREE_CODE (TREE_TYPE (value)) == ARRAY_TYPE
+ && !(TREE_CODE (value) == STRING_CST
+ && TREE_CODE (type) == ARRAY_TYPE
+ && TREE_CODE (TREE_TYPE (type)) == INTEGER_TYPE)
+ && !comptypes (TYPE_MAIN_VARIANT (TREE_TYPE (value)),
+ TYPE_MAIN_VARIANT (type))))
+ value = default_conversion (value);
+
+ if (value == error_mark_node)
+ constructor_erroneous = 1;
+ else if (!TREE_CONSTANT (value))
+ constructor_constant = 0;
+ else if (initializer_constant_valid_p (value, TREE_TYPE (value)) == 0)
+ constructor_simple = 0;
+
+ if (require_constant_value && ! TREE_CONSTANT (value))
+ {
+ error_init ("initializer element%s is not constant",
+ " for `%s'", NULL);
+ value = error_mark_node;
+ }
+ else if (require_constant_elements
+ && initializer_constant_valid_p (value, TREE_TYPE (value)) == 0)
+ {
+ error_init ("initializer element%s is not computable at load time",
+ " for `%s'", NULL);
+ value = error_mark_node;
+ }
+
+ /* If this element duplicates one on constructor_pending_elts,
+ print a message and ignore it. Don't do this when we're
+ processing elements taken off constructor_pending_elts,
+ because we'd always get spurious errors. */
+ if (pending)
+ {
+ if (TREE_CODE (constructor_type) == RECORD_TYPE
+ || TREE_CODE (constructor_type) == UNION_TYPE)
+ {
+ if (purpose_member (field, constructor_pending_elts))
+ {
+ error_init ("duplicate initializer%s", " for `%s'", NULL);
+ duplicate = 1;
+ }
+ }
+ if (TREE_CODE (constructor_type) == ARRAY_TYPE)
+ {
+ tree tail;
+ for (tail = constructor_pending_elts; tail;
+ tail = TREE_CHAIN (tail))
+ if (TREE_PURPOSE (tail) != 0
+ && TREE_CODE (TREE_PURPOSE (tail)) == INTEGER_CST
+ && tree_int_cst_equal (TREE_PURPOSE (tail), constructor_index))
+ break;
+
+ if (tail != 0)
+ {
+ error_init ("duplicate initializer%s", " for `%s'", NULL);
+ duplicate = 1;
+ }
+ }
+ }
+
+ /* If this element doesn't come next in sequence,
+ put it on constructor_pending_elts. */
+ if (TREE_CODE (constructor_type) == ARRAY_TYPE
+ && !tree_int_cst_equal (field, constructor_unfilled_index))
+ {
+ if (! duplicate)
+ /* The copy_node is needed in case field is actually
+ constructor_index, which is modified in place. */
+ constructor_pending_elts
+ = tree_cons (copy_node (field),
+ digest_init (type, value, 0, 0),
+ constructor_pending_elts);
+ }
+ else if (TREE_CODE (constructor_type) == RECORD_TYPE
+ && field != constructor_unfilled_fields)
+ {
+ /* We do this for records but not for unions. In a union,
+ no matter which field is specified, it can be initialized
+ right away since it starts at the beginning of the union. */
+ if (!duplicate)
+ constructor_pending_elts
+ = tree_cons (field,
+ digest_init (type, value, 0, 0),
+ constructor_pending_elts);
+ }
+ else
+ {
+ /* Otherwise, output this element either to
+ constructor_elements or to the assembler file. */
+
+ if (!duplicate)
+ {
+ if (! constructor_incremental)
+ {
+ if (field && TREE_CODE (field) == INTEGER_CST)
+ field = copy_node (field);
+ constructor_elements
+ = tree_cons (field, digest_init (type, value, 0, 0),
+ constructor_elements);
+ }
+ else
+ {
+ /* Structure elements may require alignment.
+ Do this, if necessary. */
+ if (TREE_CODE (constructor_type) == RECORD_TYPE)
+ {
+ /* Advance to offset of this element. */
+ if (! tree_int_cst_equal (constructor_bit_index,
+ DECL_FIELD_BITPOS (field)))
+ {
+ int next = (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field))
+ / BITS_PER_UNIT);
+ int here = (TREE_INT_CST_LOW (constructor_bit_index)
+ / BITS_PER_UNIT);
+
+ assemble_zeros (next - here);
+ }
+ }
+ output_constant (digest_init (type, value, 0, 0),
+ int_size_in_bytes (type));
+
+ /* For a record or union,
+ keep track of end position of last field. */
+ if (TREE_CODE (constructor_type) == RECORD_TYPE
+ || TREE_CODE (constructor_type) == UNION_TYPE)
+ {
+ tree temp = size_binop (PLUS_EXPR, DECL_FIELD_BITPOS (field),
+ DECL_SIZE (field));
+ TREE_INT_CST_LOW (constructor_bit_index)
+ = TREE_INT_CST_LOW (temp);
+ TREE_INT_CST_HIGH (constructor_bit_index)
+ = TREE_INT_CST_HIGH (temp);
+ }
+ }
+ }
+
+ /* Advance the variable that indicates sequential elements output. */
+ if (TREE_CODE (constructor_type) == ARRAY_TYPE)
+ {
+ tree tem = size_binop (PLUS_EXPR, constructor_unfilled_index,
+ integer_one_node);
+ TREE_INT_CST_LOW (constructor_unfilled_index)
+ = TREE_INT_CST_LOW (tem);
+ TREE_INT_CST_HIGH (constructor_unfilled_index)
+ = TREE_INT_CST_HIGH (tem);
+ }
+ else if (TREE_CODE (constructor_type) == RECORD_TYPE)
+ constructor_unfilled_fields = TREE_CHAIN (constructor_unfilled_fields);
+ else if (TREE_CODE (constructor_type) == UNION_TYPE)
+ constructor_unfilled_fields = 0;
+
+ /* Now output any pending elements which have become next. */
+ if (pending)
+ output_pending_init_elements (0);
+ }
+}
+
+/* Output any pending elements which have become next.
+ As we output elements, constructor_unfilled_{fields,index}
+ advances, which may cause other elements to become next;
+ if so, they too are output.
+
+ If ALL is 0, we return when there are
+ no more pending elements to output now.
+
+ If ALL is 1, we output space as necessary so that
+ we can output all the pending elements. */
+
+static void
+output_pending_init_elements (all)
+ int all;
+{
+ tree tail;
+ tree next;
+
+ retry:
+
+ /* Look thru the whole pending list.
+ If we find an element that should be output now,
+ output it. Otherwise, set NEXT to the element
+ that comes first among those still pending. */
+
+ next = 0;
+ for (tail = constructor_pending_elts; tail;
+ tail = TREE_CHAIN (tail))
+ {
+ if (TREE_CODE (constructor_type) == ARRAY_TYPE)
+ {
+ if (tree_int_cst_equal (TREE_PURPOSE (tail),
+ constructor_unfilled_index))
+ {
+ output_init_element (TREE_VALUE (tail),
+ TREE_TYPE (constructor_type),
+ constructor_unfilled_index, 0);
+ goto retry;
+ }
+ else if (tree_int_cst_lt (TREE_PURPOSE (tail),
+ constructor_unfilled_index))
+ ;
+ else if (next == 0
+ || tree_int_cst_lt (TREE_PURPOSE (tail), next))
+ next = TREE_PURPOSE (tail);
+ }
+ else if (TREE_CODE (constructor_type) == RECORD_TYPE
+ || TREE_CODE (constructor_type) == UNION_TYPE)
+ {
+ if (TREE_PURPOSE (tail) == constructor_unfilled_fields)
+ {
+ output_init_element (TREE_VALUE (tail),
+ TREE_TYPE (constructor_unfilled_fields),
+ constructor_unfilled_fields,
+ 0);
+ goto retry;
+ }
+ else if (constructor_unfilled_fields == 0
+ || tree_int_cst_lt (DECL_FIELD_BITPOS (TREE_PURPOSE (tail)),
+ DECL_FIELD_BITPOS (constructor_unfilled_fields)))
+ ;
+ else if (next == 0
+ || tree_int_cst_lt (DECL_FIELD_BITPOS (TREE_PURPOSE (tail)),
+ DECL_FIELD_BITPOS (next)))
+ next = TREE_PURPOSE (tail);
+ }
+ }
+
+ /* Ordinarily return, but not if we want to output all
+ and there are elements left. */
+ if (! (all && next != 0))
+ return;
+
+ /* Generate space up to the position of NEXT. */
+ if (constructor_incremental)
+ {
+ tree filled;
+ tree nextpos_tree = size_int (0);
+
+ if (TREE_CODE (constructor_type) == RECORD_TYPE
+ || TREE_CODE (constructor_type) == UNION_TYPE)
+ {
+ /* Find the last field written out, if any. */
+ for (tail = TYPE_FIELDS (constructor_type); tail;
+ tail = TREE_CHAIN (tail))
+ if (TREE_CHAIN (tail) == constructor_unfilled_fields)
+ break;
+
+ if (tail)
+ /* Find the offset of the end of that field. */
+ filled = size_binop (CEIL_DIV_EXPR,
+ size_binop (PLUS_EXPR,
+ DECL_FIELD_BITPOS (tail),
+ DECL_SIZE (tail)),
+ size_int (BITS_PER_UNIT));
+ else
+ filled = size_int (0);
+
+ nextpos_tree = size_binop (CEIL_DIV_EXPR,
+ DECL_FIELD_BITPOS (next),
+ size_int (BITS_PER_UNIT));
+
+ TREE_INT_CST_HIGH (constructor_bit_index)
+ = TREE_INT_CST_HIGH (DECL_FIELD_BITPOS (next));
+ TREE_INT_CST_LOW (constructor_bit_index)
+ = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (next));
+ constructor_unfilled_fields = next;
+ }
+ else if (TREE_CODE (constructor_type) == ARRAY_TYPE)
+ {
+ filled = size_binop (MULT_EXPR, constructor_unfilled_index,
+ size_in_bytes (TREE_TYPE (constructor_type)));
+ nextpos_tree
+ = size_binop (MULT_EXPR, next,
+ size_in_bytes (TREE_TYPE (constructor_type)));
+ TREE_INT_CST_LOW (constructor_unfilled_index)
+ = TREE_INT_CST_LOW (next);
+ TREE_INT_CST_HIGH (constructor_unfilled_index)
+ = TREE_INT_CST_HIGH (next);
+ }
+ else
+ filled = 0;
+
+ if (filled)
+ {
+ int nextpos = TREE_INT_CST_LOW (nextpos_tree);
+
+ assemble_zeros (nextpos - TREE_INT_CST_LOW (filled));
+ }
+ }
+ else
+ {
+ /* If it's not incremental, just skip over the gap,
+ so that after jumping to retry we will output the next
+ successive element. */
+ if (TREE_CODE (constructor_type) == RECORD_TYPE
+ || TREE_CODE (constructor_type) == UNION_TYPE)
+ constructor_unfilled_fields = next;
+ else if (TREE_CODE (constructor_type) == ARRAY_TYPE)
+ {
+ TREE_INT_CST_LOW (constructor_unfilled_index)
+ = TREE_INT_CST_LOW (next);
+ TREE_INT_CST_HIGH (constructor_unfilled_index)
+ = TREE_INT_CST_HIGH (next);
+ }
+ }
+
+ goto retry;
+}
+
+/* Add one non-braced element to the current constructor level.
+ This adjusts the current position within the constructor's type.
+ This may also start or terminate implicit levels
+ to handle a partly-braced initializer.
+
+ Once this has found the correct level for the new element,
+ it calls output_init_element.
+
+ Note: if we are incrementally outputting this constructor,
+ this function may be called with a null argument
+ representing a sub-constructor that was already incrementally output.
+ When that happens, we output nothing, but we do the bookkeeping
+ to skip past that element of the current constructor. */
+
+void
+process_init_element (value)
+ tree value;
+{
+ tree orig_value = value;
+ int string_flag = value != 0 && TREE_CODE (value) == STRING_CST;
+
+ /* Handle superfluous braces around string cst as in
+ char x[] = {"foo"}; */
+ if (string_flag
+ && constructor_type
+ && TREE_CODE (constructor_type) == ARRAY_TYPE
+ && TREE_CODE (TREE_TYPE (constructor_type)) == INTEGER_TYPE
+ && integer_zerop (constructor_unfilled_index))
+ {
+ constructor_stack->replacement_value = value;
+ return;
+ }
+
+ if (constructor_stack->replacement_value != 0)
+ {
+ error_init ("excess elements in struct initializer%s",
+ " after `%s'", NULL_PTR);
+ return;
+ }
+
+ /* Ignore elements of a brace group if it is entirely superfluous
+ and has already been diagnosed. */
+ if (constructor_type == 0)
+ return;
+
+ /* If we've exhausted any levels that didn't have braces,
+ pop them now. */
+ while (constructor_stack->implicit)
+ {
+ if ((TREE_CODE (constructor_type) == RECORD_TYPE
+ || TREE_CODE (constructor_type) == UNION_TYPE)
+ && constructor_fields == 0)
+ process_init_element (pop_init_level (1));
+ else if (TREE_CODE (constructor_type) == ARRAY_TYPE
+ && tree_int_cst_lt (constructor_max_index, constructor_index))
+ process_init_element (pop_init_level (1));
+ else
+ break;
+ }
+
+ while (1)
+ {
+ if (TREE_CODE (constructor_type) == RECORD_TYPE)
+ {
+ tree fieldtype;
+ enum tree_code fieldcode;
+
+ if (constructor_fields == 0)
+ {
+ pedwarn_init ("excess elements in struct initializer%s",
+ " after `%s'", NULL_PTR);
+ break;
+ }
+
+ fieldtype = TREE_TYPE (constructor_fields);
+ if (fieldtype != error_mark_node)
+ fieldtype = TYPE_MAIN_VARIANT (fieldtype);
+ fieldcode = TREE_CODE (fieldtype);
+
+ /* Accept a string constant to initialize a subarray. */
+ if (value != 0
+ && fieldcode == ARRAY_TYPE
+ && TREE_CODE (TREE_TYPE (fieldtype)) == INTEGER_TYPE
+ && string_flag)
+ value = orig_value;
+ /* Otherwise, if we have come to a subaggregate,
+ and we don't have an element of its type, push into it. */
+ else if (value != 0 && !constructor_no_implicit
+ && TYPE_MAIN_VARIANT (TREE_TYPE (value)) != fieldtype
+ && (fieldcode == RECORD_TYPE || fieldcode == ARRAY_TYPE
+ || fieldcode == UNION_TYPE))
+ {
+ push_init_level (1);
+ continue;
+ }
+
+ if (value)
+ {
+ push_member_name (constructor_fields);
+ output_init_element (value, fieldtype, constructor_fields, 1);
+ RESTORE_SPELLING_DEPTH (constructor_depth);
+ }
+ else
+ /* Do the bookkeeping for an element that was
+ directly output as a constructor. */
+ {
+ /* For a record, keep track of end position of last field. */
+ tree temp = size_binop (PLUS_EXPR,
+ DECL_FIELD_BITPOS (constructor_fields),
+ DECL_SIZE (constructor_fields));
+ TREE_INT_CST_LOW (constructor_bit_index)
+ = TREE_INT_CST_LOW (temp);
+ TREE_INT_CST_HIGH (constructor_bit_index)
+ = TREE_INT_CST_HIGH (temp);
+
+ constructor_unfilled_fields = TREE_CHAIN (constructor_fields);
+ }
+
+ constructor_fields = TREE_CHAIN (constructor_fields);
+ /* Skip any nameless bit fields atthe beginning. */
+ while (constructor_fields != 0 && DECL_BIT_FIELD (constructor_fields)
+ && DECL_NAME (constructor_fields) == 0)
+ constructor_fields = TREE_CHAIN (constructor_fields);
+ break;
+ }
+ if (TREE_CODE (constructor_type) == UNION_TYPE)
+ {
+ tree fieldtype;
+ enum tree_code fieldcode;
+
+ if (constructor_fields == 0)
+ {
+ pedwarn_init ("excess elements in union initializer%s",
+ " after `%s'", NULL_PTR);
+ break;
+ }
+
+ fieldtype = TREE_TYPE (constructor_fields);
+ if (fieldtype != error_mark_node)
+ fieldtype = TYPE_MAIN_VARIANT (fieldtype);
+ fieldcode = TREE_CODE (fieldtype);
+
+ /* Accept a string constant to initialize a subarray. */
+ if (value != 0
+ && fieldcode == ARRAY_TYPE
+ && TREE_CODE (TREE_TYPE (fieldtype)) == INTEGER_TYPE
+ && string_flag)
+ value = orig_value;
+ /* Otherwise, if we have come to a subaggregate,
+ and we don't have an element of its type, push into it. */
+ else if (value != 0 && !constructor_no_implicit
+ && TYPE_MAIN_VARIANT (TREE_TYPE (value)) != fieldtype
+ && (fieldcode == RECORD_TYPE || fieldcode == ARRAY_TYPE
+ || fieldcode == UNION_TYPE))
+ {
+ push_init_level (1);
+ continue;
+ }
+
+ if (value)
+ {
+ push_member_name (constructor_fields);
+ output_init_element (value, fieldtype, constructor_fields, 1);
+ RESTORE_SPELLING_DEPTH (constructor_depth);
+ }
+ else
+ /* Do the bookkeeping for an element that was
+ directly output as a constructor. */
+ {
+ TREE_INT_CST_LOW (constructor_bit_index)
+ = TREE_INT_CST_LOW (DECL_SIZE (constructor_fields));
+ TREE_INT_CST_HIGH (constructor_bit_index)
+ = TREE_INT_CST_HIGH (DECL_SIZE (constructor_fields));
+
+ constructor_unfilled_fields = TREE_CHAIN (constructor_fields);
+ }
+
+ constructor_fields = 0;
+ break;
+ }
+ if (TREE_CODE (constructor_type) == ARRAY_TYPE)
+ {
+ tree elttype = TYPE_MAIN_VARIANT (TREE_TYPE (constructor_type));
+ enum tree_code eltcode = TREE_CODE (elttype);
+
+ /* Accept a string constant to initialize a subarray. */
+ if (value != 0
+ && eltcode == ARRAY_TYPE
+ && TREE_CODE (TREE_TYPE (elttype)) == INTEGER_TYPE
+ && string_flag)
+ value = orig_value;
+ /* Otherwise, if we have come to a subaggregate,
+ and we don't have an element of its type, push into it. */
+ else if (value != 0 && !constructor_no_implicit
+ && TYPE_MAIN_VARIANT (TREE_TYPE (value)) != elttype
+ && (eltcode == RECORD_TYPE || eltcode == ARRAY_TYPE
+ || eltcode == UNION_TYPE))
+ {
+ push_init_level (1);
+ continue;
+ }
+
+ if (constructor_max_index != 0
+ && tree_int_cst_lt (constructor_max_index, constructor_index))
+ {
+ pedwarn_init ("excess elements in array initializer%s",
+ " after `%s'", NULL_PTR);
+ break;
+ }
+
+ /* Now output the actual element.
+ Ordinarily, output once.
+ If there is a range, repeat it till we advance past the range. */
+ do
+ {
+ tree tem;
+
+ if (value)
+ {
+ push_array_bounds (TREE_INT_CST_LOW (constructor_index));
+ output_init_element (value, elttype, constructor_index, 1);
+ RESTORE_SPELLING_DEPTH (constructor_depth);
+ }
+
+ tem = size_binop (PLUS_EXPR, constructor_index,
+ integer_one_node);
+ TREE_INT_CST_LOW (constructor_index)
+ = TREE_INT_CST_LOW (tem);
+ TREE_INT_CST_HIGH (constructor_index)
+ = TREE_INT_CST_HIGH (tem);
+
+ if (!value)
+ /* If we are doing the bookkeeping for an element that was
+ directly output as a constructor,
+ we must update constructor_unfilled_index. */
+ {
+ TREE_INT_CST_LOW (constructor_unfilled_index)
+ = TREE_INT_CST_LOW (constructor_index);
+ TREE_INT_CST_HIGH (constructor_unfilled_index)
+ = TREE_INT_CST_HIGH (constructor_index);
+ }
+ }
+ while (! (constructor_range_end == 0
+ || tree_int_cst_lt (constructor_range_end,
+ constructor_index)));
+
+ break;
+ }
+
+ /* Handle the sole element allowed in a braced initializer
+ for a scalar variable. */
+ if (constructor_fields == 0)
+ {
+ pedwarn_init ("excess elements in scalar initializer%s",
+ " after `%s'", NULL_PTR);
+ break;
+ }
+
+ if (value)
+ output_init_element (value, constructor_type, NULL_TREE, 1);
+ constructor_fields = 0;
+ break;
+ }
+
+ /* If the (lexically) previous elments are not now saved,
+ we can discard the storage for them. */
+ if (constructor_incremental && constructor_pending_elts == 0 && value != 0)
+ clear_momentary ();
+}
+
+/* Expand an ASM statement with operands, handling output operands
+ that are not variables or INDIRECT_REFS by transforming such
+ cases into cases that expand_asm_operands can handle.
+
+ Arguments are same as for expand_asm_operands. */
+
+void
+c_expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
+ tree string, outputs, inputs, clobbers;
+ int vol;
+ char *filename;
+ int line;
+{
+ int noutputs = list_length (outputs);
+ register int i;
+ /* o[I] is the place that output number I should be written. */
+ register tree *o = (tree *) alloca (noutputs * sizeof (tree));
+ register tree tail;
+
+ if (TREE_CODE (string) == ADDR_EXPR)
+ string = TREE_OPERAND (string, 0);
+ if (TREE_CODE (string) != STRING_CST)
+ {
+ error ("asm template is not a string constant");
+ return;
+ }
+
+ /* Record the contents of OUTPUTS before it is modified. */
+ for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
+ o[i] = TREE_VALUE (tail);
+
+ /* Perform default conversions on array and function inputs. */
+ /* Don't do this for other types--
+ it would screw up operands expected to be in memory. */
+ for (i = 0, tail = inputs; tail; tail = TREE_CHAIN (tail), i++)
+ if (TREE_CODE (TREE_TYPE (TREE_VALUE (tail))) == ARRAY_TYPE
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (tail))) == FUNCTION_TYPE)
+ TREE_VALUE (tail) = default_conversion (TREE_VALUE (tail));
+
+ /* Generate the ASM_OPERANDS insn;
+ store into the TREE_VALUEs of OUTPUTS some trees for
+ where the values were actually stored. */
+ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line);
+
+ /* Copy all the intermediate outputs into the specified outputs. */
+ for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
+ {
+ if (o[i] != TREE_VALUE (tail))
+ {
+ expand_expr (build_modify_expr (o[i], NOP_EXPR, TREE_VALUE (tail)),
+ 0, VOIDmode, 0);
+ free_temp_slots ();
+ }
+ /* Detect modification of read-only values.
+ (Otherwise done by build_modify_expr.) */
+ else
+ {
+ tree type = TREE_TYPE (o[i]);
+ if (TYPE_READONLY (type)
+ || ((TREE_CODE (type) == RECORD_TYPE
+ || TREE_CODE (type) == UNION_TYPE)
+ && C_TYPE_FIELDS_READONLY (type)))
+ readonly_warning (o[i], "modification by `asm'");
+ }
+ }
+
+ /* Those MODIFY_EXPRs could do autoincrements. */
+ emit_queue ();
+}
+
+/* Expand a C `return' statement.
+ RETVAL is the expression for what to return,
+ or a null pointer for `return;' with no value. */
+
+void
+c_expand_return (retval)
+ tree retval;
+{
+ tree valtype = TREE_TYPE (TREE_TYPE (current_function_decl));
+
+ if (TREE_THIS_VOLATILE (current_function_decl))
+ warning ("function declared `noreturn' has a `return' statement");
+
+ if (!retval)
+ {
+ current_function_returns_null = 1;
+ if (warn_return_type && valtype != 0 && TREE_CODE (valtype) != VOID_TYPE)
+ warning ("`return' with no value, in function returning non-void");
+ expand_null_return ();
+ }
+ else if (valtype == 0 || TREE_CODE (valtype) == VOID_TYPE)
+ {
+ current_function_returns_null = 1;
+ if (pedantic || TREE_CODE (TREE_TYPE (retval)) != VOID_TYPE)
+ pedwarn ("`return' with a value, in function returning void");
+ expand_return (retval);
+ }
+ else
+ {
+ tree t = convert_for_assignment (valtype, retval, "return",
+ NULL_TREE, NULL_TREE, 0);
+ tree res = DECL_RESULT (current_function_decl);
+ tree inner;
+
+ if (t == error_mark_node)
+ return;
+
+ inner = t = convert (TREE_TYPE (res), t);
+
+ /* Strip any conversions, additions, and subtractions, and see if
+ we are returning the address of a local variable. Warn if so. */
+ while (TREE_CODE (inner) == NOP_EXPR
+ || TREE_CODE (inner) == NON_LVALUE_EXPR
+ || TREE_CODE (inner) == CONVERT_EXPR
+ || TREE_CODE (inner) == PLUS_EXPR
+ || TREE_CODE (inner) == MINUS_EXPR)
+ inner = TREE_OPERAND (inner, 0);
+
+ if (TREE_CODE (inner) == ADDR_EXPR)
+ {
+ inner = TREE_OPERAND (inner, 0);
+
+ while (TREE_CODE_CLASS (TREE_CODE (inner)) == 'r')
+ inner = TREE_OPERAND (inner, 0);
+
+ if (TREE_CODE (inner) == VAR_DECL
+ && ! DECL_EXTERNAL (inner)
+ && ! TREE_STATIC (inner)
+ && DECL_CONTEXT (inner) == current_function_decl)
+ warning ("function returns address of local variable");
+ }
+
+ t = build (MODIFY_EXPR, TREE_TYPE (res), res, t);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_return (t);
+ current_function_returns_value = 1;
+ }
+}
+
+/* Start a C switch statement, testing expression EXP.
+ Return EXP if it is valid, an error node otherwise. */
+
+tree
+c_expand_start_case (exp)
+ tree exp;
+{
+ register enum tree_code code = TREE_CODE (TREE_TYPE (exp));
+ tree type = TREE_TYPE (exp);
+
+ if (code != INTEGER_TYPE && code != ENUMERAL_TYPE && code != ERROR_MARK)
+ {
+ error ("switch quantity not an integer");
+ exp = error_mark_node;
+ }
+ else
+ {
+ tree index;
+ type = TYPE_MAIN_VARIANT (TREE_TYPE (exp));
+
+ if (warn_traditional
+ && (type == long_integer_type_node
+ || type == long_unsigned_type_node))
+ pedwarn ("`long' switch expression not converted to `int' in ANSI C");
+
+ exp = default_conversion (exp);
+ type = TREE_TYPE (exp);
+ index = get_unwidened (exp, NULL_TREE);
+ /* We can't strip a conversion from a signed type to an unsigned,
+ because if we did, int_fits_type_p would do the wrong thing
+ when checking case values for being in range,
+ and it's too hard to do the right thing. */
+ if (TREE_UNSIGNED (TREE_TYPE (exp))
+ == TREE_UNSIGNED (TREE_TYPE (index)))
+ exp = index;
+ }
+
+ expand_start_case (1, exp, type, "switch statement");
+
+ return exp;
+}
diff --git a/gnu/usr.bin/cc/cc1plus/Makefile b/gnu/usr.bin/cc/cc1plus/Makefile
new file mode 100644
index 0000000..d41934b
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/Makefile
@@ -0,0 +1,12 @@
+#
+# $FreeBSD$
+#
+
+PROG = cc1plus
+SRCS = call.c class.c cvt.c decl.c decl2.c edsel.c errfn.c error.c except.c expr.c gc.c init.c lex.c method.c parse.c pt.c ptree.c search.c sig.c spew.c tree.c typeck.c typeck2.c xref.c
+BINDIR= /usr/libexec
+NOMAN= 1
+DPADD+= ${LIBCC_INT} ${LIBGNUMALLOC}
+LDADD+= -lcc_int -lgnumalloc
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/cc/cc1plus/call.c b/gnu/usr.bin/cc/cc1plus/call.c
new file mode 100644
index 0000000..3392a7a
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/call.c
@@ -0,0 +1,2909 @@
+/* Functions related to invoking methods and overloaded functions.
+ Copyright (C) 1987, 1992, 1993 Free Software Foundation, Inc.
+ Contributed by Michael Tiemann (tiemann@cygnus.com) and
+ hacked by Brendan Kehoe (brendan@cygnus.com).
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* High-level class interface. */
+
+#include "config.h"
+#include "tree.h"
+#include <stdio.h>
+#include "cp-tree.h"
+#include "class.h"
+#include "flags.h"
+
+#include "obstack.h"
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+extern void sorry ();
+
+extern int inhibit_warnings;
+extern int flag_assume_nonnull_objects;
+extern tree ctor_label, dtor_label;
+
+/* From typeck.c: */
+extern tree unary_complex_lvalue ();
+
+/* Compute the ease with which a conversion can be performed
+ between an expected and the given type. */
+static struct harshness_code convert_harshness ();
+
+#define EVIL_RETURN(ARG) ((ARG).code = EVIL_CODE, (ARG))
+#define QUAL_RETURN(ARG) ((ARG).code = QUAL_CODE, (ARG))
+#define TRIVIAL_RETURN(ARG) ((ARG).code = TRIVIAL_CODE, (ARG))
+#define ZERO_RETURN(ARG) ((ARG).code = 0, (ARG))
+
+/* Ordering function for overload resolution. Compare two candidates
+ by gross quality. */
+int
+rank_for_overload (x, y)
+ struct candidate *x, *y;
+{
+ if (y->h.code & (EVIL_CODE|ELLIPSIS_CODE|USER_CODE))
+ return y->h.code - x->h.code;
+ if (x->h.code & (EVIL_CODE|ELLIPSIS_CODE|USER_CODE))
+ return -1;
+
+ /* This is set by compute_conversion_costs, for calling a non-const
+ member function from a const member function. */
+ if ((y->harshness[0].code & CONST_CODE) ^ (x->harshness[0].code & CONST_CODE))
+ return y->harshness[0].code - x->harshness[0].code;
+
+ if (y->h.code & STD_CODE)
+ {
+ if (x->h.code & STD_CODE)
+ return y->h.distance - x->h.distance;
+ return 1;
+ }
+ if (x->h.code & STD_CODE)
+ return -1;
+
+ return y->h.code - x->h.code;
+}
+
+/* Compare two candidates, argument by argument. */
+int
+rank_for_ideal (x, y)
+ struct candidate *x, *y;
+{
+ int i;
+
+ if (x->h_len != y->h_len)
+ abort ();
+
+ for (i = 0; i < x->h_len; i++)
+ {
+ if (y->harshness[i].code - x->harshness[i].code)
+ return y->harshness[i].code - x->harshness[i].code;
+ if ((y->harshness[i].code & STD_CODE)
+ && (y->harshness[i].distance - x->harshness[i].distance))
+ return y->harshness[i].distance - x->harshness[i].distance;
+
+ /* They're both the same code. Now see if we're dealing with an
+ integral promotion that needs a finer grain of accuracy. */
+ if (y->harshness[0].code & PROMO_CODE
+ && (y->harshness[i].int_penalty ^ x->harshness[i].int_penalty))
+ return y->harshness[i].int_penalty - x->harshness[i].int_penalty;
+ }
+ return 0;
+}
+
+/* TYPE is the type we wish to convert to. PARM is the parameter
+ we have to work with. We use a somewhat arbitrary cost function
+ to measure this conversion. */
+static struct harshness_code
+convert_harshness (type, parmtype, parm)
+ register tree type, parmtype;
+ tree parm;
+{
+ struct harshness_code h;
+ register enum tree_code codel;
+ register enum tree_code coder;
+
+ h.code = 0;
+ h.distance = 0;
+ h.int_penalty = 0;
+
+#ifdef GATHER_STATISTICS
+ n_convert_harshness++;
+#endif
+
+ if (TYPE_PTRMEMFUNC_P (type))
+ type = TYPE_PTRMEMFUNC_FN_TYPE (type);
+ if (TYPE_PTRMEMFUNC_P (parmtype))
+ parmtype = TYPE_PTRMEMFUNC_FN_TYPE (parmtype);
+
+ if (TREE_CODE (parmtype) == REFERENCE_TYPE)
+ {
+ if (parm)
+ parm = convert_from_reference (parm);
+ parmtype = TREE_TYPE (parmtype);
+ }
+
+ codel = TREE_CODE (type);
+ coder = TREE_CODE (parmtype);
+
+ if (TYPE_MAIN_VARIANT (parmtype) == TYPE_MAIN_VARIANT (type))
+ return ZERO_RETURN (h);
+
+ if (coder == ERROR_MARK)
+ return EVIL_RETURN (h);
+
+ if (codel == POINTER_TYPE && fntype_p (parmtype))
+ {
+ tree p1, p2;
+ struct harshness_code h1, h2;
+
+ /* Get to the METHOD_TYPE or FUNCTION_TYPE that this might be. */
+ type = TREE_TYPE (type);
+
+ if (coder == POINTER_TYPE)
+ {
+ parmtype = TREE_TYPE (parmtype);
+ coder = TREE_CODE (parmtype);
+ }
+
+ if (coder != TREE_CODE (type))
+ return EVIL_RETURN (h);
+
+ /* We allow the default conversion between function type
+ and pointer-to-function type for free. */
+ if (type == parmtype)
+ return ZERO_RETURN (h);
+
+ /* Compare return types. */
+ p1 = TREE_TYPE (type);
+ p2 = TREE_TYPE (parmtype);
+ h2 = convert_harshness (p1, p2, NULL_TREE);
+ if (h2.code & EVIL_CODE)
+ return h2;
+
+ h1.code = TRIVIAL_CODE;
+ h1.distance = 0;
+
+ if (h2.distance != 0)
+ {
+ tree binfo;
+
+ /* This only works for pointers. */
+ if (TREE_CODE (p1) != POINTER_TYPE
+ && TREE_CODE (p1) != REFERENCE_TYPE)
+ return EVIL_RETURN (h);
+
+ p1 = TREE_TYPE (p1);
+ p2 = TREE_TYPE (p2);
+ /* Don't die if we happen to be dealing with void*. */
+ if (!IS_AGGR_TYPE (p1) || !IS_AGGR_TYPE (p2))
+ return EVIL_RETURN (h);
+ if (h2.distance < 0)
+ binfo = get_binfo (p2, p1, 0);
+ else
+ binfo = get_binfo (p1, p2, 0);
+
+ if (! BINFO_OFFSET_ZEROP (binfo))
+ {
+ static int explained = 0;
+ if (h2.distance < 0)
+ message_2_types (sorry, "cannot cast `%d' to `%d' at function call site", p2, p1);
+ else
+ message_2_types (sorry, "cannot cast `%d' to `%d' at function call site", p1, p2);
+
+ if (! explained++)
+ sorry ("(because pointer values change during conversion)");
+ return EVIL_RETURN (h);
+ }
+ }
+
+ h1.code |= h2.code;
+ if (h2.distance > h1.distance)
+ h1.distance = h2.distance;
+
+ p1 = TYPE_ARG_TYPES (type);
+ p2 = TYPE_ARG_TYPES (parmtype);
+ while (p1 && TREE_VALUE (p1) != void_type_node
+ && p2 && TREE_VALUE (p2) != void_type_node)
+ {
+ h2 = convert_harshness (TREE_VALUE (p1), TREE_VALUE (p2),
+ NULL_TREE);
+ if (h2.code & EVIL_CODE)
+ return h2;
+
+ if (h2.distance)
+ {
+ /* This only works for pointers and references. */
+ if (TREE_CODE (TREE_VALUE (p1)) != POINTER_TYPE
+ && TREE_CODE (TREE_VALUE (p1)) != REFERENCE_TYPE)
+ return EVIL_RETURN (h);
+ h2.distance = - h2.distance;
+ }
+
+ h1.code |= h2.code;
+ if (h2.distance > h1.distance)
+ h1.distance = h2.distance;
+ p1 = TREE_CHAIN (p1);
+ p2 = TREE_CHAIN (p2);
+ }
+ if (p1 == p2)
+ return h1;
+ if (p2)
+ {
+ if (p1)
+ return EVIL_RETURN (h);
+ h1.code |= ELLIPSIS_CODE;
+ return h1;
+ }
+ if (p1)
+ {
+ if (TREE_PURPOSE (p1) == NULL_TREE)
+ h1.code |= EVIL_CODE;
+ return h1;
+ }
+ }
+ else if (codel == POINTER_TYPE && coder == OFFSET_TYPE)
+ {
+ /* Get to the OFFSET_TYPE that this might be. */
+ type = TREE_TYPE (type);
+
+ if (coder != TREE_CODE (type))
+ return EVIL_RETURN (h);
+
+ if (TYPE_OFFSET_BASETYPE (type) == TYPE_OFFSET_BASETYPE (parmtype))
+ h.code = 0;
+ else if (UNIQUELY_DERIVED_FROM_P (TYPE_OFFSET_BASETYPE (type),
+ TYPE_OFFSET_BASETYPE (parmtype)))
+ {
+ h.code = STD_CODE;
+ h.distance = 1;
+ }
+ else if (UNIQUELY_DERIVED_FROM_P (TYPE_OFFSET_BASETYPE (parmtype),
+ TYPE_OFFSET_BASETYPE (type)))
+ {
+ h.code = STD_CODE;
+ h.distance = -1;
+ }
+ else
+ return EVIL_RETURN (h);
+ /* Now test the OFFSET_TYPE's target compatibility. */
+ type = TREE_TYPE (type);
+ parmtype = TREE_TYPE (parmtype);
+ }
+
+ if (coder == UNKNOWN_TYPE)
+ {
+ if (codel == FUNCTION_TYPE
+ || codel == METHOD_TYPE
+ || (codel == POINTER_TYPE
+ && (TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE
+ || TREE_CODE (TREE_TYPE (type)) == METHOD_TYPE)))
+ return TRIVIAL_RETURN (h);
+ return EVIL_RETURN (h);
+ }
+
+ if (coder == VOID_TYPE)
+ return EVIL_RETURN (h);
+
+ if (INTEGRAL_CODE_P (codel))
+ {
+ /* Control equivalence of ints an enums. */
+
+ if (codel == ENUMERAL_TYPE
+ && flag_int_enum_equivalence == 0)
+ {
+ /* Enums can be converted to ints, but not vice-versa. */
+ if (coder != ENUMERAL_TYPE
+ || TYPE_MAIN_VARIANT (type) != TYPE_MAIN_VARIANT (parmtype))
+ return EVIL_RETURN (h);
+ }
+
+ /* else enums and ints (almost) freely interconvert. */
+
+ if (INTEGRAL_CODE_P (coder))
+ {
+ if (TYPE_MAIN_VARIANT (type)
+ == TYPE_MAIN_VARIANT (type_promotes_to (parmtype)))
+ {
+ h.code = PROMO_CODE;
+#if 0 /* What purpose does this serve? -jason */
+ /* A char, short, wchar_t, etc., should promote to an int if
+ it can handle it, otherwise to an unsigned. So we'll make
+ an unsigned. */
+ if (type != integer_type_node)
+ h.int_penalty = 1;
+#endif
+ }
+ else
+ h.code = STD_CODE;
+
+ return h;
+ }
+ else if (coder == REAL_TYPE)
+ {
+ h.code = STD_CODE;
+ h.distance = 0;
+ return h;
+ }
+ }
+
+ if (codel == REAL_TYPE)
+ {
+ if (coder == REAL_TYPE)
+ {
+ if (TYPE_MAIN_VARIANT (type)
+ == TYPE_MAIN_VARIANT (type_promotes_to (parmtype)))
+ h.code = PROMO_CODE;
+ else
+ h.code = STD_CODE;
+
+ return h;
+ }
+ else if (INTEGRAL_CODE_P (coder))
+ {
+ h.code = STD_CODE;
+ h.distance = 0;
+ return h;
+ }
+ }
+
+ /* Convert arrays which have not previously been converted. */
+ if (codel == ARRAY_TYPE)
+ codel = POINTER_TYPE;
+ if (coder == ARRAY_TYPE)
+ coder = POINTER_TYPE;
+
+ /* Conversions among pointers */
+ if (codel == POINTER_TYPE && coder == POINTER_TYPE)
+ {
+ register tree ttl = TYPE_MAIN_VARIANT (TREE_TYPE (type));
+ register tree ttr = TYPE_MAIN_VARIANT (TREE_TYPE (parmtype));
+ int penalty = 4 * (ttl != ttr);
+
+ /* Anything converts to void *. void * converts to anything.
+ Since these may be `const void *' (etc.) use VOID_TYPE
+ instead of void_type_node. Otherwise, the targets must be the same,
+ except that we do allow (at some cost) conversion between signed and
+ unsigned pointer types. */
+
+ if ((TREE_CODE (ttl) == METHOD_TYPE
+ || TREE_CODE (ttl) == FUNCTION_TYPE)
+ && TREE_CODE (ttl) == TREE_CODE (ttr))
+ {
+ if (comptypes (ttl, ttr, -1))
+ {
+ h.code = penalty ? STD_CODE : 0;
+ h.distance = 0;
+ }
+ else
+ h.code = EVIL_CODE;
+ return h;
+ }
+
+#if 1
+ if (TREE_CODE (ttl) != VOID_TYPE && TREE_CODE (ttr) != VOID_TYPE)
+ {
+ if (TREE_UNSIGNED (ttl) != TREE_UNSIGNED (ttr))
+ {
+ ttl = unsigned_type (ttl);
+ ttr = unsigned_type (ttr);
+ penalty = 10;
+ }
+ if (! comp_target_types (ttl, ttr, 0))
+ return EVIL_RETURN (h);
+ }
+#else
+ if (!(TREE_CODE (ttl) == VOID_TYPE
+ || TREE_CODE (ttr) == VOID_TYPE
+ || (TREE_UNSIGNED (ttl) ^ TREE_UNSIGNED (ttr)
+ && (ttl = unsigned_type (ttl),
+ ttr = unsigned_type (ttr),
+ penalty = 10, 0))
+ || (comp_target_types (ttl, ttr, 0))))
+ return EVIL_RETURN (h);
+#endif
+
+ if (penalty == 10 || ttr == ttl)
+ {
+ tree tmp1 = TREE_TYPE (type), tmp2 = TREE_TYPE (parmtype);
+
+ /* If one was unsigned but the other wasn't, then we need to
+ do a standard conversion from T to unsigned T. */
+ if (penalty == 10)
+ h.code = PROMO_CODE; /* was STD_CODE */
+ else
+ h.code = 0;
+
+ /* Note conversion from `T*' to `const T*',
+ or `T*' to `volatile T*'. */
+ if (ttl == ttr
+ && ((TYPE_READONLY (tmp1) != TREE_READONLY (tmp2))
+ || (TYPE_VOLATILE (tmp1) != TYPE_VOLATILE (tmp2))))
+ h.code |= QUAL_CODE;
+
+ h.distance = 0;
+ return h;
+ }
+
+
+ if (TREE_CODE (ttl) == RECORD_TYPE && TREE_CODE (ttr) == RECORD_TYPE)
+ {
+ int b_or_d = get_base_distance (ttl, ttr, 0, 0);
+ if (b_or_d < 0)
+ {
+ b_or_d = get_base_distance (ttr, ttl, 0, 0);
+ if (b_or_d < 0)
+ return EVIL_RETURN (h);
+ h.distance = -b_or_d;
+ }
+ else
+ h.distance = b_or_d;
+ h.code = STD_CODE;
+ return h;
+ }
+
+ /* If converting from a `class*' to a `void*', make it
+ less favorable than any inheritance relationship. */
+ if (TREE_CODE (ttl) == VOID_TYPE && IS_AGGR_TYPE (ttr))
+ {
+ h.code = STD_CODE;
+ h.distance = CLASSTYPE_MAX_DEPTH (ttr)+1;
+ return h;
+ }
+ h.code = penalty ? STD_CODE : PROMO_CODE;
+ return h;
+ }
+
+ if (codel == POINTER_TYPE && coder == INTEGER_TYPE)
+ {
+ /* This is not a bad match, but don't let it beat
+ integer-enum combinations. */
+ if (parm && integer_zerop (parm))
+ {
+ h.code = STD_CODE;
+ h.distance = 0;
+ return h;
+ }
+ }
+
+ /* C++: Since the `this' parameter of a signature member function
+ is represented as a signature pointer to handle default implementations
+ correctly, we can have the case that `type' is a signature pointer
+ while `parmtype' is a pointer to a signature table. We don't really
+ do any conversions in this case, so just return 0. */
+
+ if (codel == RECORD_TYPE && coder == POINTER_TYPE
+ && IS_SIGNATURE_POINTER (type) && IS_SIGNATURE (TREE_TYPE (parmtype)))
+ return ZERO_RETURN (h);
+
+ if (codel == REFERENCE_TYPE)
+ {
+ tree ttl, ttr;
+ int constp = parm ? TREE_READONLY (parm) : TYPE_READONLY (parmtype);
+ int volatilep = (parm ? TREE_THIS_VOLATILE (parm)
+ : TYPE_VOLATILE (parmtype));
+ register tree intype = TYPE_MAIN_VARIANT (parmtype);
+ register enum tree_code form = TREE_CODE (intype);
+ int penalty = 0;
+
+ ttl = TREE_TYPE (type);
+
+ /* When passing a non-const argument into a const reference (or vice
+ versa), dig it a little, so a non-const reference is preferred
+ over this one. (mrs) */
+ if (TYPE_READONLY (ttl) != constp
+ || TYPE_VOLATILE (ttl) != volatilep)
+ penalty = 2;
+ else
+ penalty = 0;
+
+ ttl = TYPE_MAIN_VARIANT (ttl);
+
+ if (form == OFFSET_TYPE)
+ {
+ intype = TREE_TYPE (intype);
+ form = TREE_CODE (intype);
+ }
+
+ if (ttl == intype && penalty == 0)
+ return ZERO_RETURN (h);
+ else
+ penalty = 2;
+
+ if (TREE_UNSIGNED (ttl) ^ TREE_UNSIGNED (intype))
+ {
+ ttl = unsigned_type (ttl);
+ intype = unsigned_type (intype);
+ penalty += 2;
+ }
+
+ ttr = intype;
+
+ /* If the initializer is not an lvalue, then it does not
+ matter if we make life easier for the programmer
+ by creating a temporary variable with which to
+ hold the result. */
+ if (parm && (INTEGRAL_CODE_P (coder)
+ || coder == REAL_TYPE)
+ && ! lvalue_p (parm))
+ {
+ h = convert_harshness (ttl, ttr, NULL_TREE);
+ if (penalty > 2 || h.code != 0)
+ h.code |= STD_CODE;
+ else
+ h.code |= TRIVIAL_CODE;
+ h.distance = 0;
+ return h;
+ }
+
+ if (ttl == ttr)
+ {
+ if (penalty > 2)
+ {
+ h.code = STD_CODE;
+ h.distance = 0;
+ }
+ else
+ {
+ h.code = TRIVIAL_CODE;
+ /* We set this here so that build_overload_call_real will be
+ able to see the penalty we found, rather than just looking
+ at a TRIVIAL_CODE with no other information. */
+ h.int_penalty = penalty;
+ }
+ return h;
+ }
+
+ /* Pointers to voids always convert for pointers. But
+ make them less natural than more specific matches. */
+ if (TREE_CODE (ttl) == POINTER_TYPE && TREE_CODE (ttr) == POINTER_TYPE)
+ {
+ if (TREE_TYPE (ttl) == void_type_node
+ || TREE_TYPE (ttr) == void_type_node)
+ {
+ h.code = STD_CODE;
+ h.distance = 0;
+ return h;
+ }
+ }
+
+ /* Here it does matter. If this conversion is from derived to base,
+ allow it. Otherwise, types must be compatible in the strong sense. */
+ if (TREE_CODE (ttl) == RECORD_TYPE && TREE_CODE (ttr) == RECORD_TYPE)
+ {
+ int b_or_d = get_base_distance (ttl, ttr, 0, 0);
+ if (b_or_d < 0)
+ {
+ b_or_d = get_base_distance (ttr, ttl, 0, 0);
+ if (b_or_d < 0)
+ return EVIL_RETURN (h);
+ h.distance = -b_or_d;
+ }
+ /* Say that this conversion is relatively painless.
+ If it turns out that there is a user-defined X(X&)
+ constructor, then that will be invoked, but that's
+ preferable to dealing with other user-defined conversions
+ that may produce surprising results. */
+ else
+ h.distance = b_or_d;
+ h.code = STD_CODE;
+ return h;
+ }
+
+ if (comp_target_types (ttl, intype, 1))
+ {
+ if (penalty)
+ h.code = STD_CODE;
+ h.distance = 0;
+ return h;
+ }
+ }
+ if (codel == RECORD_TYPE && coder == RECORD_TYPE)
+ {
+ int b_or_d = get_base_distance (type, parmtype, 0, 0);
+ if (b_or_d < 0)
+ {
+ b_or_d = get_base_distance (parmtype, type, 0, 0);
+ if (b_or_d < 0)
+ return EVIL_RETURN (h);
+ h.distance = -b_or_d;
+ }
+ else
+ h.distance = b_or_d;
+ h.code = STD_CODE;
+ return h;
+ }
+ return EVIL_RETURN (h);
+}
+
+#ifdef DEBUG_MATCHING
+static char *
+print_harshness (h)
+ struct harshness_code *h;
+{
+ static char buf[1024];
+ char tmp[1024];
+
+ bzero (buf, 1024 * sizeof (char));
+ strcat (buf, "codes=[");
+ if (h->code & EVIL_CODE)
+ strcat (buf, "EVIL");
+ if (h->code & CONST_CODE)
+ strcat (buf, " CONST");
+ if (h->code & ELLIPSIS_CODE)
+ strcat (buf, " ELLIPSIS");
+ if (h->code & USER_CODE)
+ strcat (buf, " USER");
+ if (h->code & STD_CODE)
+ strcat (buf, " STD");
+ if (h->code & PROMO_CODE)
+ strcat (buf, " PROMO");
+ if (h->code & QUAL_CODE)
+ strcat (buf, " QUAL");
+ if (h->code & TRIVIAL_CODE)
+ strcat (buf, " TRIVIAL");
+ if (buf[0] == '\0')
+ strcat (buf, "0");
+
+ sprintf (tmp, "] distance=%d int_penalty=%d", h->distance, h->int_penalty);
+
+ strcat (buf, tmp);
+
+ return buf;
+}
+#endif
+
+/* Algorithm: For each argument, calculate how difficult it is to
+ make FUNCTION accept that argument. If we can easily tell that
+ FUNCTION won't be acceptable to one of the arguments, then we
+ don't need to compute the ease of converting the other arguments,
+ since it will never show up in the intersection of all arguments'
+ favorite functions.
+
+ Conversions between builtin and user-defined types are allowed, but
+ no function involving such a conversion is preferred to one which
+ does not require such a conversion. Furthermore, such conversions
+ must be unique. */
+
+void
+compute_conversion_costs (function, tta_in, cp, arglen)
+ tree function;
+ tree tta_in;
+ struct candidate *cp;
+ int arglen;
+{
+ tree ttf_in = TYPE_ARG_TYPES (TREE_TYPE (function));
+ tree ttf = ttf_in;
+ tree tta = tta_in;
+
+ /* Start out with no strikes against. */
+ int evil_strikes = 0;
+ int ellipsis_strikes = 0;
+ int user_strikes = 0;
+ int b_or_d_strikes = 0;
+ int easy_strikes = 0;
+
+ int strike_index = 0, win;
+ struct harshness_code lose;
+
+#ifdef GATHER_STATISTICS
+ n_compute_conversion_costs++;
+#endif
+
+ cp->function = function;
+ cp->arg = tta ? TREE_VALUE (tta) : NULL_TREE;
+ cp->u.bad_arg = 0; /* optimistic! */
+
+ cp->h.code = 0;
+ cp->h.distance = 0;
+ cp->h.int_penalty = 0;
+ bzero (cp->harshness,
+ (cp->h_len + 1) * sizeof (struct harshness_code));
+
+ while (ttf && tta)
+ {
+ struct harshness_code h;
+
+ if (ttf == void_list_node)
+ break;
+
+ if (type_unknown_p (TREE_VALUE (tta)))
+ {
+ /* Must perform some instantiation here. */
+ tree rhs = TREE_VALUE (tta);
+ tree lhstype = TREE_VALUE (ttf);
+
+ /* Keep quiet about possible contravariance violations. */
+ int old_inhibit_warnings = inhibit_warnings;
+ inhibit_warnings = 1;
+
+ /* @@ This is to undo what `grokdeclarator' does to
+ parameter types. It really should go through
+ something more general. */
+
+ TREE_TYPE (tta) = unknown_type_node;
+ rhs = instantiate_type (lhstype, rhs, 0);
+ inhibit_warnings = old_inhibit_warnings;
+
+ if (TREE_CODE (rhs) == ERROR_MARK)
+ h.code = EVIL_CODE;
+ else
+ h = convert_harshness (lhstype, TREE_TYPE (rhs), rhs);
+ }
+ else
+ {
+#ifdef DEBUG_MATCHING
+ static tree old_function = NULL_TREE;
+
+ if (!old_function || function != old_function)
+ {
+ cp_error ("trying %D", function);
+ old_function = function;
+ }
+
+ cp_error (" doing (%T) %E against arg %T",
+ TREE_TYPE (TREE_VALUE (tta)), TREE_VALUE (tta),
+ TREE_VALUE (ttf));
+#endif
+
+ h = convert_harshness (TREE_VALUE (ttf),
+ TREE_TYPE (TREE_VALUE (tta)),
+ TREE_VALUE (tta));
+
+#ifdef DEBUG_MATCHING
+ cp_error (" evaluated %s", print_harshness (&h));
+#endif
+ }
+
+ cp->harshness[strike_index] = h;
+ if ((h.code & EVIL_CODE)
+ || ((h.code & STD_CODE) && h.distance < 0))
+ {
+ cp->u.bad_arg = strike_index;
+ evil_strikes = 1;
+ }
+ else if (h.code & ELLIPSIS_CODE)
+ ellipsis_strikes += 1;
+#if 0
+ /* This is never set by `convert_harshness'. */
+ else if (h.code & USER_CODE)
+ {
+ user_strikes += 1;
+ }
+#endif
+ else
+ {
+ if ((h.code & STD_CODE) && h.distance)
+ {
+ if (h.distance > b_or_d_strikes)
+ b_or_d_strikes = h.distance;
+ }
+ else
+ easy_strikes += (h.code & (STD_CODE|PROMO_CODE|TRIVIAL_CODE));
+ cp->h.code |= h.code;
+ /* Make sure we communicate this. */
+ cp->h.int_penalty += h.int_penalty;
+ }
+
+ ttf = TREE_CHAIN (ttf);
+ tta = TREE_CHAIN (tta);
+ strike_index += 1;
+ }
+
+ if (tta)
+ {
+ /* ran out of formals, and parmlist is fixed size. */
+ if (ttf /* == void_type_node */)
+ {
+ cp->h.code = EVIL_CODE;
+ cp->u.bad_arg = -1;
+ return;
+ }
+ else
+ {
+ struct harshness_code h;
+ int l = list_length (tta);
+ ellipsis_strikes += l;
+ h.code = ELLIPSIS_CODE;
+ h.distance = 0;
+ h.int_penalty = 0;
+ for (; l; --l)
+ cp->harshness[strike_index++] = h;
+ }
+ }
+ else if (ttf && ttf != void_list_node)
+ {
+ /* ran out of actuals, and no defaults. */
+ if (TREE_PURPOSE (ttf) == NULL_TREE)
+ {
+ cp->h.code = EVIL_CODE;
+ cp->u.bad_arg = -2;
+ return;
+ }
+ /* Store index of first default. */
+ cp->harshness[arglen].distance = strike_index+1;
+ }
+ else
+ cp->harshness[arglen].distance = 0;
+
+ /* Argument list lengths work out, so don't need to check them again. */
+ if (evil_strikes)
+ {
+ /* We do not check for derived->base conversions here, since in
+ no case would they give evil strike counts, unless such conversions
+ are somehow ambiguous. */
+
+ /* See if any user-defined conversions apply.
+ But make sure that we do not loop. */
+ static int dont_convert_types = 0;
+
+ if (dont_convert_types)
+ {
+ cp->h.code = EVIL_CODE;
+ return;
+ }
+
+ win = 0; /* Only get one chance to win. */
+ ttf = TYPE_ARG_TYPES (TREE_TYPE (function));
+ tta = tta_in;
+ strike_index = 0;
+ evil_strikes = 0;
+
+ while (ttf && tta)
+ {
+ if (ttf == void_list_node)
+ break;
+
+ lose = cp->harshness[strike_index];
+ if ((lose.code & EVIL_CODE)
+ || ((lose.code & STD_CODE) && lose.distance < 0))
+ {
+ tree actual_type = TREE_TYPE (TREE_VALUE (tta));
+ tree formal_type = TREE_VALUE (ttf);
+ int extra_conversions = 0;
+
+ dont_convert_types = 1;
+
+ if (TREE_CODE (formal_type) == REFERENCE_TYPE)
+ formal_type = TREE_TYPE (formal_type);
+ if (TREE_CODE (actual_type) == REFERENCE_TYPE)
+ actual_type = TREE_TYPE (actual_type);
+
+ if (formal_type != error_mark_node
+ && actual_type != error_mark_node)
+ {
+ formal_type = TYPE_MAIN_VARIANT (formal_type);
+ actual_type = TYPE_MAIN_VARIANT (actual_type);
+
+ if (TYPE_HAS_CONSTRUCTOR (formal_type))
+ {
+ /* If it has a constructor for this type,
+ try to use it. */
+ /* @@ There is no way to save this result yet, so
+ success is a NULL_TREE for now. */
+ if (convert_to_aggr (formal_type, TREE_VALUE (tta), 0, 1)
+ != error_mark_node)
+ win++;
+ }
+ if (TYPE_LANG_SPECIFIC (actual_type)
+ && TYPE_HAS_CONVERSION (actual_type))
+ {
+ tree conv;
+ /* Don't issue warnings since we're only groping
+ around for the right answer, we haven't yet
+ committed to going with this solution. */
+ int old_inhibit_warnings = inhibit_warnings;
+
+ inhibit_warnings = 1;
+ conv = build_type_conversion
+ (CALL_EXPR, TREE_VALUE (ttf), TREE_VALUE (tta), 0);
+ inhibit_warnings = old_inhibit_warnings;
+
+ if (conv)
+ {
+ if (conv == error_mark_node)
+ win += 2;
+ else
+ {
+ win++;
+ if (TREE_CODE (conv) != CALL_EXPR)
+ extra_conversions = 1;
+ }
+ }
+ else if (TREE_CODE (TREE_VALUE (ttf)) == REFERENCE_TYPE)
+ {
+ conv = build_type_conversion (CALL_EXPR, formal_type,
+ TREE_VALUE (tta), 0);
+ if (conv)
+ {
+ if (conv == error_mark_node)
+ win += 2;
+ else
+ {
+ win++;
+ if (TREE_CODE (conv) != CALL_EXPR)
+ extra_conversions = 1;
+ }
+ }
+ }
+ }
+ }
+ dont_convert_types = 0;
+
+ if (win == 1)
+ {
+ user_strikes += 1;
+ cp->harshness[strike_index].code
+ = USER_CODE | (extra_conversions ? STD_CODE : 0);
+ win = 0;
+ }
+ else
+ {
+ if (cp->u.bad_arg > strike_index)
+ cp->u.bad_arg = strike_index;
+
+ evil_strikes = win ? 2 : 1;
+ break;
+ }
+ }
+
+ ttf = TREE_CHAIN (ttf);
+ tta = TREE_CHAIN (tta);
+ strike_index += 1;
+ }
+ }
+
+ /* Const member functions get a small penalty because defaulting
+ to const is less useful than defaulting to non-const. */
+ /* This is bogus, it does not correspond to anything in the ARM.
+ This code will be fixed when this entire section is rewritten
+ to conform to the ARM. (mrs) */
+ if (TREE_CODE (TREE_TYPE (function)) == METHOD_TYPE)
+ {
+ tree this_parm = TREE_VALUE (ttf_in);
+
+ if (TREE_CODE (this_parm) == RECORD_TYPE /* Is `this' a sig ptr? */
+ ? TYPE_READONLY (TREE_TYPE (TREE_TYPE (TYPE_FIELDS (this_parm))))
+ : TYPE_READONLY (TREE_TYPE (this_parm)))
+ {
+ cp->harshness[0].code |= TRIVIAL_CODE;
+ ++easy_strikes;
+ }
+ else
+ {
+ /* Calling a non-const member function from a const member function
+ is probably invalid, but for now we let it only draw a warning.
+ We indicate that such a mismatch has occurred by setting the
+ harshness to a maximum value. */
+ if (TREE_CODE (TREE_TYPE (TREE_VALUE (tta_in))) == POINTER_TYPE
+ && (TYPE_READONLY (TREE_TYPE (TREE_TYPE (TREE_VALUE (tta_in))))))
+ cp->harshness[0].code |= CONST_CODE;
+ }
+ }
+
+ if (evil_strikes)
+ cp->h.code = EVIL_CODE;
+ if (ellipsis_strikes)
+ cp->h.code |= ELLIPSIS_CODE;
+ if (user_strikes)
+ cp->h.code |= USER_CODE;
+#ifdef DEBUG_MATCHING
+ cp_error ("final eval %s", print_harshness (&cp->h));
+#endif
+}
+
+/* Subroutine of ideal_candidate. See if X or Y is a better match
+ than the other. */
+static int
+strictly_better (x, y)
+ unsigned short x, y;
+{
+ unsigned short xor;
+
+ if (x == y)
+ return 0;
+
+ xor = x ^ y;
+ if (xor >= x || xor >= y)
+ return 1;
+ return 0;
+}
+
+/* When one of several possible overloaded functions and/or methods
+ can be called, choose the best candidate for overloading.
+
+ BASETYPE is the context from which we start method resolution
+ or NULL if we are comparing overloaded functions.
+ CANDIDATES is the array of candidates we have to choose from.
+ N_CANDIDATES is the length of CANDIDATES.
+ PARMS is a TREE_LIST of parameters to the function we'll ultimately
+ choose. It is modified in place when resolving methods. It is not
+ modified in place when resolving overloaded functions.
+ LEN is the length of the parameter list. */
+
+static struct candidate *
+ideal_candidate (basetype, candidates, n_candidates, parms, len)
+ tree basetype;
+ struct candidate *candidates;
+ int n_candidates;
+ tree parms;
+ int len;
+{
+ struct candidate *cp = candidates+n_candidates;
+ int i, j = -1, best_code;
+
+ /* For each argument, sort the functions from best to worst for the arg.
+ For each function that's not best for this arg, set its overall
+ harshness to EVIL so that other args won't like it. The candidate
+ list for the last argument is the intersection of all the best-liked
+ functions. */
+
+#if 0
+ for (i = 0; i < len; i++)
+ {
+ qsort (candidates, n_candidates, sizeof (struct candidate),
+ rank_for_overload);
+ best_code = cp[-1].h.code;
+
+ /* To find out functions that are worse than that represented
+ by BEST_CODE, we can't just do a comparison like h.code>best_code.
+ The total harshness for the "best" fn may be 8|8 for two args, and
+ the harshness for the next-best may be 8|2. If we just compared,
+ that would be checking 8>10, which would lead to the next-best
+ being disqualified. What we actually want to do is get rid
+ of functions that are definitely worse than that represented
+ by best_code, i.e. those which have bits set higher than the
+ highest in best_code. Sooooo, what we do is clear out everything
+ represented by best_code, and see if we still come up with something
+ higher. If so (e.g., 8|8 vs 8|16), it'll disqualify it properly. */
+ for (j = n_candidates-2; j >= 0; j--)
+ if ((candidates[j].h.code & ~best_code) > best_code)
+ candidates[j].h.code = EVIL_CODE;
+ }
+
+ if (cp[-1].h.code & EVIL_CODE)
+ return NULL;
+#else
+ qsort (candidates, n_candidates, sizeof (struct candidate),
+ rank_for_overload);
+ best_code = cp[-1].h.code;
+#endif
+
+ /* If they're at least as good as each other, do an arg-by-arg check. */
+ if (! strictly_better (cp[-1].h.code, cp[-2].h.code))
+ {
+ int better = 0;
+ int worse = 0;
+
+ for (j = 0; j < n_candidates; j++)
+ if (! strictly_better (candidates[j].h.code, best_code))
+ break;
+
+ qsort (candidates+j, n_candidates-j, sizeof (struct candidate),
+ rank_for_ideal);
+ for (i = 0; i < len; i++)
+ {
+ if (cp[-1].harshness[i].code < cp[-2].harshness[i].code)
+ better = 1;
+ else if (cp[-1].harshness[i].code > cp[-2].harshness[i].code)
+ worse = 1;
+ else if (cp[-1].harshness[i].code & STD_CODE)
+ {
+ /* If it involves a standard conversion, let the
+ inheritance lattice be the final arbiter. */
+ if (cp[-1].harshness[i].distance > cp[-2].harshness[i].distance)
+ worse = 1;
+ else if (cp[-1].harshness[i].distance < cp[-2].harshness[i].distance)
+ better = 1;
+ }
+ else if (cp[-1].harshness[i].code & PROMO_CODE)
+ {
+ /* For integral promotions, take into account a finer
+ granularity for determining which types should be favored
+ over others in such promotions. */
+ if (cp[-1].harshness[i].int_penalty > cp[-2].harshness[i].int_penalty)
+ worse = 1;
+ else if (cp[-1].harshness[i].int_penalty < cp[-2].harshness[i].int_penalty)
+ better = 1;
+ }
+ }
+
+ if (! better || worse)
+ return NULL;
+ }
+ return cp-1;
+}
+
+/* Assume that if the class referred to is not in the
+ current class hierarchy, that it may be remote.
+ PARENT is assumed to be of aggregate type here. */
+static int
+may_be_remote (parent)
+ tree parent;
+{
+ if (TYPE_OVERLOADS_METHOD_CALL_EXPR (parent) == 0)
+ return 0;
+
+ if (current_class_type == NULL_TREE)
+ return 0;
+
+ if (parent == current_class_type)
+ return 0;
+
+ if (UNIQUELY_DERIVED_FROM_P (parent, current_class_type))
+ return 0;
+ return 1;
+}
+
+tree
+build_vfield_ref (datum, type)
+ tree datum, type;
+{
+ tree rval;
+ int old_assume_nonnull_objects = flag_assume_nonnull_objects;
+
+ if (datum == error_mark_node)
+ return error_mark_node;
+
+ /* Vtable references are always made from non-null objects. */
+ flag_assume_nonnull_objects = 1;
+ if (TREE_CODE (TREE_TYPE (datum)) == REFERENCE_TYPE)
+ datum = convert_from_reference (datum);
+
+ if (! TYPE_USES_COMPLEX_INHERITANCE (type))
+ rval = build (COMPONENT_REF, TREE_TYPE (CLASSTYPE_VFIELD (type)),
+ datum, CLASSTYPE_VFIELD (type));
+ else
+ rval = build_component_ref (datum, DECL_NAME (CLASSTYPE_VFIELD (type)), 0, 0);
+ flag_assume_nonnull_objects = old_assume_nonnull_objects;
+
+ return rval;
+}
+
+/* Build a call to a member of an object. I.e., one that overloads
+ operator ()(), or is a pointer-to-function or pointer-to-method. */
+static tree
+build_field_call (basetype_path, instance_ptr, name, parms)
+ tree basetype_path, instance_ptr, name, parms;
+{
+ tree field, instance;
+
+ if (instance_ptr == current_class_decl)
+ {
+ /* Check to see if we really have a reference to an instance variable
+ with `operator()()' overloaded. */
+ field = IDENTIFIER_CLASS_VALUE (name);
+
+ if (field == NULL_TREE)
+ {
+ cp_error ("`this' has no member named `%D'", name);
+ return error_mark_node;
+ }
+
+ if (TREE_CODE (field) == FIELD_DECL)
+ {
+ /* If it's a field, try overloading operator (),
+ or calling if the field is a pointer-to-function. */
+ instance = build_component_ref_1 (C_C_D, field, 0);
+ if (instance == error_mark_node)
+ return error_mark_node;
+
+ if (TYPE_LANG_SPECIFIC (TREE_TYPE (instance))
+ && TYPE_OVERLOADS_CALL_EXPR (TREE_TYPE (instance)))
+ return build_opfncall (CALL_EXPR, LOOKUP_NORMAL, instance, parms, NULL_TREE);
+
+ if (TREE_CODE (TREE_TYPE (instance)) == POINTER_TYPE)
+ {
+ if (TREE_CODE (TREE_TYPE (TREE_TYPE (instance))) == FUNCTION_TYPE)
+ return build_function_call (instance, parms);
+ else if (TREE_CODE (TREE_TYPE (TREE_TYPE (instance))) == METHOD_TYPE)
+ return build_function_call (instance, tree_cons (NULL_TREE, current_class_decl, parms));
+ }
+ }
+ return NULL_TREE;
+ }
+
+ /* Check to see if this is not really a reference to an instance variable
+ with `operator()()' overloaded. */
+ field = lookup_field (basetype_path, name, 1, 0);
+
+ /* This can happen if the reference was ambiguous or for access
+ violations. */
+ if (field == error_mark_node)
+ return error_mark_node;
+
+ if (field)
+ {
+ tree basetype;
+ tree ftype = TREE_TYPE (field);
+
+ if (TREE_CODE (ftype) == REFERENCE_TYPE)
+ ftype = TREE_TYPE (ftype);
+
+ if (TYPE_LANG_SPECIFIC (ftype) && TYPE_OVERLOADS_CALL_EXPR (ftype))
+ {
+ /* Make the next search for this field very short. */
+ basetype = DECL_FIELD_CONTEXT (field);
+ instance_ptr = convert_pointer_to (basetype, instance_ptr);
+
+ instance = build_indirect_ref (instance_ptr, NULL_PTR);
+ return build_opfncall (CALL_EXPR, LOOKUP_NORMAL,
+ build_component_ref_1 (instance, field, 0),
+ parms, NULL_TREE);
+ }
+ if (TREE_CODE (ftype) == POINTER_TYPE)
+ {
+ if (TREE_CODE (TREE_TYPE (ftype)) == FUNCTION_TYPE
+ || TREE_CODE (TREE_TYPE (ftype)) == METHOD_TYPE)
+ {
+ /* This is a member which is a pointer to function. */
+ tree ref
+ = build_component_ref_1 (build_indirect_ref (instance_ptr,
+ NULL_PTR),
+ field, LOOKUP_COMPLAIN);
+ if (ref == error_mark_node)
+ return error_mark_node;
+ return build_function_call (ref, parms);
+ }
+ }
+ else if (TREE_CODE (ftype) == METHOD_TYPE)
+ {
+ error ("invalid call via pointer-to-member function");
+ return error_mark_node;
+ }
+ else
+ return NULL_TREE;
+ }
+ return NULL_TREE;
+}
+
+tree
+find_scoped_type (type, inner_name, inner_types)
+ tree type, inner_name, inner_types;
+{
+ tree tags = CLASSTYPE_TAGS (type);
+
+ while (tags)
+ {
+ /* The TREE_PURPOSE of an enum tag (which becomes a member of the
+ enclosing class) is set to the name for the enum type. So, if
+ inner_name is `bar', and we strike `baz' for `enum bar { baz }',
+ then this test will be true. */
+ if (TREE_PURPOSE (tags) == inner_name)
+ {
+ if (inner_types == NULL_TREE)
+ return DECL_NESTED_TYPENAME (TYPE_NAME (TREE_VALUE (tags)));
+ return resolve_scope_to_name (TREE_VALUE (tags), inner_types);
+ }
+ tags = TREE_CHAIN (tags);
+ }
+
+#if 0
+ /* XXX This needs to be fixed better. */
+ if (TREE_CODE (type) == UNINSTANTIATED_P_TYPE)
+ {
+ sorry ("nested class lookup in template type");
+ return NULL_TREE;
+ }
+#endif
+
+ /* Look for a TYPE_DECL. */
+ for (tags = TYPE_FIELDS (type); tags; tags = TREE_CHAIN (tags))
+ if (TREE_CODE (tags) == TYPE_DECL && DECL_NAME (tags) == inner_name)
+ {
+ /* Code by raeburn. */
+ if (inner_types == NULL_TREE)
+ return DECL_NESTED_TYPENAME (tags);
+ return resolve_scope_to_name (TREE_TYPE (tags), inner_types);
+ }
+
+ return NULL_TREE;
+}
+
+/* Resolve an expression NAME1::NAME2::...::NAMEn to
+ the name that names the above nested type. INNER_TYPES
+ is a chain of nested type names (held together by SCOPE_REFs);
+ OUTER_TYPE is the type we know to enclose INNER_TYPES.
+ Returns NULL_TREE if there is an error. */
+tree
+resolve_scope_to_name (outer_type, inner_stuff)
+ tree outer_type, inner_stuff;
+{
+ register tree tmp;
+ tree inner_name, inner_type;
+
+ if (outer_type == NULL_TREE && current_class_type != NULL_TREE)
+ {
+ /* We first try to look for a nesting in our current class context,
+ then try any enclosing classes. */
+ tree type = current_class_type;
+
+ while (type && (TREE_CODE (type) == RECORD_TYPE
+ || TREE_CODE (type) == UNION_TYPE))
+ {
+ tree rval = resolve_scope_to_name (type, inner_stuff);
+
+ if (rval != NULL_TREE)
+ return rval;
+ type = DECL_CONTEXT (TYPE_NAME (type));
+ }
+ }
+
+ if (TREE_CODE (inner_stuff) == SCOPE_REF)
+ {
+ inner_name = TREE_OPERAND (inner_stuff, 0);
+ inner_type = TREE_OPERAND (inner_stuff, 1);
+ }
+ else
+ {
+ inner_name = inner_stuff;
+ inner_type = NULL_TREE;
+ }
+
+ if (outer_type == NULL_TREE)
+ {
+ /* If we have something that's already a type by itself,
+ use that. */
+ if (IDENTIFIER_HAS_TYPE_VALUE (inner_name))
+ {
+ if (inner_type)
+ return resolve_scope_to_name (IDENTIFIER_TYPE_VALUE (inner_name),
+ inner_type);
+ return inner_name;
+ }
+ return NULL_TREE;
+ }
+
+ if (! IS_AGGR_TYPE (outer_type))
+ return NULL_TREE;
+
+ /* Look for member classes or enums. */
+ tmp = find_scoped_type (outer_type, inner_name, inner_type);
+
+ /* If it's not a type in this class, then go down into the
+ base classes and search there. */
+ if (! tmp && TYPE_BINFO (outer_type))
+ {
+ tree binfos = TYPE_BINFO_BASETYPES (outer_type);
+ int i, n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+
+ for (i = 0; i < n_baselinks; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+ tmp = resolve_scope_to_name (BINFO_TYPE (base_binfo), inner_stuff);
+ if (tmp)
+ return tmp;
+ }
+ tmp = NULL_TREE;
+ }
+
+ return tmp;
+}
+
+/* Build a method call of the form `EXP->SCOPES::NAME (PARMS)'.
+ This is how virtual function calls are avoided. */
+tree
+build_scoped_method_call (exp, scopes, name, parms)
+ tree exp, scopes, name, parms;
+{
+ /* Because this syntactic form does not allow
+ a pointer to a base class to be `stolen',
+ we need not protect the derived->base conversion
+ that happens here.
+
+ @@ But we do have to check access privileges later. */
+ tree basename = resolve_scope_to_name (NULL_TREE, scopes);
+ tree basetype, binfo, decl;
+ tree type = TREE_TYPE (exp);
+
+ if (type == error_mark_node
+ || basename == NULL_TREE)
+ return error_mark_node;
+
+ basetype = IDENTIFIER_TYPE_VALUE (basename);
+
+ if (TREE_CODE (type) == REFERENCE_TYPE)
+ type = TREE_TYPE (type);
+
+ /* Destructors can be "called" for simple types; see 5.2.4 and 12.4 Note
+ that explicit ~int is caught in the parser; this deals with typedefs
+ and template parms. */
+ if (TREE_CODE (name) == BIT_NOT_EXPR && ! is_aggr_typedef (basename, 0))
+ {
+ if (type != basetype)
+ cp_error ("type of `%E' does not match destructor type `%T' (type was `%T')",
+ exp, basetype, type);
+ name = IDENTIFIER_TYPE_VALUE (TREE_OPERAND (name, 0));
+ if (basetype != name)
+ cp_error ("qualified type `%T' does not match destructor type `%T'",
+ basetype, name);
+ return void_zero_node;
+ }
+
+ if (! is_aggr_typedef (basename, 1))
+ return error_mark_node;
+
+ if (! IS_AGGR_TYPE (type))
+ {
+ cp_error ("base object `%E' of scoped method call is of non-aggregate type `%T'",
+ exp, type);
+ return error_mark_node;
+ }
+
+ if ((binfo = binfo_or_else (basetype, type)))
+ {
+ if (binfo == error_mark_node)
+ return error_mark_node;
+ if (TREE_CODE (exp) == INDIRECT_REF)
+ decl = build_indirect_ref (convert_pointer_to (binfo,
+ build_unary_op (ADDR_EXPR, exp, 0)), NULL_PTR);
+ else
+ decl = build_scoped_ref (exp, scopes);
+
+ /* Call to a destructor. */
+ if (TREE_CODE (name) == BIT_NOT_EXPR)
+ {
+ /* Explicit call to destructor. */
+ name = TREE_OPERAND (name, 0);
+ if (name != constructor_name (TREE_TYPE (decl)))
+ {
+ cp_error
+ ("qualified type `%T' does not match destructor type `%T'",
+ TREE_TYPE (decl), name);
+ return error_mark_node;
+ }
+ if (! TYPE_HAS_DESTRUCTOR (TREE_TYPE (decl)))
+ return void_zero_node;
+
+ return build_delete (TREE_TYPE (decl), decl, integer_two_node,
+ LOOKUP_NORMAL|LOOKUP_NONVIRTUAL|LOOKUP_DESTRUCTOR,
+ 0);
+ }
+
+ /* Call to a method. */
+ return build_method_call (decl, name, parms, binfo,
+ LOOKUP_NORMAL|LOOKUP_NONVIRTUAL);
+ }
+ return error_mark_node;
+}
+
+static void
+print_candidates (candidates)
+ tree candidates;
+{
+ cp_error_at ("candidates are: %D", TREE_VALUE (candidates));
+ candidates = TREE_CHAIN (candidates);
+
+ while (candidates)
+ {
+ cp_error_at (" %D", TREE_VALUE (candidates));
+ candidates = TREE_CHAIN (candidates);
+ }
+}
+
+static void
+print_n_candidates (candidates, n)
+ struct candidate *candidates;
+ int n;
+{
+ int i;
+
+ cp_error_at ("candidates are: %D", candidates[0].function);
+ for (i = 1; i < n; i++)
+ cp_error_at (" %D", candidates[i].function);
+}
+
+/* Build something of the form ptr->method (args)
+ or object.method (args). This can also build
+ calls to constructors, and find friends.
+
+ Member functions always take their class variable
+ as a pointer.
+
+ INSTANCE is a class instance.
+
+ NAME is the name of the method desired, usually an IDENTIFIER_NODE.
+
+ PARMS help to figure out what that NAME really refers to.
+
+ BASETYPE_PATH, if non-NULL, contains a chain from the type of INSTANCE
+ down to the real instance type to use for access checking. We need this
+ information to get protected accesses correct. This parameter is used
+ by build_member_call.
+
+ FLAGS is the logical disjunction of zero or more LOOKUP_
+ flags. See cp-tree.h for more info.
+
+ If this is all OK, calls build_function_call with the resolved
+ member function.
+
+ This function must also handle being called to perform
+ initialization, promotion/coercion of arguments, and
+ instantiation of default parameters.
+
+ Note that NAME may refer to an instance variable name. If
+ `operator()()' is defined for the type of that field, then we return
+ that result. */
+tree
+build_method_call (instance, name, parms, basetype_path, flags)
+ tree instance, name, parms, basetype_path;
+ int flags;
+{
+ register tree function, fntype, value_type;
+ register tree basetype, save_basetype;
+ register tree baselink, result, method_name, parmtypes, parm;
+ tree last;
+ int pass;
+ enum access_type access = access_public;
+
+ /* Range of cases for vtable optimization. */
+ enum vtable_needs { not_needed, maybe_needed, unneeded, needed };
+ enum vtable_needs need_vtbl = not_needed;
+
+ char *name_kind;
+ int ever_seen = 0;
+ tree instance_ptr = NULL_TREE;
+ int all_virtual = flag_all_virtual;
+ int static_call_context = 0;
+ tree found_fns = NULL_TREE;
+
+ /* Keep track of `const' and `volatile' objects. */
+ int constp, volatilep;
+
+#ifdef GATHER_STATISTICS
+ n_build_method_call++;
+#endif
+
+ if (instance == error_mark_node
+ || name == error_mark_node
+ || parms == error_mark_node
+ || (instance != NULL_TREE && TREE_TYPE (instance) == error_mark_node))
+ return error_mark_node;
+
+ /* This is the logic that magically deletes the second argument to
+ operator delete, if it is not needed. */
+ if (name == ansi_opname[(int) DELETE_EXPR] && list_length (parms)==2)
+ {
+ tree save_last = TREE_CHAIN (parms);
+ tree result;
+ /* get rid of unneeded argument */
+ TREE_CHAIN (parms) = NULL_TREE;
+ result = build_method_call (instance, name, parms, basetype_path,
+ (LOOKUP_SPECULATIVELY|flags)
+ &~LOOKUP_COMPLAIN);
+ /* If it works, return it. */
+ if (result && result != error_mark_node)
+ return build_method_call (instance, name, parms, basetype_path, flags);
+ /* If it doesn't work, two argument delete must work */
+ TREE_CHAIN (parms) = save_last;
+ }
+ /* We already know whether it's needed or not for vec delete. */
+ else if (name == ansi_opname[(int) VEC_DELETE_EXPR]
+ && ! TYPE_VEC_DELETE_TAKES_SIZE (TREE_TYPE (instance)))
+ TREE_CHAIN (parms) = NULL_TREE;
+
+ if (TREE_CODE (name) == BIT_NOT_EXPR)
+ {
+ flags |= LOOKUP_DESTRUCTOR;
+ name = TREE_OPERAND (name, 0);
+ if (parms)
+ error ("destructors take no parameters");
+ basetype = TREE_TYPE (instance);
+ if (IS_AGGR_TYPE (basetype))
+ {
+ if (name == constructor_name (basetype))
+ goto huzzah;
+ }
+ else
+ {
+ if (basetype == get_type_value (name))
+ goto huzzah;
+ }
+ cp_error ("destructor name `~%D' does not match type `%T' of expression",
+ name, basetype);
+ return void_zero_node;
+
+ huzzah:
+ if (! TYPE_HAS_DESTRUCTOR (basetype))
+ return void_zero_node;
+ instance = default_conversion (instance);
+ instance_ptr = build_unary_op (ADDR_EXPR, instance, 0);
+ return build_delete (build_pointer_type (basetype),
+ instance_ptr, integer_two_node,
+ LOOKUP_NORMAL|LOOKUP_DESTRUCTOR, 0);
+ }
+
+ {
+ char *xref_name;
+
+ /* Initialize name for error reporting. */
+ if (IDENTIFIER_OPNAME_P (name) && ! IDENTIFIER_TYPENAME_P (name))
+ {
+ char *p = operator_name_string (name);
+ xref_name = (char *)alloca (strlen (p) + 10);
+ sprintf (xref_name, "operator %s", p);
+ }
+ else if (TREE_CODE (name) == SCOPE_REF)
+ xref_name = IDENTIFIER_POINTER (TREE_OPERAND (name, 1));
+ else
+ xref_name = IDENTIFIER_POINTER (name);
+
+ GNU_xref_call (current_function_decl, xref_name);
+ }
+
+ if (instance == NULL_TREE)
+ {
+ basetype = NULL_TREE;
+ /* Check cases where this is really a call to raise
+ an exception. */
+ if (current_class_type && TREE_CODE (name) == IDENTIFIER_NODE)
+ {
+ basetype = purpose_member (name, CLASSTYPE_TAGS (current_class_type));
+ if (basetype)
+ basetype = TREE_VALUE (basetype);
+ }
+ else if (TREE_CODE (name) == SCOPE_REF
+ && TREE_CODE (TREE_OPERAND (name, 0)) == IDENTIFIER_NODE)
+ {
+ if (! is_aggr_typedef (TREE_OPERAND (name, 0), 1))
+ return error_mark_node;
+ basetype = purpose_member (TREE_OPERAND (name, 1),
+ CLASSTYPE_TAGS (IDENTIFIER_TYPE_VALUE (TREE_OPERAND (name, 0))));
+ if (basetype)
+ basetype = TREE_VALUE (basetype);
+ }
+
+ if (basetype != NULL_TREE)
+ ;
+ /* call to a constructor... */
+ else if (basetype_path)
+ basetype = BINFO_TYPE (basetype_path);
+ else if (IDENTIFIER_HAS_TYPE_VALUE (name))
+ {
+ basetype = IDENTIFIER_TYPE_VALUE (name);
+ name = constructor_name_full (basetype);
+ }
+ else
+ {
+ tree typedef_name = lookup_name (name, 1);
+ if (typedef_name && TREE_CODE (typedef_name) == TYPE_DECL)
+ {
+ /* Canonicalize the typedef name. */
+ basetype = TREE_TYPE (typedef_name);
+ name = TYPE_IDENTIFIER (basetype);
+ }
+ else
+ {
+ cp_error ("no constructor named `%T' in scope",
+ name);
+ return error_mark_node;
+ }
+ }
+
+ if (! IS_AGGR_TYPE (basetype))
+ {
+ non_aggr_error:
+ if ((flags & LOOKUP_COMPLAIN) && TREE_CODE (basetype) != ERROR_MARK)
+ cp_error ("request for member `%D' in `%E', which is of non-aggregate type `%T'",
+ name, instance, basetype);
+
+ return error_mark_node;
+ }
+ }
+ else if (instance == C_C_D || instance == current_class_decl)
+ {
+ /* When doing initialization, we side-effect the TREE_TYPE of
+ C_C_D, hence we cannot set up BASETYPE from CURRENT_CLASS_TYPE. */
+ basetype = TREE_TYPE (C_C_D);
+
+ /* Anything manifestly `this' in constructors and destructors
+ has a known type, so virtual function tables are not needed. */
+ if (TYPE_VIRTUAL_P (basetype)
+ && !(flags & LOOKUP_NONVIRTUAL))
+ need_vtbl = (dtor_label || ctor_label)
+ ? unneeded : maybe_needed;
+
+ instance = C_C_D;
+ instance_ptr = current_class_decl;
+ result = build_field_call (TYPE_BINFO (current_class_type),
+ instance_ptr, name, parms);
+
+ if (result)
+ return result;
+ }
+ else if (TREE_CODE (instance) == RESULT_DECL)
+ {
+ basetype = TREE_TYPE (instance);
+ /* Should we ever have to make a virtual function reference
+ from a RESULT_DECL, know that it must be of fixed type
+ within the scope of this function. */
+ if (!(flags & LOOKUP_NONVIRTUAL) && TYPE_VIRTUAL_P (basetype))
+ need_vtbl = maybe_needed;
+ instance_ptr = build1 (ADDR_EXPR, TYPE_POINTER_TO (basetype), instance);
+ }
+ else
+ {
+ /* The MAIN_VARIANT of the type that `instance_ptr' winds up being. */
+ tree inst_ptr_basetype;
+
+ static_call_context =
+ (TREE_CODE (instance) == INDIRECT_REF
+ && TREE_CODE (TREE_OPERAND (instance, 0)) == NOP_EXPR
+ && TREE_OPERAND (TREE_OPERAND (instance, 0), 0) == error_mark_node);
+
+ if (TREE_CODE (instance) == OFFSET_REF)
+ instance = resolve_offset_ref (instance);
+
+ /* the base type of an instance variable is pointer to class */
+ basetype = TREE_TYPE (instance);
+
+ if (TREE_CODE (basetype) == REFERENCE_TYPE)
+ {
+ basetype = TREE_TYPE (basetype);
+ if (! IS_AGGR_TYPE (basetype))
+ goto non_aggr_error;
+ /* Call to convert not needed because we are remaining
+ within the same type. */
+ instance_ptr = build1 (NOP_EXPR, build_pointer_type (basetype),
+ instance);
+ inst_ptr_basetype = TYPE_MAIN_VARIANT (basetype);
+ }
+ else
+ {
+ if (! IS_AGGR_TYPE (basetype))
+ goto non_aggr_error;
+
+ /* If `instance' is a signature pointer/reference and `name' is
+ not a constructor, we are calling a signature member function.
+ In that case set the `basetype' to the signature type. */
+ if ((IS_SIGNATURE_POINTER (basetype)
+ || IS_SIGNATURE_REFERENCE (basetype))
+ && TYPE_IDENTIFIER (basetype) != name)
+ basetype = SIGNATURE_TYPE (basetype);
+
+ if ((IS_SIGNATURE (basetype)
+ && (instance_ptr = build_optr_ref (instance)))
+ || (lvalue_p (instance)
+ && (instance_ptr = build_unary_op (ADDR_EXPR, instance, 0)))
+ || (instance_ptr = unary_complex_lvalue (ADDR_EXPR, instance)))
+ {
+ if (instance_ptr == error_mark_node)
+ return error_mark_node;
+ }
+ else if (TREE_CODE (instance) == NOP_EXPR
+ || TREE_CODE (instance) == CONSTRUCTOR)
+ {
+ /* A cast is not an lvalue. Initialize a fresh temp
+ with the value we are casting from, and proceed with
+ that temporary. We can't cast to a reference type,
+ so that simplifies the initialization to something
+ we can manage. */
+ tree temp = get_temp_name (TREE_TYPE (instance), 0);
+ if (IS_AGGR_TYPE (TREE_TYPE (instance)))
+ expand_aggr_init (temp, instance, 0);
+ else
+ {
+ store_init_value (temp, instance);
+ expand_decl_init (temp);
+ }
+ instance = temp;
+ instance_ptr = build_unary_op (ADDR_EXPR, instance, 0);
+ }
+ else
+ {
+ if (TREE_CODE (instance) != CALL_EXPR)
+ my_friendly_abort (125);
+ if (TYPE_NEEDS_CONSTRUCTING (basetype))
+ instance = build_cplus_new (basetype, instance, 0);
+ else
+ {
+ instance = get_temp_name (basetype, 0);
+ TREE_ADDRESSABLE (instance) = 1;
+ }
+ instance_ptr = build_unary_op (ADDR_EXPR, instance, 0);
+ }
+ /* @@ Should we call comp_target_types here? */
+ inst_ptr_basetype = TREE_TYPE (TREE_TYPE (instance_ptr));
+ if (TYPE_MAIN_VARIANT (basetype) == TYPE_MAIN_VARIANT (inst_ptr_basetype))
+ basetype = inst_ptr_basetype;
+ else
+ {
+ instance_ptr = convert (TYPE_POINTER_TO (basetype), instance_ptr);
+ if (instance_ptr == error_mark_node)
+ return error_mark_node;
+ }
+ }
+
+ /* After converting `instance_ptr' above, `inst_ptr_basetype' was
+ not updated, so we use `basetype' instead. */
+ if (basetype_path == NULL_TREE
+ && IS_SIGNATURE (basetype))
+ basetype_path = TYPE_BINFO (basetype);
+ else if (basetype_path == NULL_TREE ||
+ BINFO_TYPE (basetype_path) != TYPE_MAIN_VARIANT (inst_ptr_basetype))
+ basetype_path = TYPE_BINFO (inst_ptr_basetype);
+
+ result = build_field_call (basetype_path, instance_ptr, name, parms);
+ if (result)
+ return result;
+
+ if (!(flags & LOOKUP_NONVIRTUAL) && TYPE_VIRTUAL_P (basetype))
+ {
+ if (TREE_SIDE_EFFECTS (instance_ptr))
+ {
+ /* This action is needed because the instance is needed
+ for providing the base of the virtual function table.
+ Without using a SAVE_EXPR, the function we are building
+ may be called twice, or side effects on the instance
+ variable (such as a post-increment), may happen twice. */
+ instance_ptr = save_expr (instance_ptr);
+ instance = build_indirect_ref (instance_ptr, NULL_PTR);
+ }
+ else if (TREE_CODE (TREE_TYPE (instance)) == POINTER_TYPE)
+ {
+ /* This happens when called for operator new (). */
+ instance = build_indirect_ref (instance, NULL_PTR);
+ }
+
+ need_vtbl = maybe_needed;
+ }
+ }
+
+ if (TYPE_SIZE (basetype) == 0)
+ {
+ /* This is worth complaining about, I think. */
+ cp_error ("cannot lookup method in incomplete type `%T'", basetype);
+ return error_mark_node;
+ }
+
+ save_basetype = TYPE_MAIN_VARIANT (basetype);
+
+#if 0
+ if (all_virtual == 1
+ && (! strncmp (IDENTIFIER_POINTER (name), OPERATOR_METHOD_FORMAT,
+ OPERATOR_METHOD_LENGTH)
+ || instance_ptr == NULL_TREE
+ || (TYPE_OVERLOADS_METHOD_CALL_EXPR (basetype) == 0)))
+ all_virtual = 0;
+#endif
+
+ last = NULL_TREE;
+ for (parmtypes = NULL_TREE, parm = parms; parm; parm = TREE_CHAIN (parm))
+ {
+ tree t = TREE_TYPE (TREE_VALUE (parm));
+ if (TREE_CODE (t) == OFFSET_TYPE)
+ {
+ /* Convert OFFSET_TYPE entities to their normal selves. */
+ TREE_VALUE (parm) = resolve_offset_ref (TREE_VALUE (parm));
+ t = TREE_TYPE (TREE_VALUE (parm));
+ }
+ if (TREE_CODE (TREE_VALUE (parm)) == OFFSET_REF
+ && TREE_CODE (t) == METHOD_TYPE)
+ {
+ TREE_VALUE (parm) = build_unary_op (ADDR_EXPR, TREE_VALUE (parm), 0);
+ }
+ if (TREE_CODE (t) == ARRAY_TYPE)
+ {
+ /* Perform the conversion from ARRAY_TYPE to POINTER_TYPE in place.
+ This eliminates needless calls to `compute_conversion_costs'. */
+ TREE_VALUE (parm) = default_conversion (TREE_VALUE (parm));
+ t = TREE_TYPE (TREE_VALUE (parm));
+ }
+ if (t == error_mark_node)
+ return error_mark_node;
+ last = build_tree_list (NULL_TREE, t);
+ parmtypes = chainon (parmtypes, last);
+ }
+
+ if (instance)
+ {
+ /* TREE_READONLY (instance) fails for references. */
+ constp = TYPE_READONLY (TREE_TYPE (TREE_TYPE (instance_ptr)));
+ volatilep = TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (instance_ptr)));
+ parms = tree_cons (NULL_TREE, instance_ptr, parms);
+ }
+ else
+ {
+ /* Raw constructors are always in charge. */
+ if (TYPE_USES_VIRTUAL_BASECLASSES (basetype)
+ && ! (flags & LOOKUP_HAS_IN_CHARGE))
+ {
+ flags |= LOOKUP_HAS_IN_CHARGE;
+ parms = tree_cons (NULL_TREE, integer_one_node, parms);
+ parmtypes = tree_cons (NULL_TREE, integer_type_node, parmtypes);
+ }
+
+ if (flag_this_is_variable > 0)
+ {
+ constp = 0;
+ volatilep = 0;
+ parms = tree_cons (NULL_TREE, build1 (NOP_EXPR, TYPE_POINTER_TO (basetype), integer_zero_node), parms);
+ }
+ else
+ {
+ constp = 0;
+ volatilep = 0;
+ instance_ptr = build_new (NULL_TREE, basetype, void_type_node, 0);
+ if (instance_ptr == error_mark_node)
+ return error_mark_node;
+ instance_ptr = save_expr (instance_ptr);
+ TREE_CALLS_NEW (instance_ptr) = 1;
+ instance = build_indirect_ref (instance_ptr, NULL_PTR);
+
+ /* If it's a default argument initialized from a ctor, what we get
+ from instance_ptr will match the arglist for the FUNCTION_DECL
+ of the constructor. */
+ if (parms && TREE_CODE (TREE_VALUE (parms)) == CALL_EXPR
+ && TREE_OPERAND (TREE_VALUE (parms), 1)
+ && TREE_CALLS_NEW (TREE_VALUE (TREE_OPERAND (TREE_VALUE (parms), 1))))
+ parms = build_tree_list (NULL_TREE, instance_ptr);
+ else
+ parms = tree_cons (NULL_TREE, instance_ptr, parms);
+ }
+ }
+
+ parmtypes = tree_cons (NULL_TREE, TREE_TYPE (instance_ptr), parmtypes);
+
+ if (last == NULL_TREE)
+ last = parmtypes;
+
+ /* Look up function name in the structure type definition. */
+
+ if ((IDENTIFIER_HAS_TYPE_VALUE (name)
+ && ! IDENTIFIER_OPNAME_P (name)
+ && IS_AGGR_TYPE (IDENTIFIER_TYPE_VALUE (name))
+ && TREE_CODE (IDENTIFIER_TYPE_VALUE (name)) != UNINSTANTIATED_P_TYPE)
+ || name == constructor_name (basetype))
+ {
+ tree tmp = NULL_TREE;
+ if (IDENTIFIER_TYPE_VALUE (name) == basetype
+ || name == constructor_name (basetype))
+ tmp = TYPE_BINFO (basetype);
+ else
+ tmp = get_binfo (IDENTIFIER_TYPE_VALUE (name), basetype, 0);
+
+ if (tmp != NULL_TREE)
+ {
+ name_kind = "constructor";
+
+ if (TYPE_USES_VIRTUAL_BASECLASSES (basetype)
+ && ! (flags & LOOKUP_HAS_IN_CHARGE))
+ {
+ /* Constructors called for initialization
+ only are never in charge. */
+ tree tmplist;
+
+ flags |= LOOKUP_HAS_IN_CHARGE;
+ tmplist = tree_cons (NULL_TREE, integer_zero_node,
+ TREE_CHAIN (parms));
+ TREE_CHAIN (parms) = tmplist;
+ tmplist = tree_cons (NULL_TREE, integer_type_node, TREE_CHAIN (parmtypes));
+ TREE_CHAIN (parmtypes) = tmplist;
+ }
+ basetype = BINFO_TYPE (tmp);
+ }
+ else
+ name_kind = "method";
+ }
+ else
+ name_kind = "method";
+
+ if (basetype_path == NULL_TREE
+ || BINFO_TYPE (basetype_path) != TYPE_MAIN_VARIANT (basetype))
+ basetype_path = TYPE_BINFO (basetype);
+ result = lookup_fnfields (basetype_path, name,
+ (flags & LOOKUP_COMPLAIN));
+ if (result == error_mark_node)
+ return error_mark_node;
+
+
+ /* Now, go look for this method name. We do not find destructors here.
+
+ Putting `void_list_node' on the end of the parmtypes
+ fakes out `build_decl_overload' into doing the right thing. */
+ TREE_CHAIN (last) = void_list_node;
+ method_name = build_decl_overload (name, parmtypes,
+ 1 + (name == constructor_name (save_basetype)
+ || name == constructor_name_full (save_basetype)));
+ TREE_CHAIN (last) = NULL_TREE;
+
+ for (pass = 0; pass < 2; pass++)
+ {
+ struct candidate *candidates;
+ struct candidate *cp;
+ int len;
+ unsigned best = 1;
+
+ /* This increments every time we go up the type hierarchy.
+ The idea is to prefer a function of the derived class if possible. */
+ int b_or_d = 0;
+
+ baselink = result;
+
+ if (pass > 0)
+ {
+ candidates
+ = (struct candidate *) alloca ((ever_seen+1)
+ * sizeof (struct candidate));
+ bzero (candidates, (ever_seen + 1) * sizeof (struct candidate));
+ cp = candidates;
+ len = list_length (parms);
+ ever_seen = 0;
+
+ /* First see if a global function has a shot at it. */
+ if (flags & LOOKUP_GLOBAL)
+ {
+ tree friend_parms;
+ tree parm = instance_ptr;
+
+ if (TREE_CODE (TREE_TYPE (parm)) == REFERENCE_TYPE)
+ {
+ /* TREE_VALUE (parms) may have been modified by now;
+ restore it to its original value. */
+ TREE_VALUE (parms) = parm;
+ friend_parms = parms;
+ }
+ else if (TREE_CODE (TREE_TYPE (parm)) == POINTER_TYPE)
+ {
+ tree new_type;
+ parm = build_indirect_ref (parm, "friendifying parms (compiler error)");
+ new_type = c_build_type_variant (TREE_TYPE (parm), constp,
+ volatilep);
+ new_type = build_reference_type (new_type);
+ parm = convert (new_type, parm);
+ friend_parms = tree_cons (NULL_TREE, parm, TREE_CHAIN (parms));
+ }
+ else
+ my_friendly_abort (167);
+
+ cp->h_len = len;
+ cp->harshness = (struct harshness_code *)
+ alloca ((len + 1) * sizeof (struct harshness_code));
+
+ result = build_overload_call (name, friend_parms, 0, cp);
+ /* If it turns out to be the one we were actually looking for
+ (it was probably a friend function), the return the
+ good result. */
+ if (TREE_CODE (result) == CALL_EXPR)
+ return result;
+
+ while ((cp->h.code & EVIL_CODE) == 0)
+ {
+ /* non-standard uses: set the field to 0 to indicate
+ we are using a non-member function. */
+ cp->u.field = 0;
+ if (cp->harshness[len].distance == 0
+ && cp->h.code < best)
+ best = cp->h.code;
+ cp += 1;
+ }
+ }
+ }
+
+ while (baselink)
+ {
+ /* We have a hit (of sorts). If the parameter list is
+ "error_mark_node", or some variant thereof, it won't
+ match any methods. Since we have verified that the is
+ some method vaguely matching this one (in name at least),
+ silently return.
+
+ Don't stop for friends, however. */
+ basetype_path = TREE_PURPOSE (baselink);
+
+ function = TREE_VALUE (baselink);
+ if (TREE_CODE (basetype_path) == TREE_LIST)
+ basetype_path = TREE_VALUE (basetype_path);
+ basetype = BINFO_TYPE (basetype_path);
+
+ /* Cast the instance variable if necessary. */
+ if (basetype != TYPE_MAIN_VARIANT
+ (TREE_TYPE (TREE_TYPE (TREE_VALUE (parms)))))
+ {
+ if (basetype == save_basetype)
+ TREE_VALUE (parms) = instance_ptr;
+ else
+ {
+ tree type = build_pointer_type
+ (build_type_variant (basetype, constp, volatilep));
+ TREE_VALUE (parms) = convert_force (type, instance_ptr);
+ }
+ }
+
+ /* FIXME: this is the wrong place to get an error. Hopefully
+ the access-control rewrite will make this change more cleanly. */
+ if (TREE_VALUE (parms) == error_mark_node)
+ return error_mark_node;
+
+ if (DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (function)))
+ function = DECL_CHAIN (function);
+
+ for (; function; function = DECL_CHAIN (function))
+ {
+#ifdef GATHER_STATISTICS
+ n_inner_fields_searched++;
+#endif
+ ever_seen++;
+ if (pass > 0)
+ found_fns = tree_cons (NULL_TREE, function, found_fns);
+
+ /* Not looking for friends here. */
+ if (TREE_CODE (TREE_TYPE (function)) == FUNCTION_TYPE
+ && ! DECL_STATIC_FUNCTION_P (function))
+ continue;
+
+ if (pass == 0
+ && DECL_ASSEMBLER_NAME (function) == method_name)
+ goto found;
+
+ if (pass > 0)
+ {
+ tree these_parms = parms;
+
+#ifdef GATHER_STATISTICS
+ n_inner_fields_searched++;
+#endif
+ cp->h_len = len;
+ cp->harshness = (struct harshness_code *)
+ alloca ((len + 1) * sizeof (struct harshness_code));
+
+ if (DECL_STATIC_FUNCTION_P (function))
+ these_parms = TREE_CHAIN (these_parms);
+ compute_conversion_costs (function, these_parms, cp, len);
+
+ if ((cp->h.code & EVIL_CODE) == 0)
+ {
+ cp->u.field = function;
+ cp->function = function;
+ cp->basetypes = basetype_path;
+
+ /* No "two-level" conversions. */
+ if (flags & LOOKUP_NO_CONVERSION
+ && (cp->h.code & USER_CODE))
+ continue;
+
+ /* If we used default parameters, we must
+ check to see whether anyone else might
+ use them also, and report a possible
+ ambiguity. */
+ if (! TYPE_USES_MULTIPLE_INHERITANCE (save_basetype)
+ && cp->harshness[len].distance == 0
+ && cp->h.code < best)
+ {
+ if (! DECL_STATIC_FUNCTION_P (function))
+ TREE_VALUE (parms) = cp->arg;
+ if (best == 1)
+ goto found_and_maybe_warn;
+ }
+ cp++;
+ }
+ }
+ }
+ /* Now we have run through one link's member functions.
+ arrange to head-insert this link's links. */
+ baselink = next_baselink (baselink);
+ b_or_d += 1;
+ /* Don't grab functions from base classes. lookup_fnfield will
+ do the work to get us down into the right place. */
+ baselink = NULL_TREE;
+ }
+ if (pass == 0)
+ {
+ tree igv = lookup_name_nonclass (name);
+
+ /* No exact match could be found. Now try to find match
+ using default conversions. */
+ if ((flags & LOOKUP_GLOBAL) && igv)
+ {
+ if (TREE_CODE (igv) == FUNCTION_DECL)
+ ever_seen += 1;
+ else if (TREE_CODE (igv) == TREE_LIST)
+ ever_seen += count_functions (igv);
+ }
+
+ if (ever_seen == 0)
+ {
+ if ((flags & (LOOKUP_SPECULATIVELY|LOOKUP_COMPLAIN))
+ == LOOKUP_SPECULATIVELY)
+ return NULL_TREE;
+
+ TREE_CHAIN (last) = void_list_node;
+ if (flags & LOOKUP_GLOBAL)
+ cp_error ("no global or member function `%D(%A)' defined",
+ name, parmtypes);
+ else
+ cp_error ("no member function `%T::%D(%A)' defined",
+ save_basetype, name, TREE_CHAIN (parmtypes));
+ return error_mark_node;
+ }
+ continue;
+ }
+
+ if (cp - candidates != 0)
+ {
+ /* Rank from worst to best. Then cp will point to best one.
+ Private fields have their bits flipped. For unsigned
+ numbers, this should make them look very large.
+ If the best alternate has a (signed) negative value,
+ then all we ever saw were private members. */
+ if (cp - candidates > 1)
+ {
+ int n_candidates = cp - candidates;
+ TREE_VALUE (parms) = instance_ptr;
+ cp = ideal_candidate (save_basetype, candidates,
+ n_candidates, parms, len);
+ if (cp == (struct candidate *)0)
+ {
+ if (flags & LOOKUP_COMPLAIN)
+ {
+ cp_error ("call of overloaded %s `%D' is ambiguous",
+ name_kind, name);
+ print_n_candidates (candidates, n_candidates);
+ }
+ return error_mark_node;
+ }
+ if (cp->h.code & EVIL_CODE)
+ return error_mark_node;
+ }
+ else if (cp[-1].h.code & EVIL_CODE)
+ {
+ if (flags & LOOKUP_COMPLAIN)
+ cp_error ("ambiguous type conversion requested for %s `%D'",
+ name_kind, name);
+ return error_mark_node;
+ }
+ else
+ cp--;
+
+ /* The global function was the best, so use it. */
+ if (cp->u.field == 0)
+ {
+ /* We must convert the instance pointer into a reference type.
+ Global overloaded functions can only either take
+ aggregate objects (which come for free from references)
+ or reference data types anyway. */
+ TREE_VALUE (parms) = copy_node (instance_ptr);
+ TREE_TYPE (TREE_VALUE (parms)) = build_reference_type (TREE_TYPE (TREE_TYPE (instance_ptr)));
+ return build_function_call (cp->function, parms);
+ }
+
+ function = cp->function;
+ basetype_path = cp->basetypes;
+ if (! DECL_STATIC_FUNCTION_P (function))
+ TREE_VALUE (parms) = cp->arg;
+ goto found_and_maybe_warn;
+ }
+
+ if (flags & (LOOKUP_COMPLAIN|LOOKUP_SPECULATIVELY))
+ {
+ if ((flags & (LOOKUP_SPECULATIVELY|LOOKUP_COMPLAIN))
+ == LOOKUP_SPECULATIVELY)
+ return NULL_TREE;
+
+ if (DECL_STATIC_FUNCTION_P (cp->function))
+ parms = TREE_CHAIN (parms);
+ if (ever_seen)
+ {
+ if (flags & LOOKUP_SPECULATIVELY)
+ return NULL_TREE;
+ if (static_call_context
+ && TREE_CODE (TREE_TYPE (cp->function)) == METHOD_TYPE)
+ cp_error ("object missing in call to `%D'", cp->function);
+ else if (ever_seen > 1)
+ {
+ TREE_CHAIN (last) = void_list_node;
+ cp_error ("no matching function for call to `%T::%D (%A)'",
+ TREE_TYPE (TREE_TYPE (instance_ptr)),
+ name, TREE_CHAIN (parmtypes));
+ TREE_CHAIN (last) = NULL_TREE;
+ print_candidates (found_fns);
+ }
+ else
+ report_type_mismatch (cp, parms, name_kind);
+ return error_mark_node;
+ }
+
+ if ((flags & (LOOKUP_SPECULATIVELY|LOOKUP_COMPLAIN))
+ == LOOKUP_COMPLAIN)
+ {
+ cp_error ("%T has no method named %D", save_basetype, name);
+ return error_mark_node;
+ }
+ return NULL_TREE;
+ }
+ continue;
+
+ found_and_maybe_warn:
+ if ((cp->harshness[0].code & CONST_CODE)
+ /* 12.1p2: Constructors can be called for const objects. */
+ && ! DECL_CONSTRUCTOR_P (cp->function))
+ {
+ if (flags & LOOKUP_COMPLAIN)
+ {
+ cp_error_at ("non-const member function `%D'", cp->function);
+ error ("called for const object at this point in file");
+ }
+ /* Not good enough for a match. */
+ else
+ return error_mark_node;
+ }
+ goto found;
+ }
+ /* Silently return error_mark_node. */
+ return error_mark_node;
+
+ found:
+ if (flags & LOOKUP_PROTECT)
+ access = compute_access (basetype_path, function);
+
+ if (access == access_private)
+ {
+ if (flags & LOOKUP_COMPLAIN)
+ {
+ cp_error_at ("%s `%+#D' is %s", name_kind, function,
+ TREE_PRIVATE (function) ? "private"
+ : "from private base class");
+ error ("within this context");
+ }
+ return error_mark_node;
+ }
+ else if (access == access_protected)
+ {
+ if (flags & LOOKUP_COMPLAIN)
+ {
+ cp_error_at ("%s `%+#D' %s", name_kind, function,
+ TREE_PROTECTED (function) ? "is protected"
+ : "has protected accessibility");
+ error ("within this context");
+ }
+ return error_mark_node;
+ }
+
+ /* From here on down, BASETYPE is the type that INSTANCE_PTR's
+ type (if it exists) is a pointer to. */
+
+ if (DECL_ABSTRACT_VIRTUAL_P (function)
+ && instance == C_C_D
+ && DECL_CONSTRUCTOR_P (current_function_decl)
+ && ! (flags & LOOKUP_NONVIRTUAL)
+ && value_member (function, get_abstract_virtuals (basetype)))
+ cp_error ("abstract virtual `%#D' called from constructor", function);
+
+ if (IS_SIGNATURE (basetype) && static_call_context)
+ {
+ cp_error ("cannot call signature member function `%T::%D' without signature pointer/reference",
+ basetype, name);
+ return error_mark_node;
+ }
+ else if (IS_SIGNATURE (basetype))
+ return build_signature_method_call (basetype, instance, function, parms);
+
+ function = DECL_MAIN_VARIANT (function);
+ /* Declare external function if necessary. */
+ assemble_external (function);
+
+ fntype = TREE_TYPE (function);
+ if (TREE_CODE (fntype) == POINTER_TYPE)
+ fntype = TREE_TYPE (fntype);
+ basetype = DECL_CLASS_CONTEXT (function);
+
+ /* If we are referencing a virtual function from an object
+ of effectively static type, then there is no need
+ to go through the virtual function table. */
+ if (need_vtbl == maybe_needed)
+ {
+ int fixed_type = resolves_to_fixed_type_p (instance, 0);
+
+ if (all_virtual == 1
+ && DECL_VINDEX (function)
+ && may_be_remote (basetype))
+ need_vtbl = needed;
+ else if (DECL_VINDEX (function))
+ need_vtbl = fixed_type ? unneeded : needed;
+ else
+ need_vtbl = not_needed;
+ }
+
+ if (TREE_CODE (fntype) == METHOD_TYPE && static_call_context
+ && !DECL_CONSTRUCTOR_P (function))
+ {
+ /* Let's be nice to the user for now, and give reasonable
+ default behavior. */
+ instance_ptr = current_class_decl;
+ if (instance_ptr)
+ {
+ if (basetype != current_class_type)
+ {
+ tree binfo = get_binfo (basetype, current_class_type, 1);
+ if (binfo == NULL_TREE)
+ {
+ error_not_base_type (function, current_class_type);
+ return error_mark_node;
+ }
+ else if (basetype == error_mark_node)
+ return error_mark_node;
+ }
+ }
+ /* Only allow a static member function to call another static member
+ function. */
+ else if (DECL_LANG_SPECIFIC (function)
+ && !DECL_STATIC_FUNCTION_P (function))
+ {
+ cp_error ("cannot call member function `%D' without object",
+ function);
+ return error_mark_node;
+ }
+ }
+
+ value_type = TREE_TYPE (fntype) ? TREE_TYPE (fntype) : void_type_node;
+
+ if (TYPE_SIZE (value_type) == 0)
+ {
+ if (flags & LOOKUP_COMPLAIN)
+ incomplete_type_error (0, value_type);
+ return error_mark_node;
+ }
+
+ if (DECL_STATIC_FUNCTION_P (function))
+ parms = convert_arguments (NULL_TREE, TYPE_ARG_TYPES (fntype),
+ TREE_CHAIN (parms), function, LOOKUP_NORMAL);
+ else if (need_vtbl == unneeded)
+ {
+ int sub_flags = DECL_CONSTRUCTOR_P (function) ? flags : LOOKUP_NORMAL;
+ basetype = TREE_TYPE (instance);
+ if (TYPE_METHOD_BASETYPE (TREE_TYPE (function)) != TYPE_MAIN_VARIANT (basetype)
+ && TYPE_USES_COMPLEX_INHERITANCE (basetype))
+ {
+ basetype = DECL_CLASS_CONTEXT (function);
+ instance_ptr = convert_pointer_to (basetype, instance_ptr);
+ instance = build_indirect_ref (instance_ptr, NULL_PTR);
+ }
+ parms = tree_cons (NULL_TREE, instance_ptr,
+ convert_arguments (NULL_TREE, TREE_CHAIN (TYPE_ARG_TYPES (fntype)), TREE_CHAIN (parms), function, sub_flags));
+ }
+ else
+ {
+ if ((flags & LOOKUP_NONVIRTUAL) == 0)
+ basetype = DECL_CONTEXT (function);
+
+ /* First parm could be integer_zerop with casts like
+ ((Object*)0)->Object::IsA() */
+ if (!integer_zerop (TREE_VALUE (parms)))
+ {
+ /* Since we can't have inheritance with a union, doing get_binfo
+ on it won't work. We do all the convert_pointer_to_real
+ stuff to handle MI correctly...for unions, that's not
+ an issue, so we must short-circuit that extra work here. */
+ tree tmp = TREE_TYPE (TREE_TYPE (TREE_VALUE (parms)));
+ if (tmp != NULL_TREE && TREE_CODE (tmp) == UNION_TYPE)
+ instance_ptr = TREE_VALUE (parms);
+ else
+ {
+ tree binfo = get_binfo (basetype,
+ TREE_TYPE (TREE_TYPE (TREE_VALUE (parms))),
+ 0);
+ instance_ptr = convert_pointer_to_real (binfo, TREE_VALUE (parms));
+ }
+ instance_ptr
+ = convert_pointer_to (build_type_variant (basetype,
+ constp, volatilep),
+ instance_ptr);
+
+ if (TREE_CODE (instance_ptr) == COND_EXPR)
+ {
+ instance_ptr = save_expr (instance_ptr);
+ instance = build_indirect_ref (instance_ptr, NULL_PTR);
+ }
+ else if (TREE_CODE (instance_ptr) == NOP_EXPR
+ && TREE_CODE (TREE_OPERAND (instance_ptr, 0)) == ADDR_EXPR
+ && TREE_OPERAND (TREE_OPERAND (instance_ptr, 0), 0) == instance)
+ ;
+ /* The call to `convert_pointer_to' may return error_mark_node. */
+ else if (TREE_CODE (instance_ptr) == ERROR_MARK)
+ return instance_ptr;
+ else if (instance == NULL_TREE
+ || TREE_CODE (instance) != INDIRECT_REF
+ || TREE_OPERAND (instance, 0) != instance_ptr)
+ instance = build_indirect_ref (instance_ptr, NULL_PTR);
+ }
+ parms = tree_cons (NULL_TREE, instance_ptr,
+ convert_arguments (NULL_TREE, TREE_CHAIN (TYPE_ARG_TYPES (fntype)), TREE_CHAIN (parms), function, LOOKUP_NORMAL));
+ }
+
+#if 0
+ /* Constructors do not overload method calls. */
+ else if (TYPE_OVERLOADS_METHOD_CALL_EXPR (basetype)
+ && name != TYPE_IDENTIFIER (basetype)
+ && (TREE_CODE (function) != FUNCTION_DECL
+ || strncmp (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (function)),
+ OPERATOR_METHOD_FORMAT,
+ OPERATOR_METHOD_LENGTH))
+ && (may_be_remote (basetype) || instance != C_C_D))
+ {
+ tree fn_as_int;
+
+ parms = TREE_CHAIN (parms);
+
+ if (!all_virtual && TREE_CODE (function) == FUNCTION_DECL)
+ fn_as_int = build_unary_op (ADDR_EXPR, function, 0);
+ else
+ fn_as_int = convert (TREE_TYPE (default_conversion (function)), DECL_VINDEX (function));
+ if (all_virtual == 1)
+ fn_as_int = convert (integer_type_node, fn_as_int);
+
+ result = build_opfncall (METHOD_CALL_EXPR, LOOKUP_NORMAL, instance, fn_as_int, parms);
+
+ if (result == NULL_TREE)
+ {
+ compiler_error ("could not overload `operator->()(...)'");
+ return error_mark_node;
+ }
+ else if (result == error_mark_node)
+ return error_mark_node;
+
+#if 0
+ /* Do this if we want the result of operator->() to inherit
+ the type of the function it is subbing for. */
+ TREE_TYPE (result) = value_type;
+#endif
+
+ return result;
+ }
+#endif
+
+ if (need_vtbl == needed)
+ {
+ function = build_vfn_ref (&TREE_VALUE (parms), instance,
+ DECL_VINDEX (function));
+ TREE_TYPE (function) = build_pointer_type (fntype);
+ }
+
+ if (TREE_CODE (function) == FUNCTION_DECL)
+ GNU_xref_call (current_function_decl,
+ IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (function)));
+
+ {
+ int is_constructor;
+
+ if (TREE_CODE (function) == FUNCTION_DECL)
+ {
+ is_constructor = DECL_CONSTRUCTOR_P (function);
+ if (DECL_INLINE (function))
+ function = build1 (ADDR_EXPR, build_pointer_type (fntype), function);
+ else
+ {
+ assemble_external (function);
+ TREE_USED (function) = 1;
+ function = default_conversion (function);
+ }
+ }
+ else
+ {
+ is_constructor = 0;
+ function = default_conversion (function);
+ }
+
+ result = build_nt (CALL_EXPR, function, parms, NULL_TREE);
+
+ TREE_TYPE (result) = value_type;
+ TREE_SIDE_EFFECTS (result) = 1;
+ TREE_RAISES (result)
+ = TYPE_RAISES_EXCEPTIONS (fntype) || (parms && TREE_RAISES (parms));
+ TREE_HAS_CONSTRUCTOR (result) = is_constructor;
+ return result;
+ }
+}
+
+/* Similar to `build_method_call', but for overloaded non-member functions.
+ The name of this function comes through NAME. The name depends
+ on PARMS.
+
+ Note that this function must handle simple `C' promotions,
+ as well as variable numbers of arguments (...), and
+ default arguments to boot.
+
+ If the overloading is successful, we return a tree node which
+ contains the call to the function.
+
+ If overloading produces candidates which are probable, but not definite,
+ we hold these candidates. If FINAL_CP is non-zero, then we are free
+ to assume that final_cp points to enough storage for all candidates that
+ this function might generate. The `harshness' array is preallocated for
+ the first candidate, but not for subsequent ones.
+
+ Note that the DECL_RTL of FUNCTION must be made to agree with this
+ function's new name. */
+
+tree
+build_overload_call_real (fnname, parms, flags, final_cp, buildxxx)
+ tree fnname, parms;
+ int flags;
+ struct candidate *final_cp;
+ int buildxxx;
+{
+ /* must check for overloading here */
+ tree overload_name, functions, function, parm;
+ tree parmtypes = NULL_TREE, last = NULL_TREE;
+ register tree outer;
+ int length;
+ int parmlength = list_length (parms);
+
+ struct candidate *candidates, *cp;
+
+ if (final_cp)
+ {
+ final_cp[0].h.code = 0;
+ final_cp[0].h.distance = 0;
+ final_cp[0].function = 0;
+ /* end marker. */
+ final_cp[1].h.code = EVIL_CODE;
+ }
+
+ for (parm = parms; parm; parm = TREE_CHAIN (parm))
+ {
+ register tree t = TREE_TYPE (TREE_VALUE (parm));
+
+ if (t == error_mark_node)
+ {
+ if (final_cp)
+ final_cp->h.code = EVIL_CODE;
+ return error_mark_node;
+ }
+ if (TREE_CODE (t) == ARRAY_TYPE || TREE_CODE (t) == OFFSET_TYPE)
+ {
+ /* Perform the conversion from ARRAY_TYPE to POINTER_TYPE in place.
+ Also convert OFFSET_TYPE entities to their normal selves.
+ This eliminates needless calls to `compute_conversion_costs'. */
+ TREE_VALUE (parm) = default_conversion (TREE_VALUE (parm));
+ t = TREE_TYPE (TREE_VALUE (parm));
+ }
+ last = build_tree_list (NULL_TREE, t);
+ parmtypes = chainon (parmtypes, last);
+ }
+ if (last)
+ TREE_CHAIN (last) = void_list_node;
+ else
+ parmtypes = void_list_node;
+
+ if (is_overloaded_fn (fnname))
+ {
+ functions = fnname;
+ if (TREE_CODE (fnname) == TREE_LIST)
+ fnname = TREE_PURPOSE (functions);
+ else if (TREE_CODE (fnname) == FUNCTION_DECL)
+ fnname = DECL_NAME (functions);
+ }
+ else
+ functions = lookup_name_nonclass (fnname);
+
+ if (functions == NULL_TREE)
+ {
+ if (flags & LOOKUP_SPECULATIVELY)
+ return NULL_TREE;
+ if (flags & LOOKUP_COMPLAIN)
+ error ("only member functions apply");
+ if (final_cp)
+ final_cp->h.code = EVIL_CODE;
+ return error_mark_node;
+ }
+
+ if (TREE_CODE (functions) == FUNCTION_DECL && ! IDENTIFIER_OPNAME_P (fnname))
+ {
+ functions = DECL_MAIN_VARIANT (functions);
+ if (final_cp)
+ {
+ /* We are just curious whether this is a viable alternative or
+ not. */
+ compute_conversion_costs (functions, parms, final_cp, parmlength);
+ return functions;
+ }
+ else
+ return build_function_call_real (functions, parms, 1, flags);
+ }
+
+ if (TREE_CODE (functions) == TREE_LIST
+ && TREE_VALUE (functions) == NULL_TREE)
+ {
+ if (flags & LOOKUP_SPECULATIVELY)
+ return NULL_TREE;
+
+ if (flags & LOOKUP_COMPLAIN)
+ cp_error ("function `%D' declared overloaded, but no instances of that function declared",
+ TREE_PURPOSE (functions));
+ if (final_cp)
+ final_cp->h.code = EVIL_CODE;
+ return error_mark_node;
+ }
+
+ length = count_functions (functions);
+
+ if (final_cp)
+ candidates = final_cp;
+ else
+ {
+ candidates
+ = (struct candidate *)alloca ((length+1) * sizeof (struct candidate));
+ bzero (candidates, (length + 1) * sizeof (struct candidate));
+ }
+
+ cp = candidates;
+
+ my_friendly_assert (is_overloaded_fn (functions), 169);
+
+ functions = get_first_fn (functions);
+
+ /* OUTER is the list of FUNCTION_DECLS, in a TREE_LIST. */
+ for (outer = functions; outer; outer = DECL_CHAIN (outer))
+ {
+ int template_cost = 0;
+ function = outer;
+ if (TREE_CODE (function) != FUNCTION_DECL
+ && ! (TREE_CODE (function) == TEMPLATE_DECL
+ && ! DECL_TEMPLATE_IS_CLASS (function)
+ && TREE_CODE (DECL_TEMPLATE_RESULT (function)) == FUNCTION_DECL))
+ {
+ enum tree_code code = TREE_CODE (function);
+ if (code == TEMPLATE_DECL)
+ code = TREE_CODE (DECL_TEMPLATE_RESULT (function));
+ if (code == CONST_DECL)
+ cp_error_at
+ ("enumeral value `%D' conflicts with function of same name",
+ function);
+ else if (code == VAR_DECL)
+ {
+ if (TREE_STATIC (function))
+ cp_error_at
+ ("variable `%D' conflicts with function of same name",
+ function);
+ else
+ cp_error_at
+ ("constant field `%D' conflicts with function of same name",
+ function);
+ }
+ else if (code == TYPE_DECL)
+ continue;
+ else
+ my_friendly_abort (2);
+ error ("at this point in file");
+ continue;
+ }
+ if (TREE_CODE (function) == TEMPLATE_DECL)
+ {
+ int ntparms = TREE_VEC_LENGTH (DECL_TEMPLATE_PARMS (function));
+ tree *targs = (tree *) alloca (sizeof (tree) * ntparms);
+ int i;
+
+ i = type_unification (DECL_TEMPLATE_PARMS (function), targs,
+ TYPE_ARG_TYPES (TREE_TYPE (function)),
+ parms, &template_cost, 0);
+ if (i == 0)
+ function = instantiate_template (function, targs);
+ }
+
+ if (TREE_CODE (function) == TEMPLATE_DECL)
+ {
+ /* Unconverted template -- failed match. */
+ cp->function = function;
+ cp->u.bad_arg = -4;
+ cp->h.code = EVIL_CODE;
+ }
+ else
+ {
+ struct candidate *cp2;
+
+ /* Check that this decl is not the same as a function that's in
+ the list due to some template instantiation. */
+ cp2 = candidates;
+ while (cp2 != cp)
+ if (cp2->function == function)
+ break;
+ else
+ cp2 += 1;
+ if (cp2->function == function)
+ continue;
+
+ function = DECL_MAIN_VARIANT (function);
+
+ /* Can't use alloca here, since result might be
+ passed to calling function. */
+ cp->h_len = parmlength;
+ cp->harshness = (struct harshness_code *)
+ oballoc ((parmlength + 1) * sizeof (struct harshness_code));
+
+ compute_conversion_costs (function, parms, cp, parmlength);
+
+ /* Make sure this is clear as well. */
+ cp->h.int_penalty += template_cost;
+
+ if ((cp[0].h.code & EVIL_CODE) == 0)
+ {
+ cp[1].h.code = EVIL_CODE;
+ cp++;
+ }
+ }
+ }
+
+ if (cp - candidates)
+ {
+ tree rval = error_mark_node;
+
+ /* Leave marker. */
+ cp[0].h.code = EVIL_CODE;
+ if (cp - candidates > 1)
+ {
+ struct candidate *best_cp
+ = ideal_candidate (NULL_TREE, candidates,
+ cp - candidates, parms, parmlength);
+ if (best_cp == (struct candidate *)0)
+ {
+ if (flags & LOOKUP_COMPLAIN)
+ {
+ cp_error ("call of overloaded `%D' is ambiguous", fnname);
+ print_n_candidates (candidates, cp - candidates);
+ }
+ return error_mark_node;
+ }
+ else
+ rval = best_cp->function;
+ }
+ else
+ {
+ cp -= 1;
+ if (cp->h.code & EVIL_CODE)
+ {
+ if (flags & LOOKUP_COMPLAIN)
+ error ("type conversion ambiguous");
+ }
+ else
+ rval = cp->function;
+ }
+
+ if (final_cp)
+ return rval;
+
+ return buildxxx ? build_function_call_real (rval, parms, 0, flags)
+ : build_function_call_real (rval, parms, 1, flags);
+ }
+
+ if (flags & LOOKUP_SPECULATIVELY)
+ return NULL_TREE;
+
+ if (flags & LOOKUP_COMPLAIN)
+ report_type_mismatch (cp, parms, "function",
+ decl_as_string (cp->function, 1));
+
+ return error_mark_node;
+}
+
+tree
+build_overload_call (fnname, parms, flags, final_cp)
+ tree fnname, parms;
+ int flags;
+ struct candidate *final_cp;
+{
+ return build_overload_call_real (fnname, parms, flags, final_cp, 0);
+}
+
+tree
+build_overload_call_maybe (fnname, parms, flags, final_cp)
+ tree fnname, parms;
+ int flags;
+ struct candidate *final_cp;
+{
+ return build_overload_call_real (fnname, parms, flags, final_cp, 1);
+}
diff --git a/gnu/usr.bin/cc/cc1plus/class.c b/gnu/usr.bin/cc/cc1plus/class.c
new file mode 100644
index 0000000..e715c89
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/class.c
@@ -0,0 +1,4940 @@
+/* Functions related to building classes and their related objects.
+ Copyright (C) 1987, 1992, 1993, 1994 Free Software Foundation, Inc.
+ Contributed by Michael Tiemann (tiemann@cygnus.com)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* High-level class interface. */
+
+#include "config.h"
+#include "tree.h"
+#include <stdio.h>
+#include "cp-tree.h"
+#include "flags.h"
+
+#include "obstack.h"
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+extern struct obstack permanent_obstack;
+
+/* This is how we tell when two virtual member functions are really the
+ same. */
+#define SAME_FN(FN1DECL, FN2DECL) (DECL_ASSEMBLER_NAME (FN1DECL) == DECL_ASSEMBLER_NAME (FN2DECL))
+
+extern void set_class_shadows PROTO ((tree));
+
+/* Way of stacking class types. */
+static tree *current_class_base, *current_class_stack;
+static int current_class_stacksize;
+int current_class_depth;
+
+struct class_level
+{
+ /* The previous class level. */
+ struct class_level *level_chain;
+
+ /* The class instance variable, as a PARM_DECL. */
+ tree decl;
+ /* The class instance variable, as an object. */
+ tree object;
+ /* The virtual function table pointer
+ for the class instance variable. */
+ tree vtable_decl;
+
+ /* Name of the current class. */
+ tree name;
+ /* Type of the current class. */
+ tree type;
+
+ /* Flags for this class level. */
+ int this_is_variable;
+ int memoized_lookups;
+ int save_memoized;
+ int unused;
+};
+
+tree current_class_decl, C_C_D; /* PARM_DECL: the class instance variable */
+tree current_vtable_decl;
+
+/* The following two can be derived from the previous one */
+tree current_class_name; /* IDENTIFIER_NODE: name of current class */
+tree current_class_type; /* _TYPE: the type of the current class */
+tree previous_class_type; /* _TYPE: the previous type that was a class */
+tree previous_class_values; /* TREE_LIST: copy of the class_shadowed list
+ when leaving an outermost class scope. */
+static tree get_vfield_name PROTO((tree));
+tree the_null_vtable_entry;
+
+/* Way of stacking language names. */
+tree *current_lang_base, *current_lang_stack;
+int current_lang_stacksize;
+
+/* Names of languages we recognize. */
+tree lang_name_c, lang_name_cplusplus;
+tree current_lang_name;
+
+/* When layout out an aggregate type, the size of the
+ basetypes (virtual and non-virtual) is passed to layout_record
+ via this node. */
+static tree base_layout_decl;
+
+/* Variables shared between class.c and call.c. */
+
+int n_vtables = 0;
+int n_vtable_entries = 0;
+int n_vtable_searches = 0;
+int n_vtable_elems = 0;
+int n_convert_harshness = 0;
+int n_compute_conversion_costs = 0;
+int n_build_method_call = 0;
+int n_inner_fields_searched = 0;
+
+/* Virtual baseclass things. */
+tree
+build_vbase_pointer (exp, type)
+ tree exp, type;
+{
+ char *name;
+
+ name = (char *) alloca (TYPE_NAME_LENGTH (type) + sizeof (VBASE_NAME) + 1);
+ sprintf (name, VBASE_NAME_FORMAT, TYPE_NAME_STRING (type));
+ return build_component_ref (exp, get_identifier (name), 0, 0);
+}
+
+/* Is the type of the EXPR, the complete type of the object?
+ If we are going to be wrong, we must be conservative, and return 0. */
+int
+complete_type_p (expr)
+ tree expr;
+{
+ tree type = TYPE_MAIN_VARIANT (TREE_TYPE (expr));
+ while (1)
+ {
+ switch (TREE_CODE (expr))
+ {
+ case SAVE_EXPR:
+ case INDIRECT_REF:
+ case ADDR_EXPR:
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ expr = TREE_OPERAND (expr, 0);
+ continue;
+
+ case CALL_EXPR:
+ if (! TREE_HAS_CONSTRUCTOR (expr))
+ break;
+ /* fall through... */
+ case VAR_DECL:
+ case FIELD_DECL:
+ if (TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE
+ && IS_AGGR_TYPE (TREE_TYPE (TREE_TYPE (expr)))
+ && TYPE_MAIN_VARIANT (TREE_TYPE (expr)) == type)
+ return 1;
+ /* fall through... */
+ case TARGET_EXPR:
+ case PARM_DECL:
+ if (IS_AGGR_TYPE (TREE_TYPE (expr))
+ && TYPE_MAIN_VARIANT (TREE_TYPE (expr)) == type)
+ return 1;
+ /* fall through... */
+ case PLUS_EXPR:
+ default:
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+/* Build multi-level access to EXPR using hierarchy path PATH.
+ CODE is PLUS_EXPR if we are going with the grain,
+ and MINUS_EXPR if we are not (in which case, we cannot traverse
+ virtual baseclass links).
+
+ TYPE is the type we want this path to have on exit.
+
+ ALIAS_THIS is non-zero if EXPR in an expression involving `this'. */
+tree
+build_vbase_path (code, type, expr, path, alias_this)
+ enum tree_code code;
+ tree type, expr, path;
+ int alias_this;
+{
+ register int changed = 0;
+ tree last = NULL_TREE, last_virtual = NULL_TREE;
+ int nonnull = 0;
+ int fixed_type_p = resolves_to_fixed_type_p (expr, &nonnull);
+ tree null_expr = 0, nonnull_expr;
+ tree basetype;
+ tree offset = integer_zero_node;
+
+ /* We need additional logic to convert back to the unconverted type
+ (the static type of the complete object), and then convert back
+ to the type we want. Until that is done, or until we can
+ recognize when that is, we cannot do the short cut logic. (mrs) */
+ /* Do this, until we can undo any previous convertions. See net35.C
+ for a testcase. */
+ fixed_type_p = complete_type_p (expr);
+
+ if (!fixed_type_p && TREE_SIDE_EFFECTS (expr))
+ expr = save_expr (expr);
+ nonnull_expr = expr;
+
+ if (BINFO_INHERITANCE_CHAIN (path))
+ {
+ tree reverse_path = NULL_TREE;
+
+ while (path)
+ {
+ tree r = copy_node (path);
+ BINFO_INHERITANCE_CHAIN (r) = reverse_path;
+ reverse_path = r;
+ path = BINFO_INHERITANCE_CHAIN (path);
+ }
+ path = reverse_path;
+ }
+
+ basetype = BINFO_TYPE (path);
+
+ while (path)
+ {
+ if (TREE_VIA_VIRTUAL (path))
+ {
+ last_virtual = BINFO_TYPE (path);
+ if (code == PLUS_EXPR)
+ {
+ changed = ! fixed_type_p;
+
+ if (changed)
+ {
+ extern int flag_assume_nonnull_objects;
+ tree ind;
+
+ /* We already check for ambiguous things in the caller, just
+ find a path. */
+ if (last)
+ {
+ tree binfo = get_binfo (last, TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (nonnull_expr))), 0);
+ nonnull_expr = convert_pointer_to_real (binfo, nonnull_expr);
+ }
+ ind = build_indirect_ref (nonnull_expr, NULL_PTR);
+ nonnull_expr = build_vbase_pointer (ind, last_virtual);
+ if (nonnull == 0 && !flag_assume_nonnull_objects
+ && null_expr == NULL_TREE)
+ {
+ null_expr = build1 (NOP_EXPR, TYPE_POINTER_TO (last_virtual), integer_zero_node);
+ expr = build (COND_EXPR, TYPE_POINTER_TO (last_virtual),
+ build (EQ_EXPR, integer_type_node, expr,
+ integer_zero_node),
+ null_expr, nonnull_expr);
+ }
+ }
+ /* else we'll figure out the offset below. */
+
+ /* Happens in the case of parse errors. */
+ if (nonnull_expr == error_mark_node)
+ return error_mark_node;
+ }
+ else
+ {
+ cp_error ("cannot cast up from virtual baseclass `%T'",
+ last_virtual);
+ return error_mark_node;
+ }
+ }
+ last = path;
+ path = BINFO_INHERITANCE_CHAIN (path);
+ }
+ /* LAST is now the last basetype assoc on the path. */
+
+ /* A pointer to a virtual base member of a non-null object
+ is non-null. Therefore, we only need to test for zeroness once.
+ Make EXPR the canonical expression to deal with here. */
+ if (null_expr)
+ {
+ TREE_OPERAND (expr, 2) = nonnull_expr;
+ TREE_TYPE (TREE_OPERAND (expr, 1)) = TREE_TYPE (nonnull_expr);
+ }
+ else
+ expr = nonnull_expr;
+
+ /* If we go through any virtual base pointers, make sure that
+ casts to BASETYPE from the last virtual base class use
+ the right value for BASETYPE. */
+ if (changed)
+ {
+ tree intype = TREE_TYPE (TREE_TYPE (expr));
+ if (TYPE_MAIN_VARIANT (intype) == BINFO_TYPE (last))
+ basetype = intype;
+ else
+ {
+ tree binfo = get_binfo (last, TYPE_MAIN_VARIANT (intype), 0);
+ basetype = last;
+ offset = BINFO_OFFSET (binfo);
+ }
+ }
+ else
+ {
+ if (last_virtual)
+ {
+ offset = BINFO_OFFSET (binfo_member (last_virtual,
+ CLASSTYPE_VBASECLASSES (basetype)));
+ offset = size_binop (PLUS_EXPR, offset, BINFO_OFFSET (last));
+ }
+ else
+ offset = BINFO_OFFSET (last);
+ }
+
+ if (TREE_INT_CST_LOW (offset))
+ {
+ /* For multiple inheritance: if `this' can be set by any
+ function, then it could be 0 on entry to any function.
+ Preserve such zeroness here. Otherwise, only in the
+ case of constructors need we worry, and in those cases,
+ it will be zero, or initialized to some legal value to
+ which we may add. */
+ if (nonnull == 0 && (alias_this == 0 || flag_this_is_variable > 0))
+ {
+ if (null_expr)
+ TREE_TYPE (null_expr) = type;
+ else
+ null_expr = build1 (NOP_EXPR, type, integer_zero_node);
+ if (TREE_SIDE_EFFECTS (expr))
+ expr = save_expr (expr);
+
+ return build (COND_EXPR, type,
+ build (EQ_EXPR, integer_type_node, expr, integer_zero_node),
+ null_expr,
+ build (code, type, expr, offset));
+ }
+ else return build (code, type, expr, offset);
+ }
+
+ /* Cannot change the TREE_TYPE of a NOP_EXPR here, since it may
+ be used multiple times in initialization of multiple inheritance. */
+ if (null_expr)
+ {
+ TREE_TYPE (expr) = type;
+ return expr;
+ }
+ else
+ return build1 (NOP_EXPR, type, expr);
+}
+
+/* Virtual function things. */
+
+/* Virtual functions to be dealt with after laying out our base
+ classes. We do all overrides after we layout virtual base classes.
+ */
+static tree pending_hard_virtuals;
+static int doing_hard_virtuals;
+
+/* Build an entry in the virtual function table.
+ DELTA is the offset for the `this' pointer.
+ PFN is an ADDR_EXPR containing a pointer to the virtual function.
+ Note that the index (DELTA2) in the virtual function table
+ is always 0. */
+tree
+build_vtable_entry (delta, pfn)
+ tree delta, pfn;
+{
+
+ if (flag_vtable_thunks)
+ {
+ HOST_WIDE_INT idelta = TREE_INT_CST_LOW (delta);
+ extern tree make_thunk ();
+ if (idelta)
+ {
+ pfn = build1 (ADDR_EXPR, vtable_entry_type,
+ make_thunk (pfn, idelta));
+ TREE_READONLY (pfn) = 1;
+ TREE_CONSTANT (pfn) = 1;
+ }
+#ifdef GATHER_STATISTICS
+ n_vtable_entries += 1;
+#endif
+ return pfn;
+ }
+ else
+ {
+ extern int flag_huge_objects;
+ tree elems = tree_cons (NULL_TREE, delta,
+ tree_cons (NULL_TREE, integer_zero_node,
+ build_tree_list (NULL_TREE, pfn)));
+ tree entry = build (CONSTRUCTOR, vtable_entry_type, NULL_TREE, elems);
+
+ /* DELTA is constructed by `size_int', which means it may be an
+ unsigned quantity on some platforms. Therefore, we cannot use
+ `int_fits_type_p', because when DELTA is really negative,
+ `force_fit_type' will make it look like a very large number. */
+
+ if ((TREE_INT_CST_LOW (TYPE_MAX_VALUE (delta_type_node))
+ < TREE_INT_CST_LOW (delta))
+ || (TREE_INT_CST_LOW (delta)
+ < TREE_INT_CST_LOW (TYPE_MIN_VALUE (delta_type_node))))
+ if (flag_huge_objects)
+ sorry ("object size exceeds built-in limit for virtual function table implementation");
+ else
+ sorry ("object size exceeds normal limit for virtual function table implementation, recompile all source and use -fhuge-objects");
+
+ TREE_CONSTANT (entry) = 1;
+ TREE_STATIC (entry) = 1;
+ TREE_READONLY (entry) = 1;
+
+#ifdef GATHER_STATISTICS
+ n_vtable_entries += 1;
+#endif
+
+ return entry;
+ }
+}
+
+/* Given an object INSTANCE, return an expression which yields the
+ virtual function corresponding to INDEX. There are many special
+ cases for INSTANCE which we take care of here, mainly to avoid
+ creating extra tree nodes when we don't have to. */
+tree
+build_vfn_ref (ptr_to_instptr, instance, idx)
+ tree *ptr_to_instptr, instance;
+ tree idx;
+{
+ extern int building_cleanup;
+ tree vtbl, aref;
+ tree basetype = TREE_TYPE (instance);
+
+ if (TREE_CODE (basetype) == REFERENCE_TYPE)
+ basetype = TREE_TYPE (basetype);
+
+ if (instance == C_C_D)
+ {
+ if (current_vtable_decl == NULL_TREE
+ || current_vtable_decl == error_mark_node
+ || !UNIQUELY_DERIVED_FROM_P (DECL_FCONTEXT (CLASSTYPE_VFIELD (current_class_type)), basetype))
+ vtbl = build_indirect_ref (build_vfield_ref (instance, basetype), NULL_PTR);
+ else
+ vtbl = current_vtable_decl;
+ }
+ else
+ {
+ if (optimize)
+ {
+ /* Try to figure out what a reference refers to, and
+ access its virtual function table directly. */
+ tree ref = NULL_TREE;
+
+ if (TREE_CODE (instance) == INDIRECT_REF
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (instance, 0))) == REFERENCE_TYPE)
+ ref = TREE_OPERAND (instance, 0);
+ else if (TREE_CODE (TREE_TYPE (instance)) == REFERENCE_TYPE)
+ ref = instance;
+
+ if (ref && TREE_CODE (ref) == VAR_DECL
+ && DECL_INITIAL (ref))
+ {
+ tree init = DECL_INITIAL (ref);
+
+ while (TREE_CODE (init) == NOP_EXPR
+ || TREE_CODE (init) == NON_LVALUE_EXPR)
+ init = TREE_OPERAND (init, 0);
+ if (TREE_CODE (init) == ADDR_EXPR)
+ {
+ init = TREE_OPERAND (init, 0);
+ if (IS_AGGR_TYPE (TREE_TYPE (init))
+ && (TREE_CODE (init) == PARM_DECL
+ || TREE_CODE (init) == VAR_DECL))
+ instance = init;
+ }
+ }
+ }
+
+ if (IS_AGGR_TYPE (TREE_TYPE (instance))
+ && !IS_SIGNATURE_POINTER (TREE_TYPE (instance))
+ && !IS_SIGNATURE_REFERENCE (TREE_TYPE (instance))
+ && (TREE_CODE (instance) == RESULT_DECL
+ || TREE_CODE (instance) == PARM_DECL
+ || TREE_CODE (instance) == VAR_DECL))
+ vtbl = TYPE_BINFO_VTABLE (basetype);
+ else
+ vtbl = build_indirect_ref (build_vfield_ref (instance, basetype),
+ NULL_PTR);
+ }
+ if (!flag_vtable_thunks)
+ assemble_external (vtbl);
+ aref = build_array_ref (vtbl, idx);
+
+ /* Save the intermediate result in a SAVE_EXPR so we don't have to
+ compute each component of the virtual function pointer twice. */
+ if (!building_cleanup && TREE_CODE (aref) == INDIRECT_REF)
+ TREE_OPERAND (aref, 0) = save_expr (TREE_OPERAND (aref, 0));
+
+ if (flag_vtable_thunks)
+ return aref;
+ else
+ {
+ *ptr_to_instptr
+ = build (PLUS_EXPR, TREE_TYPE (*ptr_to_instptr),
+ *ptr_to_instptr,
+ convert (ptrdiff_type_node,
+ build_component_ref (aref, delta_identifier, 0, 0)));
+ return build_component_ref (aref, pfn_identifier, 0, 0);
+ }
+}
+
+/* Return the name of the virtual function table (as an IDENTIFIER_NODE)
+ for the given TYPE. */
+static tree
+get_vtable_name (type)
+ tree type;
+{
+ tree type_id = build_typename_overload (type);
+ char *buf = (char *)alloca (strlen (VTABLE_NAME_FORMAT)
+ + IDENTIFIER_LENGTH (type_id) + 2);
+ char *ptr = IDENTIFIER_POINTER (type_id);
+ int i;
+ for (i = 0; ptr[i] == OPERATOR_TYPENAME_FORMAT[i]; i++) ;
+#if 0
+ /* We don't take off the numbers; prepare_fresh_vtable uses the
+ DECL_ASSEMBLER_NAME for the type, which includes the number
+ in `3foo'. If we were to pull them off here, we'd end up with
+ something like `_vt.foo.3bar', instead of a uniform definition. */
+ while (ptr[i] >= '0' && ptr[i] <= '9')
+ i += 1;
+#endif
+ sprintf (buf, VTABLE_NAME_FORMAT, ptr+i);
+ return get_identifier (buf);
+}
+
+/* Build a virtual function for type TYPE.
+ If BINFO is non-NULL, build the vtable starting with the initial
+ approximation that it is the same as the one which is the head of
+ the association list. */
+static tree
+build_vtable (binfo, type)
+ tree binfo, type;
+{
+ tree name = get_vtable_name (type);
+ tree virtuals, decl;
+
+ if (binfo)
+ {
+ virtuals = copy_list (BINFO_VIRTUALS (binfo));
+ decl = build_decl (VAR_DECL, name, TREE_TYPE (BINFO_VTABLE (binfo)));
+ }
+ else
+ {
+ virtuals = NULL_TREE;
+ decl = build_decl (VAR_DECL, name, void_type_node);
+ }
+
+#ifdef GATHER_STATISTICS
+ n_vtables += 1;
+ n_vtable_elems += list_length (virtuals);
+#endif
+
+ /* Set TREE_PUBLIC and TREE_EXTERN as appropriate. */
+ if (! flag_vtable_thunks)
+ import_export_vtable (decl, type);
+
+ IDENTIFIER_GLOBAL_VALUE (name) = decl = pushdecl_top_level (decl);
+ /* Initialize the association list for this type, based
+ on our first approximation. */
+ TYPE_BINFO_VTABLE (type) = decl;
+ TYPE_BINFO_VIRTUALS (type) = virtuals;
+
+ TREE_STATIC (decl) = 1;
+#ifndef WRITABLE_VTABLES
+ /* Make them READONLY by default. (mrs) */
+ TREE_READONLY (decl) = 1;
+#endif
+ /* At one time the vtable info was grabbed 2 words at a time. This
+ fails on sparc unless you have 8-byte alignment. (tiemann) */
+ DECL_ALIGN (decl) = MAX (TYPE_ALIGN (double_type_node),
+ DECL_ALIGN (decl));
+
+ /* Why is this conditional? (mrs) */
+ if (binfo && write_virtuals >= 0)
+ DECL_VIRTUAL_P (decl) = 1;
+ DECL_CONTEXT (decl) = type;
+
+ binfo = TYPE_BINFO (type);
+ SET_BINFO_NEW_VTABLE_MARKED (binfo);
+ return decl;
+}
+
+/* Given a base type PARENT, and a derived type TYPE, build
+ a name which distinguishes exactly the PARENT member of TYPE's type.
+
+ FORMAT is a string which controls how sprintf formats the name
+ we have generated.
+
+ For example, given
+
+ class A; class B; class C : A, B;
+
+ it is possible to distinguish "A" from "C's A". And given
+
+ class L;
+ class A : L; class B : L; class C : A, B;
+
+ it is possible to distinguish "L" from "A's L", and also from
+ "C's L from A".
+
+ Make sure to use the DECL_ASSEMBLER_NAME of the TYPE_NAME of the
+ type, as template have DECL_NAMEs like: X<int>, whereas the
+ DECL_ASSEMBLER_NAME is set to be something the assembler can handle.
+ */
+static tree
+build_type_pathname (format, parent, type)
+ char *format;
+ tree parent, type;
+{
+ extern struct obstack temporary_obstack;
+ char *first, *base, *name;
+ int i;
+ tree id;
+
+ parent = TYPE_MAIN_VARIANT (parent);
+
+ /* Remember where to cut the obstack to. */
+ first = obstack_base (&temporary_obstack);
+
+ /* Put on TYPE+PARENT. */
+ obstack_grow (&temporary_obstack,
+ TYPE_ASSEMBLER_NAME_STRING (type),
+ TYPE_ASSEMBLER_NAME_LENGTH (type));
+#ifdef JOINER
+ obstack_1grow (&temporary_obstack, JOINER);
+#else
+ obstack_1grow (&temporary_obstack, '_');
+#endif
+ obstack_grow0 (&temporary_obstack,
+ TYPE_ASSEMBLER_NAME_STRING (parent),
+ TYPE_ASSEMBLER_NAME_LENGTH (parent));
+ i = obstack_object_size (&temporary_obstack);
+ base = obstack_base (&temporary_obstack);
+ obstack_finish (&temporary_obstack);
+
+ /* Put on FORMAT+TYPE+PARENT. */
+ obstack_blank (&temporary_obstack, strlen (format) + i + 1);
+ name = obstack_base (&temporary_obstack);
+ sprintf (name, format, base);
+ id = get_identifier (name);
+ obstack_free (&temporary_obstack, first);
+
+ return id;
+}
+
+/* Give TYPE a new virtual function table which is initialized
+ with a skeleton-copy of its original initialization. The only
+ entry that changes is the `delta' entry, so we can really
+ share a lot of structure.
+
+ FOR_TYPE is the derived type which caused this table to
+ be needed.
+
+ BINFO is the type association which provided TYPE for FOR_TYPE. */
+static void
+prepare_fresh_vtable (binfo, for_type)
+ tree binfo, for_type;
+{
+ tree basetype = BINFO_TYPE (binfo);
+ tree orig_decl = BINFO_VTABLE (binfo);
+ /* This name is too simplistic. We can have multiple basetypes for
+ for_type, and we really want different names. (mrs) */
+ tree name = build_type_pathname (VTABLE_NAME_FORMAT, basetype, for_type);
+ tree new_decl = build_decl (VAR_DECL, name, TREE_TYPE (orig_decl));
+ tree path;
+ int result;
+
+ /* Remember which class this vtable is really for. */
+ DECL_CONTEXT (new_decl) = for_type;
+
+ TREE_STATIC (new_decl) = 1;
+ BINFO_VTABLE (binfo) = pushdecl_top_level (new_decl);
+ DECL_VIRTUAL_P (new_decl) = 1;
+#ifndef WRITABLE_VTABLES
+ /* Make them READONLY by default. (mrs) */
+ TREE_READONLY (new_decl) = 1;
+#endif
+ DECL_ALIGN (new_decl) = DECL_ALIGN (orig_decl);
+
+ /* Make fresh virtual list, so we can smash it later. */
+ BINFO_VIRTUALS (binfo) = copy_list (BINFO_VIRTUALS (binfo));
+ /* Install the value for `headof' if that's what we're doing. */
+ if (flag_dossier)
+ TREE_VALUE (TREE_CHAIN (BINFO_VIRTUALS (binfo)))
+ = build_vtable_entry (size_binop (MINUS_EXPR, integer_zero_node, BINFO_OFFSET (binfo)),
+ FNADDR_FROM_VTABLE_ENTRY (TREE_VALUE (TREE_CHAIN (BINFO_VIRTUALS (binfo)))));
+
+#ifdef GATHER_STATISTICS
+ n_vtables += 1;
+ n_vtable_elems += list_length (BINFO_VIRTUALS (binfo));
+#endif
+
+ /* Set TREE_PUBLIC and TREE_EXTERN as appropriate. */
+ if (! flag_vtable_thunks)
+ import_export_vtable (new_decl, for_type);
+
+ if (TREE_VIA_VIRTUAL (binfo))
+ my_friendly_assert (binfo == binfo_member (BINFO_TYPE (binfo),
+ CLASSTYPE_VBASECLASSES (current_class_type)),
+ 170);
+ SET_BINFO_NEW_VTABLE_MARKED (binfo);
+}
+
+/* Access the virtual function table entry that logically
+ contains BASE_FNDECL. VIRTUALS is the virtual function table's
+ initializer. We can run off the end, when dealing with virtual
+ destructors in MI situations, return NULL_TREE in that case. */
+static tree
+get_vtable_entry (virtuals, base_fndecl)
+ tree virtuals, base_fndecl;
+{
+ unsigned HOST_WIDE_INT i = (HOST_BITS_PER_WIDE_INT >= BITS_PER_WORD
+ ? (TREE_INT_CST_LOW (DECL_VINDEX (base_fndecl))
+ & (((unsigned HOST_WIDE_INT)1<<(BITS_PER_WORD-1))-1))
+ : TREE_INT_CST_LOW (DECL_VINDEX (base_fndecl)));
+
+#ifdef GATHER_STATISTICS
+ n_vtable_searches += i;
+#endif
+
+ while (i > 0 && virtuals)
+ {
+ virtuals = TREE_CHAIN (virtuals);
+ i -= 1;
+ }
+ return virtuals;
+}
+
+/* Put new entry ENTRY into virtual function table initializer
+ VIRTUALS.
+
+ Also update DECL_VINDEX (FNDECL). */
+
+static void
+modify_vtable_entry (old_entry_in_list, new_entry, fndecl)
+ tree old_entry_in_list, new_entry, fndecl;
+{
+ tree base_fndecl = TREE_OPERAND (FNADDR_FROM_VTABLE_ENTRY (TREE_VALUE (old_entry_in_list)), 0);
+
+#ifdef NOTQUITE
+ cp_warning ("replaced %D with %D", DECL_ASSEMBLER_NAME (base_fndecl),
+ DECL_ASSEMBLER_NAME (fndecl));
+#endif
+ TREE_VALUE (old_entry_in_list) = new_entry;
+
+ /* Now assign virtual dispatch information, if unset. */
+ /* We can dispatch this, through any overridden base function. */
+ if (TREE_CODE (DECL_VINDEX (fndecl)) != INTEGER_CST)
+ {
+ DECL_VINDEX (fndecl) = DECL_VINDEX (base_fndecl);
+ DECL_CONTEXT (fndecl) = DECL_CONTEXT (base_fndecl);
+ }
+}
+
+/* Access the virtual function table entry i. VIRTUALS is the virtual
+ function table's initializer. */
+static tree
+get_vtable_entry_n (virtuals, i)
+ tree virtuals;
+ unsigned HOST_WIDE_INT i;
+{
+ while (i > 0)
+ {
+ virtuals = TREE_CHAIN (virtuals);
+ i -= 1;
+ }
+ return virtuals;
+}
+
+/* Add a virtual function to all the appropriate vtables for the class
+ T. DECL_VINDEX(X) should be error_mark_node, if we want to
+ allocate a new slot in our table. If it is error_mark_node, we
+ know that no other function from another vtable is overridden by X.
+ HAS_VIRTUAL keeps track of how many virtuals there are in our main
+ vtable for the type, and we build upon the PENDING_VIRTUALS list
+ and return it. */
+static tree
+add_virtual_function (pending_virtuals, has_virtual, fndecl, t)
+ tree pending_virtuals;
+ int *has_virtual;
+ tree fndecl;
+ tree t; /* Structure type. */
+{
+ /* FUNCTION_TYPEs and OFFSET_TYPEs no longer freely
+ convert to void *. Make such a conversion here. */
+ tree vfn = build1 (ADDR_EXPR, vfunc_ptr_type_node, fndecl);
+ TREE_CONSTANT (vfn) = 1;
+
+#ifndef DUMB_USER
+ if (current_class_type == 0)
+ cp_warning ("internal problem, current_class_type is zero when adding `%D', please report",
+ fndecl);
+ if (current_class_type && t != current_class_type)
+ cp_warning ("internal problem, current_class_type differs when adding `%D', please report",
+ fndecl);
+#endif
+
+ if (!flag_vtable_thunks)
+ TREE_ADDRESSABLE (fndecl) = CLASSTYPE_VTABLE_NEEDS_WRITING (t);
+
+ /* If the virtual function is a redefinition of a prior one,
+ figure out in which base class the new definition goes,
+ and if necessary, make a fresh virtual function table
+ to hold that entry. */
+ if (DECL_VINDEX (fndecl) == error_mark_node)
+ {
+ tree entry;
+
+ if (flag_dossier && *has_virtual == 0)
+ {
+ /* CLASSTYPE_DOSSIER is only used as a Boolean (NULL or not). */
+ CLASSTYPE_DOSSIER (t) = integer_one_node;
+ *has_virtual = 1;
+ }
+
+ /* Build a new INT_CST for this DECL_VINDEX. */
+ {
+ static tree index_table[256];
+ tree index;
+ int i = ++(*has_virtual);
+
+ if (i >= 256 || index_table[i] == 0)
+ {
+ index = build_int_2 (i, 0);
+ if (i < 256)
+ index_table[i] = index;
+ }
+ else
+ index = index_table[i];
+
+ /* Now assign virtual dispatch information. */
+ DECL_VINDEX (fndecl) = index;
+ DECL_CONTEXT (fndecl) = t;
+ }
+ entry = build_vtable_entry (integer_zero_node, vfn);
+ pending_virtuals = tree_cons (DECL_VINDEX (fndecl), entry, pending_virtuals);
+ }
+ /* Might already be INTEGER_CST if declared twice in class. We will
+ give error later or we've already given it. */
+ else if (TREE_CODE (DECL_VINDEX (fndecl)) != INTEGER_CST)
+ {
+ /* Need an entry in some other virtual function table.
+ Deal with this after we have laid out our virtual base classes. */
+ pending_hard_virtuals = temp_tree_cons (fndecl, vfn, pending_hard_virtuals);
+ }
+ return pending_virtuals;
+}
+
+/* Obstack on which to build the vector of class methods. */
+struct obstack class_obstack;
+extern struct obstack *current_obstack;
+
+/* Add method METHOD to class TYPE. This is used when a method
+ has been defined which did not initially appear in the class definition,
+ and helps cut down on spurious error messages.
+
+ FIELDS is the entry in the METHOD_VEC vector entry of the class type where
+ the method should be added. */
+void
+add_method (type, fields, method)
+ tree type, *fields, method;
+{
+ /* We must make a copy of METHOD here, since we must be sure that
+ we have exclusive title to this method's DECL_CHAIN. */
+ tree decl;
+
+ push_obstacks (&permanent_obstack, &permanent_obstack);
+ {
+ decl = copy_node (method);
+ if (DECL_RTL (decl) == 0
+ && (!processing_template_decl
+ || !uses_template_parms (decl)))
+ {
+ make_function_rtl (decl);
+ DECL_RTL (method) = DECL_RTL (decl);
+ }
+ }
+
+ if (fields && *fields)
+ {
+ /* Take care not to hide destructor. */
+ DECL_CHAIN (decl) = DECL_CHAIN (*fields);
+ DECL_CHAIN (*fields) = decl;
+ }
+ else if (CLASSTYPE_METHOD_VEC (type) == 0)
+ {
+ tree method_vec = make_node (TREE_VEC);
+ if (TYPE_IDENTIFIER (type) == DECL_NAME (decl))
+ {
+ TREE_VEC_ELT (method_vec, 0) = decl;
+ TREE_VEC_LENGTH (method_vec) = 1;
+ }
+ else
+ {
+ /* ??? Is it possible for there to have been enough room in the
+ current chunk for the tree_vec structure but not a tree_vec
+ plus a tree*? Will this work in that case? */
+ obstack_free (current_obstack, method_vec);
+ obstack_blank (current_obstack, sizeof (struct tree_vec) + sizeof (tree *));
+ TREE_VEC_ELT (method_vec, 1) = decl;
+ TREE_VEC_LENGTH (method_vec) = 2;
+ obstack_finish (current_obstack);
+ }
+ CLASSTYPE_METHOD_VEC (type) = method_vec;
+ }
+ else
+ {
+ tree method_vec = CLASSTYPE_METHOD_VEC (type);
+ int len = TREE_VEC_LENGTH (method_vec);
+
+ /* Adding a new ctor or dtor. This is easy because our
+ METHOD_VEC always has a slot for such entries. */
+ if (TYPE_IDENTIFIER (type) == DECL_NAME (decl))
+ {
+ /* TREE_VEC_ELT (method_vec, 0) = decl; */
+ if (decl != TREE_VEC_ELT (method_vec, 0))
+ {
+ DECL_CHAIN (decl) = TREE_VEC_ELT (method_vec, 0);
+ TREE_VEC_ELT (method_vec, 0) = decl;
+ }
+ }
+ else
+ {
+ /* This is trickier. We try to extend the TREE_VEC in-place,
+ but if that does not work, we copy all its data to a new
+ TREE_VEC that's large enough. */
+ struct obstack *ob = &class_obstack;
+ tree *end = (tree *)obstack_next_free (ob);
+
+ if (end != TREE_VEC_END (method_vec))
+ {
+ ob = current_obstack;
+ TREE_VEC_LENGTH (method_vec) += 1;
+ TREE_VEC_ELT (method_vec, len) = NULL_TREE;
+ method_vec = copy_node (method_vec);
+ TREE_VEC_LENGTH (method_vec) -= 1;
+ }
+ else
+ {
+ tree tmp_vec = (tree) obstack_base (ob);
+ if (obstack_room (ob) < sizeof (tree))
+ {
+ obstack_blank (ob, sizeof (struct tree_common)
+ + tree_code_length[(int) TREE_VEC]
+ * sizeof (char *)
+ + len * sizeof (tree));
+ tmp_vec = (tree) obstack_base (ob);
+ bcopy (method_vec, tmp_vec,
+ (sizeof (struct tree_common)
+ + tree_code_length[(int) TREE_VEC] * sizeof (char *)
+ + (len-1) * sizeof (tree)));
+ method_vec = tmp_vec;
+ }
+ else
+ obstack_blank (ob, sizeof (tree));
+ }
+
+ obstack_finish (ob);
+ TREE_VEC_ELT (method_vec, len) = decl;
+ TREE_VEC_LENGTH (method_vec) = len + 1;
+ CLASSTYPE_METHOD_VEC (type) = method_vec;
+
+ if (TYPE_BINFO_BASETYPES (type) && CLASSTYPE_BASELINK_VEC (type))
+ {
+ /* ??? May be better to know whether these can be extended? */
+ tree baselink_vec = CLASSTYPE_BASELINK_VEC (type);
+
+ TREE_VEC_LENGTH (baselink_vec) += 1;
+ CLASSTYPE_BASELINK_VEC (type) = copy_node (baselink_vec);
+ TREE_VEC_LENGTH (baselink_vec) -= 1;
+
+ TREE_VEC_ELT (CLASSTYPE_BASELINK_VEC (type), len) = 0;
+ }
+ }
+ }
+ DECL_CONTEXT (decl) = type;
+ DECL_CLASS_CONTEXT (decl) = type;
+
+ pop_obstacks ();
+}
+
+/* Subroutines of finish_struct. */
+
+/* Look through the list of fields for this struct, deleting
+ duplicates as we go. This must be recursive to handle
+ anonymous unions.
+
+ FIELD is the field which may not appear anywhere in FIELDS.
+ FIELD_PTR, if non-null, is the starting point at which
+ chained deletions may take place.
+ The value returned is the first acceptable entry found
+ in FIELDS.
+
+ Note that anonymous fields which are not of UNION_TYPE are
+ not duplicates, they are just anonymous fields. This happens
+ when we have unnamed bitfields, for example. */
+static tree
+delete_duplicate_fields_1 (field, field_ptr, fields)
+ tree field, *field_ptr, fields;
+{
+ tree x;
+ tree prev = field_ptr ? *field_ptr : 0;
+ if (DECL_NAME (field) == 0)
+ {
+ if (TREE_CODE (TREE_TYPE (field)) != UNION_TYPE)
+ return fields;
+
+ for (x = TYPE_FIELDS (TREE_TYPE (field)); x; x = TREE_CHAIN (x))
+ fields = delete_duplicate_fields_1 (x, field_ptr, fields);
+ if (prev)
+ TREE_CHAIN (prev) = fields;
+ return fields;
+ }
+ else
+ {
+ for (x = fields; x; prev = x, x = TREE_CHAIN (x))
+ {
+ if (DECL_NAME (x) == 0)
+ {
+ if (TREE_CODE (TREE_TYPE (x)) != UNION_TYPE)
+ continue;
+ TYPE_FIELDS (TREE_TYPE (x))
+ = delete_duplicate_fields_1 (field, (tree *)0, TYPE_FIELDS (TREE_TYPE (x)));
+ if (TYPE_FIELDS (TREE_TYPE (x)) == 0)
+ {
+ if (prev == 0)
+ fields = TREE_CHAIN (fields);
+ else
+ TREE_CHAIN (prev) = TREE_CHAIN (x);
+ }
+ }
+ else
+ {
+ if (DECL_NAME (field) == DECL_NAME (x))
+ {
+ if (TREE_CODE (field) == CONST_DECL
+ && TREE_CODE (x) == CONST_DECL)
+ cp_error_at ("duplicate enum value `%D'", x);
+ else if (TREE_CODE (field) == CONST_DECL
+ || TREE_CODE (x) == CONST_DECL)
+ cp_error_at ("duplicate field `%D' (as enum and non-enum)",
+ x);
+ else if (TREE_CODE (field) == TYPE_DECL
+ && TREE_CODE (x) == TYPE_DECL)
+ cp_error_at ("duplicate class scope type `%D'", x);
+ else if (TREE_CODE (field) == TYPE_DECL
+ || TREE_CODE (x) == TYPE_DECL)
+ cp_error_at ("duplicate field `%D' (as type and non-type)",
+ x);
+ else
+ cp_error_at ("duplicate member `%D'", x);
+ if (prev == 0)
+ fields = TREE_CHAIN (fields);
+ else
+ TREE_CHAIN (prev) = TREE_CHAIN (x);
+ }
+ }
+ }
+ }
+ return fields;
+}
+
+static void
+delete_duplicate_fields (fields)
+ tree fields;
+{
+ tree x;
+ for (x = fields; x && TREE_CHAIN (x); x = TREE_CHAIN (x))
+ TREE_CHAIN (x) = delete_duplicate_fields_1 (x, &x, TREE_CHAIN (x));
+}
+
+/* Change the access of FDECL to ACCESS in T.
+ Return 1 if change was legit, otherwise return 0. */
+static int
+alter_access (t, fdecl, access)
+ tree t;
+ tree fdecl;
+ enum access_type access;
+{
+ tree elem = purpose_member (t, DECL_ACCESS (fdecl));
+ if (elem && TREE_VALUE (elem) != (tree)access)
+ {
+ if (TREE_CODE (TREE_TYPE (fdecl)) == FUNCTION_DECL)
+ {
+ cp_error_at ("conflicting access specifications for method `%D', ignored", TREE_TYPE (fdecl));
+ }
+ else
+ error ("conflicting access specifications for field `%s', ignored",
+ IDENTIFIER_POINTER (DECL_NAME (fdecl)));
+ }
+ else if (TREE_PRIVATE (fdecl) && access != access_private)
+ cp_error_at ("cannot make private `%D' non-private", fdecl);
+ else if (TREE_PROTECTED (fdecl))
+ {
+ if (access == access_public)
+ cp_error_at ("cannot make protected `%D' public", fdecl);
+ goto alter;
+ }
+ /* ARM 11.3: an access declaration may not be used to restrict access
+ to a member that is accessible in the base class. */
+ else if (TREE_PUBLIC (fdecl)
+ && (access == access_private
+ || access == access_protected))
+ cp_error_at ("cannot reduce access of public member `%D'", fdecl);
+ else if (elem == NULL_TREE)
+ {
+ alter:
+ DECL_ACCESS (fdecl) = tree_cons (t, (tree)access,
+ DECL_ACCESS (fdecl));
+ return 1;
+ }
+ return 0;
+}
+
+/* Return the offset to the main vtable for a given base BINFO. */
+tree
+get_vfield_offset (binfo)
+ tree binfo;
+{
+ return size_binop (PLUS_EXPR,
+ size_binop (FLOOR_DIV_EXPR,
+ DECL_FIELD_BITPOS (CLASSTYPE_VFIELD (BINFO_TYPE (binfo))),
+ size_int (BITS_PER_UNIT)),
+ BINFO_OFFSET (binfo));
+}
+
+/* Get the offset to the start of the original binfo that we derived this
+ binfo from. */
+tree get_derived_offset (binfo)
+ tree binfo;
+{
+ tree offset1 = get_vfield_offset (TYPE_BINFO (BINFO_TYPE (binfo)));
+ tree offset2;
+ int i;
+ while (BINFO_BASETYPES (binfo)
+ && (i=CLASSTYPE_VFIELD_PARENT (BINFO_TYPE (binfo))) != -1)
+ {
+ tree binfos = BINFO_BASETYPES (binfo);
+ binfo = TREE_VEC_ELT (binfos, i);
+ }
+ offset2 = get_vfield_offset (TYPE_BINFO (BINFO_TYPE (binfo)));
+ return size_binop (MINUS_EXPR, offset1, offset2);
+}
+
+/* If FOR_TYPE needs to reinitialize virtual function table pointers
+ for TYPE's sub-objects, add such reinitializations to BASE_INIT_LIST.
+ Returns BASE_INIT_LIST appropriately modified. */
+
+static tree
+maybe_fixup_vptrs (for_type, binfo, base_init_list)
+ tree for_type, binfo, base_init_list;
+{
+ /* Now reinitialize any slots that don't fall under our virtual
+ function table pointer. */
+ tree vfields = CLASSTYPE_VFIELDS (BINFO_TYPE (binfo));
+ while (vfields)
+ {
+ tree basetype = VF_NORMAL_VALUE (vfields)
+ ? TYPE_MAIN_VARIANT (VF_NORMAL_VALUE (vfields))
+ : VF_BASETYPE_VALUE (vfields);
+
+ tree base_binfo = get_binfo (basetype, for_type, 0);
+ /* Punt until this is implemented. */
+ if (1 /* BINFO_MODIFIED (base_binfo) */)
+ {
+ tree base_offset = get_vfield_offset (base_binfo);
+ if (! tree_int_cst_equal (base_offset, get_vfield_offset (TYPE_BINFO (for_type)))
+ && ! tree_int_cst_equal (base_offset, get_vfield_offset (binfo)))
+ base_init_list = tree_cons (error_mark_node, base_binfo,
+ base_init_list);
+ }
+ vfields = TREE_CHAIN (vfields);
+ }
+ return base_init_list;
+}
+
+/* If TYPE does not have a constructor, then the compiler must
+ manually deal with all of the initialization this type requires.
+
+ If a base initializer exists only to fill in the virtual function
+ table pointer, then we mark that fact with the TREE_VIRTUAL bit.
+ This way, we avoid multiple initializations of the same field by
+ each virtual function table up the class hierarchy.
+
+ Virtual base class pointers are not initialized here. They are
+ initialized only at the "top level" of object creation. If we
+ initialized them here, we would have to skip a lot of work. */
+
+static void
+build_class_init_list (type)
+ tree type;
+{
+ tree base_init_list = NULL_TREE;
+ tree member_init_list = NULL_TREE;
+
+ /* Since we build member_init_list and base_init_list using
+ tree_cons, backwards fields the all through work. */
+ tree x;
+ tree binfos = BINFO_BASETYPES (TYPE_BINFO (type));
+ int i, n_baseclasses = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+
+ for (x = TYPE_FIELDS (type); x; x = TREE_CHAIN (x))
+ {
+ if (TREE_CODE (x) != FIELD_DECL)
+ continue;
+
+ if (TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (x))
+ || DECL_INITIAL (x) != NULL_TREE)
+ member_init_list = tree_cons (x, type, member_init_list);
+ }
+ member_init_list = nreverse (member_init_list);
+
+ /* We will end up doing this last. Need special marker
+ to avoid infinite regress. */
+ if (TYPE_VIRTUAL_P (type))
+ {
+ base_init_list = build_tree_list (error_mark_node, TYPE_BINFO (type));
+ if (CLASSTYPE_NEEDS_VIRTUAL_REINIT (type) == 0)
+ TREE_VALUE (base_init_list) = NULL_TREE;
+ TREE_ADDRESSABLE (base_init_list) = 1;
+ }
+
+ /* Each base class which needs to have initialization
+ of some kind gets to make such requests known here. */
+ for (i = n_baseclasses-1; i >= 0; i--)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+ tree blist;
+
+ /* Don't initialize virtual baseclasses this way. */
+ if (TREE_VIA_VIRTUAL (base_binfo))
+ continue;
+
+ if (TYPE_HAS_CONSTRUCTOR (BINFO_TYPE (base_binfo)))
+ {
+ /* ...and the last shall come first... */
+ base_init_list = maybe_fixup_vptrs (type, base_binfo, base_init_list);
+ base_init_list = tree_cons (NULL_TREE, base_binfo, base_init_list);
+ continue;
+ }
+
+ if ((blist = CLASSTYPE_BASE_INIT_LIST (BINFO_TYPE (base_binfo))) == NULL_TREE)
+ /* Nothing to initialize. */
+ continue;
+
+ /* ...ditto... */
+ base_init_list = maybe_fixup_vptrs (type, base_binfo, base_init_list);
+
+ /* This is normally true for single inheritance.
+ The win is we can shrink the chain of initializations
+ to be done by only converting to the actual type
+ we are interested in. */
+ if (TREE_VALUE (blist)
+ && TREE_CODE (TREE_VALUE (blist)) == TREE_VEC
+ && tree_int_cst_equal (BINFO_OFFSET (base_binfo),
+ BINFO_OFFSET (TREE_VALUE (blist))))
+ {
+ if (base_init_list)
+ {
+ /* Does it do more than just fill in a
+ virtual function table pointer? */
+ if (! TREE_ADDRESSABLE (blist))
+ base_init_list = build_tree_list (blist, base_init_list);
+ /* Can we get by just with the virtual function table
+ pointer that it fills in? */
+ else if (TREE_ADDRESSABLE (base_init_list)
+ && TREE_VALUE (base_init_list) == 0)
+ base_init_list = blist;
+ /* Maybe, but it is not obvious as the previous case. */
+ else if (! CLASSTYPE_NEEDS_VIRTUAL_REINIT (type))
+ {
+ tree last = tree_last (base_init_list);
+ while (TREE_VALUE (last)
+ && TREE_CODE (TREE_VALUE (last)) == TREE_LIST)
+ last = tree_last (TREE_VALUE (last));
+ if (TREE_VALUE (last) == 0)
+ base_init_list = build_tree_list (blist, base_init_list);
+ }
+ }
+ else
+ base_init_list = blist;
+ }
+ else
+ {
+ /* The function expand_aggr_init knows how to do the
+ initialization of `basetype' without getting
+ an explicit `blist'. */
+ if (base_init_list)
+ base_init_list = tree_cons (NULL_TREE, base_binfo, base_init_list);
+ else
+ base_init_list = CLASSTYPE_BINFO_AS_LIST (BINFO_TYPE (base_binfo));
+ }
+ }
+
+ if (base_init_list)
+ if (member_init_list)
+ CLASSTYPE_BASE_INIT_LIST (type) = build_tree_list (base_init_list, member_init_list);
+ else
+ CLASSTYPE_BASE_INIT_LIST (type) = base_init_list;
+ else if (member_init_list)
+ CLASSTYPE_BASE_INIT_LIST (type) = member_init_list;
+}
+
+struct base_info
+{
+ int has_virtual;
+ int max_has_virtual;
+ int n_ancestors;
+ tree vfield;
+ tree vfields;
+ char cant_have_default_ctor;
+ char cant_have_const_ctor;
+ char cant_synth_copy_ctor;
+ char cant_synth_asn_ref;
+ char no_const_asn_ref;
+ char needs_virtual_dtor;
+};
+
+/* Record information about type T derived from its base classes.
+ Store most of that information in T itself, and place the
+ remaining information in the struct BASE_INFO.
+
+ Propagate basetype offsets throughout the lattice. Note that the
+ lattice topped by T is really a pair: it's a DAG that gives the
+ structure of the derivation hierarchy, and it's a list of the
+ virtual baseclasses that appear anywhere in the DAG. When a vbase
+ type appears in the DAG, it's offset is 0, and it's children start
+ their offsets from that point. When a vbase type appears in the list,
+ its offset is the offset it has in the hierarchy, and its children's
+ offsets include that offset in theirs.
+
+ Returns the index of the first base class to have virtual functions,
+ or -1 if no such base class.
+
+ Note that at this point TYPE_BINFO (t) != t_binfo. */
+
+static int
+finish_base_struct (t, b, t_binfo)
+ tree t;
+ struct base_info *b;
+ tree t_binfo;
+{
+ tree binfos = BINFO_BASETYPES (t_binfo);
+ int i, n_baseclasses = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+ int first_vfn_base_index = -1;
+ bzero (b, sizeof (struct base_info));
+
+ for (i = 0; i < n_baseclasses; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+ tree basetype = BINFO_TYPE (base_binfo);
+
+ /* If the type of basetype is incomplete, then
+ we already complained about that fact
+ (and we should have fixed it up as well). */
+ if (TYPE_SIZE (basetype) == 0)
+ {
+ int j;
+ /* The base type is of incomplete type. It is
+ probably best to pretend that it does not
+ exist. */
+ if (i == n_baseclasses-1)
+ TREE_VEC_ELT (binfos, i) = NULL_TREE;
+ TREE_VEC_LENGTH (binfos) -= 1;
+ n_baseclasses -= 1;
+ for (j = i; j+1 < n_baseclasses; j++)
+ TREE_VEC_ELT (binfos, j) = TREE_VEC_ELT (binfos, j+1);
+ }
+
+ if (TYPE_HAS_INIT_REF (basetype)
+ && !TYPE_HAS_CONST_INIT_REF (basetype))
+ b->cant_have_const_ctor = 1;
+ if (! TYPE_HAS_INIT_REF (basetype)
+ || (TYPE_HAS_NONPUBLIC_CTOR (basetype) == 2
+ && ! is_friend_type (t, basetype)))
+ b->cant_synth_copy_ctor = 1;
+
+ if (TYPE_HAS_CONSTRUCTOR (basetype)
+ && ! TYPE_HAS_DEFAULT_CONSTRUCTOR (basetype))
+ {
+ b->cant_have_default_ctor = 1;
+ if (! TYPE_HAS_CONSTRUCTOR (t))
+ {
+ cp_pedwarn ("base `%T' with only non-default constructor",
+ basetype);
+ cp_pedwarn ("in class without a constructor");
+ }
+ }
+
+ if (TYPE_HAS_ASSIGN_REF (basetype)
+ && !TYPE_HAS_CONST_ASSIGN_REF (basetype))
+ b->no_const_asn_ref = 1;
+ if (! TYPE_HAS_ASSIGN_REF (basetype)
+ || TYPE_HAS_ABSTRACT_ASSIGN_REF (basetype)
+ || (TYPE_HAS_NONPUBLIC_ASSIGN_REF (basetype) == 2
+ && ! is_friend_type (t, basetype)))
+ b->cant_synth_asn_ref = 1;
+
+ b->n_ancestors += CLASSTYPE_N_SUPERCLASSES (basetype);
+ TYPE_NEEDS_CONSTRUCTING (t) |= TYPE_NEEDS_CONSTRUCTING (basetype);
+ TYPE_NEEDS_DESTRUCTOR (t) |= TYPE_NEEDS_DESTRUCTOR (basetype);
+ TYPE_HAS_COMPLEX_ASSIGN_REF (t) |= TYPE_HAS_COMPLEX_ASSIGN_REF (basetype);
+ TYPE_HAS_COMPLEX_INIT_REF (t) |= (TYPE_HAS_COMPLEX_INIT_REF (basetype)
+ || TYPE_NEEDS_CONSTRUCTING (basetype));
+
+ TYPE_OVERLOADS_CALL_EXPR (t) |= TYPE_OVERLOADS_CALL_EXPR (basetype);
+ TYPE_OVERLOADS_ARRAY_REF (t) |= TYPE_OVERLOADS_ARRAY_REF (basetype);
+ TYPE_OVERLOADS_ARROW (t) |= TYPE_OVERLOADS_ARROW (basetype);
+
+ if (! TREE_VIA_VIRTUAL (base_binfo)
+#if 0
+ /* This cannot be done, as prepare_fresh_vtable wants to modify
+ binfos associated with vfields anywhere in the hierarchy, not
+ just immediate base classes. Due to unsharing, the compiler
+ might consume 3% more memory on a real program.
+ */
+ && ! BINFO_OFFSET_ZEROP (base_binfo)
+#endif
+ && BINFO_BASETYPES (base_binfo))
+ {
+ tree base_binfos = BINFO_BASETYPES (base_binfo);
+ tree chain = NULL_TREE;
+ int j;
+
+ /* Now unshare the structure beneath BASE_BINFO. */
+ for (j = TREE_VEC_LENGTH (base_binfos)-1;
+ j >= 0; j--)
+ {
+ tree base_base_binfo = TREE_VEC_ELT (base_binfos, j);
+ if (! TREE_VIA_VIRTUAL (base_base_binfo))
+ TREE_VEC_ELT (base_binfos, j)
+ = make_binfo (BINFO_OFFSET (base_base_binfo),
+ base_base_binfo,
+ BINFO_VTABLE (base_base_binfo),
+ BINFO_VIRTUALS (base_base_binfo),
+ chain);
+ chain = TREE_VEC_ELT (base_binfos, j);
+ TREE_VIA_PUBLIC (chain) = TREE_VIA_PUBLIC (base_base_binfo);
+ TREE_VIA_PROTECTED (chain) = TREE_VIA_PROTECTED (base_base_binfo);
+ }
+
+ /* Completely unshare potentially shared data, and
+ update what is ours. */
+ propagate_binfo_offsets (base_binfo, BINFO_OFFSET (base_binfo));
+ }
+
+ if (! TREE_VIA_VIRTUAL (base_binfo))
+ CLASSTYPE_N_SUPERCLASSES (t) += 1;
+
+ if (TYPE_VIRTUAL_P (basetype))
+ {
+ /* If there's going to be a destructor needed, make
+ sure it will be virtual. */
+ b->needs_virtual_dtor = 1;
+
+ /* Don't borrow virtuals from virtual baseclasses. */
+ if (TREE_VIA_VIRTUAL (base_binfo))
+ continue;
+
+ if (first_vfn_base_index < 0)
+ {
+ tree vfields;
+ first_vfn_base_index = i;
+
+ /* Update these two, now that we know what vtable we are
+ going to extend. This is so that we can add virtual
+ functions, and override them properly. */
+ BINFO_VTABLE (t_binfo) = TYPE_BINFO_VTABLE (basetype);
+ BINFO_VIRTUALS (t_binfo) = TYPE_BINFO_VIRTUALS (basetype);
+ b->has_virtual = CLASSTYPE_VSIZE (basetype);
+ b->vfield = CLASSTYPE_VFIELD (basetype);
+ b->vfields = copy_list (CLASSTYPE_VFIELDS (basetype));
+ vfields = b->vfields;
+ while (vfields)
+ {
+ if (VF_BINFO_VALUE (vfields) == NULL_TREE
+ || ! TREE_VIA_VIRTUAL (VF_BINFO_VALUE (vfields)))
+ {
+ tree value = VF_BASETYPE_VALUE (vfields);
+ if (DECL_NAME (CLASSTYPE_VFIELD (value))
+ == DECL_NAME (CLASSTYPE_VFIELD (basetype)))
+ VF_NORMAL_VALUE (b->vfields) = basetype;
+ else
+ VF_NORMAL_VALUE (b->vfields) = VF_NORMAL_VALUE (vfields);
+ }
+ vfields = TREE_CHAIN (vfields);
+ }
+ CLASSTYPE_VFIELD (t) = b->vfield;
+ }
+ else
+ {
+ /* Only add unique vfields, and flatten them out as we go. */
+ tree vfields = CLASSTYPE_VFIELDS (basetype);
+ while (vfields)
+ {
+ if (VF_BINFO_VALUE (vfields) == NULL_TREE
+ || ! TREE_VIA_VIRTUAL (VF_BINFO_VALUE (vfields)))
+ {
+ tree value = VF_BASETYPE_VALUE (vfields);
+ b->vfields = tree_cons (base_binfo, value, b->vfields);
+ if (DECL_NAME (CLASSTYPE_VFIELD (value))
+ == DECL_NAME (CLASSTYPE_VFIELD (basetype)))
+ VF_NORMAL_VALUE (b->vfields) = basetype;
+ else
+ VF_NORMAL_VALUE (b->vfields) = VF_NORMAL_VALUE (vfields);
+ }
+ vfields = TREE_CHAIN (vfields);
+ }
+
+ if (b->has_virtual == 0)
+ {
+ first_vfn_base_index = i;
+
+ /* Update these two, now that we know what vtable we are
+ going to extend. This is so that we can add virtual
+ functions, and override them properly. */
+ BINFO_VTABLE (t_binfo) = TYPE_BINFO_VTABLE (basetype);
+ BINFO_VIRTUALS (t_binfo) = TYPE_BINFO_VIRTUALS (basetype);
+ b->has_virtual = CLASSTYPE_VSIZE (basetype);
+ b->vfield = CLASSTYPE_VFIELD (basetype);
+ CLASSTYPE_VFIELD (t) = b->vfield;
+ /* When we install the first one, set the VF_NORMAL_VALUE
+ to be the current class, as this it is the most derived
+ class. Hopefully, this is not set to something else
+ later. (mrs) */
+ vfields = b->vfields;
+ while (vfields)
+ {
+ if (DECL_NAME (CLASSTYPE_VFIELD (t))
+ == DECL_NAME (CLASSTYPE_VFIELD (basetype)))
+ {
+ VF_NORMAL_VALUE (vfields) = t;
+ /* There should only be one of them! And it should
+ always be found, if we get into here. (mrs) */
+ break;
+ }
+ vfields = TREE_CHAIN (vfields);
+ }
+ }
+ }
+ }
+ }
+
+ /* Must come after offsets are fixed for all bases. */
+ for (i = 0; i < n_baseclasses; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+ tree basetype = BINFO_TYPE (base_binfo);
+
+ if (get_base_distance (basetype, t_binfo, 0, (tree*)0) == -2)
+ {
+ cp_warning ("direct base `%T' inaccessible in `%T' due to ambiguity",
+ basetype, t);
+ b->cant_synth_asn_ref = 1;
+ b->cant_synth_copy_ctor = 1;
+ }
+ }
+ {
+ tree v = get_vbase_types (t_binfo);
+
+ for (; v; v = TREE_CHAIN (v))
+ {
+ tree basetype = BINFO_TYPE (v);
+ if (get_base_distance (basetype, t_binfo, 0, (tree*)0) == -2)
+ {
+ if (extra_warnings)
+ cp_warning ("virtual base `%T' inaccessible in `%T' due to ambiguity",
+ basetype, t);
+ b->cant_synth_asn_ref = 1;
+ b->cant_synth_copy_ctor = 1;
+ }
+ }
+ }
+
+ {
+ tree vfields;
+ /* Find the base class with the largest number of virtual functions. */
+ for (vfields = b->vfields; vfields; vfields = TREE_CHAIN (vfields))
+ {
+ if (CLASSTYPE_VSIZE (VF_BASETYPE_VALUE (vfields)) > b->max_has_virtual)
+ b->max_has_virtual = CLASSTYPE_VSIZE (VF_BASETYPE_VALUE (vfields));
+ if (VF_DERIVED_VALUE (vfields)
+ && CLASSTYPE_VSIZE (VF_DERIVED_VALUE (vfields)) > b->max_has_virtual)
+ b->max_has_virtual = CLASSTYPE_VSIZE (VF_DERIVED_VALUE (vfields));
+ }
+ }
+
+ if (b->vfield == 0)
+ /* If all virtual functions come only from virtual baseclasses. */
+ return -1;
+ return first_vfn_base_index;
+}
+
+static int
+typecode_p (type, code)
+ tree type;
+ enum tree_code code;
+{
+ return (TREE_CODE (type) == code
+ || (TREE_CODE (type) == REFERENCE_TYPE
+ && TREE_CODE (TREE_TYPE (type)) == code));
+}
+
+/* Set memoizing fields and bits of T (and its variants) for later use.
+ MAX_HAS_VIRTUAL is the largest size of any T's virtual function tables. */
+static void
+finish_struct_bits (t, max_has_virtual)
+ tree t;
+ int max_has_virtual;
+{
+ int i, n_baseclasses = CLASSTYPE_N_BASECLASSES (t);
+ tree method_vec = CLASSTYPE_METHOD_VEC (t);
+
+ /* Fix up variants (if any). */
+ tree variants = TYPE_NEXT_VARIANT (t);
+ while (variants)
+ {
+ /* These fields are in the _TYPE part of the node, not in
+ the TYPE_LANG_SPECIFIC component, so they are not shared. */
+ TYPE_HAS_CONSTRUCTOR (variants) = TYPE_HAS_CONSTRUCTOR (t);
+ TYPE_HAS_DESTRUCTOR (variants) = TYPE_HAS_DESTRUCTOR (t);
+ TYPE_NEEDS_CONSTRUCTING (variants) = TYPE_NEEDS_CONSTRUCTING (t);
+ TYPE_NEEDS_DESTRUCTOR (variants) = TYPE_NEEDS_DESTRUCTOR (t);
+
+ TYPE_USES_COMPLEX_INHERITANCE (variants) = TYPE_USES_COMPLEX_INHERITANCE (t);
+ TYPE_VIRTUAL_P (variants) = TYPE_VIRTUAL_P (t);
+ TYPE_USES_VIRTUAL_BASECLASSES (variants) = TYPE_USES_VIRTUAL_BASECLASSES (t);
+ /* Copy whatever these are holding today. */
+ TYPE_MIN_VALUE (variants) = TYPE_MIN_VALUE (t);
+ TYPE_MAX_VALUE (variants) = TYPE_MAX_VALUE (t);
+ variants = TYPE_NEXT_VARIANT (variants);
+ }
+
+ if (n_baseclasses && max_has_virtual)
+ {
+ /* Done by `finish_struct' for classes without baseclasses. */
+ int might_have_abstract_virtuals = CLASSTYPE_ABSTRACT_VIRTUALS (t) != 0;
+ tree binfos = TYPE_BINFO_BASETYPES (t);
+ for (i = n_baseclasses-1; i >= 0; i--)
+ {
+ might_have_abstract_virtuals
+ |= (CLASSTYPE_ABSTRACT_VIRTUALS (BINFO_TYPE (TREE_VEC_ELT (binfos, i))) != 0);
+ if (might_have_abstract_virtuals)
+ break;
+ }
+ if (might_have_abstract_virtuals)
+ {
+ /* We use error_mark_node from override_one_vtable to signal
+ an artificial abstract. */
+ if (CLASSTYPE_ABSTRACT_VIRTUALS (t) == error_mark_node)
+ CLASSTYPE_ABSTRACT_VIRTUALS (t) = NULL_TREE;
+ CLASSTYPE_ABSTRACT_VIRTUALS (t) = get_abstract_virtuals (t);
+ }
+ }
+
+ if (n_baseclasses)
+ {
+ /* Notice whether this class has type conversion functions defined. */
+ tree binfo = TYPE_BINFO (t);
+ tree binfos = BINFO_BASETYPES (binfo);
+ tree basetype;
+
+ for (i = n_baseclasses-1; i >= 0; i--)
+ {
+ basetype = BINFO_TYPE (TREE_VEC_ELT (binfos, i));
+
+ if (TYPE_HAS_CONVERSION (basetype))
+ {
+ TYPE_HAS_CONVERSION (t) = 1;
+ TYPE_HAS_INT_CONVERSION (t) |= TYPE_HAS_INT_CONVERSION (basetype);
+ TYPE_HAS_REAL_CONVERSION (t) |= TYPE_HAS_REAL_CONVERSION (basetype);
+ }
+ if (CLASSTYPE_MAX_DEPTH (basetype) >= CLASSTYPE_MAX_DEPTH (t))
+ CLASSTYPE_MAX_DEPTH (t) = CLASSTYPE_MAX_DEPTH (basetype) + 1;
+ }
+ }
+
+ /* Need to test METHOD_VEC here in case all methods
+ (conversions and otherwise) are inherited. */
+ if (TYPE_HAS_CONVERSION (t) && method_vec != NULL_TREE)
+ {
+ tree first_conversions[last_conversion_type];
+ tree last_conversions[last_conversion_type];
+ enum conversion_type conv_index;
+ tree *tmp;
+ int i;
+
+ bzero (first_conversions, sizeof (first_conversions));
+ bzero (last_conversions, sizeof (last_conversions));
+ for (tmp = &TREE_VEC_ELT (method_vec, 1);
+ tmp != TREE_VEC_END (method_vec); tmp += 1)
+ {
+ /* ??? This should compare DECL_NAME (*tmp) == ansi_opname[TYPE_EXPR]. */
+ if (IDENTIFIER_TYPENAME_P (DECL_ASSEMBLER_NAME (*tmp)))
+ {
+ tree fntype = TREE_TYPE (*tmp);
+ tree return_type = TREE_TYPE (fntype);
+ my_friendly_assert (TREE_CODE (fntype) == METHOD_TYPE, 171);
+
+ if (typecode_p (return_type, POINTER_TYPE))
+ {
+ if (TYPE_READONLY (TREE_TYPE (return_type)))
+ conv_index = constptr_conv;
+ else
+ conv_index = ptr_conv;
+ }
+ else if (typecode_p (return_type, INTEGER_TYPE)
+ || typecode_p (return_type, BOOLEAN_TYPE)
+ || typecode_p (return_type, ENUMERAL_TYPE))
+ {
+ TYPE_HAS_INT_CONVERSION (t) = 1;
+ conv_index = int_conv;
+ }
+ else if (typecode_p (return_type, REAL_TYPE))
+ {
+ TYPE_HAS_REAL_CONVERSION (t) = 1;
+ conv_index = real_conv;
+ }
+ else
+ continue;
+
+ if (first_conversions[(int) conv_index] == NULL_TREE)
+ first_conversions[(int) conv_index] = *tmp;
+ last_conversions[(int) conv_index] = *tmp;
+ }
+ }
+
+ for (i = 0; i < (int) last_conversion_type; i++)
+ if (first_conversions[i] != last_conversions[i])
+ CLASSTYPE_CONVERSION (t, i) = error_mark_node;
+ else
+ CLASSTYPE_CONVERSION (t, i) = first_conversions[i];
+ }
+
+ /* If this type has constructors, force its mode to be BLKmode,
+ and force its TREE_ADDRESSABLE bit to be nonzero. */
+ if (TYPE_NEEDS_CONSTRUCTING (t) || TYPE_NEEDS_DESTRUCTOR (t))
+ {
+ tree variants = t;
+
+ if (TREE_CODE (TYPE_NAME (t)) == TYPE_DECL)
+ DECL_MODE (TYPE_NAME (t)) = BLKmode;
+ while (variants)
+ {
+ TYPE_MODE (variants) = BLKmode;
+ TREE_ADDRESSABLE (variants) = 1;
+ variants = TYPE_NEXT_VARIANT (variants);
+ }
+ }
+}
+
+/* Warn about duplicate methods in fn_fields. Also compact method
+ lists so that lookup can be made faster.
+
+ Algorithm: Outer loop builds lists by method name. Inner loop
+ checks for redundant method names within a list.
+
+ Data Structure: List of method lists. The outer list is a
+ TREE_LIST, whose TREE_PURPOSE field is the field name and the
+ TREE_VALUE is the TREE_CHAIN of the FUNCTION_DECLs. Friends are
+ chained in the same way as member functions, but they live in the
+ TREE_TYPE field of the outer list. That allows them to be quickly
+ deleted, and requires no extra storage.
+
+ If there are any constructors/destructors, they are moved to the
+ front of the list. This makes pushclass more efficient.
+
+ We also link each field which has shares a name with its baseclass
+ to the head of the list of fields for that base class. This allows
+ us to reduce search time in places like `build_method_call' to
+ consider only reasonably likely functions. */
+
+static tree
+finish_struct_methods (t, fn_fields, nonprivate_method)
+ tree t;
+ tree fn_fields;
+ int nonprivate_method;
+{
+ tree method_vec;
+ tree name = constructor_name (t);
+ int i, n_baseclasses = CLASSTYPE_N_BASECLASSES (t);
+
+ /* Now prepare to gather fn_fields into vector. */
+ struct obstack *ambient_obstack = current_obstack;
+ current_obstack = &class_obstack;
+ method_vec = make_node (TREE_VEC);
+ /* Room has been saved for constructors and destructors. */
+ current_obstack = ambient_obstack;
+ /* Now make this a live vector. */
+ obstack_free (&class_obstack, method_vec);
+ obstack_blank (&class_obstack, sizeof (struct tree_vec));
+
+ while (fn_fields)
+ {
+ /* NEXT Pointer, TEST Pointer, and BASE Pointer. */
+ tree nextp, *testp;
+ tree fn_name = DECL_NAME (fn_fields);
+ if (fn_name == NULL_TREE)
+ fn_name = name;
+
+ nextp = TREE_CHAIN (fn_fields);
+ TREE_CHAIN (fn_fields) = NULL_TREE;
+
+ /* Clear out this flag.
+
+ @@ Doug may figure out how to break
+ @@ this with nested classes and friends. */
+ DECL_IN_AGGR_P (fn_fields) = 0;
+
+ /* Note here that a copy ctor is private, so we don't dare generate
+ a default copy constructor for a class that has a member
+ of this type without making sure they have access to it. */
+ if (fn_name == name)
+ {
+ tree parmtypes = FUNCTION_ARG_CHAIN (fn_fields);
+ tree parmtype = parmtypes ? TREE_VALUE (parmtypes) : void_type_node;
+
+ if (TREE_CODE (parmtype) == REFERENCE_TYPE
+ && TYPE_MAIN_VARIANT (TREE_TYPE (parmtype)) == t)
+ {
+ if (TREE_CHAIN (parmtypes) == NULL_TREE
+ || TREE_CHAIN (parmtypes) == void_list_node
+ || TREE_PURPOSE (TREE_CHAIN (parmtypes)))
+ {
+ if (TREE_PROTECTED (fn_fields))
+ TYPE_HAS_NONPUBLIC_CTOR (t) = 1;
+ else if (TREE_PRIVATE (fn_fields))
+ TYPE_HAS_NONPUBLIC_CTOR (t) = 2;
+ }
+ }
+ }
+ else if (fn_name == ansi_opname[(int) MODIFY_EXPR])
+ {
+ tree parmtype = TREE_VALUE (FUNCTION_ARG_CHAIN (fn_fields));
+
+ if (TREE_CODE (parmtype) == REFERENCE_TYPE
+ && TYPE_MAIN_VARIANT (TREE_TYPE (parmtype)) == t)
+ {
+ if (TREE_PROTECTED (fn_fields))
+ TYPE_HAS_NONPUBLIC_ASSIGN_REF (t) = 1;
+ else if (TREE_PRIVATE (fn_fields))
+ TYPE_HAS_NONPUBLIC_ASSIGN_REF (t) = 2;
+ }
+ }
+
+ /* Constructors are handled easily in search routines. */
+ if (fn_name == name)
+ {
+ DECL_CHAIN (fn_fields) = TREE_VEC_ELT (method_vec, 0);
+ TREE_VEC_ELT (method_vec, 0) = fn_fields;
+ }
+ else
+ {
+ testp = &TREE_VEC_ELT (method_vec, 0);
+ if (*testp == NULL_TREE)
+ testp++;
+ while (((HOST_WIDE_INT) testp
+ < (HOST_WIDE_INT) obstack_next_free (&class_obstack))
+ && DECL_NAME (*testp) != fn_name)
+ testp++;
+ if ((HOST_WIDE_INT) testp
+ < (HOST_WIDE_INT) obstack_next_free (&class_obstack))
+ {
+ tree x, prev_x;
+
+ for (x = *testp; x; x = DECL_CHAIN (x))
+ {
+ if (DECL_NAME (fn_fields) == ansi_opname[(int) DELETE_EXPR]
+ || DECL_NAME (fn_fields)
+ == ansi_opname[(int) VEC_DELETE_EXPR])
+ {
+ /* ANSI C++ June 5 1992 WP 12.5.5.1 */
+ cp_error_at ("`%D' overloaded", fn_fields);
+ cp_error_at ("previous declaration as `%D' here", x);
+ }
+ if (DECL_ASSEMBLER_NAME (fn_fields)==DECL_ASSEMBLER_NAME (x))
+ {
+ /* We complain about multiple destructors on sight,
+ so we do not repeat the warning here. Friend-friend
+ ambiguities are warned about outside this loop. */
+ if (!DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (fn_fields)))
+ cp_error_at ("ambiguous method `%#D' in structure",
+ fn_fields);
+ break;
+ }
+ prev_x = x;
+ }
+ if (x == 0)
+ {
+ if (*testp)
+ DECL_CHAIN (prev_x) = fn_fields;
+ else
+ *testp = fn_fields;
+ }
+ }
+ else
+ {
+ obstack_ptr_grow (&class_obstack, fn_fields);
+ method_vec = (tree)obstack_base (&class_obstack);
+ }
+ }
+ fn_fields = nextp;
+ }
+
+ TREE_VEC_LENGTH (method_vec) = (tree *)obstack_next_free (&class_obstack)
+ - (&TREE_VEC_ELT (method_vec, 0));
+ obstack_finish (&class_obstack);
+ CLASSTYPE_METHOD_VEC (t) = method_vec;
+
+ if (nonprivate_method == 0
+ && CLASSTYPE_FRIEND_CLASSES (t) == NULL_TREE
+ && DECL_FRIENDLIST (TYPE_NAME (t)) == NULL_TREE)
+ {
+ tree binfos = BINFO_BASETYPES (TYPE_BINFO (t));
+ for (i = 0; i < n_baseclasses; i++)
+ if (TREE_VIA_PUBLIC (TREE_VEC_ELT (binfos, i))
+ || TREE_VIA_PROTECTED (TREE_VEC_ELT (binfos, i)))
+ {
+ nonprivate_method = 1;
+ break;
+ }
+ if (nonprivate_method == 0)
+ cp_warning ("all member functions in class `%T' are private", t);
+ }
+
+ /* If there are constructors (and destructors), they are at the
+ front. Place destructors at very front. Also warn if all
+ constructors and/or destructors are private (in which case this
+ class is effectively unusable. */
+ if (TYPE_HAS_DESTRUCTOR (t))
+ {
+ tree dtor, prev;
+
+ for (dtor = TREE_VEC_ELT (method_vec, 0);
+ dtor;
+ prev = dtor, dtor = DECL_CHAIN (dtor))
+ {
+ if (DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (dtor)))
+ {
+ if (TREE_PRIVATE (dtor)
+ && CLASSTYPE_FRIEND_CLASSES (t) == NULL_TREE
+ && DECL_FRIENDLIST (TYPE_NAME (t)) == NULL_TREE
+ && warn_ctor_dtor_privacy)
+ warning ("class `%s' only defines a private destructor and has no friends",
+ TYPE_NAME_STRING (t));
+ break;
+ }
+ }
+
+ /* Wild parse errors can cause this to happen. */
+ if (dtor == NULL_TREE)
+ TYPE_HAS_DESTRUCTOR (t) = 0;
+ else if (dtor != TREE_VEC_ELT (method_vec, 0))
+ {
+ DECL_CHAIN (prev) = DECL_CHAIN (dtor);
+ DECL_CHAIN (dtor) = TREE_VEC_ELT (method_vec, 0);
+ TREE_VEC_ELT (method_vec, 0) = dtor;
+ }
+ }
+
+ /* Now for each member function (except for constructors and
+ destructors), compute where member functions of the same
+ name reside in base classes. */
+ if (n_baseclasses != 0
+ && TREE_VEC_LENGTH (method_vec) > 1)
+ {
+ int len = TREE_VEC_LENGTH (method_vec);
+ tree baselink_vec = make_tree_vec (len);
+ int any_links = 0;
+ tree baselink_binfo = build_tree_list (NULL_TREE, TYPE_BINFO (t));
+
+ for (i = 1; i < len; i++)
+ {
+ TREE_VEC_ELT (baselink_vec, i)
+ = get_baselinks (baselink_binfo, t, DECL_NAME (TREE_VEC_ELT (method_vec, i)));
+ if (TREE_VEC_ELT (baselink_vec, i) != 0)
+ any_links = 1;
+ }
+ if (any_links != 0)
+ CLASSTYPE_BASELINK_VEC (t) = baselink_vec;
+ else
+ obstack_free (current_obstack, baselink_vec);
+ }
+
+ /* Now add the methods to the TYPE_METHODS of T, arranged in a chain. */
+ {
+ tree x, last_x = NULL_TREE;
+ int limit = TREE_VEC_LENGTH (method_vec);
+
+ for (i = 1; i < limit; i++)
+ {
+ for (x = TREE_VEC_ELT (method_vec, i); x; x = DECL_CHAIN (x))
+ {
+ if (last_x != NULL_TREE)
+ TREE_CHAIN (last_x) = x;
+ last_x = x;
+ }
+ }
+
+ /* Put ctors and dtors at the front of the list. */
+ x = TREE_VEC_ELT (method_vec, 0);
+ if (x)
+ {
+ while (DECL_CHAIN (x))
+ {
+ /* Let's avoid being circular about this. */
+ if (x == DECL_CHAIN (x))
+ break;
+ TREE_CHAIN (x) = DECL_CHAIN (x);
+ x = DECL_CHAIN (x);
+ }
+ if (TREE_VEC_LENGTH (method_vec) > 1)
+ TREE_CHAIN (x) = TREE_VEC_ELT (method_vec, 1);
+ else
+ TREE_CHAIN (x) = NULL_TREE;
+ }
+ }
+
+ TYPE_METHODS (t) = method_vec;
+
+ return method_vec;
+}
+
+/* Emit error when a duplicate definition of a type is seen. Patch up. */
+
+void
+duplicate_tag_error (t)
+ tree t;
+{
+ cp_error ("redefinition of `%#T'", t);
+
+ /* Pretend we haven't defined this type. */
+
+ /* All of the component_decl's were TREE_CHAINed together in the parser.
+ finish_struct_methods walks these chains and assembles all methods with
+ the same base name into DECL_CHAINs. Now we don't need the parser chains
+ anymore, so we unravel them.
+ */
+ /*
+ * This used to be in finish_struct, but it turns out that the
+ * TREE_CHAIN is used by dbxout_type_methods and perhaps some other things...
+ */
+ if (CLASSTYPE_METHOD_VEC(t))
+ {
+ tree tv = CLASSTYPE_METHOD_VEC(t);
+ int i, len = TREE_VEC_LENGTH (tv);
+ for (i = 0; i < len; i++)
+ {
+ tree unchain = TREE_VEC_ELT (tv, i);
+ while (unchain != NULL_TREE)
+ {
+ TREE_CHAIN (unchain) = NULL_TREE;
+ unchain = DECL_CHAIN(unchain);
+ }
+ }
+ }
+
+ if (TYPE_LANG_SPECIFIC (t))
+ {
+ tree as_list = CLASSTYPE_AS_LIST (t);
+ tree binfo = TYPE_BINFO (t);
+ tree binfo_as_list = CLASSTYPE_BINFO_AS_LIST (t);
+ int interface_only = CLASSTYPE_INTERFACE_ONLY (t);
+ int interface_unknown = CLASSTYPE_INTERFACE_UNKNOWN (t);
+
+ bzero (TYPE_LANG_SPECIFIC (t), sizeof (struct lang_type));
+ BINFO_BASETYPES(binfo) = NULL_TREE;
+
+ CLASSTYPE_AS_LIST (t) = as_list;
+ TYPE_BINFO (t) = binfo;
+ CLASSTYPE_BINFO_AS_LIST (t) = binfo_as_list;
+ CLASSTYPE_INTERFACE_ONLY (t) = interface_only;
+ SET_CLASSTYPE_INTERFACE_UNKNOWN_X (t, interface_unknown);
+ CLASSTYPE_VBASE_SIZE (t) = integer_zero_node;
+ TYPE_REDEFINED (t) = 1;
+ }
+ TYPE_SIZE (t) = NULL_TREE;
+ TYPE_MODE (t) = VOIDmode;
+ TYPE_FIELDS (t) = NULL_TREE;
+ TYPE_METHODS (t) = NULL_TREE;
+ TYPE_VFIELD (t) = NULL_TREE;
+ TYPE_CONTEXT (t) = NULL_TREE;
+}
+
+/* finish up all new vtables. */
+static void
+finish_vtbls (binfo, do_self, t)
+ tree binfo, t;
+ int do_self;
+{
+ tree binfos = BINFO_BASETYPES (binfo);
+ int i, n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+
+ /* Should we use something besides CLASSTYPE_VFIELDS? */
+ if (do_self && CLASSTYPE_VFIELDS (BINFO_TYPE (binfo)))
+ {
+ if (BINFO_NEW_VTABLE_MARKED (binfo))
+ {
+ tree decl, context;
+
+ decl = BINFO_VTABLE (binfo);
+ context = DECL_CONTEXT (decl);
+ DECL_CONTEXT (decl) = 0;
+ if (write_virtuals >= 0
+ && DECL_INITIAL (decl) != BINFO_VIRTUALS (binfo))
+ DECL_INITIAL (decl) = build_nt (CONSTRUCTOR, NULL_TREE,
+ BINFO_VIRTUALS (binfo));
+ finish_decl (decl, DECL_INITIAL (decl), NULL_TREE, 0);
+ DECL_CONTEXT (decl) = context;
+ }
+ CLEAR_BINFO_NEW_VTABLE_MARKED (binfo);
+ }
+
+ for (i = 0; i < n_baselinks; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+ int is_not_base_vtable =
+ i != CLASSTYPE_VFIELD_PARENT (BINFO_TYPE (binfo));
+ if (TREE_VIA_VIRTUAL (base_binfo))
+ {
+ base_binfo = binfo_member (BINFO_TYPE (base_binfo), CLASSTYPE_VBASECLASSES (t));
+ }
+ finish_vtbls (base_binfo, is_not_base_vtable, t);
+ }
+}
+
+/* True if we should override the given BASE_FNDECL with the given
+ FNDECL. */
+static int
+overrides (fndecl, base_fndecl)
+ tree fndecl, base_fndecl;
+{
+ /* Destructors have special names. */
+ if (DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (base_fndecl)) &&
+ DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (fndecl)))
+ return 1;
+ if (DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (base_fndecl)) ||
+ DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (fndecl)))
+ return 0;
+ if (DECL_NAME (fndecl) == DECL_NAME (base_fndecl))
+ {
+ tree rettype, base_rettype, types, base_types;
+#if 0
+ retypes = TREE_TYPE (TREE_TYPE (fndecl));
+ base_retypes = TREE_TYPE (TREE_TYPE (base_fndecl));
+#endif
+ types = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+ base_types = TYPE_ARG_TYPES (TREE_TYPE (base_fndecl));
+ if ((TYPE_READONLY (TREE_TYPE (TREE_VALUE (base_types)))
+ == TYPE_READONLY (TREE_TYPE (TREE_VALUE (types))))
+ && compparms (TREE_CHAIN (base_types), TREE_CHAIN (types), 3))
+ return 1;
+ }
+ return 0;
+}
+
+static void
+modify_one_vtable (binfo, t, fndecl, pfn)
+ tree binfo, t, fndecl, pfn;
+{
+ tree virtuals = BINFO_VIRTUALS (binfo);
+ unsigned HOST_WIDE_INT n;
+
+ n = 0;
+ /* Skip initial vtable length field and RTTI fake object. */
+ for (; virtuals && n < 1 + flag_dossier; n++)
+ virtuals = TREE_CHAIN (virtuals);
+ while (virtuals)
+ {
+ tree current_fndecl = TREE_VALUE (virtuals);
+ current_fndecl = FNADDR_FROM_VTABLE_ENTRY (current_fndecl);
+ current_fndecl = TREE_OPERAND (current_fndecl, 0);
+ if (current_fndecl && overrides (fndecl, current_fndecl))
+ {
+ tree base_offset, offset;
+ tree context = DECL_CLASS_CONTEXT (fndecl);
+ tree vfield = CLASSTYPE_VFIELD (t);
+ tree this_offset;
+
+ offset = integer_zero_node;
+ if (context != t && TYPE_USES_COMPLEX_INHERITANCE (t))
+ {
+ offset = virtual_offset (context, CLASSTYPE_VBASECLASSES (t), offset);
+ if (offset == NULL_TREE)
+ {
+ tree binfo = get_binfo (context, t, 0);
+ offset = BINFO_OFFSET (binfo);
+ }
+ }
+
+ /* Find the right offset for the this pointer based on the
+ base class we just found. We have to take into
+ consideration the virtual base class pointers that we
+ stick in before the virtual function table pointer.
+
+ Also, we want just the delta bewteen the most base class
+ that we derived this vfield from and us. */
+ base_offset = size_binop (PLUS_EXPR,
+ get_derived_offset (binfo),
+ BINFO_OFFSET (binfo));
+ this_offset = size_binop (MINUS_EXPR, offset, base_offset);
+
+ /* Make sure we can modify the derived association with immunity. */
+ if (TREE_USED (binfo)) {
+ my_friendly_assert (0, 999);
+#if 0
+ my_friendly_assert (*binfo2_ptr == binfo, 999);
+ *binfo2_ptr = copy_binfo (binfo);
+#endif
+ }
+ if (binfo == TYPE_BINFO (t))
+ {
+ /* In this case, it is *type*'s vtable we are modifying.
+ We start with the approximation that it's vtable is that
+ of the immediate base class. */
+ if (! BINFO_NEW_VTABLE_MARKED (binfo))
+ build_vtable (TYPE_BINFO (DECL_CONTEXT (vfield)), t);
+ }
+ else
+ {
+ /* This is our very own copy of `basetype' to play with.
+ Later, we will fill in all the virtual functions
+ that override the virtual functions in these base classes
+ which are not defined by the current type. */
+ if (! BINFO_NEW_VTABLE_MARKED (binfo))
+ prepare_fresh_vtable (binfo, t);
+ }
+
+#ifdef NOTQUITE
+ cp_warning ("in %D", DECL_NAME (BINFO_VTABLE (binfo)));
+#endif
+ modify_vtable_entry (get_vtable_entry_n (BINFO_VIRTUALS (binfo), n),
+ build_vtable_entry (this_offset, pfn),
+ fndecl);
+ }
+ ++n;
+ virtuals = TREE_CHAIN (virtuals);
+ }
+}
+
+/* These are the ones that are not through virtual base classes. */
+static void
+modify_all_direct_vtables (binfo, do_self, t, fndecl, pfn)
+ tree binfo, t, fndecl, pfn;
+ int do_self;
+{
+ tree binfos = BINFO_BASETYPES (binfo);
+ int i, n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+
+ /* Should we use something besides CLASSTYPE_VFIELDS? */
+ if (do_self && CLASSTYPE_VFIELDS (BINFO_TYPE (binfo)))
+ {
+ modify_one_vtable (binfo, t, fndecl, pfn);
+ }
+
+ for (i = 0; i < n_baselinks; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+ int is_not_base_vtable =
+ i != CLASSTYPE_VFIELD_PARENT (BINFO_TYPE (binfo));
+ if (! TREE_VIA_VIRTUAL (base_binfo))
+ modify_all_direct_vtables (base_binfo, is_not_base_vtable, t, fndecl, pfn);
+ }
+}
+
+/* Fixup all the delta entries in this vtable that need updating.
+ This happens when we have non-overridden virtual functions from a
+ virtual base class, that are at a different offset, in the new
+ hierarchy, because the layout of the virtual bases has changed. */
+static void
+fixup_vtable_deltas (binfo, t)
+ tree binfo, t;
+{
+ tree virtuals = BINFO_VIRTUALS (binfo);
+ unsigned HOST_WIDE_INT n;
+
+ n = 0;
+ /* Skip initial vtable length field and RTTI fake object. */
+ for (; virtuals && n < 1 + flag_dossier; n++)
+ virtuals = TREE_CHAIN (virtuals);
+ while (virtuals)
+ {
+ tree fndecl = TREE_VALUE (virtuals);
+ tree pfn = FNADDR_FROM_VTABLE_ENTRY (fndecl);
+ tree delta = DELTA_FROM_VTABLE_ENTRY (fndecl);
+ fndecl = TREE_OPERAND (pfn, 0);
+ if (fndecl)
+ {
+ tree base_offset, offset;
+ tree context = DECL_CLASS_CONTEXT (fndecl);
+ tree vfield = CLASSTYPE_VFIELD (t);
+ tree this_offset;
+
+ offset = integer_zero_node;
+ if (context != t && TYPE_USES_COMPLEX_INHERITANCE (t))
+ {
+ offset = virtual_offset (context, CLASSTYPE_VBASECLASSES (t), offset);
+ if (offset == NULL_TREE)
+ {
+ tree binfo = get_binfo (context, t, 0);
+ offset = BINFO_OFFSET (binfo);
+ }
+ }
+
+ /* Find the right offset for the this pointer based on the
+ base class we just found. We have to take into
+ consideration the virtual base class pointers that we
+ stick in before the virtual function table pointer.
+
+ Also, we want just the delta bewteen the most base class
+ that we derived this vfield from and us. */
+ base_offset = size_binop (PLUS_EXPR,
+ get_derived_offset (binfo),
+ BINFO_OFFSET (binfo));
+ this_offset = size_binop (MINUS_EXPR, offset, base_offset);
+
+ if (! tree_int_cst_equal (this_offset, delta))
+ {
+ /* Make sure we can modify the derived association with immunity. */
+ if (TREE_USED (binfo))
+ my_friendly_assert (0, 999);
+
+ if (binfo == TYPE_BINFO (t))
+ {
+ /* In this case, it is *type*'s vtable we are modifying.
+ We start with the approximation that it's vtable is that
+ of the immediate base class. */
+ if (! BINFO_NEW_VTABLE_MARKED (binfo))
+ build_vtable (TYPE_BINFO (DECL_CONTEXT (vfield)), t);
+ }
+ else
+ {
+ /* This is our very own copy of `basetype' to play with.
+ Later, we will fill in all the virtual functions
+ that override the virtual functions in these base classes
+ which are not defined by the current type. */
+ if (! BINFO_NEW_VTABLE_MARKED (binfo))
+ prepare_fresh_vtable (binfo, t);
+ }
+
+ modify_vtable_entry (get_vtable_entry_n (BINFO_VIRTUALS (binfo), n),
+ build_vtable_entry (this_offset, pfn),
+ fndecl);
+ }
+ }
+ ++n;
+ virtuals = TREE_CHAIN (virtuals);
+ }
+}
+
+/* These are the ones that are through virtual base classes. */
+static void
+modify_all_indirect_vtables (binfo, do_self, via_virtual, t, fndecl, pfn)
+ tree binfo, t, fndecl, pfn;
+ int do_self, via_virtual;
+{
+ tree binfos = BINFO_BASETYPES (binfo);
+ int i, n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+
+ /* Should we use something besides CLASSTYPE_VFIELDS? */
+ if (do_self && via_virtual && CLASSTYPE_VFIELDS (BINFO_TYPE (binfo)))
+ {
+ modify_one_vtable (binfo, t, fndecl, pfn);
+ }
+
+ for (i = 0; i < n_baselinks; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+ int is_not_base_vtable =
+ i != CLASSTYPE_VFIELD_PARENT (BINFO_TYPE (binfo));
+ if (TREE_VIA_VIRTUAL (base_binfo))
+ {
+ via_virtual = 1;
+ base_binfo = binfo_member (BINFO_TYPE (base_binfo), CLASSTYPE_VBASECLASSES (t));
+ }
+ modify_all_indirect_vtables (base_binfo, is_not_base_vtable, via_virtual, t, fndecl, pfn);
+ }
+}
+
+static void
+modify_all_vtables (t, fndecl, vfn)
+ tree t, fndecl, vfn;
+{
+ /* Do these first, so that we will make use of any non-virtual class's
+ vtable, over a virtual classes vtable. */
+ modify_all_direct_vtables (TYPE_BINFO (t), 1, t, fndecl, vfn);
+ if (TYPE_USES_VIRTUAL_BASECLASSES (t))
+ modify_all_indirect_vtables (TYPE_BINFO (t), 1, 0, t, fndecl, vfn);
+}
+
+/* Here, we already know that they match in every respect.
+ All we have to check is where they had their declarations. */
+static int
+strictly_overrides (fndecl1, fndecl2)
+ tree fndecl1, fndecl2;
+{
+ int distance = get_base_distance (DECL_CLASS_CONTEXT (fndecl2),
+ DECL_CLASS_CONTEXT (fndecl1),
+ 0, (tree *)0);
+ if (distance == -2 || distance > 0)
+ return 1;
+ return 0;
+}
+
+/* Merge overrides for one vtable.
+ If we want to merge in same function, we are fine.
+ else
+ if one has a DECL_CLASS_CONTEXT that is a parent of the
+ other, than choose the more derived one
+ else
+ potentially ill-formed (see 10.3 [class.virtual])
+ we have to check later to see if there was an
+ override in this class. If there was ok, if not
+ then it is ill-formed. (mrs)
+
+ We take special care to reuse a vtable, if we can. */
+static void
+override_one_vtable (binfo, old, t)
+ tree binfo, old, t;
+{
+ tree virtuals = BINFO_VIRTUALS (binfo);
+ tree old_virtuals = BINFO_VIRTUALS (old);
+ enum { REUSE_NEW, REUSE_OLD, UNDECIDED, NEITHER } choose = UNDECIDED;
+
+ /* If we have already committed to modifying it, then don't try and
+ reuse another vtable. */
+ if (BINFO_NEW_VTABLE_MARKED (binfo))
+ choose = NEITHER;
+
+ /* Skip size entry. */
+ virtuals = TREE_CHAIN (virtuals);
+ /* Skip RTTI fake object. */
+ if (flag_dossier)
+ {
+ virtuals = TREE_CHAIN (virtuals);
+ }
+
+ /* Skip size entry. */
+ old_virtuals = TREE_CHAIN (old_virtuals);
+ /* Skip RTTI fake object. */
+ if (flag_dossier)
+ {
+ old_virtuals = TREE_CHAIN (old_virtuals);
+ }
+
+ while (virtuals)
+ {
+ tree fndecl = TREE_VALUE (virtuals);
+ tree old_fndecl = TREE_VALUE (old_virtuals);
+ fndecl = FNADDR_FROM_VTABLE_ENTRY (fndecl);
+ old_fndecl = FNADDR_FROM_VTABLE_ENTRY (old_fndecl);
+ fndecl = TREE_OPERAND (fndecl, 0);
+ old_fndecl = TREE_OPERAND (old_fndecl, 0);
+ /* First check to see if they are the same. */
+ if (DECL_ASSEMBLER_NAME (fndecl) == DECL_ASSEMBLER_NAME (old_fndecl))
+ {
+ /* No need to do anything. */
+ }
+ else if (strictly_overrides (fndecl, old_fndecl))
+ {
+ if (choose == UNDECIDED)
+ choose = REUSE_NEW;
+ else if (choose == REUSE_OLD)
+ {
+ choose = NEITHER;
+ if (! BINFO_NEW_VTABLE_MARKED (binfo))
+ {
+ prepare_fresh_vtable (binfo, t);
+ override_one_vtable (binfo, old, t);
+ return;
+ }
+ }
+ }
+ else if (strictly_overrides (old_fndecl, fndecl))
+ {
+ if (choose == UNDECIDED)
+ choose = REUSE_OLD;
+ else if (choose == REUSE_NEW)
+ {
+ choose = NEITHER;
+ if (! BINFO_NEW_VTABLE_MARKED (binfo))
+ {
+ prepare_fresh_vtable (binfo, t);
+ override_one_vtable (binfo, old, t);
+ return;
+ }
+ TREE_VALUE (virtuals) = TREE_VALUE (old_virtuals);
+ }
+ else if (choose == NEITHER)
+ {
+ TREE_VALUE (virtuals) = TREE_VALUE (old_virtuals);
+ }
+ }
+ else
+ {
+ choose = NEITHER;
+ if (! BINFO_NEW_VTABLE_MARKED (binfo))
+ {
+ prepare_fresh_vtable (binfo, t);
+ override_one_vtable (binfo, old, t);
+ return;
+ }
+ {
+ /* This MUST be overriden, or the class is ill-formed. */
+ /* For now, we just make it abstract. */
+ tree fndecl = TREE_OPERAND (FNADDR_FROM_VTABLE_ENTRY (TREE_VALUE (virtuals)), 0);
+ tree vfn;
+
+ fndecl = copy_node (fndecl);
+ copy_lang_decl (fndecl);
+ DECL_ABSTRACT_VIRTUAL_P (fndecl) = 1;
+ /* Make sure we search for it later. */
+ if (! CLASSTYPE_ABSTRACT_VIRTUALS (t))
+ CLASSTYPE_ABSTRACT_VIRTUALS (t) = error_mark_node;
+
+ vfn = build1 (ADDR_EXPR, vfunc_ptr_type_node, fndecl);
+ TREE_CONSTANT (vfn) = 1;
+
+ /* We can use integer_zero_node, as we will will core dump
+ if this is used anyway. */
+ TREE_VALUE (virtuals) = build_vtable_entry (integer_zero_node, vfn);
+ }
+ }
+ virtuals = TREE_CHAIN (virtuals);
+ old_virtuals = TREE_CHAIN (old_virtuals);
+ }
+
+ /* Let's reuse the old vtable. */
+ if (choose == REUSE_OLD)
+ {
+ BINFO_VTABLE (binfo) = BINFO_VTABLE (old);
+ BINFO_VIRTUALS (binfo) = BINFO_VIRTUALS (old);
+ }
+}
+
+/* Merge in overrides for virtual bases.
+ BINFO is the hierarchy we want to modify, and OLD has the potential
+ overrides. */
+static void
+merge_overrides (binfo, old, do_self, t)
+ tree binfo, old, t;
+ int do_self;
+{
+ tree binfos = BINFO_BASETYPES (binfo);
+ tree old_binfos = BINFO_BASETYPES (old);
+ int i, n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+
+ /* Should we use something besides CLASSTYPE_VFIELDS? */
+ if (do_self && CLASSTYPE_VFIELDS (BINFO_TYPE (binfo)))
+ {
+ override_one_vtable (binfo, old, t);
+ }
+
+ for (i = 0; i < n_baselinks; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+ tree old_base_binfo = TREE_VEC_ELT (old_binfos, i);
+ int is_not_base_vtable =
+ i != CLASSTYPE_VFIELD_PARENT (BINFO_TYPE (binfo));
+ if (! TREE_VIA_VIRTUAL (base_binfo))
+ merge_overrides (base_binfo, old_base_binfo, is_not_base_vtable, t);
+ }
+}
+
+/* Create a RECORD_TYPE or UNION_TYPE node for a C struct or union declaration
+ (or C++ class declaration).
+
+ For C++, we must handle the building of derived classes.
+ Also, C++ allows static class members. The way that this is
+ handled is to keep the field name where it is (as the DECL_NAME
+ of the field), and place the overloaded decl in the DECL_FIELD_BITPOS
+ of the field. layout_record and layout_union will know about this.
+
+ More C++ hair: inline functions have text in their
+ DECL_PENDING_INLINE_INFO nodes which must somehow be parsed into
+ meaningful tree structure. After the struct has been laid out, set
+ things up so that this can happen.
+
+ And still more: virtual functions. In the case of single inheritance,
+ when a new virtual function is seen which redefines a virtual function
+ from the base class, the new virtual function is placed into
+ the virtual function table at exactly the same address that
+ it had in the base class. When this is extended to multiple
+ inheritance, the same thing happens, except that multiple virtual
+ function tables must be maintained. The first virtual function
+ table is treated in exactly the same way as in the case of single
+ inheritance. Additional virtual function tables have different
+ DELTAs, which tell how to adjust `this' to point to the right thing.
+
+ LIST_OF_FIELDLISTS is just that. The elements of the list are
+ TREE_LIST elements, whose TREE_PURPOSE field tells what access
+ the list has, and the TREE_VALUE slot gives the actual fields.
+
+ If flag_all_virtual == 1, then we lay all functions into
+ the virtual function table, as though they were declared
+ virtual. Constructors do not lay down in the virtual function table.
+
+ If flag_all_virtual == 2, then we lay all functions into
+ the virtual function table, such that virtual functions
+ occupy a space by themselves, and then all functions
+ of the class occupy a space by themselves. This is illustrated
+ in the following diagram:
+
+ class A; class B : A;
+
+ Class A's vtbl: Class B's vtbl:
+ --------------------------------------------------------------------
+ | A's virtual functions| | B's virtual functions |
+ | | | (may inherit some from A). |
+ --------------------------------------------------------------------
+ | All of A's functions | | All of A's functions |
+ | (such as a->A::f). | | (such as b->A::f) |
+ --------------------------------------------------------------------
+ | B's new virtual functions |
+ | (not defined in A.) |
+ -------------------------------
+ | All of B's functions |
+ | (such as b->B::f) |
+ -------------------------------
+
+ this allows the program to make references to any function, virtual
+ or otherwise in a type-consistent manner. */
+
+tree
+finish_struct (t, list_of_fieldlists, warn_anon)
+ tree t;
+ tree list_of_fieldlists;
+ int warn_anon;
+{
+ extern int interface_only, interface_unknown;
+
+ int old;
+ int round_up_size = 1;
+
+ enum tree_code code = TREE_CODE (t);
+ register tree x, last_x, method_vec;
+ int needs_virtual_dtor;
+ tree name = TYPE_NAME (t), fields, fn_fields, *tail;
+ tree *tail_user_methods = &CLASSTYPE_METHODS (t);
+ enum access_type access;
+ int all_virtual;
+ int has_virtual;
+ int max_has_virtual;
+ tree pending_virtuals = NULL_TREE;
+ tree abstract_virtuals = NULL_TREE;
+ tree vfield;
+ tree vfields;
+ int cant_have_default_ctor;
+ int cant_have_const_ctor;
+ int cant_synth_copy_ctor;
+ int cant_synth_asn_ref;
+ int no_const_asn_ref;
+
+ /* The index of the first base class which has virtual
+ functions. Only applied to non-virtual baseclasses. */
+ int first_vfn_base_index;
+
+ int n_baseclasses;
+ int any_default_members = 0;
+ int const_sans_init = 0;
+ int ref_sans_init = 0;
+ int nonprivate_method = 0;
+ tree t_binfo = TYPE_BINFO (t);
+ tree access_decls = NULL_TREE;
+
+ if (TREE_CODE (name) == TYPE_DECL)
+ {
+#if 0 /* Maybe later. -jason */
+ struct tinst_level *til = tinst_for_decl();
+
+ if (til)
+ {
+ DECL_SOURCE_FILE (name) = til->file;
+ if (DECL_SOURCE_LINE (name))
+ DECL_SOURCE_LINE (name) = til->line;
+ }
+ else
+#endif
+ {
+ extern int lineno;
+
+ DECL_SOURCE_FILE (name) = input_filename;
+ /* For TYPE_DECL that are not typedefs (those marked with a line
+ number of zero, we don't want to mark them as real typedefs.
+ If this fails one needs to make sure real typedefs have a
+ previous line number, even if it is wrong, that way the below
+ will fill in the right line number. (mrs) */
+ if (DECL_SOURCE_LINE (name))
+ DECL_SOURCE_LINE (name) = lineno;
+ }
+ name = DECL_NAME (name);
+ }
+
+ if (warn_anon && code != UNION_TYPE && ANON_AGGRNAME_P (name))
+ pedwarn ("anonymous class type not used to declare any objects");
+
+ if (TYPE_SIZE (t))
+ {
+ if (IS_AGGR_TYPE (t))
+ cp_error ("redefinition of `%#T'", t);
+ else
+ my_friendly_abort (172);
+ popclass (0);
+ return t;
+ }
+
+ /* Append the fields we need for constructing signature tables. */
+ if (IS_SIGNATURE (t))
+ append_signature_fields (list_of_fieldlists);
+
+ GNU_xref_decl (current_function_decl, t);
+
+ /* If this type was previously laid out as a forward reference,
+ make sure we lay it out again. */
+
+ TYPE_SIZE (t) = NULL_TREE;
+ CLASSTYPE_GOT_SEMICOLON (t) = 0;
+
+ /* A signature type will contain the fields of the signature table.
+ Therefore, it's not only an interface. */
+ if (IS_SIGNATURE (t))
+ {
+ CLASSTYPE_INTERFACE_ONLY (t) = 0;
+ SET_CLASSTYPE_INTERFACE_KNOWN (t);
+ }
+ else
+ {
+ CLASSTYPE_INTERFACE_ONLY (t) = interface_only;
+ SET_CLASSTYPE_INTERFACE_UNKNOWN_X (t, interface_unknown);
+ }
+
+ if (flag_dossier)
+ build_t_desc (t, 0);
+
+ TYPE_BINFO (t) = NULL_TREE;
+
+ old = suspend_momentary ();
+
+ /* Install struct as DECL_FIELD_CONTEXT of each field decl.
+ Also process specified field sizes.
+ Set DECL_FIELD_SIZE to the specified size, or 0 if none specified.
+ The specified size is found in the DECL_INITIAL.
+ Store 0 there, except for ": 0" fields (so we can find them
+ and delete them, below). */
+
+ if (t_binfo && BINFO_BASETYPES (t_binfo))
+ n_baseclasses = TREE_VEC_LENGTH (BINFO_BASETYPES (t_binfo));
+ else
+ n_baseclasses = 0;
+
+ if (n_baseclasses > 0)
+ {
+ struct base_info base_info;
+
+ /* If using multiple inheritance, this may cause variants of our
+ basetypes to be used (instead of their canonical forms). */
+ fields = layout_basetypes (t, BINFO_BASETYPES (t_binfo));
+ last_x = tree_last (fields);
+
+ first_vfn_base_index = finish_base_struct (t, &base_info, t_binfo);
+ /* Remember where we got our vfield from */
+ CLASSTYPE_VFIELD_PARENT (t) = first_vfn_base_index;
+ has_virtual = base_info.has_virtual;
+ max_has_virtual = base_info.max_has_virtual;
+ CLASSTYPE_N_SUPERCLASSES (t) += base_info.n_ancestors;
+ vfield = base_info.vfield;
+ vfields = base_info.vfields;
+ cant_have_default_ctor = base_info.cant_have_default_ctor;
+ cant_have_const_ctor = base_info.cant_have_const_ctor;
+ cant_synth_copy_ctor = base_info.cant_synth_copy_ctor;
+ cant_synth_asn_ref = base_info.cant_synth_asn_ref;
+ no_const_asn_ref = base_info.no_const_asn_ref;
+ needs_virtual_dtor = base_info.needs_virtual_dtor;
+ n_baseclasses = TREE_VEC_LENGTH (BINFO_BASETYPES (t_binfo));
+ }
+ else
+ {
+ first_vfn_base_index = -1;
+ has_virtual = 0;
+ max_has_virtual = has_virtual;
+ vfield = NULL_TREE;
+ vfields = NULL_TREE;
+ fields = NULL_TREE;
+ last_x = NULL_TREE;
+ cant_have_default_ctor = 0;
+ cant_have_const_ctor = 0;
+ cant_synth_copy_ctor = 0;
+ cant_synth_asn_ref = 0;
+ no_const_asn_ref = 0;
+ needs_virtual_dtor = 0;
+ }
+
+ if (write_virtuals == 3 && CLASSTYPE_INTERFACE_KNOWN (t)
+ && ! IS_SIGNATURE (t))
+ {
+ CLASSTYPE_INTERFACE_ONLY (t) = interface_only;
+ CLASSTYPE_VTABLE_NEEDS_WRITING (t) = ! interface_only;
+ }
+ else if (IS_SIGNATURE (t))
+ CLASSTYPE_VTABLE_NEEDS_WRITING (t) = 0;
+
+ /* The three of these are approximations which may later be
+ modified. Needed at this point to make add_virtual_function
+ and modify_vtable_entries work. */
+ TREE_CHAIN (t_binfo) = TYPE_BINFO (t);
+ TYPE_BINFO (t) = t_binfo;
+ CLASSTYPE_VFIELDS (t) = vfields;
+ CLASSTYPE_VFIELD (t) = vfield;
+
+ tail = &fn_fields;
+ if (last_x && list_of_fieldlists)
+ TREE_CHAIN (last_x) = TREE_VALUE (list_of_fieldlists);
+
+ if (IS_SIGNATURE (t))
+ all_virtual = 0;
+ else if (flag_all_virtual == 1 && TYPE_OVERLOADS_METHOD_CALL_EXPR (t))
+ all_virtual = 1;
+ else
+ all_virtual = 0;
+
+ /* For signatures, we made all methods `public' in the parser and
+ reported an error if a access specifier was used. */
+ if (CLASSTYPE_DECLARED_CLASS (t) == 0)
+ {
+ nonprivate_method = 1;
+ if (list_of_fieldlists
+ && TREE_PURPOSE (list_of_fieldlists) == (tree)access_default)
+ TREE_PURPOSE (list_of_fieldlists) = (tree)access_public;
+ }
+ else if (list_of_fieldlists
+ && TREE_PURPOSE (list_of_fieldlists) == (tree)access_default)
+ TREE_PURPOSE (list_of_fieldlists) = (tree)access_private;
+
+ while (list_of_fieldlists)
+ {
+ access = (enum access_type)TREE_PURPOSE (list_of_fieldlists);
+
+ for (x = TREE_VALUE (list_of_fieldlists); x; x = TREE_CHAIN (x))
+ {
+ TREE_PRIVATE (x) = access == access_private;
+ TREE_PROTECTED (x) = access == access_protected;
+ GNU_xref_member (current_class_name, x);
+
+ if (TREE_CODE (x) == TYPE_DECL)
+ {
+ /* Make sure we set this up. In find_scoped_type, it explicitly
+ looks for a TYPE_DECL in the TYPE_FIELDS list. If we don't
+ do this here, we'll miss including this TYPE_DECL in the
+ list. */
+ if (! fields)
+ fields = x;
+ last_x = x;
+ continue;
+ }
+
+ /* Check for inconsistent use of this name in the class body.
+ Enums, types and static vars have already been checked. */
+ if (TREE_CODE (x) != CONST_DECL && TREE_CODE (x) != VAR_DECL)
+ {
+ tree name = DECL_NAME (x);
+ tree icv;
+
+ /* Don't get confused by access decls. */
+ if (name && TREE_CODE (name) == IDENTIFIER_NODE)
+ icv = IDENTIFIER_CLASS_VALUE (name);
+ else
+ icv = NULL_TREE;
+
+ if (icv
+ /* Don't complain about constructors. */
+ && name != constructor_name (current_class_type)
+ /* Or inherited names. */
+ && id_in_current_class (name)
+ /* Or shadowed tags. */
+ && !(TREE_CODE (icv) == TYPE_DECL
+ && DECL_CONTEXT (icv) == t))
+ {
+ cp_error_at ("declaration of identifier `%D' as `%+#D'",
+ name, x);
+ cp_error_at ("conflicts with other use in class as `%#D'",
+ icv);
+ }
+ }
+
+ if (TREE_CODE (x) == FUNCTION_DECL)
+ {
+ nonprivate_method |= ! TREE_PRIVATE (x);
+
+ /* If this was an evil function, don't keep it in class. */
+ if (IDENTIFIER_ERROR_LOCUS (DECL_ASSEMBLER_NAME (x)))
+ continue;
+
+ if (last_x)
+ TREE_CHAIN (last_x) = TREE_CHAIN (x);
+ /* Link x onto end of fn_fields and CLASSTYPE_METHODS. */
+ *tail = x;
+ tail = &TREE_CHAIN (x);
+ *tail_user_methods = x;
+ tail_user_methods = &DECL_NEXT_METHOD (x);
+
+ DECL_CLASS_CONTEXT (x) = t;
+
+ DECL_FIELD_SIZE (x) = 0;
+
+ /* The name of the field is the original field name
+ Save this in auxiliary field for later overloading. */
+ if (DECL_VINDEX (x)
+ || (all_virtual == 1 && ! DECL_CONSTRUCTOR_P (x)))
+ {
+ pending_virtuals = add_virtual_function (pending_virtuals,
+ &has_virtual, x, t);
+ if (DECL_ABSTRACT_VIRTUAL_P (x))
+ abstract_virtuals = tree_cons (NULL_TREE, x, abstract_virtuals);
+ }
+ continue;
+ }
+
+ /* Handle access declarations. */
+ if (DECL_NAME (x) && TREE_CODE (DECL_NAME (x)) == SCOPE_REF)
+ {
+ tree fdecl = TREE_OPERAND (DECL_NAME (x), 1);
+
+ if (last_x)
+ TREE_CHAIN (last_x) = TREE_CHAIN (x);
+ access_decls = tree_cons ((tree) access, fdecl, access_decls);
+ continue;
+ }
+
+ /* If we've gotten this far, it's a data member, possibly static,
+ or an enumerator. */
+
+ DECL_FIELD_CONTEXT (x) = t;
+
+ /* ``A local class cannot have static data members.'' ARM 9.4 */
+ if (current_function_decl && TREE_STATIC (x))
+ cp_error_at ("field `%D' in local class cannot be static", x);
+
+ /* Perform error checking that did not get done in
+ grokdeclarator. */
+ if (TREE_CODE (TREE_TYPE (x)) == FUNCTION_TYPE)
+ {
+ cp_error_at ("field `%D' invalidly declared function type",
+ x);
+ TREE_TYPE (x) = build_pointer_type (TREE_TYPE (x));
+ }
+ else if (TREE_CODE (TREE_TYPE (x)) == METHOD_TYPE)
+ {
+ cp_error_at ("field `%D' invalidly declared method type", x);
+ TREE_TYPE (x) = build_pointer_type (TREE_TYPE (x));
+ }
+ else if (TREE_CODE (TREE_TYPE (x)) == OFFSET_TYPE)
+ {
+ cp_error_at ("field `%D' invalidly declared offset type", x);
+ TREE_TYPE (x) = build_pointer_type (TREE_TYPE (x));
+ }
+
+ if (TREE_TYPE (x) == error_mark_node)
+ continue;
+
+ if (! fields)
+ fields = x;
+ last_x = x;
+
+ DECL_FIELD_SIZE (x) = 0;
+
+ /* When this goes into scope, it will be a non-local reference. */
+ DECL_NONLOCAL (x) = 1;
+
+ if (TREE_CODE (x) == CONST_DECL)
+ continue;
+
+ if (TREE_CODE (x) == VAR_DECL)
+ {
+ if (TREE_CODE (t) == UNION_TYPE)
+ /* Unions cannot have static members. */
+ cp_error_at ("field `%D' declared static in union", x);
+
+ continue;
+ }
+
+ /* Now it can only be a FIELD_DECL. */
+
+ /* If this is of reference type, check if it needs an init.
+ Also do a little ANSI jig if necessary. */
+ if (TREE_CODE (TREE_TYPE (x)) == REFERENCE_TYPE)
+ {
+ if (DECL_INITIAL (x) == NULL_TREE)
+ ref_sans_init = 1;
+
+ /* ARM $12.6.2: [A member initializer list] (or, for an
+ aggregate, initialization by a brace-enclosed list) is the
+ only way to initialize nonstatic const and reference
+ members. */
+ cant_synth_asn_ref = 1;
+ cant_have_default_ctor = 1;
+ TYPE_HAS_COMPLEX_INIT_REF (t) = 1;
+
+ if (! TYPE_HAS_CONSTRUCTOR (t) && extra_warnings)
+ {
+ if (DECL_NAME (x))
+ cp_warning_at ("non-static reference `%#D' in class without a constructor", x);
+ else
+ cp_warning_at ("non-static reference in class without a constructor", x);
+ }
+ }
+
+ /* If any field is const, the structure type is pseudo-const. */
+ if (TREE_READONLY (x))
+ {
+ C_TYPE_FIELDS_READONLY (t) = 1;
+ if (DECL_INITIAL (x) == NULL_TREE)
+ const_sans_init = 1;
+
+ /* ARM $12.6.2: [A member initializer list] (or, for an
+ aggregate, initialization by a brace-enclosed list) is the
+ only way to initialize nonstatic const and reference
+ members. */
+ cant_synth_asn_ref = 1;
+ cant_have_default_ctor = 1;
+ TYPE_HAS_COMPLEX_INIT_REF (t) = 1;
+
+ if (! TYPE_HAS_CONSTRUCTOR (t) && !IS_SIGNATURE (t)
+ && extra_warnings)
+ {
+ if (DECL_NAME (x))
+ cp_warning_at ("non-static const member `%#D' in class without a constructor", x);
+ else
+ cp_warning_at ("non-static const member in class without a constructor", x);
+ }
+ }
+ else
+ {
+ /* A field that is pseudo-const makes the structure
+ likewise. */
+ tree t1 = TREE_TYPE (x);
+ while (TREE_CODE (t1) == ARRAY_TYPE)
+ t1 = TREE_TYPE (t1);
+ if (IS_AGGR_TYPE (t1))
+ {
+ if (C_TYPE_FIELDS_READONLY (t1))
+ C_TYPE_FIELDS_READONLY (t) = 1;
+ if (CLASSTYPE_READONLY_FIELDS_NEED_INIT (t1))
+ const_sans_init = 1;
+ }
+ }
+
+ /* We set DECL_BIT_FIELD tentatively in grokbitfield.
+ If the type and width are valid, we'll keep it set.
+ Otherwise, the flag is cleared. */
+ if (DECL_BIT_FIELD (x))
+ {
+ DECL_BIT_FIELD (x) = 0;
+ /* Invalid bit-field size done by grokfield. */
+ /* Detect invalid bit-field type. */
+ if (DECL_INITIAL (x)
+ && ! INTEGRAL_TYPE_P (TREE_TYPE (x)))
+ {
+ cp_error_at ("bit-field `%#D' with non-integral type", x);
+ DECL_INITIAL (x) = NULL;
+ }
+
+ /* Detect and ignore out of range field width. */
+ if (DECL_INITIAL (x))
+ {
+ register int width = TREE_INT_CST_LOW (DECL_INITIAL (x));
+
+ if (width < 0)
+ {
+ DECL_INITIAL (x) = NULL;
+ cp_error_at ("negative width in bit-field `%D'", x);
+ }
+ else if (width == 0 && DECL_NAME (x) != 0)
+ {
+ DECL_INITIAL (x) = NULL;
+ cp_error_at ("zero width for bit-field `%D'", x);
+ }
+ else if ((unsigned)width > TYPE_PRECISION (TREE_TYPE (x)))
+ {
+ DECL_INITIAL (x) = NULL;
+ cp_error_at ("width of `%D' exceeds its type", x);
+ }
+ }
+
+ /* Process valid field width. */
+ if (DECL_INITIAL (x))
+ {
+ register int width = TREE_INT_CST_LOW (DECL_INITIAL (x));
+
+ if (width == 0)
+ {
+#ifdef EMPTY_FIELD_BOUNDARY
+ /* field size 0 => mark following field as "aligned" */
+ if (TREE_CHAIN (x))
+ DECL_ALIGN (TREE_CHAIN (x))
+ = MAX (DECL_ALIGN (TREE_CHAIN (x)), EMPTY_FIELD_BOUNDARY);
+ /* field of size 0 at the end => round up the size. */
+ else
+ round_up_size = EMPTY_FIELD_BOUNDARY;
+#endif
+#ifdef PCC_BITFIELD_TYPE_MATTERS
+ DECL_ALIGN (x) = MAX (DECL_ALIGN (x),
+ TYPE_ALIGN (TREE_TYPE (x)));
+#endif
+ }
+ else
+ {
+ DECL_INITIAL (x) = NULL_TREE;
+ DECL_FIELD_SIZE (x) = width;
+ DECL_BIT_FIELD (x) = 1;
+ /* Traditionally a bit field is unsigned
+ even if declared signed. */
+ if (flag_traditional
+ && TREE_CODE (TREE_TYPE (x)) == INTEGER_TYPE)
+ TREE_TYPE (x) = unsigned_type_node;
+ }
+ }
+ else
+ /* Non-bit-fields are aligned for their type. */
+ DECL_ALIGN (x) = MAX (DECL_ALIGN (x), TYPE_ALIGN (TREE_TYPE (x)));
+ }
+ else
+ {
+ tree type = TREE_TYPE (x);
+
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ type = TREE_TYPE (type);
+
+ if (TYPE_LANG_SPECIFIC (type) && ! ANON_UNION_P (x)
+ && ! TYPE_PTRMEMFUNC_P (type))
+ {
+ /* Never let anything with uninheritable virtuals
+ make it through without complaint. */
+ if (CLASSTYPE_ABSTRACT_VIRTUALS (type))
+ abstract_virtuals_error (x, type);
+
+ /* Don't let signatures make it through either. */
+ if (IS_SIGNATURE (type))
+ signature_error (x, type);
+
+ if (code == UNION_TYPE)
+ {
+ char *fie = NULL;
+ if (TYPE_NEEDS_CONSTRUCTING (type))
+ fie = "constructor";
+ else if (TYPE_NEEDS_DESTRUCTOR (type))
+ fie = "destructor";
+ else if (TYPE_HAS_REAL_ASSIGNMENT (type))
+ fie = "assignment operator";
+ if (fie)
+ cp_error_at ("member `%#D' with %s not allowed in union", x,
+ fie);
+ }
+ else
+ {
+ TYPE_NEEDS_CONSTRUCTING (t) |= TYPE_NEEDS_CONSTRUCTING (type);
+ TYPE_NEEDS_DESTRUCTOR (t) |= TYPE_NEEDS_DESTRUCTOR (type);
+ TYPE_HAS_COMPLEX_ASSIGN_REF (t) |= TYPE_HAS_COMPLEX_ASSIGN_REF (type);
+ TYPE_HAS_COMPLEX_INIT_REF (t)
+ |= (TYPE_HAS_COMPLEX_INIT_REF (type)
+ || TYPE_NEEDS_CONSTRUCTING (type));
+ }
+
+ if (! TYPE_HAS_INIT_REF (type)
+ || (TYPE_HAS_NONPUBLIC_CTOR (type)
+ && ! is_friend (t, type)))
+ cant_synth_copy_ctor = 1;
+ else if (!TYPE_HAS_CONST_INIT_REF (type))
+ cant_have_const_ctor = 1;
+
+ if (! TYPE_HAS_ASSIGN_REF (type)
+ || (TYPE_HAS_NONPUBLIC_ASSIGN_REF (type)
+ && ! is_friend (t, type)))
+ cant_synth_asn_ref = 1;
+ else if (!TYPE_HAS_CONST_ASSIGN_REF (type))
+ no_const_asn_ref = 1;
+
+ if (TYPE_HAS_CONSTRUCTOR (type)
+ && ! TYPE_HAS_DEFAULT_CONSTRUCTOR (type))
+ {
+ cant_have_default_ctor = 1;
+ if (! TYPE_HAS_CONSTRUCTOR (t))
+ {
+ if (DECL_NAME (x))
+ cp_pedwarn_at ("member `%#D' with only non-default constructor", x);
+ else
+ cp_pedwarn_at ("member with only non-default constructor", x);
+ cp_pedwarn_at ("in class without a constructor",
+ x);
+ }
+ }
+ }
+ if (DECL_INITIAL (x) != NULL_TREE)
+ {
+ /* `build_class_init_list' does not recognize
+ non-FIELD_DECLs. */
+ if (code == UNION_TYPE && any_default_members != 0)
+ cp_error_at ("multiple fields in union `%T' initialized");
+ any_default_members = 1;
+ }
+ }
+ }
+ list_of_fieldlists = TREE_CHAIN (list_of_fieldlists);
+ /* link the tail while we have it! */
+ if (last_x)
+ {
+ TREE_CHAIN (last_x) = NULL_TREE;
+
+ if (list_of_fieldlists
+ && TREE_VALUE (list_of_fieldlists)
+ && TREE_CODE (TREE_VALUE (list_of_fieldlists)) != FUNCTION_DECL)
+ TREE_CHAIN (last_x) = TREE_VALUE (list_of_fieldlists);
+ }
+ }
+
+ /* If this type has any constant members which did not come
+ with their own initialization, mark that fact here. It is
+ not an error here, since such types can be saved either by their
+ constructors, or by fortuitous initialization. */
+ CLASSTYPE_READONLY_FIELDS_NEED_INIT (t) = const_sans_init;
+ CLASSTYPE_REF_FIELDS_NEED_INIT (t) = ref_sans_init;
+ CLASSTYPE_ABSTRACT_VIRTUALS (t) = abstract_virtuals;
+
+ if (TYPE_NEEDS_DESTRUCTOR (t) && !TYPE_HAS_DESTRUCTOR (t)
+ && !IS_SIGNATURE (t))
+ {
+ /* Here we must cons up a destructor on the fly. */
+ tree dtor = cons_up_default_function (t, name, fields,
+ needs_virtual_dtor != 0);
+
+ /* If we couldn't make it work, then pretend we didn't need it. */
+ if (dtor == void_type_node)
+ TYPE_NEEDS_DESTRUCTOR (t) = 0;
+ else
+ {
+ /* Link dtor onto end of fn_fields. */
+ *tail = dtor;
+ tail = &TREE_CHAIN (dtor);
+
+ if (DECL_VINDEX (dtor) == NULL_TREE
+ && ! CLASSTYPE_DECLARED_EXCEPTION (t)
+ && (needs_virtual_dtor
+ || pending_virtuals != NULL_TREE
+ || pending_hard_virtuals != NULL_TREE))
+ DECL_VINDEX (dtor) = error_mark_node;
+ if (DECL_VINDEX (dtor))
+ pending_virtuals = add_virtual_function (pending_virtuals,
+ &has_virtual, dtor, t);
+ nonprivate_method = 1;
+ }
+ }
+
+ *tail = NULL_TREE;
+ *tail_user_methods = NULL_TREE;
+
+ TYPE_NEEDS_DESTRUCTOR (t) |= TYPE_HAS_DESTRUCTOR (t);
+
+ /* Synthesize any needed methods. Note that methods will be synthesized
+ for anonymous unions; grok_x_components undoes that. */
+
+ if (! fn_fields)
+ nonprivate_method = 1;
+
+ TYPE_HAS_COMPLEX_INIT_REF (t)
+ |= (TYPE_HAS_INIT_REF (t) || TYPE_USES_VIRTUAL_BASECLASSES (t)
+ || has_virtual || any_default_members || first_vfn_base_index >= 0);
+ TYPE_NEEDS_CONSTRUCTING (t)
+ |= (TYPE_HAS_CONSTRUCTOR (t) || TYPE_USES_VIRTUAL_BASECLASSES (t)
+ || has_virtual || any_default_members || first_vfn_base_index >= 0);
+
+ /* ARM $12.1: A default constructor will be generated for a class X
+ only if no constructor has been declared for class X. So we
+ check TYPE_HAS_CONSTRUCTOR also, to make sure we don't generate
+ one if they declared a constructor in this class. */
+ if (! TYPE_HAS_CONSTRUCTOR (t) && ! cant_have_default_ctor
+ && ! IS_SIGNATURE (t))
+ {
+ tree default_fn = cons_up_default_function (t, name, fields, 2);
+ TREE_CHAIN (default_fn) = fn_fields;
+ fn_fields = default_fn;
+ }
+
+ /* Create default copy constructor, if needed. */
+ if (! TYPE_HAS_INIT_REF (t) && ! cant_synth_copy_ctor
+ && ! IS_SIGNATURE (t))
+ {
+ /* ARM 12.18: You get either X(X&) or X(const X&), but
+ not both. --Chip */
+ tree default_fn =
+ cons_up_default_function (t, name, fields,
+ cant_have_const_ctor ? 4 : 3);
+ TREE_CHAIN (default_fn) = fn_fields;
+ fn_fields = default_fn;
+ }
+
+ TYPE_HAS_REAL_ASSIGNMENT (t) |= TYPE_HAS_ASSIGNMENT (t);
+ TYPE_HAS_REAL_ASSIGN_REF (t) |= TYPE_HAS_ASSIGN_REF (t);
+ TYPE_HAS_COMPLEX_ASSIGN_REF (t)
+ |= (TYPE_HAS_ASSIGN_REF (t) || TYPE_USES_VIRTUAL_BASECLASSES (t)
+ || has_virtual || first_vfn_base_index >= 0);
+
+ if (! TYPE_HAS_ASSIGN_REF (t) && ! cant_synth_asn_ref
+ && ! IS_SIGNATURE (t))
+ {
+ tree default_fn =
+ cons_up_default_function (t, name, fields,
+ no_const_asn_ref ? 6 : 5);
+ TREE_CHAIN (default_fn) = fn_fields;
+ fn_fields = default_fn;
+ }
+
+ if (fn_fields)
+ {
+ method_vec = finish_struct_methods (t, fn_fields, nonprivate_method);
+
+ if (TYPE_HAS_CONSTRUCTOR (t)
+ && ! CLASSTYPE_DECLARED_EXCEPTION (t)
+ && CLASSTYPE_FRIEND_CLASSES (t) == NULL_TREE
+ && DECL_FRIENDLIST (TYPE_NAME (t)) == NULL_TREE)
+ {
+ int nonprivate_ctor = 0;
+ tree ctor;
+
+ for (ctor = TREE_VEC_ELT (method_vec, 0);
+ ctor;
+ ctor = DECL_CHAIN (ctor))
+ if (! TREE_PRIVATE (ctor))
+ {
+ nonprivate_ctor = 1;
+ break;
+ }
+
+ if (nonprivate_ctor == 0 && warn_ctor_dtor_privacy)
+ cp_warning ("`%#T' only defines private constructors and has no friends",
+ t);
+ }
+ }
+ else
+ {
+ method_vec = 0;
+
+ /* Just in case these got accidentally
+ filled in by syntax errors. */
+ TYPE_HAS_CONSTRUCTOR (t) = 0;
+ TYPE_HAS_DESTRUCTOR (t) = 0;
+ }
+
+ {
+ int n_methods = method_vec ? TREE_VEC_LENGTH (method_vec) : 0;
+
+ for (access_decls = nreverse (access_decls); access_decls;
+ access_decls = TREE_CHAIN (access_decls))
+ {
+ tree fdecl = TREE_VALUE (access_decls);
+ tree flist = NULL_TREE;
+ tree name;
+ enum access_type access = (enum access_type)TREE_PURPOSE(access_decls);
+ int i = 0;
+ tree tmp;
+
+ if (TREE_CODE (fdecl) == TREE_LIST)
+ {
+ flist = fdecl;
+ fdecl = TREE_VALUE (flist);
+ }
+
+ name = DECL_NAME (fdecl);
+
+ for (; i < n_methods; i++)
+ if (DECL_NAME (TREE_VEC_ELT (method_vec, i)) == name)
+ {
+ cp_error ("cannot adjust access to `%#D' in `%#T'", fdecl, t);
+ cp_error_at (" because of local method `%#D' with same name",
+ TREE_VEC_ELT (method_vec, i));
+ fdecl = NULL_TREE;
+ break;
+ }
+
+ if (! fdecl)
+ continue;
+
+ for (tmp = fields; tmp; tmp = TREE_CHAIN (tmp))
+ if (DECL_NAME (tmp) == name)
+ {
+ cp_error ("cannot adjust access to `%#D' in `%#T'", fdecl, t);
+ cp_error_at (" because of local field `%#D' with same name", tmp);
+ fdecl = NULL_TREE;
+ break;
+ }
+
+ if (!fdecl)
+ continue;
+
+ /* Make type T see field decl FDECL with access ACCESS.*/
+ if (flist)
+ {
+ fdecl = TREE_VALUE (flist);
+ while (fdecl)
+ {
+ if (alter_access (t, fdecl, access) == 0)
+ break;
+ fdecl = DECL_CHAIN (fdecl);
+ }
+ }
+ else
+ alter_access (t, fdecl, access);
+ }
+
+ }
+
+ if (vfield == NULL_TREE && has_virtual)
+ {
+ /* We build this decl with ptr_type_node, and
+ change the type when we know what it should be. */
+ vfield = build_lang_field_decl (FIELD_DECL, get_vfield_name (t),
+ ptr_type_node);
+ /* If you change any of the below, take a look at all the
+ other VFIELD_BASEs and VTABLE_BASEs in the code, and change
+ them too. */
+ DECL_ASSEMBLER_NAME (vfield) = get_identifier (VFIELD_BASE);
+ CLASSTYPE_VFIELD (t) = vfield;
+ DECL_VIRTUAL_P (vfield) = 1;
+ DECL_FIELD_CONTEXT (vfield) = t;
+ DECL_CLASS_CONTEXT (vfield) = t;
+ DECL_FCONTEXT (vfield) = t;
+ DECL_FIELD_SIZE (vfield) = 0;
+ DECL_ALIGN (vfield) = TYPE_ALIGN (ptr_type_node);
+ if (CLASSTYPE_DOSSIER (t))
+ {
+ /* vfield is always first entry in structure. */
+ TREE_CHAIN (vfield) = fields;
+ fields = vfield;
+ }
+ else if (last_x)
+ {
+ my_friendly_assert (TREE_CHAIN (last_x) == NULL_TREE, 175);
+ TREE_CHAIN (last_x) = vfield;
+ last_x = vfield;
+ }
+ else
+ fields = vfield;
+ vfields = chainon (vfields, CLASSTYPE_AS_LIST (t));
+ }
+
+ /* Now DECL_INITIAL is null on all members except for zero-width bit-fields.
+ And they have already done their work.
+
+ C++: maybe we will support default field initialization some day... */
+
+ /* Delete all zero-width bit-fields from the front of the fieldlist */
+ while (fields && DECL_BIT_FIELD (fields)
+ && DECL_INITIAL (fields))
+ fields = TREE_CHAIN (fields);
+ /* Delete all such fields from the rest of the fields. */
+ for (x = fields; x;)
+ {
+ if (TREE_CHAIN (x) && DECL_BIT_FIELD (TREE_CHAIN (x))
+ && DECL_INITIAL (TREE_CHAIN (x)))
+ TREE_CHAIN (x) = TREE_CHAIN (TREE_CHAIN (x));
+ else
+ x = TREE_CHAIN (x);
+ }
+ /* Delete all duplicate fields from the fields */
+ delete_duplicate_fields (fields);
+
+ /* Now we have the final fieldlist for the data fields. Record it,
+ then lay out the structure or union (including the fields). */
+
+ TYPE_FIELDS (t) = fields;
+
+ /* If there's a :0 field at the end, round the size to the
+ EMPTY_FIELD_BOUNDARY. */
+ TYPE_ALIGN (t) = round_up_size;
+
+ /* Pass layout information about base classes to layout_type, if any. */
+
+ {
+ tree field;
+ for (field = TYPE_FIELDS (t); field; field = TREE_CHAIN (field))
+ {
+ if (TREE_STATIC (field))
+ continue;
+ if (TREE_CODE (field) != FIELD_DECL)
+ continue;
+
+ /* If this field is an anonymous union,
+ give each union-member the same position as the union has.
+
+ ??? This is a real kludge because it makes the structure
+ of the types look strange. This feature is only used by
+ C++, which should have build_component_ref build two
+ COMPONENT_REF operations, one for the union and one for
+ the inner field. We set the offset of this field to zero
+ so that either the old or the correct method will work.
+ Setting DECL_FIELD_CONTEXT is wrong unless the inner fields are
+ moved into the type of this field, but nothing seems to break
+ by doing this. */
+
+ if (DECL_NAME (field) == NULL_TREE
+ && TREE_CODE (TREE_TYPE (field)) == UNION_TYPE)
+ {
+ tree uelt = TYPE_FIELDS (TREE_TYPE (field));
+ for (; uelt; uelt = TREE_CHAIN (uelt))
+ {
+ DECL_FIELD_CONTEXT (uelt) = DECL_FIELD_CONTEXT (field);
+ DECL_FIELD_BITPOS (uelt) = DECL_FIELD_BITPOS (field);
+ }
+
+ DECL_FIELD_BITPOS (field) = integer_zero_node;
+ }
+ }
+ }
+
+ if (n_baseclasses)
+ {
+ tree pseudo_basetype = TREE_TYPE (base_layout_decl);
+
+ TREE_CHAIN (base_layout_decl) = TYPE_FIELDS (t);
+ TYPE_FIELDS (t) = base_layout_decl;
+
+ TYPE_SIZE (pseudo_basetype) = CLASSTYPE_SIZE (t);
+ TYPE_MODE (pseudo_basetype) = TYPE_MODE (t);
+ TYPE_ALIGN (pseudo_basetype) = CLASSTYPE_ALIGN (t);
+ DECL_ALIGN (base_layout_decl) = TYPE_ALIGN (pseudo_basetype);
+ /* Don't re-use old size. */
+ DECL_SIZE (base_layout_decl) = NULL_TREE;
+ }
+
+ layout_type (t);
+
+ {
+ tree field;
+ for (field = TYPE_FIELDS (t); field; field = TREE_CHAIN (field))
+ {
+ if (TREE_STATIC (field))
+ continue;
+ if (TREE_CODE (field) != FIELD_DECL)
+ continue;
+
+ /* If this field is an anonymous union,
+ give each union-member the same position as the union has.
+
+ ??? This is a real kludge because it makes the structure
+ of the types look strange. This feature is only used by
+ C++, which should have build_component_ref build two
+ COMPONENT_REF operations, one for the union and one for
+ the inner field. We set the offset of this field to zero
+ so that either the old or the correct method will work.
+ Setting DECL_FIELD_CONTEXT is wrong unless the inner fields are
+ moved into the type of this field, but nothing seems to break
+ by doing this. */
+
+ if (DECL_NAME (field) == NULL_TREE
+ && TREE_CODE (TREE_TYPE (field)) == UNION_TYPE)
+ {
+ tree uelt = TYPE_FIELDS (TREE_TYPE (field));
+ for (; uelt; uelt = TREE_CHAIN (uelt))
+ {
+ DECL_FIELD_CONTEXT (uelt) = DECL_FIELD_CONTEXT (field);
+ DECL_FIELD_BITPOS (uelt) = DECL_FIELD_BITPOS (field);
+ }
+
+ DECL_FIELD_BITPOS (field) = integer_zero_node;
+ }
+ }
+ }
+
+ if (n_baseclasses)
+ TYPE_FIELDS (t) = TREE_CHAIN (TYPE_FIELDS (t));
+
+ /* C++: do not let empty structures exist. */
+ if (integer_zerop (TYPE_SIZE (t)))
+ TYPE_SIZE (t) = TYPE_SIZE (char_type_node);
+
+ /* Set the TYPE_DECL for this type to contain the right
+ value for DECL_OFFSET, so that we can use it as part
+ of a COMPONENT_REF for multiple inheritance. */
+
+ if (TREE_CODE (TYPE_NAME (t)) == TYPE_DECL)
+ layout_decl (TYPE_NAME (t), 0);
+
+ /* Now fix up any virtual base class types that we left lying
+ around. We must get these done before we try to lay out the
+ virtual function table. */
+ doing_hard_virtuals = 1;
+ pending_hard_virtuals = nreverse (pending_hard_virtuals);
+
+ if (TYPE_USES_VIRTUAL_BASECLASSES (t))
+ {
+ tree vbases;
+
+ max_has_virtual = layout_vbasetypes (t, max_has_virtual);
+ vbases = CLASSTYPE_VBASECLASSES (t);
+ CLASSTYPE_N_VBASECLASSES (t) = list_length (vbases);
+
+ while (vbases)
+ {
+ /* The rtti code should do this. (mrs) */
+ /* Update dossier info with offsets for virtual baseclasses. */
+ if (flag_dossier && ! BINFO_NEW_VTABLE_MARKED (vbases))
+ prepare_fresh_vtable (vbases, t);
+ vbases = TREE_CHAIN (vbases);
+ }
+
+ {
+ /* Now fixup overrides of all functions in vtables from all
+ direct or indirect virtual base classes. */
+ tree binfos = BINFO_BASETYPES (TYPE_BINFO (t));
+ int i, n_baseclasses = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+
+ for (i = 0; i < n_baseclasses; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+ tree basetype = BINFO_TYPE (base_binfo);
+ tree vbases;
+
+ vbases = CLASSTYPE_VBASECLASSES (basetype);
+ while (vbases)
+ {
+ merge_overrides (binfo_member (BINFO_TYPE (vbases),
+ CLASSTYPE_VBASECLASSES (t)),
+ vbases, 1, t);
+ vbases = TREE_CHAIN (vbases);
+ }
+ }
+ }
+
+ /* Now fixup any virtual function entries from virtual bases
+ that have different deltas. */
+ vbases = CLASSTYPE_VBASECLASSES (t);
+ while (vbases)
+ {
+ /* We might be able to shorten the ammount of work we do by
+ only doing this for vtables that come from virtual bases
+ that have differing offsets, but don't want to miss any
+ entries. */
+ fixup_vtable_deltas (vbases, t);
+ vbases = TREE_CHAIN (vbases);
+ }
+ }
+
+ /* Set up the DECL_FIELD_BITPOS of the vfield if we need to, as we
+ might need to know it for setting up the offsets in the vtable
+ (or in thunks) below. */
+ if (vfield != NULL_TREE
+ && DECL_FIELD_CONTEXT (vfield) != t)
+ {
+ tree binfo = get_binfo (DECL_FIELD_CONTEXT (vfield), t, 0);
+ tree offset = BINFO_OFFSET (binfo);
+
+ vfield = copy_node (vfield);
+ copy_lang_decl (vfield);
+
+ if (! integer_zerop (offset))
+ offset = size_binop (MULT_EXPR, offset, size_int (BITS_PER_UNIT));
+ DECL_FIELD_CONTEXT (vfield) = t;
+ DECL_CLASS_CONTEXT (vfield) = t;
+ DECL_FIELD_BITPOS (vfield)
+ = size_binop (PLUS_EXPR, offset, DECL_FIELD_BITPOS (vfield));
+ CLASSTYPE_VFIELD (t) = vfield;
+ }
+
+#ifdef NOTQUITE
+ cp_warning ("Doing hard virtuals for %T...", t);
+#endif
+ while (pending_hard_virtuals)
+ {
+ modify_all_vtables (t,
+ TREE_PURPOSE (pending_hard_virtuals),
+ TREE_VALUE (pending_hard_virtuals));
+ pending_hard_virtuals = TREE_CHAIN (pending_hard_virtuals);
+ }
+ doing_hard_virtuals = 0;
+
+ /* Under our model of GC, every C++ class gets its own virtual
+ function table, at least virtually. */
+ if (pending_virtuals || CLASSTYPE_DOSSIER (t))
+ {
+ pending_virtuals = nreverse (pending_virtuals);
+ /* We must enter these virtuals into the table. */
+ if (first_vfn_base_index < 0)
+ {
+ if (flag_dossier)
+ pending_virtuals = tree_cons (NULL_TREE,
+ build_vtable_entry (integer_zero_node,
+ build_t_desc (t, 0)),
+ pending_virtuals);
+ pending_virtuals = tree_cons (NULL_TREE, the_null_vtable_entry,
+ pending_virtuals);
+ build_vtable (NULL_TREE, t);
+ }
+ else
+ {
+ /* Here we know enough to change the type of our virtual
+ function table, but we will wait until later this function. */
+
+ if (! BINFO_NEW_VTABLE_MARKED (TYPE_BINFO (t)))
+ build_vtable (TREE_VEC_ELT (TYPE_BINFO_BASETYPES (t), first_vfn_base_index), t);
+
+ /* Update the dossier pointer for this class. */
+ if (flag_dossier)
+ TREE_VALUE (TREE_CHAIN (TYPE_BINFO_VIRTUALS (t)))
+ = build_vtable_entry (integer_zero_node, build_t_desc (t, 0));
+ }
+
+ /* If this type has basetypes with constructors, then those
+ constructors might clobber the virtual function table. But
+ they don't if the derived class shares the exact vtable of the base
+ class. */
+
+ CLASSTYPE_NEEDS_VIRTUAL_REINIT (t) = 1;
+ }
+ else if (first_vfn_base_index >= 0)
+ {
+ tree binfo = TREE_VEC_ELT (TYPE_BINFO_BASETYPES (t), first_vfn_base_index);
+ /* This class contributes nothing new to the virtual function
+ table. However, it may have declared functions which
+ went into the virtual function table "inherited" from the
+ base class. If so, we grab a copy of those updated functions,
+ and pretend they are ours. */
+
+ /* See if we should steal the virtual info from base class. */
+ if (TYPE_BINFO_VTABLE (t) == NULL_TREE)
+ TYPE_BINFO_VTABLE (t) = BINFO_VTABLE (binfo);
+ if (TYPE_BINFO_VIRTUALS (t) == NULL_TREE)
+ TYPE_BINFO_VIRTUALS (t) = BINFO_VIRTUALS (binfo);
+ if (TYPE_BINFO_VTABLE (t) != BINFO_VTABLE (binfo))
+ CLASSTYPE_NEEDS_VIRTUAL_REINIT (t) = 1;
+ }
+
+ if (has_virtual > max_has_virtual)
+ max_has_virtual = has_virtual;
+ if (max_has_virtual || first_vfn_base_index >= 0)
+ {
+ TYPE_VIRTUAL_P (t) = 1;
+ CLASSTYPE_VSIZE (t) = has_virtual;
+ if (first_vfn_base_index >= 0)
+ {
+ if (pending_virtuals)
+ TYPE_BINFO_VIRTUALS (t) = chainon (TYPE_BINFO_VIRTUALS (t),
+ pending_virtuals);
+ }
+ else if (has_virtual)
+ {
+ TYPE_BINFO_VIRTUALS (t) = pending_virtuals;
+ if (write_virtuals >= 0)
+ DECL_VIRTUAL_P (TYPE_BINFO_VTABLE (t)) = 1;
+ }
+ }
+
+ /* Now lay out the virtual function table. */
+ if (has_virtual)
+ {
+ tree atype, itype;
+
+ if (TREE_TYPE (vfield) == ptr_type_node)
+ {
+ /* We must create a pointer to this table because
+ the one inherited from base class does not exist.
+ We will fill in the type when we know what it
+ should really be. Use `size_int' so values are memoized
+ in common cases. */
+ itype = build_index_type (size_int (has_virtual));
+ atype = build_array_type (vtable_entry_type, itype);
+ layout_type (atype);
+ TREE_TYPE (vfield) = build_pointer_type (atype);
+ }
+ else
+ {
+ atype = TREE_TYPE (TREE_TYPE (vfield));
+
+ if (has_virtual != TREE_INT_CST_LOW (TYPE_MAX_VALUE (TYPE_DOMAIN (atype))))
+ {
+ /* We must extend (or create) the boundaries on this array,
+ because we picked up virtual functions from multiple
+ base classes. */
+ itype = build_index_type (size_int (has_virtual));
+ atype = build_array_type (vtable_entry_type, itype);
+ layout_type (atype);
+ vfield = copy_node (vfield);
+ TREE_TYPE (vfield) = build_pointer_type (atype);
+ }
+ }
+
+ CLASSTYPE_VFIELD (t) = vfield;
+ if (TREE_TYPE (TYPE_BINFO_VTABLE (t)) != atype)
+ {
+ TREE_TYPE (TYPE_BINFO_VTABLE (t)) = atype;
+ layout_decl (TYPE_BINFO_VTABLE (t), 0);
+ /* At one time the vtable info was grabbed 2 words at a time. This
+ fails on sparc unless you have 8-byte alignment. (tiemann) */
+ DECL_ALIGN (TYPE_BINFO_VTABLE (t))
+ = MAX (TYPE_ALIGN (double_type_node),
+ DECL_ALIGN (TYPE_BINFO_VTABLE (t)));
+ }
+ }
+ else if (first_vfn_base_index >= 0)
+ CLASSTYPE_VFIELD (t) = vfield;
+ CLASSTYPE_VFIELDS (t) = vfields;
+
+ finish_struct_bits (t, max_has_virtual);
+
+ /* Promote each bit-field's type to int if it is narrower than that.
+ There's more: complete the rtl for any static member objects which
+ is of the same type we're working on. */
+ for (x = fields; x; x = TREE_CHAIN (x))
+ {
+ if (DECL_BIT_FIELD (x)
+ && (C_PROMOTING_INTEGER_TYPE_P (TREE_TYPE (x))
+ || DECL_FIELD_SIZE (x) < TYPE_PRECISION (integer_type_node)))
+ {
+ tree type = TREE_TYPE (x);
+
+ /* Preserve unsignedness if traditional or if not really getting
+ any wider. */
+ if (TREE_UNSIGNED (type)
+ && (flag_traditional
+ ||
+ (TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node)
+ && DECL_FIELD_SIZE (x) == TYPE_PRECISION (integer_type_node))))
+ TREE_TYPE (x) = unsigned_type_node;
+ else
+ TREE_TYPE (x) = integer_type_node;
+ }
+
+ if (TREE_CODE (x) == VAR_DECL && TREE_STATIC (x)
+ && TREE_TYPE (x) == t)
+ {
+ DECL_MODE (x) = TYPE_MODE (t);
+ make_decl_rtl (x, NULL, 0);
+ }
+ }
+
+ /* Now add the tags, if any, to the list of TYPE_DECLs
+ defined for this type. */
+ if (CLASSTYPE_TAGS (t))
+ {
+ x = CLASSTYPE_TAGS (t);
+ last_x = tree_last (TYPE_FIELDS (t));
+ while (x)
+ {
+#if 0 /* What's wrong with using the decl the type already has? */
+ tree tag = build_decl (TYPE_DECL, TREE_PURPOSE (x), TREE_VALUE (x));
+ DECL_CONTEXT (tag) = t;
+#else
+ tree tag = TYPE_NAME (TREE_VALUE (x));
+#endif
+
+#ifdef DWARF_DEBUGGING_INFO
+ if (write_symbols == DWARF_DEBUG)
+ {
+ /* Notify dwarfout.c that this TYPE_DECL node represent a
+ gratuitous typedef. */
+ DECL_IGNORED_P (tag) = 1;
+ }
+#endif /* DWARF_DEBUGGING_INFO */
+
+ TREE_NONLOCAL_FLAG (TREE_VALUE (x)) = 0;
+ x = TREE_CHAIN (x);
+ last_x = chainon (last_x, tag);
+ }
+ if (TYPE_FIELDS (t) == NULL_TREE)
+ TYPE_FIELDS (t) = last_x;
+ CLASSTYPE_LOCAL_TYPEDECLS (t) = 1;
+ }
+
+ if (TYPE_HAS_CONSTRUCTOR (t))
+ {
+ tree vfields = CLASSTYPE_VFIELDS (t);
+
+ while (vfields)
+ {
+ /* Mark the fact that constructor for T
+ could affect anybody inheriting from T
+ who wants to initialize vtables for VFIELDS's type. */
+ if (VF_DERIVED_VALUE (vfields))
+ TREE_ADDRESSABLE (vfields) = 1;
+ vfields = TREE_CHAIN (vfields);
+ }
+ if (any_default_members != 0)
+ build_class_init_list (t);
+ }
+ else if (TYPE_NEEDS_CONSTRUCTING (t))
+ build_class_init_list (t);
+
+ if (! CLASSTYPE_DECLARED_EXCEPTION (t) && ! IS_SIGNATURE (t))
+ embrace_waiting_friends (t);
+
+ /* Write out inline function definitions. */
+ do_inline_function_hair (t, CLASSTYPE_INLINE_FRIENDS (t));
+ CLASSTYPE_INLINE_FRIENDS (t) = 0;
+
+ if (CLASSTYPE_VSIZE (t) != 0)
+ {
+ if ((flag_this_is_variable & 1) == 0)
+ {
+ tree vtbl_ptr = build_decl (VAR_DECL, get_identifier (VPTR_NAME),
+ TREE_TYPE (vfield));
+ DECL_REGISTER (vtbl_ptr) = 1;
+ CLASSTYPE_VTBL_PTR (t) = vtbl_ptr;
+ }
+#if 0
+ /* This is now done above. */
+ if (DECL_FIELD_CONTEXT (vfield) != t)
+ {
+ tree binfo = get_binfo (DECL_FIELD_CONTEXT (vfield), t, 0);
+ tree offset = BINFO_OFFSET (binfo);
+
+ vfield = copy_node (vfield);
+ copy_lang_decl (vfield);
+
+ if (! integer_zerop (offset))
+ offset = size_binop (MULT_EXPR, offset, size_int (BITS_PER_UNIT));
+ DECL_FIELD_CONTEXT (vfield) = t;
+ DECL_CLASS_CONTEXT (vfield) = t;
+ DECL_FIELD_BITPOS (vfield)
+ = size_binop (PLUS_EXPR, offset, DECL_FIELD_BITPOS (vfield));
+ CLASSTYPE_VFIELD (t) = vfield;
+ }
+#endif
+
+ /* In addition to this one, all the other vfields should be listed. */
+ /* Before that can be done, we have to have FIELD_DECLs for them, and
+ a place to find them. */
+ TYPE_NONCOPIED_PARTS (t) = build_tree_list (default_conversion (TYPE_BINFO_VTABLE (t)), vfield);
+
+ if (warn_nonvdtor && TYPE_HAS_DESTRUCTOR (t)
+ && DECL_VINDEX (TREE_VEC_ELT (method_vec, 0)) == NULL_TREE)
+ cp_warning ("`%#T' has virtual functions but non-virtual destructor",
+ t);
+ }
+
+ /* Make the rtl for any new vtables we have created, and unmark
+ the base types we marked. */
+ finish_vtbls (TYPE_BINFO (t), 1, t);
+ TYPE_BEING_DEFINED (t) = 0;
+
+ if (flag_dossier && CLASSTYPE_VTABLE_NEEDS_WRITING (t))
+ {
+ tree variants;
+ tree tdecl;
+
+ /* Now instantiate its type descriptors. */
+ tdecl = TREE_OPERAND (build_t_desc (t, 1), 0);
+ variants = TYPE_POINTER_TO (t);
+ build_type_variant (variants, 1, 0);
+ while (variants)
+ {
+ build_t_desc (variants, 1);
+ variants = TYPE_NEXT_VARIANT (variants);
+ }
+ variants = build_reference_type (t);
+ build_type_variant (variants, 1, 0);
+ while (variants)
+ {
+ build_t_desc (variants, 1);
+ variants = TYPE_NEXT_VARIANT (variants);
+ }
+ DECL_CONTEXT (tdecl) = t;
+ }
+ /* Still need to instantiate this C struct's type descriptor. */
+ else if (flag_dossier && ! CLASSTYPE_DOSSIER (t))
+ build_t_desc (t, 1);
+
+#if 0
+ if (TYPE_NAME (t) && TYPE_IDENTIFIER (t))
+ undo_template_name_overload (TYPE_IDENTIFIER (t), 1);
+#endif
+ if (current_class_type)
+ popclass (0);
+ else
+ error ("trying to finish struct, but kicked out due to previous parse errors.");
+
+ hack_incomplete_structures (t);
+
+ resume_momentary (old);
+
+ if (flag_cadillac)
+ cadillac_finish_struct (t);
+
+#if 0
+ /* This has to be done after we have sorted out what to do with
+ the enclosing type. */
+ if (write_symbols != DWARF_DEBUG)
+ {
+ /* Be smarter about nested classes here. If a type is nested,
+ only output it if we would output the enclosing type. */
+ if (DECL_CONTEXT (TYPE_NAME (t))
+ && TREE_CODE_CLASS (TREE_CODE (DECL_CONTEXT (TYPE_NAME (t)))) == 't')
+ DECL_IGNORED_P (TYPE_NAME (t)) = TREE_ASM_WRITTEN (TYPE_NAME (t));
+ }
+#endif
+
+ if (write_symbols != DWARF_DEBUG)
+ {
+ /* If the type has methods, we want to think about cutting down
+ the amount of symbol table stuff we output. The value stored in
+ the TYPE_DECL's DECL_IGNORED_P slot is a first approximation.
+ For example, if a member function is seen and we decide to
+ write out that member function, then we can change the value
+ of the DECL_IGNORED_P slot, and the type will be output when
+ that member function's debug info is written out. */
+ if (CLASSTYPE_METHOD_VEC (t))
+ {
+ extern tree pending_vtables;
+
+ /* Don't output full info about any type
+ which does not have its implementation defined here. */
+ if (TYPE_VIRTUAL_P (t) && write_virtuals == 2)
+ TYPE_DECL_SUPPRESS_DEBUG (TYPE_NAME (t))
+ = (value_member (TYPE_IDENTIFIER (t), pending_vtables) == 0);
+ else if (CLASSTYPE_INTERFACE_ONLY (t))
+ TYPE_DECL_SUPPRESS_DEBUG (TYPE_NAME (t)) = 1;
+ else if (CLASSTYPE_INTERFACE_UNKNOWN (t))
+ /* Only a first approximation! */
+ TYPE_DECL_SUPPRESS_DEBUG (TYPE_NAME (t)) = 1;
+ }
+ else if (CLASSTYPE_INTERFACE_ONLY (t))
+ TYPE_DECL_SUPPRESS_DEBUG (TYPE_NAME (t)) = 1;
+ }
+
+ /* Finish debugging output for this type. */
+ rest_of_type_compilation (t, global_bindings_p ());
+
+ return t;
+}
+
+/* Return non-zero if the effective type of INSTANCE is static.
+ Used to determine whether the virtual function table is needed
+ or not.
+
+ *NONNULL is set iff INSTANCE can be known to be nonnull, regardless
+ of our knowledge of its type. */
+int
+resolves_to_fixed_type_p (instance, nonnull)
+ tree instance;
+ int *nonnull;
+{
+ switch (TREE_CODE (instance))
+ {
+ case INDIRECT_REF:
+ /* Check that we are not going through a cast of some sort. */
+ if (TREE_TYPE (instance)
+ == TREE_TYPE (TREE_TYPE (TREE_OPERAND (instance, 0))))
+ instance = TREE_OPERAND (instance, 0);
+ /* fall through... */
+ case CALL_EXPR:
+ /* This is a call to a constructor, hence it's never zero. */
+ if (TREE_HAS_CONSTRUCTOR (instance))
+ {
+ if (nonnull)
+ *nonnull = 1;
+ return 1;
+ }
+ return 0;
+
+ case SAVE_EXPR:
+ /* This is a call to a constructor, hence it's never zero. */
+ if (TREE_HAS_CONSTRUCTOR (instance))
+ {
+ if (nonnull)
+ *nonnull = 1;
+ return 1;
+ }
+ return resolves_to_fixed_type_p (TREE_OPERAND (instance, 0), nonnull);
+
+ case RTL_EXPR:
+ /* This is a call to `new', hence it's never zero. */
+ if (TREE_CALLS_NEW (instance))
+ {
+ if (nonnull)
+ *nonnull = 1;
+ return 1;
+ }
+ return 0;
+
+ case PLUS_EXPR:
+ case MINUS_EXPR:
+ if (TREE_CODE (TREE_OPERAND (instance, 1)) == INTEGER_CST)
+ /* Propagate nonnull. */
+ resolves_to_fixed_type_p (TREE_OPERAND (instance, 0), nonnull);
+ if (TREE_CODE (TREE_OPERAND (instance, 0)) == ADDR_EXPR)
+ return resolves_to_fixed_type_p (TREE_OPERAND (instance, 0), nonnull);
+ return 0;
+
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ return resolves_to_fixed_type_p (TREE_OPERAND (instance, 0), nonnull);
+
+ case ADDR_EXPR:
+ if (nonnull)
+ *nonnull = 1;
+ return resolves_to_fixed_type_p (TREE_OPERAND (instance, 0), nonnull);
+
+ case COMPONENT_REF:
+ return resolves_to_fixed_type_p (TREE_OPERAND (instance, 1), nonnull);
+
+ case WITH_CLEANUP_EXPR:
+ if (TREE_CODE (TREE_OPERAND (instance, 0)) == ADDR_EXPR)
+ return resolves_to_fixed_type_p (TREE_OPERAND (instance, 0), nonnull);
+ /* fall through... */
+ case VAR_DECL:
+ case FIELD_DECL:
+ if (TREE_CODE (TREE_TYPE (instance)) == ARRAY_TYPE
+ && IS_AGGR_TYPE (TREE_TYPE (TREE_TYPE (instance))))
+ {
+ if (nonnull)
+ *nonnull = 1;
+ return 1;
+ }
+ /* fall through... */
+ case TARGET_EXPR:
+ case PARM_DECL:
+ if (IS_AGGR_TYPE (TREE_TYPE (instance)))
+ {
+ if (nonnull)
+ *nonnull = 1;
+ return 1;
+ }
+ else if (nonnull)
+ {
+ if (instance == current_class_decl
+ && flag_this_is_variable <= 0)
+ {
+ /* Some people still use `this = 0' inside destructors. */
+ *nonnull = ! DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (current_function_decl));
+ /* In a constructor, we know our type. */
+ if (flag_this_is_variable < 0)
+ return 1;
+ }
+ else if (TREE_CODE (TREE_TYPE (instance)) == REFERENCE_TYPE)
+ /* Reference variables should be references to objects. */
+ *nonnull = 1;
+ }
+ return 0;
+
+ default:
+ return 0;
+ }
+}
+
+void
+init_class_processing ()
+{
+ current_class_depth = 0;
+ current_class_stacksize = 10;
+ current_class_base = (tree *)xmalloc(current_class_stacksize * sizeof (tree));
+ current_class_stack = current_class_base;
+
+ current_lang_stacksize = 10;
+ current_lang_base = (tree *)xmalloc(current_lang_stacksize * sizeof (tree));
+ current_lang_stack = current_lang_base;
+
+ /* Keep these values lying around. */
+ the_null_vtable_entry = build_vtable_entry (integer_zero_node, integer_zero_node);
+ base_layout_decl = build_lang_field_decl (FIELD_DECL, NULL_TREE, error_mark_node);
+ TREE_TYPE (base_layout_decl) = make_node (RECORD_TYPE);
+
+ gcc_obstack_init (&class_obstack);
+}
+
+/* Set current scope to NAME. CODE tells us if this is a
+ STRUCT, UNION, or ENUM environment.
+
+ NAME may end up being NULL_TREE if this is an anonymous or
+ late-bound struct (as in "struct { ... } foo;") */
+
+/* Set global variables CURRENT_CLASS_NAME and CURRENT_CLASS_TYPE to
+ appropriate values, found by looking up the type definition of
+ NAME (as a CODE).
+
+ If MODIFY is 1, we set IDENTIFIER_CLASS_VALUE's of names
+ which can be seen locally to the class. They are shadowed by
+ any subsequent local declaration (including parameter names).
+
+ If MODIFY is 2, we set IDENTIFIER_CLASS_VALUE's of names
+ which have static meaning (i.e., static members, static
+ member functions, enum declarations, etc).
+
+ If MODIFY is 3, we set IDENTIFIER_CLASS_VALUE of names
+ which can be seen locally to the class (as in 1), but
+ know that we are doing this for declaration purposes
+ (i.e. friend foo::bar (int)).
+
+ So that we may avoid calls to lookup_name, we cache the _TYPE
+ nodes of local TYPE_DECLs in the TREE_TYPE field of the name.
+
+ For multiple inheritance, we perform a two-pass depth-first search
+ of the type lattice. The first pass performs a pre-order search,
+ marking types after the type has had its fields installed in
+ the appropriate IDENTIFIER_CLASS_VALUE slot. The second pass merely
+ unmarks the marked types. If a field or member function name
+ appears in an ambiguous way, the IDENTIFIER_CLASS_VALUE of
+ that name becomes `error_mark_node'. */
+
+void
+pushclass (type, modify)
+ tree type;
+ int modify;
+{
+ push_memoized_context (type, modify);
+
+ current_class_depth++;
+ *current_class_stack++ = current_class_name;
+ *current_class_stack++ = current_class_type;
+ if (current_class_stack >= current_class_base + current_class_stacksize)
+ {
+ current_class_base =
+ (tree *)xrealloc (current_class_base,
+ sizeof (tree) * (current_class_stacksize + 10));
+ current_class_stack = current_class_base + current_class_stacksize;
+ current_class_stacksize += 10;
+ }
+
+ current_class_name = TYPE_NAME (type);
+ if (TREE_CODE (current_class_name) == TYPE_DECL)
+ current_class_name = DECL_NAME (current_class_name);
+ current_class_type = type;
+
+ if (previous_class_type != NULL_TREE
+ && (type != previous_class_type || TYPE_SIZE (previous_class_type) == NULL_TREE)
+ && current_class_depth == 1)
+ {
+ /* Forcibly remove any old class remnants. */
+ popclass (-1);
+ previous_class_type = NULL_TREE;
+ }
+
+ pushlevel_class ();
+
+ if (modify)
+ {
+ tree tags;
+ tree this_fndecl = current_function_decl;
+
+ if (current_function_decl
+ && DECL_CONTEXT (current_function_decl)
+ && TREE_CODE (DECL_CONTEXT (current_function_decl)) == FUNCTION_DECL)
+ current_function_decl = DECL_CONTEXT (current_function_decl);
+ else
+ current_function_decl = NULL_TREE;
+
+ if (TREE_CODE (type) == UNINSTANTIATED_P_TYPE)
+ declare_uninstantiated_type_level ();
+ else if (type != previous_class_type || current_class_depth > 1)
+ {
+ build_mi_matrix (type);
+ push_class_decls (type);
+ free_mi_matrix ();
+ if (current_class_depth == 1)
+ previous_class_type = type;
+ }
+ else
+ {
+ tree item;
+
+ /* Hooray, our cacheing was successful, let's just install the
+ cached class_shadowed list, and walk through it to get the
+ IDENTIFIER_TYPE_VALUEs correct. */
+ set_class_shadows (previous_class_values);
+ for (item = previous_class_values; item; item = TREE_CHAIN (item))
+ {
+ tree id = TREE_PURPOSE (item);
+ tree decl = IDENTIFIER_CLASS_VALUE (id);
+
+ if (TREE_CODE (decl) == TYPE_DECL)
+ set_identifier_type_value (id, TREE_TYPE (decl));
+ }
+ unuse_fields (type);
+ }
+
+ if (IDENTIFIER_TEMPLATE (TYPE_IDENTIFIER (type)))
+ overload_template_name (current_class_name, 0);
+
+ for (tags = CLASSTYPE_TAGS (type); tags; tags = TREE_CHAIN (tags))
+ {
+ TREE_NONLOCAL_FLAG (TREE_VALUE (tags)) = 1;
+ if (! TREE_PURPOSE (tags))
+ continue;
+ pushtag (TREE_PURPOSE (tags), TREE_VALUE (tags), 0);
+ }
+
+ current_function_decl = this_fndecl;
+ }
+
+ if (flag_cadillac)
+ cadillac_push_class (type);
+}
+
+/* Get out of the current class scope. If we were in a class scope
+ previously, that is the one popped to. The flag MODIFY tells whether
+ the current scope declarations needs to be modified as a result of
+ popping to the previous scope. 0 is used for class definitions. */
+void
+popclass (modify)
+ int modify;
+{
+ if (flag_cadillac)
+ cadillac_pop_class ();
+
+ if (modify < 0)
+ {
+ /* Back this old class out completely. */
+ tree tags = CLASSTYPE_TAGS (previous_class_type);
+ tree t;
+
+ /* This code can be seen as a cache miss. When we've cached a
+ class' scope's bindings and we can't use them, we need to reset
+ them. This is it! */
+ for (t = previous_class_values; t; t = TREE_CHAIN (t))
+ IDENTIFIER_CLASS_VALUE (TREE_PURPOSE (t)) = NULL_TREE;
+ while (tags)
+ {
+ TREE_NONLOCAL_FLAG (TREE_VALUE (tags)) = 0;
+ tags = TREE_CHAIN (tags);
+ }
+ goto ret;
+ }
+
+ if (modify)
+ {
+ /* Just remove from this class what didn't make
+ it into IDENTIFIER_CLASS_VALUE. */
+ tree tags = CLASSTYPE_TAGS (current_class_type);
+
+ while (tags)
+ {
+ TREE_NONLOCAL_FLAG (TREE_VALUE (tags)) = 0;
+ tags = TREE_CHAIN (tags);
+ }
+ if (IDENTIFIER_TEMPLATE (TYPE_IDENTIFIER (current_class_type)))
+ undo_template_name_overload (current_class_name, 0);
+ }
+
+ /* Force clearing of IDENTIFIER_CLASS_VALUEs after a class definition,
+ since not all class decls make it there currently. */
+ poplevel_class (! modify);
+
+ /* Since poplevel_class does the popping of class decls nowadays,
+ this really only frees the obstack used for these decls.
+ That's why it had to be moved down here. */
+ if (modify)
+ pop_class_decls (current_class_type);
+
+ current_class_depth--;
+ current_class_type = *--current_class_stack;
+ current_class_name = *--current_class_stack;
+
+ if (current_class_type)
+ {
+ if (CLASSTYPE_VTBL_PTR (current_class_type))
+ {
+ current_vtable_decl
+ = lookup_name (DECL_NAME (CLASSTYPE_VTBL_PTR (current_class_type)),
+ 0);
+ if (current_vtable_decl)
+ current_vtable_decl = build_indirect_ref (current_vtable_decl,
+ NULL_PTR);
+ }
+ current_class_decl = lookup_name (this_identifier, 0);
+ if (current_class_decl)
+ {
+ if (TREE_CODE (TREE_TYPE (current_class_decl)) == POINTER_TYPE)
+ {
+ tree temp;
+ /* Can't call build_indirect_ref here, because it has special
+ logic to return C_C_D given this argument. */
+ C_C_D = build1 (INDIRECT_REF, current_class_type, current_class_decl);
+ temp = TREE_TYPE (TREE_TYPE (current_class_decl));
+ TREE_READONLY (C_C_D) = TYPE_READONLY (temp);
+ TREE_SIDE_EFFECTS (C_C_D) = TYPE_VOLATILE (temp);
+ TREE_THIS_VOLATILE (C_C_D) = TYPE_VOLATILE (temp);
+ }
+ else
+ C_C_D = current_class_decl;
+ }
+ else
+ C_C_D = NULL_TREE;
+ }
+ else
+ {
+ current_class_decl = NULL_TREE;
+ current_vtable_decl = NULL_TREE;
+ C_C_D = NULL_TREE;
+ }
+
+ pop_memoized_context (modify);
+
+ ret:
+ ;
+}
+
+/* When entering a class scope, all enclosing class scopes' names with
+ static meaning (static variables, static functions, types and enumerators)
+ have to be visible. This recursive function calls pushclass for all
+ enclosing class contexts until global or a local scope is reached.
+ TYPE is the enclosed class and MODIFY is equivalent with the pushclass
+ formal of the same name. */
+
+void
+push_nested_class (type, modify)
+ tree type;
+ int modify;
+{
+ tree context;
+
+ if (type == error_mark_node || ! IS_AGGR_TYPE (type))
+ return;
+
+ context = DECL_CONTEXT (TYPE_NAME (type));
+
+ if (context && TREE_CODE (context) == RECORD_TYPE)
+ push_nested_class (context, 2);
+ pushclass (type, modify);
+}
+
+/* Undoes a push_nested_class call. MODIFY is passed on to popclass. */
+
+void
+pop_nested_class (modify)
+ int modify;
+{
+ tree context = DECL_CONTEXT (TYPE_NAME (current_class_type));
+
+ popclass (modify);
+ if (context && TREE_CODE (context) == RECORD_TYPE)
+ pop_nested_class (modify);
+}
+
+/* Set global variables CURRENT_LANG_NAME to appropriate value
+ so that behavior of name-mangling machinery is correct. */
+
+void
+push_lang_context (name)
+ tree name;
+{
+ *current_lang_stack++ = current_lang_name;
+ if (current_lang_stack >= current_lang_base + current_lang_stacksize)
+ {
+ current_lang_base =
+ (tree *)xrealloc (current_lang_base,
+ sizeof (tree) * (current_lang_stacksize + 10));
+ current_lang_stack = current_lang_base + current_lang_stacksize;
+ current_lang_stacksize += 10;
+ }
+
+ if (name == lang_name_cplusplus)
+ {
+ strict_prototype = strict_prototypes_lang_cplusplus;
+ current_lang_name = name;
+ }
+ else if (name == lang_name_c)
+ {
+ strict_prototype = strict_prototypes_lang_c;
+ current_lang_name = name;
+ }
+ else
+ error ("language string `\"%s\"' not recognized", IDENTIFIER_POINTER (name));
+
+ if (flag_cadillac)
+ cadillac_push_lang (name);
+}
+
+/* Get out of the current language scope. */
+void
+pop_lang_context ()
+{
+ if (flag_cadillac)
+ cadillac_pop_lang ();
+
+ current_lang_name = *--current_lang_stack;
+ if (current_lang_name == lang_name_cplusplus)
+ strict_prototype = strict_prototypes_lang_cplusplus;
+ else if (current_lang_name == lang_name_c)
+ strict_prototype = strict_prototypes_lang_c;
+}
+
+int
+root_lang_context_p ()
+{
+ return current_lang_stack == current_lang_base;
+}
+
+/* Type instantiation routines. */
+
+/* This function will instantiate the type of the expression given
+ in RHS to match the type of LHSTYPE. If LHSTYPE is NULL_TREE,
+ or other errors exist, the TREE_TYPE of RHS will be ERROR_MARK_NODE.
+
+ This function is used in build_modify_expr, convert_arguments,
+ build_c_cast, and compute_conversion_costs. */
+tree
+instantiate_type (lhstype, rhs, complain)
+ tree lhstype, rhs;
+ int complain;
+{
+ if (TREE_CODE (lhstype) == UNKNOWN_TYPE)
+ {
+ if (complain)
+ error ("not enough type information");
+ return error_mark_node;
+ }
+
+ if (TREE_TYPE (rhs) != NULL_TREE && ! (type_unknown_p (rhs)))
+ return rhs;
+
+ /* This should really only be used when attempting to distinguish
+ what sort of a pointer to function we have. For now, any
+ arithmetic operation which is not supported on pointers
+ is rejected as an error. */
+
+ switch (TREE_CODE (rhs))
+ {
+ case TYPE_EXPR:
+ case CONVERT_EXPR:
+ case SAVE_EXPR:
+ case CONSTRUCTOR:
+ case BUFFER_REF:
+ my_friendly_abort (177);
+ return error_mark_node;
+
+ case INDIRECT_REF:
+ case ARRAY_REF:
+ TREE_TYPE (rhs) = lhstype;
+ lhstype = build_pointer_type (lhstype);
+ TREE_OPERAND (rhs, 0)
+ = instantiate_type (lhstype, TREE_OPERAND (rhs, 0), complain);
+ if (TREE_OPERAND (rhs, 0) == error_mark_node)
+ return error_mark_node;
+
+ return rhs;
+
+ case NOP_EXPR:
+ rhs = copy_node (TREE_OPERAND (rhs, 0));
+ TREE_TYPE (rhs) = unknown_type_node;
+ return instantiate_type (lhstype, rhs, complain);
+
+ case COMPONENT_REF:
+ {
+ tree field = TREE_OPERAND (rhs, 1);
+ if (TREE_CODE (field) == TREE_LIST)
+ {
+ tree function = instantiate_type (lhstype, field, complain);
+ if (function == error_mark_node)
+ return error_mark_node;
+ my_friendly_assert (TREE_CODE (function) == FUNCTION_DECL, 185);
+ if (DECL_VINDEX (function))
+ {
+ tree base = TREE_OPERAND (rhs, 0);
+ tree base_ptr = build_unary_op (ADDR_EXPR, base, 0);
+ if (base_ptr == error_mark_node)
+ return error_mark_node;
+ base_ptr = convert_pointer_to (DECL_CONTEXT (function), base_ptr);
+ if (base_ptr == error_mark_node)
+ return error_mark_node;
+ return build_vfn_ref (&base_ptr, base, DECL_VINDEX (function));
+ }
+ return function;
+ }
+
+ my_friendly_assert (TREE_CODE (field) == FIELD_DECL, 178);
+ my_friendly_assert (!(TREE_CODE (TREE_TYPE (field)) == FUNCTION_TYPE
+ || TREE_CODE (TREE_TYPE (field)) == METHOD_TYPE),
+ 179);
+
+ TREE_TYPE (rhs) = lhstype;
+ /* First look for an exact match */
+
+ while (field && TREE_TYPE (field) != lhstype)
+ field = TREE_CHAIN (field);
+ if (field)
+ {
+ TREE_OPERAND (rhs, 1) = field;
+ return rhs;
+ }
+
+ /* No exact match found, look for a compatible function. */
+ field = TREE_OPERAND (rhs, 1);
+ while (field && ! comptypes (lhstype, TREE_TYPE (field), 0))
+ field = TREE_CHAIN (field);
+ if (field)
+ {
+ TREE_OPERAND (rhs, 1) = field;
+ field = TREE_CHAIN (field);
+ while (field && ! comptypes (lhstype, TREE_TYPE (field), 0))
+ field = TREE_CHAIN (field);
+ if (field)
+ {
+ if (complain)
+ error ("ambiguous overload for COMPONENT_REF requested");
+ return error_mark_node;
+ }
+ }
+ else
+ {
+ if (complain)
+ error ("no appropriate overload exists for COMPONENT_REF");
+ return error_mark_node;
+ }
+ return rhs;
+ }
+
+ case TREE_LIST:
+ {
+ tree elem, baselink, name;
+ int globals = overloaded_globals_p (rhs);
+
+#if 0 /* obsolete */
+ /* If there's only one function we know about, return that. */
+ if (globals > 0 && TREE_CHAIN (rhs) == NULL_TREE)
+ return TREE_VALUE (rhs);
+#endif
+
+ /* First look for an exact match. Search either overloaded
+ functions or member functions. May have to undo what
+ `default_conversion' might do to lhstype. */
+
+ if (TREE_CODE (lhstype) == POINTER_TYPE)
+ if (TREE_CODE (TREE_TYPE (lhstype)) == FUNCTION_TYPE
+ || TREE_CODE (TREE_TYPE (lhstype)) == METHOD_TYPE)
+ lhstype = TREE_TYPE (lhstype);
+ else
+ {
+ if (complain)
+ error ("invalid type combination for overload");
+ return error_mark_node;
+ }
+
+ if (TREE_CODE (lhstype) != FUNCTION_TYPE && globals > 0)
+ {
+ if (complain)
+ cp_error ("cannot resolve overloaded function `%D' based on non-function type",
+ TREE_PURPOSE (rhs));
+ return error_mark_node;
+ }
+
+ if (globals > 0)
+ {
+ elem = get_first_fn (rhs);
+ while (elem)
+ if (TREE_TYPE (elem) != lhstype)
+ elem = DECL_CHAIN (elem);
+ else
+ return elem;
+ /* No exact match found, look for a compatible function. */
+ elem = get_first_fn (rhs);
+ while (elem && ! comp_target_types (lhstype, TREE_TYPE (elem), 1))
+ elem = DECL_CHAIN (elem);
+ if (elem)
+ {
+ tree save_elem = elem;
+ elem = DECL_CHAIN (elem);
+ while (elem && ! comp_target_types (lhstype, TREE_TYPE (elem),
+ 0))
+ elem = DECL_CHAIN (elem);
+ if (elem)
+ {
+ if (complain)
+ {
+ cp_error ("cannot resolve overload to target type `%#T'",
+ lhstype);
+ cp_error_at (" ambiguity between `%#D'", save_elem);
+ cp_error_at (" and `%#D', at least", elem);
+ }
+ return error_mark_node;
+ }
+ if (TREE_CODE (save_elem) == TEMPLATE_DECL)
+ {
+ int ntparms = TREE_VEC_LENGTH
+ (DECL_TEMPLATE_PARMS (save_elem));
+ tree *targs = (tree *) alloca (sizeof (tree) * ntparms);
+ int i, dummy;
+ i = type_unification
+ (DECL_TEMPLATE_PARMS (save_elem), targs,
+ TYPE_ARG_TYPES (TREE_TYPE (save_elem)),
+ TYPE_ARG_TYPES (lhstype), &dummy, 0);
+ save_elem = instantiate_template (save_elem, targs);
+ }
+ return save_elem;
+ }
+ if (complain)
+ {
+ cp_error ("cannot resolve overload to target type `%#T'",
+ lhstype);
+ cp_error (" because no suitable overload of function `%D' exists",
+ TREE_PURPOSE (rhs));
+ }
+ return error_mark_node;
+ }
+
+ if (TREE_NONLOCAL_FLAG (rhs))
+ {
+ /* Got to get it as a baselink. */
+ rhs = lookup_fnfields (TYPE_BINFO (current_class_type),
+ TREE_PURPOSE (rhs), 0);
+ }
+ else
+ {
+ my_friendly_assert (TREE_CHAIN (rhs) == NULL_TREE, 181);
+ if (TREE_CODE (TREE_VALUE (rhs)) == TREE_LIST)
+ rhs = TREE_VALUE (rhs);
+ my_friendly_assert (TREE_CODE (TREE_VALUE (rhs)) == FUNCTION_DECL,
+ 182);
+ }
+
+ for (baselink = rhs; baselink;
+ baselink = next_baselink (baselink))
+ {
+ elem = TREE_VALUE (baselink);
+ while (elem)
+ if (comptypes (lhstype, TREE_TYPE (elem), 1))
+ return elem;
+ else
+ elem = TREE_CHAIN (elem);
+ }
+
+ /* No exact match found, look for a compatible method. */
+ for (baselink = rhs; baselink;
+ baselink = next_baselink (baselink))
+ {
+ elem = TREE_VALUE (baselink);
+ while (elem && ! comp_target_types (lhstype, TREE_TYPE (elem), 1))
+ elem = TREE_CHAIN (elem);
+ if (elem)
+ {
+ tree save_elem = elem;
+ elem = TREE_CHAIN (elem);
+ while (elem && ! comp_target_types (lhstype, TREE_TYPE (elem), 0))
+ elem = TREE_CHAIN (elem);
+ if (elem)
+ {
+ if (complain)
+ error ("ambiguous overload for overloaded method requested");
+ return error_mark_node;
+ }
+ return save_elem;
+ }
+ name = DECL_NAME (TREE_VALUE (rhs));
+#if 0
+ if (TREE_CODE (lhstype) == FUNCTION_TYPE && globals < 0)
+ {
+ /* Try to instantiate from non-member functions. */
+ rhs = lookup_name_nonclass (name);
+ if (rhs && TREE_CODE (rhs) == TREE_LIST)
+ {
+ /* This code seems to be missing a `return'. */
+ my_friendly_abort (4);
+ instantiate_type (lhstype, rhs, complain);
+ }
+ }
+#endif
+ }
+ if (complain)
+ error ("no static member functions named `%s'",
+ IDENTIFIER_POINTER (name));
+ return error_mark_node;
+ }
+
+ case CALL_EXPR:
+ /* This is too hard for now. */
+ my_friendly_abort (183);
+ return error_mark_node;
+
+ case PLUS_EXPR:
+ case MINUS_EXPR:
+ case COMPOUND_EXPR:
+ TREE_OPERAND (rhs, 0)
+ = instantiate_type (lhstype, TREE_OPERAND (rhs, 0), complain);
+ if (TREE_OPERAND (rhs, 0) == error_mark_node)
+ return error_mark_node;
+ TREE_OPERAND (rhs, 1)
+ = instantiate_type (lhstype, TREE_OPERAND (rhs, 1), complain);
+ if (TREE_OPERAND (rhs, 1) == error_mark_node)
+ return error_mark_node;
+
+ TREE_TYPE (rhs) = lhstype;
+ return rhs;
+
+ case MULT_EXPR:
+ case TRUNC_DIV_EXPR:
+ case FLOOR_DIV_EXPR:
+ case CEIL_DIV_EXPR:
+ case ROUND_DIV_EXPR:
+ case RDIV_EXPR:
+ case TRUNC_MOD_EXPR:
+ case FLOOR_MOD_EXPR:
+ case CEIL_MOD_EXPR:
+ case ROUND_MOD_EXPR:
+ case FIX_ROUND_EXPR:
+ case FIX_FLOOR_EXPR:
+ case FIX_CEIL_EXPR:
+ case FIX_TRUNC_EXPR:
+ case FLOAT_EXPR:
+ case NEGATE_EXPR:
+ case ABS_EXPR:
+ case MAX_EXPR:
+ case MIN_EXPR:
+ case FFS_EXPR:
+
+ case BIT_AND_EXPR:
+ case BIT_IOR_EXPR:
+ case BIT_XOR_EXPR:
+ case LSHIFT_EXPR:
+ case RSHIFT_EXPR:
+ case LROTATE_EXPR:
+ case RROTATE_EXPR:
+
+ case PREINCREMENT_EXPR:
+ case PREDECREMENT_EXPR:
+ case POSTINCREMENT_EXPR:
+ case POSTDECREMENT_EXPR:
+ if (complain)
+ error ("illegal operation on uninstantiated type");
+ return error_mark_node;
+
+ case TRUTH_AND_EXPR:
+ case TRUTH_OR_EXPR:
+ case TRUTH_XOR_EXPR:
+ case LT_EXPR:
+ case LE_EXPR:
+ case GT_EXPR:
+ case GE_EXPR:
+ case EQ_EXPR:
+ case NE_EXPR:
+ case TRUTH_ANDIF_EXPR:
+ case TRUTH_ORIF_EXPR:
+ case TRUTH_NOT_EXPR:
+ if (complain)
+ error ("not enough type information");
+ return error_mark_node;
+
+ case COND_EXPR:
+ if (type_unknown_p (TREE_OPERAND (rhs, 0)))
+ {
+ if (complain)
+ error ("not enough type information");
+ return error_mark_node;
+ }
+ TREE_OPERAND (rhs, 1)
+ = instantiate_type (lhstype, TREE_OPERAND (rhs, 1), complain);
+ if (TREE_OPERAND (rhs, 1) == error_mark_node)
+ return error_mark_node;
+ TREE_OPERAND (rhs, 2)
+ = instantiate_type (lhstype, TREE_OPERAND (rhs, 2), complain);
+ if (TREE_OPERAND (rhs, 2) == error_mark_node)
+ return error_mark_node;
+
+ TREE_TYPE (rhs) = lhstype;
+ return rhs;
+
+ case MODIFY_EXPR:
+ TREE_OPERAND (rhs, 1)
+ = instantiate_type (lhstype, TREE_OPERAND (rhs, 1), complain);
+ if (TREE_OPERAND (rhs, 1) == error_mark_node)
+ return error_mark_node;
+
+ TREE_TYPE (rhs) = lhstype;
+ return rhs;
+
+ case ADDR_EXPR:
+ if (TYPE_PTRMEMFUNC_P (lhstype))
+ lhstype = TYPE_PTRMEMFUNC_FN_TYPE (lhstype);
+ else if (TREE_CODE (lhstype) != POINTER_TYPE)
+ {
+ if (complain)
+ error ("type for resolving address of overloaded function must be pointer type");
+ return error_mark_node;
+ }
+ TREE_TYPE (rhs) = lhstype;
+ lhstype = TREE_TYPE (lhstype);
+ TREE_OPERAND (rhs, 0)
+ = instantiate_type (lhstype, TREE_OPERAND (rhs, 0), complain);
+ if (TREE_OPERAND (rhs, 0) == error_mark_node)
+ return error_mark_node;
+
+ mark_addressable (TREE_OPERAND (rhs, 0));
+ return rhs;
+
+ case ENTRY_VALUE_EXPR:
+ my_friendly_abort (184);
+ return error_mark_node;
+
+ case ERROR_MARK:
+ return error_mark_node;
+
+ default:
+ my_friendly_abort (185);
+ return error_mark_node;
+ }
+}
+
+/* Return the name of the virtual function pointer field
+ (as an IDENTIFIER_NODE) for the given TYPE. Note that
+ this may have to look back through base types to find the
+ ultimate field name. (For single inheritance, these could
+ all be the same name. Who knows for multiple inheritance). */
+static tree
+get_vfield_name (type)
+ tree type;
+{
+ tree binfo = TYPE_BINFO (type);
+ char *buf;
+
+ while (BINFO_BASETYPES (binfo)
+ && TYPE_VIRTUAL_P (BINFO_TYPE (BINFO_BASETYPE (binfo, 0)))
+ && ! TREE_VIA_VIRTUAL (BINFO_BASETYPE (binfo, 0)))
+ binfo = BINFO_BASETYPE (binfo, 0);
+
+ type = BINFO_TYPE (binfo);
+ buf = (char *)alloca (sizeof (VFIELD_NAME_FORMAT)
+ + TYPE_NAME_LENGTH (type) + 2);
+ sprintf (buf, VFIELD_NAME_FORMAT, TYPE_NAME_STRING (type));
+ return get_identifier (buf);
+}
+
+void
+print_class_statistics ()
+{
+#ifdef GATHER_STATISTICS
+ fprintf (stderr, "convert_harshness = %d\n", n_convert_harshness);
+ fprintf (stderr, "compute_conversion_costs = %d\n", n_compute_conversion_costs);
+ fprintf (stderr, "build_method_call = %d (inner = %d)\n",
+ n_build_method_call, n_inner_fields_searched);
+ if (n_vtables)
+ {
+ fprintf (stderr, "vtables = %d; vtable searches = %d\n",
+ n_vtables, n_vtable_searches);
+ fprintf (stderr, "vtable entries = %d; vtable elems = %d\n",
+ n_vtable_entries, n_vtable_elems);
+ }
+#endif
+}
+
+/* Push an obstack which is sufficiently long-lived to hold such class
+ decls that may be cached in the previous_class_values list. For now, let's
+ use the permanent obstack, later we may create a dedicated obstack just
+ for this purpose. The effect is undone by pop_obstacks. */
+void
+maybe_push_cache_obstack ()
+{
+ push_obstacks_nochange ();
+ if (current_class_depth == 1)
+ current_obstack = &permanent_obstack;
+}
diff --git a/gnu/usr.bin/cc/cc1plus/class.h b/gnu/usr.bin/cc/cc1plus/class.h
new file mode 100644
index 0000000..6f31e15
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/class.h
@@ -0,0 +1,116 @@
+/* Variables and structures for overloading rules.
+ Copyright (C) 1993 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* The following structure is used when comparing various alternatives
+ for overloading. The unsigned quantity `strikes.i' is used
+ for fast comparison of two possibilities. This number is an
+ aggregate of four constituents:
+
+ EVIL: if this is non-zero, then the candidate should not be considered
+ ELLIPSIS: if this is non-zero, then some actual argument has been matched
+ against an ellipsis
+ USER: if this is non-zero, then a user-defined type conversion is needed
+ B_OR_D: if this is non-zero, then use a base pointer instead of the
+ type of the pointer we started with.
+ EASY: if this is non-zero, then we have a builtin conversion
+ (such as int to long, int to float, etc) to do.
+
+ If two candidates require user-defined type conversions, and the
+ type conversions are not identical, then an ambiguity error
+ is reported.
+
+ If two candidates agree on user-defined type conversions,
+ and one uses pointers of strictly higher type (derived where
+ another uses base), then that alternative is silently chosen.
+
+ Note that this technique really only works for 255 arguments. Perhaps
+ this is not enough. */
+
+/* These macros and harshness_code are used by the NEW METHOD. */
+#define EVIL_CODE (1<<7)
+#define CONST_CODE (1<<6)
+#define ELLIPSIS_CODE (1<<5)
+#define USER_CODE (1<<4)
+#define STD_CODE (1<<3)
+#define PROMO_CODE (1<<2)
+#define QUAL_CODE (1<<1)
+#define TRIVIAL_CODE (1<<0)
+
+struct harshness_code
+{
+ /* What kind of conversion is involved. */
+ unsigned short code;
+
+ /* The inheritance distance. */
+ short distance;
+
+ /* For a PROMO_CODE, Any special penalties involved in integral conversions.
+ This exists because $4.1 of the ARM states that something like
+ `short unsigned int' should promote to `int', not `unsigned int'.
+ If, for example, it tries to match two fns, f(int) and f(unsigned),
+ f(int) should be a better match than f(unsigned) by this rule. Without
+ this extra metric, they both only appear as "integral promotions", which
+ will lead to an ambiguity.
+ For a TRIVIAL_CODE, This is also used by build_overload_call_real and
+ convert_harshness to keep track of other information we need. */
+ unsigned short int_penalty;
+};
+
+struct candidate
+{
+ struct harshness_code h; /* Used for single-argument conversions. */
+
+ int h_len; /* The length of the harshness vector. */
+
+ tree function; /* A FUNCTION_DECL */
+ tree basetypes; /* The path to function. */
+ tree arg; /* first parm to function. */
+
+ /* Indexed by argument number, encodes evil, user, d_to_b, and easy
+ strikes for that argument. At end of array, we store the index+1
+ of where we started using default parameters, or 0 if there are
+ none. */
+ struct harshness_code *harshness;
+
+ union
+ {
+ tree field; /* If no evil strikes, the FUNCTION_DECL of
+ the function (if a member function). */
+ int bad_arg; /* the index of the first bad argument:
+ 0 if no bad arguments
+ > 0 is first bad argument
+ -1 if extra actual arguments
+ -2 if too few actual arguments.
+ -3 if const/non const method mismatch.
+ -4 if type unification failed.
+ -5 if contravariance violation. */
+ } u;
+};
+int rank_for_overload ();
+
+/* Variables shared between class.c and call.c. */
+
+extern int n_vtables;
+extern int n_vtable_entries;
+extern int n_vtable_searches;
+extern int n_vtable_elems;
+extern int n_convert_harshness;
+extern int n_compute_conversion_costs;
+extern int n_build_method_call;
+extern int n_inner_fields_searched;
diff --git a/gnu/usr.bin/cc/cc1plus/cp-tree.h b/gnu/usr.bin/cc/cc1plus/cp-tree.h
new file mode 100644
index 0000000..80fd832
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/cp-tree.h
@@ -0,0 +1,2373 @@
+/* Definitions for C++ parsing and type checking.
+ Copyright (C) 1987, 1993 Free Software Foundation, Inc.
+ Hacked by Michael Tiemann (tiemann@cygnus.com)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _CP_TREE_H
+#define _CP_TREE_H
+
+/* Borrow everything that is C from c-tree.h,
+ but do so by copy, not by inclusion, since c-tree.h defines
+ lang_identifier. */
+
+#ifndef STDIO_PROTO
+#ifdef BUFSIZ
+#define STDIO_PROTO(ARGS) PROTO(ARGS)
+#else
+#define STDIO_PROTO(ARGS) ()
+#endif
+#endif
+
+/* Language-dependent contents of an identifier. */
+
+struct lang_identifier
+{
+ struct tree_identifier ignore;
+ tree global_value, local_value;
+ tree class_value;
+ tree class_template_info;
+ struct lang_id2 *x;
+};
+
+struct lang_id2
+{
+ tree label_value, implicit_decl;
+ tree type_desc, as_list, error_locus;
+};
+
+/* To identify to the debug emitters if it should pay attention to the
+ flag `-Wtemplate-debugging'. */
+#define HAVE_TEMPLATES 1
+
+/* Macros for access to language-specific slots in an identifier. */
+
+#define IDENTIFIER_GLOBAL_VALUE(NODE) \
+ (((struct lang_identifier *)(NODE))->global_value)
+#define IDENTIFIER_CLASS_VALUE(NODE) \
+ (((struct lang_identifier *)(NODE))->class_value)
+#define IDENTIFIER_LOCAL_VALUE(NODE) \
+ (((struct lang_identifier *)(NODE))->local_value)
+#define IDENTIFIER_TEMPLATE(NODE) \
+ (((struct lang_identifier *)(NODE))->class_template_info)
+
+#define IDENTIFIER_TYPE_VALUE(NODE) (TREE_TYPE (NODE))
+#define SET_IDENTIFIER_TYPE_VALUE(NODE,TYPE) (TREE_TYPE (NODE) = TYPE)
+#define IDENTIFIER_HAS_TYPE_VALUE(NODE) (TREE_TYPE (NODE) ? 1 : 0)
+
+#define LANG_ID_FIELD(NAME,NODE) \
+ (((struct lang_identifier *)(NODE))->x \
+ ? ((struct lang_identifier *)(NODE))->x->NAME : 0)
+#define SET_LANG_ID(NODE,VALUE,NAME) \
+ (((struct lang_identifier *)(NODE))->x == 0 \
+ ? ((struct lang_identifier *)(NODE))->x \
+ = (struct lang_id2 *)perm_calloc (1, sizeof (struct lang_id2)) : 0, \
+ ((struct lang_identifier *)(NODE))->x->NAME = (VALUE))
+
+#define IDENTIFIER_LABEL_VALUE(NODE) LANG_ID_FIELD(label_value, NODE)
+#define SET_IDENTIFIER_LABEL_VALUE(NODE,VALUE) \
+ SET_LANG_ID(NODE, VALUE, label_value)
+
+#define IDENTIFIER_IMPLICIT_DECL(NODE) LANG_ID_FIELD(implicit_decl, NODE)
+#define SET_IDENTIFIER_IMPLICIT_DECL(NODE,VALUE) \
+ SET_LANG_ID(NODE, VALUE, implicit_decl)
+
+#define IDENTIFIER_AS_DESC(NODE) LANG_ID_FIELD(type_desc, NODE)
+#define SET_IDENTIFIER_AS_DESC(NODE,DESC) \
+ SET_LANG_ID(NODE, DESC, type_desc)
+
+#define IDENTIFIER_AS_LIST(NODE) LANG_ID_FIELD(as_list, NODE)
+#define SET_IDENTIFIER_AS_LIST(NODE,LIST) \
+ SET_LANG_ID(NODE, LIST, as_list)
+
+#define IDENTIFIER_ERROR_LOCUS(NODE) LANG_ID_FIELD(error_locus, NODE)
+#define SET_IDENTIFIER_ERROR_LOCUS(NODE,VALUE) \
+ SET_LANG_ID(NODE, VALUE, error_locus)
+
+
+#define IDENTIFIER_VIRTUAL_P(NODE) TREE_LANG_FLAG_1(NODE)
+
+/* Nonzero if this identifier is the prefix for a mangled C++ operator name. */
+#define IDENTIFIER_OPNAME_P(NODE) TREE_LANG_FLAG_2(NODE)
+
+#define IDENTIFIER_TYPENAME_P(NODE) \
+ (! strncmp (IDENTIFIER_POINTER (NODE), \
+ IDENTIFIER_POINTER (ansi_opname[(int) TYPE_EXPR]), \
+ IDENTIFIER_LENGTH (ansi_opname[(int) TYPE_EXPR])))
+
+/* Nonzero means reject anything that ANSI standard C forbids. */
+extern int pedantic;
+
+/* In a RECORD_TYPE or UNION_TYPE, nonzero if any component is read-only. */
+#define C_TYPE_FIELDS_READONLY(type) TYPE_LANG_FLAG_0 (type)
+
+/* If non-zero, a VAR_DECL whose cleanup will cause a throw to the
+ next exception handler. */
+extern tree exception_throw_decl;
+
+extern tree double_type_node, long_double_type_node, float_type_node;
+extern tree char_type_node, unsigned_char_type_node, signed_char_type_node;
+extern tree ptrdiff_type_node;
+
+extern tree short_integer_type_node, short_unsigned_type_node;
+extern tree long_integer_type_node, long_unsigned_type_node;
+extern tree long_long_integer_type_node, long_long_unsigned_type_node;
+extern tree unsigned_type_node;
+extern tree string_type_node, char_array_type_node, int_array_type_node;
+extern tree wchar_array_type_node;
+extern tree wchar_type_node, signed_wchar_type_node, unsigned_wchar_type_node;
+extern tree intQI_type_node, unsigned_intQI_type_node;
+extern tree intHI_type_node, unsigned_intHI_type_node;
+extern tree intSI_type_node, unsigned_intSI_type_node;
+extern tree intDI_type_node, unsigned_intDI_type_node;
+
+extern int current_function_returns_value;
+extern int current_function_returns_null;
+extern tree current_function_return_value;
+
+extern tree ridpointers[];
+extern tree ansi_opname[];
+extern tree ansi_assopname[];
+
+/* Nonzero means `$' can be in an identifier. */
+
+extern int dollars_in_ident;
+
+/* Nonzero means allow type mismatches in conditional expressions;
+ just make their values `void'. */
+
+extern int flag_cond_mismatch;
+
+/* Nonzero means don't recognize the keyword `asm'. */
+
+extern int flag_no_asm;
+
+/* For cross referencing. */
+
+extern int flag_gnu_xref;
+
+/* For environments where you can use GNU binutils (as, ld in particular). */
+
+extern int flag_gnu_binutils;
+
+/* Nonzero means ignore `#ident' directives. */
+
+extern int flag_no_ident;
+
+/* Nonzero means warn about implicit declarations. */
+
+extern int warn_implicit;
+
+/* Nonzero means warn when all ctors or dtors are private, and the class
+ has no friends. */
+
+extern int warn_ctor_dtor_privacy;
+
+/* Nonzero means warn about function definitions that default the return type
+ or that use a null return and have a return-type other than void. */
+
+extern int warn_return_type;
+
+/* Nonzero means give string constants the type `const char *'
+ to get extra warnings from them. These warnings will be too numerous
+ to be useful, except in thoroughly ANSIfied programs. */
+
+extern int warn_write_strings;
+
+/* Nonzero means warn about sizeof(function) or addition/subtraction
+ of function pointers. */
+
+extern int warn_pointer_arith;
+
+/* Nonzero means warn for all old-style non-prototype function decls. */
+
+extern int warn_strict_prototypes;
+
+/* Nonzero means warn about suggesting putting in ()'s. */
+
+extern int warn_parentheses;
+
+/* Nonzero means warn about multiple (redundant) decls for the same single
+ variable or function. */
+
+extern int warn_redundant_decls;
+
+/* Warn if initializer is not completely bracketed. */
+
+extern int warn_missing_braces;
+
+/* Warn about a subscript that has type char. */
+
+extern int warn_char_subscripts;
+
+/* Nonzero means warn about pointer casts that can drop a type qualifier
+ from the pointer target type. */
+
+extern int warn_cast_qual;
+
+/* Warn about traditional constructs whose meanings changed in ANSI C. */
+
+extern int warn_traditional;
+
+/* Warn about *printf or *scanf format/argument anomalies. */
+
+extern int warn_format;
+
+/* Nonzero means warn about non virtual destructors in classes that have
+ virtual functions. */
+
+extern int warn_nonvdtor;
+
+/* Non-zero means warn when a function is declared extern and later inline. */
+extern int warn_extern_inline;
+
+/* Nonzero means do some things the same way PCC does. */
+
+extern int flag_traditional;
+
+/* Nonzero means to treat bitfields as unsigned unless they say `signed'. */
+
+extern int flag_signed_bitfields;
+
+/* 3 means write out only virtuals function tables `defined'
+ in this implementation file.
+ 2 means write out only specific virtual function tables
+ and give them (C) public access.
+ 1 means write out virtual function tables and give them
+ (C) public access.
+ 0 means write out virtual function tables and give them
+ (C) static access (default).
+ -1 means declare virtual function tables extern. */
+
+extern int write_virtuals;
+
+/* True for more efficient but incompatible (not not fully tested)
+ vtable implementation (using thunks).
+ 0 is old behavior; 1 is new behavior. */
+extern int flag_vtable_thunks;
+
+/* INTERFACE_ONLY nonzero means that we are in an "interface"
+ section of the compiler. INTERFACE_UNKNOWN nonzero means
+ we cannot trust the value of INTERFACE_ONLY. If INTERFACE_UNKNOWN
+ is zero and INTERFACE_ONLY is zero, it means that we are responsible
+ for exporting definitions that others might need. */
+extern int interface_only, interface_unknown;
+
+/* Nonzero means we should attempt to elide constructors when possible. */
+
+extern int flag_elide_constructors;
+
+/* Nonzero means handle things in ANSI, instead of GNU fashion. */
+
+extern int flag_ansi;
+
+/* Nonzero means recognize and handle ansi-style exception handling
+ constructs. */
+
+extern int flag_handle_exceptions;
+
+/* Nonzero means recognize and handle signature language constructs. */
+
+extern int flag_handle_signatures;
+
+/* Nonzero means that member functions defined in class scope are
+ inline by default. */
+
+extern int flag_default_inline;
+
+/* Nonzero means emit cadillac protocol. */
+
+extern int flag_cadillac;
+
+/* C++ language-specific tree codes. */
+#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) SYM,
+enum cplus_tree_code {
+ __DUMMY = LAST_AND_UNUSED_TREE_CODE,
+#include "tree.def"
+ LAST_CPLUS_TREE_CODE
+};
+#undef DEFTREECODE
+
+/* Override OFFSET_REFs from the back-end, as we want our very own. */
+/* Allow complex pointer to members to work correctly. */
+#define OFFSET_REF CP_OFFSET_REF
+
+enum languages { lang_c, lang_cplusplus };
+
+/* Macros to make error reporting functions' lives easier. */
+#define TYPE_IDENTIFIER(NODE) (DECL_NAME (TYPE_NAME (NODE)))
+#define TYPE_NAME_STRING(NODE) (IDENTIFIER_POINTER (TYPE_IDENTIFIER (NODE)))
+#define TYPE_NAME_LENGTH(NODE) (IDENTIFIER_LENGTH (TYPE_IDENTIFIER (NODE)))
+
+#define TYPE_ASSEMBLER_NAME_STRING(NODE) (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (TYPE_NAME (NODE))))
+#define TYPE_ASSEMBLER_NAME_LENGTH(NODE) (IDENTIFIER_LENGTH (DECL_ASSEMBLER_NAME (TYPE_NAME (NODE))))
+
+/* The _DECL for this _TYPE. */
+#define TYPE_MAIN_DECL(NODE) (TYPE_NAME (NODE))
+
+#define IS_AGGR_TYPE(t) (TYPE_LANG_FLAG_5 (t))
+#define IS_AGGR_TYPE_CODE(t) (t == RECORD_TYPE || t == UNION_TYPE || t == UNINSTANTIATED_P_TYPE)
+#define IS_AGGR_TYPE_2(TYPE1,TYPE2) \
+ (TREE_CODE (TYPE1) == TREE_CODE (TYPE2) \
+ && IS_AGGR_TYPE (TYPE1)&IS_AGGR_TYPE (TYPE2))
+#define IS_OVERLOAD_TYPE_CODE(t) (IS_AGGR_TYPE_CODE (t) || t == ENUMERAL_TYPE)
+#define IS_OVERLOAD_TYPE(t) (IS_OVERLOAD_TYPE_CODE (TREE_CODE (t)))
+
+/* In a *_TYPE, nonzero means a built-in type. */
+#define TYPE_BUILT_IN(NODE) TYPE_LANG_FLAG_6(NODE)
+
+/* Macros which might want to be replaced by function calls. */
+
+#define DELTA_FROM_VTABLE_ENTRY(ENTRY) \
+ (!flag_vtable_thunks ? \
+ TREE_VALUE (CONSTRUCTOR_ELTS (ENTRY)) \
+ : TREE_CODE (TREE_OPERAND ((ENTRY), 0)) != THUNK_DECL ? integer_zero_node \
+ : build_int_2 (THUNK_DELTA (TREE_OPERAND ((ENTRY), 0)), 0))
+#if 1
+/* Virtual function addresses can be gotten from a virtual function
+ table entry using this macro. */
+#define FNADDR_FROM_VTABLE_ENTRY(ENTRY) \
+ (!flag_vtable_thunks ? \
+ TREE_VALUE (TREE_CHAIN (TREE_CHAIN (CONSTRUCTOR_ELTS (ENTRY)))) \
+ : TREE_CODE (TREE_OPERAND ((ENTRY), 0)) != THUNK_DECL ? (ENTRY) \
+ : DECL_INITIAL (TREE_OPERAND ((ENTRY), 0)))
+#define SET_FNADDR_FROM_VTABLE_ENTRY(ENTRY,VALUE) \
+ (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (CONSTRUCTOR_ELTS (ENTRY)))) = (VALUE))
+#define FUNCTION_ARG_CHAIN(NODE) (TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (NODE))))
+#define PROMOTES_TO_AGGR_TYPE(NODE,CODE) \
+ (((CODE) == TREE_CODE (NODE) \
+ && IS_AGGR_TYPE (TREE_TYPE (NODE))) \
+ || IS_AGGR_TYPE (NODE))
+
+#else
+#define FNADDR_FROM_VTABLE_ENTRY(ENTRY) (fnaddr_from_vtable_entry (ENTRY))
+#define SET_FNADDR_FROM_VTABLE_ENTRY(ENTRY,VALUE) \
+ (set_fnaddr_from_vtable_entry (ENTRY, VALUE))
+/* #define TYPE_NAME_STRING(NODE) (type_name_string (NODE)) */
+#define FUNCTION_ARG_CHAIN(NODE) (function_arg_chain (NODE))
+#define PROMOTES_TO_AGGR_TYPE(NODE,CODE) (promotes_to_aggr_type (NODE, CODE))
+/* #define IS_AGGR_TYPE_2(TYPE1, TYPE2) (is_aggr_type_2 (TYPE1, TYPE2)) */
+#endif
+/* Nonzero iff TYPE is uniquely derived from PARENT. Under MI, PARENT can
+ be an ambiguous base class of TYPE, and this macro will be false. */
+#define UNIQUELY_DERIVED_FROM_P(PARENT, TYPE) (get_base_distance (PARENT, TYPE, 0, (tree *)0) >= 0)
+#define ACCESSIBLY_DERIVED_FROM_P(PARENT, TYPE) (get_base_distance (PARENT, TYPE, -1, (tree *)0) >= 0)
+#define ACCESSIBLY_UNIQUELY_DERIVED_P(PARENT, TYPE) (get_base_distance (PARENT, TYPE, 1, (tree *)0) >= 0)
+#define DERIVED_FROM_P(PARENT, TYPE) (get_base_distance (PARENT, TYPE, 0, (tree *)0) != -1)
+
+enum conversion_type { ptr_conv, constptr_conv, int_conv,
+ real_conv, last_conversion_type };
+
+/* Statistics show that while the GNU C++ compiler may generate
+ thousands of different types during a compilation run, it
+ generates relatively few (tens) of classtypes. Because of this,
+ it is not costly to store a generous amount of information
+ in classtype nodes. This struct must fill out to a multiple of 4 bytes. */
+struct lang_type
+{
+ struct
+ {
+ unsigned has_type_conversion : 1;
+ unsigned has_int_conversion : 1;
+ unsigned has_float_conversion : 1;
+ unsigned has_init_ref : 1;
+ unsigned gets_init_aggr : 1;
+ unsigned has_assignment : 1;
+ unsigned has_default_ctor : 1;
+ unsigned uses_multiple_inheritance : 1;
+
+ unsigned has_nonpublic_ctor : 2;
+ unsigned has_nonpublic_assign_ref : 2;
+ unsigned const_needs_init : 1;
+ unsigned ref_needs_init : 1;
+ unsigned has_const_assign_ref : 1;
+ unsigned vtable_needs_writing : 1;
+
+ unsigned has_assign_ref : 1;
+ unsigned gets_new : 2;
+ unsigned gets_delete : 2;
+ unsigned has_call_overloaded : 1;
+ unsigned has_array_ref_overloaded : 1;
+ unsigned has_arrow_overloaded : 1;
+
+ unsigned local_typedecls : 1;
+ unsigned interface_only : 1;
+ unsigned interface_unknown : 1;
+ unsigned needs_virtual_reinit : 1;
+ unsigned declared_exception : 1;
+ unsigned declared_class : 1;
+ unsigned being_defined : 1;
+ unsigned redefined : 1;
+
+ unsigned no_globalize : 1;
+ unsigned marked : 1;
+ unsigned marked2 : 1;
+ unsigned marked3 : 1;
+ unsigned marked4 : 1;
+ unsigned marked5 : 1;
+ unsigned marked6 : 1;
+
+ unsigned use_template : 2;
+ unsigned debug_requested : 1;
+ unsigned has_method_call_overloaded : 1;
+ unsigned private_attr : 1;
+ unsigned got_semicolon : 1;
+ unsigned ptrmemfunc_flag : 1;
+ unsigned is_signature : 1;
+ unsigned is_signature_pointer : 1;
+
+ unsigned is_signature_reference : 1;
+ unsigned has_default_implementation : 1;
+ unsigned grokking_typedef : 1;
+ unsigned has_opaque_typedecls : 1;
+ unsigned sigtable_has_been_generated : 1;
+ unsigned was_anonymous : 1;
+ unsigned has_real_assignment : 1;
+ unsigned has_real_assign_ref : 1;
+
+ unsigned has_const_init_ref : 1;
+ unsigned has_complex_init_ref : 1;
+ unsigned has_complex_assign_ref : 1;
+ unsigned vec_delete_takes_size : 1;
+ unsigned has_abstract_assign_ref : 1;
+
+ /* The MIPS compiler gets it wrong if this struct also
+ does not fill out to a multiple of 4 bytes. Add a
+ member `dummy' with new bits if you go over the edge. */
+ unsigned dummy : 19;
+
+ unsigned n_vancestors : 16;
+ } type_flags;
+
+ int cid;
+ int n_ancestors;
+ int vsize;
+ int max_depth;
+ int vfield_parent;
+
+ union tree_node *vbinfo[2];
+ union tree_node *baselink_vec;
+ union tree_node *vfields;
+ union tree_node *vbases;
+ union tree_node *vbase_size;
+
+ union tree_node *tags;
+ char *memoized_table_entry;
+
+ char *search_slot;
+
+#ifdef ONLY_INT_FIELDS
+ unsigned int mode : 8;
+#else
+ enum machine_mode mode : 8;
+#endif
+
+ unsigned char size_unit;
+ unsigned char align;
+ unsigned char sep_unit;
+
+ union tree_node *sep;
+ union tree_node *size;
+
+ union tree_node *base_init_list;
+ union tree_node *abstract_virtuals;
+ union tree_node *as_list;
+ union tree_node *id_as_list;
+ union tree_node *binfo_as_list;
+ union tree_node *vtbl_ptr;
+ union tree_node *instance_variable;
+ union tree_node *friend_classes;
+
+ char *mi_matrix;
+ union tree_node *conversions[last_conversion_type];
+
+ union tree_node *dossier;
+
+ union tree_node *methods;
+
+ union tree_node *signature;
+ union tree_node *signature_pointer_to;
+ union tree_node *signature_reference_to;
+};
+
+/* Indicates whether or not (and how) a template was expanded for this class.
+ 0=no information yet/non-template class
+ 1=implicit template instantiation
+ 2=explicit template specialization
+ 3=explicit template instantiation */
+#define CLASSTYPE_USE_TEMPLATE(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.use_template)
+
+/* Fields used for storing information before the class is defined.
+ After the class is defined, these fields hold other information. */
+
+/* List of friends which were defined inline in this class definition. */
+#define CLASSTYPE_INLINE_FRIENDS(NODE) (TYPE_NONCOPIED_PARTS (NODE))
+
+/* Nonzero for _CLASSTYPE means that the _CLASSTYPE either has
+ a special meaning for the assignment operator ("operator="),
+ or one of its fields (or base members) has a special meaning
+ defined. */
+#define TYPE_HAS_ASSIGNMENT(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_assignment)
+#define TYPE_HAS_REAL_ASSIGNMENT(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_real_assignment)
+
+/* Nonzero for _CLASSTYPE means that operator new and delete are defined,
+ respectively. */
+#define TYPE_GETS_NEW(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.gets_new)
+#define TYPE_GETS_DELETE(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.gets_delete)
+#define TYPE_GETS_REG_DELETE(NODE) (TYPE_GETS_DELETE (NODE) & 1)
+
+/* Nonzero for _CLASSTYPE means that operator vec delete is defined and
+ takes the optional size_t argument. */
+#define TYPE_VEC_DELETE_TAKES_SIZE(NODE) \
+ (TYPE_LANG_SPECIFIC(NODE)->type_flags.vec_delete_takes_size)
+#define TYPE_VEC_NEW_USES_COOKIE(NODE) \
+ (TYPE_NEEDS_DESTRUCTOR (NODE) \
+ || (TYPE_LANG_SPECIFIC (NODE) && TYPE_VEC_DELETE_TAKES_SIZE (NODE)))
+
+/* Nonzero for TREE_LIST or _TYPE node means that this node is class-local. */
+#define TREE_NONLOCAL_FLAG(NODE) (TREE_LANG_FLAG_0 (NODE))
+
+/* Nonzero for a _CLASSTYPE node which we know to be private. */
+#define TYPE_PRIVATE_P(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.private_attr)
+
+/* Nonzero means that this _CLASSTYPE node defines ways of converting
+ itself to other types. */
+#define TYPE_HAS_CONVERSION(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_type_conversion)
+
+/* Nonzero means that this _CLASSTYPE node can convert itself to an
+ INTEGER_TYPE. */
+#define TYPE_HAS_INT_CONVERSION(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_int_conversion)
+
+/* Nonzero means that this _CLASSTYPE node can convert itself to an
+ REAL_TYPE. */
+#define TYPE_HAS_REAL_CONVERSION(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_float_conversion)
+
+/* Nonzero means that this _CLASSTYPE node overloads operator=(X&). */
+#define TYPE_HAS_ASSIGN_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_assign_ref)
+#define TYPE_HAS_CONST_ASSIGN_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_const_assign_ref)
+
+/* Nonzero means that this _CLASSTYPE node has an X(X&) constructor. */
+#define TYPE_HAS_INIT_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_init_ref)
+#define TYPE_HAS_CONST_INIT_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_const_init_ref)
+
+/* Nonzero means that this _CLASSTYPE node has an X(X ...) constructor.
+ Note that there must be other arguments, or this constructor is flagged
+ as being erroneous. */
+#define TYPE_GETS_INIT_AGGR(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.gets_init_aggr)
+
+/* Nonzero means that this type is being defined. I.e., the left brace
+ starting the definition of this type has been seen. */
+#define TYPE_BEING_DEFINED(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.being_defined)
+/* Nonzero means that this type has been redefined. In this case, if
+ convenient, don't reprocess any methods that appear in its redefinition. */
+#define TYPE_REDEFINED(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.redefined)
+
+/* Nonzero means that this _CLASSTYPE node overloads the method call
+ operator. In this case, all method calls go through `operator->()(...). */
+#define TYPE_OVERLOADS_METHOD_CALL_EXPR(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_method_call_overloaded)
+
+/* Nonzero means that this type is a signature. */
+# define IS_SIGNATURE(NODE) (TYPE_LANG_SPECIFIC(NODE)?TYPE_LANG_SPECIFIC(NODE)->type_flags.is_signature:0)
+# define SET_SIGNATURE(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.is_signature=1)
+# define CLEAR_SIGNATURE(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.is_signature=0)
+
+/* Nonzero means that this type is a signature pointer type. */
+# define IS_SIGNATURE_POINTER(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.is_signature_pointer)
+
+/* Nonzero means that this type is a signature reference type. */
+# define IS_SIGNATURE_REFERENCE(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.is_signature_reference)
+
+/* Nonzero means that this signature type has a default implementation. */
+# define HAS_DEFAULT_IMPLEMENTATION(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_default_implementation)
+
+/* Nonzero means that grokdeclarator works on a signature-local typedef. */
+#define SIGNATURE_GROKKING_TYPEDEF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.grokking_typedef)
+
+/* Nonzero means that this signature contains opaque type declarations. */
+#define SIGNATURE_HAS_OPAQUE_TYPEDECLS(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_opaque_typedecls)
+
+/* Nonzero means that a signature table has been generated
+ for this signature. */
+#define SIGTABLE_HAS_BEEN_GENERATED(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.sigtable_has_been_generated)
+
+/* If NODE is a class, this is the signature type that contains NODE's
+ signature after it has been computed using sigof(). */
+#define CLASSTYPE_SIGNATURE(NODE) (TYPE_LANG_SPECIFIC(NODE)->signature)
+
+/* If NODE is a signature pointer or signature reference, this is the
+ signature type the pointer/reference points to. */
+#define SIGNATURE_TYPE(NODE) (TYPE_LANG_SPECIFIC(NODE)->signature)
+
+/* If NODE is a signature, this is a vector of all methods defined
+ in the signature or in its base types together with their default
+ implementations. */
+#define SIGNATURE_METHOD_VEC(NODE) (TYPE_LANG_SPECIFIC(NODE)->signature)
+
+/* If NODE is a signature, this is the _TYPE node that contains NODE's
+ signature pointer type. */
+#define SIGNATURE_POINTER_TO(NODE) (TYPE_LANG_SPECIFIC(NODE)->signature_pointer_to)
+
+/* If NODE is a signature, this is the _TYPE node that contains NODE's
+ signature reference type. */
+#define SIGNATURE_REFERENCE_TO(NODE) (TYPE_LANG_SPECIFIC(NODE)->signature_reference_to)
+
+/* The is the VAR_DECL that contains NODE's dossier. */
+#define CLASSTYPE_DOSSIER(NODE) (TYPE_LANG_SPECIFIC(NODE)->dossier)
+
+/* List of all explicit methods (chained using DECL_NEXT_METHOD),
+ in order they were parsed. */
+#define CLASSTYPE_METHODS(NODE) (TYPE_LANG_SPECIFIC(NODE)->methods)
+
+/* Nonzero means that this _CLASSTYPE node overloads operator(). */
+#define TYPE_OVERLOADS_CALL_EXPR(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_call_overloaded)
+
+/* Nonzero means that this _CLASSTYPE node overloads operator[]. */
+#define TYPE_OVERLOADS_ARRAY_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_array_ref_overloaded)
+
+/* Nonzero means that this _CLASSTYPE node overloads operator->. */
+#define TYPE_OVERLOADS_ARROW(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_arrow_overloaded)
+
+/* Nonzero means that this _CLASSTYPE (or one of its ancestors) uses
+ multiple inheritance. If this is 0 for the root of a type
+ hierarchy, then we can use more efficient search techniques. */
+#define TYPE_USES_MULTIPLE_INHERITANCE(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.uses_multiple_inheritance)
+
+/* Nonzero means that this _CLASSTYPE (or one of its ancestors) uses
+ virtual base classes. If this is 0 for the root of a type
+ hierarchy, then we can use more efficient search techniques. */
+#define TYPE_USES_VIRTUAL_BASECLASSES(NODE) (TREE_LANG_FLAG_3(NODE))
+
+/* List of lists of member functions defined in this class. */
+#define CLASSTYPE_METHOD_VEC(NODE) TYPE_METHODS(NODE)
+
+/* Pointer from any member function to the head of the list of
+ member functions of the type that member function belongs to. */
+#define CLASSTYPE_BASELINK_VEC(NODE) (TYPE_LANG_SPECIFIC(NODE)->baselink_vec)
+
+/* Mark bits for depth-first and breath-first searches. */
+#define CLASSTYPE_MARKED(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.marked)
+#define CLASSTYPE_MARKED2(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.marked2)
+#define CLASSTYPE_MARKED3(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.marked3)
+#define CLASSTYPE_MARKED4(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.marked4)
+#define CLASSTYPE_MARKED5(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.marked5)
+#define CLASSTYPE_MARKED6(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.marked6)
+/* Macros to modify the above flags */
+#define SET_CLASSTYPE_MARKED(NODE) (CLASSTYPE_MARKED(NODE) = 1)
+#define CLEAR_CLASSTYPE_MARKED(NODE) (CLASSTYPE_MARKED(NODE) = 0)
+#define SET_CLASSTYPE_MARKED2(NODE) (CLASSTYPE_MARKED2(NODE) = 1)
+#define CLEAR_CLASSTYPE_MARKED2(NODE) (CLASSTYPE_MARKED2(NODE) = 0)
+#define SET_CLASSTYPE_MARKED3(NODE) (CLASSTYPE_MARKED3(NODE) = 1)
+#define CLEAR_CLASSTYPE_MARKED3(NODE) (CLASSTYPE_MARKED3(NODE) = 0)
+#define SET_CLASSTYPE_MARKED4(NODE) (CLASSTYPE_MARKED4(NODE) = 1)
+#define CLEAR_CLASSTYPE_MARKED4(NODE) (CLASSTYPE_MARKED4(NODE) = 0)
+#define SET_CLASSTYPE_MARKED5(NODE) (CLASSTYPE_MARKED5(NODE) = 1)
+#define CLEAR_CLASSTYPE_MARKED5(NODE) (CLASSTYPE_MARKED5(NODE) = 0)
+#define SET_CLASSTYPE_MARKED6(NODE) (CLASSTYPE_MARKED6(NODE) = 1)
+#define CLEAR_CLASSTYPE_MARKED6(NODE) (CLASSTYPE_MARKED6(NODE) = 0)
+
+#define CLASSTYPE_TAGS(NODE) (TYPE_LANG_SPECIFIC(NODE)->tags)
+
+/* If this class has any bases, this is the number of the base class from
+ which our VFIELD is based, -1 otherwise. If this class has no base
+ classes, this is not used.
+ In D : B1, B2, PARENT would be 0, if D's vtable came from B1,
+ 1, if D's vtable came from B2. */
+#define CLASSTYPE_VFIELD_PARENT(NODE) (TYPE_LANG_SPECIFIC(NODE)->vfield_parent)
+
+/* Remove when done merging. */
+#define CLASSTYPE_VFIELD(NODE) TYPE_VFIELD(NODE)
+
+/* The number of virtual functions defined for this
+ _CLASSTYPE node. */
+#define CLASSTYPE_VSIZE(NODE) (TYPE_LANG_SPECIFIC(NODE)->vsize)
+/* The virtual base classes that this type uses. */
+#define CLASSTYPE_VBASECLASSES(NODE) (TYPE_LANG_SPECIFIC(NODE)->vbases)
+/* The virtual function pointer fields that this type contains. */
+#define CLASSTYPE_VFIELDS(NODE) (TYPE_LANG_SPECIFIC(NODE)->vfields)
+
+/* Number of baseclasses defined for this type.
+ 0 means no base classes. */
+#define CLASSTYPE_N_BASECLASSES(NODE) \
+ (TYPE_BINFO_BASETYPES (NODE) ? TREE_VEC_LENGTH (TYPE_BINFO_BASETYPES(NODE)) : 0)
+
+/* Memoize the number of super classes (base classes) tha this node
+ has. That way we can know immediately (albeit conservatively how
+ large a multiple-inheritance matrix we need to build to find
+ derivation information. */
+#define CLASSTYPE_N_SUPERCLASSES(NODE) (TYPE_LANG_SPECIFIC(NODE)->n_ancestors)
+#define CLASSTYPE_N_VBASECLASSES(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.n_vancestors)
+
+/* Record how deep the inheritance is for this class so `void*' conversions
+ are less favorable than a conversion to the most base type. */
+#define CLASSTYPE_MAX_DEPTH(NODE) (TYPE_LANG_SPECIFIC(NODE)->max_depth)
+
+/* Used for keeping search-specific information. Any search routine
+ which uses this must define what exactly this slot is used for. */
+#define CLASSTYPE_SEARCH_SLOT(NODE) (TYPE_LANG_SPECIFIC(NODE)->search_slot)
+
+/* Entry for keeping memoization tables for this type to
+ hopefully speed up search routines. Since it is a pointer,
+ it can mean almost anything. */
+#define CLASSTYPE_MTABLE_ENTRY(NODE) (TYPE_LANG_SPECIFIC(NODE)->memoized_table_entry)
+
+/* This is the total size of the baseclasses defined for this type.
+ Needed because it is desirable to layout such information
+ before beginning to process the class itself, and we
+ don't want to compute it second time when actually laying
+ out the type for real. */
+#define CLASSTYPE_SIZE(NODE) (TYPE_LANG_SPECIFIC(NODE)->size)
+#define CLASSTYPE_SIZE_UNIT(NODE) (TYPE_LANG_SPECIFIC(NODE)->size_unit)
+#define CLASSTYPE_MODE(NODE) (TYPE_LANG_SPECIFIC(NODE)->mode)
+#define CLASSTYPE_ALIGN(NODE) (TYPE_LANG_SPECIFIC(NODE)->align)
+
+/* This is the space needed for virtual base classes. NULL if
+ there are no virtual basetypes. */
+#define CLASSTYPE_VBASE_SIZE(NODE) (TYPE_LANG_SPECIFIC(NODE)->vbase_size)
+
+/* A cons list of structure elements which either have constructors
+ to be called, or virtual function table pointers which
+ need initializing. Depending on what is being initialized,
+ the TREE_PURPOSE and TREE_VALUE fields have different meanings:
+
+ Member initialization: <FIELD_DECL, TYPE>
+ Base class construction: <NULL_TREE, BASETYPE>
+ Base class initialization: <BASE_INITIALIZATION, THESE_INITIALIZATIONS>
+ Whole type: <MEMBER_INIT, BASE_INIT>. */
+#define CLASSTYPE_BASE_INIT_LIST(NODE) (TYPE_LANG_SPECIFIC(NODE)->base_init_list)
+
+/* A cons list of virtual functions which cannot be inherited by
+ derived classes. When deriving from this type, the derived
+ class must provide its own definition for each of these functions. */
+#define CLASSTYPE_ABSTRACT_VIRTUALS(NODE) (TYPE_LANG_SPECIFIC(NODE)->abstract_virtuals)
+
+/* Nonzero means that this aggr type has been `closed' by a semicolon. */
+#define CLASSTYPE_GOT_SEMICOLON(NODE) (TYPE_LANG_SPECIFIC (NODE)->type_flags.got_semicolon)
+
+/* Nonzero means that the main virtual function table pointer needs to be
+ set because base constructors have placed the wrong value there.
+ If this is zero, it means that they placed the right value there,
+ and there is no need to change it. */
+#define CLASSTYPE_NEEDS_VIRTUAL_REINIT(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.needs_virtual_reinit)
+
+/* Nonzero means that if this type has virtual functions, that
+ the virtual function table will be written out. */
+#define CLASSTYPE_VTABLE_NEEDS_WRITING(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.vtable_needs_writing)
+
+/* Nonzero means that this type defines its own local type declarations. */
+#define CLASSTYPE_LOCAL_TYPEDECLS(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.local_typedecls)
+
+/* Nonzero means that this type has an X() constructor. */
+#define TYPE_HAS_DEFAULT_CONSTRUCTOR(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_default_ctor)
+
+/* Nonzero means the type declared a ctor as private or protected. We
+ use this to make sure we don't try to generate a copy ctor for a
+ class that has a member of type NODE. */
+#define TYPE_HAS_NONPUBLIC_CTOR(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_nonpublic_ctor)
+
+/* Ditto, for operator=. */
+#define TYPE_HAS_NONPUBLIC_ASSIGN_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_nonpublic_assign_ref)
+
+/* Many routines need to cons up a list of basetypes for access
+ checking. This field contains a TREE_LIST node whose TREE_VALUE
+ is the main variant of the type, and whose TREE_VIA_PUBLIC
+ and TREE_VIA_VIRTUAL bits are correctly set. */
+#define CLASSTYPE_AS_LIST(NODE) (TYPE_LANG_SPECIFIC(NODE)->as_list)
+/* Same, but cache a list whose value is the name of this type. */
+#define CLASSTYPE_ID_AS_LIST(NODE) (TYPE_LANG_SPECIFIC(NODE)->id_as_list)
+/* Same, but cache a list whose value is the binfo of this type. */
+#define CLASSTYPE_BINFO_AS_LIST(NODE) (TYPE_LANG_SPECIFIC(NODE)->binfo_as_list)
+
+/* Slot in which to cache a copy of the local vtable pointer. */
+#define CLASSTYPE_VTBL_PTR(NODE) (TYPE_LANG_SPECIFIC(NODE)->vtbl_ptr)
+
+/* Hold the instance object associated with this method. */
+#define CLASSTYPE_INST_VAR(NODE) (TYPE_LANG_SPECIFIC(NODE)->instance_variable)
+
+/* A list of class types with which this type is a friend. */
+#define CLASSTYPE_FRIEND_CLASSES(NODE) (TYPE_LANG_SPECIFIC(NODE)->friend_classes)
+
+/* Keep an inheritance lattice around so we can quickly tell whether
+ a type is derived from another or not. */
+#define CLASSTYPE_MI_MATRIX(NODE) (TYPE_LANG_SPECIFIC(NODE)->mi_matrix)
+
+/* If there is exactly one conversion to a non-void, non-const pointer type,
+ remember that here. If there are more than one, put
+ `error_mark_node' here. If there are none, this holds NULL_TREE. */
+#define CLASSTYPE_CONVERSION(NODE,KIND) \
+ (TYPE_LANG_SPECIFIC(NODE)->conversions[(int) KIND])
+
+/* Say whether this node was declared as a "class" or a "struct". */
+#define CLASSTYPE_DECLARED_CLASS(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.declared_class)
+/* Say whether this node was declared as a "class" or a "struct". */
+#define CLASSTYPE_DECLARED_EXCEPTION(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.declared_exception)
+/* whether this can be globalized. */
+#define CLASSTYPE_NO_GLOBALIZE(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.no_globalize)
+
+/* Nonzero if this class has const members which have no specified initialization. */
+#define CLASSTYPE_READONLY_FIELDS_NEED_INIT(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.const_needs_init)
+
+/* Nonzero if this class has ref members which have no specified initialization. */
+#define CLASSTYPE_REF_FIELDS_NEED_INIT(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.ref_needs_init)
+
+/* Nonzero if this class is included from a header file which employs
+ `#pragma interface', and it is not included in its implementation file. */
+#define CLASSTYPE_INTERFACE_ONLY(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.interface_only)
+
+/* Same as above, but for classes whose purpose we do not know. */
+#define CLASSTYPE_INTERFACE_UNKNOWN(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.interface_unknown)
+#define CLASSTYPE_INTERFACE_KNOWN(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.interface_unknown == 0)
+#define SET_CLASSTYPE_INTERFACE_UNKNOWN_X(NODE,X) (TYPE_LANG_SPECIFIC(NODE)->type_flags.interface_unknown = !!(X))
+#define SET_CLASSTYPE_INTERFACE_UNKNOWN(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.interface_unknown = 1)
+#define SET_CLASSTYPE_INTERFACE_KNOWN(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.interface_unknown = 0)
+
+/* Nonzero if a _DECL node requires us to output debug info for this class. */
+#define CLASSTYPE_DEBUG_REQUESTED(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.debug_requested)
+
+/* Additional macros for inheritance information. */
+
+#define CLASSTYPE_VBINFO(NODE,VIA_PUBLIC) \
+ (TYPE_LANG_SPECIFIC (NODE)->vbinfo[VIA_PUBLIC])
+
+/* When following an binfo-specific chain, this is the cumulative
+ via-public flag. */
+#define BINFO_VIA_PUBLIC(NODE) TREE_LANG_FLAG_5 (NODE)
+
+/* When building a matrix to determine by a single lookup
+ whether one class is derived from another or not,
+ this field is the index of the class in the table. */
+#define CLASSTYPE_CID(NODE) (TYPE_LANG_SPECIFIC(NODE)->cid)
+#define BINFO_CID(NODE) CLASSTYPE_CID(BINFO_TYPE(NODE))
+
+/* Nonzero means marked by DFS or BFS search, including searches
+ by `get_binfo' and `get_base_distance'. */
+#define BINFO_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?CLASSTYPE_MARKED(BINFO_TYPE(NODE)):TREE_LANG_FLAG_0(NODE))
+/* Macros needed because of C compilers that don't allow conditional
+ expressions to be lvalues. Grr! */
+#define SET_BINFO_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?SET_CLASSTYPE_MARKED(BINFO_TYPE(NODE)):(TREE_LANG_FLAG_0(NODE)=1))
+#define CLEAR_BINFO_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?CLEAR_CLASSTYPE_MARKED(BINFO_TYPE(NODE)):(TREE_LANG_FLAG_0(NODE)=0))
+
+/* Nonzero means marked in building initialization list. */
+#define BINFO_BASEINIT_MARKED(NODE) CLASSTYPE_MARKED2 (BINFO_TYPE (NODE))
+/* Modifier macros */
+#define SET_BINFO_BASEINIT_MARKED(NODE) SET_CLASSTYPE_MARKED2 (BINFO_TYPE (NODE))
+#define CLEAR_BINFO_BASEINIT_MARKED(NODE) CLEAR_CLASSTYPE_MARKED2 (BINFO_TYPE (NODE))
+
+/* Nonzero means marked in search through virtual inheritance hierarchy. */
+#define BINFO_VBASE_MARKED(NODE) CLASSTYPE_MARKED2 (BINFO_TYPE (NODE))
+/* Modifier macros */
+#define SET_BINFO_VBASE_MARKED(NODE) SET_CLASSTYPE_MARKED2 (BINFO_TYPE (NODE))
+#define CLEAR_BINFO_VBASE_MARKED(NODE) CLEAR_CLASSTYPE_MARKED2 (BINFO_TYPE (NODE))
+
+/* Nonzero means marked in search for members or member functions. */
+#define BINFO_FIELDS_MARKED(NODE) \
+ (TREE_VIA_VIRTUAL(NODE)?CLASSTYPE_MARKED2 (BINFO_TYPE (NODE)):TREE_LANG_FLAG_2(NODE))
+#define SET_BINFO_FIELDS_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?SET_CLASSTYPE_MARKED2(BINFO_TYPE(NODE)):(TREE_LANG_FLAG_2(NODE)=1))
+#define CLEAR_BINFO_FIELDS_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?CLEAR_CLASSTYPE_MARKED2(BINFO_TYPE(NODE)):(TREE_LANG_FLAG_2(NODE)=0))
+
+/* Nonzero means that this class is on a path leading to a new vtable. */
+#define BINFO_VTABLE_PATH_MARKED(NODE) \
+ (TREE_VIA_VIRTUAL(NODE)?CLASSTYPE_MARKED3(BINFO_TYPE(NODE)):TREE_LANG_FLAG_3(NODE))
+#define SET_BINFO_VTABLE_PATH_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?SET_CLASSTYPE_MARKED3(BINFO_TYPE(NODE)):(TREE_LANG_FLAG_3(NODE)=1))
+#define CLEAR_BINFO_VTABLE_PATH_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?CLEAR_CLASSTYPE_MARKED3(BINFO_TYPE(NODE)):(TREE_LANG_FLAG_3(NODE)=0))
+
+/* Nonzero means that this class has a new vtable. */
+#define BINFO_NEW_VTABLE_MARKED(NODE) \
+ (TREE_VIA_VIRTUAL(NODE)?CLASSTYPE_MARKED4(BINFO_TYPE(NODE)):TREE_LANG_FLAG_4(NODE))
+#define SET_BINFO_NEW_VTABLE_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?SET_CLASSTYPE_MARKED4(BINFO_TYPE(NODE)):(TREE_LANG_FLAG_4(NODE)=1))
+#define CLEAR_BINFO_NEW_VTABLE_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?CLEAR_CLASSTYPE_MARKED4(BINFO_TYPE(NODE)):(TREE_LANG_FLAG_4(NODE)=0))
+
+/* Nonzero means this class has initialized its virtual baseclasses. */
+#define BINFO_VBASE_INIT_MARKED(NODE) \
+ (TREE_VIA_VIRTUAL(NODE)?CLASSTYPE_MARKED5(BINFO_TYPE(NODE)):TREE_LANG_FLAG_5(NODE))
+#define SET_BINFO_VBASE_INIT_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?SET_CLASSTYPE_MARKED5(BINFO_TYPE(NODE)):(TREE_LANG_FLAG_5(NODE)=1))
+#define CLEAR_BINFO_VBASE_INIT_MARKED(NODE) (TREE_VIA_VIRTUAL(NODE)?CLEAR_CLASSTYPE_MARKED5(BINFO_TYPE(NODE)):(TREE_LANG_FLAG_5(NODE)=0))
+
+/* Accessor macros for the vfield slots in structures. */
+
+/* Get the assoc info that caused this vfield to exist. */
+#define VF_BINFO_VALUE(NODE) TREE_PURPOSE (NODE)
+
+/* Get that same information as a _TYPE. */
+#define VF_BASETYPE_VALUE(NODE) TREE_VALUE (NODE)
+
+/* Get the value of the top-most type dominating the non-`normal' vfields. */
+#define VF_DERIVED_VALUE(NODE) (VF_BINFO_VALUE (NODE) ? BINFO_TYPE (VF_BINFO_VALUE (NODE)) : NULL_TREE)
+
+/* Get the value of the top-most type that's `normal' for the vfield. */
+#define VF_NORMAL_VALUE(NODE) TREE_TYPE (NODE)
+
+/* Nonzero for TREE_LIST node means that this list of things
+ is a list of parameters, as opposed to a list of expressions. */
+#define TREE_PARMLIST(NODE) ((NODE)->common.unsigned_flag) /* overloaded! */
+
+/* For FUNCTION_TYPE or METHOD_TYPE, a list of the exceptions that
+ this type can raise. */
+#define TYPE_RAISES_EXCEPTIONS(NODE) TYPE_NONCOPIED_PARTS (NODE)
+
+struct lang_decl_flags
+{
+#ifdef ONLY_INT_FIELDS
+ int language : 8;
+#else
+ enum languages language : 8;
+#endif
+
+ unsigned operator_attr : 1;
+ unsigned constructor_attr : 1;
+ unsigned returns_first_arg : 1;
+ unsigned preserves_first_arg : 1;
+ unsigned friend_attr : 1;
+ unsigned static_function : 1;
+ unsigned const_memfunc : 1;
+ unsigned volatile_memfunc : 1;
+
+ unsigned abstract_virtual : 1;
+ unsigned permanent_attr : 1 ;
+ unsigned constructor_for_vbase_attr : 1;
+ unsigned mutable_flag : 1;
+ unsigned is_default_implementation : 1;
+ unsigned saved_inline : 1;
+ unsigned use_template : 2;
+
+ unsigned dummy : 8;
+
+ tree access;
+ tree context;
+ tree memfunc_pointer_to;
+};
+
+struct lang_decl
+{
+ struct lang_decl_flags decl_flags;
+
+ struct template_info *template_info;
+ tree main_decl_variant;
+ struct pending_inline *pending_inline_info;
+ tree next_method;
+ tree chain;
+};
+
+/* Non-zero if NODE is a _DECL with TREE_READONLY set. */
+#define TREE_READONLY_DECL_P(NODE) \
+ (TREE_READONLY (NODE) && TREE_CODE_CLASS (TREE_CODE (NODE)) == 'd')
+
+/* For FUNCTION_DECLs: return the language in which this decl
+ was declared. */
+#define DECL_LANGUAGE(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.language)
+
+/* For FUNCTION_DECLs: nonzero means that this function is a constructor. */
+#define DECL_CONSTRUCTOR_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.constructor_attr)
+/* For FUNCTION_DECLs: nonzero means that this function is a constructor
+ for an object with virtual baseclasses. */
+#define DECL_CONSTRUCTOR_FOR_VBASE_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.constructor_for_vbase_attr)
+
+/* For FUNCTION_DECLs: nonzero means that this function is a default
+ implementation of a signature method. */
+#define IS_DEFAULT_IMPLEMENTATION(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.is_default_implementation)
+
+/* For FUNCTION_DECLs: nonzero means that the constructor
+ is known to return a non-zero `this' unchanged. */
+#define DECL_RETURNS_FIRST_ARG(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.returns_first_arg)
+
+/* Nonzero for FUNCTION_DECL means that this constructor is known to
+ not make any assignment to `this', and therefore can be trusted
+ to return it unchanged. Otherwise, we must re-assign `current_class_decl'
+ after performing base initializations. */
+#define DECL_PRESERVES_THIS(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.preserves_first_arg)
+
+/* Nonzero for _DECL means that this decl appears in (or will appear
+ in) as a member in a RECORD_TYPE or UNION_TYPE node. It is also for
+ detecting circularity in case members are multiply defined. In the
+ case of a VAR_DECL, it is also used to determine how program storage
+ should be allocated. */
+#define DECL_IN_AGGR_P(NODE) (DECL_LANG_FLAG_3(NODE))
+
+/* Nonzero for FUNCTION_DECL means that this decl is just a
+ friend declaration, and should not be added to the list of
+ member functions for this class. */
+#define DECL_FRIEND_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.friend_attr)
+
+/* Nonzero for FUNCTION_DECL means that this decl is a static
+ member function. */
+#define DECL_STATIC_FUNCTION_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.static_function)
+
+/* Nonzero for a class member means that it is shared between all objects
+ of that class. */
+#define SHARED_MEMBER_P(NODE) \
+ (TREE_CODE (NODE) == VAR_DECL || TREE_CODE (NODE) == TYPE_DECL \
+ || TREE_CODE (NODE) == CONST_DECL)
+
+/* Nonzero for FUNCTION_DECL means that this decl is a member function
+ (static or non-static). */
+#define DECL_FUNCTION_MEMBER_P(NODE) \
+ (TREE_CODE (TREE_TYPE (NODE)) == METHOD_TYPE || DECL_STATIC_FUNCTION_P (NODE))
+
+/* Nonzero for FUNCTION_DECL means that this member function
+ has `this' as const X *const. */
+#define DECL_CONST_MEMFUNC_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.const_memfunc)
+
+/* Nonzero for FUNCTION_DECL means that this member function
+ has `this' as volatile X *const. */
+#define DECL_VOLATILE_MEMFUNC_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.volatile_memfunc)
+
+/* Nonzero for _DECL means that this member object type
+ is mutable. */
+#define DECL_MUTABLE_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.mutable_flag)
+
+/* Nonzero for FUNCTION_DECL means that this member function
+ exists as part of an abstract class's interface. */
+#define DECL_ABSTRACT_VIRTUAL_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.abstract_virtual)
+
+/* Nonzero if allocated on permanent_obstack. */
+#define LANG_DECL_PERMANENT(LANGDECL) ((LANGDECL)->decl_flags.permanent_attr)
+
+/* The _TYPE context in which this _DECL appears. This field holds the
+ class where a virtual function instance is actually defined, and the
+ lexical scope of a friend function defined in a class body. */
+#define DECL_CLASS_CONTEXT(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.context)
+
+/* For a FUNCTION_DECL: the chain through which the next method
+ in the method chain is found. We now use TREE_CHAIN to
+ link into the FIELD_DECL chain. */
+#if 1
+#define DECL_CHAIN(NODE) (DECL_LANG_SPECIFIC(NODE)->chain)
+#else
+#define DECL_CHAIN(NODE) (TREE_CHAIN (NODE))
+#endif
+
+/* Next method in CLASSTYPE_METHODS list. */
+#define DECL_NEXT_METHOD(NODE) (DECL_LANG_SPECIFIC(NODE)->next_method)
+
+/* Points back to the decl which caused this lang_decl to be allocated. */
+#define DECL_MAIN_VARIANT(NODE) (DECL_LANG_SPECIFIC(NODE)->main_decl_variant)
+
+/* For a FUNCTION_DECL: if this function was declared inline inside of
+ a class declaration, this is where the text for the function is
+ squirreled away. */
+#define DECL_PENDING_INLINE_INFO(NODE) (DECL_LANG_SPECIFIC(NODE)->pending_inline_info)
+
+/* True if on the saved_inlines (see decl2.c) list. */
+#define DECL_SAVED_INLINE(DECL) \
+ (DECL_LANG_SPECIFIC(DECL)->decl_flags.saved_inline)
+
+/* For a FUNCTION_DECL: if this function was declared inside a signature
+ declaration, this is the corresponding member function pointer that was
+ created for it. */
+#define DECL_MEMFUNC_POINTER_TO(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.memfunc_pointer_to)
+
+/* For a FIELD_DECL: this points to the signature member function from
+ which this signature member function pointer was created. */
+#define DECL_MEMFUNC_POINTING_TO(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.memfunc_pointer_to)
+
+/* For a TEMPLATE_DECL: template-specific information. */
+#define DECL_TEMPLATE_INFO(NODE) (DECL_LANG_SPECIFIC(NODE)->template_info)
+
+/* Nonzero in INT_CST means that this int is negative by dint of
+ using a twos-complement negated operand. */
+#define TREE_NEGATED_INT(NODE) (TREE_LANG_FLAG_0 (NODE))
+
+/* Nonzero in any kind of _EXPR or _REF node means that it is a call
+ to a storage allocation routine. If, later, alternate storage
+ is found to hold the object, this call can be ignored. */
+#define TREE_CALLS_NEW(NODE) (TREE_LANG_FLAG_1 (NODE))
+
+/* Nonzero in any kind of _TYPE that uses multiple inheritance
+ or virtual baseclasses. */
+#define TYPE_USES_COMPLEX_INHERITANCE(NODE) (TREE_LANG_FLAG_1 (NODE))
+
+/* Nonzero in IDENTIFIER_NODE means that this name is not the name the user
+ gave; it's a DECL_NESTED_TYPENAME. Someone may want to set this on
+ mangled function names, too, but it isn't currently. */
+#define TREE_MANGLED(NODE) (TREE_LANG_FLAG_0 (NODE))
+
+#if 0 /* UNUSED */
+/* Nonzero in IDENTIFIER_NODE means that this name is overloaded, and
+ should be looked up in a non-standard way. */
+#define DECL_OVERLOADED(NODE) (DECL_LANG_FLAG_4 (NODE))
+#endif
+
+/* Nonzero if this (non-TYPE)_DECL has its virtual attribute set.
+ For a FUNCTION_DECL, this is when the function is a virtual function.
+ For a VAR_DECL, this is when the variable is a virtual function table.
+ For a FIELD_DECL, when the field is the field for the virtual function table.
+ For an IDENTIFIER_NODE, nonzero if any function with this name
+ has been declared virtual.
+
+ For a _TYPE if it uses virtual functions (or is derived from
+ one that does). */
+#define TYPE_VIRTUAL_P(NODE) (TREE_LANG_FLAG_2 (NODE))
+
+#if 0
+/* Same, but tells if this field is private in current context. */
+#define DECL_PRIVATE(NODE) (DECL_LANG_FLAG_5 (NODE))
+
+/* Same, but tells if this field is private in current context. */
+#define DECL_PROTECTED(NODE) (DECL_LANG_FLAG_6 (NODE))
+
+#define DECL_PUBLIC(NODE) (DECL_LANG_FLAG_7 (NODE))
+#endif
+
+/* This _DECL represents a compiler-generated entity. */
+#define DECL_ARTIFICIAL(NODE) (DECL_SOURCE_LINE (NODE) == 0)
+#define SET_DECL_ARTIFICIAL(NODE) (DECL_SOURCE_LINE (NODE) = 0)
+
+/* Record whether a typedef for type `int' was actually `signed int'. */
+#define C_TYPEDEF_EXPLICITLY_SIGNED(exp) DECL_LANG_FLAG_1 ((exp))
+
+/* Nonzero if the type T promotes to itself.
+ ANSI C states explicitly the list of types that promote;
+ in particular, short promotes to int even if they have the same width. */
+#define C_PROMOTING_INTEGER_TYPE_P(t) \
+ (TREE_CODE ((t)) == INTEGER_TYPE \
+ && (TYPE_MAIN_VARIANT (t) == char_type_node \
+ || TYPE_MAIN_VARIANT (t) == signed_char_type_node \
+ || TYPE_MAIN_VARIANT (t) == unsigned_char_type_node \
+ || TYPE_MAIN_VARIANT (t) == short_integer_type_node \
+ || TYPE_MAIN_VARIANT (t) == short_unsigned_type_node))
+
+#define INTEGRAL_CODE_P(CODE) \
+ (CODE == INTEGER_TYPE || CODE == ENUMERAL_TYPE || CODE == BOOLEAN_TYPE)
+#define ARITHMETIC_TYPE_P(TYPE) (INTEGRAL_TYPE_P (TYPE) || FLOAT_TYPE_P (TYPE))
+
+/* Mark which labels are explicitly declared.
+ These may be shadowed, and may be referenced from nested functions. */
+#define C_DECLARED_LABEL_FLAG(label) TREE_LANG_FLAG_1 (label)
+
+/* Record whether a type or decl was written with nonconstant size.
+ Note that TYPE_SIZE may have simplified to a constant. */
+#define C_TYPE_VARIABLE_SIZE(type) TREE_LANG_FLAG_4 (type)
+#define C_DECL_VARIABLE_SIZE(type) DECL_LANG_FLAG_8 (type)
+
+/* Nonzero for _TYPE means that the _TYPE defines
+ at least one constructor. */
+#define TYPE_HAS_CONSTRUCTOR(NODE) (TYPE_LANG_FLAG_1(NODE))
+
+/* When appearing in an INDIRECT_REF, it means that the tree structure
+ underneath is actually a call to a constructor. This is needed
+ when the constructor must initialize local storage (which can
+ be automatically destroyed), rather than allowing it to allocate
+ space from the heap.
+
+ When appearing in a SAVE_EXPR, it means that underneath
+ is a call to a constructor.
+
+ When appearing in a CONSTRUCTOR, it means that it was
+ a GNU C constructor expression.
+
+ When appearing in a FIELD_DECL, it means that this field
+ has been duly initialized in its constructor. */
+#define TREE_HAS_CONSTRUCTOR(NODE) (TREE_LANG_FLAG_4(NODE))
+
+#define EMPTY_CONSTRUCTOR_P(NODE) (TREE_CODE (NODE) == CONSTRUCTOR \
+ && CONSTRUCTOR_ELTS (NODE) == NULL_TREE)
+
+/* Indicates that a NON_LVALUE_EXPR came from a C++ reference.
+ Used to generate more helpful error message in case somebody
+ tries to take its address. */
+#define TREE_REFERENCE_EXPR(NODE) (TREE_LANG_FLAG_3(NODE))
+
+/* Nonzero for _TYPE means that the _TYPE defines a destructor. */
+#define TYPE_HAS_DESTRUCTOR(NODE) (TYPE_LANG_FLAG_2(NODE))
+
+#if 0
+/* Nonzero for _TYPE node means that creating an object of this type
+ will involve a call to a constructor. This can apply to objects
+ of ARRAY_TYPE if the type of the elements needs a constructor. */
+#define TYPE_NEEDS_CONSTRUCTING(NODE) (TYPE_LANG_FLAG_3(NODE))
+#endif
+
+/* Nonzero if there is a user-defined X::op=(x&) for this class. */
+#define TYPE_HAS_REAL_ASSIGN_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_real_assign_ref)
+#define TYPE_HAS_COMPLEX_ASSIGN_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_complex_assign_ref)
+#define TYPE_HAS_ABSTRACT_ASSIGN_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_abstract_assign_ref)
+#define TYPE_HAS_COMPLEX_INIT_REF(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.has_complex_init_ref)
+
+/* Nonzero for _TYPE node means that destroying an object of this type
+ will involve a call to a destructor. This can apply to objects
+ of ARRAY_TYPE is the type of the elements needs a destructor. */
+#define TYPE_NEEDS_DESTRUCTOR(NODE) (TYPE_LANG_FLAG_4(NODE))
+
+/* Nonzero for _TYPE node means that this type is a pointer to member
+ function type. */
+#define TYPE_PTRMEMFUNC_P(NODE) (TREE_CODE(NODE) == RECORD_TYPE && TYPE_LANG_SPECIFIC(NODE)->type_flags.ptrmemfunc_flag)
+#define TYPE_PTRMEMFUNC_FLAG(NODE) (TYPE_LANG_SPECIFIC(NODE)->type_flags.ptrmemfunc_flag)
+/* Get the POINTER_TYPE to the METHOD_TYPE associated with this
+ pointer to member function. TYPE_PTRMEMFUNC_P _must_ be true,
+ before using this macro. */
+#define TYPE_PTRMEMFUNC_FN_TYPE(NODE) (TREE_TYPE (TYPE_FIELDS (TREE_TYPE (TREE_CHAIN (TREE_CHAIN (TYPE_FIELDS (NODE)))))))
+/* These are use to manipulate the the canonical RECORD_TYPE from the
+ hashed POINTER_TYPE, and can only be used on the POINTER_TYPE. */
+#define TYPE_GET_PTRMEMFUNC_TYPE(NODE) ((tree)TYPE_LANG_SPECIFIC(NODE))
+#define TYPE_SET_PTRMEMFUNC_TYPE(NODE, VALUE) (TYPE_LANG_SPECIFIC(NODE) = ((struct lang_type *)(void*)(VALUE)))
+/* These are to get the delta2 and pfn fields from a TYPE_PTRMEMFUNC_P. */
+#define DELTA2_FROM_PTRMEMFUNC(NODE) (build_component_ref (build_component_ref ((NODE), pfn_or_delta2_identifier, 0, 0), delta2_identifier, 0, 0))
+#define PFN_FROM_PTRMEMFUNC(NODE) (build_component_ref (build_component_ref ((NODE), pfn_or_delta2_identifier, 0, 0), pfn_identifier, 0, 0))
+
+/* Nonzero for VAR_DECL and FUNCTION_DECL node means that `external' was
+ specified in its declaration. */
+#define DECL_THIS_EXTERN(NODE) (DECL_LANG_FLAG_2(NODE))
+
+/* Nonzero for SAVE_EXPR if used to initialize a PARM_DECL. */
+#define PARM_DECL_EXPR(NODE) (TREE_LANG_FLAG_2(NODE))
+
+/* Nonzero in FUNCTION_DECL means it is really an operator.
+ Just used to communicate formatting information to dbxout.c. */
+#define DECL_OPERATOR(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.operator_attr)
+
+#define ANON_UNION_P(NODE) (DECL_NAME (NODE) == 0)
+
+#define UNKNOWN_TYPE LANG_TYPE
+
+/* Define fields and accessors for nodes representing declared names. */
+
+#if 0
+/* C++: A derived class may be able to directly use the virtual
+ function table of a base class. When it does so, it may
+ still have a decl node used to access the virtual function
+ table (so that variables of this type can initialize their
+ virtual function table pointers by name). When such thievery
+ is committed, know exactly which base class's virtual function
+ table is the one being stolen. This effectively computes the
+ transitive closure. */
+#define DECL_VPARENT(NODE) ((NODE)->decl.arguments)
+#endif
+
+/* Make a slot so we can implement nested types. This slot holds
+ the IDENTIFIER_NODE that uniquely names the nested type. This
+ is for TYPE_DECLs only. */
+#define DECL_NESTED_TYPENAME(NODE) ((NODE)->decl.arguments)
+#define TYPE_NESTED_NAME(NODE) (DECL_NESTED_TYPENAME (TYPE_NAME (NODE)))
+
+#define TYPE_WAS_ANONYMOUS(NODE) (TYPE_LANG_SPECIFIC (NODE)->type_flags.was_anonymous)
+
+/* C++: all of these are overloaded! These apply only to TYPE_DECLs. */
+#define DECL_FRIENDLIST(NODE) (DECL_INITIAL (NODE))
+#if 0
+#define DECL_UNDEFINED_FRIENDS(NODE) ((NODE)->decl.result)
+#endif
+#define DECL_WAITING_FRIENDS(NODE) ((tree)(NODE)->decl.rtl)
+#define SET_DECL_WAITING_FRIENDS(NODE,VALUE) \
+ ((NODE)->decl.rtl=(struct rtx_def*)VALUE)
+
+/* The DECL_ACCESS is used to record under which context
+ special access rules apply. */
+#define DECL_ACCESS(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.access)
+
+/* C++: all of these are overloaded!
+ These apply to PARM_DECLs and VAR_DECLs. */
+#define DECL_REFERENCE_SLOT(NODE) ((tree)(NODE)->decl.arguments)
+#define SET_DECL_REFERENCE_SLOT(NODE,VAL) ((NODE)->decl.arguments=VAL)
+
+/* For local VAR_DECLs, holds index into gc-protected obstack. */
+#define DECL_GC_OFFSET(NODE) ((NODE)->decl.result)
+
+/* Accessor macros for C++ template decl nodes. */
+#define DECL_TEMPLATE_IS_CLASS(NODE) (DECL_RESULT(NODE) == NULL_TREE)
+#define DECL_TEMPLATE_PARMS(NODE) DECL_ARGUMENTS(NODE)
+/* For class templates. */
+#define DECL_TEMPLATE_MEMBERS(NODE) DECL_SIZE(NODE)
+/* For function, method, class-data templates. */
+#define DECL_TEMPLATE_RESULT(NODE) DECL_RESULT(NODE)
+#define DECL_TEMPLATE_INSTANTIATIONS(NODE) DECL_VINDEX(NODE)
+
+/* Indicates whether or not (and how) a template was expanded for this
+ FUNCTION_DECL or VAR_DECL.
+ 0=normal declaration, e.g. int min (int, int);
+ 1=implicit template instantiation
+ 2=explicit template specialization, e.g. int min<int> (int, int);
+ 3=explicit template instantiation, e.g. template int min<int> (int, int);
+ */
+#define DECL_USE_TEMPLATE(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.use_template)
+
+#define DECL_TEMPLATE_INSTANTIATION(NODE) (DECL_USE_TEMPLATE (NODE) & 1)
+#define CLASSTYPE_TEMPLATE_INSTANTIATION(NODE) \
+ (CLASSTYPE_USE_TEMPLATE (NODE) & 1)
+
+#define DECL_TEMPLATE_SPECIALIZATION(NODE) (DECL_USE_TEMPLATE (NODE) == 2)
+#define SET_DECL_TEMPLATE_SPECIALIZATION(NODE) (DECL_USE_TEMPLATE (NODE) = 2)
+#define CLASSTYPE_TEMPLATE_SPECIALIZATION(NODE) \
+ (CLASSTYPE_USE_TEMPLATE (NODE) == 2)
+#define SET_CLASSTYPE_TEMPLATE_SPECIALIZATION(NODE) \
+ (CLASSTYPE_USE_TEMPLATE (NODE) = 2)
+
+#define DECL_IMPLICIT_INSTANTIATION(NODE) (DECL_USE_TEMPLATE (NODE) == 1)
+#define SET_DECL_IMPLICIT_INSTANTIATION(NODE) (DECL_USE_TEMPLATE (NODE) = 1)
+#define CLASSTYPE_IMPLICIT_INSTANTIATION(NODE) \
+ (CLASSTYPE_USE_TEMPLATE(NODE) == 1)
+#define SET_CLASSTYPE_IMPLICIT_INSTANTIATION(NODE) \
+ (CLASSTYPE_USE_TEMPLATE(NODE) = 1)
+
+#define DECL_EXPLICIT_INSTANTIATION(NODE) (DECL_USE_TEMPLATE (NODE) == 3)
+#define SET_DECL_EXPLICIT_INSTANTIATION(NODE) (DECL_USE_TEMPLATE (NODE) = 3)
+#define CLASSTYPE_EXPLICIT_INSTANTIATION(NODE) \
+ (CLASSTYPE_USE_TEMPLATE(NODE) == 3)
+#define SET_CLASSTYPE_EXPLICIT_INSTANTIATION(NODE) \
+ (CLASSTYPE_USE_TEMPLATE(NODE) = 3)
+
+#define THUNK_DELTA(DECL) ((DECL)->decl.frame_size.i)
+
+/* ...and for unexpanded-parameterized-type nodes. */
+#define UPT_TEMPLATE(NODE) TREE_PURPOSE(TYPE_VALUES(NODE))
+#define UPT_PARMS(NODE) TREE_VALUE(TYPE_VALUES(NODE))
+
+/* An enumeration of the kind of tags that C++ accepts. */
+enum tag_types { record_type, class_type, union_type, enum_type,
+ exception_type, signature_type };
+
+/* Zero means prototype weakly, as in ANSI C (no args means nothing).
+ Each language context defines how this variable should be set. */
+extern int strict_prototype;
+extern int strict_prototypes_lang_c, strict_prototypes_lang_cplusplus;
+
+/* Non-zero means that if a label exists, and no other identifier
+ applies, use the value of the label. */
+extern int flag_labels_ok;
+
+/* Non-zero means to collect statistics which might be expensive
+ and to print them when we are done. */
+extern int flag_detailed_statistics;
+
+/* Non-zero means warn in function declared in derived class has the
+ same name as a virtual in the base class, but fails to match the
+ type signature of any virtual function in the base class. */
+extern int warn_overloaded_virtual;
+
+/* in c-common.c */
+extern void declare_function_name PROTO((void));
+extern void decl_attributes PROTO((tree, tree));
+extern void init_function_format_info PROTO((void));
+extern void record_function_format PROTO((tree, tree, int, int, int));
+extern void check_function_format PROTO((tree, tree, tree));
+/* Print an error message for invalid operands to arith operation CODE.
+ NOP_EXPR is used as a special case (see truthvalue_conversion). */
+extern void binary_op_error PROTO((enum tree_code));
+extern tree c_build_type_variant PROTO((tree, int, int));
+extern void c_expand_expr_stmt PROTO((tree));
+/* Validate the expression after `case' and apply default promotions. */
+extern tree check_case_value PROTO((tree));
+/* Concatenate a list of STRING_CST nodes into one STRING_CST. */
+extern tree combine_strings PROTO((tree));
+extern void constant_expression_warning PROTO((tree));
+extern tree convert_and_check PROTO((tree, tree));
+extern void overflow_warning PROTO((tree));
+extern void unsigned_conversion_warning PROTO((tree, tree));
+/* Read the rest of the current #-directive line. */
+extern char *get_directive_line STDIO_PROTO((FILE *));
+/* Subroutine of build_binary_op, used for comparison operations.
+ See if the operands have both been converted from subword integer types
+ and, if so, perhaps change them both back to their original type. */
+extern tree shorten_compare PROTO((tree *, tree *, tree *, enum tree_code *));
+/* Prepare expr to be an argument of a TRUTH_NOT_EXPR,
+ or validate its data type for an `if' or `while' statement or ?..: exp. */
+extern tree truthvalue_conversion PROTO((tree));
+extern tree type_for_mode PROTO((enum machine_mode, int));
+extern tree type_for_size PROTO((unsigned, int));
+
+/* in decl{2}.c */
+extern tree void_list_node;
+extern tree void_zero_node;
+extern tree default_function_type;
+extern tree vtable_entry_type;
+extern tree sigtable_entry_type;
+extern tree __t_desc_type_node, __i_desc_type_node, __m_desc_type_node;
+extern tree Type_info_type_node;
+extern tree class_star_type_node;
+extern tree this_identifier;
+extern tree pfn_identifier;
+extern tree index_identifier;
+extern tree delta_identifier;
+extern tree delta2_identifier;
+extern tree pfn_or_delta2_identifier;
+
+/* A node that is a list (length 1) of error_mark_nodes. */
+extern tree error_mark_list;
+
+extern tree ptr_type_node, const_ptr_type_node;
+extern tree class_type_node, record_type_node, union_type_node, enum_type_node;
+extern tree exception_type_node, unknown_type_node;
+extern tree opaque_type_node, signature_type_node;
+
+/* Node for "pointer to (virtual) function".
+ This may be distinct from ptr_type_node so gdb can distinuish them. */
+#define vfunc_ptr_type_node \
+ (flag_vtable_thunks ? vtable_entry_type : ptr_type_node)
+
+/* Array type `(void *)[]' */
+extern tree vtbl_type_node;
+extern tree delta_type_node;
+
+extern tree long_long_integer_type_node, long_long_unsigned_type_node;
+/* For building calls to `delete'. */
+extern tree integer_two_node, integer_three_node;
+extern tree bool_type_node, true_node, false_node;
+
+/* in except.c */
+extern tree current_exception_type;
+extern tree current_exception_decl;
+extern tree current_exception_object;
+
+/* in pt.c */
+/* PARM_VEC is a vector of template parameters, either IDENTIFIER_NODEs or
+ PARM_DECLs. BINDINGS, if non-null, is a vector of bindings for those
+ parameters. */
+struct template_info {
+ /* Vector of template parameters, either PARM_DECLs or IDENTIFIER_NODEs. */
+ tree parm_vec;
+ /* If non-null, a vector of bindings for the template parms. */
+ tree bindings;
+
+ /* Text of template, and length. */
+ char *text;
+ int length;
+ /* Where it came from. */
+ char *filename;
+ int lineno;
+
+ /* What kind of aggregate -- struct, class, or null. */
+ tree aggr;
+};
+extern int processing_template_decl, processing_template_defn;
+
+/* The template currently being instantiated, and where the instantiation
+ was triggered. */
+struct tinst_level
+{
+ tree classname;
+ int line;
+ char *file;
+ struct tinst_level *next;
+};
+
+extern struct tinst_level *current_tinst_level;
+
+/* in class.c */
+extern tree current_class_name;
+extern tree current_class_type;
+extern tree previous_class_type;
+
+extern tree current_lang_name, lang_name_cplusplus, lang_name_c;
+
+/* Points to the name of that function. May not be the DECL_NAME
+ of CURRENT_FUNCTION_DECL due to overloading */
+extern tree original_function_name;
+
+extern tree current_class_name, current_class_type, current_class_decl, C_C_D;
+extern tree current_vtable_decl;
+
+/* in init.c */
+extern tree global_base_init_list;
+extern tree current_base_init_list, current_member_init_list;
+
+extern int current_function_assigns_this;
+extern int current_function_just_assigned_this;
+extern int current_function_parms_stored;
+
+/* Here's where we control how name mangling takes place. */
+
+#define OPERATOR_ASSIGN_FORMAT "__a%s"
+#define OPERATOR_FORMAT "__%s"
+#define OPERATOR_TYPENAME_FORMAT "__op"
+#define OPERATOR_TYPENAME_P(ID_NODE) \
+ (IDENTIFIER_POINTER (ID_NODE)[0] == '_' \
+ && IDENTIFIER_POINTER (ID_NODE)[1] == '_' \
+ && IDENTIFIER_POINTER (ID_NODE)[2] == 'o' \
+ && IDENTIFIER_POINTER (ID_NODE)[3] == 'p')
+
+
+/* Cannot use '$' up front, because this confuses gdb
+ (names beginning with '$' are gdb-local identifiers).
+
+ Note that all forms in which the '$' is significant are long enough
+ for direct indexing (meaning that if we know there is a '$'
+ at a particular location, we can index into the string at
+ any other location that provides distinguishing characters). */
+
+/* Define NO_DOLLAR_IN_LABEL in your favorite tm file if your assembler
+ doesn't allow '$' in symbol names. */
+#ifndef NO_DOLLAR_IN_LABEL
+
+#define JOINER '$'
+
+#define VPTR_NAME "$v"
+#define THROW_NAME "$eh_throw"
+#define DESTRUCTOR_DECL_PREFIX "_$_"
+#define AUTO_VTABLE_NAME "__vtbl$me__"
+#define AUTO_TEMP_NAME "_$tmp_"
+#define AUTO_TEMP_FORMAT "_$tmp_%d"
+#define VTABLE_BASE "$vb"
+#define VTABLE_NAME_FORMAT (flag_vtable_thunks ? "__vt_%s" : "_vt$%s")
+#define VFIELD_BASE "$vf"
+#define VFIELD_NAME "_vptr$"
+#define VFIELD_NAME_FORMAT "_vptr$%s"
+#define VBASE_NAME "_vb$"
+#define VBASE_NAME_FORMAT "_vb$%s"
+#define STATIC_NAME_FORMAT "_%s$%s"
+#define ANON_AGGRNAME_FORMAT "$_%d"
+
+#else /* NO_DOLLAR_IN_LABEL */
+
+#ifndef NO_DOT_IN_LABEL
+
+#define JOINER '.'
+
+#define VPTR_NAME ".v"
+#define THROW_NAME ".eh_throw"
+#define DESTRUCTOR_DECL_PREFIX "_._"
+#define AUTO_VTABLE_NAME "__vtbl.me__"
+#define AUTO_TEMP_NAME "_.tmp_"
+#define AUTO_TEMP_FORMAT "_.tmp_%d"
+#define VTABLE_BASE ".vb"
+#define VTABLE_NAME_FORMAT (flag_vtable_thunks ? "__vt_%s" : "_vt.%s")
+#define VFIELD_BASE ".vf"
+#define VFIELD_NAME "_vptr."
+#define VFIELD_NAME_FORMAT "_vptr.%s"
+#define VBASE_NAME "_vb."
+#define VBASE_NAME_FORMAT "_vb.%s"
+#define STATIC_NAME_FORMAT "_%s.%s"
+
+#define ANON_AGGRNAME_FORMAT "._%d"
+
+#else /* NO_DOT_IN_LABEL */
+
+#define VPTR_NAME "__vptr"
+#define VPTR_NAME_P(ID_NODE) \
+ (!strncmp (IDENTIFIER_POINTER (ID_NODE), VPTR_NAME, sizeof (VPTR_NAME) - 1))
+#define THROW_NAME "__eh_throw"
+#define DESTRUCTOR_DECL_PREFIX "__destr_"
+#define DESTRUCTOR_NAME_P(ID_NODE) \
+ (!strncmp (IDENTIFIER_POINTER (ID_NODE), DESTRUCTOR_DECL_PREFIX, \
+ sizeof (DESTRUCTOR_DECL_PREFIX) - 1))
+#define IN_CHARGE_NAME "__in_chrg"
+#define AUTO_VTABLE_NAME "__vtbl_me__"
+#define AUTO_TEMP_NAME "__tmp_"
+#define TEMP_NAME_P(ID_NODE) \
+ (!strncmp (IDENTIFIER_POINTER (ID_NODE), AUTO_TEMP_NAME, \
+ sizeof (AUTO_TEMP_NAME) - 1))
+#define AUTO_TEMP_FORMAT "__tmp_%d"
+#define VTABLE_BASE "__vtb"
+#define VTABLE_NAME "__vt_"
+#define VTABLE_NAME_FORMAT (flag_vtable_thunks ? "__vt_%s" : "_vt_%s")
+#define VTABLE_NAME_P(ID_NODE) \
+ (!strncmp (IDENTIFIER_POINTER (ID_NODE), VTABLE_NAME, \
+ sizeof (VTABLE_NAME) - 1))
+#define VFIELD_BASE "__vfb"
+#define VFIELD_NAME "__vptr_"
+#define VFIELD_NAME_P(ID_NODE) \
+ (!strncmp (IDENTIFIER_POINTER (ID_NODE), VFIELD_NAME, \
+ sizeof (VFIELD_NAME) - 1))
+#define VFIELD_NAME_FORMAT "_vptr_%s"
+#define VBASE_NAME "__vb_"
+#define VBASE_NAME_P(ID_NODE) \
+ (!strncmp (IDENTIFIER_POINTER (ID_NODE), VBASE_NAME, \
+ sizeof (VBASE_NAME) - 1))
+#define VBASE_NAME_FORMAT "__vb_%s"
+#define STATIC_NAME_FORMAT "__static_%s_%s"
+
+#define ANON_AGGRNAME_PREFIX "__anon_"
+#define ANON_AGGRNAME_P(ID_NODE) \
+ (!strncmp (IDENTIFIER_POINTER (ID_NODE), ANON_AGGRNAME_PREFIX, \
+ sizeof (ANON_AGGRNAME_PREFIX) - 1))
+#define ANON_AGGRNAME_FORMAT "__anon_%d"
+#define ANON_PARMNAME_FORMAT "__%d"
+#define ANON_PARMNAME_P(ID_NODE) (IDENTIFIER_POINTER (ID_NODE)[0] == '_' \
+ && IDENTIFIER_POINTER (ID_NODE)[1] == '_' \
+ && IDENTIFIER_POINTER (ID_NODE)[2] <= '9')
+
+#endif /* NO_DOT_IN_LABEL */
+#endif /* NO_DOLLAR_IN_LABEL */
+
+#define THIS_NAME "this"
+#define DESTRUCTOR_NAME_FORMAT "~%s"
+#define FILE_FUNCTION_PREFIX_LEN 9
+
+#define IN_CHARGE_NAME "__in_chrg"
+
+#define VTBL_PTR_TYPE "__vtbl_ptr_type"
+#define VTABLE_DELTA_NAME "__delta"
+#define VTABLE_INDEX_NAME "__index"
+#define VTABLE_PFN_NAME "__pfn"
+#define VTABLE_DELTA2_NAME "__delta2"
+
+#define SIGNATURE_FIELD_NAME "__s_"
+#define SIGNATURE_FIELD_NAME_FORMAT "__s_%s"
+#define SIGNATURE_OPTR_NAME "__optr"
+#define SIGNATURE_SPTR_NAME "__sptr"
+#define SIGNATURE_VPTR_NAME "__vptr"
+#define SIGNATURE_POINTER_NAME "__sp_"
+#define SIGNATURE_POINTER_NAME_FORMAT "__%s%ssp_%s"
+#define SIGNATURE_REFERENCE_NAME "__sr_"
+#define SIGNATURE_REFERENCE_NAME_FORMAT "__%s%ssr_%s"
+
+#define SIGTABLE_PTR_TYPE "__sigtbl_ptr_type"
+#define SIGTABLE_NAME_FORMAT "__st_%s_%s"
+#define SIGTABLE_NAME_FORMAT_LONG "__st_%s_%s_%d"
+#define SIGTABLE_CODE_NAME "__code"
+#define SIGTABLE_OFFSET_NAME "__offset"
+#define SIGTABLE_PFN_NAME "__pfn"
+#define EXCEPTION_CLEANUP_NAME "exception cleanup"
+
+#define THIS_NAME_P(ID_NODE) (strcmp(IDENTIFIER_POINTER (ID_NODE), "this") == 0)
+
+#if !defined(NO_DOLLAR_IN_LABEL) || !defined(NO_DOT_IN_LABEL)
+
+#define VPTR_NAME_P(ID_NODE) (IDENTIFIER_POINTER (ID_NODE)[0] == JOINER \
+ && IDENTIFIER_POINTER (ID_NODE)[1] == 'v')
+#define DESTRUCTOR_NAME_P(ID_NODE) (IDENTIFIER_POINTER (ID_NODE)[1] == JOINER \
+ && IDENTIFIER_POINTER (ID_NODE)[2] == '_')
+
+#define VTABLE_NAME_P(ID_NODE) (IDENTIFIER_POINTER (ID_NODE)[1] == 'v' \
+ && IDENTIFIER_POINTER (ID_NODE)[2] == 't' \
+ && IDENTIFIER_POINTER (ID_NODE)[3] == JOINER)
+
+#define VBASE_NAME_P(ID_NODE) (IDENTIFIER_POINTER (ID_NODE)[1] == 'v' \
+ && IDENTIFIER_POINTER (ID_NODE)[2] == 'b' \
+ && IDENTIFIER_POINTER (ID_NODE)[3] == JOINER)
+
+#define TEMP_NAME_P(ID_NODE) (!strncmp (IDENTIFIER_POINTER (ID_NODE), AUTO_TEMP_NAME, sizeof (AUTO_TEMP_NAME)-1))
+#define VFIELD_NAME_P(ID_NODE) (!strncmp (IDENTIFIER_POINTER (ID_NODE), VFIELD_NAME, sizeof(VFIELD_NAME)-1))
+
+/* For anonymous aggregate types, we need some sort of name to
+ hold on to. In practice, this should not appear, but it should
+ not be harmful if it does. */
+#define ANON_AGGRNAME_P(ID_NODE) (IDENTIFIER_POINTER (ID_NODE)[0] == JOINER \
+ && IDENTIFIER_POINTER (ID_NODE)[1] == '_')
+#define ANON_PARMNAME_FORMAT "_%d"
+#define ANON_PARMNAME_P(ID_NODE) (IDENTIFIER_POINTER (ID_NODE)[0] == '_' \
+ && IDENTIFIER_POINTER (ID_NODE)[1] <= '9')
+#endif /* !defined(NO_DOLLAR_IN_LABEL) || !defined(NO_DOT_IN_LABEL) */
+
+/* Define the sets of attributes that member functions and baseclasses
+ can have. These are sensible combinations of {public,private,protected}
+ cross {virtual,non-virtual}. */
+
+enum access_type {
+ access_default,
+ access_public,
+ access_protected,
+ access_private,
+ access_default_virtual,
+ access_public_virtual,
+ access_private_virtual
+};
+
+/* in lex.c */
+extern tree current_unit_name, current_unit_language;
+
+/* Things for handling inline functions. */
+
+struct pending_inline
+{
+ struct pending_inline *next; /* pointer to next in chain */
+ int lineno; /* line number we got the text from */
+ char *filename; /* name of file we were processing */
+ tree fndecl; /* FUNCTION_DECL that brought us here */
+ int token; /* token we were scanning */
+ int token_value; /* value of token we were scanning (YYSTYPE) */
+
+ char *buf; /* pointer to character stream */
+ int len; /* length of stream */
+ tree parm_vec, bindings; /* in case this is derived from a template */
+ unsigned int can_free : 1; /* free this after we're done with it? */
+ unsigned int deja_vu : 1; /* set iff we don't want to see it again. */
+ unsigned int interface : 2; /* 0=interface 1=unknown 2=implementation */
+};
+
+/* in method.c */
+extern struct pending_inline *pending_inlines;
+
+/* 1 for -fall-virtual: make every member function (except
+ constructors) lay down in the virtual function table.
+ Calls can then either go through the virtual function table or not,
+ depending on whether we know what function will actually be called. */
+
+extern int flag_all_virtual;
+
+/* Positive values means that we cannot make optimizing assumptions about
+ `this'. Negative values means we know `this' to be of static type. */
+
+extern int flag_this_is_variable;
+
+/* Controls whether enums and ints freely convert.
+ 1 means with complete freedom.
+ 0 means enums can convert to ints, but not vice-versa. */
+
+extern int flag_int_enum_equivalence;
+
+/* Nonzero means layout structures so that we can do garbage collection. */
+
+extern int flag_gc;
+
+/* Nonzero means generate 'dossiers' that give run-time type information. */
+
+extern int flag_dossier;
+
+/* Nonzero means do emit exported implementations of functions even if
+ they can be inlined. */
+
+extern int flag_implement_inlines;
+
+/* Nonzero means templates obey #pragma interface and implementation. */
+
+extern int flag_external_templates;
+
+/* Nonzero means templates are emitted where they are instantiated. */
+
+extern int flag_alt_external_templates;
+
+/* Nonzero means implicit template instantatiations are emitted. */
+
+extern int flag_implicit_templates;
+
+/* Current end of entries in the gc obstack for stack pointer variables. */
+
+extern int current_function_obstack_index;
+
+/* Flag saying whether we have used the obstack in this function or not. */
+
+extern int current_function_obstack_usage;
+
+enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, OP_FLAG, TYPENAME_FLAG };
+
+extern tree current_class_decl, C_C_D; /* PARM_DECL: the class instance variable */
+
+/* The following two can be derived from the previous one */
+extern tree current_class_name; /* IDENTIFIER_NODE: name of current class */
+extern tree current_class_type; /* _TYPE: the type of the current class */
+
+/* Some macros for char-based bitfields. */
+#define B_SET(a,x) (a[x>>3] |= (1 << (x&7)))
+#define B_CLR(a,x) (a[x>>3] &= ~(1 << (x&7)))
+#define B_TST(a,x) (a[x>>3] & (1 << (x&7)))
+
+/* These are uses as bits in flags passed to build_method_call
+ to control its error reporting behavior.
+
+ LOOKUP_PROTECT means flag access violations.
+ LOOKUP_COMPLAIN mean complain if no suitable member function
+ matching the arguments is found.
+ LOOKUP_NORMAL is just a combination of these two.
+ LOOKUP_AGGR requires the instance to be of aggregate type.
+ LOOKUP_NONVIRTUAL means make a direct call to the member function found
+ LOOKUP_GLOBAL means search through the space of overloaded functions,
+ as well as the space of member functions.
+ LOOKUP_HAS_IN_CHARGE means that the "in charge" variable is already
+ in the parameter list.
+ LOOKUP_NO_CONVERSION means that user-defined conversions are not
+ permitted. Built-in conversions are permitted.
+ LOOKUP_DESTRUCTOR means explicit call to destructor. */
+
+#define LOOKUP_PROTECT (1)
+#define LOOKUP_COMPLAIN (2)
+#define LOOKUP_NORMAL (3)
+#define LOOKUP_AGGR (4)
+#define LOOKUP_NONVIRTUAL (8)
+#define LOOKUP_GLOBAL (16)
+#define LOOKUP_HAS_IN_CHARGE (32)
+#define LOOKUP_SPECULATIVELY (64)
+/* 128 & 256 are free */
+#define LOOKUP_NO_CONVERSION (512)
+#define LOOKUP_DESTRUCTOR (512)
+
+/* These flags are used by the conversion code.
+ CONV_IMPLICIT : Perform implicit conversions (standard and user-defined).
+ CONV_STATIC : Perform the explicit conversions for static_cast.
+ CONV_CONST : Perform the explicit conversions for const_cast.
+ CONV_REINTERPRET: Perform the explicit conversions for reinterpret_cast.
+ CONV_PRIVATE : Perform upcasts to private bases. */
+
+#define CONV_IMPLICIT 1
+#define CONV_STATIC 2
+#define CONV_CONST 4
+#define CONV_REINTERPRET 8
+#define CONV_PRIVATE 16
+#define CONV_STATIC_CAST (CONV_IMPLICIT | CONV_STATIC)
+#define CONV_OLD_CONVERT (CONV_IMPLICIT | CONV_STATIC | CONV_CONST \
+ | CONV_REINTERPRET)
+#define CONV_C_CAST (CONV_IMPLICIT | CONV_STATIC | CONV_CONST \
+ | CONV_REINTERPRET | CONV_PRIVATE)
+
+/* Anatomy of a DECL_FRIENDLIST (which is a TREE_LIST):
+ purpose = friend name (IDENTIFIER_NODE);
+ value = TREE_LIST of FUNCTION_DECLS;
+ chain, type = EMPTY; */
+#define FRIEND_NAME(LIST) (TREE_PURPOSE (LIST))
+#define FRIEND_DECLS(LIST) (TREE_VALUE (LIST))
+
+/* These macros are for accessing the fields of TEMPLATE...PARM nodes. */
+#define TEMPLATE_TYPE_TPARMLIST(NODE) TREE_PURPOSE (TYPE_FIELDS (NODE))
+#define TEMPLATE_TYPE_IDX(NODE) TREE_INT_CST_LOW (TREE_VALUE (TYPE_FIELDS (NODE)))
+#define TEMPLATE_TYPE_SET_INFO(NODE,P,I) \
+ (TYPE_FIELDS (NODE) = build_tree_list (P, build_int_2 (I, 0)))
+#define TEMPLATE_CONST_TPARMLIST(NODE) (*(tree*)&TREE_INT_CST_LOW(NODE))
+#define TEMPLATE_CONST_IDX(NODE) (TREE_INT_CST_HIGH(NODE))
+#define TEMPLATE_CONST_SET_INFO(NODE,P,I) \
+ (TEMPLATE_CONST_TPARMLIST (NODE) = saved_parmlist, \
+ TEMPLATE_CONST_IDX (NODE) = I)
+
+/* in lex.c */
+/* Indexed by TREE_CODE, these tables give C-looking names to
+ operators represented by TREE_CODES. For example,
+ opname_tab[(int) MINUS_EXPR] == "-". */
+extern char **opname_tab, **assignop_tab;
+
+/* in c-common.c */
+extern tree convert_and_check PROTO((tree, tree));
+extern void overflow_warning PROTO((tree));
+extern void unsigned_conversion_warning PROTO((tree, tree));
+
+/* in call.c */
+extern struct candidate *ansi_c_bullshit;
+
+extern int rank_for_overload PROTO((struct candidate *, struct candidate *));
+extern void compute_conversion_costs PROTO((tree, tree, struct candidate *, int));
+extern int get_arglist_len_in_bytes PROTO((tree));
+extern tree build_vfield_ref PROTO((tree, tree));
+extern tree find_scoped_type PROTO((tree, tree, tree));
+extern tree resolve_scope_to_name PROTO((tree, tree));
+extern tree build_scoped_method_call PROTO((tree, tree, tree, tree));
+extern tree build_method_call PROTO((tree, tree, tree, tree, int));
+extern tree build_overload_call_real PROTO((tree, tree, int, struct candidate *, int));
+extern tree build_overload_call PROTO((tree, tree, int, struct candidate *));
+extern tree build_overload_call_maybe PROTO((tree, tree, int, struct candidate *));
+
+/* in class.c */
+extern tree build_vbase_pointer PROTO((tree, tree));
+extern tree build_vbase_path PROTO((enum tree_code, tree, tree, tree, int));
+extern tree build_vtable_entry PROTO((tree, tree));
+extern tree build_vfn_ref PROTO((tree *, tree, tree));
+extern void add_method PROTO((tree, tree *, tree));
+extern tree get_vfield_offset PROTO((tree));
+extern void duplicate_tag_error PROTO((tree));
+extern tree finish_struct PROTO((tree, tree, int));
+extern int resolves_to_fixed_type_p PROTO((tree, int *));
+extern void init_class_processing PROTO((void));
+extern void pushclass PROTO((tree, int));
+extern void popclass PROTO((int));
+extern void push_nested_class PROTO((tree, int));
+extern void pop_nested_class PROTO((int));
+extern void push_lang_context PROTO((tree));
+extern void pop_lang_context PROTO((void));
+extern int root_lang_context_p PROTO((void));
+extern tree instantiate_type PROTO((tree, tree, int));
+extern void print_class_statistics PROTO((void));
+extern void maybe_push_cache_obstack PROTO((void));
+
+/* in cvt.c */
+extern tree convert_to_reference PROTO((tree, tree, int, int, tree));
+extern tree convert_from_reference PROTO((tree));
+extern tree convert_to_aggr PROTO((tree, tree, char **, int));
+extern tree convert_pointer_to PROTO((tree, tree));
+extern tree convert_pointer_to_real PROTO((tree, tree));
+extern tree convert_pointer_to_vbase PROTO((tree, tree));
+extern tree convert PROTO((tree, tree));
+extern tree convert_force PROTO((tree, tree));
+extern tree build_type_conversion PROTO((enum tree_code, tree, tree, int));
+extern int build_default_binary_type_conversion PROTO((enum tree_code, tree *, tree *));
+extern int build_default_unary_type_conversion PROTO((enum tree_code, tree *));
+extern tree type_promotes_to PROTO((tree));
+
+/* decl.c */
+extern int global_bindings_p PROTO((void));
+extern void keep_next_level PROTO((void));
+extern int kept_level_p PROTO((void));
+extern void declare_parm_level PROTO((void));
+extern void declare_implicit_exception PROTO((void));
+extern int have_exceptions_p PROTO((void));
+extern void declare_uninstantiated_type_level PROTO((void));
+extern int uninstantiated_type_level_p PROTO((void));
+extern void declare_pseudo_global_level PROTO((void));
+extern int pseudo_global_level_p PROTO((void));
+extern void pushlevel PROTO((int));
+extern void pushlevel_temporary PROTO((int));
+extern tree poplevel PROTO((int, int, int));
+extern void delete_block PROTO((tree));
+extern void insert_block PROTO((tree));
+extern void add_block_current_level PROTO((tree));
+extern void set_block PROTO((tree));
+extern void pushlevel_class PROTO((void));
+extern tree poplevel_class PROTO((int));
+/* skip print_other_binding_stack and print_binding_level */
+extern void print_binding_stack PROTO((void));
+extern void push_to_top_level PROTO((void));
+extern void pop_from_top_level PROTO((void));
+extern void set_identifier_type_value PROTO((tree, tree));
+extern void pop_everything PROTO((void));
+extern tree make_type_decl PROTO((tree, tree));
+extern void pushtag PROTO((tree, tree, int));
+extern tree make_anon_name PROTO((void));
+extern void clear_anon_tags PROTO((void));
+extern tree pushdecl PROTO((tree));
+extern tree pushdecl_top_level PROTO((tree));
+extern void push_class_level_binding PROTO((tree, tree));
+extern void push_overloaded_decl_top_level PROTO((tree, int));
+extern tree pushdecl_class_level PROTO((tree));
+extern int overloaded_globals_p PROTO((tree));
+extern tree push_overloaded_decl PROTO((tree, int));
+extern tree implicitly_declare PROTO((tree));
+extern tree lookup_label PROTO((tree));
+extern tree shadow_label PROTO((tree));
+extern tree define_label PROTO((char *, int, tree));
+extern void define_case_label PROTO((tree));
+extern tree getdecls PROTO((void));
+extern tree gettags PROTO((void));
+extern void set_current_level_tags_transparency PROTO((int));
+extern tree typedecl_for_tag PROTO((tree));
+extern tree lookup_name PROTO((tree, int));
+extern tree lookup_name_current_level PROTO((tree));
+extern void init_decl_processing PROTO((void));
+/* skipped define_function */
+extern void shadow_tag PROTO((tree));
+extern int grok_ctor_properties PROTO((tree, tree));
+extern tree groktypename PROTO((tree));
+extern tree start_decl PROTO((tree, tree, int, tree));
+extern void finish_decl PROTO((tree, tree, tree, int));
+extern void expand_static_init PROTO((tree, tree));
+extern int complete_array_type PROTO((tree, tree, int));
+extern tree build_ptrmemfunc_type PROTO((tree));
+extern tree grokdeclarator (); /* PROTO((tree, tree, enum decl_context, int, tree)); */
+extern int parmlist_is_exprlist PROTO((tree));
+extern tree xref_defn_tag PROTO((tree, tree, tree));
+extern tree xref_tag PROTO((tree, tree, tree, int));
+extern tree start_enum PROTO((tree));
+extern tree finish_enum PROTO((tree, tree));
+extern tree build_enumerator PROTO((tree, tree));
+extern tree grok_enum_decls PROTO((tree, tree));
+extern int start_function PROTO((tree, tree, tree, int));
+extern void store_parm_decls PROTO((void));
+extern void store_return_init PROTO((tree, tree));
+extern void finish_function PROTO((int, int));
+extern tree start_method PROTO((tree, tree, tree));
+extern tree finish_method PROTO((tree));
+extern void hack_incomplete_structures PROTO((tree));
+extern tree maybe_build_cleanup PROTO((tree));
+extern void cplus_expand_expr_stmt PROTO((tree));
+extern void finish_stmt PROTO((void));
+extern void pop_implicit_try_blocks PROTO((tree));
+extern void push_exception_cleanup PROTO((tree));
+extern void revert_static_member_fn PROTO((tree *, tree *, tree *));
+
+/* in decl2.c */
+extern int lang_decode_option PROTO((char *));
+extern tree grok_method_quals PROTO((tree, tree, tree));
+extern void grokclassfn PROTO((tree, tree, tree, enum overload_flags, tree));
+extern tree grok_alignof PROTO((tree));
+extern tree grok_array_decl PROTO((tree, tree));
+extern tree delete_sanity PROTO((tree, tree, int, int));
+extern void check_classfn PROTO((tree, tree, tree));
+extern tree grokfield PROTO((tree, tree, tree, tree, tree));
+extern tree grokbitfield PROTO((tree, tree, tree));
+extern tree groktypefield PROTO((tree, tree));
+extern tree grokoptypename PROTO((tree, tree));
+extern tree build_push_scope PROTO((tree, tree));
+extern tree constructor_name_full PROTO((tree));
+extern tree constructor_name PROTO((tree));
+extern void setup_vtbl_ptr PROTO((void));
+extern void mark_inline_for_output PROTO((tree));
+extern void clear_temp_name PROTO((void));
+extern tree get_temp_name PROTO((tree, int));
+extern tree get_temp_regvar PROTO((tree, tree));
+extern void finish_anon_union PROTO((tree));
+extern tree finish_table PROTO((tree, tree, tree, int));
+extern void finish_builtin_type PROTO((tree, char *, tree *, int, tree));
+extern tree coerce_new_type PROTO((tree));
+extern tree coerce_delete_type PROTO((tree));
+extern void walk_vtables PROTO((void (*)(), void (*)()));
+extern void walk_sigtables PROTO((void (*)(), void (*)()));
+extern void finish_file PROTO((void));
+extern void warn_if_unknown_interface PROTO((void));
+extern tree grok_x_components PROTO((tree, tree));
+extern tree reparse_absdcl_as_expr PROTO((tree, tree));
+extern tree reparse_absdcl_as_casts PROTO((tree, tree));
+extern tree reparse_decl_as_expr PROTO((tree, tree));
+extern tree finish_decl_parsing PROTO((tree));
+extern tree lookup_name_nonclass PROTO((tree));
+extern tree check_cp_case_value PROTO((tree));
+
+/* in edsel.c */
+
+/* in except.c */
+
+extern void start_protect PROTO((void));
+extern void end_protect PROTO((tree));
+extern void expand_exception_blocks PROTO((void));
+extern void expand_start_try_stmts PROTO((void));
+extern void expand_end_try_stmts PROTO((void));
+extern void expand_start_all_catch PROTO((void));
+extern void expand_end_all_catch PROTO((void));
+extern void start_catch_block PROTO((tree, tree));
+extern void end_catch_block PROTO((void));
+extern void expand_throw PROTO((tree));
+extern void build_exception_table PROTO((void));
+extern tree build_throw PROTO((tree));
+extern void init_exception_processing PROTO((void));
+
+/* in expr.c */
+/* skip cplus_expand_expr */
+extern void init_cplus_expand PROTO((void));
+extern void fixup_result_decl PROTO((tree, struct rtx_def *));
+extern int decl_in_memory_p PROTO((tree));
+
+/* in gc.c */
+extern int type_needs_gc_entry PROTO((tree));
+extern int value_safe_from_gc PROTO((tree, tree));
+extern void build_static_gc_entry PROTO((tree, tree));
+extern tree protect_value_from_gc PROTO((tree, tree));
+extern tree build_headof PROTO((tree));
+extern tree build_classof PROTO((tree));
+extern tree build_t_desc PROTO((tree, int));
+extern tree build_i_desc PROTO((tree));
+extern tree build_m_desc PROTO((tree));
+extern void expand_gc_prologue_and_epilogue PROTO((void));
+extern void lang_expand_end_bindings PROTO((struct rtx_def *, struct rtx_def *));
+extern void init_gc_processing PROTO((void));
+extern tree build_typeid PROTO((tree));
+extern tree get_typeid PROTO((tree));
+extern tree build_dynamic_cast PROTO((tree, tree));
+
+/* in init.c */
+extern void emit_base_init PROTO((tree, int));
+extern void check_base_init PROTO((tree));
+extern void expand_direct_vtbls_init PROTO((tree, tree, int, int, tree));
+extern void do_member_init PROTO((tree, tree, tree));
+extern void expand_member_init PROTO((tree, tree, tree));
+extern void expand_aggr_init PROTO((tree, tree, int));
+extern int is_aggr_typedef PROTO((tree, int));
+extern tree get_aggr_from_typedef PROTO((tree, int));
+extern tree get_type_value PROTO((tree));
+extern tree build_member_call PROTO((tree, tree, tree));
+extern tree build_offset_ref PROTO((tree, tree));
+extern tree get_member_function PROTO((tree *, tree, tree));
+extern tree get_member_function_from_ptrfunc PROTO((tree *, tree, tree));
+extern tree resolve_offset_ref PROTO((tree));
+extern tree decl_constant_value PROTO((tree));
+extern int is_friend_type PROTO((tree, tree));
+extern int is_friend PROTO((tree, tree));
+extern void make_friend_class PROTO((tree, tree));
+extern tree do_friend PROTO((tree, tree, tree, tree, enum overload_flags, tree));
+extern void embrace_waiting_friends PROTO((tree));
+extern tree build_builtin_call PROTO((tree, tree, tree));
+extern tree build_new PROTO((tree, tree, tree, int));
+extern tree expand_vec_init PROTO((tree, tree, tree, tree, int));
+extern tree build_x_delete PROTO((tree, tree, int, tree));
+extern tree build_delete PROTO((tree, tree, tree, int, int));
+extern tree build_vbase_delete PROTO((tree, tree));
+extern tree build_vec_delete PROTO((tree, tree, tree, tree, tree, int));
+
+/* in input.c */
+
+/* in lex.c */
+extern tree make_pointer_declarator PROTO((tree, tree));
+extern tree make_reference_declarator PROTO((tree, tree));
+extern char *operator_name_string PROTO((tree));
+extern void lang_init PROTO((void));
+extern void lang_finish PROTO((void));
+extern void init_filename_times PROTO((void));
+extern void reinit_lang_specific PROTO((void));
+extern void init_lex PROTO((void));
+extern void reinit_parse_for_function PROTO((void));
+extern int *init_parse PROTO((void));
+extern void print_parse_statistics PROTO((void));
+extern void extract_interface_info PROTO((void));
+extern void set_vardecl_interface_info PROTO((tree, tree));
+extern void do_pending_inlines PROTO((void));
+extern void process_next_inline PROTO((tree));
+/* skip restore_pending_input */
+extern void yyungetc PROTO((int, int));
+extern void reinit_parse_for_method PROTO((int, tree));
+#if 0
+extern void reinit_parse_for_block PROTO((int, struct obstack *, int));
+#endif
+extern tree cons_up_default_function PROTO((tree, tree, tree, int));
+extern void check_for_missing_semicolon PROTO((tree));
+extern void note_got_semicolon PROTO((tree));
+extern void note_list_got_semicolon PROTO((tree));
+extern int check_newline PROTO((void));
+extern void dont_see_typename PROTO((void));
+extern int identifier_type PROTO((tree));
+extern void see_typename PROTO((void));
+extern tree do_identifier PROTO((tree));
+extern tree identifier_typedecl_value PROTO((tree));
+extern int real_yylex PROTO((void));
+extern tree build_lang_decl PROTO((enum tree_code, tree, tree));
+extern tree build_lang_field_decl PROTO((enum tree_code, tree, tree));
+extern void copy_lang_decl PROTO((tree));
+extern tree make_lang_type PROTO((enum tree_code));
+extern void copy_decl_lang_specific PROTO((tree));
+extern void dump_time_statistics PROTO((void));
+/* extern void compiler_error PROTO((char *, HOST_WIDE_INT, HOST_WIDE_INT)); */
+extern void compiler_error_with_decl PROTO((tree, char *));
+extern void yyerror PROTO((char *));
+
+/* in errfn.c */
+extern void cp_error ();
+extern void cp_error_at ();
+extern void cp_warning ();
+extern void cp_warning_at ();
+extern void cp_pedwarn ();
+extern void cp_pedwarn_at ();
+extern void cp_compiler_error ();
+extern void cp_sprintf ();
+
+/* in error.c */
+extern void init_error PROTO((void));
+extern char *fndecl_as_string PROTO((tree, tree, int));
+extern char *type_as_string PROTO((tree, int));
+extern char *args_as_string PROTO((tree, int));
+extern char *decl_as_string PROTO((tree, int));
+extern char *expr_as_string PROTO((tree, int));
+extern char *code_as_string PROTO((enum tree_code, int));
+extern char *language_as_string PROTO((enum languages, int));
+extern char *parm_as_string PROTO((int, int));
+extern char *op_as_string PROTO((enum tree_code, int));
+
+/* in method.c */
+extern void init_method PROTO((void));
+extern tree make_anon_parm_name PROTO((void));
+extern void clear_anon_parm_name PROTO((void));
+extern void do_inline_function_hair PROTO((tree, tree));
+/* skip report_type_mismatch */
+extern char *build_overload_name PROTO((tree, int, int));
+extern tree cplus_exception_name PROTO((tree));
+extern tree build_decl_overload PROTO((tree, tree, int));
+extern tree build_typename_overload PROTO((tree));
+extern tree build_t_desc_overload PROTO((tree));
+extern void declare_overloaded PROTO((tree));
+#ifdef NO_AUTO_OVERLOAD
+extern int is_overloaded PROTO((tree));
+#endif
+extern tree build_opfncall PROTO((enum tree_code, int, tree, tree, tree));
+extern tree hack_identifier PROTO((tree, tree, int));
+extern tree build_component_type_expr PROTO((tree, tree, tree, int));
+
+/* in pt.c */
+extern void begin_template_parm_list PROTO((void));
+extern tree process_template_parm PROTO((tree, tree));
+extern tree end_template_parm_list PROTO((tree));
+extern void end_template_decl PROTO((tree, tree, tree, int));
+extern tree lookup_template_class PROTO((tree, tree, tree));
+extern void push_template_decls PROTO((tree, tree, int));
+extern void pop_template_decls PROTO((tree, tree, int));
+extern int uses_template_parms PROTO((tree));
+extern void instantiate_member_templates PROTO((tree));
+extern tree instantiate_class_template PROTO((tree, int));
+extern tree instantiate_template PROTO((tree, tree *));
+extern void undo_template_name_overload PROTO((tree, int));
+extern void overload_template_name PROTO((tree, int));
+extern void end_template_instantiation PROTO((tree));
+extern void reinit_parse_for_template PROTO((int, tree, tree));
+extern int type_unification PROTO((tree, tree *, tree, tree, int *, int));
+extern int do_pending_expansions PROTO((void));
+extern void do_pending_templates PROTO((void));
+struct tinst_level *tinst_for_decl PROTO((void));
+extern void do_function_instantiation PROTO((tree, tree, tree));
+extern void do_type_instantiation PROTO((tree, tree));
+extern tree create_nested_upt PROTO((tree, tree));
+
+/* in search.c */
+extern tree make_memoized_table_entry PROTO((tree, tree, int));
+extern void push_memoized_context PROTO((tree, int));
+extern void pop_memoized_context PROTO((int));
+extern tree get_binfo PROTO((tree, tree, int));
+extern int get_base_distance PROTO((tree, tree, int, tree *));
+extern enum access_type compute_access PROTO((tree, tree));
+extern tree lookup_field PROTO((tree, tree, int, int));
+extern tree lookup_nested_field PROTO((tree, int));
+extern tree lookup_fnfields PROTO((tree, tree, int));
+extern tree lookup_nested_tag PROTO((tree, tree));
+extern HOST_WIDE_INT breadth_first_search PROTO((tree, int (*)(), int (*)()));
+extern int tree_needs_constructor_p PROTO((tree, int));
+extern int tree_has_any_destructor_p PROTO((tree, int));
+extern tree get_matching_virtual PROTO((tree, tree, int));
+extern tree get_abstract_virtuals PROTO((tree));
+extern tree get_baselinks PROTO((tree, tree, tree));
+extern tree next_baselink PROTO((tree));
+extern tree init_vbase_pointers PROTO((tree, tree));
+extern void expand_indirect_vtbls_init PROTO((tree, tree, tree, int));
+extern void clear_search_slots PROTO((tree));
+extern tree get_vbase_types PROTO((tree));
+extern void build_mi_matrix PROTO((tree));
+extern void free_mi_matrix PROTO((void));
+extern void build_mi_virtuals PROTO((int, int));
+extern void add_mi_virtuals PROTO((int, tree));
+extern void report_ambiguous_mi_virtuals PROTO((int, tree));
+extern void note_debug_info_needed PROTO((tree));
+extern void push_class_decls PROTO((tree));
+extern void pop_class_decls PROTO((tree));
+extern void unuse_fields PROTO((tree));
+extern void unmark_finished_struct PROTO((tree));
+extern void print_search_statistics PROTO((void));
+extern void init_search_processing PROTO((void));
+extern void reinit_search_statistics PROTO((void));
+extern tree current_scope PROTO((void));
+
+/* in sig.c */
+extern tree build_signature_pointer_type PROTO((tree, int, int));
+extern tree build_signature_reference_type PROTO((tree, int, int));
+extern tree build_signature_pointer_constructor PROTO((tree, tree));
+extern tree build_signature_method_call PROTO((tree, tree, tree, tree));
+extern tree build_optr_ref PROTO((tree));
+extern tree build_sptr_ref PROTO((tree));
+extern tree build_vptr_ref PROTO((tree));
+
+/* in spew.c */
+extern void init_spew PROTO((void));
+extern int yylex PROTO((void));
+extern tree arbitrate_lookup PROTO((tree, tree, tree));
+
+/* in tree.c */
+extern int lvalue_p PROTO((tree));
+extern int lvalue_or_else PROTO((tree, char *));
+extern tree build_cplus_new PROTO((tree, tree, int));
+extern tree break_out_cleanups PROTO((tree));
+extern tree break_out_calls PROTO((tree));
+extern tree build_cplus_method_type PROTO((tree, tree, tree));
+extern tree build_cplus_staticfn_type PROTO((tree, tree, tree));
+extern tree build_cplus_array_type PROTO((tree, tree));
+extern void propagate_binfo_offsets PROTO((tree, tree));
+extern int layout_vbasetypes PROTO((tree, int));
+extern tree layout_basetypes PROTO((tree, tree));
+extern int list_hash PROTO((tree));
+extern tree list_hash_lookup PROTO((int, tree));
+extern void list_hash_add PROTO((int, tree));
+extern tree list_hash_canon PROTO((int, tree));
+extern tree hash_tree_cons PROTO((int, int, int, tree, tree, tree));
+extern tree hash_tree_chain PROTO((tree, tree));
+extern tree hash_chainon PROTO((tree, tree));
+extern tree get_decl_list PROTO((tree));
+extern tree list_hash_lookup_or_cons PROTO((tree));
+extern tree make_binfo PROTO((tree, tree, tree, tree, tree));
+extern tree copy_binfo PROTO((tree));
+extern tree binfo_value PROTO((tree, tree));
+extern tree reverse_path PROTO((tree));
+extern tree virtual_member PROTO((tree, tree));
+extern tree virtual_offset PROTO((tree, tree, tree));
+extern void debug_binfo PROTO((tree));
+extern int decl_list_length PROTO((tree));
+extern int count_functions PROTO((tree));
+extern tree decl_value_member PROTO((tree, tree));
+extern int is_overloaded_fn PROTO((tree));
+extern tree get_first_fn PROTO((tree));
+extern tree fnaddr_from_vtable_entry PROTO((tree));
+extern void set_fnaddr_from_vtable_entry PROTO((tree, tree));
+extern tree function_arg_chain PROTO((tree));
+extern int promotes_to_aggr_type PROTO((tree, enum tree_code));
+extern int is_aggr_type_2 PROTO((tree, tree));
+extern void message_2_types PROTO((void (*)(), char *, tree, tree));
+extern char *lang_printable_name PROTO((tree));
+extern tree build_exception_variant PROTO((tree, tree, tree));
+extern tree copy_to_permanent PROTO((tree));
+extern void print_lang_statistics PROTO((void));
+/* skip __eprintf */
+extern tree array_type_nelts_total PROTO((tree));
+extern tree array_type_nelts_top PROTO((tree));
+
+/* in typeck.c */
+extern tree bool_truthvalue_conversion PROTO((tree));
+extern tree target_type PROTO((tree));
+extern tree require_complete_type PROTO((tree));
+extern int type_unknown_p PROTO((tree));
+extern int fntype_p PROTO((tree));
+extern tree require_instantiated_type PROTO((tree, tree, tree));
+extern tree commonparms PROTO((tree, tree));
+extern tree common_type PROTO((tree, tree));
+extern int compexcepttypes PROTO((tree, tree, int));
+extern int comptypes PROTO((tree, tree, int));
+extern int comp_target_types PROTO((tree, tree, int));
+extern tree common_base_types PROTO((tree, tree));
+extern int compparms PROTO((tree, tree, int));
+extern int comp_target_types PROTO((tree, tree, int));
+extern int self_promoting_args_p PROTO((tree));
+extern tree unsigned_type PROTO((tree));
+extern tree signed_type PROTO((tree));
+extern tree signed_or_unsigned_type PROTO((int, tree));
+extern tree c_sizeof PROTO((tree));
+extern tree c_sizeof_nowarn PROTO((tree));
+extern tree c_alignof PROTO((tree));
+extern tree default_conversion PROTO((tree));
+extern tree build_object_ref PROTO((tree, tree, tree));
+extern tree build_component_ref_1 PROTO((tree, tree, int));
+extern tree build_component_ref PROTO((tree, tree, tree, int));
+extern tree build_x_indirect_ref PROTO((tree, char *));
+extern tree build_indirect_ref PROTO((tree, char *));
+extern tree build_x_array_ref PROTO((tree, tree));
+extern tree build_array_ref PROTO((tree, tree));
+extern tree build_x_function_call PROTO((tree, tree, tree));
+extern tree build_function_call_real PROTO((tree, tree, int, int));
+extern tree build_function_call PROTO((tree, tree));
+extern tree build_function_call_maybe PROTO((tree, tree));
+extern tree convert_arguments PROTO((tree, tree, tree, tree, int));
+extern tree build_x_binary_op PROTO((enum tree_code, tree, tree));
+extern tree build_binary_op PROTO((enum tree_code, tree, tree, int));
+extern tree build_binary_op_nodefault PROTO((enum tree_code, tree, tree, enum tree_code));
+extern tree build_component_addr PROTO((tree, tree, char *));
+extern tree build_x_unary_op PROTO((enum tree_code, tree));
+extern tree build_unary_op PROTO((enum tree_code, tree, int));
+extern tree unary_complex_lvalue PROTO((enum tree_code, tree));
+extern int mark_addressable PROTO((tree));
+extern tree build_x_conditional_expr PROTO((tree, tree, tree));
+extern tree build_conditional_expr PROTO((tree, tree, tree));
+extern tree build_x_compound_expr PROTO((tree));
+extern tree build_compound_expr PROTO((tree));
+extern tree build_static_cast PROTO((tree, tree));
+extern tree build_reinterpret_cast PROTO((tree, tree));
+extern tree build_const_cast PROTO((tree, tree));
+extern tree build_c_cast PROTO((tree, tree));
+extern tree build_modify_expr PROTO((tree, enum tree_code, tree));
+extern int language_lvalue_valid PROTO((tree));
+extern void warn_for_assignment PROTO((char *, char *, char *, tree, int, int));
+extern tree convert_for_initialization PROTO((tree, tree, tree, int, char *, tree, int));
+extern void c_expand_asm_operands PROTO((tree, tree, tree, tree, int, char *, int));
+extern void c_expand_return PROTO((tree));
+extern tree c_expand_start_case PROTO((tree));
+extern tree build_component_ref PROTO((tree, tree, tree, int));
+extern tree build_ptrmemfunc PROTO((tree, tree, int));
+
+/* in typeck2.c */
+extern tree error_not_base_type PROTO((tree, tree));
+extern tree binfo_or_else PROTO((tree, tree));
+extern void error_with_aggr_type (); /* PROTO((tree, char *, HOST_WIDE_INT)); */
+extern void readonly_error PROTO((tree, char *, int));
+extern void abstract_virtuals_error PROTO((tree, tree));
+extern void incomplete_type_error PROTO((tree, tree));
+extern void my_friendly_abort PROTO((int));
+extern void my_friendly_assert PROTO((int, int));
+extern tree store_init_value PROTO((tree, tree));
+extern tree digest_init PROTO((tree, tree, tree *));
+extern tree build_scoped_ref PROTO((tree, tree));
+extern tree build_x_arrow PROTO((tree));
+extern tree build_m_component_ref PROTO((tree, tree));
+extern tree build_functional_cast PROTO((tree, tree));
+extern char *enum_name_string PROTO((tree, tree));
+extern void report_case_error PROTO((int, tree, tree, tree));
+
+/* in xref.c */
+extern void GNU_xref_begin PROTO((char *));
+extern void GNU_xref_end PROTO((int));
+extern void GNU_xref_file PROTO((char *));
+extern void GNU_xref_start_scope PROTO((HOST_WIDE_INT));
+extern void GNU_xref_end_scope PROTO((HOST_WIDE_INT, HOST_WIDE_INT, int, int, int));
+extern void GNU_xref_def PROTO((tree, char *));
+extern void GNU_xref_decl PROTO((tree, tree));
+extern void GNU_xref_call PROTO((tree, char *));
+extern void GNU_xref_function PROTO((tree, tree));
+extern void GNU_xref_assign PROTO((tree));
+extern void GNU_xref_hier PROTO((char *, char *, int, int, int));
+extern void GNU_xref_member PROTO((tree, tree));
+
+/* -- end of C++ */
+
+#endif /* not _CP_TREE_H */
diff --git a/gnu/usr.bin/cc/cc1plus/cvt.c b/gnu/usr.bin/cc/cc1plus/cvt.c
new file mode 100644
index 0000000..cb8d968
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/cvt.c
@@ -0,0 +1,2044 @@
+/* Language-level data type conversion for GNU C++.
+ Copyright (C) 1987, 1988, 1992, 1993 Free Software Foundation, Inc.
+ Hacked by Michael Tiemann (tiemann@cygnus.com)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file contains the functions for converting C expressions
+ to different data types. The only entry point is `convert'.
+ Every language front end must have a `convert' function
+ but what kind of conversions it does will depend on the language. */
+
+#include "config.h"
+#include "tree.h"
+#include "flags.h"
+#include "cp-tree.h"
+#include "class.h"
+#include "convert.h"
+
+#undef NULL
+#define NULL (char *)0
+
+/* Change of width--truncation and extension of integers or reals--
+ is represented with NOP_EXPR. Proper functioning of many things
+ assumes that no other conversions can be NOP_EXPRs.
+
+ Conversion between integer and pointer is represented with CONVERT_EXPR.
+ Converting integer to real uses FLOAT_EXPR
+ and real to integer uses FIX_TRUNC_EXPR.
+
+ Here is a list of all the functions that assume that widening and
+ narrowing is always done with a NOP_EXPR:
+ In convert.c, convert_to_integer.
+ In c-typeck.c, build_binary_op_nodefault (boolean ops),
+ and truthvalue_conversion.
+ In expr.c: expand_expr, for operands of a MULT_EXPR.
+ In fold-const.c: fold.
+ In tree.c: get_narrower and get_unwidened.
+
+ C++: in multiple-inheritance, converting between pointers may involve
+ adjusting them by a delta stored within the class definition. */
+
+/* Subroutines of `convert'. */
+
+/* Build a thunk. What it is, is an entry point that when called will
+ adjust the this pointer (the first argument) by offset, and then
+ goto the real address of the function given by REAL_ADDR that we
+ would like called. What we return is the address of the thunk. */
+static tree
+build_thunk (offset, real_addr)
+ tree offset, real_addr;
+{
+ if (TREE_CODE (real_addr) != ADDR_EXPR
+ || TREE_CODE (TREE_OPERAND (real_addr, 0)) != FUNCTION_DECL)
+ {
+ sorry ("MI pointer to member conversion too complex");
+ return error_mark_node;
+ }
+ sorry ("MI pointer to member conversion too complex");
+ return error_mark_node;
+}
+
+/* Convert a `pointer to member' (POINTER_TYPE to METHOD_TYPE) into
+ another `pointer to method'. This may involved the creation of
+ a thunk to handle the this offset calculation. */
+static tree
+convert_fn_ptr (type, expr)
+ tree type, expr;
+{
+ tree binfo = get_binfo (TYPE_METHOD_BASETYPE (TREE_TYPE (TREE_TYPE (expr))),
+ TYPE_METHOD_BASETYPE (TREE_TYPE (type)),
+ 1);
+ if (binfo == error_mark_node)
+ {
+ error (" in pointer to member conversion");
+ return error_mark_node;
+ }
+ if (binfo == NULL_TREE)
+ {
+ /* ARM 4.8 restriction. */
+ error ("invalid pointer to member conversion");
+ return error_mark_node;
+ }
+ if (BINFO_OFFSET_ZEROP (binfo))
+ return build1 (NOP_EXPR, type, expr);
+ return build1 (NOP_EXPR, type, build_thunk (BINFO_OFFSET (binfo), expr));
+}
+
+/* if converting pointer to pointer
+ if dealing with classes, check for derived->base or vice versa
+ else if dealing with method pointers, delegate
+ else convert blindly
+ else if converting class, pass off to build_type_conversion
+ else try C-style pointer conversion */
+static tree
+cp_convert_to_pointer (type, expr)
+ tree type, expr;
+{
+ register tree intype = TREE_TYPE (expr);
+ register enum tree_code form = TREE_CODE (intype);
+
+ if (form == POINTER_TYPE)
+ {
+ intype = TYPE_MAIN_VARIANT (intype);
+
+ if (TYPE_MAIN_VARIANT (type) != intype
+ && TREE_CODE (TREE_TYPE (type)) == RECORD_TYPE
+ && TREE_CODE (TREE_TYPE (intype)) == RECORD_TYPE)
+ {
+ enum tree_code code = PLUS_EXPR;
+ tree binfo = get_binfo (TREE_TYPE (type), TREE_TYPE (intype), 1);
+ if (binfo == error_mark_node)
+ return error_mark_node;
+ if (binfo == NULL_TREE)
+ {
+ binfo = get_binfo (TREE_TYPE (intype), TREE_TYPE (type), 1);
+ if (binfo == error_mark_node)
+ return error_mark_node;
+ code = MINUS_EXPR;
+ }
+ if (binfo)
+ {
+ if (TYPE_USES_VIRTUAL_BASECLASSES (TREE_TYPE (type))
+ || TYPE_USES_VIRTUAL_BASECLASSES (TREE_TYPE (intype))
+ || ! BINFO_OFFSET_ZEROP (binfo))
+ {
+ /* Need to get the path we took. */
+ tree path;
+
+ if (code == PLUS_EXPR)
+ get_base_distance (TREE_TYPE (type), TREE_TYPE (intype), 0, &path);
+ else
+ get_base_distance (TREE_TYPE (intype), TREE_TYPE (type), 0, &path);
+ return build_vbase_path (code, type, expr, path, 0);
+ }
+ }
+ }
+ if (TYPE_MAIN_VARIANT (type) != intype
+ && TREE_CODE (TREE_TYPE (type)) == METHOD_TYPE
+ && TREE_CODE (type) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (intype)) == METHOD_TYPE)
+ return convert_fn_ptr (type, expr);
+
+ return build1 (NOP_EXPR, type, expr);
+ }
+
+ my_friendly_assert (form != OFFSET_TYPE, 186);
+
+ if (TYPE_LANG_SPECIFIC (intype)
+ && (IS_SIGNATURE_POINTER (intype) || IS_SIGNATURE_REFERENCE (intype)))
+ return convert_to_pointer (type, build_optr_ref (expr));
+
+ if (IS_AGGR_TYPE (intype))
+ {
+ tree rval;
+ rval = build_type_conversion (CONVERT_EXPR, type, expr, 1);
+ if (rval)
+ {
+ if (rval == error_mark_node)
+ cp_error ("conversion of `%E' from `%T' to `%T' is ambiguous",
+ expr, intype, type);
+ return rval;
+ }
+ }
+
+ if (integer_zerop (expr))
+ {
+ if (type == TREE_TYPE (null_pointer_node))
+ return null_pointer_node;
+ expr = build_int_2 (0, 0);
+ TREE_TYPE (expr) = type;
+ return expr;
+ }
+
+ if (INTEGRAL_CODE_P (form))
+ {
+ if (type_precision (intype) == POINTER_SIZE)
+ return build1 (CONVERT_EXPR, type, expr);
+ expr = convert (type_for_size (POINTER_SIZE, 0), expr);
+ /* Modes may be different but sizes should be the same. */
+ if (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (expr)))
+ != GET_MODE_SIZE (TYPE_MODE (type)))
+ /* There is supposed to be some integral type
+ that is the same width as a pointer. */
+ abort ();
+ return convert_to_pointer (type, expr);
+ }
+
+ cp_error ("cannot convert `%E' from type `%T' to type `%T'",
+ expr, intype, type);
+ return error_mark_node;
+}
+
+/* Like convert, except permit conversions to take place which
+ are not normally allowed due to access restrictions
+ (such as conversion from sub-type to private super-type). */
+static tree
+convert_to_pointer_force (type, expr)
+ tree type, expr;
+{
+ register tree intype = TREE_TYPE (expr);
+ register enum tree_code form = TREE_CODE (intype);
+
+ if (integer_zerop (expr))
+ {
+ if (type == TREE_TYPE (null_pointer_node))
+ return null_pointer_node;
+ expr = build_int_2 (0, 0);
+ TREE_TYPE (expr) = type;
+ return expr;
+ }
+
+ /* Convert signature pointer/reference to `void *' first. */
+ if (form == RECORD_TYPE
+ && (IS_SIGNATURE_POINTER (intype) || IS_SIGNATURE_REFERENCE (intype)))
+ {
+ expr = build_optr_ref (expr);
+ intype = TREE_TYPE (expr);
+ form = TREE_CODE (intype);
+ }
+
+ if (form == POINTER_TYPE)
+ {
+ intype = TYPE_MAIN_VARIANT (intype);
+
+ if (TYPE_MAIN_VARIANT (type) != intype
+ && TREE_CODE (TREE_TYPE (type)) == RECORD_TYPE
+ && TREE_CODE (TREE_TYPE (intype)) == RECORD_TYPE)
+ {
+ enum tree_code code = PLUS_EXPR;
+ tree path;
+ int distance = get_base_distance (TREE_TYPE (type),
+ TREE_TYPE (intype), 0, &path);
+ if (distance == -2)
+ {
+ ambig:
+ cp_error ("type `%T' is ambiguous baseclass of `%s'", TREE_TYPE (type),
+ TYPE_NAME_STRING (TREE_TYPE (intype)));
+ return error_mark_node;
+ }
+ if (distance == -1)
+ {
+ distance = get_base_distance (TREE_TYPE (intype),
+ TREE_TYPE (type), 0, &path);
+ if (distance == -2)
+ goto ambig;
+ if (distance < 0)
+ /* Doesn't need any special help from us. */
+ return build1 (NOP_EXPR, type, expr);
+
+ code = MINUS_EXPR;
+ }
+ return build_vbase_path (code, type, expr, path, 0);
+ }
+ return build1 (NOP_EXPR, type, expr);
+ }
+
+ return cp_convert_to_pointer (type, expr);
+}
+
+/* We are passing something to a function which requires a reference.
+ The type we are interested in is in TYPE. The initial
+ value we have to begin with is in ARG.
+
+ FLAGS controls how we manage access checking.
+ CHECKCONST controls if we report error messages on const subversion. */
+static tree
+build_up_reference (type, arg, flags, checkconst)
+ tree type, arg;
+ int flags, checkconst;
+{
+ tree rval, targ;
+ int literal_flag = 0;
+ tree argtype = TREE_TYPE (arg);
+ tree target_type = TREE_TYPE (type);
+ tree binfo = NULL_TREE;
+
+ my_friendly_assert (TREE_CODE (type) == REFERENCE_TYPE, 187);
+ if ((flags & LOOKUP_PROTECT)
+ && TYPE_MAIN_VARIANT (argtype) != TYPE_MAIN_VARIANT (target_type)
+ && IS_AGGR_TYPE (argtype)
+ && IS_AGGR_TYPE (target_type))
+ {
+ binfo = get_binfo (target_type, argtype, 1);
+ if (binfo == error_mark_node)
+ return error_mark_node;
+ if (binfo == NULL_TREE)
+ return error_not_base_type (target_type, argtype);
+ }
+
+ /* Pass along const and volatile down into the type. */
+ if (TYPE_READONLY (type) || TYPE_VOLATILE (type))
+ target_type = c_build_type_variant (target_type, TYPE_READONLY (type),
+ TYPE_VOLATILE (type));
+ targ = arg;
+ if (TREE_CODE (targ) == SAVE_EXPR)
+ targ = TREE_OPERAND (targ, 0);
+
+ switch (TREE_CODE (targ))
+ {
+ case INDIRECT_REF:
+ /* This is a call to a constructor which did not know what it was
+ initializing until now: it needs to initialize a temporary. */
+ if (TREE_HAS_CONSTRUCTOR (targ))
+ {
+ tree temp = build_cplus_new (argtype, TREE_OPERAND (targ, 0), 1);
+ TREE_HAS_CONSTRUCTOR (targ) = 0;
+ return build_up_reference (type, temp, flags, 1);
+ }
+ /* Let &* cancel out to simplify resulting code.
+ Also, throw away intervening NOP_EXPRs. */
+ arg = TREE_OPERAND (targ, 0);
+ if (TREE_CODE (arg) == NOP_EXPR || TREE_CODE (arg) == NON_LVALUE_EXPR
+ || (TREE_CODE (arg) == CONVERT_EXPR && TREE_REFERENCE_EXPR (arg)))
+ arg = TREE_OPERAND (arg, 0);
+
+ /* in doing a &*, we have to get rid of the const'ness on the pointer
+ value. Haven't thought about volatile here. Pointers come to mind
+ here. */
+ if (TREE_READONLY (arg))
+ {
+ arg = copy_node (arg);
+ TREE_READONLY (arg) = 0;
+ }
+
+ rval = build1 (CONVERT_EXPR, type, arg);
+ TREE_REFERENCE_EXPR (rval) = 1;
+
+ /* propagate the const flag on something like:
+
+ class Base {
+ public:
+ int foo;
+ };
+
+ class Derived : public Base {
+ public:
+ int bar;
+ };
+
+ void func(Base&);
+
+ void func2(const Derived& d) {
+ func(d);
+ }
+
+ on the d parameter. The below could have been avoided, if the flags
+ were down in the tree, not sure why they are not. (mrs) */
+ /* The below code may have to be propagated to other parts of this
+ switch. */
+ if (TREE_READONLY (targ) && !TREE_READONLY (arg)
+ && (TREE_CODE (arg) == PARM_DECL || TREE_CODE (arg) == VAR_DECL)
+ && TREE_CODE (TREE_TYPE (arg)) == REFERENCE_TYPE
+ && (TYPE_READONLY (target_type) && checkconst))
+ {
+ arg = copy_node (arg);
+ TREE_READONLY (arg) = TREE_READONLY (targ);
+ }
+ literal_flag = TREE_CONSTANT (arg);
+
+ goto done;
+
+ /* Get this out of a register if we happened to be in one by accident.
+ Also, build up references to non-lvalues it we must. */
+ /* For &x[y], return (&) x+y */
+ case ARRAY_REF:
+ if (mark_addressable (TREE_OPERAND (targ, 0)) == 0)
+ return error_mark_node;
+ rval = build_binary_op (PLUS_EXPR, TREE_OPERAND (targ, 0),
+ TREE_OPERAND (targ, 1), 1);
+ TREE_TYPE (rval) = type;
+ if (TREE_CONSTANT (TREE_OPERAND (targ, 1))
+ && staticp (TREE_OPERAND (targ, 0)))
+ TREE_CONSTANT (rval) = 1;
+ goto done;
+
+ case SCOPE_REF:
+ /* Could be a reference to a static member. */
+ {
+ tree field = TREE_OPERAND (targ, 1);
+ if (TREE_STATIC (field))
+ {
+ rval = build1 (ADDR_EXPR, type, field);
+ literal_flag = 1;
+ goto done;
+ }
+ }
+
+ /* We should have farmed out member pointers above. */
+ my_friendly_abort (188);
+
+ case COMPONENT_REF:
+ rval = build_component_addr (targ, build_pointer_type (argtype),
+ "attempt to make a reference to bit-field structure member `%s'");
+ TREE_TYPE (rval) = type;
+ literal_flag = staticp (TREE_OPERAND (targ, 0));
+
+ goto done;
+
+ /* Anything not already handled and not a true memory reference
+ needs to have a reference built up. Do so silently for
+ things like integers and return values from function,
+ but complain if we need a reference to something declared
+ as `register'. */
+
+ case RESULT_DECL:
+ if (staticp (targ))
+ literal_flag = 1;
+ TREE_ADDRESSABLE (targ) = 1;
+ put_var_into_stack (targ);
+ break;
+
+ case PARM_DECL:
+ if (targ == current_class_decl)
+ {
+ error ("address of `this' not available");
+#if 0
+ /* This code makes the following core dump the compiler on a sun4,
+ if the code below is used.
+
+ class e_decl;
+ class a_decl;
+ typedef a_decl* a_ref;
+
+ class a_s {
+ public:
+ a_s();
+ void* append(a_ref& item);
+ };
+ class a_decl {
+ public:
+ a_decl (e_decl *parent);
+ a_s generic_s;
+ a_s decls;
+ e_decl* parent;
+ };
+
+ class e_decl {
+ public:
+ e_decl();
+ a_s implementations;
+ };
+
+ void foobar(void *);
+
+ a_decl::a_decl(e_decl *parent) {
+ parent->implementations.append(this);
+ }
+ */
+
+ TREE_ADDRESSABLE (targ) = 1; /* so compiler doesn't die later */
+ put_var_into_stack (targ);
+ break;
+#else
+ return error_mark_node;
+#endif
+ }
+ /* Fall through. */
+ case VAR_DECL:
+ case CONST_DECL:
+ if (DECL_REGISTER (targ) && !TREE_ADDRESSABLE (targ))
+ warning ("address needed to build reference for `%s', which is declared `register'",
+ IDENTIFIER_POINTER (DECL_NAME (targ)));
+ else if (staticp (targ))
+ literal_flag = 1;
+
+ TREE_ADDRESSABLE (targ) = 1;
+ put_var_into_stack (targ);
+ break;
+
+ case COMPOUND_EXPR:
+ {
+ tree real_reference = build_up_reference (type, TREE_OPERAND (targ, 1),
+ LOOKUP_PROTECT, checkconst);
+ rval = build (COMPOUND_EXPR, type, TREE_OPERAND (targ, 0), real_reference);
+ TREE_CONSTANT (rval) = staticp (TREE_OPERAND (targ, 1));
+ return rval;
+ }
+
+ case MODIFY_EXPR:
+ case INIT_EXPR:
+ {
+ tree real_reference = build_up_reference (type, TREE_OPERAND (targ, 0),
+ LOOKUP_PROTECT, checkconst);
+ rval = build (COMPOUND_EXPR, type, arg, real_reference);
+ TREE_CONSTANT (rval) = staticp (TREE_OPERAND (targ, 0));
+ return rval;
+ }
+
+ case COND_EXPR:
+ return build (COND_EXPR, type,
+ TREE_OPERAND (targ, 0),
+ build_up_reference (type, TREE_OPERAND (targ, 1),
+ LOOKUP_PROTECT, checkconst),
+ build_up_reference (type, TREE_OPERAND (targ, 2),
+ LOOKUP_PROTECT, checkconst));
+
+ case WITH_CLEANUP_EXPR:
+ return build (WITH_CLEANUP_EXPR, type,
+ build_up_reference (type, TREE_OPERAND (targ, 0),
+ LOOKUP_PROTECT, checkconst),
+ 0, TREE_OPERAND (targ, 2));
+
+ case BIND_EXPR:
+ arg = TREE_OPERAND (targ, 1);
+ if (arg == NULL_TREE)
+ {
+ compiler_error ("({ ... }) expression not expanded when needed for reference");
+ return error_mark_node;
+ }
+ rval = build1 (ADDR_EXPR, type, arg);
+ TREE_REFERENCE_EXPR (rval) = 1;
+ return rval;
+
+ default:
+ break;
+ }
+
+ if (TREE_ADDRESSABLE (targ) == 0)
+ {
+ tree temp;
+
+ if (TREE_CODE (targ) == CALL_EXPR && IS_AGGR_TYPE (argtype))
+ {
+ temp = build_cplus_new (argtype, targ, 1);
+ if (TREE_CODE (temp) == WITH_CLEANUP_EXPR)
+ rval = build (WITH_CLEANUP_EXPR, type,
+ build1 (ADDR_EXPR, type, TREE_OPERAND (temp, 0)),
+ 0, TREE_OPERAND (temp, 2));
+ else
+ rval = build1 (ADDR_EXPR, type, temp);
+ goto done;
+ }
+ else
+ {
+ temp = get_temp_name (argtype, 0);
+ if (global_bindings_p ())
+ {
+ /* Give this new temp some rtl and initialize it. */
+ DECL_INITIAL (temp) = targ;
+ TREE_STATIC (temp) = 1;
+ finish_decl (temp, targ, NULL_TREE, 0);
+ /* Do this after declaring it static. */
+ rval = build_unary_op (ADDR_EXPR, temp, 0);
+ TREE_TYPE (rval) = type;
+ literal_flag = TREE_CONSTANT (rval);
+ goto done;
+ }
+ else
+ {
+ rval = build_unary_op (ADDR_EXPR, temp, 0);
+ if (binfo && !BINFO_OFFSET_ZEROP (binfo))
+ rval = convert_pointer_to (target_type, rval);
+ else
+ TREE_TYPE (rval) = type;
+
+ temp = build (MODIFY_EXPR, argtype, temp, arg);
+ TREE_SIDE_EFFECTS (temp) = 1;
+ return build (COMPOUND_EXPR, type, temp, rval);
+ }
+ }
+ }
+ else
+ rval = build1 (ADDR_EXPR, type, arg);
+
+ done:
+ if (TYPE_USES_COMPLEX_INHERITANCE (argtype)
+ || TYPE_USES_COMPLEX_INHERITANCE (target_type))
+ {
+ TREE_TYPE (rval) = build_pointer_type (argtype);
+ if (flags & LOOKUP_PROTECT)
+ rval = convert_pointer_to (target_type, rval);
+ else
+ rval
+ = convert_to_pointer_force (build_pointer_type (target_type), rval);
+ TREE_TYPE (rval) = type;
+ }
+ TREE_CONSTANT (rval) = literal_flag;
+ return rval;
+}
+
+/* For C++: Only need to do one-level references, but cannot
+ get tripped up on signed/unsigned differences.
+
+ DECL is either NULL_TREE or the _DECL node for a reference that is being
+ initialized. It can be error_mark_node if we don't know the _DECL but
+ we know it's an initialization. */
+
+tree cp_convert PROTO((tree, tree, int, int));
+
+tree
+convert_to_reference (reftype, expr, convtype, flags, decl)
+ tree reftype, expr;
+ int convtype, flags;
+ tree decl;
+{
+ register tree type = TYPE_MAIN_VARIANT (TREE_TYPE (reftype));
+ register tree intype = TREE_TYPE (expr);
+ register enum tree_code form = TREE_CODE (intype);
+ tree rval = NULL_TREE;
+
+ if (form == REFERENCE_TYPE)
+ intype = TREE_TYPE (intype);
+ intype = TYPE_MAIN_VARIANT (intype);
+
+ if (((convtype & CONV_STATIC) && comptypes (type, intype, -1))
+ || ((convtype & CONV_IMPLICIT) && comptypes (type, intype, 0)))
+ {
+ if (flags & LOOKUP_COMPLAIN)
+ {
+ tree ttl = TREE_TYPE (reftype);
+ tree ttr;
+
+ if (form == REFERENCE_TYPE)
+ ttr = TREE_TYPE (TREE_TYPE (expr));
+ else
+ {
+ int r = TREE_READONLY (expr);
+ int v = TREE_THIS_VOLATILE (expr);
+ ttr = c_build_type_variant (TREE_TYPE (expr), r, v);
+ }
+
+ if (! lvalue_p (expr) &&
+ (decl == NULL_TREE || ! TYPE_READONLY (ttl)))
+ {
+ if (decl)
+ /* Ensure semantics of [dcl.init.ref] */
+ cp_pedwarn ("initialization of non-const `%T' from rvalue `%T'",
+ reftype, intype);
+ else
+ cp_pedwarn ("conversion to `%T' from rvalue `%T'",
+ reftype, intype);
+ }
+ else if (! (convtype & CONV_CONST))
+ {
+ if (! TYPE_READONLY (ttl) && TYPE_READONLY (ttr))
+ cp_pedwarn ("conversion from `%T' to `%T' discards const",
+ ttr, reftype);
+ else if (! TYPE_VOLATILE (ttl) && TYPE_VOLATILE (ttr))
+ cp_pedwarn ("conversion from `%T' to `%T' discards volatile",
+ ttr, reftype);
+ }
+ }
+
+ if (form == REFERENCE_TYPE)
+ {
+ rval = copy_node (expr);
+ TREE_TYPE (rval) = build_pointer_type (TREE_TYPE (TREE_TYPE (expr)));
+ rval = cp_convert (build_pointer_type (TREE_TYPE (reftype)), rval,
+ convtype, flags);
+ TREE_TYPE (rval) = reftype;
+ return rval;
+ }
+
+ return build_up_reference (reftype, expr, flags,
+ ! (convtype & CONV_CONST));
+ }
+
+ if ((convtype & CONV_IMPLICIT)
+ && IS_AGGR_TYPE (intype)
+ && ! (flags & LOOKUP_NO_CONVERSION)
+ && (rval = build_type_conversion (CONVERT_EXPR, reftype, expr, 1)))
+ {
+ if (rval == error_mark_node)
+ cp_error ("conversion from `%T' to `%T' is ambiguous",
+ intype, reftype);
+ return rval;
+ }
+ else if ((convtype & CONV_REINTERPRET) && lvalue_p (expr))
+ {
+ /* When casting an lvalue to a reference type, just convert into
+ a pointer to the new type and deference it. This is allowed
+ by San Diego WP section 5.2.9 paragraph 12, though perhaps it
+ should be done directly (jason). (int &)ri ---> *(int*)&ri */
+
+ /* B* bp; A& ar = (A&)bp; is legal, but it's probably not what they
+ meant. */
+ if (form == POINTER_TYPE
+ && (comptypes (TREE_TYPE (intype), type, -1)))
+ cp_warning ("casting `%T' to `%T' does not dereference pointer",
+ intype, reftype);
+
+ rval = build_unary_op (ADDR_EXPR, expr, 0);
+ if (rval != error_mark_node)
+ rval = convert_force (build_pointer_type (TREE_TYPE (reftype)), rval);
+ if (rval != error_mark_node)
+ rval = build1 (NOP_EXPR, reftype, rval);
+ }
+ else if (decl)
+ {
+ tree rval_as_conversion = NULL_TREE;
+ tree rval_as_ctor = NULL_TREE;
+
+ if (IS_AGGR_TYPE (intype)
+ && (rval = build_type_conversion (CONVERT_EXPR, type, expr, 1)))
+ {
+ if (rval == error_mark_node)
+ return rval;
+
+ rval_as_conversion = build_up_reference (reftype, rval, flags, 1);
+ }
+
+ /* Definitely need to go through a constructor here. */
+ if (TYPE_HAS_CONSTRUCTOR (type)
+ && ! CLASSTYPE_ABSTRACT_VIRTUALS (type)
+ && (rval = build_method_call
+ (NULL_TREE, constructor_name_full (type),
+ build_tree_list (NULL_TREE, expr), TYPE_BINFO (type),
+ LOOKUP_NO_CONVERSION|LOOKUP_SPECULATIVELY)))
+ {
+ tree init;
+
+ if (global_bindings_p ())
+ {
+ extern tree static_aggregates;
+ tree t = get_temp_name (type, global_bindings_p ());
+ init = build_method_call (t, constructor_name_full (type),
+ build_tree_list (NULL_TREE, expr),
+ TYPE_BINFO (type),
+ LOOKUP_NORMAL|LOOKUP_NO_CONVERSION);
+
+ if (init == error_mark_node)
+ return error_mark_node;
+
+ make_decl_rtl (t, NULL_PTR, 1);
+ static_aggregates = perm_tree_cons (expr, t, static_aggregates);
+ rval = build_unary_op (ADDR_EXPR, t, 0);
+ }
+ else
+ {
+ init = build_method_call (NULL_TREE, constructor_name_full (type),
+ build_tree_list (NULL_TREE, expr),
+ TYPE_BINFO (type),
+ LOOKUP_NORMAL|LOOKUP_NO_CONVERSION);
+
+ if (init == error_mark_node)
+ return error_mark_node;
+
+ rval = build_cplus_new (type, init, 1);
+ rval = build_up_reference (reftype, rval, flags, 1);
+ }
+ rval_as_ctor = rval;
+ }
+
+ if (rval_as_ctor && rval_as_conversion)
+ {
+ cp_error ("ambiguous conversion from `%T' to `%T'; both user-defined conversion and constructor apply",
+ intype, reftype);
+ return error_mark_node;
+ }
+ else if (rval_as_ctor)
+ rval = rval_as_ctor;
+ else if (rval_as_conversion)
+ rval = rval_as_conversion;
+ else if (! IS_AGGR_TYPE (type) && ! IS_AGGR_TYPE (intype))
+ {
+ rval = convert (type, expr);
+ if (rval == error_mark_node)
+ return error_mark_node;
+
+ rval = build_up_reference (reftype, rval, flags, 1);
+ }
+
+ if (rval && ! TYPE_READONLY (TREE_TYPE (reftype)))
+ cp_pedwarn ("initializing non-const `%T' with `%T' will use a temporary",
+ reftype, intype);
+ }
+
+ if (rval)
+ {
+ /* If we found a way to convert earlier, then use it. */
+ return rval;
+ }
+
+ my_friendly_assert (form != OFFSET_TYPE, 189);
+
+ if (flags & LOOKUP_SPECULATIVELY)
+ return NULL_TREE;
+
+ else if (flags & LOOKUP_COMPLAIN)
+ cp_error ("cannot convert type `%T' to type `%T'", intype, reftype);
+
+ return error_mark_node;
+}
+
+/* We are using a reference VAL for its value. Bash that reference all the
+ way down to its lowest form. */
+tree
+convert_from_reference (val)
+ tree val;
+{
+ tree type = TREE_TYPE (val);
+
+ if (TREE_CODE (type) == OFFSET_TYPE)
+ type = TREE_TYPE (type);
+ if (TREE_CODE (type) == REFERENCE_TYPE)
+ {
+ tree target_type = TREE_TYPE (type);
+ tree nval;
+
+ /* This can happen if we cast to a reference type. */
+ if (TREE_CODE (val) == ADDR_EXPR)
+ {
+ nval = build1 (NOP_EXPR, build_pointer_type (target_type), val);
+ nval = build_indirect_ref (nval, NULL_PTR);
+ /* The below was missing, are other important flags missing too? */
+ TREE_SIDE_EFFECTS (nval) = TREE_SIDE_EFFECTS (val);
+ return nval;
+ }
+
+ nval = build1 (INDIRECT_REF, target_type, val);
+
+ TREE_THIS_VOLATILE (nval) = TYPE_VOLATILE (target_type);
+ TREE_SIDE_EFFECTS (nval) = TYPE_VOLATILE (target_type);
+ TREE_READONLY (nval) = TYPE_READONLY (target_type);
+ /* The below was missing, are other important flags missing too? */
+ TREE_SIDE_EFFECTS (nval) |= TREE_SIDE_EFFECTS (val);
+ return nval;
+ }
+ return val;
+}
+
+/* See if there is a constructor of type TYPE which will convert
+ EXPR. The reference manual seems to suggest (8.5.6) that we need
+ not worry about finding constructors for base classes, then converting
+ to the derived class.
+
+ MSGP is a pointer to a message that would be an appropriate error
+ string. If MSGP is NULL, then we are not interested in reporting
+ errors. */
+tree
+convert_to_aggr (type, expr, msgp, protect)
+ tree type, expr;
+ char **msgp;
+ int protect;
+{
+ tree basetype = type;
+ tree name = TYPE_IDENTIFIER (basetype);
+ tree function, fndecl, fntype, parmtypes, parmlist, result;
+ tree method_name;
+ enum access_type access;
+ int can_be_private, can_be_protected;
+
+ if (! TYPE_HAS_CONSTRUCTOR (basetype))
+ {
+ if (msgp)
+ *msgp = "type `%s' does not have a constructor";
+ return error_mark_node;
+ }
+
+ access = access_public;
+ can_be_private = 0;
+ can_be_protected = IDENTIFIER_CLASS_VALUE (name) || name == current_class_name;
+
+ parmlist = build_tree_list (NULL_TREE, expr);
+ parmtypes = tree_cons (NULL_TREE, TREE_TYPE (expr), void_list_node);
+
+ if (TYPE_USES_VIRTUAL_BASECLASSES (basetype))
+ {
+ parmtypes = tree_cons (NULL_TREE, integer_type_node, parmtypes);
+ parmlist = tree_cons (NULL_TREE, integer_one_node, parmlist);
+ }
+
+ /* The type of the first argument will be filled in inside the loop. */
+ parmlist = tree_cons (NULL_TREE, integer_zero_node, parmlist);
+ parmtypes = tree_cons (NULL_TREE, TYPE_POINTER_TO (basetype), parmtypes);
+
+ method_name = build_decl_overload (name, parmtypes, 1);
+
+ /* constructors are up front. */
+ fndecl = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (basetype), 0);
+ if (TYPE_HAS_DESTRUCTOR (basetype))
+ fndecl = DECL_CHAIN (fndecl);
+
+ while (fndecl)
+ {
+ if (DECL_ASSEMBLER_NAME (fndecl) == method_name)
+ {
+ function = fndecl;
+ if (protect)
+ {
+ if (TREE_PRIVATE (fndecl))
+ {
+ can_be_private =
+ (basetype == current_class_type
+ || is_friend (basetype, current_function_decl)
+ || purpose_member (basetype, DECL_ACCESS (fndecl)));
+ if (! can_be_private)
+ goto found;
+ }
+ else if (TREE_PROTECTED (fndecl))
+ {
+ if (! can_be_protected)
+ goto found;
+ }
+ }
+ goto found_and_ok;
+ }
+ fndecl = DECL_CHAIN (fndecl);
+ }
+
+ /* No exact conversion was found. See if an approximate
+ one will do. */
+ fndecl = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (basetype), 0);
+ if (TYPE_HAS_DESTRUCTOR (basetype))
+ fndecl = DECL_CHAIN (fndecl);
+
+ {
+ int saw_private = 0;
+ int saw_protected = 0;
+ struct candidate *candidates =
+ (struct candidate *) alloca ((decl_list_length (fndecl)+1) * sizeof (struct candidate));
+ struct candidate *cp = candidates;
+
+ while (fndecl)
+ {
+ function = fndecl;
+ cp->h_len = 2;
+ cp->harshness = (struct harshness_code *)
+ alloca (3 * sizeof (struct harshness_code));
+
+ compute_conversion_costs (fndecl, parmlist, cp, 2);
+ if ((cp->h.code & EVIL_CODE) == 0)
+ {
+ cp->u.field = fndecl;
+ if (protect)
+ {
+ if (TREE_PRIVATE (fndecl))
+ access = access_private;
+ else if (TREE_PROTECTED (fndecl))
+ access = access_protected;
+ else
+ access = access_public;
+ }
+ else
+ access = access_public;
+
+ if (access == access_private
+ ? (basetype == current_class_type
+ || is_friend (basetype, cp->function)
+ || purpose_member (basetype, DECL_ACCESS (fndecl)))
+ : access == access_protected
+ ? (can_be_protected
+ || purpose_member (basetype, DECL_ACCESS (fndecl)))
+ : 1)
+ {
+ if (cp->h.code <= TRIVIAL_CODE)
+ goto found_and_ok;
+ cp++;
+ }
+ else
+ {
+ if (access == access_private)
+ saw_private = 1;
+ else
+ saw_protected = 1;
+ }
+ }
+ fndecl = DECL_CHAIN (fndecl);
+ }
+ if (cp - candidates)
+ {
+ /* Rank from worst to best. Then cp will point to best one.
+ Private fields have their bits flipped. For unsigned
+ numbers, this should make them look very large.
+ If the best alternate has a (signed) negative value,
+ then all we ever saw were private members. */
+ if (cp - candidates > 1)
+ qsort (candidates, /* char *base */
+ cp - candidates, /* int nel */
+ sizeof (struct candidate), /* int width */
+ rank_for_overload); /* int (*compar)() */
+
+ --cp;
+ if (cp->h.code & EVIL_CODE)
+ {
+ if (msgp)
+ *msgp = "ambiguous type conversion possible for `%s'";
+ return error_mark_node;
+ }
+
+ function = cp->function;
+ fndecl = cp->u.field;
+ goto found_and_ok;
+ }
+ else if (msgp)
+ {
+ if (saw_private)
+ if (saw_protected)
+ *msgp = "only private and protected conversions apply";
+ else
+ *msgp = "only private conversions apply";
+ else if (saw_protected)
+ *msgp = "only protected conversions apply";
+ else
+ *msgp = "no appropriate conversion to type `%s'";
+ }
+ return error_mark_node;
+ }
+ /* NOTREACHED */
+
+ found:
+ if (access == access_private)
+ if (! can_be_private)
+ {
+ if (msgp)
+ *msgp = TREE_PRIVATE (fndecl)
+ ? "conversion to type `%s' is private"
+ : "conversion to type `%s' is from private base class";
+ return error_mark_node;
+ }
+ if (access == access_protected)
+ if (! can_be_protected)
+ {
+ if (msgp)
+ *msgp = TREE_PRIVATE (fndecl)
+ ? "conversion to type `%s' is protected"
+ : "conversion to type `%s' is from protected base class";
+ return error_mark_node;
+ }
+ function = fndecl;
+ found_and_ok:
+
+ /* It will convert, but we don't do anything about it yet. */
+ if (msgp == 0)
+ return NULL_TREE;
+
+ fntype = TREE_TYPE (function);
+ if (DECL_INLINE (function) && TREE_CODE (function) == FUNCTION_DECL)
+ function = build1 (ADDR_EXPR, build_pointer_type (fntype), function);
+ else
+ function = default_conversion (function);
+
+ result = build_nt (CALL_EXPR, function,
+ convert_arguments (NULL_TREE, TYPE_ARG_TYPES (fntype),
+ parmlist, NULL_TREE, LOOKUP_NORMAL),
+ NULL_TREE);
+ TREE_TYPE (result) = TREE_TYPE (fntype);
+ TREE_SIDE_EFFECTS (result) = 1;
+ TREE_RAISES (result) = !! TYPE_RAISES_EXCEPTIONS (fntype);
+ return result;
+}
+
+/* Call this when we know (for any reason) that expr is not, in fact,
+ zero. This routine is like convert_pointer_to, but it pays
+ attention to which specific instance of what type we want to
+ convert to. This routine should eventually become
+ convert_to_pointer after all references to convert_to_pointer
+ are removed. */
+tree
+convert_pointer_to_real (binfo, expr)
+ tree binfo, expr;
+{
+ register tree intype = TREE_TYPE (expr);
+ tree ptr_type;
+ tree type, rval;
+
+ if (TREE_CODE (binfo) == TREE_VEC)
+ type = BINFO_TYPE (binfo);
+ else if (IS_AGGR_TYPE (binfo))
+ {
+ type = binfo;
+ }
+ else
+ {
+ type = binfo;
+ binfo = NULL_TREE;
+ }
+
+ ptr_type = build_pointer_type (type);
+ if (ptr_type == TYPE_MAIN_VARIANT (intype))
+ return expr;
+
+ if (intype == error_mark_node)
+ return error_mark_node;
+
+ my_friendly_assert (!integer_zerop (expr), 191);
+
+ if (TREE_CODE (type) == RECORD_TYPE
+ && TREE_CODE (TREE_TYPE (intype)) == RECORD_TYPE
+ && type != TYPE_MAIN_VARIANT (TREE_TYPE (intype)))
+ {
+ tree path;
+ int distance
+ = get_base_distance (binfo, TYPE_MAIN_VARIANT (TREE_TYPE (intype)),
+ 0, &path);
+
+ /* This function shouldn't be called with unqualified arguments
+ but if it is, give them an error message that they can read. */
+ if (distance < 0)
+ {
+ cp_error ("cannot convert a pointer of type `%T' to a pointer of type `%T'",
+ TREE_TYPE (intype), type);
+
+ if (distance == -2)
+ cp_error ("because `%T' is an ambiguous base class", type);
+ return error_mark_node;
+ }
+
+ return build_vbase_path (PLUS_EXPR, ptr_type, expr, path, 1);
+ }
+ rval = build1 (NOP_EXPR, ptr_type,
+ TREE_CODE (expr) == NOP_EXPR ? TREE_OPERAND (expr, 0) : expr);
+ TREE_CONSTANT (rval) = TREE_CONSTANT (expr);
+ return rval;
+}
+
+/* Call this when we know (for any reason) that expr is
+ not, in fact, zero. This routine gets a type out of the first
+ argument and uses it to search for the type to convert to. If there
+ is more than one instance of that type in the expr, the conversion is
+ ambiguous. This routine should eventually go away, and all
+ callers should use convert_to_pointer_real. */
+tree
+convert_pointer_to (binfo, expr)
+ tree binfo, expr;
+{
+ tree type;
+
+ if (TREE_CODE (binfo) == TREE_VEC)
+ type = BINFO_TYPE (binfo);
+ else if (IS_AGGR_TYPE (binfo))
+ type = binfo;
+ else
+ type = binfo;
+ return convert_pointer_to_real (type, expr);
+}
+
+/* Same as above, but don't abort if we get an "ambiguous" baseclass.
+ There's only one virtual baseclass we are looking for, and once
+ we find one such virtual baseclass, we have found them all. */
+
+tree
+convert_pointer_to_vbase (binfo, expr)
+ tree binfo;
+ tree expr;
+{
+ tree intype = TREE_TYPE (TREE_TYPE (expr));
+ tree binfos = TYPE_BINFO_BASETYPES (intype);
+ int i;
+
+ for (i = TREE_VEC_LENGTH (binfos)-1; i >= 0; i--)
+ {
+ tree basetype = BINFO_TYPE (TREE_VEC_ELT (binfos, i));
+ if (BINFO_TYPE (binfo) == basetype)
+ return convert_pointer_to (binfo, expr);
+ if (binfo_member (BINFO_TYPE (binfo), CLASSTYPE_VBASECLASSES (basetype)))
+ return convert_pointer_to_vbase (binfo, convert_pointer_to (basetype, expr));
+ }
+ my_friendly_abort (6);
+ /* NOTREACHED */
+ return NULL_TREE;
+}
+
+tree
+cp_convert (type, expr, convtype, flags)
+ tree type, expr;
+ int convtype, flags;
+{
+ register tree e = expr;
+ register enum tree_code code = TREE_CODE (type);
+
+ if (type == TREE_TYPE (e)
+ || TREE_CODE (e) == ERROR_MARK)
+ return e;
+ if (TREE_CODE (TREE_TYPE (e)) == ERROR_MARK)
+ return error_mark_node;
+
+ /* Trivial conversion: cv-qualifiers do not matter on rvalues. */
+ if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (TREE_TYPE (e)))
+ return fold (build1 (NOP_EXPR, type, e));
+
+ if (code == VOID_TYPE && (convtype & CONV_STATIC))
+ return build1 (CONVERT_EXPR, type, e);
+
+#if 0
+ /* This is incorrect. A truncation can't be stripped this way.
+ Extensions will be stripped by the use of get_unwidened. */
+ if (TREE_CODE (e) == NOP_EXPR)
+ return convert (type, TREE_OPERAND (e, 0));
+#endif
+
+ /* Just convert to the type of the member. */
+ if (code == OFFSET_TYPE)
+ {
+ type = TREE_TYPE (type);
+ code = TREE_CODE (type);
+ }
+
+ if (code == REFERENCE_TYPE)
+ return fold (convert_to_reference (type, e, convtype, flags, NULL_TREE));
+ else if (TREE_CODE (TREE_TYPE (e)) == REFERENCE_TYPE)
+ e = convert_from_reference (e);
+
+ if (TREE_READONLY_DECL_P (e))
+ e = decl_constant_value (e);
+
+ if (INTEGRAL_CODE_P (code))
+ {
+ tree intype = TREE_TYPE (e);
+ enum tree_code form = TREE_CODE (intype);
+ /* enum = enum, enum = int, enum = float are all errors. */
+ if (flag_int_enum_equivalence == 0
+ && TREE_CODE (type) == ENUMERAL_TYPE
+ && ARITHMETIC_TYPE_P (intype))
+ {
+ cp_pedwarn ("conversion from `%#T' to `%#T'", intype, type);
+
+ if (flag_pedantic_errors)
+ return error_mark_node;
+ }
+ if (form == OFFSET_TYPE)
+ cp_error_at ("pointer-to-member expression object not composed with type `%D' object",
+ TYPE_NAME (TYPE_OFFSET_BASETYPE (intype)));
+ else if (IS_AGGR_TYPE (intype))
+ {
+ tree rval;
+ rval = build_type_conversion (CONVERT_EXPR, type, e, 1);
+ if (rval) return rval;
+ if (code == BOOLEAN_TYPE)
+ cp_error ("`%#T' used where a `bool' was expected", intype);
+ else
+ cp_error ("`%#T' used where an `int' was expected", intype);
+ return error_mark_node;
+ }
+ if (code == BOOLEAN_TYPE)
+ {
+ tree newe = truthvalue_conversion (e);
+ /* Avoid stupid (infinite) recursion from backend. */
+ if (TREE_CODE (newe) != NOP_EXPR || e != TREE_OPERAND (newe, 0))
+ e = newe;
+ if (TREE_TYPE (e) == bool_type_node)
+ return e;
+ else if (TREE_CODE (e) == INTEGER_CST)
+ {
+ if (e == integer_zero_node)
+ e = false_node;
+ else
+ e = true_node;
+ }
+ else
+ return build1 (NOP_EXPR, bool_type_node, e);
+ }
+ return fold (convert_to_integer (type, e));
+ }
+ if (code == POINTER_TYPE)
+ return fold (cp_convert_to_pointer (type, e));
+ if (code == REAL_TYPE)
+ {
+ if (IS_AGGR_TYPE (TREE_TYPE (e)))
+ {
+ tree rval;
+ rval = build_type_conversion (CONVERT_EXPR, type, e, 1);
+ if (rval)
+ return rval;
+ else
+ cp_error ("`%#T' used where a floating point value was expected",
+ TREE_TYPE (e));
+ }
+ return fold (convert_to_real (type, e));
+ }
+
+ /* New C++ semantics: since assignment is now based on
+ memberwise copying, if the rhs type is derived from the
+ lhs type, then we may still do a conversion. */
+ if (IS_AGGR_TYPE_CODE (code))
+ {
+ tree dtype = TREE_TYPE (e);
+
+ if (TREE_CODE (dtype) == REFERENCE_TYPE)
+ {
+ e = convert_from_reference (e);
+ dtype = TREE_TYPE (e);
+ }
+ dtype = TYPE_MAIN_VARIANT (dtype);
+
+ /* Conversion of object pointers or signature pointers/references
+ to signature pointers/references. */
+
+ if (TYPE_LANG_SPECIFIC (type)
+ && (IS_SIGNATURE_POINTER (type) || IS_SIGNATURE_REFERENCE (type)))
+ {
+ tree constructor = build_signature_pointer_constructor (type, expr);
+ tree sig_ty = SIGNATURE_TYPE (type);
+ tree sig_ptr;
+
+ if (constructor == error_mark_node)
+ return error_mark_node;
+
+ sig_ptr = get_temp_name (type, 1);
+ DECL_INITIAL (sig_ptr) = constructor;
+ CLEAR_SIGNATURE (sig_ty);
+ finish_decl (sig_ptr, constructor, 0, 0);
+ SET_SIGNATURE (sig_ty);
+ TREE_READONLY (sig_ptr) = 1;
+
+ return sig_ptr;
+ }
+
+ /* Conversion between aggregate types. New C++ semantics allow
+ objects of derived type to be cast to objects of base type.
+ Old semantics only allowed this between pointers.
+
+ There may be some ambiguity between using a constructor
+ vs. using a type conversion operator when both apply. */
+
+ else if (IS_AGGR_TYPE (dtype))
+ {
+ tree binfo;
+
+ tree conversion;
+
+ if (! DERIVED_FROM_P (type, dtype) && TYPE_HAS_CONVERSION (dtype))
+ conversion = build_type_conversion (CONVERT_EXPR, type, e, 1);
+ else
+ conversion = NULL_TREE;
+
+ if (TYPE_HAS_CONSTRUCTOR (type))
+ {
+ tree rval = build_method_call (NULL_TREE, constructor_name_full (type),
+ build_tree_list (NULL_TREE, e),
+ TYPE_BINFO (type),
+ conversion ? LOOKUP_NO_CONVERSION : 0);
+
+ if (rval != error_mark_node)
+ {
+ if (conversion)
+ {
+ error ("both constructor and type conversion operator apply");
+ return error_mark_node;
+ }
+ /* call to constructor successful. */
+ rval = build_cplus_new (type, rval, 1);
+ return rval;
+ }
+ }
+ /* Type conversion successful/applies. */
+ if (conversion)
+ {
+ if (conversion == error_mark_node)
+ error ("ambiguous pointer conversion");
+ return conversion;
+ }
+
+ /* now try normal C++ assignment semantics. */
+ binfo = TYPE_BINFO (dtype);
+ if (BINFO_TYPE (binfo) == type
+ || (binfo = get_binfo (type, dtype, 1)))
+ {
+ if (binfo == error_mark_node)
+ return error_mark_node;
+ }
+ if (binfo != NULL_TREE)
+ {
+ if (lvalue_p (e))
+ {
+ e = build_unary_op (ADDR_EXPR, e, 0);
+
+ if (! BINFO_OFFSET_ZEROP (binfo))
+ e = build (PLUS_EXPR, TYPE_POINTER_TO (type),
+ e, BINFO_OFFSET (binfo));
+ return build1 (INDIRECT_REF, type, e);
+ }
+
+ sorry ("addressable aggregates");
+ return error_mark_node;
+ }
+ error ("conversion between incompatible aggregate types requested");
+ return error_mark_node;
+ }
+ /* conversion from non-aggregate to aggregate type requires
+ constructor. */
+ else if (TYPE_HAS_CONSTRUCTOR (type))
+ {
+ tree rval;
+ tree init = build_method_call (NULL_TREE, constructor_name_full (type),
+ build_tree_list (NULL_TREE, e),
+ TYPE_BINFO (type), LOOKUP_NORMAL);
+ if (init == error_mark_node)
+ {
+ cp_error ("in conversion to type `%T'", type);
+ return error_mark_node;
+ }
+ /* We can't pass 1 to the with_cleanup_p arg here, because that
+ screws up passing classes by value. */
+ rval = build_cplus_new (type, init, 0);
+ return rval;
+ }
+ }
+
+ /* If TYPE or TREE_TYPE (E) is not on the permanent_obstack,
+ then the it won't be hashed and hence compare as not equal,
+ even when it is. */
+ if (code == ARRAY_TYPE
+ && TREE_TYPE (TREE_TYPE (e)) == TREE_TYPE (type)
+ && index_type_equal (TYPE_DOMAIN (TREE_TYPE (e)), TYPE_DOMAIN (type)))
+ return e;
+
+ cp_error ("conversion from `%T' to non-scalar type `%T' requested",
+ TREE_TYPE (expr), type);
+ return error_mark_node;
+}
+
+/* Create an expression whose value is that of EXPR,
+ converted to type TYPE. The TREE_TYPE of the value
+ is always TYPE. This function implements all reasonable
+ conversions; callers should filter out those that are
+ not permitted by the language being compiled. */
+
+tree
+convert (type, expr)
+ tree type, expr;
+{
+ return cp_convert (type, expr, CONV_OLD_CONVERT, 0);
+}
+
+/* Like convert, except permit conversions to take place which
+ are not normally allowed due to access restrictions
+ (such as conversion from sub-type to private super-type). */
+tree
+convert_force (type, expr)
+ tree type;
+ tree expr;
+{
+ register tree e = expr;
+ register enum tree_code code = TREE_CODE (type);
+
+ if (code == REFERENCE_TYPE)
+ return fold (convert_to_reference (type, e, CONV_C_CAST, LOOKUP_COMPLAIN,
+ NULL_TREE));
+ else if (TREE_CODE (TREE_TYPE (e)) == REFERENCE_TYPE)
+ e = convert_from_reference (e);
+
+ if (code == POINTER_TYPE)
+ return fold (convert_to_pointer_force (type, e));
+
+ /* From typeck.c convert_for_assignment */
+ if (((TREE_CODE (TREE_TYPE (e)) == POINTER_TYPE && TREE_CODE (e) == ADDR_EXPR
+ && TREE_CODE (TREE_TYPE (e)) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (TREE_TYPE (e))) == METHOD_TYPE)
+ || integer_zerop (e)
+ || TYPE_PTRMEMFUNC_P (TREE_TYPE (e)))
+ && TYPE_PTRMEMFUNC_P (type))
+ {
+ /* compatible pointer to member functions. */
+ return build_ptrmemfunc (TYPE_PTRMEMFUNC_FN_TYPE (type), e, 1);
+ }
+ {
+ int old_equiv = flag_int_enum_equivalence;
+ flag_int_enum_equivalence = 1;
+ e = convert (type, e);
+ flag_int_enum_equivalence = old_equiv;
+ }
+ return e;
+}
+
+/* Subroutine of build_type_conversion. */
+static tree
+build_type_conversion_1 (xtype, basetype, expr, typename, for_sure)
+ tree xtype, basetype;
+ tree expr;
+ tree typename;
+ int for_sure;
+{
+ tree rval;
+ int flags;
+
+ if (for_sure == 0)
+ flags = LOOKUP_PROTECT;
+ else
+ flags = LOOKUP_NORMAL;
+
+ rval = build_method_call (expr, typename, NULL_TREE, NULL_TREE, flags);
+ if (rval == error_mark_node)
+ {
+ if (for_sure == 0)
+ return NULL_TREE;
+ return error_mark_node;
+ }
+ if (TREE_CODE (TREE_TYPE (rval)) == REFERENCE_TYPE
+ && TREE_CODE (xtype) != REFERENCE_TYPE)
+ rval = default_conversion (rval);
+
+ if (warn_cast_qual
+ && TREE_TYPE (xtype)
+ && (TREE_READONLY (TREE_TYPE (TREE_TYPE (rval)))
+ > TREE_READONLY (TREE_TYPE (xtype))))
+ warning ("user-defined conversion casting away `const'");
+ return convert (xtype, rval);
+}
+
+/* Convert an aggregate EXPR to type XTYPE. If a conversion
+ exists, return the attempted conversion. This may
+ return ERROR_MARK_NODE if the conversion is not
+ allowed (references private members, etc).
+ If no conversion exists, NULL_TREE is returned.
+
+ If (FOR_SURE & 1) is non-zero, then we allow this type conversion
+ to take place immediately. Otherwise, we build a SAVE_EXPR
+ which can be evaluated if the results are ever needed.
+
+ If FOR_SURE >= 2, then we only look for exact conversions.
+
+ TYPE may be a reference type, in which case we first look
+ for something that will convert to a reference type. If
+ that fails, we will try to look for something of the
+ reference's target type, and then return a reference to that. */
+tree
+build_type_conversion (code, xtype, expr, for_sure)
+ enum tree_code code;
+ tree xtype, expr;
+ int for_sure;
+{
+ /* C++: check to see if we can convert this aggregate type
+ into the required scalar type. */
+ tree type, type_default;
+ tree typename = build_typename_overload (xtype), *typenames;
+ int n_variants = 0;
+ tree basetype, save_basetype;
+ tree rval;
+ int exact_conversion = for_sure >= 2;
+ for_sure &= 1;
+
+ if (expr == error_mark_node)
+ return error_mark_node;
+
+ basetype = TREE_TYPE (expr);
+ if (TREE_CODE (basetype) == REFERENCE_TYPE)
+ basetype = TREE_TYPE (basetype);
+
+ if (TYPE_PTRMEMFUNC_P (basetype) && TREE_CODE (xtype) == BOOLEAN_TYPE)
+ {
+ /* We convert a pointer to member function into a boolean,
+ by just checking the index value, for == 0, we want false, for
+ != 0, we want true. */
+ return convert (xtype, build_component_ref (expr, index_identifier, 0, 0));
+ }
+
+ basetype = TYPE_MAIN_VARIANT (basetype);
+ if (! TYPE_LANG_SPECIFIC (basetype) || ! TYPE_HAS_CONVERSION (basetype))
+ return NULL_TREE;
+
+ if (TREE_CODE (xtype) == POINTER_TYPE
+ || TREE_CODE (xtype) == REFERENCE_TYPE)
+ {
+ /* Prepare to match a variant of this type. */
+ type = TYPE_MAIN_VARIANT (TREE_TYPE (xtype));
+ for (n_variants = 0; type; type = TYPE_NEXT_VARIANT (type))
+ n_variants++;
+ typenames = (tree *)alloca (n_variants * sizeof (tree));
+ for (n_variants = 0, type = TYPE_MAIN_VARIANT (TREE_TYPE (xtype));
+ type; n_variants++, type = TYPE_NEXT_VARIANT (type))
+ {
+ if (type == TREE_TYPE (xtype))
+ typenames[n_variants] = typename;
+ else if (TREE_CODE (xtype) == POINTER_TYPE)
+ typenames[n_variants] = build_typename_overload (build_pointer_type (type));
+ else
+ typenames[n_variants] = build_typename_overload (build_reference_type (type));
+ }
+ }
+
+ save_basetype = basetype;
+ type = xtype;
+
+ while (TYPE_HAS_CONVERSION (basetype))
+ {
+ int i;
+ if (lookup_fnfields (TYPE_BINFO (basetype), typename, 0))
+ return build_type_conversion_1 (xtype, basetype, expr, typename, for_sure);
+ for (i = 0; i < n_variants; i++)
+ if (typenames[i] != typename
+ && lookup_fnfields (TYPE_BINFO (basetype), typenames[i], 0))
+ return build_type_conversion_1 (xtype, basetype, expr, typenames[i], for_sure);
+
+ if (TYPE_BINFO_BASETYPES (basetype))
+ basetype = TYPE_BINFO_BASETYPE (basetype, 0);
+ else
+ break;
+ }
+
+ if (TREE_CODE (type) == REFERENCE_TYPE)
+ {
+#if 0
+ /* Only reference variable initializations can use a temporary; this
+ must be handled elsewhere (like convert_to_reference and
+ compute_conversion_costs). */
+
+ type = TYPE_MAIN_VARIANT (TREE_TYPE (type));
+ typename = build_typename_overload (type);
+ basetype = save_basetype;
+
+ /* May need to build a temporary for this. */
+ while (TYPE_HAS_CONVERSION (basetype))
+ {
+ if (lookup_fnfields (TYPE_BINFO (basetype), typename, 0))
+ {
+ int flags;
+
+ if (for_sure == 0)
+ flags = LOOKUP_PROTECT;
+ else
+ flags = LOOKUP_NORMAL;
+ rval = build_method_call (expr,
+ constructor_name_full (typename),
+ NULL_TREE, NULL_TREE, flags);
+ if (rval == error_mark_node)
+ {
+ if (for_sure == 0)
+ return NULL_TREE;
+ return error_mark_node;
+ }
+
+ return convert (xtype, rval);
+ }
+ if (TYPE_BINFO_BASETYPES (basetype))
+ basetype = TYPE_BINFO_BASETYPE (basetype, 0);
+ else
+ break;
+ }
+#endif
+ /* No free conversions for reference types, right?. */
+ return NULL_TREE;
+ }
+
+ if (exact_conversion)
+ return NULL_TREE;
+
+ if (TREE_CODE (type) == BOOLEAN_TYPE)
+ {
+ tree as_int = build_type_conversion (code, long_long_unsigned_type_node, expr, 0);
+ tree as_ptr = build_type_conversion (code, ptr_type_node, expr, 0);
+ /* We are missing the conversion to pointer to member type. */
+ /* We are missing the conversion to floating type. */
+ if (as_int && as_ptr && for_sure)
+ {
+ cp_error ("ambiguous conversion from `%T' to `bool', can convert to integral type or pointer", TREE_TYPE (expr));
+ return error_mark_node;
+ }
+ if (as_int)
+ {
+ as_int = build_type_conversion (code, long_long_unsigned_type_node, expr, for_sure+exact_conversion*2);
+ return convert (xtype, as_int);
+ }
+ if (as_ptr)
+ {
+ as_ptr = build_type_conversion (code, ptr_type_node, expr, for_sure+exact_conversion*2);
+ return convert (xtype, as_ptr);
+ }
+ return NULL_TREE;
+ }
+
+ /* No perfect match found, try default. */
+#if 0 /* This is wrong; there is no standard conversion from void* to
+ anything. -jason */
+ if (code == CONVERT_EXPR && TREE_CODE (type) == POINTER_TYPE)
+ type_default = ptr_type_node;
+ else
+#endif
+ if (type == void_type_node)
+ return NULL_TREE;
+ else
+ {
+ tree tmp = default_conversion (build1 (NOP_EXPR, type, integer_zero_node));
+ if (tmp == error_mark_node)
+ return NULL_TREE;
+ type_default = TREE_TYPE (tmp);
+ }
+
+ basetype = save_basetype;
+
+ if (type_default != type)
+ {
+ type = type_default;
+ typename = build_typename_overload (type);
+
+ while (TYPE_HAS_CONVERSION (basetype))
+ {
+ if (lookup_fnfields (TYPE_BINFO (basetype), typename, 0))
+ return build_type_conversion_1 (xtype, basetype, expr, typename, for_sure);
+ if (TYPE_BINFO_BASETYPES (basetype))
+ basetype = TYPE_BINFO_BASETYPE (basetype, 0);
+ else
+ break;
+ }
+ }
+
+ if (TREE_CODE (type) == POINTER_TYPE && TYPE_READONLY (TREE_TYPE (type)))
+ {
+ /* Try converting to some other const pointer type and then using
+ standard conversions. */
+
+ while (TYPE_HAS_CONVERSION (basetype))
+ {
+ if (CLASSTYPE_CONVERSION (basetype, constptr_conv) != 0)
+ {
+ if (CLASSTYPE_CONVERSION (basetype, constptr_conv) == error_mark_node)
+ return error_mark_node;
+ typename = DECL_NAME (CLASSTYPE_CONVERSION (basetype, constptr_conv));
+ return build_type_conversion_1 (xtype, basetype, expr, typename, for_sure);
+ }
+ if (TYPE_BINFO_BASETYPES (basetype))
+ basetype = TYPE_BINFO_BASETYPE (basetype, 0);
+ else
+ break;
+ }
+ }
+ if (TREE_CODE (type) == POINTER_TYPE)
+ {
+ /* Try converting to some other pointer type and then using standard
+ conversions. */
+
+ while (TYPE_HAS_CONVERSION (basetype))
+ {
+ if (CLASSTYPE_CONVERSION (basetype, ptr_conv) != 0)
+ {
+ if (CLASSTYPE_CONVERSION (basetype, ptr_conv) == error_mark_node)
+ return error_mark_node;
+ typename = DECL_NAME (CLASSTYPE_CONVERSION (basetype, ptr_conv));
+ return build_type_conversion_1 (xtype, basetype, expr, typename, for_sure);
+ }
+ if (TYPE_BINFO_BASETYPES (basetype))
+ basetype = TYPE_BINFO_BASETYPE (basetype, 0);
+ else
+ break;
+ }
+ }
+
+ /* Use the longer or shorter conversion that is appropriate. Have
+ to check against 0 because the conversion may come from a baseclass. */
+ if (TREE_CODE (type) == INTEGER_TYPE
+ && TYPE_HAS_INT_CONVERSION (basetype)
+ && CLASSTYPE_CONVERSION (basetype, int_conv) != 0
+ && CLASSTYPE_CONVERSION (basetype, int_conv) != error_mark_node)
+ {
+ typename = DECL_NAME (CLASSTYPE_CONVERSION (basetype, int_conv));
+ return build_type_conversion_1 (xtype, basetype, expr, typename, for_sure);
+ }
+
+ if (TREE_CODE (type) == REAL_TYPE
+ && TYPE_HAS_REAL_CONVERSION (basetype)
+ && CLASSTYPE_CONVERSION (basetype, real_conv) != 0
+ && CLASSTYPE_CONVERSION (basetype, real_conv) != error_mark_node)
+ {
+ /* Only accept using an operator double() if there isn't a conflicting
+ operator int(). */
+ if (TYPE_HAS_INT_CONVERSION (basetype))
+ {
+ if (for_sure)
+ {
+ cp_error ("two possible conversions for type `%T'", type);
+ return error_mark_node;
+ }
+ else
+ return NULL_TREE;
+ }
+
+ typename = DECL_NAME (CLASSTYPE_CONVERSION (basetype, real_conv));
+ return build_type_conversion_1 (xtype, basetype, expr, typename, for_sure);
+ }
+
+ /* THESE ARE TOTAL KLUDGES. */
+ /* Default promotion yields no new alternatives, try
+ conversions which are anti-default, such as
+
+ double -> float or int -> unsigned or unsigned -> long
+
+ */
+ if (type_default == type
+ && (INTEGRAL_TYPE_P (type) || TREE_CODE (type) == REAL_TYPE))
+ {
+ int not_again = 0;
+
+ if (type == double_type_node)
+ typename = build_typename_overload (float_type_node);
+ else if (type == integer_type_node)
+ typename = build_typename_overload (unsigned_type_node);
+ else if (type == unsigned_type_node)
+ typename = build_typename_overload (long_integer_type_node);
+
+ again:
+ basetype = save_basetype;
+ while (TYPE_HAS_CONVERSION (basetype))
+ {
+ if (lookup_fnfields (TYPE_BINFO (basetype), typename, 0))
+ return build_type_conversion_1 (xtype, basetype, expr, typename, for_sure);
+ if (TYPE_BINFO_BASETYPES (basetype))
+ basetype = TYPE_BINFO_BASETYPE (basetype, 0);
+ else
+ break;
+ }
+ if (! not_again)
+ {
+ if (type == integer_type_node)
+ {
+ typename = build_typename_overload (long_integer_type_node);
+ not_again = 1;
+ goto again;
+ }
+ else
+ {
+ typename = build_typename_overload (integer_type_node);
+ not_again = 1;
+ goto again;
+ }
+ }
+ }
+
+ /* Now, try C promotions...
+
+ float -> int
+ int -> float */
+
+ basetype = save_basetype;
+ if (TREE_CODE (type) == REAL_TYPE)
+ type = integer_type_node;
+ else if (TREE_CODE (type) == INTEGER_TYPE)
+ if (TYPE_HAS_REAL_CONVERSION (basetype))
+ type = double_type_node;
+ else
+ return NULL_TREE;
+ else
+ return NULL_TREE;
+
+ typename = build_typename_overload (type);
+ while (TYPE_HAS_CONVERSION (basetype))
+ {
+ if (lookup_fnfields (TYPE_BINFO (basetype), typename, 0))
+ {
+ rval = build_type_conversion_1 (xtype, basetype, expr, typename, for_sure);
+ return rval;
+ }
+ if (TYPE_BINFO_BASETYPES (basetype))
+ basetype = TYPE_BINFO_BASETYPE (basetype, 0);
+ else
+ break;
+ }
+
+ return NULL_TREE;
+}
+
+/* Must convert two aggregate types to non-aggregate type.
+ Attempts to find a non-ambiguous, "best" type conversion.
+
+ Return 1 on success, 0 on failure.
+
+ @@ What are the real semantics of this supposed to be??? */
+int
+build_default_binary_type_conversion (code, arg1, arg2)
+ enum tree_code code;
+ tree *arg1, *arg2;
+{
+ tree type1 = TREE_TYPE (*arg1);
+ tree type2 = TREE_TYPE (*arg2);
+
+ if (TREE_CODE (type1) == REFERENCE_TYPE
+ || TREE_CODE (type1) == POINTER_TYPE)
+ type1 = TREE_TYPE (type1);
+ if (TREE_CODE (type2) == REFERENCE_TYPE
+ || TREE_CODE (type2) == POINTER_TYPE)
+ type2 = TREE_TYPE (type2);
+
+ if (TREE_CODE (TYPE_NAME (type1)) != TYPE_DECL)
+ {
+ tree decl = typedecl_for_tag (type1);
+ if (decl)
+ error ("type conversion nonexistent for type `%s'",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ else
+ error ("type conversion nonexistent for non-C++ type");
+ return 0;
+ }
+ if (TREE_CODE (TYPE_NAME (type2)) != TYPE_DECL)
+ {
+ tree decl = typedecl_for_tag (type2);
+ if (decl)
+ error ("type conversion nonexistent for type `%s'",
+ IDENTIFIER_POINTER (decl));
+ else
+ error ("type conversion nonexistent for non-C++ type");
+ return 0;
+ }
+
+ if (!IS_AGGR_TYPE (type1) || !TYPE_HAS_CONVERSION (type1))
+ {
+ if (!IS_AGGR_TYPE (type2) || !TYPE_HAS_CONVERSION (type2))
+ cp_error ("no conversion from `%T' and `%T' to types with default `%O' ",
+ type1, type2, code);
+ else
+ cp_error ("no conversion from `%T' to type with default `%O'",
+ type1, code);
+ return 0;
+ }
+ else if (!IS_AGGR_TYPE (type2) || !TYPE_HAS_CONVERSION (type2))
+ {
+ cp_error ("no conversion from `%T' to type with default `%O'",
+ type2, code);
+ return 0;
+ }
+
+ if (code == TRUTH_ANDIF_EXPR
+ || code == TRUTH_ORIF_EXPR)
+ {
+ *arg1 = convert (bool_type_node, *arg1);
+ *arg2 = convert (bool_type_node, *arg2);
+ }
+ else if (TYPE_HAS_INT_CONVERSION (type1))
+ {
+ if (TYPE_HAS_REAL_CONVERSION (type1))
+ cp_pedwarn ("ambiguous type conversion for type `%T', defaulting to int",
+ type1);
+ *arg1 = build_type_conversion (code, integer_type_node, *arg1, 1);
+ *arg2 = build_type_conversion (code, integer_type_node, *arg2, 1);
+ }
+ else if (TYPE_HAS_REAL_CONVERSION (type1))
+ {
+ *arg1 = build_type_conversion (code, double_type_node, *arg1, 1);
+ *arg2 = build_type_conversion (code, double_type_node, *arg2, 1);
+ }
+ else
+ {
+ *arg1 = build_type_conversion (code, ptr_type_node, *arg1, 1);
+ if (*arg1 == error_mark_node)
+ error ("ambiguous pointer conversion");
+ *arg2 = build_type_conversion (code, ptr_type_node, *arg2, 1);
+ if (*arg1 != error_mark_node && *arg2 == error_mark_node)
+ error ("ambiguous pointer conversion");
+ }
+ if (*arg1 == 0)
+ {
+ if (*arg2 == 0 && type1 != type2)
+ cp_error ("default type conversion for types `%T' and `%T' failed",
+ type1, type2);
+ else
+ cp_error ("default type conversion for type `%T' failed", type1);
+ return 0;
+ }
+ else if (*arg2 == 0)
+ {
+ cp_error ("default type conversion for type `%T' failed", type2);
+ return 0;
+ }
+ return 1;
+}
+
+/* Must convert an aggregate type to non-aggregate type.
+ Attempts to find a non-ambiguous, "best" type conversion.
+
+ Return 1 on success, 0 on failure.
+
+ The type of the argument is expected to be of aggregate type here.
+
+ @@ What are the real semantics of this supposed to be??? */
+int
+build_default_unary_type_conversion (code, arg)
+ enum tree_code code;
+ tree *arg;
+{
+ tree type = TREE_TYPE (*arg);
+
+ if (! TYPE_HAS_CONVERSION (type))
+ {
+ cp_error ("type conversion required for type `%T'", type);
+ return 0;
+ }
+
+ if (code == TRUTH_NOT_EXPR)
+ *arg = convert (bool_type_node, *arg);
+ else if (TYPE_HAS_INT_CONVERSION (type))
+ {
+ if (TYPE_HAS_REAL_CONVERSION (type))
+ cp_pedwarn ("ambiguous type conversion for type `%T', defaulting to int",
+ type);
+ *arg = build_type_conversion (code, integer_type_node, *arg, 1);
+ }
+ else if (TYPE_HAS_REAL_CONVERSION (type))
+ *arg = build_type_conversion (code, double_type_node, *arg, 1);
+ else
+ {
+ *arg = build_type_conversion (code, ptr_type_node, *arg, 1);
+ if (*arg == error_mark_node)
+ error ("ambiguous pointer conversion");
+ }
+ if (*arg == NULL_TREE)
+ {
+ cp_error ("default type conversion for type `%T' failed", type);
+ return 0;
+ }
+ return 1;
+}
+
+/* Implements integral promotion (4.1) and float->double promotion. */
+tree
+type_promotes_to (type)
+ tree type;
+{
+ int constp = TYPE_READONLY (type);
+ int volatilep = TYPE_VOLATILE (type);
+ type = TYPE_MAIN_VARIANT (type);
+
+ /* bool always promotes to int (not unsigned), even if it's the same
+ size. */
+ if (type == bool_type_node)
+ type = integer_type_node;
+
+ /* Normally convert enums to int, but convert wide enums to something
+ wider. */
+ else if (TREE_CODE (type) == ENUMERAL_TYPE
+ || type == wchar_type_node)
+ type = type_for_size
+ (MAX (TYPE_PRECISION (type), TYPE_PRECISION (integer_type_node)),
+ (flag_traditional
+ || (TYPE_PRECISION (type) >= TYPE_PRECISION (integer_type_node)))
+ && TREE_UNSIGNED (type));
+ else if (C_PROMOTING_INTEGER_TYPE_P (type))
+ {
+ /* Traditionally, unsignedness is preserved in default promotions.
+ Otherwise, retain unsignedness if really not getting bigger. */
+ if (TREE_UNSIGNED (type)
+ && (flag_traditional
+ || TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node)))
+ type = unsigned_type_node;
+ else
+ type = integer_type_node;
+ }
+ else if (type == float_type_node)
+ type = double_type_node;
+
+ return c_build_type_variant (type, constp, volatilep);
+}
diff --git a/gnu/usr.bin/cc/cc1plus/decl.c b/gnu/usr.bin/cc/cc1plus/decl.c
new file mode 100644
index 0000000..0690340
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/decl.c
@@ -0,0 +1,12030 @@
+/* Process declarations and variables for C compiler.
+ Copyright (C) 1988, 1992, 1993, 1994 Free Software Foundation, Inc.
+ Hacked by Michael Tiemann (tiemann@cygnus.com)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* Process declarations and symbol lookup for C front end.
+ Also constructs types; the standard scalar types at initialization,
+ and structure, union, array and enum types when they are declared. */
+
+/* ??? not all decl nodes are given the most useful possible
+ line numbers. For example, the CONST_DECLs for enum values. */
+
+#include <stdio.h>
+#include "config.h"
+#include "tree.h"
+#include "rtl.h"
+#include "flags.h"
+#include "cp-tree.h"
+#include "decl.h"
+#include "lex.h"
+#include <sys/types.h>
+#include <signal.h>
+#include "obstack.h"
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+extern tree builtin_return_address_fndecl;
+
+extern struct obstack permanent_obstack;
+
+extern int current_class_depth;
+
+extern tree cleanups_this_call;
+
+/* Stack of places to restore the search obstack back to. */
+
+/* Obstack used for remembering local class declarations (like
+ enums and static (const) members. */
+#include "stack.h"
+static struct obstack decl_obstack;
+static struct stack_level *decl_stack;
+
+#ifndef CHAR_TYPE_SIZE
+#define CHAR_TYPE_SIZE BITS_PER_UNIT
+#endif
+
+#ifndef SHORT_TYPE_SIZE
+#define SHORT_TYPE_SIZE (BITS_PER_UNIT * MIN ((UNITS_PER_WORD + 1) / 2, 2))
+#endif
+
+#ifndef INT_TYPE_SIZE
+#define INT_TYPE_SIZE BITS_PER_WORD
+#endif
+
+#ifndef LONG_TYPE_SIZE
+#define LONG_TYPE_SIZE BITS_PER_WORD
+#endif
+
+#ifndef LONG_LONG_TYPE_SIZE
+#define LONG_LONG_TYPE_SIZE (BITS_PER_WORD * 2)
+#endif
+
+#ifndef WCHAR_UNSIGNED
+#define WCHAR_UNSIGNED 0
+#endif
+
+#ifndef FLOAT_TYPE_SIZE
+#define FLOAT_TYPE_SIZE BITS_PER_WORD
+#endif
+
+#ifndef DOUBLE_TYPE_SIZE
+#define DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2)
+#endif
+
+#ifndef LONG_DOUBLE_TYPE_SIZE
+#define LONG_DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2)
+#endif
+
+/* We let tm.h override the types used here, to handle trivial differences
+ such as the choice of unsigned int or long unsigned int for size_t.
+ When machines start needing nontrivial differences in the size type,
+ it would be best to do something here to figure out automatically
+ from other information what type to use. */
+
+#ifndef SIZE_TYPE
+#define SIZE_TYPE "long unsigned int"
+#endif
+
+#ifndef PTRDIFF_TYPE
+#define PTRDIFF_TYPE "long int"
+#endif
+
+#ifndef WCHAR_TYPE
+#define WCHAR_TYPE "int"
+#endif
+
+#define builtin_function(NAME, TYPE, CODE, LIBNAME) \
+ define_function (NAME, TYPE, CODE, (void (*)())pushdecl, LIBNAME)
+#define auto_function(NAME, TYPE, CODE) \
+ do { \
+ tree __name = NAME; \
+ tree __type = TYPE; \
+ define_function (IDENTIFIER_POINTER (__name), __type, CODE, \
+ (void (*)())push_overloaded_decl_1, \
+ IDENTIFIER_POINTER (build_decl_overload (__name, TYPE_ARG_TYPES (__type), 0)));\
+ } while (0)
+
+static tree grokparms PROTO((tree, int));
+static tree lookup_nested_type PROTO((tree, tree));
+static char *redeclaration_error_message PROTO((tree, tree));
+static void grok_op_properties PROTO((tree, int, int));
+
+tree define_function
+ PROTO((char *, tree, enum built_in_function, void (*)(), char *));
+
+/* a node which has tree code ERROR_MARK, and whose type is itself.
+ All erroneous expressions are replaced with this node. All functions
+ that accept nodes as arguments should avoid generating error messages
+ if this node is one of the arguments, since it is undesirable to get
+ multiple error messages from one error in the input. */
+
+tree error_mark_node;
+
+/* Erroneous argument lists can use this *IFF* they do not modify it. */
+tree error_mark_list;
+
+/* INTEGER_TYPE and REAL_TYPE nodes for the standard data types */
+
+tree short_integer_type_node;
+tree integer_type_node;
+tree long_integer_type_node;
+tree long_long_integer_type_node;
+
+tree short_unsigned_type_node;
+tree unsigned_type_node;
+tree long_unsigned_type_node;
+tree long_long_unsigned_type_node;
+
+tree ptrdiff_type_node;
+
+tree unsigned_char_type_node;
+tree signed_char_type_node;
+tree char_type_node;
+tree wchar_type_node;
+tree signed_wchar_type_node;
+tree unsigned_wchar_type_node;
+
+tree float_type_node;
+tree double_type_node;
+tree long_double_type_node;
+
+tree intQI_type_node;
+tree intHI_type_node;
+tree intSI_type_node;
+tree intDI_type_node;
+
+tree unsigned_intQI_type_node;
+tree unsigned_intHI_type_node;
+tree unsigned_intSI_type_node;
+tree unsigned_intDI_type_node;
+
+/* a VOID_TYPE node, and the same, packaged in a TREE_LIST. */
+
+tree void_type_node, void_list_node;
+tree void_zero_node;
+
+/* Nodes for types `void *' and `const void *'. */
+
+tree ptr_type_node, const_ptr_type_node;
+
+/* Nodes for types `char *' and `const char *'. */
+
+tree string_type_node, const_string_type_node;
+
+/* Type `char[256]' or something like it.
+ Used when an array of char is needed and the size is irrelevant. */
+
+tree char_array_type_node;
+
+/* Type `int[256]' or something like it.
+ Used when an array of int needed and the size is irrelevant. */
+
+tree int_array_type_node;
+
+/* Type `wchar_t[256]' or something like it.
+ Used when a wide string literal is created. */
+
+tree wchar_array_type_node;
+
+/* The bool data type, and constants */
+tree bool_type_node, true_node, false_node;
+
+/* type `int ()' -- used for implicit declaration of functions. */
+
+tree default_function_type;
+
+/* function types `double (double)' and `double (double, double)', etc. */
+
+tree double_ftype_double, double_ftype_double_double;
+tree int_ftype_int, long_ftype_long;
+
+/* Function type `void (void *, void *, int)' and similar ones. */
+
+tree void_ftype_ptr_ptr_int, int_ftype_ptr_ptr_int, void_ftype_ptr_int_int;
+
+/* Function type `char *(char *, char *)' and similar ones */
+tree string_ftype_ptr_ptr, int_ftype_string_string;
+
+/* Function type `size_t (const char *)' */
+tree sizet_ftype_string;
+
+/* Function type `int (const void *, const void *, size_t)' */
+tree int_ftype_cptr_cptr_sizet;
+
+/* C++ extensions */
+tree vtable_entry_type;
+tree delta_type_node;
+tree __t_desc_type_node, __i_desc_type_node, __m_desc_type_node;
+tree __t_desc_array_type, __i_desc_array_type, __m_desc_array_type;
+tree class_star_type_node;
+tree class_type_node, record_type_node, union_type_node, enum_type_node;
+tree exception_type_node, unknown_type_node;
+tree opaque_type_node, signature_type_node;
+tree sigtable_entry_type;
+tree maybe_gc_cleanup;
+
+/* Array type `vtable_entry_type[]' */
+tree vtbl_type_node;
+
+/* In a destructor, the point at which all derived class destroying
+ has been done, just before any base class destroying will be done. */
+
+tree dtor_label;
+
+/* In a constructor, the point at which we are ready to return
+ the pointer to the initialized object. */
+
+tree ctor_label;
+
+/* A FUNCTION_DECL which can call `abort'. Not necessarily the
+ one that the user will declare, but sufficient to be called
+ by routines that want to abort the program. */
+
+tree abort_fndecl;
+
+extern rtx cleanup_label, return_label;
+
+/* If original DECL_RESULT of current function was a register,
+ but due to being an addressable named return value, would up
+ on the stack, this variable holds the named return value's
+ original location. */
+rtx original_result_rtx;
+
+/* Sequence of insns which represents base initialization. */
+rtx base_init_insns;
+
+/* C++: Keep these around to reduce calls to `get_identifier'.
+ Identifiers for `this' in member functions and the auto-delete
+ parameter for destructors. */
+tree this_identifier, in_charge_identifier;
+/* Used in pointer to member functions, and in vtables. */
+tree pfn_identifier, index_identifier, delta_identifier, delta2_identifier;
+tree pfn_or_delta2_identifier;
+
+/* A list (chain of TREE_LIST nodes) of named label uses.
+ The TREE_PURPOSE field is the list of variables defined
+ the the label's scope defined at the point of use.
+ The TREE_VALUE field is the LABEL_DECL used.
+ The TREE_TYPE field holds `current_binding_level' at the
+ point of the label's use.
+
+ Used only for jumps to as-yet undefined labels, since
+ jumps to defined labels can have their validity checked
+ by stmt.c. */
+
+static tree named_label_uses;
+
+/* A list of objects which have constructors or destructors
+ which reside in the global scope. The decl is stored in
+ the TREE_VALUE slot and the initializer is stored
+ in the TREE_PURPOSE slot. */
+tree static_aggregates;
+
+/* -- end of C++ */
+
+/* Two expressions that are constants with value zero.
+ The first is of type `int', the second of type `void *'. */
+
+tree integer_zero_node;
+tree null_pointer_node;
+
+/* A node for the integer constants 1, 2, and 3. */
+
+tree integer_one_node, integer_two_node, integer_three_node;
+
+/* Nonzero if we have seen an invalid cross reference
+ to a struct, union, or enum, but not yet printed the message. */
+
+tree pending_invalid_xref;
+/* File and line to appear in the eventual error message. */
+char *pending_invalid_xref_file;
+int pending_invalid_xref_line;
+
+/* While defining an enum type, this is 1 plus the last enumerator
+ constant value. */
+
+static tree enum_next_value;
+
+/* Nonzero means that there was overflow computing enum_next_value. */
+
+static int enum_overflow;
+
+/* Parsing a function declarator leaves a list of parameter names
+ or a chain or parameter decls here. */
+
+tree last_function_parms;
+
+/* Parsing a function declarator leaves here a chain of structure
+ and enum types declared in the parmlist. */
+
+static tree last_function_parm_tags;
+
+/* After parsing the declarator that starts a function definition,
+ `start_function' puts here the list of parameter names or chain of decls.
+ `store_parm_decls' finds it here. */
+
+static tree current_function_parms;
+
+/* Similar, for last_function_parm_tags. */
+static tree current_function_parm_tags;
+
+/* A list (chain of TREE_LIST nodes) of all LABEL_DECLs in the function
+ that have names. Here so we can clear out their names' definitions
+ at the end of the function. */
+
+static tree named_labels;
+
+/* A list of LABEL_DECLs from outer contexts that are currently shadowed. */
+
+static tree shadowed_labels;
+
+#if 0 /* Not needed by C++ */
+/* Nonzero when store_parm_decls is called indicates a varargs function.
+ Value not meaningful after store_parm_decls. */
+
+static int c_function_varargs;
+#endif
+
+/* The FUNCTION_DECL for the function currently being compiled,
+ or 0 if between functions. */
+tree current_function_decl;
+
+/* Set to 0 at beginning of a function definition, set to 1 if
+ a return statement that specifies a return value is seen. */
+
+int current_function_returns_value;
+
+/* Set to 0 at beginning of a function definition, set to 1 if
+ a return statement with no argument is seen. */
+
+int current_function_returns_null;
+
+/* Set to 0 at beginning of a function definition, and whenever
+ a label (case or named) is defined. Set to value of expression
+ returned from function when that value can be transformed into
+ a named return value. */
+
+tree current_function_return_value;
+
+/* Set to nonzero by `grokdeclarator' for a function
+ whose return type is defaulted, if warnings for this are desired. */
+
+static int warn_about_return_type;
+
+/* Nonzero when starting a function declared `extern inline'. */
+
+static int current_extern_inline;
+
+/* Nonzero means give `double' the same size as `float'. */
+
+extern int flag_short_double;
+
+/* Nonzero means don't recognize any builtin functions. */
+
+extern int flag_no_builtin;
+
+/* Nonzero means disable GNU extensions. */
+
+extern int flag_ansi;
+
+/* Nonzero if we want to support huge (> 2^(sizeof(short)*8-1) bytes)
+ objects. */
+extern int flag_huge_objects;
+
+/* Nonzero if we want to conserve space in the .o files. We do this
+ by putting uninitialized data and runtime initialized data into
+ .common instead of .data at the expense of not flaging multiple
+ definitions. */
+extern int flag_conserve_space;
+
+/* Pointers to the base and current top of the language name stack. */
+
+extern tree *current_lang_base, *current_lang_stack;
+
+/* C and C++ flags are in decl2.c. */
+
+/* Set to 0 at beginning of a constructor, set to 1
+ if that function does an allocation before referencing its
+ instance variable. */
+int current_function_assigns_this;
+int current_function_just_assigned_this;
+
+/* Set to 0 at beginning of a function. Set non-zero when
+ store_parm_decls is called. Don't call store_parm_decls
+ if this flag is non-zero! */
+int current_function_parms_stored;
+
+/* Current end of entries in the gc obstack for stack pointer variables. */
+
+int current_function_obstack_index;
+
+/* Flag saying whether we have used the obstack in this function or not. */
+
+int current_function_obstack_usage;
+
+/* Flag used when debugging spew.c */
+
+extern int spew_debug;
+
+/* This is a copy of the class_shadowed list of the previous class binding
+ contour when at global scope. It's used to reset IDENTIFIER_CLASS_VALUEs
+ when entering another class scope (i.e. a cache miss). */
+extern tree previous_class_values;
+
+
+/* Allocate a level of searching. */
+struct stack_level *
+push_decl_level (stack, obstack)
+ struct stack_level *stack;
+ struct obstack *obstack;
+{
+ struct stack_level tem;
+ tem.prev = stack;
+
+ return push_stack_level (obstack, (char *)&tem, sizeof (tem));
+}
+
+/* For each binding contour we allocate a binding_level structure
+ * which records the names defined in that contour.
+ * Contours include:
+ * 0) the global one
+ * 1) one for each function definition,
+ * where internal declarations of the parameters appear.
+ * 2) one for each compound statement,
+ * to record its declarations.
+ *
+ * The current meaning of a name can be found by searching the levels from
+ * the current one out to the global one.
+ *
+ * Off to the side, may be the class_binding_level. This exists
+ * only to catch class-local declarations. It is otherwise
+ * nonexistent.
+ *
+ * Also there may be binding levels that catch cleanups that
+ * must be run when exceptions occur.
+ */
+
+/* Note that the information in the `names' component of the global contour
+ is duplicated in the IDENTIFIER_GLOBAL_VALUEs of all identifiers. */
+
+struct binding_level
+ {
+ /* A chain of _DECL nodes for all variables, constants, functions,
+ * and typedef types. These are in the reverse of the order supplied.
+ */
+ tree names;
+
+ /* A list of structure, union and enum definitions,
+ * for looking up tag names.
+ * It is a chain of TREE_LIST nodes, each of whose TREE_PURPOSE is a name,
+ * or NULL_TREE; and whose TREE_VALUE is a RECORD_TYPE, UNION_TYPE,
+ * or ENUMERAL_TYPE node.
+ *
+ * C++: the TREE_VALUE nodes can be simple types for component_bindings.
+ *
+ */
+ tree tags;
+
+ /* For each level, a list of shadowed outer-level local definitions
+ to be restored when this level is popped.
+ Each link is a TREE_LIST whose TREE_PURPOSE is an identifier and
+ whose TREE_VALUE is its old definition (a kind of ..._DECL node). */
+ tree shadowed;
+
+ /* Same, for IDENTIFIER_CLASS_VALUE. */
+ tree class_shadowed;
+
+ /* Same, for IDENTIFIER_TYPE_VALUE. */
+ tree type_shadowed;
+
+ /* For each level (except not the global one),
+ a chain of BLOCK nodes for all the levels
+ that were entered and exited one level down. */
+ tree blocks;
+
+ /* The BLOCK node for this level, if one has been preallocated.
+ If 0, the BLOCK is allocated (if needed) when the level is popped. */
+ tree this_block;
+
+ /* The binding level which this one is contained in (inherits from). */
+ struct binding_level *level_chain;
+
+ /* Number of decls in `names' that have incomplete
+ structure or union types. */
+ unsigned short n_incomplete;
+
+ /* 1 for the level that holds the parameters of a function.
+ 2 for the level that holds a class declaration.
+ 3 for levels that hold parameter declarations. */
+ unsigned parm_flag : 4;
+
+ /* 1 means make a BLOCK for this level regardless of all else.
+ 2 for temporary binding contours created by the compiler. */
+ unsigned keep : 3;
+
+ /* Nonzero if this level "doesn't exist" for tags. */
+ unsigned tag_transparent : 1;
+
+ /* Nonzero if this level can safely have additional
+ cleanup-needing variables added to it. */
+ unsigned more_cleanups_ok : 1;
+ unsigned have_cleanups : 1;
+
+ /* Nonzero if we should accept any name as an identifier in
+ this scope. This happens in some template definitions. */
+ unsigned accept_any : 1;
+
+ /* Nonzero if this level is for completing a template class definition
+ inside a binding level that temporarily binds the parameters. This
+ means that definitions here should not be popped off when unwinding
+ this binding level. (Not actually implemented this way,
+ unfortunately.) */
+ unsigned pseudo_global : 1;
+
+ /* Two bits left for this word. */
+
+#if defined(DEBUG_CP_BINDING_LEVELS)
+ /* Binding depth at which this level began. */
+ unsigned binding_depth;
+#endif /* defined(DEBUG_CP_BINDING_LEVELS) */
+ };
+
+#define NULL_BINDING_LEVEL ((struct binding_level *) NULL)
+
+/* The (non-class) binding level currently in effect. */
+
+static struct binding_level *current_binding_level;
+
+/* The binding level of the current class, if any. */
+
+static struct binding_level *class_binding_level;
+
+/* The current (class or non-class) binding level currently in effect. */
+
+#define inner_binding_level \
+ (class_binding_level ? class_binding_level : current_binding_level)
+
+/* A chain of binding_level structures awaiting reuse. */
+
+static struct binding_level *free_binding_level;
+
+/* The outermost binding level, for names of file scope.
+ This is created when the compiler is started and exists
+ through the entire run. */
+
+static struct binding_level *global_binding_level;
+
+/* Binding level structures are initialized by copying this one. */
+
+static struct binding_level clear_binding_level;
+
+/* Nonzero means unconditionally make a BLOCK for the next level pushed. */
+
+static int keep_next_level_flag;
+
+#if defined(DEBUG_CP_BINDING_LEVELS)
+static int binding_depth = 0;
+static int is_class_level = 0;
+
+static void
+indent ()
+{
+ register unsigned i;
+
+ for (i = 0; i < binding_depth*2; i++)
+ putc (' ', stderr);
+}
+#endif /* defined(DEBUG_CP_BINDING_LEVELS) */
+
+static tree pushdecl_with_scope PROTO((tree, struct binding_level *));
+
+static void
+push_binding_level (newlevel, tag_transparent, keep)
+ struct binding_level *newlevel;
+ int tag_transparent, keep;
+{
+ /* Add this level to the front of the chain (stack) of levels that
+ are active. */
+ *newlevel = clear_binding_level;
+ if (class_binding_level)
+ {
+ newlevel->level_chain = class_binding_level;
+ class_binding_level = (struct binding_level *)0;
+ }
+ else
+ {
+ newlevel->level_chain = current_binding_level;
+ }
+ current_binding_level = newlevel;
+ newlevel->tag_transparent = tag_transparent;
+ newlevel->more_cleanups_ok = 1;
+ newlevel->keep = keep;
+#if defined(DEBUG_CP_BINDING_LEVELS)
+ newlevel->binding_depth = binding_depth;
+ indent ();
+ fprintf (stderr, "push %s level 0x%08x line %d\n",
+ (is_class_level) ? "class" : "block", newlevel, lineno);
+ is_class_level = 0;
+ binding_depth++;
+#endif /* defined(DEBUG_CP_BINDING_LEVELS) */
+}
+
+static void
+pop_binding_level ()
+{
+ if (class_binding_level)
+ current_binding_level = class_binding_level;
+
+ if (global_binding_level)
+ {
+ /* cannot pop a level, if there are none left to pop. */
+ if (current_binding_level == global_binding_level)
+ my_friendly_abort (123);
+ }
+ /* Pop the current level, and free the structure for reuse. */
+#if defined(DEBUG_CP_BINDING_LEVELS)
+ binding_depth--;
+ indent ();
+ fprintf (stderr, "pop %s level 0x%08x line %d\n",
+ (is_class_level) ? "class" : "block",
+ current_binding_level, lineno);
+ if (is_class_level != (current_binding_level == class_binding_level))
+#if 0 /* XXX Don't abort when we're watching how things are being managed. */
+ abort ();
+#else
+ {
+ indent ();
+ fprintf (stderr, "XXX is_class_level != (current_binding_level == class_binding_level)\n");
+ }
+#endif
+ is_class_level = 0;
+#endif /* defined(DEBUG_CP_BINDING_LEVELS) */
+ {
+ register struct binding_level *level = current_binding_level;
+ current_binding_level = current_binding_level->level_chain;
+ level->level_chain = free_binding_level;
+#if 0 /* defined(DEBUG_CP_BINDING_LEVELS) */
+ if (level->binding_depth != binding_depth)
+ abort ();
+#endif /* defined(DEBUG_CP_BINDING_LEVELS) */
+ free_binding_level = level;
+
+ class_binding_level = current_binding_level;
+ if (class_binding_level->parm_flag != 2)
+ class_binding_level = 0;
+ while (current_binding_level->parm_flag == 2)
+ current_binding_level = current_binding_level->level_chain;
+ }
+}
+
+/* Nonzero if we are currently in the global binding level. */
+
+int
+global_bindings_p ()
+{
+ return current_binding_level == global_binding_level;
+}
+
+void
+keep_next_level ()
+{
+ keep_next_level_flag = 1;
+}
+
+/* Nonzero if the current level needs to have a BLOCK made. */
+
+int
+kept_level_p ()
+{
+ return (current_binding_level->blocks != NULL_TREE
+ || current_binding_level->keep
+ || current_binding_level->names != NULL_TREE
+ || (current_binding_level->tags != NULL_TREE
+ && !current_binding_level->tag_transparent));
+}
+
+/* Identify this binding level as a level of parameters. */
+
+void
+declare_parm_level ()
+{
+ current_binding_level->parm_flag = 1;
+}
+
+void
+declare_uninstantiated_type_level ()
+{
+ current_binding_level->accept_any = 1;
+}
+
+int
+uninstantiated_type_level_p ()
+{
+ return current_binding_level->accept_any;
+}
+
+void
+declare_pseudo_global_level ()
+{
+ current_binding_level->pseudo_global = 1;
+}
+
+int
+pseudo_global_level_p ()
+{
+ return current_binding_level->pseudo_global;
+}
+
+void
+set_class_shadows (shadows)
+ tree shadows;
+{
+ class_binding_level->class_shadowed = shadows;
+}
+
+/* Enter a new binding level.
+ If TAG_TRANSPARENT is nonzero, do so only for the name space of variables,
+ not for that of tags. */
+
+void
+pushlevel (tag_transparent)
+ int tag_transparent;
+{
+ register struct binding_level *newlevel = NULL_BINDING_LEVEL;
+
+ /* If this is the top level of a function,
+ just make sure that NAMED_LABELS is 0.
+ They should have been set to 0 at the end of the previous function. */
+
+ if (current_binding_level == global_binding_level)
+ my_friendly_assert (named_labels == NULL_TREE, 134);
+
+ /* Reuse or create a struct for this binding level. */
+
+#if defined(DEBUG_CP_BINDING_LEVELS)
+ if (0)
+#else /* !defined(DEBUG_CP_BINDING_LEVELS) */
+ if (free_binding_level)
+#endif /* !defined(DEBUG_CP_BINDING_LEVELS) */
+ {
+ newlevel = free_binding_level;
+ free_binding_level = free_binding_level->level_chain;
+ }
+ else
+ {
+ /* Create a new `struct binding_level'. */
+ newlevel = (struct binding_level *) xmalloc (sizeof (struct binding_level));
+ }
+ push_binding_level (newlevel, tag_transparent, keep_next_level_flag);
+ GNU_xref_start_scope ((HOST_WIDE_INT) newlevel);
+ keep_next_level_flag = 0;
+}
+
+void
+pushlevel_temporary (tag_transparent)
+ int tag_transparent;
+{
+ pushlevel (tag_transparent);
+ current_binding_level->keep = 2;
+ clear_last_expr ();
+
+ /* Note we don't call push_momentary() here. Otherwise, it would cause
+ cleanups to be allocated on the momentary obstack, and they will be
+ overwritten by the next statement. */
+
+ expand_start_bindings (0);
+}
+
+/* Exit a binding level.
+ Pop the level off, and restore the state of the identifier-decl mappings
+ that were in effect when this level was entered.
+
+ If KEEP == 1, this level had explicit declarations, so
+ and create a "block" (a BLOCK node) for the level
+ to record its declarations and subblocks for symbol table output.
+
+ If KEEP == 2, this level's subblocks go to the front,
+ not the back of the current binding level. This happens,
+ for instance, when code for constructors and destructors
+ need to generate code at the end of a function which must
+ be moved up to the front of the function.
+
+ If FUNCTIONBODY is nonzero, this level is the body of a function,
+ so create a block as if KEEP were set and also clear out all
+ label names.
+
+ If REVERSE is nonzero, reverse the order of decls before putting
+ them into the BLOCK. */
+
+tree
+poplevel (keep, reverse, functionbody)
+ int keep;
+ int reverse;
+ int functionbody;
+{
+ register tree link;
+ /* The chain of decls was accumulated in reverse order.
+ Put it into forward order, just for cleanliness. */
+ tree decls;
+ int tmp = functionbody;
+ int implicit_try_block = current_binding_level->parm_flag == 3;
+ int real_functionbody = current_binding_level->keep == 2
+ ? ((functionbody = 0), tmp) : functionbody;
+ tree tags = functionbody >= 0 ? current_binding_level->tags : 0;
+ tree subblocks = functionbody >= 0 ? current_binding_level->blocks : 0;
+ tree block = NULL_TREE;
+ tree decl;
+ int block_previously_created;
+
+ GNU_xref_end_scope ((HOST_WIDE_INT) current_binding_level,
+ (HOST_WIDE_INT) current_binding_level->level_chain,
+ current_binding_level->parm_flag,
+ current_binding_level->keep,
+ current_binding_level->tag_transparent);
+
+ if (current_binding_level->keep == 1)
+ keep = 1;
+
+ /* This warning is turned off because it causes warnings for
+ declarations like `extern struct foo *x'. */
+#if 0
+ /* Warn about incomplete structure types in this level. */
+ for (link = tags; link; link = TREE_CHAIN (link))
+ if (TYPE_SIZE (TREE_VALUE (link)) == NULL_TREE)
+ {
+ tree type = TREE_VALUE (link);
+ char *errmsg;
+ switch (TREE_CODE (type))
+ {
+ case RECORD_TYPE:
+ errmsg = "`struct %s' incomplete in scope ending here";
+ break;
+ case UNION_TYPE:
+ errmsg = "`union %s' incomplete in scope ending here";
+ break;
+ case ENUMERAL_TYPE:
+ errmsg = "`enum %s' incomplete in scope ending here";
+ break;
+ }
+ if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
+ error (errmsg, IDENTIFIER_POINTER (TYPE_NAME (type)));
+ else
+ /* If this type has a typedef-name, the TYPE_NAME is a TYPE_DECL. */
+ error (errmsg, TYPE_NAME_STRING (type));
+ }
+#endif /* 0 */
+
+ /* Get the decls in the order they were written.
+ Usually current_binding_level->names is in reverse order.
+ But parameter decls were previously put in forward order. */
+
+ if (reverse)
+ current_binding_level->names
+ = decls = nreverse (current_binding_level->names);
+ else
+ decls = current_binding_level->names;
+
+ /* Output any nested inline functions within this block
+ if they weren't already output. */
+
+ for (decl = decls; decl; decl = TREE_CHAIN (decl))
+ if (TREE_CODE (decl) == FUNCTION_DECL
+ && ! TREE_ASM_WRITTEN (decl)
+ && DECL_INITIAL (decl) != NULL_TREE
+ && TREE_ADDRESSABLE (decl))
+ {
+ /* If this decl was copied from a file-scope decl
+ on account of a block-scope extern decl,
+ propagate TREE_ADDRESSABLE to the file-scope decl. */
+ if (DECL_ABSTRACT_ORIGIN (decl) != NULL_TREE)
+ TREE_ADDRESSABLE (DECL_ABSTRACT_ORIGIN (decl)) = 1;
+ else
+ {
+ push_function_context ();
+ output_inline_function (decl);
+ pop_function_context ();
+ }
+ }
+
+ /* If there were any declarations or structure tags in that level,
+ or if this level is a function body,
+ create a BLOCK to record them for the life of this function. */
+
+ block = NULL_TREE;
+ block_previously_created = (current_binding_level->this_block != NULL_TREE);
+ if (block_previously_created)
+ block = current_binding_level->this_block;
+ else if (keep == 1 || functionbody)
+ block = make_node (BLOCK);
+ if (block != NULL_TREE)
+ {
+ BLOCK_VARS (block) = decls;
+ BLOCK_TYPE_TAGS (block) = tags;
+ BLOCK_SUBBLOCKS (block) = subblocks;
+ /* If we created the block earlier on, and we are just diddling it now,
+ then it already should have a proper BLOCK_END_NOTE value associated
+ with it, so avoid trashing that. Otherwise, for a new block, install
+ a new BLOCK_END_NOTE value. */
+ if (! block_previously_created)
+ remember_end_note (block);
+ }
+
+ /* In each subblock, record that this is its superior. */
+
+ if (keep >= 0)
+ for (link = subblocks; link; link = TREE_CHAIN (link))
+ BLOCK_SUPERCONTEXT (link) = block;
+
+ /* Clear out the meanings of the local variables of this level. */
+
+ for (link = decls; link; link = TREE_CHAIN (link))
+ {
+ if (DECL_NAME (link) != NULL_TREE)
+ {
+ /* If the ident. was used or addressed via a local extern decl,
+ don't forget that fact. */
+ if (DECL_EXTERNAL (link))
+ {
+ if (TREE_USED (link))
+ TREE_USED (DECL_ASSEMBLER_NAME (link)) = 1;
+ if (TREE_ADDRESSABLE (link))
+ TREE_ADDRESSABLE (DECL_ASSEMBLER_NAME (link)) = 1;
+ }
+ IDENTIFIER_LOCAL_VALUE (DECL_NAME (link)) = NULL_TREE;
+ }
+ }
+
+ /* Restore all name-meanings of the outer levels
+ that were shadowed by this level. */
+
+ for (link = current_binding_level->shadowed; link; link = TREE_CHAIN (link))
+ IDENTIFIER_LOCAL_VALUE (TREE_PURPOSE (link)) = TREE_VALUE (link);
+ for (link = current_binding_level->class_shadowed;
+ link; link = TREE_CHAIN (link))
+ IDENTIFIER_CLASS_VALUE (TREE_PURPOSE (link)) = TREE_VALUE (link);
+ for (link = current_binding_level->type_shadowed;
+ link; link = TREE_CHAIN (link))
+ IDENTIFIER_TYPE_VALUE (TREE_PURPOSE (link)) = TREE_VALUE (link);
+
+ /* If the level being exited is the top level of a function,
+ check over all the labels. */
+
+ if (functionbody)
+ {
+ /* If this is the top level block of a function,
+ the vars are the function's parameters.
+ Don't leave them in the BLOCK because they are
+ found in the FUNCTION_DECL instead. */
+
+ BLOCK_VARS (block) = 0;
+
+ /* Clear out the definitions of all label names,
+ since their scopes end here. */
+
+ for (link = named_labels; link; link = TREE_CHAIN (link))
+ {
+ register tree label = TREE_VALUE (link);
+
+ if (DECL_INITIAL (label) == NULL_TREE)
+ {
+ cp_error_at ("label `%D' used but not defined", label);
+ /* Avoid crashing later. */
+ define_label (input_filename, 1, DECL_NAME (label));
+ }
+ else if (warn_unused && !TREE_USED (label))
+ cp_warning_at ("label `%D' defined but not used", label);
+ SET_IDENTIFIER_LABEL_VALUE (DECL_NAME (label), NULL_TREE);
+
+ /* Put the labels into the "variables" of the
+ top-level block, so debugger can see them. */
+ TREE_CHAIN (label) = BLOCK_VARS (block);
+ BLOCK_VARS (block) = label;
+ }
+
+ named_labels = NULL_TREE;
+ }
+
+ /* Any uses of undefined labels now operate under constraints
+ of next binding contour. */
+ {
+ struct binding_level *level_chain;
+ level_chain = current_binding_level->level_chain;
+ if (level_chain)
+ {
+ tree labels;
+ for (labels = named_label_uses; labels; labels = TREE_CHAIN (labels))
+ if (TREE_TYPE (labels) == (tree)current_binding_level)
+ {
+ TREE_TYPE (labels) = (tree)level_chain;
+ TREE_PURPOSE (labels) = level_chain->names;
+ }
+ }
+ }
+
+ tmp = current_binding_level->keep;
+
+ pop_binding_level ();
+ if (functionbody)
+ DECL_INITIAL (current_function_decl) = block;
+ else if (block)
+ {
+ if (!block_previously_created)
+ current_binding_level->blocks
+ = chainon (current_binding_level->blocks, block);
+ }
+ /* If we did not make a block for the level just exited,
+ any blocks made for inner levels
+ (since they cannot be recorded as subblocks in that level)
+ must be carried forward so they will later become subblocks
+ of something else. */
+ else if (subblocks)
+ {
+ if (keep == 2)
+ current_binding_level->blocks
+ = chainon (subblocks, current_binding_level->blocks);
+ else
+ current_binding_level->blocks
+ = chainon (current_binding_level->blocks, subblocks);
+ }
+
+ /* Take care of compiler's internal binding structures. */
+ if (tmp == 2)
+ {
+#if 0
+ /* We did not call push_momentary for this
+ binding contour, so there is nothing to pop. */
+ pop_momentary ();
+#endif
+ expand_end_bindings (getdecls (), keep, 1);
+ /* Each and every BLOCK node created here in `poplevel' is important
+ (e.g. for proper debugging information) so if we created one
+ earlier, mark it as "used". */
+ if (block)
+ TREE_USED (block) = 1;
+ block = poplevel (keep, reverse, real_functionbody);
+ }
+
+ /* Each and every BLOCK node created here in `poplevel' is important
+ (e.g. for proper debugging information) so if we created one
+ earlier, mark it as "used". */
+ if (block)
+ TREE_USED (block) = 1;
+ return block;
+}
+
+/* Delete the node BLOCK from the current binding level.
+ This is used for the block inside a stmt expr ({...})
+ so that the block can be reinserted where appropriate. */
+
+void
+delete_block (block)
+ tree block;
+{
+ tree t;
+ if (current_binding_level->blocks == block)
+ current_binding_level->blocks = TREE_CHAIN (block);
+ for (t = current_binding_level->blocks; t;)
+ {
+ if (TREE_CHAIN (t) == block)
+ TREE_CHAIN (t) = TREE_CHAIN (block);
+ else
+ t = TREE_CHAIN (t);
+ }
+ TREE_CHAIN (block) = NULL_TREE;
+ /* Clear TREE_USED which is always set by poplevel.
+ The flag is set again if insert_block is called. */
+ TREE_USED (block) = 0;
+}
+
+/* Insert BLOCK at the end of the list of subblocks of the
+ current binding level. This is used when a BIND_EXPR is expanded,
+ to handle the BLOCK node inside the BIND_EXPR. */
+
+void
+insert_block (block)
+ tree block;
+{
+ TREE_USED (block) = 1;
+ current_binding_level->blocks
+ = chainon (current_binding_level->blocks, block);
+}
+
+/* Add BLOCK to the current list of blocks for this binding contour. */
+void
+add_block_current_level (block)
+ tree block;
+{
+ current_binding_level->blocks
+ = chainon (current_binding_level->blocks, block);
+}
+
+/* Set the BLOCK node for the innermost scope
+ (the one we are currently in). */
+
+void
+set_block (block)
+ register tree block;
+{
+ current_binding_level->this_block = block;
+}
+
+/* Do a pushlevel for class declarations. */
+void
+pushlevel_class ()
+{
+ register struct binding_level *newlevel;
+
+ /* Reuse or create a struct for this binding level. */
+#if defined(DEBUG_CP_BINDING_LEVELS)
+ if (0)
+#else /* !defined(DEBUG_CP_BINDING_LEVELS) */
+ if (free_binding_level)
+#endif /* !defined(DEBUG_CP_BINDING_LEVELS) */
+ {
+ newlevel = free_binding_level;
+ free_binding_level = free_binding_level->level_chain;
+ }
+ else
+ {
+ /* Create a new `struct binding_level'. */
+ newlevel = (struct binding_level *) xmalloc (sizeof (struct binding_level));
+ }
+
+#if defined(DEBUG_CP_BINDING_LEVELS)
+ is_class_level = 1;
+#endif /* defined(DEBUG_CP_BINDING_LEVELS) */
+
+ push_binding_level (newlevel, 0, 0);
+
+ decl_stack = push_decl_level (decl_stack, &decl_obstack);
+ class_binding_level = current_binding_level;
+ class_binding_level->parm_flag = 2;
+ /* We have just pushed into a new binding level. Now, fake out the rest
+ of the compiler. Set the `current_binding_level' back to point to
+ the most closely containing non-class binding level. */
+ do
+ {
+ current_binding_level = current_binding_level->level_chain;
+ }
+ while (current_binding_level->parm_flag == 2);
+}
+
+/* ...and a poplevel for class declarations. FORCE is used to force
+ clearing out of CLASS_VALUEs after a class definition. */
+tree
+poplevel_class (force)
+ int force;
+{
+ register struct binding_level *level = class_binding_level;
+ tree block = NULL_TREE;
+ tree shadowed;
+
+ my_friendly_assert (level != 0, 354);
+
+ decl_stack = pop_stack_level (decl_stack);
+ for (shadowed = level->shadowed; shadowed; shadowed = TREE_CHAIN (shadowed))
+ IDENTIFIER_LOCAL_VALUE (TREE_PURPOSE (shadowed)) = TREE_VALUE (shadowed);
+ /* If we're leaving a toplevel class, don't bother to do the setting
+ of IDENTIFER_CLASS_VALUE to NULL_TREE, since first of all this slot
+ shouldn't even be used when current_class_type isn't set, and second,
+ if we don't touch it here, we're able to use the caching effect if the
+ next time we're entering a class scope, it is the same class. */
+ if (current_class_depth != 1 || force)
+ for (shadowed = level->class_shadowed;
+ shadowed;
+ shadowed = TREE_CHAIN (shadowed))
+ IDENTIFIER_CLASS_VALUE (TREE_PURPOSE (shadowed)) = TREE_VALUE (shadowed);
+ else
+ /* Remember to save what IDENTIFIER's were bound in this scope so we
+ can recover from cache misses. */
+ previous_class_values = class_binding_level->class_shadowed;
+ for (shadowed = level->type_shadowed;
+ shadowed;
+ shadowed = TREE_CHAIN (shadowed))
+ IDENTIFIER_TYPE_VALUE (TREE_PURPOSE (shadowed)) = TREE_VALUE (shadowed);
+
+ GNU_xref_end_scope ((HOST_WIDE_INT) class_binding_level,
+ (HOST_WIDE_INT) class_binding_level->level_chain,
+ class_binding_level->parm_flag,
+ class_binding_level->keep,
+ class_binding_level->tag_transparent);
+
+ if (class_binding_level->parm_flag != 2)
+ class_binding_level = (struct binding_level *)0;
+
+ /* Now, pop out of the the binding level which we created up in the
+ `pushlevel_class' routine. */
+#if defined(DEBUG_CP_BINDING_LEVELS)
+ is_class_level = 1;
+#endif /* defined(DEBUG_CP_BINDING_LEVELS) */
+
+ pop_binding_level ();
+
+ return block;
+}
+
+/* For debugging. */
+int no_print_functions = 0;
+int no_print_builtins = 0;
+
+void
+print_binding_level (lvl)
+ struct binding_level *lvl;
+{
+ tree t;
+ int i = 0, len;
+ fprintf (stderr, " blocks=");
+ fprintf (stderr, HOST_PTR_PRINTF, lvl->blocks);
+ fprintf (stderr, " n_incomplete=%d parm_flag=%d keep=%d",
+ lvl->n_incomplete, lvl->parm_flag, lvl->keep);
+ if (lvl->tag_transparent)
+ fprintf (stderr, " tag-transparent");
+ if (lvl->more_cleanups_ok)
+ fprintf (stderr, " more-cleanups-ok");
+ if (lvl->have_cleanups)
+ fprintf (stderr, " have-cleanups");
+ fprintf (stderr, "\n");
+ if (lvl->names)
+ {
+ fprintf (stderr, " names:\t");
+ /* We can probably fit 3 names to a line? */
+ for (t = lvl->names; t; t = TREE_CHAIN (t))
+ {
+ if (no_print_functions && (TREE_CODE(t) == FUNCTION_DECL))
+ continue;
+ if (no_print_builtins
+ && (TREE_CODE(t) == TYPE_DECL)
+ && (!strcmp(DECL_SOURCE_FILE(t),"<built-in>")))
+ continue;
+
+ /* Function decls tend to have longer names. */
+ if (TREE_CODE (t) == FUNCTION_DECL)
+ len = 3;
+ else
+ len = 2;
+ i += len;
+ if (i > 6)
+ {
+ fprintf (stderr, "\n\t");
+ i = len;
+ }
+ print_node_brief (stderr, "", t, 0);
+ if (TREE_CODE (t) == ERROR_MARK)
+ break;
+ }
+ if (i)
+ fprintf (stderr, "\n");
+ }
+ if (lvl->tags)
+ {
+ fprintf (stderr, " tags:\t");
+ i = 0;
+ for (t = lvl->tags; t; t = TREE_CHAIN (t))
+ {
+ if (TREE_PURPOSE (t) == NULL_TREE)
+ len = 3;
+ else if (TREE_PURPOSE (t) == TYPE_IDENTIFIER (TREE_VALUE (t)))
+ len = 2;
+ else
+ len = 4;
+ i += len;
+ if (i > 5)
+ {
+ fprintf (stderr, "\n\t");
+ i = len;
+ }
+ if (TREE_PURPOSE (t) == NULL_TREE)
+ {
+ print_node_brief (stderr, "<unnamed-typedef", TREE_VALUE (t), 0);
+ fprintf (stderr, ">");
+ }
+ else if (TREE_PURPOSE (t) == TYPE_IDENTIFIER (TREE_VALUE (t)))
+ print_node_brief (stderr, "", TREE_VALUE (t), 0);
+ else
+ {
+ print_node_brief (stderr, "<typedef", TREE_PURPOSE (t), 0);
+ print_node_brief (stderr, "", TREE_VALUE (t), 0);
+ fprintf (stderr, ">");
+ }
+ }
+ if (i)
+ fprintf (stderr, "\n");
+ }
+ if (lvl->shadowed)
+ {
+ fprintf (stderr, " shadowed:");
+ for (t = lvl->shadowed; t; t = TREE_CHAIN (t))
+ {
+ fprintf (stderr, " %s ", IDENTIFIER_POINTER (TREE_PURPOSE (t)));
+ }
+ fprintf (stderr, "\n");
+ }
+ if (lvl->class_shadowed)
+ {
+ fprintf (stderr, " class-shadowed:");
+ for (t = lvl->class_shadowed; t; t = TREE_CHAIN (t))
+ {
+ fprintf (stderr, " %s ", IDENTIFIER_POINTER (TREE_PURPOSE (t)));
+ }
+ fprintf (stderr, "\n");
+ }
+ if (lvl->type_shadowed)
+ {
+ fprintf (stderr, " type-shadowed:");
+ for (t = lvl->type_shadowed; t; t = TREE_CHAIN (t))
+ {
+#if 0
+ fprintf (stderr, "\n\t");
+ print_node_brief (stderr, "<", TREE_PURPOSE (t), 0);
+ if (TREE_VALUE (t))
+ print_node_brief (stderr, " ", TREE_VALUE (t), 0);
+ else
+ fprintf (stderr, " (none)");
+ fprintf (stderr, ">");
+#else
+ fprintf (stderr, " %s ", IDENTIFIER_POINTER (TREE_PURPOSE (t)));
+#endif
+ }
+ fprintf (stderr, "\n");
+ }
+}
+
+void
+print_other_binding_stack (stack)
+ struct binding_level *stack;
+{
+ struct binding_level *level;
+ for (level = stack; level != global_binding_level; level = level->level_chain)
+ {
+ fprintf (stderr, "binding level ");
+ fprintf (stderr, HOST_PTR_PRINTF, level);
+ fprintf (stderr, "\n");
+ print_binding_level (level);
+ }
+}
+
+void
+print_binding_stack ()
+{
+ struct binding_level *b;
+ fprintf (stderr, "current_binding_level=");
+ fprintf (stderr, HOST_PTR_PRINTF, current_binding_level);
+ fprintf (stderr, "\nclass_binding_level=");
+ fprintf (stderr, HOST_PTR_PRINTF, class_binding_level);
+ fprintf (stderr, "\nglobal_binding_level=");
+ fprintf (stderr, HOST_PTR_PRINTF, global_binding_level);
+ fprintf (stderr, "\n");
+ if (class_binding_level)
+ {
+ for (b = class_binding_level; b; b = b->level_chain)
+ if (b == current_binding_level)
+ break;
+ if (b)
+ b = class_binding_level;
+ else
+ b = current_binding_level;
+ }
+ else
+ b = current_binding_level;
+ print_other_binding_stack (b);
+ fprintf (stderr, "global:\n");
+ print_binding_level (global_binding_level);
+}
+
+/* Subroutines for reverting temporarily to top-level for instantiation
+ of templates and such. We actually need to clear out the class- and
+ local-value slots of all identifiers, so that only the global values
+ are at all visible. Simply setting current_binding_level to the global
+ scope isn't enough, because more binding levels may be pushed. */
+struct saved_scope {
+ struct binding_level *old_binding_level;
+ tree old_bindings;
+ struct saved_scope *prev;
+ tree class_name, class_type, class_decl, function_decl;
+ struct binding_level *class_bindings;
+ tree previous_class_type;
+ tree *lang_base, *lang_stack, lang_name;
+ int lang_stacksize;
+ tree named_labels;
+};
+static struct saved_scope *current_saved_scope;
+extern tree prev_class_type;
+
+void
+push_to_top_level ()
+{
+ extern int current_lang_stacksize;
+ struct saved_scope *s =
+ (struct saved_scope *) xmalloc (sizeof (struct saved_scope));
+ struct binding_level *b = current_binding_level;
+ tree old_bindings = NULL_TREE;
+
+ /* Have to include global_binding_level, because class-level decls
+ aren't listed anywhere useful. */
+ for (; b; b = b->level_chain)
+ {
+ tree t;
+
+ if (b == global_binding_level)
+ continue;
+
+ for (t = b->names; t; t = TREE_CHAIN (t))
+ {
+ tree binding, t1, t2 = t;
+ tree id = DECL_ASSEMBLER_NAME (t2);
+
+ if (!id
+ || (!IDENTIFIER_LOCAL_VALUE (id)
+ && !IDENTIFIER_CLASS_VALUE (id)))
+ continue;
+
+ for (t1 = old_bindings; t1; t1 = TREE_CHAIN (t1))
+ if (TREE_VEC_ELT (t1, 0) == id)
+ goto skip_it;
+
+ binding = make_tree_vec (4);
+ if (id)
+ {
+ my_friendly_assert (TREE_CODE (id) == IDENTIFIER_NODE, 135);
+ TREE_VEC_ELT (binding, 0) = id;
+ TREE_VEC_ELT (binding, 1) = IDENTIFIER_TYPE_VALUE (id);
+ TREE_VEC_ELT (binding, 2) = IDENTIFIER_LOCAL_VALUE (id);
+ TREE_VEC_ELT (binding, 3) = IDENTIFIER_CLASS_VALUE (id);
+ IDENTIFIER_LOCAL_VALUE (id) = NULL_TREE;
+ IDENTIFIER_CLASS_VALUE (id) = NULL_TREE;
+ }
+ TREE_CHAIN (binding) = old_bindings;
+ old_bindings = binding;
+ skip_it:
+ ;
+ }
+ /* Unwind type-value slots back to top level. */
+ for (t = b->type_shadowed; t; t = TREE_CHAIN (t))
+ SET_IDENTIFIER_TYPE_VALUE (TREE_PURPOSE (t), TREE_VALUE (t));
+ }
+ /* Clear out class-level bindings cache. */
+ if (current_binding_level == global_binding_level
+ && previous_class_type != NULL_TREE)
+ {
+ popclass (-1);
+ previous_class_type = NULL_TREE;
+ }
+
+ s->old_binding_level = current_binding_level;
+ current_binding_level = global_binding_level;
+
+ s->class_name = current_class_name;
+ s->class_type = current_class_type;
+ s->class_decl = current_class_decl;
+ s->function_decl = current_function_decl;
+ s->class_bindings = class_binding_level;
+ s->previous_class_type = previous_class_type;
+ s->lang_stack = current_lang_stack;
+ s->lang_base = current_lang_base;
+ s->lang_stacksize = current_lang_stacksize;
+ s->lang_name = current_lang_name;
+ s->named_labels = named_labels;
+ current_class_name = current_class_type = current_class_decl = NULL_TREE;
+ current_function_decl = NULL_TREE;
+ class_binding_level = (struct binding_level *)0;
+ previous_class_type = NULL_TREE;
+ current_lang_stacksize = 10;
+ current_lang_stack = current_lang_base
+ = (tree *) xmalloc (current_lang_stacksize * sizeof (tree));
+ current_lang_name = lang_name_cplusplus;
+ strict_prototype = strict_prototypes_lang_cplusplus;
+ named_labels = NULL_TREE;
+
+ s->prev = current_saved_scope;
+ s->old_bindings = old_bindings;
+ current_saved_scope = s;
+}
+
+void
+pop_from_top_level ()
+{
+ extern int current_lang_stacksize;
+ struct saved_scope *s = current_saved_scope;
+ tree t;
+
+ if (previous_class_type)
+ previous_class_type = NULL_TREE;
+
+ current_binding_level = s->old_binding_level;
+ current_saved_scope = s->prev;
+ for (t = s->old_bindings; t; t = TREE_CHAIN (t))
+ {
+ tree id = TREE_VEC_ELT (t, 0);
+ if (id)
+ {
+ IDENTIFIER_TYPE_VALUE (id) = TREE_VEC_ELT (t, 1);
+ IDENTIFIER_LOCAL_VALUE (id) = TREE_VEC_ELT (t, 2);
+ IDENTIFIER_CLASS_VALUE (id) = TREE_VEC_ELT (t, 3);
+ }
+ }
+ current_class_name = s->class_name;
+ current_class_type = s->class_type;
+ current_class_decl = s->class_decl;
+ if (current_class_type)
+ C_C_D = CLASSTYPE_INST_VAR (current_class_type);
+ else
+ C_C_D = NULL_TREE;
+ current_function_decl = s->function_decl;
+ class_binding_level = s->class_bindings;
+ previous_class_type = s->previous_class_type;
+ free (current_lang_base);
+ current_lang_base = s->lang_base;
+ current_lang_stack = s->lang_stack;
+ current_lang_name = s->lang_name;
+ current_lang_stacksize = s->lang_stacksize;
+ if (current_lang_name == lang_name_cplusplus)
+ strict_prototype = strict_prototypes_lang_cplusplus;
+ else if (current_lang_name == lang_name_c)
+ strict_prototype = strict_prototypes_lang_c;
+ named_labels = s->named_labels;
+
+ free (s);
+}
+
+/* Push a definition of struct, union or enum tag "name".
+ into binding_level "b". "type" should be the type node,
+ We assume that the tag "name" is not already defined.
+
+ Note that the definition may really be just a forward reference.
+ In that case, the TYPE_SIZE will be a NULL_TREE.
+
+ C++ gratuitously puts all these tags in the name space. */
+
+/* When setting the IDENTIFIER_TYPE_VALUE field of an identifier ID,
+ record the shadowed value for this binding contour. TYPE is
+ the type that ID maps to. */
+
+static void
+set_identifier_type_value_with_scope (id, type, b)
+ tree id;
+ tree type;
+ struct binding_level *b;
+{
+ if (b != global_binding_level)
+ {
+ tree old_type_value = IDENTIFIER_TYPE_VALUE (id);
+ b->type_shadowed
+ = tree_cons (id, old_type_value, b->type_shadowed);
+ }
+ SET_IDENTIFIER_TYPE_VALUE (id, type);
+}
+
+/* As set_identifier_type_value_with_scope, but using inner_binding_level. */
+
+void
+set_identifier_type_value (id, type)
+ tree id;
+ tree type;
+{
+ set_identifier_type_value_with_scope (id, type, inner_binding_level);
+}
+
+/* Subroutine "set_nested_typename" builds the nested-typename of
+ the type decl in question. (Argument CLASSNAME can actually be
+ a function as well, if that's the smallest containing scope.) */
+
+static void
+set_nested_typename (decl, classname, name, type)
+ tree decl, classname, name, type;
+{
+ my_friendly_assert (TREE_CODE (decl) == TYPE_DECL, 136);
+ if (classname != NULL_TREE)
+ {
+ char *buf;
+ my_friendly_assert (TREE_CODE (classname) == IDENTIFIER_NODE, 137);
+ my_friendly_assert (TREE_CODE (name) == IDENTIFIER_NODE, 138);
+ buf = (char *) alloca (4 + IDENTIFIER_LENGTH (classname)
+ + IDENTIFIER_LENGTH (name));
+ sprintf (buf, "%s::%s", IDENTIFIER_POINTER (classname),
+ IDENTIFIER_POINTER (name));
+ DECL_NESTED_TYPENAME (decl) = get_identifier (buf);
+ TREE_MANGLED (DECL_NESTED_TYPENAME (decl)) = 1;
+
+ /* This is a special usage of IDENTIFIER_TYPE_VALUE which have no
+ correspondence in any binding_level. This is ok since the
+ DECL_NESTED_TYPENAME is just a convenience identifier whose
+ IDENTIFIER_TYPE_VALUE will remain constant from now on. */
+ SET_IDENTIFIER_TYPE_VALUE (DECL_NESTED_TYPENAME (decl), type);
+ }
+ else
+ DECL_NESTED_TYPENAME (decl) = name;
+}
+
+/* Pop off extraneous binding levels left over due to syntax errors. */
+void
+pop_everything ()
+{
+#ifdef DEBUG_CP_BINDING_LEVELS
+ fprintf (stderr, "XXX entering pop_everything ()\n");
+#endif
+ while (current_binding_level != global_binding_level
+ && ! current_binding_level->pseudo_global)
+ {
+ if (class_binding_level)
+ pop_nested_class (1);
+ else
+ poplevel (0, 0, 0);
+ }
+#ifdef DEBUG_CP_BINDING_LEVELS
+ fprintf (stderr, "XXX leaving pop_everything ()\n");
+#endif
+}
+
+#if 0 /* not yet, should get fixed properly later */
+/* Create a TYPE_DECL node with the correct DECL_ASSEMBLER_NAME.
+ Other routines shouldn't use build_decl directly; they'll produce
+ incorrect results with `-g' unless they duplicate this code.
+
+ This is currently needed mainly for dbxout.c, but we can make
+ use of it in method.c later as well. */
+tree
+make_type_decl (name, type)
+ tree name, type;
+{
+ tree decl, id;
+ decl = build_decl (TYPE_DECL, name, type);
+ if (TYPE_NAME (type) == name)
+ /* Class/union/enum definition, or a redundant typedef for same. */
+ {
+ id = get_identifier (build_overload_name (type, 1, 1));
+ DECL_ASSEMBLER_NAME (decl) = id;
+ }
+ else if (TYPE_NAME (type) != NULL_TREE)
+ /* Explicit typedef, or implicit typedef for template expansion. */
+ DECL_ASSEMBLER_NAME (decl) = DECL_ASSEMBLER_NAME (TYPE_NAME (type));
+ else
+ {
+ /* XXX: Typedef for unnamed struct; some other situations.
+ TYPE_NAME is null; what's right here? */
+ }
+ return decl;
+}
+#endif
+
+/* Push a tag name NAME for struct/class/union/enum type TYPE.
+ Normally put into into the inner-most non-tag-tranparent scope,
+ but if GLOBALIZE is true, put it in the inner-most non-class scope.
+ The latter is needed for implicit declarations. */
+
+void
+pushtag (name, type, globalize)
+ tree name, type;
+ int globalize;
+{
+ register struct binding_level *b;
+ tree context = 0;
+ tree c_decl = 0;
+
+ b = inner_binding_level;
+ while (b->tag_transparent
+ || (globalize && b->parm_flag == 2))
+ b = b->level_chain;
+
+ if (b == global_binding_level)
+ b->tags = perm_tree_cons (name, type, b->tags);
+ else
+ b->tags = saveable_tree_cons (name, type, b->tags);
+
+ if (name)
+ {
+ context = type ? TYPE_CONTEXT (type) : NULL_TREE;
+ if (! context && ! globalize)
+ context = current_scope ();
+ if (context)
+ c_decl = TREE_CODE (context) == FUNCTION_DECL
+ ? context : TYPE_NAME (context);
+
+ /* Record the identifier as the type's name if it has none. */
+ if (TYPE_NAME (type) == NULL_TREE)
+ TYPE_NAME (type) = name;
+
+ /* Do C++ gratuitous typedefing. */
+ if (IDENTIFIER_TYPE_VALUE (name) != type
+ && (TREE_CODE (type) != RECORD_TYPE
+ || b->parm_flag != 2
+ || !CLASSTYPE_DECLARED_EXCEPTION (type)))
+ {
+ register tree d;
+ int newdecl = 0;
+
+ if (b->parm_flag != 2
+ || TYPE_SIZE (current_class_type) != NULL_TREE)
+ {
+ d = lookup_nested_type (type, c_decl);
+
+ if (d == NULL_TREE)
+ {
+ newdecl = 1;
+#if 0 /* not yet, should get fixed properly later */
+ d = make_type_decl (name, type);
+#else
+ d = build_decl (TYPE_DECL, name, type);
+#endif
+#ifdef DWARF_DEBUGGING_INFO
+ if (write_symbols == DWARF_DEBUG)
+ {
+ /* Mark the TYPE_DECL node we created just above as an
+ gratuitous one. We need to do this so that dwarfout.c
+ will understand that it is not supposed to output a
+ TAG_typedef DIE for it. */
+ DECL_IGNORED_P (d) = 1;
+ }
+#endif /* DWARF_DEBUGGING_INFO */
+ set_identifier_type_value_with_scope (name, type, b);
+ }
+ else
+ d = TYPE_NAME (d);
+
+ /* If it is anonymous, then we are called from pushdecl,
+ and we don't want to infinitely recurse. Also, if the
+ name is already in scope, we don't want to push it
+ again--pushdecl is only for pushing new decls. */
+ if (! ANON_AGGRNAME_P (name)
+ && TYPE_NAME (type)
+ && (TREE_CODE (TYPE_NAME (type)) != TYPE_DECL
+ || lookup_name (name, 1) != TYPE_NAME (type)))
+ {
+ if (b->parm_flag == 2)
+ d = pushdecl_class_level (d);
+ else
+ d = pushdecl_with_scope (d, b);
+ }
+ }
+ else
+ {
+ /* Make nested declarations go into class-level scope. */
+ newdecl = 1;
+ d = build_decl (TYPE_DECL, name, type);
+#ifdef DWARF_DEBUGGING_INFO
+ if (write_symbols == DWARF_DEBUG)
+ {
+ /* Mark the TYPE_DECL node we created just above as an
+ gratuitous one. We need to do this so that dwarfout.c
+ will understand that it is not supposed to output a
+ TAG_typedef DIE for it. */
+ DECL_IGNORED_P (d) = 1;
+ }
+#endif /* DWARF_DEBUGGING_INFO */
+ /* Make sure we're in this type's scope when we push the
+ decl for a template, otherwise class_binding_level will
+ be NULL and we'll end up dying inside of
+ push_class_level_binding. */
+ if (TREE_CODE (type) == UNINSTANTIATED_P_TYPE)
+ pushclass (type, 0);
+ d = pushdecl_class_level (d);
+ if (TREE_CODE (type) == UNINSTANTIATED_P_TYPE)
+ popclass (0);
+ }
+ if (write_symbols != DWARF_DEBUG)
+ {
+ if (ANON_AGGRNAME_P (name))
+ DECL_IGNORED_P (d) = 1;
+ }
+ TYPE_NAME (type) = d;
+
+ if (context == NULL_TREE)
+ /* Non-nested class. */
+ DECL_NESTED_TYPENAME (d) = name;
+ else if (context && TREE_CODE (context) == FUNCTION_DECL)
+ {
+ /* Function-nested class. */
+ set_nested_typename (d, DECL_ASSEMBLER_NAME (c_decl),
+ name, type);
+ /* This builds the links for classes nested in fn scope. */
+ DECL_CONTEXT (d) = context;
+ }
+/* else if (TYPE_SIZE (current_class_type) == NULL_TREE)
+*/
+ else if (context && IS_AGGR_TYPE (context))
+ {
+ /* Class-nested class. */
+ set_nested_typename (d, DECL_NESTED_TYPENAME (c_decl),
+ name, type);
+ /* This builds the links for classes nested in type scope. */
+ DECL_CONTEXT (d) = context;
+ }
+ TYPE_CONTEXT (type) = DECL_CONTEXT (d);
+ if (newdecl)
+ DECL_ASSEMBLER_NAME (d)
+ = get_identifier (build_overload_name (type, 1, 1));
+ }
+ if (b->parm_flag == 2)
+ {
+ TREE_NONLOCAL_FLAG (type) = 1;
+ if (TYPE_SIZE (current_class_type) == NULL_TREE)
+ CLASSTYPE_TAGS (current_class_type) = b->tags;
+ }
+ }
+
+ if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL)
+ /* Use the canonical TYPE_DECL for this node. */
+ TYPE_STUB_DECL (type) = TYPE_NAME (type);
+ else
+ {
+ /* Create a fake NULL-named TYPE_DECL node whose TREE_TYPE
+ will be the tagged type we just added to the current
+ binding level. This fake NULL-named TYPE_DECL node helps
+ dwarfout.c to know when it needs to output a
+ representation of a tagged type, and it also gives us a
+ convenient place to record the "scope start" address for
+ the tagged type. */
+
+#if 0 /* not yet, should get fixed properly later */
+ tree d = make_type_decl (NULL_TREE, type);
+#else
+ tree d = build_decl (TYPE_DECL, NULL_TREE, type);
+#endif
+ TYPE_STUB_DECL (type) = pushdecl_with_scope (d, b);
+ }
+}
+
+/* Counter used to create anonymous type names. */
+static int anon_cnt = 0;
+
+/* Return an IDENTIFIER which can be used as a name for
+ anonymous structs and unions. */
+tree
+make_anon_name ()
+{
+ char buf[32];
+
+ sprintf (buf, ANON_AGGRNAME_FORMAT, anon_cnt++);
+ return get_identifier (buf);
+}
+
+/* Clear the TREE_PURPOSE slot of tags which have anonymous typenames.
+ This keeps dbxout from getting confused. */
+void
+clear_anon_tags ()
+{
+ register struct binding_level *b;
+ register tree tags;
+ static int last_cnt = 0;
+
+ /* Fast out if no new anon names were declared. */
+ if (last_cnt == anon_cnt)
+ return;
+
+ b = current_binding_level;
+ while (b->tag_transparent)
+ b = b->level_chain;
+ tags = b->tags;
+ while (tags)
+ {
+ /* A NULL purpose means we have already processed all tags
+ from here to the end of the list. */
+ if (TREE_PURPOSE (tags) == NULL_TREE)
+ break;
+ if (ANON_AGGRNAME_P (TREE_PURPOSE (tags)))
+ TREE_PURPOSE (tags) = NULL_TREE;
+ tags = TREE_CHAIN (tags);
+ }
+ last_cnt = anon_cnt;
+}
+
+/* Subroutine of duplicate_decls: return truthvalue of whether
+ or not types of these decls match.
+
+ For C++, we must compare the parameter list so that `int' can match
+ `int&' in a parameter position, but `int&' is not confused with
+ `const int&'. */
+static int
+decls_match (newdecl, olddecl)
+ tree newdecl, olddecl;
+{
+ int types_match;
+
+ if (TREE_CODE (newdecl) == FUNCTION_DECL
+ && TREE_CODE (olddecl) == FUNCTION_DECL)
+ {
+ tree f1 = TREE_TYPE (newdecl);
+ tree f2 = TREE_TYPE (olddecl);
+ tree p1 = TYPE_ARG_TYPES (f1);
+ tree p2 = TYPE_ARG_TYPES (f2);
+
+ /* When we parse a static member function definition,
+ we put together a FUNCTION_DECL which thinks its type
+ is METHOD_TYPE. Change that to FUNCTION_TYPE, and
+ proceed. */
+ if (TREE_CODE (f1) == METHOD_TYPE && DECL_STATIC_FUNCTION_P (olddecl))
+ revert_static_member_fn (&newdecl, &f1, &p1);
+ else if (TREE_CODE (f2) == METHOD_TYPE
+ && DECL_STATIC_FUNCTION_P (newdecl))
+ revert_static_member_fn (&olddecl, &f2, &p2);
+
+ /* Here we must take care of the case where new default
+ parameters are specified. Also, warn if an old
+ declaration becomes ambiguous because default
+ parameters may cause the two to be ambiguous. */
+ if (TREE_CODE (f1) != TREE_CODE (f2))
+ {
+ if (TREE_CODE (f1) == OFFSET_TYPE)
+ cp_compiler_error ("`%D' redeclared as member function", newdecl);
+ else
+ cp_compiler_error ("`%D' redeclared as non-member function", newdecl);
+ return 0;
+ }
+
+ if (comptypes (TREE_TYPE (f1), TREE_TYPE (f2), 1))
+ {
+ if (! strict_prototypes_lang_c && DECL_LANGUAGE (olddecl) == lang_c
+ && p2 == NULL_TREE)
+ {
+ types_match = self_promoting_args_p (p1);
+ if (p1 == void_list_node)
+ TREE_TYPE (newdecl) = TREE_TYPE (olddecl);
+ }
+ else if (!strict_prototypes_lang_c && DECL_LANGUAGE (olddecl)==lang_c
+ && DECL_LANGUAGE (newdecl) == lang_c && p1 == NULL_TREE)
+ {
+ types_match = self_promoting_args_p (p2);
+ TREE_TYPE (newdecl) = TREE_TYPE (olddecl);
+ }
+ else
+ types_match = compparms (p1, p2, 3);
+ }
+ else
+ types_match = 0;
+ }
+ else if (TREE_CODE (newdecl) == TEMPLATE_DECL
+ && TREE_CODE (olddecl) == TEMPLATE_DECL)
+ {
+ tree newargs = DECL_TEMPLATE_PARMS (newdecl);
+ tree oldargs = DECL_TEMPLATE_PARMS (olddecl);
+ int i, len = TREE_VEC_LENGTH (newargs);
+
+ if (TREE_VEC_LENGTH (oldargs) != len)
+ return 0;
+
+ for (i = 0; i < len; i++)
+ {
+ tree newarg = TREE_VEC_ELT (newargs, i);
+ tree oldarg = TREE_VEC_ELT (oldargs, i);
+ if (TREE_CODE (newarg) != TREE_CODE (oldarg))
+ return 0;
+ else if (TREE_CODE (newarg) == IDENTIFIER_NODE)
+ /* continue */;
+ else if (! comptypes (TREE_TYPE (newarg), TREE_TYPE (oldarg), 1))
+ return 0;
+ }
+
+ if (DECL_TEMPLATE_IS_CLASS (newdecl)
+ != DECL_TEMPLATE_IS_CLASS (olddecl))
+ types_match = 0;
+ else if (DECL_TEMPLATE_IS_CLASS (newdecl))
+ types_match = 1;
+ else
+ types_match = decls_match (DECL_TEMPLATE_RESULT (olddecl),
+ DECL_TEMPLATE_RESULT (newdecl));
+ }
+ else
+ {
+ if (TREE_TYPE (newdecl) == error_mark_node)
+ types_match = TREE_TYPE (olddecl) == error_mark_node;
+ else if (TREE_TYPE (olddecl) == NULL_TREE)
+ types_match = TREE_TYPE (newdecl) == NULL_TREE;
+ else if (TREE_TYPE (newdecl) == NULL_TREE)
+ types_match = 0;
+ else
+ types_match = comptypes (TREE_TYPE (newdecl), TREE_TYPE (olddecl), 1);
+ }
+
+ return types_match;
+}
+
+/* If NEWDECL is `static' and an `extern' was seen previously,
+ warn about it. (OLDDECL may be NULL_TREE; NAME contains
+ information about previous usage as an `extern'.)
+
+ Note that this does not apply to the C++ case of declaring
+ a variable `extern const' and then later `const'.
+
+ Don't complain if -traditional, since traditional compilers
+ don't complain.
+
+ Don't complain about built-in functions, since they are beyond
+ the user's control. */
+
+static void
+warn_extern_redeclared_static (newdecl, olddecl)
+ tree newdecl, olddecl;
+{
+ tree name;
+
+ static char *explicit_extern_static_warning
+ = "`%D' was declared `extern' and later `static'";
+ static char *implicit_extern_static_warning
+ = "`%D' was declared implicitly `extern' and later `static'";
+
+ if (flag_traditional
+ || TREE_CODE (newdecl) == TYPE_DECL
+ || (! warn_extern_inline
+ && DECL_INLINE (newdecl)))
+ return;
+
+ name = DECL_ASSEMBLER_NAME (newdecl);
+ if (TREE_PUBLIC (name) && ! TREE_PUBLIC (newdecl))
+ {
+ /* It's okay to redeclare an ANSI built-in function as static,
+ or to declare a non-ANSI built-in function as anything. */
+ if (! (TREE_CODE (newdecl) == FUNCTION_DECL
+ && olddecl != NULL_TREE
+ && TREE_CODE (olddecl) == FUNCTION_DECL
+ && (DECL_BUILT_IN (olddecl)
+ || DECL_BUILT_IN_NONANSI (olddecl))))
+ {
+ cp_warning (IDENTIFIER_IMPLICIT_DECL (name)
+ ? implicit_extern_static_warning
+ : explicit_extern_static_warning, newdecl);
+ if (olddecl != NULL_TREE)
+ cp_warning_at ("previous declaration of `%D'", olddecl);
+ }
+ }
+}
+
+/* Handle when a new declaration NEWDECL has the same name as an old
+ one OLDDECL in the same binding contour. Prints an error message
+ if appropriate.
+
+ If safely possible, alter OLDDECL to look like NEWDECL, and return 1.
+ Otherwise, return 0. */
+
+int
+duplicate_decls (newdecl, olddecl)
+ register tree newdecl, olddecl;
+{
+ extern struct obstack permanent_obstack;
+ unsigned olddecl_uid = DECL_UID (olddecl);
+ int olddecl_friend = 0, types_match = 0;
+ int new_defines_function;
+ tree previous_c_decl = NULL_TREE;
+
+ types_match = decls_match (newdecl, olddecl);
+
+ if (TREE_CODE (olddecl) != TREE_LIST)
+ olddecl_friend = DECL_LANG_SPECIFIC (olddecl) && DECL_FRIEND_P (olddecl);
+
+ /* If either the type of the new decl or the type of the old decl is an
+ error_mark_node, then that implies that we have already issued an
+ error (earlier) for some bogus type specification, and in that case,
+ it is rather pointless to harass the user with yet more error message
+ about the same declaration, so well just pretent the types match here. */
+ if ((TREE_TYPE (newdecl)
+ && TREE_CODE (TREE_TYPE (newdecl)) == ERROR_MARK)
+ || (TREE_TYPE (olddecl)
+ && TREE_CODE (TREE_TYPE (olddecl)) == ERROR_MARK))
+ types_match = 1;
+
+ if (flag_traditional && TREE_CODE (newdecl) == FUNCTION_DECL
+ && IDENTIFIER_IMPLICIT_DECL (DECL_ASSEMBLER_NAME (newdecl)) == olddecl)
+ /* If -traditional, avoid error for redeclaring fcn
+ after implicit decl. */
+ ;
+ else if (TREE_CODE (olddecl) == FUNCTION_DECL
+ && (DECL_BUILT_IN (olddecl) || DECL_BUILT_IN_NONANSI (olddecl)))
+ {
+ /* If you declare a built-in or predefined function name as static,
+ the old definition is overridden, but optionally warn this was a
+ bad choice of name. Ditto for overloads. */
+ if (! TREE_PUBLIC (newdecl)
+ || (TREE_CODE (newdecl) == FUNCTION_DECL
+ && DECL_LANGUAGE (newdecl) != DECL_LANGUAGE (olddecl)))
+ {
+ if (warn_shadow)
+ cp_warning ("shadowing %s function `%#D'",
+ DECL_BUILT_IN (olddecl) ? "built-in" : "library",
+ olddecl);
+ /* Discard the old built-in function. */
+ return 0;
+ }
+ else if (! types_match)
+ {
+ if (TREE_CODE (newdecl) != FUNCTION_DECL)
+ {
+ /* If the built-in is not ansi, then programs can override
+ it even globally without an error. */
+ if (! DECL_BUILT_IN (olddecl))
+ cp_warning ("library function `%#D' redeclared as non-function `%#D'",
+ olddecl, newdecl);
+ else
+ {
+ cp_error ("declaration of `%#D'", newdecl);
+ cp_error ("conflicts with built-in declaration `%#D'",
+ olddecl);
+ }
+ return 0;
+ }
+
+ cp_warning ("declaration of `%#D'", newdecl);
+ cp_warning ("conflicts with built-in declaration `%#D'",
+ olddecl);
+ }
+ }
+ else if (TREE_CODE (olddecl) != TREE_CODE (newdecl))
+ {
+ if ((TREE_CODE (newdecl) == FUNCTION_DECL
+ && TREE_CODE (olddecl) == TEMPLATE_DECL
+ && ! DECL_TEMPLATE_IS_CLASS (olddecl))
+ || (TREE_CODE (olddecl) == FUNCTION_DECL
+ && TREE_CODE (newdecl) == TEMPLATE_DECL
+ && ! DECL_TEMPLATE_IS_CLASS (newdecl)))
+ return 0;
+
+ cp_error ("`%#D' redeclared as different kind of symbol", newdecl);
+ if (TREE_CODE (olddecl) == TREE_LIST)
+ olddecl = TREE_VALUE (olddecl);
+ cp_error_at ("previous declaration of `%#D'", olddecl);
+
+ /* New decl is completely inconsistent with the old one =>
+ tell caller to replace the old one. */
+
+ return 0;
+ }
+ else if (!types_match)
+ {
+ if (TREE_CODE (newdecl) == TEMPLATE_DECL)
+ {
+ /* The name of a class template may not be declared to refer to
+ any other template, class, function, object, namespace, value,
+ or type in the same scope. */
+ if (DECL_TEMPLATE_IS_CLASS (olddecl)
+ || DECL_TEMPLATE_IS_CLASS (newdecl))
+ {
+ cp_error ("declaration of template `%#D'", newdecl);
+ cp_error_at ("conflicts with previous declaration `%#D'",
+ olddecl);
+ }
+ return 0;
+ }
+ if (TREE_CODE (newdecl) == FUNCTION_DECL)
+ {
+ if (DECL_LANGUAGE (newdecl) == lang_c
+ && DECL_LANGUAGE (olddecl) == lang_c)
+ {
+ cp_error ("declaration of C function `%#D' conflicts with",
+ newdecl);
+ cp_error_at ("previous declaration `%#D' here", olddecl);
+ }
+
+ if (compparms (TYPE_ARG_TYPES (TREE_TYPE (newdecl)),
+ TYPE_ARG_TYPES (TREE_TYPE (olddecl)), 2))
+ {
+ cp_error ("new declaration `%#D'", newdecl);
+ cp_error_at ("ambiguates old declaration `%#D'", olddecl);
+ }
+ else
+ return 0;
+ }
+
+ /* Already complained about this, so don't do so again. */
+ else if (current_class_type == NULL_TREE
+ || IDENTIFIER_ERROR_LOCUS (DECL_ASSEMBLER_NAME (newdecl)) != current_class_type)
+ {
+ /* Since we're doing this before finish_struct can set the
+ line number on NEWDECL, we just do a regular error here. */
+ if (DECL_SOURCE_LINE (newdecl) == 0)
+ cp_error ("conflicting types for `%#D'", newdecl);
+ else
+ cp_error_at ("conflicting types for `%#D'", newdecl);
+ cp_error_at ("previous declaration as `%#D'", olddecl);
+ }
+ }
+ else
+ {
+ char *errmsg = redeclaration_error_message (newdecl, olddecl);
+ if (errmsg)
+ {
+ cp_error (errmsg, newdecl);
+ if (DECL_NAME (olddecl) != NULL_TREE)
+ cp_error_at ((DECL_INITIAL (olddecl)
+ && current_binding_level == global_binding_level)
+ ? "`%#D' previously defined here"
+ : "`%#D' previously declared here", olddecl);
+ }
+ else if (TREE_CODE (olddecl) == FUNCTION_DECL
+ && DECL_INITIAL (olddecl) != NULL_TREE
+ && TYPE_ARG_TYPES (TREE_TYPE (olddecl)) == NULL_TREE
+ && TYPE_ARG_TYPES (TREE_TYPE (newdecl)) != NULL_TREE)
+ {
+ /* Prototype decl follows defn w/o prototype. */
+ cp_warning_at ("prototype for `%#D'", newdecl);
+ cp_warning_at ("follows non-prototype definition here", olddecl);
+ }
+ else if (TREE_CODE (olddecl) == FUNCTION_DECL
+ && DECL_LANGUAGE (newdecl) != DECL_LANGUAGE (olddecl))
+ {
+ /* extern "C" int foo ();
+ int foo () { bar (); }
+ is OK. */
+ if (current_lang_stack == current_lang_base)
+ DECL_LANGUAGE (newdecl) = DECL_LANGUAGE (olddecl);
+ else
+ {
+ cp_error_at ("previous declaration of `%#D' with %L linkage",
+ olddecl, DECL_LANGUAGE (olddecl));
+ cp_error ("conflicts with new declaration with %L linkage",
+ DECL_LANGUAGE (newdecl));
+ }
+ }
+
+ /* These bits are logically part of the type. */
+ if (pedantic
+ && (TREE_READONLY (newdecl) != TREE_READONLY (olddecl)
+ || TREE_THIS_VOLATILE (newdecl) != TREE_THIS_VOLATILE (olddecl)))
+ cp_error_at ("type qualifiers for `%D' conflict with previous decl",
+ newdecl);
+ }
+
+ /* If new decl is `static' and an `extern' was seen previously,
+ warn about it. */
+ warn_extern_redeclared_static (newdecl, olddecl);
+
+ /* We have committed to returning 1 at this point. */
+ if (TREE_CODE (newdecl) == FUNCTION_DECL)
+ {
+ /* Now that functions must hold information normally held
+ by field decls, there is extra work to do so that
+ declaration information does not get destroyed during
+ definition. */
+ if (DECL_VINDEX (olddecl))
+ DECL_VINDEX (newdecl) = DECL_VINDEX (olddecl);
+ if (DECL_CONTEXT (olddecl))
+ DECL_CONTEXT (newdecl) = DECL_CONTEXT (olddecl);
+ if (DECL_CLASS_CONTEXT (olddecl))
+ DECL_CLASS_CONTEXT (newdecl) = DECL_CLASS_CONTEXT (olddecl);
+ if (DECL_CHAIN (newdecl) == NULL_TREE)
+ DECL_CHAIN (newdecl) = DECL_CHAIN (olddecl);
+ if (DECL_NEXT_METHOD (newdecl) == NULL_TREE)
+ DECL_NEXT_METHOD (newdecl) = DECL_NEXT_METHOD (olddecl);
+ if (DECL_PENDING_INLINE_INFO (newdecl) == (struct pending_inline *)0)
+ DECL_PENDING_INLINE_INFO (newdecl) = DECL_PENDING_INLINE_INFO (olddecl);
+ }
+
+ /* Deal with C++: must preserve virtual function table size. */
+ if (TREE_CODE (olddecl) == TYPE_DECL)
+ {
+ register tree newtype = TREE_TYPE (newdecl);
+ register tree oldtype = TREE_TYPE (olddecl);
+
+ if (newtype != error_mark_node && oldtype != error_mark_node
+ && TYPE_LANG_SPECIFIC (newtype) && TYPE_LANG_SPECIFIC (oldtype))
+ {
+ CLASSTYPE_VSIZE (newtype) = CLASSTYPE_VSIZE (oldtype);
+ CLASSTYPE_FRIEND_CLASSES (newtype)
+ = CLASSTYPE_FRIEND_CLASSES (oldtype);
+ }
+#if 0
+ /* why assert here? Just because debugging information is
+ messed up? (mrs) */
+ /* it happens on something like:
+ typedef struct Thing {
+ Thing();
+ int x;
+ } Thing;
+ */
+ my_friendly_assert (DECL_IGNORED_P (olddecl) == DECL_IGNORED_P (newdecl),
+ 139);
+#endif
+ }
+
+ /* Special handling ensues if new decl is a function definition. */
+ new_defines_function = (TREE_CODE (newdecl) == FUNCTION_DECL
+ && DECL_INITIAL (newdecl) != NULL_TREE);
+
+ /* Optionally warn about more than one declaration for the same name,
+ but don't warn about a function declaration followed by a definition. */
+ if (warn_redundant_decls
+ && ! DECL_ARTIFICIAL (olddecl)
+ && !(new_defines_function && DECL_INITIAL (olddecl) == NULL_TREE)
+ /* Don't warn about extern decl followed by (tentative) definition. */
+ && !(DECL_EXTERNAL (olddecl) && ! DECL_EXTERNAL (newdecl)))
+ {
+ cp_warning ("redundant redeclaration of `%D' in same scope", newdecl);
+ cp_warning ("previous declaration of `%D'", olddecl);
+ }
+
+ /* Copy all the DECL_... slots specified in the new decl
+ except for any that we copy here from the old type. */
+
+ if (types_match)
+ {
+ /* Automatically handles default parameters. */
+ tree oldtype = TREE_TYPE (olddecl);
+ /* Merge the data types specified in the two decls. */
+ tree newtype = common_type (TREE_TYPE (newdecl), TREE_TYPE (olddecl));
+
+ /* Make sure we put the new type in the same obstack as the old ones.
+ If the old types are not both in the same obstack, use the permanent
+ one. */
+ if (oldtype && TYPE_OBSTACK (oldtype) == TYPE_OBSTACK (newtype))
+ push_obstacks (TYPE_OBSTACK (oldtype), TYPE_OBSTACK (oldtype));
+ else
+ {
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+ }
+
+ if (TREE_CODE (newdecl) == VAR_DECL)
+ DECL_THIS_EXTERN (newdecl) |= DECL_THIS_EXTERN (olddecl);
+ /* Do this after calling `common_type' so that default
+ parameters don't confuse us. */
+ else if (TREE_CODE (newdecl) == FUNCTION_DECL
+ && (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (newdecl))
+ != TYPE_RAISES_EXCEPTIONS (TREE_TYPE (olddecl))))
+ {
+ tree ctype = NULL_TREE;
+ ctype = DECL_CLASS_CONTEXT (newdecl);
+ TREE_TYPE (newdecl) = build_exception_variant (ctype, newtype,
+ TYPE_RAISES_EXCEPTIONS (TREE_TYPE (newdecl)));
+ TREE_TYPE (olddecl) = build_exception_variant (ctype, newtype,
+ TYPE_RAISES_EXCEPTIONS (oldtype));
+
+ if (! compexcepttypes (TREE_TYPE (newdecl), TREE_TYPE (olddecl), 0))
+ {
+ cp_error ("declaration of `%D' raises different exceptions...",
+ newdecl);
+ cp_error_at ("...from previous declaration here", olddecl);
+ }
+ }
+ TREE_TYPE (newdecl) = TREE_TYPE (olddecl) = newtype;
+
+ /* Lay the type out, unless already done. */
+ if (oldtype != TREE_TYPE (newdecl))
+ {
+ if (TREE_TYPE (newdecl) != error_mark_node)
+ layout_type (TREE_TYPE (newdecl));
+ if (TREE_CODE (newdecl) != FUNCTION_DECL
+ && TREE_CODE (newdecl) != TYPE_DECL
+ && TREE_CODE (newdecl) != CONST_DECL
+ && TREE_CODE (newdecl) != TEMPLATE_DECL)
+ layout_decl (newdecl, 0);
+ }
+ else
+ {
+ /* Since the type is OLDDECL's, make OLDDECL's size go with. */
+ DECL_SIZE (newdecl) = DECL_SIZE (olddecl);
+ }
+
+ /* Merge the type qualifiers. */
+ if (TREE_READONLY (newdecl))
+ TREE_READONLY (olddecl) = 1;
+ if (TREE_THIS_VOLATILE (newdecl))
+ TREE_THIS_VOLATILE (olddecl) = 1;
+
+ /* Merge the initialization information. */
+ if (DECL_INITIAL (newdecl) == NULL_TREE
+ && DECL_INITIAL (olddecl) != NULL_TREE)
+ {
+ DECL_INITIAL (newdecl) = DECL_INITIAL (olddecl);
+ DECL_SOURCE_FILE (newdecl) = DECL_SOURCE_FILE (olddecl);
+ DECL_SOURCE_LINE (newdecl) = DECL_SOURCE_LINE (olddecl);
+ }
+
+ /* Merge the section attribute.
+ We want to issue an error if the sections conflict but that must be
+ done later in decl_attributes since we are called before attributes
+ are assigned. */
+ if (DECL_SECTION_NAME (newdecl) == NULL_TREE)
+ DECL_SECTION_NAME (newdecl) = DECL_SECTION_NAME (olddecl);
+
+ /* Keep the old rtl since we can safely use it, unless it's the
+ call to abort() used for abstract virtuals. */
+ if ((DECL_LANG_SPECIFIC (olddecl)
+ && !DECL_ABSTRACT_VIRTUAL_P (olddecl))
+ || DECL_RTL (olddecl) != DECL_RTL (abort_fndecl))
+ DECL_RTL (newdecl) = DECL_RTL (olddecl);
+
+ pop_obstacks ();
+ }
+ /* If cannot merge, then use the new type and qualifiers,
+ and don't preserve the old rtl. */
+ else
+ {
+ /* Clean out any memory we had of the old declaration. */
+ tree oldstatic = value_member (olddecl, static_aggregates);
+ if (oldstatic)
+ TREE_VALUE (oldstatic) = error_mark_node;
+
+ TREE_TYPE (olddecl) = TREE_TYPE (newdecl);
+ TREE_READONLY (olddecl) = TREE_READONLY (newdecl);
+ TREE_THIS_VOLATILE (olddecl) = TREE_THIS_VOLATILE (newdecl);
+ TREE_SIDE_EFFECTS (olddecl) = TREE_SIDE_EFFECTS (newdecl);
+ }
+
+ /* Merge the storage class information. */
+ if (DECL_EXTERNAL (newdecl))
+ {
+ TREE_STATIC (newdecl) = TREE_STATIC (olddecl);
+ DECL_EXTERNAL (newdecl) = DECL_EXTERNAL (olddecl);
+
+ if (TREE_CODE (newdecl) != FUNCTION_DECL)
+ TREE_PUBLIC (newdecl) = TREE_PUBLIC (olddecl);
+ }
+ else
+ {
+ TREE_STATIC (olddecl) = TREE_STATIC (newdecl);
+ /* A `const' which was not declared `extern' and is
+ in static storage is invisible. */
+ if (TREE_CODE (newdecl) == VAR_DECL
+ && TREE_READONLY (newdecl) && TREE_STATIC (newdecl)
+ && ! DECL_THIS_EXTERN (newdecl))
+ TREE_PUBLIC (newdecl) = 0;
+ else if (TREE_CODE (newdecl) != FUNCTION_DECL)
+ TREE_PUBLIC (olddecl) = TREE_PUBLIC (newdecl);
+ }
+
+ /* For functions, static overrides non-static. */
+ if (TREE_CODE (newdecl) == FUNCTION_DECL)
+ {
+ TREE_PUBLIC (newdecl) &= TREE_PUBLIC (olddecl);
+ /* This is since we don't automatically
+ copy the attributes of NEWDECL into OLDDECL. */
+ TREE_PUBLIC (olddecl) = TREE_PUBLIC (newdecl);
+ /* If this clears `static', clear it in the identifier too. */
+ if (! TREE_PUBLIC (olddecl))
+ TREE_PUBLIC (DECL_ASSEMBLER_NAME (olddecl)) = 0;
+ }
+
+ /* If either decl says `inline', this fn is inline,
+ unless its definition was passed already. */
+ if (DECL_INLINE (newdecl) && DECL_INITIAL (olddecl) == NULL_TREE)
+ DECL_INLINE (olddecl) = 1;
+ DECL_INLINE (newdecl) = DECL_INLINE (olddecl);
+
+ if (TREE_CODE (newdecl) == FUNCTION_DECL)
+ {
+ if (! types_match)
+ {
+ DECL_LANGUAGE (olddecl) = DECL_LANGUAGE (newdecl);
+ DECL_ASSEMBLER_NAME (olddecl) = DECL_ASSEMBLER_NAME (newdecl);
+ DECL_ARGUMENTS (olddecl) = DECL_ARGUMENTS (newdecl);
+ DECL_RESULT (olddecl) = DECL_RESULT (newdecl);
+ DECL_RTL (olddecl) = DECL_RTL (newdecl);
+ }
+ if (new_defines_function)
+ /* If defining a function declared with other language
+ linkage, use the previously declared language linkage. */
+ DECL_LANGUAGE (newdecl) = DECL_LANGUAGE (olddecl);
+ else
+ {
+ /* If redeclaring a builtin function, and not a definition,
+ it stays built in. */
+ if (DECL_BUILT_IN (olddecl))
+ {
+ DECL_BUILT_IN (newdecl) = 1;
+ DECL_FUNCTION_CODE (newdecl) = DECL_FUNCTION_CODE (olddecl);
+ /* If we're keeping the built-in definition, keep the rtl,
+ regardless of declaration matches. */
+ DECL_RTL (newdecl) = DECL_RTL (olddecl);
+ }
+ else
+ DECL_FRAME_SIZE (newdecl) = DECL_FRAME_SIZE (olddecl);
+
+ DECL_RESULT (newdecl) = DECL_RESULT (olddecl);
+ if ((DECL_SAVED_INSNS (newdecl) = DECL_SAVED_INSNS (olddecl)))
+ /* Previously saved insns go together with
+ the function's previous definition. */
+ DECL_INITIAL (newdecl) = DECL_INITIAL (olddecl);
+ /* Don't clear out the arguments if we're redefining a function. */
+ if (DECL_ARGUMENTS (olddecl))
+ DECL_ARGUMENTS (newdecl) = DECL_ARGUMENTS (olddecl);
+ }
+ }
+
+ if (TREE_CODE (newdecl) == TEMPLATE_DECL)
+ {
+ if (DECL_TEMPLATE_INFO (olddecl)->length)
+ DECL_TEMPLATE_INFO (newdecl) = DECL_TEMPLATE_INFO (olddecl);
+ DECL_TEMPLATE_MEMBERS (newdecl) = DECL_TEMPLATE_MEMBERS (olddecl);
+ DECL_TEMPLATE_INSTANTIATIONS (newdecl)
+ = DECL_TEMPLATE_INSTANTIATIONS (olddecl);
+ }
+
+ /* Now preserve various other info from the definition. */
+ TREE_ADDRESSABLE (newdecl) = TREE_ADDRESSABLE (olddecl);
+ TREE_ASM_WRITTEN (newdecl) = TREE_ASM_WRITTEN (olddecl);
+ DECL_COMMON (newdecl) = DECL_COMMON (olddecl);
+
+ /* Don't really know how much of the language-specific
+ values we should copy from old to new. */
+ if (DECL_LANG_SPECIFIC (olddecl))
+ {
+ DECL_IN_AGGR_P (newdecl) = DECL_IN_AGGR_P (olddecl);
+ DECL_ACCESS (newdecl) = DECL_ACCESS (olddecl);
+ }
+
+ if (TREE_CODE (newdecl) == FUNCTION_DECL)
+ {
+ int function_size;
+ struct lang_decl *ol = DECL_LANG_SPECIFIC (olddecl);
+ struct lang_decl *nl = DECL_LANG_SPECIFIC (newdecl);
+
+ function_size = sizeof (struct tree_decl);
+
+ bcopy ((char *) newdecl + sizeof (struct tree_common),
+ (char *) olddecl + sizeof (struct tree_common),
+ function_size - sizeof (struct tree_common));
+
+ /* Can we safely free the storage used by newdecl? */
+
+#define ROUND(x) ((x + obstack_alignment_mask (&permanent_obstack)) \
+ & ~ obstack_alignment_mask (&permanent_obstack))
+
+ if ((char *)newdecl + ROUND (function_size)
+ + ROUND (sizeof (struct lang_decl))
+ == obstack_next_free (&permanent_obstack))
+ {
+ DECL_MAIN_VARIANT (newdecl) = olddecl;
+ DECL_LANG_SPECIFIC (olddecl) = ol;
+ bcopy ((char *)nl, (char *)ol, sizeof (struct lang_decl));
+
+ obstack_free (&permanent_obstack, newdecl);
+ }
+ else if (LANG_DECL_PERMANENT (ol))
+ {
+ if (DECL_MAIN_VARIANT (olddecl) == olddecl)
+ {
+ /* Save these lang_decls that would otherwise be lost. */
+ extern tree free_lang_decl_chain;
+ tree free_lang_decl = (tree) ol;
+ TREE_CHAIN (free_lang_decl) = free_lang_decl_chain;
+ free_lang_decl_chain = free_lang_decl;
+ }
+ else
+ {
+ /* Storage leak. */
+ }
+ }
+ }
+ else
+ {
+ bcopy ((char *) newdecl + sizeof (struct tree_common),
+ (char *) olddecl + sizeof (struct tree_common),
+ sizeof (struct tree_decl) - sizeof (struct tree_common)
+ + tree_code_length [(int)TREE_CODE (newdecl)] * sizeof (char *));
+ }
+
+ DECL_UID (olddecl) = olddecl_uid;
+ if (olddecl_friend)
+ DECL_FRIEND_P (olddecl) = 1;
+
+ return 1;
+}
+
+/* Record a decl-node X as belonging to the current lexical scope.
+ Check for errors (such as an incompatible declaration for the same
+ name already seen in the same scope).
+
+ Returns either X or an old decl for the same name.
+ If an old decl is returned, it may have been smashed
+ to agree with what X says. */
+
+tree
+pushdecl (x)
+ tree x;
+{
+ register tree t;
+#if 0 /* not yet, should get fixed properly later */
+ register tree name;
+#else
+ register tree name = DECL_ASSEMBLER_NAME (x);
+#endif
+ register struct binding_level *b = current_binding_level;
+
+#if 0
+ static int nglobals; int len;
+
+ len = list_length (global_binding_level->names);
+ if (len < nglobals)
+ my_friendly_abort (8);
+ else if (len > nglobals)
+ nglobals = len;
+#endif
+
+ if (x != current_function_decl
+ /* Don't change DECL_CONTEXT of virtual methods. */
+ && (TREE_CODE (x) != FUNCTION_DECL || !DECL_VIRTUAL_P (x))
+ && ! DECL_CONTEXT (x))
+ DECL_CONTEXT (x) = current_function_decl;
+ /* A local declaration for a function doesn't constitute nesting. */
+ if (TREE_CODE (x) == FUNCTION_DECL && DECL_INITIAL (x) == 0)
+ DECL_CONTEXT (x) = 0;
+
+#if 0 /* not yet, should get fixed properly later */
+ /* For functions and class static data, we currently look up the encoded
+ form of the name. For types, we want the real name. The former will
+ probably be changed soon, according to MDT. */
+ if (TREE_CODE (x) == FUNCTION_DECL || TREE_CODE (x) == VAR_DECL)
+ name = DECL_ASSEMBLER_NAME (x);
+ else
+ name = DECL_NAME (x);
+#else
+ /* Type are looked up using the DECL_NAME, as that is what the rest of the
+ compiler wants to use. */
+ if (TREE_CODE (x) == TYPE_DECL)
+ name = DECL_NAME (x);
+#endif
+
+ if (name)
+ {
+ char *file;
+ int line;
+
+ t = lookup_name_current_level (name);
+ if (t == error_mark_node)
+ {
+ /* error_mark_node is 0 for a while during initialization! */
+ t = NULL_TREE;
+ cp_error_at ("`%#D' used prior to declaration", x);
+ }
+
+ else if (t != NULL_TREE)
+ {
+ if (TREE_CODE (t) == PARM_DECL)
+ {
+ if (DECL_CONTEXT (t) == NULL_TREE)
+ fatal ("parse errors have confused me too much");
+ }
+ file = DECL_SOURCE_FILE (t);
+ line = DECL_SOURCE_LINE (t);
+
+ if (((TREE_CODE (x) == FUNCTION_DECL && DECL_LANGUAGE (x) == lang_c)
+ || (TREE_CODE (x) == TEMPLATE_DECL
+ && ! DECL_TEMPLATE_IS_CLASS (x)))
+ && is_overloaded_fn (t))
+ /* don't do anything just yet */;
+ else if (TREE_CODE (t) != TREE_CODE (x))
+ {
+ if (TREE_CODE (t) == TYPE_DECL || TREE_CODE (x) == TYPE_DECL)
+ {
+ /* We do nothing special here, because C++ does such nasty
+ things with TYPE_DECLs. Instead, just let the TYPE_DECL
+ get shadowed, and know that if we need to find a TYPE_DECL
+ for a given name, we can look in the IDENTIFIER_TYPE_VALUE
+ slot of the identifier. */
+ ;
+ }
+ else if (duplicate_decls (x, t))
+ return t;
+ }
+ else if (duplicate_decls (x, t))
+ {
+#if 0
+ /* This is turned off until I have time to do it right (bpk). */
+
+ /* Also warn if they did a prototype with `static' on it, but
+ then later left the `static' off. */
+ if (! TREE_PUBLIC (name) && TREE_PUBLIC (x))
+ {
+ if (DECL_LANG_SPECIFIC (t) && DECL_FRIEND_P (t))
+ return t;
+
+ if (extra_warnings)
+ {
+ cp_warning ("`static' missing from declaration of `%D'",
+ t);
+ warning_with_file_and_line (file, line,
+ "previous declaration of `%s'",
+ decl_as_string (t, 0));
+ }
+
+ /* Now fix things so it'll do what they expect. */
+ if (current_function_decl)
+ TREE_PUBLIC (current_function_decl) = 0;
+ }
+ /* Due to interference in memory reclamation (X may be
+ obstack-deallocated at this point), we must guard against
+ one really special case. [jason: This should be handled
+ by start_function] */
+ if (current_function_decl == x)
+ current_function_decl = t;
+#endif
+ if (TREE_CODE (t) == TYPE_DECL)
+ SET_IDENTIFIER_TYPE_VALUE (name, TREE_TYPE (t));
+
+ return t;
+ }
+ }
+
+ if (TREE_CODE (x) == FUNCTION_DECL && ! DECL_FUNCTION_MEMBER_P (x))
+ {
+ t = push_overloaded_decl (x, 1);
+ if (t != x || DECL_LANGUAGE (x) == lang_c)
+ return t;
+ }
+ else if (TREE_CODE (x) == TEMPLATE_DECL && ! DECL_TEMPLATE_IS_CLASS (x))
+ return push_overloaded_decl (x, 0);
+
+ /* If declaring a type as a typedef, and the type has no known
+ typedef name, install this TYPE_DECL as its typedef name. */
+ if (TREE_CODE (x) == TYPE_DECL)
+ {
+ tree type = TREE_TYPE (x);
+ tree name = (type != error_mark_node) ? TYPE_NAME (type) : x;
+
+ if (name == NULL_TREE || TREE_CODE (name) != TYPE_DECL)
+ {
+ /* If these are different names, and we're at the global
+ binding level, make two equivalent definitions. */
+ name = x;
+ if (global_bindings_p ())
+ TYPE_NAME (type) = x;
+ }
+ else
+ {
+ tree tname = DECL_NAME (name);
+
+ if (global_bindings_p () && ANON_AGGRNAME_P (tname))
+ {
+ /* do gratuitous C++ typedefing, and make sure that
+ we access this type either through TREE_TYPE field
+ or via the tags list. */
+ TYPE_NAME (TREE_TYPE (x)) = x;
+ pushtag (tname, TREE_TYPE (x), 0);
+ }
+ }
+ my_friendly_assert (TREE_CODE (name) == TYPE_DECL, 140);
+
+ if (DECL_NAME (name) && !DECL_NESTED_TYPENAME (name))
+ set_nested_typename (x, current_class_name,
+ DECL_NAME (name), type);
+
+ if (type != error_mark_node
+ && TYPE_NAME (type)
+ && TYPE_IDENTIFIER (type))
+ set_identifier_type_value_with_scope (DECL_NAME (x), type, b);
+ }
+
+ /* Multiple external decls of the same identifier ought to match.
+
+ We get warnings about inline functions where they are defined.
+ We get warnings about other functions from push_overloaded_decl.
+
+ Avoid duplicate warnings where they are used. */
+ if (TREE_PUBLIC (x) && TREE_CODE (x) != FUNCTION_DECL)
+ {
+ tree decl;
+
+ if (IDENTIFIER_GLOBAL_VALUE (name) != NULL_TREE
+ && (DECL_EXTERNAL (IDENTIFIER_GLOBAL_VALUE (name))
+ || TREE_PUBLIC (IDENTIFIER_GLOBAL_VALUE (name))))
+ decl = IDENTIFIER_GLOBAL_VALUE (name);
+ else
+ decl = NULL_TREE;
+
+ if (decl
+ /* If different sort of thing, we already gave an error. */
+ && TREE_CODE (decl) == TREE_CODE (x)
+ && ! comptypes (TREE_TYPE (x), TREE_TYPE (decl), 1))
+ {
+ cp_pedwarn ("type mismatch with previous external decl", x);
+ cp_pedwarn_at ("previous external decl of `%#D'", decl);
+ }
+ }
+
+ /* In PCC-compatibility mode, extern decls of vars with no current decl
+ take effect at top level no matter where they are. */
+ if (flag_traditional && DECL_EXTERNAL (x)
+ && lookup_name (name, 0) == NULL_TREE)
+ b = global_binding_level;
+
+ /* This name is new in its binding level.
+ Install the new declaration and return it. */
+ if (b == global_binding_level)
+ {
+ /* Install a global value. */
+
+ /* Rule for VAR_DECLs, but not for other kinds of _DECLs:
+ A `const' which was not declared `extern' is invisible. */
+ if (TREE_CODE (x) == VAR_DECL
+ && TREE_READONLY (x) && ! DECL_THIS_EXTERN (x))
+ TREE_PUBLIC (x) = 0;
+
+ /* If the first global decl has external linkage,
+ warn if we later see static one. */
+ if (IDENTIFIER_GLOBAL_VALUE (name) == NULL_TREE && TREE_PUBLIC (x))
+ TREE_PUBLIC (name) = 1;
+
+ /* Don't install a TYPE_DECL if we already have another
+ sort of _DECL with that name. */
+ if (TREE_CODE (x) != TYPE_DECL
+ || t == NULL_TREE
+ || TREE_CODE (t) == TYPE_DECL)
+ IDENTIFIER_GLOBAL_VALUE (name) = x;
+
+ /* Don't forget if the function was used via an implicit decl. */
+ if (IDENTIFIER_IMPLICIT_DECL (name)
+ && TREE_USED (IDENTIFIER_IMPLICIT_DECL (name)))
+ TREE_USED (x) = 1;
+
+ /* Don't forget if its address was taken in that way. */
+ if (IDENTIFIER_IMPLICIT_DECL (name)
+ && TREE_ADDRESSABLE (IDENTIFIER_IMPLICIT_DECL (name)))
+ TREE_ADDRESSABLE (x) = 1;
+
+ /* Warn about mismatches against previous implicit decl. */
+ if (IDENTIFIER_IMPLICIT_DECL (name) != NULL_TREE
+ /* If this real decl matches the implicit, don't complain. */
+ && ! (TREE_CODE (x) == FUNCTION_DECL
+ && TREE_TYPE (TREE_TYPE (x)) == integer_type_node))
+ cp_warning
+ ("`%D' was previously implicitly declared to return `int'", x);
+
+ /* If new decl is `static' and an `extern' was seen previously,
+ warn about it. */
+ if (x != NULL_TREE && t != NULL_TREE && decls_match (x, t))
+ warn_extern_redeclared_static (x, t);
+ }
+ else
+ {
+ /* Here to install a non-global value. */
+ tree oldlocal = IDENTIFIER_LOCAL_VALUE (name);
+ tree oldglobal = IDENTIFIER_GLOBAL_VALUE (name);
+
+ b->shadowed = tree_cons (name, oldlocal, b->shadowed);
+ IDENTIFIER_LOCAL_VALUE (name) = x;
+
+ /* If this is a TYPE_DECL, push it into the type value slot. */
+ if (TREE_CODE (x) == TYPE_DECL)
+ set_identifier_type_value_with_scope (name, TREE_TYPE (x), b);
+
+ /* If this is an extern function declaration, see if we
+ have a global definition or declaration for the function. */
+ if (oldlocal == NULL_TREE
+ && DECL_EXTERNAL (x) && !DECL_INLINE (x)
+ && oldglobal != NULL_TREE
+ && TREE_CODE (x) == FUNCTION_DECL
+ && TREE_CODE (oldglobal) == FUNCTION_DECL)
+ {
+ /* We have one. Their types must agree. */
+ if (! comptypes (TREE_TYPE (x), TREE_TYPE (oldglobal), 1))
+ {
+ cp_warning ("extern declaration of `%#D' doesn't match", x);
+ cp_warning_at ("global declaration `%#D'", oldglobal);
+ }
+ else
+ {
+ /* Inner extern decl is inline if global one is.
+ Copy enough to really inline it. */
+ if (DECL_INLINE (oldglobal))
+ {
+ DECL_INLINE (x) = DECL_INLINE (oldglobal);
+ DECL_INITIAL (x) = (current_function_decl == oldglobal
+ ? NULL_TREE : DECL_INITIAL (oldglobal));
+ DECL_SAVED_INSNS (x) = DECL_SAVED_INSNS (oldglobal);
+ DECL_FRAME_SIZE (x) = DECL_FRAME_SIZE (oldglobal);
+ DECL_ARGUMENTS (x) = DECL_ARGUMENTS (oldglobal);
+ DECL_RESULT (x) = DECL_RESULT (oldglobal);
+ TREE_ASM_WRITTEN (x) = TREE_ASM_WRITTEN (oldglobal);
+ DECL_ABSTRACT_ORIGIN (x) = oldglobal;
+ }
+ /* Inner extern decl is built-in if global one is. */
+ if (DECL_BUILT_IN (oldglobal))
+ {
+ DECL_BUILT_IN (x) = DECL_BUILT_IN (oldglobal);
+ DECL_FUNCTION_CODE (x) = DECL_FUNCTION_CODE (oldglobal);
+ }
+ /* Keep the arg types from a file-scope fcn defn. */
+ if (TYPE_ARG_TYPES (TREE_TYPE (oldglobal)) != NULL_TREE
+ && DECL_INITIAL (oldglobal)
+ && TYPE_ARG_TYPES (TREE_TYPE (x)) == NULL_TREE)
+ TREE_TYPE (x) = TREE_TYPE (oldglobal);
+ }
+ }
+ /* If we have a local external declaration,
+ and no file-scope declaration has yet been seen,
+ then if we later have a file-scope decl it must not be static. */
+ if (oldlocal == NULL_TREE
+ && oldglobal == NULL_TREE
+ && DECL_EXTERNAL (x)
+ && TREE_PUBLIC (x))
+ {
+ TREE_PUBLIC (name) = 1;
+ }
+
+ if (DECL_FROM_INLINE (x))
+ /* Inline decls shadow nothing. */;
+
+ /* Warn if shadowing an argument at the top level of the body. */
+ else if (oldlocal != NULL_TREE && !DECL_EXTERNAL (x)
+ && TREE_CODE (oldlocal) == PARM_DECL
+ && TREE_CODE (x) != PARM_DECL)
+ {
+ /* Go to where the parms should be and see if we
+ find them there. */
+ struct binding_level *b = current_binding_level->level_chain;
+
+ if (cleanup_label)
+ b = b->level_chain;
+
+ /* ARM $8.3 */
+ if (b->parm_flag == 1)
+ cp_error ("declaration of `%#D' shadows a parameter", name);
+ }
+ /* Maybe warn if shadowing something else. */
+ else if (warn_shadow && !DECL_EXTERNAL (x)
+ /* No shadow warnings for internally generated vars. */
+ && ! DECL_ARTIFICIAL (x)
+ /* No shadow warnings for vars made for inlining. */
+ && ! DECL_FROM_INLINE (x))
+ {
+ char *warnstring = NULL;
+
+ if (oldlocal != NULL_TREE && TREE_CODE (oldlocal) == PARM_DECL)
+ warnstring = "declaration of `%s' shadows a parameter";
+ else if (IDENTIFIER_CLASS_VALUE (name) != NULL_TREE
+ && !TREE_STATIC (name))
+ warnstring = "declaration of `%s' shadows a member of `this'";
+ else if (oldlocal != NULL_TREE)
+ warnstring = "declaration of `%s' shadows previous local";
+ else if (oldglobal != NULL_TREE)
+ warnstring = "declaration of `%s' shadows global declaration";
+
+ if (warnstring)
+ warning (warnstring, IDENTIFIER_POINTER (name));
+ }
+
+ /* If storing a local value, there may already be one (inherited).
+ If so, record it for restoration when this binding level ends. */
+ if (oldlocal != NULL_TREE)
+ b->shadowed = tree_cons (name, oldlocal, b->shadowed);
+ }
+
+ /* Keep count of variables in this level with incomplete type. */
+ if (TREE_CODE (x) != TEMPLATE_DECL
+ && TYPE_SIZE (TREE_TYPE (x)) == NULL_TREE
+ && PROMOTES_TO_AGGR_TYPE (TREE_TYPE (x), ARRAY_TYPE))
+ {
+ if (++b->n_incomplete == 0)
+ error ("too many incomplete variables at this point");
+ }
+ }
+
+ if (TREE_CODE (x) == TYPE_DECL && name != NULL_TREE)
+ {
+ if (current_class_name)
+ {
+ if (! TREE_MANGLED (name))
+ set_nested_typename (x, current_class_name, DECL_NAME (x),
+ TREE_TYPE (x));
+ }
+ }
+
+ /* Put decls on list in reverse order.
+ We will reverse them later if necessary. */
+ TREE_CHAIN (x) = b->names;
+ b->names = x;
+ if (! (b != global_binding_level || TREE_PERMANENT (x)))
+ my_friendly_abort (124);
+
+ return x;
+}
+
+/* Same as pushdecl, but define X in binding-level LEVEL. */
+
+static tree
+pushdecl_with_scope (x, level)
+ tree x;
+ struct binding_level *level;
+{
+ register struct binding_level *b = current_binding_level;
+
+ current_binding_level = level;
+ x = pushdecl (x);
+ current_binding_level = b;
+ return x;
+}
+
+/* Like pushdecl, only it places X in GLOBAL_BINDING_LEVEL,
+ if appropriate. */
+tree
+pushdecl_top_level (x)
+ tree x;
+{
+ register struct binding_level *b = inner_binding_level;
+ register tree t = pushdecl_with_scope (x, global_binding_level);
+
+ /* Now, the type_shadowed stack may screw us. Munge it so it does
+ what we want. */
+ if (TREE_CODE (x) == TYPE_DECL)
+ {
+ tree name = DECL_NAME (x);
+ tree newval;
+ tree *ptr = (tree *)0;
+ for (; b != global_binding_level; b = b->level_chain)
+ {
+ tree shadowed = b->type_shadowed;
+ for (; shadowed; shadowed = TREE_CHAIN (shadowed))
+ if (TREE_PURPOSE (shadowed) == name)
+ {
+ ptr = &TREE_VALUE (shadowed);
+ /* Can't break out of the loop here because sometimes
+ a binding level will have duplicate bindings for
+ PT names. It's gross, but I haven't time to fix it. */
+ }
+ }
+ newval = TREE_TYPE (x);
+ if (ptr == (tree *)0)
+ {
+ /* @@ This shouldn't be needed. My test case "zstring.cc" trips
+ up here if this is changed to an assertion. --KR */
+ SET_IDENTIFIER_TYPE_VALUE (name, newval);
+ }
+ else
+ {
+#if 0
+ /* Disabled this 11/10/92, since there are many cases which
+ behave just fine when *ptr doesn't satisfy either of these.
+ For example, nested classes declared as friends of their enclosing
+ class will not meet this criteria. (bpk) */
+ my_friendly_assert (*ptr == NULL_TREE || *ptr == newval, 141);
+#endif
+ *ptr = newval;
+ }
+ }
+ return t;
+}
+
+/* Like push_overloaded_decl, only it places X in GLOBAL_BINDING_LEVEL,
+ if appropriate. */
+void
+push_overloaded_decl_top_level (x, forget)
+ tree x;
+ int forget;
+{
+ struct binding_level *b = current_binding_level;
+
+ current_binding_level = global_binding_level;
+ push_overloaded_decl (x, forget);
+ current_binding_level = b;
+}
+
+/* Make the declaration of X appear in CLASS scope. */
+tree
+pushdecl_class_level (x)
+ tree x;
+{
+ /* Don't use DECL_ASSEMBLER_NAME here! Everything that looks in class
+ scope looks for the pre-mangled name. */
+ register tree name = DECL_NAME (x);
+
+ if (name)
+ {
+ if (TYPE_BEING_DEFINED (current_class_type))
+ {
+ /* Check for inconsistent use of this name in the class body.
+ Types, enums, and static vars are checked here; other
+ members are checked in finish_struct. */
+ tree icv = IDENTIFIER_CLASS_VALUE (name);
+
+ if (icv
+ /* Don't complain about inherited names. */
+ && id_in_current_class (name)
+ /* Or shadowed tags. */
+ && !(TREE_CODE (icv) == TYPE_DECL
+ && DECL_CONTEXT (icv) == current_class_type))
+ {
+ cp_error ("declaration of identifier `%D' as `%#D'", name, x);
+ cp_error_at ("conflicts with previous use in class as `%#D'",
+ icv);
+ }
+ }
+
+ push_class_level_binding (name, x);
+ if (TREE_CODE (x) == TYPE_DECL)
+ {
+ set_identifier_type_value (name, TREE_TYPE (x));
+ if (!DECL_NESTED_TYPENAME (x))
+ set_nested_typename (x, current_class_name, name, TREE_TYPE (x));
+ }
+ }
+ return x;
+}
+
+/* This function is used to push the mangled decls for nested types into
+ the appropriate scope. Previously pushdecl_top_level was used, but that
+ is incorrect for members of local classes. */
+tree
+pushdecl_nonclass_level (x)
+ tree x;
+{
+ struct binding_level *b = current_binding_level;
+
+#if 0
+ /* Get out of class scope -- this isn't necessary, because class scope
+ doesn't make it into current_binding_level. */
+ while (b->parm_flag == 2)
+ b = b->level_chain;
+#else
+ my_friendly_assert (b->parm_flag != 2, 180);
+#endif
+
+ /* Get out of template binding levels */
+ while (b->pseudo_global)
+ b = b->level_chain;
+
+ pushdecl_with_scope (x, b);
+}
+
+/* Make the declaration(s) of X appear in CLASS scope
+ under the name NAME. */
+void
+push_class_level_binding (name, x)
+ tree name;
+ tree x;
+{
+ maybe_push_cache_obstack ();
+ class_binding_level->class_shadowed
+ = tree_cons (name, IDENTIFIER_CLASS_VALUE (name),
+ class_binding_level->class_shadowed);
+ pop_obstacks ();
+ IDENTIFIER_CLASS_VALUE (name) = x;
+ obstack_ptr_grow (&decl_obstack, x);
+}
+
+/* Tell caller how to interpret a TREE_LIST which contains
+ chains of FUNCTION_DECLS. */
+int
+overloaded_globals_p (list)
+ tree list;
+{
+ my_friendly_assert (TREE_CODE (list) == TREE_LIST, 142);
+
+ /* Don't commit caller to seeing them as globals. */
+ if (TREE_NONLOCAL_FLAG (list))
+ return -1;
+ /* Do commit caller to seeing them as globals. */
+ if (TREE_CODE (TREE_PURPOSE (list)) == IDENTIFIER_NODE)
+ return 1;
+ /* Do commit caller to not seeing them as globals. */
+ return 0;
+}
+
+/* DECL is a FUNCTION_DECL which may have other definitions already in
+ place. We get around this by making the value of the identifier point
+ to a list of all the things that want to be referenced by that name. It
+ is then up to the users of that name to decide what to do with that
+ list.
+
+ DECL may also be a TEMPLATE_DECL, with a FUNCTION_DECL in its DECL_RESULT
+ slot. It is dealt with the same way.
+
+ The value returned may be a previous declaration if we guessed wrong
+ about what language DECL should belong to (C or C++). Otherwise,
+ it's always DECL (and never something that's not a _DECL). */
+tree
+push_overloaded_decl (decl, forgettable)
+ tree decl;
+ int forgettable;
+{
+ tree orig_name = DECL_NAME (decl);
+ tree old;
+ int doing_global = (global_bindings_p () || ! forgettable
+ || flag_traditional || pseudo_global_level_p ());
+
+ if (doing_global)
+ {
+ old = IDENTIFIER_GLOBAL_VALUE (orig_name);
+ if (old && TREE_CODE (old) == FUNCTION_DECL
+ && DECL_ARTIFICIAL (old)
+ && (DECL_BUILT_IN (old) || DECL_BUILT_IN_NONANSI (old)))
+ {
+ if (duplicate_decls (decl, old))
+ return old;
+ old = NULL_TREE;
+ }
+ }
+ else
+ {
+ old = IDENTIFIER_LOCAL_VALUE (orig_name);
+
+ if (! purpose_member (orig_name, current_binding_level->shadowed))
+ {
+ current_binding_level->shadowed
+ = tree_cons (orig_name, old, current_binding_level->shadowed);
+ old = NULL_TREE;
+ }
+ }
+
+ if (old)
+ {
+#if 0
+ /* We cache the value of builtin functions as ADDR_EXPRs
+ in the name space. Convert it to some kind of _DECL after
+ remembering what to forget. */
+ if (TREE_CODE (old) == ADDR_EXPR)
+ old = TREE_OPERAND (old, 0);
+ else
+#endif
+ if (TREE_CODE (old) == VAR_DECL)
+ {
+ cp_error_at ("previous non-function declaration `%#D'", old);
+ cp_error ("conflicts with function declaration `%#D'", decl);
+ return error_mark_node;
+ }
+ else if (TREE_CODE (old) == TYPE_DECL)
+ {
+ tree t = TREE_TYPE (old);
+ if (IS_AGGR_TYPE (t) && warn_shadow)
+ cp_warning ("`%#D' hides constructor for `%#T'", decl, t);
+ old = NULL_TREE;
+ }
+ else if (is_overloaded_fn (old))
+ {
+ tree tmp;
+
+ for (tmp = get_first_fn (old); tmp; tmp = DECL_CHAIN (tmp))
+ if (decl == tmp || duplicate_decls (decl, tmp))
+ return tmp;
+ }
+ }
+
+ if (old || TREE_CODE (decl) == TEMPLATE_DECL)
+ {
+ if (old && is_overloaded_fn (old))
+ DECL_CHAIN (decl) = get_first_fn (old);
+ else
+ DECL_CHAIN (decl) = NULL_TREE;
+ old = tree_cons (orig_name, decl, NULL_TREE);
+ TREE_TYPE (old) = unknown_type_node;
+ }
+ else
+ /* orig_name is not ambiguous. */
+ old = decl;
+
+ if (doing_global)
+ IDENTIFIER_GLOBAL_VALUE (orig_name) = old;
+ else
+ IDENTIFIER_LOCAL_VALUE (orig_name) = old;
+
+ return decl;
+}
+
+/* Generate an implicit declaration for identifier FUNCTIONID
+ as a function of type int (). Print a warning if appropriate. */
+
+tree
+implicitly_declare (functionid)
+ tree functionid;
+{
+ register tree decl;
+ int temp = allocation_temporary_p ();
+
+ push_obstacks_nochange ();
+
+ /* Save the decl permanently so we can warn if definition follows.
+ In ANSI C, warn_implicit is usually false, so the saves little space.
+ But in C++, it's usually true, hence the extra code. */
+ if (temp && (flag_traditional || !warn_implicit
+ || current_binding_level == global_binding_level))
+ end_temporary_allocation ();
+
+ /* We used to reuse an old implicit decl here,
+ but this loses with inline functions because it can clobber
+ the saved decl chains. */
+ decl = build_lang_decl (FUNCTION_DECL, functionid, default_function_type);
+
+ DECL_EXTERNAL (decl) = 1;
+ TREE_PUBLIC (decl) = 1;
+
+ /* ANSI standard says implicit declarations are in the innermost block.
+ So we record the decl in the standard fashion.
+ If flag_traditional is set, pushdecl does it top-level. */
+ pushdecl (decl);
+ rest_of_decl_compilation (decl, NULL_PTR, 0, 0);
+
+ if (warn_implicit
+ /* Only one warning per identifier. */
+ && IDENTIFIER_IMPLICIT_DECL (functionid) == NULL_TREE)
+ {
+ cp_pedwarn ("implicit declaration of function `%#D'", decl);
+ }
+
+ SET_IDENTIFIER_IMPLICIT_DECL (functionid, decl);
+
+ pop_obstacks ();
+
+ return decl;
+}
+
+/* Return zero if the declaration NEWDECL is valid
+ when the declaration OLDDECL (assumed to be for the same name)
+ has already been seen.
+ Otherwise return an error message format string with a %s
+ where the identifier should go. */
+
+static char *
+redeclaration_error_message (newdecl, olddecl)
+ tree newdecl, olddecl;
+{
+ if (TREE_CODE (newdecl) == TYPE_DECL)
+ {
+ /* Because C++ can put things into name space for free,
+ constructs like "typedef struct foo { ... } foo"
+ would look like an erroneous redeclaration. */
+ if (comptypes (TREE_TYPE (newdecl), TREE_TYPE (olddecl), 0))
+ return 0;
+ else
+ return "redefinition of `%#D'";
+ }
+ else if (TREE_CODE (newdecl) == FUNCTION_DECL)
+ {
+ /* If this is a pure function, its olddecl will actually be
+ the original initialization to `0' (which we force to call
+ abort()). Don't complain about redefinition in this case. */
+ if (DECL_LANG_SPECIFIC (olddecl) && DECL_ABSTRACT_VIRTUAL_P (olddecl))
+ return 0;
+
+ /* Declarations of functions can insist on internal linkage
+ but they can't be inconsistent with internal linkage,
+ so there can be no error on that account.
+ However defining the same name twice is no good. */
+ if (DECL_INITIAL (olddecl) != NULL_TREE
+ && DECL_INITIAL (newdecl) != NULL_TREE
+ /* However, defining once as extern inline and a second
+ time in another way is ok. */
+ && !(DECL_INLINE (olddecl) && DECL_EXTERNAL (olddecl)
+ && !(DECL_INLINE (newdecl) && DECL_EXTERNAL (newdecl))))
+ {
+ if (DECL_NAME (olddecl) == NULL_TREE)
+ return "`%#D' not declared in class";
+ else
+ return "redefinition of `%#D'";
+ }
+
+ {
+ tree t1 = TYPE_ARG_TYPES (TREE_TYPE (olddecl));
+ tree t2 = TYPE_ARG_TYPES (TREE_TYPE (newdecl));
+
+ if (TREE_CODE (TREE_TYPE (newdecl)) == METHOD_TYPE)
+ t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2);
+
+ for (; t1; t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2))
+ if (TREE_PURPOSE (t1) && TREE_PURPOSE (t2))
+ return "duplicate default arguments given for `%#D'";
+ }
+ return 0;
+ }
+ else if (TREE_CODE (newdecl) == TEMPLATE_DECL)
+ {
+ if (DECL_INITIAL (olddecl) && DECL_INITIAL (newdecl))
+ return "redefinition of `%#D'";
+ return 0;
+ }
+ else if (current_binding_level == global_binding_level)
+ {
+ /* Objects declared at top level: */
+ /* If at least one is a reference, it's ok. */
+ if (DECL_EXTERNAL (newdecl) || DECL_EXTERNAL (olddecl))
+ return 0;
+ /* Now we have two tentative defs, or one tentative and one real def. */
+ /* Insist that the linkage match. */
+ if (TREE_PUBLIC (olddecl) != TREE_PUBLIC (newdecl))
+ return "conflicting declarations of `%#D'";
+ /* Reject two definitions. */
+ return "redefinition of `%#D'";
+ }
+ else
+ {
+ /* Objects declared with block scope: */
+ /* Reject two definitions, and reject a definition
+ together with an external reference. */
+ if (!(DECL_EXTERNAL (newdecl) && DECL_EXTERNAL (olddecl)))
+ return "redeclaration of `%#D'";
+ return 0;
+ }
+}
+
+/* Get the LABEL_DECL corresponding to identifier ID as a label.
+ Create one if none exists so far for the current function.
+ This function is called for both label definitions and label references. */
+
+tree
+lookup_label (id)
+ tree id;
+{
+ register tree decl = IDENTIFIER_LABEL_VALUE (id);
+
+ if (current_function_decl == NULL_TREE)
+ {
+ error ("label `%s' referenced outside of any function",
+ IDENTIFIER_POINTER (id));
+ return NULL_TREE;
+ }
+
+ if ((decl == NULL_TREE
+ || DECL_SOURCE_LINE (decl) == 0)
+ && (named_label_uses == NULL_TREE
+ || TREE_PURPOSE (named_label_uses) != current_binding_level->names
+ || TREE_VALUE (named_label_uses) != decl))
+ {
+ named_label_uses
+ = tree_cons (current_binding_level->names, decl, named_label_uses);
+ TREE_TYPE (named_label_uses) = (tree)current_binding_level;
+ }
+
+ /* Use a label already defined or ref'd with this name. */
+ if (decl != NULL_TREE)
+ {
+ /* But not if it is inherited and wasn't declared to be inheritable. */
+ if (DECL_CONTEXT (decl) != current_function_decl
+ && ! C_DECLARED_LABEL_FLAG (decl))
+ return shadow_label (id);
+ return decl;
+ }
+
+ decl = build_decl (LABEL_DECL, id, void_type_node);
+
+ /* A label not explicitly declared must be local to where it's ref'd. */
+ DECL_CONTEXT (decl) = current_function_decl;
+
+ DECL_MODE (decl) = VOIDmode;
+
+ /* Say where one reference is to the label,
+ for the sake of the error if it is not defined. */
+ DECL_SOURCE_LINE (decl) = lineno;
+ DECL_SOURCE_FILE (decl) = input_filename;
+
+ SET_IDENTIFIER_LABEL_VALUE (id, decl);
+
+ named_labels = tree_cons (NULL_TREE, decl, named_labels);
+ TREE_VALUE (named_label_uses) = decl;
+
+ return decl;
+}
+
+/* Make a label named NAME in the current function,
+ shadowing silently any that may be inherited from containing functions
+ or containing scopes.
+
+ Note that valid use, if the label being shadowed
+ comes from another scope in the same function,
+ requires calling declare_nonlocal_label right away. */
+
+tree
+shadow_label (name)
+ tree name;
+{
+ register tree decl = IDENTIFIER_LABEL_VALUE (name);
+
+ if (decl != NULL_TREE)
+ {
+ shadowed_labels = tree_cons (NULL_TREE, decl, shadowed_labels);
+ SET_IDENTIFIER_LABEL_VALUE (name, NULL_TREE);
+ SET_IDENTIFIER_LABEL_VALUE (decl, NULL_TREE);
+ }
+
+ return lookup_label (name);
+}
+
+/* Define a label, specifying the location in the source file.
+ Return the LABEL_DECL node for the label, if the definition is valid.
+ Otherwise return 0. */
+
+tree
+define_label (filename, line, name)
+ char *filename;
+ int line;
+ tree name;
+{
+ tree decl = lookup_label (name);
+
+ /* After labels, make any new cleanups go into their
+ own new (temporary) binding contour. */
+ current_binding_level->more_cleanups_ok = 0;
+
+ /* If label with this name is known from an outer context, shadow it. */
+ if (decl != NULL_TREE && DECL_CONTEXT (decl) != current_function_decl)
+ {
+ shadowed_labels = tree_cons (NULL_TREE, decl, shadowed_labels);
+ SET_IDENTIFIER_LABEL_VALUE (name, NULL_TREE);
+ decl = lookup_label (name);
+ }
+
+ if (DECL_INITIAL (decl) != NULL_TREE)
+ {
+ cp_error ("duplicate label `%D'", decl);
+ return 0;
+ }
+ else
+ {
+ tree uses, prev;
+
+ /* Mark label as having been defined. */
+ DECL_INITIAL (decl) = error_mark_node;
+ /* Say where in the source. */
+ DECL_SOURCE_FILE (decl) = filename;
+ DECL_SOURCE_LINE (decl) = line;
+
+ for (prev = NULL_TREE, uses = named_label_uses;
+ uses;
+ prev = uses, uses = TREE_CHAIN (uses))
+ if (TREE_VALUE (uses) == decl)
+ {
+ struct binding_level *b = current_binding_level;
+ while (b)
+ {
+ tree new_decls = b->names;
+ tree old_decls = ((tree)b == TREE_TYPE (uses)
+ ? TREE_PURPOSE (uses) : NULL_TREE);
+ while (new_decls != old_decls)
+ {
+ if (TREE_CODE (new_decls) == VAR_DECL
+ /* Don't complain about crossing initialization
+ of internal entities. They can't be accessed,
+ and they should be cleaned up
+ by the time we get to the label. */
+ && ! DECL_ARTIFICIAL (new_decls)
+ && ((DECL_INITIAL (new_decls) != NULL_TREE
+ && DECL_INITIAL (new_decls) != error_mark_node)
+ || TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (new_decls))))
+ {
+ if (IDENTIFIER_ERROR_LOCUS (decl) == NULL_TREE)
+ cp_error ("invalid jump to label `%D'", decl);
+ SET_IDENTIFIER_ERROR_LOCUS (decl, current_function_decl);
+ cp_error ("crosses initialization of `%D'", new_decls);
+ }
+ new_decls = TREE_CHAIN (new_decls);
+ }
+ if ((tree)b == TREE_TYPE (uses))
+ break;
+ b = b->level_chain;
+ }
+
+ if (prev)
+ TREE_CHAIN (prev) = TREE_CHAIN (uses);
+ else
+ named_label_uses = TREE_CHAIN (uses);
+ }
+ current_function_return_value = NULL_TREE;
+ return decl;
+ }
+}
+
+/* Same, but for CASE labels. If DECL is NULL_TREE, it's the default. */
+/* XXX Note decl is never actually used. (bpk) */
+void
+define_case_label (decl)
+ tree decl;
+{
+ tree cleanup = last_cleanup_this_contour ();
+ if (cleanup)
+ {
+ static int explained = 0;
+ cp_error_at ("destructor needed for `%#D'", TREE_PURPOSE (cleanup));
+ error ("where case label appears here");
+ if (!explained)
+ {
+ error ("(enclose actions of previous case statements requiring");
+ error ("destructors in their own binding contours.)");
+ explained = 1;
+ }
+ }
+
+ /* After labels, make any new cleanups go into their
+ own new (temporary) binding contour. */
+
+ current_binding_level->more_cleanups_ok = 0;
+ current_function_return_value = NULL_TREE;
+}
+
+/* Return the list of declarations of the current level.
+ Note that this list is in reverse order unless/until
+ you nreverse it; and when you do nreverse it, you must
+ store the result back using `storedecls' or you will lose. */
+
+tree
+getdecls ()
+{
+ return current_binding_level->names;
+}
+
+/* Return the list of type-tags (for structs, etc) of the current level. */
+
+tree
+gettags ()
+{
+ return current_binding_level->tags;
+}
+
+/* Store the list of declarations of the current level.
+ This is done for the parameter declarations of a function being defined,
+ after they are modified in the light of any missing parameters. */
+
+static void
+storedecls (decls)
+ tree decls;
+{
+ current_binding_level->names = decls;
+}
+
+/* Similarly, store the list of tags of the current level. */
+
+static void
+storetags (tags)
+ tree tags;
+{
+ current_binding_level->tags = tags;
+}
+
+/* Given NAME, an IDENTIFIER_NODE,
+ return the structure (or union or enum) definition for that name.
+ Searches binding levels from BINDING_LEVEL up to the global level.
+ If THISLEVEL_ONLY is nonzero, searches only the specified context
+ (but skips any tag-transparent contexts to find one that is
+ meaningful for tags).
+ FORM says which kind of type the caller wants;
+ it is RECORD_TYPE or UNION_TYPE or ENUMERAL_TYPE.
+ If the wrong kind of type is found, and it's not a template, an error is
+ reported. */
+
+static tree
+lookup_tag (form, name, binding_level, thislevel_only)
+ enum tree_code form;
+ struct binding_level *binding_level;
+ tree name;
+ int thislevel_only;
+{
+ register struct binding_level *level;
+
+ for (level = binding_level; level; level = level->level_chain)
+ {
+ register tree tail;
+ if (ANON_AGGRNAME_P (name))
+ for (tail = level->tags; tail; tail = TREE_CHAIN (tail))
+ {
+ /* There's no need for error checking here, because
+ anon names are unique throughout the compilation. */
+ if (TYPE_IDENTIFIER (TREE_VALUE (tail)) == name)
+ return TREE_VALUE (tail);
+ }
+ else
+ for (tail = level->tags; tail; tail = TREE_CHAIN (tail))
+ {
+ if (TREE_PURPOSE (tail) == name)
+ {
+ enum tree_code code = TREE_CODE (TREE_VALUE (tail));
+ /* Should tighten this up; it'll probably permit
+ UNION_TYPE and a struct template, for example. */
+ if (code != form
+ && !(form != ENUMERAL_TYPE
+ && (code == TEMPLATE_DECL
+ || code == UNINSTANTIATED_P_TYPE)))
+
+ {
+ /* Definition isn't the kind we were looking for. */
+ cp_error ("`%#D' redeclared as %C", TREE_VALUE (tail),
+ form);
+ }
+ return TREE_VALUE (tail);
+ }
+ }
+ if (thislevel_only && ! level->tag_transparent)
+ return NULL_TREE;
+ if (current_class_type && level->level_chain == global_binding_level)
+ {
+ /* Try looking in this class's tags before heading into
+ global binding level. */
+ tree context = current_class_type;
+ while (context)
+ {
+ switch (TREE_CODE_CLASS (TREE_CODE (context)))
+ {
+ tree these_tags;
+ case 't':
+ these_tags = CLASSTYPE_TAGS (context);
+ if (ANON_AGGRNAME_P (name))
+ while (these_tags)
+ {
+ if (TYPE_IDENTIFIER (TREE_VALUE (these_tags))
+ == name)
+ return TREE_VALUE (tail);
+ these_tags = TREE_CHAIN (these_tags);
+ }
+ else
+ while (these_tags)
+ {
+ if (TREE_PURPOSE (these_tags) == name)
+ {
+ if (TREE_CODE (TREE_VALUE (these_tags)) != form)
+ {
+ cp_error ("`%#D' redeclared as %C in class scope",
+ TREE_VALUE (tail), form);
+ }
+ return TREE_VALUE (tail);
+ }
+ these_tags = TREE_CHAIN (these_tags);
+ }
+ /* If this type is not yet complete, then don't
+ look at its context. */
+ if (TYPE_SIZE (context) == NULL_TREE)
+ goto no_context;
+ /* Go to next enclosing type, if any. */
+ context = DECL_CONTEXT (TYPE_NAME (context));
+ break;
+ case 'd':
+ context = DECL_CONTEXT (context);
+ break;
+ default:
+ my_friendly_abort (10);
+ }
+ continue;
+ no_context:
+ break;
+ }
+ }
+ }
+ return NULL_TREE;
+}
+
+void
+set_current_level_tags_transparency (tags_transparent)
+ int tags_transparent;
+{
+ current_binding_level->tag_transparent = tags_transparent;
+}
+
+/* Given a type, find the tag that was defined for it and return the tag name.
+ Otherwise return 0. However, the value can never be 0
+ in the cases in which this is used.
+
+ C++: If NAME is non-zero, this is the new name to install. This is
+ done when replacing anonymous tags with real tag names. */
+
+static tree
+lookup_tag_reverse (type, name)
+ tree type;
+ tree name;
+{
+ register struct binding_level *level;
+
+ for (level = current_binding_level; level; level = level->level_chain)
+ {
+ register tree tail;
+ for (tail = level->tags; tail; tail = TREE_CHAIN (tail))
+ {
+ if (TREE_VALUE (tail) == type)
+ {
+ if (name)
+ TREE_PURPOSE (tail) = name;
+ return TREE_PURPOSE (tail);
+ }
+ }
+ }
+ return NULL_TREE;
+}
+
+/* Given type TYPE which was not declared in C++ language context,
+ attempt to find a name by which it is referred. */
+tree
+typedecl_for_tag (tag)
+ tree tag;
+{
+ struct binding_level *b = current_binding_level;
+
+ if (TREE_CODE (TYPE_NAME (tag)) == TYPE_DECL)
+ return TYPE_NAME (tag);
+
+ while (b)
+ {
+ tree decls = b->names;
+ while (decls)
+ {
+ if (TREE_CODE (decls) == TYPE_DECL && TREE_TYPE (decls) == tag)
+ break;
+ decls = TREE_CHAIN (decls);
+ }
+ if (decls)
+ return decls;
+ b = b->level_chain;
+ }
+ return NULL_TREE;
+}
+
+/* Lookup TYPE in CONTEXT (a chain of nested types or a FUNCTION_DECL).
+ Return the type value, or NULL_TREE if not found. */
+static tree
+lookup_nested_type (type, context)
+ tree type;
+ tree context;
+{
+ if (context == NULL_TREE)
+ return NULL_TREE;
+ while (context)
+ {
+ switch (TREE_CODE (context))
+ {
+ case TYPE_DECL:
+ {
+ tree ctype = TREE_TYPE (context);
+ tree match = value_member (type, CLASSTYPE_TAGS (ctype));
+ if (match)
+ return TREE_VALUE (match);
+ context = DECL_CONTEXT (context);
+
+ /* When we have a nested class whose member functions have
+ local types (e.g., a set of enums), we'll arrive here
+ with the DECL_CONTEXT as the actual RECORD_TYPE node for
+ the enclosing class. Instead, we want to make sure we
+ come back in here with the TYPE_DECL, not the RECORD_TYPE. */
+ if (context && TREE_CODE (context) == RECORD_TYPE)
+ context = TREE_CHAIN (context);
+ }
+ break;
+ case FUNCTION_DECL:
+ return TYPE_IDENTIFIER (type) ?
+ lookup_name (TYPE_IDENTIFIER (type), 1) : NULL_TREE;
+ break;
+ default:
+ my_friendly_abort (12);
+ }
+ }
+ return NULL_TREE;
+}
+
+/* Look up NAME in the current binding level and its superiors in the
+ namespace of variables, functions and typedefs. Return a ..._DECL
+ node of some kind representing its definition if there is only one
+ such declaration, or return a TREE_LIST with all the overloaded
+ definitions if there are many, or return 0 if it is undefined.
+
+ If PREFER_TYPE is > 0, we prefer TYPE_DECLs.
+ If PREFER_TYPE is -2, we're being called from yylex(). (UGLY)
+ Otherwise we prefer non-TYPE_DECLs. */
+
+tree
+lookup_name_real (name, prefer_type, nonclass)
+ tree name;
+ int prefer_type, nonclass;
+{
+ register tree val;
+ int yylex = 0;
+
+ if (prefer_type == -2)
+ {
+ extern int looking_for_typename;
+
+ yylex = 1;
+ prefer_type = looking_for_typename;
+
+ if (got_scope != NULL_TREE)
+ {
+ if (got_scope == void_type_node)
+ val = IDENTIFIER_GLOBAL_VALUE (name);
+ else if (TREE_CODE (got_scope) == TEMPLATE_TYPE_PARM
+ /* TFIXME -- don't do this for UPTs in new model. */
+ || TREE_CODE (got_scope) == UNINSTANTIATED_P_TYPE)
+ {
+ if (prefer_type > 0)
+ val = create_nested_upt (got_scope, name);
+ else
+ val = NULL_TREE;
+ }
+ else if (! IS_AGGR_TYPE (got_scope))
+ /* Someone else will give an error about this if needed. */
+ val = NULL_TREE;
+ else if (TYPE_BEING_DEFINED (got_scope))
+ {
+ val = IDENTIFIER_CLASS_VALUE (name);
+ if (val && DECL_CONTEXT (val) != got_scope)
+ {
+ struct binding_level *b = class_binding_level;
+ for (val = NULL_TREE; b; b = b->level_chain)
+ {
+ tree t = purpose_member (name, b->class_shadowed);
+ if (t && TREE_VALUE (t)
+ && DECL_CONTEXT (TREE_VALUE (t)) == got_scope)
+ {
+ val = TREE_VALUE (t);
+ break;
+ }
+ }
+ }
+ if (val == NULL_TREE
+ && CLASSTYPE_LOCAL_TYPEDECLS (got_scope))
+ val = lookup_field (got_scope, name, 0, 1);
+ }
+ else if (got_scope == current_class_type)
+ val = IDENTIFIER_CLASS_VALUE (name);
+ else
+ val = lookup_field (got_scope, name, 0, 0);
+
+ goto done;
+ }
+ }
+
+ if (current_binding_level != global_binding_level
+ && IDENTIFIER_LOCAL_VALUE (name))
+ val = IDENTIFIER_LOCAL_VALUE (name);
+ /* In C++ class fields are between local and global scope,
+ just before the global scope. */
+ else if (current_class_type && ! nonclass)
+ {
+ val = IDENTIFIER_CLASS_VALUE (name);
+ if (val == NULL_TREE
+ && TYPE_BEING_DEFINED (current_class_type)
+ && CLASSTYPE_LOCAL_TYPEDECLS (current_class_type))
+ /* Try to find values from base classes if we are presently
+ defining a type. We are presently only interested in
+ TYPE_DECLs. */
+ val = lookup_field (current_class_type, name, 0, 1);
+
+ /* yylex() calls this with -2, since we should never start digging for
+ the nested name at the point where we haven't even, for example,
+ created the COMPONENT_REF or anything like that. */
+ if (val == NULL_TREE)
+ val = lookup_nested_field (name, ! yylex);
+
+ if (val == NULL_TREE)
+ val = IDENTIFIER_GLOBAL_VALUE (name);
+ }
+ else
+ val = IDENTIFIER_GLOBAL_VALUE (name);
+
+ done:
+ if (val)
+ {
+ if ((TREE_CODE (val) == TEMPLATE_DECL && looking_for_template)
+ || TREE_CODE (val) == TYPE_DECL || prefer_type <= 0)
+ return val;
+
+ if (IDENTIFIER_HAS_TYPE_VALUE (name))
+ return TYPE_NAME (IDENTIFIER_TYPE_VALUE (name));
+
+ if (TREE_TYPE (val) == error_mark_node)
+ return error_mark_node;
+ }
+
+ return val;
+}
+
+tree
+lookup_name_nonclass (name)
+ tree name;
+{
+ return lookup_name_real (name, 0, 1);
+}
+
+tree
+lookup_name (name, prefer_type)
+ tree name;
+ int prefer_type;
+{
+ return lookup_name_real (name, prefer_type, 0);
+}
+
+/* Similar to `lookup_name' but look only at current binding level. */
+
+tree
+lookup_name_current_level (name)
+ tree name;
+{
+ register tree t = NULL_TREE;
+
+ if (current_binding_level == global_binding_level)
+ {
+ t = IDENTIFIER_GLOBAL_VALUE (name);
+
+ /* extern "C" function() */
+ if (t != NULL_TREE && TREE_CODE (t) == TREE_LIST)
+ t = TREE_VALUE (t);
+ }
+ else if (IDENTIFIER_LOCAL_VALUE (name) != NULL_TREE)
+ {
+ struct binding_level *b = current_binding_level;
+ while (1)
+ {
+ for (t = b->names; t; t = TREE_CHAIN (t))
+ if (DECL_NAME (t) == name)
+ goto out;
+ if (b->keep == 2)
+ b = b->level_chain;
+ else
+ break;
+ }
+ out:
+ ;
+ }
+
+ return t;
+}
+
+/* Arrange for the user to get a source line number, even when the
+ compiler is going down in flames, so that she at least has a
+ chance of working around problems in the compiler. We used to
+ call error(), but that let the segmentation fault continue
+ through; now, it's much more passive by asking them to send the
+ maintainers mail about the problem. */
+
+static void
+signal_catch (sig)
+ int sig;
+{
+ signal (SIGSEGV, SIG_DFL);
+#ifdef SIGIOT
+ signal (SIGIOT, SIG_DFL);
+#endif
+#ifdef SIGILL
+ signal (SIGILL, SIG_DFL);
+#endif
+#ifdef SIGABRT
+ signal (SIGABRT, SIG_DFL);
+#endif
+#ifdef SIGBUS
+ signal (SIGBUS, SIG_DFL);
+#endif
+ my_friendly_abort (0);
+}
+
+/* Array for holding types considered "built-in". These types
+ are output in the module in which `main' is defined. */
+static tree *builtin_type_tdescs_arr;
+static int builtin_type_tdescs_len, builtin_type_tdescs_max;
+
+/* Push the declarations of builtin types into the namespace.
+ RID_INDEX, if < RID_MAX is the index of the builtin type
+ in the array RID_POINTERS. NAME is the name used when looking
+ up the builtin type. TYPE is the _TYPE node for the builtin type. */
+
+static void
+record_builtin_type (rid_index, name, type)
+ enum rid rid_index;
+ char *name;
+ tree type;
+{
+ tree rname = NULL_TREE, tname = NULL_TREE;
+ tree tdecl;
+
+ if ((int) rid_index < (int) RID_MAX)
+ rname = ridpointers[(int) rid_index];
+ if (name)
+ tname = get_identifier (name);
+
+ TYPE_BUILT_IN (type) = 1;
+
+ if (tname)
+ {
+#if 0 /* not yet, should get fixed properly later */
+ tdecl = pushdecl (make_type_decl (tname, type));
+#else
+ tdecl = pushdecl (build_decl (TYPE_DECL, tname, type));
+#endif
+ set_identifier_type_value (tname, NULL_TREE);
+ if ((int) rid_index < (int) RID_MAX)
+ IDENTIFIER_GLOBAL_VALUE (tname) = tdecl;
+ }
+ if (rname != NULL_TREE)
+ {
+ if (tname != NULL_TREE)
+ {
+ set_identifier_type_value (rname, NULL_TREE);
+ IDENTIFIER_GLOBAL_VALUE (rname) = tdecl;
+ }
+ else
+ {
+#if 0 /* not yet, should get fixed properly later */
+ tdecl = pushdecl (make_type_decl (rname, type));
+#else
+ tdecl = pushdecl (build_decl (TYPE_DECL, rname, type));
+#endif
+ set_identifier_type_value (rname, NULL_TREE);
+ }
+ }
+
+ if (flag_dossier)
+ {
+ if (builtin_type_tdescs_len+5 >= builtin_type_tdescs_max)
+ {
+ builtin_type_tdescs_max *= 2;
+ builtin_type_tdescs_arr
+ = (tree *)xrealloc (builtin_type_tdescs_arr,
+ builtin_type_tdescs_max * sizeof (tree));
+ }
+ builtin_type_tdescs_arr[builtin_type_tdescs_len++] = type;
+ if (TREE_CODE (type) != POINTER_TYPE)
+ {
+ builtin_type_tdescs_arr[builtin_type_tdescs_len++]
+ = build_pointer_type (type);
+ builtin_type_tdescs_arr[builtin_type_tdescs_len++]
+ = build_pointer_type (build_type_variant (type, 1, 0));
+ }
+ if (TREE_CODE (type) != VOID_TYPE)
+ {
+ builtin_type_tdescs_arr[builtin_type_tdescs_len++]
+ = build_reference_type (type);
+ builtin_type_tdescs_arr[builtin_type_tdescs_len++]
+ = build_reference_type (build_type_variant (type, 1, 0));
+ }
+ }
+}
+
+static void
+output_builtin_tdesc_entries ()
+{
+ extern struct obstack permanent_obstack;
+
+ /* If there's more than one main in this file, don't crash. */
+ if (builtin_type_tdescs_arr == 0)
+ return;
+
+ push_obstacks (&permanent_obstack, &permanent_obstack);
+ while (builtin_type_tdescs_len > 0)
+ {
+ tree type = builtin_type_tdescs_arr[--builtin_type_tdescs_len];
+ tree tdesc = build_t_desc (type, 0);
+ TREE_ASM_WRITTEN (tdesc) = 0;
+ build_t_desc (type, 2);
+ }
+ free (builtin_type_tdescs_arr);
+ builtin_type_tdescs_arr = 0;
+ pop_obstacks ();
+}
+
+/* Push overloaded decl, in global scope, with one argument so it
+ can be used as a callback from define_function. */
+static void
+push_overloaded_decl_1 (x)
+ tree x;
+{
+ push_overloaded_decl (x, 0);
+}
+
+/* Create the predefined scalar types of C,
+ and some nodes representing standard constants (0, 1, (void *)0).
+ Initialize the global binding level.
+ Make definitions for built-in primitive functions. */
+
+void
+init_decl_processing ()
+{
+ tree decl;
+ register tree endlink, int_endlink, double_endlink, ptr_endlink;
+ tree fields[20];
+ /* Either char* or void*. */
+ tree traditional_ptr_type_node;
+ /* Data type of memcpy. */
+ tree memcpy_ftype;
+#if 0 /* Not yet. */
+ /* Data type of strncpy. */
+ tree strncpy_ftype;
+#endif
+ int wchar_type_size;
+ tree temp;
+ tree array_domain_type;
+
+ /* Have to make these distinct before we try using them. */
+ lang_name_cplusplus = get_identifier ("C++");
+ lang_name_c = get_identifier ("C");
+
+ if (flag_ansi || pedantic)
+ strict_prototypes_lang_c = strict_prototypes_lang_cplusplus;
+
+ /* Initially, C. */
+ current_lang_name = lang_name_c;
+
+ current_function_decl = NULL_TREE;
+ named_labels = NULL_TREE;
+ named_label_uses = NULL_TREE;
+ current_binding_level = NULL_BINDING_LEVEL;
+ free_binding_level = NULL_BINDING_LEVEL;
+
+ /* Because most segmentation signals can be traced back into user
+ code, catch them and at least give the user a chance of working
+ around compiler bugs. */
+ signal (SIGSEGV, signal_catch);
+
+ /* We will also catch aborts in the back-end through signal_catch and
+ give the user a chance to see where the error might be, and to defeat
+ aborts in the back-end when there have been errors previously in their
+ code. */
+#ifdef SIGIOT
+ signal (SIGIOT, signal_catch);
+#endif
+#ifdef SIGILL
+ signal (SIGILL, signal_catch);
+#endif
+#ifdef SIGABRT
+ signal (SIGABRT, signal_catch);
+#endif
+#ifdef SIGBUS
+ signal (SIGBUS, signal_catch);
+#endif
+
+ gcc_obstack_init (&decl_obstack);
+ if (flag_dossier)
+ {
+ builtin_type_tdescs_max = 100;
+ builtin_type_tdescs_arr = (tree *)xmalloc (100 * sizeof (tree));
+ }
+
+ /* Must lay these out before anything else gets laid out. */
+ error_mark_node = make_node (ERROR_MARK);
+ TREE_PERMANENT (error_mark_node) = 1;
+ TREE_TYPE (error_mark_node) = error_mark_node;
+ error_mark_list = build_tree_list (error_mark_node, error_mark_node);
+ TREE_TYPE (error_mark_list) = error_mark_node;
+
+ /* Make the binding_level structure for global names. */
+ pushlevel (0);
+ global_binding_level = current_binding_level;
+
+ this_identifier = get_identifier (THIS_NAME);
+ in_charge_identifier = get_identifier (IN_CHARGE_NAME);
+ pfn_identifier = get_identifier (VTABLE_PFN_NAME);
+ index_identifier = get_identifier (VTABLE_INDEX_NAME);
+ delta_identifier = get_identifier (VTABLE_DELTA_NAME);
+ delta2_identifier = get_identifier (VTABLE_DELTA2_NAME);
+ pfn_or_delta2_identifier = get_identifier ("__pfn_or_delta2");
+
+ /* Define `int' and `char' first so that dbx will output them first. */
+
+ integer_type_node = make_signed_type (INT_TYPE_SIZE);
+ record_builtin_type (RID_INT, NULL_PTR, integer_type_node);
+
+ /* Define `char', which is like either `signed char' or `unsigned char'
+ but not the same as either. */
+
+ char_type_node =
+ (flag_signed_char
+ ? make_signed_type (CHAR_TYPE_SIZE)
+ : make_unsigned_type (CHAR_TYPE_SIZE));
+ record_builtin_type (RID_CHAR, "char", char_type_node);
+
+ long_integer_type_node = make_signed_type (LONG_TYPE_SIZE);
+ record_builtin_type (RID_LONG, "long int", long_integer_type_node);
+
+ unsigned_type_node = make_unsigned_type (INT_TYPE_SIZE);
+ record_builtin_type (RID_UNSIGNED, "unsigned int", unsigned_type_node);
+
+ long_unsigned_type_node = make_unsigned_type (LONG_TYPE_SIZE);
+ record_builtin_type (RID_MAX, "long unsigned int", long_unsigned_type_node);
+ record_builtin_type (RID_MAX, "unsigned long", long_unsigned_type_node);
+
+ long_long_integer_type_node = make_signed_type (LONG_LONG_TYPE_SIZE);
+ record_builtin_type (RID_MAX, "long long int", long_long_integer_type_node);
+
+ long_long_unsigned_type_node = make_unsigned_type (LONG_LONG_TYPE_SIZE);
+ record_builtin_type (RID_MAX, "long long unsigned int",
+ long_long_unsigned_type_node);
+ record_builtin_type (RID_MAX, "long long unsigned",
+ long_long_unsigned_type_node);
+
+ /* `unsigned long' is the standard type for sizeof.
+ Traditionally, use a signed type.
+ Note that stddef.h uses `unsigned long',
+ and this must agree, even of long and int are the same size. */
+ sizetype
+ = TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (get_identifier (SIZE_TYPE)));
+ if (flag_traditional && TREE_UNSIGNED (sizetype))
+ sizetype = signed_type (sizetype);
+
+ ptrdiff_type_node
+ = TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (get_identifier (PTRDIFF_TYPE)));
+
+ TREE_TYPE (TYPE_SIZE (integer_type_node)) = sizetype;
+ TREE_TYPE (TYPE_SIZE (char_type_node)) = sizetype;
+ TREE_TYPE (TYPE_SIZE (unsigned_type_node)) = sizetype;
+ TREE_TYPE (TYPE_SIZE (long_unsigned_type_node)) = sizetype;
+ TREE_TYPE (TYPE_SIZE (long_integer_type_node)) = sizetype;
+ TREE_TYPE (TYPE_SIZE (long_long_integer_type_node)) = sizetype;
+ TREE_TYPE (TYPE_SIZE (long_long_unsigned_type_node)) = sizetype;
+
+ short_integer_type_node = make_signed_type (SHORT_TYPE_SIZE);
+ record_builtin_type (RID_SHORT, "short int", short_integer_type_node);
+ short_unsigned_type_node = make_unsigned_type (SHORT_TYPE_SIZE);
+ record_builtin_type (RID_MAX, "short unsigned int", short_unsigned_type_node);
+ record_builtin_type (RID_MAX, "unsigned short", short_unsigned_type_node);
+
+ /* Define both `signed char' and `unsigned char'. */
+ signed_char_type_node = make_signed_type (CHAR_TYPE_SIZE);
+ record_builtin_type (RID_MAX, "signed char", signed_char_type_node);
+ unsigned_char_type_node = make_unsigned_type (CHAR_TYPE_SIZE);
+ record_builtin_type (RID_MAX, "unsigned char", unsigned_char_type_node);
+
+ /* These are types that type_for_size and type_for_mode use. */
+ intQI_type_node = make_signed_type (GET_MODE_BITSIZE (QImode));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, intQI_type_node));
+ intHI_type_node = make_signed_type (GET_MODE_BITSIZE (HImode));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, intHI_type_node));
+ intSI_type_node = make_signed_type (GET_MODE_BITSIZE (SImode));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, intSI_type_node));
+ intDI_type_node = make_signed_type (GET_MODE_BITSIZE (DImode));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, intDI_type_node));
+ unsigned_intQI_type_node = make_unsigned_type (GET_MODE_BITSIZE (QImode));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, unsigned_intQI_type_node));
+ unsigned_intHI_type_node = make_unsigned_type (GET_MODE_BITSIZE (HImode));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, unsigned_intHI_type_node));
+ unsigned_intSI_type_node = make_unsigned_type (GET_MODE_BITSIZE (SImode));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, unsigned_intSI_type_node));
+ unsigned_intDI_type_node = make_unsigned_type (GET_MODE_BITSIZE (DImode));
+ pushdecl (build_decl (TYPE_DECL, NULL_TREE, unsigned_intDI_type_node));
+
+ float_type_node = make_node (REAL_TYPE);
+ TYPE_PRECISION (float_type_node) = FLOAT_TYPE_SIZE;
+ record_builtin_type (RID_FLOAT, NULL_PTR, float_type_node);
+ layout_type (float_type_node);
+
+ double_type_node = make_node (REAL_TYPE);
+ if (flag_short_double)
+ TYPE_PRECISION (double_type_node) = FLOAT_TYPE_SIZE;
+ else
+ TYPE_PRECISION (double_type_node) = DOUBLE_TYPE_SIZE;
+ record_builtin_type (RID_DOUBLE, NULL_PTR, double_type_node);
+ layout_type (double_type_node);
+
+ long_double_type_node = make_node (REAL_TYPE);
+ TYPE_PRECISION (long_double_type_node) = LONG_DOUBLE_TYPE_SIZE;
+ record_builtin_type (RID_MAX, "long double", long_double_type_node);
+ layout_type (long_double_type_node);
+
+ integer_zero_node = build_int_2 (0, 0);
+ TREE_TYPE (integer_zero_node) = integer_type_node;
+ integer_one_node = build_int_2 (1, 0);
+ TREE_TYPE (integer_one_node) = integer_type_node;
+ integer_two_node = build_int_2 (2, 0);
+ TREE_TYPE (integer_two_node) = integer_type_node;
+ integer_three_node = build_int_2 (3, 0);
+ TREE_TYPE (integer_three_node) = integer_type_node;
+
+ bool_type_node = make_unsigned_type (CHAR_TYPE_SIZE);
+ TREE_SET_CODE (bool_type_node, BOOLEAN_TYPE);
+ record_builtin_type (RID_BOOL, "bool", bool_type_node);
+ false_node = build_int_2 (0, 0);
+ TREE_TYPE (false_node) = bool_type_node;
+ true_node = build_int_2 (1, 0);
+ TREE_TYPE (true_node) = bool_type_node;
+
+ /* These are needed by stor-layout.c. */
+ size_zero_node = size_int (0);
+ size_one_node = size_int (1);
+
+ void_type_node = make_node (VOID_TYPE);
+ record_builtin_type (RID_VOID, NULL_PTR, void_type_node);
+ layout_type (void_type_node); /* Uses integer_zero_node. */
+ void_list_node = build_tree_list (NULL_TREE, void_type_node);
+ TREE_PARMLIST (void_list_node) = 1;
+
+ null_pointer_node = build_int_2 (0, 0);
+ TREE_TYPE (null_pointer_node) = build_pointer_type (void_type_node);
+ layout_type (TREE_TYPE (null_pointer_node));
+
+ /* Used for expressions that do nothing, but are not errors. */
+ void_zero_node = build_int_2 (0, 0);
+ TREE_TYPE (void_zero_node) = void_type_node;
+
+ string_type_node = build_pointer_type (char_type_node);
+ const_string_type_node =
+ build_pointer_type (build_type_variant (char_type_node, 1, 0));
+ record_builtin_type (RID_MAX, NULL_PTR, string_type_node);
+
+ /* Make a type to be the domain of a few array types
+ whose domains don't really matter.
+ 200 is small enough that it always fits in size_t
+ and large enough that it can hold most function names for the
+ initializations of __FUNCTION__ and __PRETTY_FUNCTION__. */
+ array_domain_type = build_index_type (build_int_2 (200, 0));
+
+ /* make a type for arrays of characters.
+ With luck nothing will ever really depend on the length of this
+ array type. */
+ char_array_type_node
+ = build_array_type (char_type_node, array_domain_type);
+ /* Likewise for arrays of ints. */
+ int_array_type_node
+ = build_array_type (integer_type_node, array_domain_type);
+
+ /* This is just some anonymous class type. Nobody should ever
+ need to look inside this envelope. */
+ class_star_type_node = build_pointer_type (make_lang_type (RECORD_TYPE));
+
+ default_function_type
+ = build_function_type (integer_type_node, NULL_TREE);
+ build_pointer_type (default_function_type);
+
+ ptr_type_node = build_pointer_type (void_type_node);
+ const_ptr_type_node =
+ build_pointer_type (build_type_variant (void_type_node, 1, 0));
+ record_builtin_type (RID_MAX, NULL_PTR, ptr_type_node);
+ endlink = void_list_node;
+ int_endlink = tree_cons (NULL_TREE, integer_type_node, endlink);
+ double_endlink = tree_cons (NULL_TREE, double_type_node, endlink);
+ ptr_endlink = tree_cons (NULL_TREE, ptr_type_node, endlink);
+
+ double_ftype_double
+ = build_function_type (double_type_node, double_endlink);
+
+ double_ftype_double_double
+ = build_function_type (double_type_node,
+ tree_cons (NULL_TREE, double_type_node,
+ double_endlink));
+
+ int_ftype_int
+ = build_function_type (integer_type_node, int_endlink);
+
+ long_ftype_long
+ = build_function_type (long_integer_type_node,
+ tree_cons (NULL_TREE, long_integer_type_node,
+ endlink));
+
+ void_ftype_ptr_ptr_int
+ = build_function_type (void_type_node,
+ tree_cons (NULL_TREE, ptr_type_node,
+ tree_cons (NULL_TREE, ptr_type_node,
+ int_endlink)));
+
+ int_ftype_cptr_cptr_sizet
+ = build_function_type (integer_type_node,
+ tree_cons (NULL_TREE, const_ptr_type_node,
+ tree_cons (NULL_TREE, const_ptr_type_node,
+ tree_cons (NULL_TREE,
+ sizetype,
+ endlink))));
+
+ void_ftype_ptr_int_int
+ = build_function_type (void_type_node,
+ tree_cons (NULL_TREE, ptr_type_node,
+ tree_cons (NULL_TREE, integer_type_node,
+ int_endlink)));
+
+ string_ftype_ptr_ptr /* strcpy prototype */
+ = build_function_type (string_type_node,
+ tree_cons (NULL_TREE, string_type_node,
+ tree_cons (NULL_TREE,
+ const_string_type_node,
+ endlink)));
+
+#if 0
+ /* Not yet. */
+ strncpy_ftype /* strncpy prototype */
+ = build_function_type (string_type_node,
+ tree_cons (NULL_TREE, string_type_node,
+ tree_cons (NULL_TREE, const_string_type_node,
+ tree_cons (NULL_TREE,
+ sizetype,
+ endlink))));
+#endif
+
+ int_ftype_string_string /* strcmp prototype */
+ = build_function_type (integer_type_node,
+ tree_cons (NULL_TREE, const_string_type_node,
+ tree_cons (NULL_TREE,
+ const_string_type_node,
+ endlink)));
+
+ sizet_ftype_string /* strlen prototype */
+ = build_function_type (sizetype,
+ tree_cons (NULL_TREE, const_string_type_node,
+ endlink));
+
+ traditional_ptr_type_node
+ = (flag_traditional ? string_type_node : ptr_type_node);
+
+ memcpy_ftype /* memcpy prototype */
+ = build_function_type (traditional_ptr_type_node,
+ tree_cons (NULL_TREE, ptr_type_node,
+ tree_cons (NULL_TREE, const_ptr_type_node,
+ tree_cons (NULL_TREE,
+ sizetype,
+ endlink))));
+
+ if (flag_huge_objects)
+ delta_type_node = long_integer_type_node;
+ else
+ delta_type_node = short_integer_type_node;
+
+ builtin_function ("__builtin_constant_p", int_ftype_int,
+ BUILT_IN_CONSTANT_P, NULL_PTR);
+
+ builtin_return_address_fndecl =
+ builtin_function ("__builtin_return_address",
+ build_function_type (ptr_type_node,
+ tree_cons (NULL_TREE,
+ unsigned_type_node,
+ endlink)),
+ BUILT_IN_RETURN_ADDRESS, NULL_PTR);
+
+ builtin_function ("__builtin_frame_address",
+ build_function_type (ptr_type_node,
+ tree_cons (NULL_TREE,
+ unsigned_type_node,
+ endlink)),
+ BUILT_IN_FRAME_ADDRESS, NULL_PTR);
+
+
+ builtin_function ("__builtin_alloca",
+ build_function_type (ptr_type_node,
+ tree_cons (NULL_TREE,
+ sizetype,
+ endlink)),
+ BUILT_IN_ALLOCA, "alloca");
+#if 0
+ builtin_function ("alloca",
+ build_function_type (ptr_type_node,
+ tree_cons (NULL_TREE,
+ sizetype,
+ endlink)),
+ BUILT_IN_ALLOCA, NULL_PTR);
+#endif
+
+ builtin_function ("__builtin_abs", int_ftype_int,
+ BUILT_IN_ABS, NULL_PTR);
+ builtin_function ("__builtin_fabs", double_ftype_double,
+ BUILT_IN_FABS, NULL_PTR);
+ builtin_function ("__builtin_labs", long_ftype_long,
+ BUILT_IN_LABS, NULL_PTR);
+ builtin_function ("__builtin_ffs", int_ftype_int,
+ BUILT_IN_FFS, NULL_PTR);
+ builtin_function ("__builtin_fsqrt", double_ftype_double,
+ BUILT_IN_FSQRT, NULL_PTR);
+ builtin_function ("__builtin_sin", double_ftype_double,
+ BUILT_IN_SIN, "sin");
+ builtin_function ("__builtin_cos", double_ftype_double,
+ BUILT_IN_COS, "cos");
+ builtin_function ("__builtin_saveregs",
+ build_function_type (ptr_type_node, NULL_TREE),
+ BUILT_IN_SAVEREGS, NULL_PTR);
+/* EXPAND_BUILTIN_VARARGS is obsolete. */
+#if 0
+ builtin_function ("__builtin_varargs",
+ build_function_type (ptr_type_node,
+ tree_cons (NULL_TREE,
+ integer_type_node,
+ endlink)),
+ BUILT_IN_VARARGS, NULL_PTR);
+#endif
+ builtin_function ("__builtin_classify_type", default_function_type,
+ BUILT_IN_CLASSIFY_TYPE, NULL_PTR);
+ builtin_function ("__builtin_next_arg",
+ build_function_type (ptr_type_node, NULL_TREE),
+ BUILT_IN_NEXT_ARG, NULL_PTR);
+ builtin_function ("__builtin_args_info",
+ build_function_type (integer_type_node,
+ tree_cons (NULL_TREE,
+ integer_type_node,
+ endlink)),
+ BUILT_IN_ARGS_INFO, NULL_PTR);
+
+ /* Untyped call and return. */
+ builtin_function ("__builtin_apply_args",
+ build_function_type (ptr_type_node, NULL_TREE),
+ BUILT_IN_APPLY_ARGS, NULL_PTR);
+
+ temp = tree_cons (NULL_TREE,
+ build_pointer_type (build_function_type (void_type_node,
+ NULL_TREE)),
+ tree_cons (NULL_TREE,
+ ptr_type_node,
+ tree_cons (NULL_TREE,
+ sizetype,
+ endlink)));
+ builtin_function ("__builtin_apply",
+ build_function_type (ptr_type_node, temp),
+ BUILT_IN_APPLY, NULL_PTR);
+ builtin_function ("__builtin_return",
+ build_function_type (void_type_node,
+ tree_cons (NULL_TREE,
+ ptr_type_node,
+ endlink)),
+ BUILT_IN_RETURN, NULL_PTR);
+
+ /* Currently under experimentation. */
+ builtin_function ("__builtin_memcpy", memcpy_ftype,
+ BUILT_IN_MEMCPY, "memcpy");
+ builtin_function ("__builtin_memcmp", int_ftype_cptr_cptr_sizet,
+ BUILT_IN_MEMCMP, "memcmp");
+ builtin_function ("__builtin_strcmp", int_ftype_string_string,
+ BUILT_IN_STRCMP, "strcmp");
+ builtin_function ("__builtin_strcpy", string_ftype_ptr_ptr,
+ BUILT_IN_STRCPY, "strcpy");
+#if 0
+ /* Not yet. */
+ builtin_function ("__builtin_strncpy", strncpy_ftype,
+ BUILT_IN_STRNCPY, "strncpy");
+#endif
+ builtin_function ("__builtin_strlen", sizet_ftype_string,
+ BUILT_IN_STRLEN, "strlen");
+
+ if (!flag_no_builtin)
+ {
+#if 0 /* These do not work well with libg++. */
+ builtin_function ("abs", int_ftype_int, BUILT_IN_ABS, NULL_PTR);
+ builtin_function ("fabs", double_ftype_double, BUILT_IN_FABS, NULL_PTR);
+ builtin_function ("labs", long_ftype_long, BUILT_IN_LABS, NULL_PTR);
+#endif
+ builtin_function ("memcpy", memcpy_ftype, BUILT_IN_MEMCPY, NULL_PTR);
+ builtin_function ("memcmp", int_ftype_cptr_cptr_sizet, BUILT_IN_MEMCMP,
+ NULL_PTR);
+ builtin_function ("strcmp", int_ftype_string_string, BUILT_IN_STRCMP, NULL_PTR);
+ builtin_function ("strcpy", string_ftype_ptr_ptr, BUILT_IN_STRCPY,
+ NULL_PTR);
+#if 0
+ /* Not yet. */
+ builtin_function ("strncpy", strncpy_ftype, BUILT_IN_STRNCPY, NULL_PTR);
+#endif
+ builtin_function ("strlen", sizet_ftype_string, BUILT_IN_STRLEN, NULL_PTR);
+ builtin_function ("sin", double_ftype_double, BUILT_IN_SIN, NULL_PTR);
+ builtin_function ("cos", double_ftype_double, BUILT_IN_COS, NULL_PTR);
+ }
+
+#if 0
+ /* Support for these has not been written in either expand_builtin
+ or build_function_call. */
+ builtin_function ("__builtin_div", default_ftype, BUILT_IN_DIV, 0);
+ builtin_function ("__builtin_ldiv", default_ftype, BUILT_IN_LDIV, 0);
+ builtin_function ("__builtin_ffloor", double_ftype_double, BUILT_IN_FFLOOR,
+ 0);
+ builtin_function ("__builtin_fceil", double_ftype_double, BUILT_IN_FCEIL, 0);
+ builtin_function ("__builtin_fmod", double_ftype_double_double,
+ BUILT_IN_FMOD, 0);
+ builtin_function ("__builtin_frem", double_ftype_double_double,
+ BUILT_IN_FREM, 0);
+ builtin_function ("__builtin_memset", ptr_ftype_ptr_int_int, BUILT_IN_MEMSET,
+ 0);
+ builtin_function ("__builtin_getexp", double_ftype_double, BUILT_IN_GETEXP,
+ 0);
+ builtin_function ("__builtin_getman", double_ftype_double, BUILT_IN_GETMAN,
+ 0);
+#endif
+
+ /* C++ extensions */
+
+ unknown_type_node = make_node (UNKNOWN_TYPE);
+#if 0 /* not yet, should get fixed properly later */
+ pushdecl (make_type_decl (get_identifier ("unknown type"),
+ unknown_type_node));
+#else
+ decl = pushdecl (build_decl (TYPE_DECL, get_identifier ("unknown type"),
+ unknown_type_node));
+ /* Make sure the "unknown type" typedecl gets ignored for debug info. */
+ DECL_IGNORED_P (decl) = 1;
+ TYPE_DECL_SUPPRESS_DEBUG (decl) = 1;
+#endif
+ TYPE_SIZE (unknown_type_node) = TYPE_SIZE (void_type_node);
+ TYPE_ALIGN (unknown_type_node) = 1;
+ TYPE_MODE (unknown_type_node) = TYPE_MODE (void_type_node);
+ /* Indirecting an UNKNOWN_TYPE node yields an UNKNOWN_TYPE node. */
+ TREE_TYPE (unknown_type_node) = unknown_type_node;
+ /* Looking up TYPE_POINTER_TO and TYPE_REFERENCE_TO yield the same result. */
+ TYPE_POINTER_TO (unknown_type_node) = unknown_type_node;
+ TYPE_REFERENCE_TO (unknown_type_node) = unknown_type_node;
+
+ /* This is for handling opaque types in signatures. */
+ opaque_type_node = copy_node (ptr_type_node);
+ TYPE_MAIN_VARIANT (opaque_type_node) = opaque_type_node;
+ record_builtin_type (RID_MAX, 0, opaque_type_node);
+
+ /* This is special for C++ so functions can be overloaded. */
+ wchar_type_node
+ = TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (get_identifier (WCHAR_TYPE)));
+ wchar_type_size = TYPE_PRECISION (wchar_type_node);
+ signed_wchar_type_node = make_signed_type (wchar_type_size);
+ unsigned_wchar_type_node = make_unsigned_type (wchar_type_size);
+ wchar_type_node
+ = TREE_UNSIGNED (wchar_type_node)
+ ? unsigned_wchar_type_node
+ : signed_wchar_type_node;
+ record_builtin_type (RID_WCHAR, "__wchar_t", wchar_type_node);
+
+ /* This is for wide string constants. */
+ wchar_array_type_node
+ = build_array_type (wchar_type_node, array_domain_type);
+
+ /* This is a hack that should go away when we deliver the
+ real gc code. */
+ if (flag_gc)
+ {
+ builtin_function ("__gc_main", default_function_type, NOT_BUILT_IN, 0);
+ pushdecl (lookup_name (get_identifier ("__gc_main"), 0));
+ }
+
+ if (flag_vtable_thunks)
+ {
+ /* Make sure we get a unique function type, so we can give
+ its pointer type a name. (This wins for gdb.) */
+ tree vfunc_type = make_node (FUNCTION_TYPE);
+ TREE_TYPE (vfunc_type) = integer_type_node;
+ TYPE_ARG_TYPES (vfunc_type) = NULL_TREE;
+ layout_type (vfunc_type);
+
+ vtable_entry_type = build_pointer_type (vfunc_type);
+ }
+ else
+ {
+ vtable_entry_type = make_lang_type (RECORD_TYPE);
+ fields[0] = build_lang_field_decl (FIELD_DECL, delta_identifier,
+ delta_type_node);
+ fields[1] = build_lang_field_decl (FIELD_DECL, index_identifier,
+ delta_type_node);
+ fields[2] = build_lang_field_decl (FIELD_DECL, pfn_identifier,
+ ptr_type_node);
+ finish_builtin_type (vtable_entry_type, VTBL_PTR_TYPE, fields, 2,
+ double_type_node);
+
+ /* Make this part of an invisible union. */
+ fields[3] = copy_node (fields[2]);
+ TREE_TYPE (fields[3]) = delta_type_node;
+ DECL_NAME (fields[3]) = delta2_identifier;
+ DECL_MODE (fields[3]) = TYPE_MODE (delta_type_node);
+ DECL_SIZE (fields[3]) = TYPE_SIZE (delta_type_node);
+ TREE_UNSIGNED (fields[3]) = 0;
+ TREE_CHAIN (fields[2]) = fields[3];
+ vtable_entry_type = build_type_variant (vtable_entry_type, 1, 0);
+ }
+ record_builtin_type (RID_MAX, VTBL_PTR_TYPE, vtable_entry_type);
+
+ vtbl_type_node
+ = build_array_type (vtable_entry_type, NULL_TREE);
+ layout_type (vtbl_type_node);
+ vtbl_type_node = c_build_type_variant (vtbl_type_node, 1, 0);
+ record_builtin_type (RID_MAX, NULL_PTR, vtbl_type_node);
+
+ /* Simplify life by making a "sigtable_entry_type". Give its
+ fields names so that the debugger can use them. */
+
+ if (flag_handle_signatures)
+ {
+ sigtable_entry_type = make_lang_type (RECORD_TYPE);
+ fields[0] = build_lang_field_decl (FIELD_DECL,
+ get_identifier (SIGTABLE_CODE_NAME),
+ short_integer_type_node);
+ fields[1] = build_lang_field_decl (FIELD_DECL,
+ get_identifier (SIGTABLE_OFFSET_NAME),
+ short_integer_type_node);
+ fields[2] = build_lang_field_decl (FIELD_DECL,
+ get_identifier (SIGTABLE_PFN_NAME),
+ ptr_type_node);
+ finish_builtin_type (sigtable_entry_type, SIGTABLE_PTR_TYPE, fields, 2,
+ double_type_node);
+ sigtable_entry_type = build_type_variant (sigtable_entry_type, 1, 0);
+ record_builtin_type (RID_MAX, SIGTABLE_PTR_TYPE, sigtable_entry_type);
+ }
+
+ if (flag_dossier)
+ {
+ /* Must build __t_desc type. Currently, type descriptors look like this:
+
+ struct __t_desc
+ {
+ const char *name;
+ int size;
+ int bits;
+ struct __t_desc *points_to;
+ int ivars_count, meths_count;
+ struct __i_desc *ivars[];
+ struct __m_desc *meths[];
+ struct __t_desc *parents[];
+ struct __t_desc *vbases[];
+ int offsets[];
+ };
+
+ ...as per Linton's paper. */
+
+ __t_desc_type_node = make_lang_type (RECORD_TYPE);
+ __i_desc_type_node = make_lang_type (RECORD_TYPE);
+ __m_desc_type_node = make_lang_type (RECORD_TYPE);
+ __t_desc_array_type =
+ build_array_type (TYPE_POINTER_TO (__t_desc_type_node), NULL_TREE);
+ __i_desc_array_type =
+ build_array_type (TYPE_POINTER_TO (__i_desc_type_node), NULL_TREE);
+ __m_desc_array_type =
+ build_array_type (TYPE_POINTER_TO (__m_desc_type_node), NULL_TREE);
+
+ fields[0] = build_lang_field_decl (FIELD_DECL, get_identifier ("name"),
+ string_type_node);
+ fields[1] = build_lang_field_decl (FIELD_DECL, get_identifier ("size"),
+ unsigned_type_node);
+ fields[2] = build_lang_field_decl (FIELD_DECL, get_identifier ("bits"),
+ unsigned_type_node);
+ fields[3] = build_lang_field_decl (FIELD_DECL,
+ get_identifier ("points_to"),
+ TYPE_POINTER_TO (__t_desc_type_node));
+ fields[4] = build_lang_field_decl (FIELD_DECL,
+ get_identifier ("ivars_count"),
+ integer_type_node);
+ fields[5] = build_lang_field_decl (FIELD_DECL,
+ get_identifier ("meths_count"),
+ integer_type_node);
+ fields[6] = build_lang_field_decl (FIELD_DECL, get_identifier ("ivars"),
+ build_pointer_type (__i_desc_array_type));
+ fields[7] = build_lang_field_decl (FIELD_DECL, get_identifier ("meths"),
+ build_pointer_type (__m_desc_array_type));
+ fields[8] = build_lang_field_decl (FIELD_DECL, get_identifier ("parents"),
+ build_pointer_type (__t_desc_array_type));
+ fields[9] = build_lang_field_decl (FIELD_DECL, get_identifier ("vbases"),
+ build_pointer_type (__t_desc_array_type));
+ fields[10] = build_lang_field_decl (FIELD_DECL, get_identifier ("offsets"),
+ build_pointer_type (integer_type_node));
+ finish_builtin_type (__t_desc_type_node, "__t_desc", fields, 10, integer_type_node);
+
+ /* ivar descriptors look like this:
+
+ struct __i_desc
+ {
+ const char *name;
+ int offset;
+ struct __t_desc *type;
+ };
+ */
+
+ fields[0] = build_lang_field_decl (FIELD_DECL, get_identifier ("name"),
+ string_type_node);
+ fields[1] = build_lang_field_decl (FIELD_DECL, get_identifier ("offset"),
+ integer_type_node);
+ fields[2] = build_lang_field_decl (FIELD_DECL, get_identifier ("type"),
+ TYPE_POINTER_TO (__t_desc_type_node));
+ finish_builtin_type (__i_desc_type_node, "__i_desc", fields, 2,
+ integer_type_node);
+
+ /* method descriptors look like this:
+
+ struct __m_desc
+ {
+ const char *name;
+ int vindex;
+ struct __t_desc *vcontext;
+ struct __t_desc *return_type;
+ void (*address)();
+ short parm_count;
+ short required_parms;
+ struct __t_desc *parm_types[];
+ };
+ */
+
+ fields[0] = build_lang_field_decl (FIELD_DECL, get_identifier ("name"),
+ string_type_node);
+ fields[1] = build_lang_field_decl (FIELD_DECL, get_identifier ("vindex"),
+ integer_type_node);
+ fields[2] = build_lang_field_decl (FIELD_DECL, get_identifier ("vcontext"),
+ TYPE_POINTER_TO (__t_desc_type_node));
+ fields[3] = build_lang_field_decl (FIELD_DECL, get_identifier ("return_type"),
+ TYPE_POINTER_TO (__t_desc_type_node));
+ fields[4] = build_lang_field_decl (FIELD_DECL, get_identifier ("address"),
+ build_pointer_type (default_function_type));
+ fields[5] = build_lang_field_decl (FIELD_DECL, get_identifier ("parm_count"),
+ short_integer_type_node);
+ fields[6] = build_lang_field_decl (FIELD_DECL, get_identifier ("required_parms"),
+ short_integer_type_node);
+ fields[7] = build_lang_field_decl (FIELD_DECL, get_identifier ("parm_types"),
+ build_pointer_type (build_array_type (TYPE_POINTER_TO (__t_desc_type_node), NULL_TREE)));
+ finish_builtin_type (__m_desc_type_node, "__m_desc", fields, 7,
+ integer_type_node);
+ }
+
+ /* Now, C++. */
+ current_lang_name = lang_name_cplusplus;
+ if (flag_dossier)
+ {
+ int i = builtin_type_tdescs_len;
+ while (i > 0)
+ {
+ tree tdesc = build_t_desc (builtin_type_tdescs_arr[--i], 0);
+ TREE_ASM_WRITTEN (tdesc) = 1;
+ TREE_PUBLIC (TREE_OPERAND (tdesc, 0)) = 1;
+ }
+ }
+
+ auto_function (ansi_opname[(int) NEW_EXPR],
+ build_function_type (ptr_type_node,
+ tree_cons (NULL_TREE, sizetype,
+ void_list_node)),
+ NOT_BUILT_IN);
+ auto_function (ansi_opname[(int) VEC_NEW_EXPR],
+ build_function_type (ptr_type_node,
+ tree_cons (NULL_TREE, sizetype,
+ void_list_node)),
+ NOT_BUILT_IN);
+ auto_function (ansi_opname[(int) DELETE_EXPR],
+ build_function_type (void_type_node,
+ tree_cons (NULL_TREE, ptr_type_node,
+ void_list_node)),
+ NOT_BUILT_IN);
+ auto_function (ansi_opname[(int) VEC_DELETE_EXPR],
+ build_function_type (void_type_node,
+ tree_cons (NULL_TREE, ptr_type_node,
+ void_list_node)),
+ NOT_BUILT_IN);
+
+ abort_fndecl
+ = define_function ("abort",
+ build_function_type (void_type_node, void_list_node),
+ NOT_BUILT_IN, 0, 0);
+
+ /* Perform other language dependent initializations. */
+ init_class_processing ();
+ init_init_processing ();
+ init_search_processing ();
+
+ if (flag_handle_exceptions)
+ init_exception_processing ();
+ if (flag_gc)
+ init_gc_processing ();
+ if (flag_no_inline)
+ {
+ flag_inline_functions = 0;
+#if 0
+ /* This causes uneccessary emission of inline functions. */
+ flag_default_inline = 0;
+#endif
+ }
+ if (flag_cadillac)
+ init_cadillac ();
+
+ /* Create the global bindings for __FUNCTION__ and __PRETTY_FUNCTION__. */
+ declare_function_name ();
+
+ /* Prepare to check format strings against argument lists. */
+ init_function_format_info ();
+}
+
+/* Make a definition for a builtin function named NAME and whose data type
+ is TYPE. TYPE should be a function type with argument types.
+ FUNCTION_CODE tells later passes how to compile calls to this function.
+ See tree.h for its possible values.
+
+ If LIBRARY_NAME is nonzero, use that for DECL_ASSEMBLER_NAME,
+ the name to be called if we can't opencode the function. */
+
+tree
+define_function (name, type, function_code, pfn, library_name)
+ char *name;
+ tree type;
+ enum built_in_function function_code;
+ void (*pfn)();
+ char *library_name;
+{
+ tree decl = build_lang_decl (FUNCTION_DECL, get_identifier (name), type);
+ DECL_EXTERNAL (decl) = 1;
+ TREE_PUBLIC (decl) = 1;
+
+ /* Since `pushdecl' relies on DECL_ASSEMBLER_NAME instead of DECL_NAME,
+ we cannot change DECL_ASSEMBLER_NAME until we have installed this
+ function in the namespace. */
+ if (pfn) (*pfn) (decl);
+ if (library_name)
+ DECL_ASSEMBLER_NAME (decl) = get_identifier (library_name);
+ make_function_rtl (decl);
+ if (function_code != NOT_BUILT_IN)
+ {
+ DECL_BUILT_IN (decl) = 1;
+ DECL_FUNCTION_CODE (decl) = function_code;
+ }
+ return decl;
+}
+
+/* Called when a declaration is seen that contains no names to declare.
+ If its type is a reference to a structure, union or enum inherited
+ from a containing scope, shadow that tag name for the current scope
+ with a forward reference.
+ If its type defines a new named structure or union
+ or defines an enum, it is valid but we need not do anything here.
+ Otherwise, it is an error.
+
+ C++: may have to grok the declspecs to learn about static,
+ complain for anonymous unions. */
+
+void
+shadow_tag (declspecs)
+ tree declspecs;
+{
+ int found_tag = 0;
+ int warned = 0;
+ tree ob_modifier = NULL_TREE;
+ register tree link;
+ register enum tree_code code, ok_code = ERROR_MARK;
+ register tree t = NULL_TREE;
+
+ for (link = declspecs; link; link = TREE_CHAIN (link))
+ {
+ register tree value = TREE_VALUE (link);
+
+ code = TREE_CODE (value);
+ if (IS_AGGR_TYPE_CODE (code) || code == ENUMERAL_TYPE)
+ {
+ register tree name = TYPE_NAME (value);
+
+ if (code == ENUMERAL_TYPE && TYPE_SIZE (value) == 0)
+ cp_error ("forward declaration of `%#T'", value);
+
+ if (name == NULL_TREE)
+ name = lookup_tag_reverse (value, NULL_TREE);
+
+ if (name && TREE_CODE (name) == TYPE_DECL)
+ name = DECL_NAME (name);
+
+ t = lookup_tag (code, name, inner_binding_level, 1);
+
+ if (t == NULL_TREE)
+ {
+ push_obstacks (&permanent_obstack, &permanent_obstack);
+ if (IS_AGGR_TYPE_CODE (code))
+ t = make_lang_type (code);
+ else
+ t = make_node (code);
+ pushtag (name, t, 0);
+ pop_obstacks ();
+ ok_code = code;
+ }
+ else if (name != NULL_TREE || code == ENUMERAL_TYPE)
+ ok_code = code;
+
+ if (ok_code != ERROR_MARK)
+ found_tag++;
+ else
+ {
+ if (!warned)
+ pedwarn ("useless keyword or type name in declaration");
+ warned = 1;
+ }
+ }
+ else if (value == ridpointers[(int) RID_STATIC]
+ || value == ridpointers[(int) RID_EXTERN]
+ || value == ridpointers[(int) RID_AUTO]
+ || value == ridpointers[(int) RID_REGISTER])
+ ob_modifier = value;
+ }
+
+ /* This is where the variables in an anonymous union are
+ declared. An anonymous union declaration looks like:
+ union { ... } ;
+ because there is no declarator after the union, the parser
+ sends that declaration here. */
+ if (ok_code == UNION_TYPE
+ && t != NULL_TREE
+ && ((TREE_CODE (TYPE_NAME (t)) == IDENTIFIER_NODE
+ && ANON_AGGRNAME_P (TYPE_NAME (t)))
+ || (TREE_CODE (TYPE_NAME (t)) == TYPE_DECL
+ && ANON_AGGRNAME_P (TYPE_IDENTIFIER (t)))))
+ {
+ /* ANSI C++ June 5 1992 WP 9.5.3. Anonymous unions may not have
+ function members. */
+ if (TYPE_FIELDS (t))
+ {
+ tree decl = grokdeclarator (NULL_TREE, declspecs, NORMAL, 0,
+ NULL_TREE);
+ finish_anon_union (decl);
+ }
+ else
+ error ("anonymous union cannot have a function member");
+ }
+ else
+ {
+ /* Anonymous unions are objects, that's why we only check for
+ inappropriate specifiers in this branch. */
+ if (ob_modifier)
+ cp_error ("`%D' can only be specified for objects and functions",
+ ob_modifier);
+
+ if (ok_code == RECORD_TYPE
+ && found_tag == 1
+ && TYPE_LANG_SPECIFIC (t)
+ && CLASSTYPE_DECLARED_EXCEPTION (t))
+ {
+ if (TYPE_SIZE (t))
+ cp_error ("redeclaration of exception `%T'", t);
+ else
+ {
+ tree ename, decl;
+
+ push_obstacks (&permanent_obstack, &permanent_obstack);
+
+ pushclass (t, 0);
+
+ ename = TYPE_NAME (t);
+ if (TREE_CODE (ename) == TYPE_DECL)
+ ename = DECL_NAME (ename);
+ decl = build_lang_field_decl (VAR_DECL, ename, t);
+
+ pop_obstacks ();
+ }
+ }
+ else if (found_tag == 0)
+ pedwarn ("abstract declarator used as declaration");
+ else if (!warned && found_tag > 1)
+ pedwarn ("multiple types in one declaration");
+ }
+}
+
+/* Decode a "typename", such as "int **", returning a ..._TYPE node. */
+
+tree
+groktypename (typename)
+ tree typename;
+{
+ if (TREE_CODE (typename) != TREE_LIST)
+ return typename;
+ return grokdeclarator (TREE_VALUE (typename),
+ TREE_PURPOSE (typename),
+ TYPENAME, 0, NULL_TREE);
+}
+
+/* Decode a declarator in an ordinary declaration or data definition.
+ This is called as soon as the type information and variable name
+ have been parsed, before parsing the initializer if any.
+ Here we create the ..._DECL node, fill in its type,
+ and put it on the list of decls for the current context.
+ The ..._DECL node is returned as the value.
+
+ Exception: for arrays where the length is not specified,
+ the type is left null, to be filled in by `finish_decl'.
+
+ Function definitions do not come here; they go to start_function
+ instead. However, external and forward declarations of functions
+ do go through here. Structure field declarations are done by
+ grokfield and not through here. */
+
+/* Set this to zero to debug not using the temporary obstack
+ to parse initializers. */
+int debug_temp_inits = 1;
+
+tree
+start_decl (declarator, declspecs, initialized, raises)
+ tree declarator, declspecs;
+ int initialized;
+ tree raises;
+{
+ register tree decl;
+ register tree type, tem;
+ tree context;
+ extern int have_extern_spec;
+ extern int used_extern_spec;
+
+ int init_written = initialized;
+
+ /* This should only be done once on the top most decl. */
+ if (have_extern_spec && !used_extern_spec)
+ {
+ declspecs = decl_tree_cons (NULL_TREE, get_identifier ("extern"),
+ declspecs);
+ used_extern_spec = 1;
+ }
+
+ decl = grokdeclarator (declarator, declspecs, NORMAL, initialized, raises);
+ if (decl == NULL_TREE || decl == void_type_node)
+ return NULL_TREE;
+
+ type = TREE_TYPE (decl);
+
+ /* Don't lose if destructors must be executed at file-level. */
+ if (TREE_STATIC (decl)
+ && TYPE_NEEDS_DESTRUCTOR (type)
+ && !TREE_PERMANENT (decl))
+ {
+ push_obstacks (&permanent_obstack, &permanent_obstack);
+ decl = copy_node (decl);
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ tree itype = TYPE_DOMAIN (type);
+ if (itype && ! TREE_PERMANENT (itype))
+ {
+ itype = build_index_type (copy_to_permanent (TYPE_MAX_VALUE (itype)));
+ type = build_cplus_array_type (TREE_TYPE (type), itype);
+ TREE_TYPE (decl) = type;
+ }
+ }
+ pop_obstacks ();
+ }
+
+ /* Interesting work for this is done in `finish_exception_decl'. */
+ if (TREE_CODE (type) == RECORD_TYPE
+ && CLASSTYPE_DECLARED_EXCEPTION (type))
+ return decl;
+
+ /* Corresponding pop_obstacks is done in `finish_decl'. */
+ push_obstacks_nochange ();
+
+ context
+ = (TREE_CODE (decl) == FUNCTION_DECL && DECL_VIRTUAL_P (decl))
+ ? DECL_CLASS_CONTEXT (decl)
+ : DECL_CONTEXT (decl);
+
+ if (processing_template_decl)
+ {
+ tree d;
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ /* Declarator is a call_expr; extract arguments from it, since
+ grokdeclarator didn't do it. */
+ tree args;
+ args = copy_to_permanent (last_function_parms);
+ if (TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
+ {
+ tree t = TREE_TYPE (decl);
+
+ t = TYPE_METHOD_BASETYPE (t); /* type method belongs to */
+ if (TREE_CODE (t) != UNINSTANTIATED_P_TYPE)
+ {
+ t = build_pointer_type (t); /* base type of `this' */
+#if 1
+ /* I suspect this is wrong. */
+ t = build_type_variant (t, flag_this_is_variable <= 0,
+ 0); /* type of `this' */
+#else
+ t = build_type_variant (t, 0, 0); /* type of `this' */
+#endif
+ t = build (PARM_DECL, t, this_identifier);
+ TREE_CHAIN (t) = args;
+ args = t;
+ }
+ }
+ DECL_ARGUMENTS (decl) = args;
+ }
+ d = build_lang_decl (TEMPLATE_DECL, DECL_NAME (decl), TREE_TYPE (decl));
+ if (interface_unknown && flag_external_templates
+ && ! DECL_IN_SYSTEM_HEADER (decl))
+ warn_if_unknown_interface ();
+ TREE_PUBLIC (d) = TREE_PUBLIC (decl) =
+ flag_external_templates && !interface_unknown;
+ TREE_STATIC (d) = TREE_STATIC (decl);
+ DECL_EXTERNAL (d) = (DECL_EXTERNAL (decl)
+ && !(context && !DECL_THIS_EXTERN (decl)));
+ DECL_TEMPLATE_RESULT (d) = decl;
+ decl = d;
+ }
+
+ /* If this type of object needs a cleanup, and control may
+ jump past it, make a new binding level so that it is cleaned
+ up only when it is initialized first. */
+ if (TYPE_NEEDS_DESTRUCTOR (type)
+ && current_binding_level->more_cleanups_ok == 0)
+ pushlevel_temporary (1);
+
+ if (initialized)
+ /* Is it valid for this decl to have an initializer at all?
+ If not, set INITIALIZED to zero, which will indirectly
+ tell `finish_decl' to ignore the initializer once it is parsed. */
+ switch (TREE_CODE (decl))
+ {
+ case TYPE_DECL:
+ /* typedef foo = bar means give foo the same type as bar.
+ We haven't parsed bar yet, so `finish_decl' will fix that up.
+ Any other case of an initialization in a TYPE_DECL is an error. */
+ if (pedantic || list_length (declspecs) > 1)
+ {
+ cp_error ("typedef `%D' is initialized", decl);
+ initialized = 0;
+ }
+ break;
+
+ case FUNCTION_DECL:
+ cp_error ("function `%#D' is initialized like a variable", decl);
+ initialized = 0;
+ break;
+
+ default:
+ /* Don't allow initializations for incomplete types except for
+ arrays which might be completed by the initialization. */
+ if (type == error_mark_node)
+ ; /* Don't complain again. */
+ else if (TYPE_SIZE (type) != NULL_TREE)
+ ; /* A complete type is ok. */
+ else if (TREE_CODE (type) != ARRAY_TYPE)
+ {
+ cp_error ("variable `%#D' has initializer but incomplete type",
+ decl);
+ initialized = 0;
+ }
+ else if (TYPE_SIZE (TREE_TYPE (type)) == NULL_TREE)
+ {
+ cp_error ("elements of array `%#D' have incomplete type", decl);
+ initialized = 0;
+ }
+ }
+
+ if (!initialized
+ && TREE_CODE (decl) != TYPE_DECL
+ && TREE_CODE (decl) != TEMPLATE_DECL
+ && IS_AGGR_TYPE (type) && ! DECL_EXTERNAL (decl))
+ {
+ if (TYPE_SIZE (type) == NULL_TREE)
+ {
+ cp_error ("aggregate `%#D' has incomplete type and cannot be initialized",
+ decl);
+ /* Change the type so that assemble_variable will give
+ DECL an rtl we can live with: (mem (const_int 0)). */
+ TREE_TYPE (decl) = error_mark_node;
+ type = error_mark_node;
+ }
+ else
+ {
+ /* If any base type in the hierarchy of TYPE needs a constructor,
+ then we set initialized to 1. This way any nodes which are
+ created for the purposes of initializing this aggregate
+ will live as long as it does. This is necessary for global
+ aggregates which do not have their initializers processed until
+ the end of the file. */
+ initialized = TYPE_NEEDS_CONSTRUCTING (type);
+ }
+ }
+
+ if (initialized)
+ {
+ if (current_binding_level != global_binding_level
+ && DECL_EXTERNAL (decl))
+ cp_warning ("declaration of `%#D' has `extern' and is initialized",
+ decl);
+ DECL_EXTERNAL (decl) = 0;
+ if (current_binding_level == global_binding_level)
+ TREE_STATIC (decl) = 1;
+
+ /* Tell `pushdecl' this is an initialized decl
+ even though we don't yet have the initializer expression.
+ Also tell `finish_decl' it may store the real initializer. */
+ DECL_INITIAL (decl) = error_mark_node;
+ }
+
+ if (context && TYPE_SIZE (context) != NULL_TREE)
+ {
+ if (TREE_CODE (decl) == VAR_DECL)
+ {
+ tree field = lookup_field (context, DECL_NAME (decl), 0, 0);
+ if (field == NULL_TREE || TREE_CODE (field) != VAR_DECL)
+ cp_error ("`%#D' is not a static member of `%#T'", decl, context);
+ else if (duplicate_decls (decl, field))
+ decl = field;
+ }
+
+ /* If it was not explicitly declared `extern',
+ revoke any previous claims of DECL_EXTERNAL. */
+ if (DECL_THIS_EXTERN (decl) == 0)
+ DECL_EXTERNAL (decl) = 0;
+ if (DECL_LANG_SPECIFIC (decl))
+ DECL_IN_AGGR_P (decl) = 0;
+ pushclass (context, 2);
+ }
+
+ /* Add this decl to the current binding level, but not if it
+ comes from another scope, e.g. a static member variable.
+ TEM may equal DECL or it may be a previous decl of the same name. */
+
+ if ((TREE_CODE (decl) != PARM_DECL && DECL_CONTEXT (decl) != NULL_TREE)
+ || (TREE_CODE (decl) == TEMPLATE_DECL && !global_bindings_p ())
+ || TREE_CODE (type) == LANG_TYPE)
+ tem = decl;
+ else
+ tem = pushdecl (decl);
+
+ /* Tell the back-end to use or not use .common as appropriate. If we say
+ -fconserve-space, we want this to save space, at the expense of wrong
+ semantics. If we say -fno-conserve-space, we want this to produce
+ errors about redefs; to do this we force variables into the data
+ segment. Common storage is okay for non-public uninitialized data;
+ the linker can't match it with storage from other files, and we may
+ save some disk space. */
+ DECL_COMMON (tem) = flag_conserve_space || ! TREE_PUBLIC (tem);
+
+#if 0
+ /* We don't do this yet for GNU C++. */
+ /* For a local variable, define the RTL now. */
+ if (current_binding_level != global_binding_level
+ /* But not if this is a duplicate decl
+ and we preserved the rtl from the previous one
+ (which may or may not happen). */
+ && DECL_RTL (tem) == NULL_RTX)
+ {
+ if (TYPE_SIZE (TREE_TYPE (tem)) != NULL_TREE)
+ expand_decl (tem);
+ else if (TREE_CODE (TREE_TYPE (tem)) == ARRAY_TYPE
+ && DECL_INITIAL (tem) != NULL_TREE)
+ expand_decl (tem);
+ }
+#endif
+
+ if (TREE_CODE (decl) == TEMPLATE_DECL)
+ {
+ tree result = DECL_TEMPLATE_RESULT (decl);
+ if (DECL_CONTEXT (result) != NULL_TREE)
+ {
+ tree type;
+ type = DECL_CONTEXT (result);
+
+ if (TREE_CODE (type) != UNINSTANTIATED_P_TYPE)
+ {
+ cp_error ("declaration of `%D' in non-template type `%T'",
+ decl, type);
+ return NULL_TREE;
+ }
+
+ if (TREE_CODE (result) == FUNCTION_DECL)
+ return tem;
+ else if (TREE_CODE (result) == VAR_DECL)
+ {
+#if 0
+ tree tmpl = UPT_TEMPLATE (type);
+
+ fprintf (stderr, "%s:%d: adding ", __FILE__, __LINE__);
+ print_node_brief (stderr, "", DECL_NAME (tem), 0);
+ fprintf (stderr, " to class %s\n",
+ IDENTIFIER_POINTER (DECL_NAME (tmpl)));
+ DECL_TEMPLATE_MEMBERS (tmpl)
+ = perm_tree_cons (DECL_NAME (tem), tem,
+ DECL_TEMPLATE_MEMBERS (tmpl));
+ return tem;
+#else
+ sorry ("static data member templates");
+ return NULL_TREE;
+#endif
+ }
+ else
+ my_friendly_abort (13);
+ }
+ else if (TREE_CODE (result) == FUNCTION_DECL)
+ /*tem = push_overloaded_decl (tem, 0)*/;
+ else if (TREE_CODE (result) == VAR_DECL)
+ {
+ cp_error ("data template `%#D' must be member of a class template",
+ result);
+ return NULL_TREE;
+ }
+ else if (TREE_CODE (result) == TYPE_DECL)
+ {
+ cp_error ("invalid template `%#D'", result);
+ return NULL_TREE;
+ }
+ else
+ my_friendly_abort (14);
+ }
+
+ if (init_written
+ && ! (TREE_CODE (tem) == PARM_DECL
+ || (TREE_READONLY (tem)
+ && (TREE_CODE (tem) == VAR_DECL
+ || TREE_CODE (tem) == FIELD_DECL))))
+ {
+ /* When parsing and digesting the initializer,
+ use temporary storage. Do this even if we will ignore the value. */
+ if (current_binding_level == global_binding_level && debug_temp_inits)
+ {
+ if (TYPE_NEEDS_CONSTRUCTING (type)
+ || TREE_CODE (type) == REFERENCE_TYPE)
+ /* In this case, the initializer must lay down in permanent
+ storage, since it will be saved until `finish_file' is run. */
+ ;
+ else
+ temporary_allocation ();
+ }
+ }
+
+ if (flag_cadillac)
+ cadillac_start_decl (tem);
+
+ return tem;
+}
+
+#if 0 /* unused */
+static void
+make_temporary_for_reference (decl, ctor_call, init, cleanupp)
+ tree decl, ctor_call, init;
+ tree *cleanupp;
+{
+ tree type = TREE_TYPE (decl);
+ tree target_type = TREE_TYPE (type);
+ tree tmp, tmp_addr;
+
+ if (ctor_call)
+ {
+ tmp_addr = TREE_VALUE (TREE_OPERAND (ctor_call, 1));
+ if (TREE_CODE (tmp_addr) == NOP_EXPR)
+ tmp_addr = TREE_OPERAND (tmp_addr, 0);
+ my_friendly_assert (TREE_CODE (tmp_addr) == ADDR_EXPR, 146);
+ tmp = TREE_OPERAND (tmp_addr, 0);
+ }
+ else
+ {
+ tmp = get_temp_name (target_type,
+ current_binding_level == global_binding_level);
+ tmp_addr = build_unary_op (ADDR_EXPR, tmp, 0);
+ }
+
+ TREE_TYPE (tmp_addr) = build_pointer_type (target_type);
+ DECL_INITIAL (decl) = convert (TYPE_POINTER_TO (target_type), tmp_addr);
+ TREE_TYPE (DECL_INITIAL (decl)) = type;
+ if (TYPE_NEEDS_CONSTRUCTING (target_type))
+ {
+ if (current_binding_level == global_binding_level)
+ {
+ /* lay this variable out now. Otherwise `output_addressed_constants'
+ gets confused by its initializer. */
+ make_decl_rtl (tmp, NULL_PTR, 1);
+ static_aggregates = perm_tree_cons (init, tmp, static_aggregates);
+ }
+ else
+ {
+ if (ctor_call != NULL_TREE)
+ init = ctor_call;
+ else
+ init = build_method_call (tmp, constructor_name_full (target_type),
+ build_tree_list (NULL_TREE, init),
+ NULL_TREE, LOOKUP_NORMAL);
+ DECL_INITIAL (decl) = build (COMPOUND_EXPR, type, init,
+ DECL_INITIAL (decl));
+ *cleanupp = maybe_build_cleanup (tmp);
+ }
+ }
+ else
+ {
+ DECL_INITIAL (tmp) = init;
+ TREE_STATIC (tmp) = current_binding_level == global_binding_level;
+ finish_decl (tmp, init, 0, 0);
+ }
+ if (TREE_STATIC (tmp))
+ preserve_initializer ();
+}
+#endif
+
+/* Handle initialization of references.
+ These three arguments from from `finish_decl', and have the
+ same meaning here that they do there. */
+/* quotes on semantics can be found in ARM 8.4.3. */
+static void
+grok_reference_init (decl, type, init, cleanupp)
+ tree decl, type, init;
+ tree *cleanupp;
+{
+ tree tmp;
+
+ if (init == NULL_TREE)
+ {
+ if (DECL_LANG_SPECIFIC (decl) == 0
+ || DECL_IN_AGGR_P (decl) == 0)
+ {
+ cp_error ("`%D' declared as reference but not initialized", decl);
+ if (TREE_CODE (decl) == VAR_DECL)
+ SET_DECL_REFERENCE_SLOT (decl, error_mark_node);
+ }
+ return;
+ }
+
+ if (init == error_mark_node)
+ return;
+
+ if (TREE_CODE (type) == REFERENCE_TYPE
+ && TREE_CODE (init) == CONSTRUCTOR)
+ {
+ cp_error ("ANSI C++ forbids use of initializer list to initialize reference `%D'", decl);
+ return;
+ }
+
+ if (TREE_CODE (init) == TREE_LIST)
+ init = build_compound_expr (init);
+
+ if (TREE_CODE (TREE_TYPE (type)) != ARRAY_TYPE
+ && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE)
+ {
+ /* Note: default conversion is only called in very special cases. */
+ init = default_conversion (init);
+ }
+
+ tmp = convert_to_reference
+ (type, init, CONV_IMPLICIT, LOOKUP_SPECULATIVELY|LOOKUP_NORMAL, decl);
+
+ if (tmp == error_mark_node)
+ goto fail;
+ else if (tmp != NULL_TREE)
+ {
+ tree subtype = TREE_TYPE (type);
+ init = tmp;
+
+ /* Associate the cleanup with the reference so that we
+ don't get burned by "aggressive" cleanup policy. */
+ if (TYPE_NEEDS_DESTRUCTOR (subtype))
+ {
+ if (TREE_CODE (init) == WITH_CLEANUP_EXPR)
+ {
+ *cleanupp = TREE_OPERAND (init, 2);
+ TREE_OPERAND (init, 2) = error_mark_node;
+ }
+ else
+ {
+ if (TREE_CODE (tmp) == ADDR_EXPR)
+ tmp = TREE_OPERAND (tmp, 0);
+ if (TREE_CODE (tmp) == TARGET_EXPR)
+ {
+ *cleanupp = build_delete
+ (TYPE_POINTER_TO (subtype),
+ build_unary_op (ADDR_EXPR, TREE_OPERAND (tmp, 0), 0),
+ integer_two_node, LOOKUP_NORMAL|LOOKUP_DESTRUCTOR, 0);
+ TREE_OPERAND (tmp, 2) = error_mark_node;
+ }
+ }
+ }
+
+ DECL_INITIAL (decl) = save_expr (init);
+ }
+ else
+ {
+ cp_error ("cannot initialize `%T' from `%T'", type, TREE_TYPE (init));
+ goto fail;
+ }
+
+ /* ?? Can this be optimized in some cases to
+ hand back the DECL_INITIAL slot?? */
+ if (TYPE_SIZE (TREE_TYPE (type)))
+ {
+ init = convert_from_reference (decl);
+ if (TREE_PERMANENT (decl))
+ init = copy_to_permanent (init);
+ SET_DECL_REFERENCE_SLOT (decl, init);
+ }
+
+ if (TREE_STATIC (decl) && ! TREE_CONSTANT (DECL_INITIAL (decl)))
+ {
+ expand_static_init (decl, DECL_INITIAL (decl));
+ DECL_INITIAL (decl) = NULL_TREE;
+ }
+ return;
+
+ fail:
+ if (TREE_CODE (decl) == VAR_DECL)
+ SET_DECL_REFERENCE_SLOT (decl, error_mark_node);
+ return;
+}
+
+/* Finish processing of a declaration;
+ install its line number and initial value.
+ If the length of an array type is not known before,
+ it must be determined now, from the initial value, or it is an error.
+
+ Call `pop_obstacks' iff NEED_POP is nonzero.
+
+ For C++, `finish_decl' must be fairly evasive: it must keep initializers
+ for aggregates that have constructors alive on the permanent obstack,
+ so that the global initializing functions can be written at the end.
+
+ INIT0 holds the value of an initializer that should be allowed to escape
+ the normal rules.
+
+ For functions that take default parameters, DECL points to its
+ "maximal" instantiation. `finish_decl' must then also declared its
+ subsequently lower and lower forms of instantiation, checking for
+ ambiguity as it goes. This can be sped up later. */
+
+void
+finish_decl (decl, init, asmspec_tree, need_pop)
+ tree decl, init;
+ tree asmspec_tree;
+ int need_pop;
+{
+ register tree type;
+ tree cleanup = NULL_TREE, ttype;
+ int was_incomplete;
+ int temporary = allocation_temporary_p ();
+ char *asmspec = NULL;
+ int was_readonly = 0;
+
+ /* If this is 0, then we did not change obstacks. */
+ if (! decl)
+ {
+ if (init)
+ error ("assignment (not initialization) in declaration");
+ return;
+ }
+
+ /* If a name was specified, get the string. */
+ if (asmspec_tree)
+ asmspec = TREE_STRING_POINTER (asmspec_tree);
+
+ /* If the type of the thing we are declaring either has
+ a constructor, or has a virtual function table pointer,
+ AND its initialization was accepted by `start_decl',
+ then we stayed on the permanent obstack through the
+ declaration, otherwise, changed obstacks as GCC would. */
+
+ type = TREE_TYPE (decl);
+
+ was_incomplete = (DECL_SIZE (decl) == NULL_TREE);
+
+ /* Take care of TYPE_DECLs up front. */
+ if (TREE_CODE (decl) == TYPE_DECL)
+ {
+ if (init && DECL_INITIAL (decl))
+ {
+ /* typedef foo = bar; store the type of bar as the type of foo. */
+ TREE_TYPE (decl) = type = TREE_TYPE (init);
+ DECL_INITIAL (decl) = init = NULL_TREE;
+ }
+ if (type != error_mark_node
+ && IS_AGGR_TYPE (type) && DECL_NAME (decl))
+ {
+ if (TREE_TYPE (DECL_NAME (decl)) && TREE_TYPE (decl) != type)
+ cp_warning ("shadowing previous type declaration of `%#D'", decl);
+ set_identifier_type_value (DECL_NAME (decl), type);
+ CLASSTYPE_GOT_SEMICOLON (type) = 1;
+ }
+ GNU_xref_decl (current_function_decl, decl);
+ rest_of_decl_compilation (decl, NULL_PTR,
+ DECL_CONTEXT (decl) == NULL_TREE, 0);
+ goto finish_end;
+ }
+ if (type != error_mark_node && IS_AGGR_TYPE (type)
+ && CLASSTYPE_DECLARED_EXCEPTION (type))
+ {
+ CLASSTYPE_GOT_SEMICOLON (type) = 1;
+ goto finish_end;
+ }
+ if (TREE_CODE (decl) != FUNCTION_DECL)
+ {
+ ttype = target_type (type);
+#if 0 /* WTF? -KR
+ Leave this out until we can figure out why it was
+ needed/desirable in the first place. Then put a comment
+ here explaining why. Or just delete the code if no ill
+ effects arise. */
+ if (TYPE_NAME (ttype)
+ && TREE_CODE (TYPE_NAME (ttype)) == TYPE_DECL
+ && ANON_AGGRNAME_P (TYPE_IDENTIFIER (ttype)))
+ {
+ tree old_id = TYPE_IDENTIFIER (ttype);
+ char *newname = (char *)alloca (IDENTIFIER_LENGTH (old_id) + 2);
+ /* Need to preserve template data for UPT nodes. */
+ tree old_template = IDENTIFIER_TEMPLATE (old_id);
+ newname[0] = '_';
+ bcopy (IDENTIFIER_POINTER (old_id), newname + 1,
+ IDENTIFIER_LENGTH (old_id) + 1);
+ old_id = get_identifier (newname);
+ lookup_tag_reverse (ttype, old_id);
+ TYPE_IDENTIFIER (ttype) = old_id;
+ IDENTIFIER_TEMPLATE (old_id) = old_template;
+ }
+#endif
+ }
+
+ if (! DECL_EXTERNAL (decl) && TREE_READONLY (decl)
+ && TYPE_NEEDS_CONSTRUCTING (type))
+ {
+
+ /* Currently, GNU C++ puts constants in text space, making them
+ impossible to initialize. In the future, one would hope for
+ an operating system which understood the difference between
+ initialization and the running of a program. */
+ was_readonly = 1;
+ TREE_READONLY (decl) = 0;
+ }
+
+ if (TREE_CODE (decl) == FIELD_DECL)
+ {
+ if (init && init != error_mark_node)
+ my_friendly_assert (TREE_PERMANENT (init), 147);
+
+ if (asmspec)
+ {
+ /* This must override the asm specifier which was placed
+ by grokclassfn. Lay this out fresh.
+
+ @@ Should emit an error if this redefines an asm-specified
+ @@ name, or if we have already used the function's name. */
+ DECL_RTL (TREE_TYPE (decl)) = NULL_RTX;
+ DECL_ASSEMBLER_NAME (decl) = get_identifier (asmspec);
+ make_decl_rtl (decl, asmspec, 0);
+ }
+ }
+ /* If `start_decl' didn't like having an initialization, ignore it now. */
+ else if (init != NULL_TREE && DECL_INITIAL (decl) == NULL_TREE)
+ init = NULL_TREE;
+ else if (DECL_EXTERNAL (decl))
+ ;
+ else if (TREE_CODE (type) == REFERENCE_TYPE
+ || (TYPE_LANG_SPECIFIC (type) && IS_SIGNATURE_REFERENCE (type)))
+ {
+ grok_reference_init (decl, type, init, &cleanup);
+ init = NULL_TREE;
+ }
+
+ GNU_xref_decl (current_function_decl, decl);
+
+ if (TREE_CODE (decl) == FIELD_DECL)
+ ;
+ else if (TREE_CODE (decl) == CONST_DECL)
+ {
+ my_friendly_assert (TREE_CODE (decl) != REFERENCE_TYPE, 148);
+
+ DECL_INITIAL (decl) = init;
+
+ /* This will keep us from needing to worry about our obstacks. */
+ my_friendly_assert (init != NULL_TREE, 149);
+ init = NULL_TREE;
+ }
+ else if (init)
+ {
+ if (TYPE_HAS_CONSTRUCTOR (type) || TYPE_NEEDS_CONSTRUCTING (type))
+ {
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ init = digest_init (type, init, (tree *) 0);
+ else if (TREE_CODE (init) == CONSTRUCTOR)
+ {
+ if (TYPE_NEEDS_CONSTRUCTING (type))
+ {
+ cp_error ("`%D' must be initialized by constructor, not by `{...}'",
+ decl);
+ init = error_mark_node;
+ }
+ else
+ goto dont_use_constructor;
+ }
+#if 0
+ /* fix this in `build_functional_cast' instead.
+ Here's the trigger code:
+
+ struct ostream
+ {
+ ostream ();
+ ostream (int, char *);
+ ostream (char *);
+ operator char *();
+ ostream (void *);
+ operator void *();
+ operator << (int);
+ };
+ int buf_size = 1024;
+ static char buf[buf_size];
+ const char *debug(int i) {
+ char *b = &buf[0];
+ ostream o = ostream(buf_size, b);
+ o << i;
+ return buf;
+ }
+ */
+
+ else if (TREE_CODE (init) == TARGET_EXPR
+ && TREE_CODE (TREE_OPERAND (init, 1) == NEW_EXPR))
+ {
+ /* User wrote something like `foo x = foo (args)' */
+ my_friendly_assert (TREE_CODE (TREE_OPERAND (init, 0)) == VAR_DECL, 150);
+ my_friendly_assert (DECL_NAME (TREE_OPERAND (init, 0)) == NULL_TREE, 151);
+
+ /* User wrote exactly `foo x = foo (args)' */
+ if (TYPE_MAIN_VARIANT (type) == TREE_TYPE (init))
+ {
+ init = build (CALL_EXPR, TREE_TYPE (init),
+ TREE_OPERAND (TREE_OPERAND (init, 1), 0),
+ TREE_OPERAND (TREE_OPERAND (init, 1), 1), 0);
+ TREE_SIDE_EFFECTS (init) = 1;
+ }
+ }
+#endif
+
+ /* We must hide the initializer so that expand_decl
+ won't try to do something it does not understand. */
+ if (current_binding_level == global_binding_level)
+ {
+ tree value;
+ if (DECL_COMMON (decl))
+ /* Should this be a NULL_TREE? */
+ value = error_mark_node;
+ else
+ value = build (CONSTRUCTOR, type, NULL_TREE, NULL_TREE);
+ DECL_INITIAL (decl) = value;
+ }
+ else
+ DECL_INITIAL (decl) = error_mark_node;
+ }
+ else
+ {
+ dont_use_constructor:
+ if (TREE_CODE (init) != TREE_VEC)
+ init = store_init_value (decl, init);
+
+ /* Don't let anyone try to initialize this variable
+ until we are ready to do so. */
+ if (init)
+ {
+ tree value;
+ if (DECL_COMMON (decl))
+ value = error_mark_node;
+ else
+ value = build (CONSTRUCTOR, type, NULL_TREE, NULL_TREE);
+ DECL_INITIAL (decl) = value;
+ }
+ }
+ }
+ else if (DECL_EXTERNAL (decl))
+ ;
+ else if (TREE_CODE_CLASS (TREE_CODE (type)) == 't'
+ && (IS_AGGR_TYPE (type) || TYPE_NEEDS_CONSTRUCTING (type)))
+ {
+ tree ctype = type;
+ while (TREE_CODE (ctype) == ARRAY_TYPE)
+ ctype = TREE_TYPE (ctype);
+ if (! TYPE_NEEDS_CONSTRUCTING (ctype))
+ {
+ if (CLASSTYPE_READONLY_FIELDS_NEED_INIT (ctype))
+ cp_error ("structure `%D' with uninitialized const members", decl);
+ if (CLASSTYPE_REF_FIELDS_NEED_INIT (ctype))
+ cp_error ("structure `%D' with uninitialized reference members",
+ decl);
+ }
+
+ if (TREE_CODE (decl) == VAR_DECL
+ && !DECL_INITIAL (decl)
+ && !TYPE_NEEDS_CONSTRUCTING (type)
+ && (TYPE_READONLY (type) || TREE_READONLY (decl)))
+ cp_error ("uninitialized const `%D'", decl);
+
+ /* Initialize variables in need of static initialization with
+ an empty CONSTRUCTOR to keep assemble_variable from putting them in
+ the wrong program space. */
+ if (flag_pic == 0
+ && TREE_STATIC (decl)
+ && TREE_PUBLIC (decl)
+ && ! DECL_EXTERNAL (decl)
+ && TREE_CODE (decl) == VAR_DECL
+ && TYPE_NEEDS_CONSTRUCTING (type)
+ && (DECL_INITIAL (decl) == NULL_TREE
+ || DECL_INITIAL (decl) == error_mark_node)
+ && ! DECL_COMMON (decl))
+ DECL_INITIAL (decl) = build (CONSTRUCTOR, type, NULL_TREE,
+ NULL_TREE);
+ }
+ else if (TREE_CODE (decl) == VAR_DECL
+ && TREE_CODE (type) != REFERENCE_TYPE
+ && (TYPE_READONLY (type) || TREE_READONLY (decl)))
+ {
+ /* ``Unless explicitly declared extern, a const object does not have
+ external linkage and must be initialized. ($8.4; $12.1)'' ARM 7.1.6
+ However, if it's `const int foo = 1; const int foo;', don't complain
+ about the second decl, since it does have an initializer before.
+ We deliberately don't complain about arrays, because they're
+ supposed to be initialized by a constructor. */
+ if (! DECL_INITIAL (decl)
+ && TREE_CODE (type) != ARRAY_TYPE
+ && (!pedantic || !current_class_type))
+ cp_error ("uninitialized const `%#D'", decl);
+ }
+
+ /* For top-level declaration, the initial value was read in
+ the temporary obstack. MAXINDEX, rtl, etc. to be made below
+ must go in the permanent obstack; but don't discard the
+ temporary data yet. */
+
+ if (current_binding_level == global_binding_level && temporary)
+ end_temporary_allocation ();
+
+ /* Deduce size of array from initialization, if not already known. */
+
+ if (TREE_CODE (type) == ARRAY_TYPE
+ && TYPE_DOMAIN (type) == NULL_TREE
+ && TREE_CODE (decl) != TYPE_DECL)
+ {
+ int do_default
+ = (TREE_STATIC (decl)
+ /* Even if pedantic, an external linkage array
+ may have incomplete type at first. */
+ ? pedantic && ! DECL_EXTERNAL (decl)
+ : !DECL_EXTERNAL (decl));
+ tree initializer = init ? init : DECL_INITIAL (decl);
+ int failure = complete_array_type (type, initializer, do_default);
+
+ if (failure == 1)
+ cp_error ("initializer fails to determine size of `%D'", decl);
+
+ if (failure == 2)
+ {
+ if (do_default)
+ cp_error ("array size missing in `%D'", decl);
+ /* If a `static' var's size isn't known, make it extern as
+ well as static, so it does not get allocated. If it's not
+ `static', then don't mark it extern; finish_incomplete_decl
+ will give it a default size and it will get allocated. */
+ else if (!pedantic && TREE_STATIC (decl) && !TREE_PUBLIC (decl))
+ DECL_EXTERNAL (decl) = 1;
+ }
+
+ if (pedantic && TYPE_DOMAIN (type) != NULL_TREE
+ && tree_int_cst_lt (TYPE_MAX_VALUE (TYPE_DOMAIN (type)),
+ integer_zero_node))
+ cp_error ("zero-size array `%D'", decl);
+
+ layout_decl (decl, 0);
+ }
+
+ if (TREE_CODE (decl) == VAR_DECL)
+ {
+ if (DECL_SIZE (decl) == NULL_TREE
+ && TYPE_SIZE (TREE_TYPE (decl)) != NULL_TREE)
+ layout_decl (decl, 0);
+
+ if (TREE_STATIC (decl) && DECL_SIZE (decl) == NULL_TREE)
+ {
+ /* A static variable with an incomplete type:
+ that is an error if it is initialized.
+ Otherwise, let it through, but if it is not `extern'
+ then it may cause an error message later. */
+ if (DECL_INITIAL (decl) != NULL_TREE)
+ cp_error ("storage size of `%D' isn't known", decl);
+ init = NULL_TREE;
+ }
+ else if (!DECL_EXTERNAL (decl) && DECL_SIZE (decl) == NULL_TREE)
+ {
+ /* An automatic variable with an incomplete type: that is an error.
+ Don't talk about array types here, since we took care of that
+ message in grokdeclarator. */
+ cp_error ("storage size of `%D' isn't known", decl);
+ TREE_TYPE (decl) = error_mark_node;
+ }
+ else if (!DECL_EXTERNAL (decl) && IS_AGGR_TYPE (ttype))
+ /* Let debugger know it should output info for this type. */
+ note_debug_info_needed (ttype);
+
+ if ((DECL_EXTERNAL (decl) || TREE_STATIC (decl))
+ && DECL_SIZE (decl) != NULL_TREE
+ && ! TREE_CONSTANT (DECL_SIZE (decl)))
+ {
+ if (TREE_CODE (DECL_SIZE (decl)) == INTEGER_CST)
+ constant_expression_warning (DECL_SIZE (decl));
+ else
+ cp_error ("storage size of `%D' isn't constant", decl);
+ }
+
+ if (!DECL_EXTERNAL (decl) && TYPE_NEEDS_DESTRUCTOR (type))
+ {
+ int yes = suspend_momentary ();
+
+ /* If INIT comes from a functional cast, use the cleanup
+ we built for that. Otherwise, make our own cleanup. */
+ if (init && TREE_CODE (init) == WITH_CLEANUP_EXPR
+ && comptypes (TREE_TYPE (decl), TREE_TYPE (init), 1))
+ {
+ cleanup = TREE_OPERAND (init, 2);
+ init = TREE_OPERAND (init, 0);
+ current_binding_level->have_cleanups = 1;
+ }
+ else
+ cleanup = maybe_build_cleanup (decl);
+ resume_momentary (yes);
+ }
+ }
+ /* PARM_DECLs get cleanups, too. */
+ else if (TREE_CODE (decl) == PARM_DECL && TYPE_NEEDS_DESTRUCTOR (type))
+ {
+ if (temporary)
+ end_temporary_allocation ();
+ cleanup = maybe_build_cleanup (decl);
+ if (temporary)
+ resume_temporary_allocation ();
+ }
+
+ /* Output the assembler code and/or RTL code for variables and functions,
+ unless the type is an undefined structure or union.
+ If not, it will get done when the type is completed. */
+
+ if (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == FUNCTION_DECL
+ || TREE_CODE (decl) == RESULT_DECL)
+ {
+ /* ??? FIXME: What about nested classes? */
+ int toplev = (current_binding_level == global_binding_level
+ || pseudo_global_level_p ());
+ int was_temp
+ = ((flag_traditional
+ || (TREE_STATIC (decl) && TYPE_NEEDS_DESTRUCTOR (type)))
+ && allocation_temporary_p ());
+
+ if (was_temp)
+ end_temporary_allocation ();
+
+ if (TREE_CODE (decl) == VAR_DECL
+ && current_binding_level != global_binding_level
+ && ! TREE_STATIC (decl)
+ && type_needs_gc_entry (type))
+ DECL_GC_OFFSET (decl) = size_int (++current_function_obstack_index);
+
+ if (TREE_CODE (decl) == VAR_DECL && DECL_VIRTUAL_P (decl))
+ make_decl_rtl (decl, NULL_PTR, toplev);
+ else if (TREE_CODE (decl) == VAR_DECL
+ && TREE_READONLY (decl)
+ && DECL_INITIAL (decl) != NULL_TREE
+ && DECL_INITIAL (decl) != error_mark_node
+ && ! EMPTY_CONSTRUCTOR_P (DECL_INITIAL (decl)))
+ {
+ DECL_INITIAL (decl) = save_expr (DECL_INITIAL (decl));
+
+ if (asmspec)
+ DECL_ASSEMBLER_NAME (decl) = get_identifier (asmspec);
+
+ if (! toplev
+ && TREE_STATIC (decl)
+ && ! TREE_SIDE_EFFECTS (decl)
+ && ! TREE_PUBLIC (decl)
+ && ! DECL_EXTERNAL (decl)
+ && ! TYPE_NEEDS_DESTRUCTOR (type)
+ && DECL_MODE (decl) != BLKmode)
+ {
+ /* If this variable is really a constant, then fill its DECL_RTL
+ slot with something which won't take up storage.
+ If something later should take its address, we can always give
+ it legitimate RTL at that time. */
+ DECL_RTL (decl) = gen_reg_rtx (DECL_MODE (decl));
+ store_expr (DECL_INITIAL (decl), DECL_RTL (decl), 0);
+ TREE_ASM_WRITTEN (decl) = 1;
+ }
+ else if (toplev && ! TREE_PUBLIC (decl))
+ {
+ /* If this is a static const, change its apparent linkage
+ if it belongs to a #pragma interface. */
+ if (!interface_unknown)
+ {
+ TREE_PUBLIC (decl) = 1;
+ DECL_EXTERNAL (decl) = interface_only;
+ }
+ make_decl_rtl (decl, asmspec, toplev);
+ }
+ else
+ rest_of_decl_compilation (decl, asmspec, toplev, 0);
+ }
+ else if (TREE_CODE (decl) == VAR_DECL
+ && DECL_LANG_SPECIFIC (decl)
+ && DECL_IN_AGGR_P (decl))
+ {
+ if (TREE_STATIC (decl))
+ {
+ if (init == NULL_TREE
+#ifdef DEFAULT_STATIC_DEFS
+ /* If this code is dead, then users must
+ explicitly declare static member variables
+ outside the class def'n as well. */
+ && TYPE_NEEDS_CONSTRUCTING (type)
+#endif
+ )
+ {
+ DECL_EXTERNAL (decl) = 1;
+ make_decl_rtl (decl, asmspec, 1);
+ }
+ else
+ rest_of_decl_compilation (decl, asmspec, toplev, 0);
+ }
+ else
+ /* Just a constant field. Should not need any rtl. */
+ goto finish_end0;
+ }
+ else
+ rest_of_decl_compilation (decl, asmspec, toplev, 0);
+
+ if (was_temp)
+ resume_temporary_allocation ();
+
+ if (type != error_mark_node
+ && TYPE_LANG_SPECIFIC (type)
+ && CLASSTYPE_ABSTRACT_VIRTUALS (type))
+ abstract_virtuals_error (decl, type);
+ else if ((TREE_CODE (type) == FUNCTION_TYPE
+ || TREE_CODE (type) == METHOD_TYPE)
+ && TYPE_LANG_SPECIFIC (TREE_TYPE (type))
+ && CLASSTYPE_ABSTRACT_VIRTUALS (TREE_TYPE (type)))
+ abstract_virtuals_error (decl, TREE_TYPE (type));
+
+ if (TYPE_LANG_SPECIFIC (type) && IS_SIGNATURE (type))
+ signature_error (decl, type);
+ else if ((TREE_CODE (type) == FUNCTION_TYPE
+ || TREE_CODE (type) == METHOD_TYPE)
+ && TYPE_LANG_SPECIFIC (TREE_TYPE (type))
+ && IS_SIGNATURE (TREE_TYPE (type)))
+ signature_error (decl, TREE_TYPE (type));
+
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ {
+#if 0
+ /* C++: Handle overloaded functions with default parameters. */
+ if (DECL_OVERLOADED (decl))
+ {
+ tree parmtypes = TYPE_ARG_TYPES (type);
+ tree prev = NULL_TREE;
+ tree original_name = DECL_NAME (decl);
+ struct lang_decl *tmp_lang_decl = DECL_LANG_SPECIFIC (decl);
+ /* All variants will share an uncollectible lang_decl. */
+ copy_decl_lang_specific (decl);
+
+ while (parmtypes && parmtypes != void_list_node)
+ {
+ /* The default value for the parameter in parmtypes is
+ stored in the TREE_PURPOSE of the TREE_LIST. */
+ if (TREE_PURPOSE (parmtypes))
+ {
+ tree fnname, fndecl;
+ tree *argp;
+
+ argp = prev ? & TREE_CHAIN (prev)
+ : & TYPE_ARG_TYPES (type);
+
+ *argp = NULL_TREE;
+ fnname = build_decl_overload (original_name,
+ TYPE_ARG_TYPES (type), 0);
+ *argp = parmtypes;
+ fndecl = build_decl (FUNCTION_DECL, fnname, type);
+ DECL_EXTERNAL (fndecl) = DECL_EXTERNAL (decl);
+ TREE_PUBLIC (fndecl) = TREE_PUBLIC (decl);
+ DECL_INLINE (fndecl) = DECL_INLINE (decl);
+ /* Keep G++ from thinking this function is unused.
+ It is only used to speed up search in name space. */
+ TREE_USED (fndecl) = 1;
+ TREE_ASM_WRITTEN (fndecl) = 1;
+ DECL_INITIAL (fndecl) = NULL_TREE;
+ DECL_LANG_SPECIFIC (fndecl) = DECL_LANG_SPECIFIC (decl);
+ fndecl = pushdecl (fndecl);
+ DECL_INITIAL (fndecl) = error_mark_node;
+ DECL_RTL (fndecl) = DECL_RTL (decl);
+ }
+ prev = parmtypes;
+ parmtypes = TREE_CHAIN (parmtypes);
+ }
+ DECL_LANG_SPECIFIC (decl) = tmp_lang_decl;
+ }
+#endif
+ }
+ else if (DECL_EXTERNAL (decl))
+ ;
+ else if (TREE_STATIC (decl) && type != error_mark_node)
+ {
+ /* Cleanups for static variables are handled by `finish_file'. */
+ if (TYPE_NEEDS_CONSTRUCTING (type) || init != NULL_TREE)
+ expand_static_init (decl, init);
+ else if (TYPE_NEEDS_DESTRUCTOR (type))
+ static_aggregates = perm_tree_cons (NULL_TREE, decl,
+ static_aggregates);
+
+ /* Make entry in appropriate vector. */
+ if (flag_gc && type_needs_gc_entry (type))
+ build_static_gc_entry (decl, type);
+ }
+ else if (! toplev)
+ {
+ tree old_cleanups = cleanups_this_call;
+ /* This is a declared decl which must live until the
+ end of the binding contour. It may need a cleanup. */
+
+ /* Recompute the RTL of a local array now
+ if it used to be an incomplete type. */
+ if (was_incomplete && ! TREE_STATIC (decl))
+ {
+ /* If we used it already as memory, it must stay in memory. */
+ TREE_ADDRESSABLE (decl) = TREE_USED (decl);
+ /* If it's still incomplete now, no init will save it. */
+ if (DECL_SIZE (decl) == NULL_TREE)
+ DECL_INITIAL (decl) = NULL_TREE;
+ expand_decl (decl);
+ }
+ else if (! TREE_ASM_WRITTEN (decl)
+ && (TYPE_SIZE (type) != NULL_TREE
+ || TREE_CODE (type) == ARRAY_TYPE))
+ {
+ /* Do this here, because we did not expand this decl's
+ rtl in start_decl. */
+ if (DECL_RTL (decl) == NULL_RTX)
+ expand_decl (decl);
+ else if (cleanup)
+ {
+ /* XXX: Why don't we use decl here? */
+ /* Ans: Because it was already expanded? */
+ if (! expand_decl_cleanup (NULL_TREE, cleanup))
+ cp_error ("parser lost in parsing declaration of `%D'",
+ decl);
+ /* Cleanup used up here. */
+ cleanup = NULL_TREE;
+ }
+ }
+
+ if (DECL_SIZE (decl) && type != error_mark_node)
+ {
+ /* Compute and store the initial value. */
+ expand_decl_init (decl);
+
+ if (init || TYPE_NEEDS_CONSTRUCTING (type))
+ {
+ emit_line_note (DECL_SOURCE_FILE (decl),
+ DECL_SOURCE_LINE (decl));
+ expand_aggr_init (decl, init, 0);
+ }
+
+ /* Set this to 0 so we can tell whether an aggregate
+ which was initialized was ever used. */
+ if (TYPE_NEEDS_CONSTRUCTING (type))
+ TREE_USED (decl) = 0;
+
+ /* Store the cleanup, if there was one. */
+ if (cleanup)
+ {
+ if (! expand_decl_cleanup (decl, cleanup))
+ cp_error ("parser lost in parsing declaration of `%D'",
+ decl);
+ }
+ }
+ /* Cleanup any temporaries needed for the initial value. */
+ expand_cleanups_to (old_cleanups);
+ }
+ finish_end0:
+
+ /* Undo call to `pushclass' that was done in `start_decl'
+ due to initialization of qualified member variable.
+ I.e., Foo::x = 10; */
+ {
+ tree context = DECL_CONTEXT (decl);
+ if (context
+ && TREE_CODE_CLASS (TREE_CODE (context)) == 't'
+ && (TREE_CODE (decl) == VAR_DECL
+ /* We also have a pushclass done that we need to undo here
+ if we're at top level and declare a method. */
+ || (TREE_CODE (decl) == FUNCTION_DECL
+ /* If size hasn't been set, we're still defining it,
+ and therefore inside the class body; don't pop
+ the binding level.. */
+ && TYPE_SIZE (context) != NULL_TREE
+ /* The binding level gets popped elsewhere for a
+ friend declaration inside another class. */
+ /*
+ && TYPE_IDENTIFIER (context) == current_class_name
+ */
+ && context == current_class_type
+ )))
+ popclass (1);
+ }
+ }
+
+ finish_end:
+
+ /* If requested, warn about definitions of large data objects. */
+
+ if (warn_larger_than
+ && (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == PARM_DECL)
+ && !DECL_EXTERNAL (decl))
+ {
+ register tree decl_size = DECL_SIZE (decl);
+
+ if (decl_size && TREE_CODE (decl_size) == INTEGER_CST)
+ {
+ unsigned units = TREE_INT_CST_LOW (decl_size) / BITS_PER_UNIT;
+
+ if (units > larger_than_size)
+ warning_with_decl (decl, "size of `%s' is %u bytes", units);
+ }
+ }
+
+ if (need_pop)
+ {
+ /* Resume permanent allocation, if not within a function. */
+ /* The corresponding push_obstacks_nochange is in start_decl,
+ start_method, groktypename, and in grokfield. */
+ pop_obstacks ();
+ }
+
+ if (was_readonly)
+ TREE_READONLY (decl) = 1;
+
+ if (flag_cadillac)
+ cadillac_finish_decl (decl);
+}
+
+void
+expand_static_init (decl, init)
+ tree decl;
+ tree init;
+{
+ tree oldstatic = value_member (decl, static_aggregates);
+ tree old_cleanups;
+
+ if (oldstatic)
+ {
+ if (TREE_PURPOSE (oldstatic) && init != NULL_TREE)
+ cp_error ("multiple initializations given for `%D'", decl);
+ }
+ else if (current_binding_level != global_binding_level
+ && current_binding_level->pseudo_global == 0)
+ {
+ /* Emit code to perform this initialization but once. */
+ tree temp;
+
+ /* Remember this information until end of file. */
+ push_obstacks (&permanent_obstack, &permanent_obstack);
+
+ /* Emit code to perform this initialization but once. */
+ temp = get_temp_name (integer_type_node, 1);
+ rest_of_decl_compilation (temp, NULL_PTR, 0, 0);
+ expand_start_cond (build_binary_op (EQ_EXPR, temp,
+ integer_zero_node, 1), 0);
+ old_cleanups = cleanups_this_call;
+ expand_assignment (temp, integer_one_node, 0, 0);
+ if (TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl)))
+ {
+ expand_aggr_init (decl, init, 0);
+ do_pending_stack_adjust ();
+ }
+ else
+ expand_assignment (decl, init, 0, 0);
+ /* Cleanup any temporaries needed for the initial value. */
+ expand_cleanups_to (old_cleanups);
+ expand_end_cond ();
+ if (TYPE_NEEDS_DESTRUCTOR (TREE_TYPE (decl)))
+ {
+ static_aggregates = perm_tree_cons (temp, decl, static_aggregates);
+ TREE_STATIC (static_aggregates) = 1;
+ }
+
+ /* Resume old (possibly temporary) allocation. */
+ pop_obstacks ();
+ }
+ else
+ {
+ /* This code takes into account memory allocation
+ policy of `start_decl'. Namely, if TYPE_NEEDS_CONSTRUCTING
+ does not hold for this object, then we must make permanent
+ the storage currently in the temporary obstack. */
+ if (! TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl)))
+ preserve_initializer ();
+ static_aggregates = perm_tree_cons (init, decl, static_aggregates);
+ }
+}
+
+/* Make TYPE a complete type based on INITIAL_VALUE.
+ Return 0 if successful, 1 if INITIAL_VALUE can't be deciphered,
+ 2 if there was no information (in which case assume 1 if DO_DEFAULT). */
+
+int
+complete_array_type (type, initial_value, do_default)
+ tree type, initial_value;
+ int do_default;
+{
+ register tree maxindex = NULL_TREE;
+ int value = 0;
+
+ if (initial_value)
+ {
+ /* Note MAXINDEX is really the maximum index,
+ one less than the size. */
+ if (TREE_CODE (initial_value) == STRING_CST)
+ maxindex = build_int_2 (TREE_STRING_LENGTH (initial_value) - 1, 0);
+ else if (TREE_CODE (initial_value) == CONSTRUCTOR)
+ {
+ register int nelts
+ = list_length (CONSTRUCTOR_ELTS (initial_value));
+ maxindex = build_int_2 (nelts - 1, - (nelts == 0));
+ }
+ else
+ {
+ /* Make an error message unless that happened already. */
+ if (initial_value != error_mark_node)
+ value = 1;
+
+ /* Prevent further error messages. */
+ maxindex = build_int_2 (0, 0);
+ }
+ }
+
+ if (!maxindex)
+ {
+ if (do_default)
+ maxindex = build_int_2 (0, 0);
+ value = 2;
+ }
+
+ if (maxindex)
+ {
+ tree itype;
+
+ TYPE_DOMAIN (type) = build_index_type (maxindex);
+ if (!TREE_TYPE (maxindex))
+ TREE_TYPE (maxindex) = TYPE_DOMAIN (type);
+ if (initial_value)
+ itype = TREE_TYPE (initial_value);
+ else
+ itype = NULL;
+ if (itype && !TYPE_DOMAIN (itype))
+ TYPE_DOMAIN (itype) = TYPE_DOMAIN (type);
+ }
+
+ /* Lay out the type now that we can get the real answer. */
+
+ layout_type (type);
+
+ return value;
+}
+
+/* Return zero if something is declared to be a member of type
+ CTYPE when in the context of CUR_TYPE. STRING is the error
+ message to print in that case. Otherwise, quietly return 1. */
+static int
+member_function_or_else (ctype, cur_type, string)
+ tree ctype, cur_type;
+ char *string;
+{
+ if (ctype && ctype != cur_type)
+ {
+ error (string, TYPE_NAME_STRING (ctype));
+ return 0;
+ }
+ return 1;
+}
+
+/* Subroutine of `grokdeclarator'. */
+
+/* Generate errors possibly applicable for a given set of specifiers.
+ This is for ARM $7.1.2. */
+static void
+bad_specifiers (object, type, virtualp, quals, inlinep, friendp, raises)
+ tree object;
+ char *type;
+ int virtualp, quals, friendp, raises, inlinep;
+{
+ if (virtualp)
+ cp_error ("`%D' declared as a `virtual' %s", object, type);
+ if (inlinep)
+ cp_error ("`%D' declared as an `inline' %s", object, type);
+ if (quals)
+ cp_error ("`const' and `volatile' function specifiers on `%D' invalid in %s declaration",
+ object, type);
+ if (friendp)
+ cp_error_at ("invalid friend declaration", object);
+ if (raises)
+ cp_error_at ("invalid raises declaration", object);
+}
+
+/* CTYPE is class type, or null if non-class.
+ TYPE is type this FUNCTION_DECL should have, either FUNCTION_TYPE
+ or METHOD_TYPE.
+ DECLARATOR is the function's name.
+ VIRTUALP is truthvalue of whether the function is virtual or not.
+ FLAGS are to be passed through to `grokclassfn'.
+ QUALS are qualifiers indicating whether the function is `const'
+ or `volatile'.
+ RAISES is a list of exceptions that this function can raise.
+ CHECK is 1 if we must find this method in CTYPE, 0 if we should
+ not look, and -1 if we should not call `grokclassfn' at all. */
+static tree
+grokfndecl (ctype, type, declarator, virtualp, flags, quals,
+ raises, check, publicp)
+ tree ctype, type;
+ tree declarator;
+ int virtualp;
+ enum overload_flags flags;
+ tree quals, raises;
+ int check, publicp;
+{
+ tree cname, decl;
+ int staticp = ctype && TREE_CODE (type) == FUNCTION_TYPE;
+
+ if (ctype)
+ cname = TREE_CODE (TYPE_NAME (ctype)) == TYPE_DECL
+ ? TYPE_IDENTIFIER (ctype) : TYPE_NAME (ctype);
+ else
+ cname = NULL_TREE;
+
+ if (raises)
+ {
+ type = build_exception_variant (ctype, type, raises);
+ raises = TYPE_RAISES_EXCEPTIONS (type);
+ }
+ decl = build_lang_decl (FUNCTION_DECL, declarator, type);
+ /* propagate volatile out from type to decl */
+ if (TYPE_VOLATILE (type))
+ TREE_THIS_VOLATILE (decl) = 1;
+
+ /* Should probably propagate const out from type to decl I bet (mrs). */
+ if (staticp)
+ {
+ DECL_STATIC_FUNCTION_P (decl) = 1;
+ DECL_CONTEXT (decl) = ctype;
+ DECL_CLASS_CONTEXT (decl) = ctype;
+ }
+
+ if (publicp)
+ TREE_PUBLIC (decl) = 1;
+
+ DECL_EXTERNAL (decl) = 1;
+ if (quals != NULL_TREE && TREE_CODE (type) == FUNCTION_TYPE)
+ {
+ cp_error ("%smember function `%D' cannot have `%T' method qualifier",
+ (ctype ? "static " : "non-"), decl, TREE_VALUE (quals));
+ quals = NULL_TREE;
+ }
+
+ if (IDENTIFIER_OPNAME_P (DECL_NAME (decl)))
+ grok_op_properties (decl, virtualp, check < 0);
+
+ /* Caller will do the rest of this. */
+ if (check < 0)
+ return decl;
+
+ if (flags == NO_SPECIAL && ctype && constructor_name (cname) == declarator)
+ {
+ tree tmp;
+ /* Just handle constructors here. We could do this
+ inside the following if stmt, but I think
+ that the code is more legible by breaking this
+ case out. See comments below for what each of
+ the following calls is supposed to do. */
+ DECL_CONSTRUCTOR_P (decl) = 1;
+
+ grokclassfn (ctype, declarator, decl, flags, quals);
+ if (check)
+ check_classfn (ctype, declarator, decl);
+ if (! grok_ctor_properties (ctype, decl))
+ return NULL_TREE;
+
+ if (check == 0 && ! current_function_decl)
+ {
+ /* FIXME: this should only need to look at
+ IDENTIFIER_GLOBAL_VALUE. */
+ tmp = lookup_name (DECL_ASSEMBLER_NAME (decl), 0);
+ if (tmp == NULL_TREE)
+ IDENTIFIER_GLOBAL_VALUE (DECL_ASSEMBLER_NAME (decl)) = decl;
+ else if (TREE_CODE (tmp) != TREE_CODE (decl))
+ cp_error ("inconsistent declarations for `%D'", decl);
+ else
+ {
+ duplicate_decls (decl, tmp);
+ decl = tmp;
+ /* avoid creating circularities. */
+ DECL_CHAIN (decl) = NULL_TREE;
+ }
+ make_decl_rtl (decl, NULL_PTR, 1);
+ }
+ }
+ else
+ {
+ tree tmp;
+
+ /* Function gets the ugly name, field gets the nice one.
+ This call may change the type of the function (because
+ of default parameters)! */
+ if (ctype != NULL_TREE)
+ grokclassfn (ctype, cname, decl, flags, quals);
+
+ if (ctype != NULL_TREE && check)
+ check_classfn (ctype, cname, decl);
+
+ if (ctype == NULL_TREE || check)
+ return decl;
+
+ /* Now install the declaration of this function so that others may
+ find it (esp. its DECL_FRIENDLIST). Don't do this for local class
+ methods, though. */
+ if (! current_function_decl)
+ {
+ /* FIXME: this should only need to look at
+ IDENTIFIER_GLOBAL_VALUE. */
+ tmp = lookup_name (DECL_ASSEMBLER_NAME (decl), 0);
+ if (tmp == NULL_TREE)
+ IDENTIFIER_GLOBAL_VALUE (DECL_ASSEMBLER_NAME (decl)) = decl;
+ else if (TREE_CODE (tmp) != TREE_CODE (decl))
+ cp_error ("inconsistent declarations for `%D'", decl);
+ else
+ {
+ duplicate_decls (decl, tmp);
+ decl = tmp;
+ /* avoid creating circularities. */
+ DECL_CHAIN (decl) = NULL_TREE;
+ }
+ make_decl_rtl (decl, NULL_PTR, 1);
+ }
+
+ /* If this declaration supersedes the declaration of
+ a method declared virtual in the base class, then
+ mark this field as being virtual as well. */
+ {
+ tree binfos = BINFO_BASETYPES (TYPE_BINFO (ctype));
+ int i, n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+
+ for (i = 0; i < n_baselinks; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+ if (TYPE_VIRTUAL_P (BINFO_TYPE (base_binfo))
+ || flag_all_virtual == 1)
+ {
+ tmp = get_matching_virtual (base_binfo, decl,
+ flags == DTOR_FLAG);
+ if (tmp)
+ {
+ /* If this function overrides some virtual in some base
+ class, then the function itself is also necessarily
+ virtual, even if the user didn't explicitly say so. */
+ DECL_VIRTUAL_P (decl) = 1;
+
+ /* The TMP we really want is the one from the deepest
+ baseclass on this path, taking care not to
+ duplicate if we have already found it (via another
+ path to its virtual baseclass. */
+ if (staticp)
+ {
+ cp_error ("method `%D' may not be declared static",
+ decl);
+ cp_error_at ("(since `%D' declared virtual in base class.)",
+ tmp);
+ break;
+ }
+ virtualp = 1;
+
+ {
+ /* The argument types may have changed... */
+ tree argtypes = TYPE_ARG_TYPES (TREE_TYPE (decl));
+ tree base_variant = TREE_TYPE (TREE_VALUE (argtypes));
+
+ argtypes = commonparms (TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (tmp))),
+ TREE_CHAIN (argtypes));
+ /* But the return type has not. */
+ type = build_cplus_method_type (base_variant, TREE_TYPE (type), argtypes);
+ if (raises)
+ {
+ type = build_exception_variant (ctype, type, raises);
+ raises = TYPE_RAISES_EXCEPTIONS (type);
+ }
+ TREE_TYPE (decl) = type;
+ DECL_VINDEX (decl)
+ = tree_cons (NULL_TREE, tmp, DECL_VINDEX (decl));
+ }
+ break;
+ }
+ }
+ }
+ }
+ if (virtualp)
+ {
+ if (DECL_VINDEX (decl) == NULL_TREE)
+ DECL_VINDEX (decl) = error_mark_node;
+ IDENTIFIER_VIRTUAL_P (DECL_NAME (decl)) = 1;
+ if (ctype && CLASSTYPE_VTABLE_NEEDS_WRITING (ctype)
+ /* If this function is derived from a template, don't
+ make it public. This shouldn't be here, but there's
+ no good way to override the interface pragmas for one
+ function or class only. Bletch. */
+ && IDENTIFIER_TEMPLATE (TYPE_IDENTIFIER (ctype)) == NULL_TREE
+ && (write_virtuals == 2
+ || (write_virtuals == 3
+ && CLASSTYPE_INTERFACE_KNOWN (ctype))))
+ TREE_PUBLIC (decl) = 1;
+ }
+ }
+ return decl;
+}
+
+static tree
+grokvardecl (type, declarator, specbits, initialized)
+ tree type;
+ tree declarator;
+ RID_BIT_TYPE specbits;
+ int initialized;
+{
+ tree decl;
+
+ if (TREE_CODE (type) == OFFSET_TYPE)
+ {
+ /* If you declare a static member so that it
+ can be initialized, the code will reach here. */
+ tree basetype = TYPE_OFFSET_BASETYPE (type);
+ type = TREE_TYPE (type);
+ decl = build_lang_field_decl (VAR_DECL, declarator, type);
+ DECL_CONTEXT (decl) = basetype;
+ DECL_CLASS_CONTEXT (decl) = basetype;
+ }
+ else
+ decl = build_decl (VAR_DECL, declarator, type);
+
+ if (RIDBIT_SETP (RID_EXTERN, specbits))
+ {
+ DECL_THIS_EXTERN (decl) = 1;
+ DECL_EXTERNAL (decl) = !initialized;
+ }
+
+ /* In class context, static means one per class,
+ public access, and static storage. */
+ if (DECL_FIELD_CONTEXT (decl) != NULL_TREE
+ && IS_AGGR_TYPE (DECL_FIELD_CONTEXT (decl)))
+ {
+ TREE_PUBLIC (decl) = 1;
+ TREE_STATIC (decl) = 1;
+ DECL_EXTERNAL (decl) = 0;
+ }
+ /* At top level, either `static' or no s.c. makes a definition
+ (perhaps tentative), and absence of `static' makes it public. */
+ else if (current_binding_level == global_binding_level)
+ {
+ TREE_PUBLIC (decl) = RIDBIT_NOTSETP (RID_STATIC, specbits);
+ TREE_STATIC (decl) = ! DECL_EXTERNAL (decl);
+ }
+ /* Not at top level, only `static' makes a static definition. */
+ else
+ {
+ TREE_STATIC (decl) = !! RIDBIT_SETP (RID_STATIC, specbits);
+ TREE_PUBLIC (decl) = DECL_EXTERNAL (decl);
+ }
+ return decl;
+}
+
+/* Create a canonical pointer to member function type. */
+
+tree
+build_ptrmemfunc_type (type)
+ tree type;
+{
+ tree fields[4];
+ tree t;
+ tree u;
+
+ /* If a canonical type already exists for this type, use it. We use
+ this method instead of type_hash_canon, because it only does a
+ simple equality check on the list of field members. */
+
+ if ((t = TYPE_GET_PTRMEMFUNC_TYPE (type)))
+ return t;
+
+ push_obstacks (TYPE_OBSTACK (type), TYPE_OBSTACK (type));
+
+ u = make_lang_type (UNION_TYPE);
+ fields[0] = build_lang_field_decl (FIELD_DECL, pfn_identifier, type);
+ fields[1] = build_lang_field_decl (FIELD_DECL, delta2_identifier,
+ delta_type_node);
+ finish_builtin_type (u, "__ptrmemfunc_type", fields, 1, ptr_type_node);
+ TYPE_NAME (u) = NULL_TREE;
+
+ t = make_lang_type (RECORD_TYPE);
+
+ /* Let the front-end know this is a pointer to member function. */
+ TYPE_PTRMEMFUNC_FLAG(t) = 1;
+
+ fields[0] = build_lang_field_decl (FIELD_DECL, delta_identifier,
+ delta_type_node);
+ fields[1] = build_lang_field_decl (FIELD_DECL, index_identifier,
+ delta_type_node);
+ fields[2] = build_lang_field_decl (FIELD_DECL, pfn_or_delta2_identifier, u);
+ finish_builtin_type (t, "__ptrmemfunc_type", fields, 2, ptr_type_node);
+
+ pop_obstacks ();
+
+ /* Zap out the name so that the back-end will give us the debugging
+ information for this anonymous RECORD_TYPE. */
+ TYPE_NAME (t) = NULL_TREE;
+
+ TYPE_SET_PTRMEMFUNC_TYPE (type, t);
+
+ /* Seems to be wanted. */
+ CLASSTYPE_GOT_SEMICOLON (t) = 1;
+ return t;
+}
+
+/* Given declspecs and a declarator,
+ determine the name and type of the object declared
+ and construct a ..._DECL node for it.
+ (In one case we can return a ..._TYPE node instead.
+ For invalid input we sometimes return 0.)
+
+ DECLSPECS is a chain of tree_list nodes whose value fields
+ are the storage classes and type specifiers.
+
+ DECL_CONTEXT says which syntactic context this declaration is in:
+ NORMAL for most contexts. Make a VAR_DECL or FUNCTION_DECL or TYPE_DECL.
+ FUNCDEF for a function definition. Like NORMAL but a few different
+ error messages in each case. Return value may be zero meaning
+ this definition is too screwy to try to parse.
+ MEMFUNCDEF for a function definition. Like FUNCDEF but prepares to
+ handle member functions (which have FIELD context).
+ Return value may be zero meaning this definition is too screwy to
+ try to parse.
+ PARM for a parameter declaration (either within a function prototype
+ or before a function body). Make a PARM_DECL, or return void_type_node.
+ TYPENAME if for a typename (in a cast or sizeof).
+ Don't make a DECL node; just return the ..._TYPE node.
+ FIELD for a struct or union field; make a FIELD_DECL.
+ BITFIELD for a field with specified width.
+ INITIALIZED is 1 if the decl has an initializer.
+
+ In the TYPENAME case, DECLARATOR is really an absolute declarator.
+ It may also be so in the PARM case, for a prototype where the
+ argument type is specified but not the name.
+
+ This function is where the complicated C meanings of `static'
+ and `extern' are interpreted.
+
+ For C++, if there is any monkey business to do, the function which
+ calls this one must do it, i.e., prepending instance variables,
+ renaming overloaded function names, etc.
+
+ Note that for this C++, it is an error to define a method within a class
+ which does not belong to that class.
+
+ Except in the case where SCOPE_REFs are implicitly known (such as
+ methods within a class being redundantly qualified),
+ declarations which involve SCOPE_REFs are returned as SCOPE_REFs
+ (class_name::decl_name). The caller must also deal with this.
+
+ If a constructor or destructor is seen, and the context is FIELD,
+ then the type gains the attribute TREE_HAS_x. If such a declaration
+ is erroneous, NULL_TREE is returned.
+
+ QUALS is used only for FUNCDEF and MEMFUNCDEF cases. For a member
+ function, these are the qualifiers to give to the `this' pointer.
+
+ May return void_type_node if the declarator turned out to be a friend.
+ See grokfield for details. */
+
+enum return_types { return_normal, return_ctor, return_dtor, return_conversion };
+
+tree
+grokdeclarator (declarator, declspecs, decl_context, initialized, raises)
+ tree declspecs;
+ tree declarator;
+ enum decl_context decl_context;
+ int initialized;
+ tree raises;
+{
+ RID_BIT_TYPE specbits;
+ int nclasses = 0;
+ tree spec;
+ tree type = NULL_TREE;
+ int longlong = 0;
+ int constp;
+ int volatilep;
+ int virtualp, friendp, inlinep, staticp;
+ int explicit_int = 0;
+ int explicit_char = 0;
+ int opaque_typedef = 0;
+ tree typedef_decl = NULL_TREE;
+ char *name;
+ tree typedef_type = NULL_TREE;
+ int funcdef_flag = 0;
+ enum tree_code innermost_code = ERROR_MARK;
+ int bitfield = 0;
+ int size_varies = 0;
+ /* Set this to error_mark_node for FIELD_DECLs we could not handle properly.
+ All FIELD_DECLs we build here have `init' put into their DECL_INITIAL. */
+ tree init = NULL_TREE;
+
+ /* Keep track of what sort of function is being processed
+ so that we can warn about default return values, or explicit
+ return values which do not match prescribed defaults. */
+ enum return_types return_type = return_normal;
+
+ tree dname = NULL_TREE;
+ tree ctype = current_class_type;
+ tree ctor_return_type = NULL_TREE;
+ enum overload_flags flags = NO_SPECIAL;
+ tree quals = NULL_TREE;
+
+ RIDBIT_RESET_ALL (specbits);
+ if (decl_context == FUNCDEF)
+ funcdef_flag = 1, decl_context = NORMAL;
+ else if (decl_context == MEMFUNCDEF)
+ funcdef_flag = -1, decl_context = FIELD;
+ else if (decl_context == BITFIELD)
+ bitfield = 1, decl_context = FIELD;
+
+ if (flag_traditional && allocation_temporary_p ())
+ end_temporary_allocation ();
+
+ /* Look inside a declarator for the name being declared
+ and get it as a string, for an error message. */
+ {
+ tree last = NULL_TREE;
+ register tree decl = declarator;
+ name = NULL;
+
+ while (decl)
+ switch (TREE_CODE (decl))
+ {
+ case COND_EXPR:
+ ctype = NULL_TREE;
+ decl = TREE_OPERAND (decl, 0);
+ break;
+
+ case BIT_NOT_EXPR: /* for C++ destructors! */
+ {
+ tree name = TREE_OPERAND (decl, 0);
+ tree rename = NULL_TREE;
+
+ my_friendly_assert (flags == NO_SPECIAL, 152);
+ flags = DTOR_FLAG;
+ return_type = return_dtor;
+ my_friendly_assert (TREE_CODE (name) == IDENTIFIER_NODE, 153);
+ if (ctype == NULL_TREE)
+ {
+ if (current_class_type == NULL_TREE)
+ {
+ error ("destructors must be member functions");
+ flags = NO_SPECIAL;
+ }
+ else
+ {
+ tree t = constructor_name (current_class_name);
+ if (t != name)
+ rename = t;
+ }
+ }
+ else
+ {
+ tree t = constructor_name (ctype);
+ if (t != name)
+ rename = t;
+ }
+
+ if (rename)
+ {
+ error ("destructor `%s' must match class name `%s'",
+ IDENTIFIER_POINTER (name),
+ IDENTIFIER_POINTER (rename));
+ TREE_OPERAND (decl, 0) = rename;
+ }
+ decl = name;
+ }
+ break;
+
+ case ADDR_EXPR: /* C++ reference declaration */
+ /* fall through */
+ case ARRAY_REF:
+ case INDIRECT_REF:
+ ctype = NULL_TREE;
+ innermost_code = TREE_CODE (decl);
+ last = decl;
+ decl = TREE_OPERAND (decl, 0);
+ break;
+
+ case CALL_EXPR:
+ if (parmlist_is_exprlist (TREE_OPERAND (decl, 1)))
+ {
+ /* This is actually a variable declaration using constructor
+ syntax. We need to call start_decl and finish_decl so we
+ can get the variable initialized... */
+
+ if (last)
+ /* We need to insinuate ourselves into the declarator in place
+ of the CALL_EXPR. */
+ TREE_OPERAND (last, 0) = TREE_OPERAND (decl, 0);
+ else
+ declarator = TREE_OPERAND (decl, 0);
+
+ init = TREE_OPERAND (decl, 1);
+
+ decl = start_decl (declarator, declspecs, 1, NULL_TREE);
+ finish_decl (decl, init, NULL_TREE, 1);
+ return 0;
+ }
+ innermost_code = TREE_CODE (decl);
+ decl = TREE_OPERAND (decl, 0);
+ if (decl_context == FIELD && ctype == NULL_TREE)
+ ctype = current_class_type;
+ if (ctype != NULL_TREE
+ && decl != NULL_TREE && flags != DTOR_FLAG
+ && decl == constructor_name (ctype))
+ {
+ return_type = return_ctor;
+ ctor_return_type = ctype;
+ }
+ ctype = NULL_TREE;
+ break;
+
+ case IDENTIFIER_NODE:
+ dname = decl;
+ decl = NULL_TREE;
+
+ if (IDENTIFIER_OPNAME_P (dname))
+ {
+ if (IDENTIFIER_TYPENAME_P (dname))
+ {
+ my_friendly_assert (flags == NO_SPECIAL, 154);
+ flags = TYPENAME_FLAG;
+ ctor_return_type = TREE_TYPE (dname);
+ return_type = return_conversion;
+ }
+ name = operator_name_string (dname);
+ }
+ else
+ name = IDENTIFIER_POINTER (dname);
+ break;
+
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ case ENUMERAL_TYPE:
+ /* Parse error puts this typespec where
+ a declarator should go. */
+ error ("declarator name missing");
+ dname = TYPE_NAME (decl);
+ if (dname && TREE_CODE (dname) == TYPE_DECL)
+ dname = DECL_NAME (dname);
+ name = dname ? IDENTIFIER_POINTER (dname) : "<nameless>";
+ declspecs = temp_tree_cons (NULL_TREE, decl, declspecs);
+ decl = NULL_TREE;
+ break;
+
+ /* C++ extension */
+ case SCOPE_REF:
+ {
+ /* Perform error checking, and convert class names to types.
+ We may call grokdeclarator multiple times for the same
+ tree structure, so only do the conversion once. In this
+ case, we have exactly what we want for `ctype'. */
+ tree cname = TREE_OPERAND (decl, 0);
+ if (cname == NULL_TREE)
+ ctype = NULL_TREE;
+ /* Can't use IS_AGGR_TYPE because CNAME might not be a type. */
+ else if (IS_AGGR_TYPE_CODE (TREE_CODE (cname))
+ || TREE_CODE (cname) == UNINSTANTIATED_P_TYPE)
+ ctype = cname;
+ else if (! is_aggr_typedef (cname, 1))
+ {
+ TREE_OPERAND (decl, 0) = NULL_TREE;
+ }
+ /* Must test TREE_OPERAND (decl, 1), in case user gives
+ us `typedef (class::memfunc)(int); memfunc *memfuncptr;' */
+ else if (TREE_OPERAND (decl, 1)
+ && TREE_CODE (TREE_OPERAND (decl, 1)) == INDIRECT_REF)
+ {
+ TREE_OPERAND (decl, 0) = IDENTIFIER_TYPE_VALUE (cname);
+ }
+ else if (ctype == NULL_TREE)
+ {
+ ctype = IDENTIFIER_TYPE_VALUE (cname);
+ TREE_OPERAND (decl, 0) = ctype;
+ }
+ else if (TREE_COMPLEXITY (decl) == current_class_depth)
+ TREE_OPERAND (decl, 0) = ctype;
+ else
+ {
+ if (! UNIQUELY_DERIVED_FROM_P (IDENTIFIER_TYPE_VALUE (cname),
+ ctype))
+ {
+ cp_error ("type `%T' is not derived from type `%T'",
+ IDENTIFIER_TYPE_VALUE (cname), ctype);
+ TREE_OPERAND (decl, 0) = NULL_TREE;
+ }
+ else
+ {
+ ctype = IDENTIFIER_TYPE_VALUE (cname);
+ TREE_OPERAND (decl, 0) = ctype;
+ }
+ }
+
+ decl = TREE_OPERAND (decl, 1);
+ if (ctype)
+ {
+ if (TREE_CODE (decl) == IDENTIFIER_NODE
+ && constructor_name (ctype) == decl)
+ {
+ return_type = return_ctor;
+ ctor_return_type = ctype;
+ }
+ else if (TREE_CODE (decl) == BIT_NOT_EXPR
+ && TREE_CODE (TREE_OPERAND (decl, 0)) == IDENTIFIER_NODE
+ && constructor_name (ctype) == TREE_OPERAND (decl, 0))
+ {
+ return_type = return_dtor;
+ ctor_return_type = ctype;
+ flags = DTOR_FLAG;
+ decl = TREE_OPERAND (decl, 0);
+ }
+ }
+ }
+ break;
+
+ case ERROR_MARK:
+ decl = NULL_TREE;
+ break;
+
+ default:
+ return 0; /* We used to do a 155 abort here. */
+ }
+ if (name == NULL)
+ name = "type name";
+ }
+
+ /* A function definition's declarator must have the form of
+ a function declarator. */
+
+ if (funcdef_flag && innermost_code != CALL_EXPR)
+ return 0;
+
+ /* Anything declared one level down from the top level
+ must be one of the parameters of a function
+ (because the body is at least two levels down). */
+
+ /* This heuristic cannot be applied to C++ nodes! Fixed, however,
+ by not allowing C++ class definitions to specify their parameters
+ with xdecls (must be spec.d in the parmlist).
+
+ Since we now wait to push a class scope until we are sure that
+ we are in a legitimate method context, we must set oldcname
+ explicitly (since current_class_name is not yet alive). */
+
+ if (decl_context == NORMAL
+ && current_binding_level->level_chain == global_binding_level)
+ decl_context = PARM;
+
+ /* Look through the decl specs and record which ones appear.
+ Some typespecs are defined as built-in typenames.
+ Others, the ones that are modifiers of other types,
+ are represented by bits in SPECBITS: set the bits for
+ the modifiers that appear. Storage class keywords are also in SPECBITS.
+
+ If there is a typedef name or a type, store the type in TYPE.
+ This includes builtin typedefs such as `int'.
+
+ Set EXPLICIT_INT if the type is `int' or `char' and did not
+ come from a user typedef.
+
+ Set LONGLONG if `long' is mentioned twice.
+
+ For C++, constructors and destructors have their own fast treatment. */
+
+ for (spec = declspecs; spec; spec = TREE_CHAIN (spec))
+ {
+ register int i;
+ register tree id;
+
+ /* Certain parse errors slip through. For example,
+ `int class;' is not caught by the parser. Try
+ weakly to recover here. */
+ if (TREE_CODE (spec) != TREE_LIST)
+ return 0;
+
+ id = TREE_VALUE (spec);
+
+ if (TREE_CODE (id) == IDENTIFIER_NODE)
+ {
+ if (id == ridpointers[(int) RID_INT]
+ || id == ridpointers[(int) RID_CHAR]
+ || id == ridpointers[(int) RID_BOOL]
+ || id == ridpointers[(int) RID_WCHAR])
+ {
+ if (type)
+ error ("extraneous `%T' ignored", id);
+ else
+ {
+ if (id == ridpointers[(int) RID_INT])
+ explicit_int = 1;
+ else if (id == ridpointers[(int) RID_CHAR])
+ explicit_char = 1;
+ type = TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (id));
+ }
+ goto found;
+ }
+ /* C++ aggregate types. */
+ if (IDENTIFIER_HAS_TYPE_VALUE (id))
+ {
+ if (type)
+ cp_error ("multiple declarations `%T' and `%T'", type, id);
+ else
+ type = IDENTIFIER_TYPE_VALUE (id);
+ goto found;
+ }
+
+ for (i = (int) RID_FIRST_MODIFIER; i < (int) RID_MAX; i++)
+ {
+ if (ridpointers[i] == id)
+ {
+ if (i == (int) RID_LONG && RIDBIT_SETP (i, specbits))
+ {
+ if (pedantic && flag_ansi)
+ pedwarn ("duplicate `long'");
+ else if (longlong)
+ error ("`long long long' is too long for GCC");
+ else
+ longlong = 1;
+ }
+ else if (RIDBIT_SETP (i, specbits))
+ pedwarn ("duplicate `%s'", IDENTIFIER_POINTER (id));
+ RIDBIT_SET (i, specbits);
+ goto found;
+ }
+ }
+ }
+ if (type)
+ error ("two or more data types in declaration of `%s'", name);
+ else if (TREE_CODE (id) == IDENTIFIER_NODE)
+ {
+ register tree t = lookup_name (id, 1);
+ if (!t || TREE_CODE (t) != TYPE_DECL)
+ error ("`%s' fails to be a typedef or built in type",
+ IDENTIFIER_POINTER (id));
+ else
+ {
+ type = TREE_TYPE (t);
+ typedef_decl = t;
+ }
+ }
+ else if (TREE_CODE (id) != ERROR_MARK)
+ /* Can't change CLASS nodes into RECORD nodes here! */
+ type = id;
+
+ found: ;
+ }
+
+ typedef_type = type;
+
+ /* No type at all: default to `int', and set EXPLICIT_INT
+ because it was not a user-defined typedef.
+ Except when we have a `typedef' inside a signature, in
+ which case the type defaults to `unknown type' and is
+ instantiated when assigning to a signature pointer or ref. */
+
+ if (type == NULL_TREE
+ && (RIDBIT_SETP (RID_SIGNED, specbits)
+ || RIDBIT_SETP (RID_UNSIGNED, specbits)
+ || RIDBIT_SETP (RID_LONG, specbits)
+ || RIDBIT_SETP (RID_SHORT, specbits)))
+ {
+ /* These imply 'int'. */
+ type = integer_type_node;
+ explicit_int = 1;
+ }
+
+ if (type == NULL_TREE)
+ {
+ explicit_int = -1;
+ if (return_type == return_dtor)
+ type = void_type_node;
+ else if (return_type == return_ctor)
+ type = TYPE_POINTER_TO (ctor_return_type);
+ else if (return_type == return_conversion)
+ type = ctor_return_type;
+ else if (current_class_type
+ && IS_SIGNATURE (current_class_type)
+ && (RIDBIT_SETP (RID_TYPEDEF, specbits)
+ || SIGNATURE_GROKKING_TYPEDEF (current_class_type))
+ && (decl_context == FIELD || decl_context == NORMAL))
+ {
+ explicit_int = 0;
+ opaque_typedef = 1;
+ type = copy_node (opaque_type_node);
+ }
+ /* access declaration */
+ else if (decl_context == FIELD && declarator
+ && TREE_CODE (declarator) == SCOPE_REF)
+ type = void_type_node;
+ else
+ {
+ if (funcdef_flag)
+ {
+ if (warn_return_type
+ && return_type == return_normal)
+ /* Save warning until we know what is really going on. */
+ warn_about_return_type = 1;
+ }
+ else if (RIDBIT_SETP (RID_TYPEDEF, specbits))
+ pedwarn ("ANSI C++ forbids typedef which does not specify a type");
+ else if (declspecs == NULL_TREE &&
+ (innermost_code != CALL_EXPR || pedantic))
+ cp_pedwarn ("ANSI C++ forbids declaration `%D' with no type or storage class",
+ dname);
+ type = integer_type_node;
+ }
+ }
+ else if (return_type == return_dtor)
+ {
+ error ("return type specification for destructor invalid");
+ type = void_type_node;
+ }
+ else if (return_type == return_ctor)
+ {
+ error ("return type specification for constructor invalid");
+ type = TYPE_POINTER_TO (ctor_return_type);
+ }
+ else if (return_type == return_conversion)
+ {
+ if (comp_target_types (type, ctor_return_type, 1) == 0)
+ cp_error ("operator `%T' declared to return `%T'",
+ ctor_return_type, type);
+ else
+ cp_pedwarn ("return type specified for `operator %T'",
+ ctor_return_type);
+
+ type = ctor_return_type;
+ }
+ /* Catch typedefs that only specify a type, like 'typedef int;'. */
+ else if (RIDBIT_SETP (RID_TYPEDEF, specbits) && declarator == NULL_TREE)
+ {
+ /* Template "this is a type" syntax; just ignore for now. */
+ if (processing_template_defn)
+ return void_type_node;
+ }
+
+ ctype = NULL_TREE;
+
+ /* Now process the modifiers that were specified
+ and check for invalid combinations. */
+
+ /* Long double is a special combination. */
+
+ if (RIDBIT_SETP (RID_LONG, specbits)
+ && TYPE_MAIN_VARIANT (type) == double_type_node)
+ {
+ RIDBIT_RESET (RID_LONG, specbits);
+ type = build_type_variant (long_double_type_node, TYPE_READONLY (type),
+ TYPE_VOLATILE (type));
+ }
+
+ /* Check all other uses of type modifiers. */
+
+ if (RIDBIT_SETP (RID_UNSIGNED, specbits)
+ || RIDBIT_SETP (RID_SIGNED, specbits)
+ || RIDBIT_SETP (RID_LONG, specbits)
+ || RIDBIT_SETP (RID_SHORT, specbits))
+ {
+ int ok = 0;
+
+ if (TREE_CODE (type) == REAL_TYPE)
+ error ("short, signed or unsigned invalid for `%s'", name);
+ else if (TREE_CODE (type) != INTEGER_TYPE || type == wchar_type_node)
+ error ("long, short, signed or unsigned invalid for `%s'", name);
+ else if (RIDBIT_SETP (RID_LONG, specbits)
+ && RIDBIT_SETP (RID_SHORT, specbits))
+ error ("long and short specified together for `%s'", name);
+ else if ((RIDBIT_SETP (RID_LONG, specbits)
+ || RIDBIT_SETP (RID_SHORT, specbits))
+ && explicit_char)
+ error ("long or short specified with char for `%s'", name);
+ else if ((RIDBIT_SETP (RID_LONG, specbits)
+ || RIDBIT_SETP (RID_SHORT, specbits))
+ && TREE_CODE (type) == REAL_TYPE)
+ error ("long or short specified with floating type for `%s'", name);
+ else if (RIDBIT_SETP (RID_SIGNED, specbits)
+ && RIDBIT_SETP (RID_UNSIGNED, specbits))
+ error ("signed and unsigned given together for `%s'", name);
+ else
+ {
+ ok = 1;
+ if (!explicit_int && !explicit_char && pedantic)
+ {
+ pedwarn ("long, short, signed or unsigned used invalidly for `%s'",
+ name);
+ if (flag_pedantic_errors)
+ ok = 0;
+ }
+ }
+
+ /* Discard the type modifiers if they are invalid. */
+ if (! ok)
+ {
+ RIDBIT_RESET (RID_UNSIGNED, specbits);
+ RIDBIT_RESET (RID_SIGNED, specbits);
+ RIDBIT_RESET (RID_LONG, specbits);
+ RIDBIT_RESET (RID_SHORT, specbits);
+ longlong = 0;
+ }
+ }
+
+ /* Decide whether an integer type is signed or not.
+ Optionally treat bitfields as signed by default. */
+ if (RIDBIT_SETP (RID_UNSIGNED, specbits)
+ /* Traditionally, all bitfields are unsigned. */
+ || (bitfield && flag_traditional)
+ || (bitfield && ! flag_signed_bitfields
+ && (explicit_int || explicit_char
+ /* A typedef for plain `int' without `signed'
+ can be controlled just like plain `int'. */
+ || ! (typedef_decl != NULL_TREE
+ && C_TYPEDEF_EXPLICITLY_SIGNED (typedef_decl)))
+ && TREE_CODE (type) != ENUMERAL_TYPE
+ && RIDBIT_NOTSETP (RID_SIGNED, specbits)))
+ {
+ if (longlong)
+ type = long_long_unsigned_type_node;
+ else if (RIDBIT_SETP (RID_LONG, specbits))
+ type = long_unsigned_type_node;
+ else if (RIDBIT_SETP (RID_SHORT, specbits))
+ type = short_unsigned_type_node;
+ else if (type == char_type_node)
+ type = unsigned_char_type_node;
+ else if (typedef_decl)
+ type = unsigned_type (type);
+ else
+ type = unsigned_type_node;
+ }
+ else if (RIDBIT_SETP (RID_SIGNED, specbits)
+ && type == char_type_node)
+ type = signed_char_type_node;
+ else if (longlong)
+ type = long_long_integer_type_node;
+ else if (RIDBIT_SETP (RID_LONG, specbits))
+ type = long_integer_type_node;
+ else if (RIDBIT_SETP (RID_SHORT, specbits))
+ type = short_integer_type_node;
+
+ /* Set CONSTP if this declaration is `const', whether by
+ explicit specification or via a typedef.
+ Likewise for VOLATILEP. */
+
+ constp = !! RIDBIT_SETP (RID_CONST, specbits) + TYPE_READONLY (type);
+ volatilep = !! RIDBIT_SETP (RID_VOLATILE, specbits) + TYPE_VOLATILE (type);
+ staticp = 0;
+ inlinep = !! RIDBIT_SETP (RID_INLINE, specbits);
+ if (constp > 1)
+ warning ("duplicate `const'");
+ if (volatilep > 1)
+ warning ("duplicate `volatile'");
+ virtualp = RIDBIT_SETP (RID_VIRTUAL, specbits);
+
+ if (RIDBIT_SETP (RID_STATIC, specbits))
+ staticp = 1 + (decl_context == FIELD);
+
+ if (virtualp && staticp == 2)
+ {
+ cp_error ("member `%D' cannot be declared both virtual and static",
+ dname);
+ staticp = 0;
+ }
+ friendp = RIDBIT_SETP (RID_FRIEND, specbits);
+ RIDBIT_RESET (RID_VIRTUAL, specbits);
+ RIDBIT_RESET (RID_FRIEND, specbits);
+
+ if (RIDBIT_SETP (RID_MUTABLE, specbits))
+ {
+ if (decl_context == PARM)
+ {
+ error ("non-member `%s' cannot be declared mutable", name);
+ RIDBIT_RESET (RID_MUTABLE, specbits);
+ }
+ else if (friendp || decl_context == TYPENAME)
+ {
+ error ("non-object member `%s' cannot be declared mutable", name);
+ RIDBIT_RESET (RID_MUTABLE, specbits);
+ }
+ else if (staticp)
+ {
+ error ("static `%s' cannot be declared mutable", name);
+ RIDBIT_RESET (RID_MUTABLE, specbits);
+ }
+#if 0
+ if (RIDBIT_SETP (RID_TYPEDEF, specbits))
+ {
+ error ("non-object member `%s' cannot be declared mutable", name);
+ RIDBIT_RESET (RID_MUTABLE, specbits);
+ }
+ /* Because local typedefs are parsed twice, we don't want this
+ message here. */
+ else if (decl_context != FIELD)
+ {
+ error ("non-member `%s' cannot be declared mutable", name);
+ RIDBIT_RESET (RID_MUTABLE, specbits);
+ }
+#endif
+ }
+
+ /* Warn if two storage classes are given. Default to `auto'. */
+
+ if (RIDBIT_ANY_SET (specbits))
+ {
+ if (RIDBIT_SETP (RID_STATIC, specbits)) nclasses++;
+ if (RIDBIT_SETP (RID_EXTERN, specbits)) nclasses++;
+ if (decl_context == PARM && nclasses > 0)
+ error ("storage class specifiers invalid in parameter declarations");
+ if (RIDBIT_SETP (RID_TYPEDEF, specbits))
+ {
+ if (decl_context == PARM)
+ error ("typedef declaration invalid in parameter declaration");
+ nclasses++;
+ }
+ if (RIDBIT_SETP (RID_AUTO, specbits)) nclasses++;
+ if (RIDBIT_SETP (RID_REGISTER, specbits)) nclasses++;
+ }
+
+ /* Give error if `virtual' is used outside of class declaration. */
+ if (virtualp && current_class_name == NULL_TREE)
+ {
+ error ("virtual outside class declaration");
+ virtualp = 0;
+ }
+ if (current_class_name == NULL_TREE && RIDBIT_SETP (RID_MUTABLE, specbits))
+ {
+ error ("only members can be declared mutable");
+ RIDBIT_RESET (RID_MUTABLE, specbits);
+ }
+
+ /* Static anonymous unions are dealt with here. */
+ if (staticp && decl_context == TYPENAME
+ && TREE_CODE (declspecs) == TREE_LIST
+ && TREE_CODE (TREE_VALUE (declspecs)) == UNION_TYPE
+ && ANON_AGGRNAME_P (TYPE_IDENTIFIER (TREE_VALUE (declspecs))))
+ decl_context = FIELD;
+
+ /* Give error if `const,' `volatile,' `inline,' `friend,' or `virtual'
+ is used in a signature member function declaration. */
+ if (decl_context == FIELD
+ && IS_SIGNATURE (current_class_type)
+ && RIDBIT_NOTSETP(RID_TYPEDEF, specbits)
+ && !SIGNATURE_GROKKING_TYPEDEF (current_class_type))
+ {
+ if (constp)
+ {
+ error ("`const' specified for signature member function `%s'", name);
+ constp = 0;
+ }
+ if (volatilep)
+ {
+ error ("`volatile' specified for signature member function `%s'",
+ name);
+ volatilep = 0;
+ }
+ if (inlinep)
+ {
+ error ("`inline' specified for signature member function `%s'", name);
+ /* Later, we'll make signature member functions inline. */
+ inlinep = 0;
+ }
+ if (friendp)
+ {
+ error ("`friend' declaration in signature definition");
+ friendp = 0;
+ }
+ if (virtualp)
+ {
+ error ("`virtual' specified for signature member function `%s'",
+ name);
+ /* Later, we'll make signature member functions virtual. */
+ virtualp = 0;
+ }
+ }
+
+ /* Warn about storage classes that are invalid for certain
+ kinds of declarations (parameters, typenames, etc.). */
+
+ if (nclasses > 1)
+ error ("multiple storage classes in declaration of `%s'", name);
+ else if (decl_context != NORMAL && nclasses > 0)
+ {
+ if (decl_context == PARM
+ && (RIDBIT_SETP (RID_REGISTER, specbits)
+ || RIDBIT_SETP (RID_AUTO, specbits)))
+ ;
+ else if (decl_context == FIELD
+ && RIDBIT_SETP (RID_TYPEDEF, specbits))
+ {
+ /* Processing a typedef declaration nested within a class type
+ definition. */
+ register tree scanner;
+ register tree previous_declspec;
+ tree loc_typedecl;
+
+ if (initialized)
+ error ("typedef declaration includes an initializer");
+
+ /* To process a class-local typedef declaration, we descend down
+ the chain of declspecs looking for the `typedef' spec. When
+ we find it, we replace it with `static', and then recursively
+ call `grokdeclarator' with the original declarator and with
+ the newly adjusted declspecs. This call should return a
+ FIELD_DECL node with the TREE_TYPE (and other parts) set
+ appropriately. We can then just change the TREE_CODE on that
+ from FIELD_DECL to TYPE_DECL and we're done. */
+
+ for (previous_declspec = NULL_TREE, scanner = declspecs;
+ scanner;
+ previous_declspec = scanner, scanner = TREE_CHAIN (scanner))
+ {
+ if (TREE_VALUE (scanner) == ridpointers[(int) RID_TYPEDEF])
+ break;
+ }
+
+ if (scanner == IDENTIFIER_AS_LIST (ridpointers [(int) RID_TYPEDEF]))
+ {
+ if (previous_declspec)
+ TREE_CHAIN (previous_declspec)
+ = IDENTIFIER_AS_LIST (ridpointers [(int) RID_STATIC]);
+ else
+ declspecs
+ = IDENTIFIER_AS_LIST (ridpointers [(int) RID_STATIC]);
+ }
+ else
+ TREE_VALUE (scanner) = ridpointers[(int) RID_STATIC];
+
+ /* In the recursive call to grokdeclarator we need to know
+ whether we are working on a signature-local typedef. */
+ if (IS_SIGNATURE (current_class_type))
+ SIGNATURE_GROKKING_TYPEDEF (current_class_type) = 1;
+
+ loc_typedecl =
+ grokdeclarator (declarator, declspecs, FIELD, 0, NULL_TREE);
+
+ if (loc_typedecl != error_mark_node)
+ {
+ register int i = sizeof (struct lang_decl_flags) / sizeof (int);
+ register int *pi;
+
+ TREE_SET_CODE (loc_typedecl, TYPE_DECL);
+
+ pi = (int *) permalloc (sizeof (struct lang_decl_flags));
+ while (i > 0)
+ pi[--i] = 0;
+ DECL_LANG_SPECIFIC (loc_typedecl) = (struct lang_decl *) pi;
+ }
+
+ if (IS_SIGNATURE (current_class_type))
+ {
+ SIGNATURE_GROKKING_TYPEDEF (current_class_type) = 0;
+ if (loc_typedecl != error_mark_node && opaque_typedef)
+ SIGNATURE_HAS_OPAQUE_TYPEDECLS (current_class_type) = 1;
+ }
+
+ return loc_typedecl;
+ }
+ else if (decl_context == FIELD
+ && (! IS_SIGNATURE (current_class_type)
+ || SIGNATURE_GROKKING_TYPEDEF (current_class_type))
+ /* C++ allows static class elements */
+ && RIDBIT_SETP (RID_STATIC, specbits))
+ /* C++ also allows inlines and signed and unsigned elements,
+ but in those cases we don't come in here. */
+ ;
+ else
+ {
+ if (decl_context == FIELD)
+ {
+ tree tmp = TREE_OPERAND (declarator, 0);
+ register int op = IDENTIFIER_OPNAME_P (tmp);
+ error ("storage class specified for %s `%s'",
+ IS_SIGNATURE (current_class_type)
+ ? (op
+ ? "signature member operator"
+ : "signature member function")
+ : (op ? "member operator" : "structure field"),
+ op ? operator_name_string (tmp) : name);
+ }
+ else
+ error ((decl_context == PARM
+ ? "storage class specified for parameter `%s'"
+ : "storage class specified for typename"), name);
+ RIDBIT_RESET (RID_REGISTER, specbits);
+ RIDBIT_RESET (RID_AUTO, specbits);
+ RIDBIT_RESET (RID_EXTERN, specbits);
+
+ if (decl_context == FIELD && IS_SIGNATURE (current_class_type))
+ {
+ RIDBIT_RESET (RID_STATIC, specbits);
+ staticp = 0;
+ }
+ }
+ }
+ else if (RIDBIT_SETP (RID_EXTERN, specbits) && initialized && !funcdef_flag)
+ {
+ if (current_binding_level == global_binding_level)
+ {
+ /* It's common practice (and completely legal) to have a const
+ be initialized and declared extern. */
+ if (! constp)
+ warning ("`%s' initialized and declared `extern'", name);
+ }
+ else
+ error ("`%s' has both `extern' and initializer", name);
+ }
+ else if (RIDBIT_SETP (RID_EXTERN, specbits) && funcdef_flag
+ && current_binding_level != global_binding_level)
+ error ("nested function `%s' declared `extern'", name);
+ else if (current_binding_level == global_binding_level)
+ {
+ if (RIDBIT_SETP (RID_AUTO, specbits))
+ error ("top-level declaration of `%s' specifies `auto'", name);
+#if 0
+ if (RIDBIT_SETP (RID_REGISTER, specbits))
+ error ("top-level declaration of `%s' specifies `register'", name);
+#endif
+#if 0
+ /* I'm not sure under what circumstances we should turn
+ on the extern bit, and under what circumstances we should
+ warn if other bits are turned on. */
+ if (decl_context == NORMAL
+ && RIDBIT_NOSETP (RID_EXTERN, specbits)
+ && ! root_lang_context_p ())
+ {
+ RIDBIT_SET (RID_EXTERN, specbits);
+ }
+#endif
+ }
+
+ /* Now figure out the structure of the declarator proper.
+ Descend through it, creating more complex types, until we reach
+ the declared identifier (or NULL_TREE, in an absolute declarator). */
+
+ while (declarator && TREE_CODE (declarator) != IDENTIFIER_NODE)
+ {
+ /* Each level of DECLARATOR is either an ARRAY_REF (for ...[..]),
+ an INDIRECT_REF (for *...),
+ a CALL_EXPR (for ...(...)),
+ an identifier (for the name being declared)
+ or a null pointer (for the place in an absolute declarator
+ where the name was omitted).
+ For the last two cases, we have just exited the loop.
+
+ For C++ it could also be
+ a SCOPE_REF (for class :: ...). In this case, we have converted
+ sensible names to types, and those are the values we use to
+ qualify the member name.
+ an ADDR_EXPR (for &...),
+ a BIT_NOT_EXPR (for destructors)
+
+ At this point, TYPE is the type of elements of an array,
+ or for a function to return, or for a pointer to point to.
+ After this sequence of ifs, TYPE is the type of the
+ array or function or pointer, and DECLARATOR has had its
+ outermost layer removed. */
+
+ if (TREE_CODE (type) == ERROR_MARK)
+ {
+ if (TREE_CODE (declarator) == SCOPE_REF)
+ declarator = TREE_OPERAND (declarator, 1);
+ else
+ declarator = TREE_OPERAND (declarator, 0);
+ continue;
+ }
+ if (quals != NULL_TREE
+ && (declarator == NULL_TREE
+ || TREE_CODE (declarator) != SCOPE_REF))
+ {
+ if (ctype == NULL_TREE && TREE_CODE (type) == METHOD_TYPE)
+ ctype = TYPE_METHOD_BASETYPE (type);
+ if (ctype != NULL_TREE)
+ {
+#if 0 /* not yet, should get fixed properly later */
+ tree dummy = make_type_decl (NULL_TREE, type);
+#else
+ tree dummy = build_decl (TYPE_DECL, NULL_TREE, type);
+#endif
+ ctype = grok_method_quals (ctype, dummy, quals);
+ type = TREE_TYPE (dummy);
+ quals = NULL_TREE;
+ }
+ }
+ switch (TREE_CODE (declarator))
+ {
+ case ARRAY_REF:
+ {
+ register tree itype = NULL_TREE;
+ register tree size = TREE_OPERAND (declarator, 1);
+
+ declarator = TREE_OPERAND (declarator, 0);
+
+ /* Check for some types that there cannot be arrays of. */
+
+ if (TYPE_MAIN_VARIANT (type) == void_type_node)
+ {
+ cp_error ("declaration of `%D' as array of voids", dname);
+ type = error_mark_node;
+ }
+
+ if (TREE_CODE (type) == FUNCTION_TYPE)
+ {
+ cp_error ("declaration of `%D' as array of functions", dname);
+ type = error_mark_node;
+ }
+
+ /* ARM $8.4.3: Since you can't have a pointer to a reference,
+ you can't have arrays of references. If we allowed them,
+ then we'd be saying x[i] is legal for an array x, but
+ then you'd have to ask: what does `*(x + i)' mean? */
+ if (TREE_CODE (type) == REFERENCE_TYPE)
+ {
+ if (decl_context == TYPENAME)
+ cp_error ("cannot make arrays of references");
+ else
+ cp_error ("declaration of `%D' as array of references",
+ dname);
+ type = error_mark_node;
+ }
+
+ if (TREE_CODE (type) == OFFSET_TYPE)
+ {
+ cp_error ("declaration of `%D' as array of data members",
+ dname);
+ type = error_mark_node;
+ }
+
+ if (TREE_CODE (type) == METHOD_TYPE)
+ {
+ cp_error ("declaration of `%D' as array of function members",
+ dname);
+ type = error_mark_node;
+ }
+
+ if (size == error_mark_node)
+ type = error_mark_node;
+
+ if (type == error_mark_node)
+ continue;
+
+ if (size)
+ {
+ /* Must suspend_momentary here because the index
+ type may need to live until the end of the function.
+ For example, it is used in the declaration of a
+ variable which requires destructing at the end of
+ the function; then build_vec_delete will need this
+ value. */
+ int yes = suspend_momentary ();
+ /* might be a cast */
+ if (TREE_CODE (size) == NOP_EXPR
+ && TREE_TYPE (size) == TREE_TYPE (TREE_OPERAND (size, 0)))
+ size = TREE_OPERAND (size, 0);
+
+ /* If this is a template parameter, it'll be constant, but
+ we don't know what the value is yet. */
+ if (TREE_CODE (size) == TEMPLATE_CONST_PARM)
+ goto dont_grok_size;
+
+ if (TREE_CODE (TREE_TYPE (size)) != INTEGER_TYPE
+ && TREE_CODE (TREE_TYPE (size)) != ENUMERAL_TYPE)
+ {
+ cp_error ("size of array `%D' has non-integer type",
+ dname);
+ size = integer_one_node;
+ }
+ if (TREE_READONLY_DECL_P (size))
+ size = decl_constant_value (size);
+ if (flag_ansi && integer_zerop (size))
+ cp_pedwarn ("ANSI C++ forbids zero-size array `%D'", dname);
+ if (TREE_CONSTANT (size))
+ {
+ constant_expression_warning (size);
+ if (INT_CST_LT (size, integer_zero_node))
+ {
+ cp_error ("size of array `%D' is negative", dname);
+ size = integer_one_node;
+ }
+ itype = build_index_type (size_binop (MINUS_EXPR, size,
+ integer_one_node));
+ }
+ else
+ {
+ if (flag_ansi)
+ {
+ if (dname)
+ cp_pedwarn ("ANSI C++ forbids variable-size array `%D'",
+ dname);
+ else
+ cp_pedwarn ("ANSI C++ forbids variable-size array");
+ }
+ dont_grok_size:
+ itype =
+ build_binary_op (MINUS_EXPR, size, integer_one_node, 1);
+ /* Make sure the array size remains visibly nonconstant
+ even if it is (eg) a const variable with known value. */
+ size_varies = 1;
+ itype = variable_size (itype);
+ itype = build_index_type (itype);
+ }
+ resume_momentary (yes);
+ }
+
+ /* Build the array type itself, then merge any constancy or
+ volatility into the target type. We must do it in this order
+ to ensure that the TYPE_MAIN_VARIANT field of the array type
+ is set correctly. */
+
+ type = build_cplus_array_type (type, itype);
+ if (constp || volatilep)
+ type = c_build_type_variant (type, constp, volatilep);
+
+ ctype = NULL_TREE;
+ }
+ break;
+
+ case CALL_EXPR:
+ {
+ tree arg_types;
+
+ /* Declaring a function type.
+ Make sure we have a valid type for the function to return. */
+#if 0
+ /* Is this an error? Should they be merged into TYPE here? */
+ if (pedantic && (constp || volatilep))
+ pedwarn ("function declared to return const or volatile result");
+#else
+ /* Merge any constancy or volatility into the function return
+ type. */
+
+ if (constp || volatilep)
+ {
+ type = c_build_type_variant (type, constp, volatilep);
+ if (IS_AGGR_TYPE (type))
+ build_pointer_type (type);
+ constp = 0;
+ volatilep = 0;
+ }
+#endif
+
+ /* Warn about some types functions can't return. */
+
+ if (TREE_CODE (type) == FUNCTION_TYPE)
+ {
+ error ("`%s' declared as function returning a function", name);
+ type = integer_type_node;
+ }
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ error ("`%s' declared as function returning an array", name);
+ type = integer_type_node;
+ }
+
+ if (ctype == NULL_TREE
+ && decl_context == FIELD
+ && (friendp == 0 || dname == current_class_name))
+ ctype = current_class_type;
+
+ if (ctype && return_type == return_conversion)
+ TYPE_HAS_CONVERSION (ctype) = 1;
+ if (ctype && constructor_name (ctype) == dname)
+ {
+ /* We are within a class's scope. If our declarator name
+ is the same as the class name, and we are defining
+ a function, then it is a constructor/destructor, and
+ therefore returns a void type. */
+
+ if (flags == DTOR_FLAG)
+ {
+ /* ANSI C++ June 5 1992 WP 12.4.1. A destructor may
+ not be declared const or volatile. A destructor
+ may not be static. */
+ if (staticp == 2)
+ error ("destructor cannot be static member function");
+ if (TYPE_READONLY (type))
+ {
+ error ("destructors cannot be declared `const'");
+ return void_type_node;
+ }
+ if (TYPE_VOLATILE (type))
+ {
+ error ("destructors cannot be declared `volatile'");
+ return void_type_node;
+ }
+ if (decl_context == FIELD)
+ {
+ if (! member_function_or_else (ctype, current_class_type,
+ "destructor for alien class `%s' cannot be a member"))
+ return void_type_node;
+ }
+ }
+ else /* it's a constructor. */
+ {
+ /* ANSI C++ June 5 1992 WP 12.1.2. A constructor may
+ not be declared const or volatile. A constructor may
+ not be virtual. A constructor may not be static. */
+ if (staticp == 2)
+ error ("constructor cannot be static member function");
+ if (virtualp)
+ {
+ pedwarn ("constructors cannot be declared virtual");
+ virtualp = 0;
+ }
+ if (TYPE_READONLY (type))
+ {
+ error ("constructors cannot be declared `const'");
+ return void_type_node;
+ }
+ if (TYPE_VOLATILE (type))
+ {
+ error ("constructors cannot be declared `volatile'");
+ return void_type_node;
+ }
+ {
+ RID_BIT_TYPE tmp_bits;
+ bcopy ((void*)&specbits, (void*)&tmp_bits, sizeof(RID_BIT_TYPE));
+ RIDBIT_RESET (RID_INLINE, tmp_bits);
+ RIDBIT_RESET (RID_STATIC, tmp_bits);
+ if (RIDBIT_ANY_SET (tmp_bits))
+ error ("return value type specifier for constructor ignored");
+ }
+ type = TYPE_POINTER_TO (ctype);
+ if (decl_context == FIELD &&
+ IS_SIGNATURE (current_class_type))
+ {
+ error ("constructor not allowed in signature");
+ return void_type_node;
+ }
+ else if (decl_context == FIELD)
+ {
+ if (! member_function_or_else (ctype, current_class_type,
+ "constructor for alien class `%s' cannot be member"))
+ return void_type_node;
+ TYPE_HAS_CONSTRUCTOR (ctype) = 1;
+ if (return_type != return_ctor)
+ return NULL_TREE;
+ }
+ }
+ if (decl_context == FIELD)
+ staticp = 0;
+ }
+ else if (friendp && virtualp)
+ {
+ /* Cannot be both friend and virtual. */
+ error ("virtual functions cannot be friends");
+ RIDBIT_RESET (RID_FRIEND, specbits);
+ friendp = 0;
+ }
+
+ if (decl_context == NORMAL && friendp)
+ error ("friend declaration not in class definition");
+
+ /* Pick up type qualifiers which should be applied to `this'. */
+ quals = TREE_OPERAND (declarator, 2);
+
+ /* Traditionally, declaring return type float means double. */
+
+ if (flag_traditional
+ && TYPE_MAIN_VARIANT (type) == float_type_node)
+ {
+ type = build_type_variant (double_type_node,
+ TYPE_READONLY (type),
+ TYPE_VOLATILE (type));
+ }
+
+ /* Construct the function type and go to the next
+ inner layer of declarator. */
+
+ {
+ int funcdef_p;
+ tree inner_parms = TREE_OPERAND (declarator, 1);
+ tree inner_decl = TREE_OPERAND (declarator, 0);
+
+ declarator = TREE_OPERAND (declarator, 0);
+
+ if (inner_decl && TREE_CODE (inner_decl) == SCOPE_REF)
+ inner_decl = TREE_OPERAND (inner_decl, 1);
+
+ /* Say it's a definition only for the CALL_EXPR
+ closest to the identifier. */
+ funcdef_p =
+ (inner_decl && TREE_CODE (inner_decl) == IDENTIFIER_NODE)
+ ? funcdef_flag : 0;
+
+ /* FIXME: This is where default args should be fully
+ processed. */
+
+ arg_types = grokparms (inner_parms, funcdef_p);
+ }
+
+ if (declarator)
+ {
+ /* Get past destructors, etc.
+ We know we have one because FLAGS will be non-zero.
+
+ Complain about improper parameter lists here. */
+ if (TREE_CODE (declarator) == BIT_NOT_EXPR)
+ {
+ declarator = TREE_OPERAND (declarator, 0);
+
+ if (strict_prototype == 0 && arg_types == NULL_TREE)
+ arg_types = void_list_node;
+ else if (arg_types == NULL_TREE
+ || arg_types != void_list_node)
+ {
+ error ("destructors cannot be specified with parameters");
+ arg_types = void_list_node;
+ }
+ }
+ }
+
+ /* ANSI seems to say that `const int foo ();'
+ does not make the function foo const. */
+ type = build_function_type (type,
+ flag_traditional ? 0 : arg_types);
+ }
+ break;
+
+ case ADDR_EXPR:
+ case INDIRECT_REF:
+ /* Filter out pointers-to-references and references-to-references.
+ We can get these if a TYPE_DECL is used. */
+
+ if (TREE_CODE (type) == REFERENCE_TYPE)
+ {
+ error ("cannot declare %s to references",
+ TREE_CODE (declarator) == ADDR_EXPR
+ ? "references" : "pointers");
+ declarator = TREE_OPERAND (declarator, 0);
+ continue;
+ }
+
+ /* Merge any constancy or volatility into the target type
+ for the pointer. */
+
+ if (constp || volatilep)
+ {
+ /* A const or volatile signature pointer/reference is
+ pointing to a const or volatile object, i.e., the
+ `optr' is const or volatile, respectively, not the
+ signature pointer/reference itself. */
+ if (! IS_SIGNATURE (type))
+ {
+ type = c_build_type_variant (type, constp, volatilep);
+ if (IS_AGGR_TYPE (type))
+ build_pointer_type (type);
+ constp = 0;
+ volatilep = 0;
+ }
+ }
+
+ if (IS_SIGNATURE (type))
+ {
+ if (TREE_CODE (declarator) == ADDR_EXPR)
+ {
+ if (CLASSTYPE_METHOD_VEC (type) == NULL_TREE
+ && TYPE_SIZE (type))
+ cp_warning ("empty signature `%T' used in signature reference declaration",
+ type);
+#if 0
+ type = build_signature_reference_type (type,
+ constp, volatilep);
+#else
+ sorry ("signature reference");
+ return NULL_TREE;
+#endif
+ }
+ else
+ {
+ if (CLASSTYPE_METHOD_VEC (type) == NULL_TREE
+ && TYPE_SIZE (type))
+ cp_warning ("empty signature `%T' used in signature pointer declaration",
+ type);
+ type = build_signature_pointer_type (type,
+ constp, volatilep);
+ }
+ constp = 0;
+ volatilep = 0;
+ }
+ else if (TREE_CODE (declarator) == ADDR_EXPR)
+ {
+ if (TREE_CODE (type) == FUNCTION_TYPE)
+ {
+ error ("cannot declare references to functions; use pointer to function instead");
+ type = build_pointer_type (type);
+ }
+ else
+ {
+ if (TYPE_MAIN_VARIANT (type) == void_type_node)
+ error ("invalid type: `void &'");
+ else
+ type = build_reference_type (type);
+ }
+ }
+ else if (TREE_CODE (type) == METHOD_TYPE)
+ {
+ type = build_ptrmemfunc_type (build_pointer_type (type));
+ }
+ else
+ type = build_pointer_type (type);
+
+ /* Process a list of type modifier keywords (such as
+ const or volatile) that were given inside the `*' or `&'. */
+
+ if (TREE_TYPE (declarator))
+ {
+ register tree typemodlist;
+ int erred = 0;
+ for (typemodlist = TREE_TYPE (declarator); typemodlist;
+ typemodlist = TREE_CHAIN (typemodlist))
+ {
+ if (TREE_VALUE (typemodlist) == ridpointers[(int) RID_CONST])
+ constp++;
+ else if (TREE_VALUE (typemodlist) == ridpointers[(int) RID_VOLATILE])
+ volatilep++;
+ else if (!erred)
+ {
+ erred = 1;
+ error ("invalid type modifier within %s declarator",
+ TREE_CODE (declarator) == ADDR_EXPR
+ ? "reference" : "pointer");
+ }
+ }
+ if (constp > 1)
+ pedwarn ("duplicate `const'");
+ if (volatilep > 1)
+ pedwarn ("duplicate `volatile'");
+ if (TREE_CODE (declarator) == ADDR_EXPR
+ && (constp || volatilep))
+ {
+ if (constp)
+ warning ("discarding `const' applied to a reference");
+ if (volatilep)
+ warning ("discarding `volatile' applied to a reference");
+ constp = volatilep = 0;
+ }
+ }
+ declarator = TREE_OPERAND (declarator, 0);
+ ctype = NULL_TREE;
+ break;
+
+ case SCOPE_REF:
+ {
+ /* We have converted type names to NULL_TREE if the
+ name was bogus, or to a _TYPE node, if not.
+
+ The variable CTYPE holds the type we will ultimately
+ resolve to. The code here just needs to build
+ up appropriate member types. */
+ tree sname = TREE_OPERAND (declarator, 1);
+ /* Destructors can have their visibilities changed as well. */
+ if (TREE_CODE (sname) == BIT_NOT_EXPR)
+ sname = TREE_OPERAND (sname, 0);
+
+ if (TREE_COMPLEXITY (declarator) == 0)
+ /* This needs to be here, in case we are called
+ multiple times. */ ;
+ else if (friendp && (TREE_COMPLEXITY (declarator) < 2))
+ /* don't fall out into global scope. Hides real bug? --eichin */ ;
+ else if (TREE_COMPLEXITY (declarator) == current_class_depth)
+ {
+ /* This pop_nested_class corresponds to the
+ push_nested_class used to push into class scope for
+ parsing the argument list of a function decl, in
+ qualified_id. */
+ pop_nested_class (1);
+ TREE_COMPLEXITY (declarator) = current_class_depth;
+ }
+ else
+ my_friendly_abort (16);
+
+ if (TREE_OPERAND (declarator, 0) == NULL_TREE)
+ {
+ /* We had a reference to a global decl, or
+ perhaps we were given a non-aggregate typedef,
+ in which case we cleared this out, and should just
+ keep going as though it wasn't there. */
+ declarator = sname;
+ continue;
+ }
+ ctype = TREE_OPERAND (declarator, 0);
+
+ if (sname == NULL_TREE)
+ goto done_scoping;
+
+ if (TREE_CODE (sname) == IDENTIFIER_NODE)
+ {
+ /* This is the `standard' use of the scoping operator:
+ basetype :: member . */
+
+ if (TREE_CODE (type) == FUNCTION_TYPE)
+ {
+ if (current_class_type == NULL_TREE
+ || TYPE_MAIN_VARIANT (ctype) == current_class_type
+ || friendp)
+ type = build_cplus_method_type (build_type_variant (ctype, constp, volatilep),
+ TREE_TYPE (type), TYPE_ARG_TYPES (type));
+ else
+ {
+ cp_error ("cannot declare member function `%T::%s' within `%T'",
+ ctype, name, current_class_type);
+ return void_type_node;
+ }
+ }
+ else if (TYPE_MAIN_VARIANT (ctype) == current_class_type)
+ {
+ if (extra_warnings)
+ cp_warning ("redundant qualification `%T' on member `%s' ignored",
+ ctype, name);
+ type = build_offset_type (ctype, type);
+ }
+ else if (TYPE_SIZE (ctype) != NULL_TREE
+ || (RIDBIT_SETP (RID_TYPEDEF, specbits)))
+ {
+ tree t;
+ /* have to move this code elsewhere in this function.
+ this code is used for i.e., typedef int A::M; M *pm; */
+
+ if (explicit_int == -1 && decl_context == FIELD
+ && funcdef_flag == 0)
+ {
+ /* The code in here should only be used to build
+ stuff that will be grokked as access decls. */
+ t = lookup_field (ctype, sname, 0, 0);
+ if (t)
+ {
+ t = build_lang_field_decl (FIELD_DECL, build_nt (SCOPE_REF, ctype, t), type);
+ DECL_INITIAL (t) = init;
+ return t;
+ }
+ /* No such field, try member functions. */
+ t = lookup_fnfields (TYPE_BINFO (ctype), sname, 0);
+ if (t)
+ {
+ if (flags == DTOR_FLAG)
+ t = TREE_VALUE (t);
+ else if (CLASSTYPE_METHOD_VEC (ctype)
+ && TREE_VALUE (t) == TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (ctype), 0))
+ {
+ /* Don't include destructor with constructors. */
+ t = DECL_CHAIN (TREE_VALUE (t));
+ if (t == NULL_TREE)
+ error ("class `%s' does not have any constructors", IDENTIFIER_POINTER (sname));
+ t = build_tree_list (NULL_TREE, t);
+ }
+ t = build_lang_field_decl (FIELD_DECL, build_nt (SCOPE_REF, ctype, t), type);
+ DECL_INITIAL (t) = init;
+ return t;
+ }
+
+ cp_error
+ ("field `%D' is not a member of structure `%T'",
+ sname, ctype);
+ }
+
+ if (current_class_type)
+ {
+ if (TYPE_MAIN_VARIANT (ctype) != current_class_type)
+ {
+ cp_error ("cannot declare member `%T::%s' within `%T'",
+ ctype, name, current_class_type);
+ return void_type_node;
+ }
+ else if (extra_warnings)
+ cp_warning ("extra qualification `%T' on member `%s' ignored",
+ ctype, name);
+ }
+ type = build_offset_type (ctype, type);
+ }
+ else if (uses_template_parms (ctype))
+ {
+ enum tree_code c;
+ if (TREE_CODE (type) == FUNCTION_TYPE)
+ {
+ type = build_cplus_method_type (build_type_variant (ctype, constp, volatilep),
+ TREE_TYPE (type),
+ TYPE_ARG_TYPES (type));
+ c = FUNCTION_DECL;
+ }
+ }
+ else
+ {
+ cp_error ("structure `%T' not yet defined", ctype);
+ return error_mark_node;
+ }
+
+ declarator = sname;
+ }
+ else if (TREE_CODE (sname) == SCOPE_REF)
+ my_friendly_abort (17);
+ else
+ {
+ done_scoping:
+ declarator = TREE_OPERAND (declarator, 1);
+ if (declarator && TREE_CODE (declarator) == CALL_EXPR)
+ /* In this case, we will deal with it later. */
+ ;
+ else
+ {
+ if (TREE_CODE (type) == FUNCTION_TYPE)
+ type = build_cplus_method_type (build_type_variant (ctype, constp, volatilep), TREE_TYPE (type), TYPE_ARG_TYPES (type));
+ else
+ type = build_offset_type (ctype, type);
+ }
+ }
+ }
+ break;
+
+ case BIT_NOT_EXPR:
+ declarator = TREE_OPERAND (declarator, 0);
+ break;
+
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ case ENUMERAL_TYPE:
+ declarator = NULL_TREE;
+ break;
+
+ case ERROR_MARK:
+ declarator = NULL_TREE;
+ break;
+
+ default:
+ my_friendly_abort (158);
+ }
+ }
+
+ /* Now TYPE has the actual type. */
+
+ /* If this is declaring a typedef name, return a TYPE_DECL. */
+
+ if (RIDBIT_SETP (RID_TYPEDEF, specbits))
+ {
+ tree decl;
+
+ /* Note that the grammar rejects storage classes
+ in typenames, fields or parameters. */
+ if (constp || volatilep)
+ type = c_build_type_variant (type, constp, volatilep);
+
+ /* If the user declares "struct {...} foo" then `foo' will have
+ an anonymous name. Fill that name in now. Nothing can
+ refer to it, so nothing needs know about the name change.
+ The TYPE_NAME field was filled in by build_struct_xref. */
+ if (type != error_mark_node
+ && TYPE_NAME (type)
+ && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
+ && ANON_AGGRNAME_P (TYPE_IDENTIFIER (type)))
+ {
+ /* replace the anonymous name with the real name everywhere. */
+ lookup_tag_reverse (type, declarator);
+ TYPE_IDENTIFIER (type) = declarator;
+
+ if (TYPE_LANG_SPECIFIC (type))
+ TYPE_WAS_ANONYMOUS (type) = 1;
+
+ {
+ tree d = TYPE_NAME (type), c = DECL_CONTEXT (d);
+
+ if (!c)
+ set_nested_typename (d, 0, declarator, type);
+ else if (TREE_CODE (c) == FUNCTION_DECL)
+ set_nested_typename (d, DECL_ASSEMBLER_NAME (c),
+ declarator, type);
+ else
+ set_nested_typename (d, TYPE_NESTED_NAME (c), declarator, type);
+ }
+ }
+
+#if 0 /* not yet, should get fixed properly later */
+ decl = make_type_decl (declarator, type);
+#else
+ decl = build_decl (TYPE_DECL, declarator, type);
+#endif
+ if (TREE_CODE (type) == OFFSET_TYPE || TREE_CODE (type) == METHOD_TYPE)
+ {
+ cp_error_at ("typedef name may not be class-qualified", decl);
+ return NULL_TREE;
+ }
+ else if (quals)
+ {
+ if (ctype == NULL_TREE)
+ {
+ if (TREE_CODE (type) != METHOD_TYPE)
+ cp_error_at ("invalid type qualifier for non-method type", decl);
+ else
+ ctype = TYPE_METHOD_BASETYPE (type);
+ }
+ if (ctype != NULL_TREE)
+ grok_method_quals (ctype, decl, quals);
+ }
+
+ if (RIDBIT_SETP (RID_SIGNED, specbits)
+ || (typedef_decl && C_TYPEDEF_EXPLICITLY_SIGNED (typedef_decl)))
+ C_TYPEDEF_EXPLICITLY_SIGNED (decl) = 1;
+
+ if (RIDBIT_SETP (RID_MUTABLE, specbits))
+ {
+ error ("non-object member `%s' cannot be declared mutable", name);
+ }
+
+ return decl;
+ }
+
+ /* Detect the case of an array type of unspecified size
+ which came, as such, direct from a typedef name.
+ We must copy the type, so that each identifier gets
+ a distinct type, so that each identifier's size can be
+ controlled separately by its own initializer. */
+
+ if (type == typedef_type && TREE_CODE (type) == ARRAY_TYPE
+ && TYPE_DOMAIN (type) == NULL_TREE)
+ {
+ type = build_cplus_array_type (TREE_TYPE (type), TYPE_DOMAIN (type));
+ }
+
+ /* If this is a type name (such as, in a cast or sizeof),
+ compute the type and return it now. */
+
+ if (decl_context == TYPENAME)
+ {
+ /* Note that the grammar rejects storage classes
+ in typenames, fields or parameters. */
+ if (constp || volatilep)
+ if (IS_SIGNATURE (type))
+ error ("`const' or `volatile' specified with signature type");
+ else
+ type = c_build_type_variant (type, constp, volatilep);
+
+ /* Special case: "friend class foo" looks like a TYPENAME context. */
+ if (friendp)
+ {
+ /* A friendly class? */
+ if (current_class_type)
+ make_friend_class (current_class_type, TYPE_MAIN_VARIANT (type));
+ else
+ error("trying to make class `%s' a friend of global scope",
+ TYPE_NAME_STRING (type));
+ type = void_type_node;
+ }
+ else if (quals)
+ {
+#if 0 /* not yet, should get fixed properly later */
+ tree dummy = make_type_decl (declarator, type);
+#else
+ tree dummy = build_decl (TYPE_DECL, declarator, type);
+#endif
+ if (ctype == NULL_TREE)
+ {
+ my_friendly_assert (TREE_CODE (type) == METHOD_TYPE, 159);
+ ctype = TYPE_METHOD_BASETYPE (type);
+ }
+ grok_method_quals (ctype, dummy, quals);
+ type = TREE_TYPE (dummy);
+ }
+
+ return type;
+ }
+ else if (declarator == NULL_TREE && decl_context != PARM
+ && TREE_CODE (type) != UNION_TYPE
+ && ! bitfield)
+ {
+ cp_error ("abstract declarator `%T' used as declaration", type);
+ declarator = make_anon_name ();
+ }
+
+ /* `void' at top level (not within pointer)
+ is allowed only in typedefs or type names.
+ We don't complain about parms either, but that is because
+ a better error message can be made later. */
+
+ if (TYPE_MAIN_VARIANT (type) == void_type_node && decl_context != PARM)
+ {
+ if (TREE_CODE (declarator) == IDENTIFIER_NODE)
+ {
+ if (IDENTIFIER_OPNAME_P (declarator))
+#if 0 /* How could this happen? */
+ error ("operator `%s' declared void",
+ operator_name_string (declarator));
+#else
+ my_friendly_abort (356);
+#endif
+ else
+ error ("variable or field `%s' declared void", name);
+ }
+ else
+ error ("variable or field declared void");
+ type = integer_type_node;
+ }
+
+ /* Now create the decl, which may be a VAR_DECL, a PARM_DECL
+ or a FUNCTION_DECL, depending on DECL_CONTEXT and TYPE. */
+
+ {
+ register tree decl;
+
+ if (decl_context == PARM)
+ {
+ if (ctype)
+ error ("cannot use `::' in parameter declaration");
+
+ /* A parameter declared as an array of T is really a pointer to T.
+ One declared as a function is really a pointer to a function.
+ One declared as a member is really a pointer to member. */
+
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ /* Transfer const-ness of array into that of type pointed to. */
+ type = build_pointer_type
+ (c_build_type_variant (TREE_TYPE (type), constp, volatilep));
+ volatilep = constp = 0;
+ }
+ else if (TREE_CODE (type) == FUNCTION_TYPE)
+ type = build_pointer_type (type);
+ else if (TREE_CODE (type) == OFFSET_TYPE)
+ type = build_pointer_type (type);
+
+ decl = build_decl (PARM_DECL, declarator, type);
+
+ bad_specifiers (decl, "parameter", virtualp, quals != NULL_TREE,
+ inlinep, friendp, raises != NULL_TREE);
+ if (current_class_type
+ && IS_SIGNATURE (current_class_type))
+ {
+ if (inlinep)
+ error ("parameter of signature member function declared `inline'");
+ if (RIDBIT_SETP (RID_AUTO, specbits))
+ error ("parameter of signature member function declared `auto'");
+ if (RIDBIT_SETP (RID_REGISTER, specbits))
+ error ("parameter of signature member function declared `register'");
+ }
+
+ /* Compute the type actually passed in the parmlist,
+ for the case where there is no prototype.
+ (For example, shorts and chars are passed as ints.)
+ When there is a prototype, this is overridden later. */
+
+ DECL_ARG_TYPE (decl) = type_promotes_to (type);
+ }
+ else if (decl_context == FIELD)
+ {
+ if (type == error_mark_node)
+ {
+ /* Happens when declaring arrays of sizes which
+ are error_mark_node, for example. */
+ decl = NULL_TREE;
+ }
+ else if (TREE_CODE (type) == FUNCTION_TYPE)
+ {
+ int publicp = 0;
+
+ if (friendp == 0)
+ {
+ if (ctype == NULL_TREE)
+ ctype = current_class_type;
+
+ if (ctype == NULL_TREE)
+ {
+ cp_error ("can't make `%D' into a method -- not in a class",
+ declarator);
+ return void_type_node;
+ }
+
+ /* ``A union may [ ... ] not [ have ] virtual functions.''
+ ARM 9.5 */
+ if (virtualp && TREE_CODE (ctype) == UNION_TYPE)
+ {
+ cp_error ("function `%D' declared virtual inside a union",
+ declarator);
+ return void_type_node;
+ }
+
+ if (declarator == ansi_opname[(int) NEW_EXPR]
+ || declarator == ansi_opname[(int) VEC_NEW_EXPR]
+ || declarator == ansi_opname[(int) DELETE_EXPR]
+ || declarator == ansi_opname[(int) VEC_DELETE_EXPR])
+ {
+ if (virtualp)
+ {
+ cp_error ("`%D' cannot be declared virtual, since it is always static",
+ declarator);
+ virtualp = 0;
+ }
+ }
+ else if (staticp < 2)
+ type = build_cplus_method_type (build_type_variant (ctype, constp, volatilep),
+ TREE_TYPE (type), TYPE_ARG_TYPES (type));
+ }
+
+ /* Tell grokfndecl if it needs to set TREE_PUBLIC on the node. */
+ publicp = (RIDBIT_SETP (RID_EXTERN, specbits)
+ || (ctype != NULL_TREE
+ && funcdef_flag >= 0
+ && RIDBIT_NOTSETP (RID_INLINE, specbits))
+ || (friendp
+ && ! funcdef_flag
+ && RIDBIT_NOTSETP (RID_STATIC, specbits)
+ && RIDBIT_NOTSETP (RID_INLINE, specbits)));
+ decl = grokfndecl (ctype, type, declarator,
+ virtualp, flags, quals,
+ raises, friendp ? -1 : 0, publicp);
+ if (decl == NULL_TREE)
+ return NULL_TREE;
+
+ DECL_INLINE (decl) = inlinep;
+ }
+ else if (TREE_CODE (type) == METHOD_TYPE)
+ {
+ /* All method decls are public, so tell grokfndecl to set
+ TREE_PUBLIC, also. */
+ decl = grokfndecl (ctype, type, declarator,
+ virtualp, flags, quals,
+ raises, friendp ? -1 : 0, 1);
+ if (decl == NULL_TREE)
+ return NULL_TREE;
+
+ DECL_INLINE (decl) = inlinep;
+ }
+ else if (TREE_CODE (type) == RECORD_TYPE
+ && CLASSTYPE_DECLARED_EXCEPTION (type))
+ {
+ /* Handle a class-local exception declaration. */
+ decl = build_lang_field_decl (VAR_DECL, declarator, type);
+ if (ctype == NULL_TREE)
+ ctype = current_class_type;
+ return void_type_node;
+ }
+ else if (TYPE_SIZE (type) == NULL_TREE && !staticp
+ && (TREE_CODE (type) != ARRAY_TYPE || initialized == 0))
+ {
+ error ("field `%s' has incomplete type",
+ IDENTIFIER_POINTER (declarator));
+
+ /* If we're instantiating a template, tell them which
+ instantiation made the field's type be incomplete. */
+ if (current_class_type
+ && TYPE_NAME (current_class_type)
+ && IDENTIFIER_TEMPLATE (DECL_NAME (TYPE_NAME (current_class_type)))
+ && declspecs && TREE_VALUE (declspecs)
+ && TREE_TYPE (TREE_VALUE (declspecs)) == type)
+ error (" in instantiation of template `%s'",
+ TYPE_NAME_STRING (current_class_type));
+
+ type = error_mark_node;
+ decl = NULL_TREE;
+ }
+ else
+ {
+ if (friendp)
+ {
+ error ("`%s' is neither function nor method; cannot be declared friend",
+ IDENTIFIER_POINTER (declarator));
+ friendp = 0;
+ }
+ decl = NULL_TREE;
+ }
+
+ if (friendp)
+ {
+ /* Friends are treated specially. */
+ if (ctype == current_class_type)
+ warning ("member functions are implicitly friends of their class");
+ else
+ {
+ tree t = NULL_TREE;
+ if (decl && DECL_NAME (decl))
+ t = do_friend (ctype, declarator, decl,
+ last_function_parms, flags, quals);
+ if (t && funcdef_flag)
+ return t;
+
+ return void_type_node;
+ }
+ }
+
+ /* Structure field. It may not be a function, except for C++ */
+
+ if (decl == NULL_TREE)
+ {
+ if (initialized)
+ {
+ /* Motion 10 at San Diego: If a static const integral data
+ member is initialized with an integral constant
+ expression, the initializer may appear either in the
+ declaration (within the class), or in the definition,
+ but not both. If it appears in the class, the member is
+ a member constant. The file-scope definition is always
+ required. */
+ if (staticp)
+ {
+ if (pedantic)
+ {
+ if (! constp)
+ cp_pedwarn ("ANSI C++ forbids in-class initialization of non-const static member `%D'",
+ declarator);
+
+ else if (! INTEGRAL_TYPE_P (type))
+ cp_pedwarn ("ANSI C++ forbids member constant `%D' of non-integral type `%T'", declarator, type);
+ }
+ }
+
+ /* Note that initialization of const members is prohibited
+ by the draft ANSI standard, though it appears to be in
+ common practice. 12.6.2: The argument list is used to
+ initialize the named nonstatic member.... This (or an
+ initializer list) is the only way to initialize
+ nonstatic const and reference members. */
+ else if (flag_ansi || ! constp)
+ cp_pedwarn ("ANSI C++ forbids initialization of %s `%D'",
+ constp ? "const member" : "member", declarator);
+ }
+
+ if (staticp || (constp && initialized))
+ {
+ /* C++ allows static class members.
+ All other work for this is done by grokfield.
+ This VAR_DECL is built by build_lang_field_decl.
+ All other VAR_DECLs are built by build_decl. */
+ decl = build_lang_field_decl (VAR_DECL, declarator, type);
+ TREE_STATIC (decl) = 1;
+ /* In class context, 'static' means public access. */
+ TREE_PUBLIC (decl) = DECL_EXTERNAL (decl) = !!staticp;
+ }
+ else
+ {
+ decl = build_lang_field_decl (FIELD_DECL, declarator, type);
+ if (RIDBIT_SETP (RID_MUTABLE, specbits))
+ {
+ DECL_MUTABLE_P (decl) = 1;
+ RIDBIT_RESET (RID_MUTABLE, specbits);
+ }
+ }
+
+ bad_specifiers (decl, "field", virtualp, quals != NULL_TREE,
+ inlinep, friendp, raises != NULL_TREE);
+ }
+ }
+ else if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE)
+ {
+ tree original_name = declarator;
+ int publicp = 0;
+
+ if (! declarator)
+ return NULL_TREE;
+
+ if (RIDBIT_SETP (RID_AUTO, specbits))
+ error ("storage class `auto' invalid for function `%s'", name);
+ else if (RIDBIT_SETP (RID_REGISTER, specbits))
+ error ("storage class `register' invalid for function `%s'", name);
+
+ /* Function declaration not at top level.
+ Storage classes other than `extern' are not allowed
+ and `extern' makes no difference. */
+ if (current_binding_level != global_binding_level
+ && ! processing_template_decl
+ && (RIDBIT_SETP (RID_STATIC, specbits)
+ || RIDBIT_SETP (RID_INLINE, specbits))
+ && pedantic)
+ {
+ if (RIDBIT_SETP (RID_STATIC, specbits))
+ pedwarn ("storage class `static' invalid for function `%s' declared out of global scope", name);
+ else
+ pedwarn ("storage class `inline' invalid for function `%s' declared out of global scope", name);
+ }
+
+ if (ctype == NULL_TREE)
+ {
+ if (virtualp)
+ {
+ error ("virtual non-class function `%s'", name);
+ virtualp = 0;
+ }
+
+ if (current_lang_name == lang_name_cplusplus
+ && ! (IDENTIFIER_LENGTH (original_name) == 4
+ && IDENTIFIER_POINTER (original_name)[0] == 'm'
+ && strcmp (IDENTIFIER_POINTER (original_name), "main") == 0)
+ && ! (IDENTIFIER_LENGTH (original_name) > 10
+ && IDENTIFIER_POINTER (original_name)[0] == '_'
+ && IDENTIFIER_POINTER (original_name)[1] == '_'
+ && strncmp (IDENTIFIER_POINTER (original_name)+2, "builtin_", 8) == 0))
+ /* Plain overloading: will not be grok'd by grokclassfn. */
+ declarator = build_decl_overload (dname, TYPE_ARG_TYPES (type), 0);
+ }
+ else if (TREE_CODE (type) == FUNCTION_TYPE && staticp < 2)
+ type = build_cplus_method_type (build_type_variant (ctype, constp, volatilep),
+ TREE_TYPE (type), TYPE_ARG_TYPES (type));
+
+ /* Record presence of `static'. In C++, `inline' is like `static'.
+ Methods of classes should be public, unless we're dropping them
+ into some other file, so we don't clear TREE_PUBLIC for them. */
+ publicp
+ = ((ctype
+ && CLASSTYPE_INTERFACE_KNOWN (ctype))
+ || !(RIDBIT_SETP (RID_STATIC, specbits)
+ || RIDBIT_SETP (RID_INLINE, specbits)));
+
+ decl = grokfndecl (ctype, type, original_name,
+ virtualp, flags, quals,
+ raises,
+ processing_template_decl ? 0 : friendp ? 2 : 1,
+ publicp);
+ if (decl == NULL_TREE)
+ return NULL_TREE;
+
+ if (ctype == NULL_TREE && DECL_LANGUAGE (decl) != lang_c)
+ DECL_ASSEMBLER_NAME (decl) = declarator;
+
+ if (staticp == 1)
+ {
+ int illegal_static = 0;
+
+ /* Don't allow a static member function in a class, and forbid
+ declaring main to be static. */
+ if (TREE_CODE (type) == METHOD_TYPE)
+ {
+ cp_error_at ("cannot declare member function `%D' to have static linkage", decl);
+ illegal_static = 1;
+ }
+ else if (! ctype
+ && IDENTIFIER_LENGTH (original_name) == 4
+ && IDENTIFIER_POINTER (original_name)[0] == 'm'
+ && ! strcmp (IDENTIFIER_POINTER (original_name), "main"))
+ {
+ error ("cannot declare function `main' to have static linkage");
+ illegal_static = 1;
+ }
+ else if (current_function_decl)
+ {
+ /* FIXME need arm citation */
+ error ("cannot declare static function inside another function");
+ illegal_static = 1;
+ }
+
+ if (illegal_static)
+ {
+ staticp = 0;
+ RIDBIT_RESET (RID_STATIC, specbits);
+ }
+ }
+
+ /* Record presence of `inline', if it is reasonable. */
+ if (inlinep)
+ {
+ tree last = tree_last (TYPE_ARG_TYPES (type));
+
+ if (! ctype
+ && ! strcmp (IDENTIFIER_POINTER (original_name), "main"))
+ error ("cannot inline function `main'");
+ else if (last && last != void_list_node)
+ cp_warning ("cannot inline function `%D' which takes `...'", original_name);
+ else
+ /* Assume that otherwise the function can be inlined. */
+ DECL_INLINE (decl) = 1;
+
+ if (RIDBIT_SETP (RID_EXTERN, specbits))
+ {
+ current_extern_inline = 1;
+ if (flag_ansi)
+ pedwarn ("ANSI C++ does not permit `extern inline'");
+ }
+ }
+ }
+ else
+ {
+ /* It's a variable. */
+
+ /* An uninitialized decl with `extern' is a reference. */
+ decl = grokvardecl (type, declarator, specbits, initialized);
+ bad_specifiers (decl, "variable", virtualp, quals != NULL_TREE,
+ inlinep, friendp, raises != NULL_TREE);
+
+ if (ctype)
+ {
+ DECL_CONTEXT (decl) = ctype;
+ if (staticp == 1)
+ {
+ cp_error ("static member `%D' re-declared as static",
+ decl);
+ staticp = 0;
+ RIDBIT_RESET (RID_STATIC, specbits);
+ }
+ if (RIDBIT_SETP (RID_EXTERN, specbits))
+ {
+ cp_error ("cannot explicitly declare member `%#D' to have extern linkage",
+ decl);
+ RIDBIT_RESET (RID_EXTERN, specbits);
+ }
+ }
+ }
+
+ if (RIDBIT_SETP (RID_MUTABLE, specbits))
+ {
+ error ("`%s' cannot be declared mutable", name);
+ }
+
+ /* Record `register' declaration for warnings on &
+ and in case doing stupid register allocation. */
+
+ if (RIDBIT_SETP (RID_REGISTER, specbits))
+ DECL_REGISTER (decl) = 1;
+
+ if (RIDBIT_SETP (RID_EXTERN, specbits))
+ DECL_THIS_EXTERN (decl) = 1;
+
+ /* Record constancy and volatility. */
+
+ if (constp)
+ TREE_READONLY (decl) = TREE_CODE (type) != REFERENCE_TYPE;
+ if (volatilep)
+ {
+ TREE_SIDE_EFFECTS (decl) = 1;
+ TREE_THIS_VOLATILE (decl) = 1;
+ }
+
+ return decl;
+ }
+}
+
+/* Tell if a parmlist/exprlist looks like an exprlist or a parmlist.
+ An empty exprlist is a parmlist. An exprlist which
+ contains only identifiers at the global level
+ is a parmlist. Otherwise, it is an exprlist. */
+int
+parmlist_is_exprlist (exprs)
+ tree exprs;
+{
+ if (exprs == NULL_TREE || TREE_PARMLIST (exprs))
+ return 0;
+
+ if (current_binding_level == global_binding_level)
+ {
+ /* At the global level, if these are all identifiers,
+ then it is a parmlist. */
+ while (exprs)
+ {
+ if (TREE_CODE (TREE_VALUE (exprs)) != IDENTIFIER_NODE)
+ return 1;
+ exprs = TREE_CHAIN (exprs);
+ }
+ return 0;
+ }
+ return 1;
+}
+
+/* Subroutine of `grokparms'. In a fcn definition, arg types must
+ be complete.
+
+ C++: also subroutine of `start_function'. */
+static void
+require_complete_types_for_parms (parms)
+ tree parms;
+{
+ while (parms)
+ {
+ tree type = TREE_TYPE (parms);
+ if (TYPE_SIZE (type) == NULL_TREE)
+ {
+ if (DECL_NAME (parms))
+ error ("parameter `%s' has incomplete type",
+ IDENTIFIER_POINTER (DECL_NAME (parms)));
+ else
+ error ("parameter has incomplete type");
+ TREE_TYPE (parms) = error_mark_node;
+ }
+#if 0
+ /* If the arg types are incomplete in a declaration,
+ they must include undefined tags.
+ These tags can never be defined in the scope of the declaration,
+ so the types can never be completed,
+ and no call can be compiled successfully. */
+ /* This is not the right behavior for C++, but not having
+ it is also probably wrong. */
+ else
+ {
+ /* Now warn if is a pointer to an incomplete type. */
+ while (TREE_CODE (type) == POINTER_TYPE
+ || TREE_CODE (type) == REFERENCE_TYPE)
+ type = TREE_TYPE (type);
+ type = TYPE_MAIN_VARIANT (type);
+ if (TYPE_SIZE (type) == NULL_TREE)
+ {
+ if (DECL_NAME (parm) != NULL_TREE)
+ warning ("parameter `%s' points to incomplete type",
+ IDENTIFIER_POINTER (DECL_NAME (parm)));
+ else
+ warning ("parameter points to incomplete type");
+ }
+ }
+#endif
+ parms = TREE_CHAIN (parms);
+ }
+}
+
+/* Decode the list of parameter types for a function type.
+ Given the list of things declared inside the parens,
+ return a list of types.
+
+ The list we receive can have three kinds of elements:
+ an IDENTIFIER_NODE for names given without types,
+ a TREE_LIST node for arguments given as typespecs or names with typespecs,
+ or void_type_node, to mark the end of an argument list
+ when additional arguments are not permitted (... was not used).
+
+ FUNCDEF_FLAG is nonzero for a function definition, 0 for
+ a mere declaration. A nonempty identifier-list gets an error message
+ when FUNCDEF_FLAG is zero.
+ If FUNCDEF_FLAG is 1, then parameter types must be complete.
+ If FUNCDEF_FLAG is -1, then parameter types may be incomplete.
+
+ If all elements of the input list contain types,
+ we return a list of the types.
+ If all elements contain no type (except perhaps a void_type_node
+ at the end), we return a null list.
+ If some have types and some do not, it is an error, and we
+ return a null list.
+
+ Also set last_function_parms to either
+ a list of names (IDENTIFIER_NODEs) or a chain of PARM_DECLs.
+ A list of names is converted to a chain of PARM_DECLs
+ by store_parm_decls so that ultimately it is always a chain of decls.
+
+ Note that in C++, parameters can take default values. These default
+ values are in the TREE_PURPOSE field of the TREE_LIST. It is
+ an error to specify default values which are followed by parameters
+ that have no default values, or an ELLIPSES. For simplicities sake,
+ only parameters which are specified with their types can take on
+ default values. */
+
+static tree
+grokparms (first_parm, funcdef_flag)
+ tree first_parm;
+ int funcdef_flag;
+{
+ tree result = NULL_TREE;
+ tree decls = NULL_TREE;
+
+ if (first_parm != NULL_TREE
+ && TREE_CODE (TREE_VALUE (first_parm)) == IDENTIFIER_NODE)
+ {
+ if (! funcdef_flag)
+ pedwarn ("parameter names (without types) in function declaration");
+ last_function_parms = first_parm;
+ return NULL_TREE;
+ }
+ else if (first_parm != NULL_TREE
+ && TREE_CODE (TREE_VALUE (first_parm)) != TREE_LIST
+ && TREE_VALUE (first_parm) != void_type_node)
+ my_friendly_abort (145);
+ else
+ {
+ /* Types were specified. This is a list of declarators
+ each represented as a TREE_LIST node. */
+ register tree parm, chain;
+ int any_init = 0, any_error = 0, saw_void = 0;
+
+ if (first_parm != NULL_TREE)
+ {
+ tree last_result = NULL_TREE;
+ tree last_decl = NULL_TREE;
+
+ for (parm = first_parm; parm != NULL_TREE; parm = chain)
+ {
+ tree type, list_node = parm;
+ register tree decl = TREE_VALUE (parm);
+ tree init = TREE_PURPOSE (parm);
+
+ chain = TREE_CHAIN (parm);
+ /* @@ weak defense against parse errors. */
+ if (decl != void_type_node && TREE_CODE (decl) != TREE_LIST)
+ {
+ /* Give various messages as the need arises. */
+ if (TREE_CODE (decl) == STRING_CST)
+ error ("invalid string constant `%s'",
+ TREE_STRING_POINTER (decl));
+ else if (TREE_CODE (decl) == INTEGER_CST)
+ error ("invalid integer constant in parameter list, did you forget to give parameter name?");
+ continue;
+ }
+
+ if (decl != void_type_node)
+ {
+ /* @@ May need to fetch out a `raises' here. */
+ decl = grokdeclarator (TREE_VALUE (decl),
+ TREE_PURPOSE (decl),
+ PARM, init != NULL_TREE, NULL_TREE);
+ if (! decl)
+ continue;
+ type = TREE_TYPE (decl);
+ if (TYPE_MAIN_VARIANT (type) == void_type_node)
+ decl = void_type_node;
+ else if (TREE_CODE (type) == METHOD_TYPE)
+ {
+ if (DECL_NAME (decl))
+ /* Cannot use `error_with_decl' here because
+ we don't have DECL_CONTEXT set up yet. */
+ error ("parameter `%s' invalidly declared method type",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ else
+ error ("parameter invalidly declared method type");
+ type = build_pointer_type (type);
+ TREE_TYPE (decl) = type;
+ }
+ else if (TREE_CODE (type) == OFFSET_TYPE)
+ {
+ if (DECL_NAME (decl))
+ error ("parameter `%s' invalidly declared offset type",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ else
+ error ("parameter invalidly declared offset type");
+ type = build_pointer_type (type);
+ TREE_TYPE (decl) = type;
+ }
+ else if (TREE_CODE (type) == RECORD_TYPE
+ && TYPE_LANG_SPECIFIC (type)
+ && CLASSTYPE_ABSTRACT_VIRTUALS (type))
+ {
+ abstract_virtuals_error (decl, type);
+ any_error = 1; /* seems like a good idea */
+ }
+ else if (TREE_CODE (type) == RECORD_TYPE
+ && TYPE_LANG_SPECIFIC (type)
+ && IS_SIGNATURE (type))
+ {
+ signature_error (decl, type);
+ any_error = 1; /* seems like a good idea */
+ }
+ }
+
+ if (decl == void_type_node)
+ {
+ if (result == NULL_TREE)
+ {
+ result = void_list_node;
+ last_result = result;
+ }
+ else
+ {
+ TREE_CHAIN (last_result) = void_list_node;
+ last_result = void_list_node;
+ }
+ saw_void = 1;
+ if (chain
+ && (chain != void_list_node || TREE_CHAIN (chain)))
+ error ("`void' in parameter list must be entire list");
+ break;
+ }
+
+ /* Since there is a prototype, args are passed in their own types. */
+ DECL_ARG_TYPE (decl) = TREE_TYPE (decl);
+#ifdef PROMOTE_PROTOTYPES
+ if ((TREE_CODE (type) == INTEGER_TYPE
+ || TREE_CODE (type) == ENUMERAL_TYPE)
+ && TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node))
+ DECL_ARG_TYPE (decl) = integer_type_node;
+#endif
+ if (!any_error)
+ {
+ if (init)
+ {
+ any_init++;
+ if (TREE_CODE (init) == SAVE_EXPR)
+ PARM_DECL_EXPR (init) = 1;
+ else if (TREE_CODE (init) == VAR_DECL)
+ {
+ if (IDENTIFIER_LOCAL_VALUE (DECL_NAME (init)))
+ {
+ /* ``Local variables may not be used in default
+ argument expressions.'' dpANSI C++ 8.2.6 */
+ /* If extern int i; within a function is not
+ considered a local variable, then this code is
+ wrong. */
+ cp_error ("local variable `%D' may not be used as a default argument", init);
+ any_error = 1;
+ }
+ else if (TREE_READONLY_DECL_P (init))
+ init = decl_constant_value (init);
+ }
+ else
+ init = require_instantiated_type (type, init, integer_zero_node);
+ }
+ else if (any_init)
+ {
+ error ("all trailing parameters must have default arguments");
+ any_error = 1;
+ }
+ }
+ else
+ init = NULL_TREE;
+
+ if (decls == NULL_TREE)
+ {
+ decls = decl;
+ last_decl = decls;
+ }
+ else
+ {
+ TREE_CHAIN (last_decl) = decl;
+ last_decl = decl;
+ }
+ if (TREE_PERMANENT (list_node))
+ {
+ TREE_PURPOSE (list_node) = init;
+ TREE_VALUE (list_node) = type;
+ TREE_CHAIN (list_node) = NULL_TREE;
+ }
+ else
+ list_node = saveable_tree_cons (init, type, NULL_TREE);
+ if (result == NULL_TREE)
+ {
+ result = list_node;
+ last_result = result;
+ }
+ else
+ {
+ TREE_CHAIN (last_result) = list_node;
+ last_result = list_node;
+ }
+ }
+ if (last_result)
+ TREE_CHAIN (last_result) = NULL_TREE;
+ /* If there are no parameters, and the function does not end
+ with `...', then last_decl will be NULL_TREE. */
+ if (last_decl != NULL_TREE)
+ TREE_CHAIN (last_decl) = NULL_TREE;
+ }
+ }
+
+ last_function_parms = decls;
+
+ /* In a fcn definition, arg types must be complete. */
+ if (funcdef_flag > 0)
+ require_complete_types_for_parms (last_function_parms);
+
+ return result;
+}
+
+/* These memoizing functions keep track of special properties which
+ a class may have. `grok_ctor_properties' notices whether a class
+ has a constructor of the form X(X&), and also complains
+ if the class has a constructor of the form X(X).
+ `grok_op_properties' takes notice of the various forms of
+ operator= which are defined, as well as what sorts of type conversion
+ may apply. Both functions take a FUNCTION_DECL as an argument. */
+int
+grok_ctor_properties (ctype, decl)
+ tree ctype, decl;
+{
+ tree parmtypes = FUNCTION_ARG_CHAIN (decl);
+ tree parmtype = parmtypes ? TREE_VALUE (parmtypes) : void_type_node;
+
+ /* When a type has virtual baseclasses, a magical first int argument is
+ added to any ctor so we can tell if the class has been initialized
+ yet. This could screw things up in this function, so we deliberately
+ ignore the leading int if we're in that situation. */
+ if (parmtypes
+ && TREE_VALUE (parmtypes) == integer_type_node
+ && TYPE_USES_VIRTUAL_BASECLASSES (ctype))
+ {
+ parmtypes = TREE_CHAIN (parmtypes);
+ parmtype = TREE_VALUE (parmtypes);
+ }
+
+ if (TREE_CODE (parmtype) == REFERENCE_TYPE
+ && TYPE_MAIN_VARIANT (TREE_TYPE (parmtype)) == ctype)
+ {
+ if (TREE_CHAIN (parmtypes) == NULL_TREE
+ || TREE_CHAIN (parmtypes) == void_list_node
+ || TREE_PURPOSE (TREE_CHAIN (parmtypes)))
+ {
+ TYPE_HAS_INIT_REF (ctype) = 1;
+ if (TYPE_READONLY (TREE_TYPE (parmtype)))
+ TYPE_HAS_CONST_INIT_REF (ctype) = 1;
+ }
+ else
+ TYPE_GETS_INIT_AGGR (ctype) = 1;
+ }
+ else if (TYPE_MAIN_VARIANT (parmtype) == ctype)
+ {
+ if (TREE_CHAIN (parmtypes) != NULL_TREE
+ && TREE_CHAIN (parmtypes) == void_list_node)
+ {
+ cp_error ("invalid constructor; you probably meant `%T (%T&)'",
+ ctype, ctype);
+ SET_IDENTIFIER_ERROR_LOCUS (DECL_NAME (decl), ctype);
+
+ return 0;
+ }
+ else
+ TYPE_GETS_INIT_AGGR (ctype) = 1;
+ }
+ else if (TREE_CODE (parmtype) == VOID_TYPE
+ || TREE_PURPOSE (parmtypes) != NULL_TREE)
+ TYPE_HAS_DEFAULT_CONSTRUCTOR (ctype) = 1;
+
+ return 1;
+}
+
+/* An operator with this name can be either unary or binary. */
+static int
+ambi_op_p (name)
+ tree name;
+{
+ return (name == ansi_opname [(int) INDIRECT_REF]
+ || name == ansi_opname [(int) ADDR_EXPR]
+ || name == ansi_opname [(int) NEGATE_EXPR]
+ || name == ansi_opname[(int) POSTINCREMENT_EXPR]
+ || name == ansi_opname[(int) POSTDECREMENT_EXPR]
+ || name == ansi_opname [(int) CONVERT_EXPR]);
+}
+
+/* An operator with this name can only be unary. */
+static int
+unary_op_p (name)
+ tree name;
+{
+ return (name == ansi_opname [(int) TRUTH_NOT_EXPR]
+ || name == ansi_opname [(int) BIT_NOT_EXPR]
+ || name == ansi_opname [(int) COMPONENT_REF]
+ || OPERATOR_TYPENAME_P (name));
+}
+
+/* Do a little sanity-checking on how they declared their operator. */
+static void
+grok_op_properties (decl, virtualp, friendp)
+ tree decl;
+ int virtualp, friendp;
+{
+ tree argtypes = TYPE_ARG_TYPES (TREE_TYPE (decl));
+ int methodp = (TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE);
+ tree name = DECL_NAME (decl);
+
+ if (current_class_type == NULL_TREE)
+ friendp = 1;
+
+ if (! friendp)
+ {
+ if (name == ansi_opname[(int) MODIFY_EXPR])
+ TYPE_HAS_ASSIGNMENT (current_class_type) = 1;
+ else if (name == ansi_opname[(int) CALL_EXPR])
+ TYPE_OVERLOADS_CALL_EXPR (current_class_type) = 1;
+ else if (name == ansi_opname[(int) ARRAY_REF])
+ TYPE_OVERLOADS_ARRAY_REF (current_class_type) = 1;
+ else if (name == ansi_opname[(int) COMPONENT_REF]
+ || name == ansi_opname[(int) MEMBER_REF])
+ TYPE_OVERLOADS_ARROW (current_class_type) = 1;
+ else if (name == ansi_opname[(int) NEW_EXPR])
+ TYPE_GETS_NEW (current_class_type) |= 1;
+ else if (name == ansi_opname[(int) DELETE_EXPR])
+ TYPE_GETS_DELETE (current_class_type) |= 1;
+ else if (name == ansi_opname[(int) VEC_NEW_EXPR])
+ TYPE_GETS_NEW (current_class_type) |= 2;
+ else if (name == ansi_opname[(int) VEC_DELETE_EXPR])
+ TYPE_GETS_DELETE (current_class_type) |= 2;
+ }
+
+ if (name == ansi_opname[(int) NEW_EXPR]
+ || name == ansi_opname[(int) VEC_NEW_EXPR])
+ {
+ /* When the compiler encounters the definition of A::operator new, it
+ doesn't look at the class declaration to find out if it's static. */
+ if (methodp)
+ revert_static_member_fn (&decl, NULL, NULL);
+
+ /* Take care of function decl if we had syntax errors. */
+ if (argtypes == NULL_TREE)
+ TREE_TYPE (decl) =
+ build_function_type (ptr_type_node,
+ hash_tree_chain (integer_type_node,
+ void_list_node));
+ else
+ TREE_TYPE (decl) = coerce_new_type (TREE_TYPE (decl));
+ }
+ else if (name == ansi_opname[(int) DELETE_EXPR]
+ || name == ansi_opname[(int) VEC_DELETE_EXPR])
+ {
+ if (methodp)
+ revert_static_member_fn (&decl, NULL, NULL);
+
+ if (argtypes == NULL_TREE)
+ TREE_TYPE (decl) =
+ build_function_type (void_type_node,
+ hash_tree_chain (ptr_type_node,
+ void_list_node));
+ else
+ {
+ TREE_TYPE (decl) = coerce_delete_type (TREE_TYPE (decl));
+
+ if (! friendp && name == ansi_opname[(int) VEC_DELETE_EXPR]
+ && (TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (decl)))
+ != void_list_node))
+ TYPE_VEC_DELETE_TAKES_SIZE (current_class_type) = 1;
+ }
+ }
+ else
+ {
+ /* An operator function must either be a non-static member function
+ or have at least one parameter of a class, a reference to a class,
+ an enumeration, or a reference to an enumeration. 13.4.0.6 */
+ if (! methodp || DECL_STATIC_FUNCTION_P (decl))
+ {
+ if (OPERATOR_TYPENAME_P (name)
+ || name == ansi_opname[(int) CALL_EXPR]
+ || name == ansi_opname[(int) MODIFY_EXPR]
+ || name == ansi_opname[(int) COMPONENT_REF]
+ || name == ansi_opname[(int) ARRAY_REF])
+ cp_error ("`%D' must be a nonstatic member function", decl);
+ else
+ {
+ tree p = argtypes;
+
+ if (DECL_STATIC_FUNCTION_P (decl))
+ cp_error ("`%D' must be either a non-static member function or a non-member function", decl);
+
+ if (p)
+ for (; TREE_VALUE (p) != void_type_node ; p = TREE_CHAIN (p))
+ {
+ tree arg = TREE_VALUE (p);
+ if (TREE_CODE (arg) == REFERENCE_TYPE)
+ arg = TREE_TYPE (arg);
+
+ /* This lets bad template code slip through. */
+ if (IS_AGGR_TYPE (arg)
+ || TREE_CODE (arg) == ENUMERAL_TYPE
+ || TREE_CODE (arg) == TEMPLATE_TYPE_PARM)
+ goto foundaggr;
+ }
+ cp_error
+ ("`%D' must have an argument of class or enumerated type",
+ decl);
+ foundaggr:
+ ;
+ }
+ }
+
+ if (name == ansi_opname[(int) CALL_EXPR]
+ || name == ansi_opname[(int) METHOD_CALL_EXPR])
+ return; /* no restrictions on args */
+
+ if (IDENTIFIER_TYPENAME_P (name))
+ {
+ tree t = TREE_TYPE (name);
+ if (TREE_CODE (t) == VOID_TYPE)
+ pedwarn ("void is not a valid type conversion operator");
+ else if (! friendp)
+ {
+ int ref = (TREE_CODE (t) == REFERENCE_TYPE);
+ char *what = 0;
+ if (ref)
+ t = TYPE_MAIN_VARIANT (TREE_TYPE (t));
+
+ if (t == current_class_type)
+ what = "the same type";
+ else if (IS_AGGR_TYPE (t)
+ && DERIVED_FROM_P (t, current_class_type))
+ what = "a base class";
+
+ if (what)
+ warning ("conversion to %s%s will never use a type conversion operator",
+ ref ? "a reference to " : "", what);
+ }
+ }
+
+ if (name == ansi_opname[(int) MODIFY_EXPR])
+ {
+ tree parmtype;
+
+ if (list_length (argtypes) != 3 && methodp)
+ {
+ cp_error ("`%D' must take exactly one argument", decl);
+ return;
+ }
+ parmtype = TREE_VALUE (TREE_CHAIN (argtypes));
+
+ if (copy_assignment_arg_p (parmtype, virtualp)
+ && ! friendp)
+ {
+ TYPE_HAS_ASSIGN_REF (current_class_type) = 1;
+ if (TREE_CODE (parmtype) != REFERENCE_TYPE
+ || TYPE_READONLY (TREE_TYPE (parmtype)))
+ TYPE_HAS_CONST_ASSIGN_REF (current_class_type) = 1;
+#if 0 /* Too soon; done in grok_function_init */
+ if (DECL_ABSTRACT_VIRTUAL_P (decl))
+ TYPE_HAS_ABSTRACT_ASSIGN_REF (current_class_type) = 1;
+#endif
+ }
+ }
+ else if (name == ansi_opname[(int) COND_EXPR])
+ {
+ /* 13.4.0.3 */
+ pedwarn ("ANSI C++ prohibits overloading operator ?:");
+ if (list_length (argtypes) != 4)
+ cp_error ("`%D' must take exactly three arguments", decl);
+ }
+ else if (ambi_op_p (name))
+ {
+ if (list_length (argtypes) == 2)
+ /* prefix */;
+ else if (list_length (argtypes) == 3)
+ {
+ if ((name == ansi_opname[(int) POSTINCREMENT_EXPR]
+ || name == ansi_opname[(int) POSTDECREMENT_EXPR])
+ && TREE_VALUE (TREE_CHAIN (argtypes)) != integer_type_node)
+ {
+ if (methodp)
+ cp_error ("postfix `%D' must take `int' as its argument",
+ decl);
+ else
+ cp_error
+ ("postfix `%D' must take `int' as its second argument",
+ decl);
+ }
+ }
+ else
+ {
+ if (methodp)
+ cp_error ("`%D' must take either zero or one argument", decl);
+ else
+ cp_error ("`%D' must take either one or two arguments", decl);
+ }
+ }
+ else if (unary_op_p (name))
+ {
+ if (list_length (argtypes) != 2)
+ {
+ if (methodp)
+ cp_error ("`%D' must take `void'", decl);
+ else
+ cp_error ("`%D' must take exactly one argument", decl);
+ }
+ }
+ else /* if (binary_op_p (name)) */
+ {
+ if (list_length (argtypes) != 3)
+ {
+ if (methodp)
+ cp_error ("`%D' must take exactly one argument", decl);
+ else
+ cp_error ("`%D' must take exactly two arguments", decl);
+ }
+ }
+
+ /* 13.4.0.8 */
+ if (argtypes)
+ for (; argtypes != void_list_node ; argtypes = TREE_CHAIN (argtypes))
+ if (TREE_PURPOSE (argtypes))
+ {
+ TREE_PURPOSE (argtypes) = NULL_TREE;
+ if (name == ansi_opname[(int) POSTINCREMENT_EXPR]
+ || name == ansi_opname[(int) POSTDECREMENT_EXPR])
+ {
+ if (pedantic)
+ cp_pedwarn ("`%D' cannot have default arguments", decl);
+ }
+ else
+ cp_error ("`%D' cannot have default arguments", decl);
+ }
+ }
+}
+
+/* Get the struct, enum or union (CODE says which) with tag NAME.
+ Define the tag as a forward-reference if it is not defined.
+
+ C++: If a class derivation is given, process it here, and report
+ an error if multiple derivation declarations are not identical.
+
+ If this is a definition, come in through xref_tag and only look in
+ the current frame for the name (since C++ allows new names in any
+ scope.) */
+
+/* avoid rewriting all callers of xref_tag */
+static int xref_next_defn = 0;
+
+tree
+xref_defn_tag (code_type_node, name, binfo)
+ tree code_type_node;
+ tree name, binfo;
+{
+ tree rv, ncp;
+ xref_next_defn = 1;
+
+ if (class_binding_level)
+ {
+ tree n1;
+ char *buf;
+ /* we need to build a new IDENTIFIER_NODE for name which nukes
+ * the pieces... */
+/*
+ n1 = IDENTIFIER_LOCAL_VALUE (current_class_name);
+ if (n1)
+ n1 = DECL_NAME (n1);
+ else
+ n1 = current_class_name;
+*/
+ n1 = TYPE_NAME (current_class_type);
+ if (n1)
+ n1 = DECL_NESTED_TYPENAME(n1);
+ else
+ n1 = current_class_name;
+
+ buf = (char *) alloca (4 + IDENTIFIER_LENGTH (n1)
+ + IDENTIFIER_LENGTH (name));
+
+ sprintf (buf, "%s::%s", IDENTIFIER_POINTER (n1),
+ IDENTIFIER_POINTER (name));
+ ncp = get_identifier (buf);
+#ifdef SPEW_DEBUG
+ if (spew_debug)
+ printf("*** %s ***\n", IDENTIFIER_POINTER (ncp));
+#endif
+#if 0
+ IDENTIFIER_LOCAL_VALUE (name) =
+ build_decl (TYPE_DECL, ncp, NULL_TREE);
+#endif
+ rv = xref_tag (code_type_node, name, binfo, 0);
+ if (! ANON_AGGRNAME_P (name))
+ {
+ register tree type_decl = build_decl (TYPE_DECL, ncp, rv);
+#ifdef DWARF_DEBUGGING_INFO
+ /* Mark the TYPE_DECL node created just above as a gratuitous one
+ so that dwarfout.c will know not to generate a TAG_typedef DIE
+ for it. */
+ if (write_symbols == DWARF_DEBUG)
+ DECL_IGNORED_P (type_decl) = 1;
+#endif /* DWARF_DEBUGGING_INFO */
+ pushdecl_nonclass_level (type_decl);
+ }
+ }
+ else
+ {
+ rv = xref_tag (code_type_node, name, binfo, 0);
+ }
+ xref_next_defn = 0;
+ return rv;
+}
+
+tree
+xref_tag (code_type_node, name, binfo, globalize)
+ tree code_type_node;
+ tree name, binfo;
+ int globalize;
+{
+ enum tag_types tag_code;
+ enum tree_code code;
+ int temp = 0;
+ int i, len;
+ register tree ref, t;
+ struct binding_level *b = inner_binding_level;
+
+ tag_code = (enum tag_types) TREE_INT_CST_LOW (code_type_node);
+ switch (tag_code)
+ {
+ case record_type:
+ case class_type:
+ case exception_type:
+ case signature_type:
+ code = RECORD_TYPE;
+ len = list_length (binfo);
+ break;
+ case union_type:
+ code = UNION_TYPE;
+ if (binfo)
+ {
+ cp_error ("derived union `%T' invalid", name);
+ binfo = NULL_TREE;
+ }
+ len = 0;
+ break;
+ case enum_type:
+ code = ENUMERAL_TYPE;
+ break;
+ default:
+ my_friendly_abort (18);
+ }
+
+ /* If a cross reference is requested, look up the type
+ already defined for this tag and return it. */
+ t = IDENTIFIER_TYPE_VALUE (name);
+ if (t && TREE_CODE (t) != code)
+ t = NULL_TREE;
+
+ if (xref_next_defn)
+ {
+ /* If we know we are defining this tag, only look it up in this scope
+ * and don't try to find it as a type. */
+ xref_next_defn = 0;
+ if (t && TYPE_CONTEXT(t))
+ {
+ if (TREE_MANGLED (name))
+ ref = t;
+ else
+ ref = lookup_tag (code, name, b, 1);
+ }
+ else
+ ref = lookup_tag (code, name, b, 1);
+ }
+ else
+ {
+ if (t)
+ ref = t;
+ else
+ ref = lookup_tag (code, name, b, 0);
+
+ if (! ref)
+ {
+ /* Try finding it as a type declaration. If that wins, use it. */
+ ref = lookup_name (name, 1);
+ if (ref && TREE_CODE (ref) == TYPE_DECL
+ && TREE_CODE (TREE_TYPE (ref)) == code)
+ ref = TREE_TYPE (ref);
+ else
+ ref = NULL_TREE;
+ }
+ }
+
+ push_obstacks_nochange ();
+
+ if (! ref)
+ {
+ /* If no such tag is yet defined, create a forward-reference node
+ and record it as the "definition".
+ When a real declaration of this type is found,
+ the forward-reference will be altered into a real type. */
+
+ /* In C++, since these migrate into the global scope, we must
+ build them on the permanent obstack. */
+
+ temp = allocation_temporary_p ();
+ if (temp)
+ end_temporary_allocation ();
+
+ if (code == ENUMERAL_TYPE)
+ {
+ ref = make_node (ENUMERAL_TYPE);
+
+ /* Give the type a default layout like unsigned int
+ to avoid crashing if it does not get defined. */
+ TYPE_MODE (ref) = TYPE_MODE (unsigned_type_node);
+ TYPE_ALIGN (ref) = TYPE_ALIGN (unsigned_type_node);
+ TREE_UNSIGNED (ref) = 1;
+ TYPE_PRECISION (ref) = TYPE_PRECISION (unsigned_type_node);
+ TYPE_MIN_VALUE (ref) = TYPE_MIN_VALUE (unsigned_type_node);
+ TYPE_MAX_VALUE (ref) = TYPE_MAX_VALUE (unsigned_type_node);
+
+ /* Enable us to recognize when a type is created in class context.
+ To do nested classes correctly, this should probably be cleared
+ out when we leave this classes scope. Currently this in only
+ done in `start_enum'. */
+
+ pushtag (name, ref, globalize);
+ if (flag_cadillac)
+ cadillac_start_enum (ref);
+ }
+ else if (tag_code == exception_type)
+ {
+ ref = make_lang_type (code);
+ /* Enable us to recognize when an exception type is created in
+ class context. To do nested classes correctly, this should
+ probably be cleared out when we leave this class's scope. */
+ CLASSTYPE_DECLARED_EXCEPTION (ref) = 1;
+ pushtag (name, ref, globalize);
+ if (flag_cadillac)
+ cadillac_start_struct (ref);
+ }
+ else
+ {
+ extern tree pending_vtables;
+ struct binding_level *old_b = class_binding_level;
+ int needs_writing;
+
+ ref = make_lang_type (code);
+
+ /* A signature type will contain the fields of the signature
+ table. Therefore, it's not only an interface. */
+ if (tag_code == signature_type)
+ {
+ SET_SIGNATURE (ref);
+ CLASSTYPE_INTERFACE_ONLY (ref) = 0;
+ CLASSTYPE_INTERFACE_UNKNOWN (ref) = 0;
+ }
+
+ /* Record how to set the access of this class's
+ virtual functions. If write_virtuals == 2 or 3, then
+ inline virtuals are ``extern inline''. */
+ switch (write_virtuals)
+ {
+ case 0:
+ case 1:
+ needs_writing = 1;
+ break;
+ case 2:
+ needs_writing = !! value_member (name, pending_vtables);
+ break;
+ case 3:
+ needs_writing = ! CLASSTYPE_INTERFACE_ONLY (ref)
+ && CLASSTYPE_INTERFACE_KNOWN (ref);
+ break;
+ default:
+ needs_writing = 0;
+ }
+
+ /* Signatures don't have a vtable. As long as we don't have default
+ implementations, they behave as if `write_virtuals' were 3. */
+ if (tag_code == signature_type)
+ CLASSTYPE_VTABLE_NEEDS_WRITING (ref) = 0;
+ else
+ CLASSTYPE_VTABLE_NEEDS_WRITING (ref) = needs_writing;
+
+#ifdef NONNESTED_CLASSES
+ /* Class types don't nest the way enums do. */
+ class_binding_level = (struct binding_level *)0;
+#endif
+ pushtag (name, ref, globalize);
+ class_binding_level = old_b;
+
+ if (flag_cadillac)
+ cadillac_start_struct (ref);
+ }
+ }
+ else
+ {
+ if (IS_AGGR_TYPE_CODE (code))
+ {
+ if (IS_AGGR_TYPE (ref)
+ && ((tag_code == exception_type)
+ != (CLASSTYPE_DECLARED_EXCEPTION (ref) == 1)))
+ {
+ cp_error ("type `%T' is both exception and aggregate type", ref);
+ CLASSTYPE_DECLARED_EXCEPTION (ref) = (tag_code == exception_type);
+ }
+ }
+
+ /* If it no longer looks like a nested type, make sure it's
+ in global scope. */
+ if (b == global_binding_level && !class_binding_level
+ && IDENTIFIER_GLOBAL_VALUE (name) == NULL_TREE)
+ IDENTIFIER_GLOBAL_VALUE (name) = TYPE_NAME (ref);
+
+ if (binfo)
+ {
+ tree tt1 = binfo;
+ tree tt2 = TYPE_BINFO_BASETYPES (ref);
+
+ if (TYPE_BINFO_BASETYPES (ref))
+ for (i = 0; tt1; i++, tt1 = TREE_CHAIN (tt1))
+ if (TREE_VALUE (tt1) != TYPE_IDENTIFIER (BINFO_TYPE (TREE_VEC_ELT (tt2, i))))
+ {
+ cp_error ("redeclaration of derivation chain of type `%#T'",
+ ref);
+ break;
+ }
+
+ if (tt1 == NULL_TREE)
+ /* The user told us something we already knew. */
+ goto just_return;
+
+ /* In C++, since these migrate into the global scope, we must
+ build them on the permanent obstack. */
+ end_temporary_allocation ();
+ }
+ }
+
+ if (binfo)
+ {
+ /* In the declaration `A : X, Y, ... Z' we mark all the types
+ (A, X, Y, ..., Z) so we can check for duplicates. */
+ tree binfos;
+
+ SET_CLASSTYPE_MARKED (ref);
+ BINFO_BASETYPES (TYPE_BINFO (ref)) = binfos = make_tree_vec (len);
+
+ for (i = 0; binfo; binfo = TREE_CHAIN (binfo))
+ {
+ /* The base of a derived struct is public by default. */
+ int via_public
+ = (TREE_PURPOSE (binfo) == (tree)access_public
+ || TREE_PURPOSE (binfo) == (tree)access_public_virtual
+ || (tag_code != class_type
+ && (TREE_PURPOSE (binfo) == (tree)access_default
+ || TREE_PURPOSE (binfo) == (tree)access_default_virtual)));
+ int via_protected = TREE_PURPOSE (binfo) == (tree)access_protected;
+ int via_virtual
+ = (TREE_PURPOSE (binfo) == (tree)access_private_virtual
+ || TREE_PURPOSE (binfo) == (tree)access_public_virtual
+ || TREE_PURPOSE (binfo) == (tree)access_default_virtual);
+ tree basetype = TREE_TYPE (TREE_VALUE (binfo));
+ tree base_binfo;
+
+ GNU_xref_hier (IDENTIFIER_POINTER (name),
+ IDENTIFIER_POINTER (TREE_VALUE (binfo)),
+ via_public, via_virtual, 0);
+
+ if (basetype && TREE_CODE (basetype) == TYPE_DECL)
+ basetype = TREE_TYPE (basetype);
+ if (!basetype || TREE_CODE (basetype) != RECORD_TYPE)
+ {
+ error ("base type `%s' fails to be a struct or class type",
+ IDENTIFIER_POINTER (TREE_VALUE (binfo)));
+ continue;
+ }
+#if 1
+ /* This code replaces similar code in layout_basetypes. */
+ else if (TYPE_SIZE (basetype) == NULL_TREE)
+ {
+ cp_error ("base class `%T' has incomplete type", basetype);
+ continue;
+ }
+#endif
+ else
+ {
+ if (CLASSTYPE_MARKED (basetype))
+ {
+ if (basetype == ref)
+ cp_error ("recursive type `%T' undefined", basetype);
+ else
+ cp_error ("duplicate base type `%T' invalid", basetype);
+ continue;
+ }
+
+ /* Note that the BINFO records which describe individual
+ inheritances are *not* shared in the lattice! They
+ cannot be shared because a given baseclass may be
+ inherited with different `accessibility' by different
+ derived classes. (Each BINFO record describing an
+ individual inheritance contains flags which say what
+ the `accessibility' of that particular inheritance is.) */
+
+ base_binfo = make_binfo (integer_zero_node, basetype,
+ TYPE_BINFO_VTABLE (basetype),
+ TYPE_BINFO_VIRTUALS (basetype), NULL_TREE);
+
+ TREE_VEC_ELT (binfos, i) = base_binfo;
+ TREE_VIA_PUBLIC (base_binfo) = via_public;
+ TREE_VIA_PROTECTED (base_binfo) = via_protected;
+ TREE_VIA_VIRTUAL (base_binfo) = via_virtual;
+
+ SET_CLASSTYPE_MARKED (basetype);
+#if 0
+/* XYZZY TEST VIRTUAL BASECLASSES */
+if (CLASSTYPE_N_BASECLASSES (basetype) == NULL_TREE
+ && TYPE_HAS_DEFAULT_CONSTRUCTOR (basetype)
+ && via_virtual == 0)
+ {
+ warning ("making type `%s' a virtual baseclass",
+ TYPE_NAME_STRING (basetype));
+ via_virtual = 1;
+ }
+#endif
+ /* We are free to modify these bits because they are meaningless
+ at top level, and BASETYPE is a top-level type. */
+ if (via_virtual || TYPE_USES_VIRTUAL_BASECLASSES (basetype))
+ {
+ TYPE_USES_VIRTUAL_BASECLASSES (ref) = 1;
+ TYPE_USES_COMPLEX_INHERITANCE (ref) = 1;
+ }
+
+ TYPE_OVERLOADS_METHOD_CALL_EXPR (ref) |= TYPE_OVERLOADS_METHOD_CALL_EXPR (basetype);
+ TYPE_GETS_NEW (ref) |= TYPE_GETS_NEW (basetype);
+ TYPE_GETS_DELETE (ref) |= TYPE_GETS_DELETE (basetype);
+ CLASSTYPE_LOCAL_TYPEDECLS (ref) |= CLASSTYPE_LOCAL_TYPEDECLS (basetype);
+ i += 1;
+ }
+ }
+ if (i)
+ TREE_VEC_LENGTH (binfos) = i;
+ else
+ BINFO_BASETYPES (TYPE_BINFO (ref)) = NULL_TREE;
+
+ if (i > 1)
+ TYPE_USES_MULTIPLE_INHERITANCE (ref) = 1;
+ else if (i == 1)
+ TYPE_USES_MULTIPLE_INHERITANCE (ref)
+ = TYPE_USES_MULTIPLE_INHERITANCE (BINFO_TYPE (TREE_VEC_ELT (binfos, 0)));
+ if (TYPE_USES_MULTIPLE_INHERITANCE (ref))
+ TYPE_USES_COMPLEX_INHERITANCE (ref) = 1;
+
+ /* Unmark all the types. */
+ while (--i >= 0)
+ CLEAR_CLASSTYPE_MARKED (BINFO_TYPE (TREE_VEC_ELT (binfos, i)));
+ CLEAR_CLASSTYPE_MARKED (ref);
+ }
+
+ just_return:
+
+ /* Until the type is defined, tentatively accept whatever
+ structure tag the user hands us. */
+ if (TYPE_SIZE (ref) == NULL_TREE
+ && ref != current_class_type
+ /* Have to check this, in case we have contradictory tag info. */
+ && IS_AGGR_TYPE_CODE (TREE_CODE (ref)))
+ {
+ if (tag_code == class_type)
+ CLASSTYPE_DECLARED_CLASS (ref) = 1;
+ else if (tag_code == record_type || tag_code == signature_type)
+ CLASSTYPE_DECLARED_CLASS (ref) = 0;
+ }
+
+ pop_obstacks ();
+
+ return ref;
+}
+
+static tree current_local_enum = NULL_TREE;
+
+/* Begin compiling the definition of an enumeration type.
+ NAME is its name (or null if anonymous).
+ Returns the type object, as yet incomplete.
+ Also records info about it so that build_enumerator
+ may be used to declare the individual values as they are read. */
+
+tree
+start_enum (name)
+ tree name;
+{
+ register tree enumtype = NULL_TREE;
+ struct binding_level *b = inner_binding_level;
+
+ /* If this is the real definition for a previous forward reference,
+ fill in the contents in the same object that used to be the
+ forward reference. */
+
+ if (name != NULL_TREE)
+ enumtype = lookup_tag (ENUMERAL_TYPE, name, b, 1);
+
+ if (enumtype != NULL_TREE && TREE_CODE (enumtype) == ENUMERAL_TYPE)
+ cp_error ("multiple definition of enum `%T'", enumtype);
+ else
+ {
+ enumtype = make_node (ENUMERAL_TYPE);
+ pushtag (name, enumtype, 0);
+ }
+
+ if (current_class_type)
+ TREE_ADDRESSABLE (b->tags) = 1;
+ current_local_enum = NULL_TREE;
+
+ if (TYPE_VALUES (enumtype) != NULL_TREE)
+ /* Completely replace its old definition.
+ The old enumerators remain defined, however. */
+ TYPE_VALUES (enumtype) = NULL_TREE;
+
+ /* Initially, set up this enum as like `int'
+ so that we can create the enumerators' declarations and values.
+ Later on, the precision of the type may be changed and
+ it may be laid out again. */
+
+ TYPE_PRECISION (enumtype) = TYPE_PRECISION (integer_type_node);
+ TYPE_SIZE (enumtype) = NULL_TREE;
+ fixup_unsigned_type (enumtype);
+
+ /* We copy this value because enumerated type constants
+ are really of the type of the enumerator, not integer_type_node. */
+ enum_next_value = copy_node (integer_zero_node);
+ enum_overflow = 0;
+
+ GNU_xref_decl (current_function_decl, enumtype);
+ return enumtype;
+}
+
+/* After processing and defining all the values of an enumeration type,
+ install their decls in the enumeration type and finish it off.
+ ENUMTYPE is the type object and VALUES a list of name-value pairs.
+ Returns ENUMTYPE. */
+
+tree
+finish_enum (enumtype, values)
+ register tree enumtype, values;
+{
+ register tree pair, tem;
+ register HOST_WIDE_INT maxvalue = 0;
+ register HOST_WIDE_INT minvalue = 0;
+ register HOST_WIDE_INT i;
+
+ /* Calculate the maximum value of any enumerator in this type. */
+
+ if (values)
+ {
+ /* Speed up the main loop by performing some precalculations */
+
+ HOST_WIDE_INT value;
+ TREE_TYPE (TREE_VALUE (values)) = enumtype;
+ TREE_TYPE (DECL_INITIAL (TREE_VALUE (values))) = enumtype;
+ TREE_VALUE (values) = DECL_INITIAL (TREE_VALUE (values));
+ value = TREE_INT_CST_LOW (TREE_VALUE (values));
+ minvalue = maxvalue = value;
+
+ for (pair = TREE_CHAIN (values); pair; pair = TREE_CHAIN (pair))
+ {
+ TREE_TYPE (TREE_VALUE (pair)) = enumtype;
+ TREE_TYPE (DECL_INITIAL (TREE_VALUE (pair))) = enumtype;
+ TREE_VALUE (pair) = DECL_INITIAL (TREE_VALUE (pair));
+ value = TREE_INT_CST_LOW (TREE_VALUE (pair));
+ if (value > maxvalue)
+ maxvalue = value;
+ else if (value < minvalue)
+ minvalue = value;
+ }
+ }
+
+ TYPE_VALUES (enumtype) = values;
+
+ if (flag_short_enums)
+ {
+ /* Determine the precision this type needs, lay it out, and define
+ it. */
+
+ /* First reset precision */
+ TYPE_PRECISION (enumtype) = 0;
+
+ for (i = maxvalue; i; i >>= 1)
+ TYPE_PRECISION (enumtype)++;
+
+ if (!TYPE_PRECISION (enumtype))
+ TYPE_PRECISION (enumtype) = 1;
+
+ /* Cancel the laying out previously done for the enum type,
+ so that fixup_unsigned_type will do it over. */
+ TYPE_SIZE (enumtype) = NULL_TREE;
+
+ fixup_unsigned_type (enumtype);
+ }
+
+ TREE_INT_CST_LOW (TYPE_MAX_VALUE (enumtype)) = maxvalue;
+
+ /* An enum can have some negative values; then it is signed. */
+ if (minvalue < 0)
+ {
+ TREE_INT_CST_LOW (TYPE_MIN_VALUE (enumtype)) = minvalue;
+ TREE_INT_CST_HIGH (TYPE_MIN_VALUE (enumtype)) = -1;
+ TREE_UNSIGNED (enumtype) = 0;
+ }
+ if (flag_cadillac)
+ cadillac_finish_enum (enumtype);
+
+ /* Fix up all variant types of this enum type. */
+ for (tem = TYPE_MAIN_VARIANT (enumtype); tem; tem = TYPE_NEXT_VARIANT (tem))
+ {
+ TYPE_VALUES (tem) = TYPE_VALUES (enumtype);
+ TYPE_MIN_VALUE (tem) = TYPE_MIN_VALUE (enumtype);
+ TYPE_MAX_VALUE (tem) = TYPE_MAX_VALUE (enumtype);
+ TYPE_SIZE (tem) = TYPE_SIZE (enumtype);
+ TYPE_MODE (tem) = TYPE_MODE (enumtype);
+ TYPE_PRECISION (tem) = TYPE_PRECISION (enumtype);
+ TYPE_ALIGN (tem) = TYPE_ALIGN (enumtype);
+ TREE_UNSIGNED (tem) = TREE_UNSIGNED (enumtype);
+ }
+
+ /* Finish debugging output for this type. */
+#if 0
+ /* @@ Do we ever generate generate ENUMERAL_TYPE nodes for which debugging
+ information should *not* be generated? I think not. */
+ if (! DECL_IGNORED_P (TYPE_NAME (enumtype)))
+#endif
+ rest_of_type_compilation (enumtype, global_bindings_p ());
+
+ return enumtype;
+}
+
+/* Build and install a CONST_DECL for one value of the
+ current enumeration type (one that was begun with start_enum).
+ Return a tree-list containing the name and its value.
+ Assignment of sequential values by default is handled here. */
+
+tree
+build_enumerator (name, value)
+ tree name, value;
+{
+ tree decl, result;
+ /* Change this to zero if we find VALUE is not shareable. */
+ int shareable = 1;
+
+ /* Remove no-op casts from the value. */
+ if (value)
+ STRIP_TYPE_NOPS (value);
+
+ /* Validate and default VALUE. */
+ if (value != NULL_TREE)
+ {
+ if (TREE_READONLY_DECL_P (value))
+ {
+ value = decl_constant_value (value);
+ shareable = 0;
+ }
+
+ if (TREE_CODE (value) == INTEGER_CST)
+ {
+ value = default_conversion (value);
+ constant_expression_warning (value);
+ }
+ else
+ {
+ cp_error ("enumerator value for `%D' not integer constant", name);
+ value = NULL_TREE;
+ }
+ }
+
+ /* The order of things is reversed here so that we
+ can check for possible sharing of enum values,
+ to keep that from happening. */
+ /* Default based on previous value. */
+ if (value == NULL_TREE)
+ {
+ value = enum_next_value;
+ if (enum_overflow)
+ cp_error ("overflow in enumeration values at `%D'", name);
+ }
+
+ /* Remove no-op casts from the value. */
+ if (value)
+ STRIP_TYPE_NOPS (value);
+
+ /* Make up for hacks in lex.c. */
+ if (value == integer_zero_node)
+ value = build_int_2 (0, 0);
+ else if (value == integer_one_node)
+ value = build_int_2 (1, 0);
+ else if (TREE_CODE (value) == INTEGER_CST
+ && (shareable == 0
+ || TREE_CODE (TREE_TYPE (value)) == ENUMERAL_TYPE))
+ {
+ value = copy_node (value);
+ TREE_TYPE (value) = integer_type_node;
+ }
+
+ /* C++ associates enums with global, function, or class declarations. */
+
+ decl = current_scope ();
+ if (decl && decl == current_class_type)
+ {
+ /* This enum declaration is local to the class, so we must put
+ it in that class's list of decls. */
+ decl = build_lang_field_decl (CONST_DECL, name, integer_type_node);
+ DECL_INITIAL (decl) = value;
+ TREE_READONLY (decl) = 1;
+ pushdecl_class_level (decl);
+ TREE_CHAIN (decl) = current_local_enum;
+ current_local_enum = decl;
+ }
+ else
+ {
+ /* It's a global enum, or it's local to a function. (Note local to
+ a function could mean local to a class method. */
+ decl = build_decl (CONST_DECL, name, integer_type_node);
+ DECL_INITIAL (decl) = value;
+
+ pushdecl (decl);
+ GNU_xref_decl (current_function_decl, decl);
+ }
+
+ /* Set basis for default for next value. */
+ enum_next_value = build_binary_op_nodefault (PLUS_EXPR, value,
+ integer_one_node, PLUS_EXPR);
+ enum_overflow = tree_int_cst_lt (enum_next_value, value);
+
+ if (enum_next_value == integer_one_node)
+ enum_next_value = copy_node (enum_next_value);
+
+ result = saveable_tree_cons (name, decl, NULL_TREE);
+ return result;
+}
+
+tree
+grok_enum_decls (type, decl)
+ tree type, decl;
+{
+ tree d = current_local_enum;
+
+ if (d == NULL_TREE)
+ return decl;
+
+ while (1)
+ {
+ TREE_TYPE (d) = type;
+ if (TREE_CHAIN (d) == NULL_TREE)
+ {
+ TREE_CHAIN (d) = decl;
+ break;
+ }
+ d = TREE_CHAIN (d);
+ }
+
+ decl = current_local_enum;
+ current_local_enum = NULL_TREE;
+
+ return decl;
+}
+
+/* Create the FUNCTION_DECL for a function definition.
+ DECLSPECS and DECLARATOR are the parts of the declaration;
+ they describe the function's name and the type it returns,
+ but twisted together in a fashion that parallels the syntax of C.
+
+ This function creates a binding context for the function body
+ as well as setting up the FUNCTION_DECL in current_function_decl.
+
+ Returns 1 on success. If the DECLARATOR is not suitable for a function
+ (it defines a datum instead), we return 0, which tells
+ yyparse to report a parse error.
+
+ For C++, we must first check whether that datum makes any sense.
+ For example, "class A local_a(1,2);" means that variable local_a
+ is an aggregate of type A, which should have a constructor
+ applied to it with the argument list [1, 2].
+
+ @@ There is currently no way to retrieve the storage
+ @@ allocated to FUNCTION (or all of its parms) if we return
+ @@ something we had previously. */
+
+int
+start_function (declspecs, declarator, raises, pre_parsed_p)
+ tree declarator, declspecs, raises;
+ int pre_parsed_p;
+{
+ tree decl1, olddecl;
+ tree ctype = NULL_TREE;
+ tree fntype;
+ tree restype;
+ extern int have_extern_spec;
+ extern int used_extern_spec;
+ int doing_friend = 0;
+
+ /* Sanity check. */
+ my_friendly_assert (TREE_VALUE (void_list_node) == void_type_node, 160);
+ my_friendly_assert (TREE_CHAIN (void_list_node) == NULL_TREE, 161);
+
+ /* Assume, until we see it does. */
+ current_function_returns_value = 0;
+ current_function_returns_null = 0;
+ warn_about_return_type = 0;
+ current_extern_inline = 0;
+ current_function_assigns_this = 0;
+ current_function_just_assigned_this = 0;
+ current_function_parms_stored = 0;
+ original_result_rtx = NULL_RTX;
+ current_function_obstack_index = 0;
+ current_function_obstack_usage = 0;
+
+ clear_temp_name ();
+
+ /* This should only be done once on the top most decl. */
+ if (have_extern_spec && !used_extern_spec)
+ {
+ declspecs = decl_tree_cons (NULL_TREE, get_identifier ("extern"), declspecs);
+ used_extern_spec = 1;
+ }
+
+ if (pre_parsed_p)
+ {
+ decl1 = declarator;
+
+ if (! DECL_ARGUMENTS (decl1)
+ && !DECL_STATIC_FUNCTION_P (decl1)
+ && DECL_CONTEXT (decl1)
+ && DECL_NAME (TYPE_NAME (DECL_CONTEXT (decl1)))
+ && IDENTIFIER_TEMPLATE (DECL_NAME (TYPE_NAME (DECL_CONTEXT (decl1)))))
+ {
+ cp_error ("redeclaration of `%#D'", decl1);
+ if (IDENTIFIER_CLASS_VALUE (DECL_NAME (decl1)))
+ cp_error_at ("previous declaration here", IDENTIFIER_CLASS_VALUE (DECL_NAME (decl1)));
+ else if (IDENTIFIER_GLOBAL_VALUE (DECL_NAME (decl1)))
+ cp_error_at ("previous declaration here", IDENTIFIER_GLOBAL_VALUE (DECL_NAME (decl1)));
+ }
+
+ last_function_parms = DECL_ARGUMENTS (decl1);
+ last_function_parm_tags = NULL_TREE;
+ fntype = TREE_TYPE (decl1);
+ if (TREE_CODE (fntype) == METHOD_TYPE)
+ ctype = TYPE_METHOD_BASETYPE (fntype);
+
+ /* ANSI C++ June 5 1992 WP 11.4.5. A friend function defined in a
+ class is in the (lexical) scope of the class in which it is
+ defined. */
+ if (!ctype && DECL_FRIEND_P (decl1))
+ {
+ ctype = DECL_CLASS_CONTEXT (decl1);
+
+ /* CTYPE could be null here if we're dealing with a template;
+ for example, `inline friend float foo()' inside a template
+ will have no CTYPE set. */
+ if (ctype && TREE_CODE (ctype) != RECORD_TYPE)
+ ctype = NULL_TREE;
+ else
+ doing_friend = 1;
+ }
+
+ if ( !(DECL_VINDEX (decl1)
+ && write_virtuals >= 2
+ && CLASSTYPE_VTABLE_NEEDS_WRITING (ctype)))
+ current_extern_inline = DECL_THIS_EXTERN (decl1) && DECL_INLINE (decl1);
+
+ raises = TYPE_RAISES_EXCEPTIONS (fntype);
+
+ /* In a fcn definition, arg types must be complete. */
+ require_complete_types_for_parms (last_function_parms);
+ }
+ else
+ {
+ decl1 = grokdeclarator (declarator, declspecs, FUNCDEF, 1, raises);
+ /* If the declarator is not suitable for a function definition,
+ cause a syntax error. */
+ if (decl1 == NULL_TREE || TREE_CODE (decl1) != FUNCTION_DECL) return 0;
+
+ fntype = TREE_TYPE (decl1);
+
+ restype = TREE_TYPE (fntype);
+ if (IS_AGGR_TYPE (restype) && ! TYPE_PTRMEMFUNC_P (restype)
+ && ! CLASSTYPE_GOT_SEMICOLON (restype))
+ {
+ cp_error ("semicolon missing after declaration of `%#T'", restype);
+ shadow_tag (build_tree_list (NULL_TREE, restype));
+ CLASSTYPE_GOT_SEMICOLON (restype) = 1;
+ if (TREE_CODE (fntype) == FUNCTION_TYPE)
+ fntype = build_function_type (integer_type_node,
+ TYPE_ARG_TYPES (fntype));
+ else
+ fntype = build_cplus_method_type (build_type_variant (TYPE_METHOD_BASETYPE (fntype), TREE_READONLY (decl1), TREE_SIDE_EFFECTS (decl1)),
+ integer_type_node,
+ TYPE_ARG_TYPES (fntype));
+ TREE_TYPE (decl1) = fntype;
+ }
+
+ if (TREE_CODE (fntype) == METHOD_TYPE)
+ ctype = TYPE_METHOD_BASETYPE (fntype);
+ else if (IDENTIFIER_LENGTH (DECL_NAME (decl1)) == 4
+ && ! strcmp (IDENTIFIER_POINTER (DECL_NAME (decl1)), "main")
+ && DECL_CONTEXT (decl1) == NULL_TREE)
+ {
+ /* If this doesn't return integer_type, complain. */
+ if (TREE_TYPE (TREE_TYPE (decl1)) != integer_type_node)
+ {
+ if (pedantic || warn_return_type)
+ warning ("return type for `main' changed to integer type");
+ TREE_TYPE (decl1) = fntype = default_function_type;
+ }
+ warn_about_return_type = 0;
+ }
+ }
+
+ /* Warn if function was previously implicitly declared
+ (but not if we warned then). */
+ if (! warn_implicit
+ && IDENTIFIER_IMPLICIT_DECL (DECL_NAME (decl1)) != NULL_TREE)
+ cp_warning_at ("`%D' implicitly declared before its definition", IDENTIFIER_IMPLICIT_DECL (DECL_NAME (decl1)));
+
+ current_function_decl = decl1;
+
+ if (flag_cadillac)
+ cadillac_start_function (decl1);
+ else
+ announce_function (decl1);
+
+ if (TYPE_SIZE (TREE_TYPE (fntype)) == NULL_TREE)
+ {
+ if (IS_AGGR_TYPE (TREE_TYPE (fntype)))
+ error_with_aggr_type (TREE_TYPE (fntype),
+ "return-type `%s' is an incomplete type");
+ else
+ error ("return-type is an incomplete type");
+
+ /* Make it return void instead, but don't change the
+ type of the DECL_RESULT, in case we have a named return value. */
+ if (ctype)
+ TREE_TYPE (decl1)
+ = build_cplus_method_type (build_type_variant (ctype,
+ TREE_READONLY (decl1),
+ TREE_SIDE_EFFECTS (decl1)),
+ void_type_node,
+ FUNCTION_ARG_CHAIN (decl1));
+ else
+ TREE_TYPE (decl1)
+ = build_function_type (void_type_node,
+ TYPE_ARG_TYPES (TREE_TYPE (decl1)));
+ DECL_RESULT (decl1) = build_decl (RESULT_DECL, 0, TREE_TYPE (fntype));
+ }
+
+ if (warn_about_return_type)
+ warning ("return-type defaults to `int'");
+
+ /* Make the init_value nonzero so pushdecl knows this is not tentative.
+ error_mark_node is replaced below (in poplevel) with the BLOCK. */
+ DECL_INITIAL (decl1) = error_mark_node;
+
+ /* Didn't get anything from C. */
+ olddecl = NULL_TREE;
+
+ /* This function exists in static storage.
+ (This does not mean `static' in the C sense!) */
+ TREE_STATIC (decl1) = 1;
+
+ /* Record the decl so that the function name is defined.
+ If we already have a decl for this name, and it is a FUNCTION_DECL,
+ use the old decl. */
+
+ if (pre_parsed_p == 0)
+ {
+ current_function_decl = decl1 = pushdecl (decl1);
+ DECL_MAIN_VARIANT (decl1) = decl1;
+ fntype = TREE_TYPE (decl1);
+ }
+ else
+ current_function_decl = decl1;
+
+ /* If this function belongs to an interface, it is public.
+ If it belongs to someone else's interface, it is also external.
+ It doesn't matter whether it's inline or not. */
+ if (interface_unknown == 0)
+ {
+ TREE_PUBLIC (decl1) = 1;
+ DECL_EXTERNAL (decl1)
+ = (interface_only
+ || (DECL_INLINE (decl1) && ! flag_implement_inlines));
+ }
+ else if (DECL_EXPLICIT_INSTANTIATION (decl1))
+ /* PUBLIC and EXTERNAL set by do_*_instantiation */;
+ else
+ {
+ /* This is a definition, not a reference.
+ So normally clear DECL_EXTERNAL.
+ However, `extern inline' acts like a declaration except for
+ defining how to inline. So set DECL_EXTERNAL in that case. */
+ DECL_EXTERNAL (decl1) = current_extern_inline;
+
+ DECL_DEFER_OUTPUT (decl1) = DECL_INLINE (decl1);
+ }
+
+ if (ctype != NULL_TREE && DECL_STATIC_FUNCTION_P (decl1))
+ {
+ if (TREE_CODE (fntype) == METHOD_TYPE)
+ TREE_TYPE (decl1) = fntype
+ = build_function_type (TREE_TYPE (fntype),
+ TREE_CHAIN (TYPE_ARG_TYPES (fntype)));
+ last_function_parms = TREE_CHAIN (last_function_parms);
+ DECL_ARGUMENTS (decl1) = last_function_parms;
+ ctype = NULL_TREE;
+ }
+ restype = TREE_TYPE (fntype);
+
+ pushlevel (0);
+ current_binding_level->parm_flag = 1;
+
+ /* Save the parm names or decls from this function's declarator
+ where store_parm_decls will find them. */
+ current_function_parms = last_function_parms;
+ current_function_parm_tags = last_function_parm_tags;
+
+ GNU_xref_function (decl1, current_function_parms);
+
+ make_function_rtl (decl1);
+
+ if (ctype)
+ {
+ push_nested_class (ctype, 1);
+
+ /* If we're compiling a friend function, neither of the variables
+ current_class_decl nor current_class_type will have values. */
+ if (! doing_friend)
+ {
+ /* We know that this was set up by `grokclassfn'.
+ We do not wait until `store_parm_decls', since evil
+ parse errors may never get us to that point. Here
+ we keep the consistency between `current_class_type'
+ and `current_class_decl'. */
+ current_class_decl = last_function_parms;
+ my_friendly_assert (current_class_decl != NULL_TREE
+ && TREE_CODE (current_class_decl) == PARM_DECL, 162);
+ if (TREE_CODE (TREE_TYPE (current_class_decl)) == POINTER_TYPE)
+ {
+ tree variant = TREE_TYPE (TREE_TYPE (current_class_decl));
+ if (CLASSTYPE_INST_VAR (ctype) == NULL_TREE)
+ {
+ /* Can't call build_indirect_ref here, because it has special
+ logic to return C_C_D given this argument. */
+ C_C_D = build1 (INDIRECT_REF, current_class_type, current_class_decl);
+ CLASSTYPE_INST_VAR (ctype) = C_C_D;
+ }
+ else
+ {
+ C_C_D = CLASSTYPE_INST_VAR (ctype);
+ /* `current_class_decl' is different for every
+ function we compile. */
+ TREE_OPERAND (C_C_D, 0) = current_class_decl;
+ }
+ TREE_READONLY (C_C_D) = TYPE_READONLY (variant);
+ TREE_SIDE_EFFECTS (C_C_D) = TYPE_VOLATILE (variant);
+ TREE_THIS_VOLATILE (C_C_D) = TYPE_VOLATILE (variant);
+ }
+ else
+ C_C_D = current_class_decl;
+ }
+ }
+ else
+ {
+ if (DECL_STATIC_FUNCTION_P (decl1))
+ push_nested_class (DECL_CONTEXT (decl1), 2);
+ else
+ push_memoized_context (0, 1);
+ }
+
+ /* Allocate further tree nodes temporarily during compilation
+ of this function only. Tiemann moved up here from bottom of fn. */
+ temporary_allocation ();
+
+ /* Promote the value to int before returning it. */
+ if (C_PROMOTING_INTEGER_TYPE_P (restype))
+ {
+ /* It retains unsignedness if traditional or if it isn't
+ really getting wider. */
+ if (TREE_UNSIGNED (restype)
+ && (flag_traditional
+ || TYPE_PRECISION (restype)
+ == TYPE_PRECISION (integer_type_node)))
+ restype = unsigned_type_node;
+ else
+ restype = integer_type_node;
+ }
+ if (DECL_RESULT (decl1) == NULL_TREE)
+ DECL_RESULT (decl1) = build_decl (RESULT_DECL, 0, restype);
+
+ if (DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (decl1)))
+ {
+ dtor_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+ ctor_label = NULL_TREE;
+ }
+ else
+ {
+ dtor_label = NULL_TREE;
+ if (DECL_CONSTRUCTOR_P (decl1))
+ ctor_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+ }
+
+ /* If this fcn was already referenced via a block-scope `extern' decl
+ (or an implicit decl), propagate certain information about the usage. */
+ if (TREE_ADDRESSABLE (DECL_ASSEMBLER_NAME (decl1)))
+ TREE_ADDRESSABLE (decl1) = 1;
+
+ return 1;
+}
+
+/* Store the parameter declarations into the current function declaration.
+ This is called after parsing the parameter declarations, before
+ digesting the body of the function.
+
+ Also install to binding contour return value identifier, if any. */
+
+void
+store_parm_decls ()
+{
+ register tree fndecl = current_function_decl;
+ register tree parm;
+ int parms_have_cleanups = 0;
+
+ /* This is either a chain of PARM_DECLs (when a prototype is used). */
+ tree specparms = current_function_parms;
+
+ /* This is a list of types declared among parms in a prototype. */
+ tree parmtags = current_function_parm_tags;
+
+ /* This is a chain of any other decls that came in among the parm
+ declarations. If a parm is declared with enum {foo, bar} x;
+ then CONST_DECLs for foo and bar are put here. */
+ tree nonparms = NULL_TREE;
+
+ if (current_binding_level == global_binding_level)
+ fatal ("parse errors have confused me too much");
+
+ /* Initialize RTL machinery. */
+ init_function_start (fndecl, input_filename, lineno);
+
+ /* Declare __FUNCTION__ and __PRETTY_FUNCTION__ for this function. */
+ declare_function_name ();
+
+ /* Create a binding level for the parms. */
+ expand_start_bindings (0);
+
+ if (specparms != NULL_TREE)
+ {
+ /* This case is when the function was defined with an ANSI prototype.
+ The parms already have decls, so we need not do anything here
+ except record them as in effect
+ and complain if any redundant old-style parm decls were written. */
+
+ register tree next;
+
+ /* Must clear this because it might contain TYPE_DECLs declared
+ at class level. */
+ storedecls (NULL_TREE);
+ for (parm = nreverse (specparms); parm; parm = next)
+ {
+ next = TREE_CHAIN (parm);
+ if (TREE_CODE (parm) == PARM_DECL)
+ {
+ tree cleanup = maybe_build_cleanup (parm);
+ if (DECL_NAME (parm) == NULL_TREE)
+ {
+#if 0
+ cp_error_at ("parameter name omitted", parm);
+#else
+ /* for C++, this is not an error. */
+ pushdecl (parm);
+#endif
+ }
+ else if (TYPE_MAIN_VARIANT (TREE_TYPE (parm)) == void_type_node)
+ cp_error ("parameter `%D' declared void", parm);
+ else
+ {
+ /* Now fill in DECL_REFERENCE_SLOT for any of the parm decls.
+ A parameter is assumed not to have any side effects.
+ If this should change for any reason, then this
+ will have to wrap the bashed reference type in a save_expr.
+
+ Also, if the parameter type is declared to be an X
+ and there is an X(X&) constructor, we cannot lay it
+ into the stack (any more), so we make this parameter
+ look like it is really of reference type. Functions
+ which pass parameters to this function will know to
+ create a temporary in their frame, and pass a reference
+ to that. */
+
+ if (TREE_CODE (TREE_TYPE (parm)) == REFERENCE_TYPE
+ && TYPE_SIZE (TREE_TYPE (TREE_TYPE (parm))))
+ SET_DECL_REFERENCE_SLOT (parm, convert_from_reference (parm));
+
+ pushdecl (parm);
+ }
+ if (cleanup)
+ {
+ expand_decl (parm);
+ if (! expand_decl_cleanup (parm, cleanup))
+ cp_error ("parser lost in parsing declaration of `%D'",
+ parm);
+ parms_have_cleanups = 1;
+ }
+ }
+ else
+ {
+ /* If we find an enum constant or a type tag,
+ put it aside for the moment. */
+ TREE_CHAIN (parm) = NULL_TREE;
+ nonparms = chainon (nonparms, parm);
+ }
+ }
+
+ /* Get the decls in their original chain order
+ and record in the function. This is all and only the
+ PARM_DECLs that were pushed into scope by the loop above. */
+ DECL_ARGUMENTS (fndecl) = getdecls ();
+
+ storetags (chainon (parmtags, gettags ()));
+ }
+ else
+ DECL_ARGUMENTS (fndecl) = NULL_TREE;
+
+ /* Now store the final chain of decls for the arguments
+ as the decl-chain of the current lexical scope.
+ Put the enumerators in as well, at the front so that
+ DECL_ARGUMENTS is not modified. */
+
+ storedecls (chainon (nonparms, DECL_ARGUMENTS (fndecl)));
+
+ /* Initialize the RTL code for the function. */
+ DECL_SAVED_INSNS (fndecl) = NULL_RTX;
+ expand_function_start (fndecl, parms_have_cleanups);
+
+ /* Create a binding contour which can be used to catch
+ cleanup-generated temporaries. Also, if the return value needs or
+ has initialization, deal with that now. */
+ if (parms_have_cleanups)
+ {
+ pushlevel (0);
+ expand_start_bindings (0);
+ }
+
+ current_function_parms_stored = 1;
+
+ if (flag_gc)
+ {
+ maybe_gc_cleanup = build_tree_list (NULL_TREE, error_mark_node);
+ if (! expand_decl_cleanup (NULL_TREE, maybe_gc_cleanup))
+ cp_error ("parser lost in parsing declaration of `%D'", fndecl);
+ }
+
+ /* If this function is `main', emit a call to `__main'
+ to run global initializers, etc. */
+ if (DECL_NAME (fndecl)
+ && IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 4
+ && strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), "main") == 0
+ && DECL_CONTEXT (fndecl) == NULL_TREE)
+ {
+ expand_main_function ();
+
+ if (flag_gc)
+ expand_expr (build_function_call (lookup_name (get_identifier ("__gc_main"), 0), NULL_TREE),
+ 0, VOIDmode, 0);
+
+ if (flag_dossier)
+ output_builtin_tdesc_entries ();
+ }
+}
+
+/* Bind a name and initialization to the return value of
+ the current function. */
+void
+store_return_init (return_id, init)
+ tree return_id, init;
+{
+ tree decl = DECL_RESULT (current_function_decl);
+
+ if (flag_ansi)
+ /* Give this error as many times as there are occurrences,
+ so that users can use Emacs compilation buffers to find
+ and fix all such places. */
+ pedwarn ("ANSI C++ does not permit named return values");
+
+ if (return_id != NULL_TREE)
+ {
+ if (DECL_NAME (decl) == NULL_TREE)
+ {
+ DECL_NAME (decl) = return_id;
+ DECL_ASSEMBLER_NAME (decl) = return_id;
+ }
+ else
+ error ("return identifier `%s' already in place",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ }
+
+ /* Can't let this happen for constructors. */
+ if (DECL_CONSTRUCTOR_P (current_function_decl))
+ {
+ error ("can't redefine default return value for constructors");
+ return;
+ }
+
+ /* If we have a named return value, put that in our scope as well. */
+ if (DECL_NAME (decl) != NULL_TREE)
+ {
+ /* If this named return value comes in a register,
+ put it in a pseudo-register. */
+ if (DECL_REGISTER (decl))
+ {
+ original_result_rtx = DECL_RTL (decl);
+ DECL_RTL (decl) = gen_reg_rtx (DECL_MODE (decl));
+ }
+
+ /* Let `finish_decl' know that this initializer is ok. */
+ DECL_INITIAL (decl) = init;
+ pushdecl (decl);
+ finish_decl (decl, init, 0, 0);
+ }
+}
+
+#if 0
+/* Generate code for default X() constructor. */
+static void
+build_default_constructor (fndecl)
+ tree fndecl;
+{
+ int i = CLASSTYPE_N_BASECLASSES (current_class_type);
+ tree parm = TREE_CHAIN (DECL_ARGUMENTS (fndecl));
+ tree fields = TYPE_FIELDS (current_class_type);
+ tree binfos = TYPE_BINFO_BASETYPES (current_class_type);
+
+ if (TYPE_USES_VIRTUAL_BASECLASSES (current_class_type))
+ parm = TREE_CHAIN (parm);
+ parm = DECL_REFERENCE_SLOT (parm);
+
+ while (--i >= 0)
+ {
+ tree basetype = TREE_VEC_ELT (binfos, i);
+ if (TYPE_HAS_INIT_REF (basetype))
+ {
+ tree name = TYPE_NAME (basetype);
+ if (TREE_CODE (name) == TYPE_DECL)
+ name = DECL_NAME (name);
+ current_base_init_list = tree_cons (name, parm, current_base_init_list);
+ }
+ }
+ for (; fields; fields = TREE_CHAIN (fields))
+ {
+ tree name, init;
+ if (TREE_STATIC (fields))
+ continue;
+ if (TREE_CODE (fields) != FIELD_DECL)
+ continue;
+ if (DECL_NAME (fields))
+ {
+ if (VFIELD_NAME_P (DECL_NAME (fields)))
+ continue;
+ if (VBASE_NAME_P (DECL_NAME (fields)))
+ continue;
+
+ /* True for duplicate members. */
+ if (IDENTIFIER_CLASS_VALUE (DECL_NAME (fields)) != fields)
+ continue;
+ }
+
+ init = build (COMPONENT_REF, TREE_TYPE (fields), parm, fields);
+ init = build_tree_list (NULL_TREE, init);
+
+ current_member_init_list
+ = tree_cons (DECL_NAME (fields), init, current_member_init_list);
+ }
+}
+#endif
+
+/* Finish up a function declaration and compile that function
+ all the way to assembler language output. The free the storage
+ for the function definition.
+
+ This is called after parsing the body of the function definition.
+ LINENO is the current line number.
+
+ C++: CALL_POPLEVEL is non-zero if an extra call to poplevel
+ (and expand_end_bindings) must be made to take care of the binding
+ contour for the base initializers. This is only relevant for
+ constructors. */
+
+void
+finish_function (lineno, call_poplevel)
+ int lineno;
+ int call_poplevel;
+{
+ register tree fndecl = current_function_decl;
+ tree fntype, ctype = NULL_TREE;
+ rtx head, last_parm_insn, mark;
+ /* Label to use if this function is supposed to return a value. */
+ tree no_return_label = NULL_TREE;
+ tree decls = NULL_TREE;
+
+ /* When we get some parse errors, we can end up without a
+ current_function_decl, so cope. */
+ if (fndecl == NULL_TREE)
+ return;
+
+ fntype = TREE_TYPE (fndecl);
+
+/* TREE_READONLY (fndecl) = 1;
+ This caused &foo to be of type ptr-to-const-function
+ which then got a warning when stored in a ptr-to-function variable. */
+
+ /* This happens on strange parse errors. */
+ if (! current_function_parms_stored)
+ {
+ call_poplevel = 0;
+ store_parm_decls ();
+ }
+
+ if (write_symbols != NO_DEBUG && TREE_CODE (fntype) != METHOD_TYPE)
+ {
+ tree ttype = target_type (fntype);
+ tree parmdecl;
+
+ if (IS_AGGR_TYPE (ttype))
+ /* Let debugger know it should output info for this type. */
+ note_debug_info_needed (ttype);
+
+ for (parmdecl = DECL_ARGUMENTS (fndecl); parmdecl; parmdecl = TREE_CHAIN (parmdecl))
+ {
+ ttype = target_type (TREE_TYPE (parmdecl));
+ if (IS_AGGR_TYPE (ttype))
+ /* Let debugger know it should output info for this type. */
+ note_debug_info_needed (ttype);
+ }
+ }
+
+ /* Clean house because we will need to reorder insns here. */
+ do_pending_stack_adjust ();
+
+ if (dtor_label)
+ {
+ tree binfo = TYPE_BINFO (current_class_type);
+ tree cond = integer_one_node;
+ tree exprstmt, vfields;
+ tree in_charge_node = lookup_name (in_charge_identifier, 0);
+ tree virtual_size;
+ int ok_to_optimize_dtor = 0;
+
+ if (current_function_assigns_this)
+ cond = build (NE_EXPR, integer_type_node,
+ current_class_decl, integer_zero_node);
+ else
+ {
+ int n_baseclasses = CLASSTYPE_N_BASECLASSES (current_class_type);
+
+ /* If this destructor is empty, then we don't need to check
+ whether `this' is NULL in some cases. */
+ mark = get_last_insn ();
+ last_parm_insn = get_first_nonparm_insn ();
+
+ if ((flag_this_is_variable & 1) == 0)
+ ok_to_optimize_dtor = 1;
+ else if (mark == last_parm_insn)
+ ok_to_optimize_dtor
+ = (n_baseclasses == 0
+ || (n_baseclasses == 1
+ && TYPE_HAS_DESTRUCTOR (TYPE_BINFO_BASETYPE (current_class_type, 0))));
+ }
+
+ /* These initializations might go inline. Protect
+ the binding level of the parms. */
+ pushlevel (0);
+ expand_start_bindings (0);
+
+ if (current_function_assigns_this)
+ {
+ current_function_assigns_this = 0;
+ current_function_just_assigned_this = 0;
+ }
+
+ /* Generate the code to call destructor on base class.
+ If this destructor belongs to a class with virtual
+ functions, then set the virtual function table
+ pointer to represent the type of our base class. */
+
+ /* This side-effect makes call to `build_delete' generate the
+ code we have to have at the end of this destructor. */
+ TYPE_HAS_DESTRUCTOR (current_class_type) = 0;
+
+ /* These are two cases where we cannot delegate deletion. */
+ if (TYPE_USES_VIRTUAL_BASECLASSES (current_class_type)
+ || TYPE_GETS_REG_DELETE (current_class_type))
+ exprstmt = build_delete (current_class_type, C_C_D, integer_zero_node,
+ LOOKUP_NONVIRTUAL|LOOKUP_DESTRUCTOR, 0);
+ else
+ exprstmt = build_delete (current_class_type, C_C_D, in_charge_node,
+ LOOKUP_NONVIRTUAL|LOOKUP_DESTRUCTOR, 0);
+
+ /* If we did not assign to this, then `this' is non-zero at
+ the end of a destructor. As a special optimization, don't
+ emit test if this is an empty destructor. If it does nothing,
+ it does nothing. If it calls a base destructor, the base
+ destructor will perform the test. */
+
+ if (exprstmt != error_mark_node
+ && (TREE_CODE (exprstmt) != NOP_EXPR
+ || TREE_OPERAND (exprstmt, 0) != integer_zero_node
+ || TYPE_USES_VIRTUAL_BASECLASSES (current_class_type)))
+ {
+ expand_label (dtor_label);
+ if (cond != integer_one_node)
+ expand_start_cond (cond, 0);
+ if (exprstmt != void_zero_node)
+ /* Don't call `expand_expr_stmt' if we're not going to do
+ anything, since -Wall will give a diagnostic. */
+ expand_expr_stmt (exprstmt);
+
+ /* Run destructor on all virtual baseclasses. */
+ if (TYPE_USES_VIRTUAL_BASECLASSES (current_class_type))
+ {
+ tree vbases = nreverse (copy_list (CLASSTYPE_VBASECLASSES (current_class_type)));
+ expand_start_cond (build (BIT_AND_EXPR, integer_type_node,
+ in_charge_node, integer_two_node), 0);
+ while (vbases)
+ {
+ if (TYPE_NEEDS_DESTRUCTOR (BINFO_TYPE (vbases)))
+ {
+ tree ptr = convert_pointer_to_vbase (vbases, current_class_decl);
+ expand_expr_stmt (build_delete (TYPE_POINTER_TO (BINFO_TYPE (vbases)),
+ ptr, integer_zero_node,
+ LOOKUP_NONVIRTUAL|LOOKUP_DESTRUCTOR|LOOKUP_HAS_IN_CHARGE, 0));
+ }
+ vbases = TREE_CHAIN (vbases);
+ }
+ expand_end_cond ();
+ }
+
+ do_pending_stack_adjust ();
+ if (cond != integer_one_node)
+ expand_end_cond ();
+ }
+
+ TYPE_HAS_DESTRUCTOR (current_class_type) = 1;
+
+ virtual_size = c_sizeof (current_class_type);
+
+ /* At the end, call delete if that's what's requested. */
+ if (TYPE_GETS_REG_DELETE (current_class_type))
+ /* This NOP_EXPR means we are in a static call context. */
+ exprstmt =
+ build_method_call
+ (build_indirect_ref
+ (build1 (NOP_EXPR, TYPE_POINTER_TO (current_class_type),
+ error_mark_node),
+ NULL_PTR),
+ ansi_opname[(int) DELETE_EXPR],
+ tree_cons (NULL_TREE, current_class_decl,
+ build_tree_list (NULL_TREE, virtual_size)),
+ NULL_TREE, LOOKUP_NORMAL);
+ else if (TYPE_USES_VIRTUAL_BASECLASSES (current_class_type))
+ exprstmt = build_x_delete (ptr_type_node, current_class_decl, 0,
+ virtual_size);
+ else
+ exprstmt = NULL_TREE;
+
+ if (exprstmt)
+ {
+ cond = build (BIT_AND_EXPR, integer_type_node,
+ in_charge_node, integer_one_node);
+ expand_start_cond (cond, 0);
+ expand_expr_stmt (exprstmt);
+ expand_end_cond ();
+ }
+
+ /* End of destructor. */
+ expand_end_bindings (NULL_TREE, getdecls() != NULL_TREE, 0);
+ poplevel (2, 0, 0); /* XXX change to 1 */
+
+ /* Back to the top of destructor. */
+ /* Dont execute destructor code if `this' is NULL. */
+ mark = get_last_insn ();
+ last_parm_insn = get_first_nonparm_insn ();
+ if (last_parm_insn == NULL_RTX)
+ last_parm_insn = mark;
+ else
+ last_parm_insn = previous_insn (last_parm_insn);
+
+ /* Make all virtual function table pointers in non-virtual base
+ classes point to CURRENT_CLASS_TYPE's virtual function
+ tables. */
+ expand_direct_vtbls_init (binfo, binfo, 1, 0, current_class_decl);
+ if (TYPE_USES_VIRTUAL_BASECLASSES (current_class_type))
+ expand_indirect_vtbls_init (binfo, C_C_D, current_class_decl, 0);
+ if (! ok_to_optimize_dtor)
+ {
+ cond = build_binary_op (NE_EXPR,
+ current_class_decl, integer_zero_node, 1);
+ expand_start_cond (cond, 0);
+ }
+ if (mark != get_last_insn ())
+ reorder_insns (next_insn (mark), get_last_insn (), last_parm_insn);
+ if (! ok_to_optimize_dtor)
+ expand_end_cond ();
+ }
+ else if (current_function_assigns_this)
+ {
+ /* Does not need to call emit_base_init, because
+ that is done (if needed) just after assignment to this
+ is seen. */
+
+ if (DECL_CONSTRUCTOR_P (current_function_decl))
+ {
+ expand_label (ctor_label);
+ ctor_label = NULL_TREE;
+
+ if (call_poplevel)
+ {
+ decls = getdecls ();
+ expand_end_bindings (decls, decls != NULL_TREE, 0);
+ poplevel (decls != NULL_TREE, 0, 0);
+ }
+ c_expand_return (current_class_decl);
+ }
+ else if (TYPE_MAIN_VARIANT (TREE_TYPE (
+ DECL_RESULT (current_function_decl))) != void_type_node
+ && return_label != NULL_RTX)
+ no_return_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+
+ current_function_assigns_this = 0;
+ current_function_just_assigned_this = 0;
+ base_init_insns = NULL_RTX;
+ }
+ else if (DECL_CONSTRUCTOR_P (fndecl))
+ {
+ tree allocated_this;
+ tree cond, thenclause;
+ /* Allow constructor for a type to get a new instance of the object
+ using `build_new'. */
+ tree abstract_virtuals = CLASSTYPE_ABSTRACT_VIRTUALS (current_class_type);
+ CLASSTYPE_ABSTRACT_VIRTUALS (current_class_type) = NULL_TREE;
+
+ DECL_RETURNS_FIRST_ARG (fndecl) = 1;
+
+ if (flag_this_is_variable > 0)
+ {
+ cond = build_binary_op (EQ_EXPR,
+ current_class_decl, integer_zero_node, 1);
+ thenclause = build_modify_expr (current_class_decl, NOP_EXPR,
+ build_new (NULL_TREE, current_class_type, void_type_node, 0));
+ }
+
+ CLASSTYPE_ABSTRACT_VIRTUALS (current_class_type) = abstract_virtuals;
+
+ /* must keep the first insn safe. */
+ head = get_insns ();
+
+ /* this note will come up to the top with us. */
+ mark = get_last_insn ();
+
+ if (flag_this_is_variable > 0)
+ {
+ expand_start_cond (cond, 0);
+ expand_expr_stmt (thenclause);
+ expand_end_cond ();
+ }
+
+#if 0
+ if (DECL_NAME (fndecl) == NULL_TREE
+ && TREE_CHAIN (DECL_ARGUMENTS (fndecl)) != NULL_TREE)
+ build_default_constructor (fndecl);
+#endif
+
+ /* Emit insns from `emit_base_init' which sets up virtual
+ function table pointer(s). */
+ emit_insns (base_init_insns);
+ base_init_insns = NULL_RTX;
+
+ /* This is where the body of the constructor begins.
+ If there were no insns in this function body, then the
+ last_parm_insn is also the last insn.
+
+ If optimization is enabled, last_parm_insn may move, so
+ we don't hold on to it (across emit_base_init). */
+ last_parm_insn = get_first_nonparm_insn ();
+ if (last_parm_insn == NULL_RTX)
+ last_parm_insn = mark;
+ else
+ last_parm_insn = previous_insn (last_parm_insn);
+
+ if (mark != get_last_insn ())
+ reorder_insns (next_insn (mark), get_last_insn (), last_parm_insn);
+
+ /* This is where the body of the constructor ends. */
+ expand_label (ctor_label);
+ ctor_label = NULL_TREE;
+
+ if (call_poplevel)
+ {
+ expand_end_bindings (decls = getdecls (), decls != NULL_TREE, 0);
+ poplevel (decls != NULL_TREE, 1, 0);
+ }
+
+ c_expand_return (current_class_decl);
+
+ current_function_assigns_this = 0;
+ current_function_just_assigned_this = 0;
+ }
+ else if (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 4
+ && ! strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), "main")
+ && DECL_CONTEXT (fndecl) == NULL_TREE)
+ {
+ /* Make it so that `main' always returns 0 by default. */
+#ifdef VMS
+ c_expand_return (integer_one_node);
+#else
+ c_expand_return (integer_zero_node);
+#endif
+ }
+ else if (return_label != NULL_RTX
+ && current_function_return_value == NULL_TREE
+ && ! DECL_NAME (DECL_RESULT (current_function_decl)))
+ no_return_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+
+ if (flag_gc)
+ expand_gc_prologue_and_epilogue ();
+
+ /* That's the end of the vtable decl's life. Need to mark it such
+ if doing stupid register allocation.
+
+ Note that current_vtable_decl is really an INDIRECT_REF
+ on top of a VAR_DECL here. */
+ if (obey_regdecls && current_vtable_decl)
+ use_variable (DECL_RTL (TREE_OPERAND (current_vtable_decl, 0)));
+
+ /* If this function is supposed to return a value, ensure that
+ we do not fall into the cleanups by mistake. The end of our
+ function will look like this:
+
+ user code (may have return stmt somewhere)
+ goto no_return_label
+ cleanup_label:
+ cleanups
+ goto return_label
+ no_return_label:
+ NOTE_INSN_FUNCTION_END
+ return_label:
+ things for return
+
+ If the user omits a return stmt in the USER CODE section, we
+ will have a control path which reaches NOTE_INSN_FUNCTION_END.
+ Otherwise, we won't. */
+ if (no_return_label)
+ {
+ DECL_CONTEXT (no_return_label) = fndecl;
+ DECL_INITIAL (no_return_label) = error_mark_node;
+ DECL_SOURCE_FILE (no_return_label) = input_filename;
+ DECL_SOURCE_LINE (no_return_label) = lineno;
+ expand_goto (no_return_label);
+ }
+
+ if (cleanup_label)
+ {
+ /* remove the binding contour which is used
+ to catch cleanup-generated temporaries. */
+ expand_end_bindings (0, 0, 0);
+ poplevel (0, 0, 0);
+ }
+
+ if (cleanup_label)
+ /* Emit label at beginning of cleanup code for parameters. */
+ emit_label (cleanup_label);
+
+ /* Get return value into register if that's where it's supposed to be. */
+ if (original_result_rtx)
+ fixup_result_decl (DECL_RESULT (fndecl), original_result_rtx);
+
+ /* Finish building code that will trigger warnings if users forget
+ to make their functions return values. */
+ if (no_return_label || cleanup_label)
+ emit_jump (return_label);
+ if (no_return_label)
+ {
+ /* We don't need to call `expand_*_return' here because we
+ don't need any cleanups here--this path of code is only
+ for error checking purposes. */
+ expand_label (no_return_label);
+ }
+
+ /* reset scope for C++: if we were in the scope of a class,
+ then when we finish this function, we are not longer so.
+ This cannot be done until we know for sure that no more
+ class members will ever be referenced in this function
+ (i.e., calls to destructors). */
+ if (current_class_name)
+ {
+ ctype = current_class_type;
+ pop_nested_class (1);
+ }
+ else
+ pop_memoized_context (1);
+
+ /* Generate rtl for function exit. */
+ expand_function_end (input_filename, lineno, 1);
+
+ if (flag_handle_exceptions)
+ expand_exception_blocks();
+
+ /* This must come after expand_function_end because cleanups might
+ have declarations (from inline functions) that need to go into
+ this function's blocks. */
+ if (current_binding_level->parm_flag != 1)
+ my_friendly_abort (122);
+ poplevel (1, 0, 1);
+
+ /* Must mark the RESULT_DECL as being in this function. */
+ DECL_CONTEXT (DECL_RESULT (fndecl)) = DECL_INITIAL (fndecl);
+
+ /* Obey `register' declarations if `setjmp' is called in this fn. */
+ if (flag_traditional && current_function_calls_setjmp)
+ setjmp_protect (DECL_INITIAL (fndecl));
+
+ /* Set the BLOCK_SUPERCONTEXT of the outermost function scope to point
+ to the FUNCTION_DECL node itself. */
+ BLOCK_SUPERCONTEXT (DECL_INITIAL (fndecl)) = fndecl;
+
+ /* So we can tell if jump_optimize sets it to 1. */
+ can_reach_end = 0;
+
+ /* ??? Compensate for Sun brain damage in dealing with data segments
+ of PIC code. */
+ if (flag_pic
+ && (DECL_CONSTRUCTOR_P (fndecl)
+ || DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (fndecl)))
+ && CLASSTYPE_NEEDS_VIRTUAL_REINIT (TYPE_METHOD_BASETYPE (fntype)))
+ DECL_INLINE (fndecl) = 0;
+
+ if (DECL_EXTERNAL (fndecl)
+ /* This function is just along for the ride. If we can make
+ it inline, that's great. Otherwise, just punt it. */
+ && (DECL_INLINE (fndecl) == 0
+ || flag_no_inline
+ || function_cannot_inline_p (fndecl)))
+ {
+ extern int rtl_dump_and_exit;
+ int old_rtl_dump_and_exit = rtl_dump_and_exit;
+ int inline_spec = DECL_INLINE (fndecl);
+
+ /* This throws away the code for FNDECL. */
+ rtl_dump_and_exit = 1;
+ /* This throws away the memory of the code for FNDECL. */
+ if (flag_no_inline)
+ DECL_INLINE (fndecl) = 0;
+ rest_of_compilation (fndecl);
+ rtl_dump_and_exit = old_rtl_dump_and_exit;
+ DECL_INLINE (fndecl) = inline_spec;
+ }
+ else
+ {
+ /* Run the optimizers and output the assembler code for this
+ function. */
+ rest_of_compilation (fndecl);
+ }
+
+ if (DECL_INLINE (fndecl)
+ && !TREE_ASM_WRITTEN (fndecl) && DECL_FUNCTION_MEMBER_P (fndecl))
+ {
+ mark_inline_for_output (fndecl);
+ }
+
+ if (ctype && TREE_ASM_WRITTEN (fndecl))
+ note_debug_info_needed (ctype);
+
+ current_function_returns_null |= can_reach_end;
+
+ /* Since we don't normally go through c_expand_return for constructors,
+ this normally gets the wrong value.
+ Also, named return values have their return codes emitted after
+ NOTE_INSN_FUNCTION_END, confusing jump.c. */
+ if (DECL_CONSTRUCTOR_P (fndecl)
+ || DECL_NAME (DECL_RESULT (fndecl)) != NULL_TREE)
+ current_function_returns_null = 0;
+
+ if (TREE_THIS_VOLATILE (fndecl) && current_function_returns_null)
+ cp_warning ("`noreturn' function `%D' does return", fndecl);
+ else if ((warn_return_type || pedantic)
+ && current_function_returns_null
+ && TYPE_MAIN_VARIANT (TREE_TYPE (fntype)) != void_type_node)
+ {
+ /* If this function returns non-void and control can drop through,
+ complain. */
+ cp_pedwarn ("control reaches end of non-void function `%D'", fndecl);
+ }
+ /* With just -W, complain only if function returns both with
+ and without a value. */
+ else if (extra_warnings
+ && current_function_returns_value && current_function_returns_null)
+ warning ("this function may return with or without a value");
+
+ /* Free all the tree nodes making up this function. */
+ /* Switch back to allocating nodes permanently
+ until we start another function. */
+ permanent_allocation (1);
+
+ if (flag_cadillac)
+ cadillac_finish_function (fndecl);
+
+ if (DECL_SAVED_INSNS (fndecl) == NULL_RTX)
+ {
+ /* Stop pointing to the local nodes about to be freed. */
+ /* But DECL_INITIAL must remain nonzero so we know this
+ was an actual function definition. */
+ DECL_INITIAL (fndecl) = error_mark_node;
+ if (! DECL_CONSTRUCTOR_P (fndecl)
+ || !TYPE_USES_VIRTUAL_BASECLASSES (TYPE_METHOD_BASETYPE (fntype)))
+ DECL_ARGUMENTS (fndecl) = NULL_TREE;
+ }
+
+ /* Let the error reporting routines know that we're outside a function. */
+ current_function_decl = NULL_TREE;
+ named_label_uses = NULL_TREE;
+}
+
+/* Create the FUNCTION_DECL for a function definition.
+ LINE1 is the line number that the definition absolutely begins on.
+ LINE2 is the line number that the name of the function appears on.
+ DECLSPECS and DECLARATOR are the parts of the declaration;
+ they describe the return type and the name of the function,
+ but twisted together in a fashion that parallels the syntax of C.
+
+ This function creates a binding context for the function body
+ as well as setting up the FUNCTION_DECL in current_function_decl.
+
+ Returns a FUNCTION_DECL on success.
+
+ If the DECLARATOR is not suitable for a function (it defines a datum
+ instead), we return 0, which tells yyparse to report a parse error.
+
+ May return void_type_node indicating that this method is actually
+ a friend. See grokfield for more details.
+
+ Came here with a `.pushlevel' .
+
+ DO NOT MAKE ANY CHANGES TO THIS CODE WITHOUT MAKING CORRESPONDING
+ CHANGES TO CODE IN `grokfield'. */
+tree
+start_method (declspecs, declarator, raises)
+ tree declarator, declspecs, raises;
+{
+ tree fndecl = grokdeclarator (declarator, declspecs, MEMFUNCDEF, 0, raises);
+
+ /* Something too ugly to handle. */
+ if (fndecl == NULL_TREE)
+ return NULL_TREE;
+
+ /* Pass friends other than inline friend functions back. */
+ if (TYPE_MAIN_VARIANT (fndecl) == void_type_node)
+ return fndecl;
+
+ if (TREE_CODE (fndecl) != FUNCTION_DECL)
+ /* Not a function, tell parser to report parse error. */
+ return NULL_TREE;
+
+ if (IS_SIGNATURE (current_class_type))
+ {
+ IS_DEFAULT_IMPLEMENTATION (fndecl) = 1;
+ /* In case we need this info later. */
+ HAS_DEFAULT_IMPLEMENTATION (current_class_type) = 1;
+ }
+
+ if (DECL_IN_AGGR_P (fndecl))
+ {
+ if (IDENTIFIER_ERROR_LOCUS (DECL_ASSEMBLER_NAME (fndecl)) != current_class_type)
+ {
+ if (DECL_CONTEXT (fndecl))
+ cp_error ("`%D' is already defined in class %s", fndecl,
+ TYPE_NAME_STRING (DECL_CONTEXT (fndecl)));
+ }
+ return void_type_node;
+ }
+
+ if (flag_default_inline)
+ DECL_INLINE (fndecl) = 1;
+
+ if (processing_template_defn)
+ SET_DECL_IMPLICIT_INSTANTIATION (fndecl);
+
+ /* We read in the parameters on the maybepermanent_obstack,
+ but we won't be getting back to them until after we
+ may have clobbered them. So the call to preserve_data
+ will keep them safe. */
+ preserve_data ();
+
+ if (! DECL_FRIEND_P (fndecl))
+ {
+ if (DECL_CHAIN (fndecl) != NULL_TREE)
+ {
+ /* Need a fresh node here so that we don't get circularity
+ when we link these together. If FNDECL was a friend, then
+ `pushdecl' does the right thing, which is nothing wrt its
+ current value of DECL_CHAIN. */
+ fndecl = copy_node (fndecl);
+ }
+ if (TREE_CHAIN (fndecl))
+ {
+ fndecl = copy_node (fndecl);
+ TREE_CHAIN (fndecl) = NULL_TREE;
+ }
+
+ if (DECL_CONSTRUCTOR_P (fndecl))
+ {
+ if (! grok_ctor_properties (current_class_type, fndecl))
+ return void_type_node;
+ }
+ else if (IDENTIFIER_OPNAME_P (DECL_NAME (fndecl)))
+ grok_op_properties (fndecl, DECL_VIRTUAL_P (fndecl), 0);
+ }
+
+ finish_decl (fndecl, NULL_TREE, NULL_TREE, 0);
+
+ /* Make a place for the parms */
+ pushlevel (0);
+ current_binding_level->parm_flag = 1;
+
+ DECL_IN_AGGR_P (fndecl) = 1;
+ return fndecl;
+}
+
+/* Go through the motions of finishing a function definition.
+ We don't compile this method until after the whole class has
+ been processed.
+
+ FINISH_METHOD must return something that looks as though it
+ came from GROKFIELD (since we are defining a method, after all).
+
+ This is called after parsing the body of the function definition.
+ STMTS is the chain of statements that makes up the function body.
+
+ DECL is the ..._DECL that `start_method' provided. */
+
+tree
+finish_method (decl)
+ tree decl;
+{
+ register tree fndecl = decl;
+ tree old_initial;
+
+ register tree link;
+
+ if (TYPE_MAIN_VARIANT (decl) == void_type_node)
+ return decl;
+
+ old_initial = DECL_INITIAL (fndecl);
+
+ /* Undo the level for the parms (from start_method).
+ This is like poplevel, but it causes nothing to be
+ saved. Saving information here confuses symbol-table
+ output routines. Besides, this information will
+ be correctly output when this method is actually
+ compiled. */
+
+ /* Clear out the meanings of the local variables of this level;
+ also record in each decl which block it belongs to. */
+
+ for (link = current_binding_level->names; link; link = TREE_CHAIN (link))
+ {
+ if (DECL_NAME (link) != NULL_TREE)
+ IDENTIFIER_LOCAL_VALUE (DECL_NAME (link)) = 0;
+ my_friendly_assert (TREE_CODE (link) != FUNCTION_DECL, 163);
+ DECL_CONTEXT (link) = NULL_TREE;
+ }
+
+ /* Restore all name-meanings of the outer levels
+ that were shadowed by this level. */
+
+ for (link = current_binding_level->shadowed; link; link = TREE_CHAIN (link))
+ IDENTIFIER_LOCAL_VALUE (TREE_PURPOSE (link)) = TREE_VALUE (link);
+ for (link = current_binding_level->class_shadowed;
+ link; link = TREE_CHAIN (link))
+ IDENTIFIER_CLASS_VALUE (TREE_PURPOSE (link)) = TREE_VALUE (link);
+ for (link = current_binding_level->type_shadowed;
+ link; link = TREE_CHAIN (link))
+ IDENTIFIER_TYPE_VALUE (TREE_PURPOSE (link)) = TREE_VALUE (link);
+
+ GNU_xref_end_scope ((HOST_WIDE_INT) current_binding_level,
+ (HOST_WIDE_INT) current_binding_level->level_chain,
+ current_binding_level->parm_flag,
+ current_binding_level->keep,
+ current_binding_level->tag_transparent);
+
+ poplevel (0, 0, 0);
+
+ DECL_INITIAL (fndecl) = old_initial;
+
+ /* We used to check if the context of FNDECL was different from
+ current_class_type as another way to get inside here. This didn't work
+ for String.cc in libg++. */
+ if (DECL_FRIEND_P (fndecl))
+ {
+ CLASSTYPE_INLINE_FRIENDS (current_class_type)
+ = tree_cons (NULL_TREE, fndecl, CLASSTYPE_INLINE_FRIENDS (current_class_type));
+ decl = void_type_node;
+ }
+
+ return decl;
+}
+
+/* Called when a new struct TYPE is defined.
+ If this structure or union completes the type of any previous
+ variable declaration, lay it out and output its rtl. */
+
+void
+hack_incomplete_structures (type)
+ tree type;
+{
+ tree decl;
+
+ if (current_binding_level->n_incomplete == 0)
+ return;
+
+ if (!type) /* Don't do this for class templates. */
+ return;
+
+ for (decl = current_binding_level->names; decl; decl = TREE_CHAIN (decl))
+ if (TREE_TYPE (decl) == type
+ || (TREE_TYPE (decl)
+ && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE
+ && TREE_TYPE (TREE_TYPE (decl)) == type))
+ {
+ if (TREE_CODE (decl) == TYPE_DECL)
+ layout_type (TREE_TYPE (decl));
+ else
+ {
+ int toplevel = global_binding_level == current_binding_level;
+ if (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE
+ && TREE_TYPE (TREE_TYPE (decl)) == type)
+ layout_type (TREE_TYPE (decl));
+ layout_decl (decl, 0);
+ rest_of_decl_compilation (decl, NULL_PTR, toplevel, 0);
+ if (! toplevel)
+ {
+ tree cleanup;
+ expand_decl (decl);
+ cleanup = maybe_build_cleanup (decl);
+ expand_decl_init (decl);
+ if (! expand_decl_cleanup (decl, cleanup))
+ cp_error ("parser lost in parsing declaration of `%D'",
+ decl);
+ }
+ }
+ my_friendly_assert (current_binding_level->n_incomplete > 0, 164);
+ --current_binding_level->n_incomplete;
+ }
+}
+
+/* Nonzero if presently building a cleanup. Needed because
+ SAVE_EXPRs are not the right things to use inside of cleanups.
+ They are only ever evaluated once, where the cleanup
+ might be evaluated several times. In this case, a later evaluation
+ of the cleanup might fill in the SAVE_EXPR_RTL, and it will
+ not be valid for an earlier cleanup. */
+
+int building_cleanup;
+
+/* If DECL is of a type which needs a cleanup, build that cleanup here.
+ We don't build cleanups if just going for syntax checking, since
+ fixup_cleanups does not know how to not handle them.
+
+ Don't build these on the momentary obstack; they must live
+ the life of the binding contour. */
+tree
+maybe_build_cleanup (decl)
+ tree decl;
+{
+ tree type = TREE_TYPE (decl);
+ if (TYPE_NEEDS_DESTRUCTOR (type))
+ {
+ int temp = 0, flags = LOOKUP_NORMAL|LOOKUP_DESTRUCTOR;
+ tree rval;
+ int old_building_cleanup = building_cleanup;
+ building_cleanup = 1;
+
+ if (TREE_CODE (decl) != PARM_DECL)
+ temp = suspend_momentary ();
+
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ rval = decl;
+ else
+ {
+ mark_addressable (decl);
+ rval = build_unary_op (ADDR_EXPR, decl, 0);
+ }
+
+ /* Optimize for space over speed here. */
+ if (! TYPE_USES_VIRTUAL_BASECLASSES (type)
+ || flag_expensive_optimizations)
+ flags |= LOOKUP_NONVIRTUAL;
+
+ /* Use TYPE_MAIN_VARIANT so we don't get a warning about
+ calling delete on a `const' variable. */
+ if (TYPE_READONLY (TREE_TYPE (TREE_TYPE (rval))))
+ rval = build1 (NOP_EXPR, TYPE_POINTER_TO (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (rval)))), rval);
+
+ rval = build_delete (TREE_TYPE (rval), rval, integer_two_node, flags, 0);
+
+ if (TYPE_USES_VIRTUAL_BASECLASSES (type)
+ && ! TYPE_HAS_DESTRUCTOR (type))
+ rval = build_compound_expr (tree_cons (NULL_TREE, rval,
+ build_tree_list (NULL_TREE, build_vbase_delete (type, decl))));
+
+ if (TREE_CODE (decl) != PARM_DECL)
+ resume_momentary (temp);
+
+ building_cleanup = old_building_cleanup;
+
+ return rval;
+ }
+ return 0;
+}
+
+/* Expand a C++ expression at the statement level.
+ This is needed to ferret out nodes which have UNKNOWN_TYPE.
+ The C++ type checker should get all of these out when
+ expressions are combined with other, type-providing, expressions,
+ leaving only orphan expressions, such as:
+
+ &class::bar; / / takes its address, but does nothing with it.
+
+ */
+void
+cplus_expand_expr_stmt (exp)
+ tree exp;
+{
+ if (TREE_TYPE (exp) == unknown_type_node)
+ {
+ if (TREE_CODE (exp) == ADDR_EXPR || TREE_CODE (exp) == TREE_LIST)
+ error ("address of overloaded function with no contextual type information");
+ else if (TREE_CODE (exp) == COMPONENT_REF)
+ warning ("useless reference to a member function name, did you forget the ()?");
+ }
+ else
+ {
+ int remove_implicit_immediately = 0;
+
+ if (TREE_CODE (exp) == FUNCTION_DECL)
+ {
+ cp_warning ("reference, not call, to function `%D'", exp);
+ warning ("at this point in file");
+ }
+
+#if 0
+ /* We should do this eventually, but right now this causes regex.o from
+ libg++ to miscompile, and tString to core dump. */
+ exp = build1 (CLEANUP_POINT_EXPR, TREE_TYPE (exp), exp);
+#endif
+ expand_expr_stmt (break_out_cleanups (exp));
+ }
+
+ /* Clean up any pending cleanups. This happens when a function call
+ returns a cleanup-needing value that nobody uses. */
+ expand_cleanups_to (NULL_TREE);
+}
+
+/* When a stmt has been parsed, this function is called.
+
+ Currently, this function only does something within a
+ constructor's scope: if a stmt has just assigned to this,
+ and we are in a derived class, we call `emit_base_init'. */
+
+void
+finish_stmt ()
+{
+ extern struct nesting *cond_stack, *loop_stack, *case_stack;
+
+
+ if (current_function_assigns_this
+ || ! current_function_just_assigned_this)
+ return;
+ if (DECL_CONSTRUCTOR_P (current_function_decl))
+ {
+ /* Constructors must wait until we are out of control
+ zones before calling base constructors. */
+ if (cond_stack || loop_stack || case_stack)
+ return;
+ emit_insns (base_init_insns);
+ check_base_init (current_class_type);
+ }
+ current_function_assigns_this = 1;
+
+ if (flag_cadillac)
+ cadillac_finish_stmt ();
+}
+
+/* Change a static member function definition into a FUNCTION_TYPE, instead
+ of the METHOD_TYPE that we create when it's originally parsed.
+
+ WARNING: DO NOT pass &TREE_TYPE (decl) to FN or &TYPE_ARG_TYPES
+ (TREE_TYPE (decl)) to ARGTYPES, as doing so will corrupt the types of
+ other decls. Either pass the addresses of local variables or NULL. */
+
+void
+revert_static_member_fn (decl, fn, argtypes)
+ tree *decl, *fn, *argtypes;
+{
+ tree tmp;
+ tree function = fn ? *fn : TREE_TYPE (*decl);
+ tree args = argtypes ? *argtypes : TYPE_ARG_TYPES (function);
+
+ args = TREE_CHAIN (args);
+ tmp = build_function_type (TREE_TYPE (function), args);
+ tmp = build_type_variant (tmp, TYPE_READONLY (function),
+ TYPE_VOLATILE (function));
+ tmp = build_exception_variant (TYPE_METHOD_BASETYPE (function), tmp,
+ TYPE_RAISES_EXCEPTIONS (function));
+ TREE_TYPE (*decl) = tmp;
+ DECL_STATIC_FUNCTION_P (*decl) = 1;
+ if (fn)
+ *fn = tmp;
+ if (argtypes)
+ *argtypes = args;
+}
+
+int
+id_in_current_class (id)
+ tree id;
+{
+ return !!purpose_member (id, class_binding_level->class_shadowed);
+}
diff --git a/gnu/usr.bin/cc/cc1plus/decl.h b/gnu/usr.bin/cc/cc1plus/decl.h
new file mode 100644
index 0000000..b088179
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/decl.h
@@ -0,0 +1,54 @@
+/* Variables and structures for declaration processing.
+ Copyright (C) 1993 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* In grokdeclarator, distinguish syntactic contexts of declarators. */
+enum decl_context
+{ NORMAL, /* Ordinary declaration */
+ FUNCDEF, /* Function definition */
+ PARM, /* Declaration of parm before function body */
+ FIELD, /* Declaration inside struct or union */
+ BITFIELD, /* Likewise but with specified width */
+ TYPENAME, /* Typename (inside cast or sizeof) */
+ MEMFUNCDEF /* Member function definition */
+};
+
+/* C++: Keep these around to reduce calls to `get_identifier'.
+ Identifiers for `this' in member functions and the auto-delete
+ parameter for destructors. */
+extern tree this_identifier, in_charge_identifier;
+
+/* Parsing a function declarator leaves a list of parameter names
+ or a chain or parameter decls here. */
+extern tree last_function_parms;
+
+/* A list of static class variables. This is needed, because a
+ static class variable can be declared inside the class without
+ an initializer, and then initialized, staticly, outside the class. */
+extern tree pending_statics;
+
+/* A list of objects which have constructors or destructors
+ which reside in the global scope. The decl is stored in
+ the TREE_VALUE slot and the initializer is stored
+ in the TREE_PURPOSE slot. */
+extern tree static_aggregates;
+
+#ifdef DEBUG_CP_BINDING_LEVELS
+/* Purely for debugging purposes. */
+extern int debug_bindings_indentation;
+#endif
diff --git a/gnu/usr.bin/cc/cc1plus/decl2.c b/gnu/usr.bin/cc/cc1plus/decl2.c
new file mode 100644
index 0000000..2b9f8a7
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/decl2.c
@@ -0,0 +1,3102 @@
+/* Process declarations and variables for C compiler.
+ Copyright (C) 1988, 1992, 1993 Free Software Foundation, Inc.
+ Hacked by Michael Tiemann (tiemann@cygnus.com)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* Process declarations and symbol lookup for C front end.
+ Also constructs types; the standard scalar types at initialization,
+ and structure, union, array and enum types when they are declared. */
+
+/* ??? not all decl nodes are given the most useful possible
+ line numbers. For example, the CONST_DECLs for enum values. */
+
+#include "config.h"
+#include <stdio.h>
+#include "tree.h"
+#include "rtl.h"
+#include "flags.h"
+#include "cp-tree.h"
+#include "decl.h"
+#include "lex.h"
+
+extern tree grokdeclarator ();
+extern tree get_file_function_name ();
+extern tree cleanups_this_call;
+static void grok_function_init ();
+
+/* A list of virtual function tables we must make sure to write out. */
+tree pending_vtables;
+
+/* A list of static class variables. This is needed, because a
+ static class variable can be declared inside the class without
+ an initializer, and then initialized, staticly, outside the class. */
+tree pending_statics;
+
+/* A list of functions which were declared inline, but which we
+ may need to emit outline anyway. */
+static tree saved_inlines;
+
+/* Used to help generate temporary names which are unique within
+ a function. Reset to 0 by start_function. */
+
+static int temp_name_counter;
+
+/* Same, but not reset. Local temp variables and global temp variables
+ can have the same name. */
+static int global_temp_name_counter;
+
+/* Flag used when debugging spew.c */
+
+extern int spew_debug;
+
+/* C (and C++) language-specific option variables. */
+
+/* Nonzero means allow type mismatches in conditional expressions;
+ just make their values `void'. */
+
+int flag_cond_mismatch;
+
+/* Nonzero means give `double' the same size as `float'. */
+
+int flag_short_double;
+
+/* Nonzero means don't recognize the keyword `asm'. */
+
+int flag_no_asm;
+
+/* Nonzero means don't recognize the non-ANSI builtin functions. */
+
+int flag_no_builtin;
+
+/* Nonzero means do some things the same way PCC does. */
+
+int flag_traditional;
+
+/* Nonzero means to treat bitfields as unsigned unless they say `signed'. */
+
+int flag_signed_bitfields = 1;
+
+/* Nonzero means handle `#ident' directives. 0 means ignore them. */
+
+int flag_no_ident = 0;
+
+/* Nonzero means disable GNU extensions. */
+
+int flag_ansi = 0;
+
+/* Nonzero means do emit exported implementations of functions even if
+ they can be inlined. */
+
+int flag_implement_inlines = 1;
+
+/* Nonzero means do emit exported implementations of templates, instead of
+ multiple static copies in each file that needs a definition. */
+
+int flag_external_templates = 0;
+
+/* Nonzero means that the decision to emit or not emit the implementation of a
+ template depends on where the template is instantiated, rather than where
+ it is defined. */
+
+int flag_alt_external_templates = 0;
+
+/* Nonzero means that implicit instantiations will be emitted if needed. */
+
+int flag_implicit_templates = 1;
+
+/* Nonzero means warn about implicit declarations. */
+
+int warn_implicit = 1;
+
+/* Nonzero means warn when all ctors or dtors are private, and the class
+ has no friends. */
+
+int warn_ctor_dtor_privacy = 1;
+
+/* True if we want to implement vtbvales using "thunks".
+ The default is off now, but will be on later.
+
+ Also causes output of vtables to be controlled by whether
+ we seen the class's first non-inline virtual function. */
+int flag_vtable_thunks = 0;
+
+/* Nonzero means give string constants the type `const char *'
+ to get extra warnings from them. These warnings will be too numerous
+ to be useful, except in thoroughly ANSIfied programs. */
+
+int warn_write_strings;
+
+/* Nonzero means warn about pointer casts that can drop a type qualifier
+ from the pointer target type. */
+
+int warn_cast_qual;
+
+/* Nonzero means warn that dbx info for template class methods isn't fully
+ supported yet. */
+
+int warn_template_debugging;
+
+/* Warn about traditional constructs whose meanings changed in ANSI C. */
+
+int warn_traditional;
+
+/* Nonzero means warn about sizeof(function) or addition/subtraction
+ of function pointers. */
+
+int warn_pointer_arith;
+
+/* Nonzero means warn for non-prototype function decls
+ or non-prototyped defs without previous prototype. */
+
+int warn_strict_prototypes;
+
+/* Nonzero means warn for any function def without prototype decl. */
+
+int warn_missing_prototypes;
+
+/* Nonzero means warn about multiple (redundant) decls for the same single
+ variable or function. */
+
+int warn_redundant_decls;
+
+/* Warn if initializer is not completely bracketed. */
+
+int warn_missing_braces;
+
+/* Warn about *printf or *scanf format/argument anomalies. */
+
+int warn_format;
+
+/* Warn about a subscript that has type char. */
+
+int warn_char_subscripts;
+
+/* Warn if a type conversion is done that might have confusing results. */
+
+int warn_conversion;
+
+/* Warn if adding () is suggested. */
+
+int warn_parentheses = 1;
+
+/* Non-zero means warn in function declared in derived class has the
+ same name as a virtual in the base class, but fails to match the
+ type signature of any virtual function in the base class. */
+int warn_overloaded_virtual;
+
+/* Non-zero means warn when declaring a class that has a non virtual
+ destructor, when it really ought to have a virtual one. */
+int warn_nonvdtor;
+
+/* Non-zero means warn when a function is declared extern and later inline. */
+int warn_extern_inline;
+
+/* Nonzero means `$' can be in an identifier.
+ See cccp.c for reasons why this breaks some obscure ANSI C programs. */
+
+#ifndef DOLLARS_IN_IDENTIFIERS
+#define DOLLARS_IN_IDENTIFIERS 1
+#endif
+int dollars_in_ident = DOLLARS_IN_IDENTIFIERS;
+
+/* Nonzero for -fno-strict-prototype switch: do not consider empty
+ argument prototype to mean function takes no arguments. */
+
+int strict_prototype = 1;
+int strict_prototypes_lang_c, strict_prototypes_lang_cplusplus = 1;
+
+/* Nonzero means that labels can be used as first-class objects */
+
+int flag_labels_ok;
+
+/* Non-zero means to collect statistics which might be expensive
+ and to print them when we are done. */
+int flag_detailed_statistics;
+
+/* C++ specific flags. */
+/* Nonzero for -fall-virtual: make every member function (except
+ constructors) lay down in the virtual function table. Calls
+ can then either go through the virtual function table or not,
+ depending. */
+
+int flag_all_virtual;
+
+/* Zero means that `this' is a *const. This gives nice behavior in the
+ 2.0 world. 1 gives 1.2-compatible behavior. 2 gives Spring behavior.
+ -2 means we're constructing an object and it has fixed type. */
+
+int flag_this_is_variable;
+
+/* Nonzero means memoize our member lookups. */
+
+int flag_memoize_lookups; int flag_save_memoized_contexts;
+
+/* 3 means write out only virtuals function tables `defined'
+ in this implementation file.
+ 2 means write out only specific virtual function tables
+ and give them (C) public access.
+ 1 means write out virtual function tables and give them
+ (C) public access.
+ 0 means write out virtual function tables and give them
+ (C) static access (default).
+ -1 means declare virtual function tables extern. */
+
+int write_virtuals;
+
+/* Nonzero means we should attempt to elide constructors when possible. */
+
+int flag_elide_constructors;
+
+/* Nonzero means recognize and handle exception handling constructs.
+ Use ansi syntax and semantics. WORK IN PROGRESS! */
+
+int flag_handle_exceptions;
+
+/* Nonzero means recognize and handle signature language constructs. */
+
+int flag_handle_signatures;
+
+/* Nonzero means that member functions defined in class scope are
+ inline by default. */
+
+int flag_default_inline = 1;
+
+/* Controls whether enums and ints freely convert.
+ 1 means with complete freedom.
+ 0 means enums can convert to ints, but not vice-versa. */
+int flag_int_enum_equivalence;
+
+/* Controls whether compiler is operating under LUCID's Cadillac
+ system. 1 means yes, 0 means no. */
+int flag_cadillac;
+
+/* Controls whether compiler generates code to build objects
+ that can be collected when they become garbage. */
+int flag_gc;
+
+/* Controls whether compiler generates 'dossiers' that give
+ run-time type information. */
+int flag_dossier;
+
+/* Nonzero if we wish to output cross-referencing information
+ for the GNU class browser. */
+extern int flag_gnu_xref;
+
+/* Nonzero if compiler can make `reasonable' assumptions about
+ references and objects. For example, the compiler must be
+ conservative about the following and not assume that `a' is nonnull:
+
+ obj &a = g ();
+ a.f (2);
+
+ In general, it is `reasonable' to assume that for many programs,
+ and better code can be generated in that case. */
+
+int flag_assume_nonnull_objects;
+
+/* Nonzero if we want to support huge (> 2^(sizeof(short)*8-1) bytes)
+ objects. */
+int flag_huge_objects;
+
+/* Nonzero if we want to conserve space in the .o files. We do this
+ by putting uninitialized data and runtime initialized data into
+ .common instead of .data at the expense of not flaging multiple
+ definitions. */
+int flag_conserve_space;
+
+/* Table of language-dependent -f options.
+ STRING is the option name. VARIABLE is the address of the variable.
+ ON_VALUE is the value to store in VARIABLE
+ if `-fSTRING' is seen as an option.
+ (If `-fno-STRING' is seen as an option, the opposite value is stored.) */
+
+static struct { char *string; int *variable; int on_value;} lang_f_options[] =
+{
+ {"signed-char", &flag_signed_char, 1},
+ {"unsigned-char", &flag_signed_char, 0},
+ {"signed-bitfields", &flag_signed_bitfields, 1},
+ {"unsigned-bitfields", &flag_signed_bitfields, 0},
+ {"short-enums", &flag_short_enums, 1},
+ {"short-double", &flag_short_double, 1},
+ {"cond-mismatch", &flag_cond_mismatch, 1},
+ {"asm", &flag_no_asm, 0},
+ {"builtin", &flag_no_builtin, 0},
+ {"ident", &flag_no_ident, 0},
+ {"labels-ok", &flag_labels_ok, 1},
+ {"stats", &flag_detailed_statistics, 1},
+ {"this-is-variable", &flag_this_is_variable, 1},
+ {"strict-prototype", &strict_prototypes_lang_cplusplus, 1},
+ {"all-virtual", &flag_all_virtual, 1},
+ {"memoize-lookups", &flag_memoize_lookups, 1},
+ {"elide-constructors", &flag_elide_constructors, 1},
+ {"handle-exceptions", &flag_handle_exceptions, 1},
+ {"handle-signatures", &flag_handle_signatures, 1},
+ {"default-inline", &flag_default_inline, 1},
+ {"dollars-in-identifiers", &dollars_in_ident, 1},
+ {"enum-int-equiv", &flag_int_enum_equivalence, 1},
+ {"gc", &flag_gc, 1},
+ {"dossier", &flag_dossier, 1},
+ {"xref", &flag_gnu_xref, 1},
+ {"nonnull-objects", &flag_assume_nonnull_objects, 1},
+ {"implement-inlines", &flag_implement_inlines, 1},
+ {"external-templates", &flag_external_templates, 1},
+ {"implicit-templates", &flag_implicit_templates, 1},
+ {"huge-objects", &flag_huge_objects, 1},
+ {"conserve-space", &flag_conserve_space, 1},
+ {"vtable-thunks", &flag_vtable_thunks, 1},
+ {"short-temps", &flag_short_temps, 1},
+};
+
+/* Decode the string P as a language-specific option.
+ Return 1 if it is recognized (and handle it);
+ return 0 if not recognized. */
+
+int
+lang_decode_option (p)
+ char *p;
+{
+ if (!strcmp (p, "-ftraditional") || !strcmp (p, "-traditional"))
+ flag_traditional = 1, dollars_in_ident = 1, flag_writable_strings = 1,
+ flag_this_is_variable = 1;
+ /* The +e options are for cfront compatibility. They come in as
+ `-+eN', to kludge around gcc.c's argument handling. */
+ else if (p[0] == '-' && p[1] == '+' && p[2] == 'e')
+ {
+ int old_write_virtuals = write_virtuals;
+ if (p[3] == '1')
+ write_virtuals = 1;
+ else if (p[3] == '0')
+ write_virtuals = -1;
+ else if (p[3] == '2')
+ write_virtuals = 2;
+ else error ("invalid +e option");
+ if (old_write_virtuals != 0
+ && write_virtuals != old_write_virtuals)
+ error ("conflicting +e options given");
+ }
+ else if (p[0] == '-' && p[1] == 'f')
+ {
+ /* Some kind of -f option.
+ P's value is the option sans `-f'.
+ Search for it in the table of options. */
+ int found = 0, j;
+
+ p += 2;
+ /* Try special -f options. */
+
+ if (!strcmp (p, "save-memoized"))
+ {
+ flag_memoize_lookups = 1;
+ flag_save_memoized_contexts = 1;
+ found = 1;
+ }
+ if (!strcmp (p, "no-save-memoized"))
+ {
+ flag_memoize_lookups = 0;
+ flag_save_memoized_contexts = 0;
+ found = 1;
+ }
+ else if (! strncmp (p, "cadillac", 8))
+ {
+ flag_cadillac = atoi (p+9);
+ found = 1;
+ }
+ else if (! strncmp (p, "no-cadillac", 11))
+ {
+ flag_cadillac = 0;
+ found = 1;
+ }
+ else if (! strcmp (p, "gc"))
+ {
+ flag_gc = 1;
+ /* This must come along for the ride. */
+ flag_dossier = 1;
+ found = 1;
+ }
+ else if (! strcmp (p, "no-gc"))
+ {
+ flag_gc = 0;
+ /* This must come along for the ride. */
+ flag_dossier = 0;
+ found = 1;
+ }
+ else if (! strcmp (p, "alt-external-templates"))
+ {
+ flag_external_templates = 1;
+ flag_alt_external_templates = 1;
+ found = 1;
+ }
+ else if (! strcmp (p, "no-alt-external-templates"))
+ {
+ flag_alt_external_templates = 0;
+ found = 1;
+ }
+ else for (j = 0;
+ !found && j < sizeof (lang_f_options) / sizeof (lang_f_options[0]);
+ j++)
+ {
+ if (!strcmp (p, lang_f_options[j].string))
+ {
+ *lang_f_options[j].variable = lang_f_options[j].on_value;
+ /* A goto here would be cleaner,
+ but breaks the vax pcc. */
+ found = 1;
+ }
+ if (p[0] == 'n' && p[1] == 'o' && p[2] == '-'
+ && ! strcmp (p+3, lang_f_options[j].string))
+ {
+ *lang_f_options[j].variable = ! lang_f_options[j].on_value;
+ found = 1;
+ }
+ }
+ return found;
+ }
+ else if (p[0] == '-' && p[1] == 'W')
+ {
+ int setting = 1;
+
+ /* The -W options control the warning behavior of the compiler. */
+ p += 2;
+
+ if (p[0] == 'n' && p[1] == 'o' && p[2] == '-')
+ setting = 0, p += 3;
+
+ if (!strcmp (p, "implicit"))
+ warn_implicit = setting;
+ else if (!strcmp (p, "return-type"))
+ warn_return_type = setting;
+ else if (!strcmp (p, "ctor-dtor-privacy"))
+ warn_ctor_dtor_privacy = setting;
+ else if (!strcmp (p, "write-strings"))
+ warn_write_strings = setting;
+ else if (!strcmp (p, "cast-qual"))
+ warn_cast_qual = setting;
+ else if (!strcmp (p, "traditional"))
+ warn_traditional = setting;
+ else if (!strcmp (p, "char-subscripts"))
+ warn_char_subscripts = setting;
+ else if (!strcmp (p, "pointer-arith"))
+ warn_pointer_arith = setting;
+ else if (!strcmp (p, "strict-prototypes"))
+ warn_strict_prototypes = setting;
+ else if (!strcmp (p, "missing-prototypes"))
+ warn_missing_prototypes = setting;
+ else if (!strcmp (p, "redundant-decls"))
+ warn_redundant_decls = setting;
+ else if (!strcmp (p, "missing-braces"))
+ warn_missing_braces = setting;
+ else if (!strcmp (p, "format"))
+ warn_format = setting;
+ else if (!strcmp (p, "conversion"))
+ warn_conversion = setting;
+ else if (!strcmp (p, "parentheses"))
+ warn_parentheses = setting;
+ else if (!strcmp (p, "non-virtual-dtor"))
+ warn_nonvdtor = setting;
+ else if (!strcmp (p, "extern-inline"))
+ warn_extern_inline = setting;
+ else if (!strcmp (p, "comment"))
+ ; /* cpp handles this one. */
+ else if (!strcmp (p, "comments"))
+ ; /* cpp handles this one. */
+ else if (!strcmp (p, "trigraphs"))
+ ; /* cpp handles this one. */
+ else if (!strcmp (p, "import"))
+ ; /* cpp handles this one. */
+ else if (!strcmp (p, "all"))
+ {
+ extra_warnings = setting;
+ warn_return_type = setting;
+ warn_unused = setting;
+ warn_implicit = setting;
+ warn_ctor_dtor_privacy = setting;
+ warn_switch = setting;
+ warn_format = setting;
+ warn_missing_braces = setting;
+ warn_extern_inline = setting;
+ warn_nonvdtor = setting;
+ /* We save the value of warn_uninitialized, since if they put
+ -Wuninitialized on the command line, we need to generate a
+ warning about not using it without also specifying -O. */
+ if (warn_uninitialized != 1)
+ warn_uninitialized = (setting ? 2 : 0);
+ warn_template_debugging = setting;
+ }
+
+ else if (!strcmp (p, "overloaded-virtual"))
+ warn_overloaded_virtual = setting;
+ else return 0;
+ }
+ else if (!strcmp (p, "-ansi"))
+ flag_no_asm = 1, dollars_in_ident = 0, flag_ansi = 1;
+#ifdef SPEW_DEBUG
+ /* Undocumented, only ever used when you're invoking cc1plus by hand, since
+ it's probably safe to assume no sane person would ever want to use this
+ under normal circumstances. */
+ else if (!strcmp (p, "-spew-debug"))
+ spew_debug = 1;
+#endif
+ else
+ return 0;
+
+ return 1;
+}
+
+/* Incorporate `const' and `volatile' qualifiers for member functions.
+ FUNCTION is a TYPE_DECL or a FUNCTION_DECL.
+ QUALS is a list of qualifiers. */
+tree
+grok_method_quals (ctype, function, quals)
+ tree ctype, function, quals;
+{
+ tree fntype = TREE_TYPE (function);
+ tree raises = TYPE_RAISES_EXCEPTIONS (fntype);
+
+ do
+ {
+ extern tree ridpointers[];
+
+ if (TREE_VALUE (quals) == ridpointers[(int)RID_CONST])
+ {
+ if (TYPE_READONLY (ctype))
+ error ("duplicate `%s' %s",
+ IDENTIFIER_POINTER (TREE_VALUE (quals)),
+ (TREE_CODE (function) == FUNCTION_DECL
+ ? "for member function" : "in type declaration"));
+ ctype = build_type_variant (ctype, 1, TYPE_VOLATILE (ctype));
+ build_pointer_type (ctype);
+ }
+ else if (TREE_VALUE (quals) == ridpointers[(int)RID_VOLATILE])
+ {
+ if (TYPE_VOLATILE (ctype))
+ error ("duplicate `%s' %s",
+ IDENTIFIER_POINTER (TREE_VALUE (quals)),
+ (TREE_CODE (function) == FUNCTION_DECL
+ ? "for member function" : "in type declaration"));
+ ctype = build_type_variant (ctype, TYPE_READONLY (ctype), 1);
+ build_pointer_type (ctype);
+ }
+ else
+ my_friendly_abort (20);
+ quals = TREE_CHAIN (quals);
+ }
+ while (quals);
+ fntype = build_cplus_method_type (ctype, TREE_TYPE (fntype),
+ (TREE_CODE (fntype) == METHOD_TYPE
+ ? TREE_CHAIN (TYPE_ARG_TYPES (fntype))
+ : TYPE_ARG_TYPES (fntype)));
+ if (raises)
+ fntype = build_exception_variant (ctype, fntype, raises);
+
+ TREE_TYPE (function) = fntype;
+ return ctype;
+}
+
+#if 0 /* Not used. */
+/* This routine replaces cryptic DECL_NAMEs with readable DECL_NAMEs.
+ It leaves DECL_ASSEMBLER_NAMEs with the correct value. */
+/* This does not yet work with user defined conversion operators
+ It should. */
+static void
+substitute_nice_name (decl)
+ tree decl;
+{
+ if (DECL_NAME (decl) && TREE_CODE (DECL_NAME (decl)) == IDENTIFIER_NODE)
+ {
+ char *n = decl_as_string (DECL_NAME (decl), 1);
+ if (n[strlen (n) - 1] == ' ')
+ n[strlen (n) - 1] = 0;
+ DECL_NAME (decl) = get_identifier (n);
+ }
+}
+#endif
+
+/* Warn when -fexternal-templates is used and #pragma
+ interface/implementation is not used all the times it should be,
+ inform the user. */
+void
+warn_if_unknown_interface ()
+{
+ static int already_warned = 0;
+ if (++already_warned == 1)
+ warning ("templates that are built with -fexternal-templates should be in files that have #pragma interface/implementation");
+}
+
+/* A subroutine of the parser, to handle a component list. */
+tree
+grok_x_components (specs, components)
+ tree specs, components;
+{
+ register tree t, x, tcode;
+
+ /* We just got some friends. They have been recorded elsewhere. */
+ if (components == void_type_node)
+ return NULL_TREE;
+
+ if (components == NULL_TREE)
+ {
+ t = groktypename (build_decl_list (specs, NULL_TREE));
+
+ if (t == NULL_TREE)
+ {
+ error ("error in component specification");
+ return NULL_TREE;
+ }
+
+ switch (TREE_CODE (t))
+ {
+ case VAR_DECL:
+ /* Static anonymous unions come out as VAR_DECLs. */
+ if (TREE_CODE (TREE_TYPE (t)) == UNION_TYPE
+ && ANON_AGGRNAME_P (TYPE_IDENTIFIER (TREE_TYPE (t))))
+ return t;
+
+ /* We return SPECS here, because in the parser it was ending
+ up with not doing anything to $$, which is what SPECS
+ represents. */
+ return specs;
+ break;
+
+ case RECORD_TYPE:
+ /* This code may be needed for UNION_TYPEs as
+ well. */
+ tcode = record_type_node;
+ if (CLASSTYPE_DECLARED_CLASS(t))
+ tcode = class_type_node;
+ else if (IS_SIGNATURE(t))
+ tcode = signature_type_node;
+ else if (CLASSTYPE_DECLARED_EXCEPTION(t))
+ tcode = exception_type_node;
+
+ t = xref_defn_tag(tcode, TYPE_IDENTIFIER(t), NULL_TREE);
+ if (TYPE_CONTEXT(t))
+ CLASSTYPE_NO_GLOBALIZE(t) = 1;
+ if (TYPE_LANG_SPECIFIC (t)
+ && CLASSTYPE_DECLARED_EXCEPTION (t))
+ shadow_tag (specs);
+ return NULL_TREE;
+ break;
+
+ case UNION_TYPE:
+ case ENUMERAL_TYPE:
+ if (TREE_CODE(t) == UNION_TYPE)
+ tcode = union_type_node;
+ else
+ tcode = enum_type_node;
+
+ t = xref_defn_tag(tcode, TYPE_IDENTIFIER(t), NULL_TREE);
+ if (TREE_CODE(t) == UNION_TYPE && TYPE_CONTEXT(t))
+ CLASSTYPE_NO_GLOBALIZE(t) = 1;
+ if (TREE_CODE (t) == UNION_TYPE
+ && ANON_AGGRNAME_P (TYPE_IDENTIFIER (t)))
+ {
+ struct pending_inline **p;
+ x = build_lang_field_decl (FIELD_DECL, NULL_TREE, t);
+
+ /* Wipe out memory of synthesized methods */
+ TYPE_HAS_CONSTRUCTOR (t) = 0;
+ TYPE_HAS_DEFAULT_CONSTRUCTOR (t) = 0;
+ TYPE_HAS_INIT_REF (t) = 0;
+ TYPE_HAS_CONST_INIT_REF (t) = 0;
+ TYPE_HAS_ASSIGN_REF (t) = 0;
+ TYPE_HAS_ASSIGNMENT (t) = 0;
+ TYPE_HAS_CONST_ASSIGN_REF (t) = 0;
+
+ p = &pending_inlines;
+ for (; *p; *p = (*p)->next)
+ if (DECL_CONTEXT ((*p)->fndecl) != t)
+ break;
+ }
+ else if (TREE_CODE (t) == ENUMERAL_TYPE)
+ x = grok_enum_decls (t, NULL_TREE);
+ else
+ x = NULL_TREE;
+ return x;
+ break;
+
+ default:
+ if (t != void_type_node)
+ error ("empty component declaration");
+ return NULL_TREE;
+ }
+ }
+ else
+ {
+ t = TREE_TYPE (components);
+ if (TREE_CODE (t) == ENUMERAL_TYPE && TREE_NONLOCAL_FLAG (t))
+ return grok_enum_decls (t, components);
+ else
+ return components;
+ }
+}
+
+/* Classes overload their constituent function names automatically.
+ When a function name is declared in a record structure,
+ its name is changed to it overloaded name. Since names for
+ constructors and destructors can conflict, we place a leading
+ '$' for destructors.
+
+ CNAME is the name of the class we are grokking for.
+
+ FUNCTION is a FUNCTION_DECL. It was created by `grokdeclarator'.
+
+ FLAGS contains bits saying what's special about today's
+ arguments. 1 == DESTRUCTOR. 2 == OPERATOR.
+
+ If FUNCTION is a destructor, then we must add the `auto-delete' field
+ as a second parameter. There is some hair associated with the fact
+ that we must "declare" this variable in the manner consistent with the
+ way the rest of the arguments were declared.
+
+ QUALS are the qualifiers for the this pointer. */
+
+void
+grokclassfn (ctype, cname, function, flags, quals)
+ tree ctype, cname, function;
+ enum overload_flags flags;
+ tree quals;
+{
+ tree fn_name = DECL_NAME (function);
+ tree arg_types;
+ tree parm;
+ tree qualtype;
+
+ if (fn_name == NULL_TREE)
+ {
+ error ("name missing for member function");
+ fn_name = get_identifier ("<anonymous>");
+ DECL_NAME (function) = fn_name;
+ }
+
+ if (quals)
+ qualtype = grok_method_quals (ctype, function, quals);
+ else
+ qualtype = ctype;
+
+ arg_types = TYPE_ARG_TYPES (TREE_TYPE (function));
+ if (TREE_CODE (TREE_TYPE (function)) == METHOD_TYPE)
+ {
+ /* Must add the class instance variable up front. */
+ /* Right now we just make this a pointer. But later
+ we may wish to make it special. */
+ tree type = TREE_VALUE (arg_types);
+
+ if ((flag_this_is_variable > 0)
+ && (flags == DTOR_FLAG || DECL_CONSTRUCTOR_P (function)))
+ type = TYPE_MAIN_VARIANT (type);
+
+ if (DECL_CONSTRUCTOR_P (function))
+ {
+ if (TYPE_USES_VIRTUAL_BASECLASSES (ctype))
+ {
+ DECL_CONSTRUCTOR_FOR_VBASE_P (function) = 1;
+ /* In this case we need "in-charge" flag saying whether
+ this constructor is responsible for initialization
+ of virtual baseclasses or not. */
+ parm = build_decl (PARM_DECL, in_charge_identifier, integer_type_node);
+ /* Mark the artificial `__in_chrg' parameter as "artificial". */
+ SET_DECL_ARTIFICIAL (parm);
+ DECL_ARG_TYPE (parm) = integer_type_node;
+ DECL_REGISTER (parm) = 1;
+ TREE_CHAIN (parm) = last_function_parms;
+ last_function_parms = parm;
+ }
+ }
+
+ parm = build_decl (PARM_DECL, this_identifier, type);
+ /* Mark the artificial `this' parameter as "artificial". */
+ SET_DECL_ARTIFICIAL (parm);
+ DECL_ARG_TYPE (parm) = type;
+ /* We can make this a register, so long as we don't
+ accidentally complain if someone tries to take its address. */
+ DECL_REGISTER (parm) = 1;
+ if (TYPE_READONLY (type))
+ TREE_READONLY (parm) = 1;
+ TREE_CHAIN (parm) = last_function_parms;
+ last_function_parms = parm;
+ }
+
+ if (flags == DTOR_FLAG)
+ {
+ char *buf, *dbuf;
+ tree const_integer_type = build_type_variant (integer_type_node, 1, 0);
+ int len = sizeof (DESTRUCTOR_DECL_PREFIX)-1;
+
+ arg_types = hash_tree_chain (const_integer_type, void_list_node);
+ TREE_SIDE_EFFECTS (arg_types) = 1;
+ /* Build the overload name. It will look like `7Example'. */
+ if (IDENTIFIER_TYPE_VALUE (cname))
+ dbuf = build_overload_name (IDENTIFIER_TYPE_VALUE (cname), 1, 1);
+ else if (IDENTIFIER_LOCAL_VALUE (cname))
+ dbuf = build_overload_name (TREE_TYPE (IDENTIFIER_LOCAL_VALUE (cname)), 1, 1);
+ else
+ /* Using ctype fixes the `X::Y::~Y()' crash. The cname has no type when
+ it's defined out of the class definition, since poplevel_class wipes
+ it out. This used to be internal error 346. */
+ dbuf = build_overload_name (ctype, 1, 1);
+ buf = (char *) alloca (strlen (dbuf) + sizeof (DESTRUCTOR_DECL_PREFIX));
+ bcopy (DESTRUCTOR_DECL_PREFIX, buf, len);
+ buf[len] = '\0';
+ strcat (buf, dbuf);
+ DECL_ASSEMBLER_NAME (function) = get_identifier (buf);
+ parm = build_decl (PARM_DECL, in_charge_identifier, const_integer_type);
+ /* Mark the artificial `__in_chrg' parameter as "artificial". */
+ SET_DECL_ARTIFICIAL (parm);
+ TREE_USED (parm) = 1;
+#if 0
+ /* We don't need to mark the __in_chrg parameter itself as `const'
+ since its type is already `const int'. In fact we MUST NOT mark
+ it as `const' cuz that will screw up the debug info (causing it
+ to say that the type of __in_chrg is `const const int'). */
+ TREE_READONLY (parm) = 1;
+#endif
+ DECL_ARG_TYPE (parm) = const_integer_type;
+ /* This is the same chain as DECL_ARGUMENTS (...). */
+ TREE_CHAIN (last_function_parms) = parm;
+
+ TREE_TYPE (function) = build_cplus_method_type (qualtype, void_type_node,
+ arg_types);
+ TYPE_HAS_DESTRUCTOR (ctype) = 1;
+ }
+ else
+ {
+ tree these_arg_types;
+
+ if (DECL_CONSTRUCTOR_FOR_VBASE_P (function))
+ {
+ arg_types = hash_tree_chain (integer_type_node,
+ TREE_CHAIN (arg_types));
+ TREE_TYPE (function)
+ = build_cplus_method_type (qualtype,
+ TREE_TYPE (TREE_TYPE (function)),
+ arg_types);
+ arg_types = TYPE_ARG_TYPES (TREE_TYPE (function));
+ }
+
+ these_arg_types = arg_types;
+
+ if (TREE_CODE (TREE_TYPE (function)) == FUNCTION_TYPE)
+ /* Only true for static member functions. */
+ these_arg_types = hash_tree_chain (TYPE_POINTER_TO (qualtype),
+ arg_types);
+
+ DECL_ASSEMBLER_NAME (function)
+ = build_decl_overload (fn_name, these_arg_types,
+ 1 + DECL_CONSTRUCTOR_P (function));
+
+#if 0
+ /* This code is going into the compiler, but currently, it makes
+ libg++/src/Interger.cc not compile. The problem is that the nice name
+ winds up going into the symbol table, and conversion operations look
+ for the manged name. */
+ substitute_nice_name (function);
+#endif
+ }
+
+ DECL_ARGUMENTS (function) = last_function_parms;
+ /* First approximations. */
+ DECL_CONTEXT (function) = ctype;
+ DECL_CLASS_CONTEXT (function) = ctype;
+}
+
+/* Work on the expr used by alignof (this is only called by the parser). */
+tree
+grok_alignof (expr)
+ tree expr;
+{
+ tree best, t;
+ int bestalign;
+
+ if (TREE_CODE (expr) == COMPONENT_REF
+ && DECL_BIT_FIELD (TREE_OPERAND (expr, 1)))
+ error ("`__alignof__' applied to a bit-field");
+
+ if (TREE_CODE (expr) == INDIRECT_REF)
+ {
+ best = t = TREE_OPERAND (expr, 0);
+ bestalign = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (t)));
+
+ while (TREE_CODE (t) == NOP_EXPR
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == POINTER_TYPE)
+ {
+ int thisalign;
+ t = TREE_OPERAND (t, 0);
+ thisalign = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (t)));
+ if (thisalign > bestalign)
+ best = t, bestalign = thisalign;
+ }
+ return c_alignof (TREE_TYPE (TREE_TYPE (best)));
+ }
+ else
+ {
+ /* ANSI says arrays and fns are converted inside comma.
+ But we can't convert them in build_compound_expr
+ because that would break commas in lvalues.
+ So do the conversion here if operand was a comma. */
+ if (TREE_CODE (expr) == COMPOUND_EXPR
+ && (TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE
+ || TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE))
+ expr = default_conversion (expr);
+ return c_alignof (TREE_TYPE (expr));
+ }
+}
+
+/* Create an ARRAY_REF, checking for the user doing things backwards
+ along the way. */
+tree
+grok_array_decl (array_expr, index_exp)
+ tree array_expr, index_exp;
+{
+ tree type = TREE_TYPE (array_expr);
+
+ if (type == error_mark_node || index_exp == error_mark_node)
+ return error_mark_node;
+ if (type == NULL_TREE)
+ {
+ /* Something has gone very wrong. Assume we are mistakenly reducing
+ an expression instead of a declaration. */
+ error ("parser may be lost: is there a '{' missing somewhere?");
+ return NULL_TREE;
+ }
+
+ if (TREE_CODE (type) == OFFSET_TYPE
+ || TREE_CODE (type) == REFERENCE_TYPE)
+ type = TREE_TYPE (type);
+
+ /* If they have an `operator[]', use that. */
+ if (TYPE_LANG_SPECIFIC (type)
+ && TYPE_OVERLOADS_ARRAY_REF (type))
+ return build_opfncall (ARRAY_REF, LOOKUP_NORMAL,
+ array_expr, index_exp, NULL_TREE);
+
+ /* Otherwise, create an ARRAY_REF for a pointer or array type. */
+ if (TREE_CODE (type) == POINTER_TYPE
+ || TREE_CODE (type) == ARRAY_TYPE)
+ return build_array_ref (array_expr, index_exp);
+
+ /* Woops, looks like they did something like `5[a]' instead of `a[5]'.
+ We don't emit a warning or error for this, since it's allowed
+ by ARM $8.2.4. */
+
+ type = TREE_TYPE (index_exp);
+
+ if (TREE_CODE (type) == OFFSET_TYPE
+ || TREE_CODE (type) == REFERENCE_TYPE)
+ type = TREE_TYPE (type);
+
+ if (TYPE_LANG_SPECIFIC (type)
+ && TYPE_OVERLOADS_ARRAY_REF (type))
+ error ("array expression backwards");
+ else if (TREE_CODE (type) == POINTER_TYPE
+ || TREE_CODE (type) == ARRAY_TYPE)
+ return build_array_ref (index_exp, array_expr);
+ else
+ error("`[]' applied to non-pointer type");
+
+ /* We gave an error, so give an error. Huh? */
+ return error_mark_node;
+}
+
+/* Given the cast expression EXP, checking out its validity. Either return
+ an error_mark_node if there was an unavoidable error, return a cast to
+ void for trying to delete a pointer w/ the value 0, or return the
+ call to delete. If DOING_VEC is 1, we handle things differently
+ for doing an array delete. If DOING_VEC is 2, they gave us the
+ array size as an argument to delete.
+ Implements ARM $5.3.4. This is called from the parser. */
+tree
+delete_sanity (exp, size, doing_vec, use_global_delete)
+ tree exp, size;
+ int doing_vec, use_global_delete;
+{
+ tree t = stabilize_reference (convert_from_reference (exp));
+ tree type = TREE_TYPE (t);
+ enum tree_code code = TREE_CODE (type);
+ /* For a regular vector delete (aka, no size argument) we will pass
+ this down as a NULL_TREE into build_vec_delete. */
+ tree maxindex = NULL_TREE;
+ /* This is used for deleting arrays. */
+ tree elt_size;
+
+ switch (doing_vec)
+ {
+ case 2:
+ maxindex = build_binary_op (MINUS_EXPR, size, integer_one_node, 1);
+ if (! flag_traditional)
+ pedwarn ("anachronistic use of array size in vector delete");
+ /* Fall through. */
+ case 1:
+ elt_size = c_sizeof (type);
+ break;
+ default:
+ if (code != POINTER_TYPE)
+ {
+ cp_error ("type `%#T' argument given to `delete', expected pointer",
+ type);
+ return error_mark_node;
+ }
+
+ /* Deleting a pointer with the value zero is legal and has no effect. */
+ if (integer_zerop (t))
+ return build1 (NOP_EXPR, void_type_node, t);
+ }
+
+ /* You can't delete a pointer to constant. */
+ if (code == POINTER_TYPE && TREE_READONLY (TREE_TYPE (type)))
+ {
+ error ("`const *' cannot be deleted");
+ return error_mark_node;
+ }
+
+#if 0
+ /* If the type has no destructor, then we should build a regular
+ delete, instead of a vector delete. Otherwise, we would end
+ up passing a bogus offset into __builtin_delete, which is
+ not expecting it. */
+ if (doing_vec
+ && TREE_CODE (type) == POINTER_TYPE
+ && !TYPE_HAS_DESTRUCTOR (TREE_TYPE (type)))
+ {
+ doing_vec = 0;
+ use_global_delete = 1;
+ }
+#endif
+
+ if (doing_vec)
+ return build_vec_delete (t, maxindex, elt_size, integer_one_node,
+ integer_two_node, use_global_delete);
+ else
+ return build_delete (type, t, integer_three_node,
+ LOOKUP_NORMAL|LOOKUP_HAS_IN_CHARGE,
+ use_global_delete);
+}
+
+/* Sanity check: report error if this function FUNCTION is not
+ really a member of the class (CTYPE) it is supposed to belong to.
+ CNAME is the same here as it is for grokclassfn above. */
+
+void
+check_classfn (ctype, cname, function)
+ tree ctype, cname, function;
+{
+ tree fn_name = DECL_NAME (function);
+ tree fndecl;
+ tree method_vec = CLASSTYPE_METHOD_VEC (ctype);
+ tree *methods = 0;
+ tree *end = 0;
+
+ if (method_vec != 0)
+ {
+ methods = &TREE_VEC_ELT (method_vec, 0);
+ end = TREE_VEC_END (method_vec);
+
+ /* First suss out ctors and dtors. */
+ if (*methods && fn_name == cname)
+ goto got_it;
+
+ while (++methods != end)
+ {
+ if (fn_name == DECL_NAME (*methods))
+ {
+ got_it:
+ fndecl = *methods;
+ while (fndecl)
+ {
+ if (DECL_ASSEMBLER_NAME (function) == DECL_ASSEMBLER_NAME (fndecl))
+ return;
+ fndecl = DECL_CHAIN (fndecl);
+ }
+ break; /* loser */
+ }
+ }
+ }
+
+ if (methods != end)
+ cp_error ("argument list for `%D' does not match any in class `%T'",
+ fn_name, ctype);
+ else
+ {
+ methods = 0;
+ cp_error ("no `%D' member function declared in class `%T'",
+ fn_name, ctype);
+ }
+
+ /* If we did not find the method in the class, add it to
+ avoid spurious errors. */
+ add_method (ctype, methods, function);
+}
+
+/* Process the specs, declarator (NULL if omitted) and width (NULL if omitted)
+ of a structure component, returning a FIELD_DECL node.
+ QUALS is a list of type qualifiers for this decl (such as for declaring
+ const member functions).
+
+ This is done during the parsing of the struct declaration.
+ The FIELD_DECL nodes are chained together and the lot of them
+ are ultimately passed to `build_struct' to make the RECORD_TYPE node.
+
+ C++:
+
+ If class A defines that certain functions in class B are friends, then
+ the way I have set things up, it is B who is interested in permission
+ granted by A. However, it is in A's context that these declarations
+ are parsed. By returning a void_type_node, class A does not attempt
+ to incorporate the declarations of the friends within its structure.
+
+ DO NOT MAKE ANY CHANGES TO THIS CODE WITHOUT MAKING CORRESPONDING
+ CHANGES TO CODE IN `start_method'. */
+
+tree
+grokfield (declarator, declspecs, raises, init, asmspec_tree)
+ tree declarator, declspecs, raises, init, asmspec_tree;
+{
+ register tree value;
+ char *asmspec = 0;
+
+ /* Convert () initializers to = initializers. */
+ if (init == NULL_TREE && declarator != NULL_TREE
+ && TREE_CODE (declarator) == CALL_EXPR
+ && TREE_OPERAND (declarator, 0)
+ && (TREE_CODE (TREE_OPERAND (declarator, 0)) == IDENTIFIER_NODE
+ || TREE_CODE (TREE_OPERAND (declarator, 0)) == SCOPE_REF)
+ && parmlist_is_exprlist (TREE_OPERAND (declarator, 1)))
+ {
+ init = TREE_OPERAND (declarator, 1);
+ declarator = TREE_OPERAND (declarator, 0);
+ }
+
+ if (init
+ && TREE_CODE (init) == TREE_LIST
+ && TREE_VALUE (init) == error_mark_node
+ && TREE_CHAIN (init) == NULL_TREE)
+ init = NULL_TREE;
+
+ value = grokdeclarator (declarator, declspecs, FIELD, init != 0, raises);
+ if (! value)
+ return value; /* friend or constructor went bad. */
+
+ /* Pass friendly classes back. */
+ if (TREE_CODE (value) == VOID_TYPE)
+ return void_type_node;
+
+ if (DECL_NAME (value) != NULL_TREE
+ && IDENTIFIER_POINTER (DECL_NAME (value))[0] == '_'
+ && ! strcmp (IDENTIFIER_POINTER (DECL_NAME (value)), "_vptr"))
+ cp_error ("member `%D' conflicts with virtual function table field name", value);
+
+ /* Stash away type declarations. */
+ if (TREE_CODE (value) == TYPE_DECL)
+ {
+ DECL_NONLOCAL (value) = 1;
+ DECL_CONTEXT (value) = current_class_type;
+ DECL_CLASS_CONTEXT (value) = current_class_type;
+ CLASSTYPE_LOCAL_TYPEDECLS (current_class_type) = 1;
+ pushdecl_class_level (value);
+ return value;
+ }
+
+ if (IS_SIGNATURE (current_class_type)
+ && TREE_CODE (value) != FUNCTION_DECL)
+ {
+ error ("field declaration not allowed in signature");
+ return void_type_node;
+ }
+
+ if (DECL_IN_AGGR_P (value))
+ {
+ cp_error ("`%D' is already defined in the class %T", value,
+ DECL_CONTEXT (value));
+ return void_type_node;
+ }
+
+ if (flag_cadillac)
+ cadillac_start_decl (value);
+
+ if (asmspec_tree)
+ asmspec = TREE_STRING_POINTER (asmspec_tree);
+
+ if (init)
+ {
+ if (IS_SIGNATURE (current_class_type)
+ && TREE_CODE (value) == FUNCTION_DECL)
+ {
+ error ("function declarations cannot have initializers in signature");
+ init = NULL_TREE;
+ }
+ else if (TREE_CODE (value) == FUNCTION_DECL)
+ {
+ grok_function_init (value, init);
+ init = NULL_TREE;
+ }
+ else if (pedantic && TREE_CODE (value) != VAR_DECL)
+ /* Already complained in grokdeclarator. */
+ init = NULL_TREE;
+ else
+ {
+ /* We allow initializers to become parameters to base
+ initializers. */
+ if (TREE_CODE (init) == TREE_LIST)
+ {
+ if (TREE_CHAIN (init) == NULL_TREE)
+ init = TREE_VALUE (init);
+ else
+ init = digest_init (TREE_TYPE (value), init, (tree *)0);
+ }
+
+ if (TREE_CODE (init) == CONST_DECL)
+ init = DECL_INITIAL (init);
+ else if (TREE_READONLY_DECL_P (init))
+ init = decl_constant_value (init);
+ else if (TREE_CODE (init) == CONSTRUCTOR)
+ init = digest_init (TREE_TYPE (value), init, (tree *)0);
+ my_friendly_assert (TREE_PERMANENT (init), 192);
+ if (init == error_mark_node)
+ /* We must make this look different than `error_mark_node'
+ because `decl_const_value' would mis-interpret it
+ as only meaning that this VAR_DECL is defined. */
+ init = build1 (NOP_EXPR, TREE_TYPE (value), init);
+ else if (! TREE_CONSTANT (init))
+ {
+ /* We can allow references to things that are effectively
+ static, since references are initialized with the address. */
+ if (TREE_CODE (TREE_TYPE (value)) != REFERENCE_TYPE
+ || (TREE_STATIC (init) == 0
+ && (TREE_CODE_CLASS (TREE_CODE (init)) != 'd'
+ || DECL_EXTERNAL (init) == 0)))
+ {
+ error ("field initializer is not constant");
+ init = error_mark_node;
+ }
+ }
+ }
+ }
+
+ /* The corresponding pop_obstacks is in finish_decl. */
+ push_obstacks_nochange ();
+
+ if (TREE_CODE (value) == VAR_DECL)
+ {
+ /* We cannot call pushdecl here, because that would
+ fill in the value of our TREE_CHAIN. Instead, we
+ modify finish_decl to do the right thing, namely, to
+ put this decl out straight away. */
+ if (TREE_PUBLIC (value))
+ {
+ /* current_class_type can be NULL_TREE in case of error. */
+ if (asmspec == 0 && current_class_type)
+ {
+ tree name;
+ char *buf, *buf2;
+
+ buf2 = build_overload_name (current_class_type, 1, 1);
+ buf = (char *)alloca (IDENTIFIER_LENGTH (DECL_NAME (value))
+ + sizeof (STATIC_NAME_FORMAT)
+ + strlen (buf2));
+ sprintf (buf, STATIC_NAME_FORMAT, buf2,
+ IDENTIFIER_POINTER (DECL_NAME (value)));
+ name = get_identifier (buf);
+ TREE_PUBLIC (value) = 1;
+ DECL_INITIAL (value) = error_mark_node;
+ DECL_ASSEMBLER_NAME (value) = name;
+ }
+ pending_statics = perm_tree_cons (NULL_TREE, value, pending_statics);
+
+ /* Static consts need not be initialized in the class definition. */
+ if (init != NULL_TREE && TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (value)))
+ {
+ static int explanation = 0;
+
+ error ("initializer invalid for static member with constructor");
+ if (explanation++ == 0)
+ error ("(you really want to initialize it separately)");
+ init = 0;
+ }
+ /* Force the compiler to know when an uninitialized static
+ const member is being used. */
+ if (TYPE_READONLY (value) && init == 0)
+ TREE_USED (value) = 1;
+ }
+ DECL_INITIAL (value) = init;
+ DECL_IN_AGGR_P (value) = 1;
+
+ finish_decl (value, init, asmspec_tree, 1);
+ pushdecl_class_level (value);
+ return value;
+ }
+ if (TREE_CODE (value) == FIELD_DECL)
+ {
+ if (asmspec)
+ DECL_ASSEMBLER_NAME (value) = get_identifier (asmspec);
+ if (DECL_INITIAL (value) == error_mark_node)
+ init = error_mark_node;
+ finish_decl (value, init, asmspec_tree, 1);
+ DECL_INITIAL (value) = init;
+ DECL_IN_AGGR_P (value) = 1;
+ return value;
+ }
+ if (TREE_CODE (value) == FUNCTION_DECL)
+ {
+ if (DECL_CHAIN (value) != NULL_TREE)
+ {
+ /* Need a fresh node here so that we don't get circularity
+ when we link these together. */
+ value = copy_node (value);
+ /* When does this happen? */
+ my_friendly_assert (init == NULL_TREE, 193);
+ }
+ finish_decl (value, init, asmspec_tree, 1);
+
+ /* Pass friends back this way. */
+ if (DECL_FRIEND_P (value))
+ return void_type_node;
+
+ if (current_function_decl && ! IS_SIGNATURE (current_class_type))
+ cp_error ("method `%#D' of local class must be defined in class body",
+ value);
+
+ DECL_IN_AGGR_P (value) = 1;
+ return value;
+ }
+ my_friendly_abort (21);
+ /* NOTREACHED */
+ return NULL_TREE;
+}
+
+/* Like `grokfield', but for bitfields.
+ WIDTH is non-NULL for bit fields only, and is an INTEGER_CST node. */
+
+tree
+grokbitfield (declarator, declspecs, width)
+ tree declarator, declspecs, width;
+{
+ register tree value = grokdeclarator (declarator, declspecs, BITFIELD, 0, NULL_TREE);
+
+ if (! value) return NULL_TREE; /* friends went bad. */
+
+ /* Pass friendly classes back. */
+ if (TREE_CODE (value) == VOID_TYPE)
+ return void_type_node;
+
+ if (TREE_CODE (value) == TYPE_DECL)
+ {
+ cp_error ("cannot declare `%D' to be a bitfield type", value);
+ return NULL_TREE;
+ }
+
+ if (IS_SIGNATURE (current_class_type))
+ {
+ error ("field declaration not allowed in signature");
+ return void_type_node;
+ }
+
+ if (DECL_IN_AGGR_P (value))
+ {
+ cp_error ("`%D' is already defined in the class %T", value,
+ DECL_CONTEXT (value));
+ return void_type_node;
+ }
+
+ GNU_xref_member (current_class_name, value);
+
+ if (TREE_STATIC (value))
+ {
+ cp_error ("static member `%D' cannot be a bitfield", value);
+ return NULL_TREE;
+ }
+ finish_decl (value, NULL_TREE, NULL_TREE, 0);
+
+ if (width != error_mark_node)
+ {
+ /* detect invalid field size. */
+ if (TREE_CODE (width) == CONST_DECL)
+ width = DECL_INITIAL (width);
+ else if (TREE_READONLY_DECL_P (width))
+ width = decl_constant_value (width);
+ if (TREE_CODE (width) != INTEGER_CST)
+ {
+ cp_error ("structure field `%D' width not an integer constant",
+ value);
+ DECL_INITIAL (value) = NULL_TREE;
+ }
+ else
+ {
+ constant_expression_warning (width);
+ DECL_INITIAL (value) = width;
+ DECL_BIT_FIELD (value) = 1;
+ }
+ }
+
+ DECL_IN_AGGR_P (value) = 1;
+ return value;
+}
+
+#if 0
+/* Like GROKFIELD, except that the declarator has been
+ buried in DECLSPECS. Find the declarator, and
+ return something that looks like it came from
+ GROKFIELD. */
+tree
+groktypefield (declspecs, parmlist)
+ tree declspecs;
+ tree parmlist;
+{
+ tree spec = declspecs;
+ tree prev = NULL_TREE;
+
+ tree type_id = NULL_TREE;
+ tree quals = NULL_TREE;
+ tree lengths = NULL_TREE;
+ tree decl = NULL_TREE;
+
+ while (spec)
+ {
+ register tree id = TREE_VALUE (spec);
+
+ if (TREE_CODE (spec) != TREE_LIST)
+ /* Certain parse errors slip through. For example,
+ `int class ();' is not caught by the parser. Try
+ weakly to recover here. */
+ return NULL_TREE;
+
+ if (TREE_CODE (id) == TYPE_DECL
+ || (TREE_CODE (id) == IDENTIFIER_NODE && TREE_TYPE (id)))
+ {
+ /* We have a constructor/destructor or
+ conversion operator. Use it. */
+ if (prev)
+ TREE_CHAIN (prev) = TREE_CHAIN (spec);
+ else
+ declspecs = TREE_CHAIN (spec);
+
+ type_id = id;
+ goto found;
+ }
+ prev = spec;
+ spec = TREE_CHAIN (spec);
+ }
+
+ /* Nope, we have a conversion operator to a scalar type or something
+ else, that includes things like constructor declarations for
+ templates. */
+ spec = declspecs;
+ while (spec)
+ {
+ tree id = TREE_VALUE (spec);
+
+ if (TREE_CODE (id) == IDENTIFIER_NODE)
+ {
+ if (id == ridpointers[(int)RID_INT]
+ || id == ridpointers[(int)RID_DOUBLE]
+ || id == ridpointers[(int)RID_FLOAT]
+ || id == ridpointers[(int)RID_WCHAR])
+ {
+ if (type_id)
+ error ("extra `%s' ignored",
+ IDENTIFIER_POINTER (id));
+ else
+ type_id = id;
+ }
+ else if (id == ridpointers[(int)RID_LONG]
+ || id == ridpointers[(int)RID_SHORT]
+ || id == ridpointers[(int)RID_CHAR])
+ {
+ lengths = tree_cons (NULL_TREE, id, lengths);
+ }
+ else if (id == ridpointers[(int)RID_VOID])
+ {
+ if (type_id)
+ error ("spurious `void' type ignored");
+ else
+ error ("conversion to `void' type invalid");
+ }
+ else if (id == ridpointers[(int)RID_AUTO]
+ || id == ridpointers[(int)RID_REGISTER]
+ || id == ridpointers[(int)RID_TYPEDEF]
+ || id == ridpointers[(int)RID_CONST]
+ || id == ridpointers[(int)RID_VOLATILE])
+ {
+ error ("type specifier `%s' used invalidly",
+ IDENTIFIER_POINTER (id));
+ }
+ else if (id == ridpointers[(int)RID_FRIEND]
+ || id == ridpointers[(int)RID_VIRTUAL]
+ || id == ridpointers[(int)RID_INLINE]
+ || id == ridpointers[(int)RID_UNSIGNED]
+ || id == ridpointers[(int)RID_SIGNED]
+ || id == ridpointers[(int)RID_STATIC]
+ || id == ridpointers[(int)RID_EXTERN])
+ {
+ quals = tree_cons (NULL_TREE, id, quals);
+ }
+ else
+ {
+ /* Happens when we have a global typedef
+ and a class-local member function with
+ the same name. */
+ type_id = id;
+ goto found;
+ }
+ }
+ else if (TREE_CODE (id) == RECORD_TYPE)
+ {
+ type_id = TYPE_NAME (id);
+ if (TREE_CODE (type_id) == TYPE_DECL)
+ type_id = DECL_NAME (type_id);
+ if (type_id == NULL_TREE)
+ error ("identifier for aggregate type conversion omitted");
+ }
+ else if (TREE_CODE_CLASS (TREE_CODE (id)) == 't')
+ error ("`operator' missing on conversion operator or tag missing from type");
+ else
+ my_friendly_abort (194);
+ spec = TREE_CHAIN (spec);
+ }
+
+ if (type_id)
+ declspecs = chainon (lengths, quals);
+ else if (lengths)
+ {
+ if (TREE_CHAIN (lengths))
+ error ("multiple length specifiers");
+ type_id = ridpointers[(int)RID_INT];
+ declspecs = chainon (lengths, quals);
+ }
+ else if (quals)
+ {
+ error ("no type given, defaulting to `operator int ...'");
+ type_id = ridpointers[(int)RID_INT];
+ declspecs = quals;
+ }
+ else
+ return NULL_TREE;
+
+ found:
+ decl = grokdeclarator (build_parse_node (CALL_EXPR, type_id, parmlist, NULL_TREE),
+ declspecs, FIELD, 0, NULL_TREE);
+ if (decl == NULL_TREE)
+ return NULL_TREE;
+
+ if (TREE_CODE (decl) == FUNCTION_DECL && DECL_CHAIN (decl) != NULL_TREE)
+ {
+ /* Need a fresh node here so that we don't get circularity
+ when we link these together. */
+ decl = copy_node (decl);
+ }
+
+ if (decl == void_type_node
+ || (TREE_CODE (decl) == FUNCTION_DECL
+ && TREE_CODE (TREE_TYPE (decl)) != METHOD_TYPE))
+ /* bunch of friends. */
+ return decl;
+
+ if (DECL_IN_AGGR_P (decl))
+ {
+ cp_error ("`%D' already defined in the class ", decl);
+ return void_type_node;
+ }
+
+ finish_decl (decl, NULL_TREE, NULL_TREE, 0);
+
+ /* If this declaration is common to another declaration
+ complain about such redundancy, and return NULL_TREE
+ so that we don't build a circular list. */
+ if (DECL_CHAIN (decl))
+ {
+ cp_error ("function `%D' declared twice in class %T", decl,
+ DECL_CONTEXT (decl));
+ return NULL_TREE;
+ }
+ DECL_IN_AGGR_P (decl) = 1;
+ return decl;
+}
+#endif
+
+tree
+grokoptypename (declspecs, declarator)
+ tree declspecs, declarator;
+{
+ tree t = grokdeclarator (declarator, declspecs, TYPENAME, 0, NULL_TREE);
+ return build_typename_overload (t);
+}
+
+/* When a function is declared with an initializer,
+ do the right thing. Currently, there are two possibilities:
+
+ class B
+ {
+ public:
+ // initialization possibility #1.
+ virtual void f () = 0;
+ int g ();
+ };
+
+ class D1 : B
+ {
+ public:
+ int d1;
+ // error, no f ();
+ };
+
+ class D2 : B
+ {
+ public:
+ int d2;
+ void f ();
+ };
+
+ class D3 : B
+ {
+ public:
+ int d3;
+ // initialization possibility #2
+ void f () = B::f;
+ };
+
+*/
+
+int
+copy_assignment_arg_p (parmtype, virtualp)
+ tree parmtype;
+ int virtualp;
+{
+ if (TREE_CODE (parmtype) == REFERENCE_TYPE)
+ parmtype = TREE_TYPE (parmtype);
+
+ if ((TYPE_MAIN_VARIANT (parmtype) == current_class_type)
+ || (virtualp && DERIVED_FROM_P (parmtype, current_class_type)))
+ return 1;
+
+ return 0;
+}
+
+static void
+grok_function_init (decl, init)
+ tree decl;
+ tree init;
+{
+ /* An initializer for a function tells how this function should
+ be inherited. */
+ tree type = TREE_TYPE (decl);
+
+ if (TREE_CODE (type) == FUNCTION_TYPE)
+ cp_error ("initializer specified for non-member function `%D'", decl);
+ else if (DECL_VINDEX (decl) == NULL_TREE)
+ cp_error ("initializer specified for non-virtual method `%D'", decl);
+ else if (integer_zerop (init))
+ {
+#if 0
+ /* Mark this function as being "defined". */
+ DECL_INITIAL (decl) = error_mark_node;
+ /* pure virtual destructors must be defined. */
+ /* pure virtual needs to be defined (as abort) only when put in
+ vtbl. For wellformed call, it should be itself. pr4737 */
+ if (!DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (decl)))
+ {
+ extern tree abort_fndecl;
+ /* Give this node rtl from `abort'. */
+ DECL_RTL (decl) = DECL_RTL (abort_fndecl);
+ }
+#endif
+ DECL_ABSTRACT_VIRTUAL_P (decl) = 1;
+ if (DECL_NAME (decl) == ansi_opname [(int) MODIFY_EXPR])
+ {
+ tree parmtype
+ = TREE_VALUE (TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (decl))));
+
+ if (copy_assignment_arg_p (parmtype, 1))
+ TYPE_HAS_ABSTRACT_ASSIGN_REF (current_class_type) = 1;
+ }
+ }
+ else if (TREE_CODE (init) == OFFSET_REF
+ && TREE_OPERAND (init, 0) == NULL_TREE
+ && TREE_CODE (TREE_TYPE (init)) == METHOD_TYPE)
+ {
+ tree basetype = DECL_CLASS_CONTEXT (init);
+ tree basefn = TREE_OPERAND (init, 1);
+ if (TREE_CODE (basefn) != FUNCTION_DECL)
+ cp_error ("non-method initializer invalid for method `%D'", decl);
+ else if (! BINFO_OFFSET_ZEROP (TYPE_BINFO (DECL_CLASS_CONTEXT (basefn))))
+ sorry ("base member function from other than first base class");
+ else
+ {
+ tree binfo = get_binfo (basetype, TYPE_METHOD_BASETYPE (type), 1);
+ if (binfo == error_mark_node)
+ ;
+ else if (binfo == 0)
+ error_not_base_type (TYPE_METHOD_BASETYPE (TREE_TYPE (init)),
+ TYPE_METHOD_BASETYPE (type));
+ else
+ {
+ /* Mark this function as being defined,
+ and give it new rtl. */
+ DECL_INITIAL (decl) = error_mark_node;
+ DECL_RTL (decl) = DECL_RTL (basefn);
+ }
+ }
+ }
+ else
+ cp_error ("invalid initializer for virtual method `%D'", decl);
+}
+
+/* When we get a declaration of the form
+
+ type cname::fname ...
+
+ the node for `cname::fname' gets built here in a special way.
+ Namely, we push into `cname's scope. When this declaration is
+ processed, we pop back out. */
+tree
+build_push_scope (cname, name)
+ tree cname;
+ tree name;
+{
+ extern int current_class_depth;
+ tree ctype, rval;
+ int is_ttp = 0;
+
+ if (cname == error_mark_node)
+ return error_mark_node;
+
+ ctype = IDENTIFIER_TYPE_VALUE (cname);
+
+ if (TREE_CODE (ctype) == TEMPLATE_TYPE_PARM)
+ is_ttp = 1;
+ else if (ctype == NULL_TREE || ! IS_AGGR_TYPE (ctype))
+ {
+ cp_error ("`%T' not defined as aggregate type", cname);
+ return name;
+ }
+ else if (IS_SIGNATURE (ctype))
+ {
+ error ("cannot push into signature scope, scope resolution operator ignored");
+ return name;
+ }
+
+ rval = build_parse_node (SCOPE_REF, cname, name);
+
+ /* Don't need to push the scope if we're already in it.
+ We also don't need to push the scope for a ptr-to-member/method. */
+
+ if (ctype == current_class_type || TREE_CODE (name) != IDENTIFIER_NODE
+ || is_ttp)
+ return rval;
+
+ /* We do need to push the scope in this case, since CTYPE helps
+ determine subsequent intializers (i.e., Foo::Bar x = foo_enum_1;). */
+
+ push_nested_class (ctype, 3);
+ TREE_COMPLEXITY (rval) = current_class_depth;
+ return rval;
+}
+
+void cplus_decl_attributes (decl, attributes)
+ tree decl, attributes;
+{
+ if (decl && decl != void_type_node)
+ decl_attributes (decl, attributes);
+}
+
+/* CONSTRUCTOR_NAME:
+ Return the name for the constructor (or destructor) for the
+ specified class. Argument can be RECORD_TYPE, TYPE_DECL, or
+ IDENTIFIER_NODE. When given a template, this routine doesn't
+ lose the specialization. */
+tree
+constructor_name_full (thing)
+ tree thing;
+{
+ if (TREE_CODE (thing) == UNINSTANTIATED_P_TYPE)
+ return DECL_NAME (UPT_TEMPLATE (thing));
+ if (IS_AGGR_TYPE_CODE (TREE_CODE (thing)))
+ {
+ if (TYPE_WAS_ANONYMOUS (thing) && TYPE_HAS_CONSTRUCTOR (thing))
+ thing = DECL_NAME (TREE_VEC_ELT (TYPE_METHODS (thing), 0));
+ else
+ thing = TYPE_NAME (thing);
+ }
+ if (TREE_CODE (thing) == TYPE_DECL
+ || (TREE_CODE (thing) == TEMPLATE_DECL
+ && DECL_TEMPLATE_IS_CLASS (thing)))
+ thing = DECL_NAME (thing);
+ my_friendly_assert (TREE_CODE (thing) == IDENTIFIER_NODE, 197);
+ return thing;
+}
+
+/* CONSTRUCTOR_NAME:
+ Return the name for the constructor (or destructor) for the
+ specified class. Argument can be RECORD_TYPE, TYPE_DECL, or
+ IDENTIFIER_NODE. When given a template, return the plain
+ unspecialized name. */
+tree
+constructor_name (thing)
+ tree thing;
+{
+ tree t;
+ thing = constructor_name_full (thing);
+ t = IDENTIFIER_TEMPLATE (thing);
+ if (!t)
+ return thing;
+ t = TREE_PURPOSE (t);
+ return DECL_NAME (t);
+}
+
+/* Cache the value of this class's main virtual function table pointer
+ in a register variable. This will save one indirection if a
+ more than one virtual function call is made this function. */
+void
+setup_vtbl_ptr ()
+{
+ extern rtx base_init_insns;
+
+ if (base_init_insns == 0
+ && DECL_CONSTRUCTOR_P (current_function_decl))
+ emit_base_init (current_class_type, 0);
+
+#if 0
+ /* This has something a little wrong with it.
+
+ On a sun4, code like:
+
+ be L6
+ ld [%i0],%o1
+
+ is generated, when the below is used when -O4 is given. The delay
+ slot it filled with an instruction that is safe, when this isn't
+ used, like in:
+
+ be L6
+ sethi %hi(LC1),%o0
+ ld [%i0],%o1
+
+ on code like:
+
+ struct A {
+ virtual void print() { printf("xxx"); }
+ void f();
+ };
+
+ void A::f() {
+ if (this) {
+ print();
+ } else {
+ printf("0");
+ }
+ }
+
+ And that is why this is disabled for now. (mrs)
+ */
+
+ if ((flag_this_is_variable & 1) == 0
+ && optimize
+ && current_class_type
+ && CLASSTYPE_VSIZE (current_class_type)
+ && ! DECL_STATIC_FUNCTION_P (current_function_decl))
+ {
+ tree vfield = build_vfield_ref (C_C_D, current_class_type);
+ current_vtable_decl = CLASSTYPE_VTBL_PTR (current_class_type);
+ DECL_RTL (current_vtable_decl) = 0;
+ DECL_INITIAL (current_vtable_decl) = error_mark_node;
+ /* Have to cast the initializer, since it may have come from a
+ more base class then we ascribe CURRENT_VTABLE_DECL to be. */
+ finish_decl (current_vtable_decl, convert_force (TREE_TYPE (current_vtable_decl), vfield), 0, 0);
+ current_vtable_decl = build_indirect_ref (current_vtable_decl, NULL_PTR);
+ }
+ else
+#endif
+ current_vtable_decl = NULL_TREE;
+}
+
+/* Record the existence of an addressable inline function. */
+void
+mark_inline_for_output (decl)
+ tree decl;
+{
+ if (DECL_SAVED_INLINE (decl))
+ return;
+ DECL_SAVED_INLINE (decl) = 1;
+ if (DECL_PENDING_INLINE_INFO (decl) != 0
+ && ! DECL_PENDING_INLINE_INFO (decl)->deja_vu)
+ {
+ struct pending_inline *t = pending_inlines;
+ my_friendly_assert (DECL_SAVED_INSNS (decl) == 0, 198);
+ while (t)
+ {
+ if (t == DECL_PENDING_INLINE_INFO (decl))
+ break;
+ t = t->next;
+ }
+ if (t == 0)
+ {
+ t = DECL_PENDING_INLINE_INFO (decl);
+ t->next = pending_inlines;
+ pending_inlines = t;
+ }
+ DECL_PENDING_INLINE_INFO (decl) = 0;
+ }
+ saved_inlines = perm_tree_cons (NULL_TREE, decl, saved_inlines);
+}
+
+void
+clear_temp_name ()
+{
+ temp_name_counter = 0;
+}
+
+/* Hand off a unique name which can be used for variable we don't really
+ want to know about anyway, for example, the anonymous variables which
+ are needed to make references work. Declare this thing so we can use it.
+ The variable created will be of type TYPE.
+
+ STATICP is nonzero if this variable should be static. */
+
+tree
+get_temp_name (type, staticp)
+ tree type;
+ int staticp;
+{
+ char buf[sizeof (AUTO_TEMP_FORMAT) + 20];
+ tree decl;
+ int toplev = global_bindings_p ();
+
+ push_obstacks_nochange ();
+ if (toplev || staticp)
+ {
+ end_temporary_allocation ();
+ sprintf (buf, AUTO_TEMP_FORMAT, global_temp_name_counter++);
+ decl = pushdecl_top_level (build_decl (VAR_DECL, get_identifier (buf), type));
+ }
+ else
+ {
+ sprintf (buf, AUTO_TEMP_FORMAT, temp_name_counter++);
+ decl = pushdecl (build_decl (VAR_DECL, get_identifier (buf), type));
+ }
+ TREE_USED (decl) = 1;
+ TREE_STATIC (decl) = staticp;
+
+ /* If this is a local variable, then lay out its rtl now.
+ Otherwise, callers of this function are responsible for dealing
+ with this variable's rtl. */
+ if (! toplev)
+ {
+ expand_decl (decl);
+ expand_decl_init (decl);
+ }
+ pop_obstacks ();
+
+ return decl;
+}
+
+/* Get a variable which we can use for multiple assignments.
+ It is not entered into current_binding_level, because
+ that breaks things when it comes time to do final cleanups
+ (which take place "outside" the binding contour of the function). */
+tree
+get_temp_regvar (type, init)
+ tree type, init;
+{
+ static char buf[sizeof (AUTO_TEMP_FORMAT) + 20] = { '_' };
+ tree decl;
+
+ sprintf (buf+1, AUTO_TEMP_FORMAT, temp_name_counter++);
+ decl = build_decl (VAR_DECL, get_identifier (buf), type);
+ TREE_USED (decl) = 1;
+ DECL_REGISTER (decl) = 1;
+
+ if (init)
+ store_init_value (decl, init);
+
+ /* We can expand these without fear, since they cannot need
+ constructors or destructors. */
+ expand_decl (decl);
+ expand_decl_init (decl);
+
+ if (type_needs_gc_entry (type))
+ DECL_GC_OFFSET (decl) = size_int (++current_function_obstack_index);
+
+ return decl;
+}
+
+/* Make the macro TEMP_NAME_P available to units which do not
+ include c-tree.h. */
+int
+temp_name_p (decl)
+ tree decl;
+{
+ return TEMP_NAME_P (decl);
+}
+
+/* Finish off the processing of a UNION_TYPE structure.
+ If there are static members, then all members are
+ static, and must be laid out together. If the
+ union is an anonymous union, we arrange for that
+ as well. PUBLIC_P is nonzero if this union is
+ not declared static. */
+void
+finish_anon_union (anon_union_decl)
+ tree anon_union_decl;
+{
+ tree type = TREE_TYPE (anon_union_decl);
+ tree field, main_decl = NULL_TREE;
+ tree elems = NULL_TREE;
+ int public_p = TREE_PUBLIC (anon_union_decl);
+ int static_p = TREE_STATIC (anon_union_decl);
+ int external_p = DECL_EXTERNAL (anon_union_decl);
+
+ if ((field = TYPE_FIELDS (type)) == NULL_TREE)
+ return;
+
+ if (public_p)
+ {
+ error ("global anonymous unions must be declared static");
+ return;
+ }
+
+ while (field)
+ {
+ tree decl = build_decl (VAR_DECL, DECL_NAME (field), TREE_TYPE (field));
+ /* tell `pushdecl' that this is not tentative. */
+ DECL_INITIAL (decl) = error_mark_node;
+ TREE_PUBLIC (decl) = public_p;
+ TREE_STATIC (decl) = static_p;
+ DECL_EXTERNAL (decl) = external_p;
+ decl = pushdecl (decl);
+
+ /* Only write out one anon union element--choose the one that
+ can hold them all. */
+ if (main_decl == NULL_TREE
+ && simple_cst_equal (DECL_SIZE (decl), DECL_SIZE (anon_union_decl)))
+ {
+ main_decl = decl;
+ }
+ else
+ {
+ /* ??? This causes there to be no debug info written out
+ about this decl. */
+ TREE_ASM_WRITTEN (decl) = 1;
+ }
+
+ DECL_INITIAL (decl) = NULL_TREE;
+ /* If there's a cleanup to do, it belongs in the
+ TREE_PURPOSE of the following TREE_LIST. */
+ elems = tree_cons (NULL_TREE, decl, elems);
+ TREE_TYPE (elems) = type;
+ field = TREE_CHAIN (field);
+ }
+ if (static_p)
+ {
+ make_decl_rtl (main_decl, 0, global_bindings_p ());
+ DECL_RTL (anon_union_decl) = DECL_RTL (main_decl);
+ }
+
+ /* The following call assumes that there are never any cleanups
+ for anonymous unions--a reasonable assumption. */
+ expand_anon_union_decl (anon_union_decl, NULL_TREE, elems);
+
+ if (flag_cadillac)
+ cadillac_finish_anon_union (anon_union_decl);
+}
+
+/* Finish and output a table which is generated by the compiler.
+ NAME is the name to give the table.
+ TYPE is the type of the table entry.
+ INIT is all the elements in the table.
+ PUBLICP is non-zero if this table should be given external access. */
+tree
+finish_table (name, type, init, publicp)
+ tree name, type, init;
+ int publicp;
+{
+ tree itype, atype, decl;
+ static tree empty_table;
+ int is_empty = 0;
+ tree asmspec;
+
+ itype = build_index_type (size_int (list_length (init) - 1));
+ atype = build_cplus_array_type (type, itype);
+ layout_type (atype);
+
+ if (TREE_VALUE (init) == integer_zero_node
+ && TREE_CHAIN (init) == NULL_TREE)
+ {
+ if (empty_table == NULL_TREE)
+ {
+ empty_table = get_temp_name (atype, 1);
+ init = build (CONSTRUCTOR, atype, NULL_TREE, init);
+ TREE_CONSTANT (init) = 1;
+ TREE_STATIC (init) = 1;
+ DECL_INITIAL (empty_table) = init;
+ asmspec = build_string (IDENTIFIER_LENGTH (DECL_NAME (empty_table)),
+ IDENTIFIER_POINTER (DECL_NAME (empty_table)));
+ finish_decl (empty_table, init, asmspec, 0);
+ }
+ is_empty = 1;
+ }
+
+ if (name == NULL_TREE)
+ {
+ if (is_empty)
+ return empty_table;
+ decl = get_temp_name (atype, 1);
+ }
+ else
+ {
+ decl = build_decl (VAR_DECL, name, atype);
+ decl = pushdecl (decl);
+ TREE_STATIC (decl) = 1;
+ }
+
+ if (is_empty == 0)
+ {
+ TREE_PUBLIC (decl) = publicp;
+ init = build (CONSTRUCTOR, atype, NULL_TREE, init);
+ TREE_CONSTANT (init) = 1;
+ TREE_STATIC (init) = 1;
+ DECL_INITIAL (decl) = init;
+ asmspec = build_string (IDENTIFIER_LENGTH (DECL_NAME (decl)),
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ }
+ else
+ {
+ /* This will cause DECL to point to EMPTY_TABLE in rtl-land. */
+ DECL_EXTERNAL (decl) = 1;
+ TREE_STATIC (decl) = 0;
+ init = 0;
+ asmspec = build_string (IDENTIFIER_LENGTH (DECL_NAME (empty_table)),
+ IDENTIFIER_POINTER (DECL_NAME (empty_table)));
+ }
+
+ finish_decl (decl, init, asmspec, 0);
+ return decl;
+}
+
+/* Finish processing a builtin type TYPE. It's name is NAME,
+ its fields are in the array FIELDS. LEN is the number of elements
+ in FIELDS minus one, or put another way, it is the maximum subscript
+ used in FIELDS.
+
+ It is given the same alignment as ALIGN_TYPE. */
+void
+finish_builtin_type (type, name, fields, len, align_type)
+ tree type;
+ char *name;
+ tree fields[];
+ int len;
+ tree align_type;
+{
+ register int i;
+
+ TYPE_FIELDS (type) = fields[0];
+ for (i = 0; i < len; i++)
+ {
+ layout_type (TREE_TYPE (fields[i]));
+ DECL_FIELD_CONTEXT (fields[i]) = type;
+ TREE_CHAIN (fields[i]) = fields[i+1];
+ }
+ DECL_FIELD_CONTEXT (fields[i]) = type;
+ DECL_CLASS_CONTEXT (fields[i]) = type;
+ TYPE_ALIGN (type) = TYPE_ALIGN (align_type);
+ layout_type (type);
+#if 0 /* not yet, should get fixed properly later */
+ TYPE_NAME (type) = make_type_decl (get_identifier (name), type);
+#else
+ TYPE_NAME (type) = build_decl (TYPE_DECL, get_identifier (name), type);
+#endif
+ layout_decl (TYPE_NAME (type), 0);
+}
+
+/* Auxiliary functions to make type signatures for
+ `operator new' and `operator delete' correspond to
+ what compiler will be expecting. */
+
+extern tree sizetype;
+
+tree
+coerce_new_type (type)
+ tree type;
+{
+ int e1 = 0, e2 = 0;
+
+ if (TREE_CODE (type) == METHOD_TYPE)
+ type = build_function_type (TREE_TYPE (type), TREE_CHAIN (TYPE_ARG_TYPES (type)));
+ if (TREE_TYPE (type) != ptr_type_node)
+ e1 = 1, error ("`operator new' must return type `void *'");
+
+ /* Technically the type must be `size_t', but we may not know
+ what that is. */
+ if (TYPE_ARG_TYPES (type) == NULL_TREE)
+ e1 = 1, error ("`operator new' takes type `size_t' parameter");
+ else if (TREE_CODE (TREE_VALUE (TYPE_ARG_TYPES (type))) != INTEGER_TYPE
+ || TYPE_PRECISION (TREE_VALUE (TYPE_ARG_TYPES (type))) != TYPE_PRECISION (sizetype))
+ e2 = 1, error ("`operator new' takes type `size_t' as first parameter");
+ if (e2)
+ type = build_function_type (ptr_type_node, tree_cons (NULL_TREE, sizetype, TREE_CHAIN (TYPE_ARG_TYPES (type))));
+ else if (e1)
+ type = build_function_type (ptr_type_node, TYPE_ARG_TYPES (type));
+ return type;
+}
+
+tree
+coerce_delete_type (type)
+ tree type;
+{
+ int e1 = 0, e2 = 0, e3 = 0;
+ tree arg_types = TYPE_ARG_TYPES (type);
+
+ if (TREE_CODE (type) == METHOD_TYPE)
+ {
+ type = build_function_type (TREE_TYPE (type), TREE_CHAIN (arg_types));
+ arg_types = TREE_CHAIN (arg_types);
+ }
+ if (TREE_TYPE (type) != void_type_node)
+ e1 = 1, error ("`operator delete' must return type `void'");
+ if (arg_types == NULL_TREE
+ || TREE_VALUE (arg_types) != ptr_type_node)
+ e2 = 1, error ("`operator delete' takes type `void *' as first parameter");
+
+ if (arg_types
+ && TREE_CHAIN (arg_types)
+ && TREE_CHAIN (arg_types) != void_list_node)
+ {
+ /* Again, technically this argument must be `size_t', but again
+ we may not know what that is. */
+ tree t2 = TREE_VALUE (TREE_CHAIN (arg_types));
+ if (TREE_CODE (t2) != INTEGER_TYPE
+ || TYPE_PRECISION (t2) != TYPE_PRECISION (sizetype))
+ e3 = 1, error ("second argument to `operator delete' must be of type `size_t'");
+ else if (TREE_CHAIN (TREE_CHAIN (arg_types)) != void_list_node)
+ {
+ e3 = 1;
+ if (TREE_CHAIN (TREE_CHAIN (arg_types)))
+ error ("too many arguments in declaration of `operator delete'");
+ else
+ error ("`...' invalid in specification of `operator delete'");
+ }
+ }
+ if (e3)
+ arg_types = tree_cons (NULL_TREE, ptr_type_node, build_tree_list (NULL_TREE, sizetype));
+ else if (e3 |= e2)
+ {
+ if (arg_types == NULL_TREE)
+ arg_types = tree_cons (NULL_TREE, ptr_type_node, void_list_node);
+ else
+ arg_types = tree_cons (NULL_TREE, ptr_type_node, TREE_CHAIN (arg_types));
+ }
+ else e3 |= e1;
+
+ if (e3)
+ type = build_function_type (void_type_node, arg_types);
+
+ return type;
+}
+
+static void
+mark_vtable_entries (decl)
+ tree decl;
+{
+ tree entries = TREE_CHAIN (CONSTRUCTOR_ELTS (DECL_INITIAL (decl)));
+
+ if (flag_dossier)
+ entries = TREE_CHAIN (entries);
+
+ for (; entries; entries = TREE_CHAIN (entries))
+ {
+ tree fnaddr = FNADDR_FROM_VTABLE_ENTRY (TREE_VALUE (entries));
+ tree fn = TREE_OPERAND (fnaddr, 0);
+ TREE_ADDRESSABLE (fn) = 1;
+ if (DECL_ABSTRACT_VIRTUAL_P (fn))
+ {
+ extern tree abort_fndecl;
+ if (flag_vtable_thunks)
+ fnaddr = TREE_VALUE (entries);
+ TREE_OPERAND (fnaddr, 0) = abort_fndecl;
+ }
+ }
+}
+
+/* Set TREE_PUBLIC and/or TREE_EXTERN on the vtable DECL,
+ based on TYPE and other static flags.
+
+ Note that anything public is tagged TREE_PUBLIC, whether
+ it's public in this file or in another one. */
+
+void
+import_export_vtable (decl, type)
+ tree decl, type;
+{
+ if (write_virtuals >= 2
+ || CLASSTYPE_TEMPLATE_INSTANTIATION (type))
+ {
+ if (CLASSTYPE_INTERFACE_KNOWN (type))
+ {
+ TREE_PUBLIC (decl) = 1;
+ DECL_EXTERNAL (decl) = ! CLASSTYPE_VTABLE_NEEDS_WRITING (type);
+ }
+ }
+ else if (write_virtuals != 0)
+ {
+ TREE_PUBLIC (decl) = 1;
+ if (write_virtuals < 0)
+ DECL_EXTERNAL (decl) = 1;
+ }
+}
+
+static void
+import_export_template (type)
+ tree type;
+{
+ if (CLASSTYPE_IMPLICIT_INSTANTIATION (type)
+ && ! flag_implicit_templates
+ && CLASSTYPE_INTERFACE_UNKNOWN (type))
+ {
+ SET_CLASSTYPE_INTERFACE_KNOWN (type);
+ CLASSTYPE_INTERFACE_ONLY (type) = 1;
+ CLASSTYPE_VTABLE_NEEDS_WRITING (type) = 0;
+ }
+}
+
+static void
+finish_vtable_vardecl (prev, vars)
+ tree prev, vars;
+{
+ tree ctype = DECL_CONTEXT (vars);
+ import_export_template (ctype);
+ import_export_vtable (vars, ctype);
+
+ if (flag_vtable_thunks && !CLASSTYPE_INTERFACE_KNOWN (ctype))
+ {
+ tree method;
+ for (method = CLASSTYPE_METHODS (ctype); method != NULL_TREE;
+ method = DECL_NEXT_METHOD (method))
+ {
+ if (DECL_VINDEX (method) != NULL_TREE && !DECL_SAVED_INSNS (method)
+ && !DECL_ABSTRACT_VIRTUAL_P (method))
+ {
+ SET_CLASSTYPE_INTERFACE_KNOWN (ctype);
+ CLASSTYPE_INTERFACE_ONLY (ctype) = DECL_EXTERNAL (method);
+ TREE_PUBLIC (vars) = 1;
+ DECL_EXTERNAL (vars) = DECL_EXTERNAL (method);
+ break;
+ }
+ }
+ }
+
+ if (write_virtuals >= 0
+ && ! DECL_EXTERNAL (vars) && (TREE_PUBLIC (vars) || TREE_USED (vars)))
+ {
+ extern tree the_null_vtable_entry;
+
+ /* Stuff this virtual function table's size into
+ `pfn' slot of `the_null_vtable_entry'. */
+ tree nelts = array_type_nelts (TREE_TYPE (vars));
+ if (flag_vtable_thunks)
+ TREE_VALUE (CONSTRUCTOR_ELTS (DECL_INITIAL (vars))) = nelts;
+ else
+ SET_FNADDR_FROM_VTABLE_ENTRY (the_null_vtable_entry, nelts);
+ /* Kick out the dossier before writing out the vtable. */
+ if (flag_dossier)
+ rest_of_decl_compilation (TREE_OPERAND (FNADDR_FROM_VTABLE_ENTRY (TREE_VALUE (TREE_CHAIN (CONSTRUCTOR_ELTS (DECL_INITIAL (vars))))), 0), 0, 1, 1);
+
+ /* Write it out. */
+ mark_vtable_entries (vars);
+ if (TREE_TYPE (DECL_INITIAL (vars)) == 0)
+ store_init_value (vars, DECL_INITIAL (vars));
+
+#ifdef DWARF_DEBUGGING_INFO
+ if (write_symbols == DWARF_DEBUG)
+ {
+ /* Mark the VAR_DECL node representing the vtable itself as a
+ "gratuitous" one, thereby forcing dwarfout.c to ignore it.
+ It is rather important that such things be ignored because
+ any effort to actually generate DWARF for them will run
+ into trouble when/if we encounter code like:
+
+ #pragma interface
+ struct S { virtual void member (); };
+
+ because the artificial declaration of the vtable itself (as
+ manufactured by the g++ front end) will say that the vtable
+ is a static member of `S' but only *after* the debug output
+ for the definition of `S' has already been output. This causes
+ grief because the DWARF entry for the definition of the vtable
+ will try to refer back to an earlier *declaration* of the
+ vtable as a static member of `S' and there won't be one.
+ We might be able to arrange to have the "vtable static member"
+ attached to the member list for `S' before the debug info for
+ `S' get written (which would solve the problem) but that would
+ require more intrusive changes to the g++ front end. */
+
+ DECL_IGNORED_P (vars) = 1;
+ }
+#endif /* DWARF_DEBUGGING_INFO */
+
+ rest_of_decl_compilation (vars, NULL_PTR, 1, 1);
+ }
+ else if (TREE_USED (vars) && flag_vtable_thunks)
+ assemble_external (vars);
+ /* We know that PREV must be non-zero here. */
+ TREE_CHAIN (prev) = TREE_CHAIN (vars);
+}
+
+void
+walk_vtables (typedecl_fn, vardecl_fn)
+ register void (*typedecl_fn)();
+ register void (*vardecl_fn)();
+{
+ tree prev, vars;
+
+ for (prev = 0, vars = getdecls (); vars; vars = TREE_CHAIN (vars))
+ {
+ register tree type = TREE_TYPE (vars);
+
+ if (TREE_CODE (vars) == TYPE_DECL
+ && type != error_mark_node
+ && TYPE_LANG_SPECIFIC (type)
+ && CLASSTYPE_VSIZE (type))
+ {
+ if (typedecl_fn) (*typedecl_fn) (prev, vars);
+ }
+ else if (TREE_CODE (vars) == VAR_DECL && DECL_VIRTUAL_P (vars))
+ {
+ if (vardecl_fn) (*vardecl_fn) (prev, vars);
+ }
+ else
+ prev = vars;
+ }
+}
+
+static void
+finish_sigtable_vardecl (prev, vars)
+ tree prev, vars;
+{
+ /* We don't need to mark sigtable entries as addressable here as is done
+ for vtables. Since sigtables, unlike vtables, are always written out,
+ that was already done in build_signature_table_constructor. */
+
+ rest_of_decl_compilation (vars, NULL_PTR, 1, 1);
+
+ /* We know that PREV must be non-zero here. */
+ TREE_CHAIN (prev) = TREE_CHAIN (vars);
+}
+
+void
+walk_sigtables (typedecl_fn, vardecl_fn)
+ register void (*typedecl_fn)();
+ register void (*vardecl_fn)();
+{
+ tree prev, vars;
+
+ for (prev = 0, vars = getdecls (); vars; vars = TREE_CHAIN (vars))
+ {
+ register tree type = TREE_TYPE (vars);
+
+ if (TREE_CODE (vars) == TYPE_DECL
+ && type != error_mark_node
+ && IS_SIGNATURE (type))
+ {
+ if (typedecl_fn) (*typedecl_fn) (prev, vars);
+ }
+ else if (TREE_CODE (vars) == VAR_DECL
+ && TREE_TYPE (vars) != error_mark_node
+ && IS_SIGNATURE (TREE_TYPE (vars)))
+ {
+ if (vardecl_fn) (*vardecl_fn) (prev, vars);
+ }
+ else
+ prev = vars;
+ }
+}
+
+extern int parse_time, varconst_time;
+
+#define TIMEVAR(VAR, BODY) \
+do { int otime = get_run_time (); BODY; VAR += get_run_time () - otime; } while (0)
+
+/* This routine is called from the last rule in yyparse ().
+ Its job is to create all the code needed to initialize and
+ destroy the global aggregates. We do the destruction
+ first, since that way we only need to reverse the decls once. */
+
+void
+finish_file ()
+{
+ extern int lineno;
+ int start_time, this_time;
+
+ tree fnname;
+ tree vars = static_aggregates;
+ int needs_cleaning = 0, needs_messing_up = 0;
+
+ build_exception_table ();
+
+ if (flag_detailed_statistics)
+ dump_tree_statistics ();
+
+ /* Bad parse errors. Just forget about it. */
+ if (! global_bindings_p () || current_class_type)
+ return;
+
+ start_time = get_run_time ();
+
+ /* Push into C language context, because that's all
+ we'll need here. */
+ push_lang_context (lang_name_c);
+
+ /* Set up the name of the file-level functions we may need. */
+ /* Use a global object (which is already required to be unique over
+ the program) rather than the file name (which imposes extra
+ constraints). -- Raeburn@MIT.EDU, 10 Jan 1990. */
+
+ /* See if we really need the hassle. */
+ while (vars && needs_cleaning == 0)
+ {
+ tree decl = TREE_VALUE (vars);
+ tree type = TREE_TYPE (decl);
+ if (TYPE_NEEDS_DESTRUCTOR (type))
+ {
+ needs_cleaning = 1;
+ needs_messing_up = 1;
+ break;
+ }
+ else
+ needs_messing_up |= TYPE_NEEDS_CONSTRUCTING (type);
+ vars = TREE_CHAIN (vars);
+ }
+ if (needs_cleaning == 0)
+ goto mess_up;
+
+ /* Otherwise, GDB can get confused, because in only knows
+ about source for LINENO-1 lines. */
+ lineno -= 1;
+
+ fnname = get_file_function_name ('D');
+ start_function (void_list_node, build_parse_node (CALL_EXPR, fnname, void_list_node, NULL_TREE), 0, 0);
+ fnname = DECL_ASSEMBLER_NAME (current_function_decl);
+ store_parm_decls ();
+
+ pushlevel (0);
+ clear_last_expr ();
+ push_momentary ();
+ expand_start_bindings (0);
+
+ /* These must be done in backward order to destroy,
+ in which they happen to be! */
+ while (vars)
+ {
+ tree decl = TREE_VALUE (vars);
+ tree type = TREE_TYPE (decl);
+ tree temp = TREE_PURPOSE (vars);
+
+ if (TYPE_NEEDS_DESTRUCTOR (type))
+ {
+ if (TREE_STATIC (vars))
+ expand_start_cond (build_binary_op (NE_EXPR, temp, integer_zero_node, 1), 0);
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ temp = decl;
+ else
+ {
+ mark_addressable (decl);
+ temp = build1 (ADDR_EXPR, TYPE_POINTER_TO (type), decl);
+ }
+ temp = build_delete (TREE_TYPE (temp), temp,
+ integer_two_node, LOOKUP_NORMAL|LOOKUP_NONVIRTUAL|LOOKUP_DESTRUCTOR, 0);
+ expand_expr_stmt (temp);
+
+ if (TREE_STATIC (vars))
+ expand_end_cond ();
+ }
+ vars = TREE_CHAIN (vars);
+ }
+
+ expand_end_bindings (getdecls(), 1, 0);
+ poplevel (1, 0, 0);
+ pop_momentary ();
+
+ finish_function (lineno, 0);
+
+ assemble_destructor (IDENTIFIER_POINTER (fnname));
+
+ /* if it needed cleaning, then it will need messing up: drop through */
+
+ mess_up:
+ /* Must do this while we think we are at the top level. */
+ vars = nreverse (static_aggregates);
+ if (vars != NULL_TREE)
+ {
+ fnname = get_file_function_name ('I');
+ start_function (void_list_node, build_parse_node (CALL_EXPR, fnname, void_list_node, NULL_TREE), 0, 0);
+ fnname = DECL_ASSEMBLER_NAME (current_function_decl);
+ store_parm_decls ();
+
+ pushlevel (0);
+ clear_last_expr ();
+ push_momentary ();
+ expand_start_bindings (0);
+
+ while (vars)
+ {
+ tree decl = TREE_VALUE (vars);
+ tree init = TREE_PURPOSE (vars);
+ tree old_cleanups = cleanups_this_call;
+
+ /* If this was a static attribute within some function's scope,
+ then don't initialize it here. Also, don't bother
+ with initializers that contain errors. */
+ if (TREE_STATIC (vars)
+ || (init && TREE_CODE (init) == TREE_LIST
+ && value_member (error_mark_node, init)))
+ {
+ vars = TREE_CHAIN (vars);
+ continue;
+ }
+
+ if (TREE_CODE (decl) == VAR_DECL)
+ {
+ /* Set these global variables so that GDB at least puts
+ us near the declaration which required the initialization. */
+ input_filename = DECL_SOURCE_FILE (decl);
+ lineno = DECL_SOURCE_LINE (decl);
+ emit_note (input_filename, lineno);
+
+ /* 9.5p5: The initializer of a static member of a class has
+ the same acess rights as a member function. */
+ DECL_CLASS_CONTEXT (current_function_decl) = DECL_CONTEXT (decl);
+
+ if (init)
+ {
+ if (TREE_CODE (init) == VAR_DECL)
+ {
+ /* This behavior results when there are
+ multiple declarations of an aggregate,
+ the last of which defines it. */
+ if (DECL_RTL (init) == DECL_RTL (decl))
+ {
+ my_friendly_assert (DECL_INITIAL (decl) == error_mark_node
+ || (TREE_CODE (DECL_INITIAL (decl)) == CONSTRUCTOR
+ && CONSTRUCTOR_ELTS (DECL_INITIAL (decl)) == NULL_TREE),
+ 199);
+ init = DECL_INITIAL (init);
+ if (TREE_CODE (init) == CONSTRUCTOR
+ && CONSTRUCTOR_ELTS (init) == NULL_TREE)
+ init = NULL_TREE;
+ }
+#if 0
+ else if (TREE_TYPE (decl) == TREE_TYPE (init))
+ {
+#if 1
+ my_friendly_abort (200);
+#else
+ /* point to real decl's rtl anyway. */
+ DECL_RTL (init) = DECL_RTL (decl);
+ my_friendly_assert (DECL_INITIAL (decl) == error_mark_node,
+ 201);
+ init = DECL_INITIAL (init);
+#endif /* 1 */
+ }
+#endif /* 0 */
+ }
+ }
+ if (IS_AGGR_TYPE (TREE_TYPE (decl))
+ || init == 0
+ || TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
+ expand_aggr_init (decl, init, 0);
+ else if (TREE_CODE (init) == TREE_VEC)
+ {
+ expand_expr (expand_vec_init (decl, TREE_VEC_ELT (init, 0),
+ TREE_VEC_ELT (init, 1),
+ TREE_VEC_ELT (init, 2), 0),
+ const0_rtx, VOIDmode, 0);
+ free_temp_slots ();
+ }
+ else
+ expand_assignment (decl, init, 0, 0);
+
+ DECL_CLASS_CONTEXT (current_function_decl) = NULL_TREE;
+ }
+ else if (TREE_CODE (decl) == SAVE_EXPR)
+ {
+ if (! PARM_DECL_EXPR (decl))
+ {
+ /* a `new' expression at top level. */
+ expand_expr (decl, const0_rtx, VOIDmode, 0);
+ free_temp_slots ();
+ expand_aggr_init (build_indirect_ref (decl, NULL_PTR), init, 0);
+ }
+ }
+ else if (decl == error_mark_node)
+ ;
+ else my_friendly_abort (22);
+ vars = TREE_CHAIN (vars);
+ /* Cleanup any temporaries needed for the initial value. */
+ expand_cleanups_to (old_cleanups);
+ }
+
+ expand_end_bindings (getdecls(), 1, 0);
+ poplevel (1, 0, 0);
+ pop_momentary ();
+
+ finish_function (lineno, 0);
+ assemble_constructor (IDENTIFIER_POINTER (fnname));
+ }
+
+ /* Done with C language context needs. */
+ pop_lang_context ();
+
+ /* Now write out any static class variables (which may have since
+ learned how to be initialized). */
+ while (pending_statics)
+ {
+ tree decl = TREE_VALUE (pending_statics);
+ if (TREE_USED (decl) == 1
+ || TREE_READONLY (decl) == 0
+ || DECL_INITIAL (decl) == 0)
+ rest_of_decl_compilation (decl, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)), 1, 1);
+ pending_statics = TREE_CHAIN (pending_statics);
+ }
+
+ this_time = get_run_time ();
+ parse_time -= this_time - start_time;
+ varconst_time += this_time - start_time;
+
+ start_time = get_run_time ();
+
+ /* Now delete from the chain of variables all virtual function tables.
+ We output them all ourselves, because each will be treated specially. */
+
+#if 1
+ /* The reason for pushing garbage onto the global_binding_level is to
+ ensure that we can slice out _DECLs which pertain to virtual function
+ tables. If the last thing pushed onto the global_binding_level was a
+ virtual function table, then slicing it out would slice away all the
+ decls (i.e., we lose the head of the chain).
+
+ There are several ways of getting the same effect, from changing the
+ way that iterators over the chain treat the elements that pertain to
+ virtual function tables, moving the implementation of this code to
+ decl.c (where we can manipulate global_binding_level directly),
+ popping the garbage after pushing it and slicing away the vtable
+ stuff, or just leaving it alone. */
+
+ /* Make last thing in global scope not be a virtual function table. */
+#if 0 /* not yet, should get fixed properly later */
+ vars = make_type_decl (get_identifier (" @%$#@!"), integer_type_node);
+#else
+ vars = build_decl (TYPE_DECL, get_identifier (" @%$#@!"), integer_type_node);
+#endif
+ DECL_IGNORED_P (vars) = 1;
+ SET_DECL_ARTIFICIAL (vars);
+ pushdecl (vars);
+#endif
+
+ walk_vtables ((void (*)())0, finish_vtable_vardecl);
+ if (flag_handle_signatures)
+ walk_sigtables ((void (*)())0, finish_sigtable_vardecl);
+
+ for (vars = getdecls (); vars; vars = TREE_CHAIN (vars))
+ {
+ if (TREE_CODE (vars) == THUNK_DECL)
+ emit_thunk (vars);
+ }
+
+ {
+ int reconsider = 0; /* More may be referenced; check again */
+ tree delayed = NULL_TREE; /* These might be referenced later */
+
+ /* Now write out inline functions which had their addresses taken and
+ which were not declared virtual and which were not declared `extern
+ inline'. */
+ while (saved_inlines)
+ {
+ tree decl = TREE_VALUE (saved_inlines);
+ saved_inlines = TREE_CHAIN (saved_inlines);
+ /* Redefinition of a member function can cause DECL_SAVED_INSNS to be
+ 0; don't crash. */
+ if (TREE_ASM_WRITTEN (decl) || DECL_SAVED_INSNS (decl) == 0)
+ continue;
+ if (DECL_FUNCTION_MEMBER_P (decl) && !TREE_PUBLIC (decl))
+ {
+ tree ctype = DECL_CLASS_CONTEXT (decl);
+ if (CLASSTYPE_INTERFACE_KNOWN (ctype))
+ {
+ TREE_PUBLIC (decl) = 1;
+ DECL_EXTERNAL (decl)
+ = (CLASSTYPE_INTERFACE_ONLY (ctype)
+ || (DECL_INLINE (decl) && ! flag_implement_inlines));
+ }
+ }
+ if (TREE_PUBLIC (decl)
+ || TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))
+ || flag_keep_inline_functions)
+ {
+ if (DECL_EXTERNAL (decl)
+ || (DECL_IMPLICIT_INSTANTIATION (decl)
+ && ! flag_implicit_templates))
+ assemble_external (decl);
+ else
+ {
+ reconsider = 1;
+ temporary_allocation ();
+ output_inline_function (decl);
+ permanent_allocation (1);
+ }
+ }
+ else if (TREE_USED (decl)
+ || TREE_USED (DECL_ASSEMBLER_NAME (decl)))
+ delayed = tree_cons (NULL_TREE, decl, delayed);
+ }
+
+ if (reconsider && delayed)
+ {
+ while (reconsider)
+ {
+ tree place;
+ reconsider = 0;
+ for (place = delayed; place; place = TREE_CHAIN (place))
+ {
+ tree decl = TREE_VALUE (place);
+ if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))
+ && ! TREE_ASM_WRITTEN (decl))
+ {
+ if (DECL_EXTERNAL (decl)
+ || (DECL_IMPLICIT_INSTANTIATION (decl)
+ && ! flag_implicit_templates))
+ assemble_external (decl);
+ else
+ {
+ reconsider = 1;
+ temporary_allocation ();
+ output_inline_function (decl);
+ permanent_allocation (1);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (write_virtuals == 2)
+ {
+ /* Now complain about an virtual function tables promised
+ but not delivered. */
+ while (pending_vtables)
+ {
+ if (TREE_PURPOSE (pending_vtables) == NULL_TREE)
+ error ("virtual function table for `%s' not defined",
+ IDENTIFIER_POINTER (TREE_VALUE (pending_vtables)));
+ pending_vtables = TREE_CHAIN (pending_vtables);
+ }
+ }
+
+ permanent_allocation (1);
+ this_time = get_run_time ();
+ parse_time -= this_time - start_time;
+ varconst_time += this_time - start_time;
+
+ if (flag_detailed_statistics)
+ dump_time_statistics ();
+}
+
+/* This is something of the form 'A()()()()()+1' that has turned out to be an
+ expr. Since it was parsed like a type, we need to wade through and fix
+ that. Unfortunately, since operator() is left-associative, we can't use
+ tail recursion. In the above example, TYPE is `A', and DECL is
+ `()()()()()'.
+
+ Maybe this shouldn't be recursive, but how often will it actually be
+ used? (jason) */
+tree
+reparse_absdcl_as_expr (type, decl)
+ tree type, decl;
+{
+ /* do build_functional_cast (type, NULL_TREE) at bottom */
+ if (TREE_OPERAND (decl, 0) == NULL_TREE)
+ return build_functional_cast (type, NULL_TREE);
+
+ /* recurse */
+ decl = reparse_decl_as_expr (type, TREE_OPERAND (decl, 0));
+
+ decl = build_x_function_call (decl, NULL_TREE, current_class_decl);
+
+ if (TREE_CODE (decl) == CALL_EXPR && TREE_TYPE (decl) != void_type_node)
+ decl = require_complete_type (decl);
+
+ return decl;
+}
+
+/* This is something of the form `int ((int)(int)(int)1)' that has turned
+ out to be an expr. Since it was parsed like a type, we need to wade
+ through and fix that. Since casts are right-associative, we are
+ reversing the order, so we don't have to recurse.
+
+ In the above example, DECL is the `(int)(int)(int)', and EXPR is the
+ `1'. */
+tree
+reparse_absdcl_as_casts (decl, expr)
+ tree decl, expr;
+{
+ tree type;
+
+ if (TREE_CODE (expr) == CONSTRUCTOR)
+ {
+ type = groktypename (TREE_VALUE (TREE_OPERAND (decl, 1)));
+ decl = TREE_OPERAND (decl, 0);
+
+ if (IS_SIGNATURE (type))
+ {
+ error ("cast specifies signature type");
+ return error_mark_node;
+ }
+
+ expr = digest_init (type, expr, (tree *) 0);
+ if (TREE_CODE (type) == ARRAY_TYPE && TYPE_SIZE (type) == 0)
+ {
+ int failure = complete_array_type (type, expr, 1);
+ if (failure)
+ my_friendly_abort (78);
+ }
+ }
+
+ while (decl)
+ {
+ type = groktypename (TREE_VALUE (TREE_OPERAND (decl, 1)));
+ decl = TREE_OPERAND (decl, 0);
+ expr = build_c_cast (type, expr);
+ }
+
+ return expr;
+}
+
+/* Recursive helper function for reparse_decl_as_expr. It may be a good
+ idea to reimplement this using an explicit stack, rather than recursion. */
+static tree
+reparse_decl_as_expr1 (decl)
+ tree decl;
+{
+ switch (TREE_CODE (decl))
+ {
+ case IDENTIFIER_NODE:
+ return do_identifier (decl);
+ case INDIRECT_REF:
+ return build_x_indirect_ref
+ (reparse_decl_as_expr1 (TREE_OPERAND (decl, 0)), "unary *");
+ case ADDR_EXPR:
+ return build_x_unary_op (ADDR_EXPR,
+ reparse_decl_as_expr1 (TREE_OPERAND (decl, 0)));
+ case BIT_NOT_EXPR:
+ return build_x_unary_op (BIT_NOT_EXPR,
+ reparse_decl_as_expr1 (TREE_OPERAND (decl, 0)));
+ case SCOPE_REF:
+ return build_offset_ref (TREE_OPERAND (decl, 0), TREE_OPERAND (decl, 1));
+ case ARRAY_REF:
+ return grok_array_decl (reparse_decl_as_expr1 (TREE_OPERAND (decl, 0)),
+ TREE_OPERAND (decl, 1));
+ default:
+ my_friendly_abort (5);
+ return NULL_TREE;
+ }
+}
+
+/* This is something of the form `int (*a)++' that has turned out to be an
+ expr. It was only converted into parse nodes, so we need to go through
+ and build up the semantics. Most of the work is done by
+ reparse_decl_as_expr1, above.
+
+ In the above example, TYPE is `int' and DECL is `*a'. */
+tree
+reparse_decl_as_expr (type, decl)
+ tree type, decl;
+{
+ decl = build_tree_list (NULL_TREE, reparse_decl_as_expr1 (decl));
+ return build_functional_cast (type, decl);
+}
+
+/* This is something of the form `int (*a)' that has turned out to be a
+ decl. It was only converted into parse nodes, so we need to do the
+ checking that make_{pointer,reference}_declarator do. */
+
+tree
+finish_decl_parsing (decl)
+ tree decl;
+{
+ extern int current_class_depth;
+
+ switch (TREE_CODE (decl))
+ {
+ case IDENTIFIER_NODE:
+ return decl;
+ case INDIRECT_REF:
+ return make_pointer_declarator
+ (NULL_TREE, finish_decl_parsing (TREE_OPERAND (decl, 0)));
+ case ADDR_EXPR:
+ return make_reference_declarator
+ (NULL_TREE, finish_decl_parsing (TREE_OPERAND (decl, 0)));
+ case BIT_NOT_EXPR:
+ TREE_OPERAND (decl, 0) = finish_decl_parsing (TREE_OPERAND (decl, 0));
+ return decl;
+ case SCOPE_REF:
+ push_nested_class (TREE_TYPE (TREE_OPERAND (decl, 0)), 3);
+ TREE_COMPLEXITY (decl) = current_class_depth;
+ return decl;
+ case ARRAY_REF:
+ TREE_OPERAND (decl, 0) = finish_decl_parsing (TREE_OPERAND (decl, 0));
+ return decl;
+ default:
+ my_friendly_abort (5);
+ return NULL_TREE;
+ }
+}
+
+tree
+check_cp_case_value (value)
+ tree value;
+{
+ if (value == NULL_TREE)
+ return value;
+
+ /* build_c_cast puts on a NOP_EXPR to make a non-lvalue.
+ Strip such NOP_EXPRs. */
+ if (TREE_CODE (value) == NOP_EXPR
+ && TREE_TYPE (value) == TREE_TYPE (TREE_OPERAND (value, 0)))
+ value = TREE_OPERAND (value, 0);
+
+ if (TREE_READONLY_DECL_P (value))
+ {
+ value = decl_constant_value (value);
+ /* build_c_cast puts on a NOP_EXPR to make a non-lvalue.
+ Strip such NOP_EXPRs. */
+ if (TREE_CODE (value) == NOP_EXPR
+ && TREE_TYPE (value) == TREE_TYPE (TREE_OPERAND (value, 0)))
+ value = TREE_OPERAND (value, 0);
+ }
+ value = fold (value);
+
+ if (TREE_CODE (value) != INTEGER_CST
+ && value != error_mark_node)
+ {
+ cp_error ("case label `%E' does not reduce to an integer constant",
+ value);
+ value = error_mark_node;
+ }
+ else
+ /* Promote char or short to int. */
+ value = default_conversion (value);
+
+ constant_expression_warning (value);
+
+ return value;
+}
diff --git a/gnu/usr.bin/cc/cc1plus/edsel.c b/gnu/usr.bin/cc/cc1plus/edsel.c
new file mode 100644
index 0000000..78b2637
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/edsel.c
@@ -0,0 +1,927 @@
+/* Interface to LUCID Cadillac system for GNU compiler.
+ Copyright (C) 1988, 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "config.h"
+
+#include "tree.h"
+#include "flags.h"
+#include <stdio.h>
+#include "cp-tree.h"
+#include "obstack.h"
+
+#ifdef CADILLAC
+#include <compilerreq.h>
+#include <compilerconn.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <sys/file.h>
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+void init_cadillac ();
+
+extern char *input_filename;
+extern int lineno;
+
+/* Put random information we might want to get back from
+ Cadillac here. */
+typedef struct
+{
+ /* The connection to the Cadillac kernel. */
+ Connection *conn;
+
+ /* Input and output file descriptors for Cadillac. */
+ short fd_input, fd_output;
+
+ /* #include nesting of current file. */
+ short depth;
+
+ /* State variables for the connection. */
+ char messages;
+ char conversion;
+ char emission;
+ char process_until;
+
+ /* #if level of current file. */
+ int iflevel;
+
+ /* Line number that starts current source file. */
+ int lineno;
+
+ /* Name of current file. */
+ char *filename;
+
+ /* Where to stop processing (if process_until is set). */
+ char *end_filename;
+ int end_position;
+
+} cadillac_struct;
+static cadillac_struct cadillacObj;
+
+/* Nonzero if in the process of exiting. */
+static int exiting;
+
+void cadillac_note_source ();
+static void CWriteLanguageDecl ();
+static void CWriteLanguageType ();
+static void CWriteTopLevel ();
+static void cadillac_note_filepos ();
+static void cadillac_process_request (), cadillac_process_requests ();
+static void cadillac_switch_source ();
+static void exit_cadillac ();
+
+/* Blocking test. */
+static int
+readable_p (fd)
+ int fd;
+{
+ fd_set f;
+
+ FD_ZERO (&f);
+ FD_SET (fd, &f);
+
+ return select (32, &f, NULL, NULL, 0) == 1;
+}
+
+static CObjectType *tree_to_cadillac_map;
+struct obstack cadillac_obstack;
+
+
+#include "stack.h"
+
+struct context_level
+{
+ struct stack_level base;
+
+ tree context;
+};
+
+/* Stack for maintaining contexts (in case functions or types are nested).
+ When defining a struct type, the `context' field is the RECORD_TYPE.
+ When defining a function, the `context' field is the FUNCTION_DECL. */
+
+static struct context_level *context_stack;
+
+static struct context_level *
+push_context_level (stack, obstack)
+ struct stack_level *stack;
+ struct obstack *obstack;
+{
+ struct context_level tem;
+
+ tem.base.prev = stack;
+ return (struct context_level *)push_stack_level (obstack, &tem, sizeof (tem));
+}
+
+/* Discard a level of search allocation. */
+
+static struct context_level *
+pop_context_level (stack)
+ struct context_level *stack;
+{
+ stack = (struct context_level *)pop_stack_level (stack);
+ return stack;
+}
+
+void
+init_cadillac ()
+{
+ extern FILE *finput;
+ extern int errno;
+ CCompilerMessage* req;
+ cadillac_struct *cp = &cadillacObj;
+ int i;
+
+ if (! flag_cadillac)
+ return;
+
+ tree_to_cadillac_map = (CObjectType*) xmalloc (sizeof (CObjectType) * LAST_CPLUS_TREE_CODE);
+ for (i = 0; i < LAST_CPLUS_TREE_CODE; i++)
+ tree_to_cadillac_map[i] = MiscOType;
+ tree_to_cadillac_map[RECORD_TYPE] = StructOType;
+ tree_to_cadillac_map[UNION_TYPE] = UnionOType;
+ tree_to_cadillac_map[ENUMERAL_TYPE] = EnumTypeOType;
+ tree_to_cadillac_map[TYPE_DECL] = TypedefOType;
+ tree_to_cadillac_map[VAR_DECL] = VariableOType;
+ tree_to_cadillac_map[CONST_DECL] = EnumConstantOType;
+ tree_to_cadillac_map[FUNCTION_DECL] = FunctionOType;
+ tree_to_cadillac_map[FIELD_DECL] = FieldOType;
+
+#ifdef sun
+ on_exit (&exit_cadillac, 0);
+#endif
+
+ gcc_obstack_init (&cadillac_obstack);
+
+ /* Yow! This is the way Cadillac was designed to deal with
+ Oregon C++ compiler! */
+ cp->fd_input = flag_cadillac;
+ cp->fd_output = flag_cadillac;
+
+ /* Start in "turned-on" state. */
+ cp->messages = 1;
+ cp->conversion = 1;
+ cp->emission = 1;
+
+ /* Establish a connection with Cadillac here. */
+ cp->conn = NewConnection (cp, cp->fd_input, cp->fd_output);
+
+ CWriteHeader (cp->conn, WaitingMType, 0);
+ CWriteRequestBuffer (cp->conn);
+
+ if (!readable_p (cp->fd_input))
+ ;
+
+ req = CReadCompilerMessage (cp->conn);
+
+ if (!req)
+ switch (errno)
+ {
+ case EWOULDBLOCK:
+ sleep (5);
+ return;
+
+ case 0:
+ fatal ("init_cadillac: EOF on connection to kernel, exiting\n");
+ break;
+
+ default:
+ perror ("Editor to kernel connection");
+ exit (0);
+ }
+}
+
+static void
+cadillac_process_requests (conn)
+ Connection *conn;
+{
+ CCompilerMessage *req;
+ while (req = (CCompilerMessage*) CPeekNextRequest (conn))
+ {
+ req = CReadCompilerMessage (conn);
+ cadillac_process_request (&cadillacObj, req);
+ }
+}
+
+static void
+cadillac_process_request (cp, req)
+ cadillac_struct *cp;
+ CCompilerMessage *req;
+{
+ if (! req)
+ return;
+
+ switch (req->reqType)
+ {
+ case ProcessUntilMType:
+ if (cp->process_until)
+ my_friendly_abort (23);
+ cp->process_until = 1;
+ /* This is not really right. */
+ cp->end_position = ((CCompilerCommand*)req)->processuntil.position;
+#if 0
+ cp->end_filename = req->processuntil.filename;
+#endif
+ break;
+
+ case CommandMType:
+ switch (req->header.data)
+ {
+ case MessagesOnCType:
+ cp->messages = 1;
+ break;
+ case MessagesOffCType:
+ cp->messages = 0;
+ break;
+ case ConversionOnCType:
+ cp->conversion = 1;
+ break;
+ case ConversionOffCType:
+ cp->conversion = 0;
+ break;
+ case EmissionOnCType:
+ cp->emission = 1;
+ break;
+ case EmissionOffCType:
+ cp->emission = 0;
+ break;
+
+ case FinishAnalysisCType:
+ return;
+
+ case PuntAnalysisCType:
+ case ContinueAnalysisCType:
+ case GotoFileposCType:
+ case OpenSucceededCType:
+ case OpenFailedCType:
+ fprintf (stderr, "request type %d not implemented\n", req->reqType);
+ return;
+
+ case DieCType:
+ if (! exiting)
+ my_friendly_abort (24);
+ return;
+
+ }
+ break;
+
+ default:
+ fatal ("unknown request type %d", req->reqType);
+ }
+}
+
+void
+cadillac_start ()
+{
+ Connection *conn = cadillacObj.conn;
+ CCompilerMessage *req;
+
+ /* Let Cadillac know that we start in C++ language scope. */
+ CWriteHeader (conn, ForeignLinkageMType, LinkCPlus);
+ CWriteLength (conn);
+ CWriteRequestBuffer (conn);
+
+ cadillac_process_requests (conn);
+}
+
+static void
+cadillac_printf (msg, name)
+{
+ if (cadillacObj.messages)
+ printf ("[%s,%4d] %s `%s'\n", input_filename, lineno, msg, name);
+}
+
+void
+cadillac_start_decl (decl)
+ tree decl;
+{
+ Connection *conn = cadillacObj.conn;
+ CObjectType object_type = tree_to_cadillac_map [TREE_CODE (decl)];
+
+ if (context_stack)
+ switch (TREE_CODE (context_stack->context))
+ {
+ case FUNCTION_DECL:
+ /* Currently, cadillac only implements top-level forms. */
+ return;
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ cadillac_printf ("start class-level decl", IDENTIFIER_POINTER (DECL_NAME (decl)));
+ break;
+ default:
+ my_friendly_abort (25);
+ }
+ else
+ {
+ cadillac_printf ("start top-level decl", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)));
+ CWriteTopLevel (conn, StartMType);
+ }
+
+ CWriteLanguageDecl (conn, decl, tree_to_cadillac_map[TREE_CODE (decl)]);
+ CWriteRequestBuffer (conn);
+ cadillac_process_requests (conn);
+}
+
+void
+cadillac_finish_decl (decl)
+ tree decl;
+{
+ Connection *conn = cadillacObj.conn;
+
+ if (context_stack)
+ switch (TREE_CODE (context_stack->context))
+ {
+ case FUNCTION_DECL:
+ return;
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ cadillac_printf ("end class-level decl", IDENTIFIER_POINTER (DECL_NAME (decl)));
+ CWriteHeader (conn, EndDefMType, 0);
+ CWriteLength (conn);
+ break;
+ default:
+ my_friendly_abort (26);
+ }
+ else
+ {
+ cadillac_printf ("end top-level decl", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)));
+ CWriteHeader (conn, EndDefMType, 0);
+ CWriteLength (conn);
+ CWriteTopLevel (conn, StopMType);
+ }
+
+ CWriteRequestBuffer (conn);
+ cadillac_process_requests (conn);
+}
+
+void
+cadillac_start_function (fndecl)
+ tree fndecl;
+{
+ Connection *conn = cadillacObj.conn;
+
+ if (context_stack)
+ /* nested functions not yet handled. */
+ my_friendly_abort (27);
+
+ cadillac_printf ("start top-level function", lang_printable_name (fndecl));
+ context_stack = push_context_level (context_stack, &cadillac_obstack);
+ context_stack->context = fndecl;
+
+ CWriteTopLevel (conn, StartMType);
+ my_friendly_assert (TREE_CODE (fndecl) == FUNCTION_DECL, 202);
+ CWriteLanguageDecl (conn, fndecl,
+ (TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE
+ ? MemberFnOType : FunctionOType));
+ CWriteRequestBuffer (conn);
+ cadillac_process_requests (conn);
+}
+
+void
+cadillac_finish_function (fndecl)
+ tree fndecl;
+{
+ Connection *conn = cadillacObj.conn;
+
+ cadillac_printf ("end top-level function", lang_printable_name (fndecl));
+ context_stack = pop_context_level (context_stack);
+
+ if (context_stack)
+ /* nested functions not yet implemented. */
+ my_friendly_abort (28);
+
+ CWriteHeader (conn, EndDefMType, 0);
+ CWriteLength (conn);
+ CWriteTopLevel (conn, StopMType);
+ CWriteRequestBuffer (conn);
+ cadillac_process_requests (conn);
+}
+
+void
+cadillac_finish_anon_union (decl)
+ tree decl;
+{
+ Connection *conn = cadillacObj.conn;
+
+ if (! global_bindings_p ())
+ return;
+ cadillac_printf ("finish top-level anon union", "");
+ CWriteHeader (conn, EndDefMType, 0);
+ CWriteLength (conn);
+ CWriteTopLevel (conn, StopMType);
+ CWriteRequestBuffer (conn);
+ cadillac_process_requests (conn);
+}
+
+void
+cadillac_start_enum (type)
+ tree type;
+{
+ Connection *conn = cadillacObj.conn;
+
+ tree name = TYPE_NAME (type);
+
+ if (TREE_CODE (name) == TYPE_DECL)
+ name = DECL_NAME (name);
+
+ if (context_stack)
+ switch (TREE_CODE (context_stack->context))
+ {
+ case FUNCTION_DECL:
+ return;
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ break;
+ default:
+ my_friendly_abort (29);
+ }
+ else
+ {
+ cadillac_printf ("start top-level enum", IDENTIFIER_POINTER (name));
+ CWriteTopLevel (conn, StartMType);
+ }
+
+ CWriteLanguageType (conn, type, tree_to_cadillac_map[ENUMERAL_TYPE]);
+}
+
+void
+cadillac_finish_enum (type)
+ tree type;
+{
+ Connection *conn = cadillacObj.conn;
+ tree name = TYPE_NAME (type);
+
+ if (TREE_CODE (name) == TYPE_DECL)
+ name = DECL_NAME (name);
+
+ if (context_stack)
+ switch (TREE_CODE (context_stack->context))
+ {
+ case FUNCTION_DECL:
+ return;
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ CWriteHeader (conn, EndDefMType, 0);
+ CWriteLength (conn);
+ break;
+ default:
+ my_friendly_abort (30);
+ }
+ else
+ {
+ CWriteHeader (conn, EndDefMType, 0);
+ CWriteLength (conn);
+ cadillac_printf ("finish top-level enum", IDENTIFIER_POINTER (name));
+ CWriteTopLevel (conn, StopMType);
+ }
+
+ CWriteRequestBuffer (conn);
+ cadillac_process_requests (conn);
+}
+
+void
+cadillac_start_struct (type)
+ tree type;
+{
+ Connection *conn = cadillacObj.conn;
+ tree name = TYPE_NAME (type);
+
+ if (TREE_CODE (name) == TYPE_DECL)
+ name = DECL_NAME (name);
+
+ if (context_stack)
+ switch (TREE_CODE (context_stack->context))
+ {
+ case FUNCTION_DECL:
+ return;
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ return;
+ default:
+ my_friendly_abort (31);
+ }
+ else
+ {
+ cadillac_printf ("start struct", IDENTIFIER_POINTER (name));
+ CWriteTopLevel (conn, StartMType);
+ }
+
+ context_stack = push_context_level (context_stack, &cadillac_obstack);
+ context_stack->context = type;
+
+ CWriteLanguageType (conn, type,
+ TYPE_LANG_SPECIFIC (type) && CLASSTYPE_DECLARED_CLASS (type) ? ClassOType : tree_to_cadillac_map[TREE_CODE (type)]);
+}
+
+void
+cadillac_finish_struct (type)
+ tree type;
+{
+ Connection *conn = cadillacObj.conn;
+ tree name = TYPE_NAME (type);
+
+ if (TREE_CODE (name) == TYPE_DECL)
+ name = DECL_NAME (name);
+
+ context_stack = pop_context_level (context_stack);
+ if (context_stack)
+ return;
+
+ cadillac_printf ("finish struct", IDENTIFIER_POINTER (name));
+ CWriteHeader (conn, EndDefMType, 0);
+ CWriteLength (conn);
+ CWriteTopLevel (conn, StopMType);
+ CWriteRequestBuffer (conn);
+ cadillac_process_requests (conn);
+}
+
+void
+cadillac_finish_exception (type)
+ tree type;
+{
+ Connection *conn = cadillacObj.conn;
+
+ fatal ("cadillac_finish_exception");
+ CWriteHeader (conn, EndDefMType, 0);
+ CWriteLength (conn);
+ CWriteTopLevel (conn, StopMType);
+ CWriteRequestBuffer (conn);
+ cadillac_process_requests (conn);
+}
+
+void
+cadillac_push_class (type)
+ tree type;
+{
+}
+
+void
+cadillac_pop_class ()
+{
+}
+
+void
+cadillac_push_lang (name)
+ tree name;
+{
+ Connection *conn = cadillacObj.conn;
+ CLinkLanguageType m;
+
+ if (name == lang_name_cplusplus)
+ m = LinkCPlus;
+ else if (name == lang_name_c)
+ m = LinkC;
+ else
+ my_friendly_abort (32);
+ CWriteHeader (conn, ForeignLinkageMType, m);
+ CWriteRequestBuffer (conn);
+ cadillac_process_requests (conn);
+}
+
+void
+cadillac_pop_lang ()
+{
+ Connection *conn = cadillacObj.conn;
+
+ CWriteHeader (conn, ForeignLinkageMType, LinkPop);
+ CWriteRequestBuffer (conn);
+ cadillac_process_requests (conn);
+}
+
+void
+cadillac_finish_stmt ()
+{
+}
+
+void
+cadillac_note_source ()
+{
+ cadillacObj.lineno = lineno;
+ cadillacObj.filename = input_filename;
+}
+
+static void
+CWriteTopLevel (conn, m)
+ Connection *conn;
+ CMessageSubType m;
+{
+ static context_id = 0;
+ CWriteHeader (conn, TopLevelFormMType, m);
+ cadillac_note_filepos ();
+
+ /* Eventually, this will point somewhere into the digest file. */
+ context_id += 1;
+ CWriteSomething (conn, &context_id, sizeof (BITS32));
+
+ CWriteSomething (conn, &cadillacObj.iflevel, sizeof (BITS32));
+ CWriteLength (conn);
+}
+
+static void
+cadillac_note_filepos ()
+{
+ extern FILE *finput;
+ int pos = ftell (finput);
+ CWriteSomething (cadillacObj.conn, &pos, sizeof (BITS32));
+}
+
+void
+cadillac_switch_source (startflag)
+ int startflag;
+{
+ Connection *conn = cadillacObj.conn;
+ /* Send out the name of the source file being compiled. */
+
+ CWriteHeader (conn, SourceFileMType, startflag ? StartMType : StopMType);
+ CWriteSomething (conn, &cadillacObj.depth, sizeof (BITS16));
+ CWriteVstring0 (conn, input_filename);
+ CWriteLength (conn);
+ CWriteRequestBuffer (conn);
+ cadillac_process_requests (conn);
+}
+
+void
+cadillac_push_source ()
+{
+ cadillacObj.depth += 1;
+ cadillac_switch_source (1);
+}
+
+void
+cadillac_pop_source ()
+{
+ cadillacObj.depth -= 1;
+ cadillac_switch_source (0);
+}
+
+struct cadillac_mdep
+{
+ short object_type;
+ char linkage;
+ char access;
+ short length;
+};
+
+static void
+CWriteLanguageElem (conn, p, name)
+ Connection *conn;
+ struct cadillac_mdep *p;
+ char *name;
+{
+ CWriteSomething (conn, &p->object_type, sizeof (BITS16));
+ CWriteSomething (conn, &p->linkage, sizeof (BITS8));
+ CWriteSomething (conn, &p->access, sizeof (BITS8));
+ CWriteSomething (conn, &p->length, sizeof (BITS16));
+ CWriteVstring0 (conn, name);
+
+#if 0
+ /* Don't write date_type. */
+ CWriteVstring0 (conn, "");
+#endif
+ CWriteLength (conn);
+}
+
+static void
+CWriteLanguageDecl (conn, decl, object_type)
+ Connection *conn;
+ tree decl;
+ CObjectType object_type;
+{
+ struct cadillac_mdep foo;
+ tree name;
+
+ CWriteHeader (conn, LanguageElementMType, StartDefineMType);
+ foo.object_type = object_type;
+ if (decl_type_context (decl))
+ {
+ foo.linkage = ParentLinkage;
+ if (TREE_PRIVATE (decl))
+ foo.access = PrivateAccess;
+ else if (TREE_PROTECTED (decl))
+ foo.access = ProtectedAccess;
+ else
+ foo.access = PublicAccess;
+ }
+ else
+ {
+ if (TREE_PUBLIC (decl))
+ foo.linkage = GlobalLinkage;
+ else
+ foo.linkage = FileLinkage;
+ foo.access = PublicAccess;
+ }
+ name = DECL_NAME (decl);
+ foo.length = IDENTIFIER_LENGTH (name);
+
+ CWriteLanguageElem (conn, &foo, IDENTIFIER_POINTER (name));
+ CWriteRequestBuffer (conn);
+ cadillac_process_requests (conn);
+}
+
+static void
+CWriteLanguageType (conn, type, object_type)
+ Connection *conn;
+ tree type;
+ CObjectType object_type;
+{
+ struct cadillac_mdep foo;
+ tree name = TYPE_NAME (type);
+
+ CWriteHeader (conn, LanguageElementMType, StartDefineMType);
+ foo.object_type = object_type;
+ if (current_class_type)
+ {
+ foo.linkage = ParentLinkage;
+ if (TREE_PRIVATE (type))
+ foo.access = PrivateAccess;
+ else if (TREE_PROTECTED (type))
+ foo.access = ProtectedAccess;
+ else
+ foo.access = PublicAccess;
+ }
+ else
+ {
+ foo.linkage = NoLinkage;
+ foo.access = PublicAccess;
+ }
+ if (TREE_CODE (name) == TYPE_DECL)
+ name = DECL_NAME (name);
+
+ foo.length = IDENTIFIER_LENGTH (name);
+
+ CWriteLanguageElem (conn, &foo, IDENTIFIER_POINTER (name));
+ CWriteRequestBuffer (conn);
+ cadillac_process_requests (conn);
+}
+
+static void
+CWriteUseObject (conn, type, object_type, use)
+ Connection *conn;
+ tree type;
+ CObjectType object_type;
+ CMessageSubType use;
+{
+ struct cadillac_mdep foo;
+ tree name = NULL_TREE;
+
+ CWriteHeader (conn, LanguageElementMType, use);
+ foo.object_type = object_type;
+ if (current_class_type)
+ {
+ foo.linkage = ParentLinkage;
+ if (TREE_PRIVATE (type))
+ foo.access = PrivateAccess;
+ else if (TREE_PROTECTED (type))
+ foo.access = ProtectedAccess;
+ else
+ foo.access = PublicAccess;
+ }
+ else
+ {
+ foo.linkage = NoLinkage;
+ foo.access = PublicAccess;
+ }
+ switch (TREE_CODE (type))
+ {
+ case VAR_DECL:
+ case FIELD_DECL:
+ case TYPE_DECL:
+ case CONST_DECL:
+ case FUNCTION_DECL:
+ name = DECL_NAME (type);
+ break;
+
+ default:
+ my_friendly_abort (33);
+ }
+
+ foo.length = IDENTIFIER_LENGTH (name);
+
+ CWriteLanguageElem (conn, &foo, IDENTIFIER_POINTER (name));
+ CWriteRequestBuffer (conn);
+ cadillac_process_requests (conn);
+}
+
+/* Here's how we exit under cadillac. */
+
+static void
+exit_cadillac ()
+{
+ extern int errorcount;
+
+ Connection *conn = cadillacObj.conn;
+
+ if (flag_cadillac)
+ {
+ CCompilerMessage *req;
+
+ CWriteHeader (conn, FinishedMType,
+ errorcount ? 0 : CsObjectWritten | CsComplete);
+ /* Bye, bye! */
+ CWriteRequestBuffer (conn);
+
+ /* Block on read. */
+ while (! readable_p (cadillacObj.fd_input))
+ {
+ if (exiting)
+ my_friendly_abort (34);
+ exiting = 1;
+ }
+ exiting = 1;
+
+ req = CReadCompilerMessage (conn);
+ cadillac_process_request (&cadillacObj, req);
+ }
+}
+
+#else
+/* Stubs. */
+void init_cadillac () {}
+void cadillac_start () {}
+void cadillac_start_decl (decl)
+ tree decl;
+{}
+void
+cadillac_finish_decl (decl)
+ tree decl;
+{}
+void
+cadillac_start_function (fndecl)
+ tree fndecl;
+{}
+void
+cadillac_finish_function (fndecl)
+ tree fndecl;
+{}
+void
+cadillac_finish_anon_union (decl)
+ tree decl;
+{}
+void
+cadillac_start_enum (type)
+ tree type;
+{}
+void
+cadillac_finish_enum (type)
+ tree type;
+{}
+void
+cadillac_start_struct (type)
+ tree type;
+{}
+void
+cadillac_finish_struct (type)
+ tree type;
+{}
+void
+cadillac_finish_exception (type)
+ tree type;
+{}
+void
+cadillac_push_class (type)
+ tree type;
+{}
+void
+cadillac_pop_class ()
+{}
+void
+cadillac_push_lang (name)
+ tree name;
+{}
+void
+cadillac_pop_lang ()
+{}
+void
+cadillac_note_source ()
+{}
+void
+cadillac_finish_stmt ()
+{}
+void
+cadillac_switch_source ()
+{}
+void
+cadillac_push_source ()
+{}
+void
+cadillac_pop_source ()
+{}
+#endif
diff --git a/gnu/usr.bin/cc/cc1plus/errfn.c b/gnu/usr.bin/cc/cc1plus/errfn.c
new file mode 100644
index 0000000..1b345fd
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/errfn.c
@@ -0,0 +1,217 @@
+/* Provide a call-back mechanism for handling error output.
+ Copyright (C) 1993 Free Software Foundation, Inc.
+ Contributed by Jason Merrill (jason@cygnus.com)
+
+ This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "config.h"
+#include "tree.h"
+#include <ctype.h>
+
+/* cp_printer is the type of a function which converts an argument into
+ a string for digestion by printf. The cp_printer function should deal
+ with all memory management; the functions in this file will not free
+ the char*s returned. See error.c for an example use of this code. */
+
+typedef char* cp_printer PROTO((HOST_WIDE_INT, int));
+extern cp_printer * cp_printers[256];
+
+typedef void errorfn (); /* deliberately vague */
+
+extern char* cp_file_of PROTO((tree));
+extern int cp_line_of PROTO((tree));
+
+#define STRDUP(f) (ap = (char *) alloca (strlen (f) +1), strcpy (ap, (f)), ap)
+
+#define NARGS 3
+#define arglist a1, a2, a3
+#define arglist_dcl HOST_WIDE_INT a1, a2, a3;
+#define ARGSINIT args[0] = a1; args[1] = a2; args[2] = a3;
+#define ARGSLIST args[0], args[1], args[2]
+
+static void
+cp_thing (errfn, atarg1, format, arglist)
+ errorfn *errfn;
+ int atarg1;
+ char *format;
+ arglist_dcl
+{
+ char *fmt;
+ char *f;
+ char *ap;
+ int arg;
+ HOST_WIDE_INT atarg = atarg1 ? a1 : 0;
+ HOST_WIDE_INT args[NARGS];
+ ARGSINIT
+
+ fmt = STRDUP(format);
+
+ for (f = fmt, arg = 0; *f; ++f)
+ {
+ cp_printer * function;
+ int alternate;
+ int maybe_here;
+
+ /* ignore text */
+ if (*f != '%') continue;
+
+ ++f;
+
+ alternate = 0;
+ maybe_here = 0;
+
+ /* ignore most flags */
+ while (*f == ' ' || *f == '-' || *f == '+' || *f == '#')
+ {
+ if (*f == '+')
+ maybe_here = 1;
+ else if (*f == '#')
+ alternate = 1;
+ ++f;
+ }
+
+ /* ignore field width */
+ if (*f == '*')
+ {
+ ++f;
+ ++arg;
+ }
+ else
+ while (isdigit (*f))
+ ++f;
+
+ /* ignore precision */
+ if (*f == '.')
+ {
+ ++f;
+ if (*f == '*')
+ {
+ ++f;
+ ++arg;
+ }
+ else
+ while (isdigit (*f))
+ ++f;
+ }
+
+ /* ignore "long" */
+ if (*f == 'l')
+ ++f;
+
+ function = cp_printers[(int)*f];
+
+ if (function)
+ {
+ char *p;
+
+ if (arg >= NARGS) abort ();
+
+ if (maybe_here && atarg1)
+ atarg = args[arg];
+
+ /* Must use a temporary to avoid calling *function twice */
+ p = (*function) (args[arg], alternate);
+ args[arg] = (HOST_WIDE_INT) STRDUP(p);
+ *f = 's';
+ }
+
+ ++arg; /* Assume valid format string */
+
+ }
+
+ if (atarg)
+ {
+ char *file = cp_file_of ((tree) atarg);
+ int line = cp_line_of ((tree) atarg);
+ (*errfn) (file, line, fmt, ARGSLIST);
+ }
+ else
+ (*errfn) (fmt, ARGSLIST);
+
+}
+
+void
+cp_error (format, arglist)
+ char *format;
+ arglist_dcl
+{
+ extern errorfn error;
+ cp_thing (error, 0, format, arglist);
+}
+
+void
+cp_warning (format, arglist)
+ char *format;
+ arglist_dcl
+{
+ extern errorfn warning;
+ cp_thing (warning, 0, format, arglist);
+}
+
+void
+cp_pedwarn (format, arglist)
+ char *format;
+ arglist_dcl
+{
+ extern errorfn pedwarn;
+ cp_thing (pedwarn, 0, format, arglist);
+}
+
+void
+cp_compiler_error (format, arglist)
+ char *format;
+ arglist_dcl
+{
+ extern errorfn compiler_error;
+ cp_thing (compiler_error, 0, format, arglist);
+}
+
+void
+cp_sprintf (format, arglist)
+ char *format;
+ arglist_dcl
+{
+ extern errorfn sprintf;
+ cp_thing (sprintf, 0, format, arglist);
+}
+
+void
+cp_error_at (format, arglist)
+ char *format;
+ arglist_dcl
+{
+ extern errorfn error_with_file_and_line;
+ cp_thing (error_with_file_and_line, 1, format, arglist);
+}
+
+void
+cp_warning_at (format, arglist)
+ char *format;
+ arglist_dcl
+{
+ extern errorfn warning_with_file_and_line;
+ cp_thing (warning_with_file_and_line, 1, format, arglist);
+}
+
+void
+cp_pedwarn_at (format, arglist)
+ char *format;
+ arglist_dcl
+{
+ extern errorfn pedwarn_with_file_and_line;
+ cp_thing (pedwarn_with_file_and_line, 1, format, arglist);
+}
diff --git a/gnu/usr.bin/cc/cc1plus/error.c b/gnu/usr.bin/cc/cc1plus/error.c
new file mode 100644
index 0000000..f431f6a
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/error.c
@@ -0,0 +1,1404 @@
+/* Call-backs for C++ error reporting.
+ This code is non-reentrant.
+ Copyright (C) 1993 Free Software Foundation, Inc.
+
+ This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "config.h"
+#include "tree.h"
+#include "cp-tree.h"
+#include "obstack.h"
+#include <ctype.h>
+
+typedef char* cp_printer ();
+
+#define A args_as_string
+#define C code_as_string
+#define D decl_as_string
+#define E expr_as_string
+#define L language_as_string
+#define O op_as_string
+#define P parm_as_string
+#define T type_as_string
+
+#define _ (cp_printer *) 0
+cp_printer * cp_printers[256] =
+{
+/*0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* 0x00 */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* 0x10 */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* 0x20 */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* 0x30 */
+ _, A, _, C, D, E, _, _, _, _, _, _, L, _, _, O, /* 0x40 */
+ P, _, _, _, T, _, _, _, _, _, _, _, _, _, _, _, /* 0x50 */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* 0x60 */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* 0x70 */
+};
+#undef C
+#undef D
+#undef E
+#undef L
+#undef O
+#undef P
+#undef T
+#undef _
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+/* Obstack where we build text strings for overloading, etc. */
+static struct obstack scratch_obstack;
+static char *scratch_firstobj;
+
+# define OB_INIT() (scratch_firstobj ? (obstack_free (&scratch_obstack, scratch_firstobj), 0) : 0)
+# define OB_PUTC(C) (obstack_1grow (&scratch_obstack, (C)))
+# define OB_PUTC2(C1,C2) \
+ (obstack_1grow (&scratch_obstack, (C1)), obstack_1grow (&scratch_obstack, (C2)))
+# define OB_PUTS(S) (obstack_grow (&scratch_obstack, (S), sizeof (S) - 1))
+# define OB_PUTID(ID) \
+ (obstack_grow (&scratch_obstack, IDENTIFIER_POINTER (ID), \
+ IDENTIFIER_LENGTH (ID)))
+# define OB_PUTCP(S) (obstack_grow (&scratch_obstack, (S), strlen (S)))
+# define OB_FINISH() (obstack_1grow (&scratch_obstack, '\0'))
+# define OB_PUTI(CST) do { sprintf (digit_buffer, "%d", (CST)); \
+ OB_PUTCP (digit_buffer); } while (0)
+# define OB_UNPUT(N) obstack_blank (&scratch_obstack, - (N));
+
+# define NEXT_CODE(t) (TREE_CODE (TREE_TYPE (t)))
+
+static void dump_type (), dump_decl (), dump_function_decl ();
+static void dump_expr (), dump_unary_op (), dump_binary_op ();
+static void dump_aggr_type (), dump_type_prefix (), dump_type_suffix ();
+static void dump_function_name ();
+
+void
+init_error ()
+{
+ gcc_obstack_init (&scratch_obstack);
+ scratch_firstobj = (char *)obstack_alloc (&scratch_obstack, 0);
+}
+
+enum pad { none, before, after };
+
+static void
+dump_readonly_or_volatile (t, p)
+ tree t;
+ enum pad p;
+{
+ if (TYPE_READONLY (t) || TYPE_VOLATILE (t))
+ {
+ if (p == before) OB_PUTC (' ');
+ if (TYPE_READONLY (t))
+ OB_PUTS ("const");
+ if (TYPE_VOLATILE (t))
+ OB_PUTS ("volatile");
+ if (p == after) OB_PUTC (' ');
+ }
+}
+
+/* This must be large enough to hold any printed integer or floating-point
+ value. */
+static char digit_buffer[128];
+
+/* Dump into the obstack a human-readable equivalent of TYPE. */
+static void
+dump_type (t, v)
+ tree t;
+ int v; /* verbose? */
+{
+ if (t == NULL_TREE)
+ return;
+
+ if (TYPE_PTRMEMFUNC_P (t))
+ goto offset_type;
+
+ switch (TREE_CODE (t))
+ {
+ case ERROR_MARK:
+ OB_PUTS ("{error}");
+ break;
+
+ case UNKNOWN_TYPE:
+ OB_PUTS ("{unknown type}");
+ break;
+
+ case TREE_LIST:
+ /* i.e. function taking no arguments */
+ if (t != void_list_node)
+ {
+ dump_type (TREE_VALUE (t), v);
+ /* Can this happen other than for default arguments? */
+ if (TREE_PURPOSE (t) && v)
+ {
+ OB_PUTS (" = ");
+ dump_expr (TREE_PURPOSE (t));
+ }
+ if (TREE_CHAIN (t))
+ {
+ if (TREE_CHAIN (t) != void_list_node)
+ {
+ OB_PUTC2 (',', ' ');
+ dump_type (TREE_CHAIN (t), v);
+ }
+ }
+ else OB_PUTS (" ...");
+ }
+ break;
+
+ case IDENTIFIER_NODE:
+ OB_PUTID (t);
+ break;
+
+ case TREE_VEC:
+ dump_type (BINFO_TYPE (t), v);
+ break;
+
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ case ENUMERAL_TYPE:
+ if (TYPE_LANG_SPECIFIC (t)
+ && (IS_SIGNATURE_POINTER (t) || IS_SIGNATURE_REFERENCE (t)))
+ {
+ if (TYPE_READONLY (t) | TYPE_VOLATILE (t))
+ dump_readonly_or_volatile (t);
+ dump_type (SIGNATURE_TYPE (t), v);
+ if (IS_SIGNATURE_POINTER (t))
+ OB_PUTC ('*');
+ else
+ OB_PUTC ('&');
+ }
+ else
+ dump_aggr_type (t, v);
+ break;
+
+ case TYPE_DECL:
+ dump_decl (t, v);
+ break;
+
+ case INTEGER_TYPE:
+ if (!TREE_UNSIGNED (TYPE_MAIN_VARIANT (t)) && TREE_UNSIGNED (t))
+ OB_PUTS ("unsigned ");
+ else if (TREE_UNSIGNED (TYPE_MAIN_VARIANT (t)) && !TREE_UNSIGNED (t))
+ OB_PUTS ("signed ");
+
+ /* fall through. */
+ case REAL_TYPE:
+ case VOID_TYPE:
+ case BOOLEAN_TYPE:
+ dump_readonly_or_volatile (t, after);
+ OB_PUTID (TYPE_IDENTIFIER (t));
+ break;
+
+ case TEMPLATE_TYPE_PARM:
+ OB_PUTID (TYPE_IDENTIFIER (t));
+ break;
+
+ case UNINSTANTIATED_P_TYPE:
+ OB_PUTID (DECL_NAME (UPT_TEMPLATE (t)));
+ OB_PUTS ("<...>");
+ break;
+
+ /* This is not always necessary for pointers and such, but doing this
+ reduces code size. */
+ case ARRAY_TYPE:
+ case POINTER_TYPE:
+ case REFERENCE_TYPE:
+ case OFFSET_TYPE:
+ offset_type:
+ case FUNCTION_TYPE:
+ case METHOD_TYPE:
+ dump_type_prefix (t, v);
+ dump_type_suffix (t, v);
+ break;
+
+ default:
+ sorry ("`%s' not supported by dump_type",
+ tree_code_name[(int) TREE_CODE (t)]);
+ }
+}
+
+static char *
+aggr_variety (t)
+ tree t;
+{
+ if (TREE_CODE (t) == ENUMERAL_TYPE)
+ return "enum";
+ else if (TREE_CODE (t) == UNION_TYPE)
+ return "union";
+ else if (TYPE_LANG_SPECIFIC (t) && CLASSTYPE_DECLARED_CLASS (t))
+ return "class";
+ else if (TYPE_LANG_SPECIFIC (t) && IS_SIGNATURE (t))
+ return "signature";
+ else
+ return "struct";
+}
+
+/* Print out a class declaration, in the form `class foo'. */
+static void
+dump_aggr_type (t, v)
+ tree t;
+ int v; /* verbose? */
+{
+ tree name;
+ char *variety = aggr_variety (t);
+
+ dump_readonly_or_volatile (t, after);
+
+ if (v > 0)
+ {
+ OB_PUTCP (variety);
+ OB_PUTC (' ');
+ }
+
+ name = TYPE_NAME (t);
+
+ if (DECL_CONTEXT (name))
+ {
+ /* FUNCTION_DECL or RECORD_TYPE */
+ dump_decl (DECL_CONTEXT (name), 0);
+ OB_PUTC2 (':', ':');
+ }
+
+ /* kludge around wierd behavior on g++.brendan/line1.C */
+ if (TREE_CODE (name) != IDENTIFIER_NODE)
+ name = DECL_NAME (name);
+
+ if (ANON_AGGRNAME_P (name))
+ {
+ OB_PUTS ("{anonymous");
+ if (!v)
+ {
+ OB_PUTC (' ');
+ OB_PUTCP (variety);
+ }
+ OB_PUTC ('}');
+ }
+ else
+ OB_PUTID (name);
+}
+
+/* Dump into the obstack the initial part of the output for a given type.
+ This is necessary when dealing with things like functions returning
+ functions. Examples:
+
+ return type of `int (* fee ())()': pointer -> function -> int. Both
+ pointer (and reference and offset) and function (and member) types must
+ deal with prefix and suffix.
+
+ Arrays must also do this for DECL nodes, like int a[], and for things like
+ int *[]&. */
+
+static void
+dump_type_prefix (t, v)
+ tree t;
+ int v; /* verbosity */
+{
+ if (TYPE_PTRMEMFUNC_P (t))
+ {
+ t = TYPE_PTRMEMFUNC_FN_TYPE (t);
+ goto offset_type;
+ }
+
+ switch (TREE_CODE (t))
+ {
+ case POINTER_TYPE:
+ {
+ tree sub = TREE_TYPE (t);
+
+ dump_type_prefix (sub, v);
+ /* A tree for a member pointer looks like pointer to offset,
+ so let the OFFSET_TYPE case handle it. */
+ if (TREE_CODE (sub) != OFFSET_TYPE)
+ {
+ switch (TREE_CODE (sub))
+ {
+ /* We don't want int ( *)() */
+ case FUNCTION_TYPE:
+ case METHOD_TYPE:
+ break;
+
+ case ARRAY_TYPE:
+ OB_PUTC2 (' ', '(');
+ break;
+
+ case POINTER_TYPE:
+ /* We don't want "char * *" */
+ if (! (TYPE_READONLY (sub) || TYPE_VOLATILE (sub)))
+ break;
+ /* But we do want "char *const *" */
+
+ default:
+ OB_PUTC (' ');
+ }
+ OB_PUTC ('*');
+ dump_readonly_or_volatile (t, none);
+ }
+ }
+ break;
+
+ case REFERENCE_TYPE:
+ {
+ tree sub = TREE_TYPE (t);
+ dump_type_prefix (sub, v);
+
+ switch (TREE_CODE (sub))
+ {
+ case ARRAY_TYPE:
+ OB_PUTC2 (' ', '(');
+ break;
+
+ case POINTER_TYPE:
+ /* We don't want "char * &" */
+ if (! (TYPE_READONLY (sub) || TYPE_VOLATILE (sub)))
+ break;
+ /* But we do want "char *const &" */
+
+ default:
+ OB_PUTC (' ');
+ }
+ }
+ OB_PUTC ('&');
+ dump_readonly_or_volatile (t, none);
+ break;
+
+ case OFFSET_TYPE:
+ offset_type:
+ dump_type_prefix (TREE_TYPE (t), v);
+ if (TREE_CODE (t) == OFFSET_TYPE) /* pmfs deal with this in d_t_p */
+ {
+ OB_PUTC (' ');
+ dump_type (TYPE_OFFSET_BASETYPE (t), 0);
+ OB_PUTC2 (':', ':');
+ }
+ OB_PUTC ('*');
+ dump_readonly_or_volatile (t, none);
+ break;
+
+ /* Can only be reached through function pointer -- this would not be
+ correct if FUNCTION_DECLs used it. */
+ case FUNCTION_TYPE:
+ dump_type_prefix (TREE_TYPE (t), v);
+ OB_PUTC2 (' ', '(');
+ break;
+
+ case METHOD_TYPE:
+ dump_type_prefix (TREE_TYPE (t), v);
+ OB_PUTC2 (' ', '(');
+ dump_aggr_type (TYPE_METHOD_BASETYPE (t), 0);
+ OB_PUTC2 (':', ':');
+ break;
+
+ case ARRAY_TYPE:
+ dump_type_prefix (TREE_TYPE (t), v);
+ break;
+
+ case ENUMERAL_TYPE:
+ case ERROR_MARK:
+ case IDENTIFIER_NODE:
+ case INTEGER_TYPE:
+ case BOOLEAN_TYPE:
+ case REAL_TYPE:
+ case RECORD_TYPE:
+ case TEMPLATE_TYPE_PARM:
+ case TREE_LIST:
+ case TYPE_DECL:
+ case TREE_VEC:
+ case UNINSTANTIATED_P_TYPE:
+ case UNION_TYPE:
+ case UNKNOWN_TYPE:
+ case VOID_TYPE:
+ dump_type (t, v);
+ break;
+
+ default:
+ sorry ("`%s' not supported by dump_type_prefix",
+ tree_code_name[(int) TREE_CODE (t)]);
+ }
+}
+
+static void
+dump_type_suffix (t, v)
+ tree t;
+ int v; /* verbose? */
+{
+ if (TYPE_PTRMEMFUNC_P (t))
+ t = TYPE_PTRMEMFUNC_FN_TYPE (t);
+
+ switch (TREE_CODE (t))
+ {
+ case POINTER_TYPE:
+ case REFERENCE_TYPE:
+ case OFFSET_TYPE:
+ if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
+ OB_PUTC (')');
+ dump_type_suffix (TREE_TYPE (t), v);
+ break;
+
+ /* Can only be reached through function pointer */
+ case FUNCTION_TYPE:
+ case METHOD_TYPE:
+ {
+ tree arg;
+ OB_PUTC2 (')', '(');
+ arg = TYPE_ARG_TYPES (t);
+ if (TREE_CODE (t) == METHOD_TYPE)
+ arg = TREE_CHAIN (arg);
+
+ if (arg)
+ dump_type (arg, v);
+ else
+ OB_PUTS ("...");
+ OB_PUTC (')');
+ if (TREE_CODE (t) == METHOD_TYPE)
+ dump_readonly_or_volatile
+ (TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (t))), before);
+ dump_type_suffix (TREE_TYPE (t), v);
+ break;
+ }
+
+ case ARRAY_TYPE:
+ OB_PUTC ('[');
+ if (TYPE_DOMAIN (t))
+ OB_PUTI (TREE_INT_CST_LOW (TYPE_MAX_VALUE (TYPE_DOMAIN (t))) + 1);
+ OB_PUTC (']');
+ dump_type_suffix (TREE_TYPE (t), v);
+ break;
+
+ case ENUMERAL_TYPE:
+ case ERROR_MARK:
+ case IDENTIFIER_NODE:
+ case INTEGER_TYPE:
+ case BOOLEAN_TYPE:
+ case REAL_TYPE:
+ case RECORD_TYPE:
+ case TEMPLATE_TYPE_PARM:
+ case TREE_LIST:
+ case TYPE_DECL:
+ case TREE_VEC:
+ case UNINSTANTIATED_P_TYPE:
+ case UNION_TYPE:
+ case UNKNOWN_TYPE:
+ case VOID_TYPE:
+ break;
+
+ default:
+ sorry ("`%s' not supported by dump_type_suffix",
+ tree_code_name[(int) TREE_CODE (t)]);
+ }
+}
+
+/* Return a function declaration which corresponds to the IDENTIFIER_NODE
+ argument. */
+tree
+ident_fndecl (t)
+ tree t;
+{
+ tree n = lookup_name (t, 0);
+
+ if (TREE_CODE (n) == FUNCTION_DECL)
+ return n;
+ else if (TREE_CODE (n) == TREE_LIST
+ && TREE_CODE (TREE_VALUE (n)) == FUNCTION_DECL)
+ return TREE_VALUE (n);
+
+ my_friendly_abort (66);
+ return NULL_TREE;
+}
+
+#ifndef NO_DOLLAR_IN_LABEL
+# define GLOBAL_THING "_GLOBAL_$"
+#else
+# ifndef NO_DOT_IN_LABEL
+# define GLOBAL_THING "_GLOBAL_."
+# else
+# define GLOBAL_THING "_GLOBAL__"
+# endif
+#endif
+
+#define GLOBAL_IORD_P(NODE) \
+ !strncmp(IDENTIFIER_POINTER(NODE),GLOBAL_THING,sizeof(GLOBAL_THING)-1)
+
+void
+dump_global_iord (t)
+ tree t;
+{
+ char *name = IDENTIFIER_POINTER (t);
+
+ OB_PUTS ("(static ");
+ if (name [sizeof (GLOBAL_THING) - 1] == 'I')
+ OB_PUTS ("initializers");
+ else if (name [sizeof (GLOBAL_THING) - 1] == 'D')
+ OB_PUTS ("destructors");
+ else
+ my_friendly_abort (352);
+
+ OB_PUTS (" for ");
+ OB_PUTCP (input_filename);
+ OB_PUTC (')');
+}
+
+static void
+dump_decl (t, v)
+ tree t;
+ int v; /* verbosity */
+{
+ if (t == NULL_TREE)
+ return;
+
+ switch (TREE_CODE (t))
+ {
+ case ERROR_MARK:
+ OB_PUTS (" /* decl error */ ");
+ break;
+
+ case TYPE_DECL:
+ {
+ /* Don't say 'typedef class A' */
+ tree type = TREE_TYPE (t);
+ if (IS_AGGR_TYPE (type) && ! TYPE_PTRMEMFUNC_P (type)
+ && type == TYPE_MAIN_VARIANT (type))
+ {
+ dump_type (type, v);
+ break;
+ }
+ }
+ if (v > 0)
+ OB_PUTS ("typedef ");
+ goto general;
+ break;
+
+ case VAR_DECL:
+ if (VTABLE_NAME_P (DECL_NAME (t)))
+ {
+ OB_PUTS ("vtable for ");
+ dump_type (DECL_CONTEXT (t), v);
+ break;
+ }
+ /* else fall through */
+ case FIELD_DECL:
+ case PARM_DECL:
+ general:
+ if (v > 0)
+ {
+ dump_type_prefix (TREE_TYPE (t), v);
+ OB_PUTC (' ');
+ }
+ /* DECL_CLASS_CONTEXT isn't being set in some cases. Hmm... */
+ if (DECL_CONTEXT (t)
+ && TREE_CODE_CLASS (TREE_CODE (DECL_CONTEXT (t))) == 't')
+ {
+ dump_type (DECL_CONTEXT (t), 0);
+ OB_PUTC2 (':', ':');
+ }
+ if (DECL_NAME (t))
+ dump_decl (DECL_NAME (t), v);
+ else
+ OB_PUTS ("{anon}");
+ if (v > 0)
+ dump_type_suffix (TREE_TYPE (t), v);
+ break;
+
+ case ARRAY_REF:
+ dump_decl (TREE_OPERAND (t, 0), v);
+ OB_PUTC ('[');
+ dump_decl (TREE_OPERAND (t, 1), v);
+ OB_PUTC (']');
+ break;
+
+ /* So that we can do dump_decl in dump_aggr_type and have it work for
+ both class and function scope. */
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ case ENUMERAL_TYPE:
+ dump_type (t, v);
+ break;
+
+ case TYPE_EXPR:
+ my_friendly_abort (69);
+ break;
+
+ /* These special cases are duplicated here so that other functions
+ can feed identifiers to cp_error and get them demangled properly. */
+ case IDENTIFIER_NODE:
+ if (DESTRUCTOR_NAME_P (t))
+ {
+ OB_PUTC ('~');
+ dump_decl (DECL_NAME (ident_fndecl (t)), 0);
+ }
+ else if (IDENTIFIER_TYPENAME_P (t))
+ {
+ OB_PUTS ("operator ");
+ /* Not exactly IDENTIFIER_TYPE_VALUE. */
+ dump_type (TREE_TYPE (t), 0);
+ break;
+ }
+ else if (IDENTIFIER_OPNAME_P (t))
+ {
+ char *name_string = operator_name_string (t);
+ OB_PUTS ("operator ");
+ OB_PUTCP (name_string);
+ }
+ else
+ OB_PUTID (t);
+ break;
+
+ case FUNCTION_DECL:
+ if (GLOBAL_IORD_P (DECL_ASSEMBLER_NAME (t)))
+ dump_global_iord (DECL_ASSEMBLER_NAME (t));
+ else
+ dump_function_decl (t, v);
+ break;
+
+ case TEMPLATE_DECL:
+ {
+ tree args = DECL_TEMPLATE_PARMS (t);
+ int i, len = TREE_VEC_LENGTH (args);
+ OB_PUTS ("template <");
+ for (i = 0; i < len; i++)
+ {
+ tree arg = TREE_VEC_ELT (args, i);
+ if (TREE_CODE (arg) == IDENTIFIER_NODE)
+ {
+ OB_PUTS ("class ");
+ OB_PUTID (arg);
+ }
+ else
+ dump_decl (arg, 1);
+ OB_PUTC2 (',', ' ');
+ }
+ OB_UNPUT (2);
+ OB_PUTC2 ('>', ' ');
+
+ if (DECL_TEMPLATE_IS_CLASS (t))
+ {
+ OB_PUTS ("class ");
+ OB_PUTID (DECL_NAME (t));
+ }
+ else switch (NEXT_CODE (t))
+ {
+ case METHOD_TYPE:
+ case FUNCTION_TYPE:
+ dump_function_decl (t, v);
+ break;
+
+ default:
+ my_friendly_abort (353);
+ }
+ }
+ break;
+
+ case LABEL_DECL:
+ OB_PUTID (DECL_NAME (t));
+ break;
+
+ case CONST_DECL:
+ if (NEXT_CODE (t) == ENUMERAL_TYPE)
+ goto general;
+ else
+ dump_expr (DECL_INITIAL (t), 0);
+ break;
+
+ default:
+ sorry ("`%s' not supported by dump_decl",
+ tree_code_name[(int) TREE_CODE (t)]);
+ }
+}
+
+/* Pretty printing for announce_function. T is the declaration of the
+ function we are interested in seeing. V is non-zero if we should print
+ the type that this function returns. */
+
+static void
+dump_function_decl (t, v)
+ tree t;
+ int v;
+{
+ tree name = DECL_ASSEMBLER_NAME (t);
+ tree fntype = TREE_TYPE (t);
+ tree parmtypes = TYPE_ARG_TYPES (fntype);
+ tree cname = NULL_TREE;
+
+ /* Friends have DECL_CLASS_CONTEXT set, but not DECL_CONTEXT. */
+ if (DECL_CONTEXT (t))
+ cname = DECL_CLASS_CONTEXT (t);
+ /* this is for partially instantiated template methods */
+ else if (TREE_CODE (fntype) == METHOD_TYPE)
+ cname = TREE_TYPE (TREE_VALUE (parmtypes));
+
+ v = (v > 0);
+
+ if (v)
+ {
+ if (DECL_STATIC_FUNCTION_P (t))
+ OB_PUTS ("static ");
+
+ if (! IDENTIFIER_TYPENAME_P (name)
+ && ! DECL_CONSTRUCTOR_P (t)
+ && ! DESTRUCTOR_NAME_P (name))
+ {
+ dump_type_prefix (TREE_TYPE (fntype), 1);
+ OB_PUTC (' ');
+ }
+ }
+
+ if (cname)
+ {
+ dump_type (cname, 0);
+ OB_PUTC2 (':', ':');
+ if (TREE_CODE (fntype) == METHOD_TYPE && parmtypes)
+ parmtypes = TREE_CHAIN (parmtypes);
+ if (DECL_CONSTRUCTOR_FOR_VBASE_P (t))
+ /* Skip past "in_charge" identifier. */
+ parmtypes = TREE_CHAIN (parmtypes);
+ }
+
+ if (DESTRUCTOR_NAME_P (name))
+ parmtypes = TREE_CHAIN (parmtypes);
+
+ dump_function_name (t);
+
+ OB_PUTC ('(');
+
+ if (parmtypes)
+ dump_type (parmtypes, v);
+ else
+ OB_PUTS ("...");
+
+ OB_PUTC (')');
+
+ if (v && ! IDENTIFIER_TYPENAME_P (name))
+ dump_type_suffix (TREE_TYPE (fntype), 1);
+
+ if (TREE_CODE (fntype) == METHOD_TYPE)
+ {
+ if (IS_SIGNATURE (cname))
+ /* We look at the type pointed to by the `optr' field of `this.' */
+ dump_readonly_or_volatile
+ (TREE_TYPE (TREE_TYPE (TYPE_FIELDS (TREE_VALUE (TYPE_ARG_TYPES (fntype))))), before);
+ else
+ dump_readonly_or_volatile
+ (TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (fntype))), before);
+ }
+}
+
+/* Handle the function name for a FUNCTION_DECL node, grokking operators
+ and destructors properly. */
+static void
+dump_function_name (t)
+ tree t;
+{
+ tree name = DECL_NAME (t);
+
+ /* There ought to be a better way to find out whether or not something is
+ a destructor. */
+ if (DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (t)))
+ {
+ OB_PUTC ('~');
+ dump_decl (name, 0);
+ }
+ else if (IDENTIFIER_TYPENAME_P (name))
+ {
+ /* This cannot use the hack that the operator's return
+ type is stashed off of its name because it may be
+ used for error reporting. In the case of conflicting
+ declarations, both will have the same name, yet
+ the types will be different, hence the TREE_TYPE field
+ of the first name will be clobbered by the second. */
+ OB_PUTS ("operator ");
+ dump_type (TREE_TYPE (TREE_TYPE (t)), 0);
+ }
+ else if (IDENTIFIER_OPNAME_P (name))
+ {
+ char *name_string = operator_name_string (name);
+ OB_PUTS ("operator ");
+ OB_PUTCP (name_string);
+ }
+ else
+ dump_decl (name, 0);
+}
+
+static void
+dump_char (c)
+ char c;
+{
+ switch (c)
+ {
+ case TARGET_NEWLINE:
+ OB_PUTS ("\\n");
+ break;
+ case TARGET_TAB:
+ OB_PUTS ("\\t");
+ break;
+ case TARGET_VT:
+ OB_PUTS ("\\v");
+ break;
+ case TARGET_BS:
+ OB_PUTS ("\\b");
+ break;
+ case TARGET_CR:
+ OB_PUTS ("\\r");
+ break;
+ case TARGET_FF:
+ OB_PUTS ("\\f");
+ break;
+ case TARGET_BELL:
+ OB_PUTS ("\\a");
+ break;
+ case '\\':
+ OB_PUTS ("\\\\");
+ break;
+ case '\'':
+ OB_PUTS ("\\'");
+ break;
+ case '\"':
+ OB_PUTS ("\\\"");
+ break;
+ default:
+ if (isprint (c))
+ OB_PUTC (c);
+ else
+ {
+ sprintf (digit_buffer, "\\%03o", (int) c);
+ OB_PUTCP (digit_buffer);
+ }
+ }
+}
+
+/* Print out a list of initializers (subr of dump_expr) */
+static void
+dump_expr_list (l)
+ tree l;
+{
+ while (l)
+ {
+ dump_expr (TREE_VALUE (l), 0);
+ if (TREE_CHAIN (l))
+ OB_PUTC2 (',', ' ');
+ l = TREE_CHAIN (l);
+ }
+}
+
+/* Print out an expression */
+static void
+dump_expr (t, nop)
+ tree t;
+ int nop; /* suppress parens */
+{
+ switch (TREE_CODE (t))
+ {
+ case VAR_DECL:
+ case PARM_DECL:
+ case FIELD_DECL:
+ case CONST_DECL:
+ case FUNCTION_DECL:
+ dump_decl (t, -1);
+ break;
+
+ case INTEGER_CST:
+ {
+ tree type = TREE_TYPE (t);
+ my_friendly_assert (type != 0, 81);
+
+ /* If it's an enum, output its tag, rather than its value. */
+ if (TREE_CODE (type) == ENUMERAL_TYPE)
+ {
+ char *p = enum_name_string (t, type);
+ OB_PUTCP (p);
+ }
+ else if (type == char_type_node
+ || type == signed_char_type_node
+ || type == unsigned_char_type_node)
+ {
+ OB_PUTC ('\'');
+ dump_char (TREE_INT_CST_LOW (t));
+ OB_PUTC ('\'');
+ }
+ else if (TREE_INT_CST_HIGH (t)
+ != (TREE_INT_CST_LOW (t) >> (HOST_BITS_PER_WIDE_INT - 1)))
+ {
+ tree val = t;
+ if (TREE_INT_CST_HIGH (val) < 0)
+ {
+ OB_PUTC ('-');
+ val = build_int_2 (~TREE_INT_CST_LOW (val),
+ -TREE_INT_CST_HIGH (val));
+ }
+ /* Would "%x%0*x" or "%x%*0x" get zero-padding on all
+ systems? */
+ {
+ static char format[10]; /* "%x%09999x\0" */
+ if (!format[0])
+ sprintf (format, "%%x%%0%dx", HOST_BITS_PER_INT / 4);
+ sprintf (digit_buffer, format, TREE_INT_CST_HIGH (val),
+ TREE_INT_CST_LOW (val));
+ OB_PUTCP (digit_buffer);
+ }
+ }
+ else
+ OB_PUTI (TREE_INT_CST_LOW (t));
+ }
+ break;
+
+ case REAL_CST:
+#ifndef REAL_IS_NOT_DOUBLE
+ sprintf (digit_buffer, "%g", TREE_REAL_CST (t));
+#else
+ {
+ unsigned char *p = (unsigned char *) &TREE_REAL_CST (t);
+ int i;
+ strcpy (digit_buffer, "0x");
+ for (i = 0; i < sizeof TREE_REAL_CST (t); i++)
+ sprintf (digit_buffer + 2 + 2*i, "%02x", *p++);
+ }
+#endif
+ OB_PUTCP (digit_buffer);
+ break;
+
+ case STRING_CST:
+ {
+ char *p = TREE_STRING_POINTER (t);
+ int len = TREE_STRING_LENGTH (t) - 1;
+ int i;
+
+ OB_PUTC ('\"');
+ for (i = 0; i < len; i++)
+ dump_char (p[i]);
+ OB_PUTC ('\"');
+ }
+ break;
+
+ case COMPOUND_EXPR:
+ dump_binary_op (",", t);
+ break;
+
+ case COND_EXPR:
+ OB_PUTC ('(');
+ dump_expr (TREE_OPERAND (t, 0), 0);
+ OB_PUTS (" ? ");
+ dump_expr (TREE_OPERAND (t, 1), 0);
+ OB_PUTS (" : ");
+ dump_expr (TREE_OPERAND (t, 2), 0);
+ OB_PUTC (')');
+ break;
+
+ case SAVE_EXPR:
+ if (TREE_HAS_CONSTRUCTOR (t))
+ {
+ OB_PUTS ("new ");
+ dump_type (TREE_TYPE (TREE_TYPE (t)), 0);
+ PARM_DECL_EXPR (t) = 1;
+ }
+ else
+ {
+ sorry ("operand of SAVE_EXPR not understood");
+ goto error;
+ }
+ break;
+
+ case NEW_EXPR:
+ OB_PUTID (TYPE_IDENTIFIER (TREE_TYPE (t)));
+ OB_PUTC ('(');
+ dump_expr_list (TREE_CHAIN (TREE_OPERAND (t, 1)));
+ OB_PUTC (')');
+ break;
+
+ case CALL_EXPR:
+ {
+ tree fn = TREE_OPERAND (t, 0);
+ tree args = TREE_OPERAND (t, 1);
+
+ if (TREE_CODE (fn) == ADDR_EXPR)
+ fn = TREE_OPERAND (fn, 0);
+
+ if (NEXT_CODE (fn) == METHOD_TYPE)
+ {
+ tree ob = TREE_VALUE (args);
+ if (TREE_CODE (ob) == ADDR_EXPR)
+ {
+ dump_expr (TREE_OPERAND (ob, 0), 0);
+ OB_PUTC ('.');
+ }
+ else if (TREE_CODE (ob) != PARM_DECL
+ || strcmp (IDENTIFIER_POINTER (DECL_NAME (ob)), "this"))
+ {
+ dump_expr (ob, 0);
+ OB_PUTC2 ('-', '>');
+ }
+ args = TREE_CHAIN (args);
+ }
+ dump_expr (fn, 0);
+ OB_PUTC('(');
+ dump_expr_list (args);
+ OB_PUTC (')');
+ }
+ break;
+
+ case WITH_CLEANUP_EXPR:
+ /* Note that this only works for G++ cleanups. If somebody
+ builds a general cleanup, there's no way to represent it. */
+ dump_expr (TREE_OPERAND (t, 0), 0);
+ break;
+
+ case TARGET_EXPR:
+ /* Note that this only works for G++ target exprs. If somebody
+ builds a general TARGET_EXPR, there's no way to represent that
+ it initializes anything other that the parameter slot for the
+ default argument. Note we may have cleared out the first
+ operand in expand_expr, so don't go killing ourselves. */
+ if (TREE_OPERAND (t, 1))
+ dump_expr (TREE_OPERAND (t, 1), 0);
+ break;
+
+ case MODIFY_EXPR:
+ case PLUS_EXPR:
+ case MINUS_EXPR:
+ case MULT_EXPR:
+ case TRUNC_DIV_EXPR:
+ case TRUNC_MOD_EXPR:
+ case MIN_EXPR:
+ case MAX_EXPR:
+ case LSHIFT_EXPR:
+ case RSHIFT_EXPR:
+ case BIT_IOR_EXPR:
+ case BIT_XOR_EXPR:
+ case BIT_AND_EXPR:
+ case BIT_ANDTC_EXPR:
+ case TRUTH_ANDIF_EXPR:
+ case TRUTH_ORIF_EXPR:
+ case LT_EXPR:
+ case LE_EXPR:
+ case GT_EXPR:
+ case GE_EXPR:
+ case EQ_EXPR:
+ case NE_EXPR:
+ dump_binary_op (opname_tab[(int) TREE_CODE (t)], t);
+ break;
+
+ case CEIL_DIV_EXPR:
+ case FLOOR_DIV_EXPR:
+ case ROUND_DIV_EXPR:
+ dump_binary_op ("/", t);
+ break;
+
+ case CEIL_MOD_EXPR:
+ case FLOOR_MOD_EXPR:
+ case ROUND_MOD_EXPR:
+ dump_binary_op ("%", t);
+ break;
+
+ case COMPONENT_REF:
+ {
+ tree ob = TREE_OPERAND (t, 0);
+ if (TREE_CODE (ob) == INDIRECT_REF)
+ {
+ ob = TREE_OPERAND (ob, 0);
+ if (TREE_CODE (ob) != PARM_DECL
+ || strcmp (IDENTIFIER_POINTER (DECL_NAME (ob)), "this"))
+ {
+ dump_expr (ob, 0);
+ OB_PUTC2 ('-', '>');
+ }
+ }
+ else
+ {
+ dump_expr (ob, 0);
+ OB_PUTC ('.');
+ }
+ dump_expr (TREE_OPERAND (t, 1), 1);
+ }
+ break;
+
+ case CONVERT_EXPR:
+ dump_unary_op ("+", t, nop);
+ break;
+
+ case ADDR_EXPR:
+ if (TREE_CODE (TREE_OPERAND (t, 0)) == FUNCTION_DECL
+ || TREE_CODE (TREE_OPERAND (t, 0)) == STRING_CST)
+ dump_expr (TREE_OPERAND (t, 0), 0);
+ else
+ dump_unary_op ("&", t, nop);
+ break;
+
+ case INDIRECT_REF:
+ if (TREE_HAS_CONSTRUCTOR (t))
+ {
+ t = TREE_OPERAND (t, 0);
+ my_friendly_assert (TREE_CODE (t) == CALL_EXPR, 237);
+ dump_expr (TREE_OPERAND (t, 0), 0);
+ OB_PUTC ('(');
+ dump_expr_list (TREE_CHAIN (TREE_OPERAND (t, 1)));
+ OB_PUTC (')');
+ }
+ else
+ {
+ if (NEXT_CODE (TREE_OPERAND (t, 0)) == REFERENCE_TYPE)
+ dump_expr (TREE_OPERAND (t, 0), nop);
+ else
+ dump_unary_op ("*", t, nop);
+ }
+ break;
+
+ case NEGATE_EXPR:
+ case BIT_NOT_EXPR:
+ case TRUTH_NOT_EXPR:
+ case PREDECREMENT_EXPR:
+ case PREINCREMENT_EXPR:
+ dump_unary_op (opname_tab [(int)TREE_CODE (t)], t, nop);
+ break;
+
+ case POSTDECREMENT_EXPR:
+ case POSTINCREMENT_EXPR:
+ OB_PUTC ('(');
+ dump_expr (TREE_OPERAND (t, 0), 0);
+ OB_PUTCP (opname_tab[(int)TREE_CODE (t)]);
+ OB_PUTC (')');
+ break;
+
+ case NON_LVALUE_EXPR:
+ /* FIXME: This is a KLUDGE workaround for a parsing problem. There
+ should be another level of INDIRECT_REF so that I don't have to do
+ this. */
+ if (NEXT_CODE (t) == POINTER_TYPE)
+ {
+ tree next = TREE_TYPE (TREE_TYPE (t));
+
+ while (TREE_CODE (next) == POINTER_TYPE)
+ next = TREE_TYPE (next);
+
+ if (TREE_CODE (next) == FUNCTION_TYPE)
+ {
+ if (!nop) OB_PUTC ('(');
+ OB_PUTC ('*');
+ dump_expr (TREE_OPERAND (t, 0), 1);
+ if (!nop) OB_PUTC (')');
+ break;
+ }
+ /* else FALLTHRU */
+ }
+ dump_expr (TREE_OPERAND (t, 0), 0);
+ break;
+
+ case NOP_EXPR:
+ dump_expr (TREE_OPERAND (t, 0), nop);
+ break;
+
+ case CONSTRUCTOR:
+ OB_PUTC ('{');
+ dump_expr_list (CONSTRUCTOR_ELTS (t), 0);
+ OB_PUTC ('}');
+ break;
+
+ case OFFSET_REF:
+ {
+ tree ob = TREE_OPERAND (t, 0);
+ if (TREE_CODE (ob) == NOP_EXPR
+ && TREE_OPERAND (ob, 0) == error_mark_node
+ && TREE_CODE (TREE_OPERAND (t, 1)) == FUNCTION_DECL)
+ /* A::f */
+ dump_expr (TREE_OPERAND (t, 1), 0);
+ else
+ {
+ sorry ("operand of OFFSET_REF not understood");
+ goto error;
+ }
+ break;
+ }
+
+ /* This list is incomplete, but should suffice for now.
+ It is very important that `sorry' does not call
+ `report_error_function'. That could cause an infinite loop. */
+ default:
+ sorry ("`%s' not supported by dump_expr",
+ tree_code_name[(int) TREE_CODE (t)]);
+
+ /* fall through to ERROR_MARK... */
+ case ERROR_MARK:
+ error:
+ OB_PUTCP ("{error}");
+ break;
+ }
+}
+
+static void
+dump_binary_op (opstring, t)
+ char *opstring;
+ tree t;
+{
+ OB_PUTC ('(');
+ dump_expr (TREE_OPERAND (t, 0), 1);
+ OB_PUTC (' ');
+ OB_PUTCP (opstring);
+ OB_PUTC (' ');
+ dump_expr (TREE_OPERAND (t, 1), 1);
+ OB_PUTC (')');
+}
+
+static void
+dump_unary_op (opstring, t, nop)
+ char *opstring;
+ tree t;
+ int nop;
+{
+ if (!nop) OB_PUTC ('(');
+ OB_PUTCP (opstring);
+ dump_expr (TREE_OPERAND (t, 0), 1);
+ if (!nop) OB_PUTC (')');
+}
+
+char *
+fndecl_as_string (cname, fndecl, print_ret_type_p)
+ tree cname, fndecl;
+ int print_ret_type_p;
+{
+ return decl_as_string (fndecl, print_ret_type_p);
+}
+
+/* Same, but handtype a _TYPE.
+ Called from convert_to_reference, mangle_class_name_for_template,
+ build_unary_op, and GNU_xref_decl. */
+char *
+type_as_string (typ, v)
+ tree typ;
+ int v;
+{
+ OB_INIT ();
+
+ dump_type (typ, v);
+
+ OB_FINISH ();
+
+ return (char *)obstack_base (&scratch_obstack);
+}
+
+char *
+expr_as_string (decl, v)
+ tree decl;
+ int v;
+{
+ OB_INIT ();
+
+ dump_expr (decl, 1);
+
+ OB_FINISH ();
+
+ return (char *)obstack_base (&scratch_obstack);
+}
+
+/* A cross between type_as_string and fndecl_as_string.
+ Only called from substitute_nice_name. */
+char *
+decl_as_string (decl, v)
+ tree decl;
+ int v;
+{
+ OB_INIT ();
+
+ dump_decl (decl, v);
+
+ OB_FINISH ();
+
+ return (char *)obstack_base (&scratch_obstack);
+}
+
+char *
+cp_file_of (t)
+ tree t;
+{
+ if (TREE_CODE (t) == PARM_DECL)
+ return DECL_SOURCE_FILE (DECL_CONTEXT (t));
+ else if (TREE_CODE_CLASS (TREE_CODE (t)) == 't')
+ return DECL_SOURCE_FILE (TYPE_NAME (t));
+ else
+ return DECL_SOURCE_FILE (t);
+}
+
+int
+cp_line_of (t)
+ tree t;
+{
+ if (TREE_CODE (t) == PARM_DECL)
+ return DECL_SOURCE_LINE (DECL_CONTEXT (t));
+ else if (TREE_CODE_CLASS (TREE_CODE (t)) == 't')
+ return DECL_SOURCE_LINE (TYPE_NAME (t));
+ else
+ return DECL_SOURCE_LINE (t);
+}
+
+char *
+code_as_string (c, v)
+ enum tree_code c;
+ int v;
+{
+ return tree_code_name [c];
+}
+
+char *
+language_as_string (c, v)
+ enum languages c;
+ int v;
+{
+ switch (c)
+ {
+ case lang_c:
+ return "C";
+
+ case lang_cplusplus:
+ return "C++";
+
+ default:
+ my_friendly_abort (355);
+ return 0;
+ }
+}
+
+/* Return the proper printed version of a parameter to a C++ function. */
+char *
+parm_as_string (p, v)
+ int p, v;
+{
+ if (p < 0)
+ return "`this'";
+
+ sprintf (digit_buffer, "%d", p+1);
+ return digit_buffer;
+}
+
+char *
+op_as_string (p, v)
+ enum tree_code p;
+ int v;
+{
+ static char buf[] = "operator ";
+
+ if (p == 0)
+ return "{unknown}";
+
+ strcpy (buf + 9, opname_tab [p]);
+ return buf;
+}
+
+char *
+args_as_string (p, v)
+ tree p;
+ int v;
+{
+ if (p == NULL_TREE)
+ return "...";
+
+ return type_as_string (p, v);
+}
diff --git a/gnu/usr.bin/cc/cc1plus/except.c b/gnu/usr.bin/cc/cc1plus/except.c
new file mode 100644
index 0000000..dc91b9d
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/except.c
@@ -0,0 +1,1481 @@
+/* Handle exceptional things in C++.
+ Copyright (C) 1989, 1992, 1993, 1994 Free Software Foundation, Inc.
+ Contributed by Michael Tiemann <tiemann@cygnus.com>
+ Rewritten by Mike Stump <mrs@cygnus.com>, based upon an
+ initial re-implementation courtesy Tad Hunt.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* High-level class interface. */
+
+#include "config.h"
+#include "tree.h"
+#include "rtl.h"
+#include "cp-tree.h"
+#include "flags.h"
+#include "obstack.h"
+#include "expr.h"
+
+extern void (*interim_eh_hook) PROTO((tree));
+
+/* holds the fndecl for __builtin_return_address () */
+tree builtin_return_address_fndecl;
+
+/* Define at your own risk! */
+#ifndef CROSS_COMPILE
+#ifdef sun
+#ifdef sparc
+#define TRY_NEW_EH
+#endif
+#endif
+#endif
+
+#ifndef TRY_NEW_EH
+
+static void
+sorry_no_eh ()
+{
+ static int warned = 0;
+ if (! warned)
+ {
+ sorry ("exception handling not supported");
+ warned = 1;
+ }
+}
+
+void
+build_exception_table ()
+{
+}
+
+void
+expand_exception_blocks ()
+{
+}
+
+void
+start_protect ()
+{
+}
+
+void
+end_protect (finalization)
+ tree finalization;
+{
+}
+
+void
+expand_start_try_stmts ()
+{
+ sorry_no_eh ();
+}
+
+void
+expand_end_try_stmts ()
+{
+}
+
+void
+expand_start_all_catch ()
+{
+}
+
+void
+expand_end_all_catch ()
+{
+}
+
+void
+expand_start_catch_block (declspecs, declarator)
+ tree declspecs, declarator;
+{
+}
+
+void
+expand_end_catch_block ()
+{
+}
+
+void
+init_exception_processing ()
+{
+}
+
+void
+expand_throw (exp)
+ tree exp;
+{
+ sorry_no_eh ();
+}
+
+#else
+
+static int
+doing_eh (do_warn)
+ int do_warn;
+{
+ if (! flag_handle_exceptions)
+ {
+ static int warned = 0;
+ if (! warned && do_warn)
+ {
+ error ("exception handling disabled, use -fhandle-exceptions to enable.");
+ warned = 1;
+ }
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+NO GNEWS IS GOOD GNEWS WITH GARRY GNUS: This version is much closer
+to supporting exception handling as per Stroustrup's 2nd edition.
+It is a complete rewrite of all the EH stuff that was here before
+ Shortcomings:
+ 1. The type of the throw and catch must still match
+ exactly (no support yet for matching base classes)
+ 2. Throw specifications of functions still doesnt't work.
+ Cool Things:
+ 1. Destructors are called properly :-)
+ 2. No overhead for the non-exception thrown case.
+ 3. Fixing shortcomings 1 and 2 is simple.
+ -Tad Hunt (tad@mail.csh.rit.edu)
+
+*/
+
+/* A couple of backend routines from m88k.c */
+
+/* used to cache a call to __builtin_return_address () */
+static tree BuiltinReturnAddress;
+
+
+
+
+
+#include <stdio.h>
+
+/* XXX - Tad: for EH */
+/* output an exception table entry */
+
+static void
+output_exception_table_entry (file, start_label, end_label, eh_label)
+ FILE *file;
+ rtx start_label, end_label, eh_label;
+{
+ char label[100];
+
+ fprintf (file, "\t%s\t ", ASM_LONG);
+ if (GET_CODE (start_label) == CODE_LABEL)
+ {
+ ASM_GENERATE_INTERNAL_LABEL (label, "L", CODE_LABEL_NUMBER (start_label));
+ assemble_name (file, label);
+ }
+ else if (GET_CODE (start_label) == SYMBOL_REF)
+ {
+ fprintf (stderr, "YYYYYYYYYEEEEEEEESSSSSSSSSSSS!!!!!!!!!!\n");
+ assemble_name (file, XSTR (start_label, 0));
+ }
+ putc ('\n', file);
+
+ fprintf (file, "\t%s\t ", ASM_LONG);
+ ASM_GENERATE_INTERNAL_LABEL (label, "L", CODE_LABEL_NUMBER (end_label));
+ assemble_name (file, label);
+ putc ('\n', file);
+
+ fprintf (file, "\t%s\t ", ASM_LONG);
+ ASM_GENERATE_INTERNAL_LABEL (label, "L", CODE_LABEL_NUMBER (eh_label));
+ assemble_name (file, label);
+ putc ('\n', file);
+
+ putc ('\n', file); /* blank line */
+}
+
+static void
+easy_expand_asm (str)
+ char *str;
+{
+ expand_asm (build_string (strlen (str)+1, str));
+}
+
+/* unwind the stack. */
+static void
+do_unwind (throw_label)
+ rtx throw_label;
+{
+#ifdef sparc
+ extern FILE *asm_out_file;
+ tree fcall;
+ tree params;
+ rtx return_val_rtx;
+
+ /* call to __builtin_return_address () */
+ params=tree_cons (NULL_TREE, integer_zero_node, NULL_TREE);
+ fcall = build_function_call (BuiltinReturnAddress, params);
+ return_val_rtx = expand_expr (fcall, NULL_RTX, SImode, 0);
+ /* In the return, the new pc is pc+8, as the value comming in is
+ really the address of the call insn, not the next insn. */
+ emit_move_insn (return_val_rtx, plus_constant(gen_rtx (LABEL_REF,
+ Pmode,
+ throw_label), -8));
+ /* We use three values, PC, type, and value */
+ easy_expand_asm ("st %l0,[%fp]");
+ easy_expand_asm ("st %l1,[%fp+4]");
+ easy_expand_asm ("st %l2,[%fp+8]");
+ easy_expand_asm ("ret");
+ easy_expand_asm ("restore");
+ emit_barrier ();
+#endif
+#if m88k
+ rtx temp_frame = frame_pointer_rtx;
+
+ temp_frame = memory_address (Pmode, temp_frame);
+ temp_frame = copy_to_reg (gen_rtx (MEM, Pmode, temp_frame));
+
+ /* hopefully this will successfully pop the frame! */
+ emit_move_insn (frame_pointer_rtx, temp_frame);
+ emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
+ emit_move_insn (arg_pointer_rtx, frame_pointer_rtx);
+ emit_insn (gen_add2_insn (stack_pointer_rtx, gen_rtx (CONST_INT, VOIDmode,
+ (HOST_WIDE_INT)m88k_debugger_offset (stack_pointer_rtx, 0))));
+
+#if 0
+ emit_insn (gen_add2_insn (arg_pointer_rtx, gen_rtx (CONST_INT, VOIDmode,
+ -(HOST_WIDE_INT)m88k_debugger_offset (arg_pointer_rtx, 0))));
+
+ emit_move_insn (stack_pointer_rtx, arg_pointer_rtx);
+
+ emit_insn (gen_add2_insn (stack_pointer_rtx, gen_rtx (CONST_INT, VOIDmode,
+ (HOST_WIDE_INT)m88k_debugger_offset (arg_pointer_rtx, 0))));
+#endif
+#endif
+}
+
+
+
+#if 0
+/* This is the startup, and finish stuff per exception table. */
+
+/* XXX - Tad: exception handling section */
+#ifndef EXCEPT_SECTION_ASM_OP
+#define EXCEPT_SECTION_ASM_OP "section\t.gcc_except_table,\"a\",@progbits"
+#endif
+
+#ifdef EXCEPT_SECTION_ASM_OP
+typedef struct {
+ void *start_protect;
+ void *end_protect;
+ void *exception_handler;
+ } exception_table;
+#endif /* EXCEPT_SECTION_ASM_OP */
+
+#ifdef EXCEPT_SECTION_ASM_OP
+
+ /* on machines which support it, the exception table lives in another section,
+ but it needs a label so we can reference it... This sets up that
+ label! */
+asm (EXCEPT_SECTION_ASM_OP);
+exception_table __EXCEPTION_TABLE__[1] = { (void*)0, (void*)0, (void*)0 };
+asm (TEXT_SECTION_ASM_OP);
+
+#endif /* EXCEPT_SECTION_ASM_OP */
+
+#ifdef EXCEPT_SECTION_ASM_OP
+
+ /* we need to know where the end of the exception table is... so this
+ is how we do it! */
+
+asm (EXCEPT_SECTION_ASM_OP);
+exception_table __EXCEPTION_END__[1] = { (void*)-1, (void*)-1, (void*)-1 };
+asm (TEXT_SECTION_ASM_OP);
+
+#endif /* EXCEPT_SECTION_ASM_OP */
+
+#endif
+
+void
+exception_section ()
+{
+#ifdef ASM_OUTPUT_SECTION_NAME
+ named_section (".gcc_except_table");
+#else
+ text_section ();
+#endif
+}
+
+
+
+
+/* from: my-cp-except.c */
+
+/* VI: ":set ts=4" */
+#if 0
+#include <stdio.h> */
+#include "config.h"
+#include "tree.h"
+#include "rtl.h"
+#include "cp-tree.h"
+#endif
+#include "decl.h"
+#if 0
+#include "flags.h"
+#endif
+#include "insn-flags.h"
+#include "obstack.h"
+#if 0
+#include "expr.h"
+#endif
+
+/* ======================================================================
+ Briefly the algorithm works like this:
+
+ When a constructor or start of a try block is encountered,
+ push_eh_entry (&eh_stack) is called. Push_eh_entry () creates a
+ new entry in the unwind protection stack and returns a label to
+ output to start the protection for that block.
+
+ When a destructor or end try block is encountered, pop_eh_entry
+ (&eh_stack) is called. Pop_eh_entry () returns the ehEntry it
+ created when push_eh_entry () was called. The ehEntry structure
+ contains three things at this point. The start protect label,
+ the end protect label, and the exception handler label. The end
+ protect label should be output before the call to the destructor
+ (if any). If it was a destructor, then its parse tree is stored
+ in the finalization variable in the ehEntry structure. Otherwise
+ the finalization variable is set to NULL to reflect the fact that
+ is the the end of a try block. Next, this modified ehEntry node
+ is enqueued in the finalizations queue by calling
+ enqueue_eh_entry (&queue,entry).
+
+ +---------------------------------------------------------------+
+ |XXX: Will need modification to deal with partially |
+ | constructed arrays of objects |
+ | |
+ | Basically, this consists of keeping track of how many |
+ | of the objects have been constructed already (this |
+ | should be in a register though, so that shouldn't be a |
+ | problem. |
+ +---------------------------------------------------------------+
+
+ When a catch block is encountered, there is a lot of work to be
+ done.
+
+ Since we don't want to generate the catch block inline with the
+ regular flow of the function, we need to have some way of doing
+ so. Luckily, we have a couple of routines "get_last_insn ()" and
+ "set_last_insn ()" provided. When the start of a catch block is
+ encountered, we save a pointer to the last insn generated. After
+ the catch block is generated, we save a pointer to the first
+ catch block insn and the last catch block insn with the routines
+ "NEXT_INSN ()" and "get_last_insn ()". We then set the last insn
+ to be the last insn generated before the catch block, and set the
+ NEXT_INSN (last_insn) to zero.
+
+ Since catch blocks might be nested inside other catch blocks, and
+ we munge the chain of generated insns after the catch block is
+ generated, we need to store the pointers to the last insn
+ generated in a stack, so that when the end of a catch block is
+ encountered, the last insn before the current catch block can be
+ popped and set to be the last insn, and the first and last insns
+ of the catch block just generated can be enqueue'd for output at
+ a later time.
+
+ Next we must insure that when the catch block is executed, all
+ finalizations for the matching try block have been completed. If
+ any of those finalizations throw an exception, we must call
+ terminate according to the ARM (section r.15.6.1). What this
+ means is that we need to dequeue and emit finalizations for each
+ entry in the ehQueue until we get to an entry with a NULL
+ finalization field. For any of the finalization entries, if it
+ is not a call to terminate (), we must protect it by giving it
+ another start label, end label, and exception handler label,
+ setting its finalization tree to be a call to terminate (), and
+ enqueue'ing this new ehEntry to be output at an outer level.
+ Finally, after all that is done, we can get around to outputting
+ the catch block which basically wraps all the "catch (...) {...}"
+ statements in a big if/then/else construct that matches the
+ correct block to call.
+
+ ===================================================================== */
+
+extern rtx emit_insn PROTO((rtx));
+extern rtx gen_nop PROTO(());
+
+/* local globals for function calls
+ ====================================================================== */
+
+/* used to cache "terminate ()", "unexpected ()", "set_terminate ()", and
+ "set_unexpected ()" after default_conversion. (lib-except.c) */
+static tree Terminate, Unexpected, SetTerminate, SetUnexpected, CatchMatch;
+
+/* used to cache __find_first_exception_table_match ()
+ for throw (lib-except.c) */
+static tree FirstExceptionMatch;
+
+/* used to cache a call to __unwind_function () (lib-except.c) */
+static tree Unwind;
+
+/* holds a ready to emit call to "terminate ()". */
+static tree TerminateFunctionCall;
+
+/* ====================================================================== */
+
+
+
+/* data structures for my various quick and dirty stacks and queues
+ Eventually, most of this should go away, because I think it can be
+ integrated with stuff already built into the compiler. */
+
+/* =================================================================== */
+
+struct labelNode {
+ rtx label;
+ struct labelNode *chain;
+ };
+
+
+/* this is the most important structure here. Basically this is how I store
+ an exception table entry internally. */
+struct ehEntry {
+ rtx start_label;
+ rtx end_label;
+ rtx exception_handler_label;
+
+ tree finalization;
+ };
+
+struct ehNode {
+ struct ehEntry *entry;
+ struct ehNode *chain;
+ };
+
+struct ehStack {
+ struct ehNode *top;
+ };
+
+struct ehQueue {
+ struct ehNode *head;
+ struct ehNode *tail;
+ };
+
+struct exceptNode {
+ rtx catchstart;
+ rtx catchend;
+
+ struct exceptNode *chain;
+ };
+
+struct exceptStack {
+ struct exceptNode *top;
+ };
+/* ========================================================================= */
+
+
+
+/* local globals - these local globals are for storing data necessary for
+ generating the exception table and code in the correct order.
+
+ ========================================================================= */
+
+/* Holds the pc for doing "throw" */
+rtx saved_pc;
+/* Holds the type of the thing being thrown. */
+rtx saved_throw_type;
+/* Holds the value being thrown. */
+rtx saved_throw_value;
+
+rtx throw_label;
+
+static struct ehStack ehstack;
+static struct ehQueue ehqueue;
+static struct ehQueue eh_table_output_queue;
+static struct exceptStack exceptstack;
+static struct labelNode *false_label_stack = NULL;
+static struct labelNode *caught_return_label_stack = NULL;
+/* ========================================================================= */
+
+/* function prototypes */
+static struct ehEntry *pop_eh_entry PROTO((struct ehStack *stack));
+static void enqueue_eh_entry PROTO((struct ehQueue *queue, struct ehEntry *entry));
+static void push_except_stmts PROTO((struct exceptStack *exceptstack,
+ rtx catchstart, rtx catchend));
+static int pop_except_stmts PROTO((struct exceptStack *exceptstack,
+ rtx *catchstart, rtx *catchend));
+static rtx push_eh_entry PROTO((struct ehStack *stack));
+static struct ehEntry *dequeue_eh_entry PROTO((struct ehQueue *queue));
+static void new_eh_queue PROTO((struct ehQueue *queue));
+static void new_eh_stack PROTO((struct ehStack *stack));
+static void new_except_stack PROTO((struct exceptStack *queue));
+static void push_last_insn PROTO(());
+static rtx pop_last_insn PROTO(());
+static void push_label_entry PROTO((struct labelNode **labelstack, rtx label));
+static rtx pop_label_entry PROTO((struct labelNode **labelstack));
+static rtx top_label_entry PROTO((struct labelNode **labelstack));
+static struct ehEntry *copy_eh_entry PROTO((struct ehEntry *entry));
+
+
+
+/* All my cheesy stack/queue/misc data structure handling routines
+
+ ========================================================================= */
+
+static void
+push_label_entry (labelstack, label)
+ struct labelNode **labelstack;
+ rtx label;
+{
+ struct labelNode *newnode=(struct labelNode*)xmalloc (sizeof (struct labelNode));
+
+ newnode->label = label;
+ newnode->chain = *labelstack;
+ *labelstack = newnode;
+}
+
+static rtx
+pop_label_entry (labelstack)
+ struct labelNode **labelstack;
+{
+ rtx label;
+ struct labelNode *tempnode;
+
+ if (! *labelstack) return NULL_RTX;
+
+ tempnode = *labelstack;
+ label = tempnode->label;
+ *labelstack = (*labelstack)->chain;
+ free (tempnode);
+
+ return label;
+}
+
+static rtx
+top_label_entry (labelstack)
+ struct labelNode **labelstack;
+{
+ if (! *labelstack) return NULL_RTX;
+
+ return (*labelstack)->label;
+}
+
+static void
+push_except_stmts (exceptstack, catchstart, catchend)
+ struct exceptStack *exceptstack;
+ rtx catchstart, catchend;
+{
+ struct exceptNode *newnode = (struct exceptNode*)
+ xmalloc (sizeof (struct exceptNode));
+
+ newnode->catchstart = catchstart;
+ newnode->catchend = catchend;
+ newnode->chain = exceptstack->top;
+
+ exceptstack->top = newnode;
+}
+
+static int
+pop_except_stmts (exceptstack, catchstart, catchend)
+ struct exceptStack *exceptstack;
+ rtx *catchstart, *catchend;
+{
+ struct exceptNode *tempnode;
+
+ if (!exceptstack->top) {
+ *catchstart = *catchend = NULL_RTX;
+ return 0;
+ }
+
+ tempnode = exceptstack->top;
+ exceptstack->top = exceptstack->top->chain;
+
+ *catchstart = tempnode->catchstart;
+ *catchend = tempnode->catchend;
+ free (tempnode);
+
+ return 1;
+}
+
+/* Push to permanent obstack for rtl generation.
+ One level only! */
+static struct obstack *saved_rtl_obstack;
+void
+push_rtl_perm ()
+{
+ extern struct obstack permanent_obstack;
+ extern struct obstack *rtl_obstack;
+
+ saved_rtl_obstack = rtl_obstack;
+ rtl_obstack = &permanent_obstack;
+}
+
+/* Pop back to normal rtl handling. */
+static void
+pop_rtl_from_perm ()
+{
+ extern struct obstack permanent_obstack;
+ extern struct obstack *rtl_obstack;
+
+ rtl_obstack = saved_rtl_obstack;
+}
+
+static rtx
+push_eh_entry (stack)
+ struct ehStack *stack;
+{
+ struct ehNode *node = (struct ehNode*)xmalloc (sizeof (struct ehNode));
+ struct ehEntry *entry = (struct ehEntry*)xmalloc (sizeof (struct ehEntry));
+
+ if (stack == NULL) {
+ free (node);
+ free (entry);
+ return NULL_RTX;
+ }
+
+ /* These are saved for the exception table. */
+ push_rtl_perm ();
+ entry->start_label = gen_label_rtx ();
+ entry->end_label = gen_label_rtx ();
+ entry->exception_handler_label = gen_label_rtx ();
+ pop_rtl_from_perm ();
+
+ entry->finalization = NULL_TREE;
+
+ node->entry = entry;
+ node->chain = stack->top;
+ stack->top = node;
+
+ enqueue_eh_entry (&eh_table_output_queue, copy_eh_entry (entry));
+
+ return entry->start_label;
+}
+
+static struct ehEntry *
+pop_eh_entry (stack)
+ struct ehStack *stack;
+{
+ struct ehNode *tempnode;
+ struct ehEntry *tempentry;
+
+ if (stack && (tempnode = stack->top)) {
+ tempentry = tempnode->entry;
+ stack->top = stack->top->chain;
+ free (tempnode);
+
+ return tempentry;
+ }
+
+ return NULL;
+}
+
+static struct ehEntry *
+copy_eh_entry (entry)
+ struct ehEntry *entry;
+{
+ struct ehEntry *newentry;
+
+ newentry = (struct ehEntry*)xmalloc (sizeof (struct ehEntry));
+ memcpy ((void*)newentry, (void*)entry, sizeof (struct ehEntry));
+
+ return newentry;
+}
+
+static void
+enqueue_eh_entry (queue, entry)
+ struct ehQueue *queue;
+ struct ehEntry *entry;
+{
+ struct ehNode *node = (struct ehNode*)xmalloc (sizeof (struct ehNode));
+
+ node->entry = entry;
+ node->chain = NULL;
+
+ if (queue->head == NULL)
+ {
+ queue->head = node;
+ }
+ else
+ {
+ queue->tail->chain = node;
+ }
+ queue->tail = node;
+}
+
+static struct ehEntry *
+dequeue_eh_entry (queue)
+ struct ehQueue *queue;
+{
+ struct ehNode *tempnode;
+ struct ehEntry *tempentry;
+
+ if (queue->head == NULL)
+ return NULL;
+
+ tempnode = queue->head;
+ queue->head = queue->head->chain;
+
+ tempentry = tempnode->entry;
+ free (tempnode);
+
+ return tempentry;
+}
+
+static void
+new_eh_queue (queue)
+ struct ehQueue *queue;
+{
+ queue->head = queue->tail = NULL;
+}
+
+static void
+new_eh_stack (stack)
+ struct ehStack *stack;
+{
+ stack->top = NULL;
+}
+
+static void
+new_except_stack (stack)
+ struct exceptStack *stack;
+{
+ stack->top = NULL;
+}
+/* ========================================================================= */
+
+void
+lang_interim_eh (finalization)
+ tree finalization;
+{
+ if (finalization)
+ end_protect (finalization);
+ else
+ start_protect ();
+}
+
+/* sets up all the global eh stuff that needs to be initialized at the
+ start of compilation.
+
+ This includes:
+ - Setting up all the function call trees
+ - Initializing the ehqueue
+ - Initializing the eh_table_output_queue
+ - Initializing the ehstack
+ - Initializing the exceptstack
+*/
+
+void
+init_exception_processing ()
+{
+ extern tree define_function ();
+ tree unexpected_fndecl, terminate_fndecl;
+ tree set_unexpected_fndecl, set_terminate_fndecl;
+ tree catch_match_fndecl;
+ tree find_first_exception_match_fndecl;
+ tree unwind_fndecl;
+ tree temp, PFV;
+
+ interim_eh_hook = lang_interim_eh;
+
+ /* void (*)() */
+ PFV = build_pointer_type (build_function_type (void_type_node, void_list_node));
+
+ /* arg list for the build_function_type call for set_terminate () and
+ set_unexpected () */
+ temp = tree_cons (NULL_TREE, PFV, void_list_node);
+
+ push_lang_context (lang_name_c);
+
+ set_terminate_fndecl =
+ define_function ("set_terminate",
+ build_function_type (PFV, temp),
+ NOT_BUILT_IN,
+ pushdecl,
+ 0);
+ set_unexpected_fndecl =
+ define_function ("set_unexpected",
+ build_function_type (PFV, temp),
+ NOT_BUILT_IN,
+ pushdecl,
+ 0);
+
+ unexpected_fndecl =
+ define_function ("unexpected",
+ build_function_type (void_type_node, void_list_node),
+ NOT_BUILT_IN,
+ pushdecl,
+ 0);
+ terminate_fndecl =
+ define_function ("terminate",
+ build_function_type (void_type_node, void_list_node),
+ NOT_BUILT_IN,
+ pushdecl,
+ 0);
+ catch_match_fndecl =
+ define_function ("__throw_type_match",
+ build_function_type (integer_type_node,
+ tree_cons (NULL_TREE, string_type_node, tree_cons (NULL_TREE, ptr_type_node, void_list_node))),
+ NOT_BUILT_IN,
+ pushdecl,
+ 0);
+ find_first_exception_match_fndecl =
+ define_function ("__find_first_exception_table_match",
+ build_function_type (ptr_type_node,
+ tree_cons (NULL_TREE, ptr_type_node,
+ void_list_node)),
+ NOT_BUILT_IN,
+ pushdecl,
+ 0);
+ unwind_fndecl =
+ define_function ("__unwind_function",
+ build_function_type (void_type_node,
+ tree_cons (NULL_TREE, ptr_type_node, void_list_node)),
+ NOT_BUILT_IN,
+ pushdecl,
+ 0);
+
+ Unexpected = default_conversion (unexpected_fndecl);
+ Terminate = default_conversion (terminate_fndecl);
+ SetTerminate = default_conversion (set_terminate_fndecl);
+ SetUnexpected = default_conversion (set_unexpected_fndecl);
+ CatchMatch = default_conversion (catch_match_fndecl);
+ FirstExceptionMatch = default_conversion (find_first_exception_match_fndecl);
+ Unwind = default_conversion (unwind_fndecl);
+ BuiltinReturnAddress = default_conversion (builtin_return_address_fndecl);
+
+ TerminateFunctionCall = build_function_call (Terminate, NULL_TREE);
+
+ pop_lang_context ();
+ throw_label = gen_label_rtx ();
+ saved_pc = gen_rtx (REG, Pmode, 16);
+ saved_throw_type = gen_rtx (REG, Pmode, 17);
+ saved_throw_value = gen_rtx (REG, Pmode, 18);
+
+ new_eh_queue (&ehqueue);
+ new_eh_queue (&eh_table_output_queue);
+ new_eh_stack (&ehstack);
+ new_except_stack (&exceptstack);
+}
+
+/* call this to begin a block of unwind protection (ie: when an object is
+ constructed) */
+void
+start_protect ()
+{
+ if (doing_eh (0))
+ {
+ emit_label (push_eh_entry (&ehstack));
+ }
+}
+
+/* call this to end a block of unwind protection. the finalization tree is
+ the finalization which needs to be run in order to cleanly unwind through
+ this level of protection. (ie: call this when a scope is exited)*/
+void
+end_protect (finalization)
+ tree finalization;
+{
+ struct ehEntry *entry = pop_eh_entry (&ehstack);
+
+ if (! doing_eh (0))
+ return;
+
+ emit_label (entry->end_label);
+
+ entry->finalization = finalization;
+
+ enqueue_eh_entry (&ehqueue, entry);
+}
+
+/* call this on start of a try block. */
+void
+expand_start_try_stmts ()
+{
+ if (doing_eh (1))
+ {
+ start_protect ();
+ }
+}
+
+void
+expand_end_try_stmts ()
+{
+ end_protect (integer_zero_node);
+}
+
+struct insn_save_node {
+ rtx last;
+ struct insn_save_node *chain;
+ };
+
+static struct insn_save_node *InsnSave = NULL;
+
+
+/* Used to keep track of where the catch blocks start. */
+static void
+push_last_insn ()
+{
+ struct insn_save_node *newnode = (struct insn_save_node*)
+ xmalloc (sizeof (struct insn_save_node));
+
+ newnode->last = get_last_insn ();
+ newnode->chain = InsnSave;
+ InsnSave = newnode;
+}
+
+/* Use to keep track of where the catch blocks start. */
+static rtx
+pop_last_insn ()
+{
+ struct insn_save_node *tempnode;
+ rtx temprtx;
+
+ if (!InsnSave) return NULL_RTX;
+
+ tempnode = InsnSave;
+ temprtx = tempnode->last;
+ InsnSave = InsnSave->chain;
+
+ free (tempnode);
+
+ return temprtx;
+}
+
+/* call this to start processing of all the catch blocks. */
+void
+expand_start_all_catch ()
+{
+ struct ehEntry *entry;
+ rtx label;
+
+ if (! doing_eh (1))
+ return;
+
+ emit_line_note (input_filename, lineno);
+ label = gen_label_rtx ();
+ /* The label for the exception handling block we will save. */
+ emit_label (label);
+
+ push_label_entry (&caught_return_label_stack, label);
+
+ /* Remember where we started. */
+ push_last_insn ();
+
+ emit_insn (gen_nop ());
+
+ /* Will this help us not stomp on it? */
+ emit_insn (gen_rtx (USE, VOIDmode, saved_throw_type));
+ emit_insn (gen_rtx (USE, VOIDmode, saved_throw_value));
+
+ while (1)
+ {
+ entry = dequeue_eh_entry (&ehqueue);
+ emit_label (entry->exception_handler_label);
+
+ expand_expr (entry->finalization, const0_rtx, VOIDmode, 0);
+
+ /* When we get down to the matching entry, stop. */
+ if (entry->finalization == integer_zero_node)
+ break;
+
+ free (entry);
+ }
+
+ /* This goes when the below moves out of our way. */
+#if 1
+ label = gen_label_rtx ();
+ emit_jump (label);
+#endif
+
+ /* All this should be out of line, and saved back in the exception handler
+ block area. */
+#if 1
+ entry->start_label = entry->exception_handler_label;
+ /* These are saved for the exception table. */
+ push_rtl_perm ();
+ entry->end_label = gen_label_rtx ();
+ entry->exception_handler_label = gen_label_rtx ();
+ entry->finalization = TerminateFunctionCall;
+ pop_rtl_from_perm ();
+ emit_label (entry->end_label);
+
+ enqueue_eh_entry (&eh_table_output_queue, copy_eh_entry (entry));
+
+ /* After running the finalization, continue on out to the next
+ cleanup, if we have nothing better to do. */
+ emit_move_insn (saved_pc, gen_rtx (LABEL_REF, Pmode, entry->end_label));
+ /* Will this help us not stomp on it? */
+ emit_insn (gen_rtx (USE, VOIDmode, saved_throw_type));
+ emit_insn (gen_rtx (USE, VOIDmode, saved_throw_value));
+ emit_jump (throw_label);
+ emit_label (entry->exception_handler_label);
+ expand_expr (entry->finalization, const0_rtx, VOIDmode, 0);
+ emit_barrier ();
+#endif
+ emit_label (label);
+}
+
+/* call this to end processing of all the catch blocks. */
+void
+expand_end_all_catch ()
+{
+ rtx catchstart, catchend, last;
+ rtx label;
+
+ if (! doing_eh (1))
+ return;
+
+ /* Find the start of the catch block. */
+ last = pop_last_insn ();
+ catchstart = NEXT_INSN (last);
+ catchend = get_last_insn ();
+
+ NEXT_INSN (last) = 0;
+ set_last_insn (last);
+
+ /* this level of catch blocks is done, so set up the successful catch jump
+ label for the next layer of catch blocks. */
+ pop_label_entry (&caught_return_label_stack);
+
+ push_except_stmts (&exceptstack, catchstart, catchend);
+
+ /* Here we fall through into the continuation code. */
+}
+
+
+/* this is called from expand_exception_blocks () to expand the toplevel
+ finalizations for a function. */
+void
+expand_leftover_cleanups ()
+{
+ struct ehEntry *entry;
+ rtx first_label = NULL_RTX;
+
+ if (! doing_eh (0))
+ return;
+
+ /* Will this help us not stomp on it? */
+ emit_insn (gen_rtx (USE, VOIDmode, saved_throw_type));
+ emit_insn (gen_rtx (USE, VOIDmode, saved_throw_value));
+
+ while ((entry = dequeue_eh_entry (&ehqueue)) != 0)
+ {
+ if (! first_label)
+ first_label = entry->exception_handler_label;
+ emit_label (entry->exception_handler_label);
+
+ expand_expr (entry->finalization, const0_rtx, VOIDmode, 0);
+
+ /* leftover try block, opps. */
+ if (entry->finalization == integer_zero_node)
+ abort ();
+
+ free (entry);
+ }
+ if (first_label)
+ {
+ rtx label;
+ struct ehEntry entry;
+ /* These are saved for the exception table. */
+ push_rtl_perm ();
+ label = gen_label_rtx ();
+ entry.start_label = first_label;
+ entry.end_label = label;
+ entry.exception_handler_label = gen_label_rtx ();
+ entry.finalization = TerminateFunctionCall;
+ pop_rtl_from_perm ();
+ emit_label (label);
+
+ enqueue_eh_entry (&eh_table_output_queue, copy_eh_entry (&entry));
+
+ /* After running the finalization, continue on out to the next
+ cleanup, if we have nothing better to do. */
+ emit_move_insn (saved_pc, gen_rtx (LABEL_REF, Pmode, entry.end_label));
+ /* Will this help us not stomp on it? */
+ emit_insn (gen_rtx (USE, VOIDmode, saved_throw_type));
+ emit_insn (gen_rtx (USE, VOIDmode, saved_throw_value));
+ emit_jump (throw_label);
+ emit_label (entry.exception_handler_label);
+ expand_expr (entry.finalization, const0_rtx, VOIDmode, 0);
+ emit_barrier ();
+ }
+}
+
+/* call this to start a catch block. Typename is the typename, and identifier
+ is the variable to place the object in or NULL if the variable doesn't
+ matter. If typename is NULL, that means its a "catch (...)" or catch
+ everything. In that case we don't need to do any type checking.
+ (ie: it ends up as the "else" clause rather than an "else if" clause) */
+void
+expand_start_catch_block (declspecs, declarator)
+ tree declspecs, declarator;
+{
+ rtx false_label_rtx;
+ rtx protect_label_rtx;
+ tree type;
+ tree decl;
+ tree init;
+
+ if (! doing_eh (1))
+ return;
+
+ /* Create a binding level for the parm. */
+ expand_start_bindings (0);
+
+ if (declspecs)
+ {
+ tree init_type;
+ decl = grokdeclarator (declarator, declspecs, NORMAL, 1, NULL_TREE);
+
+ /* Figure out the type that the initializer is. */
+ init_type = TREE_TYPE (decl);
+ if (TREE_CODE (init_type) != REFERENCE_TYPE)
+ init_type = build_reference_type (init_type);
+
+ init = convert_from_reference (save_expr (make_tree (init_type, saved_throw_value)));
+
+ /* Do we need the below two lines? */
+ /* Let `finish_decl' know that this initializer is ok. */
+ DECL_INITIAL (decl) = init;
+ /* This needs to be preallocated under the try block,
+ in a union of all catch variables. */
+ pushdecl (decl);
+ type = TREE_TYPE (decl);
+
+ /* peel back references, so they match. */
+ if (TREE_CODE (type) == REFERENCE_TYPE)
+ type = TREE_TYPE (type);
+ }
+ else
+ type = NULL_TREE;
+
+ false_label_rtx = gen_label_rtx ();
+ push_label_entry (&false_label_stack, false_label_rtx);
+
+ /* This is saved for the exception table. */
+ push_rtl_perm ();
+ protect_label_rtx = gen_label_rtx ();
+ pop_rtl_from_perm ();
+ push_label_entry (&false_label_stack, protect_label_rtx);
+
+ if (type)
+ {
+ tree params;
+ char *typestring;
+ rtx call_rtx, return_value_rtx;
+ tree catch_match_fcall;
+ tree catchmatch_arg, argval;
+
+ typestring = build_overload_name (type, 1, 1);
+
+ params = tree_cons (NULL_TREE,
+ combine_strings (build_string (strlen (typestring)+1, typestring)),
+ tree_cons (NULL_TREE,
+ make_tree (ptr_type_node, saved_throw_type),
+ NULL_TREE));
+ catch_match_fcall = build_function_call (CatchMatch, params);
+ call_rtx = expand_call (catch_match_fcall, NULL_RTX, 0);
+
+ return_value_rtx =
+ hard_function_value (integer_type_node, catch_match_fcall);
+
+ /* did the throw type match function return TRUE? */
+ emit_cmp_insn (return_value_rtx, const0_rtx, NE, NULL_RTX,
+ GET_MODE (return_value_rtx), 0, 0);
+
+ /* if it returned FALSE, jump over the catch block, else fall into it */
+ emit_jump_insn (gen_bne (false_label_rtx));
+ finish_decl (decl, init, NULL_TREE, 0);
+ }
+ else
+ {
+ /* Fall into the catch all section. */
+ }
+
+ /* This is the starting of something to protect. */
+ emit_label (protect_label_rtx);
+
+ emit_line_note (input_filename, lineno);
+}
+
+
+/* Call this to end a catch block. Its responsible for emitting the
+ code to handle jumping back to the correct place, and for emitting
+ the label to jump to if this catch block didn't match. */
+void expand_end_catch_block ()
+{
+ if (doing_eh (1))
+ {
+ rtx start_protect_label_rtx;
+ rtx end_protect_label_rtx;
+ tree decls;
+ struct ehEntry entry;
+
+ /* label we jump to if we caught the exception */
+ emit_jump (top_label_entry (&caught_return_label_stack));
+
+ /* Code to throw out to outer context, if we get an throw from within
+ our catch handler. */
+ /* These are saved for the exception table. */
+ push_rtl_perm ();
+ entry.exception_handler_label = gen_label_rtx ();
+ pop_rtl_from_perm ();
+ emit_label (entry.exception_handler_label);
+ emit_move_insn (saved_pc, gen_rtx (LABEL_REF,
+ Pmode,
+ top_label_entry (&caught_return_label_stack)));
+ emit_jump (throw_label);
+ /* No associated finalization. */
+ entry.finalization = NULL_TREE;
+
+ /* Because we are reordered out of line, we have to protect this. */
+ /* label for the start of the protection region. */
+ start_protect_label_rtx = pop_label_entry (&false_label_stack);
+
+ /* Cleanup the EH paramater. */
+ expand_end_bindings (decls = getdecls (), decls != NULL_TREE, 0);
+
+ /* label we emit to jump to if this catch block didn't match. */
+ emit_label (end_protect_label_rtx = pop_label_entry (&false_label_stack));
+
+ /* Because we are reordered out of line, we have to protect this. */
+ entry.start_label = start_protect_label_rtx;
+ entry.end_label = end_protect_label_rtx;
+
+ /* These set up a call to throw the caught exception into the outer
+ context. */
+ enqueue_eh_entry (&eh_table_output_queue, copy_eh_entry (&entry));
+ }
+}
+
+/* cheesyness to save some typing. returns the return value rtx */
+rtx
+do_function_call (func, params, return_type)
+ tree func, params, return_type;
+{
+ tree func_call;
+ func_call = build_function_call (func, params);
+ expand_call (func_call, NULL_RTX, 0);
+ if (return_type != NULL_TREE)
+ return hard_function_value (return_type, func_call);
+ return NULL_RTX;
+}
+
+
+/* is called from expand_excpetion_blocks () to generate the code in a function
+ to "throw" if anything in the function needs to preform a throw.
+
+ expands "throw" as the following psuedo code:
+
+ throw:
+ eh = find_first_exception_match (saved_pc);
+ if (!eh) goto gotta_rethrow_it;
+ goto eh;
+
+ gotta_rethrow_it:
+ saved_pc = __builtin_return_address (0);
+ pop_to_previous_level ();
+ goto throw;
+
+ */
+static void
+expand_builtin_throw ()
+{
+ tree fcall;
+ tree params;
+ rtx return_val_rtx;
+ rtx gotta_rethrow_it = gen_label_rtx ();
+ rtx gotta_call_terminate = gen_label_rtx ();
+ rtx unwind_and_throw = gen_label_rtx ();
+ rtx goto_unwind_and_throw = gen_label_rtx ();
+
+ emit_label (throw_label);
+
+ /* search for an exception handler for the saved_pc */
+ return_val_rtx = do_function_call (FirstExceptionMatch,
+ tree_cons (NULL_TREE, make_tree (ptr_type_node, saved_pc), NULL_TREE),
+ ptr_type_node);
+
+ /* did we find one? */
+ emit_cmp_insn (return_val_rtx, const0_rtx, EQ, NULL_RTX,
+ GET_MODE (return_val_rtx), 0, 0);
+
+ /* if not, jump to gotta_rethrow_it */
+ emit_jump_insn (gen_beq (gotta_rethrow_it));
+
+ /* we found it, so jump to it */
+ emit_indirect_jump (return_val_rtx);
+
+ /* code to deal with unwinding and looking for it again */
+ emit_label (gotta_rethrow_it);
+
+ /* call to __builtin_return_address () */
+ params=tree_cons (NULL_TREE, integer_zero_node, NULL_TREE);
+ fcall = build_function_call (BuiltinReturnAddress, params);
+ return_val_rtx = expand_expr (fcall, NULL_RTX, SImode, 0);
+
+ /* did __builtin_return_address () return a valid address? */
+ emit_cmp_insn (return_val_rtx, const0_rtx, EQ, NULL_RTX,
+ GET_MODE (return_val_rtx), 0, 0);
+
+ emit_jump_insn (gen_beq (gotta_call_terminate));
+
+ /* yes it did */
+ emit_move_insn (saved_pc, return_val_rtx);
+ do_unwind (throw_label);
+ emit_jump (throw_label);
+
+ /* no it didn't --> therefore we need to call terminate */
+ emit_label (gotta_call_terminate);
+ do_function_call (Terminate, NULL_TREE, NULL_TREE);
+}
+
+
+/* This is called to expand all the toplevel exception handling
+ finalization for a function. It should only be called once per
+ function. */
+void
+expand_exception_blocks ()
+{
+ rtx catchstart, catchend;
+ rtx last;
+ static rtx funcend;
+
+ funcend = gen_label_rtx ();
+ emit_jump (funcend);
+ /* expand_null_return (); */
+
+ while (pop_except_stmts (&exceptstack, &catchstart, &catchend)) {
+ last = get_last_insn ();
+ NEXT_INSN (last) = catchstart;
+ PREV_INSN (catchstart) = last;
+ NEXT_INSN (catchend) = 0;
+ set_last_insn (catchend);
+ }
+
+ expand_leftover_cleanups ();
+
+ {
+ static int have_done = 0;
+ if (! have_done && TREE_PUBLIC (current_function_decl)
+ && ! DECL_INLINE (current_function_decl))
+ {
+ have_done = 1;
+ expand_builtin_throw ();
+ }
+ }
+ emit_label (funcend);
+}
+
+
+/* call this to expand a throw statement. This follows the following
+ algorithm:
+
+ 1. Allocate space to save the current PC onto the stack.
+ 2. Generate and emit a label and save its address into the
+ newly allocate stack space since we can't save the pc directly.
+ 3. If this is the first call to throw in this function:
+ generate a label for the throw block
+ 4. jump to the throw block label. */
+void
+expand_throw (exp)
+ tree exp;
+{
+ rtx label;
+ tree type;
+
+ if (! doing_eh (1))
+ return;
+
+ /* This is the label that represents where in the code we were, when
+ we got an exception. This needs to be updated when we rethrow an
+ exception, so that the matching routine knows to search out. */
+ label = gen_label_rtx ();
+ emit_label (label);
+ emit_move_insn (saved_pc, gen_rtx (LABEL_REF, Pmode, label));
+
+ if (exp)
+ {
+ /* throw expression */
+ /* First, decay it. */
+ exp = default_conversion (exp);
+ type = TREE_TYPE (exp);
+
+ {
+ char *typestring = build_overload_name (type, 1, 1);
+ tree throw_type = build1 (ADDR_EXPR, ptr_type_node, combine_strings (build_string (strlen (typestring)+1, typestring)));
+ rtx throw_type_rtx = expand_expr (throw_type, NULL_RTX, VOIDmode, 0);
+ rtx throw_value_rtx;
+
+ emit_move_insn (saved_throw_type, throw_type_rtx);
+ exp = convert_to_reference (build_reference_type (build_type_variant (TREE_TYPE (exp), 1, 0)), exp, CONV_STATIC, LOOKUP_COMPLAIN, NULL_TREE);
+ if (exp == error_mark_node)
+ error (" in thrown expression");
+ throw_value_rtx = expand_expr (build_unary_op (ADDR_EXPR, exp, 0), NULL_RTX, VOIDmode, 0);
+ emit_move_insn (saved_throw_value, throw_value_rtx);
+ }
+ }
+ else
+ {
+ /* rethrow current exception */
+ /* This part is easy, as we dont' have to do anything else. */
+ }
+
+ emit_jump (throw_label);
+}
+
+
+/* output the exception table */
+void
+build_exception_table ()
+{
+ extern FILE *asm_out_file;
+ struct ehEntry *entry;
+
+ if (! doing_eh (0))
+ return;
+
+ exception_section ();
+
+ /* Beginning marker for table. */
+ fprintf (asm_out_file, " .global ___EXCEPTION_TABLE__\n");
+ fprintf (asm_out_file, " .align 4\n");
+ fprintf (asm_out_file, "___EXCEPTION_TABLE__:\n");
+ fprintf (asm_out_file, " .word 0, 0, 0\n");
+
+ while (entry = dequeue_eh_entry (&eh_table_output_queue)) {
+ output_exception_table_entry (asm_out_file,
+ entry->start_label, entry->end_label, entry->exception_handler_label);
+ }
+
+ /* Ending marker for table. */
+ fprintf (asm_out_file, " .global ___EXCEPTION_END__\n");
+ fprintf (asm_out_file, "___EXCEPTION_END__:\n");
+ fprintf (asm_out_file, " .word -1, -1, -1\n");
+}
+
+/* end of: my-cp-except.c */
+#endif
+
+
+/* Build a throw expression. */
+tree
+build_throw (e)
+ tree e;
+{
+ e = build1 (THROW_EXPR, void_type_node, e);
+ TREE_SIDE_EFFECTS (e) = 1;
+ return e;
+}
diff --git a/gnu/usr.bin/cc/cc1plus/expr.c b/gnu/usr.bin/cc/cc1plus/expr.c
new file mode 100644
index 0000000..c2213d5
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/expr.c
@@ -0,0 +1,275 @@
+/* Convert language-specific tree expression to rtl instructions,
+ for GNU compiler.
+ Copyright (C) 1988, 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#include "rtl.h"
+#include "tree.h"
+#include "flags.h"
+#include "expr.h"
+#include "cp-tree.h"
+
+#undef NULL
+#define NULL 0
+
+/* Hook used by expand_expr to expand language-specific tree codes. */
+
+rtx
+cplus_expand_expr (exp, target, tmode, modifier)
+ tree exp;
+ rtx target;
+ enum machine_mode tmode;
+ enum expand_modifier modifier;
+{
+ tree type = TREE_TYPE (exp);
+ register enum machine_mode mode = TYPE_MODE (type);
+ register enum tree_code code = TREE_CODE (exp);
+ rtx original_target = target;
+ int ignore = target == const0_rtx;
+
+ if (ignore)
+ target = 0, original_target = 0;
+
+ /* No sense saving up arithmetic to be done
+ if it's all in the wrong mode to form part of an address.
+ And force_operand won't know whether to sign-extend or zero-extend. */
+
+ if (mode != Pmode && modifier == EXPAND_SUM)
+ modifier = EXPAND_NORMAL;
+
+ switch (code)
+ {
+ case NEW_EXPR:
+ {
+ /* Something needs to be initialized, but we didn't know
+ where that thing was when building the tree. For example,
+ it could be the return value of a function, or a parameter
+ to a function which lays down in the stack, or a temporary
+ variable which must be passed by reference.
+
+ Cleanups are handled in a language-specific way: they
+ might be run by the called function (true in GNU C++
+ for parameters with cleanups), or they might be
+ run by the caller, after the call (true in GNU C++
+ for other cleanup needs). */
+
+ tree func = TREE_OPERAND (exp, 0);
+ tree args = TREE_OPERAND (exp, 1);
+ tree type = TREE_TYPE (exp), slot;
+ tree fn_type = TREE_TYPE (TREE_TYPE (func));
+ tree return_type = TREE_TYPE (fn_type);
+ tree call_exp;
+ rtx call_target, return_target;
+ int pcc_struct_return = 0;
+
+ /* The expression `init' wants to initialize what
+ `target' represents. SLOT holds the slot for TARGET. */
+ slot = TREE_OPERAND (exp, 2);
+
+ if (target == 0)
+ {
+ /* Should always be called with a target in BLKmode case. */
+ my_friendly_assert (mode != BLKmode, 205);
+ my_friendly_assert (DECL_RTL (slot) != 0, 206);
+
+ target = gen_reg_rtx (mode);
+ }
+
+ /* The target the initializer will initialize (CALL_TARGET)
+ must now be directed to initialize the target we are
+ supposed to initialize (TARGET). The semantics for
+ choosing what CALL_TARGET is is language-specific,
+ as is building the call which will perform the
+ initialization. It is left here to show the choices that
+ exist for C++. */
+
+ if (TREE_CODE (func) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (func, 0)) == FUNCTION_DECL
+ && DECL_CONSTRUCTOR_P (TREE_OPERAND (func, 0)))
+ {
+ type = TYPE_POINTER_TO (type);
+ /* Don't clobber a value that might be part of a default
+ parameter value. */
+ if (TREE_PERMANENT (args))
+ args = tree_cons (0, build1 (ADDR_EXPR, type, slot),
+ TREE_CHAIN (args));
+ else
+ TREE_VALUE (args) = build1 (ADDR_EXPR, type, slot);
+ call_target = 0;
+ }
+ else if (TREE_CODE (return_type) == REFERENCE_TYPE)
+ {
+ type = return_type;
+ call_target = 0;
+ }
+ else
+ {
+#ifdef PCC_STATIC_STRUCT_RETURN
+ pcc_struct_return = 1;
+ call_target = 0;
+#else
+ call_target = target;
+#endif
+ }
+ if (call_target)
+ {
+ preserve_temp_slots (call_target);
+
+ /* Make this a valid memory address now. The code below assumes
+ that it can compare rtx and make assumptions based on the
+ result. The assumptions are true only if the address was
+ valid to begin with. */
+ call_target = validize_mem (call_target);
+ }
+
+ preserve_temp_slots (DECL_RTL (slot));
+ call_exp = build (CALL_EXPR, type, func, args, 0);
+ TREE_SIDE_EFFECTS (call_exp) = 1;
+ return_target = expand_expr (call_exp, call_target, mode, 0);
+ free_temp_slots ();
+ if (call_target == 0)
+ {
+ if (pcc_struct_return)
+ {
+ tree init = build (RTL_EXPR, type, 0, return_target);
+ TREE_ADDRESSABLE (init) = 1;
+ expand_aggr_init (slot, init, 0);
+ if (TYPE_NEEDS_DESTRUCTOR (type))
+ {
+ init = build (RTL_EXPR, build_reference_type (type), 0,
+ XEXP (return_target, 0));
+ init = maybe_build_cleanup (convert_from_reference (init));
+ if (init != NULL_TREE)
+ expand_expr (init, 0, 0, 0);
+ }
+ call_target = return_target = DECL_RTL (slot);
+ }
+ else
+ call_target = return_target;
+ }
+
+ if (call_target != return_target)
+ {
+ my_friendly_assert (! TYPE_NEEDS_CONSTRUCTING (type), 317);
+ if (GET_MODE (return_target) == BLKmode)
+ emit_block_move (call_target, return_target, expr_size (exp),
+ TYPE_ALIGN (type) / BITS_PER_UNIT);
+ else
+ emit_move_insn (call_target, return_target);
+ }
+
+ if (TREE_CODE (return_type) == REFERENCE_TYPE)
+ {
+ tree init;
+
+ if (GET_CODE (call_target) == REG
+ && REGNO (call_target) < FIRST_PSEUDO_REGISTER)
+ my_friendly_abort (39);
+
+ type = TREE_TYPE (exp);
+
+ init = build (RTL_EXPR, return_type, 0, call_target);
+ /* We got back a reference to the type we want. Now initialize
+ target with that. */
+ expand_aggr_init (slot, init, 0);
+ }
+
+ if (DECL_RTL (slot) != target)
+ emit_move_insn (DECL_RTL (slot), target);
+ return DECL_RTL (slot);
+ }
+
+ case OFFSET_REF:
+ {
+#if 1
+ return expand_expr (default_conversion (resolve_offset_ref (exp)),
+ target, tmode, EXPAND_NORMAL);
+#else
+ /* This is old crusty code, and does not handle all that the
+ resolve_offset_ref function does. (mrs) */
+ tree base = build_unary_op (ADDR_EXPR, TREE_OPERAND (exp, 0), 0);
+ tree offset = build_unary_op (ADDR_EXPR, TREE_OPERAND (exp, 1), 0);
+ return expand_expr (build (PLUS_EXPR, TREE_TYPE (exp), base, offset),
+ target, tmode, EXPAND_NORMAL);
+#endif
+ }
+
+ case THUNK_DECL:
+ return DECL_RTL (exp);
+
+ case THROW_EXPR:
+ expand_throw (TREE_OPERAND (exp, 0));
+ return NULL;
+
+ default:
+ break;
+ }
+ my_friendly_abort (40);
+ /* NOTREACHED */
+ return NULL;
+}
+
+void
+init_cplus_expand ()
+{
+ lang_expand_expr = cplus_expand_expr;
+}
+
+/* If DECL had its rtl moved from where callers expect it
+ to be, fix it up. RESULT is the nominal rtl for the RESULT_DECL,
+ which may be a pseudo instead of a hard register. */
+
+void
+fixup_result_decl (decl, result)
+ tree decl;
+ rtx result;
+{
+ if (REG_P (result))
+ {
+ if (REGNO (result) >= FIRST_PSEUDO_REGISTER)
+ {
+ rtx real_decl_result;
+
+#ifdef FUNCTION_OUTGOING_VALUE
+ real_decl_result
+ = FUNCTION_OUTGOING_VALUE (TREE_TYPE (decl), current_function_decl);
+#else
+ real_decl_result
+ = FUNCTION_VALUE (TREE_TYPE (decl), current_function_decl);
+#endif
+ REG_FUNCTION_VALUE_P (real_decl_result) = 1;
+ result = real_decl_result;
+ }
+ emit_move_insn (result, DECL_RTL (decl));
+ emit_insn (gen_rtx (USE, VOIDmode, result));
+ }
+}
+
+/* Return nonzero iff DECL is memory-based. The DECL_RTL of
+ certain const variables might be a CONST_INT, or a REG
+ in some cases. We cannot use `memory_operand' as a test
+ here because on most RISC machines, a variable's address
+ is not, by itself, a legitimate address. */
+int
+decl_in_memory_p (decl)
+ tree decl;
+{
+ return DECL_RTL (decl) != 0 && GET_CODE (DECL_RTL (decl)) == MEM;
+}
diff --git a/gnu/usr.bin/cc/cc1plus/gc.c b/gnu/usr.bin/cc/cc1plus/gc.c
new file mode 100644
index 0000000..9db4d62
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/gc.c
@@ -0,0 +1,988 @@
+/* Garbage collection primitives for GNU C++.
+ Copyright (C) 1992, 1993 Free Software Foundation, Inc.
+ Contributed by Michael Tiemann (tiemann@cygnus.com)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#include "tree.h"
+#include "cp-tree.h"
+#include "flags.h"
+
+#undef NULL
+#define NULL 0
+
+extern tree define_function ();
+extern tree build_t_desc_overload ();
+
+/* This is the function decl for the (pseudo-builtin) __gc_protect
+ function. Args are (class *value, int index); Returns value. */
+tree gc_protect_fndecl;
+
+/* This is the function decl for the (pseudo-builtin) __gc_unprotect
+ function. Args are (int index); void return. */
+tree gc_unprotect_fndecl;
+
+/* This is the function decl for the (pseudo-builtin) __gc_push
+ function. Args are (int length); void return. */
+tree gc_push_fndecl;
+
+/* This is the function decl for the (pseudo-builtin) __gc_pop
+ function. Args are void; void return. */
+tree gc_pop_fndecl;
+
+/* Special integers that are used to represent bits in gc-safe objects. */
+tree gc_nonobject;
+tree gc_visible;
+tree gc_white;
+tree gc_offwhite;
+tree gc_grey;
+tree gc_black;
+
+/* in c-common.c */
+extern tree combine_strings PROTO((tree));
+
+/* Predicate that returns non-zero if TYPE needs some kind of
+ entry for the GC. Returns zero otherwise. */
+int
+type_needs_gc_entry (type)
+ tree type;
+{
+ tree ttype = type;
+
+ if (! flag_gc || type == error_mark_node)
+ return 0;
+
+ /* Aggregate types need gc entries if any of their members
+ need gc entries. */
+ if (IS_AGGR_TYPE (type))
+ {
+ tree binfos;
+ tree fields = TYPE_FIELDS (type);
+ int i;
+
+ /* We don't care about certain pointers. Pointers
+ to virtual baseclasses are always up front. We also
+ cull out virtual function table pointers because it's
+ easy, and it simplifies the logic.*/
+ while (fields
+ && (DECL_NAME (fields) == NULL_TREE
+ || VFIELD_NAME_P (DECL_NAME (fields))
+ || VBASE_NAME_P (DECL_NAME (fields))
+ || !strcmp (IDENTIFIER_POINTER (DECL_NAME (fields)), "__bits")))
+ fields = TREE_CHAIN (fields);
+
+ while (fields)
+ {
+ if (type_needs_gc_entry (TREE_TYPE (fields)))
+ return 1;
+ fields = TREE_CHAIN (fields);
+ }
+
+ binfos = TYPE_BINFO_BASETYPES (type);
+ if (binfos)
+ for (i = TREE_VEC_LENGTH (binfos)-1; i >= 0; i--)
+ if (type_needs_gc_entry (BINFO_TYPE (TREE_VEC_ELT (binfos, i))))
+ return 1;
+
+ return 0;
+ }
+
+ while (TREE_CODE (ttype) == ARRAY_TYPE
+ && TREE_CODE (TREE_TYPE (ttype)) == ARRAY_TYPE)
+ ttype = TREE_TYPE (ttype);
+ if ((TREE_CODE (ttype) == POINTER_TYPE
+ || TREE_CODE (ttype) == ARRAY_TYPE
+ || TREE_CODE (ttype) == REFERENCE_TYPE)
+ && IS_AGGR_TYPE (TREE_TYPE (ttype))
+ && CLASSTYPE_DOSSIER (TREE_TYPE (ttype)))
+ return 1;
+
+ return 0;
+}
+
+/* Predicate that returns non-zero iff FROM is safe from the GC.
+
+ If TO is nonzero, it means we know that FROM is being stored
+ in TO, which make make it safe. */
+int
+value_safe_from_gc (to, from)
+ tree to, from;
+{
+ /* First, return non-zero for easy cases: parameters,
+ static variables. */
+ if (TREE_CODE (from) == PARM_DECL
+ || (TREE_CODE (from) == VAR_DECL
+ && TREE_STATIC (from)))
+ return 1;
+
+ /* If something has its address taken, it cannot be
+ in the heap, so it doesn't need to be protected. */
+ if (TREE_CODE (from) == ADDR_EXPR || TREE_REFERENCE_EXPR (from))
+ return 1;
+
+ /* If we are storing into a static variable, then what
+ we store will be safe from the gc. */
+ if (to && TREE_CODE (to) == VAR_DECL
+ && TREE_STATIC (to))
+ return 1;
+
+ /* Now recurse on structure of FROM. */
+ switch (TREE_CODE (from))
+ {
+ case COMPONENT_REF:
+ /* These guys are special, and safe. */
+ if (TREE_CODE (TREE_OPERAND (from, 1)) == FIELD_DECL
+ && (VFIELD_NAME_P (DECL_NAME (TREE_OPERAND (from, 1)))
+ || VBASE_NAME_P (DECL_NAME (TREE_OPERAND (from, 1)))))
+ return 1;
+ /* fall through... */
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ case NON_LVALUE_EXPR:
+ case WITH_CLEANUP_EXPR:
+ case SAVE_EXPR:
+ case PREDECREMENT_EXPR:
+ case PREINCREMENT_EXPR:
+ case POSTDECREMENT_EXPR:
+ case POSTINCREMENT_EXPR:
+ if (value_safe_from_gc (to, TREE_OPERAND (from, 0)))
+ return 1;
+ break;
+
+ case VAR_DECL:
+ case PARM_DECL:
+ /* We can safely pass these things as parameters to functions. */
+ if (to == 0)
+ return 1;
+
+ case ARRAY_REF:
+ case INDIRECT_REF:
+ case RESULT_DECL:
+ case OFFSET_REF:
+ case CALL_EXPR:
+ case METHOD_CALL_EXPR:
+ break;
+
+ case COMPOUND_EXPR:
+ case TARGET_EXPR:
+ if (value_safe_from_gc (to, TREE_OPERAND (from, 1)))
+ return 1;
+ break;
+
+ case COND_EXPR:
+ if (value_safe_from_gc (to, TREE_OPERAND (from, 1))
+ && value_safe_from_gc (to, TREE_OPERAND (from, 2)))
+ return 1;
+ break;
+
+ case PLUS_EXPR:
+ case MINUS_EXPR:
+ if ((type_needs_gc_entry (TREE_TYPE (TREE_OPERAND (from, 0)))
+ || value_safe_from_gc (to, TREE_OPERAND (from, 0)))
+ && (type_needs_gc_entry (TREE_TYPE (TREE_OPERAND (from, 1))) == 0
+ || value_safe_from_gc (to, TREE_OPERAND (from, 1))))
+ return 1;
+ break;
+
+ case RTL_EXPR:
+ /* Every time we build an RTL_EXPR in the front-end, we must
+ ensure that everything in it is safe from the garbage collector.
+ ??? This has only been done for `build_new'. */
+ return 1;
+
+ default:
+ my_friendly_abort (41);
+ }
+
+ if (to == 0)
+ return 0;
+
+ /* FROM wasn't safe. But other properties of TO might make it safe. */
+ switch (TREE_CODE (to))
+ {
+ case VAR_DECL:
+ case PARM_DECL:
+ /* We already culled out static VAR_DECLs above. */
+ return 0;
+
+ case COMPONENT_REF:
+ /* These guys are special, and safe. */
+ if (TREE_CODE (TREE_OPERAND (to, 1)) == FIELD_DECL
+ && (VFIELD_NAME_P (DECL_NAME (TREE_OPERAND (to, 1)))
+ || VBASE_NAME_P (DECL_NAME (TREE_OPERAND (to, 1)))))
+ return 1;
+ /* fall through... */
+
+ case NOP_EXPR:
+ case NON_LVALUE_EXPR:
+ case WITH_CLEANUP_EXPR:
+ case SAVE_EXPR:
+ case PREDECREMENT_EXPR:
+ case PREINCREMENT_EXPR:
+ case POSTDECREMENT_EXPR:
+ case POSTINCREMENT_EXPR:
+ return value_safe_from_gc (TREE_OPERAND (to, 0), from);
+
+ case COMPOUND_EXPR:
+ case TARGET_EXPR:
+ return value_safe_from_gc (TREE_OPERAND (to, 1), from);
+
+ case COND_EXPR:
+ return (value_safe_from_gc (TREE_OPERAND (to, 1), from)
+ && value_safe_from_gc (TREE_OPERAND (to, 2), from));
+
+ case INDIRECT_REF:
+ case ARRAY_REF:
+ /* This used to be 0, but our current restricted model
+ allows this to be 1. We'll never get arrays this way. */
+ return 1;
+
+ default:
+ my_friendly_abort (42);
+ }
+
+ /* Catch-all case is that TO/FROM is not safe. */
+ return 0;
+}
+
+/* Function to build a static GC entry for DECL. TYPE is DECL's type.
+
+ For objects of type `class *', this is just an entry in the
+ static vector __PTR_LIST__.
+
+ For objects of type `class[]', this requires building an entry
+ in the static vector __ARR_LIST__.
+
+ For aggregates, this records all fields of type `class *'
+ and `class[]' in the respective lists above. */
+void
+build_static_gc_entry (decl, type)
+ tree decl;
+ tree type;
+{
+ /* Now, figure out what sort of entry to build. */
+ if (TREE_CODE (type) == POINTER_TYPE
+ || TREE_CODE (type) == REFERENCE_TYPE)
+ assemble_gc_entry (IDENTIFIER_POINTER (DECL_NAME (decl)));
+ else if (TREE_CODE (type) == RECORD_TYPE)
+ {
+ tree ref = get_temp_name (build_reference_type (type), 1);
+ DECL_INITIAL (ref) = build1 (ADDR_EXPR, TREE_TYPE (ref), decl);
+ TREE_CONSTANT (DECL_INITIAL (ref)) = 1;
+ finish_decl (ref, DECL_INITIAL (ref), 0, 0);
+ }
+ else
+ {
+ /* Not yet implemented.
+
+ Cons up a static variable that holds address and length info
+ and add that to ___ARR_LIST__. */
+ my_friendly_abort (43);
+ }
+}
+
+/* Protect FROM from the GC, assuming FROM is going to be
+ stored into TO. We handle three cases for TO here:
+
+ case 1: TO is a stack variable.
+ case 2: TO is zero (which means it is a parameter).
+ case 3: TO is a return value. */
+
+tree
+protect_value_from_gc (to, from)
+ tree to, from;
+{
+ if (to == 0)
+ {
+ tree cleanup;
+
+ to = get_temp_regvar (TREE_TYPE (from), from);
+
+ /* Convert from integer to list form since we'll use it twice. */
+ DECL_GC_OFFSET (to) = build_tree_list (NULL_TREE, DECL_GC_OFFSET (to));
+ cleanup = build_function_call (gc_unprotect_fndecl,
+ DECL_GC_OFFSET (to));
+
+ if (! expand_decl_cleanup (to, cleanup))
+ {
+ compiler_error ("cannot unprotect parameter in this scope");
+ return error_mark_node;
+ }
+ }
+
+ /* Should never need to protect a value that's headed for static storage. */
+ if (TREE_STATIC (to))
+ my_friendly_abort (44);
+
+ switch (TREE_CODE (to))
+ {
+ case COMPONENT_REF:
+ case INDIRECT_REF:
+ return protect_value_from_gc (TREE_OPERAND (to, 0), from);
+
+ case VAR_DECL:
+ case PARM_DECL:
+ {
+ tree rval;
+ if (DECL_GC_OFFSET (to) == NULL_TREE)
+ {
+ /* Because of a cast or a conversion, we might stick
+ a value into a variable that would not normally
+ have a GC entry. */
+ DECL_GC_OFFSET (to) = size_int (++current_function_obstack_index);
+ }
+
+ if (TREE_CODE (DECL_GC_OFFSET (to)) != TREE_LIST)
+ {
+ DECL_GC_OFFSET (to)
+ = build_tree_list (NULL_TREE, DECL_GC_OFFSET (to));
+ }
+
+ current_function_obstack_usage = 1;
+ rval = build_function_call (gc_protect_fndecl,
+ tree_cons (NULL_TREE, from,
+ DECL_GC_OFFSET (to)));
+ TREE_TYPE (rval) = TREE_TYPE (from);
+ return rval;
+ }
+ }
+
+ /* If we fall through the switch, assume we lost. */
+ my_friendly_abort (45);
+ /* NOTREACHED */
+ return NULL_TREE;
+}
+
+/* Given the expression EXP of type `class *', return the head
+ of the object pointed to by EXP. */
+tree
+build_headof (exp)
+ tree exp;
+{
+ tree type = TREE_TYPE (exp);
+ tree vptr, offset;
+
+ if (TREE_CODE (type) != POINTER_TYPE)
+ {
+ error ("`headof' applied to non-pointer type");
+ return error_mark_node;
+ }
+
+ if (flag_vtable_thunks)
+ abort();
+
+ vptr = build1 (INDIRECT_REF, TYPE_POINTER_TO (vtable_entry_type), exp);
+ offset = build_component_ref (build_array_ref (vptr, integer_one_node),
+ get_identifier (VTABLE_DELTA_NAME),
+ NULL_TREE, 0);
+ return build (PLUS_EXPR, class_star_type_node, exp,
+ convert (integer_type_node, offset));
+}
+
+/* Given the expression EXP of type `class *', return the
+ type descriptor for the object pointed to by EXP. */
+tree
+build_classof (exp)
+ tree exp;
+{
+ tree type = TREE_TYPE (exp);
+ tree vptr;
+ tree t_desc_entry;
+
+ if (TREE_CODE (type) != POINTER_TYPE)
+ {
+ error ("`classof' applied to non-pointer type");
+ return error_mark_node;
+ }
+
+ vptr = build1 (INDIRECT_REF, TYPE_POINTER_TO (vtable_entry_type), exp);
+ t_desc_entry = build_component_ref (build_array_ref (vptr, integer_one_node),
+ get_identifier (VTABLE_PFN_NAME),
+ NULL_TREE, 0);
+ TREE_TYPE (t_desc_entry) = TYPE_POINTER_TO (__t_desc_type_node);
+ return t_desc_entry;
+}
+
+/* Return the Type_info node associated with the expression EXP. If EXP is
+ a reference to a polymorphic class, return the dynamic type; otherwise
+ return the static type of the expression. */
+tree
+build_typeid (exp)
+ tree exp;
+{
+ tree type;
+
+ if (exp == error_mark_node)
+ return error_mark_node;
+
+ type = TREE_TYPE (exp);
+
+ /* if b is an instance of B, typeid(b) == typeid(B). Do this before
+ reference trickiness. */
+ if (TREE_CODE (exp) == VAR_DECL && TREE_CODE (type) == RECORD_TYPE)
+ return get_typeid (type);
+
+ /* Apply trivial conversion T -> T& for dereferenced ptrs. */
+ if (TREE_CODE (type) == RECORD_TYPE)
+ type = build_reference_type (type);
+
+ /* If exp is a reference to polymorphic type, get the real Type_info. */
+ if (TREE_CODE (type) == REFERENCE_TYPE && TYPE_VIRTUAL_P (TREE_TYPE (type)))
+ {
+ /* build reference to Type_info from vtable. */
+
+ sorry ("finding Type_info for an object");
+ return error_mark_node;
+ }
+
+ /* otherwise return the Type_info for the static type of the expr. */
+ return get_typeid (type);
+}
+
+/* Return the Type_info object for TYPE, creating it if necessary. */
+tree
+get_typeid (type)
+ tree type;
+{
+ if (type == error_mark_node)
+ return error_mark_node;
+
+ /* Is it useful (and/or correct) to have different typeids for `T &'
+ and `T'? */
+ if (TREE_CODE (type) == REFERENCE_TYPE)
+ type = TREE_TYPE (type);
+
+ /* build reference to static Type_info */
+#if 1
+ sorry ("finding Type_info for a type");
+ return error_mark_node;
+#else
+ register tree t = TYPE_TINFO (type);
+
+ if (t)
+ return t;
+
+ /* ... */
+
+#endif
+}
+
+/* Execute a dynamic cast, as described in section 5.2.6 of the 9/93 working
+ paper. */
+tree
+build_dynamic_cast (type, expr)
+ tree type, expr;
+{
+ enum tree_code tc = TREE_CODE (type);
+ tree exprtype = TREE_TYPE (expr);
+ enum tree_code ec = TREE_CODE (exprtype);
+ tree retval;
+
+ if (type == error_mark_node || expr == error_mark_node)
+ return error_mark_node;
+
+ switch (tc)
+ {
+ case POINTER_TYPE:
+ if (TREE_TYPE (type) == void_type_node)
+ break;
+ /* else fall through */
+ case REFERENCE_TYPE:
+ if (TREE_CODE (TREE_TYPE (type)) == RECORD_TYPE
+ && TYPE_SIZE (TREE_TYPE (type)) != NULL_TREE)
+ break;
+ /* else fall through */
+ default:
+ cp_error ("`%#T' is not a valid type argument for dynamic_cast", type);
+ error ("(must be either pointer or reference to defined class or void *)");
+ return error_mark_node;
+ }
+
+ /* Apply trivial conversion T -> T& for dereferenced ptrs. */
+ if (ec == RECORD_TYPE)
+ {
+ exprtype = build_reference_type (exprtype);
+ ec = REFERENCE_TYPE;
+ }
+
+ /* the TREE_CODE of exprtype must match that of type. */
+ if (ec != tc)
+ {
+ cp_error ("`%E' (of type `%#T') fails to be of %s type", expr, exprtype,
+ tc == POINTER_TYPE ? "pointer" : "reference");
+ return error_mark_node;
+ }
+
+ /* If *type is an unambiguous accessible base class of *exprtype,
+ convert statically. */
+ {
+ int distance;
+ tree path;
+
+ distance = get_base_distance (TREE_TYPE (type), TREE_TYPE (exprtype), 1,
+ &path);
+ if (distance >= 0)
+ return build_vbase_path (PLUS_EXPR, type, expr, path, 0);
+ }
+
+ /* Otherwise *exprtype must be a polymorphic class (have a vtbl). */
+ if (TYPE_VIRTUAL_P (TREE_TYPE (exprtype)))
+ {
+ /* if TYPE is `void *', return pointer to complete object. */
+ if (tc == POINTER_TYPE && TREE_TYPE (type) == void_type_node)
+ {
+ /* if b is an object, dynamic_cast<void *>(&b) == (void *)&b. */
+ if (TREE_CODE (expr) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (expr, 0)) == VAR_DECL
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == RECORD_TYPE)
+ return build1 (NOP_EXPR, type, expr);
+
+ sorry ("finding pointer to complete object");
+ return build1 (NOP_EXPR, type, expr);
+ }
+ else
+ {
+ tree retval;
+
+ /* If we got here, we can't convert statically. Therefore,
+ dynamic_cast<D&>(b) (b an object) cannot succeed. */
+ if (ec == REFERENCE_TYPE)
+ {
+ if (TREE_CODE (expr) == VAR_DECL
+ && TREE_CODE (TREE_TYPE (expr)) == RECORD_TYPE)
+ {
+ cp_warning ("dynamic_cast of `%#D' to `%#T' can never succeed",
+ expr, type);
+ /* cplus_expand_throw (Bad_cast_node); */
+ sorry ("throwing Bad_cast");
+ return error_mark_node;
+ }
+ }
+ /* Ditto for dynamic_cast<D*>(&b). */
+ else if (TREE_CODE (expr) == ADDR_EXPR)
+ {
+ tree op = TREE_OPERAND (expr, 0);
+ if (TREE_CODE (op) == VAR_DECL
+ && TREE_CODE (TREE_TYPE (op)) == RECORD_TYPE)
+ {
+ cp_warning ("dynamic_cast of `%E' to `%#T' can never succeed",
+ expr, type);
+ retval = build_int_2 (0, 0);
+ TREE_TYPE (retval) = type;
+ return retval;
+ }
+ }
+ /* Build run-time conversion. */
+ sorry ("run-time type conversion");
+ retval = build_int_2 (0, 0);
+ TREE_TYPE (retval) = type;
+ return retval;
+ }
+ }
+
+ cp_error ("cannot dynamic_cast `%E' (of type `%#T') to type `%#T'",
+ expr, exprtype, type);
+ return error_mark_node;
+}
+
+/* Build and initialize various sorts of descriptors. Every descriptor
+ node has a name associated with it (the name created by mangling).
+ For this reason, we use the identifier as our access to the __*_desc
+ nodes, instead of sticking them directly in the types. Otherwise we
+ would burden all built-in types (and pointer types) with slots that
+ we don't necessarily want to use.
+
+ For each descriptor we build, we build a variable that contains
+ the descriptor's information. When we need this info at runtime,
+ all we need is access to these variables.
+
+ Note: these constructors always return the address of the descriptor
+ info, since that is simplest for their mutual interaction. */
+
+static tree
+build_generic_desc (decl, elems)
+ tree decl;
+ tree elems;
+{
+ tree init = build (CONSTRUCTOR, TREE_TYPE (decl), NULL_TREE, elems);
+ TREE_CONSTANT (init) = 1;
+ TREE_STATIC (init) = 1;
+ TREE_READONLY (init) = 1;
+
+ DECL_INITIAL (decl) = init;
+ TREE_STATIC (decl) = 1;
+ layout_decl (decl, 0);
+ finish_decl (decl, init, 0, 0);
+
+ return IDENTIFIER_AS_DESC (DECL_NAME (decl));
+}
+
+/* Build an initializer for a __t_desc node. So that we can take advantage
+ of recursion, we accept NULL for TYPE.
+ DEFINITION is greater than zero iff we must define the type descriptor
+ (as opposed to merely referencing it). 1 means treat according to
+ #pragma interface/#pragma implementation rules. 2 means define as
+ global and public, no matter what. */
+tree
+build_t_desc (type, definition)
+ tree type;
+ int definition;
+{
+ tree tdecl;
+ tree tname, name_string;
+ tree elems, fields;
+ tree parents, vbases, offsets, ivars, methods, target_type;
+ int method_count = 0, field_count = 0;
+
+ if (type == NULL_TREE)
+ return NULL_TREE;
+
+ tname = build_t_desc_overload (type);
+ if (IDENTIFIER_AS_DESC (tname)
+ && (!definition || TREE_ASM_WRITTEN (IDENTIFIER_AS_DESC (tname))))
+ return IDENTIFIER_AS_DESC (tname);
+
+ tdecl = lookup_name (tname, 0);
+ if (tdecl == NULL_TREE)
+ {
+ tdecl = build_decl (VAR_DECL, tname, __t_desc_type_node);
+ DECL_EXTERNAL (tdecl) = 1;
+ TREE_PUBLIC (tdecl) = 1;
+ tdecl = pushdecl_top_level (tdecl);
+ }
+ /* If we previously defined it, return the defined result. */
+ else if (definition && DECL_INITIAL (tdecl))
+ return IDENTIFIER_AS_DESC (tname);
+
+ if (definition)
+ {
+ tree taggr = type;
+ /* Let T* and T& be written only when T is written (if T is an aggr).
+ We do this for const, but not for volatile, since volatile
+ is rare and const is not. */
+ if (!TYPE_VOLATILE (taggr)
+ && (TREE_CODE (taggr) == POINTER_TYPE
+ || TREE_CODE (taggr) == REFERENCE_TYPE)
+ && IS_AGGR_TYPE (TREE_TYPE (taggr)))
+ taggr = TREE_TYPE (taggr);
+
+ /* If we know that we don't need to write out this type's
+ vtable, then don't write out it's dossier. Somebody
+ else will take care of that. */
+ if (IS_AGGR_TYPE (taggr) && CLASSTYPE_VFIELD (taggr))
+ {
+ if (CLASSTYPE_VTABLE_NEEDS_WRITING (taggr))
+ {
+ TREE_PUBLIC (tdecl) = ! CLASSTYPE_INTERFACE_ONLY (taggr)
+ && CLASSTYPE_INTERFACE_KNOWN (taggr);
+ TREE_STATIC (tdecl) = 1;
+ DECL_EXTERNAL (tdecl) = 0;
+ }
+ else
+ {
+ if (write_virtuals != 0)
+ TREE_PUBLIC (tdecl) = 1;
+ }
+ }
+ else
+ {
+ DECL_EXTERNAL (tdecl) = 0;
+ TREE_STATIC (tdecl) = 1;
+ TREE_PUBLIC (tdecl) = (definition > 1);
+ }
+ }
+ SET_IDENTIFIER_AS_DESC (tname, build_unary_op (ADDR_EXPR, tdecl, 0));
+ if (!definition || DECL_EXTERNAL (tdecl))
+ {
+ /* That's it! */
+ finish_decl (tdecl, 0, 0, 0);
+ return IDENTIFIER_AS_DESC (tname);
+ }
+
+ /* Show that we are defining the t_desc for this type. */
+ DECL_INITIAL (tdecl) = error_mark_node;
+
+ parents = build_tree_list (NULL_TREE, integer_zero_node);
+ vbases = build_tree_list (NULL_TREE, integer_zero_node);
+ offsets = build_tree_list (NULL_TREE, integer_zero_node);
+ methods = NULL_TREE;
+ ivars = NULL_TREE;
+
+ if (TYPE_LANG_SPECIFIC (type))
+ {
+ int i = CLASSTYPE_N_BASECLASSES (type);
+ tree method_vec = CLASSTYPE_METHOD_VEC (type);
+ tree *meth, *end;
+ tree binfos = TYPE_BINFO_BASETYPES (type);
+ tree vb = CLASSTYPE_VBASECLASSES (type);
+
+ while (--i >= 0)
+ parents = tree_cons (NULL_TREE, build_t_desc (BINFO_TYPE (TREE_VEC_ELT (binfos, i)), 0), parents);
+
+ while (vb)
+ {
+ vbases = tree_cons (NULL_TREE, build_t_desc (BINFO_TYPE (vb), 0), vbases);
+ offsets = tree_cons (NULL_TREE, BINFO_OFFSET (vb), offsets);
+ vb = TREE_CHAIN (vb);
+ }
+
+ if (method_vec)
+ for (meth = TREE_VEC_END (method_vec),
+ end = &TREE_VEC_ELT (method_vec, 0); meth-- != end; )
+ if (*meth)
+ {
+ methods = tree_cons (NULL_TREE, build_m_desc (*meth), methods);
+ method_count++;
+ }
+ }
+
+ if (IS_AGGR_TYPE (type))
+ {
+ for (fields = TYPE_FIELDS (type); fields; fields = TREE_CHAIN (fields))
+ if (TREE_CODE (fields) == FIELD_DECL
+ || TREE_CODE (fields) == VAR_DECL)
+ {
+ ivars = tree_cons (NULL_TREE, build_i_desc (fields), ivars);
+ field_count++;
+ }
+ ivars = nreverse (ivars);
+ }
+
+ parents = finish_table (0, TYPE_POINTER_TO (__t_desc_type_node), parents, 0);
+ vbases = finish_table (0, TYPE_POINTER_TO (__t_desc_type_node), vbases, 0);
+ offsets = finish_table (0, integer_type_node, offsets, 0);
+ if (methods == NULL_TREE)
+ methods = null_pointer_node;
+ else
+ methods = build_unary_op (ADDR_EXPR,
+ finish_table (0, __m_desc_type_node, methods, 0),
+ 0);
+ if (ivars == NULL_TREE)
+ ivars = null_pointer_node;
+ else
+ ivars = build_unary_op (ADDR_EXPR,
+ finish_table (0, __i_desc_type_node, ivars, 0),
+ 0);
+ if (TREE_TYPE (type))
+ target_type = build_t_desc (TREE_TYPE (type), definition);
+ else
+ target_type = integer_zero_node;
+
+ name_string = combine_strings (build_string (IDENTIFIER_LENGTH (tname)+1, IDENTIFIER_POINTER (tname)));
+
+ elems = tree_cons (NULL_TREE, build_unary_op (ADDR_EXPR, name_string, 0),
+ tree_cons (NULL_TREE,
+ TYPE_SIZE(type)? size_in_bytes(type) : integer_zero_node,
+ /* really should use bitfield initialization here. */
+ tree_cons (NULL_TREE, integer_zero_node,
+ tree_cons (NULL_TREE, target_type,
+ tree_cons (NULL_TREE, build_int_2 (field_count, 2),
+ tree_cons (NULL_TREE, build_int_2 (method_count, 2),
+ tree_cons (NULL_TREE, ivars,
+ tree_cons (NULL_TREE, methods,
+ tree_cons (NULL_TREE, build_unary_op (ADDR_EXPR, parents, 0),
+ tree_cons (NULL_TREE, build_unary_op (ADDR_EXPR, vbases, 0),
+ build_tree_list (NULL_TREE, build_unary_op (ADDR_EXPR, offsets, 0))))))))))));
+ return build_generic_desc (tdecl, elems);
+}
+
+/* Build an initializer for a __i_desc node. */
+tree
+build_i_desc (decl)
+ tree decl;
+{
+ tree elems, name_string;
+ tree taggr;
+
+ name_string = DECL_NAME (decl);
+ name_string = combine_strings (build_string (IDENTIFIER_LENGTH (name_string)+1, IDENTIFIER_POINTER (name_string)));
+
+ /* Now decide whether this ivar should cause it's type to get
+ def'd or ref'd in this file. If the type we are looking at
+ has a proxy definition, we look at the proxy (i.e., a
+ `foo *' is equivalent to a `foo'). */
+ taggr = TREE_TYPE (decl);
+
+ if ((TREE_CODE (taggr) == POINTER_TYPE
+ || TREE_CODE (taggr) == REFERENCE_TYPE)
+ && TYPE_VOLATILE (taggr) == 0)
+ taggr = TREE_TYPE (taggr);
+
+ elems = tree_cons (NULL_TREE, build_unary_op (ADDR_EXPR, name_string, 0),
+ tree_cons (NULL_TREE, DECL_FIELD_BITPOS (decl),
+ build_tree_list (NULL_TREE, build_t_desc (TREE_TYPE (decl),
+ ! IS_AGGR_TYPE (taggr)))));
+ taggr = build (CONSTRUCTOR, __i_desc_type_node, NULL_TREE, elems);
+ TREE_CONSTANT (taggr) = 1;
+ TREE_STATIC (taggr) = 1;
+ TREE_READONLY (taggr) = 1;
+ return taggr;
+}
+
+/* Build an initializer for a __m_desc node. */
+tree
+build_m_desc (decl)
+ tree decl;
+{
+ tree taggr, elems, name_string;
+ tree parm_count, req_count, vindex, vcontext;
+ tree parms;
+ int p_count, r_count;
+ tree parm_types = NULL_TREE;
+
+ for (parms = TYPE_ARG_TYPES (TREE_TYPE (decl)), p_count = 0, r_count = 0;
+ parms != NULL_TREE; parms = TREE_CHAIN (parms), p_count++)
+ {
+ taggr = TREE_VALUE (parms);
+ if ((TREE_CODE (taggr) == POINTER_TYPE
+ || TREE_CODE (taggr) == REFERENCE_TYPE)
+ && TYPE_VOLATILE (taggr) == 0)
+ taggr = TREE_TYPE (taggr);
+
+ parm_types = tree_cons (NULL_TREE, build_t_desc (TREE_VALUE (parms),
+ ! IS_AGGR_TYPE (taggr)),
+ parm_types);
+ if (TREE_PURPOSE (parms) == NULL_TREE)
+ r_count++;
+ }
+
+ parm_types = finish_table (0, TYPE_POINTER_TO (__t_desc_type_node),
+ nreverse (parm_types), 0);
+ parm_count = build_int_2 (p_count, 0);
+ req_count = build_int_2 (r_count, 0);
+
+ if (DECL_VINDEX (decl))
+ vindex = DECL_VINDEX (decl);
+ else
+ vindex = integer_zero_node;
+ if (DECL_CONTEXT (decl)
+ && TREE_CODE_CLASS (TREE_CODE (DECL_CONTEXT (decl))) == 't')
+ vcontext = build_t_desc (DECL_CONTEXT (decl), 0);
+ else
+ vcontext = integer_zero_node;
+ name_string = DECL_NAME (decl);
+ if (name_string == NULL)
+ name_string = DECL_ASSEMBLER_NAME (decl);
+ name_string = combine_strings (build_string (IDENTIFIER_LENGTH (name_string)+1, IDENTIFIER_POINTER (name_string)));
+
+ /* Now decide whether the return type of this mvar
+ should cause it's type to get def'd or ref'd in this file.
+ If the type we are looking at has a proxy definition,
+ we look at the proxy (i.e., a `foo *' is equivalent to a `foo'). */
+ taggr = TREE_TYPE (TREE_TYPE (decl));
+
+ if ((TREE_CODE (taggr) == POINTER_TYPE
+ || TREE_CODE (taggr) == REFERENCE_TYPE)
+ && TYPE_VOLATILE (taggr) == 0)
+ taggr = TREE_TYPE (taggr);
+
+ elems = tree_cons (NULL_TREE, build_unary_op (ADDR_EXPR, name_string, 0),
+ tree_cons (NULL_TREE, vindex,
+ tree_cons (NULL_TREE, vcontext,
+ tree_cons (NULL_TREE, build_t_desc (TREE_TYPE (TREE_TYPE (decl)),
+ ! IS_AGGR_TYPE (taggr)),
+ tree_cons (NULL_TREE, build_c_cast (TYPE_POINTER_TO (default_function_type), build_unary_op (ADDR_EXPR, decl, 0)),
+ tree_cons (NULL_TREE, parm_count,
+ tree_cons (NULL_TREE, req_count,
+ build_tree_list (NULL_TREE, build_unary_op (ADDR_EXPR, parm_types, 0)))))))));
+
+ taggr = build (CONSTRUCTOR, __m_desc_type_node, NULL_TREE, elems);
+ TREE_CONSTANT (taggr) = 1;
+ TREE_STATIC (taggr) = 1;
+ TREE_READONLY (taggr) = 1;
+ return taggr;
+}
+
+/* Conditionally emit code to set up an unwind-protect for the
+ garbage collector. If this function doesn't do anything that involves
+ the garbage collector, then do nothing. Otherwise, call __gc_push
+ at the beginning and __gc_pop at the end.
+
+ NOTE! The __gc_pop function must operate transparently, since
+ it comes where the logical return label lies. This means that
+ at runtime *it* must preserve any return value registers. */
+
+void
+expand_gc_prologue_and_epilogue ()
+{
+ extern tree maybe_gc_cleanup;
+ struct rtx_def *last_parm_insn, *mark;
+ extern struct rtx_def *get_last_insn ();
+ extern struct rtx_def *get_first_nonparm_insn ();
+ extern struct rtx_def *previous_insn ();
+ tree action;
+
+ /* If we didn't need the obstack, don't cons any space. */
+ if (current_function_obstack_index == 0
+ || current_function_obstack_usage == 0)
+ return;
+
+ mark = get_last_insn ();
+ last_parm_insn = get_first_nonparm_insn ();
+ if (last_parm_insn == 0) last_parm_insn = mark;
+ else last_parm_insn = previous_insn (last_parm_insn);
+
+ action = build_function_call (gc_push_fndecl,
+ build_tree_list (NULL_TREE, size_int (++current_function_obstack_index)));
+ expand_expr_stmt (action);
+
+ reorder_insns (next_insn (mark), get_last_insn (), last_parm_insn);
+
+ /* This will be expanded as a cleanup. */
+ TREE_VALUE (maybe_gc_cleanup)
+ = build_function_call (gc_pop_fndecl, NULL_TREE);
+}
+
+/* Some day we'll use this function as a call-back and clean
+ up all the unnecessary gc dribble that we otherwise create. */
+void
+lang_expand_end_bindings (first, last)
+ struct rtx_def *first, *last;
+{
+}
+
+void
+init_gc_processing ()
+{
+ tree parmtypes = hash_tree_chain (class_star_type_node,
+ hash_tree_chain (integer_type_node, NULL_TREE));
+ gc_protect_fndecl = define_function ("__gc_protect",
+ build_function_type (class_star_type_node, parmtypes),
+ NOT_BUILT_IN, 0, 0);
+
+ parmtypes = hash_tree_chain (integer_type_node, NULL_TREE);
+ gc_unprotect_fndecl = define_function ("__gc_unprotect",
+ build_function_type (void_type_node, parmtypes),
+ NOT_BUILT_IN, 0, 0);
+
+ gc_push_fndecl = define_function ("__gc_push",
+ TREE_TYPE (gc_unprotect_fndecl),
+ NOT_BUILT_IN, 0, 0);
+
+ gc_pop_fndecl = define_function ("__gc_pop",
+ build_function_type (void_type_node,
+ void_list_node),
+ NOT_BUILT_IN, 0, 0);
+ gc_nonobject = build_int_2 (0x80000000, 0);
+ gc_visible = build_int_2 (0x40000000, 0);
+ gc_white = integer_zero_node;
+ gc_offwhite = build_int_2 (0x10000000, 0);
+ gc_grey = build_int_2 (0x20000000, 0);
+ gc_black = build_int_2 (0x30000000, 0);
+}
diff --git a/gnu/usr.bin/cc/cc1plus/hash.h b/gnu/usr.bin/cc/cc1plus/hash.h
new file mode 100644
index 0000000..8453c4b
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/hash.h
@@ -0,0 +1,197 @@
+/* C code produced by gperf version 2.5 (GNU C++ version) */
+/* Command-line: gperf -p -j1 -g -o -t -N is_reserved_word -k1,4,7,$ /deneb/blob/jason/g++/small/devo/gcc/cp/gxx.gperf */
+/* Command-line: gperf -p -j1 -g -o -t -N is_reserved_word -k1,4,$,7 gplus.gperf */
+struct resword { char *name; short token; enum rid rid;};
+
+#define TOTAL_KEYWORDS 86
+#define MIN_WORD_LENGTH 2
+#define MAX_WORD_LENGTH 16
+#define MIN_HASH_VALUE 4
+#define MAX_HASH_VALUE 171
+/* maximum key range = 168, duplicates = 0 */
+
+#ifdef __GNUC__
+inline
+#endif
+static unsigned int
+hash (str, len)
+ register char *str;
+ register int unsigned len;
+{
+ static unsigned char asso_values[] =
+ {
+ 172, 172, 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 172, 172, 172, 172, 172,
+ 172, 172, 172, 172, 172, 0, 172, 36, 1, 61,
+ 0, 0, 30, 44, 44, 35, 172, 7, 12, 53,
+ 40, 17, 6, 172, 28, 2, 4, 35, 31, 51,
+ 5, 7, 172, 172, 172, 172, 172, 172,
+ };
+ register int hval = len;
+
+ switch (hval)
+ {
+ default:
+ case 7:
+ hval += asso_values[str[6]];
+ case 6:
+ case 5:
+ case 4:
+ hval += asso_values[str[3]];
+ case 3:
+ case 2:
+ case 1:
+ hval += asso_values[str[0]];
+ }
+ return hval + asso_values[str[len - 1]];
+}
+
+#ifdef __GNUC__
+inline
+#endif
+struct resword *
+is_reserved_word (str, len)
+ register char *str;
+ register unsigned int len;
+{
+ static struct resword wordlist[] =
+ {
+ {"",}, {"",}, {"",}, {"",},
+ {"else", ELSE, NORID,},
+ {"",},
+ {"delete", DELETE, NORID,},
+ {"double", TYPESPEC, RID_DOUBLE,},
+ {"true", CXX_TRUE, NORID,},
+ {"__asm__", GCC_ASM_KEYWORD, NORID},
+ {"typeid", TYPEID, NORID,},
+ {"",},
+ {"this", THIS, NORID,},
+ {"",},
+ {"try", TRY, NORID,},
+ {"",}, {"",}, {"",}, {"",},
+ {"do", DO, NORID,},
+ {"",},
+ {"static_cast", STATIC_CAST, NORID,},
+ {"template", TEMPLATE, RID_TEMPLATE,},
+ {"protected", VISSPEC, RID_PROTECTED,},
+ {"",},
+ {"__classof__", CLASSOF, NORID},
+ {"",},
+ {"__headof__", HEADOF, NORID},
+ {"",},
+ {"bool", TYPESPEC, RID_BOOL,},
+ {"__const__", TYPE_QUAL, RID_CONST},
+ {"__volatile", TYPE_QUAL, RID_VOLATILE},
+ {"__const", TYPE_QUAL, RID_CONST},
+ {"__volatile__", TYPE_QUAL, RID_VOLATILE},
+ {"__typeof__", TYPEOF, NORID},
+ {"void", TYPESPEC, RID_VOID,},
+ {"friend", SCSPEC, RID_FRIEND,},
+ {"false", CXX_FALSE, NORID,},
+ {"sizeof", SIZEOF, NORID,},
+ {"short", TYPESPEC, RID_SHORT,},
+ {"typeof", TYPEOF, NORID,},
+ {"",},
+ {"int", TYPESPEC, RID_INT,},
+ {"__signed", TYPESPEC, RID_SIGNED},
+ {"private", VISSPEC, RID_PRIVATE,},
+ {"__signed__", TYPESPEC, RID_SIGNED},
+ {"extern", SCSPEC, RID_EXTERN,},
+ {"struct", AGGR, RID_RECORD,},
+ {"signed", TYPESPEC, RID_SIGNED,},
+ {"break", BREAK, NORID,},
+ {"__attribute", ATTRIBUTE, NORID},
+ {"default", DEFAULT, NORID,},
+ {"__attribute__", ATTRIBUTE, NORID},
+ {"__classof", CLASSOF, NORID},
+ {"sigof", SIGOF, NORID /* Extension */,},
+ {"__headof", HEADOF, NORID},
+ {"switch", SWITCH, NORID,},
+ {"__label__", LABEL, NORID},
+ {"__extension__", EXTENSION, NORID},
+ {"",},
+ {"__asm", GCC_ASM_KEYWORD, NORID},
+ {"for", FOR, NORID,},
+ {"__typeof", TYPEOF, NORID},
+ {"__alignof__", ALIGNOF, NORID},
+ {"",},
+ {"case", CASE, NORID,},
+ {"virtual", SCSPEC, RID_VIRTUAL,},
+ {"if", IF, NORID,},
+ {"while", WHILE, NORID,},
+ {"",},
+ {"class", AGGR, RID_CLASS,},
+ {"typedef", SCSPEC, RID_TYPEDEF,},
+ {"const", TYPE_QUAL, RID_CONST,},
+ {"static", SCSPEC, RID_STATIC,},
+ {"auto", SCSPEC, RID_AUTO,},
+ {"float", TYPESPEC, RID_FLOAT,},
+ {"inline", SCSPEC, RID_INLINE,},
+ {"throw", THROW, NORID,},
+ {"unsigned", TYPESPEC, RID_UNSIGNED,},
+ {"",},
+ {"headof", HEADOF, NORID,},
+ {"",},
+ {"goto", GOTO, NORID,},
+ {"",}, {"",},
+ {"public", VISSPEC, RID_PUBLIC,},
+ {"signature", AGGR, RID_SIGNATURE /* Extension */,},
+ {"volatile", TYPE_QUAL, RID_VOLATILE,},
+ {"__inline", SCSPEC, RID_INLINE},
+ {"overload", OVERLOAD, NORID,},
+ {"__inline__", SCSPEC, RID_INLINE},
+ {"__alignof", ALIGNOF, NORID},
+ {"asm", ASM_KEYWORD, NORID,},
+ {"",},
+ {"new", NEW, NORID,},
+ {"",},
+ {"mutable", SCSPEC, RID_MUTABLE,},
+ {"union", AGGR, RID_UNION,},
+ {"operator", OPERATOR, NORID,},
+ {"register", SCSPEC, RID_REGISTER,},
+ {"",}, {"",},
+ {"__wchar_t", TYPESPEC, RID_WCHAR /* Unique to ANSI C++ */,},
+ {"",},
+ {"long", TYPESPEC, RID_LONG,},
+ {"",}, {"",}, {"",},
+ {"continue", CONTINUE, NORID,},
+ {"return", RETURN, NORID,},
+ {"enum", ENUM, NORID,},
+ {"",}, {"",},
+ {"dynamic_cast", DYNAMIC_CAST, NORID,},
+ {"",}, {"",},
+ {"reinterpret_cast", REINTERPRET_CAST, NORID,},
+ {"",}, {"",}, {"",}, {"",},
+ {"char", TYPESPEC, RID_CHAR,},
+ {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
+ {"classof", CLASSOF, NORID,},
+ {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
+ {"const_cast", CONST_CAST, NORID,},
+ {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
+ {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
+ {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
+ {"",}, {"",}, {"",}, {"",}, {"",},
+ {"catch", CATCH, NORID,},
+ };
+
+ if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+ {
+ register int key = hash (str, len);
+
+ if (key <= MAX_HASH_VALUE && key >= 0)
+ {
+ register char *s = wordlist[key].name;
+
+ if (*s == *str && !strcmp (str + 1, s + 1))
+ return &wordlist[key];
+ }
+ }
+ return 0;
+}
diff --git a/gnu/usr.bin/cc/cc1plus/init.c b/gnu/usr.bin/cc/cc1plus/init.c
new file mode 100644
index 0000000..5e5d580
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/init.c
@@ -0,0 +1,4077 @@
+/* Handle initialization things in C++.
+ Copyright (C) 1987, 1989, 1992, 1993, 1994 Free Software Foundation, Inc.
+ Contributed by Michael Tiemann (tiemann@cygnus.com)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* High-level class interface. */
+
+#include "config.h"
+#include "tree.h"
+#include "rtl.h"
+#include "cp-tree.h"
+#include "flags.h"
+
+#undef NULL
+#define NULL 0
+
+/* In C++, structures with well-defined constructors are initialized by
+ those constructors, unasked. CURRENT_BASE_INIT_LIST
+ holds a list of stmts for a BASE_INIT term in the grammar.
+ This list has one element for each base class which must be
+ initialized. The list elements are [basename, init], with
+ type basetype. This allows the possibly anachronistic form
+ (assuming d : a, b, c) "d (int a) : c(a+5), b (a-4), a (a+3)"
+ where each successive term can be handed down the constructor
+ line. Perhaps this was not intended. */
+tree current_base_init_list, current_member_init_list;
+
+void emit_base_init ();
+void check_base_init ();
+static void expand_aggr_vbase_init ();
+void expand_member_init ();
+void expand_aggr_init ();
+
+static void expand_aggr_init_1 ();
+static void expand_recursive_init_1 ();
+static void expand_recursive_init ();
+static void expand_virtual_init PROTO((tree, tree));
+tree expand_vec_init ();
+
+static void add_friend (), add_friends ();
+
+/* Cache _builtin_new and _builtin_delete exprs. */
+static tree BIN, BID, BIVN, BIVD;
+
+/* Cache the identifier nodes for the two magic field of a new cookie. */
+static tree nc_nelts_field_id;
+#if 0
+static tree nc_ptr_2comp_field_id;
+#endif
+
+static tree minus_one;
+
+/* Set up local variable for this file. MUST BE CALLED AFTER
+ INIT_DECL_PROCESSING. */
+
+tree BI_header_type, BI_header_size;
+
+void init_init_processing ()
+{
+ tree fields[1];
+
+ /* Define implicit `operator new' and `operator delete' functions. */
+ BIN = default_conversion (get_first_fn (IDENTIFIER_GLOBAL_VALUE (ansi_opname[(int) NEW_EXPR])));
+ TREE_USED (TREE_OPERAND (BIN, 0)) = 0;
+ BID = default_conversion (get_first_fn (IDENTIFIER_GLOBAL_VALUE (ansi_opname[(int) DELETE_EXPR])));
+ TREE_USED (TREE_OPERAND (BID, 0)) = 0;
+ BIVN = default_conversion (get_first_fn (IDENTIFIER_GLOBAL_VALUE (ansi_opname[(int) VEC_NEW_EXPR])));
+ TREE_USED (TREE_OPERAND (BIVN, 0)) = 0;
+ BIVD = default_conversion (get_first_fn (IDENTIFIER_GLOBAL_VALUE (ansi_opname[(int) VEC_DELETE_EXPR])));
+ TREE_USED (TREE_OPERAND (BIVD, 0)) = 0;
+ minus_one = build_int_2 (-1, -1);
+
+ /* Define the structure that holds header information for
+ arrays allocated via operator new. */
+ BI_header_type = make_lang_type (RECORD_TYPE);
+ nc_nelts_field_id = get_identifier ("nelts");
+ fields[0] = build_lang_field_decl (FIELD_DECL, nc_nelts_field_id, sizetype);
+ finish_builtin_type (BI_header_type, "__new_cookie", fields,
+ 0, double_type_node);
+ BI_header_size = size_in_bytes (BI_header_type);
+}
+
+/* Subroutine of emit_base_init. For BINFO, initialize all the
+ virtual function table pointers, except those that come from
+ virtual base classes. Initialize binfo's vtable pointer, if
+ INIT_SELF is true. CAN_ELIDE is true when we know that all virtual
+ function table pointers in all bases have been initialized already,
+ probably because their constructors have just be run. ADDR is the
+ pointer to the object whos vtables we are going to initialize.
+
+ REAL_BINFO is usually the same as BINFO, except when addr is not of
+ pointer to the type of the real derived type that we want to
+ initialize for. This is the case when addr is a pointer to a sub
+ object of a complete object, and we only want to do part of the
+ complete object's initiailzation of vtable pointers. This is done
+ for all virtual table pointers in virtual base classes. REAL_BINFO
+ is used to find the BINFO_VTABLE that we initialize with. BINFO is
+ used for conversions of addr to subobjects.
+
+ BINFO_TYPE (real_binfo) must be BINFO_TYPE (binfo).
+
+ Relies upon binfo being inside TYPE_BINFO (TREE_TYPE (TREE_TYPE
+ (addr))). */
+void
+expand_direct_vtbls_init (real_binfo, binfo, init_self, can_elide, addr)
+ tree real_binfo, binfo, addr;
+ int init_self, can_elide;
+{
+ tree real_binfos = BINFO_BASETYPES (real_binfo);
+ tree binfos = BINFO_BASETYPES (binfo);
+ int i, n_baselinks = real_binfos ? TREE_VEC_LENGTH (real_binfos) : 0;
+
+ for (i = 0; i < n_baselinks; i++)
+ {
+ tree real_base_binfo = TREE_VEC_ELT (real_binfos, i);
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+ int is_not_base_vtable =
+ i != CLASSTYPE_VFIELD_PARENT (BINFO_TYPE (real_binfo));
+ if (! TREE_VIA_VIRTUAL (real_base_binfo))
+ expand_direct_vtbls_init (real_base_binfo, base_binfo,
+ is_not_base_vtable, can_elide, addr);
+ }
+#if 0
+ /* Before turning this on, make sure it is correct. */
+ if (can_elide && ! BINFO_MODIFIED (binfo))
+ return;
+#endif
+ /* Should we use something besides CLASSTYPE_VFIELDS? */
+ if (init_self && CLASSTYPE_VFIELDS (BINFO_TYPE (real_binfo)))
+ {
+ tree base_ptr = convert_pointer_to_real (binfo, addr);
+ expand_virtual_init (real_binfo, base_ptr);
+ }
+}
+
+/* 348 - 351 */
+/* Subroutine of emit_base_init. */
+static void
+perform_member_init (member, name, init, explicit)
+ tree member, name, init;
+ int explicit;
+{
+ tree decl;
+ tree type = TREE_TYPE (member);
+
+ if (TYPE_NEEDS_CONSTRUCTING (type)
+ || (init && TYPE_HAS_CONSTRUCTOR (type)))
+ {
+ /* Since `init' is already a TREE_LIST on the current_member_init_list,
+ only build it into one if we aren't already a list. */
+ if (init != NULL_TREE && TREE_CODE (init) != TREE_LIST)
+ init = build_tree_list (NULL_TREE, init);
+
+ decl = build_component_ref (C_C_D, name, 0, explicit);
+
+ if (explicit
+ && TREE_CODE (type) == ARRAY_TYPE
+ && init != NULL_TREE
+ && TREE_CHAIN (init) == NULL_TREE
+ && TREE_CODE (TREE_TYPE (TREE_VALUE (init))) == ARRAY_TYPE)
+ {
+ /* Initialization of one array from another. */
+ expand_vec_init (TREE_OPERAND (decl, 1), decl,
+ array_type_nelts (type), TREE_VALUE (init), 1);
+ }
+ else
+ expand_aggr_init (decl, init, 0);
+ }
+ else
+ {
+ if (init == NULL_TREE)
+ {
+ if (explicit)
+ {
+ cp_error ("incomplete initializer for member `%D' of class `%T' which has no constructor",
+ member, current_class_type);
+ init = error_mark_node;
+ }
+ /* member traversal: note it leaves init NULL */
+ else if (TREE_CODE (TREE_TYPE (member)) == REFERENCE_TYPE)
+ cp_pedwarn ("uninitialized reference member `%D'", member);
+ }
+ else if (TREE_CODE (init) == TREE_LIST)
+ {
+ /* There was an explicit member initialization. Do some
+ work in that case. */
+ if (TREE_CHAIN (init))
+ {
+ warning ("initializer list treated as compound expression");
+ init = build_compound_expr (init);
+ }
+ else
+ init = TREE_VALUE (init);
+ }
+
+ /* We only build this with a null init if we got it from the
+ current_member_init_list. */
+ if (init || explicit)
+ {
+ decl = build_component_ref (C_C_D, name, 0, explicit);
+ expand_expr_stmt (build_modify_expr (decl, INIT_EXPR, init));
+ }
+ }
+ if (flag_handle_exceptions && TYPE_NEEDS_DESTRUCTOR (type))
+ cp_warning ("caution, member `%D' may not be destroyed in the presense of an exception during construction", member);
+}
+
+/* Subroutine of emit_member_init. */
+static tree
+sort_member_init (t)
+ tree t;
+{
+ tree x, member, name, field, init;
+ tree init_list = NULL_TREE;
+ tree fields_to_unmark = NULL_TREE;
+ int found;
+
+ for (member = TYPE_FIELDS (t); member ; member = TREE_CHAIN (member))
+ {
+ found = 0;
+ for (x = current_member_init_list ; x ; x = TREE_CHAIN (x))
+ {
+ /* If we cleared this out, then pay no attention to it. */
+ if (TREE_PURPOSE (x) == NULL_TREE)
+ continue;
+ name = TREE_PURPOSE (x);
+
+#if 0
+ field = (TREE_CODE (name) == COMPONENT_REF
+ ? TREE_OPERAND (name, 1) : IDENTIFIER_CLASS_VALUE (name));
+#else
+ /* Let's find out when this happens. */
+ my_friendly_assert (TREE_CODE (name) != COMPONENT_REF, 348);
+ field = IDENTIFIER_CLASS_VALUE (name);
+#endif
+
+ /* If one member shadows another, get the outermost one. */
+ if (TREE_CODE (field) == TREE_LIST)
+ field = TREE_VALUE (field);
+
+ if (field == member)
+ {
+ /* See if we already found an initializer for this field. */
+ if (found)
+ {
+ if (DECL_NAME (field))
+ cp_error ("multiple initializations given for member `%D'",
+ field);
+ continue;
+ }
+
+ init_list = chainon (init_list,
+ build_tree_list (name, TREE_VALUE (x)));
+ /* Make sure we won't try to work on this init again. */
+ TREE_PURPOSE (x) = NULL_TREE;
+ found = 1;
+ break;
+ }
+ }
+
+ /* If we didn't find MEMBER in the list, create a dummy entry
+ so the two lists (INIT_LIST and the list of members) will be
+ symmetrical. */
+ if (! found)
+ init_list = chainon (init_list, build_tree_list (NULL_TREE, NULL_TREE));
+ }
+
+ for (x = current_member_init_list ; x ; x = TREE_CHAIN (x))
+ {
+ if (TREE_PURPOSE (x))
+ {
+ name = TREE_PURPOSE (x);
+ init = TREE_VALUE (x);
+ /* XXX: this may need the COMPONENT_REF operand 0 check if
+ it turns out we actually get them. */
+ field = IDENTIFIER_CLASS_VALUE (name);
+
+ /* If one member shadows another, get the outermost one. */
+ if (TREE_CODE (field) == TREE_LIST)
+ {
+ field = TREE_VALUE (field);
+ if (decl_type_context (field) != current_class_type)
+ cp_error ("field `%D' not in immediate context", field);
+ }
+
+#if 0
+ /* It turns out if you have an anonymous union in the
+ class, a member from it can end up not being on the
+ list of fields (rather, the type is), and therefore
+ won't be seen by the for loop above. */
+
+ /* The code in this for loop is derived from a general loop
+ which had this check in it. Theoretically, we've hit
+ every initialization for the list of members in T, so
+ we shouldn't have anything but these left in this list. */
+ my_friendly_assert (DECL_FIELD_CONTEXT (field) != t, 351);
+#endif
+
+ if (TREE_HAS_CONSTRUCTOR (field))
+ {
+ if (DECL_NAME (field))
+ error ("multiple initializations given for member `%s'",
+ IDENTIFIER_POINTER (DECL_NAME (field)));
+ continue;
+ }
+
+ TREE_HAS_CONSTRUCTOR (field) = 1;
+ fields_to_unmark = tree_cons (NULL_TREE, field, fields_to_unmark);
+
+ perform_member_init (field, name, init, 1);
+ TREE_PURPOSE (x) = NULL_TREE;
+ }
+ }
+
+ /* Unmark fields which are initialized for the base class. */
+ while (fields_to_unmark)
+ {
+ TREE_HAS_CONSTRUCTOR (TREE_VALUE (fields_to_unmark)) = 0;
+ /* XXX is this a memory leak? */
+ fields_to_unmark = TREE_CHAIN (fields_to_unmark);
+ }
+
+ return init_list;
+}
+
+/* Perform whatever initializations have yet to be done on the base
+ class of the class variable. These actions are in the global
+ variable CURRENT_BASE_INIT_LIST. Such an action could be
+ NULL_TREE, meaning that the user has explicitly called the base
+ class constructor with no arguments.
+
+ If there is a need for a call to a constructor, we must surround
+ that call with a pushlevel/poplevel pair, since we are technically
+ at the PARM level of scope.
+
+ Argument IMMEDIATELY, if zero, forces a new sequence to be
+ generated to contain these new insns, so it can be emitted later.
+ This sequence is saved in the global variable BASE_INIT_INSNS.
+ Otherwise, the insns are emitted into the current sequence.
+
+ Note that emit_base_init does *not* initialize virtual base
+ classes. That is done specially, elsewhere. */
+
+void
+emit_base_init (t, immediately)
+ tree t;
+ int immediately;
+{
+ extern tree in_charge_identifier;
+
+ tree member, vbases;
+ tree init_list;
+ int pass, start;
+ tree t_binfo = TYPE_BINFO (t);
+ tree binfos = BINFO_BASETYPES (t_binfo);
+ int i, n_baseclasses = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+ int have_init_list = 0, from_init_list;
+
+ if (! immediately)
+ {
+ do_pending_stack_adjust ();
+ start_sequence ();
+ }
+
+ if (write_symbols == NO_DEBUG)
+ /* As a matter of principle, `start_sequence' should do this. */
+ emit_note (0, -1);
+ else
+ /* Always emit a line number note so we can step into constructors. */
+ emit_line_note_force (DECL_SOURCE_FILE (current_function_decl),
+ DECL_SOURCE_LINE (current_function_decl));
+
+ start = ! TYPE_USES_VIRTUAL_BASECLASSES (t);
+ for (pass = start; pass < 2; pass++)
+ {
+ tree vbase_init_list = NULL_TREE;
+
+ for (init_list = current_base_init_list; init_list;
+ init_list = TREE_CHAIN (init_list))
+ {
+ tree basename = TREE_PURPOSE (init_list);
+ tree binfo;
+ tree init = TREE_VALUE (init_list);
+
+ if (basename == NULL_TREE)
+ {
+ /* Initializer for single base class. Must not
+ use multiple inheritance or this is ambiguous. */
+ switch (n_baseclasses)
+ {
+ case 0:
+ error ("type `%s' does not have a base class to initialize",
+ IDENTIFIER_POINTER (current_class_name));
+ return;
+ case 1:
+ break;
+ default:
+ error ("unnamed initializer ambiguous for type `%s' which uses multiple inheritance", IDENTIFIER_POINTER (current_class_name));
+ return;
+ }
+ binfo = TREE_VEC_ELT (binfos, 0);
+ }
+ else if (is_aggr_typedef (basename, 1))
+ {
+ binfo = binfo_or_else (IDENTIFIER_TYPE_VALUE (basename), t);
+ if (binfo == NULL_TREE)
+ continue;
+
+ /* Virtual base classes are special cases. Their initializers
+ are recorded with this constructor, and they are used when
+ this constructor is the top-level constructor called. */
+ if (! TREE_VIA_VIRTUAL (binfo))
+ {
+ /* Otherwise, if it is not an immediate base class, complain. */
+ for (i = n_baseclasses-1; i >= 0; i--)
+ if (BINFO_TYPE (binfo) == BINFO_TYPE (TREE_VEC_ELT (binfos, i)))
+ break;
+ if (i < 0)
+ {
+ error ("type `%s' is not an immediate base class of type `%s'",
+ IDENTIFIER_POINTER (basename),
+ IDENTIFIER_POINTER (current_class_name));
+ continue;
+ }
+ }
+ }
+ else
+ continue;
+
+ /* The base initialization list goes up to the first
+ base class which can actually use it. */
+
+ if (pass == start)
+ {
+ char *msgp = (! TYPE_HAS_CONSTRUCTOR (BINFO_TYPE (binfo)))
+ ? "cannot pass initialization up to class `%s'" : 0;
+
+ while (! TYPE_HAS_CONSTRUCTOR (BINFO_TYPE (binfo))
+ && BINFO_BASETYPES (binfo) != NULL_TREE
+ && TREE_VEC_LENGTH (BINFO_BASETYPES (binfo)) == 1)
+ {
+ /* ?? This should be fixed in RENO by forcing
+ default constructors to exist. */
+ SET_BINFO_BASEINIT_MARKED (binfo);
+ binfo = BINFO_BASETYPE (binfo, 0);
+ }
+
+ /* We used to give an error if this wasn't true, saying that
+ there's no constructor for the initialization of basename.
+ This turned out to be incorrect---it should use the
+ default constructor, since a user could try to initialize
+ the class in a derived class's base initializer list. */
+ if (TYPE_HAS_CONSTRUCTOR (BINFO_TYPE (binfo)))
+ {
+ if (msgp)
+ {
+ if (pedantic)
+ error_with_aggr_type (binfo, msgp);
+ else
+ msgp = NULL;
+ }
+ }
+
+ if (BINFO_BASEINIT_MARKED (binfo))
+ {
+ msgp = "class `%s' initializer already specified";
+ error (msgp, IDENTIFIER_POINTER (basename));
+ }
+
+ if (msgp)
+ continue;
+
+ SET_BINFO_BASEINIT_MARKED (binfo);
+ if (TREE_VIA_VIRTUAL (binfo))
+ {
+ vbase_init_list = tree_cons (init, BINFO_TYPE (binfo),
+ vbase_init_list);
+ continue;
+ }
+ if (pass == 0)
+ continue;
+ }
+ else if (TREE_VIA_VIRTUAL (binfo))
+ continue;
+
+ member = convert_pointer_to (binfo, current_class_decl);
+ expand_aggr_init_1 (t_binfo, 0,
+ build_indirect_ref (member, NULL_PTR), init,
+ BINFO_OFFSET_ZEROP (binfo), LOOKUP_COMPLAIN);
+ }
+
+ if (pass == 0)
+ {
+ tree first_arg = TREE_CHAIN (DECL_ARGUMENTS (current_function_decl));
+ tree vbases;
+
+ if (DECL_NAME (current_function_decl) == NULL_TREE
+ && TREE_CHAIN (first_arg) != NULL_TREE)
+ {
+ /* If there are virtual baseclasses without initialization
+ specified, and this is a default X(X&) constructor,
+ build the initialization list so that each virtual baseclass
+ of the new object is initialized from the virtual baseclass
+ of the incoming arg. */
+ tree init_arg = build_unary_op (ADDR_EXPR, TREE_CHAIN (first_arg), 0);
+ for (vbases = CLASSTYPE_VBASECLASSES (t);
+ vbases; vbases = TREE_CHAIN (vbases))
+ {
+ if (BINFO_BASEINIT_MARKED (vbases) == 0)
+ {
+ member = convert_pointer_to (vbases, init_arg);
+ if (member == init_arg)
+ member = TREE_CHAIN (first_arg);
+ else
+ TREE_TYPE (member) = build_reference_type (BINFO_TYPE (vbases));
+ vbase_init_list = tree_cons (convert_from_reference (member),
+ vbases, vbase_init_list);
+ SET_BINFO_BASEINIT_MARKED (vbases);
+ }
+ }
+ }
+ expand_start_cond (first_arg, 0);
+ expand_aggr_vbase_init (t_binfo, C_C_D, current_class_decl,
+ vbase_init_list);
+ expand_end_cond ();
+ }
+ }
+ current_base_init_list = NULL_TREE;
+
+ /* Now, perform default initialization of all base classes which
+ have not yet been initialized, and unmark baseclasses which
+ have been initialized. */
+ for (i = 0; i < n_baseclasses; i++)
+ {
+ tree base = current_class_decl;
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+
+ if (TYPE_NEEDS_CONSTRUCTING (BINFO_TYPE (base_binfo)))
+ {
+ if (! TREE_VIA_VIRTUAL (base_binfo)
+ && ! BINFO_BASEINIT_MARKED (base_binfo))
+ {
+ tree ref;
+
+ if (BINFO_OFFSET_ZEROP (base_binfo))
+ base = build1 (NOP_EXPR,
+ TYPE_POINTER_TO (BINFO_TYPE (base_binfo)),
+ current_class_decl);
+ else
+ base = build (PLUS_EXPR,
+ TYPE_POINTER_TO (BINFO_TYPE (base_binfo)),
+ current_class_decl, BINFO_OFFSET (base_binfo));
+
+ ref = build_indirect_ref (base, NULL_PTR);
+ expand_aggr_init_1 (t_binfo, 0, ref, NULL_TREE,
+ BINFO_OFFSET_ZEROP (base_binfo),
+ LOOKUP_COMPLAIN);
+ }
+ }
+ CLEAR_BINFO_BASEINIT_MARKED (base_binfo);
+
+ if (! TYPE_USES_VIRTUAL_BASECLASSES (t))
+ {
+ while (! TYPE_HAS_CONSTRUCTOR (BINFO_TYPE (base_binfo))
+ && BINFO_BASETYPES (base_binfo) != NULL_TREE
+ && TREE_VEC_LENGTH (BINFO_BASETYPES (base_binfo)) == 1)
+ {
+ /* ?? This should be fixed in RENO by forcing
+ default constructors to exist. It is needed for symmetry
+ with code above. */
+ base_binfo = BINFO_BASETYPE (base_binfo, 0);
+ CLEAR_BINFO_BASEINIT_MARKED (base_binfo);
+ }
+ }
+ }
+
+ /* Initialize all the virtual function table fields that
+ do come from virtual base classes. */
+ if (TYPE_USES_VIRTUAL_BASECLASSES (t))
+ expand_indirect_vtbls_init (t_binfo, C_C_D, current_class_decl, 0);
+ for (vbases = CLASSTYPE_VBASECLASSES (t); vbases; vbases = TREE_CHAIN (vbases))
+ CLEAR_BINFO_BASEINIT_MARKED (vbases);
+
+ /* Initialize all the virtual function table fields that
+ do not come from virtual base classes. */
+ expand_direct_vtbls_init (t_binfo, t_binfo, 1, 1, current_class_decl);
+
+ if (current_member_init_list)
+ {
+ init_list = sort_member_init (t);
+ have_init_list = 1;
+ }
+
+ for (member = TYPE_FIELDS (t); member; member = TREE_CHAIN (member))
+ {
+ tree init, name;
+ from_init_list = 0;
+
+ /* See if we had a user-specified member initialization. */
+ if (have_init_list)
+ {
+ if (TREE_PURPOSE (init_list))
+ {
+ name = TREE_PURPOSE (init_list);
+ init = TREE_VALUE (init_list);
+ from_init_list = 1;
+
+ if (TREE_STATIC (member))
+ {
+ error_with_aggr_type (DECL_FIELD_CONTEXT (member),
+ "field `%s::%s' is static; only point of initialization is its declaration",
+ IDENTIFIER_POINTER (TREE_PURPOSE (init_list)));
+ continue;
+ }
+
+ /* Also see if it's ever a COMPONENT_REF here. If it is, we
+ need to do `expand_assignment (name, init, 0, 0);' and
+ a continue. */
+ my_friendly_assert (TREE_CODE (name) != COMPONENT_REF, 349);
+ }
+
+ init_list = TREE_CHAIN (init_list);
+ }
+
+ if (! from_init_list)
+ {
+ /* member could be, for example, a CONST_DECL for an enumerated
+ tag; we don't want to try to initialize that, since it already
+ has a value. */
+ if (TREE_CODE (member) != FIELD_DECL || !DECL_NAME (member))
+ continue;
+
+ name = DECL_NAME (member);
+ init = DECL_INITIAL (member);
+ }
+
+ perform_member_init (member, name, init, from_init_list);
+ }
+
+ current_member_init_list = NULL_TREE;
+
+ /* It is possible for the initializers to need cleanups.
+ Expand those cleanups now that all the initialization
+ has been done. */
+ expand_cleanups_to (NULL_TREE);
+
+ if (! immediately)
+ {
+ extern rtx base_init_insns;
+
+ do_pending_stack_adjust ();
+ my_friendly_assert (base_init_insns == 0, 207);
+ base_init_insns = get_insns ();
+ end_sequence ();
+ }
+
+ /* All the implicit try blocks we built up will be zapped
+ when we come to a real binding contour boundary. */
+}
+
+/* Check that all fields are properly initialized after
+ an assignment to `this'. */
+void
+check_base_init (t)
+ tree t;
+{
+ tree member;
+ for (member = TYPE_FIELDS (t); member; member = TREE_CHAIN (member))
+ if (DECL_NAME (member) && TREE_USED (member))
+ cp_error ("field `%D' used before initialized (after assignment to `this')",
+ member);
+}
+
+/* This code sets up the virtual function tables appropriate for
+ the pointer DECL. It is a one-ply initialization.
+
+ BINFO is the exact type that DECL is supposed to be. In
+ multiple inheritance, this might mean "C's A" if C : A, B. */
+static void
+expand_virtual_init (binfo, decl)
+ tree binfo, decl;
+{
+ tree type = BINFO_TYPE (binfo);
+ tree vtbl, vtbl_ptr;
+ tree vtype, vtype_binfo;
+
+ /* This code is crusty. Should be simple, like:
+ vtbl = BINFO_VTABLE (binfo);
+ */
+ vtype = DECL_CONTEXT (CLASSTYPE_VFIELD (type));
+ vtype_binfo = get_binfo (vtype, TREE_TYPE (TREE_TYPE (decl)), 0);
+ vtbl = BINFO_VTABLE (binfo_value (DECL_FIELD_CONTEXT (CLASSTYPE_VFIELD (type)), binfo));
+ if (!flag_vtable_thunks)
+ assemble_external (vtbl);
+ TREE_USED (vtbl) = 1;
+ vtbl = build1 (ADDR_EXPR, TYPE_POINTER_TO (TREE_TYPE (vtbl)), vtbl);
+ decl = convert_pointer_to_real (vtype_binfo, decl);
+ vtbl_ptr = build_vfield_ref (build_indirect_ref (decl, NULL_PTR), vtype);
+ if (vtbl_ptr == error_mark_node)
+ return;
+
+ /* Have to convert VTBL since array sizes may be different. */
+ vtbl = convert_force (TREE_TYPE (vtbl_ptr), vtbl);
+ expand_expr_stmt (build_modify_expr (vtbl_ptr, NOP_EXPR, vtbl));
+}
+
+/* Subroutine of `expand_aggr_vbase_init'.
+ BINFO is the binfo of the type that is being initialized.
+ INIT_LIST is the list of initializers for the virtual baseclass. */
+static void
+expand_aggr_vbase_init_1 (binfo, exp, addr, init_list)
+ tree binfo, exp, addr, init_list;
+{
+ tree init = value_member (BINFO_TYPE (binfo), init_list);
+ tree ref = build_indirect_ref (addr, NULL_PTR);
+ if (init)
+ init = TREE_PURPOSE (init);
+ /* Call constructors, but don't set up vtables. */
+ expand_aggr_init_1 (binfo, exp, ref, init, 0,
+ LOOKUP_COMPLAIN|LOOKUP_SPECULATIVELY);
+ CLEAR_BINFO_VBASE_INIT_MARKED (binfo);
+}
+
+/* Initialize this object's virtual base class pointers. This must be
+ done only at the top-level of the object being constructed.
+
+ INIT_LIST is list of initialization for constructor to perform. */
+static void
+expand_aggr_vbase_init (binfo, exp, addr, init_list)
+ tree binfo;
+ tree exp;
+ tree addr;
+ tree init_list;
+{
+ tree type = BINFO_TYPE (binfo);
+
+ if (TYPE_USES_VIRTUAL_BASECLASSES (type))
+ {
+ tree result = init_vbase_pointers (type, addr);
+ tree vbases;
+
+ if (result)
+ expand_expr_stmt (build_compound_expr (result));
+
+ /* Mark everything as having an initializer
+ (either explicit or default). */
+ for (vbases = CLASSTYPE_VBASECLASSES (type);
+ vbases; vbases = TREE_CHAIN (vbases))
+ SET_BINFO_VBASE_INIT_MARKED (vbases);
+
+ /* First, initialize baseclasses which could be baseclasses
+ for other virtual baseclasses. */
+ for (vbases = CLASSTYPE_VBASECLASSES (type);
+ vbases; vbases = TREE_CHAIN (vbases))
+ /* Don't initialize twice. */
+ if (BINFO_VBASE_INIT_MARKED (vbases))
+ {
+ tree tmp = result;
+
+ while (BINFO_TYPE (vbases) != BINFO_TYPE (TREE_PURPOSE (tmp)))
+ tmp = TREE_CHAIN (tmp);
+ expand_aggr_vbase_init_1 (vbases, exp,
+ TREE_OPERAND (TREE_VALUE (tmp), 0),
+ init_list);
+ }
+
+ /* Now initialize the baseclasses which don't have virtual baseclasses. */
+ for (; result; result = TREE_CHAIN (result))
+ /* Don't initialize twice. */
+ if (BINFO_VBASE_INIT_MARKED (TREE_PURPOSE (result)))
+ {
+ my_friendly_abort (47);
+ expand_aggr_vbase_init_1 (TREE_PURPOSE (result), exp,
+ TREE_OPERAND (TREE_VALUE (result), 0),
+ init_list);
+ }
+ }
+}
+
+/* Subroutine to perform parser actions for member initialization.
+ S_ID is the scoped identifier.
+ NAME is the name of the member.
+ INIT is the initializer, or `void_type_node' if none. */
+void
+do_member_init (s_id, name, init)
+ tree s_id, name, init;
+{
+ tree binfo, base;
+
+ if (current_class_type == NULL_TREE
+ || ! is_aggr_typedef (s_id, 1))
+ return;
+ binfo = get_binfo (IDENTIFIER_TYPE_VALUE (s_id),
+ current_class_type, 1);
+ if (binfo == error_mark_node)
+ return;
+ if (binfo == 0)
+ {
+ error_not_base_type (IDENTIFIER_TYPE_VALUE (s_id), current_class_type);
+ return;
+ }
+
+ base = convert_pointer_to (binfo, current_class_decl);
+ expand_member_init (build_indirect_ref (base, NULL_PTR), name, init);
+}
+
+/* Function to give error message if member initialization specification
+ is erroneous. FIELD is the member we decided to initialize.
+ TYPE is the type for which the initialization is being performed.
+ FIELD must be a member of TYPE, or the base type from which FIELD
+ comes must not need a constructor.
+
+ MEMBER_NAME is the name of the member. */
+
+static int
+member_init_ok_or_else (field, type, member_name)
+ tree field;
+ tree type;
+ char *member_name;
+{
+ if (field == error_mark_node)
+ return 0;
+ if (field == NULL_TREE)
+ {
+ cp_error ("class `%T' does not have any field named `%s'", type,
+ member_name);
+ return 0;
+ }
+ if (DECL_CONTEXT (field) != type
+ && TYPE_NEEDS_CONSTRUCTING (DECL_CONTEXT (field)))
+ {
+ cp_error ("member `%D' comes from base class needing constructor",
+ field);
+ return 0;
+ }
+ return 1;
+}
+
+/* If NAME is a viable field name for the aggregate DECL,
+ and PARMS is a viable parameter list, then expand an _EXPR
+ which describes this initialization.
+
+ Note that we do not need to chase through the class's base classes
+ to look for NAME, because if it's in that list, it will be handled
+ by the constructor for that base class.
+
+ We do not yet have a fixed-point finder to instantiate types
+ being fed to overloaded constructors. If there is a unique
+ constructor, then argument types can be got from that one.
+
+ If INIT is non-NULL, then it the initialization should
+ be placed in `current_base_init_list', where it will be processed
+ by `emit_base_init'. */
+void
+expand_member_init (exp, name, init)
+ tree exp, name, init;
+{
+ extern tree ptr_type_node; /* should be in tree.h */
+
+ tree basetype = NULL_TREE, field;
+ tree parm;
+ tree rval, type;
+ tree actual_name;
+
+ if (exp == NULL_TREE)
+ return; /* complain about this later */
+
+ type = TYPE_MAIN_VARIANT (TREE_TYPE (exp));
+
+ if (name == NULL_TREE && IS_AGGR_TYPE (type))
+ switch (CLASSTYPE_N_BASECLASSES (type))
+ {
+ case 0:
+ error ("base class initializer specified, but no base class to initialize");
+ return;
+ case 1:
+ basetype = TYPE_BINFO_BASETYPE (type, 0);
+ break;
+ default:
+ error ("initializer for unnamed base class ambiguous");
+ cp_error ("(type `%T' uses multiple inheritance)", type);
+ return;
+ }
+
+ if (init)
+ {
+ /* The grammar should not allow fields which have names
+ that are TYPENAMEs. Therefore, if the field has
+ a non-NULL TREE_TYPE, we may assume that this is an
+ attempt to initialize a base class member of the current
+ type. Otherwise, it is an attempt to initialize a
+ member field. */
+
+ if (init == void_type_node)
+ init = NULL_TREE;
+
+ if (name == NULL_TREE || IDENTIFIER_HAS_TYPE_VALUE (name))
+ {
+ tree base_init;
+
+ if (name == NULL_TREE)
+ {
+/*
+ if (basetype)
+ name = TYPE_IDENTIFIER (basetype);
+ else
+ {
+ error ("no base class to initialize");
+ return;
+ }
+*/
+ }
+ else
+ {
+ basetype = IDENTIFIER_TYPE_VALUE (name);
+ if (basetype != type
+ && ! binfo_member (basetype, TYPE_BINFO (type))
+ && ! binfo_member (basetype, CLASSTYPE_VBASECLASSES (type)))
+ {
+ if (IDENTIFIER_CLASS_VALUE (name))
+ goto try_member;
+ if (TYPE_USES_VIRTUAL_BASECLASSES (type))
+ error ("type `%s' is not an immediate or virtual basetype for `%s'",
+ IDENTIFIER_POINTER (name),
+ TYPE_NAME_STRING (type));
+ else
+ error ("type `%s' is not an immediate basetype for `%s'",
+ IDENTIFIER_POINTER (name),
+ TYPE_NAME_STRING (type));
+ return;
+ }
+ }
+
+ if (purpose_member (name, current_base_init_list))
+ {
+ error ("base class `%s' already initialized",
+ IDENTIFIER_POINTER (name));
+ return;
+ }
+
+ base_init = build_tree_list (name, init);
+ TREE_TYPE (base_init) = basetype;
+ current_base_init_list = chainon (current_base_init_list, base_init);
+ }
+ else
+ {
+ tree member_init;
+
+ try_member:
+ field = lookup_field (type, name, 1, 0);
+
+ if (! member_init_ok_or_else (field, type, IDENTIFIER_POINTER (name)))
+ return;
+
+ if (purpose_member (name, current_member_init_list))
+ {
+ error ("field `%s' already initialized", IDENTIFIER_POINTER (name));
+ return;
+ }
+
+ member_init = build_tree_list (name, init);
+ TREE_TYPE (member_init) = TREE_TYPE (field);
+ current_member_init_list = chainon (current_member_init_list, member_init);
+ }
+ return;
+ }
+ else if (name == NULL_TREE)
+ {
+ compiler_error ("expand_member_init: name == NULL_TREE");
+ return;
+ }
+
+ basetype = type;
+ field = lookup_field (basetype, name, 0, 0);
+
+ if (! member_init_ok_or_else (field, basetype, IDENTIFIER_POINTER (name)))
+ return;
+
+ /* now see if there is a constructor for this type
+ which will take these args. */
+
+ if (TYPE_HAS_CONSTRUCTOR (TREE_TYPE (field)))
+ {
+ tree parmtypes, fndecl;
+
+ if (TREE_CODE (exp) == VAR_DECL || TREE_CODE (exp) == PARM_DECL)
+ {
+ /* just know that we've seen something for this node */
+ DECL_INITIAL (exp) = error_mark_node;
+ TREE_USED (exp) = 1;
+ }
+ type = TYPE_MAIN_VARIANT (TREE_TYPE (field));
+ actual_name = TYPE_IDENTIFIER (type);
+ parm = build_component_ref (exp, name, 0, 0);
+
+ /* Now get to the constructor. */
+ fndecl = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type), 0);
+ /* Get past destructor, if any. */
+ if (TYPE_HAS_DESTRUCTOR (type))
+ fndecl = DECL_CHAIN (fndecl);
+
+ if (fndecl)
+ my_friendly_assert (TREE_CODE (fndecl) == FUNCTION_DECL, 209);
+
+ /* If the field is unique, we can use the parameter
+ types to guide possible type instantiation. */
+ if (DECL_CHAIN (fndecl) == NULL_TREE)
+ {
+ /* There was a confusion here between
+ FIELD and FNDECL. The following code
+ should be correct, but abort is here
+ to make sure. */
+ my_friendly_abort (48);
+ parmtypes = FUNCTION_ARG_CHAIN (fndecl);
+ }
+ else
+ {
+ parmtypes = NULL_TREE;
+ fndecl = NULL_TREE;
+ }
+
+ init = convert_arguments (parm, parmtypes, NULL_TREE, fndecl, LOOKUP_NORMAL);
+ if (init == NULL_TREE || TREE_TYPE (init) != error_mark_node)
+ rval = build_method_call (NULL_TREE, actual_name, init, NULL_TREE, LOOKUP_NORMAL);
+ else
+ return;
+
+ if (rval != error_mark_node)
+ {
+ /* Now, fill in the first parm with our guy */
+ TREE_VALUE (TREE_OPERAND (rval, 1))
+ = build_unary_op (ADDR_EXPR, parm, 0);
+ TREE_TYPE (rval) = ptr_type_node;
+ TREE_SIDE_EFFECTS (rval) = 1;
+ }
+ }
+ else if (TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (field)))
+ {
+ parm = build_component_ref (exp, name, 0, 0);
+ expand_aggr_init (parm, NULL_TREE, 0);
+ rval = error_mark_node;
+ }
+
+ /* Now initialize the member. It does not have to
+ be of aggregate type to receive initialization. */
+ if (rval != error_mark_node)
+ expand_expr_stmt (rval);
+}
+
+/* This is like `expand_member_init', only it stores one aggregate
+ value into another.
+
+ INIT comes in two flavors: it is either a value which
+ is to be stored in EXP, or it is a parameter list
+ to go to a constructor, which will operate on EXP.
+ If `init' is a CONSTRUCTOR, then we emit a warning message,
+ explaining that such initializations are illegal.
+
+ ALIAS_THIS is nonzero iff we are initializing something which is
+ essentially an alias for C_C_D. In this case, the base constructor
+ may move it on us, and we must keep track of such deviations.
+
+ If INIT resolves to a CALL_EXPR which happens to return
+ something of the type we are looking for, then we know
+ that we can safely use that call to perform the
+ initialization.
+
+ The virtual function table pointer cannot be set up here, because
+ we do not really know its type.
+
+ Virtual baseclass pointers are also set up here.
+
+ This never calls operator=().
+
+ When initializing, nothing is CONST.
+
+ A default copy constructor may have to be used to perform the
+ initialization.
+
+ A constructor or a conversion operator may have to be used to
+ perform the initialization, but not both, as it would be ambiguous.
+ */
+
+void
+expand_aggr_init (exp, init, alias_this)
+ tree exp, init;
+ int alias_this;
+{
+ tree type = TREE_TYPE (exp);
+ int was_const = TREE_READONLY (exp);
+
+ if (init == error_mark_node)
+ return;
+
+ TREE_READONLY (exp) = 0;
+
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ /* Must arrange to initialize each element of EXP
+ from elements of INIT. */
+ int was_const_elts = TYPE_READONLY (TREE_TYPE (type));
+ tree itype = init ? TREE_TYPE (init) : NULL_TREE;
+ if (was_const_elts)
+ TREE_TYPE (exp) = TYPE_MAIN_VARIANT (type);
+ if (init && TREE_TYPE (init) == NULL_TREE)
+ {
+ /* Handle bad initializers like:
+ class COMPLEX {
+ public:
+ double re, im;
+ COMPLEX(double r = 0.0, double i = 0.0) {re = r; im = i;};
+ ~COMPLEX() {};
+ };
+
+ int main(int argc, char **argv) {
+ COMPLEX zees(1.0, 0.0)[10];
+ }
+ */
+ error ("bad array initializer");
+ return;
+ }
+ expand_vec_init (exp, exp, array_type_nelts (type), init,
+ init && comptypes (TREE_TYPE (init), TREE_TYPE (exp), 1));
+ TREE_READONLY (exp) = was_const;
+ TREE_TYPE (exp) = type;
+ if (init) TREE_TYPE (init) = itype;
+ return;
+ }
+
+ if (TREE_CODE (exp) == VAR_DECL || TREE_CODE (exp) == PARM_DECL)
+ /* just know that we've seen something for this node */
+ TREE_USED (exp) = 1;
+
+#if 0
+ /* If initializing from a GNU C CONSTRUCTOR, consider the elts in the
+ constructor as parameters to an implicit GNU C++ constructor. */
+ if (init && TREE_CODE (init) == CONSTRUCTOR
+ && TYPE_HAS_CONSTRUCTOR (type)
+ && TREE_TYPE (init) == type)
+ init = CONSTRUCTOR_ELTS (init);
+#endif
+ expand_aggr_init_1 (TYPE_BINFO (type), exp, exp,
+ init, alias_this, LOOKUP_NORMAL);
+ TREE_READONLY (exp) = was_const;
+}
+
+static void
+expand_default_init (binfo, true_exp, exp, type, init, alias_this, flags)
+ tree binfo;
+ tree true_exp, exp;
+ tree type;
+ tree init;
+ int alias_this;
+ int flags;
+{
+ /* It fails because there may not be a constructor which takes
+ its own type as the first (or only parameter), but which does
+ take other types via a conversion. So, if the thing initializing
+ the expression is a unit element of type X, first try X(X&),
+ followed by initialization by X. If neither of these work
+ out, then look hard. */
+ tree rval;
+ tree parms;
+ int xxref_init_possible;
+
+ if (init == NULL_TREE || TREE_CODE (init) == TREE_LIST)
+ {
+ parms = init;
+ if (parms) init = TREE_VALUE (parms);
+ }
+ else if (TREE_CODE (init) == INDIRECT_REF && TREE_HAS_CONSTRUCTOR (init))
+ {
+ rval = convert_for_initialization (exp, type, init, 0, 0, 0, 0);
+ expand_expr_stmt (rval);
+ return;
+ }
+ else
+ parms = build_tree_list (NULL_TREE, init);
+
+ if (TYPE_HAS_INIT_REF (type)
+ || init == NULL_TREE
+ || TREE_CHAIN (parms) != NULL_TREE)
+ xxref_init_possible = 0;
+ else
+ {
+ xxref_init_possible = LOOKUP_SPECULATIVELY;
+ flags &= ~LOOKUP_COMPLAIN;
+ }
+
+ if (TYPE_USES_VIRTUAL_BASECLASSES (type))
+ {
+ if (true_exp == exp)
+ parms = tree_cons (NULL_TREE, integer_one_node, parms);
+ else
+ parms = tree_cons (NULL_TREE, integer_zero_node, parms);
+ flags |= LOOKUP_HAS_IN_CHARGE;
+ }
+
+ rval = build_method_call (exp, constructor_name_full (type),
+ parms, binfo, flags|xxref_init_possible);
+ if (rval == NULL_TREE && xxref_init_possible)
+ {
+ /* It is an error to implement a default copy constructor if
+ (see ARM 12.8 for details) ... one case being if another
+ copy constructor already exists. */
+ tree init_type = TREE_TYPE (init);
+ if (TREE_CODE (init_type) == REFERENCE_TYPE)
+ init_type = TREE_TYPE (init_type);
+ if (TYPE_MAIN_VARIANT (init_type) == TYPE_MAIN_VARIANT (type)
+ || (IS_AGGR_TYPE (init_type)
+ && UNIQUELY_DERIVED_FROM_P (type, init_type)))
+ {
+ if (type == BINFO_TYPE (binfo)
+ && TYPE_USES_VIRTUAL_BASECLASSES (type))
+ {
+ tree addr = build_unary_op (ADDR_EXPR, exp, 0);
+ expand_aggr_vbase_init (binfo, exp, addr, NULL_TREE);
+
+ expand_indirect_vtbls_init (binfo, exp, addr, 1);
+ }
+ expand_expr_stmt (build_modify_expr (exp, INIT_EXPR, init));
+ return;
+ }
+ else
+ rval = build_method_call (exp, constructor_name_full (type), parms,
+ binfo, flags);
+ }
+
+ /* Private, protected, or otherwise unavailable. */
+ if (rval == error_mark_node && (flags&LOOKUP_COMPLAIN))
+ cp_error ("in base initialization for class `%T'", binfo);
+ /* A valid initialization using constructor. */
+ else if (rval != error_mark_node && rval != NULL_TREE)
+ {
+ /* p. 222: if the base class assigns to `this', then that
+ value is used in the derived class. */
+ if ((flag_this_is_variable & 1) && alias_this)
+ {
+ TREE_TYPE (rval) = TREE_TYPE (current_class_decl);
+ expand_assignment (current_class_decl, rval, 0, 0);
+ }
+ else
+ expand_expr_stmt (rval);
+ }
+ else if (parms && TREE_CHAIN (parms) == NULL_TREE)
+ {
+ /* If we are initializing one aggregate value
+ from another, and though there are constructors,
+ and none accept the initializer, just do a bitwise
+ copy.
+
+ The above sounds wrong, ``If a class has any copy
+ constructor defined, the default copy constructor will
+ not be generated.'' 12.8 Copying Class Objects (mrs)
+
+ @@ This should reject initializer which a constructor
+ @@ rejected on access gounds, but there is
+ @@ no way right now to recognize that case with
+ @@ just `error_mark_node'. */
+ tree itype;
+ init = TREE_VALUE (parms);
+ itype = TREE_TYPE (init);
+ if (TREE_CODE (itype) == REFERENCE_TYPE)
+ {
+ init = convert_from_reference (init);
+ itype = TREE_TYPE (init);
+ }
+ itype = TYPE_MAIN_VARIANT (itype);
+
+ /* This is currently how the default X(X&) constructor
+ is implemented. */
+ if (comptypes (TYPE_MAIN_VARIANT (type), itype, 0))
+ {
+#if 0
+ warning ("bitwise copy in initialization of type `%s'",
+ TYPE_NAME_STRING (type));
+#endif
+ rval = build (INIT_EXPR, type, exp, init);
+ expand_expr_stmt (rval);
+ }
+ else
+ {
+ cp_error ("in base initialization for class `%T',", binfo);
+ cp_error ("invalid initializer to constructor for type `%T'", type);
+ return;
+ }
+ }
+ else
+ {
+ if (init == NULL_TREE)
+ my_friendly_assert (parms == NULL_TREE, 210);
+ if (parms == NULL_TREE && TREE_VIA_VIRTUAL (binfo))
+ cp_error ("virtual baseclass `%T' does not have default initializer", binfo);
+ else
+ {
+ cp_error ("in base initialization for class `%T',", binfo);
+ /* This will make an error message for us. */
+ build_method_call (exp, constructor_name_full (type), parms, binfo,
+ (TYPE_USES_VIRTUAL_BASECLASSES (type)
+ ? LOOKUP_NORMAL|LOOKUP_HAS_IN_CHARGE
+ : LOOKUP_NORMAL));
+ }
+ return;
+ }
+ /* Constructor has been called, but vtables may be for TYPE
+ rather than for FOR_TYPE. */
+}
+
+/* This function is responsible for initializing EXP with INIT
+ (if any).
+
+ BINFO is the binfo of the type for who we are performing the
+ initialization. For example, if W is a virtual base class of A and B,
+ and C : A, B.
+ If we are initializing B, then W must contain B's W vtable, whereas
+ were we initializing C, W must contain C's W vtable.
+
+ TRUE_EXP is nonzero if it is the true expression being initialized.
+ In this case, it may be EXP, or may just contain EXP. The reason we
+ need this is because if EXP is a base element of TRUE_EXP, we
+ don't necessarily know by looking at EXP where its virtual
+ baseclass fields should really be pointing. But we do know
+ from TRUE_EXP. In constructors, we don't know anything about
+ the value being initialized.
+
+ ALIAS_THIS serves the same purpose it serves for expand_aggr_init.
+
+ FLAGS is just passes to `build_method_call'. See that function for
+ its description. */
+
+static void
+expand_aggr_init_1 (binfo, true_exp, exp, init, alias_this, flags)
+ tree binfo;
+ tree true_exp, exp;
+ tree init;
+ int alias_this;
+ int flags;
+{
+ tree type = TREE_TYPE (exp);
+ tree init_type = NULL_TREE;
+
+ my_friendly_assert (init != error_mark_node && type != error_mark_node, 211);
+
+ /* Use a function returning the desired type to initialize EXP for us.
+ If the function is a constructor, and its first argument is
+ NULL_TREE, know that it was meant for us--just slide exp on
+ in and expand the constructor. Constructors now come
+ as TARGET_EXPRs. */
+ if (init)
+ {
+ tree init_list = NULL_TREE;
+
+ if (TREE_CODE (init) == TREE_LIST)
+ {
+ init_list = init;
+ if (TREE_CHAIN (init) == NULL_TREE)
+ init = TREE_VALUE (init);
+ }
+
+ init_type = TREE_TYPE (init);
+
+ if (TREE_CODE (init) != TREE_LIST)
+ {
+ if (TREE_CODE (init_type) == ERROR_MARK)
+ return;
+
+#if 0
+ /* These lines are found troublesome 5/11/89. */
+ if (TREE_CODE (init_type) == REFERENCE_TYPE)
+ init_type = TREE_TYPE (init_type);
+#endif
+
+ /* This happens when we use C++'s functional cast notation.
+ If the types match, then just use the TARGET_EXPR
+ directly. Otherwise, we need to create the initializer
+ separately from the object being initialized. */
+ if (TREE_CODE (init) == TARGET_EXPR)
+ {
+ if (init_type == type)
+ {
+ if (TREE_CODE (exp) == VAR_DECL
+ || TREE_CODE (exp) == RESULT_DECL)
+ /* Unify the initialization targets. */
+ DECL_RTL (TREE_OPERAND (init, 0)) = DECL_RTL (exp);
+ else
+ DECL_RTL (TREE_OPERAND (init, 0)) = expand_expr (exp, NULL_RTX, 0, 0);
+
+ expand_expr_stmt (init);
+ return;
+ }
+ else
+ {
+ init = TREE_OPERAND (init, 1);
+ init = build (CALL_EXPR, init_type,
+ TREE_OPERAND (init, 0), TREE_OPERAND (init, 1), 0);
+ TREE_SIDE_EFFECTS (init) = 1;
+#if 0
+ TREE_RAISES (init) = ??
+#endif
+ if (init_list)
+ TREE_VALUE (init_list) = init;
+ }
+ }
+
+ if (init_type == type && TREE_CODE (init) == CALL_EXPR
+#if 0
+ /* It is legal to directly initialize from a CALL_EXPR
+ without going through X(X&), apparently. */
+ && ! TYPE_GETS_INIT_REF (type)
+#endif
+ )
+ {
+ /* A CALL_EXPR is a legitimate form of initialization, so
+ we should not print this warning message. */
+#if 0
+ /* Should have gone away due to 5/11/89 change. */
+ if (TREE_CODE (TREE_TYPE (init)) == REFERENCE_TYPE)
+ init = convert_from_reference (init);
+#endif
+ expand_assignment (exp, init, 0, 0);
+ if (exp == DECL_RESULT (current_function_decl))
+ {
+ /* Failing this assertion means that the return value
+ from receives multiple initializations. */
+ my_friendly_assert (DECL_INITIAL (exp) == NULL_TREE
+ || DECL_INITIAL (exp) == error_mark_node,
+ 212);
+ DECL_INITIAL (exp) = init;
+ }
+ return;
+ }
+ else if (init_type == type
+ && TREE_CODE (init) == COND_EXPR)
+ {
+ /* Push value to be initialized into the cond, where possible.
+ Avoid spurious warning messages when initializing the
+ result of this function. */
+ TREE_OPERAND (init, 1)
+ = build_modify_expr (exp, INIT_EXPR, TREE_OPERAND (init, 1));
+ if (exp == DECL_RESULT (current_function_decl))
+ DECL_INITIAL (exp) = NULL_TREE;
+ TREE_OPERAND (init, 2)
+ = build_modify_expr (exp, INIT_EXPR, TREE_OPERAND (init, 2));
+ if (exp == DECL_RESULT (current_function_decl))
+ DECL_INITIAL (exp) = init;
+ TREE_SIDE_EFFECTS (init) = 1;
+ expand_expr (init, const0_rtx, VOIDmode, 0);
+ free_temp_slots ();
+ return;
+ }
+ }
+
+ /* We did not know what we were initializing before. Now we do. */
+ if (TREE_CODE (init) == TARGET_EXPR)
+ {
+ tree tmp = TREE_OPERAND (TREE_OPERAND (init, 1), 1);
+
+ if (TREE_CODE (TREE_VALUE (tmp)) == NOP_EXPR
+ && TREE_OPERAND (TREE_VALUE (tmp), 0) == integer_zero_node)
+ {
+ /* In order for this to work for RESULT_DECLs, if their
+ type has a constructor, then they must be BLKmode
+ so that they will be meaningfully addressable. */
+ tree arg = build_unary_op (ADDR_EXPR, exp, 0);
+ init = TREE_OPERAND (init, 1);
+ init = build (CALL_EXPR, build_pointer_type (TREE_TYPE (init)),
+ TREE_OPERAND (init, 0), TREE_OPERAND (init, 1), 0);
+ TREE_SIDE_EFFECTS (init) = 1;
+#if 0
+ TREE_RAISES (init) = ??
+#endif
+ TREE_VALUE (TREE_OPERAND (init, 1))
+ = convert_pointer_to (TREE_TYPE (TREE_TYPE (TREE_VALUE (tmp))), arg);
+
+ if (alias_this)
+ {
+ expand_assignment (current_function_decl, init, 0, 0);
+ return;
+ }
+ if (exp == DECL_RESULT (current_function_decl))
+ {
+ if (DECL_INITIAL (DECL_RESULT (current_function_decl)))
+ fatal ("return value from function receives multiple initializations");
+ DECL_INITIAL (exp) = init;
+ }
+ expand_expr_stmt (init);
+ return;
+ }
+ }
+
+ if (TREE_CODE (exp) == VAR_DECL
+ && TREE_CODE (init) == CONSTRUCTOR
+ && TREE_HAS_CONSTRUCTOR (init))
+ {
+ tree t = store_init_value (exp, init);
+ if (!t)
+ {
+ expand_decl_init (exp);
+ return;
+ }
+ t = build (INIT_EXPR, type, exp, init);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr_stmt (t);
+ return;
+ }
+
+ /* Handle this case: when calling a constructor: xyzzy foo(bar);
+ which really means: xyzzy foo = bar; Ugh!
+
+ More useful for this case: xyzzy *foo = new xyzzy (bar); */
+
+ if (! TYPE_NEEDS_CONSTRUCTING (type) && ! IS_AGGR_TYPE (type))
+ {
+ if (init_list && TREE_CHAIN (init_list))
+ {
+ warning ("initializer list being treated as compound expression");
+ init = convert (type, build_compound_expr (init_list));
+ if (init == error_mark_node)
+ return;
+ }
+
+ expand_assignment (exp, init, 0, 0);
+
+ return;
+ }
+ /* See whether we can go through a type conversion operator.
+ This wins over going through a non-existent constructor. If
+ there is a constructor, it is ambiguous. */
+ if (TREE_CODE (init) != TREE_LIST)
+ {
+ tree ttype = TREE_CODE (init_type) == REFERENCE_TYPE
+ ? TREE_TYPE (init_type) : init_type;
+
+ if (ttype != type && IS_AGGR_TYPE (ttype))
+ {
+ tree rval = build_type_conversion (CONVERT_EXPR, type, init, 0);
+
+ if (rval)
+ {
+ /* See if there is a constructor for``type'' that takes a
+ ``ttype''-typed object. */
+ tree parms = build_tree_list (NULL_TREE, init);
+ tree as_cons = NULL_TREE;
+ if (TYPE_HAS_CONSTRUCTOR (type))
+ as_cons = build_method_call (exp, constructor_name_full (type),
+ parms, binfo,
+ LOOKUP_SPECULATIVELY|LOOKUP_NO_CONVERSION);
+ if (as_cons != NULL_TREE && as_cons != error_mark_node)
+ /* ANSI C++ June 5 1992 WP 12.3.2.6.1 */
+ cp_error ("ambiguity between conversion to `%T' and constructor",
+ type);
+ else
+ expand_assignment (exp, rval, 0, 0);
+ return;
+ }
+ }
+ }
+ }
+
+ /* Handle default copy constructors here, does not matter if there is
+ a constructor or not. */
+ if (type == init_type && IS_AGGR_TYPE (type)
+ && init && TREE_CODE (init) != TREE_LIST)
+ expand_default_init (binfo, true_exp, exp, type, init, alias_this, flags);
+ /* Not sure why this is here... */
+ else if (TYPE_HAS_CONSTRUCTOR (type))
+ expand_default_init (binfo, true_exp, exp, type, init, alias_this, flags);
+ else if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ if (TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (type)))
+ expand_vec_init (exp, exp, array_type_nelts (type), init, 0);
+ else if (TYPE_VIRTUAL_P (TREE_TYPE (type)))
+ sorry ("arrays of objects with virtual functions but no constructors");
+ }
+ else
+ expand_recursive_init (binfo, true_exp, exp, init,
+ CLASSTYPE_BASE_INIT_LIST (type), alias_this);
+}
+
+/* A pointer which holds the initializer. First call to
+ expand_aggr_init gets this value pointed to, and sets it to init_null. */
+static tree *init_ptr, init_null;
+
+/* Subroutine of expand_recursive_init:
+
+ ADDR is the address of the expression being initialized.
+ INIT_LIST is the cons-list of initializations to be performed.
+ ALIAS_THIS is its same, lovable self. */
+static void
+expand_recursive_init_1 (binfo, true_exp, addr, init_list, alias_this)
+ tree binfo, true_exp, addr;
+ tree init_list;
+ int alias_this;
+{
+ while (init_list)
+ {
+ if (TREE_PURPOSE (init_list))
+ {
+ if (TREE_CODE (TREE_PURPOSE (init_list)) == FIELD_DECL)
+ {
+ tree member = TREE_PURPOSE (init_list);
+ tree subexp = build_indirect_ref (convert_pointer_to (TREE_VALUE (init_list), addr), NULL_PTR);
+ tree member_base = build (COMPONENT_REF, TREE_TYPE (member), subexp, member);
+ if (IS_AGGR_TYPE (TREE_TYPE (member)))
+ expand_aggr_init (member_base, DECL_INITIAL (member), 0);
+ else if (TREE_CODE (TREE_TYPE (member)) == ARRAY_TYPE
+ && TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (member)))
+ {
+ member_base = save_expr (default_conversion (member_base));
+ expand_vec_init (member, member_base,
+ array_type_nelts (TREE_TYPE (member)),
+ DECL_INITIAL (member), 0);
+ }
+ else
+ expand_expr_stmt (build_modify_expr (member_base, INIT_EXPR, DECL_INITIAL (member)));
+ }
+ else if (TREE_CODE (TREE_PURPOSE (init_list)) == TREE_LIST)
+ {
+ expand_recursive_init_1 (binfo, true_exp, addr, TREE_PURPOSE (init_list), alias_this);
+ expand_recursive_init_1 (binfo, true_exp, addr, TREE_VALUE (init_list), alias_this);
+ }
+ else if (TREE_CODE (TREE_PURPOSE (init_list)) == ERROR_MARK)
+ {
+ /* Only initialize the virtual function tables if we
+ are initializing the ultimate users of those vtables. */
+ if (TREE_VALUE (init_list))
+ {
+ /* We have to ensure that the first argment to
+ expand_virtual_init is in binfo's hierarchy. */
+ /* Is it the case that this is exactly the right binfo? */
+ /* If it is ok, then fixup expand_virtual_init, to make
+ it much simpler. */
+ expand_virtual_init (get_binfo (TREE_VALUE (init_list), binfo, 0),
+ addr);
+ if (TREE_VALUE (init_list) == binfo
+ && TYPE_USES_VIRTUAL_BASECLASSES (BINFO_TYPE (binfo)))
+ expand_indirect_vtbls_init (binfo, true_exp, addr, 1);
+ }
+ }
+ else
+ my_friendly_abort (49);
+ }
+ else if (TREE_VALUE (init_list)
+ && TREE_CODE (TREE_VALUE (init_list)) == TREE_VEC)
+ {
+ tree subexp = build_indirect_ref (convert_pointer_to (TREE_VALUE (init_list), addr), NULL_PTR);
+ expand_aggr_init_1 (binfo, true_exp, subexp, *init_ptr,
+ alias_this && BINFO_OFFSET_ZEROP (TREE_VALUE (init_list)),
+ LOOKUP_COMPLAIN);
+
+ /* INIT_PTR is used up. */
+ init_ptr = &init_null;
+ }
+ else
+ my_friendly_abort (50);
+ init_list = TREE_CHAIN (init_list);
+ }
+}
+
+/* Initialize EXP with INIT. Type EXP does not have a constructor,
+ but it has a baseclass with a constructor or a virtual function
+ table which needs initializing.
+
+ INIT_LIST is a cons-list describing what parts of EXP actually
+ need to be initialized. INIT is given to the *unique*, first
+ constructor within INIT_LIST. If there are multiple first
+ constructors, such as with multiple inheritance, INIT must
+ be zero or an ambiguity error is reported.
+
+ ALIAS_THIS is passed from `expand_aggr_init'. See comments
+ there. */
+
+static void
+expand_recursive_init (binfo, true_exp, exp, init, init_list, alias_this)
+ tree binfo, true_exp, exp, init;
+ tree init_list;
+ int alias_this;
+{
+ tree *old_init_ptr = init_ptr;
+ tree addr = build_unary_op (ADDR_EXPR, exp, 0);
+ init_ptr = &init;
+
+ if (true_exp == exp && TYPE_USES_VIRTUAL_BASECLASSES (BINFO_TYPE (binfo)))
+ {
+ expand_aggr_vbase_init (binfo, exp, addr, init_list);
+ expand_indirect_vtbls_init (binfo, true_exp, addr, 1);
+ }
+ expand_recursive_init_1 (binfo, true_exp, addr, init_list, alias_this);
+
+ if (*init_ptr)
+ {
+ tree type = TREE_TYPE (exp);
+
+ if (TREE_CODE (type) == REFERENCE_TYPE)
+ type = TREE_TYPE (type);
+ if (IS_AGGR_TYPE (type))
+ cp_error ("unexpected argument to constructor `%T'", type);
+ else
+ error ("unexpected argument to constructor");
+ }
+ init_ptr = old_init_ptr;
+}
+
+/* Report an error if NAME is not the name of a user-defined,
+ aggregate type. If OR_ELSE is nonzero, give an error message. */
+int
+is_aggr_typedef (name, or_else)
+ tree name;
+ int or_else;
+{
+ tree type;
+
+ if (name == error_mark_node)
+ return 0;
+
+ if (IDENTIFIER_HAS_TYPE_VALUE (name))
+ type = IDENTIFIER_TYPE_VALUE (name);
+ else
+ {
+ if (or_else)
+ cp_error ("`%T' is not an aggregate typedef", name);
+ return 0;
+ }
+
+ if (! IS_AGGR_TYPE (type)
+ && TREE_CODE (type) != TEMPLATE_TYPE_PARM)
+ {
+ if (or_else)
+ cp_error ("`%T' is not an aggregate type", type);
+ return 0;
+ }
+ return 1;
+}
+
+/* Like is_aggr_typedef, but returns typedef if successful. */
+tree
+get_aggr_from_typedef (name, or_else)
+ tree name;
+ int or_else;
+{
+ tree type;
+
+ if (name == error_mark_node)
+ return NULL_TREE;
+
+ if (IDENTIFIER_HAS_TYPE_VALUE (name))
+ type = IDENTIFIER_TYPE_VALUE (name);
+ else
+ {
+ if (or_else)
+ cp_error ("`%T' fails to be an aggregate typedef", name);
+ return NULL_TREE;
+ }
+
+ if (! IS_AGGR_TYPE (type)
+ && TREE_CODE (type) != TEMPLATE_TYPE_PARM)
+ {
+ if (or_else)
+ cp_error ("type `%T' is of non-aggregate type", type);
+ return NULL_TREE;
+ }
+ return type;
+}
+
+tree
+get_type_value (name)
+ tree name;
+{
+ if (name == error_mark_node)
+ return NULL_TREE;
+
+ if (IDENTIFIER_HAS_TYPE_VALUE (name))
+ return IDENTIFIER_TYPE_VALUE (name);
+ else
+ return NULL_TREE;
+}
+
+
+/* This code could just as well go in `class.c', but is placed here for
+ modularity. */
+
+/* For an expression of the form CNAME :: NAME (PARMLIST), build
+ the appropriate function call. */
+tree
+build_member_call (cname, name, parmlist)
+ tree cname, name, parmlist;
+{
+ tree type, t;
+ tree method_name = name;
+ int dtor = 0;
+ int dont_use_this = 0;
+ tree basetype_path, decl;
+
+ if (TREE_CODE (method_name) == BIT_NOT_EXPR)
+ {
+ method_name = TREE_OPERAND (method_name, 0);
+ dtor = 1;
+ }
+
+ if (TREE_CODE (cname) == SCOPE_REF)
+ cname = resolve_scope_to_name (NULL_TREE, cname);
+
+ if (cname == NULL_TREE || ! (type = get_aggr_from_typedef (cname, 1)))
+ return error_mark_node;
+
+ /* An operator we did not like. */
+ if (name == NULL_TREE)
+ return error_mark_node;
+
+ if (dtor)
+ {
+#if 0
+ /* Everything can explicitly call a destructor; see 12.4 */
+ if (! TYPE_HAS_DESTRUCTOR (type))
+ cp_error ("type `%#T' does not have a destructor", type);
+ else
+#endif
+ cp_error ("cannot call destructor `%T::~%T' without object", type,
+ method_name);
+ return error_mark_node;
+ }
+
+ /* No object? Then just fake one up, and let build_method_call
+ figure out what to do. */
+ if (current_class_type == 0
+ || get_base_distance (type, current_class_type, 0, &basetype_path) == -1)
+ dont_use_this = 1;
+
+ if (dont_use_this)
+ {
+ basetype_path = TYPE_BINFO (type);
+ decl = build1 (NOP_EXPR, TYPE_POINTER_TO (type), error_mark_node);
+ }
+ else if (current_class_decl == 0)
+ {
+ dont_use_this = 1;
+ decl = build1 (NOP_EXPR, TYPE_POINTER_TO (type), error_mark_node);
+ }
+ else
+ {
+ tree olddecl = current_class_decl;
+ tree oldtype = TREE_TYPE (TREE_TYPE (olddecl));
+ if (oldtype != type)
+ {
+ tree newtype = build_type_variant (type, TYPE_READONLY (oldtype),
+ TYPE_VOLATILE (oldtype));
+ decl = convert_force (build_pointer_type (newtype), olddecl);
+ }
+ else
+ decl = olddecl;
+ }
+
+ decl = build_indirect_ref (decl, NULL_PTR);
+
+ if (t = lookup_fnfields (basetype_path, method_name, 0))
+ return build_method_call (decl, method_name, parmlist, basetype_path,
+ LOOKUP_NORMAL|LOOKUP_NONVIRTUAL);
+ if (TREE_CODE (name) == IDENTIFIER_NODE
+ && ((t = lookup_field (TYPE_BINFO (type), name, 1, 0))))
+ {
+ if (t == error_mark_node)
+ return error_mark_node;
+ if (TREE_CODE (t) == FIELD_DECL)
+ {
+ if (dont_use_this)
+ {
+ cp_error ("invalid use of non-static field `%D'", t);
+ return error_mark_node;
+ }
+ decl = build (COMPONENT_REF, TREE_TYPE (t), decl, t);
+ }
+ else if (TREE_CODE (t) == VAR_DECL)
+ decl = t;
+ else
+ {
+ cp_error ("invalid use of member `%D'", t);
+ return error_mark_node;
+ }
+ if (TYPE_LANG_SPECIFIC (TREE_TYPE (decl))
+ && TYPE_OVERLOADS_CALL_EXPR (TREE_TYPE (decl)))
+ return build_opfncall (CALL_EXPR, LOOKUP_NORMAL, decl, parmlist, NULL_TREE);
+ return build_function_call (decl, parmlist);
+ }
+ else
+ {
+ cp_error ("no method `%T::%D'", type, name);
+ return error_mark_node;
+ }
+}
+
+/* Build a reference to a member of an aggregate. This is not a
+ C++ `&', but really something which can have its address taken,
+ and then act as a pointer to member, for example CNAME :: FIELD
+ can have its address taken by saying & CNAME :: FIELD.
+
+ @@ Prints out lousy diagnostics for operator <typename>
+ @@ fields.
+
+ @@ This function should be rewritten and placed in search.c. */
+tree
+build_offset_ref (cname, name)
+ tree cname, name;
+{
+ tree decl, type, fnfields, fields, t = error_mark_node;
+ tree basetypes = NULL_TREE;
+ int dtor = 0;
+
+ if (TREE_CODE (cname) == SCOPE_REF)
+ cname = resolve_scope_to_name (NULL_TREE, cname);
+
+ if (cname == NULL_TREE || ! is_aggr_typedef (cname, 1))
+ return error_mark_node;
+
+ type = IDENTIFIER_TYPE_VALUE (cname);
+
+ if (TREE_CODE (name) == BIT_NOT_EXPR)
+ {
+ dtor = 1;
+ name = TREE_OPERAND (name, 0);
+ }
+
+ if (TYPE_SIZE (type) == 0)
+ {
+ t = IDENTIFIER_CLASS_VALUE (name);
+ if (t == 0)
+ {
+ cp_error ("incomplete type `%T' does not have member `%D'", type,
+ name);
+ return error_mark_node;
+ }
+ if (TREE_CODE (t) == TYPE_DECL || TREE_CODE (t) == VAR_DECL
+ || TREE_CODE (t) == CONST_DECL)
+ {
+ TREE_USED (t) = 1;
+ return t;
+ }
+ if (TREE_CODE (t) == FIELD_DECL)
+ sorry ("use of member in incomplete aggregate type");
+ else if (TREE_CODE (t) == FUNCTION_DECL)
+ sorry ("use of member function in incomplete aggregate type");
+ else
+ my_friendly_abort (52);
+ return error_mark_node;
+ }
+
+#if 0
+ if (TREE_CODE (name) == TYPE_EXPR)
+ /* Pass a TYPE_DECL to build_component_type_expr. */
+ return build_component_type_expr (TYPE_NAME (TREE_TYPE (cname)),
+ name, NULL_TREE, 1);
+#endif
+
+ fnfields = lookup_fnfields (TYPE_BINFO (type), name, 1);
+ fields = lookup_field (type, name, 0, 0);
+
+ if (fields == error_mark_node || fnfields == error_mark_node)
+ return error_mark_node;
+
+ if (current_class_type == 0
+ || get_base_distance (type, current_class_type, 0, &basetypes) == -1)
+ {
+ basetypes = TYPE_BINFO (type);
+ decl = build1 (NOP_EXPR,
+ IDENTIFIER_TYPE_VALUE (cname),
+ error_mark_node);
+ }
+ else if (current_class_decl == 0)
+ decl = build1 (NOP_EXPR, IDENTIFIER_TYPE_VALUE (cname),
+ error_mark_node);
+ else
+ decl = C_C_D;
+
+ /* A lot of this logic is now handled in lookup_field and
+ lookup_fnfield. */
+ if (fnfields)
+ {
+ basetypes = TREE_PURPOSE (fnfields);
+
+ /* Go from the TREE_BASELINK to the member function info. */
+ t = TREE_VALUE (fnfields);
+
+ if (fields)
+ {
+ if (DECL_FIELD_CONTEXT (fields) == DECL_FIELD_CONTEXT (t))
+ {
+ error ("ambiguous member reference: member `%s' defined as both field and function",
+ IDENTIFIER_POINTER (name));
+ return error_mark_node;
+ }
+ if (UNIQUELY_DERIVED_FROM_P (DECL_FIELD_CONTEXT (fields), DECL_FIELD_CONTEXT (t)))
+ ;
+ else if (UNIQUELY_DERIVED_FROM_P (DECL_FIELD_CONTEXT (t), DECL_FIELD_CONTEXT (fields)))
+ t = fields;
+ else
+ {
+ error ("ambiguous member reference: member `%s' derives from distinct classes in multiple inheritance lattice");
+ return error_mark_node;
+ }
+ }
+
+ if (t == TREE_VALUE (fnfields))
+ {
+ extern int flag_save_memoized_contexts;
+
+ /* This does not handle access checking yet. */
+ if (DECL_CHAIN (t) == NULL_TREE || dtor)
+ {
+ enum access_type access;
+
+ /* unique functions are handled easily. */
+ unique:
+ access = compute_access (basetypes, t);
+ if (access == access_protected)
+ {
+ cp_error_at ("member function `%#D' is protected", t);
+ error ("in this context");
+ return error_mark_node;
+ }
+ if (access == access_private)
+ {
+ cp_error_at ("member function `%#D' is private", t);
+ error ("in this context");
+ return error_mark_node;
+ }
+ assemble_external (t);
+ return build (OFFSET_REF, TREE_TYPE (t), decl, t);
+ }
+
+ /* overloaded functions may need more work. */
+ if (cname == name)
+ {
+ if (TYPE_HAS_DESTRUCTOR (type)
+ && DECL_CHAIN (DECL_CHAIN (t)) == NULL_TREE)
+ {
+ t = DECL_CHAIN (t);
+ goto unique;
+ }
+ }
+ /* FNFIELDS is most likely allocated on the search_obstack,
+ which will go away after this class scope. If we need
+ to save this value for later (either for memoization
+ or for use as an initializer for a static variable), then
+ do so here.
+
+ ??? The smart thing to do for the case of saving initializers
+ is to resolve them before we're done with this scope. */
+ if (!TREE_PERMANENT (fnfields)
+ && ((flag_save_memoized_contexts && global_bindings_p ())
+ || ! allocation_temporary_p ()))
+ fnfields = copy_list (fnfields);
+ t = build_tree_list (error_mark_node, fnfields);
+ TREE_TYPE (t) = build_offset_type (type, unknown_type_node);
+ return t;
+ }
+ }
+
+ /* Now that we know we are looking for a field, see if we
+ have access to that field. Lookup_field will give us the
+ error message. */
+
+ t = lookup_field (basetypes, name, 1, 0);
+
+ if (t == error_mark_node)
+ return error_mark_node;
+
+ if (t == NULL_TREE)
+ {
+ cp_error ("`%D' is not a member of type `%T'", name, type);
+ return error_mark_node;
+ }
+
+ if (TREE_CODE (t) == TYPE_DECL)
+ {
+ TREE_USED (t) = 1;
+ return t;
+ }
+ /* static class members and class-specific enum
+ values can be returned without further ado. */
+ if (TREE_CODE (t) == VAR_DECL || TREE_CODE (t) == CONST_DECL)
+ {
+ assemble_external (t);
+ TREE_USED (t) = 1;
+ return t;
+ }
+
+ /* static class functions too. */
+ if (TREE_CODE (t) == FUNCTION_DECL && TREE_CODE (TREE_TYPE (t)) == FUNCTION_TYPE)
+ my_friendly_abort (53);
+
+ /* In member functions, the form `cname::name' is no longer
+ equivalent to `this->cname::name'. */
+ return build (OFFSET_REF, build_offset_type (type, TREE_TYPE (t)), decl, t);
+}
+
+/* Given an object EXP and a member function reference MEMBER,
+ return the address of the actual member function. */
+tree
+get_member_function (exp_addr_ptr, exp, member)
+ tree *exp_addr_ptr;
+ tree exp, member;
+{
+ tree ctype = TREE_TYPE (exp);
+ tree function = save_expr (build_unary_op (ADDR_EXPR, member, 0));
+
+ if (TYPE_VIRTUAL_P (ctype)
+ || (flag_all_virtual == 1 && TYPE_OVERLOADS_METHOD_CALL_EXPR (ctype)))
+ {
+ tree e0, e1, e3;
+ tree exp_addr;
+
+ /* Save away the unadulterated `this' pointer. */
+ exp_addr = save_expr (*exp_addr_ptr);
+
+ /* Cast function to signed integer. */
+ e0 = build1 (NOP_EXPR, integer_type_node, function);
+
+ /* There is a hack here that takes advantage of
+ twos complement arithmetic, and the fact that
+ there are more than one UNITS to the WORD.
+ If the high bit is set for the `function',
+ then we pretend it is a virtual function,
+ and the array indexing will knock this bit
+ out the top, leaving a valid index. */
+ if (UNITS_PER_WORD <= 1)
+ my_friendly_abort (54);
+
+ e1 = build (GT_EXPR, integer_type_node, e0, integer_zero_node);
+ e1 = build_compound_expr (tree_cons (NULL_TREE, exp_addr,
+ build_tree_list (NULL_TREE, e1)));
+ e1 = save_expr (e1);
+
+ if (TREE_SIDE_EFFECTS (*exp_addr_ptr))
+ {
+ exp = build_indirect_ref (exp_addr, NULL_PTR);
+ *exp_addr_ptr = exp_addr;
+ }
+
+ /* This is really hairy: if the function pointer is a pointer
+ to a non-virtual member function, then we can't go mucking
+ with the `this' pointer (any more than we already have to
+ this point). If it is a pointer to a virtual member function,
+ then we have to adjust the `this' pointer according to
+ what the virtual function table tells us. */
+
+ e3 = build_vfn_ref (exp_addr_ptr, exp, e0);
+ my_friendly_assert (e3 != error_mark_node, 213);
+
+ /* Change this pointer type from `void *' to the
+ type it is really supposed to be. */
+ TREE_TYPE (e3) = TREE_TYPE (function);
+
+ /* If non-virtual, use what we had originally. Otherwise,
+ use the value we get from the virtual function table. */
+ *exp_addr_ptr = build_conditional_expr (e1, exp_addr, *exp_addr_ptr);
+
+ function = build_conditional_expr (e1, function, e3);
+ }
+ return build_indirect_ref (function, NULL_PTR);
+}
+
+/* If a OFFSET_REF made it through to here, then it did
+ not have its address taken. */
+
+tree
+resolve_offset_ref (exp)
+ tree exp;
+{
+ tree type = TREE_TYPE (exp);
+ tree base = NULL_TREE;
+ tree member;
+ tree basetype, addr;
+
+ if (TREE_CODE (exp) == TREE_LIST)
+ return build_unary_op (ADDR_EXPR, exp, 0);
+
+ if (TREE_CODE (exp) != OFFSET_REF)
+ {
+ my_friendly_assert (TREE_CODE (type) == OFFSET_TYPE, 214);
+ if (TYPE_OFFSET_BASETYPE (type) != current_class_type)
+ {
+ error ("object missing in use of pointer-to-member construct");
+ return error_mark_node;
+ }
+ member = exp;
+ type = TREE_TYPE (type);
+ base = C_C_D;
+ }
+ else
+ {
+ member = TREE_OPERAND (exp, 1);
+ base = TREE_OPERAND (exp, 0);
+ }
+
+ if ((TREE_CODE (member) == VAR_DECL
+ && ! TYPE_PTRMEMFUNC_P (TREE_TYPE (member)))
+ || TREE_CODE (TREE_TYPE (member)) == FUNCTION_TYPE)
+ {
+ /* These were static members. */
+ if (mark_addressable (member) == 0)
+ return error_mark_node;
+ return member;
+ }
+
+ /* Syntax error can cause a member which should
+ have been seen as static to be grok'd as non-static. */
+ if (TREE_CODE (member) == FIELD_DECL && C_C_D == NULL_TREE)
+ {
+ if (TREE_ADDRESSABLE (member) == 0)
+ {
+ cp_error_at ("member `%D' is non-static in static member function context", member);
+ error ("at this point in file");
+ TREE_ADDRESSABLE (member) = 1;
+ }
+ return error_mark_node;
+ }
+
+ /* The first case is really just a reference to a member of `this'. */
+ if (TREE_CODE (member) == FIELD_DECL
+ && (base == C_C_D
+ || (TREE_CODE (base) == NOP_EXPR
+ && TREE_OPERAND (base, 0) == error_mark_node)))
+ {
+ tree basetype_path;
+ enum access_type access;
+
+ if (TREE_CODE (exp) == OFFSET_REF && TREE_CODE (type) == OFFSET_TYPE)
+ basetype = TYPE_OFFSET_BASETYPE (type);
+ else
+ basetype = DECL_CONTEXT (member);
+
+ base = current_class_decl;
+
+ if (get_base_distance (basetype, TREE_TYPE (TREE_TYPE (base)), 0, &basetype_path) < 0)
+ {
+ error_not_base_type (basetype, TREE_TYPE (TREE_TYPE (base)));
+ return error_mark_node;
+ }
+ addr = convert_pointer_to (basetype, base);
+ access = compute_access (basetype_path, member);
+ if (access == access_public)
+ return build (COMPONENT_REF, TREE_TYPE (member),
+ build_indirect_ref (addr, NULL_PTR), member);
+ if (access == access_protected)
+ {
+ cp_error_at ("member `%D' is protected", member);
+ error ("in this context");
+ return error_mark_node;
+ }
+ if (access == access_private)
+ {
+ cp_error_at ("member `%D' is private", member);
+ error ("in this context");
+ return error_mark_node;
+ }
+ my_friendly_abort (55);
+ }
+
+ /* If this is a reference to a member function, then return
+ the address of the member function (which may involve going
+ through the object's vtable), otherwise, return an expression
+ for the dereferenced pointer-to-member construct. */
+ addr = build_unary_op (ADDR_EXPR, base, 0);
+
+ if (TREE_CODE (TREE_TYPE (member)) == METHOD_TYPE)
+ {
+ basetype = DECL_CLASS_CONTEXT (member);
+ addr = convert_pointer_to (basetype, addr);
+ return build_unary_op (ADDR_EXPR, get_member_function (&addr, build_indirect_ref (addr, NULL_PTR), member), 0);
+ }
+ else if (TREE_CODE (TREE_TYPE (member)) == OFFSET_TYPE)
+ {
+ basetype = TYPE_OFFSET_BASETYPE (TREE_TYPE (member));
+ addr = convert_pointer_to (basetype, addr);
+ member = convert (ptr_type_node, build_unary_op (ADDR_EXPR, member, 0));
+ return build1 (INDIRECT_REF, type,
+ build (PLUS_EXPR, ptr_type_node, addr, member));
+ }
+ else if (TYPE_PTRMEMFUNC_P (TREE_TYPE (member)))
+ {
+ return get_member_function_from_ptrfunc (&addr, base, member);
+ }
+ my_friendly_abort (56);
+ /* NOTREACHED */
+ return NULL_TREE;
+}
+
+/* Return either DECL or its known constant value (if it has one). */
+
+tree
+decl_constant_value (decl)
+ tree decl;
+{
+ if (! TREE_THIS_VOLATILE (decl)
+#if 0
+ /* These may be necessary for C, but they break C++. */
+ ! TREE_PUBLIC (decl)
+ /* Don't change a variable array bound or initial value to a constant
+ in a place where a variable is invalid. */
+ && ! pedantic
+#endif /* 0 */
+ && DECL_INITIAL (decl) != 0
+ && TREE_CODE (DECL_INITIAL (decl)) != ERROR_MARK
+ /* This is invalid if initial value is not constant.
+ If it has either a function call, a memory reference,
+ or a variable, then re-evaluating it could give different results. */
+ && TREE_CONSTANT (DECL_INITIAL (decl))
+ /* Check for cases where this is sub-optimal, even though valid. */
+ && TREE_CODE (DECL_INITIAL (decl)) != CONSTRUCTOR
+#if 0
+ /* We must allow this to work outside of functions so that
+ static constants can be used for array sizes. */
+ && current_function_decl != 0
+ && DECL_MODE (decl) != BLKmode
+#endif
+ )
+ return DECL_INITIAL (decl);
+ return decl;
+}
+
+/* Friend handling routines. */
+/* Friend data structures:
+
+ Lists of friend functions come from TYPE_DECL nodes. Since all
+ aggregate types are automatically typedef'd, these nodes are guaranteed
+ to exist.
+
+ The TREE_PURPOSE of a friend list is the name of the friend,
+ and its TREE_VALUE is another list.
+
+ For each element of that list, either the TREE_VALUE or the TREE_PURPOSE
+ will be filled in, but not both. The TREE_VALUE of that list is an
+ individual function which is a friend. The TREE_PURPOSE of that list
+ indicates a type in which all functions by that name are friends.
+
+ Lists of friend classes come from _TYPE nodes. Love that consistency
+ thang. */
+
+int
+is_friend_type (type1, type2)
+ tree type1, type2;
+{
+ return is_friend (type1, type2);
+}
+
+int
+is_friend (type, supplicant)
+ tree type, supplicant;
+{
+ int declp;
+ register tree list;
+
+ if (supplicant == NULL_TREE || type == NULL_TREE)
+ return 0;
+
+ declp = (TREE_CODE_CLASS (TREE_CODE (supplicant)) == 'd');
+
+ if (declp)
+ /* It's a function decl. */
+ {
+ tree list = DECL_FRIENDLIST (TYPE_NAME (type));
+ tree name = DECL_NAME (supplicant);
+ tree ctype = DECL_CLASS_CONTEXT (supplicant);
+ for (; list ; list = TREE_CHAIN (list))
+ {
+ if (name == TREE_PURPOSE (list))
+ {
+ tree friends = TREE_VALUE (list);
+ name = DECL_ASSEMBLER_NAME (supplicant);
+ for (; friends ; friends = TREE_CHAIN (friends))
+ {
+ if (ctype == TREE_PURPOSE (friends))
+ return 1;
+ if (name == DECL_ASSEMBLER_NAME (TREE_VALUE (friends)))
+ return 1;
+ }
+ break;
+ }
+ }
+ }
+ else
+ /* It's a type. */
+ {
+ if (type == supplicant)
+ return 1;
+
+ list = CLASSTYPE_FRIEND_CLASSES (TREE_TYPE (TYPE_NAME (type)));
+ for (; list ; list = TREE_CHAIN (list))
+ if (supplicant == TREE_VALUE (list))
+ return 1;
+ }
+
+ {
+ tree context = declp ? DECL_CLASS_CONTEXT (supplicant)
+ : DECL_CONTEXT (TYPE_NAME (supplicant));
+
+ if (context)
+ return is_friend (type, context);
+ }
+
+ return 0;
+}
+
+/* Add a new friend to the friends of the aggregate type TYPE.
+ DECL is the FUNCTION_DECL of the friend being added. */
+static void
+add_friend (type, decl)
+ tree type, decl;
+{
+ tree typedecl = TYPE_NAME (type);
+ tree list = DECL_FRIENDLIST (typedecl);
+ tree name = DECL_NAME (decl);
+
+ while (list)
+ {
+ if (name == TREE_PURPOSE (list))
+ {
+ tree friends = TREE_VALUE (list);
+ for (; friends ; friends = TREE_CHAIN (friends))
+ {
+ if (decl == TREE_VALUE (friends))
+ {
+ cp_pedwarn ("`%D' is already a friend of class `%T'",
+ decl, type);
+ cp_pedwarn_at ("previous friend declaration of `%D'",
+ TREE_VALUE (friends));
+ return;
+ }
+ }
+ TREE_VALUE (list) = tree_cons (error_mark_node, decl,
+ TREE_VALUE (list));
+ return;
+ }
+ list = TREE_CHAIN (list);
+ }
+ DECL_FRIENDLIST (typedecl)
+ = tree_cons (DECL_NAME (decl), build_tree_list (error_mark_node, decl),
+ DECL_FRIENDLIST (typedecl));
+ if (DECL_NAME (decl) == ansi_opname[(int) MODIFY_EXPR])
+ {
+ tree parmtypes = TYPE_ARG_TYPES (TREE_TYPE (decl));
+ TYPE_HAS_ASSIGNMENT (TREE_TYPE (typedecl)) = 1;
+ if (parmtypes && TREE_CHAIN (parmtypes))
+ {
+ tree parmtype = TREE_VALUE (TREE_CHAIN (parmtypes));
+ if (TREE_CODE (parmtype) == REFERENCE_TYPE
+ && TREE_TYPE (parmtypes) == TREE_TYPE (typedecl))
+ TYPE_HAS_ASSIGN_REF (TREE_TYPE (typedecl)) = 1;
+ }
+ }
+}
+
+/* Declare that every member function NAME in FRIEND_TYPE
+ (which may be NULL_TREE) is a friend of type TYPE. */
+static void
+add_friends (type, name, friend_type)
+ tree type, name, friend_type;
+{
+ tree typedecl = TYPE_NAME (type);
+ tree list = DECL_FRIENDLIST (typedecl);
+
+ while (list)
+ {
+ if (name == TREE_PURPOSE (list))
+ {
+ tree friends = TREE_VALUE (list);
+ while (friends && TREE_PURPOSE (friends) != friend_type)
+ friends = TREE_CHAIN (friends);
+ if (friends)
+ if (friend_type)
+ warning ("method `%s::%s' is already a friend of class",
+ TYPE_NAME_STRING (friend_type),
+ IDENTIFIER_POINTER (name));
+ else
+ warning ("function `%s' is already a friend of class `%s'",
+ IDENTIFIER_POINTER (name),
+ IDENTIFIER_POINTER (DECL_NAME (typedecl)));
+ else
+ TREE_VALUE (list) = tree_cons (friend_type, NULL_TREE,
+ TREE_VALUE (list));
+ return;
+ }
+ list = TREE_CHAIN (list);
+ }
+ DECL_FRIENDLIST (typedecl) =
+ tree_cons (name,
+ build_tree_list (friend_type, NULL_TREE),
+ DECL_FRIENDLIST (typedecl));
+ if (! strncmp (IDENTIFIER_POINTER (name),
+ IDENTIFIER_POINTER (ansi_opname[(int) MODIFY_EXPR]),
+ strlen (IDENTIFIER_POINTER (ansi_opname[(int) MODIFY_EXPR]))))
+ {
+ TYPE_HAS_ASSIGNMENT (TREE_TYPE (typedecl)) = 1;
+ sorry ("declaring \"friend operator =\" will not find \"operator = (X&)\" if it exists");
+ }
+}
+
+/* Set up a cross reference so that type TYPE will make member function
+ CTYPE::DECL a friend when CTYPE is finally defined. For more than
+ one, set up a cross reference so that functions with the name DECL
+ and type CTYPE know that they are friends of TYPE. */
+static void
+xref_friend (type, decl, ctype)
+ tree type, decl, ctype;
+{
+ tree friend_decl = TYPE_NAME (ctype);
+#if 0
+ tree typedecl = TYPE_NAME (type);
+ tree t = tree_cons (NULL_TREE, ctype, DECL_UNDEFINED_FRIENDS (typedecl));
+
+ DECL_UNDEFINED_FRIENDS (typedecl) = t;
+#else
+ tree t = 0;
+#endif
+ SET_DECL_WAITING_FRIENDS (friend_decl,
+ tree_cons (type, t,
+ DECL_WAITING_FRIENDS (friend_decl)));
+ TREE_TYPE (DECL_WAITING_FRIENDS (friend_decl)) = decl;
+}
+
+/* Make FRIEND_TYPE a friend class to TYPE. If FRIEND_TYPE has already
+ been defined, we make all of its member functions friends of
+ TYPE. If not, we make it a pending friend, which can later be added
+ when its definition is seen. If a type is defined, then its TYPE_DECL's
+ DECL_UNDEFINED_FRIENDS contains a (possibly empty) list of friend
+ classes that are not defined. If a type has not yet been defined,
+ then the DECL_WAITING_FRIENDS contains a list of types
+ waiting to make it their friend. Note that these two can both
+ be in use at the same time! */
+void
+make_friend_class (type, friend_type)
+ tree type, friend_type;
+{
+ tree classes;
+
+ if (IS_SIGNATURE (type))
+ {
+ error ("`friend' declaration in signature definition");
+ return;
+ }
+ if (IS_SIGNATURE (friend_type))
+ {
+ error ("signature type `%s' declared `friend'",
+ IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (friend_type))));
+ return;
+ }
+ if (type == friend_type)
+ {
+ warning ("class `%s' is implicitly friends with itself",
+ TYPE_NAME_STRING (type));
+ return;
+ }
+
+ GNU_xref_hier (TYPE_NAME_STRING (type),
+ TYPE_NAME_STRING (friend_type), 0, 0, 1);
+
+ classes = CLASSTYPE_FRIEND_CLASSES (type);
+ while (classes && TREE_VALUE (classes) != friend_type)
+ classes = TREE_CHAIN (classes);
+ if (classes)
+ warning ("class `%s' is already friends with class `%s'",
+ TYPE_NAME_STRING (TREE_VALUE (classes)), TYPE_NAME_STRING (type));
+ else
+ {
+ CLASSTYPE_FRIEND_CLASSES (type)
+ = tree_cons (NULL_TREE, friend_type, CLASSTYPE_FRIEND_CLASSES (type));
+ }
+}
+
+/* Main friend processor. This is large, and for modularity purposes,
+ has been removed from grokdeclarator. It returns `void_type_node'
+ to indicate that something happened, though a FIELD_DECL is
+ not returned.
+
+ CTYPE is the class this friend belongs to.
+
+ DECLARATOR is the name of the friend.
+
+ DECL is the FUNCTION_DECL that the friend is.
+
+ In case we are parsing a friend which is part of an inline
+ definition, we will need to store PARM_DECL chain that comes
+ with it into the DECL_ARGUMENTS slot of the FUNCTION_DECL.
+
+ FLAGS is just used for `grokclassfn'.
+
+ QUALS say what special qualifies should apply to the object
+ pointed to by `this'. */
+tree
+do_friend (ctype, declarator, decl, parmdecls, flags, quals)
+ tree ctype, declarator, decl, parmdecls;
+ enum overload_flags flags;
+ tree quals;
+{
+ /* Every decl that gets here is a friend of something. */
+ DECL_FRIEND_P (decl) = 1;
+
+ if (ctype)
+ {
+ tree cname = TYPE_NAME (ctype);
+ if (TREE_CODE (cname) == TYPE_DECL)
+ cname = DECL_NAME (cname);
+
+ /* A method friend. */
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ if (flags == NO_SPECIAL && ctype && declarator == cname)
+ DECL_CONSTRUCTOR_P (decl) = 1;
+
+ /* This will set up DECL_ARGUMENTS for us. */
+ grokclassfn (ctype, cname, decl, flags, quals);
+ if (TYPE_SIZE (ctype) != 0)
+ check_classfn (ctype, cname, decl);
+
+ if (TREE_TYPE (decl) != error_mark_node)
+ {
+ if (TYPE_SIZE (ctype))
+ {
+ /* We don't call pushdecl here yet, or ever on this
+ actual FUNCTION_DECL. We must preserve its TREE_CHAIN
+ until the end. */
+ make_decl_rtl (decl, NULL_PTR, 1);
+ add_friend (current_class_type, decl);
+ }
+ else
+ {
+ register char *classname
+ = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (ctype)));
+
+ error ("member declared as friend before type `%s' defined",
+ classname);
+ }
+ }
+ }
+ else
+ {
+ /* Possibly a bunch of method friends. */
+
+ /* Get the class they belong to. */
+ tree ctype = IDENTIFIER_TYPE_VALUE (cname);
+
+ /* This class is defined, use its methods now. */
+ if (TYPE_SIZE (ctype))
+ {
+ tree fields = lookup_fnfields (TYPE_BINFO (ctype), declarator, 0);
+ if (fields)
+ add_friends (current_class_type, declarator, ctype);
+ else
+ error ("method `%s' is not a member of class `%s'",
+ IDENTIFIER_POINTER (declarator),
+ IDENTIFIER_POINTER (cname));
+ }
+ else
+ /* Note: DECLARATOR actually has more than one; in this
+ case, we're making sure that fns with the name DECLARATOR
+ and type CTYPE know they are friends of the current
+ class type. */
+ xref_friend (current_class_type, declarator, ctype);
+ decl = void_type_node;
+ }
+ }
+ else if (TREE_CODE (decl) == FUNCTION_DECL
+ && ((IDENTIFIER_LENGTH (declarator) == 4
+ && IDENTIFIER_POINTER (declarator)[0] == 'm'
+ && ! strcmp (IDENTIFIER_POINTER (declarator), "main"))
+ || (IDENTIFIER_LENGTH (declarator) > 10
+ && IDENTIFIER_POINTER (declarator)[0] == '_'
+ && IDENTIFIER_POINTER (declarator)[1] == '_'
+ && strncmp (IDENTIFIER_POINTER (declarator)+2,
+ "builtin_", 8) == 0)))
+ {
+ /* raw "main", and builtin functions never gets overloaded,
+ but they can become friends. */
+ TREE_PUBLIC (decl) = 1;
+ add_friend (current_class_type, decl);
+ DECL_FRIEND_P (decl) = 1;
+ decl = void_type_node;
+ }
+ /* A global friend.
+ @@ or possibly a friend from a base class ?!? */
+ else if (TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ /* Friends must all go through the overload machinery,
+ even though they may not technically be overloaded.
+
+ Note that because classes all wind up being top-level
+ in their scope, their friend wind up in top-level scope as well. */
+ DECL_ASSEMBLER_NAME (decl)
+ = build_decl_overload (declarator, TYPE_ARG_TYPES (TREE_TYPE (decl)),
+ TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE);
+ DECL_ARGUMENTS (decl) = parmdecls;
+ DECL_CLASS_CONTEXT (decl) = current_class_type;
+
+ /* We can call pushdecl here, because the TREE_CHAIN of this
+ FUNCTION_DECL is not needed for other purposes. */
+ decl = pushdecl (decl);
+
+ make_decl_rtl (decl, NULL_PTR, 1);
+ add_friend (current_class_type, decl);
+
+ DECL_FRIEND_P (decl) = 1;
+#if 0
+ TREE_OVERLOADED (declarator) = 1;
+#endif
+ }
+ else
+ {
+ /* @@ Should be able to ingest later definitions of this function
+ before use. */
+ tree decl = lookup_name_nonclass (declarator);
+ if (decl == NULL_TREE)
+ {
+ warning ("implicitly declaring `%s' as struct",
+ IDENTIFIER_POINTER (declarator));
+ decl = xref_tag (record_type_node, declarator, NULL_TREE, 1);
+ decl = TYPE_NAME (decl);
+ }
+
+ /* Allow abbreviated declarations of overloaded functions,
+ but not if those functions are really class names. */
+ if (TREE_CODE (decl) == TREE_LIST && TREE_TYPE (TREE_PURPOSE (decl)))
+ {
+ warning ("`friend %s' archaic, use `friend class %s' instead",
+ IDENTIFIER_POINTER (declarator),
+ IDENTIFIER_POINTER (declarator));
+ decl = TREE_TYPE (TREE_PURPOSE (decl));
+ }
+
+ if (TREE_CODE (decl) == TREE_LIST)
+ add_friends (current_class_type, TREE_PURPOSE (decl), NULL_TREE);
+ else
+ make_friend_class (current_class_type, TREE_TYPE (decl));
+ decl = void_type_node;
+ }
+ return decl;
+}
+
+/* TYPE has now been defined. It may, however, have a number of things
+ waiting make make it their friend. We resolve these references
+ here. */
+void
+embrace_waiting_friends (type)
+ tree type;
+{
+ tree decl = TYPE_NAME (type);
+ tree waiters;
+
+ if (TREE_CODE (decl) != TYPE_DECL)
+ return;
+
+ for (waiters = DECL_WAITING_FRIENDS (decl); waiters;
+ waiters = TREE_CHAIN (waiters))
+ {
+ tree waiter = TREE_PURPOSE (waiters);
+#if 0
+ tree waiter_prev = TREE_VALUE (waiters);
+#endif
+ tree decl = TREE_TYPE (waiters);
+ tree name = decl ? (TREE_CODE (decl) == IDENTIFIER_NODE
+ ? decl : DECL_NAME (decl)) : NULL_TREE;
+ if (name)
+ {
+ /* @@ There may be work to be done since we have not verified
+ @@ consistency between original and friend declarations
+ @@ of the functions waiting to become friends. */
+ tree field = lookup_fnfields (TYPE_BINFO (type), name, 0);
+ if (field)
+ if (decl == name)
+ add_friends (waiter, name, type);
+ else
+ add_friend (waiter, decl);
+ else
+ error_with_file_and_line (DECL_SOURCE_FILE (TYPE_NAME (waiter)),
+ DECL_SOURCE_LINE (TYPE_NAME (waiter)),
+ "no method `%s' defined in class `%s' to be friend",
+ IDENTIFIER_POINTER (DECL_NAME (TREE_TYPE (waiters))),
+ TYPE_NAME_STRING (type));
+ }
+ else
+ make_friend_class (type, waiter);
+
+#if 0
+ if (TREE_CHAIN (waiter_prev))
+ TREE_CHAIN (waiter_prev) = TREE_CHAIN (TREE_CHAIN (waiter_prev));
+ else
+ DECL_UNDEFINED_FRIENDS (TYPE_NAME (waiter)) = NULL_TREE;
+#endif
+ }
+}
+
+/* Common subroutines of build_new and build_vec_delete. */
+
+/* Common interface for calling "builtin" functions that are not
+ really builtin. */
+
+tree
+build_builtin_call (type, node, arglist)
+ tree type;
+ tree node;
+ tree arglist;
+{
+ tree rval = build (CALL_EXPR, type, node, arglist, 0);
+ TREE_SIDE_EFFECTS (rval) = 1;
+ assemble_external (TREE_OPERAND (node, 0));
+ TREE_USED (TREE_OPERAND (node, 0)) = 1;
+ return rval;
+}
+
+/* Generate a C++ "new" expression. DECL is either a TREE_LIST
+ (which needs to go through some sort of groktypename) or it
+ is the name of the class we are newing. INIT is an initialization value.
+ It is either an EXPRLIST, an EXPR_NO_COMMAS, or something in braces.
+ If INIT is void_type_node, it means do *not* call a constructor
+ for this instance.
+
+ For types with constructors, the data returned is initialized
+ by the appropriate constructor.
+
+ Whether the type has a constructor or not, if it has a pointer
+ to a virtual function table, then that pointer is set up
+ here.
+
+ Unless I am mistaken, a call to new () will return initialized
+ data regardless of whether the constructor itself is private or
+ not. NOPE; new fails if the constructor is private (jcm).
+
+ Note that build_new does nothing to assure that any special
+ alignment requirements of the type are met. Rather, it leaves
+ it up to malloc to do the right thing. Otherwise, folding to
+ the right alignment cal cause problems if the user tries to later
+ free the memory returned by `new'.
+
+ PLACEMENT is the `placement' list for user-defined operator new (). */
+
+tree
+build_new (placement, decl, init, use_global_new)
+ tree placement;
+ tree decl, init;
+ int use_global_new;
+{
+ tree type, true_type, size, rval;
+ tree nelts;
+ int has_array = 0;
+ enum tree_code code = NEW_EXPR;
+
+ tree pending_sizes = NULL_TREE;
+
+ if (decl == error_mark_node)
+ return error_mark_node;
+
+ if (TREE_CODE (decl) == TREE_LIST)
+ {
+ tree absdcl = TREE_VALUE (decl);
+ tree last_absdcl = NULL_TREE;
+ int old_immediate_size_expand;
+
+ if (current_function_decl
+ && DECL_CONSTRUCTOR_P (current_function_decl))
+ {
+ old_immediate_size_expand = immediate_size_expand;
+ immediate_size_expand = 0;
+ }
+
+ nelts = integer_one_node;
+
+ if (absdcl && TREE_CODE (absdcl) == CALL_EXPR)
+ my_friendly_abort (215);
+ while (absdcl && TREE_CODE (absdcl) == INDIRECT_REF)
+ {
+ last_absdcl = absdcl;
+ absdcl = TREE_OPERAND (absdcl, 0);
+ }
+
+ if (absdcl && TREE_CODE (absdcl) == ARRAY_REF)
+ {
+ /* probably meant to be a vec new */
+ tree this_nelts;
+
+ while (TREE_OPERAND (absdcl, 0)
+ && TREE_CODE (TREE_OPERAND (absdcl, 0)) == ARRAY_REF)
+ {
+ last_absdcl = absdcl;
+ absdcl = TREE_OPERAND (absdcl, 0);
+ }
+
+ has_array = 1;
+ this_nelts = TREE_OPERAND (absdcl, 1);
+ if (this_nelts != error_mark_node)
+ {
+ if (this_nelts == NULL_TREE)
+ error ("new of array type fails to specify size");
+ else
+ {
+ this_nelts = save_expr (convert (sizetype, this_nelts));
+ absdcl = TREE_OPERAND (absdcl, 0);
+ if (this_nelts == integer_zero_node)
+ {
+ warning ("zero size array reserves no space");
+ nelts = integer_zero_node;
+ }
+ else
+ nelts = build_binary_op (MULT_EXPR, nelts, this_nelts, 1);
+ }
+ }
+ else
+ nelts = integer_zero_node;
+ }
+
+ if (last_absdcl)
+ TREE_OPERAND (last_absdcl, 0) = absdcl;
+ else
+ TREE_VALUE (decl) = absdcl;
+
+ type = true_type = groktypename (decl);
+ if (! type || type == error_mark_node)
+ {
+ immediate_size_expand = old_immediate_size_expand;
+ return error_mark_node;
+ }
+
+ if (current_function_decl
+ && DECL_CONSTRUCTOR_P (current_function_decl))
+ {
+ pending_sizes = get_pending_sizes ();
+ immediate_size_expand = old_immediate_size_expand;
+ }
+ }
+ else if (TREE_CODE (decl) == IDENTIFIER_NODE)
+ {
+ if (IDENTIFIER_HAS_TYPE_VALUE (decl))
+ {
+ /* An aggregate type. */
+ type = IDENTIFIER_TYPE_VALUE (decl);
+ decl = TYPE_NAME (type);
+ }
+ else
+ {
+ /* A builtin type. */
+ decl = lookup_name (decl, 1);
+ my_friendly_assert (TREE_CODE (decl) == TYPE_DECL, 215);
+ type = TREE_TYPE (decl);
+ }
+ true_type = type;
+ }
+ else if (TREE_CODE (decl) == TYPE_DECL)
+ {
+ type = TREE_TYPE (decl);
+ true_type = type;
+ }
+ else
+ {
+ type = decl;
+ true_type = type;
+ decl = TYPE_NAME (type);
+ }
+
+ /* ``A reference cannot be created by the new operator. A reference
+ is not an object (8.2.2, 8.4.3), so a pointer to it could not be
+ returned by new.'' ARM 5.3.3 */
+ if (TREE_CODE (type) == REFERENCE_TYPE)
+ {
+ error ("new cannot be applied to a reference type");
+ type = true_type = TREE_TYPE (type);
+ }
+
+ /* When the object being created is an array, the new-expression yields a
+ pointer to the initial element (if any) of the array. For example,
+ both new int and new int[10] return an int*. 5.3.4. */
+ if (TREE_CODE (type) == ARRAY_TYPE && has_array == 0)
+ {
+ nelts = array_type_nelts_top (type);
+ has_array = 1;
+ type = true_type = TREE_TYPE (type);
+ }
+
+ if (TYPE_READONLY (type) || TYPE_VOLATILE (type))
+ {
+ pedwarn ("const and volatile types cannot be created with operator new");
+ type = true_type = TYPE_MAIN_VARIANT (type);
+ }
+
+ /* If our base type is an array, then make sure we know how many elements
+ it has. */
+ while (TREE_CODE (true_type) == ARRAY_TYPE)
+ {
+ tree this_nelts = array_type_nelts_top (true_type);
+ nelts = build_binary_op (MULT_EXPR, nelts, this_nelts, 1);
+ true_type = TREE_TYPE (true_type);
+ }
+ if (has_array)
+ size = fold (build_binary_op (MULT_EXPR, size_in_bytes (true_type),
+ nelts, 1));
+ else
+ size = size_in_bytes (type);
+
+ if (TYPE_SIZE (true_type) == 0)
+ {
+ if (true_type == void_type_node)
+ error ("invalid type for new: `void'");
+ else
+ incomplete_type_error (0, true_type);
+ return error_mark_node;
+ }
+
+ if (TYPE_LANG_SPECIFIC (true_type)
+ && CLASSTYPE_ABSTRACT_VIRTUALS (true_type))
+ {
+ abstract_virtuals_error (NULL_TREE, true_type);
+ return error_mark_node;
+ }
+
+ if (TYPE_LANG_SPECIFIC (true_type) && IS_SIGNATURE (true_type))
+ {
+ signature_error (NULL_TREE, true_type);
+ return error_mark_node;
+ }
+
+ /* Get a little extra space to store a couple of things before the new'ed
+ array. */
+ if (has_array && TYPE_VEC_NEW_USES_COOKIE (true_type))
+ {
+ tree extra = BI_header_size;
+
+ size = size_binop (PLUS_EXPR, size, extra);
+ }
+
+ if (has_array)
+ code = VEC_NEW_EXPR;
+
+ /* Allocate the object. */
+ if (! use_global_new && TYPE_LANG_SPECIFIC (true_type)
+ && (TYPE_GETS_NEW (true_type) & (1 << has_array)))
+ rval = build_opfncall (code, LOOKUP_NORMAL,
+ TYPE_POINTER_TO (true_type), size, placement);
+ else if (placement)
+ {
+ rval = build_opfncall (code, LOOKUP_GLOBAL|LOOKUP_COMPLAIN,
+ ptr_type_node, size, placement);
+ rval = convert (TYPE_POINTER_TO (true_type), rval);
+ }
+ else if (! has_array && flag_this_is_variable > 0
+ && TYPE_HAS_CONSTRUCTOR (true_type) && init != void_type_node)
+ {
+ if (init == NULL_TREE || TREE_CODE (init) == TREE_LIST)
+ rval = NULL_TREE;
+ else
+ {
+ error ("constructors take parameter lists");
+ return error_mark_node;
+ }
+ }
+ else
+ {
+ rval = build_builtin_call (build_pointer_type (true_type),
+ has_array ? BIVN : BIN,
+ build_tree_list (NULL_TREE, size));
+#if 0
+ /* See comment above as to why this is disabled. */
+ if (alignment)
+ {
+ rval = build (PLUS_EXPR, TYPE_POINTER_TO (true_type), rval,
+ alignment);
+ rval = build (BIT_AND_EXPR, TYPE_POINTER_TO (true_type),
+ rval, build1 (BIT_NOT_EXPR, integer_type_node,
+ alignment));
+ }
+#endif
+ TREE_CALLS_NEW (rval) = 1;
+ }
+
+ /* if rval is NULL_TREE I don't have to allocate it, but are we totally
+ sure we have some extra bytes in that case for the BI_header_size
+ cookies? And how does that interact with the code below? (mrs) */
+ /* Finish up some magic for new'ed arrays */
+ if (has_array && TYPE_VEC_NEW_USES_COOKIE (true_type) && rval != NULL_TREE)
+ {
+ tree extra = BI_header_size;
+ tree cookie, exp1;
+ rval = convert (ptr_type_node, rval); /* convert to void * first */
+ rval = convert (string_type_node, rval); /* lets not add void* and ints */
+ rval = save_expr (build_binary_op (PLUS_EXPR, rval, extra, 1));
+ /* Store header info. */
+ cookie = build_indirect_ref (build (MINUS_EXPR, TYPE_POINTER_TO (BI_header_type),
+ rval, extra), NULL_PTR);
+ exp1 = build (MODIFY_EXPR, void_type_node,
+ build_component_ref (cookie, nc_nelts_field_id, 0, 0),
+ nelts);
+ TREE_SIDE_EFFECTS (exp1) = 1;
+ rval = convert (build_pointer_type (true_type), rval);
+ TREE_CALLS_NEW (rval) = 1;
+ TREE_SIDE_EFFECTS (rval) = 1;
+ rval = build_compound_expr (tree_cons (NULL_TREE, exp1,
+ build_tree_list (NULL_TREE, rval)));
+ }
+
+ /* We've figured out where the allocation is to go.
+ If we're not eliding constructors, then if a constructor
+ is defined, we must go through it. */
+ if (!has_array && (rval == NULL_TREE || !flag_elide_constructors)
+ && TYPE_HAS_CONSTRUCTOR (true_type) && init != void_type_node)
+ {
+ tree newrval;
+ /* Constructors are never virtual. If it has an initialization, we
+ need to complain if we aren't allowed to use the ctor that took
+ that argument. */
+ int flags = LOOKUP_NORMAL|LOOKUP_NONVIRTUAL|LOOKUP_COMPLAIN;
+
+ /* If a copy constructor might work, set things up so that we can
+ try that after this. We deliberately don't clear LOOKUP_COMPLAIN
+ any more, since that would make it impossible to rationally use
+ the access of a constructor that matches perfectly. */
+#if 0
+ if (rval != NULL_TREE)
+ flags |= LOOKUP_SPECULATIVELY;
+#endif
+
+ if (rval && TYPE_USES_VIRTUAL_BASECLASSES (true_type))
+ {
+ init = tree_cons (NULL_TREE, integer_one_node, init);
+ flags |= LOOKUP_HAS_IN_CHARGE;
+ }
+
+ {
+ tree tmp = rval;
+
+ if (TREE_CODE (TREE_TYPE (tmp)) == POINTER_TYPE)
+ tmp = build_indirect_ref (tmp, NULL_PTR);
+
+ newrval = build_method_call (tmp, constructor_name_full (true_type),
+ init, NULL_TREE, flags);
+ }
+
+ if (newrval)
+ {
+ rval = newrval;
+ TREE_HAS_CONSTRUCTOR (rval) = 1;
+ }
+ else
+ rval = error_mark_node;
+ goto done;
+ }
+
+ if (rval == error_mark_node)
+ return error_mark_node;
+ rval = save_expr (rval);
+ TREE_HAS_CONSTRUCTOR (rval) = 1;
+
+ /* Don't call any constructors or do any initialization. */
+ if (init == void_type_node)
+ goto done;
+
+ if (TYPE_NEEDS_CONSTRUCTING (type) || init)
+ {
+ if (! TYPE_NEEDS_CONSTRUCTING (type) && ! IS_AGGR_TYPE (type))
+ {
+ /* New 2.0 interpretation: `new int (10)' means
+ allocate an int, and initialize it with 10. */
+
+ init = build_c_cast (type, init);
+ rval = build (COMPOUND_EXPR, TREE_TYPE (rval),
+ build_modify_expr (build_indirect_ref (rval, NULL_PTR),
+ NOP_EXPR, init),
+ rval);
+ TREE_SIDE_EFFECTS (rval) = 1;
+ TREE_CALLS_NEW (rval) = 1;
+ }
+ else if (current_function_decl == NULL_TREE)
+ {
+ extern tree static_aggregates;
+
+ /* In case of static initialization, SAVE_EXPR is good enough. */
+ init = copy_to_permanent (init);
+ rval = copy_to_permanent (rval);
+ static_aggregates = perm_tree_cons (init, rval, static_aggregates);
+ }
+ else
+ {
+ /* Have to wrap this in RTL_EXPR for two cases:
+ in base or member initialization and if we
+ are a branch of a ?: operator. Since we
+ can't easily know the latter, just do it always. */
+ tree xval = make_node (RTL_EXPR);
+
+ TREE_TYPE (xval) = TREE_TYPE (rval);
+ do_pending_stack_adjust ();
+ start_sequence_for_rtl_expr (xval);
+
+ /* As a matter of principle, `start_sequence' should do this. */
+ emit_note (0, -1);
+
+ if (has_array)
+ rval = expand_vec_init (decl, rval,
+ build_binary_op (MINUS_EXPR, nelts, integer_one_node, 1),
+ init, 0);
+ else
+ expand_aggr_init (build_indirect_ref (rval, NULL_PTR), init, 0);
+
+ do_pending_stack_adjust ();
+
+ TREE_SIDE_EFFECTS (xval) = 1;
+ TREE_CALLS_NEW (xval) = 1;
+ RTL_EXPR_SEQUENCE (xval) = get_insns ();
+ end_sequence ();
+
+ if (TREE_CODE (rval) == SAVE_EXPR)
+ {
+ /* Errors may cause this to not get evaluated. */
+ if (SAVE_EXPR_RTL (rval) == 0)
+ SAVE_EXPR_RTL (rval) = const0_rtx;
+ RTL_EXPR_RTL (xval) = SAVE_EXPR_RTL (rval);
+ }
+ else
+ {
+ my_friendly_assert (TREE_CODE (rval) == VAR_DECL, 217);
+ RTL_EXPR_RTL (xval) = DECL_RTL (rval);
+ }
+ rval = xval;
+ }
+ }
+ done:
+ if (rval && TREE_TYPE (rval) != build_pointer_type (type))
+ {
+ /* The type of new int [3][3] is not int *, but int [3] * */
+ rval = build_c_cast (build_pointer_type (type), rval);
+ }
+
+ if (pending_sizes)
+ rval = build_compound_expr (chainon (pending_sizes,
+ build_tree_list (NULL_TREE, rval)));
+
+ if (flag_gc)
+ {
+ extern tree gc_visible;
+ tree objbits;
+ tree update_expr;
+
+ rval = save_expr (rval);
+ /* We don't need a `headof' operation to do this because
+ we know where the object starts. */
+ objbits = build1 (INDIRECT_REF, unsigned_type_node,
+ build (MINUS_EXPR, ptr_type_node,
+ rval, c_sizeof_nowarn (unsigned_type_node)));
+ update_expr = build_modify_expr (objbits, BIT_IOR_EXPR, gc_visible);
+ rval = build_compound_expr (tree_cons (NULL_TREE, rval,
+ tree_cons (NULL_TREE, update_expr,
+ build_tree_list (NULL_TREE, rval))));
+ }
+
+ return save_expr (rval);
+}
+
+/* `expand_vec_init' performs initialization of a vector of aggregate
+ types.
+
+ DECL is passed only for error reporting, and provides line number
+ and source file name information.
+ BASE is the space where the vector will be.
+ MAXINDEX is the maximum index of the array (one less than the
+ number of elements).
+ INIT is the (possibly NULL) initializer.
+
+ FROM_ARRAY is 0 if we should init everything with INIT
+ (i.e., every element initialized from INIT).
+ FROM_ARRAY is 1 if we should index into INIT in parallel
+ with initialization of DECL.
+ FROM_ARRAY is 2 if we should index into INIT in parallel,
+ but use assignment instead of initialization. */
+
+tree
+expand_vec_init (decl, base, maxindex, init, from_array)
+ tree decl, base, maxindex, init;
+ int from_array;
+{
+ tree rval;
+ tree iterator, base2 = NULL_TREE;
+ tree type = TREE_TYPE (TREE_TYPE (base));
+ tree size;
+
+ maxindex = convert (integer_type_node, maxindex);
+ if (maxindex == error_mark_node)
+ return error_mark_node;
+
+ if (current_function_decl == NULL_TREE)
+ {
+ rval = make_tree_vec (3);
+ TREE_VEC_ELT (rval, 0) = base;
+ TREE_VEC_ELT (rval, 1) = maxindex;
+ TREE_VEC_ELT (rval, 2) = init;
+ return rval;
+ }
+
+ size = size_in_bytes (type);
+
+ /* Set to zero in case size is <= 0. Optimizer will delete this if
+ it is not needed. */
+ rval = get_temp_regvar (TYPE_POINTER_TO (type),
+ convert (TYPE_POINTER_TO (type), null_pointer_node));
+ base = default_conversion (base);
+ base = convert (TYPE_POINTER_TO (type), base);
+ expand_assignment (rval, base, 0, 0);
+ base = get_temp_regvar (TYPE_POINTER_TO (type), base);
+
+ if (init != NULL_TREE
+ && TREE_CODE (init) == CONSTRUCTOR
+ && TREE_TYPE (init) == TREE_TYPE (decl))
+ {
+ /* Initialization of array from {...}. */
+ tree elts = CONSTRUCTOR_ELTS (init);
+ tree baseref = build1 (INDIRECT_REF, type, base);
+ tree baseinc = build (PLUS_EXPR, TYPE_POINTER_TO (type), base, size);
+ int host_i = TREE_INT_CST_LOW (maxindex);
+
+ if (IS_AGGR_TYPE (type))
+ {
+ while (elts)
+ {
+ host_i -= 1;
+ expand_aggr_init (baseref, TREE_VALUE (elts), 0);
+
+ expand_assignment (base, baseinc, 0, 0);
+ elts = TREE_CHAIN (elts);
+ }
+ /* Initialize any elements by default if possible. */
+ if (host_i >= 0)
+ {
+ if (TYPE_NEEDS_CONSTRUCTING (type) == 0)
+ {
+ if (obey_regdecls)
+ use_variable (DECL_RTL (base));
+ goto done_init;
+ }
+
+ iterator = get_temp_regvar (integer_type_node,
+ build_int_2 (host_i, 0));
+ init = NULL_TREE;
+ goto init_by_default;
+ }
+ }
+ else
+ while (elts)
+ {
+ expand_assignment (baseref, TREE_VALUE (elts), 0, 0);
+
+ expand_assignment (base, baseinc, 0, 0);
+ elts = TREE_CHAIN (elts);
+ }
+
+ if (obey_regdecls)
+ use_variable (DECL_RTL (base));
+ }
+ else
+ {
+ tree itype;
+
+ iterator = get_temp_regvar (integer_type_node, maxindex);
+
+ init_by_default:
+
+ /* If initializing one array from another,
+ initialize element by element. */
+ if (from_array)
+ {
+ /* We rely upon the below calls the do argument checking */
+ if (decl == NULL_TREE)
+ {
+ sorry ("initialization of array from dissimilar array type");
+ return error_mark_node;
+ }
+ if (init)
+ {
+ base2 = default_conversion (init);
+ itype = TREE_TYPE (base2);
+ base2 = get_temp_regvar (itype, base2);
+ itype = TREE_TYPE (itype);
+ }
+ else if (TYPE_LANG_SPECIFIC (type)
+ && TYPE_NEEDS_CONSTRUCTING (type)
+ && ! TYPE_HAS_DEFAULT_CONSTRUCTOR (type))
+ {
+ error ("initializer ends prematurely");
+ return error_mark_node;
+ }
+ }
+
+ expand_start_cond (build (GE_EXPR, integer_type_node,
+ iterator, integer_zero_node), 0);
+ expand_start_loop_continue_elsewhere (1);
+
+ if (from_array)
+ {
+ tree to = build1 (INDIRECT_REF, type, base);
+ tree from;
+
+ if (base2)
+ from = build1 (INDIRECT_REF, itype, base2);
+ else
+ from = NULL_TREE;
+
+ if (from_array == 2)
+ expand_expr_stmt (build_modify_expr (to, NOP_EXPR, from));
+ else if (TYPE_NEEDS_CONSTRUCTING (type))
+ expand_aggr_init (to, from, 0);
+ else if (from)
+ expand_assignment (to, from, 0, 0);
+ else
+ my_friendly_abort (57);
+ }
+ else if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ if (init != 0)
+ sorry ("cannot initialize multi-dimensional array with initializer");
+ expand_vec_init (decl, build1 (NOP_EXPR, TYPE_POINTER_TO (TREE_TYPE (type)), base),
+ array_type_nelts (type), 0, 0);
+ }
+ else
+ expand_aggr_init (build1 (INDIRECT_REF, type, base), init, 0);
+
+ expand_assignment (base,
+ build (PLUS_EXPR, TYPE_POINTER_TO (type), base, size),
+ 0, 0);
+ if (base2)
+ expand_assignment (base2,
+ build (PLUS_EXPR, TYPE_POINTER_TO (type), base2, size), 0, 0);
+ expand_loop_continue_here ();
+ expand_exit_loop_if_false (0, build (NE_EXPR, integer_type_node,
+ build (PREDECREMENT_EXPR, integer_type_node, iterator, integer_one_node), minus_one));
+
+ if (obey_regdecls)
+ {
+ use_variable (DECL_RTL (base));
+ if (base2)
+ use_variable (DECL_RTL (base2));
+ }
+ expand_end_loop ();
+ expand_end_cond ();
+ if (obey_regdecls)
+ use_variable (DECL_RTL (iterator));
+ }
+ done_init:
+
+ if (obey_regdecls)
+ use_variable (DECL_RTL (rval));
+ return rval;
+}
+
+/* Free up storage of type TYPE, at address ADDR.
+
+ TYPE is a POINTER_TYPE and can be ptr_type_node for no special type
+ of pointer.
+
+ VIRTUAL_SIZE is the amount of storage that was allocated, and is
+ used as the second argument to operator delete. It can include
+ things like padding and magic size cookies. It has virtual in it,
+ because if you have a base pointer and you delete through a virtual
+ destructor, it should be the size of the dynamic object, not the
+ static object, see Free Store 12.5 ANSI C++ WP.
+
+ This does not call any destructors. */
+tree
+build_x_delete (type, addr, which_delete, virtual_size)
+ tree type, addr;
+ int which_delete;
+ tree virtual_size;
+{
+ int use_global_delete = which_delete & 1;
+ int use_vec_delete = !!(which_delete & 2);
+ tree rval;
+ enum tree_code code = use_vec_delete ? VEC_DELETE_EXPR : DELETE_EXPR;
+
+ if (! use_global_delete && TYPE_LANG_SPECIFIC (TREE_TYPE (type))
+ && (TYPE_GETS_DELETE (TREE_TYPE (type)) & (1 << use_vec_delete)))
+ rval = build_opfncall (code, LOOKUP_NORMAL, addr, virtual_size, NULL_TREE);
+ else
+ rval = build_builtin_call (void_type_node, use_vec_delete ? BIVD : BID,
+ build_tree_list (NULL_TREE, addr));
+ return rval;
+}
+
+/* Generate a call to a destructor. TYPE is the type to cast ADDR to.
+ ADDR is an expression which yields the store to be destroyed.
+ AUTO_DELETE is nonzero if a call to DELETE should be made or not.
+ If in the program, (AUTO_DELETE & 2) is non-zero, we tear down the
+ virtual baseclasses.
+ If in the program, (AUTO_DELETE & 1) is non-zero, then we deallocate.
+
+ FLAGS is the logical disjunction of zero or more LOOKUP_
+ flags. See cp-tree.h for more info.
+
+ This function does not delete an object's virtual base classes. */
+tree
+build_delete (type, addr, auto_delete, flags, use_global_delete)
+ tree type, addr;
+ tree auto_delete;
+ int flags;
+ int use_global_delete;
+{
+ tree function, parms;
+ tree member;
+ tree expr;
+ tree ref;
+ int ptr;
+
+ if (addr == error_mark_node)
+ return error_mark_node;
+
+ /* Can happen when CURRENT_EXCEPTION_OBJECT gets its type
+ set to `error_mark_node' before it gets properly cleaned up. */
+ if (type == error_mark_node)
+ return error_mark_node;
+
+ type = TYPE_MAIN_VARIANT (type);
+
+ if (TREE_CODE (type) == POINTER_TYPE)
+ {
+ type = TYPE_MAIN_VARIANT (TREE_TYPE (type));
+ if (TYPE_SIZE (type) == 0)
+ {
+ incomplete_type_error (0, type);
+ return error_mark_node;
+ }
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ goto handle_array;
+ if (! IS_AGGR_TYPE (type))
+ {
+ /* Call the builtin operator delete. */
+ return build_builtin_call (void_type_node, BID,
+ build_tree_list (NULL_TREE, addr));
+ }
+ if (TREE_SIDE_EFFECTS (addr))
+ addr = save_expr (addr);
+
+ /* throw away const and volatile on target type of addr */
+ addr = convert_force (build_pointer_type (type), addr);
+ ref = build_indirect_ref (addr, NULL_PTR);
+ ptr = 1;
+ }
+ else if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ handle_array:
+ if (TREE_SIDE_EFFECTS (addr))
+ addr = save_expr (addr);
+ return build_vec_delete (addr, array_type_nelts (type),
+ c_sizeof_nowarn (TREE_TYPE (type)),
+ auto_delete, integer_two_node,
+ use_global_delete);
+ }
+ else
+ {
+ /* Don't check PROTECT here; leave that decision to the
+ destructor. If the destructor is accessible, call it,
+ else report error. */
+ addr = build_unary_op (ADDR_EXPR, addr, 0);
+ if (TREE_SIDE_EFFECTS (addr))
+ addr = save_expr (addr);
+
+ if (TREE_CONSTANT (addr))
+ addr = convert_pointer_to (type, addr);
+ else
+ addr = convert_force (build_pointer_type (type), addr);
+
+ if (TREE_CODE (addr) == NOP_EXPR
+ && TREE_OPERAND (addr, 0) == current_class_decl)
+ ref = C_C_D;
+ else
+ ref = build_indirect_ref (addr, NULL_PTR);
+ ptr = 0;
+ }
+
+ my_friendly_assert (IS_AGGR_TYPE (type), 220);
+
+ if (! TYPE_NEEDS_DESTRUCTOR (type))
+ {
+ if (auto_delete == integer_zero_node)
+ return void_zero_node;
+
+ /* Pass the size of the object down to the operator delete() in
+ addition to the ADDR. */
+ if (TYPE_GETS_REG_DELETE (type) && !use_global_delete)
+ {
+ tree virtual_size = c_sizeof_nowarn (type);
+ return build_opfncall (DELETE_EXPR, LOOKUP_NORMAL, addr,
+ virtual_size, NULL_TREE);
+ }
+
+ /* Call the builtin operator delete. */
+ return build_builtin_call (void_type_node, BID,
+ build_tree_list (NULL_TREE, addr));
+ }
+ parms = build_tree_list (NULL_TREE, addr);
+
+ /* Below, we will reverse the order in which these calls are made.
+ If we have a destructor, then that destructor will take care
+ of the base classes; otherwise, we must do that here. */
+ if (TYPE_HAS_DESTRUCTOR (type))
+ {
+ tree dtor = DECL_MAIN_VARIANT (TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type), 0));
+ tree basetypes = TYPE_BINFO (type);
+ tree passed_auto_delete;
+ tree do_delete = NULL_TREE;
+
+ if (use_global_delete)
+ {
+ tree cond = fold (build (BIT_AND_EXPR, integer_type_node,
+ auto_delete, integer_one_node));
+ tree call = build_builtin_call
+ (void_type_node, BID, build_tree_list (NULL_TREE, addr));
+
+ cond = fold (build (COND_EXPR, void_type_node, cond,
+ call, void_zero_node));
+ if (cond != void_zero_node)
+ do_delete = cond;
+
+ passed_auto_delete = fold (build (BIT_AND_EXPR, integer_type_node,
+ auto_delete, integer_two_node));
+ }
+ else
+ passed_auto_delete = auto_delete;
+
+ if (flags & LOOKUP_PROTECT)
+ {
+ enum access_type access = compute_access (basetypes, dtor);
+
+ if (access == access_private)
+ {
+ if (flags & LOOKUP_COMPLAIN)
+ cp_error ("destructor for type `%T' is private in this scope", type);
+ return error_mark_node;
+ }
+ else if (access == access_protected)
+ {
+ if (flags & LOOKUP_COMPLAIN)
+ cp_error ("destructor for type `%T' is protected in this scope", type);
+ return error_mark_node;
+ }
+ }
+
+ /* Once we are in a destructor, try not going through
+ the virtual function table to find the next destructor. */
+ if (DECL_VINDEX (dtor)
+ && ! (flags & LOOKUP_NONVIRTUAL)
+ && TREE_CODE (auto_delete) != PARM_DECL
+ && (ptr == 1 || ! resolves_to_fixed_type_p (ref, 0)))
+ {
+ tree binfo, basetype;
+ /* The code below is probably all broken. See call.c for the
+ complete right way to do this. this offsets may not be right
+ in the below. (mrs) */
+ /* This destructor must be called via virtual function table. */
+ dtor = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (DECL_CONTEXT (dtor)), 0);
+ basetype = DECL_CLASS_CONTEXT (dtor);
+ binfo = get_binfo (basetype,
+ TREE_TYPE (TREE_TYPE (TREE_VALUE (parms))),
+ 0);
+ expr = convert_pointer_to_real (binfo, TREE_VALUE (parms));
+ if (expr != TREE_VALUE (parms))
+ {
+ expr = fold (expr);
+ ref = build_indirect_ref (expr, NULL_PTR);
+ TREE_VALUE (parms) = expr;
+ }
+ function = build_vfn_ref (&TREE_VALUE (parms), ref, DECL_VINDEX (dtor));
+ if (function == error_mark_node)
+ return error_mark_node;
+ TREE_TYPE (function) = build_pointer_type (TREE_TYPE (dtor));
+ TREE_CHAIN (parms) = build_tree_list (NULL_TREE, passed_auto_delete);
+ expr = build_function_call (function, parms);
+ if (do_delete)
+ expr = build (COMPOUND_EXPR, void_type_node, expr, do_delete);
+ if (ptr && (flags & LOOKUP_DESTRUCTOR) == 0)
+ {
+ /* Handle the case where a virtual destructor is
+ being called on an item that is 0.
+
+ @@ Does this really need to be done? */
+ tree ifexp = build_binary_op(NE_EXPR, addr, integer_zero_node,1);
+#if 0
+ if (TREE_CODE (ref) == VAR_DECL
+ || TREE_CODE (ref) == COMPONENT_REF)
+ warning ("losing in build_delete");
+#endif
+ expr = build (COND_EXPR, void_type_node,
+ ifexp, expr, void_zero_node);
+ }
+ }
+ else
+ {
+ tree ifexp;
+
+ if ((flags & LOOKUP_DESTRUCTOR)
+ || TREE_CODE (ref) == VAR_DECL
+ || TREE_CODE (ref) == PARM_DECL
+ || TREE_CODE (ref) == COMPONENT_REF
+ || TREE_CODE (ref) == ARRAY_REF)
+ /* These can't be 0. */
+ ifexp = integer_one_node;
+ else
+ /* Handle the case where a non-virtual destructor is
+ being called on an item that is 0. */
+ ifexp = build_binary_op (NE_EXPR, addr, integer_zero_node, 1);
+
+ /* Used to mean that this destructor was known to be empty,
+ but that's now obsolete. */
+ my_friendly_assert (DECL_INITIAL (dtor) != void_type_node, 221);
+
+ TREE_CHAIN (parms) = build_tree_list (NULL_TREE, passed_auto_delete);
+ expr = build_function_call (dtor, parms);
+ if (do_delete)
+ expr = build (COMPOUND_EXPR, void_type_node, expr, do_delete);
+
+ if (ifexp != integer_one_node)
+ expr = build (COND_EXPR, void_type_node,
+ ifexp, expr, void_zero_node);
+ }
+ return expr;
+ }
+ else
+ {
+ /* This can get visibilities wrong. */
+ tree binfos = BINFO_BASETYPES (TYPE_BINFO (type));
+ int i, n_baseclasses = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+ tree base_binfo = n_baseclasses > 0 ? TREE_VEC_ELT (binfos, 0) : NULL_TREE;
+ tree exprstmt = NULL_TREE;
+ tree parent_auto_delete = auto_delete;
+ tree cond;
+
+ /* If this type does not have a destructor, but does have
+ operator delete, call the parent parent destructor (if any),
+ but let this node do the deleting. Otherwise, it is ok
+ to let the parent destructor do the deleting. */
+ if (TYPE_GETS_REG_DELETE (type) && !use_global_delete)
+ {
+ parent_auto_delete = integer_zero_node;
+ if (auto_delete == integer_zero_node)
+ cond = NULL_TREE;
+ else
+ {
+ tree virtual_size;
+
+ /* This is probably wrong. It should be the size of the
+ virtual object being deleted. */
+ virtual_size = c_sizeof_nowarn (type);
+
+ expr = build_opfncall (DELETE_EXPR, LOOKUP_NORMAL, addr,
+ virtual_size, NULL_TREE);
+ if (expr == error_mark_node)
+ return error_mark_node;
+ if (auto_delete != integer_one_node)
+ cond = build (COND_EXPR, void_type_node,
+ build (BIT_AND_EXPR, integer_type_node,
+ auto_delete, integer_one_node),
+ expr, void_zero_node);
+ else
+ cond = expr;
+ }
+ }
+ else if (base_binfo == NULL_TREE
+ || (TREE_VIA_VIRTUAL (base_binfo) == 0
+ && ! TYPE_NEEDS_DESTRUCTOR (BINFO_TYPE (base_binfo))))
+ {
+ tree virtual_size;
+
+ /* This is probably wrong. It should be the size of the virtual
+ object being deleted. */
+ virtual_size = c_sizeof_nowarn (type);
+
+ cond = build (COND_EXPR, void_type_node,
+ build (BIT_AND_EXPR, integer_type_node, auto_delete, integer_one_node),
+ build_builtin_call (void_type_node, BID,
+ build_tree_list (NULL_TREE, addr)),
+ void_zero_node);
+ }
+ else
+ cond = NULL_TREE;
+
+ if (cond)
+ exprstmt = build_tree_list (NULL_TREE, cond);
+
+ if (base_binfo
+ && ! TREE_VIA_VIRTUAL (base_binfo)
+ && TYPE_NEEDS_DESTRUCTOR (BINFO_TYPE (base_binfo)))
+ {
+ tree this_auto_delete;
+
+ if (BINFO_OFFSET_ZEROP (base_binfo))
+ this_auto_delete = parent_auto_delete;
+ else
+ this_auto_delete = integer_zero_node;
+
+ expr = build_delete (TYPE_POINTER_TO (BINFO_TYPE (base_binfo)), addr,
+ this_auto_delete, flags, 0);
+ exprstmt = tree_cons (NULL_TREE, expr, exprstmt);
+ }
+
+ /* Take care of the remaining baseclasses. */
+ for (i = 1; i < n_baseclasses; i++)
+ {
+ base_binfo = TREE_VEC_ELT (binfos, i);
+ if (! TYPE_NEEDS_DESTRUCTOR (BINFO_TYPE (base_binfo))
+ || TREE_VIA_VIRTUAL (base_binfo))
+ continue;
+
+ /* May be zero offset if other baseclasses are virtual. */
+ expr = fold (build (PLUS_EXPR, TYPE_POINTER_TO (BINFO_TYPE (base_binfo)),
+ addr, BINFO_OFFSET (base_binfo)));
+
+ expr = build_delete (TYPE_POINTER_TO (BINFO_TYPE (base_binfo)), expr,
+ integer_zero_node,
+ flags, 0);
+
+ exprstmt = tree_cons (NULL_TREE, expr, exprstmt);
+ }
+
+ for (member = TYPE_FIELDS (type); member; member = TREE_CHAIN (member))
+ {
+ if (TREE_CODE (member) != FIELD_DECL)
+ continue;
+ if (TYPE_NEEDS_DESTRUCTOR (TREE_TYPE (member)))
+ {
+ tree this_member = build_component_ref (ref, DECL_NAME (member), 0, 0);
+ tree this_type = TREE_TYPE (member);
+ expr = build_delete (this_type, this_member, integer_two_node, flags, 0);
+ exprstmt = tree_cons (NULL_TREE, expr, exprstmt);
+ }
+ }
+
+ if (exprstmt)
+ return build_compound_expr (exprstmt);
+ /* Virtual base classes make this function do nothing. */
+ return void_zero_node;
+ }
+}
+
+/* For type TYPE, delete the virtual baseclass objects of DECL. */
+
+tree
+build_vbase_delete (type, decl)
+ tree type, decl;
+{
+ tree vbases = CLASSTYPE_VBASECLASSES (type);
+ tree result = NULL_TREE;
+ tree addr = build_unary_op (ADDR_EXPR, decl, 0);
+
+ my_friendly_assert (addr != error_mark_node, 222);
+
+ while (vbases)
+ {
+ tree this_addr = convert_force (TYPE_POINTER_TO (BINFO_TYPE (vbases)),
+ addr);
+ result = tree_cons (NULL_TREE,
+ build_delete (TREE_TYPE (this_addr), this_addr,
+ integer_zero_node,
+ LOOKUP_NORMAL|LOOKUP_DESTRUCTOR, 0),
+ result);
+ vbases = TREE_CHAIN (vbases);
+ }
+ return build_compound_expr (nreverse (result));
+}
+
+/* Build a C++ vector delete expression.
+ MAXINDEX is the number of elements to be deleted.
+ ELT_SIZE is the nominal size of each element in the vector.
+ BASE is the expression that should yield the store to be deleted.
+ This function expands (or synthesizes) these calls itself.
+ AUTO_DELETE_VEC says whether the container (vector) should be deallocated.
+ AUTO_DELETE say whether each item in the container should be deallocated.
+
+ This also calls delete for virtual baseclasses of elements of the vector.
+
+ Update: MAXINDEX is no longer needed. The size can be extracted from the
+ start of the vector for pointers, and from the type for arrays. We still
+ use MAXINDEX for arrays because it happens to already have one of the
+ values we'd have to extract. (We could use MAXINDEX with pointers to
+ confirm the size, and trap if the numbers differ; not clear that it'd
+ be worth bothering.) */
+tree
+build_vec_delete (base, maxindex, elt_size, auto_delete_vec, auto_delete,
+ use_global_delete)
+ tree base, maxindex, elt_size;
+ tree auto_delete_vec, auto_delete;
+ int use_global_delete;
+{
+ tree ptype = TREE_TYPE (base);
+ tree type;
+ tree virtual_size;
+ /* Temporary variables used by the loop. */
+ tree tbase, size_exp, tbase_init;
+
+ /* This is the body of the loop that implements the deletion of a
+ single element, and moves temp variables to next elements. */
+ tree body;
+
+ /* This is the LOOP_EXPR that governs the deletion of the elements. */
+ tree loop;
+
+ /* This is the thing that governs what to do after the loop has run. */
+ tree deallocate_expr = 0;
+
+ /* This is the BIND_EXPR which holds the outermost iterator of the
+ loop. It is convenient to set this variable up and test it before
+ executing any other code in the loop.
+ This is also the containing expression returned by this function. */
+ tree controller = NULL_TREE;
+
+ /* This is the BLOCK to record the symbol binding for debugging. */
+ tree block;
+
+ base = stabilize_reference (base);
+
+ /* Since we can use base many times, save_expr it. */
+ if (TREE_SIDE_EFFECTS (base))
+ base = save_expr (base);
+
+ if (TREE_CODE (ptype) == POINTER_TYPE)
+ {
+ /* Step back one from start of vector, and read dimension. */
+ tree cookie_addr = build (MINUS_EXPR, TYPE_POINTER_TO (BI_header_type),
+ base, BI_header_size);
+ tree cookie = build_indirect_ref (cookie_addr, NULL_PTR);
+ maxindex = build_component_ref (cookie, nc_nelts_field_id, 0, 0);
+ do
+ ptype = TREE_TYPE (ptype);
+ while (TREE_CODE (ptype) == ARRAY_TYPE);
+ }
+ else if (TREE_CODE (ptype) == ARRAY_TYPE)
+ {
+ /* get the total number of things in the array, maxindex is a bad name */
+ maxindex = array_type_nelts_total (ptype);
+ while (TREE_CODE (ptype) == ARRAY_TYPE)
+ ptype = TREE_TYPE (ptype);
+ base = build_unary_op (ADDR_EXPR, base, 1);
+ }
+ else
+ {
+ error ("type to vector delete is neither pointer or array type");
+ return error_mark_node;
+ }
+ type = ptype;
+ ptype = TYPE_POINTER_TO (type);
+
+ size_exp = size_in_bytes (type);
+
+ if (! IS_AGGR_TYPE (type) || ! TYPE_NEEDS_DESTRUCTOR (type))
+ {
+ loop = integer_zero_node;
+ goto no_destructor;
+ }
+
+ /* The below is short by BI_header_size */
+ virtual_size = fold (size_binop (MULT_EXPR, size_exp, maxindex));
+
+ tbase = build_decl (VAR_DECL, NULL_TREE, ptype);
+ tbase_init = build_modify_expr (tbase, NOP_EXPR,
+ fold (build (PLUS_EXPR, ptype,
+ base,
+ virtual_size)));
+ DECL_REGISTER (tbase) = 1;
+ controller = build (BIND_EXPR, void_type_node, tbase, 0, 0);
+ TREE_SIDE_EFFECTS (controller) = 1;
+ block = build_block (tbase, 0, 0, 0, 0);
+ add_block_current_level (block);
+
+ if (auto_delete != integer_zero_node
+ && auto_delete != integer_two_node)
+ {
+ tree base_tbd = convert (ptype,
+ build_binary_op (MINUS_EXPR,
+ convert (ptr_type_node, base),
+ BI_header_size,
+ 1));
+ /* This is the real size */
+ virtual_size = size_binop (PLUS_EXPR, virtual_size, BI_header_size);
+ body = build_tree_list (NULL_TREE,
+ build_x_delete (ptype, base_tbd,
+ 2 | use_global_delete,
+ virtual_size));
+ body = build (COND_EXPR, void_type_node,
+ build (BIT_AND_EXPR, integer_type_node,
+ auto_delete, integer_one_node),
+ body, integer_zero_node);
+ }
+ else
+ body = NULL_TREE;
+
+ body = tree_cons (NULL_TREE,
+ build_delete (ptype, tbase, auto_delete,
+ LOOKUP_NORMAL|LOOKUP_DESTRUCTOR, 1),
+ body);
+
+ body = tree_cons (NULL_TREE,
+ build_modify_expr (tbase, NOP_EXPR, build (MINUS_EXPR, ptype, tbase, size_exp)),
+ body);
+
+ body = tree_cons (NULL_TREE,
+ build (EXIT_EXPR, void_type_node,
+ build (EQ_EXPR, integer_type_node, base, tbase)),
+ body);
+
+ loop = build (LOOP_EXPR, void_type_node, build_compound_expr (body));
+
+ loop = tree_cons (NULL_TREE, tbase_init,
+ tree_cons (NULL_TREE, loop, NULL_TREE));
+ loop = build_compound_expr (loop);
+
+ no_destructor:
+ /* If the delete flag is one, or anything else with the low bit set,
+ delete the storage. */
+ if (auto_delete_vec == integer_zero_node
+ || auto_delete_vec == integer_two_node)
+ deallocate_expr = integer_zero_node;
+ else
+ {
+ tree base_tbd;
+
+ /* The below is short by BI_header_size */
+ virtual_size = fold (size_binop (MULT_EXPR, size_exp, maxindex));
+
+ if (! TYPE_VEC_NEW_USES_COOKIE (type))
+ /* no header */
+ base_tbd = base;
+ else
+ {
+ base_tbd = convert (ptype,
+ build_binary_op (MINUS_EXPR,
+ convert (string_type_node, base),
+ BI_header_size,
+ 1));
+ /* True size with header. */
+ virtual_size = size_binop (PLUS_EXPR, virtual_size, BI_header_size);
+ }
+ deallocate_expr = build_x_delete (ptype, base_tbd,
+ 2 | use_global_delete,
+ virtual_size);
+ if (auto_delete_vec != integer_one_node)
+ deallocate_expr = build (COND_EXPR, void_type_node,
+ build (BIT_AND_EXPR, integer_type_node,
+ auto_delete_vec, integer_one_node),
+ deallocate_expr, integer_zero_node);
+ }
+
+ if (loop && deallocate_expr != integer_zero_node)
+ {
+ body = tree_cons (NULL_TREE, loop,
+ tree_cons (NULL_TREE, deallocate_expr, NULL_TREE));
+ body = build_compound_expr (body);
+ }
+ else
+ body = loop;
+
+ /* Outermost wrapper: If pointer is null, punt. */
+ body = build (COND_EXPR, void_type_node,
+ build (NE_EXPR, integer_type_node, base, integer_zero_node),
+ body, integer_zero_node);
+ body = build1 (NOP_EXPR, void_type_node, body);
+
+ if (controller)
+ {
+ TREE_OPERAND (controller, 1) = body;
+ return controller;
+ }
+ else
+ return convert (void_type_node, body);
+}
diff --git a/gnu/usr.bin/cc/cc1plus/input.c b/gnu/usr.bin/cc/cc1plus/input.c
new file mode 100644
index 0000000..1570489
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/input.c
@@ -0,0 +1,184 @@
+/* Input handling for G++.
+ Copyright (C) 1992, 1993 Free Software Foundation, Inc.
+ Written by Ken Raeburn (raeburn@cygnus.com) while at Watchmaker Computing.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* G++ needs to do enough saving and re-parsing of text that it is
+ necessary to abandon the simple FILE* model and use a mechanism where
+ we can pre-empt one input stream with another derived from saved text;
+ we may need to do this arbitrarily often, and cannot depend on having
+ the GNU library available, so FILE objects just don't cut it.
+
+ This file is written as a separate module, but can be included by
+ lex.c for very minor efficiency gains (primarily in function
+ inlining). */
+
+#include <stdio.h>
+#include "obstack.h"
+
+extern FILE *finput;
+
+struct pending_input *save_pending_input ();
+void restore_pending_input ();
+
+struct input_source {
+ /* saved string */
+ char *str;
+ int length;
+ /* current position, when reading as input */
+ int offset;
+ /* obstack to free this input string from when finished, if any */
+ struct obstack *obstack;
+ /* linked list maintenance */
+ struct input_source *next;
+ /* values to restore after reading all of current string */
+ char *filename;
+ int lineno;
+ struct pending_input *input;
+ int putback_char;
+};
+
+static struct input_source *input, *free_inputs;
+
+extern char *input_filename;
+extern int lineno;
+
+#ifdef __GNUC__
+#define inline __inline__
+#else
+#define inline
+#endif
+
+static inline struct input_source *
+allocate_input ()
+{
+ struct input_source *inp;
+ if (free_inputs)
+ {
+ inp = free_inputs;
+ free_inputs = inp->next;
+ inp->next = 0;
+ return inp;
+ }
+ inp = (struct input_source *) xmalloc (sizeof (struct input_source));
+ inp->next = 0;
+ inp->obstack = 0;
+ return inp;
+}
+
+static inline void
+free_input (inp)
+ struct input_source *inp;
+{
+ if (inp->obstack)
+ obstack_free (inp->obstack, inp->str);
+ inp->obstack = 0;
+ inp->str = 0;
+ inp->length = 0;
+ inp->next = free_inputs;
+ free_inputs = inp;
+}
+
+static int putback_char = -1;
+
+/* Some of these external functions are declared inline in case this file
+ is included in lex.c. */
+
+inline
+void
+feed_input (str, len, delete)
+ char *str;
+ int len;
+ struct obstack *delete;
+{
+ struct input_source *inp = allocate_input ();
+
+ /* This shouldn't be necessary. */
+ while (len && !str[len-1])
+ len--;
+
+ inp->str = str;
+ inp->length = len;
+ inp->obstack = delete;
+ inp->offset = 0;
+ inp->next = input;
+ inp->filename = input_filename;
+ inp->lineno = lineno;
+ inp->input = save_pending_input ();
+ inp->putback_char = putback_char;
+ putback_char = -1;
+ input = inp;
+}
+
+struct pending_input *to_be_restored; /* XXX */
+extern int end_of_file;
+
+int
+getch ()
+{
+ if (putback_char != -1)
+ {
+ int ch = putback_char;
+ putback_char = -1;
+ return ch;
+ }
+ if (input)
+ {
+ if (input->offset == input->length)
+ {
+ struct input_source *inp = input;
+ my_friendly_assert (putback_char == -1, 223);
+ to_be_restored = inp->input;
+ input->offset++;
+ return EOF;
+ }
+ else if (input->offset > input->length)
+ {
+ struct input_source *inp = input;
+
+ end_of_file = 0;
+ input = inp->next;
+ input_filename = inp->filename;
+ lineno = inp->lineno;
+ /* Get interface/implementation back in sync. */
+ extract_interface_info ();
+ putback_char = inp->putback_char;
+ free_input (inp);
+ return getch ();
+ }
+ if (input)
+ return input->str[input->offset++];
+ }
+ return getc (finput);
+}
+
+inline
+void
+put_back (ch)
+ int ch;
+{
+ my_friendly_assert (putback_char == -1, 224);
+ putback_char = ch;
+}
+
+inline
+int
+input_redirected ()
+{
+ return input != 0;
+}
diff --git a/gnu/usr.bin/cc/cc1plus/lex.c b/gnu/usr.bin/cc/cc1plus/lex.c
new file mode 100644
index 0000000..5edf659
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/lex.c
@@ -0,0 +1,4818 @@
+/* Separate lexical analyzer for GNU C++.
+ Copyright (C) 1987, 1989, 1992, 1993, 1994 Free Software Foundation, Inc.
+ Hacked by Michael Tiemann (tiemann@cygnus.com)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file is the lexical analyzer for GNU C++. */
+
+/* Cause the `yydebug' variable to be defined. */
+#define YYDEBUG 1
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <errno.h>
+#include <setjmp.h>
+#include "config.h"
+#include "input.h"
+#include "tree.h"
+#include "lex.h"
+#include "parse.h"
+#include "cp-tree.h"
+#include "flags.h"
+#include "obstack.h"
+
+#ifdef MULTIBYTE_CHARS
+#include <stdlib.h>
+#include <locale.h>
+#endif
+
+#ifndef errno
+extern int errno; /* needed for VAX. */
+#endif
+extern jmp_buf toplevel;
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+extern struct obstack *expression_obstack, permanent_obstack;
+extern struct obstack *current_obstack, *saveable_obstack;
+
+extern double atof ();
+
+extern char *get_directive_line (); /* In c-common.c */
+
+/* Given a file name X, return the nondirectory portion.
+ Keep in mind that X can be computed more than once. */
+#ifndef FILE_NAME_NONDIRECTORY
+#define FILE_NAME_NONDIRECTORY(X) \
+ (rindex (X, '/') != 0 ? rindex (X, '/') + 1 : X)
+#endif
+
+extern char *index ();
+extern char *rindex ();
+
+void extract_interface_info ();
+void yyerror ();
+
+/* This obstack is needed to hold text. It is not safe to use
+ TOKEN_BUFFER because `check_newline' calls `yylex'. */
+struct obstack inline_text_obstack;
+static char *inline_text_firstobj;
+
+int end_of_file;
+
+/* Pending language change.
+ Positive is push count, negative is pop count. */
+int pending_lang_change = 0;
+
+/* Wrap the current header file in extern "C". */
+static int c_header_level = 0;
+
+extern int first_token;
+extern struct obstack token_obstack;
+
+/* ??? Don't really know where this goes yet. */
+#if 1
+#include "input.c"
+#else
+extern void put_back (/* int */);
+extern int input_redirected ();
+extern void feed_input (/* char *, int, struct obstack * */);
+#endif
+
+/* Holds translations from TREE_CODEs to operator name strings,
+ i.e., opname_tab[PLUS_EXPR] == "+". */
+char **opname_tab;
+char **assignop_tab;
+
+extern int yychar; /* the lookahead symbol */
+extern YYSTYPE yylval; /* the semantic value of the */
+ /* lookahead symbol */
+
+#if 0
+YYLTYPE yylloc; /* location data for the lookahead */
+ /* symbol */
+#endif
+
+
+/* the declaration found for the last IDENTIFIER token read in.
+ yylex must look this up to detect typedefs, which get token type TYPENAME,
+ so it is left around in case the identifier is not a typedef but is
+ used in a context which makes it a reference to a variable. */
+tree lastiddecl;
+
+/* The elements of `ridpointers' are identifier nodes
+ for the reserved type names and storage classes.
+ It is indexed by a RID_... value. */
+tree ridpointers[(int) RID_MAX];
+
+/* We may keep statistics about how long which files took to compile. */
+static int header_time, body_time;
+static tree get_time_identifier ();
+static tree filename_times;
+static tree this_filename_time;
+
+/* For implementing #pragma unit. */
+tree current_unit_name;
+tree current_unit_language;
+
+/* Array for holding counts of the numbers of tokens seen. */
+extern int *token_count;
+
+/* Textual definition used for default functions. */
+static void default_copy_constructor_body ();
+static void default_assign_ref_body ();
+
+/* Return something to represent absolute declarators containing a *.
+ TARGET is the absolute declarator that the * contains.
+ TYPE_QUALS is a list of modifiers such as const or volatile
+ to apply to the pointer type, represented as identifiers.
+
+ We return an INDIRECT_REF whose "contents" are TARGET
+ and whose type is the modifier list. */
+
+tree
+make_pointer_declarator (type_quals, target)
+ tree type_quals, target;
+{
+ if (target && TREE_CODE (target) == IDENTIFIER_NODE
+ && ANON_AGGRNAME_P (target))
+ error ("type name expected before `*'");
+ target = build_parse_node (INDIRECT_REF, target);
+ TREE_TYPE (target) = type_quals;
+ return target;
+}
+
+/* Return something to represent absolute declarators containing a &.
+ TARGET is the absolute declarator that the & contains.
+ TYPE_QUALS is a list of modifiers such as const or volatile
+ to apply to the reference type, represented as identifiers.
+
+ We return an ADDR_EXPR whose "contents" are TARGET
+ and whose type is the modifier list. */
+
+tree
+make_reference_declarator (type_quals, target)
+ tree type_quals, target;
+{
+ if (target)
+ {
+ if (TREE_CODE (target) == ADDR_EXPR)
+ {
+ error ("cannot declare references to references");
+ return target;
+ }
+ if (TREE_CODE (target) == INDIRECT_REF)
+ {
+ error ("cannot declare pointers to references");
+ return target;
+ }
+ if (TREE_CODE (target) == IDENTIFIER_NODE && ANON_AGGRNAME_P (target))
+ error ("type name expected before `&'");
+ }
+ target = build_parse_node (ADDR_EXPR, target);
+ TREE_TYPE (target) = type_quals;
+ return target;
+}
+
+/* Build names and nodes for overloaded operators. */
+
+tree ansi_opname[LAST_CPLUS_TREE_CODE];
+tree ansi_assopname[LAST_CPLUS_TREE_CODE];
+
+char *
+operator_name_string (name)
+ tree name;
+{
+ char *opname = IDENTIFIER_POINTER (name) + 2;
+ tree *opname_table;
+ int i, assign;
+
+ /* Works for builtin and user defined types. */
+ if (IDENTIFIER_GLOBAL_VALUE (name)
+ && TREE_CODE (IDENTIFIER_GLOBAL_VALUE (name)) == TYPE_DECL)
+ return IDENTIFIER_POINTER (name);
+
+ if (opname[0] == 'a' && opname[2] != '\0' && opname[2] != '_')
+ {
+ opname += 1;
+ assign = 1;
+ opname_table = ansi_assopname;
+ }
+ else
+ {
+ assign = 0;
+ opname_table = ansi_opname;
+ }
+
+ for (i = 0; i < (int) LAST_CPLUS_TREE_CODE; i++)
+ {
+ if (opname[0] == IDENTIFIER_POINTER (opname_table[i])[2+assign]
+ && opname[1] == IDENTIFIER_POINTER (opname_table[i])[3+assign])
+ break;
+ }
+
+ if (i == LAST_CPLUS_TREE_CODE)
+ return "<invalid operator>";
+
+ if (assign)
+ return assignop_tab[i];
+ else
+ return opname_tab[i];
+}
+
+int interface_only; /* whether or not current file is only for
+ interface definitions. */
+int interface_unknown; /* whether or not we know this class
+ to behave according to #pragma interface. */
+
+/* lexical analyzer */
+
+/* File used for outputting assembler code. */
+extern FILE *asm_out_file;
+
+#ifndef WCHAR_TYPE_SIZE
+#ifdef INT_TYPE_SIZE
+#define WCHAR_TYPE_SIZE INT_TYPE_SIZE
+#else
+#define WCHAR_TYPE_SIZE BITS_PER_WORD
+#endif
+#endif
+
+/* Number of bytes in a wide character. */
+#define WCHAR_BYTES (WCHAR_TYPE_SIZE / BITS_PER_UNIT)
+
+static int maxtoken; /* Current nominal length of token buffer. */
+char *token_buffer; /* Pointer to token buffer.
+ Actual allocated length is maxtoken + 2. */
+
+#include "hash.h"
+
+int check_newline ();
+
+/* Nonzero tells yylex to ignore \ in string constants. */
+static int ignore_escape_flag = 0;
+
+static int skip_white_space ();
+
+static tree
+get_time_identifier (name)
+ char *name;
+{
+ tree time_identifier;
+ int len = strlen (name);
+ char *buf = (char *) alloca (len + 6);
+ strcpy (buf, "file ");
+ bcopy (name, buf+5, len);
+ buf[len+5] = '\0';
+ time_identifier = get_identifier (buf);
+ if (IDENTIFIER_LOCAL_VALUE (time_identifier) == NULL_TREE)
+ {
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+ IDENTIFIER_LOCAL_VALUE (time_identifier) = build_int_2 (0, 0);
+ IDENTIFIER_CLASS_VALUE (time_identifier) = build_int_2 (0, 1);
+ IDENTIFIER_GLOBAL_VALUE (time_identifier) = filename_times;
+ filename_times = time_identifier;
+ pop_obstacks ();
+ }
+ return time_identifier;
+}
+
+#ifdef __GNUC__
+__inline
+#endif
+static int
+my_get_run_time ()
+{
+ int old_quiet_flag = quiet_flag;
+ int this_time;
+ quiet_flag = 0;
+ this_time = get_run_time ();
+ quiet_flag = old_quiet_flag;
+ return this_time;
+}
+
+/* Table indexed by tree code giving a string containing a character
+ classifying the tree code. Possibilities are
+ t, d, s, c, r, <, 1 and 2. See cp/tree.def for details. */
+
+#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) TYPE,
+
+char *cplus_tree_code_type[] = {
+ "x",
+#include "tree.def"
+};
+#undef DEFTREECODE
+
+/* Table indexed by tree code giving number of expression
+ operands beyond the fixed part of the node structure.
+ Not used for types or decls. */
+
+#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) LENGTH,
+
+int cplus_tree_code_length[] = {
+ 0,
+#include "tree.def"
+};
+#undef DEFTREECODE
+
+/* Names of tree components.
+ Used for printing out the tree and error messages. */
+#define DEFTREECODE(SYM, NAME, TYPE, LEN) NAME,
+
+char *cplus_tree_code_name[] = {
+ "@@dummy",
+#include "tree.def"
+};
+#undef DEFTREECODE
+
+/* toplev.c needs to call these. */
+
+void
+lang_init ()
+{
+ /* the beginning of the file is a new line; check for # */
+ /* With luck, we discover the real source file's name from that
+ and put it in input_filename. */
+ put_back (check_newline ());
+
+ if (flag_cadillac)
+ cadillac_start ();
+ if (flag_gnu_xref) GNU_xref_begin (input_filename);
+}
+
+void
+lang_finish ()
+{
+ extern int errorcount, sorrycount;
+ if (flag_gnu_xref) GNU_xref_end (errorcount+sorrycount);
+}
+
+char *
+lang_identify ()
+{
+ return "cplusplus";
+}
+
+void
+init_filename_times ()
+{
+ this_filename_time = get_time_identifier ("<top level>");
+ if (flag_detailed_statistics)
+ {
+ header_time = 0;
+ body_time = my_get_run_time ();
+ TREE_INT_CST_LOW (IDENTIFIER_LOCAL_VALUE (this_filename_time)) = body_time;
+ }
+}
+
+/* Change by Bryan Boreham, Kewill, Thu Jul 27 09:46:05 1989.
+ Stuck this hack in to get the files open correctly; this is called
+ in place of init_lex if we are an unexec'd binary. */
+void
+reinit_lang_specific ()
+{
+ init_filename_times ();
+ reinit_search_statistics ();
+}
+
+void
+init_lex ()
+{
+ extern char *(*decl_printable_name) ();
+
+ int i;
+
+ /* Initialize the lookahead machinery. */
+ init_spew ();
+
+ /* Make identifier nodes long enough for the language-specific slots. */
+ set_identifier_size (sizeof (struct lang_identifier));
+ decl_printable_name = lang_printable_name;
+
+ init_cplus_expand ();
+
+ tree_code_type
+ = (char **) realloc (tree_code_type,
+ sizeof (char *) * LAST_CPLUS_TREE_CODE);
+ tree_code_length
+ = (int *) realloc (tree_code_length,
+ sizeof (int) * LAST_CPLUS_TREE_CODE);
+ tree_code_name
+ = (char **) realloc (tree_code_name,
+ sizeof (char *) * LAST_CPLUS_TREE_CODE);
+ bcopy ((char *)cplus_tree_code_type,
+ (char *)(tree_code_type + (int) LAST_AND_UNUSED_TREE_CODE),
+ (LAST_CPLUS_TREE_CODE - (int)LAST_AND_UNUSED_TREE_CODE) * sizeof (char *));
+ bcopy ((char *)cplus_tree_code_length,
+ (char *)(tree_code_length + (int) LAST_AND_UNUSED_TREE_CODE),
+ (LAST_CPLUS_TREE_CODE - (int)LAST_AND_UNUSED_TREE_CODE) * sizeof (int));
+ bcopy ((char *)cplus_tree_code_name,
+ (char *)(tree_code_name + (int) LAST_AND_UNUSED_TREE_CODE),
+ (LAST_CPLUS_TREE_CODE - (int)LAST_AND_UNUSED_TREE_CODE) * sizeof (char *));
+
+ opname_tab = (char **)oballoc ((int)LAST_CPLUS_TREE_CODE * sizeof (char *));
+ bzero ((char *)opname_tab, (int)LAST_CPLUS_TREE_CODE * sizeof (char *));
+ assignop_tab = (char **)oballoc ((int)LAST_CPLUS_TREE_CODE * sizeof (char *));
+ bzero ((char *)assignop_tab, (int)LAST_CPLUS_TREE_CODE * sizeof (char *));
+
+ ansi_opname[0] = get_identifier ("<invalid operator>");
+ for (i = 0; i < (int) LAST_CPLUS_TREE_CODE; i++)
+ {
+ ansi_opname[i] = ansi_opname[0];
+ ansi_assopname[i] = ansi_opname[0];
+ }
+
+ ansi_opname[(int) MULT_EXPR] = get_identifier ("__ml");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) MULT_EXPR]) = 1;
+ ansi_opname[(int) INDIRECT_REF] = ansi_opname[(int) MULT_EXPR];
+ ansi_assopname[(int) MULT_EXPR] = get_identifier ("__aml");
+ IDENTIFIER_OPNAME_P (ansi_assopname[(int) MULT_EXPR]) = 1;
+ ansi_assopname[(int) INDIRECT_REF] = ansi_assopname[(int) MULT_EXPR];
+ ansi_opname[(int) TRUNC_MOD_EXPR] = get_identifier ("__md");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) TRUNC_MOD_EXPR]) = 1;
+ ansi_assopname[(int) TRUNC_MOD_EXPR] = get_identifier ("__amd");
+ IDENTIFIER_OPNAME_P (ansi_assopname[(int) TRUNC_MOD_EXPR]) = 1;
+ ansi_opname[(int) CEIL_MOD_EXPR] = ansi_opname[(int) TRUNC_MOD_EXPR];
+ ansi_opname[(int) FLOOR_MOD_EXPR] = ansi_opname[(int) TRUNC_MOD_EXPR];
+ ansi_opname[(int) ROUND_MOD_EXPR] = ansi_opname[(int) TRUNC_MOD_EXPR];
+ ansi_opname[(int) MINUS_EXPR] = get_identifier ("__mi");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) MINUS_EXPR]) = 1;
+ ansi_opname[(int) NEGATE_EXPR] = ansi_opname[(int) MINUS_EXPR];
+ ansi_assopname[(int) MINUS_EXPR] = get_identifier ("__ami");
+ IDENTIFIER_OPNAME_P (ansi_assopname[(int) MINUS_EXPR]) = 1;
+ ansi_assopname[(int) NEGATE_EXPR] = ansi_assopname[(int) MINUS_EXPR];
+ ansi_opname[(int) RSHIFT_EXPR] = get_identifier ("__rs");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) RSHIFT_EXPR]) = 1;
+ ansi_assopname[(int) RSHIFT_EXPR] = get_identifier ("__ars");
+ IDENTIFIER_OPNAME_P (ansi_assopname[(int) RSHIFT_EXPR]) = 1;
+ ansi_opname[(int) NE_EXPR] = get_identifier ("__ne");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) NE_EXPR]) = 1;
+ ansi_opname[(int) GT_EXPR] = get_identifier ("__gt");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) GT_EXPR]) = 1;
+ ansi_opname[(int) GE_EXPR] = get_identifier ("__ge");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) GE_EXPR]) = 1;
+ ansi_opname[(int) BIT_IOR_EXPR] = get_identifier ("__or");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) BIT_IOR_EXPR]) = 1;
+ ansi_assopname[(int) BIT_IOR_EXPR] = get_identifier ("__aor");
+ IDENTIFIER_OPNAME_P (ansi_assopname[(int) BIT_IOR_EXPR]) = 1;
+ ansi_opname[(int) TRUTH_ANDIF_EXPR] = get_identifier ("__aa");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) TRUTH_ANDIF_EXPR]) = 1;
+ ansi_opname[(int) TRUTH_NOT_EXPR] = get_identifier ("__nt");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) TRUTH_NOT_EXPR]) = 1;
+ ansi_opname[(int) PREINCREMENT_EXPR] = get_identifier ("__pp");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) PREINCREMENT_EXPR]) = 1;
+ ansi_opname[(int) POSTINCREMENT_EXPR] = ansi_opname[(int) PREINCREMENT_EXPR];
+ ansi_opname[(int) MODIFY_EXPR] = get_identifier ("__as");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) MODIFY_EXPR]) = 1;
+ ansi_assopname[(int) NOP_EXPR] = ansi_opname[(int) MODIFY_EXPR];
+ ansi_opname[(int) COMPOUND_EXPR] = get_identifier ("__cm");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) COMPOUND_EXPR]) = 1;
+ ansi_opname[(int) EXACT_DIV_EXPR] = get_identifier ("__dv");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) EXACT_DIV_EXPR]) = 1;
+ ansi_assopname[(int) EXACT_DIV_EXPR] = get_identifier ("__adv");
+ IDENTIFIER_OPNAME_P (ansi_assopname[(int) EXACT_DIV_EXPR]) = 1;
+ ansi_opname[(int) TRUNC_DIV_EXPR] = ansi_opname[(int) EXACT_DIV_EXPR];
+ ansi_opname[(int) CEIL_DIV_EXPR] = ansi_opname[(int) EXACT_DIV_EXPR];
+ ansi_opname[(int) FLOOR_DIV_EXPR] = ansi_opname[(int) EXACT_DIV_EXPR];
+ ansi_opname[(int) ROUND_DIV_EXPR] = ansi_opname[(int) EXACT_DIV_EXPR];
+ ansi_opname[(int) PLUS_EXPR] = get_identifier ("__pl");
+ ansi_assopname[(int) TRUNC_DIV_EXPR] = ansi_assopname[(int) EXACT_DIV_EXPR];
+ ansi_assopname[(int) CEIL_DIV_EXPR] = ansi_assopname[(int) EXACT_DIV_EXPR];
+ ansi_assopname[(int) FLOOR_DIV_EXPR] = ansi_assopname[(int) EXACT_DIV_EXPR];
+ ansi_assopname[(int) ROUND_DIV_EXPR] = ansi_assopname[(int) EXACT_DIV_EXPR];
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) PLUS_EXPR]) = 1;
+ ansi_assopname[(int) PLUS_EXPR] = get_identifier ("__apl");
+ IDENTIFIER_OPNAME_P (ansi_assopname[(int) PLUS_EXPR]) = 1;
+ ansi_opname[(int) CONVERT_EXPR] = ansi_opname[(int) PLUS_EXPR];
+ ansi_assopname[(int) CONVERT_EXPR] = ansi_assopname[(int) PLUS_EXPR];
+ ansi_opname[(int) LSHIFT_EXPR] = get_identifier ("__ls");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) LSHIFT_EXPR]) = 1;
+ ansi_assopname[(int) LSHIFT_EXPR] = get_identifier ("__als");
+ IDENTIFIER_OPNAME_P (ansi_assopname[(int) LSHIFT_EXPR]) = 1;
+ ansi_opname[(int) EQ_EXPR] = get_identifier ("__eq");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) EQ_EXPR]) = 1;
+ ansi_opname[(int) LT_EXPR] = get_identifier ("__lt");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) LT_EXPR]) = 1;
+ ansi_opname[(int) LE_EXPR] = get_identifier ("__le");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) LE_EXPR]) = 1;
+ ansi_opname[(int) BIT_AND_EXPR] = get_identifier ("__ad");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) BIT_AND_EXPR]) = 1;
+ ansi_assopname[(int) BIT_AND_EXPR] = get_identifier ("__aad");
+ IDENTIFIER_OPNAME_P (ansi_assopname[(int) BIT_AND_EXPR]) = 1;
+ ansi_opname[(int) ADDR_EXPR] = ansi_opname[(int) BIT_AND_EXPR];
+ ansi_assopname[(int) ADDR_EXPR] = ansi_assopname[(int) BIT_AND_EXPR];
+ ansi_opname[(int) BIT_XOR_EXPR] = get_identifier ("__er");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) BIT_XOR_EXPR]) = 1;
+ ansi_assopname[(int) BIT_XOR_EXPR] = get_identifier ("__aer");
+ IDENTIFIER_OPNAME_P (ansi_assopname[(int) BIT_XOR_EXPR]) = 1;
+ ansi_opname[(int) TRUTH_ORIF_EXPR] = get_identifier ("__oo");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) TRUTH_ORIF_EXPR]) = 1;
+ ansi_opname[(int) BIT_NOT_EXPR] = get_identifier ("__co");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) BIT_NOT_EXPR]) = 1;
+ ansi_opname[(int) PREDECREMENT_EXPR] = get_identifier ("__mm");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) PREDECREMENT_EXPR]) = 1;
+ ansi_opname[(int) POSTDECREMENT_EXPR] = ansi_opname[(int) PREDECREMENT_EXPR];
+ ansi_opname[(int) COMPONENT_REF] = get_identifier ("__rf");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) COMPONENT_REF]) = 1;
+ ansi_opname[(int) MEMBER_REF] = get_identifier ("__rm");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) MEMBER_REF]) = 1;
+ ansi_opname[(int) CALL_EXPR] = get_identifier ("__cl");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) CALL_EXPR]) = 1;
+ ansi_opname[(int) ARRAY_REF] = get_identifier ("__vc");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) ARRAY_REF]) = 1;
+ ansi_opname[(int) NEW_EXPR] = get_identifier ("__nw");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) NEW_EXPR]) = 1;
+ ansi_opname[(int) DELETE_EXPR] = get_identifier ("__dl");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) DELETE_EXPR]) = 1;
+ ansi_opname[(int) VEC_NEW_EXPR] = get_identifier ("__vn");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) VEC_NEW_EXPR]) = 1;
+ ansi_opname[(int) VEC_DELETE_EXPR] = get_identifier ("__vd");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) VEC_DELETE_EXPR]) = 1;
+ ansi_opname[(int) TYPE_EXPR] = get_identifier ("__op");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) TYPE_EXPR]) = 1;
+
+ /* This is not true: these operators are not defined in ANSI,
+ but we need them anyway. */
+ ansi_opname[(int) MIN_EXPR] = get_identifier ("__mn");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) MIN_EXPR]) = 1;
+ ansi_opname[(int) MAX_EXPR] = get_identifier ("__mx");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) MAX_EXPR]) = 1;
+ ansi_opname[(int) COND_EXPR] = get_identifier ("__cn");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) COND_EXPR]) = 1;
+ ansi_opname[(int) METHOD_CALL_EXPR] = get_identifier ("__wr");
+ IDENTIFIER_OPNAME_P (ansi_opname[(int) METHOD_CALL_EXPR]) = 1;
+
+ init_method ();
+ init_error ();
+ gcc_obstack_init (&inline_text_obstack);
+ inline_text_firstobj = (char *) obstack_alloc (&inline_text_obstack, 0);
+
+ /* Start it at 0, because check_newline is called at the very beginning
+ and will increment it to 1. */
+ lineno = 0;
+ input_filename = "<internal>";
+ current_function_decl = NULL;
+
+ maxtoken = 40;
+ token_buffer = (char *) xmalloc (maxtoken + 2);
+
+ ridpointers[(int) RID_INT] = get_identifier ("int");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_INT],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_INT]));
+ ridpointers[(int) RID_BOOL] = get_identifier ("bool");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_BOOL],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_BOOL]));
+ ridpointers[(int) RID_CHAR] = get_identifier ("char");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_CHAR],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_CHAR]));
+ ridpointers[(int) RID_VOID] = get_identifier ("void");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_VOID],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_VOID]));
+ ridpointers[(int) RID_FLOAT] = get_identifier ("float");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_FLOAT],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_FLOAT]));
+ ridpointers[(int) RID_DOUBLE] = get_identifier ("double");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_DOUBLE],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_DOUBLE]));
+ ridpointers[(int) RID_SHORT] = get_identifier ("short");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_SHORT],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_SHORT]));
+ ridpointers[(int) RID_LONG] = get_identifier ("long");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_LONG],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_LONG]));
+ ridpointers[(int) RID_UNSIGNED] = get_identifier ("unsigned");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_UNSIGNED],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_UNSIGNED]));
+ ridpointers[(int) RID_SIGNED] = get_identifier ("signed");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_SIGNED],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_SIGNED]));
+ ridpointers[(int) RID_INLINE] = get_identifier ("inline");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_INLINE],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_INLINE]));
+ ridpointers[(int) RID_CONST] = get_identifier ("const");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_CONST],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_CONST]));
+ ridpointers[(int) RID_VOLATILE] = get_identifier ("volatile");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_VOLATILE],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_VOLATILE]));
+ ridpointers[(int) RID_AUTO] = get_identifier ("auto");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_AUTO],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_AUTO]));
+ ridpointers[(int) RID_STATIC] = get_identifier ("static");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_STATIC],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_STATIC]));
+ ridpointers[(int) RID_EXTERN] = get_identifier ("extern");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_EXTERN],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_EXTERN]));
+ ridpointers[(int) RID_TYPEDEF] = get_identifier ("typedef");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_TYPEDEF],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_TYPEDEF]));
+ ridpointers[(int) RID_REGISTER] = get_identifier ("register");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_REGISTER],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_REGISTER]));
+
+ /* C++ extensions. These are probably not correctly named. */
+ ridpointers[(int) RID_WCHAR] = get_identifier ("__wchar_t");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_WCHAR],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_WCHAR]));
+ class_type_node = build_int_2 (class_type, 0);
+ TREE_TYPE (class_type_node) = class_type_node;
+ ridpointers[(int) RID_CLASS] = class_type_node;
+
+ record_type_node = build_int_2 (record_type, 0);
+ TREE_TYPE (record_type_node) = record_type_node;
+ ridpointers[(int) RID_RECORD] = record_type_node;
+
+ union_type_node = build_int_2 (union_type, 0);
+ TREE_TYPE (union_type_node) = union_type_node;
+ ridpointers[(int) RID_UNION] = union_type_node;
+
+ enum_type_node = build_int_2 (enum_type, 0);
+ TREE_TYPE (enum_type_node) = enum_type_node;
+ ridpointers[(int) RID_ENUM] = enum_type_node;
+
+ ridpointers[(int) RID_VIRTUAL] = get_identifier ("virtual");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_VIRTUAL],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_VIRTUAL]));
+ ridpointers[(int) RID_FRIEND] = get_identifier ("friend");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_FRIEND],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_FRIEND]));
+
+ ridpointers[(int) RID_PUBLIC] = get_identifier ("public");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_PUBLIC],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_PUBLIC]));
+ ridpointers[(int) RID_PRIVATE] = get_identifier ("private");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_PRIVATE],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_PRIVATE]));
+ ridpointers[(int) RID_PROTECTED] = get_identifier ("protected");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_PROTECTED],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_PROTECTED]));
+ ridpointers[(int) RID_TEMPLATE] = get_identifier ("template");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_TEMPLATE],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_TEMPLATE]));
+ /* This is for ANSI C++. */
+ ridpointers[(int) RID_MUTABLE] = get_identifier ("mutable");
+ SET_IDENTIFIER_AS_LIST (ridpointers[(int) RID_MUTABLE],
+ build_tree_list (NULL_TREE, ridpointers[(int) RID_MUTABLE]));
+
+ /* Exception handling extensions. */
+ exception_type_node = build_int_2 (exception_type, 0);
+ TREE_TYPE (exception_type_node) = exception_type_node;
+ ridpointers[(int) RID_EXCEPTION] = exception_type_node;
+
+ /* Signature handling extensions. */
+ signature_type_node = build_int_2 (signature_type, 0);
+ TREE_TYPE (signature_type_node) = signature_type_node;
+ ridpointers[(int) RID_SIGNATURE] = signature_type_node;
+
+ opname_tab[(int) COMPONENT_REF] = "->";
+ opname_tab[(int) MEMBER_REF] = "->*";
+ opname_tab[(int) METHOD_CALL_EXPR] = "->()";
+ opname_tab[(int) INDIRECT_REF] = "(unary *)";
+ opname_tab[(int) ARRAY_REF] = "[]";
+ opname_tab[(int) MODIFY_EXPR] = "=";
+ opname_tab[(int) NEW_EXPR] = "new";
+ opname_tab[(int) DELETE_EXPR] = "delete";
+ opname_tab[(int) VEC_NEW_EXPR] = "new []";
+ opname_tab[(int) VEC_DELETE_EXPR] = "delete []";
+ opname_tab[(int) COND_EXPR] = "... ? ... : ...";
+ opname_tab[(int) CALL_EXPR] = "()";
+ opname_tab[(int) PLUS_EXPR] = "+";
+ opname_tab[(int) MINUS_EXPR] = "-";
+ opname_tab[(int) MULT_EXPR] = "*";
+ opname_tab[(int) TRUNC_DIV_EXPR] = "/";
+ opname_tab[(int) CEIL_DIV_EXPR] = "(ceiling /)";
+ opname_tab[(int) FLOOR_DIV_EXPR] = "(floor /)";
+ opname_tab[(int) ROUND_DIV_EXPR] = "(round /)";
+ opname_tab[(int) TRUNC_MOD_EXPR] = "%";
+ opname_tab[(int) CEIL_MOD_EXPR] = "(ceiling %)";
+ opname_tab[(int) FLOOR_MOD_EXPR] = "(floor %)";
+ opname_tab[(int) ROUND_MOD_EXPR] = "(round %)";
+ opname_tab[(int) NEGATE_EXPR] = "-";
+ opname_tab[(int) MIN_EXPR] = "<?";
+ opname_tab[(int) MAX_EXPR] = ">?";
+ opname_tab[(int) ABS_EXPR] = "abs";
+ opname_tab[(int) FFS_EXPR] = "ffs";
+ opname_tab[(int) LSHIFT_EXPR] = "<<";
+ opname_tab[(int) RSHIFT_EXPR] = ">>";
+ opname_tab[(int) BIT_IOR_EXPR] = "|";
+ opname_tab[(int) BIT_XOR_EXPR] = "^";
+ opname_tab[(int) BIT_AND_EXPR] = "&";
+ opname_tab[(int) BIT_ANDTC_EXPR] = "&~";
+ opname_tab[(int) BIT_NOT_EXPR] = "~";
+ opname_tab[(int) TRUTH_ANDIF_EXPR] = "&&";
+ opname_tab[(int) TRUTH_ORIF_EXPR] = "||";
+ opname_tab[(int) TRUTH_AND_EXPR] = "strict &&";
+ opname_tab[(int) TRUTH_OR_EXPR] = "strict ||";
+ opname_tab[(int) TRUTH_NOT_EXPR] = "!";
+ opname_tab[(int) LT_EXPR] = "<";
+ opname_tab[(int) LE_EXPR] = "<=";
+ opname_tab[(int) GT_EXPR] = ">";
+ opname_tab[(int) GE_EXPR] = ">=";
+ opname_tab[(int) EQ_EXPR] = "==";
+ opname_tab[(int) NE_EXPR] = "!=";
+ opname_tab[(int) IN_EXPR] = "in";
+ opname_tab[(int) RANGE_EXPR] = "..";
+ opname_tab[(int) CONVERT_EXPR] = "(unary +)";
+ opname_tab[(int) ADDR_EXPR] = "(unary &)";
+ opname_tab[(int) PREDECREMENT_EXPR] = "--";
+ opname_tab[(int) PREINCREMENT_EXPR] = "++";
+ opname_tab[(int) POSTDECREMENT_EXPR] = "--";
+ opname_tab[(int) POSTINCREMENT_EXPR] = "++";
+ opname_tab[(int) COMPOUND_EXPR] = ",";
+
+ assignop_tab[(int) NOP_EXPR] = "=";
+ assignop_tab[(int) PLUS_EXPR] = "+=";
+ assignop_tab[(int) CONVERT_EXPR] = "+=";
+ assignop_tab[(int) MINUS_EXPR] = "-=";
+ assignop_tab[(int) NEGATE_EXPR] = "-=";
+ assignop_tab[(int) MULT_EXPR] = "*=";
+ assignop_tab[(int) INDIRECT_REF] = "*=";
+ assignop_tab[(int) TRUNC_DIV_EXPR] = "/=";
+ assignop_tab[(int) EXACT_DIV_EXPR] = "(exact /=)";
+ assignop_tab[(int) CEIL_DIV_EXPR] = "(ceiling /=)";
+ assignop_tab[(int) FLOOR_DIV_EXPR] = "(floor /=)";
+ assignop_tab[(int) ROUND_DIV_EXPR] = "(round /=)";
+ assignop_tab[(int) TRUNC_MOD_EXPR] = "%=";
+ assignop_tab[(int) CEIL_MOD_EXPR] = "(ceiling %=)";
+ assignop_tab[(int) FLOOR_MOD_EXPR] = "(floor %=)";
+ assignop_tab[(int) ROUND_MOD_EXPR] = "(round %=)";
+ assignop_tab[(int) MIN_EXPR] = "<?=";
+ assignop_tab[(int) MAX_EXPR] = ">?=";
+ assignop_tab[(int) LSHIFT_EXPR] = "<<=";
+ assignop_tab[(int) RSHIFT_EXPR] = ">>=";
+ assignop_tab[(int) BIT_IOR_EXPR] = "|=";
+ assignop_tab[(int) BIT_XOR_EXPR] = "^=";
+ assignop_tab[(int) BIT_AND_EXPR] = "&=";
+ assignop_tab[(int) ADDR_EXPR] = "&=";
+
+ init_filename_times ();
+
+ /* Some options inhibit certain reserved words.
+ Clear those words out of the hash table so they won't be recognized. */
+#define UNSET_RESERVED_WORD(STRING) \
+ do { struct resword *s = is_reserved_word (STRING, sizeof (STRING) - 1); \
+ if (s) s->name = ""; } while (0)
+
+#if 0
+ /* let's parse things, and if they use it, then give them an error. */
+ if (!flag_handle_exceptions)
+ {
+ UNSET_RESERVED_WORD ("throw");
+ UNSET_RESERVED_WORD ("try");
+ UNSET_RESERVED_WORD ("catch");
+ }
+#endif
+
+ if (! (flag_gc || flag_dossier))
+ {
+ UNSET_RESERVED_WORD ("classof");
+ UNSET_RESERVED_WORD ("headof");
+ }
+ if (! flag_handle_signatures)
+ {
+ /* Easiest way to not recognize signature
+ handling extensions... */
+ UNSET_RESERVED_WORD ("signature");
+ UNSET_RESERVED_WORD ("sigof");
+ }
+ if (flag_no_asm)
+ UNSET_RESERVED_WORD ("asm");
+ if (flag_no_asm || flag_traditional)
+ UNSET_RESERVED_WORD ("typeof");
+
+ token_count = init_parse ();
+ interface_unknown = 1;
+}
+
+void
+reinit_parse_for_function ()
+{
+ current_base_init_list = NULL_TREE;
+ current_member_init_list = NULL_TREE;
+}
+
+#ifdef __GNUC__
+__inline
+#endif
+void
+yyprint (file, yychar, yylval)
+ FILE *file;
+ int yychar;
+ YYSTYPE yylval;
+{
+ tree t;
+ switch (yychar)
+ {
+ case IDENTIFIER:
+ case TYPENAME:
+ case TYPESPEC:
+ case PTYPENAME:
+ case IDENTIFIER_DEFN:
+ case TYPENAME_DEFN:
+ case PTYPENAME_DEFN:
+ case TYPENAME_ELLIPSIS:
+ case SCSPEC:
+ case PRE_PARSED_CLASS_DECL:
+ t = yylval.ttype;
+ my_friendly_assert (TREE_CODE (t) == IDENTIFIER_NODE, 224);
+ if (IDENTIFIER_POINTER (t))
+ fprintf (file, " `%s'", IDENTIFIER_POINTER (t));
+ break;
+ case AGGR:
+ if (yylval.ttype == class_type_node)
+ fprintf (file, " `class'");
+ else if (yylval.ttype == record_type_node)
+ fprintf (file, " `struct'");
+ else if (yylval.ttype == union_type_node)
+ fprintf (file, " `union'");
+ else if (yylval.ttype == enum_type_node)
+ fprintf (file, " `enum'");
+ else if (yylval.ttype == signature_type_node)
+ fprintf (file, " `signature'");
+ else
+ my_friendly_abort (80);
+ break;
+ }
+}
+
+static int *reduce_count;
+int *token_count;
+
+#define REDUCE_LENGTH (sizeof (yyr2) / sizeof (yyr2[0]))
+#define TOKEN_LENGTH (256 + sizeof (yytname) / sizeof (yytname[0]))
+
+int *
+init_parse ()
+{
+#ifdef GATHER_STATISTICS
+ reduce_count = (int *)malloc (sizeof (int) * (REDUCE_LENGTH + 1));
+ bzero (reduce_count, sizeof (int) * (REDUCE_LENGTH + 1));
+ reduce_count += 1;
+ token_count = (int *)malloc (sizeof (int) * (TOKEN_LENGTH + 1));
+ bzero (token_count, sizeof (int) * (TOKEN_LENGTH + 1));
+ token_count += 1;
+#endif
+ return token_count;
+}
+
+#ifdef GATHER_STATISTICS
+void
+yyhook (yyn)
+ int yyn;
+{
+ reduce_count[yyn] += 1;
+}
+
+static int
+reduce_cmp (p, q)
+ int *p, *q;
+{
+ return reduce_count[*q] - reduce_count[*p];
+}
+
+static int
+token_cmp (p, q)
+ int *p, *q;
+{
+ return token_count[*q] - token_count[*p];
+}
+#endif
+
+void
+print_parse_statistics ()
+{
+#ifdef GATHER_STATISTICS
+#if YYDEBUG != 0
+ int i;
+ int maxlen = REDUCE_LENGTH;
+ unsigned *sorted;
+
+ if (reduce_count[-1] == 0)
+ return;
+
+ if (TOKEN_LENGTH > REDUCE_LENGTH)
+ maxlen = TOKEN_LENGTH;
+ sorted = (unsigned *) alloca (sizeof (int) * maxlen);
+
+ for (i = 0; i < TOKEN_LENGTH; i++)
+ sorted[i] = i;
+ qsort (sorted, TOKEN_LENGTH, sizeof (int), token_cmp);
+ for (i = 0; i < TOKEN_LENGTH; i++)
+ {
+ int index = sorted[i];
+ if (token_count[index] == 0)
+ break;
+ if (token_count[index] < token_count[-1])
+ break;
+ fprintf (stderr, "token %d, `%s', count = %d\n",
+ index, yytname[YYTRANSLATE (index)], token_count[index]);
+ }
+ fprintf (stderr, "\n");
+ for (i = 0; i < REDUCE_LENGTH; i++)
+ sorted[i] = i;
+ qsort (sorted, REDUCE_LENGTH, sizeof (int), reduce_cmp);
+ for (i = 0; i < REDUCE_LENGTH; i++)
+ {
+ int index = sorted[i];
+ if (reduce_count[index] == 0)
+ break;
+ if (reduce_count[index] < reduce_count[-1])
+ break;
+ fprintf (stderr, "rule %d, line %d, count = %d\n",
+ index, yyrline[index], reduce_count[index]);
+ }
+ fprintf (stderr, "\n");
+#endif
+#endif
+}
+
+/* Sets the value of the 'yydebug' variable to VALUE.
+ This is a function so we don't have to have YYDEBUG defined
+ in order to build the compiler. */
+void
+set_yydebug (value)
+ int value;
+{
+#if YYDEBUG != 0
+ extern int yydebug;
+ yydebug = value;
+#else
+ warning ("YYDEBUG not defined.");
+#endif
+}
+
+#ifdef SPEW_DEBUG
+const char *
+debug_yytranslate (value)
+ int value;
+{
+ return yytname[YYTRANSLATE (value)];
+}
+
+#endif
+
+/* Functions and data structures for #pragma interface.
+
+ `#pragma implementation' means that the main file being compiled
+ is considered to implement (provide) the classes that appear in
+ its main body. I.e., if this is file "foo.cc", and class `bar'
+ is defined in "foo.cc", then we say that "foo.cc implements bar".
+
+ All main input files "implement" themselves automagically.
+
+ `#pragma interface' means that unless this file (of the form "foo.h"
+ is not presently being included by file "foo.cc", the
+ CLASSTYPE_INTERFACE_ONLY bit gets set. The effect is that none
+ of the vtables nor any of the inline functions defined in foo.h
+ will ever be output.
+
+ There are cases when we want to link files such as "defs.h" and
+ "main.cc". In this case, we give "defs.h" a `#pragma interface',
+ and "main.cc" has `#pragma implementation "defs.h"'. */
+
+struct impl_files
+{
+ char *filename;
+ struct impl_files *next;
+};
+
+static struct impl_files *impl_file_chain;
+
+/* Helper function to load global variables with interface
+ information. */
+void
+extract_interface_info ()
+{
+ tree fileinfo = 0;
+
+ if (flag_alt_external_templates)
+ {
+ struct tinst_level *til = tinst_for_decl ();
+
+ if (til)
+ fileinfo = get_time_identifier (til->file);
+ }
+ if (!fileinfo)
+ fileinfo = get_time_identifier (input_filename);
+ fileinfo = IDENTIFIER_CLASS_VALUE (fileinfo);
+ interface_only = TREE_INT_CST_LOW (fileinfo);
+ if (!processing_template_defn || flag_external_templates)
+ interface_unknown = TREE_INT_CST_HIGH (fileinfo);
+}
+
+/* Return nonzero if S is not considered part of an
+ INTERFACE/IMPLEMENTATION pair. Otherwise, return 0. */
+static int
+interface_strcmp (s)
+ char *s;
+{
+ /* Set the interface/implementation bits for this scope. */
+ struct impl_files *ifiles;
+ char *s1;
+
+ for (ifiles = impl_file_chain; ifiles; ifiles = ifiles->next)
+ {
+ char *t1 = ifiles->filename;
+ s1 = s;
+
+ if (*s1 != *t1 || *s1 == 0)
+ continue;
+
+ while (*s1 == *t1 && *s1 != 0)
+ s1++, t1++;
+
+ /* A match. */
+ if (*s1 == *t1)
+ return 0;
+
+ /* Don't get faked out by xxx.yyy.cc vs xxx.zzz.cc. */
+ if (index (s1, '.') || index (t1, '.'))
+ continue;
+
+ if (*s1 == '\0' || s1[-1] != '.' || t1[-1] != '.')
+ continue;
+
+ /* A match. */
+ return 0;
+ }
+
+ /* No matches. */
+ return 1;
+}
+
+void
+set_typedecl_interface_info (prev, vars)
+ tree prev, vars;
+{
+ tree id = get_time_identifier (DECL_SOURCE_FILE (vars));
+ tree fileinfo = IDENTIFIER_CLASS_VALUE (id);
+ tree type = TREE_TYPE (vars);
+
+ CLASSTYPE_INTERFACE_ONLY (type) = TREE_INT_CST_LOW (fileinfo)
+ = interface_strcmp (FILE_NAME_NONDIRECTORY (DECL_SOURCE_FILE (vars)));
+}
+
+void
+set_vardecl_interface_info (prev, vars)
+ tree prev, vars;
+{
+ tree type = DECL_CONTEXT (vars);
+
+ if (CLASSTYPE_INTERFACE_KNOWN (type))
+ {
+ if (CLASSTYPE_INTERFACE_ONLY (type))
+ set_typedecl_interface_info (prev, TYPE_NAME (type));
+ else
+ CLASSTYPE_VTABLE_NEEDS_WRITING (type) = 1;
+ DECL_EXTERNAL (vars) = CLASSTYPE_INTERFACE_ONLY (type);
+ TREE_PUBLIC (vars) = 1;
+ }
+}
+
+/* Called from the top level: if there are any pending inlines to
+ do, set up to process them now. This function sets up the first function
+ to be parsed; after it has been, the rule for fndef in parse.y will
+ call process_next_inline to start working on the next one. */
+void
+do_pending_inlines ()
+{
+ struct pending_inline *prev = 0, *tail;
+ struct pending_inline *t;
+
+ /* Oops, we're still dealing with the last batch. */
+ if (yychar == PRE_PARSED_FUNCTION_DECL)
+ return;
+
+ /* Reverse the pending inline functions, since
+ they were cons'd instead of appended. */
+
+ for (t = pending_inlines; t; t = tail)
+ {
+ t->deja_vu = 1;
+ tail = t->next;
+ t->next = prev;
+ prev = t;
+ }
+ /* Reset to zero so that if the inline functions we are currently
+ processing define inline functions of their own, that is handled
+ correctly. ??? This hasn't been checked in a while. */
+ pending_inlines = 0;
+
+ /* Now start processing the first inline function. */
+ t = prev;
+ my_friendly_assert ((t->parm_vec == NULL_TREE) == (t->bindings == NULL_TREE),
+ 226);
+ if (t->parm_vec)
+ push_template_decls (t->parm_vec, t->bindings, 0);
+ if (t->len > 0)
+ {
+ feed_input (t->buf, t->len, t->can_free ? &inline_text_obstack : 0);
+ lineno = t->lineno;
+#if 0
+ if (input_filename != t->filename)
+ {
+ input_filename = t->filename;
+ /* Get interface/implementation back in sync. */
+ extract_interface_info ();
+ }
+#else
+ input_filename = t->filename;
+ interface_unknown = t->interface == 1;
+ interface_only = t->interface == 0;
+#endif
+ yychar = PRE_PARSED_FUNCTION_DECL;
+ }
+ /* Pass back a handle on the rest of the inline functions, so that they
+ can be processed later. */
+ yylval.ttype = build_tree_list ((tree) t, t->fndecl);
+#if 0
+ if (flag_default_inline && t->fndecl
+ /* If we're working from a template, don't change
+ the `inline' state. */
+ && t->parm_vec == NULL_TREE)
+ DECL_INLINE (t->fndecl) = 1;
+#endif
+ DECL_PENDING_INLINE_INFO (t->fndecl) = 0;
+}
+
+extern struct pending_input *to_be_restored;
+static int nextchar = -1;
+
+/* Called from the fndecl rule in the parser when the function just parsed
+ was declared using a PRE_PARSED_FUNCTION_DECL (i.e. came from
+ do_pending_inlines). */
+void
+process_next_inline (t)
+ tree t;
+{
+ struct pending_inline *i = (struct pending_inline *) TREE_PURPOSE (t);
+ my_friendly_assert ((i->parm_vec == NULL_TREE) == (i->bindings == NULL_TREE),
+ 227);
+ if (i->parm_vec)
+ pop_template_decls (i->parm_vec, i->bindings, 0);
+ i = i->next;
+ if (yychar == YYEMPTY)
+ yychar = yylex ();
+ if (yychar != END_OF_SAVED_INPUT)
+ {
+ error ("parse error at end of saved function text");
+ /* restore_pending_input will abort unless yychar is either
+ * END_OF_SAVED_INPUT or YYEMPTY; since we already know we're
+ * hosed, feed back YYEMPTY.
+ * We also need to discard nextchar, since that may have gotten
+ * set as well.
+ */
+ nextchar = -1;
+ }
+ yychar = YYEMPTY;
+ if (to_be_restored == 0)
+ my_friendly_abort (123);
+ restore_pending_input (to_be_restored);
+ to_be_restored = 0;
+ if (i && i->fndecl != NULL_TREE)
+ {
+ my_friendly_assert ((i->parm_vec == NULL_TREE) == (i->bindings == NULL_TREE),
+ 228);
+ if (i->parm_vec)
+ push_template_decls (i->parm_vec, i->bindings, 0);
+ feed_input (i->buf, i->len, i->can_free ? &inline_text_obstack : 0);
+ lineno = i->lineno;
+ input_filename = i->filename;
+ yychar = PRE_PARSED_FUNCTION_DECL;
+ yylval.ttype = build_tree_list ((tree) i, i->fndecl);
+#if 0
+ if (flag_default_inline
+ /* If we're working from a template, don't change
+ the `inline' state. */
+ && i->parm_vec == NULL_TREE)
+ DECL_INLINE (i->fndecl) = 1;
+#endif
+ DECL_PENDING_INLINE_INFO (i->fndecl) = 0;
+ }
+ if (i)
+ {
+ interface_unknown = i->interface == 1;
+ interface_only = i->interface == 0;
+ }
+ else
+ extract_interface_info ();
+}
+
+/* Since inline methods can refer to text which has not yet been seen,
+ we store the text of the method in a structure which is placed in the
+ DECL_PENDING_INLINE_INFO field of the FUNCTION_DECL.
+ After parsing the body of the class definition, the FUNCTION_DECL's are
+ scanned to see which ones have this field set. Those are then digested
+ one at a time.
+
+ This function's FUNCTION_DECL will have a bit set in its common so
+ that we know to watch out for it. */
+
+static void
+consume_string (this_obstack, matching_char)
+ register struct obstack *this_obstack;
+ int matching_char;
+{
+ register int c;
+ int starting_lineno = lineno;
+ do
+ {
+ c = getch ();
+ if (c == EOF)
+ {
+ int save_lineno = lineno;
+ lineno = starting_lineno;
+ if (matching_char == '"')
+ error ("end of file encountered inside string constant");
+ else
+ error ("end of file encountered inside character constant");
+ lineno = save_lineno;
+ return;
+ }
+ if (c == '\\')
+ {
+ obstack_1grow (this_obstack, c);
+ c = getch ();
+ obstack_1grow (this_obstack, c);
+
+ /* Make sure we continue the loop */
+ c = 0;
+ continue;
+ }
+ if (c == '\n')
+ {
+ if (pedantic)
+ pedwarn ("ANSI C++ forbids newline in string constant");
+ lineno++;
+ }
+ obstack_1grow (this_obstack, c);
+ }
+ while (c != matching_char);
+}
+
+static int nextyychar = YYEMPTY;
+static YYSTYPE nextyylval;
+
+struct pending_input {
+ int nextchar, yychar, nextyychar, eof;
+ YYSTYPE yylval, nextyylval;
+ struct obstack token_obstack;
+ int first_token;
+};
+
+struct pending_input *
+save_pending_input ()
+{
+ struct pending_input *p;
+ p = (struct pending_input *) xmalloc (sizeof (struct pending_input));
+ p->nextchar = nextchar;
+ p->yychar = yychar;
+ p->nextyychar = nextyychar;
+ p->yylval = yylval;
+ p->nextyylval = nextyylval;
+ p->eof = end_of_file;
+ yychar = nextyychar = YYEMPTY;
+ nextchar = -1;
+ p->first_token = first_token;
+ p->token_obstack = token_obstack;
+
+ first_token = 0;
+ gcc_obstack_init (&token_obstack);
+ end_of_file = 0;
+ return p;
+}
+
+void
+restore_pending_input (p)
+ struct pending_input *p;
+{
+ my_friendly_assert (nextchar == -1, 229);
+ nextchar = p->nextchar;
+ my_friendly_assert (yychar == YYEMPTY || yychar == END_OF_SAVED_INPUT, 230);
+ yychar = p->yychar;
+ my_friendly_assert (nextyychar == YYEMPTY, 231);
+ nextyychar = p->nextyychar;
+ yylval = p->yylval;
+ nextyylval = p->nextyylval;
+ first_token = p->first_token;
+ obstack_free (&token_obstack, (char *) 0);
+ token_obstack = p->token_obstack;
+ end_of_file = p->eof;
+ free (p);
+}
+
+/* Return next non-whitespace input character, which may come
+ from `finput', or from `nextchar'. */
+static int
+yynextch ()
+{
+ int c;
+
+ if (nextchar >= 0)
+ {
+ c = nextchar;
+ nextchar = -1;
+ }
+ else c = getch ();
+ return skip_white_space (c);
+}
+
+/* Unget character CH from the input stream.
+ If RESCAN is non-zero, then we want to `see' this
+ character as the next input token. */
+void
+yyungetc (ch, rescan)
+ int ch;
+ int rescan;
+{
+ /* Unget a character from the input stream. */
+ if (yychar == YYEMPTY || rescan == 0)
+ {
+ if (nextchar >= 0)
+ put_back (nextchar);
+ nextchar = ch;
+ }
+ else
+ {
+ my_friendly_assert (nextyychar == YYEMPTY, 232);
+ nextyychar = yychar;
+ nextyylval = yylval;
+ yychar = ch;
+ }
+}
+
+/* This function stores away the text for an inline function that should
+ be processed later. It decides how much later, and may need to move
+ the info between obstacks; therefore, the caller should not refer to
+ the T parameter after calling this function.
+
+ This function also stores the list of template-parameter bindings that
+ will be needed for expanding the template, if any. */
+
+static void
+store_pending_inline (decl, t)
+ tree decl;
+ struct pending_inline *t;
+{
+ extern int processing_template_defn;
+ int delay_to_eof = 0;
+ struct pending_inline **inlines;
+
+ t->fndecl = decl;
+ /* Default: compile right away, and no extra bindings are needed. */
+ t->parm_vec = t->bindings = 0;
+ if (processing_template_defn)
+ {
+ tree type = current_class_type;
+ /* Assumption: In this (possibly) nested class sequence, only
+ one name will have template parms. */
+ while (type && TREE_CODE_CLASS (TREE_CODE (type)) == 't')
+ {
+ tree decl = TYPE_NAME (type);
+ tree tmpl = IDENTIFIER_TEMPLATE (DECL_NAME (decl));
+ if (tmpl)
+ {
+ t->parm_vec = DECL_TEMPLATE_INFO (TREE_PURPOSE (tmpl))->parm_vec;
+ t->bindings = TREE_VALUE (tmpl);
+ }
+ type = DECL_CONTEXT (decl);
+ }
+ if (TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE
+ || TREE_CODE (TREE_TYPE (decl)) == FUNCTION_TYPE)
+ {
+ if (TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
+ my_friendly_assert (TYPE_MAX_VALUE (TREE_TYPE (decl)) == current_class_type,
+ 233);
+
+ /* Inline functions can be compiled immediately. Other functions
+ will be output separately, so if we're in interface-only mode,
+ punt them now, or output them now if we're doing implementations
+ and we know no overrides will exist. Otherwise, we delay until
+ end-of-file, to see if the definition is really required. */
+ if (DECL_INLINE (decl))
+ /* delay_to_eof == 0 */;
+ else if (current_class_type && !interface_unknown)
+ {
+ if (interface_only)
+ {
+#if 0
+ print_node_brief (stderr, "\ndiscarding text for ", decl, 0);
+#endif
+ if (t->can_free)
+ obstack_free (&inline_text_obstack, t->buf);
+ DECL_PENDING_INLINE_INFO (decl) = 0;
+ return;
+ }
+ }
+ /* Don't delay the processing of virtual functions. */
+ else if (DECL_VINDEX (decl) == NULL_TREE)
+ delay_to_eof = 1;
+ }
+ else
+ my_friendly_abort (58);
+ }
+
+ if (delay_to_eof)
+ {
+ extern struct pending_inline *pending_template_expansions;
+
+ if (t->can_free)
+ {
+ char *free_to = t->buf;
+ t->buf = (char *) obstack_copy (&permanent_obstack, t->buf,
+ t->len + 1);
+ t = (struct pending_inline *) obstack_copy (&permanent_obstack,
+ (char *)t, sizeof (*t));
+ obstack_free (&inline_text_obstack, free_to);
+ }
+ inlines = &pending_template_expansions;
+ t->can_free = 0;
+ }
+ else
+ {
+ inlines = &pending_inlines;
+ DECL_PENDING_INLINE_INFO (decl) = t;
+ }
+
+ /* Because we use obstacks, we must process these in precise order. */
+ t->next = *inlines;
+ *inlines = t;
+}
+
+void reinit_parse_for_block ();
+
+void
+reinit_parse_for_method (yychar, decl)
+ int yychar;
+ tree decl;
+{
+ int len;
+ int starting_lineno = lineno;
+ char *starting_filename = input_filename;
+
+ reinit_parse_for_block (yychar, &inline_text_obstack, 0);
+
+ len = obstack_object_size (&inline_text_obstack);
+ current_base_init_list = NULL_TREE;
+ current_member_init_list = NULL_TREE;
+ if (decl == void_type_node
+ || (current_class_type && TYPE_REDEFINED (current_class_type)))
+ {
+ /* Happens when we get two declarations of the same
+ function in the same scope. */
+ char *buf = obstack_finish (&inline_text_obstack);
+ obstack_free (&inline_text_obstack, buf);
+ return;
+ }
+ else
+ {
+ struct pending_inline *t;
+ char *buf = obstack_finish (&inline_text_obstack);
+
+ t = (struct pending_inline *) obstack_alloc (&inline_text_obstack,
+ sizeof (struct pending_inline));
+ t->lineno = starting_lineno;
+ t->filename = starting_filename;
+ t->token = YYEMPTY;
+ t->token_value = 0;
+ t->buf = buf;
+ t->len = len;
+ t->can_free = 1;
+ t->deja_vu = 0;
+ if (interface_unknown && processing_template_defn && flag_external_templates && ! DECL_IN_SYSTEM_HEADER (decl))
+ warn_if_unknown_interface ();
+ t->interface = (interface_unknown ? 1 : (interface_only ? 0 : 2));
+ store_pending_inline (decl, t);
+ }
+}
+
+/* Consume a block -- actually, a method or template definition beginning
+ with `:' or `{' -- and save it away on the specified obstack.
+
+ Argument IS_TEMPLATE indicates which set of error messages should be
+ output if something goes wrong. This should really be cleaned up somehow,
+ without loss of clarity. */
+void
+reinit_parse_for_block (yychar, obstackp, is_template)
+ int yychar;
+ struct obstack *obstackp;
+ int is_template;
+{
+ register int c = 0;
+ int blev = 1;
+ int starting_lineno = lineno;
+ char *starting_filename = input_filename;
+ int len;
+ int look_for_semicolon = 0;
+ int look_for_lbrac = 0;
+
+ if (yychar == '{')
+ obstack_1grow (obstackp, '{');
+ else if (yychar == '=')
+ look_for_semicolon = 1;
+ else if (yychar != ':' && (yychar != RETURN || is_template))
+ {
+ yyerror (is_template
+ ? "parse error in template specification"
+ : "parse error in method specification");
+ obstack_1grow (obstackp, '{');
+ }
+ else
+ {
+ obstack_1grow (obstackp, yychar);
+ look_for_lbrac = 1;
+ blev = 0;
+ }
+
+ if (nextchar != EOF)
+ {
+ c = nextchar;
+ nextchar = EOF;
+ }
+ else
+ c = getch ();
+
+ while (c != EOF)
+ {
+ int this_lineno = lineno;
+
+ c = skip_white_space (c);
+
+ /* Don't lose our cool if there are lots of comments. */
+ if (lineno == this_lineno + 1)
+ obstack_1grow (obstackp, '\n');
+ else if (lineno == this_lineno)
+ ;
+ else if (lineno - this_lineno < 10)
+ {
+ int i;
+ for (i = lineno - this_lineno; i > 0; i--)
+ obstack_1grow (obstackp, '\n');
+ }
+ else
+ {
+ char buf[16];
+ sprintf (buf, "\n# %d \"", lineno);
+ len = strlen (buf);
+ obstack_grow (obstackp, buf, len);
+
+ len = strlen (input_filename);
+ obstack_grow (obstackp, input_filename, len);
+ obstack_1grow (obstackp, '\"');
+ obstack_1grow (obstackp, '\n');
+ }
+
+ while (c > ' ') /* ASCII dependent... */
+ {
+ obstack_1grow (obstackp, c);
+ if (c == '{')
+ {
+ look_for_lbrac = 0;
+ blev++;
+ }
+ else if (c == '}')
+ {
+ blev--;
+ if (blev == 0 && !look_for_semicolon)
+ goto done;
+ }
+ else if (c == '\\')
+ {
+ /* Don't act on the next character...e.g, doing an escaped
+ double-quote. */
+ c = getch ();
+ if (c == EOF)
+ {
+ error_with_file_and_line (starting_filename,
+ starting_lineno,
+ "end of file read inside definition");
+ goto done;
+ }
+ obstack_1grow (obstackp, c);
+ }
+ else if (c == '\"')
+ consume_string (obstackp, c);
+ else if (c == '\'')
+ consume_string (obstackp, c);
+ else if (c == ';')
+ {
+ if (look_for_lbrac)
+ {
+ error (is_template
+ ? "template body missing"
+ : "function body for constructor missing");
+ obstack_1grow (obstackp, '{');
+ obstack_1grow (obstackp, '}');
+ len += 2;
+ goto done;
+ }
+ else if (look_for_semicolon && blev == 0)
+ goto done;
+ }
+ c = getch ();
+ }
+
+ if (c == EOF)
+ {
+ error_with_file_and_line (starting_filename,
+ starting_lineno,
+ "end of file read inside definition");
+ goto done;
+ }
+ else if (c != '\n')
+ {
+ obstack_1grow (obstackp, c);
+ c = getch ();
+ }
+ }
+ done:
+ obstack_1grow (obstackp, '\0');
+}
+
+/* Build a default function named NAME for type TYPE.
+ KIND says what to build.
+
+ When KIND == 0, build default destructor.
+ When KIND == 1, build virtual destructor.
+ When KIND == 2, build default constructor.
+ When KIND == 3, build default X(const X&) constructor.
+ When KIND == 4, build default X(X&) constructor.
+ When KIND == 5, build default operator = (const X&).
+ When KIND == 6, build default operator = (X&). */
+
+tree
+cons_up_default_function (type, name, fields, kind)
+ tree type, name, fields;
+ int kind;
+{
+ extern tree void_list_node;
+ char *func_buf = NULL;
+ int func_len = 0;
+ tree declspecs = NULL_TREE;
+ tree fn, args;
+ tree argtype;
+ int retref = 0;
+
+ name = constructor_name (name);
+ switch (kind)
+ {
+ /* Destructors. */
+ case 1:
+ declspecs = build_decl_list (NULL_TREE, ridpointers [(int) RID_VIRTUAL]);
+ /* Fall through... */
+ case 0:
+ name = build_parse_node (BIT_NOT_EXPR, name);
+ /* Fall through... */
+ case 2:
+ /* Default constructor. */
+ args = void_list_node;
+ {
+ if (declspecs)
+ declspecs = decl_tree_cons (NULL_TREE,
+ ridpointers [(int) RID_INLINE],
+ declspecs);
+ else
+ declspecs = build_decl_list (NULL_TREE, ridpointers [(int) RID_INLINE]);
+ }
+ break;
+
+ case 3:
+ type = build_type_variant (type, 1, 0);
+ /* Fall through... */
+ case 4:
+ /* According to ARM $12.8, the default copy ctor will be declared, but
+ not defined, unless it's needed. So we mark this as `inline'; that
+ way, if it's never used it won't be emitted. */
+ declspecs = build_decl_list (NULL_TREE, ridpointers [(int) RID_INLINE]);
+
+ argtype = build_reference_type (type);
+ args = tree_cons (NULL_TREE,
+ build_tree_list (hash_tree_chain (argtype, NULL_TREE),
+ get_identifier ("_ctor_arg")),
+ void_list_node);
+ default_copy_constructor_body (&func_buf, &func_len, type, fields);
+ break;
+
+ case 5:
+ type = build_type_variant (type, 1, 0);
+ /* Fall through... */
+ case 6:
+ retref = 1;
+ declspecs =
+ decl_tree_cons (NULL_TREE, name,
+ decl_tree_cons (NULL_TREE,
+ ridpointers [(int) RID_INLINE],
+ NULL_TREE));
+
+ name = ansi_opname [(int) MODIFY_EXPR];
+
+ argtype = build_reference_type (type);
+ args = tree_cons (NULL_TREE,
+ build_tree_list (hash_tree_chain (argtype, NULL_TREE),
+ get_identifier ("_ctor_arg")),
+ void_list_node);
+ default_assign_ref_body (&func_buf, &func_len, type, fields);
+ break;
+
+ default:
+ my_friendly_abort (59);
+ }
+
+ if (!func_buf)
+ {
+ func_len = 2;
+ func_buf = obstack_alloc (&inline_text_obstack, func_len);
+ strcpy (func_buf, "{}");
+ }
+
+ TREE_PARMLIST (args) = 1;
+
+ {
+ tree declarator = build_parse_node (CALL_EXPR, name, args, NULL_TREE);
+ if (retref)
+ declarator = build_parse_node (ADDR_EXPR, declarator);
+
+ fn = start_method (declspecs, declarator, NULL_TREE);
+ }
+
+ if (fn == void_type_node)
+ return fn;
+
+ current_base_init_list = NULL_TREE;
+ current_member_init_list = NULL_TREE;
+
+ {
+ struct pending_inline *t;
+
+ t = (struct pending_inline *) obstack_alloc (&inline_text_obstack,
+ sizeof (struct pending_inline));
+ t->lineno = lineno;
+
+#if 1
+ t->filename = input_filename;
+#else /* This breaks; why? */
+#define MGMSG "(synthetic code at) "
+ t->filename = obstack_alloc (&inline_text_obstack,
+ strlen (input_filename) + sizeof (MGMSG) + 1);
+ strcpy (t->filename, MGMSG);
+ strcat (t->filename, input_filename);
+#endif
+ t->token = YYEMPTY;
+ t->token_value = 0;
+ t->buf = func_buf;
+ t->len = func_len;
+ t->can_free = 1;
+ t->deja_vu = 0;
+ if (interface_unknown && processing_template_defn && flag_external_templates && ! DECL_IN_SYSTEM_HEADER (fn))
+ warn_if_unknown_interface ();
+ t->interface = (interface_unknown ? 1 : (interface_only ? 0 : 2));
+ store_pending_inline (fn, t);
+ if (interface_unknown)
+ TREE_PUBLIC (fn) = 0;
+ else
+ {
+ TREE_PUBLIC (fn) = 1;
+ DECL_EXTERNAL (fn) = interface_only;
+ }
+ }
+
+ finish_method (fn);
+
+#ifdef DEBUG_DEFAULT_FUNCTIONS
+ { char *fn_type = NULL;
+ tree t = name;
+ switch (kind)
+ {
+ case 0: fn_type = "default destructor"; break;
+ case 1: fn_type = "virtual destructor"; break;
+ case 2: fn_type = "default constructor"; break;
+ case 3: fn_type = "default X(const X&)"; break;
+ case 4: fn_type = "default X(X&)"; break;
+ }
+ if (fn_type)
+ {
+ if (TREE_CODE (name) == BIT_NOT_EXPR)
+ t = TREE_OPERAND (name, 0);
+ fprintf (stderr, "[[[[ %s for %s:\n%s]]]]\n", fn_type,
+ IDENTIFIER_POINTER (t), func_buf);
+ }
+ }
+#endif /* DEBUG_DEFAULT_FUNCTIONS */
+
+ DECL_CLASS_CONTEXT (fn) = TYPE_MAIN_VARIANT (type);
+
+ /* Show that this function was generated by the compiler. */
+ SET_DECL_ARTIFICIAL (fn);
+
+ return fn;
+}
+
+/* Used by default_copy_constructor_body. For the anonymous union
+ in TYPE, return the member that is at least as large as the rest
+ of the members, so we can copy it. */
+static tree
+largest_union_member (type)
+ tree type;
+{
+ tree f, type_size = TYPE_SIZE (type);
+
+ for (f = TYPE_FIELDS (type); f; f = TREE_CHAIN (f))
+ if (simple_cst_equal (DECL_SIZE (f), type_size))
+ return f;
+
+ /* We should always find one. */
+ my_friendly_abort (323);
+ return NULL_TREE;
+}
+
+/* Construct the body of a default assignment operator.
+ Mostly copied directly from default_copy_constructor_body. */
+static void
+default_assign_ref_body (bufp, lenp, type, fields)
+ char **bufp;
+ int *lenp;
+ tree type, fields;
+{
+ static struct obstack body;
+ static int inited = FALSE;
+ int n_bases = CLASSTYPE_N_BASECLASSES (type);
+ char *tbuf;
+ int tgot, tneed;
+
+ if (!inited)
+ {
+ obstack_init (&body);
+ inited = TRUE;
+ }
+ body.next_free = body.object_base;
+
+ obstack_1grow (&body, '{');
+
+ /* Small buffer for sprintf(). */
+
+ tgot = 100;
+ tbuf = (char *) alloca (tgot);
+
+ /* If we don't need a real op=, just do a bitwise copy. */
+ if (! TYPE_HAS_COMPLEX_ASSIGN_REF (type))
+ {
+ tbuf = "{__builtin_memcpy(this,&_ctor_arg,sizeof(_ctor_arg));return *this;}";
+ *lenp = strlen (tbuf);
+ *bufp = obstack_alloc (&inline_text_obstack, *lenp + 1);
+ strcpy (*bufp, tbuf);
+ return;
+ }
+
+ if (TREE_CODE (type) == UNION_TYPE)
+ {
+ if (fields)
+ {
+ tree main = fields;
+ char * s;
+ tree f;
+
+ for (f = TREE_CHAIN (fields); f; f = TREE_CHAIN (f))
+ if (tree_int_cst_lt (TYPE_SIZE (TREE_TYPE (main)),
+ TYPE_SIZE (TREE_TYPE (f))))
+ main = f;
+
+ s = IDENTIFIER_POINTER (DECL_NAME (main));
+
+ tneed = (2 * strlen (s)) + 28;
+ if (tgot < tneed)
+ {
+ tgot = tneed;
+ tbuf = (char *) alloca (tgot);
+ }
+
+ sprintf (tbuf, "{%s=_ctor_arg.%s;return *this;}", s, s);
+ }
+ else
+ tbuf = "{}";
+
+ *lenp = strlen (tbuf);
+ *bufp = obstack_alloc (&inline_text_obstack, *lenp + 1);
+ strcpy (*bufp, tbuf);
+ return;
+ }
+
+ /* Construct base classes...
+ FIXME: Does not deal with multiple inheritance and virtual bases
+ correctly. See g++.old-deja/g++.jason/opeq5.C for a testcase.
+ We need to do wacky things if everything between us and the virtual
+ base (by all paths) has a "complex" op=. */
+
+ if (n_bases)
+ {
+ tree bases = TYPE_BINFO_BASETYPES (type);
+ int i = 0;
+
+ for (i = 0; i < n_bases; i++)
+ {
+ tree binfo = TREE_VEC_ELT (bases, i);
+ tree btype, name;
+ char *s;
+
+ btype = BINFO_TYPE (binfo);
+ name = TYPE_NESTED_NAME (btype);
+ s = IDENTIFIER_POINTER (name);
+
+ tneed = (2 * strlen (s)) + 42;
+ if (tgot < tneed)
+ {
+ tgot = tneed;
+ tbuf = (char *) alloca (tgot);
+ }
+
+ sprintf (tbuf, "%s::operator=((%s%s ::%s&)_ctor_arg);", s,
+ TYPE_READONLY (type) ? "const " : "",
+ CLASSTYPE_DECLARED_CLASS (btype) ? "class" : "struct",
+ s);
+ obstack_grow (&body, tbuf, strlen (tbuf));
+ }
+ }
+
+ /* Construct fields. */
+
+ if (fields)
+ {
+ tree f;
+
+ for (f = fields; f; f = TREE_CHAIN (f))
+ {
+ if (TREE_CODE (f) == FIELD_DECL && ! DECL_VIRTUAL_P (f))
+ {
+ char *s;
+ tree x;
+ tree t = TREE_TYPE (f);
+
+ if (DECL_NAME (f))
+ x = f;
+ else if (t != NULL_TREE
+ && TREE_CODE (t) == UNION_TYPE
+ && ((TREE_CODE (TYPE_NAME (t)) == IDENTIFIER_NODE
+ && ANON_AGGRNAME_P (TYPE_NAME (t)))
+ || (TREE_CODE (TYPE_NAME (t)) == TYPE_DECL
+ && ANON_AGGRNAME_P (TYPE_IDENTIFIER (t))))
+ && TYPE_FIELDS (t) != NULL_TREE)
+ x = largest_union_member (t);
+ else
+ continue;
+
+ s = IDENTIFIER_POINTER (DECL_NAME (x));
+ tneed = (2 * strlen (s)) + 13;
+ if (tgot < tneed)
+ {
+ tgot = tneed;
+ tbuf = (char *) alloca (tgot);
+ }
+
+ sprintf (tbuf, "%s=_ctor_arg.%s;", s, s);
+ obstack_grow (&body, tbuf, strlen (tbuf));
+ }
+ }
+ }
+
+ obstack_grow (&body, "return *this;}", 15);
+
+ *lenp = obstack_object_size (&body) - 1;
+ *bufp = obstack_alloc (&inline_text_obstack, *lenp);
+
+ strcpy (*bufp, body.object_base);
+}
+
+/* Construct the body of a default copy constructor. */
+static void
+default_copy_constructor_body (bufp, lenp, type, fields)
+ char **bufp;
+ int *lenp;
+ tree type, fields;
+{
+ static struct obstack prologue;
+ static int inited = FALSE;
+ int n_bases = CLASSTYPE_N_BASECLASSES (type);
+ char sep = ':';
+ char *tbuf;
+ int tgot, tneed;
+
+ /* Create a buffer to call base class constructors and construct members
+ (fields). */
+
+ if (!inited)
+ {
+ obstack_init (&prologue);
+ inited = TRUE;
+ }
+ prologue.next_free = prologue.object_base;
+
+ /* If we don't need a real copy ctor, just do a bitwise copy. */
+ if (! TYPE_HAS_COMPLEX_INIT_REF (type))
+ {
+ tbuf = "{__builtin_memcpy(this,&_ctor_arg,sizeof(_ctor_arg));}";
+ *lenp = strlen (tbuf);
+ *bufp = obstack_alloc (&inline_text_obstack, *lenp + 1);
+ strcpy (*bufp, tbuf);
+ return;
+ }
+
+ /* Small buffer for sprintf(). */
+
+ tgot = 100;
+ tbuf = (char *) alloca (tgot);
+
+ if (TREE_CODE (type) == UNION_TYPE)
+ {
+ if (fields)
+ {
+ tree main = fields;
+ char * s;
+ tree f;
+
+ for (f = TREE_CHAIN (fields); f; f = TREE_CHAIN (f))
+ if (tree_int_cst_lt (TYPE_SIZE (TREE_TYPE (main)),
+ TYPE_SIZE (TREE_TYPE (f))))
+ main = f;
+
+ s = IDENTIFIER_POINTER (DECL_NAME (main));
+ tneed = (2 * strlen (s)) + 16;
+ if (tgot < tneed)
+ {
+ tgot = tneed;
+ tbuf = (char *) alloca (tgot);
+ }
+
+ sprintf (tbuf, ":%s(_ctor_arg.%s){}", s, s);
+ }
+ else
+ tbuf = "{}";
+
+ *lenp = strlen (tbuf);
+ *bufp = obstack_alloc (&inline_text_obstack, *lenp + 1);
+ strcpy (*bufp, tbuf);
+ return;
+ }
+
+ /* Construct base classes... */
+
+ if (n_bases)
+ {
+ /* Note that CLASSTYPE_VBASECLASSES isn't set yet... */
+ tree v = get_vbase_types (type);
+ tree bases = TYPE_BINFO_BASETYPES (type);
+ int i = 0;
+
+ for (;;)
+ {
+ tree binfo, btype, name;
+ char *s;
+
+ if (v)
+ {
+ binfo = v;
+ v = TREE_CHAIN (v);
+ }
+ else if (i < n_bases)
+ {
+ binfo = TREE_VEC_ELT (bases, i++);
+ if (TREE_VIA_VIRTUAL (binfo))
+ continue;
+ }
+ else
+ break;
+
+ btype = BINFO_TYPE (binfo);
+ name = TYPE_NESTED_NAME (btype);
+ s = IDENTIFIER_POINTER (name);
+
+ tneed = (2 * strlen (s)) + 39;
+ if (tgot < tneed)
+ {
+ tgot = tneed;
+ tbuf = (char *) alloca (tgot);
+ }
+
+ sprintf (tbuf, "%c%s((%s%s ::%s&)_ctor_arg)", sep, s,
+ TYPE_READONLY (type) ? "const " : "",
+ CLASSTYPE_DECLARED_CLASS (btype) ? "class" : "struct",
+ s);
+ sep = ',';
+ obstack_grow (&prologue, tbuf, strlen (tbuf));
+ }
+ }
+
+ /* Construct fields. */
+
+ if (fields)
+ {
+ tree f;
+
+ for (f = fields; f; f = TREE_CHAIN (f))
+ {
+ if (TREE_CODE (f) == FIELD_DECL && ! DECL_VIRTUAL_P (f))
+ {
+ char *s;
+ tree x;
+ tree t = TREE_TYPE (f);
+
+ if (DECL_NAME (f))
+ x = f;
+ else if (t != NULL_TREE
+ && TREE_CODE (t) == UNION_TYPE
+ && ((TREE_CODE (TYPE_NAME (t)) == IDENTIFIER_NODE
+ && ANON_AGGRNAME_P (TYPE_NAME (t)))
+ || (TREE_CODE (TYPE_NAME (t)) == TYPE_DECL
+ && ANON_AGGRNAME_P (TYPE_IDENTIFIER (t))))
+ && TYPE_FIELDS (t) != NULL_TREE)
+ x = largest_union_member (t);
+ else
+ continue;
+
+ s = IDENTIFIER_POINTER (DECL_NAME (x));
+ tneed = (2 * strlen (s)) + 30;
+ if (tgot < tneed)
+ {
+ tgot = tneed;
+ tbuf = (char *) alloca (tgot);
+ }
+
+ sprintf (tbuf, "%c%s(_ctor_arg.%s)", sep, s, s);
+ sep = ',';
+ obstack_grow (&prologue, tbuf, strlen (tbuf));
+ }
+ }
+ }
+
+ /* Concatenate constructor body to prologue. */
+
+ *lenp = obstack_object_size (&prologue) + 2;
+ *bufp = obstack_alloc (&inline_text_obstack, *lenp + 1);
+
+ obstack_1grow (&prologue, '\0');
+
+ strcpy (*bufp, prologue.object_base);
+ strcat (*bufp, "{}");
+}
+
+/* Heuristic to tell whether the user is missing a semicolon
+ after a struct or enum declaration. Emit an error message
+ if we know the user has blown it. */
+void
+check_for_missing_semicolon (type)
+ tree type;
+{
+ if (yychar < 0)
+ yychar = yylex ();
+
+ if (yychar > 255
+ && yychar != SCSPEC
+ && yychar != IDENTIFIER
+ && yychar != TYPENAME)
+ {
+ if (ANON_AGGRNAME_P (TYPE_IDENTIFIER (type)))
+ error ("semicolon missing after %s declaration",
+ TREE_CODE (type) == ENUMERAL_TYPE ? "enum" : "struct");
+ else
+ error ("semicolon missing after declaration of `%s'",
+ TYPE_NAME_STRING (type));
+ shadow_tag (build_tree_list (0, type));
+ }
+ /* Could probably also hack cases where class { ... } f (); appears. */
+ clear_anon_tags ();
+}
+
+void
+note_got_semicolon (type)
+ tree type;
+{
+ if (TREE_CODE_CLASS (TREE_CODE (type)) != 't')
+ my_friendly_abort (60);
+ if (IS_AGGR_TYPE (type))
+ CLASSTYPE_GOT_SEMICOLON (type) = 1;
+}
+
+void
+note_list_got_semicolon (declspecs)
+ tree declspecs;
+{
+ tree link;
+
+ for (link = declspecs; link; link = TREE_CHAIN (link))
+ {
+ tree type = TREE_VALUE (link);
+ if (TREE_CODE_CLASS (TREE_CODE (type)) == 't')
+ note_got_semicolon (type);
+ }
+ clear_anon_tags ();
+}
+
+/* If C is not whitespace, return C.
+ Otherwise skip whitespace and return first nonwhite char read. */
+
+static int
+skip_white_space (c)
+ register int c;
+{
+ for (;;)
+ {
+ switch (c)
+ {
+ case '\n':
+ c = check_newline ();
+ break;
+
+ case ' ':
+ case '\t':
+ case '\f':
+ case '\r':
+ case '\v':
+ case '\b':
+ do
+ c = getch ();
+ while (c == ' ' || c == '\t');
+ break;
+
+ case '\\':
+ c = getch ();
+ if (c == '\n')
+ lineno++;
+ else
+ error ("stray '\\' in program");
+ c = getch ();
+ break;
+
+ default:
+ return (c);
+ }
+ }
+}
+
+
+
+/* Make the token buffer longer, preserving the data in it.
+ P should point to just beyond the last valid character in the old buffer.
+ The value we return is a pointer to the new buffer
+ at a place corresponding to P. */
+
+static char *
+extend_token_buffer (p)
+ char *p;
+{
+ int offset = p - token_buffer;
+
+ maxtoken = maxtoken * 2 + 10;
+ token_buffer = (char *) xrealloc (token_buffer, maxtoken + 2);
+
+ return token_buffer + offset;
+}
+
+static int
+get_last_nonwhite_on_line ()
+{
+ register int c;
+
+ /* Is this the last nonwhite stuff on the line? */
+ if (nextchar >= 0)
+ c = nextchar, nextchar = -1;
+ else
+ c = getch ();
+
+ while (c == ' ' || c == '\t')
+ c = getch ();
+ return c;
+}
+
+/* At the beginning of a line, increment the line number
+ and process any #-directive on this line.
+ If the line is a #-directive, read the entire line and return a newline.
+ Otherwise, return the line's first non-whitespace character. */
+
+int
+check_newline ()
+{
+ register int c;
+ register int token;
+
+ /* Read first nonwhite char on the line. Do this before incrementing the
+ line number, in case we're at the end of saved text. */
+
+ do
+ c = getch ();
+ while (c == ' ' || c == '\t');
+
+ lineno++;
+
+ if (c != '#')
+ {
+ /* If not #, return it so caller will use it. */
+ return c;
+ }
+
+ /* Read first nonwhite char after the `#'. */
+
+ do
+ c = getch ();
+ while (c == ' ' || c == '\t');
+
+ /* If a letter follows, then if the word here is `line', skip
+ it and ignore it; otherwise, ignore the line, with an error
+ if the word isn't `pragma'. */
+
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
+ {
+ if (c == 'p')
+ {
+ if (getch () == 'r'
+ && getch () == 'a'
+ && getch () == 'g'
+ && getch () == 'm'
+ && getch () == 'a')
+ {
+ /* Read first nonwhite char after the `#pragma'. */
+
+ do
+ c = getch ();
+ while (c == ' ' || c == '\t');
+
+ if (c == 'v'
+ && getch () == 't'
+ && getch () == 'a'
+ && getch () == 'b'
+ && getch () == 'l'
+ && getch () == 'e'
+ && ((c = getch ()) == ' ' || c == '\t' || c == '\n'))
+ {
+ extern tree pending_vtables;
+
+ /* More follows: it must be a string constant (class name). */
+ token = real_yylex ();
+ if (token != STRING || TREE_CODE (yylval.ttype) != STRING_CST)
+ {
+ error ("invalid #pragma vtable");
+ goto skipline;
+ }
+ if (write_virtuals != 2)
+ {
+ warning ("use `+e2' option to enable #pragma vtable");
+ goto skipline;
+ }
+ pending_vtables = perm_tree_cons (NULL_TREE, get_identifier (TREE_STRING_POINTER (yylval.ttype)), pending_vtables);
+ if (nextchar < 0)
+ nextchar = getch ();
+ c = nextchar;
+ if (c != '\n')
+ warning ("trailing characters ignored");
+ }
+ else if (c == 'u'
+ && getch () == 'n'
+ && getch () == 'i'
+ && getch () == 't'
+ && ((c = getch ()) == ' ' || c == '\t' || c == '\n'))
+ {
+ /* More follows: it must be a string constant (unit name). */
+ token = real_yylex ();
+ if (token != STRING || TREE_CODE (yylval.ttype) != STRING_CST)
+ {
+ error ("invalid #pragma unit");
+ goto skipline;
+ }
+ current_unit_name = get_identifier (TREE_STRING_POINTER (yylval.ttype));
+ current_unit_language = current_lang_name;
+ if (nextchar < 0)
+ nextchar = getch ();
+ c = nextchar;
+ if (c != '\n')
+ warning ("trailing characters ignored");
+ }
+ else if (c == 'i')
+ {
+ tree fileinfo = IDENTIFIER_CLASS_VALUE (get_time_identifier (input_filename));
+ c = getch ();
+
+ if (c == 'n'
+ && getch () == 't'
+ && getch () == 'e'
+ && getch () == 'r'
+ && getch () == 'f'
+ && getch () == 'a'
+ && getch () == 'c'
+ && getch () == 'e'
+ && ((c = getch ()) == ' ' || c == '\t' || c == '\n'))
+ {
+ int warned_already = 0;
+ char *main_filename = input_filename;
+
+ main_filename = FILE_NAME_NONDIRECTORY (main_filename);
+ while (c == ' ' || c == '\t')
+ c = getch ();
+ if (c != '\n')
+ {
+ put_back (c);
+ token = real_yylex ();
+ if (token != STRING
+ || TREE_CODE (yylval.ttype) != STRING_CST)
+ {
+ error ("invalid `#pragma interface'");
+ goto skipline;
+ }
+ main_filename = TREE_STRING_POINTER (yylval.ttype);
+ c = getch();
+ put_back (c);
+ }
+
+ while (c == ' ' || c == '\t')
+ c = getch ();
+
+ while (c != '\n')
+ {
+ if (!warned_already && extra_warnings
+ && c != ' ' && c != '\t' && c != '\n')
+ {
+ warning ("garbage after `#pragma interface' ignored");
+ warned_already = 1;
+ }
+ c = getch ();
+ }
+
+ write_virtuals = 3;
+
+ if (impl_file_chain == 0)
+ {
+ /* If this is zero at this point, then we are
+ auto-implementing. */
+ if (main_input_filename == 0)
+ main_input_filename = input_filename;
+
+#ifdef AUTO_IMPLEMENT
+ filename = FILE_NAME_NONDIRECTORY (main_input_filename);
+ fi = get_time_identifier (filename);
+ fi = IDENTIFIER_CLASS_VALUE (fi);
+ TREE_INT_CST_LOW (fi) = 0;
+ TREE_INT_CST_HIGH (fi) = 1;
+ /* Get default. */
+ impl_file_chain = (struct impl_files *)permalloc (sizeof (struct impl_files));
+ impl_file_chain->filename = filename;
+ impl_file_chain->next = 0;
+#endif
+ }
+
+ interface_only = interface_strcmp (main_filename);
+ interface_unknown = 0;
+ TREE_INT_CST_LOW (fileinfo) = interface_only;
+ TREE_INT_CST_HIGH (fileinfo) = interface_unknown;
+ }
+ else if (c == 'm'
+ && getch () == 'p'
+ && getch () == 'l'
+ && getch () == 'e'
+ && getch () == 'm'
+ && getch () == 'e'
+ && getch () == 'n'
+ && getch () == 't'
+ && getch () == 'a'
+ && getch () == 't'
+ && getch () == 'i'
+ && getch () == 'o'
+ && getch () == 'n'
+ && ((c = getch ()) == ' ' || c == '\t' || c == '\n'))
+ {
+ int warned_already = 0;
+ char *main_filename = main_input_filename ? main_input_filename : input_filename;
+
+ main_filename = FILE_NAME_NONDIRECTORY (main_filename);
+ while (c == ' ' || c == '\t')
+ c = getch ();
+ if (c != '\n')
+ {
+ put_back (c);
+ token = real_yylex ();
+ if (token != STRING
+ || TREE_CODE (yylval.ttype) != STRING_CST)
+ {
+ error ("invalid `#pragma implementation'");
+ goto skipline;
+ }
+ main_filename = TREE_STRING_POINTER (yylval.ttype);
+ c = getch();
+ put_back (c);
+ }
+
+ while (c == ' ' || c == '\t')
+ c = getch ();
+
+ while (c != '\n')
+ {
+ if (!warned_already && extra_warnings
+ && c != ' ' && c != '\t' && c != '\n')
+ {
+ warning ("garbage after `#pragma implementation' ignored");
+ warned_already = 1;
+ }
+ c = getch ();
+ }
+
+ if (write_virtuals == 3)
+ {
+ struct impl_files *ifiles = impl_file_chain;
+ while (ifiles)
+ {
+ if (! strcmp (ifiles->filename, main_filename))
+ break;
+ ifiles = ifiles->next;
+ }
+ if (ifiles == 0)
+ {
+ ifiles = (struct impl_files*) permalloc (sizeof (struct impl_files));
+ ifiles->filename = main_filename;
+ ifiles->next = impl_file_chain;
+ impl_file_chain = ifiles;
+ }
+ }
+ else if ((main_input_filename != 0
+ && ! strcmp (main_input_filename, input_filename))
+ || ! strcmp (input_filename, main_filename))
+ {
+ write_virtuals = 3;
+ if (impl_file_chain == 0)
+ {
+ impl_file_chain = (struct impl_files*) permalloc (sizeof (struct impl_files));
+ impl_file_chain->filename = main_filename;
+ impl_file_chain->next = 0;
+ }
+ }
+ else
+ error ("`#pragma implementation' can only appear at top-level");
+ interface_only = 0;
+#if 1
+ /* We make this non-zero so that we infer decl linkage
+ in the impl file only for variables first declared
+ in the interface file. */
+ interface_unknown = 1;
+#else
+ /* We make this zero so that templates in the impl
+ file will be emitted properly. */
+ interface_unknown = 0;
+#endif
+ TREE_INT_CST_LOW (fileinfo) = interface_only;
+ TREE_INT_CST_HIGH (fileinfo) = interface_unknown;
+ }
+ }
+ }
+ goto skipline;
+ }
+ else if (c == 'd')
+ {
+ if (getch () == 'e'
+ && getch () == 'f'
+ && getch () == 'i'
+ && getch () == 'n'
+ && getch () == 'e'
+ && ((c = getch ()) == ' ' || c == '\t' || c == '\n'))
+ {
+#ifdef DWARF_DEBUGGING_INFO
+ if ((debug_info_level == DINFO_LEVEL_VERBOSE)
+ && (write_symbols == DWARF_DEBUG))
+ dwarfout_define (lineno, get_directive_line (finput));
+#endif /* DWARF_DEBUGGING_INFO */
+ goto skipline;
+ }
+ }
+ else if (c == 'u')
+ {
+ if (getch () == 'n'
+ && getch () == 'd'
+ && getch () == 'e'
+ && getch () == 'f'
+ && ((c = getch ()) == ' ' || c == '\t' || c == '\n'))
+ {
+#ifdef DWARF_DEBUGGING_INFO
+ if ((debug_info_level == DINFO_LEVEL_VERBOSE)
+ && (write_symbols == DWARF_DEBUG))
+ dwarfout_undef (lineno, get_directive_line (finput));
+#endif /* DWARF_DEBUGGING_INFO */
+ goto skipline;
+ }
+ }
+ else if (c == 'l')
+ {
+ if (getch () == 'i'
+ && getch () == 'n'
+ && getch () == 'e'
+ && ((c = getch ()) == ' ' || c == '\t'))
+ goto linenum;
+ }
+ else if (c == 'i')
+ {
+ if (getch () == 'd'
+ && getch () == 'e'
+ && getch () == 'n'
+ && getch () == 't'
+ && ((c = getch ()) == ' ' || c == '\t'))
+ {
+#ifdef ASM_OUTPUT_IDENT
+ extern FILE *asm_out_file;
+#endif
+ /* #ident. The pedantic warning is now in cccp.c. */
+
+ /* Here we have just seen `#ident '.
+ A string constant should follow. */
+
+ while (c == ' ' || c == '\t')
+ c = getch ();
+
+ /* If no argument, ignore the line. */
+ if (c == '\n')
+ return c;
+
+ put_back (c);
+ token = real_yylex ();
+ if (token != STRING
+ || TREE_CODE (yylval.ttype) != STRING_CST)
+ {
+ error ("invalid #ident");
+ goto skipline;
+ }
+
+ if (! flag_no_ident)
+ {
+#ifdef ASM_OUTPUT_IDENT
+ ASM_OUTPUT_IDENT (asm_out_file,
+ TREE_STRING_POINTER (yylval.ttype));
+#endif
+ }
+
+ /* Skip the rest of this line. */
+ goto skipline;
+ }
+ }
+ else if (c == 'n')
+ {
+ if (getch () == 'e'
+ && getch () == 'w'
+ && getch () == 'w'
+ && getch () == 'o'
+ && getch () == 'r'
+ && getch () == 'l'
+ && getch () == 'd'
+ && ((c = getch ()) == ' ' || c == '\t'))
+ {
+ /* Used to test incremental compilation. */
+ sorry ("#pragma newworld");
+ goto skipline;
+ }
+ }
+ error ("undefined or invalid # directive");
+ goto skipline;
+ }
+
+linenum:
+ /* Here we have either `#line' or `# <nonletter>'.
+ In either case, it should be a line number; a digit should follow. */
+
+ while (c == ' ' || c == '\t')
+ c = getch ();
+
+ /* If the # is the only nonwhite char on the line,
+ just ignore it. Check the new newline. */
+ if (c == '\n')
+ return c;
+
+ /* Something follows the #; read a token. */
+
+ put_back (c);
+ token = real_yylex ();
+
+ if (token == CONSTANT
+ && TREE_CODE (yylval.ttype) == INTEGER_CST)
+ {
+ int old_lineno = lineno;
+ enum { act_none, act_push, act_pop } action = act_none;
+ int entering_system_header = 0;
+ int entering_c_header = 0;
+
+ /* subtract one, because it is the following line that
+ gets the specified number */
+
+ int l = TREE_INT_CST_LOW (yylval.ttype) - 1;
+ c = get_last_nonwhite_on_line ();
+ if (c == '\n')
+ {
+ /* No more: store the line number and check following line. */
+ lineno = l;
+ return c;
+ }
+ put_back (c);
+
+ /* More follows: it must be a string constant (filename). */
+
+ /* Read the string constant, but don't treat \ as special. */
+ ignore_escape_flag = 1;
+ token = real_yylex ();
+ ignore_escape_flag = 0;
+
+ if (token != STRING || TREE_CODE (yylval.ttype) != STRING_CST)
+ {
+ error ("invalid #line");
+ goto skipline;
+ }
+
+ /* Changing files again. This means currently collected time
+ is charged against header time, and body time starts back
+ at 0. */
+ if (flag_detailed_statistics)
+ {
+ int this_time = my_get_run_time ();
+ tree time_identifier = get_time_identifier (TREE_STRING_POINTER (yylval.ttype));
+ header_time += this_time - body_time;
+ TREE_INT_CST_LOW (IDENTIFIER_LOCAL_VALUE (this_filename_time))
+ += this_time - body_time;
+ this_filename_time = time_identifier;
+ body_time = this_time;
+ }
+
+ if (flag_cadillac)
+ cadillac_note_source ();
+
+ input_filename
+ = (char *) permalloc (TREE_STRING_LENGTH (yylval.ttype) + 1);
+ strcpy (input_filename, TREE_STRING_POINTER (yylval.ttype));
+ lineno = l;
+ GNU_xref_file (input_filename);
+
+ if (main_input_filename == 0)
+ {
+ struct impl_files *ifiles = impl_file_chain;
+
+ if (ifiles)
+ {
+ while (ifiles->next)
+ ifiles = ifiles->next;
+ ifiles->filename = FILE_NAME_NONDIRECTORY (input_filename);
+ }
+
+ main_input_filename = input_filename;
+ if (write_virtuals == 3)
+ walk_vtables (set_typedecl_interface_info, set_vardecl_interface_info);
+ }
+
+ extract_interface_info ();
+
+ c = get_last_nonwhite_on_line ();
+ if (c != '\n')
+ {
+ put_back (c);
+
+ token = real_yylex ();
+
+ /* `1' after file name means entering new file.
+ `2' after file name means just left a file. */
+
+ if (token == CONSTANT
+ && TREE_CODE (yylval.ttype) == INTEGER_CST)
+ {
+ if (TREE_INT_CST_LOW (yylval.ttype) == 1)
+ action = act_push;
+ else if (TREE_INT_CST_LOW (yylval.ttype) == 2)
+ action = act_pop;
+
+ if (action)
+ {
+ c = get_last_nonwhite_on_line ();
+ if (c != '\n')
+ {
+ put_back (c);
+ token = real_yylex ();
+ }
+ }
+ }
+
+ /* `3' after file name means this is a system header file. */
+
+ if (token == CONSTANT
+ && TREE_CODE (yylval.ttype) == INTEGER_CST
+ && TREE_INT_CST_LOW (yylval.ttype) == 3)
+ {
+ entering_system_header = 1;
+
+ c = get_last_nonwhite_on_line ();
+ if (c != '\n')
+ {
+ put_back (c);
+ token = real_yylex ();
+ }
+ }
+
+ /* `4' after file name means this is a C header file. */
+
+ if (token == CONSTANT
+ && TREE_CODE (yylval.ttype) == INTEGER_CST
+ && TREE_INT_CST_LOW (yylval.ttype) == 4)
+ {
+ entering_c_header = 1;
+
+ c = get_last_nonwhite_on_line ();
+ if (c != '\n')
+ {
+ put_back (c);
+ token = real_yylex ();
+ }
+ }
+
+ /* Do the actions implied by the preceeding numbers. */
+
+ if (action == act_push)
+ {
+ /* Pushing to a new file. */
+ struct file_stack *p;
+
+ p = (struct file_stack *) xmalloc (sizeof (struct file_stack));
+ input_file_stack->line = old_lineno;
+ p->next = input_file_stack;
+ p->name = input_filename;
+ input_file_stack = p;
+ input_file_stack_tick++;
+#ifdef DWARF_DEBUGGING_INFO
+ if (debug_info_level == DINFO_LEVEL_VERBOSE
+ && write_symbols == DWARF_DEBUG)
+ dwarfout_start_new_source_file (input_filename);
+#endif /* DWARF_DEBUGGING_INFO */
+ if (flag_cadillac)
+ cadillac_push_source ();
+ in_system_header = entering_system_header;
+ if (c_header_level)
+ ++c_header_level;
+ else if (entering_c_header)
+ {
+ c_header_level = 1;
+ ++pending_lang_change;
+ }
+ }
+ else if (action == act_pop)
+ {
+ /* Popping out of a file. */
+ if (input_file_stack->next)
+ {
+ struct file_stack *p;
+
+ if (c_header_level && --c_header_level == 0)
+ {
+ if (entering_c_header)
+ warning ("Badly nested C headers from preprocessor");
+ --pending_lang_change;
+ }
+ if (flag_cadillac)
+ cadillac_pop_source ();
+ in_system_header = entering_system_header;
+
+ p = input_file_stack;
+ input_file_stack = p->next;
+ free (p);
+ input_file_stack_tick++;
+#ifdef DWARF_DEBUGGING_INFO
+ if (debug_info_level == DINFO_LEVEL_VERBOSE
+ && write_symbols == DWARF_DEBUG)
+ dwarfout_resume_previous_source_file (input_file_stack->line);
+#endif /* DWARF_DEBUGGING_INFO */
+ }
+ else
+ error ("#-lines for entering and leaving files don't match");
+ }
+ else
+ {
+ in_system_header = entering_system_header;
+ if (flag_cadillac)
+ cadillac_switch_source (-1);
+ }
+ }
+
+ /* If NEXTCHAR is not end of line, we don't care what it is. */
+ if (nextchar == '\n')
+ return '\n';
+ }
+ else
+ error ("invalid #-line");
+
+ /* skip the rest of this line. */
+ skipline:
+ if (c == '\n')
+ return c;
+ while ((c = getch ()) != EOF && c != '\n');
+ return c;
+}
+
+void
+do_pending_lang_change ()
+{
+ for (; pending_lang_change > 0; --pending_lang_change)
+ push_lang_context (lang_name_c);
+ for (; pending_lang_change < 0; ++pending_lang_change)
+ pop_lang_context ();
+}
+
+#if 0
+#define isalnum(char) (char >= 'a' ? char <= 'z' : char >= '0' ? char <= '9' || (char >= 'A' && char <= 'Z') : 0)
+#define isdigit(char) (char >= '0' && char <= '9')
+#else
+#include <ctype.h>
+#endif
+
+#define ENDFILE -1 /* token that represents end-of-file */
+
+/* Read an escape sequence, returning its equivalent as a character,
+ or store 1 in *ignore_ptr if it is backslash-newline. */
+
+static int
+readescape (ignore_ptr)
+ int *ignore_ptr;
+{
+ register int c = getch ();
+ register int code;
+ register unsigned count;
+ unsigned firstdig;
+ int nonnull;
+
+ switch (c)
+ {
+ case 'x':
+ if (warn_traditional)
+ warning ("the meaning of `\\x' varies with -traditional");
+
+ if (flag_traditional)
+ return c;
+
+ code = 0;
+ count = 0;
+ nonnull = 0;
+ while (1)
+ {
+ c = getch ();
+ if (! isxdigit (c))
+ {
+ put_back (c);
+ break;
+ }
+ code *= 16;
+ if (c >= 'a' && c <= 'f')
+ code += c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ code += c - 'A' + 10;
+ if (c >= '0' && c <= '9')
+ code += c - '0';
+ if (code != 0 || count != 0)
+ {
+ if (count == 0)
+ firstdig = code;
+ count++;
+ }
+ nonnull = 1;
+ }
+ if (! nonnull)
+ error ("\\x used with no following hex digits");
+ else if (count == 0)
+ /* Digits are all 0's. Ok. */
+ ;
+ else if ((count - 1) * 4 >= TYPE_PRECISION (integer_type_node)
+ || (count > 1
+ && ((1 << (TYPE_PRECISION (integer_type_node) - (count - 1) * 4))
+ <= firstdig)))
+ pedwarn ("hex escape out of range");
+ return code;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7':
+ code = 0;
+ count = 0;
+ while ((c <= '7') && (c >= '0') && (count++ < 3))
+ {
+ code = (code * 8) + (c - '0');
+ c = getch ();
+ }
+ put_back (c);
+ return code;
+
+ case '\\': case '\'': case '"':
+ return c;
+
+ case '\n':
+ lineno++;
+ *ignore_ptr = 1;
+ return 0;
+
+ case 'n':
+ return TARGET_NEWLINE;
+
+ case 't':
+ return TARGET_TAB;
+
+ case 'r':
+ return TARGET_CR;
+
+ case 'f':
+ return TARGET_FF;
+
+ case 'b':
+ return TARGET_BS;
+
+ case 'a':
+ if (warn_traditional)
+ warning ("the meaning of `\\a' varies with -traditional");
+
+ if (flag_traditional)
+ return c;
+ return TARGET_BELL;
+
+ case 'v':
+ return TARGET_VT;
+
+ case 'e':
+ case 'E':
+ if (pedantic)
+ pedwarn ("non-ANSI-standard escape sequence, `\\%c'", c);
+ return 033;
+
+ case '?':
+ return c;
+
+ /* `\(', etc, are used at beginning of line to avoid confusing Emacs. */
+ case '(':
+ case '{':
+ case '[':
+ /* `\%' is used to prevent SCCS from getting confused. */
+ case '%':
+ if (pedantic)
+ pedwarn ("unknown escape sequence `\\%c'", c);
+ return c;
+ }
+ if (c >= 040 && c < 0177)
+ pedwarn ("unknown escape sequence `\\%c'", c);
+ else
+ pedwarn ("unknown escape sequence: `\\' followed by char code 0x%x", c);
+ return c;
+}
+
+/* Value is 1 (or 2) if we should try to make the next identifier look like
+ a typename (when it may be a local variable or a class variable).
+ Value is 0 if we treat this name in a default fashion. */
+int looking_for_typename = 0;
+
+#if 0
+/* NO LONGER USED: Value is -1 if we must not see a type name. */
+void
+dont_see_typename ()
+{
+ looking_for_typename = -1;
+ if (yychar == TYPENAME || yychar == PTYPENAME)
+ {
+ yychar = IDENTIFIER;
+ lastiddecl = 0;
+ }
+}
+#endif
+
+#ifdef __GNUC__
+extern __inline int identifier_type ();
+__inline
+#endif
+int
+identifier_type (decl)
+ tree decl;
+{
+ if (TREE_CODE (decl) == TEMPLATE_DECL
+ && DECL_TEMPLATE_IS_CLASS (decl))
+ return PTYPENAME;
+ if (TREE_CODE (decl) != TYPE_DECL)
+ return IDENTIFIER;
+ return TYPENAME;
+}
+
+void
+see_typename ()
+{
+ looking_for_typename = 0;
+ if (yychar == IDENTIFIER)
+ {
+ lastiddecl = lookup_name (yylval.ttype, -2);
+ if (lastiddecl == 0)
+ {
+ if (flag_labels_ok)
+ lastiddecl = IDENTIFIER_LABEL_VALUE (yylval.ttype);
+ }
+ else
+ yychar = identifier_type (lastiddecl);
+ }
+}
+
+tree
+do_identifier (token)
+ register tree token;
+{
+ register tree id = lastiddecl;
+
+ if (yychar == YYEMPTY)
+ yychar = yylex ();
+ /* Scope class declarations before global
+ declarations. */
+ if (id == IDENTIFIER_GLOBAL_VALUE (token)
+ && current_class_type != 0
+ && TYPE_SIZE (current_class_type) == 0
+ && TREE_CODE (current_class_type) != UNINSTANTIATED_P_TYPE)
+ {
+ /* Could be from one of the base classes. */
+ tree field = lookup_field (current_class_type, token, 1, 0);
+ if (field == 0)
+ ;
+ else if (field == error_mark_node)
+ /* We have already generated the error message.
+ But we still want to return this value. */
+ id = lookup_field (current_class_type, token, 0, 0);
+ else if (TREE_CODE (field) == VAR_DECL
+ || TREE_CODE (field) == CONST_DECL)
+ id = field;
+ else if (TREE_CODE (field) != FIELD_DECL)
+ my_friendly_abort (61);
+ else
+ {
+ cp_error ("invalid use of member `%D' from base class `%T'", field,
+ DECL_FIELD_CONTEXT (field));
+ id = error_mark_node;
+ return id;
+ }
+ }
+
+ /* Remember that this name has been used in the class definition, as per
+ [class.scope0] */
+ if (id && current_class_type
+ && TYPE_BEING_DEFINED (current_class_type)
+ && ! IDENTIFIER_CLASS_VALUE (token))
+ pushdecl_class_level (id);
+
+ if (!id || id == error_mark_node)
+ {
+ if (id == error_mark_node && current_class_type != NULL_TREE)
+ {
+ id = lookup_nested_field (token, 1);
+ /* In lookup_nested_field(), we marked this so we can gracefully
+ leave this whole mess. */
+ if (id && id != error_mark_node && TREE_TYPE (id) == error_mark_node)
+ return id;
+ }
+ if (yychar == '(' || yychar == LEFT_RIGHT)
+ {
+ id = implicitly_declare (token);
+ }
+ else if (current_function_decl == 0)
+ {
+ cp_error ("`%D' was not declared in this scope", token);
+ id = error_mark_node;
+ }
+ else
+ {
+ if (IDENTIFIER_GLOBAL_VALUE (token) != error_mark_node
+ || IDENTIFIER_ERROR_LOCUS (token) != current_function_decl)
+ {
+ static int undeclared_variable_notice;
+
+ cp_error ("`%D' undeclared (first use this function)", token);
+
+ if (! undeclared_variable_notice)
+ {
+ error ("(Each undeclared identifier is reported only once");
+ error ("for each function it appears in.)");
+ undeclared_variable_notice = 1;
+ }
+ }
+ id = error_mark_node;
+ /* Prevent repeated error messages. */
+ IDENTIFIER_GLOBAL_VALUE (token) = error_mark_node;
+ SET_IDENTIFIER_ERROR_LOCUS (token, current_function_decl);
+ }
+ }
+ /* TREE_USED is set in `hack_identifier'. */
+ if (TREE_CODE (id) == CONST_DECL)
+ {
+ if (IDENTIFIER_CLASS_VALUE (token) == id)
+ {
+ /* Check access. */
+ enum access_type access
+ = compute_access (TYPE_BINFO (current_class_type), id);
+ if (access == access_private)
+ cp_error ("enum `%D' is private", id);
+ /* protected is OK, since it's an enum of `this'. */
+ }
+ id = DECL_INITIAL (id);
+ }
+ else
+ id = hack_identifier (id, token, yychar);
+ return id;
+}
+
+tree
+identifier_typedecl_value (node)
+ tree node;
+{
+ tree t, type;
+ type = IDENTIFIER_TYPE_VALUE (node);
+ if (type == NULL_TREE)
+ return NULL_TREE;
+#define do(X) \
+ { \
+ t = (X); \
+ if (t && TREE_CODE (t) == TYPE_DECL && TREE_TYPE (t) == type) \
+ return t; \
+ }
+ do (IDENTIFIER_LOCAL_VALUE (node));
+ do (IDENTIFIER_CLASS_VALUE (node));
+ do (IDENTIFIER_GLOBAL_VALUE (node));
+#undef do
+ /* Will this one ever happen? */
+ if (TYPE_NAME (type))
+ return TYPE_NAME (type);
+
+ /* We used to do an internal error of 62 here, but instead we will
+ handle the return of a null appropriately in the callers. */
+ return NULL_TREE;
+}
+
+struct try_type
+{
+ tree *node_var;
+ char unsigned_flag;
+ char long_flag;
+ char long_long_flag;
+};
+
+struct try_type type_sequence[] =
+{
+ { &integer_type_node, 0, 0, 0},
+ { &unsigned_type_node, 1, 0, 0},
+ { &long_integer_type_node, 0, 1, 0},
+ { &long_unsigned_type_node, 1, 1, 0},
+ { &long_long_integer_type_node, 0, 1, 1},
+ { &long_long_unsigned_type_node, 1, 1, 1}
+};
+
+int
+real_yylex ()
+{
+ register int c;
+ register int value;
+ int wide_flag = 0;
+ int dollar_seen = 0;
+ int i;
+
+ if (nextchar >= 0)
+ c = nextchar, nextchar = -1;
+ else
+ c = getch ();
+
+ /* Effectively do c = skip_white_space (c)
+ but do it faster in the usual cases. */
+ while (1)
+ switch (c)
+ {
+ case ' ':
+ case '\t':
+ case '\f':
+ case '\v':
+ case '\b':
+ c = getch ();
+ break;
+
+ case '\r':
+ /* Call skip_white_space so we can warn if appropriate. */
+
+ case '\n':
+ case '/':
+ case '\\':
+ c = skip_white_space (c);
+ default:
+ goto found_nonwhite;
+ }
+ found_nonwhite:
+
+ token_buffer[0] = c;
+ token_buffer[1] = 0;
+
+/* yylloc.first_line = lineno; */
+
+ switch (c)
+ {
+ case EOF:
+ token_buffer[0] = '\0';
+ end_of_file = 1;
+ if (input_redirected ())
+ value = END_OF_SAVED_INPUT;
+ else if (do_pending_expansions ())
+ /* this will set yychar for us */
+ return yychar;
+ else
+ value = ENDFILE;
+ break;
+
+ case '$':
+ if (dollars_in_ident)
+ {
+ dollar_seen = 1;
+ goto letter;
+ }
+ value = '$';
+ goto done;
+
+ case 'L':
+ /* Capital L may start a wide-string or wide-character constant. */
+ {
+ register int c = getch ();
+ if (c == '\'')
+ {
+ wide_flag = 1;
+ goto char_constant;
+ }
+ if (c == '"')
+ {
+ wide_flag = 1;
+ goto string_constant;
+ }
+ put_back (c);
+ }
+
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F': case 'G': case 'H': case 'I': case 'J':
+ case 'K': case 'M': case 'N': case 'O':
+ case 'P': case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X': case 'Y':
+ case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f': case 'g': case 'h': case 'i': case 'j':
+ case 'k': case 'l': case 'm': case 'n': case 'o':
+ case 'p': case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x': case 'y':
+ case 'z':
+ case '_':
+ letter:
+ {
+ register char *p;
+
+ p = token_buffer;
+ if (input == 0)
+ {
+ /* We know that `token_buffer' can hold at least on char,
+ so we install C immediately.
+ We may have to read the value in `putback_char', so call
+ `getch' once. */
+ *p++ = c;
+ c = getch ();
+
+ /* Make this run fast. We know that we are reading straight
+ from FINPUT in this case (since identifiers cannot straddle
+ input sources. */
+ while (isalnum (c) || (c == '_') || c == '$')
+ {
+ if (c == '$' && ! dollars_in_ident)
+ break;
+ if (p >= token_buffer + maxtoken)
+ p = extend_token_buffer (p);
+
+ *p++ = c;
+ c = getc (finput);
+ }
+ }
+ else
+ {
+ /* We know that `token_buffer' can hold at least on char,
+ so we install C immediately. */
+ *p++ = c;
+ c = getch ();
+
+ while (isalnum (c) || (c == '_') || c == '$')
+ {
+ if (c == '$' && ! dollars_in_ident)
+ break;
+ if (p >= token_buffer + maxtoken)
+ p = extend_token_buffer (p);
+
+ *p++ = c;
+ c = getch ();
+ }
+ }
+
+ *p = 0;
+ nextchar = c;
+
+ value = IDENTIFIER;
+ yylval.itype = 0;
+
+ /* Try to recognize a keyword. Uses minimum-perfect hash function */
+
+ {
+ register struct resword *ptr;
+
+ if (ptr = is_reserved_word (token_buffer, p - token_buffer))
+ {
+ if (ptr->rid)
+ {
+ tree old_ttype = ridpointers[(int) ptr->rid];
+
+ /* If this provides a type for us, then revert lexical
+ state to standard state. */
+ if (TREE_CODE (old_ttype) == IDENTIFIER_NODE
+ && IDENTIFIER_GLOBAL_VALUE (old_ttype) != 0
+ && TREE_CODE (IDENTIFIER_GLOBAL_VALUE (old_ttype)) == TYPE_DECL)
+ looking_for_typename = 0;
+ else if (ptr->token == AGGR || ptr->token == ENUM)
+ looking_for_typename = 1;
+
+ /* Check if this is a language-type declaration.
+ Just glimpse the next non-white character. */
+ nextchar = skip_white_space (nextchar);
+ if (nextchar == '"')
+ {
+ /* We are looking at a string. Complain
+ if the token before the string is no `extern'.
+
+ Could cheat some memory by placing this string
+ on the temporary_, instead of the saveable_
+ obstack. */
+
+ if (ptr->rid != RID_EXTERN)
+ error ("invalid modifier `%s' for language string",
+ ptr->name);
+ real_yylex ();
+ value = EXTERN_LANG_STRING;
+ yylval.ttype = get_identifier (TREE_STRING_POINTER (yylval.ttype));
+ break;
+ }
+ if (ptr->token == VISSPEC)
+ {
+ switch (ptr->rid)
+ {
+ case RID_PUBLIC:
+ yylval.itype = access_public;
+ break;
+ case RID_PRIVATE:
+ yylval.itype = access_private;
+ break;
+ case RID_PROTECTED:
+ yylval.itype = access_protected;
+ break;
+ default:
+ my_friendly_abort (63);
+ }
+ }
+ else
+ yylval.ttype = old_ttype;
+ }
+ value = (int) ptr->token;
+ }
+ }
+
+ /* If we did not find a keyword, look for an identifier
+ (or a typename). */
+
+ if (strcmp ("catch", token_buffer) == 0
+ || strcmp ("throw", token_buffer) == 0
+ || strcmp ("try", token_buffer) == 0)
+ {
+ static int did_warn = 0;
+ if (! did_warn && ! flag_handle_exceptions)
+ {
+ pedwarn ("`catch', `throw', and `try' are all C++ reserved words");
+ did_warn = 1;
+ }
+ }
+
+ if (value == IDENTIFIER || value == TYPESPEC)
+ GNU_xref_ref (current_function_decl, token_buffer);
+
+ if (value == IDENTIFIER)
+ {
+ register tree tmp = get_identifier (token_buffer);
+
+#if !defined(VMS) && defined(JOINER)
+ /* Make sure that user does not collide with our internal
+ naming scheme. */
+ if (JOINER == '$'
+ && dollar_seen
+ && (THIS_NAME_P (tmp)
+ || VPTR_NAME_P (tmp)
+ || DESTRUCTOR_NAME_P (tmp)
+ || VTABLE_NAME_P (tmp)
+ || TEMP_NAME_P (tmp)
+ || ANON_AGGRNAME_P (tmp)
+ || ANON_PARMNAME_P (tmp)))
+ warning ("identifier name `%s' conflicts with GNU C++ internal naming strategy",
+ token_buffer);
+#endif
+
+ yylval.ttype = tmp;
+
+ /* A user-invisible read-only initialized variable
+ should be replaced by its value. We only handle strings
+ since that's the only case used in C (and C++). */
+ /* Note we go right after the local value for the identifier
+ (e.g., __FUNCTION__ or __PRETTY_FUNCTION__). We used to
+ call lookup_name, but that could result in an error about
+ ambiguities. */
+ tmp = IDENTIFIER_LOCAL_VALUE (yylval.ttype);
+ if (tmp != NULL_TREE
+ && TREE_CODE (tmp) == VAR_DECL
+ && DECL_IGNORED_P (tmp)
+ && TREE_READONLY (tmp)
+ && DECL_INITIAL (tmp) != NULL_TREE
+ && TREE_CODE (DECL_INITIAL (tmp)) == STRING_CST)
+ {
+ yylval.ttype = DECL_INITIAL (tmp);
+ value = STRING;
+ }
+ }
+ if (value == NEW && ! global_bindings_p ())
+ {
+ value = NEW;
+ goto done;
+ }
+ }
+ break;
+
+ case '.':
+ {
+ register int c1 = getch ();
+ token_buffer[0] = c;
+ token_buffer[1] = c1;
+ if (c1 == '*')
+ {
+ value = DOT_STAR;
+ token_buffer[2] = 0;
+ goto done;
+ }
+ if (c1 == '.')
+ {
+ c1 = getch ();
+ if (c1 == '.')
+ {
+ token_buffer[2] = c1;
+ token_buffer[3] = 0;
+ value = ELLIPSIS;
+ goto done;
+ }
+ error ("parse error at `..'");
+ }
+ if (isdigit (c1))
+ {
+ put_back (c1);
+ goto resume_numerical_scan;
+ }
+ nextchar = c1;
+ value = '.';
+ token_buffer[1] = 0;
+ goto done;
+ }
+ case '0': case '1':
+ /* Optimize for most frequent case. */
+ {
+ register int c1 = getch ();
+ if (! isalnum (c1) && c1 != '.')
+ {
+ /* Terminate string. */
+ token_buffer[0] = c;
+ token_buffer[1] = 0;
+ if (c == '0')
+ yylval.ttype = integer_zero_node;
+ else
+ yylval.ttype = integer_one_node;
+ nextchar = c1;
+ value = CONSTANT;
+ goto done;
+ }
+ put_back (c1);
+ }
+ /* fall through... */
+ case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ resume_numerical_scan:
+ {
+ register char *p;
+ int base = 10;
+ int count = 0;
+ int largest_digit = 0;
+ int numdigits = 0;
+ /* for multi-precision arithmetic,
+ we actually store only HOST_BITS_PER_CHAR bits in each part.
+ The number of parts is chosen so as to be sufficient to hold
+ the enough bits to fit into the two HOST_WIDE_INTs that contain
+ the integer value (this is always at least as many bits as are
+ in a target `long long' value, but may be wider). */
+#define TOTAL_PARTS ((HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR) * 2 + 2)
+ int parts[TOTAL_PARTS];
+ int overflow = 0;
+
+ enum anon1 { NOT_FLOAT, AFTER_POINT, TOO_MANY_POINTS} floatflag
+ = NOT_FLOAT;
+
+ p = token_buffer;
+ *p++ = c;
+
+ for (count = 0; count < TOTAL_PARTS; count++)
+ parts[count] = 0;
+
+ if (c == '0')
+ {
+ *p++ = (c = getch ());
+ if ((c == 'x') || (c == 'X'))
+ {
+ base = 16;
+ *p++ = (c = getch ());
+ }
+ /* Leading 0 forces octal unless the 0 is the only digit. */
+ else if (c >= '0' && c <= '9')
+ {
+ base = 8;
+ numdigits++;
+ }
+ else
+ numdigits++;
+ }
+
+ /* Read all the digits-and-decimal-points. */
+
+ while (c == '.'
+ || (isalnum (c) && (c != 'l') && (c != 'L')
+ && (c != 'u') && (c != 'U')
+ && (floatflag == NOT_FLOAT || ((c != 'f') && (c != 'F')))))
+ {
+ if (c == '.')
+ {
+ if (base == 16)
+ error ("floating constant may not be in radix 16");
+ if (floatflag == AFTER_POINT)
+ {
+ error ("malformed floating constant");
+ floatflag = TOO_MANY_POINTS;
+ }
+ else
+ floatflag = AFTER_POINT;
+
+ base = 10;
+ *p++ = c = getch ();
+ /* Accept '.' as the start of a floating-point number
+ only when it is followed by a digit.
+ Otherwise, unread the following non-digit
+ and use the '.' as a structural token. */
+ if (p == token_buffer + 2 && !isdigit (c))
+ {
+ if (c == '.')
+ {
+ c = getch ();
+ if (c == '.')
+ {
+ *p++ = '.';
+ *p = '\0';
+ value = ELLIPSIS;
+ goto done;
+ }
+ error ("parse error at `..'");
+ }
+ nextchar = c;
+ token_buffer[1] = '\0';
+ value = '.';
+ goto done;
+ }
+ }
+ else
+ {
+ /* It is not a decimal point.
+ It should be a digit (perhaps a hex digit). */
+
+ if (isdigit (c))
+ {
+ c = c - '0';
+ }
+ else if (base <= 10)
+ {
+ if (c == 'e' || c == 'E')
+ {
+ base = 10;
+ floatflag = AFTER_POINT;
+ break; /* start of exponent */
+ }
+ error ("nondigits in number and not hexadecimal");
+ c = 0;
+ }
+ else if (c >= 'a')
+ {
+ c = c - 'a' + 10;
+ }
+ else
+ {
+ c = c - 'A' + 10;
+ }
+ if (c >= largest_digit)
+ largest_digit = c;
+ numdigits++;
+
+ for (count = 0; count < TOTAL_PARTS; count++)
+ {
+ parts[count] *= base;
+ if (count)
+ {
+ parts[count]
+ += (parts[count-1] >> HOST_BITS_PER_CHAR);
+ parts[count-1]
+ &= (1 << HOST_BITS_PER_CHAR) - 1;
+ }
+ else
+ parts[0] += c;
+ }
+
+ /* If the extra highest-order part ever gets anything in it,
+ the number is certainly too big. */
+ if (parts[TOTAL_PARTS - 1] != 0)
+ overflow = 1;
+
+ if (p >= token_buffer + maxtoken - 3)
+ p = extend_token_buffer (p);
+ *p++ = (c = getch ());
+ }
+ }
+
+ if (numdigits == 0)
+ error ("numeric constant with no digits");
+
+ if (largest_digit >= base)
+ error ("numeric constant contains digits beyond the radix");
+
+ /* Remove terminating char from the token buffer and delimit the string */
+ *--p = 0;
+
+ if (floatflag != NOT_FLOAT)
+ {
+ tree type = double_type_node;
+ char f_seen = 0;
+ char l_seen = 0;
+ int garbage_chars = 0;
+ REAL_VALUE_TYPE value;
+ jmp_buf handler;
+
+ /* Read explicit exponent if any, and put it in tokenbuf. */
+
+ if ((c == 'e') || (c == 'E'))
+ {
+ if (p >= token_buffer + maxtoken - 3)
+ p = extend_token_buffer (p);
+ *p++ = c;
+ c = getch ();
+ if ((c == '+') || (c == '-'))
+ {
+ *p++ = c;
+ c = getch ();
+ }
+ if (! isdigit (c))
+ error ("floating constant exponent has no digits");
+ while (isdigit (c))
+ {
+ if (p >= token_buffer + maxtoken - 3)
+ p = extend_token_buffer (p);
+ *p++ = c;
+ c = getch ();
+ }
+ }
+
+ *p = 0;
+ errno = 0;
+
+ /* Convert string to a double, checking for overflow. */
+ if (setjmp (handler))
+ {
+ error ("floating constant out of range");
+ value = dconst0;
+ }
+ else
+ {
+ set_float_handler (handler);
+ /* The second argument, machine_mode, of REAL_VALUE_ATOF
+ tells the desired precision of the binary result of
+ decimal-to-binary conversion. */
+
+ /* Read the suffixes to choose a data type. */
+ switch (c)
+ {
+ case 'f': case 'F':
+ type = float_type_node;
+ value = REAL_VALUE_ATOF (token_buffer, TYPE_MODE (type));
+ garbage_chars = -1;
+ break;
+
+ case 'l': case 'L':
+ type = long_double_type_node;
+ value = REAL_VALUE_ATOF (token_buffer, TYPE_MODE (type));
+ garbage_chars = -1;
+ break;
+
+ default:
+ value = REAL_VALUE_ATOF (token_buffer, TYPE_MODE (type));
+ }
+ set_float_handler (NULL_PTR);
+ }
+ if (pedantic
+ && (REAL_VALUE_ISINF (value)
+#ifdef ERANGE
+ || (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+ && errno == ERANGE
+ /* ERANGE is also reported for underflow, so test the
+ value to distinguish overflow from that. */
+ && (REAL_VALUES_LESS (dconst1, value)
+ || REAL_VALUES_LESS (value, dconstm1)))
+#endif
+ ))
+ {
+ pedwarn ("floating point number exceeds range of `%s'",
+ IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))));
+ }
+ /* Note: garbage_chars is -1 if first char is *not* garbage. */
+ while (isalnum (c))
+ {
+ if (c == 'f' || c == 'F')
+ {
+ if (f_seen)
+ error ("two `f's in floating constant");
+ f_seen = 1;
+ }
+ if (c == 'l' || c == 'L')
+ {
+ if (l_seen)
+ error ("two `l's in floating constant");
+ l_seen = 1;
+ }
+ if (p >= token_buffer + maxtoken - 3)
+ p = extend_token_buffer (p);
+ *p++ = c;
+ c = getch ();
+ garbage_chars++;
+ }
+
+ if (garbage_chars > 0)
+ error ("garbage at end of number");
+
+ /* Create a node with determined type and value. */
+ yylval.ttype = build_real (type, value);
+
+ put_back (c);
+ *p = 0;
+ }
+ else
+ {
+ tree type;
+ HOST_WIDE_INT high, low;
+ int spec_unsigned = 0;
+ int spec_long = 0;
+ int spec_long_long = 0;
+ int bytes, warn;
+
+ while (1)
+ {
+ if (c == 'u' || c == 'U')
+ {
+ if (spec_unsigned)
+ error ("two `u's in integer constant");
+ spec_unsigned = 1;
+ }
+ else if (c == 'l' || c == 'L')
+ {
+ if (spec_long)
+ {
+ if (spec_long_long)
+ error ("three `l's in integer constant");
+ else if (pedantic)
+ pedwarn ("ANSI C++ forbids long long integer constants");
+ spec_long_long = 1;
+ }
+ spec_long = 1;
+ }
+ else
+ {
+ if (isalnum (c))
+ {
+ error ("garbage at end of number");
+ while (isalnum (c))
+ {
+ if (p >= token_buffer + maxtoken - 3)
+ p = extend_token_buffer (p);
+ *p++ = c;
+ c = getch ();
+ }
+ }
+ break;
+ }
+ if (p >= token_buffer + maxtoken - 3)
+ p = extend_token_buffer (p);
+ *p++ = c;
+ c = getch ();
+ }
+
+ put_back (c);
+
+ /* If the constant is not long long and it won't fit in an
+ unsigned long, or if the constant is long long and won't fit
+ in an unsigned long long, then warn that the constant is out
+ of range. */
+
+ /* ??? This assumes that long long and long integer types are
+ a multiple of 8 bits. This better than the original code
+ though which assumed that long was exactly 32 bits and long
+ long was exactly 64 bits. */
+
+ if (spec_long_long)
+ bytes = TYPE_PRECISION (long_long_integer_type_node) / 8;
+ else
+ bytes = TYPE_PRECISION (long_integer_type_node) / 8;
+
+ warn = overflow;
+ for (i = bytes; i < TOTAL_PARTS; i++)
+ if (parts[i])
+ warn = 1;
+ if (warn)
+ pedwarn ("integer constant out of range");
+
+ /* This is simplified by the fact that our constant
+ is always positive. */
+ high = low = 0;
+
+ for (i = 0; i < HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR; i++)
+ {
+ high |= ((HOST_WIDE_INT) parts[i + (HOST_BITS_PER_WIDE_INT
+ / HOST_BITS_PER_CHAR)]
+ << (i * HOST_BITS_PER_CHAR));
+ low |= (HOST_WIDE_INT) parts[i] << (i * HOST_BITS_PER_CHAR);
+ }
+
+
+ yylval.ttype = build_int_2 (low, high);
+ TREE_TYPE (yylval.ttype) = long_long_unsigned_type_node;
+
+#if 0
+ /* Find the first allowable type that the value fits in. */
+ type = 0;
+ for (i = 0; i < sizeof (type_sequence) / sizeof (type_sequence[0]);
+ i++)
+ if (!(spec_long && !type_sequence[i].long_flag)
+ && !(spec_long_long && !type_sequence[i].long_long_flag)
+ && !(spec_unsigned && !type_sequence[i].unsigned_flag)
+ /* A hex or octal constant traditionally is unsigned. */
+ && !(base != 10 && flag_traditional
+ && !type_sequence[i].unsigned_flag)
+ /* A decimal constant can't be unsigned int
+ unless explicitly specified. */
+ && !(base == 10 && !spec_unsigned
+ && *type_sequence[i].node_var == unsigned_type_node))
+ if (int_fits_type_p (yylval.ttype, *type_sequence[i].node_var))
+ {
+ type = *type_sequence[i].node_var;
+ break;
+ }
+ if (flag_traditional && type == long_unsigned_type_node
+ && !spec_unsigned)
+ type = long_integer_type_node;
+
+ if (type == 0)
+ {
+ type = long_long_integer_type_node;
+ warning ("integer constant out of range");
+ }
+
+ /* Warn about some cases where the type of a given constant
+ changes from traditional C to ANSI C. */
+ if (warn_traditional)
+ {
+ tree other_type = 0;
+
+ /* This computation is the same as the previous one
+ except that flag_traditional is used backwards. */
+ for (i = 0; i < sizeof (type_sequence) / sizeof (type_sequence[0]);
+ i++)
+ if (!(spec_long && !type_sequence[i].long_flag)
+ && !(spec_long_long && !type_sequence[i].long_long_flag)
+ && !(spec_unsigned && !type_sequence[i].unsigned_flag)
+ /* A hex or octal constant traditionally is unsigned. */
+ && !(base != 10 && !flag_traditional
+ && !type_sequence[i].unsigned_flag)
+ /* A decimal constant can't be unsigned int
+ unless explicitly specified. */
+ && !(base == 10 && !spec_unsigned
+ && *type_sequence[i].node_var == unsigned_type_node))
+ if (int_fits_type_p (yylval.ttype, *type_sequence[i].node_var))
+ {
+ other_type = *type_sequence[i].node_var;
+ break;
+ }
+ if (!flag_traditional && type == long_unsigned_type_node
+ && !spec_unsigned)
+ type = long_integer_type_node;
+
+ if (other_type != 0 && other_type != type)
+ {
+ if (flag_traditional)
+ warning ("type of integer constant would be different without -traditional");
+ else
+ warning ("type of integer constant would be different with -traditional");
+ }
+ }
+
+#else /* 1 */
+ if (!spec_long && !spec_unsigned
+ && !(flag_traditional && base != 10)
+ && int_fits_type_p (yylval.ttype, integer_type_node))
+ {
+#if 0
+ if (warn_traditional && base != 10)
+ warning ("small nondecimal constant becomes signed in ANSI C++");
+#endif
+ type = integer_type_node;
+ }
+ else if (!spec_long && (base != 10 || spec_unsigned)
+ && int_fits_type_p (yylval.ttype, unsigned_type_node))
+ {
+ /* Nondecimal constants try unsigned even in traditional C. */
+ type = unsigned_type_node;
+ }
+
+ else if (!spec_unsigned && !spec_long_long
+ && int_fits_type_p (yylval.ttype, long_integer_type_node))
+ type = long_integer_type_node;
+
+ else if (! spec_long_long
+ && int_fits_type_p (yylval.ttype,
+ long_unsigned_type_node))
+ {
+#if 0
+ if (warn_traditional && !spec_unsigned)
+ warning ("large integer constant becomes unsigned in ANSI C++");
+#endif
+ if (flag_traditional && !spec_unsigned)
+ type = long_integer_type_node;
+ else
+ type = long_unsigned_type_node;
+ }
+
+ else if (! spec_unsigned
+ /* Verify value does not overflow into sign bit. */
+ && TREE_INT_CST_HIGH (yylval.ttype) >= 0
+ && int_fits_type_p (yylval.ttype,
+ long_long_integer_type_node))
+ type = long_long_integer_type_node;
+
+ else if (int_fits_type_p (yylval.ttype,
+ long_long_unsigned_type_node))
+ {
+#if 0
+ if (warn_traditional && !spec_unsigned)
+ warning ("large nondecimal constant is unsigned in ANSI C++");
+#endif
+
+ if (flag_traditional && !spec_unsigned)
+ type = long_long_integer_type_node;
+ else
+ type = long_long_unsigned_type_node;
+ }
+
+ else
+ {
+ type = long_long_integer_type_node;
+ warning ("integer constant out of range");
+
+ if (base == 10 && ! spec_unsigned && TREE_UNSIGNED (type))
+ warning ("decimal integer constant is so large that it is unsigned");
+ }
+#endif
+
+ TREE_TYPE (yylval.ttype) = type;
+ *p = 0;
+ }
+
+ value = CONSTANT; break;
+ }
+
+ case '\'':
+ char_constant:
+ {
+ register int result = 0;
+ register int num_chars = 0;
+ unsigned width = TYPE_PRECISION (char_type_node);
+ int max_chars;
+
+ if (wide_flag)
+ {
+ width = WCHAR_TYPE_SIZE;
+#ifdef MULTIBYTE_CHARS
+ max_chars = MB_CUR_MAX;
+#else
+ max_chars = 1;
+#endif
+ }
+ else
+ max_chars = TYPE_PRECISION (integer_type_node) / width;
+
+ while (1)
+ {
+ tryagain:
+
+ c = getch ();
+
+ if (c == '\'' || c == EOF)
+ break;
+
+ if (c == '\\')
+ {
+ int ignore = 0;
+ c = readescape (&ignore);
+ if (ignore)
+ goto tryagain;
+ if (width < HOST_BITS_PER_INT
+ && (unsigned) c >= (1 << width))
+ pedwarn ("escape sequence out of range for character");
+#ifdef MAP_CHARACTER
+ if (isprint (c))
+ c = MAP_CHARACTER (c);
+#endif
+ }
+ else if (c == '\n')
+ {
+ if (pedantic)
+ pedwarn ("ANSI C++ forbids newline in character constant");
+ lineno++;
+ }
+#ifdef MAP_CHARACTER
+ else
+ c = MAP_CHARACTER (c);
+#endif
+
+ num_chars++;
+ if (num_chars > maxtoken - 4)
+ extend_token_buffer (token_buffer);
+
+ token_buffer[num_chars] = c;
+
+ /* Merge character into result; ignore excess chars. */
+ if (num_chars < max_chars + 1)
+ {
+ if (width < HOST_BITS_PER_INT)
+ result = (result << width) | (c & ((1 << width) - 1));
+ else
+ result = c;
+ }
+ }
+
+ token_buffer[num_chars + 1] = '\'';
+ token_buffer[num_chars + 2] = 0;
+
+ if (c != '\'')
+ error ("malformatted character constant");
+ else if (num_chars == 0)
+ error ("empty character constant");
+ else if (num_chars > max_chars)
+ {
+ num_chars = max_chars;
+ error ("character constant too long");
+ }
+ else if (num_chars != 1 && ! flag_traditional)
+ warning ("multi-character character constant");
+
+ /* If char type is signed, sign-extend the constant. */
+ if (! wide_flag)
+ {
+ int num_bits = num_chars * width;
+ if (num_bits == 0)
+ /* We already got an error; avoid invalid shift. */
+ yylval.ttype = build_int_2 (0, 0);
+ else if (TREE_UNSIGNED (char_type_node)
+ || ((result >> (num_bits - 1)) & 1) == 0)
+ yylval.ttype
+ = build_int_2 (result & ((unsigned HOST_WIDE_INT) ~0
+ >> (HOST_BITS_PER_INT - num_bits)),
+ 0);
+ else
+ yylval.ttype
+ = build_int_2 (result | ~((unsigned HOST_WIDE_INT) ~0
+ >> (HOST_BITS_PER_INT - num_bits)),
+ -1);
+ if (num_chars<=1)
+ TREE_TYPE (yylval.ttype) = char_type_node;
+ else
+ TREE_TYPE (yylval.ttype) = integer_type_node;
+ }
+ else
+ {
+#ifdef MULTIBYTE_CHARS
+ /* Set the initial shift state and convert the next sequence. */
+ result = 0;
+ /* In all locales L'\0' is zero and mbtowc will return zero,
+ so don't use it. */
+ if (num_chars > 1
+ || (num_chars == 1 && token_buffer[1] != '\0'))
+ {
+ wchar_t wc;
+ (void) mbtowc (NULL, NULL, 0);
+ if (mbtowc (& wc, token_buffer + 1, num_chars) == num_chars)
+ result = wc;
+ else
+ warning ("Ignoring invalid multibyte character");
+ }
+#endif
+ yylval.ttype = build_int_2 (result, 0);
+ TREE_TYPE (yylval.ttype) = wchar_type_node;
+ }
+
+ value = CONSTANT;
+ break;
+ }
+
+ case '"':
+ string_constant:
+ {
+ register char *p;
+
+ c = getch ();
+ p = token_buffer + 1;
+
+ while (c != '"' && c >= 0)
+ {
+ /* ignore_escape_flag is set for reading the filename in #line. */
+ if (!ignore_escape_flag && c == '\\')
+ {
+ int ignore = 0;
+ c = readescape (&ignore);
+ if (ignore)
+ goto skipnewline;
+ if (!wide_flag
+ && TYPE_PRECISION (char_type_node) < HOST_BITS_PER_INT
+ && c >= ((unsigned) 1 << TYPE_PRECISION (char_type_node)))
+ pedwarn ("escape sequence out of range for character");
+ }
+ else if (c == '\n')
+ {
+ if (pedantic)
+ pedwarn ("ANSI C++ forbids newline in string constant");
+ lineno++;
+ }
+
+ if (p == token_buffer + maxtoken)
+ p = extend_token_buffer (p);
+ *p++ = c;
+
+ skipnewline:
+ c = getch ();
+ if (c == EOF) {
+ error("Unterminated string");
+ break;
+ }
+ }
+ *p = 0;
+
+ /* We have read the entire constant.
+ Construct a STRING_CST for the result. */
+
+ if (wide_flag)
+ {
+ /* If this is a L"..." wide-string, convert the multibyte string
+ to a wide character string. */
+ char *widep = (char *) alloca ((p - token_buffer) * WCHAR_BYTES);
+ int len;
+
+#ifdef MULTIBYTE_CHARS
+ len = mbstowcs ((wchar_t *) widep, token_buffer + 1, p - token_buffer);
+ if (len < 0 || len >= (p - token_buffer))
+ {
+ warning ("Ignoring invalid multibyte string");
+ len = 0;
+ }
+ bzero (widep + (len * WCHAR_BYTES), WCHAR_BYTES);
+#else
+ {
+ union { long l; char c[sizeof (long)]; } u;
+ int big_endian;
+ char *wp, *cp;
+
+ /* Determine whether host is little or big endian. */
+ u.l = 1;
+ big_endian = u.c[sizeof (long) - 1];
+ wp = widep + (big_endian ? WCHAR_BYTES - 1 : 0);
+
+ bzero (widep, (p - token_buffer) * WCHAR_BYTES);
+ for (cp = token_buffer + 1; cp < p; cp++)
+ *wp = *cp, wp += WCHAR_BYTES;
+ len = p - token_buffer - 1;
+ }
+#endif
+ yylval.ttype = build_string ((len + 1) * WCHAR_BYTES, widep);
+ TREE_TYPE (yylval.ttype) = wchar_array_type_node;
+ }
+ else
+ {
+ yylval.ttype = build_string (p - token_buffer, token_buffer + 1);
+ TREE_TYPE (yylval.ttype) = char_array_type_node;
+ }
+
+ *p++ = '"';
+ *p = 0;
+
+ value = STRING; break;
+ }
+
+ case '+':
+ case '-':
+ case '&':
+ case '|':
+ case '<':
+ case '>':
+ case '*':
+ case '/':
+ case '%':
+ case '^':
+ case '!':
+ case '=':
+ {
+ register int c1;
+
+ combine:
+
+ switch (c)
+ {
+ case '+':
+ yylval.code = PLUS_EXPR; break;
+ case '-':
+ yylval.code = MINUS_EXPR; break;
+ case '&':
+ yylval.code = BIT_AND_EXPR; break;
+ case '|':
+ yylval.code = BIT_IOR_EXPR; break;
+ case '*':
+ yylval.code = MULT_EXPR; break;
+ case '/':
+ yylval.code = TRUNC_DIV_EXPR; break;
+ case '%':
+ yylval.code = TRUNC_MOD_EXPR; break;
+ case '^':
+ yylval.code = BIT_XOR_EXPR; break;
+ case LSHIFT:
+ yylval.code = LSHIFT_EXPR; break;
+ case RSHIFT:
+ yylval.code = RSHIFT_EXPR; break;
+ case '<':
+ yylval.code = LT_EXPR; break;
+ case '>':
+ yylval.code = GT_EXPR; break;
+ }
+
+ token_buffer[1] = c1 = getch ();
+ token_buffer[2] = 0;
+
+ if (c1 == '=')
+ {
+ switch (c)
+ {
+ case '<':
+ value = ARITHCOMPARE; yylval.code = LE_EXPR; goto done;
+ case '>':
+ value = ARITHCOMPARE; yylval.code = GE_EXPR; goto done;
+ case '!':
+ value = EQCOMPARE; yylval.code = NE_EXPR; goto done;
+ case '=':
+ value = EQCOMPARE; yylval.code = EQ_EXPR; goto done;
+ }
+ value = ASSIGN; goto done;
+ }
+ else if (c == c1)
+ switch (c)
+ {
+ case '+':
+ value = PLUSPLUS; goto done;
+ case '-':
+ value = MINUSMINUS; goto done;
+ case '&':
+ value = ANDAND; goto done;
+ case '|':
+ value = OROR; goto done;
+ case '<':
+ c = LSHIFT;
+ goto combine;
+ case '>':
+ c = RSHIFT;
+ goto combine;
+ }
+ else if ((c == '-') && (c1 == '>'))
+ {
+ nextchar = skip_white_space (getch ());
+ if (nextchar == '*')
+ {
+ nextchar = -1;
+ value = POINTSAT_STAR;
+ }
+ else
+ value = POINTSAT;
+ goto done;
+ }
+ else if (c1 == '?' && (c == '<' || c == '>'))
+ {
+ token_buffer[3] = 0;
+
+ c1 = getch ();
+ yylval.code = (c == '<' ? MIN_EXPR : MAX_EXPR);
+ if (c1 == '=')
+ {
+ /* <?= or >?= expression. */
+ token_buffer[2] = c1;
+ value = ASSIGN;
+ }
+ else
+ {
+ value = MIN_MAX;
+ nextchar = c1;
+ }
+ if (flag_ansi)
+ pedwarn ("use of `operator %s' is not standard C++",
+ token_buffer);
+ goto done;
+ }
+
+ nextchar = c1;
+ token_buffer[1] = 0;
+
+ value = c;
+ goto done;
+ }
+
+ case ':':
+ c = getch ();
+ if (c == ':')
+ {
+ token_buffer[1] = ':';
+ token_buffer[2] = '\0';
+ value = SCOPE;
+ yylval.itype = 1;
+ }
+ else
+ {
+ nextchar = c;
+ value = ':';
+ }
+ break;
+
+ case 0:
+ /* Don't make yyparse think this is eof. */
+ value = 1;
+ break;
+
+ case '(':
+ /* try, weakly, to handle casts to pointers to functions. */
+ nextchar = skip_white_space (getch ());
+ if (nextchar == '*')
+ {
+ int next_c = skip_white_space (getch ());
+ if (next_c == ')')
+ {
+ nextchar = -1;
+ yylval.ttype = build1 (INDIRECT_REF, 0, 0);
+ value = PAREN_STAR_PAREN;
+ }
+ else
+ {
+ put_back (next_c);
+ value = c;
+ }
+ }
+ else if (nextchar == ')')
+ {
+ nextchar = -1;
+ yylval.ttype = NULL_TREE;
+ value = LEFT_RIGHT;
+ }
+ else value = c;
+ break;
+
+ default:
+ value = c;
+ }
+
+done:
+/* yylloc.last_line = lineno; */
+#ifdef GATHER_STATISTICS
+ token_count[value] += 1;
+#endif
+
+ return value;
+}
+
+typedef enum
+{
+ d_kind, t_kind, s_kind, r_kind, e_kind, c_kind,
+ id_kind, op_id_kind, perm_list_kind, temp_list_kind,
+ vec_kind, x_kind, lang_decl, lang_type, all_kinds
+} tree_node_kind;
+extern int tree_node_counts[];
+extern int tree_node_sizes[];
+extern char *tree_node_kind_names[];
+
+/* Place to save freed lang_decls which were allocated on the
+ permanent_obstack. @@ Not currently used. */
+tree free_lang_decl_chain;
+
+tree
+build_lang_decl (code, name, type)
+ enum tree_code code;
+ tree name;
+ tree type;
+{
+ register tree t = build_decl (code, name, type);
+ struct obstack *obstack = current_obstack;
+ register int i = sizeof (struct lang_decl) / sizeof (int);
+ register int *pi;
+
+ if (! TREE_PERMANENT (t))
+ obstack = saveable_obstack;
+ else
+ /* Could be that saveable is permanent and current is not. */
+ obstack = &permanent_obstack;
+
+ if (free_lang_decl_chain && obstack == &permanent_obstack)
+ {
+ pi = (int *)free_lang_decl_chain;
+ free_lang_decl_chain = TREE_CHAIN (free_lang_decl_chain);
+ }
+ else
+ pi = (int *) obstack_alloc (obstack, sizeof (struct lang_decl));
+
+ while (i > 0)
+ pi[--i] = 0;
+
+ DECL_LANG_SPECIFIC (t) = (struct lang_decl *) pi;
+ LANG_DECL_PERMANENT ((struct lang_decl *) pi)
+ = obstack == &permanent_obstack;
+ my_friendly_assert (LANG_DECL_PERMANENT ((struct lang_decl *) pi)
+ == TREE_PERMANENT (t), 234);
+ DECL_MAIN_VARIANT (t) = t;
+ if (current_lang_name == lang_name_cplusplus)
+ {
+ DECL_LANGUAGE (t) = lang_cplusplus;
+#if 0
+#ifndef NO_AUTO_OVERLOAD
+ if (code == FUNCTION_DECL && name != 0
+ && ! (IDENTIFIER_LENGTH (name) == 4
+ && IDENTIFIER_POINTER (name)[0] == 'm'
+ && strcmp (IDENTIFIER_POINTER (name), "main") == 0)
+ && ! (IDENTIFIER_LENGTH (name) > 10
+ && IDENTIFIER_POINTER (name)[0] == '_'
+ && IDENTIFIER_POINTER (name)[1] == '_'
+ && strncmp (IDENTIFIER_POINTER (name)+2, "builtin_", 8) == 0))
+ TREE_OVERLOADED (name) = 1;
+#endif
+#endif
+ }
+ else if (current_lang_name == lang_name_c)
+ DECL_LANGUAGE (t) = lang_c;
+ else my_friendly_abort (64);
+
+#if 0 /* not yet, should get fixed properly later */
+ if (code == TYPE_DECL)
+ {
+ tree id;
+ id = get_identifier (build_overload_name (type, 1, 1));
+ DECL_ASSEMBLER_NAME (t) = id;
+ }
+
+#endif
+#ifdef GATHER_STATISTICS
+ tree_node_counts[(int)lang_decl] += 1;
+ tree_node_sizes[(int)lang_decl] += sizeof(struct lang_decl);
+#endif
+
+ return t;
+}
+
+tree
+build_lang_field_decl (code, name, type)
+ enum tree_code code;
+ tree name;
+ tree type;
+{
+ extern struct obstack *current_obstack, *saveable_obstack;
+ register tree t = build_decl (code, name, type);
+ struct obstack *obstack = current_obstack;
+ register int i = sizeof (struct lang_decl_flags) / sizeof (int);
+ register int *pi;
+#if 0 /* not yet, should get fixed properly later */
+
+ if (code == TYPE_DECL)
+ {
+ tree id;
+ id = get_identifier (build_overload_name (type, 1, 1));
+ DECL_ASSEMBLER_NAME (t) = id;
+ }
+#endif
+
+ if (! TREE_PERMANENT (t))
+ obstack = saveable_obstack;
+ else
+ my_friendly_assert (obstack == &permanent_obstack, 235);
+
+ pi = (int *) obstack_alloc (obstack, sizeof (struct lang_decl_flags));
+ while (i > 0)
+ pi[--i] = 0;
+
+ DECL_LANG_SPECIFIC (t) = (struct lang_decl *) pi;
+ return t;
+}
+
+void
+copy_lang_decl (node)
+ tree node;
+{
+ int size;
+ int *pi;
+
+ if (TREE_CODE (node) == FIELD_DECL)
+ size = sizeof (struct lang_decl_flags);
+ else
+ size = sizeof (struct lang_decl);
+ pi = (int *)obstack_alloc (&permanent_obstack, size);
+ bcopy ((char *)DECL_LANG_SPECIFIC (node), (char *)pi, size);
+ DECL_LANG_SPECIFIC (node) = (struct lang_decl *)pi;
+}
+
+tree
+make_lang_type (code)
+ enum tree_code code;
+{
+ extern struct obstack *current_obstack, *saveable_obstack;
+ register tree t = make_node (code);
+ struct obstack *obstack = current_obstack;
+ register int i = sizeof (struct lang_type) / sizeof (int);
+ register int *pi;
+
+ /* Set up some flags that give proper default behavior. */
+ IS_AGGR_TYPE (t) = 1;
+
+ if (! TREE_PERMANENT (t))
+ obstack = saveable_obstack;
+ else
+ my_friendly_assert (obstack == &permanent_obstack, 236);
+
+ pi = (int *) obstack_alloc (obstack, sizeof (struct lang_type));
+ while (i > 0)
+ pi[--i] = 0;
+
+ TYPE_LANG_SPECIFIC (t) = (struct lang_type *) pi;
+ CLASSTYPE_AS_LIST (t) = build_tree_list (NULL_TREE, t);
+ SET_CLASSTYPE_INTERFACE_UNKNOWN_X (t, interface_unknown);
+ CLASSTYPE_INTERFACE_ONLY (t) = interface_only;
+ CLASSTYPE_VBASE_SIZE (t) = integer_zero_node;
+ TYPE_BINFO (t) = make_binfo (integer_zero_node, t, NULL_TREE, NULL_TREE,
+ NULL_TREE);
+ CLASSTYPE_BINFO_AS_LIST (t) = build_tree_list (NULL_TREE, TYPE_BINFO (t));
+
+ /* Make sure this is laid out, for ease of use later.
+ In the presence of parse errors, the normal was of assuring
+ this might not ever get executed, so we lay it out *immediately*. */
+ build_pointer_type (t);
+
+#ifdef GATHER_STATISTICS
+ tree_node_counts[(int)lang_type] += 1;
+ tree_node_sizes[(int)lang_type] += sizeof(struct lang_type);
+#endif
+
+ return t;
+}
+
+void
+copy_decl_lang_specific (decl)
+ tree decl;
+{
+ extern struct obstack *current_obstack, *saveable_obstack;
+ register int *old = (int *)DECL_LANG_SPECIFIC (decl);
+ struct obstack *obstack = current_obstack;
+ register int i = sizeof (struct lang_decl) / sizeof (int);
+ register int *pi;
+
+ if (! TREE_PERMANENT (decl))
+ obstack = saveable_obstack;
+ else
+ my_friendly_assert (obstack == &permanent_obstack, 237);
+
+ pi = (int *) obstack_alloc (obstack, sizeof (struct lang_decl));
+ while (i-- > 0)
+ pi[i] = old[i];
+
+ DECL_LANG_SPECIFIC (decl) = (struct lang_decl *) pi;
+
+#ifdef GATHER_STATISTICS
+ tree_node_counts[(int)lang_decl] += 1;
+ tree_node_sizes[(int)lang_decl] += sizeof(struct lang_decl);
+#endif
+}
+
+void
+dump_time_statistics ()
+{
+ register tree prev = 0, decl, next;
+ int this_time = my_get_run_time ();
+ TREE_INT_CST_LOW (IDENTIFIER_LOCAL_VALUE (this_filename_time))
+ += this_time - body_time;
+
+ fprintf (stderr, "\n******\n");
+ print_time ("header files (total)", header_time);
+ print_time ("main file (total)", this_time - body_time);
+ fprintf (stderr, "ratio = %g : 1\n",
+ (double)header_time / (double)(this_time - body_time));
+ fprintf (stderr, "\n******\n");
+
+ for (decl = filename_times; decl; decl = next)
+ {
+ next = IDENTIFIER_GLOBAL_VALUE (decl);
+ IDENTIFIER_GLOBAL_VALUE (decl) = prev;
+ prev = decl;
+ }
+
+ for (decl = prev; decl; decl = IDENTIFIER_GLOBAL_VALUE (decl))
+ print_time (IDENTIFIER_POINTER (decl),
+ TREE_INT_CST_LOW (IDENTIFIER_LOCAL_VALUE (decl)));
+}
+
+void
+compiler_error (s, v, v2)
+ char *s;
+ HOST_WIDE_INT v, v2; /* @@also used as pointer */
+{
+ char buf[1024];
+ sprintf (buf, s, v, v2);
+ error_with_file_and_line (input_filename, lineno, "%s (compiler error)", buf);
+}
+
+void
+compiler_error_with_decl (decl, s)
+ tree decl;
+ char *s;
+{
+ char *name;
+ count_error (0);
+
+ report_error_function (0);
+
+ if (TREE_CODE (decl) == PARM_DECL)
+ fprintf (stderr, "%s:%d: ",
+ DECL_SOURCE_FILE (DECL_CONTEXT (decl)),
+ DECL_SOURCE_LINE (DECL_CONTEXT (decl)));
+ else
+ fprintf (stderr, "%s:%d: ",
+ DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl));
+
+ name = lang_printable_name (decl);
+ if (name)
+ fprintf (stderr, s, name);
+ else
+ fprintf (stderr, s, "((anonymous))");
+ fprintf (stderr, " (compiler error)\n");
+}
+
+void
+yyerror (string)
+ char *string;
+{
+ extern int end_of_file;
+ char buf[200];
+
+ strcpy (buf, string);
+
+ /* We can't print string and character constants well
+ because the token_buffer contains the result of processing escapes. */
+ if (end_of_file)
+ strcat (buf, input_redirected ()
+ ? " at end of saved text"
+ : " at end of input");
+ else if (token_buffer[0] == 0)
+ strcat (buf, " at null character");
+ else if (token_buffer[0] == '"')
+ strcat (buf, " before string constant");
+ else if (token_buffer[0] == '\'')
+ strcat (buf, " before character constant");
+ else if (token_buffer[0] < 040 || (unsigned char) token_buffer[0] >= 0177)
+ sprintf (buf + strlen (buf), " before character 0%o",
+ (unsigned char) token_buffer[0]);
+ else
+ strcat (buf, " before `%s'");
+
+ error (buf, token_buffer);
+}
diff --git a/gnu/usr.bin/cc/cc1plus/lex.h b/gnu/usr.bin/cc/cc1plus/lex.h
new file mode 100644
index 0000000..3da4635
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/lex.h
@@ -0,0 +1,130 @@
+/* Define constants and variables for communication with parse.y.
+ Copyright (C) 1987, 1992, 1993 Free Software Foundation, Inc.
+ Hacked by Michael Tiemann (tiemann@cygnus.com)
+ and by Brendan Kehoe (brendan@cygnus.com).
+
+This file is part of GNU CC.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY. No author or distributor
+accepts responsibility to anyone for the consequences of using it
+or for whether it serves any particular purpose or works at all,
+unless he says so in writing. Refer to the GNU CC General Public
+License for full details.
+
+Everyone is granted permission to copy, modify and redistribute
+GNU CC, but only under the conditions described in the
+GNU CC General Public License. A copy of this license is
+supposed to have been given to you along with GNU CC so you
+can know your rights and responsibilities. It should be in a
+file named COPYING. Among other things, the copyright notice
+and this notice must be preserved on all copies. */
+
+
+
+enum rid
+{
+ RID_UNUSED,
+ RID_INT,
+ RID_BOOL,
+ RID_CHAR,
+ RID_WCHAR,
+ RID_FLOAT,
+ RID_DOUBLE,
+ RID_VOID,
+
+ /* C++ extension */
+ RID_CLASS,
+ RID_RECORD,
+ RID_UNION,
+ RID_ENUM,
+ RID_LONGLONG,
+
+ /* This is where grokdeclarator starts its search when setting the specbits.
+ The first seven are in the order of most frequently used, as found
+ building libg++. */
+
+ RID_EXTERN,
+ RID_CONST,
+ RID_LONG,
+ RID_TYPEDEF,
+ RID_UNSIGNED,
+ RID_SHORT,
+ RID_INLINE,
+
+ RID_STATIC,
+
+ RID_REGISTER,
+ RID_VOLATILE,
+ RID_FRIEND,
+ RID_VIRTUAL,
+ RID_PUBLIC,
+ RID_PRIVATE,
+ RID_PROTECTED,
+ RID_SIGNED,
+ RID_EXCEPTION,
+ RID_RAISES,
+ RID_AUTO,
+ RID_MUTABLE,
+ RID_TEMPLATE,
+ RID_SIGNATURE,
+ /* Before adding enough to get up to 64, the RIDBIT_* macros
+ will have to be changed a little. */
+ RID_MAX
+};
+
+#define NORID RID_UNUSED
+
+#define RID_FIRST_MODIFIER RID_EXTERN
+
+/* The type that can represent all values of RIDBIT. */
+/* We assume that we can stick in at least 32 bits into this. */
+typedef struct { unsigned long idata[2]; }
+ RID_BIT_TYPE;
+
+/* Be careful, all these modify N twice. */
+#define RIDBIT_SETP(N, V) (((unsigned long)1 << (int) ((N)%32)) \
+ & (V).idata[(N)/32])
+#define RIDBIT_NOTSETP(NN, VV) (! RIDBIT_SETP (NN, VV))
+#define RIDBIT_SET(N, V) do { \
+ (V).idata[(N)/32] \
+ |= ((unsigned long)1 << (int) ((N)%32)); \
+ } while (0)
+#define RIDBIT_RESET(N, V) do { \
+ (V).idata[(N)/32] \
+ &= ~((unsigned long)1 << (int) ((N)%32)); \
+ } while (0)
+#define RIDBIT_RESET_ALL(V) do { \
+ (V).idata[0] = 0; \
+ (V).idata[1] = 0; \
+ } while (0)
+#define RIDBIT_ANY_SET(V) ((V).idata[0] || (V).idata[1])
+
+/* The elements of `ridpointers' are identifier nodes
+ for the reserved type names and storage classes.
+ It is indexed by a RID_... value. */
+extern tree ridpointers[(int) RID_MAX];
+
+/* the declaration found for the last IDENTIFIER token read in.
+ yylex must look this up to detect typedefs, which get token type TYPENAME,
+ so it is left around in case the identifier is not a typedef but is
+ used in a context which makes it a reference to a variable. */
+extern tree lastiddecl;
+
+extern char *token_buffer; /* Pointer to token buffer. */
+
+/* Back-door communication channel to the lexer. */
+extern int looking_for_typename;
+extern int looking_for_template;
+
+/* Tell the lexer where to look for names. */
+extern tree got_scope;
+
+/* Pending language change.
+ Positive is push count, negative is pop count. */
+extern int pending_lang_change;
+
+extern tree make_pointer_declarator (), make_reference_declarator ();
+extern void reinit_parse_for_function ();
+extern void reinit_parse_for_method ();
+extern int yylex ();
diff --git a/gnu/usr.bin/cc/cc1plus/method.c b/gnu/usr.bin/cc/cc1plus/method.c
new file mode 100644
index 0000000..f64a16a
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/method.c
@@ -0,0 +1,1948 @@
+/* Handle the hair of processing (but not expanding) inline functions.
+ Also manage function and variable name overloading.
+ Copyright (C) 1987, 1989, 1992, 1993 Free Software Foundation, Inc.
+ Contributed by Michael Tiemann (tiemann@cygnus.com)
+
+ This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#ifndef PARM_CAN_BE_ARRAY_TYPE
+#define PARM_CAN_BE_ARRAY_TYPE 1
+#endif
+
+/* Handle method declarations. */
+#include <stdio.h>
+#include "config.h"
+#include "tree.h"
+#include "cp-tree.h"
+#include "class.h"
+#include "obstack.h"
+#include <ctype.h>
+#include "rtl.h"
+#include "expr.h"
+#include "output.h"
+#include "hard-reg-set.h"
+#include "flags.h"
+
+/* TREE_LIST of the current inline functions that need to be
+ processed. */
+struct pending_inline *pending_inlines;
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+/* Obstack where we build text strings for overloading, etc. */
+static struct obstack scratch_obstack;
+static char *scratch_firstobj;
+
+# define OB_INIT() (scratch_firstobj ? (obstack_free (&scratch_obstack, scratch_firstobj), 0) : 0)
+# define OB_PUTC(C) (obstack_1grow (&scratch_obstack, (C)))
+# define OB_PUTC2(C1,C2) \
+ (obstack_1grow (&scratch_obstack, (C1)), obstack_1grow (&scratch_obstack, (C2)))
+# define OB_PUTS(S) (obstack_grow (&scratch_obstack, (S), sizeof (S) - 1))
+# define OB_PUTID(ID) \
+ (obstack_grow (&scratch_obstack, IDENTIFIER_POINTER (ID), \
+ IDENTIFIER_LENGTH (ID)))
+# define OB_PUTCP(S) (obstack_grow (&scratch_obstack, (S), strlen (S)))
+# define OB_FINISH() (obstack_1grow (&scratch_obstack, '\0'))
+
+#ifdef NO_AUTO_OVERLOAD
+int is_overloaded ();
+#endif
+
+void
+init_method ()
+{
+ gcc_obstack_init (&scratch_obstack);
+ scratch_firstobj = (char *)obstack_alloc (&scratch_obstack, 0);
+}
+
+/* This must be large enough to hold any printed integer or floating-point
+ value. */
+static char digit_buffer[128];
+
+/* Move inline function definitions out of structure so that they
+ can be processed normally. CNAME is the name of the class
+ we are working from, METHOD_LIST is the list of method lists
+ of the structure. We delete friend methods here, after
+ saving away their inline function definitions (if any). */
+
+void
+do_inline_function_hair (type, friend_list)
+ tree type, friend_list;
+{
+ tree method = TYPE_METHODS (type);
+
+ if (method && TREE_CODE (method) == TREE_VEC)
+ {
+ if (TREE_VEC_ELT (method, 0))
+ method = TREE_VEC_ELT (method, 0);
+ else
+ method = TREE_VEC_ELT (method, 1);
+ }
+
+ while (method)
+ {
+ /* Do inline member functions. */
+ struct pending_inline *info = DECL_PENDING_INLINE_INFO (method);
+ if (info)
+ {
+ tree args;
+
+ my_friendly_assert (info->fndecl == method, 238);
+ args = DECL_ARGUMENTS (method);
+ while (args)
+ {
+ DECL_CONTEXT (args) = method;
+ args = TREE_CHAIN (args);
+ }
+
+ /* Allow this decl to be seen in global scope. Don't do this for
+ local class methods, though. */
+ if (! current_function_decl)
+ IDENTIFIER_GLOBAL_VALUE (DECL_ASSEMBLER_NAME (method)) = method;
+ }
+ method = TREE_CHAIN (method);
+ }
+ while (friend_list)
+ {
+ tree fndecl = TREE_VALUE (friend_list);
+ struct pending_inline *info = DECL_PENDING_INLINE_INFO (fndecl);
+ if (info)
+ {
+ tree args;
+
+ my_friendly_assert (info->fndecl == fndecl, 239);
+ args = DECL_ARGUMENTS (fndecl);
+ while (args)
+ {
+ DECL_CONTEXT (args) = fndecl;
+ args = TREE_CHAIN (args);
+ }
+
+ /* Allow this decl to be seen in global scope */
+ if (! current_function_decl)
+ IDENTIFIER_GLOBAL_VALUE (DECL_ASSEMBLER_NAME (fndecl)) = fndecl;
+ }
+
+ friend_list = TREE_CHAIN (friend_list);
+ }
+}
+
+/* Report an argument type mismatch between the best declared function
+ we could find and the current argument list that we have. */
+void
+report_type_mismatch (cp, parmtypes, name_kind)
+ struct candidate *cp;
+ tree parmtypes;
+ char *name_kind;
+{
+ int i = cp->u.bad_arg;
+ tree ttf, tta;
+ char *tmp_firstobj;
+
+ switch (i)
+ {
+ case -4:
+ my_friendly_assert (TREE_CODE (cp->function) == TEMPLATE_DECL, 240);
+ cp_error ("type unification failed for function template `%#D'",
+ cp->function);
+ return;
+
+ case -3:
+ if (TYPE_READONLY (TREE_TYPE (TREE_VALUE (parmtypes))))
+ cp_error ("call to const %s `%#D' with non-const object", name_kind,
+ cp->function);
+ else
+ cp_error ("call to non-const %s `%#D' with const object", name_kind,
+ cp->function);
+ return;
+ case -2:
+ cp_error ("too few arguments for %s `%#D'", name_kind, cp->function);
+ return;
+ case -1:
+ cp_error ("too many arguments for %s `%#D'", name_kind, cp->function);
+ return;
+ case 0:
+ if (TREE_CODE (TREE_TYPE (cp->function)) == METHOD_TYPE)
+ {
+ /* Happens when we have an ambiguous base class. */
+ my_friendly_assert (get_binfo (DECL_CLASS_CONTEXT (cp->function),
+ TREE_TYPE (TREE_TYPE (TREE_VALUE (parmtypes))), 1) == error_mark_node,
+ 241);
+ return;
+ }
+ }
+
+ ttf = TYPE_ARG_TYPES (TREE_TYPE (cp->function));
+ tta = parmtypes;
+
+ while (i-- > 0)
+ {
+ ttf = TREE_CHAIN (ttf);
+ tta = TREE_CHAIN (tta);
+ }
+
+ OB_INIT ();
+ OB_PUTS ("bad argument ");
+ sprintf (digit_buffer, "%d", cp->u.bad_arg
+ - (TREE_CODE (TREE_TYPE (cp->function)) == METHOD_TYPE)
+ + 1);
+ OB_PUTCP (digit_buffer);
+
+ OB_PUTS (" for function `");
+ OB_PUTCP (decl_as_string (cp->function, 1));
+ OB_PUTS ("' (type was ");
+
+ /* Reset `i' so that type printing routines do the right thing. */
+ if (tta)
+ {
+ enum tree_code code = TREE_CODE (TREE_TYPE (TREE_VALUE (tta)));
+ if (code == ERROR_MARK)
+ OB_PUTS ("(failed type instantiation)");
+ else
+ {
+ i = (code == FUNCTION_TYPE || code == METHOD_TYPE);
+ OB_PUTCP (type_as_string (TREE_TYPE (TREE_VALUE (tta)), 1));
+ }
+ }
+ else OB_PUTS ("void");
+ OB_PUTC (')');
+ OB_FINISH ();
+
+ tmp_firstobj = (char *)alloca (obstack_object_size (&scratch_obstack));
+ bcopy (obstack_base (&scratch_obstack), tmp_firstobj,
+ obstack_object_size (&scratch_obstack));
+ error (tmp_firstobj);
+}
+
+/* Here is where overload code starts. */
+
+/* Array of types seen so far in top-level call to `build_overload_name'.
+ Allocated and deallocated by caller. */
+static tree *typevec;
+
+/* Number of types interned by `build_overload_name' so far. */
+static int maxtype;
+
+/* Number of occurrences of last type seen. */
+static int nrepeats;
+
+/* Nonzero if we should not try folding parameter types. */
+static int nofold;
+
+#define ALLOCATE_TYPEVEC(PARMTYPES) \
+ do { maxtype = 0, nrepeats = 0; \
+ typevec = (tree *)alloca (list_length (PARMTYPES) * sizeof (tree)); } while (0)
+
+#define DEALLOCATE_TYPEVEC(PARMTYPES) \
+ do { tree t = (PARMTYPES); \
+ while (t) { TREE_USED (TREE_VALUE (t)) = 0; t = TREE_CHAIN (t); } \
+ } while (0)
+
+/* Code to concatenate an asciified integer to a string. */
+static
+#ifdef __GNUC__
+__inline
+#endif
+void
+icat (i)
+ int i;
+{
+ /* Handle this case first, to go really quickly. For many common values,
+ the result of i/10 below is 1. */
+ if (i == 1)
+ {
+ OB_PUTC ('1');
+ return;
+ }
+
+ if (i < 0)
+ {
+ OB_PUTC ('m');
+ i = -i;
+ }
+ if (i < 10)
+ OB_PUTC ('0' + i);
+ else
+ {
+ icat (i / 10);
+ OB_PUTC ('0' + (i % 10));
+ }
+}
+
+static
+#ifdef __GNUC__
+__inline
+#endif
+void
+flush_repeats (type)
+ tree type;
+{
+ int tindex = 0;
+
+ while (typevec[tindex] != type)
+ tindex++;
+
+ if (nrepeats > 1)
+ {
+ OB_PUTC ('N');
+ icat (nrepeats);
+ if (nrepeats > 9)
+ OB_PUTC ('_');
+ }
+ else
+ OB_PUTC ('T');
+ nrepeats = 0;
+ icat (tindex);
+ if (tindex > 9)
+ OB_PUTC ('_');
+}
+
+static void build_overload_identifier ();
+
+static void
+build_overload_nested_name (context)
+ tree context;
+{
+ /* We use DECL_NAME here, because pushtag now sets the DECL_ASSEMBLER_NAME. */
+ tree name = DECL_NAME (context);
+ if (DECL_CONTEXT (context))
+ {
+ context = DECL_CONTEXT (context);
+ if (TREE_CODE_CLASS (TREE_CODE (context)) == 't')
+ context = TYPE_NAME (context);
+ build_overload_nested_name (context);
+ }
+ build_overload_identifier (name);
+}
+
+static void
+build_overload_value (type, value)
+ tree type, value;
+{
+ while (TREE_CODE (value) == NON_LVALUE_EXPR
+ || TREE_CODE (value) == NOP_EXPR)
+ value = TREE_OPERAND (value, 0);
+ my_friendly_assert (TREE_CODE (type) == PARM_DECL, 242);
+ type = TREE_TYPE (type);
+ switch (TREE_CODE (type))
+ {
+ case INTEGER_TYPE:
+ case ENUMERAL_TYPE:
+ {
+ my_friendly_assert (TREE_CODE (value) == INTEGER_CST, 243);
+ if (TYPE_PRECISION (value) == 2 * HOST_BITS_PER_WIDE_INT)
+ {
+ if (tree_int_cst_lt (value, integer_zero_node))
+ {
+ OB_PUTC ('m');
+ value = build_int_2 (~ TREE_INT_CST_LOW (value),
+ - TREE_INT_CST_HIGH (value));
+ }
+ if (TREE_INT_CST_HIGH (value)
+ != (TREE_INT_CST_LOW (value) >> (HOST_BITS_PER_WIDE_INT - 1)))
+ {
+ /* need to print a DImode value in decimal */
+ sorry ("conversion of long long as PT parameter");
+ }
+ /* else fall through to print in smaller mode */
+ }
+ /* Wordsize or smaller */
+ icat (TREE_INT_CST_LOW (value));
+ return;
+ }
+ case BOOLEAN_TYPE:
+ {
+ icat (TREE_INT_CST_LOW (value));
+ return;
+ }
+#ifndef REAL_IS_NOT_DOUBLE
+ case REAL_TYPE:
+ {
+ REAL_VALUE_TYPE val;
+ char *bufp = digit_buffer;
+ extern char *index ();
+
+ my_friendly_assert (TREE_CODE (value) == REAL_CST, 244);
+ val = TREE_REAL_CST (value);
+ if (val < 0)
+ {
+ val = -val;
+ *bufp++ = 'm';
+ }
+ sprintf (bufp, "%e", val);
+ bufp = (char *) index (bufp, 'e');
+ if (!bufp)
+ strcat (digit_buffer, "e0");
+ else
+ {
+ char *p;
+ bufp++;
+ if (*bufp == '-')
+ {
+ *bufp++ = 'm';
+ }
+ p = bufp;
+ if (*p == '+')
+ p++;
+ while (*p == '0')
+ p++;
+ if (*p == 0)
+ {
+ *bufp++ = '0';
+ *bufp = 0;
+ }
+ else if (p != bufp)
+ {
+ while (*p)
+ *bufp++ = *p++;
+ *bufp = 0;
+ }
+ }
+ OB_PUTCP (digit_buffer);
+ return;
+ }
+#endif
+ case POINTER_TYPE:
+ value = TREE_OPERAND (value, 0);
+ if (TREE_CODE (value) == VAR_DECL)
+ {
+ my_friendly_assert (DECL_NAME (value) != 0, 245);
+ build_overload_identifier (DECL_NAME (value));
+ return;
+ }
+ else if (TREE_CODE (value) == FUNCTION_DECL)
+ {
+ my_friendly_assert (DECL_NAME (value) != 0, 246);
+ build_overload_identifier (DECL_NAME (value));
+ return;
+ }
+ else
+ my_friendly_abort (71);
+ break; /* not really needed */
+
+ default:
+ sorry ("conversion of %s as template parameter",
+ tree_code_name [(int) TREE_CODE (type)]);
+ my_friendly_abort (72);
+ }
+}
+
+static void
+build_overload_identifier (name)
+ tree name;
+{
+ if (IDENTIFIER_TEMPLATE (name))
+ {
+ tree template, parmlist, arglist, tname;
+ int i, nparms;
+ template = IDENTIFIER_TEMPLATE (name);
+ arglist = TREE_VALUE (template);
+ template = TREE_PURPOSE (template);
+ tname = DECL_NAME (template);
+ parmlist = DECL_ARGUMENTS (template);
+ nparms = TREE_VEC_LENGTH (parmlist);
+ OB_PUTC ('t');
+ icat (IDENTIFIER_LENGTH (tname));
+ OB_PUTID (tname);
+ icat (nparms);
+ for (i = 0; i < nparms; i++)
+ {
+ tree parm = TREE_VEC_ELT (parmlist, i);
+ tree arg = TREE_VEC_ELT (arglist, i);
+ if (TREE_CODE (parm) == IDENTIFIER_NODE)
+ {
+ /* This parameter is a type. */
+ OB_PUTC ('Z');
+ build_overload_name (arg, 0, 0);
+ }
+ else
+ {
+ /* It's a PARM_DECL. */
+ build_overload_name (TREE_TYPE (parm), 0, 0);
+ build_overload_value (parm, arg);
+ }
+ }
+ }
+ else
+ {
+ icat (IDENTIFIER_LENGTH (name));
+ OB_PUTID (name);
+ }
+}
+
+/* Given a list of parameters in PARMTYPES, create an unambiguous
+ overload string. Should distinguish any type that C (or C++) can
+ distinguish. I.e., pointers to functions are treated correctly.
+
+ Caller must deal with whether a final `e' goes on the end or not.
+
+ Any default conversions must take place before this function
+ is called.
+
+ BEGIN and END control initialization and finalization of the
+ obstack where we build the string. */
+
+char *
+build_overload_name (parmtypes, begin, end)
+ tree parmtypes;
+ int begin, end;
+{
+ int just_one;
+ tree parmtype;
+
+ if (begin) OB_INIT ();
+
+ if ((just_one = (TREE_CODE (parmtypes) != TREE_LIST)))
+ {
+ parmtype = parmtypes;
+ goto only_one;
+ }
+
+ while (parmtypes)
+ {
+ parmtype = TREE_VALUE (parmtypes);
+
+ only_one:
+
+ if (! nofold)
+ {
+ if (! just_one)
+ /* Every argument gets counted. */
+ typevec[maxtype++] = parmtype;
+
+ if (TREE_USED (parmtype))
+ {
+ if (! just_one && parmtype == typevec[maxtype-2])
+ nrepeats++;
+ else
+ {
+ if (nrepeats)
+ flush_repeats (parmtype);
+ if (! just_one && TREE_CHAIN (parmtypes)
+ && parmtype == TREE_VALUE (TREE_CHAIN (parmtypes)))
+ nrepeats++;
+ else
+ {
+ int tindex = 0;
+
+ while (typevec[tindex] != parmtype)
+ tindex++;
+ OB_PUTC ('T');
+ icat (tindex);
+ if (tindex > 9)
+ OB_PUTC ('_');
+ }
+ }
+ goto next;
+ }
+ if (nrepeats)
+ flush_repeats (typevec[maxtype-2]);
+ if (! just_one
+ /* Only cache types which take more than one character. */
+ && (parmtype != TYPE_MAIN_VARIANT (parmtype)
+ || (TREE_CODE (parmtype) != INTEGER_TYPE
+ && TREE_CODE (parmtype) != REAL_TYPE)))
+ TREE_USED (parmtype) = 1;
+ }
+
+ if (TYPE_PTRMEMFUNC_P (parmtype))
+ parmtype = TYPE_PTRMEMFUNC_FN_TYPE (parmtype);
+
+ if (TREE_READONLY (parmtype))
+ OB_PUTC ('C');
+ if (TREE_CODE (parmtype) == INTEGER_TYPE
+ && TYPE_MAIN_VARIANT (parmtype) == unsigned_type (TYPE_MAIN_VARIANT (parmtype)))
+ OB_PUTC ('U');
+ if (TYPE_VOLATILE (parmtype))
+ OB_PUTC ('V');
+
+ switch (TREE_CODE (parmtype))
+ {
+ case OFFSET_TYPE:
+ OB_PUTC ('O');
+ build_overload_name (TYPE_OFFSET_BASETYPE (parmtype), 0, 0);
+ OB_PUTC ('_');
+ build_overload_name (TREE_TYPE (parmtype), 0, 0);
+ break;
+
+ case REFERENCE_TYPE:
+ OB_PUTC ('R');
+ goto more;
+
+ case ARRAY_TYPE:
+#if PARM_CAN_BE_ARRAY_TYPE
+ {
+ tree length;
+
+ OB_PUTC ('A');
+ if (TYPE_DOMAIN (parmtype) == NULL_TREE)
+ error ("pointer or reference to array of unknown bound in parm type");
+ else
+ {
+ length = array_type_nelts (parmtype);
+ if (TREE_CODE (length) == INTEGER_CST)
+ icat (TREE_INT_CST_LOW (length) + 1);
+ }
+ OB_PUTC ('_');
+ goto more;
+ }
+#else
+ OB_PUTC ('P');
+ goto more;
+#endif
+
+ case POINTER_TYPE:
+ OB_PUTC ('P');
+ more:
+ build_overload_name (TREE_TYPE (parmtype), 0, 0);
+ break;
+
+ case FUNCTION_TYPE:
+ case METHOD_TYPE:
+ {
+ tree firstarg = TYPE_ARG_TYPES (parmtype);
+ /* Otherwise have to implement reentrant typevecs,
+ unmark and remark types, etc. */
+ int old_nofold = nofold;
+ nofold = 1;
+
+ if (nrepeats)
+ flush_repeats (typevec[maxtype-1]);
+
+ /* @@ It may be possible to pass a function type in
+ which is not preceded by a 'P'. */
+ if (TREE_CODE (parmtype) == FUNCTION_TYPE)
+ {
+ OB_PUTC ('F');
+ if (firstarg == NULL_TREE)
+ OB_PUTC ('e');
+ else if (firstarg == void_list_node)
+ OB_PUTC ('v');
+ else
+ build_overload_name (firstarg, 0, 0);
+ }
+ else
+ {
+ int constp = TYPE_READONLY (TREE_TYPE (TREE_VALUE (firstarg)));
+ int volatilep = TYPE_VOLATILE (TREE_TYPE (TREE_VALUE (firstarg)));
+ OB_PUTC ('M');
+ firstarg = TREE_CHAIN (firstarg);
+
+ build_overload_name (TYPE_METHOD_BASETYPE (parmtype), 0, 0);
+ if (constp)
+ OB_PUTC ('C');
+ if (volatilep)
+ OB_PUTC ('V');
+
+ /* For cfront 2.0 compatibility. */
+ OB_PUTC ('F');
+
+ if (firstarg == NULL_TREE)
+ OB_PUTC ('e');
+ else if (firstarg == void_list_node)
+ OB_PUTC ('v');
+ else
+ build_overload_name (firstarg, 0, 0);
+ }
+
+ /* Separate args from return type. */
+ OB_PUTC ('_');
+ build_overload_name (TREE_TYPE (parmtype), 0, 0);
+ nofold = old_nofold;
+ break;
+ }
+
+ case INTEGER_TYPE:
+ parmtype = TYPE_MAIN_VARIANT (parmtype);
+ if (parmtype == integer_type_node
+ || parmtype == unsigned_type_node)
+ OB_PUTC ('i');
+ else if (parmtype == long_integer_type_node
+ || parmtype == long_unsigned_type_node)
+ OB_PUTC ('l');
+ else if (parmtype == short_integer_type_node
+ || parmtype == short_unsigned_type_node)
+ OB_PUTC ('s');
+ else if (parmtype == signed_char_type_node)
+ {
+ OB_PUTC ('S');
+ OB_PUTC ('c');
+ }
+ else if (parmtype == char_type_node
+ || parmtype == unsigned_char_type_node)
+ OB_PUTC ('c');
+ else if (parmtype == wchar_type_node)
+ OB_PUTC ('w');
+ else if (parmtype == long_long_integer_type_node
+ || parmtype == long_long_unsigned_type_node)
+ OB_PUTC ('x');
+#if 0
+ /* it would seem there is no way to enter these in source code,
+ yet. (mrs) */
+ else if (parmtype == long_long_long_integer_type_node
+ || parmtype == long_long_long_unsigned_type_node)
+ OB_PUTC ('q');
+#endif
+ else
+ my_friendly_abort (73);
+ break;
+
+ case BOOLEAN_TYPE:
+ OB_PUTC ('b');
+ break;
+
+ case REAL_TYPE:
+ parmtype = TYPE_MAIN_VARIANT (parmtype);
+ if (parmtype == long_double_type_node)
+ OB_PUTC ('r');
+ else if (parmtype == double_type_node)
+ OB_PUTC ('d');
+ else if (parmtype == float_type_node)
+ OB_PUTC ('f');
+ else my_friendly_abort (74);
+ break;
+
+ case VOID_TYPE:
+ if (! just_one)
+ {
+#if 0
+ extern tree void_list_node;
+
+ /* See if anybody is wasting memory. */
+ my_friendly_assert (parmtypes == void_list_node, 247);
+#endif
+ /* This is the end of a parameter list. */
+ if (end) OB_FINISH ();
+ return (char *)obstack_base (&scratch_obstack);
+ }
+ OB_PUTC ('v');
+ break;
+
+ case ERROR_MARK: /* not right, but nothing is anyway */
+ break;
+
+ /* have to do these */
+ case UNION_TYPE:
+ case RECORD_TYPE:
+ if (! just_one)
+ /* Make this type signature look incompatible
+ with AT&T. */
+ OB_PUTC ('G');
+ goto common;
+ case ENUMERAL_TYPE:
+ common:
+ {
+ tree name = TYPE_NAME (parmtype);
+ int i = 1;
+
+ if (TREE_CODE (name) == TYPE_DECL)
+ {
+ tree context = name;
+ while (DECL_CONTEXT (context))
+ {
+ i += 1;
+ context = DECL_CONTEXT (context);
+ if (TREE_CODE_CLASS (TREE_CODE (context)) == 't')
+ context = TYPE_NAME (context);
+ }
+ name = DECL_NAME (name);
+ }
+ my_friendly_assert (TREE_CODE (name) == IDENTIFIER_NODE, 248);
+ if (i > 1)
+ {
+ OB_PUTC ('Q');
+ if (i > 9)
+ OB_PUTC ('_');
+ icat (i);
+ if (i > 9)
+ OB_PUTC ('_');
+ build_overload_nested_name (TYPE_NAME (parmtype));
+ }
+ else
+ build_overload_identifier (name);
+ break;
+ }
+
+ case UNKNOWN_TYPE:
+ /* This will take some work. */
+ OB_PUTC ('?');
+ break;
+
+ case TEMPLATE_TYPE_PARM:
+ case TEMPLATE_CONST_PARM:
+ case UNINSTANTIATED_P_TYPE:
+ /* We don't ever want this output, but it's inconvenient not to
+ be able to build the string. This should cause assembler
+ errors we'll notice. */
+ {
+ static int n;
+ sprintf (digit_buffer, " *%d", n++);
+ OB_PUTCP (digit_buffer);
+ }
+ break;
+
+ default:
+ my_friendly_abort (75);
+ }
+
+ next:
+ if (just_one) break;
+ parmtypes = TREE_CHAIN (parmtypes);
+ }
+ if (! just_one)
+ {
+ if (nrepeats)
+ flush_repeats (typevec[maxtype-1]);
+
+ /* To get here, parms must end with `...'. */
+ OB_PUTC ('e');
+ }
+
+ if (end) OB_FINISH ();
+ return (char *)obstack_base (&scratch_obstack);
+}
+
+/* Generate an identifier that encodes the (ANSI) exception TYPE. */
+
+/* This should be part of `ansi_opname', or at least be defined by the std. */
+#define EXCEPTION_NAME_PREFIX "__ex"
+#define EXCEPTION_NAME_LENGTH 4
+
+tree
+cplus_exception_name (type)
+ tree type;
+{
+ OB_INIT ();
+ OB_PUTS (EXCEPTION_NAME_PREFIX);
+ return get_identifier (build_overload_name (type, 0, 1));
+}
+
+/* Change the name of a function definition so that it may be
+ overloaded. NAME is the name of the function to overload,
+ PARMS is the parameter list (which determines what name the
+ final function obtains).
+
+ FOR_METHOD is 1 if this overload is being performed
+ for a method, rather than a function type. It is 2 if
+ this overload is being performed for a constructor. */
+tree
+build_decl_overload (dname, parms, for_method)
+ tree dname;
+ tree parms;
+ int for_method;
+{
+ char *name = IDENTIFIER_POINTER (dname);
+
+ /* member operators new and delete look like methods at this point. */
+ if (! for_method && parms != NULL_TREE && TREE_CODE (parms) == TREE_LIST)
+ {
+ if (TREE_VALUE (parms) == sizetype
+ && TREE_CHAIN (parms) == void_list_node)
+ {
+ if (dname == ansi_opname[(int) NEW_EXPR])
+ return get_identifier ("__builtin_new");
+ else if (dname == ansi_opname[(int) VEC_NEW_EXPR])
+ return get_identifier ("__builtin_vec_new");
+ }
+ else if (dname == ansi_opname[(int) DELETE_EXPR])
+ return get_identifier ("__builtin_delete");
+ else if (dname == ansi_opname[(int) VEC_DELETE_EXPR])
+ return get_identifier ("__builtin_vec_delete");
+ }
+
+ OB_INIT ();
+ if (for_method != 2)
+ OB_PUTCP (name);
+ /* Otherwise, we can divine that this is a constructor,
+ and figure out its name without any extra encoding. */
+
+ OB_PUTC2 ('_', '_');
+ if (for_method)
+ {
+#if 0
+ /* We can get away without doing this. */
+ OB_PUTC ('M');
+#endif
+ {
+ tree this_type = TREE_VALUE (parms);
+
+ if (TREE_CODE (this_type) == RECORD_TYPE) /* a signature pointer */
+ parms = temp_tree_cons (NULL_TREE, SIGNATURE_TYPE (this_type),
+ TREE_CHAIN (parms));
+ else
+ parms = temp_tree_cons (NULL_TREE, TREE_TYPE (this_type),
+ TREE_CHAIN (parms));
+ }
+ }
+ else
+ OB_PUTC ('F');
+
+ if (parms == NULL_TREE)
+ OB_PUTC2 ('e', '\0');
+ else if (parms == void_list_node)
+ OB_PUTC2 ('v', '\0');
+ else
+ {
+ ALLOCATE_TYPEVEC (parms);
+ nofold = 0;
+ if (for_method)
+ {
+ build_overload_name (TREE_VALUE (parms), 0, 0);
+
+ typevec[maxtype++] = TREE_VALUE (parms);
+ TREE_USED (TREE_VALUE (parms)) = 1;
+
+ if (TREE_CHAIN (parms))
+ build_overload_name (TREE_CHAIN (parms), 0, 1);
+ else
+ OB_PUTC2 ('e', '\0');
+ }
+ else
+ build_overload_name (parms, 0, 1);
+ DEALLOCATE_TYPEVEC (parms);
+ }
+ {
+ tree n = get_identifier (obstack_base (&scratch_obstack));
+ if (IDENTIFIER_OPNAME_P (dname))
+ IDENTIFIER_OPNAME_P (n) = 1;
+ return n;
+ }
+}
+
+/* Build an overload name for the type expression TYPE. */
+tree
+build_typename_overload (type)
+ tree type;
+{
+ tree id;
+
+ OB_INIT ();
+ OB_PUTID (ansi_opname[(int) TYPE_EXPR]);
+ nofold = 1;
+ build_overload_name (type, 0, 1);
+ id = get_identifier (obstack_base (&scratch_obstack));
+ IDENTIFIER_OPNAME_P (id) = 1;
+#if 0
+ IDENTIFIER_GLOBAL_VALUE (id) = TYPE_NAME (type);
+#endif
+ TREE_TYPE (id) = type;
+ return id;
+}
+
+#ifndef NO_DOLLAR_IN_LABEL
+#define T_DESC_FORMAT "TD$"
+#define I_DESC_FORMAT "ID$"
+#define M_DESC_FORMAT "MD$"
+#else
+#if !defined(NO_DOT_IN_LABEL)
+#define T_DESC_FORMAT "TD."
+#define I_DESC_FORMAT "ID."
+#define M_DESC_FORMAT "MD."
+#else
+#define T_DESC_FORMAT "__t_desc_"
+#define I_DESC_FORMAT "__i_desc_"
+#define M_DESC_FORMAT "__m_desc_"
+#endif
+#endif
+
+/* Build an overload name for the type expression TYPE. */
+tree
+build_t_desc_overload (type)
+ tree type;
+{
+ OB_INIT ();
+ OB_PUTS (T_DESC_FORMAT);
+ nofold = 1;
+
+#if 0
+ /* Use a different format if the type isn't defined yet. */
+ if (TYPE_SIZE (type) == NULL_TREE)
+ {
+ char *p;
+ int changed;
+
+ for (p = tname; *p; p++)
+ if (isupper (*p))
+ {
+ changed = 1;
+ *p = tolower (*p);
+ }
+ /* If there's no change, we have an inappropriate T_DESC_FORMAT. */
+ my_friendly_assert (changed != 0, 249);
+ }
+#endif
+
+ build_overload_name (type, 0, 1);
+ return get_identifier (obstack_base (&scratch_obstack));
+}
+
+/* Top-level interface to explicit overload requests. Allow NAME
+ to be overloaded. Error if NAME is already declared for the current
+ scope. Warning if function is redundantly overloaded. */
+
+void
+declare_overloaded (name)
+ tree name;
+{
+#ifdef NO_AUTO_OVERLOAD
+ if (is_overloaded (name))
+ warning ("function `%s' already declared overloaded",
+ IDENTIFIER_POINTER (name));
+ else if (IDENTIFIER_GLOBAL_VALUE (name))
+ error ("overloading function `%s' that is already defined",
+ IDENTIFIER_POINTER (name));
+ else
+ {
+ TREE_OVERLOADED (name) = 1;
+ IDENTIFIER_GLOBAL_VALUE (name) = build_tree_list (name, NULL_TREE);
+ TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (name)) = unknown_type_node;
+ }
+#else
+ if (current_lang_name == lang_name_cplusplus)
+ {
+ if (0)
+ warning ("functions are implicitly overloaded in C++");
+ }
+ else if (current_lang_name == lang_name_c)
+ error ("overloading function `%s' cannot be done in C language context");
+ else
+ my_friendly_abort (76);
+#endif
+}
+
+#ifdef NO_AUTO_OVERLOAD
+/* Check to see if NAME is overloaded. For first approximation,
+ check to see if its TREE_OVERLOADED is set. This is used on
+ IDENTIFIER nodes. */
+int
+is_overloaded (name)
+ tree name;
+{
+ /* @@ */
+ return (TREE_OVERLOADED (name)
+ && (! IDENTIFIER_CLASS_VALUE (name) || current_class_type == 0)
+ && ! IDENTIFIER_LOCAL_VALUE (name));
+}
+#endif
+
+/* Given a tree_code CODE, and some arguments (at least one),
+ attempt to use an overloaded operator on the arguments.
+
+ For unary operators, only the first argument need be checked.
+ For binary operators, both arguments may need to be checked.
+
+ Member functions can convert class references to class pointers,
+ for one-level deep indirection. More than that is not supported.
+ Operators [](), ()(), and ->() must be member functions.
+
+ We call function call building calls with LOOKUP_COMPLAIN if they
+ are our only hope. This is true when we see a vanilla operator
+ applied to something of aggregate type. If this fails, we are free
+ to return `error_mark_node', because we will have reported the
+ error.
+
+ Operators NEW and DELETE overload in funny ways: operator new takes
+ a single `size' parameter, and operator delete takes a pointer to the
+ storage being deleted. When overloading these operators, success is
+ assumed. If there is a failure, report an error message and return
+ `error_mark_node'. */
+
+/* NOSTRICT */
+tree
+build_opfncall (code, flags, xarg1, xarg2, arg3)
+ enum tree_code code;
+ int flags;
+ tree xarg1, xarg2, arg3;
+{
+ tree rval = 0;
+ tree arg1, arg2;
+ tree type1, type2, fnname;
+ tree fields1 = 0, parms = 0;
+ tree global_fn;
+ int try_second;
+ int binary_is_unary;
+
+ if (xarg1 == error_mark_node)
+ return error_mark_node;
+
+ if (code == COND_EXPR)
+ {
+ if (TREE_CODE (xarg2) == ERROR_MARK
+ || TREE_CODE (arg3) == ERROR_MARK)
+ return error_mark_node;
+ }
+ if (code == COMPONENT_REF)
+ if (TREE_CODE (TREE_TYPE (xarg1)) == POINTER_TYPE)
+ return rval;
+
+ /* First, see if we can work with the first argument */
+ type1 = TREE_TYPE (xarg1);
+
+ /* Some tree codes have length > 1, but we really only want to
+ overload them if their first argument has a user defined type. */
+ switch (code)
+ {
+ case PREINCREMENT_EXPR:
+ case PREDECREMENT_EXPR:
+ case POSTINCREMENT_EXPR:
+ case POSTDECREMENT_EXPR:
+ case COMPONENT_REF:
+ binary_is_unary = 1;
+ try_second = 0;
+ break;
+
+ /* ARRAY_REFs and CALL_EXPRs must overload successfully.
+ If they do not, return error_mark_node instead of NULL_TREE. */
+ case ARRAY_REF:
+ if (xarg2 == error_mark_node)
+ return error_mark_node;
+ case CALL_EXPR:
+ rval = error_mark_node;
+ binary_is_unary = 0;
+ try_second = 0;
+ break;
+
+ case VEC_NEW_EXPR:
+ case NEW_EXPR:
+ {
+ tree args = tree_cons (NULL_TREE, xarg2, arg3);
+ fnname = ansi_opname[(int) code];
+ if (flags & LOOKUP_GLOBAL)
+ return build_overload_call (fnname, args, flags & LOOKUP_COMPLAIN,
+ (struct candidate *)0);
+
+ rval = build_method_call
+ (build_indirect_ref (build1 (NOP_EXPR, xarg1, error_mark_node),
+ "new"),
+ fnname, args, NULL_TREE, flags);
+ if (rval == error_mark_node)
+ /* User might declare fancy operator new, but invoke it
+ like standard one. */
+ return rval;
+
+ TREE_TYPE (rval) = xarg1;
+ TREE_CALLS_NEW (rval) = 1;
+ return rval;
+ }
+ break;
+
+ case VEC_DELETE_EXPR:
+ case DELETE_EXPR:
+ {
+ fnname = ansi_opname[(int) code];
+ if (flags & LOOKUP_GLOBAL)
+ return build_overload_call (fnname,
+ build_tree_list (NULL_TREE, xarg1),
+ flags & LOOKUP_COMPLAIN,
+ (struct candidate *)0);
+
+ rval = build_method_call
+ (build_indirect_ref (build1 (NOP_EXPR, TREE_TYPE (xarg1),
+ error_mark_node),
+ NULL_PTR),
+ fnname, tree_cons (NULL_TREE, xarg1,
+ build_tree_list (NULL_TREE, xarg2)),
+ NULL_TREE, flags);
+ /* This happens when the user mis-declares `operator delete'.
+ Should now be impossible. */
+ my_friendly_assert (rval != error_mark_node, 250);
+ TREE_TYPE (rval) = void_type_node;
+ return rval;
+ }
+ break;
+
+ default:
+ binary_is_unary = 0;
+ try_second = tree_code_length [(int) code] == 2;
+ if (try_second && xarg2 == error_mark_node)
+ return error_mark_node;
+ break;
+ }
+
+ if (try_second && xarg2 == error_mark_node)
+ return error_mark_node;
+
+ /* What ever it was, we do not know how to deal with it. */
+ if (type1 == NULL_TREE)
+ return rval;
+
+ if (TREE_CODE (type1) == OFFSET_TYPE)
+ type1 = TREE_TYPE (type1);
+
+ if (TREE_CODE (type1) == REFERENCE_TYPE)
+ {
+ arg1 = convert_from_reference (xarg1);
+ type1 = TREE_TYPE (arg1);
+ }
+ else
+ {
+ arg1 = xarg1;
+ }
+
+ if (!IS_AGGR_TYPE (type1) || TYPE_PTRMEMFUNC_P (type1))
+ {
+ /* Try to fail. First, fail if unary */
+ if (! try_second)
+ return rval;
+ /* Second, see if second argument is non-aggregate. */
+ type2 = TREE_TYPE (xarg2);
+ if (TREE_CODE (type2) == OFFSET_TYPE)
+ type2 = TREE_TYPE (type2);
+ if (TREE_CODE (type2) == REFERENCE_TYPE)
+ {
+ arg2 = convert_from_reference (xarg2);
+ type2 = TREE_TYPE (arg2);
+ }
+ else
+ {
+ arg2 = xarg2;
+ }
+
+ if (!IS_AGGR_TYPE (type2))
+ return rval;
+ try_second = 0;
+ }
+
+ if (try_second)
+ {
+ /* First arg may succeed; see whether second should. */
+ type2 = TREE_TYPE (xarg2);
+ if (TREE_CODE (type2) == OFFSET_TYPE)
+ type2 = TREE_TYPE (type2);
+ if (TREE_CODE (type2) == REFERENCE_TYPE)
+ {
+ arg2 = convert_from_reference (xarg2);
+ type2 = TREE_TYPE (arg2);
+ }
+ else
+ {
+ arg2 = xarg2;
+ }
+
+ if (! IS_AGGR_TYPE (type2))
+ try_second = 0;
+ }
+
+ if (type1 == unknown_type_node
+ || (try_second && TREE_TYPE (xarg2) == unknown_type_node))
+ {
+ /* This will not be implemented in the foreseeable future. */
+ return rval;
+ }
+
+ if (code == MODIFY_EXPR)
+ fnname = ansi_assopname[(int) TREE_CODE (arg3)];
+ else
+ fnname = ansi_opname[(int) code];
+
+ global_fn = lookup_name_nonclass (fnname);
+
+ /* This is the last point where we will accept failure. This
+ may be too eager if we wish an overloaded operator not to match,
+ but would rather a normal operator be called on a type-converted
+ argument. */
+
+ if (IS_AGGR_TYPE (type1))
+ {
+ fields1 = lookup_fnfields (TYPE_BINFO (type1), fnname, 0);
+ /* ARM $13.4.7, prefix/postfix ++/--. */
+ if (code == POSTINCREMENT_EXPR || code == POSTDECREMENT_EXPR)
+ {
+ xarg2 = integer_zero_node;
+ binary_is_unary = 0;
+
+ if (fields1)
+ {
+ tree t, t2;
+ int have_postfix = 0;
+
+ /* Look for an `operator++ (int)'. If they didn't have
+ one, then we fall back to the old way of doing things. */
+ for (t = TREE_VALUE (fields1); t ; t = TREE_CHAIN (t))
+ {
+ t2 = TYPE_ARG_TYPES (TREE_TYPE (t));
+ if (TREE_CHAIN (t2) != NULL_TREE
+ && TREE_VALUE (TREE_CHAIN (t2)) == integer_type_node)
+ {
+ have_postfix = 1;
+ break;
+ }
+ }
+
+ if (! have_postfix)
+ {
+ char *op = POSTINCREMENT_EXPR ? "++" : "--";
+
+ /* There's probably a LOT of code in the world that
+ relies upon this old behavior. So we'll only give this
+ warning when we've been given -pedantic. A few
+ releases after 2.4, we'll convert this to be a pedwarn
+ or something else more appropriate. */
+ if (pedantic)
+ warning ("no `operator%s (int)' declared for postfix `%s'",
+ op, op);
+ xarg2 = NULL_TREE;
+ binary_is_unary = 1;
+ }
+ }
+ }
+ }
+
+ if (fields1 == NULL_TREE && global_fn == NULL_TREE)
+ return rval;
+
+ /* If RVAL winds up being `error_mark_node', we will return
+ that... There is no way that normal semantics of these
+ operators will succeed. */
+
+ /* This argument may be an uncommitted OFFSET_REF. This is
+ the case for example when dealing with static class members
+ which are referenced from their class name rather than
+ from a class instance. */
+ if (TREE_CODE (xarg1) == OFFSET_REF
+ && TREE_CODE (TREE_OPERAND (xarg1, 1)) == VAR_DECL)
+ xarg1 = TREE_OPERAND (xarg1, 1);
+ if (try_second && xarg2 && TREE_CODE (xarg2) == OFFSET_REF
+ && TREE_CODE (TREE_OPERAND (xarg2, 1)) == VAR_DECL)
+ xarg2 = TREE_OPERAND (xarg2, 1);
+
+ if (global_fn)
+ flags |= LOOKUP_GLOBAL;
+
+ if (code == CALL_EXPR)
+ {
+ /* This can only be a member function. */
+ return build_method_call (xarg1, fnname, xarg2,
+ NULL_TREE, LOOKUP_NORMAL);
+ }
+ else if (tree_code_length[(int) code] == 1 || binary_is_unary)
+ {
+ parms = NULL_TREE;
+ rval = build_method_call (xarg1, fnname, NULL_TREE, NULL_TREE, flags);
+ }
+ else if (code == COND_EXPR)
+ {
+ parms = tree_cons (0, xarg2, build_tree_list (NULL_TREE, arg3));
+ rval = build_method_call (xarg1, fnname, parms, NULL_TREE, flags);
+ }
+ else if (code == METHOD_CALL_EXPR)
+ {
+ /* must be a member function. */
+ parms = tree_cons (NULL_TREE, xarg2, arg3);
+ return build_method_call (xarg1, fnname, parms, NULL_TREE,
+ LOOKUP_NORMAL);
+ }
+ else if (fields1)
+ {
+ parms = build_tree_list (NULL_TREE, xarg2);
+ rval = build_method_call (xarg1, fnname, parms, NULL_TREE, flags);
+ }
+ else
+ {
+ parms = tree_cons (NULL_TREE, xarg1,
+ build_tree_list (NULL_TREE, xarg2));
+ rval = build_overload_call (fnname, parms, flags,
+ (struct candidate *)0);
+ }
+
+ return rval;
+}
+
+/* This function takes an identifier, ID, and attempts to figure out what
+ it means. There are a number of possible scenarios, presented in increasing
+ order of hair:
+
+ 1) not in a class's scope
+ 2) in class's scope, member name of the class's method
+ 3) in class's scope, but not a member name of the class
+ 4) in class's scope, member name of a class's variable
+
+ NAME is $1 from the bison rule. It is an IDENTIFIER_NODE.
+ VALUE is $$ from the bison rule. It is the value returned by lookup_name ($1)
+ yychar is the pending input character (suitably encoded :-).
+
+ As a last ditch, try to look up the name as a label and return that
+ address.
+
+ Values which are declared as being of REFERENCE_TYPE are
+ automatically dereferenced here (as a hack to make the
+ compiler faster). */
+
+tree
+hack_identifier (value, name, yychar)
+ tree value, name;
+ int yychar;
+{
+ tree type;
+
+ if (TREE_CODE (value) == ERROR_MARK)
+ {
+ if (current_class_name)
+ {
+ tree fields = lookup_fnfields (TYPE_BINFO (current_class_type), name, 1);
+ if (fields == error_mark_node)
+ return error_mark_node;
+ if (fields)
+ {
+ tree fndecl;
+
+ fndecl = TREE_VALUE (fields);
+ my_friendly_assert (TREE_CODE (fndecl) == FUNCTION_DECL, 251);
+ if (DECL_CHAIN (fndecl) == NULL_TREE)
+ {
+ warning ("methods cannot be converted to function pointers");
+ return fndecl;
+ }
+ else
+ {
+ error ("ambiguous request for method pointer `%s'",
+ IDENTIFIER_POINTER (name));
+ return error_mark_node;
+ }
+ }
+ }
+ if (flag_labels_ok && IDENTIFIER_LABEL_VALUE (name))
+ {
+ return IDENTIFIER_LABEL_VALUE (name);
+ }
+ return error_mark_node;
+ }
+
+ type = TREE_TYPE (value);
+ if (TREE_CODE (value) == FIELD_DECL)
+ {
+ if (current_class_decl == NULL_TREE)
+ {
+ error ("request for member `%s' in static member function",
+ IDENTIFIER_POINTER (DECL_NAME (value)));
+ return error_mark_node;
+ }
+ TREE_USED (current_class_decl) = 1;
+ if (yychar == '(')
+ if (! ((TYPE_LANG_SPECIFIC (type)
+ && TYPE_OVERLOADS_CALL_EXPR (type))
+ || (TREE_CODE (type) == REFERENCE_TYPE
+ && TYPE_LANG_SPECIFIC (TREE_TYPE (type))
+ && TYPE_OVERLOADS_CALL_EXPR (TREE_TYPE (type))))
+ && TREE_CODE (type) != FUNCTION_TYPE
+ && TREE_CODE (type) != METHOD_TYPE
+ && !TYPE_PTRMEMFUNC_P (type)
+ && (TREE_CODE (type) != POINTER_TYPE
+ || (TREE_CODE (TREE_TYPE (type)) != FUNCTION_TYPE
+ && TREE_CODE (TREE_TYPE (type)) != METHOD_TYPE)))
+ {
+ error ("component `%s' is not a method",
+ IDENTIFIER_POINTER (name));
+ return error_mark_node;
+ }
+ /* Mark so that if we are in a constructor, and then find that
+ this field was initialized by a base initializer,
+ we can emit an error message. */
+ TREE_USED (value) = 1;
+ return build_component_ref (C_C_D, name, 0, 1);
+ }
+
+ if (really_overloaded_fn (value))
+ {
+ tree t = get_first_fn (value);
+ while (t)
+ {
+ assemble_external (t);
+ TREE_USED (t) = 1;
+ t = DECL_CHAIN (t);
+ }
+ }
+ else if (TREE_CODE (value) == TREE_LIST)
+ {
+ tree t = value;
+ while (t && TREE_CODE (t) == TREE_LIST)
+ {
+ assemble_external (TREE_VALUE (t));
+ TREE_USED (t) = 1;
+ t = TREE_CHAIN (t);
+ }
+ }
+ else
+ {
+ assemble_external (value);
+ TREE_USED (value) = 1;
+ }
+
+ if (TREE_CODE_CLASS (TREE_CODE (value)) == 'd' && DECL_NONLOCAL (value))
+ {
+ if (DECL_LANG_SPECIFIC (value)
+ && DECL_CLASS_CONTEXT (value) != current_class_type)
+ {
+ tree path;
+ enum access_type access;
+ register tree context
+ = (TREE_CODE (value) == FUNCTION_DECL && DECL_VIRTUAL_P (value))
+ ? DECL_CLASS_CONTEXT (value)
+ : DECL_CONTEXT (value);
+
+ get_base_distance (context, current_class_type, 0, &path);
+ if (path)
+ {
+ access = compute_access (path, value);
+ if (access != access_public)
+ {
+ if (TREE_CODE (value) == VAR_DECL)
+ error ("static member `%s' is %s",
+ IDENTIFIER_POINTER (name),
+ TREE_PRIVATE (value) ? "private" :
+ "from a private base class");
+ else
+ error ("enum `%s' is from private base class",
+ IDENTIFIER_POINTER (name));
+ return error_mark_node;
+ }
+ }
+ }
+ return value;
+ }
+ if (TREE_CODE (value) == TREE_LIST && TREE_NONLOCAL_FLAG (value))
+ {
+ if (type == 0)
+ {
+ error ("request for member `%s' is ambiguous in multiple inheritance lattice",
+ IDENTIFIER_POINTER (name));
+ return error_mark_node;
+ }
+
+ return value;
+ }
+
+ if (TREE_CODE (type) == REFERENCE_TYPE)
+ {
+ my_friendly_assert (TREE_CODE (value) == VAR_DECL
+ || TREE_CODE (value) == PARM_DECL
+ || TREE_CODE (value) == RESULT_DECL, 252);
+ if (DECL_REFERENCE_SLOT (value))
+ return DECL_REFERENCE_SLOT (value);
+ }
+ return value;
+}
+
+
+#if 0
+/* Given an object OF, and a type conversion operator COMPONENT
+ build a call to the conversion operator, if a call is requested,
+ or return the address (as a pointer to member function) if one is not.
+
+ OF can be a TYPE_DECL or any kind of datum that would normally
+ be passed to `build_component_ref'. It may also be NULL_TREE,
+ in which case `current_class_type' and `current_class_decl'
+ provide default values.
+
+ BASETYPE_PATH, if non-null, is the path of basetypes
+ to go through before we get the the instance of interest.
+
+ PROTECT says whether we apply C++ scoping rules or not. */
+tree
+build_component_type_expr (of, component, basetype_path, protect)
+ tree of, component, basetype_path;
+ int protect;
+{
+ tree cname = NULL_TREE;
+ tree tmp, last;
+ tree name;
+ int flags = protect ? LOOKUP_NORMAL : LOOKUP_COMPLAIN;
+
+ if (of)
+ my_friendly_assert (IS_AGGR_TYPE (TREE_TYPE (of)), 253);
+ my_friendly_assert (TREE_CODE (component) == TYPE_EXPR, 254);
+
+ tmp = TREE_OPERAND (component, 0);
+ last = NULL_TREE;
+
+ while (tmp)
+ {
+ switch (TREE_CODE (tmp))
+ {
+ case CALL_EXPR:
+ if (last)
+ TREE_OPERAND (last, 0) = TREE_OPERAND (tmp, 0);
+ else
+ TREE_OPERAND (component, 0) = TREE_OPERAND (tmp, 0);
+
+ last = groktypename (build_tree_list (TREE_TYPE (component),
+ TREE_OPERAND (component, 0)));
+ name = build_typename_overload (last);
+ TREE_TYPE (name) = last;
+
+ if (TREE_OPERAND (tmp, 0)
+ && TREE_OPERAND (tmp, 0) != void_list_node)
+ {
+ cp_error ("`operator %T' requires empty parameter list", last);
+ TREE_OPERAND (tmp, 0) = NULL_TREE;
+ }
+
+ if (of && TREE_CODE (of) != TYPE_DECL)
+ return build_method_call (of, name, NULL_TREE, NULL_TREE, flags);
+ else if (of)
+ {
+ tree this_this;
+
+ if (current_class_decl == NULL_TREE)
+ {
+ cp_error ("object required for `operator %T' call",
+ TREE_TYPE (name));
+ return error_mark_node;
+ }
+
+ this_this = convert_pointer_to (TREE_TYPE (of),
+ current_class_decl);
+ this_this = build_indirect_ref (this_this, NULL_PTR);
+ return build_method_call (this_this, name, NULL_TREE,
+ NULL_TREE, flags | LOOKUP_NONVIRTUAL);
+ }
+ else if (current_class_decl)
+ return build_method_call (tmp, name, NULL_TREE, NULL_TREE, flags);
+
+ cp_error ("object required for `operator %T' call",
+ TREE_TYPE (name));
+ return error_mark_node;
+
+ case INDIRECT_REF:
+ case ADDR_EXPR:
+ case ARRAY_REF:
+ break;
+
+ case SCOPE_REF:
+ my_friendly_assert (cname == 0, 255);
+ cname = TREE_OPERAND (tmp, 0);
+ tmp = TREE_OPERAND (tmp, 1);
+ break;
+
+ default:
+ my_friendly_abort (77);
+ }
+ last = tmp;
+ tmp = TREE_OPERAND (tmp, 0);
+ }
+
+ last = groktypename (build_tree_list (TREE_TYPE (component), TREE_OPERAND (component, 0)));
+ name = build_typename_overload (last);
+ TREE_TYPE (name) = last;
+ if (of && TREE_CODE (of) == TYPE_DECL)
+ {
+ if (cname == NULL_TREE)
+ {
+ cname = DECL_NAME (of);
+ of = NULL_TREE;
+ }
+ else my_friendly_assert (cname == DECL_NAME (of), 256);
+ }
+
+ if (of)
+ {
+ tree this_this;
+
+ if (current_class_decl == NULL_TREE)
+ {
+ cp_error ("object required for `operator %T' call",
+ TREE_TYPE (name));
+ return error_mark_node;
+ }
+
+ this_this = convert_pointer_to (TREE_TYPE (of), current_class_decl);
+ return build_component_ref (this_this, name, 0, protect);
+ }
+ else if (cname)
+ return build_offset_ref (cname, name);
+ else if (current_class_name)
+ return build_offset_ref (current_class_name, name);
+
+ cp_error ("object required for `operator %T' member reference",
+ TREE_TYPE (name));
+ return error_mark_node;
+}
+#endif
+
+static char *
+thunk_printable_name (decl)
+ tree decl;
+{
+ return "<thunk function>";
+}
+
+tree
+make_thunk (function, delta)
+ tree function;
+ int delta;
+{
+ char buffer[250];
+ tree thunk_fndecl, thunk_id;
+ tree thunk;
+ char *func_name;
+ static int thunk_number = 0;
+ tree func_decl;
+ if (TREE_CODE (function) != ADDR_EXPR)
+ abort ();
+ func_decl = TREE_OPERAND (function, 0);
+ if (TREE_CODE (func_decl) != FUNCTION_DECL)
+ abort ();
+ func_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (func_decl));
+ sprintf (buffer, "__thunk_%d_%s", -delta, func_name);
+ thunk_id = get_identifier (buffer);
+ thunk = IDENTIFIER_GLOBAL_VALUE (thunk_id);
+ if (thunk && TREE_CODE (thunk) != THUNK_DECL)
+ {
+ error_with_decl ("implementation-reserved name `%s' used");
+ IDENTIFIER_GLOBAL_VALUE (thunk_id) = thunk = NULL_TREE;
+ }
+ if (thunk == NULL_TREE)
+ {
+ thunk = build_decl (THUNK_DECL, thunk_id, TREE_TYPE (func_decl));
+ DECL_RESULT (thunk)
+ = build_decl (RESULT_DECL, NULL_TREE, TREE_TYPE (vtable_entry_type));
+ make_function_rtl (thunk);
+ DECL_INITIAL (thunk) = function;
+ THUNK_DELTA (thunk) = delta;
+ /* So that finish_file can write out any thunks that need to be: */
+ pushdecl_top_level (thunk);
+ }
+ return thunk;
+}
+
+void
+emit_thunk (thunk_fndecl)
+ tree thunk_fndecl;
+{
+ rtx insns;
+ char *fnname;
+ char buffer[250];
+ tree argp;
+ struct args_size stack_args_size;
+ tree function = TREE_OPERAND (DECL_INITIAL (thunk_fndecl), 0);
+ int delta = THUNK_DELTA (thunk_fndecl);
+ int tem;
+ int failure = 0;
+ int current_call_is_indirect = 0; /* needed for HPPA FUNCTION_ARG */
+
+ /* Used to remember which regs we need to emit a USE rtx for. */
+ rtx need_use[FIRST_PSEUDO_REGISTER];
+ int need_use_count = 0;
+
+ /* rtx for the 'this' parameter. */
+ rtx this_rtx = 0, this_reg_rtx = 0, fixed_this_rtx;
+
+ char *(*save_decl_printable_name) () = decl_printable_name;
+ /* Data on reg parms scanned so far. */
+ CUMULATIVE_ARGS args_so_far;
+
+ if (TREE_ASM_WRITTEN (thunk_fndecl))
+ return;
+
+ TREE_ASM_WRITTEN (thunk_fndecl) = 1;
+
+ if (TREE_PUBLIC (function))
+ {
+ TREE_PUBLIC (thunk_fndecl) = 1;
+ if (DECL_EXTERNAL (function))
+ {
+ DECL_EXTERNAL (thunk_fndecl) = 1;
+ assemble_external (thunk_fndecl);
+ return;
+ }
+ }
+
+ decl_printable_name = thunk_printable_name;
+ if (current_function_decl)
+ abort ();
+ current_function_decl = thunk_fndecl;
+ init_function_start (thunk_fndecl, input_filename, lineno);
+ pushlevel (0);
+ expand_start_bindings (1);
+
+ /* Start updating where the next arg would go. */
+ INIT_CUMULATIVE_ARGS (args_so_far, TREE_TYPE (function), NULL_RTX);
+ stack_args_size.constant = 0;
+ stack_args_size.var = 0;
+ /* SETUP for possible structure return address FIXME */
+
+ /* Now look through all the parameters, make sure that we
+ don't clobber any registers used for parameters.
+ Also, pick up an rtx for the first "this" parameter. */
+ for (argp = TYPE_ARG_TYPES (TREE_TYPE (function));
+ argp != NULL_TREE;
+ argp = TREE_CHAIN (argp))
+
+ {
+ tree passed_type = TREE_VALUE (argp);
+ register rtx entry_parm;
+ int named = 1; /* FIXME */
+ struct args_size stack_offset;
+ struct args_size arg_size;
+
+ if (passed_type == void_type_node)
+ break;
+
+ if ((TREE_CODE (TYPE_SIZE (passed_type)) != INTEGER_CST
+ && contains_placeholder_p (TYPE_SIZE (passed_type)))
+#ifdef FUNCTION_ARG_PASS_BY_REFERENCE
+ || FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far,
+ TYPE_MODE (passed_type),
+ passed_type, named)
+#endif
+ )
+ passed_type = build_pointer_type (passed_type);
+
+ entry_parm = FUNCTION_ARG (args_so_far,
+ TYPE_MODE (passed_type),
+ passed_type,
+ named);
+ if (entry_parm != 0)
+ need_use[need_use_count++] = entry_parm;
+
+ locate_and_pad_parm (TYPE_MODE (passed_type), passed_type,
+#ifdef STACK_PARMS_IN_REG_PARM_AREA
+ 1,
+#else
+ entry_parm != 0,
+#endif
+ thunk_fndecl,
+ &stack_args_size, &stack_offset, &arg_size);
+
+/* REGNO (entry_parm);*/
+ if (this_rtx == 0)
+ {
+ this_reg_rtx = entry_parm;
+ if (!entry_parm)
+ {
+ rtx offset_rtx = ARGS_SIZE_RTX (stack_offset);
+
+ rtx internal_arg_pointer, stack_parm;
+
+ if ((ARG_POINTER_REGNUM == STACK_POINTER_REGNUM
+ || ! (fixed_regs[ARG_POINTER_REGNUM]
+ || ARG_POINTER_REGNUM == FRAME_POINTER_REGNUM)))
+ internal_arg_pointer = copy_to_reg (virtual_incoming_args_rtx);
+ else
+ internal_arg_pointer = virtual_incoming_args_rtx;
+
+ if (offset_rtx == const0_rtx)
+ entry_parm = gen_rtx (MEM, TYPE_MODE (passed_type),
+ internal_arg_pointer);
+ else
+ entry_parm = gen_rtx (MEM, TYPE_MODE (passed_type),
+ gen_rtx (PLUS, Pmode,
+ internal_arg_pointer,
+ offset_rtx));
+ }
+
+ this_rtx = entry_parm;
+ }
+
+ FUNCTION_ARG_ADVANCE (args_so_far,
+ TYPE_MODE (passed_type),
+ passed_type,
+ named);
+ }
+
+ fixed_this_rtx = plus_constant (this_rtx, delta);
+ if (this_rtx != fixed_this_rtx)
+ emit_move_insn (this_rtx, fixed_this_rtx);
+
+ if (this_reg_rtx)
+ emit_insn (gen_rtx (USE, VOIDmode, this_reg_rtx));
+
+ emit_indirect_jump (XEXP (DECL_RTL (function), 0));
+
+ while (need_use_count > 0)
+ emit_insn (gen_rtx (USE, VOIDmode, need_use[--need_use_count]));
+
+ expand_end_bindings (NULL, 1, 0);
+ poplevel (0, 0, 0);
+
+ /* From now on, allocate rtl in current_obstack, not in saveable_obstack.
+ Note that that may have been done above, in save_for_inline_copying.
+ The call to resume_temporary_allocation near the end of this function
+ goes back to the usual state of affairs. */
+
+ rtl_in_current_obstack ();
+
+ insns = get_insns ();
+
+ /* Copy any shared structure that should not be shared. */
+
+ unshare_all_rtl (insns);
+
+ /* We are no longer anticipating cse in this function, at least. */
+
+ cse_not_expected = 1;
+
+ /* Now we choose between stupid (pcc-like) register allocation
+ (if we got the -noreg switch and not -opt)
+ and smart register allocation. */
+
+ if (optimize > 0) /* Stupid allocation probably won't work */
+ obey_regdecls = 0; /* if optimizations being done. */
+
+ regclass_init ();
+
+ regclass (insns, max_reg_num ());
+ if (obey_regdecls)
+ {
+ stupid_life_analysis (insns, max_reg_num (), NULL);
+ failure = reload (insns, 0, NULL);
+ }
+ else
+ {
+ /* Do control and data flow analysis,
+ and write some of the results to dump file. */
+
+ flow_analysis (insns, max_reg_num (), NULL);
+ local_alloc ();
+ failure = global_alloc (NULL);
+ }
+
+ reload_completed = 1;
+
+#ifdef LEAF_REGISTERS
+ leaf_function = 0;
+ if (optimize > 0 && only_leaf_regs_used () && leaf_function_p ())
+ leaf_function = 1;
+#endif
+
+ /* If a machine dependent reorganization is needed, call it. */
+#ifdef MACHINE_DEPENDENT_REORG
+ MACHINE_DEPENDENT_REORG (insns);
+#endif
+
+ /* Now turn the rtl into assembler code. */
+
+ {
+ char *fnname = XSTR (XEXP (DECL_RTL (thunk_fndecl), 0), 0);
+ assemble_start_function (thunk_fndecl, fnname);
+ final (insns, asm_out_file, optimize, 0);
+ assemble_end_function (thunk_fndecl, fnname);
+ };
+
+ exit_rest_of_compilation:
+
+ reload_completed = 0;
+
+ /* Cancel the effect of rtl_in_current_obstack. */
+
+ resume_temporary_allocation ();
+
+ decl_printable_name = save_decl_printable_name;
+ current_function_decl = 0;
+}
diff --git a/gnu/usr.bin/cc/cc1plus/parse.c b/gnu/usr.bin/cc/cc1plus/parse.c
new file mode 100644
index 0000000..fb8c2e15
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/parse.c
@@ -0,0 +1,7604 @@
+
+/* A Bison parser, made from parse.y with Bison version GNU Bison version 1.22
+ */
+
+#define YYBISON 1 /* Identify Bison output. */
+
+#define IDENTIFIER 258
+#define TYPENAME 259
+#define SCSPEC 260
+#define TYPESPEC 261
+#define TYPE_QUAL 262
+#define CONSTANT 263
+#define STRING 264
+#define ELLIPSIS 265
+#define SIZEOF 266
+#define ENUM 267
+#define IF 268
+#define ELSE 269
+#define WHILE 270
+#define DO 271
+#define FOR 272
+#define SWITCH 273
+#define CASE 274
+#define DEFAULT 275
+#define BREAK 276
+#define CONTINUE 277
+#define RETURN 278
+#define GOTO 279
+#define ASM_KEYWORD 280
+#define GCC_ASM_KEYWORD 281
+#define TYPEOF 282
+#define ALIGNOF 283
+#define HEADOF 284
+#define CLASSOF 285
+#define SIGOF 286
+#define ATTRIBUTE 287
+#define EXTENSION 288
+#define LABEL 289
+#define AGGR 290
+#define VISSPEC 291
+#define DELETE 292
+#define NEW 293
+#define OVERLOAD 294
+#define THIS 295
+#define OPERATOR 296
+#define CXX_TRUE 297
+#define CXX_FALSE 298
+#define LEFT_RIGHT 299
+#define TEMPLATE 300
+#define TYPEID 301
+#define DYNAMIC_CAST 302
+#define STATIC_CAST 303
+#define REINTERPRET_CAST 304
+#define CONST_CAST 305
+#define SCOPE 306
+#define EMPTY 307
+#define PTYPENAME 308
+#define ASSIGN 309
+#define OROR 310
+#define ANDAND 311
+#define MIN_MAX 312
+#define EQCOMPARE 313
+#define ARITHCOMPARE 314
+#define LSHIFT 315
+#define RSHIFT 316
+#define POINTSAT_STAR 317
+#define DOT_STAR 318
+#define UNARY 319
+#define PLUSPLUS 320
+#define MINUSMINUS 321
+#define HYPERUNARY 322
+#define PAREN_STAR_PAREN 323
+#define POINTSAT 324
+#define TRY 325
+#define CATCH 326
+#define THROW 327
+#define TYPENAME_ELLIPSIS 328
+#define PRE_PARSED_FUNCTION_DECL 329
+#define EXTERN_LANG_STRING 330
+#define ALL 331
+#define PRE_PARSED_CLASS_DECL 332
+#define TYPENAME_DEFN 333
+#define IDENTIFIER_DEFN 334
+#define PTYPENAME_DEFN 335
+#define END_OF_SAVED_INPUT 336
+
+#line 42 "parse.y"
+
+/* Cause the `yydebug' variable to be defined. */
+#define YYDEBUG 1
+
+#include "config.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "tree.h"
+#include "input.h"
+#include "flags.h"
+#include "lex.h"
+#include "cp-tree.h"
+
+/* Since parsers are distinct for each language, put the language string
+ definition here. (fnf) */
+char *language_string = "GNU C++";
+
+extern tree void_list_node;
+extern struct obstack permanent_obstack;
+
+#ifndef errno
+extern int errno;
+#endif
+
+extern int end_of_file;
+extern int current_class_depth;
+
+void yyerror ();
+
+/* Like YYERROR but do call yyerror. */
+#define YYERROR1 { yyerror ("syntax error"); YYERROR; }
+
+#define OP0(NODE) (TREE_OPERAND (NODE, 0))
+#define OP1(NODE) (TREE_OPERAND (NODE, 1))
+
+/* Contains the statement keyword (if/while/do) to include in an
+ error message if the user supplies an empty conditional expression. */
+static char *cond_stmt_keyword;
+
+/* Nonzero if we have an `extern "C"' acting as an extern specifier. */
+int have_extern_spec;
+int used_extern_spec;
+
+void yyhook ();
+
+/* Cons up an empty parameter list. */
+#ifdef __GNUC__
+__inline
+#endif
+static tree
+empty_parms ()
+{
+ tree parms;
+
+ if (strict_prototype)
+ parms = void_list_node;
+ else
+ parms = NULL_TREE;
+ return parms;
+}
+
+#line 108 "parse.y"
+typedef union {long itype; tree ttype; char *strtype; enum tree_code code; } YYSTYPE;
+#line 276 "parse.y"
+
+/* List of types and structure classes of the current declaration. */
+static tree current_declspecs;
+
+/* When defining an aggregate, this is the most recent one being defined. */
+static tree current_aggr;
+
+/* Tell yyparse how to print a token's value, if yydebug is set. */
+
+#define YYPRINT(FILE,YYCHAR,YYLVAL) yyprint(FILE,YYCHAR,YYLVAL)
+extern void yyprint ();
+extern tree combine_strings PROTO((tree));
+
+#ifndef YYLTYPE
+typedef
+ struct yyltype
+ {
+ int timestamp;
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+ char *text;
+ }
+ yyltype;
+
+#define YYLTYPE yyltype
+#endif
+
+#include <stdio.h>
+
+#ifndef __cplusplus
+#ifndef __STDC__
+#define const
+#endif
+#endif
+
+
+
+#define YYFINAL 1346
+#define YYFLAG -32768
+#define YYNTBASE 106
+
+#define YYTRANSLATE(x) ((unsigned)(x) <= 336 ? yytranslate[x] : 336)
+
+static const char yytranslate[] = { 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 104, 2, 2, 2, 77, 65, 2, 88,
+ 102, 75, 73, 55, 74, 87, 76, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 60, 56, 69,
+ 58, 70, 59, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 89, 2, 105, 64, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 54, 63, 103, 83, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 1, 2, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 53, 57, 61,
+ 62, 66, 67, 68, 71, 72, 78, 79, 80, 81,
+ 82, 84, 85, 86, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101
+};
+
+#if YYDEBUG != 0
+static const short yyprhs[] = { 0,
+ 0, 1, 3, 4, 7, 10, 11, 12, 14, 16,
+ 17, 20, 22, 24, 26, 28, 34, 39, 43, 48,
+ 53, 55, 56, 62, 64, 68, 71, 76, 78, 82,
+ 84, 88, 89, 95, 96, 102, 103, 109, 110, 116,
+ 120, 124, 131, 139, 144, 148, 152, 154, 156, 158,
+ 160, 162, 165, 169, 173, 177, 181, 184, 187, 190,
+ 193, 196, 198, 202, 207, 211, 217, 222, 226, 230,
+ 233, 237, 241, 244, 246, 253, 258, 262, 266, 269,
+ 272, 274, 278, 283, 286, 290, 291, 292, 294, 298,
+ 301, 305, 307, 312, 315, 320, 323, 328, 331, 333,
+ 335, 337, 339, 341, 343, 345, 347, 351, 355, 360,
+ 365, 369, 374, 379, 380, 382, 386, 388, 390, 391,
+ 398, 399, 401, 402, 405, 407, 409, 411, 413, 415,
+ 417, 419, 421, 425, 427, 431, 432, 434, 436, 437,
+ 446, 448, 451, 456, 461, 463, 467, 471, 475, 479,
+ 481, 483, 485, 486, 490, 493, 496, 499, 502, 505,
+ 508, 513, 516, 521, 524, 528, 532, 537, 542, 548,
+ 554, 561, 564, 569, 575, 579, 583, 587, 589, 593,
+ 596, 600, 605, 607, 610, 616, 618, 623, 628, 633,
+ 635, 639, 643, 647, 651, 655, 659, 663, 667, 671,
+ 675, 679, 683, 687, 691, 695, 699, 703, 707, 711,
+ 717, 721, 725, 727, 730, 734, 736, 738, 740, 742,
+ 744, 746, 748, 751, 754, 756, 758, 760, 762, 764,
+ 766, 768, 772, 776, 777, 782, 783, 790, 793, 798,
+ 801, 804, 806, 811, 813, 821, 829, 837, 845, 850,
+ 855, 858, 861, 863, 868, 871, 874, 877, 883, 887,
+ 893, 897, 902, 909, 911, 914, 916, 919, 921, 923,
+ 925, 928, 929, 932, 935, 939, 943, 947, 951, 955,
+ 958, 961, 963, 965, 967, 970, 973, 976, 979, 981,
+ 983, 985, 987, 990, 993, 997, 1001, 1006, 1008, 1011,
+ 1014, 1016, 1018, 1021, 1024, 1026, 1029, 1032, 1036, 1038,
+ 1041, 1043, 1045, 1047, 1052, 1057, 1062, 1067, 1069, 1071,
+ 1073, 1075, 1079, 1081, 1085, 1087, 1091, 1092, 1097, 1098,
+ 1106, 1111, 1112, 1120, 1125, 1126, 1134, 1139, 1140, 1148,
+ 1153, 1154, 1156, 1158, 1161, 1168, 1170, 1174, 1175, 1177,
+ 1182, 1189, 1194, 1196, 1198, 1200, 1202, 1204, 1208, 1210,
+ 1213, 1217, 1222, 1224, 1226, 1230, 1235, 1242, 1246, 1252,
+ 1253, 1261, 1266, 1267, 1274, 1278, 1281, 1284, 1289, 1291,
+ 1292, 1294, 1295, 1297, 1299, 1302, 1305, 1308, 1311, 1315,
+ 1318, 1321, 1324, 1328, 1332, 1334, 1337, 1338, 1339, 1343,
+ 1347, 1350, 1352, 1354, 1355, 1357, 1360, 1362, 1366, 1368,
+ 1371, 1373, 1378, 1383, 1385, 1387, 1390, 1393, 1395, 1396,
+ 1398, 1403, 1407, 1409, 1412, 1415, 1418, 1421, 1424, 1427,
+ 1430, 1433, 1438, 1441, 1443, 1449, 1453, 1454, 1456, 1460,
+ 1461, 1463, 1467, 1469, 1471, 1473, 1475, 1480, 1487, 1492,
+ 1497, 1504, 1509, 1513, 1518, 1525, 1530, 1535, 1542, 1547,
+ 1551, 1553, 1557, 1559, 1563, 1566, 1568, 1575, 1576, 1579,
+ 1581, 1584, 1585, 1588, 1592, 1596, 1599, 1602, 1606, 1608,
+ 1610, 1612, 1615, 1621, 1627, 1631, 1637, 1642, 1646, 1650,
+ 1653, 1655, 1659, 1663, 1666, 1669, 1673, 1675, 1679, 1683,
+ 1686, 1689, 1693, 1695, 1701, 1707, 1711, 1717, 1721, 1725,
+ 1730, 1734, 1737, 1740, 1742, 1745, 1750, 1755, 1758, 1760,
+ 1762, 1764, 1767, 1770, 1773, 1775, 1778, 1780, 1783, 1786,
+ 1790, 1792, 1796, 1799, 1803, 1806, 1809, 1813, 1815, 1819,
+ 1824, 1828, 1831, 1834, 1836, 1840, 1843, 1846, 1848, 1851,
+ 1855, 1857, 1861, 1863, 1869, 1873, 1878, 1882, 1887, 1890,
+ 1893, 1897, 1900, 1902, 1904, 1907, 1910, 1913, 1914, 1915,
+ 1917, 1919, 1922, 1926, 1928, 1931, 1935, 1941, 1948, 1954,
+ 1955, 1956, 1963, 1965, 1968, 1970, 1972, 1974, 1977, 1978,
+ 1983, 1985, 1986, 1987, 1994, 1995, 1996, 2004, 2005, 2006,
+ 2007, 2018, 2019, 2020, 2021, 2032, 2033, 2041, 2042, 2048,
+ 2049, 2057, 2058, 2063, 2066, 2069, 2072, 2076, 2083, 2092,
+ 2103, 2116, 2121, 2125, 2128, 2131, 2133, 2135, 2136, 2137,
+ 2145, 2147, 2150, 2153, 2154, 2155, 2161, 2163, 2165, 2169,
+ 2173, 2176, 2179, 2183, 2188, 2193, 2197, 2202, 2209, 2216,
+ 2217, 2219, 2220, 2222, 2224, 2225, 2227, 2229, 2233, 2238,
+ 2240, 2244, 2245, 2247, 2249, 2251, 2254, 2257, 2260, 2262,
+ 2264, 2267, 2270, 2273, 2276, 2278, 2282, 2285, 2290, 2293,
+ 2298, 2301, 2304, 2307, 2310, 2313, 2316, 2318, 2321, 2323,
+ 2325, 2326, 2327, 2329, 2330, 2335, 2337, 2339, 2343, 2344,
+ 2348, 2352, 2356, 2358, 2361, 2364, 2367, 2370, 2373, 2376,
+ 2379, 2382, 2385, 2388, 2391, 2394, 2397, 2400, 2403, 2406,
+ 2409, 2412, 2415, 2418, 2421, 2424, 2427, 2431, 2434, 2437,
+ 2440, 2443, 2447, 2450, 2453, 2458, 2463, 2467
+};
+
+static const short yyrhs[] = { -1,
+ 107, 0, 0, 108, 112, 0, 107, 112, 0, 0,
+ 0, 25, 0, 26, 0, 0, 113, 114, 0, 129,
+ 0, 128, 0, 122, 0, 120, 0, 111, 88, 179,
+ 102, 56, 0, 115, 54, 107, 103, 0, 115, 54,
+ 103, 0, 115, 109, 129, 110, 0, 115, 109, 128,
+ 110, 0, 95, 0, 0, 45, 69, 117, 118, 70,
+ 0, 119, 0, 118, 55, 119, 0, 220, 138, 0,
+ 220, 140, 60, 232, 0, 327, 0, 39, 121, 56,
+ 0, 3, 0, 121, 55, 3, 0, 0, 116, 222,
+ 54, 123, 56, 0, 0, 116, 223, 54, 124, 56,
+ 0, 0, 116, 222, 60, 125, 56, 0, 0, 116,
+ 223, 60, 126, 56, 0, 116, 222, 56, 0, 116,
+ 223, 56, 0, 116, 258, 330, 197, 206, 127, 0,
+ 116, 186, 183, 330, 197, 206, 127, 0, 116, 189,
+ 258, 127, 0, 116, 1, 103, 0, 116, 1, 56,
+ 0, 54, 0, 60, 0, 56, 0, 58, 0, 23,
+ 0, 196, 56, 0, 189, 195, 56, 0, 189, 258,
+ 56, 0, 186, 194, 56, 0, 186, 183, 56, 0,
+ 189, 56, 0, 141, 56, 0, 186, 56, 0, 1,
+ 56, 0, 1, 103, 0, 56, 0, 130, 134, 282,
+ 0, 130, 133, 134, 282, 0, 130, 180, 282, 0,
+ 130, 133, 56, 180, 282, 0, 130, 133, 180, 282,
+ 0, 186, 183, 1, 0, 189, 258, 1, 0, 258,
+ 1, 0, 186, 183, 330, 0, 189, 258, 330, 0,
+ 258, 330, 0, 94, 0, 186, 88, 322, 102, 250,
+ 330, 0, 186, 44, 250, 330, 0, 186, 183, 330,
+ 0, 189, 258, 330, 0, 258, 330, 0, 23, 3,
+ 0, 132, 0, 132, 58, 213, 0, 132, 88, 160,
+ 102, 0, 132, 44, 0, 60, 135, 136, 0, 0,
+ 0, 137, 0, 136, 55, 137, 0, 136, 1, 0,
+ 88, 160, 102, 0, 44, 0, 139, 88, 160, 102,
+ 0, 139, 44, 0, 268, 88, 160, 102, 0, 268,
+ 44, 0, 262, 88, 160, 102, 0, 262, 44, 0,
+ 3, 0, 4, 0, 53, 0, 3, 0, 53, 0,
+ 99, 0, 98, 0, 100, 0, 45, 221, 149, 0,
+ 45, 186, 183, 0, 5, 45, 221, 149, 0, 5,
+ 45, 186, 183, 0, 143, 144, 149, 0, 53, 69,
+ 145, 70, 0, 4, 69, 145, 70, 0, 0, 146,
+ 0, 145, 55, 146, 0, 185, 0, 168, 0, 0,
+ 97, 229, 148, 234, 235, 103, 0, 0, 147, 0,
+ 0, 147, 150, 0, 74, 0, 73, 0, 81, 0,
+ 82, 0, 104, 0, 159, 0, 168, 0, 44, 0,
+ 88, 152, 102, 0, 44, 0, 88, 156, 102, 0,
+ 0, 156, 0, 1, 0, 0, 311, 183, 330, 197,
+ 206, 58, 157, 213, 0, 152, 0, 54, 103, 0,
+ 54, 279, 276, 103, 0, 54, 279, 1, 103, 0,
+ 289, 0, 168, 55, 168, 0, 168, 55, 1, 0,
+ 159, 55, 168, 0, 159, 55, 1, 0, 168, 0,
+ 159, 0, 173, 0, 0, 33, 162, 166, 0, 75,
+ 166, 0, 65, 166, 0, 83, 166, 0, 151, 166,
+ 0, 62, 138, 0, 11, 161, 0, 11, 88, 185,
+ 102, 0, 28, 161, 0, 28, 88, 185, 102, 0,
+ 176, 249, 0, 176, 249, 164, 0, 176, 163, 249,
+ 0, 176, 163, 249, 164, 0, 176, 88, 185, 102,
+ 0, 176, 88, 185, 102, 164, 0, 176, 163, 88,
+ 185, 102, 0, 176, 163, 88, 185, 102, 164, 0,
+ 177, 166, 0, 177, 89, 105, 166, 0, 177, 89,
+ 152, 105, 166, 0, 88, 160, 102, 0, 54, 160,
+ 103, 0, 88, 160, 102, 0, 44, 0, 88, 192,
+ 102, 0, 58, 213, 0, 88, 185, 102, 0, 165,
+ 88, 185, 102, 0, 167, 0, 165, 167, 0, 165,
+ 54, 214, 218, 103, 0, 161, 0, 29, 88, 152,
+ 102, 0, 30, 88, 152, 102, 0, 30, 88, 4,
+ 102, 0, 166, 0, 168, 78, 168, 0, 168, 79,
+ 168, 0, 168, 73, 168, 0, 168, 74, 168, 0,
+ 168, 75, 168, 0, 168, 76, 168, 0, 168, 77,
+ 168, 0, 168, 71, 168, 0, 168, 72, 168, 0,
+ 168, 68, 168, 0, 168, 69, 168, 0, 168, 70,
+ 168, 0, 168, 67, 168, 0, 168, 66, 168, 0,
+ 168, 65, 168, 0, 168, 63, 168, 0, 168, 64,
+ 168, 0, 168, 62, 168, 0, 168, 61, 168, 0,
+ 168, 59, 317, 60, 168, 0, 168, 58, 168, 0,
+ 168, 57, 168, 0, 92, 0, 92, 168, 0, 83,
+ 328, 138, 0, 335, 0, 3, 0, 53, 0, 169,
+ 0, 4, 0, 169, 0, 262, 0, 75, 171, 0,
+ 65, 171, 0, 260, 0, 169, 0, 262, 0, 169,
+ 0, 8, 0, 178, 0, 179, 0, 88, 152, 102,
+ 0, 88, 1, 102, 0, 0, 88, 174, 283, 102,
+ 0, 0, 173, 88, 160, 102, 175, 150, 0, 173,
+ 44, 0, 173, 89, 152, 105, 0, 173, 81, 0,
+ 173, 82, 0, 40, 0, 7, 88, 160, 102, 0,
+ 264, 0, 47, 69, 185, 70, 88, 152, 102, 0,
+ 48, 69, 185, 70, 88, 152, 102, 0, 49, 69,
+ 185, 70, 88, 152, 102, 0, 50, 69, 185, 70,
+ 88, 152, 102, 0, 46, 88, 152, 102, 0, 46,
+ 88, 185, 102, 0, 271, 3, 0, 271, 335, 0,
+ 263, 0, 263, 88, 160, 102, 0, 263, 44, 0,
+ 181, 170, 0, 181, 261, 0, 181, 170, 88, 160,
+ 102, 0, 181, 170, 44, 0, 181, 261, 88, 160,
+ 102, 0, 181, 261, 44, 0, 181, 83, 6, 44,
+ 0, 181, 6, 51, 83, 6, 44, 0, 38, 0,
+ 271, 38, 0, 37, 0, 271, 177, 0, 42, 0,
+ 43, 0, 9, 0, 179, 9, 0, 0, 173, 87,
+ 0, 173, 86, 0, 192, 183, 56, 0, 186, 183,
+ 56, 0, 192, 194, 56, 0, 186, 194, 56, 0,
+ 189, 195, 56, 0, 186, 56, 0, 189, 56, 0,
+ 254, 0, 258, 0, 44, 0, 184, 44, 0, 190,
+ 274, 0, 251, 274, 0, 192, 274, 0, 190, 0,
+ 251, 0, 190, 0, 187, 0, 189, 192, 0, 192,
+ 188, 0, 189, 192, 188, 0, 189, 192, 191, 0,
+ 189, 192, 191, 188, 0, 5, 0, 188, 193, 0,
+ 188, 5, 0, 251, 0, 5, 0, 189, 7, 0,
+ 189, 5, 0, 192, 0, 251, 192, 0, 192, 191,
+ 0, 251, 192, 191, 0, 193, 0, 191, 193, 0,
+ 215, 0, 6, 0, 268, 0, 27, 88, 152, 102,
+ 0, 27, 88, 185, 102, 0, 31, 88, 152, 102,
+ 0, 31, 88, 185, 102, 0, 6, 0, 7, 0,
+ 215, 0, 198, 0, 194, 55, 200, 0, 202, 0,
+ 195, 55, 200, 0, 204, 0, 196, 55, 200, 0,
+ 0, 111, 88, 179, 102, 0, 0, 183, 330, 197,
+ 206, 58, 199, 213, 0, 183, 330, 197, 206, 0,
+ 0, 183, 330, 197, 206, 58, 201, 213, 0, 183,
+ 330, 197, 206, 0, 0, 258, 330, 197, 206, 58,
+ 203, 213, 0, 258, 330, 197, 206, 0, 0, 258,
+ 330, 197, 206, 58, 205, 213, 0, 258, 330, 197,
+ 206, 0, 0, 207, 0, 208, 0, 207, 208, 0,
+ 32, 88, 88, 209, 102, 102, 0, 210, 0, 209,
+ 55, 210, 0, 0, 211, 0, 211, 88, 3, 102,
+ 0, 211, 88, 3, 55, 160, 102, 0, 211, 88,
+ 160, 102, 0, 138, 0, 5, 0, 6, 0, 7,
+ 0, 138, 0, 212, 55, 138, 0, 168, 0, 54,
+ 103, 0, 54, 214, 103, 0, 54, 214, 55, 103,
+ 0, 1, 0, 213, 0, 214, 55, 213, 0, 89,
+ 168, 105, 213, 0, 214, 55, 19, 168, 60, 213,
+ 0, 138, 60, 213, 0, 214, 55, 138, 60, 213,
+ 0, 0, 12, 138, 54, 216, 247, 219, 103, 0,
+ 12, 138, 54, 103, 0, 0, 12, 54, 217, 247,
+ 219, 103, 0, 12, 54, 103, 0, 12, 138, 0,
+ 12, 269, 0, 228, 234, 235, 103, 0, 228, 0,
+ 0, 55, 0, 0, 55, 0, 35, 0, 220, 5,
+ 0, 220, 6, 0, 220, 7, 0, 220, 35, 0,
+ 220, 143, 56, 0, 220, 138, 0, 220, 269, 0,
+ 220, 142, 0, 220, 143, 54, 0, 220, 143, 60,
+ 0, 221, 0, 220, 140, 0, 0, 0, 222, 224,
+ 229, 0, 223, 225, 229, 0, 220, 54, 0, 227,
+ 0, 226, 0, 0, 60, 0, 60, 230, 0, 231,
+ 0, 230, 55, 231, 0, 232, 0, 233, 232, 0,
+ 268, 0, 31, 88, 152, 102, 0, 31, 88, 185,
+ 102, 0, 36, 0, 5, 0, 233, 36, 0, 233,
+ 5, 0, 54, 0, 0, 236, 0, 235, 36, 60,
+ 236, 0, 235, 36, 60, 0, 237, 0, 236, 237,
+ 0, 236, 56, 0, 238, 56, 0, 238, 103, 0,
+ 131, 60, 0, 131, 54, 0, 186, 239, 0, 189,
+ 240, 0, 258, 330, 197, 206, 0, 60, 168, 0,
+ 1, 0, 186, 88, 322, 102, 250, 0, 186, 44,
+ 250, 0, 0, 241, 0, 239, 55, 242, 0, 0,
+ 244, 0, 240, 55, 246, 0, 243, 0, 244, 0,
+ 245, 0, 246, 0, 254, 330, 197, 206, 0, 254,
+ 330, 197, 206, 58, 213, 0, 4, 60, 168, 206,
+ 0, 258, 330, 197, 206, 0, 258, 330, 197, 206,
+ 58, 213, 0, 3, 60, 168, 206, 0, 60, 168,
+ 206, 0, 254, 330, 197, 206, 0, 254, 330, 197,
+ 206, 58, 213, 0, 4, 60, 168, 206, 0, 258,
+ 330, 197, 206, 0, 258, 330, 197, 206, 58, 213,
+ 0, 3, 60, 168, 206, 0, 60, 168, 206, 0,
+ 248, 0, 247, 55, 248, 0, 138, 0, 138, 58,
+ 168, 0, 311, 272, 0, 311, 0, 88, 185, 102,
+ 89, 152, 105, 0, 0, 250, 7, 0, 7, 0,
+ 251, 7, 0, 0, 253, 152, 0, 75, 251, 254,
+ 0, 65, 251, 254, 0, 75, 254, 0, 65, 254,
+ 0, 270, 250, 254, 0, 257, 0, 265, 0, 256,
+ 0, 266, 265, 0, 257, 88, 160, 102, 250, 0,
+ 257, 88, 322, 102, 250, 0, 257, 44, 250, 0,
+ 257, 88, 1, 102, 250, 0, 257, 89, 252, 105,
+ 0, 257, 89, 105, 0, 88, 254, 102, 0, 266,
+ 265, 0, 265, 0, 75, 251, 258, 0, 65, 251,
+ 258, 0, 75, 258, 0, 65, 258, 0, 270, 250,
+ 258, 0, 172, 0, 75, 251, 258, 0, 65, 251,
+ 258, 0, 75, 259, 0, 65, 259, 0, 270, 250,
+ 258, 0, 260, 0, 172, 88, 160, 102, 250, 0,
+ 172, 88, 322, 102, 250, 0, 172, 44, 250, 0,
+ 172, 88, 1, 102, 250, 0, 88, 171, 102, 0,
+ 88, 259, 102, 0, 172, 89, 252, 105, 0, 172,
+ 89, 105, 0, 266, 170, 0, 266, 169, 0, 262,
+ 0, 271, 262, 0, 192, 88, 160, 102, 0, 192,
+ 88, 171, 102, 0, 192, 184, 0, 4, 0, 142,
+ 0, 267, 0, 266, 267, 0, 4, 51, 0, 142,
+ 51, 0, 255, 0, 271, 255, 0, 256, 0, 271,
+ 255, 0, 266, 75, 0, 271, 266, 75, 0, 51,
+ 0, 75, 250, 272, 0, 75, 250, 0, 65, 250,
+ 272, 0, 65, 250, 0, 270, 250, 0, 270, 250,
+ 272, 0, 273, 0, 89, 152, 105, 0, 273, 89,
+ 252, 105, 0, 75, 251, 274, 0, 75, 274, 0,
+ 75, 251, 0, 75, 0, 65, 251, 274, 0, 65,
+ 274, 0, 65, 251, 0, 65, 0, 270, 250, 0,
+ 270, 250, 274, 0, 275, 0, 88, 274, 102, 0,
+ 85, 0, 275, 88, 322, 102, 250, 0, 275, 44,
+ 250, 0, 275, 89, 252, 105, 0, 275, 89, 105,
+ 0, 88, 323, 102, 250, 0, 165, 250, 0, 184,
+ 250, 0, 89, 252, 105, 0, 89, 105, 0, 288,
+ 0, 277, 0, 276, 288, 0, 276, 277, 0, 1,
+ 56, 0, 0, 0, 280, 0, 281, 0, 280, 281,
+ 0, 34, 212, 56, 0, 283, 0, 1, 283, 0,
+ 54, 278, 103, 0, 54, 278, 279, 276, 103, 0,
+ 54, 278, 279, 276, 1, 103, 0, 54, 278, 279,
+ 1, 103, 0, 0, 0, 13, 285, 278, 154, 286,
+ 287, 0, 283, 0, 278, 289, 0, 283, 0, 289,
+ 0, 182, 0, 152, 56, 0, 0, 284, 14, 290,
+ 287, 0, 284, 0, 0, 0, 15, 291, 278, 154,
+ 292, 158, 0, 0, 0, 16, 293, 287, 15, 294,
+ 153, 56, 0, 0, 0, 0, 314, 295, 278, 155,
+ 56, 296, 317, 102, 297, 158, 0, 0, 0, 0,
+ 315, 298, 278, 155, 56, 299, 317, 102, 300, 158,
+ 0, 0, 18, 278, 88, 156, 102, 301, 287, 0,
+ 0, 19, 168, 60, 302, 288, 0, 0, 19, 168,
+ 10, 168, 60, 303, 288, 0, 0, 20, 60, 304,
+ 288, 0, 21, 56, 0, 22, 56, 0, 23, 56,
+ 0, 23, 152, 56, 0, 111, 316, 88, 179, 102,
+ 56, 0, 111, 316, 88, 179, 60, 318, 102, 56,
+ 0, 111, 316, 88, 179, 60, 318, 60, 318, 102,
+ 56, 0, 111, 316, 88, 179, 60, 318, 60, 318,
+ 60, 321, 102, 56, 0, 24, 75, 152, 56, 0,
+ 24, 138, 56, 0, 313, 288, 0, 313, 103, 0,
+ 56, 0, 305, 0, 0, 0, 90, 54, 278, 306,
+ 308, 307, 309, 0, 103, 0, 276, 103, 0, 1,
+ 103, 0, 0, 0, 309, 91, 310, 312, 283, 0,
+ 190, 0, 251, 0, 88, 10, 102, 0, 88, 327,
+ 102, 0, 3, 60, 0, 53, 60, 0, 17, 88,
+ 56, 0, 17, 88, 152, 56, 0, 17, 88, 54,
+ 103, 0, 17, 88, 182, 0, 17, 88, 1, 56,
+ 0, 17, 88, 54, 278, 276, 103, 0, 17, 88,
+ 54, 278, 1, 103, 0, 0, 7, 0, 0, 152,
+ 0, 1, 0, 0, 319, 0, 320, 0, 319, 55,
+ 320, 0, 9, 88, 152, 102, 0, 9, 0, 321,
+ 55, 9, 0, 0, 323, 0, 185, 0, 324, 0,
+ 325, 10, 0, 324, 10, 0, 185, 10, 0, 10,
+ 0, 93, 0, 324, 93, 0, 185, 93, 0, 324,
+ 60, 0, 185, 60, 0, 326, 0, 327, 58, 213,
+ 0, 325, 327, 0, 325, 327, 58, 213, 0, 325,
+ 329, 0, 325, 329, 58, 213, 0, 324, 55, 0,
+ 185, 55, 0, 187, 183, 0, 190, 183, 0, 192,
+ 183, 0, 187, 274, 0, 187, 0, 189, 258, 0,
+ 326, 0, 185, 0, 0, 0, 258, 0, 0, 92,
+ 88, 332, 102, 0, 185, 0, 331, 0, 332, 55,
+ 331, 0, 0, 75, 250, 333, 0, 65, 250, 333,
+ 0, 270, 250, 333, 0, 41, 0, 334, 75, 0,
+ 334, 76, 0, 334, 77, 0, 334, 73, 0, 334,
+ 74, 0, 334, 65, 0, 334, 63, 0, 334, 64,
+ 0, 334, 83, 0, 334, 55, 0, 334, 68, 0,
+ 334, 69, 0, 334, 70, 0, 334, 67, 0, 334,
+ 57, 0, 334, 58, 0, 334, 71, 0, 334, 72,
+ 0, 334, 81, 0, 334, 82, 0, 334, 62, 0,
+ 334, 61, 0, 334, 104, 0, 334, 59, 60, 0,
+ 334, 66, 0, 334, 86, 0, 334, 78, 0, 334,
+ 44, 0, 334, 89, 105, 0, 334, 38, 0, 334,
+ 37, 0, 334, 38, 89, 105, 0, 334, 37, 89,
+ 105, 0, 334, 311, 333, 0, 334, 1, 0
+};
+
+#endif
+
+#if YYDEBUG != 0
+static const short yyrline[] = { 0,
+ 291, 292, 306, 308, 309, 313, 318, 322, 324, 327,
+ 330, 334, 337, 339, 341, 342, 345, 347, 349, 352,
+ 357, 362, 365, 369, 372, 376, 392, 401, 404, 409,
+ 411, 415, 421, 421, 424, 424, 427, 427, 442, 442,
+ 447, 452, 469, 492, 502, 503, 506, 507, 508, 509,
+ 510, 513, 516, 519, 524, 529, 535, 537, 538, 557,
+ 558, 559, 562, 576, 589, 592, 595, 598, 600, 602,
+ 606, 612, 617, 622, 629, 640, 647, 649, 651, 655,
+ 663, 665, 667, 669, 673, 686, 709, 712, 714, 715,
+ 718, 724, 730, 732, 734, 736, 739, 743, 749, 751,
+ 752, 755, 757, 760, 762, 763, 766, 769, 771, 773,
+ 777, 782, 785, 789, 794, 797, 801, 804, 807, 841,
+ 857, 860, 864, 867, 871, 873, 875, 877, 879, 883,
+ 885, 888, 893, 897, 902, 906, 909, 910, 914, 933,
+ 940, 943, 946, 948, 950, 954, 958, 961, 963, 967,
+ 970, 973, 982, 985, 988, 990, 992, 994, 1001, 1012,
+ 1032, 1034, 1036, 1041, 1043, 1045, 1047, 1049, 1052, 1054,
+ 1056, 1059, 1061, 1065, 1071, 1074, 1081, 1084, 1086, 1094,
+ 1103, 1109, 1115, 1117, 1119, 1132, 1134, 1136, 1138, 1155,
+ 1158, 1160, 1162, 1164, 1166, 1168, 1170, 1172, 1174, 1176,
+ 1178, 1180, 1182, 1184, 1186, 1188, 1190, 1192, 1194, 1196,
+ 1198, 1200, 1207, 1209, 1226, 1229, 1230, 1231, 1234, 1236,
+ 1239, 1241, 1242, 1244, 1248, 1250, 1251, 1256, 1276, 1277,
+ 1278, 1280, 1282, 1284, 1292, 1313, 1318, 1325, 1332, 1334,
+ 1343, 1348, 1371, 1415, 1416, 1419, 1422, 1425, 1428, 1430,
+ 1433, 1472, 1479, 1481, 1483, 1485, 1487, 1489, 1504, 1519,
+ 1530, 1542, 1549, 1598, 1600, 1604, 1606, 1610, 1613, 1618,
+ 1620, 1624, 1637, 1638, 1644, 1655, 1663, 1669, 1674, 1676,
+ 1681, 1688, 1690, 1694, 1698, 1704, 1707, 1709, 1711, 1713,
+ 1721, 1723, 1725, 1728, 1730, 1732, 1734, 1739, 1745, 1747,
+ 1758, 1761, 1763, 1766, 1781, 1784, 1786, 1788, 1792, 1795,
+ 1803, 1804, 1805, 1806, 1810, 1814, 1828, 1846, 1847, 1848,
+ 1851, 1853, 1856, 1858, 1861, 1863, 1866, 1869, 1873, 1890,
+ 1892, 1910, 1916, 1917, 1923, 1931, 1933, 1942, 1950, 1952,
+ 1963, 1966, 1970, 1973, 1977, 1982, 1985, 1989, 1992, 1994,
+ 1996, 1998, 2005, 2007, 2008, 2009, 2013, 2016, 2020, 2022,
+ 2025, 2028, 2031, 2037, 2040, 2043, 2045, 2047, 2049, 2053,
+ 2057, 2061, 2064, 2067, 2071, 2074, 2076, 2080, 2131, 2146,
+ 2148, 2151, 2153, 2157, 2158, 2160, 2162, 2164, 2168, 2177,
+ 2180, 2182, 2184, 2190, 2192, 2195, 2200, 2203, 2206, 2215,
+ 2226, 2231, 2231, 2233, 2236, 2238, 2242, 2244, 2248, 2276,
+ 2307, 2309, 2331, 2355, 2357, 2361, 2387, 2396, 2424, 2427,
+ 2434, 2445, 2454, 2458, 2471, 2474, 2476, 2481, 2483, 2487,
+ 2495, 2499, 2502, 2504, 2515, 2520, 2528, 2531, 2532, 2543,
+ 2546, 2547, 2558, 2560, 2563, 2565, 2568, 2573, 2577, 2583,
+ 2588, 2592, 2596, 2602, 2606, 2609, 2614, 2618, 2621, 2624,
+ 2633, 2635, 2639, 2642, 2647, 2650, 2654, 2663, 2666, 2670,
+ 2673, 2681, 2683, 2688, 2691, 2693, 2695, 2697, 2701, 2704,
+ 2718, 2721, 2726, 2729, 2731, 2733, 2735, 2737, 2739, 2741,
+ 2745, 2751, 2754, 2756, 2758, 2760, 2764, 2767, 2770, 2772,
+ 2774, 2776, 2780, 2783, 2786, 2788, 2790, 2792, 2794, 2796,
+ 2798, 2802, 2808, 2814, 2816, 2820, 2823, 2825, 2829, 2831,
+ 2834, 2836, 2842, 2845, 2859, 2861, 2865, 2867, 2871, 2874,
+ 2880, 2886, 2889, 2891, 2893, 2895, 2899, 2903, 2907, 2910,
+ 2915, 2918, 2920, 2922, 2924, 2926, 2928, 2930, 2932, 2936,
+ 2940, 2944, 2948, 2949, 2951, 2953, 2955, 2957, 2959, 2961,
+ 2963, 2965, 2973, 2975, 2976, 2977, 2980, 2987, 2997, 2999,
+ 3004, 3006, 3009, 3023, 3026, 3029, 3033, 3037, 3041, 3047,
+ 3050, 3054, 3056, 3059, 3065, 3068, 3071, 3074, 3087, 3090,
+ 3095, 3101, 3106, 3109, 3114, 3118, 3121, 3127, 3132, 3135,
+ 3140, 3149, 3153, 3156, 3162, 3172, 3179, 3185, 3210, 3210,
+ 3242, 3242, 3258, 3258, 3262, 3266, 3269, 3274, 3281, 3290,
+ 3299, 3308, 3311, 3317, 3319, 3323, 3325, 3328, 3332, 3335,
+ 3338, 3346, 3350, 3356, 3358, 3360, 3364, 3366, 3369, 3382,
+ 3387, 3395, 3399, 3402, 3404, 3408, 3411, 3413, 3415, 3421,
+ 3425, 3429, 3432, 3433, 3439, 3441, 3444, 3446, 3450, 3455,
+ 3458, 3468, 3475, 3476, 3483, 3489, 3494, 3498, 3503, 3510,
+ 3514, 3518, 3523, 3534, 3548, 3551, 3553, 3555, 3557, 3559,
+ 3563, 3565, 3573, 3590, 3592, 3594, 3596, 3598, 3602, 3604,
+ 3607, 3629, 3635, 3642, 3645, 3649, 3654, 3656, 3663, 3666,
+ 3668, 3670, 3676, 3680, 3683, 3685, 3687, 3689, 3691, 3693,
+ 3695, 3697, 3699, 3701, 3703, 3705, 3707, 3709, 3711, 3713,
+ 3715, 3717, 3719, 3721, 3723, 3725, 3727, 3729, 3731, 3733,
+ 3735, 3737, 3739, 3741, 3743, 3745, 3748, 3750
+};
+
+static const char * const yytname[] = { "$","error","$illegal.","IDENTIFIER",
+"TYPENAME","SCSPEC","TYPESPEC","TYPE_QUAL","CONSTANT","STRING","ELLIPSIS","SIZEOF",
+"ENUM","IF","ELSE","WHILE","DO","FOR","SWITCH","CASE","DEFAULT","BREAK","CONTINUE",
+"RETURN","GOTO","ASM_KEYWORD","GCC_ASM_KEYWORD","TYPEOF","ALIGNOF","HEADOF",
+"CLASSOF","SIGOF","ATTRIBUTE","EXTENSION","LABEL","AGGR","VISSPEC","DELETE",
+"NEW","OVERLOAD","THIS","OPERATOR","CXX_TRUE","CXX_FALSE","LEFT_RIGHT","TEMPLATE",
+"TYPEID","DYNAMIC_CAST","STATIC_CAST","REINTERPRET_CAST","CONST_CAST","SCOPE",
+"EMPTY","PTYPENAME","'{'","','","';'","ASSIGN","'='","'?'","':'","OROR","ANDAND",
+"'|'","'^'","'&'","MIN_MAX","EQCOMPARE","ARITHCOMPARE","'<'","'>'","LSHIFT",
+"RSHIFT","'+'","'-'","'*'","'/'","'%'","POINTSAT_STAR","DOT_STAR","UNARY","PLUSPLUS",
+"MINUSMINUS","'~'","HYPERUNARY","PAREN_STAR_PAREN","POINTSAT","'.'","'('","'['",
+"TRY","CATCH","THROW","TYPENAME_ELLIPSIS","PRE_PARSED_FUNCTION_DECL","EXTERN_LANG_STRING",
+"ALL","PRE_PARSED_CLASS_DECL","TYPENAME_DEFN","IDENTIFIER_DEFN","PTYPENAME_DEFN",
+"END_OF_SAVED_INPUT","')'","'}'","'!'","']'","program","extdefs","@1",".hush_warning",
+".warning_ok","asm_keyword","lang_extdef","@2","extdef","extern_lang_string",
+"template_header","@3","template_parm_list","template_parm","overloaddef","ov_identifiers",
+"template_def","@4","@5","@6","@7","fn_tmpl_end","datadef","fndef","fn.def1",
+"fn.def2","return_id","return_init","base_init",".set_base_init","member_init_list",
+"member_init","identifier","notype_identifier","identifier_defn","explicit_instantiation",
+"template_type","template_type_name","tmpl.2","template_arg_list","template_arg",
+"template_instantiate_once","@8","template_instantiation","template_instantiate_some",
+"unop","expr","paren_expr_or_null","paren_cond_or_null","xcond","condition",
+"@9","already_scoped_stmt","nontrivial_exprlist","nonnull_exprlist","unary_expr",
+"@10","new_placement","new_initializer","regcast_or_absdcl","cast_expr","sub_cast_expr",
+"expr_no_commas","notype_unqualified_id","unqualified_id","expr_or_declarator",
+"direct_notype_declarator","primary","@11","@12","new","delete","boolean.literal",
+"string","nodecls","object","decl","declarator","fcast_or_absdcl","type_id",
+"typed_declspecs","typed_declspecs1","reserved_declspecs","declmods","typed_typespecs",
+"reserved_typespecquals","typespec","typespecqual_reserved","initdecls","notype_initdecls",
+"nomods_initdecls","maybeasm","initdcl0","@13","initdcl","@14","notype_initdcl0",
+"@15","nomods_initdcl0","@16","maybe_attribute","attributes","attribute","attribute_list",
+"attrib","any_word","identifiers_or_typenames","init","initlist","structsp",
+"@17","@18","maybecomma","maybecomma_warn","aggr","specialization","named_class_head_sans_basetype",
+"named_class_head_sans_basetype_defn","do_xref","do_xref_defn","named_class_head",
+"unnamed_class_head","class_head","maybe_base_class_list","base_class_list",
+"base_class","base_class.1","base_class_access_list","left_curly","opt.component_decl_list",
+"component_decl_list","component_decl","component_decl_1","components","notype_components",
+"component_declarator0","component_declarator","after_type_component_declarator0",
+"notype_component_declarator0","after_type_component_declarator","notype_component_declarator",
+"enumlist","enumerator","new_type_id","type_quals","nonempty_type_quals","nonmomentary_expr",
+"@19","after_type_declarator","qualified_type_name","nested_type","direct_after_type_declarator",
+"notype_declarator","complex_notype_declarator","complex_direct_notype_declarator",
+"qualified_id","notype_qualified_id","overqualified_id","functional_cast","type_name",
+"nested_name_specifier","nested_name_specifier_1","complete_type_name","complex_type_name",
+"ptr_to_mem","global_scope","new_declarator","direct_new_declarator","absdcl",
+"direct_abstract_declarator","stmts","errstmt",".pushlevel","maybe_label_decls",
+"label_decls","label_decl","compstmt_or_error","compstmt","simple_if","@20",
+"@21","implicitly_scoped_stmt","stmt","simple_stmt","@22","@23","@24","@25",
+"@26","@27","@28","@29","@30","@31","@32","@33","@34","@35","@36","try_block",
+"@37","@38","ansi_try_stmts","handler_seq","@39","type_specifier_seq","handler_args",
+"label_colon","forhead.1","forhead.2","maybe_type_qual","xexpr","asm_operands",
+"nonnull_asm_operands","asm_operand","asm_clobbers","parmlist","complex_parmlist",
+"parms","parms_comma","named_parm","parm","see_typename","bad_parm","maybe_raises",
+"ansi_raise_identifier","ansi_raise_identifiers","conversion_declarator","operator",
+"operator_name",""
+};
+#endif
+
+static const short yyr1[] = { 0,
+ 106, 106, 108, 107, 107, 109, 110, 111, 111, 113,
+ 112, 114, 114, 114, 114, 114, 114, 114, 114, 114,
+ 115, 117, 116, 118, 118, 119, 119, 119, 120, 121,
+ 121, 123, 122, 124, 122, 125, 122, 126, 122, 122,
+ 122, 122, 122, 122, 122, 122, 127, 127, 127, 127,
+ 127, 128, 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 129, 129, 129, 129, 129, 129, 129, 129,
+ 130, 130, 130, 130, 131, 131, 131, 131, 131, 132,
+ 133, 133, 133, 133, 134, 135, 136, 136, 136, 136,
+ 137, 137, 137, 137, 137, 137, 137, 137, 138, 138,
+ 138, 139, 139, 140, 140, 140, 141, 141, 141, 141,
+ 142, 143, 143, 144, 145, 145, 146, 146, 148, 147,
+ 149, 149, 150, 150, 151, 151, 151, 151, 151, 152,
+ 152, 153, 153, 154, 154, 155, 155, 155, 157, 156,
+ 156, 158, 158, 158, 158, 159, 159, 159, 159, 160,
+ 160, 161, 162, 161, 161, 161, 161, 161, 161, 161,
+ 161, 161, 161, 161, 161, 161, 161, 161, 161, 161,
+ 161, 161, 161, 161, 163, 163, 164, 164, 164, 164,
+ 165, 165, 166, 166, 166, 167, 167, 167, 167, 168,
+ 168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
+ 168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
+ 168, 168, 168, 168, 169, 169, 169, 169, 170, 170,
+ 171, 171, 171, 171, 172, 172, 172, 173, 173, 173,
+ 173, 173, 173, 174, 173, 175, 173, 173, 173, 173,
+ 173, 173, 173, 173, 173, 173, 173, 173, 173, 173,
+ 173, 173, 173, 173, 173, 173, 173, 173, 173, 173,
+ 173, 173, 173, 176, 176, 177, 177, 178, 178, 179,
+ 179, 180, 181, 181, 182, 182, 182, 182, 182, 182,
+ 182, 183, 183, 184, 184, 185, 185, 185, 185, 185,
+ 186, 186, 187, 187, 187, 187, 187, 188, 188, 188,
+ 189, 189, 189, 189, 190, 190, 190, 190, 191, 191,
+ 192, 192, 192, 192, 192, 192, 192, 193, 193, 193,
+ 194, 194, 195, 195, 196, 196, 197, 197, 199, 198,
+ 198, 201, 200, 200, 203, 202, 202, 205, 204, 204,
+ 206, 206, 207, 207, 208, 209, 209, 210, 210, 210,
+ 210, 210, 211, 211, 211, 211, 212, 212, 213, 213,
+ 213, 213, 213, 214, 214, 214, 214, 214, 214, 216,
+ 215, 215, 217, 215, 215, 215, 215, 215, 215, 218,
+ 218, 219, 219, 220, 220, 220, 220, 220, 221, 222,
+ 222, 222, 222, 222, 222, 223, 224, 225, 226, 226,
+ 227, 228, 228, 229, 229, 229, 230, 230, 231, 231,
+ 232, 232, 232, 233, 233, 233, 233, 234, 235, 235,
+ 235, 235, 236, 236, 236, 237, 237, 237, 237, 238,
+ 238, 238, 238, 238, 238, 238, 239, 239, 239, 240,
+ 240, 240, 241, 241, 242, 242, 243, 243, 243, 244,
+ 244, 244, 244, 245, 245, 245, 246, 246, 246, 246,
+ 247, 247, 248, 248, 249, 249, 249, 250, 250, 251,
+ 251, 253, 252, 254, 254, 254, 254, 254, 254, 255,
+ 255, 256, 257, 257, 257, 257, 257, 257, 257, 257,
+ 257, 258, 258, 258, 258, 258, 258, 259, 259, 259,
+ 259, 259, 259, 260, 260, 260, 260, 260, 260, 260,
+ 260, 261, 262, 263, 263, 264, 264, 264, 265, 265,
+ 266, 266, 267, 267, 268, 268, 269, 269, 270, 270,
+ 271, 272, 272, 272, 272, 272, 272, 272, 273, 273,
+ 274, 274, 274, 274, 274, 274, 274, 274, 274, 274,
+ 274, 275, 275, 275, 275, 275, 275, 275, 275, 275,
+ 275, 275, 276, 276, 276, 276, 277, 278, 279, 279,
+ 280, 280, 281, 282, 282, 283, 283, 283, 283, 285,
+ 286, 284, 287, 287, 288, 288, 289, 289, 290, 289,
+ 289, 291, 292, 289, 293, 294, 289, 295, 296, 297,
+ 289, 298, 299, 300, 289, 301, 289, 302, 289, 303,
+ 289, 304, 289, 289, 289, 289, 289, 289, 289, 289,
+ 289, 289, 289, 289, 289, 289, 289, 306, 307, 305,
+ 308, 308, 308, 309, 310, 309, 311, 311, 312, 312,
+ 313, 313, 314, 314, 314, 315, 315, 315, 315, 316,
+ 316, 317, 317, 317, 318, 318, 319, 319, 320, 321,
+ 321, 322, 322, 322, 323, 323, 323, 323, 323, 323,
+ 323, 323, 323, 323, 324, 324, 324, 324, 324, 324,
+ 325, 325, 326, 326, 326, 326, 326, 326, 327, 327,
+ 328, 329, 329, 330, 330, 331, 332, 332, 333, 333,
+ 333, 333, 334, 335, 335, 335, 335, 335, 335, 335,
+ 335, 335, 335, 335, 335, 335, 335, 335, 335, 335,
+ 335, 335, 335, 335, 335, 335, 335, 335, 335, 335,
+ 335, 335, 335, 335, 335, 335, 335, 335
+};
+
+static const short yyr2[] = { 0,
+ 0, 1, 0, 2, 2, 0, 0, 1, 1, 0,
+ 2, 1, 1, 1, 1, 5, 4, 3, 4, 4,
+ 1, 0, 5, 1, 3, 2, 4, 1, 3, 1,
+ 3, 0, 5, 0, 5, 0, 5, 0, 5, 3,
+ 3, 6, 7, 4, 3, 3, 1, 1, 1, 1,
+ 1, 2, 3, 3, 3, 3, 2, 2, 2, 2,
+ 2, 1, 3, 4, 3, 5, 4, 3, 3, 2,
+ 3, 3, 2, 1, 6, 4, 3, 3, 2, 2,
+ 1, 3, 4, 2, 3, 0, 0, 1, 3, 2,
+ 3, 1, 4, 2, 4, 2, 4, 2, 1, 1,
+ 1, 1, 1, 1, 1, 1, 3, 3, 4, 4,
+ 3, 4, 4, 0, 1, 3, 1, 1, 0, 6,
+ 0, 1, 0, 2, 1, 1, 1, 1, 1, 1,
+ 1, 1, 3, 1, 3, 0, 1, 1, 0, 8,
+ 1, 2, 4, 4, 1, 3, 3, 3, 3, 1,
+ 1, 1, 0, 3, 2, 2, 2, 2, 2, 2,
+ 4, 2, 4, 2, 3, 3, 4, 4, 5, 5,
+ 6, 2, 4, 5, 3, 3, 3, 1, 3, 2,
+ 3, 4, 1, 2, 5, 1, 4, 4, 4, 1,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 5,
+ 3, 3, 1, 2, 3, 1, 1, 1, 1, 1,
+ 1, 1, 2, 2, 1, 1, 1, 1, 1, 1,
+ 1, 3, 3, 0, 4, 0, 6, 2, 4, 2,
+ 2, 1, 4, 1, 7, 7, 7, 7, 4, 4,
+ 2, 2, 1, 4, 2, 2, 2, 5, 3, 5,
+ 3, 4, 6, 1, 2, 1, 2, 1, 1, 1,
+ 2, 0, 2, 2, 3, 3, 3, 3, 3, 2,
+ 2, 1, 1, 1, 2, 2, 2, 2, 1, 1,
+ 1, 1, 2, 2, 3, 3, 4, 1, 2, 2,
+ 1, 1, 2, 2, 1, 2, 2, 3, 1, 2,
+ 1, 1, 1, 4, 4, 4, 4, 1, 1, 1,
+ 1, 3, 1, 3, 1, 3, 0, 4, 0, 7,
+ 4, 0, 7, 4, 0, 7, 4, 0, 7, 4,
+ 0, 1, 1, 2, 6, 1, 3, 0, 1, 4,
+ 6, 4, 1, 1, 1, 1, 1, 3, 1, 2,
+ 3, 4, 1, 1, 3, 4, 6, 3, 5, 0,
+ 7, 4, 0, 6, 3, 2, 2, 4, 1, 0,
+ 1, 0, 1, 1, 2, 2, 2, 2, 3, 2,
+ 2, 2, 3, 3, 1, 2, 0, 0, 3, 3,
+ 2, 1, 1, 0, 1, 2, 1, 3, 1, 2,
+ 1, 4, 4, 1, 1, 2, 2, 1, 0, 1,
+ 4, 3, 1, 2, 2, 2, 2, 2, 2, 2,
+ 2, 4, 2, 1, 5, 3, 0, 1, 3, 0,
+ 1, 3, 1, 1, 1, 1, 4, 6, 4, 4,
+ 6, 4, 3, 4, 6, 4, 4, 6, 4, 3,
+ 1, 3, 1, 3, 2, 1, 6, 0, 2, 1,
+ 2, 0, 2, 3, 3, 2, 2, 3, 1, 1,
+ 1, 2, 5, 5, 3, 5, 4, 3, 3, 2,
+ 1, 3, 3, 2, 2, 3, 1, 3, 3, 2,
+ 2, 3, 1, 5, 5, 3, 5, 3, 3, 4,
+ 3, 2, 2, 1, 2, 4, 4, 2, 1, 1,
+ 1, 2, 2, 2, 1, 2, 1, 2, 2, 3,
+ 1, 3, 2, 3, 2, 2, 3, 1, 3, 4,
+ 3, 2, 2, 1, 3, 2, 2, 1, 2, 3,
+ 1, 3, 1, 5, 3, 4, 3, 4, 2, 2,
+ 3, 2, 1, 1, 2, 2, 2, 0, 0, 1,
+ 1, 2, 3, 1, 2, 3, 5, 6, 5, 0,
+ 0, 6, 1, 2, 1, 1, 1, 2, 0, 4,
+ 1, 0, 0, 6, 0, 0, 7, 0, 0, 0,
+ 10, 0, 0, 0, 10, 0, 7, 0, 5, 0,
+ 7, 0, 4, 2, 2, 2, 3, 6, 8, 10,
+ 12, 4, 3, 2, 2, 1, 1, 0, 0, 7,
+ 1, 2, 2, 0, 0, 5, 1, 1, 3, 3,
+ 2, 2, 3, 4, 4, 3, 4, 6, 6, 0,
+ 1, 0, 1, 1, 0, 1, 1, 3, 4, 1,
+ 3, 0, 1, 1, 1, 2, 2, 2, 1, 1,
+ 2, 2, 2, 2, 1, 3, 2, 4, 2, 4,
+ 2, 2, 2, 2, 2, 2, 1, 2, 1, 1,
+ 0, 0, 1, 0, 4, 1, 1, 3, 0, 3,
+ 3, 3, 1, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 3, 2, 2, 2,
+ 2, 3, 2, 2, 4, 4, 3, 2
+};
+
+static const short yydefact[] = { 3,
+ 10, 10, 5, 0, 4, 0, 217, 519, 302, 312,
+ 470, 0, 8, 9, 0, 0, 384, 0, 703, 0,
+ 531, 218, 62, 0, 0, 691, 0, 74, 21, 0,
+ 11, 6, 0, 15, 14, 13, 12, 272, 0, 520,
+ 114, 226, 497, 0, 292, 0, 291, 305, 0, 325,
+ 311, 0, 395, 397, 398, 403, 402, 379, 301, 525,
+ 481, 0, 225, 227, 480, 0, 521, 313, 468, 0,
+ 0, 216, 60, 61, 523, 0, 0, 99, 100, 101,
+ 373, 376, 0, 527, 0, 377, 0, 0, 0, 30,
+ 0, 302, 0, 22, 0, 0, 395, 0, 0, 0,
+ 0, 495, 0, 0, 0, 494, 0, 0, 0, 226,
+ 0, 0, 0, 225, 227, 468, 0, 3, 0, 0,
+ 0, 0, 397, 398, 694, 0, 86, 81, 272, 0,
+ 0, 58, 524, 121, 468, 0, 472, 59, 0, 0,
+ 0, 0, 0, 321, 282, 479, 283, 491, 0, 468,
+ 304, 303, 57, 293, 0, 323, 0, 298, 318, 319,
+ 294, 307, 309, 320, 0, 52, 385, 386, 387, 388,
+ 401, 105, 104, 106, 390, 396, 392, 114, 391, 404,
+ 404, 418, 0, 471, 306, 70, 0, 73, 529, 513,
+ 482, 522, 0, 526, 0, 738, 734, 733, 731, 713,
+ 718, 719, 0, 725, 724, 710, 711, 709, 728, 717,
+ 714, 715, 716, 720, 721, 707, 708, 704, 705, 706,
+ 730, 722, 723, 712, 729, 0, 726, 637, 305, 638,
+ 699, 470, 229, 270, 0, 0, 0, 0, 153, 266,
+ 264, 242, 268, 269, 0, 0, 0, 0, 0, 0,
+ 0, 126, 125, 0, 127, 128, 0, 0, 213, 129,
+ 0, 115, 0, 186, 0, 190, 183, 118, 228, 152,
+ 0, 0, 230, 231, 0, 117, 289, 305, 290, 514,
+ 253, 244, 0, 0, 0, 395, 375, 0, 370, 528,
+ 0, 130, 131, 0, 0, 0, 0, 29, 0, 108,
+ 404, 122, 107, 0, 493, 0, 492, 100, 101, 215,
+ 224, 0, 501, 223, 0, 500, 508, 509, 0, 0,
+ 18, 10, 0, 7, 7, 46, 45, 694, 0, 32,
+ 40, 36, 34, 41, 38, 327, 80, 87, 84, 0,
+ 0, 272, 0, 0, 0, 568, 63, 574, 65, 111,
+ 506, 0, 669, 670, 151, 0, 150, 664, 687, 0,
+ 289, 305, 290, 0, 663, 665, 692, 675, 0, 511,
+ 0, 0, 0, 477, 0, 476, 0, 0, 0, 468,
+ 68, 56, 71, 0, 55, 468, 0, 472, 490, 0,
+ 295, 296, 0, 53, 69, 54, 72, 300, 299, 310,
+ 694, 326, 393, 389, 394, 405, 399, 400, 434, 0,
+ 0, 437, 440, 0, 0, 423, 0, 694, 308, 0,
+ 0, 341, 469, 496, 530, 0, 0, 727, 732, 468,
+ 468, 0, 468, 737, 0, 0, 0, 160, 0, 0,
+ 162, 0, 0, 0, 0, 0, 0, 0, 0, 159,
+ 156, 155, 157, 0, 0, 0, 0, 214, 0, 113,
+ 158, 0, 0, 184, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 238, 240, 241, 274,
+ 273, 0, 0, 0, 0, 0, 164, 466, 0, 172,
+ 271, 220, 0, 691, 219, 256, 257, 0, 284, 548,
+ 544, 553, 0, 472, 468, 468, 468, 286, 551, 0,
+ 518, 288, 0, 287, 255, 0, 251, 265, 267, 515,
+ 0, 252, 110, 109, 463, 382, 461, 372, 0, 314,
+ 0, 0, 315, 316, 317, 31, 0, 24, 690, 305,
+ 0, 689, 28, 119, 112, 499, 498, 502, 0, 17,
+ 20, 19, 327, 51, 47, 49, 50, 48, 44, 0,
+ 0, 0, 0, 341, 102, 92, 103, 0, 85, 88,
+ 0, 0, 0, 363, 0, 359, 82, 0, 0, 64,
+ 67, 575, 569, 468, 468, 668, 682, 674, 672, 548,
+ 544, 0, 683, 468, 686, 688, 684, 0, 685, 468,
+ 667, 681, 673, 671, 666, 693, 677, 679, 0, 510,
+ 473, 475, 474, 0, 0, 489, 0, 341, 322, 485,
+ 0, 0, 0, 488, 0, 478, 297, 324, 341, 327,
+ 415, 0, 414, 406, 407, 409, 0, 411, 433, 429,
+ 428, 217, 519, 468, 0, 662, 694, 430, 438, 443,
+ 444, 694, 694, 431, 441, 694, 0, 378, 425, 424,
+ 426, 427, 327, 696, 305, 697, 0, 0, 0, 340,
+ 342, 343, 736, 735, 699, 699, 699, 0, 0, 0,
+ 518, 0, 0, 519, 0, 154, 0, 0, 0, 0,
+ 0, 0, 233, 232, 0, 181, 116, 217, 519, 218,
+ 0, 0, 364, 380, 0, 212, 211, 654, 653, 0,
+ 209, 208, 206, 207, 205, 204, 203, 200, 201, 202,
+ 198, 199, 193, 194, 195, 196, 197, 191, 192, 0,
+ 0, 0, 0, 0, 0, 166, 178, 0, 0, 165,
+ 468, 468, 0, 468, 465, 538, 0, 0, 0, 0,
+ 259, 0, 261, 0, 512, 547, 546, 543, 542, 690,
+ 0, 0, 562, 0, 0, 559, 285, 560, 549, 468,
+ 662, 472, 548, 544, 0, 0, 468, 228, 0, 514,
+ 0, 0, 0, 383, 0, 382, 149, 148, 147, 146,
+ 0, 23, 390, 396, 0, 16, 341, 33, 37, 35,
+ 39, 0, 0, 90, 0, 94, 0, 98, 0, 96,
+ 0, 360, 0, 83, 66, 0, 576, 0, 570, 571,
+ 507, 504, 547, 543, 548, 544, 480, 0, 468, 549,
+ 548, 544, 0, 228, 0, 514, 505, 0, 0, 676,
+ 331, 468, 468, 468, 487, 337, 341, 0, 0, 417,
+ 416, 410, 0, 0, 436, 341, 0, 77, 0, 327,
+ 327, 0, 327, 0, 341, 0, 695, 0, 0, 338,
+ 344, 701, 700, 702, 243, 161, 0, 0, 163, 187,
+ 189, 188, 249, 250, 0, 0, 0, 0, 235, 0,
+ 0, 0, 0, 182, 0, 236, 239, 176, 175, 168,
+ 0, 167, 180, 0, 0, 535, 533, 0, 536, 472,
+ 173, 0, 0, 262, 0, 0, 545, 541, 552, 468,
+ 561, 550, 555, 0, 557, 0, 548, 544, 516, 517,
+ 0, 254, 464, 462, 374, 0, 25, 0, 0, 0,
+ 42, 91, 89, 0, 0, 0, 0, 361, 357, 0,
+ 0, 217, 580, 592, 595, 0, 568, 0, 0, 0,
+ 0, 0, 0, 218, 626, 0, 650, 0, 587, 0,
+ 0, 305, 0, 564, 585, 591, 563, 586, 627, 0,
+ 598, 602, 572, 547, 543, 482, 549, 517, 678, 680,
+ 329, 486, 483, 484, 335, 334, 0, 0, 408, 341,
+ 341, 76, 453, 468, 217, 519, 0, 439, 445, 446,
+ 694, 694, 341, 341, 442, 0, 432, 698, 328, 348,
+ 0, 0, 0, 0, 0, 0, 368, 0, 0, 365,
+ 185, 210, 123, 0, 169, 170, 177, 179, 534, 532,
+ 539, 537, 0, 174, 0, 258, 260, 558, 468, 556,
+ 371, 27, 0, 43, 93, 97, 95, 362, 0, 573,
+ 567, 579, 641, 568, 568, 568, 0, 0, 0, 612,
+ 614, 615, 616, 0, 0, 0, 642, 568, 651, 0,
+ 588, 280, 694, 0, 281, 0, 694, 0, 694, 0,
+ 0, 577, 566, 565, 589, 625, 624, 568, 568, 0,
+ 0, 332, 412, 413, 452, 449, 435, 0, 0, 341,
+ 327, 327, 447, 450, 354, 355, 356, 353, 0, 346,
+ 349, 339, 0, 0, 0, 0, 366, 0, 0, 123,
+ 237, 0, 171, 540, 263, 554, 120, 358, 0, 0,
+ 0, 583, 0, 0, 568, 643, 0, 646, 0, 0,
+ 608, 0, 617, 0, 623, 628, 0, 276, 327, 278,
+ 279, 327, 0, 0, 0, 275, 277, 578, 568, 0,
+ 0, 330, 336, 0, 75, 341, 341, 460, 341, 341,
+ 0, 0, 348, 0, 0, 245, 246, 247, 248, 0,
+ 369, 124, 467, 134, 0, 581, 593, 584, 596, 647,
+ 645, 0, 644, 141, 0, 305, 0, 0, 0, 613,
+ 622, 0, 0, 590, 138, 0, 137, 0, 333, 459,
+ 456, 454, 457, 448, 451, 347, 345, 217, 0, 367,
+ 0, 568, 0, 0, 0, 0, 606, 694, 610, 609,
+ 0, 631, 0, 629, 655, 0, 599, 603, 0, 0,
+ 0, 350, 352, 135, 582, 569, 594, 145, 132, 0,
+ 0, 649, 0, 648, 568, 327, 0, 633, 632, 634,
+ 0, 0, 656, 657, 618, 0, 0, 455, 458, 0,
+ 142, 0, 0, 597, 607, 341, 611, 630, 0, 655,
+ 0, 0, 0, 0, 351, 0, 0, 133, 0, 635,
+ 0, 0, 619, 658, 600, 604, 144, 143, 139, 0,
+ 659, 0, 0, 0, 0, 0, 0, 0, 660, 0,
+ 620, 601, 605, 140, 0, 0, 636, 0, 0, 639,
+ 640, 661, 621, 0, 0, 0
+};
+
+static const short yydefgoto[] = { 1344,
+ 1, 2, 119, 561, 977, 3, 4, 31, 32, 33,
+ 299, 547, 548, 34, 91, 35, 570, 572, 571, 573,
+ 569, 36, 37, 38, 411, 128, 129, 130, 338, 579,
+ 580, 535, 581, 176, 39, 40, 41, 134, 261, 262,
+ 302, 805, 303, 1141, 263, 978, 1271, 1206, 1226, 1227,
+ 1326, 1267, 292, 786, 264, 444, 496, 750, 265, 266,
+ 267, 293, 269, 506, 311, 43, 270, 456, 1043, 271,
+ 272, 273, 274, 131, 275, 979, 401, 516, 770, 980,
+ 45, 161, 981, 47, 162, 439, 163, 143, 155, 49,
+ 628, 144, 1110, 402, 1184, 156, 1111, 50, 1031, 680,
+ 681, 682, 1129, 1130, 1131, 960, 713, 714, 51, 539,
+ 288, 903, 795, 52, 53, 54, 55, 180, 181, 56,
+ 57, 58, 407, 644, 645, 646, 647, 183, 414, 415,
+ 416, 417, 658, 664, 659, 1018, 660, 661, 1019, 1020,
+ 536, 537, 497, 776, 59, 371, 372, 145, 60, 61,
+ 146, 147, 113, 63, 507, 280, 281, 282, 65, 283,
+ 67, 68, 179, 69, 284, 755, 756, 767, 519, 983,
+ 984, 1151, 828, 829, 830, 347, 985, 986, 1074, 1242,
+ 1153, 987, 988, 1179, 1075, 1243, 1076, 1244, 1108, 1286,
+ 1324, 1109, 1287, 1325, 1275, 1219, 1277, 1162, 989, 1222,
+ 1280, 1254, 1298, 1320, 1217, 1328, 990, 991, 992, 1090,
+ 720, 1282, 1283, 1284, 1330, 364, 772, 366, 367, 368,
+ 369, 107, 618, 1169, 676, 677, 434, 71, 72
+};
+
+static const short yypact[] = { 71,
+ 88,-32768,-32768, 2859,-32768, 188,-32768, 266, 64,-32768,
+-32768, 555,-32768,-32768, 140, 146,-32768, 235,-32768, 1989,
+-32768, 237,-32768, 1666, 1666,-32768, 1814,-32768,-32768, 222,
+-32768, 285, 3383,-32768,-32768,-32768,-32768, 201, 286, 315,
+-32768,-32768, 80, 1865,-32768, 4890,-32768, 846, 619,-32768,
+-32768, 142,-32768,-32768,-32768,-32768,-32768, 316, 1575,-32768,
+-32768, 675,-32768,-32768,-32768, 347,-32768,-32768,-32768, 215,
+ 3638,-32768,-32768,-32768,-32768, 8393, 4228,-32768, 266, 237,
+ 284, 340, 315,-32768, 215,-32768, 215, 8393, 8393,-32768,
+ 623,-32768, 237,-32768, 2940, 4475, 399, 215, 8393, 266,
+ 2258,-32768, 410, 245, 2258,-32768, 420, 2904, 2904, 296,
+ 325, 80, 331, 345, 348,-32768, 444, 361, 2291, 262,
+ 2940, 9527, 664, 847, 375, 475,-32768, 382, 149, 119,
+ 119,-32768,-32768, 387,-32768, 6095, 390,-32768, 3062, 3062,
+ 4178, 1327, 637,-32768,-32768, 242,-32768,-32768, 347,-32768,
+-32768,-32768,-32768, 846, 688,-32768, 1462,-32768,-32768,-32768,
+ 959, 730,-32768,-32768, 2940,-32768,-32768,-32768,-32768,-32768,
+-32768,-32768,-32768,-32768,-32768,-32768, 315, 871,-32768, 443,
+ 443,-32768, 2169,-32768, 730,-32768, 469, 861,-32768,-32768,
+-32768,-32768, 3286,-32768, 155,-32768, 419, 499,-32768,-32768,
+-32768,-32768, 463,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+-32768,-32768,-32768,-32768,-32768, 485,-32768,-32768, 730, 1575,
+ 342, 504,-32768,-32768, 3906, 9334, 542, 573,-32768,-32768,
+-32768,-32768,-32768,-32768, 577, 588, 616, 643, 646, 420,
+ 9085,-32768,-32768, 9085,-32768,-32768, 9085, 6659, 4696,-32768,
+ 41,-32768, 9085,-32768, 8480,-32768,-32768, 9696,-32768, 898,
+ 3155, 8563,-32768, 678, 713,-32768, 1174, 2042, 3006,-32768,
+ 157,-32768, 393, 808, 2940, 399,-32768, 420, 622,-32768,
+ 630, 666, 9604, 647, 656, 674, 780,-32768, 4228,-32768,
+ 443,-32768,-32768, 282,-32768, 195,-32768,-32768,-32768,-32768,
+-32768, 2258,-32768,-32768, 2258,-32768,-32768,-32768, 3286, 42,
+-32768, 686, 4228,-32768,-32768,-32768,-32768, 375, 732,-32768,
+-32768,-32768,-32768,-32768,-32768, 589,-32768, 209,-32768, 6749,
+ 8650,-32768, 119, 119, 745,-32768,-32768,-32768,-32768,-32768,
+ 803, 721,-32768,-32768, 666, 726, 9604, 74, 2165, 9527,
+ 2165, 3271, 3772, 742,-32768, 143, 9420, 763, 776,-32768,
+ 749, 8650, 3372,-32768, 3372,-32768, 3542, 3542, 748,-32768,
+-32768,-32768, 861, 2940,-32768,-32768, 6197, 751,-32768, 4114,
+ 959, 846, 2940,-32768,-32768,-32768, 861,-32768,-32768,-32768,
+ 375,-32768,-32768,-32768,-32768, 764,-32768,-32768,-32768, 8650,
+ 39, 1890, 9437, 72, 1839,-32768, 269, 375, 730, 3218,
+ 759, 832,-32768,-32768,-32768, 794, 801,-32768,-32768,-32768,
+-32768, 229,-32768,-32768, 8650, 504, 6659,-32768, 191, 6659,
+-32768, 8650, 8737, 9085, 8393, 3218, 3218, 3218, 3218,-32768,
+-32768,-32768,-32768, 767, 790, 745, 812,-32768, 8393,-32768,
+-32768, 2485, 6659,-32768, 8650, 8650, 6839, 8650, 8650, 8650,
+ 8650, 8650, 8650, 8650, 8650, 8650, 8650, 8650, 8650, 8650,
+ 8650, 8650, 8650, 8650, 8650, 8650,-32768,-32768,-32768,-32768,
+-32768, 8650, 8650, 8650, 8393, 997, 558, 225, 7388,-32768,
+-32768, 266, 873, 920,-32768, 477, 543, 631,-32768, 99,
+ 99,-32768, 3482, 817, 848, 894,-32768,-32768, 539, 7863,
+ 1305,-32768, 252,-32768,-32768, 8650,-32768,-32768,-32768,-32768,
+ 174,-32768,-32768,-32768, 893, 905,-32768,-32768, 420,-32768,
+ 7211, 7301,-32768,-32768,-32768,-32768, 320,-32768,-32768, 4012,
+ 142,-32768,-32768,-32768,-32768,-32768,-32768,-32768, 897,-32768,
+-32768,-32768, 589,-32768,-32768,-32768,-32768,-32768,-32768, 907,
+ 927, 939, 942, 832,-32768,-32768, 237, 8650, 945,-32768,
+ 559, 563, 567,-32768, 6299, 9673,-32768, 906, 119,-32768,
+-32768,-32768, 36,-32768,-32768,-32768,-32768,-32768,-32768, 2346,
+ 2346, 3148,-32768,-32768,-32768,-32768,-32768, 7954,-32768,-32768,
+-32768,-32768,-32768,-32768,-32768,-32768, 944, 949, 6749,-32768,
+-32768,-32768,-32768, 3372, 3372,-32768, 4114, 832,-32768, 803,
+ 908, 912, 913,-32768, 911,-32768, 959,-32768, 832, 589,
+-32768, 930,-32768, 962,-32768,-32768, 916,-32768, 9673,-32768,
+-32768, 961, 639,-32768, 8650, 4879, 375, 970,-32768,-32768,
+-32768, 594, 596, 971,-32768, 375, 969,-32768,-32768,-32768,
+-32768,-32768, 535,-32768, 4103,-32768, 210, 444, 943, 976,
+ 832,-32768,-32768,-32768, 424, 424, 424, 933, 934, 8824,
+ 894, 935, 938, 141, 940,-32768, 950, 951, 974, 975,
+ 985, 986,-32768,-32768, 955,-32768,-32768, 998, 849, 509,
+ 8650, 1003,-32768, 991, 968, 9673, 9673,-32768,-32768, 1004,
+ 9714, 9731, 9747, 4614, 4486, 2373, 3583, 1046, 1046, 1046,
+ 1055, 1055, 800, 800, 540, 540, 540,-32768,-32768, 973,
+ 966, 977, 980, 984, 3218, 558,-32768, 6749, 8650,-32768,
+-32768,-32768, 8650,-32768,-32768, 983, 9085, 982, 995, 1035,
+-32768, 8650,-32768, 8650,-32768, 467,-32768, 467,-32768, 70,
+ 989, 990,-32768, 988, 3218, 803,-32768, 803, 719,-32768,
+ 1540, 993, 8045, 8045, 5119, 994, 8480, 296, 1000, 348,
+ 808, 1001, 8650, 420, 996, 905,-32768, 9673,-32768, 9673,
+ 4228,-32768, 428, 1040, 316,-32768, 832,-32768,-32768,-32768,
+-32768, 732, 1005,-32768, 209,-32768, 8650,-32768, 8650,-32768,
+ 8650,-32768, 24,-32768,-32768, 420,-32768, 5723, 1071,-32768,
+ 803, 803, 2634, 2634, 2729, 2729,-32768, 347,-32768, 2804,
+ 8132, 8132, 5993, 317, 1007, 412, 803, 6749, 6749,-32768,
+ 1048,-32768,-32768,-32768,-32768, 1052, 832, 8393, 764,-32768,
+-32768,-32768, 8650, 8650, 68, 9579, 1009,-32768, 1369, 589,
+ 589, 2412, 731, 2580, 832, 3218,-32768, 60, 1025,-32768,
+-32768,-32768,-32768,-32768,-32768,-32768, 9168, 9168,-32768,-32768,
+-32768,-32768,-32768,-32768, 1027, 1038, 1047, 1053,-32768, 9555,
+ 6749, 6389, 1033,-32768, 8650,-32768,-32768,-32768,-32768, 536,
+ 1041,-32768,-32768, 1042, 265, 273, 273, 1049, 273,-32768,
+-32768, 9085, 1139,-32768, 1044, 1051,-32768,-32768,-32768,-32768,
+-32768,-32768, 803, 1056,-32768, 1050, 8219, 8219,-32768,-32768,
+ 821,-32768, 9673,-32768,-32768, 1057,-32768, 435, 2169, 732,
+-32768,-32768,-32768, 1059, 1061, 1063, 6479,-32768,-32768, 705,
+ 354, 1099,-32768,-32768,-32768, 1084,-32768, 8650, 1114, 1119,
+ 1120, 8306, 329, 524,-32768, 1127, 1177, 1129,-32768, 4142,
+ 9510, 2148, 5029,-32768,-32768, 1178,-32768,-32768,-32768, 7491,
+-32768,-32768,-32768, 2634, 2634,-32768, 2804, 1719,-32768,-32768,
+-32768, 803, 803, 803,-32768, 1130, 1089, 1092,-32768, 9579,
+ 9579,-32768,-32768,-32768, 1136, 899, 8650,-32768,-32768,-32768,
+ 375, 375, 832, 832,-32768, 1978,-32768,-32768,-32768, 569,
+ 6749, 8650, 8650, 8650, 8650, 6749,-32768, 8650, 1137,-32768,
+-32768, 4942, 387, 8650,-32768, 536,-32768,-32768,-32768,-32768,
+-32768,-32768, 1094,-32768, 1159,-32768,-32768, 803,-32768,-32768,
+-32768,-32768, 83,-32768,-32768,-32768,-32768,-32768, 420,-32768,
+-32768,-32768,-32768,-32768,-32768, 745, 6569, 1117, 5185,-32768,
+-32768,-32768,-32768, 1151, 8650, 1152,-32768,-32768,-32768, 1122,
+-32768,-32768, 31, 750,-32768, 787, 375, 8911, 34, 900,
+ 362,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768, 6749,
+ 6749,-32768,-32768,-32768,-32768,-32768, 68, 8650, 8650, 9579,
+ 589, 589, 1153, 1154,-32768,-32768,-32768,-32768, 330,-32768,
+ 1126,-32768, 1113, 1115, 1121, 1131,-32768, 9627, 6749, 387,
+-32768, 1111,-32768,-32768,-32768, 803,-32768,-32768, 582, 582,
+ 7773,-32768, 1204, 1165, 1138,-32768, 1166,-32768, 8393, 8650,
+-32768, 7593,-32768, 1168,-32768,-32768, 444,-32768, 589,-32768,
+-32768, 589, 9251, 9251, 6929,-32768,-32768,-32768, 745, 7019,
+ 7019,-32768,-32768, 6749,-32768, 9579, 9579,-32768, 832, 832,
+ 6749, 6749, 569, 1142, 8998,-32768,-32768,-32768,-32768, 6749,
+-32768,-32768,-32768,-32768, 8393,-32768,-32768,-32768,-32768,-32768,
+-32768, 5813,-32768,-32768, 1144, 257, 2940, 9650, 7593,-32768,
+-32768, 5271, 53,-32768,-32768, 1176,-32768, 1191,-32768,-32768,
+-32768, 1190, 1192,-32768,-32768,-32768,-32768, 380, 1149,-32768,
+ 1156, 745, 7683, 585, 423, 5411,-32768, 375,-32768,-32768,
+ 434,-32768, 5515,-32768, 1245, 1208,-32768,-32768, 6749, 6749,
+ 8650,-32768,-32768,-32768,-32768, 48,-32768,-32768,-32768, 8650,
+ 1209,-32768, 1213,-32768, 745, 589, 7593,-32768,-32768,-32768,
+ 1183, 8, 1217,-32768,-32768, 7109, 7109,-32768,-32768, 1173,
+-32768, 5903, 1184,-32768,-32768, 832,-32768, 1187, 8650, 1245,
+ 1223, 1245, 1188, 1189,-32768, 498, 5619,-32768, 1222,-32768,
+ 1195, 239,-32768,-32768,-32768,-32768,-32768,-32768,-32768, 1201,
+-32768, 1283, 1237, 7683, 7683, 6749, 4122, 745,-32768, 436,
+-32768,-32768,-32768,-32768, 1196, 1199,-32768, 1294, 1248,-32768,
+-32768,-32768,-32768, 1306, 1307,-32768
+};
+
+static const short yypgoto[] = {-32768,
+ 1193,-32768,-32768, 992, 18, 1303,-32768,-32768,-32768,-32768,
+-32768,-32768, 513,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+ -756, 1200, 1202,-32768,-32768,-32768,-32768, 1181,-32768,-32768,
+ 503, 0,-32768, 771,-32768, 3899, -26,-32768, 1226, 867,
+ -998,-32768, -84, 187,-32768, 196,-32768, 179, 150, -1074,
+-32768, -350, 929, 118, 753,-32768,-32768, -703, 2564, 926,
+ -251, 2283, 2895, 822, 618, 425,-32768,-32768,-32768,-32768,
+ -248,-32768, -108, -83,-32768, 255, 17, -273, 82, 21,
+ -121, -120, -3, 1868, 46, 1413, -98, -418, 352,-32768,
+ -160,-32768,-32768, 207,-32768,-32768,-32768,-32768,-32768, 66,
+-32768, 653,-32768, 144,-32768,-32768, 394, 756, 29,-32768,
+-32768,-32768, 546, -261, 37, 1302, 1310,-32768,-32768,-32768,
+-32768,-32768, -116,-32768, 479, -614,-32768, 531, 395, 471,
+ -409,-32768,-32768,-32768,-32768,-32768,-32768, 941,-32768, 474,
+ 809, 553, 860, 1539, 1625, -357,-32768, 2462, -63, 20,
+-32768, 4399, -90, 441,-32768, 3481,-32768,-32768, 4001, -4,
+ 154, -311, 1345, 3650, 789, -169,-32768, 4093,-32768, -1136,
+ -924, -335, 92,-32768, 532, -92, -128,-32768,-32768,-32768,
+ -1121, -930, -1102,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+-32768,-32768,-32768,-32768, -24,-32768,-32768,-32768,-32768,-32768,
+ -281, 67,-32768, 76,-32768, -343, -132,-32768,-32768, -289,
+ -286,-32768,-32768, 40, 490,-32768, -18,-32768, -247
+};
+
+
+#define YYLAST 9826
+
+
+static const short yytable[] = { 66,
+ 46, 348, 348, 365, 521, 670, 194, 85, 320, 552,
+ 593, 82, 553, 464, 359, 85, 96, 313, 316, 103,
+ 103, 30, 103, 290, 44, 178, 583, 422, 66, 122,
+ 635, 84, 862, 391, 194, 529, 532, 551, 349, 149,
+ 95, 66, 912, 633, 1140, 344, 231, 85, 1208, 350,
+ 501, 175, 1104, 121, 85, 951, 97, 1224, 1103, 1107,
+ 142, 501, 399, 400, 408, 195, 85, 1300, 501, 826,
+ -1, 84, 85, 96, 423, 1246, 164, 552, 957, 596,
+ 617, 826, 85, 596, 1215, 1253, 1168, -2, 521, 1176,
+ 149, 85, 650, 85, 648, 459, 103, 285, 651, 306,
+ 103, 188, 100, 103, 103, 11, 310, 667, 77, 1301,
+ 460, 300, 1255, 286, 66, 46, 149, 66, 667, 345,
+ 1265, -694, 187, 135, 597, 187, 958, -694, 597, 598,
+ 1241, -690, 360, 598, 149, 149, 149, 328, 827, 44,
+ 1268, 1140, 509, 559, 78, 79, 167, 168, 169, 21,
+ 1291, 93, 611, 1295, 1256, 1307, 774, 276, 8, 187,
+ 149, 1029, 599, 510, 336, 691, 599, 136, 137, 294,
+ 296, 706, 346, 511, 668, 574, 170, 359, 66, 413,
+ 276, 383, 164, 512, 554, 1147, 513, 514, 103, 164,
+ 164, 75, 21, 1064, 80, 171, 397, 612, 100, 392,
+ 525, 534, 613, 412, 342, 421, 1045, 93, 127, 76,
+ 240, 575, 8, 164, 348, 348, 592, 358, 8, 192,
+ 194, 1268, 1268, 126, 21, 85, 432, 88, 100, 425,
+ 419, 1220, 100, 89, 509, 614, 639, 90, 192, 172,
+ 173, 174, 891, 73, 526, 359, 498, 93, 100, 450,
+ 590, 591, 576, 356, 365, 8, 192, 164, 589, 21,
+ 127, 577, 159, 160, 876, 359, 85, 93, 12, 425,
+ 508, 637, 432, 432, 523, 21, 100, 93, 690, 423,
+ 149, 93, 529, 291, 295, 386, 313, 316, 1250, 751,
+ 74, 17, 399, 400, 85, 360, 578, 93, 1322, 752,
+ 509, 533, 192, 189, 93, 99, 164, 103, 509, 117,
+ 103, 877, 867, 753, 103, 1104, 75, 326, 85, 96,
+ 400, 1103, 1104, 21, 671, 93, 189, 705, 1103, 387,
+ 388, 78, 308, 1062, 76, 648, 459, 751, 118, 457,
+ 1323, 132, 1143, 95, 690, 100, 1297, 752, 192, 7,
+ 8, 555, 690, 421, 149, 66, 149, 149, 523, 97,
+ -226, 753, 66, 360, 327, 133, 1048, 563, 149, 182,
+ 149, 672, 149, 149, 801, 603, 1104, 607, 609, 149,
+ 549, 309, 1103, 360, 1193, 149, 287, 19, 149, 802,
+ 164, 359, 21, 289, 93, 7, 8, -221, 359, 22,
+ 421, 85, 807, 1085, -226, -226, 430, 149, 66, 1071,
+ 66, 413, 7, 100, 421, 85, 431, 1071, -221, 164,
+ 164, 189, 78, 308, 936, 339, 317, 100, 657, 26,
+ 423, 1194, 318, 19, 1261, 412, 192, 934, 8, 340,
+ 640, 85, 85, 85, 85, 22, -503, 164, 549, -222,
+ 19, 112, 234, 455, -121, -227, 1072, 673, 588, 192,
+ 348, 712, 22, 321, 1178, 642, 187, 114, 358, 341,
+ 100, 498, 309, 184, 21, 26, 93, 337, 1071, 857,
+ 359, 1262, -26, 301, 189, 21, 359, 93, 430, 1071,
+ 1338, 85, 26, 432, 370, 301, 825, -26, 431, -227,
+ -227, 674, 406, 583, 632, 432, 432, 426, 523, 360,
+ 509, 552, 875, -222, 553, 66, 360, 21, 689, 93,
+ 761, 692, 428, 365, 178, 1272, 698, 699, 700, 701,
+ 702, 510, 112, 112, 359, 464, 1278, 1339, 399, 551,
+ 276, 511, 529, 532, 715, 149, 85, 648, 114, 114,
+ 803, 512, 688, 1071, 513, 514, 420, 78, 79, 13,
+ 14, 1094, 1053, 1100, 762, 112, 609, 621, -101, 878,
+ 84, 78, 308, 1125, 1126, 1127, 744, 99, 164, 747,
+ 421, 114, 780, 1087, 712, 192, 763, 427, -79, 429,
+ 629, 435, 99, 748, -79, 149, 149, 838, 360, 638,
+ 1317, 747, 816, 838, 360, 21, 818, 80, 81, 740,
+ 820, 742, 743, 13, 14, 748, 670, 485, 486, 149,
+ 149, 309, 149, 749, 1044, 1204, 781, 782, 1269, 442,
+ 764, 1078, 455, 7, 502, 455, 648, 693, 695, 812,
+ 697, 691, 85, 792, 111, 749, 817, -282, 365, -283,
+ 819, 838, 360, -282, 821, -283, 446, 421, 455, 359,
+ 443, 192, 719, 359, 445, 164, 882, 883, 884, 1205,
+ 432, 19, 1270, 165, 166, 186, 192, 297, 298, 359,
+ 432, 432, 432, 22, 447, 187, 501, 187, 741, 75,
+ 421, 384, 385, 851, 758, 813, 868, -694, 864, -694,
+ -694, 870, 871, 164, 856, 873, -694, 76, 691, 1023,
+ 1024, 448, 1024, 26, 449, 7, 502, 330, 503, 331,
+ 541, 359, 100, 332, 538, 423, 314, 194, -694, -694,
+ -694, 540, -694, 587, -694, 159, 160, 358, 1149, 1150,
+ 85, 12, 393, 394, 313, 316, 1049, 1050, 543, 1052,
+ 313, 316, 1166, 19, 564, 13, 14, 544, 111, 1069,
+ 1070, 432, 509, 432, 17, 22, 187, 8, 641, 21,
+ 85, 93, 1180, 1181, 432, 545, 85, 360, 66, 66,
+ 66, 360, 546, 510, -78, 565, 941, 566, 560, 567,
+ -78, 568, 70, 511, 642, 504, 85, 360, 346, 643,
+ 87, 112, 112, 512, 384, 1170, 513, 514, 98, 423,
+ 527, 8, 104, 104, 21, 104, 93, 114, 114, 1212,
+ -689, 70, 594, 7, 8, 959, 911, 595, 149, 149,
+ 149, 149, 104, 619, 70, 149, 838, 838, 838, 360,
+ 87, 393, 1171, 610, 240, 528, 678, 98, 19, 626,
+ 158, 159, 160, 620, 85, 634, 715, 12, 21, 98,
+ 93, 19, 358, 679, 149, 98, 914, 103, 703, 66,
+ 413, 85, 950, 22, 482, 483, 484, 485, 486, 925,
+ 17, 926, 549, 104, 98, 13, 14, 421, 421, 104,
+ 421, 704, -327, 104, 412, 425, 104, 104, 683, 75,
+ 333, 1039, 334, 26, 1012, 684, 335, 70, -100, 104,
+ 70, 432, 432, 706, 432, -327, -327, 76, -327, 8,
+ 860, 773, 1006, 759, 403, 760, 404, 104, 104, 104,
+ 405, 1013, 66, 66, 954, 775, 955, 777, 956, 1008,
+ 1027, 487, 691, 85, 66, 413, 642, 1152, 918, 75,
+ 793, 861, 806, 104, 384, 1177, 1039, 674, 1119, 794,
+ 1189, 1190, 808, 398, 159, 160, 21, 76, 93, 412,
+ 12, 70, 1086, 1332, 1333, 149, 66, 149, 488, 489,
+ 455, 104, 809, 490, 491, 492, 493, 438, 441, 149,
+ 149, 192, 149, 17, 810, 314, 1093, 811, 1099, 815,
+ 8, 848, 10, 11, 1303, 1304, 849, 824, 12, 852,
+ 164, 639, 850, 853, 854, 855, 859, 858, 98, 104,
+ 863, 66, 413, 15, 869, 872, 112, 16, 874, 1128,
+ 879, 17, 112, 880, 885, 886, 889, 552, 455, 890,
+ 1336, 892, 114, 895, 896, 902, 412, 21, 114, 93,
+ 1152, 893, 894, 1007, 897, 898, 899, -99, 1223, 98,
+ 1121, 1122, 901, 905, 355, 104, 104, 70, 1148, 904,
+ 907, 920, 531, 104, 906, 1115, 1116, 923, 924, 908,
+ 112, 909, 313, 316, 745, 910, 922, 98, 1123, 1124,
+ 929, 930, 931, 838, 192, 939, 114, 935, 945, 948,
+ 104, 940, 942, 104, 826, 1001, 952, 104, 998, 1005,
+ 1014, 98, 1030, 1152, 1032, 1296, 478, 479, 480, 481,
+ 482, 483, 484, 485, 486, 1033, 98, 480, 481, 482,
+ 483, 484, 485, 486, 1034, 1041, 1172, 789, 421, 421,
+ 1035, 913, 1046, 1047, 1055, 1056, 1152, 104, 70, 104,
+ 104, 70, 1057, 1051, 1060, 70, 1185, 1059, 1073, 1061,
+ 1065, 104, 1066, 104, 1067, 104, 104, 1084, 838, 838,
+ 838, 1077, 104, 1080, 1081, 1082, 451, 100, 104, 452,
+ 1088, 104, 453, 1089, 1091, 1188, 421, 1112, 461, 421,
+ 1113, 1105, 1128, 1114, 98, 1118, 1139, 500, 1144, 1337,
+ 104, 70, 1145, 70, 1159, 359, 1163, 1165, 98, 1167,
+ 1191, 1192, 149, 1195, 1196, 1203, 1197, 509, 1209, 111,
+ 1210, 1213, 1198, 1221, 21, 845, 93, 1133, 1134, 1135,
+ 1136, 1257, 1199, 1248, 98, 98, 98, 98, 510, 1142,
+ 1211, 999, 1000, 1237, 164, 1247, 1258, 1259, 511, 1260,
+ 1263, 1230, 1231, 1281, 1232, 1233, 457, 1264, 512, 112,
+ 112, 513, 514, 1285, 1294, 112, 112, 112, 1071, 355,
+ 1299, 1302, 1157, 111, 1305, 114, 114, 1310, 1313, 1319,
+ 1164, 114, 114, 114, 98, 1308, 104, 1276, 1327, 1315,
+ 1316, 1329, 1331, 421, 1037, 1040, 1321, 1340, 104, 104,
+ 1341, 70, 1342, 1343, 5, 1345, 1346, 789, 791, 343,
+ 322, -468, 1239, 947, -468, 355, 562, 953, 324, 531,
+ 325, 804, 85, 360, 304, 707, 1202, 381, 1207, 765,
+ 1228, 1158, 1096, 881, 123, 949, 1236, 1009, 104, 87,
+ 823, 946, 124, 1063, 1026, 1025, 944, 796, 777, -694,
+ 1040, -694, -694, 665, 1214, 746, 86, 1292, -694, -468,
+ 993, 1309, -468, 355, -468, 1028, 1312, 0, 0, 696,
+ 455, 1015, 1016, 0, -468, 1214, 1214, 1314, 1290, 0,
+ -694, -694, 382, 0, -694, 0, -694, 0, 104, 104,
+ 70, 0, -468, -468, 0, 0, 791, -468, 0, 0,
+ 1214, 314, 0, 0, 0, 0, -468, 0, 549, 19,
+ 0, 0, 104, 104, 0, 104, 48, 0, 187, 21,
+ 355, 22, 355, 355, 1132, 0, 0, 0, 1017, 1137,
+ 0, 0, 48, 139, 0, 98, 0, 0, 0, 0,
+ 0, 0, 0, 140, 70, 48, 0, 0, 355, 0,
+ 0, 26, 0, 314, 355, 0, 141, 0, 154, 314,
+ 111, 0, 395, 104, 0, 1293, 0, 0, 0, 0,
+ 0, 185, 0, 104, 104, 104, 0, 0, 0, 0,
+ 0, 719, 719, 229, -694, 0, -694, -694, 278, 48,
+ 0, 0, 0, -694, 1311, 0, 0, 0, 0, 0,
+ 278, 278, 0, 1182, 1183, 314, 355, 0, 154, 0,
+ 0, 278, 0, 0, 0, -694, -694, 396, 0, -694,
+ 0, -694, 112, 0, 0, 0, 0, 0, 0, 0,
+ 0, 48, 1201, 98, 154, 0, 355, 0, 114, 0,
+ 0, 0, 0, 8, 92, 10, 11, 0, 362, 353,
+ 0, 12, 0, 187, 104, 0, 104, 0, 0, 0,
+ 0, 0, 0, 98, 0, 0, 15, 104, 0, 98,
+ 16, 791, 791, 791, 17, 0, 0, 1229, 8, 531,
+ 10, 184, 0, 0, 1234, 1235, 12, 0, 0, 98,
+ 21, 0, 93, 1240, 0, 48, 0, 112, 112, 112,
+ 0, 15, 0, 98, 0, 16, 0, 193, 0, 17,
+ 0, 0, 0, 114, 114, 114, 0, 0, 355, 0,
+ 0, 104, 104, 104, 104, 21, 0, 93, 104, 791,
+ 791, 791, 354, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 185, 0, 0, 0, 0, 98, 101, 105,
+ 0, 0, 1288, 1289, 319, 0, 0, 104, 0, 0,
+ 104, 0, 70, 0, 98, 0, 0, 0, 7, 100,
+ 278, 0, 11, 351, 0, 0, 0, 355, 0, 0,
+ 0, 0, 921, 229, 0, 0, 0, 0, 390, 0,
+ 355, 185, 355, 0, 0, 230, 0, 0, 0, 0,
+ 279, 0, 0, 0, 104, 104, 19, 104, 451, 452,
+ 0, 550, 279, 279, 0, 845, 21, 0, 22, 1334,
+ 0, 0, 0, 279, 0, 791, 791, 0, -508, 0,
+ 24, 0, 312, 315, 0, 48, 98, 70, 0, 0,
+ 25, 0, 0, -508, -508, 355, 0, 355, 26, 355,
+ -508, 0, 0, 27, 0, 0, 0, 0, 0, 0,
+ 363, 0, -508, 373, 375, 0, 451, 452, 104, 70,
+ 104, 0, 154, -508, -508, 185, -508, 0, -508, 550,
+ 0, 0, 104, 104, 0, 104, 0, 0, 0, 0,
+ 0, 314, 111, 0, 0, 0, 0, 0, 0, 362,
+ 0, 0, 0, 0, 0, 0, -508, -508, 0, 0,
+ -508, -508, 451, 452, 70, 0, 7, 100, 0, 0,
+ -508, 0, 0, 0, 0, 154, 0, 48, 0, 0,
+ 0, 0, 675, 0, 0, 0, 0, 0, 0, 409,
+ 0, 7, 8, 92, 10, 11, 0, 1054, 0, 278,
+ 12, 0, 278, 0, 19, 0, 0, 278, 675, 675,
+ 675, 675, 451, 452, 21, 15, 22, 7, 8, 16,
+ 0, 278, 0, 17, -420, 278, 0, 0, 108, 19,
+ 0, 0, 279, 0, 0, 0, 791, 0, 109, 21,
+ 0, 22, 652, 653, 669, 230, 26, 0, 410, 0,
+ 0, 27, 0, 24, 0, 19, 0, 278, 229, 0,
+ 0, 0, 0, 25, 0, 21, 0, 22, 627, 0,
+ 138, 26, 0, 363, 630, 550, 27, 0, 0, 139,
+ 19, 0, 362, 654, 0, 0, 0, 0, 228, 140,
+ 21, -420, 22, 277, 0, 0, 0, 26, 0, 655,
+ 0, 0, 141, 0, 139, 277, 277, 0, 0, 0,
+ 0, 791, 791, 791, 140, 0, 277, 0, 685, 686,
+ 0, 687, 26, 0, 0, 0, 0, 656, 409, 0,
+ 7, 8, 92, 10, 11, 0, 0, 0, 0, 12,
+ 0, 363, 8, 92, 10, 11, 0, 0, 0, 0,
+ 12, 624, 625, 361, 15, 104, 0, 0, 16, 0,
+ 0, 363, 17, -421, 550, 15, 0, 0, 19, 16,
+ 362, 0, 0, 17, 0, 0, 355, 0, 21, 0,
+ 22, 0, 0, 669, 0, 0, 0, 410, 0, 21,
+ 0, 93, 24, 0, 279, 100, 0, 159, 160, 0,
+ 0, 0, 25, 12, 778, 779, 0, 94, 0, 778,
+ 26, 279, 0, 0, 279, 27, 0, 0, 550, 279,
+ 279, 279, 279, 279, 0, 0, 17, 0, 0, 0,
+ -421, 0, 0, 279, 0, 509, 0, 279, 0, 0,
+ 0, 0, 21, 0, 93, 0, 0, 0, 451, 452,
+ 0, 0, 0, 0, 0, 0, 510, 0, 0, 0,
+ 0, 0, 0, 0, 0, 98, 511, 0, 0, 279,
+ 230, 0, 0, 355, 0, 277, 512, 0, 0, 520,
+ 514, 0, 831, 832, 766, 768, 0, 363, 228, 0,
+ 0, 0, 840, 0, 363, 0, 0, 0, 847, 0,
+ 7, 8, 158, 159, 160, 0, 0, 675, 0, 12,
+ 0, 915, 0, 0, 0, 0, 361, 7, 8, 409,
+ 0, 7, 8, 92, 10, 11, 0, 0, 0, 0,
+ 12, 0, 17, 0, 0, 0, 0, 675, 19, 355,
+ 0, 509, 865, 550, 0, 15, 0, 362, 21, 16,
+ 22, 0, 0, 17, -419, 19, 0, 0, 509, 19,
+ 0, 0, 139, 550, 0, 21, 0, 22, 0, 21,
+ 0, 22, 140, 0, 833, 834, 363, 0, 410, 600,
+ 26, 0, 363, 24, 361, 1098, 0, 0, 0, 601,
+ 982, 0, 0, 25, 0, 0, 0, 26, 0, 512,
+ 0, 26, 602, 514, 361, 362, 27, 0, 0, 0,
+ 7, 100, 0, 0, 184, 0, 0, 0, 0, 0,
+ 278, -419, 0, 0, 0, 0, 0, 0, 0, 0,
+ 363, 0, 0, 0, 0, 0, 48, 277, 675, 916,
+ 917, 6, 919, 7, 8, 9, 10, 11, 19, 0,
+ 0, 0, 12, 0, 277, 0, 0, 277, 21, 0,
+ 22, 0, 277, 277, 277, 277, 277, 15, 933, 0,
+ 0, 16, 24, 0, 0, 17, 277, 0, 0, 0,
+ 277, 19, 25, 0, 0, 323, 0, 0, 0, 0,
+ 26, 21, 0, 22, 0, 27, 23, 0, 7, 8,
+ 0, 0, 11, 0, 0, 24, 0, 0, 268, 0,
+ 0, 48, 277, 228, 0, 25, 0, 0, 0, 279,
+ 0, 0, 0, 26, 0, 0, 0, 997, 27, 0,
+ 361, 268, 0, 0, 28, 0, 19, 361, 0, 509,
+ 1002, 1003, 1004, 154, 0, 982, 21, 0, 22, 279,
+ 0, 0, 982, 0, 0, 363, 0, 766, 768, 363,
+ 600, 0, 0, 0, 1015, 100, 0, 0, 357, 0,
+ 601, 0, 0, 0, 0, 363, 0, 0, 26, 0,
+ 512, 0, 0, 602, 514, 0, 0, 0, 48, 474,
+ 475, 476, 477, 478, 479, 480, 481, 482, 483, 484,
+ 485, 486, 19, 0, 0, 0, 0, 0, 0, 994,
+ 995, 0, 21, 0, 22, 994, 995, 363, 1058, 361,
+ 0, 1017, 0, 0, 0, 361, 24, 0, 0, 0,
+ 0, 0, 279, 0, 0, 584, 25, 708, 709, 982,
+ 10, 436, 233, 234, 26, 235, 12, 0, 0, 27,
+ 279, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 15, 236, 237, 238, 16, 0, 239, 0, 17,
+ 0, 240, 241, 361, 242, 19, 243, 244, 0, 0,
+ 245, 246, 247, 248, 249, 21, 0, 710, 585, 0,
+ 0, 458, 0, 0, 0, 0, 250, 0, 0, 251,
+ 0, 0, 1117, 0, 0, 0, 0, 252, 253, 254,
+ 0, 766, 768, 982, 0, 255, 256, 257, 0, 0,
+ 0, 1216, 258, 711, 982, 0, 259, 0, 0, 0,
+ 409, 0, 7, 8, 92, 10, 11, 278, 260, 0,
+ 0, 12, 1216, 1216, 0, 0, 0, 1146, 0, 0,
+ 374, 376, 379, 0, 0, 0, 15, 0, 0, 0,
+ 16, 0, 277, 0, 17, -422, 0, 1216, 0, 0,
+ 19, 0, 586, 357, 982, 0, 0, 0, 0, 0,
+ 21, 982, 22, 0, 982, 0, 7, 8, 0, 410,
+ 184, 0, 277, 0, 24, 0, 0, 0, 361, 0,
+ 0, 0, 361, 0, 25, 982, 0, 0, 982, 0,
+ 0, 0, 26, 0, 0, 982, 0, 27, 361, 357,
+ 0, 0, 0, 0, 19, 0, 0, 509, 0, 0,
+ 0, 0, -422, 0, 21, 0, 22, 0, 0, 982,
+ 0, 0, 649, 0, 0, 0, 0, 0, 600, 0,
+ 0, 0, 0, 0, 982, 0, 0, 0, 601, 0,
+ 361, 0, 0, 0, 0, 0, 26, 357, 512, 982,
+ 0, 602, 514, 0, 0, 277, 0, 0, 0, 0,
+ 0, 7, 8, 0, 0, 11, 982, 982, 0, 550,
+ 0, 268, 0, 277, 586, 0, 0, 716, 717, 0,
+ 721, 722, 723, 724, 725, 726, 727, 728, 729, 730,
+ 731, 732, 733, 734, 735, 736, 737, 738, 739, 19,
+ 0, 0, 509, 0, 357, 0, 357, 357, 0, 21,
+ 0, 22, 0, 230, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 835, 0, 0, 0, 624, 625, 279,
+ 0, 0, 357, 836, 230, 230, 7, 8, 357, 0,
+ 423, 26, 0, 512, 0, 0, 602, 514, 0, 0,
+ 0, 0, 0, 798, 800, 0, 0, 0, 0, 230,
+ 0, 0, 0, 0, 622, 0, 623, 0, 374, 376,
+ 515, 515, 515, 0, 19, 0, 0, 509, 0, 0,
+ 0, 636, 0, 0, 21, 0, 22, 0, 0, 6,
+ 357, 7, 8, 9, 10, 11, 0, 586, 600, 0,
+ 12, 0, 0, 662, 0, 0, 0, 0, 601, 0,
+ 0, 0, 0, 13, 14, 15, 26, 0, 512, 16,
+ 357, 602, 514, 17, 0, 0, 0, 18, 42, 19,
+ 0, 586, 0, 20, 0, 0, 7, 100, 0, 21,
+ 11, 22, 0, 0, 23, 0, 0, 0, 42, 42,
+ 0, 110, 515, 24, 515, 515, 515, 42, 0, 0,
+ 0, 0, 0, 25, 0, 0, 0, 866, 42, 0,
+ 42, 26, 7, 8, 19, 0, 27, 0, 0, 0,
+ 0, 363, 28, 29, 21, 0, 22, 0, 0, 0,
+ 190, 0, 0, 0, 0, 0, 0, 0, 108, 0,
+ 0, 0, 357, 0, 0, 0, 0, 0, 109, 0,
+ 19, 0, 0, 0, 0, 0, 26, 0, 0, 42,
+ 21, 27, 22, 900, 0, 42, 0, 190, 0, 42,
+ 0, 0, 110, 110, 139, 0, 0, 0, 0, 8,
+ 0, 10, 184, 42, 140, 42, 42, 12, 0, 0,
+ 0, 0, 26, 0, 0, 0, 228, 141, 0, 0,
+ 586, 357, 15, 42, 42, 110, 16, 0, 0, 0,
+ 17, 0, 277, 190, 357, 0, 357, 228, 228, 509,
+ 0, 0, 0, 0, 0, 0, 21, 0, 93, 42,
+ 0, 374, 376, 379, 7, 8, 0, 0, 11, 379,
+ 510, 0, 228, 515, 515, 943, 515, 42, 0, 0,
+ 511, 0, 0, 787, 0, 622, 623, 42, 636, 0,
+ 512, 0, 0, 513, 514, 0, 0, 0, 0, 357,
+ 0, 357, 19, 357, 0, 0, 0, 0, 0, 0,
+ 0, 0, 21, 515, 22, 0, 0, 379, 0, 0,
+ 0, 0, 0, 0, 0, 0, 139, 0, 0, 0,
+ 586, 586, 0, 0, 0, 0, 140, 0, 0, 0,
+ 0, 0, 0, 0, 26, 1010, 1011, 0, 0, 141,
+ 7, 8, 92, 10, 11, 0, 0, 353, 8, 12,
+ 10, 11, 0, 515, 515, 515, 12, 0, 0, 505,
+ 0, 787, 0, 0, 15, 0, 0, 190, 16, 42,
+ 0, 15, 17, 586, 586, 16, 0, 1042, 19, 17,
+ 0, 509, 0, 0, 361, 0, 0, 0, 21, 0,
+ 22, 0, 0, 0, 0, 21, 42, 93, 494, 42,
+ 0, 0, 835, 42, 0, 0, 0, 0, 0, 0,
+ 0, 8, 836, 10, 11, 0, 0, 0, 0, 12,
+ 26, 0, 512, 0, 0, 602, 514, 0, 515, 586,
+ 354, 0, 495, 0, 15, 0, 0, 0, 16, 0,
+ 1079, 0, 17, 42, 42, 42, 42, 0, 0, 0,
+ 0, 42, 0, 0, 0, 0, 0, 42, 21, 42,
+ 93, 110, 110, 7, 8, 158, 159, 160, 42, 0,
+ 0, 0, 12, 0, 42, 0, 0, 42, 7, 100,
+ 0, 0, 423, 0, 622, 623, 374, 376, 0, 1120,
+ 0, 636, 374, 376, 379, 17, 42, 42, 0, 42,
+ 0, 19, 0, 586, 509, 0, 0, 0, 586, 0,
+ 1138, 21, 0, 22, 0, 0, 19, 0, 0, 515,
+ 1021, 515, 0, 0, 0, 600, 21, 0, 22, 0,
+ 0, 0, 515, 0, 0, 601, 787, 787, 787, 0,
+ 24, 0, 0, 26, 0, 512, 0, 0, 608, 514,
+ 25, 0, 0, 0, 0, 0, 0, 0, 26, 0,
+ 0, 0, 0, 27, 7, 8, 0, 0, 184, 0,
+ 357, 0, 0, 120, 0, 7, 8, 92, 10, 11,
+ 0, 0, 586, 586, 12, 0, 515, 515, 515, 515,
+ 1186, 1187, 505, 515, 787, 787, 787, 0, 0, 15,
+ 0, 0, 19, 16, 788, 0, 0, 17, 0, 0,
+ 0, 586, 21, 19, 22, 0, 0, 0, 0, 0,
+ 0, 0, 0, 21, 0, 22, 139, 0, 0, 0,
+ 0, 0, 1218, 0, 42, 0, 140, 24, 0, 0,
+ 0, 0, 0, 0, 26, 622, 623, 25, 636, 141,
+ 0, 0, 0, 0, 0, 26, 586, 0, 0, 0,
+ 27, 0, 0, 586, 586, 0, 0, 357, 0, 0,
+ 0, 0, 586, 0, 64, 8, 92, 10, 11, 0,
+ 0, 353, 0, 12, 42, 42, 110, 0, 0, 0,
+ 787, 787, 844, 0, 64, 64, 0, 115, 15, 0,
+ 0, 0, 16, 64, 0, 0, 17, 0, 42, 42,
+ 0, 42, 0, 0, 64, 509, 64, 0, 0, 0,
+ 0, 0, 21, 0, 93, 0, 0, 0, 0, 0,
+ 0, 586, 586, 357, 7, 8, 510, 0, 11, 0,
+ 110, 0, 0, 0, 0, 0, 511, 515, 515, 379,
+ 515, 0, 0, 0, 0, 0, 512, 0, 0, 513,
+ 514, 0, 0, 0, 354, 64, 0, 0, 0, 0,
+ 0, 64, 19, 0, 788, 64, 0, 0, 115, 115,
+ 0, 0, 21, 0, 22, 0, 0, 0, 0, 64,
+ 0, 64, 64, 0, 0, 0, 377, 0, 586, 0,
+ 0, 0, 0, 0, 0, 0, 378, 0, 0, 64,
+ 64, 115, 0, 0, 26, 0, 0, 0, 0, 141,
+ 0, 0, 0, 0, 374, 376, 379, 0, 196, 0,
+ 0, 8, 0, 10, 11, 64, 0, 0, 0, 12,
+ 475, 476, 477, 478, 479, 480, 481, 482, 483, 484,
+ 485, 486, 0, 64, 15, 0, 0, 0, 16, 0,
+ 0, 0, 17, 64, 197, 198, 116, 788, 788, 0,
+ 0, 199, 0, 0, 0, 0, 0, 0, 21, 0,
+ 93, 0, 200, 150, 201, 202, 203, 0, 204, 205,
+ 206, 207, 208, 209, 210, 211, 212, 213, 214, 215,
+ 216, 217, 218, 219, 220, 221, 0, 0, 222, 223,
+ 224, 0, 0, 225, 0, 0, 226, 42, 42, 110,
+ 110, 0, 190, 0, 42, 844, 844, 844, 0, 0,
+ 0, 227, 0, 0, 150, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 116, 116, 0,
+ 0, 0, 0, 42, 530, 64, 42, 0, 42, 0,
+ 150, 0, 0, 0, -301, 8, -301, 10, 184, 0,
+ 0, 788, 788, 12, 0, 0, 0, 0, 150, 150,
+ 380, 0, 64, 0, 0, 64, 0, 0, 15, 64,
+ 0, 0, 16, 0, 0, 0, 17, 0, 0, 0,
+ 0, 0, -301, 0, 150, 509, 0, 0, 582, 0,
+ 0, 0, 21, 0, 93, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 190, 510, 0, 0, 64,
+ 64, 64, 64, 42, 0, 0, 511, 64, 0, 0,
+ 0, 0, 0, 64, -301, 64, 512, 115, 115, 513,
+ 514, 0, 0, 0, 64, 0, 0, 0, 0, 0,
+ 64, 0, 0, 64, 42, 42, 42, 0, 0, 0,
+ 433, 0, 0, 0, 0, 0, 0, 0, 42, 42,
+ 0, 42, 64, 64, 0, 64, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 7, 8,
+ 83, 10, 436, 233, 234, 0, 235, 12, 0, 0,
+ 42, 0, 83, 83, 0, 83, 517, 517, 517, 0,
+ 0, 0, 15, 236, 150, 0, 16, 0, 239, 0,
+ 17, 0, 240, 241, 0, 242, 19, 243, 244, 0,
+ 177, 245, 246, 247, 248, 249, 21, 0, 22, 0,
+ 0, 0, 0, 0, 0, 0, 0, 250, 0, 0,
+ 251, 0, 0, 0, 0, 0, 0, 0, 252, 253,
+ 254, 0, 0, 0, 0, 0, 255, 256, 257, 0,
+ 0, 0, 844, 437, 0, 0, 0, 0, 0, 83,
+ 790, 83, 83, 83, 0, 0, 83, 83, 604, 260,
+ 604, 604, 517, 0, 7, 8, 158, 159, 160, 0,
+ 0, 0, 150, 12, 150, 0, 380, 380, 0, 0,
+ 64, 0, 0, 150, 0, 0, 0, 0, 0, 150,
+ 0, 0, 150, 0, 148, 0, 17, 0, 0, 0,
+ 0, 0, 19, 0, 0, 509, 0, 0, 0, 0,
+ 0, 150, 21, 0, 22, 0, 191, 844, 844, 844,
+ 0, 0, 0, 0, 0, 0, 600, 0, 0, 0,
+ 64, 64, 115, 0, 0, 191, 601, 0, 846, 0,
+ 0, 83, 0, 0, 26, 148, 512, 0, 0, 602,
+ 514, 0, 0, 0, 64, 64, 100, 64, 159, 160,
+ 0, 42, 0, 0, 12, 0, 7, 8, 0, 0,
+ 423, 148, 0, 0, 0, 8, 92, 10, 11, 83,
+ 0, 1335, 0, 12, 0, 0, 115, 17, 0, 148,
+ 148, 148, 0, 0, 7, 8, 509, 754, 15, 389,
+ 0, 0, 16, 21, 19, 93, 17, 0, 0, 517,
+ 517, 0, 517, 0, 21, 148, 22, 510, 0, 517,
+ 790, 0, 21, 83, 93, 83, 83, 511, 139, 0,
+ 7, 8, 19, 0, 0, 0, 0, 512, 140, 0,
+ 513, 514, 21, 0, 22, 191, 26, 1092, 0, 604,
+ 0, 141, 0, 0, 83, 0, 139, 0, 0, 0,
+ 83, 0, 0, 83, 0, 0, 140, 83, 19, 0,
+ 0, 0, 0, 0, 26, 0, 0, 0, 21, 141,
+ 22, 8, 92, 10, 11, 0, 0, 0, 0, 12,
+ 0, 0, 377, 0, 0, 0, 0, 0, 0, 604,
+ 604, 839, 378, 0, 15, 0, 0, 839, 16, 0,
+ 26, 0, 17, 790, 790, 141, 0, 0, 0, 0,
+ 0, 530, 0, 150, 150, 0, 150, 0, 21, 0,
+ 93, 0, 0, 191, 0, 148, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 582, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 380, 0, 0, 0, 0,
+ 0, 0, 0, 64, 64, 115, 115, 0, 0, 0,
+ 64, 846, 846, 846, 517, 0, 0, 0, 0, 0,
+ 83, 0, 0, 0, 433, 433, 433, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 64,
+ 0, 0, 64, 0, 64, 0, 0, 0, 0, 148,
+ 0, 148, 148, 0, 0, 0, 0, 790, 790, 518,
+ 522, 524, 0, 148, 0, 148, 0, 148, 148, 0,
+ 0, 0, 0, 0, 148, 0, 0, 0, 0, 0,
+ 148, 0, 0, 148, 0, 0, 83, 0, 0, 0,
+ 0, 0, 62, 0, 0, 0, 83, 0, 83, 83,
+ 0, 0, 148, 0, 0, 517, 0, 517, 0, 0,
+ 0, 0, 102, 106, 0, 0, 0, 0, 517, 64,
+ 0, 125, 517, 517, 517, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 157, 0, 0, 0, 0, 177,
+ 0, 605, 0, 518, 522, 524, 0, 0, 0, 0,
+ 64, 64, 64, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 64, 64, 0, 64, 8, 151,
+ 10, 152, 604, 604, 839, 839, 12, 0, 0, 604,
+ 839, 839, 839, 0, 0, 0, 0, 0, 0, 305,
+ 0, 15, 0, 307, 0, 16, 64, 0, 0, 17,
+ 0, 0, 0, 0, 0, 0, 0, 62, 150, 0,
+ 329, 0, 0, 191, 0, 21, 0, 93, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 102, 106, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 148, 473, 474, 475, 476, 477, 478, 479, 480, 481,
+ 482, 483, 484, 485, 486, 754, 754, 0, 754, 0,
+ 0, 0, 0, 83, 0, 0, 0, 0, 846, 0,
+ 0, 418, 0, 83, 83, 83, 517, 517, 0, 0,
+ 0, 424, 0, 0, 0, 0, 0, 0, 0, 0,
+ 148, 148, 837, 769, 0, 771, 0, 0, 837, 0,
+ 0, 0, 771, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 148, 148, 0, 148, 0, 150,
+ 0, 150, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 522, 604, 604, 0, 604, 0, 0, 0,
+ 0, 0, 0, 846, 846, 846, 837, 0, 0, 0,
+ 0, 0, 0, 0, 83, 0, 83, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 83, 472, 473,
+ 474, 475, 476, 477, 478, 479, 480, 481, 482, 483,
+ 484, 485, 486, 769, 771, 0, 0, 64, 7, 8,
+ 771, 10, 436, 233, 234, 0, 235, 12, 0, 0,
+ 556, 0, 0, 557, 0, 0, 0, 558, 0, 0,
+ 0, 0, 15, 236, 237, 238, 16, 0, 239, 0,
+ 17, 0, 240, 241, 0, 242, 19, 243, 244, 0,
+ 0, 245, 246, 247, 248, 249, 21, 380, 22, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 606, 0,
+ 0, 0, 0, 0, 0, 616, 0, 522, 0, 0,
+ 83, 305, 0, 307, 0, 0, 255, 256, 257, 0,
+ 0, 0, 0, 258, 0, 0, 0, 259, 424, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 260,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 663, 666, 0, 418, 83, 83, 0, 83, 0, 0,
+ 0, 0, 380, 380, 380, 0, 0, 0, 0, 0,
+ 0, 0, 0, 148, 148, 148, 148, 0, 996, 0,
+ 148, 837, 837, 837, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 927, 0,
+ 928, 0, 0, 0, 0, 0, 150, 0, 0, 148,
+ 0, 932, 0, 0, 0, 0, 769, 771, 0, 0,
+ 0, 7, 8, 92, 10, 11, 0, 0, 353, 0,
+ 12, 0, 7, 8, 151, 10, 152, 0, 0, 0,
+ 0, 12, 0, 0, 0, 15, 0, 0, 0, 16,
+ 0, 0, 0, 17, 0, 0, 15, 0, 0, 19,
+ 16, 0, 0, 0, 17, 927, 928, 0, 769, 21,
+ 19, 22, 932, 0, 769, 771, 0, 0, 0, 0,
+ 21, 191, 22, 377, 0, 153, 0, 0, 0, 0,
+ 0, 0, 0, 378, 24, 0, 0, 0, 0, 0,
+ 0, 26, 0, 0, 25, 0, 141, 0, 0, 0,
+ 0, 354, 26, 0, 0, 0, 0, 27, 0, 0,
+ 148, 0, 148, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 148, 148, 0, 148, 102, 106,
+ 467, 0, 468, 469, 470, 471, 472, 473, 474, 475,
+ 476, 477, 478, 479, 480, 481, 482, 483, 484, 485,
+ 486, 0, 556, 557, 0, 558, 0, 0, 0, 1101,
+ 769, 962, 8, 92, 10, 232, 233, 234, 0, 235,
+ 12, 963, 0, 964, 965, 966, 967, 968, 969, 970,
+ 971, 972, 973, 13, 14, 15, 236, 237, 238, 16,
+ 0, 239, 0, 17, 0, 240, 241, 0, 242, 19,
+ 243, 244, 0, 0, 245, 246, 247, 248, 249, 21,
+ 0, 974, 346, 0, 975, 0, 927, 928, 0, 932,
+ 250, 0, 0, 251, 0, 0, 0, 0, 837, 0,
+ 0, 252, 253, 254, 0, 0, 0, 0, 0, 255,
+ 256, 257, 0, 0, 0, 0, 258, 0, 976, 454,
+ 259, 7, 8, 92, 10, 232, 233, 234, 353, 235,
+ 12, 1102, 260, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 15, 236, 237, 238, 16,
+ 0, 239, 0, 17, 0, 240, 241, 0, 242, 19,
+ 243, 244, 509, 0, 245, 246, 247, 248, 249, 21,
+ 0, 22, -234, 837, 837, 837, 0, 0, 0, 0,
+ 250, 0, 0, 937, 0, 0, 0, 0, 0, 0,
+ 0, 252, 253, 938, 1160, 0, 0, 0, 0, 255,
+ 256, 257, 0, 512, 0, 0, 785, 514, 0, 0,
+ 259, 354, 0, 0, 0, 0, 0, 148, 0, 0,
+ 0, 0, 260, 0, 0, 0, 0, 0, 0, 0,
+ 0, 305, 307, 0, 0, 0, 0, 0, 424, 0,
+ 0, 465, 466, 467, 1161, 468, 469, 470, 471, 472,
+ 473, 474, 475, 476, 477, 478, 479, 480, 481, 482,
+ 483, 484, 485, 486, 0, 0, 0, 1022, 0, 0,
+ 1022, 1251, 418, 962, 8, 92, 10, 232, 233, 234,
+ 0, 235, 12, 963, 0, 964, 965, 966, 967, 968,
+ 969, 970, 971, 972, 973, 13, 14, 15, 236, 237,
+ 238, 16, 0, 239, 0, 17, 0, 240, 241, 0,
+ 242, 19, 243, 244, 0, 0, 245, 246, 247, 248,
+ 249, 21, 0, 974, 346, 0, 975, 0, 0, 0,
+ 0, 0, 250, 0, 0, 251, 0, 0, 0, 0,
+ 0, 0, 0, 252, 253, 254, 0, 418, 0, 0,
+ 0, 255, 256, 257, 0, 0, 0, 0, 258, 0,
+ 976, 0, 259, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1252, 260, 0, 0, 0, 0, 1097,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 556, 557, 0, 558, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1273, 0, 962, 8, 92, 10, 232, 233, 234,
+ 0, 235, 12, 963, 418, 964, 965, 966, 967, 968,
+ 969, 970, 971, 972, 973, 13, 14, 15, 236, 237,
+ 238, 16, 0, 239, 0, 17, 0, 240, 241, 0,
+ 242, 19, 243, 244, 0, 0, 245, 246, 247, 248,
+ 249, 21, 0, 974, 346, 0, 975, 0, 0, 0,
+ 0, 0, 250, 0, 0, 251, 0, 0, 0, 0,
+ 0, 0, 0, 252, 253, 254, 0, 0, 0, 0,
+ 0, 255, 256, 257, 0, 0, 0, 0, 258, 0,
+ 976, 0, 259, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1274, 260, 1273, 0, 962, 8, 92,
+ 10, 232, 233, 234, 0, 235, 12, 963, 0, 964,
+ 965, 966, 967, 968, 969, 970, 971, 972, 973, 13,
+ 14, 15, 236, 237, 238, 16, 0, 239, 0, 17,
+ 0, 240, 241, 0, 242, 19, 243, 244, 0, 0,
+ 245, 246, 247, 248, 249, 21, 0, 974, 346, 0,
+ 975, 0, 0, 0, 0, 0, 250, 0, 0, 251,
+ 0, 0, 0, 0, 0, 0, 0, 252, 253, 254,
+ 0, 0, 0, 0, 0, 255, 256, 257, 0, 0,
+ 0, 0, 258, 0, 976, 0, 259, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1279, 260, 1273,
+ 0, 962, 8, 92, 10, 232, 233, 234, 0, 235,
+ 12, 963, 0, 964, 965, 966, 967, 968, 969, 970,
+ 971, 972, 973, 13, 14, 15, 236, 237, 238, 16,
+ 0, 239, 0, 17, 0, 240, 241, 0, 242, 19,
+ 243, 244, 0, 0, 245, 246, 247, 248, 249, 21,
+ 0, 974, 346, 0, 975, 0, 0, 0, 0, 0,
+ 250, 0, 0, 251, 0, 0, 0, 0, 0, 0,
+ 0, 252, 253, 254, 0, 0, 0, 0, 0, 255,
+ 256, 257, 0, 0, 0, 0, 258, 0, 976, 0,
+ 259, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1318, 260, 961, 0, 962, 8, 92, 10, 232,
+ 233, 234, 0, 235, 12, 963, 0, 964, 965, 966,
+ 967, 968, 969, 970, 971, 972, 973, 13, 14, 15,
+ 236, 237, 238, 16, 0, 239, 0, 17, 0, 240,
+ 241, 0, 242, 19, 243, 244, 0, 0, 245, 246,
+ 247, 248, 249, 21, 0, 974, 346, 0, 975, 0,
+ 0, 0, 0, 0, 250, 0, 0, 251, 0, 0,
+ 0, 0, 0, 0, 0, 252, 253, 254, 0, 0,
+ 0, 0, 0, 255, 256, 257, 0, 0, 0, 0,
+ 258, 0, 976, 1245, 259, 962, 8, 92, 10, 232,
+ 233, 234, 0, 235, 12, 963, 260, 964, 965, 966,
+ 967, 968, 969, 970, 971, 972, 973, 13, 14, 15,
+ 236, 237, 238, 16, 0, 239, 0, 17, 0, 240,
+ 241, 0, 242, 19, 243, 244, 0, 0, 245, 246,
+ 247, 248, 249, 21, 0, 974, 346, 0, 975, 0,
+ 0, 0, 0, 0, 250, 0, 0, 251, 0, 0,
+ 0, 0, 0, 0, 0, 252, 253, 254, 0, 0,
+ 0, 0, 0, 255, 256, 257, 0, 0, 0, 0,
+ 258, 0, 976, 1306, 259, 962, 8, 92, 10, 232,
+ 233, 234, 0, 235, 12, 963, 260, 964, 965, 966,
+ 967, 968, 969, 970, 971, 972, 973, 13, 14, 15,
+ 236, 237, 238, 16, 0, 239, 0, 17, 0, 240,
+ 241, 0, 242, 19, 243, 244, 0, 0, 245, 246,
+ 247, 248, 249, 21, 0, 974, 346, 0, 975, 0,
+ 0, 0, 0, 0, 250, 0, 0, 251, 0, 0,
+ 0, 0, 0, 0, 0, 252, 253, 254, 0, 0,
+ 0, 0, 0, 255, 256, 257, 0, 0, 0, 0,
+ 258, 0, 976, 454, 259, 7, 8, 92, 10, 232,
+ 233, 234, 353, 235, 12, 0, 260, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 15,
+ 236, 237, 238, 16, 0, 239, 0, 17, 0, 240,
+ 241, 0, 242, 19, 243, 244, 509, 0, 245, 246,
+ 247, 248, 249, 21, 0, 22, -234, 0, 0, 0,
+ 0, 0, 0, 0, 250, 0, 0, 841, 0, 0,
+ 0, 0, 0, 0, 0, 252, 253, 842, 0, 0,
+ 0, 0, 0, 255, 256, 257, 0, 512, 0, 0,
+ 843, 514, 0, 0, 259, 354, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 352, 260, 7, 8, 92,
+ 10, 232, 233, 234, 353, 235, 12, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 15, 236, 237, 238, 16, 0, 239, 0, 17,
+ 0, 240, 241, 0, 242, 19, 243, 244, 0, 0,
+ 245, 246, 247, 248, 249, 21, 0, 22, 0, 0,
+ 0, 0, 0, 0, 0, 0, 250, 0, 0, 251,
+ 0, 0, 0, 0, 0, 0, 0, 252, 253, 254,
+ 0, 0, 0, 0, 0, 255, 256, 257, 0, 0,
+ 0, 0, 258, 0, 0, 0, 259, 354, 0, 0,
+ 0, 0, 0, 0, 0, 0, -662, 631, 260, 7,
+ 8, 92, 10, 232, 233, 234, 353, 235, 12, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 15, 236, 237, 238, 16, 0, 239,
+ 0, 17, 0, 240, 241, 0, 242, 19, 243, 244,
+ 0, 0, 245, 246, 247, 248, 249, 21, 0, 22,
+ 0, 0, 0, 0, 0, 0, 0, 0, 250, 0,
+ 0, 251, 0, 0, 0, 0, 0, 0, 0, 252,
+ 253, 254, 0, 0, 0, 0, 0, 255, 256, 257,
+ 0, 0, 0, 0, 258, 0, 0, 0, 259, 354,
+ 0, 0, 0, 0, 0, 0, 0, 0, -662, 584,
+ 260, 708, 709, 0, 10, 436, 233, 234, 0, 235,
+ 12, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 15, 236, 237, 238, 16,
+ 0, 239, 0, 17, 0, 240, 241, 0, 242, 19,
+ 243, 244, 0, 0, 245, 246, 247, 248, 249, 21,
+ 0, 710, 585, 0, 0, 0, 0, 0, 0, 0,
+ 250, 0, 0, 251, 0, 0, 0, 0, 0, 0,
+ 0, 252, 253, 254, 0, 0, 0, 0, 0, 255,
+ 256, 257, 0, 0, 0, 0, 258, 711, 0, 584,
+ 259, 708, 709, 0, 10, 436, 233, 234, 0, 235,
+ 12, 822, 260, 0, 0, 0, 0, 1038, 0, 0,
+ 0, 0, 0, 0, 0, 15, 236, 237, 238, 16,
+ 0, 239, 0, 17, 0, 240, 241, 0, 242, 19,
+ 243, 244, 0, 0, 245, 246, 247, 248, 249, 21,
+ 0, 710, 585, 0, 0, 0, 0, 0, 0, 0,
+ 250, 0, 0, 251, 0, 0, 0, 0, 0, 0,
+ 0, 252, 253, 254, 0, 0, 0, 0, 0, 255,
+ 256, 257, 0, 0, 0, 0, 258, 0, 0, 584,
+ 259, 708, 709, 0, 10, 436, 233, 234, 0, 235,
+ 12, -381, 260, 0, 0, 0, 0, 1038, 0, 0,
+ 0, 0, 0, 0, 0, 15, 236, 237, 238, 16,
+ 0, 239, 0, 17, 0, 240, 241, 0, 242, 19,
+ 243, 244, 0, 0, 245, 246, 247, 248, 249, 21,
+ 0, 710, 585, 0, 0, 0, 0, 0, 0, 0,
+ 250, 0, 0, 251, 0, 0, 0, 0, 0, 0,
+ 0, 252, 253, 254, 0, 0, 0, 0, 0, 255,
+ 256, 257, 0, 0, 0, 0, 258, 0, 0, 1154,
+ 259, 7, 8, 92, 10, 232, 233, 234, 0, 235,
+ 12, 1068, 260, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 15, 236, 237, 238, 16,
+ 0, 239, 0, 17, 0, 240, 241, 0, 242, 19,
+ 243, 244, 0, 0, 245, 246, 247, 248, 249, 21,
+ 0, 22, 1155, 0, 1156, 0, 0, 0, 0, 0,
+ 250, 0, 0, 251, 0, 0, 0, 0, 0, 0,
+ 0, 252, 253, 254, 0, 0, 0, 0, 0, 255,
+ 256, 257, 0, 0, 0, 0, 258, 0, 0, 454,
+ 259, 7, 8, 0, 10, 232, 233, 234, 0, 235,
+ 12, 0, 260, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 15, 236, 237, 238, 16,
+ 0, 239, 0, 17, 0, 240, 241, 0, 242, 19,
+ 243, 244, 0, 0, 245, 246, 247, 248, 249, 21,
+ 0, 22, -234, 0, 0, 0, 0, 0, 0, 0,
+ 250, 0, 0, 251, 0, 0, 0, 0, 0, 0,
+ 0, 252, 253, 254, 0, 0, 0, 0, 0, 255,
+ 256, 257, 0, 0, 0, 0, 258, 0, 0, 584,
+ 259, 7, 8, 0, 10, 436, 233, 234, 0, 235,
+ 12, 0, 260, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 15, 236, 237, 238, 16,
+ 0, 239, 0, 17, 0, 240, 241, 0, 242, 19,
+ 243, 244, 0, 0, 245, 246, 247, 248, 249, 21,
+ 0, 22, 585, 0, 0, 0, 0, 0, 0, 0,
+ 250, 0, 0, 251, 0, 0, 0, 0, 0, 0,
+ 0, 252, 253, 254, 0, 0, 0, 0, 0, 255,
+ 256, 257, 0, 0, 0, 0, 258, 0, 0, 718,
+ 259, 7, 8, 0, 10, 436, 233, 234, 0, 235,
+ 12, 0, 260, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 15, 236, 237, 238, 16,
+ 0, 239, 0, 17, 0, 240, 241, 0, 242, 19,
+ 243, 244, 0, 0, 245, 246, 247, 248, 249, 21,
+ 0, 22, 0, 0, 0, 0, 0, 0, -652, 0,
+ 250, 0, 0, 251, 0, 0, 0, 0, 0, 0,
+ 0, 252, 253, 254, 0, 0, 0, 0, 0, 255,
+ 256, 257, 0, 0, 0, 0, 258, 0, 0, 454,
+ 259, 7, 8, 0, 10, 232, 233, 234, 0, 235,
+ 12, 0, 260, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 15, 236, 237, 238, 16,
+ 0, 239, 0, 17, 0, 240, 241, 0, 242, 19,
+ 243, 244, 0, 0, 245, 246, 247, 248, 249, 21,
+ 0, 22, -234, 0, 0, 0, 0, 0, 0, 0,
+ 250, 0, 0, 1173, 0, 0, 0, 0, 0, 0,
+ 0, 252, 253, 1174, 0, 0, 0, 0, 0, 255,
+ 256, 257, 0, 0, 0, 0, 1175, 0, 0, 1225,
+ 259, 7, 8, 0, 10, 232, 233, 234, 0, 235,
+ 12, 0, 260, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 15, 236, 237, 238, 16,
+ 0, 239, 0, 17, 0, 240, 241, 0, 242, 19,
+ 243, 244, 0, 0, 245, 246, 247, 248, 249, 21,
+ 0, 22, 0, 0, -136, 0, 0, 0, 0, 0,
+ 250, 0, 0, 251, 0, 0, 0, 0, 0, 0,
+ 0, 252, 253, 254, 0, 0, 0, 0, 0, 255,
+ 256, 257, 0, 0, 0, 0, 258, 0, 0, 718,
+ 259, 7, 8, 0, 10, 436, 233, 234, 0, 235,
+ 12, 0, 260, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 15, 236, 237, 238, 16,
+ 0, 239, 0, 17, 0, 240, 241, 0, 242, 19,
+ 243, 244, 0, 0, 245, 246, 247, 248, 249, 21,
+ 0, 22, 0, 0, 0, 0, 0, 0, 0, 0,
+ 250, 0, 0, 251, 0, 0, 0, 0, 0, 0,
+ 0, 252, 253, 254, 0, 0, 0, 0, 0, 255,
+ 256, 257, 0, 0, 0, 0, 258, 0, 0, 0,
+ 259, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ -652, 797, 260, 7, 8, 0, 10, 436, 233, 234,
+ 0, 235, 12, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 15, 236, 237,
+ 238, 16, 0, 239, 0, 17, 0, 240, 241, 0,
+ 242, 19, 243, 244, 0, 0, 245, 246, 247, 248,
+ 249, 21, 0, 22, 0, 0, 0, 0, 0, 0,
+ 0, 0, 250, 0, 0, 251, 0, 0, 0, 0,
+ 0, 0, 0, 252, 253, 254, 0, 0, 0, 0,
+ 0, 255, 256, 257, 0, 0, 0, 0, 258, 0,
+ 0, 799, 259, 7, 8, 0, 10, 436, 233, 234,
+ 0, 235, 12, 0, 260, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 15, 236, 237,
+ 238, 16, 0, 239, 0, 17, 0, 240, 241, 0,
+ 242, 19, 243, 244, 0, 0, 245, 246, 247, 248,
+ 249, 21, 0, 22, 0, 0, 0, 0, 0, 0,
+ 0, 0, 250, 0, 0, 251, 0, 0, 0, 0,
+ 0, 0, 0, 252, 253, 254, 0, 0, 0, 0,
+ 0, 255, 256, 257, 0, 0, 0, 0, 258, 0,
+ 7, 8, 259, 10, 436, 233, 234, 0, 235, 12,
+ 0, 0, 0, 0, 260, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 15, 236, 237, 238, 16, 0,
+ 239, 0, 17, 0, 240, 241, 0, 242, 19, 243,
+ 244, 0, 0, 245, 246, 247, 248, 249, 21, 0,
+ 22, 0, 0, 0, 0, 0, 0, 0, 0, 250,
+ 0, 0, 251, 0, 0, 0, 0, 0, 0, 0,
+ 252, 253, 254, 0, 0, 0, 0, 0, 255, 256,
+ 257, 0, 0, 0, 0, 258, 0, 0, 0, 259,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 260, 757, 962, 8, 92, 10, 232, 233, 234,
+ 0, 235, 12, 963, 0, 964, 965, 966, 967, 968,
+ 969, 970, 971, 972, 973, 13, 14, 15, 236, 237,
+ 238, 16, 0, 239, 0, 17, 0, 240, 241, 0,
+ 242, 19, 243, 244, 0, 0, 245, 246, 247, 248,
+ 249, 21, 0, 974, 346, 0, 975, 0, 0, 0,
+ 0, 0, 250, 0, 0, 251, 0, 0, 0, 0,
+ 0, 0, 0, 252, 253, 254, 0, 0, 0, 0,
+ 0, 255, 256, 257, 0, 0, 0, 0, 258, 0,
+ 976, 0, 259, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1106, 260, 962, 8, 92, 10, 232,
+ 233, 234, 0, 235, 12, 963, 0, 964, 965, 966,
+ 967, 968, 969, 970, 971, 972, 973, 13, 14, 15,
+ 236, 237, 238, 16, 0, 239, 0, 17, 0, 240,
+ 241, 0, 242, 19, 243, 244, 0, 0, 245, 246,
+ 247, 248, 249, 21, 0, 974, 346, 0, 975, 0,
+ 0, 0, 0, 0, 250, 0, 0, 251, 0, 0,
+ 0, 0, 0, 0, 0, 252, 253, 254, 0, 0,
+ 0, 0, 0, 255, 256, 257, 0, 0, 0, 0,
+ 258, 0, 976, 0, 259, 962, 8, 92, 10, 232,
+ 233, 234, 0, 235, 12, 963, 260, 964, 965, 966,
+ 967, 968, 969, 970, 971, 972, 973, 13, 14, 15,
+ 236, 237, 238, 16, 0, 239, 0, 17, 0, 240,
+ 241, 0, 242, 19, 243, 244, 0, 0, 245, 246,
+ 247, 248, 249, 21, 0, 974, 1266, 0, 975, 0,
+ 0, 0, 0, 0, 250, 0, 0, 251, 0, 0,
+ 0, 0, 0, 0, 0, 252, 253, 254, 0, 0,
+ 0, 0, 0, 255, 256, 257, 0, 0, 0, 0,
+ 258, 0, 976, 0, 259, 962, 8, 92, 10, 232,
+ 233, 234, 0, 235, 12, 963, 260, 964, 965, 966,
+ 967, 968, 969, 970, 971, 972, 973, 13, 14, 15,
+ 236, 237, 238, 16, 0, 239, 0, 17, 0, 240,
+ 241, 0, 242, 19, 243, 244, 0, 0, 245, 246,
+ 247, 248, 249, 21, 0, 974, 0, 0, 975, 0,
+ 0, 0, 0, 0, 250, 0, 0, 251, 0, 0,
+ 0, 0, 0, 0, 0, 252, 253, 254, 0, 0,
+ 0, 0, 0, 255, 256, 257, 0, 0, 0, 0,
+ 258, 0, 976, 0, 259, 7, 8, 92, 10, 232,
+ 233, 234, 353, 235, 12, 0, 260, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 15,
+ 236, 237, 238, 16, 0, 239, 0, 17, 0, 240,
+ 241, 0, 242, 19, 243, 244, 509, 0, 245, 246,
+ 247, 248, 249, 21, 0, 22, 0, 0, 0, 0,
+ 0, 0, 0, 0, 250, 0, 0, 783, 0, 0,
+ 0, 0, 0, 0, 0, 252, 253, 784, 0, 0,
+ 0, 0, 0, 255, 256, 257, 0, 512, 0, 0,
+ 785, 514, 0, 0, 259, 354, 7, 8, 92, 10,
+ 232, 233, 234, 353, 235, 12, 260, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 15, 236, 237, 238, 16, 0, 239, 0, 17, 0,
+ 240, 241, 0, 242, 19, 243, 244, 509, 0, 245,
+ 246, 247, 248, 249, 21, 0, 22, 0, 0, 0,
+ 0, 0, 0, 0, 0, 250, 0, 0, 841, 0,
+ 0, 0, 0, 0, 0, 0, 252, 253, 842, 0,
+ 0, 0, 0, 0, 255, 256, 257, 0, 512, 0,
+ 0, 843, 514, 0, 0, 259, 354, 7, 8, 0,
+ 10, 232, 233, 234, 0, 235, 12, 260, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 15, 236, 237, 238, 16, 0, 239, 0, 17,
+ 0, 240, 241, 0, 242, 19, 243, 244, 509, 0,
+ 245, 246, 247, 248, 249, 21, 0, 22, 0, 0,
+ 0, 0, 0, 0, 0, 0, 250, 0, 0, 783,
+ 0, 0, 0, 0, 0, 0, 0, 252, 253, 784,
+ 0, 0, 0, 0, 0, 255, 256, 257, 0, 512,
+ 0, 0, 785, 514, 7, 8, 0, 10, 232, 233,
+ 234, 0, 235, 12, 0, 0, 0, 0, 260, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 15, 236,
+ 237, 238, 16, 0, 239, 0, 17, 0, 240, 241,
+ 0, 242, 19, 243, 244, 509, 0, 245, 246, 247,
+ 248, 249, 21, 0, 22, 0, 0, 0, 0, 0,
+ 0, 0, 0, 250, 0, 0, 841, 0, 0, 0,
+ 0, 0, 0, 0, 252, 253, 842, 0, 0, 0,
+ 0, 0, 255, 256, 257, 0, 512, 0, 0, 843,
+ 514, 7, 8, 0, 10, 232, 233, 234, 0, 235,
+ 12, 0, 0, 0, 0, 260, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 15, 236, 237, 238, 16,
+ 0, 239, 0, 17, 0, 240, 241, 0, 242, 19,
+ 243, 244, 509, 0, 245, 246, 247, 248, 249, 21,
+ 0, 22, 0, 0, 0, 0, 0, 0, 0, 0,
+ 250, 0, 0, 937, 0, 0, 0, 0, 0, 0,
+ 0, 252, 253, 938, 0, 0, 0, 0, 0, 255,
+ 256, 257, 0, 512, 0, 0, 785, 514, 7, 8,
+ 0, 10, 436, 233, 234, 0, 235, 12, 0, 0,
+ 0, 0, 260, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 15, 236, 237, 238, 16, 0, 239, 0,
+ 17, 0, 240, 241, 0, 242, 19, 243, 244, 0,
+ 0, 245, 246, 247, 248, 249, 21, 0, 22, 0,
+ 0, 1083, 0, 0, 0, 0, 0, 250, 0, 0,
+ 251, 0, 0, 0, 0, 0, 0, 0, 252, 253,
+ 254, 0, 0, 0, 0, 0, 255, 256, 257, 0,
+ 0, 0, 0, 258, 0, 7, 8, 259, 10, 232,
+ 233, 234, 0, 235, 12, 0, 0, 0, 0, 260,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 15,
+ 236, 237, 238, 16, 0, 239, 0, 17, 0, 240,
+ 241, 0, 242, 19, 243, 244, 0, 0, 245, 246,
+ 247, 248, 249, 21, 0, 22, 0, 0, 0, 0,
+ 0, 0, 0, 0, 250, 0, 0, 251, 0, 0,
+ 0, 0, 0, 0, 0, 252, 253, 254, 0, 0,
+ 0, 0, 0, 255, 256, 257, 0, 0, 0, 0,
+ 258, 0, 7, 8, 259, 10, 436, 233, 234, 0,
+ 235, 12, 0, 0, 0, 0, 260, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 15, 236, 237, 238,
+ 16, 0, 239, 0, 17, 0, 240, 241, 0, 242,
+ 19, 243, 244, 0, 0, 245, 246, 247, 248, 249,
+ 21, 0, 22, 462, 0, 0, 0, 0, 0, 0,
+ 0, 250, 0, 0, 251, 0, 0, 0, 0, 0,
+ 0, 0, 252, 253, 254, 0, 0, 0, 0, 0,
+ 255, 256, 257, 0, 0, 7, 8, 463, 10, 436,
+ 233, 234, 0, 235, 12, 0, 0, 0, 0, 0,
+ 0, 0, 0, 260, 0, 0, 0, 0, 0, 15,
+ 236, 237, 238, 16, 0, 239, 0, 17, 0, 240,
+ 241, 0, 242, 19, 243, 244, 0, 0, 245, 246,
+ 247, 248, 249, 21, 0, 22, 0, 0, 0, 0,
+ 0, 0, 0, 0, 250, 0, 0, 251, 0, 0,
+ 0, 0, 0, 0, 0, 252, 253, 254, 0, 0,
+ 0, 0, 0, 255, 256, 257, 0, 0, 0, 0,
+ 258, 499, 7, 8, 0, 10, 436, 233, 234, 0,
+ 235, 12, 0, 0, 0, 0, 260, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 15, 236, 237, 238,
+ 16, 0, 239, 0, 17, 0, 240, 241, 0, 242,
+ 19, 243, 244, 0, 0, 245, 246, 247, 248, 249,
+ 21, 0, 22, 0, 0, 0, 0, 0, 0, 0,
+ 0, 250, 0, 0, 251, 0, 0, 0, 0, 0,
+ 0, 0, 252, 253, 254, 0, 0, 0, 0, 0,
+ 255, 256, 257, 0, 0, 0, 0, 258, 0, 7,
+ 694, 259, 10, 436, 233, 234, 0, 235, 12, 0,
+ 0, 0, 0, 260, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 15, 236, 237, 238, 16, 0, 239,
+ 0, 17, 0, 240, 241, 0, 242, 19, 243, 244,
+ 0, 0, 245, 246, 247, 248, 249, 21, 0, 22,
+ 0, 0, 0, 0, 0, 0, 0, 0, 250, 0,
+ 0, 251, 0, 0, 0, 0, 0, 0, 0, 252,
+ 253, 254, 0, 0, 0, 0, 0, 255, 256, 257,
+ 0, 0, 0, 0, 258, 0, 7, 8, 259, 10,
+ 436, 233, 234, 0, 235, 12, 0, 0, 0, 0,
+ 260, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 15, 236, 237, 238, 16, 0, 239, 0, 17, 0,
+ 240, 241, 0, 242, 19, 243, 244, 0, 0, 245,
+ 246, 247, 248, 249, 21, 0, 22, 0, 0, 0,
+ 0, 0, 0, 0, 0, 250, 0, 0, 887, 0,
+ 0, 0, 0, 0, 0, 0, 252, 253, 888, 0,
+ 0, 0, 0, 0, 255, 256, 257, 0, 0, 0,
+ 0, 258, 0, 7, 8, 259, 10, 436, 233, 234,
+ 0, 235, 12, 0, 0, 0, 0, 260, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 15, 236, 237,
+ 238, 16, 0, 239, 0, 17, 0, 240, 241, 0,
+ 242, 19, 243, 244, 0, 0, 245, 246, 247, 248,
+ 249, 21, 0, 22, 0, 0, 0, 0, 0, 0,
+ 0, 0, 250, 0, 0, 1173, 0, 0, 0, 0,
+ 0, 0, 0, 252, 253, 1174, 0, 0, 0, 0,
+ 0, 255, 256, 257, 0, 0, 0, 0, 1175, 0,
+ 1238, 8, 259, 10, 436, 233, 234, 0, 235, 12,
+ 0, 0, 0, 0, 260, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 15, 236, 237, 238, 16, 0,
+ 239, 0, 17, 0, 240, 241, 0, 242, 19, 243,
+ 244, 0, 0, 245, 246, 247, 248, 249, 21, 0,
+ 22, 0, 0, 0, 0, 0, 0, 0, 0, 250,
+ 0, 0, 251, 0, 0, 0, 0, 0, 0, 0,
+ 252, 253, 254, 0, 0, 0, 0, 0, 255, 256,
+ 257, 0, 0, 0, 0, 258, 0, 7, 8, 259,
+ 10, 436, 233, 234, 0, 235, 12, 0, 0, 0,
+ 0, 260, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 15, 236, 237, 238, 16, 0, 239, 0, 17,
+ 0, 240, 241, 0, 242, 19, 243, 244, 0, 0,
+ 245, 246, 247, 248, 249, 21, 0, 22, 0, 0,
+ 0, 0, 0, 0, 0, 0, 250, 0, 0, 251,
+ 0, 0, 0, 0, 0, 0, 0, 252, 253, 254,
+ 0, 0, 0, 0, 0, 255, 256, 257, 0, 0,
+ 7, 8, 258, 10, 436, 233, 234, 0, 235, 12,
+ 0, 0, 0, 0, 0, 0, 0, 0, 260, 0,
+ 0, 0, 0, 0, 15, 236, 237, 238, 16, 0,
+ 239, 0, 17, 0, 240, 241, 0, 242, 19, 243,
+ 244, 0, 0, 245, 246, 247, 248, 249, 21, 0,
+ 22, 0, 0, 0, 0, 0, 0, 0, 0, 250,
+ 0, 0, 887, 0, 0, 0, 0, 0, 0, 0,
+ 252, 253, 888, 0, 0, 0, 0, 0, 255, 256,
+ 257, 0, 0, 7, 8, 258, 10, 232, 233, 234,
+ 0, 235, 12, 0, 0, 0, 0, 0, 0, 0,
+ 0, 260, 0, 0, 0, 0, 0, 15, 236, 237,
+ 238, 16, 0, 239, 0, 17, 0, 240, 241, 0,
+ 242, 19, 243, 244, 0, 0, 245, 246, 247, 248,
+ 249, 21, 0, 22, 0, 0, 0, 0, 0, 0,
+ 0, 0, 250, 0, 0, 1173, 0, 0, 0, 0,
+ 0, 0, 0, 252, 253, 1174, 0, 0, 0, 0,
+ 0, 255, 256, 257, 0, 0, 7, 8, 1175, 10,
+ 436, 233, 234, 0, 235, 12, 0, 0, 0, 0,
+ 0, 0, 0, 0, 260, 0, 0, 0, 0, 0,
+ 15, 236, 0, 0, 16, 0, 239, 0, 17, 0,
+ 240, 241, 0, 242, 19, 243, 244, 0, 0, 245,
+ 246, 247, 248, 249, 21, 0, 22, 0, 0, 0,
+ 0, 0, 0, 0, 0, 250, 0, 0, 251, 0,
+ 0, 0, 0, 0, 0, 0, 252, 253, 254, 0,
+ 0, 0, 0, 0, 255, 256, 257, 0, 0, 0,
+ 0, 440, 7, 8, 92, 10, 11, 0, 0, 615,
+ 0, 12, 0, 0, 0, 0, 0, 260, 0, 652,
+ 8, 151, 10, 152, 0, 0, 15, 0, 12, 0,
+ 16, 0, 0, 0, 17, 0, 0, 0, 0, 0,
+ 19, 0, 0, 15, 0, 0, 0, 16, 0, 0,
+ 21, 17, 22, 0, 0, 0, 0, 19, 0, 0,
+ 0, 0, 0, 0, 24, 0, 0, 21, 0, 22,
+ 0, 0, 0, 0, 25, 0, 655, 0, 0, 0,
+ 0, 24, 26, 0, 0, 0, 0, 27, 0, 0,
+ 0, 25, 7, 8, 151, 10, 152, 0, 0, 26,
+ 0, 12, 0, 0, 27, 0, 0, 0, 0, 7,
+ 8, 151, 10, 152, 0, 0, 15, 0, 12, 0,
+ 16, 0, 0, 0, 17, 0, 0, 0, 0, 0,
+ 19, 0, 0, 15, 0, 0, 0, 16, 0, 0,
+ 21, 17, 22, 0, 0, 1095, 0, 19, 0, 0,
+ 0, 0, 0, 0, 24, 0, 0, 21, 0, 22,
+ 0, 0, 0, 0, 25, 0, 0, 0, 0, 0,
+ 0, 24, 26, 0, 0, 0, 0, 27, 0, 0,
+ 0, 25, 0, 0, 0, 0, 0, 0, 0, 26,
+ 679, 465, 466, 467, 27, 468, 469, 470, 471, 472,
+ 473, 474, 475, 476, 477, 478, 479, 480, 481, 482,
+ 483, 484, 485, 486, 0, 465, 466, 467, 0, 468,
+ 469, 470, 471, 472, 473, 474, 475, 476, 477, 478,
+ 479, 480, 481, 482, 483, 484, 485, 486, 542, 1036,
+ 465, 466, 467, 0, 468, 469, 470, 471, 472, 473,
+ 474, 475, 476, 477, 478, 479, 480, 481, 482, 483,
+ 484, 485, 486, 465, 466, 467, 1200, 468, 469, 470,
+ 471, 472, 473, 474, 475, 476, 477, 478, 479, 480,
+ 481, 482, 483, 484, 485, 486, 465, 466, 467, 1249,
+ 468, 469, 470, 471, 472, 473, 474, 475, 476, 477,
+ 478, 479, 480, 481, 482, 483, 484, 485, 486, 465,
+ 466, 467, 0, 468, 469, 470, 471, 472, 473, 474,
+ 475, 476, 477, 478, 479, 480, 481, 482, 483, 484,
+ 485, 486, 465, 466, 467, 0, 468, 469, 470, 471,
+ 472, 473, 474, 475, 476, 0, 478, 479, 480, 481,
+ 482, 483, 484, 485, 486, 469, 470, 471, 472, 473,
+ 474, 475, 476, 477, 478, 479, 480, 481, 482, 483,
+ 484, 485, 486, 470, 471, 472, 473, 474, 475, 476,
+ 477, 478, 479, 480, 481, 482, 483, 484, 485, 486,
+ 471, 472, 473, 474, 475, 476, 477, 478, 479, 480,
+ 481, 482, 483, 484, 485, 486
+};
+
+static const short yycheck[] = { 4,
+ 4, 130, 131, 136, 278, 415, 70, 12, 117, 299,
+ 346, 12, 299, 265, 136, 20, 20, 108, 109, 24,
+ 25, 4, 27, 87, 4, 52, 338, 188, 33, 33,
+ 388, 12, 647, 154, 98, 284, 284, 299, 131, 44,
+ 20, 46, 746, 387, 1043, 129, 71, 52, 1151, 134,
+ 9, 52, 983, 33, 59, 812, 20, 1179, 983, 990,
+ 44, 9, 161, 162, 181, 70, 71, 60, 9, 34,
+ 0, 52, 77, 77, 7, 1212, 48, 367, 55, 10,
+ 367, 34, 87, 10, 1159, 1222, 56, 0, 362, 56,
+ 95, 96, 54, 98, 406, 55, 101, 77, 60, 104,
+ 105, 62, 4, 108, 109, 7, 107, 36, 45, 102,
+ 70, 95, 60, 77, 119, 119, 121, 122, 36, 1,
+ 1242, 54, 92, 44, 55, 92, 103, 60, 55, 60,
+ 1205, 58, 136, 60, 139, 140, 141, 121, 103, 119,
+ 1243, 1140, 44, 102, 3, 4, 5, 6, 7, 51,
+ 103, 53, 10, 1275, 102, 1292, 514, 76, 4, 92,
+ 165, 102, 93, 65, 125, 439, 93, 88, 89, 88,
+ 89, 102, 54, 75, 103, 336, 35, 299, 183, 183,
+ 99, 142, 154, 85, 301, 103, 88, 89, 193, 161,
+ 162, 51, 51, 950, 53, 54, 157, 55, 4, 154,
+ 44, 286, 60, 183, 56, 188, 910, 53, 60, 69,
+ 37, 3, 4, 185, 343, 344, 345, 136, 4, 66,
+ 284, 1324, 1325, 23, 51, 230, 231, 88, 4, 75,
+ 185, 1162, 4, 88, 44, 93, 397, 3, 85, 98,
+ 99, 100, 102, 56, 88, 367, 271, 53, 4, 250,
+ 343, 344, 44, 136, 387, 4, 103, 229, 342, 51,
+ 60, 53, 6, 7, 55, 387, 271, 53, 12, 75,
+ 275, 392, 277, 278, 279, 51, 4, 53, 88, 7,
+ 285, 53, 531, 88, 89, 44, 377, 378, 1219, 65,
+ 103, 35, 391, 392, 299, 299, 88, 53, 60, 75,
+ 44, 285, 149, 75, 53, 69, 278, 312, 44, 88,
+ 315, 102, 656, 89, 319, 1246, 51, 56, 323, 323,
+ 419, 1246, 1253, 51, 56, 53, 75, 456, 1253, 88,
+ 89, 3, 4, 948, 69, 647, 55, 65, 54, 258,
+ 102, 56, 1046, 323, 88, 4, 1277, 75, 195, 3,
+ 4, 70, 88, 336, 359, 360, 361, 362, 363, 323,
+ 44, 89, 367, 367, 103, 51, 102, 328, 373, 54,
+ 375, 103, 377, 378, 55, 359, 1307, 361, 362, 384,
+ 299, 53, 1307, 387, 55, 390, 103, 41, 393, 70,
+ 362, 513, 51, 54, 53, 3, 4, 102, 520, 53,
+ 383, 406, 563, 75, 88, 89, 65, 412, 413, 56,
+ 415, 415, 3, 4, 397, 420, 75, 56, 102, 391,
+ 392, 75, 3, 4, 782, 44, 102, 4, 412, 83,
+ 7, 102, 102, 41, 55, 415, 283, 781, 4, 58,
+ 401, 446, 447, 448, 449, 53, 102, 419, 367, 102,
+ 41, 27, 9, 258, 56, 44, 103, 418, 341, 306,
+ 589, 462, 53, 103, 103, 31, 92, 27, 387, 88,
+ 4, 496, 53, 7, 51, 83, 53, 3, 56, 640,
+ 602, 102, 55, 97, 75, 51, 608, 53, 65, 56,
+ 55, 496, 83, 498, 105, 97, 589, 70, 75, 88,
+ 89, 420, 60, 815, 387, 510, 511, 89, 513, 513,
+ 44, 801, 673, 102, 801, 520, 520, 51, 437, 53,
+ 44, 440, 60, 656, 551, 103, 445, 446, 447, 448,
+ 449, 65, 108, 109, 656, 787, 103, 102, 637, 801,
+ 459, 75, 791, 791, 463, 550, 551, 859, 108, 109,
+ 551, 85, 435, 56, 88, 89, 88, 3, 4, 25,
+ 26, 980, 920, 982, 88, 141, 550, 372, 60, 678,
+ 551, 3, 4, 5, 6, 7, 495, 69, 550, 44,
+ 563, 141, 44, 60, 585, 432, 44, 89, 54, 105,
+ 384, 88, 69, 58, 60, 600, 601, 602, 602, 393,
+ 103, 44, 44, 608, 608, 51, 44, 53, 54, 492,
+ 44, 494, 495, 25, 26, 58, 1026, 78, 79, 624,
+ 625, 53, 627, 88, 89, 44, 88, 89, 44, 88,
+ 88, 967, 437, 3, 4, 440, 948, 442, 443, 574,
+ 445, 915, 647, 526, 27, 88, 88, 54, 781, 54,
+ 88, 656, 656, 60, 88, 60, 69, 640, 463, 781,
+ 88, 508, 467, 785, 88, 637, 685, 686, 687, 88,
+ 675, 41, 88, 55, 56, 1, 523, 55, 56, 801,
+ 685, 686, 687, 53, 69, 92, 9, 92, 493, 51,
+ 673, 55, 56, 628, 499, 578, 657, 23, 60, 25,
+ 26, 662, 663, 675, 639, 666, 32, 69, 982, 870,
+ 871, 69, 873, 83, 69, 3, 4, 54, 6, 56,
+ 55, 843, 4, 60, 103, 7, 109, 791, 54, 55,
+ 56, 102, 58, 340, 60, 6, 7, 656, 1074, 1075,
+ 745, 12, 55, 56, 835, 836, 916, 917, 102, 919,
+ 841, 842, 1088, 41, 23, 25, 26, 102, 141, 55,
+ 56, 766, 44, 768, 35, 53, 92, 4, 5, 51,
+ 775, 53, 1108, 1109, 779, 102, 781, 781, 783, 784,
+ 785, 785, 3, 65, 54, 54, 791, 56, 103, 58,
+ 60, 60, 4, 75, 31, 83, 801, 801, 54, 36,
+ 12, 377, 378, 85, 55, 56, 88, 89, 20, 7,
+ 3, 4, 24, 25, 51, 27, 53, 377, 378, 1155,
+ 58, 33, 102, 3, 4, 826, 745, 102, 833, 834,
+ 835, 836, 44, 58, 46, 840, 841, 842, 843, 843,
+ 52, 55, 56, 102, 37, 38, 88, 59, 41, 102,
+ 5, 6, 7, 105, 859, 105, 775, 12, 51, 71,
+ 53, 41, 781, 32, 869, 77, 749, 872, 102, 874,
+ 874, 876, 807, 53, 75, 76, 77, 78, 79, 762,
+ 35, 764, 801, 95, 96, 25, 26, 870, 871, 101,
+ 873, 102, 32, 105, 874, 75, 108, 109, 105, 51,
+ 54, 902, 56, 83, 865, 105, 60, 119, 60, 121,
+ 122, 916, 917, 102, 919, 55, 56, 69, 58, 4,
+ 5, 105, 857, 51, 54, 6, 56, 139, 140, 141,
+ 60, 866, 937, 938, 817, 88, 819, 44, 821, 858,
+ 875, 44, 1216, 948, 949, 949, 31, 1076, 753, 51,
+ 58, 36, 56, 165, 55, 56, 957, 876, 60, 55,
+ 1121, 1122, 56, 5, 6, 7, 51, 69, 53, 949,
+ 12, 183, 973, 1324, 1325, 980, 981, 982, 81, 82,
+ 785, 193, 56, 86, 87, 88, 89, 235, 236, 994,
+ 995, 838, 997, 35, 56, 378, 980, 56, 982, 55,
+ 4, 58, 6, 7, 1286, 1287, 58, 102, 12, 102,
+ 982, 1172, 619, 102, 102, 105, 55, 88, 230, 231,
+ 60, 1026, 1026, 27, 55, 55, 602, 31, 60, 1030,
+ 88, 35, 608, 58, 102, 102, 102, 1327, 843, 102,
+ 1327, 102, 602, 70, 70, 55, 1026, 51, 608, 53,
+ 1179, 102, 102, 858, 70, 70, 102, 60, 1167, 271,
+ 1021, 1022, 60, 60, 136, 277, 278, 279, 1069, 102,
+ 105, 89, 284, 285, 102, 1010, 1011, 83, 44, 103,
+ 656, 102, 1173, 1174, 88, 102, 105, 299, 1023, 1024,
+ 102, 102, 105, 1098, 941, 102, 656, 105, 103, 60,
+ 312, 102, 102, 315, 34, 58, 102, 319, 102, 58,
+ 102, 323, 88, 1242, 88, 1276, 71, 72, 73, 74,
+ 75, 76, 77, 78, 79, 88, 338, 73, 74, 75,
+ 76, 77, 78, 79, 88, 103, 1097, 520, 1121, 1122,
+ 88, 748, 102, 102, 6, 102, 1275, 359, 360, 361,
+ 362, 363, 102, 105, 105, 367, 1117, 102, 60, 103,
+ 102, 373, 102, 375, 102, 377, 378, 972, 1173, 1174,
+ 1175, 88, 384, 60, 56, 56, 251, 4, 390, 254,
+ 54, 393, 257, 7, 56, 1120, 1169, 58, 263, 1172,
+ 102, 14, 1193, 102, 406, 60, 60, 272, 105, 1328,
+ 412, 413, 44, 415, 88, 1327, 56, 56, 420, 88,
+ 58, 58, 1217, 88, 102, 105, 102, 44, 15, 602,
+ 56, 56, 102, 56, 51, 608, 53, 1032, 1033, 1034,
+ 1035, 56, 102, 1217, 446, 447, 448, 449, 65, 1044,
+ 103, 848, 849, 102, 1216, 102, 56, 58, 75, 58,
+ 102, 1186, 1187, 9, 1189, 1190, 1175, 102, 85, 835,
+ 836, 88, 89, 56, 56, 841, 842, 843, 56, 341,
+ 88, 55, 1077, 656, 102, 835, 836, 91, 56, 58,
+ 1085, 841, 842, 843, 496, 102, 498, 1248, 88, 102,
+ 102, 9, 56, 1276, 901, 902, 102, 102, 510, 511,
+ 102, 513, 9, 56, 2, 0, 0, 690, 520, 129,
+ 118, 7, 1195, 801, 10, 387, 325, 815, 119, 531,
+ 119, 551, 1327, 1327, 99, 459, 1140, 1, 1150, 508,
+ 1181, 1077, 981, 681, 33, 805, 1193, 859, 550, 551,
+ 585, 796, 33, 949, 874, 872, 794, 539, 44, 23,
+ 957, 25, 26, 413, 1159, 496, 12, 1266, 32, 55,
+ 829, 1296, 58, 435, 60, 876, 1300, -1, -1, 444,
+ 1175, 3, 4, -1, 70, 1180, 1181, 1302, 1261, -1,
+ 54, 55, 56, -1, 58, -1, 60, -1, 600, 601,
+ 602, -1, 88, 89, -1, -1, 608, 93, -1, -1,
+ 1205, 784, -1, -1, -1, -1, 102, -1, 1327, 41,
+ -1, -1, 624, 625, -1, 627, 4, -1, 92, 51,
+ 492, 53, 494, 495, 1031, -1, -1, -1, 60, 1036,
+ -1, -1, 20, 65, -1, 647, -1, -1, -1, -1,
+ -1, -1, -1, 75, 656, 33, -1, -1, 520, -1,
+ -1, 83, -1, 836, 526, -1, 88, -1, 46, 842,
+ 843, -1, 1, 675, -1, 1270, -1, -1, -1, -1,
+ -1, 59, -1, 685, 686, 687, -1, -1, -1, -1,
+ -1, 1286, 1287, 71, 23, -1, 25, 26, 76, 77,
+ -1, -1, -1, 32, 1299, -1, -1, -1, -1, -1,
+ 88, 89, -1, 1110, 1111, 888, 578, -1, 96, -1,
+ -1, 99, -1, -1, -1, 54, 55, 56, -1, 58,
+ -1, 60, 1098, -1, -1, -1, -1, -1, -1, -1,
+ -1, 119, 1139, 745, 122, -1, 608, -1, 1098, -1,
+ -1, -1, -1, 4, 5, 6, 7, -1, 136, 10,
+ -1, 12, -1, 92, 766, -1, 768, -1, -1, -1,
+ -1, -1, -1, 775, -1, -1, 27, 779, -1, 781,
+ 31, 783, 784, 785, 35, -1, -1, 1184, 4, 791,
+ 6, 7, -1, -1, 1191, 1192, 12, -1, -1, 801,
+ 51, -1, 53, 1200, -1, 183, -1, 1173, 1174, 1175,
+ -1, 27, -1, 815, -1, 31, -1, 69, -1, 35,
+ -1, -1, -1, 1173, 1174, 1175, -1, -1, 690, -1,
+ -1, 833, 834, 835, 836, 51, -1, 53, 840, 841,
+ 842, 843, 93, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 230, -1, -1, -1, -1, 859, 24, 25,
+ -1, -1, 1259, 1260, 116, -1, -1, 869, -1, -1,
+ 872, -1, 874, -1, 876, -1, -1, -1, 3, 4,
+ 258, -1, 7, 135, -1, -1, -1, 749, -1, -1,
+ -1, -1, 757, 271, -1, -1, -1, -1, 150, -1,
+ 762, 279, 764, -1, -1, 71, -1, -1, -1, -1,
+ 76, -1, -1, -1, 916, 917, 41, 919, 783, 784,
+ -1, 299, 88, 89, -1, 1098, 51, -1, 53, 1326,
+ -1, -1, -1, 99, -1, 937, 938, -1, 10, -1,
+ 65, -1, 108, 109, -1, 323, 948, 949, -1, -1,
+ 75, -1, -1, 25, 26, 817, -1, 819, 83, 821,
+ 32, -1, -1, 88, -1, -1, -1, -1, -1, -1,
+ 136, -1, 44, 139, 140, -1, 841, 842, 980, 981,
+ 982, -1, 360, 55, 56, 363, 58, -1, 60, 367,
+ -1, -1, 994, 995, -1, 997, -1, -1, -1, -1,
+ -1, 1174, 1175, -1, -1, -1, -1, -1, -1, 387,
+ -1, -1, -1, -1, -1, -1, 88, 89, -1, -1,
+ 92, 93, 887, 888, 1026, -1, 3, 4, -1, -1,
+ 102, -1, -1, -1, -1, 413, -1, 415, -1, -1,
+ -1, -1, 420, -1, -1, -1, -1, -1, -1, 1,
+ -1, 3, 4, 5, 6, 7, -1, 922, -1, 437,
+ 12, -1, 440, -1, 41, -1, -1, 445, 446, 447,
+ 448, 449, 937, 938, 51, 27, 53, 3, 4, 31,
+ -1, 459, -1, 35, 36, 463, -1, -1, 65, 41,
+ -1, -1, 258, -1, -1, -1, 1098, -1, 75, 51,
+ -1, 53, 3, 4, 56, 271, 83, -1, 60, -1,
+ -1, 88, -1, 65, -1, 41, -1, 495, 496, -1,
+ -1, -1, -1, 75, -1, 51, -1, 53, 380, -1,
+ 56, 83, -1, 299, 386, 513, 88, -1, -1, 65,
+ 41, -1, 520, 44, -1, -1, -1, -1, 71, 75,
+ 51, 103, 53, 76, -1, -1, -1, 83, -1, 60,
+ -1, -1, 88, -1, 65, 88, 89, -1, -1, -1,
+ -1, 1173, 1174, 1175, 75, -1, 99, -1, 430, 431,
+ -1, 433, 83, -1, -1, -1, -1, 88, 1, -1,
+ 3, 4, 5, 6, 7, -1, -1, -1, -1, 12,
+ -1, 367, 4, 5, 6, 7, -1, -1, -1, -1,
+ 12, 377, 378, 136, 27, 1217, -1, -1, 31, -1,
+ -1, 387, 35, 36, 602, 27, -1, -1, 41, 31,
+ 608, -1, -1, 35, -1, -1, 1098, -1, 51, -1,
+ 53, -1, -1, 56, -1, -1, -1, 60, -1, 51,
+ -1, 53, 65, -1, 420, 4, -1, 6, 7, -1,
+ -1, -1, 75, 12, 516, 517, -1, 69, -1, 521,
+ 83, 437, -1, -1, 440, 88, -1, -1, 656, 445,
+ 446, 447, 448, 449, -1, -1, 35, -1, -1, -1,
+ 103, -1, -1, 459, -1, 44, -1, 463, -1, -1,
+ -1, -1, 51, -1, 53, -1, -1, -1, 1173, 1174,
+ -1, -1, -1, -1, -1, -1, 65, -1, -1, -1,
+ -1, -1, -1, -1, -1, 1327, 75, -1, -1, 495,
+ 496, -1, -1, 1195, -1, 258, 85, -1, -1, 88,
+ 89, -1, 594, 595, 510, 511, -1, 513, 271, -1,
+ -1, -1, 604, -1, 520, -1, -1, -1, 610, -1,
+ 3, 4, 5, 6, 7, -1, -1, 745, -1, 12,
+ -1, 749, -1, -1, -1, -1, 299, 3, 4, 1,
+ -1, 3, 4, 5, 6, 7, -1, -1, -1, -1,
+ 12, -1, 35, -1, -1, -1, -1, 775, 41, 1261,
+ -1, 44, 654, 781, -1, 27, -1, 785, 51, 31,
+ 53, -1, -1, 35, 36, 41, -1, -1, 44, 41,
+ -1, -1, 65, 801, -1, 51, -1, 53, -1, 51,
+ -1, 53, 75, -1, 600, 601, 602, -1, 60, 65,
+ 83, -1, 608, 65, 367, 88, -1, -1, -1, 75,
+ 828, -1, -1, 75, -1, -1, -1, 83, -1, 85,
+ -1, 83, 88, 89, 387, 843, 88, -1, -1, -1,
+ 3, 4, -1, -1, 7, -1, -1, -1, -1, -1,
+ 858, 103, -1, -1, -1, -1, -1, -1, -1, -1,
+ 656, -1, -1, -1, -1, -1, 874, 420, 876, 751,
+ 752, 1, 754, 3, 4, 5, 6, 7, 41, -1,
+ -1, -1, 12, -1, 437, -1, -1, 440, 51, -1,
+ 53, -1, 445, 446, 447, 448, 449, 27, 780, -1,
+ -1, 31, 65, -1, -1, 35, 459, -1, -1, -1,
+ 463, 41, 75, -1, -1, 45, -1, -1, -1, -1,
+ 83, 51, -1, 53, -1, 88, 56, -1, 3, 4,
+ -1, -1, 7, -1, -1, 65, -1, -1, 76, -1,
+ -1, 949, 495, 496, -1, 75, -1, -1, -1, 745,
+ -1, -1, -1, 83, -1, -1, -1, 839, 88, -1,
+ 513, 99, -1, -1, 94, -1, 41, 520, -1, 44,
+ 852, 853, 854, 981, -1, 983, 51, -1, 53, 775,
+ -1, -1, 990, -1, -1, 781, -1, 783, 784, 785,
+ 65, -1, -1, -1, 3, 4, -1, -1, 136, -1,
+ 75, -1, -1, -1, -1, 801, -1, -1, 83, -1,
+ 85, -1, -1, 88, 89, -1, -1, -1, 1026, 67,
+ 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
+ 78, 79, 41, -1, -1, -1, -1, -1, -1, 835,
+ 836, -1, 51, -1, 53, 841, 842, 843, 930, 602,
+ -1, 60, -1, -1, -1, 608, 65, -1, -1, -1,
+ -1, -1, 858, -1, -1, 1, 75, 3, 4, 1077,
+ 6, 7, 8, 9, 83, 11, 12, -1, -1, 88,
+ 876, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 27, 28, 29, 30, 31, -1, 33, -1, 35,
+ -1, 37, 38, 656, 40, 41, 42, 43, -1, -1,
+ 46, 47, 48, 49, 50, 51, -1, 53, 54, -1,
+ -1, 259, -1, -1, -1, -1, 62, -1, -1, 65,
+ -1, -1, 1014, -1, -1, -1, -1, 73, 74, 75,
+ -1, 937, 938, 1151, -1, 81, 82, 83, -1, -1,
+ -1, 1159, 88, 89, 1162, -1, 92, -1, -1, -1,
+ 1, -1, 3, 4, 5, 6, 7, 1175, 104, -1,
+ -1, 12, 1180, 1181, -1, -1, -1, 1059, -1, -1,
+ 139, 140, 141, -1, -1, -1, 27, -1, -1, -1,
+ 31, -1, 745, -1, 35, 36, -1, 1205, -1, -1,
+ 41, -1, 340, 341, 1212, -1, -1, -1, -1, -1,
+ 51, 1219, 53, -1, 1222, -1, 3, 4, -1, 60,
+ 7, -1, 775, -1, 65, -1, -1, -1, 781, -1,
+ -1, -1, 785, -1, 75, 1243, -1, -1, 1246, -1,
+ -1, -1, 83, -1, -1, 1253, -1, 88, 801, 387,
+ -1, -1, -1, -1, 41, -1, -1, 44, -1, -1,
+ -1, -1, 103, -1, 51, -1, 53, -1, -1, 1277,
+ -1, -1, 410, -1, -1, -1, -1, -1, 65, -1,
+ -1, -1, -1, -1, 1292, -1, -1, -1, 75, -1,
+ 843, -1, -1, -1, -1, -1, 83, 435, 85, 1307,
+ -1, 88, 89, -1, -1, 858, -1, -1, -1, -1,
+ -1, 3, 4, -1, -1, 7, 1324, 1325, -1, 1327,
+ -1, 459, -1, 876, 462, -1, -1, 465, 466, -1,
+ 468, 469, 470, 471, 472, 473, 474, 475, 476, 477,
+ 478, 479, 480, 481, 482, 483, 484, 485, 486, 41,
+ -1, -1, 44, -1, 492, -1, 494, 495, -1, 51,
+ -1, 53, -1, 1159, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 65, -1, -1, -1, 1173, 1174, 1175,
+ -1, -1, 520, 75, 1180, 1181, 3, 4, 526, -1,
+ 7, 83, -1, 85, -1, -1, 88, 89, -1, -1,
+ -1, -1, -1, 541, 542, -1, -1, -1, -1, 1205,
+ -1, -1, -1, -1, 373, -1, 375, -1, 377, 378,
+ 277, 278, 279, -1, 41, -1, -1, 44, -1, -1,
+ -1, 390, -1, -1, 51, -1, 53, -1, -1, 1,
+ 578, 3, 4, 5, 6, 7, -1, 585, 65, -1,
+ 12, -1, -1, 412, -1, -1, -1, -1, 75, -1,
+ -1, -1, -1, 25, 26, 27, 83, -1, 85, 31,
+ 608, 88, 89, 35, -1, -1, -1, 39, 4, 41,
+ -1, 619, -1, 45, -1, -1, 3, 4, -1, 51,
+ 7, 53, -1, -1, 56, -1, -1, -1, 24, 25,
+ -1, 27, 359, 65, 361, 362, 363, 33, -1, -1,
+ -1, -1, -1, 75, -1, -1, -1, 655, 44, -1,
+ 46, 83, 3, 4, 41, -1, 88, -1, -1, -1,
+ -1, 1327, 94, 95, 51, -1, 53, -1, -1, -1,
+ 66, -1, -1, -1, -1, -1, -1, -1, 65, -1,
+ -1, -1, 690, -1, -1, -1, -1, -1, 75, -1,
+ 41, -1, -1, -1, -1, -1, 83, -1, -1, 95,
+ 51, 88, 53, 711, -1, 101, -1, 103, -1, 105,
+ -1, -1, 108, 109, 65, -1, -1, -1, -1, 4,
+ -1, 6, 7, 119, 75, 121, 122, 12, -1, -1,
+ -1, -1, 83, -1, -1, -1, 1159, 88, -1, -1,
+ 748, 749, 27, 139, 140, 141, 31, -1, -1, -1,
+ 35, -1, 1175, 149, 762, -1, 764, 1180, 1181, 44,
+ -1, -1, -1, -1, -1, -1, 51, -1, 53, 165,
+ -1, 600, 601, 602, 3, 4, -1, -1, 7, 608,
+ 65, -1, 1205, 510, 511, 793, 513, 183, -1, -1,
+ 75, -1, -1, 520, -1, 624, 625, 193, 627, -1,
+ 85, -1, -1, 88, 89, -1, -1, -1, -1, 817,
+ -1, 819, 41, 821, -1, -1, -1, -1, -1, -1,
+ -1, -1, 51, 550, 53, -1, -1, 656, -1, -1,
+ -1, -1, -1, -1, -1, -1, 65, -1, -1, -1,
+ 848, 849, -1, -1, -1, -1, 75, -1, -1, -1,
+ -1, -1, -1, -1, 83, 863, 864, -1, -1, 88,
+ 3, 4, 5, 6, 7, -1, -1, 10, 4, 12,
+ 6, 7, -1, 600, 601, 602, 12, -1, -1, 275,
+ -1, 608, -1, -1, 27, -1, -1, 283, 31, 285,
+ -1, 27, 35, 901, 902, 31, -1, 905, 41, 35,
+ -1, 44, -1, -1, 1327, -1, -1, -1, 51, -1,
+ 53, -1, -1, -1, -1, 51, 312, 53, 54, 315,
+ -1, -1, 65, 319, -1, -1, -1, -1, -1, -1,
+ -1, 4, 75, 6, 7, -1, -1, -1, -1, 12,
+ 83, -1, 85, -1, -1, 88, 89, -1, 675, 957,
+ 93, -1, 88, -1, 27, -1, -1, -1, 31, -1,
+ 968, -1, 35, 359, 360, 361, 362, -1, -1, -1,
+ -1, 367, -1, -1, -1, -1, -1, 373, 51, 375,
+ 53, 377, 378, 3, 4, 5, 6, 7, 384, -1,
+ -1, -1, 12, -1, 390, -1, -1, 393, 3, 4,
+ -1, -1, 7, -1, 833, 834, 835, 836, -1, 1017,
+ -1, 840, 841, 842, 843, 35, 412, 413, -1, 415,
+ -1, 41, -1, 1031, 44, -1, -1, -1, 1036, -1,
+ 1038, 51, -1, 53, -1, -1, 41, -1, -1, 766,
+ 869, 768, -1, -1, -1, 65, 51, -1, 53, -1,
+ -1, -1, 779, -1, -1, 75, 783, 784, 785, -1,
+ 65, -1, -1, 83, -1, 85, -1, -1, 88, 89,
+ 75, -1, -1, -1, -1, -1, -1, -1, 83, -1,
+ -1, -1, -1, 88, 3, 4, -1, -1, 7, -1,
+ 1098, -1, -1, 1, -1, 3, 4, 5, 6, 7,
+ -1, -1, 1110, 1111, 12, -1, 833, 834, 835, 836,
+ 1118, 1119, 508, 840, 841, 842, 843, -1, -1, 27,
+ -1, -1, 41, 31, 520, -1, -1, 35, -1, -1,
+ -1, 1139, 51, 41, 53, -1, -1, -1, -1, -1,
+ -1, -1, -1, 51, -1, 53, 65, -1, -1, -1,
+ -1, -1, 1160, -1, 550, -1, 75, 65, -1, -1,
+ -1, -1, -1, -1, 83, 994, 995, 75, 997, 88,
+ -1, -1, -1, -1, -1, 83, 1184, -1, -1, -1,
+ 88, -1, -1, 1191, 1192, -1, -1, 1195, -1, -1,
+ -1, -1, 1200, -1, 4, 4, 5, 6, 7, -1,
+ -1, 10, -1, 12, 600, 601, 602, -1, -1, -1,
+ 937, 938, 608, -1, 24, 25, -1, 27, 27, -1,
+ -1, -1, 31, 33, -1, -1, 35, -1, 624, 625,
+ -1, 627, -1, -1, 44, 44, 46, -1, -1, -1,
+ -1, -1, 51, -1, 53, -1, -1, -1, -1, -1,
+ -1, 1259, 1260, 1261, 3, 4, 65, -1, 7, -1,
+ 656, -1, -1, -1, -1, -1, 75, 994, 995, 1098,
+ 997, -1, -1, -1, -1, -1, 85, -1, -1, 88,
+ 89, -1, -1, -1, 93, 95, -1, -1, -1, -1,
+ -1, 101, 41, -1, 690, 105, -1, -1, 108, 109,
+ -1, -1, 51, -1, 53, -1, -1, -1, -1, 119,
+ -1, 121, 122, -1, -1, -1, 65, -1, 1326, -1,
+ -1, -1, -1, -1, -1, -1, 75, -1, -1, 139,
+ 140, 141, -1, -1, 83, -1, -1, -1, -1, 88,
+ -1, -1, -1, -1, 1173, 1174, 1175, -1, 1, -1,
+ -1, 4, -1, 6, 7, 165, -1, -1, -1, 12,
+ 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
+ 78, 79, -1, 183, 27, -1, -1, -1, 31, -1,
+ -1, -1, 35, 193, 37, 38, 27, 783, 784, -1,
+ -1, 44, -1, -1, -1, -1, -1, -1, 51, -1,
+ 53, -1, 55, 44, 57, 58, 59, -1, 61, 62,
+ 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 76, 77, 78, -1, -1, 81, 82,
+ 83, -1, -1, 86, -1, -1, 89, 833, 834, 835,
+ 836, -1, 838, -1, 840, 841, 842, 843, -1, -1,
+ -1, 104, -1, -1, 95, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 108, 109, -1,
+ -1, -1, -1, 869, 284, 285, 872, -1, 874, -1,
+ 121, -1, -1, -1, 3, 4, 5, 6, 7, -1,
+ -1, 887, 888, 12, -1, -1, -1, -1, 139, 140,
+ 141, -1, 312, -1, -1, 315, -1, -1, 27, 319,
+ -1, -1, 31, -1, -1, -1, 35, -1, -1, -1,
+ -1, -1, 41, -1, 165, 44, -1, -1, 338, -1,
+ -1, -1, 51, -1, 53, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 941, 65, -1, -1, 359,
+ 360, 361, 362, 949, -1, -1, 75, 367, -1, -1,
+ -1, -1, -1, 373, 83, 375, 85, 377, 378, 88,
+ 89, -1, -1, -1, 384, -1, -1, -1, -1, -1,
+ 390, -1, -1, 393, 980, 981, 982, -1, -1, -1,
+ 231, -1, -1, -1, -1, -1, -1, -1, 994, 995,
+ -1, 997, 412, 413, -1, 415, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 3, 4,
+ 12, 6, 7, 8, 9, -1, 11, 12, -1, -1,
+ 1026, -1, 24, 25, -1, 27, 277, 278, 279, -1,
+ -1, -1, 27, 28, 285, -1, 31, -1, 33, -1,
+ 35, -1, 37, 38, -1, 40, 41, 42, 43, -1,
+ 52, 46, 47, 48, 49, 50, 51, -1, 53, -1,
+ -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
+ 65, -1, -1, -1, -1, -1, -1, -1, 73, 74,
+ 75, -1, -1, -1, -1, -1, 81, 82, 83, -1,
+ -1, -1, 1098, 88, -1, -1, -1, -1, -1, 101,
+ 520, 103, 104, 105, -1, -1, 108, 109, 359, 104,
+ 361, 362, 363, -1, 3, 4, 5, 6, 7, -1,
+ -1, -1, 373, 12, 375, -1, 377, 378, -1, -1,
+ 550, -1, -1, 384, -1, -1, -1, -1, -1, 390,
+ -1, -1, 393, -1, 44, -1, 35, -1, -1, -1,
+ -1, -1, 41, -1, -1, 44, -1, -1, -1, -1,
+ -1, 412, 51, -1, 53, -1, 66, 1173, 1174, 1175,
+ -1, -1, -1, -1, -1, -1, 65, -1, -1, -1,
+ 600, 601, 602, -1, -1, 85, 75, -1, 608, -1,
+ -1, 193, -1, -1, 83, 95, 85, -1, -1, 88,
+ 89, -1, -1, -1, 624, 625, 4, 627, 6, 7,
+ -1, 1217, -1, -1, 12, -1, 3, 4, -1, -1,
+ 7, 121, -1, -1, -1, 4, 5, 6, 7, 231,
+ -1, 10, -1, 12, -1, -1, 656, 35, -1, 139,
+ 140, 141, -1, -1, 3, 4, 44, 498, 27, 149,
+ -1, -1, 31, 51, 41, 53, 35, -1, -1, 510,
+ 511, -1, 513, -1, 51, 165, 53, 65, -1, 520,
+ 690, -1, 51, 275, 53, 277, 278, 75, 65, -1,
+ 3, 4, 41, -1, -1, -1, -1, 85, 75, -1,
+ 88, 89, 51, -1, 53, 195, 83, 56, -1, 550,
+ -1, 88, -1, -1, 306, -1, 65, -1, -1, -1,
+ 312, -1, -1, 315, -1, -1, 75, 319, 41, -1,
+ -1, -1, -1, -1, 83, -1, -1, -1, 51, 88,
+ 53, 4, 5, 6, 7, -1, -1, -1, -1, 12,
+ -1, -1, 65, -1, -1, -1, -1, -1, -1, 600,
+ 601, 602, 75, -1, 27, -1, -1, 608, 31, -1,
+ 83, -1, 35, 783, 784, 88, -1, -1, -1, -1,
+ -1, 791, -1, 624, 625, -1, 627, -1, 51, -1,
+ 53, -1, -1, 283, -1, 285, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 815, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 656, -1, -1, -1, -1,
+ -1, -1, -1, 833, 834, 835, 836, -1, -1, -1,
+ 840, 841, 842, 843, 675, -1, -1, -1, -1, -1,
+ 432, -1, -1, -1, 685, 686, 687, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 869,
+ -1, -1, 872, -1, 874, -1, -1, -1, -1, 359,
+ -1, 361, 362, -1, -1, -1, -1, 887, 888, 277,
+ 278, 279, -1, 373, -1, 375, -1, 377, 378, -1,
+ -1, -1, -1, -1, 384, -1, -1, -1, -1, -1,
+ 390, -1, -1, 393, -1, -1, 498, -1, -1, -1,
+ -1, -1, 4, -1, -1, -1, 508, -1, 510, 511,
+ -1, -1, 412, -1, -1, 766, -1, 768, -1, -1,
+ -1, -1, 24, 25, -1, -1, -1, -1, 779, 949,
+ -1, 33, 783, 784, 785, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 46, -1, -1, -1, -1, 551,
+ -1, 359, -1, 361, 362, 363, -1, -1, -1, -1,
+ 980, 981, 982, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 994, 995, -1, 997, 4, 5,
+ 6, 7, 833, 834, 835, 836, 12, -1, -1, 840,
+ 841, 842, 843, -1, -1, -1, -1, -1, -1, 101,
+ -1, 27, -1, 105, -1, 31, 1026, -1, -1, 35,
+ -1, -1, -1, -1, -1, -1, -1, 119, 869, -1,
+ 122, -1, -1, 523, -1, 51, -1, 53, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 139, 140, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 550, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 77, 78, 79, 916, 917, -1, 919, -1,
+ -1, -1, -1, 675, -1, -1, -1, -1, 1098, -1,
+ -1, 183, -1, 685, 686, 687, 937, 938, -1, -1,
+ -1, 193, -1, -1, -1, -1, -1, -1, -1, -1,
+ 600, 601, 602, 511, -1, 513, -1, -1, 608, -1,
+ -1, -1, 520, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 624, 625, -1, 627, -1, 980,
+ -1, 982, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 550, 994, 995, -1, 997, -1, -1, -1,
+ -1, -1, -1, 1173, 1174, 1175, 656, -1, -1, -1,
+ -1, -1, -1, -1, 766, -1, 768, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 779, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 77, 78, 79, 601, 602, -1, -1, 1217, 3, 4,
+ 608, 6, 7, 8, 9, -1, 11, 12, -1, -1,
+ 312, -1, -1, 315, -1, -1, -1, 319, -1, -1,
+ -1, -1, 27, 28, 29, 30, 31, -1, 33, -1,
+ 35, -1, 37, 38, -1, 40, 41, 42, 43, -1,
+ -1, 46, 47, 48, 49, 50, 51, 1098, 53, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 360, -1,
+ -1, -1, -1, -1, -1, 367, -1, 675, -1, -1,
+ 872, 373, -1, 375, -1, -1, 81, 82, 83, -1,
+ -1, -1, -1, 88, -1, -1, -1, 92, 390, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 104,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 412, 413, -1, 415, 916, 917, -1, 919, -1, -1,
+ -1, -1, 1173, 1174, 1175, -1, -1, -1, -1, -1,
+ -1, -1, -1, 833, 834, 835, 836, -1, 838, -1,
+ 840, 841, 842, 843, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 766, -1,
+ 768, -1, -1, -1, -1, -1, 1217, -1, -1, 869,
+ -1, 779, -1, -1, -1, -1, 784, 785, -1, -1,
+ -1, 3, 4, 5, 6, 7, -1, -1, 10, -1,
+ 12, -1, 3, 4, 5, 6, 7, -1, -1, -1,
+ -1, 12, -1, -1, -1, 27, -1, -1, -1, 31,
+ -1, -1, -1, 35, -1, -1, 27, -1, -1, 41,
+ 31, -1, -1, -1, 35, 833, 834, -1, 836, 51,
+ 41, 53, 840, -1, 842, 843, -1, -1, -1, -1,
+ 51, 941, 53, 65, -1, 56, -1, -1, -1, -1,
+ -1, -1, -1, 75, 65, -1, -1, -1, -1, -1,
+ -1, 83, -1, -1, 75, -1, 88, -1, -1, -1,
+ -1, 93, 83, -1, -1, -1, -1, 88, -1, -1,
+ 980, -1, 982, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 994, 995, -1, 997, 600, 601,
+ 59, -1, 61, 62, 63, 64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
+ 79, -1, 624, 625, -1, 627, -1, -1, -1, 1,
+ 938, 3, 4, 5, 6, 7, 8, 9, -1, 11,
+ 12, 13, -1, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ -1, 33, -1, 35, -1, 37, 38, -1, 40, 41,
+ 42, 43, -1, -1, 46, 47, 48, 49, 50, 51,
+ -1, 53, 54, -1, 56, -1, 994, 995, -1, 997,
+ 62, -1, -1, 65, -1, -1, -1, -1, 1098, -1,
+ -1, 73, 74, 75, -1, -1, -1, -1, -1, 81,
+ 82, 83, -1, -1, -1, -1, 88, -1, 90, 1,
+ 92, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 103, 104, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 27, 28, 29, 30, 31,
+ -1, 33, -1, 35, -1, 37, 38, -1, 40, 41,
+ 42, 43, 44, -1, 46, 47, 48, 49, 50, 51,
+ -1, 53, 54, 1173, 1174, 1175, -1, -1, -1, -1,
+ 62, -1, -1, 65, -1, -1, -1, -1, -1, -1,
+ -1, 73, 74, 75, 10, -1, -1, -1, -1, 81,
+ 82, 83, -1, 85, -1, -1, 88, 89, -1, -1,
+ 92, 93, -1, -1, -1, -1, -1, 1217, -1, -1,
+ -1, -1, 104, -1, -1, -1, -1, -1, -1, -1,
+ -1, 833, 834, -1, -1, -1, -1, -1, 840, -1,
+ -1, 57, 58, 59, 60, 61, 62, 63, 64, 65,
+ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
+ 76, 77, 78, 79, -1, -1, -1, 869, -1, -1,
+ 872, 1, 874, 3, 4, 5, 6, 7, 8, 9,
+ -1, 11, 12, 13, -1, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, -1, 33, -1, 35, -1, 37, 38, -1,
+ 40, 41, 42, 43, -1, -1, 46, 47, 48, 49,
+ 50, 51, -1, 53, 54, -1, 56, -1, -1, -1,
+ -1, -1, 62, -1, -1, 65, -1, -1, -1, -1,
+ -1, -1, -1, 73, 74, 75, -1, 949, -1, -1,
+ -1, 81, 82, 83, -1, -1, -1, -1, 88, -1,
+ 90, -1, 92, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 103, 104, -1, -1, -1, -1, 981,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 994, 995, -1, 997, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 1, -1, 3, 4, 5, 6, 7, 8, 9,
+ -1, 11, 12, 13, 1026, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, -1, 33, -1, 35, -1, 37, 38, -1,
+ 40, 41, 42, 43, -1, -1, 46, 47, 48, 49,
+ 50, 51, -1, 53, 54, -1, 56, -1, -1, -1,
+ -1, -1, 62, -1, -1, 65, -1, -1, -1, -1,
+ -1, -1, -1, 73, 74, 75, -1, -1, -1, -1,
+ -1, 81, 82, 83, -1, -1, -1, -1, 88, -1,
+ 90, -1, 92, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 103, 104, 1, -1, 3, 4, 5,
+ 6, 7, 8, 9, -1, 11, 12, 13, -1, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, -1, 33, -1, 35,
+ -1, 37, 38, -1, 40, 41, 42, 43, -1, -1,
+ 46, 47, 48, 49, 50, 51, -1, 53, 54, -1,
+ 56, -1, -1, -1, -1, -1, 62, -1, -1, 65,
+ -1, -1, -1, -1, -1, -1, -1, 73, 74, 75,
+ -1, -1, -1, -1, -1, 81, 82, 83, -1, -1,
+ -1, -1, 88, -1, 90, -1, 92, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 103, 104, 1,
+ -1, 3, 4, 5, 6, 7, 8, 9, -1, 11,
+ 12, 13, -1, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ -1, 33, -1, 35, -1, 37, 38, -1, 40, 41,
+ 42, 43, -1, -1, 46, 47, 48, 49, 50, 51,
+ -1, 53, 54, -1, 56, -1, -1, -1, -1, -1,
+ 62, -1, -1, 65, -1, -1, -1, -1, -1, -1,
+ -1, 73, 74, 75, -1, -1, -1, -1, -1, 81,
+ 82, 83, -1, -1, -1, -1, 88, -1, 90, -1,
+ 92, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 103, 104, 1, -1, 3, 4, 5, 6, 7,
+ 8, 9, -1, 11, 12, 13, -1, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, -1, 33, -1, 35, -1, 37,
+ 38, -1, 40, 41, 42, 43, -1, -1, 46, 47,
+ 48, 49, 50, 51, -1, 53, 54, -1, 56, -1,
+ -1, -1, -1, -1, 62, -1, -1, 65, -1, -1,
+ -1, -1, -1, -1, -1, 73, 74, 75, -1, -1,
+ -1, -1, -1, 81, 82, 83, -1, -1, -1, -1,
+ 88, -1, 90, 1, 92, 3, 4, 5, 6, 7,
+ 8, 9, -1, 11, 12, 13, 104, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, -1, 33, -1, 35, -1, 37,
+ 38, -1, 40, 41, 42, 43, -1, -1, 46, 47,
+ 48, 49, 50, 51, -1, 53, 54, -1, 56, -1,
+ -1, -1, -1, -1, 62, -1, -1, 65, -1, -1,
+ -1, -1, -1, -1, -1, 73, 74, 75, -1, -1,
+ -1, -1, -1, 81, 82, 83, -1, -1, -1, -1,
+ 88, -1, 90, 1, 92, 3, 4, 5, 6, 7,
+ 8, 9, -1, 11, 12, 13, 104, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, -1, 33, -1, 35, -1, 37,
+ 38, -1, 40, 41, 42, 43, -1, -1, 46, 47,
+ 48, 49, 50, 51, -1, 53, 54, -1, 56, -1,
+ -1, -1, -1, -1, 62, -1, -1, 65, -1, -1,
+ -1, -1, -1, -1, -1, 73, 74, 75, -1, -1,
+ -1, -1, -1, 81, 82, 83, -1, -1, -1, -1,
+ 88, -1, 90, 1, 92, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, -1, 104, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 27,
+ 28, 29, 30, 31, -1, 33, -1, 35, -1, 37,
+ 38, -1, 40, 41, 42, 43, 44, -1, 46, 47,
+ 48, 49, 50, 51, -1, 53, 54, -1, -1, -1,
+ -1, -1, -1, -1, 62, -1, -1, 65, -1, -1,
+ -1, -1, -1, -1, -1, 73, 74, 75, -1, -1,
+ -1, -1, -1, 81, 82, 83, -1, 85, -1, -1,
+ 88, 89, -1, -1, 92, 93, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 1, 104, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 27, 28, 29, 30, 31, -1, 33, -1, 35,
+ -1, 37, 38, -1, 40, 41, 42, 43, -1, -1,
+ 46, 47, 48, 49, 50, 51, -1, 53, -1, -1,
+ -1, -1, -1, -1, -1, -1, 62, -1, -1, 65,
+ -1, -1, -1, -1, -1, -1, -1, 73, 74, 75,
+ -1, -1, -1, -1, -1, 81, 82, 83, -1, -1,
+ -1, -1, 88, -1, -1, -1, 92, 93, -1, -1,
+ -1, -1, -1, -1, -1, -1, 102, 1, 104, 3,
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 27, 28, 29, 30, 31, -1, 33,
+ -1, 35, -1, 37, 38, -1, 40, 41, 42, 43,
+ -1, -1, 46, 47, 48, 49, 50, 51, -1, 53,
+ -1, -1, -1, -1, -1, -1, -1, -1, 62, -1,
+ -1, 65, -1, -1, -1, -1, -1, -1, -1, 73,
+ 74, 75, -1, -1, -1, -1, -1, 81, 82, 83,
+ -1, -1, -1, -1, 88, -1, -1, -1, 92, 93,
+ -1, -1, -1, -1, -1, -1, -1, -1, 102, 1,
+ 104, 3, 4, -1, 6, 7, 8, 9, -1, 11,
+ 12, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 27, 28, 29, 30, 31,
+ -1, 33, -1, 35, -1, 37, 38, -1, 40, 41,
+ 42, 43, -1, -1, 46, 47, 48, 49, 50, 51,
+ -1, 53, 54, -1, -1, -1, -1, -1, -1, -1,
+ 62, -1, -1, 65, -1, -1, -1, -1, -1, -1,
+ -1, 73, 74, 75, -1, -1, -1, -1, -1, 81,
+ 82, 83, -1, -1, -1, -1, 88, 89, -1, 1,
+ 92, 3, 4, -1, 6, 7, 8, 9, -1, 11,
+ 12, 103, 104, -1, -1, -1, -1, 19, -1, -1,
+ -1, -1, -1, -1, -1, 27, 28, 29, 30, 31,
+ -1, 33, -1, 35, -1, 37, 38, -1, 40, 41,
+ 42, 43, -1, -1, 46, 47, 48, 49, 50, 51,
+ -1, 53, 54, -1, -1, -1, -1, -1, -1, -1,
+ 62, -1, -1, 65, -1, -1, -1, -1, -1, -1,
+ -1, 73, 74, 75, -1, -1, -1, -1, -1, 81,
+ 82, 83, -1, -1, -1, -1, 88, -1, -1, 1,
+ 92, 3, 4, -1, 6, 7, 8, 9, -1, 11,
+ 12, 103, 104, -1, -1, -1, -1, 19, -1, -1,
+ -1, -1, -1, -1, -1, 27, 28, 29, 30, 31,
+ -1, 33, -1, 35, -1, 37, 38, -1, 40, 41,
+ 42, 43, -1, -1, 46, 47, 48, 49, 50, 51,
+ -1, 53, 54, -1, -1, -1, -1, -1, -1, -1,
+ 62, -1, -1, 65, -1, -1, -1, -1, -1, -1,
+ -1, 73, 74, 75, -1, -1, -1, -1, -1, 81,
+ 82, 83, -1, -1, -1, -1, 88, -1, -1, 1,
+ 92, 3, 4, 5, 6, 7, 8, 9, -1, 11,
+ 12, 103, 104, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 27, 28, 29, 30, 31,
+ -1, 33, -1, 35, -1, 37, 38, -1, 40, 41,
+ 42, 43, -1, -1, 46, 47, 48, 49, 50, 51,
+ -1, 53, 54, -1, 56, -1, -1, -1, -1, -1,
+ 62, -1, -1, 65, -1, -1, -1, -1, -1, -1,
+ -1, 73, 74, 75, -1, -1, -1, -1, -1, 81,
+ 82, 83, -1, -1, -1, -1, 88, -1, -1, 1,
+ 92, 3, 4, -1, 6, 7, 8, 9, -1, 11,
+ 12, -1, 104, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 27, 28, 29, 30, 31,
+ -1, 33, -1, 35, -1, 37, 38, -1, 40, 41,
+ 42, 43, -1, -1, 46, 47, 48, 49, 50, 51,
+ -1, 53, 54, -1, -1, -1, -1, -1, -1, -1,
+ 62, -1, -1, 65, -1, -1, -1, -1, -1, -1,
+ -1, 73, 74, 75, -1, -1, -1, -1, -1, 81,
+ 82, 83, -1, -1, -1, -1, 88, -1, -1, 1,
+ 92, 3, 4, -1, 6, 7, 8, 9, -1, 11,
+ 12, -1, 104, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 27, 28, 29, 30, 31,
+ -1, 33, -1, 35, -1, 37, 38, -1, 40, 41,
+ 42, 43, -1, -1, 46, 47, 48, 49, 50, 51,
+ -1, 53, 54, -1, -1, -1, -1, -1, -1, -1,
+ 62, -1, -1, 65, -1, -1, -1, -1, -1, -1,
+ -1, 73, 74, 75, -1, -1, -1, -1, -1, 81,
+ 82, 83, -1, -1, -1, -1, 88, -1, -1, 1,
+ 92, 3, 4, -1, 6, 7, 8, 9, -1, 11,
+ 12, -1, 104, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 27, 28, 29, 30, 31,
+ -1, 33, -1, 35, -1, 37, 38, -1, 40, 41,
+ 42, 43, -1, -1, 46, 47, 48, 49, 50, 51,
+ -1, 53, -1, -1, -1, -1, -1, -1, 60, -1,
+ 62, -1, -1, 65, -1, -1, -1, -1, -1, -1,
+ -1, 73, 74, 75, -1, -1, -1, -1, -1, 81,
+ 82, 83, -1, -1, -1, -1, 88, -1, -1, 1,
+ 92, 3, 4, -1, 6, 7, 8, 9, -1, 11,
+ 12, -1, 104, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 27, 28, 29, 30, 31,
+ -1, 33, -1, 35, -1, 37, 38, -1, 40, 41,
+ 42, 43, -1, -1, 46, 47, 48, 49, 50, 51,
+ -1, 53, 54, -1, -1, -1, -1, -1, -1, -1,
+ 62, -1, -1, 65, -1, -1, -1, -1, -1, -1,
+ -1, 73, 74, 75, -1, -1, -1, -1, -1, 81,
+ 82, 83, -1, -1, -1, -1, 88, -1, -1, 1,
+ 92, 3, 4, -1, 6, 7, 8, 9, -1, 11,
+ 12, -1, 104, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 27, 28, 29, 30, 31,
+ -1, 33, -1, 35, -1, 37, 38, -1, 40, 41,
+ 42, 43, -1, -1, 46, 47, 48, 49, 50, 51,
+ -1, 53, -1, -1, 56, -1, -1, -1, -1, -1,
+ 62, -1, -1, 65, -1, -1, -1, -1, -1, -1,
+ -1, 73, 74, 75, -1, -1, -1, -1, -1, 81,
+ 82, 83, -1, -1, -1, -1, 88, -1, -1, 1,
+ 92, 3, 4, -1, 6, 7, 8, 9, -1, 11,
+ 12, -1, 104, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 27, 28, 29, 30, 31,
+ -1, 33, -1, 35, -1, 37, 38, -1, 40, 41,
+ 42, 43, -1, -1, 46, 47, 48, 49, 50, 51,
+ -1, 53, -1, -1, -1, -1, -1, -1, -1, -1,
+ 62, -1, -1, 65, -1, -1, -1, -1, -1, -1,
+ -1, 73, 74, 75, -1, -1, -1, -1, -1, 81,
+ 82, 83, -1, -1, -1, -1, 88, -1, -1, -1,
+ 92, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 102, 1, 104, 3, 4, -1, 6, 7, 8, 9,
+ -1, 11, 12, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 27, 28, 29,
+ 30, 31, -1, 33, -1, 35, -1, 37, 38, -1,
+ 40, 41, 42, 43, -1, -1, 46, 47, 48, 49,
+ 50, 51, -1, 53, -1, -1, -1, -1, -1, -1,
+ -1, -1, 62, -1, -1, 65, -1, -1, -1, -1,
+ -1, -1, -1, 73, 74, 75, -1, -1, -1, -1,
+ -1, 81, 82, 83, -1, -1, -1, -1, 88, -1,
+ -1, 1, 92, 3, 4, -1, 6, 7, 8, 9,
+ -1, 11, 12, -1, 104, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 27, 28, 29,
+ 30, 31, -1, 33, -1, 35, -1, 37, 38, -1,
+ 40, 41, 42, 43, -1, -1, 46, 47, 48, 49,
+ 50, 51, -1, 53, -1, -1, -1, -1, -1, -1,
+ -1, -1, 62, -1, -1, 65, -1, -1, -1, -1,
+ -1, -1, -1, 73, 74, 75, -1, -1, -1, -1,
+ -1, 81, 82, 83, -1, -1, -1, -1, 88, -1,
+ 3, 4, 92, 6, 7, 8, 9, -1, 11, 12,
+ -1, -1, -1, -1, 104, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 27, 28, 29, 30, 31, -1,
+ 33, -1, 35, -1, 37, 38, -1, 40, 41, 42,
+ 43, -1, -1, 46, 47, 48, 49, 50, 51, -1,
+ 53, -1, -1, -1, -1, -1, -1, -1, -1, 62,
+ -1, -1, 65, -1, -1, -1, -1, -1, -1, -1,
+ 73, 74, 75, -1, -1, -1, -1, -1, 81, 82,
+ 83, -1, -1, -1, -1, 88, -1, -1, -1, 92,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 104, 105, 3, 4, 5, 6, 7, 8, 9,
+ -1, 11, 12, 13, -1, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, -1, 33, -1, 35, -1, 37, 38, -1,
+ 40, 41, 42, 43, -1, -1, 46, 47, 48, 49,
+ 50, 51, -1, 53, 54, -1, 56, -1, -1, -1,
+ -1, -1, 62, -1, -1, 65, -1, -1, -1, -1,
+ -1, -1, -1, 73, 74, 75, -1, -1, -1, -1,
+ -1, 81, 82, 83, -1, -1, -1, -1, 88, -1,
+ 90, -1, 92, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 103, 104, 3, 4, 5, 6, 7,
+ 8, 9, -1, 11, 12, 13, -1, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, -1, 33, -1, 35, -1, 37,
+ 38, -1, 40, 41, 42, 43, -1, -1, 46, 47,
+ 48, 49, 50, 51, -1, 53, 54, -1, 56, -1,
+ -1, -1, -1, -1, 62, -1, -1, 65, -1, -1,
+ -1, -1, -1, -1, -1, 73, 74, 75, -1, -1,
+ -1, -1, -1, 81, 82, 83, -1, -1, -1, -1,
+ 88, -1, 90, -1, 92, 3, 4, 5, 6, 7,
+ 8, 9, -1, 11, 12, 13, 104, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, -1, 33, -1, 35, -1, 37,
+ 38, -1, 40, 41, 42, 43, -1, -1, 46, 47,
+ 48, 49, 50, 51, -1, 53, 54, -1, 56, -1,
+ -1, -1, -1, -1, 62, -1, -1, 65, -1, -1,
+ -1, -1, -1, -1, -1, 73, 74, 75, -1, -1,
+ -1, -1, -1, 81, 82, 83, -1, -1, -1, -1,
+ 88, -1, 90, -1, 92, 3, 4, 5, 6, 7,
+ 8, 9, -1, 11, 12, 13, 104, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, -1, 33, -1, 35, -1, 37,
+ 38, -1, 40, 41, 42, 43, -1, -1, 46, 47,
+ 48, 49, 50, 51, -1, 53, -1, -1, 56, -1,
+ -1, -1, -1, -1, 62, -1, -1, 65, -1, -1,
+ -1, -1, -1, -1, -1, 73, 74, 75, -1, -1,
+ -1, -1, -1, 81, 82, 83, -1, -1, -1, -1,
+ 88, -1, 90, -1, 92, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, -1, 104, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 27,
+ 28, 29, 30, 31, -1, 33, -1, 35, -1, 37,
+ 38, -1, 40, 41, 42, 43, 44, -1, 46, 47,
+ 48, 49, 50, 51, -1, 53, -1, -1, -1, -1,
+ -1, -1, -1, -1, 62, -1, -1, 65, -1, -1,
+ -1, -1, -1, -1, -1, 73, 74, 75, -1, -1,
+ -1, -1, -1, 81, 82, 83, -1, 85, -1, -1,
+ 88, 89, -1, -1, 92, 93, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 104, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 27, 28, 29, 30, 31, -1, 33, -1, 35, -1,
+ 37, 38, -1, 40, 41, 42, 43, 44, -1, 46,
+ 47, 48, 49, 50, 51, -1, 53, -1, -1, -1,
+ -1, -1, -1, -1, -1, 62, -1, -1, 65, -1,
+ -1, -1, -1, -1, -1, -1, 73, 74, 75, -1,
+ -1, -1, -1, -1, 81, 82, 83, -1, 85, -1,
+ -1, 88, 89, -1, -1, 92, 93, 3, 4, -1,
+ 6, 7, 8, 9, -1, 11, 12, 104, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 27, 28, 29, 30, 31, -1, 33, -1, 35,
+ -1, 37, 38, -1, 40, 41, 42, 43, 44, -1,
+ 46, 47, 48, 49, 50, 51, -1, 53, -1, -1,
+ -1, -1, -1, -1, -1, -1, 62, -1, -1, 65,
+ -1, -1, -1, -1, -1, -1, -1, 73, 74, 75,
+ -1, -1, -1, -1, -1, 81, 82, 83, -1, 85,
+ -1, -1, 88, 89, 3, 4, -1, 6, 7, 8,
+ 9, -1, 11, 12, -1, -1, -1, -1, 104, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 27, 28,
+ 29, 30, 31, -1, 33, -1, 35, -1, 37, 38,
+ -1, 40, 41, 42, 43, 44, -1, 46, 47, 48,
+ 49, 50, 51, -1, 53, -1, -1, -1, -1, -1,
+ -1, -1, -1, 62, -1, -1, 65, -1, -1, -1,
+ -1, -1, -1, -1, 73, 74, 75, -1, -1, -1,
+ -1, -1, 81, 82, 83, -1, 85, -1, -1, 88,
+ 89, 3, 4, -1, 6, 7, 8, 9, -1, 11,
+ 12, -1, -1, -1, -1, 104, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 27, 28, 29, 30, 31,
+ -1, 33, -1, 35, -1, 37, 38, -1, 40, 41,
+ 42, 43, 44, -1, 46, 47, 48, 49, 50, 51,
+ -1, 53, -1, -1, -1, -1, -1, -1, -1, -1,
+ 62, -1, -1, 65, -1, -1, -1, -1, -1, -1,
+ -1, 73, 74, 75, -1, -1, -1, -1, -1, 81,
+ 82, 83, -1, 85, -1, -1, 88, 89, 3, 4,
+ -1, 6, 7, 8, 9, -1, 11, 12, -1, -1,
+ -1, -1, 104, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 27, 28, 29, 30, 31, -1, 33, -1,
+ 35, -1, 37, 38, -1, 40, 41, 42, 43, -1,
+ -1, 46, 47, 48, 49, 50, 51, -1, 53, -1,
+ -1, 56, -1, -1, -1, -1, -1, 62, -1, -1,
+ 65, -1, -1, -1, -1, -1, -1, -1, 73, 74,
+ 75, -1, -1, -1, -1, -1, 81, 82, 83, -1,
+ -1, -1, -1, 88, -1, 3, 4, 92, 6, 7,
+ 8, 9, -1, 11, 12, -1, -1, -1, -1, 104,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 27,
+ 28, 29, 30, 31, -1, 33, -1, 35, -1, 37,
+ 38, -1, 40, 41, 42, 43, -1, -1, 46, 47,
+ 48, 49, 50, 51, -1, 53, -1, -1, -1, -1,
+ -1, -1, -1, -1, 62, -1, -1, 65, -1, -1,
+ -1, -1, -1, -1, -1, 73, 74, 75, -1, -1,
+ -1, -1, -1, 81, 82, 83, -1, -1, -1, -1,
+ 88, -1, 3, 4, 92, 6, 7, 8, 9, -1,
+ 11, 12, -1, -1, -1, -1, 104, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 27, 28, 29, 30,
+ 31, -1, 33, -1, 35, -1, 37, 38, -1, 40,
+ 41, 42, 43, -1, -1, 46, 47, 48, 49, 50,
+ 51, -1, 53, 54, -1, -1, -1, -1, -1, -1,
+ -1, 62, -1, -1, 65, -1, -1, -1, -1, -1,
+ -1, -1, 73, 74, 75, -1, -1, -1, -1, -1,
+ 81, 82, 83, -1, -1, 3, 4, 88, 6, 7,
+ 8, 9, -1, 11, 12, -1, -1, -1, -1, -1,
+ -1, -1, -1, 104, -1, -1, -1, -1, -1, 27,
+ 28, 29, 30, 31, -1, 33, -1, 35, -1, 37,
+ 38, -1, 40, 41, 42, 43, -1, -1, 46, 47,
+ 48, 49, 50, 51, -1, 53, -1, -1, -1, -1,
+ -1, -1, -1, -1, 62, -1, -1, 65, -1, -1,
+ -1, -1, -1, -1, -1, 73, 74, 75, -1, -1,
+ -1, -1, -1, 81, 82, 83, -1, -1, -1, -1,
+ 88, 89, 3, 4, -1, 6, 7, 8, 9, -1,
+ 11, 12, -1, -1, -1, -1, 104, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 27, 28, 29, 30,
+ 31, -1, 33, -1, 35, -1, 37, 38, -1, 40,
+ 41, 42, 43, -1, -1, 46, 47, 48, 49, 50,
+ 51, -1, 53, -1, -1, -1, -1, -1, -1, -1,
+ -1, 62, -1, -1, 65, -1, -1, -1, -1, -1,
+ -1, -1, 73, 74, 75, -1, -1, -1, -1, -1,
+ 81, 82, 83, -1, -1, -1, -1, 88, -1, 3,
+ 4, 92, 6, 7, 8, 9, -1, 11, 12, -1,
+ -1, -1, -1, 104, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 27, 28, 29, 30, 31, -1, 33,
+ -1, 35, -1, 37, 38, -1, 40, 41, 42, 43,
+ -1, -1, 46, 47, 48, 49, 50, 51, -1, 53,
+ -1, -1, -1, -1, -1, -1, -1, -1, 62, -1,
+ -1, 65, -1, -1, -1, -1, -1, -1, -1, 73,
+ 74, 75, -1, -1, -1, -1, -1, 81, 82, 83,
+ -1, -1, -1, -1, 88, -1, 3, 4, 92, 6,
+ 7, 8, 9, -1, 11, 12, -1, -1, -1, -1,
+ 104, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 27, 28, 29, 30, 31, -1, 33, -1, 35, -1,
+ 37, 38, -1, 40, 41, 42, 43, -1, -1, 46,
+ 47, 48, 49, 50, 51, -1, 53, -1, -1, -1,
+ -1, -1, -1, -1, -1, 62, -1, -1, 65, -1,
+ -1, -1, -1, -1, -1, -1, 73, 74, 75, -1,
+ -1, -1, -1, -1, 81, 82, 83, -1, -1, -1,
+ -1, 88, -1, 3, 4, 92, 6, 7, 8, 9,
+ -1, 11, 12, -1, -1, -1, -1, 104, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 27, 28, 29,
+ 30, 31, -1, 33, -1, 35, -1, 37, 38, -1,
+ 40, 41, 42, 43, -1, -1, 46, 47, 48, 49,
+ 50, 51, -1, 53, -1, -1, -1, -1, -1, -1,
+ -1, -1, 62, -1, -1, 65, -1, -1, -1, -1,
+ -1, -1, -1, 73, 74, 75, -1, -1, -1, -1,
+ -1, 81, 82, 83, -1, -1, -1, -1, 88, -1,
+ 3, 4, 92, 6, 7, 8, 9, -1, 11, 12,
+ -1, -1, -1, -1, 104, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 27, 28, 29, 30, 31, -1,
+ 33, -1, 35, -1, 37, 38, -1, 40, 41, 42,
+ 43, -1, -1, 46, 47, 48, 49, 50, 51, -1,
+ 53, -1, -1, -1, -1, -1, -1, -1, -1, 62,
+ -1, -1, 65, -1, -1, -1, -1, -1, -1, -1,
+ 73, 74, 75, -1, -1, -1, -1, -1, 81, 82,
+ 83, -1, -1, -1, -1, 88, -1, 3, 4, 92,
+ 6, 7, 8, 9, -1, 11, 12, -1, -1, -1,
+ -1, 104, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 27, 28, 29, 30, 31, -1, 33, -1, 35,
+ -1, 37, 38, -1, 40, 41, 42, 43, -1, -1,
+ 46, 47, 48, 49, 50, 51, -1, 53, -1, -1,
+ -1, -1, -1, -1, -1, -1, 62, -1, -1, 65,
+ -1, -1, -1, -1, -1, -1, -1, 73, 74, 75,
+ -1, -1, -1, -1, -1, 81, 82, 83, -1, -1,
+ 3, 4, 88, 6, 7, 8, 9, -1, 11, 12,
+ -1, -1, -1, -1, -1, -1, -1, -1, 104, -1,
+ -1, -1, -1, -1, 27, 28, 29, 30, 31, -1,
+ 33, -1, 35, -1, 37, 38, -1, 40, 41, 42,
+ 43, -1, -1, 46, 47, 48, 49, 50, 51, -1,
+ 53, -1, -1, -1, -1, -1, -1, -1, -1, 62,
+ -1, -1, 65, -1, -1, -1, -1, -1, -1, -1,
+ 73, 74, 75, -1, -1, -1, -1, -1, 81, 82,
+ 83, -1, -1, 3, 4, 88, 6, 7, 8, 9,
+ -1, 11, 12, -1, -1, -1, -1, -1, -1, -1,
+ -1, 104, -1, -1, -1, -1, -1, 27, 28, 29,
+ 30, 31, -1, 33, -1, 35, -1, 37, 38, -1,
+ 40, 41, 42, 43, -1, -1, 46, 47, 48, 49,
+ 50, 51, -1, 53, -1, -1, -1, -1, -1, -1,
+ -1, -1, 62, -1, -1, 65, -1, -1, -1, -1,
+ -1, -1, -1, 73, 74, 75, -1, -1, -1, -1,
+ -1, 81, 82, 83, -1, -1, 3, 4, 88, 6,
+ 7, 8, 9, -1, 11, 12, -1, -1, -1, -1,
+ -1, -1, -1, -1, 104, -1, -1, -1, -1, -1,
+ 27, 28, -1, -1, 31, -1, 33, -1, 35, -1,
+ 37, 38, -1, 40, 41, 42, 43, -1, -1, 46,
+ 47, 48, 49, 50, 51, -1, 53, -1, -1, -1,
+ -1, -1, -1, -1, -1, 62, -1, -1, 65, -1,
+ -1, -1, -1, -1, -1, -1, 73, 74, 75, -1,
+ -1, -1, -1, -1, 81, 82, 83, -1, -1, -1,
+ -1, 88, 3, 4, 5, 6, 7, -1, -1, 10,
+ -1, 12, -1, -1, -1, -1, -1, 104, -1, 3,
+ 4, 5, 6, 7, -1, -1, 27, -1, 12, -1,
+ 31, -1, -1, -1, 35, -1, -1, -1, -1, -1,
+ 41, -1, -1, 27, -1, -1, -1, 31, -1, -1,
+ 51, 35, 53, -1, -1, -1, -1, 41, -1, -1,
+ -1, -1, -1, -1, 65, -1, -1, 51, -1, 53,
+ -1, -1, -1, -1, 75, -1, 60, -1, -1, -1,
+ -1, 65, 83, -1, -1, -1, -1, 88, -1, -1,
+ -1, 75, 3, 4, 5, 6, 7, -1, -1, 83,
+ -1, 12, -1, -1, 88, -1, -1, -1, -1, 3,
+ 4, 5, 6, 7, -1, -1, 27, -1, 12, -1,
+ 31, -1, -1, -1, 35, -1, -1, -1, -1, -1,
+ 41, -1, -1, 27, -1, -1, -1, 31, -1, -1,
+ 51, 35, 53, -1, -1, 56, -1, 41, -1, -1,
+ -1, -1, -1, -1, 65, -1, -1, 51, -1, 53,
+ -1, -1, -1, -1, 75, -1, -1, -1, -1, -1,
+ -1, 65, 83, -1, -1, -1, -1, 88, -1, -1,
+ -1, 75, -1, -1, -1, -1, -1, -1, -1, 83,
+ 32, 57, 58, 59, 88, 61, 62, 63, 64, 65,
+ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
+ 76, 77, 78, 79, -1, 57, 58, 59, -1, 61,
+ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79, 55, 105,
+ 57, 58, 59, -1, 61, 62, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 77, 78, 79, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, 77, 78, 79, 57, 58, 59, 60,
+ 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
+ 71, 72, 73, 74, 75, 76, 77, 78, 79, 57,
+ 58, 59, -1, 61, 62, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
+ 78, 79, 57, 58, 59, -1, 61, 62, 63, 64,
+ 65, 66, 67, 68, 69, -1, 71, 72, 73, 74,
+ 75, 76, 77, 78, 79, 62, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 77, 78, 79, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, 77, 78, 79
+};
+/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
+#line 3 "/usr/local/lib/bison.simple"
+
+/* Skeleton output parser for bison,
+ Copyright (C) 1984, 1989, 1990 Bob Corbett and Richard Stallman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#ifndef alloca
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not GNU C. */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi)
+#include <alloca.h>
+#else /* not sparc */
+#if defined (MSDOS) && !defined (__TURBOC__)
+#include <malloc.h>
+#else /* not MSDOS, or __TURBOC__ */
+#if defined(_AIX)
+#include <malloc.h>
+ #pragma alloca
+#else /* not MSDOS, __TURBOC__, or _AIX */
+#ifdef __hpux
+#ifdef __cplusplus
+extern "C" {
+void *alloca (unsigned int);
+};
+#else /* not __cplusplus */
+void *alloca ();
+#endif /* not __cplusplus */
+#endif /* __hpux */
+#endif /* not _AIX */
+#endif /* not MSDOS, or __TURBOC__ */
+#endif /* not sparc. */
+#endif /* not GNU C. */
+#endif /* alloca not defined. */
+
+/* This is the parser code that is written into each bison parser
+ when the %semantic_parser declaration is not specified in the grammar.
+ It was written by Richard Stallman by simplifying the hairy parser
+ used when %semantic_parser is specified. */
+
+/* Note: there must be only one dollar sign in this file.
+ It is replaced by the list of actions, each action
+ as one case of the switch. */
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY -2
+#define YYEOF 0
+#define YYACCEPT return(0)
+#define YYABORT return(1)
+#define YYERROR goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+ This remains here temporarily to ease the
+ transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+#define YYFAIL goto yyerrlab
+#define YYRECOVERING() (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { yychar = (token), yylval = (value); \
+ yychar1 = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { yyerror ("syntax error: cannot back up"); YYERROR; } \
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+#ifndef YYPURE
+#define YYLEX yylex()
+#endif
+
+#ifdef YYPURE
+#ifdef YYLSP_NEEDED
+#define YYLEX yylex(&yylval, &yylloc)
+#else
+#define YYLEX yylex(&yylval)
+#endif
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYPURE
+
+int yychar; /* the lookahead symbol */
+YYSTYPE yylval; /* the semantic value of the */
+ /* lookahead symbol */
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc; /* location data for the lookahead */
+ /* symbol */
+#endif
+
+int yynerrs; /* number of parse errors so far */
+#endif /* not YYPURE */
+
+#if YYDEBUG != 0
+int yydebug; /* nonzero means print parse trace */
+/* Since this is uninitialized, it does not stop multiple parsers
+ from coexisting. */
+#endif
+
+/* YYINITDEPTH indicates the initial size of the parser's stacks */
+
+#ifndef YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH is the maximum size the stacks can grow to
+ (effective only if the built-in stack extension method is used). */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+
+/* Prevent warning if -Wstrict-prototypes. */
+#ifdef __GNUC__
+int yyparse (void);
+#endif
+
+#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */
+#define __yy_bcopy(FROM,TO,COUNT) __builtin_memcpy(TO,FROM,COUNT)
+#else /* not GNU C or C++ */
+#ifndef __cplusplus
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_bcopy (from, to, count)
+ char *from;
+ char *to;
+ int count;
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#else /* __cplusplus */
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_bcopy (char *from, char *to, int count)
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#endif
+#endif
+
+#line 184 "/usr/local/lib/bison.simple"
+
+/* The user can define YYPARSE_PARAM as the name of an argument to be passed
+ into yyparse. The argument should have type void *.
+ It should actually point to an object.
+ Grammar actions can access the variable by casting it
+ to the proper pointer type. */
+
+#ifdef YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
+#else
+#define YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL
+#endif
+
+int
+yyparse(YYPARSE_PARAM)
+ YYPARSE_PARAM_DECL
+{
+ register int yystate;
+ register int yyn;
+ register short *yyssp;
+ register YYSTYPE *yyvsp;
+ int yyerrstatus; /* number of tokens to shift before error messages enabled */
+ int yychar1 = 0; /* lookahead token as an internal (translated) token number */
+
+ short yyssa[YYINITDEPTH]; /* the state stack */
+ YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
+
+ short *yyss = yyssa; /* refer to the stacks thru separate pointers */
+ YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */
+ YYLTYPE *yyls = yylsa;
+ YYLTYPE *yylsp;
+
+#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--)
+#else
+#define YYPOPSTACK (yyvsp--, yyssp--)
+#endif
+
+ int yystacksize = YYINITDEPTH;
+
+#ifdef YYPURE
+ int yychar;
+ YYSTYPE yylval;
+ int yynerrs;
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylloc;
+#endif
+#endif
+
+ YYSTYPE yyval; /* the variable used to return */
+ /* semantic values from the action */
+ /* routines */
+
+ int yylen;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Starting parse\n");
+#endif
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss - 1;
+ yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in yystate . */
+/* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks. */
+yynewstate:
+
+ *++yyssp = yystate;
+
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ /* Give user a chance to reallocate the stack */
+ /* Use copies of these so that the &'s don't force the real ones into memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+ YYLTYPE *yyls1 = yyls;
+#endif
+
+ /* Get the current used size of the three stacks, in elements. */
+ int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ /* Each stack pointer address is followed by the size of
+ the data in use in that stack, in bytes. */
+#ifdef YYLSP_NEEDED
+ /* This used to be a conditional around just the two extra args,
+ but that might be undefined if yyoverflow is a macro. */
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yyls1, size * sizeof (*yylsp),
+ &yystacksize);
+#else
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yystacksize);
+#endif
+
+ yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+ yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+ /* Extend the stack our own way. */
+ if (yystacksize >= YYMAXDEPTH)
+ {
+ yyerror("parser stack overflow");
+ return 2;
+ }
+ yystacksize *= 2;
+ if (yystacksize > YYMAXDEPTH)
+ yystacksize = YYMAXDEPTH;
+ yyss = (short *) alloca (yystacksize * sizeof (*yyssp));
+ __yy_bcopy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
+ yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp));
+ __yy_bcopy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+ yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp));
+ __yy_bcopy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + size - 1;
+ yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+ if (yyssp >= yyss + yystacksize - 1)
+ YYABORT;
+ }
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+ goto yybackup;
+ yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a lookahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* yychar is either YYEMPTY or YYEOF
+ or a valid token in external form. */
+
+ if (yychar == YYEMPTY)
+ {
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Reading a token: ");
+#endif
+ yychar = YYLEX;
+ }
+
+ /* Convert token to internal form (in yychar1) for indexing tables with */
+
+ if (yychar <= 0) /* This means end of input. */
+ {
+ yychar1 = 0;
+ yychar = YYEOF; /* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Now at end of input.\n");
+#endif
+ }
+ else
+ {
+ yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
+ /* Give the individual parser a way to print the precise meaning
+ of a token, for further debugging info. */
+#ifdef YYPRINT
+ YYPRINT (stderr, yychar, yylval);
+#endif
+ fprintf (stderr, ")\n");
+ }
+#endif
+ }
+
+ yyn += yychar1;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+ goto yydefault;
+
+ yyn = yytable[yyn];
+
+ /* yyn is what to do for this token type in this state.
+ Negative => reduce, -yyn is rule number.
+ Positive => shift, yyn is new state.
+ New state is final state => don't bother to shift,
+ just return success.
+ 0, or most negative number => error. */
+
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrlab;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the lookahead token. */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
+#endif
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ /* count tokens shifted since error; after three, turn off error status. */
+ if (yyerrstatus) yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+/* Do the default action for the current state. */
+yydefault:
+
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+
+/* Do a reduction. yyn is the number of a rule to reduce with. */
+yyreduce:
+ yylen = yyr2[yyn];
+ if (yylen > 0)
+ yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ int i;
+
+ fprintf (stderr, "Reducing via rule %d (line %d), ",
+ yyn, yyrline[yyn]);
+
+ /* Print the symbols being reduced, and their result. */
+ for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+ fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+ fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+ }
+#endif
+
+
+ switch (yyn) {
+
+case 2:
+#line 293 "parse.y"
+{
+ /* In case there were missing closebraces,
+ get us back to the global binding level. */
+ while (! global_bindings_p ())
+ poplevel (0, 0, 0);
+ finish_file ();
+ ;
+ break;}
+case 3:
+#line 307 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 4:
+#line 308 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 5:
+#line 310 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 6:
+#line 314 "parse.y"
+{ have_extern_spec = 1;
+ used_extern_spec = 0;
+ yyval.ttype = NULL_TREE; ;
+ break;}
+case 7:
+#line 319 "parse.y"
+{ have_extern_spec = 0; ;
+ break;}
+case 10:
+#line 328 "parse.y"
+{ if (pending_lang_change) do_pending_lang_change(); ;
+ break;}
+case 11:
+#line 330 "parse.y"
+{ if (! global_bindings_p () && ! pseudo_global_level_p())
+ pop_everything (); ;
+ break;}
+case 12:
+#line 336 "parse.y"
+{ if (pending_inlines) do_pending_inlines (); ;
+ break;}
+case 13:
+#line 338 "parse.y"
+{ if (pending_inlines) do_pending_inlines (); ;
+ break;}
+case 14:
+#line 340 "parse.y"
+{ if (pending_inlines) do_pending_inlines (); ;
+ break;}
+case 16:
+#line 343 "parse.y"
+{ if (TREE_CHAIN (yyvsp[-2].ttype)) yyvsp[-2].ttype = combine_strings (yyvsp[-2].ttype);
+ assemble_asm (yyvsp[-2].ttype); ;
+ break;}
+case 17:
+#line 346 "parse.y"
+{ pop_lang_context (); ;
+ break;}
+case 18:
+#line 348 "parse.y"
+{ pop_lang_context (); ;
+ break;}
+case 19:
+#line 350 "parse.y"
+{ if (pending_inlines) do_pending_inlines ();
+ pop_lang_context (); ;
+ break;}
+case 20:
+#line 353 "parse.y"
+{ if (pending_inlines) do_pending_inlines ();
+ pop_lang_context (); ;
+ break;}
+case 21:
+#line 359 "parse.y"
+{ push_lang_context (yyvsp[0].ttype); ;
+ break;}
+case 22:
+#line 364 "parse.y"
+{ begin_template_parm_list (); ;
+ break;}
+case 23:
+#line 366 "parse.y"
+{ yyval.ttype = end_template_parm_list (yyvsp[-1].ttype); ;
+ break;}
+case 24:
+#line 371 "parse.y"
+{ yyval.ttype = process_template_parm (NULL_TREE, yyvsp[0].ttype); ;
+ break;}
+case 25:
+#line 373 "parse.y"
+{ yyval.ttype = process_template_parm (yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 26:
+#line 385 "parse.y"
+{
+ if (yyvsp[-1].ttype == signature_type_node)
+ sorry ("signature as template type parameter");
+ else if (yyvsp[-1].ttype != class_type_node)
+ error ("template type parameter must use keyword `class'");
+ yyval.ttype = build_tree_list (yyvsp[0].ttype, NULL_TREE);
+ ;
+ break;}
+case 27:
+#line 393 "parse.y"
+{
+ if (yyvsp[-3].ttype == signature_type_node)
+ sorry ("signature as template type parameter");
+ else if (yyvsp[-3].ttype != class_type_node)
+ error ("template type parameter must use keyword `class'");
+ warning ("restricted template type parameters not yet implemented");
+ yyval.ttype = build_tree_list (yyvsp[-2].ttype, yyvsp[0].ttype);
+ ;
+ break;}
+case 29:
+#line 406 "parse.y"
+{ warning ("use of `overload' is an anachronism"); ;
+ break;}
+case 30:
+#line 410 "parse.y"
+{ declare_overloaded (yyvsp[0].ttype); ;
+ break;}
+case 31:
+#line 412 "parse.y"
+{ declare_overloaded (yyvsp[0].ttype); ;
+ break;}
+case 32:
+#line 419 "parse.y"
+{ yychar = '{'; goto template1; ;
+ break;}
+case 34:
+#line 422 "parse.y"
+{ yychar = '{'; goto template1; ;
+ break;}
+case 36:
+#line 425 "parse.y"
+{ yychar = ':'; goto template1; ;
+ break;}
+case 38:
+#line 428 "parse.y"
+{
+ yychar = ':';
+ template1:
+ if (current_aggr == exception_type_node)
+ error ("template type must define an aggregate or union");
+ else if (current_aggr == signature_type_node)
+ sorry ("template type defining a signature");
+ /* Maybe pedantic warning for union?
+ How about an enum? :-) */
+ end_template_decl (yyvsp[-2].ttype, yyvsp[-1].ttype, current_aggr, 1);
+ reinit_parse_for_template (yychar, yyvsp[-2].ttype, yyvsp[-1].ttype);
+ yychar = YYEMPTY;
+ ;
+ break;}
+case 40:
+#line 443 "parse.y"
+{
+ end_template_decl (yyvsp[-2].ttype, yyvsp[-1].ttype, current_aggr, 0);
+ /* declare $2 as template name with $1 parm list */
+ ;
+ break;}
+case 41:
+#line 448 "parse.y"
+{
+ end_template_decl (yyvsp[-2].ttype, yyvsp[-1].ttype, current_aggr, 0);
+ /* declare $2 as template name with $1 parm list */
+ ;
+ break;}
+case 42:
+#line 455 "parse.y"
+{
+ tree d;
+ int momentary;
+ int def = (yyvsp[0].itype != ';');
+ momentary = suspend_momentary ();
+ d = start_decl (yyvsp[-4].ttype, /*current_declspecs*/NULL_TREE, 0,
+ yyvsp[-3].ttype);
+ cplus_decl_attributes (d, yyvsp[-1].ttype);
+ finish_decl (d, NULL_TREE, yyvsp[-2].ttype, 0);
+ end_template_decl (yyvsp[-5].ttype, d, 0, def);
+ if (def)
+ reinit_parse_for_template ((int) yyvsp[0].itype, yyvsp[-5].ttype, d);
+ resume_momentary (momentary);
+ ;
+ break;}
+case 43:
+#line 472 "parse.y"
+{
+ tree d;
+ int momentary;
+ int def = (yyvsp[0].itype != ';');
+
+ current_declspecs = yyvsp[-5].ttype;
+ momentary = suspend_momentary ();
+ d = start_decl (yyvsp[-4].ttype, current_declspecs,
+ 0, yyvsp[-3].ttype);
+ cplus_decl_attributes (d, yyvsp[-1].ttype);
+ finish_decl (d, NULL_TREE, yyvsp[-2].ttype, 0);
+ end_template_decl (yyvsp[-6].ttype, d, 0, def);
+ if (def)
+ {
+ reinit_parse_for_template ((int) yyvsp[0].itype, yyvsp[-6].ttype, d);
+ yychar = YYEMPTY;
+ }
+ note_list_got_semicolon (yyvsp[-5].ttype);
+ resume_momentary (momentary);
+ ;
+ break;}
+case 44:
+#line 493 "parse.y"
+{
+ int def = (yyvsp[0].itype != ';');
+ tree d = start_decl (yyvsp[-1].ttype, yyvsp[-2].ttype, 0, NULL_TREE);
+ finish_decl (d, NULL_TREE, NULL_TREE, 0);
+ end_template_decl (yyvsp[-3].ttype, d, 0, def);
+ if (def)
+ reinit_parse_for_template ((int) yyvsp[0].itype, yyvsp[-3].ttype, d);
+ ;
+ break;}
+case 45:
+#line 502 "parse.y"
+{ end_template_decl (yyvsp[-2].ttype, 0, 0, 0); ;
+ break;}
+case 46:
+#line 503 "parse.y"
+{ end_template_decl (yyvsp[-2].ttype, 0, 0, 0); ;
+ break;}
+case 47:
+#line 506 "parse.y"
+{ yyval.itype = '{'; ;
+ break;}
+case 48:
+#line 507 "parse.y"
+{ yyval.itype = ':'; ;
+ break;}
+case 49:
+#line 508 "parse.y"
+{ yyval.itype = ';'; ;
+ break;}
+case 50:
+#line 509 "parse.y"
+{ yyval.itype = '='; ;
+ break;}
+case 51:
+#line 510 "parse.y"
+{ yyval.itype = RETURN; ;
+ break;}
+case 52:
+#line 515 "parse.y"
+{;
+ break;}
+case 53:
+#line 517 "parse.y"
+{;
+ break;}
+case 54:
+#line 520 "parse.y"
+{ tree d;
+ d = start_decl (yyvsp[-1].ttype, yyval.ttype, 0, NULL_TREE);
+ finish_decl (d, NULL_TREE, NULL_TREE, 0);
+ ;
+ break;}
+case 55:
+#line 525 "parse.y"
+{
+ note_list_got_semicolon (yyval.ttype);
+ ;
+ break;}
+case 56:
+#line 530 "parse.y"
+{ tree d;
+ d = start_decl (yyvsp[-1].ttype, yyval.ttype, 0, NULL_TREE);
+ finish_decl (d, NULL_TREE, NULL_TREE, 0);
+ note_list_got_semicolon (yyval.ttype);
+ ;
+ break;}
+case 57:
+#line 536 "parse.y"
+{ pedwarn ("empty declaration"); ;
+ break;}
+case 59:
+#line 539 "parse.y"
+{
+ tree t = yyval.ttype;
+ shadow_tag (t);
+ if (TREE_CODE (t) == TREE_LIST
+ && TREE_PURPOSE (t) == NULL_TREE)
+ {
+ t = TREE_VALUE (t);
+ if (IS_AGGR_TYPE (t)
+ && IDENTIFIER_TEMPLATE (TYPE_IDENTIFIER (t)))
+ {
+ if (CLASSTYPE_USE_TEMPLATE (t) == 0)
+ SET_CLASSTYPE_TEMPLATE_SPECIALIZATION (t);
+ else if (CLASSTYPE_TEMPLATE_INSTANTIATION (t))
+ error ("override declaration for already-expanded template");
+ }
+ }
+ note_list_got_semicolon (yyval.ttype);
+ ;
+ break;}
+case 63:
+#line 564 "parse.y"
+{
+ finish_function (lineno, 1);
+ /* finish_function performs these three statements:
+
+ expand_end_bindings (getdecls (), 1, 0);
+ poplevel (1, 1, 0);
+
+ expand_end_bindings (0, 0, 0);
+ poplevel (0, 0, 1);
+ */
+ if (yyval.ttype) process_next_inline (yyval.ttype);
+ ;
+ break;}
+case 64:
+#line 577 "parse.y"
+{
+ finish_function (lineno, 1);
+ /* finish_function performs these three statements:
+
+ expand_end_bindings (getdecls (), 1, 0);
+ poplevel (1, 1, 0);
+
+ expand_end_bindings (0, 0, 0);
+ poplevel (0, 0, 1);
+ */
+ if (yyval.ttype) process_next_inline (yyval.ttype);
+ ;
+ break;}
+case 65:
+#line 590 "parse.y"
+{ finish_function (lineno, 0);
+ if (yyval.ttype) process_next_inline (yyval.ttype); ;
+ break;}
+case 66:
+#line 593 "parse.y"
+{ finish_function (lineno, 0);
+ if (yyval.ttype) process_next_inline (yyval.ttype); ;
+ break;}
+case 67:
+#line 596 "parse.y"
+{ finish_function (lineno, 0);
+ if (yyval.ttype) process_next_inline (yyval.ttype); ;
+ break;}
+case 68:
+#line 599 "parse.y"
+{;
+ break;}
+case 69:
+#line 601 "parse.y"
+{;
+ break;}
+case 70:
+#line 603 "parse.y"
+{;
+ break;}
+case 71:
+#line 608 "parse.y"
+{ if (! start_function (yyval.ttype, yyvsp[-1].ttype, yyvsp[0].ttype, 0))
+ YYERROR1;
+ reinit_parse_for_function ();
+ yyval.ttype = NULL_TREE; ;
+ break;}
+case 72:
+#line 613 "parse.y"
+{ if (! start_function (yyval.ttype, yyvsp[-1].ttype, yyvsp[0].ttype, 0))
+ YYERROR1;
+ reinit_parse_for_function ();
+ yyval.ttype = NULL_TREE; ;
+ break;}
+case 73:
+#line 618 "parse.y"
+{ if (! start_function (NULL_TREE, yyval.ttype, yyvsp[0].ttype, 0))
+ YYERROR1;
+ reinit_parse_for_function ();
+ yyval.ttype = NULL_TREE; ;
+ break;}
+case 74:
+#line 623 "parse.y"
+{ start_function (NULL_TREE, TREE_VALUE (yyval.ttype), NULL_TREE, 1);
+ reinit_parse_for_function (); ;
+ break;}
+case 75:
+#line 631 "parse.y"
+{
+ yyval.ttype = build_parse_node (CALL_EXPR, TREE_VALUE (yyvsp[-5].ttype), yyvsp[-3].ttype, yyvsp[-1].ttype);
+ yyval.ttype = start_method (TREE_CHAIN (yyvsp[-5].ttype), yyval.ttype, yyvsp[0].ttype);
+ rest_of_mdef:
+ if (! yyval.ttype)
+ YYERROR1;
+ if (yychar == YYEMPTY)
+ yychar = YYLEX;
+ reinit_parse_for_method (yychar, yyval.ttype); ;
+ break;}
+case 76:
+#line 641 "parse.y"
+{
+ yyval.ttype = build_parse_node (CALL_EXPR, TREE_VALUE (yyvsp[-3].ttype),
+ empty_parms (), yyvsp[-1].ttype);
+ yyval.ttype = start_method (TREE_CHAIN (yyvsp[-3].ttype), yyval.ttype, yyvsp[0].ttype);
+ goto rest_of_mdef;
+ ;
+ break;}
+case 77:
+#line 648 "parse.y"
+{ yyval.ttype = start_method (yyval.ttype, yyvsp[-1].ttype, yyvsp[0].ttype); goto rest_of_mdef; ;
+ break;}
+case 78:
+#line 650 "parse.y"
+{ yyval.ttype = start_method (yyval.ttype, yyvsp[-1].ttype, yyvsp[0].ttype); goto rest_of_mdef; ;
+ break;}
+case 79:
+#line 652 "parse.y"
+{ yyval.ttype = start_method (NULL_TREE, yyval.ttype, yyvsp[0].ttype); goto rest_of_mdef; ;
+ break;}
+case 80:
+#line 656 "parse.y"
+{
+ if (! current_function_parms_stored)
+ store_parm_decls ();
+ yyval.ttype = yyvsp[0].ttype;
+ ;
+ break;}
+case 81:
+#line 664 "parse.y"
+{ store_return_init (yyval.ttype, NULL_TREE); ;
+ break;}
+case 82:
+#line 666 "parse.y"
+{ store_return_init (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 83:
+#line 668 "parse.y"
+{ store_return_init (yyval.ttype, yyvsp[-1].ttype); ;
+ break;}
+case 84:
+#line 670 "parse.y"
+{ store_return_init (yyval.ttype, NULL_TREE); ;
+ break;}
+case 85:
+#line 675 "parse.y"
+{
+ if (yyvsp[0].itype == 0)
+ error ("no base initializers given following ':'");
+ setup_vtbl_ptr ();
+ /* Always keep the BLOCK node associated with the outermost
+ pair of curley braces of a function. These are needed
+ for correct operation of dwarfout.c. */
+ keep_next_level ();
+ ;
+ break;}
+case 86:
+#line 688 "parse.y"
+{
+ if (! current_function_parms_stored)
+ store_parm_decls ();
+
+ /* Flag that we are processing base and member initializers. */
+ current_vtable_decl = error_mark_node;
+
+ if (DECL_CONSTRUCTOR_P (current_function_decl))
+ {
+ /* Make a contour for the initializer list. */
+ pushlevel (0);
+ clear_last_expr ();
+ expand_start_bindings (0);
+ }
+ else if (current_class_type == NULL_TREE)
+ error ("base initializers not allowed for non-member functions");
+ else if (! DECL_CONSTRUCTOR_P (current_function_decl))
+ error ("only constructors take base initializers");
+ ;
+ break;}
+case 87:
+#line 711 "parse.y"
+{ yyval.itype = 0; ;
+ break;}
+case 88:
+#line 713 "parse.y"
+{ yyval.itype = 1; ;
+ break;}
+case 91:
+#line 719 "parse.y"
+{
+ if (current_class_name && !flag_traditional)
+ pedwarn ("anachronistic old style base class initializer");
+ expand_member_init (C_C_D, NULL_TREE, yyvsp[-1].ttype);
+ ;
+ break;}
+case 92:
+#line 725 "parse.y"
+{
+ if (current_class_name && !flag_traditional)
+ pedwarn ("anachronistic old style base class initializer");
+ expand_member_init (C_C_D, NULL_TREE, void_type_node);
+ ;
+ break;}
+case 93:
+#line 731 "parse.y"
+{ expand_member_init (C_C_D, yyval.ttype, yyvsp[-1].ttype); ;
+ break;}
+case 94:
+#line 733 "parse.y"
+{ expand_member_init (C_C_D, yyval.ttype, void_type_node); ;
+ break;}
+case 95:
+#line 735 "parse.y"
+{ expand_member_init (C_C_D, yyval.ttype, yyvsp[-1].ttype); ;
+ break;}
+case 96:
+#line 737 "parse.y"
+{ expand_member_init (C_C_D, yyval.ttype, void_type_node); ;
+ break;}
+case 97:
+#line 740 "parse.y"
+{
+ do_member_init (OP0 (yyvsp[-3].ttype), OP1 (yyvsp[-3].ttype), yyvsp[-1].ttype);
+ ;
+ break;}
+case 98:
+#line 744 "parse.y"
+{
+ do_member_init (OP0 (yyvsp[-1].ttype), OP1 (yyvsp[-1].ttype), void_type_node);
+ ;
+ break;}
+case 107:
+#line 768 "parse.y"
+{ do_type_instantiation (yyvsp[0].ttype ? yyvsp[0].ttype : yyvsp[-1].ttype, NULL_TREE); ;
+ break;}
+case 108:
+#line 770 "parse.y"
+{ do_function_instantiation (yyvsp[-1].ttype, yyvsp[0].ttype, NULL_TREE); ;
+ break;}
+case 109:
+#line 772 "parse.y"
+{ do_type_instantiation (yyvsp[0].ttype ? yyvsp[0].ttype : yyvsp[-1].ttype, yyvsp[-3].ttype); ;
+ break;}
+case 110:
+#line 774 "parse.y"
+{ do_function_instantiation (yyvsp[-1].ttype, yyvsp[0].ttype, yyvsp[-3].ttype); ;
+ break;}
+case 111:
+#line 779 "parse.y"
+{ if (yyvsp[0].ttype) yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 112:
+#line 784 "parse.y"
+{ yyval.ttype = lookup_template_class (yyval.ttype, yyvsp[-1].ttype, NULL_TREE); ;
+ break;}
+case 113:
+#line 786 "parse.y"
+{ yyval.ttype = lookup_template_class (yyval.ttype, yyvsp[-1].ttype, NULL_TREE); ;
+ break;}
+case 114:
+#line 791 "parse.y"
+{ yyval.ttype = instantiate_class_template (yyvsp[0].ttype, 1); ;
+ break;}
+case 115:
+#line 796 "parse.y"
+{ yyval.ttype = build_tree_list (NULL_TREE, yyval.ttype); ;
+ break;}
+case 116:
+#line 798 "parse.y"
+{ yyval.ttype = chainon (yyval.ttype, build_tree_list (NULL_TREE, yyvsp[0].ttype)); ;
+ break;}
+case 117:
+#line 803 "parse.y"
+{ yyval.ttype = groktypename (yyval.ttype); ;
+ break;}
+case 119:
+#line 809 "parse.y"
+{
+ tree t, decl, tmpl;
+
+ tmpl = TREE_PURPOSE (IDENTIFIER_TEMPLATE (yyvsp[-1].ttype));
+ t = xref_tag (DECL_TEMPLATE_INFO (tmpl)->aggr, yyvsp[-1].ttype, yyvsp[0].ttype, 0);
+ set_current_level_tags_transparency (1);
+ my_friendly_assert (TREE_CODE (t) == RECORD_TYPE
+ || TREE_CODE (t) == UNION_TYPE, 257);
+ yyval.ttype = t;
+
+ /* Now, put a copy of the decl in global scope, to avoid
+ recursive expansion. */
+ decl = IDENTIFIER_LOCAL_VALUE (yyvsp[-1].ttype);
+ if (!decl)
+ decl = IDENTIFIER_CLASS_VALUE (yyvsp[-1].ttype);
+ /* Now, put a copy of the decl in global scope, to avoid
+ recursive expansion. */
+ if (decl)
+ {
+ /* Need to copy it to clear the chain pointer,
+ and need to get it into permanent storage. */
+ my_friendly_assert (TREE_CODE (decl) == TYPE_DECL, 258);
+ push_obstacks (&permanent_obstack, &permanent_obstack);
+ decl = copy_node (decl);
+ if (DECL_LANG_SPECIFIC (decl))
+ copy_lang_decl (decl);
+ pop_obstacks ();
+ pushdecl_top_level (decl);
+ }
+ /* Kludge; see instantiate_class_template. */
+ TYPE_BEING_DEFINED (t) = 0;
+ ;
+ break;}
+case 120:
+#line 842 "parse.y"
+{
+ tree t = finish_struct (yyvsp[-3].ttype, yyvsp[-1].ttype, 0);
+
+ pop_obstacks ();
+ end_template_instantiation (yyvsp[-5].ttype);
+
+ /* Now go after the methods & class data. */
+ instantiate_member_templates (yyvsp[-5].ttype);
+
+ pop_tinst_level();
+
+ CLASSTYPE_GOT_SEMICOLON (t) = 1;
+ ;
+ break;}
+case 121:
+#line 859 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 122:
+#line 861 "parse.y"
+{ yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 123:
+#line 866 "parse.y"
+{ yyval.ttype = NULL_TREE; /* never used from here... */;
+ break;}
+case 124:
+#line 868 "parse.y"
+{ yyval.ttype = yyvsp[-1].ttype; /*???*/ ;
+ break;}
+case 125:
+#line 872 "parse.y"
+{ yyval.code = NEGATE_EXPR; ;
+ break;}
+case 126:
+#line 874 "parse.y"
+{ yyval.code = CONVERT_EXPR; ;
+ break;}
+case 127:
+#line 876 "parse.y"
+{ yyval.code = PREINCREMENT_EXPR; ;
+ break;}
+case 128:
+#line 878 "parse.y"
+{ yyval.code = PREDECREMENT_EXPR; ;
+ break;}
+case 129:
+#line 880 "parse.y"
+{ yyval.code = TRUTH_NOT_EXPR; ;
+ break;}
+case 130:
+#line 884 "parse.y"
+{ yyval.ttype = build_x_compound_expr (yyval.ttype); ;
+ break;}
+case 132:
+#line 890 "parse.y"
+{ error ("ANSI C++ forbids an empty condition for `%s'",
+ cond_stmt_keyword);
+ yyval.ttype = integer_zero_node; ;
+ break;}
+case 133:
+#line 894 "parse.y"
+{ yyval.ttype = yyvsp[-1].ttype; ;
+ break;}
+case 134:
+#line 899 "parse.y"
+{ error ("ANSI C++ forbids an empty condition for `%s'",
+ cond_stmt_keyword);
+ yyval.ttype = integer_zero_node; ;
+ break;}
+case 135:
+#line 903 "parse.y"
+{ yyval.ttype = yyvsp[-1].ttype; ;
+ break;}
+case 136:
+#line 908 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 138:
+#line 911 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 139:
+#line 916 "parse.y"
+{ {
+ tree d;
+ for (d = getdecls (); d; d = TREE_CHAIN (d))
+ if (TREE_CODE (d) == TYPE_DECL) {
+ tree s = TREE_TYPE (d);
+ if (TREE_CODE (s) == RECORD_TYPE)
+ cp_error ("definition of class `%T' in condition", s);
+ else if (TREE_CODE (s) == ENUMERAL_TYPE)
+ cp_error ("definition of enum `%T' in condition", s);
+ }
+ }
+ current_declspecs = yyvsp[-5].ttype;
+ yyvsp[0].itype = suspend_momentary ();
+ yyval.ttype = start_decl (yyvsp[-4].ttype, current_declspecs, 1, yyvsp[-3].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[-1].ttype);
+ ;
+ break;}
+case 140:
+#line 933 "parse.y"
+{
+ finish_decl (yyvsp[-1].ttype, yyvsp[0].ttype, yyvsp[-3].ttype, 0);
+ resume_momentary (yyvsp[-2].itype);
+ yyval.ttype = yyvsp[-1].ttype;
+ if (TREE_CODE (TREE_TYPE (yyval.ttype)) == ARRAY_TYPE)
+ cp_error ("definition of array `%#D' in condition", yyval.ttype);
+ ;
+ break;}
+case 142:
+#line 945 "parse.y"
+{ finish_stmt (); ;
+ break;}
+case 143:
+#line 947 "parse.y"
+{ finish_stmt (); ;
+ break;}
+case 144:
+#line 949 "parse.y"
+{ finish_stmt (); ;
+ break;}
+case 146:
+#line 956 "parse.y"
+{ yyval.ttype = tree_cons (NULL_TREE, yyval.ttype,
+ build_tree_list (NULL_TREE, yyvsp[0].ttype)); ;
+ break;}
+case 147:
+#line 959 "parse.y"
+{ yyval.ttype = tree_cons (NULL_TREE, yyval.ttype,
+ build_tree_list (NULL_TREE, error_mark_node)); ;
+ break;}
+case 148:
+#line 962 "parse.y"
+{ chainon (yyval.ttype, build_tree_list (NULL_TREE, yyvsp[0].ttype)); ;
+ break;}
+case 149:
+#line 964 "parse.y"
+{ chainon (yyval.ttype, build_tree_list (NULL_TREE, error_mark_node)); ;
+ break;}
+case 150:
+#line 969 "parse.y"
+{ yyval.ttype = build_tree_list (NULL_TREE, yyval.ttype); ;
+ break;}
+case 152:
+#line 975 "parse.y"
+{
+#if 0
+ if (TREE_CODE (yyval.ttype) == TYPE_EXPR)
+ yyval.ttype = build_component_type_expr (C_C_D, yyval.ttype, NULL_TREE, 1);
+#endif
+ ;
+ break;}
+case 153:
+#line 983 "parse.y"
+{ yyvsp[0].itype = pedantic;
+ pedantic = 0; ;
+ break;}
+case 154:
+#line 986 "parse.y"
+{ yyval.ttype = yyvsp[0].ttype;
+ pedantic = yyvsp[-2].itype; ;
+ break;}
+case 155:
+#line 989 "parse.y"
+{ yyval.ttype = build_x_indirect_ref (yyvsp[0].ttype, "unary *"); ;
+ break;}
+case 156:
+#line 991 "parse.y"
+{ yyval.ttype = build_x_unary_op (ADDR_EXPR, yyvsp[0].ttype); ;
+ break;}
+case 157:
+#line 993 "parse.y"
+{ yyval.ttype = build_x_unary_op (BIT_NOT_EXPR, yyvsp[0].ttype); ;
+ break;}
+case 158:
+#line 995 "parse.y"
+{ yyval.ttype = build_x_unary_op (yyvsp[-1].code, yyvsp[0].ttype);
+ if (yyvsp[-1].code == NEGATE_EXPR && TREE_CODE (yyvsp[0].ttype) == INTEGER_CST)
+ TREE_NEGATED_INT (yyval.ttype) = 1;
+ overflow_warning (yyval.ttype);
+ ;
+ break;}
+case 159:
+#line 1002 "parse.y"
+{ tree label = lookup_label (yyvsp[0].ttype);
+ if (label == NULL_TREE)
+ yyval.ttype = null_pointer_node;
+ else
+ {
+ TREE_USED (label) = 1;
+ yyval.ttype = build1 (ADDR_EXPR, ptr_type_node, label);
+ TREE_CONSTANT (yyval.ttype) = 1;
+ }
+ ;
+ break;}
+case 160:
+#line 1013 "parse.y"
+{ if (TREE_CODE (yyvsp[0].ttype) == COMPONENT_REF
+ && DECL_BIT_FIELD (TREE_OPERAND (yyvsp[0].ttype, 1)))
+ error ("sizeof applied to a bit-field");
+ /* ANSI says arrays and functions are converted inside comma.
+ But we can't really convert them in build_compound_expr
+ because that would break commas in lvalues.
+ So do the conversion here if operand was a comma. */
+ if (TREE_CODE (yyvsp[0].ttype) == COMPOUND_EXPR
+ && (TREE_CODE (TREE_TYPE (yyvsp[0].ttype)) == ARRAY_TYPE
+ || TREE_CODE (TREE_TYPE (yyvsp[0].ttype)) == FUNCTION_TYPE))
+ yyvsp[0].ttype = default_conversion (yyvsp[0].ttype);
+ else if (TREE_CODE (yyvsp[0].ttype) == TREE_LIST)
+ {
+ tree t = TREE_VALUE (yyvsp[0].ttype);
+ if (t != NULL_TREE
+ && TREE_CODE (TREE_TYPE (t)) == FUNCTION_TYPE)
+ pedwarn ("ANSI C++ forbids using sizeof() on a function");
+ }
+ yyval.ttype = c_sizeof (TREE_TYPE (yyvsp[0].ttype)); ;
+ break;}
+case 161:
+#line 1033 "parse.y"
+{ yyval.ttype = c_sizeof (groktypename (yyvsp[-1].ttype)); ;
+ break;}
+case 162:
+#line 1035 "parse.y"
+{ yyval.ttype = grok_alignof (yyvsp[0].ttype); ;
+ break;}
+case 163:
+#line 1037 "parse.y"
+{ yyval.ttype = c_alignof (groktypename (yyvsp[-1].ttype)); ;
+ break;}
+case 164:
+#line 1042 "parse.y"
+{ yyval.ttype = build_new (NULL_TREE, yyvsp[0].ttype, NULL_TREE, yyvsp[-1].itype); ;
+ break;}
+case 165:
+#line 1044 "parse.y"
+{ yyval.ttype = build_new (NULL_TREE, yyvsp[-1].ttype, yyvsp[0].ttype, yyvsp[-2].itype); ;
+ break;}
+case 166:
+#line 1046 "parse.y"
+{ yyval.ttype = build_new (yyvsp[-1].ttype, yyvsp[0].ttype, NULL_TREE, yyvsp[-2].itype); ;
+ break;}
+case 167:
+#line 1048 "parse.y"
+{ yyval.ttype = build_new (yyvsp[-2].ttype, yyvsp[-1].ttype, yyvsp[0].ttype, yyvsp[-3].itype); ;
+ break;}
+case 168:
+#line 1050 "parse.y"
+{ yyval.ttype = build_new (NULL_TREE, groktypename(yyvsp[-1].ttype),
+ NULL_TREE, yyvsp[-3].itype); ;
+ break;}
+case 169:
+#line 1053 "parse.y"
+{ yyval.ttype = build_new (NULL_TREE, groktypename(yyvsp[-2].ttype), yyvsp[0].ttype, yyvsp[-4].itype); ;
+ break;}
+case 170:
+#line 1055 "parse.y"
+{ yyval.ttype = build_new (yyvsp[-3].ttype, groktypename(yyvsp[-1].ttype), NULL_TREE, yyvsp[-4].itype); ;
+ break;}
+case 171:
+#line 1057 "parse.y"
+{ yyval.ttype = build_new (yyvsp[-4].ttype, groktypename(yyvsp[-2].ttype), yyvsp[0].ttype, yyvsp[-5].itype); ;
+ break;}
+case 172:
+#line 1060 "parse.y"
+{ yyval.ttype = delete_sanity (yyvsp[0].ttype, NULL_TREE, 0, yyvsp[-1].itype); ;
+ break;}
+case 173:
+#line 1062 "parse.y"
+{ yyval.ttype = delete_sanity (yyvsp[0].ttype, NULL_TREE, 1, yyvsp[-3].itype);
+ if (yychar == YYEMPTY)
+ yychar = YYLEX; ;
+ break;}
+case 174:
+#line 1066 "parse.y"
+{ yyval.ttype = delete_sanity (yyvsp[0].ttype, yyvsp[-2].ttype, 2, yyvsp[-4].itype);
+ if (yychar == YYEMPTY)
+ yychar = YYLEX; ;
+ break;}
+case 175:
+#line 1073 "parse.y"
+{ yyval.ttype = yyvsp[-1].ttype; ;
+ break;}
+case 176:
+#line 1075 "parse.y"
+{
+ yyval.ttype = yyvsp[-1].ttype;
+ pedwarn ("old style placement syntax, use () instead");
+ ;
+ break;}
+case 177:
+#line 1083 "parse.y"
+{ yyval.ttype = yyvsp[-1].ttype; ;
+ break;}
+case 178:
+#line 1085 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 179:
+#line 1087 "parse.y"
+{
+ cp_error ("`%T' is not a valid expression", yyvsp[-1].ttype);
+ yyval.ttype = error_mark_node;
+ ;
+ break;}
+case 180:
+#line 1095 "parse.y"
+{
+ if (flag_ansi)
+ pedwarn ("ANSI C++ forbids initialization of new expression with `='");
+ yyval.ttype = yyvsp[0].ttype;
+ ;
+ break;}
+case 181:
+#line 1105 "parse.y"
+{ yyvsp[-1].ttype = tree_cons (NULL_TREE, yyvsp[-1].ttype, void_list_node);
+ TREE_PARMLIST (yyvsp[-1].ttype) = 1;
+ yyval.ttype = build_parse_node (CALL_EXPR, NULL_TREE, yyvsp[-1].ttype,
+ NULL_TREE); ;
+ break;}
+case 182:
+#line 1110 "parse.y"
+{ yyvsp[-1].ttype = tree_cons (NULL_TREE, yyvsp[-1].ttype, void_list_node);
+ TREE_PARMLIST (yyvsp[-1].ttype) = 1;
+ yyval.ttype = build_parse_node (CALL_EXPR, yyval.ttype, yyvsp[-1].ttype, NULL_TREE); ;
+ break;}
+case 184:
+#line 1118 "parse.y"
+{ yyval.ttype = reparse_absdcl_as_casts (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 185:
+#line 1120 "parse.y"
+{
+ tree init = build_nt (CONSTRUCTOR, NULL_TREE,
+ nreverse (yyvsp[-2].ttype));
+ if (flag_ansi)
+ pedwarn ("ANSI C++ forbids constructor-expressions");
+ /* Indicate that this was a GNU C constructor expression. */
+ TREE_HAS_CONSTRUCTOR (init) = 1;
+
+ yyval.ttype = reparse_absdcl_as_casts (yyval.ttype, init);
+ ;
+ break;}
+case 187:
+#line 1135 "parse.y"
+{ yyval.ttype = build_headof (yyvsp[-1].ttype); ;
+ break;}
+case 188:
+#line 1137 "parse.y"
+{ yyval.ttype = build_classof (yyvsp[-1].ttype); ;
+ break;}
+case 189:
+#line 1139 "parse.y"
+{ if (is_aggr_typedef (yyvsp[-1].ttype, 1))
+ {
+ tree type = IDENTIFIER_TYPE_VALUE (yyvsp[-1].ttype);
+ if (! IS_SIGNATURE(type))
+ yyval.ttype = CLASSTYPE_DOSSIER (type);
+ else
+ {
+ sorry ("signature name as argument of `classof'");
+ yyval.ttype = error_mark_node;
+ }
+ }
+ else
+ yyval.ttype = error_mark_node;
+ ;
+ break;}
+case 191:
+#line 1159 "parse.y"
+{ yyval.ttype = build_x_binary_op (MEMBER_REF, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 192:
+#line 1161 "parse.y"
+{ yyval.ttype = build_m_component_ref (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 193:
+#line 1163 "parse.y"
+{ yyval.ttype = build_x_binary_op (yyvsp[-1].code, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 194:
+#line 1165 "parse.y"
+{ yyval.ttype = build_x_binary_op (yyvsp[-1].code, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 195:
+#line 1167 "parse.y"
+{ yyval.ttype = build_x_binary_op (yyvsp[-1].code, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 196:
+#line 1169 "parse.y"
+{ yyval.ttype = build_x_binary_op (yyvsp[-1].code, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 197:
+#line 1171 "parse.y"
+{ yyval.ttype = build_x_binary_op (yyvsp[-1].code, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 198:
+#line 1173 "parse.y"
+{ yyval.ttype = build_x_binary_op (yyvsp[-1].code, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 199:
+#line 1175 "parse.y"
+{ yyval.ttype = build_x_binary_op (yyvsp[-1].code, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 200:
+#line 1177 "parse.y"
+{ yyval.ttype = build_x_binary_op (yyvsp[-1].code, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 201:
+#line 1179 "parse.y"
+{ yyval.ttype = build_x_binary_op (LT_EXPR, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 202:
+#line 1181 "parse.y"
+{ yyval.ttype = build_x_binary_op (GT_EXPR, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 203:
+#line 1183 "parse.y"
+{ yyval.ttype = build_x_binary_op (yyvsp[-1].code, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 204:
+#line 1185 "parse.y"
+{ yyval.ttype = build_x_binary_op (yyvsp[-1].code, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 205:
+#line 1187 "parse.y"
+{ yyval.ttype = build_x_binary_op (yyvsp[-1].code, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 206:
+#line 1189 "parse.y"
+{ yyval.ttype = build_x_binary_op (yyvsp[-1].code, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 207:
+#line 1191 "parse.y"
+{ yyval.ttype = build_x_binary_op (yyvsp[-1].code, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 208:
+#line 1193 "parse.y"
+{ yyval.ttype = build_x_binary_op (TRUTH_ANDIF_EXPR, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 209:
+#line 1195 "parse.y"
+{ yyval.ttype = build_x_binary_op (TRUTH_ORIF_EXPR, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 210:
+#line 1197 "parse.y"
+{ yyval.ttype = build_x_conditional_expr (yyval.ttype, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 211:
+#line 1199 "parse.y"
+{ yyval.ttype = build_modify_expr (yyval.ttype, NOP_EXPR, yyvsp[0].ttype); ;
+ break;}
+case 212:
+#line 1201 "parse.y"
+{ register tree rval;
+ if ((rval = build_opfncall (MODIFY_EXPR, LOOKUP_NORMAL, yyval.ttype, yyvsp[0].ttype,
+ make_node (yyvsp[-1].code))))
+ yyval.ttype = rval;
+ else
+ yyval.ttype = build_modify_expr (yyval.ttype, yyvsp[-1].code, yyvsp[0].ttype); ;
+ break;}
+case 213:
+#line 1208 "parse.y"
+{ yyval.ttype = build_throw (NULL_TREE); ;
+ break;}
+case 214:
+#line 1210 "parse.y"
+{ yyval.ttype = build_throw (yyvsp[0].ttype); ;
+ break;}
+case 215:
+#line 1228 "parse.y"
+{ yyval.ttype = build_parse_node (BIT_NOT_EXPR, yyvsp[0].ttype); ;
+ break;}
+case 223:
+#line 1243 "parse.y"
+{ yyval.ttype = build_parse_node (INDIRECT_REF, yyvsp[0].ttype); ;
+ break;}
+case 224:
+#line 1245 "parse.y"
+{ yyval.ttype = build_parse_node (ADDR_EXPR, yyvsp[0].ttype); ;
+ break;}
+case 227:
+#line 1252 "parse.y"
+{ push_nested_class (TREE_TYPE (OP0 (yyval.ttype)), 3);
+ TREE_COMPLEXITY (yyval.ttype) = current_class_depth; ;
+ break;}
+case 228:
+#line 1258 "parse.y"
+{
+ if (TREE_CODE (yyval.ttype) == BIT_NOT_EXPR)
+ yyval.ttype = build_x_unary_op (BIT_NOT_EXPR, TREE_OPERAND (yyval.ttype, 0));
+ else if (IDENTIFIER_OPNAME_P (yyval.ttype))
+ {
+ tree op = yyval.ttype;
+ yyval.ttype = lookup_name (op, 0);
+ if (yyval.ttype == NULL_TREE)
+ {
+ if (op != ansi_opname[ERROR_MARK])
+ error ("operator %s not defined",
+ operator_name_string (op));
+ yyval.ttype = error_mark_node;
+ }
+ }
+ else
+ yyval.ttype = do_identifier (yyval.ttype);
+ ;
+ break;}
+case 231:
+#line 1279 "parse.y"
+{ yyval.ttype = combine_strings (yyval.ttype); ;
+ break;}
+case 232:
+#line 1281 "parse.y"
+{ yyval.ttype = yyvsp[-1].ttype; ;
+ break;}
+case 233:
+#line 1283 "parse.y"
+{ yyval.ttype = error_mark_node; ;
+ break;}
+case 234:
+#line 1285 "parse.y"
+{ if (current_function_decl == 0)
+ {
+ error ("braced-group within expression allowed only inside a function");
+ YYERROR;
+ }
+ keep_next_level ();
+ yyval.ttype = expand_start_stmt_expr (); ;
+ break;}
+case 235:
+#line 1293 "parse.y"
+{ tree rtl_exp;
+ if (flag_ansi)
+ pedwarn ("ANSI C++ forbids braced-groups within expressions");
+ rtl_exp = expand_end_stmt_expr (yyvsp[-2].ttype);
+ /* The statements have side effects, so the group does. */
+ TREE_SIDE_EFFECTS (rtl_exp) = 1;
+
+ if (TREE_CODE (yyvsp[-1].ttype) == BLOCK)
+ {
+ /* Make a BIND_EXPR for the BLOCK already made. */
+ yyval.ttype = build (BIND_EXPR, TREE_TYPE (rtl_exp),
+ NULL_TREE, rtl_exp, yyvsp[-1].ttype);
+ /* Remove the block from the tree at this point.
+ It gets put back at the proper place
+ when the BIND_EXPR is expanded. */
+ delete_block (yyvsp[-1].ttype);
+ }
+ else
+ yyval.ttype = yyvsp[-1].ttype;
+ ;
+ break;}
+case 236:
+#line 1314 "parse.y"
+{ /* [eichin:19911016.1902EST] */
+ yyval.ttype = build_x_function_call (yyvsp[-3].ttype, yyvsp[-1].ttype, current_class_decl);
+ /* here we instantiate_class_template as needed... */
+ do_pending_templates ();
+ ;
+ break;}
+case 237:
+#line 1318 "parse.y"
+{
+ if (TREE_CODE (yyvsp[-1].ttype) == CALL_EXPR
+ && TREE_TYPE (yyvsp[-1].ttype) != void_type_node)
+ yyval.ttype = require_complete_type (yyvsp[-1].ttype);
+ else
+ yyval.ttype = yyvsp[-1].ttype;
+ ;
+ break;}
+case 238:
+#line 1326 "parse.y"
+{
+ yyval.ttype = build_x_function_call (yyval.ttype, NULL_TREE, current_class_decl);
+ if (TREE_CODE (yyval.ttype) == CALL_EXPR
+ && TREE_TYPE (yyval.ttype) != void_type_node)
+ yyval.ttype = require_complete_type (yyval.ttype);
+ ;
+ break;}
+case 239:
+#line 1333 "parse.y"
+{ yyval.ttype = grok_array_decl (yyval.ttype, yyvsp[-1].ttype); ;
+ break;}
+case 240:
+#line 1335 "parse.y"
+{ /* If we get an OFFSET_REF, turn it into what it really
+ means (e.g., a COMPONENT_REF). This way if we've got,
+ say, a reference to a static member that's being operated
+ on, we don't end up trying to find a member operator for
+ the class it's in. */
+ if (TREE_CODE (yyval.ttype) == OFFSET_REF)
+ yyval.ttype = resolve_offset_ref (yyval.ttype);
+ yyval.ttype = build_x_unary_op (POSTINCREMENT_EXPR, yyval.ttype); ;
+ break;}
+case 241:
+#line 1344 "parse.y"
+{ if (TREE_CODE (yyval.ttype) == OFFSET_REF)
+ yyval.ttype = resolve_offset_ref (yyval.ttype);
+ yyval.ttype = build_x_unary_op (POSTDECREMENT_EXPR, yyval.ttype); ;
+ break;}
+case 242:
+#line 1349 "parse.y"
+{ if (current_class_decl)
+ {
+#ifdef WARNING_ABOUT_CCD
+ TREE_USED (current_class_decl) = 1;
+#endif
+ yyval.ttype = current_class_decl;
+ }
+ else if (current_function_decl
+ && DECL_STATIC_FUNCTION_P (current_function_decl))
+ {
+ error ("`this' is unavailable for static member functions");
+ yyval.ttype = error_mark_node;
+ }
+ else
+ {
+ if (current_function_decl)
+ error ("invalid use of `this' in non-member function");
+ else
+ error ("invalid use of `this' at top level");
+ yyval.ttype = error_mark_node;
+ }
+ ;
+ break;}
+case 243:
+#line 1372 "parse.y"
+{
+ tree type;
+ tree id = yyval.ttype;
+
+ /* This is a C cast in C++'s `functional' notation. */
+ if (yyvsp[-1].ttype == error_mark_node)
+ {
+ yyval.ttype = error_mark_node;
+ break;
+ }
+#if 0
+ if (yyvsp[-1].ttype == NULL_TREE)
+ {
+ error ("cannot cast null list to type `%s'",
+ IDENTIFIER_POINTER (TYPE_NAME (id)));
+ yyval.ttype = error_mark_node;
+ break;
+ }
+#endif
+#if 0
+ /* type is not set! (mrs) */
+ if (type == error_mark_node)
+ yyval.ttype = error_mark_node;
+ else
+#endif
+ {
+ if (id == ridpointers[(int) RID_CONST])
+ type = build_type_variant (integer_type_node, 1, 0);
+ else if (id == ridpointers[(int) RID_VOLATILE])
+ type = build_type_variant (integer_type_node, 0, 1);
+#if 0
+ /* should not be able to get here (mrs) */
+ else if (id == ridpointers[(int) RID_FRIEND])
+ {
+ error ("cannot cast expression to `friend' type");
+ yyval.ttype = error_mark_node;
+ break;
+ }
+#endif
+ else my_friendly_abort (79);
+ yyval.ttype = build_c_cast (type, build_compound_expr (yyvsp[-1].ttype));
+ }
+ ;
+ break;}
+case 245:
+#line 1417 "parse.y"
+{ tree type = groktypename (yyvsp[-4].ttype);
+ yyval.ttype = build_dynamic_cast (type, yyvsp[-1].ttype); ;
+ break;}
+case 246:
+#line 1420 "parse.y"
+{ tree type = groktypename (yyvsp[-4].ttype);
+ yyval.ttype = build_static_cast (type, yyvsp[-1].ttype); ;
+ break;}
+case 247:
+#line 1423 "parse.y"
+{ tree type = groktypename (yyvsp[-4].ttype);
+ yyval.ttype = build_reinterpret_cast (type, yyvsp[-1].ttype); ;
+ break;}
+case 248:
+#line 1426 "parse.y"
+{ tree type = groktypename (yyvsp[-4].ttype);
+ yyval.ttype = build_const_cast (type, yyvsp[-1].ttype); ;
+ break;}
+case 249:
+#line 1429 "parse.y"
+{ yyval.ttype = build_typeid (yyvsp[-1].ttype); ;
+ break;}
+case 250:
+#line 1431 "parse.y"
+{ tree type = groktypename (yyvsp[-1].ttype);
+ yyval.ttype = get_typeid (type); ;
+ break;}
+case 251:
+#line 1434 "parse.y"
+{
+ do_scoped_id:
+ yyval.ttype = IDENTIFIER_GLOBAL_VALUE (yyvsp[0].ttype);
+ if (yychar == YYEMPTY)
+ yychar = YYLEX;
+ if (! yyval.ttype)
+ {
+ if (yychar == '(' || yychar == LEFT_RIGHT)
+ yyval.ttype = implicitly_declare (yyvsp[0].ttype);
+ else
+ {
+ if (IDENTIFIER_GLOBAL_VALUE (yyvsp[0].ttype) != error_mark_node)
+ error ("undeclared variable `%s' (first use here)",
+ IDENTIFIER_POINTER (yyvsp[0].ttype));
+ yyval.ttype = error_mark_node;
+ /* Prevent repeated error messages. */
+ IDENTIFIER_GLOBAL_VALUE (yyvsp[0].ttype) = error_mark_node;
+ }
+ }
+ else
+ {
+ if (TREE_CODE (yyval.ttype) == ADDR_EXPR)
+ assemble_external (TREE_OPERAND (yyval.ttype, 0));
+ else
+ assemble_external (yyval.ttype);
+ TREE_USED (yyval.ttype) = 1;
+ }
+ if (TREE_CODE (yyval.ttype) == CONST_DECL)
+ {
+ /* XXX CHS - should we set TREE_USED of the constant? */
+ yyval.ttype = DECL_INITIAL (yyval.ttype);
+ /* This is to prevent an enum whose value is 0
+ from being considered a null pointer constant. */
+ yyval.ttype = build1 (NOP_EXPR, TREE_TYPE (yyval.ttype), yyval.ttype);
+ TREE_CONSTANT (yyval.ttype) = 1;
+ }
+
+ ;
+ break;}
+case 252:
+#line 1473 "parse.y"
+{
+ got_scope = NULL_TREE;
+ if (TREE_CODE (yyvsp[0].ttype) == IDENTIFIER_NODE)
+ goto do_scoped_id;
+ yyval.ttype = yyvsp[0].ttype;
+ ;
+ break;}
+case 253:
+#line 1480 "parse.y"
+{ yyval.ttype = build_offset_ref (OP0 (yyval.ttype), OP1 (yyval.ttype)); ;
+ break;}
+case 254:
+#line 1482 "parse.y"
+{ yyval.ttype = build_member_call (OP0 (yyval.ttype), OP1 (yyval.ttype), yyvsp[-1].ttype); ;
+ break;}
+case 255:
+#line 1484 "parse.y"
+{ yyval.ttype = build_member_call (OP0 (yyval.ttype), OP1 (yyval.ttype), NULL_TREE); ;
+ break;}
+case 256:
+#line 1486 "parse.y"
+{ yyval.ttype = build_component_ref (yyval.ttype, yyvsp[0].ttype, NULL_TREE, 1); ;
+ break;}
+case 257:
+#line 1488 "parse.y"
+{ yyval.ttype = build_object_ref (yyval.ttype, OP0 (yyvsp[0].ttype), OP1 (yyvsp[0].ttype)); ;
+ break;}
+case 258:
+#line 1490 "parse.y"
+{
+#if 0
+ /* This is a future direction of this code, but because
+ build_x_function_call cannot always undo what is done
+ in build_component_ref entirely yet, we cannot do this. */
+ yyval.ttype = build_x_function_call (build_component_ref (yyval.ttype, yyvsp[-3].ttype, NULL_TREE, 1), yyvsp[-1].ttype, yyval.ttype);
+ if (TREE_CODE (yyval.ttype) == CALL_EXPR
+ && TREE_TYPE (yyval.ttype) != void_type_node)
+ yyval.ttype = require_complete_type (yyval.ttype);
+#else
+ yyval.ttype = build_method_call (yyval.ttype, yyvsp[-3].ttype, yyvsp[-1].ttype, NULL_TREE,
+ (LOOKUP_NORMAL|LOOKUP_AGGR));
+#endif
+ ;
+ break;}
+case 259:
+#line 1505 "parse.y"
+{
+#if 0
+ /* This is a future direction of this code, but because
+ build_x_function_call cannot always undo what is done
+ in build_component_ref entirely yet, we cannot do this. */
+ yyval.ttype = build_x_function_call (build_component_ref (yyval.ttype, yyvsp[-1].ttype, NULL_TREE, 1), NULL_TREE, yyval.ttype);
+ if (TREE_CODE (yyval.ttype) == CALL_EXPR
+ && TREE_TYPE (yyval.ttype) != void_type_node)
+ yyval.ttype = require_complete_type (yyval.ttype);
+#else
+ yyval.ttype = build_method_call (yyval.ttype, yyvsp[-1].ttype, NULL_TREE, NULL_TREE,
+ (LOOKUP_NORMAL|LOOKUP_AGGR));
+#endif
+ ;
+ break;}
+case 260:
+#line 1520 "parse.y"
+{
+ if (IS_SIGNATURE (IDENTIFIER_TYPE_VALUE (OP0 (yyvsp[-3].ttype))))
+ {
+ warning ("signature name in scope resolution ignored");
+ yyval.ttype = build_method_call (yyval.ttype, OP1 (yyvsp[-3].ttype), yyvsp[-1].ttype, NULL_TREE,
+ (LOOKUP_NORMAL|LOOKUP_AGGR));
+ }
+ else
+ yyval.ttype = build_scoped_method_call (yyval.ttype, OP0 (yyvsp[-3].ttype), OP1 (yyvsp[-3].ttype), yyvsp[-1].ttype);
+ ;
+ break;}
+case 261:
+#line 1531 "parse.y"
+{
+ if (IS_SIGNATURE (IDENTIFIER_TYPE_VALUE (OP0 (yyvsp[-1].ttype))))
+ {
+ warning ("signature name in scope resolution ignored");
+ yyval.ttype = build_method_call (yyval.ttype, OP1 (yyvsp[-1].ttype), NULL_TREE, NULL_TREE,
+ (LOOKUP_NORMAL|LOOKUP_AGGR));
+ }
+ else
+ yyval.ttype = build_scoped_method_call (yyval.ttype, OP0 (yyvsp[-1].ttype), OP1 (yyvsp[-1].ttype), NULL_TREE);
+ ;
+ break;}
+case 262:
+#line 1543 "parse.y"
+{
+ if (TREE_CODE (TREE_TYPE (yyvsp[-3].ttype))
+ != TREE_CODE (TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (yyvsp[-1].ttype))))
+ cp_error ("`%E' is not of type `%T'", yyvsp[-3].ttype, yyvsp[-1].ttype);
+ yyval.ttype = void_zero_node;
+ ;
+ break;}
+case 263:
+#line 1550 "parse.y"
+{
+ if (yyvsp[-4].ttype != yyvsp[-1].ttype)
+ cp_error ("destructor specifier `%T::~%T()' must have matching names", yyvsp[-4].ttype, yyvsp[-1].ttype);
+ if (TREE_CODE (TREE_TYPE (yyvsp[-5].ttype))
+ != TREE_CODE (TREE_TYPE (IDENTIFIER_GLOBAL_VALUE (yyvsp[-4].ttype))))
+ cp_error ("`%E' is not of type `%T'", yyvsp[-5].ttype, yyvsp[-4].ttype);
+ yyval.ttype = void_zero_node;
+ ;
+ break;}
+case 264:
+#line 1599 "parse.y"
+{ yyval.itype = 0; ;
+ break;}
+case 265:
+#line 1601 "parse.y"
+{ got_scope = NULL_TREE; yyval.itype = 1; ;
+ break;}
+case 266:
+#line 1605 "parse.y"
+{ yyval.itype = 0; ;
+ break;}
+case 267:
+#line 1607 "parse.y"
+{ got_scope = NULL_TREE; yyval.itype = 1; ;
+ break;}
+case 268:
+#line 1612 "parse.y"
+{ yyval.ttype = true_node; ;
+ break;}
+case 269:
+#line 1614 "parse.y"
+{ yyval.ttype = false_node; ;
+ break;}
+case 271:
+#line 1621 "parse.y"
+{ yyval.ttype = chainon (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 272:
+#line 1626 "parse.y"
+{
+ if (! current_function_parms_stored)
+ store_parm_decls ();
+ setup_vtbl_ptr ();
+ /* Always keep the BLOCK node associated with the outermost
+ pair of curley braces of a function. These are needed
+ for correct operation of dwarfout.c. */
+ keep_next_level ();
+ ;
+ break;}
+case 274:
+#line 1639 "parse.y"
+{
+ yyval.ttype = build_x_arrow (yyval.ttype);
+ ;
+ break;}
+case 275:
+#line 1647 "parse.y"
+{ tree d = get_decl_list (yyvsp[-2].ttype);
+ int yes = suspend_momentary ();
+ d = start_decl (yyvsp[-1].ttype, d, 0, NULL_TREE);
+ finish_decl (d, NULL_TREE, NULL_TREE, 0);
+ resume_momentary (yes);
+ if (IS_AGGR_TYPE_CODE (TREE_CODE (yyvsp[-2].ttype)))
+ note_got_semicolon (yyvsp[-2].ttype);
+ ;
+ break;}
+case 276:
+#line 1656 "parse.y"
+{ tree d = yyvsp[-2].ttype;
+ int yes = suspend_momentary ();
+ d = start_decl (yyvsp[-1].ttype, d, 0, NULL_TREE);
+ finish_decl (d, NULL_TREE, NULL_TREE, 0);
+ resume_momentary (yes);
+ note_list_got_semicolon (yyvsp[-2].ttype);
+ ;
+ break;}
+case 277:
+#line 1664 "parse.y"
+{
+ resume_momentary (yyvsp[-1].itype);
+ if (IS_AGGR_TYPE_CODE (TREE_CODE (yyvsp[-2].ttype)))
+ note_got_semicolon (yyvsp[-2].ttype);
+ ;
+ break;}
+case 278:
+#line 1670 "parse.y"
+{
+ resume_momentary (yyvsp[-1].itype);
+ note_list_got_semicolon (yyvsp[-2].ttype);
+ ;
+ break;}
+case 279:
+#line 1675 "parse.y"
+{ resume_momentary (yyvsp[-1].itype); ;
+ break;}
+case 280:
+#line 1677 "parse.y"
+{
+ shadow_tag (yyvsp[-1].ttype);
+ note_list_got_semicolon (yyvsp[-1].ttype);
+ ;
+ break;}
+case 281:
+#line 1682 "parse.y"
+{ warning ("empty declaration"); ;
+ break;}
+case 284:
+#line 1696 "parse.y"
+{ yyval.ttype = build_parse_node (CALL_EXPR, NULL_TREE, empty_parms (),
+ NULL_TREE); ;
+ break;}
+case 285:
+#line 1699 "parse.y"
+{ yyval.ttype = build_parse_node (CALL_EXPR, yyval.ttype, empty_parms (),
+ NULL_TREE); ;
+ break;}
+case 286:
+#line 1706 "parse.y"
+{ yyval.ttype = build_decl_list (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 287:
+#line 1708 "parse.y"
+{ yyval.ttype = build_decl_list (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 288:
+#line 1710 "parse.y"
+{ yyval.ttype = build_decl_list (get_decl_list (yyval.ttype), yyvsp[0].ttype); ;
+ break;}
+case 289:
+#line 1712 "parse.y"
+{ yyval.ttype = build_decl_list (yyval.ttype, NULL_TREE); ;
+ break;}
+case 290:
+#line 1714 "parse.y"
+{ yyval.ttype = build_decl_list (yyval.ttype, NULL_TREE); ;
+ break;}
+case 293:
+#line 1727 "parse.y"
+{ yyval.ttype = decl_tree_cons (NULL_TREE, yyvsp[0].ttype, yyval.ttype); ;
+ break;}
+case 294:
+#line 1729 "parse.y"
+{ yyval.ttype = decl_tree_cons (NULL_TREE, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 295:
+#line 1731 "parse.y"
+{ yyval.ttype = decl_tree_cons (NULL_TREE, yyvsp[-1].ttype, chainon (yyvsp[0].ttype, yyval.ttype)); ;
+ break;}
+case 296:
+#line 1733 "parse.y"
+{ yyval.ttype = decl_tree_cons (NULL_TREE, yyvsp[-1].ttype, chainon (yyvsp[0].ttype, yyval.ttype)); ;
+ break;}
+case 297:
+#line 1735 "parse.y"
+{ yyval.ttype = decl_tree_cons (NULL_TREE, yyvsp[-2].ttype,
+ chainon (yyvsp[-1].ttype, chainon (yyvsp[0].ttype, yyval.ttype))); ;
+ break;}
+case 298:
+#line 1741 "parse.y"
+{ if (extra_warnings)
+ warning ("`%s' is not at beginning of declaration",
+ IDENTIFIER_POINTER (yyval.ttype));
+ yyval.ttype = build_decl_list (NULL_TREE, yyval.ttype); ;
+ break;}
+case 299:
+#line 1746 "parse.y"
+{ yyval.ttype = decl_tree_cons (NULL_TREE, yyvsp[0].ttype, yyval.ttype); ;
+ break;}
+case 300:
+#line 1748 "parse.y"
+{ if (extra_warnings)
+ warning ("`%s' is not at beginning of declaration",
+ IDENTIFIER_POINTER (yyvsp[0].ttype));
+ yyval.ttype = decl_tree_cons (NULL_TREE, yyvsp[0].ttype, yyval.ttype); ;
+ break;}
+case 301:
+#line 1760 "parse.y"
+{ TREE_STATIC (yyval.ttype) = 1; ;
+ break;}
+case 302:
+#line 1762 "parse.y"
+{ yyval.ttype = IDENTIFIER_AS_LIST (yyval.ttype); ;
+ break;}
+case 303:
+#line 1764 "parse.y"
+{ yyval.ttype = decl_tree_cons (NULL_TREE, yyvsp[0].ttype, yyval.ttype);
+ TREE_STATIC (yyval.ttype) = 1; ;
+ break;}
+case 304:
+#line 1767 "parse.y"
+{ if (extra_warnings && TREE_STATIC (yyval.ttype))
+ warning ("`%s' is not at beginning of declaration",
+ IDENTIFIER_POINTER (yyvsp[0].ttype));
+ yyval.ttype = decl_tree_cons (NULL_TREE, yyvsp[0].ttype, yyval.ttype);
+ TREE_STATIC (yyval.ttype) = TREE_STATIC (yyvsp[-1].ttype); ;
+ break;}
+case 305:
+#line 1783 "parse.y"
+{ yyval.ttype = get_decl_list (yyval.ttype); ;
+ break;}
+case 306:
+#line 1785 "parse.y"
+{ yyval.ttype = decl_tree_cons (NULL_TREE, yyvsp[0].ttype, yyval.ttype); ;
+ break;}
+case 307:
+#line 1787 "parse.y"
+{ yyval.ttype = decl_tree_cons (NULL_TREE, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 308:
+#line 1789 "parse.y"
+{ yyval.ttype = decl_tree_cons (NULL_TREE, yyvsp[-1].ttype, chainon (yyvsp[0].ttype, yyval.ttype)); ;
+ break;}
+case 309:
+#line 1794 "parse.y"
+{ yyval.ttype = build_decl_list (NULL_TREE, yyval.ttype); ;
+ break;}
+case 310:
+#line 1796 "parse.y"
+{ yyval.ttype = decl_tree_cons (NULL_TREE, yyvsp[0].ttype, yyval.ttype); ;
+ break;}
+case 314:
+#line 1807 "parse.y"
+{ yyval.ttype = TREE_TYPE (yyvsp[-1].ttype);
+ if (flag_ansi)
+ pedwarn ("ANSI C++ forbids `typeof'"); ;
+ break;}
+case 315:
+#line 1811 "parse.y"
+{ yyval.ttype = groktypename (yyvsp[-1].ttype);
+ if (flag_ansi)
+ pedwarn ("ANSI C++ forbids `typeof'"); ;
+ break;}
+case 316:
+#line 1815 "parse.y"
+{ tree type = TREE_TYPE (yyvsp[-1].ttype);
+
+ if (IS_AGGR_TYPE (type))
+ {
+ sorry ("sigof type specifier");
+ yyval.ttype = type;
+ }
+ else
+ {
+ error ("`sigof' applied to non-aggregate expression");
+ yyval.ttype = error_mark_node;
+ }
+ ;
+ break;}
+case 317:
+#line 1829 "parse.y"
+{ tree type = groktypename (yyvsp[-1].ttype);
+
+ if (IS_AGGR_TYPE (type))
+ {
+ sorry ("sigof type specifier");
+ yyval.ttype = type;
+ }
+ else
+ {
+ error("`sigof' applied to non-aggregate type");
+ yyval.ttype = error_mark_node;
+ }
+ ;
+ break;}
+case 327:
+#line 1868 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 328:
+#line 1870 "parse.y"
+{ if (TREE_CHAIN (yyvsp[-1].ttype)) yyvsp[-1].ttype = combine_strings (yyvsp[-1].ttype); yyval.ttype = yyvsp[-1].ttype; ;
+ break;}
+case 329:
+#line 1875 "parse.y"
+{ current_declspecs = yyvsp[-5].ttype;
+ if (TREE_CODE (current_declspecs) != TREE_LIST)
+ current_declspecs = get_decl_list (current_declspecs);
+ if (have_extern_spec && !used_extern_spec)
+ {
+ current_declspecs = decl_tree_cons
+ (NULL_TREE, get_identifier ("extern"),
+ current_declspecs);
+ used_extern_spec = 1;
+ }
+ yyvsp[0].itype = suspend_momentary ();
+ yyval.ttype = start_decl (yyvsp[-4].ttype, current_declspecs, 1, yyvsp[-3].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[-1].ttype); ;
+ break;}
+case 330:
+#line 1890 "parse.y"
+{ finish_decl (yyvsp[-1].ttype, yyvsp[0].ttype, yyvsp[-4].ttype, 0);
+ yyval.itype = yyvsp[-2].itype; ;
+ break;}
+case 331:
+#line 1893 "parse.y"
+{ tree d;
+ current_declspecs = yyvsp[-4].ttype;
+ if (TREE_CODE (current_declspecs) != TREE_LIST)
+ current_declspecs = get_decl_list (current_declspecs);
+ if (have_extern_spec && !used_extern_spec)
+ {
+ current_declspecs = decl_tree_cons
+ (NULL_TREE, get_identifier ("extern"),
+ current_declspecs);
+ used_extern_spec = 1;
+ }
+ yyval.itype = suspend_momentary ();
+ d = start_decl (yyvsp[-3].ttype, current_declspecs, 0, yyvsp[-2].ttype);
+ cplus_decl_attributes (d, yyvsp[0].ttype);
+ finish_decl (d, NULL_TREE, yyvsp[-1].ttype, 0); ;
+ break;}
+case 332:
+#line 1912 "parse.y"
+{ yyval.ttype = start_decl (yyvsp[-4].ttype, current_declspecs, 1, yyvsp[-3].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[-1].ttype); ;
+ break;}
+case 333:
+#line 1916 "parse.y"
+{ finish_decl (yyvsp[-1].ttype, yyvsp[0].ttype, yyvsp[-4].ttype, 0); ;
+ break;}
+case 334:
+#line 1918 "parse.y"
+{ yyval.ttype = start_decl (yyvsp[-3].ttype, current_declspecs, 0, yyvsp[-2].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[0].ttype);
+ finish_decl (yyval.ttype, NULL_TREE, yyvsp[-1].ttype, 0); ;
+ break;}
+case 335:
+#line 1925 "parse.y"
+{ current_declspecs = yyvsp[-5].ttype;
+ yyvsp[0].itype = suspend_momentary ();
+ yyval.ttype = start_decl (yyvsp[-4].ttype, current_declspecs, 1, yyvsp[-3].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[-1].ttype); ;
+ break;}
+case 336:
+#line 1931 "parse.y"
+{ finish_decl (yyvsp[-1].ttype, yyvsp[0].ttype, yyvsp[-4].ttype, 0);
+ yyval.itype = yyvsp[-2].itype; ;
+ break;}
+case 337:
+#line 1934 "parse.y"
+{ tree d;
+ current_declspecs = yyvsp[-4].ttype;
+ yyval.itype = suspend_momentary ();
+ d = start_decl (yyvsp[-3].ttype, current_declspecs, 0, yyvsp[-2].ttype);
+ cplus_decl_attributes (d, yyvsp[0].ttype);
+ finish_decl (d, NULL_TREE, yyvsp[-1].ttype, 0); ;
+ break;}
+case 338:
+#line 1944 "parse.y"
+{ current_declspecs = NULL_TREE;
+ yyvsp[0].itype = suspend_momentary ();
+ yyval.ttype = start_decl (yyvsp[-4].ttype, current_declspecs, 1, yyvsp[-3].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[-1].ttype); ;
+ break;}
+case 339:
+#line 1950 "parse.y"
+{ finish_decl (yyvsp[-1].ttype, yyvsp[0].ttype, yyvsp[-4].ttype, 0);
+ yyval.itype = yyvsp[-2].itype; ;
+ break;}
+case 340:
+#line 1953 "parse.y"
+{ tree d;
+ current_declspecs = NULL_TREE;
+ yyval.itype = suspend_momentary ();
+ d = start_decl (yyvsp[-3].ttype, current_declspecs, 0, yyvsp[-2].ttype);
+ cplus_decl_attributes (d, yyvsp[0].ttype);
+ finish_decl (d, NULL_TREE, yyvsp[-1].ttype, 0); ;
+ break;}
+case 341:
+#line 1965 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 342:
+#line 1967 "parse.y"
+{ yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 343:
+#line 1972 "parse.y"
+{ yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 344:
+#line 1974 "parse.y"
+{ yyval.ttype = chainon (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 345:
+#line 1979 "parse.y"
+{ yyval.ttype = yyvsp[-2].ttype; ;
+ break;}
+case 346:
+#line 1984 "parse.y"
+{ yyval.ttype = build_tree_list (NULL_TREE, yyvsp[0].ttype); ;
+ break;}
+case 347:
+#line 1986 "parse.y"
+{ yyval.ttype = chainon (yyvsp[-2].ttype, build_tree_list (NULL_TREE, yyvsp[0].ttype)); ;
+ break;}
+case 348:
+#line 1991 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 349:
+#line 1993 "parse.y"
+{ yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 350:
+#line 1995 "parse.y"
+{ yyval.ttype = tree_cons (yyvsp[-3].ttype, NULL_TREE, build_tree_list (NULL_TREE, yyvsp[-1].ttype)); ;
+ break;}
+case 351:
+#line 1997 "parse.y"
+{ yyval.ttype = tree_cons (yyvsp[-5].ttype, NULL_TREE, tree_cons (NULL_TREE, yyvsp[-3].ttype, yyvsp[-1].ttype)); ;
+ break;}
+case 352:
+#line 1999 "parse.y"
+{ yyval.ttype = tree_cons (yyvsp[-3].ttype, NULL_TREE, yyvsp[-1].ttype); ;
+ break;}
+case 357:
+#line 2015 "parse.y"
+{ yyval.ttype = build_tree_list (NULL_TREE, yyvsp[0].ttype); ;
+ break;}
+case 358:
+#line 2017 "parse.y"
+{ yyval.ttype = chainon (yyvsp[-2].ttype, build_tree_list (NULL_TREE, yyvsp[0].ttype)); ;
+ break;}
+case 360:
+#line 2023 "parse.y"
+{ yyval.ttype = build_nt (CONSTRUCTOR, NULL_TREE, NULL_TREE);
+ TREE_HAS_CONSTRUCTOR (yyval.ttype) = 1; ;
+ break;}
+case 361:
+#line 2026 "parse.y"
+{ yyval.ttype = build_nt (CONSTRUCTOR, NULL_TREE, nreverse (yyvsp[-1].ttype));
+ TREE_HAS_CONSTRUCTOR (yyval.ttype) = 1; ;
+ break;}
+case 362:
+#line 2029 "parse.y"
+{ yyval.ttype = build_nt (CONSTRUCTOR, NULL_TREE, nreverse (yyvsp[-2].ttype));
+ TREE_HAS_CONSTRUCTOR (yyval.ttype) = 1; ;
+ break;}
+case 363:
+#line 2032 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 364:
+#line 2039 "parse.y"
+{ yyval.ttype = build_tree_list (NULL_TREE, yyval.ttype); ;
+ break;}
+case 365:
+#line 2041 "parse.y"
+{ yyval.ttype = tree_cons (NULL_TREE, yyvsp[0].ttype, yyval.ttype); ;
+ break;}
+case 366:
+#line 2044 "parse.y"
+{ yyval.ttype = build_tree_list (yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 367:
+#line 2046 "parse.y"
+{ yyval.ttype = tree_cons (yyvsp[-2].ttype, yyvsp[0].ttype, yyval.ttype); ;
+ break;}
+case 368:
+#line 2048 "parse.y"
+{ yyval.ttype = build_tree_list (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 369:
+#line 2050 "parse.y"
+{ yyval.ttype = tree_cons (yyvsp[-2].ttype, yyvsp[0].ttype, yyval.ttype); ;
+ break;}
+case 370:
+#line 2055 "parse.y"
+{ yyvsp[0].itype = suspend_momentary ();
+ yyval.ttype = start_enum (yyvsp[-1].ttype); ;
+ break;}
+case 371:
+#line 2058 "parse.y"
+{ yyval.ttype = finish_enum (yyvsp[-3].ttype, yyvsp[-2].ttype);
+ resume_momentary ((int) yyvsp[-4].itype);
+ check_for_missing_semicolon (yyvsp[-3].ttype); ;
+ break;}
+case 372:
+#line 2062 "parse.y"
+{ yyval.ttype = finish_enum (start_enum (yyvsp[-2].ttype), NULL_TREE);
+ check_for_missing_semicolon (yyval.ttype); ;
+ break;}
+case 373:
+#line 2065 "parse.y"
+{ yyvsp[0].itype = suspend_momentary ();
+ yyval.ttype = start_enum (make_anon_name ()); ;
+ break;}
+case 374:
+#line 2068 "parse.y"
+{ yyval.ttype = finish_enum (yyvsp[-3].ttype, yyvsp[-2].ttype);
+ resume_momentary ((int) yyvsp[-5].itype);
+ check_for_missing_semicolon (yyvsp[-3].ttype); ;
+ break;}
+case 375:
+#line 2072 "parse.y"
+{ yyval.ttype = finish_enum (start_enum (make_anon_name()), NULL_TREE);
+ check_for_missing_semicolon (yyval.ttype); ;
+ break;}
+case 376:
+#line 2075 "parse.y"
+{ yyval.ttype = xref_tag (enum_type_node, yyvsp[0].ttype, NULL_TREE, 0); ;
+ break;}
+case 377:
+#line 2077 "parse.y"
+{ yyval.ttype = xref_tag (enum_type_node, yyvsp[0].ttype, NULL_TREE, 0); ;
+ break;}
+case 378:
+#line 2081 "parse.y"
+{
+ int semi;
+ tree id;
+
+#if 0
+ /* Need to rework class nesting in the
+ presence of nested classes, etc. */
+ shadow_tag (CLASSTYPE_AS_LIST (yyval.ttype)); */
+#endif
+ if (yychar == YYEMPTY)
+ yychar = YYLEX;
+ semi = yychar == ';';
+ /* finish_struct nukes this anyway; if
+ finish_exception does too, then it can go. */
+ if (semi)
+ note_got_semicolon (yyval.ttype);
+
+ if (TREE_CODE (yyval.ttype) == ENUMERAL_TYPE)
+ /* $$ = $1 from default rule. */;
+ else if (CLASSTYPE_DECLARED_EXCEPTION (yyval.ttype))
+ {
+ }
+ else
+ {
+ yyval.ttype = finish_struct (yyval.ttype, yyvsp[-1].ttype, semi);
+ if (semi) note_got_semicolon (yyval.ttype);
+ }
+
+ pop_obstacks ();
+
+ id = TYPE_IDENTIFIER (yyval.ttype);
+ if (id && IDENTIFIER_TEMPLATE (id))
+ {
+ tree decl;
+
+ /* I don't know if the copying of this TYPE_DECL is
+ * really needed. However, it's such a small per-
+ * formance penalty that the extra safety is a bargain.
+ * - niklas@appli.se
+ */
+ push_obstacks (&permanent_obstack, &permanent_obstack);
+ decl = copy_node (lookup_name (id, 0));
+ if (DECL_LANG_SPECIFIC (decl))
+ copy_lang_decl (decl);
+ pop_obstacks ();
+ undo_template_name_overload (id, 0);
+ pushdecl_top_level (decl);
+ }
+ if (! semi)
+ check_for_missing_semicolon (yyval.ttype); ;
+ break;}
+case 379:
+#line 2132 "parse.y"
+{
+#if 0
+ /* It's no longer clear what the following error is supposed to
+ accomplish. If it turns out to be needed, add a comment why. */
+ if (TYPE_BINFO_BASETYPES (yyval.ttype) && !TYPE_SIZE (yyval.ttype))
+ {
+ error ("incomplete definition of type `%s'",
+ TYPE_NAME_STRING (yyval.ttype));
+ yyval.ttype = error_mark_node;
+ }
+#endif
+ ;
+ break;}
+case 383:
+#line 2154 "parse.y"
+{ if (pedantic) pedwarn ("comma at end of enumerator list"); ;
+ break;}
+case 385:
+#line 2159 "parse.y"
+{ error ("storage class specifier `%s' not allowed after struct or class", IDENTIFIER_POINTER (yyvsp[0].ttype)); ;
+ break;}
+case 386:
+#line 2161 "parse.y"
+{ error ("type specifier `%s' not allowed after struct or class", IDENTIFIER_POINTER (yyvsp[0].ttype)); ;
+ break;}
+case 387:
+#line 2163 "parse.y"
+{ error ("type qualifier `%s' not allowed after struct or class", IDENTIFIER_POINTER (yyvsp[0].ttype)); ;
+ break;}
+case 388:
+#line 2165 "parse.y"
+{ error ("no body nor ';' separates two class, struct or union declarations"); ;
+ break;}
+case 389:
+#line 2170 "parse.y"
+{
+ yyungetc (';', 1); current_aggr = yyval.ttype; yyval.ttype = yyvsp[-1].ttype;
+ if (yyvsp[-3].ttype == ridpointers[(int) RID_TEMPLATE])
+ instantiate_class_template (yyval.ttype, 2);
+ ;
+ break;}
+case 390:
+#line 2179 "parse.y"
+{ current_aggr = yyval.ttype; yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 391:
+#line 2181 "parse.y"
+{ current_aggr = yyval.ttype; yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 392:
+#line 2183 "parse.y"
+{ current_aggr = yyval.ttype; yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 393:
+#line 2185 "parse.y"
+{ yyungetc ('{', 1);
+ aggr2:
+ current_aggr = yyval.ttype;
+ yyval.ttype = yyvsp[-1].ttype;
+ overload_template_name (yyval.ttype, 0); ;
+ break;}
+case 394:
+#line 2191 "parse.y"
+{ yyungetc (':', 1); goto aggr2; ;
+ break;}
+case 396:
+#line 2197 "parse.y"
+{ current_aggr = yyval.ttype; yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 397:
+#line 2201 "parse.y"
+{ yyval.ttype = xref_tag (current_aggr, yyvsp[0].ttype, NULL_TREE, 1); ;
+ break;}
+case 398:
+#line 2204 "parse.y"
+{ yyval.ttype = xref_defn_tag (current_aggr, yyvsp[0].ttype, NULL_TREE); ;
+ break;}
+case 399:
+#line 2209 "parse.y"
+{
+ if (yyvsp[0].ttype)
+ yyval.ttype = xref_tag (current_aggr, yyvsp[-2].ttype, yyvsp[0].ttype, 1);
+ else
+ yyval.ttype = yyvsp[-1].ttype;
+ ;
+ break;}
+case 400:
+#line 2218 "parse.y"
+{
+ if (yyvsp[0].ttype)
+ yyval.ttype = xref_defn_tag (current_aggr, yyvsp[-2].ttype, yyvsp[0].ttype);
+ else
+ yyval.ttype = yyvsp[-1].ttype;
+ ;
+ break;}
+case 401:
+#line 2227 "parse.y"
+{ yyval.ttype = xref_tag (yyval.ttype, make_anon_name (), NULL_TREE, 0);
+ yyungetc ('{', 1); ;
+ break;}
+case 404:
+#line 2235 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 405:
+#line 2237 "parse.y"
+{ yyungetc(':', 1); yyval.ttype = NULL_TREE; ;
+ break;}
+case 406:
+#line 2239 "parse.y"
+{ yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 408:
+#line 2245 "parse.y"
+{ yyval.ttype = chainon (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 409:
+#line 2250 "parse.y"
+{
+ tree type;
+ do_base_class1:
+ type = IDENTIFIER_TYPE_VALUE (yyval.ttype);
+ if (! is_aggr_typedef (yyval.ttype, 1))
+ yyval.ttype = NULL_TREE;
+ else if (current_aggr == signature_type_node
+ && (! type) && (! IS_SIGNATURE (type)))
+ {
+ error ("class name not allowed as base signature");
+ yyval.ttype = NULL_TREE;
+ }
+ else if (current_aggr == signature_type_node)
+ {
+ sorry ("signature inheritance, base type `%s' ignored",
+ IDENTIFIER_POINTER (yyval.ttype));
+ yyval.ttype = build_tree_list ((tree)access_public, yyval.ttype);
+ }
+ else if (type && IS_SIGNATURE (type))
+ {
+ error ("signature name not allowed as base class");
+ yyval.ttype = NULL_TREE;
+ }
+ else
+ yyval.ttype = build_tree_list ((tree)access_default, yyval.ttype);
+ ;
+ break;}
+case 410:
+#line 2277 "parse.y"
+{
+ tree type;
+ do_base_class2:
+ type = IDENTIFIER_TYPE_VALUE (yyvsp[0].ttype);
+ if (current_aggr == signature_type_node)
+ error ("access and source specifiers not allowed in signature");
+ if (! is_aggr_typedef (yyvsp[0].ttype, 1))
+ yyval.ttype = NULL_TREE;
+ else if (current_aggr == signature_type_node
+ && (! type) && (! IS_SIGNATURE (type)))
+ {
+ error ("class name not allowed as base signature");
+ yyval.ttype = NULL_TREE;
+ }
+ else if (current_aggr == signature_type_node)
+ {
+ sorry ("signature inheritance, base type `%s' ignored",
+ IDENTIFIER_POINTER (yyval.ttype));
+ yyval.ttype = build_tree_list ((tree)access_public, yyvsp[0].ttype);
+ }
+ else if (type && IS_SIGNATURE (type))
+ {
+ error ("signature name not allowed as base class");
+ yyval.ttype = NULL_TREE;
+ }
+ else
+ yyval.ttype = build_tree_list ((tree) yyval.ttype, yyvsp[0].ttype);
+ ;
+ break;}
+case 412:
+#line 2310 "parse.y"
+{
+ if (current_aggr == signature_type_node)
+ {
+ if (IS_AGGR_TYPE (TREE_TYPE (yyvsp[-1].ttype)))
+ {
+ sorry ("`sigof' as base signature specifier");
+ /* need to return some dummy signature identifier */
+ yyval.ttype = yyvsp[-1].ttype;
+ }
+ else
+ {
+ error ("`sigof' applied to non-aggregate expression");
+ yyval.ttype = error_mark_node;
+ }
+ }
+ else
+ {
+ error ("`sigof' in struct or class declaration");
+ yyval.ttype = error_mark_node;
+ }
+ ;
+ break;}
+case 413:
+#line 2332 "parse.y"
+{
+ if (current_aggr == signature_type_node)
+ {
+ if (IS_AGGR_TYPE (groktypename (yyvsp[-1].ttype)))
+ {
+ sorry ("`sigof' as base signature specifier");
+ /* need to return some dummy signature identifier */
+ yyval.ttype = yyvsp[-1].ttype;
+ }
+ else
+ {
+ error ("`sigof' applied to non-aggregate expression");
+ yyval.ttype = error_mark_node;
+ }
+ }
+ else
+ {
+ error ("`sigof' in struct or class declaration");
+ yyval.ttype = error_mark_node;
+ }
+ ;
+ break;}
+case 415:
+#line 2358 "parse.y"
+{ if (yyval.ttype != ridpointers[(int)RID_VIRTUAL])
+ sorry ("non-virtual access");
+ yyval.itype = access_default_virtual; ;
+ break;}
+case 416:
+#line 2362 "parse.y"
+{ int err = 0;
+ if (yyvsp[0].itype == access_protected)
+ {
+ warning ("`protected' access not implemented");
+ yyvsp[0].itype = access_public;
+ err++;
+ }
+ else if (yyvsp[0].itype == access_public)
+ {
+ if (yyvsp[-1].itype == access_private)
+ {
+ mixed:
+ error ("base class cannot be public and private");
+ }
+ else if (yyvsp[-1].itype == access_default_virtual)
+ yyval.itype = access_public_virtual;
+ }
+ else /* $2 == access_private */
+ {
+ if (yyvsp[-1].itype == access_public)
+ goto mixed;
+ else if (yyvsp[-1].itype == access_default_virtual)
+ yyval.itype = access_private_virtual;
+ }
+ ;
+ break;}
+case 417:
+#line 2388 "parse.y"
+{ if (yyvsp[0].ttype != ridpointers[(int)RID_VIRTUAL])
+ sorry ("non-virtual access");
+ if (yyval.itype == access_public)
+ yyval.itype = access_public_virtual;
+ else if (yyval.itype == access_private)
+ yyval.itype = access_private_virtual; ;
+ break;}
+case 418:
+#line 2397 "parse.y"
+{ tree t;
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+
+ if (! IS_AGGR_TYPE (yyvsp[-1].ttype))
+ {
+ yyvsp[-1].ttype = make_lang_type (RECORD_TYPE);
+ TYPE_NAME (yyvsp[-1].ttype) = get_identifier ("erroneous type");
+ }
+ if (TYPE_SIZE (yyvsp[-1].ttype))
+ duplicate_tag_error (yyvsp[-1].ttype);
+ if (TYPE_SIZE (yyvsp[-1].ttype) || TYPE_BEING_DEFINED (yyvsp[-1].ttype))
+ {
+ t = make_lang_type (TREE_CODE (yyvsp[-1].ttype));
+ pushtag (TYPE_IDENTIFIER (yyvsp[-1].ttype), t, 0);
+ yyvsp[-1].ttype = t;
+ }
+ pushclass (yyvsp[-1].ttype, 0);
+ TYPE_BEING_DEFINED (yyvsp[-1].ttype) = 1;
+#if 0
+ t = TYPE_IDENTIFIER (yyvsp[-1].ttype);
+ if (t && IDENTIFIER_TEMPLATE (t))
+ overload_template_name (t, 1);
+#endif
+ ;
+ break;}
+case 419:
+#line 2426 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 420:
+#line 2428 "parse.y"
+{
+ if (current_aggr == signature_type_node)
+ yyval.ttype = build_tree_list ((tree) access_public, yyval.ttype);
+ else
+ yyval.ttype = build_tree_list ((tree) access_default, yyval.ttype);
+ ;
+ break;}
+case 421:
+#line 2435 "parse.y"
+{
+ tree visspec = (tree) yyvsp[-2].itype;
+
+ if (current_aggr == signature_type_node)
+ {
+ error ("access specifier not allowed in signature");
+ visspec = (tree) access_public;
+ }
+ yyval.ttype = chainon (yyval.ttype, build_tree_list (visspec, yyvsp[0].ttype));
+ ;
+ break;}
+case 422:
+#line 2446 "parse.y"
+{
+ if (current_aggr == signature_type_node)
+ error ("access specifier not allowed in signature");
+ ;
+ break;}
+case 423:
+#line 2456 "parse.y"
+{ if (yyval.ttype == void_type_node) yyval.ttype = NULL_TREE;
+ ;
+ break;}
+case 424:
+#line 2459 "parse.y"
+{ /* In pushdecl, we created a reverse list of names
+ in this binding level. Make sure that the chain
+ of what we're trying to add isn't the item itself
+ (which can happen with what pushdecl's doing). */
+ if (yyvsp[0].ttype != NULL_TREE && yyvsp[0].ttype != void_type_node)
+ {
+ if (TREE_CHAIN (yyvsp[0].ttype) != yyval.ttype)
+ yyval.ttype = chainon (yyval.ttype, yyvsp[0].ttype);
+ else
+ yyval.ttype = yyvsp[0].ttype;
+ }
+ ;
+ break;}
+case 427:
+#line 2477 "parse.y"
+{ error ("missing ';' before right brace");
+ yyungetc ('}', 0); ;
+ break;}
+case 428:
+#line 2482 "parse.y"
+{ yyval.ttype = finish_method (yyval.ttype); ;
+ break;}
+case 429:
+#line 2484 "parse.y"
+{ yyval.ttype = finish_method (yyval.ttype); ;
+ break;}
+case 430:
+#line 2492 "parse.y"
+{
+ yyval.ttype = grok_x_components (yyval.ttype, yyvsp[0].ttype);
+ ;
+ break;}
+case 431:
+#line 2496 "parse.y"
+{
+ yyval.ttype = grok_x_components (yyval.ttype, yyvsp[0].ttype);
+ ;
+ break;}
+case 432:
+#line 2500 "parse.y"
+{ yyval.ttype = grokfield (yyval.ttype, NULL_TREE, yyvsp[-2].ttype, NULL_TREE, yyvsp[-1].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 433:
+#line 2503 "parse.y"
+{ yyval.ttype = grokbitfield (NULL_TREE, NULL_TREE, yyvsp[0].ttype); ;
+ break;}
+case 434:
+#line 2505 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 435:
+#line 2516 "parse.y"
+{ yyval.ttype = build_parse_node (CALL_EXPR, TREE_VALUE (yyvsp[-4].ttype),
+ yyvsp[-2].ttype, yyvsp[0].ttype);
+ yyval.ttype = grokfield (yyval.ttype, TREE_CHAIN (yyvsp[-4].ttype), NULL_TREE, NULL_TREE,
+ NULL_TREE); ;
+ break;}
+case 436:
+#line 2521 "parse.y"
+{ yyval.ttype = build_parse_node (CALL_EXPR, TREE_VALUE (yyvsp[-2].ttype),
+ empty_parms (), yyvsp[0].ttype);
+ yyval.ttype = grokfield (yyval.ttype, TREE_CHAIN (yyvsp[-2].ttype), NULL_TREE, NULL_TREE,
+ NULL_TREE); ;
+ break;}
+case 437:
+#line 2530 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 439:
+#line 2533 "parse.y"
+{
+ /* In this context, void_type_node encodes
+ friends. They have been recorded elsewhere. */
+ if (yyval.ttype == void_type_node)
+ yyval.ttype = yyvsp[0].ttype;
+ else
+ yyval.ttype = chainon (yyval.ttype, yyvsp[0].ttype);
+ ;
+ break;}
+case 440:
+#line 2545 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 442:
+#line 2548 "parse.y"
+{
+ /* In this context, void_type_node encodes
+ friends. They have been recorded elsewhere. */
+ if (yyval.ttype == void_type_node)
+ yyval.ttype = yyvsp[0].ttype;
+ else
+ yyval.ttype = chainon (yyval.ttype, yyvsp[0].ttype);
+ ;
+ break;}
+case 447:
+#line 2570 "parse.y"
+{ current_declspecs = yyvsp[-4].ttype;
+ yyval.ttype = grokfield (yyval.ttype, current_declspecs, yyvsp[-2].ttype, NULL_TREE, yyvsp[-1].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 448:
+#line 2574 "parse.y"
+{ current_declspecs = yyvsp[-6].ttype;
+ yyval.ttype = grokfield (yyval.ttype, current_declspecs, yyvsp[-4].ttype, yyvsp[0].ttype, yyvsp[-3].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[-2].ttype); ;
+ break;}
+case 449:
+#line 2578 "parse.y"
+{ current_declspecs = yyvsp[-4].ttype;
+ yyval.ttype = grokbitfield (yyval.ttype, current_declspecs, yyvsp[-1].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 450:
+#line 2585 "parse.y"
+{ current_declspecs = yyvsp[-4].ttype;
+ yyval.ttype = grokfield (yyval.ttype, current_declspecs, yyvsp[-2].ttype, NULL_TREE, yyvsp[-1].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 451:
+#line 2589 "parse.y"
+{ current_declspecs = yyvsp[-6].ttype;
+ yyval.ttype = grokfield (yyval.ttype, current_declspecs, yyvsp[-4].ttype, yyvsp[0].ttype, yyvsp[-3].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[-2].ttype); ;
+ break;}
+case 452:
+#line 2593 "parse.y"
+{ current_declspecs = yyvsp[-4].ttype;
+ yyval.ttype = grokbitfield (yyval.ttype, current_declspecs, yyvsp[-1].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 453:
+#line 2597 "parse.y"
+{ current_declspecs = yyvsp[-3].ttype;
+ yyval.ttype = grokbitfield (NULL_TREE, current_declspecs, yyvsp[-1].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 454:
+#line 2604 "parse.y"
+{ yyval.ttype = grokfield (yyval.ttype, current_declspecs, yyvsp[-2].ttype, NULL_TREE, yyvsp[-1].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 455:
+#line 2607 "parse.y"
+{ yyval.ttype = grokfield (yyval.ttype, current_declspecs, yyvsp[-4].ttype, yyvsp[0].ttype, yyvsp[-3].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[-2].ttype); ;
+ break;}
+case 456:
+#line 2610 "parse.y"
+{ yyval.ttype = grokbitfield (yyval.ttype, current_declspecs, yyvsp[-1].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 457:
+#line 2616 "parse.y"
+{ yyval.ttype = grokfield (yyval.ttype, current_declspecs, yyvsp[-2].ttype, NULL_TREE, yyvsp[-1].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 458:
+#line 2619 "parse.y"
+{ yyval.ttype = grokfield (yyval.ttype, current_declspecs, yyvsp[-4].ttype, yyvsp[0].ttype, yyvsp[-3].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[-2].ttype); ;
+ break;}
+case 459:
+#line 2622 "parse.y"
+{ yyval.ttype = grokbitfield (yyval.ttype, current_declspecs, yyvsp[-1].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 460:
+#line 2625 "parse.y"
+{ yyval.ttype = grokbitfield (NULL_TREE, current_declspecs, yyvsp[-1].ttype);
+ cplus_decl_attributes (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 462:
+#line 2636 "parse.y"
+{ TREE_CHAIN (yyvsp[0].ttype) = yyval.ttype; yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 463:
+#line 2641 "parse.y"
+{ yyval.ttype = build_enumerator (yyval.ttype, NULL_TREE); ;
+ break;}
+case 464:
+#line 2643 "parse.y"
+{ yyval.ttype = build_enumerator (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 465:
+#line 2649 "parse.y"
+{ yyval.ttype = build_decl_list (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 466:
+#line 2651 "parse.y"
+{ yyval.ttype = build_decl_list (yyval.ttype, NULL_TREE); ;
+ break;}
+case 467:
+#line 2655 "parse.y"
+{
+ if (flag_ansi)
+ pedwarn ("ANSI C++ forbids array dimensions with parenthesized type in new");
+ yyval.ttype = build_parse_node (ARRAY_REF, TREE_VALUE (yyvsp[-4].ttype), yyvsp[-1].ttype);
+ yyval.ttype = build_decl_list (TREE_PURPOSE (yyvsp[-4].ttype), yyval.ttype);
+ ;
+ break;}
+case 468:
+#line 2665 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 469:
+#line 2667 "parse.y"
+{ yyval.ttype = decl_tree_cons (NULL_TREE, yyvsp[0].ttype, yyval.ttype); ;
+ break;}
+case 470:
+#line 2672 "parse.y"
+{ yyval.ttype = IDENTIFIER_AS_LIST (yyval.ttype); ;
+ break;}
+case 471:
+#line 2674 "parse.y"
+{ yyval.ttype = decl_tree_cons (NULL_TREE, yyvsp[0].ttype, yyval.ttype); ;
+ break;}
+case 472:
+#line 2682 "parse.y"
+{ yyval.itype = suspend_momentary (); ;
+ break;}
+case 473:
+#line 2683 "parse.y"
+{ resume_momentary ((int) yyvsp[-1].itype); yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 474:
+#line 2690 "parse.y"
+{ yyval.ttype = make_pointer_declarator (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 475:
+#line 2692 "parse.y"
+{ yyval.ttype = make_reference_declarator (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 476:
+#line 2694 "parse.y"
+{ yyval.ttype = make_pointer_declarator (NULL_TREE, yyvsp[0].ttype); ;
+ break;}
+case 477:
+#line 2696 "parse.y"
+{ yyval.ttype = make_reference_declarator (NULL_TREE, yyvsp[0].ttype); ;
+ break;}
+case 478:
+#line 2698 "parse.y"
+{ tree arg = make_pointer_declarator (yyvsp[-1].ttype, yyvsp[0].ttype);
+ yyval.ttype = build_parse_node (SCOPE_REF, yyvsp[-2].ttype, arg);
+ ;
+ break;}
+case 480:
+#line 2706 "parse.y"
+{
+ /* Remember that this name has been used in the class
+ definition, as per [class.scope0] */
+ if (current_class_type
+ && TYPE_BEING_DEFINED (current_class_type)
+ && ! IDENTIFIER_CLASS_VALUE (yyval.ttype))
+ {
+ tree t = lookup_name (yyval.ttype, -2);
+ if (t)
+ pushdecl_class_level (t);
+ }
+ ;
+ break;}
+case 482:
+#line 2723 "parse.y"
+{ yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 483:
+#line 2728 "parse.y"
+{ yyval.ttype = build_parse_node (CALL_EXPR, yyval.ttype, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 484:
+#line 2730 "parse.y"
+{ yyval.ttype = build_parse_node (CALL_EXPR, yyval.ttype, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 485:
+#line 2732 "parse.y"
+{ yyval.ttype = build_parse_node (CALL_EXPR, yyval.ttype, empty_parms (), yyvsp[0].ttype); ;
+ break;}
+case 486:
+#line 2734 "parse.y"
+{ yyval.ttype = build_parse_node (CALL_EXPR, yyval.ttype, NULL_TREE, NULL_TREE); ;
+ break;}
+case 487:
+#line 2736 "parse.y"
+{ yyval.ttype = build_parse_node (ARRAY_REF, yyval.ttype, yyvsp[-1].ttype); ;
+ break;}
+case 488:
+#line 2738 "parse.y"
+{ yyval.ttype = build_parse_node (ARRAY_REF, yyval.ttype, NULL_TREE); ;
+ break;}
+case 489:
+#line 2740 "parse.y"
+{ yyval.ttype = yyvsp[-1].ttype; ;
+ break;}
+case 490:
+#line 2742 "parse.y"
+{ push_nested_class (TREE_TYPE (yyval.ttype), 3);
+ yyval.ttype = build_parse_node (SCOPE_REF, yyval.ttype, yyvsp[0].ttype);
+ TREE_COMPLEXITY (yyval.ttype) = current_class_depth; ;
+ break;}
+case 492:
+#line 2753 "parse.y"
+{ yyval.ttype = make_pointer_declarator (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 493:
+#line 2755 "parse.y"
+{ yyval.ttype = make_reference_declarator (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 494:
+#line 2757 "parse.y"
+{ yyval.ttype = make_pointer_declarator (NULL_TREE, yyvsp[0].ttype); ;
+ break;}
+case 495:
+#line 2759 "parse.y"
+{ yyval.ttype = make_reference_declarator (NULL_TREE, yyvsp[0].ttype); ;
+ break;}
+case 496:
+#line 2761 "parse.y"
+{ tree arg = make_pointer_declarator (yyvsp[-1].ttype, yyvsp[0].ttype);
+ yyval.ttype = build_parse_node (SCOPE_REF, yyvsp[-2].ttype, arg);
+ ;
+ break;}
+case 498:
+#line 2769 "parse.y"
+{ yyval.ttype = make_pointer_declarator (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 499:
+#line 2771 "parse.y"
+{ yyval.ttype = make_reference_declarator (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 500:
+#line 2773 "parse.y"
+{ yyval.ttype = make_pointer_declarator (NULL_TREE, yyvsp[0].ttype); ;
+ break;}
+case 501:
+#line 2775 "parse.y"
+{ yyval.ttype = make_reference_declarator (NULL_TREE, yyvsp[0].ttype); ;
+ break;}
+case 502:
+#line 2777 "parse.y"
+{ tree arg = make_pointer_declarator (yyvsp[-1].ttype, yyvsp[0].ttype);
+ yyval.ttype = build_parse_node (SCOPE_REF, yyvsp[-2].ttype, arg);
+ ;
+ break;}
+case 504:
+#line 2785 "parse.y"
+{ yyval.ttype = build_parse_node (CALL_EXPR, yyval.ttype, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 505:
+#line 2787 "parse.y"
+{ yyval.ttype = build_parse_node (CALL_EXPR, yyval.ttype, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 506:
+#line 2789 "parse.y"
+{ yyval.ttype = build_parse_node (CALL_EXPR, yyval.ttype, empty_parms (), yyvsp[0].ttype); ;
+ break;}
+case 507:
+#line 2791 "parse.y"
+{ yyval.ttype = build_parse_node (CALL_EXPR, yyval.ttype, NULL_TREE, NULL_TREE); ;
+ break;}
+case 508:
+#line 2793 "parse.y"
+{ yyval.ttype = finish_decl_parsing (yyvsp[-1].ttype); ;
+ break;}
+case 509:
+#line 2795 "parse.y"
+{ yyval.ttype = yyvsp[-1].ttype; ;
+ break;}
+case 510:
+#line 2797 "parse.y"
+{ yyval.ttype = build_parse_node (ARRAY_REF, yyval.ttype, yyvsp[-1].ttype); ;
+ break;}
+case 511:
+#line 2799 "parse.y"
+{ yyval.ttype = build_parse_node (ARRAY_REF, yyval.ttype, NULL_TREE); ;
+ break;}
+case 512:
+#line 2804 "parse.y"
+{ got_scope = NULL_TREE;
+ yyval.ttype = build_parse_node (SCOPE_REF, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 513:
+#line 2810 "parse.y"
+{ got_scope = NULL_TREE;
+ yyval.ttype = build_parse_node (SCOPE_REF, yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 515:
+#line 2817 "parse.y"
+{ yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 516:
+#line 2822 "parse.y"
+{ yyval.ttype = build_functional_cast (yyval.ttype, yyvsp[-1].ttype); ;
+ break;}
+case 517:
+#line 2824 "parse.y"
+{ yyval.ttype = reparse_decl_as_expr (yyval.ttype, yyvsp[-1].ttype); ;
+ break;}
+case 518:
+#line 2826 "parse.y"
+{ yyval.ttype = reparse_absdcl_as_expr (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 522:
+#line 2837 "parse.y"
+{ yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 523:
+#line 2844 "parse.y"
+{ got_scope = TREE_TYPE (yyval.ttype); ;
+ break;}
+case 524:
+#line 2846 "parse.y"
+{ got_scope = TREE_TYPE (yyval.ttype); ;
+ break;}
+case 526:
+#line 2862 "parse.y"
+{ yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 528:
+#line 2868 "parse.y"
+{ yyval.ttype = yyvsp[0].ttype; ;
+ break;}
+case 529:
+#line 2873 "parse.y"
+{ got_scope = NULL_TREE; ;
+ break;}
+case 530:
+#line 2875 "parse.y"
+{ yyval.ttype = yyvsp[-1].ttype; got_scope = NULL_TREE; ;
+ break;}
+case 531:
+#line 2882 "parse.y"
+{ got_scope = void_type_node; ;
+ break;}
+case 532:
+#line 2888 "parse.y"
+{ yyval.ttype = make_pointer_declarator (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 533:
+#line 2890 "parse.y"
+{ yyval.ttype = make_pointer_declarator (yyvsp[0].ttype, NULL_TREE); ;
+ break;}
+case 534:
+#line 2892 "parse.y"
+{ yyval.ttype = make_reference_declarator (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 535:
+#line 2894 "parse.y"
+{ yyval.ttype = make_reference_declarator (yyvsp[0].ttype, NULL_TREE); ;
+ break;}
+case 536:
+#line 2896 "parse.y"
+{ tree arg = make_pointer_declarator (yyvsp[0].ttype, NULL_TREE);
+ yyval.ttype = build_parse_node (SCOPE_REF, yyvsp[-1].ttype, arg);
+ ;
+ break;}
+case 537:
+#line 2900 "parse.y"
+{ tree arg = make_pointer_declarator (yyvsp[-1].ttype, yyvsp[0].ttype);
+ yyval.ttype = build_parse_node (SCOPE_REF, yyvsp[-2].ttype, arg);
+ ;
+ break;}
+case 539:
+#line 2909 "parse.y"
+{ yyval.ttype = build_parse_node (ARRAY_REF, NULL_TREE, yyvsp[-1].ttype); ;
+ break;}
+case 540:
+#line 2911 "parse.y"
+{ yyval.ttype = build_parse_node (ARRAY_REF, yyval.ttype, yyvsp[-1].ttype); ;
+ break;}
+case 541:
+#line 2917 "parse.y"
+{ yyval.ttype = make_pointer_declarator (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 542:
+#line 2919 "parse.y"
+{ yyval.ttype = make_pointer_declarator (NULL_TREE, yyvsp[0].ttype); ;
+ break;}
+case 543:
+#line 2921 "parse.y"
+{ yyval.ttype = make_pointer_declarator (yyvsp[0].ttype, NULL_TREE); ;
+ break;}
+case 544:
+#line 2923 "parse.y"
+{ yyval.ttype = make_pointer_declarator (NULL_TREE, NULL_TREE); ;
+ break;}
+case 545:
+#line 2925 "parse.y"
+{ yyval.ttype = make_reference_declarator (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 546:
+#line 2927 "parse.y"
+{ yyval.ttype = make_reference_declarator (NULL_TREE, yyvsp[0].ttype); ;
+ break;}
+case 547:
+#line 2929 "parse.y"
+{ yyval.ttype = make_reference_declarator (yyvsp[0].ttype, NULL_TREE); ;
+ break;}
+case 548:
+#line 2931 "parse.y"
+{ yyval.ttype = make_reference_declarator (NULL_TREE, NULL_TREE); ;
+ break;}
+case 549:
+#line 2933 "parse.y"
+{ tree arg = make_pointer_declarator (yyvsp[0].ttype, NULL_TREE);
+ yyval.ttype = build_parse_node (SCOPE_REF, yyvsp[-1].ttype, arg);
+ ;
+ break;}
+case 550:
+#line 2937 "parse.y"
+{ tree arg = make_pointer_declarator (yyvsp[-1].ttype, yyvsp[0].ttype);
+ yyval.ttype = build_parse_node (SCOPE_REF, yyvsp[-2].ttype, arg);
+ ;
+ break;}
+case 552:
+#line 2946 "parse.y"
+{ yyval.ttype = yyvsp[-1].ttype; ;
+ break;}
+case 554:
+#line 2950 "parse.y"
+{ yyval.ttype = build_parse_node (CALL_EXPR, yyval.ttype, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 555:
+#line 2952 "parse.y"
+{ yyval.ttype = build_parse_node (CALL_EXPR, yyval.ttype, empty_parms (), yyvsp[0].ttype); ;
+ break;}
+case 556:
+#line 2954 "parse.y"
+{ yyval.ttype = build_parse_node (ARRAY_REF, yyval.ttype, yyvsp[-1].ttype); ;
+ break;}
+case 557:
+#line 2956 "parse.y"
+{ yyval.ttype = build_parse_node (ARRAY_REF, yyval.ttype, NULL_TREE); ;
+ break;}
+case 558:
+#line 2958 "parse.y"
+{ yyval.ttype = build_parse_node (CALL_EXPR, NULL_TREE, yyvsp[-2].ttype, yyvsp[0].ttype); ;
+ break;}
+case 559:
+#line 2960 "parse.y"
+{ TREE_OPERAND (yyval.ttype, 2) = yyvsp[0].ttype; ;
+ break;}
+case 560:
+#line 2962 "parse.y"
+{ TREE_OPERAND (yyval.ttype, 2) = yyvsp[0].ttype; ;
+ break;}
+case 561:
+#line 2964 "parse.y"
+{ yyval.ttype = build_parse_node (ARRAY_REF, NULL_TREE, yyvsp[-1].ttype); ;
+ break;}
+case 562:
+#line 2966 "parse.y"
+{ yyval.ttype = build_parse_node (ARRAY_REF, NULL_TREE, NULL_TREE); ;
+ break;}
+case 568:
+#line 2988 "parse.y"
+{ emit_line_note (input_filename, lineno);
+ pushlevel (0);
+ clear_last_expr ();
+ push_momentary ();
+ expand_start_bindings (0); ;
+ break;}
+case 570:
+#line 3000 "parse.y"
+{ if (flag_ansi)
+ pedwarn ("ANSI C++ forbids label declarations"); ;
+ break;}
+case 573:
+#line 3011 "parse.y"
+{ tree link;
+ for (link = yyvsp[-1].ttype; link; link = TREE_CHAIN (link))
+ {
+ tree label = shadow_label (TREE_VALUE (link));
+ C_DECLARED_LABEL_FLAG (label) = 1;
+ declare_nonlocal_label (label);
+ }
+ ;
+ break;}
+case 574:
+#line 3025 "parse.y"
+{;
+ break;}
+case 576:
+#line 3030 "parse.y"
+{ expand_end_bindings (getdecls (), kept_level_p(), 1);
+ yyval.ttype = poplevel (kept_level_p (), 1, 0);
+ pop_momentary (); ;
+ break;}
+case 577:
+#line 3034 "parse.y"
+{ expand_end_bindings (getdecls (), kept_level_p(), 1);
+ yyval.ttype = poplevel (kept_level_p (), 1, 0);
+ pop_momentary (); ;
+ break;}
+case 578:
+#line 3038 "parse.y"
+{ expand_end_bindings (getdecls (), kept_level_p(), 1);
+ yyval.ttype = poplevel (kept_level_p (), 0, 0);
+ pop_momentary (); ;
+ break;}
+case 579:
+#line 3042 "parse.y"
+{ expand_end_bindings (getdecls (), kept_level_p(), 1);
+ yyval.ttype = poplevel (kept_level_p (), 0, 0);
+ pop_momentary (); ;
+ break;}
+case 580:
+#line 3049 "parse.y"
+{ cond_stmt_keyword = "if"; ;
+ break;}
+case 581:
+#line 3051 "parse.y"
+{ emit_line_note (input_filename, lineno);
+ expand_start_cond (bool_truthvalue_conversion (yyvsp[0].ttype), 0); ;
+ break;}
+case 583:
+#line 3058 "parse.y"
+{ finish_stmt (); ;
+ break;}
+case 584:
+#line 3060 "parse.y"
+{ expand_end_bindings (getdecls (), kept_level_p (), 1);
+ yyval.ttype = poplevel (kept_level_p (), 1, 0);
+ pop_momentary (); ;
+ break;}
+case 585:
+#line 3067 "parse.y"
+{ finish_stmt (); ;
+ break;}
+case 587:
+#line 3073 "parse.y"
+{ finish_stmt (); ;
+ break;}
+case 588:
+#line 3075 "parse.y"
+{
+ tree expr = yyvsp[-1].ttype;
+ emit_line_note (input_filename, lineno);
+ /* Do default conversion if safe and possibly important,
+ in case within ({...}). */
+ if ((TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE
+ && lvalue_p (expr))
+ || TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE)
+ expr = default_conversion (expr);
+ cplus_expand_expr_stmt (expr);
+ clear_momentary ();
+ finish_stmt (); ;
+ break;}
+case 589:
+#line 3088 "parse.y"
+{ expand_start_else (); ;
+ break;}
+case 590:
+#line 3090 "parse.y"
+{ expand_end_cond ();
+ expand_end_bindings (getdecls (), kept_level_p (), 1);
+ poplevel (kept_level_p (), 1, 0);
+ pop_momentary ();
+ finish_stmt (); ;
+ break;}
+case 591:
+#line 3096 "parse.y"
+{ expand_end_cond ();
+ expand_end_bindings (getdecls (), kept_level_p (), 1);
+ poplevel (kept_level_p (), 1, 0);
+ pop_momentary ();
+ finish_stmt (); ;
+ break;}
+case 592:
+#line 3102 "parse.y"
+{ emit_nop ();
+ emit_line_note (input_filename, lineno);
+ expand_start_loop (1);
+ cond_stmt_keyword = "while"; ;
+ break;}
+case 593:
+#line 3107 "parse.y"
+{ expand_exit_loop_if_false (0, bool_truthvalue_conversion (yyvsp[0].ttype)); ;
+ break;}
+case 594:
+#line 3109 "parse.y"
+{ expand_end_bindings (getdecls (), kept_level_p (), 1);
+ poplevel (kept_level_p (), 1, 0);
+ pop_momentary ();
+ expand_end_loop ();
+ finish_stmt (); ;
+ break;}
+case 595:
+#line 3115 "parse.y"
+{ emit_nop ();
+ emit_line_note (input_filename, lineno);
+ expand_start_loop_continue_elsewhere (1); ;
+ break;}
+case 596:
+#line 3119 "parse.y"
+{ expand_loop_continue_here ();
+ cond_stmt_keyword = "do"; ;
+ break;}
+case 597:
+#line 3122 "parse.y"
+{ emit_line_note (input_filename, lineno);
+ expand_exit_loop_if_false (0, bool_truthvalue_conversion (yyvsp[-1].ttype));
+ expand_end_loop ();
+ clear_momentary ();
+ finish_stmt (); ;
+ break;}
+case 598:
+#line 3128 "parse.y"
+{ emit_nop ();
+ emit_line_note (input_filename, lineno);
+ if (yyvsp[0].ttype) cplus_expand_expr_stmt (yyvsp[0].ttype);
+ expand_start_loop_continue_elsewhere (1); ;
+ break;}
+case 599:
+#line 3133 "parse.y"
+{ emit_line_note (input_filename, lineno);
+ if (yyvsp[-1].ttype) expand_exit_loop_if_false (0, bool_truthvalue_conversion (yyvsp[-1].ttype)); ;
+ break;}
+case 600:
+#line 3138 "parse.y"
+{ push_momentary (); ;
+ break;}
+case 601:
+#line 3140 "parse.y"
+{ emit_line_note (input_filename, lineno);
+ expand_end_bindings (getdecls (), kept_level_p (), 1);
+ poplevel (kept_level_p (), 1, 0);
+ pop_momentary ();
+ expand_loop_continue_here ();
+ if (yyvsp[-3].ttype) cplus_expand_expr_stmt (yyvsp[-3].ttype);
+ pop_momentary ();
+ expand_end_loop ();
+ finish_stmt (); ;
+ break;}
+case 602:
+#line 3150 "parse.y"
+{ emit_nop ();
+ emit_line_note (input_filename, lineno);
+ expand_start_loop_continue_elsewhere (1); ;
+ break;}
+case 603:
+#line 3154 "parse.y"
+{ emit_line_note (input_filename, lineno);
+ if (yyvsp[-1].ttype) expand_exit_loop_if_false (0, bool_truthvalue_conversion (yyvsp[-1].ttype)); ;
+ break;}
+case 604:
+#line 3159 "parse.y"
+{ push_momentary ();
+ yyvsp[0].itype = lineno; ;
+ break;}
+case 605:
+#line 3162 "parse.y"
+{ emit_line_note (input_filename, (int) yyvsp[-2].itype);
+ expand_end_bindings (getdecls (), kept_level_p (), 1);
+ poplevel (kept_level_p (), 1, 0);
+ pop_momentary ();
+ expand_loop_continue_here ();
+ if (yyvsp[-3].ttype) cplus_expand_expr_stmt (yyvsp[-3].ttype);
+ pop_momentary ();
+ expand_end_loop ();
+ finish_stmt ();
+ ;
+ break;}
+case 606:
+#line 3173 "parse.y"
+{ emit_line_note (input_filename, lineno);
+ c_expand_start_case (yyvsp[-1].ttype);
+ /* Don't let the tree nodes for $4 be discarded by
+ clear_momentary during the parsing of the next stmt. */
+ push_momentary (); ;
+ break;}
+case 607:
+#line 3179 "parse.y"
+{ expand_end_case (yyvsp[-3].ttype);
+ pop_momentary ();
+ expand_end_bindings (getdecls (), kept_level_p (), 1);
+ poplevel (kept_level_p (), 1, 0);
+ pop_momentary ();
+ finish_stmt (); ;
+ break;}
+case 608:
+#line 3186 "parse.y"
+{ register tree value = check_cp_case_value (yyvsp[-1].ttype);
+ register tree label
+ = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+
+ if (value != error_mark_node)
+ {
+ tree duplicate;
+ int success = pushcase (value, convert_and_check,
+ label, &duplicate);
+ if (success == 1)
+ cp_error ("case label `%E' not within a switch statement", yyvsp[-1].ttype);
+ else if (success == 2)
+ {
+ cp_error ("duplicate case value `%E'", yyvsp[-1].ttype);
+ cp_error_at ("`%E' previously used here", duplicate);
+ }
+ else if (success == 3)
+ warning ("case value out of range");
+ else if (success == 5)
+ cp_error ("case label `%E' within scope of cleanup or variable array", yyvsp[-1].ttype);
+ }
+ define_case_label (label);
+ ;
+ break;}
+case 610:
+#line 3211 "parse.y"
+{ register tree value1 = check_cp_case_value (yyvsp[-3].ttype);
+ register tree value2 = check_cp_case_value (yyvsp[-1].ttype);
+ register tree label
+ = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+
+ if (flag_ansi)
+ pedwarn ("ANSI C++ forbids range expressions in switch statement");
+ if (value1 != error_mark_node
+ && value2 != error_mark_node)
+ {
+ tree duplicate;
+ int success = pushcase_range (value1, value2,
+ convert_and_check, label,
+ &duplicate);
+ if (success == 1)
+ error ("case label not within a switch statement");
+ else if (success == 2)
+ {
+ error ("duplicate (or overlapping) case value");
+ error_with_decl (duplicate, "this is the first entry overlapping that value");
+ }
+ else if (success == 3)
+ warning ("case value out of range");
+ else if (success == 4)
+ warning ("empty range specified");
+ else if (success == 5)
+ error ("case label within scope of cleanup or variable array");
+ }
+ define_case_label (label);
+ ;
+ break;}
+case 612:
+#line 3243 "parse.y"
+{
+ tree duplicate;
+ register tree label
+ = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+ int success = pushcase (NULL_TREE, 0, label, &duplicate);
+ if (success == 1)
+ error ("default label not within a switch statement");
+ else if (success == 2)
+ {
+ error ("multiple default labels in one switch");
+ error_with_decl (duplicate, "this is the first default label");
+ }
+ define_case_label (NULL_TREE);
+ ;
+ break;}
+case 614:
+#line 3259 "parse.y"
+{ emit_line_note (input_filename, lineno);
+ if ( ! expand_exit_something ())
+ error ("break statement not within loop or switch"); ;
+ break;}
+case 615:
+#line 3263 "parse.y"
+{ emit_line_note (input_filename, lineno);
+ if (! expand_continue_loop (0))
+ error ("continue statement not within a loop"); ;
+ break;}
+case 616:
+#line 3267 "parse.y"
+{ emit_line_note (input_filename, lineno);
+ c_expand_return (NULL_TREE); ;
+ break;}
+case 617:
+#line 3270 "parse.y"
+{ emit_line_note (input_filename, lineno);
+ c_expand_return (yyvsp[-1].ttype);
+ finish_stmt ();
+ ;
+ break;}
+case 618:
+#line 3275 "parse.y"
+{ if (TREE_CHAIN (yyvsp[-2].ttype)) yyvsp[-2].ttype = combine_strings (yyvsp[-2].ttype);
+ emit_line_note (input_filename, lineno);
+ expand_asm (yyvsp[-2].ttype);
+ finish_stmt ();
+ ;
+ break;}
+case 619:
+#line 3282 "parse.y"
+{ if (TREE_CHAIN (yyvsp[-4].ttype)) yyvsp[-4].ttype = combine_strings (yyvsp[-4].ttype);
+ emit_line_note (input_filename, lineno);
+ c_expand_asm_operands (yyvsp[-4].ttype, yyvsp[-2].ttype, NULL_TREE, NULL_TREE,
+ yyvsp[-6].ttype == ridpointers[(int)RID_VOLATILE],
+ input_filename, lineno);
+ finish_stmt ();
+ ;
+ break;}
+case 620:
+#line 3291 "parse.y"
+{ if (TREE_CHAIN (yyvsp[-6].ttype)) yyvsp[-6].ttype = combine_strings (yyvsp[-6].ttype);
+ emit_line_note (input_filename, lineno);
+ c_expand_asm_operands (yyvsp[-6].ttype, yyvsp[-4].ttype, yyvsp[-2].ttype, NULL_TREE,
+ yyvsp[-8].ttype == ridpointers[(int)RID_VOLATILE],
+ input_filename, lineno);
+ finish_stmt ();
+ ;
+ break;}
+case 621:
+#line 3301 "parse.y"
+{ if (TREE_CHAIN (yyvsp[-8].ttype)) yyvsp[-8].ttype = combine_strings (yyvsp[-8].ttype);
+ emit_line_note (input_filename, lineno);
+ c_expand_asm_operands (yyvsp[-8].ttype, yyvsp[-6].ttype, yyvsp[-4].ttype, yyvsp[-2].ttype,
+ yyvsp[-10].ttype == ridpointers[(int)RID_VOLATILE],
+ input_filename, lineno);
+ finish_stmt ();
+ ;
+ break;}
+case 622:
+#line 3309 "parse.y"
+{ emit_line_note (input_filename, lineno);
+ expand_computed_goto (yyvsp[-1].ttype); ;
+ break;}
+case 623:
+#line 3312 "parse.y"
+{ tree decl;
+ emit_line_note (input_filename, lineno);
+ decl = lookup_label (yyvsp[-1].ttype);
+ TREE_USED (decl) = 1;
+ expand_goto (decl); ;
+ break;}
+case 624:
+#line 3318 "parse.y"
+{ finish_stmt (); ;
+ break;}
+case 625:
+#line 3320 "parse.y"
+{ error ("label must be followed by statement");
+ yyungetc ('}', 0);
+ finish_stmt (); ;
+ break;}
+case 626:
+#line 3324 "parse.y"
+{ finish_stmt (); ;
+ break;}
+case 628:
+#line 3330 "parse.y"
+{ expand_start_try_stmts (); ;
+ break;}
+case 629:
+#line 3332 "parse.y"
+{ expand_end_try_stmts ();
+ expand_start_all_catch (); ;
+ break;}
+case 630:
+#line 3335 "parse.y"
+{ expand_end_all_catch (); ;
+ break;}
+case 631:
+#line 3343 "parse.y"
+{ expand_end_bindings (0,1,1);
+ poplevel (2,0,0);
+ ;
+ break;}
+case 632:
+#line 3347 "parse.y"
+{ expand_end_bindings (0,1,1);
+ poplevel (2,0,0);
+ ;
+ break;}
+case 633:
+#line 3351 "parse.y"
+{ expand_end_bindings (0,1,1);
+ poplevel (2,0,0);
+ ;
+ break;}
+case 635:
+#line 3359 "parse.y"
+{ emit_line_note (input_filename, lineno); ;
+ break;}
+case 636:
+#line 3361 "parse.y"
+{ expand_end_catch_block (); ;
+ break;}
+case 639:
+#line 3371 "parse.y"
+{ expand_start_catch_block (NULL_TREE, NULL_TREE); ;
+ break;}
+case 640:
+#line 3383 "parse.y"
+{ expand_start_catch_block (TREE_PURPOSE (yyvsp[-1].ttype),
+ TREE_VALUE (yyvsp[-1].ttype)); ;
+ break;}
+case 641:
+#line 3389 "parse.y"
+{ tree label;
+ do_label:
+ label = define_label (input_filename, lineno, yyvsp[-1].ttype);
+ if (label)
+ expand_label (label);
+ ;
+ break;}
+case 642:
+#line 3396 "parse.y"
+{ goto do_label; ;
+ break;}
+case 643:
+#line 3401 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 644:
+#line 3403 "parse.y"
+{ yyval.ttype = yyvsp[-1].ttype; ;
+ break;}
+case 645:
+#line 3405 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 646:
+#line 3410 "parse.y"
+{ yyval.itype = 0; ;
+ break;}
+case 647:
+#line 3412 "parse.y"
+{ yyval.itype = 0; ;
+ break;}
+case 648:
+#line 3414 "parse.y"
+{ yyval.itype = 1; ;
+ break;}
+case 649:
+#line 3416 "parse.y"
+{ yyval.itype = -1; ;
+ break;}
+case 650:
+#line 3423 "parse.y"
+{ emit_line_note (input_filename, lineno);
+ yyval.ttype = NULL_TREE; ;
+ break;}
+case 651:
+#line 3426 "parse.y"
+{ emit_line_note (input_filename, lineno); ;
+ break;}
+case 652:
+#line 3431 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 654:
+#line 3434 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 655:
+#line 3440 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 658:
+#line 3447 "parse.y"
+{ yyval.ttype = chainon (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 659:
+#line 3452 "parse.y"
+{ yyval.ttype = build_tree_list (yyval.ttype, yyvsp[-1].ttype); ;
+ break;}
+case 660:
+#line 3457 "parse.y"
+{ yyval.ttype = tree_cons (NULL_TREE, yyval.ttype, NULL_TREE); ;
+ break;}
+case 661:
+#line 3459 "parse.y"
+{ yyval.ttype = tree_cons (NULL_TREE, yyvsp[0].ttype, yyval.ttype); ;
+ break;}
+case 662:
+#line 3469 "parse.y"
+{
+ if (strict_prototype)
+ yyval.ttype = void_list_node;
+ else
+ yyval.ttype = NULL_TREE;
+ ;
+ break;}
+case 664:
+#line 3477 "parse.y"
+{ yyval.ttype = tree_cons (NULL_TREE, yyval.ttype, void_list_node);
+ TREE_PARMLIST (yyval.ttype) = 1; ;
+ break;}
+case 665:
+#line 3485 "parse.y"
+{
+ yyval.ttype = chainon (yyval.ttype, void_list_node);
+ TREE_PARMLIST (yyval.ttype) = 1;
+ ;
+ break;}
+case 666:
+#line 3490 "parse.y"
+{
+ TREE_PARMLIST (yyval.ttype) = 1;
+ ;
+ break;}
+case 667:
+#line 3495 "parse.y"
+{
+ TREE_PARMLIST (yyval.ttype) = 1;
+ ;
+ break;}
+case 668:
+#line 3499 "parse.y"
+{
+ yyval.ttype = build_tree_list (NULL_TREE, yyval.ttype);
+ TREE_PARMLIST (yyval.ttype) = 1;
+ ;
+ break;}
+case 669:
+#line 3504 "parse.y"
+{
+ /* ARM $8.2.5 has this as a boxed-off comment. */
+ if (pedantic)
+ warning ("use of `...' without a first argument is non-portable");
+ yyval.ttype = NULL_TREE;
+ ;
+ break;}
+case 670:
+#line 3511 "parse.y"
+{
+ TREE_PARMLIST (yyval.ttype) = 1;
+ ;
+ break;}
+case 671:
+#line 3515 "parse.y"
+{
+ TREE_PARMLIST (yyval.ttype) = 1;
+ ;
+ break;}
+case 672:
+#line 3519 "parse.y"
+{
+ yyval.ttype = build_tree_list (NULL_TREE, yyval.ttype);
+ TREE_PARMLIST (yyval.ttype) = 1;
+ ;
+ break;}
+case 673:
+#line 3524 "parse.y"
+{
+ /* This helps us recover from really nasty
+ parse errors, for example, a missing right
+ parenthesis. */
+ yyerror ("possibly missing ')'");
+ yyval.ttype = chainon (yyval.ttype, void_list_node);
+ TREE_PARMLIST (yyval.ttype) = 1;
+ yyungetc (':', 0);
+ yychar = ')';
+ ;
+ break;}
+case 674:
+#line 3535 "parse.y"
+{
+ /* This helps us recover from really nasty
+ parse errors, for example, a missing right
+ parenthesis. */
+ yyerror ("possibly missing ')'");
+ yyval.ttype = tree_cons (NULL_TREE, yyval.ttype, void_list_node);
+ TREE_PARMLIST (yyval.ttype) = 1;
+ yyungetc (':', 0);
+ yychar = ')';
+ ;
+ break;}
+case 675:
+#line 3550 "parse.y"
+{ yyval.ttype = build_tree_list (NULL_TREE, yyval.ttype); ;
+ break;}
+case 676:
+#line 3552 "parse.y"
+{ yyval.ttype = build_tree_list (yyvsp[0].ttype, yyval.ttype); ;
+ break;}
+case 677:
+#line 3554 "parse.y"
+{ yyval.ttype = chainon (yyval.ttype, build_tree_list (NULL_TREE, yyvsp[0].ttype)); ;
+ break;}
+case 678:
+#line 3556 "parse.y"
+{ yyval.ttype = chainon (yyval.ttype, build_tree_list (yyvsp[0].ttype, yyvsp[-2].ttype)); ;
+ break;}
+case 679:
+#line 3558 "parse.y"
+{ yyval.ttype = chainon (yyval.ttype, build_tree_list (NULL_TREE, yyvsp[0].ttype)); ;
+ break;}
+case 680:
+#line 3560 "parse.y"
+{ yyval.ttype = chainon (yyval.ttype, build_tree_list (yyvsp[0].ttype, yyvsp[-2].ttype)); ;
+ break;}
+case 682:
+#line 3566 "parse.y"
+{ yyval.ttype = build_tree_list (NULL_TREE, yyval.ttype); ;
+ break;}
+case 683:
+#line 3589 "parse.y"
+{ yyval.ttype = build_tree_list (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 684:
+#line 3591 "parse.y"
+{ yyval.ttype = build_tree_list (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 685:
+#line 3593 "parse.y"
+{ yyval.ttype = build_tree_list (get_decl_list (yyval.ttype), yyvsp[0].ttype); ;
+ break;}
+case 686:
+#line 3595 "parse.y"
+{ yyval.ttype = build_tree_list (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 687:
+#line 3597 "parse.y"
+{ yyval.ttype = build_tree_list (yyval.ttype, NULL_TREE); ;
+ break;}
+case 688:
+#line 3599 "parse.y"
+{ yyval.ttype = build_tree_list (yyval.ttype, yyvsp[0].ttype); ;
+ break;}
+case 691:
+#line 3608 "parse.y"
+{ see_typename (); ;
+ break;}
+case 692:
+#line 3631 "parse.y"
+{
+ warning ("type specifier omitted for parameter");
+ yyval.ttype = build_tree_list (TREE_PURPOSE (TREE_VALUE (yyvsp[-1].ttype)), NULL_TREE);
+ ;
+ break;}
+case 693:
+#line 3636 "parse.y"
+{
+ warning ("type specifier omitted for parameter");
+ yyval.ttype = build_tree_list (TREE_PURPOSE (TREE_VALUE (yyvsp[-2].ttype)), yyval.ttype);
+ ;
+ break;}
+case 694:
+#line 3644 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 695:
+#line 3646 "parse.y"
+{ yyval.ttype = yyvsp[-1].ttype; ;
+ break;}
+case 696:
+#line 3651 "parse.y"
+{ yyval.ttype = build_decl_list (NULL_TREE, yyval.ttype); ;
+ break;}
+case 698:
+#line 3657 "parse.y"
+{
+ TREE_CHAIN (yyvsp[0].ttype) = yyval.ttype;
+ yyval.ttype = yyvsp[0].ttype;
+ ;
+ break;}
+case 699:
+#line 3665 "parse.y"
+{ yyval.ttype = NULL_TREE; ;
+ break;}
+case 700:
+#line 3667 "parse.y"
+{ yyval.ttype = make_pointer_declarator (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 701:
+#line 3669 "parse.y"
+{ yyval.ttype = make_reference_declarator (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 702:
+#line 3671 "parse.y"
+{ tree arg = make_pointer_declarator (yyvsp[-1].ttype, yyvsp[0].ttype);
+ yyval.ttype = build_parse_node (SCOPE_REF, yyvsp[-2].ttype, arg);
+ ;
+ break;}
+case 703:
+#line 3677 "parse.y"
+{ got_scope = NULL_TREE; ;
+ break;}
+case 704:
+#line 3682 "parse.y"
+{ yyval.ttype = ansi_opname[MULT_EXPR]; ;
+ break;}
+case 705:
+#line 3684 "parse.y"
+{ yyval.ttype = ansi_opname[TRUNC_DIV_EXPR]; ;
+ break;}
+case 706:
+#line 3686 "parse.y"
+{ yyval.ttype = ansi_opname[TRUNC_MOD_EXPR]; ;
+ break;}
+case 707:
+#line 3688 "parse.y"
+{ yyval.ttype = ansi_opname[PLUS_EXPR]; ;
+ break;}
+case 708:
+#line 3690 "parse.y"
+{ yyval.ttype = ansi_opname[MINUS_EXPR]; ;
+ break;}
+case 709:
+#line 3692 "parse.y"
+{ yyval.ttype = ansi_opname[BIT_AND_EXPR]; ;
+ break;}
+case 710:
+#line 3694 "parse.y"
+{ yyval.ttype = ansi_opname[BIT_IOR_EXPR]; ;
+ break;}
+case 711:
+#line 3696 "parse.y"
+{ yyval.ttype = ansi_opname[BIT_XOR_EXPR]; ;
+ break;}
+case 712:
+#line 3698 "parse.y"
+{ yyval.ttype = ansi_opname[BIT_NOT_EXPR]; ;
+ break;}
+case 713:
+#line 3700 "parse.y"
+{ yyval.ttype = ansi_opname[COMPOUND_EXPR]; ;
+ break;}
+case 714:
+#line 3702 "parse.y"
+{ yyval.ttype = ansi_opname[yyvsp[0].code]; ;
+ break;}
+case 715:
+#line 3704 "parse.y"
+{ yyval.ttype = ansi_opname[LT_EXPR]; ;
+ break;}
+case 716:
+#line 3706 "parse.y"
+{ yyval.ttype = ansi_opname[GT_EXPR]; ;
+ break;}
+case 717:
+#line 3708 "parse.y"
+{ yyval.ttype = ansi_opname[yyvsp[0].code]; ;
+ break;}
+case 718:
+#line 3710 "parse.y"
+{ yyval.ttype = ansi_assopname[yyvsp[0].code]; ;
+ break;}
+case 719:
+#line 3712 "parse.y"
+{ yyval.ttype = ansi_opname [MODIFY_EXPR]; ;
+ break;}
+case 720:
+#line 3714 "parse.y"
+{ yyval.ttype = ansi_opname[yyvsp[0].code]; ;
+ break;}
+case 721:
+#line 3716 "parse.y"
+{ yyval.ttype = ansi_opname[yyvsp[0].code]; ;
+ break;}
+case 722:
+#line 3718 "parse.y"
+{ yyval.ttype = ansi_opname[POSTINCREMENT_EXPR]; ;
+ break;}
+case 723:
+#line 3720 "parse.y"
+{ yyval.ttype = ansi_opname[PREDECREMENT_EXPR]; ;
+ break;}
+case 724:
+#line 3722 "parse.y"
+{ yyval.ttype = ansi_opname[TRUTH_ANDIF_EXPR]; ;
+ break;}
+case 725:
+#line 3724 "parse.y"
+{ yyval.ttype = ansi_opname[TRUTH_ORIF_EXPR]; ;
+ break;}
+case 726:
+#line 3726 "parse.y"
+{ yyval.ttype = ansi_opname[TRUTH_NOT_EXPR]; ;
+ break;}
+case 727:
+#line 3728 "parse.y"
+{ yyval.ttype = ansi_opname[COND_EXPR]; ;
+ break;}
+case 728:
+#line 3730 "parse.y"
+{ yyval.ttype = ansi_opname[yyvsp[0].code]; ;
+ break;}
+case 729:
+#line 3732 "parse.y"
+{ yyval.ttype = ansi_opname[COMPONENT_REF]; ;
+ break;}
+case 730:
+#line 3734 "parse.y"
+{ yyval.ttype = ansi_opname[MEMBER_REF]; ;
+ break;}
+case 731:
+#line 3736 "parse.y"
+{ yyval.ttype = ansi_opname[CALL_EXPR]; ;
+ break;}
+case 732:
+#line 3738 "parse.y"
+{ yyval.ttype = ansi_opname[ARRAY_REF]; ;
+ break;}
+case 733:
+#line 3740 "parse.y"
+{ yyval.ttype = ansi_opname[NEW_EXPR]; ;
+ break;}
+case 734:
+#line 3742 "parse.y"
+{ yyval.ttype = ansi_opname[DELETE_EXPR]; ;
+ break;}
+case 735:
+#line 3744 "parse.y"
+{ yyval.ttype = ansi_opname[VEC_NEW_EXPR]; ;
+ break;}
+case 736:
+#line 3746 "parse.y"
+{ yyval.ttype = ansi_opname[VEC_DELETE_EXPR]; ;
+ break;}
+case 737:
+#line 3749 "parse.y"
+{ yyval.ttype = grokoptypename (yyvsp[-1].ttype, yyvsp[0].ttype); ;
+ break;}
+case 738:
+#line 3751 "parse.y"
+{ yyval.ttype = ansi_opname[ERROR_MARK]; ;
+ break;}
+}
+ /* the action file gets copied in in place of this dollarsign */
+#line 480 "/usr/local/lib/bison.simple"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+ yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+ *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+ yylsp++;
+ if (yylen == 0)
+ {
+ yylsp->first_line = yylloc.first_line;
+ yylsp->first_column = yylloc.first_column;
+ yylsp->last_line = (yylsp-1)->last_line;
+ yylsp->last_column = (yylsp-1)->last_column;
+ yylsp->text = 0;
+ }
+ else
+ {
+ yylsp->last_line = (yylsp+yylen-1)->last_line;
+ yylsp->last_column = (yylsp+yylen-1)->last_column;
+ }
+#endif
+
+ /* Now "shift" the result of the reduction.
+ Determine what state that goes to,
+ based on the state we popped back to
+ and the rule number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+ if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTBASE];
+
+ goto yynewstate;
+
+yyerrlab: /* here on detecting error */
+
+ if (! yyerrstatus)
+ /* If not already recovering from an error, report this error. */
+ {
+ ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (yyn > YYFLAG && yyn < YYLAST)
+ {
+ int size = 0;
+ char *msg;
+ int x, count;
+
+ count = 0;
+ /* Start X at -yyn if nec to avoid negative indexes in yycheck. */
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ size += strlen(yytname[x]) + 15, count++;
+ msg = (char *) malloc(size + 15);
+ if (msg != 0)
+ {
+ strcpy(msg, "parse error");
+
+ if (count < 5)
+ {
+ count = 0;
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ {
+ strcat(msg, count == 0 ? ", expecting `" : " or `");
+ strcat(msg, yytname[x]);
+ strcat(msg, "'");
+ count++;
+ }
+ }
+ yyerror(msg);
+ free(msg);
+ }
+ else
+ yyerror ("parse error; also virtual memory exceeded");
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror("parse error");
+ }
+
+ goto yyerrlab1;
+yyerrlab1: /* here on error raised explicitly by an action */
+
+ if (yyerrstatus == 3)
+ {
+ /* if just tried and failed to reuse lookahead token after an error, discard it. */
+
+ /* return failure if at end of input */
+ if (yychar == YYEOF)
+ YYABORT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#endif
+
+ yychar = YYEMPTY;
+ }
+
+ /* Else will try to reuse lookahead token
+ after shifting the error token. */
+
+ yyerrstatus = 3; /* Each real token shifted decrements this */
+
+ goto yyerrhandle;
+
+yyerrdefault: /* current state does not do anything special for the error token. */
+
+#if 0
+ /* This is wrong; only states that explicitly want error tokens
+ should shift them. */
+ yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
+ if (yyn) goto yydefault;
+#endif
+
+yyerrpop: /* pop the current state because it cannot handle the error token */
+
+ if (yyssp == yyss) YYABORT;
+ yyvsp--;
+ yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+ yylsp--;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "Error: state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+yyerrhandle:
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yyerrdefault;
+
+ yyn += YYTERROR;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+ goto yyerrdefault;
+
+ yyn = yytable[yyn];
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrpop;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrpop;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting error token, ");
+#endif
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ yystate = yyn;
+ goto yynewstate;
+}
+#line 3754 "parse.y"
+
diff --git a/gnu/usr.bin/cc/cc1plus/parse.h b/gnu/usr.bin/cc/cc1plus/parse.h
new file mode 100644
index 0000000..18ef379
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/parse.h
@@ -0,0 +1,84 @@
+typedef union {long itype; tree ttype; char *strtype; enum tree_code code; } YYSTYPE;
+#define IDENTIFIER 258
+#define TYPENAME 259
+#define SCSPEC 260
+#define TYPESPEC 261
+#define TYPE_QUAL 262
+#define CONSTANT 263
+#define STRING 264
+#define ELLIPSIS 265
+#define SIZEOF 266
+#define ENUM 267
+#define IF 268
+#define ELSE 269
+#define WHILE 270
+#define DO 271
+#define FOR 272
+#define SWITCH 273
+#define CASE 274
+#define DEFAULT 275
+#define BREAK 276
+#define CONTINUE 277
+#define RETURN 278
+#define GOTO 279
+#define ASM_KEYWORD 280
+#define GCC_ASM_KEYWORD 281
+#define TYPEOF 282
+#define ALIGNOF 283
+#define HEADOF 284
+#define CLASSOF 285
+#define SIGOF 286
+#define ATTRIBUTE 287
+#define EXTENSION 288
+#define LABEL 289
+#define AGGR 290
+#define VISSPEC 291
+#define DELETE 292
+#define NEW 293
+#define OVERLOAD 294
+#define THIS 295
+#define OPERATOR 296
+#define CXX_TRUE 297
+#define CXX_FALSE 298
+#define LEFT_RIGHT 299
+#define TEMPLATE 300
+#define TYPEID 301
+#define DYNAMIC_CAST 302
+#define STATIC_CAST 303
+#define REINTERPRET_CAST 304
+#define CONST_CAST 305
+#define SCOPE 306
+#define EMPTY 307
+#define PTYPENAME 308
+#define ASSIGN 309
+#define OROR 310
+#define ANDAND 311
+#define MIN_MAX 312
+#define EQCOMPARE 313
+#define ARITHCOMPARE 314
+#define LSHIFT 315
+#define RSHIFT 316
+#define POINTSAT_STAR 317
+#define DOT_STAR 318
+#define UNARY 319
+#define PLUSPLUS 320
+#define MINUSMINUS 321
+#define HYPERUNARY 322
+#define PAREN_STAR_PAREN 323
+#define POINTSAT 324
+#define TRY 325
+#define CATCH 326
+#define THROW 327
+#define TYPENAME_ELLIPSIS 328
+#define PRE_PARSED_FUNCTION_DECL 329
+#define EXTERN_LANG_STRING 330
+#define ALL 331
+#define PRE_PARSED_CLASS_DECL 332
+#define TYPENAME_DEFN 333
+#define IDENTIFIER_DEFN 334
+#define PTYPENAME_DEFN 335
+#define END_OF_SAVED_INPUT 336
+
+
+extern YYSTYPE yylval;
+#define YYEMPTY -2
diff --git a/gnu/usr.bin/cc/cc1plus/pt.c b/gnu/usr.bin/cc/cc1plus/pt.c
new file mode 100644
index 0000000..c808cf4
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/pt.c
@@ -0,0 +1,2465 @@
+/* Handle parameterized types (templates) for GNU C++.
+ Copyright (C) 1992, 1993 Free Software Foundation, Inc.
+ Written by Ken Raeburn (raeburn@cygnus.com) while at Watchmaker Computing.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Known bugs or deficiencies include:
+ * templates for class static data don't work (methods only)
+ * duplicated method templates can crash the compiler
+ * interface/impl data is taken from file defining the template
+ * all methods must be provided in header files; can't use a source
+ file that contains only the method templates and "just win"
+ * method templates must be seen before the expansion of the
+ class template is done
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include "obstack.h"
+
+#include "tree.h"
+#include "flags.h"
+#include "cp-tree.h"
+#include "decl.h"
+#include "parse.h"
+#include "lex.h"
+
+extern struct obstack permanent_obstack;
+extern tree grokdeclarator ();
+
+extern int lineno;
+extern char *input_filename;
+struct pending_inline *pending_template_expansions;
+
+int processing_template_decl;
+int processing_template_defn;
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+static int unify ();
+static void add_pending_template ();
+
+void overload_template_name (), pop_template_decls ();
+
+/* We've got a template header coming up; set obstacks up to save the
+ nodes created permanently. (There might be cases with nested templates
+ where we don't have to do this, but they aren't implemented, and it
+ probably wouldn't be worth the effort.) */
+void
+begin_template_parm_list ()
+{
+ pushlevel (0);
+ push_obstacks (&permanent_obstack, &permanent_obstack);
+ pushlevel (0);
+}
+
+/* Process information from new template parameter NEXT and append it to the
+ LIST being built. The rules for use of a template parameter type name
+ by later parameters are not well-defined for us just yet. However, the
+ only way to avoid having to parse expressions of unknown complexity (and
+ with tokens of unknown types) is to disallow it completely. So for now,
+ that is what is assumed. */
+tree
+process_template_parm (list, next)
+ tree list, next;
+{
+ tree parm;
+ tree decl = 0;
+ int is_type;
+ parm = next;
+ my_friendly_assert (TREE_CODE (parm) == TREE_LIST, 259);
+ is_type = TREE_CODE (TREE_PURPOSE (parm)) == IDENTIFIER_NODE;
+ if (!is_type)
+ {
+ tree tinfo = 0;
+ parm = TREE_PURPOSE (parm);
+ my_friendly_assert (TREE_CODE (parm) == TREE_LIST, 260);
+ parm = TREE_VALUE (parm);
+ /* is a const-param */
+ parm = grokdeclarator (TREE_VALUE (next), TREE_PURPOSE (next),
+ PARM, 0, NULL_TREE);
+ /* A template parameter is not modifiable. */
+ TREE_READONLY (parm) = 1;
+ if (TREE_CODE (TREE_TYPE (parm)) == RECORD_TYPE
+ || TREE_CODE (TREE_TYPE (parm)) == UNION_TYPE)
+ {
+ sorry ("aggregate template parameter types");
+ TREE_TYPE (parm) = void_type_node;
+ }
+ tinfo = make_node (TEMPLATE_CONST_PARM);
+ my_friendly_assert (TREE_PERMANENT (tinfo), 260.5);
+ if (TREE_PERMANENT (parm) == 0)
+ {
+ parm = copy_node (parm);
+ TREE_PERMANENT (parm) = 1;
+ }
+ TREE_TYPE (tinfo) = TREE_TYPE (parm);
+ decl = build_decl (CONST_DECL, DECL_NAME (parm), TREE_TYPE (parm));
+ DECL_INITIAL (decl) = tinfo;
+ DECL_INITIAL (parm) = tinfo;
+ }
+ else
+ {
+ tree t = make_node (TEMPLATE_TYPE_PARM);
+ decl = build_decl (TYPE_DECL, TREE_PURPOSE (parm), t);
+ TYPE_NAME (t) = decl;
+ TREE_VALUE (parm) = t;
+ }
+ pushdecl (decl);
+ return chainon (list, parm);
+}
+
+/* The end of a template parameter list has been reached. Process the
+ tree list into a parameter vector, converting each parameter into a more
+ useful form. Type parameters are saved as IDENTIFIER_NODEs, and others
+ as PARM_DECLs. */
+
+tree
+end_template_parm_list (parms)
+ tree parms;
+{
+ int nparms = 0;
+ tree saved_parmlist;
+ tree parm;
+ for (parm = parms; parm; parm = TREE_CHAIN (parm))
+ nparms++;
+ saved_parmlist = make_tree_vec (nparms);
+
+ for (parm = parms, nparms = 0; parm; parm = TREE_CHAIN (parm), nparms++)
+ {
+ tree p = parm;
+ if (TREE_CODE (p) == TREE_LIST)
+ {
+ tree t = TREE_VALUE (p);
+ TREE_VALUE (p) = NULL_TREE;
+ p = TREE_PURPOSE (p);
+ my_friendly_assert (TREE_CODE (p) == IDENTIFIER_NODE, 261);
+ TEMPLATE_TYPE_SET_INFO (t, saved_parmlist, nparms);
+ }
+ else
+ {
+ tree tinfo = DECL_INITIAL (p);
+ DECL_INITIAL (p) = NULL_TREE;
+ TEMPLATE_CONST_SET_INFO (tinfo, saved_parmlist, nparms);
+ }
+ TREE_VEC_ELT (saved_parmlist, nparms) = p;
+ }
+ set_current_level_tags_transparency (1);
+ processing_template_decl++;
+ return saved_parmlist;
+}
+
+/* end_template_decl is called after a template declaration is seen.
+ D1 is template header; D2 is class_head_sans_basetype or a
+ TEMPLATE_DECL with its DECL_RESULT field set. */
+void
+end_template_decl (d1, d2, is_class, defn)
+ tree d1, d2, is_class;
+ int defn;
+{
+ tree decl;
+ struct template_info *tmpl;
+
+ tmpl = (struct template_info *) obstack_alloc (&permanent_obstack,
+ sizeof (struct template_info));
+ tmpl->text = 0;
+ tmpl->length = 0;
+ tmpl->aggr = is_class;
+
+ /* cloned from reinit_parse_for_template */
+ tmpl->filename = input_filename;
+ tmpl->lineno = lineno;
+ tmpl->parm_vec = d1; /* [eichin:19911015.2306EST] */
+
+ if (d2 == NULL_TREE || d2 == error_mark_node)
+ {
+ decl = 0;
+ goto lose;
+ }
+
+ if (is_class)
+ {
+ decl = build_lang_decl (TEMPLATE_DECL, d2, NULL_TREE);
+ GNU_xref_decl (current_function_decl, decl);
+ }
+ else
+ {
+ if (TREE_CODE (d2) == TEMPLATE_DECL)
+ decl = d2;
+ else
+ {
+ /* Class destructor templates and operator templates are
+ slipping past as non-template nodes. Process them here, since
+ I haven't figured out where to catch them earlier. I could
+ go do that, but it's a choice between getting that done and
+ staying only N months behind schedule. Sorry.... */
+ enum tree_code code;
+ my_friendly_assert (TREE_CODE (d2) == CALL_EXPR, 263);
+ code = TREE_CODE (TREE_OPERAND (d2, 0));
+ my_friendly_assert (code == BIT_NOT_EXPR
+ || code == OP_IDENTIFIER
+ || code == SCOPE_REF, 264);
+ d2 = grokdeclarator (d2, NULL_TREE, MEMFUNCDEF, 0, NULL_TREE);
+ decl = build_lang_decl (TEMPLATE_DECL, DECL_NAME (d2),
+ TREE_TYPE (d2));
+ DECL_TEMPLATE_RESULT (decl) = d2;
+ DECL_CONTEXT (decl) = DECL_CONTEXT (d2);
+ DECL_CLASS_CONTEXT (decl) = DECL_CLASS_CONTEXT (d2);
+ DECL_NAME (decl) = DECL_NAME (d2);
+ TREE_TYPE (decl) = TREE_TYPE (d2);
+ if (interface_unknown && flag_external_templates && ! DECL_IN_SYSTEM_HEADER (decl))
+ warn_if_unknown_interface ();
+ TREE_PUBLIC (decl) = TREE_PUBLIC (d2) = flag_external_templates && !interface_unknown;
+ DECL_EXTERNAL (decl) = (DECL_EXTERNAL (d2)
+ && !(DECL_CLASS_CONTEXT (d2)
+ && !DECL_THIS_EXTERN (d2)));
+ }
+
+ /* All routines creating TEMPLATE_DECL nodes should now be using
+ build_lang_decl, which will have set this up already. */
+ my_friendly_assert (DECL_LANG_SPECIFIC (decl) != 0, 265);
+
+ /* @@ Somewhere, permanent allocation isn't being used. */
+ if (! DECL_TEMPLATE_IS_CLASS (decl)
+ && TREE_CODE (DECL_TEMPLATE_RESULT (decl)) == FUNCTION_DECL)
+ {
+ tree result = DECL_TEMPLATE_RESULT (decl);
+ /* Will do nothing if allocation was already permanent. */
+ DECL_ARGUMENTS (result) = copy_to_permanent (DECL_ARGUMENTS (result));
+ }
+
+ /* If this is for a method, there's an extra binding level here. */
+ if (DECL_CONTEXT (DECL_TEMPLATE_RESULT (decl)) != NULL_TREE)
+ {
+ /* @@ Find out where this should be getting set! */
+ tree r = DECL_TEMPLATE_RESULT (decl);
+ if (DECL_LANG_SPECIFIC (r) && DECL_CLASS_CONTEXT (r) == NULL_TREE)
+ DECL_CLASS_CONTEXT (r) = DECL_CONTEXT (r);
+ }
+ }
+ DECL_TEMPLATE_INFO (decl) = tmpl;
+ DECL_TEMPLATE_PARMS (decl) = d1;
+
+ /* So that duplicate_decls can do the right thing. */
+ if (defn)
+ DECL_INITIAL (decl) = error_mark_node;
+
+ /* If context of decl is non-null (i.e., method template), add it
+ to the appropriate class template, and pop the binding levels. */
+ if (! is_class && DECL_CONTEXT (DECL_TEMPLATE_RESULT (decl)) != NULL_TREE)
+ {
+ tree ctx = DECL_CONTEXT (DECL_TEMPLATE_RESULT (decl));
+ tree tmpl, t;
+ my_friendly_assert (TREE_CODE (ctx) == UNINSTANTIATED_P_TYPE, 266);
+ tmpl = UPT_TEMPLATE (ctx);
+ for (t = DECL_TEMPLATE_MEMBERS (tmpl); t; t = TREE_CHAIN (t))
+ if (TREE_PURPOSE (t) == DECL_NAME (decl)
+ && duplicate_decls (decl, TREE_VALUE (t)))
+ goto already_there;
+ DECL_TEMPLATE_MEMBERS (tmpl) =
+ perm_tree_cons (DECL_NAME (decl), decl, DECL_TEMPLATE_MEMBERS (tmpl));
+ already_there:
+ poplevel (0, 0, 0);
+ poplevel (0, 0, 0);
+ }
+ /* Otherwise, go back to top level first, and push the template decl
+ again there. */
+ else
+ {
+ poplevel (0, 0, 0);
+ poplevel (0, 0, 0);
+ pushdecl (decl);
+ }
+ lose:
+#if 0 /* It happens sometimes, with syntactic or semantic errors.
+
+ One specific case:
+ template <class A, int X, int Y> class Foo { ... };
+ template <class A, int X, int y> Foo<X,Y>::method (Foo& x) { ... }
+ Note the missing "A" in the class containing "method". */
+ my_friendly_assert (global_bindings_p (), 267);
+#else
+ while (! global_bindings_p ())
+ poplevel (0, 0, 0);
+#endif
+ pop_obstacks ();
+ processing_template_decl--;
+ (void) get_pending_sizes ();
+}
+
+/* If TYPE contains a template parm type, then substitute that type
+ with its actual type that is found in TVEC. */
+static void
+grok_template_type (tvec, type)
+ tree tvec;
+ tree* type;
+{
+ switch (TREE_CODE (*type))
+ {
+ case TEMPLATE_TYPE_PARM:
+ if (*type != TYPE_MAIN_VARIANT (*type))
+ {
+ /* we are here for cases like const T* etc. */
+ grok_template_type (tvec, &TYPE_MAIN_VARIANT (*type));
+ *type = c_build_type_variant (TYPE_MAIN_VARIANT (*type),
+ TYPE_READONLY (*type),
+ TYPE_VOLATILE (*type));
+ }
+ else
+ *type = TREE_VEC_ELT (tvec, TEMPLATE_TYPE_IDX (*type));
+ return;
+ case POINTER_TYPE:
+ case REFERENCE_TYPE:
+ grok_template_type (tvec, &TREE_TYPE (*type));
+ return;
+ case FUNCTION_TYPE:
+ {
+ tree p;
+
+ /* take care of function's return type first */
+ grok_template_type (tvec, &TREE_TYPE (*type));
+
+ /* take care of function's arguments */
+ for (p = TYPE_ARG_TYPES (*type); p; p = TREE_CHAIN (p))
+ grok_template_type (tvec, &TREE_VALUE (p));
+ return;
+ }
+ default:
+ break;
+ }
+ return;
+}
+
+/* Convert all template arguments to their appropriate types, and return
+ a vector containing the resulting values. If any error occurs, return
+ error_mark_node. */
+static tree
+coerce_template_parms (parms, arglist, in_decl)
+ tree parms, arglist;
+ tree in_decl;
+{
+ int nparms, i, lost = 0;
+ tree vec;
+
+ if (TREE_CODE (arglist) == TREE_VEC)
+ nparms = TREE_VEC_LENGTH (arglist);
+ else
+ nparms = list_length (arglist);
+ if (nparms != TREE_VEC_LENGTH (parms))
+ {
+ error ("incorrect number of parameters (%d, should be %d)",
+ nparms, TREE_VEC_LENGTH (parms));
+ if (in_decl)
+ cp_error_at ("in template expansion for decl `%D'", in_decl);
+ return error_mark_node;
+ }
+
+ if (TREE_CODE (arglist) == TREE_VEC)
+ vec = copy_node (arglist);
+ else
+ {
+ vec = make_tree_vec (nparms);
+ for (i = 0; i < nparms; i++)
+ {
+ tree arg = arglist;
+ arglist = TREE_CHAIN (arglist);
+ if (arg == error_mark_node)
+ lost++;
+ else
+ arg = TREE_VALUE (arg);
+ TREE_VEC_ELT (vec, i) = arg;
+ }
+ }
+ for (i = 0; i < nparms; i++)
+ {
+ tree arg = TREE_VEC_ELT (vec, i);
+ tree parm = TREE_VEC_ELT (parms, i);
+ tree val = 0;
+ int is_type, requires_type;
+
+ is_type = TREE_CODE_CLASS (TREE_CODE (arg)) == 't';
+ requires_type = TREE_CODE (parm) == IDENTIFIER_NODE;
+ if (is_type != requires_type)
+ {
+ if (in_decl)
+ cp_error_at ("type/value mismatch in template parameter list for `%D'", in_decl);
+ lost++;
+ TREE_VEC_ELT (vec, i) = error_mark_node;
+ continue;
+ }
+ if (is_type)
+ val = groktypename (arg);
+ else if (TREE_CODE (arg) == STRING_CST)
+ {
+ cp_error ("string literal %E is not a valid template argument", arg);
+ error ("because it is the address of an object with static linkage");
+ val = error_mark_node;
+ }
+ else
+ {
+ grok_template_type (vec, &TREE_TYPE (parm));
+ val = digest_init (TREE_TYPE (parm), arg, (tree *) 0);
+
+ if (val == error_mark_node)
+ ;
+
+ /* 14.2: Other template-arguments must be constant-expressions,
+ addresses of objects or functions with external linkage, or of
+ static class members. */
+ else if (!TREE_CONSTANT (val))
+ {
+ cp_error ("non-const `%E' cannot be used as template argument",
+ arg);
+ val = error_mark_node;
+ }
+ else if (TREE_CODE (val) == ADDR_EXPR)
+ {
+ tree a = TREE_OPERAND (val, 0);
+ if ((TREE_CODE (a) == VAR_DECL
+ || TREE_CODE (a) == FUNCTION_DECL)
+ && !TREE_PUBLIC (a))
+ {
+ cp_error ("address of non-extern `%E' cannot be used as template argument", a);
+ val = error_mark_node;
+ }
+ }
+ }
+
+ if (val == error_mark_node)
+ lost++;
+
+ TREE_VEC_ELT (vec, i) = val;
+ }
+ if (lost)
+ return error_mark_node;
+ return vec;
+}
+
+/* Given class template name and parameter list, produce a user-friendly name
+ for the instantiation. */
+static char *
+mangle_class_name_for_template (name, parms, arglist)
+ char *name;
+ tree parms, arglist;
+{
+ static struct obstack scratch_obstack;
+ static char *scratch_firstobj;
+ int i, nparms;
+
+ if (!scratch_firstobj)
+ {
+ gcc_obstack_init (&scratch_obstack);
+ scratch_firstobj = obstack_alloc (&scratch_obstack, 1);
+ }
+ else
+ obstack_free (&scratch_obstack, scratch_firstobj);
+
+#if 0
+#define buflen sizeof(buf)
+#define check if (bufp >= buf+buflen-1) goto too_long
+#define ccat(c) *bufp++=(c); check
+#define advance bufp+=strlen(bufp); check
+#define cat(s) strncpy(bufp, s, buf+buflen-bufp-1); advance
+#else
+#define check
+#define ccat(c) obstack_1grow (&scratch_obstack, (c));
+#define advance
+#define cat(s) obstack_grow (&scratch_obstack, (s), strlen (s))
+#endif
+
+ cat (name);
+ ccat ('<');
+ nparms = TREE_VEC_LENGTH (parms);
+ my_friendly_assert (nparms == TREE_VEC_LENGTH (arglist), 268);
+ for (i = 0; i < nparms; i++)
+ {
+ tree parm = TREE_VEC_ELT (parms, i), arg = TREE_VEC_ELT (arglist, i);
+
+ if (i)
+ ccat (',');
+
+ if (TREE_CODE (parm) == IDENTIFIER_NODE)
+ {
+ cat (type_as_string (arg, 0));
+ continue;
+ }
+ else
+ my_friendly_assert (TREE_CODE (parm) == PARM_DECL, 269);
+
+ if (TREE_CODE (arg) == TREE_LIST)
+ {
+ /* New list cell was built because old chain link was in
+ use. */
+ my_friendly_assert (TREE_PURPOSE (arg) == NULL_TREE, 270);
+ arg = TREE_VALUE (arg);
+ }
+ /* No need to check arglist against parmlist here; we did that
+ in coerce_template_parms, called from lookup_template_class. */
+ cat (expr_as_string (arg, 0));
+ }
+ {
+ char *bufp = obstack_next_free (&scratch_obstack);
+ int offset = 0;
+ while (bufp[offset - 1] == ' ')
+ offset--;
+ obstack_blank_fast (&scratch_obstack, offset);
+
+ /* B<C<char> >, not B<C<char>> */
+ if (bufp[offset - 1] == '>')
+ ccat (' ');
+ }
+ ccat ('>');
+ ccat ('\0');
+ return (char *) obstack_base (&scratch_obstack);
+
+#if 0
+ too_long:
+#endif
+ fatal ("out of (preallocated) string space creating template instantiation name");
+ /* NOTREACHED */
+ return NULL;
+}
+
+/* Given an IDENTIFIER_NODE (type TEMPLATE_DECL) and a chain of
+ parameters, find the desired type.
+
+ D1 is the PTYPENAME terminal, and ARGLIST is the list of arguments.
+ Since ARGLIST is build on the decl_obstack, we must copy it here
+ to keep it from being reclaimed when the decl storage is reclaimed.
+
+ IN_DECL, if non-NULL, is the template declaration we are trying to
+ instantiate. */
+tree
+lookup_template_class (d1, arglist, in_decl)
+ tree d1, arglist;
+ tree in_decl;
+{
+ tree template, parmlist;
+ char *mangled_name;
+ tree id;
+
+ my_friendly_assert (TREE_CODE (d1) == IDENTIFIER_NODE, 272);
+ template = IDENTIFIER_GLOBAL_VALUE (d1); /* XXX */
+ if (! template)
+ template = IDENTIFIER_CLASS_VALUE (d1);
+ /* With something like `template <class T> class X class X { ... };'
+ we could end up with D1 having nothing but an IDENTIFIER_LOCAL_VALUE.
+ We don't want to do that, but we have to deal with the situation, so
+ let's give them some syntax errors to chew on instead of a crash. */
+ if (! template)
+ return error_mark_node;
+ if (TREE_CODE (template) != TEMPLATE_DECL)
+ {
+ cp_error ("non-template type `%T' used as a template", d1);
+ if (in_decl)
+ cp_error_at ("for template declaration `%D'", in_decl);
+ return error_mark_node;
+ }
+ parmlist = DECL_TEMPLATE_PARMS (template);
+
+ arglist = coerce_template_parms (parmlist, arglist, in_decl);
+ if (arglist == error_mark_node)
+ return error_mark_node;
+ if (uses_template_parms (arglist))
+ {
+ tree t = make_lang_type (UNINSTANTIATED_P_TYPE);
+ tree d;
+ id = make_anon_name ();
+ d = build_decl (TYPE_DECL, id, t);
+ TYPE_NAME (t) = d;
+ TYPE_VALUES (t) = build_tree_list (template, arglist);
+ pushdecl_top_level (d);
+ }
+ else
+ {
+ mangled_name = mangle_class_name_for_template (IDENTIFIER_POINTER (d1),
+ parmlist, arglist);
+ id = get_identifier (mangled_name);
+ }
+ if (!IDENTIFIER_TEMPLATE (id))
+ {
+ arglist = copy_to_permanent (arglist);
+ IDENTIFIER_TEMPLATE (id) = perm_tree_cons (template, arglist, NULL_TREE);
+ }
+ return id;
+}
+
+void
+push_template_decls (parmlist, arglist, class_level)
+ tree parmlist, arglist;
+ int class_level;
+{
+ int i, nparms;
+
+ /* Don't want to push values into global context. */
+ if (!class_level)
+ {
+ pushlevel (1);
+ declare_pseudo_global_level ();
+ }
+
+ nparms = TREE_VEC_LENGTH (parmlist);
+
+ for (i = 0; i < nparms; i++)
+ {
+ int requires_type, is_type;
+ tree parm = TREE_VEC_ELT (parmlist, i);
+ tree arg = TREE_VEC_ELT (arglist, i);
+ tree decl = 0;
+
+ requires_type = TREE_CODE (parm) == IDENTIFIER_NODE;
+ is_type = TREE_CODE_CLASS (TREE_CODE (arg)) == 't';
+ if (is_type)
+ {
+ /* add typename to namespace */
+ if (!requires_type)
+ {
+ error ("template use error: type provided where value needed");
+ continue;
+ }
+ decl = arg;
+ my_friendly_assert (TREE_CODE_CLASS (TREE_CODE (decl)) == 't', 273);
+ decl = build_decl (TYPE_DECL, parm, decl);
+ }
+ else
+ {
+ /* add const decl to namespace */
+ tree val;
+ if (requires_type)
+ {
+ error ("template use error: value provided where type needed");
+ continue;
+ }
+ val = digest_init (TREE_TYPE (parm), arg, (tree *) 0);
+ if (val != error_mark_node)
+ {
+ decl = build_decl (VAR_DECL, DECL_NAME (parm), TREE_TYPE (parm));
+ DECL_INITIAL (decl) = val;
+ TREE_READONLY (decl) = 1;
+ }
+ }
+ if (decl != 0)
+ {
+ layout_decl (decl, 0);
+ if (class_level)
+ pushdecl_class_level (decl);
+ else
+ pushdecl (decl);
+ }
+ }
+}
+
+void
+pop_template_decls (parmlist, arglist, class_level)
+ tree parmlist, arglist;
+ int class_level;
+{
+ if (!class_level)
+ poplevel (0, 0, 0);
+}
+
+/* Should be defined in parse.h. */
+extern int yychar;
+
+int
+uses_template_parms (t)
+ tree t;
+{
+ if (!t)
+ return 0;
+ switch (TREE_CODE (t))
+ {
+ case INDIRECT_REF:
+ case COMPONENT_REF:
+ /* We assume that the object must be instantiated in order to build
+ the COMPONENT_REF, so we test only whether the type of the
+ COMPONENT_REF uses template parms. */
+ return uses_template_parms (TREE_TYPE (t));
+
+ case IDENTIFIER_NODE:
+ if (!IDENTIFIER_TEMPLATE (t))
+ return 0;
+ return uses_template_parms (TREE_VALUE (IDENTIFIER_TEMPLATE (t)));
+
+ /* aggregates of tree nodes */
+ case TREE_VEC:
+ {
+ int i = TREE_VEC_LENGTH (t);
+ while (i--)
+ if (uses_template_parms (TREE_VEC_ELT (t, i)))
+ return 1;
+ return 0;
+ }
+ case TREE_LIST:
+ if (uses_template_parms (TREE_PURPOSE (t))
+ || uses_template_parms (TREE_VALUE (t)))
+ return 1;
+ return uses_template_parms (TREE_CHAIN (t));
+
+ /* constructed type nodes */
+ case POINTER_TYPE:
+ case REFERENCE_TYPE:
+ return uses_template_parms (TREE_TYPE (t));
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ if (!TYPE_NAME (t))
+ return 0;
+ if (!TYPE_IDENTIFIER (t))
+ return 0;
+ return uses_template_parms (TYPE_IDENTIFIER (t));
+ case FUNCTION_TYPE:
+ if (uses_template_parms (TYPE_ARG_TYPES (t)))
+ return 1;
+ return uses_template_parms (TREE_TYPE (t));
+ case ARRAY_TYPE:
+ if (uses_template_parms (TYPE_DOMAIN (t)))
+ return 1;
+ return uses_template_parms (TREE_TYPE (t));
+ case OFFSET_TYPE:
+ if (uses_template_parms (TYPE_OFFSET_BASETYPE (t)))
+ return 1;
+ return uses_template_parms (TREE_TYPE (t));
+ case METHOD_TYPE:
+ if (uses_template_parms (TYPE_OFFSET_BASETYPE (t)))
+ return 1;
+ if (uses_template_parms (TYPE_ARG_TYPES (t)))
+ return 1;
+ return uses_template_parms (TREE_TYPE (t));
+
+ /* decl nodes */
+ case TYPE_DECL:
+ return uses_template_parms (DECL_NAME (t));
+ case FUNCTION_DECL:
+ if (uses_template_parms (TREE_TYPE (t)))
+ return 1;
+ /* fall through */
+ case VAR_DECL:
+ case PARM_DECL:
+ /* ??? What about FIELD_DECLs? */
+ /* The type of a decl can't use template parms if the name of the
+ variable doesn't, because it's impossible to resolve them. So
+ ignore the type field for now. */
+ if (DECL_CONTEXT (t) && uses_template_parms (DECL_CONTEXT (t)))
+ return 1;
+ if (uses_template_parms (TREE_TYPE (t)))
+ {
+ error ("template parms used where they can't be resolved");
+ }
+ return 0;
+
+ case CALL_EXPR:
+ return uses_template_parms (TREE_TYPE (t));
+ case ADDR_EXPR:
+ return uses_template_parms (TREE_OPERAND (t, 0));
+
+ /* template parm nodes */
+ case TEMPLATE_TYPE_PARM:
+ case TEMPLATE_CONST_PARM:
+ return 1;
+
+ /* simple type nodes */
+ case INTEGER_TYPE:
+ if (uses_template_parms (TYPE_MIN_VALUE (t)))
+ return 1;
+ return uses_template_parms (TYPE_MAX_VALUE (t));
+
+ case REAL_TYPE:
+ case VOID_TYPE:
+ case ENUMERAL_TYPE:
+ case BOOLEAN_TYPE:
+ return 0;
+
+ /* constants */
+ case INTEGER_CST:
+ case REAL_CST:
+ case STRING_CST:
+ return 0;
+
+ case ERROR_MARK:
+ /* Non-error_mark_node ERROR_MARKs are bad things. */
+ my_friendly_assert (t == error_mark_node, 274);
+ /* NOTREACHED */
+ return 0;
+
+ case UNINSTANTIATED_P_TYPE:
+ return 1;
+
+ default:
+ switch (TREE_CODE_CLASS (TREE_CODE (t)))
+ {
+ case '1':
+ case '2':
+ case '3':
+ case '<':
+ {
+ int i;
+ for (i = tree_code_length[(int) TREE_CODE (t)]; --i >= 0;)
+ if (uses_template_parms (TREE_OPERAND (t, i)))
+ return 1;
+ return 0;
+ }
+ default:
+ break;
+ }
+ sorry ("testing %s for template parms",
+ tree_code_name [(int) TREE_CODE (t)]);
+ my_friendly_abort (82);
+ /* NOTREACHED */
+ return 0;
+ }
+}
+
+void
+instantiate_member_templates (classname)
+ tree classname;
+{
+ tree t;
+ tree id = classname;
+ tree members = DECL_TEMPLATE_MEMBERS (TREE_PURPOSE (IDENTIFIER_TEMPLATE (id)));
+
+ for (t = members; t; t = TREE_CHAIN (t))
+ {
+ tree parmvec, type, classparms, tdecl, t2;
+ int nparms, xxx = 0, i;
+
+ my_friendly_assert (TREE_VALUE (t) != NULL_TREE, 275);
+ my_friendly_assert (TREE_CODE (TREE_VALUE (t)) == TEMPLATE_DECL, 276);
+ /* @@ Should verify that class parm list is a list of
+ distinct template parameters, and covers all the template
+ parameters. */
+ tdecl = TREE_VALUE (t);
+ type = DECL_CONTEXT (DECL_TEMPLATE_RESULT (tdecl));
+ classparms = UPT_PARMS (type);
+ nparms = TREE_VEC_LENGTH (classparms);
+ parmvec = make_tree_vec (nparms);
+ for (i = 0; i < nparms; i++)
+ TREE_VEC_ELT (parmvec, i) = NULL_TREE;
+ switch (unify (DECL_TEMPLATE_PARMS (tdecl),
+ &TREE_VEC_ELT (parmvec, 0), nparms,
+ type, IDENTIFIER_TYPE_VALUE (classname),
+ &xxx))
+ {
+ case 0:
+ /* Success -- well, no inconsistency, at least. */
+ for (i = 0; i < nparms; i++)
+ if (TREE_VEC_ELT (parmvec, i) == NULL_TREE)
+ goto failure;
+ t2 = instantiate_template (tdecl,
+ &TREE_VEC_ELT (parmvec, 0));
+ type = IDENTIFIER_TYPE_VALUE (id);
+ my_friendly_assert (type != 0, 277);
+ if (CLASSTYPE_INTERFACE_UNKNOWN (type))
+ {
+ DECL_EXTERNAL (t2) = 0;
+ TREE_PUBLIC (t2) = 0;
+ }
+ else
+ {
+ DECL_EXTERNAL (t2) = CLASSTYPE_INTERFACE_ONLY (type);
+ TREE_PUBLIC (t2) = 1;
+ }
+ break;
+ case 1:
+ /* Failure. */
+ failure:
+ cp_error ("type unification error instantiating %T::%D",
+ classname, tdecl);
+ cp_error_at ("for template declaration `%D'", tdecl);
+
+ continue /* loop of members */;
+ default:
+ /* Eek, a bug. */
+ my_friendly_abort (83);
+ }
+ }
+}
+
+struct tinst_level *current_tinst_level = 0;
+struct tinst_level *free_tinst_level = 0;
+
+void
+push_tinst_level (name)
+ tree name;
+{
+ struct tinst_level *new;
+ tree global = IDENTIFIER_GLOBAL_VALUE (name);
+
+ if (free_tinst_level)
+ {
+ new = free_tinst_level;
+ free_tinst_level = new->next;
+ }
+ else
+ new = (struct tinst_level *) xmalloc (sizeof (struct tinst_level));
+
+ new->classname = name;
+ if (global)
+ {
+ new->line = DECL_SOURCE_LINE (global);
+ new->file = DECL_SOURCE_FILE (global);
+ }
+ else
+ {
+ new->line = lineno;
+ new->file = input_filename;
+ }
+ new->next = current_tinst_level;
+ current_tinst_level = new;
+}
+
+void
+pop_tinst_level ()
+{
+ struct tinst_level *old = current_tinst_level;
+
+ current_tinst_level = old->next;
+ old->next = free_tinst_level;
+ free_tinst_level = old;
+}
+
+struct tinst_level *
+tinst_for_decl ()
+{
+ struct tinst_level *p = current_tinst_level;
+
+ if (p)
+ for (; p->next ; p = p->next )
+ ;
+ return p;
+}
+
+tree
+instantiate_class_template (classname, setup_parse)
+ tree classname;
+ int setup_parse;
+{
+ struct template_info *template_info;
+ tree template, t1;
+
+ if (classname == error_mark_node)
+ return error_mark_node;
+
+ my_friendly_assert (TREE_CODE (classname) == IDENTIFIER_NODE, 278);
+ template = IDENTIFIER_TEMPLATE (classname);
+
+ if (IDENTIFIER_HAS_TYPE_VALUE (classname))
+ {
+ tree type = IDENTIFIER_TYPE_VALUE (classname);
+ if (TREE_CODE (type) == UNINSTANTIATED_P_TYPE)
+ return type;
+ if (TYPE_BEING_DEFINED (type)
+ || TYPE_SIZE (type)
+ || CLASSTYPE_USE_TEMPLATE (type) != 0)
+ return type;
+ }
+
+ /* If IDENTIFIER_LOCAL_VALUE is already set on this template classname
+ (it's something like `foo<int>'), that means we're already working on
+ the instantiation for it. Normally, a classname comes in with nothing
+ but its IDENTIFIER_TEMPLATE slot set. If we were to try to instantiate
+ this again, we'd get a redeclaration error. Since we're already working
+ on it, we'll pass back this classname's TYPE_DECL (it's the value of
+ the classname's IDENTIFIER_LOCAL_VALUE). Only do this if we're setting
+ things up for the parser, though---if we're just trying to instantiate
+ it (e.g., via tsubst) we can trip up cuz it may not have an
+ IDENTIFIER_TYPE_VALUE when it will need one. */
+ if (setup_parse && IDENTIFIER_LOCAL_VALUE (classname))
+ return IDENTIFIER_LOCAL_VALUE (classname);
+
+ if (uses_template_parms (classname))
+ {
+ if (!TREE_TYPE (classname))
+ {
+ tree t = make_lang_type (RECORD_TYPE);
+ tree d = build_decl (TYPE_DECL, classname, t);
+ DECL_NAME (d) = classname;
+ TYPE_NAME (t) = d;
+ pushdecl (d);
+ }
+ return NULL_TREE;
+ }
+
+ t1 = TREE_PURPOSE (template);
+ my_friendly_assert (TREE_CODE (t1) == TEMPLATE_DECL, 279);
+
+ /* If a template is declared but not defined, accept it; don't crash.
+ Later uses requiring the definition will be flagged as errors by
+ other code. Thanks to niklas@appli.se for this bug fix. */
+ if (DECL_TEMPLATE_INFO (t1)->text == 0)
+ setup_parse = 0;
+
+ push_to_top_level ();
+ template_info = DECL_TEMPLATE_INFO (t1);
+ if (setup_parse)
+ {
+ push_tinst_level (classname);
+ push_template_decls (DECL_TEMPLATE_PARMS (TREE_PURPOSE (template)),
+ TREE_VALUE (template), 0);
+ set_current_level_tags_transparency (1);
+ feed_input (template_info->text, template_info->length, (struct obstack *)0);
+ lineno = template_info->lineno;
+ input_filename = template_info->filename;
+ /* Get interface/implementation back in sync. */
+ extract_interface_info ();
+ overload_template_name (classname, 0);
+ /* Kludge so that we don't get screwed by our own base classes. */
+ TYPE_BEING_DEFINED (TREE_TYPE (classname)) = 1;
+ yychar = PRE_PARSED_CLASS_DECL;
+ yylval.ttype = classname;
+ processing_template_defn++;
+ if (!flag_external_templates)
+ interface_unknown++;
+ }
+ else
+ {
+ tree t, decl, id, tmpl;
+
+ id = classname;
+ tmpl = TREE_PURPOSE (IDENTIFIER_TEMPLATE (id));
+ t = xref_tag (DECL_TEMPLATE_INFO (tmpl)->aggr, id, NULL_TREE, 0);
+ my_friendly_assert (TREE_CODE (t) == RECORD_TYPE
+ || TREE_CODE (t) == UNION_TYPE, 280);
+
+ /* Now, put a copy of the decl in global scope, to avoid
+ * recursive expansion. */
+ decl = IDENTIFIER_LOCAL_VALUE (id);
+ if (!decl)
+ decl = IDENTIFIER_CLASS_VALUE (id);
+ if (decl)
+ {
+ my_friendly_assert (TREE_CODE (decl) == TYPE_DECL, 281);
+ /* We'd better make sure we're on the permanent obstack or else
+ * we'll get a "friendly" abort 124 in pushdecl. Perhaps a
+ * copy_to_permanent would be sufficient here, but then a
+ * sharing problem might occur. I don't know -- niklas@appli.se */
+ push_obstacks (&permanent_obstack, &permanent_obstack);
+ pushdecl_top_level (copy_node (decl));
+ pop_obstacks ();
+ }
+ pop_from_top_level ();
+ }
+
+ return NULL_TREE;
+}
+
+static int
+list_eq (t1, t2)
+ tree t1, t2;
+{
+ if (t1 == NULL_TREE)
+ return t2 == NULL_TREE;
+ if (t2 == NULL_TREE)
+ return 0;
+ /* Don't care if one declares its arg const and the other doesn't -- the
+ main variant of the arg type is all that matters. */
+ if (TYPE_MAIN_VARIANT (TREE_VALUE (t1))
+ != TYPE_MAIN_VARIANT (TREE_VALUE (t2)))
+ return 0;
+ return list_eq (TREE_CHAIN (t1), TREE_CHAIN (t2));
+}
+
+static tree
+lookup_nested_type_by_name (ctype, name)
+ tree ctype, name;
+{
+ tree t;
+
+ t = TREE_VALUE(CLASSTYPE_TAGS(ctype));
+ while (t)
+ {
+ if (strcmp(IDENTIFIER_POINTER(name), IDENTIFIER_POINTER(TYPE_IDENTIFIER(t)))
+ == 0)
+ return t;
+ else
+ t = TREE_CHAIN(t);
+ }
+ return NULL_TREE;
+}
+
+static tree
+search_nested_type_in_tmpl (tmpl, type)
+ tree tmpl, type;
+{
+ tree t;
+
+ if (tmpl == NULL || TYPE_CONTEXT(type) == NULL)
+ return tmpl;
+ t = search_nested_type_in_tmpl (tmpl, TYPE_CONTEXT(type));
+ if (t == NULL) return t;
+ t = lookup_nested_type_by_name(t, DECL_NAME(TYPE_NAME(type)));
+ return t;
+}
+
+static tree
+tsubst (t, args, nargs, in_decl)
+ tree t, *args;
+ int nargs;
+ tree in_decl;
+{
+ tree type;
+
+ if (t == NULL_TREE || t == error_mark_node)
+ return t;
+
+ type = TREE_TYPE (t);
+ if (type
+ /* Minor optimization.
+ ?? Are these really the most frequent cases? Is the savings
+ significant? */
+ && type != integer_type_node
+ && type != void_type_node
+ && type != char_type_node)
+ type = c_build_type_variant (tsubst (type, args, nargs, in_decl),
+ TYPE_READONLY (type),
+ TYPE_VOLATILE (type));
+ switch (TREE_CODE (t))
+ {
+ case RECORD_TYPE:
+ if (TYPE_PTRMEMFUNC_P (t))
+ return build_ptrmemfunc_type
+ (tsubst (TYPE_PTRMEMFUNC_FN_TYPE (t), args, nargs, in_decl));
+
+ /* else fall through */
+
+ case ERROR_MARK:
+ case IDENTIFIER_NODE:
+ case OP_IDENTIFIER:
+ case VOID_TYPE:
+ case REAL_TYPE:
+ case ENUMERAL_TYPE:
+ case BOOLEAN_TYPE:
+ case INTEGER_CST:
+ case REAL_CST:
+ case STRING_CST:
+ case UNION_TYPE:
+ return t;
+
+ case INTEGER_TYPE:
+ if (t == integer_type_node)
+ return t;
+
+ if (TREE_CODE (TYPE_MIN_VALUE (t)) == INTEGER_CST
+ && TREE_CODE (TYPE_MAX_VALUE (t)) == INTEGER_CST)
+ return t;
+ return build_index_2_type
+ (tsubst (TYPE_MIN_VALUE (t), args, nargs, in_decl),
+ tsubst (TYPE_MAX_VALUE (t), args, nargs, in_decl));
+
+ case TEMPLATE_TYPE_PARM:
+ return c_build_type_variant (args[TEMPLATE_TYPE_IDX (t)],
+ TYPE_READONLY (t),
+ TYPE_VOLATILE (t));
+
+ case TEMPLATE_CONST_PARM:
+ return args[TEMPLATE_CONST_IDX (t)];
+
+ case FUNCTION_DECL:
+ {
+ tree r;
+ tree fnargs, result;
+
+ if (type == TREE_TYPE (t)
+ && (DECL_CONTEXT (t) == NULL_TREE
+ || TREE_CODE_CLASS (TREE_CODE (DECL_CONTEXT (t))) != 't'))
+ return t;
+ fnargs = tsubst (DECL_ARGUMENTS (t), args, nargs, t);
+ result = tsubst (DECL_RESULT (t), args, nargs, t);
+ if (DECL_CONTEXT (t) != NULL_TREE
+ && TREE_CODE_CLASS (TREE_CODE (DECL_CONTEXT (t))) == 't')
+ {
+ /* Look it up in that class, and return the decl node there,
+ instead of creating a new one. */
+ tree ctx, methods, name, method;
+ int n_methods;
+ int i, found = 0;
+
+ name = DECL_NAME (t);
+ ctx = tsubst (DECL_CONTEXT (t), args, nargs, t);
+ methods = CLASSTYPE_METHOD_VEC (ctx);
+ if (methods == NULL_TREE)
+ /* No methods at all -- no way this one can match. */
+ goto no_match;
+ n_methods = TREE_VEC_LENGTH (methods);
+
+ r = NULL_TREE;
+
+ if (!strncmp (OPERATOR_TYPENAME_FORMAT,
+ IDENTIFIER_POINTER (name),
+ sizeof (OPERATOR_TYPENAME_FORMAT) - 1))
+ {
+ /* Type-conversion operator. Reconstruct the name, in
+ case it's the name of one of the template's parameters. */
+ name = build_typename_overload (TREE_TYPE (type));
+ }
+
+ if (DECL_CONTEXT (t) != NULL_TREE
+ && TREE_CODE_CLASS (TREE_CODE (DECL_CONTEXT (t))) == 't'
+ && constructor_name (DECL_CONTEXT (t)) == DECL_NAME (t))
+ name = constructor_name (ctx);
+#if 0
+ fprintf (stderr, "\nfor function %s in class %s:\n",
+ IDENTIFIER_POINTER (name),
+ IDENTIFIER_POINTER (TYPE_IDENTIFIER (ctx)));
+#endif
+ for (i = 0; i < n_methods; i++)
+ {
+ int pass;
+
+ method = TREE_VEC_ELT (methods, i);
+ if (method == NULL_TREE || DECL_NAME (method) != name)
+ continue;
+
+ pass = 0;
+ maybe_error:
+ for (; method; method = DECL_CHAIN (method))
+ {
+ my_friendly_assert (TREE_CODE (method) == FUNCTION_DECL,
+ 282);
+ if (! comptypes (type, TREE_TYPE (method), 1))
+ {
+ tree mtype = TREE_TYPE (method);
+ tree t1, t2;
+
+ /* Keep looking for a method that matches
+ perfectly. This takes care of the problem
+ where destructors (which have implicit int args)
+ look like constructors which have an int arg. */
+ if (pass == 0)
+ continue;
+
+ t1 = TYPE_ARG_TYPES (mtype);
+ t2 = TYPE_ARG_TYPES (type);
+ if (TREE_CODE (mtype) == FUNCTION_TYPE)
+ t2 = TREE_CHAIN (t2);
+
+ if (list_eq (t1, t2))
+ {
+ if (TREE_CODE (mtype) == FUNCTION_TYPE)
+ {
+ tree newtype;
+ newtype = build_function_type (TREE_TYPE (type),
+ TYPE_ARG_TYPES (type));
+ newtype = build_type_variant (newtype,
+ TYPE_READONLY (type),
+ TYPE_VOLATILE (type));
+ type = newtype;
+ if (TREE_TYPE (type) != TREE_TYPE (mtype))
+ goto maybe_bad_return_type;
+ }
+ else if (TYPE_METHOD_BASETYPE (mtype)
+ == TYPE_METHOD_BASETYPE (type))
+ {
+ /* Types didn't match, but arg types and
+ `this' do match, so the return type is
+ all that should be messing it up. */
+ maybe_bad_return_type:
+ if (TREE_TYPE (type) != TREE_TYPE (mtype))
+ error ("inconsistent return types for method `%s' in class `%s'",
+ IDENTIFIER_POINTER (name),
+ IDENTIFIER_POINTER (TYPE_IDENTIFIER (ctx)));
+ }
+ r = method;
+ break;
+ }
+ found = 1;
+ continue;
+ }
+#if 0
+ fprintf (stderr, "\tfound %s\n\n",
+ IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (method)));
+#endif
+ if (DECL_ARTIFICIAL (method))
+ {
+ cp_error ("template for method `%D' which has default implementation in class `%T'", name, ctx);
+ if (in_decl)
+ cp_error_at ("in attempt to instantiate `%D' declared at this point in file", in_decl);
+ return error_mark_node;
+ }
+
+ if (DECL_ARGUMENTS (method)
+ && ! TREE_PERMANENT (DECL_ARGUMENTS (method)))
+ /* @@ Is this early enough? Might we want to do
+ this instead while processing the expansion? */
+ DECL_ARGUMENTS (method)
+ = tsubst (DECL_ARGUMENTS (t), args, nargs, t);
+ r = method;
+ break;
+ }
+ if (r == NULL_TREE && pass == 0)
+ {
+ pass = 1;
+ method = TREE_VEC_ELT (methods, i);
+ goto maybe_error;
+ }
+ }
+ if (r == NULL_TREE)
+ {
+ no_match:
+ cp_error
+ (found
+ ? "template for method `%D' doesn't match any in class `%T'"
+ : "method `%D' not found in class `%T'", name, ctx);
+ if (in_decl)
+ cp_error_at ("in attempt to instantiate `%D' declared at this point in file", in_decl);
+ return error_mark_node;
+ }
+ }
+ else
+ {
+ r = DECL_NAME (t);
+ {
+ tree decls;
+ int got_it = 0;
+
+ decls = lookup_name_nonclass (r);
+ if (decls == NULL_TREE)
+ /* no match */;
+ else if (TREE_CODE (decls) == TREE_LIST)
+ for (decls = TREE_VALUE (decls); decls ;
+ decls = DECL_CHAIN (decls))
+ {
+ if (TREE_CODE (decls) == FUNCTION_DECL
+ && TREE_TYPE (decls) == type)
+ {
+ got_it = 1;
+ r = decls;
+ break;
+ }
+ }
+ else
+ {
+ tree val = decls;
+ decls = NULL_TREE;
+ if (TREE_CODE (val) == FUNCTION_DECL
+ && TREE_TYPE (val) == type)
+ {
+ got_it = 1;
+ r = val;
+ }
+ }
+
+ if (!got_it)
+ {
+ r = build_decl_overload (r, TYPE_VALUES (type),
+ DECL_CONTEXT (t) != NULL_TREE);
+ r = build_lang_decl (FUNCTION_DECL, r, type);
+ }
+ else if (DECL_INLINE (r) && DECL_SAVED_INSNS (r))
+ {
+ /* This overrides the template version, use it. */
+ return r;
+ }
+ }
+ }
+ TREE_PUBLIC (r) = TREE_PUBLIC (t);
+ DECL_EXTERNAL (r) = DECL_EXTERNAL (t);
+ TREE_STATIC (r) = TREE_STATIC (t);
+ DECL_INLINE (r) = DECL_INLINE (t);
+ {
+#if 0 /* Maybe later. -jason */
+ struct tinst_level *til = tinst_for_decl();
+
+ /* should always be true under new approach */
+ if (til)
+ {
+ DECL_SOURCE_FILE (r) = til->file;
+ DECL_SOURCE_LINE (r) = til->line;
+ }
+ else
+#endif
+ {
+ DECL_SOURCE_FILE (r) = DECL_SOURCE_FILE (t);
+ DECL_SOURCE_LINE (r) = DECL_SOURCE_LINE (t);
+ }
+ }
+ DECL_CLASS_CONTEXT (r) = tsubst (DECL_CLASS_CONTEXT (t), args, nargs, t);
+ make_decl_rtl (r, NULL_PTR, 1);
+ DECL_ARGUMENTS (r) = fnargs;
+ DECL_RESULT (r) = result;
+ if (DECL_CONTEXT (t) == NULL_TREE
+ || TREE_CODE_CLASS (TREE_CODE (DECL_CONTEXT (t))) != 't')
+ push_overloaded_decl_top_level (r, 0);
+ return r;
+ }
+
+ case PARM_DECL:
+ {
+ tree r;
+ r = build_decl (PARM_DECL, DECL_NAME (t), type);
+ DECL_INITIAL (r) = TREE_TYPE (r);
+ if (TREE_CHAIN (t))
+ TREE_CHAIN (r) = tsubst (TREE_CHAIN (t), args, nargs, TREE_CHAIN (t));
+ return r;
+ }
+
+ case TREE_LIST:
+ {
+ tree purpose, value, chain, result;
+ int via_public, via_virtual, via_protected;
+
+ if (t == void_list_node)
+ return t;
+
+ via_public = TREE_VIA_PUBLIC (t);
+ via_protected = TREE_VIA_PROTECTED (t);
+ via_virtual = TREE_VIA_VIRTUAL (t);
+
+ purpose = TREE_PURPOSE (t);
+ if (purpose)
+ purpose = tsubst (purpose, args, nargs, in_decl);
+ value = TREE_VALUE (t);
+ if (value)
+ value = tsubst (value, args, nargs, in_decl);
+ chain = TREE_CHAIN (t);
+ if (chain && chain != void_type_node)
+ chain = tsubst (chain, args, nargs, in_decl);
+ if (purpose == TREE_PURPOSE (t)
+ && value == TREE_VALUE (t)
+ && chain == TREE_CHAIN (t))
+ return t;
+ result = hash_tree_cons (via_public, via_virtual, via_protected,
+ purpose, value, chain);
+ TREE_PARMLIST (result) = TREE_PARMLIST (t);
+ return result;
+ }
+ case TREE_VEC:
+ {
+ int len = TREE_VEC_LENGTH (t), need_new = 0, i;
+ tree *elts = (tree *) alloca (len * sizeof (tree));
+ bzero (elts, len * sizeof (tree));
+
+ for (i = 0; i < len; i++)
+ {
+ elts[i] = tsubst (TREE_VEC_ELT (t, i), args, nargs, in_decl);
+ if (elts[i] != TREE_VEC_ELT (t, i))
+ need_new = 1;
+ }
+
+ if (!need_new)
+ return t;
+
+ t = make_tree_vec (len);
+ for (i = 0; i < len; i++)
+ TREE_VEC_ELT (t, i) = elts[i];
+ return t;
+ }
+ case POINTER_TYPE:
+ case REFERENCE_TYPE:
+ {
+ tree r;
+ enum tree_code code;
+ if (type == TREE_TYPE (t))
+ return t;
+
+ code = TREE_CODE (t);
+ if (code == POINTER_TYPE)
+ r = build_pointer_type (type);
+ else
+ r = build_reference_type (type);
+ r = c_build_type_variant (r, TYPE_READONLY (t), TYPE_VOLATILE (t));
+ /* Will this ever be needed for TYPE_..._TO values? */
+ layout_type (r);
+ return r;
+ }
+ case OFFSET_TYPE:
+ return build_offset_type
+ (tsubst (TYPE_OFFSET_BASETYPE (t), args, nargs, in_decl), type);
+ case FUNCTION_TYPE:
+ case METHOD_TYPE:
+ {
+ tree values = TYPE_VALUES (t); /* same as TYPE_ARG_TYPES */
+ tree context = TYPE_CONTEXT (t);
+ tree new_value;
+
+ /* Don't bother recursing if we know it won't change anything. */
+ if (values != void_list_node)
+ values = tsubst (values, args, nargs, in_decl);
+ if (context)
+ context = tsubst (context, args, nargs, in_decl);
+ /* Could also optimize cases where return value and
+ values have common elements (e.g., T min(const &T, const T&). */
+
+ /* If the above parameters haven't changed, just return the type. */
+ if (type == TREE_TYPE (t)
+ && values == TYPE_VALUES (t)
+ && context == TYPE_CONTEXT (t))
+ return t;
+
+ /* Construct a new type node and return it. */
+ if (TREE_CODE (t) == FUNCTION_TYPE
+ && context == NULL_TREE)
+ {
+ new_value = build_function_type (type, values);
+ }
+ else if (context == NULL_TREE)
+ {
+ tree base = tsubst (TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (t))),
+ args, nargs, in_decl);
+ new_value = build_cplus_method_type (base, type,
+ TREE_CHAIN (values));
+ }
+ else
+ {
+ new_value = make_node (TREE_CODE (t));
+ TREE_TYPE (new_value) = type;
+ TYPE_CONTEXT (new_value) = context;
+ TYPE_VALUES (new_value) = values;
+ TYPE_SIZE (new_value) = TYPE_SIZE (t);
+ TYPE_ALIGN (new_value) = TYPE_ALIGN (t);
+ TYPE_MODE (new_value) = TYPE_MODE (t);
+ if (TYPE_METHOD_BASETYPE (t))
+ TYPE_METHOD_BASETYPE (new_value) = tsubst (TYPE_METHOD_BASETYPE (t),
+ args, nargs, in_decl);
+ /* Need to generate hash value. */
+ my_friendly_abort (84);
+ }
+ new_value = build_type_variant (new_value,
+ TYPE_READONLY (t),
+ TYPE_VOLATILE (t));
+ return new_value;
+ }
+ case ARRAY_TYPE:
+ {
+ tree domain = tsubst (TYPE_DOMAIN (t), args, nargs, in_decl);
+ tree r;
+ if (type == TREE_TYPE (t) && domain == TYPE_DOMAIN (t))
+ return t;
+ r = build_cplus_array_type (type, domain);
+ return r;
+ }
+
+ case UNINSTANTIATED_P_TYPE:
+ {
+ int nparms = TREE_VEC_LENGTH (DECL_TEMPLATE_PARMS (UPT_TEMPLATE (t)));
+ tree argvec = make_tree_vec (nparms);
+ tree parmvec = UPT_PARMS (t);
+ int i;
+ tree id, rt;
+ for (i = 0; i < nparms; i++)
+ TREE_VEC_ELT (argvec, i) = tsubst (TREE_VEC_ELT (parmvec, i),
+ args, nargs, in_decl);
+ id = lookup_template_class (DECL_NAME (UPT_TEMPLATE (t)), argvec, NULL_TREE);
+ if (! IDENTIFIER_HAS_TYPE_VALUE (id)) {
+ instantiate_class_template(id, 0);
+ /* set up pending_classes */
+ add_pending_template (id);
+
+ TYPE_MAIN_VARIANT (IDENTIFIER_TYPE_VALUE (id)) =
+ IDENTIFIER_TYPE_VALUE (id);
+ }
+ rt = IDENTIFIER_TYPE_VALUE (id);
+
+ /* kung: this part handles nested type in template definition */
+
+ if ( !ANON_AGGRNAME_P (DECL_NAME(TYPE_NAME(t))))
+ {
+ rt = search_nested_type_in_tmpl (rt, t);
+ }
+
+ return build_type_variant (rt, TYPE_READONLY (t), TYPE_VOLATILE (t));
+ }
+
+ case MINUS_EXPR:
+ case PLUS_EXPR:
+ return fold (build (TREE_CODE (t), TREE_TYPE (t),
+ tsubst (TREE_OPERAND (t, 0), args, nargs, in_decl),
+ tsubst (TREE_OPERAND (t, 1), args, nargs, in_decl)));
+
+ case NEGATE_EXPR:
+ case NOP_EXPR:
+ return fold (build1 (TREE_CODE (t), TREE_TYPE (t),
+ tsubst (TREE_OPERAND (t, 0), args, nargs, in_decl)));
+
+ default:
+ sorry ("use of `%s' in function template",
+ tree_code_name [(int) TREE_CODE (t)]);
+ return error_mark_node;
+ }
+}
+
+tree
+instantiate_template (tmpl, targ_ptr)
+ tree tmpl, *targ_ptr;
+{
+ tree targs, fndecl;
+ int i, len;
+ struct pending_inline *p;
+ struct template_info *t;
+ struct obstack *old_fmp_obstack;
+ extern struct obstack *function_maybepermanent_obstack;
+
+ push_obstacks (&permanent_obstack, &permanent_obstack);
+ old_fmp_obstack = function_maybepermanent_obstack;
+ function_maybepermanent_obstack = &permanent_obstack;
+
+ my_friendly_assert (TREE_CODE (tmpl) == TEMPLATE_DECL, 283);
+ len = TREE_VEC_LENGTH (DECL_TEMPLATE_PARMS (tmpl));
+
+ for (fndecl = DECL_TEMPLATE_INSTANTIATIONS (tmpl);
+ fndecl; fndecl = TREE_CHAIN (fndecl))
+ {
+ tree *t1 = &TREE_VEC_ELT (TREE_PURPOSE (fndecl), 0);
+ for (i = len - 1; i >= 0; i--)
+ if (t1[i] != targ_ptr[i])
+ goto no_match;
+
+ /* Here, we have a match. */
+ fndecl = TREE_VALUE (fndecl);
+ goto exit;
+
+ no_match:
+ ;
+ }
+
+ targs = make_tree_vec (len);
+ i = len;
+ while (i--)
+ TREE_VEC_ELT (targs, i) = targ_ptr[i];
+
+ /* substitute template parameters */
+ fndecl = tsubst (DECL_RESULT (tmpl), targ_ptr,
+ TREE_VEC_LENGTH (targs), tmpl);
+
+ if (fndecl == error_mark_node)
+ goto exit;
+
+ /* If it's a static member fn in the template, we need to change it
+ into a FUNCTION_TYPE and chop off its this pointer. */
+ if (TREE_CODE (TREE_TYPE (DECL_RESULT (tmpl))) == METHOD_TYPE
+ && DECL_STATIC_FUNCTION_P (fndecl))
+ {
+ tree olddecl = DECL_RESULT (tmpl);
+ revert_static_member_fn (&DECL_RESULT (tmpl), NULL, NULL);
+ /* Chop off the this pointer that grokclassfn so kindly added
+ for us (it didn't know yet if the fn was static or not). */
+ DECL_ARGUMENTS (olddecl) = TREE_CHAIN (DECL_ARGUMENTS (olddecl));
+ DECL_ARGUMENTS (fndecl) = TREE_CHAIN (DECL_ARGUMENTS (fndecl));
+ }
+
+ t = DECL_TEMPLATE_INFO (tmpl);
+
+ /* If we have a preexisting version of this function, don't expand
+ the template version, use the other instead. */
+ if (DECL_INLINE (fndecl) && DECL_SAVED_INSNS (fndecl))
+ {
+ SET_DECL_TEMPLATE_SPECIALIZATION (fndecl);
+ p = (struct pending_inline *)0;
+ }
+ else if (t->text)
+ {
+ SET_DECL_IMPLICIT_INSTANTIATION (fndecl);
+ p = (struct pending_inline *) permalloc (sizeof (struct pending_inline));
+ p->parm_vec = t->parm_vec;
+ p->bindings = targs;
+ p->can_free = 0;
+ p->deja_vu = 0;
+ p->buf = t->text;
+ p->len = t->length;
+ p->fndecl = fndecl;
+ {
+ int l = lineno;
+ char * f = input_filename;
+
+ lineno = p->lineno = t->lineno;
+ input_filename = p->filename = t->filename;
+
+ extract_interface_info ();
+
+ if (interface_unknown && flag_external_templates && ! DECL_IN_SYSTEM_HEADER (tmpl))
+ warn_if_unknown_interface ();
+ if (interface_unknown || !flag_external_templates)
+ p->interface = 1; /* unknown */
+ else
+ p->interface = interface_only ? 0 : 2;
+
+ lineno = l;
+ input_filename = f;
+
+ extract_interface_info ();
+ }
+ }
+ else
+ p = (struct pending_inline *)0;
+
+ DECL_TEMPLATE_INSTANTIATIONS (tmpl) =
+ tree_cons (targs, fndecl, DECL_TEMPLATE_INSTANTIATIONS (tmpl));
+
+ if (p == (struct pending_inline *)0)
+ {
+ /* do nothing */
+ }
+ else if (DECL_INLINE (fndecl))
+ {
+ DECL_PENDING_INLINE_INFO (fndecl) = p;
+ p->next = pending_inlines;
+ pending_inlines = p;
+ }
+ else
+ {
+ p->next = pending_template_expansions;
+ pending_template_expansions = p;
+ }
+ exit:
+ function_maybepermanent_obstack = old_fmp_obstack;
+ pop_obstacks ();
+
+ return fndecl;
+}
+
+/* classlevel should now never be true. jason 4/12/94 */
+void
+undo_template_name_overload (id, classlevel)
+ tree id;
+ int classlevel;
+{
+ tree template;
+
+ template = IDENTIFIER_TEMPLATE (id);
+ if (!template)
+ return;
+
+#if 0 /* not yet, should get fixed properly later */
+ poplevel (0, 0, 0);
+#endif
+#if 1 /* XXX */
+ /* This was a botch... See `overload_template_name' just below. */
+ if (!classlevel)
+ poplevel (0, 0, 0);
+#endif
+}
+
+/* classlevel should now never be true. jason 4/12/94 */
+void
+overload_template_name (id, classlevel)
+ tree id;
+ int classlevel;
+{
+ tree template, t, decl;
+ struct template_info *tinfo;
+
+ my_friendly_assert (TREE_CODE (id) == IDENTIFIER_NODE, 284);
+ template = IDENTIFIER_TEMPLATE (id);
+ if (!template)
+ return;
+
+ template = TREE_PURPOSE (template);
+ tinfo = DECL_TEMPLATE_INFO (template);
+ template = DECL_NAME (template);
+ my_friendly_assert (template != NULL_TREE, 285);
+
+#if 1 /* XXX */
+ /* This was a botch... names of templates do not get their own private
+ scopes. Rather, they should go into the binding level already created
+ by push_template_decls. Except that there isn't one of those for
+ specializations. */
+ if (!classlevel)
+ {
+ pushlevel (1);
+ declare_pseudo_global_level ();
+ }
+#endif
+
+ t = xref_tag (tinfo->aggr, id, NULL_TREE, 0);
+ my_friendly_assert (TREE_CODE (t) == RECORD_TYPE
+ || TREE_CODE (t) == UNION_TYPE
+ || TREE_CODE (t) == UNINSTANTIATED_P_TYPE, 286);
+
+ decl = build_decl (TYPE_DECL, template, t);
+
+#if 0 /* fix this later */
+ /* We don't want to call here if the work has already been done. */
+ t = (classlevel
+ ? IDENTIFIER_CLASS_VALUE (template)
+ : IDENTIFIER_LOCAL_VALUE (template));
+ if (t
+ && TREE_CODE (t) == TYPE_DECL
+ && TREE_TYPE (t) == t)
+ my_friendly_abort (85);
+#endif
+
+ if (classlevel)
+ pushdecl_class_level (decl);
+ else
+ pushdecl (decl);
+
+#if 0 /* This seems bogus to me; if it isn't, explain why. (jason) */
+ /* Fake this for now, just to make dwarfout.c happy. It will have to
+ be done in a proper way later on. */
+ DECL_CONTEXT (decl) = t;
+#endif
+}
+
+/* NAME is the IDENTIFIER value of a PRE_PARSED_CLASS_DECL. */
+void
+end_template_instantiation (name)
+ tree name;
+{
+ extern struct pending_input *to_be_restored;
+ tree t, decl;
+
+ processing_template_defn--;
+ if (!flag_external_templates)
+ interface_unknown--;
+
+ /* Restore the old parser input state. */
+ if (yychar == YYEMPTY)
+ yychar = yylex ();
+ if (yychar != END_OF_SAVED_INPUT)
+ error ("parse error at end of class template");
+ else
+ {
+ restore_pending_input (to_be_restored);
+ to_be_restored = 0;
+ }
+
+ /* Our declarations didn't get stored in the global slot, since
+ there was a (supposedly tags-transparent) scope in between. */
+ t = IDENTIFIER_TYPE_VALUE (name);
+ my_friendly_assert (t != NULL_TREE
+ && TREE_CODE_CLASS (TREE_CODE (t)) == 't',
+ 287);
+ SET_CLASSTYPE_IMPLICIT_INSTANTIATION (t);
+ /* Make methods of template classes static, unless
+ -fexternal-templates is given. */
+ if (!flag_external_templates)
+ SET_CLASSTYPE_INTERFACE_UNKNOWN (t);
+ decl = IDENTIFIER_GLOBAL_VALUE (name);
+ my_friendly_assert (TREE_CODE (decl) == TYPE_DECL, 288);
+
+ undo_template_name_overload (name, 0);
+ t = IDENTIFIER_TEMPLATE (name);
+ pop_template_decls (DECL_TEMPLATE_PARMS (TREE_PURPOSE (t)), TREE_VALUE (t),
+ 0);
+ /* This will fix up the type-value field. */
+ pushdecl (decl);
+ pop_from_top_level ();
+
+#ifdef DWARF_DEBUGGING_INFO
+ if (write_symbols == DWARF_DEBUG && TREE_CODE (decl) == TYPE_DECL)
+ {
+ /* We just completed the definition of a new file-scope type,
+ so we can go ahead and output debug-info for it now. */
+ TYPE_STUB_DECL (TREE_TYPE (decl)) = decl;
+ rest_of_type_compilation (TREE_TYPE (decl), 1);
+ }
+#endif /* DWARF_DEBUGGING_INFO */
+
+ /* Restore interface/implementation settings. */
+ extract_interface_info ();
+}
+
+/* Store away the text of an template. */
+
+void
+reinit_parse_for_template (yychar, d1, d2)
+ int yychar;
+ tree d1, d2;
+{
+ struct template_info *template_info;
+ extern struct obstack inline_text_obstack; /* see comment in lex.c */
+
+ if (d2 == NULL_TREE || d2 == error_mark_node)
+ {
+ lose:
+ /* @@ Should use temp obstack, and discard results. */
+ reinit_parse_for_block (yychar, &inline_text_obstack, 1);
+ return;
+ }
+
+ if (TREE_CODE (d2) == IDENTIFIER_NODE)
+ d2 = IDENTIFIER_GLOBAL_VALUE (d2);
+ if (!d2)
+ goto lose;
+ template_info = DECL_TEMPLATE_INFO (d2);
+ if (!template_info)
+ {
+ template_info = (struct template_info *) permalloc (sizeof (struct template_info));
+ bzero (template_info, sizeof (struct template_info));
+ DECL_TEMPLATE_INFO (d2) = template_info;
+ }
+ template_info->filename = input_filename;
+ template_info->lineno = lineno;
+ reinit_parse_for_block (yychar, &inline_text_obstack, 1);
+ template_info->text = obstack_base (&inline_text_obstack);
+ template_info->length = obstack_object_size (&inline_text_obstack);
+ obstack_finish (&inline_text_obstack);
+ template_info->parm_vec = d1;
+}
+
+/* Type unification.
+
+ We have a function template signature with one or more references to
+ template parameters, and a parameter list we wish to fit to this
+ template. If possible, produce a list of parameters for the template
+ which will cause it to fit the supplied parameter list.
+
+ Return zero for success, 2 for an incomplete match that doesn't resolve
+ all the types, and 1 for complete failure. An error message will be
+ printed only for an incomplete match.
+
+ TPARMS[NTPARMS] is an array of template parameter types;
+ TARGS[NTPARMS] is the array of template parameter values. PARMS is
+ the function template's signature (using TEMPLATE_PARM_IDX nodes),
+ and ARGS is the argument list we're trying to match against it.
+
+ If SUBR is 1, we're being called recursively (to unify the arguments of
+ a function or method parameter of a function template), so don't zero
+ out targs and don't fail on an incomplete match. */
+
+int
+type_unification (tparms, targs, parms, args, nsubsts, subr)
+ tree tparms, *targs, parms, args;
+ int *nsubsts, subr;
+{
+ tree parm, arg;
+ int i;
+ int ntparms = TREE_VEC_LENGTH (tparms);
+
+ my_friendly_assert (TREE_CODE (tparms) == TREE_VEC, 289);
+ my_friendly_assert (TREE_CODE (parms) == TREE_LIST, 290);
+ /* ARGS could be NULL (via a call from parse.y to
+ build_x_function_call). */
+ if (args)
+ my_friendly_assert (TREE_CODE (args) == TREE_LIST, 291);
+ my_friendly_assert (ntparms > 0, 292);
+
+ if (!subr)
+ bzero (targs, sizeof (tree) * ntparms);
+
+ while (parms
+ && parms != void_list_node
+ && args
+ && args != void_list_node)
+ {
+ parm = TREE_VALUE (parms);
+ parms = TREE_CHAIN (parms);
+ arg = TREE_VALUE (args);
+ args = TREE_CHAIN (args);
+
+ if (arg == error_mark_node)
+ return 1;
+ if (arg == unknown_type_node)
+ return 1;
+#if 0
+ if (TREE_CODE (arg) == VAR_DECL)
+ arg = TREE_TYPE (arg);
+ else if (TREE_CODE_CLASS (TREE_CODE (arg)) == 'e')
+ arg = TREE_TYPE (arg);
+#else
+ if (TREE_CODE_CLASS (TREE_CODE (arg)) != 't')
+ {
+ my_friendly_assert (TREE_TYPE (arg) != NULL_TREE, 293);
+ arg = TREE_TYPE (arg);
+ }
+#endif
+ if (TREE_CODE (arg) == FUNCTION_TYPE
+ || TREE_CODE (arg) == METHOD_TYPE)
+ arg = build_pointer_type (arg);
+
+ switch (unify (tparms, targs, ntparms, parm, arg, nsubsts))
+ {
+ case 0:
+ break;
+ case 1:
+ return 1;
+ }
+ }
+ /* Fail if we've reached the end of the parm list, and more args
+ are present, and the parm list isn't variadic. */
+ if (args && args != void_list_node && parms == void_list_node)
+ return 1;
+ /* Fail if parms are left and they don't have default values. */
+ if (parms
+ && parms != void_list_node
+ && TREE_PURPOSE (parms) == NULL_TREE)
+ return 1;
+ if (!subr)
+ for (i = 0; i < ntparms; i++)
+ if (!targs[i])
+ {
+ error ("incomplete type unification");
+ return 2;
+ }
+ return 0;
+}
+
+/* Tail recursion is your friend. */
+static int
+unify (tparms, targs, ntparms, parm, arg, nsubsts)
+ tree tparms, *targs, parm, arg;
+ int *nsubsts, ntparms;
+{
+ int idx;
+
+ /* I don't think this will do the right thing with respect to types.
+ But the only case I've seen it in so far has been array bounds, where
+ signedness is the only information lost, and I think that will be
+ okay. */
+ while (TREE_CODE (parm) == NOP_EXPR)
+ parm = TREE_OPERAND (parm, 0);
+
+ if (arg == error_mark_node)
+ return 1;
+ if (arg == unknown_type_node)
+ return 1;
+ if (arg == parm)
+ return 0;
+
+ if (TREE_CODE (arg) == REFERENCE_TYPE)
+ arg = TREE_TYPE (arg);
+
+ switch (TREE_CODE (parm))
+ {
+ case TEMPLATE_TYPE_PARM:
+ (*nsubsts)++;
+ if (TEMPLATE_TYPE_TPARMLIST (parm) != tparms)
+ {
+ error ("mixed template headers?!");
+ my_friendly_abort (86);
+ return 1;
+ }
+ idx = TEMPLATE_TYPE_IDX (parm);
+ /* Simple cases: Value already set, does match or doesn't. */
+ if (targs[idx] == arg)
+ return 0;
+ else if (targs[idx])
+ {
+ if (TYPE_MAIN_VARIANT (targs[idx]) == TYPE_MAIN_VARIANT (arg))
+ /* allow different parms to have different cv-qualifiers */;
+ else
+ return 1;
+ }
+ /* Check for mixed types and values. */
+ if (TREE_CODE (TREE_VEC_ELT (tparms, idx)) != IDENTIFIER_NODE)
+ return 1;
+ /* Allow trivial conversions. */
+ if (TYPE_READONLY (parm) < TYPE_READONLY (arg)
+ || TYPE_VOLATILE (parm) < TYPE_VOLATILE (arg))
+ return 1;
+ targs[idx] = arg;
+ return 0;
+ case TEMPLATE_CONST_PARM:
+ (*nsubsts)++;
+ idx = TEMPLATE_CONST_IDX (parm);
+ if (targs[idx] == arg)
+ return 0;
+ else if (targs[idx])
+ {
+ tree t = targs[idx];
+ if (TREE_CODE (t) == TREE_CODE (arg))
+ switch (TREE_CODE (arg))
+ {
+ case INTEGER_CST:
+ if (tree_int_cst_equal (t, arg))
+ return 0;
+ break;
+ case REAL_CST:
+ if (REAL_VALUES_EQUAL (TREE_REAL_CST (t), TREE_REAL_CST (arg)))
+ return 0;
+ break;
+ /* STRING_CST values are not valid template const parms. */
+ default:
+ ;
+ }
+ my_friendly_abort (87);
+ return 1;
+ }
+/* else if (typeof arg != tparms[idx])
+ return 1;*/
+
+ targs[idx] = copy_to_permanent (arg);
+ return 0;
+
+ case POINTER_TYPE:
+ if (TREE_CODE (arg) != POINTER_TYPE)
+ return 1;
+ return unify (tparms, targs, ntparms, TREE_TYPE (parm), TREE_TYPE (arg),
+ nsubsts);
+
+ case REFERENCE_TYPE:
+ return unify (tparms, targs, ntparms, TREE_TYPE (parm), arg, nsubsts);
+
+ case ARRAY_TYPE:
+ if (TREE_CODE (arg) != ARRAY_TYPE)
+ return 1;
+ if (unify (tparms, targs, ntparms, TYPE_DOMAIN (parm), TYPE_DOMAIN (arg),
+ nsubsts) != 0)
+ return 1;
+ return unify (tparms, targs, ntparms, TREE_TYPE (parm), TREE_TYPE (arg),
+ nsubsts);
+
+ case REAL_TYPE:
+ case INTEGER_TYPE:
+ if (TREE_CODE (parm) == INTEGER_TYPE && TREE_CODE (arg) == INTEGER_TYPE)
+ {
+ if (TYPE_MIN_VALUE (parm) && TYPE_MIN_VALUE (arg)
+ && unify (tparms, targs, ntparms,
+ TYPE_MIN_VALUE (parm), TYPE_MIN_VALUE (arg), nsubsts))
+ return 1;
+ if (TYPE_MAX_VALUE (parm) && TYPE_MAX_VALUE (arg)
+ && unify (tparms, targs, ntparms,
+ TYPE_MAX_VALUE (parm), TYPE_MAX_VALUE (arg), nsubsts))
+ return 1;
+ }
+ /* As far as unification is concerned, this wins. Later checks
+ will invalidate it if necessary. */
+ return 0;
+
+ /* Types INTEGER_CST and MINUS_EXPR can come from array bounds. */
+ case INTEGER_CST:
+ if (TREE_CODE (arg) != INTEGER_CST)
+ return 1;
+ return !tree_int_cst_equal (parm, arg);
+
+ case MINUS_EXPR:
+ {
+ tree t1, t2;
+ t1 = TREE_OPERAND (parm, 0);
+ t2 = TREE_OPERAND (parm, 1);
+ if (TREE_CODE (t1) != TEMPLATE_CONST_PARM)
+ return 1;
+ return unify (tparms, targs, ntparms, t1,
+ fold (build (PLUS_EXPR, integer_type_node, arg, t2)),
+ nsubsts);
+ }
+
+ case TREE_VEC:
+ {
+ int i;
+ if (TREE_CODE (arg) != TREE_VEC)
+ return 1;
+ if (TREE_VEC_LENGTH (parm) != TREE_VEC_LENGTH (arg))
+ return 1;
+ for (i = TREE_VEC_LENGTH (parm) - 1; i >= 0; i--)
+ if (unify (tparms, targs, ntparms,
+ TREE_VEC_ELT (parm, i), TREE_VEC_ELT (arg, i),
+ nsubsts))
+ return 1;
+ return 0;
+ }
+
+ case UNINSTANTIATED_P_TYPE:
+ {
+ tree a;
+ /* Unification of something that is not a template fails. (mrs) */
+ if (TYPE_NAME (arg) == 0)
+ return 1;
+ a = IDENTIFIER_TEMPLATE (TYPE_IDENTIFIER (arg));
+ /* Unification of something that is not a template fails. (mrs) */
+ if (a == 0)
+ return 1;
+ if (UPT_TEMPLATE (parm) != TREE_PURPOSE (a))
+ /* different templates */
+ return 1;
+ return unify (tparms, targs, ntparms, UPT_PARMS (parm), TREE_VALUE (a),
+ nsubsts);
+ }
+
+ case RECORD_TYPE:
+ if (TYPE_PTRMEMFUNC_P (parm))
+ return unify (tparms, targs, ntparms, TYPE_PTRMEMFUNC_FN_TYPE (parm),
+ arg, nsubsts);
+
+ /* Allow trivial conversions. */
+ if (TYPE_MAIN_VARIANT (parm) != TYPE_MAIN_VARIANT (arg)
+ || TYPE_READONLY (parm) < TYPE_READONLY (arg)
+ || TYPE_VOLATILE (parm) < TYPE_VOLATILE (arg))
+ return 1;
+ return 0;
+
+ case METHOD_TYPE:
+ if (TREE_CODE (arg) != METHOD_TYPE)
+ return 1;
+ goto check_args;
+
+ case FUNCTION_TYPE:
+ if (TREE_CODE (arg) != FUNCTION_TYPE)
+ return 1;
+ check_args:
+ return type_unification (tparms, targs, TYPE_ARG_TYPES (parm),
+ TYPE_ARG_TYPES (arg), nsubsts, 1);
+
+ case OFFSET_TYPE:
+ if (TREE_CODE (arg) != OFFSET_TYPE)
+ return 1;
+ if (unify (tparms, targs, ntparms, TYPE_OFFSET_BASETYPE (parm),
+ TYPE_OFFSET_BASETYPE (arg), nsubsts))
+ return 1;
+ return unify (tparms, targs, ntparms, TREE_TYPE (parm),
+ TREE_TYPE (arg), nsubsts);
+
+ default:
+ sorry ("use of `%s' in template type unification",
+ tree_code_name [(int) TREE_CODE (parm)]);
+ return 1;
+ }
+}
+
+
+#undef DEBUG
+
+int
+do_pending_expansions ()
+{
+ struct pending_inline *i, *new_list = 0;
+
+ if (!pending_template_expansions)
+ return 0;
+
+#ifdef DEBUG
+ fprintf (stderr, "\n\n\t\t IN DO_PENDING_EXPANSIONS\n\n");
+#endif
+
+ i = pending_template_expansions;
+ while (i)
+ {
+ tree context;
+
+ struct pending_inline *next = i->next;
+ tree t = i->fndecl;
+
+ int decision = 0;
+#define DECIDE(N) do {decision=(N); goto decided;} while(0)
+
+ my_friendly_assert (TREE_CODE (t) == FUNCTION_DECL
+ || TREE_CODE (t) == VAR_DECL, 294);
+ if (TREE_ASM_WRITTEN (t))
+ DECIDE (0);
+
+ if (DECL_EXPLICIT_INSTANTIATION (t))
+ DECIDE (! DECL_EXTERNAL (t));
+ else if (! flag_implicit_templates)
+ DECIDE (0);
+
+ /* If it's a method, let the class type decide it.
+ @@ What if the method template is in a separate file?
+ Maybe both file contexts should be taken into account?
+ Maybe only do this if i->interface == 1 (unknown)? */
+ context = DECL_CONTEXT (t);
+ if (context != NULL_TREE
+ && TREE_CODE_CLASS (TREE_CODE (context)) == 't')
+ {
+ /* I'm interested in the context of this version of the function,
+ not the original virtual declaration. */
+ context = DECL_CLASS_CONTEXT (t);
+
+ /* If `unknown', we might want a static copy.
+ If `implementation', we want a global one.
+ If `interface', ext ref. */
+ if (CLASSTYPE_INTERFACE_KNOWN (context))
+ DECIDE (!CLASSTYPE_INTERFACE_ONLY (context));
+#if 0 /* This doesn't get us stuff needed only by the file initializer. */
+ DECIDE (TREE_USED (t));
+#else /* This compiles too much stuff, but that's probably better in
+ most cases than never compiling the stuff we need. */
+ DECIDE (1);
+#endif
+ }
+
+ if (i->interface == 1)
+ DECIDE (TREE_USED (t));
+ else
+ DECIDE (i->interface);
+
+ decided:
+#ifdef DEBUG
+ print_node_brief (stderr, decision ? "yes: " : "no: ", t, 0);
+ fprintf (stderr, "\t%s\n",
+ (DECL_ASSEMBLER_NAME (t)
+ ? IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (t))
+ : ""));
+#endif
+ if (decision)
+ {
+ i->next = pending_inlines;
+ pending_inlines = i;
+ }
+ else
+ {
+ i->next = new_list;
+ new_list = i;
+ }
+ i = next;
+ }
+ pending_template_expansions = new_list;
+ if (!pending_inlines)
+ return 0;
+ do_pending_inlines ();
+ return 1;
+}
+
+
+struct pending_template {
+ struct pending_template *next;
+ tree id;
+};
+
+static struct pending_template* pending_templates;
+
+void
+do_pending_templates ()
+{
+ struct pending_template* t;
+
+ for ( t = pending_templates; t; t = t->next)
+ {
+ instantiate_class_template (t->id, 1);
+ }
+
+ for ( t = pending_templates; t; t = pending_templates)
+ {
+ pending_templates = t->next;
+ free(t);
+ }
+}
+
+static void
+add_pending_template (pt)
+ tree pt;
+{
+ struct pending_template *p;
+
+ p = (struct pending_template *) malloc (sizeof (struct pending_template));
+ p->next = pending_templates;
+ pending_templates = p;
+ p->id = pt;
+}
+
+/* called from the parser. */
+void
+do_function_instantiation (declspecs, declarator, storage)
+ tree declspecs, declarator, storage;
+{
+ tree decl = grokdeclarator (declarator, declspecs, NORMAL, 0, 0);
+ tree name = DECL_NAME (decl);
+ tree fn = IDENTIFIER_GLOBAL_VALUE (name);
+ tree result = NULL_TREE;
+ if (fn)
+ {
+ for (fn = get_first_fn (fn); fn; fn = DECL_CHAIN (fn))
+ if (TREE_CODE (fn) == TEMPLATE_DECL)
+ {
+ int ntparms = TREE_VEC_LENGTH (DECL_TEMPLATE_PARMS (fn));
+ tree *targs = (tree *) malloc (sizeof (tree) * ntparms);
+ int i, dummy;
+ i = type_unification (DECL_TEMPLATE_PARMS (fn), targs,
+ TYPE_ARG_TYPES (TREE_TYPE (fn)),
+ TYPE_ARG_TYPES (TREE_TYPE (decl)),
+ &dummy, 0);
+ if (i == 0)
+ {
+ if (result)
+ cp_error ("ambiguous template instantiation for `%D' requested", decl);
+ else
+ result = instantiate_template (fn, targs);
+ }
+ }
+ }
+ if (! result)
+ cp_error ("no matching template for `%D' found", decl);
+
+ if (flag_external_templates)
+ return;
+
+ if (DECL_EXPLICIT_INSTANTIATION (result) && ! DECL_EXTERNAL (result))
+ return;
+
+ SET_DECL_EXPLICIT_INSTANTIATION (result);
+ TREE_PUBLIC (result) = 1;
+
+ if (storage == NULL_TREE)
+ DECL_EXTERNAL (result) = DECL_INLINE (result) && ! flag_implement_inlines;
+ else if (storage == ridpointers[(int) RID_EXTERN])
+ DECL_EXTERNAL (result) = 1;
+ else
+ cp_error ("storage class `%D' applied to template instantiation",
+ storage);
+}
+
+void
+do_type_instantiation (name, storage)
+ tree name, storage;
+{
+ tree t = TREE_TYPE (name);
+ int extern_p;
+
+ if (flag_external_templates)
+ return;
+
+ if (CLASSTYPE_EXPLICIT_INSTANTIATION (t) && ! CLASSTYPE_INTERFACE_ONLY (t))
+ return;
+
+ if (TYPE_SIZE (t) == NULL_TREE)
+ {
+ cp_error ("explicit instantiation of `%#T' before definition of template",
+ t);
+ return;
+ }
+
+ if (storage == NULL_TREE)
+ extern_p = 0;
+ else if (storage == ridpointers[(int) RID_EXTERN])
+ extern_p = 1;
+ else
+ {
+ cp_error ("storage class `%D' applied to template instantiation",
+ storage);
+ extern_p = 0;
+ }
+
+ SET_CLASSTYPE_EXPLICIT_INSTANTIATION (t);
+ CLASSTYPE_VTABLE_NEEDS_WRITING (t) = ! extern_p;
+ SET_CLASSTYPE_INTERFACE_KNOWN (t);
+ CLASSTYPE_INTERFACE_ONLY (t) = extern_p;
+ if (! extern_p)
+ {
+ CLASSTYPE_DEBUG_REQUESTED (t) = 1;
+ TYPE_DECL_SUPPRESS_DEBUG (TYPE_NAME (t)) = 0;
+ rest_of_type_compilation (t, 1);
+ }
+
+ /* this should really be done by instantiate_member_templates */
+ {
+ tree method = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (t), 0);
+ for (; method; method = TREE_CHAIN (method))
+ {
+ SET_DECL_EXPLICIT_INSTANTIATION (method);
+ TREE_PUBLIC (method) = 1;
+ DECL_EXTERNAL (method)
+ = (extern_p || (DECL_INLINE (method) && ! flag_implement_inlines));
+ }
+ }
+
+ /* and data member templates, too */
+}
+
+tree
+create_nested_upt (scope, name)
+ tree scope, name;
+{
+ tree t = make_lang_type (UNINSTANTIATED_P_TYPE);
+ tree d = build_decl (TYPE_DECL, name, t);
+
+ TYPE_NAME (t) = d;
+ TYPE_VALUES (t) = TYPE_VALUES (scope);
+ TYPE_CONTEXT (t) = scope;
+
+ pushdecl (d);
+ return d;
+}
diff --git a/gnu/usr.bin/cc/cc1plus/ptree.c b/gnu/usr.bin/cc/cc1plus/ptree.c
new file mode 100644
index 0000000..38e3c77
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/ptree.c
@@ -0,0 +1,167 @@
+/* Prints out trees in human readable form.
+ Copyright (C) 1992, 1993 Free Software Foundation, Inc.
+ Hacked by Michael Tiemann (tiemann@cygnus.com)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#include "tree.h"
+#include <stdio.h>
+#include "cp-tree.h"
+
+void
+print_lang_decl (file, node, indent)
+ FILE *file;
+ tree node;
+ int indent;
+{
+ if (!DECL_LANG_SPECIFIC (node))
+ return;
+ /* A FIELD_DECL only has the flags structure, which we aren't displaying
+ anyways. */
+ if (DECL_MUTABLE_P (node))
+ {
+ indent_to (file, indent + 3);
+ fprintf (file, " mutable ");
+ }
+ if (TREE_CODE (node) == FIELD_DECL)
+ return;
+ indent_to (file, indent + 3);
+ if (DECL_MAIN_VARIANT (node))
+ {
+ fprintf (file, " decl-main-variant ");
+ fprintf (file, HOST_PTR_PRINTF, DECL_MAIN_VARIANT (node));
+ }
+ if (DECL_PENDING_INLINE_INFO (node))
+ {
+ fprintf (file, " pending-inline-info ");
+ fprintf (file, HOST_PTR_PRINTF, DECL_PENDING_INLINE_INFO (node));
+ }
+ if (DECL_TEMPLATE_INFO (node))
+ {
+ fprintf (file, " template-info ");
+ fprintf (file, HOST_PTR_PRINTF, DECL_TEMPLATE_INFO (node));
+ }
+}
+
+void
+print_lang_type (file, node, indent)
+ FILE *file;
+ register tree node;
+ int indent;
+{
+ if (TREE_CODE (node) == TEMPLATE_TYPE_PARM)
+ {
+ print_node (file, "tinfo", TYPE_VALUES (node), indent + 4);
+ return;
+ }
+
+ if (TREE_CODE (node) == UNINSTANTIATED_P_TYPE)
+ {
+ print_node (file, "template", UPT_TEMPLATE (node), indent + 4);
+ print_node (file, "parameters", UPT_PARMS (node), indent + 4);
+ return;
+ }
+
+ if (! (TREE_CODE (node) == RECORD_TYPE
+ || TREE_CODE (node) == UNION_TYPE))
+ return;
+
+ if (!TYPE_LANG_SPECIFIC (node))
+ return;
+
+ indent_to (file, indent + 3);
+
+ if (TYPE_NEEDS_CONSTRUCTING (node))
+ fputs ( "needs-constructor", file);
+ if (TYPE_NEEDS_DESTRUCTOR (node))
+ fputs (" needs-destructor", file);
+ if (TYPE_HAS_DESTRUCTOR (node))
+ fputs (" ~X()", file);
+ if (TYPE_HAS_DEFAULT_CONSTRUCTOR (node))
+ fputs (" X()", file);
+ if (TYPE_HAS_CONVERSION (node))
+ fputs (" has-type-conversion", file);
+ if (TYPE_HAS_INT_CONVERSION (node))
+ fputs (" has-int-conversion", file);
+ if (TYPE_HAS_REAL_CONVERSION (node))
+ fputs (" has-float-conversion", file);
+ if (TYPE_HAS_INIT_REF (node))
+ {
+ if (TYPE_HAS_CONST_INIT_REF (node))
+ fputs (" X(constX&)", file);
+ else
+ fputs (" X(X&)", file);
+ }
+ if (TYPE_GETS_NEW (node) & 1)
+ fputs (" new", file);
+ if (TYPE_GETS_NEW (node) & 2)
+ fputs (" new[]", file);
+ if (TYPE_GETS_DELETE (node) & 1)
+ fputs (" delete", file);
+ if (TYPE_GETS_DELETE (node) & 2)
+ fputs (" delete[]", file);
+ if (TYPE_HAS_ASSIGNMENT (node))
+ fputs (" has=", file);
+ if (TYPE_HAS_ASSIGN_REF (node))
+ fputs (" this=(X&)", file);
+ if (TYPE_OVERLOADS_METHOD_CALL_EXPR (node))
+ fputs (" op->()", file);
+ if (TYPE_GETS_INIT_AGGR (node))
+ fputs (" gets X(X, ...)", file);
+ if (TYPE_OVERLOADS_CALL_EXPR (node))
+ fputs (" op()", file);
+ if (TYPE_OVERLOADS_ARRAY_REF (node))
+ fputs (" op[]", file);
+ if (TYPE_OVERLOADS_ARROW (node))
+ fputs (" op->", file);
+ if (TYPE_USES_MULTIPLE_INHERITANCE (node))
+ fputs (" uses-multiple-inheritance", file);
+
+ if (TREE_CODE (node) == RECORD_TYPE)
+ {
+ fprintf (file, " n_parents %d n_ancestors %d",
+ CLASSTYPE_N_BASECLASSES (node),
+ CLASSTYPE_N_SUPERCLASSES (node));
+ fprintf (file, " use_template=%d", CLASSTYPE_USE_TEMPLATE (node));
+ if (CLASSTYPE_INTERFACE_ONLY (node))
+ fprintf (file, " interface-only");
+ if (CLASSTYPE_INTERFACE_UNKNOWN (node))
+ fprintf (file, " interface-unknown");
+ print_node (file, "member-functions", CLASSTYPE_METHOD_VEC (node),
+ indent + 4);
+ print_node (file, "baselinks",
+ TYPE_BINFO_BASETYPES (node) ? CLASSTYPE_BASELINK_VEC (node) : NULL_TREE,
+ indent + 4);
+ }
+}
+
+void
+print_lang_identifier (file, node, indent)
+ FILE *file;
+ tree node;
+ int indent;
+{
+ print_node (file, "global", IDENTIFIER_GLOBAL_VALUE (node), indent + 4);
+ print_node (file, "class", IDENTIFIER_CLASS_VALUE (node), indent + 4);
+ print_node (file, "local", IDENTIFIER_LOCAL_VALUE (node), indent + 4);
+ print_node (file, "label", IDENTIFIER_LABEL_VALUE (node), indent + 4);
+ print_node (file, "template", IDENTIFIER_TEMPLATE (node), indent + 4);
+ print_node (file, "implicit", IDENTIFIER_IMPLICIT_DECL (node), indent + 4);
+ print_node (file, "error locus", IDENTIFIER_ERROR_LOCUS (node), indent + 4);
+}
diff --git a/gnu/usr.bin/cc/cc1plus/search.c b/gnu/usr.bin/cc/cc1plus/search.c
new file mode 100644
index 0000000..c4c6a4e
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/search.c
@@ -0,0 +1,3199 @@
+/* Breadth-first and depth-first routines for
+ searching multiple-inheritance lattice for GNU C++.
+ Copyright (C) 1987, 1989, 1992, 1993 Free Software Foundation, Inc.
+ Contributed by Michael Tiemann (tiemann@cygnus.com)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* High-level class interface. */
+
+#include "config.h"
+#include "tree.h"
+#include <stdio.h>
+#include "cp-tree.h"
+#include "obstack.h"
+#include "flags.h"
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+void init_search ();
+extern struct obstack *current_obstack;
+
+#include "stack.h"
+
+/* Obstack used for remembering decision points of breadth-first. */
+static struct obstack search_obstack;
+
+/* Methods for pushing and popping objects to and from obstacks. */
+struct stack_level *
+push_stack_level (obstack, tp, size)
+ struct obstack *obstack;
+ char *tp; /* Sony NewsOS 5.0 compiler doesn't like void * here. */
+ int size;
+{
+ struct stack_level *stack;
+ obstack_grow (obstack, tp, size);
+ stack = (struct stack_level *) ((char*)obstack_next_free (obstack) - size);
+ obstack_finish (obstack);
+ stack->obstack = obstack;
+ stack->first = (tree *) obstack_base (obstack);
+ stack->limit = obstack_room (obstack) / sizeof (tree *);
+ return stack;
+}
+
+struct stack_level *
+pop_stack_level (stack)
+ struct stack_level *stack;
+{
+ struct stack_level *tem = stack;
+ struct obstack *obstack = tem->obstack;
+ stack = tem->prev;
+ obstack_free (obstack, tem);
+ return stack;
+}
+
+#define search_level stack_level
+static struct search_level *search_stack;
+
+static tree lookup_field_1 ();
+static int lookup_fnfields_1 ();
+static void dfs_walk ();
+static int markedp ();
+static void dfs_unmark ();
+static void dfs_init_vbase_pointers ();
+
+static tree vbase_types;
+static tree vbase_decl, vbase_decl_ptr;
+static tree vbase_decl_ptr_intermediate;
+static tree vbase_init_result;
+
+/* Allocate a level of searching. */
+static struct search_level *
+push_search_level (stack, obstack)
+ struct stack_level *stack;
+ struct obstack *obstack;
+{
+ struct search_level tem;
+
+ tem.prev = stack;
+ return push_stack_level (obstack, (char *)&tem, sizeof (tem));
+}
+
+/* Discard a level of search allocation. */
+static struct search_level *
+pop_search_level (obstack)
+ struct stack_level *obstack;
+{
+ register struct search_level *stack = pop_stack_level (obstack);
+
+ return stack;
+}
+
+/* Search memoization. */
+struct type_level
+{
+ struct stack_level base;
+
+ /* First object allocated in obstack of entries. */
+ char *entries;
+
+ /* Number of types memoized in this context. */
+ int len;
+
+ /* Type being memoized; save this if we are saving
+ memoized contexts. */
+ tree type;
+};
+
+/* Obstack used for memoizing member and member function lookup. */
+
+static struct obstack type_obstack, type_obstack_entries;
+static struct type_level *type_stack;
+static tree _vptr_name;
+
+/* Make things that look like tree nodes, but allocate them
+ on type_obstack_entries. */
+static int my_tree_node_counter;
+static tree my_tree_cons (), my_build_string ();
+
+extern int flag_memoize_lookups, flag_save_memoized_contexts;
+
+/* Variables for gathering statistics. */
+static int my_memoized_entry_counter;
+static int memoized_fast_finds[2], memoized_adds[2], memoized_fast_rejects[2];
+static int memoized_fields_searched[2];
+static int n_fields_searched;
+static int n_calls_lookup_field, n_calls_lookup_field_1;
+static int n_calls_lookup_fnfields, n_calls_lookup_fnfields_1;
+static int n_calls_get_base_type;
+static int n_outer_fields_searched;
+static int n_contexts_saved;
+
+/* Local variables to help save memoization contexts. */
+static tree prev_type_memoized;
+static struct type_level *prev_type_stack;
+
+/* This list is used by push_class_decls to know what decls need to
+ be pushed into class scope. */
+static tree closed_envelopes = NULL_TREE;
+
+/* Allocate a level of type memoization context. */
+static struct type_level *
+push_type_level (stack, obstack)
+ struct stack_level *stack;
+ struct obstack *obstack;
+{
+ struct type_level tem;
+
+ tem.base.prev = stack;
+
+ obstack_finish (&type_obstack_entries);
+ tem.entries = (char *) obstack_base (&type_obstack_entries);
+ tem.len = 0;
+ tem.type = NULL_TREE;
+
+ return (struct type_level *)push_stack_level (obstack, (char *)&tem, sizeof (tem));
+}
+
+/* Discard a level of type memoization context. */
+
+static struct type_level *
+pop_type_level (stack)
+ struct type_level *stack;
+{
+ obstack_free (&type_obstack_entries, stack->entries);
+ return (struct type_level *)pop_stack_level ((struct stack_level *)stack);
+}
+
+/* Make something that looks like a TREE_LIST, but
+ do it on the type_obstack_entries obstack. */
+static tree
+my_tree_cons (purpose, value, chain)
+ tree purpose, value, chain;
+{
+ tree p = (tree)obstack_alloc (&type_obstack_entries, sizeof (struct tree_list));
+ ++my_tree_node_counter;
+ TREE_TYPE (p) = NULL_TREE;
+ ((HOST_WIDE_INT *)p)[3] = 0;
+ TREE_SET_CODE (p, TREE_LIST);
+ TREE_PURPOSE (p) = purpose;
+ TREE_VALUE (p) = value;
+ TREE_CHAIN (p) = chain;
+ return p;
+}
+
+static tree
+my_build_string (str)
+ char *str;
+{
+ tree p = (tree)obstack_alloc (&type_obstack_entries, sizeof (struct tree_string));
+ ++my_tree_node_counter;
+ TREE_TYPE (p) = 0;
+ ((int *)p)[3] = 0;
+ TREE_SET_CODE (p, STRING_CST);
+ TREE_STRING_POINTER (p) = str;
+ TREE_STRING_LENGTH (p) = strlen (str);
+ return p;
+}
+
+/* Memoizing machinery to make searches for multiple inheritance
+ reasonably efficient. */
+#define MEMOIZE_HASHSIZE 8
+typedef struct memoized_entry
+{
+ struct memoized_entry *chain;
+ int uid;
+ tree data_members[MEMOIZE_HASHSIZE];
+ tree function_members[MEMOIZE_HASHSIZE];
+} *ME;
+
+#define MEMOIZED_CHAIN(ENTRY) (((ME)ENTRY)->chain)
+#define MEMOIZED_UID(ENTRY) (((ME)ENTRY)->uid)
+#define MEMOIZED_FIELDS(ENTRY,INDEX) (((ME)ENTRY)->data_members[INDEX])
+#define MEMOIZED_FNFIELDS(ENTRY,INDEX) (((ME)ENTRY)->function_members[INDEX])
+/* The following is probably a lousy hash function. */
+#define MEMOIZED_HASH_FN(NODE) (((long)(NODE)>>4)&(MEMOIZE_HASHSIZE - 1))
+
+static struct memoized_entry *
+my_new_memoized_entry (chain)
+ struct memoized_entry *chain;
+{
+ struct memoized_entry *p =
+ (struct memoized_entry *)obstack_alloc (&type_obstack_entries,
+ sizeof (struct memoized_entry));
+ bzero (p, sizeof (struct memoized_entry));
+ MEMOIZED_CHAIN (p) = chain;
+ MEMOIZED_UID (p) = ++my_memoized_entry_counter;
+ return p;
+}
+
+/* Make an entry in the memoized table for type TYPE
+ that the entry for NAME is FIELD. */
+
+tree
+make_memoized_table_entry (type, name, function_p)
+ tree type, name;
+ int function_p;
+{
+ int index = MEMOIZED_HASH_FN (name);
+ tree entry, *prev_entry;
+
+ memoized_adds[function_p] += 1;
+ if (CLASSTYPE_MTABLE_ENTRY (type) == 0)
+ {
+ obstack_ptr_grow (&type_obstack, type);
+ obstack_blank (&type_obstack, sizeof (struct memoized_entry *));
+ CLASSTYPE_MTABLE_ENTRY (type) = (char *)my_new_memoized_entry ((struct memoized_entry *)0);
+ type_stack->len++;
+ if (type_stack->len * 2 >= type_stack->base.limit)
+ my_friendly_abort (88);
+ }
+ if (function_p)
+ prev_entry = &MEMOIZED_FNFIELDS (CLASSTYPE_MTABLE_ENTRY (type), index);
+ else
+ prev_entry = &MEMOIZED_FIELDS (CLASSTYPE_MTABLE_ENTRY (type), index);
+
+ entry = my_tree_cons (name, NULL_TREE, *prev_entry);
+ *prev_entry = entry;
+
+ /* Don't know the error message to give yet. */
+ TREE_TYPE (entry) = error_mark_node;
+
+ return entry;
+}
+
+/* When a new function or class context is entered, we build
+ a table of types which have been searched for members.
+ The table is an array (obstack) of types. When a type is
+ entered into the obstack, its CLASSTYPE_MTABLE_ENTRY
+ field is set to point to a new record, of type struct memoized_entry.
+
+ A non-NULL TREE_TYPE of the entry contains an access control error message.
+
+ The slots for the data members are arrays of tree nodes.
+ These tree nodes are lists, with the TREE_PURPOSE
+ of this list the known member name, and the TREE_VALUE
+ as the FIELD_DECL for the member.
+
+ For member functions, the TREE_PURPOSE is again the
+ name of the member functions for that class,
+ and the TREE_VALUE of the list is a pairs
+ whose TREE_PURPOSE is a member functions of this name,
+ and whose TREE_VALUE is a list of known argument lists this
+ member function has been called with. The TREE_TYPE of the pair,
+ if non-NULL, is an error message to print. */
+
+/* Tell search machinery that we are entering a new context, and
+ to update tables appropriately.
+
+ TYPE is the type of the context we are entering, which can
+ be NULL_TREE if we are not in a class's scope.
+
+ USE_OLD, if nonzero tries to use previous context. */
+void
+push_memoized_context (type, use_old)
+ tree type;
+ int use_old;
+{
+ int len;
+ tree *tem;
+
+ if (prev_type_stack)
+ {
+ if (use_old && prev_type_memoized == type)
+ {
+#ifdef GATHER_STATISTICS
+ n_contexts_saved++;
+#endif
+ type_stack = prev_type_stack;
+ prev_type_stack = 0;
+
+ tem = &type_stack->base.first[0];
+ len = type_stack->len;
+ while (len--)
+ CLASSTYPE_MTABLE_ENTRY (tem[len*2]) = (char *)tem[len*2+1];
+ return;
+ }
+ /* Otherwise, need to pop old stack here. */
+ type_stack = pop_type_level (prev_type_stack);
+ prev_type_memoized = 0;
+ prev_type_stack = 0;
+ }
+
+ type_stack = push_type_level ((struct stack_level *)type_stack,
+ &type_obstack);
+ type_stack->type = type;
+}
+
+/* Tell search machinery that we have left a context.
+ We do not currently save these contexts for later use.
+ If we wanted to, we could not use pop_search_level, since
+ poping that level allows the data we have collected to
+ be clobbered; a stack of obstacks would be needed. */
+void
+pop_memoized_context (use_old)
+ int use_old;
+{
+ int len;
+ tree *tem = &type_stack->base.first[0];
+
+ if (! flag_save_memoized_contexts)
+ use_old = 0;
+ else if (use_old)
+ {
+ len = type_stack->len;
+ while (len--)
+ tem[len*2+1] = (tree)CLASSTYPE_MTABLE_ENTRY (tem[len*2]);
+
+ prev_type_stack = type_stack;
+ prev_type_memoized = type_stack->type;
+ }
+
+ if (flag_memoize_lookups)
+ {
+ len = type_stack->len;
+ while (len--)
+ CLASSTYPE_MTABLE_ENTRY (tem[len*2])
+ = (char *)MEMOIZED_CHAIN (CLASSTYPE_MTABLE_ENTRY (tem[len*2]));
+ }
+ if (! use_old)
+ type_stack = pop_type_level (type_stack);
+ else
+ type_stack = (struct type_level *)type_stack->base.prev;
+}
+
+#if 0 /* unused */
+/* This is the newer recursive depth first search routine. */
+/* Return non-zero if PARENT is directly derived from TYPE. By directly
+ we mean it's only one step up the inheritance lattice. We check this
+ by walking horizontally across the types that TYPE directly inherits
+ from, to see if PARENT is among them. This is used by get_binfo and
+ by compute_access. */
+static int
+immediately_derived (parent, type)
+ tree parent, type;
+{
+ if (TYPE_BINFO (type))
+ {
+ tree binfos = BINFO_BASETYPES (TYPE_BINFO (type));
+ int i, n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+
+ for (i = 0; i < n_baselinks; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+
+ if (parent == BINFO_TYPE (base_binfo))
+ return 1;
+ }
+ }
+ return 0;
+}
+#endif
+
+/* Check whether the type given in BINFO is derived from PARENT. If
+ it isn't, return 0. If it is, but the derivation is MI-ambiguous
+ AND protect != 0, emit an error message and return error_mark_node.
+
+ Otherwise, if TYPE is derived from PARENT, return the actual base
+ information, unless a one of the protection violations below
+ occurs, in which case emit an error message and return error_mark_node.
+
+ If PROTECT is 1, then check if access to a public field of PARENT
+ would be private. Also check for ambiguity. */
+
+tree
+get_binfo (parent, binfo, protect)
+ register tree parent, binfo;
+ int protect;
+{
+ tree type;
+ int dist;
+ tree rval = NULL_TREE;
+
+ if (TREE_CODE (parent) == TREE_VEC)
+ parent = BINFO_TYPE (parent);
+ /* unions cannot participate in inheritance relationships */
+ else if (TREE_CODE (parent) == UNION_TYPE)
+ return NULL_TREE;
+ else if (TREE_CODE (parent) != RECORD_TYPE)
+ my_friendly_abort (89);
+
+ if (TREE_CODE (binfo) == TREE_VEC)
+ type = BINFO_TYPE (binfo);
+ else if (TREE_CODE (binfo) == RECORD_TYPE)
+ type = binfo;
+ else if (TREE_CODE (binfo) == UNION_TYPE)
+ return NULL_TREE;
+ else
+ my_friendly_abort (90);
+
+ dist = get_base_distance (parent, binfo, protect, &rval);
+
+ if (dist == -3)
+ {
+ cp_error ("fields of `%T' are inaccessible in `%T' due to private inheritance",
+ parent, type);
+ return error_mark_node;
+ }
+ else if (dist == -2 && protect)
+ {
+ cp_error ("type `%T' is ambiguous base class for type `%T'", parent,
+ type);
+ return error_mark_node;
+ }
+
+ return rval;
+}
+
+/* This is the newer depth first get_base_distance routine. */
+static int
+get_base_distance_recursive (binfo, depth, is_private, basetype_path, rval,
+ rval_private_ptr, new_binfo_ptr, parent, path_ptr,
+ protect, via_virtual_ptr, via_virtual)
+ tree binfo, basetype_path, *new_binfo_ptr, parent, *path_ptr;
+ int *rval_private_ptr, depth, is_private, rval, protect, *via_virtual_ptr,
+ via_virtual;
+{
+ tree binfos;
+ int i, n_baselinks;
+
+ if (BINFO_TYPE (binfo) == parent || binfo == parent)
+ {
+ if (rval == -1)
+ {
+ rval = depth;
+ *rval_private_ptr = is_private;
+ *new_binfo_ptr = binfo;
+ *via_virtual_ptr = via_virtual;
+ }
+ else
+ {
+ int same_object = (tree_int_cst_equal (BINFO_OFFSET (*new_binfo_ptr),
+ BINFO_OFFSET (binfo))
+ && *via_virtual_ptr && via_virtual);
+
+ if (*via_virtual_ptr && via_virtual==0)
+ {
+ *rval_private_ptr = is_private;
+ *new_binfo_ptr = binfo;
+ *via_virtual_ptr = via_virtual;
+ }
+ else if (same_object)
+ {
+ if (*rval_private_ptr && ! is_private)
+ {
+ *rval_private_ptr = is_private;
+ *new_binfo_ptr = binfo;
+ *via_virtual_ptr = via_virtual;
+ }
+ return rval;
+ }
+
+ rval = -2;
+ }
+ return rval;
+ }
+
+ binfos = BINFO_BASETYPES (binfo);
+ n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+ depth += 1;
+
+ /* Process base types. */
+ for (i = 0; i < n_baselinks; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+
+ /* Find any specific instance of a virtual base, when searching with
+ a binfo... */
+ if (BINFO_MARKED (base_binfo) == 0 || TREE_CODE (parent) == TREE_VEC)
+ {
+ int via_private
+ = (protect
+ && (is_private
+ || (!TREE_VIA_PUBLIC (base_binfo)
+ && !is_friend (BINFO_TYPE (binfo), current_scope ()))));
+ int this_virtual = via_virtual || TREE_VIA_VIRTUAL (base_binfo);
+ int was;
+
+ /* When searching for a non-virtual, we cannot mark
+ virtually found binfos. */
+ if (! this_virtual)
+ SET_BINFO_MARKED (base_binfo);
+
+#define WATCH_VALUES(rval, via_private) (rval == -1 ? 3 : via_private)
+
+ was = WATCH_VALUES (rval, *via_virtual_ptr);
+ rval = get_base_distance_recursive (base_binfo, depth, via_private,
+ binfo, rval, rval_private_ptr,
+ new_binfo_ptr, parent, path_ptr,
+ protect, via_virtual_ptr,
+ this_virtual);
+ /* watch for updates; only update if path is good. */
+ if (path_ptr && WATCH_VALUES (rval, *via_virtual_ptr) != was)
+ BINFO_INHERITANCE_CHAIN (base_binfo) = binfo;
+ if (rval == -2 && *via_virtual_ptr == 0)
+ return rval;
+
+#undef WATCH_VALUES
+
+ }
+ }
+
+ return rval;
+}
+
+/* Return the number of levels between type PARENT and the type given
+ in BINFO, following the leftmost path to PARENT not found along a
+ virtual path, if there are no real PARENTs (all come from virtual
+ base classes), then follow the leftmost path to PARENT.
+
+ Return -1 if TYPE is not derived from PARENT.
+ Return -2 if PARENT is an ambiguous base class of TYPE, and PROTECT is
+ non-negative.
+ Return -3 if PARENT is private to TYPE, and PROTECT is non-zero.
+
+ If PATH_PTR is non-NULL, then also build the list of types
+ from PARENT to TYPE, with TREE_VIA_VIRUAL and TREE_VIA_PUBLIC
+ set.
+
+ PARENT can also be a binfo, in which case that exact parent is found
+ and no other. convert_pointer_to_real uses this functionality.
+
+ If BINFO is a binfo, its BINFO_INHERITANCE_CHAIN will be left alone. */
+
+int
+get_base_distance (parent, binfo, protect, path_ptr)
+ register tree parent, binfo;
+ int protect;
+ tree *path_ptr;
+{
+ int rval;
+ int rval_private = 0;
+ tree type;
+ tree new_binfo = NULL_TREE;
+ int via_virtual;
+ int watch_access = protect;
+
+ if (TREE_CODE (parent) != TREE_VEC)
+ parent = TYPE_MAIN_VARIANT (parent);
+
+ if (TREE_CODE (binfo) == TREE_VEC)
+ type = BINFO_TYPE (binfo);
+ else if (IS_AGGR_TYPE_CODE (TREE_CODE (binfo)))
+ {
+ type = binfo;
+ binfo = TYPE_BINFO (type);
+
+ if (path_ptr)
+ BINFO_INHERITANCE_CHAIN (binfo) = NULL_TREE;
+ }
+ else
+ my_friendly_abort (92);
+
+ if (parent == type || parent == binfo)
+ {
+ /* If the distance is 0, then we don't really need
+ a path pointer, but we shouldn't let garbage go back. */
+ if (path_ptr)
+ *path_ptr = binfo;
+ return 0;
+ }
+
+ if (path_ptr)
+ watch_access = 1;
+
+ rval = get_base_distance_recursive (binfo, 0, 0, NULL_TREE, -1,
+ &rval_private, &new_binfo, parent,
+ path_ptr, watch_access, &via_virtual, 0);
+
+ dfs_walk (binfo, dfs_unmark, markedp);
+
+ /* Access restrictions don't count if we found an ambiguous basetype. */
+ if (rval == -2 && protect >= 0)
+ rval_private = 0;
+
+ if (rval && protect && rval_private)
+ return -3;
+
+ /* find real virtual base classes. */
+ if (rval == -1 && TREE_CODE (parent) == TREE_VEC
+ && parent == binfo_member (BINFO_TYPE (parent),
+ CLASSTYPE_VBASECLASSES (type)))
+ {
+ BINFO_INHERITANCE_CHAIN (parent) = binfo;
+ new_binfo = parent;
+ rval = 1;
+ }
+
+ if (path_ptr)
+ *path_ptr = new_binfo;
+ return rval;
+}
+
+/* Search for a member with name NAME in a multiple inheritance lattice
+ specified by TYPE. If it does not exist, return NULL_TREE.
+ If the member is ambiguously referenced, return `error_mark_node'.
+ Otherwise, return the FIELD_DECL. */
+
+/* Do a 1-level search for NAME as a member of TYPE. The caller must
+ figure out whether it can access this field. (Since it is only one
+ level, this is reasonable.) */
+static tree
+lookup_field_1 (type, name)
+ tree type, name;
+{
+ register tree field = TYPE_FIELDS (type);
+
+#ifdef GATHER_STATISTICS
+ n_calls_lookup_field_1++;
+#endif
+ while (field)
+ {
+#ifdef GATHER_STATISTICS
+ n_fields_searched++;
+#endif
+ if (DECL_NAME (field) == NULL_TREE
+ && TREE_CODE (TREE_TYPE (field)) == UNION_TYPE)
+ {
+ tree temp = lookup_field_1 (TREE_TYPE (field), name);
+ if (temp)
+ return temp;
+ }
+ if (DECL_NAME (field) == name)
+ {
+ if ((TREE_CODE(field) == VAR_DECL || TREE_CODE(field) == CONST_DECL)
+ && DECL_ASSEMBLER_NAME (field) != NULL)
+ GNU_xref_ref(current_function_decl,
+ IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (field)));
+ return field;
+ }
+ field = TREE_CHAIN (field);
+ }
+ /* Not found. */
+ if (name == _vptr_name)
+ {
+ /* Give the user what s/he thinks s/he wants. */
+ if (TYPE_VIRTUAL_P (type))
+ return CLASSTYPE_VFIELD (type);
+ }
+ return NULL_TREE;
+}
+
+/* There are a number of cases we need to be aware of here:
+ current_class_type current_function_decl
+ * global NULL NULL
+ * fn-local NULL SET
+ * class-local SET NULL
+ * class->fn SET SET
+ * fn->class SET SET
+
+ Those last two make life interesting. If we're in a function which is
+ itself inside a class, we need decls to go into the fn's decls (our
+ second case below). But if we're in a class and the class itself is
+ inside a function, we need decls to go into the decls for the class. To
+ achieve this last goal, we must see if, when both current_class_decl and
+ current_function_decl are set, the class was declared inside that
+ function. If so, we know to put the decls into the class's scope. */
+
+tree
+current_scope ()
+{
+ if (current_function_decl == NULL_TREE)
+ return current_class_type;
+ if (current_class_type == NULL_TREE)
+ return current_function_decl;
+ if (DECL_CLASS_CONTEXT (current_function_decl) == current_class_type)
+ return current_function_decl;
+
+ return current_class_type;
+}
+
+/* Compute the access of FIELD. This is done by computing
+ the access available to each type in BASETYPES (which comes
+ as a list of [via_public/basetype] in reverse order, namely base
+ class before derived class). The first one which defines a
+ access defines the access for the field. Otherwise, the
+ access of the field is that which occurs normally.
+
+ Uses global variables CURRENT_CLASS_TYPE and
+ CURRENT_FUNCTION_DECL to use friend relationships
+ if necessary.
+
+ This will be static when lookup_fnfield comes into this file.
+
+ access_public means that the field can be accessed by the current lexical
+ scope.
+
+ access_protected means that the field cannot be accessed by the current
+ lexical scope because it is protected.
+
+ access_private means that the field cannot be accessed by the current
+ lexical scope because it is private. */
+
+#if 0
+#define PUBLIC_RETURN return (DECL_PUBLIC (field) = 1), access_public
+#define PROTECTED_RETURN return (DECL_PROTECTED (field) = 1), access_protected
+#define PRIVATE_RETURN return (DECL_PRIVATE (field) = 1), access_private
+#else
+#define PUBLIC_RETURN return access_public
+#define PROTECTED_RETURN return access_protected
+#define PRIVATE_RETURN return access_private
+#endif
+
+#if 0
+/* Disabled with DECL_PUBLIC &c. */
+static tree previous_scope = NULL_TREE;
+#endif
+
+enum access_type
+compute_access (basetype_path, field)
+ tree basetype_path, field;
+{
+ enum access_type access;
+ tree types;
+ tree context;
+ int protected_ok, via_protected;
+#if 1
+ /* Replaces static decl above. */
+ tree previous_scope;
+#endif
+ int static_mem =
+ ((TREE_CODE (field) == FUNCTION_DECL && DECL_STATIC_FUNCTION_P (field))
+ || (TREE_CODE (field) != FUNCTION_DECL && TREE_STATIC (field)));
+
+ /* The field lives in the current class. */
+ if (BINFO_TYPE (basetype_path) == current_class_type)
+ return access_public;
+
+#if 0
+ /* Disabled until pushing function scope clears these out. If ever. */
+ /* Make these special cases fast. */
+ if (current_scope () == previous_scope)
+ {
+ if (DECL_PUBLIC (field))
+ return access_public;
+ if (DECL_PROTECTED (field))
+ return access_protected;
+ if (DECL_PRIVATE (field))
+ return access_private;
+ }
+#endif
+
+ previous_scope = current_scope ();
+
+ context = DECL_CLASS_CONTEXT (field);
+ if (context == NULL_TREE)
+ context = DECL_CONTEXT (field);
+
+ /* Fields coming from nested anonymous unions have their DECL_CLASS_CONTEXT
+ slot set to the union type rather than the record type containing
+ the anonymous union. In this case, DECL_FIELD_CONTEXT is correct. */
+ if (context && TREE_CODE (context) == UNION_TYPE
+ && ANON_AGGRNAME_P (TYPE_IDENTIFIER (context)))
+ context = DECL_FIELD_CONTEXT (field);
+
+ /* Virtual function tables are never private. But we should know that
+ we are looking for this, and not even try to hide it. */
+ if (DECL_NAME (field) && VFIELD_NAME_P (DECL_NAME (field)) == 1)
+ PUBLIC_RETURN;
+
+ /* Member found immediately within object. */
+ if (BINFO_INHERITANCE_CHAIN (basetype_path) == NULL_TREE)
+ {
+ /* Are we (or an enclosing scope) friends with the class that has
+ FIELD? */
+ if (is_friend (context, previous_scope))
+ PUBLIC_RETURN;
+
+ /* If it's private, it's private, you letch. */
+ if (TREE_PRIVATE (field))
+ PRIVATE_RETURN;
+
+ /* ARM $11.5. Member functions of a derived class can access the
+ non-static protected members of a base class only through a
+ pointer to the derived class, a reference to it, or an object
+ of it. Also any subsequently derived classes also have
+ access. */
+ else if (TREE_PROTECTED (field))
+ {
+ if (current_class_type
+ && static_mem
+ && ACCESSIBLY_DERIVED_FROM_P (context, current_class_type))
+ PUBLIC_RETURN;
+ else
+ PROTECTED_RETURN;
+ }
+ else
+ PUBLIC_RETURN;
+ }
+
+ /* must reverse more than one element */
+ basetype_path = reverse_path (basetype_path);
+ types = basetype_path;
+ via_protected = 0;
+ access = access_default;
+ protected_ok = static_mem && current_class_type
+ && ACCESSIBLY_DERIVED_FROM_P (BINFO_TYPE (types), current_class_type);
+
+ while (1)
+ {
+ tree member;
+ tree binfo = types;
+ tree type = BINFO_TYPE (binfo);
+ int private_ok = 0;
+
+ /* Friends of a class can see protected members of its bases.
+ Note that classes are their own friends. */
+ if (is_friend (type, previous_scope))
+ {
+ protected_ok = 1;
+ private_ok = 1;
+ }
+
+ member = purpose_member (type, DECL_ACCESS (field));
+ if (member)
+ {
+ access = (enum access_type) TREE_VALUE (member);
+ break;
+ }
+
+ types = BINFO_INHERITANCE_CHAIN (types);
+
+ /* If the next type was VIA_PROTECTED, then fields of all remaining
+ classes past that one are *at least* protected. */
+ if (types)
+ {
+ if (TREE_VIA_PROTECTED (types))
+ via_protected = 1;
+ else if (! TREE_VIA_PUBLIC (types) && ! private_ok)
+ {
+ access = access_private;
+ break;
+ }
+ }
+ else
+ break;
+ }
+ reverse_path (basetype_path);
+
+ /* No special visibilities apply. Use normal rules. */
+
+ if (access == access_default)
+ {
+ if (is_friend (context, previous_scope))
+ access = access_public;
+ else if (TREE_PRIVATE (field))
+ access = access_private;
+ else if (TREE_PROTECTED (field))
+ access = access_protected;
+ else
+ access = access_public;
+ }
+
+ if (access == access_public && via_protected)
+ access = access_protected;
+
+ if (access == access_protected && protected_ok)
+ access = access_public;
+
+#if 0
+ if (access == access_public)
+ DECL_PUBLIC (field) = 1;
+ else if (access == access_protected)
+ DECL_PROTECTED (field) = 1;
+ else if (access == access_private)
+ DECL_PRIVATE (field) = 1;
+ else my_friendly_abort (96);
+#endif
+ return access;
+}
+
+/* Routine to see if the sub-object denoted by the binfo PARENT can be
+ found as a base class and sub-object of the object denoted by
+ BINFO. This routine relies upon binfos not being shared, except
+ for binfos for virtual bases. */
+static int
+is_subobject_of_p (parent, binfo)
+ tree parent, binfo;
+{
+ tree binfos = BINFO_BASETYPES (binfo);
+ int i, n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+
+ if (parent == binfo)
+ return 1;
+
+ /* Process and/or queue base types. */
+ for (i = 0; i < n_baselinks; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+ if (TREE_VIA_VIRTUAL (base_binfo))
+ base_binfo = TYPE_BINFO (BINFO_TYPE (base_binfo));
+ if (is_subobject_of_p (parent, base_binfo))
+ return 1;
+ }
+ return 0;
+}
+
+/* See if a one FIELD_DECL hides another. This routine is meant to
+ correspond to ANSI working paper Sept 17, 1992 10p4. The two
+ binfos given are the binfos corresponding to the particular places
+ the FIELD_DECLs are found. This routine relies upon binfos not
+ being shared, except for virtual bases. */
+static int
+hides (hider_binfo, hidee_binfo)
+ tree hider_binfo, hidee_binfo;
+{
+ /* hider hides hidee, if hider has hidee as a base class and
+ the instance of hidee is a sub-object of hider. The first
+ part is always true is the second part is true.
+
+ When hider and hidee are the same (two ways to get to the exact
+ same member) we consider either one as hiding the other. */
+ return is_subobject_of_p (hidee_binfo, hider_binfo);
+}
+
+/* Very similar to lookup_fnfields_1 but it ensures that at least one
+ function was declared inside the class given by TYPE. It really should
+ only return functions that match the given TYPE. */
+static int
+lookup_fnfields_here (type, name)
+ tree type, name;
+{
+ int index = lookup_fnfields_1 (type, name);
+ tree fndecls;
+
+ if (index <= 0)
+ return index;
+ fndecls = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type), index);
+ while (fndecls)
+ {
+ if (TYPE_MAIN_VARIANT (DECL_CLASS_CONTEXT (fndecls))
+ == TYPE_MAIN_VARIANT (type))
+ return index;
+ fndecls = TREE_CHAIN (fndecls);
+ }
+ return -1;
+}
+
+/* Look for a field named NAME in an inheritance lattice dominated by
+ XBASETYPE. PROTECT is zero if we can avoid computing access
+ information, otherwise it is 1. WANT_TYPE is 1 when we should only
+ return TYPE_DECLs, if no TYPE_DECL can be found return NULL_TREE.
+
+ It was not clear what should happen if WANT_TYPE is set, and an
+ ambiguity is found. At least one use (lookup_name) to not see
+ the error. */
+tree
+lookup_field (xbasetype, name, protect, want_type)
+ register tree xbasetype, name;
+ int protect, want_type;
+{
+ int head = 0, tail = 0;
+ tree rval, rval_binfo = NULL_TREE, rval_binfo_h;
+ tree type, basetype_chain, basetype_path;
+ enum access_type this_v = access_default;
+ tree entry, binfo, binfo_h;
+ enum access_type own_access = access_default;
+ int vbase_name_p = VBASE_NAME_P (name);
+
+ /* rval_binfo is the binfo associated with the found member, note,
+ this can be set with useful information, even when rval is not
+ set, because it must deal with ALL members, not just non-function
+ members. It is used for ambiguity checking and the hidden
+ checks. Whereas rval is only set if a proper (not hidden)
+ non-function member is found. */
+
+ /* rval_binfo_h and binfo_h are binfo values used when we perform the
+ hiding checks, as virtual base classes may not be shared. The strategy
+ is we always go into the the binfo hierarchy owned by TYPE_BINFO of
+ virtual base classes, as we cross virtual base class lines. This way
+ we know that binfo of a virtual base class will always == itself when
+ found along any line. (mrs) */
+
+ char *errstr = 0;
+
+ /* Set this to nonzero if we don't know how to compute
+ accurate error messages for access control. */
+ int index = MEMOIZED_HASH_FN (name);
+
+ /* If we are looking for a constructor in a templated type, use the
+ unspecialized name, as that is how we store it. */
+ if (IDENTIFIER_TEMPLATE (name))
+ name = constructor_name (name);
+
+ if (TREE_CODE (xbasetype) == TREE_VEC)
+ {
+ type = BINFO_TYPE (xbasetype);
+ basetype_path = xbasetype;
+ }
+ else if (IS_AGGR_TYPE_CODE (TREE_CODE (xbasetype)))
+ {
+ type = xbasetype;
+ basetype_path = TYPE_BINFO (xbasetype);
+ BINFO_VIA_PUBLIC (basetype_path) = 1;
+ BINFO_INHERITANCE_CHAIN (basetype_path) = NULL_TREE;
+ }
+ else my_friendly_abort (97);
+
+ if (CLASSTYPE_MTABLE_ENTRY (type))
+ {
+ tree tem = MEMOIZED_FIELDS (CLASSTYPE_MTABLE_ENTRY (type), index);
+
+ while (tem && TREE_PURPOSE (tem) != name)
+ {
+ memoized_fields_searched[0]++;
+ tem = TREE_CHAIN (tem);
+ }
+ if (tem)
+ {
+ if (protect && TREE_TYPE (tem))
+ {
+ error (TREE_STRING_POINTER (TREE_TYPE (tem)),
+ IDENTIFIER_POINTER (name),
+ TYPE_NAME_STRING (DECL_FIELD_CONTEXT (TREE_VALUE (tem))));
+ return error_mark_node;
+ }
+ if (TREE_VALUE (tem) == NULL_TREE)
+ memoized_fast_rejects[0] += 1;
+ else
+ memoized_fast_finds[0] += 1;
+ return TREE_VALUE (tem);
+ }
+ }
+
+#ifdef GATHER_STATISTICS
+ n_calls_lookup_field++;
+#endif
+ if (protect && flag_memoize_lookups && ! global_bindings_p ())
+ entry = make_memoized_table_entry (type, name, 0);
+ else
+ entry = 0;
+
+ rval = lookup_field_1 (type, name);
+ if (rval || lookup_fnfields_here (type, name)>=0)
+ {
+ rval_binfo = basetype_path;
+ rval_binfo_h = rval_binfo;
+ }
+
+ if (rval && TREE_CODE (rval) != TYPE_DECL && want_type)
+ rval = NULL_TREE;
+
+ if (rval)
+ {
+ if (protect)
+ {
+ if (TREE_PRIVATE (rval) | TREE_PROTECTED (rval))
+ this_v = compute_access (basetype_path, rval);
+ if (TREE_CODE (rval) == CONST_DECL)
+ {
+ if (this_v == access_private)
+ errstr = "enum `%D' is a private value of class `%T'";
+ else if (this_v == access_protected)
+ errstr = "enum `%D' is a protected value of class `%T'";
+ }
+ else
+ {
+ if (this_v == access_private)
+ errstr = "member `%D' is a private member of class `%T'";
+ else if (this_v == access_protected)
+ errstr = "member `%D' is a protected member of class `%T'";
+ }
+ }
+
+ if (entry)
+ {
+ if (errstr)
+ {
+ /* This depends on behavior of lookup_field_1! */
+ tree error_string = my_build_string (errstr);
+ TREE_TYPE (entry) = error_string;
+ }
+ else
+ {
+ /* Let entry know there is no problem with this access. */
+ TREE_TYPE (entry) = NULL_TREE;
+ }
+ TREE_VALUE (entry) = rval;
+ }
+
+ if (errstr && protect)
+ {
+ cp_error (errstr, name, type);
+ return error_mark_node;
+ }
+ return rval;
+ }
+
+ basetype_chain = build_tree_list (NULL_TREE, basetype_path);
+ TREE_VIA_PUBLIC (basetype_chain) = TREE_VIA_PUBLIC (basetype_path);
+ TREE_VIA_PROTECTED (basetype_chain) = TREE_VIA_PROTECTED (basetype_path);
+ TREE_VIA_VIRTUAL (basetype_chain) = TREE_VIA_VIRTUAL (basetype_path);
+
+ /* The ambiguity check relies upon breadth first searching. */
+
+ search_stack = push_search_level (search_stack, &search_obstack);
+ binfo = basetype_path;
+ binfo_h = binfo;
+
+ while (1)
+ {
+ tree binfos = BINFO_BASETYPES (binfo);
+ int i, n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+ tree nval;
+
+ /* Process and/or queue base types. */
+ for (i = 0; i < n_baselinks; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+ if (BINFO_FIELDS_MARKED (base_binfo) == 0)
+ {
+ tree btypes;
+
+ SET_BINFO_FIELDS_MARKED (base_binfo);
+ btypes = my_tree_cons (NULL_TREE, base_binfo, basetype_chain);
+ TREE_VIA_PUBLIC (btypes) = TREE_VIA_PUBLIC (base_binfo);
+ TREE_VIA_PROTECTED (btypes) = TREE_VIA_PROTECTED (base_binfo);
+ TREE_VIA_VIRTUAL (btypes) = TREE_VIA_VIRTUAL (base_binfo);
+ if (TREE_VIA_VIRTUAL (base_binfo))
+ btypes = tree_cons (NULL_TREE,
+ TYPE_BINFO (BINFO_TYPE (TREE_VEC_ELT (BINFO_BASETYPES (binfo_h), i))),
+ btypes);
+ else
+ btypes = tree_cons (NULL_TREE,
+ TREE_VEC_ELT (BINFO_BASETYPES (binfo_h), i),
+ btypes);
+ obstack_ptr_grow (&search_obstack, btypes);
+ tail += 1;
+ if (tail >= search_stack->limit)
+ my_friendly_abort (98);
+ }
+ }
+
+ /* Process head of queue, if one exists. */
+ if (head >= tail)
+ break;
+
+ basetype_chain = search_stack->first[head++];
+ binfo_h = TREE_VALUE (basetype_chain);
+ basetype_chain = TREE_CHAIN (basetype_chain);
+ basetype_path = TREE_VALUE (basetype_chain);
+ if (TREE_CHAIN (basetype_chain))
+ BINFO_INHERITANCE_CHAIN (basetype_path) = TREE_VALUE (TREE_CHAIN (basetype_chain));
+ else
+ BINFO_INHERITANCE_CHAIN (basetype_path) = NULL_TREE;
+
+ binfo = basetype_path;
+ type = BINFO_TYPE (binfo);
+
+ /* See if we can find NAME in TYPE. If RVAL is nonzero,
+ and we do find NAME in TYPE, verify that such a second
+ sighting is in fact legal. */
+
+ nval = lookup_field_1 (type, name);
+
+ if (nval || lookup_fnfields_here (type, name)>=0)
+ {
+ if (nval && nval == rval && SHARED_MEMBER_P (nval))
+ {
+ /* This is ok, the member found is the same [class.ambig] */
+ }
+ else if (rval_binfo && hides (rval_binfo_h, binfo_h))
+ {
+ /* This is ok, the member found is in rval_binfo, not
+ here (binfo). */
+ }
+ else if (rval_binfo==NULL_TREE || hides (binfo_h, rval_binfo_h))
+ {
+ /* This is ok, the member found is here (binfo), not in
+ rval_binfo. */
+ if (nval)
+ {
+ rval = nval;
+ if (entry || protect)
+ this_v = compute_access (basetype_path, rval);
+ /* These may look ambiguous, but they really are not. */
+ if (vbase_name_p)
+ break;
+ }
+ else
+ {
+ /* Undo finding it before, as something else hides it. */
+ rval = NULL_TREE;
+ }
+ rval_binfo = binfo;
+ rval_binfo_h = binfo_h;
+ }
+ else
+ {
+ /* This is ambiguous. */
+ errstr = "request for member `%D' is ambiguous";
+ protect = 2;
+ break;
+ }
+ }
+ }
+ {
+ tree *tp = search_stack->first;
+ tree *search_tail = tp + tail;
+
+ if (entry)
+ TREE_VALUE (entry) = rval;
+
+ if (want_type && (rval == NULL_TREE || TREE_CODE (rval) != TYPE_DECL))
+ {
+ rval = NULL_TREE;
+ errstr = 0;
+ }
+
+ /* If this FIELD_DECL defines its own access level, deal with that. */
+ if (rval && errstr == 0
+ && ((protect&1) || entry)
+ && DECL_LANG_SPECIFIC (rval)
+ && DECL_ACCESS (rval))
+ {
+ while (tp < search_tail)
+ {
+ /* If is possible for one of the derived types on the path to
+ have defined special access for this field. Look for such
+ declarations and report an error if a conflict is found. */
+ enum access_type new_v;
+
+ if (this_v != access_default)
+ new_v = compute_access (TREE_VALUE (TREE_CHAIN (*tp)), rval);
+ if (this_v != access_default && new_v != this_v)
+ {
+ errstr = "conflicting access to member `%D'";
+ this_v = access_default;
+ }
+ own_access = new_v;
+ CLEAR_BINFO_FIELDS_MARKED (TREE_VALUE (TREE_CHAIN (*tp)));
+ tp += 1;
+ }
+ }
+ else
+ {
+ while (tp < search_tail)
+ {
+ CLEAR_BINFO_FIELDS_MARKED (TREE_VALUE (TREE_CHAIN (*tp)));
+ tp += 1;
+ }
+ }
+ }
+ search_stack = pop_search_level (search_stack);
+
+ if (errstr == 0)
+ {
+ if (own_access == access_private)
+ errstr = "member `%D' declared private";
+ else if (own_access == access_protected)
+ errstr = "member `%D' declared protected";
+ else if (this_v == access_private)
+ errstr = TREE_PRIVATE (rval)
+ ? "member `%D' is private"
+ : "member `%D' is from private base class";
+ else if (this_v == access_protected)
+ errstr = TREE_PROTECTED (rval)
+ ? "member `%D' is protected"
+ : "member `%D' is from protected base class";
+ }
+
+ if (entry)
+ {
+ if (errstr)
+ {
+ tree error_string = my_build_string (errstr);
+ /* Save error message with entry. */
+ TREE_TYPE (entry) = error_string;
+ }
+ else
+ {
+ /* Mark entry as having no error string. */
+ TREE_TYPE (entry) = NULL_TREE;
+ }
+ }
+
+ if (errstr && protect)
+ {
+ cp_error (errstr, name, type);
+ rval = error_mark_node;
+ }
+ return rval;
+}
+
+/* Try to find NAME inside a nested class. */
+tree
+lookup_nested_field (name, complain)
+ tree name;
+ int complain;
+{
+ register tree t;
+
+ tree id = NULL_TREE;
+ if (TREE_CHAIN (current_class_type))
+ {
+ /* Climb our way up the nested ladder, seeing if we're trying to
+ modify a field in an enclosing class. If so, we should only
+ be able to modify if it's static. */
+ for (t = TREE_CHAIN (current_class_type);
+ t && DECL_CONTEXT (t);
+ t = TREE_CHAIN (DECL_CONTEXT (t)))
+ {
+ if (TREE_CODE (DECL_CONTEXT (t)) != RECORD_TYPE)
+ break;
+
+ /* N.B.: lookup_field will do the access checking for us */
+ id = lookup_field (DECL_CONTEXT (t), name, complain, 0);
+ if (id == error_mark_node)
+ {
+ id = NULL_TREE;
+ continue;
+ }
+
+ if (id != NULL_TREE)
+ {
+ if (TREE_CODE (id) == FIELD_DECL
+ && ! TREE_STATIC (id)
+ && TREE_TYPE (id) != error_mark_node)
+ {
+ if (complain)
+ {
+ /* At parse time, we don't want to give this error, since
+ we won't have enough state to make this kind of
+ decision properly. But there are times (e.g., with
+ enums in nested classes) when we do need to call
+ this fn at parse time. So, in those cases, we pass
+ complain as a 0 and just return a NULL_TREE. */
+ error ("assignment to non-static member `%s' of enclosing class `%s'",
+ lang_printable_name (id),
+ IDENTIFIER_POINTER (TYPE_IDENTIFIER
+ (DECL_CONTEXT (t))));
+ /* Mark this for do_identifier(). It would otherwise
+ claim that the variable was undeclared. */
+ TREE_TYPE (id) = error_mark_node;
+ }
+ else
+ {
+ id = NULL_TREE;
+ continue;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ return id;
+}
+
+/* TYPE is a class type. Return the index of the fields within
+ the method vector with name NAME, or -1 is no such field exists. */
+static int
+lookup_fnfields_1 (type, name)
+ tree type, name;
+{
+ register tree method_vec = CLASSTYPE_METHOD_VEC (type);
+
+ if (method_vec != 0)
+ {
+ register tree *methods = &TREE_VEC_ELT (method_vec, 0);
+ register tree *end = TREE_VEC_END (method_vec);
+
+#ifdef GATHER_STATISTICS
+ n_calls_lookup_fnfields_1++;
+#endif
+ if (*methods && name == constructor_name (type))
+ return 0;
+
+ while (++methods != end)
+ {
+#ifdef GATHER_STATISTICS
+ n_outer_fields_searched++;
+#endif
+ if (DECL_NAME (*methods) == name)
+ break;
+ }
+ if (methods != end)
+ return methods - &TREE_VEC_ELT (method_vec, 0);
+ }
+
+ return -1;
+}
+
+/* Starting from BASETYPE, return a TREE_BASELINK-like object
+ which gives the following information (in a list):
+
+ TREE_TYPE: list of basetypes needed to get to...
+ TREE_VALUE: list of all functions in of given type
+ which have name NAME.
+
+ No access information is computed by this function,
+ other then to adorn the list of basetypes with
+ TREE_VIA_PUBLIC.
+
+ If there are two ways to find a name (two members), if COMPLAIN is
+ non-zero, then error_mark_node is returned, and an error message is
+ printed, otherwise, just an error_mark_node is returned.
+
+ As a special case, is COMPLAIN is -1, we don't complain, and we
+ don't return error_mark_node, but rather the complete list of
+ virtuals. This is used by get_virtuals_named_this. */
+tree
+lookup_fnfields (basetype_path, name, complain)
+ tree basetype_path, name;
+ int complain;
+{
+ int head = 0, tail = 0;
+ tree type, rval, rval_binfo = NULL_TREE, rvals = NULL_TREE, rval_binfo_h;
+ tree entry, binfo, basetype_chain, binfo_h;
+ int find_all = 0;
+
+ /* rval_binfo is the binfo associated with the found member, note,
+ this can be set with useful information, even when rval is not
+ set, because it must deal with ALL members, not just function
+ members. It is used for ambiguity checking and the hidden
+ checks. Whereas rval is only set if a proper (not hidden)
+ function member is found. */
+
+ /* rval_binfo_h and binfo_h are binfo values used when we perform the
+ hiding checks, as virtual base classes may not be shared. The strategy
+ is we always go into the the binfo hierarchy owned by TYPE_BINFO of
+ virtual base classes, as we cross virtual base class lines. This way
+ we know that binfo of a virtual base class will always == itself when
+ found along any line. (mrs) */
+
+ /* For now, don't try this. */
+ int protect = complain;
+
+ char *errstr = 0;
+
+ /* Set this to nonzero if we don't know how to compute
+ accurate error messages for access control. */
+ int index = MEMOIZED_HASH_FN (name);
+
+ if (complain == -1)
+ {
+ find_all = 1;
+ protect = complain = 0;
+ }
+
+ /* If we are looking for a constructor in a templated type, use the
+ unspecialized name, as that is how we store it. */
+ if (IDENTIFIER_TEMPLATE (name))
+ name = constructor_name (name);
+
+ binfo = basetype_path;
+ binfo_h = binfo;
+ type = BINFO_TYPE (basetype_path);
+
+ /* The memoization code is in need of maintenance. */
+ if (!find_all && CLASSTYPE_MTABLE_ENTRY (type))
+ {
+ tree tem = MEMOIZED_FNFIELDS (CLASSTYPE_MTABLE_ENTRY (type), index);
+
+ while (tem && TREE_PURPOSE (tem) != name)
+ {
+ memoized_fields_searched[1]++;
+ tem = TREE_CHAIN (tem);
+ }
+ if (tem)
+ {
+ if (protect && TREE_TYPE (tem))
+ {
+ error (TREE_STRING_POINTER (TREE_TYPE (tem)),
+ IDENTIFIER_POINTER (name),
+ TYPE_NAME_STRING (DECL_CLASS_CONTEXT (TREE_VALUE (TREE_VALUE (tem)))));
+ return error_mark_node;
+ }
+ if (TREE_VALUE (tem) == NULL_TREE)
+ {
+ memoized_fast_rejects[1] += 1;
+ return NULL_TREE;
+ }
+ else
+ {
+ /* Want to return this, but we must make sure
+ that access information is consistent. */
+ tree baselink = TREE_VALUE (tem);
+ tree memoized_basetypes = TREE_PURPOSE (baselink);
+ tree these_basetypes = basetype_path;
+ while (memoized_basetypes && these_basetypes)
+ {
+ memoized_fields_searched[1]++;
+ if (TREE_VALUE (memoized_basetypes) != these_basetypes)
+ break;
+ memoized_basetypes = TREE_CHAIN (memoized_basetypes);
+ these_basetypes = BINFO_INHERITANCE_CHAIN (these_basetypes);
+ }
+ /* The following statement is true only when both are NULL. */
+ if (memoized_basetypes == these_basetypes)
+ {
+ memoized_fast_finds[1] += 1;
+ return TREE_VALUE (tem);
+ }
+ /* else, we must re-find this field by hand. */
+ baselink = tree_cons (basetype_path, TREE_VALUE (baselink), TREE_CHAIN (baselink));
+ return baselink;
+ }
+ }
+ }
+
+#ifdef GATHER_STATISTICS
+ n_calls_lookup_fnfields++;
+#endif
+ if (protect && flag_memoize_lookups && ! global_bindings_p ())
+ entry = make_memoized_table_entry (type, name, 1);
+ else
+ entry = 0;
+
+ index = lookup_fnfields_here (type, name);
+ if (index >= 0 || lookup_field_1 (type, name))
+ {
+ rval_binfo = basetype_path;
+ rval_binfo_h = rval_binfo;
+ }
+
+ if (index >= 0)
+ {
+ rval = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type), index);
+ rvals = my_tree_cons (basetype_path, rval, rvals);
+ if (BINFO_BASETYPES (binfo) && CLASSTYPE_BASELINK_VEC (type))
+ TREE_TYPE (rvals) = TREE_VEC_ELT (CLASSTYPE_BASELINK_VEC (type), index);
+
+ if (entry)
+ {
+ TREE_VALUE (entry) = rvals;
+ TREE_TYPE (entry) = NULL_TREE;
+ }
+
+ return rvals;
+ }
+ rval = NULL_TREE;
+
+ if (basetype_path == TYPE_BINFO (type))
+ {
+ basetype_chain = CLASSTYPE_BINFO_AS_LIST (type);
+ TREE_VIA_PUBLIC (basetype_chain) = 1;
+ BINFO_VIA_PUBLIC (basetype_path) = 1;
+ BINFO_INHERITANCE_CHAIN (basetype_path) = NULL_TREE;
+ }
+ else
+ {
+ basetype_chain = build_tree_list (NULL_TREE, basetype_path);
+ TREE_VIA_PUBLIC (basetype_chain) = TREE_VIA_PUBLIC (basetype_path);
+ TREE_VIA_PROTECTED (basetype_chain) = TREE_VIA_PROTECTED (basetype_path);
+ TREE_VIA_VIRTUAL (basetype_chain) = TREE_VIA_VIRTUAL (basetype_path);
+ }
+
+ /* The ambiguity check relies upon breadth first searching. */
+
+ search_stack = push_search_level (search_stack, &search_obstack);
+ binfo = basetype_path;
+ binfo_h = binfo;
+
+ while (1)
+ {
+ tree binfos = BINFO_BASETYPES (binfo);
+ int i, n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+ int index;
+
+ /* Process and/or queue base types. */
+ for (i = 0; i < n_baselinks; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+ if (BINFO_FIELDS_MARKED (base_binfo) == 0)
+ {
+ tree btypes;
+
+ SET_BINFO_FIELDS_MARKED (base_binfo);
+ btypes = my_tree_cons (NULL_TREE, base_binfo, basetype_chain);
+ TREE_VIA_PUBLIC (btypes) = TREE_VIA_PUBLIC (base_binfo);
+ TREE_VIA_PROTECTED (btypes) = TREE_VIA_PROTECTED (base_binfo);
+ TREE_VIA_VIRTUAL (btypes) = TREE_VIA_VIRTUAL (base_binfo);
+ if (TREE_VIA_VIRTUAL (base_binfo))
+ btypes = tree_cons (NULL_TREE,
+ TYPE_BINFO (BINFO_TYPE (TREE_VEC_ELT (BINFO_BASETYPES (binfo_h), i))),
+ btypes);
+ else
+ btypes = tree_cons (NULL_TREE,
+ TREE_VEC_ELT (BINFO_BASETYPES (binfo_h), i),
+ btypes);
+ obstack_ptr_grow (&search_obstack, btypes);
+ tail += 1;
+ if (tail >= search_stack->limit)
+ my_friendly_abort (99);
+ }
+ }
+
+ /* Process head of queue, if one exists. */
+ if (head >= tail)
+ break;
+
+ basetype_chain = search_stack->first[head++];
+ binfo_h = TREE_VALUE (basetype_chain);
+ basetype_chain = TREE_CHAIN (basetype_chain);
+ basetype_path = TREE_VALUE (basetype_chain);
+ if (TREE_CHAIN (basetype_chain))
+ BINFO_INHERITANCE_CHAIN (basetype_path) = TREE_VALUE (TREE_CHAIN (basetype_chain));
+ else
+ BINFO_INHERITANCE_CHAIN (basetype_path) = NULL_TREE;
+
+ binfo = basetype_path;
+ type = BINFO_TYPE (binfo);
+
+ /* See if we can find NAME in TYPE. If RVAL is nonzero,
+ and we do find NAME in TYPE, verify that such a second
+ sighting is in fact legal. */
+
+ index = lookup_fnfields_here (type, name);
+
+ if (index >= 0 || (lookup_field_1 (type, name)!=NULL_TREE && !find_all))
+ {
+ if (rval_binfo && !find_all && hides (rval_binfo_h, binfo_h))
+ {
+ /* This is ok, the member found is in rval_binfo, not
+ here (binfo). */
+ }
+ else if (rval_binfo==NULL_TREE || find_all || hides (binfo_h, rval_binfo_h))
+ {
+ /* This is ok, the member found is here (binfo), not in
+ rval_binfo. */
+ if (index >= 0)
+ {
+ rval = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type), index);
+ /* Note, rvals can only be previously set if find_all is
+ true. */
+ rvals = my_tree_cons (basetype_path, rval, rvals);
+ if (TYPE_BINFO_BASETYPES (type)
+ && CLASSTYPE_BASELINK_VEC (type))
+ TREE_TYPE (rvals) = TREE_VEC_ELT (CLASSTYPE_BASELINK_VEC (type), index);
+ }
+ else
+ {
+ /* Undo finding it before, as something else hides it. */
+ rval = NULL_TREE;
+ rvals = NULL_TREE;
+ }
+ rval_binfo = binfo;
+ rval_binfo_h = binfo_h;
+ }
+ else
+ {
+ /* This is ambiguous. */
+ errstr = "request for method `%D' is ambiguous";
+ rvals = error_mark_node;
+ break;
+ }
+ }
+ }
+ {
+ tree *tp = search_stack->first;
+ tree *search_tail = tp + tail;
+
+ while (tp < search_tail)
+ {
+ CLEAR_BINFO_FIELDS_MARKED (TREE_VALUE (TREE_CHAIN (*tp)));
+ tp += 1;
+ }
+ }
+ search_stack = pop_search_level (search_stack);
+
+ if (entry)
+ {
+ if (errstr)
+ {
+ tree error_string = my_build_string (errstr);
+ /* Save error message with entry. */
+ TREE_TYPE (entry) = error_string;
+ }
+ else
+ {
+ /* Mark entry as having no error string. */
+ TREE_TYPE (entry) = NULL_TREE;
+ TREE_VALUE (entry) = rvals;
+ }
+ }
+
+ if (errstr && protect)
+ {
+ cp_error (errstr, name);
+ rvals = error_mark_node;
+ }
+
+ return rvals;
+}
+
+/* BREADTH-FIRST SEARCH ROUTINES. */
+
+/* Search a multiple inheritance hierarchy by breadth-first search.
+
+ TYPE is an aggregate type, possibly in a multiple-inheritance hierarchy.
+ TESTFN is a function, which, if true, means that our condition has been met,
+ and its return value should be returned.
+ QFN, if non-NULL, is a predicate dictating whether the type should
+ even be queued. */
+
+HOST_WIDE_INT
+breadth_first_search (binfo, testfn, qfn)
+ tree binfo;
+ int (*testfn)();
+ int (*qfn)();
+{
+ int head = 0, tail = 0;
+ int rval = 0;
+
+ search_stack = push_search_level (search_stack, &search_obstack);
+
+ while (1)
+ {
+ tree binfos = BINFO_BASETYPES (binfo);
+ int n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+ int i;
+
+ /* Process and/or queue base types. */
+ for (i = 0; i < n_baselinks; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+
+ if (BINFO_MARKED (base_binfo) == 0
+ && (qfn == 0 || (*qfn) (binfo, i)))
+ {
+ SET_BINFO_MARKED (base_binfo);
+ obstack_ptr_grow (&search_obstack, binfo);
+ obstack_ptr_grow (&search_obstack, (HOST_WIDE_INT) i);
+ tail += 2;
+ if (tail >= search_stack->limit)
+ my_friendly_abort (100);
+ }
+ }
+ /* Process head of queue, if one exists. */
+ if (head >= tail)
+ {
+ rval = 0;
+ break;
+ }
+
+ binfo = search_stack->first[head++];
+ i = (HOST_WIDE_INT) search_stack->first[head++];
+ if (rval = (*testfn) (binfo, i))
+ break;
+ binfo = BINFO_BASETYPE (binfo, i);
+ }
+ {
+ tree *tp = search_stack->first;
+ tree *search_tail = tp + tail;
+ while (tp < search_tail)
+ {
+ tree binfo = *tp++;
+ int i = (HOST_WIDE_INT)(*tp++);
+ CLEAR_BINFO_MARKED (BINFO_BASETYPE (binfo, i));
+ }
+ }
+
+ search_stack = pop_search_level (search_stack);
+ return rval;
+}
+
+/* Functions to use in breadth first searches. */
+typedef tree (*pft)();
+typedef int (*pfi)();
+
+int tree_needs_constructor_p (binfo, i)
+ tree binfo;
+ int i;
+{
+ tree basetype;
+ my_friendly_assert (i != 0, 296);
+ basetype = BINFO_TYPE (BINFO_BASETYPE (binfo, i));
+ return TYPE_NEEDS_CONSTRUCTING (basetype);
+}
+
+static tree declarator;
+
+static tree
+get_virtuals_named_this (binfo)
+ tree binfo;
+{
+ tree fields;
+
+ fields = lookup_fnfields (binfo, declarator, -1);
+ /* fields cannot be error_mark_node */
+
+ if (fields == 0)
+ return 0;
+
+ /* Get to the function decls, and return the first virtual function
+ with this name, if there is one. */
+ while (fields)
+ {
+ tree fndecl;
+
+ for (fndecl = TREE_VALUE (fields); fndecl; fndecl = DECL_CHAIN (fndecl))
+ if (DECL_VINDEX (fndecl))
+ return fields;
+ fields = next_baselink (fields);
+ }
+ return NULL_TREE;
+}
+
+static tree get_virtual_destructor (binfo, i)
+ tree binfo;
+ int i;
+{
+ tree type = BINFO_TYPE (binfo);
+ if (i >= 0)
+ type = BINFO_TYPE (TREE_VEC_ELT (BINFO_BASETYPES (binfo), i));
+ if (TYPE_HAS_DESTRUCTOR (type)
+ && DECL_VINDEX (TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type), 0)))
+ return TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type), 0);
+ return 0;
+}
+
+int tree_has_any_destructor_p (binfo, i)
+ tree binfo;
+ int i;
+{
+ tree type = BINFO_TYPE (binfo);
+ if (i >= 0)
+ type = BINFO_TYPE (TREE_VEC_ELT (BINFO_BASETYPES (binfo), i));
+ return TYPE_NEEDS_DESTRUCTOR (type);
+}
+
+/* Given a class type TYPE, and a function decl FNDECL, look for a
+ virtual function in TYPE's hierarchy which FNDECL could match as a
+ virtual function. It doesn't matter which one we find.
+
+ DTORP is nonzero if we are looking for a destructor. Destructors
+ need special treatment because they do not match by name. */
+tree
+get_matching_virtual (binfo, fndecl, dtorp)
+ tree binfo, fndecl;
+ int dtorp;
+{
+ tree tmp = NULL_TREE;
+
+ /* Breadth first search routines start searching basetypes
+ of TYPE, so we must perform first ply of search here. */
+ if (dtorp)
+ {
+ if (tree_has_any_destructor_p (binfo, -1))
+ tmp = get_virtual_destructor (binfo, -1);
+
+ if (tmp)
+ return tmp;
+
+ tmp = (tree) breadth_first_search (binfo,
+ (pfi) get_virtual_destructor,
+ tree_has_any_destructor_p);
+ return tmp;
+ }
+ else
+ {
+ tree drettype, dtypes, btypes, instptr_type;
+ tree basetype = DECL_CLASS_CONTEXT (fndecl);
+ tree baselink, best = NULL_TREE;
+ tree name = DECL_ASSEMBLER_NAME (fndecl);
+
+ declarator = DECL_NAME (fndecl);
+ if (IDENTIFIER_VIRTUAL_P (declarator) == 0)
+ return NULL_TREE;
+
+ drettype = TREE_TYPE (TREE_TYPE (fndecl));
+ dtypes = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+ if (DECL_STATIC_FUNCTION_P (fndecl))
+ instptr_type = NULL_TREE;
+ else
+ instptr_type = TREE_TYPE (TREE_VALUE (dtypes));
+
+ for (baselink = get_virtuals_named_this (binfo);
+ baselink; baselink = next_baselink (baselink))
+ {
+ for (tmp = TREE_VALUE (baselink); tmp; tmp = DECL_CHAIN (tmp))
+ {
+ if (! DECL_VINDEX (tmp))
+ continue;
+
+ btypes = TYPE_ARG_TYPES (TREE_TYPE (tmp));
+ if (instptr_type == NULL_TREE)
+ {
+ if (compparms (TREE_CHAIN (btypes), dtypes, 3))
+ /* Caller knows to give error in this case. */
+ return tmp;
+ return NULL_TREE;
+ }
+
+ if ((TYPE_READONLY (TREE_TYPE (TREE_VALUE (btypes)))
+ == TYPE_READONLY (instptr_type))
+ && compparms (TREE_CHAIN (btypes), TREE_CHAIN (dtypes), 3))
+ {
+ if (IDENTIFIER_ERROR_LOCUS (name) == NULL_TREE
+ && ! comptypes (TREE_TYPE (TREE_TYPE (tmp)), drettype, 1))
+ {
+ cp_error ("conflicting return type specified for virtual function `%#D'", fndecl);
+ cp_error ("overriding definition as `%#D'", tmp);
+ SET_IDENTIFIER_ERROR_LOCUS (name, basetype);
+ }
+ break;
+ }
+ }
+ if (tmp)
+ {
+ best = tmp;
+ break;
+ }
+ }
+ if (best == NULL_TREE && warn_overloaded_virtual)
+ cp_warning_at ("conflicting specification deriving virtual function `%D'", fndecl);
+
+ return best;
+ }
+}
+
+/* Return the list of virtual functions which are abstract in type
+ TYPE that come from non virtual base classes. See
+ expand_direct_vtbls_init for the style of search we do. */
+static tree
+get_abstract_virtuals_1 (binfo, do_self, abstract_virtuals)
+ tree binfo, abstract_virtuals;
+ int do_self;
+{
+ tree binfos = BINFO_BASETYPES (binfo);
+ int i, n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+
+ for (i = 0; i < n_baselinks; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+ int is_not_base_vtable =
+ i != CLASSTYPE_VFIELD_PARENT (BINFO_TYPE (binfo));
+ if (! TREE_VIA_VIRTUAL (base_binfo))
+ abstract_virtuals
+ = get_abstract_virtuals_1 (base_binfo, is_not_base_vtable,
+ abstract_virtuals);
+ }
+ /* Should we use something besides CLASSTYPE_VFIELDS? */
+ if (do_self && CLASSTYPE_VFIELDS (BINFO_TYPE (binfo)))
+ {
+ tree tmp = TREE_CHAIN (BINFO_VIRTUALS (binfo));
+
+ /* Get around dossier entry if there is one. */
+ if (flag_dossier)
+ tmp = TREE_CHAIN (tmp);
+
+ while (tmp)
+ {
+ tree base_pfn = FNADDR_FROM_VTABLE_ENTRY (TREE_VALUE (tmp));
+ tree base_fndecl = TREE_OPERAND (base_pfn, 0);
+ if (DECL_ABSTRACT_VIRTUAL_P (base_fndecl))
+ abstract_virtuals = tree_cons (NULL_TREE, base_fndecl, abstract_virtuals);
+ tmp = TREE_CHAIN (tmp);
+ }
+ }
+ return abstract_virtuals;
+}
+
+/* Return the list of virtual functions which are abstract in type TYPE.
+ This information is cached, and so must be built on a
+ non-temporary obstack. */
+tree
+get_abstract_virtuals (type)
+ tree type;
+{
+ tree vbases, tmp;
+ tree abstract_virtuals = CLASSTYPE_ABSTRACT_VIRTUALS (type);
+
+ /* First get all from non-virtual bases. */
+ abstract_virtuals
+ = get_abstract_virtuals_1 (TYPE_BINFO (type), 1, abstract_virtuals);
+
+ for (vbases = CLASSTYPE_VBASECLASSES (type); vbases; vbases = TREE_CHAIN (vbases))
+ {
+ if (! BINFO_VIRTUALS (vbases))
+ continue;
+
+ tmp = TREE_CHAIN (BINFO_VIRTUALS (vbases));
+ while (tmp)
+ {
+ tree base_pfn = FNADDR_FROM_VTABLE_ENTRY (TREE_VALUE (tmp));
+ tree base_fndecl = TREE_OPERAND (base_pfn, 0);
+ if (DECL_ABSTRACT_VIRTUAL_P (base_fndecl))
+ abstract_virtuals = tree_cons (NULL_TREE, base_fndecl, abstract_virtuals);
+ tmp = TREE_CHAIN (tmp);
+ }
+ }
+ return nreverse (abstract_virtuals);
+}
+
+/* For the type TYPE, return a list of member functions available from
+ base classes with name NAME. The TREE_VALUE of the list is a chain of
+ member functions with name NAME. The TREE_PURPOSE of the list is a
+ basetype, or a list of base types (in reverse order) which were
+ traversed to reach the chain of member functions. If we reach a base
+ type which provides a member function of name NAME, and which has at
+ most one base type itself, then we can terminate the search. */
+
+tree
+get_baselinks (type_as_binfo_list, type, name)
+ tree type_as_binfo_list;
+ tree type, name;
+{
+ int head = 0, tail = 0, index;
+ tree rval = 0, nval = 0;
+ tree basetypes = type_as_binfo_list;
+ tree binfo = TYPE_BINFO (type);
+
+ search_stack = push_search_level (search_stack, &search_obstack);
+
+ while (1)
+ {
+ tree binfos = BINFO_BASETYPES (binfo);
+ int i, n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+
+ /* Process and/or queue base types. */
+ for (i = 0; i < n_baselinks; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+ tree btypes;
+
+ btypes = hash_tree_cons (TREE_VIA_PUBLIC (base_binfo),
+ TREE_VIA_VIRTUAL (base_binfo),
+ TREE_VIA_PROTECTED (base_binfo),
+ NULL_TREE, base_binfo,
+ basetypes);
+ obstack_ptr_grow (&search_obstack, btypes);
+ search_stack->first = (tree *)obstack_base (&search_obstack);
+ tail += 1;
+ }
+
+ dont_queue:
+ /* Process head of queue, if one exists. */
+ if (head >= tail)
+ break;
+
+ basetypes = search_stack->first[head++];
+ binfo = TREE_VALUE (basetypes);
+ type = BINFO_TYPE (binfo);
+ index = lookup_fnfields_1 (type, name);
+ if (index >= 0)
+ {
+ nval = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type), index);
+ rval = hash_tree_cons (0, 0, 0, basetypes, nval, rval);
+ if (TYPE_BINFO_BASETYPES (type) == 0)
+ goto dont_queue;
+ else if (TREE_VEC_LENGTH (TYPE_BINFO_BASETYPES (type)) == 1)
+ {
+ if (CLASSTYPE_BASELINK_VEC (type))
+ TREE_TYPE (rval) = TREE_VEC_ELT (CLASSTYPE_BASELINK_VEC (type), index);
+ goto dont_queue;
+ }
+ }
+ nval = NULL_TREE;
+ }
+
+ search_stack = pop_search_level (search_stack);
+ return rval;
+}
+
+tree
+next_baselink (baselink)
+ tree baselink;
+{
+ tree tmp = TREE_TYPE (baselink);
+ baselink = TREE_CHAIN (baselink);
+ while (tmp)
+ {
+ /* @@ does not yet add previous base types. */
+ baselink = tree_cons (TREE_PURPOSE (tmp), TREE_VALUE (tmp),
+ baselink);
+ TREE_TYPE (baselink) = TREE_TYPE (tmp);
+ tmp = TREE_CHAIN (tmp);
+ }
+ return baselink;
+}
+
+/* DEPTH-FIRST SEARCH ROUTINES. */
+
+/* Assign unique numbers to _CLASSTYPE members of the lattice
+ specified by TYPE. The root nodes are marked first; the nodes
+ are marked depth-fisrt, left-right. */
+
+static int cid;
+
+/* Matrix implementing a relation from CLASSTYPE X CLASSTYPE => INT.
+ Relation yields 1 if C1 <= C2, 0 otherwise. */
+typedef char mi_boolean;
+static mi_boolean *mi_matrix;
+
+/* Type for which this matrix is defined. */
+static tree mi_type;
+
+/* Size of the matrix for indexing purposes. */
+static int mi_size;
+
+/* Return nonzero if class C2 derives from class C1. */
+#define BINFO_DERIVES_FROM(C1, C2) \
+ ((mi_matrix+mi_size*(BINFO_CID (C1)-1))[BINFO_CID (C2)-1])
+#define TYPE_DERIVES_FROM(C1, C2) \
+ ((mi_matrix+mi_size*(CLASSTYPE_CID (C1)-1))[CLASSTYPE_CID (C2)-1])
+#define BINFO_DERIVES_FROM_STAR(C) \
+ (mi_matrix+(BINFO_CID (C)-1))
+
+/* This routine converts a pointer to be a pointer of an immediate
+ base class. The normal convert_pointer_to routine would diagnose
+ the conversion as ambiguous, under MI code that has the base class
+ as an ambiguous base class. */
+static tree
+convert_pointer_to_single_level (to_type, expr)
+ tree to_type, expr;
+{
+ tree binfo_of_derived;
+ tree last;
+
+ binfo_of_derived = TYPE_BINFO (TREE_TYPE (TREE_TYPE (expr)));
+ last = get_binfo (to_type, TREE_TYPE (TREE_TYPE (expr)), 0);
+ BINFO_INHERITANCE_CHAIN (last) = binfo_of_derived;
+ BINFO_INHERITANCE_CHAIN (binfo_of_derived) = NULL_TREE;
+ return build_vbase_path (PLUS_EXPR, TYPE_POINTER_TO (to_type), expr, last, 1);
+}
+
+/* The main function which implements depth first search.
+
+ This routine has to remember the path it walked up, when
+ dfs_init_vbase_pointers is the work function, as otherwise there
+ would be no record. */
+static void
+dfs_walk (binfo, fn, qfn)
+ tree binfo;
+ void (*fn)();
+ int (*qfn)();
+{
+ tree binfos = BINFO_BASETYPES (binfo);
+ int i, n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+
+ for (i = 0; i < n_baselinks; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+
+ if ((*qfn)(base_binfo))
+ {
+ if (fn == dfs_init_vbase_pointers)
+ {
+ /* When traversing an arbitrary MI hierarchy, we need to keep
+ a record of the path we took to get down to the final base
+ type, as otherwise there would be no record of it, and just
+ trying to blindly convert at the bottom would be ambiguous.
+
+ The easiest way is to do the conversions one step at a time,
+ as we know we want the immediate base class at each step.
+
+ The only special trick to converting one step at a time,
+ is that when we hit the last virtual base class, we must
+ use the SLOT value for it, and not use the normal convert
+ routine. We use the last virtual base class, as in our
+ implementation, we have pointers to all virtual base
+ classes in the base object. */
+
+ tree saved_vbase_decl_ptr_intermediate
+ = vbase_decl_ptr_intermediate;
+
+ if (TREE_VIA_VIRTUAL (base_binfo))
+ {
+ /* No need for the conversion here, as we know it is the
+ right type. */
+ vbase_decl_ptr_intermediate
+ = (tree)CLASSTYPE_SEARCH_SLOT (BINFO_TYPE (base_binfo));
+ }
+ else
+ {
+ vbase_decl_ptr_intermediate
+ = convert_pointer_to_single_level (BINFO_TYPE (base_binfo),
+ vbase_decl_ptr_intermediate);
+ }
+
+ dfs_walk (base_binfo, fn, qfn);
+
+ vbase_decl_ptr_intermediate = saved_vbase_decl_ptr_intermediate;
+ } else
+ dfs_walk (base_binfo, fn, qfn);
+ }
+ }
+
+ fn (binfo);
+}
+
+/* Predicate functions which serve for dfs_walk. */
+static int numberedp (binfo) tree binfo;
+{ return BINFO_CID (binfo); }
+static int unnumberedp (binfo) tree binfo;
+{ return BINFO_CID (binfo) == 0; }
+
+static int markedp (binfo) tree binfo;
+{ return BINFO_MARKED (binfo); }
+static int bfs_markedp (binfo, i) tree binfo; int i;
+{ return BINFO_MARKED (BINFO_BASETYPE (binfo, i)); }
+static int unmarkedp (binfo) tree binfo;
+{ return BINFO_MARKED (binfo) == 0; }
+static int bfs_unmarkedp (binfo, i) tree binfo; int i;
+{ return BINFO_MARKED (BINFO_BASETYPE (binfo, i)) == 0; }
+static int marked_vtable_pathp (binfo) tree binfo;
+{ return BINFO_VTABLE_PATH_MARKED (binfo); }
+static int bfs_marked_vtable_pathp (binfo, i) tree binfo; int i;
+{ return BINFO_VTABLE_PATH_MARKED (BINFO_BASETYPE (binfo, i)); }
+static int unmarked_vtable_pathp (binfo) tree binfo;
+{ return BINFO_VTABLE_PATH_MARKED (binfo) == 0; }
+static int bfs_unmarked_vtable_pathp (binfo, i) tree binfo; int i;
+{ return BINFO_VTABLE_PATH_MARKED (BINFO_BASETYPE (binfo, i)) == 0; }
+static int marked_new_vtablep (binfo) tree binfo;
+{ return BINFO_NEW_VTABLE_MARKED (binfo); }
+static int bfs_marked_new_vtablep (binfo, i) tree binfo; int i;
+{ return BINFO_NEW_VTABLE_MARKED (BINFO_BASETYPE (binfo, i)); }
+static int unmarked_new_vtablep (binfo) tree binfo;
+{ return BINFO_NEW_VTABLE_MARKED (binfo) == 0; }
+static int bfs_unmarked_new_vtablep (binfo, i) tree binfo; int i;
+{ return BINFO_NEW_VTABLE_MARKED (BINFO_BASETYPE (binfo, i)) == 0; }
+
+static int dfs_search_slot_nonempty_p (binfo) tree binfo;
+{ return CLASSTYPE_SEARCH_SLOT (BINFO_TYPE (binfo)) != 0; }
+
+static int dfs_debug_unmarkedp (binfo) tree binfo;
+{ return CLASSTYPE_DEBUG_REQUESTED (BINFO_TYPE (binfo)) == 0; }
+
+/* The worker functions for `dfs_walk'. These do not need to
+ test anything (vis a vis marking) if they are paired with
+ a predicate function (above). */
+
+/* Assign each type within the lattice a number which is unique
+ in the lattice. The first number assigned is 1. */
+
+static void
+dfs_number (binfo)
+ tree binfo;
+{
+ BINFO_CID (binfo) = ++cid;
+}
+
+static void
+dfs_unnumber (binfo)
+ tree binfo;
+{
+ BINFO_CID (binfo) = 0;
+}
+
+static void
+dfs_mark (binfo) tree binfo;
+{ SET_BINFO_MARKED (binfo); }
+
+static void
+dfs_unmark (binfo) tree binfo;
+{ CLEAR_BINFO_MARKED (binfo); }
+
+static void
+dfs_mark_vtable_path (binfo) tree binfo;
+{ SET_BINFO_VTABLE_PATH_MARKED (binfo); }
+
+static void
+dfs_unmark_vtable_path (binfo) tree binfo;
+{ CLEAR_BINFO_VTABLE_PATH_MARKED (binfo); }
+
+static void
+dfs_mark_new_vtable (binfo) tree binfo;
+{ SET_BINFO_NEW_VTABLE_MARKED (binfo); }
+
+static void
+dfs_unmark_new_vtable (binfo) tree binfo;
+{ CLEAR_BINFO_NEW_VTABLE_MARKED (binfo); }
+
+static void
+dfs_clear_search_slot (binfo) tree binfo;
+{ CLASSTYPE_SEARCH_SLOT (BINFO_TYPE (binfo)) = 0; }
+
+static void
+dfs_debug_mark (binfo)
+ tree binfo;
+{
+ tree t = BINFO_TYPE (binfo);
+
+ /* Use heuristic that if there are virtual functions,
+ ignore until we see a non-inline virtual function. */
+ tree methods = CLASSTYPE_METHOD_VEC (t);
+
+ CLASSTYPE_DEBUG_REQUESTED (t) = 1;
+
+ /* If interface info is known, the value of (?@@?) is correct. */
+ if (methods == 0
+ || CLASSTYPE_INTERFACE_KNOWN (t)
+ || (write_virtuals == 2 && TYPE_VIRTUAL_P (t)))
+ return;
+
+ /* If debug info is requested from this context for this type, supply it.
+ If debug info is requested from another context for this type,
+ see if some third context can supply it. */
+ if (current_function_decl == NULL_TREE
+ || DECL_CLASS_CONTEXT (current_function_decl) != t)
+ {
+ if (TREE_VEC_ELT (methods, 0))
+ methods = TREE_VEC_ELT (methods, 0);
+ else
+ methods = TREE_VEC_ELT (methods, 1);
+ while (methods)
+ {
+ if (DECL_VINDEX (methods)
+ && DECL_SAVED_INSNS (methods) == 0
+ && DECL_PENDING_INLINE_INFO (methods) == 0
+ && DECL_ABSTRACT_VIRTUAL_P (methods) == 0)
+ {
+ /* Somebody, somewhere is going to have to define this
+ virtual function. When they do, they will provide
+ the debugging info. */
+ return;
+ }
+ methods = TREE_CHAIN (methods);
+ }
+ }
+ /* We cannot rely on some alien method to solve our problems,
+ so we must write out the debug info ourselves. */
+ TYPE_DECL_SUPPRESS_DEBUG (TYPE_NAME (t)) = 0;
+ rest_of_type_compilation (t, global_bindings_p ());
+}
+
+/* Attach to the type of the virtual base class, the pointer to the
+ virtual base class, given the global pointer vbase_decl_ptr.
+
+ We use the global vbase_types. ICK! */
+static void
+dfs_find_vbases (binfo)
+ tree binfo;
+{
+ tree binfos = BINFO_BASETYPES (binfo);
+ int i, n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+
+ for (i = n_baselinks-1; i >= 0; i--)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+
+ if (TREE_VIA_VIRTUAL (base_binfo)
+ && CLASSTYPE_SEARCH_SLOT (BINFO_TYPE (base_binfo)) == 0)
+ {
+ tree vbase = BINFO_TYPE (base_binfo);
+ tree binfo = binfo_member (vbase, vbase_types);
+
+ CLASSTYPE_SEARCH_SLOT (vbase)
+ = (char *) build (PLUS_EXPR, TYPE_POINTER_TO (vbase),
+ vbase_decl_ptr, BINFO_OFFSET (binfo));
+ }
+ }
+ SET_BINFO_VTABLE_PATH_MARKED (binfo);
+ SET_BINFO_NEW_VTABLE_MARKED (binfo);
+}
+
+static void
+dfs_init_vbase_pointers (binfo)
+ tree binfo;
+{
+ tree type = BINFO_TYPE (binfo);
+ tree fields = TYPE_FIELDS (type);
+ tree this_vbase_ptr;
+
+ CLEAR_BINFO_VTABLE_PATH_MARKED (binfo);
+
+ /* If there is a dossier, it is the first field, though perhaps from
+ the base class. Otherwise, the first fields are virtual base class
+ pointer fields. */
+ if (CLASSTYPE_DOSSIER (type) && VFIELD_NAME_P (DECL_NAME (fields)))
+ /* Get past vtable for the object. */
+ fields = TREE_CHAIN (fields);
+
+ if (fields == NULL_TREE
+ || DECL_NAME (fields) == NULL_TREE
+ || ! VBASE_NAME_P (DECL_NAME (fields)))
+ return;
+
+ this_vbase_ptr = vbase_decl_ptr_intermediate;
+
+ if (TYPE_POINTER_TO (type) != TYPE_MAIN_VARIANT (TREE_TYPE (this_vbase_ptr)))
+ my_friendly_abort (125);
+
+ while (fields && DECL_NAME (fields)
+ && VBASE_NAME_P (DECL_NAME (fields)))
+ {
+ tree ref = build (COMPONENT_REF, TREE_TYPE (fields),
+ build_indirect_ref (this_vbase_ptr, NULL_PTR), fields);
+ tree init = (tree)CLASSTYPE_SEARCH_SLOT (TREE_TYPE (TREE_TYPE (fields)));
+ vbase_init_result = tree_cons (binfo_member (TREE_TYPE (TREE_TYPE (fields)),
+ vbase_types),
+ build_modify_expr (ref, NOP_EXPR, init),
+ vbase_init_result);
+ fields = TREE_CHAIN (fields);
+ }
+}
+
+/* Sometimes this needs to clear both VTABLE_PATH and NEW_VTABLE. Other
+ times, just NEW_VTABLE, but optimizer should make both with equal
+ efficiency (though it does not currently). */
+static void
+dfs_clear_vbase_slots (binfo)
+ tree binfo;
+{
+ tree type = BINFO_TYPE (binfo);
+ CLASSTYPE_SEARCH_SLOT (type) = 0;
+ CLEAR_BINFO_VTABLE_PATH_MARKED (binfo);
+ CLEAR_BINFO_NEW_VTABLE_MARKED (binfo);
+}
+
+tree
+init_vbase_pointers (type, decl_ptr)
+ tree type;
+ tree decl_ptr;
+{
+ if (TYPE_USES_VIRTUAL_BASECLASSES (type))
+ {
+ int old_flag = flag_this_is_variable;
+ tree binfo = TYPE_BINFO (type);
+ flag_this_is_variable = -2;
+ vbase_types = CLASSTYPE_VBASECLASSES (type);
+ vbase_decl_ptr = decl_ptr;
+ vbase_decl = build_indirect_ref (decl_ptr, NULL_PTR);
+ vbase_decl_ptr_intermediate = vbase_decl_ptr;
+ vbase_init_result = NULL_TREE;
+ dfs_walk (binfo, dfs_find_vbases, unmarked_vtable_pathp);
+ dfs_walk (binfo, dfs_init_vbase_pointers, marked_vtable_pathp);
+ dfs_walk (binfo, dfs_clear_vbase_slots, marked_new_vtablep);
+ flag_this_is_variable = old_flag;
+ return vbase_init_result;
+ }
+ return 0;
+}
+
+/* Build a COMPOUND_EXPR which when expanded will generate the code
+ needed to initialize all the virtual function table slots of all
+ the virtual baseclasses. MAIN_BINFO is the binfo which determines
+ the virtual baseclasses to use; TYPE is the type of the object to
+ which the initialization applies. TRUE_EXP is the true object we
+ are initializing, and DECL_PTR is the pointer to the sub-object we
+ are initializing.
+
+ When USE_COMPUTED_OFFSETS is non-zero, we can assume that the
+ object was laidout by a top-level contructor and the computed
+ offsets are valid to store vtables. When zero, we must store new
+ vtables through virtual baseclass pointers.
+
+ We setup and use the globals: vbase_decl, vbase_decl_ptr, vbase_types
+ ICK! */
+
+void
+expand_indirect_vtbls_init (binfo, true_exp, decl_ptr, use_computed_offsets)
+ tree binfo;
+ tree true_exp, decl_ptr;
+ int use_computed_offsets;
+{
+ tree type = BINFO_TYPE (binfo);
+ if (TYPE_USES_VIRTUAL_BASECLASSES (type))
+ {
+ int old_flag = flag_this_is_variable;
+ tree vbases = CLASSTYPE_VBASECLASSES (type);
+ vbase_types = vbases;
+ vbase_decl_ptr = true_exp ? build_unary_op (ADDR_EXPR, true_exp, 0) : decl_ptr;
+ vbase_decl = true_exp ? true_exp : build_indirect_ref (decl_ptr, NULL_PTR);
+
+ if (use_computed_offsets)
+ {
+ /* This is an object of type IN_TYPE, */
+ flag_this_is_variable = -2;
+ dfs_walk (binfo, dfs_find_vbases, unmarked_new_vtablep);
+ }
+
+ /* Initialized with vtables of type TYPE. */
+ for (; vbases; vbases = TREE_CHAIN (vbases))
+ {
+ tree addr;
+ if (use_computed_offsets)
+ addr = (tree)CLASSTYPE_SEARCH_SLOT (BINFO_TYPE (vbases));
+ else
+ {
+ tree vbinfo = get_binfo (TREE_TYPE (vbases),
+ TREE_TYPE (vbase_decl),
+ 0);
+
+ /* See is we can get lucky. */
+ if (TREE_VIA_VIRTUAL (vbinfo))
+ addr = convert_pointer_to_real (vbinfo, vbase_decl_ptr);
+ else
+ {
+ /* We go through all these contortions to avoid this
+ call, as it will fail when the virtual base type
+ is ambiguous from here. We don't yet have a way
+ to search for and find just an instance of the
+ virtual base class. Searching for the binfo in
+ vbases won't work, as we don't have the vbase
+ pointer field, for all vbases in the main class,
+ only direct vbases. */
+ addr = convert_pointer_to_real (TREE_TYPE (vbases),
+ vbase_decl_ptr);
+ if (addr == error_mark_node)
+ continue;
+ }
+ }
+
+ /* Do all vtables from this virtual base. */
+ /* This assumes that virtual bases can never serve as parent
+ binfos. (in the CLASSTPE_VFIELD_PARENT sense) */
+ expand_direct_vtbls_init (vbases, TYPE_BINFO (BINFO_TYPE (vbases)),
+ 1, 0, addr);
+ }
+
+ dfs_walk (binfo, dfs_clear_vbase_slots, marked_new_vtablep);
+
+ flag_this_is_variable = old_flag;
+ }
+}
+
+void
+clear_search_slots (type)
+ tree type;
+{
+ dfs_walk (TYPE_BINFO (type),
+ dfs_clear_search_slot, dfs_search_slot_nonempty_p);
+}
+
+/* get virtual base class types.
+ This adds type to the vbase_types list in reverse dfs order.
+ Ordering is very important, so don't change it. */
+
+static void
+dfs_get_vbase_types (binfo)
+ tree binfo;
+{
+ if (TREE_VIA_VIRTUAL (binfo) && ! BINFO_VBASE_MARKED (binfo))
+ {
+ vbase_types = make_binfo (integer_zero_node, binfo,
+ BINFO_VTABLE (binfo),
+ BINFO_VIRTUALS (binfo), vbase_types);
+ TREE_VIA_VIRTUAL (vbase_types) = 1;
+ SET_BINFO_VBASE_MARKED (binfo);
+ }
+ SET_BINFO_MARKED (binfo);
+}
+
+/* get a list of virtual base classes in dfs order. */
+tree
+get_vbase_types (type)
+ tree type;
+{
+ tree vbases;
+ tree binfo;
+
+ if (TREE_CODE (type) == TREE_VEC)
+ binfo = type;
+ else
+ binfo = TYPE_BINFO (type);
+
+ vbase_types = NULL_TREE;
+ dfs_walk (binfo, dfs_get_vbase_types, unmarkedp);
+ dfs_walk (binfo, dfs_unmark, markedp);
+ /* Rely upon the reverse dfs ordering from dfs_get_vbase_types, and now
+ reverse it so that we get normal dfs ordering. */
+ vbase_types = nreverse (vbase_types);
+
+ /* unmark marked vbases */
+ for (vbases = vbase_types; vbases; vbases = TREE_CHAIN (vbases))
+ CLEAR_BINFO_VBASE_MARKED (vbases);
+
+ return vbase_types;
+}
+
+static void
+dfs_record_inheritance (binfo)
+ tree binfo;
+{
+ tree binfos = BINFO_BASETYPES (binfo);
+ int i, n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+ mi_boolean *derived_row = BINFO_DERIVES_FROM_STAR (binfo);
+
+ for (i = n_baselinks-1; i >= 0; i--)
+ {
+ int j;
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+ tree baseclass = BINFO_TYPE (base_binfo);
+ mi_boolean *base_row = BINFO_DERIVES_FROM_STAR (base_binfo);
+
+ /* Don't search if there's nothing there! MI_SIZE can be
+ zero as a result of parse errors. */
+ if (TYPE_BINFO_BASETYPES (baseclass) && mi_size > 0)
+ for (j = mi_size*(CLASSTYPE_CID (baseclass)-1); j >= 0; j -= mi_size)
+ derived_row[j] |= base_row[j];
+ TYPE_DERIVES_FROM (baseclass, BINFO_TYPE (binfo)) = 1;
+ }
+
+ SET_BINFO_MARKED (binfo);
+}
+
+/* Given a _CLASSTYPE node in a multiple inheritance lattice,
+ convert the lattice into a simple relation such that,
+ given to CIDs, C1 and C2, one can determine if C1 <= C2
+ or C2 <= C1 or C1 <> C2.
+
+ Once constructed, we walk the lattice depth fisrt,
+ applying various functions to elements as they are encountered.
+
+ We use xmalloc here, in case we want to randomly free these tables. */
+
+#define SAVE_MI_MATRIX
+
+void
+build_mi_matrix (type)
+ tree type;
+{
+ tree binfo = TYPE_BINFO (type);
+ cid = 0;
+
+#ifdef SAVE_MI_MATRIX
+ if (CLASSTYPE_MI_MATRIX (type))
+ {
+ mi_size = CLASSTYPE_N_SUPERCLASSES (type) + CLASSTYPE_N_VBASECLASSES (type);
+ mi_matrix = CLASSTYPE_MI_MATRIX (type);
+ mi_type = type;
+ dfs_walk (binfo, dfs_number, unnumberedp);
+ return;
+ }
+#endif
+
+ mi_size = CLASSTYPE_N_SUPERCLASSES (type) + CLASSTYPE_N_VBASECLASSES (type);
+ mi_matrix = (char *)xmalloc ((mi_size + 1) * (mi_size + 1));
+ mi_type = type;
+ bzero (mi_matrix, (mi_size + 1) * (mi_size + 1));
+ dfs_walk (binfo, dfs_number, unnumberedp);
+ dfs_walk (binfo, dfs_record_inheritance, unmarkedp);
+ dfs_walk (binfo, dfs_unmark, markedp);
+}
+
+void
+free_mi_matrix ()
+{
+ dfs_walk (TYPE_BINFO (mi_type), dfs_unnumber, numberedp);
+
+#ifdef SAVE_MI_MATRIX
+ CLASSTYPE_MI_MATRIX (mi_type) = mi_matrix;
+#else
+ free (mi_matrix);
+ mi_size = 0;
+ cid = 0;
+#endif
+}
+
+/* If we want debug info for a type TYPE, make sure all its base types
+ are also marked as being potentially interesting. This avoids
+ the problem of not writing any debug info for intermediate basetypes
+ that have abstract virtual functions. */
+
+void
+note_debug_info_needed (type)
+ tree type;
+{
+ dfs_walk (TYPE_BINFO (type), dfs_debug_mark, dfs_debug_unmarkedp);
+}
+
+/* Subroutines of push_class_decls (). */
+
+/* Add the instance variables which this class contributed to the
+ current class binding contour. When a redefinition occurs,
+ if the redefinition is strictly within a single inheritance path,
+ we just overwrite (in the case of a data field) or
+ cons (in the case of a member function) the old declaration with
+ the new. If the fields are not within a single inheritance path,
+ we must cons them in either case.
+
+ In order to know what decls are new (stemming from the current
+ invocation of push_class_decls) we enclose them in an "envelope",
+ which is a TREE_LIST node where the TREE_PURPOSE slot contains the
+ new decl (or possibly a list of competing ones), the TREE_VALUE slot
+ points to the old value and the TREE_CHAIN slot chains together all
+ envelopes which needs to be "opened" in push_class_decls. Opening an
+ envelope means: push the old value onto the class_shadowed list,
+ install the new one and if it's a TYPE_DECL do the same to the
+ IDENTIFIER_TYPE_VALUE. Such an envelope is recognized by seeing that
+ the TREE_PURPOSE slot is non-null, and that it is not an identifier.
+ Because if it is, it could be a set of overloaded methods from an
+ outer scope. */
+
+static void
+dfs_pushdecls (binfo)
+ tree binfo;
+{
+ tree type = BINFO_TYPE (binfo);
+ tree fields, *methods, *end;
+ tree method_vec;
+
+ for (fields = TYPE_FIELDS (type); fields; fields = TREE_CHAIN (fields))
+ {
+ /* Unmark so that if we are in a constructor, and then find that
+ this field was initialized by a base initializer,
+ we can emit an error message. */
+ if (TREE_CODE (fields) == FIELD_DECL)
+ TREE_USED (fields) = 0;
+
+ /* Recurse into anonymous unions. */
+ if (DECL_NAME (fields) == NULL_TREE
+ && TREE_CODE (TREE_TYPE (fields)) == UNION_TYPE)
+ {
+ dfs_pushdecls (TYPE_BINFO (TREE_TYPE (fields)));
+ continue;
+ }
+
+#if 0
+ if (TREE_CODE (fields) != TYPE_DECL)
+ {
+ DECL_PUBLIC (fields) = 0;
+ DECL_PROTECTED (fields) = 0;
+ DECL_PRIVATE (fields) = 0;
+ }
+#endif
+
+ if (DECL_NAME (fields))
+ {
+ tree class_value = IDENTIFIER_CLASS_VALUE (DECL_NAME (fields));
+
+ /* If the class value is an envelope of the kind described in
+ the comment above, we try to rule out possible ambiguities.
+ If we can't do that, keep a TREE_LIST with possibly ambiguous
+ decls in there. */
+ if (class_value && TREE_CODE (class_value) == TREE_LIST
+ && TREE_PURPOSE (class_value) != NULL_TREE
+ && (TREE_CODE (TREE_PURPOSE (class_value))
+ != IDENTIFIER_NODE))
+ {
+ tree value = TREE_PURPOSE (class_value);
+ tree context;
+
+ /* Possible ambiguity. If its defining type(s)
+ is (are all) derived from us, no problem. */
+ if (TREE_CODE (value) != TREE_LIST)
+ {
+ context = (TREE_CODE (value) == FUNCTION_DECL
+ && DECL_VIRTUAL_P (value))
+ ? DECL_CLASS_CONTEXT (value)
+ : DECL_CONTEXT (value);
+
+ if (context && (context == type
+ || TYPE_DERIVES_FROM (context, type)))
+ value = fields;
+ else
+ value = tree_cons (NULL_TREE, fields,
+ build_tree_list (NULL_TREE, value));
+ }
+ else
+ {
+ /* All children may derive from us, in which case
+ there is no problem. Otherwise, we have to
+ keep lists around of what the ambiguities might be. */
+ tree values;
+ int problem = 0;
+
+ for (values = value; values; values = TREE_CHAIN (values))
+ {
+ tree sub_values = TREE_VALUE (values);
+
+ if (TREE_CODE (sub_values) == TREE_LIST)
+ {
+ for (; sub_values; sub_values = TREE_CHAIN (sub_values))
+ {
+ register tree list_mbr = TREE_VALUE (sub_values);
+
+ context = (TREE_CODE (list_mbr) == FUNCTION_DECL
+ && DECL_VIRTUAL_P (list_mbr))
+ ? DECL_CLASS_CONTEXT (list_mbr)
+ : DECL_CONTEXT (list_mbr);
+
+ if (! TYPE_DERIVES_FROM (context, type))
+ {
+ value = tree_cons (NULL_TREE, TREE_VALUE (values), value);
+ problem = 1;
+ break;
+ }
+ }
+ }
+ else
+ {
+ context = (TREE_CODE (sub_values) == FUNCTION_DECL
+ && DECL_VIRTUAL_P (sub_values))
+ ? DECL_CLASS_CONTEXT (sub_values)
+ : DECL_CONTEXT (sub_values);
+
+ if (context && ! TYPE_DERIVES_FROM (context, type))
+ {
+ value = tree_cons (NULL_TREE, values, value);
+ problem = 1;
+ break;
+ }
+ }
+ }
+ if (! problem) value = fields;
+ }
+
+ /* Mark this as a potentially ambiguous member. */
+ if (TREE_CODE (value) == TREE_LIST)
+ {
+ /* Leaving TREE_TYPE blank is intentional.
+ We cannot use `error_mark_node' (lookup_name)
+ or `unknown_type_node' (all member functions use this). */
+ TREE_NONLOCAL_FLAG (value) = 1;
+ }
+
+ /* Put the new contents in our envelope. */
+ TREE_PURPOSE (class_value) = value;
+ }
+ else
+ {
+ /* See comment above for a description of envelopes. */
+ tree envelope = tree_cons (fields, class_value,
+ closed_envelopes);
+
+ closed_envelopes = envelope;
+ IDENTIFIER_CLASS_VALUE (DECL_NAME (fields)) = envelope;
+ }
+ }
+ }
+
+ method_vec = CLASSTYPE_METHOD_VEC (type);
+ if (method_vec != 0)
+ {
+ /* Farm out constructors and destructors. */
+ methods = &TREE_VEC_ELT (method_vec, 1);
+ end = TREE_VEC_END (method_vec);
+
+ /* This does not work for multiple inheritance yet. */
+ while (methods != end)
+ {
+ /* This will cause lookup_name to return a pointer
+ to the tree_list of possible methods of this name.
+ If the order is a problem, we can nreverse them. */
+ tree tmp;
+ tree class_value = IDENTIFIER_CLASS_VALUE (DECL_NAME (*methods));
+
+ if (class_value && TREE_CODE (class_value) == TREE_LIST
+ && TREE_PURPOSE (class_value) != NULL_TREE
+ && TREE_CODE (TREE_PURPOSE (class_value)) != IDENTIFIER_NODE)
+ {
+ tree old = TREE_PURPOSE (class_value);
+
+ maybe_push_cache_obstack ();
+ if (TREE_CODE (old) == TREE_LIST)
+ tmp = tree_cons (DECL_NAME (*methods), *methods, old);
+ else
+ {
+ /* Only complain if we shadow something we can access. */
+ if (old
+ && warn_shadow
+ && ((DECL_LANG_SPECIFIC (old)
+ && DECL_CLASS_CONTEXT (old) == current_class_type)
+ || ! TREE_PRIVATE (old)))
+ /* Should figure out access control more accurately. */
+ {
+ cp_warning_at ("member `%#D' is shadowed", old);
+ cp_warning_at ("by member function `%#D'", *methods);
+ warning ("in this context");
+ }
+ tmp = build_tree_list (DECL_NAME (*methods), *methods);
+ }
+ pop_obstacks ();
+
+ TREE_TYPE (tmp) = unknown_type_node;
+#if 0
+ TREE_OVERLOADED (tmp) = DECL_OVERLOADED (*methods);
+#endif
+ TREE_NONLOCAL_FLAG (tmp) = 1;
+
+ /* Put the new contents in our envelope. */
+ TREE_PURPOSE (class_value) = tmp;
+ }
+ else
+ {
+ maybe_push_cache_obstack ();
+ tmp = build_tree_list (DECL_NAME (*methods), *methods);
+ pop_obstacks ();
+
+ TREE_TYPE (tmp) = unknown_type_node;
+#if 0
+ TREE_OVERLOADED (tmp) = DECL_OVERLOADED (*methods);
+#endif
+ TREE_NONLOCAL_FLAG (tmp) = 1;
+
+ /* See comment above for a description of envelopes. */
+ closed_envelopes = tree_cons (tmp, class_value,
+ closed_envelopes);
+ IDENTIFIER_CLASS_VALUE (DECL_NAME (*methods)) = closed_envelopes;
+ }
+#if 0
+ tmp = *methods;
+ while (tmp != 0)
+ {
+ DECL_PUBLIC (tmp) = 0;
+ DECL_PROTECTED (tmp) = 0;
+ DECL_PRIVATE (tmp) = 0;
+ tmp = DECL_CHAIN (tmp);
+ }
+#endif
+
+ methods++;
+ }
+ }
+ SET_BINFO_MARKED (binfo);
+}
+
+/* Consolidate unique (by name) member functions. */
+static void
+dfs_compress_decls (binfo)
+ tree binfo;
+{
+ tree type = BINFO_TYPE (binfo);
+ tree method_vec = CLASSTYPE_METHOD_VEC (type);
+
+ if (method_vec != 0)
+ {
+ /* Farm out constructors and destructors. */
+ tree *methods = &TREE_VEC_ELT (method_vec, 1);
+ tree *end = TREE_VEC_END (method_vec);
+
+ for (; methods != end; methods++)
+ {
+ /* This is known to be an envelope of the kind described before
+ dfs_pushdecls. */
+ tree class_value = IDENTIFIER_CLASS_VALUE (DECL_NAME (*methods));
+ tree tmp = TREE_PURPOSE (class_value);
+
+ /* This was replaced in scope by somebody else. Just leave it
+ alone. */
+ if (TREE_CODE (tmp) != TREE_LIST)
+ continue;
+
+ if (TREE_CHAIN (tmp) == NULL_TREE
+ && TREE_VALUE (tmp)
+ && DECL_CHAIN (TREE_VALUE (tmp)) == NULL_TREE)
+ {
+ TREE_PURPOSE (class_value) = TREE_VALUE (tmp);
+ }
+ }
+ }
+ CLEAR_BINFO_MARKED (binfo);
+}
+
+/* When entering the scope of a class, we cache all of the
+ fields that that class provides within its inheritance
+ lattice. Where ambiguities result, we mark them
+ with `error_mark_node' so that if they are encountered
+ without explicit qualification, we can emit an error
+ message. */
+void
+push_class_decls (type)
+ tree type;
+{
+ tree id;
+ struct obstack *ambient_obstack = current_obstack;
+
+#if 0
+ tree tags = CLASSTYPE_TAGS (type);
+
+ while (tags)
+ {
+ tree code_type_node;
+ tree tag;
+
+ switch (TREE_CODE (TREE_VALUE (tags)))
+ {
+ case ENUMERAL_TYPE:
+ code_type_node = enum_type_node;
+ break;
+ case RECORD_TYPE:
+ code_type_node = record_type_node;
+ break;
+ case CLASS_TYPE:
+ code_type_node = class_type_node;
+ break;
+ case UNION_TYPE:
+ code_type_node = union_type_node;
+ break;
+ default:
+ my_friendly_abort (297);
+ }
+ tag = xref_tag (code_type_node, TREE_PURPOSE (tags),
+ TYPE_BINFO_BASETYPE (TREE_VALUE (tags), 0), 0);
+#if 0 /* not yet, should get fixed properly later */
+ pushdecl (make_type_decl (TREE_PURPOSE (tags), TREE_VALUE (tags)));
+#else
+ pushdecl (build_decl (TYPE_DECL, TREE_PURPOSE (tags), TREE_VALUE (tags)));
+#endif
+ }
+#endif
+
+ search_stack = push_search_level (search_stack, &search_obstack);
+
+ id = TYPE_IDENTIFIER (type);
+#if 0
+ if (IDENTIFIER_TEMPLATE (id) != 0)
+ {
+ tree tmpl = IDENTIFIER_TEMPLATE (id);
+ push_template_decls (DECL_ARGUMENTS (TREE_PURPOSE (tmpl)),
+ TREE_VALUE (tmpl), 1);
+ overload_template_name (id, 1);
+ }
+#endif
+
+ /* Push class fields into CLASS_VALUE scope, and mark. */
+ dfs_walk (TYPE_BINFO (type), dfs_pushdecls, unmarkedp);
+
+ /* Compress fields which have only a single entry
+ by a given name, and unmark. */
+ dfs_walk (TYPE_BINFO (type), dfs_compress_decls, markedp);
+
+ /* Open up all the closed envelopes and push the contained decls into
+ class scope. */
+ while (closed_envelopes)
+ {
+ tree new = TREE_PURPOSE (closed_envelopes);
+ tree id;
+
+ /* This is messy because the class value may be a *_DECL, or a
+ TREE_LIST of overloaded *_DECLs or even a TREE_LIST of ambiguous
+ *_DECLs. The name is stored at different places in these three
+ cases. */
+ if (TREE_CODE (new) == TREE_LIST)
+ {
+ if (TREE_PURPOSE (new) != NULL_TREE)
+ id = TREE_PURPOSE (new);
+ else
+ {
+ tree node = TREE_VALUE (new);
+
+ while (TREE_CODE (node) == TREE_LIST)
+ node = TREE_VALUE (node);
+ id = DECL_NAME (node);
+ }
+ }
+ else
+ id = DECL_NAME (new);
+
+ /* Install the original class value in order to make
+ pushdecl_class_level work correctly. */
+ IDENTIFIER_CLASS_VALUE (id) = TREE_VALUE (closed_envelopes);
+ if (TREE_CODE (new) == TREE_LIST)
+ push_class_level_binding (id, new);
+ else
+ pushdecl_class_level (new);
+ closed_envelopes = TREE_CHAIN (closed_envelopes);
+ }
+ current_obstack = ambient_obstack;
+}
+
+/* Here's a subroutine we need because C lacks lambdas. */
+static void
+dfs_unuse_fields (binfo)
+ tree binfo;
+{
+ tree type = TREE_TYPE (binfo);
+ tree fields;
+
+ for (fields = TYPE_FIELDS (type); fields; fields = TREE_CHAIN (fields))
+ {
+ if (TREE_CODE (fields) != FIELD_DECL)
+ continue;
+
+ TREE_USED (fields) = 0;
+ if (DECL_NAME (fields) == NULL_TREE
+ && TREE_CODE (TREE_TYPE (fields)) == UNION_TYPE)
+ unuse_fields (TREE_TYPE (fields));
+ }
+}
+
+void
+unuse_fields (type)
+ tree type;
+{
+ dfs_walk (TYPE_BINFO (type), dfs_unuse_fields, unmarkedp);
+}
+
+void
+pop_class_decls (type)
+ tree type;
+{
+ /* We haven't pushed a search level when dealing with cached classes,
+ so we'd better not try to pop it. */
+ if (search_stack)
+ search_stack = pop_search_level (search_stack);
+}
+
+void
+print_search_statistics ()
+{
+#ifdef GATHER_STATISTICS
+ if (flag_memoize_lookups)
+ {
+ fprintf (stderr, "%d memoized contexts saved\n",
+ n_contexts_saved);
+ fprintf (stderr, "%d local tree nodes made\n", my_tree_node_counter);
+ fprintf (stderr, "%d local hash nodes made\n", my_memoized_entry_counter);
+ fprintf (stderr, "fields statistics:\n");
+ fprintf (stderr, " memoized finds = %d; rejects = %d; (searches = %d)\n",
+ memoized_fast_finds[0], memoized_fast_rejects[0],
+ memoized_fields_searched[0]);
+ fprintf (stderr, " memoized_adds = %d\n", memoized_adds[0]);
+ fprintf (stderr, "fnfields statistics:\n");
+ fprintf (stderr, " memoized finds = %d; rejects = %d; (searches = %d)\n",
+ memoized_fast_finds[1], memoized_fast_rejects[1],
+ memoized_fields_searched[1]);
+ fprintf (stderr, " memoized_adds = %d\n", memoized_adds[1]);
+ }
+ fprintf (stderr, "%d fields searched in %d[%d] calls to lookup_field[_1]\n",
+ n_fields_searched, n_calls_lookup_field, n_calls_lookup_field_1);
+ fprintf (stderr, "%d fnfields searched in %d calls to lookup_fnfields\n",
+ n_outer_fields_searched, n_calls_lookup_fnfields);
+ fprintf (stderr, "%d calls to get_base_type\n", n_calls_get_base_type);
+#else
+ fprintf (stderr, "no search statistics\n");
+#endif
+}
+
+void
+init_search_processing ()
+{
+ gcc_obstack_init (&search_obstack);
+ gcc_obstack_init (&type_obstack);
+ gcc_obstack_init (&type_obstack_entries);
+
+ /* This gives us room to build our chains of basetypes,
+ whether or not we decide to memoize them. */
+ type_stack = push_type_level (0, &type_obstack);
+ _vptr_name = get_identifier ("_vptr");
+}
+
+void
+reinit_search_statistics ()
+{
+ my_memoized_entry_counter = 0;
+ memoized_fast_finds[0] = 0;
+ memoized_fast_finds[1] = 0;
+ memoized_adds[0] = 0;
+ memoized_adds[1] = 0;
+ memoized_fast_rejects[0] = 0;
+ memoized_fast_rejects[1] = 0;
+ memoized_fields_searched[0] = 0;
+ memoized_fields_searched[1] = 0;
+ n_fields_searched = 0;
+ n_calls_lookup_field = 0, n_calls_lookup_field_1 = 0;
+ n_calls_lookup_fnfields = 0, n_calls_lookup_fnfields_1 = 0;
+ n_calls_get_base_type = 0;
+ n_outer_fields_searched = 0;
+ n_contexts_saved = 0;
+}
diff --git a/gnu/usr.bin/cc/cc1plus/sig.c b/gnu/usr.bin/cc/cc1plus/sig.c
new file mode 100644
index 0000000..1426168
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/sig.c
@@ -0,0 +1,1023 @@
+/* Functions dealing with signatures and signature pointers/references.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+ Contributed by Gerald Baumgartner (gb@cs.purdue.edu)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#include <stdio.h>
+#include "obstack.h"
+#include "tree.h"
+#include "cp-tree.h"
+#include "flags.h"
+#include "assert.h"
+
+extern struct obstack *current_obstack;
+extern struct obstack permanent_obstack;
+extern struct obstack *saveable_obstack;
+
+extern void error ();
+extern void sorry ();
+extern void compiler_error ();
+extern void make_decl_rtl PROTO((tree, char *, int));
+
+/* Used to help generate globally unique names for signature tables. */
+
+static int global_sigtable_name_counter;
+
+/* Build an identifier for a signature pointer or reference, so we
+ can use it's name in function name mangling. */
+
+static tree
+build_signature_pointer_or_reference_name (to_type, constp, volatilep, refp)
+ tree to_type;
+ int constp, volatilep, refp;
+{
+ char * sig_name = TYPE_NAME_STRING (to_type);
+ int name_len = TYPE_NAME_LENGTH (to_type) + constp + volatilep;
+ char * name;
+
+ if (refp)
+ {
+ name = (char *) alloca (name_len + sizeof (SIGNATURE_REFERENCE_NAME) +2);
+ sprintf (name, SIGNATURE_REFERENCE_NAME_FORMAT,
+ constp ? "C" : "", volatilep ? "V": "", sig_name);
+ }
+ else
+ {
+ name = (char *) alloca (name_len + sizeof (SIGNATURE_POINTER_NAME) + 2);
+ sprintf (name, SIGNATURE_POINTER_NAME_FORMAT,
+ constp ? "C" : "", volatilep ? "V": "", sig_name);
+ }
+ return get_identifier (name);
+}
+
+/* Build a DECL node for a signature pointer or reference, so we can
+ tell the debugger the structure of signature pointers/references.
+ This function is called at most eight times for a given signature,
+ once for each [const] [volatile] signature pointer/reference. */
+
+static void
+build_signature_pointer_or_reference_decl (type, name)
+ tree type, name;
+{
+ tree decl;
+
+ /* We don't enter this declaration in any sort of symbol table. */
+ decl = build_decl (TYPE_DECL, name, type);
+ TYPE_NAME (type) = decl;
+ TREE_CHAIN (type) = decl;
+}
+
+/* Construct, lay out and return the type of pointers or references
+ to signature TO_TYPE. If such a type has already been constructed,
+ reuse it. If CONSTP or VOLATILEP is specified, make the `optr' const
+ or volatile, respectively. If we are constructing a const/volatile
+ type variant and the main type variant doesn't exist yet, it is built
+ as well. If REFP is 1, we construct a signature reference, otherwise
+ a signature pointer is constructed.
+
+ This function is a subroutine of `build_signature_pointer_type' and
+ `build_signature_reference_type'. */
+
+static tree
+build_signature_pointer_or_reference_type (to_type, constp, volatilep, refp)
+ tree to_type;
+ int constp, volatilep, refp;
+{
+ register tree t, m;
+ register struct obstack *ambient_obstack = current_obstack;
+ register struct obstack *ambient_saveable_obstack = saveable_obstack;
+
+ m = refp ? SIGNATURE_REFERENCE_TO (to_type) : SIGNATURE_POINTER_TO (to_type);
+
+ /* If we don't have the main variant yet, construct it. */
+ if (m == NULL_TREE
+ && (constp || volatilep))
+ m = build_signature_pointer_or_reference_type (to_type, 0, 0, refp);
+
+ /* Treat any nonzero argument as 1. */
+ constp = !!constp;
+ volatilep = !!volatilep;
+ refp = !!refp;
+
+ /* If not generating auxiliary info, search the chain of variants to see
+ if there is already one there just like the one we need to have. If so,
+ use that existing one.
+
+ We don't do this in the case where we are generating aux info because
+ in that case we want each typedef names to get it's own distinct type
+ node, even if the type of this new typedef is the same as some other
+ (existing) type. */
+
+ if (m && !flag_gen_aux_info)
+ for (t = m; t; t = TYPE_NEXT_VARIANT (t))
+ if (constp == TYPE_READONLY (TREE_TYPE (TREE_TYPE (TYPE_FIELDS (t))))
+ && volatilep == TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (TYPE_FIELDS (t)))))
+ return t;
+
+ /* We need a new one. If TO_TYPE is permanent, make this permanent too. */
+ if (TREE_PERMANENT (to_type))
+ {
+ current_obstack = &permanent_obstack;
+ saveable_obstack = &permanent_obstack;
+ }
+
+ /* A signature pointer or reference to a signature `s' looks like this:
+
+ struct {
+ void * optr;
+ const s * sptr;
+ vtbl_type_node * vptr;
+ };
+
+ A `const' signature pointer/reference is a
+
+ struct {
+ const void * optr;
+ const s * sptr;
+ vtbl_type_node * vptr;
+ };
+
+ Similarly, for `volatile' and `const volatile'.
+ */
+
+ t = make_lang_type (RECORD_TYPE);
+ {
+ tree obj_type = build_type_variant (void_type_node, constp, volatilep);
+ tree optr_type = build_pointer_type (obj_type);
+ tree optr, sptr, vptr;
+
+ optr = build_lang_field_decl (FIELD_DECL,
+ get_identifier (SIGNATURE_OPTR_NAME),
+ optr_type);
+ DECL_FIELD_CONTEXT (optr) = t;
+ DECL_CLASS_CONTEXT (optr) = t;
+
+ if (m)
+ {
+ /* We can share `sptr' and `vptr' among type variants. */
+ sptr = TREE_CHAIN (TYPE_FIELDS (m));
+ vptr = TREE_CHAIN (sptr);
+ }
+ else
+ {
+ tree sig_tbl_type = c_build_type_variant (to_type, 1, 0);
+
+ sptr = build_lang_field_decl (FIELD_DECL,
+ get_identifier (SIGNATURE_SPTR_NAME),
+ build_pointer_type (sig_tbl_type));
+ vptr = build_lang_field_decl (FIELD_DECL,
+ get_identifier (SIGNATURE_VPTR_NAME),
+ build_pointer_type (vtbl_type_node));
+ DECL_FIELD_CONTEXT (sptr) = t;
+ DECL_CLASS_CONTEXT (sptr) = t;
+ DECL_FIELD_CONTEXT (vptr) = t;
+ DECL_CLASS_CONTEXT (vptr) = t;
+ TREE_CHAIN (sptr) = vptr;
+ TREE_CHAIN (vptr) = NULL_TREE;
+ }
+
+ TREE_CHAIN (optr) = sptr;
+ TYPE_FIELDS (t) = optr;
+ /* To make `build_vfn_ref' work when building a signature method call. */
+ CLASSTYPE_VFIELD (t) = vptr;
+ DECL_FCONTEXT (CLASSTYPE_VFIELD (t)) = t;
+ TYPE_ALIGN (t) = TYPE_ALIGN (optr_type);
+ }
+
+ {
+ tree name = build_signature_pointer_or_reference_name (to_type, constp,
+ volatilep, refp);
+
+ /* Build a DECL node for this type, so the debugger has access to it. */
+ build_signature_pointer_or_reference_decl (t, name);
+ }
+
+ CLASSTYPE_GOT_SEMICOLON (t) = 1;
+ IS_SIGNATURE_POINTER (t) = ! refp;
+ IS_SIGNATURE_REFERENCE (t) = refp;
+ SIGNATURE_TYPE (t) = to_type;
+
+ if (m)
+ {
+ /* Add this type to the chain of variants of TYPE.
+ Every type has to be its own TYPE_MAIN_VARIANT. */
+ TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (m);
+ TYPE_NEXT_VARIANT (m) = t;
+ }
+ else if (refp)
+ /* Record this type as the reference to TO_TYPE. */
+ SIGNATURE_REFERENCE_TO (to_type) = t;
+ else
+ /* Record this type as the pointer to TO_TYPE. */
+ SIGNATURE_POINTER_TO (to_type) = t;
+
+ /* Lay out the type. This function has many callers that are concerned
+ with expression-construction, and this simplifies them all.
+ Also, it guarantees the TYPE_SIZE is permanent if the type is. */
+ layout_type (t);
+
+ current_obstack = ambient_obstack;
+ saveable_obstack = ambient_saveable_obstack;
+
+ /* Ouput debug information for this type. */
+ rest_of_type_compilation (t, 1);
+
+ return t;
+}
+
+/* Construct, lay out and return the type of pointers to signature TO_TYPE. */
+
+tree
+build_signature_pointer_type (to_type, constp, volatilep)
+ tree to_type;
+ int constp, volatilep;
+{
+ return
+ build_signature_pointer_or_reference_type (to_type, constp, volatilep, 0);
+}
+
+/* Construct, lay out and return the type of pointers to signature TO_TYPE. */
+
+tree
+build_signature_reference_type (to_type, constp, volatilep)
+ tree to_type;
+ int constp, volatilep;
+{
+ return
+ build_signature_pointer_or_reference_type (to_type, constp, volatilep, 1);
+}
+
+/* Return the name of the signature table (as an IDENTIFIER_NODE)
+ for the given signature type SIG_TYPE and rhs type RHS_TYPE. */
+
+static tree
+get_sigtable_name (sig_type, rhs_type)
+ tree sig_type, rhs_type;
+{
+ tree sig_type_id = build_typename_overload (sig_type);
+ tree rhs_type_id = build_typename_overload (rhs_type);
+ char *buf = (char *) alloca (sizeof (SIGTABLE_NAME_FORMAT_LONG)
+ + IDENTIFIER_LENGTH (sig_type_id)
+ + IDENTIFIER_LENGTH (rhs_type_id) + 20);
+ char *sig_ptr = IDENTIFIER_POINTER (sig_type_id);
+ char *rhs_ptr = IDENTIFIER_POINTER (rhs_type_id);
+ int i, j;
+
+ for (i = 0; sig_ptr[i] == OPERATOR_TYPENAME_FORMAT[i]; i++)
+ /* do nothing */;
+ while (sig_ptr[i] >= '0' && sig_ptr[i] <= '9')
+ i += 1;
+
+ for (j = 0; rhs_ptr[j] == OPERATOR_TYPENAME_FORMAT[j]; j++)
+ /* do nothing */;
+ while (rhs_ptr[j] >= '0' && rhs_ptr[j] <= '9')
+ j += 1;
+
+ if (IS_SIGNATURE (rhs_type))
+ sprintf (buf, SIGTABLE_NAME_FORMAT_LONG, sig_ptr+i, rhs_ptr+j,
+ global_sigtable_name_counter++);
+ else
+ sprintf (buf, SIGTABLE_NAME_FORMAT, sig_ptr+i, rhs_ptr+j);
+ return get_identifier (buf);
+}
+
+/* Build a field decl that points to a signature member function. */
+
+static tree
+build_member_function_pointer (member)
+ tree member;
+{
+ char *namstr = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (member));
+ int namlen = IDENTIFIER_LENGTH (DECL_ASSEMBLER_NAME (member));
+ char *name;
+ tree entry;
+
+ name = (char *) alloca (namlen + sizeof (SIGNATURE_FIELD_NAME) + 2);
+ sprintf (name, SIGNATURE_FIELD_NAME_FORMAT, namstr);
+
+ /* @@ Do we really want to xref signature table fields? */
+ GNU_xref_ref (current_function_decl, name);
+
+ entry = build_lang_field_decl (FIELD_DECL, get_identifier (name),
+ TYPE_MAIN_VARIANT (sigtable_entry_type));
+ TREE_CONSTANT (entry) = 1;
+ TREE_READONLY (entry) = 1;
+
+ /* @@ Do we really want to xref signature table fields? */
+ GNU_xref_decl (current_function_decl, entry);
+
+ return entry;
+}
+
+/* For each FUNCTION_DECL in a signature we construct a member function
+ pointer of the appropriate type. We also need two flags to test
+ whether the member function pointer points to a virtual function or
+ to a default implementation. Those flags will be the two lower order
+ bits of the member function pointer (or the two higher order bits,
+ based on the configuration).
+
+ The new FIELD_DECLs are appended at the end of the last (and only)
+ sublist of `list_of_fieldlists.'
+
+ As a side effect, each member function in the signature gets the
+ `decl.ignored' bit turned on, so we don't output debug info for it. */
+
+void
+append_signature_fields (list_of_fieldlists)
+ tree list_of_fieldlists;
+{
+ tree l, x;
+ tree last_x = NULL_TREE;
+ tree mfptr;
+ tree last_mfptr;
+ tree mfptr_list = NULL_TREE;
+
+ /* For signatures it should actually be only a list with one element. */
+ for (l = list_of_fieldlists; l; l = TREE_CHAIN (l))
+ {
+ for (x = TREE_VALUE (l); x; x = TREE_CHAIN (x))
+ {
+ if (TREE_CODE (x) == FUNCTION_DECL)
+ {
+ mfptr = build_member_function_pointer (x);
+ DECL_MEMFUNC_POINTER_TO (x) = mfptr;
+ DECL_MEMFUNC_POINTING_TO (mfptr) = x;
+ DECL_IGNORED_P (x) = 1;
+ DECL_IN_AGGR_P (mfptr) = 1;
+ if (! mfptr_list)
+ mfptr_list = last_mfptr = mfptr;
+ else
+ {
+ TREE_CHAIN (last_mfptr) = mfptr;
+ last_mfptr = mfptr;
+ }
+ }
+ last_x = x;
+ }
+ }
+
+ /* Append the lists. */
+ if (last_x && mfptr_list)
+ {
+ TREE_CHAIN (last_x) = mfptr_list;
+ TREE_CHAIN (last_mfptr) = NULL_TREE;
+ }
+}
+
+/* Compare the types of a signature member function and a class member
+ function. Returns 1 if the types are in the C++ `<=' relationship.
+
+ If we have a signature pointer/reference as argument or return type
+ we don't want to do a recursive conformance check. The conformance
+ check only succeeds if both LHS and RHS refer to the same signature
+ pointer. Otherwise we need to keep information about parameter types
+ around at run time to initialize the signature table correctly. */
+
+static int
+match_method_types (sig_mtype, class_mtype)
+ tree sig_mtype, class_mtype;
+{
+ tree sig_return_type = TREE_TYPE (sig_mtype);
+ tree sig_arg_types = TYPE_ARG_TYPES (sig_mtype);
+ tree class_return_type = TREE_TYPE (class_mtype);
+ tree class_arg_types = TYPE_ARG_TYPES (class_mtype);
+
+ /* The return types have to be the same. */
+ if (! comptypes (sig_return_type, class_return_type, 1))
+ return 0;
+
+ /* Compare the first argument `this.' */
+ {
+ /* Get the type of what the `optr' is pointing to. */
+ tree sig_this =
+ TREE_TYPE (TREE_TYPE (TYPE_FIELDS (TREE_VALUE (sig_arg_types))));
+ tree class_this = TREE_VALUE (class_arg_types);
+
+ if (TREE_CODE (class_this) == RECORD_TYPE) /* Is `this' a sig ptr? */
+ class_this = TREE_TYPE (TREE_TYPE (TYPE_FIELDS (class_this)));
+ else
+ class_this = TREE_TYPE (class_this);
+
+ /* If a signature method's `this' is const or volatile, so has to be
+ the corresponding class method's `this.' */
+ if ((TYPE_READONLY (sig_this) && ! TYPE_READONLY (class_this))
+ || (TYPE_VOLATILE (sig_this) && ! TYPE_VOLATILE (class_this)))
+ return 0;
+ }
+
+ sig_arg_types = TREE_CHAIN (sig_arg_types);
+ class_arg_types = TREE_CHAIN (class_arg_types);
+
+ /* The number of arguments and the argument types have to be the same. */
+ return compparms (sig_arg_types, class_arg_types, 3);
+}
+
+/* Undo casts of opaque type variables to the RHS types. */
+static void
+undo_casts (sig_ty)
+ tree sig_ty;
+{
+ tree field = TYPE_FIELDS (sig_ty);
+
+ /* Since all the FIELD_DECLs for the signature table entries are at the end
+ of the chain (see `append_signature_fields'), we can do it this way. */
+ for (; field && TREE_CODE (field) != FIELD_DECL; field = TREE_CHAIN (field))
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (field)) == opaque_type_node)
+ TREE_TYPE (TREE_TYPE (field)) = TREE_TYPE (ptr_type_node);
+}
+
+/* Do the type checking necessary to see whether the `rhs' conforms to
+ the lhs's `sig_ty'. Depending on the type of `rhs' return a NULL_TREE,
+ an integer_zero_node, a constructor, or an expression offsetting the
+ `rhs' signature table. */
+
+static tree
+build_signature_table_constructor (sig_ty, rhs)
+ tree sig_ty, rhs;
+{
+ tree rhstype = TREE_TYPE (rhs);
+ tree sig_field = TYPE_FIELDS (sig_ty);
+ tree result = NULL_TREE;
+ tree first_rhs_field = NULL_TREE;
+ tree last_rhs_field;
+ int sig_ptr_p = IS_SIGNATURE (rhstype);
+ int offset_p = sig_ptr_p;
+
+ rhstype = sig_ptr_p ? rhstype : TREE_TYPE (rhstype);
+
+ if (CLASSTYPE_TAGS (sig_ty))
+ {
+ sorry ("conformance check with signature containing class declarations");
+ return error_mark_node;
+ }
+
+ for (; sig_field; sig_field = TREE_CHAIN (sig_field))
+ {
+ tree basetype_path, baselink, basetypes;
+ tree sig_method, sig_mname, sig_mtype;
+ tree rhs_method, tbl_entry;
+
+ if (TREE_CODE (sig_field) == TYPE_DECL)
+ {
+ tree sig_field_type = TREE_TYPE (sig_field);
+
+ if (TYPE_MAIN_VARIANT (sig_field_type) == opaque_type_node)
+ {
+ /* We've got an opaque type here. */
+ tree oty_name = DECL_NAME (sig_field);
+ tree oty_type = lookup_field (rhstype, oty_name, 1, 1);
+
+ if (oty_type == NULL_TREE || oty_type == error_mark_node)
+ {
+ cp_error ("class `%T' does not contain type `%T'",
+ rhstype, oty_type);
+ undo_casts (sig_ty);
+ return error_mark_node;
+ }
+ oty_type = TREE_TYPE (oty_type);
+
+ /* Cast `sig_field' to be of type `oty_type'. This will be
+ undone in `undo_casts' by walking over all the TYPE_DECLs. */
+ TREE_TYPE (sig_field_type) = TREE_TYPE (oty_type);
+ }
+ /* If we don't have an opaque type, we can ignore the `typedef'. */
+ continue;
+ }
+
+ /* Find the signature method corresponding to `sig_field'. */
+ sig_method = DECL_MEMFUNC_POINTING_TO (sig_field);
+ sig_mname = DECL_NAME (sig_method);
+ sig_mtype = TREE_TYPE (sig_method);
+
+ basetype_path = TYPE_BINFO (rhstype);
+ baselink = lookup_fnfields (basetype_path, sig_mname, 0);
+ if (baselink == NULL_TREE || baselink == error_mark_node)
+ {
+ if (! IS_DEFAULT_IMPLEMENTATION (sig_method))
+ {
+ cp_error ("class `%T' does not contain method `%D'",
+ rhstype, sig_mname);
+ undo_casts (sig_ty);
+ return error_mark_node;
+ }
+ else
+ {
+ /* We use the signature's default implementation. */
+ rhs_method = sig_method;
+ }
+ }
+ else
+ {
+ /* Find the class method of the correct type. */
+
+ basetypes = TREE_PURPOSE (baselink);
+ if (TREE_CODE (basetypes) == TREE_LIST)
+ basetypes = TREE_VALUE (basetypes);
+
+ rhs_method = TREE_VALUE (baselink);
+ for (; rhs_method; rhs_method = TREE_CHAIN (rhs_method))
+ if (sig_mname == DECL_NAME (rhs_method)
+ && ! DECL_STATIC_FUNCTION_P (rhs_method)
+ && match_method_types (sig_mtype, TREE_TYPE (rhs_method)))
+ break;
+
+ if (rhs_method == NULL_TREE
+ || (compute_access (basetypes, rhs_method)
+ != access_public))
+ {
+ error ("class `%s' does not contain a method conforming to `%s'",
+ TYPE_NAME_STRING (rhstype),
+ fndecl_as_string (NULL, sig_method, 1));
+ undo_casts (sig_ty);
+ return error_mark_node;
+ }
+ }
+
+ if (sig_ptr_p && rhs_method != sig_method)
+ {
+ tree rhs_field = DECL_MEMFUNC_POINTER_TO (rhs_method);
+
+ if (first_rhs_field == NULL_TREE)
+ {
+ first_rhs_field = rhs_field;
+ last_rhs_field = rhs_field;
+ }
+ else if (TREE_CHAIN (last_rhs_field) == rhs_field)
+ last_rhs_field = rhs_field;
+ else
+ offset_p = 0;
+
+ tbl_entry = build_component_ref (rhs, DECL_NAME (rhs_field),
+ NULL_TREE, 1);
+ }
+ else
+ {
+ tree code, offset, pfn;
+
+ if (rhs_method == sig_method)
+ {
+ code = integer_two_node;
+ offset = integer_zero_node;
+ pfn = build_unary_op (ADDR_EXPR, rhs_method, 0);
+ TREE_TYPE (pfn) = ptr_type_node;
+ offset_p = 0; /* we can't offset the rhs sig table */
+ }
+ else if (DECL_VINDEX (rhs_method))
+ {
+ code = integer_one_node;
+ offset = DECL_VINDEX (rhs_method);
+ pfn = null_pointer_node;
+ }
+ else
+ {
+ code = integer_zero_node;
+ offset = integer_zero_node;
+ pfn = build_unary_op (ADDR_EXPR, rhs_method, 0);
+ TREE_TYPE (pfn) = ptr_type_node;
+ TREE_ADDRESSABLE (rhs_method) = 1;
+ }
+
+ tbl_entry = tree_cons (NULL_TREE, code,
+ tree_cons (NULL_TREE, offset,
+ build_tree_list (NULL_TREE, pfn)));
+ tbl_entry = build_nt (CONSTRUCTOR, NULL_TREE, tbl_entry);
+ TREE_HAS_CONSTRUCTOR (tbl_entry) = 1;
+ TREE_CONSTANT (tbl_entry) = 1;
+ }
+
+ /* Chain those function address expressions together. */
+ if (result)
+ result = tree_cons (NULL_TREE, tbl_entry, result);
+ else
+ result = build_tree_list (NULL_TREE, tbl_entry);
+ }
+
+ if (result == NULL_TREE)
+ {
+ /* The signature was empty, we don't need a signature table. */
+ undo_casts (sig_ty);
+ return NULL_TREE;
+ }
+
+ if (offset_p)
+ {
+ if (first_rhs_field == TYPE_FIELDS (rhstype))
+ {
+ /* The sptr field on the lhs can be copied from the rhs. */
+ undo_casts (sig_ty);
+ return integer_zero_node;
+ }
+ else
+ {
+ /* The sptr field on the lhs will point into the rhs sigtable. */
+ undo_casts (sig_ty);
+ return build_component_ref (rhs, DECL_NAME (first_rhs_field),
+ NULL_TREE, 0);
+ }
+ }
+
+ /* We need to construct a new signature table. */
+ result = build_nt (CONSTRUCTOR, NULL_TREE, nreverse (result));
+ TREE_HAS_CONSTRUCTOR (result) = 1;
+ TREE_CONSTANT (result) = !sig_ptr_p;
+
+ undo_casts (sig_ty);
+ return result;
+}
+
+/* Build a signature table declaration and initialize it or return an
+ existing one if we built one already. If we don't get a constructor
+ as initialization expression, we don't need a new signature table
+ variable and just hand back the init expression.
+
+ The declaration processing is done by hand instead of using `finish_decl'
+ so that we can make signature pointers global variables instead of
+ static ones. */
+
+static tree
+build_sigtable (sig_type, rhs_type, init_from)
+ tree sig_type, rhs_type, init_from;
+{
+ tree name = NULL_TREE;
+ tree decl = NULL_TREE;
+ tree init_expr;
+
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+
+ if (! IS_SIGNATURE (rhs_type))
+ {
+ name = get_sigtable_name (sig_type, rhs_type);
+ decl = IDENTIFIER_GLOBAL_VALUE (name);
+ }
+ if (decl == NULL_TREE)
+ {
+ tree init;
+
+ /* We allow only one signature table to be generated for signatures
+ with opaque types. Otherwise we create a loophole in the type
+ system since we could cast data from one classes implementation
+ of the opaque type to that of another class. */
+ if (SIGNATURE_HAS_OPAQUE_TYPEDECLS (sig_type)
+ && SIGTABLE_HAS_BEEN_GENERATED (sig_type))
+ {
+ error ("signature with opaque type implemented by multiple classes");
+ return error_mark_node;
+ }
+ SIGTABLE_HAS_BEEN_GENERATED (sig_type) = 1;
+
+ init_expr = build_signature_table_constructor (sig_type, init_from);
+ if (init_expr == NULL_TREE || TREE_CODE (init_expr) != CONSTRUCTOR)
+ return init_expr;
+
+ if (name == NULL_TREE)
+ name = get_sigtable_name (sig_type, rhs_type);
+ {
+ tree context = current_function_decl;
+
+ /* Make the signature table global, not just static in whichever
+ function a signature pointer/ref is used for the first time. */
+ current_function_decl = NULL_TREE;
+ decl = pushdecl_top_level (build_decl (VAR_DECL, name, sig_type));
+ current_function_decl = context;
+ }
+ IDENTIFIER_GLOBAL_VALUE (name) = decl;
+ store_init_value (decl, init_expr);
+ if (IS_SIGNATURE (rhs_type))
+ {
+ init = DECL_INITIAL (decl);
+ DECL_INITIAL (decl) = error_mark_node;
+ }
+
+ DECL_ALIGN (decl) = MAX (TYPE_ALIGN (double_type_node),
+ DECL_ALIGN (decl));
+#if 0
+ /* GDB-4.7 doesn't find the initialization value of a signature table
+ when it is constant. */
+ TREE_READONLY (decl) = 1;
+#endif
+ TREE_STATIC (decl) = 1;
+ TREE_USED (decl) = 1;
+
+ make_decl_rtl (decl, NULL, 1);
+ if (IS_SIGNATURE (rhs_type))
+ expand_static_init (decl, init);
+ }
+
+ pop_obstacks ();
+
+ return decl;
+}
+
+/* Create a constructor or modify expression if the LHS of an assignment
+ is a signature pointer or a signature reference. If LHS is a record
+ type node, we build a constructor, otherwise a compound expression. */
+
+tree
+build_signature_pointer_constructor (lhs, rhs)
+ tree lhs, rhs;
+{
+ register struct obstack *ambient_obstack = current_obstack;
+ register struct obstack *ambient_saveable_obstack = saveable_obstack;
+ int initp = (TREE_CODE (lhs) == RECORD_TYPE);
+ tree lhstype = initp ? lhs : TREE_TYPE (lhs);
+ tree rhstype = TREE_TYPE (rhs);
+ tree sig_ty = SIGNATURE_TYPE (lhstype);
+ tree sig_tbl, sptr_expr, optr_expr, vptr_expr;
+ tree result;
+
+ if (! ((TREE_CODE (rhstype) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (rhstype)) == RECORD_TYPE)
+ || (TYPE_LANG_SPECIFIC (rhstype) &&
+ (IS_SIGNATURE_POINTER (rhstype)
+ || IS_SIGNATURE_REFERENCE (rhstype)))))
+ {
+ error ("invalid assignment to signature pointer or reference");
+ return error_mark_node;
+ }
+
+ if (TYPE_SIZE (sig_ty) == NULL_TREE)
+ {
+ cp_error ("undefined signature `%T' used in signature %s declaration",
+ sig_ty,
+ IS_SIGNATURE_POINTER (lhstype) ? "pointer" : "reference");
+ return error_mark_node;
+ }
+
+ /* If SIG_TY is permanent, make the signature table constructor and
+ the signature pointer/reference constructor permanent too. */
+ if (TREE_PERMANENT (sig_ty))
+ {
+ current_obstack = &permanent_obstack;
+ saveable_obstack = &permanent_obstack;
+ }
+
+ if (TYPE_LANG_SPECIFIC (rhstype) &&
+ (IS_SIGNATURE_POINTER (rhstype) || IS_SIGNATURE_REFERENCE (rhstype)))
+ {
+ if (SIGNATURE_TYPE (rhstype) == sig_ty)
+ {
+ /* LHS and RHS are signature pointers/refs of the same signature. */
+ optr_expr = build_optr_ref (rhs);
+ sptr_expr = build_sptr_ref (rhs);
+ vptr_expr = build_vptr_ref (rhs);
+ }
+ else
+ {
+ /* We need to create a new signature table and copy
+ elements from the rhs signature table. */
+ tree rhs_sptr_ref = build_sptr_ref (rhs);
+ tree rhs_tbl = build1 (INDIRECT_REF, SIGNATURE_TYPE (rhstype),
+ rhs_sptr_ref);
+
+ sig_tbl = build_sigtable (sig_ty, SIGNATURE_TYPE (rhstype), rhs_tbl);
+ if (sig_tbl == error_mark_node)
+ return error_mark_node;
+
+ optr_expr = build_optr_ref (rhs);
+ if (sig_tbl == NULL_TREE)
+ /* The signature was empty. The signature pointer is
+ pretty useless, but the user has been warned. */
+ sptr_expr = copy_node (null_pointer_node);
+ else if (sig_tbl == integer_zero_node)
+ sptr_expr = rhs_sptr_ref;
+ else
+ sptr_expr = build_unary_op (ADDR_EXPR, sig_tbl, 0);
+ TREE_TYPE (sptr_expr) = build_pointer_type (sig_ty);
+ vptr_expr = build_vptr_ref (rhs);
+ }
+ }
+ else
+ {
+ tree rhs_vptr;
+
+ if (TYPE_USES_COMPLEX_INHERITANCE (TREE_TYPE (rhstype)))
+ {
+ sorry ("class with multiple inheritance as implementation of signature");
+ return error_mark_node;
+ }
+
+ sig_tbl = build_sigtable (sig_ty, TREE_TYPE (rhstype), rhs);
+ if (sig_tbl == error_mark_node)
+ return error_mark_node;
+
+ optr_expr = rhs;
+ if (sig_tbl == NULL_TREE)
+ /* The signature was empty. The signature pointer is
+ pretty useless, but the user has been warned. */
+ {
+ sptr_expr = copy_node (null_pointer_node);
+ TREE_TYPE (sptr_expr) = build_pointer_type (sig_ty);
+ }
+ else
+ sptr_expr = build_unary_op (ADDR_EXPR, sig_tbl, 0);
+ if (CLASSTYPE_VFIELD (TREE_TYPE (rhstype)))
+ {
+ rhs_vptr = DECL_NAME (CLASSTYPE_VFIELD (TREE_TYPE (rhstype)));
+ vptr_expr = build_component_ref (build_indirect_ref (rhs, 0),
+ rhs_vptr, NULL_TREE, 0);
+ }
+ else
+ vptr_expr = copy_node (null_pointer_node);
+ TREE_TYPE (vptr_expr) = build_pointer_type (vtbl_type_node);
+ }
+
+ if (initp)
+ {
+ result = tree_cons (NULL_TREE, optr_expr,
+ tree_cons (NULL_TREE, sptr_expr,
+ build_tree_list (NULL_TREE, vptr_expr)));
+ result = build_nt (CONSTRUCTOR, NULL_TREE, result);
+ TREE_HAS_CONSTRUCTOR (result) = 1;
+ result = digest_init (lhstype, result, 0);
+ }
+ else
+ {
+ if (TREE_READONLY (lhs) || TYPE_READONLY (lhstype))
+ readonly_error (lhs, "assignment", 0);
+
+ optr_expr = build_modify_expr (build_optr_ref (lhs), NOP_EXPR,
+ optr_expr);
+ sptr_expr = build_modify_expr (build_sptr_ref (lhs), NOP_EXPR,
+ sptr_expr);
+ vptr_expr = build_modify_expr (build_vptr_ref (lhs), NOP_EXPR,
+ vptr_expr);
+
+ result = tree_cons (NULL_TREE, optr_expr,
+ tree_cons (NULL_TREE, sptr_expr,
+ tree_cons (NULL_TREE, vptr_expr,
+ build_tree_list (NULL_TREE,
+ lhs))));
+ result = build_compound_expr (result);
+ }
+
+ current_obstack = ambient_obstack;
+ saveable_obstack = ambient_saveable_obstack;
+ return result;
+}
+
+/* Build a temporary variable declaration for the instance of a signature
+ member function call if it isn't a declaration node already. Simply
+ using a SAVE_EXPR doesn't work since we need `this' in both branches
+ of a conditional expression. */
+
+static tree
+save_this (instance)
+ tree instance;
+{
+ tree decl;
+
+ if (TREE_CODE_CLASS (TREE_CODE (instance)) == 'd')
+ decl = instance;
+ else
+ {
+ decl = build_decl (VAR_DECL, NULL_TREE, TREE_TYPE (instance));
+ DECL_REGISTER (decl) = 1;
+ layout_decl (decl, 0);
+ expand_decl (decl);
+ }
+
+ return decl;
+}
+
+/* Build a signature member function call. Looks up the signature table
+ entry corresponding to FUNCTION. Depending on the value of the CODE
+ field, either call the function in PFN directly, or use OFFSET to
+ index INSTANCE's virtual function table. */
+
+tree
+build_signature_method_call (basetype, instance, function, parms)
+ tree basetype, instance, function, parms;
+{
+ tree saved_instance = save_this (instance); /* Create temp for `this'. */
+ tree signature_tbl_ptr = build_sptr_ref (saved_instance);
+ tree sig_field_name = DECL_NAME (DECL_MEMFUNC_POINTER_TO (function));
+ tree basetype_path = TYPE_BINFO (basetype);
+ tree tbl_entry = build_component_ref (build1 (INDIRECT_REF, basetype,
+ signature_tbl_ptr),
+ sig_field_name, basetype_path, 1);
+ tree code, offset, pfn, vfn;
+ tree deflt_call = NULL_TREE, direct_call, virtual_call, result;
+
+ code = build_component_ref (tbl_entry, get_identifier (SIGTABLE_CODE_NAME),
+ NULL_TREE, 1);
+ offset = build_component_ref (tbl_entry,
+ get_identifier (SIGTABLE_OFFSET_NAME),
+ NULL_TREE, 1);
+ pfn = build_component_ref (tbl_entry, get_identifier (SIGTABLE_PFN_NAME),
+ NULL_TREE, 1);
+ TREE_TYPE (pfn) = build_pointer_type (TREE_TYPE (function));
+
+ if (IS_DEFAULT_IMPLEMENTATION (function))
+ {
+ pfn = save_expr (pfn);
+ deflt_call = build_function_call (pfn,
+ tree_cons (NULL_TREE, saved_instance,
+ TREE_CHAIN (parms)));
+ }
+
+ {
+ /* Cast the signature method to have `this' of a normal pointer type. */
+ tree old_this = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (TREE_TYPE (pfn))));
+
+ TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (TREE_TYPE (pfn)))) =
+ build_type_variant (TYPE_POINTER_TO (basetype),
+ TYPE_READONLY (old_this),
+ TYPE_VOLATILE (old_this));
+
+ direct_call = build_function_call (pfn, parms);
+
+ vfn = build_vfn_ref (&TREE_VALUE (parms), saved_instance, offset);
+ TREE_TYPE (vfn) = build_pointer_type (TREE_TYPE (function));
+ virtual_call = build_function_call (vfn, parms);
+
+ /* Undo the cast, make `this' a signature pointer again. */
+ TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (TREE_TYPE (pfn)))) = old_this;
+ }
+
+ /* Once the function was found, there should be no reason why we
+ couldn't build the member function pointer call. */
+ if (!direct_call || direct_call == error_mark_node
+ || !virtual_call || virtual_call == error_mark_node
+ || (IS_DEFAULT_IMPLEMENTATION (function)
+ && (!deflt_call || deflt_call == error_mark_node)))
+ {
+ compiler_error ("cannot build call of signature member function `%s'",
+ fndecl_as_string (NULL, function, 1));
+ return error_mark_node;
+ }
+
+ if (IS_DEFAULT_IMPLEMENTATION (function))
+ {
+ tree test = build_binary_op_nodefault (EQ_EXPR, code, integer_one_node,
+ EQ_EXPR);
+ result = build_conditional_expr (code,
+ build_conditional_expr (test,
+ virtual_call,
+ deflt_call),
+ direct_call);
+ }
+ else
+ result = build_conditional_expr (code, virtual_call, direct_call);
+
+ /* If we created a temporary variable for `this', initialize it first. */
+ if (instance != saved_instance)
+ result = build (COMPOUND_EXPR, TREE_TYPE (result),
+ build_modify_expr (saved_instance, NOP_EXPR, instance),
+ result);
+
+ return result;
+}
+
+/* Create a COMPONENT_REF expression for referencing the OPTR field
+ of a signature pointer or reference. */
+
+tree
+build_optr_ref (instance)
+ tree instance;
+{
+ tree field = get_identifier (SIGNATURE_OPTR_NAME);
+
+ return build_component_ref (instance, field, NULL_TREE, 1);
+}
+
+/* Create a COMPONENT_REF expression for referencing the SPTR field
+ of a signature pointer or reference. */
+
+tree
+build_sptr_ref (instance)
+ tree instance;
+{
+ tree field = get_identifier (SIGNATURE_SPTR_NAME);
+
+ return build_component_ref (instance, field, NULL_TREE, 1);
+}
+
+/* Create a COMPONENT_REF expression for referencing the VPTR field
+ of a signature pointer or reference. */
+
+tree
+build_vptr_ref (instance)
+ tree instance;
+{
+ tree field = get_identifier (SIGNATURE_VPTR_NAME);
+
+ return build_component_ref (instance, field, NULL_TREE, 1);
+}
diff --git a/gnu/usr.bin/cc/cc1plus/spew.c b/gnu/usr.bin/cc/cc1plus/spew.c
new file mode 100644
index 0000000..ea00ba2
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/spew.c
@@ -0,0 +1,436 @@
+/* Type Analyzer for GNU C++.
+ Copyright (C) 1987, 1989, 1992, 1993 Free Software Foundation, Inc.
+ Hacked... nay, bludgeoned... by Mark Eichin (eichin@cygnus.com)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file is the type analyzer for GNU C++. To debug it, define SPEW_DEBUG
+ when compiling parse.c and spew.c. */
+
+#include "config.h"
+#include <stdio.h>
+#include "input.h"
+#include "tree.h"
+#include "lex.h"
+#include "parse.h"
+#include "cp-tree.h"
+#include "flags.h"
+#include "obstack.h"
+
+/* This takes a token stream that hasn't decided much about types and
+ tries to figure out as much as it can, with excessive lookahead and
+ backtracking. */
+
+/* fifo of tokens recognized and available to parser. */
+struct token {
+ /* The values for YYCHAR will fit in a short. */
+ short yychar;
+ short end_of_file;
+ YYSTYPE yylval;
+};
+
+static int do_aggr ();
+
+/* From lex.c: */
+/* the declaration found for the last IDENTIFIER token read in.
+ yylex must look this up to detect typedefs, which get token type TYPENAME,
+ so it is left around in case the identifier is not a typedef but is
+ used in a context which makes it a reference to a variable. */
+extern tree lastiddecl; /* let our brains leak out here too */
+extern int yychar; /* the lookahead symbol */
+extern YYSTYPE yylval; /* the semantic value of the */
+ /* lookahead symbol */
+extern int end_of_file;
+
+struct obstack token_obstack;
+int first_token;
+
+#ifdef SPEW_DEBUG
+int spew_debug = 0;
+static unsigned int yylex_ctr = 0;
+static int debug_yychar ();
+#endif
+
+/* Initialize token_obstack. Called once, from init_lex. */
+void
+init_spew ()
+{
+ gcc_obstack_init(&token_obstack);
+}
+
+#ifdef SPEW_DEBUG
+/* Use functions for debugging... */
+
+/* Return the number of tokens available on the fifo. */
+static int
+num_tokens ()
+{
+ return (obstack_object_size(&token_obstack)/sizeof(struct token))
+ - first_token;
+}
+
+/* Fetch the token N down the line from the head of the fifo. */
+static struct token*
+nth_token (n)
+ int n;
+{
+ /* could just have this do slurp_ implicitly, but this way is easier
+ * to debug... */
+ my_friendly_assert (n < num_tokens(), 298);
+ return ((struct token*)obstack_base(&token_obstack))+n+first_token;
+}
+
+/* Add a token to the token fifo. */
+static void
+add_token (t)
+ struct token* t;
+{
+ obstack_grow(&token_obstack,t,sizeof (struct token));
+}
+
+/* Consume the next token out of the fifo. */
+static void
+consume_token()
+{
+ if (num_tokens() == 1)
+ {
+ obstack_free(&token_obstack, obstack_base (&token_obstack));
+ first_token = 0;
+ }
+ else
+ first_token++;
+}
+
+#else
+/* ...otherwise use macros. */
+
+#define num_tokens() \
+ ((obstack_object_size(&token_obstack)/sizeof(struct token)) - first_token)
+
+#define nth_token(N) \
+ (((struct token*)obstack_base(&token_obstack))+(N)+first_token)
+
+#define add_token(T) obstack_grow(&token_obstack, (T), sizeof (struct token))
+
+#define consume_token() \
+ (num_tokens() == 1 \
+ ? (obstack_free (&token_obstack, obstack_base (&token_obstack)), \
+ (first_token = 0)) \
+ : first_token++)
+#endif
+
+/* Pull in enough tokens from real_yylex that the queue is N long beyond
+ the current token. */
+
+static void
+scan_tokens (n)
+ int n;
+{
+ int i;
+ struct token *tmp;
+
+ /* We cannot read past certain tokens, so make sure we don't. */
+ i = num_tokens ();
+ if (i > n)
+ return;
+ while (i-- > 0)
+ {
+ tmp = nth_token (i);
+ /* Never read past these characters: they might separate
+ the current input stream from one we save away later. */
+ if (tmp->yychar == '{' || tmp->yychar == ':' || tmp->yychar == ';')
+ goto pad_tokens;
+ }
+
+ while (num_tokens() <= n)
+ {
+ obstack_blank(&token_obstack,sizeof (struct token));
+ tmp = ((struct token *)obstack_next_free (&token_obstack))-1;
+ tmp->yychar = real_yylex();
+ tmp->end_of_file = end_of_file;
+ tmp->yylval = yylval;
+ end_of_file = 0;
+ if (tmp->yychar == '{'
+ || tmp->yychar == ':'
+ || tmp->yychar == ';')
+ {
+ pad_tokens:
+ while (num_tokens () <= n)
+ {
+ obstack_blank(&token_obstack,sizeof (struct token));
+ tmp = ((struct token *)obstack_next_free (&token_obstack))-1;
+ tmp->yychar = EMPTY;
+ tmp->end_of_file = 0;
+ }
+ }
+ }
+}
+
+/* Create room for N tokens at the front of the fifo. This is used
+ to insert new tokens into the stream ahead of the current token. */
+
+static void
+shift_tokens (n)
+ int n;
+{
+ if (first_token >= n)
+ first_token -= n;
+ else
+ {
+ int old_token_count = num_tokens ();
+ char *tmp;
+
+ obstack_blank (&token_obstack, (n-first_token) * sizeof (struct token));
+ if (old_token_count)
+ {
+ tmp = (char *)alloca ((num_tokens () + (n-first_token))
+ * sizeof (struct token));
+ /* This move does not rely on the system being able to handle
+ overlapping moves. */
+ bcopy (nth_token (0), tmp, old_token_count * sizeof (struct token));
+ bcopy (tmp, nth_token (n), old_token_count * sizeof (struct token));
+ }
+ first_token = 0;
+ }
+}
+
+static int
+probe_obstack (h, obj, nlevels)
+ struct obstack *h;
+ tree obj;
+ unsigned int nlevels;
+{
+ register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk* plp; /* point to previous chunk if any */
+
+ lp = (h)->chunk;
+ /* We use >= rather than > since the object cannot be exactly at
+ the beginning of the chunk but might be an empty object exactly
+ at the end of an adjacent chunk. */
+ for (; nlevels != 0 && lp != 0 && ((tree)lp >= obj || (tree)lp->limit < obj);
+ nlevels -= 1)
+ {
+ plp = lp->prev;
+ lp = plp;
+ }
+ return nlevels != 0 && lp != 0;
+}
+
+/* from lex.c: */
+/* Value is 1 (or 2) if we should try to make the next identifier look like
+ a typename (when it may be a local variable or a class variable).
+ Value is 0 if we treat this name in a default fashion. */
+extern int looking_for_typename;
+int looking_for_template;
+
+extern struct obstack *current_obstack, *saveable_obstack;
+tree got_scope;
+
+int
+yylex()
+{
+ struct token tmp_token;
+ tree trrr;
+
+ retry:
+#ifdef SPEW_DEBUG
+ if (spew_debug)
+ {
+ yylex_ctr ++;
+ fprintf(stderr, "\t\t## %d ##",yylex_ctr);
+ }
+#endif
+
+ /* if we've got tokens, send them */
+ if (num_tokens())
+ {
+ tmp_token= *nth_token(0);
+
+ /* TMP_TOKEN.YYLVAL.TTYPE may have been allocated on the wrong obstack.
+ If we don't find it in CURRENT_OBSTACK's current or immediately
+ previous chunk, assume it was and copy it to the current obstack. */
+ if ((tmp_token.yychar == CONSTANT
+ || tmp_token.yychar == STRING)
+ && ! TREE_PERMANENT (tmp_token.yylval.ttype)
+ && ! probe_obstack (current_obstack, tmp_token.yylval.ttype, 2)
+ && ! probe_obstack (saveable_obstack, tmp_token.yylval.ttype, 2))
+ tmp_token.yylval.ttype = copy_node (tmp_token.yylval.ttype);
+ }
+ else
+ {
+ /* if not, grab the next one and think about it */
+ tmp_token.yychar = real_yylex ();
+ tmp_token.yylval = yylval;
+ tmp_token.end_of_file = end_of_file;
+ add_token(&tmp_token);
+ }
+
+ /* many tokens just need to be returned. At first glance, all we
+ * have to do is send them back up, but some of them are needed to
+ * figure out local context. */
+ switch(tmp_token.yychar)
+ {
+ case EMPTY:
+ /* This is a lexical no-op. */
+ consume_token ();
+#ifdef SPEW_DEBUG
+ if (spew_debug)
+ debug_yychar (tmp_token.yychar);
+#endif
+ goto retry;
+
+ case IDENTIFIER:
+ scan_tokens (1);
+ if (nth_token (1)->yychar == SCOPE)
+ /* Don't interfere with the setting from an 'aggr' prefix. */
+ looking_for_typename++;
+ else if (nth_token (1)->yychar == '<')
+ looking_for_template = 1;
+
+ trrr = lookup_name (tmp_token.yylval.ttype, -2);
+
+ if (trrr)
+ {
+ tmp_token.yychar = identifier_type (trrr);
+ switch (tmp_token.yychar)
+ {
+ case TYPENAME:
+ lastiddecl = identifier_typedecl_value (tmp_token.yylval.ttype);
+ if (lastiddecl != trrr)
+ {
+ lastiddecl = trrr;
+ if (got_scope)
+ tmp_token.yylval.ttype = DECL_NESTED_TYPENAME (trrr);
+ }
+ break;
+ case IDENTIFIER:
+ lastiddecl = trrr;
+ break;
+ case PTYPENAME:
+ lastiddecl = NULL_TREE;
+ break;
+ default:
+ my_friendly_abort (101);
+ }
+ }
+ else
+ lastiddecl = trrr;
+ got_scope = NULL_TREE;
+ /* and fall through to... */
+ case TYPENAME:
+ case PTYPENAME:
+ consume_token ();
+ if (looking_for_typename > 0)
+ looking_for_typename--;
+ looking_for_template = 0;
+ break;
+
+ case SCSPEC:
+ /* do_aggr needs to check if the previous token was RID_FRIEND,
+ so just increment first_token instead of calling consume_token. */
+ first_token++;
+ break;
+ case TYPESPEC:
+ consume_token ();
+ break;
+
+ case AGGR:
+ *nth_token(0) = tmp_token;
+ do_aggr ();
+ /* fall through to output... */
+ case ENUM:
+ /* Set this again, in case we are rescanning. */
+ looking_for_typename = 1;
+ /* fall through... */
+ default:
+ consume_token();
+ }
+
+ yylval = tmp_token.yylval;
+ yychar = tmp_token.yychar;
+ end_of_file = tmp_token.end_of_file;
+#ifdef SPEW_DEBUG
+ if (spew_debug)
+ debug_yychar(yychar);
+#endif
+ return yychar;
+}
+
+/* token[0] == AGGR (struct/union/enum)
+ * Thus, token[1] is either a TYPENAME or a TYPENAME_DEFN.
+ * If token[2] == '{' or ':' then it's TYPENAME_DEFN.
+ * It's also a definition if it's a forward declaration (as in 'struct Foo;')
+ * which we can tell lf token[2] == ';' *and* token[-1] != FRIEND.
+ */
+static int
+do_aggr ()
+{
+ int yc1, yc2;
+
+ scan_tokens (2);
+ yc1 = nth_token (1)->yychar;
+ if (yc1 != TYPENAME && yc1 != IDENTIFIER && yc1 != PTYPENAME)
+ return 0;
+ yc2 = nth_token (2)->yychar;
+ if (yc2 == ';')
+ {
+ /* It's a forward declaration iff we were not preceded by 'friend'. */
+ if (first_token > 0 && nth_token (-1)->yychar == SCSPEC
+ && nth_token (-1)->yylval.ttype == ridpointers[(int) RID_FRIEND])
+ return 0;
+ }
+ else if (yc2 != '{' && yc2 != ':')
+ return 0;
+
+ switch (yc1)
+ {
+ case TYPENAME:
+ nth_token (1)->yychar = TYPENAME_DEFN;
+ break;
+ case PTYPENAME:
+ nth_token (1)->yychar = PTYPENAME_DEFN;
+ break;
+ case IDENTIFIER:
+ nth_token (1)->yychar = IDENTIFIER_DEFN;
+ break;
+ default:
+ my_friendly_abort (102);
+ }
+ return 0;
+}
+
+#ifdef SPEW_DEBUG
+/* debug_yychar takes a yychar (token number) value and prints its name. */
+static int
+debug_yychar (yy)
+ int yy;
+{
+ /* In parse.y: */
+ extern char *debug_yytranslate ();
+
+ int i;
+
+ if(yy<256) {
+ fprintf (stderr, "<%d: %c >\n", yy, yy);
+ return 0;
+ }
+ fprintf (stderr, "<%d:%s>\n", yy, debug_yytranslate (yy));
+ return 1;
+}
+
+#endif
diff --git a/gnu/usr.bin/cc/cc1plus/tree.c b/gnu/usr.bin/cc/cc1plus/tree.c
new file mode 100644
index 0000000..88466b8
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/tree.c
@@ -0,0 +1,1763 @@
+/* Language-dependent node constructors for parse phase of GNU compiler.
+ Copyright (C) 1987, 1988, 1992, 1993 Free Software Foundation, Inc.
+ Hacked by Michael Tiemann (tiemann@cygnus.com)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "config.h"
+#include <stdio.h>
+#include "obstack.h"
+#include "tree.h"
+#include "cp-tree.h"
+#include "flags.h"
+
+#define CEIL(x,y) (((x) + (y) - 1) / (y))
+
+/* Return nonzero if REF is an lvalue valid for this language.
+ Lvalues can be assigned, unless they have TREE_READONLY.
+ Lvalues can have their address taken, unless they have DECL_REGISTER. */
+
+int
+lvalue_p (ref)
+ tree ref;
+{
+ register enum tree_code code = TREE_CODE (ref);
+
+ if (language_lvalue_valid (ref))
+ {
+ if (TREE_CODE (TREE_TYPE (ref)) == REFERENCE_TYPE)
+ return 1;
+
+ switch (code)
+ {
+ /* preincrements and predecrements are valid lvals, provided
+ what they refer to are valid lvals. */
+ case PREINCREMENT_EXPR:
+ case PREDECREMENT_EXPR:
+ case COMPONENT_REF:
+ case SAVE_EXPR:
+ return lvalue_p (TREE_OPERAND (ref, 0));
+
+ case STRING_CST:
+ return 1;
+
+ case VAR_DECL:
+ if (TREE_READONLY (ref) && ! TREE_STATIC (ref)
+ && DECL_LANG_SPECIFIC (ref)
+ && DECL_IN_AGGR_P (ref))
+ return 0;
+ case INDIRECT_REF:
+ case ARRAY_REF:
+ case PARM_DECL:
+ case RESULT_DECL:
+ case ERROR_MARK:
+ if (TREE_CODE (TREE_TYPE (ref)) != FUNCTION_TYPE
+ && TREE_CODE (TREE_TYPE (ref)) != METHOD_TYPE)
+ return 1;
+ break;
+
+ case TARGET_EXPR:
+ case WITH_CLEANUP_EXPR:
+ return 1;
+
+ /* A currently unresolved scope ref. */
+ case SCOPE_REF:
+ my_friendly_abort (103);
+ case OFFSET_REF:
+ if (TREE_CODE (TREE_OPERAND (ref, 1)) == FUNCTION_DECL)
+ return 1;
+ return lvalue_p (TREE_OPERAND (ref, 0))
+ && lvalue_p (TREE_OPERAND (ref, 1));
+ break;
+
+ case COND_EXPR:
+ return (lvalue_p (TREE_OPERAND (ref, 1))
+ && lvalue_p (TREE_OPERAND (ref, 2)));
+
+ case MODIFY_EXPR:
+ return 1;
+
+ case COMPOUND_EXPR:
+ return lvalue_p (TREE_OPERAND (ref, 1));
+ }
+ }
+ return 0;
+}
+
+/* Return nonzero if REF is an lvalue valid for this language;
+ otherwise, print an error message and return zero. */
+
+int
+lvalue_or_else (ref, string)
+ tree ref;
+ char *string;
+{
+ int win = lvalue_p (ref);
+ if (! win)
+ error ("non-lvalue in %s", string);
+ return win;
+}
+
+/* INIT is a CALL_EXPR which needs info about its target.
+ TYPE is the type that this initialization should appear to have.
+
+ Build an encapsulation of the initialization to perform
+ and return it so that it can be processed by language-independent
+ and language-specific expression expanders.
+
+ If WITH_CLEANUP_P is nonzero, we build a cleanup for this expression.
+ Otherwise, cleanups are not built here. For example, when building
+ an initialization for a stack slot, since the called function handles
+ the cleanup, we would not want to do it here. */
+tree
+build_cplus_new (type, init, with_cleanup_p)
+ tree type;
+ tree init;
+ int with_cleanup_p;
+{
+ tree slot = build (VAR_DECL, type);
+ tree rval = build (NEW_EXPR, type,
+ TREE_OPERAND (init, 0), TREE_OPERAND (init, 1), slot);
+ TREE_SIDE_EFFECTS (rval) = 1;
+ TREE_ADDRESSABLE (rval) = 1;
+ rval = build (TARGET_EXPR, type, slot, rval, 0);
+ TREE_SIDE_EFFECTS (rval) = 1;
+ TREE_ADDRESSABLE (rval) = 1;
+
+#if 0
+ if (with_cleanup_p && TYPE_NEEDS_DESTRUCTOR (type))
+ {
+ TREE_OPERAND (rval, 2) = error_mark_node;
+ rval = build (WITH_CLEANUP_EXPR, type, rval, 0,
+ build_delete (TYPE_POINTER_TO (type),
+ build_unary_op (ADDR_EXPR, slot, 0),
+ integer_two_node,
+ LOOKUP_NORMAL|LOOKUP_DESTRUCTOR, 0));
+ TREE_SIDE_EFFECTS (rval) = 1;
+ TREE_ADDRESSABLE (rval) = 1;
+ }
+#endif
+ return rval;
+}
+
+/* Recursively search EXP for CALL_EXPRs that need cleanups and replace
+ these CALL_EXPRs with tree nodes that will perform the cleanups. */
+
+tree
+break_out_cleanups (exp)
+ tree exp;
+{
+ tree tmp = exp;
+
+ if (TREE_CODE (tmp) == CALL_EXPR
+ && TYPE_NEEDS_DESTRUCTOR (TREE_TYPE (tmp)))
+ return build_cplus_new (TREE_TYPE (tmp), tmp, 1);
+
+ while (TREE_CODE (tmp) == NOP_EXPR
+ || TREE_CODE (tmp) == CONVERT_EXPR
+ || TREE_CODE (tmp) == NON_LVALUE_EXPR)
+ {
+ if (TREE_CODE (TREE_OPERAND (tmp, 0)) == CALL_EXPR
+ && TYPE_NEEDS_DESTRUCTOR (TREE_TYPE (TREE_OPERAND (tmp, 0))))
+ {
+ TREE_OPERAND (tmp, 0)
+ = build_cplus_new (TREE_TYPE (TREE_OPERAND (tmp, 0)),
+ TREE_OPERAND (tmp, 0), 1);
+ break;
+ }
+ else
+ tmp = TREE_OPERAND (tmp, 0);
+ }
+ return exp;
+}
+
+/* Recursively perform a preorder search EXP for CALL_EXPRs, making
+ copies where they are found. Returns a deep copy all nodes transitively
+ containing CALL_EXPRs. */
+
+tree
+break_out_calls (exp)
+ tree exp;
+{
+ register tree t1, t2;
+ register enum tree_code code;
+ register int changed = 0;
+ register int i;
+
+ if (exp == NULL_TREE)
+ return exp;
+
+ code = TREE_CODE (exp);
+
+ if (code == CALL_EXPR)
+ return copy_node (exp);
+
+ /* Don't try and defeat a save_expr, as it should only be done once. */
+ if (code == SAVE_EXPR)
+ return exp;
+
+ switch (TREE_CODE_CLASS (code))
+ {
+ default:
+ abort ();
+
+ case 'c': /* a constant */
+ case 't': /* a type node */
+ case 'x': /* something random, like an identifier or an ERROR_MARK. */
+ return exp;
+
+ case 'd': /* A decl node */
+ t1 = break_out_calls (DECL_INITIAL (exp));
+ if (t1 != DECL_INITIAL (exp))
+ {
+ exp = copy_node (exp);
+ DECL_INITIAL (exp) = t1;
+ }
+ return exp;
+
+ case 'b': /* A block node */
+ {
+ /* Don't know how to handle these correctly yet. Must do a
+ break_out_calls on all DECL_INITIAL values for local variables,
+ and also break_out_calls on all sub-blocks and sub-statements. */
+ abort ();
+ }
+ return exp;
+
+ case 'e': /* an expression */
+ case 'r': /* a reference */
+ case 's': /* an expression with side effects */
+ for (i = tree_code_length[(int) code] - 1; i >= 0; i--)
+ {
+ t1 = break_out_calls (TREE_OPERAND (exp, i));
+ if (t1 != TREE_OPERAND (exp, i))
+ {
+ exp = copy_node (exp);
+ TREE_OPERAND (exp, i) = t1;
+ }
+ }
+ return exp;
+
+ case '<': /* a comparison expression */
+ case '2': /* a binary arithmetic expression */
+ t2 = break_out_calls (TREE_OPERAND (exp, 1));
+ if (t2 != TREE_OPERAND (exp, 1))
+ changed = 1;
+ case '1': /* a unary arithmetic expression */
+ t1 = break_out_calls (TREE_OPERAND (exp, 0));
+ if (t1 != TREE_OPERAND (exp, 0))
+ changed = 1;
+ if (changed)
+ {
+ if (tree_code_length[(int) code] == 1)
+ return build1 (code, TREE_TYPE (exp), t1);
+ else
+ return build (code, TREE_TYPE (exp), t1, t2);
+ }
+ return exp;
+ }
+
+}
+
+extern struct obstack *current_obstack;
+extern struct obstack permanent_obstack, class_obstack;
+extern struct obstack *saveable_obstack;
+
+/* Here is how primitive or already-canonicalized types' hash
+ codes are made. MUST BE CONSISTENT WITH tree.c !!! */
+#define TYPE_HASH(TYPE) ((HOST_WIDE_INT) (TYPE) & 0777777)
+
+/* Construct, lay out and return the type of methods belonging to class
+ BASETYPE and whose arguments are described by ARGTYPES and whose values
+ are described by RETTYPE. If each type exists already, reuse it. */
+tree
+build_cplus_method_type (basetype, rettype, argtypes)
+ tree basetype, rettype, argtypes;
+{
+ register tree t;
+ tree ptype;
+ int hashcode;
+
+ /* Make a node of the sort we want. */
+ t = make_node (METHOD_TYPE);
+
+ TYPE_METHOD_BASETYPE (t) = TYPE_MAIN_VARIANT (basetype);
+ TREE_TYPE (t) = rettype;
+ if (IS_SIGNATURE (basetype))
+ ptype = build_signature_pointer_type (TYPE_MAIN_VARIANT (basetype),
+ TYPE_READONLY (basetype),
+ TYPE_VOLATILE (basetype));
+ else
+ {
+ ptype = build_pointer_type (basetype);
+ ptype = build_type_variant (ptype, 1, 0);
+ }
+ /* The actual arglist for this function includes a "hidden" argument
+ which is "this". Put it into the list of argument types. */
+
+ argtypes = tree_cons (NULL_TREE, ptype, argtypes);
+ TYPE_ARG_TYPES (t) = argtypes;
+ TREE_SIDE_EFFECTS (argtypes) = 1; /* Mark first argtype as "artificial". */
+
+ /* If we already have such a type, use the old one and free this one.
+ Note that it also frees up the above cons cell if found. */
+ hashcode = TYPE_HASH (basetype) + TYPE_HASH (rettype) + type_hash_list (argtypes);
+ t = type_hash_canon (hashcode, t);
+
+ if (TYPE_SIZE (t) == 0)
+ layout_type (t);
+
+ return t;
+}
+
+tree
+build_cplus_staticfn_type (basetype, rettype, argtypes)
+ tree basetype, rettype, argtypes;
+{
+ register tree t;
+ int hashcode;
+
+ /* Make a node of the sort we want. */
+ t = make_node (FUNCTION_TYPE);
+
+ TYPE_METHOD_BASETYPE (t) = TYPE_MAIN_VARIANT (basetype);
+ TREE_TYPE (t) = rettype;
+
+ TYPE_ARG_TYPES (t) = argtypes;
+
+ /* If we already have such a type, use the old one and free this one.
+ Note that it also frees up the above cons cell if found. */
+ hashcode = TYPE_HASH (basetype) + TYPE_HASH (rettype) + type_hash_list (argtypes);
+ t = type_hash_canon (hashcode, t);
+
+ if (TYPE_SIZE (t) == 0)
+ layout_type (t);
+
+ return t;
+}
+
+tree
+build_cplus_array_type (elt_type, index_type)
+ tree elt_type;
+ tree index_type;
+{
+ register struct obstack *ambient_obstack = current_obstack;
+ register struct obstack *ambient_saveable_obstack = saveable_obstack;
+ tree t;
+
+ /* We need a new one. If both ELT_TYPE and INDEX_TYPE are permanent,
+ make this permanent too. */
+ if (TREE_PERMANENT (elt_type)
+ && (index_type == 0 || TREE_PERMANENT (index_type)))
+ {
+ current_obstack = &permanent_obstack;
+ saveable_obstack = &permanent_obstack;
+ }
+
+ t = build_array_type (elt_type, index_type);
+
+ /* Push these needs up so that initialization takes place
+ more easily. */
+ TYPE_NEEDS_CONSTRUCTING (t) = TYPE_NEEDS_CONSTRUCTING (TYPE_MAIN_VARIANT (elt_type));
+ TYPE_NEEDS_DESTRUCTOR (t) = TYPE_NEEDS_DESTRUCTOR (TYPE_MAIN_VARIANT (elt_type));
+ current_obstack = ambient_obstack;
+ saveable_obstack = ambient_saveable_obstack;
+ return t;
+}
+
+/* Add OFFSET to all base types of T.
+
+ OFFSET, which is a type offset, is number of bytes.
+
+ Note that we don't have to worry about having two paths to the
+ same base type, since this type owns its association list. */
+void
+propagate_binfo_offsets (binfo, offset)
+ tree binfo;
+ tree offset;
+{
+ tree binfos = BINFO_BASETYPES (binfo);
+ int i, n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+
+ for (i = 0; i < n_baselinks; /* note increment is done in the loop. */)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+
+ if (TREE_VIA_VIRTUAL (base_binfo))
+ i += 1;
+ else
+ {
+ int j;
+ tree base_binfos = BINFO_BASETYPES (base_binfo);
+ tree delta;
+
+ for (j = i+1; j < n_baselinks; j++)
+ if (! TREE_VIA_VIRTUAL (TREE_VEC_ELT (binfos, j)))
+ {
+ /* The next basetype offset must take into account the space
+ between the classes, not just the size of each class. */
+ delta = size_binop (MINUS_EXPR,
+ BINFO_OFFSET (TREE_VEC_ELT (binfos, j)),
+ BINFO_OFFSET (base_binfo));
+ break;
+ }
+
+#if 0
+ if (BINFO_OFFSET_ZEROP (base_binfo))
+ BINFO_OFFSET (base_binfo) = offset;
+ else
+ BINFO_OFFSET (base_binfo)
+ = size_binop (PLUS_EXPR, BINFO_OFFSET (base_binfo), offset);
+#else
+ BINFO_OFFSET (base_binfo) = offset;
+#endif
+ if (base_binfos)
+ {
+ int k;
+ tree chain = NULL_TREE;
+
+ /* Now unshare the structure beneath BASE_BINFO. */
+ for (k = TREE_VEC_LENGTH (base_binfos)-1;
+ k >= 0; k--)
+ {
+ tree base_base_binfo = TREE_VEC_ELT (base_binfos, k);
+ if (! TREE_VIA_VIRTUAL (base_base_binfo))
+ TREE_VEC_ELT (base_binfos, k)
+ = make_binfo (BINFO_OFFSET (base_base_binfo),
+ base_base_binfo,
+ BINFO_VTABLE (base_base_binfo),
+ BINFO_VIRTUALS (base_base_binfo),
+ chain);
+ chain = TREE_VEC_ELT (base_binfos, k);
+ TREE_VIA_PUBLIC (chain) = TREE_VIA_PUBLIC (base_base_binfo);
+ TREE_VIA_PROTECTED (chain) = TREE_VIA_PROTECTED (base_base_binfo);
+ }
+ /* Now propagate the offset to the base types. */
+ propagate_binfo_offsets (base_binfo, offset);
+ }
+
+ /* Go to our next class that counts for offset propagation. */
+ i = j;
+ if (i < n_baselinks)
+ offset = size_binop (PLUS_EXPR, offset, delta);
+ }
+ }
+}
+
+/* Compute the actual offsets that our virtual base classes
+ will have *for this type*. This must be performed after
+ the fields are laid out, since virtual baseclasses must
+ lay down at the end of the record.
+
+ Returns the maximum number of virtual functions any of the virtual
+ baseclasses provide. */
+int
+layout_vbasetypes (rec, max)
+ tree rec;
+ int max;
+{
+ /* Get all the virtual base types that this type uses.
+ The TREE_VALUE slot holds the virtual baseclass type. */
+ tree vbase_types = get_vbase_types (rec);
+
+#ifdef STRUCTURE_SIZE_BOUNDARY
+ unsigned record_align = MAX (STRUCTURE_SIZE_BOUNDARY, TYPE_ALIGN (rec));
+#else
+ unsigned record_align = MAX (BITS_PER_UNIT, TYPE_ALIGN (rec));
+#endif
+ int desired_align;
+
+ /* Record size so far is CONST_SIZE + VAR_SIZE bits,
+ where CONST_SIZE is an integer
+ and VAR_SIZE is a tree expression.
+ If VAR_SIZE is null, the size is just CONST_SIZE.
+ Naturally we try to avoid using VAR_SIZE. */
+ register unsigned const_size = 0;
+ register tree var_size = 0;
+ int nonvirtual_const_size;
+ tree nonvirtual_var_size;
+
+ CLASSTYPE_VBASECLASSES (rec) = vbase_types;
+
+ if (TREE_CODE (TYPE_SIZE (rec)) == INTEGER_CST)
+ const_size = TREE_INT_CST_LOW (TYPE_SIZE (rec));
+ else
+ var_size = TYPE_SIZE (rec);
+
+ nonvirtual_const_size = const_size;
+ nonvirtual_var_size = var_size;
+
+ while (vbase_types)
+ {
+ tree basetype = BINFO_TYPE (vbase_types);
+ tree offset;
+
+ desired_align = TYPE_ALIGN (basetype);
+ record_align = MAX (record_align, desired_align);
+
+ if (const_size == 0)
+ offset = integer_zero_node;
+ else
+ {
+ /* Give each virtual base type the alignment it wants. */
+ const_size = CEIL (const_size, TYPE_ALIGN (basetype))
+ * TYPE_ALIGN (basetype);
+ offset = size_int (CEIL (const_size, BITS_PER_UNIT));
+ }
+
+ if (CLASSTYPE_VSIZE (basetype) > max)
+ max = CLASSTYPE_VSIZE (basetype);
+ BINFO_OFFSET (vbase_types) = offset;
+
+ if (TREE_CODE (TYPE_SIZE (basetype)) == INTEGER_CST)
+ const_size += MAX (BITS_PER_UNIT,
+ TREE_INT_CST_LOW (TYPE_SIZE (basetype))
+ - TREE_INT_CST_LOW (CLASSTYPE_VBASE_SIZE (basetype)));
+ else if (var_size == 0)
+ var_size = TYPE_SIZE (basetype);
+ else
+ var_size = size_binop (PLUS_EXPR, var_size, TYPE_SIZE (basetype));
+
+ vbase_types = TREE_CHAIN (vbase_types);
+ }
+
+ /* Set the alignment in the complete type. We don't set CLASSTYPE_ALIGN
+ here, as that is for this class, without any virtual base classes. */
+ TYPE_ALIGN (rec) = record_align;
+ if (const_size != nonvirtual_const_size)
+ {
+ CLASSTYPE_VBASE_SIZE (rec)
+ = size_int (const_size - nonvirtual_const_size);
+ TYPE_SIZE (rec) = size_int (const_size);
+ }
+
+ /* Now propagate offset information throughout the lattice
+ under the vbase type. */
+ for (vbase_types = CLASSTYPE_VBASECLASSES (rec); vbase_types;
+ vbase_types = TREE_CHAIN (vbase_types))
+ {
+ tree base_binfos = BINFO_BASETYPES (vbase_types);
+
+ if (base_binfos)
+ {
+ tree chain = NULL_TREE;
+ int j;
+ /* Now unshare the structure beneath BASE_BINFO. */
+
+ for (j = TREE_VEC_LENGTH (base_binfos)-1;
+ j >= 0; j--)
+ {
+ tree base_base_binfo = TREE_VEC_ELT (base_binfos, j);
+ if (! TREE_VIA_VIRTUAL (base_base_binfo))
+ TREE_VEC_ELT (base_binfos, j)
+ = make_binfo (BINFO_OFFSET (base_base_binfo),
+ base_base_binfo,
+ BINFO_VTABLE (base_base_binfo),
+ BINFO_VIRTUALS (base_base_binfo),
+ chain);
+ chain = TREE_VEC_ELT (base_binfos, j);
+ TREE_VIA_PUBLIC (chain) = TREE_VIA_PUBLIC (base_base_binfo);
+ TREE_VIA_PROTECTED (chain) = TREE_VIA_PROTECTED (base_base_binfo);
+ }
+
+ propagate_binfo_offsets (vbase_types, BINFO_OFFSET (vbase_types));
+ }
+ }
+
+ return max;
+}
+
+/* Lay out the base types of a record type, REC.
+ Tentatively set the size and alignment of REC
+ according to the base types alone.
+
+ Offsets for immediate nonvirtual baseclasses are also computed here.
+
+ TYPE_BINFO (REC) should be NULL_TREE on entry, and this routine
+ creates a list of base_binfos in TYPE_BINFO (REC) from BINFOS.
+
+ Returns list of virtual base classes in a FIELD_DECL chain. */
+tree
+layout_basetypes (rec, binfos)
+ tree rec, binfos;
+{
+ /* Chain to hold all the new FIELD_DECLs which point at virtual
+ base classes. */
+ tree vbase_decls = NULL_TREE;
+
+#ifdef STRUCTURE_SIZE_BOUNDARY
+ unsigned record_align = MAX (STRUCTURE_SIZE_BOUNDARY, TYPE_ALIGN (rec));
+#else
+ unsigned record_align = MAX (BITS_PER_UNIT, TYPE_ALIGN (rec));
+#endif
+
+ /* Record size so far is CONST_SIZE + VAR_SIZE bits, where CONST_SIZE is
+ an integer and VAR_SIZE is a tree expression. If VAR_SIZE is null,
+ the size is just CONST_SIZE. Naturally we try to avoid using
+ VAR_SIZE. And so far, we've been sucessful. */
+#if 0
+ register tree var_size = 0;
+#endif
+
+ register unsigned const_size = 0;
+ int i, n_baseclasses = binfos ? TREE_VEC_LENGTH (binfos) : 0;
+
+ /* Handle basetypes almost like fields, but record their
+ offsets differently. */
+
+ for (i = 0; i < n_baseclasses; i++)
+ {
+ int inc, desired_align, int_vbase_size;
+ register tree base_binfo = TREE_VEC_ELT (binfos, i);
+ register tree basetype = BINFO_TYPE (base_binfo);
+ tree decl, offset;
+
+ if (TYPE_SIZE (basetype) == 0)
+ {
+#if 0
+ /* This error is now reported in xref_tag, thus giving better
+ location information. */
+ error_with_aggr_type (base_binfo,
+ "base class `%s' has incomplete type");
+
+ TREE_VIA_PUBLIC (base_binfo) = 1;
+ TREE_VIA_PROTECTED (base_binfo) = 0;
+ TREE_VIA_VIRTUAL (base_binfo) = 0;
+
+ /* Should handle this better so that
+
+ class A;
+ class B: private A { virtual void F(); };
+
+ does not dump core when compiled. */
+ my_friendly_abort (121);
+#endif
+ continue;
+ }
+
+ /* All basetypes are recorded in the association list of the
+ derived type. */
+
+ if (TREE_VIA_VIRTUAL (base_binfo))
+ {
+ int j;
+ char *name = (char *)alloca (TYPE_NAME_LENGTH (basetype)
+ + sizeof (VBASE_NAME) + 1);
+
+ /* The offset for a virtual base class is only used in computing
+ virtual function tables and for initializing virtual base
+ pointers. It is built once `get_vbase_types' is called. */
+
+ /* If this basetype can come from another vbase pointer
+ without an additional indirection, we will share
+ that pointer. If an indirection is involved, we
+ make our own pointer. */
+ for (j = 0; j < n_baseclasses; j++)
+ {
+ tree other_base_binfo = TREE_VEC_ELT (binfos, j);
+ if (! TREE_VIA_VIRTUAL (other_base_binfo)
+ && binfo_member (basetype,
+ CLASSTYPE_VBASECLASSES (BINFO_TYPE (other_base_binfo))))
+ goto got_it;
+ }
+ sprintf (name, VBASE_NAME_FORMAT, TYPE_NAME_STRING (basetype));
+ decl = build_lang_decl (FIELD_DECL, get_identifier (name),
+ build_pointer_type (basetype));
+ /* If you change any of the below, take a look at all the
+ other VFIELD_BASEs and VTABLE_BASEs in the code, and change
+ them too. */
+ DECL_ASSEMBLER_NAME (decl) = get_identifier (VTABLE_BASE);
+ DECL_VIRTUAL_P (decl) = 1;
+ DECL_FIELD_CONTEXT (decl) = rec;
+ DECL_CLASS_CONTEXT (decl) = rec;
+ DECL_FCONTEXT (decl) = basetype;
+ DECL_FIELD_SIZE (decl) = 0;
+ DECL_ALIGN (decl) = TYPE_ALIGN (ptr_type_node);
+ TREE_CHAIN (decl) = vbase_decls;
+ BINFO_VPTR_FIELD (base_binfo) = decl;
+ vbase_decls = decl;
+
+ if (warn_nonvdtor && TYPE_HAS_DESTRUCTOR (basetype)
+ && DECL_VINDEX (TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (basetype), 0)) == NULL_TREE)
+ {
+ warning_with_decl (TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (basetype), 0),
+ "destructor `%s' non-virtual");
+ warning ("in inheritance relationship `%s: virtual %s'",
+ TYPE_NAME_STRING (rec),
+ TYPE_NAME_STRING (basetype));
+ }
+ got_it:
+ /* The space this decl occupies has already been accounted for. */
+ continue;
+ }
+
+ if (const_size == 0)
+ offset = integer_zero_node;
+ else
+ {
+ /* Give each base type the alignment it wants. */
+ const_size = CEIL (const_size, TYPE_ALIGN (basetype))
+ * TYPE_ALIGN (basetype);
+ offset = size_int ((const_size + BITS_PER_UNIT - 1) / BITS_PER_UNIT);
+
+#if 0
+ /* bpk: Disabled this check until someone is willing to
+ claim it as theirs and explain exactly what circumstances
+ warrant the warning. */
+ if (warn_nonvdtor && TYPE_HAS_DESTRUCTOR (basetype)
+ && DECL_VINDEX (TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (basetype), 0)) == NULL_TREE)
+ {
+ warning_with_decl (TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (basetype), 0),
+ "destructor `%s' non-virtual");
+ warning ("in inheritance relationship `%s:%s %s'",
+ TYPE_NAME_STRING (rec),
+ TREE_VIA_VIRTUAL (base_binfo) ? " virtual" : "",
+ TYPE_NAME_STRING (basetype));
+ }
+#endif
+ }
+ BINFO_OFFSET (base_binfo) = offset;
+ if (CLASSTYPE_VSIZE (basetype))
+ {
+ BINFO_VTABLE (base_binfo) = TYPE_BINFO_VTABLE (basetype);
+ BINFO_VIRTUALS (base_binfo) = TYPE_BINFO_VIRTUALS (basetype);
+ }
+ TREE_CHAIN (base_binfo) = TYPE_BINFO (rec);
+ TYPE_BINFO (rec) = base_binfo;
+
+ /* Add only the amount of storage not present in
+ the virtual baseclasses. */
+
+ int_vbase_size = TREE_INT_CST_LOW (CLASSTYPE_VBASE_SIZE (basetype));
+ if (TREE_INT_CST_LOW (TYPE_SIZE (basetype)) > int_vbase_size)
+ {
+ inc = MAX (record_align,
+ (TREE_INT_CST_LOW (TYPE_SIZE (basetype))
+ - int_vbase_size));
+
+ /* Record must have at least as much alignment as any field. */
+ desired_align = TYPE_ALIGN (basetype);
+ record_align = MAX (record_align, desired_align);
+
+ const_size += inc;
+ }
+ }
+
+ if (const_size)
+ CLASSTYPE_SIZE (rec) = size_int (const_size);
+ else
+ CLASSTYPE_SIZE (rec) = integer_zero_node;
+ CLASSTYPE_ALIGN (rec) = record_align;
+
+ return vbase_decls;
+}
+
+/* Hashing of lists so that we don't make duplicates.
+ The entry point is `list_hash_canon'. */
+
+/* Each hash table slot is a bucket containing a chain
+ of these structures. */
+
+struct list_hash
+{
+ struct list_hash *next; /* Next structure in the bucket. */
+ int hashcode; /* Hash code of this list. */
+ tree list; /* The list recorded here. */
+};
+
+/* Now here is the hash table. When recording a list, it is added
+ to the slot whose index is the hash code mod the table size.
+ Note that the hash table is used for several kinds of lists.
+ While all these live in the same table, they are completely independent,
+ and the hash code is computed differently for each of these. */
+
+#define TYPE_HASH_SIZE 59
+struct list_hash *list_hash_table[TYPE_HASH_SIZE];
+
+/* Compute a hash code for a list (chain of TREE_LIST nodes
+ with goodies in the TREE_PURPOSE, TREE_VALUE, and bits of the
+ TREE_COMMON slots), by adding the hash codes of the individual entries. */
+
+int
+list_hash (list)
+ tree list;
+{
+ register int hashcode = 0;
+
+ if (TREE_CHAIN (list))
+ hashcode += TYPE_HASH (TREE_CHAIN (list));
+
+ if (TREE_VALUE (list))
+ hashcode += TYPE_HASH (TREE_VALUE (list));
+ else
+ hashcode += 1007;
+ if (TREE_PURPOSE (list))
+ hashcode += TYPE_HASH (TREE_PURPOSE (list));
+ else
+ hashcode += 1009;
+ return hashcode;
+}
+
+/* Look in the type hash table for a type isomorphic to TYPE.
+ If one is found, return it. Otherwise return 0. */
+
+tree
+list_hash_lookup (hashcode, list)
+ int hashcode;
+ tree list;
+{
+ register struct list_hash *h;
+ for (h = list_hash_table[hashcode % TYPE_HASH_SIZE]; h; h = h->next)
+ if (h->hashcode == hashcode
+ && TREE_VIA_VIRTUAL (h->list) == TREE_VIA_VIRTUAL (list)
+ && TREE_VIA_PUBLIC (h->list) == TREE_VIA_PUBLIC (list)
+ && TREE_VIA_PROTECTED (h->list) == TREE_VIA_PROTECTED (list)
+ && TREE_PURPOSE (h->list) == TREE_PURPOSE (list)
+ && TREE_VALUE (h->list) == TREE_VALUE (list)
+ && TREE_CHAIN (h->list) == TREE_CHAIN (list))
+ {
+ my_friendly_assert (TREE_TYPE (h->list) == TREE_TYPE (list), 299);
+ return h->list;
+ }
+ return 0;
+}
+
+/* Add an entry to the list-hash-table
+ for a list TYPE whose hash code is HASHCODE. */
+
+void
+list_hash_add (hashcode, list)
+ int hashcode;
+ tree list;
+{
+ register struct list_hash *h;
+
+ h = (struct list_hash *) obstack_alloc (&class_obstack, sizeof (struct list_hash));
+ h->hashcode = hashcode;
+ h->list = list;
+ h->next = list_hash_table[hashcode % TYPE_HASH_SIZE];
+ list_hash_table[hashcode % TYPE_HASH_SIZE] = h;
+}
+
+/* Given TYPE, and HASHCODE its hash code, return the canonical
+ object for an identical list if one already exists.
+ Otherwise, return TYPE, and record it as the canonical object
+ if it is a permanent object.
+
+ To use this function, first create a list of the sort you want.
+ Then compute its hash code from the fields of the list that
+ make it different from other similar lists.
+ Then call this function and use the value.
+ This function frees the list you pass in if it is a duplicate. */
+
+/* Set to 1 to debug without canonicalization. Never set by program. */
+static int debug_no_list_hash = 0;
+
+tree
+list_hash_canon (hashcode, list)
+ int hashcode;
+ tree list;
+{
+ tree t1;
+
+ if (debug_no_list_hash)
+ return list;
+
+ t1 = list_hash_lookup (hashcode, list);
+ if (t1 != 0)
+ {
+ obstack_free (&class_obstack, list);
+ return t1;
+ }
+
+ /* If this is a new list, record it for later reuse. */
+ list_hash_add (hashcode, list);
+
+ return list;
+}
+
+tree
+hash_tree_cons (via_public, via_virtual, via_protected, purpose, value, chain)
+ int via_public, via_virtual, via_protected;
+ tree purpose, value, chain;
+{
+ struct obstack *ambient_obstack = current_obstack;
+ tree t;
+ int hashcode;
+
+ current_obstack = &class_obstack;
+ t = tree_cons (purpose, value, chain);
+ TREE_VIA_PUBLIC (t) = via_public;
+ TREE_VIA_PROTECTED (t) = via_protected;
+ TREE_VIA_VIRTUAL (t) = via_virtual;
+ hashcode = list_hash (t);
+ t = list_hash_canon (hashcode, t);
+ current_obstack = ambient_obstack;
+ return t;
+}
+
+/* Constructor for hashed lists. */
+tree
+hash_tree_chain (value, chain)
+ tree value, chain;
+{
+ struct obstack *ambient_obstack = current_obstack;
+ tree t;
+ int hashcode;
+
+ current_obstack = &class_obstack;
+ t = tree_cons (NULL_TREE, value, chain);
+ hashcode = list_hash (t);
+ t = list_hash_canon (hashcode, t);
+ current_obstack = ambient_obstack;
+ return t;
+}
+
+/* Similar, but used for concatenating two lists. */
+tree
+hash_chainon (list1, list2)
+ tree list1, list2;
+{
+ if (list2 == 0)
+ return list1;
+ if (list1 == 0)
+ return list2;
+ if (TREE_CHAIN (list1) == NULL_TREE)
+ return hash_tree_chain (TREE_VALUE (list1), list2);
+ return hash_tree_chain (TREE_VALUE (list1),
+ hash_chainon (TREE_CHAIN (list1), list2));
+}
+
+static tree
+get_identifier_list (value)
+ tree value;
+{
+ tree list = IDENTIFIER_AS_LIST (value);
+ if (list != NULL_TREE
+ && (TREE_CODE (list) != TREE_LIST
+ || TREE_VALUE (list) != value))
+ list = NULL_TREE;
+ else if (IDENTIFIER_HAS_TYPE_VALUE (value)
+ && TREE_CODE (IDENTIFIER_TYPE_VALUE (value)) == RECORD_TYPE
+ && IDENTIFIER_TYPE_VALUE (value)
+ == TYPE_MAIN_VARIANT (IDENTIFIER_TYPE_VALUE (value)))
+ {
+ tree type = IDENTIFIER_TYPE_VALUE (value);
+
+ if (TYPE_PTRMEMFUNC_P (type))
+ list = NULL_TREE;
+ else if (type == current_class_type)
+ /* Don't mess up the constructor name. */
+ list = tree_cons (NULL_TREE, value, NULL_TREE);
+ else
+ {
+ register tree id;
+ /* This will return the correct thing for regular types,
+ nested types, and templates. Yay! */
+ if (TYPE_NESTED_NAME (type))
+ id = TYPE_NESTED_NAME (type);
+ else
+ id = TYPE_IDENTIFIER (type);
+
+ if (CLASSTYPE_ID_AS_LIST (type) == NULL_TREE)
+ CLASSTYPE_ID_AS_LIST (type)
+ = perm_tree_cons (NULL_TREE, id, NULL_TREE);
+ list = CLASSTYPE_ID_AS_LIST (type);
+ }
+ }
+ return list;
+}
+
+tree
+get_decl_list (value)
+ tree value;
+{
+ tree list = NULL_TREE;
+
+ if (TREE_CODE (value) == IDENTIFIER_NODE)
+ list = get_identifier_list (value);
+ else if (TREE_CODE (value) == RECORD_TYPE
+ && TYPE_LANG_SPECIFIC (value))
+ list = CLASSTYPE_AS_LIST (value);
+
+ if (list != NULL_TREE)
+ {
+ my_friendly_assert (TREE_CHAIN (list) == NULL_TREE, 301);
+ return list;
+ }
+
+ return build_decl_list (NULL_TREE, value);
+}
+
+/* Look in the type hash table for a type isomorphic to
+ `build_tree_list (NULL_TREE, VALUE)'.
+ If one is found, return it. Otherwise return 0. */
+
+tree
+list_hash_lookup_or_cons (value)
+ tree value;
+{
+ register int hashcode = TYPE_HASH (value);
+ register struct list_hash *h;
+ struct obstack *ambient_obstack;
+ tree list = NULL_TREE;
+
+ if (TREE_CODE (value) == IDENTIFIER_NODE)
+ list = get_identifier_list (value);
+ else if (TREE_CODE (value) == TYPE_DECL
+ && TREE_CODE (TREE_TYPE (value)) == RECORD_TYPE
+ && TYPE_LANG_SPECIFIC (TREE_TYPE (value)))
+ list = CLASSTYPE_ID_AS_LIST (TREE_TYPE (value));
+ else if (TREE_CODE (value) == RECORD_TYPE
+ && TYPE_LANG_SPECIFIC (value))
+ list = CLASSTYPE_AS_LIST (value);
+
+ if (list != NULL_TREE)
+ {
+ my_friendly_assert (TREE_CHAIN (list) == NULL_TREE, 302);
+ return list;
+ }
+
+ if (debug_no_list_hash)
+ return hash_tree_chain (value, NULL_TREE);
+
+ for (h = list_hash_table[hashcode % TYPE_HASH_SIZE]; h; h = h->next)
+ if (h->hashcode == hashcode
+ && TREE_VIA_VIRTUAL (h->list) == 0
+ && TREE_VIA_PUBLIC (h->list) == 0
+ && TREE_VIA_PROTECTED (h->list) == 0
+ && TREE_PURPOSE (h->list) == 0
+ && TREE_VALUE (h->list) == value)
+ {
+ my_friendly_assert (TREE_TYPE (h->list) == 0, 303);
+ my_friendly_assert (TREE_CHAIN (h->list) == 0, 304);
+ return h->list;
+ }
+
+ ambient_obstack = current_obstack;
+ current_obstack = &class_obstack;
+ list = build_tree_list (NULL_TREE, value);
+ list_hash_add (hashcode, list);
+ current_obstack = ambient_obstack;
+ return list;
+}
+
+/* Build an association between TYPE and some parameters:
+
+ OFFSET is the offset added to `this' to convert it to a pointer
+ of type `TYPE *'
+
+ BINFO is the base binfo to use, if we are deriving from one. This
+ is necessary, as we want specialized parent binfos from base
+ classes, so that the VTABLE_NAMEs of bases are for the most derived
+ type, instead of of the simple type.
+
+ VTABLE is the virtual function table with which to initialize
+ sub-objects of type TYPE.
+
+ VIRTUALS are the virtual functions sitting in VTABLE.
+
+ CHAIN are more associations we must retain. */
+
+tree
+make_binfo (offset, binfo, vtable, virtuals, chain)
+ tree offset, binfo;
+ tree vtable, virtuals;
+ tree chain;
+{
+ tree new_binfo = make_tree_vec (6);
+ tree type;
+
+ if (TREE_CODE (binfo) == TREE_VEC)
+ type = BINFO_TYPE (binfo);
+ else
+ {
+ type = binfo;
+ binfo = TYPE_BINFO (binfo);
+ }
+
+ TREE_CHAIN (new_binfo) = chain;
+ if (chain)
+ TREE_USED (new_binfo) = TREE_USED (chain);
+
+ TREE_TYPE (new_binfo) = TYPE_MAIN_VARIANT (type);
+ BINFO_OFFSET (new_binfo) = offset;
+ BINFO_VTABLE (new_binfo) = vtable;
+ BINFO_VIRTUALS (new_binfo) = virtuals;
+ BINFO_VPTR_FIELD (new_binfo) = NULL_TREE;
+
+ if (binfo && BINFO_BASETYPES (binfo) != NULL_TREE)
+ BINFO_BASETYPES (new_binfo) = copy_node (BINFO_BASETYPES (binfo));
+ return new_binfo;
+}
+
+tree
+copy_binfo (list)
+ tree list;
+{
+ tree binfo = copy_list (list);
+ tree rval = binfo;
+ while (binfo)
+ {
+ TREE_USED (binfo) = 0;
+ if (BINFO_BASETYPES (binfo))
+ BINFO_BASETYPES (binfo) = copy_node (BINFO_BASETYPES (binfo));
+ binfo = TREE_CHAIN (binfo);
+ }
+ return rval;
+}
+
+/* Return the binfo value for ELEM in TYPE. */
+
+tree
+binfo_value (elem, type)
+ tree elem;
+ tree type;
+{
+ if (get_base_distance (elem, type, 0, (tree *)0) == -2)
+ compiler_error ("base class `%s' ambiguous in binfo_value",
+ TYPE_NAME_STRING (elem));
+ if (elem == type)
+ return TYPE_BINFO (type);
+ if (TREE_CODE (elem) == RECORD_TYPE && TYPE_BINFO (elem) == type)
+ return type;
+ return get_binfo (elem, type, 0);
+}
+
+tree
+reverse_path (path)
+ tree path;
+{
+ register tree prev = 0, tmp, next;
+ for (tmp = path; tmp; tmp = next)
+ {
+ next = BINFO_INHERITANCE_CHAIN (tmp);
+ BINFO_INHERITANCE_CHAIN (tmp) = prev;
+ prev = tmp;
+ }
+ return prev;
+}
+
+tree
+virtual_member (elem, list)
+ tree elem;
+ tree list;
+{
+ tree t;
+ tree rval, nval;
+
+ for (t = list; t; t = TREE_CHAIN (t))
+ if (elem == BINFO_TYPE (t))
+ return t;
+ rval = 0;
+ for (t = list; t; t = TREE_CHAIN (t))
+ {
+ tree binfos = BINFO_BASETYPES (t);
+ int i;
+
+ if (binfos != NULL_TREE)
+ for (i = TREE_VEC_LENGTH (binfos)-1; i >= 0; i--)
+ {
+ nval = binfo_value (elem, BINFO_TYPE (TREE_VEC_ELT (binfos, i)));
+ if (nval)
+ {
+ if (rval && BINFO_OFFSET (nval) != BINFO_OFFSET (rval))
+ my_friendly_abort (104);
+ rval = nval;
+ }
+ }
+ }
+ return rval;
+}
+
+/* Return the offset (as an INTEGER_CST) for ELEM in LIST.
+ INITIAL_OFFSET is the value to add to the offset that ELEM's
+ binfo entry in LIST provides.
+
+ Returns NULL if ELEM does not have an binfo value in LIST. */
+
+tree
+virtual_offset (elem, list, initial_offset)
+ tree elem;
+ tree list;
+ tree initial_offset;
+{
+ tree vb, offset;
+ tree rval, nval;
+
+ for (vb = list; vb; vb = TREE_CHAIN (vb))
+ if (elem == BINFO_TYPE (vb))
+ return size_binop (PLUS_EXPR, initial_offset, BINFO_OFFSET (vb));
+ rval = 0;
+ for (vb = list; vb; vb = TREE_CHAIN (vb))
+ {
+ tree binfos = BINFO_BASETYPES (vb);
+ int i;
+
+ if (binfos == NULL_TREE)
+ continue;
+
+ for (i = TREE_VEC_LENGTH (binfos)-1; i >= 0; i--)
+ {
+ nval = binfo_value (elem, BINFO_TYPE (TREE_VEC_ELT (binfos, i)));
+ if (nval)
+ {
+ if (rval && BINFO_OFFSET (nval) != BINFO_OFFSET (rval))
+ my_friendly_abort (105);
+ offset = BINFO_OFFSET (vb);
+ rval = nval;
+ }
+ }
+ }
+ if (rval == NULL_TREE)
+ return rval;
+ return size_binop (PLUS_EXPR, offset, BINFO_OFFSET (rval));
+}
+
+void
+debug_binfo (elem)
+ tree elem;
+{
+ int i;
+ tree virtuals;
+
+ fprintf (stderr, "type \"%s\"; offset = %d\n",
+ TYPE_NAME_STRING (BINFO_TYPE (elem)),
+ TREE_INT_CST_LOW (BINFO_OFFSET (elem)));
+ fprintf (stderr, "vtable type:\n");
+ debug_tree (BINFO_TYPE (elem));
+ if (BINFO_VTABLE (elem))
+ fprintf (stderr, "vtable decl \"%s\"\n", IDENTIFIER_POINTER (DECL_NAME (BINFO_VTABLE (elem))));
+ else
+ fprintf (stderr, "no vtable decl yet\n");
+ fprintf (stderr, "virtuals:\n");
+ virtuals = BINFO_VIRTUALS (elem);
+ if (virtuals != 0)
+ {
+ virtuals = TREE_CHAIN (virtuals);
+ if (flag_dossier)
+ virtuals = TREE_CHAIN (virtuals);
+ }
+ i = 1;
+ while (virtuals)
+ {
+ tree fndecl = TREE_OPERAND (FNADDR_FROM_VTABLE_ENTRY (TREE_VALUE (virtuals)), 0);
+ fprintf (stderr, "%s [%d =? %d]\n",
+ IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (fndecl)),
+ i, TREE_INT_CST_LOW (DECL_VINDEX (fndecl)));
+ virtuals = TREE_CHAIN (virtuals);
+ i += 1;
+ }
+}
+
+/* Return the length of a chain of nodes chained through DECL_CHAIN.
+ We expect a null pointer to mark the end of the chain.
+ This is the Lisp primitive `length'. */
+
+int
+decl_list_length (t)
+ tree t;
+{
+ register tree tail;
+ register int len = 0;
+
+ my_friendly_assert (TREE_CODE (t) == FUNCTION_DECL
+ || TREE_CODE (t) == TEMPLATE_DECL, 300);
+ for (tail = t; tail; tail = DECL_CHAIN (tail))
+ len++;
+
+ return len;
+}
+
+int
+count_functions (t)
+ tree t;
+{
+ if (TREE_CODE (t) == FUNCTION_DECL)
+ return 1;
+ else if (TREE_CODE (t) == TREE_LIST)
+ return decl_list_length (TREE_VALUE (t));
+
+ my_friendly_abort (359);
+ return 0;
+}
+
+/* Like value_member, but for DECL_CHAINs. */
+tree
+decl_value_member (elem, list)
+ tree elem, list;
+{
+ while (list)
+ {
+ if (elem == list)
+ return list;
+ list = DECL_CHAIN (list);
+ }
+ return NULL_TREE;
+}
+
+int
+is_overloaded_fn (x)
+ tree x;
+{
+ if (TREE_CODE (x) == FUNCTION_DECL)
+ return 1;
+
+ if (TREE_CODE (x) == TREE_LIST
+ && (TREE_CODE (TREE_VALUE (x)) == FUNCTION_DECL
+ || TREE_CODE (TREE_VALUE (x)) == TEMPLATE_DECL))
+ return 1;
+
+ return 0;
+}
+
+int
+really_overloaded_fn (x)
+ tree x;
+{
+ if (TREE_CODE (x) == TREE_LIST
+ && (TREE_CODE (TREE_VALUE (x)) == FUNCTION_DECL
+ || TREE_CODE (TREE_VALUE (x)) == TEMPLATE_DECL))
+ return 1;
+
+ return 0;
+}
+
+tree
+get_first_fn (from)
+ tree from;
+{
+ if (TREE_CODE (from) == FUNCTION_DECL)
+ return from;
+
+ my_friendly_assert (TREE_CODE (from) == TREE_LIST, 9);
+
+ return TREE_VALUE (from);
+}
+
+tree
+fnaddr_from_vtable_entry (entry)
+ tree entry;
+{
+ if (flag_vtable_thunks)
+ {
+ tree func = entry;
+ if (TREE_CODE (func) == ADDR_EXPR)
+ func = TREE_OPERAND (func, 0);
+ if (TREE_CODE (func) == THUNK_DECL)
+ return DECL_INITIAL (func);
+ else
+ return entry;
+ }
+ else
+ return TREE_VALUE (TREE_CHAIN (TREE_CHAIN (CONSTRUCTOR_ELTS (entry))));
+}
+
+void
+set_fnaddr_from_vtable_entry (entry, value)
+ tree entry, value;
+{
+ if (flag_vtable_thunks)
+ abort ();
+ else
+ TREE_VALUE (TREE_CHAIN (TREE_CHAIN (CONSTRUCTOR_ELTS (entry)))) = value;
+}
+
+tree
+function_arg_chain (t)
+ tree t;
+{
+ return TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (t)));
+}
+
+int
+promotes_to_aggr_type (t, code)
+ tree t;
+ enum tree_code code;
+{
+ if (TREE_CODE (t) == code)
+ t = TREE_TYPE (t);
+ return IS_AGGR_TYPE (t);
+}
+
+int
+is_aggr_type_2 (t1, t2)
+ tree t1, t2;
+{
+ if (TREE_CODE (t1) != TREE_CODE (t2))
+ return 0;
+ return IS_AGGR_TYPE (t1) && IS_AGGR_TYPE (t2);
+}
+
+/* Give message using types TYPE1 and TYPE2 as arguments.
+ PFN is the function which will print the message;
+ S is the format string for PFN to use. */
+void
+message_2_types (pfn, s, type1, type2)
+ void (*pfn) ();
+ char *s;
+ tree type1, type2;
+{
+ tree name1 = TYPE_NAME (type1);
+ tree name2 = TYPE_NAME (type2);
+ if (TREE_CODE (name1) == TYPE_DECL)
+ name1 = DECL_NAME (name1);
+ if (TREE_CODE (name2) == TYPE_DECL)
+ name2 = DECL_NAME (name2);
+ (*pfn) (s, IDENTIFIER_POINTER (name1), IDENTIFIER_POINTER (name2));
+}
+
+#define PRINT_RING_SIZE 4
+
+char *
+lang_printable_name (decl)
+ tree decl;
+{
+ static tree decl_ring[PRINT_RING_SIZE];
+ static char *print_ring[PRINT_RING_SIZE];
+ static int ring_counter;
+ int i;
+
+ /* Only cache functions. */
+ if (TREE_CODE (decl) != FUNCTION_DECL
+ || DECL_LANG_SPECIFIC (decl) == 0)
+ return decl_as_string (decl, 1);
+
+ /* See if this print name is lying around. */
+ for (i = 0; i < PRINT_RING_SIZE; i++)
+ if (decl_ring[i] == decl)
+ /* yes, so return it. */
+ return print_ring[i];
+
+ if (++ring_counter == PRINT_RING_SIZE)
+ ring_counter = 0;
+
+ if (current_function_decl != NULL_TREE)
+ {
+ if (decl_ring[ring_counter] == current_function_decl)
+ ring_counter += 1;
+ if (ring_counter == PRINT_RING_SIZE)
+ ring_counter = 0;
+ if (decl_ring[ring_counter] == current_function_decl)
+ my_friendly_abort (106);
+ }
+
+ if (print_ring[ring_counter])
+ free (print_ring[ring_counter]);
+
+ {
+ int print_ret_type_p
+ = (!DECL_CONSTRUCTOR_P (decl)
+ && !DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (decl)));
+
+ char *name = (char *)decl_as_string (decl, print_ret_type_p);
+ print_ring[ring_counter] = (char *)malloc (strlen (name) + 1);
+ strcpy (print_ring[ring_counter], name);
+ decl_ring[ring_counter] = decl;
+ }
+ return print_ring[ring_counter];
+}
+
+/* Comparison function for sorting identifiers in RAISES lists.
+ Note that because IDENTIFIER_NODEs are unique, we can sort
+ them by address, saving an indirection. */
+static int
+id_cmp (p1, p2)
+ tree *p1, *p2;
+{
+ return (HOST_WIDE_INT)TREE_VALUE (*p1) - (HOST_WIDE_INT)TREE_VALUE (*p2);
+}
+
+/* Build the FUNCTION_TYPE or METHOD_TYPE which may raise exceptions
+ listed in RAISES. */
+tree
+build_exception_variant (ctype, type, raises)
+ tree ctype, type;
+ tree raises;
+{
+ int i;
+ tree v = TYPE_MAIN_VARIANT (type);
+ tree t, t2, cname;
+ tree *a = (tree *)alloca ((list_length (raises)+1) * sizeof (tree));
+ int constp = TYPE_READONLY (type);
+ int volatilep = TYPE_VOLATILE (type);
+
+ for (v = TYPE_NEXT_VARIANT (v); v; v = TYPE_NEXT_VARIANT (v))
+ {
+ if (TYPE_READONLY (v) != constp
+ || TYPE_VOLATILE (v) != volatilep)
+ continue;
+
+ t = raises;
+ t2 = TYPE_RAISES_EXCEPTIONS (v);
+ while (t && t2)
+ {
+ if (TREE_TYPE (t) == TREE_TYPE (t2))
+ {
+ t = TREE_CHAIN (t);
+ t2 = TREE_CHAIN (t2);
+ }
+ else break;
+ }
+ if (t || t2)
+ continue;
+ /* List of exceptions raised matches previously found list.
+
+ @@ Nice to free up storage used in consing up the
+ @@ list of exceptions raised. */
+ return v;
+ }
+
+ /* Need to build a new variant. */
+ v = copy_node (type);
+ TYPE_NEXT_VARIANT (v) = TYPE_NEXT_VARIANT (type);
+ TYPE_NEXT_VARIANT (type) = v;
+ if (raises && ! TREE_PERMANENT (raises))
+ {
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+ raises = copy_list (raises);
+ pop_obstacks ();
+ }
+ TYPE_RAISES_EXCEPTIONS (v) = raises;
+ return v;
+}
+
+/* Subroutine of copy_to_permanent
+
+ Assuming T is a node build bottom-up, make it all exist on
+ permanent obstack, if it is not permanent already. */
+static tree
+make_deep_copy (t)
+ tree t;
+{
+ enum tree_code code;
+
+ if (t == NULL_TREE || TREE_PERMANENT (t))
+ return t;
+
+ switch (code = TREE_CODE (t))
+ {
+ case ERROR_MARK:
+ return error_mark_node;
+
+ case VAR_DECL:
+ case FUNCTION_DECL:
+ case CONST_DECL:
+ break;
+
+ case PARM_DECL:
+ {
+ tree chain = TREE_CHAIN (t);
+ t = copy_node (t);
+ TREE_CHAIN (t) = make_deep_copy (chain);
+ TREE_TYPE (t) = make_deep_copy (TREE_TYPE (t));
+ DECL_INITIAL (t) = make_deep_copy (DECL_INITIAL (t));
+ DECL_SIZE (t) = make_deep_copy (DECL_SIZE (t));
+ return t;
+ }
+
+ case TREE_LIST:
+ {
+ tree chain = TREE_CHAIN (t);
+ t = copy_node (t);
+ TREE_PURPOSE (t) = make_deep_copy (TREE_PURPOSE (t));
+ TREE_VALUE (t) = make_deep_copy (TREE_VALUE (t));
+ TREE_CHAIN (t) = make_deep_copy (chain);
+ return t;
+ }
+
+ case TREE_VEC:
+ {
+ int len = TREE_VEC_LENGTH (t);
+
+ t = copy_node (t);
+ while (len--)
+ TREE_VEC_ELT (t, len) = make_deep_copy (TREE_VEC_ELT (t, len));
+ return t;
+ }
+
+ case INTEGER_CST:
+ case REAL_CST:
+ case STRING_CST:
+ return copy_node (t);
+
+ case COND_EXPR:
+ case TARGET_EXPR:
+ case NEW_EXPR:
+ t = copy_node (t);
+ TREE_OPERAND (t, 0) = make_deep_copy (TREE_OPERAND (t, 0));
+ TREE_OPERAND (t, 1) = make_deep_copy (TREE_OPERAND (t, 1));
+ TREE_OPERAND (t, 2) = make_deep_copy (TREE_OPERAND (t, 2));
+ return t;
+
+ case SAVE_EXPR:
+ t = copy_node (t);
+ TREE_OPERAND (t, 0) = make_deep_copy (TREE_OPERAND (t, 0));
+ return t;
+
+ case MODIFY_EXPR:
+ case PLUS_EXPR:
+ case MINUS_EXPR:
+ case MULT_EXPR:
+ case TRUNC_DIV_EXPR:
+ case TRUNC_MOD_EXPR:
+ case MIN_EXPR:
+ case MAX_EXPR:
+ case LSHIFT_EXPR:
+ case RSHIFT_EXPR:
+ case BIT_IOR_EXPR:
+ case BIT_XOR_EXPR:
+ case BIT_AND_EXPR:
+ case BIT_ANDTC_EXPR:
+ case TRUTH_ANDIF_EXPR:
+ case TRUTH_ORIF_EXPR:
+ case LT_EXPR:
+ case LE_EXPR:
+ case GT_EXPR:
+ case GE_EXPR:
+ case EQ_EXPR:
+ case NE_EXPR:
+ case CEIL_DIV_EXPR:
+ case FLOOR_DIV_EXPR:
+ case ROUND_DIV_EXPR:
+ case CEIL_MOD_EXPR:
+ case FLOOR_MOD_EXPR:
+ case ROUND_MOD_EXPR:
+ case COMPOUND_EXPR:
+ case PREDECREMENT_EXPR:
+ case PREINCREMENT_EXPR:
+ case POSTDECREMENT_EXPR:
+ case POSTINCREMENT_EXPR:
+ case CALL_EXPR:
+ t = copy_node (t);
+ TREE_OPERAND (t, 0) = make_deep_copy (TREE_OPERAND (t, 0));
+ TREE_OPERAND (t, 1) = make_deep_copy (TREE_OPERAND (t, 1));
+ return t;
+
+ case CONVERT_EXPR:
+ case ADDR_EXPR:
+ case INDIRECT_REF:
+ case NEGATE_EXPR:
+ case BIT_NOT_EXPR:
+ case TRUTH_NOT_EXPR:
+ case NOP_EXPR:
+ case COMPONENT_REF:
+ t = copy_node (t);
+ TREE_OPERAND (t, 0) = make_deep_copy (TREE_OPERAND (t, 0));
+ return t;
+
+ /* This list is incomplete, but should suffice for now.
+ It is very important that `sorry' does not call
+ `report_error_function'. That could cause an infinite loop. */
+ default:
+ sorry ("initializer contains unrecognized tree code");
+ return error_mark_node;
+
+ }
+ my_friendly_abort (107);
+ /* NOTREACHED */
+ return NULL_TREE;
+}
+
+/* Assuming T is a node built bottom-up, make it all exist on
+ permanent obstack, if it is not permanent already. */
+tree
+copy_to_permanent (t)
+ tree t;
+{
+ register struct obstack *ambient_obstack = current_obstack;
+ register struct obstack *ambient_saveable_obstack = saveable_obstack;
+
+ if (t == NULL_TREE || TREE_PERMANENT (t))
+ return t;
+
+ saveable_obstack = &permanent_obstack;
+ current_obstack = saveable_obstack;
+
+ t = make_deep_copy (t);
+
+ current_obstack = ambient_obstack;
+ saveable_obstack = ambient_saveable_obstack;
+
+ return t;
+}
+
+void
+print_lang_statistics ()
+{
+ extern struct obstack maybepermanent_obstack;
+ print_obstack_statistics ("class_obstack", &class_obstack);
+ print_obstack_statistics ("permanent_obstack", &permanent_obstack);
+ print_obstack_statistics ("maybepermanent_obstack", &maybepermanent_obstack);
+ print_search_statistics ();
+ print_class_statistics ();
+}
+
+/* This is used by the `assert' macro. It is provided in libgcc.a,
+ which `cc' doesn't know how to link. Note that the C++ front-end
+ no longer actually uses the `assert' macro (instead, it calls
+ my_friendly_assert). But all of the back-end files still need this. */
+void
+__eprintf (string, expression, line, filename)
+#ifdef __STDC__
+ const char *string;
+ const char *expression;
+ unsigned line;
+ const char *filename;
+#else
+ char *string;
+ char *expression;
+ unsigned line;
+ char *filename;
+#endif
+{
+ fprintf (stderr, string, expression, line, filename);
+ fflush (stderr);
+ abort ();
+}
+
+/* Return, as an INTEGER_CST node, the number of elements for
+ TYPE (which is an ARRAY_TYPE). This counts only elements of the top array. */
+
+tree
+array_type_nelts_top (type)
+ tree type;
+{
+ return fold (build (PLUS_EXPR, integer_type_node,
+ array_type_nelts (type),
+ integer_one_node));
+}
+
+/* Return, as an INTEGER_CST node, the number of elements for
+ TYPE (which is an ARRAY_TYPE). This one is a recursive count of all
+ ARRAY_TYPEs that are clumped together. */
+
+tree
+array_type_nelts_total (type)
+ tree type;
+{
+ tree sz = array_type_nelts_top (type);
+ type = TREE_TYPE (type);
+ while (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ tree n = array_type_nelts_top (type);
+ sz = fold (build (MULT_EXPR, integer_type_node, sz, n));
+ type = TREE_TYPE (type);
+ }
+ return sz;
+}
diff --git a/gnu/usr.bin/cc/cc1plus/tree.def b/gnu/usr.bin/cc/cc1plus/tree.def
new file mode 100644
index 0000000..4f33c9f
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/tree.def
@@ -0,0 +1,103 @@
+/* This file contains the definitions and documentation for the
+ additional tree codes used in the GNU C++ compiler (see tree.def
+ for the standard codes).
+ Copyright (C) 1987, 1988, 1990, 1993 Free Software Foundation, Inc.
+ Hacked by Michael Tiemann (tiemann@cygnus.com)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* Reference to the contents of an offset
+ (a value whose type is an OFFSET_TYPE).
+ Operand 0 is the object within which the offset is taken.
+ Operand 1 is the offset. The language independent OFFSET_REF
+ just won't work for us. */
+DEFTREECODE (CP_OFFSET_REF, "cp_offset_ref", "r", 2)
+
+/* For DELETE_EXPR, operand 0 is the store to be destroyed.
+ Operand 1 is the value to pass to the destroying function
+ saying whether the store should be deallocated as well. */
+DEFTREECODE (DELETE_EXPR, "dl_expr", "e", 2)
+DEFTREECODE (VEC_DELETE_EXPR, "vec_dl_expr", "e", 2)
+
+/* Value is reference to particular overloaded class method.
+ Operand 0 is the class name (an IDENTIFIER_NODE);
+ operand 1 is the field (also an IDENTIFIER_NODE).
+ The COMPLEXITY field holds the class level (usually 0). */
+DEFTREECODE (SCOPE_REF, "scope_ref", "r", 2)
+
+/* When composing an object with a member, this is the result.
+ Operand 0 is the object. Operand 1 is the member (usually
+ a dereferenced pointer to member). */
+DEFTREECODE (MEMBER_REF, "member_ref", "r", 2)
+
+/* Type conversion operator in C++. TREE_TYPE is type that this
+ operator converts to. Operand is expression to be converted. */
+DEFTREECODE (TYPE_EXPR, "type_expr", "e", 1)
+
+/* For CPLUS_NEW_EXPR, operand 0 is function which performs initialization,
+ operand 1 is argument list to initialization function,
+ and operand 2 is the slot which was allocated for this expression. */
+DEFTREECODE (NEW_EXPR, "nw_expr", "e", 3)
+DEFTREECODE (VEC_NEW_EXPR, "vec_nw_expr", "e", 3)
+
+/* A throw expression. operand 0 is the expression, if there was one,
+ else it is NULL_TREE. */
+DEFTREECODE (THROW_EXPR, "throw_expr", "e", 1)
+
+/* Template definition. The following fields have the specified uses,
+ although there are other macros in cp-tree.h that should be used for
+ accessing this data.
+ DECL_ARGUMENTS template parm vector
+ DECL_TEMPLATE_INFO template text &c
+ DECL_VINDEX list of instantiations already produced;
+ only done for functions so far
+ For class template:
+ DECL_INITIAL associated templates (methods &c)
+ DECL_RESULT null
+ For non-class templates:
+ TREE_TYPE type of object to be constructed
+ DECL_RESULT decl for object to be created
+ (e.g., FUNCTION_DECL with tmpl parms used)
+ */
+DEFTREECODE (TEMPLATE_DECL, "template_decl", "d", 0)
+
+/* Index into a template parameter list. This parameter must be a type.
+ Use TYPE_FIELDS to find parmlist and index. */
+DEFTREECODE (TEMPLATE_TYPE_PARM, "template_type_parm", "t", 0)
+
+/* Index into a template parameter list. This parameter must not be a
+ type. */
+DEFTREECODE (TEMPLATE_CONST_PARM, "template_const_parm", "c", 2)
+
+/* For uninstantiated parameterized types.
+ TYPE_VALUES tree list:
+ TREE_PURPOSE template decl
+ TREE_VALUE parm vector
+ TREE_CHAIN null
+ Other useful fields to be defined later. */
+DEFTREECODE (UNINSTANTIATED_P_TYPE, "uninstantiated_p_type", "t", 0)
+
+/* A thunk is a stub function.
+
+ Thunks are used to implement multiple inheritance:
+ At run-time, such a thunk subtracts THUNK_DELTA (an int, not a tree)
+ from the this pointer, and then jumps to DECL_INITIAL
+ (which is an ADDR_EXPR whose operand is a FUNCTION_DECL).
+
+ Other kinds of thunks may be defined later. */
+DEFTREECODE (THUNK_DECL, "thunk_decl", "d", 0)
diff --git a/gnu/usr.bin/cc/cc1plus/typeck.c b/gnu/usr.bin/cc/cc1plus/typeck.c
new file mode 100644
index 0000000..fe8e7ba
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/typeck.c
@@ -0,0 +1,7233 @@
+/* Build expressions with type checking for C++ compiler.
+ Copyright (C) 1987, 88, 89, 92, 93, 1994 Free Software Foundation, Inc.
+ Hacked by Michael Tiemann (tiemann@cygnus.com)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file is part of the C++ front end.
+ It contains routines to build C++ expressions given their operands,
+ including computing the types of the result, C and C++ specific error
+ checks, and some optimization.
+
+ There are also routines to build RETURN_STMT nodes and CASE_STMT nodes,
+ and to process initializations in declarations (since they work
+ like a strange sort of assignment). */
+
+extern void error ();
+extern void warning ();
+
+#include "config.h"
+#include <stdio.h>
+#include "tree.h"
+#include "rtl.h"
+#include "cp-tree.h"
+#include "flags.h"
+
+int mark_addressable ();
+static tree convert_for_assignment ();
+/* static */ tree convert_for_initialization ();
+extern tree shorten_compare ();
+extern void binary_op_error ();
+static tree pointer_int_sum ();
+static tree pointer_diff ();
+static tree convert_sequence ();
+/* static */ tree unary_complex_lvalue ();
+
+extern rtx original_result_rtx;
+
+/* Return the target type of TYPE, which meas return T for:
+ T*, T&, T[], T (...), and otherwise, just T. */
+
+tree
+target_type (type)
+ tree type;
+{
+ if (TREE_CODE (type) == REFERENCE_TYPE)
+ type = TREE_TYPE (type);
+ while (TREE_CODE (type) == POINTER_TYPE
+ || TREE_CODE (type) == ARRAY_TYPE
+ || TREE_CODE (type) == FUNCTION_TYPE
+ || TREE_CODE (type) == METHOD_TYPE
+ || TREE_CODE (type) == OFFSET_TYPE)
+ type = TREE_TYPE (type);
+ return type;
+}
+
+/* Do `exp = require_complete_type (exp);' to make sure exp
+ does not have an incomplete type. (That includes void types.) */
+
+tree
+require_complete_type (value)
+ tree value;
+{
+ tree type = TREE_TYPE (value);
+
+ /* First, detect a valid value with a complete type. */
+ if (TYPE_SIZE (type) != 0
+ && type != void_type_node
+ && ! (TYPE_LANG_SPECIFIC (type)
+ && (IS_SIGNATURE_POINTER (type) || IS_SIGNATURE_REFERENCE (type))
+ && TYPE_SIZE (SIGNATURE_TYPE (type)) == 0))
+ return value;
+
+ /* If we see X::Y, we build an OFFSET_TYPE which has
+ not been laid out. Try to avoid an error by interpreting
+ it as this->X::Y, if reasonable. */
+ if (TREE_CODE (value) == OFFSET_REF
+ && C_C_D != 0
+ && TREE_OPERAND (value, 0) == C_C_D)
+ {
+ tree base, member = TREE_OPERAND (value, 1);
+ tree basetype = TYPE_OFFSET_BASETYPE (type);
+ my_friendly_assert (TREE_CODE (member) == FIELD_DECL, 305);
+ base = convert_pointer_to (basetype, current_class_decl);
+ value = build (COMPONENT_REF, TREE_TYPE (member),
+ build_indirect_ref (base, NULL_PTR), member);
+ return require_complete_type (value);
+ }
+
+ incomplete_type_error (value, type);
+ return error_mark_node;
+}
+
+/* Return truthvalue of whether type of EXP is instantiated. */
+int
+type_unknown_p (exp)
+ tree exp;
+{
+ return (TREE_CODE (exp) == TREE_LIST
+ || TREE_TYPE (exp) == unknown_type_node
+ || (TREE_CODE (TREE_TYPE (exp)) == OFFSET_TYPE
+ && TREE_TYPE (TREE_TYPE (exp)) == unknown_type_node));
+}
+
+/* Return truthvalue of whether T is function (or pfn) type. */
+int
+fntype_p (t)
+ tree t;
+{
+ return (TREE_CODE (t) == FUNCTION_TYPE || TREE_CODE (t) == METHOD_TYPE
+ || (TREE_CODE (t) == POINTER_TYPE
+ && (TREE_CODE (TREE_TYPE (t)) == FUNCTION_TYPE
+ || TREE_CODE (TREE_TYPE (t)) == METHOD_TYPE)));
+}
+
+/* Do `exp = require_instantiated_type (type, exp);' to make sure EXP
+ does not have an uninstantiated type.
+ TYPE is type to instantiate with, if uninstantiated. */
+tree
+require_instantiated_type (type, exp, errval)
+ tree type, exp, errval;
+{
+ if (TREE_TYPE (exp) == NULL_TREE)
+ {
+ error ("argument list may not have an initializer list");
+ return errval;
+ }
+
+ if (TREE_TYPE (exp) == unknown_type_node
+ || (TREE_CODE (TREE_TYPE (exp)) == OFFSET_TYPE
+ && TREE_TYPE (TREE_TYPE (exp)) == unknown_type_node))
+ {
+ exp = instantiate_type (type, exp, 1);
+ if (TREE_TYPE (exp) == error_mark_node)
+ return errval;
+ }
+ return exp;
+}
+
+/* Return a variant of TYPE which has all the type qualifiers of LIKE
+ as well as those of TYPE. */
+
+static tree
+qualify_type (type, like)
+ tree type, like;
+{
+ int constflag = TYPE_READONLY (type) || TYPE_READONLY (like);
+ int volflag = TYPE_VOLATILE (type) || TYPE_VOLATILE (like);
+ /* @@ Must do member pointers here. */
+ return c_build_type_variant (type, constflag, volflag);
+}
+
+/* Return the common type of two parameter lists.
+ We assume that comptypes has already been done and returned 1;
+ if that isn't so, this may crash.
+
+ As an optimization, free the space we allocate if the parameter
+ lists are already common. */
+
+tree
+commonparms (p1, p2)
+ tree p1, p2;
+{
+ tree oldargs = p1, newargs, n;
+ int i, len;
+ int any_change = 0;
+ char *first_obj = (char *) oballoc (0);
+
+ len = list_length (p1);
+ newargs = tree_last (p1);
+
+ if (newargs == void_list_node)
+ i = 1;
+ else
+ {
+ i = 0;
+ newargs = 0;
+ }
+
+ for (; i < len; i++)
+ newargs = tree_cons (NULL_TREE, NULL_TREE, newargs);
+
+ n = newargs;
+
+ for (i = 0; p1;
+ p1 = TREE_CHAIN (p1), p2 = TREE_CHAIN (p2), n = TREE_CHAIN (n), i++)
+ {
+ if (TREE_PURPOSE (p1) && !TREE_PURPOSE (p2))
+ {
+ /* We used to give a warning here that advised about a default
+ argument being given in the prototype but not in the function's
+ declaration. It's best not to bother. */
+ TREE_PURPOSE (n) = TREE_PURPOSE (p1);
+ any_change = 1;
+ }
+ else if (! TREE_PURPOSE (p1))
+ {
+ if (TREE_PURPOSE (p2))
+ {
+ TREE_PURPOSE (n) = TREE_PURPOSE (p2);
+ any_change = 1;
+ }
+ }
+ else
+ {
+ int cmp = simple_cst_equal (TREE_PURPOSE (p1), TREE_PURPOSE (p2));
+ if (cmp < 0)
+ my_friendly_abort (111);
+ if (cmp == 0)
+ any_change = 1;
+ TREE_PURPOSE (n) = TREE_PURPOSE (p2);
+ }
+ if (TREE_VALUE (p1) != TREE_VALUE (p2))
+ {
+ any_change = 1;
+ TREE_VALUE (n) = common_type (TREE_VALUE (p1), TREE_VALUE (p2));
+ }
+ else
+ TREE_VALUE (n) = TREE_VALUE (p1);
+ }
+ if (! any_change)
+ {
+ obfree (first_obj);
+ return oldargs;
+ }
+
+ return newargs;
+}
+
+/* Return the common type of two types.
+ We assume that comptypes has already been done and returned 1;
+ if that isn't so, this may crash.
+
+ This is the type for the result of most arithmetic operations
+ if the operands have the given two types.
+
+ We do not deal with enumeral types here because they have already been
+ converted to integer types. */
+
+tree
+common_type (t1, t2)
+ tree t1, t2;
+{
+ register enum tree_code code1;
+ register enum tree_code code2;
+ tree attributes;
+
+ /* Save time if the two types are the same. */
+
+ if (t1 == t2) return t1;
+
+ /* If one type is nonsense, use the other. */
+ if (t1 == error_mark_node)
+ return t2;
+ if (t2 == error_mark_node)
+ return t1;
+
+ /* Merge the attributes */
+
+ { register tree a1, a2;
+ a1 = TYPE_ATTRIBUTES (t1);
+ a2 = TYPE_ATTRIBUTES (t2);
+
+ /* Either one unset? Take the set one. */
+
+ if (!(attributes = a1))
+ attributes = a2;
+
+ /* One that completely contains the other? Take it. */
+
+ else if (a2 && !attribute_list_contained (a1, a2))
+ if (attribute_list_contained (a2, a1))
+ attributes = a2;
+ else
+ {
+ /* Pick the longest list, and hang on the other
+ list. */
+
+ if (list_length (a1) < list_length (a2))
+ attributes = a2, a2 = a1;
+
+ for (; a2; a2 = TREE_CHAIN (a2))
+ if (!value_member (attributes, a2))
+ {
+ a1 = copy_node (a2);
+ TREE_CHAIN (a1) = attributes;
+ attributes = a1;
+ }
+ }
+ }
+
+ /* Treat an enum type as the unsigned integer type of the same width. */
+
+ if (TREE_CODE (t1) == ENUMERAL_TYPE)
+ t1 = type_for_size (TYPE_PRECISION (t1), 1);
+ if (TREE_CODE (t2) == ENUMERAL_TYPE)
+ t2 = type_for_size (TYPE_PRECISION (t2), 1);
+
+ code1 = TREE_CODE (t1);
+ code2 = TREE_CODE (t2);
+
+ switch (code1)
+ {
+ case INTEGER_TYPE:
+ case REAL_TYPE:
+ /* If only one is real, use it as the result. */
+
+ if (code1 == REAL_TYPE && code2 != REAL_TYPE)
+ return build_type_attribute_variant (t1, attributes);
+
+ if (code2 == REAL_TYPE && code1 != REAL_TYPE)
+ return build_type_attribute_variant (t2, attributes);
+
+ /* Both real or both integers; use the one with greater precision. */
+
+ if (TYPE_PRECISION (t1) > TYPE_PRECISION (t2))
+ return build_type_attribute_variant (t1, attributes);
+ else if (TYPE_PRECISION (t2) > TYPE_PRECISION (t1))
+ return build_type_attribute_variant (t2, attributes);
+
+ /* Same precision. Prefer longs to ints even when same size. */
+
+ if (TYPE_MAIN_VARIANT (t1) == long_unsigned_type_node
+ || TYPE_MAIN_VARIANT (t2) == long_unsigned_type_node)
+ return build_type_attribute_variant (long_unsigned_type_node,
+ attributes);
+
+ if (TYPE_MAIN_VARIANT (t1) == long_integer_type_node
+ || TYPE_MAIN_VARIANT (t2) == long_integer_type_node)
+ {
+ /* But preserve unsignedness from the other type,
+ since long cannot hold all the values of an unsigned int. */
+ if (TREE_UNSIGNED (t1) || TREE_UNSIGNED (t2))
+ t1 = long_unsigned_type_node;
+ else
+ t1 = long_integer_type_node;
+ return build_type_attribute_variant (t1, attributes);
+ }
+
+ /* Otherwise prefer the unsigned one. */
+
+ if (TREE_UNSIGNED (t1))
+ return build_type_attribute_variant (t1, attributes);
+ else
+ return build_type_attribute_variant (t2, attributes);
+
+ case POINTER_TYPE:
+ case REFERENCE_TYPE:
+ /* For two pointers, do this recursively on the target type,
+ and combine the qualifiers of the two types' targets. */
+ /* This code was turned off; I don't know why.
+ But ANSI C++ specifies doing this with the qualifiers.
+ So I turned it on again. */
+ {
+ tree target = common_type (TYPE_MAIN_VARIANT (TREE_TYPE (t1)),
+ TYPE_MAIN_VARIANT (TREE_TYPE (t2)));
+ int constp
+ = TYPE_READONLY (TREE_TYPE (t1)) || TYPE_READONLY (TREE_TYPE (t2));
+ int volatilep
+ = TYPE_VOLATILE (TREE_TYPE (t1)) || TYPE_VOLATILE (TREE_TYPE (t2));
+ target = c_build_type_variant (target, constp, volatilep);
+ if (code1 == POINTER_TYPE)
+ t1 = build_pointer_type (target);
+ else
+ t1 = build_reference_type (target);
+ return build_type_attribute_variant (t1, attributes);
+ }
+#if 0
+ case POINTER_TYPE:
+ t1 = build_pointer_type (common_type (TREE_TYPE (t1), TREE_TYPE (t2)));
+ return build_type_attribute_variant (t1, attributes);
+
+ case REFERENCE_TYPE:
+ t1 = build_reference_type (common_type (TREE_TYPE (t1), TREE_TYPE (t2)));
+ return build_type_attribute_variant (t1, attributes);
+#endif
+
+ case ARRAY_TYPE:
+ {
+ tree elt = common_type (TREE_TYPE (t1), TREE_TYPE (t2));
+ /* Save space: see if the result is identical to one of the args. */
+ if (elt == TREE_TYPE (t1) && TYPE_DOMAIN (t1))
+ return build_type_attribute_variant (t1, attributes);
+ if (elt == TREE_TYPE (t2) && TYPE_DOMAIN (t2))
+ return build_type_attribute_variant (t2, attributes);
+ /* Merge the element types, and have a size if either arg has one. */
+ t1 = build_array_type (elt, TYPE_DOMAIN (TYPE_DOMAIN (t1) ? t1 : t2));
+ return build_type_attribute_variant (t1, attributes);
+ }
+
+ case FUNCTION_TYPE:
+ /* Function types: prefer the one that specified arg types.
+ If both do, merge the arg types. Also merge the return types. */
+ {
+ tree valtype = common_type (TREE_TYPE (t1), TREE_TYPE (t2));
+ tree p1 = TYPE_ARG_TYPES (t1);
+ tree p2 = TYPE_ARG_TYPES (t2);
+ tree rval, raises;
+
+ /* Save space: see if the result is identical to one of the args. */
+ if (valtype == TREE_TYPE (t1) && ! p2)
+ return build_type_attribute_variant (t1, attributes);
+ if (valtype == TREE_TYPE (t2) && ! p1)
+ return build_type_attribute_variant (t2, attributes);
+
+ /* Simple way if one arg fails to specify argument types. */
+ if (p1 == NULL_TREE || TREE_VALUE (p1) == void_type_node)
+ {
+ rval = build_function_type (valtype, p2);
+ if ((raises = TYPE_RAISES_EXCEPTIONS (t2)))
+ rval = build_exception_variant (NULL_TREE, rval, raises);
+ return build_type_attribute_variant (rval, attributes);
+ }
+ raises = TYPE_RAISES_EXCEPTIONS (t1);
+ if (p2 == NULL_TREE || TREE_VALUE (p2) == void_type_node)
+ {
+ rval = build_function_type (valtype, p1);
+ if (raises)
+ rval = build_exception_variant (NULL_TREE, rval, raises);
+ return build_type_attribute_variant (rval, attributes);
+ }
+
+ rval = build_function_type (valtype, commonparms (p1, p2));
+ rval = build_exception_variant (NULL_TREE, rval, raises);
+ return build_type_attribute_variant (rval, attributes);
+ }
+
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ my_friendly_assert (TYPE_MAIN_VARIANT (t1) == t1
+ && TYPE_MAIN_VARIANT (t2) == t2, 306);
+
+ if (! binfo_or_else (t1, t2))
+ compiler_error ("common_type called with uncommon aggregate types");
+ return build_type_attribute_variant (t1, attributes);
+
+ case METHOD_TYPE:
+ if (comptypes (TYPE_METHOD_BASETYPE (t1), TYPE_METHOD_BASETYPE (t2), 1)
+ && TREE_CODE (TREE_TYPE (t1)) == TREE_CODE (TREE_TYPE (t2)))
+ {
+ /* Get this value the long way, since TYPE_METHOD_BASETYPE
+ is just the main variant of this. */
+ tree basetype = TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (t1)));
+ tree raises, t3;
+
+ raises = TYPE_RAISES_EXCEPTIONS (t1);
+
+ /* If this was a member function type, get back to the
+ original type of type member function (i.e., without
+ the class instance variable up front. */
+ t1 = build_function_type (TREE_TYPE (t1), TREE_CHAIN (TYPE_ARG_TYPES (t1)));
+ t2 = build_function_type (TREE_TYPE (t2), TREE_CHAIN (TYPE_ARG_TYPES (t2)));
+ t3 = common_type (t1, t2);
+ t3 = build_cplus_method_type (basetype, TREE_TYPE (t3), TYPE_ARG_TYPES (t3));
+ t1 = build_exception_variant (basetype, t3, raises);
+ }
+ else
+ compiler_error ("common_type called with uncommon method types");
+
+ return build_type_attribute_variant (t1, attributes);
+
+ case OFFSET_TYPE:
+ if (TYPE_OFFSET_BASETYPE (t1) == TYPE_OFFSET_BASETYPE (t2)
+ && TREE_CODE (TREE_TYPE (t1)) == TREE_CODE (TREE_TYPE (t2)))
+ {
+ tree basetype = TYPE_OFFSET_BASETYPE (t1);
+ t1 = build_offset_type (basetype,
+ common_type (TREE_TYPE (t1), TREE_TYPE (t2)));
+ }
+ else
+ compiler_error ("common_type called with uncommon member types");
+
+ /* ... falls through ... */
+
+ default:
+ return build_type_attribute_variant (t1, attributes);
+ }
+}
+
+/* Return 1 if TYPE1 and TYPE2 raise the same exceptions. */
+int
+compexcepttypes (t1, t2, strict)
+ tree t1, t2;
+ int strict;
+{
+ return TYPE_RAISES_EXCEPTIONS (t1) == TYPE_RAISES_EXCEPTIONS (t2);
+}
+
+static int
+comp_array_types (cmp, t1, t2, strict)
+ register int (*cmp)();
+ tree t1, t2;
+ int strict;
+{
+ tree d1 = TYPE_DOMAIN (t1);
+ tree d2 = TYPE_DOMAIN (t2);
+
+ /* Target types must match incl. qualifiers. */
+ if (!(TREE_TYPE (t1) == TREE_TYPE (t2)
+ || (*cmp) (TREE_TYPE (t1), TREE_TYPE (t2), strict)))
+ return 0;
+
+ /* Sizes must match unless one is missing or variable. */
+ if (d1 == 0 || d2 == 0 || d1 == d2
+ || TREE_CODE (TYPE_MIN_VALUE (d1)) != INTEGER_CST
+ || TREE_CODE (TYPE_MIN_VALUE (d2)) != INTEGER_CST
+ || TREE_CODE (TYPE_MAX_VALUE (d1)) != INTEGER_CST
+ || TREE_CODE (TYPE_MAX_VALUE (d2)) != INTEGER_CST)
+ return 1;
+
+ return ((TREE_INT_CST_LOW (TYPE_MIN_VALUE (d1))
+ == TREE_INT_CST_LOW (TYPE_MIN_VALUE (d2)))
+ && (TREE_INT_CST_HIGH (TYPE_MIN_VALUE (d1))
+ == TREE_INT_CST_HIGH (TYPE_MIN_VALUE (d2)))
+ && (TREE_INT_CST_LOW (TYPE_MAX_VALUE (d1))
+ == TREE_INT_CST_LOW (TYPE_MAX_VALUE (d2)))
+ && (TREE_INT_CST_HIGH (TYPE_MAX_VALUE (d1))
+ == TREE_INT_CST_HIGH (TYPE_MAX_VALUE (d2))));
+}
+
+/* Return 1 if TYPE1 and TYPE2 are compatible types for assignment
+ or various other operations. This is what ANSI C++ speaks of as
+ "being the same".
+
+ For C++: argument STRICT says we should be strict about this
+ comparison:
+
+ 2 : strict, except that if one type is a reference and
+ the other is not, compare the target type of the
+ reference to the type that's not a reference (ARM, p308).
+ This is used for checking for illegal overloading.
+ 1 : strict (compared according to ANSI C)
+ This is used for checking whether two function decls match.
+ 0 : <= (compared according to C++)
+ -1: <= or >= (relaxed)
+
+ Otherwise, pointers involving base classes and derived classes
+ can be mixed as legal: i.e. a pointer to a base class may be assigned
+ to a pointer to one of its derived classes, as per C++. A pointer to
+ a derived class may be passed as a parameter to a function expecting a
+ pointer to a base classes. These allowances do not commute. In this
+ case, TYPE1 is assumed to be the base class, and TYPE2 is assumed to
+ be the derived class. */
+int
+comptypes (type1, type2, strict)
+ tree type1, type2;
+ int strict;
+{
+ register tree t1 = type1;
+ register tree t2 = type2;
+ int attrval, val;
+
+ /* Suppress errors caused by previously reported errors */
+
+ if (t1 == t2)
+ return 1;
+
+ /* This should never happen. */
+ my_friendly_assert (t1 != error_mark_node, 307);
+
+ if (t2 == error_mark_node)
+ return 0;
+
+ if (strict < 0)
+ {
+ /* Treat an enum type as the unsigned integer type of the same width. */
+
+ if (TREE_CODE (t1) == ENUMERAL_TYPE)
+ t1 = type_for_size (TYPE_PRECISION (t1), 1);
+ if (TREE_CODE (t2) == ENUMERAL_TYPE)
+ t2 = type_for_size (TYPE_PRECISION (t2), 1);
+
+ if (t1 == t2)
+ return 1;
+ }
+
+ /* Different classes of types can't be compatible. */
+
+ if (TREE_CODE (t1) != TREE_CODE (t2))
+ {
+ if (strict == 2
+ && ((TREE_CODE (t1) == REFERENCE_TYPE)
+ ^ (TREE_CODE (t2) == REFERENCE_TYPE)))
+ {
+ if (TREE_CODE (t1) == REFERENCE_TYPE)
+ return comptypes (TREE_TYPE (t1), t2, 1);
+ return comptypes (t1, TREE_TYPE (t2), 1);
+ }
+
+ return 0;
+ }
+ if (strict > 1)
+ strict = 1;
+
+ /* Qualifiers must match. */
+
+ if (TYPE_READONLY (t1) != TYPE_READONLY (t2))
+ return 0;
+ if (TYPE_VOLATILE (t1) != TYPE_VOLATILE (t2))
+ return 0;
+
+ /* Allow for two different type nodes which have essentially the same
+ definition. Note that we already checked for equality of the type
+ type qualifiers (just above). */
+
+ if (TYPE_MAIN_VARIANT (t1) == TYPE_MAIN_VARIANT (t2))
+ return 1;
+
+#ifdef COMP_TYPE_ATTRIBUTES
+ if (! (attrval = COMP_TYPE_ATTRIBUTES (t1, t2)))
+ return 0;
+#else
+ /* 1 if no need for warning yet, 2 if warning cause has been seen. */
+ attrval = 1;
+#endif
+
+ /* 1 if no need for warning yet, 2 if warning cause has been seen. */
+ val = 0;
+
+ switch (TREE_CODE (t1))
+ {
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ if (strict <= 0)
+ goto look_hard;
+ return 0;
+
+ case OFFSET_TYPE:
+ val = (comptypes (TYPE_POINTER_TO (TYPE_OFFSET_BASETYPE (t1)),
+ TYPE_POINTER_TO (TYPE_OFFSET_BASETYPE (t2)), strict)
+ && comptypes (TREE_TYPE (t1), TREE_TYPE (t2), strict));
+ break;
+
+ case METHOD_TYPE:
+ if (! compexcepttypes (t1, t2, strict))
+ return 0;
+
+ /* This case is anti-symmetrical!
+ One can pass a base member (or member function)
+ to something expecting a derived member (or member function),
+ but not vice-versa! */
+
+ val = (comptypes (TYPE_POINTER_TO (TYPE_METHOD_BASETYPE (t2)),
+ TYPE_POINTER_TO (TYPE_METHOD_BASETYPE (t1)), strict)
+ && comptypes (TREE_TYPE (t1), TREE_TYPE (t2), strict)
+ && compparms (TREE_CHAIN (TYPE_ARG_TYPES (t1)),
+ TREE_CHAIN (TYPE_ARG_TYPES (t2)), strict));
+ break;
+
+ case POINTER_TYPE:
+ case REFERENCE_TYPE:
+ t1 = TREE_TYPE (t1);
+ t2 = TREE_TYPE (t2);
+ if (t1 == t2)
+ {
+ val = 1;
+ break;
+ }
+ if (strict <= 0)
+ {
+ if (TREE_CODE (t1) == RECORD_TYPE && TREE_CODE (t2) == RECORD_TYPE)
+ {
+ int rval;
+ look_hard:
+ rval = t1 == t2 || UNIQUELY_DERIVED_FROM_P (t1, t2);
+
+ if (rval)
+ {
+ val = 1;
+ break;
+ }
+ if (strict < 0)
+ {
+ val = UNIQUELY_DERIVED_FROM_P (t2, t1);
+ break;
+ }
+ }
+ return 0;
+ }
+ else
+ val = comptypes (t1, t2, strict);
+ break;
+
+ case FUNCTION_TYPE:
+ if (! compexcepttypes (t1, t2, strict))
+ return 0;
+
+ val = ((TREE_TYPE (t1) == TREE_TYPE (t2)
+ || comptypes (TREE_TYPE (t1), TREE_TYPE (t2), strict))
+ && compparms (TYPE_ARG_TYPES (t1), TYPE_ARG_TYPES (t2), strict));
+ break;
+
+ case ARRAY_TYPE:
+ /* Target types must match incl. qualifiers. */
+ val = comp_array_types (comptypes, t1, t2, strict);
+ break;
+
+ case TEMPLATE_TYPE_PARM:
+ return 1;
+
+ case UNINSTANTIATED_P_TYPE:
+ return UPT_TEMPLATE (t1) == UPT_TEMPLATE (t2);
+ }
+ return attrval == 2 && val == 1 ? 2 : val;
+}
+
+/* Return 1 if TTL and TTR are pointers to types that are equivalent,
+ ignoring their qualifiers.
+
+ NPTRS is the number of pointers we can strip off and keep cool.
+ This is used to permit (for aggr A, aggr B) A, B* to convert to A*,
+ but to not permit B** to convert to A**. */
+
+int
+comp_target_types (ttl, ttr, nptrs)
+ tree ttl, ttr;
+ int nptrs;
+{
+ ttl = TYPE_MAIN_VARIANT (ttl);
+ ttr = TYPE_MAIN_VARIANT (ttr);
+ if (ttl == ttr)
+ return 1;
+ if (TREE_CODE (ttr) == TEMPLATE_TYPE_PARM)
+ return 1;
+
+ if (TREE_CODE (ttr) != TREE_CODE (ttl))
+ return 0;
+
+ if (TREE_CODE (ttr) == POINTER_TYPE)
+ {
+ if (TREE_CODE (TREE_TYPE (ttl)) == POINTER_TYPE
+ || TREE_CODE (TREE_TYPE (ttl)) == ARRAY_TYPE)
+ return comp_ptr_ttypes (TREE_TYPE (ttl), TREE_TYPE (ttr));
+ else
+ return comp_target_types (TREE_TYPE (ttl), TREE_TYPE (ttr), nptrs - 1);
+ }
+
+ if (TREE_CODE (ttr) == REFERENCE_TYPE)
+ return comp_target_types (TREE_TYPE (ttl), TREE_TYPE (ttr), nptrs);
+ if (TREE_CODE (ttr) == ARRAY_TYPE)
+ return comp_array_types (comp_target_types, ttl, ttr, 0);
+ else if (TREE_CODE (ttr) == FUNCTION_TYPE || TREE_CODE (ttr) == METHOD_TYPE)
+ if (comp_target_types (TREE_TYPE (ttl), TREE_TYPE (ttr), nptrs))
+ switch (comp_target_parms (TYPE_ARG_TYPES (ttl), TYPE_ARG_TYPES (ttr), 1))
+ {
+ case 0:
+ return 0;
+ case 1:
+ return 1;
+ case 2:
+ cp_pedwarn ("converting `%T' to `%T' is a contravariance violation",
+ ttr, ttl);
+ return 1;
+ default:
+ my_friendly_abort (112);
+ }
+ else
+ return 0;
+
+ /* for C++ */
+ else if (TREE_CODE (ttr) == OFFSET_TYPE)
+ {
+ /* Contravariance: we can assign a pointer to base member to a pointer
+ to derived member. Note difference from simple pointer case, where
+ we can pass a pointer to derived to a pointer to base. */
+ if (comptypes (TYPE_OFFSET_BASETYPE (ttr), TYPE_OFFSET_BASETYPE (ttl), 0))
+ return comp_target_types (TREE_TYPE (ttl), TREE_TYPE (ttr), nptrs);
+ else if (comptypes (TYPE_OFFSET_BASETYPE (ttl), TYPE_OFFSET_BASETYPE (ttr), 0)
+ && comp_target_types (TREE_TYPE (ttl), TREE_TYPE (ttr), nptrs))
+ {
+ cp_pedwarn ("converting `%T' to `%T' is a contravariance violation",
+ ttr, ttl);
+ return 1;
+ }
+ }
+ else if (IS_AGGR_TYPE (ttl))
+ {
+ if (nptrs < 0)
+ return 0;
+ return comptypes (TYPE_POINTER_TO (ttl), TYPE_POINTER_TO (ttr), 0);
+ }
+
+ return 0;
+}
+
+/* If two types share a common base type, return that basetype.
+ If there is not a unique most-derived base type, this function
+ returns ERROR_MARK_NODE. */
+tree
+common_base_type (tt1, tt2)
+ tree tt1, tt2;
+{
+ tree best = NULL_TREE, tmp;
+ int i;
+
+ /* If one is a baseclass of another, that's good enough. */
+ if (UNIQUELY_DERIVED_FROM_P (tt1, tt2))
+ return tt1;
+ if (UNIQUELY_DERIVED_FROM_P (tt2, tt1))
+ return tt2;
+
+ /* If they share a virtual baseclass, that's good enough. */
+ for (tmp = CLASSTYPE_VBASECLASSES (tt1); tmp; tmp = TREE_CHAIN (tmp))
+ {
+ if (binfo_member (BINFO_TYPE (tmp), CLASSTYPE_VBASECLASSES (tt2)))
+ return BINFO_TYPE (tmp);
+ }
+
+ /* Otherwise, try to find a unique baseclass of TT1
+ that is shared by TT2, and follow that down. */
+ for (i = CLASSTYPE_N_BASECLASSES (tt1)-1; i >= 0; i--)
+ {
+ tree basetype = TYPE_BINFO_BASETYPE (tt1, i);
+ tree trial = common_base_type (basetype, tt2);
+ if (trial)
+ {
+ if (trial == error_mark_node)
+ return trial;
+ if (best == NULL_TREE)
+ best = trial;
+ else if (best != trial)
+ return error_mark_node;
+ }
+ }
+
+ /* Same for TT2. */
+ for (i = CLASSTYPE_N_BASECLASSES (tt2)-1; i >= 0; i--)
+ {
+ tree basetype = TYPE_BINFO_BASETYPE (tt2, i);
+ tree trial = common_base_type (tt1, basetype);
+ if (trial)
+ {
+ if (trial == error_mark_node)
+ return trial;
+ if (best == NULL_TREE)
+ best = trial;
+ else if (best != trial)
+ return error_mark_node;
+ }
+ }
+ return best;
+}
+
+/* Subroutines of `comptypes'. */
+
+/* Return 1 if two parameter type lists PARMS1 and PARMS2
+ are equivalent in the sense that functions with those parameter types
+ can have equivalent types.
+ If either list is empty, we win.
+ Otherwise, the two lists must be equivalent, element by element.
+
+ C++: See comment above about TYPE1, TYPE2, STRICT.
+ If STRICT == 3, it means checking is strict, but do not compare
+ default parameter values. */
+int
+compparms (parms1, parms2, strict)
+ tree parms1, parms2;
+ int strict;
+{
+ register tree t1 = parms1, t2 = parms2;
+
+ /* An unspecified parmlist matches any specified parmlist
+ whose argument types don't need default promotions. */
+
+ if (strict <= 0 && t1 == 0)
+ return self_promoting_args_p (t2);
+ if (strict < 0 && t2 == 0)
+ return self_promoting_args_p (t1);
+
+ while (1)
+ {
+ if (t1 == 0 && t2 == 0)
+ return 1;
+ /* If one parmlist is shorter than the other,
+ they fail to match, unless STRICT is <= 0. */
+ if (t1 == 0 || t2 == 0)
+ {
+ if (strict > 0)
+ return 0;
+ if (strict < 0)
+ return 1;
+ if (strict == 0)
+ return t1 && TREE_PURPOSE (t1);
+ }
+ if (! comptypes (TREE_VALUE (t2), TREE_VALUE (t1), strict))
+ {
+ if (strict > 0)
+ return 0;
+ if (strict == 0)
+ return t2 == void_list_node && TREE_PURPOSE (t1);
+ return TREE_PURPOSE (t1) || TREE_PURPOSE (t2);
+ }
+ if (strict != 3 && TREE_PURPOSE (t1) && TREE_PURPOSE (t2))
+ {
+ int cmp = simple_cst_equal (TREE_PURPOSE (t1), TREE_PURPOSE (t2));
+ if (cmp < 0)
+ my_friendly_abort (113);
+ if (cmp == 0)
+ return 0;
+ }
+
+ t1 = TREE_CHAIN (t1);
+ t2 = TREE_CHAIN (t2);
+ }
+}
+
+/* This really wants return whether or not parameter type lists
+ would make their owning functions assignment compatible or not. */
+int
+comp_target_parms (parms1, parms2, strict)
+ tree parms1, parms2;
+ int strict;
+{
+ register tree t1 = parms1, t2 = parms2;
+ int warn_contravariance = 0;
+
+ /* An unspecified parmlist matches any specified parmlist
+ whose argument types don't need default promotions.
+ @@@ see 13.3.3 for a counterexample... */
+
+ if (t1 == 0 && t2 != 0)
+ {
+ cp_pedwarn ("ANSI C++ prohibits conversion from `(%#T)' to `(...)'",
+ parms2);
+ return self_promoting_args_p (t2);
+ }
+ if (t2 == 0)
+ return self_promoting_args_p (t1);
+
+ for (; t1 || t2; t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2))
+ {
+ tree p1, p2;
+
+ /* If one parmlist is shorter than the other,
+ they fail to match, unless STRICT is <= 0. */
+ if (t1 == 0 || t2 == 0)
+ {
+ if (strict > 0)
+ return 0;
+ if (strict < 0)
+ return 1 + warn_contravariance;
+ return ((t1 && TREE_PURPOSE (t1)) + warn_contravariance);
+ }
+ p1 = TREE_VALUE (t1);
+ p2 = TREE_VALUE (t2);
+ if (p1 == p2)
+ continue;
+ if (TREE_CODE (p2) == TEMPLATE_TYPE_PARM)
+ continue;
+
+ if ((TREE_CODE (p1) == POINTER_TYPE && TREE_CODE (p2) == POINTER_TYPE)
+ || (TREE_CODE (p1) == REFERENCE_TYPE && TREE_CODE (p2) == REFERENCE_TYPE))
+ {
+ if (strict <= 0
+ && (TYPE_MAIN_VARIANT (TREE_TYPE (p1))
+ == TYPE_MAIN_VARIANT (TREE_TYPE (p2))))
+ continue;
+
+ if (TREE_CODE (TREE_TYPE (p2)) == TEMPLATE_TYPE_PARM)
+ continue;
+
+ /* The following is wrong for contravariance,
+ but many programs depend on it. */
+ if (TREE_TYPE (p1) == void_type_node)
+ continue;
+ if (TREE_TYPE (p2) == void_type_node)
+ {
+ warn_contravariance = 1;
+ continue;
+ }
+ if (IS_AGGR_TYPE (TREE_TYPE (p1)))
+ {
+ if (comptypes (p2, p1, 0) == 0)
+ {
+ if (comptypes (p1, p2, 0) != 0)
+ warn_contravariance = 1;
+ else
+ return 0;
+ }
+ continue;
+ }
+ }
+ /* Note backwards order due to contravariance. */
+ if (comp_target_types (p2, p1, 1) == 0)
+ {
+ if (comp_target_types (p1, p2, 1))
+ {
+ warn_contravariance = 1;
+ continue;
+ }
+ if (strict != 0)
+ return 0;
+#if 0
+ /* What good do these cases do? */
+ if (strict == 0)
+ return p2 == void_type_node && TREE_PURPOSE (t1);
+ return TREE_PURPOSE (t1) || TREE_PURPOSE (t2);
+#endif
+ }
+ /* Target types are compatible--just make sure that if
+ we use parameter lists, that they are ok as well. */
+ if (TREE_CODE (p1) == FUNCTION_TYPE || TREE_CODE (p1) == METHOD_TYPE)
+ switch (comp_target_parms (TYPE_ARG_TYPES (p1),
+ TYPE_ARG_TYPES (p2),
+ strict))
+ {
+ case 0:
+ return 0;
+ case 1:
+ break;
+ case 2:
+ warn_contravariance = 1;
+ }
+
+ if (TREE_PURPOSE (t1) && TREE_PURPOSE (t2))
+ {
+ int cmp = simple_cst_equal (TREE_PURPOSE (t1), TREE_PURPOSE (t2));
+ if (cmp < 0)
+ my_friendly_abort (114);
+ if (cmp == 0)
+ return 0;
+ }
+ }
+ return 1 + warn_contravariance;
+}
+
+/* Return 1 if PARMS specifies a fixed number of parameters
+ and none of their types is affected by default promotions. */
+
+int
+self_promoting_args_p (parms)
+ tree parms;
+{
+ register tree t;
+ for (t = parms; t; t = TREE_CHAIN (t))
+ {
+ register tree type = TREE_VALUE (t);
+
+ if (TREE_CHAIN (t) == 0 && type != void_type_node)
+ return 0;
+
+ if (TYPE_MAIN_VARIANT (type) == float_type_node)
+ return 0;
+
+ if (type == 0)
+ return 0;
+
+ if (C_PROMOTING_INTEGER_TYPE_P (type))
+ return 0;
+ }
+ return 1;
+}
+
+/* Return an unsigned type the same as TYPE in other respects.
+
+ C++: must make these work for type variants as well. */
+
+tree
+unsigned_type (type)
+ tree type;
+{
+ tree type1 = TYPE_MAIN_VARIANT (type);
+ if (type1 == signed_char_type_node || type1 == char_type_node)
+ return unsigned_char_type_node;
+ if (type1 == integer_type_node)
+ return unsigned_type_node;
+ if (type1 == short_integer_type_node)
+ return short_unsigned_type_node;
+ if (type1 == long_integer_type_node)
+ return long_unsigned_type_node;
+ if (type1 == long_long_integer_type_node)
+ return long_long_unsigned_type_node;
+ return type;
+}
+
+/* Return a signed type the same as TYPE in other respects. */
+
+tree
+signed_type (type)
+ tree type;
+{
+ tree type1 = TYPE_MAIN_VARIANT (type);
+ if (type1 == unsigned_char_type_node || type1 == char_type_node)
+ return signed_char_type_node;
+ if (type1 == unsigned_type_node)
+ return integer_type_node;
+ if (type1 == short_unsigned_type_node)
+ return short_integer_type_node;
+ if (type1 == long_unsigned_type_node)
+ return long_integer_type_node;
+ if (type1 == long_long_unsigned_type_node)
+ return long_long_integer_type_node;
+ return type;
+}
+
+/* Return a type the same as TYPE except unsigned or
+ signed according to UNSIGNEDP. */
+
+tree
+signed_or_unsigned_type (unsignedp, type)
+ int unsignedp;
+ tree type;
+{
+ if (! INTEGRAL_TYPE_P (type))
+ return type;
+ if (TYPE_PRECISION (type) == TYPE_PRECISION (signed_char_type_node))
+ return unsignedp ? unsigned_char_type_node : signed_char_type_node;
+ if (TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node))
+ return unsignedp ? unsigned_type_node : integer_type_node;
+ if (TYPE_PRECISION (type) == TYPE_PRECISION (short_integer_type_node))
+ return unsignedp ? short_unsigned_type_node : short_integer_type_node;
+ if (TYPE_PRECISION (type) == TYPE_PRECISION (long_integer_type_node))
+ return unsignedp ? long_unsigned_type_node : long_integer_type_node;
+ if (TYPE_PRECISION (type) == TYPE_PRECISION (long_long_integer_type_node))
+ return (unsignedp ? long_long_unsigned_type_node
+ : long_long_integer_type_node);
+ return type;
+}
+
+tree
+c_sizeof (type)
+ tree type;
+{
+ enum tree_code code = TREE_CODE (type);
+ tree t;
+
+ if (code == FUNCTION_TYPE)
+ {
+ if (pedantic || warn_pointer_arith)
+ pedwarn ("ANSI C++ forbids taking the sizeof a function type");
+ return size_int (1);
+ }
+ if (code == METHOD_TYPE)
+ {
+ if (pedantic || warn_pointer_arith)
+ pedwarn ("ANSI C++ forbids taking the sizeof a method type");
+ return size_int (1);
+ }
+ if (code == VOID_TYPE)
+ {
+ if (pedantic || warn_pointer_arith)
+ pedwarn ("ANSI C++ forbids taking the sizeof a void type");
+ return size_int (1);
+ }
+ if (code == ERROR_MARK)
+ return size_int (1);
+
+ /* ARM $5.3.2: ``When applied to a reference, the result is the size of the
+ referenced object.'' */
+ if (code == REFERENCE_TYPE)
+ type = TREE_TYPE (type);
+
+ /* We couldn't find anything in the ARM or the draft standard that says,
+ one way or the other, if doing sizeof on something that doesn't have
+ an object associated with it is correct or incorrect. For example, if
+ you declare `struct S { char str[16]; };', and in your program do
+ a `sizeof (S::str)', should we flag that as an error or should we give
+ the size of it? Since it seems like a reasonable thing to do, we'll go
+ with giving the value. */
+ if (code == OFFSET_TYPE)
+ type = TREE_TYPE (type);
+
+ /* @@ This also produces an error for a signature ref.
+ In that case we should be able to do better. */
+ if (IS_SIGNATURE (type))
+ {
+ error ("`sizeof' applied to a signature type");
+ return size_int (0);
+ }
+
+ if (TYPE_SIZE (type) == 0)
+ {
+ error ("`sizeof' applied to an incomplete type");
+ return size_int (0);
+ }
+
+ /* Convert in case a char is more than one unit. */
+ t = size_binop (CEIL_DIV_EXPR, TYPE_SIZE (type),
+ size_int (TYPE_PRECISION (char_type_node)));
+ /* size_binop does not put the constant in range, so do it now. */
+ if (TREE_CODE (t) == INTEGER_CST && force_fit_type (t, 0))
+ TREE_CONSTANT_OVERFLOW (t) = TREE_OVERFLOW (t) = 1;
+ return t;
+}
+
+tree
+c_sizeof_nowarn (type)
+ tree type;
+{
+ enum tree_code code = TREE_CODE (type);
+ tree t;
+
+ if (code == FUNCTION_TYPE
+ || code == METHOD_TYPE
+ || code == VOID_TYPE
+ || code == ERROR_MARK)
+ return size_int (1);
+ if (code == REFERENCE_TYPE)
+ type = TREE_TYPE (type);
+
+ if (TYPE_SIZE (type) == 0)
+ {
+#if 0
+ /* ??? Tiemann, why have any diagnostic here?
+ There is none in the corresponding function for C. */
+ warning ("sizeof applied to an incomplete type");
+#endif
+ return size_int (0);
+ }
+
+ /* Convert in case a char is more than one unit. */
+ t = size_binop (CEIL_DIV_EXPR, TYPE_SIZE (type),
+ size_int (TYPE_PRECISION (char_type_node)));
+ force_fit_type (t, 0);
+ return t;
+}
+
+/* Implement the __alignof keyword: Return the minimum required
+ alignment of TYPE, measured in bytes. */
+
+tree
+c_alignof (type)
+ tree type;
+{
+ enum tree_code code = TREE_CODE (type);
+ tree t;
+
+ if (code == FUNCTION_TYPE || code == METHOD_TYPE)
+ return size_int (FUNCTION_BOUNDARY / BITS_PER_UNIT);
+
+ if (code == VOID_TYPE || code == ERROR_MARK)
+ return size_int (1);
+
+ /* C++: this is really correct! */
+ if (code == REFERENCE_TYPE)
+ type = TREE_TYPE (type);
+
+ /* @@ This also produces an error for a signature ref.
+ In that case we should be able to do better. */
+ if (IS_SIGNATURE (type))
+ {
+ error ("`__alignof' applied to a signature type");
+ return size_int (1);
+ }
+
+ t = size_int (TYPE_ALIGN (type) / BITS_PER_UNIT);
+ force_fit_type (t, 0);
+ return t;
+}
+
+/* Perform default promotions for C data used in expressions.
+ Arrays and functions are converted to pointers;
+ enumeral types or short or char, to int.
+ In addition, manifest constants symbols are replaced by their values.
+
+ C++: this will automatically bash references to their target type. */
+
+tree
+default_conversion (exp)
+ tree exp;
+{
+ register tree type = TREE_TYPE (exp);
+ register enum tree_code code = TREE_CODE (type);
+
+ if (code == OFFSET_TYPE /* || TREE_CODE (exp) == OFFSET_REF */ )
+ {
+ if (TREE_CODE (exp) == OFFSET_REF)
+ return default_conversion (resolve_offset_ref (exp));
+
+ type = TREE_TYPE (type);
+ code = TREE_CODE (type);
+ }
+
+ if (code == REFERENCE_TYPE)
+ {
+ exp = convert_from_reference (exp);
+ type = TREE_TYPE (exp);
+ code = TREE_CODE (type);
+ }
+
+ /* Constants can be used directly unless they're not loadable. */
+ if (TREE_CODE (exp) == CONST_DECL)
+ exp = DECL_INITIAL (exp);
+ /* Replace a nonvolatile const static variable with its value. */
+ else if (TREE_READONLY_DECL_P (exp) && DECL_MODE (exp) != BLKmode)
+ {
+ exp = decl_constant_value (exp);
+ type = TREE_TYPE (exp);
+ }
+
+ /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.
+ Leave such NOP_EXPRs, since RHS is being used in non-lvalue context. */
+
+ if (INTEGRAL_CODE_P (code))
+ {
+ tree t = type_promotes_to (type);
+ if (t != TYPE_MAIN_VARIANT (type))
+ return convert (t, exp);
+ }
+ if (flag_traditional
+ && TYPE_MAIN_VARIANT (type) == float_type_node)
+ return convert (double_type_node, exp);
+ if (code == VOID_TYPE)
+ {
+ error ("void value not ignored as it ought to be");
+ return error_mark_node;
+ }
+ if (code == FUNCTION_TYPE)
+ {
+ return build_unary_op (ADDR_EXPR, exp, 0);
+ }
+ if (code == METHOD_TYPE)
+ {
+ if (TREE_CODE (exp) == OFFSET_REF)
+ {
+ my_friendly_assert (TREE_CODE (TREE_OPERAND (exp, 1)) == FUNCTION_DECL,
+ 308);
+ return build_unary_op (ADDR_EXPR, TREE_OPERAND (exp, 1), 0);
+ }
+ return build_unary_op (ADDR_EXPR, exp, 0);
+ }
+ if (code == ARRAY_TYPE)
+ {
+ register tree adr;
+ tree restype;
+ tree ptrtype;
+ int constp, volatilep;
+
+ if (TREE_CODE (exp) == INDIRECT_REF)
+ {
+ /* Stripping away the INDIRECT_REF is not the right
+ thing to do for references... */
+ tree inner = TREE_OPERAND (exp, 0);
+ if (TREE_CODE (TREE_TYPE (inner)) == REFERENCE_TYPE)
+ {
+ inner = build1 (CONVERT_EXPR,
+ build_pointer_type (TREE_TYPE (TREE_TYPE (inner))),
+ inner);
+ TREE_REFERENCE_EXPR (inner) = 1;
+ }
+ return convert (TYPE_POINTER_TO (TREE_TYPE (type)), inner);
+ }
+
+ if (TREE_CODE (exp) == COMPOUND_EXPR)
+ {
+ tree op1 = default_conversion (TREE_OPERAND (exp, 1));
+ return build (COMPOUND_EXPR, TREE_TYPE (op1),
+ TREE_OPERAND (exp, 0), op1);
+ }
+
+ if (!lvalue_p (exp)
+ && ! (TREE_CODE (exp) == CONSTRUCTOR && TREE_STATIC (exp)))
+ {
+ error ("invalid use of non-lvalue array");
+ return error_mark_node;
+ }
+
+ constp = volatilep = 0;
+ if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'r'
+ || TREE_CODE_CLASS (TREE_CODE (exp)) == 'd')
+ {
+ constp = TREE_READONLY (exp);
+ volatilep = TREE_THIS_VOLATILE (exp);
+ }
+
+ restype = TREE_TYPE (type);
+ if (TYPE_READONLY (type) || TYPE_VOLATILE (type)
+ || constp || volatilep)
+ restype = c_build_type_variant (restype,
+ TYPE_READONLY (type) || constp,
+ TYPE_VOLATILE (type) || volatilep);
+ ptrtype = build_pointer_type (restype);
+
+ if (TREE_CODE (exp) == VAR_DECL)
+ {
+ /* ??? This is not really quite correct
+ in that the type of the operand of ADDR_EXPR
+ is not the target type of the type of the ADDR_EXPR itself.
+ Question is, can this lossage be avoided? */
+ adr = build1 (ADDR_EXPR, ptrtype, exp);
+ if (mark_addressable (exp) == 0)
+ return error_mark_node;
+ TREE_CONSTANT (adr) = staticp (exp);
+ TREE_SIDE_EFFECTS (adr) = 0; /* Default would be, same as EXP. */
+ return adr;
+ }
+ /* This way is better for a COMPONENT_REF since it can
+ simplify the offset for a component. */
+ adr = build_unary_op (ADDR_EXPR, exp, 1);
+ return convert (ptrtype, adr);
+ }
+ return exp;
+}
+
+tree
+build_object_ref (datum, basetype, field)
+ tree datum, basetype, field;
+{
+ if (datum == error_mark_node)
+ return error_mark_node;
+ else if (IS_SIGNATURE (IDENTIFIER_TYPE_VALUE (basetype)))
+ {
+ warning ("signature name in scope resolution ignored");
+ return build_component_ref (datum, field, NULL_TREE, 1);
+ }
+ else if (is_aggr_typedef (basetype, 1))
+ {
+ tree real_basetype = IDENTIFIER_TYPE_VALUE (basetype);
+ tree binfo = binfo_or_else (real_basetype, TREE_TYPE (datum));
+ if (binfo)
+ return build_component_ref (build_scoped_ref (datum, basetype),
+ field, binfo, 1);
+ }
+ return error_mark_node;
+}
+
+/* Like `build_component_ref, but uses an already found field.
+ Must compute access for C_C_D. Otherwise, ok. */
+tree
+build_component_ref_1 (datum, field, protect)
+ tree datum, field;
+ int protect;
+{
+ register tree basetype = TREE_TYPE (datum);
+ register enum tree_code code = TREE_CODE (basetype);
+ register tree ref;
+
+ if (code == REFERENCE_TYPE)
+ {
+ datum = convert_from_reference (datum);
+ basetype = TREE_TYPE (datum);
+ code = TREE_CODE (basetype);
+ }
+
+ if (! IS_AGGR_TYPE_CODE (code))
+ {
+ if (code != ERROR_MARK)
+ cp_error ("request for member `%D' in `%E', which is of non-aggregate type `%T'",
+ field, datum, basetype);
+ return error_mark_node;
+ }
+
+ if (TYPE_SIZE (basetype) == 0)
+ {
+ incomplete_type_error (0, basetype);
+ return error_mark_node;
+ }
+
+ /* Look up component name in the structure type definition. */
+
+ if (field == error_mark_node)
+ my_friendly_abort (115);
+
+ if (TREE_STATIC (field))
+ return field;
+
+ if (datum == C_C_D)
+ {
+ enum access_type access
+ = compute_access (TYPE_BINFO (current_class_type), field);
+
+ if (access == access_private)
+ {
+ cp_error ("field `%D' is private", field);
+ return error_mark_node;
+ }
+ else if (access == access_protected)
+ {
+ cp_error ("field `%D' is protected", field);
+ return error_mark_node;
+ }
+ }
+
+ ref = build (COMPONENT_REF, TREE_TYPE (field), datum, field);
+
+ if (TREE_READONLY (datum) || TREE_READONLY (field))
+ TREE_READONLY (ref) = 1;
+ if (TREE_THIS_VOLATILE (datum) || TREE_THIS_VOLATILE (field))
+ TREE_THIS_VOLATILE (ref) = 1;
+ if (DECL_MUTABLE_P (field))
+ TREE_READONLY (ref) = 0;
+
+ return ref;
+}
+
+/* Given a COND_EXPR in T, return it in a form that we can, for
+ example, use as an lvalue. This code used to be in unary_complex_lvalue,
+ but we needed it to deal with `a = (d == c) ? b : c' expressions, where
+ we're dealing with aggregates. So, we now call this in unary_complex_lvalue,
+ and in build_modify_expr. The case (in particular) that led to this was
+ with CODE == ADDR_EXPR, since it's not an lvalue when we'd get it there. */
+static tree
+rationalize_conditional_expr (code, t)
+ enum tree_code code;
+ tree t;
+{
+ return
+ build_conditional_expr (TREE_OPERAND (t, 0),
+ build_unary_op (code, TREE_OPERAND (t, 1), 0),
+ build_unary_op (code, TREE_OPERAND (t, 2), 0));
+}
+
+tree
+build_component_ref (datum, component, basetype_path, protect)
+ tree datum, component, basetype_path;
+ int protect;
+{
+ register tree basetype = TREE_TYPE (datum);
+ register enum tree_code code = TREE_CODE (basetype);
+ register tree field = NULL;
+ register tree ref;
+
+ /* If DATUM is a COMPOUND_EXPR or COND_EXPR, move our reference inside it
+ unless we are not to support things not strictly ANSI. */
+ switch (TREE_CODE (datum))
+ {
+ case COMPOUND_EXPR:
+ {
+ tree value = build_component_ref (TREE_OPERAND (datum, 1), component,
+ basetype_path, protect);
+ return build (COMPOUND_EXPR, TREE_TYPE (value),
+ TREE_OPERAND (datum, 0), value);
+ }
+ case COND_EXPR:
+ return build_conditional_expr
+ (TREE_OPERAND (datum, 0),
+ build_component_ref (TREE_OPERAND (datum, 1), component,
+ basetype_path, protect),
+ build_component_ref (TREE_OPERAND (datum, 2), component,
+ basetype_path, protect));
+ }
+
+ if (code == REFERENCE_TYPE)
+ {
+#if 0
+ /* TREE_REFERENCE_EXPRs are not converted by `convert_from_reference'.
+ @@ Maybe that is not right. */
+ if (TREE_REFERENCE_EXPR (datum))
+ datum = build1 (INDIRECT_REF, TREE_TYPE (basetype), datum);
+ else
+#endif
+ datum = convert_from_reference (datum);
+ basetype = TREE_TYPE (datum);
+ code = TREE_CODE (basetype);
+ }
+
+ /* First, see if there is a field or component with name COMPONENT. */
+ if (TREE_CODE (component) == TREE_LIST)
+ {
+ my_friendly_assert (!(TREE_CHAIN (component) == NULL_TREE
+ && DECL_CHAIN (TREE_VALUE (component)) == NULL_TREE), 309);
+ return build (COMPONENT_REF, TREE_TYPE (component), datum, component);
+ }
+#if 0
+ if (TREE_CODE (component) == TYPE_EXPR)
+ return build_component_type_expr (datum, component, NULL_TREE, protect);
+#endif
+
+ if (! IS_AGGR_TYPE_CODE (code))
+ {
+ if (code != ERROR_MARK)
+ cp_error ("request for member `%D' in `%E', which is of non-aggregate type `%T'",
+ component, datum, basetype);
+ return error_mark_node;
+ }
+
+ if (TYPE_SIZE (basetype) == 0)
+ {
+ incomplete_type_error (0, basetype);
+ return error_mark_node;
+ }
+
+ if (TREE_CODE (component) == BIT_NOT_EXPR)
+ {
+ if (TYPE_IDENTIFIER (basetype) != TREE_OPERAND (component, 0))
+ {
+ cp_error ("destructor specifier `%T::~%T' must have matching names",
+ basetype, TREE_OPERAND (component, 0));
+ return error_mark_node;
+ }
+ if (! TYPE_HAS_DESTRUCTOR (basetype))
+ {
+ cp_error ("type `%T' has no destructor", basetype);
+ return error_mark_node;
+ }
+ return TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (basetype), 0);
+ }
+
+ /* Look up component name in the structure type definition. */
+ if (CLASSTYPE_VFIELD (basetype)
+ && DECL_NAME (CLASSTYPE_VFIELD (basetype)) == component)
+ /* Special-case this because if we use normal lookups in an ambiguous
+ hierarchy, the compiler will abort (because vptr lookups are
+ not supposed to be ambiguous. */
+ field = CLASSTYPE_VFIELD (basetype);
+ else
+ {
+ if (basetype_path == NULL_TREE)
+ basetype_path = TYPE_BINFO (basetype);
+ field = lookup_field (basetype_path, component,
+ protect && ! VFIELD_NAME_P (component), 0);
+ if (field == error_mark_node)
+ return error_mark_node;
+
+ if (field == NULL_TREE)
+ {
+ /* Not found as a data field, look for it as a method. If found,
+ then if this is the only possible one, return it, else
+ report ambiguity error. */
+ tree fndecls = lookup_fnfields (basetype_path, component, 1);
+ if (fndecls == error_mark_node)
+ return error_mark_node;
+ if (fndecls)
+ {
+ if (TREE_CHAIN (fndecls) == NULL_TREE
+ && DECL_CHAIN (TREE_VALUE (fndecls)) == NULL_TREE)
+ {
+ enum access_type access;
+ tree fndecl;
+
+ /* Unique, so use this one now. */
+ basetype = TREE_PURPOSE (fndecls);
+ fndecl = TREE_VALUE (fndecls);
+ access = compute_access (TREE_PURPOSE (fndecls), fndecl);
+ if (access == access_public)
+ {
+ if (DECL_VINDEX (fndecl)
+ && ! resolves_to_fixed_type_p (datum, 0))
+ {
+ tree addr = build_unary_op (ADDR_EXPR, datum, 0);
+ addr = convert_pointer_to (DECL_CONTEXT (fndecl), addr);
+ datum = build_indirect_ref (addr, NULL_PTR);
+ my_friendly_assert (datum != error_mark_node, 310);
+ fndecl = build_vfn_ref (&addr, datum, DECL_VINDEX (fndecl));
+ }
+ return fndecl;
+ }
+ if (access == access_protected)
+ cp_error ("member function `%D' is protected", fndecl);
+ else
+ cp_error ("member function `%D' is private", fndecl);
+ return error_mark_node;
+ }
+ else
+ return build (COMPONENT_REF, unknown_type_node, datum, fndecls);
+ }
+
+#if 0
+ if (component == ansi_opname[(int) TYPE_EXPR])
+ cp_error ("`%#T' has no such type conversion operator", basetype);
+ else
+#endif
+ cp_error ("`%#T' has no member named `%D'", basetype, component);
+ return error_mark_node;
+ }
+ else if (TREE_TYPE (field) == error_mark_node)
+ return error_mark_node;
+
+ if (TREE_CODE (field) != FIELD_DECL)
+ {
+ if (TREE_CODE (field) == TYPE_DECL)
+ {
+ cp_error ("invalid use of type decl `%#D' as expression", field);
+ return error_mark_node;
+ }
+ if (DECL_RTL (field) != 0)
+ assemble_external (field);
+ TREE_USED (field) = 1;
+ return field;
+ }
+ }
+
+ if (DECL_FIELD_CONTEXT (field) != basetype
+ && TYPE_USES_COMPLEX_INHERITANCE (basetype))
+ {
+ tree addr = build_unary_op (ADDR_EXPR, datum, 0);
+ if (integer_zerop (addr))
+ {
+ error ("invalid reference to NULL ptr, use ptr-to-member instead");
+ return error_mark_node;
+ }
+ addr = convert_pointer_to (DECL_FIELD_CONTEXT (field), addr);
+ datum = build_indirect_ref (addr, NULL_PTR);
+ my_friendly_assert (datum != error_mark_node, 311);
+ }
+ ref = build (COMPONENT_REF, TREE_TYPE (field), break_out_cleanups (datum), field);
+
+ if (TREE_READONLY (datum) || TREE_READONLY (field))
+ TREE_READONLY (ref) = 1;
+ if (TREE_THIS_VOLATILE (datum) || TREE_THIS_VOLATILE (field))
+ TREE_THIS_VOLATILE (ref) = 1;
+ if (DECL_MUTABLE_P (field))
+ TREE_READONLY (ref) = 0;
+
+ return ref;
+}
+
+/* Given an expression PTR for a pointer, return an expression
+ for the value pointed to.
+ ERRORSTRING is the name of the operator to appear in error messages.
+
+ This function may need to overload OPERATOR_FNNAME.
+ Must also handle REFERENCE_TYPEs for C++. */
+
+tree
+build_x_indirect_ref (ptr, errorstring)
+ tree ptr;
+ char *errorstring;
+{
+ tree rval = build_opfncall (INDIRECT_REF, LOOKUP_NORMAL, ptr, NULL_TREE, NULL_TREE);
+ if (rval)
+ return rval;
+ return build_indirect_ref (ptr, errorstring);
+}
+
+tree
+build_indirect_ref (ptr, errorstring)
+ tree ptr;
+ char *errorstring;
+{
+ register tree pointer = default_conversion (ptr);
+ register tree type = TREE_TYPE (pointer);
+
+ if (ptr == current_class_decl)
+ return C_C_D;
+
+ if (TREE_CODE (type) == POINTER_TYPE || TREE_CODE (type) == REFERENCE_TYPE)
+ {
+ if (TREE_CODE (pointer) == ADDR_EXPR
+ && (TREE_TYPE (TREE_OPERAND (pointer, 0))
+ == TREE_TYPE (type)))
+ return TREE_OPERAND (pointer, 0);
+ else
+ {
+ tree t = TREE_TYPE (type);
+ register tree ref = build1 (INDIRECT_REF,
+ TYPE_MAIN_VARIANT (t), pointer);
+
+ TREE_READONLY (ref) = TYPE_READONLY (t);
+ TREE_THIS_VOLATILE (ref) = TYPE_VOLATILE (t);
+ TREE_SIDE_EFFECTS (ref)
+ = TYPE_VOLATILE (t) || TREE_SIDE_EFFECTS (pointer);
+ return ref;
+ }
+ }
+ /* `pointer' won't be an error_mark_node if we were given a
+ pointer to member, so it's cool to check for this here. */
+ else if (TYPE_PTRMEMFUNC_P (type))
+ error ("invalid use of `%s' on pointer to member function", errorstring);
+ else if (TREE_CODE (type) == RECORD_TYPE
+ && (IS_SIGNATURE_POINTER (type) || IS_SIGNATURE_REFERENCE (type)))
+ error ("cannot dereference signature pointer/reference");
+ else if (pointer != error_mark_node)
+ {
+ if (errorstring)
+ error ("invalid type argument of `%s'", errorstring);
+ else
+ error ("invalid type argument");
+ }
+ return error_mark_node;
+}
+
+/* This handles expressions of the form "a[i]", which denotes
+ an array reference.
+
+ This is logically equivalent in C to *(a+i), but we may do it differently.
+ If A is a variable or a member, we generate a primitive ARRAY_REF.
+ This avoids forcing the array out of registers, and can work on
+ arrays that are not lvalues (for example, members of structures returned
+ by functions).
+
+ If INDEX is of some user-defined type, it must be converted to
+ integer type. Otherwise, to make a compatible PLUS_EXPR, it
+ will inherit the type of the array, which will be some pointer type. */
+
+tree
+build_x_array_ref (array, index)
+ tree array, index;
+{
+ tree rval = build_opfncall (ARRAY_REF, LOOKUP_NORMAL, array, index, NULL_TREE);
+ if (rval)
+ return rval;
+ return build_array_ref (array, index);
+}
+
+tree
+build_array_ref (array, idx)
+ tree array, idx;
+{
+ tree itype;
+
+ if (idx == 0)
+ {
+ error ("subscript missing in array reference");
+ return error_mark_node;
+ }
+
+ if (TREE_TYPE (array) == error_mark_node
+ || TREE_TYPE (idx) == error_mark_node)
+ return error_mark_node;
+
+ itype = TREE_TYPE (idx);
+ /* We must check here for the reference, so we can do the possible
+ conversions immediately afterwards. */
+ if (TREE_CODE (itype) == REFERENCE_TYPE)
+ {
+ idx = convert_from_reference (idx);
+ itype = TREE_TYPE (idx);
+ }
+
+ if (IS_AGGR_TYPE (itype))
+ {
+ if (TYPE_HAS_INT_CONVERSION (itype))
+ idx = build_type_conversion (CONVERT_EXPR,
+ integer_type_node, idx, 1);
+ else
+ {
+ error_with_aggr_type (itype,
+ "type `%s' requires integer conversion for array indexing");
+ return error_mark_node;
+ }
+ }
+
+ if (TREE_CODE (TREE_TYPE (array)) == ARRAY_TYPE
+ && TREE_CODE (array) != INDIRECT_REF)
+ {
+ tree rval, type;
+
+ /* Subscripting with type char is likely to lose
+ on a machine where chars are signed.
+ So warn on any machine, but optionally.
+ Don't warn for unsigned char since that type is safe.
+ Don't warn for signed char because anyone who uses that
+ must have done so deliberately. */
+ if (warn_char_subscripts
+ && TYPE_MAIN_VARIANT (TREE_TYPE (idx)) == char_type_node)
+ warning ("array subscript has type `char'");
+
+ /* Apply default promotions *after* noticing character types. */
+ idx = default_conversion (idx);
+
+ if (TREE_CODE (TREE_TYPE (idx)) != INTEGER_TYPE)
+ {
+ error ("array subscript is not an integer");
+ return error_mark_node;
+ }
+
+ /* An array that is indexed by a non-constant
+ cannot be stored in a register; we must be able to do
+ address arithmetic on its address.
+ Likewise an array of elements of variable size. */
+ if (TREE_CODE (idx) != INTEGER_CST
+ || (TYPE_SIZE (TREE_TYPE (TREE_TYPE (array))) != 0
+ && TREE_CODE (TYPE_SIZE (TREE_TYPE (TREE_TYPE (array)))) != INTEGER_CST))
+ {
+ if (mark_addressable (array) == 0)
+ return error_mark_node;
+ }
+ /* An array that is indexed by a constant value which is not within
+ the array bounds cannot be stored in a register either; because we
+ would get a crash in store_bit_field/extract_bit_field when trying
+ to access a non-existent part of the register. */
+ if (TREE_CODE (idx) == INTEGER_CST
+ && TYPE_VALUES (TREE_TYPE (array))
+ && ! int_fits_type_p (idx, TYPE_VALUES (TREE_TYPE (array))))
+ {
+ if (mark_addressable (array) == 0)
+ return error_mark_node;
+ }
+
+ /* Note in C++ we don't bother warning about subscripting a
+ `register' array, since it's legal in C++ to take the address
+ of something with that storage specification. */
+ if (pedantic && !lvalue_p (array))
+ pedwarn ("ANSI C++ forbids subscripting non-lvalue array");
+
+ if (pedantic)
+ {
+ tree foo = array;
+ while (TREE_CODE (foo) == COMPONENT_REF)
+ foo = TREE_OPERAND (foo, 0);
+ if (TREE_CODE (foo) == VAR_DECL && DECL_REGISTER (foo))
+ pedwarn ("ANSI C++ forbids subscripting non-lvalue array");
+ }
+
+ type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (array)));
+ rval = build (ARRAY_REF, type, array, idx);
+ /* Array ref is const/volatile if the array elements are
+ or if the array is.. */
+ TREE_READONLY (rval)
+ |= (TYPE_READONLY (TREE_TYPE (TREE_TYPE (array)))
+ | TREE_READONLY (array));
+ TREE_SIDE_EFFECTS (rval)
+ |= (TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (array)))
+ | TREE_SIDE_EFFECTS (array));
+ TREE_THIS_VOLATILE (rval)
+ |= (TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (array)))
+ /* This was added by rms on 16 Nov 91.
+ It fixes vol struct foo *a; a->elts[1]
+ in an inline function.
+ Hope it doesn't break something else. */
+ | TREE_THIS_VOLATILE (array));
+ return require_complete_type (fold (rval));
+ }
+
+ {
+ tree ar = default_conversion (array);
+ tree ind = default_conversion (idx);
+
+ /* Put the integer in IND to simplify error checking. */
+ if (TREE_CODE (TREE_TYPE (ar)) == INTEGER_TYPE)
+ {
+ tree temp = ar;
+ ar = ind;
+ ind = temp;
+ }
+
+ if (ar == error_mark_node)
+ return ar;
+
+ if (TREE_CODE (TREE_TYPE (ar)) != POINTER_TYPE)
+ {
+ error ("subscripted value is neither array nor pointer");
+ return error_mark_node;
+ }
+ if (TREE_CODE (TREE_TYPE (ind)) != INTEGER_TYPE)
+ {
+ error ("array subscript is not an integer");
+ return error_mark_node;
+ }
+
+ return build_indirect_ref (build_binary_op_nodefault (PLUS_EXPR, ar, ind, PLUS_EXPR),
+ "array indexing");
+ }
+}
+
+/* Build a function call to function FUNCTION with parameters PARAMS.
+ PARAMS is a list--a chain of TREE_LIST nodes--in which the
+ TREE_VALUE of each node is a parameter-expression.
+ FUNCTION's data type may be a function type or a pointer-to-function.
+
+ For C++: If FUNCTION's data type is a TREE_LIST, then the tree list
+ is the list of possible methods that FUNCTION could conceivably
+ be. If the list of methods comes from a class, then it will be
+ a list of lists (where each element is associated with the class
+ that produced it), otherwise it will be a simple list (for
+ functions overloaded in global scope).
+
+ In the first case, TREE_VALUE (function) is the head of one of those
+ lists, and TREE_PURPOSE is the name of the function.
+
+ In the second case, TREE_PURPOSE (function) is the function's
+ name directly.
+
+ DECL is the class instance variable, usually CURRENT_CLASS_DECL. */
+
+/*
+ * [eichin:19911015.1726EST] actually return a possibly incomplete
+ * type
+ */
+tree
+build_x_function_call (function, params, decl)
+ tree function, params, decl;
+{
+ tree type;
+ int is_method;
+
+ if (function == error_mark_node)
+ return error_mark_node;
+
+ type = TREE_TYPE (function);
+ is_method = ((TREE_CODE (function) == TREE_LIST
+ && current_class_type != NULL_TREE
+ && IDENTIFIER_CLASS_VALUE (TREE_PURPOSE (function)) == function)
+ || TREE_CODE (function) == IDENTIFIER_NODE
+ || TREE_CODE (type) == METHOD_TYPE
+ || TYPE_PTRMEMFUNC_P (type));
+
+ /* Handle methods, friends, and overloaded functions, respectively. */
+ if (is_method)
+ {
+ if (TREE_CODE (function) == FUNCTION_DECL)
+ {
+ if (DECL_NAME (function))
+ function = DECL_NAME (function);
+ else
+ function = TYPE_IDENTIFIER (DECL_CLASS_CONTEXT (function));
+ }
+ else if (TREE_CODE (function) == TREE_LIST)
+ {
+#if 0
+ if (TREE_CODE (TREE_VALUE (function)) == TREE_LIST)
+ function = TREE_PURPOSE (TREE_VALUE (function));
+ else
+ function = TREE_PURPOSE (function);
+#else
+ my_friendly_assert (TREE_CODE (TREE_VALUE (function)) == FUNCTION_DECL, 312);
+ function = TREE_PURPOSE (function);
+#endif
+ }
+ else if (TREE_CODE (function) != IDENTIFIER_NODE)
+ {
+ if (TREE_CODE (function) == OFFSET_REF)
+ {
+ if (TREE_OPERAND (function, 0))
+ decl = TREE_OPERAND (function, 0);
+ }
+ /* Call via a pointer to member function. */
+ if (decl == NULL_TREE)
+ {
+ error ("pointer to member function called, but not in class scope");
+ return error_mark_node;
+ }
+ /* What other type of POINTER_TYPE could this be? */
+ if (TREE_CODE (TREE_TYPE (function)) != POINTER_TYPE
+ && ! TYPE_PTRMEMFUNC_P (TREE_TYPE (function))
+ && TREE_CODE (function) != OFFSET_REF)
+ function = build (OFFSET_REF, TREE_TYPE (type), NULL_TREE, function);
+ goto do_x_function;
+ }
+
+ /* this is an abbreviated method call.
+ must go through here in case it is a virtual function.
+ @@ Perhaps this could be optimized. */
+
+ if (decl == NULL_TREE)
+ {
+ if (current_class_type == NULL_TREE)
+ {
+ error ("object missing in call to method `%s'",
+ IDENTIFIER_POINTER (function));
+ return error_mark_node;
+ }
+ /* Yow: call from a static member function. */
+ decl = build1 (NOP_EXPR, TYPE_POINTER_TO (current_class_type),
+ error_mark_node);
+ decl = build_indirect_ref (decl, NULL_PTR);
+ }
+
+ return build_method_call (decl, function, params,
+ NULL_TREE, LOOKUP_NORMAL);
+ }
+ else if (TREE_CODE (function) == COMPONENT_REF
+ && type == unknown_type_node)
+ {
+ /* Should we undo what was done in build_component_ref? */
+ if (TREE_CODE (TREE_PURPOSE (TREE_OPERAND (function, 1))) == TREE_VEC)
+ /* Get the name that build_component_ref hid. */
+ function = DECL_NAME (TREE_VALUE (TREE_OPERAND (function, 1)));
+ else
+ function = TREE_PURPOSE (TREE_OPERAND (function, 1));
+ return build_method_call (decl, function, params,
+ NULL_TREE, LOOKUP_NORMAL);
+ }
+ else if (TREE_CODE (function) == TREE_LIST)
+ {
+ if (TREE_VALUE (function) == NULL_TREE)
+ {
+ cp_error ("function `%D' declared overloaded, but no definitions appear with which to resolve it?!?",
+ TREE_PURPOSE (function));
+ return error_mark_node;
+ }
+ else
+ {
+ tree val = TREE_VALUE (function);
+
+ if (TREE_CODE (val) == TEMPLATE_DECL)
+ return build_overload_call_maybe
+ (function, params, LOOKUP_COMPLAIN, (struct candidate *)0);
+ else if (DECL_CHAIN (val) != NULL_TREE)
+ return build_overload_call
+ (function, params, LOOKUP_COMPLAIN, (struct candidate *)0);
+ else
+ my_friendly_abort (360);
+ }
+ }
+
+ do_x_function:
+ if (TREE_CODE (function) == OFFSET_REF)
+ {
+ /* If the component is a data element (or a virtual function), we play
+ games here to make things work. */
+ tree decl_addr;
+
+ if (TREE_OPERAND (function, 0))
+ decl = TREE_OPERAND (function, 0);
+ else
+ decl = C_C_D;
+
+ decl_addr = build_unary_op (ADDR_EXPR, decl, 0);
+ function = get_member_function_from_ptrfunc (&decl_addr, decl,
+ TREE_OPERAND (function, 1));
+ params = tree_cons (NULL_TREE, decl_addr, params);
+ return build_function_call (function, params);
+ }
+
+ type = TREE_TYPE (function);
+ if (type != error_mark_node)
+ {
+ if (TREE_CODE (type) == REFERENCE_TYPE)
+ type = TREE_TYPE (type);
+
+ if (TYPE_LANG_SPECIFIC (type) && TYPE_OVERLOADS_CALL_EXPR (type))
+ return build_opfncall (CALL_EXPR, LOOKUP_NORMAL, function, params, NULL_TREE);
+ }
+
+ if (is_method)
+ {
+ tree fntype = TREE_TYPE (function);
+ tree ctypeptr;
+
+ /* Explicitly named method? */
+ if (TREE_CODE (function) == FUNCTION_DECL)
+ ctypeptr = TYPE_POINTER_TO (DECL_CLASS_CONTEXT (function));
+ /* Expression with ptr-to-method type? It could either be a plain
+ usage, or it might be a case where the ptr-to-method is being
+ passed in as an argument. */
+ else if (TYPE_PTRMEMFUNC_P (fntype))
+ {
+ tree rec = TYPE_METHOD_BASETYPE (TREE_TYPE (TYPE_PTRMEMFUNC_FN_TYPE (fntype)));
+ ctypeptr = TYPE_POINTER_TO (rec);
+ }
+ /* Unexpected node type? */
+ else
+ my_friendly_abort (116);
+ if (decl == NULL_TREE)
+ {
+ if (current_function_decl
+ && DECL_STATIC_FUNCTION_P (current_function_decl))
+ error ("invalid call to member function needing `this' in static member function scope");
+ else
+ error ("pointer to member function called, but not in class scope");
+ return error_mark_node;
+ }
+ if (TREE_CODE (TREE_TYPE (decl)) != POINTER_TYPE
+ && ! TYPE_PTRMEMFUNC_P (TREE_TYPE (decl)))
+ {
+ decl = build_unary_op (ADDR_EXPR, decl, 0);
+ decl = convert_pointer_to (TREE_TYPE (ctypeptr), decl);
+ }
+ else
+ decl = build_c_cast (ctypeptr, decl);
+ params = tree_cons (NULL_TREE, decl, params);
+ }
+
+ return build_function_call (function, params);
+}
+
+/* Resolve a pointer to member function. INSTANCE is the object
+ instance to use, if the member points to a virtual member. */
+
+tree
+get_member_function_from_ptrfunc (instance_ptrptr, instance, function)
+ tree *instance_ptrptr;
+ tree instance;
+ tree function;
+{
+ if (TREE_CODE (function) == OFFSET_REF)
+ {
+ function = TREE_OPERAND (function, 1);
+ }
+
+ if (TYPE_PTRMEMFUNC_P (TREE_TYPE (function)))
+ {
+ tree fntype = TYPE_PTRMEMFUNC_FN_TYPE (TREE_TYPE (function));
+ tree index = save_expr (convert (integer_type_node,
+ build_component_ref (function,
+ index_identifier,
+ 0, 0)));
+ tree e1 = build (GT_EXPR, integer_type_node, index, integer_zero_node);
+ tree delta = build_component_ref (function, delta_identifier, 0, 0);
+ tree delta2 = DELTA2_FROM_PTRMEMFUNC (function);
+ tree e2;
+ tree e3;
+ tree aref, vtbl;
+
+ /* convert down to the right base, before using the instance. */
+ instance = convert_pointer_to_real (TYPE_METHOD_BASETYPE (TREE_TYPE (fntype)),
+ build_unary_op (ADDR_EXPR, instance, 0));
+ if (instance == error_mark_node)
+ return instance;
+
+ vtbl = convert_pointer_to (ptr_type_node, instance);
+ vtbl
+ = build (PLUS_EXPR,
+ build_pointer_type (build_pointer_type (vtable_entry_type)),
+ vtbl, convert (sizetype, delta2));
+ vtbl = build_indirect_ref (vtbl, NULL_PTR);
+ aref = build_array_ref (vtbl, size_binop (MINUS_EXPR,
+ index,
+ integer_one_node));
+ if (! flag_vtable_thunks)
+ {
+ aref = save_expr (aref);
+
+ /* Save the intermediate result in a SAVE_EXPR so we don't have to
+ compute each component of the virtual function pointer twice. */
+ if (/* !building_cleanup && */ TREE_CODE (aref) == INDIRECT_REF)
+ TREE_OPERAND (aref, 0) = save_expr (TREE_OPERAND (aref, 0));
+
+ delta = build (PLUS_EXPR, integer_type_node,
+ build_conditional_expr (e1, build_component_ref (aref, delta_identifier, 0, 0), integer_zero_node),
+ delta);
+ }
+
+ *instance_ptrptr = build (PLUS_EXPR, TREE_TYPE (*instance_ptrptr),
+ *instance_ptrptr,
+ convert (integer_type_node, delta));
+ if (flag_vtable_thunks)
+ e2 = aref;
+ else
+ e2 = build_component_ref (aref, pfn_identifier, 0, 0);
+
+ e3 = PFN_FROM_PTRMEMFUNC (function);
+ TREE_TYPE (e2) = TREE_TYPE (e3);
+ function = build_conditional_expr (e1, e2, e3);
+ }
+ return function;
+}
+
+tree
+build_function_call_real (function, params, require_complete, flags)
+ tree function, params;
+ int require_complete, flags;
+{
+ register tree fntype, fndecl;
+ register tree value_type;
+ register tree coerced_params;
+ tree name = NULL_TREE, assembler_name = NULL_TREE;
+ int is_method;
+
+ /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.
+ Strip such NOP_EXPRs, since FUNCTION is used in non-lvalue context. */
+ if (TREE_CODE (function) == NOP_EXPR
+ && TREE_TYPE (function) == TREE_TYPE (TREE_OPERAND (function, 0)))
+ function = TREE_OPERAND (function, 0);
+
+ if (TREE_CODE (function) == FUNCTION_DECL)
+ {
+ name = DECL_NAME (function);
+ assembler_name = DECL_ASSEMBLER_NAME (function);
+
+ GNU_xref_call (current_function_decl,
+ IDENTIFIER_POINTER (name ? name
+ : TYPE_IDENTIFIER (DECL_CLASS_CONTEXT (function))));
+ assemble_external (function);
+ fndecl = function;
+
+ /* Convert anything with function type to a pointer-to-function. */
+ if (pedantic
+ && name
+ && IDENTIFIER_LENGTH (name) == 4
+ && ! strcmp (IDENTIFIER_POINTER (name), "main")
+ && DECL_CONTEXT (function) == NULL_TREE)
+ {
+ pedwarn ("ANSI C++ forbids calling `main' from within program");
+ }
+
+ /* Differs from default_conversion by not setting TREE_ADDRESSABLE
+ (because calling an inline function does not mean the function
+ needs to be separately compiled). */
+
+ if (DECL_INLINE (function))
+ {
+ fntype = build_type_variant (TREE_TYPE (function),
+ TREE_READONLY (function),
+ TREE_THIS_VOLATILE (function));
+ function = build1 (ADDR_EXPR, build_pointer_type (fntype), function);
+ }
+ else
+ {
+ assemble_external (function);
+ TREE_USED (function) = 1;
+ function = default_conversion (function);
+ }
+ }
+ else
+ {
+ fndecl = NULL_TREE;
+
+ /* Convert anything with function type to a pointer-to-function. */
+ if (function == error_mark_node)
+ return error_mark_node;
+ function = default_conversion (function);
+ }
+
+ fntype = TREE_TYPE (function);
+
+ if (TYPE_PTRMEMFUNC_P (fntype))
+ {
+ tree instance_ptr = build_unary_op (ADDR_EXPR, C_C_D, 0);
+ fntype = TYPE_PTRMEMFUNC_FN_TYPE (fntype);
+ function = get_member_function_from_ptrfunc (&instance_ptr, C_C_D, function);
+ }
+
+ is_method = (TREE_CODE (fntype) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (fntype)) == METHOD_TYPE);
+
+ if (!((TREE_CODE (fntype) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (fntype)) == FUNCTION_TYPE)
+ || is_method))
+ {
+ error ("called object is not a function");
+ return error_mark_node;
+ }
+
+ /* fntype now gets the type of function pointed to. */
+ fntype = TREE_TYPE (fntype);
+
+ /* Convert the parameters to the types declared in the
+ function prototype, or apply default promotions. */
+
+ if (flags & LOOKUP_COMPLAIN)
+ coerced_params = convert_arguments (NULL_TREE, TYPE_ARG_TYPES (fntype),
+ params, fndecl, LOOKUP_NORMAL);
+ else
+ coerced_params = convert_arguments (NULL_TREE, TYPE_ARG_TYPES (fntype),
+ params, fndecl, 0);
+
+ /* Check for errors in format strings. */
+
+ if (warn_format && (name || assembler_name))
+ check_function_format (name, assembler_name, coerced_params);
+
+ /* Recognize certain built-in functions so we can make tree-codes
+ other than CALL_EXPR. We do this when it enables fold-const.c
+ to do something useful. */
+
+ if (TREE_CODE (function) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (function, 0)) == FUNCTION_DECL
+ && DECL_BUILT_IN (TREE_OPERAND (function, 0)))
+ switch (DECL_FUNCTION_CODE (TREE_OPERAND (function, 0)))
+ {
+ case BUILT_IN_ABS:
+ case BUILT_IN_LABS:
+ case BUILT_IN_FABS:
+ if (coerced_params == 0)
+ return integer_zero_node;
+ return build_unary_op (ABS_EXPR, TREE_VALUE (coerced_params), 0);
+ }
+
+ /* C++ */
+ value_type = TREE_TYPE (fntype) ? TREE_TYPE (fntype) : void_type_node;
+ {
+ register tree result =
+ build (CALL_EXPR, value_type,
+ function, coerced_params, NULL_TREE);
+
+ TREE_SIDE_EFFECTS (result) = 1;
+ /* Remove this sometime. */
+ TREE_RAISES (result) |= !! TYPE_RAISES_EXCEPTIONS (fntype);
+ if (! require_complete)
+ return result;
+ if (value_type == void_type_node)
+ return result;
+ return require_complete_type (result);
+ }
+}
+
+tree
+build_function_call (function, params)
+ tree function, params;
+{
+ return build_function_call_real (function, params, 1, LOOKUP_NORMAL);
+}
+
+tree
+build_function_call_maybe (function, params)
+ tree function, params;
+{
+ return build_function_call_real (function, params, 0, 0);
+}
+
+
+/* Convert the actual parameter expressions in the list VALUES
+ to the types in the list TYPELIST.
+ If parmdecls is exhausted, or when an element has NULL as its type,
+ perform the default conversions.
+
+ RETURN_LOC is the location of the return value, if known, NULL_TREE
+ otherwise. This is useful in the case where we can avoid creating
+ a temporary variable in the case where we can initialize the return
+ value directly. If we are not eliding constructors, then we set this
+ to NULL_TREE to avoid this avoidance.
+
+ NAME is an IDENTIFIER_NODE or 0. It is used only for error messages.
+
+ This is also where warnings about wrong number of args are generated.
+
+ Return a list of expressions for the parameters as converted.
+
+ Both VALUES and the returned value are chains of TREE_LIST nodes
+ with the elements of the list in the TREE_VALUE slots of those nodes.
+
+ In C++, unspecified trailing parameters can be filled in with their
+ default arguments, if such were specified. Do so here. */
+
+tree
+convert_arguments (return_loc, typelist, values, fndecl, flags)
+ tree return_loc, typelist, values, fndecl;
+ int flags;
+{
+ extern tree gc_protect_fndecl;
+ register tree typetail, valtail;
+ register tree result = NULL_TREE;
+ char *called_thing;
+ int maybe_raises = 0;
+ int i = 0;
+
+ if (! flag_elide_constructors)
+ return_loc = 0;
+
+ if (fndecl)
+ {
+ if (TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE)
+ {
+ if (DECL_NAME (fndecl) == NULL_TREE
+ || IDENTIFIER_HAS_TYPE_VALUE (DECL_NAME (fndecl)))
+ called_thing = "constructor";
+ else
+ called_thing = "member function";
+ }
+ else
+ called_thing = "function";
+ }
+
+ for (valtail = values, typetail = typelist;
+ valtail;
+ valtail = TREE_CHAIN (valtail), i++)
+ {
+ register tree type = typetail ? TREE_VALUE (typetail) : 0;
+ register tree val = TREE_VALUE (valtail);
+
+ if (type == void_type_node)
+ {
+ if (fndecl)
+ {
+ char *buf = (char *)alloca (40 + strlen (called_thing));
+ sprintf (buf, "too many arguments to %s `%%s'", called_thing);
+ error_with_decl (fndecl, buf);
+ error ("at this point in file");
+ }
+ else
+ error ("too many arguments to function");
+ /* In case anybody wants to know if this argument
+ list is valid. */
+ if (result)
+ TREE_TYPE (tree_last (result)) = error_mark_node;
+ break;
+ }
+
+ /* The tree type of the parameter being passed may not yet be
+ known. In this case, its type is TYPE_UNKNOWN, and will
+ be instantiated by the type given by TYPE. If TYPE
+ is also NULL, the tree type of VAL is ERROR_MARK_NODE. */
+ if (type && type_unknown_p (val))
+ val = require_instantiated_type (type, val, integer_zero_node);
+ else if (type_unknown_p (val))
+ {
+ /* Strip the `&' from an overloaded FUNCTION_DECL. */
+ if (TREE_CODE (val) == ADDR_EXPR)
+ val = TREE_OPERAND (val, 0);
+ if (TREE_CODE (val) == TREE_LIST
+ && TREE_CHAIN (val) == NULL_TREE
+ && TREE_TYPE (TREE_VALUE (val)) != NULL_TREE
+ && (TREE_TYPE (val) == unknown_type_node
+ || DECL_CHAIN (TREE_VALUE (val)) == NULL_TREE))
+ /* Instantiates automatically. */
+ val = TREE_VALUE (val);
+ else
+ {
+ error ("insufficient type information in parameter list");
+ val = integer_zero_node;
+ }
+ }
+ else if (TREE_CODE (val) == OFFSET_REF
+ && TREE_CODE (TREE_TYPE (val)) == METHOD_TYPE)
+ {
+ /* This is unclean. Should be handled elsewhere. */
+ val = build_unary_op (ADDR_EXPR, val, 0);
+ }
+ else if (TREE_CODE (val) == OFFSET_REF)
+ val = resolve_offset_ref (val);
+
+ {
+#if 0
+ /* This code forces the assumption that if we have a ptr-to-func
+ type in an arglist, that every routine that wants to check
+ its validity has done so, and thus we need not do any
+ more conversion. I don't remember why this is necessary. */
+ else if (TREE_CODE (ttype) == FUNCTION_TYPE
+ && (type == NULL
+ || TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE
+ || TREE_CODE (TREE_TYPE (type)) == VOID_TYPE))
+ {
+ type = build_pointer_type (ttype);
+ }
+#endif
+ }
+
+ /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.
+ Strip such NOP_EXPRs, since VAL is used in non-lvalue context. */
+ if (TREE_CODE (val) == NOP_EXPR
+ && TREE_TYPE (val) == TREE_TYPE (TREE_OPERAND (val, 0))
+ && (type == 0 || TREE_CODE (type) != REFERENCE_TYPE))
+ val = TREE_OPERAND (val, 0);
+
+ if ((type == 0 || TREE_CODE (type) != REFERENCE_TYPE)
+ && (TREE_CODE (TREE_TYPE (val)) == ARRAY_TYPE
+ || TREE_CODE (TREE_TYPE (val)) == FUNCTION_TYPE
+ || TREE_CODE (TREE_TYPE (val)) == METHOD_TYPE))
+ val = default_conversion (val);
+
+ val = require_complete_type (val);
+
+ if (val == error_mark_node)
+ continue;
+
+ maybe_raises |= TREE_RAISES (val);
+
+ if (type != 0)
+ {
+ /* Formal parm type is specified by a function prototype. */
+ tree parmval;
+
+ if (TYPE_SIZE (type) == 0)
+ {
+ error ("parameter type of called function is incomplete");
+ parmval = val;
+ }
+ else
+ {
+#ifdef PROMOTE_PROTOTYPES
+ /* Rather than truncating and then reextending,
+ convert directly to int, if that's the type we will want. */
+ if (! flag_traditional
+ && (TREE_CODE (type) == INTEGER_TYPE
+ || TREE_CODE (type) == ENUMERAL_TYPE)
+ && (TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node)))
+ type = integer_type_node;
+#endif
+ parmval = convert_for_initialization (return_loc, type, val, flags,
+ "argument passing", fndecl, i);
+#ifdef PROMOTE_PROTOTYPES
+ if ((TREE_CODE (type) == INTEGER_TYPE
+ || TREE_CODE (type) == ENUMERAL_TYPE)
+ && (TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node)))
+ parmval = default_conversion (parmval);
+#endif
+ }
+ result = tree_cons (NULL_TREE, parmval, result);
+ }
+ else
+ {
+ if (TREE_CODE (TREE_TYPE (val)) == REFERENCE_TYPE)
+ val = convert_from_reference (val);
+
+ if (TREE_CODE (TREE_TYPE (val)) == REAL_TYPE
+ && (TYPE_PRECISION (TREE_TYPE (val))
+ < TYPE_PRECISION (double_type_node)))
+ /* Convert `float' to `double'. */
+ result = tree_cons (NULL_TREE, convert (double_type_node, val), result);
+ else if (TYPE_LANG_SPECIFIC (TREE_TYPE (val))
+ && (TYPE_HAS_INIT_REF (TREE_TYPE (val))
+ || TYPE_HAS_ASSIGN_REF (TREE_TYPE (val))))
+ {
+ cp_warning ("cannot pass objects of type `%T' through `...'",
+ TREE_TYPE (val));
+ result = tree_cons (NULL_TREE, val, result);
+ }
+ else
+ /* Convert `short' and `char' to full-size `int'. */
+ result = tree_cons (NULL_TREE, default_conversion (val), result);
+ }
+
+ if (flag_gc
+ /* There are certain functions for which we don't need
+ to protect our arguments. GC_PROTECT_FNDECL is one. */
+ && fndecl != gc_protect_fndecl
+ && type_needs_gc_entry (TREE_TYPE (TREE_VALUE (result)))
+ && ! value_safe_from_gc (NULL_TREE, TREE_VALUE (result)))
+ /* This will build a temporary variable whose cleanup is
+ to clear the obstack entry. */
+ TREE_VALUE (result) = protect_value_from_gc (NULL_TREE,
+ TREE_VALUE (result));
+
+ if (typetail)
+ typetail = TREE_CHAIN (typetail);
+ }
+
+ if (typetail != 0 && typetail != void_list_node)
+ {
+ /* See if there are default arguments that can be used */
+ if (TREE_PURPOSE (typetail))
+ {
+ while (typetail != void_list_node)
+ {
+ tree type = TREE_VALUE (typetail);
+ tree val = TREE_PURPOSE (typetail);
+ tree parmval;
+
+ if (val == NULL_TREE)
+ parmval = error_mark_node;
+ else if (TREE_CODE (val) == CONSTRUCTOR)
+ {
+ parmval = digest_init (type, val, (tree *)0);
+ parmval = convert_for_initialization (return_loc, type, parmval, flags,
+ "default constructor", fndecl, i);
+ }
+ else
+ {
+ /* This could get clobbered by the following call. */
+ if (TREE_HAS_CONSTRUCTOR (val))
+ val = copy_node (val);
+
+ parmval = convert_for_initialization (return_loc, type, val, flags,
+ "default argument", fndecl, i);
+#ifdef PROMOTE_PROTOTYPES
+ if ((TREE_CODE (type) == INTEGER_TYPE
+ || TREE_CODE (type) == ENUMERAL_TYPE)
+ && (TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node)))
+ parmval = default_conversion (parmval);
+#endif
+ }
+ maybe_raises |= TREE_RAISES (parmval);
+
+ if (flag_gc
+ && type_needs_gc_entry (TREE_TYPE (parmval))
+ && ! value_safe_from_gc (NULL_TREE, parmval))
+ parmval = protect_value_from_gc (NULL_TREE, parmval);
+
+ result = tree_cons (0, parmval, result);
+ typetail = TREE_CHAIN (typetail);
+ /* ends with `...'. */
+ if (typetail == NULL_TREE)
+ break;
+ }
+ }
+ else
+ {
+ if (fndecl)
+ {
+ char *buf = (char *)alloca (32 + strlen (called_thing));
+ sprintf (buf, "too few arguments to %s `%%#D'", called_thing);
+ cp_error_at (buf, fndecl);
+ error ("at this point in file");
+ }
+ else
+ error ("too few arguments to function");
+ return error_mark_list;
+ }
+ }
+ if (result)
+ TREE_RAISES (result) = maybe_raises;
+
+ return nreverse (result);
+}
+
+/* Build a binary-operation expression, after performing default
+ conversions on the operands. CODE is the kind of expression to build. */
+
+tree
+build_x_binary_op (code, arg1, arg2)
+ enum tree_code code;
+ tree arg1, arg2;
+{
+ tree rval = build_opfncall (code, LOOKUP_SPECULATIVELY,
+ arg1, arg2, NULL_TREE);
+ if (rval)
+ return build_opfncall (code, LOOKUP_NORMAL, arg1, arg2, NULL_TREE);
+ if (code == MEMBER_REF)
+ return build_m_component_ref (build_indirect_ref (arg1, NULL_PTR),
+ arg2);
+ return build_binary_op (code, arg1, arg2, 1);
+}
+
+tree
+build_binary_op (code, arg1, arg2, convert_p)
+ enum tree_code code;
+ tree arg1, arg2;
+ int convert_p;
+{
+ tree type1, type2;
+ tree args[2];
+
+ args[0] = arg1;
+ args[1] = arg2;
+
+ if (convert_p)
+ {
+ args[0] = default_conversion (args[0]);
+ args[1] = default_conversion (args[1]);
+
+ if (type_unknown_p (args[0]))
+ {
+ args[0] = instantiate_type (TREE_TYPE (args[1]), args[0], 1);
+ args[0] = default_conversion (args[0]);
+ }
+ else if (type_unknown_p (args[1]))
+ {
+ args[1] = require_instantiated_type (TREE_TYPE (args[0]),
+ args[1],
+ error_mark_node);
+ args[1] = default_conversion (args[1]);
+ }
+
+ type1 = TREE_TYPE (args[0]);
+ type2 = TREE_TYPE (args[1]);
+
+ if (IS_AGGR_TYPE_2 (type1, type2) && ! TYPE_PTRMEMFUNC_P (type1))
+ {
+ /* Try to convert this to something reasonable. */
+ if (! build_default_binary_type_conversion(code, &args[0], &args[1]))
+ return error_mark_node;
+ }
+ else if ((IS_AGGR_TYPE (type1) && ! TYPE_PTRMEMFUNC_P (type1))
+ || (IS_AGGR_TYPE (type2) && ! TYPE_PTRMEMFUNC_P (type2)))
+ {
+ int convert_index = IS_AGGR_TYPE (type2);
+ /* Avoid being tripped up by things like (ARG1 != 0). */
+ tree types[2], try;
+
+ types[0] = type1; types[1] = type2;
+ if (code == TRUTH_ANDIF_EXPR || code == TRUTH_ORIF_EXPR)
+ try = build_type_conversion (code, bool_type_node,
+ args[convert_index], 1);
+ else
+ {
+ try = build_type_conversion (code, types[convert_index ^ 1],
+ args[convert_index], 1);
+
+ if (try == 0
+ && args[1] == integer_zero_node
+ && (code == NE_EXPR || code == EQ_EXPR))
+ try = build_type_conversion (code, ptr_type_node,
+ args[convert_index], 1);
+ }
+
+ if (try == 0)
+ {
+ cp_error ("no match for `%O(%#T, %#T)'", code,
+ TREE_TYPE (arg1), TREE_TYPE (arg2));
+ return error_mark_node;
+ }
+ if (try == error_mark_node)
+ error ("ambiguous pointer conversion");
+ args[convert_index] = try;
+ }
+ }
+ return build_binary_op_nodefault (code, args[0], args[1], code);
+}
+
+/* Build a binary-operation expression without default conversions.
+ CODE is the kind of expression to build.
+ This function differs from `build' in several ways:
+ the data type of the result is computed and recorded in it,
+ warnings are generated if arg data types are invalid,
+ special handling for addition and subtraction of pointers is known,
+ and some optimization is done (operations on narrow ints
+ are done in the narrower type when that gives the same result).
+ Constant folding is also done before the result is returned.
+
+ ERROR_CODE is the code that determines what to say in error messages.
+ It is usually, but not always, the same as CODE.
+
+ Note that the operands will never have enumeral types
+ because either they have just had the default conversions performed
+ or they have both just been converted to some other type in which
+ the arithmetic is to be done.
+
+ C++: must do special pointer arithmetic when implementing
+ multiple inheritance, and deal with pointer to member functions. */
+
+tree
+build_binary_op_nodefault (code, orig_op0, orig_op1, error_code)
+ enum tree_code code;
+ tree orig_op0, orig_op1;
+ enum tree_code error_code;
+{
+ tree op0, op1;
+ register enum tree_code code0, code1;
+ tree type0, type1;
+
+ /* Expression code to give to the expression when it is built.
+ Normally this is CODE, which is what the caller asked for,
+ but in some special cases we change it. */
+ register enum tree_code resultcode = code;
+
+ /* Data type in which the computation is to be performed.
+ In the simplest cases this is the common type of the arguments. */
+ register tree result_type = NULL;
+
+ /* Nonzero means operands have already been type-converted
+ in whatever way is necessary.
+ Zero means they need to be converted to RESULT_TYPE. */
+ int converted = 0;
+
+ /* Nonzero means after finally constructing the expression
+ give it this type. Otherwise, give it type RESULT_TYPE. */
+ tree final_type = 0;
+
+ /* Nonzero if this is an operation like MIN or MAX which can
+ safely be computed in short if both args are promoted shorts.
+ Also implies COMMON.
+ -1 indicates a bitwise operation; this makes a difference
+ in the exact conditions for when it is safe to do the operation
+ in a narrower mode. */
+ int shorten = 0;
+
+ /* Nonzero if this is a comparison operation;
+ if both args are promoted shorts, compare the original shorts.
+ Also implies COMMON. */
+ int short_compare = 0;
+
+ /* Nonzero if this is a right-shift operation, which can be computed on the
+ original short and then promoted if the operand is a promoted short. */
+ int short_shift = 0;
+
+ /* Nonzero means set RESULT_TYPE to the common type of the args. */
+ int common = 0;
+
+ /* Apply default conversions. */
+ op0 = default_conversion (orig_op0);
+ op1 = default_conversion (orig_op1);
+
+ type0 = TREE_TYPE (op0);
+ type1 = TREE_TYPE (op1);
+
+ /* The expression codes of the data types of the arguments tell us
+ whether the arguments are integers, floating, pointers, etc. */
+ code0 = TREE_CODE (type0);
+ code1 = TREE_CODE (type1);
+
+ /* Strip NON_LVALUE_EXPRs, etc., since we aren't using as an lvalue. */
+ STRIP_TYPE_NOPS (op0);
+ STRIP_TYPE_NOPS (op1);
+
+ /* If an error was already reported for one of the arguments,
+ avoid reporting another error. */
+
+ if (code0 == ERROR_MARK || code1 == ERROR_MARK)
+ return error_mark_node;
+
+ switch (code)
+ {
+ case PLUS_EXPR:
+ /* Handle the pointer + int case. */
+ if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE)
+ return pointer_int_sum (PLUS_EXPR, op0, op1);
+ else if (code1 == POINTER_TYPE && code0 == INTEGER_TYPE)
+ return pointer_int_sum (PLUS_EXPR, op1, op0);
+ else
+ common = 1;
+ break;
+
+ case MINUS_EXPR:
+ /* Subtraction of two similar pointers.
+ We must subtract them as integers, then divide by object size. */
+ if (code0 == POINTER_TYPE && code1 == POINTER_TYPE
+ && comp_target_types (type0, type1, 1))
+ return pointer_diff (op0, op1);
+ /* Handle pointer minus int. Just like pointer plus int. */
+ else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE)
+ return pointer_int_sum (MINUS_EXPR, op0, op1);
+ else
+ common = 1;
+ break;
+
+ case MULT_EXPR:
+ common = 1;
+ break;
+
+ case TRUNC_DIV_EXPR:
+ case CEIL_DIV_EXPR:
+ case FLOOR_DIV_EXPR:
+ case ROUND_DIV_EXPR:
+ case EXACT_DIV_EXPR:
+ if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE)
+ && (code1 == INTEGER_TYPE || code1 == REAL_TYPE))
+ {
+ if (TREE_CODE (op1) == INTEGER_CST && integer_zerop (op1))
+ cp_warning ("division by zero in `%E / 0'", op0);
+ else if (TREE_CODE (op1) == REAL_CST && real_zerop (op1))
+ cp_warning ("division by zero in `%E / 0.'", op0);
+
+ if (!(code0 == INTEGER_TYPE && code1 == INTEGER_TYPE))
+ resultcode = RDIV_EXPR;
+ else
+ /* When dividing two signed integers, we have to promote to int.
+ unless we divide by a conatant != -1. Note that default
+ conversion will have been performed on the operands at this
+ point, so we have to dig out the original type to find out if
+ it was unsigned. */
+ shorten = ((TREE_CODE (op0) == NOP_EXPR
+ && TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (op0, 0))))
+ || (TREE_CODE (op1) == INTEGER_CST
+ && (TREE_INT_CST_LOW (op1) != -1
+ || TREE_INT_CST_HIGH (op1) != -1)));
+ common = 1;
+ }
+ break;
+
+ case BIT_AND_EXPR:
+ case BIT_ANDTC_EXPR:
+ case BIT_IOR_EXPR:
+ case BIT_XOR_EXPR:
+ if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)
+ shorten = -1;
+ /* If one operand is a constant, and the other is a short type
+ that has been converted to an int,
+ really do the work in the short type and then convert the
+ result to int. If we are lucky, the constant will be 0 or 1
+ in the short type, making the entire operation go away. */
+ if (TREE_CODE (op0) == INTEGER_CST
+ && TREE_CODE (op1) == NOP_EXPR
+ && TYPE_PRECISION (type1) > TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (op1, 0)))
+ && TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (op1, 0))))
+ {
+ final_type = result_type;
+ op1 = TREE_OPERAND (op1, 0);
+ result_type = TREE_TYPE (op1);
+ }
+ if (TREE_CODE (op1) == INTEGER_CST
+ && TREE_CODE (op0) == NOP_EXPR
+ && TYPE_PRECISION (type0) > TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (op0, 0)))
+ && TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (op0, 0))))
+ {
+ final_type = result_type;
+ op0 = TREE_OPERAND (op0, 0);
+ result_type = TREE_TYPE (op0);
+ }
+ break;
+
+ case TRUNC_MOD_EXPR:
+ case FLOOR_MOD_EXPR:
+ if (code1 == INTEGER_TYPE && integer_zerop (op1))
+ cp_warning ("division by zero in `%E % 0'", op0);
+ else if (code1 == REAL_TYPE && real_zerop (op1))
+ cp_warning ("division by zero in `%E % 0.'", op0);
+
+ if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)
+ {
+ /* Although it would be tempting to shorten always here, that loses
+ on some targets, since the modulo instruction is undefined if the
+ quotient can't be represented in the computation mode. We shorten
+ only if unsigned or if dividing by something we know != -1. */
+ shorten = ((TREE_CODE (op0) == NOP_EXPR
+ && TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (op0, 0))))
+ || (TREE_CODE (op1) == INTEGER_CST
+ && (TREE_INT_CST_LOW (op1) != -1
+ || TREE_INT_CST_HIGH (op1) != -1)));
+ common = 1;
+ }
+ break;
+
+ case TRUTH_ANDIF_EXPR:
+ case TRUTH_ORIF_EXPR:
+ case TRUTH_AND_EXPR:
+ case TRUTH_OR_EXPR:
+ result_type = bool_type_node;
+ op0 = bool_truthvalue_conversion (op0);
+ op1 = bool_truthvalue_conversion (op1);
+ converted = 1;
+ break;
+
+ /* Shift operations: result has same type as first operand;
+ always convert second operand to int.
+ Also set SHORT_SHIFT if shifting rightward. */
+
+ case RSHIFT_EXPR:
+ if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)
+ {
+ result_type = type0;
+ if (TREE_CODE (op1) == INTEGER_CST)
+ {
+ if (tree_int_cst_lt (op1, integer_zero_node))
+ warning ("right shift count is negative");
+ else
+ {
+ if (TREE_INT_CST_LOW (op1) | TREE_INT_CST_HIGH (op1))
+ short_shift = 1;
+ if (TREE_INT_CST_HIGH (op1) != 0
+ || ((unsigned HOST_WIDE_INT) TREE_INT_CST_LOW (op1)
+ >= TYPE_PRECISION (type0)))
+ warning ("right shift count >= width of type");
+ }
+ }
+ /* Convert the shift-count to an integer, regardless of
+ size of value being shifted. */
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (op1)) != integer_type_node)
+ op1 = convert (integer_type_node, op1);
+ /* Avoid converting op1 to result_type later. */
+ converted = 1;
+ }
+ break;
+
+ case LSHIFT_EXPR:
+ if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)
+ {
+ result_type = type0;
+ if (TREE_CODE (op1) == INTEGER_CST)
+ {
+ if (tree_int_cst_lt (op1, integer_zero_node))
+ warning ("left shift count is negative");
+ else if (TREE_INT_CST_HIGH (op1) != 0
+ || ((unsigned HOST_WIDE_INT) TREE_INT_CST_LOW (op1)
+ >= TYPE_PRECISION (type0)))
+ warning ("left shift count >= width of type");
+ }
+ /* Convert the shift-count to an integer, regardless of
+ size of value being shifted. */
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (op1)) != integer_type_node)
+ op1 = convert (integer_type_node, op1);
+ /* Avoid converting op1 to result_type later. */
+ converted = 1;
+ }
+ break;
+
+ case RROTATE_EXPR:
+ case LROTATE_EXPR:
+ if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)
+ {
+ result_type = type0;
+ if (TREE_CODE (op1) == INTEGER_CST)
+ {
+ if (tree_int_cst_lt (op1, integer_zero_node))
+ warning ("%s rotate count is negative",
+ (code == LROTATE_EXPR) ? "left" : "right");
+ else if (TREE_INT_CST_HIGH (op1) != 0
+ || ((unsigned HOST_WIDE_INT) TREE_INT_CST_LOW (op1)
+ >= TYPE_PRECISION (type0)))
+ warning ("%s rotate count >= width of type",
+ (code == LROTATE_EXPR) ? "left" : "right");
+ }
+ /* Convert the shift-count to an integer, regardless of
+ size of value being shifted. */
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (op1)) != integer_type_node)
+ op1 = convert (integer_type_node, op1);
+ }
+ break;
+
+ case EQ_EXPR:
+ case NE_EXPR:
+ result_type = bool_type_node;
+ converted = 1;
+ if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE)
+ && (code1 == INTEGER_TYPE || code1 == REAL_TYPE))
+ short_compare = 1;
+ else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE)
+ {
+ register tree tt0 = TYPE_MAIN_VARIANT (TREE_TYPE (type0));
+ register tree tt1 = TYPE_MAIN_VARIANT (TREE_TYPE (type1));
+ /* Anything compares with void *. void * compares with anything.
+ Otherwise, the targets must be the same. */
+ if (tt0 != tt1 && TREE_CODE (tt0) == RECORD_TYPE
+ && TREE_CODE (tt1) == RECORD_TYPE)
+ {
+ tree base = common_base_type (tt0, tt1);
+ if (base == NULL_TREE)
+ cp_warning ("comparison of distinct object pointer types `%T' and `%T'", type0, type1);
+ else if (base == error_mark_node)
+ {
+ cp_error ("comparison of pointer types `%T' and `%T' requires conversion to ambiguous supertype", type0, type1);
+ return error_mark_node;
+ }
+ else
+ {
+ if (integer_zerop (op0))
+ op0 = null_pointer_node;
+ else
+ op0 = convert_pointer_to (base, op0);
+ if (integer_zerop (op1))
+ op1 = null_pointer_node;
+ else
+ op1 = convert_pointer_to (base, op1);
+ }
+ }
+ else if (comp_target_types (type0, type1, 1))
+ ;
+ else if (tt0 == void_type_node)
+ {
+ if (pedantic && TREE_CODE (tt1) == FUNCTION_TYPE
+ && tree_int_cst_lt (TYPE_SIZE (type0), TYPE_SIZE (type1)))
+ pedwarn ("ANSI C++ forbids comparison of `void *' with function pointer");
+ }
+ else if (tt1 == void_type_node)
+ {
+ if (pedantic && TREE_CODE (tt0) == FUNCTION_TYPE
+ && tree_int_cst_lt (TYPE_SIZE (type1), TYPE_SIZE (type0)))
+ pedwarn ("ANSI C++ forbids comparison of `void *' with function pointer");
+ }
+ else if ((TYPE_SIZE (tt0) != 0) != (TYPE_SIZE (tt1) != 0))
+ cp_pedwarn ("comparison of %scomplete and %scomplete pointers `%T' and `%T'",
+ TYPE_SIZE (tt0) == 0 ? "in" : "",
+ TYPE_SIZE (tt1) == 0 ? "in" : "",
+ type0, type1);
+ else
+ cp_pedwarn ("comparison of distinct pointer types `%T' and `%T' lacks a cast",
+ type0, type1);
+ }
+ else if (code0 == POINTER_TYPE && TREE_CODE (op1) == INTEGER_CST
+ && integer_zerop (op1))
+ op1 = null_pointer_node;
+ else if (code1 == POINTER_TYPE && TREE_CODE (op0) == INTEGER_CST
+ && integer_zerop (op0))
+ op0 = null_pointer_node;
+ else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE)
+ {
+ error ("ANSI C++ forbids comparison between pointer and integer");
+ op1 = convert (TREE_TYPE (op0), op1);
+ }
+ else if (code0 == INTEGER_TYPE && code1 == POINTER_TYPE)
+ {
+ error ("ANSI C++ forbids comparison between pointer and integer");
+ op0 = convert (TREE_TYPE (op1), op0);
+ }
+ else if (TYPE_PTRMEMFUNC_P (type0) && TREE_CODE (op1) == INTEGER_CST
+ && integer_zerop (op1))
+ {
+ op0 = build_component_ref (op0, index_identifier, 0, 0);
+ op1 = integer_zero_node;
+ }
+ else if (TYPE_PTRMEMFUNC_P (type1) && TREE_CODE (op0) == INTEGER_CST
+ && integer_zerop (op0))
+ {
+ op0 = build_component_ref (op1, index_identifier, 0, 0);
+ op1 = integer_zero_node;
+ }
+ else if (TYPE_PTRMEMFUNC_P (type0) && TYPE_PTRMEMFUNC_P (type1)
+ && (TYPE_PTRMEMFUNC_FN_TYPE (type0)
+ == TYPE_PTRMEMFUNC_FN_TYPE (type1)))
+ {
+ /* The code we generate for the test is:
+
+ (op0.index == op1.index
+ && ((op1.index != -1 && op0.delta2 == op1.delta2)
+ || op0.pfn == op1.pfn)) */
+
+ tree index0 = build_component_ref (op0, index_identifier, 0, 0);
+ tree index1 = save_expr (build_component_ref (op1, index_identifier, 0, 0));
+ tree pfn0 = PFN_FROM_PTRMEMFUNC (op0);
+ tree pfn1 = PFN_FROM_PTRMEMFUNC (op1);
+ tree delta20 = DELTA2_FROM_PTRMEMFUNC (op0);
+ tree delta21 = DELTA2_FROM_PTRMEMFUNC (op1);
+ tree e1, e2, e3;
+ tree integer_neg_one_node
+ = size_binop (MINUS_EXPR, integer_zero_node, integer_one_node);
+ e1 = build_binary_op (EQ_EXPR, index0, index1, 1);
+ e2 = build_binary_op (NE_EXPR, index1, integer_neg_one_node, 1);
+ e2 = build_binary_op (TRUTH_ANDIF_EXPR, e2, build_binary_op (EQ_EXPR, delta20, delta21, 1), 1);
+ e3 = build_binary_op (EQ_EXPR, pfn0, pfn1, 1);
+ e2 = build_binary_op (TRUTH_ORIF_EXPR, e2, e3, 1);
+ e2 = build_binary_op (TRUTH_ANDIF_EXPR, e1, e2, 1);
+ if (code == EQ_EXPR)
+ return e2;
+ return build_binary_op (EQ_EXPR, e2, integer_zero_node, 1);
+ }
+ else if (TYPE_PTRMEMFUNC_P (type0)
+ && TYPE_PTRMEMFUNC_FN_TYPE (type0) == type1)
+ {
+ tree index0 = build_component_ref (op0, index_identifier, 0, 0);
+ tree index1;
+ tree pfn0 = PFN_FROM_PTRMEMFUNC (op0);
+ tree delta20 = DELTA2_FROM_PTRMEMFUNC (op0);
+ tree delta21 = integer_zero_node;
+ tree e1, e2, e3;
+ tree integer_neg_one_node
+ = size_binop (MINUS_EXPR, integer_zero_node, integer_one_node);
+ if (TREE_CODE (TREE_OPERAND (op1, 0)) == FUNCTION_DECL
+ && DECL_VINDEX (TREE_OPERAND (op1, 0)))
+ {
+ /* Map everything down one to make room for the null pointer to member. */
+ index1 = size_binop (PLUS_EXPR,
+ DECL_VINDEX (TREE_OPERAND (op1, 0)),
+ integer_one_node);
+ op1 = integer_zero_node;
+ delta21 = CLASSTYPE_VFIELD (TYPE_METHOD_BASETYPE (TREE_TYPE (type1)));
+ delta21 = DECL_FIELD_BITPOS (delta21);
+ delta21 = size_binop (FLOOR_DIV_EXPR, delta21, size_int (BITS_PER_UNIT));
+ }
+ else
+ index1 = integer_neg_one_node;
+ {
+ tree nop1 = build1 (NOP_EXPR, TYPE_PTRMEMFUNC_FN_TYPE (type0), op1);
+ TREE_CONSTANT (nop1) = TREE_CONSTANT (op1);
+ op1 = nop1;
+ }
+ e1 = build_binary_op (EQ_EXPR, index0, index1, 1);
+ e2 = build_binary_op (NE_EXPR, index1, integer_neg_one_node, 1);
+ e2 = build_binary_op (TRUTH_ANDIF_EXPR, e2, build_binary_op (EQ_EXPR, delta20, delta21, 1), 1);
+ e3 = build_binary_op (EQ_EXPR, pfn0, op1, 1);
+ e2 = build_binary_op (TRUTH_ORIF_EXPR, e2, e3, 1);
+ e2 = build_binary_op (TRUTH_ANDIF_EXPR, e1, e2, 1);
+ if (code == EQ_EXPR)
+ return e2;
+ return build_binary_op (EQ_EXPR, e2, integer_zero_node, 1);
+ }
+ else if (TYPE_PTRMEMFUNC_P (type1)
+ && TYPE_PTRMEMFUNC_FN_TYPE (type1) == type0)
+ {
+ return build_binary_op (code, op1, op0, 1);
+ }
+ else
+ /* If args are not valid, clear out RESULT_TYPE
+ to cause an error message later. */
+ result_type = 0;
+ break;
+
+ case MAX_EXPR:
+ case MIN_EXPR:
+ if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE)
+ && (code1 == INTEGER_TYPE || code1 == REAL_TYPE))
+ shorten = 1;
+ else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE)
+ {
+ if (! comp_target_types (type0, type1, 1))
+ cp_pedwarn ("comparison of distinct pointer types `%T' and `%T' lacks a cast",
+ type0, type1);
+ else if ((TYPE_SIZE (TREE_TYPE (type0)) != 0)
+ != (TYPE_SIZE (TREE_TYPE (type1)) != 0))
+ cp_pedwarn ("comparison of %scomplete and %scomplete pointers",
+ TYPE_SIZE (TREE_TYPE (type0)) == 0 ? "in" : "",
+ TYPE_SIZE (TREE_TYPE (type1)) == 0 ? "in" : "",
+ type0, type1);
+ else if (pedantic
+ && TREE_CODE (TREE_TYPE (type0)) == FUNCTION_TYPE)
+ pedwarn ("ANSI C++ forbids ordered comparisons of pointers to functions");
+ result_type = common_type (type0, type1);
+ }
+ break;
+
+ case LE_EXPR:
+ case GE_EXPR:
+ case LT_EXPR:
+ case GT_EXPR:
+ if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE)
+ && (code1 == INTEGER_TYPE || code1 == REAL_TYPE))
+ short_compare = 1;
+ else if (code0 == POINTER_TYPE && code1 == POINTER_TYPE)
+ {
+ if (! comp_target_types (type0, type1, 1))
+ cp_pedwarn ("comparison of distinct pointer types `%T' and `%T' lacks a cast",
+ type0, type1);
+ else if ((TYPE_SIZE (TREE_TYPE (type0)) != 0)
+ != (TYPE_SIZE (TREE_TYPE (type1)) != 0))
+ cp_pedwarn ("comparison of %scomplete and %scomplete pointers",
+ TYPE_SIZE (TREE_TYPE (type0)) == 0 ? "in" : "",
+ TYPE_SIZE (TREE_TYPE (type1)) == 0 ? "in" : "",
+ type0, type1);
+ else if (pedantic
+ && TREE_CODE (TREE_TYPE (type0)) == FUNCTION_TYPE)
+ pedwarn ("ANSI C++ forbids ordered comparisons of pointers to functions");
+ }
+ else if (code0 == POINTER_TYPE && TREE_CODE (op1) == INTEGER_CST
+ && integer_zerop (op1))
+ {
+ op1 = null_pointer_node;
+ if (pedantic)
+ pedwarn ("ordered comparison of pointer with integer zero");
+ }
+ else if (code1 == POINTER_TYPE && TREE_CODE (op0) == INTEGER_CST
+ && integer_zerop (op0))
+ {
+ op0 = null_pointer_node;
+ if (pedantic)
+ pedwarn ("ANSI C++ forbids ordered comparison of pointer with integer zero");
+ }
+ else if (code0 == POINTER_TYPE && code1 == INTEGER_TYPE)
+ {
+ if (pedantic)
+ pedwarn ("ANSI C++ forbids comparison between pointer and integer");
+ else if (! flag_traditional)
+ warning ("comparison between pointer and integer");
+ op1 = convert (TREE_TYPE (op0), op1);
+ }
+ else if (code0 == INTEGER_TYPE && code1 == POINTER_TYPE)
+ {
+ if (pedantic)
+ pedwarn ("ANSI C++ forbids comparison between pointer and integer");
+ else if (! flag_traditional)
+ warning ("comparison between pointer and integer");
+ op0 = convert (TREE_TYPE (op1), op0);
+ }
+ result_type = bool_type_node;
+ converted = 1;
+ break;
+ }
+
+ if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE)
+ && (code1 == INTEGER_TYPE || code1 == REAL_TYPE))
+ {
+ if (shorten || common || short_compare)
+ result_type = common_type (type0, type1);
+
+ /* For certain operations (which identify themselves by shorten != 0)
+ if both args were extended from the same smaller type,
+ do the arithmetic in that type and then extend.
+
+ shorten !=0 and !=1 indicates a bitwise operation.
+ For them, this optimization is safe only if
+ both args are zero-extended or both are sign-extended.
+ Otherwise, we might change the result.
+ Eg, (short)-1 | (unsigned short)-1 is (int)-1
+ but calculated in (unsigned short) it would be (unsigned short)-1. */
+
+ if (shorten)
+ {
+ int unsigned0, unsigned1;
+ tree arg0 = get_narrower (op0, &unsigned0);
+ tree arg1 = get_narrower (op1, &unsigned1);
+ /* UNS is 1 if the operation to be done is an unsigned one. */
+ int uns = TREE_UNSIGNED (result_type);
+ tree type;
+
+ final_type = result_type;
+
+ /* Handle the case that OP0 does not *contain* a conversion
+ but it *requires* conversion to FINAL_TYPE. */
+
+ if (op0 == arg0 && TREE_TYPE (op0) != final_type)
+ unsigned0 = TREE_UNSIGNED (TREE_TYPE (op0));
+ if (op1 == arg1 && TREE_TYPE (op1) != final_type)
+ unsigned1 = TREE_UNSIGNED (TREE_TYPE (op1));
+
+ /* Now UNSIGNED0 is 1 if ARG0 zero-extends to FINAL_TYPE. */
+
+ /* For bitwise operations, signedness of nominal type
+ does not matter. Consider only how operands were extended. */
+ if (shorten == -1)
+ uns = unsigned0;
+
+ /* Note that in all three cases below we refrain from optimizing
+ an unsigned operation on sign-extended args.
+ That would not be valid. */
+
+ /* Both args variable: if both extended in same way
+ from same width, do it in that width.
+ Do it unsigned if args were zero-extended. */
+ if ((TYPE_PRECISION (TREE_TYPE (arg0))
+ < TYPE_PRECISION (result_type))
+ && (TYPE_PRECISION (TREE_TYPE (arg1))
+ == TYPE_PRECISION (TREE_TYPE (arg0)))
+ && unsigned0 == unsigned1
+ && (unsigned0 || !uns))
+ result_type
+ = signed_or_unsigned_type (unsigned0,
+ common_type (TREE_TYPE (arg0), TREE_TYPE (arg1)));
+ else if (TREE_CODE (arg0) == INTEGER_CST
+ && (unsigned1 || !uns)
+ && (TYPE_PRECISION (TREE_TYPE (arg1))
+ < TYPE_PRECISION (result_type))
+ && (type = signed_or_unsigned_type (unsigned1,
+ TREE_TYPE (arg1)),
+ int_fits_type_p (arg0, type)))
+ result_type = type;
+ else if (TREE_CODE (arg1) == INTEGER_CST
+ && (unsigned0 || !uns)
+ && (TYPE_PRECISION (TREE_TYPE (arg0))
+ < TYPE_PRECISION (result_type))
+ && (type = signed_or_unsigned_type (unsigned0,
+ TREE_TYPE (arg0)),
+ int_fits_type_p (arg1, type)))
+ result_type = type;
+ }
+
+ /* Shifts can be shortened if shifting right. */
+
+ if (short_shift)
+ {
+ int unsigned_arg;
+ tree arg0 = get_narrower (op0, &unsigned_arg);
+
+ final_type = result_type;
+
+ if (arg0 == op0 && final_type == TREE_TYPE (op0))
+ unsigned_arg = TREE_UNSIGNED (TREE_TYPE (op0));
+
+ if (TYPE_PRECISION (TREE_TYPE (arg0)) < TYPE_PRECISION (result_type)
+ /* If arg is sign-extended and then unsigned-shifted,
+ we can simulate this with a signed shift in arg's type
+ only if the extended result is at least twice as wide
+ as the arg. Otherwise, the shift could use up all the
+ ones made by sign-extension and bring in zeros.
+ We can't optimize that case at all, but in most machines
+ it never happens because available widths are 2**N. */
+ && (!TREE_UNSIGNED (final_type)
+ || unsigned_arg
+ || ((unsigned) 2 * TYPE_PRECISION (TREE_TYPE (arg0))
+ <= TYPE_PRECISION (result_type))))
+ {
+ /* Do an unsigned shift if the operand was zero-extended. */
+ result_type
+ = signed_or_unsigned_type (unsigned_arg,
+ TREE_TYPE (arg0));
+ /* Convert value-to-be-shifted to that type. */
+ if (TREE_TYPE (op0) != result_type)
+ op0 = convert (result_type, op0);
+ converted = 1;
+ }
+ }
+
+ /* Comparison operations are shortened too but differently.
+ They identify themselves by setting short_compare = 1. */
+
+ if (short_compare)
+ {
+ /* Don't write &op0, etc., because that would prevent op0
+ from being kept in a register.
+ Instead, make copies of the our local variables and
+ pass the copies by reference, then copy them back afterward. */
+ tree xop0 = op0, xop1 = op1, xresult_type = result_type;
+ enum tree_code xresultcode = resultcode;
+ tree val
+ = shorten_compare (&xop0, &xop1, &xresult_type, &xresultcode);
+ if (val != 0)
+ return convert (bool_type_node, val);
+ op0 = xop0, op1 = xop1, result_type = bool_type_node;
+ resultcode = xresultcode;
+ }
+
+ if (short_compare && extra_warnings)
+ {
+ int unsignedp0, unsignedp1;
+ tree primop0 = get_narrower (op0, &unsignedp0);
+ tree primop1 = get_narrower (op1, &unsignedp1);
+
+ /* Warn if signed and unsigned are being compared in a size larger
+ than their original size, as this will always fail. */
+
+ if (unsignedp0 != unsignedp1
+ && (TYPE_PRECISION (TREE_TYPE (primop0))
+ < TYPE_PRECISION (result_type))
+ && (TYPE_PRECISION (TREE_TYPE (primop1))
+ < TYPE_PRECISION (result_type)))
+ warning ("comparison between promoted unsigned and signed");
+
+ /* Warn if two unsigned values are being compared in a size
+ larger than their original size, and one (and only one) is the
+ result of a `~' operator. This comparison will always fail.
+
+ Also warn if one operand is a constant, and the constant does not
+ have all bits set that are set in the ~ operand when it is
+ extended. */
+
+ else if (TREE_CODE (primop0) == BIT_NOT_EXPR
+ ^ TREE_CODE (primop1) == BIT_NOT_EXPR)
+ {
+ if (TREE_CODE (primop0) == BIT_NOT_EXPR)
+ primop0 = get_narrower (TREE_OPERAND (op0, 0), &unsignedp0);
+ if (TREE_CODE (primop1) == BIT_NOT_EXPR)
+ primop1 = get_narrower (TREE_OPERAND (op1, 0), &unsignedp1);
+
+ if (TREE_CODE (primop0) == INTEGER_CST
+ || TREE_CODE (primop1) == INTEGER_CST)
+ {
+ tree primop;
+ HOST_WIDE_INT constant, mask;
+ int unsignedp;
+ unsigned bits;
+
+ if (TREE_CODE (primop0) == INTEGER_CST)
+ {
+ primop = primop1;
+ unsignedp = unsignedp1;
+ constant = TREE_INT_CST_LOW (primop0);
+ }
+ else
+ {
+ primop = primop0;
+ unsignedp = unsignedp0;
+ constant = TREE_INT_CST_LOW (primop1);
+ }
+
+ bits = TYPE_PRECISION (TREE_TYPE (primop));
+ if (bits < TYPE_PRECISION (result_type)
+ && bits < HOST_BITS_PER_LONG && unsignedp)
+ {
+ mask = (~ (HOST_WIDE_INT) 0) << bits;
+ if ((mask & constant) != mask)
+ warning ("comparison of promoted ~unsigned with constant");
+ }
+ }
+ else if (unsignedp0 && unsignedp1
+ && (TYPE_PRECISION (TREE_TYPE (primop0))
+ < TYPE_PRECISION (result_type))
+ && (TYPE_PRECISION (TREE_TYPE (primop1))
+ < TYPE_PRECISION (result_type)))
+ warning ("comparison of promoted ~unsigned with unsigned");
+ }
+ }
+ }
+
+ /* At this point, RESULT_TYPE must be nonzero to avoid an error message.
+ If CONVERTED is zero, both args will be converted to type RESULT_TYPE.
+ Then the expression will be built.
+ It will be given type FINAL_TYPE if that is nonzero;
+ otherwise, it will be given type RESULT_TYPE. */
+
+ if (!result_type)
+ {
+ binary_op_error (error_code);
+ return error_mark_node;
+ }
+
+ if (! converted)
+ {
+ if (TREE_TYPE (op0) != result_type)
+ op0 = convert (result_type, op0);
+ if (TREE_TYPE (op1) != result_type)
+ op1 = convert (result_type, op1);
+ }
+
+ {
+ register tree result = build (resultcode, result_type, op0, op1);
+ register tree folded;
+
+ folded = fold (result);
+ if (folded == result)
+ TREE_CONSTANT (folded) = TREE_CONSTANT (op0) & TREE_CONSTANT (op1);
+ if (final_type != 0)
+ return convert (final_type, folded);
+ return folded;
+ }
+}
+
+/* Return a tree for the sum or difference (RESULTCODE says which)
+ of pointer PTROP and integer INTOP. */
+
+static tree
+pointer_int_sum (resultcode, ptrop, intop)
+ enum tree_code resultcode;
+ register tree ptrop, intop;
+{
+ tree size_exp;
+
+ register tree result;
+ register tree folded = fold (intop);
+
+ /* The result is a pointer of the same type that is being added. */
+
+ register tree result_type = TREE_TYPE (ptrop);
+
+ if (TREE_CODE (TREE_TYPE (result_type)) == VOID_TYPE)
+ {
+ if (pedantic || warn_pointer_arith)
+ pedwarn ("ANSI C++ forbids using pointer of type `void *' in arithmetic");
+ size_exp = integer_one_node;
+ }
+ else if (TREE_CODE (TREE_TYPE (result_type)) == FUNCTION_TYPE)
+ {
+ if (pedantic || warn_pointer_arith)
+ pedwarn ("ANSI C++ forbids using pointer to a function in arithmetic");
+ size_exp = integer_one_node;
+ }
+ else if (TREE_CODE (TREE_TYPE (result_type)) == METHOD_TYPE)
+ {
+ if (pedantic || warn_pointer_arith)
+ pedwarn ("ANSI C++ forbids using pointer to a method in arithmetic");
+ size_exp = integer_one_node;
+ }
+ else if (TREE_CODE (TREE_TYPE (result_type)) == OFFSET_TYPE)
+ {
+ if (pedantic)
+ pedwarn ("ANSI C++ forbids using pointer to a member in arithmetic");
+ size_exp = integer_one_node;
+ }
+ else
+ size_exp = size_in_bytes (TREE_TYPE (result_type));
+
+ /* Needed to make OOPS V2R3 work. */
+ intop = folded;
+ if (TREE_CODE (intop) == INTEGER_CST
+ && TREE_INT_CST_LOW (intop) == 0
+ && TREE_INT_CST_HIGH (intop) == 0)
+ return ptrop;
+
+ /* If what we are about to multiply by the size of the elements
+ contains a constant term, apply distributive law
+ and multiply that constant term separately.
+ This helps produce common subexpressions. */
+
+ if ((TREE_CODE (intop) == PLUS_EXPR || TREE_CODE (intop) == MINUS_EXPR)
+ && ! TREE_CONSTANT (intop)
+ && TREE_CONSTANT (TREE_OPERAND (intop, 1))
+ && TREE_CONSTANT (size_exp))
+ {
+ enum tree_code subcode = resultcode;
+ if (TREE_CODE (intop) == MINUS_EXPR)
+ subcode = (subcode == PLUS_EXPR ? MINUS_EXPR : PLUS_EXPR);
+ ptrop = build_binary_op (subcode, ptrop, TREE_OPERAND (intop, 1), 1);
+ intop = TREE_OPERAND (intop, 0);
+ }
+
+ /* Convert the integer argument to a type the same size as a pointer
+ so the multiply won't overflow spuriously. */
+
+ if (TYPE_PRECISION (TREE_TYPE (intop)) != POINTER_SIZE)
+ intop = convert (type_for_size (POINTER_SIZE, 0), intop);
+
+ /* Replace the integer argument with a suitable product by the object size.
+ Do this multiplication as signed, then convert to the appropriate
+ pointer type (actually unsigned integral). */
+
+ intop = convert (result_type,
+ build_binary_op (MULT_EXPR, intop,
+ convert (TREE_TYPE (intop), size_exp), 1));
+
+ /* Create the sum or difference. */
+
+ result = build (resultcode, result_type, ptrop, intop);
+
+ folded = fold (result);
+ if (folded == result)
+ TREE_CONSTANT (folded) = TREE_CONSTANT (ptrop) & TREE_CONSTANT (intop);
+ return folded;
+}
+
+/* Return a tree for the difference of pointers OP0 and OP1.
+ The resulting tree has type int. */
+
+static tree
+pointer_diff (op0, op1)
+ register tree op0, op1;
+{
+ register tree result, folded;
+ tree restype = ptrdiff_type_node;
+ tree target_type = TREE_TYPE (TREE_TYPE (op0));
+
+ if (pedantic)
+ {
+ if (TREE_CODE (target_type) == VOID_TYPE)
+ pedwarn ("ANSI C++ forbids using pointer of type `void *' in subtraction");
+ if (TREE_CODE (target_type) == FUNCTION_TYPE)
+ pedwarn ("ANSI C++ forbids using pointer to a function in subtraction");
+ if (TREE_CODE (target_type) == METHOD_TYPE)
+ pedwarn ("ANSI C++ forbids using pointer to a method in subtraction");
+ if (TREE_CODE (target_type) == OFFSET_TYPE)
+ pedwarn ("ANSI C++ forbids using pointer to a member in subtraction");
+ }
+
+ /* First do the subtraction as integers;
+ then drop through to build the divide operator. */
+
+ op0 = build_binary_op (MINUS_EXPR,
+ convert (restype, op0), convert (restype, op1), 1);
+
+ /* This generates an error if op1 is a pointer to an incomplete type. */
+ if (TYPE_SIZE (TREE_TYPE (TREE_TYPE (op1))) == 0)
+ error ("arithmetic on pointer to an incomplete type");
+
+ op1 = ((TREE_CODE (target_type) == VOID_TYPE
+ || TREE_CODE (target_type) == FUNCTION_TYPE
+ || TREE_CODE (target_type) == METHOD_TYPE
+ || TREE_CODE (target_type) == OFFSET_TYPE)
+ ? integer_one_node
+ : size_in_bytes (target_type));
+
+ /* Do the division. */
+
+ result = build (EXACT_DIV_EXPR, restype, op0, convert (restype, op1));
+
+ folded = fold (result);
+ if (folded == result)
+ TREE_CONSTANT (folded) = TREE_CONSTANT (op0) & TREE_CONSTANT (op1);
+ return folded;
+}
+
+/* Handle the case of taking the address of a COMPONENT_REF.
+ Called by `build_unary_op' and `build_up_reference'.
+
+ ARG is the COMPONENT_REF whose address we want.
+ ARGTYPE is the pointer type that this address should have.
+ MSG is an error message to print if this COMPONENT_REF is not
+ addressable (such as a bitfield). */
+
+tree
+build_component_addr (arg, argtype, msg)
+ tree arg, argtype;
+ char *msg;
+{
+ tree field = TREE_OPERAND (arg, 1);
+ tree basetype = decl_type_context (field);
+ tree rval = build_unary_op (ADDR_EXPR, TREE_OPERAND (arg, 0), 0);
+
+ if (DECL_BIT_FIELD (field))
+ {
+ error (msg, IDENTIFIER_POINTER (DECL_NAME (field)));
+ return error_mark_node;
+ }
+
+ if (flag_gc)
+ cp_warning ("address of `%T::%D' taken", basetype, field);
+
+ if (TREE_CODE (field) == FIELD_DECL
+ && TYPE_USES_COMPLEX_INHERITANCE (basetype))
+ {
+ /* Can't convert directly to ARGTYPE, since that
+ may have the same pointer type as one of our
+ baseclasses. */
+ rval = build1 (NOP_EXPR, argtype,
+ convert_pointer_to (basetype, rval));
+ TREE_CONSTANT (rval) = TREE_CONSTANT (TREE_OPERAND (rval, 0));
+ }
+ else
+ /* This conversion is harmless. */
+ rval = convert (argtype, rval);
+
+ if (! integer_zerop (DECL_FIELD_BITPOS (field)))
+ {
+ tree offset = size_binop (EASY_DIV_EXPR, DECL_FIELD_BITPOS (field),
+ size_int (BITS_PER_UNIT));
+ int flag = TREE_CONSTANT (rval);
+ rval = fold (build (PLUS_EXPR, argtype,
+ rval, convert (argtype, offset)));
+ TREE_CONSTANT (rval) = flag;
+ }
+ return rval;
+}
+
+/* Construct and perhaps optimize a tree representation
+ for a unary operation. CODE, a tree_code, specifies the operation
+ and XARG is the operand. */
+
+tree
+build_x_unary_op (code, xarg)
+ enum tree_code code;
+ tree xarg;
+{
+ /* & rec, on incomplete RECORD_TYPEs is the simple opr &, not an
+ error message. */
+ if (code != ADDR_EXPR || TREE_CODE (TREE_TYPE (xarg)) != RECORD_TYPE
+ || TYPE_SIZE (TREE_TYPE (xarg)))
+ {
+ tree rval = build_opfncall (code, LOOKUP_SPECULATIVELY, xarg,
+ NULL_TREE, NULL_TREE);
+ if (rval)
+ return build_opfncall (code, LOOKUP_NORMAL, xarg,
+ NULL_TREE, NULL_TREE);
+ }
+ return build_unary_op (code, xarg, 0);
+}
+
+/* Just like truthvalue_conversion, but we want a BOOLEAN_TYPE */
+tree
+bool_truthvalue_conversion (expr)
+ tree expr;
+{
+ /* We really want to preform the optimizations in truthvalue_conversion
+ but, not this way. */
+ /* expr = truthvalue_conversion (expr); */
+ return convert (bool_type_node, expr);
+}
+
+/* C++: Must handle pointers to members.
+
+ Perhaps type instantiation should be extended to handle conversion
+ from aggregates to types we don't yet know we want? (Or are those
+ cases typically errors which should be reported?)
+
+ NOCONVERT nonzero suppresses the default promotions
+ (such as from short to int). */
+tree
+build_unary_op (code, xarg, noconvert)
+ enum tree_code code;
+ tree xarg;
+ int noconvert;
+{
+ /* No default_conversion here. It causes trouble for ADDR_EXPR. */
+ register tree arg = xarg;
+ register tree argtype = 0;
+ register enum tree_code typecode = TREE_CODE (TREE_TYPE (arg));
+ char *errstring = NULL;
+ tree val;
+ int isaggrtype;
+
+ if (typecode == ERROR_MARK)
+ return error_mark_node;
+
+ if (typecode == REFERENCE_TYPE && code != ADDR_EXPR && ! noconvert)
+ {
+ arg = convert_from_reference (arg);
+ typecode = TREE_CODE (TREE_TYPE (arg));
+ }
+
+ if (typecode == ENUMERAL_TYPE)
+ typecode = INTEGER_TYPE;
+
+ isaggrtype = IS_AGGR_TYPE_CODE (typecode);
+
+ switch (code)
+ {
+ case CONVERT_EXPR:
+ /* This is used for unary plus, because a CONVERT_EXPR
+ is enough to prevent anybody from looking inside for
+ associativity, but won't generate any code. */
+ if (!(typecode == INTEGER_TYPE || typecode == REAL_TYPE))
+ errstring = "wrong type argument to unary plus";
+ else if (!noconvert)
+ arg = default_conversion (arg);
+ break;
+
+ case NEGATE_EXPR:
+ if (isaggrtype)
+ {
+ if (!noconvert)
+ arg = default_conversion (arg);
+ else
+ {
+ cp_error ("type conversion for type `%T' not allowed",
+ TREE_TYPE (arg));
+ return error_mark_node;
+ }
+ typecode = TREE_CODE (TREE_TYPE (arg));
+ noconvert = 1;
+ }
+
+ if (!(typecode == INTEGER_TYPE || typecode == REAL_TYPE))
+ errstring = "wrong type argument to unary minus";
+ else if (!noconvert)
+ arg = default_conversion (arg);
+ break;
+
+ case BIT_NOT_EXPR:
+ if (isaggrtype)
+ {
+ if (!noconvert)
+ arg = default_conversion (arg);
+ else
+ {
+ cp_error ("type conversion for type `%T' not allowed",
+ TREE_TYPE (arg));
+ return error_mark_node;
+ }
+ typecode = TREE_CODE (TREE_TYPE (arg));
+ noconvert = 1;
+ }
+
+ if (typecode != INTEGER_TYPE)
+ errstring = "wrong type argument to bit-complement";
+ else if (!noconvert)
+ arg = default_conversion (arg);
+ break;
+
+ case ABS_EXPR:
+ if (isaggrtype)
+ {
+ if (!noconvert)
+ arg = default_conversion (arg);
+ else
+ {
+ cp_error ("type conversion for type `%T' not allowed",
+ TREE_TYPE (arg));
+ return error_mark_node;
+ }
+ typecode = TREE_CODE (TREE_TYPE (arg));
+ noconvert = 1;
+ }
+
+ if (!(typecode == INTEGER_TYPE || typecode == REAL_TYPE))
+ errstring = "wrong type argument to abs";
+ else if (!noconvert)
+ arg = default_conversion (arg);
+ break;
+
+ case TRUTH_NOT_EXPR:
+ arg = bool_truthvalue_conversion (arg);
+ val = invert_truthvalue (arg);
+ if (arg != error_mark_node)
+ return val;
+ errstring = "in argument to unary !";
+ break;
+
+ case NOP_EXPR:
+ break;
+
+ case PREINCREMENT_EXPR:
+ case POSTINCREMENT_EXPR:
+ case PREDECREMENT_EXPR:
+ case POSTDECREMENT_EXPR:
+ /* Handle complex lvalues (when permitted)
+ by reduction to simpler cases. */
+
+ val = unary_complex_lvalue (code, arg);
+ if (val != 0)
+ return val;
+
+ /* Report invalid types. */
+
+ if (isaggrtype)
+ {
+ arg = default_conversion (arg);
+ typecode = TREE_CODE (TREE_TYPE (arg));
+ }
+
+ if (typecode != POINTER_TYPE
+ && typecode != INTEGER_TYPE && typecode != REAL_TYPE)
+ {
+ if (code == PREINCREMENT_EXPR)
+ errstring ="no pre-increment operator for type";
+ else if (code == POSTINCREMENT_EXPR)
+ errstring ="no post-increment operator for type";
+ else if (code == PREDECREMENT_EXPR)
+ errstring ="no pre-decrement operator for type";
+ else
+ errstring ="no post-decrement operator for type";
+ break;
+ }
+
+ /* Report something read-only. */
+
+ if (TYPE_READONLY (TREE_TYPE (arg))
+ || TREE_READONLY (arg))
+ readonly_error (arg, ((code == PREINCREMENT_EXPR
+ || code == POSTINCREMENT_EXPR)
+ ? "increment" : "decrement"),
+ 0);
+
+ {
+ register tree inc;
+ tree result_type = TREE_TYPE (arg);
+
+ arg = get_unwidened (arg, 0);
+ argtype = TREE_TYPE (arg);
+
+ /* ARM $5.2.5 last annotation says this should be forbidden. */
+ if (TREE_CODE (argtype) == ENUMERAL_TYPE)
+ pedwarn ("ANSI C++ forbids %sing an enum",
+ (code == PREINCREMENT_EXPR || code == POSTINCREMENT_EXPR)
+ ? "increment" : "decrement");
+
+ /* Compute the increment. */
+
+ if (typecode == POINTER_TYPE)
+ {
+ enum tree_code tmp = TREE_CODE (TREE_TYPE (argtype));
+ if (TYPE_SIZE (TREE_TYPE (argtype)) == 0)
+ cp_error ("cannot %s a pointer to incomplete type `%T'",
+ ((code == PREINCREMENT_EXPR
+ || code == POSTINCREMENT_EXPR)
+ ? "increment" : "decrement"), TREE_TYPE (argtype));
+ else if (tmp == FUNCTION_TYPE || tmp == METHOD_TYPE
+ || tmp == VOID_TYPE || tmp == OFFSET_TYPE)
+ cp_pedwarn ("ANSI C++ forbids %sing a pointer of type `%T'",
+ ((code == PREINCREMENT_EXPR
+ || code == POSTINCREMENT_EXPR)
+ ? "increment" : "decrement"), argtype);
+ inc = c_sizeof_nowarn (TREE_TYPE (argtype));
+ }
+ else
+ inc = integer_one_node;
+
+ inc = convert (argtype, inc);
+
+ /* Handle incrementing a cast-expression. */
+
+ switch (TREE_CODE (arg))
+ {
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ case FLOAT_EXPR:
+ case FIX_TRUNC_EXPR:
+ case FIX_FLOOR_EXPR:
+ case FIX_ROUND_EXPR:
+ case FIX_CEIL_EXPR:
+ {
+ tree incremented, modify, value;
+ if (! lvalue_p (arg) && pedantic)
+ pedwarn ("cast to non-reference type used as lvalue");
+ arg = stabilize_reference (arg);
+ if (code == PREINCREMENT_EXPR || code == PREDECREMENT_EXPR)
+ value = arg;
+ else
+ value = save_expr (arg);
+ incremented = build (((code == PREINCREMENT_EXPR
+ || code == POSTINCREMENT_EXPR)
+ ? PLUS_EXPR : MINUS_EXPR),
+ argtype, value, inc);
+ TREE_SIDE_EFFECTS (incremented) = 1;
+ modify = build_modify_expr (arg, NOP_EXPR, incremented);
+ return build (COMPOUND_EXPR, TREE_TYPE (arg), modify, value);
+ }
+ }
+
+ if (TREE_CODE (arg) == OFFSET_REF)
+ arg = resolve_offset_ref (arg);
+
+ /* Complain about anything else that is not a true lvalue. */
+ if (!lvalue_or_else (arg, ((code == PREINCREMENT_EXPR
+ || code == POSTINCREMENT_EXPR)
+ ? "increment" : "decrement")))
+ return error_mark_node;
+
+ val = build (code, TREE_TYPE (arg), arg, inc);
+ TREE_SIDE_EFFECTS (val) = 1;
+ return convert (result_type, val);
+ }
+
+ case ADDR_EXPR:
+ /* Note that this operation never does default_conversion
+ regardless of NOCONVERT. */
+
+ if (typecode == REFERENCE_TYPE)
+ {
+ arg = build1 (CONVERT_EXPR, build_pointer_type (TREE_TYPE (TREE_TYPE (arg))), arg);
+ TREE_REFERENCE_EXPR (arg) = 1;
+ return arg;
+ }
+ else if (pedantic
+ && TREE_CODE (arg) == FUNCTION_DECL
+ && DECL_NAME (arg)
+ && DECL_CONTEXT (arg) == NULL_TREE
+ && IDENTIFIER_LENGTH (DECL_NAME (arg)) == 4
+ && IDENTIFIER_POINTER (DECL_NAME (arg))[0] == 'm'
+ && ! strcmp (IDENTIFIER_POINTER (DECL_NAME (arg)), "main"))
+ /* ARM $3.4 */
+ pedwarn ("taking address of function `main'");
+
+ /* Let &* cancel out to simplify resulting code. */
+ if (TREE_CODE (arg) == INDIRECT_REF)
+ {
+ /* We don't need to have `current_class_decl' wrapped in a
+ NON_LVALUE_EXPR node. */
+ if (arg == C_C_D)
+ return current_class_decl;
+
+ /* Keep `default_conversion' from converting if
+ ARG is of REFERENCE_TYPE. */
+ arg = TREE_OPERAND (arg, 0);
+ if (TREE_CODE (TREE_TYPE (arg)) == REFERENCE_TYPE)
+ {
+ if (TREE_CODE (arg) == VAR_DECL && DECL_INITIAL (arg)
+ && !TREE_SIDE_EFFECTS (DECL_INITIAL (arg)))
+ arg = DECL_INITIAL (arg);
+ arg = build1 (CONVERT_EXPR, build_pointer_type (TREE_TYPE (TREE_TYPE (arg))), arg);
+ TREE_REFERENCE_EXPR (arg) = 1;
+ TREE_CONSTANT (arg) = TREE_CONSTANT (TREE_OPERAND (arg, 0));
+ }
+ else if (lvalue_p (arg))
+ /* Don't let this be an lvalue. */
+ return non_lvalue (arg);
+ return arg;
+ }
+
+ /* For &x[y], return x+y */
+ if (TREE_CODE (arg) == ARRAY_REF)
+ {
+ if (mark_addressable (TREE_OPERAND (arg, 0)) == 0)
+ return error_mark_node;
+ return build_binary_op (PLUS_EXPR, TREE_OPERAND (arg, 0),
+ TREE_OPERAND (arg, 1), 1);
+ }
+
+ /* For &(++foo), we are really taking the address of the variable
+ being acted upon by the increment/decrement operator. ARM $5.3.1
+ However, according to ARM $5.2.5, we don't allow postfix ++ and
+ --, since the prefix operators return lvalues, but the postfix
+ operators do not. */
+ if (TREE_CODE (arg) == PREINCREMENT_EXPR
+ || TREE_CODE (arg) == PREDECREMENT_EXPR)
+ arg = TREE_OPERAND (arg, 0);
+
+ /* Uninstantiated types are all functions. Taking the
+ address of a function is a no-op, so just return the
+ argument. */
+
+ if (TREE_CODE (arg) == IDENTIFIER_NODE
+ && IDENTIFIER_OPNAME_P (arg))
+ {
+ my_friendly_abort (117);
+ /* We don't know the type yet, so just work around the problem.
+ We know that this will resolve to an lvalue. */
+ return build1 (ADDR_EXPR, unknown_type_node, arg);
+ }
+
+ if (TREE_CODE (arg) == TREE_LIST)
+ {
+ /* Look at methods with only this name. */
+ if (TREE_CODE (TREE_VALUE (arg)) == FUNCTION_DECL)
+ {
+ tree targ = TREE_VALUE (arg);
+
+ /* If this function is unique, or it is a unique
+ constructor, we can take its address easily. */
+ if (DECL_CHAIN (targ) == NULL_TREE
+ || (DESTRUCTOR_NAME_P (DECL_ASSEMBLER_NAME (targ))
+ && DECL_CHAIN (DECL_CHAIN (targ)) == NULL_TREE))
+ {
+ if (DECL_CHAIN (targ))
+ targ = DECL_CHAIN (targ);
+ if (DECL_CLASS_CONTEXT (targ))
+ targ = build (OFFSET_REF, TREE_TYPE (targ), C_C_D, targ);
+
+ val = unary_complex_lvalue (ADDR_EXPR, targ);
+ if (val)
+ return val;
+ }
+
+ /* This possible setting of TREE_CONSTANT is what makes it possible
+ with an initializer list to emit the entire thing in the data
+ section, rather than a run-time initialization. */
+ arg = build1 (ADDR_EXPR, unknown_type_node, arg);
+ if (staticp (targ))
+ TREE_CONSTANT (arg) = 1;
+ return arg;
+ }
+ if (TREE_CHAIN (arg) == NULL_TREE
+ && TREE_CODE (TREE_VALUE (arg)) == TREE_LIST
+ && DECL_CHAIN (TREE_VALUE (TREE_VALUE (arg))) == NULL_TREE)
+ {
+ /* Unique overloaded member function. */
+ return build_unary_op (ADDR_EXPR, TREE_VALUE (TREE_VALUE (arg)), 0);
+ }
+ return build1 (ADDR_EXPR, unknown_type_node, arg);
+ }
+
+ /* Handle complex lvalues (when permitted)
+ by reduction to simpler cases. */
+ val = unary_complex_lvalue (code, arg);
+ if (val != 0)
+ return val;
+
+ switch (TREE_CODE (arg))
+ {
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ case FLOAT_EXPR:
+ case FIX_TRUNC_EXPR:
+ case FIX_FLOOR_EXPR:
+ case FIX_ROUND_EXPR:
+ case FIX_CEIL_EXPR:
+ if (! lvalue_p (arg) && pedantic)
+ pedwarn ("taking the address of a cast to non-reference type");
+ }
+
+ /* Allow the address of a constructor if all the elements
+ are constant. */
+ if (TREE_CODE (arg) == CONSTRUCTOR && TREE_CONSTANT (arg))
+ ;
+ /* Anything not already handled and not a true memory reference
+ is an error. */
+ else if (typecode != FUNCTION_TYPE
+ && typecode != METHOD_TYPE
+ && !lvalue_or_else (arg, "unary `&'"))
+ return error_mark_node;
+
+ /* Ordinary case; arg is a COMPONENT_REF or a decl. */
+ argtype = TREE_TYPE (arg);
+ /* If the lvalue is const or volatile,
+ merge that into the type that the address will point to. */
+ if (TREE_CODE_CLASS (TREE_CODE (arg)) == 'd'
+ || TREE_CODE_CLASS (TREE_CODE (arg)) == 'r')
+ {
+ if (TREE_READONLY (arg) || TREE_THIS_VOLATILE (arg))
+ argtype = c_build_type_variant (argtype,
+ TREE_READONLY (arg),
+ TREE_THIS_VOLATILE (arg));
+ }
+
+ argtype = build_pointer_type (argtype);
+
+ if (mark_addressable (arg) == 0)
+ return error_mark_node;
+
+ {
+ tree addr;
+
+ if (TREE_CODE (arg) == COMPONENT_REF)
+ addr = build_component_addr (arg, argtype,
+ "attempt to take address of bit-field structure member `%s'");
+ else
+ addr = build1 (code, argtype, arg);
+
+ /* Address of a static or external variable or
+ function counts as a constant */
+ if (staticp (arg))
+ TREE_CONSTANT (addr) = 1;
+ return addr;
+ }
+ }
+
+ if (!errstring)
+ {
+ if (argtype == 0)
+ argtype = TREE_TYPE (arg);
+ return fold (build1 (code, argtype, arg));
+ }
+
+ error (errstring);
+ return error_mark_node;
+}
+
+/* If CONVERSIONS is a conversion expression or a nested sequence of such,
+ convert ARG with the same conversions in the same order
+ and return the result. */
+
+static tree
+convert_sequence (conversions, arg)
+ tree conversions;
+ tree arg;
+{
+ switch (TREE_CODE (conversions))
+ {
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ case FLOAT_EXPR:
+ case FIX_TRUNC_EXPR:
+ case FIX_FLOOR_EXPR:
+ case FIX_ROUND_EXPR:
+ case FIX_CEIL_EXPR:
+ return convert (TREE_TYPE (conversions),
+ convert_sequence (TREE_OPERAND (conversions, 0),
+ arg));
+
+ default:
+ return arg;
+ }
+}
+
+/* Apply unary lvalue-demanding operator CODE to the expression ARG
+ for certain kinds of expressions which are not really lvalues
+ but which we can accept as lvalues.
+
+ If ARG is not a kind of expression we can handle, return zero. */
+
+tree
+unary_complex_lvalue (code, arg)
+ enum tree_code code;
+ tree arg;
+{
+ /* Handle (a, b) used as an "lvalue". */
+ if (TREE_CODE (arg) == COMPOUND_EXPR)
+ {
+ tree real_result = build_unary_op (code, TREE_OPERAND (arg, 1), 0);
+ return build (COMPOUND_EXPR, TREE_TYPE (real_result),
+ TREE_OPERAND (arg, 0), real_result);
+ }
+
+ /* Handle (a ? b : c) used as an "lvalue". */
+ if (TREE_CODE (arg) == COND_EXPR)
+ return rationalize_conditional_expr (code, arg);
+
+ if (TREE_CODE (arg) == MODIFY_EXPR)
+ return unary_complex_lvalue
+ (code, build (COMPOUND_EXPR, TREE_TYPE (TREE_OPERAND (arg, 0)),
+ arg, TREE_OPERAND (arg, 0)));
+
+ if (code != ADDR_EXPR)
+ return 0;
+
+ /* Handle (a = b) used as an "lvalue" for `&'. */
+ if (TREE_CODE (arg) == MODIFY_EXPR
+ || TREE_CODE (arg) == INIT_EXPR)
+ {
+ tree real_result = build_unary_op (code, TREE_OPERAND (arg, 0), 0);
+ return build (COMPOUND_EXPR, TREE_TYPE (real_result), arg, real_result);
+ }
+
+ if (TREE_CODE (arg) == WITH_CLEANUP_EXPR)
+ {
+ tree real_result = build_unary_op (code, TREE_OPERAND (arg, 0), 0);
+ real_result = build (WITH_CLEANUP_EXPR, TREE_TYPE (real_result),
+ real_result, 0, TREE_OPERAND (arg, 2));
+ return real_result;
+ }
+
+ if (TREE_CODE (TREE_TYPE (arg)) == FUNCTION_TYPE
+ || TREE_CODE (TREE_TYPE (arg)) == METHOD_TYPE
+ || TREE_CODE (TREE_TYPE (arg)) == OFFSET_TYPE)
+ {
+ /* The representation of something of type OFFSET_TYPE
+ is really the representation of a pointer to it.
+ Here give the representation its true type. */
+ tree t;
+ tree offset;
+
+ my_friendly_assert (TREE_CODE (arg) != SCOPE_REF, 313);
+
+ if (TREE_CODE (arg) != OFFSET_REF)
+ return 0;
+
+ t = TREE_OPERAND (arg, 1);
+
+ if (TREE_CODE (t) == FUNCTION_DECL) /* Check all this code for right semantics. */
+ return build_unary_op (ADDR_EXPR, t, 0);
+ if (TREE_CODE (t) == VAR_DECL)
+ return build_unary_op (ADDR_EXPR, t, 0);
+ else
+ {
+ /* Can't build a pointer to member if the member must
+ go through virtual base classes. */
+ if (virtual_member (DECL_FIELD_CONTEXT (t),
+ CLASSTYPE_VBASECLASSES (TREE_TYPE (TREE_OPERAND (arg, 0)))))
+ {
+ sorry ("pointer to member via virtual baseclass");
+ return error_mark_node;
+ }
+
+ if (TREE_OPERAND (arg, 0)
+ && (TREE_CODE (TREE_OPERAND (arg, 0)) != NOP_EXPR
+ || TREE_OPERAND (TREE_OPERAND (arg, 0), 0) != error_mark_node))
+ {
+ /* Don't know if this should return address to just
+ _DECL, or actual address resolved in this expression. */
+ sorry ("address of bound pointer-to-member expression");
+ return error_mark_node;
+ }
+
+ return convert (build_pointer_type (TREE_TYPE (arg)),
+ size_binop (EASY_DIV_EXPR,
+ DECL_FIELD_BITPOS (t),
+ size_int (BITS_PER_UNIT)));
+ }
+ }
+
+ if (TREE_CODE (arg) == OFFSET_REF)
+ {
+ tree left = TREE_OPERAND (arg, 0), left_addr;
+ tree right_addr = build_unary_op (ADDR_EXPR, TREE_OPERAND (arg, 1), 0);
+
+ if (left == 0)
+ if (current_class_decl)
+ left_addr = current_class_decl;
+ else
+ {
+ error ("no `this' for pointer to member");
+ return error_mark_node;
+ }
+ else
+ left_addr = build_unary_op (ADDR_EXPR, left, 0);
+
+ return build (PLUS_EXPR, build_pointer_type (TREE_TYPE (arg)),
+ build1 (NOP_EXPR, integer_type_node, left_addr),
+ build1 (NOP_EXPR, integer_type_node, right_addr));
+ }
+
+ /* We permit compiler to make function calls returning
+ objects of aggregate type look like lvalues. */
+ {
+ tree targ = arg;
+
+ if (TREE_CODE (targ) == SAVE_EXPR)
+ targ = TREE_OPERAND (targ, 0);
+
+ if (TREE_CODE (targ) == CALL_EXPR && IS_AGGR_TYPE (TREE_TYPE (targ)))
+ {
+ if (TREE_CODE (arg) == SAVE_EXPR)
+ targ = arg;
+ else
+ targ = build_cplus_new (TREE_TYPE (arg), arg, 1);
+ return build1 (ADDR_EXPR, TYPE_POINTER_TO (TREE_TYPE (arg)), targ);
+ }
+
+ if (TREE_CODE (arg) == SAVE_EXPR && TREE_CODE (targ) == INDIRECT_REF)
+ return build (SAVE_EXPR, TYPE_POINTER_TO (TREE_TYPE (arg)),
+ TREE_OPERAND (targ, 0), current_function_decl, NULL);
+
+ /* We shouldn't wrap WITH_CLEANUP_EXPRs inside of SAVE_EXPRs, but in case
+ we do, here's how to handle it. */
+ if (TREE_CODE (arg) == SAVE_EXPR && TREE_CODE (targ) == WITH_CLEANUP_EXPR)
+ {
+#if 0
+ /* Not really a bug, but something to turn on when testing. */
+ compiler_error ("WITH_CLEANUP_EXPR wrapped in SAVE_EXPR");
+#endif
+ return unary_complex_lvalue (ADDR_EXPR, targ);
+ }
+ }
+
+ /* Don't let anything else be handled specially. */
+ return 0;
+}
+
+/* Mark EXP saying that we need to be able to take the
+ address of it; it should not be allocated in a register.
+ Value is 1 if successful.
+
+ C++: we do not allow `current_class_decl' to be addressable. */
+
+int
+mark_addressable (exp)
+ tree exp;
+{
+ register tree x = exp;
+
+ if (TREE_ADDRESSABLE (x) == 1)
+ return 1;
+
+ while (1)
+ switch (TREE_CODE (x))
+ {
+ case ADDR_EXPR:
+ case COMPONENT_REF:
+ case ARRAY_REF:
+ x = TREE_OPERAND (x, 0);
+ break;
+
+ case PARM_DECL:
+ if (x == current_class_decl)
+ {
+ error ("address of `this' not available");
+ TREE_ADDRESSABLE (x) = 1; /* so compiler doesn't die later */
+ put_var_into_stack (x);
+ return 1;
+ }
+ case VAR_DECL:
+ if (TREE_STATIC (x)
+ && TREE_READONLY (x)
+ && DECL_RTL (x) != 0
+ && ! decl_in_memory_p (x))
+ {
+ /* We thought this would make a good constant variable,
+ but we were wrong. */
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+
+ TREE_ASM_WRITTEN (x) = 0;
+ DECL_RTL (x) = 0;
+ rest_of_decl_compilation (x, 0, IDENTIFIER_LOCAL_VALUE (x) == 0, 0);
+ TREE_ADDRESSABLE (x) = 1;
+
+ pop_obstacks ();
+
+ return 1;
+ }
+ /* Caller should not be trying to mark initialized
+ constant fields addressable. */
+ my_friendly_assert (DECL_LANG_SPECIFIC (x) == 0
+ || DECL_IN_AGGR_P (x) == 0
+ || TREE_STATIC (x)
+ || DECL_EXTERNAL (x), 314);
+
+ case CONST_DECL:
+ case RESULT_DECL:
+ /* For C++, we don't warn about taking the address of a register
+ variable for CONST_DECLs; ARM p97 explicitly says it's okay. */
+ put_var_into_stack (x);
+ TREE_ADDRESSABLE (x) = 1;
+ return 1;
+
+ case FUNCTION_DECL:
+ /* We have to test both conditions here. The first may
+ be non-zero in the case of processing a default function.
+ The second may be non-zero in the case of a template function. */
+ x = DECL_MAIN_VARIANT (x);
+ if ((DECL_INLINE (x) || DECL_PENDING_INLINE_INFO (x))
+ && (DECL_CONTEXT (x) == NULL_TREE
+ || TREE_CODE_CLASS (TREE_CODE (DECL_CONTEXT (x))) != 't'
+ || ! CLASSTYPE_INTERFACE_ONLY (DECL_CONTEXT (x))))
+ {
+ mark_inline_for_output (x);
+ if (x == current_function_decl)
+ DECL_EXTERNAL (x) = 0;
+ }
+ TREE_ADDRESSABLE (x) = 1;
+ TREE_USED (x) = 1;
+ TREE_ADDRESSABLE (DECL_ASSEMBLER_NAME (x)) = 1;
+ return 1;
+
+ default:
+ return 1;
+ }
+}
+
+/* Build and return a conditional expression IFEXP ? OP1 : OP2. */
+
+tree
+build_x_conditional_expr (ifexp, op1, op2)
+ tree ifexp, op1, op2;
+{
+ tree rval = NULL_TREE;
+
+ /* See comments in `build_x_binary_op'. */
+ if (op1 != 0)
+ rval = build_opfncall (COND_EXPR, LOOKUP_SPECULATIVELY, ifexp, op1, op2);
+ if (rval)
+ return build_opfncall (COND_EXPR, LOOKUP_NORMAL, ifexp, op1, op2);
+
+ return build_conditional_expr (ifexp, op1, op2);
+}
+
+tree
+build_conditional_expr (ifexp, op1, op2)
+ tree ifexp, op1, op2;
+{
+ register tree type1;
+ register tree type2;
+ register enum tree_code code1;
+ register enum tree_code code2;
+ register tree result_type = NULL_TREE;
+ tree orig_op1 = op1, orig_op2 = op2;
+
+ /* If second operand is omitted, it is the same as the first one;
+ make sure it is calculated only once. */
+ if (op1 == 0)
+ {
+ if (pedantic)
+ pedwarn ("ANSI C++ forbids omitting the middle term of a ?: expression");
+ ifexp = op1 = save_expr (ifexp);
+ }
+
+ ifexp = bool_truthvalue_conversion (default_conversion (ifexp));
+
+ if (TREE_CODE (ifexp) == ERROR_MARK)
+ return error_mark_node;
+
+ op1 = require_instantiated_type (TREE_TYPE (op2), op1, error_mark_node);
+ if (op1 == error_mark_node)
+ return error_mark_node;
+ op2 = require_instantiated_type (TREE_TYPE (op1), op2, error_mark_node);
+ if (op2 == error_mark_node)
+ return error_mark_node;
+
+ /* C++: REFERENCE_TYPES must be dereferenced. */
+ type1 = TREE_TYPE (op1);
+ code1 = TREE_CODE (type1);
+ type2 = TREE_TYPE (op2);
+ code2 = TREE_CODE (type2);
+
+ if (code1 == REFERENCE_TYPE)
+ {
+ op1 = convert_from_reference (op1);
+ type1 = TREE_TYPE (op1);
+ code1 = TREE_CODE (type1);
+ }
+ if (code2 == REFERENCE_TYPE)
+ {
+ op2 = convert_from_reference (op2);
+ type2 = TREE_TYPE (op2);
+ code2 = TREE_CODE (type2);
+ }
+
+#if 1 /* Produces wrong result if within sizeof. Sorry. */
+ /* Don't promote the operands separately if they promote
+ the same way. Return the unpromoted type and let the combined
+ value get promoted if necessary. */
+
+ if (TYPE_MAIN_VARIANT (type1) == TYPE_MAIN_VARIANT (type2)
+ && code2 != ARRAY_TYPE
+#if 0
+ /* For C++, let the enumeral type come through. */
+ && code2 != ENUMERAL_TYPE
+#endif
+ && code2 != FUNCTION_TYPE
+ && code2 != METHOD_TYPE)
+ {
+ tree result;
+
+ if (TREE_CONSTANT (ifexp)
+ && (TREE_CODE (ifexp) == INTEGER_CST
+ || TREE_CODE (ifexp) == ADDR_EXPR))
+ return (integer_zerop (ifexp) ? op2 : op1);
+
+ if (TREE_CODE (op1) == CONST_DECL)
+ op1 = DECL_INITIAL (op1);
+ else if (TREE_READONLY_DECL_P (op1))
+ op1 = decl_constant_value (op1);
+ if (TREE_CODE (op2) == CONST_DECL)
+ op2 = DECL_INITIAL (op2);
+ else if (TREE_READONLY_DECL_P (op2))
+ op2 = decl_constant_value (op2);
+ if (type1 != type2)
+ type1 = c_build_type_variant
+ (type1,
+ TREE_READONLY (op1) || TREE_READONLY (op2),
+ TREE_THIS_VOLATILE (op1) || TREE_THIS_VOLATILE (op2));
+ /* ??? This is a kludge to deal with the fact that
+ we don't sort out integers and enums properly, yet. */
+ result = fold (build (COND_EXPR, type1, ifexp, op1, op2));
+ if (TREE_TYPE (result) != type1)
+ result = build1 (NOP_EXPR, type1, result);
+ return result;
+ }
+#endif
+
+ /* They don't match; promote them both and then try to reconcile them.
+ But don't permit mismatching enum types. */
+ if (code1 == ENUMERAL_TYPE)
+ {
+ if (code2 == ENUMERAL_TYPE)
+ {
+ message_2_types (error, "enumeral mismatch in conditional expression: `%s' vs `%s'", type1, type2);
+ return error_mark_node;
+ }
+ else if (extra_warnings && ! IS_AGGR_TYPE_CODE (code2))
+ warning ("enumeral and non-enumeral type in conditional expression");
+ }
+ else if (extra_warnings
+ && code2 == ENUMERAL_TYPE && ! IS_AGGR_TYPE_CODE (code1))
+ warning ("enumeral and non-enumeral type in conditional expression");
+
+ if (code1 != VOID_TYPE)
+ {
+ op1 = default_conversion (op1);
+ type1 = TREE_TYPE (op1);
+ code1 = TREE_CODE (type1);
+ }
+ if (code2 != VOID_TYPE)
+ {
+ op2 = default_conversion (op2);
+ type2 = TREE_TYPE (op2);
+ code2 = TREE_CODE (type2);
+ }
+
+ /* Quickly detect the usual case where op1 and op2 have the same type
+ after promotion. */
+ if (TYPE_MAIN_VARIANT (type1) == TYPE_MAIN_VARIANT (type2))
+ {
+ if (type1 == type2)
+ result_type = type1;
+ else
+ result_type = c_build_type_variant
+ (type1,
+ TREE_READONLY (op1) || TREE_READONLY (op2),
+ TREE_THIS_VOLATILE (op1) || TREE_THIS_VOLATILE (op2));
+ }
+ else if ((code1 == INTEGER_TYPE || code1 == REAL_TYPE)
+ && (code2 == INTEGER_TYPE || code2 == REAL_TYPE))
+ {
+ result_type = common_type (type1, type2);
+ }
+ else if (code1 == VOID_TYPE || code2 == VOID_TYPE)
+ {
+ if (pedantic && (code1 != VOID_TYPE || code2 != VOID_TYPE))
+ pedwarn ("ANSI C++ forbids conditional expr with only one void side");
+ result_type = void_type_node;
+ }
+ else if (code1 == POINTER_TYPE && code2 == POINTER_TYPE)
+ {
+ if (comp_target_types (type1, type2, 1))
+ result_type = common_type (type1, type2);
+ else if (integer_zerop (op1) && TREE_TYPE (type1) == void_type_node
+ && TREE_CODE (orig_op1) != NOP_EXPR)
+ result_type = qualify_type (type2, type1);
+ else if (integer_zerop (op2) && TREE_TYPE (type2) == void_type_node
+ && TREE_CODE (orig_op2) != NOP_EXPR)
+ result_type = qualify_type (type1, type2);
+ else if (TYPE_MAIN_VARIANT (TREE_TYPE (type1)) == void_type_node)
+ {
+ if (pedantic && TREE_CODE (type2) == FUNCTION_TYPE)
+ pedwarn ("ANSI C++ forbids conditional expr between `void *' and function pointer");
+ result_type = qualify_type (type1, type2);
+ }
+ else if (TYPE_MAIN_VARIANT (TREE_TYPE (type2)) == void_type_node)
+ {
+ if (pedantic && TREE_CODE (type1) == FUNCTION_TYPE)
+ pedwarn ("ANSI C++ forbids conditional expr between `void *' and function pointer");
+ result_type = qualify_type (type2, type1);
+ }
+ /* C++ */
+ else if (comptypes (type2, type1, 0))
+ result_type = type2;
+ else if (IS_AGGR_TYPE (TREE_TYPE (type1))
+ && IS_AGGR_TYPE (TREE_TYPE (type2))
+ && (result_type = common_base_type (TREE_TYPE (type1), TREE_TYPE (type2))))
+ {
+ if (result_type == error_mark_node)
+ {
+ message_2_types (error, "common base type of types `%s' and `%s' is ambiguous",
+ TREE_TYPE (type1), TREE_TYPE (type2));
+ result_type = ptr_type_node;
+ }
+ else result_type = TYPE_POINTER_TO (result_type);
+ }
+ else
+ {
+ pedwarn ("pointer type mismatch in conditional expression");
+ result_type = ptr_type_node;
+ }
+ }
+ else if (code1 == POINTER_TYPE && code2 == INTEGER_TYPE)
+ {
+ if (!integer_zerop (op2))
+ pedwarn ("pointer/integer type mismatch in conditional expression");
+ else
+ {
+ op2 = null_pointer_node;
+#if 0 /* Sez who? */
+ if (pedantic && TREE_CODE (type1) == FUNCTION_TYPE)
+ pedwarn ("ANSI C++ forbids conditional expr between 0 and function pointer");
+#endif
+ }
+ result_type = type1;
+ }
+ else if (code2 == POINTER_TYPE && code1 == INTEGER_TYPE)
+ {
+ if (!integer_zerop (op1))
+ pedwarn ("pointer/integer type mismatch in conditional expression");
+ else
+ {
+ op1 = null_pointer_node;
+#if 0 /* Sez who? */
+ if (pedantic && TREE_CODE (type2) == FUNCTION_TYPE)
+ pedwarn ("ANSI C++ forbids conditional expr between 0 and function pointer");
+#endif
+ }
+ result_type = type2;
+ }
+
+ if (!result_type)
+ {
+ /* The match does not look good. If either is
+ an aggregate value, try converting to a scalar type. */
+ if (code1 == RECORD_TYPE && code2 == RECORD_TYPE)
+ {
+ message_2_types (error, "aggregate mismatch in conditional expression: `%s' vs `%s'", type1, type2);
+ return error_mark_node;
+ }
+ if (code1 == RECORD_TYPE && TYPE_HAS_CONVERSION (type1))
+ {
+ tree tmp = build_type_conversion (CONVERT_EXPR, type2, op1, 0);
+ if (tmp == NULL_TREE)
+ {
+ cp_error ("aggregate type `%T' could not convert on lhs of `:'", type1);
+ return error_mark_node;
+ }
+ if (tmp == error_mark_node)
+ error ("ambiguous pointer conversion");
+ result_type = type2;
+ op1 = tmp;
+ }
+ else if (code2 == RECORD_TYPE && TYPE_HAS_CONVERSION (type2))
+ {
+ tree tmp = build_type_conversion (CONVERT_EXPR, type1, op2, 0);
+ if (tmp == NULL_TREE)
+ {
+ cp_error ("aggregate type `%T' could not convert on rhs of `:'", type2);
+ return error_mark_node;
+ }
+ if (tmp == error_mark_node)
+ error ("ambiguous pointer conversion");
+ result_type = type1;
+ op2 = tmp;
+ }
+ else if (flag_cond_mismatch)
+ result_type = void_type_node;
+ else
+ {
+ error ("type mismatch in conditional expression");
+ return error_mark_node;
+ }
+ }
+
+ if (result_type != TREE_TYPE (op1))
+ op1 = convert_and_check (result_type, op1);
+ if (result_type != TREE_TYPE (op2))
+ op2 = convert_and_check (result_type, op2);
+
+#if 0
+ /* XXX delete me, I've been here for years. */
+ if (IS_AGGR_TYPE_CODE (code1))
+ {
+ result_type = TREE_TYPE (op1);
+ if (TREE_CONSTANT (ifexp))
+ return (integer_zerop (ifexp) ? op2 : op1);
+
+ if (TYPE_MODE (result_type) == BLKmode)
+ {
+ register tree tempvar
+ = build_decl (VAR_DECL, NULL_TREE, result_type);
+ register tree xop1 = build_modify_expr (tempvar, NOP_EXPR, op1);
+ register tree xop2 = build_modify_expr (tempvar, NOP_EXPR, op2);
+ register tree result = fold (build (COND_EXPR, result_type,
+ ifexp, xop1, xop2));
+
+ layout_decl (tempvar, 0);
+ /* No way to handle variable-sized objects here.
+ I fear that the entire handling of BLKmode conditional exprs
+ needs to be redone. */
+ my_friendly_assert (TREE_CONSTANT (DECL_SIZE (tempvar)), 315);
+ DECL_RTL (tempvar)
+ = assign_stack_local (DECL_MODE (tempvar),
+ (TREE_INT_CST_LOW (DECL_SIZE (tempvar))
+ + BITS_PER_UNIT - 1)
+ / BITS_PER_UNIT,
+ 0);
+
+ TREE_SIDE_EFFECTS (result)
+ = TREE_SIDE_EFFECTS (ifexp) | TREE_SIDE_EFFECTS (op1)
+ | TREE_SIDE_EFFECTS (op2);
+ return build (COMPOUND_EXPR, result_type, result, tempvar);
+ }
+ }
+#endif /* 0 */
+
+ if (TREE_CONSTANT (ifexp))
+ return integer_zerop (ifexp) ? op2 : op1;
+
+ return fold (build (COND_EXPR, result_type, ifexp, op1, op2));
+}
+
+/* Handle overloading of the ',' operator when needed. Otherwise,
+ this function just builds an expression list. */
+tree
+build_x_compound_expr (list)
+ tree list;
+{
+ tree rest = TREE_CHAIN (list);
+ tree result;
+
+ if (rest == NULL_TREE)
+ return build_compound_expr (list);
+
+ result = build_opfncall (COMPOUND_EXPR, LOOKUP_NORMAL,
+ TREE_VALUE (list), TREE_VALUE (rest), NULL_TREE);
+ if (result)
+ return build_x_compound_expr (tree_cons (NULL_TREE, result, TREE_CHAIN (rest)));
+ return build_compound_expr (tree_cons (NULL_TREE, TREE_VALUE (list),
+ build_tree_list (NULL_TREE, build_x_compound_expr (rest))));
+}
+
+/* Given a list of expressions, return a compound expression
+ that performs them all and returns the value of the last of them. */
+
+tree
+build_compound_expr (list)
+ tree list;
+{
+ register tree rest;
+
+ if (TREE_READONLY_DECL_P (TREE_VALUE (list)))
+ TREE_VALUE (list) = decl_constant_value (TREE_VALUE (list));
+
+ if (TREE_CHAIN (list) == 0)
+ {
+ /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.
+ Strip such NOP_EXPRs, since LIST is used in non-lvalue context. */
+ if (TREE_CODE (list) == NOP_EXPR
+ && TREE_TYPE (list) == TREE_TYPE (TREE_OPERAND (list, 0)))
+ list = TREE_OPERAND (list, 0);
+
+ /* Convert arrays to pointers. */
+ if (TREE_CODE (TREE_TYPE (TREE_VALUE (list))) == ARRAY_TYPE)
+ return default_conversion (TREE_VALUE (list));
+ else
+ return TREE_VALUE (list);
+ }
+
+ rest = build_compound_expr (TREE_CHAIN (list));
+
+ /* When pedantic, a compound expression can be neither an lvalue
+ nor an integer constant expression. */
+ if (! TREE_SIDE_EFFECTS (TREE_VALUE (list)) && ! pedantic)
+ return rest;
+
+ return build (COMPOUND_EXPR, TREE_TYPE (rest),
+ break_out_cleanups (TREE_VALUE (list)), rest);
+}
+
+tree build_static_cast (type, expr)
+ tree type, expr;
+{
+ return build_c_cast (type, expr);
+}
+
+tree build_reinterpret_cast (type, expr)
+ tree type, expr;
+{
+ return build_c_cast (type, expr);
+}
+
+tree build_const_cast (type, expr)
+ tree type, expr;
+{
+ return build_c_cast (type, expr);
+}
+
+/* Build an expression representing a cast to type TYPE of expression EXPR. */
+
+tree
+build_c_cast (type, expr)
+ register tree type;
+ tree expr;
+{
+ register tree value = expr;
+
+ if (type == error_mark_node || expr == error_mark_node)
+ return error_mark_node;
+
+ /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.
+ Strip such NOP_EXPRs, since VALUE is being used in non-lvalue context. */
+ if (TREE_CODE (value) == NOP_EXPR
+ && TREE_TYPE (value) == TREE_TYPE (TREE_OPERAND (value, 0)))
+ value = TREE_OPERAND (value, 0);
+
+ if (TREE_TYPE (expr)
+ && TREE_CODE (TREE_TYPE (expr)) == OFFSET_TYPE
+ && TREE_CODE (type) != OFFSET_TYPE)
+ value = resolve_offset_ref (value);
+
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ /* Allow casting from T1* to T2[] because Cfront allows it.
+ NIHCL uses it. It is not valid ANSI C however, and hence, not
+ valid ANSI C++. */
+ if (TREE_CODE (TREE_TYPE (expr)) == POINTER_TYPE)
+ {
+ if (pedantic)
+ pedwarn ("ANSI C++ forbids casting to an array type");
+ type = build_pointer_type (TREE_TYPE (type));
+ }
+ else
+ {
+ error ("ANSI C++ forbids casting to an array type");
+ return error_mark_node;
+ }
+ }
+
+ if (TREE_CODE (type) == FUNCTION_TYPE
+ || TREE_CODE (type) == METHOD_TYPE)
+ {
+ cp_error ("casting to function type `%T'", type);
+ return error_mark_node;
+ }
+
+ if (IS_SIGNATURE (type))
+ {
+ error ("cast specifies signature type");
+ return error_mark_node;
+ }
+
+ /* If there's only one function in the overloaded space,
+ just take it. */
+ if (TREE_CODE (value) == TREE_LIST
+ && TREE_CHAIN (value) == NULL_TREE)
+ value = TREE_VALUE (value);
+
+ if (TREE_CODE (type) == VOID_TYPE)
+ value = build1 (NOP_EXPR, type, value);
+ else if (TREE_TYPE (value) == NULL_TREE
+ || type_unknown_p (value))
+ {
+ value = instantiate_type (type, value, 1);
+ /* Did we lose? */
+ if (value == error_mark_node)
+ return error_mark_node;
+ }
+ else
+ {
+ tree otype, ovalue;
+
+ /* Convert functions and arrays to pointers and
+ convert references to their expanded types,
+ but don't convert any other types. */
+ if (TREE_CODE (TREE_TYPE (value)) == FUNCTION_TYPE
+ || TREE_CODE (TREE_TYPE (value)) == METHOD_TYPE
+ || TREE_CODE (TREE_TYPE (value)) == ARRAY_TYPE
+ || TREE_CODE (TREE_TYPE (value)) == REFERENCE_TYPE)
+ value = default_conversion (value);
+ otype = TREE_TYPE (value);
+
+ /* Optionally warn about potentially worrisome casts. */
+
+ if (warn_cast_qual
+ && TREE_CODE (type) == POINTER_TYPE
+ && TREE_CODE (otype) == POINTER_TYPE)
+ {
+ /* For C++ we make these regular warnings, rather than
+ softening them into pedwarns. */
+ if (TYPE_VOLATILE (TREE_TYPE (otype))
+ && ! TYPE_VOLATILE (TREE_TYPE (type)))
+ warning ("cast discards `volatile' from pointer target type");
+ if (TYPE_READONLY (TREE_TYPE (otype))
+ && ! TYPE_READONLY (TREE_TYPE (type)))
+ warning ("cast discards `const' from pointer target type");
+ }
+
+ /* Warn about possible alignment problems. */
+ if (STRICT_ALIGNMENT && warn_cast_align
+ && TREE_CODE (type) == POINTER_TYPE
+ && TREE_CODE (otype) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (otype)) != VOID_TYPE
+ && TREE_CODE (TREE_TYPE (otype)) != FUNCTION_TYPE
+ && TYPE_ALIGN (TREE_TYPE (type)) > TYPE_ALIGN (TREE_TYPE (otype)))
+ warning ("cast increases required alignment of target type");
+
+#if 0
+ if (TREE_CODE (type) == INTEGER_TYPE
+ && TREE_CODE (otype) == POINTER_TYPE
+ && TYPE_PRECISION (type) != TYPE_PRECISION (otype))
+ warning ("cast from pointer to integer of different size");
+
+ if (TREE_CODE (type) == POINTER_TYPE
+ && TREE_CODE (otype) == INTEGER_TYPE
+ && TYPE_PRECISION (type) != TYPE_PRECISION (otype)
+ /* Don't warn about converting 0 to pointer,
+ provided the 0 was explicit--not cast or made by folding. */
+ && !(TREE_CODE (value) == INTEGER_CST && integer_zerop (value)))
+ warning ("cast to pointer from integer of different size");
+#endif
+
+ ovalue = value;
+ value = convert_force (type, value);
+
+ /* Ignore any integer overflow caused by the cast. */
+ if (TREE_CODE (value) == INTEGER_CST)
+ {
+ TREE_OVERFLOW (value) = TREE_OVERFLOW (ovalue);
+ TREE_CONSTANT_OVERFLOW (value) = TREE_CONSTANT_OVERFLOW (ovalue);
+ }
+ }
+
+ /* Always produce some operator for an explicit cast,
+ so we can tell (for -pedantic) that the cast is no lvalue.
+ Also, pedantically, don't let (void *) (FOO *) 0 be a null
+ pointer constant. */
+ if (value == expr
+ || (pedantic
+ && TREE_CODE (value) == INTEGER_CST
+ && TREE_CODE (expr) == INTEGER_CST
+ && TREE_CODE (TREE_TYPE (expr)) != INTEGER_TYPE))
+ {
+ tree nvalue = build1 (NOP_EXPR, type, value);
+ TREE_CONSTANT (nvalue) = TREE_CONSTANT (value);
+ return nvalue;
+ }
+
+ return value;
+}
+
+#if 0
+/* Build an assignment expression of lvalue LHS from value RHS.
+
+ In C++, if the left hand side of the assignment is a REFERENCE_TYPE,
+ that reference becomes deferenced down to it base type. */
+
+/* Return a reference to the BASE_INDEX part of EXPR. TYPE is
+ the type to which BASE_INDEX applies. */
+static tree
+get_base_ref (type, base_index, expr)
+ tree type;
+ int base_index;
+ tree expr;
+{
+ tree binfos = TYPE_BINFO_BASETYPES (type);
+ tree base_binfo = TREE_VEC_ELT (binfos, base_index);
+ tree ref;
+
+ if (TREE_CODE (expr) == ARRAY_REF
+ || ! BINFO_OFFSET_ZEROP (base_binfo)
+ || TREE_VIA_VIRTUAL (base_binfo)
+ || TYPE_MODE (type) != TYPE_MODE (BINFO_TYPE (base_binfo)))
+ {
+ tree addr = build_unary_op (ADDR_EXPR, expr, 0);
+ ref = build_indirect_ref (convert_pointer_to (base_binfo, addr),
+ NULL_PTR);
+ }
+ else
+ {
+ ref = copy_node (expr);
+ TREE_TYPE (ref) = BINFO_TYPE (base_binfo);
+ }
+ return ref;
+}
+
+/* Build an assignment expression of lvalue LHS from value RHS.
+ MODIFYCODE is the code for a binary operator that we use
+ to combine the old value of LHS with RHS to get the new value.
+ Or else MODIFYCODE is NOP_EXPR meaning do a simple assignment.
+
+ C++: If MODIFYCODE is INIT_EXPR, then leave references unbashed.
+
+ `build_modify_expr_1' implements recursive part of memberwise
+ assignment operation. */
+static tree
+build_modify_expr_1 (lhs, modifycode, rhs, basetype_path)
+ tree lhs, rhs;
+ enum tree_code modifycode;
+ tree basetype_path;
+{
+ register tree result;
+ tree newrhs = rhs;
+ tree lhstype = TREE_TYPE (lhs);
+ tree olhstype = lhstype;
+
+ /* Avoid duplicate error messages from operands that had errors. */
+ if (TREE_CODE (lhs) == ERROR_MARK || TREE_CODE (rhs) == ERROR_MARK)
+ return error_mark_node;
+
+ /* If a binary op has been requested, combine the old LHS value with the RHS
+ producing the value we should actually store into the LHS. */
+
+ if (modifycode == INIT_EXPR)
+ ;
+ else if (modifycode == NOP_EXPR)
+ {
+ /* must deal with overloading of `operator=' here. */
+ if (TREE_CODE (lhstype) == REFERENCE_TYPE)
+ lhstype = TREE_TYPE (lhstype);
+ else
+ lhstype = olhstype;
+ }
+ else
+ {
+ lhs = stabilize_reference (lhs);
+ newrhs = build_binary_op (modifycode, lhs, rhs, 1);
+ modifycode = NOP_EXPR;
+ }
+
+ /* If storing into a structure or union member,
+ it has probably been given type `int'.
+ Compute the type that would go with
+ the actual amount of storage the member occupies. */
+
+ if (TREE_CODE (lhs) == COMPONENT_REF
+ && (TREE_CODE (lhstype) == INTEGER_TYPE
+ || TREE_CODE (lhstype) == REAL_TYPE
+ || TREE_CODE (lhstype) == ENUMERAL_TYPE))
+ lhstype = TREE_TYPE (get_unwidened (lhs, 0));
+
+ /* C++: The semantics of C++ differ from those of C when an
+ assignment of an aggregate is desired. Assignment in C++ is
+ now defined as memberwise assignment of non-static members
+ and base class objects. This rule applies recursively
+ until a member of a built-in type is found.
+
+ Also, we cannot do a bit-wise copy of aggregates which
+ contain virtual function table pointers. Those
+ pointer values must be preserved through the copy.
+ However, this is handled in expand_expr, and not here.
+ This is because much better code can be generated at
+ that stage than this one. */
+ if (TREE_CODE (lhstype) == RECORD_TYPE
+ && TYPE_LANG_SPECIFIC (lhstype)
+ && TYPE_MAIN_VARIANT (lhstype) == TYPE_MAIN_VARIANT (TREE_TYPE (newrhs)))
+ {
+ register tree elt;
+ int i;
+
+ /* Perform operation on object. */
+ if (modifycode == INIT_EXPR && TYPE_HAS_INIT_REF (lhstype))
+ {
+ result = build_method_call (lhs, constructor_name_full (lhstype),
+ build_tree_list (NULL_TREE, rhs),
+ basetype_path, LOOKUP_NORMAL);
+ return build_indirect_ref (result, NULL_PTR);
+ }
+ else if (modifycode == NOP_EXPR)
+ {
+ /* `operator=' is not an inheritable operator; see 13.4.3. */
+ if (TYPE_LANG_SPECIFIC (lhstype) && TYPE_HAS_ASSIGNMENT (lhstype))
+ {
+ result = build_opfncall (MODIFY_EXPR, LOOKUP_NORMAL,
+ lhs, rhs, make_node (NOP_EXPR));
+ if (result == NULL_TREE)
+ return error_mark_node;
+ return result;
+ }
+ }
+
+ if (TYPE_USES_VIRTUAL_BASECLASSES (lhstype)
+ || (modifycode == NOP_EXPR && TYPE_GETS_ASSIGNMENT (lhstype))
+ || (modifycode == INIT_EXPR && TYPE_GETS_INIT_REF (lhstype)))
+ {
+ tree binfos = BINFO_BASETYPES (TYPE_BINFO (lhstype));
+ result = NULL_TREE;
+
+ if (binfos != NULL_TREE)
+ /* Perform operation on each member, depth-first, left-right. */
+ for (i = 0; i <= TREE_VEC_LENGTH (binfos)-1; i++)
+ {
+ tree base_binfo = TREE_VEC_ELT (binfos, i);
+ tree base_lhs, base_rhs;
+ tree new_result;
+
+ /* Assignments from virtual baseclasses handled elsewhere. */
+ if (TREE_VIA_VIRTUAL (base_binfo))
+ continue;
+
+ base_lhs = get_base_ref (lhstype, i, lhs);
+ base_rhs = get_base_ref (lhstype, i, newrhs);
+
+ BINFO_INHERITANCE_CHAIN (base_binfo) = basetype_path;
+ new_result
+ = build_modify_expr_1 (base_lhs, modifycode, base_rhs,
+ base_binfo);
+
+ /* We either get back a compound stmt, or a simple one. */
+ if (new_result && TREE_CODE (new_result) == TREE_LIST)
+ new_result = build_compound_expr (new_result);
+ result = tree_cons (NULL_TREE, new_result, result);
+ }
+
+ for (elt = TYPE_FIELDS (lhstype); elt; elt = TREE_CHAIN (elt))
+ {
+ tree vbases = NULL_TREE;
+ tree elt_lhs, elt_rhs;
+
+ if (TREE_CODE (elt) != FIELD_DECL)
+ continue;
+ if (DECL_NAME (elt)
+ && (VFIELD_NAME_P (DECL_NAME (elt))
+ || VBASE_NAME_P (DECL_NAME (elt))))
+ continue;
+
+ if (TREE_READONLY (elt)
+ || TREE_CODE (TREE_TYPE (elt)) == REFERENCE_TYPE)
+ {
+ cp_error ("cannot generate default `%T::operator ='",
+ lhstype);
+ if (TREE_CODE (TREE_TYPE (elt)) == REFERENCE_TYPE)
+ cp_error_at ("because member `%#D' is a reference", elt);
+ else
+ cp_error_at ("because member `%#D' is const", elt);
+
+ return error_mark_node;
+ }
+
+ if (IS_AGGR_TYPE (TREE_TYPE (elt))
+ && TYPE_LANG_SPECIFIC (TREE_TYPE (elt)))
+ vbases = CLASSTYPE_VBASECLASSES (TREE_TYPE (elt));
+
+ elt_lhs = build (COMPONENT_REF, TREE_TYPE (elt), lhs, elt);
+ elt_rhs = build (COMPONENT_REF, TREE_TYPE (elt), newrhs, elt);
+ /* It is not always safe to go through `build_modify_expr_1'
+ when performing element-wise copying. This is because
+ an element may be of ARRAY_TYPE, which will not
+ be properly copied as a naked element. */
+ if (TREE_CODE (TREE_TYPE (elt)) == RECORD_TYPE
+ && TYPE_LANG_SPECIFIC (TREE_TYPE (elt)))
+ basetype_path = TYPE_BINFO (TREE_TYPE (elt));
+
+ while (vbases)
+ {
+ tree elt_lhs_addr = build_unary_op (ADDR_EXPR, elt_lhs, 0);
+ tree elt_rhs_addr = build_unary_op (ADDR_EXPR, elt_rhs, 0);
+
+ elt_lhs_addr = convert_pointer_to (vbases, elt_lhs_addr);
+ elt_rhs_addr = convert_pointer_to (vbases, elt_rhs_addr);
+ result
+ = tree_cons (NULL_TREE,
+ build_modify_expr_1
+ (build_indirect_ref (elt_lhs_addr, NULL_PTR),
+ modifycode,
+ build_indirect_ref (elt_rhs_addr, NULL_PTR),
+ basetype_path),
+ result);
+ if (TREE_VALUE (result) == error_mark_node)
+ return error_mark_node;
+ vbases = TREE_CHAIN (vbases);
+ }
+ elt_lhs = build_modify_expr_1 (elt_lhs, modifycode, elt_rhs,
+ basetype_path);
+ result = tree_cons (NULL_TREE, elt_lhs, result);
+ }
+
+ if (result)
+ return build_compound_expr (result);
+ /* No fields to move. */
+ return integer_zero_node;
+ }
+ else
+ {
+ result = build (modifycode == NOP_EXPR ? MODIFY_EXPR : INIT_EXPR,
+ void_type_node, lhs, rhs);
+ TREE_SIDE_EFFECTS (result) = 1;
+ return result;
+ }
+ }
+
+ result = build_modify_expr (lhs, modifycode, newrhs);
+ /* ARRAY_TYPEs cannot be converted to anything meaningful,
+ and leaving it there screws up `build_compound_expr' when
+ it tries to defaultly convert everything. */
+ if (TREE_CODE (TREE_TYPE (result)) == ARRAY_TYPE)
+ TREE_TYPE (result) = void_type_node;
+ return result;
+}
+#endif
+
+/* Taken from expr.c:
+ Subroutine of expand_expr:
+ record the non-copied parts (LIST) of an expr (LHS), and return a list
+ which specifies the initial values of these parts. */
+
+static tree
+init_noncopied_parts (lhs, list)
+ tree lhs;
+ tree list;
+{
+ tree tail;
+ tree parts = 0;
+
+ for (tail = list; tail; tail = TREE_CHAIN (tail))
+ if (TREE_CODE (TREE_VALUE (tail)) == TREE_LIST)
+ parts = chainon (parts, init_noncopied_parts (lhs, TREE_VALUE (tail)));
+ else
+ {
+ tree part = TREE_VALUE (tail);
+ tree part_type = TREE_TYPE (part);
+ tree to_be_initialized = build (COMPONENT_REF, part_type, lhs, part);
+ parts = tree_cons (TREE_PURPOSE (tail), to_be_initialized, parts);
+ }
+ return parts;
+}
+
+/* Build an assignment expression of lvalue LHS from value RHS.
+ MODIFYCODE is the code for a binary operator that we use
+ to combine the old value of LHS with RHS to get the new value.
+ Or else MODIFYCODE is NOP_EXPR meaning do a simple assignment.
+
+ C++: If MODIFYCODE is INIT_EXPR, then leave references unbashed.
+*/
+tree
+build_modify_expr (lhs, modifycode, rhs)
+ tree lhs;
+ enum tree_code modifycode;
+ tree rhs;
+{
+ register tree result;
+ tree newrhs = rhs;
+ tree lhstype = TREE_TYPE (lhs);
+ tree olhstype = lhstype;
+ tree olhs = lhs;
+
+ /* Types that aren't fully specified cannot be used in assignments. */
+ lhs = require_complete_type (lhs);
+
+ /* Avoid duplicate error messages from operands that had errors. */
+ if (TREE_CODE (lhs) == ERROR_MARK || TREE_CODE (rhs) == ERROR_MARK)
+ return error_mark_node;
+
+ /* Decide early if we are going to protect RHS from GC
+ before assigning it to LHS. */
+ if (type_needs_gc_entry (TREE_TYPE (rhs))
+ && ! value_safe_from_gc (lhs, rhs))
+ rhs = protect_value_from_gc (lhs, rhs);
+
+ newrhs = rhs;
+
+ /* Handle assignment to signature pointers/refs. */
+
+ if (TYPE_LANG_SPECIFIC (lhstype) &&
+ (IS_SIGNATURE_POINTER (lhstype) || IS_SIGNATURE_REFERENCE (lhstype)))
+ {
+ return build_signature_pointer_constructor (lhs, rhs);
+ }
+
+ /* Handle control structure constructs used as "lvalues". */
+
+ switch (TREE_CODE (lhs))
+ {
+ /* Handle --foo = 5; as these are valid constructs in C++ */
+ case PREDECREMENT_EXPR:
+ case PREINCREMENT_EXPR:
+ if (TREE_SIDE_EFFECTS (TREE_OPERAND (lhs, 0)))
+ lhs = build (TREE_CODE (lhs), TREE_TYPE (lhs),
+ stabilize_reference (TREE_OPERAND (lhs, 0)));
+ return build (COMPOUND_EXPR, lhstype,
+ lhs,
+ build_modify_expr (TREE_OPERAND (lhs, 0),
+ modifycode, rhs));
+
+ /* Handle (a, b) used as an "lvalue". */
+ case COMPOUND_EXPR:
+ newrhs = build_modify_expr (TREE_OPERAND (lhs, 1),
+ modifycode, rhs);
+ if (TREE_CODE (newrhs) == ERROR_MARK)
+ return error_mark_node;
+ return build (COMPOUND_EXPR, lhstype,
+ TREE_OPERAND (lhs, 0), newrhs);
+
+ case MODIFY_EXPR:
+ newrhs = build_modify_expr (TREE_OPERAND (lhs, 0), modifycode, rhs);
+ if (TREE_CODE (newrhs) == ERROR_MARK)
+ return error_mark_node;
+ return build (COMPOUND_EXPR, lhstype, lhs, newrhs);
+
+ /* Handle (a ? b : c) used as an "lvalue". */
+ case COND_EXPR:
+ rhs = save_expr (rhs);
+ {
+ /* Produce (a ? (b = rhs) : (c = rhs))
+ except that the RHS goes through a save-expr
+ so the code to compute it is only emitted once. */
+ tree cond
+ = build_conditional_expr (TREE_OPERAND (lhs, 0),
+ build_modify_expr (TREE_OPERAND (lhs, 1),
+ modifycode, rhs),
+ build_modify_expr (TREE_OPERAND (lhs, 2),
+ modifycode, rhs));
+ if (TREE_CODE (cond) == ERROR_MARK)
+ return cond;
+ /* Make sure the code to compute the rhs comes out
+ before the split. */
+ return build (COMPOUND_EXPR, TREE_TYPE (lhs),
+ /* Case to void to suppress warning
+ from warn_if_unused_value. */
+ convert (void_type_node, rhs), cond);
+ }
+ }
+
+ if (TREE_CODE (lhs) == OFFSET_REF)
+ {
+ if (TREE_OPERAND (lhs, 0) == NULL_TREE)
+ {
+ /* Static class member? */
+ tree member = TREE_OPERAND (lhs, 1);
+ if (TREE_CODE (member) == VAR_DECL)
+ lhs = member;
+ else
+ {
+ compiler_error ("invalid static class member");
+ return error_mark_node;
+ }
+ }
+ else
+ lhs = resolve_offset_ref (lhs);
+
+ olhstype = lhstype = TREE_TYPE (lhs);
+ }
+
+ if (TREE_CODE (lhstype) == REFERENCE_TYPE
+ && modifycode != INIT_EXPR)
+ {
+ lhs = convert_from_reference (lhs);
+ olhstype = lhstype = TREE_TYPE (lhs);
+ }
+
+ /* If a binary op has been requested, combine the old LHS value with the RHS
+ producing the value we should actually store into the LHS. */
+
+ if (modifycode == INIT_EXPR)
+ {
+ if (TYPE_LANG_SPECIFIC (lhstype) && TYPE_HAS_CONSTRUCTOR (lhstype))
+ {
+ result = build_method_call (lhs, constructor_name_full (lhstype),
+ build_tree_list (NULL_TREE, rhs),
+ NULL_TREE, LOOKUP_NORMAL);
+ if (result == NULL_TREE)
+ return error_mark_node;
+ return result;
+ }
+ }
+ else if (modifycode == NOP_EXPR)
+ {
+#if 1
+ /* `operator=' is not an inheritable operator. */
+ if (TYPE_LANG_SPECIFIC (lhstype) && TYPE_HAS_ASSIGNMENT (lhstype))
+ {
+ result = build_opfncall (MODIFY_EXPR, LOOKUP_NORMAL,
+ lhs, rhs, make_node (NOP_EXPR));
+ if (result == NULL_TREE)
+ return error_mark_node;
+ return result;
+ }
+#else
+ /* Treat `operator=' as an inheritable operator. */
+ if (TYPE_LANG_SPECIFIC (lhstype) && TYPE_GETS_ASSIGNMENT (lhstype))
+ {
+ tree orig_lhstype = lhstype;
+ while (! TYPE_HAS_ASSIGNMENT (lhstype))
+ {
+ int i, n_baseclasses = CLASSTYPE_N_BASECLASSES (lhstype);
+ tree basetype = NULL_TREE;
+ for (i = 0; i < n_baseclasses; i++)
+ if (TYPE_GETS_ASSIGNMENT (TYPE_BINFO_BASETYPE (lhstype, i)))
+ {
+ if (basetype != NULL_TREE)
+ {
+ message_2_types (error, "base classes `%s' and `%s' both have operator ='",
+ basetype,
+ TYPE_BINFO_BASETYPE (lhstype, i));
+ return error_mark_node;
+ }
+ basetype = TYPE_BINFO_BASETYPE (lhstype, i);
+ }
+ lhstype = basetype;
+ }
+ if (orig_lhstype != lhstype)
+ {
+ lhs = build_indirect_ref (convert_pointer_to (lhstype,
+ build_unary_op (ADDR_EXPR, lhs, 0)), NULL_PTR);
+ if (lhs == error_mark_node)
+ {
+ cp_error ("conversion to private basetype `%T'", lhstype);
+ return error_mark_node;
+ }
+ }
+ result = build_opfncall (MODIFY_EXPR, LOOKUP_NORMAL,
+ lhs, rhs, make_node (NOP_EXPR));
+ if (result == NULL_TREE)
+ return error_mark_node;
+ return result;
+ }
+#endif
+ lhstype = olhstype;
+ }
+ else if (PROMOTES_TO_AGGR_TYPE (lhstype, REFERENCE_TYPE))
+ {
+ /* This case must convert to some sort of lvalue that
+ can participate in an op= operation. */
+ tree lhs_tmp = lhs;
+ tree rhs_tmp = rhs;
+ if (build_default_binary_type_conversion (modifycode, &lhs_tmp, &rhs_tmp))
+ {
+ lhs = stabilize_reference (lhs_tmp);
+ /* Forget is was ever anything else. */
+ olhstype = lhstype = TREE_TYPE (lhs);
+ newrhs = build_binary_op (modifycode, lhs, rhs_tmp, 1);
+ }
+ else
+ return error_mark_node;
+ }
+ else
+ {
+ lhs = stabilize_reference (lhs);
+ newrhs = build_binary_op (modifycode, lhs, rhs, 1);
+ }
+
+ /* Handle a cast used as an "lvalue".
+ We have already performed any binary operator using the value as cast.
+ Now convert the result to the cast type of the lhs,
+ and then true type of the lhs and store it there;
+ then convert result back to the cast type to be the value
+ of the assignment. */
+
+ switch (TREE_CODE (lhs))
+ {
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ case FLOAT_EXPR:
+ case FIX_TRUNC_EXPR:
+ case FIX_FLOOR_EXPR:
+ case FIX_ROUND_EXPR:
+ case FIX_CEIL_EXPR:
+ if (TREE_CODE (TREE_TYPE (newrhs)) == ARRAY_TYPE
+ || TREE_CODE (TREE_TYPE (newrhs)) == FUNCTION_TYPE
+ || TREE_CODE (TREE_TYPE (newrhs)) == METHOD_TYPE
+ || TREE_CODE (TREE_TYPE (newrhs)) == OFFSET_TYPE)
+ newrhs = default_conversion (newrhs);
+ {
+ tree inner_lhs = TREE_OPERAND (lhs, 0);
+ tree result;
+ if (! lvalue_p (lhs) && pedantic)
+ pedwarn ("cast to non-reference type used as lvalue");
+
+ result = build_modify_expr (inner_lhs, NOP_EXPR,
+ convert (TREE_TYPE (inner_lhs),
+ convert (lhstype, newrhs)));
+ if (TREE_CODE (result) == ERROR_MARK)
+ return result;
+ return convert (TREE_TYPE (lhs), result);
+ }
+ }
+
+ /* Now we have handled acceptable kinds of LHS that are not truly lvalues.
+ Reject anything strange now. */
+
+ if (!lvalue_or_else (lhs, "assignment"))
+ return error_mark_node;
+
+ GNU_xref_assign (lhs);
+
+ /* Warn about storing in something that is `const'. */
+ /* For C++, don't warn if this is initialization. */
+ if (modifycode != INIT_EXPR
+ /* For assignment to `const' signature pointer/reference fields,
+ don't warn either, we already printed a better message before. */
+ && ! (TREE_CODE (lhs) == COMPONENT_REF
+ && (IS_SIGNATURE_POINTER (TREE_TYPE (TREE_OPERAND (lhs, 0)))
+ || IS_SIGNATURE_REFERENCE (TREE_TYPE (TREE_OPERAND (lhs, 0)))))
+ && (TREE_READONLY (lhs) || TYPE_READONLY (lhstype)
+ || ((TREE_CODE (lhstype) == RECORD_TYPE
+ || TREE_CODE (lhstype) == UNION_TYPE)
+ && C_TYPE_FIELDS_READONLY (lhstype))
+ || (TREE_CODE (lhstype) == REFERENCE_TYPE
+ && TYPE_READONLY (TREE_TYPE (lhstype)))))
+ readonly_error (lhs, "assignment", 0);
+
+ /* If storing into a structure or union member,
+ it has probably been given type `int'.
+ Compute the type that would go with
+ the actual amount of storage the member occupies. */
+
+ if (TREE_CODE (lhs) == COMPONENT_REF
+ && (TREE_CODE (lhstype) == INTEGER_TYPE
+ || TREE_CODE (lhstype) == REAL_TYPE
+ || TREE_CODE (lhstype) == ENUMERAL_TYPE))
+ {
+ lhstype = TREE_TYPE (get_unwidened (lhs, 0));
+
+ /* If storing in a field that is in actuality a short or narrower
+ than one, we must store in the field in its actual type. */
+
+ if (lhstype != TREE_TYPE (lhs))
+ {
+ lhs = copy_node (lhs);
+ TREE_TYPE (lhs) = lhstype;
+ }
+ }
+
+ /* check to see if there is an assignment to `this' */
+ if (lhs == current_class_decl)
+ {
+ if (flag_this_is_variable > 0
+ && DECL_NAME (current_function_decl) != NULL_TREE
+ && current_class_name != DECL_NAME (current_function_decl))
+ warning ("assignment to `this' not in constructor or destructor");
+ current_function_just_assigned_this = 1;
+ }
+
+ /* The TREE_TYPE of RHS may be TYPE_UNKNOWN. This can happen
+ when the type of RHS is not yet known, i.e. its type
+ is inherited from LHS. */
+ rhs = require_instantiated_type (lhstype, newrhs, error_mark_node);
+ if (rhs == error_mark_node)
+ return error_mark_node;
+ newrhs = rhs;
+
+ if (modifycode != INIT_EXPR)
+ {
+ /* Make modifycode now either a NOP_EXPR or an INIT_EXPR. */
+ modifycode = NOP_EXPR;
+ /* Reference-bashing */
+ if (TREE_CODE (lhstype) == REFERENCE_TYPE)
+ {
+ tree tmp = convert_from_reference (lhs);
+ lhstype = TREE_TYPE (tmp);
+ if (TYPE_SIZE (lhstype) == 0)
+ {
+ incomplete_type_error (lhs, lhstype);
+ return error_mark_node;
+ }
+ lhs = tmp;
+ olhstype = lhstype;
+ }
+ if (TREE_CODE (TREE_TYPE (newrhs)) == REFERENCE_TYPE)
+ {
+ tree tmp = convert_from_reference (newrhs);
+ if (TYPE_SIZE (TREE_TYPE (tmp)) == 0)
+ {
+ incomplete_type_error (newrhs, TREE_TYPE (tmp));
+ return error_mark_node;
+ }
+ newrhs = tmp;
+ }
+ }
+
+ if (TREE_SIDE_EFFECTS (lhs))
+ lhs = stabilize_reference (lhs);
+ if (TREE_SIDE_EFFECTS (newrhs))
+ newrhs = stabilize_reference (newrhs);
+
+ /* C++: The semantics of C++ differ from those of C when an
+ assignment of an aggregate is desired. Assignment in C++ is
+ now defined as memberwise assignment of non-static members
+ and base class objects. This rule applies recursively
+ until a member of a built-in type is found.
+
+ Also, we cannot do a bit-wise copy of aggregates which
+ contain virtual function table pointers. Those
+ pointer values must be preserved through the copy.
+ However, this is handled in expand_expr, and not here.
+ This is because much better code can be generated at
+ that stage than this one. */
+ if (TREE_CODE (lhstype) == RECORD_TYPE
+ && ! TYPE_PTRMEMFUNC_P (lhstype)
+ && (TYPE_MAIN_VARIANT (lhstype) == TYPE_MAIN_VARIANT (TREE_TYPE (newrhs))
+ || (TREE_CODE (TREE_TYPE (newrhs)) == RECORD_TYPE
+ && UNIQUELY_DERIVED_FROM_P (lhstype, TREE_TYPE (newrhs)))))
+ {
+ /* This was decided in finish_struct. */
+ if (modifycode == INIT_EXPR)
+ cp_error ("can't generate default copy constructor for `%T'", lhstype);
+ else
+ cp_error ("can't generate default assignment operator for `%T'",
+ lhstype);
+#if 0
+ /* This is now done by generating X(X&) and operator=(X&). */
+ tree vbases = CLASSTYPE_VBASECLASSES (lhstype);
+ tree lhs_addr = build_unary_op (ADDR_EXPR, lhs, 0);
+ tree rhs_addr;
+
+ /* Memberwise assignment would cause NEWRHS to be
+ evaluated for every member that gets assigned.
+ By wrapping side-effecting exprs in a SAVE_EXPR,
+ NEWRHS will only be evaluated once. */
+ if (IS_AGGR_TYPE (TREE_TYPE (newrhs))
+ && TREE_SIDE_EFFECTS (newrhs)
+ /* This are things we don't have to save. */
+ && TREE_CODE (newrhs) != COND_EXPR
+ && TREE_CODE (newrhs) != TARGET_EXPR
+ && TREE_CODE (newrhs) != WITH_CLEANUP_EXPR)
+ /* Call `break_out_cleanups' on NEWRHS in case there are cleanups.
+ If NEWRHS is a CALL_EXPR that needs a cleanup, failure to do so
+ will result in expand_expr expanding the call without knowing
+ that it should run the cleanup. */
+ newrhs = save_expr (break_out_cleanups (newrhs));
+
+ if (TREE_CODE (newrhs) == COND_EXPR)
+ rhs_addr = rationalize_conditional_expr (ADDR_EXPR, newrhs);
+ else
+ rhs_addr = build_unary_op (ADDR_EXPR, newrhs, 0);
+
+ result = tree_cons (NULL_TREE,
+ convert (build_reference_type (lhstype), lhs),
+ NULL_TREE);
+
+ if (! comptypes (TREE_TYPE (lhs_addr), TREE_TYPE (rhs_addr), 1))
+ rhs_addr = convert_pointer_to (TREE_TYPE (TREE_TYPE (lhs_addr)), rhs_addr);
+ {
+ tree noncopied_parts = NULL_TREE;
+
+ if (TYPE_NONCOPIED_PARTS (lhstype) != 0)
+ noncopied_parts = init_noncopied_parts (lhs,
+ TYPE_NONCOPIED_PARTS (lhstype));
+ while (noncopied_parts != 0)
+ {
+ result = tree_cons (NULL_TREE,
+ build_modify_expr (convert (ptr_type_node, TREE_VALUE (noncopied_parts)),
+ NOP_EXPR,
+ TREE_PURPOSE (noncopied_parts)),
+ result);
+ noncopied_parts = TREE_CHAIN (noncopied_parts);
+ }
+ }
+ /* Once we have our hands on an address, we must change NEWRHS
+ to work from there. Otherwise we can get multiple evaluations
+ of NEWRHS. */
+ if (TREE_CODE (newrhs) != SAVE_EXPR)
+ newrhs = build_indirect_ref (rhs_addr, NULL_PTR);
+
+ while (vbases)
+ {
+ tree elt_lhs = convert_pointer_to (vbases, lhs_addr);
+ tree elt_rhs = convert_pointer_to (vbases, rhs_addr);
+ result
+ = tree_cons (NULL_TREE,
+ build_modify_expr_1 (build_indirect_ref (elt_lhs, NULL_PTR),
+ modifycode,
+ build_indirect_ref (elt_rhs, NULL_PTR),
+ TYPE_BINFO (lhstype)),
+ result);
+ if (TREE_VALUE (result) == error_mark_node)
+ return error_mark_node;
+ vbases = TREE_CHAIN (vbases);
+ }
+ result = tree_cons (NULL_TREE,
+ build_modify_expr_1 (lhs,
+ modifycode,
+ newrhs,
+ TYPE_BINFO (lhstype)),
+ result);
+ return build_compound_expr (result);
+#endif
+ }
+
+ /* Convert new value to destination type. */
+
+ if (TREE_CODE (lhstype) == ARRAY_TYPE)
+ {
+ /* Allow array assignment in compiler-generated code. */
+ if ((pedantic || flag_ansi)
+ && ! DECL_ARTIFICIAL (current_function_decl))
+ pedwarn ("ANSI C++ forbids assignment between arrays");
+
+ /* Have to wrap this in RTL_EXPR for two cases:
+ in base or member initialization and if we
+ are a branch of a ?: operator. Since we
+ can't easily know the latter, just do it always. */
+
+ result = make_node (RTL_EXPR);
+
+ TREE_TYPE (result) = void_type_node;
+ do_pending_stack_adjust ();
+ start_sequence_for_rtl_expr (result);
+
+ /* As a matter of principle, `start_sequence' should do this. */
+ emit_note (0, -1);
+
+ expand_vec_init (lhs, lhs, array_type_nelts (lhstype), newrhs,
+ 1 + (modifycode != INIT_EXPR));
+
+ do_pending_stack_adjust ();
+
+ TREE_SIDE_EFFECTS (result) = 1;
+ RTL_EXPR_SEQUENCE (result) = get_insns ();
+ RTL_EXPR_RTL (result) = const0_rtx;
+ end_sequence ();
+ return result;
+ }
+
+ if (modifycode == INIT_EXPR)
+ {
+ newrhs = convert_for_initialization (lhs, lhstype, newrhs, LOOKUP_NORMAL,
+ "assignment", NULL_TREE, 0);
+ if (lhs == DECL_RESULT (current_function_decl))
+ {
+ if (DECL_INITIAL (lhs))
+ warning ("return value from function receives multiple initializations");
+ DECL_INITIAL (lhs) = newrhs;
+ }
+ }
+ else
+ {
+ if (IS_AGGR_TYPE (lhstype))
+ {
+ if (result = build_opfncall (MODIFY_EXPR,
+ LOOKUP_NORMAL, lhs, newrhs,
+ make_node (NOP_EXPR)))
+ return result;
+ }
+ /* Avoid warnings on enum bit fields. */
+ if (TREE_CODE (olhstype) == ENUMERAL_TYPE
+ && TREE_CODE (lhstype) == INTEGER_TYPE)
+ {
+ newrhs = convert_for_assignment (olhstype, newrhs, "assignment",
+ NULL_TREE, 0);
+ newrhs = convert_force (lhstype, newrhs);
+ }
+ else
+ newrhs = convert_for_assignment (lhstype, newrhs, "assignment",
+ NULL_TREE, 0);
+ if (flag_elide_constructors == 0
+ && TREE_CODE (newrhs) == CALL_EXPR
+ && TREE_ADDRESSABLE (lhstype))
+ {
+ /* Can't initialized directly from a CALL_EXPR, since
+ we don't know about what doesn't alias what. */
+
+ tree temp = get_temp_name (lhstype, 0);
+ newrhs = build (COMPOUND_EXPR, lhstype,
+ build_modify_expr (temp, INIT_EXPR, newrhs),
+ temp);
+ }
+ }
+
+ if (TREE_CODE (newrhs) == ERROR_MARK)
+ return error_mark_node;
+
+ if (TREE_CODE (newrhs) == COND_EXPR)
+ {
+ tree lhs1;
+ tree cond = TREE_OPERAND (newrhs, 0);
+
+ if (TREE_SIDE_EFFECTS (lhs))
+ cond = build_compound_expr (tree_cons
+ (NULL_TREE, lhs,
+ build_tree_list (NULL_TREE, cond)));
+
+ /* Cannot have two identical lhs on this one tree (result) as preexpand
+ calls will rip them out and fill in RTL for them, but when the
+ rtl is generated, the calls will only be in the first side of the
+ condition, not on both, or before the conditional jump! (mrs) */
+ lhs1 = break_out_calls (lhs);
+
+ if (lhs == lhs1)
+ /* If there's no change, the COND_EXPR behaves like any other rhs. */
+ result = build (modifycode == NOP_EXPR ? MODIFY_EXPR : INIT_EXPR,
+ lhstype, lhs, newrhs);
+ else
+ {
+ tree result_type = TREE_TYPE (newrhs);
+ /* We have to convert each arm to the proper type because the
+ types may have been munged by constant folding. */
+ result
+ = build (COND_EXPR, result_type, cond,
+ build_modify_expr (lhs, modifycode,
+ convert (result_type,
+ TREE_OPERAND (newrhs, 1))),
+ build_modify_expr (lhs1, modifycode,
+ convert (result_type,
+ TREE_OPERAND (newrhs, 2))));
+ }
+ }
+ else if (modifycode != INIT_EXPR && TREE_CODE (newrhs) == WITH_CLEANUP_EXPR)
+ {
+ tree cleanup = TREE_OPERAND (newrhs, 2);
+ tree slot;
+
+ /* Finish up by running cleanups and having the "value" of the lhs. */
+ tree exprlist = tree_cons (NULL_TREE, cleanup,
+ build_tree_list (NULL_TREE, lhs));
+ newrhs = TREE_OPERAND (newrhs, 0);
+ if (TREE_CODE (newrhs) == TARGET_EXPR)
+ slot = TREE_OPERAND (newrhs, 0);
+ else if (TREE_CODE (newrhs) == ADDR_EXPR)
+ {
+ /* Bad but legal. */
+ slot = newrhs;
+ warning ("address taken of temporary object");
+ }
+ else
+ my_friendly_abort (118);
+
+ /* Copy the value computed in SLOT into LHS. */
+ exprlist = tree_cons (NULL_TREE,
+ build_modify_expr (lhs, modifycode, slot),
+ exprlist);
+ /* Evaluate the expression that needs CLEANUP. This will
+ compute the value into SLOT. */
+ exprlist = tree_cons (NULL_TREE, newrhs, exprlist);
+ result = convert (lhstype, build_compound_expr (exprlist));
+ }
+ else
+ result = build (modifycode == NOP_EXPR ? MODIFY_EXPR : INIT_EXPR,
+ lhstype, lhs, newrhs);
+ TREE_SIDE_EFFECTS (result) = 1;
+
+ /* If we got the LHS in a different type for storing in,
+ convert the result back to the nominal type of LHS
+ so that the value we return always has the same type
+ as the LHS argument. */
+
+ if (olhstype == TREE_TYPE (result))
+ return result;
+ /* Avoid warnings converting integral types back into enums
+ for enum bit fields. */
+ if (TREE_CODE (TREE_TYPE (result)) == INTEGER_TYPE
+ && TREE_CODE (olhstype) == ENUMERAL_TYPE)
+ {
+ result = build (COMPOUND_EXPR, olhstype, result, olhs);
+ TREE_NO_UNUSED_WARNING (result) = 1;
+ return result;
+ }
+ return convert_for_assignment (olhstype, result, "assignment",
+ NULL_TREE, 0);
+}
+
+
+/* Return 0 if EXP is not a valid lvalue in this language
+ even though `lvalue_or_else' would accept it. */
+
+int
+language_lvalue_valid (exp)
+ tree exp;
+{
+ return 1;
+}
+
+/* Get differnce in deltas for different pointer to member function
+ types. Return inetger_zero_node, if FROM cannot be converted to a
+ TO type. If FORCE is true, then allow reverse conversions as well. */
+static tree
+get_delta_difference (from, to, force)
+ tree from, to;
+ int force;
+{
+ tree delta = integer_zero_node;
+ tree binfo;
+
+ if (to == from)
+ return delta;
+
+ /* Should get_base_distance here, so we can check if any thing along the
+ path is virtual, and we need to make sure we stay
+ inside the real binfos when going through virtual bases.
+ Maybe we should replace virtual bases with
+ binfo_member (...CLASSTYPE_VBASECLASSES...)... (mrs) */
+ binfo = get_binfo (from, to, 1);
+ if (binfo == error_mark_node)
+ {
+ error (" in pointer to member function conversion");
+ return delta;
+ }
+ if (binfo == 0)
+ {
+ if (!force)
+ {
+ error_not_base_type (from, to);
+ error (" in pointer to member function conversion");
+ return delta;
+ }
+ binfo = get_binfo (to, from, 1);
+ if (binfo == error_mark_node)
+ {
+ error (" in pointer to member function conversion");
+ return delta;
+ }
+ if (binfo == 0)
+ {
+ error ("cannot convert pointer to member of type %T to unrelated pointer to member of type %T", from, to);
+ return delta;
+ }
+ if (TREE_VIA_VIRTUAL (binfo))
+ {
+ warning ("pointer to member conversion to virtual base class will only work if your very careful");
+ }
+ return fold (size_binop (MINUS_EXPR,
+ integer_zero_node,
+ BINFO_OFFSET (binfo)));
+ }
+ if (TREE_VIA_VIRTUAL (binfo))
+ {
+ warning ("pointer to member conversion from virtual base class will only work if your very careful");
+ }
+ return BINFO_OFFSET (binfo);
+}
+
+/* Build a constructor for a pointer to member function. It can be
+ used to initialize global variables, local variable, or used
+ as a value in expressions. TYPE is the POINTER to METHOD_TYPE we
+ want to be.
+
+ If FORCE is non-zero, then force this conversion, even if
+ we would rather not do it. Usually set when using an explicit
+ cast.
+
+ Return error_mark_node, if something goes wrong. */
+
+tree
+build_ptrmemfunc (type, pfn, force)
+ tree type, pfn;
+ int force;
+{
+ tree index = integer_zero_node;
+ tree delta = integer_zero_node;
+ tree delta2 = integer_zero_node;
+ tree vfield_offset;
+ tree npfn;
+ tree u;
+
+ /* Handle multiple conversions of pointer to member fucntions. */
+ if (TYPE_PTRMEMFUNC_P (TREE_TYPE (pfn)))
+ {
+ tree ndelta, ndelta2, nindex;
+ /* Is is already the right type? */
+#if 0
+ /* Sorry, can't do this, the backend is too stupid. */
+ if (TYPE_METHOD_BASETYPE (TREE_TYPE (type))
+ == TYPE_METHOD_BASETYPE (TREE_TYPE (TYPE_PTRMEMFUNC_FN_TYPE (TREE_TYPE (pfn)))))
+ {
+ if (type != TYPE_PTRMEMFUNC_FN_TYPE (TREE_TYPE (pfn)))
+ {
+ npfn = build1 (NOP_EXPR, TYPE_GET_PTRMEMFUNC_TYPE (type), pfn);
+ TREE_CONSTANT (npfn) = TREE_CONSTANT (pfn);
+ }
+ return pfn;
+ }
+#else
+ if (type == TYPE_PTRMEMFUNC_FN_TYPE (TREE_TYPE (pfn)))
+ return pfn;
+#endif
+
+ if (TREE_CODE (pfn) != CONSTRUCTOR)
+ {
+ tree e1, e2, e3;
+ ndelta = convert (sizetype, build_component_ref (pfn, delta_identifier, 0, 0));
+ ndelta2 = convert (sizetype, DELTA2_FROM_PTRMEMFUNC (pfn));
+ index = build_component_ref (pfn, index_identifier, 0, 0);
+ delta = get_delta_difference (TYPE_METHOD_BASETYPE (TREE_TYPE (TYPE_PTRMEMFUNC_FN_TYPE (TREE_TYPE (pfn)))),
+ TYPE_METHOD_BASETYPE (TREE_TYPE (type)),
+ force);
+ delta = fold (size_binop (PLUS_EXPR, delta, ndelta));
+ delta2 = fold (size_binop (PLUS_EXPR, ndelta2, delta2));
+ e1 = fold (build (GT_EXPR, integer_type_node, index, integer_zero_node));
+
+ u = build_nt (CONSTRUCTOR, 0, tree_cons (delta2_identifier, delta2, NULL_TREE));
+ u = build_nt (CONSTRUCTOR, 0, tree_cons (NULL_TREE, delta,
+ tree_cons (NULL_TREE, index,
+ tree_cons (NULL_TREE, u, NULL_TREE))));
+ e2 = digest_init (TYPE_GET_PTRMEMFUNC_TYPE (type), u, (tree*)0);
+
+ pfn = PFN_FROM_PTRMEMFUNC (pfn);
+ npfn = build1 (NOP_EXPR, type, pfn);
+ TREE_CONSTANT (npfn) = TREE_CONSTANT (pfn);
+
+ u = build_nt (CONSTRUCTOR, 0, tree_cons (pfn_identifier, npfn, NULL_TREE));
+ u = build_nt (CONSTRUCTOR, 0, tree_cons (NULL_TREE, delta,
+ tree_cons (NULL_TREE, index,
+ tree_cons (NULL_TREE, u, NULL_TREE))));
+ e3 = digest_init (TYPE_GET_PTRMEMFUNC_TYPE (type), u, (tree*)0);
+ return build_conditional_expr (e1, e2, e3);
+ }
+
+ ndelta = TREE_VALUE (CONSTRUCTOR_ELTS (pfn));
+ nindex = TREE_VALUE (TREE_CHAIN (CONSTRUCTOR_ELTS (pfn)));
+ npfn = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (CONSTRUCTOR_ELTS (pfn))));
+ npfn = TREE_VALUE (CONSTRUCTOR_ELTS (npfn));
+ if (integer_zerop (nindex))
+ pfn = integer_zero_node;
+ else
+ {
+ sorry ("value casting of varible nonnull pointer to member functions not supported");
+ return error_mark_node;
+ }
+ }
+
+ /* Handle null pointer to member function conversions. */
+ if (integer_zerop (pfn))
+ {
+ pfn = build_c_cast (type, integer_zero_node);
+ u = build_nt (CONSTRUCTOR, 0, tree_cons (pfn_identifier, pfn, NULL_TREE));
+ u = build_nt (CONSTRUCTOR, 0, tree_cons (NULL_TREE, integer_zero_node,
+ tree_cons (NULL_TREE, integer_zero_node,
+ tree_cons (NULL_TREE, u, NULL_TREE))));
+ return digest_init (TYPE_GET_PTRMEMFUNC_TYPE (type), u, (tree*)0);
+ }
+
+ if (TREE_CODE (pfn) == TREE_LIST)
+ {
+ pfn = instantiate_type (type, pfn, 1);
+ if (pfn == error_mark_node)
+ return error_mark_node;
+ pfn = build_unary_op (ADDR_EXPR, pfn, 0);
+ }
+
+ /* Allow pointer to member conversions here. */
+ delta = get_delta_difference (TYPE_METHOD_BASETYPE (TREE_TYPE (TREE_TYPE (pfn))),
+ TYPE_METHOD_BASETYPE (TREE_TYPE (type)),
+ force);
+ delta2 = fold (size_binop (PLUS_EXPR, delta2, delta));
+
+ if (TREE_CODE (TREE_OPERAND (pfn, 0)) != FUNCTION_DECL)
+ warning ("assuming pointer to member function is non-virtual");
+
+ if (TREE_CODE (TREE_OPERAND (pfn, 0)) == FUNCTION_DECL
+ && DECL_VINDEX (TREE_OPERAND (pfn, 0)))
+ {
+ /* Find the offset to the vfield pointer in the object. */
+ vfield_offset = get_binfo (DECL_CONTEXT (TREE_OPERAND (pfn, 0)),
+ DECL_CLASS_CONTEXT (TREE_OPERAND (pfn, 0)),
+ 0);
+ vfield_offset = get_vfield_offset (vfield_offset);
+ delta2 = size_binop (PLUS_EXPR, vfield_offset, delta2);
+
+ /* Map everything down one to make room for the null pointer to member. */
+ index = size_binop (PLUS_EXPR,
+ DECL_VINDEX (TREE_OPERAND (pfn, 0)),
+ integer_one_node);
+ u = build_nt (CONSTRUCTOR, 0, tree_cons (delta2_identifier, delta2, NULL_TREE));
+ }
+ else
+ {
+ index = fold (size_binop (MINUS_EXPR, integer_zero_node, integer_one_node));
+
+ npfn = build1 (NOP_EXPR, type, pfn);
+ TREE_CONSTANT (npfn) = TREE_CONSTANT (pfn);
+
+ u = build_nt (CONSTRUCTOR, 0, tree_cons (pfn_identifier, npfn, NULL_TREE));
+ }
+
+ u = build_nt (CONSTRUCTOR, 0, tree_cons (NULL_TREE, delta,
+ tree_cons (NULL_TREE, index,
+ tree_cons (NULL_TREE, u, NULL_TREE))));
+ return digest_init (TYPE_GET_PTRMEMFUNC_TYPE (type), u, (tree*)0);
+}
+
+/* Convert value RHS to type TYPE as preparation for an assignment
+ to an lvalue of type TYPE.
+ The real work of conversion is done by `convert'.
+ The purpose of this function is to generate error messages
+ for assignments that are not allowed in C.
+ ERRTYPE is a string to use in error messages:
+ "assignment", "return", etc.
+
+ C++: attempts to allow `convert' to find conversions involving
+ implicit type conversion between aggregate and scalar types
+ as per 8.5.6 of C++ manual. Does not randomly dereference
+ pointers to aggregates! */
+
+static tree
+convert_for_assignment (type, rhs, errtype, fndecl, parmnum)
+ tree type, rhs;
+ char *errtype;
+ tree fndecl;
+ int parmnum;
+{
+ register enum tree_code codel = TREE_CODE (type);
+ register tree rhstype;
+ register enum tree_code coder = TREE_CODE (TREE_TYPE (rhs));
+
+ if (coder == UNKNOWN_TYPE)
+ rhs = instantiate_type (type, rhs, 1);
+
+ if (coder == ERROR_MARK)
+ return error_mark_node;
+
+ if (codel == OFFSET_TYPE)
+ {
+ type = TREE_TYPE (type);
+ codel = TREE_CODE (type);
+ }
+
+ /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
+ if (TREE_CODE (rhs) == NON_LVALUE_EXPR)
+ rhs = TREE_OPERAND (rhs, 0);
+
+ if (rhs == error_mark_node)
+ return error_mark_node;
+
+ if (TREE_VALUE (rhs) == error_mark_node)
+ return error_mark_node;
+
+ if (TREE_CODE (TREE_TYPE (rhs)) == OFFSET_TYPE)
+ {
+ rhs = resolve_offset_ref (rhs);
+ if (rhs == error_mark_node)
+ return error_mark_node;
+ rhstype = TREE_TYPE (rhs);
+ coder = TREE_CODE (rhstype);
+ }
+
+ if (TREE_CODE (TREE_TYPE (rhs)) == ARRAY_TYPE
+ || TREE_CODE (TREE_TYPE (rhs)) == FUNCTION_TYPE
+ || TREE_CODE (TREE_TYPE (rhs)) == METHOD_TYPE)
+ rhs = default_conversion (rhs);
+ else if (TREE_CODE (TREE_TYPE (rhs)) == REFERENCE_TYPE)
+ rhs = convert_from_reference (rhs);
+
+ rhstype = TREE_TYPE (rhs);
+ coder = TREE_CODE (rhstype);
+
+ /* This should no longer change types on us. */
+ if (TREE_CODE (rhs) == CONST_DECL)
+ rhs = DECL_INITIAL (rhs);
+ else if (TREE_READONLY_DECL_P (rhs))
+ rhs = decl_constant_value (rhs);
+
+ if (type == rhstype)
+ {
+ overflow_warning (rhs);
+ return rhs;
+ }
+
+ if (coder == VOID_TYPE)
+ {
+ error ("void value not ignored as it ought to be");
+ return error_mark_node;
+ }
+ /* Arithmetic types all interconvert. */
+ if ((codel == INTEGER_TYPE || codel == REAL_TYPE || codel == BOOLEAN_TYPE)
+ && (coder == INTEGER_TYPE || coder == REAL_TYPE || coder == BOOLEAN_TYPE))
+ {
+ /* But we should warn if assigning REAL_TYPE to INTEGER_TYPE. */
+ if (coder == REAL_TYPE && codel == INTEGER_TYPE)
+ {
+ if (fndecl)
+ cp_warning ("`%T' used for argument %P of `%D'",
+ rhstype, parmnum, fndecl);
+ else
+ cp_warning ("%s to `%T' from `%T'", errtype, type, rhstype);
+ }
+ /* And we should warn if assigning a negative value to
+ an unsigned variable. */
+ else if (TREE_UNSIGNED (type) && codel != BOOLEAN_TYPE)
+ {
+ if (TREE_CODE (rhs) == INTEGER_CST
+ && TREE_NEGATED_INT (rhs))
+ {
+ if (fndecl)
+ cp_warning ("negative value `%E' passed as argument %P of `%D'",
+ rhs, parmnum, fndecl);
+ else
+ cp_warning ("%s of negative value `%E' to `%T'",
+ errtype, rhs, type);
+ }
+ overflow_warning (rhs);
+ if (TREE_CONSTANT (rhs))
+ rhs = fold (rhs);
+ }
+
+ return convert_and_check (type, rhs);
+ }
+ /* Conversions involving enums. */
+ else if ((codel == ENUMERAL_TYPE
+ && (coder == ENUMERAL_TYPE || coder == INTEGER_TYPE || coder == REAL_TYPE))
+ || (coder == ENUMERAL_TYPE
+ && (codel == ENUMERAL_TYPE || codel == INTEGER_TYPE || codel == REAL_TYPE)))
+ {
+ return convert (type, rhs);
+ }
+ /* Conversions among pointers */
+ else if (codel == POINTER_TYPE
+ && (coder == POINTER_TYPE
+ || (coder == RECORD_TYPE
+ && (IS_SIGNATURE_POINTER (rhstype)
+ || IS_SIGNATURE_REFERENCE (rhstype)))))
+ {
+ register tree ttl = TREE_TYPE (type);
+ register tree ttr;
+
+ if (coder == RECORD_TYPE)
+ {
+ rhs = build_optr_ref (rhs);
+ rhstype = TREE_TYPE (rhs);
+ }
+ ttr = TREE_TYPE (rhstype);
+
+ /* If both pointers are of aggregate type, then we
+ can give better error messages, and save some work
+ as well. */
+ if (TREE_CODE (ttl) == RECORD_TYPE && TREE_CODE (ttr) == RECORD_TYPE)
+ {
+ tree binfo;
+
+ if (TYPE_MAIN_VARIANT (ttl) == TYPE_MAIN_VARIANT (ttr)
+ || type == class_star_type_node
+ || rhstype == class_star_type_node)
+ binfo = TYPE_BINFO (ttl);
+ else
+ binfo = get_binfo (ttl, ttr, 1);
+
+ if (binfo == error_mark_node)
+ return error_mark_node;
+ if (binfo == 0)
+ return error_not_base_type (ttl, ttr);
+
+ if (! TYPE_READONLY (ttl) && TYPE_READONLY (ttr))
+ {
+ if (fndecl)
+ cp_pedwarn ("passing `%T' as argument %P of `%D' discards const",
+ rhstype, parmnum, fndecl);
+ else
+ cp_pedwarn ("%s to `%T' from `%T' discards const",
+ errtype, type, rhstype);
+ }
+ if (! TYPE_VOLATILE (ttl) && TYPE_VOLATILE (ttr))
+ {
+ if (fndecl)
+ cp_pedwarn ("passing `%T' as argument %P of `%D' discards volatile",
+ rhstype, parmnum, fndecl);
+ else
+ cp_pedwarn ("%s to `%T' from `%T' discards volatile",
+ errtype, type, rhstype);
+ }
+ }
+
+ /* Any non-function converts to a [const][volatile] void *
+ and vice versa; otherwise, targets must be the same.
+ Meanwhile, the lhs target must have all the qualifiers of the rhs. */
+ else if (TYPE_MAIN_VARIANT (ttl) == void_type_node
+ || TYPE_MAIN_VARIANT (ttr) == void_type_node
+ || comp_target_types (type, rhstype, 1)
+ || (unsigned_type (TYPE_MAIN_VARIANT (ttl))
+ == unsigned_type (TYPE_MAIN_VARIANT (ttr))))
+ {
+ /* ARM $4.8, commentary on p39. */
+ if (TYPE_MAIN_VARIANT (ttl) == void_type_node
+ && TREE_CODE (ttr) == OFFSET_TYPE)
+ {
+ error ("no standard conversion from pointer to member to `void *'");
+ return error_mark_node;
+ }
+
+ if (TYPE_MAIN_VARIANT (ttl) != void_type_node
+ && TYPE_MAIN_VARIANT (ttr) == void_type_node
+ && rhs != null_pointer_node)
+ {
+ if (coder == RECORD_TYPE)
+ pedwarn ("implicit conversion of signature pointer to type `%s'",
+ type_as_string (type, 0));
+ else
+ pedwarn ("ANSI C++ forbids implicit conversion from `void *' in %s",
+ errtype);
+ }
+ /* Const and volatile mean something different for function types,
+ so the usual warnings are not appropriate. */
+ else if ((TREE_CODE (ttr) != FUNCTION_TYPE && TREE_CODE (ttr) != METHOD_TYPE)
+ || (TREE_CODE (ttl) != FUNCTION_TYPE && TREE_CODE (ttl) != METHOD_TYPE))
+ {
+ if (TREE_CODE (ttl) == OFFSET_TYPE
+ && binfo_member (TYPE_OFFSET_BASETYPE (ttr),
+ CLASSTYPE_VBASECLASSES (TYPE_OFFSET_BASETYPE (ttl))))
+ {
+ sorry ("%s between pointer to members converting across virtual baseclasses", errtype);
+ return error_mark_node;
+ }
+ else if (! TYPE_READONLY (ttl) && TYPE_READONLY (ttr))
+ {
+ if (fndecl)
+ cp_pedwarn ("passing `%T' as argument %P of `%D' discards const",
+ rhstype, parmnum, fndecl);
+ else
+ cp_pedwarn ("%s to `%T' from `%T' discards const",
+ errtype, type, rhstype);
+ }
+ else if (! TYPE_VOLATILE (ttl) && TYPE_VOLATILE (ttr))
+ {
+ if (fndecl)
+ cp_pedwarn ("passing `%T' as argument %P of `%D' discards volatile",
+ rhstype, parmnum, fndecl);
+ else
+ cp_pedwarn ("%s to `%T' from `%T' discards volatile",
+ errtype, type, rhstype);
+ }
+ else if (TREE_CODE (ttl) == TREE_CODE (ttr)
+ && ! comp_target_types (type, rhstype, 1))
+ {
+ if (fndecl)
+ cp_pedwarn ("passing `%T' as argument %P of `%D' changes signedness",
+ rhstype, parmnum, fndecl);
+ else
+ cp_pedwarn ("%s to `%T' from `%T' changes signedness",
+ errtype, type, rhstype);
+ }
+ }
+ }
+ else if (TREE_CODE (ttr) == OFFSET_TYPE
+ && TREE_CODE (ttl) != OFFSET_TYPE)
+ {
+ /* Normally, pointers to different type codes (other
+ than void) are not compatible, but we perform
+ some type instantiation if that resolves the
+ ambiguity of (X Y::*) and (X *). */
+
+ if (current_class_decl)
+ {
+ if (TREE_CODE (rhs) == INTEGER_CST)
+ {
+ rhs = build (PLUS_EXPR, build_pointer_type (TREE_TYPE (ttr)),
+ current_class_decl, rhs);
+ return convert_for_assignment (type, rhs,
+ errtype, fndecl, parmnum);
+ }
+ }
+ if (TREE_CODE (ttl) == METHOD_TYPE)
+ error ("%s between pointer-to-method and pointer-to-member types",
+ errtype);
+ else
+ error ("%s between pointer and pointer-to-member types", errtype);
+ return error_mark_node;
+ }
+ else
+ {
+ int add_quals = 0, const_parity = 0, volatile_parity = 0;
+ int left_const = 1;
+ int unsigned_parity;
+ int nptrs = 0;
+
+ /* This code is basically a duplicate of comp_ptr_ttypes_real. */
+ for (; ; ttl = TREE_TYPE (ttl), ttr = TREE_TYPE (ttr))
+ {
+ nptrs -= 1;
+ const_parity |= TYPE_READONLY (ttl) < TYPE_READONLY (ttr);
+ volatile_parity |= TYPE_VOLATILE (ttl) < TYPE_VOLATILE (ttr);
+
+ if (! left_const
+ && (TYPE_READONLY (ttl) > TYPE_READONLY (ttr)
+ || TYPE_VOLATILE (ttl) > TYPE_VOLATILE (ttr)))
+ add_quals = 1;
+ left_const &= TYPE_READONLY (ttl);
+
+ if (TREE_CODE (ttl) != POINTER_TYPE)
+ break;
+ }
+ unsigned_parity = TREE_UNSIGNED (ttl) - TREE_UNSIGNED (ttr);
+ if (unsigned_parity)
+ {
+ if (TREE_UNSIGNED (ttl))
+ ttr = unsigned_type (ttr);
+ else
+ ttl = unsigned_type (ttl);
+ }
+
+ if (comp_target_types (ttl, ttr, nptrs))
+ {
+ if (add_quals)
+ {
+ if (fndecl)
+ cp_pedwarn ("passing `%T' as argument %P of `%D' adds cv-quals without intervening `const'",
+ rhstype, parmnum, fndecl);
+ else
+ cp_pedwarn ("%s to `%T' from `%T' adds cv-quals without intervening `const'",
+ errtype, type, rhstype);
+ }
+ if (const_parity)
+ {
+ if (fndecl)
+ cp_pedwarn ("passing `%T' as argument %P of `%D' discards const",
+ rhstype, parmnum, fndecl);
+ else
+ cp_pedwarn ("%s to `%T' from `%T' discards const",
+ errtype, type, rhstype);
+ }
+ if (volatile_parity)
+ {
+ if (fndecl)
+ cp_pedwarn ("passing `%T' as argument %P of `%D' discards volatile",
+ rhstype, parmnum, fndecl);
+ else
+ cp_pedwarn ("%s to `%T' from `%T' discards volatile",
+ errtype, type, rhstype);
+ }
+ if (unsigned_parity > 0)
+ {
+ if (fndecl)
+ cp_pedwarn ("passing `%T' as argument %P of `%D' changes signed to unsigned",
+ rhstype, parmnum, fndecl);
+ else
+ cp_pedwarn ("%s to `%T' from `%T' changes signed to unsigned",
+ errtype, type, rhstype);
+ }
+ else if (unsigned_parity < 0)
+ {
+ if (fndecl)
+ cp_pedwarn ("passing `%T' as argument %P of `%D' changes unsigned to signed",
+ rhstype, parmnum, fndecl);
+ else
+ cp_pedwarn ("%s to `%T' from `%T' changes unsigned to signed",
+ errtype, type, rhstype);
+ }
+
+ /* C++ is not so friendly about converting function and
+ member function pointers as C. Emit warnings here. */
+ if (TREE_CODE (ttl) == FUNCTION_TYPE
+ || TREE_CODE (ttl) == METHOD_TYPE)
+ if (! comptypes (ttl, ttr, 0))
+ {
+ warning ("conflicting function types in %s:", errtype);
+ cp_warning ("\t`%T' != `%T'", type, rhstype);
+ }
+ }
+ else if (TREE_CODE (TREE_TYPE (rhs)) == METHOD_TYPE)
+ {
+ /* When does this happen? */
+ my_friendly_abort (119);
+ /* Conversion of a pointer-to-member type to void *. */
+ rhs = build_unary_op (ADDR_EXPR, rhs, 0);
+ TREE_TYPE (rhs) = type;
+ return rhs;
+ }
+ else if (TREE_CODE (TREE_TYPE (rhs)) == OFFSET_TYPE)
+ {
+ /* When does this happen? */
+ my_friendly_abort (120);
+ /* Conversion of a pointer-to-member type to void *. */
+ rhs = build_unary_op (ADDR_EXPR, rhs, 0);
+ TREE_TYPE (rhs) = type;
+ return rhs;
+ }
+ else
+ {
+ if (fndecl)
+ cp_error ("passing `%T' as argument %P of `%D'",
+ rhstype, parmnum, fndecl);
+ else
+ cp_error ("%s to `%T' from `%T'", errtype, type, rhstype);
+ return error_mark_node;
+ }
+ }
+ return convert (type, rhs);
+ }
+ else if (codel == POINTER_TYPE && coder == INTEGER_TYPE)
+ {
+ /* An explicit constant 0 can convert to a pointer,
+ but not a 0 that results from casting or folding. */
+ if (! (TREE_CODE (rhs) == INTEGER_CST && integer_zerop (rhs)))
+ {
+ if (fndecl)
+ cp_pedwarn ("passing `%T' to argument %P of `%D' lacks a cast",
+ rhstype, parmnum, fndecl);
+ else
+ cp_pedwarn ("%s to `%T' from `%T' lacks a cast",
+ errtype, type, rhstype);
+ return convert (type, rhs);
+ }
+ return null_pointer_node;
+ }
+ else if (codel == INTEGER_TYPE
+ && (coder == POINTER_TYPE
+ || (coder == RECORD_TYPE
+ && (IS_SIGNATURE_POINTER (rhstype)
+ || IS_SIGNATURE_REFERENCE (rhstype)))))
+ {
+ if (fndecl)
+ cp_pedwarn ("passing `%T' to argument %P of `%D' lacks a cast",
+ rhstype, parmnum, fndecl);
+ else
+ cp_pedwarn ("%s to `%T' from `%T' lacks a cast",
+ errtype, type, rhstype);
+ return convert (type, rhs);
+ }
+
+ /* C++ */
+ else if (((coder == POINTER_TYPE && TREE_CODE (rhs) == ADDR_EXPR
+ && TREE_CODE (rhstype) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (rhstype)) == METHOD_TYPE)
+ || integer_zerop (rhs)
+ || TYPE_PTRMEMFUNC_P (TREE_TYPE (rhs)))
+ && TYPE_PTRMEMFUNC_P (type))
+ {
+ /* compatible pointer to member functions. */
+ return build_ptrmemfunc (TYPE_PTRMEMFUNC_FN_TYPE (type), rhs, 0);
+ }
+ else if (codel == ERROR_MARK || coder == ERROR_MARK)
+ return error_mark_node;
+
+ /* This should no longer happen. References are initialized via
+ `convert_for_initialization'. They should otherwise be
+ bashed before coming here. */
+ else if (codel == REFERENCE_TYPE)
+ /* Force an abort. */
+ my_friendly_assert (codel != REFERENCE_TYPE, 317);
+ else if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (TREE_TYPE (rhs)))
+ {
+ tree nrhs = build1 (NOP_EXPR, type, rhs);
+ TREE_CONSTANT (nrhs) = TREE_CONSTANT (rhs);
+ return nrhs;
+ }
+ else if (TYPE_HAS_CONSTRUCTOR (type) || IS_AGGR_TYPE (TREE_TYPE (rhs)))
+ return convert (type, rhs);
+
+ cp_error ("%s to `%T' from `%T'", errtype, type, rhstype);
+ return error_mark_node;
+}
+
+/* Convert RHS to be of type TYPE. If EXP is non-zero,
+ it is the target of the initialization.
+ ERRTYPE is a string to use in error messages.
+
+ Two major differences between the behavior of
+ `convert_for_assignment' and `convert_for_initialization'
+ are that references are bashed in the former, while
+ copied in the latter, and aggregates are assigned in
+ the former (operator=) while initialized in the
+ latter (X(X&)).
+
+ If using constructor make sure no conversion operator exists, if one does
+ exist, an ambiguity exists. */
+tree
+convert_for_initialization (exp, type, rhs, flags, errtype, fndecl, parmnum)
+ tree exp, type, rhs;
+ int flags;
+ char *errtype;
+ tree fndecl;
+ int parmnum;
+{
+ register enum tree_code codel = TREE_CODE (type);
+ register tree rhstype;
+ register enum tree_code coder;
+
+ /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.
+ Strip such NOP_EXPRs, since RHS is used in non-lvalue context. */
+ if (TREE_CODE (rhs) == NOP_EXPR
+ && TREE_TYPE (rhs) == TREE_TYPE (TREE_OPERAND (rhs, 0))
+ && codel != REFERENCE_TYPE)
+ rhs = TREE_OPERAND (rhs, 0);
+
+ if (rhs == error_mark_node
+ || (TREE_CODE (rhs) == TREE_LIST && TREE_VALUE (rhs) == error_mark_node))
+ return error_mark_node;
+
+ if (TREE_CODE (TREE_TYPE (rhs)) == OFFSET_TYPE)
+ {
+ rhs = resolve_offset_ref (rhs);
+ if (rhs == error_mark_node)
+ return error_mark_node;
+ rhstype = TREE_TYPE (rhs);
+ coder = TREE_CODE (rhstype);
+ }
+
+ if ((TREE_CODE (TREE_TYPE (rhs)) == ARRAY_TYPE
+ && TREE_CODE (type) != ARRAY_TYPE && TREE_CODE (type) != REFERENCE_TYPE)
+ || TREE_CODE (TREE_TYPE (rhs)) == FUNCTION_TYPE
+ || TREE_CODE (TREE_TYPE (rhs)) == METHOD_TYPE)
+ rhs = default_conversion (rhs);
+
+ rhstype = TREE_TYPE (rhs);
+ coder = TREE_CODE (rhstype);
+
+ if (coder == UNKNOWN_TYPE)
+ {
+ rhs = instantiate_type (type, rhs, 1);
+ rhstype = TREE_TYPE (rhs);
+ coder = TREE_CODE (rhstype);
+ }
+
+ if (coder == ERROR_MARK)
+ return error_mark_node;
+
+#if 0
+ /* This is *not* the quick way out! It is the way to disaster. */
+ if (type == rhstype)
+ goto converted;
+#endif
+
+ /* We accept references to incomplete types, so we can
+ return here before checking if RHS is of complete type. */
+
+ if (codel == REFERENCE_TYPE)
+ {
+ /* This should eventually happen in convert_arguments. */
+ extern int warningcount, errorcount;
+ int savew, savee;
+
+ if (fndecl)
+ savew = warningcount, savee = errorcount;
+ rhs = convert_to_reference (type, rhs, CONV_IMPLICIT, flags,
+ exp ? exp : error_mark_node);
+ if (fndecl)
+ {
+ if (warningcount > savew)
+ cp_warning_at ("in passing argument %P of `%+D'", parmnum, fndecl);
+ else if (errorcount > savee)
+ cp_error_at ("in passing argument %P of `%+D'", parmnum, fndecl);
+ }
+ return rhs;
+ }
+
+ rhs = require_complete_type (rhs);
+ if (rhs == error_mark_node)
+ return error_mark_node;
+
+ if (exp != 0) exp = require_complete_type (exp);
+ if (exp == error_mark_node)
+ return error_mark_node;
+
+ if (TREE_CODE (rhstype) == REFERENCE_TYPE)
+ rhstype = TREE_TYPE (rhstype);
+
+ if (TYPE_LANG_SPECIFIC (type)
+ && (IS_SIGNATURE_POINTER (type) || IS_SIGNATURE_REFERENCE (type)))
+ return build_signature_pointer_constructor (type, rhs);
+
+ if (IS_AGGR_TYPE (type) && TYPE_NEEDS_CONSTRUCTING (type))
+ {
+ if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (rhstype))
+ {
+ /* This is sufficient to perform initialization. No need,
+ apparently, to go through X(X&) to do first-cut
+ initialization. Return through a TARGET_EXPR so that we get
+ cleanups if it is used. */
+ if (TREE_CODE (rhs) == CALL_EXPR)
+ {
+ rhs = build_cplus_new (type, rhs, 0);
+ return rhs;
+ }
+ /* Handle the case of default parameter initialization and
+ initialization of static variables. */
+ else if (TREE_CODE (rhs) == INDIRECT_REF && TREE_HAS_CONSTRUCTOR (rhs))
+ {
+ my_friendly_assert (TREE_CODE (TREE_OPERAND (rhs, 0)) == CALL_EXPR, 318);
+ if (exp)
+ {
+ my_friendly_assert (TREE_VALUE (TREE_OPERAND (TREE_OPERAND (rhs, 0), 1)) == NULL_TREE, 316);
+ TREE_VALUE (TREE_OPERAND (TREE_OPERAND (rhs, 0), 1))
+ = build_unary_op (ADDR_EXPR, exp, 0);
+ }
+ else
+ rhs = build_cplus_new (type, TREE_OPERAND (rhs, 0), 0);
+ return rhs;
+ }
+ }
+ if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (rhstype)
+ || (IS_AGGR_TYPE (rhstype) && UNIQUELY_DERIVED_FROM_P (type, rhstype)))
+ {
+ if (TYPE_HAS_INIT_REF (type))
+ {
+ tree init = build_method_call (exp, constructor_name_full (type),
+ build_tree_list (NULL_TREE, rhs),
+ TYPE_BINFO (type), LOOKUP_NORMAL);
+
+ if (init == error_mark_node)
+ return error_mark_node;
+
+ if (exp == 0)
+ {
+ exp = build_cplus_new (type, init, 0);
+ return exp;
+ }
+
+ return build (COMPOUND_EXPR, type, init, exp);
+ }
+
+ /* ??? The following warnings are turned off because
+ this is another place where the default X(X&) constructor
+ is implemented. */
+ if (TYPE_HAS_ASSIGNMENT (type))
+ cp_warning ("bitwise copy: `%T' defines operator=", type);
+
+ if (TREE_CODE (TREE_TYPE (rhs)) == REFERENCE_TYPE)
+ rhs = convert_from_reference (rhs);
+ if (type != rhstype)
+ {
+ tree nrhs = build1 (NOP_EXPR, type, rhs);
+ TREE_CONSTANT (nrhs) = TREE_CONSTANT (rhs);
+ rhs = nrhs;
+ }
+ return rhs;
+ }
+
+ return convert (type, rhs);
+ }
+
+ if (type == TREE_TYPE (rhs))
+ {
+ if (TREE_READONLY_DECL_P (rhs))
+ rhs = decl_constant_value (rhs);
+ return rhs;
+ }
+
+ return convert_for_assignment (type, rhs, errtype, fndecl, parmnum);
+}
+
+/* Expand an ASM statement with operands, handling output operands
+ that are not variables or INDIRECT_REFS by transforming such
+ cases into cases that expand_asm_operands can handle.
+
+ Arguments are same as for expand_asm_operands.
+
+ We don't do default conversions on all inputs, because it can screw
+ up operands that are expected to be in memory. */
+
+void
+c_expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
+ tree string, outputs, inputs, clobbers;
+ int vol;
+ char *filename;
+ int line;
+{
+ int noutputs = list_length (outputs);
+ register int i;
+ /* o[I] is the place that output number I should be written. */
+ register tree *o = (tree *) alloca (noutputs * sizeof (tree));
+ register tree tail;
+
+ /* Record the contents of OUTPUTS before it is modified. */
+ for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
+ o[i] = TREE_VALUE (tail);
+
+ /* Generate the ASM_OPERANDS insn;
+ store into the TREE_VALUEs of OUTPUTS some trees for
+ where the values were actually stored. */
+ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line);
+
+ /* Copy all the intermediate outputs into the specified outputs. */
+ for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
+ {
+ if (o[i] != TREE_VALUE (tail))
+ {
+ expand_expr (build_modify_expr (o[i], NOP_EXPR, TREE_VALUE (tail)),
+ const0_rtx, VOIDmode, 0);
+ free_temp_slots ();
+ }
+ /* Detect modification of read-only values.
+ (Otherwise done by build_modify_expr.) */
+ else
+ {
+ tree type = TREE_TYPE (o[i]);
+ if (TYPE_READONLY (type)
+ || ((TREE_CODE (type) == RECORD_TYPE
+ || TREE_CODE (type) == UNION_TYPE)
+ && C_TYPE_FIELDS_READONLY (type)))
+ readonly_error (o[i], "modification by `asm'", 1);
+ }
+ }
+
+ /* Those MODIFY_EXPRs could do autoincrements. */
+ emit_queue ();
+}
+
+/* Expand a C `return' statement.
+ RETVAL is the expression for what to return,
+ or a null pointer for `return;' with no value.
+
+ C++: upon seeing a `return', we must call destructors on all
+ variables in scope which had constructors called on them.
+ This means that if in a destructor, the base class destructors
+ must be called before returning.
+
+ The RETURN statement in C++ has initialization semantics. */
+
+void
+c_expand_return (retval)
+ tree retval;
+{
+ extern struct nesting *cond_stack, *loop_stack, *case_stack;
+ extern tree dtor_label, ctor_label;
+ tree result = DECL_RESULT (current_function_decl);
+ tree valtype = TREE_TYPE (result);
+ register int use_temp = 0;
+ int returns_value = 1;
+
+ if (TREE_THIS_VOLATILE (current_function_decl))
+ warning ("function declared `noreturn' has a `return' statement");
+
+ if (retval == error_mark_node)
+ {
+ current_function_returns_null = 1;
+ return;
+ }
+
+ if (retval == NULL_TREE)
+ {
+ /* A non-named return value does not count. */
+
+ /* Can't just return from a destructor. */
+ if (dtor_label)
+ {
+ expand_goto (dtor_label);
+ return;
+ }
+
+ if (DECL_CONSTRUCTOR_P (current_function_decl))
+ retval = current_class_decl;
+ else if (DECL_NAME (result) != NULL_TREE
+ && TREE_CODE (valtype) != VOID_TYPE)
+ retval = result;
+ else
+ {
+ current_function_returns_null = 1;
+
+ if (valtype != NULL_TREE && TREE_CODE (valtype) != VOID_TYPE)
+ {
+ if (DECL_NAME (DECL_RESULT (current_function_decl)) == NULL_TREE)
+ {
+ pedwarn ("`return' with no value, in function returning non-void");
+ /* Clear this, so finish_function won't say that we
+ reach the end of a non-void function (which we don't,
+ we gave a return!). */
+ current_function_returns_null = 0;
+ }
+ }
+
+ expand_null_return ();
+ return;
+ }
+ }
+ else if (DECL_CONSTRUCTOR_P (current_function_decl)
+ && retval != current_class_decl)
+ {
+ error ("return from a constructor: use `this = ...' instead");
+ retval = current_class_decl;
+ }
+
+ if (valtype == NULL_TREE || TREE_CODE (valtype) == VOID_TYPE)
+ {
+ current_function_returns_null = 1;
+ /* We do this here so we'll avoid a warning about how the function
+ "may or may not return a value" in finish_function. */
+ returns_value = 0;
+
+ if (retval)
+ pedwarn ("`return' with a value, in function returning void");
+ expand_return (retval);
+ }
+ /* Add some useful error checking for C++. */
+ else if (TREE_CODE (valtype) == REFERENCE_TYPE)
+ {
+ tree whats_returned;
+ tree tmp_result = result;
+
+ /* Don't initialize directly into a non-BLKmode retval, since that
+ could lose when being inlined by another caller. (GCC can't
+ read the function return register in an inline function when
+ the return value is being ignored). */
+ if (result && TYPE_MODE (TREE_TYPE (tmp_result)) != BLKmode)
+ tmp_result = 0;
+
+ /* convert to reference now, so we can give error if we
+ return an reference to a non-lvalue. */
+ retval = convert_for_initialization (tmp_result, valtype, retval,
+ LOOKUP_NORMAL, "return",
+ NULL_TREE, 0);
+
+ /* Sort through common things to see what it is
+ we are returning. */
+ whats_returned = retval;
+ if (TREE_CODE (whats_returned) == COMPOUND_EXPR)
+ {
+ whats_returned = TREE_OPERAND (whats_returned, 1);
+ if (TREE_CODE (whats_returned) == ADDR_EXPR)
+ whats_returned = TREE_OPERAND (whats_returned, 0);
+ }
+ if (TREE_CODE (whats_returned) == ADDR_EXPR)
+ {
+ whats_returned = TREE_OPERAND (whats_returned, 0);
+ while (TREE_CODE (whats_returned) == NEW_EXPR
+ || TREE_CODE (whats_returned) == TARGET_EXPR
+ || TREE_CODE (whats_returned) == WITH_CLEANUP_EXPR)
+ /* Get the target. */
+ whats_returned = TREE_OPERAND (whats_returned, 0);
+ }
+
+ if (TREE_CODE (whats_returned) == VAR_DECL && DECL_NAME (whats_returned))
+ {
+ if (TEMP_NAME_P (DECL_NAME (whats_returned)))
+ warning ("reference to non-lvalue returned");
+ else if (! TREE_STATIC (whats_returned)
+ && IDENTIFIER_LOCAL_VALUE (DECL_NAME (whats_returned)))
+ cp_warning_at ("reference to local variable `%D' returned", whats_returned);
+ }
+ }
+ else if (TREE_CODE (retval) == ADDR_EXPR)
+ {
+ tree whats_returned = TREE_OPERAND (retval, 0);
+
+ if (TREE_CODE (whats_returned) == VAR_DECL
+ && DECL_NAME (whats_returned)
+ && IDENTIFIER_LOCAL_VALUE (DECL_NAME (whats_returned))
+ && !TREE_STATIC (whats_returned))
+ cp_warning_at ("address of local variable `%D' returned", whats_returned);
+ }
+
+ /* Now deal with possible C++ hair:
+ (1) Compute the return value.
+ (2) If there are aggregate values with destructors which
+ must be cleaned up, clean them (taking care
+ not to clobber the return value).
+ (3) If an X(X&) constructor is defined, the return
+ value must be returned via that. */
+
+ if (retval == result
+ /* Watch out for constructors, which "return" aggregates
+ via initialization, but which otherwise "return" a pointer. */
+ || DECL_CONSTRUCTOR_P (current_function_decl))
+ {
+ /* This is just an error--it's already been reported. */
+ if (TYPE_SIZE (valtype) == NULL_TREE)
+ return;
+
+ if (TYPE_MODE (valtype) != BLKmode
+ && any_pending_cleanups (1))
+ {
+ retval = get_temp_regvar (valtype, retval);
+ use_temp = obey_regdecls;
+ }
+ }
+ else if (IS_AGGR_TYPE (valtype) && TYPE_NEEDS_CONSTRUCTING (valtype))
+ {
+ /* Throw away the cleanup that `build_functional_cast' gave us. */
+ if (TREE_CODE (retval) == WITH_CLEANUP_EXPR
+ && TREE_CODE (TREE_OPERAND (retval, 0)) == TARGET_EXPR)
+ retval = TREE_OPERAND (retval, 0);
+ expand_aggr_init (result, retval, 0);
+ DECL_INITIAL (result) = NULL_TREE;
+ retval = 0;
+ }
+ else
+ {
+ if (TYPE_MODE (valtype) == VOIDmode)
+ {
+ if (TYPE_MODE (TREE_TYPE (result)) != VOIDmode
+ && warn_return_type)
+ warning ("return of void value in function returning non-void");
+ expand_expr_stmt (retval);
+ retval = 0;
+ result = 0;
+ }
+ else if (TYPE_MODE (valtype) != BLKmode
+ && any_pending_cleanups (1))
+ {
+ retval = get_temp_regvar (valtype, retval);
+ use_temp = obey_regdecls;
+ result = 0;
+ }
+ else
+ {
+ retval = convert_for_initialization (result, valtype, retval,
+ LOOKUP_NORMAL,
+ "return", NULL_TREE, 0);
+ DECL_INITIAL (result) = NULL_TREE;
+ }
+ if (retval == error_mark_node)
+ return;
+ }
+
+ emit_queue ();
+
+ if (retval != NULL_TREE
+ && TREE_CODE_CLASS (TREE_CODE (retval)) == 'd'
+ && cond_stack == 0 && loop_stack == 0 && case_stack == 0)
+ current_function_return_value = retval;
+
+ if (result)
+ {
+ /* Everything's great--RETVAL is in RESULT. */
+ if (original_result_rtx)
+ store_expr (result, original_result_rtx, 0);
+ else if (retval && retval != result)
+ {
+ /* Clear this out so the later call to decl_function_context
+ won't end up bombing on us. */
+ if (DECL_CONTEXT (result) == error_mark_node)
+ DECL_CONTEXT (result) = NULL_TREE;
+ /* Here is where we finally get RETVAL into RESULT.
+ `expand_return' does the magic of protecting
+ RESULT from cleanups. */
+ retval = build (INIT_EXPR, TREE_TYPE (result), result, retval);
+ TREE_SIDE_EFFECTS (retval) = 1;
+ expand_return (retval);
+ }
+ else
+ expand_return (result);
+
+ use_variable (DECL_RTL (result));
+ if (ctor_label && TREE_CODE (ctor_label) != ERROR_MARK)
+ expand_goto (ctor_label);
+ else
+ expand_null_return ();
+ }
+ else
+ {
+ /* We may still need to put RETVAL into RESULT. */
+ result = DECL_RESULT (current_function_decl);
+ if (original_result_rtx)
+ {
+ /* Here we have a named return value that went
+ into memory. We can compute RETVAL into that. */
+ if (retval)
+ expand_assignment (result, retval, 0, 0);
+ else
+ store_expr (result, original_result_rtx, 0);
+ result = make_tree (TREE_TYPE (result), original_result_rtx);
+ }
+ else if (ctor_label && TREE_CODE (ctor_label) != ERROR_MARK)
+ {
+ /* Here RETVAL is CURRENT_CLASS_DECL, so there's nothing to do. */
+ expand_goto (ctor_label);
+ }
+ else if (retval)
+ {
+ /* Here is where we finally get RETVAL into RESULT.
+ `expand_return' does the magic of protecting
+ RESULT from cleanups. */
+ result = build (INIT_EXPR, TREE_TYPE (result), result, retval);
+ TREE_SIDE_EFFECTS (result) = 1;
+ expand_return (result);
+ }
+ else if (TYPE_MODE (TREE_TYPE (result)) != VOIDmode)
+ expand_return (result);
+ }
+
+ current_function_returns_value = returns_value;
+#if 0
+ /* These wind up after the BARRIER, which causes problems for
+ expand_end_binding. What purpose were they supposed to serve? */
+ if (original_result_rtx)
+ use_variable (original_result_rtx);
+ if (use_temp)
+ use_variable (DECL_RTL (DECL_RESULT (current_function_decl)));
+#endif
+
+ /* One way to clear out cleanups that EXPR might
+ generate. Note that this code will really be
+ dead code, but that is ok--cleanups that were
+ needed were handled by the magic of `return'. */
+ expand_cleanups_to (NULL_TREE);
+}
+
+/* Start a C switch statement, testing expression EXP.
+ Return EXP if it is valid, an error node otherwise. */
+
+tree
+c_expand_start_case (exp)
+ tree exp;
+{
+ tree type;
+ register enum tree_code code;
+
+ /* Convert from references, etc. */
+ exp = default_conversion (exp);
+ type = TREE_TYPE (exp);
+ code = TREE_CODE (type);
+
+ if (IS_AGGR_TYPE_CODE (code))
+ exp = build_type_conversion (CONVERT_EXPR, integer_type_node, exp, 1);
+
+ if (exp == NULL_TREE)
+ {
+ error ("switch quantity not an integer");
+ exp = error_mark_node;
+ }
+ type = TREE_TYPE (exp);
+ code = TREE_CODE (type);
+
+ if (code != INTEGER_TYPE && code != ENUMERAL_TYPE && code != ERROR_MARK)
+ {
+ error ("switch quantity not an integer");
+ exp = error_mark_node;
+ }
+ else
+ {
+ tree index;
+
+ exp = default_conversion (exp);
+ type = TREE_TYPE (exp);
+ index = get_unwidened (exp, 0);
+ /* We can't strip a conversion from a signed type to an unsigned,
+ because if we did, int_fits_type_p would do the wrong thing
+ when checking case values for being in range,
+ and it's too hard to do the right thing. */
+ if (TREE_UNSIGNED (TREE_TYPE (exp))
+ == TREE_UNSIGNED (TREE_TYPE (index)))
+ exp = index;
+ }
+
+ expand_start_case (1, exp, type, "switch statement");
+
+ return exp;
+}
+
+/* CONSTP remembers whether or not all the intervening pointers in the `to'
+ type have been const. */
+int
+comp_ptr_ttypes_real (to, from, constp)
+ tree to, from;
+ int constp;
+{
+ for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from))
+ {
+ if (TREE_CODE (to) != TREE_CODE (from))
+ return 0;
+
+ if (TYPE_READONLY (from) > TYPE_READONLY (to)
+ || TYPE_VOLATILE (from) > TYPE_VOLATILE (to))
+ return 0;
+
+ if (! constp
+ && (TYPE_READONLY (to) > TYPE_READONLY (from)
+ || TYPE_VOLATILE (to) > TYPE_READONLY (from)))
+ return 0;
+ constp &= TYPE_READONLY (to);
+
+ if (TREE_CODE (to) != POINTER_TYPE)
+ return comptypes (TYPE_MAIN_VARIANT (to), TYPE_MAIN_VARIANT (from), 1);
+ }
+}
+
+/* When comparing, say, char ** to char const **, this function takes the
+ 'char *' and 'char const *'. Do not pass non-pointer types to this
+ function. */
+int
+comp_ptr_ttypes (to, from)
+ tree to, from;
+{
+ return comp_ptr_ttypes_real (to, from, 1);
+}
diff --git a/gnu/usr.bin/cc/cc1plus/typeck2.c b/gnu/usr.bin/cc/cc1plus/typeck2.c
new file mode 100644
index 0000000..871173f
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/typeck2.c
@@ -0,0 +1,1607 @@
+/* Report error messages, build initializers, and perform
+ some front-end optimizations for C++ compiler.
+ Copyright (C) '87, '88, '89, '92, 1993, 1994 Free Software Foundation, Inc.
+ Hacked by Michael Tiemann (tiemann@cygnus.com)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file is part of the C++ front end.
+ It contains routines to build C++ expressions given their operands,
+ including computing the types of the result, C and C++ specific error
+ checks, and some optimization.
+
+ There are also routines to build RETURN_STMT nodes and CASE_STMT nodes,
+ and to process initializations in declarations (since they work
+ like a strange sort of assignment). */
+
+#include "config.h"
+#include <stdio.h>
+#include "tree.h"
+#include "cp-tree.h"
+#include "flags.h"
+
+static tree process_init_constructor ();
+extern void pedwarn (), error ();
+
+extern int errorcount;
+extern int sorrycount;
+
+/* Print an error message stemming from an attempt to use
+ BASETYPE as a base class for TYPE. */
+tree
+error_not_base_type (basetype, type)
+ tree basetype, type;
+{
+ if (TREE_CODE (basetype) == FUNCTION_DECL)
+ basetype = DECL_CLASS_CONTEXT (basetype);
+ cp_error ("type `%T' is not a base type for type `%T'", basetype, type);
+ return error_mark_node;
+}
+
+tree
+binfo_or_else (parent_or_type, type)
+ tree parent_or_type, type;
+{
+ tree binfo;
+ if (TYPE_MAIN_VARIANT (parent_or_type) == TYPE_MAIN_VARIANT (type))
+ return TYPE_BINFO (parent_or_type);
+ if ((binfo = get_binfo (parent_or_type, TYPE_MAIN_VARIANT (type), 0)))
+ {
+ if (binfo == error_mark_node)
+ return NULL_TREE;
+ return binfo;
+ }
+ error_not_base_type (parent_or_type, type);
+ return NULL_TREE;
+}
+
+/* Print an error message stemming from an invalid use of an
+ aggregate type.
+
+ TYPE is the type or binfo which draws the error.
+ MSG is the message to print.
+ ARG is an optional argument which may provide more information. */
+void
+error_with_aggr_type (type, msg, arg)
+ tree type;
+ char *msg;
+ HOST_WIDE_INT arg;
+{
+ tree name;
+
+ if (TREE_CODE (type) == TREE_VEC)
+ type = BINFO_TYPE (type);
+
+ name = TYPE_NAME (type);
+ if (TREE_CODE (name) == TYPE_DECL)
+ name = DECL_NAME (name);
+ error (msg, IDENTIFIER_POINTER (name), arg);
+}
+
+/* According to ARM $7.1.6, "A `const' object may be initialized, but its
+ value may not be changed thereafter. Thus, we emit hard errors for these,
+ rather than just pedwarns. If `SOFT' is 1, then we just pedwarn. (For
+ example, conversions to references.) */
+void
+readonly_error (arg, string, soft)
+ tree arg;
+ char *string;
+ int soft;
+{
+ char *fmt;
+ void (*fn)();
+
+ if (soft)
+ fn = pedwarn;
+ else
+ fn = error;
+
+ if (TREE_CODE (arg) == COMPONENT_REF)
+ {
+ if (TYPE_READONLY (TREE_TYPE (TREE_OPERAND (arg, 0))))
+ fmt = "%s of member `%s' in read-only structure";
+ else
+ fmt = "%s of read-only member `%s'";
+ (*fn) (fmt, string, lang_printable_name (TREE_OPERAND (arg, 1)));
+ }
+ else if (TREE_CODE (arg) == VAR_DECL)
+ {
+ if (DECL_LANG_SPECIFIC (arg)
+ && DECL_IN_AGGR_P (arg)
+ && !TREE_STATIC (arg))
+ fmt = "%s of constant field `%s'";
+ else
+ fmt = "%s of read-only variable `%s'";
+ (*fn) (fmt, string, lang_printable_name (arg));
+ }
+ else if (TREE_CODE (arg) == PARM_DECL)
+ (*fn) ("%s of read-only parameter `%s'", string,
+ lang_printable_name (arg));
+ else if (TREE_CODE (arg) == INDIRECT_REF
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 0))) == REFERENCE_TYPE
+ && (TREE_CODE (TREE_OPERAND (arg, 0)) == VAR_DECL
+ || TREE_CODE (TREE_OPERAND (arg, 0)) == PARM_DECL))
+ (*fn) ("%s of read-only reference `%s'",
+ string, lang_printable_name (TREE_OPERAND (arg, 0)));
+ else if (TREE_CODE (arg) == RESULT_DECL)
+ (*fn) ("%s of read-only named return value `%s'",
+ string, lang_printable_name (arg));
+ else
+ (*fn) ("%s of read-only location", string);
+}
+
+/* Print an error message for invalid use of a type which declares
+ virtual functions which are not inheritable. */
+void
+abstract_virtuals_error (decl, type)
+ tree decl;
+ tree type;
+{
+ tree u = CLASSTYPE_ABSTRACT_VIRTUALS (type);
+
+ if (decl)
+ {
+ if (TREE_CODE (decl) == RESULT_DECL)
+ return;
+
+ if (TREE_CODE (decl) == VAR_DECL)
+ cp_error ("cannot declare variable `%D' to be of type `%T'",
+ decl, type);
+ else if (TREE_CODE (decl) == PARM_DECL)
+ cp_error ("cannot declare parameter `%D' to be of type `%T'",
+ decl, type);
+ else if (TREE_CODE (decl) == FIELD_DECL)
+ cp_error ("cannot declare field `%D' to be of type `%T'",
+ decl, type);
+ else if (TREE_CODE (decl) == FUNCTION_DECL
+ && TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
+ cp_error ("invalid return type for method `%#D'", decl);
+ else if (TREE_CODE (decl) == FUNCTION_DECL)
+ cp_error ("invalid return type for function `%#D'", decl);
+ }
+ else cp_error ("cannot allocate an object of type `%T'", type);
+ /* Only go through this once. */
+ if (TREE_PURPOSE (u) == NULL_TREE)
+ {
+ error (" since the following virtual functions are abstract:");
+ TREE_PURPOSE (u) = error_mark_node;
+ while (u)
+ {
+ cp_error ("\t%#D", TREE_VALUE (u));
+ u = TREE_CHAIN (u);
+ }
+ }
+ else cp_error (" since type `%T' has abstract virtual functions", type);
+}
+
+/* Print an error message for invalid use of a signature type.
+ Signatures are treated similar to abstract classes here, they
+ cannot be instantiated. */
+void
+signature_error (decl, type)
+ tree decl;
+ tree type;
+{
+ if (decl)
+ {
+ if (TREE_CODE (decl) == RESULT_DECL)
+ return;
+
+ if (TREE_CODE (decl) == VAR_DECL)
+ cp_error ("cannot declare variable `%D' to be of signature type `%T'",
+ decl, type);
+ else if (TREE_CODE (decl) == PARM_DECL)
+ cp_error ("cannot declare parameter `%D' to be of signature type `%T'",
+ decl, type);
+ else if (TREE_CODE (decl) == FIELD_DECL)
+ cp_error ("cannot declare field `%D' to be of signature type `%T'",
+ decl, type);
+ else if (TREE_CODE (decl) == FUNCTION_DECL
+ && TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
+ cp_error ("invalid return type for method `%#D'", decl);
+ else if (TREE_CODE (decl) == FUNCTION_DECL)
+ cp_error ("invalid return type for function `%#D'", decl);
+ }
+ else
+ cp_error ("cannot allocate an object of signature type `%T'", type);
+}
+
+/* Print an error message for invalid use of an incomplete type.
+ VALUE is the expression that was used (or 0 if that isn't known)
+ and TYPE is the type that was invalid. */
+
+void
+incomplete_type_error (value, type)
+ tree value;
+ tree type;
+{
+ char *errmsg;
+
+ /* Avoid duplicate error message. */
+ if (TREE_CODE (type) == ERROR_MARK)
+ return;
+
+ if (value != 0 && (TREE_CODE (value) == VAR_DECL
+ || TREE_CODE (value) == PARM_DECL))
+ error ("`%s' has an incomplete type",
+ IDENTIFIER_POINTER (DECL_NAME (value)));
+ else
+ {
+ retry:
+ /* We must print an error message. Be clever about what it says. */
+
+ switch (TREE_CODE (type))
+ {
+ case RECORD_TYPE:
+ errmsg = "invalid use of undefined type `struct %s'";
+ break;
+
+ case UNION_TYPE:
+ errmsg = "invalid use of undefined type `union %s'";
+ break;
+
+ case ENUMERAL_TYPE:
+ errmsg = "invalid use of undefined type `enum %s'";
+ break;
+
+ case VOID_TYPE:
+ error ("invalid use of void expression");
+ return;
+
+ case ARRAY_TYPE:
+ if (TYPE_DOMAIN (type))
+ {
+ type = TREE_TYPE (type);
+ goto retry;
+ }
+ error ("invalid use of array with unspecified bounds");
+ return;
+
+ case OFFSET_TYPE:
+ error ("invalid use of member type (did you forget the `&' ?)");
+ return;
+
+ default:
+ my_friendly_abort (108);
+ }
+
+ error_with_aggr_type (type, errmsg);
+ }
+}
+
+/* Like error(), but don't call report_error_function(). */
+static void
+ack (s, v, v2)
+ char *s;
+ HOST_WIDE_INT v;
+ HOST_WIDE_INT v2;
+{
+ extern char * progname;
+
+ if (input_filename)
+ fprintf (stderr, "%s:%d: ", input_filename, lineno);
+ else
+ fprintf (stderr, "%s: ", progname);
+
+ fprintf (stderr, s, v, v2);
+ fprintf (stderr, "\n");
+}
+
+/* There are times when the compiler can get very confused, confused
+ to the point of giving up by aborting, simply because of previous
+ input errors. It is much better to have the user go back and
+ correct those errors first, and see if it makes us happier, than it
+ is to abort on him. This is because when one has a 10,000 line
+ program, and the compiler comes back with ``core dump'', the user
+ is left not knowing even where to begin to fix things and no place
+ to even try and work around things.
+
+ The parameter is to uniquely identify the problem to the user, so
+ that they can say, I am having problem 59, and know that fix 7 will
+ probably solve their problem. Or, we can document what problem
+ 59 is, so they can understand how to work around it, should they
+ ever run into it.
+
+ Note, there will be no more calls in the C++ front end to abort,
+ because the C++ front end is so unreliable still. The C front end
+ can get away with calling abort, because for most of the calls to
+ abort on most machines, it, I suspect, can be proven that it is
+ impossible to ever call abort. The same is not yet true for C++,
+ one day, maybe it will be.
+
+ We used to tell people to "fix the above error[s] and try recompiling
+ the program" via a call to fatal, but that message tended to look
+ silly. So instead, we just do the equivalent of a call to fatal in the
+ same situation (call exit). */
+
+/* First used: 0 (reserved), Last used: 360. Free: */
+
+static int abortcount = 0;
+
+void
+my_friendly_abort (i)
+ int i;
+{
+ /* if the previous error came through here, i.e. report_error_function
+ ended up calling us again, don't just exit; we want a diagnostic of
+ some kind. */
+ if (abortcount == 1)
+ current_function_decl = NULL_TREE;
+ else if (errorcount > 0 || sorrycount > 0)
+ {
+ if (abortcount > 1)
+ {
+ if (i == 0)
+ ack ("Internal compiler error.");
+ else
+ ack ("Internal compiler error %d.", i);
+ ack ("Please submit a full bug report to `bug-g++@prep.ai.mit.edu'.");
+ }
+ else
+ error ("confused by earlier errors, bailing out");
+
+ exit (34);
+ }
+ ++abortcount;
+
+ if (i == 0)
+ error ("Internal compiler error.");
+ else
+ error ("Internal compiler error %d.", i);
+
+ fatal ("Please submit a full bug report to `bug-g++@prep.ai.mit.edu'.");
+}
+
+void
+my_friendly_assert (cond, where)
+ int cond, where;
+{
+ if (cond == 0)
+ my_friendly_abort (where);
+}
+
+/* Return nonzero if VALUE is a valid constant-valued expression
+ for use in initializing a static variable; one that can be an
+ element of a "constant" initializer.
+
+ Return 1 if the value is absolute; return 2 if it is relocatable.
+ We assume that VALUE has been folded as much as possible;
+ therefore, we do not need to check for such things as
+ arithmetic-combinations of integers. */
+
+static int
+initializer_constant_valid_p (value)
+ tree value;
+{
+ switch (TREE_CODE (value))
+ {
+ case CONSTRUCTOR:
+ return TREE_STATIC (value);
+
+ case INTEGER_CST:
+ case REAL_CST:
+ case STRING_CST:
+ return 1;
+
+ case ADDR_EXPR:
+ return 2;
+
+ case CONVERT_EXPR:
+ case NOP_EXPR:
+ /* Allow conversions between types of the same kind. */
+ if (TREE_CODE (TREE_TYPE (value))
+ == TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))))
+ return initializer_constant_valid_p (TREE_OPERAND (value, 0));
+ /* Allow (int) &foo provided int is as wide as a pointer. */
+ if (TREE_CODE (TREE_TYPE (value)) == INTEGER_TYPE
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (value, 0))) == POINTER_TYPE
+ && ! tree_int_cst_lt (TYPE_SIZE (TREE_TYPE (value)),
+ TYPE_SIZE (TREE_TYPE (TREE_OPERAND (value, 0)))))
+ return initializer_constant_valid_p (TREE_OPERAND (value, 0));
+ return 0;
+
+ case PLUS_EXPR:
+ {
+ int valid0 = initializer_constant_valid_p (TREE_OPERAND (value, 0));
+ int valid1 = initializer_constant_valid_p (TREE_OPERAND (value, 1));
+ if (valid0 == 1 && valid1 == 2)
+ return 2;
+ if (valid0 == 2 && valid1 == 1)
+ return 2;
+ return 0;
+ }
+
+ case MINUS_EXPR:
+ {
+ int valid0 = initializer_constant_valid_p (TREE_OPERAND (value, 0));
+ int valid1 = initializer_constant_valid_p (TREE_OPERAND (value, 1));
+ if (valid0 == 2 && valid1 == 1)
+ return 2;
+ return 0;
+ }
+
+ default:
+ return 0;
+ }
+}
+
+/* Perform appropriate conversions on the initial value of a variable,
+ store it in the declaration DECL,
+ and print any error messages that are appropriate.
+ If the init is invalid, store an ERROR_MARK.
+
+ C++: Note that INIT might be a TREE_LIST, which would mean that it is
+ a base class initializer for some aggregate type, hopefully compatible
+ with DECL. If INIT is a single element, and DECL is an aggregate
+ type, we silently convert INIT into a TREE_LIST, allowing a constructor
+ to be called.
+
+ If INIT is a TREE_LIST and there is no constructor, turn INIT
+ into a CONSTRUCTOR and use standard initialization techniques.
+ Perhaps a warning should be generated?
+
+ Returns value of initializer if initialization could not be
+ performed for static variable. In that case, caller must do
+ the storing. */
+
+tree
+store_init_value (decl, init)
+ tree decl, init;
+{
+ register tree value, type;
+
+ /* If variable's type was invalidly declared, just ignore it. */
+
+ type = TREE_TYPE (decl);
+ if (TREE_CODE (type) == ERROR_MARK)
+ return NULL_TREE;
+
+ /* Take care of C++ business up here. */
+ type = TYPE_MAIN_VARIANT (type);
+
+ /* implicitly tests if IS_AGGR_TYPE. */
+ if (TYPE_NEEDS_CONSTRUCTING (type) && TREE_CODE (init) != CONSTRUCTOR)
+ my_friendly_abort (109);
+ else if (IS_AGGR_TYPE (type))
+ {
+ /* Although we are not allowed to declare variables of signature
+ type, we complain about a possible constructor call in such a
+ declaration as well. */
+ if (TREE_CODE (init) == TREE_LIST
+ && IS_SIGNATURE (type))
+ {
+ cp_error ("constructor syntax cannot be used with signature type `%T'",
+ type);
+ init = error_mark_node;
+ }
+ else if (TREE_CODE (init) == TREE_LIST)
+ {
+ cp_error ("constructor syntax used, but no constructor declared for type `%T'", type);
+ init = build_nt (CONSTRUCTOR, NULL_TREE, nreverse (init));
+ }
+#if 0
+ if (TREE_CODE (init) == CONSTRUCTOR)
+ {
+ tree field;
+ tree funcs;
+ int func;
+
+ /* Check that we're really an aggregate as ARM 8.4.1 defines it. */
+ if (CLASSTYPE_N_BASECLASSES (type))
+ cp_error_at ("initializer list construction illegal for derived class object `%D'", decl);
+ if (CLASSTYPE_VTBL_PTR (type))
+ cp_error_at ("initializer list construction illegal for polymorphic class object `%D'", decl);
+ if (TYPE_NEEDS_CONSTRUCTING (type))
+ {
+ cp_error_at ("initializer list construction illegal for `%D'", decl);
+ error ("due to the presence of a constructor");
+ }
+ for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+ if (TREE_PRIVATE (field) || TREE_PROTECTED (field))
+ {
+ cp_error_at ("initializer list construction illegal for `%D'", decl);
+ cp_error_at ("due to non-public access of member `%D'", field);
+ }
+ funcs = TYPE_METHODS (type);
+ if (funcs)
+ for (func = 0; func < TREE_VEC_LENGTH (funcs); func++)
+ {
+ field = TREE_VEC_ELT (funcs, func);
+ if (field && (TREE_PRIVATE (field) || TREE_PROTECTED (field)))
+ {
+ cp_error_at ("initializer list construction illegal for `%D'", decl);
+ cp_error_at ("due to non-public access of member `%D'", field);
+ }
+ }
+ }
+#endif
+ }
+ else if (TREE_CODE (init) == TREE_LIST
+ && TREE_TYPE (init) != unknown_type_node)
+ {
+ if (TREE_CODE (decl) == RESULT_DECL)
+ {
+ if (TREE_CHAIN (init))
+ {
+ warning ("comma expression used to initialize return value");
+ init = build_compound_expr (init);
+ }
+ else
+ init = TREE_VALUE (init);
+ }
+ else if (TREE_TYPE (init) != 0
+ && TREE_CODE (TREE_TYPE (init)) == OFFSET_TYPE)
+ {
+ /* Use the type of our variable to instantiate
+ the type of our initializer. */
+ init = instantiate_type (type, init, 1);
+ }
+ else if (TREE_CODE (init) == TREE_LIST
+ && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
+ {
+ error ("cannot initialize arrays using this syntax");
+ return NULL_TREE;
+ }
+ else
+ {
+ /* We get here with code like `int a (2);' */
+
+ if (TREE_CHAIN (init) != NULL_TREE)
+ {
+ pedwarn ("initializer list being treated as compound expression");
+ init = build_compound_expr (init);
+ }
+ else
+ init = TREE_VALUE (init);
+ }
+ }
+
+ /* End of special C++ code. */
+
+ /* Digest the specified initializer into an expression. */
+
+ value = digest_init (type, init, (tree *) 0);
+
+ /* Store the expression if valid; else report error. */
+
+ if (TREE_CODE (value) == ERROR_MARK)
+ ;
+ else if (TREE_STATIC (decl)
+ && (! TREE_CONSTANT (value)
+ || ! initializer_constant_valid_p (value)
+#if 0
+ /* A STATIC PUBLIC int variable doesn't have to be
+ run time inited when doing pic. (mrs) */
+ /* Since ctors and dtors are the only things that can
+ reference vtables, and they are always written down
+ the the vtable definition, we can leave the
+ vtables in initialized data space.
+ However, other initialized data cannot be initialized
+ this way. Instead a global file-level initializer
+ must do the job. */
+ || (flag_pic && !DECL_VIRTUAL_P (decl) && TREE_PUBLIC (decl))
+#endif
+ ))
+
+ return value;
+ else
+ {
+ if (pedantic && TREE_CODE (value) == CONSTRUCTOR
+ /* Don't complain about non-constant initializers of
+ signature tables and signature pointers/references. */
+ && ! (TYPE_LANG_SPECIFIC (type)
+ && (IS_SIGNATURE (type)
+ || IS_SIGNATURE_POINTER (type)
+ || IS_SIGNATURE_REFERENCE (type))))
+ {
+ if (! TREE_CONSTANT (value) || ! TREE_STATIC (value))
+ pedwarn ("ANSI C++ forbids non-constant aggregate initializer expressions");
+ }
+ }
+ DECL_INITIAL (decl) = value;
+ return NULL_TREE;
+}
+
+/* Digest the parser output INIT as an initializer for type TYPE.
+ Return a C expression of type TYPE to represent the initial value.
+
+ If TAIL is nonzero, it points to a variable holding a list of elements
+ of which INIT is the first. We update the list stored there by
+ removing from the head all the elements that we use.
+ Normally this is only one; we use more than one element only if
+ TYPE is an aggregate and INIT is not a constructor. */
+
+tree
+digest_init (type, init, tail)
+ tree type, init, *tail;
+{
+ enum tree_code code = TREE_CODE (type);
+ tree element = 0;
+ tree old_tail_contents;
+ /* Nonzero if INIT is a braced grouping, which comes in as a CONSTRUCTOR
+ tree node which has no TREE_TYPE. */
+ int raw_constructor;
+
+ /* By default, assume we use one element from a list.
+ We correct this later in the sole case where it is not true. */
+
+ if (tail)
+ {
+ old_tail_contents = *tail;
+ *tail = TREE_CHAIN (*tail);
+ }
+
+ if (init == error_mark_node || (TREE_CODE (init) == TREE_LIST
+ && TREE_VALUE (init) == error_mark_node))
+ return error_mark_node;
+
+ /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
+ if (TREE_CODE (init) == NON_LVALUE_EXPR)
+ init = TREE_OPERAND (init, 0);
+
+ if (init && TREE_TYPE (init) && TYPE_PTRMEMFUNC_P (type))
+ init = default_conversion (init);
+
+ if (init && TYPE_PTRMEMFUNC_P (type)
+ && ((TREE_CODE (init) == ADDR_EXPR
+ && TREE_CODE (TREE_TYPE (init)) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (TREE_TYPE (init))) == METHOD_TYPE)
+ || TREE_CODE (init) == TREE_LIST
+ || integer_zerop (init)
+ || (TREE_TYPE (init) && TYPE_PTRMEMFUNC_P (TREE_TYPE (init)))))
+ {
+ return build_ptrmemfunc (TYPE_PTRMEMFUNC_FN_TYPE (type), init, 0);
+ }
+
+ raw_constructor = TREE_CODE (init) == CONSTRUCTOR && TREE_TYPE (init) == 0;
+
+ if (init && raw_constructor
+ && CONSTRUCTOR_ELTS (init) != 0
+ && TREE_CHAIN (CONSTRUCTOR_ELTS (init)) == 0)
+ {
+ element = TREE_VALUE (CONSTRUCTOR_ELTS (init));
+ /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
+ if (element && TREE_CODE (element) == NON_LVALUE_EXPR)
+ element = TREE_OPERAND (element, 0);
+ if (element == error_mark_node)
+ return element;
+ }
+
+ /* Any type can be initialized from an expression of the same type,
+ optionally with braces. */
+
+ if (init && TREE_TYPE (init)
+ && (TYPE_MAIN_VARIANT (TREE_TYPE (init)) == type
+ || (code == ARRAY_TYPE && comptypes (TREE_TYPE (init), type, 1))))
+ {
+ if (pedantic && code == ARRAY_TYPE
+ && TREE_CODE (init) != STRING_CST)
+ pedwarn ("ANSI C++ forbids initializing array from array expression");
+ if (TREE_CODE (init) == CONST_DECL)
+ init = DECL_INITIAL (init);
+ else if (TREE_READONLY_DECL_P (init))
+ init = decl_constant_value (init);
+ return init;
+ }
+
+ if (element && (TREE_TYPE (element) == type
+ || (code == ARRAY_TYPE && TREE_TYPE (element)
+ && comptypes (TREE_TYPE (element), type, 1))))
+ {
+ if (pedantic && code == ARRAY_TYPE)
+ pedwarn ("ANSI C++ forbids initializing array from array expression");
+ if (pedantic && (code == RECORD_TYPE || code == UNION_TYPE))
+ pedwarn ("ANSI C++ forbids single nonscalar initializer with braces");
+ if (TREE_CODE (element) == CONST_DECL)
+ element = DECL_INITIAL (element);
+ else if (TREE_READONLY_DECL_P (element))
+ element = decl_constant_value (element);
+ return element;
+ }
+
+ /* Initialization of an array of chars from a string constant
+ optionally enclosed in braces. */
+
+ if (code == ARRAY_TYPE)
+ {
+ tree typ1 = TYPE_MAIN_VARIANT (TREE_TYPE (type));
+ if ((typ1 == char_type_node
+ || typ1 == signed_char_type_node
+ || typ1 == unsigned_char_type_node
+ || typ1 == unsigned_wchar_type_node
+ || typ1 == signed_wchar_type_node)
+ && ((init && TREE_CODE (init) == STRING_CST)
+ || (element && TREE_CODE (element) == STRING_CST)))
+ {
+ tree string = element ? element : init;
+
+ if ((TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (string)))
+ != char_type_node)
+ && TYPE_PRECISION (typ1) == BITS_PER_UNIT)
+ {
+ error ("char-array initialized from wide string");
+ return error_mark_node;
+ }
+ if ((TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (string)))
+ == char_type_node)
+ && TYPE_PRECISION (typ1) != BITS_PER_UNIT)
+ {
+ error ("int-array initialized from non-wide string");
+ return error_mark_node;
+ }
+
+ if (pedantic
+ && typ1 != char_type_node
+ && typ1 != signed_char_type_node
+ && typ1 != unsigned_char_type_node)
+ pedwarn ("ANSI C++ forbids string initializer except for `char' elements");
+ TREE_TYPE (string) = type;
+ if (TYPE_DOMAIN (type) != 0
+ && TREE_CONSTANT (TYPE_SIZE (type)))
+ {
+ register int size
+ = TREE_INT_CST_LOW (TYPE_SIZE (type));
+ size = (size + BITS_PER_UNIT - 1) / BITS_PER_UNIT;
+ /* In C it is ok to subtract 1 from the length of the string
+ because it's ok to ignore the terminating null char that is
+ counted in the length of the constant, but in C++ this would
+ be invalid. */
+ if (size < TREE_STRING_LENGTH (string))
+ pedwarn ("initializer-string for array of chars is too long");
+ }
+ return string;
+ }
+ }
+
+ /* Handle scalar types, including conversions,
+ and signature pointers and references. */
+
+ if (code == INTEGER_TYPE || code == REAL_TYPE || code == POINTER_TYPE
+ || code == ENUMERAL_TYPE || code == REFERENCE_TYPE
+ || code == BOOLEAN_TYPE
+ || (code == RECORD_TYPE && ! raw_constructor
+ && (IS_SIGNATURE_POINTER (type) || IS_SIGNATURE_REFERENCE (type))))
+ {
+ if (raw_constructor)
+ {
+ if (element == 0)
+ {
+ error ("initializer for scalar variable requires one element");
+ return error_mark_node;
+ }
+ init = element;
+ }
+
+ return convert_for_initialization (0, type, init, LOOKUP_NORMAL,
+ "initialization", NULL_TREE, 0);
+ }
+
+ /* Come here only for records and arrays (and unions with constructors). */
+
+ if (TYPE_SIZE (type) && ! TREE_CONSTANT (TYPE_SIZE (type)))
+ {
+ cp_error ("variable-sized object of type `%T' may not be initialized",
+ type);
+ return error_mark_node;
+ }
+
+ if (code == ARRAY_TYPE || code == RECORD_TYPE || code == UNION_TYPE)
+ {
+ if (raw_constructor)
+ return process_init_constructor (type, init, (tree *)0);
+ else if (TYPE_NEEDS_CONSTRUCTING (type))
+ {
+ /* This can only be reached when caller is initializing
+ ARRAY_TYPE. In that case, we don't want to convert
+ INIT to TYPE. We will let `expand_vec_init' do it. */
+ return init;
+ }
+ else if (tail != 0)
+ {
+ *tail = old_tail_contents;
+ return process_init_constructor (type, 0, tail);
+ }
+ else if (flag_traditional)
+ /* Traditionally one can say `char x[100] = 0;'. */
+ return process_init_constructor (type,
+ build_nt (CONSTRUCTOR, 0,
+ tree_cons (0, init, 0)),
+ 0);
+ if (code != ARRAY_TYPE)
+ return convert_for_initialization (0, type, init, LOOKUP_NORMAL,
+ "initialization", NULL_TREE, 0);
+ }
+
+ error ("invalid initializer");
+ return error_mark_node;
+}
+
+/* Process a constructor for a variable of type TYPE.
+ The constructor elements may be specified either with INIT or with ELTS,
+ only one of which should be non-null.
+
+ If INIT is specified, it is a CONSTRUCTOR node which is specifically
+ and solely for initializing this datum.
+
+ If ELTS is specified, it is the address of a variable containing
+ a list of expressions. We take as many elements as we need
+ from the head of the list and update the list.
+
+ In the resulting constructor, TREE_CONSTANT is set if all elts are
+ constant, and TREE_STATIC is set if, in addition, all elts are simple enough
+ constants that the assembler and linker can compute them. */
+
+static tree
+process_init_constructor (type, init, elts)
+ tree type, init, *elts;
+{
+ register tree tail;
+ /* List of the elements of the result constructor,
+ in reverse order. */
+ register tree members = NULL;
+ tree result;
+ int allconstant = 1;
+ int allsimple = 1;
+ int erroneous = 0;
+
+ /* Make TAIL be the list of elements to use for the initialization,
+ no matter how the data was given to us. */
+
+ if (elts)
+ {
+ if (warn_missing_braces)
+ warning ("aggregate has a partly bracketed initializer");
+ tail = *elts;
+ }
+ else
+ tail = CONSTRUCTOR_ELTS (init);
+
+ /* Gobble as many elements as needed, and make a constructor or initial value
+ for each element of this aggregate. Chain them together in result.
+ If there are too few, use 0 for each scalar ultimate component. */
+
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ tree domain = TYPE_DOMAIN (type);
+ register long len;
+ register int i;
+
+ if (domain)
+ len = (TREE_INT_CST_LOW (TYPE_MAX_VALUE (domain))
+ - TREE_INT_CST_LOW (TYPE_MIN_VALUE (domain))
+ + 1);
+ else
+ len = -1; /* Take as many as there are */
+
+ for (i = 0; (len < 0 || i < len) && tail != 0; i++)
+ {
+ register tree next1;
+
+ if (TREE_VALUE (tail) != 0)
+ {
+ tree tail1 = tail;
+ next1 = digest_init (TYPE_MAIN_VARIANT (TREE_TYPE (type)),
+ TREE_VALUE (tail), &tail1);
+ my_friendly_assert (tail1 == 0
+ || TREE_CODE (tail1) == TREE_LIST, 319);
+ if (tail == tail1 && len < 0)
+ {
+ error ("non-empty initializer for array of empty elements");
+ /* Just ignore what we were supposed to use. */
+ tail1 = 0;
+ }
+ tail = tail1;
+ }
+ else
+ {
+ next1 = error_mark_node;
+ tail = TREE_CHAIN (tail);
+ }
+
+ if (next1 == error_mark_node)
+ erroneous = 1;
+ else if (!TREE_CONSTANT (next1))
+ allconstant = 0;
+ else if (! initializer_constant_valid_p (next1))
+ allsimple = 0;
+ members = tree_cons (NULL_TREE, next1, members);
+ }
+ }
+ if (TREE_CODE (type) == RECORD_TYPE)
+ {
+ register tree field;
+
+ if (tail)
+ {
+ if (TYPE_USES_VIRTUAL_BASECLASSES (type))
+ {
+ sorry ("initializer list for object of class with virtual baseclasses");
+ return error_mark_node;
+ }
+
+ if (TYPE_BINFO_BASETYPES (type))
+ {
+ sorry ("initializer list for object of class with baseclasses");
+ return error_mark_node;
+ }
+
+ if (TYPE_VIRTUAL_P (type))
+ {
+ sorry ("initializer list for object using virtual functions");
+ return error_mark_node;
+ }
+ }
+
+ for (field = TYPE_FIELDS (type); field && tail;
+ field = TREE_CHAIN (field))
+ {
+ register tree next1;
+
+ if (! DECL_NAME (field))
+ {
+ members = tree_cons (field, integer_zero_node, members);
+ continue;
+ }
+
+ if (TREE_CODE (field) != FIELD_DECL)
+ continue;
+
+ if (TREE_VALUE (tail) != 0)
+ {
+ tree tail1 = tail;
+
+ next1 = digest_init (TREE_TYPE (field),
+ TREE_VALUE (tail), &tail1);
+ my_friendly_assert (tail1 == 0
+ || TREE_CODE (tail1) == TREE_LIST, 320);
+ tail = tail1;
+ }
+ else
+ {
+ next1 = error_mark_node;
+ tail = TREE_CHAIN (tail);
+ }
+
+ if (next1 == error_mark_node)
+ erroneous = 1;
+ else if (!TREE_CONSTANT (next1))
+ allconstant = 0;
+ else if (! initializer_constant_valid_p (next1))
+ allsimple = 0;
+ members = tree_cons (field, next1, members);
+ }
+ for (; field; field = TREE_CHAIN (field))
+ {
+ if (TREE_CODE (field) != FIELD_DECL)
+ continue;
+
+ /* Does this field have a default initialization? */
+ if (DECL_INITIAL (field))
+ {
+ register tree next1 = DECL_INITIAL (field);
+ if (TREE_CODE (next1) == ERROR_MARK)
+ erroneous = 1;
+ else if (!TREE_CONSTANT (next1))
+ allconstant = 0;
+ else if (! initializer_constant_valid_p (next1))
+ allsimple = 0;
+ members = tree_cons (field, next1, members);
+ }
+ else if (TREE_READONLY (field))
+ error ("uninitialized const member `%s'",
+ IDENTIFIER_POINTER (DECL_NAME (field)));
+ else if (TYPE_LANG_SPECIFIC (TREE_TYPE (field))
+ && CLASSTYPE_READONLY_FIELDS_NEED_INIT (TREE_TYPE (field)))
+ error ("member `%s' with uninitialized const fields",
+ IDENTIFIER_POINTER (DECL_NAME (field)));
+ else if (TREE_CODE (TREE_TYPE (field)) == REFERENCE_TYPE)
+ error ("member `%s' is uninitialized reference",
+ IDENTIFIER_POINTER (DECL_NAME (field)));
+ }
+ }
+
+ if (TREE_CODE (type) == UNION_TYPE)
+ {
+ register tree field = TYPE_FIELDS (type);
+ register tree next1;
+
+ /* Find the first named field. ANSI decided in September 1990
+ that only named fields count here. */
+ while (field && DECL_NAME (field) == 0)
+ field = TREE_CHAIN (field);
+
+ /* If this element specifies a field, initialize via that field. */
+ if (TREE_PURPOSE (tail) != NULL_TREE)
+ {
+ int win = 0;
+
+ if (TREE_CODE (TREE_PURPOSE (tail)) == FIELD_DECL)
+ /* Handle the case of a call by build_c_cast. */
+ field = TREE_PURPOSE (tail), win = 1;
+ else if (TREE_CODE (TREE_PURPOSE (tail)) != IDENTIFIER_NODE)
+ error ("index value instead of field name in union initializer");
+ else
+ {
+ tree temp;
+ for (temp = TYPE_FIELDS (type);
+ temp;
+ temp = TREE_CHAIN (temp))
+ if (DECL_NAME (temp) == TREE_PURPOSE (tail))
+ break;
+ if (temp)
+ field = temp, win = 1;
+ else
+ error ("no field `%s' in union being initialized",
+ IDENTIFIER_POINTER (TREE_PURPOSE (tail)));
+ }
+ if (!win)
+ TREE_VALUE (tail) = error_mark_node;
+ }
+ else if (field == 0)
+ {
+ cp_error ("union `%T' with no named members cannot be initialized",
+ type);
+ TREE_VALUE (tail) = error_mark_node;
+ }
+
+ if (TREE_VALUE (tail) != 0)
+ {
+ tree tail1 = tail;
+
+ next1 = digest_init (TREE_TYPE (field),
+ TREE_VALUE (tail), &tail1);
+ if (tail1 != 0 && TREE_CODE (tail1) != TREE_LIST)
+ my_friendly_abort (357);
+ tail = tail1;
+ }
+ else
+ {
+ next1 = error_mark_node;
+ tail = TREE_CHAIN (tail);
+ }
+
+ if (next1 == error_mark_node)
+ erroneous = 1;
+ else if (!TREE_CONSTANT (next1))
+ allconstant = 0;
+ else if (initializer_constant_valid_p (next1) == 0)
+ allsimple = 0;
+ members = tree_cons (field, next1, members);
+ }
+
+ /* If arguments were specified as a list, just remove the ones we used. */
+ if (elts)
+ *elts = tail;
+ /* If arguments were specified as a constructor,
+ complain unless we used all the elements of the constructor. */
+ else if (tail)
+ pedwarn ("excess elements in aggregate initializer");
+
+ if (erroneous)
+ return error_mark_node;
+
+ result = build (CONSTRUCTOR, type, NULL_TREE, nreverse (members));
+ if (init)
+ TREE_HAS_CONSTRUCTOR (result) = TREE_HAS_CONSTRUCTOR (init);
+ if (allconstant) TREE_CONSTANT (result) = 1;
+ if (allconstant && allsimple) TREE_STATIC (result) = 1;
+ return result;
+}
+
+/* Given a structure or union value DATUM, construct and return
+ the structure or union component which results from narrowing
+ that value by the types specified in TYPES. For example, given the
+ hierarchy
+
+ class L { int ii; };
+ class A : L { ... };
+ class B : L { ... };
+ class C : A, B { ... };
+
+ and the declaration
+
+ C x;
+
+ then the expression
+
+ x::C::A::L::ii refers to the ii member of the L part of
+ of A part of the C object named by X. In this case,
+ DATUM would be x, and TYPES would be a SCOPE_REF consisting of
+
+ SCOPE_REF
+ SCOPE_REF
+ C A
+ L
+
+ The last entry in the SCOPE_REF is always an IDENTIFIER_NODE.
+
+*/
+
+tree
+build_scoped_ref (datum, types)
+ tree datum;
+ tree types;
+{
+ tree ref;
+ tree type = TREE_TYPE (datum);
+
+ if (datum == error_mark_node)
+ return error_mark_node;
+
+ if (TREE_CODE (type) == REFERENCE_TYPE)
+ type = TREE_TYPE (type);
+
+ type = TYPE_MAIN_VARIANT (type);
+
+ if (TREE_CODE (types) == SCOPE_REF)
+ {
+ /* We have some work to do. */
+ struct type_chain { tree type; struct type_chain *next; } *chain = 0, *head = 0, scratch;
+ ref = build_unary_op (ADDR_EXPR, datum, 0);
+ while (TREE_CODE (types) == SCOPE_REF)
+ {
+ tree t = TREE_OPERAND (types, 1);
+ if (is_aggr_typedef (t, 1))
+ {
+ head = (struct type_chain *)alloca (sizeof (struct type_chain));
+ head->type = IDENTIFIER_TYPE_VALUE (t);
+ head->next = chain;
+ chain = head;
+ types = TREE_OPERAND (types, 0);
+ }
+ else return error_mark_node;
+ }
+ if (! is_aggr_typedef (types, 1))
+ return error_mark_node;
+
+ head = &scratch;
+ head->type = IDENTIFIER_TYPE_VALUE (types);
+ head->next = chain;
+ chain = head;
+ while (chain)
+ {
+ tree binfo = chain->type;
+ type = TREE_TYPE (TREE_TYPE (ref));
+ if (binfo != TYPE_BINFO (type))
+ {
+ binfo = get_binfo (binfo, type, 1);
+ if (binfo == error_mark_node)
+ return error_mark_node;
+ if (binfo == 0)
+ return error_not_base_type (chain->type, type);
+ ref = convert_pointer_to (binfo, ref);
+ }
+ chain = chain->next;
+ }
+ return build_indirect_ref (ref, "(compiler error in build_scoped_ref)");
+ }
+
+ /* This is an easy conversion. */
+ if (is_aggr_typedef (types, 1))
+ {
+ tree binfo = TYPE_BINFO (IDENTIFIER_TYPE_VALUE (types));
+ if (binfo != TYPE_BINFO (type))
+ {
+ binfo = get_binfo (binfo, type, 1);
+ if (binfo == error_mark_node)
+ return error_mark_node;
+ if (binfo == 0)
+ return error_not_base_type (IDENTIFIER_TYPE_VALUE (types), type);
+ }
+
+ switch (TREE_CODE (datum))
+ {
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ case FLOAT_EXPR:
+ case FIX_TRUNC_EXPR:
+ case FIX_FLOOR_EXPR:
+ case FIX_ROUND_EXPR:
+ case FIX_CEIL_EXPR:
+ ref = convert_pointer_to (binfo,
+ build_unary_op (ADDR_EXPR, TREE_OPERAND (datum, 0), 0));
+ break;
+ default:
+ ref = convert_pointer_to (binfo,
+ build_unary_op (ADDR_EXPR, datum, 0));
+ }
+ return build_indirect_ref (ref, "(compiler error in build_scoped_ref)");
+ }
+ return error_mark_node;
+}
+
+/* Build a reference to an object specified by the C++ `->' operator.
+ Usually this just involves dereferencing the object, but if the
+ `->' operator is overloaded, then such overloads must be
+ performed until an object which does not have the `->' operator
+ overloaded is found. An error is reported when circular pointer
+ delegation is detected. */
+tree
+build_x_arrow (datum)
+ tree datum;
+{
+ tree types_memoized = NULL_TREE;
+ register tree rval = datum;
+ tree type = TREE_TYPE (rval);
+ tree last_rval;
+
+ if (type == error_mark_node)
+ return error_mark_node;
+
+ if (TREE_CODE (rval) == OFFSET_REF)
+ {
+ rval = resolve_offset_ref (datum);
+ type = TREE_TYPE (rval);
+ }
+
+ if (TREE_CODE (type) == REFERENCE_TYPE)
+ {
+ rval = convert_from_reference (rval);
+ type = TREE_TYPE (rval);
+ }
+
+ if (IS_AGGR_TYPE (type) && TYPE_OVERLOADS_ARROW (type))
+ {
+ while ((rval = build_opfncall (COMPONENT_REF, LOOKUP_NORMAL, rval, NULL_TREE, NULL_TREE)))
+ {
+ if (rval == error_mark_node)
+ return error_mark_node;
+
+ if (value_member (TREE_TYPE (rval), types_memoized))
+ {
+ error ("circular pointer delegation detected");
+ return error_mark_node;
+ }
+ else
+ {
+ types_memoized = tree_cons (NULL_TREE, TREE_TYPE (rval),
+ types_memoized);
+ }
+ last_rval = rval;
+ }
+ if (TREE_CODE (TREE_TYPE (last_rval)) == REFERENCE_TYPE)
+ last_rval = convert_from_reference (last_rval);
+ }
+ else
+ last_rval = default_conversion (rval);
+
+ /* Signature pointers are not dereferenced. */
+ if (TYPE_LANG_SPECIFIC (TREE_TYPE (last_rval))
+ && IS_SIGNATURE_POINTER (TREE_TYPE (last_rval)))
+ return last_rval;
+
+ if (TREE_CODE (TREE_TYPE (last_rval)) == POINTER_TYPE)
+ return build_indirect_ref (last_rval, NULL_PTR);
+
+ if (types_memoized)
+ error ("result of `operator->()' yields non-pointer result");
+ else
+ error ("base operand of `->' is not a pointer");
+ return error_mark_node;
+}
+
+/* Make an expression to refer to the COMPONENT field of
+ structure or union value DATUM. COMPONENT is an arbitrary
+ expression. DATUM has not already been checked out to be of
+ aggregate type.
+
+ For C++, COMPONENT may be a TREE_LIST. This happens when we must
+ return an object of member type to a method of the current class,
+ but there is not yet enough typing information to know which one.
+ As a special case, if there is only one method by that name,
+ it is returned. Otherwise we return an expression which other
+ routines will have to know how to deal with later. */
+tree
+build_m_component_ref (datum, component)
+ tree datum, component;
+{
+ tree type;
+ tree objtype = TREE_TYPE (datum);
+ tree rettype;
+
+ if (TYPE_PTRMEMFUNC_P (TREE_TYPE (component)))
+ {
+ type = TREE_TYPE (TYPE_PTRMEMFUNC_FN_TYPE (TREE_TYPE (component)));
+ rettype = type;
+ }
+ else
+ {
+ component = build_indirect_ref (component, NULL_PTR);
+ type = TREE_TYPE (component);
+ rettype = TREE_TYPE (TREE_TYPE (component));
+ }
+
+ if (datum == error_mark_node || component == error_mark_node)
+ return error_mark_node;
+
+ if (TREE_CODE (type) != OFFSET_TYPE && TREE_CODE (type) != METHOD_TYPE)
+ {
+ cp_error ("`%E' cannot be used as a member pointer, since it is of type `%T'", component, type);
+ return error_mark_node;
+ }
+
+ if (TREE_CODE (objtype) == REFERENCE_TYPE)
+ objtype = TREE_TYPE (objtype);
+
+ if (! IS_AGGR_TYPE (objtype))
+ {
+ cp_error ("cannot apply member pointer `%E' to `%E'", component, datum);
+ cp_error ("which is of non-aggregate type `%T'", objtype);
+ return error_mark_node;
+ }
+
+ if (! comptypes (TYPE_METHOD_BASETYPE (type), objtype, 0))
+ {
+ cp_error ("member type `%T::' incompatible with object type `%T'",
+ TYPE_METHOD_BASETYPE (type), objtype);
+ return error_mark_node;
+ }
+
+ return build (OFFSET_REF, rettype, datum, component);
+}
+
+/* Return a tree node for the expression TYPENAME '(' PARMS ')'.
+
+ Because we cannot tell whether this construct is really a call to a
+ constructor or a request for a type conversion, we try both, and
+ report any ambiguities we find. */
+tree
+build_functional_cast (exp, parms)
+ tree exp;
+ tree parms;
+{
+ /* This is either a call to a constructor,
+ or a C cast in C++'s `functional' notation. */
+ tree type, name = NULL_TREE;
+ tree expr_as_ctor = NULL_TREE;
+
+ if (exp == error_mark_node || parms == error_mark_node)
+ return error_mark_node;
+
+ if (TREE_CODE (exp) == IDENTIFIER_NODE)
+ {
+ name = exp;
+
+ if (IDENTIFIER_HAS_TYPE_VALUE (exp))
+ /* Either an enum or an aggregate type. */
+ type = IDENTIFIER_TYPE_VALUE (exp);
+ else
+ {
+ type = lookup_name (exp, 1);
+ if (!type || TREE_CODE (type) != TYPE_DECL)
+ {
+ cp_error ("`%T' fails to be a typedef or built-in type", name);
+ return error_mark_node;
+ }
+ type = TREE_TYPE (type);
+ }
+ }
+ else
+ type = exp;
+
+ if (IS_SIGNATURE (type))
+ {
+ error ("signature type not allowed in cast or constructor expression");
+ return error_mark_node;
+ }
+
+ /* Prepare to evaluate as a call to a constructor. If this expression
+ is actually used, for example,
+
+ return X (arg1, arg2, ...);
+
+ then the slot being initialized will be filled in. */
+
+ if (name == NULL_TREE)
+ {
+ name = TYPE_NAME (type);
+ if (TREE_CODE (name) == TYPE_DECL)
+ name = DECL_NAME (name);
+ }
+
+ if (! IS_AGGR_TYPE (type))
+ {
+ /* this must build a C cast */
+ if (parms == NULL_TREE)
+ return build1 (NOP_EXPR, type, integer_zero_node);
+ else if (TREE_CHAIN (parms) != NULL_TREE)
+ {
+ pedwarn ("initializer list being treated as compound expression");
+ parms = build_compound_expr (parms);
+ }
+ return build_c_cast (type, parms);
+ }
+
+ if (TYPE_SIZE (type) == NULL_TREE)
+ {
+ cp_error ("type `%T' is not yet defined", type);
+ return error_mark_node;
+ }
+
+ if (parms && TREE_CHAIN (parms) == NULL_TREE)
+ return build_c_cast (type, parms);
+
+ expr_as_ctor = build_method_call (NULL_TREE, name, parms,
+ NULL_TREE, LOOKUP_NORMAL);
+
+ if (expr_as_ctor == error_mark_node)
+ return error_mark_node;
+
+ if (current_function_decl)
+ return build_cplus_new (type, expr_as_ctor, 1);
+
+ {
+ register tree parm = TREE_OPERAND (expr_as_ctor, 1);
+
+ /* Initializers for static variables and parameters have
+ to handle doing the initialization and cleanup themselves. */
+ my_friendly_assert (TREE_CODE (expr_as_ctor) == CALL_EXPR, 322);
+#if 0
+ /* The following assertion fails in cases where we are initializing
+ a static member variable of a particular instance of a template
+ class with a call to a constructor of the given instance, as in:
+
+ TMPL<int> object = TMPL<int>();
+
+ Curiously, the assertion does not fail if we do the same thing
+ for a static member of a non-template class, as in:
+
+ T object = T();
+
+ I can't see why we should care here whether or not the initializer
+ expression involves a call to `new', so for the time being, it
+ seems best to just avoid doing this assertion. */
+ my_friendly_assert (TREE_CALLS_NEW (TREE_VALUE (parm)), 323);
+#endif
+ TREE_VALUE (parm) = NULL_TREE;
+ expr_as_ctor = build_indirect_ref (expr_as_ctor, NULL_PTR);
+ TREE_HAS_CONSTRUCTOR (expr_as_ctor) = 1;
+ }
+ return expr_as_ctor;
+}
+
+/* Return the character string for the name that encodes the
+ enumeral value VALUE in the domain TYPE. */
+char *
+enum_name_string (value, type)
+ tree value;
+ tree type;
+{
+ register tree values = TYPE_VALUES (type);
+ register HOST_WIDE_INT intval = TREE_INT_CST_LOW (value);
+
+ my_friendly_assert (TREE_CODE (type) == ENUMERAL_TYPE, 324);
+ while (values
+ && TREE_INT_CST_LOW (TREE_VALUE (values)) != intval)
+ values = TREE_CHAIN (values);
+ if (values == NULL_TREE)
+ {
+ char *buf = (char *)oballoc (16 + TYPE_NAME_LENGTH (type));
+
+ /* Value must have been cast. */
+ sprintf (buf, "(enum %s)%d",
+ TYPE_NAME_STRING (type), intval);
+ return buf;
+ }
+ return IDENTIFIER_POINTER (TREE_PURPOSE (values));
+}
+
+#if 0
+/* Print out a language-specific error message for
+ (Pascal) case or (C) switch statements.
+ CODE tells what sort of message to print.
+ TYPE is the type of the switch index expression.
+ NEW is the new value that we were trying to add.
+ OLD is the old value that stopped us from adding it. */
+void
+report_case_error (code, type, new_value, old_value)
+ int code;
+ tree type;
+ tree new_value, old_value;
+{
+ if (code == 1)
+ {
+ if (new_value)
+ error ("case label not within a switch statement");
+ else
+ error ("default label not within a switch statement");
+ }
+ else if (code == 2)
+ {
+ if (new_value == 0)
+ {
+ error ("multiple default labels in one switch");
+ return;
+ }
+ if (TREE_CODE (new_value) == RANGE_EXPR)
+ if (TREE_CODE (old_value) == RANGE_EXPR)
+ {
+ char *buf = (char *)alloca (4 * (8 + TYPE_NAME_LENGTH (type)));
+ if (TREE_CODE (type) == ENUMERAL_TYPE)
+ sprintf (buf, "overlapping ranges [%s..%s], [%s..%s] in case expression",
+ enum_name_string (TREE_OPERAND (new_value, 0), type),
+ enum_name_string (TREE_OPERAND (new_value, 1), type),
+ enum_name_string (TREE_OPERAND (old_value, 0), type),
+ enum_name_string (TREE_OPERAND (old_value, 1), type));
+ else
+ sprintf (buf, "overlapping ranges [%d..%d], [%d..%d] in case expression",
+ TREE_INT_CST_LOW (TREE_OPERAND (new_value, 0)),
+ TREE_INT_CST_LOW (TREE_OPERAND (new_value, 1)),
+ TREE_INT_CST_LOW (TREE_OPERAND (old_value, 0)),
+ TREE_INT_CST_LOW (TREE_OPERAND (old_value, 1)));
+ error (buf);
+ }
+ else
+ {
+ char *buf = (char *)alloca (4 * (8 + TYPE_NAME_LENGTH (type)));
+ if (TREE_CODE (type) == ENUMERAL_TYPE)
+ sprintf (buf, "range [%s..%s] includes element `%s' in case expression",
+ enum_name_string (TREE_OPERAND (new_value, 0), type),
+ enum_name_string (TREE_OPERAND (new_value, 1), type),
+ enum_name_string (old_value, type));
+ else
+ sprintf (buf, "range [%d..%d] includes (%d) in case expression",
+ TREE_INT_CST_LOW (TREE_OPERAND (new_value, 0)),
+ TREE_INT_CST_LOW (TREE_OPERAND (new_value, 1)),
+ TREE_INT_CST_LOW (old_value));
+ error (buf);
+ }
+ else if (TREE_CODE (old_value) == RANGE_EXPR)
+ {
+ char *buf = (char *)alloca (4 * (8 + TYPE_NAME_LENGTH (type)));
+ if (TREE_CODE (type) == ENUMERAL_TYPE)
+ sprintf (buf, "range [%s..%s] includes element `%s' in case expression",
+ enum_name_string (TREE_OPERAND (old_value, 0), type),
+ enum_name_string (TREE_OPERAND (old_value, 1), type),
+ enum_name_string (new_value, type));
+ else
+ sprintf (buf, "range [%d..%d] includes (%d) in case expression",
+ TREE_INT_CST_LOW (TREE_OPERAND (old_value, 0)),
+ TREE_INT_CST_LOW (TREE_OPERAND (old_value, 1)),
+ TREE_INT_CST_LOW (new_value));
+ error (buf);
+ }
+ else
+ {
+ if (TREE_CODE (type) == ENUMERAL_TYPE)
+ error ("duplicate label `%s' in switch statement",
+ enum_name_string (new_value, type));
+ else
+ error ("duplicate label (%d) in switch statement",
+ TREE_INT_CST_LOW (new_value));
+ }
+ }
+ else if (code == 3)
+ {
+ if (TREE_CODE (type) == ENUMERAL_TYPE)
+ warning ("case value out of range for enum %s",
+ TYPE_NAME_STRING (type));
+ else
+ warning ("case value out of range");
+ }
+ else if (code == 4)
+ {
+ if (TREE_CODE (type) == ENUMERAL_TYPE)
+ error ("range values `%s' and `%s' reversed",
+ enum_name_string (new_value, type),
+ enum_name_string (old_value, type));
+ else
+ error ("range values reversed");
+ }
+}
+#endif
diff --git a/gnu/usr.bin/cc/cc1plus/xref.c b/gnu/usr.bin/cc/cc1plus/xref.c
new file mode 100644
index 0000000..283771b
--- /dev/null
+++ b/gnu/usr.bin/cc/cc1plus/xref.c
@@ -0,0 +1,839 @@
+/* Code for handling XREF output from GNU C++.
+ Copyright (C) 1992, 1993 Free Software Foundation, Inc.
+ Contributed by Michael Tiemann (tiemann@cygnus.com)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#include "tree.h"
+#include <stdio.h>
+#include "cp-tree.h"
+#include "input.h"
+
+#include <ctype.h>
+
+extern char *getpwd ();
+
+extern char *index ();
+extern char *rindex ();
+
+/* The character(s) used to join a directory specification (obtained with
+ getwd or equivalent) with a non-absolute file name. */
+
+#ifndef FILE_NAME_JOINER
+#define FILE_NAME_JOINER "/"
+#endif
+
+/* Nonzero if NAME as a file name is absolute. */
+#ifndef FILE_NAME_ABSOLUTE_P
+#define FILE_NAME_ABSOLUTE_P(NAME) (NAME[0] == '/')
+#endif
+
+/* For cross referencing. */
+
+int flag_gnu_xref;
+
+/************************************************************************/
+/* */
+/* Common definitions */
+/* */
+/************************************************************************/
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef NULL
+#define NULL 0
+#endif
+
+#define PALLOC(typ) ((typ *) calloc(1,sizeof(typ)))
+
+
+/* Return a malloc'd copy of STR. */
+#define SALLOC(str) \
+ ((char *) ((str) == NULL ? NULL \
+ : (char *) strcpy ((char *) malloc (strlen ((str)) + 1), (str))))
+#define SFREE(str) (str != NULL && (free(str),0))
+
+#define STREQL(s1,s2) (strcmp((s1),(s2)) == 0)
+#define STRNEQ(s1,s2) (strcmp((s1),(s2)) != 0)
+#define STRLSS(s1,s2) (strcmp((s1),(s2)) < 0)
+#define STRLEQ(s1,s2) (strcmp((s1),(s2)) <= 0)
+#define STRGTR(s1,s2) (strcmp((s1),(s2)) > 0)
+#define STRGEQ(s1,s2) (strcmp((s1),(s2)) >= 0)
+
+/************************************************************************/
+/* */
+/* Type definitions */
+/* */
+/************************************************************************/
+
+
+typedef struct _XREF_FILE * XREF_FILE;
+typedef struct _XREF_SCOPE * XREF_SCOPE;
+
+typedef struct _XREF_FILE
+{
+ char *name;
+ char *outname;
+ XREF_FILE next;
+} XREF_FILE_INFO;
+
+typedef struct _XREF_SCOPE
+{
+ int gid;
+ int lid;
+ XREF_FILE file;
+ int start;
+ XREF_SCOPE outer;
+} XREF_SCOPE_INFO;
+
+/************************************************************************/
+/* */
+/* Local storage */
+/* */
+/************************************************************************/
+
+static char doing_xref = 0;
+static FILE * xref_file = NULL;
+static char xref_name[1024];
+static XREF_FILE all_files = NULL;
+static char * wd_name = NULL;
+static XREF_SCOPE cur_scope = NULL;
+static int scope_ctr = 0;
+static XREF_FILE last_file = NULL;
+static tree last_fndecl = NULL;
+
+/************************************************************************/
+/* */
+/* Forward definitions */
+/* */
+/************************************************************************/
+
+extern void GNU_xref_begin();
+extern void GNU_xref_end();
+extern void GNU_xref_file();
+extern void GNU_xref_start_scope();
+extern void GNU_xref_end_scope();
+extern void GNU_xref_ref();
+extern void GNU_xref_decl();
+extern void GNU_xref_call();
+extern void GNU_xref_function();
+extern void GNU_xref_assign();
+extern void GNU_xref_hier();
+extern void GNU_xref_member();
+
+static void gen_assign();
+static XREF_FILE find_file();
+static char * filename();
+static char * fctname();
+static char * declname();
+static void simplify_type();
+static char * fixname();
+static void open_xref_file();
+
+extern char * type_as_string();
+
+/* Start cross referencing. FILE is the name of the file we xref. */
+
+void
+GNU_xref_begin (file)
+ char *file;
+{
+ doing_xref = 1;
+
+ if (file != NULL && STRNEQ (file,"-"))
+ {
+ open_xref_file(file);
+ GNU_xref_file(file);
+ }
+}
+
+/* Finish cross-referencing. ERRCNT is the number of errors
+ we encountered. */
+
+void
+GNU_xref_end (ect)
+ int ect;
+{
+ XREF_FILE xf;
+
+ if (!doing_xref) return;
+
+ xf = find_file (input_filename);
+ if (xf == NULL) return;
+
+ while (cur_scope != NULL)
+ GNU_xref_end_scope(cur_scope->gid,0,0,0,0);
+
+ doing_xref = 0;
+
+ if (xref_file == NULL) return;
+
+ fclose (xref_file);
+
+ xref_file = NULL;
+ all_files = NULL;
+
+ if (ect > 0) unlink (xref_name);
+}
+
+/* Write out xref for file named NAME. */
+
+void
+GNU_xref_file (name)
+ char *name;
+{
+ XREF_FILE xf;
+
+ if (!doing_xref || name == NULL) return;
+
+ if (xref_file == NULL)
+ {
+ open_xref_file (name);
+ if (!doing_xref) return;
+ }
+
+ if (all_files == NULL)
+ fprintf(xref_file,"SCP * 0 0 0 0 RESET\n");
+
+ xf = find_file (name);
+ if (xf != NULL) return;
+
+ xf = PALLOC (XREF_FILE_INFO);
+ xf->name = SALLOC (name);
+ xf->next = all_files;
+ all_files = xf;
+
+ if (wd_name == NULL)
+ wd_name = getpwd ();
+
+ if (FILE_NAME_ABSOLUTE_P (name) || ! wd_name)
+ xf->outname = xf->name;
+ else
+ {
+ char *nmbuf
+ = (char *) malloc (strlen (wd_name) + strlen (FILE_NAME_JOINER)
+ + strlen (name) + 1);
+ sprintf (nmbuf, "%s%s%s", wd_name, FILE_NAME_JOINER, name);
+ name = nmbuf;
+ xf->outname = nmbuf;
+ }
+
+ fprintf (xref_file, "FIL %s %s 0\n", name, wd_name);
+
+ filename (xf);
+ fctname (NULL);
+}
+
+/* Start a scope identified at level ID. */
+
+void
+GNU_xref_start_scope (id)
+ HOST_WIDE_INT id;
+{
+ XREF_SCOPE xs;
+ XREF_FILE xf;
+
+ if (!doing_xref) return;
+ xf = find_file (input_filename);
+
+ xs = PALLOC (XREF_SCOPE_INFO);
+ xs->file = xf;
+ xs->start = lineno;
+ if (xs->start <= 0) xs->start = 1;
+ xs->gid = id;
+ xs->lid = ++scope_ctr;
+ xs->outer = cur_scope;
+ cur_scope = xs;
+}
+
+/* Finish a scope at level ID.
+ INID is ???
+ PRM is ???
+ KEEP is nonzero iff this scope is retained (nonzero if it's
+ a compiler-generated invisible scope).
+ TRNS is ??? */
+
+void
+GNU_xref_end_scope (id,inid,prm,keep,trns)
+ HOST_WIDE_INT id;
+ HOST_WIDE_INT inid;
+ int prm,keep,trns;
+{
+ XREF_FILE xf;
+ XREF_SCOPE xs,lxs,oxs;
+ char *stype;
+
+ if (!doing_xref) return;
+ xf = find_file (input_filename);
+ if (xf == NULL) return;
+
+ lxs = NULL;
+ for (xs = cur_scope; xs != NULL; xs = xs->outer)
+ {
+ if (xs->gid == id) break;
+ lxs = xs;
+ }
+ if (xs == NULL) return;
+
+ if (inid != 0) {
+ for (oxs = cur_scope; oxs != NULL; oxs = oxs->outer) {
+ if (oxs->gid == inid) break;
+ }
+ if (oxs == NULL) return;
+ inid = oxs->lid;
+ }
+
+ if (prm == 2) stype = "SUE";
+ else if (prm != 0) stype = "ARGS";
+ else if (keep == 2 || inid != 0) stype = "INTERN";
+ else stype = "EXTERN";
+
+ fprintf (xref_file,"SCP %s %d %d %d %d %s\n",
+ filename (xf), xs->start, lineno,xs->lid, inid, stype);
+
+ if (lxs == NULL) cur_scope = xs->outer;
+ else lxs->outer = xs->outer;
+
+ free (xs);
+}
+
+/* Output a reference to NAME in FNDECL. */
+
+void
+GNU_xref_ref (fndecl,name)
+ tree fndecl;
+ char *name;
+{
+ XREF_FILE xf;
+
+ if (!doing_xref) return;
+ xf = find_file (input_filename);
+ if (xf == NULL) return;
+
+ fprintf (xref_file, "REF %s %d %s %s\n",
+ filename (xf), lineno, fctname (fndecl), name);
+}
+
+/* Output a reference to DECL in FNDECL. */
+
+void
+GNU_xref_decl (fndecl,decl)
+ tree fndecl;
+ tree decl;
+{
+ XREF_FILE xf,xf1;
+ char *cls;
+ char *name;
+ char buf[10240];
+ int uselin;
+
+ if (!doing_xref) return;
+ xf = find_file (input_filename);
+ if (xf == NULL) return;
+
+ uselin = FALSE;
+
+ if (TREE_CODE (decl) == TYPE_DECL) cls = "TYPEDEF";
+ else if (TREE_CODE (decl) == FIELD_DECL) cls = "FIELD";
+ else if (TREE_CODE (decl) == VAR_DECL)
+ {
+ if (fndecl == NULL && TREE_STATIC(decl)
+ && TREE_READONLY(decl) && DECL_INITIAL(decl) != 0
+ && !TREE_PUBLIC(decl) && !DECL_EXTERNAL(decl)
+ && DECL_MODE(decl) != BLKmode) cls = "CONST";
+ else if (DECL_EXTERNAL(decl)) cls = "EXTERN";
+ else if (TREE_PUBLIC(decl)) cls = "EXTDEF";
+ else if (TREE_STATIC(decl)) cls = "STATIC";
+ else if (DECL_REGISTER(decl)) cls = "REGISTER";
+ else cls = "AUTO";
+ }
+ else if (TREE_CODE (decl) == PARM_DECL) cls = "PARAM";
+ else if (TREE_CODE (decl) == FIELD_DECL) cls = "FIELD";
+ else if (TREE_CODE (decl) == CONST_DECL) cls = "CONST";
+ else if (TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ if (DECL_EXTERNAL (decl)) cls = "EXTERN";
+ else if (TREE_PUBLIC (decl)) cls = "EFUNCTION";
+ else cls = "SFUNCTION";
+ }
+ else if (TREE_CODE (decl) == LABEL_DECL) cls = "LABEL";
+ else if (TREE_CODE (decl) == UNION_TYPE)
+ {
+ cls = "UNIONID";
+ decl = TYPE_NAME (decl);
+ uselin = TRUE;
+ }
+ else if (TREE_CODE (decl) == RECORD_TYPE)
+ {
+ if (CLASSTYPE_DECLARED_CLASS (decl)) cls = "CLASSID";
+ else if (IS_SIGNATURE (decl)) cls = "SIGNATUREID";
+ else cls = "STRUCTID";
+ decl = TYPE_NAME (decl);
+ uselin = TRUE;
+ }
+ else if (TREE_CODE (decl) == ENUMERAL_TYPE)
+ {
+ cls = "ENUMID";
+ decl = TYPE_NAME (decl);
+ uselin = TRUE;
+ }
+ else if (TREE_CODE (decl) == TEMPLATE_DECL)
+ {
+ if (DECL_TEMPLATE_IS_CLASS (decl))
+ cls = "CLASSTEMP";
+ else if (TREE_CODE (DECL_RESULT (decl)) == FUNCTION_DECL)
+ cls = "FUNCTEMP";
+ else if (TREE_CODE (DECL_RESULT (decl)) == VAR_DECL)
+ cls = "VARTEMP";
+ else
+ my_friendly_abort (358);
+ uselin = TRUE;
+ }
+ else cls = "UNKNOWN";
+
+ if (decl == NULL || DECL_NAME (decl) == NULL) return;
+
+ if (uselin && decl->decl.linenum > 0 && decl->decl.filename != NULL)
+ {
+ xf1 = find_file (decl->decl.filename);
+ if (xf1 != NULL)
+ {
+ lineno = decl->decl.linenum;
+ xf = xf1;
+ }
+ }
+
+ if (DECL_ASSEMBLER_NAME (decl))
+ name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+ else
+ name = IDENTIFIER_POINTER (DECL_NAME (decl));
+
+ strcpy (buf, type_as_string (TREE_TYPE (decl), 0));
+ simplify_type (buf);
+
+ fprintf (xref_file, "DCL %s %d %s %d %s %s %s\n",
+ filename(xf), lineno, name,
+ (cur_scope != NULL ? cur_scope->lid : 0),
+ cls, fctname(fndecl), buf);
+
+ if (STREQL (cls, "STRUCTID") || STREQL (cls, "UNIONID")
+ || STREQL (cls, "SIGNATUREID"))
+ {
+ cls = "CLASSID";
+ fprintf (xref_file, "DCL %s %d %s %d %s %s %s\n",
+ filename(xf), lineno,name,
+ (cur_scope != NULL ? cur_scope->lid : 0),
+ cls, fctname(fndecl), buf);
+ }
+}
+
+/* Output a reference to a call to NAME in FNDECL. */
+
+void
+GNU_xref_call (fndecl, name)
+ tree fndecl;
+ char *name;
+{
+ XREF_FILE xf;
+ char buf[1024];
+ char *s;
+
+ if (!doing_xref) return;
+ xf = find_file (input_filename);
+ if (xf == NULL) return;
+ name = fixname (name, buf);
+
+ for (s = name; *s != 0; ++s)
+ if (*s == '_' && s[1] == '_') break;
+ if (*s != 0) GNU_xref_ref (fndecl, name);
+
+ fprintf (xref_file, "CAL %s %d %s %s\n",
+ filename (xf), lineno, name, fctname (fndecl));
+}
+
+/* Output cross-reference info about FNDECL. If non-NULL,
+ ARGS are the arguments for the function (i.e., before the FUNCTION_DECL
+ has been fully built). */
+
+void
+GNU_xref_function (fndecl, args)
+ tree fndecl;
+ tree args;
+{
+ XREF_FILE xf;
+ int ct;
+ char buf[1024];
+
+ if (!doing_xref) return;
+ xf = find_file (input_filename);
+ if (xf == NULL) return;
+
+ ct = 0;
+ buf[0] = 0;
+ if (args == NULL) args = DECL_ARGUMENTS (fndecl);
+
+ GNU_xref_decl (NULL, fndecl);
+
+ for ( ; args != NULL; args = TREE_CHAIN (args))
+ {
+ GNU_xref_decl (fndecl,args);
+ if (ct != 0) strcat (buf,",");
+ strcat (buf, declname (args));
+ ++ct;
+ }
+
+ fprintf (xref_file, "PRC %s %d %s %d %d %s\n",
+ filename(xf), lineno, declname(fndecl),
+ (cur_scope != NULL ? cur_scope->lid : 0),
+ ct, buf);
+}
+
+/* Output cross-reference info about an assignment to NAME. */
+
+void
+GNU_xref_assign(name)
+ tree name;
+{
+ XREF_FILE xf;
+
+ if (!doing_xref) return;
+ xf = find_file(input_filename);
+ if (xf == NULL) return;
+
+ gen_assign(xf, name);
+}
+
+static void
+gen_assign(xf, name)
+ XREF_FILE xf;
+ tree name;
+{
+ char *s;
+
+ s = NULL;
+
+ switch (TREE_CODE (name))
+ {
+ case IDENTIFIER_NODE :
+ s = IDENTIFIER_POINTER(name);
+ break;
+ case VAR_DECL :
+ s = declname(name);
+ break;
+ case COMPONENT_REF :
+ gen_assign(xf, TREE_OPERAND(name, 0));
+ gen_assign(xf, TREE_OPERAND(name, 1));
+ break;
+ case INDIRECT_REF :
+ case OFFSET_REF :
+ case ARRAY_REF :
+ case BUFFER_REF :
+ gen_assign(xf, TREE_OPERAND(name, 0));
+ break;
+ case COMPOUND_EXPR :
+ gen_assign(xf, TREE_OPERAND(name, 1));
+ break;
+ default :
+ break;
+ }
+
+ if (s != NULL)
+ fprintf(xref_file, "ASG %s %d %s\n", filename(xf), lineno, s);
+}
+
+/* Output cross-reference info about a class hierarchy.
+ CLS is the class type of interest. BASE is a baseclass
+ for CLS. PUB and VIRT give the access info about
+ the class derivation. FRND is nonzero iff BASE is a friend
+ of CLS.
+
+ ??? Needs to handle nested classes. */
+void
+GNU_xref_hier(cls, base, pub, virt, frnd)
+ char *cls;
+ char *base;
+ int pub;
+ int virt;
+ int frnd;
+{
+ XREF_FILE xf;
+
+ if (!doing_xref) return;
+ xf = find_file(input_filename);
+ if (xf == NULL) return;
+
+ fprintf(xref_file, "HIE %s %d %s %s %d %d %d\n",
+ filename(xf), lineno, cls, base, pub, virt, frnd);
+}
+
+/* Output cross-reference info about class members. CLS
+ is the containing type; FLD is the class member. */
+
+void
+GNU_xref_member(cls, fld)
+ tree cls;
+ tree fld;
+{
+ XREF_FILE xf;
+ char *prot;
+ int confg, pure;
+ char *d;
+ int i;
+ char buf[1024], bufa[1024];
+
+ if (!doing_xref) return;
+ xf = find_file(fld->decl.filename);
+ if (xf == NULL) return;
+
+ if (TREE_PRIVATE (fld)) prot = "PRIVATE";
+ else if (TREE_PROTECTED(fld)) prot = "PROTECTED";
+ else prot = "PUBLIC";
+
+ confg = 0;
+ if (TREE_CODE (fld) == FUNCTION_DECL && DECL_CONST_MEMFUNC_P(fld))
+ confg = 1;
+ else if (TREE_CODE (fld) == CONST_DECL)
+ confg = 1;
+
+ pure = 0;
+ if (TREE_CODE (fld) == FUNCTION_DECL && DECL_ABSTRACT_VIRTUAL_P(fld))
+ pure = 1;
+
+ d = IDENTIFIER_POINTER(cls);
+ sprintf(buf, "%d%s", strlen(d), d);
+ i = strlen(buf);
+ strcpy(bufa, declname(fld));
+
+#ifdef XREF_SHORT_MEMBER_NAMES
+ for (p = &bufa[1]; *p != 0; ++p)
+ {
+ if (p[0] == '_' && p[1] == '_' && p[2] >= '0' && p[2] <= '9') {
+ if (strncmp(&p[2], buf, i) == 0) *p = 0;
+ break;
+ }
+ else if (p[0] == '_' && p[1] == '_' && p[2] == 'C' && p[3] >= '0' && p[3] <= '9') {
+ if (strncmp(&p[3], buf, i) == 0) *p = 0;
+ break;
+ }
+ }
+#endif
+
+ fprintf(xref_file, "MEM %s %d %s %s %s %d %d %d %d %d %d %d\n",
+ filename(xf), fld->decl.linenum, d, bufa, prot,
+ (TREE_CODE (fld) == FUNCTION_DECL ? 0 : 1),
+ (DECL_INLINE (fld) ? 1 : 0),
+ (DECL_FRIEND_P(fld) ? 1 : 0),
+ (DECL_VINDEX(fld) ? 1 : 0),
+ (TREE_STATIC(fld) ? 1 : 0),
+ pure, confg);
+}
+
+/* Find file entry given name. */
+
+static XREF_FILE
+find_file(name)
+ char *name;
+{
+ XREF_FILE xf;
+
+ for (xf = all_files; xf != NULL; xf = xf->next) {
+ if (STREQL(name, xf->name)) break;
+ }
+
+ return xf;
+}
+
+/* Return filename for output purposes. */
+
+static char *
+filename(xf)
+ XREF_FILE xf;
+{
+ if (xf == NULL) {
+ last_file = NULL;
+ return "*";
+ }
+
+ if (last_file == xf) return "*";
+
+ last_file = xf;
+
+ return xf->outname;
+}
+
+/* Return function name for output purposes. */
+
+static char *
+fctname(fndecl)
+ tree fndecl;
+{
+ static char fctbuf[1024];
+ char *s;
+
+ if (fndecl == NULL && last_fndecl == NULL) return "*";
+
+ if (fndecl == NULL)
+ {
+ last_fndecl = NULL;
+ return "*TOP*";
+ }
+
+ if (fndecl == last_fndecl) return "*";
+
+ last_fndecl = fndecl;
+
+ s = declname(fndecl);
+ s = fixname(s, fctbuf);
+
+ return s;
+}
+
+/* Return decl name for output purposes. */
+
+static char *
+declname(dcl)
+ tree dcl;
+{
+ if (DECL_NAME (dcl) == NULL) return "?";
+
+ if (DECL_ASSEMBLER_NAME (dcl))
+ return IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (dcl));
+ else
+ return IDENTIFIER_POINTER (DECL_NAME (dcl));
+}
+
+/* Simplify a type string by removing unneeded parenthesis. */
+
+static void
+simplify_type(typ)
+ char *typ;
+{
+ char *s;
+ int lvl, i;
+
+ i = strlen(typ);
+ while (i > 0 && isspace(typ[i-1])) typ[--i] = 0;
+
+ if (i > 7 && STREQL(&typ[i-5], "const"))
+ {
+ typ[i-5] = 0;
+ i -= 5;
+ }
+
+ if (typ[i-1] != ')') return;
+
+ s = &typ[i-2];
+ lvl = 1;
+ while (*s != 0) {
+ if (*s == ')') ++lvl;
+ else if (*s == '(')
+ {
+ --lvl;
+ if (lvl == 0)
+ {
+ s[1] = ')';
+ s[2] = 0;
+ break;
+ }
+ }
+ --s;
+ }
+
+ if (*s != 0 && s[-1] == ')')
+ {
+ --s;
+ --s;
+ if (*s == '(') s[2] = 0;
+ else if (*s == ':') {
+ while (*s != '(') --s;
+ s[1] = ')';
+ s[2] = 0;
+ }
+ }
+}
+
+/* Fixup a function name (take care of embedded spaces). */
+
+static char *
+fixname(nam, buf)
+ char *nam;
+ char *buf;
+{
+ char *s, *t;
+ int fg;
+
+ s = nam;
+ t = buf;
+ fg = 0;
+
+ while (*s != 0)
+ {
+ if (*s == ' ')
+ {
+ *t++ = '\36';
+ ++fg;
+ }
+ else *t++ = *s;
+ ++s;
+ }
+ *t = 0;
+
+ if (fg == 0) return nam;
+
+ return buf;
+}
+
+/* Open file for xrefing. */
+
+static void
+open_xref_file(file)
+ char *file;
+{
+ char *s, *t;
+
+#ifdef XREF_FILE_NAME
+ XREF_FILE_NAME (xref_name, file);
+#else
+ s = rindex (file, '/');
+ if (s == NULL)
+ sprintf (xref_name, ".%s.gxref", file);
+ else
+ {
+ ++s;
+ strcpy (xref_name, file);
+ t = rindex (xref_name, '/');
+ ++t;
+ *t++ = '.';
+ strcpy (t, s);
+ strcat (t, ".gxref");
+ }
+#endif /* no XREF_FILE_NAME */
+
+ xref_file = fopen(xref_name, "w");
+
+ if (xref_file == NULL)
+ {
+ error("Can't create cross-reference file `%s'", xref_name);
+ doing_xref = 0;
+ }
+}
diff --git a/gnu/usr.bin/cc/cc_int/Makefile b/gnu/usr.bin/cc/cc_int/Makefile
new file mode 100644
index 0000000..794d760
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/Makefile
@@ -0,0 +1,12 @@
+#
+# $FreeBSD$
+#
+
+SRCS = aux-output.c bc-emit.c bc-optab.c c-common.c caller-save.c calls.c combine.c convert.c cse.c dbxout.c dwarfout.c emit-rtl.c explow.c expmed.c expr.c final.c flow.c fold-const.c function.c getpwd.c global.c insn-attrtab.c insn-emit.c insn-extract.c insn-opinit.c insn-output.c insn-peep.c insn-recog.c integrate.c jump.c local-alloc.c loop.c obstack.c optabs.c print-rtl.c print-tree.c real.c recog.c reg-stack.c regclass.c reload.c reload1.c reorg.c rtl.c rtlanal.c sched.c sdbout.c stmt.c stor-layout.c stupid.c toplev.c tree.c unroll.c varasm.c version.c xcoffout.c
+LIB = cc_int
+NOPROFILE= 1
+
+install:
+ @true
+
+.include <bsd.lib.mk>
diff --git a/gnu/usr.bin/cc/cc_int/aux-output.c b/gnu/usr.bin/cc/cc_int/aux-output.c
new file mode 100644
index 0000000..bc498e0
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/aux-output.c
@@ -0,0 +1,2138 @@
+/* Subroutines for insn-output.c for Intel 80386.
+ Copyright (C) 1988, 1992 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <setjmp.h>
+#include "config.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "real.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "insn-flags.h"
+#include "output.h"
+#include "insn-attr.h"
+#include "tree.h"
+#include "flags.h"
+#include "function.h"
+
+#ifdef EXTRA_CONSTRAINT
+/* If EXTRA_CONSTRAINT is defined, then the 'S'
+ constraint in REG_CLASS_FROM_LETTER will no longer work, and various
+ asm statements that need 'S' for class SIREG will break. */
+ error EXTRA_CONSTRAINT conflicts with S constraint letter
+/* The previous line used to be #error, but some compilers barf
+ even if the conditional was untrue. */
+#endif
+
+#define AT_BP(mode) (gen_rtx (MEM, (mode), frame_pointer_rtx))
+
+extern FILE *asm_out_file;
+extern char *strcat ();
+
+char *singlemove_string ();
+char *output_move_const_single ();
+char *output_fp_cc0_set ();
+
+char *hi_reg_name[] = HI_REGISTER_NAMES;
+char *qi_reg_name[] = QI_REGISTER_NAMES;
+char *qi_high_reg_name[] = QI_HIGH_REGISTER_NAMES;
+
+/* Array of the smallest class containing reg number REGNO, indexed by
+ REGNO. Used by REGNO_REG_CLASS in i386.h. */
+
+enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] =
+{
+ /* ax, dx, cx, bx */
+ AREG, DREG, CREG, BREG,
+ /* si, di, bp, sp */
+ SIREG, DIREG, INDEX_REGS, GENERAL_REGS,
+ /* FP registers */
+ FP_TOP_REG, FP_SECOND_REG, FLOAT_REGS, FLOAT_REGS,
+ FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS,
+ /* arg pointer */
+ INDEX_REGS
+};
+
+/* Test and compare insns in i386.md store the information needed to
+ generate branch and scc insns here. */
+
+struct rtx_def *i386_compare_op0, *i386_compare_op1;
+struct rtx_def *(*i386_compare_gen)(), *(*i386_compare_gen_eq)();
+
+/* Output an insn whose source is a 386 integer register. SRC is the
+ rtx for the register, and TEMPLATE is the op-code template. SRC may
+ be either SImode or DImode.
+
+ The template will be output with operands[0] as SRC, and operands[1]
+ as a pointer to the top of the 386 stack. So a call from floatsidf2
+ would look like this:
+
+ output_op_from_reg (operands[1], AS1 (fild%z0,%1));
+
+ where %z0 corresponds to the caller's operands[1], and is used to
+ emit the proper size suffix.
+
+ ??? Extend this to handle HImode - a 387 can load and store HImode
+ values directly. */
+
+void
+output_op_from_reg (src, template)
+ rtx src;
+ char *template;
+{
+ rtx xops[4];
+ int size = GET_MODE_SIZE (GET_MODE (src));
+
+ xops[0] = src;
+ xops[1] = AT_SP (Pmode);
+ xops[2] = GEN_INT (size);
+ xops[3] = stack_pointer_rtx;
+
+ if (size > UNITS_PER_WORD)
+ {
+ rtx high;
+ if (size > 2 * UNITS_PER_WORD)
+ {
+ high = gen_rtx (REG, SImode, REGNO (src) + 2);
+ output_asm_insn (AS1 (push%L0,%0), &high);
+ }
+ high = gen_rtx (REG, SImode, REGNO (src) + 1);
+ output_asm_insn (AS1 (push%L0,%0), &high);
+ }
+ output_asm_insn (AS1 (push%L0,%0), &src);
+
+ output_asm_insn (template, xops);
+
+ output_asm_insn (AS2 (add%L3,%2,%3), xops);
+}
+
+/* Output an insn to pop an value from the 387 top-of-stack to 386
+ register DEST. The 387 register stack is popped if DIES is true. If
+ the mode of DEST is an integer mode, a `fist' integer store is done,
+ otherwise a `fst' float store is done. */
+
+void
+output_to_reg (dest, dies)
+ rtx dest;
+ int dies;
+{
+ rtx xops[4];
+ int size = GET_MODE_SIZE (GET_MODE (dest));
+
+ xops[0] = AT_SP (Pmode);
+ xops[1] = stack_pointer_rtx;
+ xops[2] = GEN_INT (size);
+ xops[3] = dest;
+
+ output_asm_insn (AS2 (sub%L1,%2,%1), xops);
+
+ if (GET_MODE_CLASS (GET_MODE (dest)) == MODE_INT)
+ {
+ if (dies)
+ output_asm_insn (AS1 (fistp%z3,%y0), xops);
+ else
+ output_asm_insn (AS1 (fist%z3,%y0), xops);
+ }
+ else if (GET_MODE_CLASS (GET_MODE (dest)) == MODE_FLOAT)
+ {
+ if (dies)
+ output_asm_insn (AS1 (fstp%z3,%y0), xops);
+ else
+ {
+ if (GET_MODE (dest) == XFmode)
+ {
+ output_asm_insn (AS1 (fstp%z3,%y0), xops);
+ output_asm_insn (AS1 (fld%z3,%y0), xops);
+ }
+ else
+ output_asm_insn (AS1 (fst%z3,%y0), xops);
+ }
+ }
+ else
+ abort ();
+
+ output_asm_insn (AS1 (pop%L0,%0), &dest);
+
+ if (size > UNITS_PER_WORD)
+ {
+ dest = gen_rtx (REG, SImode, REGNO (dest) + 1);
+ output_asm_insn (AS1 (pop%L0,%0), &dest);
+ if (size > 2 * UNITS_PER_WORD)
+ {
+ dest = gen_rtx (REG, SImode, REGNO (dest) + 1);
+ output_asm_insn (AS1 (pop%L0,%0), &dest);
+ }
+ }
+}
+
+char *
+singlemove_string (operands)
+ rtx *operands;
+{
+ rtx x;
+ if (GET_CODE (operands[0]) == MEM
+ && GET_CODE (x = XEXP (operands[0], 0)) == PRE_DEC)
+ {
+ if (XEXP (x, 0) != stack_pointer_rtx)
+ abort ();
+ return "push%L1 %1";
+ }
+ else if (GET_CODE (operands[1]) == CONST_DOUBLE)
+ {
+ return output_move_const_single (operands);
+ }
+ else if (GET_CODE (operands[0]) == REG || GET_CODE (operands[1]) == REG)
+ return AS2 (mov%L0,%1,%0);
+ else if (CONSTANT_P (operands[1]))
+ return AS2 (mov%L0,%1,%0);
+ else
+ {
+ output_asm_insn ("push%L1 %1", operands);
+ return "pop%L0 %0";
+ }
+}
+
+/* Return a REG that occurs in ADDR with coefficient 1.
+ ADDR can be effectively incremented by incrementing REG. */
+
+static rtx
+find_addr_reg (addr)
+ rtx addr;
+{
+ while (GET_CODE (addr) == PLUS)
+ {
+ if (GET_CODE (XEXP (addr, 0)) == REG)
+ addr = XEXP (addr, 0);
+ else if (GET_CODE (XEXP (addr, 1)) == REG)
+ addr = XEXP (addr, 1);
+ else if (CONSTANT_P (XEXP (addr, 0)))
+ addr = XEXP (addr, 1);
+ else if (CONSTANT_P (XEXP (addr, 1)))
+ addr = XEXP (addr, 0);
+ else
+ abort ();
+ }
+ if (GET_CODE (addr) == REG)
+ return addr;
+ abort ();
+}
+
+/* Output an insn to add the constant N to the register X. */
+
+static void
+asm_add (n, x)
+ int n;
+ rtx x;
+{
+ rtx xops[2];
+ xops[1] = x;
+ if (n < 0)
+ {
+ xops[0] = GEN_INT (-n);
+ output_asm_insn (AS2 (sub%L0,%0,%1), xops);
+ }
+ else if (n > 0)
+ {
+ xops[0] = GEN_INT (n);
+ output_asm_insn (AS2 (add%L0,%0,%1), xops);
+ }
+}
+
+/* Output assembler code to perform a doubleword move insn
+ with operands OPERANDS. */
+
+char *
+output_move_double (operands)
+ rtx *operands;
+{
+ enum {REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
+ rtx latehalf[2];
+ rtx middlehalf[2];
+ rtx xops[2];
+ rtx addreg0 = 0, addreg1 = 0;
+ int dest_overlapped_low = 0;
+ int size = GET_MODE_SIZE (GET_MODE (operands[1]));
+
+ middlehalf[0] = 0;
+ middlehalf[1] = 0;
+
+ /* First classify both operands. */
+
+ if (REG_P (operands[0]))
+ optype0 = REGOP;
+ else if (offsettable_memref_p (operands[0]))
+ optype0 = OFFSOP;
+ else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC)
+ optype0 = POPOP;
+ else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
+ optype0 = PUSHOP;
+ else if (GET_CODE (operands[0]) == MEM)
+ optype0 = MEMOP;
+ else
+ optype0 = RNDOP;
+
+ if (REG_P (operands[1]))
+ optype1 = REGOP;
+ else if (CONSTANT_P (operands[1]))
+ optype1 = CNSTOP;
+ else if (offsettable_memref_p (operands[1]))
+ optype1 = OFFSOP;
+ else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
+ optype1 = POPOP;
+ else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
+ optype1 = PUSHOP;
+ else if (GET_CODE (operands[1]) == MEM)
+ optype1 = MEMOP;
+ else
+ optype1 = RNDOP;
+
+ /* Check for the cases that the operand constraints are not
+ supposed to allow to happen. Abort if we get one,
+ because generating code for these cases is painful. */
+
+ if (optype0 == RNDOP || optype1 == RNDOP)
+ abort ();
+
+ /* If one operand is decrementing and one is incrementing
+ decrement the former register explicitly
+ and change that operand into ordinary indexing. */
+
+ if (optype0 == PUSHOP && optype1 == POPOP)
+ {
+ /* ??? Can this ever happen on i386? */
+ operands[0] = XEXP (XEXP (operands[0], 0), 0);
+ asm_add (-size, operands[0]);
+ if (GET_MODE (operands[1]) == XFmode)
+ operands[0] = gen_rtx (MEM, XFmode, operands[0]);
+ else if (GET_MODE (operands[0]) == DFmode)
+ operands[0] = gen_rtx (MEM, DFmode, operands[0]);
+ else
+ operands[0] = gen_rtx (MEM, DImode, operands[0]);
+ optype0 = OFFSOP;
+ }
+
+ if (optype0 == POPOP && optype1 == PUSHOP)
+ {
+ /* ??? Can this ever happen on i386? */
+ operands[1] = XEXP (XEXP (operands[1], 0), 0);
+ asm_add (-size, operands[1]);
+ if (GET_MODE (operands[1]) == XFmode)
+ operands[1] = gen_rtx (MEM, XFmode, operands[1]);
+ else if (GET_MODE (operands[1]) == DFmode)
+ operands[1] = gen_rtx (MEM, DFmode, operands[1]);
+ else
+ operands[1] = gen_rtx (MEM, DImode, operands[1]);
+ optype1 = OFFSOP;
+ }
+
+ /* If an operand is an unoffsettable memory ref, find a register
+ we can increment temporarily to make it refer to the second word. */
+
+ if (optype0 == MEMOP)
+ addreg0 = find_addr_reg (XEXP (operands[0], 0));
+
+ if (optype1 == MEMOP)
+ addreg1 = find_addr_reg (XEXP (operands[1], 0));
+
+ /* Ok, we can do one word at a time.
+ Normally we do the low-numbered word first,
+ but if either operand is autodecrementing then we
+ do the high-numbered word first.
+
+ In either case, set up in LATEHALF the operands to use
+ for the high-numbered word and in some cases alter the
+ operands in OPERANDS to be suitable for the low-numbered word. */
+
+ if (size == 12)
+ {
+ if (optype0 == REGOP)
+ {
+ middlehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
+ latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 2);
+ }
+ else if (optype0 == OFFSOP)
+ {
+ middlehalf[0] = adj_offsettable_operand (operands[0], 4);
+ latehalf[0] = adj_offsettable_operand (operands[0], 8);
+ }
+ else
+ {
+ middlehalf[0] = operands[0];
+ latehalf[0] = operands[0];
+ }
+
+ if (optype1 == REGOP)
+ {
+ middlehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
+ latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 2);
+ }
+ else if (optype1 == OFFSOP)
+ {
+ middlehalf[1] = adj_offsettable_operand (operands[1], 4);
+ latehalf[1] = adj_offsettable_operand (operands[1], 8);
+ }
+ else if (optype1 == CNSTOP)
+ {
+ if (GET_CODE (operands[1]) == CONST_DOUBLE)
+ {
+ REAL_VALUE_TYPE r; long l[3];
+
+ REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
+ REAL_VALUE_TO_TARGET_LONG_DOUBLE (r, l);
+ operands[1] = GEN_INT (l[0]);
+ middlehalf[1] = GEN_INT (l[1]);
+ latehalf[1] = GEN_INT (l[2]);
+ }
+ else if (CONSTANT_P (operands[1]))
+ /* No non-CONST_DOUBLE constant should ever appear here. */
+ abort ();
+ }
+ else
+ {
+ middlehalf[1] = operands[1];
+ latehalf[1] = operands[1];
+ }
+ }
+ else /* size is not 12: */
+ {
+ if (optype0 == REGOP)
+ latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
+ else if (optype0 == OFFSOP)
+ latehalf[0] = adj_offsettable_operand (operands[0], 4);
+ else
+ latehalf[0] = operands[0];
+
+ if (optype1 == REGOP)
+ latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
+ else if (optype1 == OFFSOP)
+ latehalf[1] = adj_offsettable_operand (operands[1], 4);
+ else if (optype1 == CNSTOP)
+ {
+ if (GET_CODE (operands[1]) == CONST_DOUBLE)
+ split_double (operands[1], &operands[1], &latehalf[1]);
+ else if (CONSTANT_P (operands[1]))
+ {
+ /* ??? jrv: Can this really happen? A DImode constant
+ that isn't a CONST_DOUBLE? */
+ if (GET_CODE (operands[1]) == CONST_INT
+ && INTVAL (operands[1]) < 0)
+ latehalf[1] = constm1_rtx;
+ else
+ latehalf[1] = const0_rtx;
+ }
+ }
+ else
+ latehalf[1] = operands[1];
+ }
+
+ /* If insn is effectively movd N (sp),-(sp) then we will do the
+ high word first. We should use the adjusted operand 1
+ (which is N+4 (sp) or N+8 (sp))
+ for the low word and middle word as well,
+ to compensate for the first decrement of sp. */
+ if (optype0 == PUSHOP
+ && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
+ && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
+ middlehalf[1] = operands[1] = latehalf[1];
+
+ /* For (set (reg:DI N) (mem:DI ... (reg:SI N) ...)),
+ if the upper part of reg N does not appear in the MEM, arrange to
+ emit the move late-half first. Otherwise, compute the MEM address
+ into the upper part of N and use that as a pointer to the memory
+ operand. */
+ if (optype0 == REGOP
+ && (optype1 == OFFSOP || optype1 == MEMOP))
+ {
+ if (reg_mentioned_p (operands[0], XEXP (operands[1], 0))
+ && reg_mentioned_p (latehalf[0], XEXP (operands[1], 0)))
+ {
+ /* If both halves of dest are used in the src memory address,
+ compute the address into latehalf of dest. */
+compadr:
+ xops[0] = latehalf[0];
+ xops[1] = XEXP (operands[1], 0);
+ output_asm_insn (AS2 (lea%L0,%a1,%0), xops);
+ if( GET_MODE (operands[1]) == XFmode )
+ {
+/* abort (); */
+ operands[1] = gen_rtx (MEM, XFmode, latehalf[0]);
+ middlehalf[1] = adj_offsettable_operand (operands[1], size-8);
+ latehalf[1] = adj_offsettable_operand (operands[1], size-4);
+ }
+ else
+ {
+ operands[1] = gen_rtx (MEM, DImode, latehalf[0]);
+ latehalf[1] = adj_offsettable_operand (operands[1], size-4);
+ }
+ }
+ else if (size == 12
+ && reg_mentioned_p (middlehalf[0], XEXP (operands[1], 0)))
+ {
+ /* Check for two regs used by both source and dest. */
+ if (reg_mentioned_p (operands[0], XEXP (operands[1], 0))
+ || reg_mentioned_p (latehalf[0], XEXP (operands[1], 0)))
+ goto compadr;
+
+ /* JRV says this can't happen: */
+ if (addreg0 || addreg1)
+ abort();
+
+ /* Only the middle reg conflicts; simply put it last. */
+ output_asm_insn (singlemove_string (operands), operands);
+ output_asm_insn (singlemove_string (latehalf), latehalf);
+ output_asm_insn (singlemove_string (middlehalf), middlehalf);
+ return "";
+ }
+ else if (reg_mentioned_p (operands[0], XEXP (operands[1], 0)))
+ /* If the low half of dest is mentioned in the source memory
+ address, the arrange to emit the move late half first. */
+ dest_overlapped_low = 1;
+ }
+
+ /* If one or both operands autodecrementing,
+ do the two words, high-numbered first. */
+
+ /* Likewise, the first move would clobber the source of the second one,
+ do them in the other order. This happens only for registers;
+ such overlap can't happen in memory unless the user explicitly
+ sets it up, and that is an undefined circumstance. */
+
+/*
+ if (optype0 == PUSHOP || optype1 == PUSHOP
+ || (optype0 == REGOP && optype1 == REGOP
+ && REGNO (operands[0]) == REGNO (latehalf[1]))
+ || dest_overlapped_low)
+*/
+ if (optype0 == PUSHOP || optype1 == PUSHOP
+ || (optype0 == REGOP && optype1 == REGOP
+ && ((middlehalf[1] && REGNO (operands[0]) == REGNO (middlehalf[1]))
+ || REGNO (operands[0]) == REGNO (latehalf[1])))
+ || dest_overlapped_low)
+ {
+ /* Make any unoffsettable addresses point at high-numbered word. */
+ if (addreg0)
+ asm_add (size-4, addreg0);
+ if (addreg1)
+ asm_add (size-4, addreg1);
+
+ /* Do that word. */
+ output_asm_insn (singlemove_string (latehalf), latehalf);
+
+ /* Undo the adds we just did. */
+ if (addreg0)
+ asm_add (-4, addreg0);
+ if (addreg1)
+ asm_add (-4, addreg1);
+
+ if (size == 12)
+ {
+ output_asm_insn (singlemove_string (middlehalf), middlehalf);
+ if (addreg0)
+ asm_add (-4, addreg0);
+ if (addreg1)
+ asm_add (-4, addreg1);
+ }
+
+ /* Do low-numbered word. */
+ return singlemove_string (operands);
+ }
+
+ /* Normal case: do the two words, low-numbered first. */
+
+ output_asm_insn (singlemove_string (operands), operands);
+
+ /* Do the middle one of the three words for long double */
+ if (size == 12)
+ {
+ if (addreg0)
+ asm_add (4, addreg0);
+ if (addreg1)
+ asm_add (4, addreg1);
+
+ output_asm_insn (singlemove_string (middlehalf), middlehalf);
+ }
+
+ /* Make any unoffsettable addresses point at high-numbered word. */
+ if (addreg0)
+ asm_add (4, addreg0);
+ if (addreg1)
+ asm_add (4, addreg1);
+
+ /* Do that word. */
+ output_asm_insn (singlemove_string (latehalf), latehalf);
+
+ /* Undo the adds we just did. */
+ if (addreg0)
+ asm_add (4-size, addreg0);
+ if (addreg1)
+ asm_add (4-size, addreg1);
+
+ return "";
+}
+
+int
+standard_80387_constant_p (x)
+ rtx x;
+{
+#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
+ REAL_VALUE_TYPE d;
+ jmp_buf handler;
+ int is0, is1;
+
+ if (setjmp (handler))
+ return 0;
+
+ set_float_handler (handler);
+ REAL_VALUE_FROM_CONST_DOUBLE (d, x);
+ is0 = REAL_VALUES_EQUAL (d, dconst0);
+ is1 = REAL_VALUES_EQUAL (d, dconst1);
+ set_float_handler (NULL_PTR);
+
+ if (is0)
+ return 1;
+
+ if (is1)
+ return 2;
+
+ /* Note that on the 80387, other constants, such as pi,
+ are much slower to load as standard constants
+ than to load from doubles in memory! */
+#endif
+
+ return 0;
+}
+
+char *
+output_move_const_single (operands)
+ rtx *operands;
+{
+ if (FP_REG_P (operands[0]))
+ {
+ int conval = standard_80387_constant_p (operands[1]);
+
+ if (conval == 1)
+ return "fldz";
+
+ if (conval == 2)
+ return "fld1";
+ }
+ if (GET_CODE (operands[1]) == CONST_DOUBLE)
+ {
+ REAL_VALUE_TYPE r; long l;
+
+ if (GET_MODE (operands[1]) == XFmode)
+ abort ();
+
+ REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
+ REAL_VALUE_TO_TARGET_SINGLE (r, l);
+ operands[1] = GEN_INT (l);
+ }
+ return singlemove_string (operands);
+}
+
+/* Returns 1 if OP is either a symbol reference or a sum of a symbol
+ reference and a constant. */
+
+int
+symbolic_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ switch (GET_CODE (op))
+ {
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return 1;
+ case CONST:
+ op = XEXP (op, 0);
+ return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+ || GET_CODE (XEXP (op, 0)) == LABEL_REF)
+ && GET_CODE (XEXP (op, 1)) == CONST_INT);
+ default:
+ return 0;
+ }
+}
+
+/* Test for a valid operand for a call instruction.
+ Don't allow the arg pointer register or virtual regs
+ since they may change into reg + const, which the patterns
+ can't handle yet. */
+
+int
+call_insn_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) == MEM
+ && ((CONSTANT_ADDRESS_P (XEXP (op, 0))
+ /* This makes a difference for PIC. */
+ && general_operand (XEXP (op, 0), Pmode))
+ || (GET_CODE (XEXP (op, 0)) == REG
+ && XEXP (op, 0) != arg_pointer_rtx
+ && !(REGNO (XEXP (op, 0)) >= FIRST_PSEUDO_REGISTER
+ && REGNO (XEXP (op, 0)) <= LAST_VIRTUAL_REGISTER))))
+ return 1;
+ return 0;
+}
+
+/* Like call_insn_operand but allow (mem (symbol_ref ...))
+ even if pic. */
+
+int
+expander_call_insn_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) == MEM
+ && (CONSTANT_ADDRESS_P (XEXP (op, 0))
+ || (GET_CODE (XEXP (op, 0)) == REG
+ && XEXP (op, 0) != arg_pointer_rtx
+ && !(REGNO (XEXP (op, 0)) >= FIRST_PSEUDO_REGISTER
+ && REGNO (XEXP (op, 0)) <= LAST_VIRTUAL_REGISTER))))
+ return 1;
+ return 0;
+}
+
+/* Returns 1 if OP contains a symbol reference */
+
+int
+symbolic_reference_mentioned_p (op)
+ rtx op;
+{
+ register char *fmt;
+ register int i;
+
+ if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF)
+ return 1;
+
+ fmt = GET_RTX_FORMAT (GET_CODE (op));
+ for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ register int j;
+
+ for (j = XVECLEN (op, i) - 1; j >= 0; j--)
+ if (symbolic_reference_mentioned_p (XVECEXP (op, i, j)))
+ return 1;
+ }
+ else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i)))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Return a legitimate reference for ORIG (an address) using the
+ register REG. If REG is 0, a new pseudo is generated.
+
+ There are three types of references that must be handled:
+
+ 1. Global data references must load the address from the GOT, via
+ the PIC reg. An insn is emitted to do this load, and the reg is
+ returned.
+
+ 2. Static data references must compute the address as an offset
+ from the GOT, whose base is in the PIC reg. An insn is emitted to
+ compute the address into a reg, and the reg is returned. Static
+ data objects have SYMBOL_REF_FLAG set to differentiate them from
+ global data objects.
+
+ 3. Constant pool addresses must be handled special. They are
+ considered legitimate addresses, but only if not used with regs.
+ When printed, the output routines know to print the reference with the
+ PIC reg, even though the PIC reg doesn't appear in the RTL.
+
+ GO_IF_LEGITIMATE_ADDRESS rejects symbolic references unless the PIC
+ reg also appears in the address (except for constant pool references,
+ noted above).
+
+ "switch" statements also require special handling when generating
+ PIC code. See comments by the `casesi' insn in i386.md for details. */
+
+rtx
+legitimize_pic_address (orig, reg)
+ rtx orig;
+ rtx reg;
+{
+ rtx addr = orig;
+ rtx new = orig;
+
+ if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
+ {
+ if (GET_CODE (addr) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (addr))
+ reg = new = orig;
+ else
+ {
+ if (reg == 0)
+ reg = gen_reg_rtx (Pmode);
+
+ if (GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_FLAG (addr))
+ new = gen_rtx (PLUS, Pmode, pic_offset_table_rtx, orig);
+ else
+ new = gen_rtx (MEM, Pmode,
+ gen_rtx (PLUS, Pmode,
+ pic_offset_table_rtx, orig));
+
+ emit_move_insn (reg, new);
+ }
+ current_function_uses_pic_offset_table = 1;
+ return reg;
+ }
+ else if (GET_CODE (addr) == CONST || GET_CODE (addr) == PLUS)
+ {
+ rtx base;
+
+ if (GET_CODE (addr) == CONST)
+ {
+ addr = XEXP (addr, 0);
+ if (GET_CODE (addr) != PLUS)
+ abort ();
+ }
+
+ if (XEXP (addr, 0) == pic_offset_table_rtx)
+ return orig;
+
+ if (reg == 0)
+ reg = gen_reg_rtx (Pmode);
+
+ base = legitimize_pic_address (XEXP (addr, 0), reg);
+ addr = legitimize_pic_address (XEXP (addr, 1),
+ base == reg ? NULL_RTX : reg);
+
+ if (GET_CODE (addr) == CONST_INT)
+ return plus_constant (base, INTVAL (addr));
+
+ if (GET_CODE (addr) == PLUS && CONSTANT_P (XEXP (addr, 1)))
+ {
+ base = gen_rtx (PLUS, Pmode, base, XEXP (addr, 0));
+ addr = XEXP (addr, 1);
+ }
+ return gen_rtx (PLUS, Pmode, base, addr);
+ }
+ return new;
+}
+
+/* Emit insns to move operands[1] into operands[0]. */
+
+void
+emit_pic_move (operands, mode)
+ rtx *operands;
+ enum machine_mode mode;
+{
+ rtx temp = reload_in_progress ? operands[0] : gen_reg_rtx (Pmode);
+
+ if (GET_CODE (operands[0]) == MEM && SYMBOLIC_CONST (operands[1]))
+ operands[1] = (rtx) force_reg (SImode, operands[1]);
+ else
+ operands[1] = legitimize_pic_address (operands[1], temp);
+}
+
+/* This function generates the assembly code for function entry.
+ FILE is an stdio stream to output the code to.
+ SIZE is an int: how many units of temporary storage to allocate. */
+
+void
+function_prologue (file, size)
+ FILE *file;
+ int size;
+{
+ register int regno;
+ int limit;
+ rtx xops[4];
+ int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
+ || current_function_uses_const_pool
+ || profile_flag || profile_block_flag);
+
+ xops[0] = stack_pointer_rtx;
+ xops[1] = frame_pointer_rtx;
+ xops[2] = GEN_INT (size);
+ if (frame_pointer_needed)
+ {
+ output_asm_insn ("push%L1 %1", xops);
+ output_asm_insn (AS2 (mov%L0,%0,%1), xops);
+ }
+
+ if (size)
+ output_asm_insn (AS2 (sub%L0,%2,%0), xops);
+
+ /* Note If use enter it is NOT reversed args.
+ This one is not reversed from intel!!
+ I think enter is slower. Also sdb doesn't like it.
+ But if you want it the code is:
+ {
+ xops[3] = const0_rtx;
+ output_asm_insn ("enter %2,%3", xops);
+ }
+ */
+ limit = (frame_pointer_needed ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
+ for (regno = limit - 1; regno >= 0; regno--)
+ if ((regs_ever_live[regno] && ! call_used_regs[regno])
+ || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used))
+ {
+ xops[0] = gen_rtx (REG, SImode, regno);
+ output_asm_insn ("push%L0 %0", xops);
+ }
+
+ if (pic_reg_used)
+ {
+ xops[0] = pic_offset_table_rtx;
+ xops[1] = (rtx) gen_label_rtx ();
+
+ output_asm_insn (AS1 (call,%P1), xops);
+ ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (xops[1]));
+ output_asm_insn (AS1 (pop%L0,%0), xops);
+ output_asm_insn ("addl $_GLOBAL_OFFSET_TABLE_+[.-%P1],%0", xops);
+ }
+}
+
+/* Return 1 if it is appropriate to emit `ret' instructions in the
+ body of a function. Do this only if the epilogue is simple, needing a
+ couple of insns. Prior to reloading, we can't tell how many registers
+ must be saved, so return 0 then.
+
+ If NON_SAVING_SETJMP is defined and true, then it is not possible
+ for the epilogue to be simple, so return 0. This is a special case
+ since NON_SAVING_SETJMP will not cause regs_ever_live to change until
+ final, but jump_optimize may need to know sooner if a `return' is OK. */
+
+int
+simple_386_epilogue ()
+{
+ int regno;
+ int nregs = 0;
+ int reglimit = (frame_pointer_needed
+ ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
+
+#ifdef FUNCTION_PROFILER_EPILOGUE
+ if (profile_flag)
+ return 0;
+#endif
+
+ if (flag_pic && (current_function_uses_pic_offset_table
+ || current_function_uses_const_pool
+ || profile_flag || profile_block_flag))
+ return 0;
+
+#ifdef NON_SAVING_SETJMP
+ if (NON_SAVING_SETJMP && current_function_calls_setjmp)
+ return 0;
+#endif
+
+ if (! reload_completed)
+ return 0;
+
+ for (regno = reglimit - 1; regno >= 0; regno--)
+ if (regs_ever_live[regno] && ! call_used_regs[regno])
+ nregs++;
+
+ return nregs == 0 || ! frame_pointer_needed;
+}
+
+/* This function generates the assembly code for function exit.
+ FILE is an stdio stream to output the code to.
+ SIZE is an int: how many units of temporary storage to deallocate. */
+
+void
+function_epilogue (file, size)
+ FILE *file;
+ int size;
+{
+ register int regno;
+ register int nregs, limit;
+ int offset;
+ rtx xops[3];
+ int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
+ || current_function_uses_const_pool);
+
+#ifdef FUNCTION_PROFILER_EPILOGUE
+ if (profile_flag)
+ FUNCTION_PROFILER_EPILOGUE (file);
+#endif
+
+ /* Compute the number of registers to pop */
+
+ limit = (frame_pointer_needed
+ ? FRAME_POINTER_REGNUM
+ : STACK_POINTER_REGNUM);
+
+ nregs = 0;
+
+ for (regno = limit - 1; regno >= 0; regno--)
+ if ((regs_ever_live[regno] && ! call_used_regs[regno])
+ || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used))
+ nregs++;
+
+ /* sp is often unreliable so we must go off the frame pointer,
+ */
+
+ /* In reality, we may not care if sp is unreliable, because we can
+ restore the register relative to the frame pointer. In theory,
+ since each move is the same speed as a pop, and we don't need the
+ leal, this is faster. For now restore multiple registers the old
+ way. */
+
+ offset = -size - (nregs * UNITS_PER_WORD);
+
+ xops[2] = stack_pointer_rtx;
+
+ if (nregs > 1 || ! frame_pointer_needed)
+ {
+ if (frame_pointer_needed)
+ {
+ xops[0] = adj_offsettable_operand (AT_BP (Pmode), offset);
+ output_asm_insn (AS2 (lea%L2,%0,%2), xops);
+ }
+
+ for (regno = 0; regno < limit; regno++)
+ if ((regs_ever_live[regno] && ! call_used_regs[regno])
+ || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used))
+ {
+ xops[0] = gen_rtx (REG, SImode, regno);
+ output_asm_insn ("pop%L0 %0", xops);
+ }
+ }
+ else
+ for (regno = 0; regno < limit; regno++)
+ if ((regs_ever_live[regno] && ! call_used_regs[regno])
+ || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used))
+ {
+ xops[0] = gen_rtx (REG, SImode, regno);
+ xops[1] = adj_offsettable_operand (AT_BP (Pmode), offset);
+ output_asm_insn (AS2 (mov%L0,%1,%0), xops);
+ offset += 4;
+ }
+
+ if (frame_pointer_needed)
+ {
+ /* On i486, mov & pop is faster than "leave". */
+
+ if (TARGET_486)
+ {
+ xops[0] = frame_pointer_rtx;
+ output_asm_insn (AS2 (mov%L2,%0,%2), xops);
+ output_asm_insn ("pop%L0 %0", xops);
+ }
+ else
+ output_asm_insn ("leave", xops);
+ }
+ else if (size)
+ {
+ /* If there is no frame pointer, we must still release the frame. */
+
+ xops[0] = GEN_INT (size);
+ output_asm_insn (AS2 (add%L2,%0,%2), xops);
+ }
+
+ if (current_function_pops_args && current_function_args_size)
+ {
+ xops[1] = GEN_INT (current_function_pops_args);
+
+ /* i386 can only pop 32K bytes (maybe 64K? Is it signed?). If
+ asked to pop more, pop return address, do explicit add, and jump
+ indirectly to the caller. */
+
+ if (current_function_pops_args >= 32768)
+ {
+ /* ??? Which register to use here? */
+ xops[0] = gen_rtx (REG, SImode, 2);
+ output_asm_insn ("pop%L0 %0", xops);
+ output_asm_insn (AS2 (add%L2,%1,%2), xops);
+ output_asm_insn ("jmp %*%0", xops);
+ }
+ else
+ output_asm_insn ("ret %1", xops);
+ }
+ else
+ output_asm_insn ("ret", xops);
+}
+
+/* Print an integer constant expression in assembler syntax. Addition
+ and subtraction are the only arithmetic that may appear in these
+ expressions. FILE is the stdio stream to write to, X is the rtx, and
+ CODE is the operand print code from the output string. */
+
+static void
+output_pic_addr_const (file, x, code)
+ FILE *file;
+ rtx x;
+ int code;
+{
+ char buf[256];
+
+ switch (GET_CODE (x))
+ {
+ case PC:
+ if (flag_pic)
+ putc ('.', file);
+ else
+ abort ();
+ break;
+
+ case SYMBOL_REF:
+ case LABEL_REF:
+ if (GET_CODE (x) == SYMBOL_REF)
+ assemble_name (file, XSTR (x, 0));
+ else
+ {
+ ASM_GENERATE_INTERNAL_LABEL (buf, "L",
+ CODE_LABEL_NUMBER (XEXP (x, 0)));
+ assemble_name (asm_out_file, buf);
+ }
+
+ if (GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x))
+ fprintf (file, "@GOTOFF(%%ebx)");
+ else if (code == 'P')
+ fprintf (file, "@PLT");
+ else if (GET_CODE (x) == LABEL_REF || ! SYMBOL_REF_FLAG (x))
+ fprintf (file, "@GOT");
+ else
+ fprintf (file, "@GOTOFF");
+
+ break;
+
+ case CODE_LABEL:
+ ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x));
+ assemble_name (asm_out_file, buf);
+ break;
+
+ case CONST_INT:
+ fprintf (file, "%d", INTVAL (x));
+ break;
+
+ case CONST:
+ /* This used to output parentheses around the expression,
+ but that does not work on the 386 (either ATT or BSD assembler). */
+ output_pic_addr_const (file, XEXP (x, 0), code);
+ break;
+
+ case CONST_DOUBLE:
+ if (GET_MODE (x) == VOIDmode)
+ {
+ /* We can use %d if the number is <32 bits and positive. */
+ if (CONST_DOUBLE_HIGH (x) || CONST_DOUBLE_LOW (x) < 0)
+ fprintf (file, "0x%x%08x",
+ CONST_DOUBLE_HIGH (x), CONST_DOUBLE_LOW (x));
+ else
+ fprintf (file, "%d", CONST_DOUBLE_LOW (x));
+ }
+ else
+ /* We can't handle floating point constants;
+ PRINT_OPERAND must handle them. */
+ output_operand_lossage ("floating constant misused");
+ break;
+
+ case PLUS:
+ /* Some assemblers need integer constants to appear last (eg masm). */
+ if (GET_CODE (XEXP (x, 0)) == CONST_INT)
+ {
+ output_pic_addr_const (file, XEXP (x, 1), code);
+ if (INTVAL (XEXP (x, 0)) >= 0)
+ fprintf (file, "+");
+ output_pic_addr_const (file, XEXP (x, 0), code);
+ }
+ else
+ {
+ output_pic_addr_const (file, XEXP (x, 0), code);
+ if (INTVAL (XEXP (x, 1)) >= 0)
+ fprintf (file, "+");
+ output_pic_addr_const (file, XEXP (x, 1), code);
+ }
+ break;
+
+ case MINUS:
+ output_pic_addr_const (file, XEXP (x, 0), code);
+ fprintf (file, "-");
+ output_pic_addr_const (file, XEXP (x, 1), code);
+ break;
+
+ default:
+ output_operand_lossage ("invalid expression as operand");
+ }
+}
+
+/* Meaning of CODE:
+ f -- float insn (print a CONST_DOUBLE as a float rather than in hex).
+ D,L,W,B,Q,S -- print the opcode suffix for specified size of operand.
+ R -- print the prefix for register names.
+ z -- print the opcode suffix for the size of the current operand.
+ * -- print a star (in certain assembler syntax)
+ w -- print the operand as if it's a "word" (HImode) even if it isn't.
+ c -- don't print special prefixes before constant operands.
+*/
+
+void
+print_operand (file, x, code)
+ FILE *file;
+ rtx x;
+ int code;
+{
+ if (code)
+ {
+ switch (code)
+ {
+ case '*':
+ if (USE_STAR)
+ putc ('*', file);
+ return;
+
+ case 'L':
+ PUT_OP_SIZE (code, 'l', file);
+ return;
+
+ case 'W':
+ PUT_OP_SIZE (code, 'w', file);
+ return;
+
+ case 'B':
+ PUT_OP_SIZE (code, 'b', file);
+ return;
+
+ case 'Q':
+ PUT_OP_SIZE (code, 'l', file);
+ return;
+
+ case 'S':
+ PUT_OP_SIZE (code, 's', file);
+ return;
+
+ case 'T':
+ PUT_OP_SIZE (code, 't', file);
+ return;
+
+ case 'z':
+ /* 387 opcodes don't get size suffixes if the operands are
+ registers. */
+
+ if (STACK_REG_P (x))
+ return;
+
+ /* this is the size of op from size of operand */
+ switch (GET_MODE_SIZE (GET_MODE (x)))
+ {
+ case 1:
+ PUT_OP_SIZE ('B', 'b', file);
+ return;
+
+ case 2:
+ PUT_OP_SIZE ('W', 'w', file);
+ return;
+
+ case 4:
+ if (GET_MODE (x) == SFmode)
+ {
+ PUT_OP_SIZE ('S', 's', file);
+ return;
+ }
+ else
+ PUT_OP_SIZE ('L', 'l', file);
+ return;
+
+ case 12:
+ PUT_OP_SIZE ('T', 't', file);
+ return;
+
+ case 8:
+ if (GET_MODE_CLASS (GET_MODE (x)) == MODE_INT)
+ {
+#ifdef GAS_MNEMONICS
+ PUT_OP_SIZE ('Q', 'q', file);
+ return;
+#else
+ PUT_OP_SIZE ('Q', 'l', file); /* Fall through */
+#endif
+ }
+
+ PUT_OP_SIZE ('Q', 'l', file);
+ return;
+ }
+
+ case 'b':
+ case 'w':
+ case 'k':
+ case 'h':
+ case 'y':
+ case 'P':
+ break;
+
+ default:
+ {
+ char str[50];
+
+ sprintf (str, "invalid operand code `%c'", code);
+ output_operand_lossage (str);
+ }
+ }
+ }
+ if (GET_CODE (x) == REG)
+ {
+ PRINT_REG (x, code, file);
+ }
+ else if (GET_CODE (x) == MEM)
+ {
+ PRINT_PTR (x, file);
+ if (CONSTANT_ADDRESS_P (XEXP (x, 0)))
+ {
+ if (flag_pic)
+ output_pic_addr_const (file, XEXP (x, 0), code);
+ else
+ output_addr_const (file, XEXP (x, 0));
+ }
+ else
+ output_address (XEXP (x, 0));
+ }
+ else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == SFmode)
+ {
+ REAL_VALUE_TYPE r; long l;
+ REAL_VALUE_FROM_CONST_DOUBLE (r, x);
+ REAL_VALUE_TO_TARGET_SINGLE (r, l);
+ PRINT_IMMED_PREFIX (file);
+ fprintf (file, "0x%x", l);
+ }
+ /* These float cases don't actually occur as immediate operands. */
+ else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == DFmode)
+ {
+ REAL_VALUE_TYPE r; char dstr[30];
+ REAL_VALUE_FROM_CONST_DOUBLE (r, x);
+ REAL_VALUE_TO_DECIMAL (r, "%.22e", dstr);
+ fprintf (file, "%s", dstr);
+ }
+ else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == XFmode)
+ {
+ REAL_VALUE_TYPE r; char dstr[30];
+ REAL_VALUE_FROM_CONST_DOUBLE (r, x);
+ REAL_VALUE_TO_DECIMAL (r, "%.22e", dstr);
+ fprintf (file, "%s", dstr);
+ }
+ else
+ {
+ if (code != 'P')
+ {
+ if (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE)
+ PRINT_IMMED_PREFIX (file);
+ else if (GET_CODE (x) == CONST || GET_CODE (x) == SYMBOL_REF
+ || GET_CODE (x) == LABEL_REF)
+ PRINT_OFFSET_PREFIX (file);
+ }
+ if (flag_pic)
+ output_pic_addr_const (file, x, code);
+ else
+ output_addr_const (file, x);
+ }
+}
+
+/* Print a memory operand whose address is ADDR. */
+
+void
+print_operand_address (file, addr)
+ FILE *file;
+ register rtx addr;
+{
+ register rtx reg1, reg2, breg, ireg;
+ rtx offset;
+
+ switch (GET_CODE (addr))
+ {
+ case REG:
+ ADDR_BEG (file);
+ fprintf (file, "%se", RP);
+ fputs (hi_reg_name[REGNO (addr)], file);
+ ADDR_END (file);
+ break;
+
+ case PLUS:
+ reg1 = 0;
+ reg2 = 0;
+ ireg = 0;
+ breg = 0;
+ offset = 0;
+ if (CONSTANT_ADDRESS_P (XEXP (addr, 0)))
+ {
+ offset = XEXP (addr, 0);
+ addr = XEXP (addr, 1);
+ }
+ else if (CONSTANT_ADDRESS_P (XEXP (addr, 1)))
+ {
+ offset = XEXP (addr, 1);
+ addr = XEXP (addr, 0);
+ }
+ if (GET_CODE (addr) != PLUS) ;
+ else if (GET_CODE (XEXP (addr, 0)) == MULT)
+ {
+ reg1 = XEXP (addr, 0);
+ addr = XEXP (addr, 1);
+ }
+ else if (GET_CODE (XEXP (addr, 1)) == MULT)
+ {
+ reg1 = XEXP (addr, 1);
+ addr = XEXP (addr, 0);
+ }
+ else if (GET_CODE (XEXP (addr, 0)) == REG)
+ {
+ reg1 = XEXP (addr, 0);
+ addr = XEXP (addr, 1);
+ }
+ else if (GET_CODE (XEXP (addr, 1)) == REG)
+ {
+ reg1 = XEXP (addr, 1);
+ addr = XEXP (addr, 0);
+ }
+ if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT)
+ {
+ if (reg1 == 0) reg1 = addr;
+ else reg2 = addr;
+ addr = 0;
+ }
+ if (offset != 0)
+ {
+ if (addr != 0) abort ();
+ addr = offset;
+ }
+ if ((reg1 && GET_CODE (reg1) == MULT)
+ || (reg2 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg2))))
+ {
+ breg = reg2;
+ ireg = reg1;
+ }
+ else if (reg1 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg1)))
+ {
+ breg = reg1;
+ ireg = reg2;
+ }
+
+ if (ireg != 0 || breg != 0)
+ {
+ int scale = 1;
+
+ if (addr != 0)
+ {
+ if (GET_CODE (addr) == LABEL_REF)
+ output_asm_label (addr);
+ else
+ {
+ if (flag_pic)
+ output_pic_addr_const (file, addr, 0);
+ else
+ output_addr_const (file, addr);
+ }
+ }
+
+ if (ireg != 0 && GET_CODE (ireg) == MULT)
+ {
+ scale = INTVAL (XEXP (ireg, 1));
+ ireg = XEXP (ireg, 0);
+ }
+
+ /* The stack pointer can only appear as a base register,
+ never an index register, so exchange the regs if it is wrong. */
+
+ if (scale == 1 && ireg && REGNO (ireg) == STACK_POINTER_REGNUM)
+ {
+ rtx tmp;
+
+ tmp = breg;
+ breg = ireg;
+ ireg = tmp;
+ }
+
+ /* output breg+ireg*scale */
+ PRINT_B_I_S (breg, ireg, scale, file);
+ break;
+ }
+
+ case MULT:
+ {
+ int scale;
+ if (GET_CODE (XEXP (addr, 0)) == CONST_INT)
+ {
+ scale = INTVAL (XEXP (addr, 0));
+ ireg = XEXP (addr, 1);
+ }
+ else
+ {
+ scale = INTVAL (XEXP (addr, 1));
+ ireg = XEXP (addr, 0);
+ }
+ output_addr_const (file, const0_rtx);
+ PRINT_B_I_S ((rtx) 0, ireg, scale, file);
+ }
+ break;
+
+ default:
+ if (GET_CODE (addr) == CONST_INT
+ && INTVAL (addr) < 0x8000
+ && INTVAL (addr) >= -0x8000)
+ fprintf (file, "%d", INTVAL (addr));
+ else
+ {
+ if (flag_pic)
+ output_pic_addr_const (file, addr, 0);
+ else
+ output_addr_const (file, addr);
+ }
+ }
+}
+
+/* Set the cc_status for the results of an insn whose pattern is EXP.
+ On the 80386, we assume that only test and compare insns, as well
+ as SI, HI, & DI mode ADD, SUB, NEG, AND, IOR, XOR, ASHIFT,
+ ASHIFTRT, and LSHIFTRT instructions set the condition codes usefully.
+ Also, we assume that jumps, moves and sCOND don't affect the condition
+ codes. All else clobbers the condition codes, by assumption.
+
+ We assume that ALL integer add, minus, etc. instructions effect the
+ condition codes. This MUST be consistent with i386.md.
+
+ We don't record any float test or compare - the redundant test &
+ compare check in final.c does not handle stack-like regs correctly. */
+
+void
+notice_update_cc (exp)
+ rtx exp;
+{
+ if (GET_CODE (exp) == SET)
+ {
+ /* Jumps do not alter the cc's. */
+ if (SET_DEST (exp) == pc_rtx)
+ return;
+ /* Moving register or memory into a register:
+ it doesn't alter the cc's, but it might invalidate
+ the RTX's which we remember the cc's came from.
+ (Note that moving a constant 0 or 1 MAY set the cc's). */
+ if (REG_P (SET_DEST (exp))
+ && (REG_P (SET_SRC (exp)) || GET_CODE (SET_SRC (exp)) == MEM
+ || GET_RTX_CLASS (GET_CODE (SET_SRC (exp))) == '<'))
+ {
+ if (cc_status.value1
+ && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1))
+ cc_status.value1 = 0;
+ if (cc_status.value2
+ && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2))
+ cc_status.value2 = 0;
+ return;
+ }
+ /* Moving register into memory doesn't alter the cc's.
+ It may invalidate the RTX's which we remember the cc's came from. */
+ if (GET_CODE (SET_DEST (exp)) == MEM
+ && (REG_P (SET_SRC (exp))
+ || GET_RTX_CLASS (GET_CODE (SET_SRC (exp))) == '<'))
+ {
+ if (cc_status.value1 && GET_CODE (cc_status.value1) == MEM)
+ cc_status.value1 = 0;
+ if (cc_status.value2 && GET_CODE (cc_status.value2) == MEM)
+ cc_status.value2 = 0;
+ return;
+ }
+ /* Function calls clobber the cc's. */
+ else if (GET_CODE (SET_SRC (exp)) == CALL)
+ {
+ CC_STATUS_INIT;
+ return;
+ }
+ /* Tests and compares set the cc's in predictable ways. */
+ else if (SET_DEST (exp) == cc0_rtx)
+ {
+ CC_STATUS_INIT;
+ cc_status.value1 = SET_SRC (exp);
+ return;
+ }
+ /* Certain instructions effect the condition codes. */
+ else if (GET_MODE (SET_SRC (exp)) == SImode
+ || GET_MODE (SET_SRC (exp)) == HImode
+ || GET_MODE (SET_SRC (exp)) == QImode)
+ switch (GET_CODE (SET_SRC (exp)))
+ {
+ case ASHIFTRT: case LSHIFTRT:
+ case ASHIFT:
+ /* Shifts on the 386 don't set the condition codes if the
+ shift count is zero. */
+ if (GET_CODE (XEXP (SET_SRC (exp), 1)) != CONST_INT)
+ {
+ CC_STATUS_INIT;
+ break;
+ }
+ /* We assume that the CONST_INT is non-zero (this rtx would
+ have been deleted if it were zero. */
+
+ case PLUS: case MINUS: case NEG:
+ case AND: case IOR: case XOR:
+ cc_status.flags = CC_NO_OVERFLOW;
+ cc_status.value1 = SET_SRC (exp);
+ cc_status.value2 = SET_DEST (exp);
+ break;
+
+ default:
+ CC_STATUS_INIT;
+ }
+ else
+ {
+ CC_STATUS_INIT;
+ }
+ }
+ else if (GET_CODE (exp) == PARALLEL
+ && GET_CODE (XVECEXP (exp, 0, 0)) == SET)
+ {
+ if (SET_DEST (XVECEXP (exp, 0, 0)) == pc_rtx)
+ return;
+ if (SET_DEST (XVECEXP (exp, 0, 0)) == cc0_rtx)
+ {
+ CC_STATUS_INIT;
+ if (stack_regs_mentioned_p (SET_SRC (XVECEXP (exp, 0, 0))))
+ cc_status.flags |= CC_IN_80387;
+ else
+ cc_status.value1 = SET_SRC (XVECEXP (exp, 0, 0));
+ return;
+ }
+ CC_STATUS_INIT;
+ }
+ else
+ {
+ CC_STATUS_INIT;
+ }
+}
+
+/* Split one or more DImode RTL references into pairs of SImode
+ references. The RTL can be REG, offsettable MEM, integer constant, or
+ CONST_DOUBLE. "operands" is a pointer to an array of DImode RTL to
+ split and "num" is its length. lo_half and hi_half are output arrays
+ that parallel "operands". */
+
+void
+split_di (operands, num, lo_half, hi_half)
+ rtx operands[];
+ int num;
+ rtx lo_half[], hi_half[];
+{
+ while (num--)
+ {
+ if (GET_CODE (operands[num]) == REG)
+ {
+ lo_half[num] = gen_rtx (REG, SImode, REGNO (operands[num]));
+ hi_half[num] = gen_rtx (REG, SImode, REGNO (operands[num]) + 1);
+ }
+ else if (CONSTANT_P (operands[num]))
+ {
+ split_double (operands[num], &lo_half[num], &hi_half[num]);
+ }
+ else if (offsettable_memref_p (operands[num]))
+ {
+ lo_half[num] = operands[num];
+ hi_half[num] = adj_offsettable_operand (operands[num], 4);
+ }
+ else
+ abort();
+ }
+}
+
+/* Return 1 if this is a valid binary operation on a 387.
+ OP is the expression matched, and MODE is its mode. */
+
+int
+binary_387_op (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (mode != VOIDmode && mode != GET_MODE (op))
+ return 0;
+
+ switch (GET_CODE (op))
+ {
+ case PLUS:
+ case MINUS:
+ case MULT:
+ case DIV:
+ return GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT;
+
+ default:
+ return 0;
+ }
+}
+
+/* Return 1 if this is a valid conversion operation on a 387.
+ OP is the expression matched, and MODE is its mode. */
+
+int
+convert_387_op (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (mode != VOIDmode && mode != GET_MODE (op))
+ return 0;
+
+ switch (GET_CODE (op))
+ {
+ case FLOAT:
+ return GET_MODE (XEXP (op, 0)) == SImode;
+
+ case FLOAT_EXTEND:
+ return ((mode == DFmode && GET_MODE (XEXP (op, 0)) == SFmode)
+ || (mode == XFmode && GET_MODE (XEXP (op, 0)) == DFmode)
+ || (mode == XFmode && GET_MODE (XEXP (op, 0)) == SFmode));
+
+ default:
+ return 0;
+ }
+}
+
+/* Return 1 if this is a valid shift or rotate operation on a 386.
+ OP is the expression matched, and MODE is its mode. */
+
+int
+shift_op (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ rtx operand = XEXP (op, 0);
+
+ if (mode != VOIDmode && mode != GET_MODE (op))
+ return 0;
+
+ if (GET_MODE (operand) != GET_MODE (op)
+ || GET_MODE_CLASS (GET_MODE (op)) != MODE_INT)
+ return 0;
+
+ return (GET_CODE (op) == ASHIFT
+ || GET_CODE (op) == ASHIFTRT
+ || GET_CODE (op) == LSHIFTRT
+ || GET_CODE (op) == ROTATE
+ || GET_CODE (op) == ROTATERT);
+}
+
+/* Return 1 if OP is COMPARE rtx with mode VOIDmode.
+ MODE is not used. */
+
+int
+VOIDmode_compare_op (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ return GET_CODE (op) == COMPARE && GET_MODE (op) == VOIDmode;
+}
+
+/* Output code to perform a 387 binary operation in INSN, one of PLUS,
+ MINUS, MULT or DIV. OPERANDS are the insn operands, where operands[3]
+ is the expression of the binary operation. The output may either be
+ emitted here, or returned to the caller, like all output_* functions.
+
+ There is no guarantee that the operands are the same mode, as they
+ might be within FLOAT or FLOAT_EXTEND expressions. */
+
+char *
+output_387_binary_op (insn, operands)
+ rtx insn;
+ rtx *operands;
+{
+ rtx temp;
+ char *base_op;
+ static char buf[100];
+
+ switch (GET_CODE (operands[3]))
+ {
+ case PLUS:
+ if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
+ || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
+ base_op = "fiadd";
+ else
+ base_op = "fadd";
+ break;
+
+ case MINUS:
+ if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
+ || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
+ base_op = "fisub";
+ else
+ base_op = "fsub";
+ break;
+
+ case MULT:
+ if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
+ || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
+ base_op = "fimul";
+ else
+ base_op = "fmul";
+ break;
+
+ case DIV:
+ if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
+ || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
+ base_op = "fidiv";
+ else
+ base_op = "fdiv";
+ break;
+
+ default:
+ abort ();
+ }
+
+ strcpy (buf, base_op);
+
+ switch (GET_CODE (operands[3]))
+ {
+ case MULT:
+ case PLUS:
+ if (REG_P (operands[2]) && REGNO (operands[0]) == REGNO (operands[2]))
+ {
+ temp = operands[2];
+ operands[2] = operands[1];
+ operands[1] = temp;
+ }
+
+ if (GET_CODE (operands[2]) == MEM)
+ return strcat (buf, AS1 (%z2,%2));
+
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], strcat (buf, AS1 (%z0,%1)));
+ RET;
+ }
+ else if (NON_STACK_REG_P (operands[2]))
+ {
+ output_op_from_reg (operands[2], strcat (buf, AS1 (%z0,%1)));
+ RET;
+ }
+
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[2])))
+ return strcat (buf, AS2 (p,%2,%0));
+
+ if (STACK_TOP_P (operands[0]))
+ return strcat (buf, AS2 (,%y2,%0));
+ else
+ return strcat (buf, AS2 (,%2,%0));
+
+ case MINUS:
+ case DIV:
+ if (GET_CODE (operands[1]) == MEM)
+ return strcat (buf, AS1 (r%z1,%1));
+
+ if (GET_CODE (operands[2]) == MEM)
+ return strcat (buf, AS1 (%z2,%2));
+
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], strcat (buf, AS1 (r%z0,%1)));
+ RET;
+ }
+ else if (NON_STACK_REG_P (operands[2]))
+ {
+ output_op_from_reg (operands[2], strcat (buf, AS1 (%z0,%1)));
+ RET;
+ }
+
+ if (! STACK_REG_P (operands[1]) || ! STACK_REG_P (operands[2]))
+ abort ();
+
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[2])))
+ return strcat (buf, AS2 (rp,%2,%0));
+
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return strcat (buf, AS2 (p,%1,%0));
+
+ if (STACK_TOP_P (operands[0]))
+ {
+ if (STACK_TOP_P (operands[1]))
+ return strcat (buf, AS2 (,%y2,%0));
+ else
+ return strcat (buf, AS2 (r,%y1,%0));
+ }
+ else if (STACK_TOP_P (operands[1]))
+ return strcat (buf, AS2 (,%1,%0));
+ else
+ return strcat (buf, AS2 (r,%2,%0));
+
+ default:
+ abort ();
+ }
+}
+
+/* Output code for INSN to convert a float to a signed int. OPERANDS
+ are the insn operands. The output may be SFmode or DFmode and the
+ input operand may be SImode or DImode. As a special case, make sure
+ that the 387 stack top dies if the output mode is DImode, because the
+ hardware requires this. */
+
+char *
+output_fix_trunc (insn, operands)
+ rtx insn;
+ rtx *operands;
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+ rtx xops[2];
+
+ if (! STACK_TOP_P (operands[1]) ||
+ (GET_MODE (operands[0]) == DImode && ! stack_top_dies))
+ abort ();
+
+ xops[0] = GEN_INT (12);
+ xops[1] = operands[4];
+
+ output_asm_insn (AS1 (fnstc%W2,%2), operands);
+ output_asm_insn (AS2 (mov%L2,%2,%4), operands);
+ output_asm_insn (AS2 (mov%B1,%0,%h1), xops);
+ output_asm_insn (AS2 (mov%L4,%4,%3), operands);
+ output_asm_insn (AS1 (fldc%W3,%3), operands);
+
+ if (NON_STACK_REG_P (operands[0]))
+ output_to_reg (operands[0], stack_top_dies);
+ else if (GET_CODE (operands[0]) == MEM)
+ {
+ if (stack_top_dies)
+ output_asm_insn (AS1 (fistp%z0,%0), operands);
+ else
+ output_asm_insn (AS1 (fist%z0,%0), operands);
+ }
+ else
+ abort ();
+
+ return AS1 (fldc%W2,%2);
+}
+
+/* Output code for INSN to compare OPERANDS. The two operands might
+ not have the same mode: one might be within a FLOAT or FLOAT_EXTEND
+ expression. If the compare is in mode CCFPEQmode, use an opcode that
+ will not fault if a qNaN is present. */
+
+char *
+output_float_compare (insn, operands)
+ rtx insn;
+ rtx *operands;
+{
+ int stack_top_dies;
+ rtx body = XVECEXP (PATTERN (insn), 0, 0);
+ int unordered_compare = GET_MODE (SET_SRC (body)) == CCFPEQmode;
+
+ if (! STACK_TOP_P (operands[0]))
+ abort ();
+
+ stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ if (STACK_REG_P (operands[1])
+ && stack_top_dies
+ && find_regno_note (insn, REG_DEAD, REGNO (operands[1]))
+ && REGNO (operands[1]) != FIRST_STACK_REG)
+ {
+ /* If both the top of the 387 stack dies, and the other operand
+ is also a stack register that dies, then this must be a
+ `fcompp' float compare */
+
+ if (unordered_compare)
+ output_asm_insn ("fucompp", operands);
+ else
+ output_asm_insn ("fcompp", operands);
+ }
+ else
+ {
+ static char buf[100];
+
+ /* Decide if this is the integer or float compare opcode, or the
+ unordered float compare. */
+
+ if (unordered_compare)
+ strcpy (buf, "fucom");
+ else if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_FLOAT)
+ strcpy (buf, "fcom");
+ else
+ strcpy (buf, "ficom");
+
+ /* Modify the opcode if the 387 stack is to be popped. */
+
+ if (stack_top_dies)
+ strcat (buf, "p");
+
+ if (NON_STACK_REG_P (operands[1]))
+ output_op_from_reg (operands[1], strcat (buf, AS1 (%z0,%1)));
+ else
+ output_asm_insn (strcat (buf, AS1 (%z1,%y1)), operands);
+ }
+
+ /* Now retrieve the condition code. */
+
+ return output_fp_cc0_set (insn);
+}
+
+/* Output opcodes to transfer the results of FP compare or test INSN
+ from the FPU to the CPU flags. If TARGET_IEEE_FP, ensure that if the
+ result of the compare or test is unordered, no comparison operator
+ succeeds except NE. Return an output template, if any. */
+
+char *
+output_fp_cc0_set (insn)
+ rtx insn;
+{
+ rtx xops[3];
+ rtx unordered_label;
+ rtx next;
+ enum rtx_code code;
+
+ xops[0] = gen_rtx (REG, HImode, 0);
+ output_asm_insn (AS1 (fnsts%W0,%0), xops);
+
+ if (! TARGET_IEEE_FP)
+ return "sahf";
+
+ next = next_cc0_user (insn);
+ if (next == NULL_RTX)
+ abort ();
+
+ if (GET_CODE (next) == JUMP_INSN
+ && GET_CODE (PATTERN (next)) == SET
+ && SET_DEST (PATTERN (next)) == pc_rtx
+ && GET_CODE (SET_SRC (PATTERN (next))) == IF_THEN_ELSE)
+ {
+ code = GET_CODE (XEXP (SET_SRC (PATTERN (next)), 0));
+ }
+ else if (GET_CODE (PATTERN (next)) == SET)
+ {
+ code = GET_CODE (SET_SRC (PATTERN (next)));
+ }
+ else
+ abort ();
+
+ xops[0] = gen_rtx (REG, QImode, 0);
+
+ switch (code)
+ {
+ case GT:
+ xops[1] = GEN_INT (0x45);
+ output_asm_insn (AS2 (and%B0,%1,%h0), xops);
+ /* je label */
+ break;
+
+ case LT:
+ xops[1] = GEN_INT (0x45);
+ xops[2] = GEN_INT (0x01);
+ output_asm_insn (AS2 (and%B0,%1,%h0), xops);
+ output_asm_insn (AS2 (cmp%B0,%2,%h0), xops);
+ /* je label */
+ break;
+
+ case GE:
+ xops[1] = GEN_INT (0x05);
+ output_asm_insn (AS2 (and%B0,%1,%h0), xops);
+ /* je label */
+ break;
+
+ case LE:
+ xops[1] = GEN_INT (0x45);
+ xops[2] = GEN_INT (0x40);
+ output_asm_insn (AS2 (and%B0,%1,%h0), xops);
+ output_asm_insn (AS1 (dec%B0,%h0), xops);
+ output_asm_insn (AS2 (cmp%B0,%2,%h0), xops);
+ /* jb label */
+ break;
+
+ case EQ:
+ xops[1] = GEN_INT (0x45);
+ xops[2] = GEN_INT (0x40);
+ output_asm_insn (AS2 (and%B0,%1,%h0), xops);
+ output_asm_insn (AS2 (cmp%B0,%2,%h0), xops);
+ /* je label */
+ break;
+
+ case NE:
+ xops[1] = GEN_INT (0x44);
+ xops[2] = GEN_INT (0x40);
+ output_asm_insn (AS2 (and%B0,%1,%h0), xops);
+ output_asm_insn (AS2 (xor%B0,%2,%h0), xops);
+ /* jne label */
+ break;
+
+ case GTU:
+ case LTU:
+ case GEU:
+ case LEU:
+ default:
+ abort ();
+ }
+ RET;
+}
+
+#define MAX_386_STACK_LOCALS 2
+
+static rtx i386_stack_locals[(int) MAX_MACHINE_MODE][MAX_386_STACK_LOCALS];
+
+/* Define the structure for the machine field in struct function. */
+struct machine_function
+{
+ rtx i386_stack_locals[(int) MAX_MACHINE_MODE][MAX_386_STACK_LOCALS];
+};
+
+/* Functions to save and restore i386_stack_locals.
+ These will be called, via pointer variables,
+ from push_function_context and pop_function_context. */
+
+void
+save_386_machine_status (p)
+ struct function *p;
+{
+ p->machine = (struct machine_function *) xmalloc (sizeof i386_stack_locals);
+ bcopy (i386_stack_locals, p->machine->i386_stack_locals,
+ sizeof i386_stack_locals);
+}
+
+void
+restore_386_machine_status (p)
+ struct function *p;
+{
+ bcopy (p->machine->i386_stack_locals, i386_stack_locals,
+ sizeof i386_stack_locals);
+ free (p->machine);
+}
+
+/* Clear stack slot assignments remembered from previous functions.
+ This is called from INIT_EXPANDERS once before RTL is emitted for each
+ function. */
+
+void
+clear_386_stack_locals ()
+{
+ enum machine_mode mode;
+ int n;
+
+ for (mode = VOIDmode; (int) mode < (int) MAX_MACHINE_MODE;
+ mode = (enum machine_mode) ((int) mode + 1))
+ for (n = 0; n < MAX_386_STACK_LOCALS; n++)
+ i386_stack_locals[(int) mode][n] = NULL_RTX;
+
+ /* Arrange to save and restore i386_stack_locals around nested functions. */
+ save_machine_status = save_386_machine_status;
+ restore_machine_status = restore_386_machine_status;
+}
+
+/* Return a MEM corresponding to a stack slot with mode MODE.
+ Allocate a new slot if necessary.
+
+ The RTL for a function can have several slots available: N is
+ which slot to use. */
+
+rtx
+assign_386_stack_local (mode, n)
+ enum machine_mode mode;
+ int n;
+{
+ if (n < 0 || n >= MAX_386_STACK_LOCALS)
+ abort ();
+
+ if (i386_stack_locals[(int) mode][n] == NULL_RTX)
+ i386_stack_locals[(int) mode][n]
+ = assign_stack_local (mode, GET_MODE_SIZE (mode), 0);
+
+ return i386_stack_locals[(int) mode][n];
+}
diff --git a/gnu/usr.bin/cc/cc_int/bc-emit.c b/gnu/usr.bin/cc/cc_int/bc-emit.c
new file mode 100644
index 0000000..b5d0c6c
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/bc-emit.c
@@ -0,0 +1,991 @@
+/* Output bytecodes for GNU C-compiler.
+ Copyright (C) 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include "machmode.h"
+#include "rtl.h"
+#include "real.h"
+#include "obstack.h"
+#include "bytecode.h"
+#ifdef __GNUC__
+#include "bytetypes.h"
+#endif
+#include "bc-emit.h"
+#include "bc-opcode.h"
+#include "bc-typecd.h"
+#include "bi-run.h"
+
+#include <stdio.h>
+
+extern char *xmalloc (), *xrealloc ();
+extern void free ();
+
+extern struct obstack *rtl_obstack;
+
+/* Indexed by mode class, gives the narrowest mode for each class. */
+
+extern enum machine_mode class_narrowest_mode[(int) MAX_MODE_CLASS];
+
+/* Commonly used modes. */
+/* Mode whose width is BITS_PER_UNIT */
+extern enum machine_mode byte_mode;
+
+/* Mode whose width is BITS_PER_WORD */
+extern enum machine_mode word_mode;
+
+/* Vector indexed by opcode giving info about the args for each opcode. */
+static struct arityvec arityvec[] = {
+#include "bc-arity.h"
+};
+
+/* How to print a symbol name for the assembler. */
+static void
+prsym (file, s)
+ FILE *file;
+ char *s;
+{
+ if (*s == '*')
+ fprintf (file, "%s", s + 1);
+ else
+
+#ifdef NAMES_HAVE_UNDERSCORES
+ fprintf (file, "_%s", s);
+#else
+ fprintf (file, "%s", s);
+#endif
+
+}
+
+/* Maintain a bucket hash table for symbol names. */
+
+#define HASH_BITS 32
+#define HASH_SIZE 509
+
+static struct bc_sym *hashtab[HASH_SIZE];
+
+static unsigned int
+hash (name)
+ char *name;
+{
+ unsigned int hash = 0;
+
+ while (*name)
+ {
+ hash = hash << 3 | hash >> HASH_BITS - 3;
+ hash += *name++;
+ }
+
+ return hash % HASH_SIZE;
+}
+
+
+/* Look up the named symbol, creating it if it doesn't exist. */
+struct bc_sym *
+sym_lookup (name)
+ char *name;
+{
+ int i;
+ struct bc_sym *s;
+
+ i = hash (name);
+ for (s = hashtab[i]; s; s = s->next)
+ if (!strcmp (s->name, name))
+ return s;
+
+ s = (struct bc_sym *) xmalloc (sizeof (struct bc_sym));
+ s->name = xmalloc (strlen (name) + 1);
+ strcpy (s->name, name);
+ s->defined = s->global = s->common = 0;
+ s->val = 0;
+ s->next = hashtab[i];
+ hashtab[i] = s;
+ return s;
+}
+
+
+/* Write out .globl and common symbols to the named file. */
+static void
+bc_sym_write (file)
+ FILE *file;
+{
+ int i;
+ struct bc_sym *s;
+
+ for (i = 0; i < HASH_SIZE; ++i)
+ for (s = hashtab[i]; s; s = s->next)
+ {
+ if (s->global)
+ {
+ fprintf (file, "\n\t.globl ");
+ prsym (file, s->name);
+ putc ('\n', file);
+ if (s->common)
+ {
+ fprintf (file, "\n\t.comm ");
+ prsym (file, s->name);
+ fprintf (file, ", %d\n", s->val);
+ }
+ }
+ else if (s->common)
+ {
+ fprintf (file, "\n\t.lcomm ");
+ prsym (file, s->name);
+ fprintf (file, ", %d\n", s->val);
+ }
+ }
+}
+
+
+
+
+/* Create and initialize a new segment. */
+static struct bc_seg *
+seg_create ()
+{
+ struct bc_seg *result;
+
+ result = (struct bc_seg *) xmalloc (sizeof (struct bc_seg));
+ result->alloc = 256;
+ result->data = xmalloc (result->alloc);
+ result->size = 0;
+ result->syms = 0;
+ result->relocs = 0;
+ return result;
+}
+
+
+/* Advance the segment index to the next alignment boundary. */
+static void
+seg_align (seg, log)
+ struct bc_seg *seg;
+ int log;
+{
+ unsigned int oldsize = seg->size;
+
+ seg->size = seg->size + (1 << log) - 1 & ~((1 << log) - 1);
+ if (seg->size > seg->alloc)
+ {
+ while (seg->size > seg->alloc)
+ seg->alloc *= 2;
+ seg->data = xrealloc (seg->data, seg->alloc);
+ }
+ bzero (seg->data + oldsize, seg->size - oldsize);
+}
+
+
+/* Append the given data to the given segment. */
+static void
+seg_data (seg, data, size)
+ struct bc_seg *seg;
+ char *data;
+ unsigned int size;
+{
+ if (seg->size + size > seg->alloc)
+ {
+ while (seg->size + size > seg->alloc)
+ seg->alloc *= 2;
+ seg->data = xrealloc (seg->data, seg->alloc);
+ }
+
+ bcopy (data, seg->data + seg->size, size);
+ seg->size += size;
+}
+
+
+/* Append a zero-filled skip to the given segment. */
+static void
+seg_skip (seg, size)
+ struct bc_seg *seg;
+ unsigned int size;
+{
+ if (seg->size + size > seg->alloc)
+ {
+ while (seg->size + size > seg->alloc)
+ seg->alloc *= 2;
+ seg->data = xrealloc (seg->data, seg->alloc);
+ }
+
+ memset (seg->data + seg->size, 0, size);
+ seg->size += size;
+}
+
+
+/* Define the given name as the current offset in the given segment. It
+ is an error if the name is already defined. Return 0 or 1 indicating
+ failure or success respectively. */
+static int
+seg_defsym (seg, name)
+ struct bc_seg *seg;
+ char *name;
+{
+ struct bc_sym *sym;
+ struct bc_segsym *segsym;
+
+ sym = sym_lookup (name);
+ if (sym->defined)
+ return 0;
+
+ sym->defined = 1;
+ sym->val = seg->size;
+ segsym = (struct bc_segsym *) xmalloc (sizeof (struct bc_segsym));
+ segsym->sym = sym;
+ segsym->next = seg->syms;
+ seg->syms = segsym;
+ return 1;
+}
+
+
+/* Generate in seg's data a reference to the given sym, adjusted by
+ the given offset. */
+static void
+seg_refsym (seg, name, offset)
+ struct bc_seg *seg;
+ char *name;
+ int offset;
+{
+ struct bc_sym *sym;
+ struct bc_segreloc *segreloc;
+
+ sym = sym_lookup (name);
+ segreloc = (struct bc_segreloc *) xmalloc (sizeof (struct bc_segreloc));
+ segreloc->offset = seg->size;
+ segreloc->sym = sym;
+ segreloc->next = seg->relocs;
+ seg->relocs = segreloc;
+ seg_data (seg, (char *) &offset, sizeof offset);
+}
+
+
+/* Concatenate the contents of given segments into the first argument. */
+static void
+seg_concat (result, seg)
+ struct bc_seg *result, *seg;
+{
+ unsigned int fix;
+ struct bc_segsym *segsym;
+ struct bc_segreloc *segreloc;
+
+ seg_align (result, MACHINE_SEG_ALIGN);
+ fix = result->size;
+ seg_data (result, seg->data, seg->size);
+ free (seg->data);
+
+ /* Go through the symbols and relocs of SEG, adjusting their offsets
+ for their new location in RESULT. */
+ if (seg->syms)
+ {
+ segsym = seg->syms;
+ do
+ segsym->sym->val += fix;
+ while (segsym->next && (segsym = segsym->next));
+ segsym->next = result->syms;
+ result->syms = seg->syms;
+ }
+ if (seg->relocs)
+ {
+ segreloc = seg->relocs;
+ do
+ segreloc->offset += fix;
+ while (segreloc->next && (segreloc = segreloc->next));
+ segreloc->next = result->relocs;
+ result->relocs = seg->relocs;
+ }
+
+ free ((char *) seg);
+}
+
+/* Write a segment to a file. */
+static void
+bc_seg_write (seg, file)
+ struct bc_seg *seg;
+ FILE *file;
+{
+ struct bc_segsym *segsym, *nsegsym, *psegsym;
+ struct bc_segreloc *segreloc, *nsegreloc, *psegreloc;
+ int i, offset, flag;
+
+ /* Reverse the list of symbols. */
+ for (psegsym = 0, segsym = seg->syms; segsym; segsym = nsegsym)
+ {
+ nsegsym = segsym->next;
+ segsym->next = psegsym;
+ psegsym = segsym;
+ }
+ seg->syms = psegsym;
+
+ /* Reverse the list of relocs. */
+ for (psegreloc = 0, segreloc = seg->relocs; segreloc; segreloc = nsegreloc)
+ {
+ nsegreloc = segreloc->next;
+ segreloc->next = psegreloc;
+ psegreloc = segreloc;
+ }
+ seg->relocs = psegreloc;
+
+ /* Output each byte of the segment. */
+ for (i = 0, segsym = seg->syms, segreloc = seg->relocs; i < seg->size; ++i)
+ {
+ while (segsym && segsym->sym->val == i)
+ {
+ if (i % 8 != 0)
+ putc ('\n', file);
+
+ BC_WRITE_SEGSYM (segsym, file);
+ segsym = segsym->next;
+ flag = 1;
+ }
+ if (segreloc && segreloc->offset == i)
+ {
+ if (i % 8 != 0)
+ putc ('\n', file);
+
+ bcopy (seg->data + i, (char *) &offset, sizeof (int));
+ i += sizeof (int) - 1;
+
+ BC_WRITE_RELOC_ENTRY (segreloc, file, offset);
+ segreloc = segreloc->next;
+ flag = 1;
+ }
+ else
+ {
+ if (i % 8 == 0 || flag)
+ BC_START_BYTECODE_LINE (file);
+
+ BC_WRITE_BYTECODE (i % 8 == 0 || flag ? ' ' : ',',
+ seg->data[i] & 0xFF,
+ file);
+ flag = 0;
+ if (i % 8 == 7)
+ putc ('\n', file);
+ }
+ }
+
+ /* Paranoia check--we should have visited all syms and relocs during
+ the output pass. */
+
+ if (segsym || segreloc)
+ abort ();
+}
+
+
+
+/* Text and data segments of the object file in making. */
+static struct bc_seg *bc_text_seg;
+static struct bc_seg *bc_data_seg;
+
+/* Called before anything else in this module. */
+void
+bc_initialize ()
+{
+ int min_class_size[(int) MAX_MODE_CLASS];
+ enum machine_mode mode;
+ int i;
+
+ bc_init_mode_to_code_map ();
+
+ bc_text_seg = seg_create ();
+ bc_data_seg = seg_create ();
+
+ dconst0 = REAL_VALUE_ATOF ("0", DFmode);
+ dconst1 = REAL_VALUE_ATOF ("1", DFmode);
+ dconst2 = REAL_VALUE_ATOF ("2", DFmode);
+ dconstm1 = REAL_VALUE_ATOF ("-1", DFmode);
+
+ /* Find the narrowest mode for each class and compute the word and byte
+ modes. */
+
+ for (i = 0; i < (int) MAX_MODE_CLASS; i++)
+ min_class_size[i] = 1000;
+
+ for (mode = VOIDmode; (int) mode < (int) MAX_MACHINE_MODE;
+ mode = (enum machine_mode) ((int) mode + 1))
+ {
+ if (GET_MODE_SIZE (mode) < min_class_size[(int) GET_MODE_CLASS (mode)])
+ {
+ class_narrowest_mode[(int) GET_MODE_CLASS (mode)] = mode;
+ min_class_size[(int) GET_MODE_CLASS (mode)] = GET_MODE_SIZE (mode);
+ }
+ if (GET_MODE_CLASS (mode) == MODE_INT
+ && GET_MODE_BITSIZE (mode) == BITS_PER_UNIT)
+ byte_mode = mode;
+
+ if (GET_MODE_CLASS (mode) == MODE_INT
+ && GET_MODE_BITSIZE (mode) == BITS_PER_WORD)
+ word_mode = mode;
+ }
+}
+
+
+/* External addresses referenced in a function. Rather than trying to
+ work relocatable address directly into bytecoded functions (which would
+ require us to provide hairy location info and possibly obey alignment
+ rules imposed by the architecture) we build an auxilary table of
+ pointer constants, and encode just offsets into this table into the
+ actual bytecode. */
+static struct bc_seg *ptrconsts;
+
+/* Trampoline code for the function entry. */
+struct bc_seg *trampoline;
+
+/* Actual byte code of the function. */
+struct bc_seg *bytecode;
+
+/* List of labels defined in the function. */
+struct bc_label *labels;
+
+/* List of label references in the function. */
+struct bc_labelref *labelrefs;
+
+
+/* Add symbol to pointer table. Return offset into table where
+ pointer was stored. The offset usually goes into the bytecode
+ stream as a constP literal. */
+int
+bc_define_pointer (p)
+ char *p;
+{
+ int offset = ptrconsts->size;
+
+ seg_refsym (ptrconsts, p, 0);
+ return offset;
+}
+
+
+/* Begin a bytecoded function. */
+int
+bc_begin_function (name)
+ char *name;
+{
+ ptrconsts = seg_create ();
+ trampoline = seg_create ();
+ bytecode = seg_create ();
+ return seg_defsym (trampoline, name);
+}
+
+
+/* Force alignment in inline bytecode. */
+void
+bc_align_bytecode (align)
+ int align;
+{
+ seg_align (bytecode, align);
+}
+
+
+/* Emit data inline into bytecode. */
+void
+bc_emit_bytecode_const (data, size)
+ char *data;
+ unsigned int size;
+{
+ if (bytecode)
+ seg_data (bytecode, data, size);
+}
+
+
+/* Create a new "bytecode label", to have its value defined later.
+ Bytecode labels have nothing to do with the object file symbol table,
+ and are purely local to a given bytecoded function. */
+struct bc_label *
+bc_get_bytecode_label ()
+{
+ struct bc_label *result;
+
+ result = (struct bc_label *) xmalloc (sizeof (struct bc_label));
+ result->defined = 0;
+ result->next = labels;
+ result->uid = 0;
+ labels = result;
+ return result;
+}
+
+
+/* Define the given label with the current location counter. */
+int
+bc_emit_bytecode_labeldef (label)
+ struct bc_label *label;
+{
+ extern int bc_new_uid ();
+
+ if (!label || label->defined)
+ return 0;
+
+ label->offset = bytecode->size;
+ label->defined = 1;
+ label->uid = bc_new_uid ();
+
+#ifdef DEBUG_PRINT_CODE
+ fprintf (stderr, "$%lx:\n", label);
+#endif
+
+ return 1;
+}
+
+
+/* Generate a location-relative reference to the given bytecode label.
+ It need not be defined yet; label references will be backpatched later. */
+void
+bc_emit_bytecode_labelref (label)
+ struct bc_label *label;
+{
+ struct bc_labelref *labelref;
+ static int zero;
+
+ labelref = (struct bc_labelref *) xmalloc (sizeof (struct bc_labelref));
+ labelref->label = label;
+ labelref->offset = bytecode->size;
+ labelref->next = labelrefs;
+ labelrefs = labelref;
+
+#ifdef DEBUG_PRINT_CODE
+ fprintf (stderr, " $%lx", label);
+#endif
+
+ seg_data (bytecode, (char *) &zero, sizeof zero);
+}
+
+
+/* Emit a reference to an external address; generate the reference in the
+ ptrconst area, and emit an offset in the bytecode. */
+void
+bc_emit_code_labelref (name, offset)
+ char *name;
+ int offset;
+{
+ int ptroff;
+
+ ptroff = ptrconsts->size / sizeof (char *);
+ seg_data (bytecode, (char *) &ptroff, sizeof ptroff);
+ seg_refsym (ptrconsts, name, offset);
+
+#ifdef DEBUG_PRINT_CODE
+ fprintf (stderr, " [external <%x> %s]", ptroff, name);
+#endif
+}
+
+
+/* Backpatch label references in the byte code, and concatenate the bytecode
+ and pointer constant segments to the cumulative text for the object file.
+ Return a label name for the pointer constants region. */
+char *
+bc_end_function ()
+{
+ int addr;
+ struct bc_label *label, *next;
+ struct bc_labelref *ref, *nextref;
+ char ptrconsts_label[20];
+ static int nlab;
+
+ /* Backpatch bytecode label references. */
+ for (ref = labelrefs; ref; ref = ref->next)
+ if (ref->label->defined)
+ {
+ addr = ref->label->offset;
+ bcopy ((char *) &addr, bytecode->data + ref->offset, sizeof addr);
+ }
+
+ /* Free the chains of labelrefs and labeldefs. */
+ for (ref = labelrefs; ref; ref = nextref)
+ {
+ nextref = ref->next;
+ free ((char *) ref);
+ }
+
+ for (label = labels; label; label = next)
+ {
+ next = label->next;
+ free ((char *) label);
+ }
+
+ seg_concat (trampoline, bytecode);
+ seg_align (trampoline, MACHINE_SEG_ALIGN);
+ sprintf (ptrconsts_label, "*LP%d", nlab++);
+ seg_defsym (trampoline, ptrconsts_label);
+ seg_concat (trampoline, ptrconsts);
+ seg_concat (bc_text_seg, trampoline);
+
+ labels = 0;
+ labelrefs = 0;
+ trampoline = 0;
+ bytecode = 0;
+ ptrconsts = 0;
+
+ return sym_lookup (ptrconsts_label)->name;
+}
+
+/* Force alignment in const data. */
+void
+bc_align_const (align)
+ int align;
+{
+ seg_align (bc_text_seg, align);
+}
+
+/* Emit const data. */
+void
+bc_emit_const (data, size)
+ char *data;
+ unsigned int size;
+{
+ seg_data (bc_text_seg, data, size);
+}
+
+/* Emit a zero-filled constant skip. */
+void
+bc_emit_const_skip (size)
+ unsigned int size;
+{
+ seg_skip (bc_text_seg, size);
+}
+
+/* Emit a label definition in const data. */
+int
+bc_emit_const_labeldef (name)
+ char *name;
+{
+ return seg_defsym (bc_text_seg, name);
+}
+
+/* Emit a label reference in const data. */
+void
+bc_emit_const_labelref (name, offset)
+ char *name;
+ int offset;
+{
+ seg_refsym (bc_text_seg, name, offset);
+}
+
+/* Force alignment in data. */
+void
+bc_align_data (align)
+ int align;
+{
+ seg_align (bc_data_seg, align);
+}
+
+/* Emit data. */
+void
+bc_emit_data (data, size)
+ char *data;
+ unsigned int size;
+{
+ seg_data (bc_data_seg, data, size);
+}
+
+/* Emit a zero-filled data skip. */
+void
+bc_emit_data_skip (size)
+ unsigned int size;
+{
+ seg_skip (bc_data_seg, size);
+}
+
+/* Emit label definition in data. */
+int
+bc_emit_data_labeldef (name)
+ char *name;
+{
+ return seg_defsym (bc_data_seg, name);
+}
+
+/* Emit label reference in data. */
+void
+bc_emit_data_labelref (name, offset)
+ char *name;
+ int offset;
+{
+ seg_refsym (bc_data_seg, name, offset);
+}
+
+/* Emit a common block of the given name and size. Note that
+ when the .o file is actually written non-global "common"
+ blocks will have to be turned into space in the data section. */
+int
+bc_emit_common (name, size)
+ char *name;
+ unsigned int size;
+{
+ struct bc_sym *sym;
+
+ sym = sym_lookup (name);
+ if (sym->defined)
+ return 0;
+
+ sym->defined = 1;
+ sym->common = 1;
+ sym->val = size;
+ return 1;
+}
+
+/* Globalize the given label. */
+void
+bc_globalize_label (name)
+ char *name;
+{
+ struct bc_sym *sym;
+
+ sym = sym_lookup (name);
+ sym->global = 1;
+}
+
+static enum { in_text, in_data } section = in_text;
+
+void
+bc_text ()
+{
+ section = in_text;
+}
+
+void
+bc_data ()
+{
+ section = in_data;
+}
+
+void
+bc_align (align)
+ int align;
+{
+ if (section == in_text)
+ bc_align_const (align);
+ else
+ bc_align_data (align);
+}
+
+void
+bc_emit (data, size)
+ char *data;
+ unsigned int size;
+{
+ if (section == in_text)
+ bc_emit_const (data, size);
+ else
+ bc_emit_data (data, size);
+}
+
+void
+bc_emit_skip (size)
+ unsigned int size;
+{
+ if (section == in_text)
+ bc_emit_const_skip (size);
+ else
+ bc_emit_data_skip (size);
+}
+
+int
+bc_emit_labeldef (name)
+ char *name;
+{
+ if (section == in_text)
+ return bc_emit_const_labeldef (name);
+ else
+ return bc_emit_data_labeldef (name);
+}
+
+void
+bc_emit_labelref (name, offset)
+ char *name;
+ int offset;
+{
+ if (section == in_text)
+ bc_emit_const_labelref (name, offset);
+ else
+ bc_emit_data_labelref (name, offset);
+}
+
+void
+bc_write_file (file)
+ FILE *file;
+{
+ BC_WRITE_FILE (file);
+}
+
+
+/* Allocate a new bytecode rtx.
+ If you supply a null BC_LABEL, we generate one. */
+
+rtx
+bc_gen_rtx (label, offset, bc_label)
+ char *label;
+ int offset;
+ struct bc_label *bc_label;
+{
+ rtx r;
+
+ if (bc_label == 0)
+ bc_label = (struct bc_label *) xmalloc (sizeof (struct bc_label));
+
+ r = gen_rtx (CODE_LABEL, VOIDmode, label, bc_label);
+ bc_label->offset = offset;
+
+ return r;
+}
+
+
+/* Print bytecode rtx */
+void
+bc_print_rtl (fp, r)
+ FILE *fp;
+ rtx r;
+{
+#if 0 /* This needs to get fixed to really work again. */
+ /* BC_WRITE_RTL has a definition
+ that doesn't even make sense for this use. */
+ BC_WRITE_RTL (r, fp);
+#endif
+}
+
+
+/* Emit a bytecode, keeping a running tally of the stack depth. */
+void
+bc_emit_bytecode (bytecode)
+ enum bytecode_opcode bytecode;
+{
+ char byte;
+ static int prev_lineno = -1;
+
+ byte = bytecode;
+
+#ifdef BCDEBUG_PRINT_CODE
+ if (lineno != prev_lineno)
+ {
+ fprintf (stderr, "<line %d>\n", lineno);
+ prev_lineno = lineno;
+ }
+
+ fputs (opcode_name[(unsigned int) bytecode], stderr);
+#endif
+
+ /* Due to errors we are often requested to output bytecodes that
+ will cause an interpreter stack undeflow when executed. Instead of
+ dumping core on such occasions, we omit the bytecode. Erroneous code
+ should not be executed, regardless. This makes life much easier, since
+ we don't have to deceive ourselves about the known stack depth. */
+
+ bc_emit_bytecode_const (&byte, 1);
+
+ if ((stack_depth -= arityvec[(int) bytecode].ninputs) >= 0)
+ {
+ if ((stack_depth += arityvec[(int) bytecode].noutputs) > max_stack_depth)
+ max_stack_depth = stack_depth;
+ }
+
+#ifdef VALIDATE_STACK_FOR_BC
+ VALIDATE_STACK_FOR_BC ();
+#endif
+}
+
+
+#ifdef BCDEBUG_PRINT_CODE
+#define PRLIT(TYPE, PTR) fprintf (stderr, " [%x]", *(TYPE *) PTR)
+#else
+#define PRLIT(X,Y)
+#endif
+
+/* Emit a complete bytecode instruction, expecting the correct number
+ of literal values in the call. First argument is the instruction, the
+ remaining arguments are literals of size HOST_WIDE_INT or smaller. */
+void
+bc_emit_instruction VPROTO((enum bytecode_opcode opcode, ...))
+{
+#ifndef __STDC__
+ enum bytecode_opcode opcode;
+#endif
+ va_list arguments;
+ int nliteral, instruction;
+
+ VA_START (arguments, opcode);
+
+#ifndef __STDC__
+ opcode = va_arg (arguments, enum bytecode_opcode);
+#endif
+
+ /* Emit instruction bytecode */
+ bc_emit_bytecode (opcode);
+ instruction = (int) opcode;
+
+ /* Loop literals and emit as bytecode constants */
+ for (nliteral = 0; nliteral < arityvec[instruction].nliterals; nliteral++)
+ {
+ switch (arityvec[instruction].literals[nliteral])
+ {
+/* This conditional is a kludge, but it's necessary
+ because TYPE might be long long. */
+#ifdef __GNUC__
+ /* Expand definitions into case statements */
+#define DEFTYPECODE(CODE, NAME, MODE, TYPE) \
+ case CODE: \
+ { \
+ TYPE temp = va_arg (arguments, TYPE); \
+ bc_emit_bytecode_const ((void *) &temp, sizeof temp); \
+ PRLIT (TYPE, &temp); } \
+ break;
+
+#include "bc-typecd.def"
+
+#undef DEFTYPECODE
+#endif /* __GNUC__ */
+
+ default:
+ abort ();
+ }
+ }
+
+#ifdef BCDEBUG_PRINT_CODE
+ fputc ('\n', stderr);
+#endif
+}
+
+/* Emit the machine-code interface trampoline at the beginning of a byte
+ coded function. The argument is a label name of the interpreter
+ bytecode callinfo structure; the return value is a label name for
+ the beginning of the actual bytecode. */
+char *
+bc_emit_trampoline (callinfo)
+ char *callinfo;
+{
+ char mylab[20];
+ static int n;
+
+ sprintf (mylab, "*LB%d", n++);
+
+ BC_EMIT_TRAMPOLINE (trampoline, callinfo);
+
+ seg_defsym (bytecode, mylab);
+ return sym_lookup (mylab)->name;
+}
+
+
+/* Simple strdup */
+char *
+bc_xstrdup (str)
+ char *str;
+{
+ char *tmp = xmalloc (strlen (str) + 1);
+
+ strcpy (tmp, str);
+ return tmp;
+}
diff --git a/gnu/usr.bin/cc/cc_int/bc-optab.c b/gnu/usr.bin/cc/cc_int/bc-optab.c
new file mode 100644
index 0000000..b8ac57d
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/bc-optab.c
@@ -0,0 +1,788 @@
+/* Bytecode conversion definitions for GNU C-compiler.
+ Copyright (C) 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#include "tree.h"
+#include "rtl.h"
+#include "machmode.h"
+#include "obstack.h"
+#include "bytecode.h"
+#include "bc-typecd.h"
+#include "bc-opcode.h"
+#include "bc-optab.h"
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+extern char *xmalloc ();
+extern void free ();
+
+/* Table relating interpreter typecodes to machine modes. */
+#define GET_TYPECODE_MODE(CODE) (typecode_mode[((int) CODE)])
+enum machine_mode typecode_mode[] = {
+#define DEFTYPECODE(CODE, NAME, MODE, TYPE) MODE,
+#include "bc-typecd.def"
+#undef DEFTYPECODE
+};
+
+/* Machine mode to type code map */
+static enum typecode signed_mode_to_code_map[MAX_MACHINE_MODE+1];
+static enum typecode unsigned_mode_to_code_map[MAX_MACHINE_MODE+1];
+
+#define GET_TYPECODE_SIZE(CODE) GET_MODE_SIZE (GET_TYPECODE_MODE (CODE))
+
+#define BIG_ARBITRARY_NUMBER 100000
+
+/* Table of recipes for conversions among scalar types, to be filled
+ in as needed at run time. */
+static struct conversion_recipe
+{
+ unsigned char *opcodes; /* Bytecodes to emit in order. */
+ int nopcodes; /* Count of bytecodes. */
+ int cost; /* A rather arbitrary cost function. */
+} conversion_recipe[NUM_TYPECODES][NUM_TYPECODES];
+
+/* Binary operator tables. */
+struct binary_operator optab_plus_expr[] = {
+ { addSI, SIcode, SIcode, SIcode },
+ { addDI, DIcode, DIcode, DIcode },
+ { addSF, SFcode, SFcode, SFcode },
+ { addDF, DFcode, DFcode, DFcode },
+ { addXF, XFcode, XFcode, XFcode },
+ { addPSI, Pcode, Pcode, SIcode },
+ { -1, -1, -1, -1 },
+};
+
+struct binary_operator optab_minus_expr[] = {
+ { subSI, SIcode, SIcode, SIcode },
+ { subDI, DIcode, DIcode, DIcode },
+ { subSF, SFcode, SFcode, SFcode },
+ { subDF, DFcode, DFcode, DFcode },
+ { subXF, XFcode, XFcode, XFcode },
+ { subPP, SIcode, Pcode, Pcode },
+ { -1, -1, -1, -1 },
+};
+
+/* The ordering of the tables for multiplicative operators
+ is such that unsigned operations will be preferred to signed
+ operations when one argument is unsigned. */
+
+struct binary_operator optab_mult_expr[] = {
+ { mulSU, SUcode, SUcode, SUcode },
+ { mulDU, DUcode, DUcode, DUcode },
+ { mulSI, SIcode, SIcode, SIcode },
+ { mulDI, DIcode, DIcode, DIcode },
+ { mulSF, SFcode, SFcode, SFcode },
+ { mulDF, DFcode, DFcode, DFcode },
+ { mulXF, XFcode, XFcode, XFcode },
+ { -1, -1, -1, -1 },
+};
+
+struct binary_operator optab_trunc_div_expr[] = {
+ { divSU, SUcode, SUcode, SUcode },
+ { divDU, DUcode, DUcode, DUcode },
+ { divSI, SIcode, SIcode, SIcode },
+ { divDI, DIcode, DIcode, DIcode },
+ { -1, -1, -1, -1 },
+};
+
+struct binary_operator optab_trunc_mod_expr[] = {
+ { modSU, SUcode, SUcode, SUcode },
+ { modDU, DUcode, DUcode, DUcode },
+ { modSI, SIcode, SIcode, SIcode },
+ { modDI, DIcode, DIcode, DIcode },
+ { -1, -1, -1, -1 },
+};
+
+struct binary_operator optab_rdiv_expr[] = {
+ { divSF, SFcode, SFcode, SFcode },
+ { divDF, DFcode, DFcode, DFcode },
+ { divXF, XFcode, XFcode, XFcode },
+ { -1, -1, -1, -1 },
+};
+
+struct binary_operator optab_bit_and_expr[] = {
+ { andSI, SIcode, SIcode, SIcode },
+ { andDI, DIcode, DIcode, DIcode },
+ { -1, -1, -1, -1 },
+};
+
+struct binary_operator optab_bit_ior_expr[] = {
+ { iorSI, SIcode, SIcode, SIcode },
+ { iorDI, DIcode, DIcode, DIcode },
+ { -1, -1, -1, -1 },
+};
+
+struct binary_operator optab_bit_xor_expr[] = {
+ { xorSI, SIcode, SIcode, SIcode },
+ { xorDI, DIcode, DIcode, DIcode },
+ { -1, -1, -1, -1 },
+};
+
+struct binary_operator optab_lshift_expr[] = {
+ { lshiftSI, SIcode, SIcode, SIcode },
+ { lshiftSU, SUcode, SUcode, SIcode },
+ { lshiftDI, DIcode, DIcode, SIcode },
+ { lshiftDU, DUcode, DUcode, SIcode },
+ { -1, -1, -1, -1 },
+};
+
+struct binary_operator optab_rshift_expr[] = {
+ { rshiftSI, SIcode, SIcode, SIcode },
+ { rshiftSU, SUcode, SUcode, SIcode },
+ { rshiftDI, DIcode, DIcode, SIcode },
+ { rshiftDU, DUcode, DUcode, SIcode },
+ { -1, -1, -1, -1 },
+};
+
+struct binary_operator optab_truth_and_expr[] = {
+ { andSI, SIcode, Tcode, Tcode },
+ { -1, -1, -1, -1 },
+};
+
+struct binary_operator optab_truth_or_expr[] = {
+ { iorSI, SIcode, Tcode, Tcode },
+ { -1, -1, -1, -1 },
+};
+
+struct binary_operator optab_lt_expr[] = {
+ { ltSI, Tcode, SIcode, SIcode },
+ { ltSU, Tcode, SUcode, SUcode },
+ { ltDI, Tcode, DIcode, DIcode },
+ { ltDU, Tcode, DUcode, DUcode },
+ { ltSF, Tcode, SFcode, SFcode },
+ { ltDF, Tcode, DFcode, DFcode },
+ { ltXF, Tcode, XFcode, XFcode },
+ { ltP, Tcode, Pcode, Pcode },
+ { -1, -1, -1, -1 },
+};
+
+struct binary_operator optab_le_expr[] = {
+ { leSI, Tcode, SIcode, SIcode },
+ { leSU, Tcode, SUcode, SUcode },
+ { leDI, Tcode, DIcode, DIcode },
+ { leDU, Tcode, DUcode, DUcode },
+ { leSF, Tcode, SFcode, SFcode },
+ { leDF, Tcode, DFcode, DFcode },
+ { leXF, Tcode, XFcode, XFcode },
+ { leP, Tcode, Pcode, Pcode },
+ { -1, -1, -1, -1 },
+};
+
+struct binary_operator optab_ge_expr[] = {
+ { geSI, Tcode, SIcode, SIcode },
+ { geSU, Tcode, SUcode, SUcode },
+ { geDI, Tcode, DIcode, DIcode },
+ { geDU, Tcode, DUcode, DUcode },
+ { geSF, Tcode, SFcode, SFcode },
+ { geDF, Tcode, DFcode, DFcode },
+ { geXF, Tcode, XFcode, XFcode },
+ { geP, Tcode, Pcode, Pcode },
+ { -1, -1, -1, -1 },
+};
+
+struct binary_operator optab_gt_expr[] = {
+ { gtSI, Tcode, SIcode, SIcode },
+ { gtSU, Tcode, SUcode, SUcode },
+ { gtDI, Tcode, DIcode, DIcode },
+ { gtDU, Tcode, DUcode, DUcode },
+ { gtSF, Tcode, SFcode, SFcode },
+ { gtDF, Tcode, DFcode, DFcode },
+ { gtXF, Tcode, XFcode, XFcode },
+ { gtP, Tcode, Pcode, Pcode },
+ { -1, -1, -1, -1 },
+};
+
+struct binary_operator optab_eq_expr[] = {
+ { eqSI, Tcode, SIcode, SIcode },
+ { eqDI, Tcode, DIcode, DIcode },
+ { eqSF, Tcode, SFcode, SFcode },
+ { eqDF, Tcode, DFcode, DFcode },
+ { eqXF, Tcode, XFcode, XFcode },
+ { eqP, Tcode, Pcode, Pcode },
+ { -1, -1, -1, -1 },
+};
+
+struct binary_operator optab_ne_expr[] = {
+ { neSI, Tcode, SIcode, SIcode },
+ { neDI, Tcode, DIcode, DIcode },
+ { neSF, Tcode, SFcode, SFcode },
+ { neDF, Tcode, DFcode, DFcode },
+ { neXF, Tcode, XFcode, XFcode },
+ { neP, Tcode, Pcode, Pcode },
+ { -1, -1, -1, -1 },
+};
+
+/* Unary operator tables. */
+struct unary_operator optab_negate_expr[] = {
+ { negSI, SIcode, SIcode },
+ { negDI, DIcode, DIcode },
+ { negSF, SFcode, SFcode },
+ { negDF, DFcode, DFcode },
+ { negXF, XFcode, XFcode },
+ { -1, -1, -1 },
+};
+
+struct unary_operator optab_bit_not_expr[] = {
+ { notSI, SIcode, SIcode },
+ { notDI, DIcode, DIcode },
+ { -1, -1, -1 },
+};
+
+struct unary_operator optab_truth_not_expr[] = {
+ { notT, SIcode, SIcode },
+ { -1, -1, -1 },
+};
+
+/* Increment operator tables. */
+struct increment_operator optab_predecrement_expr[] = {
+ { predecQI, QIcode },
+ { predecQI, QUcode },
+ { predecHI, HIcode },
+ { predecHI, HUcode },
+ { predecSI, SIcode },
+ { predecSI, SUcode },
+ { predecDI, DIcode },
+ { predecDI, DUcode },
+ { predecP, Pcode },
+ { predecSF, SFcode },
+ { predecDF, DFcode },
+ { predecXF, XFcode },
+ { -1, -1 },
+};
+
+struct increment_operator optab_preincrement_expr[] = {
+ { preincQI, QIcode },
+ { preincQI, QUcode },
+ { preincHI, HIcode },
+ { preincHI, HUcode },
+ { preincSI, SIcode },
+ { preincSI, SUcode },
+ { preincDI, DIcode },
+ { preincDI, DUcode },
+ { preincP, Pcode },
+ { preincSF, SFcode },
+ { preincDF, DFcode },
+ { preincXF, XFcode },
+ { -1, -1 },
+};
+
+struct increment_operator optab_postdecrement_expr[] = {
+ { postdecQI, QIcode },
+ { postdecQI, QUcode },
+ { postdecHI, HIcode },
+ { postdecHI, HUcode },
+ { postdecSI, SIcode },
+ { postdecSI, SUcode },
+ { postdecDI, DIcode },
+ { postdecDI, DUcode },
+ { postdecP, Pcode },
+ { postdecSF, SFcode },
+ { postdecDF, DFcode },
+ { postdecXF, XFcode },
+ { -1, -1 },
+};
+
+struct increment_operator optab_postincrement_expr[] = {
+ { postincQI, QIcode },
+ { postincQI, QUcode },
+ { postincHI, HIcode },
+ { postincHI, HUcode },
+ { postincSI, SIcode },
+ { postincSI, SUcode },
+ { postincDI, DIcode },
+ { postincDI, DUcode },
+ { postincP, Pcode },
+ { postincSF, SFcode },
+ { postincDF, DFcode },
+ { postincXF, XFcode },
+ { -1, -1 },
+};
+
+/* Table of conversions supported by the interpreter. */
+static struct conversion_info
+{
+ enum bytecode_opcode opcode; /* here indicates the conversion needs no opcode. */
+ enum typecode from;
+ enum typecode to;
+ int cost; /* 1 for no-op conversions, 2 for widening conversions,
+ 4 for int/float conversions, 8 for narrowing conversions. */
+} conversion_info[] = {
+ { -1, QIcode, QUcode, 1 },
+ { -1, HIcode, HUcode, 1 },
+ { -1, SIcode, SUcode, 1 },
+ { -1, DIcode, DUcode, 1 },
+ { -1, QUcode, QIcode, 1 },
+ { -1, HUcode, HIcode, 1 },
+ { -1, SUcode, SIcode, 1 },
+ { -1, DUcode, DIcode, 1 },
+ { -1, Tcode, SIcode, 1 },
+ { convertQIHI, QIcode, HIcode, 2 },
+ { convertQUHU, QUcode, HUcode, 2 },
+ { convertQUSU, QUcode, SUcode, 2 },
+ { convertHISI, HIcode, SIcode, 2 },
+ { convertHUSU, HUcode, SUcode, 2 },
+ { convertSIDI, SIcode, DIcode, 2 },
+ { convertSUDU, SUcode, DUcode, 2 },
+ { convertSFDF, SFcode, DFcode, 2 },
+ { convertDFXF, DFcode, XFcode, 2 },
+ { convertHIQI, HIcode, QIcode, 8 },
+ { convertSIQI, SIcode, QIcode, 8 },
+ { convertSIHI, SIcode, HIcode, 8 },
+ { convertSUQU, SUcode, QUcode, 8 },
+ { convertDISI, DIcode, SIcode, 8 },
+ { convertDFSF, DFcode, SFcode, 8 },
+ { convertXFDF, XFcode, DFcode, 8 },
+ { convertPSI, Pcode, SIcode, 2 },
+ { convertSIP, SIcode, Pcode, 2 },
+ { convertSIT, SIcode, Tcode, 2 },
+ { convertDIT, DIcode, Tcode, 2 },
+ { convertSFT, SFcode, Tcode, 2 },
+ { convertDFT, DFcode, Tcode, 2 },
+ { convertXFT, XFcode, Tcode, 2 },
+ { convertQISI, QIcode, SIcode, 2 },
+ { convertPT, Pcode, Tcode, 2 },
+ { convertSISF, SIcode, SFcode, 4 },
+ { convertSIDF, SIcode, DFcode, 4 },
+ { convertSIXF, SIcode, XFcode, 4 },
+ { convertSUSF, SUcode, SFcode, 4 },
+ { convertSUDF, SUcode, DFcode, 4 },
+ { convertSUXF, SUcode, XFcode, 4 },
+ { convertDISF, DIcode, SFcode, 4 },
+ { convertDIDF, DIcode, DFcode, 4 },
+ { convertDIXF, DIcode, XFcode, 4 },
+ { convertDUSF, DUcode, SFcode, 4 },
+ { convertDUDF, DUcode, DFcode, 4 },
+ { convertDUXF, DUcode, XFcode, 4 },
+ { convertSFSI, SFcode, SIcode, 4 },
+ { convertDFSI, DFcode, SIcode, 4 },
+ { convertXFSI, XFcode, SIcode, 4 },
+ { convertSFSU, SFcode, SUcode, 4 },
+ { convertDFSU, DFcode, SUcode, 4 },
+ { convertXFSU, XFcode, SUcode, 4 },
+ { convertSFDI, SFcode, DIcode, 4 },
+ { convertDFDI, DFcode, DIcode, 4 },
+ { convertXFDI, XFcode, DIcode, 4 },
+ { convertSFDU, SFcode, DUcode, 4 },
+ { convertDFDU, DFcode, DUcode, 4 },
+ { convertXFDU, XFcode, DUcode, 4 },
+ { convertSIQI, SIcode, QIcode, 8 },
+};
+
+#define NUM_CONVERSIONS (sizeof conversion_info / sizeof (struct conversion_info))
+
+/* List form of a conversion recipe. */
+struct conversion_list
+{
+ enum bytecode_opcode opcode;
+ enum typecode to;
+ int cost;
+ struct conversion_list *prev;
+};
+
+/* Determine if it is "reasonable" to add a given conversion to
+ a given list of conversions. The following criteria define
+ "reasonable" conversion lists:
+ * No typecode appears more than once in the sequence (no loops).
+ * At most one conversion from integer to float or vice versa is present.
+ * Either sign extensions or zero extensions may be present, but not both.
+ * No widening conversions occur after a signed/unsigned conversion.
+ * The sequence of sizes must be strict nonincreasing or nondecreasing. */
+static int
+conversion_reasonable_p (conversion, list)
+ struct conversion_info *conversion;
+ struct conversion_list *list;
+{
+ struct conversion_list *curr;
+ int curr_size, prev_size;
+ int has_int_float, has_float_int;
+ int has_sign_extend, has_zero_extend;
+ int has_signed_unsigned, has_unsigned_signed;
+
+ has_int_float = 0;
+ has_float_int = 0;
+ has_sign_extend = 0;
+ has_zero_extend = 0;
+ has_signed_unsigned = 0;
+ has_unsigned_signed = 0;
+
+ /* Make sure the destination typecode doesn't already appear in
+ the list. */
+ for (curr = list; curr; curr = curr->prev)
+ if (conversion->to == curr->to)
+ return 0;
+
+ /* Check for certain kinds of conversions. */
+ if (TYPECODE_INTEGER_P (conversion->from)
+ && TYPECODE_FLOAT_P (conversion->to))
+ has_int_float = 1;
+ if (TYPECODE_FLOAT_P (conversion->from)
+ && TYPECODE_INTEGER_P (conversion->to))
+ has_float_int = 1;
+ if (TYPECODE_SIGNED_P (conversion->from)
+ && TYPECODE_SIGNED_P (conversion->to)
+ && GET_TYPECODE_SIZE (conversion->from)
+ < GET_TYPECODE_SIZE (conversion->to))
+ has_sign_extend = 1;
+ if (TYPECODE_UNSIGNED_P (conversion->from)
+ && TYPECODE_UNSIGNED_P (conversion->to)
+ && GET_TYPECODE_SIZE (conversion->from)
+ < GET_TYPECODE_SIZE (conversion->to))
+ has_zero_extend = 1;
+
+ for (curr = list; curr && curr->prev; curr = curr->prev)
+ {
+ if (TYPECODE_INTEGER_P (curr->prev->to)
+ && TYPECODE_FLOAT_P (curr->to))
+ has_int_float = 1;
+ if (TYPECODE_FLOAT_P (curr->prev->to)
+ && TYPECODE_INTEGER_P (curr->to))
+ has_float_int = 1;
+ if (TYPECODE_SIGNED_P (curr->prev->to)
+ && TYPECODE_SIGNED_P (curr->to)
+ && GET_TYPECODE_SIZE (curr->prev->to)
+ < GET_TYPECODE_SIZE (curr->to))
+ has_sign_extend = 1;
+ if (TYPECODE_UNSIGNED_P (curr->prev->to)
+ && TYPECODE_UNSIGNED_P (curr->to)
+ && GET_TYPECODE_SIZE (curr->prev->to)
+ < GET_TYPECODE_SIZE (curr->to))
+ has_zero_extend = 1;
+ if (TYPECODE_SIGNED_P (curr->prev->to)
+ && TYPECODE_UNSIGNED_P (curr->to))
+ has_signed_unsigned = 1;
+ if (TYPECODE_UNSIGNED_P (curr->prev->to)
+ && TYPECODE_SIGNED_P (curr->to))
+ has_unsigned_signed = 1;
+ }
+
+ if (TYPECODE_INTEGER_P (conversion->from)
+ && TYPECODE_INTEGER_P (conversion->to)
+ && GET_TYPECODE_SIZE (conversion->to)
+ > GET_TYPECODE_SIZE (conversion->from)
+ && (has_signed_unsigned || has_unsigned_signed))
+ return 0;
+
+ if (has_float_int && has_int_float || has_sign_extend && has_zero_extend)
+ return 0;
+
+ /* Make sure the sequence of destination typecode sizes is
+ strictly nondecreasing or strictly nonincreasing. */
+ prev_size = GET_TYPECODE_SIZE (conversion->to);
+ for (curr = list; curr; curr = curr->prev)
+ {
+ curr_size = GET_TYPECODE_SIZE (curr->to);
+ if (curr_size != prev_size)
+ break;
+ }
+ if (!curr)
+ return 1;
+
+ if (curr_size < prev_size)
+ for (prev_size = curr_size; curr; curr = curr->prev)
+ {
+ curr_size = GET_TYPECODE_SIZE (curr->to);
+ if (curr_size > prev_size)
+ return 0;
+ prev_size = curr_size;
+ }
+ else
+ for (prev_size = curr_size; curr; curr = curr->prev)
+ {
+ curr_size = GET_TYPECODE_SIZE (curr->to);
+ if (curr_size < prev_size)
+ return 0;
+ prev_size = curr_size;
+ }
+ return 1;
+}
+
+
+/* Exhaustively search all reasonable conversions to find one to
+ convert the given types. */
+static struct conversion_recipe
+deduce_conversion (from, to)
+ enum typecode from, to;
+{
+ struct rl
+ {
+ struct conversion_list *list;
+ struct rl *next;
+ } *prev, curr, *good, *temp;
+ struct conversion_list *conv, *best;
+ int i, cost, bestcost;
+ struct conversion_recipe result;
+ struct obstack recipe_obstack;
+
+
+ obstack_init (&recipe_obstack);
+ curr.next = (struct rl *) obstack_alloc (&recipe_obstack, sizeof (struct rl));
+ curr.next->list =
+ (struct conversion_list *) obstack_alloc (&recipe_obstack,
+ sizeof (struct conversion_list));
+ curr.next->list->opcode = -1;
+ curr.next->list->to = from;
+ curr.next->list->cost = 0;
+ curr.next->list->prev = 0;
+ curr.next->next = 0;
+ good = 0;
+
+ while (curr.next)
+ {
+ /* Remove successful conversions from further consideration. */
+ for (prev = &curr; prev; prev = prev->next)
+ if (prev->next && prev->next->list->to == to)
+ {
+ temp = prev->next->next;
+ prev->next->next = good;
+ good = prev->next;
+ prev->next = temp;
+ }
+
+ /* Go through each of the pending conversion chains, trying
+ all possible candidate conversions on them. */
+ for (prev = curr.next, curr.next = 0; prev; prev = prev->next)
+ for (i = 0; i < NUM_CONVERSIONS; ++i)
+ if (conversion_info[i].from == prev->list->to
+ && conversion_reasonable_p (&conversion_info[i], prev->list))
+ {
+ temp = (struct rl *) obstack_alloc (&recipe_obstack,
+ sizeof (struct rl));
+ temp->list = (struct conversion_list *)
+ obstack_alloc (&recipe_obstack,
+ sizeof (struct conversion_list));
+ temp->list->opcode = conversion_info[i].opcode;
+ temp->list->to = conversion_info[i].to;
+ temp->list->cost = conversion_info[i].cost;
+ temp->list->prev = prev->list;
+ temp->next = curr.next;
+ curr.next = temp;
+ }
+ }
+
+ bestcost = BIG_ARBITRARY_NUMBER;
+ best = 0;
+ for (temp = good; temp; temp = temp->next)
+ {
+ for (conv = temp->list, cost = 0; conv; conv = conv->prev)
+ cost += conv->cost;
+ if (cost < bestcost)
+ {
+ bestcost = cost;
+ best = temp->list;
+ }
+ }
+
+ if (!best)
+ abort ();
+
+ for (i = 0, conv = best; conv; conv = conv->prev)
+ if (conv->opcode != -1)
+ ++i;
+
+ result.opcodes = (unsigned char *) xmalloc (i);
+ result.nopcodes = i;
+ for (conv = best; conv; conv = conv->prev)
+ if (conv->opcode != -1)
+ result.opcodes[--i] = conv->opcode;
+ result.cost = bestcost;
+ obstack_free (&recipe_obstack, 0);
+ return result;
+}
+
+#define DEDUCE_CONVERSION(FROM, TO) \
+ (conversion_recipe[(int) FROM][(int) TO].opcodes ? 0 \
+ : (conversion_recipe[(int) FROM][(int) TO] \
+ = deduce_conversion (FROM, TO), 0))
+
+
+/* Emit a conversion between the given scalar types. */
+void
+emit_typecode_conversion (from, to)
+ enum typecode from, to;
+{
+ int i;
+
+ DEDUCE_CONVERSION (from, to);
+ for (i = 0; i < conversion_recipe[(int) from][(int) to].nopcodes; ++i)
+ bc_emit_instruction (conversion_recipe[(int) from][(int) to].opcodes[i]);
+}
+
+
+/* Initialize mode_to_code_map[] */
+void
+bc_init_mode_to_code_map ()
+{
+ int mode;
+
+ for (mode = 0; mode < MAX_MACHINE_MODE + 1; mode++)
+ {
+ signed_mode_to_code_map[mode] =
+ unsigned_mode_to_code_map[mode] =
+ LAST_AND_UNUSED_TYPECODE;
+ }
+
+#define DEF_MODEMAP(SYM, CODE, UCODE, CONST, LOAD, STORE) \
+ { signed_mode_to_code_map[(int) SYM] = CODE; \
+ unsigned_mode_to_code_map[(int) SYM] = UCODE; }
+#include "modemap.def"
+#undef DEF_MODEMAP
+
+ /* Initialize opcode maps for const, load, and store */
+ bc_init_mode_to_opcode_maps ();
+}
+
+/* Given a machine mode return the preferred typecode. */
+enum typecode
+preferred_typecode (mode, unsignedp)
+ enum machine_mode mode;
+ int unsignedp;
+{
+ enum typecode code = (unsignedp
+ ? unsigned_mode_to_code_map
+ : signed_mode_to_code_map) [MIN ((int) mode,
+ (int) MAX_MACHINE_MODE)];
+
+ if (code == LAST_AND_UNUSED_TYPECODE)
+ abort ();
+
+ return code;
+}
+
+
+/* Expand a conversion between the given types. */
+void
+bc_expand_conversion (from, to)
+ tree from, to;
+{
+ enum typecode fcode, tcode;
+
+ fcode = preferred_typecode (TYPE_MODE (from), TREE_UNSIGNED (from));
+ tcode = preferred_typecode (TYPE_MODE (to), TREE_UNSIGNED (to));
+
+ emit_typecode_conversion (fcode, tcode);
+}
+
+/* Expand a conversion of the given type to a truth value. */
+void
+bc_expand_truth_conversion (from)
+ tree from;
+{
+ enum typecode fcode;
+
+ fcode = preferred_typecode (TYPE_MODE (from), TREE_UNSIGNED (from));
+ emit_typecode_conversion (fcode, Tcode);
+}
+
+/* Emit an appropriate binary operation. */
+void
+bc_expand_binary_operation (optab, resulttype, arg0, arg1)
+ struct binary_operator optab[];
+ tree resulttype, arg0, arg1;
+{
+ int i, besti, cost, bestcost;
+ enum typecode resultcode, arg0code, arg1code;
+
+ resultcode = preferred_typecode (TYPE_MODE (resulttype), TREE_UNSIGNED (resulttype));
+ arg0code = preferred_typecode (TYPE_MODE (TREE_TYPE (arg0)), TREE_UNSIGNED (resulttype));
+ arg1code = preferred_typecode (TYPE_MODE (TREE_TYPE (arg1)), TREE_UNSIGNED (resulttype));
+
+ besti = -1;
+ bestcost = BIG_ARBITRARY_NUMBER;
+
+ for (i = 0; optab[i].opcode != -1; ++i)
+ {
+ cost = 0;
+ DEDUCE_CONVERSION (arg0code, optab[i].arg0);
+ cost += conversion_recipe[(int) arg0code][(int) optab[i].arg0].cost;
+ DEDUCE_CONVERSION (arg1code, optab[i].arg1);
+ cost += conversion_recipe[(int) arg1code][(int) optab[i].arg1].cost;
+ if (cost < bestcost)
+ {
+ besti = i;
+ bestcost = cost;
+ }
+ }
+
+ if (besti == -1)
+ abort ();
+
+ expand_expr (arg1, 0, VOIDmode, 0);
+ emit_typecode_conversion (arg1code, optab[besti].arg1);
+ expand_expr (arg0, 0, VOIDmode, 0);
+ emit_typecode_conversion (arg0code, optab[besti].arg0);
+ bc_emit_instruction (optab[besti].opcode);
+ emit_typecode_conversion (optab[besti].result, resultcode);
+}
+
+/* Emit an appropriate unary operation. */
+void
+bc_expand_unary_operation (optab, resulttype, arg0)
+ struct unary_operator optab[];
+ tree resulttype, arg0;
+{
+ int i, besti, cost, bestcost;
+ enum typecode resultcode, arg0code;
+
+ resultcode = preferred_typecode (TYPE_MODE (resulttype), TREE_UNSIGNED (resulttype));
+ arg0code = preferred_typecode (TYPE_MODE (TREE_TYPE (arg0)), TREE_UNSIGNED (TREE_TYPE (arg0)));
+
+ besti = -1;
+ bestcost = BIG_ARBITRARY_NUMBER;
+
+ for (i = 0; optab[i].opcode != -1; ++i)
+ {
+ DEDUCE_CONVERSION (arg0code, optab[i].arg0);
+ cost = conversion_recipe[(int) arg0code][(int) optab[i].arg0].cost;
+ if (cost < bestcost)
+ {
+ besti = i;
+ bestcost = cost;
+ }
+ }
+
+ if (besti == -1)
+ abort ();
+
+ expand_expr (arg0, 0, VOIDmode, 0);
+ emit_typecode_conversion (arg0code, optab[besti].arg0);
+ bc_emit_instruction (optab[besti].opcode);
+ emit_typecode_conversion (optab[besti].result, resultcode);
+}
+
+
+/* Emit an appropriate increment. */
+void
+bc_expand_increment (optab, type)
+ struct increment_operator optab[];
+ tree type;
+{
+ enum typecode code;
+ int i;
+
+ code = preferred_typecode (TYPE_MODE (type), TREE_UNSIGNED (type));
+ for (i = 0; (int) optab[i].opcode >= 0; ++i)
+ if (code == optab[i].arg)
+ {
+ bc_emit_instruction (optab[i].opcode);
+ return;
+ }
+ abort ();
+}
diff --git a/gnu/usr.bin/cc/cc_int/c-common.c b/gnu/usr.bin/cc/cc_int/c-common.c
new file mode 100644
index 0000000..f18c270
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/c-common.c
@@ -0,0 +1,1997 @@
+/* Subroutines shared by all languages that are variants of C.
+ Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "config.h"
+#include "tree.h"
+#include "c-lex.h"
+#include "c-tree.h"
+#include "flags.h"
+#include "obstack.h"
+#include <stdio.h>
+#include <ctype.h>
+
+extern struct obstack permanent_obstack;
+
+static void declare_hidden_char_array PROTO((char *, char *));
+
+/* Make bindings for __FUNCTION__ and __PRETTY_FUNCTION__. */
+
+void
+declare_function_name ()
+{
+ char *name, *printable_name;
+
+ if (current_function_decl == NULL)
+ {
+ name = "";
+ printable_name = "top level";
+ }
+ else
+ {
+ char *kind = "function";
+ if (TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE)
+ kind = "method";
+ /* Allow functions to be nameless (such as artificial ones). */
+ if (DECL_NAME (current_function_decl))
+ name = IDENTIFIER_POINTER (DECL_NAME (current_function_decl));
+ else
+ name = "";
+ printable_name = (*decl_printable_name) (current_function_decl, &kind);
+ }
+
+ declare_hidden_char_array ("__FUNCTION__", name);
+ declare_hidden_char_array ("__PRETTY_FUNCTION__", printable_name);
+}
+
+static void
+declare_hidden_char_array (name, value)
+ char *name, *value;
+{
+ tree decl, type, init;
+ int vlen;
+
+ /* If the default size of char arrays isn't big enough for the name,
+ or if we want to give warnings for large objects, make a bigger one. */
+ vlen = strlen (value) + 1;
+ type = char_array_type_node;
+ if (TREE_INT_CST_LOW (TYPE_MAX_VALUE (TREE_TYPE (type))) < vlen
+ || warn_larger_than)
+ type = build_array_type (char_type_node,
+ build_index_type (build_int_2 (vlen, 0)));
+ push_obstacks_nochange ();
+ decl = build_decl (VAR_DECL, get_identifier (name), type);
+ TREE_STATIC (decl) = 1;
+ TREE_READONLY (decl) = 1;
+ TREE_ASM_WRITTEN (decl) = 1;
+ DECL_SOURCE_LINE (decl) = 0;
+ DECL_IN_SYSTEM_HEADER (decl) = 1;
+ DECL_IGNORED_P (decl) = 1;
+ init = build_string (vlen, value);
+ TREE_TYPE (init) = type;
+ DECL_INITIAL (decl) = init;
+ finish_decl (pushdecl (decl), init, NULL_TREE);
+}
+
+/* Given a chain of STRING_CST nodes,
+ concatenate them into one STRING_CST
+ and give it a suitable array-of-chars data type. */
+
+tree
+combine_strings (strings)
+ tree strings;
+{
+ register tree value, t;
+ register int length = 1;
+ int wide_length = 0;
+ int wide_flag = 0;
+ int wchar_bytes = TYPE_PRECISION (wchar_type_node) / BITS_PER_UNIT;
+ int nchars;
+
+ if (TREE_CHAIN (strings))
+ {
+ /* More than one in the chain, so concatenate. */
+ register char *p, *q;
+
+ /* Don't include the \0 at the end of each substring,
+ except for the last one.
+ Count wide strings and ordinary strings separately. */
+ for (t = strings; t; t = TREE_CHAIN (t))
+ {
+ if (TREE_TYPE (t) == wchar_array_type_node)
+ {
+ wide_length += (TREE_STRING_LENGTH (t) - wchar_bytes);
+ wide_flag = 1;
+ }
+ else
+ length += (TREE_STRING_LENGTH (t) - 1);
+ }
+
+ /* If anything is wide, the non-wides will be converted,
+ which makes them take more space. */
+ if (wide_flag)
+ length = length * wchar_bytes + wide_length;
+
+ p = savealloc (length);
+
+ /* Copy the individual strings into the new combined string.
+ If the combined string is wide, convert the chars to ints
+ for any individual strings that are not wide. */
+
+ q = p;
+ for (t = strings; t; t = TREE_CHAIN (t))
+ {
+ int len = (TREE_STRING_LENGTH (t)
+ - ((TREE_TYPE (t) == wchar_array_type_node)
+ ? wchar_bytes : 1));
+ if ((TREE_TYPE (t) == wchar_array_type_node) == wide_flag)
+ {
+ bcopy (TREE_STRING_POINTER (t), q, len);
+ q += len;
+ }
+ else
+ {
+ int i;
+ for (i = 0; i < len; i++)
+ ((int *) q)[i] = TREE_STRING_POINTER (t)[i];
+ q += len * wchar_bytes;
+ }
+ }
+ if (wide_flag)
+ {
+ int i;
+ for (i = 0; i < wchar_bytes; i++)
+ *q++ = 0;
+ }
+ else
+ *q = 0;
+
+ value = make_node (STRING_CST);
+ TREE_STRING_POINTER (value) = p;
+ TREE_STRING_LENGTH (value) = length;
+ TREE_CONSTANT (value) = 1;
+ }
+ else
+ {
+ value = strings;
+ length = TREE_STRING_LENGTH (value);
+ if (TREE_TYPE (value) == wchar_array_type_node)
+ wide_flag = 1;
+ }
+
+ /* Compute the number of elements, for the array type. */
+ nchars = wide_flag ? length / wchar_bytes : length;
+
+ /* Create the array type for the string constant.
+ -Wwrite-strings says make the string constant an array of const char
+ so that copying it to a non-const pointer will get a warning. */
+ if (warn_write_strings
+ && (! flag_traditional && ! flag_writable_strings))
+ {
+ tree elements
+ = build_type_variant (wide_flag ? wchar_type_node : char_type_node,
+ 1, 0);
+ TREE_TYPE (value)
+ = build_array_type (elements,
+ build_index_type (build_int_2 (nchars - 1, 0)));
+ }
+ else
+ TREE_TYPE (value)
+ = build_array_type (wide_flag ? wchar_type_node : char_type_node,
+ build_index_type (build_int_2 (nchars - 1, 0)));
+ TREE_CONSTANT (value) = 1;
+ TREE_STATIC (value) = 1;
+ return value;
+}
+
+/* Process the attributes listed in ATTRIBUTES
+ and install them in DECL. */
+
+void
+decl_attributes (decl, attributes)
+ tree decl, attributes;
+{
+ tree a, name, args, type, new_attr;
+
+ type = TREE_TYPE (decl);
+
+ new_attr = TYPE_ATTRIBUTES (type);
+
+ for (a = attributes; a; a = TREE_CHAIN (a))
+ if (!(name = TREE_VALUE (a)))
+ continue;
+ else if (name == get_identifier ("packed"))
+ {
+ if (TREE_CODE (decl) == FIELD_DECL)
+ DECL_PACKED (decl) = 1;
+ /* We can't set DECL_PACKED for a VAR_DECL, because the bit is
+ used for DECL_REGISTER. It wouldn't mean anything anyway. */
+ else
+ warning_with_decl (decl, "`packed' attribute ignore");
+
+ }
+ else if (TREE_VALUE (a) == get_identifier ("noreturn")
+ || TREE_VALUE (a) == get_identifier ("volatile"))
+ {
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ TREE_THIS_VOLATILE (decl) = 1;
+ else if (TREE_CODE (type) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE)
+ TREE_TYPE (decl) = type
+ = build_pointer_type
+ (build_type_variant (TREE_TYPE (type),
+ TREE_READONLY (TREE_TYPE (type)), 1));
+ else
+ warning_with_decl (decl, "`%s' attribute ignored",
+ IDENTIFIER_POINTER (TREE_VALUE (a)));
+ }
+ else if (TREE_VALUE (a) == get_identifier ("const"))
+ {
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ TREE_READONLY (decl) = 1;
+ else if (TREE_CODE (type) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE)
+ TREE_TYPE (decl) = type
+ = build_pointer_type
+ (build_type_variant (TREE_TYPE (type), 1,
+ TREE_THIS_VOLATILE (TREE_TYPE (type))));
+ else
+ warning_with_decl (decl, "`const' attribute ignored");
+ }
+ else if (TREE_CODE (name) != TREE_LIST)
+ {
+#ifdef VALID_MACHINE_ATTRIBUTE
+ if (VALID_MACHINE_ATTRIBUTE (type, new_attr, name))
+ {
+ register tree atlist;
+
+ for (atlist = new_attr; atlist; atlist = TREE_CHAIN (atlist))
+ if (TREE_VALUE (atlist) == name)
+ goto found_attr;
+
+ new_attr = tree_cons (NULL_TREE, name, new_attr);
+found_attr:;
+ }
+ else
+#endif
+ warning ("`%s' attribute directive ignored",
+ IDENTIFIER_POINTER (name));
+ }
+ else if ( args = TREE_CHAIN(name),
+ !strcmp (IDENTIFIER_POINTER (name = TREE_PURPOSE (name)), "mode")
+ && list_length (args) == 1
+ && TREE_CODE (TREE_VALUE (args)) == IDENTIFIER_NODE)
+ {
+ int i;
+ char *specified_name
+ = IDENTIFIER_POINTER (TREE_VALUE (args));
+
+ /* Give this decl a type with the specified mode. */
+ for (i = 0; i < NUM_MACHINE_MODES; i++)
+ if (!strcmp (specified_name, GET_MODE_NAME (i)))
+ {
+ tree typefm
+ = type_for_mode (i, TREE_UNSIGNED (type));
+ if (typefm != 0)
+ {
+ TREE_TYPE (decl) = type = typefm;
+ DECL_SIZE (decl) = 0;
+ layout_decl (decl, 0);
+ }
+ else
+ error ("no data type for mode `%s'", specified_name);
+ break;
+ }
+ if (i == NUM_MACHINE_MODES)
+ error_with_decl (decl, "unknown machine mode `%s'", specified_name);
+ }
+ else if (!strcmp (IDENTIFIER_POINTER (name), "section")
+ && list_length (args) == 1
+ && TREE_CODE (TREE_VALUE (args)) == STRING_CST)
+ {
+#ifdef ASM_OUTPUT_SECTION_NAME
+ if (TREE_CODE (decl) == FUNCTION_DECL || TREE_CODE (decl) == VAR_DECL)
+ {
+ if (TREE_CODE (decl) == VAR_DECL && current_function_decl != NULL_TREE)
+ error_with_decl (decl,
+ "section attribute cannot be specified for local variables");
+ /* The decl may have already been given a section attribute from
+ a previous declaration. Ensure they match. */
+ else if (DECL_SECTION_NAME (decl) != NULL_TREE
+ && strcmp (TREE_STRING_POINTER (DECL_SECTION_NAME (decl)),
+ TREE_STRING_POINTER (TREE_VALUE (args))) != 0)
+ error_with_decl (decl,
+ "section of `%s' conflicts with previous declaration");
+ else
+ DECL_SECTION_NAME (decl) = TREE_VALUE (args);
+ }
+ else
+ error_with_decl (decl,
+ "section attribute not allowed for `%s'");
+#else
+ error_with_decl (decl, "section attributes are not supported for this target");
+#endif
+ }
+ else if (!strcmp (IDENTIFIER_POINTER (name), "aligned")
+ && list_length (args) == 1
+ && TREE_CODE (TREE_VALUE (args)) == INTEGER_CST)
+ {
+ tree align_expr = TREE_VALUE (args);
+ int align;
+
+ /* Strip any NOPs of any kind. */
+ while (TREE_CODE (align_expr) == NOP_EXPR
+ || TREE_CODE (align_expr) == CONVERT_EXPR
+ || TREE_CODE (align_expr) == NON_LVALUE_EXPR)
+ align_expr = TREE_OPERAND (align_expr, 0);
+
+ if (TREE_CODE (align_expr) != INTEGER_CST)
+ {
+ error_with_decl (decl,
+ "requested alignment of `%s' is not a constant");
+ continue;
+ }
+
+ align = TREE_INT_CST_LOW (align_expr) * BITS_PER_UNIT;
+
+ if (exact_log2 (align) == -1)
+ error_with_decl (decl,
+ "requested alignment of `%s' is not a power of 2");
+ else if (TREE_CODE (decl) != VAR_DECL
+ && TREE_CODE (decl) != FIELD_DECL)
+ error_with_decl (decl,
+ "alignment specified for `%s'");
+ else
+ DECL_ALIGN (decl) = align;
+ }
+ else if (!strcmp (IDENTIFIER_POINTER (name), "format")
+ && list_length (args) == 3
+ && TREE_CODE (TREE_VALUE (args)) == IDENTIFIER_NODE
+ && TREE_CODE (TREE_VALUE (TREE_CHAIN (args))) == INTEGER_CST
+ && TREE_CODE (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)))) == INTEGER_CST )
+ {
+ tree format_type = TREE_VALUE (args);
+ tree format_num_expr = TREE_VALUE (TREE_CHAIN (args));
+ tree first_arg_num_expr = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
+ int format_num;
+ int first_arg_num;
+ int is_scan;
+ tree argument;
+ int arg_num;
+
+ if (TREE_CODE (decl) != FUNCTION_DECL)
+ {
+ error_with_decl (decl,
+ "argument format specified for non-function `%s'");
+ continue;
+ }
+
+ if (!strcmp (IDENTIFIER_POINTER (format_type), "printf"))
+ is_scan = 0;
+ else if (!strcmp (IDENTIFIER_POINTER (format_type), "scanf"))
+ is_scan = 1;
+ else
+ {
+ error_with_decl (decl, "unrecognized format specifier for `%s'");
+ continue;
+ }
+
+ /* Strip any conversions from the string index and first arg number
+ and verify they are constants. */
+ while (TREE_CODE (format_num_expr) == NOP_EXPR
+ || TREE_CODE (format_num_expr) == CONVERT_EXPR
+ || TREE_CODE (format_num_expr) == NON_LVALUE_EXPR)
+ format_num_expr = TREE_OPERAND (format_num_expr, 0);
+
+ while (TREE_CODE (first_arg_num_expr) == NOP_EXPR
+ || TREE_CODE (first_arg_num_expr) == CONVERT_EXPR
+ || TREE_CODE (first_arg_num_expr) == NON_LVALUE_EXPR)
+ first_arg_num_expr = TREE_OPERAND (first_arg_num_expr, 0);
+
+ if (TREE_CODE (format_num_expr) != INTEGER_CST
+ || TREE_CODE (first_arg_num_expr) != INTEGER_CST)
+ {
+ error_with_decl (decl,
+ "format string for `%s' has non-constant operand number");
+ continue;
+ }
+
+ format_num = TREE_INT_CST_LOW (format_num_expr);
+ first_arg_num = TREE_INT_CST_LOW (first_arg_num_expr);
+ if (first_arg_num != 0 && first_arg_num <= format_num)
+ {
+ error_with_decl (decl,
+ "format string arg follows the args to be formatted, for `%s'");
+ continue;
+ }
+
+ /* If a parameter list is specified, verify that the format_num
+ argument is actually a string, in case the format attribute
+ is in error. */
+ argument = TYPE_ARG_TYPES (type);
+ if (argument)
+ {
+ for (arg_num = 1; ; ++arg_num)
+ {
+ if (argument == 0 || arg_num == format_num)
+ break;
+ argument = TREE_CHAIN (argument);
+ }
+ if (! argument
+ || TREE_CODE (TREE_VALUE (argument)) != POINTER_TYPE
+ || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_VALUE (argument)))
+ != char_type_node))
+ {
+ error_with_decl (decl,
+ "format string arg not a string type, for `%s'");
+ continue;
+ }
+ if (first_arg_num != 0)
+ {
+ /* Verify that first_arg_num points to the last arg, the ... */
+ while (argument)
+ arg_num++, argument = TREE_CHAIN (argument);
+ if (arg_num != first_arg_num)
+ {
+ error_with_decl (decl,
+ "args to be formatted is not ..., for `%s'");
+ continue;
+ }
+ }
+ }
+
+ record_function_format (DECL_NAME (decl), DECL_ASSEMBLER_NAME (decl),
+ is_scan, format_num, first_arg_num);
+ }
+ else
+ warning ("`%s' attribute directive ignored",
+ IDENTIFIER_POINTER (name));
+
+ TREE_TYPE (decl) = build_type_attribute_variant (type, new_attr);
+}
+
+/* Check a printf/fprintf/sprintf/scanf/fscanf/sscanf format against
+ a parameter list. */
+
+#define T_I &integer_type_node
+#define T_L &long_integer_type_node
+#define T_S &short_integer_type_node
+#define T_UI &unsigned_type_node
+#define T_UL &long_unsigned_type_node
+#define T_US &short_unsigned_type_node
+#define T_F &float_type_node
+#define T_D &double_type_node
+#define T_LD &long_double_type_node
+#define T_C &char_type_node
+#define T_V &void_type_node
+#define T_W &wchar_type_node
+#define T_ST &sizetype
+
+typedef struct {
+ char *format_chars;
+ int pointer_count;
+ /* Type of argument if no length modifier is used. */
+ tree *nolen;
+ /* Type of argument if length modifier for shortening is used.
+ If NULL, then this modifier is not allowed. */
+ tree *hlen;
+ /* Type of argument if length modifier `l' is used.
+ If NULL, then this modifier is not allowed. */
+ tree *llen;
+ /* Type of argument if length modifier `L' is used.
+ If NULL, then this modifier is not allowed. */
+ tree *bigllen;
+ /* List of other modifier characters allowed with these options. */
+ char *flag_chars;
+} format_char_info;
+
+static format_char_info print_char_table[] = {
+ { "di", 0, T_I, T_I, T_L, NULL, "-wp0 +" },
+ { "oxX", 0, T_UI, T_UI, T_UL, NULL, "-wp0#" },
+ { "u", 0, T_UI, T_UI, T_UL, NULL, "-wp0" },
+/* Two GNU extensions. */
+ { "Z", 0, T_ST, NULL, NULL, NULL, "-wp0" },
+ { "m", 0, T_UI, T_UI, T_UL, NULL, "-wp" },
+ { "feEgG", 0, T_D, NULL, NULL, T_LD, "-wp0 +#" },
+ { "c", 0, T_I, NULL, T_W, NULL, "-w" },
+ { "C", 0, T_W, NULL, NULL, NULL, "-w" },
+ { "s", 1, T_C, NULL, T_W, NULL, "-wp" },
+ { "S", 1, T_W, NULL, NULL, NULL, "-wp" },
+ { "p", 1, T_V, NULL, NULL, NULL, "-w" },
+ { "n", 1, T_I, T_S, T_L, NULL, "" },
+ { NULL }
+};
+
+static format_char_info scan_char_table[] = {
+ { "di", 1, T_I, T_S, T_L, NULL, "*" },
+ { "ouxX", 1, T_UI, T_US, T_UL, NULL, "*" },
+ { "efgEG", 1, T_F, NULL, T_D, T_LD, "*" },
+ { "sc", 1, T_C, NULL, T_W, NULL, "*a" },
+ { "[", 1, T_C, NULL, NULL, NULL, "*a" },
+ { "C", 1, T_W, NULL, NULL, NULL, "*" },
+ { "S", 1, T_W, NULL, NULL, NULL, "*" },
+ { "p", 2, T_V, NULL, NULL, NULL, "*" },
+ { "n", 1, T_I, T_S, T_L, NULL, "" },
+ { NULL }
+};
+
+typedef struct function_format_info {
+ struct function_format_info *next; /* next structure on the list */
+ tree name; /* identifier such as "printf" */
+ tree assembler_name; /* optional mangled identifier (for C++) */
+ int is_scan; /* TRUE if *scanf */
+ int format_num; /* number of format argument */
+ int first_arg_num; /* number of first arg (zero for varargs) */
+} function_format_info;
+
+static function_format_info *function_format_list = NULL;
+
+static void check_format_info PROTO((function_format_info *, tree));
+
+/* Initialize the table of functions to perform format checking on.
+ The ANSI functions are always checked (whether <stdio.h> is
+ included or not), since it is common to call printf without
+ including <stdio.h>. There shouldn't be a problem with this,
+ since ANSI reserves these function names whether you include the
+ header file or not. In any case, the checking is harmless. */
+
+void
+init_function_format_info ()
+{
+ record_function_format (get_identifier ("printf"), NULL_TREE, 0, 1, 2);
+ record_function_format (get_identifier ("fprintf"), NULL_TREE, 0, 2, 3);
+ record_function_format (get_identifier ("sprintf"), NULL_TREE, 0, 2, 3);
+ record_function_format (get_identifier ("scanf"), NULL_TREE, 1, 1, 2);
+ record_function_format (get_identifier ("fscanf"), NULL_TREE, 1, 2, 3);
+ record_function_format (get_identifier ("sscanf"), NULL_TREE, 1, 2, 3);
+ record_function_format (get_identifier ("vprintf"), NULL_TREE, 0, 1, 0);
+ record_function_format (get_identifier ("vfprintf"), NULL_TREE, 0, 2, 0);
+ record_function_format (get_identifier ("vsprintf"), NULL_TREE, 0, 2, 0);
+}
+
+/* Record information for argument format checking. FUNCTION_IDENT is
+ the identifier node for the name of the function to check (its decl
+ need not exist yet). IS_SCAN is true for scanf-type format checking;
+ false indicates printf-style format checking. FORMAT_NUM is the number
+ of the argument which is the format control string (starting from 1).
+ FIRST_ARG_NUM is the number of the first actual argument to check
+ against teh format string, or zero if no checking is not be done
+ (e.g. for varargs such as vfprintf). */
+
+void
+record_function_format (name, assembler_name, is_scan,
+ format_num, first_arg_num)
+ tree name;
+ tree assembler_name;
+ int is_scan;
+ int format_num;
+ int first_arg_num;
+{
+ function_format_info *info;
+
+ /* Re-use existing structure if it's there. */
+
+ for (info = function_format_list; info; info = info->next)
+ {
+ if (info->name == name && info->assembler_name == assembler_name)
+ break;
+ }
+ if (! info)
+ {
+ info = (function_format_info *) xmalloc (sizeof (function_format_info));
+ info->next = function_format_list;
+ function_format_list = info;
+
+ info->name = name;
+ info->assembler_name = assembler_name;
+ }
+
+ info->is_scan = is_scan;
+ info->format_num = format_num;
+ info->first_arg_num = first_arg_num;
+}
+
+static char tfaff[] = "too few arguments for format";
+
+/* Check the argument list of a call to printf, scanf, etc.
+ NAME is the function identifier.
+ ASSEMBLER_NAME is the function's assembler identifier.
+ (Either NAME or ASSEMBLER_NAME, but not both, may be NULL_TREE.)
+ PARAMS is the list of argument values. */
+
+void
+check_function_format (name, assembler_name, params)
+ tree name;
+ tree assembler_name;
+ tree params;
+{
+ function_format_info *info;
+
+ /* See if this function is a format function. */
+ for (info = function_format_list; info; info = info->next)
+ {
+ if (info->assembler_name
+ ? (info->assembler_name == assembler_name)
+ : (info->name == name))
+ {
+ /* Yup; check it. */
+ check_format_info (info, params);
+ break;
+ }
+ }
+}
+
+/* Check the argument list of a call to printf, scanf, etc.
+ INFO points to the function_format_info structure.
+ PARAMS is the list of argument values. */
+
+static void
+check_format_info (info, params)
+ function_format_info *info;
+ tree params;
+{
+ int i;
+ int arg_num;
+ int suppressed, wide, precise;
+ int length_char;
+ int format_char;
+ int format_length;
+ tree format_tree;
+ tree cur_param;
+ tree cur_type;
+ tree wanted_type;
+ tree first_fillin_param;
+ char *format_chars;
+ format_char_info *fci;
+ static char message[132];
+ char flag_chars[8];
+ int has_operand_number = 0;
+
+ /* Skip to format argument. If the argument isn't available, there's
+ no work for us to do; prototype checking will catch the problem. */
+ for (arg_num = 1; ; ++arg_num)
+ {
+ if (params == 0)
+ return;
+ if (arg_num == info->format_num)
+ break;
+ params = TREE_CHAIN (params);
+ }
+ format_tree = TREE_VALUE (params);
+ params = TREE_CHAIN (params);
+ if (format_tree == 0)
+ return;
+ /* We can only check the format if it's a string constant. */
+ while (TREE_CODE (format_tree) == NOP_EXPR)
+ format_tree = TREE_OPERAND (format_tree, 0); /* strip coercion */
+ if (format_tree == null_pointer_node)
+ {
+ warning ("null format string");
+ return;
+ }
+ if (TREE_CODE (format_tree) != ADDR_EXPR)
+ return;
+ format_tree = TREE_OPERAND (format_tree, 0);
+ if (TREE_CODE (format_tree) != STRING_CST)
+ return;
+ format_chars = TREE_STRING_POINTER (format_tree);
+ format_length = TREE_STRING_LENGTH (format_tree);
+ if (format_length <= 1)
+ warning ("zero-length format string");
+ if (format_chars[--format_length] != 0)
+ {
+ warning ("unterminated format string");
+ return;
+ }
+ /* Skip to first argument to check. */
+ while (arg_num + 1 < info->first_arg_num)
+ {
+ if (params == 0)
+ return;
+ params = TREE_CHAIN (params);
+ ++arg_num;
+ }
+
+ first_fillin_param = params;
+ while (1)
+ {
+ int aflag;
+ if (*format_chars == 0)
+ {
+ if (format_chars - TREE_STRING_POINTER (format_tree) != format_length)
+ warning ("embedded `\\0' in format");
+ if (info->first_arg_num != 0 && params != 0 && ! has_operand_number)
+ warning ("too many arguments for format");
+ return;
+ }
+ if (*format_chars++ != '%')
+ continue;
+ if (*format_chars == 0)
+ {
+ warning ("spurious trailing `%%' in format");
+ continue;
+ }
+ if (*format_chars == '%')
+ {
+ ++format_chars;
+ continue;
+ }
+ flag_chars[0] = 0;
+ suppressed = wide = precise = FALSE;
+ if (info->is_scan)
+ {
+ suppressed = *format_chars == '*';
+ if (suppressed)
+ ++format_chars;
+ while (isdigit (*format_chars))
+ ++format_chars;
+ }
+ else
+ {
+ /* See if we have a number followed by a dollar sign. If we do,
+ it is an operand number, so set PARAMS to that operand. */
+ if (*format_chars >= '0' && *format_chars <= '9')
+ {
+ char *p = format_chars;
+
+ while (*p >= '0' && *p++ <= '9')
+ ;
+
+ if (*p == '$')
+ {
+ int opnum = atoi (format_chars);
+
+ params = first_fillin_param;
+ format_chars = p + 1;
+ has_operand_number = 1;
+
+ for (i = 1; i < opnum && params != 0; i++)
+ params = TREE_CHAIN (params);
+
+ if (opnum == 0 || params == 0)
+ {
+ warning ("operand number out of range in format");
+ return;
+ }
+ }
+ }
+
+ while (*format_chars != 0 && index (" +#0-", *format_chars) != 0)
+ {
+ if (index (flag_chars, *format_chars) != 0)
+ {
+ sprintf (message, "repeated `%c' flag in format",
+ *format_chars);
+ warning (message);
+ }
+ i = strlen (flag_chars);
+ flag_chars[i++] = *format_chars++;
+ flag_chars[i] = 0;
+ }
+ /* "If the space and + flags both appear,
+ the space flag will be ignored." */
+ if (index (flag_chars, ' ') != 0
+ && index (flag_chars, '+') != 0)
+ warning ("use of both ` ' and `+' flags in format");
+ /* "If the 0 and - flags both appear,
+ the 0 flag will be ignored." */
+ if (index (flag_chars, '0') != 0
+ && index (flag_chars, '-') != 0)
+ warning ("use of both `0' and `-' flags in format");
+ if (*format_chars == '*')
+ {
+ wide = TRUE;
+ /* "...a field width...may be indicated by an asterisk.
+ In this case, an int argument supplies the field width..." */
+ ++format_chars;
+ if (params == 0)
+ {
+ warning (tfaff);
+ return;
+ }
+ if (info->first_arg_num != 0)
+ {
+ cur_param = TREE_VALUE (params);
+ params = TREE_CHAIN (params);
+ ++arg_num;
+ /* size_t is generally not valid here.
+ It will work on most machines, because size_t and int
+ have the same mode. But might as well warn anyway,
+ since it will fail on other machines. */
+ if ((TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
+ != integer_type_node)
+ &&
+ (TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
+ != unsigned_type_node))
+ {
+ sprintf (message,
+ "field width is not type int (arg %d)",
+ arg_num);
+ warning (message);
+ }
+ }
+ }
+ else
+ {
+ while (isdigit (*format_chars))
+ {
+ wide = TRUE;
+ ++format_chars;
+ }
+ }
+ if (*format_chars == '.')
+ {
+ precise = TRUE;
+ ++format_chars;
+ if (*format_chars != '*' && !isdigit (*format_chars))
+ warning ("`.' not followed by `*' or digit in format");
+ /* "...a...precision...may be indicated by an asterisk.
+ In this case, an int argument supplies the...precision." */
+ if (*format_chars == '*')
+ {
+ if (info->first_arg_num != 0)
+ {
+ ++format_chars;
+ if (params == 0)
+ {
+ warning (tfaff);
+ return;
+ }
+ cur_param = TREE_VALUE (params);
+ params = TREE_CHAIN (params);
+ ++arg_num;
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
+ != integer_type_node)
+ {
+ sprintf (message,
+ "field width is not type int (arg %d)",
+ arg_num);
+ warning (message);
+ }
+ }
+ }
+ else
+ {
+ while (isdigit (*format_chars))
+ ++format_chars;
+ }
+ }
+ }
+ if (*format_chars == 'h' || *format_chars == 'l' || *format_chars == 'L')
+ length_char = *format_chars++;
+ else
+ length_char = 0;
+ aflag = 0;
+ if (*format_chars == 'a')
+ {
+ aflag = 1;
+ format_chars++;
+ }
+ if (suppressed && length_char != 0)
+ {
+ sprintf (message,
+ "use of `*' and `%c' together in format",
+ length_char);
+ warning (message);
+ }
+ format_char = *format_chars;
+ if (format_char == 0)
+ {
+ warning ("conversion lacks type at end of format");
+ continue;
+ }
+ format_chars++;
+ fci = info->is_scan ? scan_char_table : print_char_table;
+ while (fci->format_chars != 0
+ && index (fci->format_chars, format_char) == 0)
+ ++fci;
+ if (fci->format_chars == 0)
+ {
+ if (format_char >= 040 && format_char < 0177)
+ sprintf (message,
+ "unknown conversion type character `%c' in format",
+ format_char);
+ else
+ sprintf (message,
+ "unknown conversion type character 0x%x in format",
+ format_char);
+ warning (message);
+ continue;
+ }
+ if (wide && index (fci->flag_chars, 'w') == 0)
+ {
+ sprintf (message, "width used with `%c' format",
+ format_char);
+ warning (message);
+ }
+ if (precise && index (fci->flag_chars, 'p') == 0)
+ {
+ sprintf (message, "precision used with `%c' format",
+ format_char);
+ warning (message);
+ }
+ if (aflag && index (fci->flag_chars, 'a') == 0)
+ {
+ sprintf (message, "`a' flag used with `%c' format",
+ format_char);
+ warning (message);
+ }
+ if (info->is_scan && format_char == '[')
+ {
+ /* Skip over scan set, in case it happens to have '%' in it. */
+ if (*format_chars == '^')
+ ++format_chars;
+ /* Find closing bracket; if one is hit immediately, then
+ it's part of the scan set rather than a terminator. */
+ if (*format_chars == ']')
+ ++format_chars;
+ while (*format_chars && *format_chars != ']')
+ ++format_chars;
+ if (*format_chars != ']')
+ /* The end of the format string was reached. */
+ warning ("no closing `]' for `%%[' format");
+ }
+ if (suppressed)
+ {
+ if (index (fci->flag_chars, '*') == 0)
+ {
+ sprintf (message,
+ "suppression of `%c' conversion in format",
+ format_char);
+ warning (message);
+ }
+ continue;
+ }
+ for (i = 0; flag_chars[i] != 0; ++i)
+ {
+ if (index (fci->flag_chars, flag_chars[i]) == 0)
+ {
+ sprintf (message, "flag `%c' used with type `%c'",
+ flag_chars[i], format_char);
+ warning (message);
+ }
+ }
+ if (precise && index (flag_chars, '0') != 0
+ && (format_char == 'd' || format_char == 'i'
+ || format_char == 'o' || format_char == 'u'
+ || format_char == 'x' || format_char == 'x'))
+ {
+ sprintf (message,
+ "precision and `0' flag not both allowed with `%c' format",
+ format_char);
+ warning (message);
+ }
+ switch (length_char)
+ {
+ default: wanted_type = fci->nolen ? *(fci->nolen) : 0; break;
+ case 'h': wanted_type = fci->hlen ? *(fci->hlen) : 0; break;
+ case 'l': wanted_type = fci->llen ? *(fci->llen) : 0; break;
+ case 'L': wanted_type = fci->bigllen ? *(fci->bigllen) : 0; break;
+ }
+ if (wanted_type == 0)
+ {
+ sprintf (message,
+ "use of `%c' length character with `%c' type character",
+ length_char, format_char);
+ warning (message);
+ }
+
+ /*
+ ** XXX -- should kvetch about stuff such as
+ ** {
+ ** const int i;
+ **
+ ** scanf ("%d", &i);
+ ** }
+ */
+
+ /* Finally. . .check type of argument against desired type! */
+ if (info->first_arg_num == 0)
+ continue;
+ if (params == 0)
+ {
+ warning (tfaff);
+ return;
+ }
+ cur_param = TREE_VALUE (params);
+ params = TREE_CHAIN (params);
+ ++arg_num;
+ cur_type = TREE_TYPE (cur_param);
+
+ /* Check the types of any additional pointer arguments
+ that precede the "real" argument. */
+ for (i = 0; i < fci->pointer_count; ++i)
+ {
+ if (TREE_CODE (cur_type) == POINTER_TYPE)
+ {
+ cur_type = TREE_TYPE (cur_type);
+ continue;
+ }
+ sprintf (message,
+ "format argument is not a %s (arg %d)",
+ ((fci->pointer_count == 1) ? "pointer" : "pointer to a pointer"),
+ arg_num);
+ warning (message);
+ break;
+ }
+
+ /* Check the type of the "real" argument, if there's a type we want. */
+ if (i == fci->pointer_count && wanted_type != 0
+ && wanted_type != TYPE_MAIN_VARIANT (cur_type)
+ /* If we want `void *', allow any pointer type.
+ (Anything else would already have got a warning.) */
+ && ! (wanted_type == void_type_node
+ && fci->pointer_count > 0)
+ /* Don't warn about differences merely in signedness. */
+ && !(TREE_CODE (wanted_type) == INTEGER_TYPE
+ && TREE_CODE (TYPE_MAIN_VARIANT (cur_type)) == INTEGER_TYPE
+ && (TREE_UNSIGNED (wanted_type)
+ ? wanted_type == (cur_type = unsigned_type (cur_type))
+ : wanted_type == (cur_type = signed_type (cur_type))))
+ /* Likewise, "signed char", "unsigned char" and "char" are
+ equivalent but the above test won't consider them equivalent. */
+ && ! (wanted_type == char_type_node
+ && (TYPE_MAIN_VARIANT (cur_type) == signed_char_type_node
+ || TYPE_MAIN_VARIANT (cur_type) == unsigned_char_type_node)))
+ {
+ register char *this;
+ register char *that;
+
+ this = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (wanted_type)));
+ that = 0;
+ if (TREE_CODE (cur_type) != ERROR_MARK
+ && TYPE_NAME (cur_type) != 0
+ && TREE_CODE (cur_type) != INTEGER_TYPE
+ && !(TREE_CODE (cur_type) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (cur_type)) == INTEGER_TYPE))
+ {
+ if (TREE_CODE (TYPE_NAME (cur_type)) == TYPE_DECL
+ && DECL_NAME (TYPE_NAME (cur_type)) != 0)
+ that = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (cur_type)));
+ else
+ that = IDENTIFIER_POINTER (TYPE_NAME (cur_type));
+ }
+
+ /* A nameless type can't possibly match what the format wants.
+ So there will be a warning for it.
+ Make up a string to describe vaguely what it is. */
+ if (that == 0)
+ {
+ if (TREE_CODE (cur_type) == POINTER_TYPE)
+ that = "pointer";
+ else
+ that = "different type";
+ }
+
+ /* Make the warning better in case of mismatch of int vs long. */
+ if (TREE_CODE (cur_type) == INTEGER_TYPE
+ && TREE_CODE (wanted_type) == INTEGER_TYPE
+ && TYPE_PRECISION (cur_type) == TYPE_PRECISION (wanted_type)
+ && TYPE_NAME (cur_type) != 0
+ && TREE_CODE (TYPE_NAME (cur_type)) == TYPE_DECL)
+ that = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (cur_type)));
+
+ if (strcmp (this, that) != 0)
+ {
+ sprintf (message, "%s format, %s arg (arg %d)",
+ this, that, arg_num);
+ warning (message);
+ }
+ }
+ }
+}
+
+/* Print a warning if a constant expression had overflow in folding.
+ Invoke this function on every expression that the language
+ requires to be a constant expression.
+ Note the ANSI C standard says it is erroneous for a
+ constant expression to overflow. */
+
+void
+constant_expression_warning (value)
+ tree value;
+{
+ if ((TREE_CODE (value) == INTEGER_CST || TREE_CODE (value) == REAL_CST
+ || TREE_CODE (value) == COMPLEX_CST)
+ && TREE_CONSTANT_OVERFLOW (value) && pedantic)
+ pedwarn ("overflow in constant expression");
+}
+
+/* Print a warning if an expression had overflow in folding.
+ Invoke this function on every expression that
+ (1) appears in the source code, and
+ (2) might be a constant expression that overflowed, and
+ (3) is not already checked by convert_and_check;
+ however, do not invoke this function on operands of explicit casts. */
+
+void
+overflow_warning (value)
+ tree value;
+{
+ if ((TREE_CODE (value) == INTEGER_CST
+ || (TREE_CODE (value) == COMPLEX_CST
+ && TREE_CODE (TREE_REALPART (value)) == INTEGER_CST))
+ && TREE_OVERFLOW (value))
+ {
+ TREE_OVERFLOW (value) = 0;
+ warning ("integer overflow in expression");
+ }
+ else if ((TREE_CODE (value) == REAL_CST
+ || (TREE_CODE (value) == COMPLEX_CST
+ && TREE_CODE (TREE_REALPART (value)) == REAL_CST))
+ && TREE_OVERFLOW (value))
+ {
+ TREE_OVERFLOW (value) = 0;
+ warning ("floating-pointer overflow in expression");
+ }
+}
+
+/* Print a warning if a large constant is truncated to unsigned,
+ or if -Wconversion is used and a constant < 0 is converted to unsigned.
+ Invoke this function on every expression that might be implicitly
+ converted to an unsigned type. */
+
+void
+unsigned_conversion_warning (result, operand)
+ tree result, operand;
+{
+ if (TREE_CODE (operand) == INTEGER_CST
+ && TREE_CODE (TREE_TYPE (result)) == INTEGER_TYPE
+ && TREE_UNSIGNED (TREE_TYPE (result))
+ && !int_fits_type_p (operand, TREE_TYPE (result)))
+ {
+ if (!int_fits_type_p (operand, signed_type (TREE_TYPE (result))))
+ /* This detects cases like converting -129 or 256 to unsigned char. */
+ warning ("large integer implicitly truncated to unsigned type");
+ else if (warn_conversion)
+ warning ("negative integer implicitly converted to unsigned type");
+ }
+}
+
+/* Convert EXPR to TYPE, warning about conversion problems with constants.
+ Invoke this function on every expression that is converted implicitly,
+ i.e. because of language rules and not because of an explicit cast. */
+
+tree
+convert_and_check (type, expr)
+ tree type, expr;
+{
+ tree t = convert (type, expr);
+ if (TREE_CODE (t) == INTEGER_CST)
+ {
+ if (TREE_OVERFLOW (t))
+ {
+ TREE_OVERFLOW (t) = 0;
+
+ /* No warning for converting 0x80000000 to int. */
+ if (!(TREE_UNSIGNED (type) < TREE_UNSIGNED (TREE_TYPE (expr))
+ && TREE_CODE (TREE_TYPE (expr)) == INTEGER_TYPE
+ && TYPE_PRECISION (type) == TYPE_PRECISION (TREE_TYPE (expr))))
+ /* If EXPR fits in the unsigned version of TYPE,
+ don't warn unless pedantic. */
+ if (pedantic
+ || TREE_UNSIGNED (type)
+ || ! int_fits_type_p (expr, unsigned_type (type)))
+ warning ("overflow in implicit constant conversion");
+ }
+ else
+ unsigned_conversion_warning (t, expr);
+ }
+ return t;
+}
+
+void
+c_expand_expr_stmt (expr)
+ tree expr;
+{
+ /* Do default conversion if safe and possibly important,
+ in case within ({...}). */
+ if ((TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE && lvalue_p (expr))
+ || TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE)
+ expr = default_conversion (expr);
+
+ if (TREE_TYPE (expr) != error_mark_node
+ && TYPE_SIZE (TREE_TYPE (expr)) == 0
+ && TREE_CODE (TREE_TYPE (expr)) != ARRAY_TYPE)
+ error ("expression statement has incomplete type");
+
+ expand_expr_stmt (expr);
+}
+
+/* Validate the expression after `case' and apply default promotions. */
+
+tree
+check_case_value (value)
+ tree value;
+{
+ if (value == NULL_TREE)
+ return value;
+
+ /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
+ STRIP_TYPE_NOPS (value);
+
+ if (TREE_CODE (value) != INTEGER_CST
+ && value != error_mark_node)
+ {
+ error ("case label does not reduce to an integer constant");
+ value = error_mark_node;
+ }
+ else
+ /* Promote char or short to int. */
+ value = default_conversion (value);
+
+ constant_expression_warning (value);
+
+ return value;
+}
+
+/* Return an integer type with BITS bits of precision,
+ that is unsigned if UNSIGNEDP is nonzero, otherwise signed. */
+
+tree
+type_for_size (bits, unsignedp)
+ unsigned bits;
+ int unsignedp;
+{
+ if (bits == TYPE_PRECISION (signed_char_type_node))
+ return unsignedp ? unsigned_char_type_node : signed_char_type_node;
+
+ if (bits == TYPE_PRECISION (short_integer_type_node))
+ return unsignedp ? short_unsigned_type_node : short_integer_type_node;
+
+ if (bits == TYPE_PRECISION (integer_type_node))
+ return unsignedp ? unsigned_type_node : integer_type_node;
+
+ if (bits == TYPE_PRECISION (long_integer_type_node))
+ return unsignedp ? long_unsigned_type_node : long_integer_type_node;
+
+ if (bits == TYPE_PRECISION (long_long_integer_type_node))
+ return (unsignedp ? long_long_unsigned_type_node
+ : long_long_integer_type_node);
+
+ if (bits <= TYPE_PRECISION (intQI_type_node))
+ return unsignedp ? unsigned_intQI_type_node : intQI_type_node;
+
+ if (bits <= TYPE_PRECISION (intHI_type_node))
+ return unsignedp ? unsigned_intHI_type_node : intHI_type_node;
+
+ if (bits <= TYPE_PRECISION (intSI_type_node))
+ return unsignedp ? unsigned_intSI_type_node : intSI_type_node;
+
+ if (bits <= TYPE_PRECISION (intDI_type_node))
+ return unsignedp ? unsigned_intDI_type_node : intDI_type_node;
+
+ return 0;
+}
+
+/* Return a data type that has machine mode MODE.
+ If the mode is an integer,
+ then UNSIGNEDP selects between signed and unsigned types. */
+
+tree
+type_for_mode (mode, unsignedp)
+ enum machine_mode mode;
+ int unsignedp;
+{
+ if (mode == TYPE_MODE (signed_char_type_node))
+ return unsignedp ? unsigned_char_type_node : signed_char_type_node;
+
+ if (mode == TYPE_MODE (short_integer_type_node))
+ return unsignedp ? short_unsigned_type_node : short_integer_type_node;
+
+ if (mode == TYPE_MODE (integer_type_node))
+ return unsignedp ? unsigned_type_node : integer_type_node;
+
+ if (mode == TYPE_MODE (long_integer_type_node))
+ return unsignedp ? long_unsigned_type_node : long_integer_type_node;
+
+ if (mode == TYPE_MODE (long_long_integer_type_node))
+ return unsignedp ? long_long_unsigned_type_node : long_long_integer_type_node;
+
+ if (mode == TYPE_MODE (intQI_type_node))
+ return unsignedp ? unsigned_intQI_type_node : intQI_type_node;
+
+ if (mode == TYPE_MODE (intHI_type_node))
+ return unsignedp ? unsigned_intHI_type_node : intHI_type_node;
+
+ if (mode == TYPE_MODE (intSI_type_node))
+ return unsignedp ? unsigned_intSI_type_node : intSI_type_node;
+
+ if (mode == TYPE_MODE (intDI_type_node))
+ return unsignedp ? unsigned_intDI_type_node : intDI_type_node;
+
+ if (mode == TYPE_MODE (float_type_node))
+ return float_type_node;
+
+ if (mode == TYPE_MODE (double_type_node))
+ return double_type_node;
+
+ if (mode == TYPE_MODE (long_double_type_node))
+ return long_double_type_node;
+
+ if (mode == TYPE_MODE (build_pointer_type (char_type_node)))
+ return build_pointer_type (char_type_node);
+
+ if (mode == TYPE_MODE (build_pointer_type (integer_type_node)))
+ return build_pointer_type (integer_type_node);
+
+ return 0;
+}
+
+/* Print an error message for invalid operands to arith operation CODE.
+ NOP_EXPR is used as a special case (see truthvalue_conversion). */
+
+void
+binary_op_error (code)
+ enum tree_code code;
+{
+ register char *opname = "unknown";
+
+ switch (code)
+ {
+ case NOP_EXPR:
+ error ("invalid truth-value expression");
+ return;
+
+ case PLUS_EXPR:
+ opname = "+"; break;
+ case MINUS_EXPR:
+ opname = "-"; break;
+ case MULT_EXPR:
+ opname = "*"; break;
+ case MAX_EXPR:
+ opname = "max"; break;
+ case MIN_EXPR:
+ opname = "min"; break;
+ case EQ_EXPR:
+ opname = "=="; break;
+ case NE_EXPR:
+ opname = "!="; break;
+ case LE_EXPR:
+ opname = "<="; break;
+ case GE_EXPR:
+ opname = ">="; break;
+ case LT_EXPR:
+ opname = "<"; break;
+ case GT_EXPR:
+ opname = ">"; break;
+ case LSHIFT_EXPR:
+ opname = "<<"; break;
+ case RSHIFT_EXPR:
+ opname = ">>"; break;
+ case TRUNC_MOD_EXPR:
+ case FLOOR_MOD_EXPR:
+ opname = "%"; break;
+ case TRUNC_DIV_EXPR:
+ case FLOOR_DIV_EXPR:
+ opname = "/"; break;
+ case BIT_AND_EXPR:
+ opname = "&"; break;
+ case BIT_IOR_EXPR:
+ opname = "|"; break;
+ case TRUTH_ANDIF_EXPR:
+ opname = "&&"; break;
+ case TRUTH_ORIF_EXPR:
+ opname = "||"; break;
+ case BIT_XOR_EXPR:
+ opname = "^"; break;
+ case LROTATE_EXPR:
+ case RROTATE_EXPR:
+ opname = "rotate"; break;
+ }
+ error ("invalid operands to binary %s", opname);
+}
+
+/* Subroutine of build_binary_op, used for comparison operations.
+ See if the operands have both been converted from subword integer types
+ and, if so, perhaps change them both back to their original type.
+ This function is also responsible for converting the two operands
+ to the proper common type for comparison.
+
+ The arguments of this function are all pointers to local variables
+ of build_binary_op: OP0_PTR is &OP0, OP1_PTR is &OP1,
+ RESTYPE_PTR is &RESULT_TYPE and RESCODE_PTR is &RESULTCODE.
+
+ If this function returns nonzero, it means that the comparison has
+ a constant value. What this function returns is an expression for
+ that value. */
+
+tree
+shorten_compare (op0_ptr, op1_ptr, restype_ptr, rescode_ptr)
+ tree *op0_ptr, *op1_ptr;
+ tree *restype_ptr;
+ enum tree_code *rescode_ptr;
+{
+ register tree type;
+ tree op0 = *op0_ptr;
+ tree op1 = *op1_ptr;
+ int unsignedp0, unsignedp1;
+ int real1, real2;
+ tree primop0, primop1;
+ enum tree_code code = *rescode_ptr;
+
+ /* Throw away any conversions to wider types
+ already present in the operands. */
+
+ primop0 = get_narrower (op0, &unsignedp0);
+ primop1 = get_narrower (op1, &unsignedp1);
+
+ /* Handle the case that OP0 does not *contain* a conversion
+ but it *requires* conversion to FINAL_TYPE. */
+
+ if (op0 == primop0 && TREE_TYPE (op0) != *restype_ptr)
+ unsignedp0 = TREE_UNSIGNED (TREE_TYPE (op0));
+ if (op1 == primop1 && TREE_TYPE (op1) != *restype_ptr)
+ unsignedp1 = TREE_UNSIGNED (TREE_TYPE (op1));
+
+ /* If one of the operands must be floated, we cannot optimize. */
+ real1 = TREE_CODE (TREE_TYPE (primop0)) == REAL_TYPE;
+ real2 = TREE_CODE (TREE_TYPE (primop1)) == REAL_TYPE;
+
+ /* If first arg is constant, swap the args (changing operation
+ so value is preserved), for canonicalization. */
+
+ if (TREE_CONSTANT (primop0))
+ {
+ register tree tem = primop0;
+ register int temi = unsignedp0;
+ primop0 = primop1;
+ primop1 = tem;
+ tem = op0;
+ op0 = op1;
+ op1 = tem;
+ *op0_ptr = op0;
+ *op1_ptr = op1;
+ unsignedp0 = unsignedp1;
+ unsignedp1 = temi;
+ temi = real1;
+ real1 = real2;
+ real2 = temi;
+
+ switch (code)
+ {
+ case LT_EXPR:
+ code = GT_EXPR;
+ break;
+ case GT_EXPR:
+ code = LT_EXPR;
+ break;
+ case LE_EXPR:
+ code = GE_EXPR;
+ break;
+ case GE_EXPR:
+ code = LE_EXPR;
+ break;
+ }
+ *rescode_ptr = code;
+ }
+
+ /* If comparing an integer against a constant more bits wide,
+ maybe we can deduce a value of 1 or 0 independent of the data.
+ Or else truncate the constant now
+ rather than extend the variable at run time.
+
+ This is only interesting if the constant is the wider arg.
+ Also, it is not safe if the constant is unsigned and the
+ variable arg is signed, since in this case the variable
+ would be sign-extended and then regarded as unsigned.
+ Our technique fails in this case because the lowest/highest
+ possible unsigned results don't follow naturally from the
+ lowest/highest possible values of the variable operand.
+ For just EQ_EXPR and NE_EXPR there is another technique that
+ could be used: see if the constant can be faithfully represented
+ in the other operand's type, by truncating it and reextending it
+ and see if that preserves the constant's value. */
+
+ if (!real1 && !real2
+ && TREE_CODE (primop1) == INTEGER_CST
+ && TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (*restype_ptr))
+ {
+ int min_gt, max_gt, min_lt, max_lt;
+ tree maxval, minval;
+ /* 1 if comparison is nominally unsigned. */
+ int unsignedp = TREE_UNSIGNED (*restype_ptr);
+ tree val;
+
+ type = signed_or_unsigned_type (unsignedp0, TREE_TYPE (primop0));
+
+ maxval = TYPE_MAX_VALUE (type);
+ minval = TYPE_MIN_VALUE (type);
+
+ if (unsignedp && !unsignedp0)
+ *restype_ptr = signed_type (*restype_ptr);
+
+ if (TREE_TYPE (primop1) != *restype_ptr)
+ primop1 = convert (*restype_ptr, primop1);
+ if (type != *restype_ptr)
+ {
+ minval = convert (*restype_ptr, minval);
+ maxval = convert (*restype_ptr, maxval);
+ }
+
+ if (unsignedp && unsignedp0)
+ {
+ min_gt = INT_CST_LT_UNSIGNED (primop1, minval);
+ max_gt = INT_CST_LT_UNSIGNED (primop1, maxval);
+ min_lt = INT_CST_LT_UNSIGNED (minval, primop1);
+ max_lt = INT_CST_LT_UNSIGNED (maxval, primop1);
+ }
+ else
+ {
+ min_gt = INT_CST_LT (primop1, minval);
+ max_gt = INT_CST_LT (primop1, maxval);
+ min_lt = INT_CST_LT (minval, primop1);
+ max_lt = INT_CST_LT (maxval, primop1);
+ }
+
+ val = 0;
+ /* This used to be a switch, but Genix compiler can't handle that. */
+ if (code == NE_EXPR)
+ {
+ if (max_lt || min_gt)
+ val = integer_one_node;
+ }
+ else if (code == EQ_EXPR)
+ {
+ if (max_lt || min_gt)
+ val = integer_zero_node;
+ }
+ else if (code == LT_EXPR)
+ {
+ if (max_lt)
+ val = integer_one_node;
+ if (!min_lt)
+ val = integer_zero_node;
+ }
+ else if (code == GT_EXPR)
+ {
+ if (min_gt)
+ val = integer_one_node;
+ if (!max_gt)
+ val = integer_zero_node;
+ }
+ else if (code == LE_EXPR)
+ {
+ if (!max_gt)
+ val = integer_one_node;
+ if (min_gt)
+ val = integer_zero_node;
+ }
+ else if (code == GE_EXPR)
+ {
+ if (!min_lt)
+ val = integer_one_node;
+ if (max_lt)
+ val = integer_zero_node;
+ }
+
+ /* If primop0 was sign-extended and unsigned comparison specd,
+ we did a signed comparison above using the signed type bounds.
+ But the comparison we output must be unsigned.
+
+ Also, for inequalities, VAL is no good; but if the signed
+ comparison had *any* fixed result, it follows that the
+ unsigned comparison just tests the sign in reverse
+ (positive values are LE, negative ones GE).
+ So we can generate an unsigned comparison
+ against an extreme value of the signed type. */
+
+ if (unsignedp && !unsignedp0)
+ {
+ if (val != 0)
+ switch (code)
+ {
+ case LT_EXPR:
+ case GE_EXPR:
+ primop1 = TYPE_MIN_VALUE (type);
+ val = 0;
+ break;
+
+ case LE_EXPR:
+ case GT_EXPR:
+ primop1 = TYPE_MAX_VALUE (type);
+ val = 0;
+ break;
+ }
+ type = unsigned_type (type);
+ }
+
+ if (!max_gt && !unsignedp0 && TREE_CODE (primop0) != INTEGER_CST)
+ {
+ /* This is the case of (char)x >?< 0x80, which people used to use
+ expecting old C compilers to change the 0x80 into -0x80. */
+ if (val == integer_zero_node)
+ warning ("comparison is always 0 due to limited range of data type");
+ if (val == integer_one_node)
+ warning ("comparison is always 1 due to limited range of data type");
+ }
+
+ if (!min_lt && unsignedp0 && TREE_CODE (primop0) != INTEGER_CST)
+ {
+ /* This is the case of (unsigned char)x >?< -1 or < 0. */
+ if (val == integer_zero_node)
+ warning ("comparison is always 0 due to limited range of data type");
+ if (val == integer_one_node)
+ warning ("comparison is always 1 due to limited range of data type");
+ }
+
+ if (val != 0)
+ {
+ /* Don't forget to evaluate PRIMOP0 if it has side effects. */
+ if (TREE_SIDE_EFFECTS (primop0))
+ return build (COMPOUND_EXPR, TREE_TYPE (val), primop0, val);
+ return val;
+ }
+
+ /* Value is not predetermined, but do the comparison
+ in the type of the operand that is not constant.
+ TYPE is already properly set. */
+ }
+ else if (real1 && real2
+ && (TYPE_PRECISION (TREE_TYPE (primop0))
+ == TYPE_PRECISION (TREE_TYPE (primop1))))
+ type = TREE_TYPE (primop0);
+
+ /* If args' natural types are both narrower than nominal type
+ and both extend in the same manner, compare them
+ in the type of the wider arg.
+ Otherwise must actually extend both to the nominal
+ common type lest different ways of extending
+ alter the result.
+ (eg, (short)-1 == (unsigned short)-1 should be 0.) */
+
+ else if (unsignedp0 == unsignedp1 && real1 == real2
+ && TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (*restype_ptr)
+ && TYPE_PRECISION (TREE_TYPE (primop1)) < TYPE_PRECISION (*restype_ptr))
+ {
+ type = common_type (TREE_TYPE (primop0), TREE_TYPE (primop1));
+ type = signed_or_unsigned_type (unsignedp0
+ || TREE_UNSIGNED (*restype_ptr),
+ type);
+ /* Make sure shorter operand is extended the right way
+ to match the longer operand. */
+ primop0 = convert (signed_or_unsigned_type (unsignedp0, TREE_TYPE (primop0)),
+ primop0);
+ primop1 = convert (signed_or_unsigned_type (unsignedp1, TREE_TYPE (primop1)),
+ primop1);
+ }
+ else
+ {
+ /* Here we must do the comparison on the nominal type
+ using the args exactly as we received them. */
+ type = *restype_ptr;
+ primop0 = op0;
+ primop1 = op1;
+
+ if (!real1 && !real2 && integer_zerop (primop1)
+ && TREE_UNSIGNED (*restype_ptr))
+ {
+ tree value = 0;
+ switch (code)
+ {
+ case GE_EXPR:
+ if (extra_warnings)
+ warning ("unsigned value >= 0 is always 1");
+ value = integer_one_node;
+ break;
+
+ case LT_EXPR:
+ if (extra_warnings)
+ warning ("unsigned value < 0 is always 0");
+ value = integer_zero_node;
+ }
+
+ if (value != 0)
+ {
+ /* Don't forget to evaluate PRIMOP0 if it has side effects. */
+ if (TREE_SIDE_EFFECTS (primop0))
+ return build (COMPOUND_EXPR, TREE_TYPE (value),
+ primop0, value);
+ return value;
+ }
+ }
+ }
+
+ *op0_ptr = convert (type, primop0);
+ *op1_ptr = convert (type, primop1);
+
+ *restype_ptr = integer_type_node;
+
+ return 0;
+}
+
+/* Prepare expr to be an argument of a TRUTH_NOT_EXPR,
+ or validate its data type for an `if' or `while' statement or ?..: exp.
+
+ This preparation consists of taking the ordinary
+ representation of an expression expr and producing a valid tree
+ boolean expression describing whether expr is nonzero. We could
+ simply always do build_binary_op (NE_EXPR, expr, integer_zero_node, 1),
+ but we optimize comparisons, &&, ||, and !.
+
+ The resulting type should always be `integer_type_node'. */
+
+tree
+truthvalue_conversion (expr)
+ tree expr;
+{
+ if (TREE_CODE (expr) == ERROR_MARK)
+ return expr;
+
+#if 0 /* This appears to be wrong for C++. */
+ /* These really should return error_mark_node after 2.4 is stable.
+ But not all callers handle ERROR_MARK properly. */
+ switch (TREE_CODE (TREE_TYPE (expr)))
+ {
+ case RECORD_TYPE:
+ error ("struct type value used where scalar is required");
+ return integer_zero_node;
+
+ case UNION_TYPE:
+ error ("union type value used where scalar is required");
+ return integer_zero_node;
+
+ case ARRAY_TYPE:
+ error ("array type value used where scalar is required");
+ return integer_zero_node;
+
+ default:
+ break;
+ }
+#endif /* 0 */
+
+ switch (TREE_CODE (expr))
+ {
+ /* It is simpler and generates better code to have only TRUTH_*_EXPR
+ or comparison expressions as truth values at this level. */
+#if 0
+ case COMPONENT_REF:
+ /* A one-bit unsigned bit-field is already acceptable. */
+ if (1 == TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (expr, 1)))
+ && TREE_UNSIGNED (TREE_OPERAND (expr, 1)))
+ return expr;
+ break;
+#endif
+
+ case EQ_EXPR:
+ /* It is simpler and generates better code to have only TRUTH_*_EXPR
+ or comparison expressions as truth values at this level. */
+#if 0
+ if (integer_zerop (TREE_OPERAND (expr, 1)))
+ return build_unary_op (TRUTH_NOT_EXPR, TREE_OPERAND (expr, 0), 0);
+#endif
+ case NE_EXPR: case LE_EXPR: case GE_EXPR: case LT_EXPR: case GT_EXPR:
+ case TRUTH_ANDIF_EXPR:
+ case TRUTH_ORIF_EXPR:
+ case TRUTH_AND_EXPR:
+ case TRUTH_OR_EXPR:
+ case TRUTH_XOR_EXPR:
+ return convert (integer_type_node, expr);
+
+ case ERROR_MARK:
+ return expr;
+
+ case INTEGER_CST:
+ return integer_zerop (expr) ? integer_zero_node : integer_one_node;
+
+ case REAL_CST:
+ return real_zerop (expr) ? integer_zero_node : integer_one_node;
+
+ case ADDR_EXPR:
+ if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 0)))
+ return build (COMPOUND_EXPR, integer_type_node,
+ TREE_OPERAND (expr, 0), integer_one_node);
+ else
+ return integer_one_node;
+
+ case COMPLEX_EXPR:
+ return build_binary_op ((TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 1))
+ ? TRUTH_OR_EXPR : TRUTH_ORIF_EXPR),
+ truthvalue_conversion (TREE_OPERAND (expr, 0)),
+ truthvalue_conversion (TREE_OPERAND (expr, 1)),
+ 0);
+
+ case NEGATE_EXPR:
+ case ABS_EXPR:
+ case FLOAT_EXPR:
+ case FFS_EXPR:
+ /* These don't change whether an object is non-zero or zero. */
+ return truthvalue_conversion (TREE_OPERAND (expr, 0));
+
+ case LROTATE_EXPR:
+ case RROTATE_EXPR:
+ /* These don't change whether an object is zero or non-zero, but
+ we can't ignore them if their second arg has side-effects. */
+ if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 1)))
+ return build (COMPOUND_EXPR, integer_type_node, TREE_OPERAND (expr, 1),
+ truthvalue_conversion (TREE_OPERAND (expr, 0)));
+ else
+ return truthvalue_conversion (TREE_OPERAND (expr, 0));
+
+ case COND_EXPR:
+ /* Distribute the conversion into the arms of a COND_EXPR. */
+ return fold (build (COND_EXPR, integer_type_node, TREE_OPERAND (expr, 0),
+ truthvalue_conversion (TREE_OPERAND (expr, 1)),
+ truthvalue_conversion (TREE_OPERAND (expr, 2))));
+
+ case CONVERT_EXPR:
+ /* Don't cancel the effect of a CONVERT_EXPR from a REFERENCE_TYPE,
+ since that affects how `default_conversion' will behave. */
+ if (TREE_CODE (TREE_TYPE (expr)) == REFERENCE_TYPE
+ || TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == REFERENCE_TYPE)
+ break;
+ /* fall through... */
+ case NOP_EXPR:
+ /* If this is widening the argument, we can ignore it. */
+ if (TYPE_PRECISION (TREE_TYPE (expr))
+ >= TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (expr, 0))))
+ return truthvalue_conversion (TREE_OPERAND (expr, 0));
+ break;
+
+ case MINUS_EXPR:
+ /* With IEEE arithmetic, x - x may not equal 0, so we can't optimize
+ this case. */
+ if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
+ && TREE_CODE (TREE_TYPE (expr)) == REAL_TYPE)
+ break;
+ /* fall through... */
+ case BIT_XOR_EXPR:
+ /* This and MINUS_EXPR can be changed into a comparison of the
+ two objects. */
+ if (TREE_TYPE (TREE_OPERAND (expr, 0))
+ == TREE_TYPE (TREE_OPERAND (expr, 1)))
+ return build_binary_op (NE_EXPR, TREE_OPERAND (expr, 0),
+ TREE_OPERAND (expr, 1), 1);
+ return build_binary_op (NE_EXPR, TREE_OPERAND (expr, 0),
+ fold (build1 (NOP_EXPR,
+ TREE_TYPE (TREE_OPERAND (expr, 0)),
+ TREE_OPERAND (expr, 1))), 1);
+
+ case BIT_AND_EXPR:
+ if (integer_onep (TREE_OPERAND (expr, 1)))
+ return expr;
+
+ case MODIFY_EXPR:
+ if (warn_parentheses && C_EXP_ORIGINAL_CODE (expr) == MODIFY_EXPR)
+ warning ("suggest parentheses around assignment used as truth value");
+ break;
+ }
+
+ if (TREE_CODE (TREE_TYPE (expr)) == COMPLEX_TYPE)
+ return (build_binary_op
+ ((TREE_SIDE_EFFECTS (expr)
+ ? TRUTH_OR_EXPR : TRUTH_ORIF_EXPR),
+ truthvalue_conversion (build_unary_op (REALPART_EXPR, expr, 0)),
+ truthvalue_conversion (build_unary_op (IMAGPART_EXPR, expr, 0)),
+ 0));
+
+ return build_binary_op (NE_EXPR, expr, integer_zero_node, 1);
+}
+
+/* Read the rest of a #-directive from input stream FINPUT.
+ In normal use, the directive name and the white space after it
+ have already been read, so they won't be included in the result.
+ We allow for the fact that the directive line may contain
+ a newline embedded within a character or string literal which forms
+ a part of the directive.
+
+ The value is a string in a reusable buffer. It remains valid
+ only until the next time this function is called. */
+
+char *
+get_directive_line (finput)
+ register FILE *finput;
+{
+ static char *directive_buffer = NULL;
+ static unsigned buffer_length = 0;
+ register char *p;
+ register char *buffer_limit;
+ register int looking_for = 0;
+ register int char_escaped = 0;
+
+ if (buffer_length == 0)
+ {
+ directive_buffer = (char *)xmalloc (128);
+ buffer_length = 128;
+ }
+
+ buffer_limit = &directive_buffer[buffer_length];
+
+ for (p = directive_buffer; ; )
+ {
+ int c;
+
+ /* Make buffer bigger if it is full. */
+ if (p >= buffer_limit)
+ {
+ register unsigned bytes_used = (p - directive_buffer);
+
+ buffer_length *= 2;
+ directive_buffer
+ = (char *)xrealloc (directive_buffer, buffer_length);
+ p = &directive_buffer[bytes_used];
+ buffer_limit = &directive_buffer[buffer_length];
+ }
+
+ c = getc (finput);
+
+ /* Discard initial whitespace. */
+ if ((c == ' ' || c == '\t') && p == directive_buffer)
+ continue;
+
+ /* Detect the end of the directive. */
+ if (c == '\n' && looking_for == 0)
+ {
+ ungetc (c, finput);
+ c = '\0';
+ }
+
+ *p++ = c;
+
+ if (c == 0)
+ return directive_buffer;
+
+ /* Handle string and character constant syntax. */
+ if (looking_for)
+ {
+ if (looking_for == c && !char_escaped)
+ looking_for = 0; /* Found terminator... stop looking. */
+ }
+ else
+ if (c == '\'' || c == '"')
+ looking_for = c; /* Don't stop buffering until we see another
+ another one of these (or an EOF). */
+
+ /* Handle backslash. */
+ char_escaped = (c == '\\' && ! char_escaped);
+ }
+}
+
+/* Make a variant type in the proper way for C/C++, propagating qualifiers
+ down to the element type of an array. */
+
+tree
+c_build_type_variant (type, constp, volatilep)
+ tree type;
+ int constp, volatilep;
+{
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ tree real_main_variant = TYPE_MAIN_VARIANT (type);
+
+ push_obstacks (TYPE_OBSTACK (real_main_variant),
+ TYPE_OBSTACK (real_main_variant));
+ type = build_array_type (c_build_type_variant (TREE_TYPE (type),
+ constp, volatilep),
+ TYPE_DOMAIN (type));
+
+ /* TYPE must be on same obstack as REAL_MAIN_VARIANT. If not,
+ make a copy. (TYPE might have come from the hash table and
+ REAL_MAIN_VARIANT might be in some function's obstack.) */
+
+ if (TYPE_OBSTACK (type) != TYPE_OBSTACK (real_main_variant))
+ {
+ type = copy_node (type);
+ TYPE_POINTER_TO (type) = TYPE_REFERENCE_TO (type) = 0;
+ }
+
+ TYPE_MAIN_VARIANT (type) = real_main_variant;
+ pop_obstacks ();
+ }
+ return build_type_variant (type, constp, volatilep);
+}
diff --git a/gnu/usr.bin/cc/cc_int/caller-save.c b/gnu/usr.bin/cc/cc_int/caller-save.c
new file mode 100644
index 0000000..5b09606
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/caller-save.c
@@ -0,0 +1,762 @@
+/* Save and restore call-clobbered registers which are live across a call.
+ Copyright (C) 1989, 1992, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "config.h"
+#include "rtl.h"
+#include "insn-config.h"
+#include "flags.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "recog.h"
+#include "basic-block.h"
+#include "reload.h"
+#include "expr.h"
+
+#ifndef MAX_MOVE_MAX
+#define MAX_MOVE_MAX MOVE_MAX
+#endif
+
+#ifndef MAX_UNITS_PER_WORD
+#define MAX_UNITS_PER_WORD UNITS_PER_WORD
+#endif
+
+/* Modes for each hard register that we can save. The smallest mode is wide
+ enough to save the entire contents of the register. When saving the
+ register because it is live we first try to save in multi-register modes.
+ If that is not possible the save is done one register at a time. */
+
+static enum machine_mode
+ regno_save_mode[FIRST_PSEUDO_REGISTER][MAX_MOVE_MAX / MAX_UNITS_PER_WORD + 1];
+
+/* For each hard register, a place on the stack where it can be saved,
+ if needed. */
+
+static rtx
+ regno_save_mem[FIRST_PSEUDO_REGISTER][MAX_MOVE_MAX / MAX_UNITS_PER_WORD + 1];
+
+/* We will only make a register eligible for caller-save if it can be
+ saved in its widest mode with a simple SET insn as long as the memory
+ address is valid. We record the INSN_CODE is those insns here since
+ when we emit them, the addresses might not be valid, so they might not
+ be recognized. */
+
+static enum insn_code
+ reg_save_code[FIRST_PSEUDO_REGISTER][MAX_MOVE_MAX / MAX_UNITS_PER_WORD + 1];
+static enum insn_code
+ reg_restore_code[FIRST_PSEUDO_REGISTER][MAX_MOVE_MAX / MAX_UNITS_PER_WORD + 1];
+
+/* Set of hard regs currently live (during scan of all insns). */
+
+static HARD_REG_SET hard_regs_live;
+
+/* Set of hard regs currently residing in save area (during insn scan). */
+
+static HARD_REG_SET hard_regs_saved;
+
+/* Set of hard regs which need to be restored before referenced. */
+
+static HARD_REG_SET hard_regs_need_restore;
+
+/* Number of registers currently in hard_regs_saved. */
+
+int n_regs_saved;
+
+static void set_reg_live PROTO((rtx, rtx));
+static void clear_reg_live PROTO((rtx));
+static void restore_referenced_regs PROTO((rtx, rtx, enum machine_mode));
+static int insert_save_restore PROTO((rtx, int, int,
+ enum machine_mode, int));
+
+/* Initialize for caller-save.
+
+ Look at all the hard registers that are used by a call and for which
+ regclass.c has not already excluded from being used across a call.
+
+ Ensure that we can find a mode to save the register and that there is a
+ simple insn to save and restore the register. This latter check avoids
+ problems that would occur if we tried to save the MQ register of some
+ machines directly into memory. */
+
+void
+init_caller_save ()
+{
+ char *first_obj = (char *) oballoc (0);
+ rtx addr_reg;
+ int offset;
+ rtx address;
+ int i, j;
+
+ /* First find all the registers that we need to deal with and all
+ the modes that they can have. If we can't find a mode to use,
+ we can't have the register live over calls. */
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ if (call_used_regs[i] && ! call_fixed_regs[i])
+ {
+ for (j = 1; j <= MOVE_MAX / UNITS_PER_WORD; j++)
+ {
+ regno_save_mode[i][j] = choose_hard_reg_mode (i, j);
+ if (regno_save_mode[i][j] == VOIDmode && j == 1)
+ {
+ call_fixed_regs[i] = 1;
+ SET_HARD_REG_BIT (call_fixed_reg_set, i);
+ }
+ }
+ }
+ else
+ regno_save_mode[i][1] = VOIDmode;
+ }
+
+ /* The following code tries to approximate the conditions under which
+ we can easily save and restore a register without scratch registers or
+ other complexities. It will usually work, except under conditions where
+ the validity of an insn operand is dependent on the address offset.
+ No such cases are currently known.
+
+ We first find a typical offset from some BASE_REG_CLASS register.
+ This address is chosen by finding the first register in the class
+ and by finding the smallest power of two that is a valid offset from
+ that register in every mode we will use to save registers. */
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (TEST_HARD_REG_BIT (reg_class_contents[(int) BASE_REG_CLASS], i))
+ break;
+
+ if (i == FIRST_PSEUDO_REGISTER)
+ abort ();
+
+ addr_reg = gen_rtx (REG, Pmode, i);
+
+ for (offset = 1 << (HOST_BITS_PER_INT / 2); offset; offset >>= 1)
+ {
+ address = gen_rtx (PLUS, Pmode, addr_reg, GEN_INT (offset));
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (regno_save_mode[i][1] != VOIDmode
+ && ! strict_memory_address_p (regno_save_mode[i][1], address))
+ break;
+
+ if (i == FIRST_PSEUDO_REGISTER)
+ break;
+ }
+
+ /* If we didn't find a valid address, we must use register indirect. */
+ if (offset == 0)
+ address = addr_reg;
+
+ /* Next we try to form an insn to save and restore the register. We
+ see if such an insn is recognized and meets its constraints. */
+
+ start_sequence ();
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ for (j = 1; j <= MOVE_MAX / UNITS_PER_WORD; j++)
+ if (regno_save_mode[i][j] != VOIDmode)
+ {
+ rtx mem = gen_rtx (MEM, regno_save_mode[i][j], address);
+ rtx reg = gen_rtx (REG, regno_save_mode[i][j], i);
+ rtx savepat = gen_rtx (SET, VOIDmode, mem, reg);
+ rtx restpat = gen_rtx (SET, VOIDmode, reg, mem);
+ rtx saveinsn = emit_insn (savepat);
+ rtx restinsn = emit_insn (restpat);
+ int ok;
+
+ reg_save_code[i][j] = recog_memoized (saveinsn);
+ reg_restore_code[i][j] = recog_memoized (restinsn);
+
+ /* Now extract both insns and see if we can meet their constraints. */
+ ok = (reg_save_code[i][j] != -1 && reg_restore_code[i][j] != -1);
+ if (ok)
+ {
+ insn_extract (saveinsn);
+ ok = constrain_operands (reg_save_code[i][j], 1);
+ insn_extract (restinsn);
+ ok &= constrain_operands (reg_restore_code[i][j], 1);
+ }
+
+ if (! ok)
+ {
+ regno_save_mode[i][j] = VOIDmode;
+ if (j == 1)
+ {
+ call_fixed_regs[i] = 1;
+ SET_HARD_REG_BIT (call_fixed_reg_set, i);
+ }
+ }
+ }
+
+ end_sequence ();
+
+ obfree (first_obj);
+}
+
+/* Initialize save areas by showing that we haven't allocated any yet. */
+
+void
+init_save_areas ()
+{
+ int i, j;
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ for (j = 1; j <= MOVE_MAX / UNITS_PER_WORD; j++)
+ regno_save_mem[i][j] = 0;
+}
+
+/* Allocate save areas for any hard registers that might need saving.
+ We take a conservative approach here and look for call-clobbered hard
+ registers that are assigned to pseudos that cross calls. This may
+ overestimate slightly (especially if some of these registers are later
+ used as spill registers), but it should not be significant.
+
+ Then perform register elimination in the addresses of the save area
+ locations; return 1 if all eliminated addresses are strictly valid.
+ We assume that our caller has set up the elimination table to the
+ worst (largest) possible offsets.
+
+ Set *PCHANGED to 1 if we had to allocate some memory for the save area.
+
+ Future work:
+
+ In the fallback case we should iterate backwards across all possible
+ modes for the save, choosing the largest available one instead of
+ falling back to the smallest mode immediately. (eg TF -> DF -> SF).
+
+ We do not try to use "move multiple" instructions that exist
+ on some machines (such as the 68k moveml). It could be a win to try
+ and use them when possible. The hard part is doing it in a way that is
+ machine independent since they might be saving non-consecutive
+ registers. (imagine caller-saving d0,d1,a0,a1 on the 68k) */
+
+int
+setup_save_areas (pchanged)
+ int *pchanged;
+{
+ int i, j, k;
+ HARD_REG_SET hard_regs_used;
+ int ok = 1;
+
+
+ /* Allocate space in the save area for the largest multi-register
+ pseudos first, then work backwards to single register
+ pseudos. */
+
+ /* Find and record all call-used hard-registers in this function. */
+ CLEAR_HARD_REG_SET (hard_regs_used);
+ for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+ if (reg_renumber[i] >= 0 && reg_n_calls_crossed[i] > 0)
+ {
+ int regno = reg_renumber[i];
+ int endregno
+ = regno + HARD_REGNO_NREGS (regno, GET_MODE (regno_reg_rtx[i]));
+ int nregs = endregno - regno;
+
+ for (j = 0; j < nregs; j++)
+ {
+ if (call_used_regs[regno+j])
+ SET_HARD_REG_BIT (hard_regs_used, regno+j);
+ }
+ }
+
+ /* Now run through all the call-used hard-registers and allocate
+ space for them in the caller-save area. Try to allocate space
+ in a manner which allows multi-register saves/restores to be done. */
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ for (j = MOVE_MAX / UNITS_PER_WORD; j > 0; j--)
+ {
+ int ok = 1;
+ int do_save;
+
+ /* If no mode exists for this size, try another. Also break out
+ if we have already saved this hard register. */
+ if (regno_save_mode[i][j] == VOIDmode || regno_save_mem[i][1] != 0)
+ continue;
+
+ /* See if any register in this group has been saved. */
+ do_save = 1;
+ for (k = 0; k < j; k++)
+ if (regno_save_mem[i + k][1])
+ {
+ do_save = 0;
+ break;
+ }
+ if (! do_save)
+ continue;
+
+ for (k = 0; k < j; k++)
+ {
+ int regno = i + k;
+ ok &= (TEST_HARD_REG_BIT (hard_regs_used, regno) != 0);
+ }
+
+ /* We have found an acceptable mode to store in. */
+ if (ok)
+ {
+
+ regno_save_mem[i][j]
+ = assign_stack_local (regno_save_mode[i][j],
+ GET_MODE_SIZE (regno_save_mode[i][j]), 0);
+
+ /* Setup single word save area just in case... */
+ for (k = 0; k < j; k++)
+ {
+ /* This should not depend on WORDS_BIG_ENDIAN.
+ The order of words in regs is the same as in memory. */
+ rtx temp = gen_rtx (MEM, regno_save_mode[i+k][1],
+ XEXP (regno_save_mem[i][j], 0));
+
+ regno_save_mem[i+k][1]
+ = adj_offsettable_operand (temp, k * UNITS_PER_WORD);
+ }
+ *pchanged = 1;
+ }
+ }
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ for (j = 1; j <= MOVE_MAX / UNITS_PER_WORD; j++)
+ if (regno_save_mem[i][j] != 0)
+ ok &= strict_memory_address_p (GET_MODE (regno_save_mem[i][j]),
+ XEXP (eliminate_regs (regno_save_mem[i][j], 0, NULL_RTX), 0));
+
+ return ok;
+}
+
+/* Find the places where hard regs are live across calls and save them.
+
+ INSN_MODE is the mode to assign to any insns that we add. This is used
+ by reload to determine whether or not reloads or register eliminations
+ need be done on these insns. */
+
+void
+save_call_clobbered_regs (insn_mode)
+ enum machine_mode insn_mode;
+{
+ rtx insn;
+ int b;
+
+ for (b = 0; b < n_basic_blocks; b++)
+ {
+ regset regs_live = basic_block_live_at_start[b];
+ rtx prev_block_last = PREV_INSN (basic_block_head[b]);
+ REGSET_ELT_TYPE bit;
+ int offset, i, j;
+ int regno;
+
+ /* Compute hard regs live at start of block -- this is the
+ real hard regs marked live, plus live pseudo regs that
+ have been renumbered to hard regs. No registers have yet been
+ saved because we restore all of them before the end of the basic
+ block. */
+
+#ifdef HARD_REG_SET
+ hard_regs_live = *regs_live;
+#else
+ COPY_HARD_REG_SET (hard_regs_live, regs_live);
+#endif
+
+ CLEAR_HARD_REG_SET (hard_regs_saved);
+ CLEAR_HARD_REG_SET (hard_regs_need_restore);
+ n_regs_saved = 0;
+
+ for (offset = 0, i = 0; offset < regset_size; offset++)
+ {
+ if (regs_live[offset] == 0)
+ i += REGSET_ELT_BITS;
+ else
+ for (bit = 1; bit && i < max_regno; bit <<= 1, i++)
+ if ((regs_live[offset] & bit)
+ && (regno = reg_renumber[i]) >= 0)
+ for (j = regno;
+ j < regno + HARD_REGNO_NREGS (regno,
+ PSEUDO_REGNO_MODE (i));
+ j++)
+ SET_HARD_REG_BIT (hard_regs_live, j);
+
+ }
+
+ /* Now scan the insns in the block, keeping track of what hard
+ regs are live as we go. When we see a call, save the live
+ call-clobbered hard regs. */
+
+ for (insn = basic_block_head[b]; ; insn = NEXT_INSN (insn))
+ {
+ RTX_CODE code = GET_CODE (insn);
+
+ if (GET_RTX_CLASS (code) == 'i')
+ {
+ rtx link;
+
+ /* If some registers have been saved, see if INSN references
+ any of them. We must restore them before the insn if so. */
+
+ if (n_regs_saved)
+ restore_referenced_regs (PATTERN (insn), insn, insn_mode);
+
+ /* NB: the normal procedure is to first enliven any
+ registers set by insn, then deaden any registers that
+ had their last use at insn. This is incorrect now,
+ since multiple pseudos may have been mapped to the
+ same hard reg, and the death notes are ambiguous. So
+ it must be done in the other, safe, order. */
+
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_DEAD)
+ clear_reg_live (XEXP (link, 0));
+
+ /* When we reach a call, we need to save all registers that are
+ live, call-used, not fixed, and not already saved. We must
+ test at this point because registers that die in a CALL_INSN
+ are not live across the call and likewise for registers that
+ are born in the CALL_INSN.
+
+ If registers are filled with parameters for this function,
+ and some of these are also being set by this function, then
+ they will not appear to die (no REG_DEAD note for them),
+ to check if in fact they do, collect the set registers in
+ hard_regs_live first. */
+
+ if (code == CALL_INSN)
+ {
+ HARD_REG_SET this_call_sets;
+ {
+ HARD_REG_SET old_hard_regs_live;
+
+ /* Save the hard_regs_live information. */
+ COPY_HARD_REG_SET (old_hard_regs_live, hard_regs_live);
+
+ /* Now calculate hard_regs_live for this CALL_INSN
+ only. */
+ CLEAR_HARD_REG_SET (hard_regs_live);
+ note_stores (PATTERN (insn), set_reg_live);
+ COPY_HARD_REG_SET (this_call_sets, hard_regs_live);
+
+ /* Restore the hard_regs_live information. */
+ COPY_HARD_REG_SET (hard_regs_live, old_hard_regs_live);
+ }
+
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (call_used_regs[regno] && ! call_fixed_regs[regno]
+ && TEST_HARD_REG_BIT (hard_regs_live, regno)
+ /* It must not be set by this instruction. */
+ && ! TEST_HARD_REG_BIT (this_call_sets, regno)
+ && ! TEST_HARD_REG_BIT (hard_regs_saved, regno))
+ regno += insert_save_restore (insn, 1, regno,
+ insn_mode, 0);
+
+ /* Put the information for this CALL_INSN on top of what
+ we already had. */
+ IOR_HARD_REG_SET (hard_regs_live, this_call_sets);
+ COPY_HARD_REG_SET (hard_regs_need_restore, hard_regs_saved);
+
+ /* Must recompute n_regs_saved. */
+ n_regs_saved = 0;
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (TEST_HARD_REG_BIT (hard_regs_saved, regno))
+ n_regs_saved++;
+ }
+ else
+ note_stores (PATTERN (insn), set_reg_live);
+
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_UNUSED)
+ clear_reg_live (XEXP (link, 0));
+ }
+
+ if (insn == basic_block_end[b])
+ break;
+ }
+
+ /* At the end of the basic block, we must restore any registers that
+ remain saved. If the last insn in the block is a JUMP_INSN, put
+ the restore before the insn, otherwise, put it after the insn. */
+
+ if (n_regs_saved)
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (TEST_HARD_REG_BIT (hard_regs_need_restore, regno))
+ regno += insert_save_restore ((GET_CODE (insn) == JUMP_INSN
+ ? insn : NEXT_INSN (insn)), 0,
+ regno, insn_mode, MOVE_MAX / UNITS_PER_WORD);
+
+ /* If we added any insns at the start of the block, update the start
+ of the block to point at those insns. */
+ basic_block_head[b] = NEXT_INSN (prev_block_last);
+ }
+}
+
+/* Here from note_stores when an insn stores a value in a register.
+ Set the proper bit or bits in hard_regs_live. All pseudos that have
+ been assigned hard regs have had their register number changed already,
+ so we can ignore pseudos. */
+
+static void
+set_reg_live (reg, setter)
+ rtx reg, setter;
+{
+ register int regno, endregno, i;
+ enum machine_mode mode = GET_MODE (reg);
+ int word = 0;
+
+ if (GET_CODE (reg) == SUBREG)
+ {
+ word = SUBREG_WORD (reg);
+ reg = SUBREG_REG (reg);
+ }
+
+ if (GET_CODE (reg) != REG || REGNO (reg) >= FIRST_PSEUDO_REGISTER)
+ return;
+
+ regno = REGNO (reg) + word;
+ endregno = regno + HARD_REGNO_NREGS (regno, mode);
+
+ for (i = regno; i < endregno; i++)
+ {
+ SET_HARD_REG_BIT (hard_regs_live, i);
+ CLEAR_HARD_REG_BIT (hard_regs_saved, i);
+ CLEAR_HARD_REG_BIT (hard_regs_need_restore, i);
+ }
+}
+
+/* Here when a REG_DEAD note records the last use of a reg. Clear
+ the appropriate bit or bits in hard_regs_live. Again we can ignore
+ pseudos. */
+
+static void
+clear_reg_live (reg)
+ rtx reg;
+{
+ register int regno, endregno, i;
+
+ if (GET_CODE (reg) != REG || REGNO (reg) >= FIRST_PSEUDO_REGISTER)
+ return;
+
+ regno = REGNO (reg);
+ endregno= regno + HARD_REGNO_NREGS (regno, GET_MODE (reg));
+
+ for (i = regno; i < endregno; i++)
+ {
+ CLEAR_HARD_REG_BIT (hard_regs_live, i);
+ CLEAR_HARD_REG_BIT (hard_regs_need_restore, i);
+ CLEAR_HARD_REG_BIT (hard_regs_saved, i);
+ }
+}
+
+/* If any register currently residing in the save area is referenced in X,
+ which is part of INSN, emit code to restore the register in front of INSN.
+ INSN_MODE is the mode to assign to any insns that we add. */
+
+static void
+restore_referenced_regs (x, insn, insn_mode)
+ rtx x;
+ rtx insn;
+ enum machine_mode insn_mode;
+{
+ enum rtx_code code = GET_CODE (x);
+ char *fmt;
+ int i, j;
+
+ if (code == CLOBBER)
+ return;
+
+ if (code == REG)
+ {
+ int regno = REGNO (x);
+
+ /* If this is a pseudo, scan its memory location, since it might
+ involve the use of another register, which might be saved. */
+
+ if (regno >= FIRST_PSEUDO_REGISTER
+ && reg_equiv_mem[regno] != 0)
+ restore_referenced_regs (XEXP (reg_equiv_mem[regno], 0),
+ insn, insn_mode);
+ else if (regno >= FIRST_PSEUDO_REGISTER
+ && reg_equiv_address[regno] != 0)
+ restore_referenced_regs (reg_equiv_address[regno],
+ insn, insn_mode);
+
+ /* Otherwise if this is a hard register, restore any piece of it that
+ is currently saved. */
+
+ else if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ int numregs = HARD_REGNO_NREGS (regno, GET_MODE (x));
+ /* Save at most SAVEREGS at a time. This can not be larger than
+ MOVE_MAX, because that causes insert_save_restore to fail. */
+ int saveregs = MIN (numregs, MOVE_MAX / UNITS_PER_WORD);
+ int endregno = regno + numregs;
+
+ for (i = regno; i < endregno; i++)
+ if (TEST_HARD_REG_BIT (hard_regs_need_restore, i))
+ i += insert_save_restore (insn, 0, i, insn_mode, saveregs);
+ }
+
+ return;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ restore_referenced_regs (XEXP (x, i), insn, insn_mode);
+ else if (fmt[i] == 'E')
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ restore_referenced_regs (XVECEXP (x, i, j), insn, insn_mode);
+ }
+}
+
+/* Insert a sequence of insns to save or restore, SAVE_P says which,
+ REGNO. Place these insns in front of INSN. INSN_MODE is the mode
+ to assign to these insns. MAXRESTORE is the maximum number of registers
+ which should be restored during this call (when SAVE_P == 0). It should
+ never be less than 1 since we only work with entire registers.
+
+ Note that we have verified in init_caller_save that we can do this
+ with a simple SET, so use it. Set INSN_CODE to what we save there
+ since the address might not be valid so the insn might not be recognized.
+ These insns will be reloaded and have register elimination done by
+ find_reload, so we need not worry about that here.
+
+ Return the extra number of registers saved. */
+
+static int
+insert_save_restore (insn, save_p, regno, insn_mode, maxrestore)
+ rtx insn;
+ int save_p;
+ int regno;
+ enum machine_mode insn_mode;
+ int maxrestore;
+{
+ rtx pat;
+ enum insn_code code;
+ int i, numregs;
+
+ /* A common failure mode if register status is not correct in the RTL
+ is for this routine to be called with a REGNO we didn't expect to
+ save. That will cause us to write an insn with a (nil) SET_DEST
+ or SET_SRC. Instead of doing so and causing a crash later, check
+ for this common case and abort here instead. This will remove one
+ step in debugging such problems. */
+
+ if (regno_save_mem[regno][1] == 0)
+ abort ();
+
+#ifdef HAVE_cc0
+ /* If INSN references CC0, put our insns in front of the insn that sets
+ CC0. This is always safe, since the only way we could be passed an
+ insn that references CC0 is for a restore, and doing a restore earlier
+ isn't a problem. We do, however, assume here that CALL_INSNs don't
+ reference CC0. Guard against non-INSN's like CODE_LABEL. */
+
+ if ((GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN)
+ && reg_referenced_p (cc0_rtx, PATTERN (insn)))
+ insn = prev_nonnote_insn (insn);
+#endif
+
+ /* Get the pattern to emit and update our status. */
+ if (save_p)
+ {
+ int i, j, k;
+ int ok;
+
+ /* See if we can save several registers with a single instruction.
+ Work backwards to the single register case. */
+ for (i = MOVE_MAX / UNITS_PER_WORD; i > 0; i--)
+ {
+ ok = 1;
+ if (regno_save_mem[regno][i] != 0)
+ for (j = 0; j < i; j++)
+ {
+ if (! call_used_regs[regno + j] || call_fixed_regs[regno + j]
+ || ! TEST_HARD_REG_BIT (hard_regs_live, regno + j)
+ || TEST_HARD_REG_BIT (hard_regs_saved, regno + j))
+ ok = 0;
+ }
+ else
+ continue;
+
+ /* Must do this one save at a time */
+ if (! ok)
+ continue;
+
+ pat = gen_rtx (SET, VOIDmode, regno_save_mem[regno][i],
+ gen_rtx (REG, GET_MODE (regno_save_mem[regno][i]), regno));
+ code = reg_save_code[regno][i];
+
+ /* Set hard_regs_saved for all the registers we saved. */
+ for (k = 0; k < i; k++)
+ {
+ SET_HARD_REG_BIT (hard_regs_saved, regno + k);
+ SET_HARD_REG_BIT (hard_regs_need_restore, regno + k);
+ n_regs_saved++;
+ }
+
+ numregs = i;
+ break;
+ }
+ }
+ else
+ {
+ int i, j, k;
+ int ok;
+
+ /* See if we can restore `maxrestore' registers at once. Work
+ backwards to the single register case. */
+ for (i = maxrestore; i > 0; i--)
+ {
+ ok = 1;
+ if (regno_save_mem[regno][i])
+ for (j = 0; j < i; j++)
+ {
+ if (! TEST_HARD_REG_BIT (hard_regs_need_restore, regno + j))
+ ok = 0;
+ }
+ else
+ continue;
+
+ /* Must do this one restore at a time */
+ if (! ok)
+ continue;
+
+ pat = gen_rtx (SET, VOIDmode,
+ gen_rtx (REG, GET_MODE (regno_save_mem[regno][i]),
+ regno),
+ regno_save_mem[regno][i]);
+ code = reg_restore_code[regno][i];
+
+
+ /* Clear status for all registers we restored. */
+ for (k = 0; k < i; k++)
+ {
+ CLEAR_HARD_REG_BIT (hard_regs_need_restore, regno + k);
+ n_regs_saved--;
+ }
+
+ numregs = i;
+ break;
+ }
+ }
+ /* Emit the insn and set the code and mode. */
+
+ insn = emit_insn_before (pat, insn);
+ PUT_MODE (insn, insn_mode);
+ INSN_CODE (insn) = code;
+
+ /* Tell our callers how many extra registers we saved/restored */
+ return numregs - 1;
+}
diff --git a/gnu/usr.bin/cc/cc_int/calls.c b/gnu/usr.bin/cc/cc_int/calls.c
new file mode 100644
index 0000000..f035079
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/calls.c
@@ -0,0 +1,3061 @@
+/* Convert function calls to rtl insns, for GNU C compiler.
+ Copyright (C) 1989, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "config.h"
+#include "rtl.h"
+#include "tree.h"
+#include "flags.h"
+#include "expr.h"
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include "insn-flags.h"
+
+/* Decide whether a function's arguments should be processed
+ from first to last or from last to first.
+
+ They should if the stack and args grow in opposite directions, but
+ only if we have push insns. */
+
+#ifdef PUSH_ROUNDING
+
+#if defined (STACK_GROWS_DOWNWARD) != defined (ARGS_GROW_DOWNWARD)
+#define PUSH_ARGS_REVERSED /* If it's last to first */
+#endif
+
+#endif
+
+/* Like STACK_BOUNDARY but in units of bytes, not bits. */
+#define STACK_BYTES (STACK_BOUNDARY / BITS_PER_UNIT)
+
+/* Data structure and subroutines used within expand_call. */
+
+struct arg_data
+{
+ /* Tree node for this argument. */
+ tree tree_value;
+ /* Mode for value; TYPE_MODE unless promoted. */
+ enum machine_mode mode;
+ /* Current RTL value for argument, or 0 if it isn't precomputed. */
+ rtx value;
+ /* Initially-compute RTL value for argument; only for const functions. */
+ rtx initial_value;
+ /* Register to pass this argument in, 0 if passed on stack, or an
+ EXPR_LIST if the arg is to be copied into multiple different
+ registers. */
+ rtx reg;
+ /* If REG was promoted from the actual mode of the argument expression,
+ indicates whether the promotion is sign- or zero-extended. */
+ int unsignedp;
+ /* Number of registers to use. 0 means put the whole arg in registers.
+ Also 0 if not passed in registers. */
+ int partial;
+ /* Non-zero if argument must be passed on stack.
+ Note that some arguments may be passed on the stack
+ even though pass_on_stack is zero, just because FUNCTION_ARG says so.
+ pass_on_stack identifies arguments that *cannot* go in registers. */
+ int pass_on_stack;
+ /* Offset of this argument from beginning of stack-args. */
+ struct args_size offset;
+ /* Similar, but offset to the start of the stack slot. Different from
+ OFFSET if this arg pads downward. */
+ struct args_size slot_offset;
+ /* Size of this argument on the stack, rounded up for any padding it gets,
+ parts of the argument passed in registers do not count.
+ If REG_PARM_STACK_SPACE is defined, then register parms
+ are counted here as well. */
+ struct args_size size;
+ /* Location on the stack at which parameter should be stored. The store
+ has already been done if STACK == VALUE. */
+ rtx stack;
+ /* Location on the stack of the start of this argument slot. This can
+ differ from STACK if this arg pads downward. This location is known
+ to be aligned to FUNCTION_ARG_BOUNDARY. */
+ rtx stack_slot;
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ /* Place that this stack area has been saved, if needed. */
+ rtx save_area;
+#endif
+#ifdef STRICT_ALIGNMENT
+ /* If an argument's alignment does not permit direct copying into registers,
+ copy in smaller-sized pieces into pseudos. These are stored in a
+ block pointed to by this field. The next field says how many
+ word-sized pseudos we made. */
+ rtx *aligned_regs;
+ int n_aligned_regs;
+#endif
+};
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+/* A vector of one char per byte of stack space. A byte if non-zero if
+ the corresponding stack location has been used.
+ This vector is used to prevent a function call within an argument from
+ clobbering any stack already set up. */
+static char *stack_usage_map;
+
+/* Size of STACK_USAGE_MAP. */
+static int highest_outgoing_arg_in_use;
+
+/* stack_arg_under_construction is nonzero when an argument may be
+ initialized with a constructor call (including a C function that
+ returns a BLKmode struct) and expand_call must take special action
+ to make sure the object being constructed does not overlap the
+ argument list for the constructor call. */
+int stack_arg_under_construction;
+#endif
+
+static int calls_function PROTO((tree, int));
+static int calls_function_1 PROTO((tree, int));
+static void emit_call_1 PROTO((rtx, tree, int, int, rtx, rtx, int,
+ rtx, int));
+static void store_one_arg PROTO ((struct arg_data *, rtx, int, int,
+ tree, int));
+
+/* If WHICH is 1, return 1 if EXP contains a call to the built-in function
+ `alloca'.
+
+ If WHICH is 0, return 1 if EXP contains a call to any function.
+ Actually, we only need return 1 if evaluating EXP would require pushing
+ arguments on the stack, but that is too difficult to compute, so we just
+ assume any function call might require the stack. */
+
+static tree calls_function_save_exprs;
+
+static int
+calls_function (exp, which)
+ tree exp;
+ int which;
+{
+ int val;
+ calls_function_save_exprs = 0;
+ val = calls_function_1 (exp, which);
+ calls_function_save_exprs = 0;
+ return val;
+}
+
+static int
+calls_function_1 (exp, which)
+ tree exp;
+ int which;
+{
+ register int i;
+ enum tree_code code = TREE_CODE (exp);
+ int type = TREE_CODE_CLASS (code);
+ int length = tree_code_length[(int) code];
+
+ /* If this code is langauge-specific, we don't know what it will do. */
+ if ((int) code >= NUM_TREE_CODES)
+ return 1;
+
+ /* Only expressions and references can contain calls. */
+ if (type != 'e' && type != '<' && type != '1' && type != '2' && type != 'r'
+ && type != 'b')
+ return 0;
+
+ switch (code)
+ {
+ case CALL_EXPR:
+ if (which == 0)
+ return 1;
+ else if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
+ && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
+ == FUNCTION_DECL))
+ {
+ tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+
+ if ((DECL_BUILT_IN (fndecl)
+ && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_ALLOCA)
+ || (DECL_SAVED_INSNS (fndecl)
+ && (FUNCTION_FLAGS (DECL_SAVED_INSNS (fndecl))
+ & FUNCTION_FLAGS_CALLS_ALLOCA)))
+ return 1;
+ }
+
+ /* Third operand is RTL. */
+ length = 2;
+ break;
+
+ case SAVE_EXPR:
+ if (SAVE_EXPR_RTL (exp) != 0)
+ return 0;
+ if (value_member (exp, calls_function_save_exprs))
+ return 0;
+ calls_function_save_exprs = tree_cons (NULL_TREE, exp,
+ calls_function_save_exprs);
+ return (TREE_OPERAND (exp, 0) != 0
+ && calls_function_1 (TREE_OPERAND (exp, 0), which));
+
+ case BLOCK:
+ {
+ register tree local;
+
+ for (local = BLOCK_VARS (exp); local; local = TREE_CHAIN (local))
+ if (DECL_INITIAL (local) != 0
+ && calls_function_1 (DECL_INITIAL (local), which))
+ return 1;
+ }
+ {
+ register tree subblock;
+
+ for (subblock = BLOCK_SUBBLOCKS (exp);
+ subblock;
+ subblock = TREE_CHAIN (subblock))
+ if (calls_function_1 (subblock, which))
+ return 1;
+ }
+ return 0;
+
+ case METHOD_CALL_EXPR:
+ length = 3;
+ break;
+
+ case WITH_CLEANUP_EXPR:
+ length = 1;
+ break;
+
+ case RTL_EXPR:
+ return 0;
+ }
+
+ for (i = 0; i < length; i++)
+ if (TREE_OPERAND (exp, i) != 0
+ && calls_function_1 (TREE_OPERAND (exp, i), which))
+ return 1;
+
+ return 0;
+}
+
+/* Force FUNEXP into a form suitable for the address of a CALL,
+ and return that as an rtx. Also load the static chain register
+ if FNDECL is a nested function.
+
+ CALL_FUSAGE points to a variable holding the prospective
+ CALL_INSN_FUNCTION_USAGE information. */
+
+rtx
+prepare_call_address (funexp, fndecl, call_fusage, reg_parm_seen)
+ rtx funexp;
+ tree fndecl;
+ rtx *call_fusage;
+ int reg_parm_seen;
+{
+ rtx static_chain_value = 0;
+
+ funexp = protect_from_queue (funexp, 0);
+
+ if (fndecl != 0)
+ /* Get possible static chain value for nested function in C. */
+ static_chain_value = lookup_static_chain (fndecl);
+
+ /* Make a valid memory address and copy constants thru pseudo-regs,
+ but not for a constant address if -fno-function-cse. */
+ if (GET_CODE (funexp) != SYMBOL_REF)
+ funexp =
+#ifdef SMALL_REGISTER_CLASSES
+ /* If we are using registers for parameters, force the
+ function address into a register now. */
+ reg_parm_seen ? force_not_mem (memory_address (FUNCTION_MODE, funexp))
+ :
+#endif
+ memory_address (FUNCTION_MODE, funexp);
+ else
+ {
+#ifndef NO_FUNCTION_CSE
+ if (optimize && ! flag_no_function_cse)
+#ifdef NO_RECURSIVE_FUNCTION_CSE
+ if (fndecl != current_function_decl)
+#endif
+ funexp = force_reg (Pmode, funexp);
+#endif
+ }
+
+ if (static_chain_value != 0)
+ {
+ emit_move_insn (static_chain_rtx, static_chain_value);
+
+ use_reg (call_fusage, static_chain_rtx);
+ }
+
+ return funexp;
+}
+
+/* Generate instructions to call function FUNEXP,
+ and optionally pop the results.
+ The CALL_INSN is the first insn generated.
+
+ FUNTYPE is the data type of the function, or, for a library call,
+ the identifier for the name of the call. This is given to the
+ macro RETURN_POPS_ARGS to determine whether this function pops its own args.
+
+ STACK_SIZE is the number of bytes of arguments on the stack,
+ rounded up to STACK_BOUNDARY; zero if the size is variable.
+ This is both to put into the call insn and
+ to generate explicit popping code if necessary.
+
+ STRUCT_VALUE_SIZE is the number of bytes wanted in a structure value.
+ It is zero if this call doesn't want a structure value.
+
+ NEXT_ARG_REG is the rtx that results from executing
+ FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1)
+ just after all the args have had their registers assigned.
+ This could be whatever you like, but normally it is the first
+ arg-register beyond those used for args in this call,
+ or 0 if all the arg-registers are used in this call.
+ It is passed on to `gen_call' so you can put this info in the call insn.
+
+ VALREG is a hard register in which a value is returned,
+ or 0 if the call does not return a value.
+
+ OLD_INHIBIT_DEFER_POP is the value that `inhibit_defer_pop' had before
+ the args to this call were processed.
+ We restore `inhibit_defer_pop' to that value.
+
+ CALL_FUSAGE is either empty or an EXPR_LIST of USE expressions that
+ denote registers used by the called function.
+
+ IS_CONST is true if this is a `const' call. */
+
+static void
+emit_call_1 (funexp, funtype, stack_size, struct_value_size, next_arg_reg,
+ valreg, old_inhibit_defer_pop, call_fusage, is_const)
+ rtx funexp;
+ tree funtype;
+ int stack_size;
+ int struct_value_size;
+ rtx next_arg_reg;
+ rtx valreg;
+ int old_inhibit_defer_pop;
+ rtx call_fusage;
+ int is_const;
+{
+ rtx stack_size_rtx = GEN_INT (stack_size);
+ rtx struct_value_size_rtx = GEN_INT (struct_value_size);
+ rtx call_insn;
+ int already_popped = 0;
+
+ /* Ensure address is valid. SYMBOL_REF is already valid, so no need,
+ and we don't want to load it into a register as an optimization,
+ because prepare_call_address already did it if it should be done. */
+ if (GET_CODE (funexp) != SYMBOL_REF)
+ funexp = memory_address (FUNCTION_MODE, funexp);
+
+#ifndef ACCUMULATE_OUTGOING_ARGS
+#if defined (HAVE_call_pop) && defined (HAVE_call_value_pop)
+ if (HAVE_call_pop && HAVE_call_value_pop
+ && (RETURN_POPS_ARGS (funtype, stack_size) > 0 || stack_size == 0))
+ {
+ rtx n_pop = GEN_INT (RETURN_POPS_ARGS (funtype, stack_size));
+ rtx pat;
+
+ /* If this subroutine pops its own args, record that in the call insn
+ if possible, for the sake of frame pointer elimination. */
+ if (valreg)
+ pat = gen_call_value_pop (valreg,
+ gen_rtx (MEM, FUNCTION_MODE, funexp),
+ stack_size_rtx, next_arg_reg, n_pop);
+ else
+ pat = gen_call_pop (gen_rtx (MEM, FUNCTION_MODE, funexp),
+ stack_size_rtx, next_arg_reg, n_pop);
+
+ emit_call_insn (pat);
+ already_popped = 1;
+ }
+ else
+#endif
+#endif
+
+#if defined (HAVE_call) && defined (HAVE_call_value)
+ if (HAVE_call && HAVE_call_value)
+ {
+ if (valreg)
+ emit_call_insn (gen_call_value (valreg,
+ gen_rtx (MEM, FUNCTION_MODE, funexp),
+ stack_size_rtx, next_arg_reg,
+ NULL_RTX));
+ else
+ emit_call_insn (gen_call (gen_rtx (MEM, FUNCTION_MODE, funexp),
+ stack_size_rtx, next_arg_reg,
+ struct_value_size_rtx));
+ }
+ else
+#endif
+ abort ();
+
+ /* Find the CALL insn we just emitted. */
+ for (call_insn = get_last_insn ();
+ call_insn && GET_CODE (call_insn) != CALL_INSN;
+ call_insn = PREV_INSN (call_insn))
+ ;
+
+ if (! call_insn)
+ abort ();
+
+ /* Put the register usage information on the CALL. If there is already
+ some usage information, put ours at the end. */
+ if (CALL_INSN_FUNCTION_USAGE (call_insn))
+ {
+ rtx link;
+
+ for (link = CALL_INSN_FUNCTION_USAGE (call_insn); XEXP (link, 1) != 0;
+ link = XEXP (link, 1))
+ ;
+
+ XEXP (link, 1) = call_fusage;
+ }
+ else
+ CALL_INSN_FUNCTION_USAGE (call_insn) = call_fusage;
+
+ /* If this is a const call, then set the insn's unchanging bit. */
+ if (is_const)
+ CONST_CALL_P (call_insn) = 1;
+
+ /* Restore this now, so that we do defer pops for this call's args
+ if the context of the call as a whole permits. */
+ inhibit_defer_pop = old_inhibit_defer_pop;
+
+#ifndef ACCUMULATE_OUTGOING_ARGS
+ /* If returning from the subroutine does not automatically pop the args,
+ we need an instruction to pop them sooner or later.
+ Perhaps do it now; perhaps just record how much space to pop later.
+
+ If returning from the subroutine does pop the args, indicate that the
+ stack pointer will be changed. */
+
+ if (stack_size != 0 && RETURN_POPS_ARGS (funtype, stack_size) > 0)
+ {
+ if (!already_popped)
+ CALL_INSN_FUNCTION_USAGE (call_insn) =
+ gen_rtx (EXPR_LIST, CLOBBER, stack_pointer_rtx,
+ CALL_INSN_FUNCTION_USAGE (call_insn));
+ stack_size -= RETURN_POPS_ARGS (funtype, stack_size);
+ stack_size_rtx = GEN_INT (stack_size);
+ }
+
+ if (stack_size != 0)
+ {
+ if (flag_defer_pop && inhibit_defer_pop == 0 && !is_const)
+ pending_stack_adjust += stack_size;
+ else
+ adjust_stack (stack_size_rtx);
+ }
+#endif
+}
+
+/* Generate all the code for a function call
+ and return an rtx for its value.
+ Store the value in TARGET (specified as an rtx) if convenient.
+ If the value is stored in TARGET then TARGET is returned.
+ If IGNORE is nonzero, then we ignore the value of the function call. */
+
+rtx
+expand_call (exp, target, ignore)
+ tree exp;
+ rtx target;
+ int ignore;
+{
+ /* List of actual parameters. */
+ tree actparms = TREE_OPERAND (exp, 1);
+ /* RTX for the function to be called. */
+ rtx funexp;
+ /* Tree node for the function to be called (not the address!). */
+ tree funtree;
+ /* Data type of the function. */
+ tree funtype;
+ /* Declaration of the function being called,
+ or 0 if the function is computed (not known by name). */
+ tree fndecl = 0;
+ char *name = 0;
+
+ /* Register in which non-BLKmode value will be returned,
+ or 0 if no value or if value is BLKmode. */
+ rtx valreg;
+ /* Address where we should return a BLKmode value;
+ 0 if value not BLKmode. */
+ rtx structure_value_addr = 0;
+ /* Nonzero if that address is being passed by treating it as
+ an extra, implicit first parameter. Otherwise,
+ it is passed by being copied directly into struct_value_rtx. */
+ int structure_value_addr_parm = 0;
+ /* Size of aggregate value wanted, or zero if none wanted
+ or if we are using the non-reentrant PCC calling convention
+ or expecting the value in registers. */
+ int struct_value_size = 0;
+ /* Nonzero if called function returns an aggregate in memory PCC style,
+ by returning the address of where to find it. */
+ int pcc_struct_value = 0;
+
+ /* Number of actual parameters in this call, including struct value addr. */
+ int num_actuals;
+ /* Number of named args. Args after this are anonymous ones
+ and they must all go on the stack. */
+ int n_named_args;
+ /* Count arg position in order args appear. */
+ int argpos;
+
+ /* Vector of information about each argument.
+ Arguments are numbered in the order they will be pushed,
+ not the order they are written. */
+ struct arg_data *args;
+
+ /* Total size in bytes of all the stack-parms scanned so far. */
+ struct args_size args_size;
+ /* Size of arguments before any adjustments (such as rounding). */
+ struct args_size original_args_size;
+ /* Data on reg parms scanned so far. */
+ CUMULATIVE_ARGS args_so_far;
+ /* Nonzero if a reg parm has been scanned. */
+ int reg_parm_seen;
+ /* Nonzero if this is an indirect function call. */
+ int current_call_is_indirect = 0;
+
+ /* Nonzero if we must avoid push-insns in the args for this call.
+ If stack space is allocated for register parameters, but not by the
+ caller, then it is preallocated in the fixed part of the stack frame.
+ So the entire argument block must then be preallocated (i.e., we
+ ignore PUSH_ROUNDING in that case). */
+
+#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE)
+ int must_preallocate = 1;
+#else
+#ifdef PUSH_ROUNDING
+ int must_preallocate = 0;
+#else
+ int must_preallocate = 1;
+#endif
+#endif
+
+ /* Size of the stack reserved for parameter registers. */
+ int reg_parm_stack_space = 0;
+
+ /* 1 if scanning parms front to back, -1 if scanning back to front. */
+ int inc;
+ /* Address of space preallocated for stack parms
+ (on machines that lack push insns), or 0 if space not preallocated. */
+ rtx argblock = 0;
+
+ /* Nonzero if it is plausible that this is a call to alloca. */
+ int may_be_alloca;
+ /* Nonzero if this is a call to setjmp or a related function. */
+ int returns_twice;
+ /* Nonzero if this is a call to `longjmp'. */
+ int is_longjmp;
+ /* Nonzero if this is a call to an inline function. */
+ int is_integrable = 0;
+ /* Nonzero if this is a call to a `const' function.
+ Note that only explicitly named functions are handled as `const' here. */
+ int is_const = 0;
+ /* Nonzero if this is a call to a `volatile' function. */
+ int is_volatile = 0;
+#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
+ /* Define the boundary of the register parm stack space that needs to be
+ save, if any. */
+ int low_to_save = -1, high_to_save;
+ rtx save_area = 0; /* Place that it is saved */
+#endif
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
+ char *initial_stack_usage_map = stack_usage_map;
+#endif
+
+ rtx old_stack_level = 0;
+ int old_pending_adj = 0;
+ int old_stack_arg_under_construction;
+ int old_inhibit_defer_pop = inhibit_defer_pop;
+ tree old_cleanups = cleanups_this_call;
+ rtx call_fusage = 0;
+ register tree p;
+ register int i, j;
+
+ /* See if we can find a DECL-node for the actual function.
+ As a result, decide whether this is a call to an integrable function. */
+
+ p = TREE_OPERAND (exp, 0);
+ if (TREE_CODE (p) == ADDR_EXPR)
+ {
+ fndecl = TREE_OPERAND (p, 0);
+ if (TREE_CODE (fndecl) != FUNCTION_DECL)
+ fndecl = 0;
+ else
+ {
+ if (!flag_no_inline
+ && fndecl != current_function_decl
+ && DECL_SAVED_INSNS (fndecl))
+ is_integrable = 1;
+ else if (! TREE_ADDRESSABLE (fndecl))
+ {
+ /* In case this function later becomes inlinable,
+ record that there was already a non-inline call to it.
+
+ Use abstraction instead of setting TREE_ADDRESSABLE
+ directly. */
+ if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline)
+ warning_with_decl (fndecl, "can't inline call to `%s'");
+ mark_addressable (fndecl);
+ }
+
+ if (TREE_READONLY (fndecl) && ! TREE_THIS_VOLATILE (fndecl)
+ && TYPE_MODE (TREE_TYPE (exp)) != VOIDmode)
+ is_const = 1;
+
+ if (TREE_THIS_VOLATILE (fndecl))
+ is_volatile = 1;
+ }
+ }
+
+ /* If we don't have specific function to call, see if we have a
+ constant or `noreturn' function from the type. */
+ if (fndecl == 0)
+ {
+ is_const = TREE_READONLY (TREE_TYPE (TREE_TYPE (p)));
+ is_volatile = TREE_THIS_VOLATILE (TREE_TYPE (TREE_TYPE (p)));
+ }
+
+#ifdef REG_PARM_STACK_SPACE
+#ifdef MAYBE_REG_PARM_STACK_SPACE
+ reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
+#else
+ reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
+#endif
+#endif
+
+ /* Warn if this value is an aggregate type,
+ regardless of which calling convention we are using for it. */
+ if (warn_aggregate_return && AGGREGATE_TYPE_P (TREE_TYPE (exp)))
+ warning ("function call has aggregate value");
+
+ /* Set up a place to return a structure. */
+
+ /* Cater to broken compilers. */
+ if (aggregate_value_p (exp))
+ {
+ /* This call returns a big structure. */
+ is_const = 0;
+
+#ifdef PCC_STATIC_STRUCT_RETURN
+ {
+ pcc_struct_value = 1;
+ /* Easier than making that case work right. */
+ if (is_integrable)
+ {
+ /* In case this is a static function, note that it has been
+ used. */
+ if (! TREE_ADDRESSABLE (fndecl))
+ mark_addressable (fndecl);
+ is_integrable = 0;
+ }
+ }
+#else /* not PCC_STATIC_STRUCT_RETURN */
+ {
+ struct_value_size = int_size_in_bytes (TREE_TYPE (exp));
+
+ if (struct_value_size < 0)
+ abort ();
+
+ if (target && GET_CODE (target) == MEM)
+ structure_value_addr = XEXP (target, 0);
+ else
+ {
+ /* Assign a temporary on the stack to hold the value. */
+
+ /* For variable-sized objects, we must be called with a target
+ specified. If we were to allocate space on the stack here,
+ we would have no way of knowing when to free it. */
+
+ structure_value_addr
+ = XEXP (assign_stack_temp (BLKmode, struct_value_size, 1), 0);
+ target = 0;
+ }
+ }
+#endif /* not PCC_STATIC_STRUCT_RETURN */
+ }
+
+ /* If called function is inline, try to integrate it. */
+
+ if (is_integrable)
+ {
+ rtx temp;
+ rtx before_call = get_last_insn ();
+
+ temp = expand_inline_function (fndecl, actparms, target,
+ ignore, TREE_TYPE (exp),
+ structure_value_addr);
+
+ /* If inlining succeeded, return. */
+ if ((HOST_WIDE_INT) temp != -1)
+ {
+ if (flag_short_temps)
+ {
+ /* Perform all cleanups needed for the arguments of this
+ call (i.e. destructors in C++). It is ok if these
+ destructors clobber RETURN_VALUE_REG, because the
+ only time we care about this is when TARGET is that
+ register. But in C++, we take care to never return
+ that register directly. */
+ expand_cleanups_to (old_cleanups);
+ }
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ /* If the outgoing argument list must be preserved, push
+ the stack before executing the inlined function if it
+ makes any calls. */
+
+ for (i = reg_parm_stack_space - 1; i >= 0; i--)
+ if (i < highest_outgoing_arg_in_use && stack_usage_map[i] != 0)
+ break;
+
+ if (stack_arg_under_construction || i >= 0)
+ {
+ rtx insn = NEXT_INSN (before_call), seq;
+
+ /* Look for a call in the inline function code.
+ If OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl)) is
+ nonzero then there is a call and it is not necessary
+ to scan the insns. */
+
+ if (OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl)) == 0)
+ for (; insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == CALL_INSN)
+ break;
+
+ if (insn)
+ {
+ /* Reserve enough stack space so that the largest
+ argument list of any function call in the inline
+ function does not overlap the argument list being
+ evaluated. This is usually an overestimate because
+ allocate_dynamic_stack_space reserves space for an
+ outgoing argument list in addition to the requested
+ space, but there is no way to ask for stack space such
+ that an argument list of a certain length can be
+ safely constructed. */
+
+ int adjust = OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl));
+#ifdef REG_PARM_STACK_SPACE
+ /* Add the stack space reserved for register arguments
+ in the inline function. What is really needed is the
+ largest value of reg_parm_stack_space in the inline
+ function, but that is not available. Using the current
+ value of reg_parm_stack_space is wrong, but gives
+ correct results on all supported machines. */
+ adjust += reg_parm_stack_space;
+#endif
+ start_sequence ();
+ emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
+ allocate_dynamic_stack_space (GEN_INT (adjust),
+ NULL_RTX, BITS_PER_UNIT);
+ seq = get_insns ();
+ end_sequence ();
+ emit_insns_before (seq, NEXT_INSN (before_call));
+ emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
+ }
+ }
+#endif
+
+ /* If the result is equivalent to TARGET, return TARGET to simplify
+ checks in store_expr. They can be equivalent but not equal in the
+ case of a function that returns BLKmode. */
+ if (temp != target && rtx_equal_p (temp, target))
+ return target;
+ return temp;
+ }
+
+ /* If inlining failed, mark FNDECL as needing to be compiled
+ separately after all. If function was declared inline,
+ give a warning. */
+ if (DECL_INLINE (fndecl) && warn_inline && !flag_no_inline
+ && ! TREE_ADDRESSABLE (fndecl))
+ warning_with_decl (fndecl, "can't inline call to `%s'");
+ mark_addressable (fndecl);
+ }
+
+ /* When calling a const function, we must pop the stack args right away,
+ so that the pop is deleted or moved with the call. */
+ if (is_const)
+ NO_DEFER_POP;
+
+ function_call_count++;
+
+ if (fndecl && DECL_NAME (fndecl))
+ name = IDENTIFIER_POINTER (DECL_NAME (fndecl));
+
+ /* On some machines (such as the PA) indirect calls have a different
+ calling convention than normal calls. FUNCTION_ARG in the target
+ description can look at current_call_is_indirect to determine which
+ calling convention to use. */
+ current_call_is_indirect = (fndecl == 0);
+#if 0
+ = TREE_CODE (TREE_OPERAND (exp, 0)) == NON_LVALUE_EXPR ? 1 : 0;
+#endif
+
+#if 0
+ /* Unless it's a call to a specific function that isn't alloca,
+ if it has one argument, we must assume it might be alloca. */
+
+ may_be_alloca =
+ (!(fndecl != 0 && strcmp (name, "alloca"))
+ && actparms != 0
+ && TREE_CHAIN (actparms) == 0);
+#else
+ /* We assume that alloca will always be called by name. It
+ makes no sense to pass it as a pointer-to-function to
+ anything that does not understand its behavior. */
+ may_be_alloca =
+ (name && ((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 6
+ && name[0] == 'a'
+ && ! strcmp (name, "alloca"))
+ || (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 16
+ && name[0] == '_'
+ && ! strcmp (name, "__builtin_alloca"))));
+#endif
+
+ /* See if this is a call to a function that can return more than once
+ or a call to longjmp. */
+
+ returns_twice = 0;
+ is_longjmp = 0;
+
+ if (name != 0 && IDENTIFIER_LENGTH (DECL_NAME (fndecl)) <= 15)
+ {
+ char *tname = name;
+
+ /* Disregard prefix _, __ or __x. */
+ if (name[0] == '_')
+ {
+ if (name[1] == '_' && name[2] == 'x')
+ tname += 3;
+ else if (name[1] == '_')
+ tname += 2;
+ else
+ tname += 1;
+ }
+
+ if (tname[0] == 's')
+ {
+ returns_twice
+ = ((tname[1] == 'e'
+ && (! strcmp (tname, "setjmp")
+ || ! strcmp (tname, "setjmp_syscall")))
+ || (tname[1] == 'i'
+ && ! strcmp (tname, "sigsetjmp"))
+ || (tname[1] == 'a'
+ && ! strcmp (tname, "savectx")));
+ if (tname[1] == 'i'
+ && ! strcmp (tname, "siglongjmp"))
+ is_longjmp = 1;
+ }
+ else if ((tname[0] == 'q' && tname[1] == 's'
+ && ! strcmp (tname, "qsetjmp"))
+ || (tname[0] == 'v' && tname[1] == 'f'
+ && ! strcmp (tname, "vfork")))
+ returns_twice = 1;
+
+ else if (tname[0] == 'l' && tname[1] == 'o'
+ && ! strcmp (tname, "longjmp"))
+ is_longjmp = 1;
+ }
+
+ if (may_be_alloca)
+ current_function_calls_alloca = 1;
+
+ /* Don't let pending stack adjusts add up to too much.
+ Also, do all pending adjustments now
+ if there is any chance this might be a call to alloca. */
+
+ if (pending_stack_adjust >= 32
+ || (pending_stack_adjust > 0 && may_be_alloca))
+ do_pending_stack_adjust ();
+
+ /* Operand 0 is a pointer-to-function; get the type of the function. */
+ funtype = TREE_TYPE (TREE_OPERAND (exp, 0));
+ if (TREE_CODE (funtype) != POINTER_TYPE)
+ abort ();
+ funtype = TREE_TYPE (funtype);
+
+ /* Push the temporary stack slot level so that we can free any temporaries
+ we make. */
+ push_temp_slots ();
+
+ /* Start updating where the next arg would go. */
+ INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_RTX);
+
+ /* If struct_value_rtx is 0, it means pass the address
+ as if it were an extra parameter. */
+ if (structure_value_addr && struct_value_rtx == 0)
+ {
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ /* If the stack will be adjusted, make sure the structure address
+ does not refer to virtual_outgoing_args_rtx. */
+ rtx temp = (stack_arg_under_construction
+ ? copy_addr_to_reg (structure_value_addr)
+ : force_reg (Pmode, structure_value_addr));
+#else
+ rtx temp = force_reg (Pmode, structure_value_addr);
+#endif
+
+ actparms
+ = tree_cons (error_mark_node,
+ make_tree (build_pointer_type (TREE_TYPE (funtype)),
+ temp),
+ actparms);
+ structure_value_addr_parm = 1;
+ }
+
+ /* Count the arguments and set NUM_ACTUALS. */
+ for (p = actparms, i = 0; p; p = TREE_CHAIN (p)) i++;
+ num_actuals = i;
+
+ /* Compute number of named args.
+ Normally, don't include the last named arg if anonymous args follow.
+ (If no anonymous args follow, the result of list_length
+ is actually one too large.)
+
+ If SETUP_INCOMING_VARARGS is defined, this machine will be able to
+ place unnamed args that were passed in registers into the stack. So
+ treat all args as named. This allows the insns emitting for a specific
+ argument list to be independent of the function declaration.
+
+ If SETUP_INCOMING_VARARGS is not defined, we do not have any reliable
+ way to pass unnamed args in registers, so we must force them into
+ memory. */
+#ifndef SETUP_INCOMING_VARARGS
+ if (TYPE_ARG_TYPES (funtype) != 0)
+ n_named_args
+ = list_length (TYPE_ARG_TYPES (funtype)) - 1
+ /* Count the struct value address, if it is passed as a parm. */
+ + structure_value_addr_parm;
+ else
+#endif
+ /* If we know nothing, treat all args as named. */
+ n_named_args = num_actuals;
+
+ /* Make a vector to hold all the information about each arg. */
+ args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data));
+ bzero ((char *) args, num_actuals * sizeof (struct arg_data));
+
+ args_size.constant = 0;
+ args_size.var = 0;
+
+ /* In this loop, we consider args in the order they are written.
+ We fill up ARGS from the front of from the back if necessary
+ so that in any case the first arg to be pushed ends up at the front. */
+
+#ifdef PUSH_ARGS_REVERSED
+ i = num_actuals - 1, inc = -1;
+ /* In this case, must reverse order of args
+ so that we compute and push the last arg first. */
+#else
+ i = 0, inc = 1;
+#endif
+
+ /* I counts args in order (to be) pushed; ARGPOS counts in order written. */
+ for (p = actparms, argpos = 0; p; p = TREE_CHAIN (p), i += inc, argpos++)
+ {
+ tree type = TREE_TYPE (TREE_VALUE (p));
+ int unsignedp;
+ enum machine_mode mode;
+
+ args[i].tree_value = TREE_VALUE (p);
+
+ /* Replace erroneous argument with constant zero. */
+ if (type == error_mark_node || TYPE_SIZE (type) == 0)
+ args[i].tree_value = integer_zero_node, type = integer_type_node;
+
+ /* Decide where to pass this arg.
+
+ args[i].reg is nonzero if all or part is passed in registers.
+
+ args[i].partial is nonzero if part but not all is passed in registers,
+ and the exact value says how many words are passed in registers.
+
+ args[i].pass_on_stack is nonzero if the argument must at least be
+ computed on the stack. It may then be loaded back into registers
+ if args[i].reg is nonzero.
+
+ These decisions are driven by the FUNCTION_... macros and must agree
+ with those made by function.c. */
+
+ /* See if this argument should be passed by invisible reference. */
+ if ((TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
+ && contains_placeholder_p (TYPE_SIZE (type)))
+ || TYPE_NEEDS_CONSTRUCTING (type)
+#ifdef FUNCTION_ARG_PASS_BY_REFERENCE
+ || FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, TYPE_MODE (type),
+ type, argpos < n_named_args)
+#endif
+ )
+ {
+#ifdef FUNCTION_ARG_CALLEE_COPIES
+ if (FUNCTION_ARG_CALLEE_COPIES (args_so_far, TYPE_MODE (type), type,
+ argpos < n_named_args)
+ /* If it's in a register, we must make a copy of it too. */
+ /* ??? Is this a sufficient test? Is there a better one? */
+ && !(TREE_CODE (args[i].tree_value) == VAR_DECL
+ && REG_P (DECL_RTL (args[i].tree_value))))
+ {
+ args[i].tree_value = build1 (ADDR_EXPR,
+ build_pointer_type (type),
+ args[i].tree_value);
+ type = build_pointer_type (type);
+ }
+ else
+#endif
+ {
+ /* We make a copy of the object and pass the address to the
+ function being called. */
+ rtx copy;
+
+ if (TYPE_SIZE (type) == 0
+ || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+ {
+ /* This is a variable-sized object. Make space on the stack
+ for it. */
+ rtx size_rtx = expr_size (TREE_VALUE (p));
+
+ if (old_stack_level == 0)
+ {
+ emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
+ old_pending_adj = pending_stack_adjust;
+ pending_stack_adjust = 0;
+ }
+
+ copy = gen_rtx (MEM, BLKmode,
+ allocate_dynamic_stack_space (size_rtx,
+ NULL_RTX,
+ TYPE_ALIGN (type)));
+ }
+ else
+ {
+ int size = int_size_in_bytes (type);
+ copy = assign_stack_temp (TYPE_MODE (type), size, 1);
+ }
+
+ MEM_IN_STRUCT_P (copy) = AGGREGATE_TYPE_P (type);
+
+ store_expr (args[i].tree_value, copy, 0);
+
+ args[i].tree_value = build1 (ADDR_EXPR,
+ build_pointer_type (type),
+ make_tree (type, copy));
+ type = build_pointer_type (type);
+ }
+ }
+
+ mode = TYPE_MODE (type);
+ unsignedp = TREE_UNSIGNED (type);
+
+#ifdef PROMOTE_FUNCTION_ARGS
+ mode = promote_mode (type, mode, &unsignedp, 1);
+#endif
+
+ args[i].unsignedp = unsignedp;
+ args[i].mode = mode;
+ args[i].reg = FUNCTION_ARG (args_so_far, mode, type,
+ argpos < n_named_args);
+#ifdef FUNCTION_ARG_PARTIAL_NREGS
+ if (args[i].reg)
+ args[i].partial
+ = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, type,
+ argpos < n_named_args);
+#endif
+
+ args[i].pass_on_stack = MUST_PASS_IN_STACK (mode, type);
+
+ /* If FUNCTION_ARG returned an (expr_list (nil) FOO), it means that
+ we are to pass this arg in the register(s) designated by FOO, but
+ also to pass it in the stack. */
+ if (args[i].reg && GET_CODE (args[i].reg) == EXPR_LIST
+ && XEXP (args[i].reg, 0) == 0)
+ args[i].pass_on_stack = 1, args[i].reg = XEXP (args[i].reg, 1);
+
+ /* If this is an addressable type, we must preallocate the stack
+ since we must evaluate the object into its final location.
+
+ If this is to be passed in both registers and the stack, it is simpler
+ to preallocate. */
+ if (TREE_ADDRESSABLE (type)
+ || (args[i].pass_on_stack && args[i].reg != 0))
+ must_preallocate = 1;
+
+ /* If this is an addressable type, we cannot pre-evaluate it. Thus,
+ we cannot consider this function call constant. */
+ if (TREE_ADDRESSABLE (type))
+ is_const = 0;
+
+ /* Compute the stack-size of this argument. */
+ if (args[i].reg == 0 || args[i].partial != 0
+#ifdef REG_PARM_STACK_SPACE
+ || reg_parm_stack_space > 0
+#endif
+ || args[i].pass_on_stack)
+ locate_and_pad_parm (mode, type,
+#ifdef STACK_PARMS_IN_REG_PARM_AREA
+ 1,
+#else
+ args[i].reg != 0,
+#endif
+ fndecl, &args_size, &args[i].offset,
+ &args[i].size);
+
+#ifndef ARGS_GROW_DOWNWARD
+ args[i].slot_offset = args_size;
+#endif
+
+#ifndef REG_PARM_STACK_SPACE
+ /* If a part of the arg was put into registers,
+ don't include that part in the amount pushed. */
+ if (! args[i].pass_on_stack)
+ args[i].size.constant -= ((args[i].partial * UNITS_PER_WORD)
+ / (PARM_BOUNDARY / BITS_PER_UNIT)
+ * (PARM_BOUNDARY / BITS_PER_UNIT));
+#endif
+
+ /* Update ARGS_SIZE, the total stack space for args so far. */
+
+ args_size.constant += args[i].size.constant;
+ if (args[i].size.var)
+ {
+ ADD_PARM_SIZE (args_size, args[i].size.var);
+ }
+
+ /* Since the slot offset points to the bottom of the slot,
+ we must record it after incrementing if the args grow down. */
+#ifdef ARGS_GROW_DOWNWARD
+ args[i].slot_offset = args_size;
+
+ args[i].slot_offset.constant = -args_size.constant;
+ if (args_size.var)
+ {
+ SUB_PARM_SIZE (args[i].slot_offset, args_size.var);
+ }
+#endif
+
+ /* Increment ARGS_SO_FAR, which has info about which arg-registers
+ have been used, etc. */
+
+ FUNCTION_ARG_ADVANCE (args_so_far, TYPE_MODE (type), type,
+ argpos < n_named_args);
+ }
+
+#ifdef FINAL_REG_PARM_STACK_SPACE
+ reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
+ args_size.var);
+#endif
+
+ /* Compute the actual size of the argument block required. The variable
+ and constant sizes must be combined, the size may have to be rounded,
+ and there may be a minimum required size. */
+
+ original_args_size = args_size;
+ if (args_size.var)
+ {
+ /* If this function requires a variable-sized argument list, don't try to
+ make a cse'able block for this call. We may be able to do this
+ eventually, but it is too complicated to keep track of what insns go
+ in the cse'able block and which don't. */
+
+ is_const = 0;
+ must_preallocate = 1;
+
+ args_size.var = ARGS_SIZE_TREE (args_size);
+ args_size.constant = 0;
+
+#ifdef STACK_BOUNDARY
+ if (STACK_BOUNDARY != BITS_PER_UNIT)
+ args_size.var = round_up (args_size.var, STACK_BYTES);
+#endif
+
+#ifdef REG_PARM_STACK_SPACE
+ if (reg_parm_stack_space > 0)
+ {
+ args_size.var
+ = size_binop (MAX_EXPR, args_size.var,
+ size_int (REG_PARM_STACK_SPACE (fndecl)));
+
+#ifndef OUTGOING_REG_PARM_STACK_SPACE
+ /* The area corresponding to register parameters is not to count in
+ the size of the block we need. So make the adjustment. */
+ args_size.var
+ = size_binop (MINUS_EXPR, args_size.var,
+ size_int (reg_parm_stack_space));
+#endif
+ }
+#endif
+ }
+ else
+ {
+#ifdef STACK_BOUNDARY
+ args_size.constant = (((args_size.constant + (STACK_BYTES - 1))
+ / STACK_BYTES) * STACK_BYTES);
+#endif
+
+#ifdef REG_PARM_STACK_SPACE
+ args_size.constant = MAX (args_size.constant,
+ reg_parm_stack_space);
+#ifdef MAYBE_REG_PARM_STACK_SPACE
+ if (reg_parm_stack_space == 0)
+ args_size.constant = 0;
+#endif
+#ifndef OUTGOING_REG_PARM_STACK_SPACE
+ args_size.constant -= reg_parm_stack_space;
+#endif
+#endif
+ }
+
+ /* See if we have or want to preallocate stack space.
+
+ If we would have to push a partially-in-regs parm
+ before other stack parms, preallocate stack space instead.
+
+ If the size of some parm is not a multiple of the required stack
+ alignment, we must preallocate.
+
+ If the total size of arguments that would otherwise create a copy in
+ a temporary (such as a CALL) is more than half the total argument list
+ size, preallocation is faster.
+
+ Another reason to preallocate is if we have a machine (like the m88k)
+ where stack alignment is required to be maintained between every
+ pair of insns, not just when the call is made. However, we assume here
+ that such machines either do not have push insns (and hence preallocation
+ would occur anyway) or the problem is taken care of with
+ PUSH_ROUNDING. */
+
+ if (! must_preallocate)
+ {
+ int partial_seen = 0;
+ int copy_to_evaluate_size = 0;
+
+ for (i = 0; i < num_actuals && ! must_preallocate; i++)
+ {
+ if (args[i].partial > 0 && ! args[i].pass_on_stack)
+ partial_seen = 1;
+ else if (partial_seen && args[i].reg == 0)
+ must_preallocate = 1;
+
+ if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
+ && (TREE_CODE (args[i].tree_value) == CALL_EXPR
+ || TREE_CODE (args[i].tree_value) == TARGET_EXPR
+ || TREE_CODE (args[i].tree_value) == COND_EXPR
+ || TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value))))
+ copy_to_evaluate_size
+ += int_size_in_bytes (TREE_TYPE (args[i].tree_value));
+ }
+
+ if (copy_to_evaluate_size * 2 >= args_size.constant
+ && args_size.constant > 0)
+ must_preallocate = 1;
+ }
+
+ /* If the structure value address will reference the stack pointer, we must
+ stabilize it. We don't need to do this if we know that we are not going
+ to adjust the stack pointer in processing this call. */
+
+ if (structure_value_addr
+ && (reg_mentioned_p (virtual_stack_dynamic_rtx, structure_value_addr)
+ || reg_mentioned_p (virtual_outgoing_args_rtx, structure_value_addr))
+ && (args_size.var
+#ifndef ACCUMULATE_OUTGOING_ARGS
+ || args_size.constant
+#endif
+ ))
+ structure_value_addr = copy_to_reg (structure_value_addr);
+
+ /* If this function call is cse'able, precompute all the parameters.
+ Note that if the parameter is constructed into a temporary, this will
+ cause an additional copy because the parameter will be constructed
+ into a temporary location and then copied into the outgoing arguments.
+ If a parameter contains a call to alloca and this function uses the
+ stack, precompute the parameter. */
+
+ /* If we preallocated the stack space, and some arguments must be passed
+ on the stack, then we must precompute any parameter which contains a
+ function call which will store arguments on the stack.
+ Otherwise, evaluating the parameter may clobber previous parameters
+ which have already been stored into the stack. */
+
+ for (i = 0; i < num_actuals; i++)
+ if (is_const
+ || ((args_size.var != 0 || args_size.constant != 0)
+ && calls_function (args[i].tree_value, 1))
+ || (must_preallocate && (args_size.var != 0 || args_size.constant != 0)
+ && calls_function (args[i].tree_value, 0)))
+ {
+ push_temp_slots ();
+
+ args[i].initial_value = args[i].value
+ = expand_expr (args[i].tree_value, NULL_RTX, VOIDmode, 0);
+
+ if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) != args[i].mode)
+ args[i].value
+ = convert_modes (args[i].mode,
+ TYPE_MODE (TREE_TYPE (args[i].tree_value)),
+ args[i].value, args[i].unsignedp);
+
+ preserve_temp_slots (args[i].value);
+ pop_temp_slots ();
+
+ /* ANSI doesn't require a sequence point here,
+ but PCC has one, so this will avoid some problems. */
+ emit_queue ();
+ }
+
+ /* Now we are about to start emitting insns that can be deleted
+ if a libcall is deleted. */
+ if (is_const)
+ start_sequence ();
+
+ /* If we have no actual push instructions, or shouldn't use them,
+ make space for all args right now. */
+
+ if (args_size.var != 0)
+ {
+ if (old_stack_level == 0)
+ {
+ emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
+ old_pending_adj = pending_stack_adjust;
+ pending_stack_adjust = 0;
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ /* stack_arg_under_construction says whether a stack arg is
+ being constructed at the old stack level. Pushing the stack
+ gets a clean outgoing argument block. */
+ old_stack_arg_under_construction = stack_arg_under_construction;
+ stack_arg_under_construction = 0;
+#endif
+ }
+ argblock = push_block (ARGS_SIZE_RTX (args_size), 0, 0);
+ }
+ else if (must_preallocate)
+ {
+ /* Note that we must go through the motions of allocating an argument
+ block even if the size is zero because we may be storing args
+ in the area reserved for register arguments, which may be part of
+ the stack frame. */
+ int needed = args_size.constant;
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ /* Store the maximum argument space used. It will be pushed by the
+ prologue.
+
+ Since the stack pointer will never be pushed, it is possible for
+ the evaluation of a parm to clobber something we have already
+ written to the stack. Since most function calls on RISC machines
+ do not use the stack, this is uncommon, but must work correctly.
+
+ Therefore, we save any area of the stack that was already written
+ and that we are using. Here we set up to do this by making a new
+ stack usage map from the old one. The actual save will be done
+ by store_one_arg.
+
+ Another approach might be to try to reorder the argument
+ evaluations to avoid this conflicting stack usage. */
+
+ if (needed > current_function_outgoing_args_size)
+ current_function_outgoing_args_size = needed;
+
+#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE)
+ /* Since we will be writing into the entire argument area, the
+ map must be allocated for its entire size, not just the part that
+ is the responsibility of the caller. */
+ needed += reg_parm_stack_space;
+#endif
+
+#ifdef ARGS_GROW_DOWNWARD
+ highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
+ needed + 1);
+#else
+ highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use, needed);
+#endif
+ stack_usage_map = (char *) alloca (highest_outgoing_arg_in_use);
+
+ if (initial_highest_arg_in_use)
+ bcopy (initial_stack_usage_map, stack_usage_map,
+ initial_highest_arg_in_use);
+
+ if (initial_highest_arg_in_use != highest_outgoing_arg_in_use)
+ bzero (&stack_usage_map[initial_highest_arg_in_use],
+ highest_outgoing_arg_in_use - initial_highest_arg_in_use);
+ needed = 0;
+
+ /* The address of the outgoing argument list must not be copied to a
+ register here, because argblock would be left pointing to the
+ wrong place after the call to allocate_dynamic_stack_space below. */
+
+ argblock = virtual_outgoing_args_rtx;
+
+#else /* not ACCUMULATE_OUTGOING_ARGS */
+ if (inhibit_defer_pop == 0)
+ {
+ /* Try to reuse some or all of the pending_stack_adjust
+ to get this space. Maybe we can avoid any pushing. */
+ if (needed > pending_stack_adjust)
+ {
+ needed -= pending_stack_adjust;
+ pending_stack_adjust = 0;
+ }
+ else
+ {
+ pending_stack_adjust -= needed;
+ needed = 0;
+ }
+ }
+ /* Special case this because overhead of `push_block' in this
+ case is non-trivial. */
+ if (needed == 0)
+ argblock = virtual_outgoing_args_rtx;
+ else
+ argblock = push_block (GEN_INT (needed), 0, 0);
+
+ /* We only really need to call `copy_to_reg' in the case where push
+ insns are going to be used to pass ARGBLOCK to a function
+ call in ARGS. In that case, the stack pointer changes value
+ from the allocation point to the call point, and hence
+ the value of VIRTUAL_OUTGOING_ARGS_RTX changes as well.
+ But might as well always do it. */
+ argblock = copy_to_reg (argblock);
+#endif /* not ACCUMULATE_OUTGOING_ARGS */
+ }
+
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ /* The save/restore code in store_one_arg handles all cases except one:
+ a constructor call (including a C function returning a BLKmode struct)
+ to initialize an argument. */
+ if (stack_arg_under_construction)
+ {
+#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE)
+ rtx push_size = GEN_INT (reg_parm_stack_space + args_size.constant);
+#else
+ rtx push_size = GEN_INT (args_size.constant);
+#endif
+ if (old_stack_level == 0)
+ {
+ emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
+ old_pending_adj = pending_stack_adjust;
+ pending_stack_adjust = 0;
+ /* stack_arg_under_construction says whether a stack arg is
+ being constructed at the old stack level. Pushing the stack
+ gets a clean outgoing argument block. */
+ old_stack_arg_under_construction = stack_arg_under_construction;
+ stack_arg_under_construction = 0;
+ /* Make a new map for the new argument list. */
+ stack_usage_map = (char *)alloca (highest_outgoing_arg_in_use);
+ bzero (stack_usage_map, highest_outgoing_arg_in_use);
+ highest_outgoing_arg_in_use = 0;
+ }
+ allocate_dynamic_stack_space (push_size, NULL_RTX, BITS_PER_UNIT);
+ }
+ /* If argument evaluation might modify the stack pointer, copy the
+ address of the argument list to a register. */
+ for (i = 0; i < num_actuals; i++)
+ if (args[i].pass_on_stack)
+ {
+ argblock = copy_addr_to_reg (argblock);
+ break;
+ }
+#endif
+
+
+ /* If we preallocated stack space, compute the address of each argument.
+ We need not ensure it is a valid memory address here; it will be
+ validized when it is used. */
+ if (argblock)
+ {
+ rtx arg_reg = argblock;
+ int arg_offset = 0;
+
+ if (GET_CODE (argblock) == PLUS)
+ arg_reg = XEXP (argblock, 0), arg_offset = INTVAL (XEXP (argblock, 1));
+
+ for (i = 0; i < num_actuals; i++)
+ {
+ rtx offset = ARGS_SIZE_RTX (args[i].offset);
+ rtx slot_offset = ARGS_SIZE_RTX (args[i].slot_offset);
+ rtx addr;
+
+ /* Skip this parm if it will not be passed on the stack. */
+ if (! args[i].pass_on_stack && args[i].reg != 0)
+ continue;
+
+ if (GET_CODE (offset) == CONST_INT)
+ addr = plus_constant (arg_reg, INTVAL (offset));
+ else
+ addr = gen_rtx (PLUS, Pmode, arg_reg, offset);
+
+ addr = plus_constant (addr, arg_offset);
+ args[i].stack = gen_rtx (MEM, args[i].mode, addr);
+ MEM_IN_STRUCT_P (args[i].stack)
+ = AGGREGATE_TYPE_P (TREE_TYPE (args[i].tree_value));
+
+ if (GET_CODE (slot_offset) == CONST_INT)
+ addr = plus_constant (arg_reg, INTVAL (slot_offset));
+ else
+ addr = gen_rtx (PLUS, Pmode, arg_reg, slot_offset);
+
+ addr = plus_constant (addr, arg_offset);
+ args[i].stack_slot = gen_rtx (MEM, args[i].mode, addr);
+ }
+ }
+
+#ifdef PUSH_ARGS_REVERSED
+#ifdef STACK_BOUNDARY
+ /* If we push args individually in reverse order, perform stack alignment
+ before the first push (the last arg). */
+ if (argblock == 0)
+ anti_adjust_stack (GEN_INT (args_size.constant
+ - original_args_size.constant));
+#endif
+#endif
+
+ /* Don't try to defer pops if preallocating, not even from the first arg,
+ since ARGBLOCK probably refers to the SP. */
+ if (argblock)
+ NO_DEFER_POP;
+
+ /* Get the function to call, in the form of RTL. */
+ if (fndecl)
+ {
+ /* If this is the first use of the function, see if we need to
+ make an external definition for it. */
+ if (! TREE_USED (fndecl))
+ {
+ assemble_external (fndecl);
+ TREE_USED (fndecl) = 1;
+ }
+
+ /* Get a SYMBOL_REF rtx for the function address. */
+ funexp = XEXP (DECL_RTL (fndecl), 0);
+ }
+ else
+ /* Generate an rtx (probably a pseudo-register) for the address. */
+ {
+ push_temp_slots ();
+ funexp = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0);
+ pop_temp_slots (); /* FUNEXP can't be BLKmode */
+ emit_queue ();
+ }
+
+ /* Figure out the register where the value, if any, will come back. */
+ valreg = 0;
+ if (TYPE_MODE (TREE_TYPE (exp)) != VOIDmode
+ && ! structure_value_addr)
+ {
+ if (pcc_struct_value)
+ valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)),
+ fndecl);
+ else
+ valreg = hard_function_value (TREE_TYPE (exp), fndecl);
+ }
+
+ /* Precompute all register parameters. It isn't safe to compute anything
+ once we have started filling any specific hard regs. */
+ reg_parm_seen = 0;
+ for (i = 0; i < num_actuals; i++)
+ if (args[i].reg != 0 && ! args[i].pass_on_stack)
+ {
+ reg_parm_seen = 1;
+
+ if (args[i].value == 0)
+ {
+ push_temp_slots ();
+ args[i].value = expand_expr (args[i].tree_value, NULL_RTX,
+ VOIDmode, 0);
+ preserve_temp_slots (args[i].value);
+ pop_temp_slots ();
+
+ /* ANSI doesn't require a sequence point here,
+ but PCC has one, so this will avoid some problems. */
+ emit_queue ();
+ }
+
+ /* If we are to promote the function arg to a wider mode,
+ do it now. */
+
+ if (args[i].mode != TYPE_MODE (TREE_TYPE (args[i].tree_value)))
+ args[i].value
+ = convert_modes (args[i].mode,
+ TYPE_MODE (TREE_TYPE (args[i].tree_value)),
+ args[i].value, args[i].unsignedp);
+
+ /* If the value is expensive, and we are inside an appropriately
+ short loop, put the value into a pseudo and then put the pseudo
+ into the hard reg.
+
+ For small register classes, also do this if this call uses
+ register parameters. This is to avoid reload conflicts while
+ loading the parameters registers. */
+
+ if ((! (GET_CODE (args[i].value) == REG
+ || (GET_CODE (args[i].value) == SUBREG
+ && GET_CODE (SUBREG_REG (args[i].value)) == REG)))
+ && args[i].mode != BLKmode
+ && rtx_cost (args[i].value, SET) > 2
+#ifdef SMALL_REGISTER_CLASSES
+ && (reg_parm_seen || preserve_subexpressions_p ()))
+#else
+ && preserve_subexpressions_p ())
+#endif
+ args[i].value = copy_to_mode_reg (args[i].mode, args[i].value);
+ }
+
+#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
+ /* The argument list is the property of the called routine and it
+ may clobber it. If the fixed area has been used for previous
+ parameters, we must save and restore it.
+
+ Here we compute the boundary of the that needs to be saved, if any. */
+
+#ifdef ARGS_GROW_DOWNWARD
+ for (i = 0; i < reg_parm_stack_space + 1; i++)
+#else
+ for (i = 0; i < reg_parm_stack_space; i++)
+#endif
+ {
+ if (i >= highest_outgoing_arg_in_use
+ || stack_usage_map[i] == 0)
+ continue;
+
+ if (low_to_save == -1)
+ low_to_save = i;
+
+ high_to_save = i;
+ }
+
+ if (low_to_save >= 0)
+ {
+ int num_to_save = high_to_save - low_to_save + 1;
+ enum machine_mode save_mode
+ = mode_for_size (num_to_save * BITS_PER_UNIT, MODE_INT, 1);
+ rtx stack_area;
+
+ /* If we don't have the required alignment, must do this in BLKmode. */
+ if ((low_to_save & (MIN (GET_MODE_SIZE (save_mode),
+ BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1)))
+ save_mode = BLKmode;
+
+ stack_area = gen_rtx (MEM, save_mode,
+ memory_address (save_mode,
+
+#ifdef ARGS_GROW_DOWNWARD
+ plus_constant (argblock,
+ - high_to_save)
+#else
+ plus_constant (argblock,
+ low_to_save)
+#endif
+ ));
+ if (save_mode == BLKmode)
+ {
+ save_area = assign_stack_temp (BLKmode, num_to_save, 1);
+ emit_block_move (validize_mem (save_area), stack_area,
+ GEN_INT (num_to_save),
+ PARM_BOUNDARY / BITS_PER_UNIT);
+ }
+ else
+ {
+ save_area = gen_reg_rtx (save_mode);
+ emit_move_insn (save_area, stack_area);
+ }
+ }
+#endif
+
+
+ /* Now store (and compute if necessary) all non-register parms.
+ These come before register parms, since they can require block-moves,
+ which could clobber the registers used for register parms.
+ Parms which have partial registers are not stored here,
+ but we do preallocate space here if they want that. */
+
+ for (i = 0; i < num_actuals; i++)
+ if (args[i].reg == 0 || args[i].pass_on_stack)
+ store_one_arg (&args[i], argblock, may_be_alloca,
+ args_size.var != 0, fndecl, reg_parm_stack_space);
+
+#ifdef STRICT_ALIGNMENT
+ /* If we have a parm that is passed in registers but not in memory
+ and whose alignment does not permit a direct copy into registers,
+ make a group of pseudos that correspond to each register that we
+ will later fill. */
+
+ for (i = 0; i < num_actuals; i++)
+ if (args[i].reg != 0 && ! args[i].pass_on_stack
+ && args[i].mode == BLKmode
+ && (TYPE_ALIGN (TREE_TYPE (args[i].tree_value))
+ < MIN (BIGGEST_ALIGNMENT, BITS_PER_WORD)))
+ {
+ int bytes = int_size_in_bytes (TREE_TYPE (args[i].tree_value));
+ int big_endian_correction = 0;
+
+ args[i].n_aligned_regs
+ = args[i].partial ? args[i].partial
+ : (bytes + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
+
+ args[i].aligned_regs = (rtx *) alloca (sizeof (rtx)
+ * args[i].n_aligned_regs);
+
+ /* Structures smaller than a word are aligned to the least signifcant
+ byte (to the right). On a BYTES_BIG_ENDIAN machine, this means we
+ must skip the empty high order bytes when calculating the bit
+ offset. */
+ if (BYTES_BIG_ENDIAN && bytes < UNITS_PER_WORD)
+ big_endian_correction = (BITS_PER_WORD - (bytes * BITS_PER_UNIT));
+
+ for (j = 0; j < args[i].n_aligned_regs; j++)
+ {
+ rtx reg = gen_reg_rtx (word_mode);
+ rtx word = operand_subword_force (args[i].value, j, BLKmode);
+ int bitsize = TYPE_ALIGN (TREE_TYPE (args[i].tree_value));
+ int bitpos;
+
+ args[i].aligned_regs[j] = reg;
+
+ /* Clobber REG and move each partword into it. Ensure we don't
+ go past the end of the structure. Note that the loop below
+ works because we've already verified that padding
+ and endianness are compatible. */
+
+ emit_insn (gen_rtx (CLOBBER, VOIDmode, reg));
+
+ for (bitpos = 0;
+ bitpos < BITS_PER_WORD && bytes > 0;
+ bitpos += bitsize, bytes -= bitsize / BITS_PER_UNIT)
+ {
+ int xbitpos = bitpos + big_endian_correction;
+
+ store_bit_field (reg, bitsize, xbitpos, word_mode,
+ extract_bit_field (word, bitsize, bitpos, 1,
+ NULL_RTX, word_mode,
+ word_mode,
+ bitsize / BITS_PER_UNIT,
+ BITS_PER_WORD),
+ bitsize / BITS_PER_UNIT, BITS_PER_WORD);
+ }
+ }
+ }
+#endif
+
+ /* Now store any partially-in-registers parm.
+ This is the last place a block-move can happen. */
+ if (reg_parm_seen)
+ for (i = 0; i < num_actuals; i++)
+ if (args[i].partial != 0 && ! args[i].pass_on_stack)
+ store_one_arg (&args[i], argblock, may_be_alloca,
+ args_size.var != 0, fndecl, reg_parm_stack_space);
+
+#ifndef PUSH_ARGS_REVERSED
+#ifdef STACK_BOUNDARY
+ /* If we pushed args in forward order, perform stack alignment
+ after pushing the last arg. */
+ if (argblock == 0)
+ anti_adjust_stack (GEN_INT (args_size.constant
+ - original_args_size.constant));
+#endif
+#endif
+
+ /* If register arguments require space on the stack and stack space
+ was not preallocated, allocate stack space here for arguments
+ passed in registers. */
+#if ! defined(ACCUMULATE_OUTGOING_ARGS) && defined(OUTGOING_REG_PARM_STACK_SPACE)
+ if (must_preallocate == 0 && reg_parm_stack_space > 0)
+ anti_adjust_stack (GEN_INT (reg_parm_stack_space));
+#endif
+
+ /* Pass the function the address in which to return a structure value. */
+ if (structure_value_addr && ! structure_value_addr_parm)
+ {
+ emit_move_insn (struct_value_rtx,
+ force_reg (Pmode,
+ force_operand (structure_value_addr,
+ NULL_RTX)));
+ if (GET_CODE (struct_value_rtx) == REG)
+ use_reg (&call_fusage, struct_value_rtx);
+ }
+
+ funexp = prepare_call_address (funexp, fndecl, &call_fusage, reg_parm_seen);
+
+ /* Now do the register loads required for any wholly-register parms or any
+ parms which are passed both on the stack and in a register. Their
+ expressions were already evaluated.
+
+ Mark all register-parms as living through the call, putting these USE
+ insns in the CALL_INSN_FUNCTION_USAGE field. */
+
+ for (i = 0; i < num_actuals; i++)
+ {
+ rtx list = args[i].reg;
+ int partial = args[i].partial;
+
+ while (list)
+ {
+ rtx reg;
+ int nregs;
+
+ /* Process each register that needs to get this arg. */
+ if (GET_CODE (list) == EXPR_LIST)
+ reg = XEXP (list, 0), list = XEXP (list, 1);
+ else
+ reg = list, list = 0;
+
+ /* Set to non-negative if must move a word at a time, even if just
+ one word (e.g, partial == 1 && mode == DFmode). Set to -1 if
+ we just use a normal move insn. This value can be zero if the
+ argument is a zero size structure with no fields. */
+ nregs = (partial ? partial
+ : (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
+ ? ((int_size_in_bytes (TREE_TYPE (args[i].tree_value))
+ + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)
+ : -1));
+
+ /* If simple case, just do move. If normal partial, store_one_arg
+ has already loaded the register for us. In all other cases,
+ load the register(s) from memory. */
+
+ if (nregs == -1)
+ emit_move_insn (reg, args[i].value);
+
+#ifdef STRICT_ALIGNMENT
+ /* If we have pre-computed the values to put in the registers in
+ the case of non-aligned structures, copy them in now. */
+
+ else if (args[i].n_aligned_regs != 0)
+ for (j = 0; j < args[i].n_aligned_regs; j++)
+ emit_move_insn (gen_rtx (REG, word_mode, REGNO (reg) + j),
+ args[i].aligned_regs[j]);
+#endif
+
+ else if (args[i].partial == 0 || args[i].pass_on_stack)
+ move_block_to_reg (REGNO (reg),
+ validize_mem (args[i].value), nregs,
+ args[i].mode);
+
+ if (nregs == -1)
+ use_reg (&call_fusage, reg);
+ else
+ use_regs (&call_fusage, REGNO (reg), nregs == 0 ? 1 : nregs);
+
+ /* PARTIAL referred only to the first register, so clear it for the
+ next time. */
+ partial = 0;
+ }
+ }
+
+ /* Perform postincrements before actually calling the function. */
+ emit_queue ();
+
+ /* All arguments and registers used for the call must be set up by now! */
+
+ /* Generate the actual call instruction. */
+ emit_call_1 (funexp, funtype, args_size.constant, struct_value_size,
+ FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
+ valreg, old_inhibit_defer_pop, call_fusage, is_const);
+
+ /* If call is cse'able, make appropriate pair of reg-notes around it.
+ Test valreg so we don't crash; may safely ignore `const'
+ if return type is void. */
+ if (is_const && valreg != 0)
+ {
+ rtx note = 0;
+ rtx temp = gen_reg_rtx (GET_MODE (valreg));
+ rtx insns;
+
+ /* Construct an "equal form" for the value which mentions all the
+ arguments in order as well as the function name. */
+#ifdef PUSH_ARGS_REVERSED
+ for (i = 0; i < num_actuals; i++)
+ note = gen_rtx (EXPR_LIST, VOIDmode, args[i].initial_value, note);
+#else
+ for (i = num_actuals - 1; i >= 0; i--)
+ note = gen_rtx (EXPR_LIST, VOIDmode, args[i].initial_value, note);
+#endif
+ note = gen_rtx (EXPR_LIST, VOIDmode, funexp, note);
+
+ insns = get_insns ();
+ end_sequence ();
+
+ emit_libcall_block (insns, temp, valreg, note);
+
+ valreg = temp;
+ }
+
+ /* For calls to `setjmp', etc., inform flow.c it should complain
+ if nonvolatile values are live. */
+
+ if (returns_twice)
+ {
+ emit_note (name, NOTE_INSN_SETJMP);
+ current_function_calls_setjmp = 1;
+ }
+
+ if (is_longjmp)
+ current_function_calls_longjmp = 1;
+
+ /* Notice functions that cannot return.
+ If optimizing, insns emitted below will be dead.
+ If not optimizing, they will exist, which is useful
+ if the user uses the `return' command in the debugger. */
+
+ if (is_volatile || is_longjmp)
+ emit_barrier ();
+
+ /* If value type not void, return an rtx for the value. */
+
+ /* If there are cleanups to be called, don't use a hard reg as target. */
+ if (cleanups_this_call != old_cleanups
+ && target && REG_P (target)
+ && REGNO (target) < FIRST_PSEUDO_REGISTER)
+ target = 0;
+
+ if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode
+ || ignore)
+ {
+ target = const0_rtx;
+ }
+ else if (structure_value_addr)
+ {
+ if (target == 0 || GET_CODE (target) != MEM)
+ {
+ target = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)),
+ memory_address (TYPE_MODE (TREE_TYPE (exp)),
+ structure_value_addr));
+ MEM_IN_STRUCT_P (target) = AGGREGATE_TYPE_P (TREE_TYPE (exp));
+ }
+ }
+ else if (pcc_struct_value)
+ {
+ if (target == 0)
+ {
+ /* We used leave the value in the location that it is
+ returned in, but that causes problems if it is used more
+ than once in one expression. Rather than trying to track
+ when a copy is required, we always copy when TARGET is
+ not specified. This calling sequence is only used on
+ a few machines and TARGET is usually nonzero. */
+ if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode)
+ {
+ target = assign_stack_temp (BLKmode,
+ int_size_in_bytes (TREE_TYPE (exp)),
+ 0);
+
+ MEM_IN_STRUCT_P (target) = AGGREGATE_TYPE_P (TREE_TYPE (exp));
+
+ /* Save this temp slot around the pop below. */
+ preserve_temp_slots (target);
+ }
+ else
+ target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
+ }
+
+ if (TYPE_MODE (TREE_TYPE (exp)) != BLKmode)
+ emit_move_insn (target, gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)),
+ copy_to_reg (valreg)));
+ else
+ emit_block_move (target, gen_rtx (MEM, BLKmode, copy_to_reg (valreg)),
+ expr_size (exp),
+ TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT);
+ }
+ else if (target && GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp))
+ && GET_MODE (target) == GET_MODE (valreg))
+ /* TARGET and VALREG cannot be equal at this point because the latter
+ would not have REG_FUNCTION_VALUE_P true, while the former would if
+ it were referring to the same register.
+
+ If they refer to the same register, this move will be a no-op, except
+ when function inlining is being done. */
+ emit_move_insn (target, valreg);
+ else
+ target = copy_to_reg (valreg);
+
+#ifdef PROMOTE_FUNCTION_RETURN
+ /* If we promoted this return value, make the proper SUBREG. TARGET
+ might be const0_rtx here, so be careful. */
+ if (GET_CODE (target) == REG
+ && GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp)))
+ {
+ tree type = TREE_TYPE (exp);
+ int unsignedp = TREE_UNSIGNED (type);
+
+ /* If we don't promote as expected, something is wrong. */
+ if (GET_MODE (target)
+ != promote_mode (type, TYPE_MODE (type), &unsignedp, 1))
+ abort ();
+
+ target = gen_rtx (SUBREG, TYPE_MODE (type), target, 0);
+ SUBREG_PROMOTED_VAR_P (target) = 1;
+ SUBREG_PROMOTED_UNSIGNED_P (target) = unsignedp;
+ }
+#endif
+
+ if (flag_short_temps)
+ {
+ /* Perform all cleanups needed for the arguments of this call
+ (i.e. destructors in C++). */
+ expand_cleanups_to (old_cleanups);
+ }
+
+ /* If size of args is variable or this was a constructor call for a stack
+ argument, restore saved stack-pointer value. */
+
+ if (old_stack_level)
+ {
+ emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
+ pending_stack_adjust = old_pending_adj;
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ stack_arg_under_construction = old_stack_arg_under_construction;
+ highest_outgoing_arg_in_use = initial_highest_arg_in_use;
+ stack_usage_map = initial_stack_usage_map;
+#endif
+ }
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ else
+ {
+#ifdef REG_PARM_STACK_SPACE
+ if (save_area)
+ {
+ enum machine_mode save_mode = GET_MODE (save_area);
+ rtx stack_area
+ = gen_rtx (MEM, save_mode,
+ memory_address (save_mode,
+#ifdef ARGS_GROW_DOWNWARD
+ plus_constant (argblock, - high_to_save)
+#else
+ plus_constant (argblock, low_to_save)
+#endif
+ ));
+
+ if (save_mode != BLKmode)
+ emit_move_insn (stack_area, save_area);
+ else
+ emit_block_move (stack_area, validize_mem (save_area),
+ GEN_INT (high_to_save - low_to_save + 1),
+ PARM_BOUNDARY / BITS_PER_UNIT);
+ }
+#endif
+
+ /* If we saved any argument areas, restore them. */
+ for (i = 0; i < num_actuals; i++)
+ if (args[i].save_area)
+ {
+ enum machine_mode save_mode = GET_MODE (args[i].save_area);
+ rtx stack_area
+ = gen_rtx (MEM, save_mode,
+ memory_address (save_mode,
+ XEXP (args[i].stack_slot, 0)));
+
+ if (save_mode != BLKmode)
+ emit_move_insn (stack_area, args[i].save_area);
+ else
+ emit_block_move (stack_area, validize_mem (args[i].save_area),
+ GEN_INT (args[i].size.constant),
+ PARM_BOUNDARY / BITS_PER_UNIT);
+ }
+
+ highest_outgoing_arg_in_use = initial_highest_arg_in_use;
+ stack_usage_map = initial_stack_usage_map;
+ }
+#endif
+
+ /* If this was alloca, record the new stack level for nonlocal gotos.
+ Check for the handler slots since we might not have a save area
+ for non-local gotos. */
+
+ if (may_be_alloca && nonlocal_goto_handler_slot != 0)
+ emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX);
+
+ pop_temp_slots ();
+
+ return target;
+}
+
+/* Output a library call to function FUN (a SYMBOL_REF rtx)
+ (emitting the queue unless NO_QUEUE is nonzero),
+ for a value of mode OUTMODE,
+ with NARGS different arguments, passed as alternating rtx values
+ and machine_modes to convert them to.
+ The rtx values should have been passed through protect_from_queue already.
+
+ NO_QUEUE will be true if and only if the library call is a `const' call
+ which will be enclosed in REG_LIBCALL/REG_RETVAL notes; it is equivalent
+ to the variable is_const in expand_call.
+
+ NO_QUEUE must be true for const calls, because if it isn't, then
+ any pending increment will be emitted between REG_LIBCALL/REG_RETVAL notes,
+ and will be lost if the libcall sequence is optimized away.
+
+ NO_QUEUE must be false for non-const calls, because if it isn't, the
+ call insn will have its CONST_CALL_P bit set, and it will be incorrectly
+ optimized. For instance, the instruction scheduler may incorrectly
+ move memory references across the non-const call. */
+
+void
+emit_library_call VPROTO((rtx orgfun, int no_queue, enum machine_mode outmode,
+ int nargs, ...))
+{
+#ifndef __STDC__
+ rtx orgfun;
+ int no_queue;
+ enum machine_mode outmode;
+ int nargs;
+#endif
+ va_list p;
+ /* Total size in bytes of all the stack-parms scanned so far. */
+ struct args_size args_size;
+ /* Size of arguments before any adjustments (such as rounding). */
+ struct args_size original_args_size;
+ register int argnum;
+ rtx fun;
+ int inc;
+ int count;
+ rtx argblock = 0;
+ CUMULATIVE_ARGS args_so_far;
+ struct arg { rtx value; enum machine_mode mode; rtx reg; int partial;
+ struct args_size offset; struct args_size size; };
+ struct arg *argvec;
+ int old_inhibit_defer_pop = inhibit_defer_pop;
+ rtx call_fusage = 0;
+ /* library calls are never indirect calls. */
+ int current_call_is_indirect = 0;
+
+ VA_START (p, nargs);
+
+#ifndef __STDC__
+ orgfun = va_arg (p, rtx);
+ no_queue = va_arg (p, int);
+ outmode = va_arg (p, enum machine_mode);
+ nargs = va_arg (p, int);
+#endif
+
+ fun = orgfun;
+
+ /* Copy all the libcall-arguments out of the varargs data
+ and into a vector ARGVEC.
+
+ Compute how to pass each argument. We only support a very small subset
+ of the full argument passing conventions to limit complexity here since
+ library functions shouldn't have many args. */
+
+ argvec = (struct arg *) alloca (nargs * sizeof (struct arg));
+
+ INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun);
+
+ args_size.constant = 0;
+ args_size.var = 0;
+
+ push_temp_slots ();
+
+ for (count = 0; count < nargs; count++)
+ {
+ rtx val = va_arg (p, rtx);
+ enum machine_mode mode = va_arg (p, enum machine_mode);
+
+ /* We cannot convert the arg value to the mode the library wants here;
+ must do it earlier where we know the signedness of the arg. */
+ if (mode == BLKmode
+ || (GET_MODE (val) != mode && GET_MODE (val) != VOIDmode))
+ abort ();
+
+ /* On some machines, there's no way to pass a float to a library fcn.
+ Pass it as a double instead. */
+#ifdef LIBGCC_NEEDS_DOUBLE
+ if (LIBGCC_NEEDS_DOUBLE && mode == SFmode)
+ val = convert_modes (DFmode, SFmode, val, 0), mode = DFmode;
+#endif
+
+ /* There's no need to call protect_from_queue, because
+ either emit_move_insn or emit_push_insn will do that. */
+
+ /* Make sure it is a reasonable operand for a move or push insn. */
+ if (GET_CODE (val) != REG && GET_CODE (val) != MEM
+ && ! (CONSTANT_P (val) && LEGITIMATE_CONSTANT_P (val)))
+ val = force_operand (val, NULL_RTX);
+
+#ifdef FUNCTION_ARG_PASS_BY_REFERENCE
+ if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, mode, NULL_TREE, 1))
+ {
+ /* We do not support FUNCTION_ARG_CALLEE_COPIES here since it can
+ be viewed as just an efficiency improvement. */
+ rtx slot = assign_stack_temp (mode, GET_MODE_SIZE (mode), 0);
+ emit_move_insn (slot, val);
+ val = XEXP (slot, 0);
+ mode = Pmode;
+ }
+#endif
+
+ argvec[count].value = val;
+ argvec[count].mode = mode;
+
+ argvec[count].reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1);
+ if (argvec[count].reg && GET_CODE (argvec[count].reg) == EXPR_LIST)
+ abort ();
+#ifdef FUNCTION_ARG_PARTIAL_NREGS
+ argvec[count].partial
+ = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, NULL_TREE, 1);
+#else
+ argvec[count].partial = 0;
+#endif
+
+ locate_and_pad_parm (mode, NULL_TREE,
+ argvec[count].reg && argvec[count].partial == 0,
+ NULL_TREE, &args_size, &argvec[count].offset,
+ &argvec[count].size);
+
+ if (argvec[count].size.var)
+ abort ();
+
+#ifndef REG_PARM_STACK_SPACE
+ if (argvec[count].partial)
+ argvec[count].size.constant -= argvec[count].partial * UNITS_PER_WORD;
+#endif
+
+ if (argvec[count].reg == 0 || argvec[count].partial != 0
+#ifdef REG_PARM_STACK_SPACE
+ || 1
+#endif
+ )
+ args_size.constant += argvec[count].size.constant;
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ /* If this arg is actually passed on the stack, it might be
+ clobbering something we already put there (this library call might
+ be inside the evaluation of an argument to a function whose call
+ requires the stack). This will only occur when the library call
+ has sufficient args to run out of argument registers. Abort in
+ this case; if this ever occurs, code must be added to save and
+ restore the arg slot. */
+
+ if (argvec[count].reg == 0 || argvec[count].partial != 0)
+ abort ();
+#endif
+
+ FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree)0, 1);
+ }
+ va_end (p);
+
+ /* If this machine requires an external definition for library
+ functions, write one out. */
+ assemble_external_libcall (fun);
+
+ original_args_size = args_size;
+#ifdef STACK_BOUNDARY
+ args_size.constant = (((args_size.constant + (STACK_BYTES - 1))
+ / STACK_BYTES) * STACK_BYTES);
+#endif
+
+#ifdef REG_PARM_STACK_SPACE
+ args_size.constant = MAX (args_size.constant,
+ REG_PARM_STACK_SPACE (NULL_TREE));
+#ifndef OUTGOING_REG_PARM_STACK_SPACE
+ args_size.constant -= REG_PARM_STACK_SPACE (NULL_TREE);
+#endif
+#endif
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ if (args_size.constant > current_function_outgoing_args_size)
+ current_function_outgoing_args_size = args_size.constant;
+ args_size.constant = 0;
+#endif
+
+#ifndef PUSH_ROUNDING
+ argblock = push_block (GEN_INT (args_size.constant), 0, 0);
+#endif
+
+#ifdef PUSH_ARGS_REVERSED
+#ifdef STACK_BOUNDARY
+ /* If we push args individually in reverse order, perform stack alignment
+ before the first push (the last arg). */
+ if (argblock == 0)
+ anti_adjust_stack (GEN_INT (args_size.constant
+ - original_args_size.constant));
+#endif
+#endif
+
+#ifdef PUSH_ARGS_REVERSED
+ inc = -1;
+ argnum = nargs - 1;
+#else
+ inc = 1;
+ argnum = 0;
+#endif
+
+ /* Push the args that need to be pushed. */
+
+ for (count = 0; count < nargs; count++, argnum += inc)
+ {
+ register enum machine_mode mode = argvec[argnum].mode;
+ register rtx val = argvec[argnum].value;
+ rtx reg = argvec[argnum].reg;
+ int partial = argvec[argnum].partial;
+
+ if (! (reg != 0 && partial == 0))
+ emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0,
+ argblock, GEN_INT (argvec[count].offset.constant));
+ NO_DEFER_POP;
+ }
+
+#ifndef PUSH_ARGS_REVERSED
+#ifdef STACK_BOUNDARY
+ /* If we pushed args in forward order, perform stack alignment
+ after pushing the last arg. */
+ if (argblock == 0)
+ anti_adjust_stack (GEN_INT (args_size.constant
+ - original_args_size.constant));
+#endif
+#endif
+
+#ifdef PUSH_ARGS_REVERSED
+ argnum = nargs - 1;
+#else
+ argnum = 0;
+#endif
+
+ fun = prepare_call_address (fun, NULL_TREE, &call_fusage, 0);
+
+ /* Now load any reg parms into their regs. */
+
+ for (count = 0; count < nargs; count++, argnum += inc)
+ {
+ register enum machine_mode mode = argvec[argnum].mode;
+ register rtx val = argvec[argnum].value;
+ rtx reg = argvec[argnum].reg;
+ int partial = argvec[argnum].partial;
+
+ if (reg != 0 && partial == 0)
+ emit_move_insn (reg, val);
+ NO_DEFER_POP;
+ }
+
+ /* For version 1.37, try deleting this entirely. */
+ if (! no_queue)
+ emit_queue ();
+
+ /* Any regs containing parms remain in use through the call. */
+ for (count = 0; count < nargs; count++)
+ if (argvec[count].reg != 0)
+ use_reg (&call_fusage, argvec[count].reg);
+
+ /* Don't allow popping to be deferred, since then
+ cse'ing of library calls could delete a call and leave the pop. */
+ NO_DEFER_POP;
+
+ /* We pass the old value of inhibit_defer_pop + 1 to emit_call_1, which
+ will set inhibit_defer_pop to that value. */
+
+ emit_call_1 (fun, get_identifier (XSTR (orgfun, 0)), args_size.constant, 0,
+ FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
+ outmode != VOIDmode ? hard_libcall_value (outmode) : NULL_RTX,
+ old_inhibit_defer_pop + 1, call_fusage, no_queue);
+
+ pop_temp_slots ();
+
+ /* Now restore inhibit_defer_pop to its actual original value. */
+ OK_DEFER_POP;
+}
+
+/* Like emit_library_call except that an extra argument, VALUE,
+ comes second and says where to store the result.
+ (If VALUE is zero, this function chooses a convenient way
+ to return the value.
+
+ This function returns an rtx for where the value is to be found.
+ If VALUE is nonzero, VALUE is returned. */
+
+rtx
+emit_library_call_value VPROTO((rtx orgfun, rtx value, int no_queue,
+ enum machine_mode outmode, int nargs, ...))
+{
+#ifndef __STDC__
+ rtx orgfun;
+ rtx value;
+ int no_queue;
+ enum machine_mode outmode;
+ int nargs;
+#endif
+ va_list p;
+ /* Total size in bytes of all the stack-parms scanned so far. */
+ struct args_size args_size;
+ /* Size of arguments before any adjustments (such as rounding). */
+ struct args_size original_args_size;
+ register int argnum;
+ rtx fun;
+ int inc;
+ int count;
+ rtx argblock = 0;
+ CUMULATIVE_ARGS args_so_far;
+ struct arg { rtx value; enum machine_mode mode; rtx reg; int partial;
+ struct args_size offset; struct args_size size; };
+ struct arg *argvec;
+ int old_inhibit_defer_pop = inhibit_defer_pop;
+ rtx call_fusage = 0;
+ rtx mem_value = 0;
+ int pcc_struct_value = 0;
+ int struct_value_size = 0;
+ /* library calls are never indirect calls. */
+ int current_call_is_indirect = 0;
+ int is_const;
+
+ VA_START (p, nargs);
+
+#ifndef __STDC__
+ orgfun = va_arg (p, rtx);
+ value = va_arg (p, rtx);
+ no_queue = va_arg (p, int);
+ outmode = va_arg (p, enum machine_mode);
+ nargs = va_arg (p, int);
+#endif
+
+ is_const = no_queue;
+ fun = orgfun;
+
+ /* If this kind of value comes back in memory,
+ decide where in memory it should come back. */
+ if (aggregate_value_p (type_for_mode (outmode, 0)))
+ {
+#ifdef PCC_STATIC_STRUCT_RETURN
+ rtx pointer_reg
+ = hard_function_value (build_pointer_type (type_for_mode (outmode, 0)),
+ 0);
+ mem_value = gen_rtx (MEM, outmode, pointer_reg);
+ pcc_struct_value = 1;
+ if (value == 0)
+ value = gen_reg_rtx (outmode);
+#else /* not PCC_STATIC_STRUCT_RETURN */
+ struct_value_size = GET_MODE_SIZE (outmode);
+ if (value != 0 && GET_CODE (value) == MEM)
+ mem_value = value;
+ else
+ mem_value = assign_stack_temp (outmode, GET_MODE_SIZE (outmode), 0);
+#endif
+
+ /* This call returns a big structure. */
+ is_const = 0;
+ }
+
+ /* ??? Unfinished: must pass the memory address as an argument. */
+
+ /* Copy all the libcall-arguments out of the varargs data
+ and into a vector ARGVEC.
+
+ Compute how to pass each argument. We only support a very small subset
+ of the full argument passing conventions to limit complexity here since
+ library functions shouldn't have many args. */
+
+ argvec = (struct arg *) alloca ((nargs + 1) * sizeof (struct arg));
+
+ INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun);
+
+ args_size.constant = 0;
+ args_size.var = 0;
+
+ count = 0;
+
+ push_temp_slots ();
+
+ /* If there's a structure value address to be passed,
+ either pass it in the special place, or pass it as an extra argument. */
+ if (mem_value && struct_value_rtx == 0 && ! pcc_struct_value)
+ {
+ rtx addr = XEXP (mem_value, 0);
+ nargs++;
+
+ /* Make sure it is a reasonable operand for a move or push insn. */
+ if (GET_CODE (addr) != REG && GET_CODE (addr) != MEM
+ && ! (CONSTANT_P (addr) && LEGITIMATE_CONSTANT_P (addr)))
+ addr = force_operand (addr, NULL_RTX);
+
+ argvec[count].value = addr;
+ argvec[count].mode = Pmode;
+ argvec[count].partial = 0;
+
+ argvec[count].reg = FUNCTION_ARG (args_so_far, Pmode, NULL_TREE, 1);
+#ifdef FUNCTION_ARG_PARTIAL_NREGS
+ if (FUNCTION_ARG_PARTIAL_NREGS (args_so_far, Pmode, NULL_TREE, 1))
+ abort ();
+#endif
+
+ locate_and_pad_parm (Pmode, NULL_TREE,
+ argvec[count].reg && argvec[count].partial == 0,
+ NULL_TREE, &args_size, &argvec[count].offset,
+ &argvec[count].size);
+
+
+ if (argvec[count].reg == 0 || argvec[count].partial != 0
+#ifdef REG_PARM_STACK_SPACE
+ || 1
+#endif
+ )
+ args_size.constant += argvec[count].size.constant;
+
+ FUNCTION_ARG_ADVANCE (args_so_far, Pmode, (tree)0, 1);
+
+ count++;
+ }
+
+ for (; count < nargs; count++)
+ {
+ rtx val = va_arg (p, rtx);
+ enum machine_mode mode = va_arg (p, enum machine_mode);
+
+ /* We cannot convert the arg value to the mode the library wants here;
+ must do it earlier where we know the signedness of the arg. */
+ if (mode == BLKmode
+ || (GET_MODE (val) != mode && GET_MODE (val) != VOIDmode))
+ abort ();
+
+ /* On some machines, there's no way to pass a float to a library fcn.
+ Pass it as a double instead. */
+#ifdef LIBGCC_NEEDS_DOUBLE
+ if (LIBGCC_NEEDS_DOUBLE && mode == SFmode)
+ val = convert_modes (DFmode, SFmode, val, 0), mode = DFmode;
+#endif
+
+ /* There's no need to call protect_from_queue, because
+ either emit_move_insn or emit_push_insn will do that. */
+
+ /* Make sure it is a reasonable operand for a move or push insn. */
+ if (GET_CODE (val) != REG && GET_CODE (val) != MEM
+ && ! (CONSTANT_P (val) && LEGITIMATE_CONSTANT_P (val)))
+ val = force_operand (val, NULL_RTX);
+
+#ifdef FUNCTION_ARG_PASS_BY_REFERENCE
+ if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, mode, NULL_TREE, 1))
+ {
+ /* We do not support FUNCTION_ARG_CALLEE_COPIES here since it can
+ be viewed as just an efficiency improvement. */
+ rtx slot = assign_stack_temp (mode, GET_MODE_SIZE (mode), 0);
+ emit_move_insn (slot, val);
+ val = XEXP (slot, 0);
+ mode = Pmode;
+ }
+#endif
+
+ argvec[count].value = val;
+ argvec[count].mode = mode;
+
+ argvec[count].reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1);
+ if (argvec[count].reg && GET_CODE (argvec[count].reg) == EXPR_LIST)
+ abort ();
+#ifdef FUNCTION_ARG_PARTIAL_NREGS
+ argvec[count].partial
+ = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, NULL_TREE, 1);
+#else
+ argvec[count].partial = 0;
+#endif
+
+ locate_and_pad_parm (mode, NULL_TREE,
+ argvec[count].reg && argvec[count].partial == 0,
+ NULL_TREE, &args_size, &argvec[count].offset,
+ &argvec[count].size);
+
+ if (argvec[count].size.var)
+ abort ();
+
+#ifndef REG_PARM_STACK_SPACE
+ if (argvec[count].partial)
+ argvec[count].size.constant -= argvec[count].partial * UNITS_PER_WORD;
+#endif
+
+ if (argvec[count].reg == 0 || argvec[count].partial != 0
+#ifdef REG_PARM_STACK_SPACE
+ || 1
+#endif
+ )
+ args_size.constant += argvec[count].size.constant;
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ /* If this arg is actually passed on the stack, it might be
+ clobbering something we already put there (this library call might
+ be inside the evaluation of an argument to a function whose call
+ requires the stack). This will only occur when the library call
+ has sufficient args to run out of argument registers. Abort in
+ this case; if this ever occurs, code must be added to save and
+ restore the arg slot. */
+
+ if (argvec[count].reg == 0 || argvec[count].partial != 0)
+ abort ();
+#endif
+
+ FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree)0, 1);
+ }
+ va_end (p);
+
+ /* If this machine requires an external definition for library
+ functions, write one out. */
+ assemble_external_libcall (fun);
+
+ original_args_size = args_size;
+#ifdef STACK_BOUNDARY
+ args_size.constant = (((args_size.constant + (STACK_BYTES - 1))
+ / STACK_BYTES) * STACK_BYTES);
+#endif
+
+#ifdef REG_PARM_STACK_SPACE
+ args_size.constant = MAX (args_size.constant,
+ REG_PARM_STACK_SPACE (NULL_TREE));
+#ifndef OUTGOING_REG_PARM_STACK_SPACE
+ args_size.constant -= REG_PARM_STACK_SPACE (NULL_TREE);
+#endif
+#endif
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ if (args_size.constant > current_function_outgoing_args_size)
+ current_function_outgoing_args_size = args_size.constant;
+ args_size.constant = 0;
+#endif
+
+#ifndef PUSH_ROUNDING
+ argblock = push_block (GEN_INT (args_size.constant), 0, 0);
+#endif
+
+#ifdef PUSH_ARGS_REVERSED
+#ifdef STACK_BOUNDARY
+ /* If we push args individually in reverse order, perform stack alignment
+ before the first push (the last arg). */
+ if (argblock == 0)
+ anti_adjust_stack (GEN_INT (args_size.constant
+ - original_args_size.constant));
+#endif
+#endif
+
+#ifdef PUSH_ARGS_REVERSED
+ inc = -1;
+ argnum = nargs - 1;
+#else
+ inc = 1;
+ argnum = 0;
+#endif
+
+ /* Push the args that need to be pushed. */
+
+ for (count = 0; count < nargs; count++, argnum += inc)
+ {
+ register enum machine_mode mode = argvec[argnum].mode;
+ register rtx val = argvec[argnum].value;
+ rtx reg = argvec[argnum].reg;
+ int partial = argvec[argnum].partial;
+
+ if (! (reg != 0 && partial == 0))
+ emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0,
+ argblock, GEN_INT (argvec[count].offset.constant));
+ NO_DEFER_POP;
+ }
+
+#ifndef PUSH_ARGS_REVERSED
+#ifdef STACK_BOUNDARY
+ /* If we pushed args in forward order, perform stack alignment
+ after pushing the last arg. */
+ if (argblock == 0)
+ anti_adjust_stack (GEN_INT (args_size.constant
+ - original_args_size.constant));
+#endif
+#endif
+
+#ifdef PUSH_ARGS_REVERSED
+ argnum = nargs - 1;
+#else
+ argnum = 0;
+#endif
+
+ fun = prepare_call_address (fun, NULL_TREE, &call_fusage, 0);
+
+ /* Now load any reg parms into their regs. */
+
+ for (count = 0; count < nargs; count++, argnum += inc)
+ {
+ register enum machine_mode mode = argvec[argnum].mode;
+ register rtx val = argvec[argnum].value;
+ rtx reg = argvec[argnum].reg;
+ int partial = argvec[argnum].partial;
+
+ if (reg != 0 && partial == 0)
+ emit_move_insn (reg, val);
+ NO_DEFER_POP;
+ }
+
+#if 0
+ /* For version 1.37, try deleting this entirely. */
+ if (! no_queue)
+ emit_queue ();
+#endif
+
+ /* Any regs containing parms remain in use through the call. */
+ for (count = 0; count < nargs; count++)
+ if (argvec[count].reg != 0)
+ use_reg (&call_fusage, argvec[count].reg);
+
+ /* Pass the function the address in which to return a structure value. */
+ if (mem_value != 0 && struct_value_rtx != 0 && ! pcc_struct_value)
+ {
+ emit_move_insn (struct_value_rtx,
+ force_reg (Pmode,
+ force_operand (XEXP (mem_value, 0),
+ NULL_RTX)));
+ if (GET_CODE (struct_value_rtx) == REG)
+ use_reg (&call_fusage, struct_value_rtx);
+ }
+
+ /* Don't allow popping to be deferred, since then
+ cse'ing of library calls could delete a call and leave the pop. */
+ NO_DEFER_POP;
+
+ /* We pass the old value of inhibit_defer_pop + 1 to emit_call_1, which
+ will set inhibit_defer_pop to that value. */
+
+ emit_call_1 (fun, get_identifier (XSTR (orgfun, 0)), args_size.constant,
+ struct_value_size,
+ FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1),
+ (outmode != VOIDmode && mem_value == 0
+ ? hard_libcall_value (outmode) : NULL_RTX),
+ old_inhibit_defer_pop + 1, call_fusage, is_const);
+
+ /* Now restore inhibit_defer_pop to its actual original value. */
+ OK_DEFER_POP;
+
+ pop_temp_slots ();
+
+ /* Copy the value to the right place. */
+ if (outmode != VOIDmode)
+ {
+ if (mem_value)
+ {
+ if (value == 0)
+ value = mem_value;
+ if (value != mem_value)
+ emit_move_insn (value, mem_value);
+ }
+ else if (value != 0)
+ emit_move_insn (value, hard_libcall_value (outmode));
+ else
+ value = hard_libcall_value (outmode);
+ }
+
+ return value;
+}
+
+#if 0
+/* Return an rtx which represents a suitable home on the stack
+ given TYPE, the type of the argument looking for a home.
+ This is called only for BLKmode arguments.
+
+ SIZE is the size needed for this target.
+ ARGS_ADDR is the address of the bottom of the argument block for this call.
+ OFFSET describes this parameter's offset into ARGS_ADDR. It is meaningless
+ if this machine uses push insns. */
+
+static rtx
+target_for_arg (type, size, args_addr, offset)
+ tree type;
+ rtx size;
+ rtx args_addr;
+ struct args_size offset;
+{
+ rtx target;
+ rtx offset_rtx = ARGS_SIZE_RTX (offset);
+
+ /* We do not call memory_address if possible,
+ because we want to address as close to the stack
+ as possible. For non-variable sized arguments,
+ this will be stack-pointer relative addressing. */
+ if (GET_CODE (offset_rtx) == CONST_INT)
+ target = plus_constant (args_addr, INTVAL (offset_rtx));
+ else
+ {
+ /* I have no idea how to guarantee that this
+ will work in the presence of register parameters. */
+ target = gen_rtx (PLUS, Pmode, args_addr, offset_rtx);
+ target = memory_address (QImode, target);
+ }
+
+ return gen_rtx (MEM, BLKmode, target);
+}
+#endif
+
+/* Store a single argument for a function call
+ into the register or memory area where it must be passed.
+ *ARG describes the argument value and where to pass it.
+
+ ARGBLOCK is the address of the stack-block for all the arguments,
+ or 0 on a machine where arguments are pushed individually.
+
+ MAY_BE_ALLOCA nonzero says this could be a call to `alloca'
+ so must be careful about how the stack is used.
+
+ VARIABLE_SIZE nonzero says that this was a variable-sized outgoing
+ argument stack. This is used if ACCUMULATE_OUTGOING_ARGS to indicate
+ that we need not worry about saving and restoring the stack.
+
+ FNDECL is the declaration of the function we are calling. */
+
+static void
+store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl,
+ reg_parm_stack_space)
+ struct arg_data *arg;
+ rtx argblock;
+ int may_be_alloca;
+ int variable_size;
+ tree fndecl;
+ int reg_parm_stack_space;
+{
+ register tree pval = arg->tree_value;
+ rtx reg = 0;
+ int partial = 0;
+ int used = 0;
+ int i, lower_bound, upper_bound;
+
+ if (TREE_CODE (pval) == ERROR_MARK)
+ return;
+
+ /* Push a new temporary level for any temporaries we make for
+ this argument. */
+ push_temp_slots ();
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ /* If this is being stored into a pre-allocated, fixed-size, stack area,
+ save any previous data at that location. */
+ if (argblock && ! variable_size && arg->stack)
+ {
+#ifdef ARGS_GROW_DOWNWARD
+ /* stack_slot is negative, but we want to index stack_usage_map */
+ /* with positive values. */
+ if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS)
+ upper_bound = -INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1)) + 1;
+ else
+ abort ();
+
+ lower_bound = upper_bound - arg->size.constant;
+#else
+ if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS)
+ lower_bound = INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1));
+ else
+ lower_bound = 0;
+
+ upper_bound = lower_bound + arg->size.constant;
+#endif
+
+ for (i = lower_bound; i < upper_bound; i++)
+ if (stack_usage_map[i]
+#ifdef REG_PARM_STACK_SPACE
+ /* Don't store things in the fixed argument area at this point;
+ it has already been saved. */
+ && i > reg_parm_stack_space
+#endif
+ )
+ break;
+
+ if (i != upper_bound)
+ {
+ /* We need to make a save area. See what mode we can make it. */
+ enum machine_mode save_mode
+ = mode_for_size (arg->size.constant * BITS_PER_UNIT, MODE_INT, 1);
+ rtx stack_area
+ = gen_rtx (MEM, save_mode,
+ memory_address (save_mode, XEXP (arg->stack_slot, 0)));
+
+ if (save_mode == BLKmode)
+ {
+ arg->save_area = assign_stack_temp (BLKmode,
+ arg->size.constant, 1);
+ preserve_temp_slots (arg->save_area);
+ emit_block_move (validize_mem (arg->save_area), stack_area,
+ GEN_INT (arg->size.constant),
+ PARM_BOUNDARY / BITS_PER_UNIT);
+ }
+ else
+ {
+ arg->save_area = gen_reg_rtx (save_mode);
+ emit_move_insn (arg->save_area, stack_area);
+ }
+ }
+ }
+#endif
+
+ /* If this isn't going to be placed on both the stack and in registers,
+ set up the register and number of words. */
+ if (! arg->pass_on_stack)
+ reg = arg->reg, partial = arg->partial;
+
+ if (reg != 0 && partial == 0)
+ /* Being passed entirely in a register. We shouldn't be called in
+ this case. */
+ abort ();
+
+#ifdef STRICT_ALIGNMENT
+ /* If this arg needs special alignment, don't load the registers
+ here. */
+ if (arg->n_aligned_regs != 0)
+ reg = 0;
+#endif
+
+ /* If this is being partially passed in a register, but multiple locations
+ are specified, we assume that the one partially used is the one that is
+ listed first. */
+ if (reg && GET_CODE (reg) == EXPR_LIST)
+ reg = XEXP (reg, 0);
+
+ /* If this is being passed partially in a register, we can't evaluate
+ it directly into its stack slot. Otherwise, we can. */
+ if (arg->value == 0)
+ {
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ /* stack_arg_under_construction is nonzero if a function argument is
+ being evaluated directly into the outgoing argument list and
+ expand_call must take special action to preserve the argument list
+ if it is called recursively.
+
+ For scalar function arguments stack_usage_map is sufficient to
+ determine which stack slots must be saved and restored. Scalar
+ arguments in general have pass_on_stack == 0.
+
+ If this argument is initialized by a function which takes the
+ address of the argument (a C++ constructor or a C function
+ returning a BLKmode structure), then stack_usage_map is
+ insufficient and expand_call must push the stack around the
+ function call. Such arguments have pass_on_stack == 1.
+
+ Note that it is always safe to set stack_arg_under_construction,
+ but this generates suboptimal code if set when not needed. */
+
+ if (arg->pass_on_stack)
+ stack_arg_under_construction++;
+#endif
+ arg->value = expand_expr (pval,
+ (partial
+ || TYPE_MODE (TREE_TYPE (pval)) != arg->mode)
+ ? NULL_RTX : arg->stack,
+ VOIDmode, 0);
+
+ /* If we are promoting object (or for any other reason) the mode
+ doesn't agree, convert the mode. */
+
+ if (arg->mode != TYPE_MODE (TREE_TYPE (pval)))
+ arg->value = convert_modes (arg->mode, TYPE_MODE (TREE_TYPE (pval)),
+ arg->value, arg->unsignedp);
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ if (arg->pass_on_stack)
+ stack_arg_under_construction--;
+#endif
+ }
+
+ /* Don't allow anything left on stack from computation
+ of argument to alloca. */
+ if (may_be_alloca)
+ do_pending_stack_adjust ();
+
+ if (arg->value == arg->stack)
+ /* If the value is already in the stack slot, we are done. */
+ ;
+ else if (arg->mode != BLKmode)
+ {
+ register int size;
+
+ /* Argument is a scalar, not entirely passed in registers.
+ (If part is passed in registers, arg->partial says how much
+ and emit_push_insn will take care of putting it there.)
+
+ Push it, and if its size is less than the
+ amount of space allocated to it,
+ also bump stack pointer by the additional space.
+ Note that in C the default argument promotions
+ will prevent such mismatches. */
+
+ size = GET_MODE_SIZE (arg->mode);
+ /* Compute how much space the push instruction will push.
+ On many machines, pushing a byte will advance the stack
+ pointer by a halfword. */
+#ifdef PUSH_ROUNDING
+ size = PUSH_ROUNDING (size);
+#endif
+ used = size;
+
+ /* Compute how much space the argument should get:
+ round up to a multiple of the alignment for arguments. */
+ if (none != FUNCTION_ARG_PADDING (arg->mode, TREE_TYPE (pval)))
+ used = (((size + PARM_BOUNDARY / BITS_PER_UNIT - 1)
+ / (PARM_BOUNDARY / BITS_PER_UNIT))
+ * (PARM_BOUNDARY / BITS_PER_UNIT));
+
+ /* This isn't already where we want it on the stack, so put it there.
+ This can either be done with push or copy insns. */
+ emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX,
+ 0, partial, reg, used - size,
+ argblock, ARGS_SIZE_RTX (arg->offset));
+ }
+ else
+ {
+ /* BLKmode, at least partly to be pushed. */
+
+ register int excess;
+ rtx size_rtx;
+
+ /* Pushing a nonscalar.
+ If part is passed in registers, PARTIAL says how much
+ and emit_push_insn will take care of putting it there. */
+
+ /* Round its size up to a multiple
+ of the allocation unit for arguments. */
+
+ if (arg->size.var != 0)
+ {
+ excess = 0;
+ size_rtx = ARGS_SIZE_RTX (arg->size);
+ }
+ else
+ {
+ /* PUSH_ROUNDING has no effect on us, because
+ emit_push_insn for BLKmode is careful to avoid it. */
+ excess = (arg->size.constant - int_size_in_bytes (TREE_TYPE (pval))
+ + partial * UNITS_PER_WORD);
+ size_rtx = expr_size (pval);
+ }
+
+ emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
+ TYPE_ALIGN (TREE_TYPE (pval)) / BITS_PER_UNIT, partial,
+ reg, excess, argblock, ARGS_SIZE_RTX (arg->offset));
+ }
+
+
+ /* Unless this is a partially-in-register argument, the argument is now
+ in the stack.
+
+ ??? Note that this can change arg->value from arg->stack to
+ arg->stack_slot and it matters when they are not the same.
+ It isn't totally clear that this is correct in all cases. */
+ if (partial == 0)
+ arg->value = arg->stack_slot;
+
+ /* Once we have pushed something, pops can't safely
+ be deferred during the rest of the arguments. */
+ NO_DEFER_POP;
+
+ /* ANSI doesn't require a sequence point here,
+ but PCC has one, so this will avoid some problems. */
+ emit_queue ();
+
+ /* Free any temporary slots made in processing this argument. */
+ free_temp_slots ();
+ pop_temp_slots ();
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ /* Now mark the segment we just used. */
+ if (argblock && ! variable_size && arg->stack)
+ for (i = lower_bound; i < upper_bound; i++)
+ stack_usage_map[i] = 1;
+#endif
+}
diff --git a/gnu/usr.bin/cc/cc_int/combine.c b/gnu/usr.bin/cc/cc_int/combine.c
new file mode 100644
index 0000000..990fa4d
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/combine.c
@@ -0,0 +1,10790 @@
+/* Optimize by combining instructions for GNU compiler.
+ Copyright (C) 1987, 1988, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This module is essentially the "combiner" phase of the U. of Arizona
+ Portable Optimizer, but redone to work on our list-structured
+ representation for RTL instead of their string representation.
+
+ The LOG_LINKS of each insn identify the most recent assignment
+ to each REG used in the insn. It is a list of previous insns,
+ each of which contains a SET for a REG that is used in this insn
+ and not used or set in between. LOG_LINKs never cross basic blocks.
+ They were set up by the preceding pass (lifetime analysis).
+
+ We try to combine each pair of insns joined by a logical link.
+ We also try to combine triples of insns A, B and C when
+ C has a link back to B and B has a link back to A.
+
+ LOG_LINKS does not have links for use of the CC0. They don't
+ need to, because the insn that sets the CC0 is always immediately
+ before the insn that tests it. So we always regard a branch
+ insn as having a logical link to the preceding insn. The same is true
+ for an insn explicitly using CC0.
+
+ We check (with use_crosses_set_p) to avoid combining in such a way
+ as to move a computation to a place where its value would be different.
+
+ Combination is done by mathematically substituting the previous
+ insn(s) values for the regs they set into the expressions in
+ the later insns that refer to these regs. If the result is a valid insn
+ for our target machine, according to the machine description,
+ we install it, delete the earlier insns, and update the data flow
+ information (LOG_LINKS and REG_NOTES) for what we did.
+
+ There are a few exceptions where the dataflow information created by
+ flow.c aren't completely updated:
+
+ - reg_live_length is not updated
+ - reg_n_refs is not adjusted in the rare case when a register is
+ no longer required in a computation
+ - there are extremely rare cases (see distribute_regnotes) when a
+ REG_DEAD note is lost
+ - a LOG_LINKS entry that refers to an insn with multiple SETs may be
+ removed because there is no way to know which register it was
+ linking
+
+ To simplify substitution, we combine only when the earlier insn(s)
+ consist of only a single assignment. To simplify updating afterward,
+ we never combine when a subroutine call appears in the middle.
+
+ Since we do not represent assignments to CC0 explicitly except when that
+ is all an insn does, there is no LOG_LINKS entry in an insn that uses
+ the condition code for the insn that set the condition code.
+ Fortunately, these two insns must be consecutive.
+ Therefore, every JUMP_INSN is taken to have an implicit logical link
+ to the preceding insn. This is not quite right, since non-jumps can
+ also use the condition code; but in practice such insns would not
+ combine anyway. */
+
+#include "config.h"
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+/* Must precede rtl.h for FFS. */
+#include <stdio.h>
+
+#include "rtl.h"
+#include "flags.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "expr.h"
+#include "basic-block.h"
+#include "insn-config.h"
+#include "insn-flags.h"
+#include "insn-codes.h"
+#include "insn-attr.h"
+#include "recog.h"
+#include "real.h"
+
+/* It is not safe to use ordinary gen_lowpart in combine.
+ Use gen_lowpart_for_combine instead. See comments there. */
+#define gen_lowpart dont_use_gen_lowpart_you_dummy
+
+/* Number of attempts to combine instructions in this function. */
+
+static int combine_attempts;
+
+/* Number of attempts that got as far as substitution in this function. */
+
+static int combine_merges;
+
+/* Number of instructions combined with added SETs in this function. */
+
+static int combine_extras;
+
+/* Number of instructions combined in this function. */
+
+static int combine_successes;
+
+/* Totals over entire compilation. */
+
+static int total_attempts, total_merges, total_extras, total_successes;
+
+/* Define a defulat value for REVERSIBLE_CC_MODE.
+ We can never assume that a condition code mode is safe to reverse unless
+ the md tells us so. */
+#ifndef REVERSIBLE_CC_MODE
+#define REVERSIBLE_CC_MODE(MODE) 0
+#endif
+
+/* Vector mapping INSN_UIDs to cuids.
+ The cuids are like uids but increase monotonically always.
+ Combine always uses cuids so that it can compare them.
+ But actually renumbering the uids, which we used to do,
+ proves to be a bad idea because it makes it hard to compare
+ the dumps produced by earlier passes with those from later passes. */
+
+static int *uid_cuid;
+
+/* Get the cuid of an insn. */
+
+#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)])
+
+/* Maximum register number, which is the size of the tables below. */
+
+static int combine_max_regno;
+
+/* Record last point of death of (hard or pseudo) register n. */
+
+static rtx *reg_last_death;
+
+/* Record last point of modification of (hard or pseudo) register n. */
+
+static rtx *reg_last_set;
+
+/* Record the cuid of the last insn that invalidated memory
+ (anything that writes memory, and subroutine calls, but not pushes). */
+
+static int mem_last_set;
+
+/* Record the cuid of the last CALL_INSN
+ so we can tell whether a potential combination crosses any calls. */
+
+static int last_call_cuid;
+
+/* When `subst' is called, this is the insn that is being modified
+ (by combining in a previous insn). The PATTERN of this insn
+ is still the old pattern partially modified and it should not be
+ looked at, but this may be used to examine the successors of the insn
+ to judge whether a simplification is valid. */
+
+static rtx subst_insn;
+
+/* If nonzero, this is the insn that should be presumed to be
+ immediately in front of `subst_insn'. */
+
+static rtx subst_prev_insn;
+
+/* This is the lowest CUID that `subst' is currently dealing with.
+ get_last_value will not return a value if the register was set at or
+ after this CUID. If not for this mechanism, we could get confused if
+ I2 or I1 in try_combine were an insn that used the old value of a register
+ to obtain a new value. In that case, we might erroneously get the
+ new value of the register when we wanted the old one. */
+
+static int subst_low_cuid;
+
+/* This contains any hard registers that are used in newpat; reg_dead_at_p
+ must consider all these registers to be always live. */
+
+static HARD_REG_SET newpat_used_regs;
+
+/* This is an insn to which a LOG_LINKS entry has been added. If this
+ insn is the earlier than I2 or I3, combine should rescan starting at
+ that location. */
+
+static rtx added_links_insn;
+
+/* This is the value of undobuf.num_undo when we started processing this
+ substitution. This will prevent gen_rtx_combine from re-used a piece
+ from the previous expression. Doing so can produce circular rtl
+ structures. */
+
+static int previous_num_undos;
+
+/* Basic block number of the block in which we are performing combines. */
+static int this_basic_block;
+
+/* The next group of arrays allows the recording of the last value assigned
+ to (hard or pseudo) register n. We use this information to see if a
+ operation being processed is redundant given a prior operation performed
+ on the register. For example, an `and' with a constant is redundant if
+ all the zero bits are already known to be turned off.
+
+ We use an approach similar to that used by cse, but change it in the
+ following ways:
+
+ (1) We do not want to reinitialize at each label.
+ (2) It is useful, but not critical, to know the actual value assigned
+ to a register. Often just its form is helpful.
+
+ Therefore, we maintain the following arrays:
+
+ reg_last_set_value the last value assigned
+ reg_last_set_label records the value of label_tick when the
+ register was assigned
+ reg_last_set_table_tick records the value of label_tick when a
+ value using the register is assigned
+ reg_last_set_invalid set to non-zero when it is not valid
+ to use the value of this register in some
+ register's value
+
+ To understand the usage of these tables, it is important to understand
+ the distinction between the value in reg_last_set_value being valid
+ and the register being validly contained in some other expression in the
+ table.
+
+ Entry I in reg_last_set_value is valid if it is non-zero, and either
+ reg_n_sets[i] is 1 or reg_last_set_label[i] == label_tick.
+
+ Register I may validly appear in any expression returned for the value
+ of another register if reg_n_sets[i] is 1. It may also appear in the
+ value for register J if reg_last_set_label[i] < reg_last_set_label[j] or
+ reg_last_set_invalid[j] is zero.
+
+ If an expression is found in the table containing a register which may
+ not validly appear in an expression, the register is replaced by
+ something that won't match, (clobber (const_int 0)).
+
+ reg_last_set_invalid[i] is set non-zero when register I is being assigned
+ to and reg_last_set_table_tick[i] == label_tick. */
+
+/* Record last value assigned to (hard or pseudo) register n. */
+
+static rtx *reg_last_set_value;
+
+/* Record the value of label_tick when the value for register n is placed in
+ reg_last_set_value[n]. */
+
+static int *reg_last_set_label;
+
+/* Record the value of label_tick when an expression involving register n
+ is placed in reg_last_set_value. */
+
+static int *reg_last_set_table_tick;
+
+/* Set non-zero if references to register n in expressions should not be
+ used. */
+
+static char *reg_last_set_invalid;
+
+/* Incremented for each label. */
+
+static int label_tick;
+
+/* Some registers that are set more than once and used in more than one
+ basic block are nevertheless always set in similar ways. For example,
+ a QImode register may be loaded from memory in two places on a machine
+ where byte loads zero extend.
+
+ We record in the following array what we know about the nonzero
+ bits of a register, specifically which bits are known to be zero.
+
+ If an entry is zero, it means that we don't know anything special. */
+
+static unsigned HOST_WIDE_INT *reg_nonzero_bits;
+
+/* Mode used to compute significance in reg_nonzero_bits. It is the largest
+ integer mode that can fit in HOST_BITS_PER_WIDE_INT. */
+
+static enum machine_mode nonzero_bits_mode;
+
+/* Nonzero if we know that a register has some leading bits that are always
+ equal to the sign bit. */
+
+static char *reg_sign_bit_copies;
+
+/* Nonzero when reg_nonzero_bits and reg_sign_bit_copies can be safely used.
+ It is zero while computing them and after combine has completed. This
+ former test prevents propagating values based on previously set values,
+ which can be incorrect if a variable is modified in a loop. */
+
+static int nonzero_sign_valid;
+
+/* These arrays are maintained in parallel with reg_last_set_value
+ and are used to store the mode in which the register was last set,
+ the bits that were known to be zero when it was last set, and the
+ number of sign bits copies it was known to have when it was last set. */
+
+static enum machine_mode *reg_last_set_mode;
+static unsigned HOST_WIDE_INT *reg_last_set_nonzero_bits;
+static char *reg_last_set_sign_bit_copies;
+
+/* Record one modification to rtl structure
+ to be undone by storing old_contents into *where.
+ is_int is 1 if the contents are an int. */
+
+struct undo
+{
+ int is_int;
+ union {rtx r; int i;} old_contents;
+ union {rtx *r; int *i;} where;
+};
+
+/* Record a bunch of changes to be undone, up to MAX_UNDO of them.
+ num_undo says how many are currently recorded.
+
+ storage is nonzero if we must undo the allocation of new storage.
+ The value of storage is what to pass to obfree.
+
+ other_insn is nonzero if we have modified some other insn in the process
+ of working on subst_insn. It must be verified too. */
+
+#define MAX_UNDO 50
+
+struct undobuf
+{
+ int num_undo;
+ char *storage;
+ struct undo undo[MAX_UNDO];
+ rtx other_insn;
+};
+
+static struct undobuf undobuf;
+
+/* Substitute NEWVAL, an rtx expression, into INTO, a place in some
+ insn. The substitution can be undone by undo_all. If INTO is already
+ set to NEWVAL, do not record this change. Because computing NEWVAL might
+ also call SUBST, we have to compute it before we put anything into
+ the undo table. */
+
+#define SUBST(INTO, NEWVAL) \
+ do { rtx _new = (NEWVAL); \
+ if (undobuf.num_undo < MAX_UNDO) \
+ { \
+ undobuf.undo[undobuf.num_undo].is_int = 0; \
+ undobuf.undo[undobuf.num_undo].where.r = &INTO; \
+ undobuf.undo[undobuf.num_undo].old_contents.r = INTO; \
+ INTO = _new; \
+ if (undobuf.undo[undobuf.num_undo].old_contents.r != INTO) \
+ undobuf.num_undo++; \
+ } \
+ } while (0)
+
+/* Similar to SUBST, but NEWVAL is an int. INTO will normally be an XINT
+ expression.
+ Note that substitution for the value of a CONST_INT is not safe. */
+
+#define SUBST_INT(INTO, NEWVAL) \
+ do { if (undobuf.num_undo < MAX_UNDO) \
+{ \
+ undobuf.undo[undobuf.num_undo].is_int = 1; \
+ undobuf.undo[undobuf.num_undo].where.i = (int *) &INTO; \
+ undobuf.undo[undobuf.num_undo].old_contents.i = INTO; \
+ INTO = NEWVAL; \
+ if (undobuf.undo[undobuf.num_undo].old_contents.i != INTO) \
+ undobuf.num_undo++; \
+ } \
+ } while (0)
+
+/* Number of times the pseudo being substituted for
+ was found and replaced. */
+
+static int n_occurrences;
+
+static void init_reg_last_arrays PROTO(());
+static void setup_incoming_promotions PROTO(());
+static void set_nonzero_bits_and_sign_copies PROTO((rtx, rtx));
+static int can_combine_p PROTO((rtx, rtx, rtx, rtx, rtx *, rtx *));
+static int combinable_i3pat PROTO((rtx, rtx *, rtx, rtx, int, rtx *));
+static rtx try_combine PROTO((rtx, rtx, rtx));
+static void undo_all PROTO((void));
+static rtx *find_split_point PROTO((rtx *, rtx));
+static rtx subst PROTO((rtx, rtx, rtx, int, int));
+static rtx simplify_rtx PROTO((rtx, enum machine_mode, int, int));
+static rtx simplify_if_then_else PROTO((rtx));
+static rtx simplify_set PROTO((rtx));
+static rtx simplify_logical PROTO((rtx, int));
+static rtx expand_compound_operation PROTO((rtx));
+static rtx expand_field_assignment PROTO((rtx));
+static rtx make_extraction PROTO((enum machine_mode, rtx, int, rtx, int,
+ int, int, int));
+static rtx extract_left_shift PROTO((rtx, int));
+static rtx make_compound_operation PROTO((rtx, enum rtx_code));
+static int get_pos_from_mask PROTO((unsigned HOST_WIDE_INT, int *));
+static rtx force_to_mode PROTO((rtx, enum machine_mode,
+ unsigned HOST_WIDE_INT, rtx, int));
+static rtx if_then_else_cond PROTO((rtx, rtx *, rtx *));
+static rtx known_cond PROTO((rtx, enum rtx_code, rtx, rtx));
+static rtx make_field_assignment PROTO((rtx));
+static rtx apply_distributive_law PROTO((rtx));
+static rtx simplify_and_const_int PROTO((rtx, enum machine_mode, rtx,
+ unsigned HOST_WIDE_INT));
+static unsigned HOST_WIDE_INT nonzero_bits PROTO((rtx, enum machine_mode));
+static int num_sign_bit_copies PROTO((rtx, enum machine_mode));
+static int merge_outer_ops PROTO((enum rtx_code *, HOST_WIDE_INT *,
+ enum rtx_code, HOST_WIDE_INT,
+ enum machine_mode, int *));
+static rtx simplify_shift_const PROTO((rtx, enum rtx_code, enum machine_mode,
+ rtx, int));
+static int recog_for_combine PROTO((rtx *, rtx, rtx *));
+static rtx gen_lowpart_for_combine PROTO((enum machine_mode, rtx));
+static rtx gen_rtx_combine PVPROTO((enum rtx_code code, enum machine_mode mode,
+ ...));
+static rtx gen_binary PROTO((enum rtx_code, enum machine_mode,
+ rtx, rtx));
+static rtx gen_unary PROTO((enum rtx_code, enum machine_mode,
+ enum machine_mode, rtx));
+static enum rtx_code simplify_comparison PROTO((enum rtx_code, rtx *, rtx *));
+static int reversible_comparison_p PROTO((rtx));
+static void update_table_tick PROTO((rtx));
+static void record_value_for_reg PROTO((rtx, rtx, rtx));
+static void record_dead_and_set_regs_1 PROTO((rtx, rtx));
+static void record_dead_and_set_regs PROTO((rtx));
+static int get_last_value_validate PROTO((rtx *, int, int));
+static rtx get_last_value PROTO((rtx));
+static int use_crosses_set_p PROTO((rtx, int));
+static void reg_dead_at_p_1 PROTO((rtx, rtx));
+static int reg_dead_at_p PROTO((rtx, rtx));
+static void move_deaths PROTO((rtx, int, rtx, rtx *));
+static int reg_bitfield_target_p PROTO((rtx, rtx));
+static void distribute_notes PROTO((rtx, rtx, rtx, rtx, rtx, rtx));
+static void distribute_links PROTO((rtx));
+static void mark_used_regs_combine PROTO((rtx));
+
+/* Main entry point for combiner. F is the first insn of the function.
+ NREGS is the first unused pseudo-reg number. */
+
+void
+combine_instructions (f, nregs)
+ rtx f;
+ int nregs;
+{
+ register rtx insn, next, prev;
+ register int i;
+ register rtx links, nextlinks;
+
+ combine_attempts = 0;
+ combine_merges = 0;
+ combine_extras = 0;
+ combine_successes = 0;
+ undobuf.num_undo = previous_num_undos = 0;
+
+ combine_max_regno = nregs;
+
+ reg_nonzero_bits
+ = (unsigned HOST_WIDE_INT *) alloca (nregs * sizeof (HOST_WIDE_INT));
+ reg_sign_bit_copies = (char *) alloca (nregs * sizeof (char));
+
+ bzero ((char *) reg_nonzero_bits, nregs * sizeof (HOST_WIDE_INT));
+ bzero (reg_sign_bit_copies, nregs * sizeof (char));
+
+ reg_last_death = (rtx *) alloca (nregs * sizeof (rtx));
+ reg_last_set = (rtx *) alloca (nregs * sizeof (rtx));
+ reg_last_set_value = (rtx *) alloca (nregs * sizeof (rtx));
+ reg_last_set_table_tick = (int *) alloca (nregs * sizeof (int));
+ reg_last_set_label = (int *) alloca (nregs * sizeof (int));
+ reg_last_set_invalid = (char *) alloca (nregs * sizeof (char));
+ reg_last_set_mode
+ = (enum machine_mode *) alloca (nregs * sizeof (enum machine_mode));
+ reg_last_set_nonzero_bits
+ = (unsigned HOST_WIDE_INT *) alloca (nregs * sizeof (HOST_WIDE_INT));
+ reg_last_set_sign_bit_copies
+ = (char *) alloca (nregs * sizeof (char));
+
+ init_reg_last_arrays ();
+
+ init_recog_no_volatile ();
+
+ /* Compute maximum uid value so uid_cuid can be allocated. */
+
+ for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
+ if (INSN_UID (insn) > i)
+ i = INSN_UID (insn);
+
+ uid_cuid = (int *) alloca ((i + 1) * sizeof (int));
+
+ nonzero_bits_mode = mode_for_size (HOST_BITS_PER_WIDE_INT, MODE_INT, 0);
+
+ /* Don't use reg_nonzero_bits when computing it. This can cause problems
+ when, for example, we have j <<= 1 in a loop. */
+
+ nonzero_sign_valid = 0;
+
+ /* Compute the mapping from uids to cuids.
+ Cuids are numbers assigned to insns, like uids,
+ except that cuids increase monotonically through the code.
+
+ Scan all SETs and see if we can deduce anything about what
+ bits are known to be zero for some registers and how many copies
+ of the sign bit are known to exist for those registers.
+
+ Also set any known values so that we can use it while searching
+ for what bits are known to be set. */
+
+ label_tick = 1;
+
+ setup_incoming_promotions ();
+
+ for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
+ {
+ INSN_CUID (insn) = ++i;
+ subst_low_cuid = i;
+ subst_insn = insn;
+
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ note_stores (PATTERN (insn), set_nonzero_bits_and_sign_copies);
+ record_dead_and_set_regs (insn);
+ }
+
+ if (GET_CODE (insn) == CODE_LABEL)
+ label_tick++;
+ }
+
+ nonzero_sign_valid = 1;
+
+ /* Now scan all the insns in forward order. */
+
+ this_basic_block = -1;
+ label_tick = 1;
+ last_call_cuid = 0;
+ mem_last_set = 0;
+ init_reg_last_arrays ();
+ setup_incoming_promotions ();
+
+ for (insn = f; insn; insn = next ? next : NEXT_INSN (insn))
+ {
+ next = 0;
+
+ /* If INSN starts a new basic block, update our basic block number. */
+ if (this_basic_block + 1 < n_basic_blocks
+ && basic_block_head[this_basic_block + 1] == insn)
+ this_basic_block++;
+
+ if (GET_CODE (insn) == CODE_LABEL)
+ label_tick++;
+
+ else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ /* Try this insn with each insn it links back to. */
+
+ for (links = LOG_LINKS (insn); links; links = XEXP (links, 1))
+ if ((next = try_combine (insn, XEXP (links, 0), NULL_RTX)) != 0)
+ goto retry;
+
+ /* Try each sequence of three linked insns ending with this one. */
+
+ for (links = LOG_LINKS (insn); links; links = XEXP (links, 1))
+ for (nextlinks = LOG_LINKS (XEXP (links, 0)); nextlinks;
+ nextlinks = XEXP (nextlinks, 1))
+ if ((next = try_combine (insn, XEXP (links, 0),
+ XEXP (nextlinks, 0))) != 0)
+ goto retry;
+
+#ifdef HAVE_cc0
+ /* Try to combine a jump insn that uses CC0
+ with a preceding insn that sets CC0, and maybe with its
+ logical predecessor as well.
+ This is how we make decrement-and-branch insns.
+ We need this special code because data flow connections
+ via CC0 do not get entered in LOG_LINKS. */
+
+ if (GET_CODE (insn) == JUMP_INSN
+ && (prev = prev_nonnote_insn (insn)) != 0
+ && GET_CODE (prev) == INSN
+ && sets_cc0_p (PATTERN (prev)))
+ {
+ if ((next = try_combine (insn, prev, NULL_RTX)) != 0)
+ goto retry;
+
+ for (nextlinks = LOG_LINKS (prev); nextlinks;
+ nextlinks = XEXP (nextlinks, 1))
+ if ((next = try_combine (insn, prev,
+ XEXP (nextlinks, 0))) != 0)
+ goto retry;
+ }
+
+ /* Do the same for an insn that explicitly references CC0. */
+ if (GET_CODE (insn) == INSN
+ && (prev = prev_nonnote_insn (insn)) != 0
+ && GET_CODE (prev) == INSN
+ && sets_cc0_p (PATTERN (prev))
+ && GET_CODE (PATTERN (insn)) == SET
+ && reg_mentioned_p (cc0_rtx, SET_SRC (PATTERN (insn))))
+ {
+ if ((next = try_combine (insn, prev, NULL_RTX)) != 0)
+ goto retry;
+
+ for (nextlinks = LOG_LINKS (prev); nextlinks;
+ nextlinks = XEXP (nextlinks, 1))
+ if ((next = try_combine (insn, prev,
+ XEXP (nextlinks, 0))) != 0)
+ goto retry;
+ }
+
+ /* Finally, see if any of the insns that this insn links to
+ explicitly references CC0. If so, try this insn, that insn,
+ and its predecessor if it sets CC0. */
+ for (links = LOG_LINKS (insn); links; links = XEXP (links, 1))
+ if (GET_CODE (XEXP (links, 0)) == INSN
+ && GET_CODE (PATTERN (XEXP (links, 0))) == SET
+ && reg_mentioned_p (cc0_rtx, SET_SRC (PATTERN (XEXP (links, 0))))
+ && (prev = prev_nonnote_insn (XEXP (links, 0))) != 0
+ && GET_CODE (prev) == INSN
+ && sets_cc0_p (PATTERN (prev))
+ && (next = try_combine (insn, XEXP (links, 0), prev)) != 0)
+ goto retry;
+#endif
+
+ /* Try combining an insn with two different insns whose results it
+ uses. */
+ for (links = LOG_LINKS (insn); links; links = XEXP (links, 1))
+ for (nextlinks = XEXP (links, 1); nextlinks;
+ nextlinks = XEXP (nextlinks, 1))
+ if ((next = try_combine (insn, XEXP (links, 0),
+ XEXP (nextlinks, 0))) != 0)
+ goto retry;
+
+ if (GET_CODE (insn) != NOTE)
+ record_dead_and_set_regs (insn);
+
+ retry:
+ ;
+ }
+ }
+
+ total_attempts += combine_attempts;
+ total_merges += combine_merges;
+ total_extras += combine_extras;
+ total_successes += combine_successes;
+
+ nonzero_sign_valid = 0;
+}
+
+/* Wipe the reg_last_xxx arrays in preparation for another pass. */
+
+static void
+init_reg_last_arrays ()
+{
+ int nregs = combine_max_regno;
+
+ bzero ((char *) reg_last_death, nregs * sizeof (rtx));
+ bzero ((char *) reg_last_set, nregs * sizeof (rtx));
+ bzero ((char *) reg_last_set_value, nregs * sizeof (rtx));
+ bzero ((char *) reg_last_set_table_tick, nregs * sizeof (int));
+ bzero ((char *) reg_last_set_label, nregs * sizeof (int));
+ bzero (reg_last_set_invalid, nregs * sizeof (char));
+ bzero ((char *) reg_last_set_mode, nregs * sizeof (enum machine_mode));
+ bzero ((char *) reg_last_set_nonzero_bits, nregs * sizeof (HOST_WIDE_INT));
+ bzero (reg_last_set_sign_bit_copies, nregs * sizeof (char));
+}
+
+/* Set up any promoted values for incoming argument registers. */
+
+static void
+setup_incoming_promotions ()
+{
+#ifdef PROMOTE_FUNCTION_ARGS
+ int regno;
+ rtx reg;
+ enum machine_mode mode;
+ int unsignedp;
+ rtx first = get_insns ();
+
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (FUNCTION_ARG_REGNO_P (regno)
+ && (reg = promoted_input_arg (regno, &mode, &unsignedp)) != 0)
+ record_value_for_reg (reg, first,
+ gen_rtx (unsignedp ? ZERO_EXTEND : SIGN_EXTEND,
+ GET_MODE (reg),
+ gen_rtx (CLOBBER, mode, const0_rtx)));
+#endif
+}
+
+/* Called via note_stores. If X is a pseudo that is used in more than
+ one basic block, is narrower that HOST_BITS_PER_WIDE_INT, and is being
+ set, record what bits are known zero. If we are clobbering X,
+ ignore this "set" because the clobbered value won't be used.
+
+ If we are setting only a portion of X and we can't figure out what
+ portion, assume all bits will be used since we don't know what will
+ be happening.
+
+ Similarly, set how many bits of X are known to be copies of the sign bit
+ at all locations in the function. This is the smallest number implied
+ by any set of X. */
+
+static void
+set_nonzero_bits_and_sign_copies (x, set)
+ rtx x;
+ rtx set;
+{
+ int num;
+
+ if (GET_CODE (x) == REG
+ && REGNO (x) >= FIRST_PSEUDO_REGISTER
+ && reg_n_sets[REGNO (x)] > 1
+ && reg_basic_block[REGNO (x)] < 0
+ /* If this register is undefined at the start of the file, we can't
+ say what its contents were. */
+ && ! (basic_block_live_at_start[0][REGNO (x) / REGSET_ELT_BITS]
+ & ((REGSET_ELT_TYPE) 1 << (REGNO (x) % REGSET_ELT_BITS)))
+ && GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT)
+ {
+ if (GET_CODE (set) == CLOBBER)
+ {
+ reg_nonzero_bits[REGNO (x)] = GET_MODE_MASK (GET_MODE (x));
+ reg_sign_bit_copies[REGNO (x)] = 0;
+ return;
+ }
+
+ /* If this is a complex assignment, see if we can convert it into a
+ simple assignment. */
+ set = expand_field_assignment (set);
+
+ /* If this is a simple assignment, or we have a paradoxical SUBREG,
+ set what we know about X. */
+
+ if (SET_DEST (set) == x
+ || (GET_CODE (SET_DEST (set)) == SUBREG
+ && (GET_MODE_SIZE (GET_MODE (SET_DEST (set)))
+ > GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (set)))))
+ && SUBREG_REG (SET_DEST (set)) == x))
+ {
+ rtx src = SET_SRC (set);
+
+#ifdef SHORT_IMMEDIATES_SIGN_EXTEND
+ /* If X is narrower than a word and SRC is a non-negative
+ constant that would appear negative in the mode of X,
+ sign-extend it for use in reg_nonzero_bits because some
+ machines (maybe most) will actually do the sign-extension
+ and this is the conservative approach.
+
+ ??? For 2.5, try to tighten up the MD files in this regard
+ instead of this kludge. */
+
+ if (GET_MODE_BITSIZE (GET_MODE (x)) < BITS_PER_WORD
+ && GET_CODE (src) == CONST_INT
+ && INTVAL (src) > 0
+ && 0 != (INTVAL (src)
+ & ((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (GET_MODE (x)) - 1))))
+ src = GEN_INT (INTVAL (src)
+ | ((HOST_WIDE_INT) (-1)
+ << GET_MODE_BITSIZE (GET_MODE (x))));
+#endif
+
+ reg_nonzero_bits[REGNO (x)]
+ |= nonzero_bits (src, nonzero_bits_mode);
+ num = num_sign_bit_copies (SET_SRC (set), GET_MODE (x));
+ if (reg_sign_bit_copies[REGNO (x)] == 0
+ || reg_sign_bit_copies[REGNO (x)] > num)
+ reg_sign_bit_copies[REGNO (x)] = num;
+ }
+ else
+ {
+ reg_nonzero_bits[REGNO (x)] = GET_MODE_MASK (GET_MODE (x));
+ reg_sign_bit_copies[REGNO (x)] = 0;
+ }
+ }
+}
+
+/* See if INSN can be combined into I3. PRED and SUCC are optionally
+ insns that were previously combined into I3 or that will be combined
+ into the merger of INSN and I3.
+
+ Return 0 if the combination is not allowed for any reason.
+
+ If the combination is allowed, *PDEST will be set to the single
+ destination of INSN and *PSRC to the single source, and this function
+ will return 1. */
+
+static int
+can_combine_p (insn, i3, pred, succ, pdest, psrc)
+ rtx insn;
+ rtx i3;
+ rtx pred, succ;
+ rtx *pdest, *psrc;
+{
+ int i;
+ rtx set = 0, src, dest;
+ rtx p, link;
+ int all_adjacent = (succ ? (next_active_insn (insn) == succ
+ && next_active_insn (succ) == i3)
+ : next_active_insn (insn) == i3);
+
+ /* Can combine only if previous insn is a SET of a REG, a SUBREG or CC0.
+ or a PARALLEL consisting of such a SET and CLOBBERs.
+
+ If INSN has CLOBBER parallel parts, ignore them for our processing.
+ By definition, these happen during the execution of the insn. When it
+ is merged with another insn, all bets are off. If they are, in fact,
+ needed and aren't also supplied in I3, they may be added by
+ recog_for_combine. Otherwise, it won't match.
+
+ We can also ignore a SET whose SET_DEST is mentioned in a REG_UNUSED
+ note.
+
+ Get the source and destination of INSN. If more than one, can't
+ combine. */
+
+ if (GET_CODE (PATTERN (insn)) == SET)
+ set = PATTERN (insn);
+ else if (GET_CODE (PATTERN (insn)) == PARALLEL
+ && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET)
+ {
+ for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
+ {
+ rtx elt = XVECEXP (PATTERN (insn), 0, i);
+
+ switch (GET_CODE (elt))
+ {
+ /* We can ignore CLOBBERs. */
+ case CLOBBER:
+ break;
+
+ case SET:
+ /* Ignore SETs whose result isn't used but not those that
+ have side-effects. */
+ if (find_reg_note (insn, REG_UNUSED, SET_DEST (elt))
+ && ! side_effects_p (elt))
+ break;
+
+ /* If we have already found a SET, this is a second one and
+ so we cannot combine with this insn. */
+ if (set)
+ return 0;
+
+ set = elt;
+ break;
+
+ default:
+ /* Anything else means we can't combine. */
+ return 0;
+ }
+ }
+
+ if (set == 0
+ /* If SET_SRC is an ASM_OPERANDS we can't throw away these CLOBBERs,
+ so don't do anything with it. */
+ || GET_CODE (SET_SRC (set)) == ASM_OPERANDS)
+ return 0;
+ }
+ else
+ return 0;
+
+ if (set == 0)
+ return 0;
+
+ set = expand_field_assignment (set);
+ src = SET_SRC (set), dest = SET_DEST (set);
+
+ /* Don't eliminate a store in the stack pointer. */
+ if (dest == stack_pointer_rtx
+ /* If we couldn't eliminate a field assignment, we can't combine. */
+ || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == STRICT_LOW_PART
+ /* Don't combine with an insn that sets a register to itself if it has
+ a REG_EQUAL note. This may be part of a REG_NO_CONFLICT sequence. */
+ || (rtx_equal_p (src, dest) && find_reg_note (insn, REG_EQUAL, NULL_RTX))
+ /* Can't merge a function call. */
+ || GET_CODE (src) == CALL
+ /* Don't eliminate a function call argument. */
+ || (GET_CODE (i3) == CALL_INSN && find_reg_fusage (i3, USE, dest))
+ /* Don't substitute into an incremented register. */
+ || FIND_REG_INC_NOTE (i3, dest)
+ || (succ && FIND_REG_INC_NOTE (succ, dest))
+ /* Don't combine the end of a libcall into anything. */
+ || find_reg_note (insn, REG_RETVAL, NULL_RTX)
+ /* Make sure that DEST is not used after SUCC but before I3. */
+ || (succ && ! all_adjacent
+ && reg_used_between_p (dest, succ, i3))
+ /* Make sure that the value that is to be substituted for the register
+ does not use any registers whose values alter in between. However,
+ If the insns are adjacent, a use can't cross a set even though we
+ think it might (this can happen for a sequence of insns each setting
+ the same destination; reg_last_set of that register might point to
+ a NOTE). If INSN has a REG_EQUIV note, the register is always
+ equivalent to the memory so the substitution is valid even if there
+ are intervening stores. Also, don't move a volatile asm or
+ UNSPEC_VOLATILE across any other insns. */
+ || (! all_adjacent
+ && (((GET_CODE (src) != MEM
+ || ! find_reg_note (insn, REG_EQUIV, src))
+ && use_crosses_set_p (src, INSN_CUID (insn)))
+ || (GET_CODE (src) == ASM_OPERANDS && MEM_VOLATILE_P (src))
+ || GET_CODE (src) == UNSPEC_VOLATILE))
+ /* If there is a REG_NO_CONFLICT note for DEST in I3 or SUCC, we get
+ better register allocation by not doing the combine. */
+ || find_reg_note (i3, REG_NO_CONFLICT, dest)
+ || (succ && find_reg_note (succ, REG_NO_CONFLICT, dest))
+ /* Don't combine across a CALL_INSN, because that would possibly
+ change whether the life span of some REGs crosses calls or not,
+ and it is a pain to update that information.
+ Exception: if source is a constant, moving it later can't hurt.
+ Accept that special case, because it helps -fforce-addr a lot. */
+ || (INSN_CUID (insn) < last_call_cuid && ! CONSTANT_P (src)))
+ return 0;
+
+ /* DEST must either be a REG or CC0. */
+ if (GET_CODE (dest) == REG)
+ {
+ /* If register alignment is being enforced for multi-word items in all
+ cases except for parameters, it is possible to have a register copy
+ insn referencing a hard register that is not allowed to contain the
+ mode being copied and which would not be valid as an operand of most
+ insns. Eliminate this problem by not combining with such an insn.
+
+ Also, on some machines we don't want to extend the life of a hard
+ register. */
+
+ if (GET_CODE (src) == REG
+ && ((REGNO (dest) < FIRST_PSEUDO_REGISTER
+ && ! HARD_REGNO_MODE_OK (REGNO (dest), GET_MODE (dest)))
+ /* Don't extend the life of a hard register unless it is
+ user variable (if we have few registers) or it can't
+ fit into the desired register (meaning something special
+ is going on). */
+ || (REGNO (src) < FIRST_PSEUDO_REGISTER
+ && (! HARD_REGNO_MODE_OK (REGNO (src), GET_MODE (src))
+#ifdef SMALL_REGISTER_CLASSES
+ || ! REG_USERVAR_P (src)
+#endif
+ ))))
+ return 0;
+ }
+ else if (GET_CODE (dest) != CC0)
+ return 0;
+
+ /* Don't substitute for a register intended as a clobberable operand.
+ Similarly, don't substitute an expression containing a register that
+ will be clobbered in I3. */
+ if (GET_CODE (PATTERN (i3)) == PARALLEL)
+ for (i = XVECLEN (PATTERN (i3), 0) - 1; i >= 0; i--)
+ if (GET_CODE (XVECEXP (PATTERN (i3), 0, i)) == CLOBBER
+ && (reg_overlap_mentioned_p (XEXP (XVECEXP (PATTERN (i3), 0, i), 0),
+ src)
+ || rtx_equal_p (XEXP (XVECEXP (PATTERN (i3), 0, i), 0), dest)))
+ return 0;
+
+ /* If INSN contains anything volatile, or is an `asm' (whether volatile
+ or not), reject, unless nothing volatile comes between it and I3,
+ with the exception of SUCC. */
+
+ if (GET_CODE (src) == ASM_OPERANDS || volatile_refs_p (src))
+ for (p = NEXT_INSN (insn); p != i3; p = NEXT_INSN (p))
+ if (GET_RTX_CLASS (GET_CODE (p)) == 'i'
+ && p != succ && volatile_refs_p (PATTERN (p)))
+ return 0;
+
+ /* If there are any volatile insns between INSN and I3, reject, because
+ they might affect machine state. */
+
+ for (p = NEXT_INSN (insn); p != i3; p = NEXT_INSN (p))
+ if (GET_RTX_CLASS (GET_CODE (p)) == 'i'
+ && p != succ && volatile_insn_p (PATTERN (p)))
+ return 0;
+
+ /* If INSN or I2 contains an autoincrement or autodecrement,
+ make sure that register is not used between there and I3,
+ and not already used in I3 either.
+ Also insist that I3 not be a jump; if it were one
+ and the incremented register were spilled, we would lose. */
+
+#ifdef AUTO_INC_DEC
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_INC
+ && (GET_CODE (i3) == JUMP_INSN
+ || reg_used_between_p (XEXP (link, 0), insn, i3)
+ || reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i3))))
+ return 0;
+#endif
+
+#ifdef HAVE_cc0
+ /* Don't combine an insn that follows a CC0-setting insn.
+ An insn that uses CC0 must not be separated from the one that sets it.
+ We do, however, allow I2 to follow a CC0-setting insn if that insn
+ is passed as I1; in that case it will be deleted also.
+ We also allow combining in this case if all the insns are adjacent
+ because that would leave the two CC0 insns adjacent as well.
+ It would be more logical to test whether CC0 occurs inside I1 or I2,
+ but that would be much slower, and this ought to be equivalent. */
+
+ p = prev_nonnote_insn (insn);
+ if (p && p != pred && GET_CODE (p) == INSN && sets_cc0_p (PATTERN (p))
+ && ! all_adjacent)
+ return 0;
+#endif
+
+ /* If we get here, we have passed all the tests and the combination is
+ to be allowed. */
+
+ *pdest = dest;
+ *psrc = src;
+
+ return 1;
+}
+
+/* LOC is the location within I3 that contains its pattern or the component
+ of a PARALLEL of the pattern. We validate that it is valid for combining.
+
+ One problem is if I3 modifies its output, as opposed to replacing it
+ entirely, we can't allow the output to contain I2DEST or I1DEST as doing
+ so would produce an insn that is not equivalent to the original insns.
+
+ Consider:
+
+ (set (reg:DI 101) (reg:DI 100))
+ (set (subreg:SI (reg:DI 101) 0) <foo>)
+
+ This is NOT equivalent to:
+
+ (parallel [(set (subreg:SI (reg:DI 100) 0) <foo>)
+ (set (reg:DI 101) (reg:DI 100))])
+
+ Not only does this modify 100 (in which case it might still be valid
+ if 100 were dead in I2), it sets 101 to the ORIGINAL value of 100.
+
+ We can also run into a problem if I2 sets a register that I1
+ uses and I1 gets directly substituted into I3 (not via I2). In that
+ case, we would be getting the wrong value of I2DEST into I3, so we
+ must reject the combination. This case occurs when I2 and I1 both
+ feed into I3, rather than when I1 feeds into I2, which feeds into I3.
+ If I1_NOT_IN_SRC is non-zero, it means that finding I1 in the source
+ of a SET must prevent combination from occurring.
+
+ On machines where SMALL_REGISTER_CLASSES is defined, we don't combine
+ if the destination of a SET is a hard register that isn't a user
+ variable.
+
+ Before doing the above check, we first try to expand a field assignment
+ into a set of logical operations.
+
+ If PI3_DEST_KILLED is non-zero, it is a pointer to a location in which
+ we place a register that is both set and used within I3. If more than one
+ such register is detected, we fail.
+
+ Return 1 if the combination is valid, zero otherwise. */
+
+static int
+combinable_i3pat (i3, loc, i2dest, i1dest, i1_not_in_src, pi3dest_killed)
+ rtx i3;
+ rtx *loc;
+ rtx i2dest;
+ rtx i1dest;
+ int i1_not_in_src;
+ rtx *pi3dest_killed;
+{
+ rtx x = *loc;
+
+ if (GET_CODE (x) == SET)
+ {
+ rtx set = expand_field_assignment (x);
+ rtx dest = SET_DEST (set);
+ rtx src = SET_SRC (set);
+ rtx inner_dest = dest, inner_src = src;
+
+ SUBST (*loc, set);
+
+ while (GET_CODE (inner_dest) == STRICT_LOW_PART
+ || GET_CODE (inner_dest) == SUBREG
+ || GET_CODE (inner_dest) == ZERO_EXTRACT)
+ inner_dest = XEXP (inner_dest, 0);
+
+ /* We probably don't need this any more now that LIMIT_RELOAD_CLASS
+ was added. */
+#if 0
+ while (GET_CODE (inner_src) == STRICT_LOW_PART
+ || GET_CODE (inner_src) == SUBREG
+ || GET_CODE (inner_src) == ZERO_EXTRACT)
+ inner_src = XEXP (inner_src, 0);
+
+ /* If it is better that two different modes keep two different pseudos,
+ avoid combining them. This avoids producing the following pattern
+ on a 386:
+ (set (subreg:SI (reg/v:QI 21) 0)
+ (lshiftrt:SI (reg/v:SI 20)
+ (const_int 24)))
+ If that were made, reload could not handle the pair of
+ reg 20/21, since it would try to get any GENERAL_REGS
+ but some of them don't handle QImode. */
+
+ if (rtx_equal_p (inner_src, i2dest)
+ && GET_CODE (inner_dest) == REG
+ && ! MODES_TIEABLE_P (GET_MODE (i2dest), GET_MODE (inner_dest)))
+ return 0;
+#endif
+
+ /* Check for the case where I3 modifies its output, as
+ discussed above. */
+ if ((inner_dest != dest
+ && (reg_overlap_mentioned_p (i2dest, inner_dest)
+ || (i1dest && reg_overlap_mentioned_p (i1dest, inner_dest))))
+ /* This is the same test done in can_combine_p except that we
+ allow a hard register with SMALL_REGISTER_CLASSES if SRC is a
+ CALL operation. */
+ || (GET_CODE (inner_dest) == REG
+ && REGNO (inner_dest) < FIRST_PSEUDO_REGISTER
+ && (! HARD_REGNO_MODE_OK (REGNO (inner_dest),
+ GET_MODE (inner_dest))
+#ifdef SMALL_REGISTER_CLASSES
+ || (GET_CODE (src) != CALL && ! REG_USERVAR_P (inner_dest))
+#endif
+ ))
+ || (i1_not_in_src && reg_overlap_mentioned_p (i1dest, src)))
+ return 0;
+
+ /* If DEST is used in I3, it is being killed in this insn,
+ so record that for later.
+ Never add REG_DEAD notes for the FRAME_POINTER_REGNUM or the
+ STACK_POINTER_REGNUM, since these are always considered to be
+ live. Similarly for ARG_POINTER_REGNUM if it is fixed. */
+ if (pi3dest_killed && GET_CODE (dest) == REG
+ && reg_referenced_p (dest, PATTERN (i3))
+ && REGNO (dest) != FRAME_POINTER_REGNUM
+#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ && REGNO (dest) != HARD_FRAME_POINTER_REGNUM
+#endif
+#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ && (REGNO (dest) != ARG_POINTER_REGNUM
+ || ! fixed_regs [REGNO (dest)])
+#endif
+ && REGNO (dest) != STACK_POINTER_REGNUM)
+ {
+ if (*pi3dest_killed)
+ return 0;
+
+ *pi3dest_killed = dest;
+ }
+ }
+
+ else if (GET_CODE (x) == PARALLEL)
+ {
+ int i;
+
+ for (i = 0; i < XVECLEN (x, 0); i++)
+ if (! combinable_i3pat (i3, &XVECEXP (x, 0, i), i2dest, i1dest,
+ i1_not_in_src, pi3dest_killed))
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Try to combine the insns I1 and I2 into I3.
+ Here I1 and I2 appear earlier than I3.
+ I1 can be zero; then we combine just I2 into I3.
+
+ It we are combining three insns and the resulting insn is not recognized,
+ try splitting it into two insns. If that happens, I2 and I3 are retained
+ and I1 is pseudo-deleted by turning it into a NOTE. Otherwise, I1 and I2
+ are pseudo-deleted.
+
+ Return 0 if the combination does not work. Then nothing is changed.
+ If we did the combination, return the insn at which combine should
+ resume scanning. */
+
+static rtx
+try_combine (i3, i2, i1)
+ register rtx i3, i2, i1;
+{
+ /* New patterns for I3 and I3, respectively. */
+ rtx newpat, newi2pat = 0;
+ /* Indicates need to preserve SET in I1 or I2 in I3 if it is not dead. */
+ int added_sets_1, added_sets_2;
+ /* Total number of SETs to put into I3. */
+ int total_sets;
+ /* Nonzero is I2's body now appears in I3. */
+ int i2_is_used;
+ /* INSN_CODEs for new I3, new I2, and user of condition code. */
+ int insn_code_number, i2_code_number, other_code_number;
+ /* Contains I3 if the destination of I3 is used in its source, which means
+ that the old life of I3 is being killed. If that usage is placed into
+ I2 and not in I3, a REG_DEAD note must be made. */
+ rtx i3dest_killed = 0;
+ /* SET_DEST and SET_SRC of I2 and I1. */
+ rtx i2dest, i2src, i1dest = 0, i1src = 0;
+ /* PATTERN (I2), or a copy of it in certain cases. */
+ rtx i2pat;
+ /* Indicates if I2DEST or I1DEST is in I2SRC or I1_SRC. */
+ int i2dest_in_i2src = 0, i1dest_in_i1src = 0, i2dest_in_i1src = 0;
+ int i1_feeds_i3 = 0;
+ /* Notes that must be added to REG_NOTES in I3 and I2. */
+ rtx new_i3_notes, new_i2_notes;
+ /* Notes that we substituted I3 into I2 instead of the normal case. */
+ int i3_subst_into_i2 = 0;
+
+ int maxreg;
+ rtx temp;
+ register rtx link;
+ int i;
+
+ /* If any of I1, I2, and I3 isn't really an insn, we can't do anything.
+ This can occur when flow deletes an insn that it has merged into an
+ auto-increment address. We also can't do anything if I3 has a
+ REG_LIBCALL note since we don't want to disrupt the contiguity of a
+ libcall. */
+
+ if (GET_RTX_CLASS (GET_CODE (i3)) != 'i'
+ || GET_RTX_CLASS (GET_CODE (i2)) != 'i'
+ || (i1 && GET_RTX_CLASS (GET_CODE (i1)) != 'i')
+ || find_reg_note (i3, REG_LIBCALL, NULL_RTX))
+ return 0;
+
+ combine_attempts++;
+
+ undobuf.num_undo = previous_num_undos = 0;
+ undobuf.other_insn = 0;
+
+ /* Save the current high-water-mark so we can free storage if we didn't
+ accept this combination. */
+ undobuf.storage = (char *) oballoc (0);
+
+ /* Reset the hard register usage information. */
+ CLEAR_HARD_REG_SET (newpat_used_regs);
+
+ /* If I1 and I2 both feed I3, they can be in any order. To simplify the
+ code below, set I1 to be the earlier of the two insns. */
+ if (i1 && INSN_CUID (i1) > INSN_CUID (i2))
+ temp = i1, i1 = i2, i2 = temp;
+
+ subst_prev_insn = 0;
+ added_links_insn = 0;
+
+ /* First check for one important special-case that the code below will
+ not handle. Namely, the case where I1 is zero, I2 has multiple sets,
+ and I3 is a SET whose SET_SRC is a SET_DEST in I2. In that case,
+ we may be able to replace that destination with the destination of I3.
+ This occurs in the common code where we compute both a quotient and
+ remainder into a structure, in which case we want to do the computation
+ directly into the structure to avoid register-register copies.
+
+ We make very conservative checks below and only try to handle the
+ most common cases of this. For example, we only handle the case
+ where I2 and I3 are adjacent to avoid making difficult register
+ usage tests. */
+
+ if (i1 == 0 && GET_CODE (i3) == INSN && GET_CODE (PATTERN (i3)) == SET
+ && GET_CODE (SET_SRC (PATTERN (i3))) == REG
+ && REGNO (SET_SRC (PATTERN (i3))) >= FIRST_PSEUDO_REGISTER
+#ifdef SMALL_REGISTER_CLASSES
+ && (GET_CODE (SET_DEST (PATTERN (i3))) != REG
+ || REGNO (SET_DEST (PATTERN (i3))) >= FIRST_PSEUDO_REGISTER
+ || REG_USERVAR_P (SET_DEST (PATTERN (i3))))
+#endif
+ && find_reg_note (i3, REG_DEAD, SET_SRC (PATTERN (i3)))
+ && GET_CODE (PATTERN (i2)) == PARALLEL
+ && ! side_effects_p (SET_DEST (PATTERN (i3)))
+ /* If the dest of I3 is a ZERO_EXTRACT or STRICT_LOW_PART, the code
+ below would need to check what is inside (and reg_overlap_mentioned_p
+ doesn't support those codes anyway). Don't allow those destinations;
+ the resulting insn isn't likely to be recognized anyway. */
+ && GET_CODE (SET_DEST (PATTERN (i3))) != ZERO_EXTRACT
+ && GET_CODE (SET_DEST (PATTERN (i3))) != STRICT_LOW_PART
+ && ! reg_overlap_mentioned_p (SET_SRC (PATTERN (i3)),
+ SET_DEST (PATTERN (i3)))
+ && next_real_insn (i2) == i3)
+ {
+ rtx p2 = PATTERN (i2);
+
+ /* Make sure that the destination of I3,
+ which we are going to substitute into one output of I2,
+ is not used within another output of I2. We must avoid making this:
+ (parallel [(set (mem (reg 69)) ...)
+ (set (reg 69) ...)])
+ which is not well-defined as to order of actions.
+ (Besides, reload can't handle output reloads for this.)
+
+ The problem can also happen if the dest of I3 is a memory ref,
+ if another dest in I2 is an indirect memory ref. */
+ for (i = 0; i < XVECLEN (p2, 0); i++)
+ if (GET_CODE (XVECEXP (p2, 0, i)) == SET
+ && reg_overlap_mentioned_p (SET_DEST (PATTERN (i3)),
+ SET_DEST (XVECEXP (p2, 0, i))))
+ break;
+
+ if (i == XVECLEN (p2, 0))
+ for (i = 0; i < XVECLEN (p2, 0); i++)
+ if (SET_DEST (XVECEXP (p2, 0, i)) == SET_SRC (PATTERN (i3)))
+ {
+ combine_merges++;
+
+ subst_insn = i3;
+ subst_low_cuid = INSN_CUID (i2);
+
+ added_sets_2 = added_sets_1 = 0;
+ i2dest = SET_SRC (PATTERN (i3));
+
+ /* Replace the dest in I2 with our dest and make the resulting
+ insn the new pattern for I3. Then skip to where we
+ validate the pattern. Everything was set up above. */
+ SUBST (SET_DEST (XVECEXP (p2, 0, i)),
+ SET_DEST (PATTERN (i3)));
+
+ newpat = p2;
+ i3_subst_into_i2 = 1;
+ goto validate_replacement;
+ }
+ }
+
+#ifndef HAVE_cc0
+ /* If we have no I1 and I2 looks like:
+ (parallel [(set (reg:CC X) (compare:CC OP (const_int 0)))
+ (set Y OP)])
+ make up a dummy I1 that is
+ (set Y OP)
+ and change I2 to be
+ (set (reg:CC X) (compare:CC Y (const_int 0)))
+
+ (We can ignore any trailing CLOBBERs.)
+
+ This undoes a previous combination and allows us to match a branch-and-
+ decrement insn. */
+
+ if (i1 == 0 && GET_CODE (PATTERN (i2)) == PARALLEL
+ && XVECLEN (PATTERN (i2), 0) >= 2
+ && GET_CODE (XVECEXP (PATTERN (i2), 0, 0)) == SET
+ && (GET_MODE_CLASS (GET_MODE (SET_DEST (XVECEXP (PATTERN (i2), 0, 0))))
+ == MODE_CC)
+ && GET_CODE (SET_SRC (XVECEXP (PATTERN (i2), 0, 0))) == COMPARE
+ && XEXP (SET_SRC (XVECEXP (PATTERN (i2), 0, 0)), 1) == const0_rtx
+ && GET_CODE (XVECEXP (PATTERN (i2), 0, 1)) == SET
+ && GET_CODE (SET_DEST (XVECEXP (PATTERN (i2), 0, 1))) == REG
+ && rtx_equal_p (XEXP (SET_SRC (XVECEXP (PATTERN (i2), 0, 0)), 0),
+ SET_SRC (XVECEXP (PATTERN (i2), 0, 1))))
+ {
+ for (i = XVECLEN (PATTERN (i2), 0) - 1; i >= 2; i--)
+ if (GET_CODE (XVECEXP (PATTERN (i2), 0, i)) != CLOBBER)
+ break;
+
+ if (i == 1)
+ {
+ /* We make I1 with the same INSN_UID as I2. This gives it
+ the same INSN_CUID for value tracking. Our fake I1 will
+ never appear in the insn stream so giving it the same INSN_UID
+ as I2 will not cause a problem. */
+
+ subst_prev_insn = i1
+ = gen_rtx (INSN, VOIDmode, INSN_UID (i2), 0, i2,
+ XVECEXP (PATTERN (i2), 0, 1), -1, 0, 0);
+
+ SUBST (PATTERN (i2), XVECEXP (PATTERN (i2), 0, 0));
+ SUBST (XEXP (SET_SRC (PATTERN (i2)), 0),
+ SET_DEST (PATTERN (i1)));
+ }
+ }
+#endif
+
+ /* Verify that I2 and I1 are valid for combining. */
+ if (! can_combine_p (i2, i3, i1, NULL_RTX, &i2dest, &i2src)
+ || (i1 && ! can_combine_p (i1, i3, NULL_RTX, i2, &i1dest, &i1src)))
+ {
+ undo_all ();
+ return 0;
+ }
+
+ /* Record whether I2DEST is used in I2SRC and similarly for the other
+ cases. Knowing this will help in register status updating below. */
+ i2dest_in_i2src = reg_overlap_mentioned_p (i2dest, i2src);
+ i1dest_in_i1src = i1 && reg_overlap_mentioned_p (i1dest, i1src);
+ i2dest_in_i1src = i1 && reg_overlap_mentioned_p (i2dest, i1src);
+
+ /* See if I1 directly feeds into I3. It does if I1DEST is not used
+ in I2SRC. */
+ i1_feeds_i3 = i1 && ! reg_overlap_mentioned_p (i1dest, i2src);
+
+ /* Ensure that I3's pattern can be the destination of combines. */
+ if (! combinable_i3pat (i3, &PATTERN (i3), i2dest, i1dest,
+ i1 && i2dest_in_i1src && i1_feeds_i3,
+ &i3dest_killed))
+ {
+ undo_all ();
+ return 0;
+ }
+
+ /* If I3 has an inc, then give up if I1 or I2 uses the reg that is inc'd.
+ We used to do this EXCEPT in one case: I3 has a post-inc in an
+ output operand. However, that exception can give rise to insns like
+ mov r3,(r3)+
+ which is a famous insn on the PDP-11 where the value of r3 used as the
+ source was model-dependent. Avoid this sort of thing. */
+
+#if 0
+ if (!(GET_CODE (PATTERN (i3)) == SET
+ && GET_CODE (SET_SRC (PATTERN (i3))) == REG
+ && GET_CODE (SET_DEST (PATTERN (i3))) == MEM
+ && (GET_CODE (XEXP (SET_DEST (PATTERN (i3)), 0)) == POST_INC
+ || GET_CODE (XEXP (SET_DEST (PATTERN (i3)), 0)) == POST_DEC)))
+ /* It's not the exception. */
+#endif
+#ifdef AUTO_INC_DEC
+ for (link = REG_NOTES (i3); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_INC
+ && (reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i2))
+ || (i1 != 0
+ && reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i1)))))
+ {
+ undo_all ();
+ return 0;
+ }
+#endif
+
+ /* See if the SETs in I1 or I2 need to be kept around in the merged
+ instruction: whenever the value set there is still needed past I3.
+ For the SETs in I2, this is easy: we see if I2DEST dies or is set in I3.
+
+ For the SET in I1, we have two cases: If I1 and I2 independently
+ feed into I3, the set in I1 needs to be kept around if I1DEST dies
+ or is set in I3. Otherwise (if I1 feeds I2 which feeds I3), the set
+ in I1 needs to be kept around unless I1DEST dies or is set in either
+ I2 or I3. We can distinguish these cases by seeing if I2SRC mentions
+ I1DEST. If so, we know I1 feeds into I2. */
+
+ added_sets_2 = ! dead_or_set_p (i3, i2dest);
+
+ added_sets_1
+ = i1 && ! (i1_feeds_i3 ? dead_or_set_p (i3, i1dest)
+ : (dead_or_set_p (i3, i1dest) || dead_or_set_p (i2, i1dest)));
+
+ /* If the set in I2 needs to be kept around, we must make a copy of
+ PATTERN (I2), so that when we substitute I1SRC for I1DEST in
+ PATTERN (I2), we are only substituting for the original I1DEST, not into
+ an already-substituted copy. This also prevents making self-referential
+ rtx. If I2 is a PARALLEL, we just need the piece that assigns I2SRC to
+ I2DEST. */
+
+ i2pat = (GET_CODE (PATTERN (i2)) == PARALLEL
+ ? gen_rtx (SET, VOIDmode, i2dest, i2src)
+ : PATTERN (i2));
+
+ if (added_sets_2)
+ i2pat = copy_rtx (i2pat);
+
+ combine_merges++;
+
+ /* Substitute in the latest insn for the regs set by the earlier ones. */
+
+ maxreg = max_reg_num ();
+
+ subst_insn = i3;
+
+ /* It is possible that the source of I2 or I1 may be performing an
+ unneeded operation, such as a ZERO_EXTEND of something that is known
+ to have the high part zero. Handle that case by letting subst look at
+ the innermost one of them.
+
+ Another way to do this would be to have a function that tries to
+ simplify a single insn instead of merging two or more insns. We don't
+ do this because of the potential of infinite loops and because
+ of the potential extra memory required. However, doing it the way
+ we are is a bit of a kludge and doesn't catch all cases.
+
+ But only do this if -fexpensive-optimizations since it slows things down
+ and doesn't usually win. */
+
+ if (flag_expensive_optimizations)
+ {
+ /* Pass pc_rtx so no substitutions are done, just simplifications.
+ The cases that we are interested in here do not involve the few
+ cases were is_replaced is checked. */
+ if (i1)
+ {
+ subst_low_cuid = INSN_CUID (i1);
+ i1src = subst (i1src, pc_rtx, pc_rtx, 0, 0);
+ }
+ else
+ {
+ subst_low_cuid = INSN_CUID (i2);
+ i2src = subst (i2src, pc_rtx, pc_rtx, 0, 0);
+ }
+
+ previous_num_undos = undobuf.num_undo;
+ }
+
+#ifndef HAVE_cc0
+ /* Many machines that don't use CC0 have insns that can both perform an
+ arithmetic operation and set the condition code. These operations will
+ be represented as a PARALLEL with the first element of the vector
+ being a COMPARE of an arithmetic operation with the constant zero.
+ The second element of the vector will set some pseudo to the result
+ of the same arithmetic operation. If we simplify the COMPARE, we won't
+ match such a pattern and so will generate an extra insn. Here we test
+ for this case, where both the comparison and the operation result are
+ needed, and make the PARALLEL by just replacing I2DEST in I3SRC with
+ I2SRC. Later we will make the PARALLEL that contains I2. */
+
+ if (i1 == 0 && added_sets_2 && GET_CODE (PATTERN (i3)) == SET
+ && GET_CODE (SET_SRC (PATTERN (i3))) == COMPARE
+ && XEXP (SET_SRC (PATTERN (i3)), 1) == const0_rtx
+ && rtx_equal_p (XEXP (SET_SRC (PATTERN (i3)), 0), i2dest))
+ {
+ rtx *cc_use;
+ enum machine_mode compare_mode;
+
+ newpat = PATTERN (i3);
+ SUBST (XEXP (SET_SRC (newpat), 0), i2src);
+
+ i2_is_used = 1;
+
+#ifdef EXTRA_CC_MODES
+ /* See if a COMPARE with the operand we substituted in should be done
+ with the mode that is currently being used. If not, do the same
+ processing we do in `subst' for a SET; namely, if the destination
+ is used only once, try to replace it with a register of the proper
+ mode and also replace the COMPARE. */
+ if (undobuf.other_insn == 0
+ && (cc_use = find_single_use (SET_DEST (newpat), i3,
+ &undobuf.other_insn))
+ && ((compare_mode = SELECT_CC_MODE (GET_CODE (*cc_use),
+ i2src, const0_rtx))
+ != GET_MODE (SET_DEST (newpat))))
+ {
+ int regno = REGNO (SET_DEST (newpat));
+ rtx new_dest = gen_rtx (REG, compare_mode, regno);
+
+ if (regno < FIRST_PSEUDO_REGISTER
+ || (reg_n_sets[regno] == 1 && ! added_sets_2
+ && ! REG_USERVAR_P (SET_DEST (newpat))))
+ {
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ SUBST (regno_reg_rtx[regno], new_dest);
+
+ SUBST (SET_DEST (newpat), new_dest);
+ SUBST (XEXP (*cc_use, 0), new_dest);
+ SUBST (SET_SRC (newpat),
+ gen_rtx_combine (COMPARE, compare_mode,
+ i2src, const0_rtx));
+ }
+ else
+ undobuf.other_insn = 0;
+ }
+#endif
+ }
+ else
+#endif
+ {
+ n_occurrences = 0; /* `subst' counts here */
+
+ /* If I1 feeds into I2 (not into I3) and I1DEST is in I1SRC, we
+ need to make a unique copy of I2SRC each time we substitute it
+ to avoid self-referential rtl. */
+
+ subst_low_cuid = INSN_CUID (i2);
+ newpat = subst (PATTERN (i3), i2dest, i2src, 0,
+ ! i1_feeds_i3 && i1dest_in_i1src);
+ previous_num_undos = undobuf.num_undo;
+
+ /* Record whether i2's body now appears within i3's body. */
+ i2_is_used = n_occurrences;
+ }
+
+ /* If we already got a failure, don't try to do more. Otherwise,
+ try to substitute in I1 if we have it. */
+
+ if (i1 && GET_CODE (newpat) != CLOBBER)
+ {
+ /* Before we can do this substitution, we must redo the test done
+ above (see detailed comments there) that ensures that I1DEST
+ isn't mentioned in any SETs in NEWPAT that are field assignments. */
+
+ if (! combinable_i3pat (NULL_RTX, &newpat, i1dest, NULL_RTX,
+ 0, NULL_PTR))
+ {
+ undo_all ();
+ return 0;
+ }
+
+ n_occurrences = 0;
+ subst_low_cuid = INSN_CUID (i1);
+ newpat = subst (newpat, i1dest, i1src, 0, 0);
+ previous_num_undos = undobuf.num_undo;
+ }
+
+ /* Fail if an autoincrement side-effect has been duplicated. Be careful
+ to count all the ways that I2SRC and I1SRC can be used. */
+ if ((FIND_REG_INC_NOTE (i2, NULL_RTX) != 0
+ && i2_is_used + added_sets_2 > 1)
+ || (i1 != 0 && FIND_REG_INC_NOTE (i1, NULL_RTX) != 0
+ && (n_occurrences + added_sets_1 + (added_sets_2 && ! i1_feeds_i3)
+ > 1))
+ /* Fail if we tried to make a new register (we used to abort, but there's
+ really no reason to). */
+ || max_reg_num () != maxreg
+ /* Fail if we couldn't do something and have a CLOBBER. */
+ || GET_CODE (newpat) == CLOBBER)
+ {
+ undo_all ();
+ return 0;
+ }
+
+ /* If the actions of the earlier insns must be kept
+ in addition to substituting them into the latest one,
+ we must make a new PARALLEL for the latest insn
+ to hold additional the SETs. */
+
+ if (added_sets_1 || added_sets_2)
+ {
+ combine_extras++;
+
+ if (GET_CODE (newpat) == PARALLEL)
+ {
+ rtvec old = XVEC (newpat, 0);
+ total_sets = XVECLEN (newpat, 0) + added_sets_1 + added_sets_2;
+ newpat = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (total_sets));
+ bcopy ((char *) &old->elem[0], (char *) &XVECEXP (newpat, 0, 0),
+ sizeof (old->elem[0]) * old->num_elem);
+ }
+ else
+ {
+ rtx old = newpat;
+ total_sets = 1 + added_sets_1 + added_sets_2;
+ newpat = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (total_sets));
+ XVECEXP (newpat, 0, 0) = old;
+ }
+
+ if (added_sets_1)
+ XVECEXP (newpat, 0, --total_sets)
+ = (GET_CODE (PATTERN (i1)) == PARALLEL
+ ? gen_rtx (SET, VOIDmode, i1dest, i1src) : PATTERN (i1));
+
+ if (added_sets_2)
+ {
+ /* If there is no I1, use I2's body as is. We used to also not do
+ the subst call below if I2 was substituted into I3,
+ but that could lose a simplification. */
+ if (i1 == 0)
+ XVECEXP (newpat, 0, --total_sets) = i2pat;
+ else
+ /* See comment where i2pat is assigned. */
+ XVECEXP (newpat, 0, --total_sets)
+ = subst (i2pat, i1dest, i1src, 0, 0);
+ }
+ }
+
+ /* We come here when we are replacing a destination in I2 with the
+ destination of I3. */
+ validate_replacement:
+
+ /* Note which hard regs this insn has as inputs. */
+ mark_used_regs_combine (newpat);
+
+ /* Is the result of combination a valid instruction? */
+ insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
+
+ /* If the result isn't valid, see if it is a PARALLEL of two SETs where
+ the second SET's destination is a register that is unused. In that case,
+ we just need the first SET. This can occur when simplifying a divmod
+ insn. We *must* test for this case here because the code below that
+ splits two independent SETs doesn't handle this case correctly when it
+ updates the register status. Also check the case where the first
+ SET's destination is unused. That would not cause incorrect code, but
+ does cause an unneeded insn to remain. */
+
+ if (insn_code_number < 0 && GET_CODE (newpat) == PARALLEL
+ && XVECLEN (newpat, 0) == 2
+ && GET_CODE (XVECEXP (newpat, 0, 0)) == SET
+ && GET_CODE (XVECEXP (newpat, 0, 1)) == SET
+ && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) == REG
+ && find_reg_note (i3, REG_UNUSED, SET_DEST (XVECEXP (newpat, 0, 1)))
+ && ! side_effects_p (SET_SRC (XVECEXP (newpat, 0, 1)))
+ && asm_noperands (newpat) < 0)
+ {
+ newpat = XVECEXP (newpat, 0, 0);
+ insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
+ }
+
+ else if (insn_code_number < 0 && GET_CODE (newpat) == PARALLEL
+ && XVECLEN (newpat, 0) == 2
+ && GET_CODE (XVECEXP (newpat, 0, 0)) == SET
+ && GET_CODE (XVECEXP (newpat, 0, 1)) == SET
+ && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) == REG
+ && find_reg_note (i3, REG_UNUSED, SET_DEST (XVECEXP (newpat, 0, 0)))
+ && ! side_effects_p (SET_SRC (XVECEXP (newpat, 0, 0)))
+ && asm_noperands (newpat) < 0)
+ {
+ newpat = XVECEXP (newpat, 0, 1);
+ insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
+ }
+
+ /* If we were combining three insns and the result is a simple SET
+ with no ASM_OPERANDS that wasn't recognized, try to split it into two
+ insns. There are two ways to do this. It can be split using a
+ machine-specific method (like when you have an addition of a large
+ constant) or by combine in the function find_split_point. */
+
+ if (i1 && insn_code_number < 0 && GET_CODE (newpat) == SET
+ && asm_noperands (newpat) < 0)
+ {
+ rtx m_split, *split;
+ rtx ni2dest = i2dest;
+
+ /* See if the MD file can split NEWPAT. If it can't, see if letting it
+ use I2DEST as a scratch register will help. In the latter case,
+ convert I2DEST to the mode of the source of NEWPAT if we can. */
+
+ m_split = split_insns (newpat, i3);
+
+ /* We can only use I2DEST as a scratch reg if it doesn't overlap any
+ inputs of NEWPAT. */
+
+ /* ??? If I2DEST is not safe, and I1DEST exists, then it would be
+ possible to try that as a scratch reg. This would require adding
+ more code to make it work though. */
+
+ if (m_split == 0 && ! reg_overlap_mentioned_p (ni2dest, newpat))
+ {
+ /* If I2DEST is a hard register or the only use of a pseudo,
+ we can change its mode. */
+ if (GET_MODE (SET_DEST (newpat)) != GET_MODE (i2dest)
+ && GET_MODE (SET_DEST (newpat)) != VOIDmode
+ && GET_CODE (i2dest) == REG
+ && (REGNO (i2dest) < FIRST_PSEUDO_REGISTER
+ || (reg_n_sets[REGNO (i2dest)] == 1 && ! added_sets_2
+ && ! REG_USERVAR_P (i2dest))))
+ ni2dest = gen_rtx (REG, GET_MODE (SET_DEST (newpat)),
+ REGNO (i2dest));
+
+ m_split = split_insns (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (2, newpat,
+ gen_rtx (CLOBBER,
+ VOIDmode,
+ ni2dest))),
+ i3);
+ }
+
+ if (m_split && GET_CODE (m_split) == SEQUENCE
+ && XVECLEN (m_split, 0) == 2
+ && (next_real_insn (i2) == i3
+ || ! use_crosses_set_p (PATTERN (XVECEXP (m_split, 0, 0)),
+ INSN_CUID (i2))))
+ {
+ rtx i2set, i3set;
+ rtx newi3pat = PATTERN (XVECEXP (m_split, 0, 1));
+ newi2pat = PATTERN (XVECEXP (m_split, 0, 0));
+
+ i3set = single_set (XVECEXP (m_split, 0, 1));
+ i2set = single_set (XVECEXP (m_split, 0, 0));
+
+ /* In case we changed the mode of I2DEST, replace it in the
+ pseudo-register table here. We can't do it above in case this
+ code doesn't get executed and we do a split the other way. */
+
+ if (REGNO (i2dest) >= FIRST_PSEUDO_REGISTER)
+ SUBST (regno_reg_rtx[REGNO (i2dest)], ni2dest);
+
+ i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
+
+ /* If I2 or I3 has multiple SETs, we won't know how to track
+ register status, so don't use these insns. */
+
+ if (i2_code_number >= 0 && i2set && i3set)
+ insn_code_number = recog_for_combine (&newi3pat, i3,
+ &new_i3_notes);
+
+ if (insn_code_number >= 0)
+ newpat = newi3pat;
+
+ /* It is possible that both insns now set the destination of I3.
+ If so, we must show an extra use of it. */
+
+ if (insn_code_number >= 0 && GET_CODE (SET_DEST (i3set)) == REG
+ && GET_CODE (SET_DEST (i2set)) == REG
+ && REGNO (SET_DEST (i3set)) == REGNO (SET_DEST (i2set)))
+ reg_n_sets[REGNO (SET_DEST (i2set))]++;
+ }
+
+ /* If we can split it and use I2DEST, go ahead and see if that
+ helps things be recognized. Verify that none of the registers
+ are set between I2 and I3. */
+ if (insn_code_number < 0 && (split = find_split_point (&newpat, i3)) != 0
+#ifdef HAVE_cc0
+ && GET_CODE (i2dest) == REG
+#endif
+ /* We need I2DEST in the proper mode. If it is a hard register
+ or the only use of a pseudo, we can change its mode. */
+ && (GET_MODE (*split) == GET_MODE (i2dest)
+ || GET_MODE (*split) == VOIDmode
+ || REGNO (i2dest) < FIRST_PSEUDO_REGISTER
+ || (reg_n_sets[REGNO (i2dest)] == 1 && ! added_sets_2
+ && ! REG_USERVAR_P (i2dest)))
+ && (next_real_insn (i2) == i3
+ || ! use_crosses_set_p (*split, INSN_CUID (i2)))
+ /* We can't overwrite I2DEST if its value is still used by
+ NEWPAT. */
+ && ! reg_referenced_p (i2dest, newpat))
+ {
+ rtx newdest = i2dest;
+
+ /* Get NEWDEST as a register in the proper mode. We have already
+ validated that we can do this. */
+ if (GET_MODE (i2dest) != GET_MODE (*split)
+ && GET_MODE (*split) != VOIDmode)
+ {
+ newdest = gen_rtx (REG, GET_MODE (*split), REGNO (i2dest));
+
+ if (REGNO (i2dest) >= FIRST_PSEUDO_REGISTER)
+ SUBST (regno_reg_rtx[REGNO (i2dest)], newdest);
+ }
+
+ /* If *SPLIT is a (mult FOO (const_int pow2)), convert it to
+ an ASHIFT. This can occur if it was inside a PLUS and hence
+ appeared to be a memory address. This is a kludge. */
+ if (GET_CODE (*split) == MULT
+ && GET_CODE (XEXP (*split, 1)) == CONST_INT
+ && (i = exact_log2 (INTVAL (XEXP (*split, 1)))) >= 0)
+ SUBST (*split, gen_rtx_combine (ASHIFT, GET_MODE (*split),
+ XEXP (*split, 0), GEN_INT (i)));
+
+#ifdef INSN_SCHEDULING
+ /* If *SPLIT is a paradoxical SUBREG, when we split it, it should
+ be written as a ZERO_EXTEND. */
+ if (GET_CODE (*split) == SUBREG
+ && GET_CODE (SUBREG_REG (*split)) == MEM)
+ SUBST (*split, gen_rtx_combine (ZERO_EXTEND, GET_MODE (*split),
+ XEXP (*split, 0)));
+#endif
+
+ newi2pat = gen_rtx_combine (SET, VOIDmode, newdest, *split);
+ SUBST (*split, newdest);
+ i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
+ if (i2_code_number >= 0)
+ insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
+ }
+ }
+
+ /* Check for a case where we loaded from memory in a narrow mode and
+ then sign extended it, but we need both registers. In that case,
+ we have a PARALLEL with both loads from the same memory location.
+ We can split this into a load from memory followed by a register-register
+ copy. This saves at least one insn, more if register allocation can
+ eliminate the copy.
+
+ We cannot do this if the destination of the second assignment is
+ a register that we have already assumed is zero-extended. Similarly
+ for a SUBREG of such a register. */
+
+ else if (i1 && insn_code_number < 0 && asm_noperands (newpat) < 0
+ && GET_CODE (newpat) == PARALLEL
+ && XVECLEN (newpat, 0) == 2
+ && GET_CODE (XVECEXP (newpat, 0, 0)) == SET
+ && GET_CODE (SET_SRC (XVECEXP (newpat, 0, 0))) == SIGN_EXTEND
+ && GET_CODE (XVECEXP (newpat, 0, 1)) == SET
+ && rtx_equal_p (SET_SRC (XVECEXP (newpat, 0, 1)),
+ XEXP (SET_SRC (XVECEXP (newpat, 0, 0)), 0))
+ && ! use_crosses_set_p (SET_SRC (XVECEXP (newpat, 0, 1)),
+ INSN_CUID (i2))
+ && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != ZERO_EXTRACT
+ && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != STRICT_LOW_PART
+ && ! (temp = SET_DEST (XVECEXP (newpat, 0, 1)),
+ (GET_CODE (temp) == REG
+ && reg_nonzero_bits[REGNO (temp)] != 0
+ && GET_MODE_BITSIZE (GET_MODE (temp)) < BITS_PER_WORD
+ && GET_MODE_BITSIZE (GET_MODE (temp)) < HOST_BITS_PER_INT
+ && (reg_nonzero_bits[REGNO (temp)]
+ != GET_MODE_MASK (word_mode))))
+ && ! (GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) == SUBREG
+ && (temp = SUBREG_REG (SET_DEST (XVECEXP (newpat, 0, 1))),
+ (GET_CODE (temp) == REG
+ && reg_nonzero_bits[REGNO (temp)] != 0
+ && GET_MODE_BITSIZE (GET_MODE (temp)) < BITS_PER_WORD
+ && GET_MODE_BITSIZE (GET_MODE (temp)) < HOST_BITS_PER_INT
+ && (reg_nonzero_bits[REGNO (temp)]
+ != GET_MODE_MASK (word_mode)))))
+ && ! reg_overlap_mentioned_p (SET_DEST (XVECEXP (newpat, 0, 1)),
+ SET_SRC (XVECEXP (newpat, 0, 1)))
+ && ! find_reg_note (i3, REG_UNUSED,
+ SET_DEST (XVECEXP (newpat, 0, 0))))
+ {
+ rtx ni2dest;
+
+ newi2pat = XVECEXP (newpat, 0, 0);
+ ni2dest = SET_DEST (XVECEXP (newpat, 0, 0));
+ newpat = XVECEXP (newpat, 0, 1);
+ SUBST (SET_SRC (newpat),
+ gen_lowpart_for_combine (GET_MODE (SET_SRC (newpat)), ni2dest));
+ i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
+ if (i2_code_number >= 0)
+ insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
+
+ if (insn_code_number >= 0)
+ {
+ rtx insn;
+ rtx link;
+
+ /* If we will be able to accept this, we have made a change to the
+ destination of I3. This can invalidate a LOG_LINKS pointing
+ to I3. No other part of combine.c makes such a transformation.
+
+ The new I3 will have a destination that was previously the
+ destination of I1 or I2 and which was used in i2 or I3. Call
+ distribute_links to make a LOG_LINK from the next use of
+ that destination. */
+
+ PATTERN (i3) = newpat;
+ distribute_links (gen_rtx (INSN_LIST, VOIDmode, i3, NULL_RTX));
+
+ /* I3 now uses what used to be its destination and which is
+ now I2's destination. That means we need a LOG_LINK from
+ I3 to I2. But we used to have one, so we still will.
+
+ However, some later insn might be using I2's dest and have
+ a LOG_LINK pointing at I3. We must remove this link.
+ The simplest way to remove the link is to point it at I1,
+ which we know will be a NOTE. */
+
+ for (insn = NEXT_INSN (i3);
+ insn && (this_basic_block == n_basic_blocks - 1
+ || insn != basic_block_head[this_basic_block + 1]);
+ insn = NEXT_INSN (insn))
+ {
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && reg_referenced_p (ni2dest, PATTERN (insn)))
+ {
+ for (link = LOG_LINKS (insn); link;
+ link = XEXP (link, 1))
+ if (XEXP (link, 0) == i3)
+ XEXP (link, 0) = i1;
+
+ break;
+ }
+ }
+ }
+ }
+
+ /* Similarly, check for a case where we have a PARALLEL of two independent
+ SETs but we started with three insns. In this case, we can do the sets
+ as two separate insns. This case occurs when some SET allows two
+ other insns to combine, but the destination of that SET is still live. */
+
+ else if (i1 && insn_code_number < 0 && asm_noperands (newpat) < 0
+ && GET_CODE (newpat) == PARALLEL
+ && XVECLEN (newpat, 0) == 2
+ && GET_CODE (XVECEXP (newpat, 0, 0)) == SET
+ && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) != ZERO_EXTRACT
+ && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) != STRICT_LOW_PART
+ && GET_CODE (XVECEXP (newpat, 0, 1)) == SET
+ && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != ZERO_EXTRACT
+ && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != STRICT_LOW_PART
+ && ! use_crosses_set_p (SET_SRC (XVECEXP (newpat, 0, 1)),
+ INSN_CUID (i2))
+ /* Don't pass sets with (USE (MEM ...)) dests to the following. */
+ && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != USE
+ && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) != USE
+ && ! reg_referenced_p (SET_DEST (XVECEXP (newpat, 0, 1)),
+ XVECEXP (newpat, 0, 0))
+ && ! reg_referenced_p (SET_DEST (XVECEXP (newpat, 0, 0)),
+ XVECEXP (newpat, 0, 1)))
+ {
+ newi2pat = XVECEXP (newpat, 0, 1);
+ newpat = XVECEXP (newpat, 0, 0);
+
+ i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
+ if (i2_code_number >= 0)
+ insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
+ }
+
+ /* If it still isn't recognized, fail and change things back the way they
+ were. */
+ if ((insn_code_number < 0
+ /* Is the result a reasonable ASM_OPERANDS? */
+ && (! check_asm_operands (newpat) || added_sets_1 || added_sets_2)))
+ {
+ undo_all ();
+ return 0;
+ }
+
+ /* If we had to change another insn, make sure it is valid also. */
+ if (undobuf.other_insn)
+ {
+ rtx other_pat = PATTERN (undobuf.other_insn);
+ rtx new_other_notes;
+ rtx note, next;
+
+ CLEAR_HARD_REG_SET (newpat_used_regs);
+
+ other_code_number = recog_for_combine (&other_pat, undobuf.other_insn,
+ &new_other_notes);
+
+ if (other_code_number < 0 && ! check_asm_operands (other_pat))
+ {
+ undo_all ();
+ return 0;
+ }
+
+ PATTERN (undobuf.other_insn) = other_pat;
+
+ /* If any of the notes in OTHER_INSN were REG_UNUSED, ensure that they
+ are still valid. Then add any non-duplicate notes added by
+ recog_for_combine. */
+ for (note = REG_NOTES (undobuf.other_insn); note; note = next)
+ {
+ next = XEXP (note, 1);
+
+ if (REG_NOTE_KIND (note) == REG_UNUSED
+ && ! reg_set_p (XEXP (note, 0), PATTERN (undobuf.other_insn)))
+ {
+ if (GET_CODE (XEXP (note, 0)) == REG)
+ reg_n_deaths[REGNO (XEXP (note, 0))]--;
+
+ remove_note (undobuf.other_insn, note);
+ }
+ }
+
+ for (note = new_other_notes; note; note = XEXP (note, 1))
+ if (GET_CODE (XEXP (note, 0)) == REG)
+ reg_n_deaths[REGNO (XEXP (note, 0))]++;
+
+ distribute_notes (new_other_notes, undobuf.other_insn,
+ undobuf.other_insn, NULL_RTX, NULL_RTX, NULL_RTX);
+ }
+
+ /* We now know that we can do this combination. Merge the insns and
+ update the status of registers and LOG_LINKS. */
+
+ {
+ rtx i3notes, i2notes, i1notes = 0;
+ rtx i3links, i2links, i1links = 0;
+ rtx midnotes = 0;
+ register int regno;
+ /* Compute which registers we expect to eliminate. */
+ rtx elim_i2 = (newi2pat || i2dest_in_i2src || i2dest_in_i1src
+ ? 0 : i2dest);
+ rtx elim_i1 = i1 == 0 || i1dest_in_i1src ? 0 : i1dest;
+
+ /* Get the old REG_NOTES and LOG_LINKS from all our insns and
+ clear them. */
+ i3notes = REG_NOTES (i3), i3links = LOG_LINKS (i3);
+ i2notes = REG_NOTES (i2), i2links = LOG_LINKS (i2);
+ if (i1)
+ i1notes = REG_NOTES (i1), i1links = LOG_LINKS (i1);
+
+ /* Ensure that we do not have something that should not be shared but
+ occurs multiple times in the new insns. Check this by first
+ resetting all the `used' flags and then copying anything is shared. */
+
+ reset_used_flags (i3notes);
+ reset_used_flags (i2notes);
+ reset_used_flags (i1notes);
+ reset_used_flags (newpat);
+ reset_used_flags (newi2pat);
+ if (undobuf.other_insn)
+ reset_used_flags (PATTERN (undobuf.other_insn));
+
+ i3notes = copy_rtx_if_shared (i3notes);
+ i2notes = copy_rtx_if_shared (i2notes);
+ i1notes = copy_rtx_if_shared (i1notes);
+ newpat = copy_rtx_if_shared (newpat);
+ newi2pat = copy_rtx_if_shared (newi2pat);
+ if (undobuf.other_insn)
+ reset_used_flags (PATTERN (undobuf.other_insn));
+
+ INSN_CODE (i3) = insn_code_number;
+ PATTERN (i3) = newpat;
+ if (undobuf.other_insn)
+ INSN_CODE (undobuf.other_insn) = other_code_number;
+
+ /* We had one special case above where I2 had more than one set and
+ we replaced a destination of one of those sets with the destination
+ of I3. In that case, we have to update LOG_LINKS of insns later
+ in this basic block. Note that this (expensive) case is rare.
+
+ Also, in this case, we must pretend that all REG_NOTEs for I2
+ actually came from I3, so that REG_UNUSED notes from I2 will be
+ properly handled. */
+
+ if (i3_subst_into_i2)
+ {
+ for (i = 0; i < XVECLEN (PATTERN (i2), 0); i++)
+ if (GET_CODE (SET_DEST (XVECEXP (PATTERN (i2), 0, i))) == REG
+ && SET_DEST (XVECEXP (PATTERN (i2), 0, i)) != i2dest
+ && ! find_reg_note (i2, REG_UNUSED,
+ SET_DEST (XVECEXP (PATTERN (i2), 0, i))))
+ for (temp = NEXT_INSN (i2);
+ temp && (this_basic_block == n_basic_blocks - 1
+ || basic_block_head[this_basic_block] != temp);
+ temp = NEXT_INSN (temp))
+ if (temp != i3 && GET_RTX_CLASS (GET_CODE (temp)) == 'i')
+ for (link = LOG_LINKS (temp); link; link = XEXP (link, 1))
+ if (XEXP (link, 0) == i2)
+ XEXP (link, 0) = i3;
+
+ if (i3notes)
+ {
+ rtx link = i3notes;
+ while (XEXP (link, 1))
+ link = XEXP (link, 1);
+ XEXP (link, 1) = i2notes;
+ }
+ else
+ i3notes = i2notes;
+ i2notes = 0;
+ }
+
+ LOG_LINKS (i3) = 0;
+ REG_NOTES (i3) = 0;
+ LOG_LINKS (i2) = 0;
+ REG_NOTES (i2) = 0;
+
+ if (newi2pat)
+ {
+ INSN_CODE (i2) = i2_code_number;
+ PATTERN (i2) = newi2pat;
+ }
+ else
+ {
+ PUT_CODE (i2, NOTE);
+ NOTE_LINE_NUMBER (i2) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (i2) = 0;
+ }
+
+ if (i1)
+ {
+ LOG_LINKS (i1) = 0;
+ REG_NOTES (i1) = 0;
+ PUT_CODE (i1, NOTE);
+ NOTE_LINE_NUMBER (i1) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (i1) = 0;
+ }
+
+ /* Get death notes for everything that is now used in either I3 or
+ I2 and used to die in a previous insn. */
+
+ move_deaths (newpat, i1 ? INSN_CUID (i1) : INSN_CUID (i2), i3, &midnotes);
+ if (newi2pat)
+ move_deaths (newi2pat, INSN_CUID (i1), i2, &midnotes);
+
+ /* Distribute all the LOG_LINKS and REG_NOTES from I1, I2, and I3. */
+ if (i3notes)
+ distribute_notes (i3notes, i3, i3, newi2pat ? i2 : NULL_RTX,
+ elim_i2, elim_i1);
+ if (i2notes)
+ distribute_notes (i2notes, i2, i3, newi2pat ? i2 : NULL_RTX,
+ elim_i2, elim_i1);
+ if (i1notes)
+ distribute_notes (i1notes, i1, i3, newi2pat ? i2 : NULL_RTX,
+ elim_i2, elim_i1);
+ if (midnotes)
+ distribute_notes (midnotes, NULL_RTX, i3, newi2pat ? i2 : NULL_RTX,
+ elim_i2, elim_i1);
+
+ /* Distribute any notes added to I2 or I3 by recog_for_combine. We
+ know these are REG_UNUSED and want them to go to the desired insn,
+ so we always pass it as i3. We have not counted the notes in
+ reg_n_deaths yet, so we need to do so now. */
+
+ if (newi2pat && new_i2_notes)
+ {
+ for (temp = new_i2_notes; temp; temp = XEXP (temp, 1))
+ if (GET_CODE (XEXP (temp, 0)) == REG)
+ reg_n_deaths[REGNO (XEXP (temp, 0))]++;
+
+ distribute_notes (new_i2_notes, i2, i2, NULL_RTX, NULL_RTX, NULL_RTX);
+ }
+
+ if (new_i3_notes)
+ {
+ for (temp = new_i3_notes; temp; temp = XEXP (temp, 1))
+ if (GET_CODE (XEXP (temp, 0)) == REG)
+ reg_n_deaths[REGNO (XEXP (temp, 0))]++;
+
+ distribute_notes (new_i3_notes, i3, i3, NULL_RTX, NULL_RTX, NULL_RTX);
+ }
+
+ /* If I3DEST was used in I3SRC, it really died in I3. We may need to
+ put a REG_DEAD note for it somewhere. Similarly for I2 and I1.
+ Show an additional death due to the REG_DEAD note we make here. If
+ we discard it in distribute_notes, we will decrement it again. */
+
+ if (i3dest_killed)
+ {
+ if (GET_CODE (i3dest_killed) == REG)
+ reg_n_deaths[REGNO (i3dest_killed)]++;
+
+ distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i3dest_killed,
+ NULL_RTX),
+ NULL_RTX, i3, newi2pat ? i2 : NULL_RTX,
+ NULL_RTX, NULL_RTX);
+ }
+
+ /* For I2 and I1, we have to be careful. If NEWI2PAT exists and sets
+ I2DEST or I1DEST, the death must be somewhere before I2, not I3. If
+ we passed I3 in that case, it might delete I2. */
+
+ if (i2dest_in_i2src)
+ {
+ if (GET_CODE (i2dest) == REG)
+ reg_n_deaths[REGNO (i2dest)]++;
+
+ if (newi2pat && reg_set_p (i2dest, newi2pat))
+ distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i2dest, NULL_RTX),
+ NULL_RTX, i2, NULL_RTX, NULL_RTX, NULL_RTX);
+ else
+ distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i2dest, NULL_RTX),
+ NULL_RTX, i3, newi2pat ? i2 : NULL_RTX,
+ NULL_RTX, NULL_RTX);
+ }
+
+ if (i1dest_in_i1src)
+ {
+ if (GET_CODE (i1dest) == REG)
+ reg_n_deaths[REGNO (i1dest)]++;
+
+ if (newi2pat && reg_set_p (i1dest, newi2pat))
+ distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i1dest, NULL_RTX),
+ NULL_RTX, i2, NULL_RTX, NULL_RTX, NULL_RTX);
+ else
+ distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i1dest, NULL_RTX),
+ NULL_RTX, i3, newi2pat ? i2 : NULL_RTX,
+ NULL_RTX, NULL_RTX);
+ }
+
+ distribute_links (i3links);
+ distribute_links (i2links);
+ distribute_links (i1links);
+
+ if (GET_CODE (i2dest) == REG)
+ {
+ rtx link;
+ rtx i2_insn = 0, i2_val = 0, set;
+
+ /* The insn that used to set this register doesn't exist, and
+ this life of the register may not exist either. See if one of
+ I3's links points to an insn that sets I2DEST. If it does,
+ that is now the last known value for I2DEST. If we don't update
+ this and I2 set the register to a value that depended on its old
+ contents, we will get confused. If this insn is used, thing
+ will be set correctly in combine_instructions. */
+
+ for (link = LOG_LINKS (i3); link; link = XEXP (link, 1))
+ if ((set = single_set (XEXP (link, 0))) != 0
+ && rtx_equal_p (i2dest, SET_DEST (set)))
+ i2_insn = XEXP (link, 0), i2_val = SET_SRC (set);
+
+ record_value_for_reg (i2dest, i2_insn, i2_val);
+
+ /* If the reg formerly set in I2 died only once and that was in I3,
+ zero its use count so it won't make `reload' do any work. */
+ if (! added_sets_2 && newi2pat == 0 && ! i2dest_in_i2src)
+ {
+ regno = REGNO (i2dest);
+ reg_n_sets[regno]--;
+ if (reg_n_sets[regno] == 0
+ && ! (basic_block_live_at_start[0][regno / REGSET_ELT_BITS]
+ & ((REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS))))
+ reg_n_refs[regno] = 0;
+ }
+ }
+
+ if (i1 && GET_CODE (i1dest) == REG)
+ {
+ rtx link;
+ rtx i1_insn = 0, i1_val = 0, set;
+
+ for (link = LOG_LINKS (i3); link; link = XEXP (link, 1))
+ if ((set = single_set (XEXP (link, 0))) != 0
+ && rtx_equal_p (i1dest, SET_DEST (set)))
+ i1_insn = XEXP (link, 0), i1_val = SET_SRC (set);
+
+ record_value_for_reg (i1dest, i1_insn, i1_val);
+
+ regno = REGNO (i1dest);
+ if (! added_sets_1 && ! i1dest_in_i1src)
+ {
+ reg_n_sets[regno]--;
+ if (reg_n_sets[regno] == 0
+ && ! (basic_block_live_at_start[0][regno / REGSET_ELT_BITS]
+ & ((REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS))))
+ reg_n_refs[regno] = 0;
+ }
+ }
+
+ /* Update reg_nonzero_bits et al for any changes that may have been made
+ to this insn. */
+
+ note_stores (newpat, set_nonzero_bits_and_sign_copies);
+ if (newi2pat)
+ note_stores (newi2pat, set_nonzero_bits_and_sign_copies);
+
+ /* If I3 is now an unconditional jump, ensure that it has a
+ BARRIER following it since it may have initially been a
+ conditional jump. It may also be the last nonnote insn. */
+
+ if ((GET_CODE (newpat) == RETURN || simplejump_p (i3))
+ && ((temp = next_nonnote_insn (i3)) == NULL_RTX
+ || GET_CODE (temp) != BARRIER))
+ emit_barrier_after (i3);
+ }
+
+ combine_successes++;
+
+ if (added_links_insn
+ && (newi2pat == 0 || INSN_CUID (added_links_insn) < INSN_CUID (i2))
+ && INSN_CUID (added_links_insn) < INSN_CUID (i3))
+ return added_links_insn;
+ else
+ return newi2pat ? i2 : i3;
+}
+
+/* Undo all the modifications recorded in undobuf. */
+
+static void
+undo_all ()
+{
+ register int i;
+ if (undobuf.num_undo > MAX_UNDO)
+ undobuf.num_undo = MAX_UNDO;
+ for (i = undobuf.num_undo - 1; i >= 0; i--)
+ {
+ if (undobuf.undo[i].is_int)
+ *undobuf.undo[i].where.i = undobuf.undo[i].old_contents.i;
+ else
+ *undobuf.undo[i].where.r = undobuf.undo[i].old_contents.r;
+
+ }
+
+ obfree (undobuf.storage);
+ undobuf.num_undo = 0;
+}
+
+/* Find the innermost point within the rtx at LOC, possibly LOC itself,
+ where we have an arithmetic expression and return that point. LOC will
+ be inside INSN.
+
+ try_combine will call this function to see if an insn can be split into
+ two insns. */
+
+static rtx *
+find_split_point (loc, insn)
+ rtx *loc;
+ rtx insn;
+{
+ rtx x = *loc;
+ enum rtx_code code = GET_CODE (x);
+ rtx *split;
+ int len = 0, pos, unsignedp;
+ rtx inner;
+
+ /* First special-case some codes. */
+ switch (code)
+ {
+ case SUBREG:
+#ifdef INSN_SCHEDULING
+ /* If we are making a paradoxical SUBREG invalid, it becomes a split
+ point. */
+ if (GET_CODE (SUBREG_REG (x)) == MEM)
+ return loc;
+#endif
+ return find_split_point (&SUBREG_REG (x), insn);
+
+ case MEM:
+#ifdef HAVE_lo_sum
+ /* If we have (mem (const ..)) or (mem (symbol_ref ...)), split it
+ using LO_SUM and HIGH. */
+ if (GET_CODE (XEXP (x, 0)) == CONST
+ || GET_CODE (XEXP (x, 0)) == SYMBOL_REF)
+ {
+ SUBST (XEXP (x, 0),
+ gen_rtx_combine (LO_SUM, Pmode,
+ gen_rtx_combine (HIGH, Pmode, XEXP (x, 0)),
+ XEXP (x, 0)));
+ return &XEXP (XEXP (x, 0), 0);
+ }
+#endif
+
+ /* If we have a PLUS whose second operand is a constant and the
+ address is not valid, perhaps will can split it up using
+ the machine-specific way to split large constants. We use
+ the first psuedo-reg (one of the virtual regs) as a placeholder;
+ it will not remain in the result. */
+ if (GET_CODE (XEXP (x, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+ && ! memory_address_p (GET_MODE (x), XEXP (x, 0)))
+ {
+ rtx reg = regno_reg_rtx[FIRST_PSEUDO_REGISTER];
+ rtx seq = split_insns (gen_rtx (SET, VOIDmode, reg, XEXP (x, 0)),
+ subst_insn);
+
+ /* This should have produced two insns, each of which sets our
+ placeholder. If the source of the second is a valid address,
+ we can make put both sources together and make a split point
+ in the middle. */
+
+ if (seq && XVECLEN (seq, 0) == 2
+ && GET_CODE (XVECEXP (seq, 0, 0)) == INSN
+ && GET_CODE (PATTERN (XVECEXP (seq, 0, 0))) == SET
+ && SET_DEST (PATTERN (XVECEXP (seq, 0, 0))) == reg
+ && ! reg_mentioned_p (reg,
+ SET_SRC (PATTERN (XVECEXP (seq, 0, 0))))
+ && GET_CODE (XVECEXP (seq, 0, 1)) == INSN
+ && GET_CODE (PATTERN (XVECEXP (seq, 0, 1))) == SET
+ && SET_DEST (PATTERN (XVECEXP (seq, 0, 1))) == reg
+ && memory_address_p (GET_MODE (x),
+ SET_SRC (PATTERN (XVECEXP (seq, 0, 1)))))
+ {
+ rtx src1 = SET_SRC (PATTERN (XVECEXP (seq, 0, 0)));
+ rtx src2 = SET_SRC (PATTERN (XVECEXP (seq, 0, 1)));
+
+ /* Replace the placeholder in SRC2 with SRC1. If we can
+ find where in SRC2 it was placed, that can become our
+ split point and we can replace this address with SRC2.
+ Just try two obvious places. */
+
+ src2 = replace_rtx (src2, reg, src1);
+ split = 0;
+ if (XEXP (src2, 0) == src1)
+ split = &XEXP (src2, 0);
+ else if (GET_RTX_FORMAT (GET_CODE (XEXP (src2, 0)))[0] == 'e'
+ && XEXP (XEXP (src2, 0), 0) == src1)
+ split = &XEXP (XEXP (src2, 0), 0);
+
+ if (split)
+ {
+ SUBST (XEXP (x, 0), src2);
+ return split;
+ }
+ }
+
+ /* If that didn't work, perhaps the first operand is complex and
+ needs to be computed separately, so make a split point there.
+ This will occur on machines that just support REG + CONST
+ and have a constant moved through some previous computation. */
+
+ else if (GET_RTX_CLASS (GET_CODE (XEXP (XEXP (x, 0), 0))) != 'o'
+ && ! (GET_CODE (XEXP (XEXP (x, 0), 0)) == SUBREG
+ && (GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (XEXP (x, 0), 0))))
+ == 'o')))
+ return &XEXP (XEXP (x, 0), 0);
+ }
+ break;
+
+ case SET:
+#ifdef HAVE_cc0
+ /* If SET_DEST is CC0 and SET_SRC is not an operand, a COMPARE, or a
+ ZERO_EXTRACT, the most likely reason why this doesn't match is that
+ we need to put the operand into a register. So split at that
+ point. */
+
+ if (SET_DEST (x) == cc0_rtx
+ && GET_CODE (SET_SRC (x)) != COMPARE
+ && GET_CODE (SET_SRC (x)) != ZERO_EXTRACT
+ && GET_RTX_CLASS (GET_CODE (SET_SRC (x))) != 'o'
+ && ! (GET_CODE (SET_SRC (x)) == SUBREG
+ && GET_RTX_CLASS (GET_CODE (SUBREG_REG (SET_SRC (x)))) == 'o'))
+ return &SET_SRC (x);
+#endif
+
+ /* See if we can split SET_SRC as it stands. */
+ split = find_split_point (&SET_SRC (x), insn);
+ if (split && split != &SET_SRC (x))
+ return split;
+
+ /* See if this is a bitfield assignment with everything constant. If
+ so, this is an IOR of an AND, so split it into that. */
+ if (GET_CODE (SET_DEST (x)) == ZERO_EXTRACT
+ && (GET_MODE_BITSIZE (GET_MODE (XEXP (SET_DEST (x), 0)))
+ <= HOST_BITS_PER_WIDE_INT)
+ && GET_CODE (XEXP (SET_DEST (x), 1)) == CONST_INT
+ && GET_CODE (XEXP (SET_DEST (x), 2)) == CONST_INT
+ && GET_CODE (SET_SRC (x)) == CONST_INT
+ && ((INTVAL (XEXP (SET_DEST (x), 1))
+ + INTVAL (XEXP (SET_DEST (x), 2)))
+ <= GET_MODE_BITSIZE (GET_MODE (XEXP (SET_DEST (x), 0))))
+ && ! side_effects_p (XEXP (SET_DEST (x), 0)))
+ {
+ int pos = INTVAL (XEXP (SET_DEST (x), 2));
+ int len = INTVAL (XEXP (SET_DEST (x), 1));
+ int src = INTVAL (SET_SRC (x));
+ rtx dest = XEXP (SET_DEST (x), 0);
+ enum machine_mode mode = GET_MODE (dest);
+ unsigned HOST_WIDE_INT mask = ((HOST_WIDE_INT) 1 << len) - 1;
+
+#if BITS_BIG_ENDIAN
+ pos = GET_MODE_BITSIZE (mode) - len - pos;
+#endif
+
+ if (src == mask)
+ SUBST (SET_SRC (x),
+ gen_binary (IOR, mode, dest, GEN_INT (src << pos)));
+ else
+ SUBST (SET_SRC (x),
+ gen_binary (IOR, mode,
+ gen_binary (AND, mode, dest,
+ GEN_INT (~ (mask << pos)
+ & GET_MODE_MASK (mode))),
+ GEN_INT (src << pos)));
+
+ SUBST (SET_DEST (x), dest);
+
+ split = find_split_point (&SET_SRC (x), insn);
+ if (split && split != &SET_SRC (x))
+ return split;
+ }
+
+ /* Otherwise, see if this is an operation that we can split into two.
+ If so, try to split that. */
+ code = GET_CODE (SET_SRC (x));
+
+ switch (code)
+ {
+ case AND:
+ /* If we are AND'ing with a large constant that is only a single
+ bit and the result is only being used in a context where we
+ need to know if it is zero or non-zero, replace it with a bit
+ extraction. This will avoid the large constant, which might
+ have taken more than one insn to make. If the constant were
+ not a valid argument to the AND but took only one insn to make,
+ this is no worse, but if it took more than one insn, it will
+ be better. */
+
+ if (GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT
+ && GET_CODE (XEXP (SET_SRC (x), 0)) == REG
+ && (pos = exact_log2 (INTVAL (XEXP (SET_SRC (x), 1)))) >= 7
+ && GET_CODE (SET_DEST (x)) == REG
+ && (split = find_single_use (SET_DEST (x), insn, NULL_PTR)) != 0
+ && (GET_CODE (*split) == EQ || GET_CODE (*split) == NE)
+ && XEXP (*split, 0) == SET_DEST (x)
+ && XEXP (*split, 1) == const0_rtx)
+ {
+ SUBST (SET_SRC (x),
+ make_extraction (GET_MODE (SET_DEST (x)),
+ XEXP (SET_SRC (x), 0),
+ pos, NULL_RTX, 1, 1, 0, 0));
+ return find_split_point (loc, insn);
+ }
+ break;
+
+ case SIGN_EXTEND:
+ inner = XEXP (SET_SRC (x), 0);
+ pos = 0;
+ len = GET_MODE_BITSIZE (GET_MODE (inner));
+ unsignedp = 0;
+ break;
+
+ case SIGN_EXTRACT:
+ case ZERO_EXTRACT:
+ if (GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT
+ && GET_CODE (XEXP (SET_SRC (x), 2)) == CONST_INT)
+ {
+ inner = XEXP (SET_SRC (x), 0);
+ len = INTVAL (XEXP (SET_SRC (x), 1));
+ pos = INTVAL (XEXP (SET_SRC (x), 2));
+
+#if BITS_BIG_ENDIAN
+ pos = GET_MODE_BITSIZE (GET_MODE (inner)) - len - pos;
+#endif
+ unsignedp = (code == ZERO_EXTRACT);
+ }
+ break;
+ }
+
+ if (len && pos >= 0 && pos + len <= GET_MODE_BITSIZE (GET_MODE (inner)))
+ {
+ enum machine_mode mode = GET_MODE (SET_SRC (x));
+
+ /* For unsigned, we have a choice of a shift followed by an
+ AND or two shifts. Use two shifts for field sizes where the
+ constant might be too large. We assume here that we can
+ always at least get 8-bit constants in an AND insn, which is
+ true for every current RISC. */
+
+ if (unsignedp && len <= 8)
+ {
+ SUBST (SET_SRC (x),
+ gen_rtx_combine
+ (AND, mode,
+ gen_rtx_combine (LSHIFTRT, mode,
+ gen_lowpart_for_combine (mode, inner),
+ GEN_INT (pos)),
+ GEN_INT (((HOST_WIDE_INT) 1 << len) - 1)));
+
+ split = find_split_point (&SET_SRC (x), insn);
+ if (split && split != &SET_SRC (x))
+ return split;
+ }
+ else
+ {
+ SUBST (SET_SRC (x),
+ gen_rtx_combine
+ (unsignedp ? LSHIFTRT : ASHIFTRT, mode,
+ gen_rtx_combine (ASHIFT, mode,
+ gen_lowpart_for_combine (mode, inner),
+ GEN_INT (GET_MODE_BITSIZE (mode)
+ - len - pos)),
+ GEN_INT (GET_MODE_BITSIZE (mode) - len)));
+
+ split = find_split_point (&SET_SRC (x), insn);
+ if (split && split != &SET_SRC (x))
+ return split;
+ }
+ }
+
+ /* See if this is a simple operation with a constant as the second
+ operand. It might be that this constant is out of range and hence
+ could be used as a split point. */
+ if ((GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '2'
+ || GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == 'c'
+ || GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '<')
+ && CONSTANT_P (XEXP (SET_SRC (x), 1))
+ && (GET_RTX_CLASS (GET_CODE (XEXP (SET_SRC (x), 0))) == 'o'
+ || (GET_CODE (XEXP (SET_SRC (x), 0)) == SUBREG
+ && (GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (SET_SRC (x), 0))))
+ == 'o'))))
+ return &XEXP (SET_SRC (x), 1);
+
+ /* Finally, see if this is a simple operation with its first operand
+ not in a register. The operation might require this operand in a
+ register, so return it as a split point. We can always do this
+ because if the first operand were another operation, we would have
+ already found it as a split point. */
+ if ((GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '2'
+ || GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == 'c'
+ || GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '<'
+ || GET_RTX_CLASS (GET_CODE (SET_SRC (x))) == '1')
+ && ! register_operand (XEXP (SET_SRC (x), 0), VOIDmode))
+ return &XEXP (SET_SRC (x), 0);
+
+ return 0;
+
+ case AND:
+ case IOR:
+ /* We write NOR as (and (not A) (not B)), but if we don't have a NOR,
+ it is better to write this as (not (ior A B)) so we can split it.
+ Similarly for IOR. */
+ if (GET_CODE (XEXP (x, 0)) == NOT && GET_CODE (XEXP (x, 1)) == NOT)
+ {
+ SUBST (*loc,
+ gen_rtx_combine (NOT, GET_MODE (x),
+ gen_rtx_combine (code == IOR ? AND : IOR,
+ GET_MODE (x),
+ XEXP (XEXP (x, 0), 0),
+ XEXP (XEXP (x, 1), 0))));
+ return find_split_point (loc, insn);
+ }
+
+ /* Many RISC machines have a large set of logical insns. If the
+ second operand is a NOT, put it first so we will try to split the
+ other operand first. */
+ if (GET_CODE (XEXP (x, 1)) == NOT)
+ {
+ rtx tem = XEXP (x, 0);
+ SUBST (XEXP (x, 0), XEXP (x, 1));
+ SUBST (XEXP (x, 1), tem);
+ }
+ break;
+ }
+
+ /* Otherwise, select our actions depending on our rtx class. */
+ switch (GET_RTX_CLASS (code))
+ {
+ case 'b': /* This is ZERO_EXTRACT and SIGN_EXTRACT. */
+ case '3':
+ split = find_split_point (&XEXP (x, 2), insn);
+ if (split)
+ return split;
+ /* ... fall through ... */
+ case '2':
+ case 'c':
+ case '<':
+ split = find_split_point (&XEXP (x, 1), insn);
+ if (split)
+ return split;
+ /* ... fall through ... */
+ case '1':
+ /* Some machines have (and (shift ...) ...) insns. If X is not
+ an AND, but XEXP (X, 0) is, use it as our split point. */
+ if (GET_CODE (x) != AND && GET_CODE (XEXP (x, 0)) == AND)
+ return &XEXP (x, 0);
+
+ split = find_split_point (&XEXP (x, 0), insn);
+ if (split)
+ return split;
+ return loc;
+ }
+
+ /* Otherwise, we don't have a split point. */
+ return 0;
+}
+
+/* Throughout X, replace FROM with TO, and return the result.
+ The result is TO if X is FROM;
+ otherwise the result is X, but its contents may have been modified.
+ If they were modified, a record was made in undobuf so that
+ undo_all will (among other things) return X to its original state.
+
+ If the number of changes necessary is too much to record to undo,
+ the excess changes are not made, so the result is invalid.
+ The changes already made can still be undone.
+ undobuf.num_undo is incremented for such changes, so by testing that
+ the caller can tell whether the result is valid.
+
+ `n_occurrences' is incremented each time FROM is replaced.
+
+ IN_DEST is non-zero if we are processing the SET_DEST of a SET.
+
+ UNIQUE_COPY is non-zero if each substitution must be unique. We do this
+ by copying if `n_occurrences' is non-zero. */
+
+static rtx
+subst (x, from, to, in_dest, unique_copy)
+ register rtx x, from, to;
+ int in_dest;
+ int unique_copy;
+{
+ register enum rtx_code code = GET_CODE (x);
+ enum machine_mode op0_mode = VOIDmode;
+ register char *fmt;
+ register int len, i;
+ rtx new;
+
+/* Two expressions are equal if they are identical copies of a shared
+ RTX or if they are both registers with the same register number
+ and mode. */
+
+#define COMBINE_RTX_EQUAL_P(X,Y) \
+ ((X) == (Y) \
+ || (GET_CODE (X) == REG && GET_CODE (Y) == REG \
+ && REGNO (X) == REGNO (Y) && GET_MODE (X) == GET_MODE (Y)))
+
+ if (! in_dest && COMBINE_RTX_EQUAL_P (x, from))
+ {
+ n_occurrences++;
+ return (unique_copy && n_occurrences > 1 ? copy_rtx (to) : to);
+ }
+
+ /* If X and FROM are the same register but different modes, they will
+ not have been seen as equal above. However, flow.c will make a
+ LOG_LINKS entry for that case. If we do nothing, we will try to
+ rerecognize our original insn and, when it succeeds, we will
+ delete the feeding insn, which is incorrect.
+
+ So force this insn not to match in this (rare) case. */
+ if (! in_dest && code == REG && GET_CODE (from) == REG
+ && REGNO (x) == REGNO (from))
+ return gen_rtx (CLOBBER, GET_MODE (x), const0_rtx);
+
+ /* If this is an object, we are done unless it is a MEM or LO_SUM, both
+ of which may contain things that can be combined. */
+ if (code != MEM && code != LO_SUM && GET_RTX_CLASS (code) == 'o')
+ return x;
+
+ /* It is possible to have a subexpression appear twice in the insn.
+ Suppose that FROM is a register that appears within TO.
+ Then, after that subexpression has been scanned once by `subst',
+ the second time it is scanned, TO may be found. If we were
+ to scan TO here, we would find FROM within it and create a
+ self-referent rtl structure which is completely wrong. */
+ if (COMBINE_RTX_EQUAL_P (x, to))
+ return to;
+
+ len = GET_RTX_LENGTH (code);
+ fmt = GET_RTX_FORMAT (code);
+
+ /* We don't need to process a SET_DEST that is a register, CC0, or PC, so
+ set up to skip this common case. All other cases where we want to
+ suppress replacing something inside a SET_SRC are handled via the
+ IN_DEST operand. */
+ if (code == SET
+ && (GET_CODE (SET_DEST (x)) == REG
+ || GET_CODE (SET_DEST (x)) == CC0
+ || GET_CODE (SET_DEST (x)) == PC))
+ fmt = "ie";
+
+ /* Get the mode of operand 0 in case X is now a SIGN_EXTEND of a constant. */
+ if (fmt[0] == 'e')
+ op0_mode = GET_MODE (XEXP (x, 0));
+
+ for (i = 0; i < len; i++)
+ {
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ {
+ if (COMBINE_RTX_EQUAL_P (XVECEXP (x, i, j), from))
+ {
+ new = (unique_copy && n_occurrences ? copy_rtx (to) : to);
+ n_occurrences++;
+ }
+ else
+ {
+ new = subst (XVECEXP (x, i, j), from, to, 0, unique_copy);
+
+ /* If this substitution failed, this whole thing fails. */
+ if (GET_CODE (new) == CLOBBER && XEXP (new, 0) == const0_rtx)
+ return new;
+ }
+
+ SUBST (XVECEXP (x, i, j), new);
+ }
+ }
+ else if (fmt[i] == 'e')
+ {
+ if (COMBINE_RTX_EQUAL_P (XEXP (x, i), from))
+ {
+ /* In general, don't install a subreg involving two modes not
+ tieable. It can worsen register allocation, and can even
+ make invalid reload insns, since the reg inside may need to
+ be copied from in the outside mode, and that may be invalid
+ if it is an fp reg copied in integer mode.
+
+ We allow two exceptions to this: It is valid if it is inside
+ another SUBREG and the mode of that SUBREG and the mode of
+ the inside of TO is tieable and it is valid if X is a SET
+ that copies FROM to CC0. */
+ if (GET_CODE (to) == SUBREG
+ && ! MODES_TIEABLE_P (GET_MODE (to),
+ GET_MODE (SUBREG_REG (to)))
+ && ! (code == SUBREG
+ && MODES_TIEABLE_P (GET_MODE (x),
+ GET_MODE (SUBREG_REG (to))))
+#ifdef HAVE_cc0
+ && ! (code == SET && i == 1 && XEXP (x, 0) == cc0_rtx)
+#endif
+ )
+ return gen_rtx (CLOBBER, VOIDmode, const0_rtx);
+
+ new = (unique_copy && n_occurrences ? copy_rtx (to) : to);
+ n_occurrences++;
+ }
+ else
+ /* If we are in a SET_DEST, suppress most cases unless we
+ have gone inside a MEM, in which case we want to
+ simplify the address. We assume here that things that
+ are actually part of the destination have their inner
+ parts in the first expression. This is true for SUBREG,
+ STRICT_LOW_PART, and ZERO_EXTRACT, which are the only
+ things aside from REG and MEM that should appear in a
+ SET_DEST. */
+ new = subst (XEXP (x, i), from, to,
+ (((in_dest
+ && (code == SUBREG || code == STRICT_LOW_PART
+ || code == ZERO_EXTRACT))
+ || code == SET)
+ && i == 0), unique_copy);
+
+ /* If we found that we will have to reject this combination,
+ indicate that by returning the CLOBBER ourselves, rather than
+ an expression containing it. This will speed things up as
+ well as prevent accidents where two CLOBBERs are considered
+ to be equal, thus producing an incorrect simplification. */
+
+ if (GET_CODE (new) == CLOBBER && XEXP (new, 0) == const0_rtx)
+ return new;
+
+ SUBST (XEXP (x, i), new);
+ }
+ }
+
+ /* Try to simplify X. If the simplification changed the code, it is likely
+ that further simplification will help, so loop, but limit the number
+ of repetitions that will be performed. */
+
+ for (i = 0; i < 4; i++)
+ {
+ /* If X is sufficiently simple, don't bother trying to do anything
+ with it. */
+ if (code != CONST_INT && code != REG && code != CLOBBER)
+ x = simplify_rtx (x, op0_mode, i == 3, in_dest);
+
+ if (GET_CODE (x) == code)
+ break;
+
+ code = GET_CODE (x);
+
+ /* We no longer know the original mode of operand 0 since we
+ have changed the form of X) */
+ op0_mode = VOIDmode;
+ }
+
+ return x;
+}
+
+/* Simplify X, a piece of RTL. We just operate on the expression at the
+ outer level; call `subst' to simplify recursively. Return the new
+ expression.
+
+ OP0_MODE is the original mode of XEXP (x, 0); LAST is nonzero if this
+ will be the iteration even if an expression with a code different from
+ X is returned; IN_DEST is nonzero if we are inside a SET_DEST. */
+
+static rtx
+simplify_rtx (x, op0_mode, last, in_dest)
+ rtx x;
+ enum machine_mode op0_mode;
+ int last;
+ int in_dest;
+{
+ enum rtx_code code = GET_CODE (x);
+ enum machine_mode mode = GET_MODE (x);
+ rtx temp;
+ int i;
+
+ /* If this is a commutative operation, put a constant last and a complex
+ expression first. We don't need to do this for comparisons here. */
+ if (GET_RTX_CLASS (code) == 'c'
+ && ((CONSTANT_P (XEXP (x, 0)) && GET_CODE (XEXP (x, 1)) != CONST_INT)
+ || (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == 'o'
+ && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) != 'o')
+ || (GET_CODE (XEXP (x, 0)) == SUBREG
+ && GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (x, 0)))) == 'o'
+ && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) != 'o')))
+ {
+ temp = XEXP (x, 0);
+ SUBST (XEXP (x, 0), XEXP (x, 1));
+ SUBST (XEXP (x, 1), temp);
+ }
+
+ /* If this is a PLUS, MINUS, or MULT, and the first operand is the
+ sign extension of a PLUS with a constant, reverse the order of the sign
+ extension and the addition. Note that this not the same as the original
+ code, but overflow is undefined for signed values. Also note that the
+ PLUS will have been partially moved "inside" the sign-extension, so that
+ the first operand of X will really look like:
+ (ashiftrt (plus (ashift A C4) C5) C4).
+ We convert this to
+ (plus (ashiftrt (ashift A C4) C2) C4)
+ and replace the first operand of X with that expression. Later parts
+ of this function may simplify the expression further.
+
+ For example, if we start with (mult (sign_extend (plus A C1)) C2),
+ we swap the SIGN_EXTEND and PLUS. Later code will apply the
+ distributive law to produce (plus (mult (sign_extend X) C1) C3).
+
+ We do this to simplify address expressions. */
+
+ if ((code == PLUS || code == MINUS || code == MULT)
+ && GET_CODE (XEXP (x, 0)) == ASHIFTRT
+ && GET_CODE (XEXP (XEXP (x, 0), 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == ASHIFT
+ && GET_CODE (XEXP (XEXP (XEXP (XEXP (x, 0), 0), 0), 1)) == CONST_INT
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+ && XEXP (XEXP (XEXP (XEXP (x, 0), 0), 0), 1) == XEXP (XEXP (x, 0), 1)
+ && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT
+ && (temp = simplify_binary_operation (ASHIFTRT, mode,
+ XEXP (XEXP (XEXP (x, 0), 0), 1),
+ XEXP (XEXP (x, 0), 1))) != 0)
+ {
+ rtx new
+ = simplify_shift_const (NULL_RTX, ASHIFT, mode,
+ XEXP (XEXP (XEXP (XEXP (x, 0), 0), 0), 0),
+ INTVAL (XEXP (XEXP (x, 0), 1)));
+
+ new = simplify_shift_const (NULL_RTX, ASHIFTRT, mode, new,
+ INTVAL (XEXP (XEXP (x, 0), 1)));
+
+ SUBST (XEXP (x, 0), gen_binary (PLUS, mode, new, temp));
+ }
+
+ /* If this is a simple operation applied to an IF_THEN_ELSE, try
+ applying it to the arms of the IF_THEN_ELSE. This often simplifies
+ things. Check for cases where both arms are testing the same
+ condition.
+
+ Don't do anything if all operands are very simple. */
+
+ if (((GET_RTX_CLASS (code) == '2' || GET_RTX_CLASS (code) == 'c'
+ || GET_RTX_CLASS (code) == '<')
+ && ((GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) != 'o'
+ && ! (GET_CODE (XEXP (x, 0)) == SUBREG
+ && (GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (x, 0))))
+ == 'o')))
+ || (GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) != 'o'
+ && ! (GET_CODE (XEXP (x, 1)) == SUBREG
+ && (GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (x, 1))))
+ == 'o')))))
+ || (GET_RTX_CLASS (code) == '1'
+ && ((GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) != 'o'
+ && ! (GET_CODE (XEXP (x, 0)) == SUBREG
+ && (GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (x, 0))))
+ == 'o'))))))
+ {
+ rtx cond, true, false;
+
+ cond = if_then_else_cond (x, &true, &false);
+ if (cond != 0)
+ {
+ rtx cop1 = const0_rtx;
+ enum rtx_code cond_code = simplify_comparison (NE, &cond, &cop1);
+
+ /* Simplify the alternative arms; this may collapse the true and
+ false arms to store-flag values. */
+ true = subst (true, pc_rtx, pc_rtx, 0, 0);
+ false = subst (false, pc_rtx, pc_rtx, 0, 0);
+
+ /* Restarting if we generate a store-flag expression will cause
+ us to loop. Just drop through in this case. */
+
+ /* If the result values are STORE_FLAG_VALUE and zero, we can
+ just make the comparison operation. */
+ if (true == const_true_rtx && false == const0_rtx)
+ x = gen_binary (cond_code, mode, cond, cop1);
+ else if (true == const0_rtx && false == const_true_rtx)
+ x = gen_binary (reverse_condition (cond_code), mode, cond, cop1);
+
+ /* Likewise, we can make the negate of a comparison operation
+ if the result values are - STORE_FLAG_VALUE and zero. */
+ else if (GET_CODE (true) == CONST_INT
+ && INTVAL (true) == - STORE_FLAG_VALUE
+ && false == const0_rtx)
+ x = gen_unary (NEG, mode, mode,
+ gen_binary (cond_code, mode, cond, cop1));
+ else if (GET_CODE (false) == CONST_INT
+ && INTVAL (false) == - STORE_FLAG_VALUE
+ && true == const0_rtx)
+ x = gen_unary (NEG, mode, mode,
+ gen_binary (reverse_condition (cond_code),
+ mode, cond, cop1));
+ else
+ return gen_rtx (IF_THEN_ELSE, mode,
+ gen_binary (cond_code, VOIDmode, cond, cop1),
+ true, false);
+
+ code = GET_CODE (x);
+ op0_mode = VOIDmode;
+ }
+ }
+
+ /* Try to fold this expression in case we have constants that weren't
+ present before. */
+ temp = 0;
+ switch (GET_RTX_CLASS (code))
+ {
+ case '1':
+ temp = simplify_unary_operation (code, mode, XEXP (x, 0), op0_mode);
+ break;
+ case '<':
+ temp = simplify_relational_operation (code, op0_mode,
+ XEXP (x, 0), XEXP (x, 1));
+#ifdef FLOAT_STORE_FLAG_VALUE
+ if (temp != 0 && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+ temp = ((temp == const0_rtx) ? CONST0_RTX (GET_MODE (x))
+ : immed_real_const_1 (FLOAT_STORE_FLAG_VALUE, GET_MODE (x)));
+#endif
+ break;
+ case 'c':
+ case '2':
+ temp = simplify_binary_operation (code, mode, XEXP (x, 0), XEXP (x, 1));
+ break;
+ case 'b':
+ case '3':
+ temp = simplify_ternary_operation (code, mode, op0_mode, XEXP (x, 0),
+ XEXP (x, 1), XEXP (x, 2));
+ break;
+ }
+
+ if (temp)
+ x = temp, code = GET_CODE (temp);
+
+ /* First see if we can apply the inverse distributive law. */
+ if (code == PLUS || code == MINUS
+ || code == AND || code == IOR || code == XOR)
+ {
+ x = apply_distributive_law (x);
+ code = GET_CODE (x);
+ }
+
+ /* If CODE is an associative operation not otherwise handled, see if we
+ can associate some operands. This can win if they are constants or
+ if they are logically related (i.e. (a & b) & a. */
+ if ((code == PLUS || code == MINUS
+ || code == MULT || code == AND || code == IOR || code == XOR
+ || code == DIV || code == UDIV
+ || code == SMAX || code == SMIN || code == UMAX || code == UMIN)
+ && INTEGRAL_MODE_P (mode))
+ {
+ if (GET_CODE (XEXP (x, 0)) == code)
+ {
+ rtx other = XEXP (XEXP (x, 0), 0);
+ rtx inner_op0 = XEXP (XEXP (x, 0), 1);
+ rtx inner_op1 = XEXP (x, 1);
+ rtx inner;
+
+ /* Make sure we pass the constant operand if any as the second
+ one if this is a commutative operation. */
+ if (CONSTANT_P (inner_op0) && GET_RTX_CLASS (code) == 'c')
+ {
+ rtx tem = inner_op0;
+ inner_op0 = inner_op1;
+ inner_op1 = tem;
+ }
+ inner = simplify_binary_operation (code == MINUS ? PLUS
+ : code == DIV ? MULT
+ : code == UDIV ? MULT
+ : code,
+ mode, inner_op0, inner_op1);
+
+ /* For commutative operations, try the other pair if that one
+ didn't simplify. */
+ if (inner == 0 && GET_RTX_CLASS (code) == 'c')
+ {
+ other = XEXP (XEXP (x, 0), 1);
+ inner = simplify_binary_operation (code, mode,
+ XEXP (XEXP (x, 0), 0),
+ XEXP (x, 1));
+ }
+
+ if (inner)
+ return gen_binary (code, mode, other, inner);
+ }
+ }
+
+ /* A little bit of algebraic simplification here. */
+ switch (code)
+ {
+ case MEM:
+ /* Ensure that our address has any ASHIFTs converted to MULT in case
+ address-recognizing predicates are called later. */
+ temp = make_compound_operation (XEXP (x, 0), MEM);
+ SUBST (XEXP (x, 0), temp);
+ break;
+
+ case SUBREG:
+ /* (subreg:A (mem:B X) N) becomes a modified MEM unless the SUBREG
+ is paradoxical. If we can't do that safely, then it becomes
+ something nonsensical so that this combination won't take place. */
+
+ if (GET_CODE (SUBREG_REG (x)) == MEM
+ && (GET_MODE_SIZE (mode)
+ <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))))
+ {
+ rtx inner = SUBREG_REG (x);
+ int endian_offset = 0;
+ /* Don't change the mode of the MEM
+ if that would change the meaning of the address. */
+ if (MEM_VOLATILE_P (SUBREG_REG (x))
+ || mode_dependent_address_p (XEXP (inner, 0)))
+ return gen_rtx (CLOBBER, mode, const0_rtx);
+
+#if BYTES_BIG_ENDIAN
+ if (GET_MODE_SIZE (mode) < UNITS_PER_WORD)
+ endian_offset += UNITS_PER_WORD - GET_MODE_SIZE (mode);
+ if (GET_MODE_SIZE (GET_MODE (inner)) < UNITS_PER_WORD)
+ endian_offset -= UNITS_PER_WORD - GET_MODE_SIZE (GET_MODE (inner));
+#endif
+ /* Note if the plus_constant doesn't make a valid address
+ then this combination won't be accepted. */
+ x = gen_rtx (MEM, mode,
+ plus_constant (XEXP (inner, 0),
+ (SUBREG_WORD (x) * UNITS_PER_WORD
+ + endian_offset)));
+ MEM_VOLATILE_P (x) = MEM_VOLATILE_P (inner);
+ RTX_UNCHANGING_P (x) = RTX_UNCHANGING_P (inner);
+ MEM_IN_STRUCT_P (x) = MEM_IN_STRUCT_P (inner);
+ return x;
+ }
+
+ /* If we are in a SET_DEST, these other cases can't apply. */
+ if (in_dest)
+ return x;
+
+ /* Changing mode twice with SUBREG => just change it once,
+ or not at all if changing back to starting mode. */
+ if (GET_CODE (SUBREG_REG (x)) == SUBREG)
+ {
+ if (mode == GET_MODE (SUBREG_REG (SUBREG_REG (x)))
+ && SUBREG_WORD (x) == 0 && SUBREG_WORD (SUBREG_REG (x)) == 0)
+ return SUBREG_REG (SUBREG_REG (x));
+
+ SUBST_INT (SUBREG_WORD (x),
+ SUBREG_WORD (x) + SUBREG_WORD (SUBREG_REG (x)));
+ SUBST (SUBREG_REG (x), SUBREG_REG (SUBREG_REG (x)));
+ }
+
+ /* SUBREG of a hard register => just change the register number
+ and/or mode. If the hard register is not valid in that mode,
+ suppress this combination. If the hard register is the stack,
+ frame, or argument pointer, leave this as a SUBREG. */
+
+ if (GET_CODE (SUBREG_REG (x)) == REG
+ && REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER
+ && REGNO (SUBREG_REG (x)) != FRAME_POINTER_REGNUM
+#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ && REGNO (SUBREG_REG (x)) != HARD_FRAME_POINTER_REGNUM
+#endif
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ && REGNO (SUBREG_REG (x)) != ARG_POINTER_REGNUM
+#endif
+ && REGNO (SUBREG_REG (x)) != STACK_POINTER_REGNUM)
+ {
+ if (HARD_REGNO_MODE_OK (REGNO (SUBREG_REG (x)) + SUBREG_WORD (x),
+ mode))
+ return gen_rtx (REG, mode,
+ REGNO (SUBREG_REG (x)) + SUBREG_WORD (x));
+ else
+ return gen_rtx (CLOBBER, mode, const0_rtx);
+ }
+
+ /* For a constant, try to pick up the part we want. Handle a full
+ word and low-order part. Only do this if we are narrowing
+ the constant; if it is being widened, we have no idea what
+ the extra bits will have been set to. */
+
+ if (CONSTANT_P (SUBREG_REG (x)) && op0_mode != VOIDmode
+ && GET_MODE_SIZE (mode) == UNITS_PER_WORD
+ && GET_MODE_SIZE (op0_mode) < UNITS_PER_WORD
+ && GET_MODE_CLASS (mode) == MODE_INT)
+ {
+ temp = operand_subword (SUBREG_REG (x), SUBREG_WORD (x),
+ 0, op0_mode);
+ if (temp)
+ return temp;
+ }
+
+ /* If we want a subreg of a constant, at offset 0,
+ take the low bits. On a little-endian machine, that's
+ always valid. On a big-endian machine, it's valid
+ only if the constant's mode fits in one word. */
+ if (CONSTANT_P (SUBREG_REG (x)) && subreg_lowpart_p (x)
+ && GET_MODE_SIZE (mode) < GET_MODE_SIZE (op0_mode)
+#if WORDS_BIG_ENDIAN
+ && GET_MODE_BITSIZE (op0_mode) <= BITS_PER_WORD
+#endif
+ )
+ return gen_lowpart_for_combine (mode, SUBREG_REG (x));
+
+ /* A paradoxical SUBREG of a VOIDmode constant is the same constant,
+ since we are saying that the high bits don't matter. */
+ if (CONSTANT_P (SUBREG_REG (x)) && GET_MODE (SUBREG_REG (x)) == VOIDmode
+ && GET_MODE_SIZE (mode) > GET_MODE_SIZE (op0_mode))
+ return SUBREG_REG (x);
+
+ /* Note that we cannot do any narrowing for non-constants since
+ we might have been counting on using the fact that some bits were
+ zero. We now do this in the SET. */
+
+ break;
+
+ case NOT:
+ /* (not (plus X -1)) can become (neg X). */
+ if (GET_CODE (XEXP (x, 0)) == PLUS
+ && XEXP (XEXP (x, 0), 1) == constm1_rtx)
+ return gen_rtx_combine (NEG, mode, XEXP (XEXP (x, 0), 0));
+
+ /* Similarly, (not (neg X)) is (plus X -1). */
+ if (GET_CODE (XEXP (x, 0)) == NEG)
+ return gen_rtx_combine (PLUS, mode, XEXP (XEXP (x, 0), 0),
+ constm1_rtx);
+
+ /* (not (xor X C)) for C constant is (xor X D) with D = ~ C. */
+ if (GET_CODE (XEXP (x, 0)) == XOR
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+ && (temp = simplify_unary_operation (NOT, mode,
+ XEXP (XEXP (x, 0), 1),
+ mode)) != 0)
+ {
+ SUBST (XEXP (XEXP (x, 0), 1), temp);
+ return XEXP (x, 0);
+ }
+
+ /* (not (ashift 1 X)) is (rotate ~1 X). We used to do this for operands
+ other than 1, but that is not valid. We could do a similar
+ simplification for (not (lshiftrt C X)) where C is just the sign bit,
+ but this doesn't seem common enough to bother with. */
+ if (GET_CODE (XEXP (x, 0)) == ASHIFT
+ && XEXP (XEXP (x, 0), 0) == const1_rtx)
+ return gen_rtx (ROTATE, mode, gen_unary (NOT, mode, mode, const1_rtx),
+ XEXP (XEXP (x, 0), 1));
+
+ if (GET_CODE (XEXP (x, 0)) == SUBREG
+ && subreg_lowpart_p (XEXP (x, 0))
+ && (GET_MODE_SIZE (GET_MODE (XEXP (x, 0)))
+ < GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (x, 0)))))
+ && GET_CODE (SUBREG_REG (XEXP (x, 0))) == ASHIFT
+ && XEXP (SUBREG_REG (XEXP (x, 0)), 0) == const1_rtx)
+ {
+ enum machine_mode inner_mode = GET_MODE (SUBREG_REG (XEXP (x, 0)));
+
+ x = gen_rtx (ROTATE, inner_mode,
+ gen_unary (NOT, inner_mode, inner_mode, const1_rtx),
+ XEXP (SUBREG_REG (XEXP (x, 0)), 1));
+ return gen_lowpart_for_combine (mode, x);
+ }
+
+#if STORE_FLAG_VALUE == -1
+ /* (not (comparison foo bar)) can be done by reversing the comparison
+ code if valid. */
+ if (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
+ && reversible_comparison_p (XEXP (x, 0)))
+ return gen_rtx_combine (reverse_condition (GET_CODE (XEXP (x, 0))),
+ mode, XEXP (XEXP (x, 0), 0),
+ XEXP (XEXP (x, 0), 1));
+
+ /* (ashiftrt foo C) where C is the number of bits in FOO minus 1
+ is (lt foo (const_int 0)), so we can perform the above
+ simplification. */
+
+ if (XEXP (x, 1) == const1_rtx
+ && GET_CODE (XEXP (x, 0)) == ASHIFTRT
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+ && INTVAL (XEXP (XEXP (x, 0), 1)) == GET_MODE_BITSIZE (mode) - 1)
+ return gen_rtx_combine (GE, mode, XEXP (XEXP (x, 0), 0), const0_rtx);
+#endif
+
+ /* Apply De Morgan's laws to reduce number of patterns for machines
+ with negating logical insns (and-not, nand, etc.). If result has
+ only one NOT, put it first, since that is how the patterns are
+ coded. */
+
+ if (GET_CODE (XEXP (x, 0)) == IOR || GET_CODE (XEXP (x, 0)) == AND)
+ {
+ rtx in1 = XEXP (XEXP (x, 0), 0), in2 = XEXP (XEXP (x, 0), 1);
+
+ if (GET_CODE (in1) == NOT)
+ in1 = XEXP (in1, 0);
+ else
+ in1 = gen_rtx_combine (NOT, GET_MODE (in1), in1);
+
+ if (GET_CODE (in2) == NOT)
+ in2 = XEXP (in2, 0);
+ else if (GET_CODE (in2) == CONST_INT
+ && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
+ in2 = GEN_INT (GET_MODE_MASK (mode) & ~ INTVAL (in2));
+ else
+ in2 = gen_rtx_combine (NOT, GET_MODE (in2), in2);
+
+ if (GET_CODE (in2) == NOT)
+ {
+ rtx tem = in2;
+ in2 = in1; in1 = tem;
+ }
+
+ return gen_rtx_combine (GET_CODE (XEXP (x, 0)) == IOR ? AND : IOR,
+ mode, in1, in2);
+ }
+ break;
+
+ case NEG:
+ /* (neg (plus X 1)) can become (not X). */
+ if (GET_CODE (XEXP (x, 0)) == PLUS
+ && XEXP (XEXP (x, 0), 1) == const1_rtx)
+ return gen_rtx_combine (NOT, mode, XEXP (XEXP (x, 0), 0));
+
+ /* Similarly, (neg (not X)) is (plus X 1). */
+ if (GET_CODE (XEXP (x, 0)) == NOT)
+ return plus_constant (XEXP (XEXP (x, 0), 0), 1);
+
+ /* (neg (minus X Y)) can become (minus Y X). */
+ if (GET_CODE (XEXP (x, 0)) == MINUS
+ && (! FLOAT_MODE_P (mode)
+ /* x-y != -(y-x) with IEEE floating point. */
+ || TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+ || flag_fast_math))
+ return gen_binary (MINUS, mode, XEXP (XEXP (x, 0), 1),
+ XEXP (XEXP (x, 0), 0));
+
+ /* (neg (xor A 1)) is (plus A -1) if A is known to be either 0 or 1. */
+ if (GET_CODE (XEXP (x, 0)) == XOR && XEXP (XEXP (x, 0), 1) == const1_rtx
+ && nonzero_bits (XEXP (XEXP (x, 0), 0), mode) == 1)
+ return gen_binary (PLUS, mode, XEXP (XEXP (x, 0), 0), constm1_rtx);
+
+ /* NEG commutes with ASHIFT since it is multiplication. Only do this
+ if we can then eliminate the NEG (e.g.,
+ if the operand is a constant). */
+
+ if (GET_CODE (XEXP (x, 0)) == ASHIFT)
+ {
+ temp = simplify_unary_operation (NEG, mode,
+ XEXP (XEXP (x, 0), 0), mode);
+ if (temp)
+ {
+ SUBST (XEXP (XEXP (x, 0), 0), temp);
+ return XEXP (x, 0);
+ }
+ }
+
+ temp = expand_compound_operation (XEXP (x, 0));
+
+ /* For C equal to the width of MODE minus 1, (neg (ashiftrt X C)) can be
+ replaced by (lshiftrt X C). This will convert
+ (neg (sign_extract X 1 Y)) to (zero_extract X 1 Y). */
+
+ if (GET_CODE (temp) == ASHIFTRT
+ && GET_CODE (XEXP (temp, 1)) == CONST_INT
+ && INTVAL (XEXP (temp, 1)) == GET_MODE_BITSIZE (mode) - 1)
+ return simplify_shift_const (temp, LSHIFTRT, mode, XEXP (temp, 0),
+ INTVAL (XEXP (temp, 1)));
+
+ /* If X has only a single bit that might be nonzero, say, bit I, convert
+ (neg X) to (ashiftrt (ashift X C-I) C-I) where C is the bitsize of
+ MODE minus 1. This will convert (neg (zero_extract X 1 Y)) to
+ (sign_extract X 1 Y). But only do this if TEMP isn't a register
+ or a SUBREG of one since we'd be making the expression more
+ complex if it was just a register. */
+
+ if (GET_CODE (temp) != REG
+ && ! (GET_CODE (temp) == SUBREG
+ && GET_CODE (SUBREG_REG (temp)) == REG)
+ && (i = exact_log2 (nonzero_bits (temp, mode))) >= 0)
+ {
+ rtx temp1 = simplify_shift_const
+ (NULL_RTX, ASHIFTRT, mode,
+ simplify_shift_const (NULL_RTX, ASHIFT, mode, temp,
+ GET_MODE_BITSIZE (mode) - 1 - i),
+ GET_MODE_BITSIZE (mode) - 1 - i);
+
+ /* If all we did was surround TEMP with the two shifts, we
+ haven't improved anything, so don't use it. Otherwise,
+ we are better off with TEMP1. */
+ if (GET_CODE (temp1) != ASHIFTRT
+ || GET_CODE (XEXP (temp1, 0)) != ASHIFT
+ || XEXP (XEXP (temp1, 0), 0) != temp)
+ return temp1;
+ }
+ break;
+
+ case FLOAT_TRUNCATE:
+ /* (float_truncate:SF (float_extend:DF foo:SF)) = foo:SF. */
+ if (GET_CODE (XEXP (x, 0)) == FLOAT_EXTEND
+ && GET_MODE (XEXP (XEXP (x, 0), 0)) == mode)
+ return XEXP (XEXP (x, 0), 0);
+
+ /* (float_truncate:SF (OP:DF (float_extend:DF foo:sf))) is
+ (OP:SF foo:SF) if OP is NEG or ABS. */
+ if ((GET_CODE (XEXP (x, 0)) == ABS
+ || GET_CODE (XEXP (x, 0)) == NEG)
+ && GET_CODE (XEXP (XEXP (x, 0), 0)) == FLOAT_EXTEND
+ && GET_MODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == mode)
+ return gen_unary (GET_CODE (XEXP (x, 0)), mode, mode,
+ XEXP (XEXP (XEXP (x, 0), 0), 0));
+ break;
+
+#ifdef HAVE_cc0
+ case COMPARE:
+ /* Convert (compare FOO (const_int 0)) to FOO unless we aren't
+ using cc0, in which case we want to leave it as a COMPARE
+ so we can distinguish it from a register-register-copy. */
+ if (XEXP (x, 1) == const0_rtx)
+ return XEXP (x, 0);
+
+ /* In IEEE floating point, x-0 is not the same as x. */
+ if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+ || ! FLOAT_MODE_P (GET_MODE (XEXP (x, 0)))
+ || flag_fast_math)
+ && XEXP (x, 1) == CONST0_RTX (GET_MODE (XEXP (x, 0))))
+ return XEXP (x, 0);
+ break;
+#endif
+
+ case CONST:
+ /* (const (const X)) can become (const X). Do it this way rather than
+ returning the inner CONST since CONST can be shared with a
+ REG_EQUAL note. */
+ if (GET_CODE (XEXP (x, 0)) == CONST)
+ SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0));
+ break;
+
+#ifdef HAVE_lo_sum
+ case LO_SUM:
+ /* Convert (lo_sum (high FOO) FOO) to FOO. This is necessary so we
+ can add in an offset. find_split_point will split this address up
+ again if it doesn't match. */
+ if (GET_CODE (XEXP (x, 0)) == HIGH
+ && rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1)))
+ return XEXP (x, 1);
+ break;
+#endif
+
+ case PLUS:
+ /* If we have (plus (plus (A const) B)), associate it so that CONST is
+ outermost. That's because that's the way indexed addresses are
+ supposed to appear. This code used to check many more cases, but
+ they are now checked elsewhere. */
+ if (GET_CODE (XEXP (x, 0)) == PLUS
+ && CONSTANT_ADDRESS_P (XEXP (XEXP (x, 0), 1)))
+ return gen_binary (PLUS, mode,
+ gen_binary (PLUS, mode, XEXP (XEXP (x, 0), 0),
+ XEXP (x, 1)),
+ XEXP (XEXP (x, 0), 1));
+
+ /* (plus (xor (and <foo> (const_int pow2 - 1)) <c>) <-c>)
+ when c is (const_int (pow2 + 1) / 2) is a sign extension of a
+ bit-field and can be replaced by either a sign_extend or a
+ sign_extract. The `and' may be a zero_extend. */
+ if (GET_CODE (XEXP (x, 0)) == XOR
+ && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) == - INTVAL (XEXP (XEXP (x, 0), 1))
+ && (i = exact_log2 (INTVAL (XEXP (XEXP (x, 0), 1)))) >= 0
+ && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+ && ((GET_CODE (XEXP (XEXP (x, 0), 0)) == AND
+ && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT
+ && (INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1))
+ == ((HOST_WIDE_INT) 1 << (i + 1)) - 1))
+ || (GET_CODE (XEXP (XEXP (x, 0), 0)) == ZERO_EXTEND
+ && (GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (XEXP (x, 0), 0), 0)))
+ == i + 1))))
+ return simplify_shift_const
+ (NULL_RTX, ASHIFTRT, mode,
+ simplify_shift_const (NULL_RTX, ASHIFT, mode,
+ XEXP (XEXP (XEXP (x, 0), 0), 0),
+ GET_MODE_BITSIZE (mode) - (i + 1)),
+ GET_MODE_BITSIZE (mode) - (i + 1));
+
+ /* (plus (comparison A B) C) can become (neg (rev-comp A B)) if
+ C is 1 and STORE_FLAG_VALUE is -1 or if C is -1 and STORE_FLAG_VALUE
+ is 1. This produces better code than the alternative immediately
+ below. */
+ if (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
+ && reversible_comparison_p (XEXP (x, 0))
+ && ((STORE_FLAG_VALUE == -1 && XEXP (x, 1) == const1_rtx)
+ || (STORE_FLAG_VALUE == 1 && XEXP (x, 1) == constm1_rtx)))
+ return
+ gen_unary (NEG, mode, mode,
+ gen_binary (reverse_condition (GET_CODE (XEXP (x, 0))),
+ mode, XEXP (XEXP (x, 0), 0),
+ XEXP (XEXP (x, 0), 1)));
+
+ /* If only the low-order bit of X is possibly nonzero, (plus x -1)
+ can become (ashiftrt (ashift (xor x 1) C) C) where C is
+ the bitsize of the mode - 1. This allows simplification of
+ "a = (b & 8) == 0;" */
+ if (XEXP (x, 1) == constm1_rtx
+ && GET_CODE (XEXP (x, 0)) != REG
+ && ! (GET_CODE (XEXP (x,0)) == SUBREG
+ && GET_CODE (SUBREG_REG (XEXP (x, 0))) == REG)
+ && nonzero_bits (XEXP (x, 0), mode) == 1)
+ return simplify_shift_const (NULL_RTX, ASHIFTRT, mode,
+ simplify_shift_const (NULL_RTX, ASHIFT, mode,
+ gen_rtx_combine (XOR, mode,
+ XEXP (x, 0), const1_rtx),
+ GET_MODE_BITSIZE (mode) - 1),
+ GET_MODE_BITSIZE (mode) - 1);
+
+ /* If we are adding two things that have no bits in common, convert
+ the addition into an IOR. This will often be further simplified,
+ for example in cases like ((a & 1) + (a & 2)), which can
+ become a & 3. */
+
+ if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+ && (nonzero_bits (XEXP (x, 0), mode)
+ & nonzero_bits (XEXP (x, 1), mode)) == 0)
+ return gen_binary (IOR, mode, XEXP (x, 0), XEXP (x, 1));
+ break;
+
+ case MINUS:
+#if STORE_FLAG_VALUE == 1
+ /* (minus 1 (comparison foo bar)) can be done by reversing the comparison
+ code if valid. */
+ if (XEXP (x, 0) == const1_rtx
+ && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<'
+ && reversible_comparison_p (XEXP (x, 1)))
+ return gen_binary (reverse_condition (GET_CODE (XEXP (x, 1))),
+ mode, XEXP (XEXP (x, 1), 0),
+ XEXP (XEXP (x, 1), 1));
+#endif
+
+ /* (minus <foo> (and <foo> (const_int -pow2))) becomes
+ (and <foo> (const_int pow2-1)) */
+ if (GET_CODE (XEXP (x, 1)) == AND
+ && GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT
+ && exact_log2 (- INTVAL (XEXP (XEXP (x, 1), 1))) >= 0
+ && rtx_equal_p (XEXP (XEXP (x, 1), 0), XEXP (x, 0)))
+ return simplify_and_const_int (NULL_RTX, mode, XEXP (x, 0),
+ - INTVAL (XEXP (XEXP (x, 1), 1)) - 1);
+
+ /* Canonicalize (minus A (plus B C)) to (minus (minus A B) C) for
+ integers. */
+ if (GET_CODE (XEXP (x, 1)) == PLUS && INTEGRAL_MODE_P (mode))
+ return gen_binary (MINUS, mode,
+ gen_binary (MINUS, mode, XEXP (x, 0),
+ XEXP (XEXP (x, 1), 0)),
+ XEXP (XEXP (x, 1), 1));
+ break;
+
+ case MULT:
+ /* If we have (mult (plus A B) C), apply the distributive law and then
+ the inverse distributive law to see if things simplify. This
+ occurs mostly in addresses, often when unrolling loops. */
+
+ if (GET_CODE (XEXP (x, 0)) == PLUS)
+ {
+ x = apply_distributive_law
+ (gen_binary (PLUS, mode,
+ gen_binary (MULT, mode,
+ XEXP (XEXP (x, 0), 0), XEXP (x, 1)),
+ gen_binary (MULT, mode,
+ XEXP (XEXP (x, 0), 1), XEXP (x, 1))));
+
+ if (GET_CODE (x) != MULT)
+ return x;
+ }
+ break;
+
+ case UDIV:
+ /* If this is a divide by a power of two, treat it as a shift if
+ its first operand is a shift. */
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && (i = exact_log2 (INTVAL (XEXP (x, 1)))) >= 0
+ && (GET_CODE (XEXP (x, 0)) == ASHIFT
+ || GET_CODE (XEXP (x, 0)) == LSHIFTRT
+ || GET_CODE (XEXP (x, 0)) == ASHIFTRT
+ || GET_CODE (XEXP (x, 0)) == ROTATE
+ || GET_CODE (XEXP (x, 0)) == ROTATERT))
+ return simplify_shift_const (NULL_RTX, LSHIFTRT, mode, XEXP (x, 0), i);
+ break;
+
+ case EQ: case NE:
+ case GT: case GTU: case GE: case GEU:
+ case LT: case LTU: case LE: case LEU:
+ /* If the first operand is a condition code, we can't do anything
+ with it. */
+ if (GET_CODE (XEXP (x, 0)) == COMPARE
+ || (GET_MODE_CLASS (GET_MODE (XEXP (x, 0))) != MODE_CC
+#ifdef HAVE_cc0
+ && XEXP (x, 0) != cc0_rtx
+#endif
+ ))
+ {
+ rtx op0 = XEXP (x, 0);
+ rtx op1 = XEXP (x, 1);
+ enum rtx_code new_code;
+
+ if (GET_CODE (op0) == COMPARE)
+ op1 = XEXP (op0, 1), op0 = XEXP (op0, 0);
+
+ /* Simplify our comparison, if possible. */
+ new_code = simplify_comparison (code, &op0, &op1);
+
+#if STORE_FLAG_VALUE == 1
+ /* If STORE_FLAG_VALUE is 1, we can convert (ne x 0) to simply X
+ if only the low-order bit is possibly nonzero in X (such as when
+ X is a ZERO_EXTRACT of one bit). Similarly, we can convert EQ to
+ (xor X 1) or (minus 1 X); we use the former. Finally, if X is
+ known to be either 0 or -1, NE becomes a NEG and EQ becomes
+ (plus X 1).
+
+ Remove any ZERO_EXTRACT we made when thinking this was a
+ comparison. It may now be simpler to use, e.g., an AND. If a
+ ZERO_EXTRACT is indeed appropriate, it will be placed back by
+ the call to make_compound_operation in the SET case. */
+
+ if (new_code == NE && GET_MODE_CLASS (mode) == MODE_INT
+ && op1 == const0_rtx
+ && nonzero_bits (op0, mode) == 1)
+ return gen_lowpart_for_combine (mode,
+ expand_compound_operation (op0));
+
+ else if (new_code == NE && GET_MODE_CLASS (mode) == MODE_INT
+ && op1 == const0_rtx
+ && (num_sign_bit_copies (op0, mode)
+ == GET_MODE_BITSIZE (mode)))
+ {
+ op0 = expand_compound_operation (op0);
+ return gen_unary (NEG, mode, mode,
+ gen_lowpart_for_combine (mode, op0));
+ }
+
+ else if (new_code == EQ && GET_MODE_CLASS (mode) == MODE_INT
+ && op1 == const0_rtx
+ && nonzero_bits (op0, mode) == 1)
+ {
+ op0 = expand_compound_operation (op0);
+ return gen_binary (XOR, mode,
+ gen_lowpart_for_combine (mode, op0),
+ const1_rtx);
+ }
+
+ else if (new_code == EQ && GET_MODE_CLASS (mode) == MODE_INT
+ && op1 == const0_rtx
+ && (num_sign_bit_copies (op0, mode)
+ == GET_MODE_BITSIZE (mode)))
+ {
+ op0 = expand_compound_operation (op0);
+ return plus_constant (gen_lowpart_for_combine (mode, op0), 1);
+ }
+#endif
+
+#if STORE_FLAG_VALUE == -1
+ /* If STORE_FLAG_VALUE is -1, we have cases similar to
+ those above. */
+ if (new_code == NE && GET_MODE_CLASS (mode) == MODE_INT
+ && op1 == const0_rtx
+ && (num_sign_bit_copies (op0, mode)
+ == GET_MODE_BITSIZE (mode)))
+ return gen_lowpart_for_combine (mode,
+ expand_compound_operation (op0));
+
+ else if (new_code == NE && GET_MODE_CLASS (mode) == MODE_INT
+ && op1 == const0_rtx
+ && nonzero_bits (op0, mode) == 1)
+ {
+ op0 = expand_compound_operation (op0);
+ return gen_unary (NEG, mode, mode,
+ gen_lowpart_for_combine (mode, op0));
+ }
+
+ else if (new_code == EQ && GET_MODE_CLASS (mode) == MODE_INT
+ && op1 == const0_rtx
+ && (num_sign_bit_copies (op0, mode)
+ == GET_MODE_BITSIZE (mode)))
+ {
+ op0 = expand_compound_operation (op0);
+ return gen_unary (NOT, mode, mode,
+ gen_lowpart_for_combine (mode, op0));
+ }
+
+ /* If X is 0/1, (eq X 0) is X-1. */
+ else if (new_code == EQ && GET_MODE_CLASS (mode) == MODE_INT
+ && op1 == const0_rtx
+ && nonzero_bits (op0, mode) == 1)
+ {
+ op0 = expand_compound_operation (op0);
+ return plus_constant (gen_lowpart_for_combine (mode, op0), -1);
+ }
+#endif
+
+ /* If STORE_FLAG_VALUE says to just test the sign bit and X has just
+ one bit that might be nonzero, we can convert (ne x 0) to
+ (ashift x c) where C puts the bit in the sign bit. Remove any
+ AND with STORE_FLAG_VALUE when we are done, since we are only
+ going to test the sign bit. */
+ if (new_code == NE && GET_MODE_CLASS (mode) == MODE_INT
+ && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+ && (STORE_FLAG_VALUE
+ == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))
+ && op1 == const0_rtx
+ && mode == GET_MODE (op0)
+ && (i = exact_log2 (nonzero_bits (op0, mode))) >= 0)
+ {
+ x = simplify_shift_const (NULL_RTX, ASHIFT, mode,
+ expand_compound_operation (op0),
+ GET_MODE_BITSIZE (mode) - 1 - i);
+ if (GET_CODE (x) == AND && XEXP (x, 1) == const_true_rtx)
+ return XEXP (x, 0);
+ else
+ return x;
+ }
+
+ /* If the code changed, return a whole new comparison. */
+ if (new_code != code)
+ return gen_rtx_combine (new_code, mode, op0, op1);
+
+ /* Otherwise, keep this operation, but maybe change its operands.
+ This also converts (ne (compare FOO BAR) 0) to (ne FOO BAR). */
+ SUBST (XEXP (x, 0), op0);
+ SUBST (XEXP (x, 1), op1);
+ }
+ break;
+
+ case IF_THEN_ELSE:
+ return simplify_if_then_else (x);
+
+ case ZERO_EXTRACT:
+ case SIGN_EXTRACT:
+ case ZERO_EXTEND:
+ case SIGN_EXTEND:
+ /* If we are processing SET_DEST, we are done. */
+ if (in_dest)
+ return x;
+
+ return expand_compound_operation (x);
+
+ case SET:
+ return simplify_set (x);
+
+ case AND:
+ case IOR:
+ case XOR:
+ return simplify_logical (x, last);
+
+ case ABS:
+ /* (abs (neg <foo>)) -> (abs <foo>) */
+ if (GET_CODE (XEXP (x, 0)) == NEG)
+ SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0));
+
+ /* If operand is something known to be positive, ignore the ABS. */
+ if (GET_CODE (XEXP (x, 0)) == FFS || GET_CODE (XEXP (x, 0)) == ABS
+ || ((GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))
+ <= HOST_BITS_PER_WIDE_INT)
+ && ((nonzero_bits (XEXP (x, 0), GET_MODE (XEXP (x, 0)))
+ & ((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - 1)))
+ == 0)))
+ return XEXP (x, 0);
+
+
+ /* If operand is known to be only -1 or 0, convert ABS to NEG. */
+ if (num_sign_bit_copies (XEXP (x, 0), mode) == GET_MODE_BITSIZE (mode))
+ return gen_rtx_combine (NEG, mode, XEXP (x, 0));
+
+ break;
+
+ case FFS:
+ /* (ffs (*_extend <X>)) = (ffs <X>) */
+ if (GET_CODE (XEXP (x, 0)) == SIGN_EXTEND
+ || GET_CODE (XEXP (x, 0)) == ZERO_EXTEND)
+ SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0));
+ break;
+
+ case FLOAT:
+ /* (float (sign_extend <X>)) = (float <X>). */
+ if (GET_CODE (XEXP (x, 0)) == SIGN_EXTEND)
+ SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0));
+ break;
+
+ case ASHIFT:
+ case LSHIFTRT:
+ case ASHIFTRT:
+ case ROTATE:
+ case ROTATERT:
+ /* If this is a shift by a constant amount, simplify it. */
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+ return simplify_shift_const (x, code, mode, XEXP (x, 0),
+ INTVAL (XEXP (x, 1)));
+
+#ifdef SHIFT_COUNT_TRUNCATED
+ else if (SHIFT_COUNT_TRUNCATED && GET_CODE (XEXP (x, 1)) != REG)
+ SUBST (XEXP (x, 1),
+ force_to_mode (XEXP (x, 1), GET_MODE (x),
+ ((HOST_WIDE_INT) 1
+ << exact_log2 (GET_MODE_BITSIZE (GET_MODE (x))))
+ - 1,
+ NULL_RTX, 0));
+#endif
+
+ break;
+ }
+
+ return x;
+}
+
+/* Simplify X, an IF_THEN_ELSE expression. Return the new expression. */
+
+static rtx
+simplify_if_then_else (x)
+ rtx x;
+{
+ enum machine_mode mode = GET_MODE (x);
+ rtx cond = XEXP (x, 0);
+ rtx true = XEXP (x, 1);
+ rtx false = XEXP (x, 2);
+ enum rtx_code true_code = GET_CODE (cond);
+ int comparison_p = GET_RTX_CLASS (true_code) == '<';
+ rtx temp;
+ int i;
+
+ /* Simplify storing of the truth value. */
+ if (comparison_p && true == const_true_rtx && false == const0_rtx)
+ return gen_binary (true_code, mode, XEXP (cond, 0), XEXP (cond, 1));
+
+ /* Also when the truth value has to be reversed. */
+ if (comparison_p && reversible_comparison_p (cond)
+ && true == const0_rtx && false == const_true_rtx)
+ return gen_binary (reverse_condition (true_code),
+ mode, XEXP (cond, 0), XEXP (cond, 1));
+
+ /* Sometimes we can simplify the arm of an IF_THEN_ELSE if a register used
+ in it is being compared against certain values. Get the true and false
+ comparisons and see if that says anything about the value of each arm. */
+
+ if (comparison_p && reversible_comparison_p (cond)
+ && GET_CODE (XEXP (cond, 0)) == REG)
+ {
+ HOST_WIDE_INT nzb;
+ rtx from = XEXP (cond, 0);
+ enum rtx_code false_code = reverse_condition (true_code);
+ rtx true_val = XEXP (cond, 1);
+ rtx false_val = true_val;
+ int swapped = 0;
+
+ /* If FALSE_CODE is EQ, swap the codes and arms. */
+
+ if (false_code == EQ)
+ {
+ swapped = 1, true_code = EQ, false_code = NE;
+ temp = true, true = false, false = temp;
+ }
+
+ /* If we are comparing against zero and the expression being tested has
+ only a single bit that might be nonzero, that is its value when it is
+ not equal to zero. Similarly if it is known to be -1 or 0. */
+
+ if (true_code == EQ && true_val == const0_rtx
+ && exact_log2 (nzb = nonzero_bits (from, GET_MODE (from))) >= 0)
+ false_code = EQ, false_val = GEN_INT (nzb);
+ else if (true_code == EQ && true_val == const0_rtx
+ && (num_sign_bit_copies (from, GET_MODE (from))
+ == GET_MODE_BITSIZE (GET_MODE (from))))
+ false_code = EQ, false_val = constm1_rtx;
+
+ /* Now simplify an arm if we know the value of the register in the
+ branch and it is used in the arm. Be careful due to the potential
+ of locally-shared RTL. */
+
+ if (reg_mentioned_p (from, true))
+ true = subst (known_cond (copy_rtx (true), true_code, from, true_val),
+ pc_rtx, pc_rtx, 0, 0);
+ if (reg_mentioned_p (from, false))
+ false = subst (known_cond (copy_rtx (false), false_code,
+ from, false_val),
+ pc_rtx, pc_rtx, 0, 0);
+
+ SUBST (XEXP (x, 1), swapped ? false : true);
+ SUBST (XEXP (x, 2), swapped ? true : false);
+
+ true = XEXP (x, 1), false = XEXP (x, 2), true_code = GET_CODE (cond);
+ }
+
+ /* If we have (if_then_else FOO (pc) (label_ref BAR)) and FOO can be
+ reversed, do so to avoid needing two sets of patterns for
+ subtract-and-branch insns. Similarly if we have a constant in the true
+ arm, the false arm is the same as the first operand of the comparison, or
+ the false arm is more complicated than the true arm. */
+
+ if (comparison_p && reversible_comparison_p (cond)
+ && (true == pc_rtx
+ || (CONSTANT_P (true)
+ && GET_CODE (false) != CONST_INT && false != pc_rtx)
+ || true == const0_rtx
+ || (GET_RTX_CLASS (GET_CODE (true)) == 'o'
+ && GET_RTX_CLASS (GET_CODE (false)) != 'o')
+ || (GET_CODE (true) == SUBREG
+ && GET_RTX_CLASS (GET_CODE (SUBREG_REG (true))) == 'o'
+ && GET_RTX_CLASS (GET_CODE (false)) != 'o')
+ || reg_mentioned_p (true, false)
+ || rtx_equal_p (false, XEXP (cond, 0))))
+ {
+ true_code = reverse_condition (true_code);
+ SUBST (XEXP (x, 0),
+ gen_binary (true_code, GET_MODE (cond), XEXP (cond, 0),
+ XEXP (cond, 1)));
+
+ SUBST (XEXP (x, 1), false);
+ SUBST (XEXP (x, 2), true);
+
+ temp = true, true = false, false = temp, cond = XEXP (x, 0);
+ }
+
+ /* If the two arms are identical, we don't need the comparison. */
+
+ if (rtx_equal_p (true, false) && ! side_effects_p (cond))
+ return true;
+
+ /* Look for cases where we have (abs x) or (neg (abs X)). */
+
+ if (GET_MODE_CLASS (mode) == MODE_INT
+ && GET_CODE (false) == NEG
+ && rtx_equal_p (true, XEXP (false, 0))
+ && comparison_p
+ && rtx_equal_p (true, XEXP (cond, 0))
+ && ! side_effects_p (true))
+ switch (true_code)
+ {
+ case GT:
+ case GE:
+ return gen_unary (ABS, mode, mode, true);
+ case LT:
+ case LE:
+ return gen_unary (NEG, mode, mode, gen_unary (ABS, mode, mode, true));
+ }
+
+ /* Look for MIN or MAX. */
+
+ if ((! FLOAT_MODE_P (mode) | flag_fast_math)
+ && comparison_p
+ && rtx_equal_p (XEXP (cond, 0), true)
+ && rtx_equal_p (XEXP (cond, 1), false)
+ && ! side_effects_p (cond))
+ switch (true_code)
+ {
+ case GE:
+ case GT:
+ return gen_binary (SMAX, mode, true, false);
+ case LE:
+ case LT:
+ return gen_binary (SMIN, mode, true, false);
+ case GEU:
+ case GTU:
+ return gen_binary (UMAX, mode, true, false);
+ case LEU:
+ case LTU:
+ return gen_binary (UMIN, mode, true, false);
+ }
+
+#if STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1
+
+ /* If we have (if_then_else COND (OP Z C1) Z) and OP is an identity when its
+ second operand is zero, this can be done as (OP Z (mult COND C2)) where
+ C2 = C1 * STORE_FLAG_VALUE. Similarly if OP has an outer ZERO_EXTEND or
+ SIGN_EXTEND as long as Z is already extended (so we don't destroy it).
+ We can do this kind of thing in some cases when STORE_FLAG_VALUE is
+ neither of the above, but it isn't worth checking for. */
+
+ if (comparison_p && mode != VOIDmode && ! side_effects_p (x))
+ {
+ rtx t = make_compound_operation (true, SET);
+ rtx f = make_compound_operation (false, SET);
+ rtx cond_op0 = XEXP (cond, 0);
+ rtx cond_op1 = XEXP (cond, 1);
+ enum rtx_code op, extend_op = NIL;
+ enum machine_mode m = mode;
+ rtx z = 0, c1;
+
+ if ((GET_CODE (t) == PLUS || GET_CODE (t) == MINUS
+ || GET_CODE (t) == IOR || GET_CODE (t) == XOR
+ || GET_CODE (t) == ASHIFT
+ || GET_CODE (t) == LSHIFTRT || GET_CODE (t) == ASHIFTRT)
+ && rtx_equal_p (XEXP (t, 0), f))
+ c1 = XEXP (t, 1), op = GET_CODE (t), z = f;
+
+ /* If an identity-zero op is commutative, check whether there
+ would be a match if we swapped the operands. */
+ else if ((GET_CODE (t) == PLUS || GET_CODE (t) == IOR
+ || GET_CODE (t) == XOR)
+ && rtx_equal_p (XEXP (t, 1), f))
+ c1 = XEXP (t, 0), op = GET_CODE (t), z = f;
+ else if (GET_CODE (t) == SIGN_EXTEND
+ && (GET_CODE (XEXP (t, 0)) == PLUS
+ || GET_CODE (XEXP (t, 0)) == MINUS
+ || GET_CODE (XEXP (t, 0)) == IOR
+ || GET_CODE (XEXP (t, 0)) == XOR
+ || GET_CODE (XEXP (t, 0)) == ASHIFT
+ || GET_CODE (XEXP (t, 0)) == LSHIFTRT
+ || GET_CODE (XEXP (t, 0)) == ASHIFTRT)
+ && GET_CODE (XEXP (XEXP (t, 0), 0)) == SUBREG
+ && subreg_lowpart_p (XEXP (XEXP (t, 0), 0))
+ && rtx_equal_p (SUBREG_REG (XEXP (XEXP (t, 0), 0)), f)
+ && (num_sign_bit_copies (f, GET_MODE (f))
+ > (GET_MODE_BITSIZE (mode)
+ - GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (t, 0), 0))))))
+ {
+ c1 = XEXP (XEXP (t, 0), 1); z = f; op = GET_CODE (XEXP (t, 0));
+ extend_op = SIGN_EXTEND;
+ m = GET_MODE (XEXP (t, 0));
+ }
+ else if (GET_CODE (t) == SIGN_EXTEND
+ && (GET_CODE (XEXP (t, 0)) == PLUS
+ || GET_CODE (XEXP (t, 0)) == IOR
+ || GET_CODE (XEXP (t, 0)) == XOR)
+ && GET_CODE (XEXP (XEXP (t, 0), 1)) == SUBREG
+ && subreg_lowpart_p (XEXP (XEXP (t, 0), 1))
+ && rtx_equal_p (SUBREG_REG (XEXP (XEXP (t, 0), 1)), f)
+ && (num_sign_bit_copies (f, GET_MODE (f))
+ > (GET_MODE_BITSIZE (mode)
+ - GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (t, 0), 1))))))
+ {
+ c1 = XEXP (XEXP (t, 0), 0); z = f; op = GET_CODE (XEXP (t, 0));
+ extend_op = SIGN_EXTEND;
+ m = GET_MODE (XEXP (t, 0));
+ }
+ else if (GET_CODE (t) == ZERO_EXTEND
+ && (GET_CODE (XEXP (t, 0)) == PLUS
+ || GET_CODE (XEXP (t, 0)) == MINUS
+ || GET_CODE (XEXP (t, 0)) == IOR
+ || GET_CODE (XEXP (t, 0)) == XOR
+ || GET_CODE (XEXP (t, 0)) == ASHIFT
+ || GET_CODE (XEXP (t, 0)) == LSHIFTRT
+ || GET_CODE (XEXP (t, 0)) == ASHIFTRT)
+ && GET_CODE (XEXP (XEXP (t, 0), 0)) == SUBREG
+ && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+ && subreg_lowpart_p (XEXP (XEXP (t, 0), 0))
+ && rtx_equal_p (SUBREG_REG (XEXP (XEXP (t, 0), 0)), f)
+ && ((nonzero_bits (f, GET_MODE (f))
+ & ~ GET_MODE_MASK (GET_MODE (XEXP (XEXP (t, 0), 0))))
+ == 0))
+ {
+ c1 = XEXP (XEXP (t, 0), 1); z = f; op = GET_CODE (XEXP (t, 0));
+ extend_op = ZERO_EXTEND;
+ m = GET_MODE (XEXP (t, 0));
+ }
+ else if (GET_CODE (t) == ZERO_EXTEND
+ && (GET_CODE (XEXP (t, 0)) == PLUS
+ || GET_CODE (XEXP (t, 0)) == IOR
+ || GET_CODE (XEXP (t, 0)) == XOR)
+ && GET_CODE (XEXP (XEXP (t, 0), 1)) == SUBREG
+ && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+ && subreg_lowpart_p (XEXP (XEXP (t, 0), 1))
+ && rtx_equal_p (SUBREG_REG (XEXP (XEXP (t, 0), 1)), f)
+ && ((nonzero_bits (f, GET_MODE (f))
+ & ~ GET_MODE_MASK (GET_MODE (XEXP (XEXP (t, 0), 1))))
+ == 0))
+ {
+ c1 = XEXP (XEXP (t, 0), 0); z = f; op = GET_CODE (XEXP (t, 0));
+ extend_op = ZERO_EXTEND;
+ m = GET_MODE (XEXP (t, 0));
+ }
+
+ if (z)
+ {
+ temp = subst (gen_binary (true_code, m, cond_op0, cond_op1),
+ pc_rtx, pc_rtx, 0, 0);
+ temp = gen_binary (MULT, m, temp,
+ gen_binary (MULT, m, c1, const_true_rtx));
+ temp = subst (temp, pc_rtx, pc_rtx, 0, 0);
+ temp = gen_binary (op, m, gen_lowpart_for_combine (m, z), temp);
+
+ if (extend_op != NIL)
+ temp = gen_unary (extend_op, mode, m, temp);
+
+ return temp;
+ }
+ }
+#endif
+
+ /* If we have (if_then_else (ne A 0) C1 0) and either A is known to be 0 or
+ 1 and C1 is a single bit or A is known to be 0 or -1 and C1 is the
+ negation of a single bit, we can convert this operation to a shift. We
+ can actually do this more generally, but it doesn't seem worth it. */
+
+ if (true_code == NE && XEXP (cond, 1) == const0_rtx
+ && false == const0_rtx && GET_CODE (true) == CONST_INT
+ && ((1 == nonzero_bits (XEXP (cond, 0), mode)
+ && (i = exact_log2 (INTVAL (true))) >= 0)
+ || ((num_sign_bit_copies (XEXP (cond, 0), mode)
+ == GET_MODE_BITSIZE (mode))
+ && (i = exact_log2 (- INTVAL (true))) >= 0)))
+ return
+ simplify_shift_const (NULL_RTX, ASHIFT, mode,
+ gen_lowpart_for_combine (mode, XEXP (cond, 0)), i);
+
+ return x;
+}
+
+/* Simplify X, a SET expression. Return the new expression. */
+
+static rtx
+simplify_set (x)
+ rtx x;
+{
+ rtx src = SET_SRC (x);
+ rtx dest = SET_DEST (x);
+ enum machine_mode mode
+ = GET_MODE (src) != VOIDmode ? GET_MODE (src) : GET_MODE (dest);
+ rtx other_insn;
+ rtx *cc_use;
+
+ /* (set (pc) (return)) gets written as (return). */
+ if (GET_CODE (dest) == PC && GET_CODE (src) == RETURN)
+ return src;
+
+ /* Now that we know for sure which bits of SRC we are using, see if we can
+ simplify the expression for the object knowing that we only need the
+ low-order bits. */
+
+ if (GET_MODE_CLASS (mode) == MODE_INT)
+ src = force_to_mode (src, mode, GET_MODE_MASK (mode), NULL_RTX, 0);
+
+ /* If we are setting CC0 or if the source is a COMPARE, look for the use of
+ the comparison result and try to simplify it unless we already have used
+ undobuf.other_insn. */
+ if ((GET_CODE (src) == COMPARE
+#ifdef HAVE_cc0
+ || dest == cc0_rtx
+#endif
+ )
+ && (cc_use = find_single_use (dest, subst_insn, &other_insn)) != 0
+ && (undobuf.other_insn == 0 || other_insn == undobuf.other_insn)
+ && GET_RTX_CLASS (GET_CODE (*cc_use)) == '<'
+ && rtx_equal_p (XEXP (*cc_use, 0), dest))
+ {
+ enum rtx_code old_code = GET_CODE (*cc_use);
+ enum rtx_code new_code;
+ rtx op0, op1;
+ int other_changed = 0;
+ enum machine_mode compare_mode = GET_MODE (dest);
+
+ if (GET_CODE (src) == COMPARE)
+ op0 = XEXP (src, 0), op1 = XEXP (src, 1);
+ else
+ op0 = src, op1 = const0_rtx;
+
+ /* Simplify our comparison, if possible. */
+ new_code = simplify_comparison (old_code, &op0, &op1);
+
+#ifdef EXTRA_CC_MODES
+ /* If this machine has CC modes other than CCmode, check to see if we
+ need to use a different CC mode here. */
+ compare_mode = SELECT_CC_MODE (new_code, op0, op1);
+#endif /* EXTRA_CC_MODES */
+
+#if !defined (HAVE_cc0) && defined (EXTRA_CC_MODES)
+ /* If the mode changed, we have to change SET_DEST, the mode in the
+ compare, and the mode in the place SET_DEST is used. If SET_DEST is
+ a hard register, just build new versions with the proper mode. If it
+ is a pseudo, we lose unless it is only time we set the pseudo, in
+ which case we can safely change its mode. */
+ if (compare_mode != GET_MODE (dest))
+ {
+ int regno = REGNO (dest);
+ rtx new_dest = gen_rtx (REG, compare_mode, regno);
+
+ if (regno < FIRST_PSEUDO_REGISTER
+ || (reg_n_sets[regno] == 1 && ! REG_USERVAR_P (dest)))
+ {
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ SUBST (regno_reg_rtx[regno], new_dest);
+
+ SUBST (SET_DEST (x), new_dest);
+ SUBST (XEXP (*cc_use, 0), new_dest);
+ other_changed = 1;
+
+ dest = new_dest;
+ }
+ }
+#endif
+
+ /* If the code changed, we have to build a new comparison in
+ undobuf.other_insn. */
+ if (new_code != old_code)
+ {
+ unsigned HOST_WIDE_INT mask;
+
+ SUBST (*cc_use, gen_rtx_combine (new_code, GET_MODE (*cc_use),
+ dest, const0_rtx));
+
+ /* If the only change we made was to change an EQ into an NE or
+ vice versa, OP0 has only one bit that might be nonzero, and OP1
+ is zero, check if changing the user of the condition code will
+ produce a valid insn. If it won't, we can keep the original code
+ in that insn by surrounding our operation with an XOR. */
+
+ if (((old_code == NE && new_code == EQ)
+ || (old_code == EQ && new_code == NE))
+ && ! other_changed && op1 == const0_rtx
+ && GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT
+ && exact_log2 (mask = nonzero_bits (op0, GET_MODE (op0))) >= 0)
+ {
+ rtx pat = PATTERN (other_insn), note = 0;
+
+ if ((recog_for_combine (&pat, other_insn, &note) < 0
+ && ! check_asm_operands (pat)))
+ {
+ PUT_CODE (*cc_use, old_code);
+ other_insn = 0;
+
+ op0 = gen_binary (XOR, GET_MODE (op0), op0, GEN_INT (mask));
+ }
+ }
+
+ other_changed = 1;
+ }
+
+ if (other_changed)
+ undobuf.other_insn = other_insn;
+
+#ifdef HAVE_cc0
+ /* If we are now comparing against zero, change our source if
+ needed. If we do not use cc0, we always have a COMPARE. */
+ if (op1 == const0_rtx && dest == cc0_rtx)
+ {
+ SUBST (SET_SRC (x), op0);
+ src = op0;
+ }
+ else
+#endif
+
+ /* Otherwise, if we didn't previously have a COMPARE in the
+ correct mode, we need one. */
+ if (GET_CODE (src) != COMPARE || GET_MODE (src) != compare_mode)
+ {
+ SUBST (SET_SRC (x),
+ gen_rtx_combine (COMPARE, compare_mode, op0, op1));
+ src = SET_SRC (x);
+ }
+ else
+ {
+ /* Otherwise, update the COMPARE if needed. */
+ SUBST (XEXP (src, 0), op0);
+ SUBST (XEXP (src, 1), op1);
+ }
+ }
+ else
+ {
+ /* Get SET_SRC in a form where we have placed back any
+ compound expressions. Then do the checks below. */
+ src = make_compound_operation (src, SET);
+ SUBST (SET_SRC (x), src);
+ }
+
+ /* If we have (set x (subreg:m1 (op:m2 ...) 0)) with OP being some operation,
+ and X being a REG or (subreg (reg)), we may be able to convert this to
+ (set (subreg:m2 x) (op)).
+
+ We can always do this if M1 is narrower than M2 because that means that
+ we only care about the low bits of the result.
+
+ However, on machines without WORD_REGISTER_OPERATIONS defined, we cannot
+ perform a narrower operation that requested since the high-order bits will
+ be undefined. On machine where it is defined, this transformation is safe
+ as long as M1 and M2 have the same number of words. */
+
+ if (GET_CODE (src) == SUBREG && subreg_lowpart_p (src)
+ && GET_RTX_CLASS (GET_CODE (SUBREG_REG (src))) != 'o'
+ && (((GET_MODE_SIZE (GET_MODE (src)) + (UNITS_PER_WORD - 1))
+ / UNITS_PER_WORD)
+ == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))
+ + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD))
+#ifndef WORD_REGISTER_OPERATIONS
+ && (GET_MODE_SIZE (GET_MODE (src))
+ < GET_MODE_SIZE (GET_MODE (SUBREG_REG (src))))
+#endif
+ && (GET_CODE (dest) == REG
+ || (GET_CODE (dest) == SUBREG
+ && GET_CODE (SUBREG_REG (dest)) == REG)))
+ {
+ SUBST (SET_DEST (x),
+ gen_lowpart_for_combine (GET_MODE (SUBREG_REG (src)),
+ dest));
+ SUBST (SET_SRC (x), SUBREG_REG (src));
+
+ src = SET_SRC (x), dest = SET_DEST (x);
+ }
+
+#ifdef LOAD_EXTEND_OP
+ /* If we have (set FOO (subreg:M (mem:N BAR) 0)) with M wider than N, this
+ would require a paradoxical subreg. Replace the subreg with a
+ zero_extend to avoid the reload that would otherwise be required. */
+
+ if (GET_CODE (src) == SUBREG && subreg_lowpart_p (src)
+ && LOAD_EXTEND_OP (GET_MODE (SUBREG_REG (src))) != NIL
+ && SUBREG_WORD (src) == 0
+ && (GET_MODE_SIZE (GET_MODE (src))
+ > GET_MODE_SIZE (GET_MODE (SUBREG_REG (src))))
+ && GET_CODE (SUBREG_REG (src)) == MEM)
+ {
+ SUBST (SET_SRC (x),
+ gen_rtx_combine (LOAD_EXTEND_OP (GET_MODE (SUBREG_REG (src))),
+ GET_MODE (src), XEXP (src, 0)));
+
+ src = SET_SRC (x);
+ }
+#endif
+
+ /* If we don't have a conditional move, SET_SRC is an IF_THEN_ELSE, and we
+ are comparing an item known to be 0 or -1 against 0, use a logical
+ operation instead. Check for one of the arms being an IOR of the other
+ arm with some value. We compute three terms to be IOR'ed together. In
+ practice, at most two will be nonzero. Then we do the IOR's. */
+
+ if (GET_CODE (dest) != PC
+ && GET_CODE (src) == IF_THEN_ELSE
+#ifdef HAVE_conditional_move
+ && ! HAVE_conditional_move
+#endif
+ && GET_MODE_CLASS (GET_MODE (src)) == MODE_INT
+ && (GET_CODE (XEXP (src, 0)) == EQ || GET_CODE (XEXP (src, 0)) == NE)
+ && XEXP (XEXP (src, 0), 1) == const0_rtx
+ && (num_sign_bit_copies (XEXP (XEXP (src, 0), 0),
+ GET_MODE (XEXP (XEXP (src, 0), 0)))
+ == GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (src, 0), 0))))
+ && ! side_effects_p (src))
+ {
+ rtx true = (GET_CODE (XEXP (src, 0)) == NE
+ ? XEXP (src, 1) : XEXP (src, 2));
+ rtx false = (GET_CODE (XEXP (src, 0)) == NE
+ ? XEXP (src, 2) : XEXP (src, 1));
+ rtx term1 = const0_rtx, term2, term3;
+
+ if (GET_CODE (true) == IOR && rtx_equal_p (XEXP (true, 0), false))
+ term1 = false, true = XEXP (true, 1), false = const0_rtx;
+ else if (GET_CODE (true) == IOR
+ && rtx_equal_p (XEXP (true, 1), false))
+ term1 = false, true = XEXP (true, 0), false = const0_rtx;
+ else if (GET_CODE (false) == IOR
+ && rtx_equal_p (XEXP (false, 0), true))
+ term1 = true, false = XEXP (false, 1), true = const0_rtx;
+ else if (GET_CODE (false) == IOR
+ && rtx_equal_p (XEXP (false, 1), true))
+ term1 = true, false = XEXP (false, 0), true = const0_rtx;
+
+ term2 = gen_binary (AND, GET_MODE (src), XEXP (XEXP (src, 0), 0), true);
+ term3 = gen_binary (AND, GET_MODE (src),
+ gen_unary (NOT, GET_MODE (src), GET_MODE (src),
+ XEXP (XEXP (src, 0), 0)),
+ false);
+
+ SUBST (SET_SRC (x),
+ gen_binary (IOR, GET_MODE (src),
+ gen_binary (IOR, GET_MODE (src), term1, term2),
+ term3));
+
+ src = SET_SRC (x);
+ }
+
+ /* If either SRC or DEST is a CLOBBER of (const_int 0), make this
+ whole thing fail. */
+ if (GET_CODE (src) == CLOBBER && XEXP (src, 0) == const0_rtx)
+ return src;
+ else if (GET_CODE (dest) == CLOBBER && XEXP (dest, 0) == const0_rtx)
+ return dest;
+ else
+ /* Convert this into a field assignment operation, if possible. */
+ return make_field_assignment (x);
+}
+
+/* Simplify, X, and AND, IOR, or XOR operation, and return the simplified
+ result. LAST is nonzero if this is the last retry. */
+
+static rtx
+simplify_logical (x, last)
+ rtx x;
+ int last;
+{
+ enum machine_mode mode = GET_MODE (x);
+ rtx op0 = XEXP (x, 0);
+ rtx op1 = XEXP (x, 1);
+
+ switch (GET_CODE (x))
+ {
+ case AND:
+ /* Convert (A ^ B) & A to A & (~ B) since the latter is often a single
+ insn (and may simplify more). */
+ if (GET_CODE (op0) == XOR
+ && rtx_equal_p (XEXP (op0, 0), op1)
+ && ! side_effects_p (op1))
+ x = gen_binary (AND, mode,
+ gen_unary (NOT, mode, mode, XEXP (op0, 1)), op1);
+
+ if (GET_CODE (op0) == XOR
+ && rtx_equal_p (XEXP (op0, 1), op1)
+ && ! side_effects_p (op1))
+ x = gen_binary (AND, mode,
+ gen_unary (NOT, mode, mode, XEXP (op0, 0)), op1);
+
+ /* Similarly for (~ (A ^ B)) & A. */
+ if (GET_CODE (op0) == NOT
+ && GET_CODE (XEXP (op0, 0)) == XOR
+ && rtx_equal_p (XEXP (XEXP (op0, 0), 0), op1)
+ && ! side_effects_p (op1))
+ x = gen_binary (AND, mode, XEXP (XEXP (op0, 0), 1), op1);
+
+ if (GET_CODE (op0) == NOT
+ && GET_CODE (XEXP (op0, 0)) == XOR
+ && rtx_equal_p (XEXP (XEXP (op0, 0), 1), op1)
+ && ! side_effects_p (op1))
+ x = gen_binary (AND, mode, XEXP (XEXP (op0, 0), 0), op1);
+
+ if (GET_CODE (op1) == CONST_INT)
+ {
+ x = simplify_and_const_int (x, mode, op0, INTVAL (op1));
+
+ /* If we have (ior (and (X C1) C2)) and the next restart would be
+ the last, simplify this by making C1 as small as possible
+ and then exit. */
+ if (last
+ && GET_CODE (x) == IOR && GET_CODE (op0) == AND
+ && GET_CODE (XEXP (op0, 1)) == CONST_INT
+ && GET_CODE (op1) == CONST_INT)
+ return gen_binary (IOR, mode,
+ gen_binary (AND, mode, XEXP (op0, 0),
+ GEN_INT (INTVAL (XEXP (op0, 1))
+ & ~ INTVAL (op1))), op1);
+
+ if (GET_CODE (x) != AND)
+ return x;
+ }
+
+ /* Convert (A | B) & A to A. */
+ if (GET_CODE (op0) == IOR
+ && (rtx_equal_p (XEXP (op0, 0), op1)
+ || rtx_equal_p (XEXP (op0, 1), op1))
+ && ! side_effects_p (XEXP (op0, 0))
+ && ! side_effects_p (XEXP (op0, 1)))
+ return op1;
+
+ /* In the following group of tests (and those in case IOR below),
+ we start with some combination of logical operations and apply
+ the distributive law followed by the inverse distributive law.
+ Most of the time, this results in no change. However, if some of
+ the operands are the same or inverses of each other, simplifications
+ will result.
+
+ For example, (and (ior A B) (not B)) can occur as the result of
+ expanding a bit field assignment. When we apply the distributive
+ law to this, we get (ior (and (A (not B))) (and (B (not B)))),
+ which then simplifies to (and (A (not B))).
+
+ If we have (and (ior A B) C), apply the distributive law and then
+ the inverse distributive law to see if things simplify. */
+
+ if (GET_CODE (op0) == IOR || GET_CODE (op0) == XOR)
+ {
+ x = apply_distributive_law
+ (gen_binary (GET_CODE (op0), mode,
+ gen_binary (AND, mode, XEXP (op0, 0), op1),
+ gen_binary (AND, mode, XEXP (op0, 1), op1)));
+ if (GET_CODE (x) != AND)
+ return x;
+ }
+
+ if (GET_CODE (op1) == IOR || GET_CODE (op1) == XOR)
+ return apply_distributive_law
+ (gen_binary (GET_CODE (op1), mode,
+ gen_binary (AND, mode, XEXP (op1, 0), op0),
+ gen_binary (AND, mode, XEXP (op1, 1), op0)));
+
+ /* Similarly, taking advantage of the fact that
+ (and (not A) (xor B C)) == (xor (ior A B) (ior A C)) */
+
+ if (GET_CODE (op0) == NOT && GET_CODE (op1) == XOR)
+ return apply_distributive_law
+ (gen_binary (XOR, mode,
+ gen_binary (IOR, mode, XEXP (op0, 0), XEXP (op1, 0)),
+ gen_binary (IOR, mode, XEXP (op0, 0), XEXP (op1, 1))));
+
+ else if (GET_CODE (op1) == NOT && GET_CODE (op0) == XOR)
+ return apply_distributive_law
+ (gen_binary (XOR, mode,
+ gen_binary (IOR, mode, XEXP (op1, 0), XEXP (op0, 0)),
+ gen_binary (IOR, mode, XEXP (op1, 0), XEXP (op0, 1))));
+ break;
+
+ case IOR:
+ /* (ior A C) is C if all bits of A that might be nonzero are on in C. */
+ if (GET_CODE (op1) == CONST_INT
+ && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+ && (nonzero_bits (op0, mode) & ~ INTVAL (op1)) == 0)
+ return op1;
+
+ /* Convert (A & B) | A to A. */
+ if (GET_CODE (op0) == AND
+ && (rtx_equal_p (XEXP (op0, 0), op1)
+ || rtx_equal_p (XEXP (op0, 1), op1))
+ && ! side_effects_p (XEXP (op0, 0))
+ && ! side_effects_p (XEXP (op0, 1)))
+ return op1;
+
+ /* If we have (ior (and A B) C), apply the distributive law and then
+ the inverse distributive law to see if things simplify. */
+
+ if (GET_CODE (op0) == AND)
+ {
+ x = apply_distributive_law
+ (gen_binary (AND, mode,
+ gen_binary (IOR, mode, XEXP (op0, 0), op1),
+ gen_binary (IOR, mode, XEXP (op0, 1), op1)));
+
+ if (GET_CODE (x) != IOR)
+ return x;
+ }
+
+ if (GET_CODE (op1) == AND)
+ {
+ x = apply_distributive_law
+ (gen_binary (AND, mode,
+ gen_binary (IOR, mode, XEXP (op1, 0), op0),
+ gen_binary (IOR, mode, XEXP (op1, 1), op0)));
+
+ if (GET_CODE (x) != IOR)
+ return x;
+ }
+
+ /* Convert (ior (ashift A CX) (lshiftrt A CY)) where CX+CY equals the
+ mode size to (rotate A CX). */
+
+ if (((GET_CODE (op0) == ASHIFT && GET_CODE (op1) == LSHIFTRT)
+ || (GET_CODE (op1) == ASHIFT && GET_CODE (op0) == LSHIFTRT))
+ && rtx_equal_p (XEXP (op0, 0), XEXP (op1, 0))
+ && GET_CODE (XEXP (op0, 1)) == CONST_INT
+ && GET_CODE (XEXP (op1, 1)) == CONST_INT
+ && (INTVAL (XEXP (op0, 1)) + INTVAL (XEXP (op1, 1))
+ == GET_MODE_BITSIZE (mode)))
+ return gen_rtx (ROTATE, mode, XEXP (op0, 0),
+ (GET_CODE (op0) == ASHIFT
+ ? XEXP (op0, 1) : XEXP (op1, 1)));
+
+ /* If OP0 is (ashiftrt (plus ...) C), it might actually be
+ a (sign_extend (plus ...)). If so, OP1 is a CONST_INT, and the PLUS
+ does not affect any of the bits in OP1, it can really be done
+ as a PLUS and we can associate. We do this by seeing if OP1
+ can be safely shifted left C bits. */
+ if (GET_CODE (op1) == CONST_INT && GET_CODE (op0) == ASHIFTRT
+ && GET_CODE (XEXP (op0, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (op0, 0), 1)) == CONST_INT
+ && GET_CODE (XEXP (op0, 1)) == CONST_INT
+ && INTVAL (XEXP (op0, 1)) < HOST_BITS_PER_WIDE_INT)
+ {
+ int count = INTVAL (XEXP (op0, 1));
+ HOST_WIDE_INT mask = INTVAL (op1) << count;
+
+ if (mask >> count == INTVAL (op1)
+ && (mask & nonzero_bits (XEXP (op0, 0), mode)) == 0)
+ {
+ SUBST (XEXP (XEXP (op0, 0), 1),
+ GEN_INT (INTVAL (XEXP (XEXP (op0, 0), 1)) | mask));
+ return op0;
+ }
+ }
+ break;
+
+ case XOR:
+ /* Convert (XOR (NOT x) (NOT y)) to (XOR x y).
+ Also convert (XOR (NOT x) y) to (NOT (XOR x y)), similarly for
+ (NOT y). */
+ {
+ int num_negated = 0;
+
+ if (GET_CODE (op0) == NOT)
+ num_negated++, op0 = XEXP (op0, 0);
+ if (GET_CODE (op1) == NOT)
+ num_negated++, op1 = XEXP (op1, 0);
+
+ if (num_negated == 2)
+ {
+ SUBST (XEXP (x, 0), op0);
+ SUBST (XEXP (x, 1), op1);
+ }
+ else if (num_negated == 1)
+ return gen_unary (NOT, mode, mode, gen_binary (XOR, mode, op0, op1));
+ }
+
+ /* Convert (xor (and A B) B) to (and (not A) B). The latter may
+ correspond to a machine insn or result in further simplifications
+ if B is a constant. */
+
+ if (GET_CODE (op0) == AND
+ && rtx_equal_p (XEXP (op0, 1), op1)
+ && ! side_effects_p (op1))
+ return gen_binary (AND, mode,
+ gen_unary (NOT, mode, mode, XEXP (op0, 0)),
+ op1);
+
+ else if (GET_CODE (op0) == AND
+ && rtx_equal_p (XEXP (op0, 0), op1)
+ && ! side_effects_p (op1))
+ return gen_binary (AND, mode,
+ gen_unary (NOT, mode, mode, XEXP (op0, 1)),
+ op1);
+
+#if STORE_FLAG_VALUE == 1
+ /* (xor (comparison foo bar) (const_int 1)) can become the reversed
+ comparison. */
+ if (op1 == const1_rtx
+ && GET_RTX_CLASS (GET_CODE (op0)) == '<'
+ && reversible_comparison_p (op0))
+ return gen_rtx_combine (reverse_condition (GET_CODE (op0)),
+ mode, XEXP (op0, 0), XEXP (op0, 1));
+
+ /* (lshiftrt foo C) where C is the number of bits in FOO minus 1
+ is (lt foo (const_int 0)), so we can perform the above
+ simplification. */
+
+ if (op1 == const1_rtx
+ && GET_CODE (op0) == LSHIFTRT
+ && GET_CODE (XEXP (op0, 1)) == CONST_INT
+ && INTVAL (XEXP (op0, 1)) == GET_MODE_BITSIZE (mode) - 1)
+ return gen_rtx_combine (GE, mode, XEXP (op0, 0), const0_rtx);
+#endif
+
+ /* (xor (comparison foo bar) (const_int sign-bit))
+ when STORE_FLAG_VALUE is the sign bit. */
+ if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+ && (STORE_FLAG_VALUE
+ == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))
+ && op1 == const_true_rtx
+ && GET_RTX_CLASS (GET_CODE (op0)) == '<'
+ && reversible_comparison_p (op0))
+ return gen_rtx_combine (reverse_condition (GET_CODE (op0)),
+ mode, XEXP (op0, 0), XEXP (op0, 1));
+ break;
+ }
+
+ return x;
+}
+
+/* We consider ZERO_EXTRACT, SIGN_EXTRACT, and SIGN_EXTEND as "compound
+ operations" because they can be replaced with two more basic operations.
+ ZERO_EXTEND is also considered "compound" because it can be replaced with
+ an AND operation, which is simpler, though only one operation.
+
+ The function expand_compound_operation is called with an rtx expression
+ and will convert it to the appropriate shifts and AND operations,
+ simplifying at each stage.
+
+ The function make_compound_operation is called to convert an expression
+ consisting of shifts and ANDs into the equivalent compound expression.
+ It is the inverse of this function, loosely speaking. */
+
+static rtx
+expand_compound_operation (x)
+ rtx x;
+{
+ int pos = 0, len;
+ int unsignedp = 0;
+ int modewidth;
+ rtx tem;
+
+ switch (GET_CODE (x))
+ {
+ case ZERO_EXTEND:
+ unsignedp = 1;
+ case SIGN_EXTEND:
+ /* We can't necessarily use a const_int for a multiword mode;
+ it depends on implicitly extending the value.
+ Since we don't know the right way to extend it,
+ we can't tell whether the implicit way is right.
+
+ Even for a mode that is no wider than a const_int,
+ we can't win, because we need to sign extend one of its bits through
+ the rest of it, and we don't know which bit. */
+ if (GET_CODE (XEXP (x, 0)) == CONST_INT)
+ return x;
+
+ /* Return if (subreg:MODE FROM 0) is not a safe replacement for
+ (zero_extend:MODE FROM) or (sign_extend:MODE FROM). It is for any MEM
+ because (SUBREG (MEM...)) is guaranteed to cause the MEM to be
+ reloaded. If not for that, MEM's would very rarely be safe.
+
+ Reject MODEs bigger than a word, because we might not be able
+ to reference a two-register group starting with an arbitrary register
+ (and currently gen_lowpart might crash for a SUBREG). */
+
+ if (GET_MODE_SIZE (GET_MODE (XEXP (x, 0))) > UNITS_PER_WORD)
+ return x;
+
+ len = GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)));
+ /* If the inner object has VOIDmode (the only way this can happen
+ is if it is a ASM_OPERANDS), we can't do anything since we don't
+ know how much masking to do. */
+ if (len == 0)
+ return x;
+
+ break;
+
+ case ZERO_EXTRACT:
+ unsignedp = 1;
+ case SIGN_EXTRACT:
+ /* If the operand is a CLOBBER, just return it. */
+ if (GET_CODE (XEXP (x, 0)) == CLOBBER)
+ return XEXP (x, 0);
+
+ if (GET_CODE (XEXP (x, 1)) != CONST_INT
+ || GET_CODE (XEXP (x, 2)) != CONST_INT
+ || GET_MODE (XEXP (x, 0)) == VOIDmode)
+ return x;
+
+ len = INTVAL (XEXP (x, 1));
+ pos = INTVAL (XEXP (x, 2));
+
+ /* If this goes outside the object being extracted, replace the object
+ with a (use (mem ...)) construct that only combine understands
+ and is used only for this purpose. */
+ if (len + pos > GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))))
+ SUBST (XEXP (x, 0), gen_rtx (USE, GET_MODE (x), XEXP (x, 0)));
+
+#if BITS_BIG_ENDIAN
+ pos = GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - len - pos;
+#endif
+ break;
+
+ default:
+ return x;
+ }
+
+ /* If we reach here, we want to return a pair of shifts. The inner
+ shift is a left shift of BITSIZE - POS - LEN bits. The outer
+ shift is a right shift of BITSIZE - LEN bits. It is arithmetic or
+ logical depending on the value of UNSIGNEDP.
+
+ If this was a ZERO_EXTEND or ZERO_EXTRACT, this pair of shifts will be
+ converted into an AND of a shift.
+
+ We must check for the case where the left shift would have a negative
+ count. This can happen in a case like (x >> 31) & 255 on machines
+ that can't shift by a constant. On those machines, we would first
+ combine the shift with the AND to produce a variable-position
+ extraction. Then the constant of 31 would be substituted in to produce
+ a such a position. */
+
+ modewidth = GET_MODE_BITSIZE (GET_MODE (x));
+ if (modewidth >= pos - len)
+ tem = simplify_shift_const (NULL_RTX, unsignedp ? LSHIFTRT : ASHIFTRT,
+ GET_MODE (x),
+ simplify_shift_const (NULL_RTX, ASHIFT,
+ GET_MODE (x),
+ XEXP (x, 0),
+ modewidth - pos - len),
+ modewidth - len);
+
+ else if (unsignedp && len < HOST_BITS_PER_WIDE_INT)
+ tem = simplify_and_const_int (NULL_RTX, GET_MODE (x),
+ simplify_shift_const (NULL_RTX, LSHIFTRT,
+ GET_MODE (x),
+ XEXP (x, 0), pos),
+ ((HOST_WIDE_INT) 1 << len) - 1);
+ else
+ /* Any other cases we can't handle. */
+ return x;
+
+
+ /* If we couldn't do this for some reason, return the original
+ expression. */
+ if (GET_CODE (tem) == CLOBBER)
+ return x;
+
+ return tem;
+}
+
+/* X is a SET which contains an assignment of one object into
+ a part of another (such as a bit-field assignment, STRICT_LOW_PART,
+ or certain SUBREGS). If possible, convert it into a series of
+ logical operations.
+
+ We half-heartedly support variable positions, but do not at all
+ support variable lengths. */
+
+static rtx
+expand_field_assignment (x)
+ rtx x;
+{
+ rtx inner;
+ rtx pos; /* Always counts from low bit. */
+ int len;
+ rtx mask;
+ enum machine_mode compute_mode;
+
+ /* Loop until we find something we can't simplify. */
+ while (1)
+ {
+ if (GET_CODE (SET_DEST (x)) == STRICT_LOW_PART
+ && GET_CODE (XEXP (SET_DEST (x), 0)) == SUBREG)
+ {
+ inner = SUBREG_REG (XEXP (SET_DEST (x), 0));
+ len = GET_MODE_BITSIZE (GET_MODE (XEXP (SET_DEST (x), 0)));
+ pos = const0_rtx;
+ }
+ else if (GET_CODE (SET_DEST (x)) == ZERO_EXTRACT
+ && GET_CODE (XEXP (SET_DEST (x), 1)) == CONST_INT)
+ {
+ inner = XEXP (SET_DEST (x), 0);
+ len = INTVAL (XEXP (SET_DEST (x), 1));
+ pos = XEXP (SET_DEST (x), 2);
+
+ /* If the position is constant and spans the width of INNER,
+ surround INNER with a USE to indicate this. */
+ if (GET_CODE (pos) == CONST_INT
+ && INTVAL (pos) + len > GET_MODE_BITSIZE (GET_MODE (inner)))
+ inner = gen_rtx (USE, GET_MODE (SET_DEST (x)), inner);
+
+#if BITS_BIG_ENDIAN
+ if (GET_CODE (pos) == CONST_INT)
+ pos = GEN_INT (GET_MODE_BITSIZE (GET_MODE (inner)) - len
+ - INTVAL (pos));
+ else if (GET_CODE (pos) == MINUS
+ && GET_CODE (XEXP (pos, 1)) == CONST_INT
+ && (INTVAL (XEXP (pos, 1))
+ == GET_MODE_BITSIZE (GET_MODE (inner)) - len))
+ /* If position is ADJUST - X, new position is X. */
+ pos = XEXP (pos, 0);
+ else
+ pos = gen_binary (MINUS, GET_MODE (pos),
+ GEN_INT (GET_MODE_BITSIZE (GET_MODE (inner))
+ - len),
+ pos);
+#endif
+ }
+
+ /* A SUBREG between two modes that occupy the same numbers of words
+ can be done by moving the SUBREG to the source. */
+ else if (GET_CODE (SET_DEST (x)) == SUBREG
+ && (((GET_MODE_SIZE (GET_MODE (SET_DEST (x)))
+ + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)
+ == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (x))))
+ + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)))
+ {
+ x = gen_rtx (SET, VOIDmode, SUBREG_REG (SET_DEST (x)),
+ gen_lowpart_for_combine (GET_MODE (SUBREG_REG (SET_DEST (x))),
+ SET_SRC (x)));
+ continue;
+ }
+ else
+ break;
+
+ while (GET_CODE (inner) == SUBREG && subreg_lowpart_p (inner))
+ inner = SUBREG_REG (inner);
+
+ compute_mode = GET_MODE (inner);
+
+ /* Compute a mask of LEN bits, if we can do this on the host machine. */
+ if (len < HOST_BITS_PER_WIDE_INT)
+ mask = GEN_INT (((HOST_WIDE_INT) 1 << len) - 1);
+ else
+ break;
+
+ /* Now compute the equivalent expression. Make a copy of INNER
+ for the SET_DEST in case it is a MEM into which we will substitute;
+ we don't want shared RTL in that case. */
+ x = gen_rtx (SET, VOIDmode, copy_rtx (inner),
+ gen_binary (IOR, compute_mode,
+ gen_binary (AND, compute_mode,
+ gen_unary (NOT, compute_mode,
+ compute_mode,
+ gen_binary (ASHIFT,
+ compute_mode,
+ mask, pos)),
+ inner),
+ gen_binary (ASHIFT, compute_mode,
+ gen_binary (AND, compute_mode,
+ gen_lowpart_for_combine
+ (compute_mode,
+ SET_SRC (x)),
+ mask),
+ pos)));
+ }
+
+ return x;
+}
+
+/* Return an RTX for a reference to LEN bits of INNER. If POS_RTX is nonzero,
+ it is an RTX that represents a variable starting position; otherwise,
+ POS is the (constant) starting bit position (counted from the LSB).
+
+ INNER may be a USE. This will occur when we started with a bitfield
+ that went outside the boundary of the object in memory, which is
+ allowed on most machines. To isolate this case, we produce a USE
+ whose mode is wide enough and surround the MEM with it. The only
+ code that understands the USE is this routine. If it is not removed,
+ it will cause the resulting insn not to match.
+
+ UNSIGNEDP is non-zero for an unsigned reference and zero for a
+ signed reference.
+
+ IN_DEST is non-zero if this is a reference in the destination of a
+ SET. This is used when a ZERO_ or SIGN_EXTRACT isn't needed. If non-zero,
+ a STRICT_LOW_PART will be used, if zero, ZERO_EXTEND or SIGN_EXTEND will
+ be used.
+
+ IN_COMPARE is non-zero if we are in a COMPARE. This means that a
+ ZERO_EXTRACT should be built even for bits starting at bit 0.
+
+ MODE is the desired mode of the result (if IN_DEST == 0). */
+
+static rtx
+make_extraction (mode, inner, pos, pos_rtx, len,
+ unsignedp, in_dest, in_compare)
+ enum machine_mode mode;
+ rtx inner;
+ int pos;
+ rtx pos_rtx;
+ int len;
+ int unsignedp;
+ int in_dest, in_compare;
+{
+ /* This mode describes the size of the storage area
+ to fetch the overall value from. Within that, we
+ ignore the POS lowest bits, etc. */
+ enum machine_mode is_mode = GET_MODE (inner);
+ enum machine_mode inner_mode;
+ enum machine_mode wanted_mem_mode = byte_mode;
+ enum machine_mode pos_mode = word_mode;
+ enum machine_mode extraction_mode = word_mode;
+ enum machine_mode tmode = mode_for_size (len, MODE_INT, 1);
+ int spans_byte = 0;
+ rtx new = 0;
+ rtx orig_pos_rtx = pos_rtx;
+ int orig_pos;
+
+ /* Get some information about INNER and get the innermost object. */
+ if (GET_CODE (inner) == USE)
+ /* (use:SI (mem:QI foo)) stands for (mem:SI foo). */
+ /* We don't need to adjust the position because we set up the USE
+ to pretend that it was a full-word object. */
+ spans_byte = 1, inner = XEXP (inner, 0);
+ else if (GET_CODE (inner) == SUBREG && subreg_lowpart_p (inner))
+ {
+ /* If going from (subreg:SI (mem:QI ...)) to (mem:QI ...),
+ consider just the QI as the memory to extract from.
+ The subreg adds or removes high bits; its mode is
+ irrelevant to the meaning of this extraction,
+ since POS and LEN count from the lsb. */
+ if (GET_CODE (SUBREG_REG (inner)) == MEM)
+ is_mode = GET_MODE (SUBREG_REG (inner));
+ inner = SUBREG_REG (inner);
+ }
+
+ inner_mode = GET_MODE (inner);
+
+ if (pos_rtx && GET_CODE (pos_rtx) == CONST_INT)
+ pos = INTVAL (pos_rtx), pos_rtx = 0;
+
+ /* See if this can be done without an extraction. We never can if the
+ width of the field is not the same as that of some integer mode. For
+ registers, we can only avoid the extraction if the position is at the
+ low-order bit and this is either not in the destination or we have the
+ appropriate STRICT_LOW_PART operation available.
+
+ For MEM, we can avoid an extract if the field starts on an appropriate
+ boundary and we can change the mode of the memory reference. However,
+ we cannot directly access the MEM if we have a USE and the underlying
+ MEM is not TMODE. This combination means that MEM was being used in a
+ context where bits outside its mode were being referenced; that is only
+ valid in bit-field insns. */
+
+ if (tmode != BLKmode
+ && ! (spans_byte && inner_mode != tmode)
+ && ((pos_rtx == 0 && pos == 0 && GET_CODE (inner) != MEM
+ && (! in_dest
+ || (GET_CODE (inner) == REG
+ && (movstrict_optab->handlers[(int) tmode].insn_code
+ != CODE_FOR_nothing))))
+ || (GET_CODE (inner) == MEM && pos_rtx == 0
+ && (pos
+ % (STRICT_ALIGNMENT ? GET_MODE_ALIGNMENT (tmode)
+ : BITS_PER_UNIT)) == 0
+ /* We can't do this if we are widening INNER_MODE (it
+ may not be aligned, for one thing). */
+ && GET_MODE_BITSIZE (inner_mode) >= GET_MODE_BITSIZE (tmode)
+ && (inner_mode == tmode
+ || (! mode_dependent_address_p (XEXP (inner, 0))
+ && ! MEM_VOLATILE_P (inner))))))
+ {
+ /* If INNER is a MEM, make a new MEM that encompasses just the desired
+ field. If the original and current mode are the same, we need not
+ adjust the offset. Otherwise, we do if bytes big endian.
+
+ If INNER is not a MEM, get a piece consisting of the just the field
+ of interest (in this case POS must be 0). */
+
+ if (GET_CODE (inner) == MEM)
+ {
+ int offset;
+ /* POS counts from lsb, but make OFFSET count in memory order. */
+ if (BYTES_BIG_ENDIAN)
+ offset = (GET_MODE_BITSIZE (is_mode) - len - pos) / BITS_PER_UNIT;
+ else
+ offset = pos / BITS_PER_UNIT;
+
+ new = gen_rtx (MEM, tmode, plus_constant (XEXP (inner, 0), offset));
+ RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (inner);
+ MEM_VOLATILE_P (new) = MEM_VOLATILE_P (inner);
+ MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (inner);
+ }
+ else if (GET_CODE (inner) == REG)
+ {
+ /* We can't call gen_lowpart_for_combine here since we always want
+ a SUBREG and it would sometimes return a new hard register. */
+ if (tmode != inner_mode)
+ new = gen_rtx (SUBREG, tmode, inner,
+ (WORDS_BIG_ENDIAN
+ && GET_MODE_SIZE (inner_mode) > UNITS_PER_WORD
+ ? ((GET_MODE_SIZE (inner_mode)
+ - GET_MODE_SIZE (tmode))
+ / UNITS_PER_WORD)
+ : 0));
+ else
+ new = inner;
+ }
+ else
+ new = force_to_mode (inner, tmode,
+ len >= HOST_BITS_PER_WIDE_INT
+ ? GET_MODE_MASK (tmode)
+ : ((HOST_WIDE_INT) 1 << len) - 1,
+ NULL_RTX, 0);
+
+ /* If this extraction is going into the destination of a SET,
+ make a STRICT_LOW_PART unless we made a MEM. */
+
+ if (in_dest)
+ return (GET_CODE (new) == MEM ? new
+ : (GET_CODE (new) != SUBREG
+ ? gen_rtx (CLOBBER, tmode, const0_rtx)
+ : gen_rtx_combine (STRICT_LOW_PART, VOIDmode, new)));
+
+ /* Otherwise, sign- or zero-extend unless we already are in the
+ proper mode. */
+
+ return (mode == tmode ? new
+ : gen_rtx_combine (unsignedp ? ZERO_EXTEND : SIGN_EXTEND,
+ mode, new));
+ }
+
+ /* Unless this is a COMPARE or we have a funny memory reference,
+ don't do anything with zero-extending field extracts starting at
+ the low-order bit since they are simple AND operations. */
+ if (pos_rtx == 0 && pos == 0 && ! in_dest
+ && ! in_compare && ! spans_byte && unsignedp)
+ return 0;
+
+ /* Unless we are allowed to span bytes, reject this if we would be
+ spanning bytes or if the position is not a constant and the length
+ is not 1. In all other cases, we would only be going outside
+ out object in cases when an original shift would have been
+ undefined. */
+ if (! spans_byte
+ && ((pos_rtx == 0 && pos + len > GET_MODE_BITSIZE (is_mode))
+ || (pos_rtx != 0 && len != 1)))
+ return 0;
+
+ /* Get the mode to use should INNER be a MEM, the mode for the position,
+ and the mode for the result. */
+#ifdef HAVE_insv
+ if (in_dest)
+ {
+ wanted_mem_mode = insn_operand_mode[(int) CODE_FOR_insv][0];
+ pos_mode = insn_operand_mode[(int) CODE_FOR_insv][2];
+ extraction_mode = insn_operand_mode[(int) CODE_FOR_insv][3];
+ }
+#endif
+
+#ifdef HAVE_extzv
+ if (! in_dest && unsignedp)
+ {
+ wanted_mem_mode = insn_operand_mode[(int) CODE_FOR_extzv][1];
+ pos_mode = insn_operand_mode[(int) CODE_FOR_extzv][3];
+ extraction_mode = insn_operand_mode[(int) CODE_FOR_extzv][0];
+ }
+#endif
+
+#ifdef HAVE_extv
+ if (! in_dest && ! unsignedp)
+ {
+ wanted_mem_mode = insn_operand_mode[(int) CODE_FOR_extv][1];
+ pos_mode = insn_operand_mode[(int) CODE_FOR_extv][3];
+ extraction_mode = insn_operand_mode[(int) CODE_FOR_extv][0];
+ }
+#endif
+
+ /* Never narrow an object, since that might not be safe. */
+
+ if (mode != VOIDmode
+ && GET_MODE_SIZE (extraction_mode) < GET_MODE_SIZE (mode))
+ extraction_mode = mode;
+
+ if (pos_rtx && GET_MODE (pos_rtx) != VOIDmode
+ && GET_MODE_SIZE (pos_mode) < GET_MODE_SIZE (GET_MODE (pos_rtx)))
+ pos_mode = GET_MODE (pos_rtx);
+
+ /* If this is not from memory or we have to change the mode of memory and
+ cannot, the desired mode is EXTRACTION_MODE. */
+ if (GET_CODE (inner) != MEM
+ || (inner_mode != wanted_mem_mode
+ && (mode_dependent_address_p (XEXP (inner, 0))
+ || MEM_VOLATILE_P (inner))))
+ wanted_mem_mode = extraction_mode;
+
+ orig_pos = pos;
+
+#if BITS_BIG_ENDIAN
+ /* If position is constant, compute new position. Otherwise, build
+ subtraction. */
+ if (pos_rtx == 0)
+ pos = (MAX (GET_MODE_BITSIZE (is_mode), GET_MODE_BITSIZE (wanted_mem_mode))
+ - len - pos);
+ else
+ pos_rtx
+ = gen_rtx_combine (MINUS, GET_MODE (pos_rtx),
+ GEN_INT (MAX (GET_MODE_BITSIZE (is_mode),
+ GET_MODE_BITSIZE (wanted_mem_mode))
+ - len),
+ pos_rtx);
+#endif
+
+ /* If INNER has a wider mode, make it smaller. If this is a constant
+ extract, try to adjust the byte to point to the byte containing
+ the value. */
+ if (wanted_mem_mode != VOIDmode
+ && GET_MODE_SIZE (wanted_mem_mode) < GET_MODE_SIZE (is_mode)
+ && ((GET_CODE (inner) == MEM
+ && (inner_mode == wanted_mem_mode
+ || (! mode_dependent_address_p (XEXP (inner, 0))
+ && ! MEM_VOLATILE_P (inner))))))
+ {
+ int offset = 0;
+
+ /* The computations below will be correct if the machine is big
+ endian in both bits and bytes or little endian in bits and bytes.
+ If it is mixed, we must adjust. */
+
+ /* If bytes are big endian and we had a paradoxical SUBREG, we must
+ adjust OFFSET to compensate. */
+#if BYTES_BIG_ENDIAN
+ if (! spans_byte
+ && GET_MODE_SIZE (inner_mode) < GET_MODE_SIZE (is_mode))
+ offset -= GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (inner_mode);
+#endif
+
+ /* If this is a constant position, we can move to the desired byte. */
+ if (pos_rtx == 0)
+ {
+ offset += pos / BITS_PER_UNIT;
+ pos %= GET_MODE_BITSIZE (wanted_mem_mode);
+ }
+
+#if BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN
+ if (! spans_byte && is_mode != wanted_mem_mode)
+ offset = (GET_MODE_SIZE (is_mode)
+ - GET_MODE_SIZE (wanted_mem_mode) - offset);
+#endif
+
+ if (offset != 0 || inner_mode != wanted_mem_mode)
+ {
+ rtx newmem = gen_rtx (MEM, wanted_mem_mode,
+ plus_constant (XEXP (inner, 0), offset));
+ RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (inner);
+ MEM_VOLATILE_P (newmem) = MEM_VOLATILE_P (inner);
+ MEM_IN_STRUCT_P (newmem) = MEM_IN_STRUCT_P (inner);
+ inner = newmem;
+ }
+ }
+
+ /* If INNER is not memory, we can always get it into the proper mode. */
+ else if (GET_CODE (inner) != MEM)
+ inner = force_to_mode (inner, extraction_mode,
+ pos_rtx || len + orig_pos >= HOST_BITS_PER_WIDE_INT
+ ? GET_MODE_MASK (extraction_mode)
+ : (((HOST_WIDE_INT) 1 << len) - 1) << orig_pos,
+ NULL_RTX, 0);
+
+ /* Adjust mode of POS_RTX, if needed. If we want a wider mode, we
+ have to zero extend. Otherwise, we can just use a SUBREG. */
+ if (pos_rtx != 0
+ && GET_MODE_SIZE (pos_mode) > GET_MODE_SIZE (GET_MODE (pos_rtx)))
+ pos_rtx = gen_rtx_combine (ZERO_EXTEND, pos_mode, pos_rtx);
+ else if (pos_rtx != 0
+ && GET_MODE_SIZE (pos_mode) < GET_MODE_SIZE (GET_MODE (pos_rtx)))
+ pos_rtx = gen_lowpart_for_combine (pos_mode, pos_rtx);
+
+ /* Make POS_RTX unless we already have it and it is correct. If we don't
+ have a POS_RTX but we do have an ORIG_POS_RTX, the latter must
+ be a CONST_INT. */
+ if (pos_rtx == 0 && orig_pos_rtx != 0 && INTVAL (orig_pos_rtx) == pos)
+ pos_rtx = orig_pos_rtx;
+
+ else if (pos_rtx == 0)
+ pos_rtx = GEN_INT (pos);
+
+ /* Make the required operation. See if we can use existing rtx. */
+ new = gen_rtx_combine (unsignedp ? ZERO_EXTRACT : SIGN_EXTRACT,
+ extraction_mode, inner, GEN_INT (len), pos_rtx);
+ if (! in_dest)
+ new = gen_lowpart_for_combine (mode, new);
+
+ return new;
+}
+
+/* See if X contains an ASHIFT of COUNT or more bits that can be commuted
+ with any other operations in X. Return X without that shift if so. */
+
+static rtx
+extract_left_shift (x, count)
+ rtx x;
+ int count;
+{
+ enum rtx_code code = GET_CODE (x);
+ enum machine_mode mode = GET_MODE (x);
+ rtx tem;
+
+ switch (code)
+ {
+ case ASHIFT:
+ /* This is the shift itself. If it is wide enough, we will return
+ either the value being shifted if the shift count is equal to
+ COUNT or a shift for the difference. */
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) >= count)
+ return simplify_shift_const (NULL_RTX, ASHIFT, mode, XEXP (x, 0),
+ INTVAL (XEXP (x, 1)) - count);
+ break;
+
+ case NEG: case NOT:
+ if ((tem = extract_left_shift (XEXP (x, 0), count)) != 0)
+ return gen_unary (code, mode, mode, tem);
+
+ break;
+
+ case PLUS: case IOR: case XOR: case AND:
+ /* If we can safely shift this constant and we find the inner shift,
+ make a new operation. */
+ if (GET_CODE (XEXP (x,1)) == CONST_INT
+ && (INTVAL (XEXP (x, 1)) & (((HOST_WIDE_INT) 1 << count)) - 1) == 0
+ && (tem = extract_left_shift (XEXP (x, 0), count)) != 0)
+ return gen_binary (code, mode, tem,
+ GEN_INT (INTVAL (XEXP (x, 1)) >> count));
+
+ break;
+ }
+
+ return 0;
+}
+
+/* Look at the expression rooted at X. Look for expressions
+ equivalent to ZERO_EXTRACT, SIGN_EXTRACT, ZERO_EXTEND, SIGN_EXTEND.
+ Form these expressions.
+
+ Return the new rtx, usually just X.
+
+ Also, for machines like the Vax that don't have logical shift insns,
+ try to convert logical to arithmetic shift operations in cases where
+ they are equivalent. This undoes the canonicalizations to logical
+ shifts done elsewhere.
+
+ We try, as much as possible, to re-use rtl expressions to save memory.
+
+ IN_CODE says what kind of expression we are processing. Normally, it is
+ SET. In a memory address (inside a MEM, PLUS or minus, the latter two
+ being kludges), it is MEM. When processing the arguments of a comparison
+ or a COMPARE against zero, it is COMPARE. */
+
+static rtx
+make_compound_operation (x, in_code)
+ rtx x;
+ enum rtx_code in_code;
+{
+ enum rtx_code code = GET_CODE (x);
+ enum machine_mode mode = GET_MODE (x);
+ int mode_width = GET_MODE_BITSIZE (mode);
+ rtx rhs, lhs;
+ enum rtx_code next_code;
+ int i;
+ rtx new = 0;
+ rtx tem;
+ char *fmt;
+
+ /* Select the code to be used in recursive calls. Once we are inside an
+ address, we stay there. If we have a comparison, set to COMPARE,
+ but once inside, go back to our default of SET. */
+
+ next_code = (code == MEM || code == PLUS || code == MINUS ? MEM
+ : ((code == COMPARE || GET_RTX_CLASS (code) == '<')
+ && XEXP (x, 1) == const0_rtx) ? COMPARE
+ : in_code == COMPARE ? SET : in_code);
+
+ /* Process depending on the code of this operation. If NEW is set
+ non-zero, it will be returned. */
+
+ switch (code)
+ {
+ case ASHIFT:
+ /* Convert shifts by constants into multiplications if inside
+ an address. */
+ if (in_code == MEM && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT
+ && INTVAL (XEXP (x, 1)) >= 0)
+ {
+ new = make_compound_operation (XEXP (x, 0), next_code);
+ new = gen_rtx_combine (MULT, mode, new,
+ GEN_INT ((HOST_WIDE_INT) 1
+ << INTVAL (XEXP (x, 1))));
+ }
+ break;
+
+ case AND:
+ /* If the second operand is not a constant, we can't do anything
+ with it. */
+ if (GET_CODE (XEXP (x, 1)) != CONST_INT)
+ break;
+
+ /* If the constant is a power of two minus one and the first operand
+ is a logical right shift, make an extraction. */
+ if (GET_CODE (XEXP (x, 0)) == LSHIFTRT
+ && (i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0)
+ {
+ new = make_compound_operation (XEXP (XEXP (x, 0), 0), next_code);
+ new = make_extraction (mode, new, 0, XEXP (XEXP (x, 0), 1), i, 1,
+ 0, in_code == COMPARE);
+ }
+
+ /* Same as previous, but for (subreg (lshiftrt ...)) in first op. */
+ else if (GET_CODE (XEXP (x, 0)) == SUBREG
+ && subreg_lowpart_p (XEXP (x, 0))
+ && GET_CODE (SUBREG_REG (XEXP (x, 0))) == LSHIFTRT
+ && (i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0)
+ {
+ new = make_compound_operation (XEXP (SUBREG_REG (XEXP (x, 0)), 0),
+ next_code);
+ new = make_extraction (mode, new, 0,
+ XEXP (SUBREG_REG (XEXP (x, 0)), 1), i, 1,
+ 0, in_code == COMPARE);
+ }
+ /* Same as previous, but for (xor/ior (lshiftrt...) (lshiftrt...)). */
+ else if ((GET_CODE (XEXP (x, 0)) == XOR
+ || GET_CODE (XEXP (x, 0)) == IOR)
+ && GET_CODE (XEXP (XEXP (x, 0), 0)) == LSHIFTRT
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == LSHIFTRT
+ && (i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0)
+ {
+ /* Apply the distributive law, and then try to make extractions. */
+ new = gen_rtx_combine (GET_CODE (XEXP (x, 0)), mode,
+ gen_rtx (AND, mode, XEXP (XEXP (x, 0), 0),
+ XEXP (x, 1)),
+ gen_rtx (AND, mode, XEXP (XEXP (x, 0), 1),
+ XEXP (x, 1)));
+ new = make_compound_operation (new, in_code);
+ }
+
+ /* If we are have (and (rotate X C) M) and C is larger than the number
+ of bits in M, this is an extraction. */
+
+ else if (GET_CODE (XEXP (x, 0)) == ROTATE
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+ && (i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0
+ && i <= INTVAL (XEXP (XEXP (x, 0), 1)))
+ {
+ new = make_compound_operation (XEXP (XEXP (x, 0), 0), next_code);
+ new = make_extraction (mode, new,
+ (GET_MODE_BITSIZE (mode)
+ - INTVAL (XEXP (XEXP (x, 0), 1))),
+ NULL_RTX, i, 1, 0, in_code == COMPARE);
+ }
+
+ /* On machines without logical shifts, if the operand of the AND is
+ a logical shift and our mask turns off all the propagated sign
+ bits, we can replace the logical shift with an arithmetic shift. */
+ else if (ashr_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing
+ && (lshr_optab->handlers[(int) mode].insn_code
+ == CODE_FOR_nothing)
+ && GET_CODE (XEXP (x, 0)) == LSHIFTRT
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+ && INTVAL (XEXP (XEXP (x, 0), 1)) >= 0
+ && INTVAL (XEXP (XEXP (x, 0), 1)) < HOST_BITS_PER_WIDE_INT
+ && mode_width <= HOST_BITS_PER_WIDE_INT)
+ {
+ unsigned HOST_WIDE_INT mask = GET_MODE_MASK (mode);
+
+ mask >>= INTVAL (XEXP (XEXP (x, 0), 1));
+ if ((INTVAL (XEXP (x, 1)) & ~mask) == 0)
+ SUBST (XEXP (x, 0),
+ gen_rtx_combine (ASHIFTRT, mode,
+ make_compound_operation (XEXP (XEXP (x, 0), 0),
+ next_code),
+ XEXP (XEXP (x, 0), 1)));
+ }
+
+ /* If the constant is one less than a power of two, this might be
+ representable by an extraction even if no shift is present.
+ If it doesn't end up being a ZERO_EXTEND, we will ignore it unless
+ we are in a COMPARE. */
+ else if ((i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0)
+ new = make_extraction (mode,
+ make_compound_operation (XEXP (x, 0),
+ next_code),
+ 0, NULL_RTX, i, 1, 0, in_code == COMPARE);
+
+ /* If we are in a comparison and this is an AND with a power of two,
+ convert this into the appropriate bit extract. */
+ else if (in_code == COMPARE
+ && (i = exact_log2 (INTVAL (XEXP (x, 1)))) >= 0)
+ new = make_extraction (mode,
+ make_compound_operation (XEXP (x, 0),
+ next_code),
+ i, NULL_RTX, 1, 1, 0, 1);
+
+ break;
+
+ case LSHIFTRT:
+ /* If the sign bit is known to be zero, replace this with an
+ arithmetic shift. */
+ if (ashr_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing
+ && lshr_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing
+ && mode_width <= HOST_BITS_PER_WIDE_INT
+ && (nonzero_bits (XEXP (x, 0), mode) & (1 << (mode_width - 1))) == 0)
+ {
+ new = gen_rtx_combine (ASHIFTRT, mode,
+ make_compound_operation (XEXP (x, 0),
+ next_code),
+ XEXP (x, 1));
+ break;
+ }
+
+ /* ... fall through ... */
+
+ case ASHIFTRT:
+ lhs = XEXP (x, 0);
+ rhs = XEXP (x, 1);
+
+ /* If we have (ashiftrt (ashift foo C1) C2) with C2 >= C1,
+ this is a SIGN_EXTRACT. */
+ if (GET_CODE (rhs) == CONST_INT
+ && GET_CODE (lhs) == ASHIFT
+ && GET_CODE (XEXP (lhs, 1)) == CONST_INT
+ && INTVAL (rhs) >= INTVAL (XEXP (lhs, 1)))
+ {
+ new = make_compound_operation (XEXP (lhs, 0), next_code);
+ new = make_extraction (mode, new,
+ INTVAL (rhs) - INTVAL (XEXP (lhs, 1)),
+ NULL_RTX, mode_width - INTVAL (rhs),
+ code == LSHIFTRT, 0, in_code == COMPARE);
+ }
+
+ /* See if we have operations between an ASHIFTRT and an ASHIFT.
+ If so, try to merge the shifts into a SIGN_EXTEND. We could
+ also do this for some cases of SIGN_EXTRACT, but it doesn't
+ seem worth the effort; the case checked for occurs on Alpha. */
+
+ if (GET_RTX_CLASS (GET_CODE (lhs)) != 'o'
+ && ! (GET_CODE (lhs) == SUBREG
+ && (GET_RTX_CLASS (GET_CODE (SUBREG_REG (lhs))) == 'o'))
+ && GET_CODE (rhs) == CONST_INT
+ && INTVAL (rhs) < HOST_BITS_PER_WIDE_INT
+ && (new = extract_left_shift (lhs, INTVAL (rhs))) != 0)
+ new = make_extraction (mode, make_compound_operation (new, next_code),
+ 0, NULL_RTX, mode_width - INTVAL (rhs),
+ code == LSHIFTRT, 0, in_code == COMPARE);
+
+ break;
+
+ case SUBREG:
+ /* Call ourselves recursively on the inner expression. If we are
+ narrowing the object and it has a different RTL code from
+ what it originally did, do this SUBREG as a force_to_mode. */
+
+ tem = make_compound_operation (SUBREG_REG (x), in_code);
+ if (GET_CODE (tem) != GET_CODE (SUBREG_REG (x))
+ && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (tem))
+ && subreg_lowpart_p (x))
+ {
+ rtx newer = force_to_mode (tem, mode,
+ GET_MODE_MASK (mode), NULL_RTX, 0);
+
+ /* If we have something other than a SUBREG, we might have
+ done an expansion, so rerun outselves. */
+ if (GET_CODE (newer) != SUBREG)
+ newer = make_compound_operation (newer, in_code);
+
+ return newer;
+ }
+ }
+
+ if (new)
+ {
+ x = gen_lowpart_for_combine (mode, new);
+ code = GET_CODE (x);
+ }
+
+ /* Now recursively process each operand of this operation. */
+ fmt = GET_RTX_FORMAT (code);
+ for (i = 0; i < GET_RTX_LENGTH (code); i++)
+ if (fmt[i] == 'e')
+ {
+ new = make_compound_operation (XEXP (x, i), next_code);
+ SUBST (XEXP (x, i), new);
+ }
+
+ return x;
+}
+
+/* Given M see if it is a value that would select a field of bits
+ within an item, but not the entire word. Return -1 if not.
+ Otherwise, return the starting position of the field, where 0 is the
+ low-order bit.
+
+ *PLEN is set to the length of the field. */
+
+static int
+get_pos_from_mask (m, plen)
+ unsigned HOST_WIDE_INT m;
+ int *plen;
+{
+ /* Get the bit number of the first 1 bit from the right, -1 if none. */
+ int pos = exact_log2 (m & - m);
+
+ if (pos < 0)
+ return -1;
+
+ /* Now shift off the low-order zero bits and see if we have a power of
+ two minus 1. */
+ *plen = exact_log2 ((m >> pos) + 1);
+
+ if (*plen <= 0)
+ return -1;
+
+ return pos;
+}
+
+/* See if X can be simplified knowing that we will only refer to it in
+ MODE and will only refer to those bits that are nonzero in MASK.
+ If other bits are being computed or if masking operations are done
+ that select a superset of the bits in MASK, they can sometimes be
+ ignored.
+
+ Return a possibly simplified expression, but always convert X to
+ MODE. If X is a CONST_INT, AND the CONST_INT with MASK.
+
+ Also, if REG is non-zero and X is a register equal in value to REG,
+ replace X with REG.
+
+ If JUST_SELECT is nonzero, don't optimize by noticing that bits in MASK
+ are all off in X. This is used when X will be complemented, by either
+ NOT, NEG, or XOR. */
+
+static rtx
+force_to_mode (x, mode, mask, reg, just_select)
+ rtx x;
+ enum machine_mode mode;
+ unsigned HOST_WIDE_INT mask;
+ rtx reg;
+ int just_select;
+{
+ enum rtx_code code = GET_CODE (x);
+ int next_select = just_select || code == XOR || code == NOT || code == NEG;
+ enum machine_mode op_mode;
+ unsigned HOST_WIDE_INT fuller_mask, nonzero;
+ rtx op0, op1, temp;
+
+ /* If this is a CALL, don't do anything. Some of the code below
+ will do the wrong thing since the mode of a CALL is VOIDmode. */
+ if (code == CALL)
+ return x;
+
+ /* We want to perform the operation is its present mode unless we know
+ that the operation is valid in MODE, in which case we do the operation
+ in MODE. */
+ op_mode = ((GET_MODE_CLASS (mode) == GET_MODE_CLASS (GET_MODE (x))
+ && code_to_optab[(int) code] != 0
+ && (code_to_optab[(int) code]->handlers[(int) mode].insn_code
+ != CODE_FOR_nothing))
+ ? mode : GET_MODE (x));
+
+ /* It is not valid to do a right-shift in a narrower mode
+ than the one it came in with. */
+ if ((code == LSHIFTRT || code == ASHIFTRT)
+ && GET_MODE_BITSIZE (mode) < GET_MODE_BITSIZE (GET_MODE (x)))
+ op_mode = GET_MODE (x);
+
+ /* Truncate MASK to fit OP_MODE. */
+ if (op_mode)
+ mask &= GET_MODE_MASK (op_mode);
+
+ /* When we have an arithmetic operation, or a shift whose count we
+ do not know, we need to assume that all bit the up to the highest-order
+ bit in MASK will be needed. This is how we form such a mask. */
+ if (op_mode)
+ fuller_mask = (GET_MODE_BITSIZE (op_mode) >= HOST_BITS_PER_WIDE_INT
+ ? GET_MODE_MASK (op_mode)
+ : ((HOST_WIDE_INT) 1 << (floor_log2 (mask) + 1)) - 1);
+ else
+ fuller_mask = ~ (HOST_WIDE_INT) 0;
+
+ /* Determine what bits of X are guaranteed to be (non)zero. */
+ nonzero = nonzero_bits (x, mode);
+
+ /* If none of the bits in X are needed, return a zero. */
+ if (! just_select && (nonzero & mask) == 0)
+ return const0_rtx;
+
+ /* If X is a CONST_INT, return a new one. Do this here since the
+ test below will fail. */
+ if (GET_CODE (x) == CONST_INT)
+ {
+ HOST_WIDE_INT cval = INTVAL (x) & mask;
+ int width = GET_MODE_BITSIZE (mode);
+
+ /* If MODE is narrower that HOST_WIDE_INT and CVAL is a negative
+ number, sign extend it. */
+ if (width > 0 && width < HOST_BITS_PER_WIDE_INT
+ && (cval & ((HOST_WIDE_INT) 1 << (width - 1))) != 0)
+ cval |= (HOST_WIDE_INT) -1 << width;
+
+ return GEN_INT (cval);
+ }
+
+ /* If X is narrower than MODE and we want all the bits in X's mode, just
+ get X in the proper mode. */
+ if (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (mode)
+ && (GET_MODE_MASK (GET_MODE (x)) & ~ mask) == 0)
+ return gen_lowpart_for_combine (mode, x);
+
+ /* If we aren't changing the mode, X is not a SUBREG, and all zero bits in
+ MASK are already known to be zero in X, we need not do anything. */
+ if (GET_MODE (x) == mode && code != SUBREG && (~ mask & nonzero) == 0)
+ return x;
+
+ switch (code)
+ {
+ case CLOBBER:
+ /* If X is a (clobber (const_int)), return it since we know we are
+ generating something that won't match. */
+ return x;
+
+#if ! BITS_BIG_ENDIAN
+ case USE:
+ /* X is a (use (mem ..)) that was made from a bit-field extraction that
+ spanned the boundary of the MEM. If we are now masking so it is
+ within that boundary, we don't need the USE any more. */
+ if ((mask & ~ GET_MODE_MASK (GET_MODE (XEXP (x, 0)))) == 0)
+ return force_to_mode (XEXP (x, 0), mode, mask, reg, next_select);
+#endif
+
+ case SIGN_EXTEND:
+ case ZERO_EXTEND:
+ case ZERO_EXTRACT:
+ case SIGN_EXTRACT:
+ x = expand_compound_operation (x);
+ if (GET_CODE (x) != code)
+ return force_to_mode (x, mode, mask, reg, next_select);
+ break;
+
+ case REG:
+ if (reg != 0 && (rtx_equal_p (get_last_value (reg), x)
+ || rtx_equal_p (reg, get_last_value (x))))
+ x = reg;
+ break;
+
+ case SUBREG:
+ if (subreg_lowpart_p (x)
+ /* We can ignore the effect of this SUBREG if it narrows the mode or
+ if the constant masks to zero all the bits the mode doesn't
+ have. */
+ && ((GET_MODE_SIZE (GET_MODE (x))
+ < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+ || (0 == (mask
+ & GET_MODE_MASK (GET_MODE (x))
+ & ~ GET_MODE_MASK (GET_MODE (SUBREG_REG (x)))))))
+ return force_to_mode (SUBREG_REG (x), mode, mask, reg, next_select);
+ break;
+
+ case AND:
+ /* If this is an AND with a constant, convert it into an AND
+ whose constant is the AND of that constant with MASK. If it
+ remains an AND of MASK, delete it since it is redundant. */
+
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT)
+ {
+ x = simplify_and_const_int (x, op_mode, XEXP (x, 0),
+ mask & INTVAL (XEXP (x, 1)));
+
+ /* If X is still an AND, see if it is an AND with a mask that
+ is just some low-order bits. If so, and it is MASK, we don't
+ need it. */
+
+ if (GET_CODE (x) == AND && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) == mask)
+ x = XEXP (x, 0);
+
+ /* If it remains an AND, try making another AND with the bits
+ in the mode mask that aren't in MASK turned on. If the
+ constant in the AND is wide enough, this might make a
+ cheaper constant. */
+
+ if (GET_CODE (x) == AND && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && GET_MODE_MASK (GET_MODE (x)) != mask)
+ {
+ HOST_WIDE_INT cval = (INTVAL (XEXP (x, 1))
+ | (GET_MODE_MASK (GET_MODE (x)) & ~ mask));
+ int width = GET_MODE_BITSIZE (GET_MODE (x));
+ rtx y;
+
+ /* If MODE is narrower that HOST_WIDE_INT and CVAL is a negative
+ number, sign extend it. */
+ if (width > 0 && width < HOST_BITS_PER_WIDE_INT
+ && (cval & ((HOST_WIDE_INT) 1 << (width - 1))) != 0)
+ cval |= (HOST_WIDE_INT) -1 << width;
+
+ y = gen_binary (AND, GET_MODE (x), XEXP (x, 0), GEN_INT (cval));
+ if (rtx_cost (y, SET) < rtx_cost (x, SET))
+ x = y;
+ }
+
+ break;
+ }
+
+ goto binop;
+
+ case PLUS:
+ /* In (and (plus FOO C1) M), if M is a mask that just turns off
+ low-order bits (as in an alignment operation) and FOO is already
+ aligned to that boundary, mask C1 to that boundary as well.
+ This may eliminate that PLUS and, later, the AND. */
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && exact_log2 (- mask) >= 0
+ && (nonzero_bits (XEXP (x, 0), mode) & ~ mask) == 0
+ && (INTVAL (XEXP (x, 1)) & ~ mask) != 0)
+ return force_to_mode (plus_constant (XEXP (x, 0),
+ INTVAL (XEXP (x, 1)) & mask),
+ mode, mask, reg, next_select);
+
+ /* ... fall through ... */
+
+ case MINUS:
+ case MULT:
+ /* For PLUS, MINUS and MULT, we need any bits less significant than the
+ most significant bit in MASK since carries from those bits will
+ affect the bits we are interested in. */
+ mask = fuller_mask;
+ goto binop;
+
+ case IOR:
+ case XOR:
+ /* If X is (ior (lshiftrt FOO C1) C2), try to commute the IOR and
+ LSHIFTRT so we end up with an (and (lshiftrt (ior ...) ...) ...)
+ operation which may be a bitfield extraction. Ensure that the
+ constant we form is not wider than the mode of X. */
+
+ if (GET_CODE (XEXP (x, 0)) == LSHIFTRT
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+ && INTVAL (XEXP (XEXP (x, 0), 1)) >= 0
+ && INTVAL (XEXP (XEXP (x, 0), 1)) < HOST_BITS_PER_WIDE_INT
+ && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && ((INTVAL (XEXP (XEXP (x, 0), 1))
+ + floor_log2 (INTVAL (XEXP (x, 1))))
+ < GET_MODE_BITSIZE (GET_MODE (x)))
+ && (INTVAL (XEXP (x, 1))
+ & ~ nonzero_bits (XEXP (x, 0), GET_MODE (x)) == 0))
+ {
+ temp = GEN_INT ((INTVAL (XEXP (x, 1)) & mask)
+ << INTVAL (XEXP (XEXP (x, 0), 1)));
+ temp = gen_binary (GET_CODE (x), GET_MODE (x),
+ XEXP (XEXP (x, 0), 0), temp);
+ x = gen_binary (LSHIFTRT, GET_MODE (x), temp, XEXP (x, 1));
+ return force_to_mode (x, mode, mask, reg, next_select);
+ }
+
+ binop:
+ /* For most binary operations, just propagate into the operation and
+ change the mode if we have an operation of that mode. */
+
+ op0 = gen_lowpart_for_combine (op_mode,
+ force_to_mode (XEXP (x, 0), mode, mask,
+ reg, next_select));
+ op1 = gen_lowpart_for_combine (op_mode,
+ force_to_mode (XEXP (x, 1), mode, mask,
+ reg, next_select));
+
+ /* If OP1 is a CONST_INT and X is an IOR or XOR, clear bits outside
+ MASK since OP1 might have been sign-extended but we never want
+ to turn on extra bits, since combine might have previously relied
+ on them being off. */
+ if (GET_CODE (op1) == CONST_INT && (code == IOR || code == XOR)
+ && (INTVAL (op1) & mask) != 0)
+ op1 = GEN_INT (INTVAL (op1) & mask);
+
+ if (op_mode != GET_MODE (x) || op0 != XEXP (x, 0) || op1 != XEXP (x, 1))
+ x = gen_binary (code, op_mode, op0, op1);
+ break;
+
+ case ASHIFT:
+ /* For left shifts, do the same, but just for the first operand.
+ However, we cannot do anything with shifts where we cannot
+ guarantee that the counts are smaller than the size of the mode
+ because such a count will have a different meaning in a
+ wider mode. */
+
+ if (! (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) >= 0
+ && INTVAL (XEXP (x, 1)) < GET_MODE_BITSIZE (mode))
+ && ! (GET_MODE (XEXP (x, 1)) != VOIDmode
+ && (nonzero_bits (XEXP (x, 1), GET_MODE (XEXP (x, 1)))
+ < (unsigned HOST_WIDE_INT) GET_MODE_BITSIZE (mode))))
+ break;
+
+ /* If the shift count is a constant and we can do arithmetic in
+ the mode of the shift, refine which bits we need. Otherwise, use the
+ conservative form of the mask. */
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) >= 0
+ && INTVAL (XEXP (x, 1)) < GET_MODE_BITSIZE (op_mode)
+ && GET_MODE_BITSIZE (op_mode) <= HOST_BITS_PER_WIDE_INT)
+ mask >>= INTVAL (XEXP (x, 1));
+ else
+ mask = fuller_mask;
+
+ op0 = gen_lowpart_for_combine (op_mode,
+ force_to_mode (XEXP (x, 0), op_mode,
+ mask, reg, next_select));
+
+ if (op_mode != GET_MODE (x) || op0 != XEXP (x, 0))
+ x = gen_binary (code, op_mode, op0, XEXP (x, 1));
+ break;
+
+ case LSHIFTRT:
+ /* Here we can only do something if the shift count is a constant,
+ this shift constant is valid for the host, and we can do arithmetic
+ in OP_MODE. */
+
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT
+ && GET_MODE_BITSIZE (op_mode) <= HOST_BITS_PER_WIDE_INT)
+ {
+ rtx inner = XEXP (x, 0);
+
+ /* Select the mask of the bits we need for the shift operand. */
+ mask <<= INTVAL (XEXP (x, 1));
+
+ /* We can only change the mode of the shift if we can do arithmetic
+ in the mode of the shift and MASK is no wider than the width of
+ OP_MODE. */
+ if (GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT
+ || (mask & ~ GET_MODE_MASK (op_mode)) != 0)
+ op_mode = GET_MODE (x);
+
+ inner = force_to_mode (inner, op_mode, mask, reg, next_select);
+
+ if (GET_MODE (x) != op_mode || inner != XEXP (x, 0))
+ x = gen_binary (LSHIFTRT, op_mode, inner, XEXP (x, 1));
+ }
+
+ /* If we have (and (lshiftrt FOO C1) C2) where the combination of the
+ shift and AND produces only copies of the sign bit (C2 is one less
+ than a power of two), we can do this with just a shift. */
+
+ if (GET_CODE (x) == LSHIFTRT
+ && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && ((INTVAL (XEXP (x, 1))
+ + num_sign_bit_copies (XEXP (x, 0), GET_MODE (XEXP (x, 0))))
+ >= GET_MODE_BITSIZE (GET_MODE (x)))
+ && exact_log2 (mask + 1) >= 0
+ && (num_sign_bit_copies (XEXP (x, 0), GET_MODE (XEXP (x, 0)))
+ >= exact_log2 (mask + 1)))
+ x = gen_binary (LSHIFTRT, GET_MODE (x), XEXP (x, 0),
+ GEN_INT (GET_MODE_BITSIZE (GET_MODE (x))
+ - exact_log2 (mask + 1)));
+ break;
+
+ case ASHIFTRT:
+ /* If we are just looking for the sign bit, we don't need this shift at
+ all, even if it has a variable count. */
+ if (mask == ((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (GET_MODE (x)) - 1)))
+ return force_to_mode (XEXP (x, 0), mode, mask, reg, next_select);
+
+ /* If this is a shift by a constant, get a mask that contains those bits
+ that are not copies of the sign bit. We then have two cases: If
+ MASK only includes those bits, this can be a logical shift, which may
+ allow simplifications. If MASK is a single-bit field not within
+ those bits, we are requesting a copy of the sign bit and hence can
+ shift the sign bit to the appropriate location. */
+
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) >= 0
+ && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT)
+ {
+ int i = -1;
+
+ nonzero = GET_MODE_MASK (GET_MODE (x));
+ nonzero >>= INTVAL (XEXP (x, 1));
+
+ if ((mask & ~ nonzero) == 0
+ || (i = exact_log2 (mask)) >= 0)
+ {
+ x = simplify_shift_const
+ (x, LSHIFTRT, GET_MODE (x), XEXP (x, 0),
+ i < 0 ? INTVAL (XEXP (x, 1))
+ : GET_MODE_BITSIZE (GET_MODE (x)) - 1 - i);
+
+ if (GET_CODE (x) != ASHIFTRT)
+ return force_to_mode (x, mode, mask, reg, next_select);
+ }
+ }
+
+ /* If MASK is 1, convert this to a LSHIFTRT. This can be done
+ even if the shift count isn't a constant. */
+ if (mask == 1)
+ x = gen_binary (LSHIFTRT, GET_MODE (x), XEXP (x, 0), XEXP (x, 1));
+
+ /* If this is a sign-extension operation that just affects bits
+ we don't care about, remove it. Be sure the call above returned
+ something that is still a shift. */
+
+ if ((GET_CODE (x) == LSHIFTRT || GET_CODE (x) == ASHIFTRT)
+ && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) >= 0
+ && (INTVAL (XEXP (x, 1))
+ <= GET_MODE_BITSIZE (GET_MODE (x)) - (floor_log2 (mask) + 1))
+ && GET_CODE (XEXP (x, 0)) == ASHIFT
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+ && INTVAL (XEXP (XEXP (x, 0), 1)) == INTVAL (XEXP (x, 1)))
+ return force_to_mode (XEXP (XEXP (x, 0), 0), mode, mask,
+ reg, next_select);
+
+ break;
+
+ case ROTATE:
+ case ROTATERT:
+ /* If the shift count is constant and we can do computations
+ in the mode of X, compute where the bits we care about are.
+ Otherwise, we can't do anything. Don't change the mode of
+ the shift or propagate MODE into the shift, though. */
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) >= 0)
+ {
+ temp = simplify_binary_operation (code == ROTATE ? ROTATERT : ROTATE,
+ GET_MODE (x), GEN_INT (mask),
+ XEXP (x, 1));
+ if (temp && GET_CODE(temp) == CONST_INT)
+ SUBST (XEXP (x, 0),
+ force_to_mode (XEXP (x, 0), GET_MODE (x),
+ INTVAL (temp), reg, next_select));
+ }
+ break;
+
+ case NEG:
+ /* If we just want the low-order bit, the NEG isn't needed since it
+ won't change the low-order bit. */
+ if (mask == 1)
+ return force_to_mode (XEXP (x, 0), mode, mask, reg, just_select);
+
+ /* We need any bits less significant than the most significant bit in
+ MASK since carries from those bits will affect the bits we are
+ interested in. */
+ mask = fuller_mask;
+ goto unop;
+
+ case NOT:
+ /* (not FOO) is (xor FOO CONST), so if FOO is an LSHIFTRT, we can do the
+ same as the XOR case above. Ensure that the constant we form is not
+ wider than the mode of X. */
+
+ if (GET_CODE (XEXP (x, 0)) == LSHIFTRT
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+ && INTVAL (XEXP (XEXP (x, 0), 1)) >= 0
+ && (INTVAL (XEXP (XEXP (x, 0), 1)) + floor_log2 (mask)
+ < GET_MODE_BITSIZE (GET_MODE (x)))
+ && INTVAL (XEXP (XEXP (x, 0), 1)) < HOST_BITS_PER_WIDE_INT)
+ {
+ temp = GEN_INT (mask << INTVAL (XEXP (XEXP (x, 0), 1)));
+ temp = gen_binary (XOR, GET_MODE (x), XEXP (XEXP (x, 0), 0), temp);
+ x = gen_binary (LSHIFTRT, GET_MODE (x), temp, XEXP (XEXP (x, 0), 1));
+
+ return force_to_mode (x, mode, mask, reg, next_select);
+ }
+
+ unop:
+ op0 = gen_lowpart_for_combine (op_mode,
+ force_to_mode (XEXP (x, 0), mode, mask,
+ reg, next_select));
+ if (op_mode != GET_MODE (x) || op0 != XEXP (x, 0))
+ x = gen_unary (code, op_mode, op_mode, op0);
+ break;
+
+ case NE:
+ /* (and (ne FOO 0) CONST) can be (and FOO CONST) if CONST is included
+ in STORE_FLAG_VALUE and FOO has no bits that might be nonzero not
+ in CONST. */
+ if ((mask & ~ STORE_FLAG_VALUE) == 0 && XEXP (x, 0) == const0_rtx
+ && (nonzero_bits (XEXP (x, 0), mode) & ~ mask) == 0)
+ return force_to_mode (XEXP (x, 0), mode, mask, reg, next_select);
+
+ break;
+
+ case IF_THEN_ELSE:
+ /* We have no way of knowing if the IF_THEN_ELSE can itself be
+ written in a narrower mode. We play it safe and do not do so. */
+
+ SUBST (XEXP (x, 1),
+ gen_lowpart_for_combine (GET_MODE (x),
+ force_to_mode (XEXP (x, 1), mode,
+ mask, reg, next_select)));
+ SUBST (XEXP (x, 2),
+ gen_lowpart_for_combine (GET_MODE (x),
+ force_to_mode (XEXP (x, 2), mode,
+ mask, reg,next_select)));
+ break;
+ }
+
+ /* Ensure we return a value of the proper mode. */
+ return gen_lowpart_for_combine (mode, x);
+}
+
+/* Return nonzero if X is an expression that has one of two values depending on
+ whether some other value is zero or nonzero. In that case, we return the
+ value that is being tested, *PTRUE is set to the value if the rtx being
+ returned has a nonzero value, and *PFALSE is set to the other alternative.
+
+ If we return zero, we set *PTRUE and *PFALSE to X. */
+
+static rtx
+if_then_else_cond (x, ptrue, pfalse)
+ rtx x;
+ rtx *ptrue, *pfalse;
+{
+ enum machine_mode mode = GET_MODE (x);
+ enum rtx_code code = GET_CODE (x);
+ int size = GET_MODE_BITSIZE (mode);
+ rtx cond0, cond1, true0, true1, false0, false1;
+ unsigned HOST_WIDE_INT nz;
+
+ /* If this is a unary operation whose operand has one of two values, apply
+ our opcode to compute those values. */
+ if (GET_RTX_CLASS (code) == '1'
+ && (cond0 = if_then_else_cond (XEXP (x, 0), &true0, &false0)) != 0)
+ {
+ *ptrue = gen_unary (code, mode, GET_MODE (XEXP (x, 0)), true0);
+ *pfalse = gen_unary (code, mode, GET_MODE (XEXP (x, 0)), false0);
+ return cond0;
+ }
+
+ /* If this is a COMPARE, do nothing, since the IF_THEN_ELSE we would
+ make can't possibly match and would supress other optimizations. */
+ else if (code == COMPARE)
+ ;
+
+ /* If this is a binary operation, see if either side has only one of two
+ values. If either one does or if both do and they are conditional on
+ the same value, compute the new true and false values. */
+ else if (GET_RTX_CLASS (code) == 'c' || GET_RTX_CLASS (code) == '2'
+ || GET_RTX_CLASS (code) == '<')
+ {
+ cond0 = if_then_else_cond (XEXP (x, 0), &true0, &false0);
+ cond1 = if_then_else_cond (XEXP (x, 1), &true1, &false1);
+
+ if ((cond0 != 0 || cond1 != 0)
+ && ! (cond0 != 0 && cond1 != 0 && ! rtx_equal_p (cond0, cond1)))
+ {
+ *ptrue = gen_binary (code, mode, true0, true1);
+ *pfalse = gen_binary (code, mode, false0, false1);
+ return cond0 ? cond0 : cond1;
+ }
+
+#if STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1
+
+ /* See if we have PLUS, IOR, XOR, MINUS or UMAX, where one of the
+ operands is zero when the other is non-zero, and vice-versa. */
+
+ if ((code == PLUS || code == IOR || code == XOR || code == MINUS
+ || code == UMAX)
+ && GET_CODE (XEXP (x, 0)) == MULT && GET_CODE (XEXP (x, 1)) == MULT)
+ {
+ rtx op0 = XEXP (XEXP (x, 0), 1);
+ rtx op1 = XEXP (XEXP (x, 1), 1);
+
+ cond0 = XEXP (XEXP (x, 0), 0);
+ cond1 = XEXP (XEXP (x, 1), 0);
+
+ if (GET_RTX_CLASS (GET_CODE (cond0)) == '<'
+ && GET_RTX_CLASS (GET_CODE (cond1)) == '<'
+ && reversible_comparison_p (cond1)
+ && ((GET_CODE (cond0) == reverse_condition (GET_CODE (cond1))
+ && rtx_equal_p (XEXP (cond0, 0), XEXP (cond1, 0))
+ && rtx_equal_p (XEXP (cond0, 1), XEXP (cond1, 1)))
+ || ((swap_condition (GET_CODE (cond0))
+ == reverse_condition (GET_CODE (cond1)))
+ && rtx_equal_p (XEXP (cond0, 0), XEXP (cond1, 1))
+ && rtx_equal_p (XEXP (cond0, 1), XEXP (cond1, 0))))
+ && ! side_effects_p (x))
+ {
+ *ptrue = gen_binary (MULT, mode, op0, const_true_rtx);
+ *pfalse = gen_binary (MULT, mode,
+ (code == MINUS
+ ? gen_unary (NEG, mode, mode, op1) : op1),
+ const_true_rtx);
+ return cond0;
+ }
+ }
+
+ /* Similarly for MULT, AND and UMIN, execpt that for these the result
+ is always zero. */
+ if ((code == MULT || code == AND || code == UMIN)
+ && GET_CODE (XEXP (x, 0)) == MULT && GET_CODE (XEXP (x, 1)) == MULT)
+ {
+ cond0 = XEXP (XEXP (x, 0), 0);
+ cond1 = XEXP (XEXP (x, 1), 0);
+
+ if (GET_RTX_CLASS (GET_CODE (cond0)) == '<'
+ && GET_RTX_CLASS (GET_CODE (cond1)) == '<'
+ && reversible_comparison_p (cond1)
+ && ((GET_CODE (cond0) == reverse_condition (GET_CODE (cond1))
+ && rtx_equal_p (XEXP (cond0, 0), XEXP (cond1, 0))
+ && rtx_equal_p (XEXP (cond0, 1), XEXP (cond1, 1)))
+ || ((swap_condition (GET_CODE (cond0))
+ == reverse_condition (GET_CODE (cond1)))
+ && rtx_equal_p (XEXP (cond0, 0), XEXP (cond1, 1))
+ && rtx_equal_p (XEXP (cond0, 1), XEXP (cond1, 0))))
+ && ! side_effects_p (x))
+ {
+ *ptrue = *pfalse = const0_rtx;
+ return cond0;
+ }
+ }
+#endif
+ }
+
+ else if (code == IF_THEN_ELSE)
+ {
+ /* If we have IF_THEN_ELSE already, extract the condition and
+ canonicalize it if it is NE or EQ. */
+ cond0 = XEXP (x, 0);
+ *ptrue = XEXP (x, 1), *pfalse = XEXP (x, 2);
+ if (GET_CODE (cond0) == NE && XEXP (cond0, 1) == const0_rtx)
+ return XEXP (cond0, 0);
+ else if (GET_CODE (cond0) == EQ && XEXP (cond0, 1) == const0_rtx)
+ {
+ *ptrue = XEXP (x, 2), *pfalse = XEXP (x, 1);
+ return XEXP (cond0, 0);
+ }
+ else
+ return cond0;
+ }
+
+ /* If X is a normal SUBREG with both inner and outer modes integral,
+ we can narrow both the true and false values of the inner expression,
+ if there is a condition. */
+ else if (code == SUBREG && GET_MODE_CLASS (mode) == MODE_INT
+ && GET_MODE_CLASS (GET_MODE (SUBREG_REG (x))) == MODE_INT
+ && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
+ && 0 != (cond0 = if_then_else_cond (SUBREG_REG (x),
+ &true0, &false0)))
+ {
+ *ptrue = force_to_mode (true0, mode, GET_MODE_MASK (mode), NULL_RTX, 0);
+ *pfalse
+ = force_to_mode (false0, mode, GET_MODE_MASK (mode), NULL_RTX, 0);
+
+ return cond0;
+ }
+
+ /* If X is a constant, this isn't special and will cause confusions
+ if we treat it as such. Likewise if it is equivalent to a constant. */
+ else if (CONSTANT_P (x)
+ || ((cond0 = get_last_value (x)) != 0 && CONSTANT_P (cond0)))
+ ;
+
+ /* If X is known to be either 0 or -1, those are the true and
+ false values when testing X. */
+ else if (num_sign_bit_copies (x, mode) == size)
+ {
+ *ptrue = constm1_rtx, *pfalse = const0_rtx;
+ return x;
+ }
+
+ /* Likewise for 0 or a single bit. */
+ else if (exact_log2 (nz = nonzero_bits (x, mode)) >= 0)
+ {
+ *ptrue = GEN_INT (nz), *pfalse = const0_rtx;
+ return x;
+ }
+
+ /* Otherwise fail; show no condition with true and false values the same. */
+ *ptrue = *pfalse = x;
+ return 0;
+}
+
+/* Return the value of expression X given the fact that condition COND
+ is known to be true when applied to REG as its first operand and VAL
+ as its second. X is known to not be shared and so can be modified in
+ place.
+
+ We only handle the simplest cases, and specifically those cases that
+ arise with IF_THEN_ELSE expressions. */
+
+static rtx
+known_cond (x, cond, reg, val)
+ rtx x;
+ enum rtx_code cond;
+ rtx reg, val;
+{
+ enum rtx_code code = GET_CODE (x);
+ rtx temp;
+ char *fmt;
+ int i, j;
+
+ if (side_effects_p (x))
+ return x;
+
+ if (cond == EQ && rtx_equal_p (x, reg))
+ return val;
+
+ /* If X is (abs REG) and we know something about REG's relationship
+ with zero, we may be able to simplify this. */
+
+ if (code == ABS && rtx_equal_p (XEXP (x, 0), reg) && val == const0_rtx)
+ switch (cond)
+ {
+ case GE: case GT: case EQ:
+ return XEXP (x, 0);
+ case LT: case LE:
+ return gen_unary (NEG, GET_MODE (XEXP (x, 0)), GET_MODE (XEXP (x, 0)),
+ XEXP (x, 0));
+ }
+
+ /* The only other cases we handle are MIN, MAX, and comparisons if the
+ operands are the same as REG and VAL. */
+
+ else if (GET_RTX_CLASS (code) == '<' || GET_RTX_CLASS (code) == 'c')
+ {
+ if (rtx_equal_p (XEXP (x, 0), val))
+ cond = swap_condition (cond), temp = val, val = reg, reg = temp;
+
+ if (rtx_equal_p (XEXP (x, 0), reg) && rtx_equal_p (XEXP (x, 1), val))
+ {
+ if (GET_RTX_CLASS (code) == '<')
+ return (comparison_dominates_p (cond, code) ? const_true_rtx
+ : (comparison_dominates_p (cond,
+ reverse_condition (code))
+ ? const0_rtx : x));
+
+ else if (code == SMAX || code == SMIN
+ || code == UMIN || code == UMAX)
+ {
+ int unsignedp = (code == UMIN || code == UMAX);
+
+ if (code == SMAX || code == UMAX)
+ cond = reverse_condition (cond);
+
+ switch (cond)
+ {
+ case GE: case GT:
+ return unsignedp ? x : XEXP (x, 1);
+ case LE: case LT:
+ return unsignedp ? x : XEXP (x, 0);
+ case GEU: case GTU:
+ return unsignedp ? XEXP (x, 1) : x;
+ case LEU: case LTU:
+ return unsignedp ? XEXP (x, 0) : x;
+ }
+ }
+ }
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ SUBST (XEXP (x, i), known_cond (XEXP (x, i), cond, reg, val));
+ else if (fmt[i] == 'E')
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ SUBST (XVECEXP (x, i, j), known_cond (XVECEXP (x, i, j),
+ cond, reg, val));
+ }
+
+ return x;
+}
+
+/* See if X, a SET operation, can be rewritten as a bit-field assignment.
+ Return that assignment if so.
+
+ We only handle the most common cases. */
+
+static rtx
+make_field_assignment (x)
+ rtx x;
+{
+ rtx dest = SET_DEST (x);
+ rtx src = SET_SRC (x);
+ rtx assign;
+ HOST_WIDE_INT c1;
+ int pos, len;
+ rtx other;
+ enum machine_mode mode;
+
+ /* If SRC was (and (not (ashift (const_int 1) POS)) DEST), this is
+ a clear of a one-bit field. We will have changed it to
+ (and (rotate (const_int -2) POS) DEST), so check for that. Also check
+ for a SUBREG. */
+
+ if (GET_CODE (src) == AND && GET_CODE (XEXP (src, 0)) == ROTATE
+ && GET_CODE (XEXP (XEXP (src, 0), 0)) == CONST_INT
+ && INTVAL (XEXP (XEXP (src, 0), 0)) == -2
+ && (rtx_equal_p (dest, XEXP (src, 1))
+ || rtx_equal_p (dest, get_last_value (XEXP (src, 1)))
+ || rtx_equal_p (get_last_value (dest), XEXP (src, 1))))
+ {
+ assign = make_extraction (VOIDmode, dest, 0, XEXP (XEXP (src, 0), 1),
+ 1, 1, 1, 0);
+ return gen_rtx (SET, VOIDmode, assign, const0_rtx);
+ }
+
+ else if (GET_CODE (src) == AND && GET_CODE (XEXP (src, 0)) == SUBREG
+ && subreg_lowpart_p (XEXP (src, 0))
+ && (GET_MODE_SIZE (GET_MODE (XEXP (src, 0)))
+ < GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (src, 0)))))
+ && GET_CODE (SUBREG_REG (XEXP (src, 0))) == ROTATE
+ && INTVAL (XEXP (SUBREG_REG (XEXP (src, 0)), 0)) == -2
+ && (rtx_equal_p (dest, XEXP (src, 1))
+ || rtx_equal_p (dest, get_last_value (XEXP (src, 1)))
+ || rtx_equal_p (get_last_value (dest), XEXP (src, 1))))
+ {
+ assign = make_extraction (VOIDmode, dest, 0,
+ XEXP (SUBREG_REG (XEXP (src, 0)), 1),
+ 1, 1, 1, 0);
+ return gen_rtx (SET, VOIDmode, assign, const0_rtx);
+ }
+
+ /* If SRC is (ior (ashift (const_int 1) POS DEST)), this is a set of a
+ one-bit field. */
+ else if (GET_CODE (src) == IOR && GET_CODE (XEXP (src, 0)) == ASHIFT
+ && XEXP (XEXP (src, 0), 0) == const1_rtx
+ && (rtx_equal_p (dest, XEXP (src, 1))
+ || rtx_equal_p (dest, get_last_value (XEXP (src, 1)))
+ || rtx_equal_p (get_last_value (dest), XEXP (src, 1))))
+ {
+ assign = make_extraction (VOIDmode, dest, 0, XEXP (XEXP (src, 0), 1),
+ 1, 1, 1, 0);
+ return gen_rtx (SET, VOIDmode, assign, const1_rtx);
+ }
+
+ /* The other case we handle is assignments into a constant-position
+ field. They look like (ior (and DEST C1) OTHER). If C1 represents
+ a mask that has all one bits except for a group of zero bits and
+ OTHER is known to have zeros where C1 has ones, this is such an
+ assignment. Compute the position and length from C1. Shift OTHER
+ to the appropriate position, force it to the required mode, and
+ make the extraction. Check for the AND in both operands. */
+
+ if (GET_CODE (src) == IOR && GET_CODE (XEXP (src, 0)) == AND
+ && GET_CODE (XEXP (XEXP (src, 0), 1)) == CONST_INT
+ && (rtx_equal_p (XEXP (XEXP (src, 0), 0), dest)
+ || rtx_equal_p (XEXP (XEXP (src, 0), 0), get_last_value (dest))
+ || rtx_equal_p (get_last_value (XEXP (XEXP (src, 0), 1)), dest)))
+ c1 = INTVAL (XEXP (XEXP (src, 0), 1)), other = XEXP (src, 1);
+ else if (GET_CODE (src) == IOR && GET_CODE (XEXP (src, 1)) == AND
+ && GET_CODE (XEXP (XEXP (src, 1), 1)) == CONST_INT
+ && (rtx_equal_p (XEXP (XEXP (src, 1), 0), dest)
+ || rtx_equal_p (XEXP (XEXP (src, 1), 0), get_last_value (dest))
+ || rtx_equal_p (get_last_value (XEXP (XEXP (src, 1), 0)),
+ dest)))
+ c1 = INTVAL (XEXP (XEXP (src, 1), 1)), other = XEXP (src, 0);
+ else
+ return x;
+
+ pos = get_pos_from_mask (c1 ^ GET_MODE_MASK (GET_MODE (dest)), &len);
+ if (pos < 0 || pos + len > GET_MODE_BITSIZE (GET_MODE (dest))
+ || (GET_MODE_BITSIZE (GET_MODE (other)) <= HOST_BITS_PER_WIDE_INT
+ && (c1 & nonzero_bits (other, GET_MODE (other))) != 0))
+ return x;
+
+ assign = make_extraction (VOIDmode, dest, pos, NULL_RTX, len, 1, 1, 0);
+
+ /* The mode to use for the source is the mode of the assignment, or of
+ what is inside a possible STRICT_LOW_PART. */
+ mode = (GET_CODE (assign) == STRICT_LOW_PART
+ ? GET_MODE (XEXP (assign, 0)) : GET_MODE (assign));
+
+ /* Shift OTHER right POS places and make it the source, restricting it
+ to the proper length and mode. */
+
+ src = force_to_mode (simplify_shift_const (NULL_RTX, LSHIFTRT,
+ GET_MODE (src), other, pos),
+ mode,
+ GET_MODE_BITSIZE (mode) >= HOST_BITS_PER_WIDE_INT
+ ? GET_MODE_MASK (mode)
+ : ((HOST_WIDE_INT) 1 << len) - 1,
+ dest, 0);
+
+ return gen_rtx_combine (SET, VOIDmode, assign, src);
+}
+
+/* See if X is of the form (+ (* a c) (* b c)) and convert to (* (+ a b) c)
+ if so. */
+
+static rtx
+apply_distributive_law (x)
+ rtx x;
+{
+ enum rtx_code code = GET_CODE (x);
+ rtx lhs, rhs, other;
+ rtx tem;
+ enum rtx_code inner_code;
+
+ /* Distributivity is not true for floating point.
+ It can change the value. So don't do it.
+ -- rms and moshier@world.std.com. */
+ if (FLOAT_MODE_P (GET_MODE (x)))
+ return x;
+
+ /* The outer operation can only be one of the following: */
+ if (code != IOR && code != AND && code != XOR
+ && code != PLUS && code != MINUS)
+ return x;
+
+ lhs = XEXP (x, 0), rhs = XEXP (x, 1);
+
+ /* If either operand is a primitive we can't do anything, so get out fast. */
+ if (GET_RTX_CLASS (GET_CODE (lhs)) == 'o'
+ || GET_RTX_CLASS (GET_CODE (rhs)) == 'o')
+ return x;
+
+ lhs = expand_compound_operation (lhs);
+ rhs = expand_compound_operation (rhs);
+ inner_code = GET_CODE (lhs);
+ if (inner_code != GET_CODE (rhs))
+ return x;
+
+ /* See if the inner and outer operations distribute. */
+ switch (inner_code)
+ {
+ case LSHIFTRT:
+ case ASHIFTRT:
+ case AND:
+ case IOR:
+ /* These all distribute except over PLUS. */
+ if (code == PLUS || code == MINUS)
+ return x;
+ break;
+
+ case MULT:
+ if (code != PLUS && code != MINUS)
+ return x;
+ break;
+
+ case ASHIFT:
+ /* This is also a multiply, so it distributes over everything. */
+ break;
+
+ case SUBREG:
+ /* Non-paradoxical SUBREGs distributes over all operations, provided
+ the inner modes and word numbers are the same, this is an extraction
+ of a low-order part, we don't convert an fp operation to int or
+ vice versa, and we would not be converting a single-word
+ operation into a multi-word operation. The latter test is not
+ required, but it prevents generating unneeded multi-word operations.
+ Some of the previous tests are redundant given the latter test, but
+ are retained because they are required for correctness.
+
+ We produce the result slightly differently in this case. */
+
+ if (GET_MODE (SUBREG_REG (lhs)) != GET_MODE (SUBREG_REG (rhs))
+ || SUBREG_WORD (lhs) != SUBREG_WORD (rhs)
+ || ! subreg_lowpart_p (lhs)
+ || (GET_MODE_CLASS (GET_MODE (lhs))
+ != GET_MODE_CLASS (GET_MODE (SUBREG_REG (lhs))))
+ || (GET_MODE_SIZE (GET_MODE (lhs))
+ < GET_MODE_SIZE (GET_MODE (SUBREG_REG (lhs))))
+ || GET_MODE_SIZE (GET_MODE (SUBREG_REG (lhs))) > UNITS_PER_WORD)
+ return x;
+
+ tem = gen_binary (code, GET_MODE (SUBREG_REG (lhs)),
+ SUBREG_REG (lhs), SUBREG_REG (rhs));
+ return gen_lowpart_for_combine (GET_MODE (x), tem);
+
+ default:
+ return x;
+ }
+
+ /* Set LHS and RHS to the inner operands (A and B in the example
+ above) and set OTHER to the common operand (C in the example).
+ These is only one way to do this unless the inner operation is
+ commutative. */
+ if (GET_RTX_CLASS (inner_code) == 'c'
+ && rtx_equal_p (XEXP (lhs, 0), XEXP (rhs, 0)))
+ other = XEXP (lhs, 0), lhs = XEXP (lhs, 1), rhs = XEXP (rhs, 1);
+ else if (GET_RTX_CLASS (inner_code) == 'c'
+ && rtx_equal_p (XEXP (lhs, 0), XEXP (rhs, 1)))
+ other = XEXP (lhs, 0), lhs = XEXP (lhs, 1), rhs = XEXP (rhs, 0);
+ else if (GET_RTX_CLASS (inner_code) == 'c'
+ && rtx_equal_p (XEXP (lhs, 1), XEXP (rhs, 0)))
+ other = XEXP (lhs, 1), lhs = XEXP (lhs, 0), rhs = XEXP (rhs, 1);
+ else if (rtx_equal_p (XEXP (lhs, 1), XEXP (rhs, 1)))
+ other = XEXP (lhs, 1), lhs = XEXP (lhs, 0), rhs = XEXP (rhs, 0);
+ else
+ return x;
+
+ /* Form the new inner operation, seeing if it simplifies first. */
+ tem = gen_binary (code, GET_MODE (x), lhs, rhs);
+
+ /* There is one exception to the general way of distributing:
+ (a ^ b) | (a ^ c) -> (~a) & (b ^ c) */
+ if (code == XOR && inner_code == IOR)
+ {
+ inner_code = AND;
+ other = gen_unary (NOT, GET_MODE (x), GET_MODE (x), other);
+ }
+
+ /* We may be able to continuing distributing the result, so call
+ ourselves recursively on the inner operation before forming the
+ outer operation, which we return. */
+ return gen_binary (inner_code, GET_MODE (x),
+ apply_distributive_law (tem), other);
+}
+
+/* We have X, a logical `and' of VAROP with the constant CONSTOP, to be done
+ in MODE.
+
+ Return an equivalent form, if different from X. Otherwise, return X. If
+ X is zero, we are to always construct the equivalent form. */
+
+static rtx
+simplify_and_const_int (x, mode, varop, constop)
+ rtx x;
+ enum machine_mode mode;
+ rtx varop;
+ unsigned HOST_WIDE_INT constop;
+{
+ unsigned HOST_WIDE_INT nonzero;
+ int i;
+
+ /* Simplify VAROP knowing that we will be only looking at some of the
+ bits in it. */
+ varop = force_to_mode (varop, mode, constop, NULL_RTX, 0);
+
+ /* If VAROP is a CLOBBER, we will fail so return it; if it is a
+ CONST_INT, we are done. */
+ if (GET_CODE (varop) == CLOBBER || GET_CODE (varop) == CONST_INT)
+ return varop;
+
+ /* See what bits may be nonzero in VAROP. Unlike the general case of
+ a call to nonzero_bits, here we don't care about bits outside
+ MODE. */
+
+ nonzero = nonzero_bits (varop, mode) & GET_MODE_MASK (mode);
+
+ /* Turn off all bits in the constant that are known to already be zero.
+ Thus, if the AND isn't needed at all, we will have CONSTOP == NONZERO_BITS
+ which is tested below. */
+
+ constop &= nonzero;
+
+ /* If we don't have any bits left, return zero. */
+ if (constop == 0)
+ return const0_rtx;
+
+ /* If VAROP is a NEG of something known to be zero or 1 and CONSTOP is
+ a power of two, we can replace this with a ASHIFT. */
+ if (GET_CODE (varop) == NEG && nonzero_bits (XEXP (varop, 0), mode) == 1
+ && (i = exact_log2 (constop)) >= 0)
+ return simplify_shift_const (NULL_RTX, ASHIFT, mode, XEXP (varop, 0), i);
+
+ /* If VAROP is an IOR or XOR, apply the AND to both branches of the IOR
+ or XOR, then try to apply the distributive law. This may eliminate
+ operations if either branch can be simplified because of the AND.
+ It may also make some cases more complex, but those cases probably
+ won't match a pattern either with or without this. */
+
+ if (GET_CODE (varop) == IOR || GET_CODE (varop) == XOR)
+ return
+ gen_lowpart_for_combine
+ (mode,
+ apply_distributive_law
+ (gen_binary (GET_CODE (varop), GET_MODE (varop),
+ simplify_and_const_int (NULL_RTX, GET_MODE (varop),
+ XEXP (varop, 0), constop),
+ simplify_and_const_int (NULL_RTX, GET_MODE (varop),
+ XEXP (varop, 1), constop))));
+
+ /* Get VAROP in MODE. Try to get a SUBREG if not. Don't make a new SUBREG
+ if we already had one (just check for the simplest cases). */
+ if (x && GET_CODE (XEXP (x, 0)) == SUBREG
+ && GET_MODE (XEXP (x, 0)) == mode
+ && SUBREG_REG (XEXP (x, 0)) == varop)
+ varop = XEXP (x, 0);
+ else
+ varop = gen_lowpart_for_combine (mode, varop);
+
+ /* If we can't make the SUBREG, try to return what we were given. */
+ if (GET_CODE (varop) == CLOBBER)
+ return x ? x : varop;
+
+ /* If we are only masking insignificant bits, return VAROP. */
+ if (constop == nonzero)
+ x = varop;
+
+ /* Otherwise, return an AND. See how much, if any, of X we can use. */
+ else if (x == 0 || GET_CODE (x) != AND || GET_MODE (x) != mode)
+ x = gen_binary (AND, mode, varop, GEN_INT (constop));
+
+ else
+ {
+ if (GET_CODE (XEXP (x, 1)) != CONST_INT
+ || INTVAL (XEXP (x, 1)) != constop)
+ SUBST (XEXP (x, 1), GEN_INT (constop));
+
+ SUBST (XEXP (x, 0), varop);
+ }
+
+ return x;
+}
+
+/* Given an expression, X, compute which bits in X can be non-zero.
+ We don't care about bits outside of those defined in MODE.
+
+ For most X this is simply GET_MODE_MASK (GET_MODE (MODE)), but if X is
+ a shift, AND, or zero_extract, we can do better. */
+
+static unsigned HOST_WIDE_INT
+nonzero_bits (x, mode)
+ rtx x;
+ enum machine_mode mode;
+{
+ unsigned HOST_WIDE_INT nonzero = GET_MODE_MASK (mode);
+ unsigned HOST_WIDE_INT inner_nz;
+ enum rtx_code code;
+ int mode_width = GET_MODE_BITSIZE (mode);
+ rtx tem;
+
+ /* For floating-point values, assume all bits are needed. */
+ if (FLOAT_MODE_P (GET_MODE (x)) || FLOAT_MODE_P (mode))
+ return nonzero;
+
+ /* If X is wider than MODE, use its mode instead. */
+ if (GET_MODE_BITSIZE (GET_MODE (x)) > mode_width)
+ {
+ mode = GET_MODE (x);
+ nonzero = GET_MODE_MASK (mode);
+ mode_width = GET_MODE_BITSIZE (mode);
+ }
+
+ if (mode_width > HOST_BITS_PER_WIDE_INT)
+ /* Our only callers in this case look for single bit values. So
+ just return the mode mask. Those tests will then be false. */
+ return nonzero;
+
+#ifndef WORD_REGISTER_OPERATIONS
+ /* If MODE is wider than X, but both are a single word for both the host
+ and target machines, we can compute this from which bits of the
+ object might be nonzero in its own mode, taking into account the fact
+ that on many CISC machines, accessing an object in a wider mode
+ causes the high-order bits to become undefined. So they are
+ not known to be zero. */
+
+ if (GET_MODE (x) != VOIDmode && GET_MODE (x) != mode
+ && GET_MODE_BITSIZE (GET_MODE (x)) <= BITS_PER_WORD
+ && GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT
+ && GET_MODE_BITSIZE (mode) > GET_MODE_BITSIZE (GET_MODE (x)))
+ {
+ nonzero &= nonzero_bits (x, GET_MODE (x));
+ nonzero |= GET_MODE_MASK (mode) & ~ GET_MODE_MASK (GET_MODE (x));
+ return nonzero;
+ }
+#endif
+
+ code = GET_CODE (x);
+ switch (code)
+ {
+ case REG:
+#ifdef STACK_BOUNDARY
+ /* If this is the stack pointer, we may know something about its
+ alignment. If PUSH_ROUNDING is defined, it is possible for the
+ stack to be momentarily aligned only to that amount, so we pick
+ the least alignment. */
+
+ if (x == stack_pointer_rtx)
+ {
+ int sp_alignment = STACK_BOUNDARY / BITS_PER_UNIT;
+
+#ifdef PUSH_ROUNDING
+ sp_alignment = MIN (PUSH_ROUNDING (1), sp_alignment);
+#endif
+
+ return nonzero & ~ (sp_alignment - 1);
+ }
+#endif
+
+ /* If X is a register whose nonzero bits value is current, use it.
+ Otherwise, if X is a register whose value we can find, use that
+ value. Otherwise, use the previously-computed global nonzero bits
+ for this register. */
+
+ if (reg_last_set_value[REGNO (x)] != 0
+ && reg_last_set_mode[REGNO (x)] == mode
+ && (reg_n_sets[REGNO (x)] == 1
+ || reg_last_set_label[REGNO (x)] == label_tick)
+ && INSN_CUID (reg_last_set[REGNO (x)]) < subst_low_cuid)
+ return reg_last_set_nonzero_bits[REGNO (x)];
+
+ tem = get_last_value (x);
+
+ if (tem)
+ {
+#ifdef SHORT_IMMEDIATES_SIGN_EXTEND
+ /* If X is narrower than MODE and TEM is a non-negative
+ constant that would appear negative in the mode of X,
+ sign-extend it for use in reg_nonzero_bits because some
+ machines (maybe most) will actually do the sign-extension
+ and this is the conservative approach.
+
+ ??? For 2.5, try to tighten up the MD files in this regard
+ instead of this kludge. */
+
+ if (GET_MODE_BITSIZE (GET_MODE (x)) < mode_width
+ && GET_CODE (tem) == CONST_INT
+ && INTVAL (tem) > 0
+ && 0 != (INTVAL (tem)
+ & ((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (GET_MODE (x)) - 1))))
+ tem = GEN_INT (INTVAL (tem)
+ | ((HOST_WIDE_INT) (-1)
+ << GET_MODE_BITSIZE (GET_MODE (x))));
+#endif
+ return nonzero_bits (tem, mode);
+ }
+ else if (nonzero_sign_valid && reg_nonzero_bits[REGNO (x)])
+ return reg_nonzero_bits[REGNO (x)] & nonzero;
+ else
+ return nonzero;
+
+ case CONST_INT:
+#ifdef SHORT_IMMEDIATES_SIGN_EXTEND
+ /* If X is negative in MODE, sign-extend the value. */
+ if (INTVAL (x) > 0 && mode_width < BITS_PER_WORD
+ && 0 != (INTVAL (x) & ((HOST_WIDE_INT) 1 << (mode_width - 1))))
+ return (INTVAL (x) | ((HOST_WIDE_INT) (-1) << mode_width));
+#endif
+
+ return INTVAL (x);
+
+ case MEM:
+#ifdef LOAD_EXTEND_OP
+ /* In many, if not most, RISC machines, reading a byte from memory
+ zeros the rest of the register. Noticing that fact saves a lot
+ of extra zero-extends. */
+ if (LOAD_EXTEND_OP (GET_MODE (x)) == ZERO_EXTEND)
+ nonzero &= GET_MODE_MASK (GET_MODE (x));
+#endif
+ break;
+
+ case EQ: case NE:
+ case GT: case GTU:
+ case LT: case LTU:
+ case GE: case GEU:
+ case LE: case LEU:
+
+ /* If this produces an integer result, we know which bits are set.
+ Code here used to clear bits outside the mode of X, but that is
+ now done above. */
+
+ if (GET_MODE_CLASS (mode) == MODE_INT
+ && mode_width <= HOST_BITS_PER_WIDE_INT)
+ nonzero = STORE_FLAG_VALUE;
+ break;
+
+ case NEG:
+ if (num_sign_bit_copies (XEXP (x, 0), GET_MODE (x))
+ == GET_MODE_BITSIZE (GET_MODE (x)))
+ nonzero = 1;
+
+ if (GET_MODE_SIZE (GET_MODE (x)) < mode_width)
+ nonzero |= (GET_MODE_MASK (mode) & ~ GET_MODE_MASK (GET_MODE (x)));
+ break;
+
+ case ABS:
+ if (num_sign_bit_copies (XEXP (x, 0), GET_MODE (x))
+ == GET_MODE_BITSIZE (GET_MODE (x)))
+ nonzero = 1;
+ break;
+
+ case TRUNCATE:
+ nonzero &= (nonzero_bits (XEXP (x, 0), mode) & GET_MODE_MASK (mode));
+ break;
+
+ case ZERO_EXTEND:
+ nonzero &= nonzero_bits (XEXP (x, 0), mode);
+ if (GET_MODE (XEXP (x, 0)) != VOIDmode)
+ nonzero &= GET_MODE_MASK (GET_MODE (XEXP (x, 0)));
+ break;
+
+ case SIGN_EXTEND:
+ /* If the sign bit is known clear, this is the same as ZERO_EXTEND.
+ Otherwise, show all the bits in the outer mode but not the inner
+ may be non-zero. */
+ inner_nz = nonzero_bits (XEXP (x, 0), mode);
+ if (GET_MODE (XEXP (x, 0)) != VOIDmode)
+ {
+ inner_nz &= GET_MODE_MASK (GET_MODE (XEXP (x, 0)));
+ if (inner_nz &
+ (((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - 1))))
+ inner_nz |= (GET_MODE_MASK (mode)
+ & ~ GET_MODE_MASK (GET_MODE (XEXP (x, 0))));
+ }
+
+ nonzero &= inner_nz;
+ break;
+
+ case AND:
+ nonzero &= (nonzero_bits (XEXP (x, 0), mode)
+ & nonzero_bits (XEXP (x, 1), mode));
+ break;
+
+ case XOR: case IOR:
+ case UMIN: case UMAX: case SMIN: case SMAX:
+ nonzero &= (nonzero_bits (XEXP (x, 0), mode)
+ | nonzero_bits (XEXP (x, 1), mode));
+ break;
+
+ case PLUS: case MINUS:
+ case MULT:
+ case DIV: case UDIV:
+ case MOD: case UMOD:
+ /* We can apply the rules of arithmetic to compute the number of
+ high- and low-order zero bits of these operations. We start by
+ computing the width (position of the highest-order non-zero bit)
+ and the number of low-order zero bits for each value. */
+ {
+ unsigned HOST_WIDE_INT nz0 = nonzero_bits (XEXP (x, 0), mode);
+ unsigned HOST_WIDE_INT nz1 = nonzero_bits (XEXP (x, 1), mode);
+ int width0 = floor_log2 (nz0) + 1;
+ int width1 = floor_log2 (nz1) + 1;
+ int low0 = floor_log2 (nz0 & -nz0);
+ int low1 = floor_log2 (nz1 & -nz1);
+ int op0_maybe_minusp = (nz0 & ((HOST_WIDE_INT) 1 << (mode_width - 1)));
+ int op1_maybe_minusp = (nz1 & ((HOST_WIDE_INT) 1 << (mode_width - 1)));
+ int result_width = mode_width;
+ int result_low = 0;
+
+ switch (code)
+ {
+ case PLUS:
+ result_width = MAX (width0, width1) + 1;
+ result_low = MIN (low0, low1);
+ break;
+ case MINUS:
+ result_low = MIN (low0, low1);
+ break;
+ case MULT:
+ result_width = width0 + width1;
+ result_low = low0 + low1;
+ break;
+ case DIV:
+ if (! op0_maybe_minusp && ! op1_maybe_minusp)
+ result_width = width0;
+ break;
+ case UDIV:
+ result_width = width0;
+ break;
+ case MOD:
+ if (! op0_maybe_minusp && ! op1_maybe_minusp)
+ result_width = MIN (width0, width1);
+ result_low = MIN (low0, low1);
+ break;
+ case UMOD:
+ result_width = MIN (width0, width1);
+ result_low = MIN (low0, low1);
+ break;
+ }
+
+ if (result_width < mode_width)
+ nonzero &= ((HOST_WIDE_INT) 1 << result_width) - 1;
+
+ if (result_low > 0)
+ nonzero &= ~ (((HOST_WIDE_INT) 1 << result_low) - 1);
+ }
+ break;
+
+ case ZERO_EXTRACT:
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT)
+ nonzero &= ((HOST_WIDE_INT) 1 << INTVAL (XEXP (x, 1))) - 1;
+ break;
+
+ case SUBREG:
+ /* If this is a SUBREG formed for a promoted variable that has
+ been zero-extended, we know that at least the high-order bits
+ are zero, though others might be too. */
+
+ if (SUBREG_PROMOTED_VAR_P (x) && SUBREG_PROMOTED_UNSIGNED_P (x))
+ nonzero = (GET_MODE_MASK (GET_MODE (x))
+ & nonzero_bits (SUBREG_REG (x), GET_MODE (x)));
+
+ /* If the inner mode is a single word for both the host and target
+ machines, we can compute this from which bits of the inner
+ object might be nonzero. */
+ if (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))) <= BITS_PER_WORD
+ && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x)))
+ <= HOST_BITS_PER_WIDE_INT))
+ {
+ nonzero &= nonzero_bits (SUBREG_REG (x), mode);
+
+#ifndef WORD_REGISTER_OPERATIONS
+ /* On many CISC machines, accessing an object in a wider mode
+ causes the high-order bits to become undefined. So they are
+ not known to be zero. */
+ if (GET_MODE_SIZE (GET_MODE (x))
+ > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+ nonzero |= (GET_MODE_MASK (GET_MODE (x))
+ & ~ GET_MODE_MASK (GET_MODE (SUBREG_REG (x))));
+#endif
+ }
+ break;
+
+ case ASHIFTRT:
+ case LSHIFTRT:
+ case ASHIFT:
+ case ROTATE:
+ /* The nonzero bits are in two classes: any bits within MODE
+ that aren't in GET_MODE (x) are always significant. The rest of the
+ nonzero bits are those that are significant in the operand of
+ the shift when shifted the appropriate number of bits. This
+ shows that high-order bits are cleared by the right shift and
+ low-order bits by left shifts. */
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) >= 0
+ && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT)
+ {
+ enum machine_mode inner_mode = GET_MODE (x);
+ int width = GET_MODE_BITSIZE (inner_mode);
+ int count = INTVAL (XEXP (x, 1));
+ unsigned HOST_WIDE_INT mode_mask = GET_MODE_MASK (inner_mode);
+ unsigned HOST_WIDE_INT op_nonzero = nonzero_bits (XEXP (x, 0), mode);
+ unsigned HOST_WIDE_INT inner = op_nonzero & mode_mask;
+ unsigned HOST_WIDE_INT outer = 0;
+
+ if (mode_width > width)
+ outer = (op_nonzero & nonzero & ~ mode_mask);
+
+ if (code == LSHIFTRT)
+ inner >>= count;
+ else if (code == ASHIFTRT)
+ {
+ inner >>= count;
+
+ /* If the sign bit may have been nonzero before the shift, we
+ need to mark all the places it could have been copied to
+ by the shift as possibly nonzero. */
+ if (inner & ((HOST_WIDE_INT) 1 << (width - 1 - count)))
+ inner |= (((HOST_WIDE_INT) 1 << count) - 1) << (width - count);
+ }
+ else if (code == ASHIFT)
+ inner <<= count;
+ else
+ inner = ((inner << (count % width)
+ | (inner >> (width - (count % width)))) & mode_mask);
+
+ nonzero &= (outer | inner);
+ }
+ break;
+
+ case FFS:
+ /* This is at most the number of bits in the mode. */
+ nonzero = ((HOST_WIDE_INT) 1 << (floor_log2 (mode_width) + 1)) - 1;
+ break;
+
+ case IF_THEN_ELSE:
+ nonzero &= (nonzero_bits (XEXP (x, 1), mode)
+ | nonzero_bits (XEXP (x, 2), mode));
+ break;
+ }
+
+ return nonzero;
+}
+
+/* Return the number of bits at the high-order end of X that are known to
+ be equal to the sign bit. X will be used in mode MODE; if MODE is
+ VOIDmode, X will be used in its own mode. The returned value will always
+ be between 1 and the number of bits in MODE. */
+
+static int
+num_sign_bit_copies (x, mode)
+ rtx x;
+ enum machine_mode mode;
+{
+ enum rtx_code code = GET_CODE (x);
+ int bitwidth;
+ int num0, num1, result;
+ unsigned HOST_WIDE_INT nonzero;
+ rtx tem;
+
+ /* If we weren't given a mode, use the mode of X. If the mode is still
+ VOIDmode, we don't know anything. Likewise if one of the modes is
+ floating-point. */
+
+ if (mode == VOIDmode)
+ mode = GET_MODE (x);
+
+ if (mode == VOIDmode || FLOAT_MODE_P (mode) || FLOAT_MODE_P (GET_MODE (x)))
+ return 1;
+
+ bitwidth = GET_MODE_BITSIZE (mode);
+
+ /* For a smaller object, just ignore the high bits. */
+ if (bitwidth < GET_MODE_BITSIZE (GET_MODE (x)))
+ return MAX (1, (num_sign_bit_copies (x, GET_MODE (x))
+ - (GET_MODE_BITSIZE (GET_MODE (x)) - bitwidth)));
+
+#ifndef WORD_REGISTER_OPERATIONS
+ /* If this machine does not do all register operations on the entire
+ register and MODE is wider than the mode of X, we can say nothing
+ at all about the high-order bits. */
+ if (GET_MODE (x) != VOIDmode && bitwidth > GET_MODE_BITSIZE (GET_MODE (x)))
+ return 1;
+#endif
+
+ switch (code)
+ {
+ case REG:
+
+ if (reg_last_set_value[REGNO (x)] != 0
+ && reg_last_set_mode[REGNO (x)] == mode
+ && (reg_n_sets[REGNO (x)] == 1
+ || reg_last_set_label[REGNO (x)] == label_tick)
+ && INSN_CUID (reg_last_set[REGNO (x)]) < subst_low_cuid)
+ return reg_last_set_sign_bit_copies[REGNO (x)];
+
+ tem = get_last_value (x);
+ if (tem != 0)
+ return num_sign_bit_copies (tem, mode);
+
+ if (nonzero_sign_valid && reg_sign_bit_copies[REGNO (x)] != 0)
+ return reg_sign_bit_copies[REGNO (x)];
+ break;
+
+ case MEM:
+#ifdef LOAD_EXTEND_OP
+ /* Some RISC machines sign-extend all loads of smaller than a word. */
+ if (LOAD_EXTEND_OP (GET_MODE (x)) == SIGN_EXTEND)
+ return MAX (1, bitwidth - GET_MODE_BITSIZE (GET_MODE (x)) + 1);
+#endif
+ break;
+
+ case CONST_INT:
+ /* If the constant is negative, take its 1's complement and remask.
+ Then see how many zero bits we have. */
+ nonzero = INTVAL (x) & GET_MODE_MASK (mode);
+ if (bitwidth <= HOST_BITS_PER_WIDE_INT
+ && (nonzero & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0)
+ nonzero = (~ nonzero) & GET_MODE_MASK (mode);
+
+ return (nonzero == 0 ? bitwidth : bitwidth - floor_log2 (nonzero) - 1);
+
+ case SUBREG:
+ /* If this is a SUBREG for a promoted object that is sign-extended
+ and we are looking at it in a wider mode, we know that at least the
+ high-order bits are known to be sign bit copies. */
+
+ if (SUBREG_PROMOTED_VAR_P (x) && ! SUBREG_PROMOTED_UNSIGNED_P (x))
+ return MAX (bitwidth - GET_MODE_BITSIZE (GET_MODE (x)) + 1,
+ num_sign_bit_copies (SUBREG_REG (x), mode));
+
+ /* For a smaller object, just ignore the high bits. */
+ if (bitwidth <= GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))))
+ {
+ num0 = num_sign_bit_copies (SUBREG_REG (x), VOIDmode);
+ return MAX (1, (num0
+ - (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x)))
+ - bitwidth)));
+ }
+
+#ifdef WORD_REGISTER_OPERATIONS
+ /* For paradoxical SUBREGs on machines where all register operations
+ affect the entire register, just look inside. Note that we are
+ passing MODE to the recursive call, so the number of sign bit copies
+ will remain relative to that mode, not the inner mode. */
+
+ if (GET_MODE_SIZE (GET_MODE (x))
+ > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+ return num_sign_bit_copies (SUBREG_REG (x), mode);
+#endif
+ break;
+
+ case SIGN_EXTRACT:
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+ return MAX (1, bitwidth - INTVAL (XEXP (x, 1)));
+ break;
+
+ case SIGN_EXTEND:
+ return (bitwidth - GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))
+ + num_sign_bit_copies (XEXP (x, 0), VOIDmode));
+
+ case TRUNCATE:
+ /* For a smaller object, just ignore the high bits. */
+ num0 = num_sign_bit_copies (XEXP (x, 0), VOIDmode);
+ return MAX (1, (num0 - (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))
+ - bitwidth)));
+
+ case NOT:
+ return num_sign_bit_copies (XEXP (x, 0), mode);
+
+ case ROTATE: case ROTATERT:
+ /* If we are rotating left by a number of bits less than the number
+ of sign bit copies, we can just subtract that amount from the
+ number. */
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) >= 0 && INTVAL (XEXP (x, 1)) < bitwidth)
+ {
+ num0 = num_sign_bit_copies (XEXP (x, 0), mode);
+ return MAX (1, num0 - (code == ROTATE ? INTVAL (XEXP (x, 1))
+ : bitwidth - INTVAL (XEXP (x, 1))));
+ }
+ break;
+
+ case NEG:
+ /* In general, this subtracts one sign bit copy. But if the value
+ is known to be positive, the number of sign bit copies is the
+ same as that of the input. Finally, if the input has just one bit
+ that might be nonzero, all the bits are copies of the sign bit. */
+ nonzero = nonzero_bits (XEXP (x, 0), mode);
+ if (nonzero == 1)
+ return bitwidth;
+
+ num0 = num_sign_bit_copies (XEXP (x, 0), mode);
+ if (num0 > 1
+ && bitwidth <= HOST_BITS_PER_WIDE_INT
+ && (((HOST_WIDE_INT) 1 << (bitwidth - 1)) & nonzero))
+ num0--;
+
+ return num0;
+
+ case IOR: case AND: case XOR:
+ case SMIN: case SMAX: case UMIN: case UMAX:
+ /* Logical operations will preserve the number of sign-bit copies.
+ MIN and MAX operations always return one of the operands. */
+ num0 = num_sign_bit_copies (XEXP (x, 0), mode);
+ num1 = num_sign_bit_copies (XEXP (x, 1), mode);
+ return MIN (num0, num1);
+
+ case PLUS: case MINUS:
+ /* For addition and subtraction, we can have a 1-bit carry. However,
+ if we are subtracting 1 from a positive number, there will not
+ be such a carry. Furthermore, if the positive number is known to
+ be 0 or 1, we know the result is either -1 or 0. */
+
+ if (code == PLUS && XEXP (x, 1) == constm1_rtx
+ && bitwidth <= HOST_BITS_PER_WIDE_INT)
+ {
+ nonzero = nonzero_bits (XEXP (x, 0), mode);
+ if ((((HOST_WIDE_INT) 1 << (bitwidth - 1)) & nonzero) == 0)
+ return (nonzero == 1 || nonzero == 0 ? bitwidth
+ : bitwidth - floor_log2 (nonzero) - 1);
+ }
+
+ num0 = num_sign_bit_copies (XEXP (x, 0), mode);
+ num1 = num_sign_bit_copies (XEXP (x, 1), mode);
+ return MAX (1, MIN (num0, num1) - 1);
+
+ case MULT:
+ /* The number of bits of the product is the sum of the number of
+ bits of both terms. However, unless one of the terms if known
+ to be positive, we must allow for an additional bit since negating
+ a negative number can remove one sign bit copy. */
+
+ num0 = num_sign_bit_copies (XEXP (x, 0), mode);
+ num1 = num_sign_bit_copies (XEXP (x, 1), mode);
+
+ result = bitwidth - (bitwidth - num0) - (bitwidth - num1);
+ if (result > 0
+ && bitwidth <= HOST_BITS_PER_WIDE_INT
+ && ((nonzero_bits (XEXP (x, 0), mode)
+ & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0)
+ && (nonzero_bits (XEXP (x, 1), mode)
+ & ((HOST_WIDE_INT) 1 << (bitwidth - 1)) != 0))
+ result--;
+
+ return MAX (1, result);
+
+ case UDIV:
+ /* The result must be <= the first operand. */
+ return num_sign_bit_copies (XEXP (x, 0), mode);
+
+ case UMOD:
+ /* The result must be <= the scond operand. */
+ return num_sign_bit_copies (XEXP (x, 1), mode);
+
+ case DIV:
+ /* Similar to unsigned division, except that we have to worry about
+ the case where the divisor is negative, in which case we have
+ to add 1. */
+ result = num_sign_bit_copies (XEXP (x, 0), mode);
+ if (result > 1
+ && bitwidth <= HOST_BITS_PER_WIDE_INT
+ && (nonzero_bits (XEXP (x, 1), mode)
+ & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0)
+ result --;
+
+ return result;
+
+ case MOD:
+ result = num_sign_bit_copies (XEXP (x, 1), mode);
+ if (result > 1
+ && bitwidth <= HOST_BITS_PER_WIDE_INT
+ && (nonzero_bits (XEXP (x, 1), mode)
+ & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0)
+ result --;
+
+ return result;
+
+ case ASHIFTRT:
+ /* Shifts by a constant add to the number of bits equal to the
+ sign bit. */
+ num0 = num_sign_bit_copies (XEXP (x, 0), mode);
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) > 0)
+ num0 = MIN (bitwidth, num0 + INTVAL (XEXP (x, 1)));
+
+ return num0;
+
+ case ASHIFT:
+ /* Left shifts destroy copies. */
+ if (GET_CODE (XEXP (x, 1)) != CONST_INT
+ || INTVAL (XEXP (x, 1)) < 0
+ || INTVAL (XEXP (x, 1)) >= bitwidth)
+ return 1;
+
+ num0 = num_sign_bit_copies (XEXP (x, 0), mode);
+ return MAX (1, num0 - INTVAL (XEXP (x, 1)));
+
+ case IF_THEN_ELSE:
+ num0 = num_sign_bit_copies (XEXP (x, 1), mode);
+ num1 = num_sign_bit_copies (XEXP (x, 2), mode);
+ return MIN (num0, num1);
+
+#if STORE_FLAG_VALUE == -1
+ case EQ: case NE: case GE: case GT: case LE: case LT:
+ case GEU: case GTU: case LEU: case LTU:
+ return bitwidth;
+#endif
+ }
+
+ /* If we haven't been able to figure it out by one of the above rules,
+ see if some of the high-order bits are known to be zero. If so,
+ count those bits and return one less than that amount. If we can't
+ safely compute the mask for this mode, always return BITWIDTH. */
+
+ if (bitwidth > HOST_BITS_PER_WIDE_INT)
+ return 1;
+
+ nonzero = nonzero_bits (x, mode);
+ return (nonzero & ((HOST_WIDE_INT) 1 << (bitwidth - 1))
+ ? 1 : bitwidth - floor_log2 (nonzero) - 1);
+}
+
+/* Return the number of "extended" bits there are in X, when interpreted
+ as a quantity in MODE whose signedness is indicated by UNSIGNEDP. For
+ unsigned quantities, this is the number of high-order zero bits.
+ For signed quantities, this is the number of copies of the sign bit
+ minus 1. In both case, this function returns the number of "spare"
+ bits. For example, if two quantities for which this function returns
+ at least 1 are added, the addition is known not to overflow.
+
+ This function will always return 0 unless called during combine, which
+ implies that it must be called from a define_split. */
+
+int
+extended_count (x, mode, unsignedp)
+ rtx x;
+ enum machine_mode mode;
+ int unsignedp;
+{
+ if (nonzero_sign_valid == 0)
+ return 0;
+
+ return (unsignedp
+ ? (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+ && (GET_MODE_BITSIZE (mode) - 1
+ - floor_log2 (nonzero_bits (x, mode))))
+ : num_sign_bit_copies (x, mode) - 1);
+}
+
+/* This function is called from `simplify_shift_const' to merge two
+ outer operations. Specifically, we have already found that we need
+ to perform operation *POP0 with constant *PCONST0 at the outermost
+ position. We would now like to also perform OP1 with constant CONST1
+ (with *POP0 being done last).
+
+ Return 1 if we can do the operation and update *POP0 and *PCONST0 with
+ the resulting operation. *PCOMP_P is set to 1 if we would need to
+ complement the innermost operand, otherwise it is unchanged.
+
+ MODE is the mode in which the operation will be done. No bits outside
+ the width of this mode matter. It is assumed that the width of this mode
+ is smaller than or equal to HOST_BITS_PER_WIDE_INT.
+
+ If *POP0 or OP1 are NIL, it means no operation is required. Only NEG, PLUS,
+ IOR, XOR, and AND are supported. We may set *POP0 to SET if the proper
+ result is simply *PCONST0.
+
+ If the resulting operation cannot be expressed as one operation, we
+ return 0 and do not change *POP0, *PCONST0, and *PCOMP_P. */
+
+static int
+merge_outer_ops (pop0, pconst0, op1, const1, mode, pcomp_p)
+ enum rtx_code *pop0;
+ HOST_WIDE_INT *pconst0;
+ enum rtx_code op1;
+ HOST_WIDE_INT const1;
+ enum machine_mode mode;
+ int *pcomp_p;
+{
+ enum rtx_code op0 = *pop0;
+ HOST_WIDE_INT const0 = *pconst0;
+
+ const0 &= GET_MODE_MASK (mode);
+ const1 &= GET_MODE_MASK (mode);
+
+ /* If OP0 is an AND, clear unimportant bits in CONST1. */
+ if (op0 == AND)
+ const1 &= const0;
+
+ /* If OP0 or OP1 is NIL, this is easy. Similarly if they are the same or
+ if OP0 is SET. */
+
+ if (op1 == NIL || op0 == SET)
+ return 1;
+
+ else if (op0 == NIL)
+ op0 = op1, const0 = const1;
+
+ else if (op0 == op1)
+ {
+ switch (op0)
+ {
+ case AND:
+ const0 &= const1;
+ break;
+ case IOR:
+ const0 |= const1;
+ break;
+ case XOR:
+ const0 ^= const1;
+ break;
+ case PLUS:
+ const0 += const1;
+ break;
+ case NEG:
+ op0 = NIL;
+ break;
+ }
+ }
+
+ /* Otherwise, if either is a PLUS or NEG, we can't do anything. */
+ else if (op0 == PLUS || op1 == PLUS || op0 == NEG || op1 == NEG)
+ return 0;
+
+ /* If the two constants aren't the same, we can't do anything. The
+ remaining six cases can all be done. */
+ else if (const0 != const1)
+ return 0;
+
+ else
+ switch (op0)
+ {
+ case IOR:
+ if (op1 == AND)
+ /* (a & b) | b == b */
+ op0 = SET;
+ else /* op1 == XOR */
+ /* (a ^ b) | b == a | b */
+ ;
+ break;
+
+ case XOR:
+ if (op1 == AND)
+ /* (a & b) ^ b == (~a) & b */
+ op0 = AND, *pcomp_p = 1;
+ else /* op1 == IOR */
+ /* (a | b) ^ b == a & ~b */
+ op0 = AND, *pconst0 = ~ const0;
+ break;
+
+ case AND:
+ if (op1 == IOR)
+ /* (a | b) & b == b */
+ op0 = SET;
+ else /* op1 == XOR */
+ /* (a ^ b) & b) == (~a) & b */
+ *pcomp_p = 1;
+ break;
+ }
+
+ /* Check for NO-OP cases. */
+ const0 &= GET_MODE_MASK (mode);
+ if (const0 == 0
+ && (op0 == IOR || op0 == XOR || op0 == PLUS))
+ op0 = NIL;
+ else if (const0 == 0 && op0 == AND)
+ op0 = SET;
+ else if (const0 == GET_MODE_MASK (mode) && op0 == AND)
+ op0 = NIL;
+
+ *pop0 = op0;
+ *pconst0 = const0;
+
+ return 1;
+}
+
+/* Simplify a shift of VAROP by COUNT bits. CODE says what kind of shift.
+ The result of the shift is RESULT_MODE. X, if non-zero, is an expression
+ that we started with.
+
+ The shift is normally computed in the widest mode we find in VAROP, as
+ long as it isn't a different number of words than RESULT_MODE. Exceptions
+ are ASHIFTRT and ROTATE, which are always done in their original mode, */
+
+static rtx
+simplify_shift_const (x, code, result_mode, varop, count)
+ rtx x;
+ enum rtx_code code;
+ enum machine_mode result_mode;
+ rtx varop;
+ int count;
+{
+ enum rtx_code orig_code = code;
+ int orig_count = count;
+ enum machine_mode mode = result_mode;
+ enum machine_mode shift_mode, tmode;
+ int mode_words
+ = (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
+ /* We form (outer_op (code varop count) (outer_const)). */
+ enum rtx_code outer_op = NIL;
+ HOST_WIDE_INT outer_const = 0;
+ rtx const_rtx;
+ int complement_p = 0;
+ rtx new;
+
+ /* If we were given an invalid count, don't do anything except exactly
+ what was requested. */
+
+ if (count < 0 || count > GET_MODE_BITSIZE (mode))
+ {
+ if (x)
+ return x;
+
+ return gen_rtx (code, mode, varop, GEN_INT (count));
+ }
+
+ /* Unless one of the branches of the `if' in this loop does a `continue',
+ we will `break' the loop after the `if'. */
+
+ while (count != 0)
+ {
+ /* If we have an operand of (clobber (const_int 0)), just return that
+ value. */
+ if (GET_CODE (varop) == CLOBBER)
+ return varop;
+
+ /* If we discovered we had to complement VAROP, leave. Making a NOT
+ here would cause an infinite loop. */
+ if (complement_p)
+ break;
+
+ /* Convert ROTATETRT to ROTATE. */
+ if (code == ROTATERT)
+ code = ROTATE, count = GET_MODE_BITSIZE (result_mode) - count;
+
+ /* We need to determine what mode we will do the shift in. If the
+ shift is a ASHIFTRT or ROTATE, we must always do it in the mode it
+ was originally done in. Otherwise, we can do it in MODE, the widest
+ mode encountered. */
+ shift_mode = (code == ASHIFTRT || code == ROTATE ? result_mode : mode);
+
+ /* Handle cases where the count is greater than the size of the mode
+ minus 1. For ASHIFT, use the size minus one as the count (this can
+ occur when simplifying (lshiftrt (ashiftrt ..))). For rotates,
+ take the count modulo the size. For other shifts, the result is
+ zero.
+
+ Since these shifts are being produced by the compiler by combining
+ multiple operations, each of which are defined, we know what the
+ result is supposed to be. */
+
+ if (count > GET_MODE_BITSIZE (shift_mode) - 1)
+ {
+ if (code == ASHIFTRT)
+ count = GET_MODE_BITSIZE (shift_mode) - 1;
+ else if (code == ROTATE || code == ROTATERT)
+ count %= GET_MODE_BITSIZE (shift_mode);
+ else
+ {
+ /* We can't simply return zero because there may be an
+ outer op. */
+ varop = const0_rtx;
+ count = 0;
+ break;
+ }
+ }
+
+ /* Negative counts are invalid and should not have been made (a
+ programmer-specified negative count should have been handled
+ above). */
+ else if (count < 0)
+ abort ();
+
+ /* An arithmetic right shift of a quantity known to be -1 or 0
+ is a no-op. */
+ if (code == ASHIFTRT
+ && (num_sign_bit_copies (varop, shift_mode)
+ == GET_MODE_BITSIZE (shift_mode)))
+ {
+ count = 0;
+ break;
+ }
+
+ /* If we are doing an arithmetic right shift and discarding all but
+ the sign bit copies, this is equivalent to doing a shift by the
+ bitsize minus one. Convert it into that shift because it will often
+ allow other simplifications. */
+
+ if (code == ASHIFTRT
+ && (count + num_sign_bit_copies (varop, shift_mode)
+ >= GET_MODE_BITSIZE (shift_mode)))
+ count = GET_MODE_BITSIZE (shift_mode) - 1;
+
+ /* We simplify the tests below and elsewhere by converting
+ ASHIFTRT to LSHIFTRT if we know the sign bit is clear.
+ `make_compound_operation' will convert it to a ASHIFTRT for
+ those machines (such as Vax) that don't have a LSHIFTRT. */
+ if (GET_MODE_BITSIZE (shift_mode) <= HOST_BITS_PER_WIDE_INT
+ && code == ASHIFTRT
+ && ((nonzero_bits (varop, shift_mode)
+ & ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (shift_mode) - 1)))
+ == 0))
+ code = LSHIFTRT;
+
+ switch (GET_CODE (varop))
+ {
+ case SIGN_EXTEND:
+ case ZERO_EXTEND:
+ case SIGN_EXTRACT:
+ case ZERO_EXTRACT:
+ new = expand_compound_operation (varop);
+ if (new != varop)
+ {
+ varop = new;
+ continue;
+ }
+ break;
+
+ case MEM:
+ /* If we have (xshiftrt (mem ...) C) and C is MODE_WIDTH
+ minus the width of a smaller mode, we can do this with a
+ SIGN_EXTEND or ZERO_EXTEND from the narrower memory location. */
+ if ((code == ASHIFTRT || code == LSHIFTRT)
+ && ! mode_dependent_address_p (XEXP (varop, 0))
+ && ! MEM_VOLATILE_P (varop)
+ && (tmode = mode_for_size (GET_MODE_BITSIZE (mode) - count,
+ MODE_INT, 1)) != BLKmode)
+ {
+#if BYTES_BIG_ENDIAN
+ new = gen_rtx (MEM, tmode, XEXP (varop, 0));
+#else
+ new = gen_rtx (MEM, tmode,
+ plus_constant (XEXP (varop, 0),
+ count / BITS_PER_UNIT));
+ RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (varop);
+ MEM_VOLATILE_P (new) = MEM_VOLATILE_P (varop);
+ MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (varop);
+#endif
+ varop = gen_rtx_combine (code == ASHIFTRT ? SIGN_EXTEND
+ : ZERO_EXTEND, mode, new);
+ count = 0;
+ continue;
+ }
+ break;
+
+ case USE:
+ /* Similar to the case above, except that we can only do this if
+ the resulting mode is the same as that of the underlying
+ MEM and adjust the address depending on the *bits* endianness
+ because of the way that bit-field extract insns are defined. */
+ if ((code == ASHIFTRT || code == LSHIFTRT)
+ && (tmode = mode_for_size (GET_MODE_BITSIZE (mode) - count,
+ MODE_INT, 1)) != BLKmode
+ && tmode == GET_MODE (XEXP (varop, 0)))
+ {
+#if BITS_BIG_ENDIAN
+ new = XEXP (varop, 0);
+#else
+ new = copy_rtx (XEXP (varop, 0));
+ SUBST (XEXP (new, 0),
+ plus_constant (XEXP (new, 0),
+ count / BITS_PER_UNIT));
+#endif
+
+ varop = gen_rtx_combine (code == ASHIFTRT ? SIGN_EXTEND
+ : ZERO_EXTEND, mode, new);
+ count = 0;
+ continue;
+ }
+ break;
+
+ case SUBREG:
+ /* If VAROP is a SUBREG, strip it as long as the inner operand has
+ the same number of words as what we've seen so far. Then store
+ the widest mode in MODE. */
+ if (subreg_lowpart_p (varop)
+ && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (varop)))
+ > GET_MODE_SIZE (GET_MODE (varop)))
+ && (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (varop)))
+ + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)
+ == mode_words))
+ {
+ varop = SUBREG_REG (varop);
+ if (GET_MODE_SIZE (GET_MODE (varop)) > GET_MODE_SIZE (mode))
+ mode = GET_MODE (varop);
+ continue;
+ }
+ break;
+
+ case MULT:
+ /* Some machines use MULT instead of ASHIFT because MULT
+ is cheaper. But it is still better on those machines to
+ merge two shifts into one. */
+ if (GET_CODE (XEXP (varop, 1)) == CONST_INT
+ && exact_log2 (INTVAL (XEXP (varop, 1))) >= 0)
+ {
+ varop = gen_binary (ASHIFT, GET_MODE (varop), XEXP (varop, 0),
+ GEN_INT (exact_log2 (INTVAL (XEXP (varop, 1)))));;
+ continue;
+ }
+ break;
+
+ case UDIV:
+ /* Similar, for when divides are cheaper. */
+ if (GET_CODE (XEXP (varop, 1)) == CONST_INT
+ && exact_log2 (INTVAL (XEXP (varop, 1))) >= 0)
+ {
+ varop = gen_binary (LSHIFTRT, GET_MODE (varop), XEXP (varop, 0),
+ GEN_INT (exact_log2 (INTVAL (XEXP (varop, 1)))));
+ continue;
+ }
+ break;
+
+ case ASHIFTRT:
+ /* If we are extracting just the sign bit of an arithmetic right
+ shift, that shift is not needed. */
+ if (code == LSHIFTRT && count == GET_MODE_BITSIZE (result_mode) - 1)
+ {
+ varop = XEXP (varop, 0);
+ continue;
+ }
+
+ /* ... fall through ... */
+
+ case LSHIFTRT:
+ case ASHIFT:
+ case ROTATE:
+ /* Here we have two nested shifts. The result is usually the
+ AND of a new shift with a mask. We compute the result below. */
+ if (GET_CODE (XEXP (varop, 1)) == CONST_INT
+ && INTVAL (XEXP (varop, 1)) >= 0
+ && INTVAL (XEXP (varop, 1)) < GET_MODE_BITSIZE (GET_MODE (varop))
+ && GET_MODE_BITSIZE (result_mode) <= HOST_BITS_PER_WIDE_INT
+ && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
+ {
+ enum rtx_code first_code = GET_CODE (varop);
+ int first_count = INTVAL (XEXP (varop, 1));
+ unsigned HOST_WIDE_INT mask;
+ rtx mask_rtx;
+
+ /* We have one common special case. We can't do any merging if
+ the inner code is an ASHIFTRT of a smaller mode. However, if
+ we have (ashift:M1 (subreg:M1 (ashiftrt:M2 FOO C1) 0) C2)
+ with C2 == GET_MODE_BITSIZE (M1) - GET_MODE_BITSIZE (M2),
+ we can convert it to
+ (ashiftrt:M1 (ashift:M1 (and:M1 (subreg:M1 FOO 0 C2) C3) C1).
+ This simplifies certain SIGN_EXTEND operations. */
+ if (code == ASHIFT && first_code == ASHIFTRT
+ && (GET_MODE_BITSIZE (result_mode)
+ - GET_MODE_BITSIZE (GET_MODE (varop))) == count)
+ {
+ /* C3 has the low-order C1 bits zero. */
+
+ mask = (GET_MODE_MASK (mode)
+ & ~ (((HOST_WIDE_INT) 1 << first_count) - 1));
+
+ varop = simplify_and_const_int (NULL_RTX, result_mode,
+ XEXP (varop, 0), mask);
+ varop = simplify_shift_const (NULL_RTX, ASHIFT, result_mode,
+ varop, count);
+ count = first_count;
+ code = ASHIFTRT;
+ continue;
+ }
+
+ /* If this was (ashiftrt (ashift foo C1) C2) and FOO has more
+ than C1 high-order bits equal to the sign bit, we can convert
+ this to either an ASHIFT or a ASHIFTRT depending on the
+ two counts.
+
+ We cannot do this if VAROP's mode is not SHIFT_MODE. */
+
+ if (code == ASHIFTRT && first_code == ASHIFT
+ && GET_MODE (varop) == shift_mode
+ && (num_sign_bit_copies (XEXP (varop, 0), shift_mode)
+ > first_count))
+ {
+ count -= first_count;
+ if (count < 0)
+ count = - count, code = ASHIFT;
+ varop = XEXP (varop, 0);
+ continue;
+ }
+
+ /* There are some cases we can't do. If CODE is ASHIFTRT,
+ we can only do this if FIRST_CODE is also ASHIFTRT.
+
+ We can't do the case when CODE is ROTATE and FIRST_CODE is
+ ASHIFTRT.
+
+ If the mode of this shift is not the mode of the outer shift,
+ we can't do this if either shift is ASHIFTRT or ROTATE.
+
+ Finally, we can't do any of these if the mode is too wide
+ unless the codes are the same.
+
+ Handle the case where the shift codes are the same
+ first. */
+
+ if (code == first_code)
+ {
+ if (GET_MODE (varop) != result_mode
+ && (code == ASHIFTRT || code == ROTATE))
+ break;
+
+ count += first_count;
+ varop = XEXP (varop, 0);
+ continue;
+ }
+
+ if (code == ASHIFTRT
+ || (code == ROTATE && first_code == ASHIFTRT)
+ || GET_MODE_BITSIZE (mode) > HOST_BITS_PER_WIDE_INT
+ || (GET_MODE (varop) != result_mode
+ && (first_code == ASHIFTRT || first_code == ROTATE
+ || code == ROTATE)))
+ break;
+
+ /* To compute the mask to apply after the shift, shift the
+ nonzero bits of the inner shift the same way the
+ outer shift will. */
+
+ mask_rtx = GEN_INT (nonzero_bits (varop, GET_MODE (varop)));
+
+ mask_rtx
+ = simplify_binary_operation (code, result_mode, mask_rtx,
+ GEN_INT (count));
+
+ /* Give up if we can't compute an outer operation to use. */
+ if (mask_rtx == 0
+ || GET_CODE (mask_rtx) != CONST_INT
+ || ! merge_outer_ops (&outer_op, &outer_const, AND,
+ INTVAL (mask_rtx),
+ result_mode, &complement_p))
+ break;
+
+ /* If the shifts are in the same direction, we add the
+ counts. Otherwise, we subtract them. */
+ if ((code == ASHIFTRT || code == LSHIFTRT)
+ == (first_code == ASHIFTRT || first_code == LSHIFTRT))
+ count += first_count;
+ else
+ count -= first_count;
+
+ /* If COUNT is positive, the new shift is usually CODE,
+ except for the two exceptions below, in which case it is
+ FIRST_CODE. If the count is negative, FIRST_CODE should
+ always be used */
+ if (count > 0
+ && ((first_code == ROTATE && code == ASHIFT)
+ || (first_code == ASHIFTRT && code == LSHIFTRT)))
+ code = first_code;
+ else if (count < 0)
+ code = first_code, count = - count;
+
+ varop = XEXP (varop, 0);
+ continue;
+ }
+
+ /* If we have (A << B << C) for any shift, we can convert this to
+ (A << C << B). This wins if A is a constant. Only try this if
+ B is not a constant. */
+
+ else if (GET_CODE (varop) == code
+ && GET_CODE (XEXP (varop, 1)) != CONST_INT
+ && 0 != (new
+ = simplify_binary_operation (code, mode,
+ XEXP (varop, 0),
+ GEN_INT (count))))
+ {
+ varop = gen_rtx_combine (code, mode, new, XEXP (varop, 1));
+ count = 0;
+ continue;
+ }
+ break;
+
+ case NOT:
+ /* Make this fit the case below. */
+ varop = gen_rtx_combine (XOR, mode, XEXP (varop, 0),
+ GEN_INT (GET_MODE_MASK (mode)));
+ continue;
+
+ case IOR:
+ case AND:
+ case XOR:
+ /* If we have (xshiftrt (ior (plus X (const_int -1)) X) C)
+ with C the size of VAROP - 1 and the shift is logical if
+ STORE_FLAG_VALUE is 1 and arithmetic if STORE_FLAG_VALUE is -1,
+ we have an (le X 0) operation. If we have an arithmetic shift
+ and STORE_FLAG_VALUE is 1 or we have a logical shift with
+ STORE_FLAG_VALUE of -1, we have a (neg (le X 0)) operation. */
+
+ if (GET_CODE (varop) == IOR && GET_CODE (XEXP (varop, 0)) == PLUS
+ && XEXP (XEXP (varop, 0), 1) == constm1_rtx
+ && (STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1)
+ && (code == LSHIFTRT || code == ASHIFTRT)
+ && count == GET_MODE_BITSIZE (GET_MODE (varop)) - 1
+ && rtx_equal_p (XEXP (XEXP (varop, 0), 0), XEXP (varop, 1)))
+ {
+ count = 0;
+ varop = gen_rtx_combine (LE, GET_MODE (varop), XEXP (varop, 1),
+ const0_rtx);
+
+ if (STORE_FLAG_VALUE == 1 ? code == ASHIFTRT : code == LSHIFTRT)
+ varop = gen_rtx_combine (NEG, GET_MODE (varop), varop);
+
+ continue;
+ }
+
+ /* If we have (shift (logical)), move the logical to the outside
+ to allow it to possibly combine with another logical and the
+ shift to combine with another shift. This also canonicalizes to
+ what a ZERO_EXTRACT looks like. Also, some machines have
+ (and (shift)) insns. */
+
+ if (GET_CODE (XEXP (varop, 1)) == CONST_INT
+ && (new = simplify_binary_operation (code, result_mode,
+ XEXP (varop, 1),
+ GEN_INT (count))) != 0
+ && GET_CODE(new) == CONST_INT
+ && merge_outer_ops (&outer_op, &outer_const, GET_CODE (varop),
+ INTVAL (new), result_mode, &complement_p))
+ {
+ varop = XEXP (varop, 0);
+ continue;
+ }
+
+ /* If we can't do that, try to simplify the shift in each arm of the
+ logical expression, make a new logical expression, and apply
+ the inverse distributive law. */
+ {
+ rtx lhs = simplify_shift_const (NULL_RTX, code, shift_mode,
+ XEXP (varop, 0), count);
+ rtx rhs = simplify_shift_const (NULL_RTX, code, shift_mode,
+ XEXP (varop, 1), count);
+
+ varop = gen_binary (GET_CODE (varop), shift_mode, lhs, rhs);
+ varop = apply_distributive_law (varop);
+
+ count = 0;
+ }
+ break;
+
+ case EQ:
+ /* convert (lshiftrt (eq FOO 0) C) to (xor FOO 1) if STORE_FLAG_VALUE
+ says that the sign bit can be tested, FOO has mode MODE, C is
+ GET_MODE_BITSIZE (MODE) - 1, and FOO has only its low-order bit
+ that may be nonzero. */
+ if (code == LSHIFTRT
+ && XEXP (varop, 1) == const0_rtx
+ && GET_MODE (XEXP (varop, 0)) == result_mode
+ && count == GET_MODE_BITSIZE (result_mode) - 1
+ && GET_MODE_BITSIZE (result_mode) <= HOST_BITS_PER_WIDE_INT
+ && ((STORE_FLAG_VALUE
+ & ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (result_mode) - 1))))
+ && nonzero_bits (XEXP (varop, 0), result_mode) == 1
+ && merge_outer_ops (&outer_op, &outer_const, XOR,
+ (HOST_WIDE_INT) 1, result_mode,
+ &complement_p))
+ {
+ varop = XEXP (varop, 0);
+ count = 0;
+ continue;
+ }
+ break;
+
+ case NEG:
+ /* (lshiftrt (neg A) C) where A is either 0 or 1 and C is one less
+ than the number of bits in the mode is equivalent to A. */
+ if (code == LSHIFTRT && count == GET_MODE_BITSIZE (result_mode) - 1
+ && nonzero_bits (XEXP (varop, 0), result_mode) == 1)
+ {
+ varop = XEXP (varop, 0);
+ count = 0;
+ continue;
+ }
+
+ /* NEG commutes with ASHIFT since it is multiplication. Move the
+ NEG outside to allow shifts to combine. */
+ if (code == ASHIFT
+ && merge_outer_ops (&outer_op, &outer_const, NEG,
+ (HOST_WIDE_INT) 0, result_mode,
+ &complement_p))
+ {
+ varop = XEXP (varop, 0);
+ continue;
+ }
+ break;
+
+ case PLUS:
+ /* (lshiftrt (plus A -1) C) where A is either 0 or 1 and C
+ is one less than the number of bits in the mode is
+ equivalent to (xor A 1). */
+ if (code == LSHIFTRT && count == GET_MODE_BITSIZE (result_mode) - 1
+ && XEXP (varop, 1) == constm1_rtx
+ && nonzero_bits (XEXP (varop, 0), result_mode) == 1
+ && merge_outer_ops (&outer_op, &outer_const, XOR,
+ (HOST_WIDE_INT) 1, result_mode,
+ &complement_p))
+ {
+ count = 0;
+ varop = XEXP (varop, 0);
+ continue;
+ }
+
+ /* If we have (xshiftrt (plus FOO BAR) C), and the only bits
+ that might be nonzero in BAR are those being shifted out and those
+ bits are known zero in FOO, we can replace the PLUS with FOO.
+ Similarly in the other operand order. This code occurs when
+ we are computing the size of a variable-size array. */
+
+ if ((code == ASHIFTRT || code == LSHIFTRT)
+ && count < HOST_BITS_PER_WIDE_INT
+ && nonzero_bits (XEXP (varop, 1), result_mode) >> count == 0
+ && (nonzero_bits (XEXP (varop, 1), result_mode)
+ & nonzero_bits (XEXP (varop, 0), result_mode)) == 0)
+ {
+ varop = XEXP (varop, 0);
+ continue;
+ }
+ else if ((code == ASHIFTRT || code == LSHIFTRT)
+ && count < HOST_BITS_PER_WIDE_INT
+ && GET_MODE_BITSIZE (result_mode) <= HOST_BITS_PER_WIDE_INT
+ && 0 == (nonzero_bits (XEXP (varop, 0), result_mode)
+ >> count)
+ && 0 == (nonzero_bits (XEXP (varop, 0), result_mode)
+ & nonzero_bits (XEXP (varop, 1),
+ result_mode)))
+ {
+ varop = XEXP (varop, 1);
+ continue;
+ }
+
+ /* (ashift (plus foo C) N) is (plus (ashift foo N) C'). */
+ if (code == ASHIFT
+ && GET_CODE (XEXP (varop, 1)) == CONST_INT
+ && (new = simplify_binary_operation (ASHIFT, result_mode,
+ XEXP (varop, 1),
+ GEN_INT (count))) != 0
+ && GET_CODE(new) == CONST_INT
+ && merge_outer_ops (&outer_op, &outer_const, PLUS,
+ INTVAL (new), result_mode, &complement_p))
+ {
+ varop = XEXP (varop, 0);
+ continue;
+ }
+ break;
+
+ case MINUS:
+ /* If we have (xshiftrt (minus (ashiftrt X C)) X) C)
+ with C the size of VAROP - 1 and the shift is logical if
+ STORE_FLAG_VALUE is 1 and arithmetic if STORE_FLAG_VALUE is -1,
+ we have a (gt X 0) operation. If the shift is arithmetic with
+ STORE_FLAG_VALUE of 1 or logical with STORE_FLAG_VALUE == -1,
+ we have a (neg (gt X 0)) operation. */
+
+ if (GET_CODE (XEXP (varop, 0)) == ASHIFTRT
+ && count == GET_MODE_BITSIZE (GET_MODE (varop)) - 1
+ && (STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1)
+ && (code == LSHIFTRT || code == ASHIFTRT)
+ && GET_CODE (XEXP (XEXP (varop, 0), 1)) == CONST_INT
+ && INTVAL (XEXP (XEXP (varop, 0), 1)) == count
+ && rtx_equal_p (XEXP (XEXP (varop, 0), 0), XEXP (varop, 1)))
+ {
+ count = 0;
+ varop = gen_rtx_combine (GT, GET_MODE (varop), XEXP (varop, 1),
+ const0_rtx);
+
+ if (STORE_FLAG_VALUE == 1 ? code == ASHIFTRT : code == LSHIFTRT)
+ varop = gen_rtx_combine (NEG, GET_MODE (varop), varop);
+
+ continue;
+ }
+ break;
+ }
+
+ break;
+ }
+
+ /* We need to determine what mode to do the shift in. If the shift is
+ a ASHIFTRT or ROTATE, we must always do it in the mode it was originally
+ done in. Otherwise, we can do it in MODE, the widest mode encountered.
+ The code we care about is that of the shift that will actually be done,
+ not the shift that was originally requested. */
+ shift_mode = (code == ASHIFTRT || code == ROTATE ? result_mode : mode);
+
+ /* We have now finished analyzing the shift. The result should be
+ a shift of type CODE with SHIFT_MODE shifting VAROP COUNT places. If
+ OUTER_OP is non-NIL, it is an operation that needs to be applied
+ to the result of the shift. OUTER_CONST is the relevant constant,
+ but we must turn off all bits turned off in the shift.
+
+ If we were passed a value for X, see if we can use any pieces of
+ it. If not, make new rtx. */
+
+ if (x && GET_RTX_CLASS (GET_CODE (x)) == '2'
+ && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) == count)
+ const_rtx = XEXP (x, 1);
+ else
+ const_rtx = GEN_INT (count);
+
+ if (x && GET_CODE (XEXP (x, 0)) == SUBREG
+ && GET_MODE (XEXP (x, 0)) == shift_mode
+ && SUBREG_REG (XEXP (x, 0)) == varop)
+ varop = XEXP (x, 0);
+ else if (GET_MODE (varop) != shift_mode)
+ varop = gen_lowpart_for_combine (shift_mode, varop);
+
+ /* If we can't make the SUBREG, try to return what we were given. */
+ if (GET_CODE (varop) == CLOBBER)
+ return x ? x : varop;
+
+ new = simplify_binary_operation (code, shift_mode, varop, const_rtx);
+ if (new != 0)
+ x = new;
+ else
+ {
+ if (x == 0 || GET_CODE (x) != code || GET_MODE (x) != shift_mode)
+ x = gen_rtx_combine (code, shift_mode, varop, const_rtx);
+
+ SUBST (XEXP (x, 0), varop);
+ SUBST (XEXP (x, 1), const_rtx);
+ }
+
+ /* If we have an outer operation and we just made a shift, it is
+ possible that we could have simplified the shift were it not
+ for the outer operation. So try to do the simplification
+ recursively. */
+
+ if (outer_op != NIL && GET_CODE (x) == code
+ && GET_CODE (XEXP (x, 1)) == CONST_INT)
+ x = simplify_shift_const (x, code, shift_mode, XEXP (x, 0),
+ INTVAL (XEXP (x, 1)));
+
+ /* If we were doing a LSHIFTRT in a wider mode than it was originally,
+ turn off all the bits that the shift would have turned off. */
+ if (orig_code == LSHIFTRT && result_mode != shift_mode)
+ x = simplify_and_const_int (NULL_RTX, shift_mode, x,
+ GET_MODE_MASK (result_mode) >> orig_count);
+
+ /* Do the remainder of the processing in RESULT_MODE. */
+ x = gen_lowpart_for_combine (result_mode, x);
+
+ /* If COMPLEMENT_P is set, we have to complement X before doing the outer
+ operation. */
+ if (complement_p)
+ x = gen_unary (NOT, result_mode, result_mode, x);
+
+ if (outer_op != NIL)
+ {
+ if (GET_MODE_BITSIZE (result_mode) < HOST_BITS_PER_WIDE_INT)
+ outer_const &= GET_MODE_MASK (result_mode);
+
+ if (outer_op == AND)
+ x = simplify_and_const_int (NULL_RTX, result_mode, x, outer_const);
+ else if (outer_op == SET)
+ /* This means that we have determined that the result is
+ equivalent to a constant. This should be rare. */
+ x = GEN_INT (outer_const);
+ else if (GET_RTX_CLASS (outer_op) == '1')
+ x = gen_unary (outer_op, result_mode, result_mode, x);
+ else
+ x = gen_binary (outer_op, result_mode, x, GEN_INT (outer_const));
+ }
+
+ return x;
+}
+
+/* Like recog, but we receive the address of a pointer to a new pattern.
+ We try to match the rtx that the pointer points to.
+ If that fails, we may try to modify or replace the pattern,
+ storing the replacement into the same pointer object.
+
+ Modifications include deletion or addition of CLOBBERs.
+
+ PNOTES is a pointer to a location where any REG_UNUSED notes added for
+ the CLOBBERs are placed.
+
+ The value is the final insn code from the pattern ultimately matched,
+ or -1. */
+
+static int
+recog_for_combine (pnewpat, insn, pnotes)
+ rtx *pnewpat;
+ rtx insn;
+ rtx *pnotes;
+{
+ register rtx pat = *pnewpat;
+ int insn_code_number;
+ int num_clobbers_to_add = 0;
+ int i;
+ rtx notes = 0;
+
+ /* If PAT is a PARALLEL, check to see if it contains the CLOBBER
+ we use to indicate that something didn't match. If we find such a
+ thing, force rejection. */
+ if (GET_CODE (pat) == PARALLEL)
+ for (i = XVECLEN (pat, 0) - 1; i >= 0; i--)
+ if (GET_CODE (XVECEXP (pat, 0, i)) == CLOBBER
+ && XEXP (XVECEXP (pat, 0, i), 0) == const0_rtx)
+ return -1;
+
+ /* Is the result of combination a valid instruction? */
+ insn_code_number = recog (pat, insn, &num_clobbers_to_add);
+
+ /* If it isn't, there is the possibility that we previously had an insn
+ that clobbered some register as a side effect, but the combined
+ insn doesn't need to do that. So try once more without the clobbers
+ unless this represents an ASM insn. */
+
+ if (insn_code_number < 0 && ! check_asm_operands (pat)
+ && GET_CODE (pat) == PARALLEL)
+ {
+ int pos;
+
+ for (pos = 0, i = 0; i < XVECLEN (pat, 0); i++)
+ if (GET_CODE (XVECEXP (pat, 0, i)) != CLOBBER)
+ {
+ if (i != pos)
+ SUBST (XVECEXP (pat, 0, pos), XVECEXP (pat, 0, i));
+ pos++;
+ }
+
+ SUBST_INT (XVECLEN (pat, 0), pos);
+
+ if (pos == 1)
+ pat = XVECEXP (pat, 0, 0);
+
+ insn_code_number = recog (pat, insn, &num_clobbers_to_add);
+ }
+
+ /* If we had any clobbers to add, make a new pattern than contains
+ them. Then check to make sure that all of them are dead. */
+ if (num_clobbers_to_add)
+ {
+ rtx newpat = gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (GET_CODE (pat) == PARALLEL
+ ? XVECLEN (pat, 0) + num_clobbers_to_add
+ : num_clobbers_to_add + 1));
+
+ if (GET_CODE (pat) == PARALLEL)
+ for (i = 0; i < XVECLEN (pat, 0); i++)
+ XVECEXP (newpat, 0, i) = XVECEXP (pat, 0, i);
+ else
+ XVECEXP (newpat, 0, 0) = pat;
+
+ add_clobbers (newpat, insn_code_number);
+
+ for (i = XVECLEN (newpat, 0) - num_clobbers_to_add;
+ i < XVECLEN (newpat, 0); i++)
+ {
+ if (GET_CODE (XEXP (XVECEXP (newpat, 0, i), 0)) == REG
+ && ! reg_dead_at_p (XEXP (XVECEXP (newpat, 0, i), 0), insn))
+ return -1;
+ notes = gen_rtx (EXPR_LIST, REG_UNUSED,
+ XEXP (XVECEXP (newpat, 0, i), 0), notes);
+ }
+ pat = newpat;
+ }
+
+ *pnewpat = pat;
+ *pnotes = notes;
+
+ return insn_code_number;
+}
+
+/* Like gen_lowpart but for use by combine. In combine it is not possible
+ to create any new pseudoregs. However, it is safe to create
+ invalid memory addresses, because combine will try to recognize
+ them and all they will do is make the combine attempt fail.
+
+ If for some reason this cannot do its job, an rtx
+ (clobber (const_int 0)) is returned.
+ An insn containing that will not be recognized. */
+
+#undef gen_lowpart
+
+static rtx
+gen_lowpart_for_combine (mode, x)
+ enum machine_mode mode;
+ register rtx x;
+{
+ rtx result;
+
+ if (GET_MODE (x) == mode)
+ return x;
+
+ /* We can only support MODE being wider than a word if X is a
+ constant integer or has a mode the same size. */
+
+ if (GET_MODE_SIZE (mode) > UNITS_PER_WORD
+ && ! ((GET_MODE (x) == VOIDmode
+ && (GET_CODE (x) == CONST_INT
+ || GET_CODE (x) == CONST_DOUBLE))
+ || GET_MODE_SIZE (GET_MODE (x)) == GET_MODE_SIZE (mode)))
+ return gen_rtx (CLOBBER, GET_MODE (x), const0_rtx);
+
+ /* X might be a paradoxical (subreg (mem)). In that case, gen_lowpart
+ won't know what to do. So we will strip off the SUBREG here and
+ process normally. */
+ if (GET_CODE (x) == SUBREG && GET_CODE (SUBREG_REG (x)) == MEM)
+ {
+ x = SUBREG_REG (x);
+ if (GET_MODE (x) == mode)
+ return x;
+ }
+
+ result = gen_lowpart_common (mode, x);
+ if (result)
+ return result;
+
+ if (GET_CODE (x) == MEM)
+ {
+ register int offset = 0;
+ rtx new;
+
+ /* Refuse to work on a volatile memory ref or one with a mode-dependent
+ address. */
+ if (MEM_VOLATILE_P (x) || mode_dependent_address_p (XEXP (x, 0)))
+ return gen_rtx (CLOBBER, GET_MODE (x), const0_rtx);
+
+ /* If we want to refer to something bigger than the original memref,
+ generate a perverse subreg instead. That will force a reload
+ of the original memref X. */
+ if (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (mode))
+ return gen_rtx (SUBREG, mode, x, 0);
+
+#if WORDS_BIG_ENDIAN
+ offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD)
+ - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD));
+#endif
+#if BYTES_BIG_ENDIAN
+ /* Adjust the address so that the address-after-the-data
+ is unchanged. */
+ offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))
+ - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x))));
+#endif
+ new = gen_rtx (MEM, mode, plus_constant (XEXP (x, 0), offset));
+ RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (x);
+ MEM_VOLATILE_P (new) = MEM_VOLATILE_P (x);
+ MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (x);
+ return new;
+ }
+
+ /* If X is a comparison operator, rewrite it in a new mode. This
+ probably won't match, but may allow further simplifications. */
+ else if (GET_RTX_CLASS (GET_CODE (x)) == '<')
+ return gen_rtx_combine (GET_CODE (x), mode, XEXP (x, 0), XEXP (x, 1));
+
+ /* If we couldn't simplify X any other way, just enclose it in a
+ SUBREG. Normally, this SUBREG won't match, but some patterns may
+ include an explicit SUBREG or we may simplify it further in combine. */
+ else
+ {
+ int word = 0;
+
+ if (WORDS_BIG_ENDIAN && GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD)
+ word = ((GET_MODE_SIZE (GET_MODE (x))
+ - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD))
+ / UNITS_PER_WORD);
+ return gen_rtx (SUBREG, mode, x, word);
+ }
+}
+
+/* Make an rtx expression. This is a subset of gen_rtx and only supports
+ expressions of 1, 2, or 3 operands, each of which are rtx expressions.
+
+ If the identical expression was previously in the insn (in the undobuf),
+ it will be returned. Only if it is not found will a new expression
+ be made. */
+
+/*VARARGS2*/
+static rtx
+gen_rtx_combine VPROTO((enum rtx_code code, enum machine_mode mode, ...))
+{
+#ifndef __STDC__
+ enum rtx_code code;
+ enum machine_mode mode;
+#endif
+ va_list p;
+ int n_args;
+ rtx args[3];
+ int i, j;
+ char *fmt;
+ rtx rt;
+
+ VA_START (p, mode);
+
+#ifndef __STDC__
+ code = va_arg (p, enum rtx_code);
+ mode = va_arg (p, enum machine_mode);
+#endif
+
+ n_args = GET_RTX_LENGTH (code);
+ fmt = GET_RTX_FORMAT (code);
+
+ if (n_args == 0 || n_args > 3)
+ abort ();
+
+ /* Get each arg and verify that it is supposed to be an expression. */
+ for (j = 0; j < n_args; j++)
+ {
+ if (*fmt++ != 'e')
+ abort ();
+
+ args[j] = va_arg (p, rtx);
+ }
+
+ /* See if this is in undobuf. Be sure we don't use objects that came
+ from another insn; this could produce circular rtl structures. */
+
+ for (i = previous_num_undos; i < undobuf.num_undo; i++)
+ if (!undobuf.undo[i].is_int
+ && GET_CODE (undobuf.undo[i].old_contents.r) == code
+ && GET_MODE (undobuf.undo[i].old_contents.r) == mode)
+ {
+ for (j = 0; j < n_args; j++)
+ if (XEXP (undobuf.undo[i].old_contents.r, j) != args[j])
+ break;
+
+ if (j == n_args)
+ return undobuf.undo[i].old_contents.r;
+ }
+
+ /* Otherwise make a new rtx. We know we have 1, 2, or 3 args.
+ Use rtx_alloc instead of gen_rtx because it's faster on RISC. */
+ rt = rtx_alloc (code);
+ PUT_MODE (rt, mode);
+ XEXP (rt, 0) = args[0];
+ if (n_args > 1)
+ {
+ XEXP (rt, 1) = args[1];
+ if (n_args > 2)
+ XEXP (rt, 2) = args[2];
+ }
+ return rt;
+}
+
+/* These routines make binary and unary operations by first seeing if they
+ fold; if not, a new expression is allocated. */
+
+static rtx
+gen_binary (code, mode, op0, op1)
+ enum rtx_code code;
+ enum machine_mode mode;
+ rtx op0, op1;
+{
+ rtx result;
+ rtx tem;
+
+ if (GET_RTX_CLASS (code) == 'c'
+ && (GET_CODE (op0) == CONST_INT
+ || (CONSTANT_P (op0) && GET_CODE (op1) != CONST_INT)))
+ tem = op0, op0 = op1, op1 = tem;
+
+ if (GET_RTX_CLASS (code) == '<')
+ {
+ enum machine_mode op_mode = GET_MODE (op0);
+
+ /* Strip the COMPARE from (REL_OP (compare X Y) 0) to get
+ just (REL_OP X Y). */
+ if (GET_CODE (op0) == COMPARE && op1 == const0_rtx)
+ {
+ op1 = XEXP (op0, 1);
+ op0 = XEXP (op0, 0);
+ op_mode = GET_MODE (op0);
+ }
+
+ if (op_mode == VOIDmode)
+ op_mode = GET_MODE (op1);
+ result = simplify_relational_operation (code, op_mode, op0, op1);
+ }
+ else
+ result = simplify_binary_operation (code, mode, op0, op1);
+
+ if (result)
+ return result;
+
+ /* Put complex operands first and constants second. */
+ if (GET_RTX_CLASS (code) == 'c'
+ && ((CONSTANT_P (op0) && GET_CODE (op1) != CONST_INT)
+ || (GET_RTX_CLASS (GET_CODE (op0)) == 'o'
+ && GET_RTX_CLASS (GET_CODE (op1)) != 'o')
+ || (GET_CODE (op0) == SUBREG
+ && GET_RTX_CLASS (GET_CODE (SUBREG_REG (op0))) == 'o'
+ && GET_RTX_CLASS (GET_CODE (op1)) != 'o')))
+ return gen_rtx_combine (code, mode, op1, op0);
+
+ return gen_rtx_combine (code, mode, op0, op1);
+}
+
+static rtx
+gen_unary (code, mode, op0_mode, op0)
+ enum rtx_code code;
+ enum machine_mode mode, op0_mode;
+ rtx op0;
+{
+ rtx result = simplify_unary_operation (code, mode, op0, op0_mode);
+
+ if (result)
+ return result;
+
+ return gen_rtx_combine (code, mode, op0);
+}
+
+/* Simplify a comparison between *POP0 and *POP1 where CODE is the
+ comparison code that will be tested.
+
+ The result is a possibly different comparison code to use. *POP0 and
+ *POP1 may be updated.
+
+ It is possible that we might detect that a comparison is either always
+ true or always false. However, we do not perform general constant
+ folding in combine, so this knowledge isn't useful. Such tautologies
+ should have been detected earlier. Hence we ignore all such cases. */
+
+static enum rtx_code
+simplify_comparison (code, pop0, pop1)
+ enum rtx_code code;
+ rtx *pop0;
+ rtx *pop1;
+{
+ rtx op0 = *pop0;
+ rtx op1 = *pop1;
+ rtx tem, tem1;
+ int i;
+ enum machine_mode mode, tmode;
+
+ /* Try a few ways of applying the same transformation to both operands. */
+ while (1)
+ {
+#ifndef WORD_REGISTER_OPERATIONS
+ /* The test below this one won't handle SIGN_EXTENDs on these machines,
+ so check specially. */
+ if (code != GTU && code != GEU && code != LTU && code != LEU
+ && GET_CODE (op0) == ASHIFTRT && GET_CODE (op1) == ASHIFTRT
+ && GET_CODE (XEXP (op0, 0)) == ASHIFT
+ && GET_CODE (XEXP (op1, 0)) == ASHIFT
+ && GET_CODE (XEXP (XEXP (op0, 0), 0)) == SUBREG
+ && GET_CODE (XEXP (XEXP (op1, 0), 0)) == SUBREG
+ && (GET_MODE (SUBREG_REG (XEXP (XEXP (op0, 0), 0)))
+ == GET_MODE (SUBREG_REG (XEXP (XEXP (op1, 0), 0))))
+ && GET_CODE (XEXP (op0, 1)) == CONST_INT
+ && GET_CODE (XEXP (op1, 1)) == CONST_INT
+ && GET_CODE (XEXP (XEXP (op0, 0), 1)) == CONST_INT
+ && GET_CODE (XEXP (XEXP (op1, 0), 1)) == CONST_INT
+ && INTVAL (XEXP (op0, 1)) == INTVAL (XEXP (op1, 1))
+ && INTVAL (XEXP (op0, 1)) == INTVAL (XEXP (XEXP (op0, 0), 1))
+ && INTVAL (XEXP (op0, 1)) == INTVAL (XEXP (XEXP (op1, 0), 1))
+ && (INTVAL (XEXP (op0, 1))
+ == (GET_MODE_BITSIZE (GET_MODE (op0))
+ - (GET_MODE_BITSIZE
+ (GET_MODE (SUBREG_REG (XEXP (XEXP (op0, 0), 0))))))))
+ {
+ op0 = SUBREG_REG (XEXP (XEXP (op0, 0), 0));
+ op1 = SUBREG_REG (XEXP (XEXP (op1, 0), 0));
+ }
+#endif
+
+ /* If both operands are the same constant shift, see if we can ignore the
+ shift. We can if the shift is a rotate or if the bits shifted out of
+ this shift are known to be zero for both inputs and if the type of
+ comparison is compatible with the shift. */
+ if (GET_CODE (op0) == GET_CODE (op1)
+ && GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT
+ && ((GET_CODE (op0) == ROTATE && (code == NE || code == EQ))
+ || ((GET_CODE (op0) == LSHIFTRT || GET_CODE (op0) == ASHIFT)
+ && (code != GT && code != LT && code != GE && code != LE))
+ || (GET_CODE (op0) == ASHIFTRT
+ && (code != GTU && code != LTU
+ && code != GEU && code != GEU)))
+ && GET_CODE (XEXP (op0, 1)) == CONST_INT
+ && INTVAL (XEXP (op0, 1)) >= 0
+ && INTVAL (XEXP (op0, 1)) < HOST_BITS_PER_WIDE_INT
+ && XEXP (op0, 1) == XEXP (op1, 1))
+ {
+ enum machine_mode mode = GET_MODE (op0);
+ unsigned HOST_WIDE_INT mask = GET_MODE_MASK (mode);
+ int shift_count = INTVAL (XEXP (op0, 1));
+
+ if (GET_CODE (op0) == LSHIFTRT || GET_CODE (op0) == ASHIFTRT)
+ mask &= (mask >> shift_count) << shift_count;
+ else if (GET_CODE (op0) == ASHIFT)
+ mask = (mask & (mask << shift_count)) >> shift_count;
+
+ if ((nonzero_bits (XEXP (op0, 0), mode) & ~ mask) == 0
+ && (nonzero_bits (XEXP (op1, 0), mode) & ~ mask) == 0)
+ op0 = XEXP (op0, 0), op1 = XEXP (op1, 0);
+ else
+ break;
+ }
+
+ /* If both operands are AND's of a paradoxical SUBREG by constant, the
+ SUBREGs are of the same mode, and, in both cases, the AND would
+ be redundant if the comparison was done in the narrower mode,
+ do the comparison in the narrower mode (e.g., we are AND'ing with 1
+ and the operand's possibly nonzero bits are 0xffffff01; in that case
+ if we only care about QImode, we don't need the AND). This case
+ occurs if the output mode of an scc insn is not SImode and
+ STORE_FLAG_VALUE == 1 (e.g., the 386).
+
+ Similarly, check for a case where the AND's are ZERO_EXTEND
+ operations from some narrower mode even though a SUBREG is not
+ present. */
+
+ else if (GET_CODE (op0) == AND && GET_CODE (op1) == AND
+ && GET_CODE (XEXP (op0, 1)) == CONST_INT
+ && GET_CODE (XEXP (op1, 1)) == CONST_INT)
+ {
+ rtx inner_op0 = XEXP (op0, 0);
+ rtx inner_op1 = XEXP (op1, 0);
+ HOST_WIDE_INT c0 = INTVAL (XEXP (op0, 1));
+ HOST_WIDE_INT c1 = INTVAL (XEXP (op1, 1));
+ int changed = 0;
+
+ if (GET_CODE (inner_op0) == SUBREG && GET_CODE (inner_op1) == SUBREG
+ && (GET_MODE_SIZE (GET_MODE (inner_op0))
+ > GET_MODE_SIZE (GET_MODE (SUBREG_REG (inner_op0))))
+ && (GET_MODE (SUBREG_REG (inner_op0))
+ == GET_MODE (SUBREG_REG (inner_op1)))
+ && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)))
+ <= HOST_BITS_PER_WIDE_INT)
+ && (0 == (~c0) & nonzero_bits (SUBREG_REG (inner_op0),
+ GET_MODE (SUBREG_REG (op0))))
+ && (0 == (~c1) & nonzero_bits (SUBREG_REG (inner_op1),
+ GET_MODE (SUBREG_REG (inner_op1)))))
+ {
+ op0 = SUBREG_REG (inner_op0);
+ op1 = SUBREG_REG (inner_op1);
+
+ /* The resulting comparison is always unsigned since we masked
+ off the original sign bit. */
+ code = unsigned_condition (code);
+
+ changed = 1;
+ }
+
+ else if (c0 == c1)
+ for (tmode = GET_CLASS_NARROWEST_MODE
+ (GET_MODE_CLASS (GET_MODE (op0)));
+ tmode != GET_MODE (op0); tmode = GET_MODE_WIDER_MODE (tmode))
+ if (c0 == GET_MODE_MASK (tmode))
+ {
+ op0 = gen_lowpart_for_combine (tmode, inner_op0);
+ op1 = gen_lowpart_for_combine (tmode, inner_op1);
+ changed = 1;
+ break;
+ }
+
+ if (! changed)
+ break;
+ }
+
+ /* If both operands are NOT, we can strip off the outer operation
+ and adjust the comparison code for swapped operands; similarly for
+ NEG, except that this must be an equality comparison. */
+ else if ((GET_CODE (op0) == NOT && GET_CODE (op1) == NOT)
+ || (GET_CODE (op0) == NEG && GET_CODE (op1) == NEG
+ && (code == EQ || code == NE)))
+ op0 = XEXP (op0, 0), op1 = XEXP (op1, 0), code = swap_condition (code);
+
+ else
+ break;
+ }
+
+ /* If the first operand is a constant, swap the operands and adjust the
+ comparison code appropriately. */
+ if (CONSTANT_P (op0))
+ {
+ tem = op0, op0 = op1, op1 = tem;
+ code = swap_condition (code);
+ }
+
+ /* We now enter a loop during which we will try to simplify the comparison.
+ For the most part, we only are concerned with comparisons with zero,
+ but some things may really be comparisons with zero but not start
+ out looking that way. */
+
+ while (GET_CODE (op1) == CONST_INT)
+ {
+ enum machine_mode mode = GET_MODE (op0);
+ int mode_width = GET_MODE_BITSIZE (mode);
+ unsigned HOST_WIDE_INT mask = GET_MODE_MASK (mode);
+ int equality_comparison_p;
+ int sign_bit_comparison_p;
+ int unsigned_comparison_p;
+ HOST_WIDE_INT const_op;
+
+ /* We only want to handle integral modes. This catches VOIDmode,
+ CCmode, and the floating-point modes. An exception is that we
+ can handle VOIDmode if OP0 is a COMPARE or a comparison
+ operation. */
+
+ if (GET_MODE_CLASS (mode) != MODE_INT
+ && ! (mode == VOIDmode
+ && (GET_CODE (op0) == COMPARE
+ || GET_RTX_CLASS (GET_CODE (op0)) == '<')))
+ break;
+
+ /* Get the constant we are comparing against and turn off all bits
+ not on in our mode. */
+ const_op = INTVAL (op1);
+ if (mode_width <= HOST_BITS_PER_WIDE_INT)
+ const_op &= mask;
+
+ /* If we are comparing against a constant power of two and the value
+ being compared can only have that single bit nonzero (e.g., it was
+ `and'ed with that bit), we can replace this with a comparison
+ with zero. */
+ if (const_op
+ && (code == EQ || code == NE || code == GE || code == GEU
+ || code == LT || code == LTU)
+ && mode_width <= HOST_BITS_PER_WIDE_INT
+ && exact_log2 (const_op) >= 0
+ && nonzero_bits (op0, mode) == const_op)
+ {
+ code = (code == EQ || code == GE || code == GEU ? NE : EQ);
+ op1 = const0_rtx, const_op = 0;
+ }
+
+ /* Similarly, if we are comparing a value known to be either -1 or
+ 0 with -1, change it to the opposite comparison against zero. */
+
+ if (const_op == -1
+ && (code == EQ || code == NE || code == GT || code == LE
+ || code == GEU || code == LTU)
+ && num_sign_bit_copies (op0, mode) == mode_width)
+ {
+ code = (code == EQ || code == LE || code == GEU ? NE : EQ);
+ op1 = const0_rtx, const_op = 0;
+ }
+
+ /* Do some canonicalizations based on the comparison code. We prefer
+ comparisons against zero and then prefer equality comparisons.
+ If we can reduce the size of a constant, we will do that too. */
+
+ switch (code)
+ {
+ case LT:
+ /* < C is equivalent to <= (C - 1) */
+ if (const_op > 0)
+ {
+ const_op -= 1;
+ op1 = GEN_INT (const_op);
+ code = LE;
+ /* ... fall through to LE case below. */
+ }
+ else
+ break;
+
+ case LE:
+ /* <= C is equivalent to < (C + 1); we do this for C < 0 */
+ if (const_op < 0)
+ {
+ const_op += 1;
+ op1 = GEN_INT (const_op);
+ code = LT;
+ }
+
+ /* If we are doing a <= 0 comparison on a value known to have
+ a zero sign bit, we can replace this with == 0. */
+ else if (const_op == 0
+ && mode_width <= HOST_BITS_PER_WIDE_INT
+ && (nonzero_bits (op0, mode)
+ & ((HOST_WIDE_INT) 1 << (mode_width - 1))) == 0)
+ code = EQ;
+ break;
+
+ case GE:
+ /* >= C is equivalent to > (C - 1). */
+ if (const_op > 0)
+ {
+ const_op -= 1;
+ op1 = GEN_INT (const_op);
+ code = GT;
+ /* ... fall through to GT below. */
+ }
+ else
+ break;
+
+ case GT:
+ /* > C is equivalent to >= (C + 1); we do this for C < 0*/
+ if (const_op < 0)
+ {
+ const_op += 1;
+ op1 = GEN_INT (const_op);
+ code = GE;
+ }
+
+ /* If we are doing a > 0 comparison on a value known to have
+ a zero sign bit, we can replace this with != 0. */
+ else if (const_op == 0
+ && mode_width <= HOST_BITS_PER_WIDE_INT
+ && (nonzero_bits (op0, mode)
+ & ((HOST_WIDE_INT) 1 << (mode_width - 1))) == 0)
+ code = NE;
+ break;
+
+ case LTU:
+ /* < C is equivalent to <= (C - 1). */
+ if (const_op > 0)
+ {
+ const_op -= 1;
+ op1 = GEN_INT (const_op);
+ code = LEU;
+ /* ... fall through ... */
+ }
+
+ /* (unsigned) < 0x80000000 is equivalent to >= 0. */
+ else if (const_op == (HOST_WIDE_INT) 1 << (mode_width - 1))
+ {
+ const_op = 0, op1 = const0_rtx;
+ code = GE;
+ break;
+ }
+ else
+ break;
+
+ case LEU:
+ /* unsigned <= 0 is equivalent to == 0 */
+ if (const_op == 0)
+ code = EQ;
+
+ /* (unsigned) <= 0x7fffffff is equivalent to >= 0. */
+ else if (const_op == ((HOST_WIDE_INT) 1 << (mode_width - 1)) - 1)
+ {
+ const_op = 0, op1 = const0_rtx;
+ code = GE;
+ }
+ break;
+
+ case GEU:
+ /* >= C is equivalent to < (C - 1). */
+ if (const_op > 1)
+ {
+ const_op -= 1;
+ op1 = GEN_INT (const_op);
+ code = GTU;
+ /* ... fall through ... */
+ }
+
+ /* (unsigned) >= 0x80000000 is equivalent to < 0. */
+ else if (const_op == (HOST_WIDE_INT) 1 << (mode_width - 1))
+ {
+ const_op = 0, op1 = const0_rtx;
+ code = LT;
+ }
+ else
+ break;
+
+ case GTU:
+ /* unsigned > 0 is equivalent to != 0 */
+ if (const_op == 0)
+ code = NE;
+
+ /* (unsigned) > 0x7fffffff is equivalent to < 0. */
+ else if (const_op == ((HOST_WIDE_INT) 1 << (mode_width - 1)) - 1)
+ {
+ const_op = 0, op1 = const0_rtx;
+ code = LT;
+ }
+ break;
+ }
+
+ /* Compute some predicates to simplify code below. */
+
+ equality_comparison_p = (code == EQ || code == NE);
+ sign_bit_comparison_p = ((code == LT || code == GE) && const_op == 0);
+ unsigned_comparison_p = (code == LTU || code == LEU || code == GTU
+ || code == LEU);
+
+ /* If this is a sign bit comparison and we can do arithmetic in
+ MODE, say that we will only be needing the sign bit of OP0. */
+ if (sign_bit_comparison_p
+ && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
+ op0 = force_to_mode (op0, mode,
+ ((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (mode) - 1)),
+ NULL_RTX, 0);
+
+ /* Now try cases based on the opcode of OP0. If none of the cases
+ does a "continue", we exit this loop immediately after the
+ switch. */
+
+ switch (GET_CODE (op0))
+ {
+ case ZERO_EXTRACT:
+ /* If we are extracting a single bit from a variable position in
+ a constant that has only a single bit set and are comparing it
+ with zero, we can convert this into an equality comparison
+ between the position and the location of the single bit. We can't
+ do this if bit endian and we don't have an extzv since we then
+ can't know what mode to use for the endianness adjustment. */
+
+#if ! BITS_BIG_ENDIAN || defined (HAVE_extzv)
+ if (GET_CODE (XEXP (op0, 0)) == CONST_INT
+ && XEXP (op0, 1) == const1_rtx
+ && equality_comparison_p && const_op == 0
+ && (i = exact_log2 (INTVAL (XEXP (op0, 0)))) >= 0)
+ {
+#if BITS_BIG_ENDIAN
+ i = (GET_MODE_BITSIZE
+ (insn_operand_mode[(int) CODE_FOR_extzv][1]) - 1 - i);
+#endif
+
+ op0 = XEXP (op0, 2);
+ op1 = GEN_INT (i);
+ const_op = i;
+
+ /* Result is nonzero iff shift count is equal to I. */
+ code = reverse_condition (code);
+ continue;
+ }
+#endif
+
+ /* ... fall through ... */
+
+ case SIGN_EXTRACT:
+ tem = expand_compound_operation (op0);
+ if (tem != op0)
+ {
+ op0 = tem;
+ continue;
+ }
+ break;
+
+ case NOT:
+ /* If testing for equality, we can take the NOT of the constant. */
+ if (equality_comparison_p
+ && (tem = simplify_unary_operation (NOT, mode, op1, mode)) != 0)
+ {
+ op0 = XEXP (op0, 0);
+ op1 = tem;
+ continue;
+ }
+
+ /* If just looking at the sign bit, reverse the sense of the
+ comparison. */
+ if (sign_bit_comparison_p)
+ {
+ op0 = XEXP (op0, 0);
+ code = (code == GE ? LT : GE);
+ continue;
+ }
+ break;
+
+ case NEG:
+ /* If testing for equality, we can take the NEG of the constant. */
+ if (equality_comparison_p
+ && (tem = simplify_unary_operation (NEG, mode, op1, mode)) != 0)
+ {
+ op0 = XEXP (op0, 0);
+ op1 = tem;
+ continue;
+ }
+
+ /* The remaining cases only apply to comparisons with zero. */
+ if (const_op != 0)
+ break;
+
+ /* When X is ABS or is known positive,
+ (neg X) is < 0 if and only if X != 0. */
+
+ if (sign_bit_comparison_p
+ && (GET_CODE (XEXP (op0, 0)) == ABS
+ || (mode_width <= HOST_BITS_PER_WIDE_INT
+ && (nonzero_bits (XEXP (op0, 0), mode)
+ & ((HOST_WIDE_INT) 1 << (mode_width - 1))) == 0)))
+ {
+ op0 = XEXP (op0, 0);
+ code = (code == LT ? NE : EQ);
+ continue;
+ }
+
+ /* If we have NEG of something whose two high-order bits are the
+ same, we know that "(-a) < 0" is equivalent to "a > 0". */
+ if (num_sign_bit_copies (op0, mode) >= 2)
+ {
+ op0 = XEXP (op0, 0);
+ code = swap_condition (code);
+ continue;
+ }
+ break;
+
+ case ROTATE:
+ /* If we are testing equality and our count is a constant, we
+ can perform the inverse operation on our RHS. */
+ if (equality_comparison_p && GET_CODE (XEXP (op0, 1)) == CONST_INT
+ && (tem = simplify_binary_operation (ROTATERT, mode,
+ op1, XEXP (op0, 1))) != 0)
+ {
+ op0 = XEXP (op0, 0);
+ op1 = tem;
+ continue;
+ }
+
+ /* If we are doing a < 0 or >= 0 comparison, it means we are testing
+ a particular bit. Convert it to an AND of a constant of that
+ bit. This will be converted into a ZERO_EXTRACT. */
+ if (const_op == 0 && sign_bit_comparison_p
+ && GET_CODE (XEXP (op0, 1)) == CONST_INT
+ && mode_width <= HOST_BITS_PER_WIDE_INT)
+ {
+ op0 = simplify_and_const_int (NULL_RTX, mode, XEXP (op0, 0),
+ ((HOST_WIDE_INT) 1
+ << (mode_width - 1
+ - INTVAL (XEXP (op0, 1)))));
+ code = (code == LT ? NE : EQ);
+ continue;
+ }
+
+ /* ... fall through ... */
+
+ case ABS:
+ /* ABS is ignorable inside an equality comparison with zero. */
+ if (const_op == 0 && equality_comparison_p)
+ {
+ op0 = XEXP (op0, 0);
+ continue;
+ }
+ break;
+
+
+ case SIGN_EXTEND:
+ /* Can simplify (compare (zero/sign_extend FOO) CONST)
+ to (compare FOO CONST) if CONST fits in FOO's mode and we
+ are either testing inequality or have an unsigned comparison
+ with ZERO_EXTEND or a signed comparison with SIGN_EXTEND. */
+ if (! unsigned_comparison_p
+ && (GET_MODE_BITSIZE (GET_MODE (XEXP (op0, 0)))
+ <= HOST_BITS_PER_WIDE_INT)
+ && ((unsigned HOST_WIDE_INT) const_op
+ < (((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (GET_MODE (XEXP (op0, 0))) - 1)))))
+ {
+ op0 = XEXP (op0, 0);
+ continue;
+ }
+ break;
+
+ case SUBREG:
+ /* Check for the case where we are comparing A - C1 with C2,
+ both constants are smaller than 1/2 the maxium positive
+ value in MODE, and the comparison is equality or unsigned.
+ In that case, if A is either zero-extended to MODE or has
+ sufficient sign bits so that the high-order bit in MODE
+ is a copy of the sign in the inner mode, we can prove that it is
+ safe to do the operation in the wider mode. This simplifies
+ many range checks. */
+
+ if (mode_width <= HOST_BITS_PER_WIDE_INT
+ && subreg_lowpart_p (op0)
+ && GET_CODE (SUBREG_REG (op0)) == PLUS
+ && GET_CODE (XEXP (SUBREG_REG (op0), 1)) == CONST_INT
+ && INTVAL (XEXP (SUBREG_REG (op0), 1)) < 0
+ && (- INTVAL (XEXP (SUBREG_REG (op0), 1))
+ < GET_MODE_MASK (mode) / 2)
+ && (unsigned HOST_WIDE_INT) const_op < GET_MODE_MASK (mode) / 2
+ && (0 == (nonzero_bits (XEXP (SUBREG_REG (op0), 0),
+ GET_MODE (SUBREG_REG (op0)))
+ & ~ GET_MODE_MASK (mode))
+ || (num_sign_bit_copies (XEXP (SUBREG_REG (op0), 0),
+ GET_MODE (SUBREG_REG (op0)))
+ > (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)))
+ - GET_MODE_BITSIZE (mode)))))
+ {
+ op0 = SUBREG_REG (op0);
+ continue;
+ }
+
+ /* If the inner mode is narrower and we are extracting the low part,
+ we can treat the SUBREG as if it were a ZERO_EXTEND. */
+ if (subreg_lowpart_p (op0)
+ && GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0))) < mode_width)
+ /* Fall through */ ;
+ else
+ break;
+
+ /* ... fall through ... */
+
+ case ZERO_EXTEND:
+ if ((unsigned_comparison_p || equality_comparison_p)
+ && (GET_MODE_BITSIZE (GET_MODE (XEXP (op0, 0)))
+ <= HOST_BITS_PER_WIDE_INT)
+ && ((unsigned HOST_WIDE_INT) const_op
+ < GET_MODE_MASK (GET_MODE (XEXP (op0, 0)))))
+ {
+ op0 = XEXP (op0, 0);
+ continue;
+ }
+ break;
+
+ case PLUS:
+ /* (eq (plus X A) B) -> (eq X (minus B A)). We can only do
+ this for equality comparisons due to pathological cases involving
+ overflows. */
+ if (equality_comparison_p
+ && 0 != (tem = simplify_binary_operation (MINUS, mode,
+ op1, XEXP (op0, 1))))
+ {
+ op0 = XEXP (op0, 0);
+ op1 = tem;
+ continue;
+ }
+
+ /* (plus (abs X) (const_int -1)) is < 0 if and only if X == 0. */
+ if (const_op == 0 && XEXP (op0, 1) == constm1_rtx
+ && GET_CODE (XEXP (op0, 0)) == ABS && sign_bit_comparison_p)
+ {
+ op0 = XEXP (XEXP (op0, 0), 0);
+ code = (code == LT ? EQ : NE);
+ continue;
+ }
+ break;
+
+ case MINUS:
+ /* (eq (minus A B) C) -> (eq A (plus B C)) or
+ (eq B (minus A C)), whichever simplifies. We can only do
+ this for equality comparisons due to pathological cases involving
+ overflows. */
+ if (equality_comparison_p
+ && 0 != (tem = simplify_binary_operation (PLUS, mode,
+ XEXP (op0, 1), op1)))
+ {
+ op0 = XEXP (op0, 0);
+ op1 = tem;
+ continue;
+ }
+
+ if (equality_comparison_p
+ && 0 != (tem = simplify_binary_operation (MINUS, mode,
+ XEXP (op0, 0), op1)))
+ {
+ op0 = XEXP (op0, 1);
+ op1 = tem;
+ continue;
+ }
+
+ /* The sign bit of (minus (ashiftrt X C) X), where C is the number
+ of bits in X minus 1, is one iff X > 0. */
+ if (sign_bit_comparison_p && GET_CODE (XEXP (op0, 0)) == ASHIFTRT
+ && GET_CODE (XEXP (XEXP (op0, 0), 1)) == CONST_INT
+ && INTVAL (XEXP (XEXP (op0, 0), 1)) == mode_width - 1
+ && rtx_equal_p (XEXP (XEXP (op0, 0), 0), XEXP (op0, 1)))
+ {
+ op0 = XEXP (op0, 1);
+ code = (code == GE ? LE : GT);
+ continue;
+ }
+ break;
+
+ case XOR:
+ /* (eq (xor A B) C) -> (eq A (xor B C)). This is a simplification
+ if C is zero or B is a constant. */
+ if (equality_comparison_p
+ && 0 != (tem = simplify_binary_operation (XOR, mode,
+ XEXP (op0, 1), op1)))
+ {
+ op0 = XEXP (op0, 0);
+ op1 = tem;
+ continue;
+ }
+ break;
+
+ case EQ: case NE:
+ case LT: case LTU: case LE: case LEU:
+ case GT: case GTU: case GE: case GEU:
+ /* We can't do anything if OP0 is a condition code value, rather
+ than an actual data value. */
+ if (const_op != 0
+#ifdef HAVE_cc0
+ || XEXP (op0, 0) == cc0_rtx
+#endif
+ || GET_MODE_CLASS (GET_MODE (XEXP (op0, 0))) == MODE_CC)
+ break;
+
+ /* Get the two operands being compared. */
+ if (GET_CODE (XEXP (op0, 0)) == COMPARE)
+ tem = XEXP (XEXP (op0, 0), 0), tem1 = XEXP (XEXP (op0, 0), 1);
+ else
+ tem = XEXP (op0, 0), tem1 = XEXP (op0, 1);
+
+ /* Check for the cases where we simply want the result of the
+ earlier test or the opposite of that result. */
+ if (code == NE
+ || (code == EQ && reversible_comparison_p (op0))
+ || (GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT
+ && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT
+ && (STORE_FLAG_VALUE
+ & (((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (GET_MODE (op0)) - 1))))
+ && (code == LT
+ || (code == GE && reversible_comparison_p (op0)))))
+ {
+ code = (code == LT || code == NE
+ ? GET_CODE (op0) : reverse_condition (GET_CODE (op0)));
+ op0 = tem, op1 = tem1;
+ continue;
+ }
+ break;
+
+ case IOR:
+ /* The sign bit of (ior (plus X (const_int -1)) X) is non-zero
+ iff X <= 0. */
+ if (sign_bit_comparison_p && GET_CODE (XEXP (op0, 0)) == PLUS
+ && XEXP (XEXP (op0, 0), 1) == constm1_rtx
+ && rtx_equal_p (XEXP (XEXP (op0, 0), 0), XEXP (op0, 1)))
+ {
+ op0 = XEXP (op0, 1);
+ code = (code == GE ? GT : LE);
+ continue;
+ }
+ break;
+
+ case AND:
+ /* Convert (and (xshift 1 X) Y) to (and (lshiftrt Y X) 1). This
+ will be converted to a ZERO_EXTRACT later. */
+ if (const_op == 0 && equality_comparison_p
+ && GET_CODE (XEXP (op0, 0)) == ASHIFT
+ && XEXP (XEXP (op0, 0), 0) == const1_rtx)
+ {
+ op0 = simplify_and_const_int
+ (op0, mode, gen_rtx_combine (LSHIFTRT, mode,
+ XEXP (op0, 1),
+ XEXP (XEXP (op0, 0), 1)),
+ (HOST_WIDE_INT) 1);
+ continue;
+ }
+
+ /* If we are comparing (and (lshiftrt X C1) C2) for equality with
+ zero and X is a comparison and C1 and C2 describe only bits set
+ in STORE_FLAG_VALUE, we can compare with X. */
+ if (const_op == 0 && equality_comparison_p
+ && mode_width <= HOST_BITS_PER_WIDE_INT
+ && GET_CODE (XEXP (op0, 1)) == CONST_INT
+ && GET_CODE (XEXP (op0, 0)) == LSHIFTRT
+ && GET_CODE (XEXP (XEXP (op0, 0), 1)) == CONST_INT
+ && INTVAL (XEXP (XEXP (op0, 0), 1)) >= 0
+ && INTVAL (XEXP (XEXP (op0, 0), 1)) < HOST_BITS_PER_WIDE_INT)
+ {
+ mask = ((INTVAL (XEXP (op0, 1)) & GET_MODE_MASK (mode))
+ << INTVAL (XEXP (XEXP (op0, 0), 1)));
+ if ((~ STORE_FLAG_VALUE & mask) == 0
+ && (GET_RTX_CLASS (GET_CODE (XEXP (XEXP (op0, 0), 0))) == '<'
+ || ((tem = get_last_value (XEXP (XEXP (op0, 0), 0))) != 0
+ && GET_RTX_CLASS (GET_CODE (tem)) == '<')))
+ {
+ op0 = XEXP (XEXP (op0, 0), 0);
+ continue;
+ }
+ }
+
+ /* If we are doing an equality comparison of an AND of a bit equal
+ to the sign bit, replace this with a LT or GE comparison of
+ the underlying value. */
+ if (equality_comparison_p
+ && const_op == 0
+ && GET_CODE (XEXP (op0, 1)) == CONST_INT
+ && mode_width <= HOST_BITS_PER_WIDE_INT
+ && ((INTVAL (XEXP (op0, 1)) & GET_MODE_MASK (mode))
+ == (HOST_WIDE_INT) 1 << (mode_width - 1)))
+ {
+ op0 = XEXP (op0, 0);
+ code = (code == EQ ? GE : LT);
+ continue;
+ }
+
+ /* If this AND operation is really a ZERO_EXTEND from a narrower
+ mode, the constant fits within that mode, and this is either an
+ equality or unsigned comparison, try to do this comparison in
+ the narrower mode. */
+ if ((equality_comparison_p || unsigned_comparison_p)
+ && GET_CODE (XEXP (op0, 1)) == CONST_INT
+ && (i = exact_log2 ((INTVAL (XEXP (op0, 1))
+ & GET_MODE_MASK (mode))
+ + 1)) >= 0
+ && const_op >> i == 0
+ && (tmode = mode_for_size (i, MODE_INT, 1)) != BLKmode)
+ {
+ op0 = gen_lowpart_for_combine (tmode, XEXP (op0, 0));
+ continue;
+ }
+ break;
+
+ case ASHIFT:
+ /* If we have (compare (ashift FOO N) (const_int C)) and
+ the high order N bits of FOO (N+1 if an inequality comparison)
+ are known to be zero, we can do this by comparing FOO with C
+ shifted right N bits so long as the low-order N bits of C are
+ zero. */
+ if (GET_CODE (XEXP (op0, 1)) == CONST_INT
+ && INTVAL (XEXP (op0, 1)) >= 0
+ && ((INTVAL (XEXP (op0, 1)) + ! equality_comparison_p)
+ < HOST_BITS_PER_WIDE_INT)
+ && ((const_op
+ & (((HOST_WIDE_INT) 1 << INTVAL (XEXP (op0, 1))) - 1)) == 0)
+ && mode_width <= HOST_BITS_PER_WIDE_INT
+ && (nonzero_bits (XEXP (op0, 0), mode)
+ & ~ (mask >> (INTVAL (XEXP (op0, 1))
+ + ! equality_comparison_p))) == 0)
+ {
+ const_op >>= INTVAL (XEXP (op0, 1));
+ op1 = GEN_INT (const_op);
+ op0 = XEXP (op0, 0);
+ continue;
+ }
+
+ /* If we are doing a sign bit comparison, it means we are testing
+ a particular bit. Convert it to the appropriate AND. */
+ if (sign_bit_comparison_p && GET_CODE (XEXP (op0, 1)) == CONST_INT
+ && mode_width <= HOST_BITS_PER_WIDE_INT)
+ {
+ op0 = simplify_and_const_int (NULL_RTX, mode, XEXP (op0, 0),
+ ((HOST_WIDE_INT) 1
+ << (mode_width - 1
+ - INTVAL (XEXP (op0, 1)))));
+ code = (code == LT ? NE : EQ);
+ continue;
+ }
+
+ /* If this an equality comparison with zero and we are shifting
+ the low bit to the sign bit, we can convert this to an AND of the
+ low-order bit. */
+ if (const_op == 0 && equality_comparison_p
+ && GET_CODE (XEXP (op0, 1)) == CONST_INT
+ && INTVAL (XEXP (op0, 1)) == mode_width - 1)
+ {
+ op0 = simplify_and_const_int (NULL_RTX, mode, XEXP (op0, 0),
+ (HOST_WIDE_INT) 1);
+ continue;
+ }
+ break;
+
+ case ASHIFTRT:
+ /* If this is an equality comparison with zero, we can do this
+ as a logical shift, which might be much simpler. */
+ if (equality_comparison_p && const_op == 0
+ && GET_CODE (XEXP (op0, 1)) == CONST_INT)
+ {
+ op0 = simplify_shift_const (NULL_RTX, LSHIFTRT, mode,
+ XEXP (op0, 0),
+ INTVAL (XEXP (op0, 1)));
+ continue;
+ }
+
+ /* If OP0 is a sign extension and CODE is not an unsigned comparison,
+ do the comparison in a narrower mode. */
+ if (! unsigned_comparison_p
+ && GET_CODE (XEXP (op0, 1)) == CONST_INT
+ && GET_CODE (XEXP (op0, 0)) == ASHIFT
+ && XEXP (op0, 1) == XEXP (XEXP (op0, 0), 1)
+ && (tmode = mode_for_size (mode_width - INTVAL (XEXP (op0, 1)),
+ MODE_INT, 1)) != BLKmode
+ && ((unsigned HOST_WIDE_INT) const_op <= GET_MODE_MASK (tmode)
+ || ((unsigned HOST_WIDE_INT) - const_op
+ <= GET_MODE_MASK (tmode))))
+ {
+ op0 = gen_lowpart_for_combine (tmode, XEXP (XEXP (op0, 0), 0));
+ continue;
+ }
+
+ /* ... fall through ... */
+ case LSHIFTRT:
+ /* If we have (compare (xshiftrt FOO N) (const_int C)) and
+ the low order N bits of FOO are known to be zero, we can do this
+ by comparing FOO with C shifted left N bits so long as no
+ overflow occurs. */
+ if (GET_CODE (XEXP (op0, 1)) == CONST_INT
+ && INTVAL (XEXP (op0, 1)) >= 0
+ && INTVAL (XEXP (op0, 1)) < HOST_BITS_PER_WIDE_INT
+ && mode_width <= HOST_BITS_PER_WIDE_INT
+ && (nonzero_bits (XEXP (op0, 0), mode)
+ & (((HOST_WIDE_INT) 1 << INTVAL (XEXP (op0, 1))) - 1)) == 0
+ && (const_op == 0
+ || (floor_log2 (const_op) + INTVAL (XEXP (op0, 1))
+ < mode_width)))
+ {
+ const_op <<= INTVAL (XEXP (op0, 1));
+ op1 = GEN_INT (const_op);
+ op0 = XEXP (op0, 0);
+ continue;
+ }
+
+ /* If we are using this shift to extract just the sign bit, we
+ can replace this with an LT or GE comparison. */
+ if (const_op == 0
+ && (equality_comparison_p || sign_bit_comparison_p)
+ && GET_CODE (XEXP (op0, 1)) == CONST_INT
+ && INTVAL (XEXP (op0, 1)) == mode_width - 1)
+ {
+ op0 = XEXP (op0, 0);
+ code = (code == NE || code == GT ? LT : GE);
+ continue;
+ }
+ break;
+ }
+
+ break;
+ }
+
+ /* Now make any compound operations involved in this comparison. Then,
+ check for an outmost SUBREG on OP0 that isn't doing anything or is
+ paradoxical. The latter case can only occur when it is known that the
+ "extra" bits will be zero. Therefore, it is safe to remove the SUBREG.
+ We can never remove a SUBREG for a non-equality comparison because the
+ sign bit is in a different place in the underlying object. */
+
+ op0 = make_compound_operation (op0, op1 == const0_rtx ? COMPARE : SET);
+ op1 = make_compound_operation (op1, SET);
+
+ if (GET_CODE (op0) == SUBREG && subreg_lowpart_p (op0)
+ && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT
+ && (code == NE || code == EQ)
+ && ((GET_MODE_SIZE (GET_MODE (op0))
+ > GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0))))))
+ {
+ op0 = SUBREG_REG (op0);
+ op1 = gen_lowpart_for_combine (GET_MODE (op0), op1);
+ }
+
+ else if (GET_CODE (op0) == SUBREG && subreg_lowpart_p (op0)
+ && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT
+ && (code == NE || code == EQ)
+ && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)))
+ <= HOST_BITS_PER_WIDE_INT)
+ && (nonzero_bits (SUBREG_REG (op0), GET_MODE (SUBREG_REG (op0)))
+ & ~ GET_MODE_MASK (GET_MODE (op0))) == 0
+ && (tem = gen_lowpart_for_combine (GET_MODE (SUBREG_REG (op0)),
+ op1),
+ (nonzero_bits (tem, GET_MODE (SUBREG_REG (op0)))
+ & ~ GET_MODE_MASK (GET_MODE (op0))) == 0))
+ op0 = SUBREG_REG (op0), op1 = tem;
+
+ /* We now do the opposite procedure: Some machines don't have compare
+ insns in all modes. If OP0's mode is an integer mode smaller than a
+ word and we can't do a compare in that mode, see if there is a larger
+ mode for which we can do the compare. There are a number of cases in
+ which we can use the wider mode. */
+
+ mode = GET_MODE (op0);
+ if (mode != VOIDmode && GET_MODE_CLASS (mode) == MODE_INT
+ && GET_MODE_SIZE (mode) < UNITS_PER_WORD
+ && cmp_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+ for (tmode = GET_MODE_WIDER_MODE (mode);
+ (tmode != VOIDmode
+ && GET_MODE_BITSIZE (tmode) <= HOST_BITS_PER_WIDE_INT);
+ tmode = GET_MODE_WIDER_MODE (tmode))
+ if (cmp_optab->handlers[(int) tmode].insn_code != CODE_FOR_nothing)
+ {
+ /* If the only nonzero bits in OP0 and OP1 are those in the
+ narrower mode and this is an equality or unsigned comparison,
+ we can use the wider mode. Similarly for sign-extended
+ values, in which case it is true for all comparisons. */
+ if (((code == EQ || code == NE
+ || code == GEU || code == GTU || code == LEU || code == LTU)
+ && (nonzero_bits (op0, tmode) & ~ GET_MODE_MASK (mode)) == 0
+ && (nonzero_bits (op1, tmode) & ~ GET_MODE_MASK (mode)) == 0)
+ || ((num_sign_bit_copies (op0, tmode)
+ > GET_MODE_BITSIZE (tmode) - GET_MODE_BITSIZE (mode))
+ && (num_sign_bit_copies (op1, tmode)
+ > GET_MODE_BITSIZE (tmode) - GET_MODE_BITSIZE (mode))))
+ {
+ op0 = gen_lowpart_for_combine (tmode, op0);
+ op1 = gen_lowpart_for_combine (tmode, op1);
+ break;
+ }
+
+ /* If this is a test for negative, we can make an explicit
+ test of the sign bit. */
+
+ if (op1 == const0_rtx && (code == LT || code == GE)
+ && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
+ {
+ op0 = gen_binary (AND, tmode,
+ gen_lowpart_for_combine (tmode, op0),
+ GEN_INT ((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (mode) - 1)));
+ code = (code == LT) ? NE : EQ;
+ break;
+ }
+ }
+
+#ifdef CANONICALIZE_COMPARISON
+ /* If this machine only supports a subset of valid comparisons, see if we
+ can convert an unsupported one into a supported one. */
+ CANONICALIZE_COMPARISON (code, op0, op1);
+#endif
+
+ *pop0 = op0;
+ *pop1 = op1;
+
+ return code;
+}
+
+/* Return 1 if we know that X, a comparison operation, is not operating
+ on a floating-point value or is EQ or NE, meaning that we can safely
+ reverse it. */
+
+static int
+reversible_comparison_p (x)
+ rtx x;
+{
+ if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+ || flag_fast_math
+ || GET_CODE (x) == NE || GET_CODE (x) == EQ)
+ return 1;
+
+ switch (GET_MODE_CLASS (GET_MODE (XEXP (x, 0))))
+ {
+ case MODE_INT:
+ case MODE_PARTIAL_INT:
+ case MODE_COMPLEX_INT:
+ return 1;
+
+ case MODE_CC:
+ /* If the mode of the condition codes tells us that this is safe,
+ we need look no further. */
+ if (REVERSIBLE_CC_MODE (GET_MODE (XEXP (x, 0))))
+ return 1;
+
+ /* Otherwise try and find where the condition codes were last set and
+ use that. */
+ x = get_last_value (XEXP (x, 0));
+ return (x && GET_CODE (x) == COMPARE
+ && ! FLOAT_MODE_P (GET_MODE (XEXP (x, 0))));
+ }
+
+ return 0;
+}
+
+/* Utility function for following routine. Called when X is part of a value
+ being stored into reg_last_set_value. Sets reg_last_set_table_tick
+ for each register mentioned. Similar to mention_regs in cse.c */
+
+static void
+update_table_tick (x)
+ rtx x;
+{
+ register enum rtx_code code = GET_CODE (x);
+ register char *fmt = GET_RTX_FORMAT (code);
+ register int i;
+
+ if (code == REG)
+ {
+ int regno = REGNO (x);
+ int endregno = regno + (regno < FIRST_PSEUDO_REGISTER
+ ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1);
+
+ for (i = regno; i < endregno; i++)
+ reg_last_set_table_tick[i] = label_tick;
+
+ return;
+ }
+
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ /* Note that we can't have an "E" in values stored; see
+ get_last_value_validate. */
+ if (fmt[i] == 'e')
+ update_table_tick (XEXP (x, i));
+}
+
+/* Record that REG is set to VALUE in insn INSN. If VALUE is zero, we
+ are saying that the register is clobbered and we no longer know its
+ value. If INSN is zero, don't update reg_last_set; this is only permitted
+ with VALUE also zero and is used to invalidate the register. */
+
+static void
+record_value_for_reg (reg, insn, value)
+ rtx reg;
+ rtx insn;
+ rtx value;
+{
+ int regno = REGNO (reg);
+ int endregno = regno + (regno < FIRST_PSEUDO_REGISTER
+ ? HARD_REGNO_NREGS (regno, GET_MODE (reg)) : 1);
+ int i;
+
+ /* If VALUE contains REG and we have a previous value for REG, substitute
+ the previous value. */
+ if (value && insn && reg_overlap_mentioned_p (reg, value))
+ {
+ rtx tem;
+
+ /* Set things up so get_last_value is allowed to see anything set up to
+ our insn. */
+ subst_low_cuid = INSN_CUID (insn);
+ tem = get_last_value (reg);
+
+ if (tem)
+ value = replace_rtx (copy_rtx (value), reg, tem);
+ }
+
+ /* For each register modified, show we don't know its value, that
+ we don't know about its bitwise content, that its value has been
+ updated, and that we don't know the location of the death of the
+ register. */
+ for (i = regno; i < endregno; i ++)
+ {
+ if (insn)
+ reg_last_set[i] = insn;
+ reg_last_set_value[i] = 0;
+ reg_last_set_mode[i] = 0;
+ reg_last_set_nonzero_bits[i] = 0;
+ reg_last_set_sign_bit_copies[i] = 0;
+ reg_last_death[i] = 0;
+ }
+
+ /* Mark registers that are being referenced in this value. */
+ if (value)
+ update_table_tick (value);
+
+ /* Now update the status of each register being set.
+ If someone is using this register in this block, set this register
+ to invalid since we will get confused between the two lives in this
+ basic block. This makes using this register always invalid. In cse, we
+ scan the table to invalidate all entries using this register, but this
+ is too much work for us. */
+
+ for (i = regno; i < endregno; i++)
+ {
+ reg_last_set_label[i] = label_tick;
+ if (value && reg_last_set_table_tick[i] == label_tick)
+ reg_last_set_invalid[i] = 1;
+ else
+ reg_last_set_invalid[i] = 0;
+ }
+
+ /* The value being assigned might refer to X (like in "x++;"). In that
+ case, we must replace it with (clobber (const_int 0)) to prevent
+ infinite loops. */
+ if (value && ! get_last_value_validate (&value,
+ reg_last_set_label[regno], 0))
+ {
+ value = copy_rtx (value);
+ if (! get_last_value_validate (&value, reg_last_set_label[regno], 1))
+ value = 0;
+ }
+
+ /* For the main register being modified, update the value, the mode, the
+ nonzero bits, and the number of sign bit copies. */
+
+ reg_last_set_value[regno] = value;
+
+ if (value)
+ {
+ subst_low_cuid = INSN_CUID (insn);
+ reg_last_set_mode[regno] = GET_MODE (reg);
+ reg_last_set_nonzero_bits[regno] = nonzero_bits (value, GET_MODE (reg));
+ reg_last_set_sign_bit_copies[regno]
+ = num_sign_bit_copies (value, GET_MODE (reg));
+ }
+}
+
+/* Used for communication between the following two routines. */
+static rtx record_dead_insn;
+
+/* Called via note_stores from record_dead_and_set_regs to handle one
+ SET or CLOBBER in an insn. */
+
+static void
+record_dead_and_set_regs_1 (dest, setter)
+ rtx dest, setter;
+{
+ if (GET_CODE (dest) == REG)
+ {
+ /* If we are setting the whole register, we know its value. Otherwise
+ show that we don't know the value. We can handle SUBREG in
+ some cases. */
+ if (GET_CODE (setter) == SET && dest == SET_DEST (setter))
+ record_value_for_reg (dest, record_dead_insn, SET_SRC (setter));
+ else if (GET_CODE (setter) == SET
+ && GET_CODE (SET_DEST (setter)) == SUBREG
+ && SUBREG_REG (SET_DEST (setter)) == dest
+ && GET_MODE_BITSIZE (GET_MODE (dest)) <= BITS_PER_WORD
+ && subreg_lowpart_p (SET_DEST (setter)))
+ record_value_for_reg (dest, record_dead_insn,
+ gen_lowpart_for_combine (GET_MODE (dest),
+ SET_SRC (setter)));
+ else
+ record_value_for_reg (dest, record_dead_insn, NULL_RTX);
+ }
+ else if (GET_CODE (dest) == MEM
+ /* Ignore pushes, they clobber nothing. */
+ && ! push_operand (dest, GET_MODE (dest)))
+ mem_last_set = INSN_CUID (record_dead_insn);
+}
+
+/* Update the records of when each REG was most recently set or killed
+ for the things done by INSN. This is the last thing done in processing
+ INSN in the combiner loop.
+
+ We update reg_last_set, reg_last_set_value, reg_last_set_mode,
+ reg_last_set_nonzero_bits, reg_last_set_sign_bit_copies, reg_last_death,
+ and also the similar information mem_last_set (which insn most recently
+ modified memory) and last_call_cuid (which insn was the most recent
+ subroutine call). */
+
+static void
+record_dead_and_set_regs (insn)
+ rtx insn;
+{
+ register rtx link;
+ int i;
+
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+ {
+ if (REG_NOTE_KIND (link) == REG_DEAD
+ && GET_CODE (XEXP (link, 0)) == REG)
+ {
+ int regno = REGNO (XEXP (link, 0));
+ int endregno
+ = regno + (regno < FIRST_PSEUDO_REGISTER
+ ? HARD_REGNO_NREGS (regno, GET_MODE (XEXP (link, 0)))
+ : 1);
+
+ for (i = regno; i < endregno; i++)
+ reg_last_death[i] = insn;
+ }
+ else if (REG_NOTE_KIND (link) == REG_INC)
+ record_value_for_reg (XEXP (link, 0), insn, NULL_RTX);
+ }
+
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (call_used_regs[i])
+ {
+ reg_last_set_value[i] = 0;
+ reg_last_set_mode[i] = 0;
+ reg_last_set_nonzero_bits[i] = 0;
+ reg_last_set_sign_bit_copies[i] = 0;
+ reg_last_death[i] = 0;
+ }
+
+ last_call_cuid = mem_last_set = INSN_CUID (insn);
+ }
+
+ record_dead_insn = insn;
+ note_stores (PATTERN (insn), record_dead_and_set_regs_1);
+}
+
+/* Utility routine for the following function. Verify that all the registers
+ mentioned in *LOC are valid when *LOC was part of a value set when
+ label_tick == TICK. Return 0 if some are not.
+
+ If REPLACE is non-zero, replace the invalid reference with
+ (clobber (const_int 0)) and return 1. This replacement is useful because
+ we often can get useful information about the form of a value (e.g., if
+ it was produced by a shift that always produces -1 or 0) even though
+ we don't know exactly what registers it was produced from. */
+
+static int
+get_last_value_validate (loc, tick, replace)
+ rtx *loc;
+ int tick;
+ int replace;
+{
+ rtx x = *loc;
+ char *fmt = GET_RTX_FORMAT (GET_CODE (x));
+ int len = GET_RTX_LENGTH (GET_CODE (x));
+ int i;
+
+ if (GET_CODE (x) == REG)
+ {
+ int regno = REGNO (x);
+ int endregno = regno + (regno < FIRST_PSEUDO_REGISTER
+ ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1);
+ int j;
+
+ for (j = regno; j < endregno; j++)
+ if (reg_last_set_invalid[j]
+ /* If this is a pseudo-register that was only set once, it is
+ always valid. */
+ || (! (regno >= FIRST_PSEUDO_REGISTER && reg_n_sets[regno] == 1)
+ && reg_last_set_label[j] > tick))
+ {
+ if (replace)
+ *loc = gen_rtx (CLOBBER, GET_MODE (x), const0_rtx);
+ return replace;
+ }
+
+ return 1;
+ }
+
+ for (i = 0; i < len; i++)
+ if ((fmt[i] == 'e'
+ && get_last_value_validate (&XEXP (x, i), tick, replace) == 0)
+ /* Don't bother with these. They shouldn't occur anyway. */
+ || fmt[i] == 'E')
+ return 0;
+
+ /* If we haven't found a reason for it to be invalid, it is valid. */
+ return 1;
+}
+
+/* Get the last value assigned to X, if known. Some registers
+ in the value may be replaced with (clobber (const_int 0)) if their value
+ is known longer known reliably. */
+
+static rtx
+get_last_value (x)
+ rtx x;
+{
+ int regno;
+ rtx value;
+
+ /* If this is a non-paradoxical SUBREG, get the value of its operand and
+ then convert it to the desired mode. If this is a paradoxical SUBREG,
+ we cannot predict what values the "extra" bits might have. */
+ if (GET_CODE (x) == SUBREG
+ && subreg_lowpart_p (x)
+ && (GET_MODE_SIZE (GET_MODE (x))
+ <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+ && (value = get_last_value (SUBREG_REG (x))) != 0)
+ return gen_lowpart_for_combine (GET_MODE (x), value);
+
+ if (GET_CODE (x) != REG)
+ return 0;
+
+ regno = REGNO (x);
+ value = reg_last_set_value[regno];
+
+ /* If we don't have a value or if it isn't for this basic block, return 0. */
+
+ if (value == 0
+ || (reg_n_sets[regno] != 1
+ && reg_last_set_label[regno] != label_tick))
+ return 0;
+
+ /* If the value was set in a later insn that the ones we are processing,
+ we can't use it even if the register was only set once, but make a quick
+ check to see if the previous insn set it to something. This is commonly
+ the case when the same pseudo is used by repeated insns. */
+
+ if (INSN_CUID (reg_last_set[regno]) >= subst_low_cuid)
+ {
+ rtx insn, set;
+
+ /* If there is an insn that is supposed to be immediately
+ in front of subst_insn, use it. */
+ if (subst_prev_insn != 0)
+ insn = subst_prev_insn;
+ else
+ for (insn = prev_nonnote_insn (subst_insn);
+ insn && INSN_CUID (insn) >= subst_low_cuid;
+ insn = prev_nonnote_insn (insn))
+ ;
+
+ if (insn
+ && (set = single_set (insn)) != 0
+ && rtx_equal_p (SET_DEST (set), x))
+ {
+ value = SET_SRC (set);
+
+ /* Make sure that VALUE doesn't reference X. Replace any
+ expliit references with a CLOBBER. If there are any remaining
+ references (rare), don't use the value. */
+
+ if (reg_mentioned_p (x, value))
+ value = replace_rtx (copy_rtx (value), x,
+ gen_rtx (CLOBBER, GET_MODE (x), const0_rtx));
+
+ if (reg_overlap_mentioned_p (x, value))
+ return 0;
+ }
+ else
+ return 0;
+ }
+
+ /* If the value has all its registers valid, return it. */
+ if (get_last_value_validate (&value, reg_last_set_label[regno], 0))
+ return value;
+
+ /* Otherwise, make a copy and replace any invalid register with
+ (clobber (const_int 0)). If that fails for some reason, return 0. */
+
+ value = copy_rtx (value);
+ if (get_last_value_validate (&value, reg_last_set_label[regno], 1))
+ return value;
+
+ return 0;
+}
+
+/* Return nonzero if expression X refers to a REG or to memory
+ that is set in an instruction more recent than FROM_CUID. */
+
+static int
+use_crosses_set_p (x, from_cuid)
+ register rtx x;
+ int from_cuid;
+{
+ register char *fmt;
+ register int i;
+ register enum rtx_code code = GET_CODE (x);
+
+ if (code == REG)
+ {
+ register int regno = REGNO (x);
+ int endreg = regno + (regno < FIRST_PSEUDO_REGISTER
+ ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1);
+
+#ifdef PUSH_ROUNDING
+ /* Don't allow uses of the stack pointer to be moved,
+ because we don't know whether the move crosses a push insn. */
+ if (regno == STACK_POINTER_REGNUM)
+ return 1;
+#endif
+ for (;regno < endreg; regno++)
+ if (reg_last_set[regno]
+ && INSN_CUID (reg_last_set[regno]) > from_cuid)
+ return 1;
+ return 0;
+ }
+
+ if (code == MEM && mem_last_set > from_cuid)
+ return 1;
+
+ fmt = GET_RTX_FORMAT (code);
+
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (use_crosses_set_p (XVECEXP (x, i, j), from_cuid))
+ return 1;
+ }
+ else if (fmt[i] == 'e'
+ && use_crosses_set_p (XEXP (x, i), from_cuid))
+ return 1;
+ }
+ return 0;
+}
+
+/* Define three variables used for communication between the following
+ routines. */
+
+static int reg_dead_regno, reg_dead_endregno;
+static int reg_dead_flag;
+
+/* Function called via note_stores from reg_dead_at_p.
+
+ If DEST is within [reg_dead_rengno, reg_dead_endregno), set
+ reg_dead_flag to 1 if X is a CLOBBER and to -1 it is a SET. */
+
+static void
+reg_dead_at_p_1 (dest, x)
+ rtx dest;
+ rtx x;
+{
+ int regno, endregno;
+
+ if (GET_CODE (dest) != REG)
+ return;
+
+ regno = REGNO (dest);
+ endregno = regno + (regno < FIRST_PSEUDO_REGISTER
+ ? HARD_REGNO_NREGS (regno, GET_MODE (dest)) : 1);
+
+ if (reg_dead_endregno > regno && reg_dead_regno < endregno)
+ reg_dead_flag = (GET_CODE (x) == CLOBBER) ? 1 : -1;
+}
+
+/* Return non-zero if REG is known to be dead at INSN.
+
+ We scan backwards from INSN. If we hit a REG_DEAD note or a CLOBBER
+ referencing REG, it is dead. If we hit a SET referencing REG, it is
+ live. Otherwise, see if it is live or dead at the start of the basic
+ block we are in. Hard regs marked as being live in NEWPAT_USED_REGS
+ must be assumed to be always live. */
+
+static int
+reg_dead_at_p (reg, insn)
+ rtx reg;
+ rtx insn;
+{
+ int block, i;
+
+ /* Set variables for reg_dead_at_p_1. */
+ reg_dead_regno = REGNO (reg);
+ reg_dead_endregno = reg_dead_regno + (reg_dead_regno < FIRST_PSEUDO_REGISTER
+ ? HARD_REGNO_NREGS (reg_dead_regno,
+ GET_MODE (reg))
+ : 1);
+
+ reg_dead_flag = 0;
+
+ /* Check that reg isn't mentioned in NEWPAT_USED_REGS. */
+ if (reg_dead_regno < FIRST_PSEUDO_REGISTER)
+ {
+ for (i = reg_dead_regno; i < reg_dead_endregno; i++)
+ if (TEST_HARD_REG_BIT (newpat_used_regs, i))
+ return 0;
+ }
+
+ /* Scan backwards until we find a REG_DEAD note, SET, CLOBBER, label, or
+ beginning of function. */
+ for (; insn && GET_CODE (insn) != CODE_LABEL;
+ insn = prev_nonnote_insn (insn))
+ {
+ note_stores (PATTERN (insn), reg_dead_at_p_1);
+ if (reg_dead_flag)
+ return reg_dead_flag == 1 ? 1 : 0;
+
+ if (find_regno_note (insn, REG_DEAD, reg_dead_regno))
+ return 1;
+ }
+
+ /* Get the basic block number that we were in. */
+ if (insn == 0)
+ block = 0;
+ else
+ {
+ for (block = 0; block < n_basic_blocks; block++)
+ if (insn == basic_block_head[block])
+ break;
+
+ if (block == n_basic_blocks)
+ return 0;
+ }
+
+ for (i = reg_dead_regno; i < reg_dead_endregno; i++)
+ if (basic_block_live_at_start[block][i / REGSET_ELT_BITS]
+ & ((REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS)))
+ return 0;
+
+ return 1;
+}
+
+/* Note hard registers in X that are used. This code is similar to
+ that in flow.c, but much simpler since we don't care about pseudos. */
+
+static void
+mark_used_regs_combine (x)
+ rtx x;
+{
+ register RTX_CODE code = GET_CODE (x);
+ register int regno;
+ int i;
+
+ switch (code)
+ {
+ case LABEL_REF:
+ case SYMBOL_REF:
+ case CONST_INT:
+ case CONST:
+ case CONST_DOUBLE:
+ case PC:
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ case ASM_INPUT:
+#ifdef HAVE_cc0
+ /* CC0 must die in the insn after it is set, so we don't need to take
+ special note of it here. */
+ case CC0:
+#endif
+ return;
+
+ case CLOBBER:
+ /* If we are clobbering a MEM, mark any hard registers inside the
+ address as used. */
+ if (GET_CODE (XEXP (x, 0)) == MEM)
+ mark_used_regs_combine (XEXP (XEXP (x, 0), 0));
+ return;
+
+ case REG:
+ regno = REGNO (x);
+ /* A hard reg in a wide mode may really be multiple registers.
+ If so, mark all of them just like the first. */
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ /* None of this applies to the stack, frame or arg pointers */
+ if (regno == STACK_POINTER_REGNUM
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+ || regno == HARD_FRAME_POINTER_REGNUM
+#endif
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ || (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
+#endif
+ || regno == FRAME_POINTER_REGNUM)
+ return;
+
+ i = HARD_REGNO_NREGS (regno, GET_MODE (x));
+ while (i-- > 0)
+ SET_HARD_REG_BIT (newpat_used_regs, regno + i);
+ }
+ return;
+
+ case SET:
+ {
+ /* If setting a MEM, or a SUBREG of a MEM, then note any hard regs in
+ the address. */
+ register rtx testreg = SET_DEST (x);
+
+ while (GET_CODE (testreg) == SUBREG
+ || GET_CODE (testreg) == ZERO_EXTRACT
+ || GET_CODE (testreg) == SIGN_EXTRACT
+ || GET_CODE (testreg) == STRICT_LOW_PART)
+ testreg = XEXP (testreg, 0);
+
+ if (GET_CODE (testreg) == MEM)
+ mark_used_regs_combine (XEXP (testreg, 0));
+
+ mark_used_regs_combine (SET_SRC (x));
+ return;
+ }
+ }
+
+ /* Recursively scan the operands of this expression. */
+
+ {
+ register char *fmt = GET_RTX_FORMAT (code);
+
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ mark_used_regs_combine (XEXP (x, i));
+ else if (fmt[i] == 'E')
+ {
+ register int j;
+
+ for (j = 0; j < XVECLEN (x, i); j++)
+ mark_used_regs_combine (XVECEXP (x, i, j));
+ }
+ }
+ }
+}
+
+
+/* Remove register number REGNO from the dead registers list of INSN.
+
+ Return the note used to record the death, if there was one. */
+
+rtx
+remove_death (regno, insn)
+ int regno;
+ rtx insn;
+{
+ register rtx note = find_regno_note (insn, REG_DEAD, regno);
+
+ if (note)
+ {
+ reg_n_deaths[regno]--;
+ remove_note (insn, note);
+ }
+
+ return note;
+}
+
+/* For each register (hardware or pseudo) used within expression X, if its
+ death is in an instruction with cuid between FROM_CUID (inclusive) and
+ TO_INSN (exclusive), put a REG_DEAD note for that register in the
+ list headed by PNOTES.
+
+ This is done when X is being merged by combination into TO_INSN. These
+ notes will then be distributed as needed. */
+
+static void
+move_deaths (x, from_cuid, to_insn, pnotes)
+ rtx x;
+ int from_cuid;
+ rtx to_insn;
+ rtx *pnotes;
+{
+ register char *fmt;
+ register int len, i;
+ register enum rtx_code code = GET_CODE (x);
+
+ if (code == REG)
+ {
+ register int regno = REGNO (x);
+ register rtx where_dead = reg_last_death[regno];
+
+ if (where_dead && INSN_CUID (where_dead) >= from_cuid
+ && INSN_CUID (where_dead) < INSN_CUID (to_insn))
+ {
+ rtx note = remove_death (regno, where_dead);
+
+ /* It is possible for the call above to return 0. This can occur
+ when reg_last_death points to I2 or I1 that we combined with.
+ In that case make a new note.
+
+ We must also check for the case where X is a hard register
+ and NOTE is a death note for a range of hard registers
+ including X. In that case, we must put REG_DEAD notes for
+ the remaining registers in place of NOTE. */
+
+ if (note != 0 && regno < FIRST_PSEUDO_REGISTER
+ && (GET_MODE_SIZE (GET_MODE (XEXP (note, 0)))
+ != GET_MODE_SIZE (GET_MODE (x))))
+ {
+ int deadregno = REGNO (XEXP (note, 0));
+ int deadend
+ = (deadregno + HARD_REGNO_NREGS (deadregno,
+ GET_MODE (XEXP (note, 0))));
+ int ourend = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
+ int i;
+
+ for (i = deadregno; i < deadend; i++)
+ if (i < regno || i >= ourend)
+ REG_NOTES (where_dead)
+ = gen_rtx (EXPR_LIST, REG_DEAD,
+ gen_rtx (REG, reg_raw_mode[i], i),
+ REG_NOTES (where_dead));
+ }
+
+ if (note != 0 && GET_MODE (XEXP (note, 0)) == GET_MODE (x))
+ {
+ XEXP (note, 1) = *pnotes;
+ *pnotes = note;
+ }
+ else
+ *pnotes = gen_rtx (EXPR_LIST, REG_DEAD, x, *pnotes);
+
+ reg_n_deaths[regno]++;
+ }
+
+ return;
+ }
+
+ else if (GET_CODE (x) == SET)
+ {
+ rtx dest = SET_DEST (x);
+
+ move_deaths (SET_SRC (x), from_cuid, to_insn, pnotes);
+
+ /* In the case of a ZERO_EXTRACT, a STRICT_LOW_PART, or a SUBREG
+ that accesses one word of a multi-word item, some
+ piece of everything register in the expression is used by
+ this insn, so remove any old death. */
+
+ if (GET_CODE (dest) == ZERO_EXTRACT
+ || GET_CODE (dest) == STRICT_LOW_PART
+ || (GET_CODE (dest) == SUBREG
+ && (((GET_MODE_SIZE (GET_MODE (dest))
+ + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
+ == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest)))
+ + UNITS_PER_WORD - 1) / UNITS_PER_WORD))))
+ {
+ move_deaths (dest, from_cuid, to_insn, pnotes);
+ return;
+ }
+
+ /* If this is some other SUBREG, we know it replaces the entire
+ value, so use that as the destination. */
+ if (GET_CODE (dest) == SUBREG)
+ dest = SUBREG_REG (dest);
+
+ /* If this is a MEM, adjust deaths of anything used in the address.
+ For a REG (the only other possibility), the entire value is
+ being replaced so the old value is not used in this insn. */
+
+ if (GET_CODE (dest) == MEM)
+ move_deaths (XEXP (dest, 0), from_cuid, to_insn, pnotes);
+ return;
+ }
+
+ else if (GET_CODE (x) == CLOBBER)
+ return;
+
+ len = GET_RTX_LENGTH (code);
+ fmt = GET_RTX_FORMAT (code);
+
+ for (i = 0; i < len; i++)
+ {
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ move_deaths (XVECEXP (x, i, j), from_cuid, to_insn, pnotes);
+ }
+ else if (fmt[i] == 'e')
+ move_deaths (XEXP (x, i), from_cuid, to_insn, pnotes);
+ }
+}
+
+/* Return 1 if X is the target of a bit-field assignment in BODY, the
+ pattern of an insn. X must be a REG. */
+
+static int
+reg_bitfield_target_p (x, body)
+ rtx x;
+ rtx body;
+{
+ int i;
+
+ if (GET_CODE (body) == SET)
+ {
+ rtx dest = SET_DEST (body);
+ rtx target;
+ int regno, tregno, endregno, endtregno;
+
+ if (GET_CODE (dest) == ZERO_EXTRACT)
+ target = XEXP (dest, 0);
+ else if (GET_CODE (dest) == STRICT_LOW_PART)
+ target = SUBREG_REG (XEXP (dest, 0));
+ else
+ return 0;
+
+ if (GET_CODE (target) == SUBREG)
+ target = SUBREG_REG (target);
+
+ if (GET_CODE (target) != REG)
+ return 0;
+
+ tregno = REGNO (target), regno = REGNO (x);
+ if (tregno >= FIRST_PSEUDO_REGISTER || regno >= FIRST_PSEUDO_REGISTER)
+ return target == x;
+
+ endtregno = tregno + HARD_REGNO_NREGS (tregno, GET_MODE (target));
+ endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
+
+ return endregno > tregno && regno < endtregno;
+ }
+
+ else if (GET_CODE (body) == PARALLEL)
+ for (i = XVECLEN (body, 0) - 1; i >= 0; i--)
+ if (reg_bitfield_target_p (x, XVECEXP (body, 0, i)))
+ return 1;
+
+ return 0;
+}
+
+/* Given a chain of REG_NOTES originally from FROM_INSN, try to place them
+ as appropriate. I3 and I2 are the insns resulting from the combination
+ insns including FROM (I2 may be zero).
+
+ ELIM_I2 and ELIM_I1 are either zero or registers that we know will
+ not need REG_DEAD notes because they are being substituted for. This
+ saves searching in the most common cases.
+
+ Each note in the list is either ignored or placed on some insns, depending
+ on the type of note. */
+
+static void
+distribute_notes (notes, from_insn, i3, i2, elim_i2, elim_i1)
+ rtx notes;
+ rtx from_insn;
+ rtx i3, i2;
+ rtx elim_i2, elim_i1;
+{
+ rtx note, next_note;
+ rtx tem;
+
+ for (note = notes; note; note = next_note)
+ {
+ rtx place = 0, place2 = 0;
+
+ /* If this NOTE references a pseudo register, ensure it references
+ the latest copy of that register. */
+ if (XEXP (note, 0) && GET_CODE (XEXP (note, 0)) == REG
+ && REGNO (XEXP (note, 0)) >= FIRST_PSEUDO_REGISTER)
+ XEXP (note, 0) = regno_reg_rtx[REGNO (XEXP (note, 0))];
+
+ next_note = XEXP (note, 1);
+ switch (REG_NOTE_KIND (note))
+ {
+ case REG_UNUSED:
+ /* If this note is from any insn other than i3, then we have no
+ use for it, and must ignore it.
+
+ Any clobbers for i3 may still exist, and so we must process
+ REG_UNUSED notes from that insn.
+
+ Any clobbers from i2 or i1 can only exist if they were added by
+ recog_for_combine. In that case, recog_for_combine created the
+ necessary REG_UNUSED notes. Trying to keep any original
+ REG_UNUSED notes from these insns can cause incorrect output
+ if it is for the same register as the original i3 dest.
+ In that case, we will notice that the register is set in i3,
+ and then add a REG_UNUSED note for the destination of i3, which
+ is wrong. */
+ if (from_insn != i3)
+ break;
+
+ /* If this register is set or clobbered in I3, put the note there
+ unless there is one already. */
+ else if (reg_set_p (XEXP (note, 0), PATTERN (i3)))
+ {
+ if (! (GET_CODE (XEXP (note, 0)) == REG
+ ? find_regno_note (i3, REG_UNUSED, REGNO (XEXP (note, 0)))
+ : find_reg_note (i3, REG_UNUSED, XEXP (note, 0))))
+ place = i3;
+ }
+ /* Otherwise, if this register is used by I3, then this register
+ now dies here, so we must put a REG_DEAD note here unless there
+ is one already. */
+ else if (reg_referenced_p (XEXP (note, 0), PATTERN (i3))
+ && ! (GET_CODE (XEXP (note, 0)) == REG
+ ? find_regno_note (i3, REG_DEAD, REGNO (XEXP (note, 0)))
+ : find_reg_note (i3, REG_DEAD, XEXP (note, 0))))
+ {
+ PUT_REG_NOTE_KIND (note, REG_DEAD);
+ place = i3;
+ }
+ break;
+
+ case REG_EQUAL:
+ case REG_EQUIV:
+ case REG_NONNEG:
+ /* These notes say something about results of an insn. We can
+ only support them if they used to be on I3 in which case they
+ remain on I3. Otherwise they are ignored.
+
+ If the note refers to an expression that is not a constant, we
+ must also ignore the note since we cannot tell whether the
+ equivalence is still true. It might be possible to do
+ slightly better than this (we only have a problem if I2DEST
+ or I1DEST is present in the expression), but it doesn't
+ seem worth the trouble. */
+
+ if (from_insn == i3
+ && (XEXP (note, 0) == 0 || CONSTANT_P (XEXP (note, 0))))
+ place = i3;
+ break;
+
+ case REG_INC:
+ case REG_NO_CONFLICT:
+ case REG_LABEL:
+ /* These notes say something about how a register is used. They must
+ be present on any use of the register in I2 or I3. */
+ if (reg_mentioned_p (XEXP (note, 0), PATTERN (i3)))
+ place = i3;
+
+ if (i2 && reg_mentioned_p (XEXP (note, 0), PATTERN (i2)))
+ {
+ if (place)
+ place2 = i2;
+ else
+ place = i2;
+ }
+ break;
+
+ case REG_WAS_0:
+ /* It is too much trouble to try to see if this note is still
+ correct in all situations. It is better to simply delete it. */
+ break;
+
+ case REG_RETVAL:
+ /* If the insn previously containing this note still exists,
+ put it back where it was. Otherwise move it to the previous
+ insn. Adjust the corresponding REG_LIBCALL note. */
+ if (GET_CODE (from_insn) != NOTE)
+ place = from_insn;
+ else
+ {
+ tem = find_reg_note (XEXP (note, 0), REG_LIBCALL, NULL_RTX);
+ place = prev_real_insn (from_insn);
+ if (tem && place)
+ XEXP (tem, 0) = place;
+ }
+ break;
+
+ case REG_LIBCALL:
+ /* This is handled similarly to REG_RETVAL. */
+ if (GET_CODE (from_insn) != NOTE)
+ place = from_insn;
+ else
+ {
+ tem = find_reg_note (XEXP (note, 0), REG_RETVAL, NULL_RTX);
+ place = next_real_insn (from_insn);
+ if (tem && place)
+ XEXP (tem, 0) = place;
+ }
+ break;
+
+ case REG_DEAD:
+ /* If the register is used as an input in I3, it dies there.
+ Similarly for I2, if it is non-zero and adjacent to I3.
+
+ If the register is not used as an input in either I3 or I2
+ and it is not one of the registers we were supposed to eliminate,
+ there are two possibilities. We might have a non-adjacent I2
+ or we might have somehow eliminated an additional register
+ from a computation. For example, we might have had A & B where
+ we discover that B will always be zero. In this case we will
+ eliminate the reference to A.
+
+ In both cases, we must search to see if we can find a previous
+ use of A and put the death note there. */
+
+ if (from_insn
+ && GET_CODE (from_insn) == CALL_INSN
+ && find_reg_fusage (from_insn, USE, XEXP (note, 0)))
+ place = from_insn;
+ else if (reg_referenced_p (XEXP (note, 0), PATTERN (i3)))
+ place = i3;
+ else if (i2 != 0 && next_nonnote_insn (i2) == i3
+ && reg_referenced_p (XEXP (note, 0), PATTERN (i2)))
+ place = i2;
+
+ if (XEXP (note, 0) == elim_i2 || XEXP (note, 0) == elim_i1)
+ break;
+
+ /* If the register is used in both I2 and I3 and it dies in I3,
+ we might have added another reference to it. If reg_n_refs
+ was 2, bump it to 3. This has to be correct since the
+ register must have been set somewhere. The reason this is
+ done is because local-alloc.c treats 2 references as a
+ special case. */
+
+ if (place == i3 && i2 != 0 && GET_CODE (XEXP (note, 0)) == REG
+ && reg_n_refs[REGNO (XEXP (note, 0))]== 2
+ && reg_referenced_p (XEXP (note, 0), PATTERN (i2)))
+ reg_n_refs[REGNO (XEXP (note, 0))] = 3;
+
+ if (place == 0)
+ for (tem = prev_nonnote_insn (i3);
+ tem && (GET_CODE (tem) == INSN
+ || GET_CODE (tem) == CALL_INSN);
+ tem = prev_nonnote_insn (tem))
+ {
+ /* If the register is being set at TEM, see if that is all
+ TEM is doing. If so, delete TEM. Otherwise, make this
+ into a REG_UNUSED note instead. */
+ if (reg_set_p (XEXP (note, 0), PATTERN (tem)))
+ {
+ rtx set = single_set (tem);
+
+ /* Verify that it was the set, and not a clobber that
+ modified the register. */
+
+ if (set != 0 && ! side_effects_p (SET_SRC (set))
+ && rtx_equal_p (XEXP (note, 0), SET_DEST (set)))
+ {
+ /* Move the notes and links of TEM elsewhere.
+ This might delete other dead insns recursively.
+ First set the pattern to something that won't use
+ any register. */
+
+ PATTERN (tem) = pc_rtx;
+
+ distribute_notes (REG_NOTES (tem), tem, tem,
+ NULL_RTX, NULL_RTX, NULL_RTX);
+ distribute_links (LOG_LINKS (tem));
+
+ PUT_CODE (tem, NOTE);
+ NOTE_LINE_NUMBER (tem) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (tem) = 0;
+ }
+ else
+ {
+ PUT_REG_NOTE_KIND (note, REG_UNUSED);
+
+ /* If there isn't already a REG_UNUSED note, put one
+ here. */
+ if (! find_regno_note (tem, REG_UNUSED,
+ REGNO (XEXP (note, 0))))
+ place = tem;
+ break;
+ }
+ }
+ else if (reg_referenced_p (XEXP (note, 0), PATTERN (tem))
+ || (GET_CODE (tem) == CALL_INSN
+ && find_reg_fusage (tem, USE, XEXP (note, 0))))
+ {
+ place = tem;
+ break;
+ }
+ }
+
+ /* If the register is set or already dead at PLACE, we needn't do
+ anything with this note if it is still a REG_DEAD note.
+
+ Note that we cannot use just `dead_or_set_p' here since we can
+ convert an assignment to a register into a bit-field assignment.
+ Therefore, we must also omit the note if the register is the
+ target of a bitfield assignment. */
+
+ if (place && REG_NOTE_KIND (note) == REG_DEAD)
+ {
+ int regno = REGNO (XEXP (note, 0));
+
+ if (dead_or_set_p (place, XEXP (note, 0))
+ || reg_bitfield_target_p (XEXP (note, 0), PATTERN (place)))
+ {
+ /* Unless the register previously died in PLACE, clear
+ reg_last_death. [I no longer understand why this is
+ being done.] */
+ if (reg_last_death[regno] != place)
+ reg_last_death[regno] = 0;
+ place = 0;
+ }
+ else
+ reg_last_death[regno] = place;
+
+ /* If this is a death note for a hard reg that is occupying
+ multiple registers, ensure that we are still using all
+ parts of the object. If we find a piece of the object
+ that is unused, we must add a USE for that piece before
+ PLACE and put the appropriate REG_DEAD note on it.
+
+ An alternative would be to put a REG_UNUSED for the pieces
+ on the insn that set the register, but that can't be done if
+ it is not in the same block. It is simpler, though less
+ efficient, to add the USE insns. */
+
+ if (place && regno < FIRST_PSEUDO_REGISTER
+ && HARD_REGNO_NREGS (regno, GET_MODE (XEXP (note, 0))) > 1)
+ {
+ int endregno
+ = regno + HARD_REGNO_NREGS (regno,
+ GET_MODE (XEXP (note, 0)));
+ int all_used = 1;
+ int i;
+
+ for (i = regno; i < endregno; i++)
+ if (! refers_to_regno_p (i, i + 1, PATTERN (place), 0)
+ && ! find_regno_fusage (place, USE, i))
+ {
+ rtx piece = gen_rtx (REG, reg_raw_mode[i], i);
+ rtx p;
+
+ /* See if we already placed a USE note for this
+ register in front of PLACE. */
+ for (p = place;
+ GET_CODE (PREV_INSN (p)) == INSN
+ && GET_CODE (PATTERN (PREV_INSN (p))) == USE;
+ p = PREV_INSN (p))
+ if (rtx_equal_p (piece,
+ XEXP (PATTERN (PREV_INSN (p)), 0)))
+ {
+ p = 0;
+ break;
+ }
+
+ if (p)
+ {
+ rtx use_insn
+ = emit_insn_before (gen_rtx (USE, VOIDmode,
+ piece),
+ p);
+ REG_NOTES (use_insn)
+ = gen_rtx (EXPR_LIST, REG_DEAD, piece,
+ REG_NOTES (use_insn));
+ }
+
+ all_used = 0;
+ }
+
+ /* Check for the case where the register dying partially
+ overlaps the register set by this insn. */
+ if (all_used)
+ for (i = regno; i < endregno; i++)
+ if (dead_or_set_regno_p (place, i))
+ {
+ all_used = 0;
+ break;
+ }
+
+ if (! all_used)
+ {
+ /* Put only REG_DEAD notes for pieces that are
+ still used and that are not already dead or set. */
+
+ for (i = regno; i < endregno; i++)
+ {
+ rtx piece = gen_rtx (REG, reg_raw_mode[i], i);
+
+ if (reg_referenced_p (piece, PATTERN (place))
+ && ! dead_or_set_p (place, piece)
+ && ! reg_bitfield_target_p (piece,
+ PATTERN (place)))
+ REG_NOTES (place) = gen_rtx (EXPR_LIST, REG_DEAD,
+ piece,
+ REG_NOTES (place));
+ }
+
+ place = 0;
+ }
+ }
+ }
+ break;
+
+ default:
+ /* Any other notes should not be present at this point in the
+ compilation. */
+ abort ();
+ }
+
+ if (place)
+ {
+ XEXP (note, 1) = REG_NOTES (place);
+ REG_NOTES (place) = note;
+ }
+ else if ((REG_NOTE_KIND (note) == REG_DEAD
+ || REG_NOTE_KIND (note) == REG_UNUSED)
+ && GET_CODE (XEXP (note, 0)) == REG)
+ reg_n_deaths[REGNO (XEXP (note, 0))]--;
+
+ if (place2)
+ {
+ if ((REG_NOTE_KIND (note) == REG_DEAD
+ || REG_NOTE_KIND (note) == REG_UNUSED)
+ && GET_CODE (XEXP (note, 0)) == REG)
+ reg_n_deaths[REGNO (XEXP (note, 0))]++;
+
+ REG_NOTES (place2) = gen_rtx (GET_CODE (note), REG_NOTE_KIND (note),
+ XEXP (note, 0), REG_NOTES (place2));
+ }
+ }
+}
+
+/* Similarly to above, distribute the LOG_LINKS that used to be present on
+ I3, I2, and I1 to new locations. This is also called in one case to
+ add a link pointing at I3 when I3's destination is changed. */
+
+static void
+distribute_links (links)
+ rtx links;
+{
+ rtx link, next_link;
+
+ for (link = links; link; link = next_link)
+ {
+ rtx place = 0;
+ rtx insn;
+ rtx set, reg;
+
+ next_link = XEXP (link, 1);
+
+ /* If the insn that this link points to is a NOTE or isn't a single
+ set, ignore it. In the latter case, it isn't clear what we
+ can do other than ignore the link, since we can't tell which
+ register it was for. Such links wouldn't be used by combine
+ anyway.
+
+ It is not possible for the destination of the target of the link to
+ have been changed by combine. The only potential of this is if we
+ replace I3, I2, and I1 by I3 and I2. But in that case the
+ destination of I2 also remains unchanged. */
+
+ if (GET_CODE (XEXP (link, 0)) == NOTE
+ || (set = single_set (XEXP (link, 0))) == 0)
+ continue;
+
+ reg = SET_DEST (set);
+ while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == ZERO_EXTRACT
+ || GET_CODE (reg) == SIGN_EXTRACT
+ || GET_CODE (reg) == STRICT_LOW_PART)
+ reg = XEXP (reg, 0);
+
+ /* A LOG_LINK is defined as being placed on the first insn that uses
+ a register and points to the insn that sets the register. Start
+ searching at the next insn after the target of the link and stop
+ when we reach a set of the register or the end of the basic block.
+
+ Note that this correctly handles the link that used to point from
+ I3 to I2. Also note that not much searching is typically done here
+ since most links don't point very far away. */
+
+ for (insn = NEXT_INSN (XEXP (link, 0));
+ (insn && (this_basic_block == n_basic_blocks - 1
+ || basic_block_head[this_basic_block + 1] != insn));
+ insn = NEXT_INSN (insn))
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && reg_overlap_mentioned_p (reg, PATTERN (insn)))
+ {
+ if (reg_referenced_p (reg, PATTERN (insn)))
+ place = insn;
+ break;
+ }
+ else if (GET_CODE (insn) == CALL_INSN
+ && find_reg_fusage (insn, USE, reg))
+ {
+ place = insn;
+ break;
+ }
+
+ /* If we found a place to put the link, place it there unless there
+ is already a link to the same insn as LINK at that point. */
+
+ if (place)
+ {
+ rtx link2;
+
+ for (link2 = LOG_LINKS (place); link2; link2 = XEXP (link2, 1))
+ if (XEXP (link2, 0) == XEXP (link, 0))
+ break;
+
+ if (link2 == 0)
+ {
+ XEXP (link, 1) = LOG_LINKS (place);
+ LOG_LINKS (place) = link;
+
+ /* Set added_links_insn to the earliest insn we added a
+ link to. */
+ if (added_links_insn == 0
+ || INSN_CUID (added_links_insn) > INSN_CUID (place))
+ added_links_insn = place;
+ }
+ }
+ }
+}
+
+void
+dump_combine_stats (file)
+ FILE *file;
+{
+ fprintf
+ (file,
+ ";; Combiner statistics: %d attempts, %d substitutions (%d requiring new space),\n;; %d successes.\n\n",
+ combine_attempts, combine_merges, combine_extras, combine_successes);
+}
+
+void
+dump_combine_total_stats (file)
+ FILE *file;
+{
+ fprintf
+ (file,
+ "\n;; Combiner totals: %d attempts, %d substitutions (%d requiring new space),\n;; %d successes.\n",
+ total_attempts, total_merges, total_extras, total_successes);
+}
diff --git a/gnu/usr.bin/cc/cc_int/convert.c b/gnu/usr.bin/cc/cc_int/convert.c
new file mode 100644
index 0000000..2cb5990
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/convert.c
@@ -0,0 +1,460 @@
+/* Utility routines for data type conversion for GNU C.
+ Copyright (C) 1987, 1988, 1991, 1992, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU C.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* These routines are somewhat language-independent utility function
+ intended to be called by the language-specific convert () functions. */
+
+#include "config.h"
+#include "tree.h"
+#include "flags.h"
+#include "convert.h"
+
+/* Convert EXPR to some pointer type TYPE.
+
+ EXPR must be pointer, integer, enumeral, or literal zero;
+ in other cases error is called. */
+
+tree
+convert_to_pointer (type, expr)
+ tree type, expr;
+{
+ register tree intype = TREE_TYPE (expr);
+ register enum tree_code form = TREE_CODE (intype);
+
+ if (integer_zerop (expr))
+ {
+ if (type == TREE_TYPE (null_pointer_node))
+ return null_pointer_node;
+ expr = build_int_2 (0, 0);
+ TREE_TYPE (expr) = type;
+ return expr;
+ }
+
+ if (form == POINTER_TYPE)
+ return build1 (NOP_EXPR, type, expr);
+
+
+ if (form == INTEGER_TYPE || form == ENUMERAL_TYPE)
+ {
+ if (type_precision (intype) == POINTER_SIZE)
+ return build1 (CONVERT_EXPR, type, expr);
+ expr = convert (type_for_size (POINTER_SIZE, 0), expr);
+ /* Modes may be different but sizes should be the same. */
+ if (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (expr)))
+ != GET_MODE_SIZE (TYPE_MODE (type)))
+ /* There is supposed to be some integral type
+ that is the same width as a pointer. */
+ abort ();
+ return convert_to_pointer (type, expr);
+ }
+
+ error ("cannot convert to a pointer type");
+
+ return null_pointer_node;
+}
+
+/* Convert EXPR to some floating-point type TYPE.
+
+ EXPR must be float, integer, or enumeral;
+ in other cases error is called. */
+
+tree
+convert_to_real (type, expr)
+ tree type, expr;
+{
+ register enum tree_code form = TREE_CODE (TREE_TYPE (expr));
+
+ if (form == REAL_TYPE)
+ return build1 (flag_float_store ? CONVERT_EXPR : NOP_EXPR,
+ type, expr);
+
+ if (INTEGRAL_TYPE_P (TREE_TYPE (expr)))
+ return build1 (FLOAT_EXPR, type, expr);
+
+ if (form == COMPLEX_TYPE)
+ return convert (type, fold (build1 (REALPART_EXPR,
+ TREE_TYPE (TREE_TYPE (expr)), expr)));
+
+ if (form == POINTER_TYPE)
+ error ("pointer value used where a floating point value was expected");
+ else
+ error ("aggregate value used where a float was expected");
+
+ {
+ register tree tem = make_node (REAL_CST);
+ TREE_TYPE (tem) = type;
+ TREE_REAL_CST (tem) = REAL_VALUE_ATOF ("0.0", TYPE_MODE (type));
+ return tem;
+ }
+}
+
+/* Convert EXPR to some integer (or enum) type TYPE.
+
+ EXPR must be pointer, integer, discrete (enum, char, or bool), or float;
+ in other cases error is called.
+
+ The result of this is always supposed to be a newly created tree node
+ not in use in any existing structure. */
+
+tree
+convert_to_integer (type, expr)
+ tree type, expr;
+{
+ register tree intype = TREE_TYPE (expr);
+ register enum tree_code form = TREE_CODE (intype);
+
+ if (form == POINTER_TYPE)
+ {
+ if (integer_zerop (expr))
+ expr = integer_zero_node;
+ else
+ expr = fold (build1 (CONVERT_EXPR,
+ type_for_size (POINTER_SIZE, 0), expr));
+ intype = TREE_TYPE (expr);
+ form = TREE_CODE (intype);
+ if (intype == type)
+ return expr;
+ }
+
+ if (form == INTEGER_TYPE || form == ENUMERAL_TYPE
+ || form == BOOLEAN_TYPE || form == CHAR_TYPE)
+ {
+ register unsigned outprec = TYPE_PRECISION (type);
+ register unsigned inprec = TYPE_PRECISION (intype);
+ register enum tree_code ex_form = TREE_CODE (expr);
+
+ /* If we are widening the type, put in an explicit conversion.
+ Similarly if we are not changing the width. However, if this is
+ a logical operation that just returns 0 or 1, we can change the
+ type of the expression. For logical operations, we must
+ also change the types of the operands to maintain type
+ correctness. */
+
+ if (TREE_CODE_CLASS (ex_form) == '<')
+ {
+ TREE_TYPE (expr) = type;
+ return expr;
+ }
+ else if (ex_form == TRUTH_AND_EXPR || ex_form == TRUTH_ANDIF_EXPR
+ || ex_form == TRUTH_OR_EXPR || ex_form == TRUTH_ORIF_EXPR
+ || ex_form == TRUTH_XOR_EXPR)
+ {
+ TREE_OPERAND (expr, 0) = convert (type, TREE_OPERAND (expr, 0));
+ TREE_OPERAND (expr, 1) = convert (type, TREE_OPERAND (expr, 1));
+ TREE_TYPE (expr) = type;
+ return expr;
+ }
+ else if (ex_form == TRUTH_NOT_EXPR)
+ {
+ TREE_OPERAND (expr, 0) = convert (type, TREE_OPERAND (expr, 0));
+ TREE_TYPE (expr) = type;
+ return expr;
+ }
+ else if (outprec >= inprec)
+ return build1 (NOP_EXPR, type, expr);
+
+ /* Here detect when we can distribute the truncation down past some
+ arithmetic. For example, if adding two longs and converting to an
+ int, we can equally well convert both to ints and then add.
+ For the operations handled here, such truncation distribution
+ is always safe.
+ It is desirable in these cases:
+ 1) when truncating down to full-word from a larger size
+ 2) when truncating takes no work.
+ 3) when at least one operand of the arithmetic has been extended
+ (as by C's default conversions). In this case we need two conversions
+ if we do the arithmetic as already requested, so we might as well
+ truncate both and then combine. Perhaps that way we need only one.
+
+ Note that in general we cannot do the arithmetic in a type
+ shorter than the desired result of conversion, even if the operands
+ are both extended from a shorter type, because they might overflow
+ if combined in that type. The exceptions to this--the times when
+ two narrow values can be combined in their narrow type even to
+ make a wider result--are handled by "shorten" in build_binary_op. */
+
+ switch (ex_form)
+ {
+ case RSHIFT_EXPR:
+ /* We can pass truncation down through right shifting
+ when the shift count is a nonpositive constant. */
+ if (TREE_CODE (TREE_OPERAND (expr, 1)) == INTEGER_CST
+ && tree_int_cst_lt (TREE_OPERAND (expr, 1),
+ convert (TREE_TYPE (TREE_OPERAND (expr, 1)),
+ integer_one_node)))
+ goto trunc1;
+ break;
+
+ case LSHIFT_EXPR:
+ /* We can pass truncation down through left shifting
+ when the shift count is a nonnegative constant. */
+ if (TREE_CODE (TREE_OPERAND (expr, 1)) == INTEGER_CST
+ && tree_int_cst_sgn (TREE_OPERAND (expr, 1)) >= 0
+ && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST)
+ {
+ /* If shift count is less than the width of the truncated type,
+ really shift. */
+ if (tree_int_cst_lt (TREE_OPERAND (expr, 1), TYPE_SIZE (type)))
+ /* In this case, shifting is like multiplication. */
+ goto trunc1;
+ else
+ {
+ /* If it is >= that width, result is zero.
+ Handling this with trunc1 would give the wrong result:
+ (int) ((long long) a << 32) is well defined (as 0)
+ but (int) a << 32 is undefined and would get a
+ warning. */
+
+ tree t = convert_to_integer (type, integer_zero_node);
+
+ /* If the original expression had side-effects, we must
+ preserve it. */
+ if (TREE_SIDE_EFFECTS (expr))
+ return build (COMPOUND_EXPR, type, expr, t);
+ else
+ return t;
+ }
+ }
+ break;
+
+ case MAX_EXPR:
+ case MIN_EXPR:
+ case MULT_EXPR:
+ {
+ tree arg0 = get_unwidened (TREE_OPERAND (expr, 0), type);
+ tree arg1 = get_unwidened (TREE_OPERAND (expr, 1), type);
+
+ /* Don't distribute unless the output precision is at least as big
+ as the actual inputs. Otherwise, the comparison of the
+ truncated values will be wrong. */
+ if (outprec >= TYPE_PRECISION (TREE_TYPE (arg0))
+ && outprec >= TYPE_PRECISION (TREE_TYPE (arg1))
+ /* If signedness of arg0 and arg1 don't match,
+ we can't necessarily find a type to compare them in. */
+ && (TREE_UNSIGNED (TREE_TYPE (arg0))
+ == TREE_UNSIGNED (TREE_TYPE (arg1))))
+ goto trunc1;
+ break;
+ }
+
+ case PLUS_EXPR:
+ case MINUS_EXPR:
+ case BIT_AND_EXPR:
+ case BIT_IOR_EXPR:
+ case BIT_XOR_EXPR:
+ case BIT_ANDTC_EXPR:
+ trunc1:
+ {
+ tree arg0 = get_unwidened (TREE_OPERAND (expr, 0), type);
+ tree arg1 = get_unwidened (TREE_OPERAND (expr, 1), type);
+
+ if (outprec >= BITS_PER_WORD
+ || TRULY_NOOP_TRUNCATION (outprec, inprec)
+ || inprec > TYPE_PRECISION (TREE_TYPE (arg0))
+ || inprec > TYPE_PRECISION (TREE_TYPE (arg1)))
+ {
+ /* Do the arithmetic in type TYPEX,
+ then convert result to TYPE. */
+ register tree typex = type;
+
+ /* Can't do arithmetic in enumeral types
+ so use an integer type that will hold the values. */
+ if (TREE_CODE (typex) == ENUMERAL_TYPE)
+ typex = type_for_size (TYPE_PRECISION (typex),
+ TREE_UNSIGNED (typex));
+
+ /* But now perhaps TYPEX is as wide as INPREC.
+ In that case, do nothing special here.
+ (Otherwise would recurse infinitely in convert. */
+ if (TYPE_PRECISION (typex) != inprec)
+ {
+ /* Don't do unsigned arithmetic where signed was wanted,
+ or vice versa.
+ Exception: if either of the original operands were
+ unsigned then can safely do the work as unsigned.
+ And we may need to do it as unsigned
+ if we truncate to the original size. */
+ typex = ((TREE_UNSIGNED (TREE_TYPE (expr))
+ || TREE_UNSIGNED (TREE_TYPE (arg0))
+ || TREE_UNSIGNED (TREE_TYPE (arg1)))
+ ? unsigned_type (typex) : signed_type (typex));
+ return convert (type,
+ fold (build (ex_form, typex,
+ convert (typex, arg0),
+ convert (typex, arg1),
+ 0)));
+ }
+ }
+ }
+ break;
+
+ case NEGATE_EXPR:
+ case BIT_NOT_EXPR:
+ /* This is not correct for ABS_EXPR,
+ since we must test the sign before truncation. */
+ {
+ register tree typex = type;
+
+ /* Can't do arithmetic in enumeral types
+ so use an integer type that will hold the values. */
+ if (TREE_CODE (typex) == ENUMERAL_TYPE)
+ typex = type_for_size (TYPE_PRECISION (typex),
+ TREE_UNSIGNED (typex));
+
+ /* But now perhaps TYPEX is as wide as INPREC.
+ In that case, do nothing special here.
+ (Otherwise would recurse infinitely in convert. */
+ if (TYPE_PRECISION (typex) != inprec)
+ {
+ /* Don't do unsigned arithmetic where signed was wanted,
+ or vice versa. */
+ typex = (TREE_UNSIGNED (TREE_TYPE (expr))
+ ? unsigned_type (typex) : signed_type (typex));
+ return convert (type,
+ fold (build1 (ex_form, typex,
+ convert (typex,
+ TREE_OPERAND (expr, 0)))));
+ }
+ }
+
+ case NOP_EXPR:
+ /* If truncating after truncating, might as well do all at once.
+ If truncating after extending, we may get rid of wasted work. */
+ return convert (type, get_unwidened (TREE_OPERAND (expr, 0), type));
+
+ case COND_EXPR:
+ /* Can treat the two alternative values like the operands
+ of an arithmetic expression. */
+ {
+ tree arg1 = get_unwidened (TREE_OPERAND (expr, 1), type);
+ tree arg2 = get_unwidened (TREE_OPERAND (expr, 2), type);
+
+ if (outprec >= BITS_PER_WORD
+ || TRULY_NOOP_TRUNCATION (outprec, inprec)
+ || inprec > TYPE_PRECISION (TREE_TYPE (arg1))
+ || inprec > TYPE_PRECISION (TREE_TYPE (arg2)))
+ {
+ /* Do the arithmetic in type TYPEX,
+ then convert result to TYPE. */
+ register tree typex = type;
+
+ /* Can't do arithmetic in enumeral types
+ so use an integer type that will hold the values. */
+ if (TREE_CODE (typex) == ENUMERAL_TYPE)
+ typex = type_for_size (TYPE_PRECISION (typex),
+ TREE_UNSIGNED (typex));
+
+ /* But now perhaps TYPEX is as wide as INPREC.
+ In that case, do nothing special here.
+ (Otherwise would recurse infinitely in convert. */
+ if (TYPE_PRECISION (typex) != inprec)
+ {
+ /* Don't do unsigned arithmetic where signed was wanted,
+ or vice versa. */
+ typex = (TREE_UNSIGNED (TREE_TYPE (expr))
+ ? unsigned_type (typex) : signed_type (typex));
+ return convert (type,
+ fold (build (COND_EXPR, typex,
+ TREE_OPERAND (expr, 0),
+ convert (typex, arg1),
+ convert (typex, arg2))));
+ }
+ else
+ /* It is sometimes worthwhile
+ to push the narrowing down through the conditional. */
+ return fold (build (COND_EXPR, type,
+ TREE_OPERAND (expr, 0),
+ convert (type, TREE_OPERAND (expr, 1)),
+ convert (type, TREE_OPERAND (expr, 2))));
+ }
+ }
+
+ }
+
+ return build1 (NOP_EXPR, type, expr);
+ }
+
+ if (form == REAL_TYPE)
+ return build1 (FIX_TRUNC_EXPR, type, expr);
+
+ if (form == COMPLEX_TYPE)
+ return convert (type, fold (build1 (REALPART_EXPR,
+ TREE_TYPE (TREE_TYPE (expr)), expr)));
+
+ error ("aggregate value used where an integer was expected");
+
+ {
+ register tree tem = build_int_2 (0, 0);
+ TREE_TYPE (tem) = type;
+ return tem;
+ }
+}
+
+/* Convert EXPR to the complex type TYPE in the usual ways. */
+
+tree
+convert_to_complex (type, expr)
+ tree type, expr;
+{
+ register enum tree_code form = TREE_CODE (TREE_TYPE (expr));
+ tree subtype = TREE_TYPE (type);
+
+ if (form == REAL_TYPE || form == INTEGER_TYPE || form == ENUMERAL_TYPE)
+ {
+ expr = convert (subtype, expr);
+ return build (COMPLEX_EXPR, type, expr,
+ convert (subtype, integer_zero_node));
+ }
+
+ if (form == COMPLEX_TYPE)
+ {
+ tree elt_type = TREE_TYPE (TREE_TYPE (expr));
+ if (TYPE_MAIN_VARIANT (elt_type) == TYPE_MAIN_VARIANT (subtype))
+ return expr;
+ else if (TREE_CODE (expr) == COMPLEX_EXPR)
+ return fold (build (COMPLEX_EXPR,
+ type,
+ convert (subtype, TREE_OPERAND (expr, 0)),
+ convert (subtype, TREE_OPERAND (expr, 1))));
+ else
+ {
+ expr = save_expr (expr);
+ return fold (build (COMPLEX_EXPR,
+ type,
+ convert (subtype,
+ fold (build1 (REALPART_EXPR,
+ TREE_TYPE (TREE_TYPE (expr)),
+ expr))),
+ convert (subtype,
+ fold (build1 (IMAGPART_EXPR,
+ TREE_TYPE (TREE_TYPE (expr)),
+ expr)))));
+ }
+ }
+
+ if (form == POINTER_TYPE)
+ error ("pointer value used where a complex was expected");
+ else
+ error ("aggregate value used where a complex was expected");
+
+ return build (COMPLEX_EXPR, type,
+ convert (subtype, integer_zero_node),
+ convert (subtype, integer_zero_node));
+}
diff --git a/gnu/usr.bin/cc/cc_int/cse.c b/gnu/usr.bin/cc/cc_int/cse.c
new file mode 100644
index 0000000..b4947d0
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/cse.c
@@ -0,0 +1,8546 @@
+/* Common subexpression elimination for GNU compiler.
+ Copyright (C) 1987, 88, 89, 92, 93, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+/* Must precede rtl.h for FFS. */
+#include <stdio.h>
+
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "flags.h"
+#include "real.h"
+#include "insn-config.h"
+#include "recog.h"
+
+#include <setjmp.h>
+
+/* The basic idea of common subexpression elimination is to go
+ through the code, keeping a record of expressions that would
+ have the same value at the current scan point, and replacing
+ expressions encountered with the cheapest equivalent expression.
+
+ It is too complicated to keep track of the different possibilities
+ when control paths merge; so, at each label, we forget all that is
+ known and start fresh. This can be described as processing each
+ basic block separately. Note, however, that these are not quite
+ the same as the basic blocks found by a later pass and used for
+ data flow analysis and register packing. We do not need to start fresh
+ after a conditional jump instruction if there is no label there.
+
+ We use two data structures to record the equivalent expressions:
+ a hash table for most expressions, and several vectors together
+ with "quantity numbers" to record equivalent (pseudo) registers.
+
+ The use of the special data structure for registers is desirable
+ because it is faster. It is possible because registers references
+ contain a fairly small number, the register number, taken from
+ a contiguously allocated series, and two register references are
+ identical if they have the same number. General expressions
+ do not have any such thing, so the only way to retrieve the
+ information recorded on an expression other than a register
+ is to keep it in a hash table.
+
+Registers and "quantity numbers":
+
+ At the start of each basic block, all of the (hardware and pseudo)
+ registers used in the function are given distinct quantity
+ numbers to indicate their contents. During scan, when the code
+ copies one register into another, we copy the quantity number.
+ When a register is loaded in any other way, we allocate a new
+ quantity number to describe the value generated by this operation.
+ `reg_qty' records what quantity a register is currently thought
+ of as containing.
+
+ All real quantity numbers are greater than or equal to `max_reg'.
+ If register N has not been assigned a quantity, reg_qty[N] will equal N.
+
+ Quantity numbers below `max_reg' do not exist and none of the `qty_...'
+ variables should be referenced with an index below `max_reg'.
+
+ We also maintain a bidirectional chain of registers for each
+ quantity number. `qty_first_reg', `qty_last_reg',
+ `reg_next_eqv' and `reg_prev_eqv' hold these chains.
+
+ The first register in a chain is the one whose lifespan is least local.
+ Among equals, it is the one that was seen first.
+ We replace any equivalent register with that one.
+
+ If two registers have the same quantity number, it must be true that
+ REG expressions with `qty_mode' must be in the hash table for both
+ registers and must be in the same class.
+
+ The converse is not true. Since hard registers may be referenced in
+ any mode, two REG expressions might be equivalent in the hash table
+ but not have the same quantity number if the quantity number of one
+ of the registers is not the same mode as those expressions.
+
+Constants and quantity numbers
+
+ When a quantity has a known constant value, that value is stored
+ in the appropriate element of qty_const. This is in addition to
+ putting the constant in the hash table as is usual for non-regs.
+
+ Whether a reg or a constant is preferred is determined by the configuration
+ macro CONST_COSTS and will often depend on the constant value. In any
+ event, expressions containing constants can be simplified, by fold_rtx.
+
+ When a quantity has a known nearly constant value (such as an address
+ of a stack slot), that value is stored in the appropriate element
+ of qty_const.
+
+ Integer constants don't have a machine mode. However, cse
+ determines the intended machine mode from the destination
+ of the instruction that moves the constant. The machine mode
+ is recorded in the hash table along with the actual RTL
+ constant expression so that different modes are kept separate.
+
+Other expressions:
+
+ To record known equivalences among expressions in general
+ we use a hash table called `table'. It has a fixed number of buckets
+ that contain chains of `struct table_elt' elements for expressions.
+ These chains connect the elements whose expressions have the same
+ hash codes.
+
+ Other chains through the same elements connect the elements which
+ currently have equivalent values.
+
+ Register references in an expression are canonicalized before hashing
+ the expression. This is done using `reg_qty' and `qty_first_reg'.
+ The hash code of a register reference is computed using the quantity
+ number, not the register number.
+
+ When the value of an expression changes, it is necessary to remove from the
+ hash table not just that expression but all expressions whose values
+ could be different as a result.
+
+ 1. If the value changing is in memory, except in special cases
+ ANYTHING referring to memory could be changed. That is because
+ nobody knows where a pointer does not point.
+ The function `invalidate_memory' removes what is necessary.
+
+ The special cases are when the address is constant or is
+ a constant plus a fixed register such as the frame pointer
+ or a static chain pointer. When such addresses are stored in,
+ we can tell exactly which other such addresses must be invalidated
+ due to overlap. `invalidate' does this.
+ All expressions that refer to non-constant
+ memory addresses are also invalidated. `invalidate_memory' does this.
+
+ 2. If the value changing is a register, all expressions
+ containing references to that register, and only those,
+ must be removed.
+
+ Because searching the entire hash table for expressions that contain
+ a register is very slow, we try to figure out when it isn't necessary.
+ Precisely, this is necessary only when expressions have been
+ entered in the hash table using this register, and then the value has
+ changed, and then another expression wants to be added to refer to
+ the register's new value. This sequence of circumstances is rare
+ within any one basic block.
+
+ The vectors `reg_tick' and `reg_in_table' are used to detect this case.
+ reg_tick[i] is incremented whenever a value is stored in register i.
+ reg_in_table[i] holds -1 if no references to register i have been
+ entered in the table; otherwise, it contains the value reg_tick[i] had
+ when the references were entered. If we want to enter a reference
+ and reg_in_table[i] != reg_tick[i], we must scan and remove old references.
+ Until we want to enter a new entry, the mere fact that the two vectors
+ don't match makes the entries be ignored if anyone tries to match them.
+
+ Registers themselves are entered in the hash table as well as in
+ the equivalent-register chains. However, the vectors `reg_tick'
+ and `reg_in_table' do not apply to expressions which are simple
+ register references. These expressions are removed from the table
+ immediately when they become invalid, and this can be done even if
+ we do not immediately search for all the expressions that refer to
+ the register.
+
+ A CLOBBER rtx in an instruction invalidates its operand for further
+ reuse. A CLOBBER or SET rtx whose operand is a MEM:BLK
+ invalidates everything that resides in memory.
+
+Related expressions:
+
+ Constant expressions that differ only by an additive integer
+ are called related. When a constant expression is put in
+ the table, the related expression with no constant term
+ is also entered. These are made to point at each other
+ so that it is possible to find out if there exists any
+ register equivalent to an expression related to a given expression. */
+
+/* One plus largest register number used in this function. */
+
+static int max_reg;
+
+/* Length of vectors indexed by quantity number.
+ We know in advance we will not need a quantity number this big. */
+
+static int max_qty;
+
+/* Next quantity number to be allocated.
+ This is 1 + the largest number needed so far. */
+
+static int next_qty;
+
+/* Indexed by quantity number, gives the first (or last) (pseudo) register
+ in the chain of registers that currently contain this quantity. */
+
+static int *qty_first_reg;
+static int *qty_last_reg;
+
+/* Index by quantity number, gives the mode of the quantity. */
+
+static enum machine_mode *qty_mode;
+
+/* Indexed by quantity number, gives the rtx of the constant value of the
+ quantity, or zero if it does not have a known value.
+ A sum of the frame pointer (or arg pointer) plus a constant
+ can also be entered here. */
+
+static rtx *qty_const;
+
+/* Indexed by qty number, gives the insn that stored the constant value
+ recorded in `qty_const'. */
+
+static rtx *qty_const_insn;
+
+/* The next three variables are used to track when a comparison between a
+ quantity and some constant or register has been passed. In that case, we
+ know the results of the comparison in case we see it again. These variables
+ record a comparison that is known to be true. */
+
+/* Indexed by qty number, gives the rtx code of a comparison with a known
+ result involving this quantity. If none, it is UNKNOWN. */
+static enum rtx_code *qty_comparison_code;
+
+/* Indexed by qty number, gives the constant being compared against in a
+ comparison of known result. If no such comparison, it is undefined.
+ If the comparison is not with a constant, it is zero. */
+
+static rtx *qty_comparison_const;
+
+/* Indexed by qty number, gives the quantity being compared against in a
+ comparison of known result. If no such comparison, if it undefined.
+ If the comparison is not with a register, it is -1. */
+
+static int *qty_comparison_qty;
+
+#ifdef HAVE_cc0
+/* For machines that have a CC0, we do not record its value in the hash
+ table since its use is guaranteed to be the insn immediately following
+ its definition and any other insn is presumed to invalidate it.
+
+ Instead, we store below the value last assigned to CC0. If it should
+ happen to be a constant, it is stored in preference to the actual
+ assigned value. In case it is a constant, we store the mode in which
+ the constant should be interpreted. */
+
+static rtx prev_insn_cc0;
+static enum machine_mode prev_insn_cc0_mode;
+#endif
+
+/* Previous actual insn. 0 if at first insn of basic block. */
+
+static rtx prev_insn;
+
+/* Insn being scanned. */
+
+static rtx this_insn;
+
+/* Index by (pseudo) register number, gives the quantity number
+ of the register's current contents. */
+
+static int *reg_qty;
+
+/* Index by (pseudo) register number, gives the number of the next (or
+ previous) (pseudo) register in the chain of registers sharing the same
+ value.
+
+ Or -1 if this register is at the end of the chain.
+
+ If reg_qty[N] == N, reg_next_eqv[N] is undefined. */
+
+static int *reg_next_eqv;
+static int *reg_prev_eqv;
+
+/* Index by (pseudo) register number, gives the number of times
+ that register has been altered in the current basic block. */
+
+static int *reg_tick;
+
+/* Index by (pseudo) register number, gives the reg_tick value at which
+ rtx's containing this register are valid in the hash table.
+ If this does not equal the current reg_tick value, such expressions
+ existing in the hash table are invalid.
+ If this is -1, no expressions containing this register have been
+ entered in the table. */
+
+static int *reg_in_table;
+
+/* A HARD_REG_SET containing all the hard registers for which there is
+ currently a REG expression in the hash table. Note the difference
+ from the above variables, which indicate if the REG is mentioned in some
+ expression in the table. */
+
+static HARD_REG_SET hard_regs_in_table;
+
+/* A HARD_REG_SET containing all the hard registers that are invalidated
+ by a CALL_INSN. */
+
+static HARD_REG_SET regs_invalidated_by_call;
+
+/* Two vectors of ints:
+ one containing max_reg -1's; the other max_reg + 500 (an approximation
+ for max_qty) elements where element i contains i.
+ These are used to initialize various other vectors fast. */
+
+static int *all_minus_one;
+static int *consec_ints;
+
+/* CUID of insn that starts the basic block currently being cse-processed. */
+
+static int cse_basic_block_start;
+
+/* CUID of insn that ends the basic block currently being cse-processed. */
+
+static int cse_basic_block_end;
+
+/* Vector mapping INSN_UIDs to cuids.
+ The cuids are like uids but increase monotonically always.
+ We use them to see whether a reg is used outside a given basic block. */
+
+static int *uid_cuid;
+
+/* Highest UID in UID_CUID. */
+static int max_uid;
+
+/* Get the cuid of an insn. */
+
+#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)])
+
+/* Nonzero if cse has altered conditional jump insns
+ in such a way that jump optimization should be redone. */
+
+static int cse_jumps_altered;
+
+/* canon_hash stores 1 in do_not_record
+ if it notices a reference to CC0, PC, or some other volatile
+ subexpression. */
+
+static int do_not_record;
+
+#ifdef LOAD_EXTEND_OP
+
+/* Scratch rtl used when looking for load-extended copy of a MEM. */
+static rtx memory_extend_rtx;
+#endif
+
+/* canon_hash stores 1 in hash_arg_in_memory
+ if it notices a reference to memory within the expression being hashed. */
+
+static int hash_arg_in_memory;
+
+/* canon_hash stores 1 in hash_arg_in_struct
+ if it notices a reference to memory that's part of a structure. */
+
+static int hash_arg_in_struct;
+
+/* The hash table contains buckets which are chains of `struct table_elt's,
+ each recording one expression's information.
+ That expression is in the `exp' field.
+
+ Those elements with the same hash code are chained in both directions
+ through the `next_same_hash' and `prev_same_hash' fields.
+
+ Each set of expressions with equivalent values
+ are on a two-way chain through the `next_same_value'
+ and `prev_same_value' fields, and all point with
+ the `first_same_value' field at the first element in
+ that chain. The chain is in order of increasing cost.
+ Each element's cost value is in its `cost' field.
+
+ The `in_memory' field is nonzero for elements that
+ involve any reference to memory. These elements are removed
+ whenever a write is done to an unidentified location in memory.
+ To be safe, we assume that a memory address is unidentified unless
+ the address is either a symbol constant or a constant plus
+ the frame pointer or argument pointer.
+
+ The `in_struct' field is nonzero for elements that
+ involve any reference to memory inside a structure or array.
+
+ The `related_value' field is used to connect related expressions
+ (that differ by adding an integer).
+ The related expressions are chained in a circular fashion.
+ `related_value' is zero for expressions for which this
+ chain is not useful.
+
+ The `cost' field stores the cost of this element's expression.
+
+ The `is_const' flag is set if the element is a constant (including
+ a fixed address).
+
+ The `flag' field is used as a temporary during some search routines.
+
+ The `mode' field is usually the same as GET_MODE (`exp'), but
+ if `exp' is a CONST_INT and has no machine mode then the `mode'
+ field is the mode it was being used as. Each constant is
+ recorded separately for each mode it is used with. */
+
+
+struct table_elt
+{
+ rtx exp;
+ struct table_elt *next_same_hash;
+ struct table_elt *prev_same_hash;
+ struct table_elt *next_same_value;
+ struct table_elt *prev_same_value;
+ struct table_elt *first_same_value;
+ struct table_elt *related_value;
+ int cost;
+ enum machine_mode mode;
+ char in_memory;
+ char in_struct;
+ char is_const;
+ char flag;
+};
+
+/* We don't want a lot of buckets, because we rarely have very many
+ things stored in the hash table, and a lot of buckets slows
+ down a lot of loops that happen frequently. */
+#define NBUCKETS 31
+
+/* Compute hash code of X in mode M. Special-case case where X is a pseudo
+ register (hard registers may require `do_not_record' to be set). */
+
+#define HASH(X, M) \
+ (GET_CODE (X) == REG && REGNO (X) >= FIRST_PSEUDO_REGISTER \
+ ? (((unsigned) REG << 7) + (unsigned) reg_qty[REGNO (X)]) % NBUCKETS \
+ : canon_hash (X, M) % NBUCKETS)
+
+/* Determine whether register number N is considered a fixed register for CSE.
+ It is desirable to replace other regs with fixed regs, to reduce need for
+ non-fixed hard regs.
+ A reg wins if it is either the frame pointer or designated as fixed,
+ but not if it is an overlapping register. */
+#ifdef OVERLAPPING_REGNO_P
+#define FIXED_REGNO_P(N) \
+ (((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM \
+ || fixed_regs[N] || global_regs[N]) \
+ && ! OVERLAPPING_REGNO_P ((N)))
+#else
+#define FIXED_REGNO_P(N) \
+ ((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM \
+ || fixed_regs[N] || global_regs[N])
+#endif
+
+/* Compute cost of X, as stored in the `cost' field of a table_elt. Fixed
+ hard registers and pointers into the frame are the cheapest with a cost
+ of 0. Next come pseudos with a cost of one and other hard registers with
+ a cost of 2. Aside from these special cases, call `rtx_cost'. */
+
+#define CHEAP_REGNO(N) \
+ ((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM \
+ || (N) == STACK_POINTER_REGNUM || (N) == ARG_POINTER_REGNUM \
+ || ((N) >= FIRST_VIRTUAL_REGISTER && (N) <= LAST_VIRTUAL_REGISTER) \
+ || ((N) < FIRST_PSEUDO_REGISTER \
+ && FIXED_REGNO_P (N) && REGNO_REG_CLASS (N) != NO_REGS))
+
+/* A register is cheap if it is a user variable assigned to the register
+ or if its register number always corresponds to a cheap register. */
+
+#define CHEAP_REG(N) \
+ ((REG_USERVAR_P (N) && REGNO (N) < FIRST_PSEUDO_REGISTER) \
+ || CHEAP_REGNO (REGNO (N)))
+
+#define COST(X) \
+ (GET_CODE (X) == REG \
+ ? (CHEAP_REG (X) ? 0 \
+ : REGNO (X) >= FIRST_PSEUDO_REGISTER ? 1 \
+ : 2) \
+ : rtx_cost (X, SET) * 2)
+
+/* Determine if the quantity number for register X represents a valid index
+ into the `qty_...' variables. */
+
+#define REGNO_QTY_VALID_P(N) (reg_qty[N] != (N))
+
+static struct table_elt *table[NBUCKETS];
+
+/* Chain of `struct table_elt's made so far for this function
+ but currently removed from the table. */
+
+static struct table_elt *free_element_chain;
+
+/* Number of `struct table_elt' structures made so far for this function. */
+
+static int n_elements_made;
+
+/* Maximum value `n_elements_made' has had so far in this compilation
+ for functions previously processed. */
+
+static int max_elements_made;
+
+/* Surviving equivalence class when two equivalence classes are merged
+ by recording the effects of a jump in the last insn. Zero if the
+ last insn was not a conditional jump. */
+
+static struct table_elt *last_jump_equiv_class;
+
+/* Set to the cost of a constant pool reference if one was found for a
+ symbolic constant. If this was found, it means we should try to
+ convert constants into constant pool entries if they don't fit in
+ the insn. */
+
+static int constant_pool_entries_cost;
+
+/* Bits describing what kind of values in memory must be invalidated
+ for a particular instruction. If all three bits are zero,
+ no memory refs need to be invalidated. Each bit is more powerful
+ than the preceding ones, and if a bit is set then the preceding
+ bits are also set.
+
+ Here is how the bits are set:
+ Pushing onto the stack invalidates only the stack pointer,
+ writing at a fixed address invalidates only variable addresses,
+ writing in a structure element at variable address
+ invalidates all but scalar variables,
+ and writing in anything else at variable address invalidates everything. */
+
+struct write_data
+{
+ int sp : 1; /* Invalidate stack pointer. */
+ int var : 1; /* Invalidate variable addresses. */
+ int nonscalar : 1; /* Invalidate all but scalar variables. */
+ int all : 1; /* Invalidate all memory refs. */
+};
+
+/* Define maximum length of a branch path. */
+
+#define PATHLENGTH 10
+
+/* This data describes a block that will be processed by cse_basic_block. */
+
+struct cse_basic_block_data {
+ /* Lowest CUID value of insns in block. */
+ int low_cuid;
+ /* Highest CUID value of insns in block. */
+ int high_cuid;
+ /* Total number of SETs in block. */
+ int nsets;
+ /* Last insn in the block. */
+ rtx last;
+ /* Size of current branch path, if any. */
+ int path_size;
+ /* Current branch path, indicating which branches will be taken. */
+ struct branch_path {
+ /* The branch insn. */
+ rtx branch;
+ /* Whether it should be taken or not. AROUND is the same as taken
+ except that it is used when the destination label is not preceded
+ by a BARRIER. */
+ enum taken {TAKEN, NOT_TAKEN, AROUND} status;
+ } path[PATHLENGTH];
+};
+
+/* Nonzero if X has the form (PLUS frame-pointer integer). We check for
+ virtual regs here because the simplify_*_operation routines are called
+ by integrate.c, which is called before virtual register instantiation. */
+
+#define FIXED_BASE_PLUS_P(X) \
+ ((X) == frame_pointer_rtx || (X) == hard_frame_pointer_rtx \
+ || (X) == arg_pointer_rtx \
+ || (X) == virtual_stack_vars_rtx \
+ || (X) == virtual_incoming_args_rtx \
+ || (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \
+ && (XEXP (X, 0) == frame_pointer_rtx \
+ || XEXP (X, 0) == hard_frame_pointer_rtx \
+ || XEXP (X, 0) == arg_pointer_rtx \
+ || XEXP (X, 0) == virtual_stack_vars_rtx \
+ || XEXP (X, 0) == virtual_incoming_args_rtx)))
+
+/* Similar, but also allows reference to the stack pointer.
+
+ This used to include FIXED_BASE_PLUS_P, however, we can't assume that
+ arg_pointer_rtx by itself is nonzero, because on at least one machine,
+ the i960, the arg pointer is zero when it is unused. */
+
+#define NONZERO_BASE_PLUS_P(X) \
+ ((X) == frame_pointer_rtx || (X) == hard_frame_pointer_rtx \
+ || (X) == virtual_stack_vars_rtx \
+ || (X) == virtual_incoming_args_rtx \
+ || (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \
+ && (XEXP (X, 0) == frame_pointer_rtx \
+ || XEXP (X, 0) == hard_frame_pointer_rtx \
+ || XEXP (X, 0) == arg_pointer_rtx \
+ || XEXP (X, 0) == virtual_stack_vars_rtx \
+ || XEXP (X, 0) == virtual_incoming_args_rtx)) \
+ || (X) == stack_pointer_rtx \
+ || (X) == virtual_stack_dynamic_rtx \
+ || (X) == virtual_outgoing_args_rtx \
+ || (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \
+ && (XEXP (X, 0) == stack_pointer_rtx \
+ || XEXP (X, 0) == virtual_stack_dynamic_rtx \
+ || XEXP (X, 0) == virtual_outgoing_args_rtx)))
+
+static void new_basic_block PROTO((void));
+static void make_new_qty PROTO((int));
+static void make_regs_eqv PROTO((int, int));
+static void delete_reg_equiv PROTO((int));
+static int mention_regs PROTO((rtx));
+static int insert_regs PROTO((rtx, struct table_elt *, int));
+static void free_element PROTO((struct table_elt *));
+static void remove_from_table PROTO((struct table_elt *, unsigned));
+static struct table_elt *get_element PROTO((void));
+static struct table_elt *lookup PROTO((rtx, unsigned, enum machine_mode)),
+ *lookup_for_remove PROTO((rtx, unsigned, enum machine_mode));
+static rtx lookup_as_function PROTO((rtx, enum rtx_code));
+static struct table_elt *insert PROTO((rtx, struct table_elt *, unsigned,
+ enum machine_mode));
+static void merge_equiv_classes PROTO((struct table_elt *,
+ struct table_elt *));
+static void invalidate PROTO((rtx));
+static void remove_invalid_refs PROTO((int));
+static void rehash_using_reg PROTO((rtx));
+static void invalidate_memory PROTO((struct write_data *));
+static void invalidate_for_call PROTO((void));
+static rtx use_related_value PROTO((rtx, struct table_elt *));
+static unsigned canon_hash PROTO((rtx, enum machine_mode));
+static unsigned safe_hash PROTO((rtx, enum machine_mode));
+static int exp_equiv_p PROTO((rtx, rtx, int, int));
+static void set_nonvarying_address_components PROTO((rtx, int, rtx *,
+ HOST_WIDE_INT *,
+ HOST_WIDE_INT *));
+static int refers_to_p PROTO((rtx, rtx));
+static int refers_to_mem_p PROTO((rtx, rtx, HOST_WIDE_INT,
+ HOST_WIDE_INT));
+static int cse_rtx_addr_varies_p PROTO((rtx));
+static rtx canon_reg PROTO((rtx, rtx));
+static void find_best_addr PROTO((rtx, rtx *));
+static enum rtx_code find_comparison_args PROTO((enum rtx_code, rtx *, rtx *,
+ enum machine_mode *,
+ enum machine_mode *));
+static rtx cse_gen_binary PROTO((enum rtx_code, enum machine_mode,
+ rtx, rtx));
+static rtx simplify_plus_minus PROTO((enum rtx_code, enum machine_mode,
+ rtx, rtx));
+static rtx fold_rtx PROTO((rtx, rtx));
+static rtx equiv_constant PROTO((rtx));
+static void record_jump_equiv PROTO((rtx, int));
+static void record_jump_cond PROTO((enum rtx_code, enum machine_mode,
+ rtx, rtx, int));
+static void cse_insn PROTO((rtx, int));
+static void note_mem_written PROTO((rtx, struct write_data *));
+static void invalidate_from_clobbers PROTO((struct write_data *, rtx));
+static rtx cse_process_notes PROTO((rtx, rtx));
+static void cse_around_loop PROTO((rtx));
+static void invalidate_skipped_set PROTO((rtx, rtx));
+static void invalidate_skipped_block PROTO((rtx));
+static void cse_check_loop_start PROTO((rtx, rtx));
+static void cse_set_around_loop PROTO((rtx, rtx, rtx));
+static rtx cse_basic_block PROTO((rtx, rtx, struct branch_path *, int));
+static void count_reg_usage PROTO((rtx, int *, rtx, int));
+
+extern int rtx_equal_function_value_matters;
+
+/* Return an estimate of the cost of computing rtx X.
+ One use is in cse, to decide which expression to keep in the hash table.
+ Another is in rtl generation, to pick the cheapest way to multiply.
+ Other uses like the latter are expected in the future. */
+
+/* Return the right cost to give to an operation
+ to make the cost of the corresponding register-to-register instruction
+ N times that of a fast register-to-register instruction. */
+
+#define COSTS_N_INSNS(N) ((N) * 4 - 2)
+
+int
+rtx_cost (x, outer_code)
+ rtx x;
+ enum rtx_code outer_code;
+{
+ register int i, j;
+ register enum rtx_code code;
+ register char *fmt;
+ register int total;
+
+ if (x == 0)
+ return 0;
+
+ /* Compute the default costs of certain things.
+ Note that RTX_COSTS can override the defaults. */
+
+ code = GET_CODE (x);
+ switch (code)
+ {
+ case MULT:
+ /* Count multiplication by 2**n as a shift,
+ because if we are considering it, we would output it as a shift. */
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && exact_log2 (INTVAL (XEXP (x, 1))) >= 0)
+ total = 2;
+ else
+ total = COSTS_N_INSNS (5);
+ break;
+ case DIV:
+ case UDIV:
+ case MOD:
+ case UMOD:
+ total = COSTS_N_INSNS (7);
+ break;
+ case USE:
+ /* Used in loop.c and combine.c as a marker. */
+ total = 0;
+ break;
+ case ASM_OPERANDS:
+ /* We don't want these to be used in substitutions because
+ we have no way of validating the resulting insn. So assign
+ anything containing an ASM_OPERANDS a very high cost. */
+ total = 1000;
+ break;
+ default:
+ total = 2;
+ }
+
+ switch (code)
+ {
+ case REG:
+ return ! CHEAP_REG (x);
+
+ case SUBREG:
+ /* If we can't tie these modes, make this expensive. The larger
+ the mode, the more expensive it is. */
+ if (! MODES_TIEABLE_P (GET_MODE (x), GET_MODE (SUBREG_REG (x))))
+ return COSTS_N_INSNS (2
+ + GET_MODE_SIZE (GET_MODE (x)) / UNITS_PER_WORD);
+ return 2;
+#ifdef RTX_COSTS
+ RTX_COSTS (x, code, outer_code);
+#endif
+ CONST_COSTS (x, code, outer_code);
+ }
+
+ /* Sum the costs of the sub-rtx's, plus cost of this operation,
+ which is already in total. */
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ if (fmt[i] == 'e')
+ total += rtx_cost (XEXP (x, i), code);
+ else if (fmt[i] == 'E')
+ for (j = 0; j < XVECLEN (x, i); j++)
+ total += rtx_cost (XVECEXP (x, i, j), code);
+
+ return total;
+}
+
+/* Clear the hash table and initialize each register with its own quantity,
+ for a new basic block. */
+
+static void
+new_basic_block ()
+{
+ register int i;
+
+ next_qty = max_reg;
+
+ bzero ((char *) reg_tick, max_reg * sizeof (int));
+
+ bcopy ((char *) all_minus_one, (char *) reg_in_table,
+ max_reg * sizeof (int));
+ bcopy ((char *) consec_ints, (char *) reg_qty, max_reg * sizeof (int));
+ CLEAR_HARD_REG_SET (hard_regs_in_table);
+
+ /* The per-quantity values used to be initialized here, but it is
+ much faster to initialize each as it is made in `make_new_qty'. */
+
+ for (i = 0; i < NBUCKETS; i++)
+ {
+ register struct table_elt *this, *next;
+ for (this = table[i]; this; this = next)
+ {
+ next = this->next_same_hash;
+ free_element (this);
+ }
+ }
+
+ bzero ((char *) table, sizeof table);
+
+ prev_insn = 0;
+
+#ifdef HAVE_cc0
+ prev_insn_cc0 = 0;
+#endif
+}
+
+/* Say that register REG contains a quantity not in any register before
+ and initialize that quantity. */
+
+static void
+make_new_qty (reg)
+ register int reg;
+{
+ register int q;
+
+ if (next_qty >= max_qty)
+ abort ();
+
+ q = reg_qty[reg] = next_qty++;
+ qty_first_reg[q] = reg;
+ qty_last_reg[q] = reg;
+ qty_const[q] = qty_const_insn[q] = 0;
+ qty_comparison_code[q] = UNKNOWN;
+
+ reg_next_eqv[reg] = reg_prev_eqv[reg] = -1;
+}
+
+/* Make reg NEW equivalent to reg OLD.
+ OLD is not changing; NEW is. */
+
+static void
+make_regs_eqv (new, old)
+ register int new, old;
+{
+ register int lastr, firstr;
+ register int q = reg_qty[old];
+
+ /* Nothing should become eqv until it has a "non-invalid" qty number. */
+ if (! REGNO_QTY_VALID_P (old))
+ abort ();
+
+ reg_qty[new] = q;
+ firstr = qty_first_reg[q];
+ lastr = qty_last_reg[q];
+
+ /* Prefer fixed hard registers to anything. Prefer pseudo regs to other
+ hard regs. Among pseudos, if NEW will live longer than any other reg
+ of the same qty, and that is beyond the current basic block,
+ make it the new canonical replacement for this qty. */
+ if (! (firstr < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (firstr))
+ /* Certain fixed registers might be of the class NO_REGS. This means
+ that not only can they not be allocated by the compiler, but
+ they cannot be used in substitutions or canonicalizations
+ either. */
+ && (new >= FIRST_PSEUDO_REGISTER || REGNO_REG_CLASS (new) != NO_REGS)
+ && ((new < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (new))
+ || (new >= FIRST_PSEUDO_REGISTER
+ && (firstr < FIRST_PSEUDO_REGISTER
+ || ((uid_cuid[regno_last_uid[new]] > cse_basic_block_end
+ || (uid_cuid[regno_first_uid[new]]
+ < cse_basic_block_start))
+ && (uid_cuid[regno_last_uid[new]]
+ > uid_cuid[regno_last_uid[firstr]]))))))
+ {
+ reg_prev_eqv[firstr] = new;
+ reg_next_eqv[new] = firstr;
+ reg_prev_eqv[new] = -1;
+ qty_first_reg[q] = new;
+ }
+ else
+ {
+ /* If NEW is a hard reg (known to be non-fixed), insert at end.
+ Otherwise, insert before any non-fixed hard regs that are at the
+ end. Registers of class NO_REGS cannot be used as an
+ equivalent for anything. */
+ while (lastr < FIRST_PSEUDO_REGISTER && reg_prev_eqv[lastr] >= 0
+ && (REGNO_REG_CLASS (lastr) == NO_REGS || ! FIXED_REGNO_P (lastr))
+ && new >= FIRST_PSEUDO_REGISTER)
+ lastr = reg_prev_eqv[lastr];
+ reg_next_eqv[new] = reg_next_eqv[lastr];
+ if (reg_next_eqv[lastr] >= 0)
+ reg_prev_eqv[reg_next_eqv[lastr]] = new;
+ else
+ qty_last_reg[q] = new;
+ reg_next_eqv[lastr] = new;
+ reg_prev_eqv[new] = lastr;
+ }
+}
+
+/* Remove REG from its equivalence class. */
+
+static void
+delete_reg_equiv (reg)
+ register int reg;
+{
+ register int q = reg_qty[reg];
+ register int p, n;
+
+ /* If invalid, do nothing. */
+ if (q == reg)
+ return;
+
+ p = reg_prev_eqv[reg];
+ n = reg_next_eqv[reg];
+
+ if (n != -1)
+ reg_prev_eqv[n] = p;
+ else
+ qty_last_reg[q] = p;
+ if (p != -1)
+ reg_next_eqv[p] = n;
+ else
+ qty_first_reg[q] = n;
+
+ reg_qty[reg] = reg;
+}
+
+/* Remove any invalid expressions from the hash table
+ that refer to any of the registers contained in expression X.
+
+ Make sure that newly inserted references to those registers
+ as subexpressions will be considered valid.
+
+ mention_regs is not called when a register itself
+ is being stored in the table.
+
+ Return 1 if we have done something that may have changed the hash code
+ of X. */
+
+static int
+mention_regs (x)
+ rtx x;
+{
+ register enum rtx_code code;
+ register int i, j;
+ register char *fmt;
+ register int changed = 0;
+
+ if (x == 0)
+ return 0;
+
+ code = GET_CODE (x);
+ if (code == REG)
+ {
+ register int regno = REGNO (x);
+ register int endregno
+ = regno + (regno >= FIRST_PSEUDO_REGISTER ? 1
+ : HARD_REGNO_NREGS (regno, GET_MODE (x)));
+ int i;
+
+ for (i = regno; i < endregno; i++)
+ {
+ if (reg_in_table[i] >= 0 && reg_in_table[i] != reg_tick[i])
+ remove_invalid_refs (i);
+
+ reg_in_table[i] = reg_tick[i];
+ }
+
+ return 0;
+ }
+
+ /* If X is a comparison or a COMPARE and either operand is a register
+ that does not have a quantity, give it one. This is so that a later
+ call to record_jump_equiv won't cause X to be assigned a different
+ hash code and not found in the table after that call.
+
+ It is not necessary to do this here, since rehash_using_reg can
+ fix up the table later, but doing this here eliminates the need to
+ call that expensive function in the most common case where the only
+ use of the register is in the comparison. */
+
+ if (code == COMPARE || GET_RTX_CLASS (code) == '<')
+ {
+ if (GET_CODE (XEXP (x, 0)) == REG
+ && ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 0))))
+ if (insert_regs (XEXP (x, 0), NULL_PTR, 0))
+ {
+ rehash_using_reg (XEXP (x, 0));
+ changed = 1;
+ }
+
+ if (GET_CODE (XEXP (x, 1)) == REG
+ && ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 1))))
+ if (insert_regs (XEXP (x, 1), NULL_PTR, 0))
+ {
+ rehash_using_reg (XEXP (x, 1));
+ changed = 1;
+ }
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ if (fmt[i] == 'e')
+ changed |= mention_regs (XEXP (x, i));
+ else if (fmt[i] == 'E')
+ for (j = 0; j < XVECLEN (x, i); j++)
+ changed |= mention_regs (XVECEXP (x, i, j));
+
+ return changed;
+}
+
+/* Update the register quantities for inserting X into the hash table
+ with a value equivalent to CLASSP.
+ (If the class does not contain a REG, it is irrelevant.)
+ If MODIFIED is nonzero, X is a destination; it is being modified.
+ Note that delete_reg_equiv should be called on a register
+ before insert_regs is done on that register with MODIFIED != 0.
+
+ Nonzero value means that elements of reg_qty have changed
+ so X's hash code may be different. */
+
+static int
+insert_regs (x, classp, modified)
+ rtx x;
+ struct table_elt *classp;
+ int modified;
+{
+ if (GET_CODE (x) == REG)
+ {
+ register int regno = REGNO (x);
+
+ /* If REGNO is in the equivalence table already but is of the
+ wrong mode for that equivalence, don't do anything here. */
+
+ if (REGNO_QTY_VALID_P (regno)
+ && qty_mode[reg_qty[regno]] != GET_MODE (x))
+ return 0;
+
+ if (modified || ! REGNO_QTY_VALID_P (regno))
+ {
+ if (classp)
+ for (classp = classp->first_same_value;
+ classp != 0;
+ classp = classp->next_same_value)
+ if (GET_CODE (classp->exp) == REG
+ && GET_MODE (classp->exp) == GET_MODE (x))
+ {
+ make_regs_eqv (regno, REGNO (classp->exp));
+ return 1;
+ }
+
+ make_new_qty (regno);
+ qty_mode[reg_qty[regno]] = GET_MODE (x);
+ return 1;
+ }
+
+ return 0;
+ }
+
+ /* If X is a SUBREG, we will likely be inserting the inner register in the
+ table. If that register doesn't have an assigned quantity number at
+ this point but does later, the insertion that we will be doing now will
+ not be accessible because its hash code will have changed. So assign
+ a quantity number now. */
+
+ else if (GET_CODE (x) == SUBREG && GET_CODE (SUBREG_REG (x)) == REG
+ && ! REGNO_QTY_VALID_P (REGNO (SUBREG_REG (x))))
+ {
+ insert_regs (SUBREG_REG (x), NULL_PTR, 0);
+ mention_regs (SUBREG_REG (x));
+ return 1;
+ }
+ else
+ return mention_regs (x);
+}
+
+/* Look in or update the hash table. */
+
+/* Put the element ELT on the list of free elements. */
+
+static void
+free_element (elt)
+ struct table_elt *elt;
+{
+ elt->next_same_hash = free_element_chain;
+ free_element_chain = elt;
+}
+
+/* Return an element that is free for use. */
+
+static struct table_elt *
+get_element ()
+{
+ struct table_elt *elt = free_element_chain;
+ if (elt)
+ {
+ free_element_chain = elt->next_same_hash;
+ return elt;
+ }
+ n_elements_made++;
+ return (struct table_elt *) oballoc (sizeof (struct table_elt));
+}
+
+/* Remove table element ELT from use in the table.
+ HASH is its hash code, made using the HASH macro.
+ It's an argument because often that is known in advance
+ and we save much time not recomputing it. */
+
+static void
+remove_from_table (elt, hash)
+ register struct table_elt *elt;
+ unsigned hash;
+{
+ if (elt == 0)
+ return;
+
+ /* Mark this element as removed. See cse_insn. */
+ elt->first_same_value = 0;
+
+ /* Remove the table element from its equivalence class. */
+
+ {
+ register struct table_elt *prev = elt->prev_same_value;
+ register struct table_elt *next = elt->next_same_value;
+
+ if (next) next->prev_same_value = prev;
+
+ if (prev)
+ prev->next_same_value = next;
+ else
+ {
+ register struct table_elt *newfirst = next;
+ while (next)
+ {
+ next->first_same_value = newfirst;
+ next = next->next_same_value;
+ }
+ }
+ }
+
+ /* Remove the table element from its hash bucket. */
+
+ {
+ register struct table_elt *prev = elt->prev_same_hash;
+ register struct table_elt *next = elt->next_same_hash;
+
+ if (next) next->prev_same_hash = prev;
+
+ if (prev)
+ prev->next_same_hash = next;
+ else if (table[hash] == elt)
+ table[hash] = next;
+ else
+ {
+ /* This entry is not in the proper hash bucket. This can happen
+ when two classes were merged by `merge_equiv_classes'. Search
+ for the hash bucket that it heads. This happens only very
+ rarely, so the cost is acceptable. */
+ for (hash = 0; hash < NBUCKETS; hash++)
+ if (table[hash] == elt)
+ table[hash] = next;
+ }
+ }
+
+ /* Remove the table element from its related-value circular chain. */
+
+ if (elt->related_value != 0 && elt->related_value != elt)
+ {
+ register struct table_elt *p = elt->related_value;
+ while (p->related_value != elt)
+ p = p->related_value;
+ p->related_value = elt->related_value;
+ if (p->related_value == p)
+ p->related_value = 0;
+ }
+
+ free_element (elt);
+}
+
+/* Look up X in the hash table and return its table element,
+ or 0 if X is not in the table.
+
+ MODE is the machine-mode of X, or if X is an integer constant
+ with VOIDmode then MODE is the mode with which X will be used.
+
+ Here we are satisfied to find an expression whose tree structure
+ looks like X. */
+
+static struct table_elt *
+lookup (x, hash, mode)
+ rtx x;
+ unsigned hash;
+ enum machine_mode mode;
+{
+ register struct table_elt *p;
+
+ for (p = table[hash]; p; p = p->next_same_hash)
+ if (mode == p->mode && ((x == p->exp && GET_CODE (x) == REG)
+ || exp_equiv_p (x, p->exp, GET_CODE (x) != REG, 0)))
+ return p;
+
+ return 0;
+}
+
+/* Like `lookup' but don't care whether the table element uses invalid regs.
+ Also ignore discrepancies in the machine mode of a register. */
+
+static struct table_elt *
+lookup_for_remove (x, hash, mode)
+ rtx x;
+ unsigned hash;
+ enum machine_mode mode;
+{
+ register struct table_elt *p;
+
+ if (GET_CODE (x) == REG)
+ {
+ int regno = REGNO (x);
+ /* Don't check the machine mode when comparing registers;
+ invalidating (REG:SI 0) also invalidates (REG:DF 0). */
+ for (p = table[hash]; p; p = p->next_same_hash)
+ if (GET_CODE (p->exp) == REG
+ && REGNO (p->exp) == regno)
+ return p;
+ }
+ else
+ {
+ for (p = table[hash]; p; p = p->next_same_hash)
+ if (mode == p->mode && (x == p->exp || exp_equiv_p (x, p->exp, 0, 0)))
+ return p;
+ }
+
+ return 0;
+}
+
+/* Look for an expression equivalent to X and with code CODE.
+ If one is found, return that expression. */
+
+static rtx
+lookup_as_function (x, code)
+ rtx x;
+ enum rtx_code code;
+{
+ register struct table_elt *p = lookup (x, safe_hash (x, VOIDmode) % NBUCKETS,
+ GET_MODE (x));
+ if (p == 0)
+ return 0;
+
+ for (p = p->first_same_value; p; p = p->next_same_value)
+ {
+ if (GET_CODE (p->exp) == code
+ /* Make sure this is a valid entry in the table. */
+ && exp_equiv_p (p->exp, p->exp, 1, 0))
+ return p->exp;
+ }
+
+ return 0;
+}
+
+/* Insert X in the hash table, assuming HASH is its hash code
+ and CLASSP is an element of the class it should go in
+ (or 0 if a new class should be made).
+ It is inserted at the proper position to keep the class in
+ the order cheapest first.
+
+ MODE is the machine-mode of X, or if X is an integer constant
+ with VOIDmode then MODE is the mode with which X will be used.
+
+ For elements of equal cheapness, the most recent one
+ goes in front, except that the first element in the list
+ remains first unless a cheaper element is added. The order of
+ pseudo-registers does not matter, as canon_reg will be called to
+ find the cheapest when a register is retrieved from the table.
+
+ The in_memory field in the hash table element is set to 0.
+ The caller must set it nonzero if appropriate.
+
+ You should call insert_regs (X, CLASSP, MODIFY) before calling here,
+ and if insert_regs returns a nonzero value
+ you must then recompute its hash code before calling here.
+
+ If necessary, update table showing constant values of quantities. */
+
+#define CHEAPER(X,Y) ((X)->cost < (Y)->cost)
+
+static struct table_elt *
+insert (x, classp, hash, mode)
+ register rtx x;
+ register struct table_elt *classp;
+ unsigned hash;
+ enum machine_mode mode;
+{
+ register struct table_elt *elt;
+
+ /* If X is a register and we haven't made a quantity for it,
+ something is wrong. */
+ if (GET_CODE (x) == REG && ! REGNO_QTY_VALID_P (REGNO (x)))
+ abort ();
+
+ /* If X is a hard register, show it is being put in the table. */
+ if (GET_CODE (x) == REG && REGNO (x) < FIRST_PSEUDO_REGISTER)
+ {
+ int regno = REGNO (x);
+ int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
+ int i;
+
+ for (i = regno; i < endregno; i++)
+ SET_HARD_REG_BIT (hard_regs_in_table, i);
+ }
+
+
+ /* Put an element for X into the right hash bucket. */
+
+ elt = get_element ();
+ elt->exp = x;
+ elt->cost = COST (x);
+ elt->next_same_value = 0;
+ elt->prev_same_value = 0;
+ elt->next_same_hash = table[hash];
+ elt->prev_same_hash = 0;
+ elt->related_value = 0;
+ elt->in_memory = 0;
+ elt->mode = mode;
+ elt->is_const = (CONSTANT_P (x)
+ /* GNU C++ takes advantage of this for `this'
+ (and other const values). */
+ || (RTX_UNCHANGING_P (x)
+ && GET_CODE (x) == REG
+ && REGNO (x) >= FIRST_PSEUDO_REGISTER)
+ || FIXED_BASE_PLUS_P (x));
+
+ if (table[hash])
+ table[hash]->prev_same_hash = elt;
+ table[hash] = elt;
+
+ /* Put it into the proper value-class. */
+ if (classp)
+ {
+ classp = classp->first_same_value;
+ if (CHEAPER (elt, classp))
+ /* Insert at the head of the class */
+ {
+ register struct table_elt *p;
+ elt->next_same_value = classp;
+ classp->prev_same_value = elt;
+ elt->first_same_value = elt;
+
+ for (p = classp; p; p = p->next_same_value)
+ p->first_same_value = elt;
+ }
+ else
+ {
+ /* Insert not at head of the class. */
+ /* Put it after the last element cheaper than X. */
+ register struct table_elt *p, *next;
+ for (p = classp; (next = p->next_same_value) && CHEAPER (next, elt);
+ p = next);
+ /* Put it after P and before NEXT. */
+ elt->next_same_value = next;
+ if (next)
+ next->prev_same_value = elt;
+ elt->prev_same_value = p;
+ p->next_same_value = elt;
+ elt->first_same_value = classp;
+ }
+ }
+ else
+ elt->first_same_value = elt;
+
+ /* If this is a constant being set equivalent to a register or a register
+ being set equivalent to a constant, note the constant equivalence.
+
+ If this is a constant, it cannot be equivalent to a different constant,
+ and a constant is the only thing that can be cheaper than a register. So
+ we know the register is the head of the class (before the constant was
+ inserted).
+
+ If this is a register that is not already known equivalent to a
+ constant, we must check the entire class.
+
+ If this is a register that is already known equivalent to an insn,
+ update `qty_const_insn' to show that `this_insn' is the latest
+ insn making that quantity equivalent to the constant. */
+
+ if (elt->is_const && classp && GET_CODE (classp->exp) == REG)
+ {
+ qty_const[reg_qty[REGNO (classp->exp)]]
+ = gen_lowpart_if_possible (qty_mode[reg_qty[REGNO (classp->exp)]], x);
+ qty_const_insn[reg_qty[REGNO (classp->exp)]] = this_insn;
+ }
+
+ else if (GET_CODE (x) == REG && classp && ! qty_const[reg_qty[REGNO (x)]])
+ {
+ register struct table_elt *p;
+
+ for (p = classp; p != 0; p = p->next_same_value)
+ {
+ if (p->is_const)
+ {
+ qty_const[reg_qty[REGNO (x)]]
+ = gen_lowpart_if_possible (GET_MODE (x), p->exp);
+ qty_const_insn[reg_qty[REGNO (x)]] = this_insn;
+ break;
+ }
+ }
+ }
+
+ else if (GET_CODE (x) == REG && qty_const[reg_qty[REGNO (x)]]
+ && GET_MODE (x) == qty_mode[reg_qty[REGNO (x)]])
+ qty_const_insn[reg_qty[REGNO (x)]] = this_insn;
+
+ /* If this is a constant with symbolic value,
+ and it has a term with an explicit integer value,
+ link it up with related expressions. */
+ if (GET_CODE (x) == CONST)
+ {
+ rtx subexp = get_related_value (x);
+ unsigned subhash;
+ struct table_elt *subelt, *subelt_prev;
+
+ if (subexp != 0)
+ {
+ /* Get the integer-free subexpression in the hash table. */
+ subhash = safe_hash (subexp, mode) % NBUCKETS;
+ subelt = lookup (subexp, subhash, mode);
+ if (subelt == 0)
+ subelt = insert (subexp, NULL_PTR, subhash, mode);
+ /* Initialize SUBELT's circular chain if it has none. */
+ if (subelt->related_value == 0)
+ subelt->related_value = subelt;
+ /* Find the element in the circular chain that precedes SUBELT. */
+ subelt_prev = subelt;
+ while (subelt_prev->related_value != subelt)
+ subelt_prev = subelt_prev->related_value;
+ /* Put new ELT into SUBELT's circular chain just before SUBELT.
+ This way the element that follows SUBELT is the oldest one. */
+ elt->related_value = subelt_prev->related_value;
+ subelt_prev->related_value = elt;
+ }
+ }
+
+ return elt;
+}
+
+/* Given two equivalence classes, CLASS1 and CLASS2, put all the entries from
+ CLASS2 into CLASS1. This is done when we have reached an insn which makes
+ the two classes equivalent.
+
+ CLASS1 will be the surviving class; CLASS2 should not be used after this
+ call.
+
+ Any invalid entries in CLASS2 will not be copied. */
+
+static void
+merge_equiv_classes (class1, class2)
+ struct table_elt *class1, *class2;
+{
+ struct table_elt *elt, *next, *new;
+
+ /* Ensure we start with the head of the classes. */
+ class1 = class1->first_same_value;
+ class2 = class2->first_same_value;
+
+ /* If they were already equal, forget it. */
+ if (class1 == class2)
+ return;
+
+ for (elt = class2; elt; elt = next)
+ {
+ unsigned hash;
+ rtx exp = elt->exp;
+ enum machine_mode mode = elt->mode;
+
+ next = elt->next_same_value;
+
+ /* Remove old entry, make a new one in CLASS1's class.
+ Don't do this for invalid entries as we cannot find their
+ hash code (it also isn't necessary). */
+ if (GET_CODE (exp) == REG || exp_equiv_p (exp, exp, 1, 0))
+ {
+ hash_arg_in_memory = 0;
+ hash_arg_in_struct = 0;
+ hash = HASH (exp, mode);
+
+ if (GET_CODE (exp) == REG)
+ delete_reg_equiv (REGNO (exp));
+
+ remove_from_table (elt, hash);
+
+ if (insert_regs (exp, class1, 0))
+ hash = HASH (exp, mode);
+ new = insert (exp, class1, hash, mode);
+ new->in_memory = hash_arg_in_memory;
+ new->in_struct = hash_arg_in_struct;
+ }
+ }
+}
+
+/* Remove from the hash table, or mark as invalid,
+ all expressions whose values could be altered by storing in X.
+ X is a register, a subreg, or a memory reference with nonvarying address
+ (because, when a memory reference with a varying address is stored in,
+ all memory references are removed by invalidate_memory
+ so specific invalidation is superfluous).
+
+ A nonvarying address may be just a register or just
+ a symbol reference, or it may be either of those plus
+ a numeric offset. */
+
+static void
+invalidate (x)
+ rtx x;
+{
+ register int i;
+ register struct table_elt *p;
+ rtx base;
+ HOST_WIDE_INT start, end;
+
+ /* If X is a register, dependencies on its contents
+ are recorded through the qty number mechanism.
+ Just change the qty number of the register,
+ mark it as invalid for expressions that refer to it,
+ and remove it itself. */
+
+ if (GET_CODE (x) == REG)
+ {
+ register int regno = REGNO (x);
+ register unsigned hash = HASH (x, GET_MODE (x));
+
+ /* Remove REGNO from any quantity list it might be on and indicate
+ that it's value might have changed. If it is a pseudo, remove its
+ entry from the hash table.
+
+ For a hard register, we do the first two actions above for any
+ additional hard registers corresponding to X. Then, if any of these
+ registers are in the table, we must remove any REG entries that
+ overlap these registers. */
+
+ delete_reg_equiv (regno);
+ reg_tick[regno]++;
+
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ remove_from_table (lookup_for_remove (x, hash, GET_MODE (x)), hash);
+ else
+ {
+ HOST_WIDE_INT in_table
+ = TEST_HARD_REG_BIT (hard_regs_in_table, regno);
+ int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
+ int tregno, tendregno;
+ register struct table_elt *p, *next;
+
+ CLEAR_HARD_REG_BIT (hard_regs_in_table, regno);
+
+ for (i = regno + 1; i < endregno; i++)
+ {
+ in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, i);
+ CLEAR_HARD_REG_BIT (hard_regs_in_table, i);
+ delete_reg_equiv (i);
+ reg_tick[i]++;
+ }
+
+ if (in_table)
+ for (hash = 0; hash < NBUCKETS; hash++)
+ for (p = table[hash]; p; p = next)
+ {
+ next = p->next_same_hash;
+
+ if (GET_CODE (p->exp) != REG
+ || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
+ continue;
+
+ tregno = REGNO (p->exp);
+ tendregno
+ = tregno + HARD_REGNO_NREGS (tregno, GET_MODE (p->exp));
+ if (tendregno > regno && tregno < endregno)
+ remove_from_table (p, hash);
+ }
+ }
+
+ return;
+ }
+
+ if (GET_CODE (x) == SUBREG)
+ {
+ if (GET_CODE (SUBREG_REG (x)) != REG)
+ abort ();
+ invalidate (SUBREG_REG (x));
+ return;
+ }
+
+ /* X is not a register; it must be a memory reference with
+ a nonvarying address. Remove all hash table elements
+ that refer to overlapping pieces of memory. */
+
+ if (GET_CODE (x) != MEM)
+ abort ();
+
+ set_nonvarying_address_components (XEXP (x, 0), GET_MODE_SIZE (GET_MODE (x)),
+ &base, &start, &end);
+
+ for (i = 0; i < NBUCKETS; i++)
+ {
+ register struct table_elt *next;
+ for (p = table[i]; p; p = next)
+ {
+ next = p->next_same_hash;
+ if (refers_to_mem_p (p->exp, base, start, end))
+ remove_from_table (p, i);
+ }
+ }
+}
+
+/* Remove all expressions that refer to register REGNO,
+ since they are already invalid, and we are about to
+ mark that register valid again and don't want the old
+ expressions to reappear as valid. */
+
+static void
+remove_invalid_refs (regno)
+ int regno;
+{
+ register int i;
+ register struct table_elt *p, *next;
+
+ for (i = 0; i < NBUCKETS; i++)
+ for (p = table[i]; p; p = next)
+ {
+ next = p->next_same_hash;
+ if (GET_CODE (p->exp) != REG
+ && refers_to_regno_p (regno, regno + 1, p->exp, NULL_PTR))
+ remove_from_table (p, i);
+ }
+}
+
+/* Recompute the hash codes of any valid entries in the hash table that
+ reference X, if X is a register, or SUBREG_REG (X) if X is a SUBREG.
+
+ This is called when we make a jump equivalence. */
+
+static void
+rehash_using_reg (x)
+ rtx x;
+{
+ int i;
+ struct table_elt *p, *next;
+ unsigned hash;
+
+ if (GET_CODE (x) == SUBREG)
+ x = SUBREG_REG (x);
+
+ /* If X is not a register or if the register is known not to be in any
+ valid entries in the table, we have no work to do. */
+
+ if (GET_CODE (x) != REG
+ || reg_in_table[REGNO (x)] < 0
+ || reg_in_table[REGNO (x)] != reg_tick[REGNO (x)])
+ return;
+
+ /* Scan all hash chains looking for valid entries that mention X.
+ If we find one and it is in the wrong hash chain, move it. We can skip
+ objects that are registers, since they are handled specially. */
+
+ for (i = 0; i < NBUCKETS; i++)
+ for (p = table[i]; p; p = next)
+ {
+ next = p->next_same_hash;
+ if (GET_CODE (p->exp) != REG && reg_mentioned_p (x, p->exp)
+ && exp_equiv_p (p->exp, p->exp, 1, 0)
+ && i != (hash = safe_hash (p->exp, p->mode) % NBUCKETS))
+ {
+ if (p->next_same_hash)
+ p->next_same_hash->prev_same_hash = p->prev_same_hash;
+
+ if (p->prev_same_hash)
+ p->prev_same_hash->next_same_hash = p->next_same_hash;
+ else
+ table[i] = p->next_same_hash;
+
+ p->next_same_hash = table[hash];
+ p->prev_same_hash = 0;
+ if (table[hash])
+ table[hash]->prev_same_hash = p;
+ table[hash] = p;
+ }
+ }
+}
+
+/* Remove from the hash table all expressions that reference memory,
+ or some of them as specified by *WRITES. */
+
+static void
+invalidate_memory (writes)
+ struct write_data *writes;
+{
+ register int i;
+ register struct table_elt *p, *next;
+ int all = writes->all;
+ int nonscalar = writes->nonscalar;
+
+ for (i = 0; i < NBUCKETS; i++)
+ for (p = table[i]; p; p = next)
+ {
+ next = p->next_same_hash;
+ if (p->in_memory
+ && (all
+ || (nonscalar && p->in_struct)
+ || cse_rtx_addr_varies_p (p->exp)))
+ remove_from_table (p, i);
+ }
+}
+
+/* Remove from the hash table any expression that is a call-clobbered
+ register. Also update their TICK values. */
+
+static void
+invalidate_for_call ()
+{
+ int regno, endregno;
+ int i;
+ unsigned hash;
+ struct table_elt *p, *next;
+ int in_table = 0;
+
+ /* Go through all the hard registers. For each that is clobbered in
+ a CALL_INSN, remove the register from quantity chains and update
+ reg_tick if defined. Also see if any of these registers is currently
+ in the table. */
+
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
+ {
+ delete_reg_equiv (regno);
+ if (reg_tick[regno] >= 0)
+ reg_tick[regno]++;
+
+ in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, regno);
+ }
+
+ /* In the case where we have no call-clobbered hard registers in the
+ table, we are done. Otherwise, scan the table and remove any
+ entry that overlaps a call-clobbered register. */
+
+ if (in_table)
+ for (hash = 0; hash < NBUCKETS; hash++)
+ for (p = table[hash]; p; p = next)
+ {
+ next = p->next_same_hash;
+
+ if (GET_CODE (p->exp) != REG
+ || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
+ continue;
+
+ regno = REGNO (p->exp);
+ endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (p->exp));
+
+ for (i = regno; i < endregno; i++)
+ if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i))
+ {
+ remove_from_table (p, hash);
+ break;
+ }
+ }
+}
+
+/* Given an expression X of type CONST,
+ and ELT which is its table entry (or 0 if it
+ is not in the hash table),
+ return an alternate expression for X as a register plus integer.
+ If none can be found, return 0. */
+
+static rtx
+use_related_value (x, elt)
+ rtx x;
+ struct table_elt *elt;
+{
+ register struct table_elt *relt = 0;
+ register struct table_elt *p, *q;
+ HOST_WIDE_INT offset;
+
+ /* First, is there anything related known?
+ If we have a table element, we can tell from that.
+ Otherwise, must look it up. */
+
+ if (elt != 0 && elt->related_value != 0)
+ relt = elt;
+ else if (elt == 0 && GET_CODE (x) == CONST)
+ {
+ rtx subexp = get_related_value (x);
+ if (subexp != 0)
+ relt = lookup (subexp,
+ safe_hash (subexp, GET_MODE (subexp)) % NBUCKETS,
+ GET_MODE (subexp));
+ }
+
+ if (relt == 0)
+ return 0;
+
+ /* Search all related table entries for one that has an
+ equivalent register. */
+
+ p = relt;
+ while (1)
+ {
+ /* This loop is strange in that it is executed in two different cases.
+ The first is when X is already in the table. Then it is searching
+ the RELATED_VALUE list of X's class (RELT). The second case is when
+ X is not in the table. Then RELT points to a class for the related
+ value.
+
+ Ensure that, whatever case we are in, that we ignore classes that have
+ the same value as X. */
+
+ if (rtx_equal_p (x, p->exp))
+ q = 0;
+ else
+ for (q = p->first_same_value; q; q = q->next_same_value)
+ if (GET_CODE (q->exp) == REG)
+ break;
+
+ if (q)
+ break;
+
+ p = p->related_value;
+
+ /* We went all the way around, so there is nothing to be found.
+ Alternatively, perhaps RELT was in the table for some other reason
+ and it has no related values recorded. */
+ if (p == relt || p == 0)
+ break;
+ }
+
+ if (q == 0)
+ return 0;
+
+ offset = (get_integer_term (x) - get_integer_term (p->exp));
+ /* Note: OFFSET may be 0 if P->xexp and X are related by commutativity. */
+ return plus_constant (q->exp, offset);
+}
+
+/* Hash an rtx. We are careful to make sure the value is never negative.
+ Equivalent registers hash identically.
+ MODE is used in hashing for CONST_INTs only;
+ otherwise the mode of X is used.
+
+ Store 1 in do_not_record if any subexpression is volatile.
+
+ Store 1 in hash_arg_in_memory if X contains a MEM rtx
+ which does not have the RTX_UNCHANGING_P bit set.
+ In this case, also store 1 in hash_arg_in_struct
+ if there is a MEM rtx which has the MEM_IN_STRUCT_P bit set.
+
+ Note that cse_insn knows that the hash code of a MEM expression
+ is just (int) MEM plus the hash code of the address. */
+
+static unsigned
+canon_hash (x, mode)
+ rtx x;
+ enum machine_mode mode;
+{
+ register int i, j;
+ register unsigned hash = 0;
+ register enum rtx_code code;
+ register char *fmt;
+
+ /* repeat is used to turn tail-recursion into iteration. */
+ repeat:
+ if (x == 0)
+ return hash;
+
+ code = GET_CODE (x);
+ switch (code)
+ {
+ case REG:
+ {
+ register int regno = REGNO (x);
+
+ /* On some machines, we can't record any non-fixed hard register,
+ because extending its life will cause reload problems. We
+ consider ap, fp, and sp to be fixed for this purpose.
+ On all machines, we can't record any global registers. */
+
+ if (regno < FIRST_PSEUDO_REGISTER
+ && (global_regs[regno]
+#ifdef SMALL_REGISTER_CLASSES
+ || (! fixed_regs[regno]
+ && regno != FRAME_POINTER_REGNUM
+ && regno != HARD_FRAME_POINTER_REGNUM
+ && regno != ARG_POINTER_REGNUM
+ && regno != STACK_POINTER_REGNUM)
+#endif
+ ))
+ {
+ do_not_record = 1;
+ return 0;
+ }
+ hash += ((unsigned) REG << 7) + (unsigned) reg_qty[regno];
+ return hash;
+ }
+
+ case CONST_INT:
+ {
+ unsigned HOST_WIDE_INT tem = INTVAL (x);
+ hash += ((unsigned) CONST_INT << 7) + (unsigned) mode + tem;
+ return hash;
+ }
+
+ case CONST_DOUBLE:
+ /* This is like the general case, except that it only counts
+ the integers representing the constant. */
+ hash += (unsigned) code + (unsigned) GET_MODE (x);
+ for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++)
+ {
+ unsigned tem = XINT (x, i);
+ hash += tem;
+ }
+ return hash;
+
+ /* Assume there is only one rtx object for any given label. */
+ case LABEL_REF:
+ hash
+ += ((unsigned) LABEL_REF << 7) + (unsigned HOST_WIDE_INT) XEXP (x, 0);
+ return hash;
+
+ case SYMBOL_REF:
+ hash
+ += ((unsigned) SYMBOL_REF << 7) + (unsigned HOST_WIDE_INT) XSTR (x, 0);
+ return hash;
+
+ case MEM:
+ if (MEM_VOLATILE_P (x))
+ {
+ do_not_record = 1;
+ return 0;
+ }
+ if (! RTX_UNCHANGING_P (x))
+ {
+ hash_arg_in_memory = 1;
+ if (MEM_IN_STRUCT_P (x)) hash_arg_in_struct = 1;
+ }
+ /* Now that we have already found this special case,
+ might as well speed it up as much as possible. */
+ hash += (unsigned) MEM;
+ x = XEXP (x, 0);
+ goto repeat;
+
+ case PRE_DEC:
+ case PRE_INC:
+ case POST_DEC:
+ case POST_INC:
+ case PC:
+ case CC0:
+ case CALL:
+ case UNSPEC_VOLATILE:
+ do_not_record = 1;
+ return 0;
+
+ case ASM_OPERANDS:
+ if (MEM_VOLATILE_P (x))
+ {
+ do_not_record = 1;
+ return 0;
+ }
+ }
+
+ i = GET_RTX_LENGTH (code) - 1;
+ hash += (unsigned) code + (unsigned) GET_MODE (x);
+ fmt = GET_RTX_FORMAT (code);
+ for (; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ {
+ rtx tem = XEXP (x, i);
+ rtx tem1;
+
+ /* If the operand is a REG that is equivalent to a constant, hash
+ as if we were hashing the constant, since we will be comparing
+ that way. */
+ if (tem != 0 && GET_CODE (tem) == REG
+ && REGNO_QTY_VALID_P (REGNO (tem))
+ && qty_mode[reg_qty[REGNO (tem)]] == GET_MODE (tem)
+ && (tem1 = qty_const[reg_qty[REGNO (tem)]]) != 0
+ && CONSTANT_P (tem1))
+ tem = tem1;
+
+ /* If we are about to do the last recursive call
+ needed at this level, change it into iteration.
+ This function is called enough to be worth it. */
+ if (i == 0)
+ {
+ x = tem;
+ goto repeat;
+ }
+ hash += canon_hash (tem, 0);
+ }
+ else if (fmt[i] == 'E')
+ for (j = 0; j < XVECLEN (x, i); j++)
+ hash += canon_hash (XVECEXP (x, i, j), 0);
+ else if (fmt[i] == 's')
+ {
+ register unsigned char *p = (unsigned char *) XSTR (x, i);
+ if (p)
+ while (*p)
+ hash += *p++;
+ }
+ else if (fmt[i] == 'i')
+ {
+ register unsigned tem = XINT (x, i);
+ hash += tem;
+ }
+ else
+ abort ();
+ }
+ return hash;
+}
+
+/* Like canon_hash but with no side effects. */
+
+static unsigned
+safe_hash (x, mode)
+ rtx x;
+ enum machine_mode mode;
+{
+ int save_do_not_record = do_not_record;
+ int save_hash_arg_in_memory = hash_arg_in_memory;
+ int save_hash_arg_in_struct = hash_arg_in_struct;
+ unsigned hash = canon_hash (x, mode);
+ hash_arg_in_memory = save_hash_arg_in_memory;
+ hash_arg_in_struct = save_hash_arg_in_struct;
+ do_not_record = save_do_not_record;
+ return hash;
+}
+
+/* Return 1 iff X and Y would canonicalize into the same thing,
+ without actually constructing the canonicalization of either one.
+ If VALIDATE is nonzero,
+ we assume X is an expression being processed from the rtl
+ and Y was found in the hash table. We check register refs
+ in Y for being marked as valid.
+
+ If EQUAL_VALUES is nonzero, we allow a register to match a constant value
+ that is known to be in the register. Ordinarily, we don't allow them
+ to match, because letting them match would cause unpredictable results
+ in all the places that search a hash table chain for an equivalent
+ for a given value. A possible equivalent that has different structure
+ has its hash code computed from different data. Whether the hash code
+ is the same as that of the the given value is pure luck. */
+
+static int
+exp_equiv_p (x, y, validate, equal_values)
+ rtx x, y;
+ int validate;
+ int equal_values;
+{
+ register int i, j;
+ register enum rtx_code code;
+ register char *fmt;
+
+ /* Note: it is incorrect to assume an expression is equivalent to itself
+ if VALIDATE is nonzero. */
+ if (x == y && !validate)
+ return 1;
+ if (x == 0 || y == 0)
+ return x == y;
+
+ code = GET_CODE (x);
+ if (code != GET_CODE (y))
+ {
+ if (!equal_values)
+ return 0;
+
+ /* If X is a constant and Y is a register or vice versa, they may be
+ equivalent. We only have to validate if Y is a register. */
+ if (CONSTANT_P (x) && GET_CODE (y) == REG
+ && REGNO_QTY_VALID_P (REGNO (y))
+ && GET_MODE (y) == qty_mode[reg_qty[REGNO (y)]]
+ && rtx_equal_p (x, qty_const[reg_qty[REGNO (y)]])
+ && (! validate || reg_in_table[REGNO (y)] == reg_tick[REGNO (y)]))
+ return 1;
+
+ if (CONSTANT_P (y) && code == REG
+ && REGNO_QTY_VALID_P (REGNO (x))
+ && GET_MODE (x) == qty_mode[reg_qty[REGNO (x)]]
+ && rtx_equal_p (y, qty_const[reg_qty[REGNO (x)]]))
+ return 1;
+
+ return 0;
+ }
+
+ /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. */
+ if (GET_MODE (x) != GET_MODE (y))
+ return 0;
+
+ switch (code)
+ {
+ case PC:
+ case CC0:
+ return x == y;
+
+ case CONST_INT:
+ return INTVAL (x) == INTVAL (y);
+
+ case LABEL_REF:
+ return XEXP (x, 0) == XEXP (y, 0);
+
+ case SYMBOL_REF:
+ return XSTR (x, 0) == XSTR (y, 0);
+
+ case REG:
+ {
+ int regno = REGNO (y);
+ int endregno
+ = regno + (regno >= FIRST_PSEUDO_REGISTER ? 1
+ : HARD_REGNO_NREGS (regno, GET_MODE (y)));
+ int i;
+
+ /* If the quantities are not the same, the expressions are not
+ equivalent. If there are and we are not to validate, they
+ are equivalent. Otherwise, ensure all regs are up-to-date. */
+
+ if (reg_qty[REGNO (x)] != reg_qty[regno])
+ return 0;
+
+ if (! validate)
+ return 1;
+
+ for (i = regno; i < endregno; i++)
+ if (reg_in_table[i] != reg_tick[i])
+ return 0;
+
+ return 1;
+ }
+
+ /* For commutative operations, check both orders. */
+ case PLUS:
+ case MULT:
+ case AND:
+ case IOR:
+ case XOR:
+ case NE:
+ case EQ:
+ return ((exp_equiv_p (XEXP (x, 0), XEXP (y, 0), validate, equal_values)
+ && exp_equiv_p (XEXP (x, 1), XEXP (y, 1),
+ validate, equal_values))
+ || (exp_equiv_p (XEXP (x, 0), XEXP (y, 1),
+ validate, equal_values)
+ && exp_equiv_p (XEXP (x, 1), XEXP (y, 0),
+ validate, equal_values)));
+ }
+
+ /* Compare the elements. If any pair of corresponding elements
+ fail to match, return 0 for the whole things. */
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ switch (fmt[i])
+ {
+ case 'e':
+ if (! exp_equiv_p (XEXP (x, i), XEXP (y, i), validate, equal_values))
+ return 0;
+ break;
+
+ case 'E':
+ if (XVECLEN (x, i) != XVECLEN (y, i))
+ return 0;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ if (! exp_equiv_p (XVECEXP (x, i, j), XVECEXP (y, i, j),
+ validate, equal_values))
+ return 0;
+ break;
+
+ case 's':
+ if (strcmp (XSTR (x, i), XSTR (y, i)))
+ return 0;
+ break;
+
+ case 'i':
+ if (XINT (x, i) != XINT (y, i))
+ return 0;
+ break;
+
+ case 'w':
+ if (XWINT (x, i) != XWINT (y, i))
+ return 0;
+ break;
+
+ case '0':
+ break;
+
+ default:
+ abort ();
+ }
+ }
+
+ return 1;
+}
+
+/* Return 1 iff any subexpression of X matches Y.
+ Here we do not require that X or Y be valid (for registers referred to)
+ for being in the hash table. */
+
+static int
+refers_to_p (x, y)
+ rtx x, y;
+{
+ register int i;
+ register enum rtx_code code;
+ register char *fmt;
+
+ repeat:
+ if (x == y)
+ return 1;
+ if (x == 0 || y == 0)
+ return 0;
+
+ code = GET_CODE (x);
+ /* If X as a whole has the same code as Y, they may match.
+ If so, return 1. */
+ if (code == GET_CODE (y))
+ {
+ if (exp_equiv_p (x, y, 0, 1))
+ return 1;
+ }
+
+ /* X does not match, so try its subexpressions. */
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ if (fmt[i] == 'e')
+ {
+ if (i == 0)
+ {
+ x = XEXP (x, 0);
+ goto repeat;
+ }
+ else
+ if (refers_to_p (XEXP (x, i), y))
+ return 1;
+ }
+ else if (fmt[i] == 'E')
+ {
+ int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ if (refers_to_p (XVECEXP (x, i, j), y))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Given ADDR and SIZE (a memory address, and the size of the memory reference),
+ set PBASE, PSTART, and PEND which correspond to the base of the address,
+ the starting offset, and ending offset respectively.
+
+ ADDR is known to be a nonvarying address.
+
+ cse_address_varies_p returns zero for nonvarying addresses. */
+
+static void
+set_nonvarying_address_components (addr, size, pbase, pstart, pend)
+ rtx addr;
+ int size;
+ rtx *pbase;
+ HOST_WIDE_INT *pstart, *pend;
+{
+ rtx base;
+ HOST_WIDE_INT start, end;
+
+ base = addr;
+ start = 0;
+ end = 0;
+
+ /* Registers with nonvarying addresses usually have constant equivalents;
+ but the frame pointer register is also possible. */
+ if (GET_CODE (base) == REG
+ && qty_const != 0
+ && REGNO_QTY_VALID_P (REGNO (base))
+ && qty_mode[reg_qty[REGNO (base)]] == GET_MODE (base)
+ && qty_const[reg_qty[REGNO (base)]] != 0)
+ base = qty_const[reg_qty[REGNO (base)]];
+ else if (GET_CODE (base) == PLUS
+ && GET_CODE (XEXP (base, 1)) == CONST_INT
+ && GET_CODE (XEXP (base, 0)) == REG
+ && qty_const != 0
+ && REGNO_QTY_VALID_P (REGNO (XEXP (base, 0)))
+ && (qty_mode[reg_qty[REGNO (XEXP (base, 0))]]
+ == GET_MODE (XEXP (base, 0)))
+ && qty_const[reg_qty[REGNO (XEXP (base, 0))]])
+ {
+ start = INTVAL (XEXP (base, 1));
+ base = qty_const[reg_qty[REGNO (XEXP (base, 0))]];
+ }
+
+ /* Handle everything that we can find inside an address that has been
+ viewed as constant. */
+
+ while (1)
+ {
+ /* If no part of this switch does a "continue", the code outside
+ will exit this loop. */
+
+ switch (GET_CODE (base))
+ {
+ case LO_SUM:
+ /* By definition, operand1 of a LO_SUM is the associated constant
+ address. Use the associated constant address as the base
+ instead. */
+ base = XEXP (base, 1);
+ continue;
+
+ case CONST:
+ /* Strip off CONST. */
+ base = XEXP (base, 0);
+ continue;
+
+ case PLUS:
+ if (GET_CODE (XEXP (base, 1)) == CONST_INT)
+ {
+ start += INTVAL (XEXP (base, 1));
+ base = XEXP (base, 0);
+ continue;
+ }
+ break;
+
+ case AND:
+ /* Handle the case of an AND which is the negative of a power of
+ two. This is used to represent unaligned memory operations. */
+ if (GET_CODE (XEXP (base, 1)) == CONST_INT
+ && exact_log2 (- INTVAL (XEXP (base, 1))) > 0)
+ {
+ set_nonvarying_address_components (XEXP (base, 0), size,
+ pbase, pstart, pend);
+
+ /* Assume the worst misalignment. START is affected, but not
+ END, so compensate but adjusting SIZE. Don't lose any
+ constant we already had. */
+
+ size = *pend - *pstart - INTVAL (XEXP (base, 1)) - 1;
+ start += *pstart - INTVAL (XEXP (base, 1)) - 1;
+ base = *pbase;
+ }
+ break;
+ }
+
+ break;
+ }
+
+ end = start + size;
+
+ /* Set the return values. */
+ *pbase = base;
+ *pstart = start;
+ *pend = end;
+}
+
+/* Return 1 iff any subexpression of X refers to memory
+ at an address of BASE plus some offset
+ such that any of the bytes' offsets fall between START (inclusive)
+ and END (exclusive).
+
+ The value is undefined if X is a varying address (as determined by
+ cse_rtx_addr_varies_p). This function is not used in such cases.
+
+ When used in the cse pass, `qty_const' is nonzero, and it is used
+ to treat an address that is a register with a known constant value
+ as if it were that constant value.
+ In the loop pass, `qty_const' is zero, so this is not done. */
+
+static int
+refers_to_mem_p (x, base, start, end)
+ rtx x, base;
+ HOST_WIDE_INT start, end;
+{
+ register HOST_WIDE_INT i;
+ register enum rtx_code code;
+ register char *fmt;
+
+ if (GET_CODE (base) == CONST_INT)
+ {
+ start += INTVAL (base);
+ end += INTVAL (base);
+ base = const0_rtx;
+ }
+
+ repeat:
+ if (x == 0)
+ return 0;
+
+ code = GET_CODE (x);
+ if (code == MEM)
+ {
+ register rtx addr = XEXP (x, 0); /* Get the address. */
+ rtx mybase;
+ HOST_WIDE_INT mystart, myend;
+
+ set_nonvarying_address_components (addr, GET_MODE_SIZE (GET_MODE (x)),
+ &mybase, &mystart, &myend);
+
+
+ /* refers_to_mem_p is never called with varying addresses.
+ If the base addresses are not equal, there is no chance
+ of the memory addresses conflicting. */
+ if (! rtx_equal_p (mybase, base))
+ return 0;
+
+ return myend > start && mystart < end;
+ }
+
+ /* X does not match, so try its subexpressions. */
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ if (fmt[i] == 'e')
+ {
+ if (i == 0)
+ {
+ x = XEXP (x, 0);
+ goto repeat;
+ }
+ else
+ if (refers_to_mem_p (XEXP (x, i), base, start, end))
+ return 1;
+ }
+ else if (fmt[i] == 'E')
+ {
+ int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ if (refers_to_mem_p (XVECEXP (x, i, j), base, start, end))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Nonzero if X refers to memory at a varying address;
+ except that a register which has at the moment a known constant value
+ isn't considered variable. */
+
+static int
+cse_rtx_addr_varies_p (x)
+ rtx x;
+{
+ /* We need not check for X and the equivalence class being of the same
+ mode because if X is equivalent to a constant in some mode, it
+ doesn't vary in any mode. */
+
+ if (GET_CODE (x) == MEM
+ && GET_CODE (XEXP (x, 0)) == REG
+ && REGNO_QTY_VALID_P (REGNO (XEXP (x, 0)))
+ && GET_MODE (XEXP (x, 0)) == qty_mode[reg_qty[REGNO (XEXP (x, 0))]]
+ && qty_const[reg_qty[REGNO (XEXP (x, 0))]] != 0)
+ return 0;
+
+ if (GET_CODE (x) == MEM
+ && GET_CODE (XEXP (x, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+ && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
+ && REGNO_QTY_VALID_P (REGNO (XEXP (XEXP (x, 0), 0)))
+ && (GET_MODE (XEXP (XEXP (x, 0), 0))
+ == qty_mode[reg_qty[REGNO (XEXP (XEXP (x, 0), 0))]])
+ && qty_const[reg_qty[REGNO (XEXP (XEXP (x, 0), 0))]])
+ return 0;
+
+ return rtx_addr_varies_p (x);
+}
+
+/* Canonicalize an expression:
+ replace each register reference inside it
+ with the "oldest" equivalent register.
+
+ If INSN is non-zero and we are replacing a pseudo with a hard register
+ or vice versa, validate_change is used to ensure that INSN remains valid
+ after we make our substitution. The calls are made with IN_GROUP non-zero
+ so apply_change_group must be called upon the outermost return from this
+ function (unless INSN is zero). The result of apply_change_group can
+ generally be discarded since the changes we are making are optional. */
+
+static rtx
+canon_reg (x, insn)
+ rtx x;
+ rtx insn;
+{
+ register int i;
+ register enum rtx_code code;
+ register char *fmt;
+
+ if (x == 0)
+ return x;
+
+ code = GET_CODE (x);
+ switch (code)
+ {
+ case PC:
+ case CC0:
+ case CONST:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ return x;
+
+ case REG:
+ {
+ register int first;
+
+ /* Never replace a hard reg, because hard regs can appear
+ in more than one machine mode, and we must preserve the mode
+ of each occurrence. Also, some hard regs appear in
+ MEMs that are shared and mustn't be altered. Don't try to
+ replace any reg that maps to a reg of class NO_REGS. */
+ if (REGNO (x) < FIRST_PSEUDO_REGISTER
+ || ! REGNO_QTY_VALID_P (REGNO (x)))
+ return x;
+
+ first = qty_first_reg[reg_qty[REGNO (x)]];
+ return (first >= FIRST_PSEUDO_REGISTER ? regno_reg_rtx[first]
+ : REGNO_REG_CLASS (first) == NO_REGS ? x
+ : gen_rtx (REG, qty_mode[reg_qty[REGNO (x)]], first));
+ }
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ register int j;
+
+ if (fmt[i] == 'e')
+ {
+ rtx new = canon_reg (XEXP (x, i), insn);
+
+ /* If replacing pseudo with hard reg or vice versa, ensure the
+ insn remains valid. Likewise if the insn has MATCH_DUPs. */
+ if (insn != 0 && new != 0
+ && GET_CODE (new) == REG && GET_CODE (XEXP (x, i)) == REG
+ && (((REGNO (new) < FIRST_PSEUDO_REGISTER)
+ != (REGNO (XEXP (x, i)) < FIRST_PSEUDO_REGISTER))
+ || insn_n_dups[recog_memoized (insn)] > 0))
+ validate_change (insn, &XEXP (x, i), new, 1);
+ else
+ XEXP (x, i) = new;
+ }
+ else if (fmt[i] == 'E')
+ for (j = 0; j < XVECLEN (x, i); j++)
+ XVECEXP (x, i, j) = canon_reg (XVECEXP (x, i, j), insn);
+ }
+
+ return x;
+}
+
+/* LOC is a location with INSN that is an operand address (the contents of
+ a MEM). Find the best equivalent address to use that is valid for this
+ insn.
+
+ On most CISC machines, complicated address modes are costly, and rtx_cost
+ is a good approximation for that cost. However, most RISC machines have
+ only a few (usually only one) memory reference formats. If an address is
+ valid at all, it is often just as cheap as any other address. Hence, for
+ RISC machines, we use the configuration macro `ADDRESS_COST' to compare the
+ costs of various addresses. For two addresses of equal cost, choose the one
+ with the highest `rtx_cost' value as that has the potential of eliminating
+ the most insns. For equal costs, we choose the first in the equivalence
+ class. Note that we ignore the fact that pseudo registers are cheaper
+ than hard registers here because we would also prefer the pseudo registers.
+ */
+
+static void
+find_best_addr (insn, loc)
+ rtx insn;
+ rtx *loc;
+{
+ struct table_elt *elt, *p;
+ rtx addr = *loc;
+ int our_cost;
+ int found_better = 1;
+ int save_do_not_record = do_not_record;
+ int save_hash_arg_in_memory = hash_arg_in_memory;
+ int save_hash_arg_in_struct = hash_arg_in_struct;
+ int addr_volatile;
+ int regno;
+ unsigned hash;
+
+ /* Do not try to replace constant addresses or addresses of local and
+ argument slots. These MEM expressions are made only once and inserted
+ in many instructions, as well as being used to control symbol table
+ output. It is not safe to clobber them.
+
+ There are some uncommon cases where the address is already in a register
+ for some reason, but we cannot take advantage of that because we have
+ no easy way to unshare the MEM. In addition, looking up all stack
+ addresses is costly. */
+ if ((GET_CODE (addr) == PLUS
+ && GET_CODE (XEXP (addr, 0)) == REG
+ && GET_CODE (XEXP (addr, 1)) == CONST_INT
+ && (regno = REGNO (XEXP (addr, 0)),
+ regno == FRAME_POINTER_REGNUM || regno == HARD_FRAME_POINTER_REGNUM
+ || regno == ARG_POINTER_REGNUM))
+ || (GET_CODE (addr) == REG
+ && (regno = REGNO (addr), regno == FRAME_POINTER_REGNUM
+ || regno == HARD_FRAME_POINTER_REGNUM
+ || regno == ARG_POINTER_REGNUM))
+ || CONSTANT_ADDRESS_P (addr))
+ return;
+
+ /* If this address is not simply a register, try to fold it. This will
+ sometimes simplify the expression. Many simplifications
+ will not be valid, but some, usually applying the associative rule, will
+ be valid and produce better code. */
+ if (GET_CODE (addr) != REG
+ && validate_change (insn, loc, fold_rtx (addr, insn), 0))
+ addr = *loc;
+
+ /* If this address is not in the hash table, we can't look for equivalences
+ of the whole address. Also, ignore if volatile. */
+
+ do_not_record = 0;
+ hash = HASH (addr, Pmode);
+ addr_volatile = do_not_record;
+ do_not_record = save_do_not_record;
+ hash_arg_in_memory = save_hash_arg_in_memory;
+ hash_arg_in_struct = save_hash_arg_in_struct;
+
+ if (addr_volatile)
+ return;
+
+ elt = lookup (addr, hash, Pmode);
+
+#ifndef ADDRESS_COST
+ if (elt)
+ {
+ our_cost = elt->cost;
+
+ /* Find the lowest cost below ours that works. */
+ for (elt = elt->first_same_value; elt; elt = elt->next_same_value)
+ if (elt->cost < our_cost
+ && (GET_CODE (elt->exp) == REG
+ || exp_equiv_p (elt->exp, elt->exp, 1, 0))
+ && validate_change (insn, loc,
+ canon_reg (copy_rtx (elt->exp), NULL_RTX), 0))
+ return;
+ }
+#else
+
+ if (elt)
+ {
+ /* We need to find the best (under the criteria documented above) entry
+ in the class that is valid. We use the `flag' field to indicate
+ choices that were invalid and iterate until we can't find a better
+ one that hasn't already been tried. */
+
+ for (p = elt->first_same_value; p; p = p->next_same_value)
+ p->flag = 0;
+
+ while (found_better)
+ {
+ int best_addr_cost = ADDRESS_COST (*loc);
+ int best_rtx_cost = (elt->cost + 1) >> 1;
+ struct table_elt *best_elt = elt;
+
+ found_better = 0;
+ for (p = elt->first_same_value; p; p = p->next_same_value)
+ if (! p->flag
+ && (GET_CODE (p->exp) == REG
+ || exp_equiv_p (p->exp, p->exp, 1, 0))
+ && (ADDRESS_COST (p->exp) < best_addr_cost
+ || (ADDRESS_COST (p->exp) == best_addr_cost
+ && (p->cost + 1) >> 1 > best_rtx_cost)))
+ {
+ found_better = 1;
+ best_addr_cost = ADDRESS_COST (p->exp);
+ best_rtx_cost = (p->cost + 1) >> 1;
+ best_elt = p;
+ }
+
+ if (found_better)
+ {
+ if (validate_change (insn, loc,
+ canon_reg (copy_rtx (best_elt->exp),
+ NULL_RTX), 0))
+ return;
+ else
+ best_elt->flag = 1;
+ }
+ }
+ }
+
+ /* If the address is a binary operation with the first operand a register
+ and the second a constant, do the same as above, but looking for
+ equivalences of the register. Then try to simplify before checking for
+ the best address to use. This catches a few cases: First is when we
+ have REG+const and the register is another REG+const. We can often merge
+ the constants and eliminate one insn and one register. It may also be
+ that a machine has a cheap REG+REG+const. Finally, this improves the
+ code on the Alpha for unaligned byte stores. */
+
+ if (flag_expensive_optimizations
+ && (GET_RTX_CLASS (GET_CODE (*loc)) == '2'
+ || GET_RTX_CLASS (GET_CODE (*loc)) == 'c')
+ && GET_CODE (XEXP (*loc, 0)) == REG
+ && GET_CODE (XEXP (*loc, 1)) == CONST_INT)
+ {
+ rtx c = XEXP (*loc, 1);
+
+ do_not_record = 0;
+ hash = HASH (XEXP (*loc, 0), Pmode);
+ do_not_record = save_do_not_record;
+ hash_arg_in_memory = save_hash_arg_in_memory;
+ hash_arg_in_struct = save_hash_arg_in_struct;
+
+ elt = lookup (XEXP (*loc, 0), hash, Pmode);
+ if (elt == 0)
+ return;
+
+ /* We need to find the best (under the criteria documented above) entry
+ in the class that is valid. We use the `flag' field to indicate
+ choices that were invalid and iterate until we can't find a better
+ one that hasn't already been tried. */
+
+ for (p = elt->first_same_value; p; p = p->next_same_value)
+ p->flag = 0;
+
+ while (found_better)
+ {
+ int best_addr_cost = ADDRESS_COST (*loc);
+ int best_rtx_cost = (COST (*loc) + 1) >> 1;
+ struct table_elt *best_elt = elt;
+ rtx best_rtx = *loc;
+ int count;
+
+ /* This is at worst case an O(n^2) algorithm, so limit our search
+ to the first 32 elements on the list. This avoids trouble
+ compiling code with very long basic blocks that can easily
+ call cse_gen_binary so many times that we run out of memory. */
+
+ found_better = 0;
+ for (p = elt->first_same_value, count = 0;
+ p && count < 32;
+ p = p->next_same_value, count++)
+ if (! p->flag
+ && (GET_CODE (p->exp) == REG
+ || exp_equiv_p (p->exp, p->exp, 1, 0)))
+ {
+ rtx new = cse_gen_binary (GET_CODE (*loc), Pmode, p->exp, c);
+
+ if ((ADDRESS_COST (new) < best_addr_cost
+ || (ADDRESS_COST (new) == best_addr_cost
+ && (COST (new) + 1) >> 1 > best_rtx_cost)))
+ {
+ found_better = 1;
+ best_addr_cost = ADDRESS_COST (new);
+ best_rtx_cost = (COST (new) + 1) >> 1;
+ best_elt = p;
+ best_rtx = new;
+ }
+ }
+
+ if (found_better)
+ {
+ if (validate_change (insn, loc,
+ canon_reg (copy_rtx (best_rtx),
+ NULL_RTX), 0))
+ return;
+ else
+ best_elt->flag = 1;
+ }
+ }
+ }
+#endif
+}
+
+/* Given an operation (CODE, *PARG1, *PARG2), where code is a comparison
+ operation (EQ, NE, GT, etc.), follow it back through the hash table and
+ what values are being compared.
+
+ *PARG1 and *PARG2 are updated to contain the rtx representing the values
+ actually being compared. For example, if *PARG1 was (cc0) and *PARG2
+ was (const_int 0), *PARG1 and *PARG2 will be set to the objects that were
+ compared to produce cc0.
+
+ The return value is the comparison operator and is either the code of
+ A or the code corresponding to the inverse of the comparison. */
+
+static enum rtx_code
+find_comparison_args (code, parg1, parg2, pmode1, pmode2)
+ enum rtx_code code;
+ rtx *parg1, *parg2;
+ enum machine_mode *pmode1, *pmode2;
+{
+ rtx arg1, arg2;
+
+ arg1 = *parg1, arg2 = *parg2;
+
+ /* If ARG2 is const0_rtx, see what ARG1 is equivalent to. */
+
+ while (arg2 == CONST0_RTX (GET_MODE (arg1)))
+ {
+ /* Set non-zero when we find something of interest. */
+ rtx x = 0;
+ int reverse_code = 0;
+ struct table_elt *p = 0;
+
+ /* If arg1 is a COMPARE, extract the comparison arguments from it.
+ On machines with CC0, this is the only case that can occur, since
+ fold_rtx will return the COMPARE or item being compared with zero
+ when given CC0. */
+
+ if (GET_CODE (arg1) == COMPARE && arg2 == const0_rtx)
+ x = arg1;
+
+ /* If ARG1 is a comparison operator and CODE is testing for
+ STORE_FLAG_VALUE, get the inner arguments. */
+
+ else if (GET_RTX_CLASS (GET_CODE (arg1)) == '<')
+ {
+ if (code == NE
+ || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_INT
+ && code == LT && STORE_FLAG_VALUE == -1)
+#ifdef FLOAT_STORE_FLAG_VALUE
+ || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_FLOAT
+ && FLOAT_STORE_FLAG_VALUE < 0)
+#endif
+ )
+ x = arg1;
+ else if (code == EQ
+ || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_INT
+ && code == GE && STORE_FLAG_VALUE == -1)
+#ifdef FLOAT_STORE_FLAG_VALUE
+ || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_FLOAT
+ && FLOAT_STORE_FLAG_VALUE < 0)
+#endif
+ )
+ x = arg1, reverse_code = 1;
+ }
+
+ /* ??? We could also check for
+
+ (ne (and (eq (...) (const_int 1))) (const_int 0))
+
+ and related forms, but let's wait until we see them occurring. */
+
+ if (x == 0)
+ /* Look up ARG1 in the hash table and see if it has an equivalence
+ that lets us see what is being compared. */
+ p = lookup (arg1, safe_hash (arg1, GET_MODE (arg1)) % NBUCKETS,
+ GET_MODE (arg1));
+ if (p) p = p->first_same_value;
+
+ for (; p; p = p->next_same_value)
+ {
+ enum machine_mode inner_mode = GET_MODE (p->exp);
+
+ /* If the entry isn't valid, skip it. */
+ if (! exp_equiv_p (p->exp, p->exp, 1, 0))
+ continue;
+
+ if (GET_CODE (p->exp) == COMPARE
+ /* Another possibility is that this machine has a compare insn
+ that includes the comparison code. In that case, ARG1 would
+ be equivalent to a comparison operation that would set ARG1 to
+ either STORE_FLAG_VALUE or zero. If this is an NE operation,
+ ORIG_CODE is the actual comparison being done; if it is an EQ,
+ we must reverse ORIG_CODE. On machine with a negative value
+ for STORE_FLAG_VALUE, also look at LT and GE operations. */
+ || ((code == NE
+ || (code == LT
+ && GET_MODE_CLASS (inner_mode) == MODE_INT
+ && (GET_MODE_BITSIZE (inner_mode)
+ <= HOST_BITS_PER_WIDE_INT)
+ && (STORE_FLAG_VALUE
+ & ((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (inner_mode) - 1))))
+#ifdef FLOAT_STORE_FLAG_VALUE
+ || (code == LT
+ && GET_MODE_CLASS (inner_mode) == MODE_FLOAT
+ && FLOAT_STORE_FLAG_VALUE < 0)
+#endif
+ )
+ && GET_RTX_CLASS (GET_CODE (p->exp)) == '<'))
+ {
+ x = p->exp;
+ break;
+ }
+ else if ((code == EQ
+ || (code == GE
+ && GET_MODE_CLASS (inner_mode) == MODE_INT
+ && (GET_MODE_BITSIZE (inner_mode)
+ <= HOST_BITS_PER_WIDE_INT)
+ && (STORE_FLAG_VALUE
+ & ((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (inner_mode) - 1))))
+#ifdef FLOAT_STORE_FLAG_VALUE
+ || (code == GE
+ && GET_MODE_CLASS (inner_mode) == MODE_FLOAT
+ && FLOAT_STORE_FLAG_VALUE < 0)
+#endif
+ )
+ && GET_RTX_CLASS (GET_CODE (p->exp)) == '<')
+ {
+ reverse_code = 1;
+ x = p->exp;
+ break;
+ }
+
+ /* If this is fp + constant, the equivalent is a better operand since
+ it may let us predict the value of the comparison. */
+ else if (NONZERO_BASE_PLUS_P (p->exp))
+ {
+ arg1 = p->exp;
+ continue;
+ }
+ }
+
+ /* If we didn't find a useful equivalence for ARG1, we are done.
+ Otherwise, set up for the next iteration. */
+ if (x == 0)
+ break;
+
+ arg1 = XEXP (x, 0), arg2 = XEXP (x, 1);
+ if (GET_RTX_CLASS (GET_CODE (x)) == '<')
+ code = GET_CODE (x);
+
+ if (reverse_code)
+ code = reverse_condition (code);
+ }
+
+ /* Return our results. Return the modes from before fold_rtx
+ because fold_rtx might produce const_int, and then it's too late. */
+ *pmode1 = GET_MODE (arg1), *pmode2 = GET_MODE (arg2);
+ *parg1 = fold_rtx (arg1, 0), *parg2 = fold_rtx (arg2, 0);
+
+ return code;
+}
+
+/* Try to simplify a unary operation CODE whose output mode is to be
+ MODE with input operand OP whose mode was originally OP_MODE.
+ Return zero if no simplification can be made. */
+
+rtx
+simplify_unary_operation (code, mode, op, op_mode)
+ enum rtx_code code;
+ enum machine_mode mode;
+ rtx op;
+ enum machine_mode op_mode;
+{
+ register int width = GET_MODE_BITSIZE (mode);
+
+ /* The order of these tests is critical so that, for example, we don't
+ check the wrong mode (input vs. output) for a conversion operation,
+ such as FIX. At some point, this should be simplified. */
+
+#if !defined(REAL_IS_NOT_DOUBLE) || defined(REAL_ARITHMETIC)
+
+ if (code == FLOAT && GET_MODE (op) == VOIDmode
+ && (GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT))
+ {
+ HOST_WIDE_INT hv, lv;
+ REAL_VALUE_TYPE d;
+
+ if (GET_CODE (op) == CONST_INT)
+ lv = INTVAL (op), hv = INTVAL (op) < 0 ? -1 : 0;
+ else
+ lv = CONST_DOUBLE_LOW (op), hv = CONST_DOUBLE_HIGH (op);
+
+#ifdef REAL_ARITHMETIC
+ REAL_VALUE_FROM_INT (d, lv, hv);
+#else
+ if (hv < 0)
+ {
+ d = (double) (~ hv);
+ d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))
+ * (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)));
+ d += (double) (unsigned HOST_WIDE_INT) (~ lv);
+ d = (- d - 1.0);
+ }
+ else
+ {
+ d = (double) hv;
+ d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))
+ * (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)));
+ d += (double) (unsigned HOST_WIDE_INT) lv;
+ }
+#endif /* REAL_ARITHMETIC */
+
+ return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
+ }
+ else if (code == UNSIGNED_FLOAT && GET_MODE (op) == VOIDmode
+ && (GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT))
+ {
+ HOST_WIDE_INT hv, lv;
+ REAL_VALUE_TYPE d;
+
+ if (GET_CODE (op) == CONST_INT)
+ lv = INTVAL (op), hv = INTVAL (op) < 0 ? -1 : 0;
+ else
+ lv = CONST_DOUBLE_LOW (op), hv = CONST_DOUBLE_HIGH (op);
+
+ if (GET_MODE_BITSIZE (op_mode) >= HOST_BITS_PER_WIDE_INT * 2)
+ ;
+ else
+ hv = 0, lv &= GET_MODE_MASK (op_mode);
+
+#ifdef REAL_ARITHMETIC
+ REAL_VALUE_FROM_UNSIGNED_INT (d, lv, hv);
+#else
+
+ d = (double) (unsigned HOST_WIDE_INT) hv;
+ d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))
+ * (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)));
+ d += (double) (unsigned HOST_WIDE_INT) lv;
+#endif /* REAL_ARITHMETIC */
+
+ return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
+ }
+#endif
+
+ if (GET_CODE (op) == CONST_INT
+ && width <= HOST_BITS_PER_WIDE_INT && width > 0)
+ {
+ register HOST_WIDE_INT arg0 = INTVAL (op);
+ register HOST_WIDE_INT val;
+
+ switch (code)
+ {
+ case NOT:
+ val = ~ arg0;
+ break;
+
+ case NEG:
+ val = - arg0;
+ break;
+
+ case ABS:
+ val = (arg0 >= 0 ? arg0 : - arg0);
+ break;
+
+ case FFS:
+ /* Don't use ffs here. Instead, get low order bit and then its
+ number. If arg0 is zero, this will return 0, as desired. */
+ arg0 &= GET_MODE_MASK (mode);
+ val = exact_log2 (arg0 & (- arg0)) + 1;
+ break;
+
+ case TRUNCATE:
+ val = arg0;
+ break;
+
+ case ZERO_EXTEND:
+ if (op_mode == VOIDmode)
+ op_mode = mode;
+ if (GET_MODE_BITSIZE (op_mode) == HOST_BITS_PER_WIDE_INT)
+ {
+ /* If we were really extending the mode,
+ we would have to distinguish between zero-extension
+ and sign-extension. */
+ if (width != GET_MODE_BITSIZE (op_mode))
+ abort ();
+ val = arg0;
+ }
+ else if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT)
+ val = arg0 & ~((HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (op_mode));
+ else
+ return 0;
+ break;
+
+ case SIGN_EXTEND:
+ if (op_mode == VOIDmode)
+ op_mode = mode;
+ if (GET_MODE_BITSIZE (op_mode) == HOST_BITS_PER_WIDE_INT)
+ {
+ /* If we were really extending the mode,
+ we would have to distinguish between zero-extension
+ and sign-extension. */
+ if (width != GET_MODE_BITSIZE (op_mode))
+ abort ();
+ val = arg0;
+ }
+ else if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT)
+ {
+ val
+ = arg0 & ~((HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (op_mode));
+ if (val
+ & ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (op_mode) - 1)))
+ val -= (HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (op_mode);
+ }
+ else
+ return 0;
+ break;
+
+ case SQRT:
+ return 0;
+
+ default:
+ abort ();
+ }
+
+ /* Clear the bits that don't belong in our mode,
+ unless they and our sign bit are all one.
+ So we get either a reasonable negative value or a reasonable
+ unsigned value for this mode. */
+ if (width < HOST_BITS_PER_WIDE_INT
+ && ((val & ((HOST_WIDE_INT) (-1) << (width - 1)))
+ != ((HOST_WIDE_INT) (-1) << (width - 1))))
+ val &= (1 << width) - 1;
+
+ return GEN_INT (val);
+ }
+
+ /* We can do some operations on integer CONST_DOUBLEs. Also allow
+ for a DImode operation on a CONST_INT. */
+ else if (GET_MODE (op) == VOIDmode && width == HOST_BITS_PER_INT * 2
+ && (GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT))
+ {
+ HOST_WIDE_INT l1, h1, lv, hv;
+
+ if (GET_CODE (op) == CONST_DOUBLE)
+ l1 = CONST_DOUBLE_LOW (op), h1 = CONST_DOUBLE_HIGH (op);
+ else
+ l1 = INTVAL (op), h1 = l1 < 0 ? -1 : 0;
+
+ switch (code)
+ {
+ case NOT:
+ lv = ~ l1;
+ hv = ~ h1;
+ break;
+
+ case NEG:
+ neg_double (l1, h1, &lv, &hv);
+ break;
+
+ case ABS:
+ if (h1 < 0)
+ neg_double (l1, h1, &lv, &hv);
+ else
+ lv = l1, hv = h1;
+ break;
+
+ case FFS:
+ hv = 0;
+ if (l1 == 0)
+ lv = HOST_BITS_PER_WIDE_INT + exact_log2 (h1 & (-h1)) + 1;
+ else
+ lv = exact_log2 (l1 & (-l1)) + 1;
+ break;
+
+ case TRUNCATE:
+ if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
+ return GEN_INT (l1 & GET_MODE_MASK (mode));
+ else
+ return 0;
+ break;
+
+ case ZERO_EXTEND:
+ if (op_mode == VOIDmode
+ || GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT)
+ return 0;
+
+ hv = 0;
+ lv = l1 & GET_MODE_MASK (op_mode);
+ break;
+
+ case SIGN_EXTEND:
+ if (op_mode == VOIDmode
+ || GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT)
+ return 0;
+ else
+ {
+ lv = l1 & GET_MODE_MASK (op_mode);
+ if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT
+ && (lv & ((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (op_mode) - 1))) != 0)
+ lv -= (HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (op_mode);
+
+ hv = (lv < 0) ? ~ (HOST_WIDE_INT) 0 : 0;
+ }
+ break;
+
+ case SQRT:
+ return 0;
+
+ default:
+ return 0;
+ }
+
+ return immed_double_const (lv, hv, mode);
+ }
+
+#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
+ else if (GET_CODE (op) == CONST_DOUBLE
+ && GET_MODE_CLASS (mode) == MODE_FLOAT)
+ {
+ REAL_VALUE_TYPE d;
+ jmp_buf handler;
+ rtx x;
+
+ if (setjmp (handler))
+ /* There used to be a warning here, but that is inadvisable.
+ People may want to cause traps, and the natural way
+ to do it should not get a warning. */
+ return 0;
+
+ set_float_handler (handler);
+
+ REAL_VALUE_FROM_CONST_DOUBLE (d, op);
+
+ switch (code)
+ {
+ case NEG:
+ d = REAL_VALUE_NEGATE (d);
+ break;
+
+ case ABS:
+ if (REAL_VALUE_NEGATIVE (d))
+ d = REAL_VALUE_NEGATE (d);
+ break;
+
+ case FLOAT_TRUNCATE:
+ d = real_value_truncate (mode, d);
+ break;
+
+ case FLOAT_EXTEND:
+ /* All this does is change the mode. */
+ break;
+
+ case FIX:
+ d = REAL_VALUE_RNDZINT (d);
+ break;
+
+ case UNSIGNED_FIX:
+ d = REAL_VALUE_UNSIGNED_RNDZINT (d);
+ break;
+
+ case SQRT:
+ return 0;
+
+ default:
+ abort ();
+ }
+
+ x = CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
+ set_float_handler (NULL_PTR);
+ return x;
+ }
+ else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE_CLASS (mode) == MODE_INT
+ && width <= HOST_BITS_PER_WIDE_INT && width > 0)
+ {
+ REAL_VALUE_TYPE d;
+ jmp_buf handler;
+ HOST_WIDE_INT val;
+
+ if (setjmp (handler))
+ return 0;
+
+ set_float_handler (handler);
+
+ REAL_VALUE_FROM_CONST_DOUBLE (d, op);
+
+ switch (code)
+ {
+ case FIX:
+ val = REAL_VALUE_FIX (d);
+ break;
+
+ case UNSIGNED_FIX:
+ val = REAL_VALUE_UNSIGNED_FIX (d);
+ break;
+
+ default:
+ abort ();
+ }
+
+ set_float_handler (NULL_PTR);
+
+ /* Clear the bits that don't belong in our mode,
+ unless they and our sign bit are all one.
+ So we get either a reasonable negative value or a reasonable
+ unsigned value for this mode. */
+ if (width < HOST_BITS_PER_WIDE_INT
+ && ((val & ((HOST_WIDE_INT) (-1) << (width - 1)))
+ != ((HOST_WIDE_INT) (-1) << (width - 1))))
+ val &= ((HOST_WIDE_INT) 1 << width) - 1;
+
+ return GEN_INT (val);
+ }
+#endif
+ /* This was formerly used only for non-IEEE float.
+ eggert@twinsun.com says it is safe for IEEE also. */
+ else
+ {
+ /* There are some simplifications we can do even if the operands
+ aren't constant. */
+ switch (code)
+ {
+ case NEG:
+ case NOT:
+ /* (not (not X)) == X, similarly for NEG. */
+ if (GET_CODE (op) == code)
+ return XEXP (op, 0);
+ break;
+
+ case SIGN_EXTEND:
+ /* (sign_extend (truncate (minus (label_ref L1) (label_ref L2))))
+ becomes just the MINUS if its mode is MODE. This allows
+ folding switch statements on machines using casesi (such as
+ the Vax). */
+ if (GET_CODE (op) == TRUNCATE
+ && GET_MODE (XEXP (op, 0)) == mode
+ && GET_CODE (XEXP (op, 0)) == MINUS
+ && GET_CODE (XEXP (XEXP (op, 0), 0)) == LABEL_REF
+ && GET_CODE (XEXP (XEXP (op, 0), 1)) == LABEL_REF)
+ return XEXP (op, 0);
+ break;
+ }
+
+ return 0;
+ }
+}
+
+/* Simplify a binary operation CODE with result mode MODE, operating on OP0
+ and OP1. Return 0 if no simplification is possible.
+
+ Don't use this for relational operations such as EQ or LT.
+ Use simplify_relational_operation instead. */
+
+rtx
+simplify_binary_operation (code, mode, op0, op1)
+ enum rtx_code code;
+ enum machine_mode mode;
+ rtx op0, op1;
+{
+ register HOST_WIDE_INT arg0, arg1, arg0s, arg1s;
+ HOST_WIDE_INT val;
+ int width = GET_MODE_BITSIZE (mode);
+ rtx tem;
+
+ /* Relational operations don't work here. We must know the mode
+ of the operands in order to do the comparison correctly.
+ Assuming a full word can give incorrect results.
+ Consider comparing 128 with -128 in QImode. */
+
+ if (GET_RTX_CLASS (code) == '<')
+ abort ();
+
+#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
+ if (GET_MODE_CLASS (mode) == MODE_FLOAT
+ && GET_CODE (op0) == CONST_DOUBLE && GET_CODE (op1) == CONST_DOUBLE
+ && mode == GET_MODE (op0) && mode == GET_MODE (op1))
+ {
+ REAL_VALUE_TYPE f0, f1, value;
+ jmp_buf handler;
+
+ if (setjmp (handler))
+ return 0;
+
+ set_float_handler (handler);
+
+ REAL_VALUE_FROM_CONST_DOUBLE (f0, op0);
+ REAL_VALUE_FROM_CONST_DOUBLE (f1, op1);
+ f0 = real_value_truncate (mode, f0);
+ f1 = real_value_truncate (mode, f1);
+
+#ifdef REAL_ARITHMETIC
+ REAL_ARITHMETIC (value, rtx_to_tree_code (code), f0, f1);
+#else
+ switch (code)
+ {
+ case PLUS:
+ value = f0 + f1;
+ break;
+ case MINUS:
+ value = f0 - f1;
+ break;
+ case MULT:
+ value = f0 * f1;
+ break;
+ case DIV:
+#ifndef REAL_INFINITY
+ if (f1 == 0)
+ return 0;
+#endif
+ value = f0 / f1;
+ break;
+ case SMIN:
+ value = MIN (f0, f1);
+ break;
+ case SMAX:
+ value = MAX (f0, f1);
+ break;
+ default:
+ abort ();
+ }
+#endif
+
+ value = real_value_truncate (mode, value);
+ set_float_handler (NULL_PTR);
+ return CONST_DOUBLE_FROM_REAL_VALUE (value, mode);
+ }
+#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
+
+ /* We can fold some multi-word operations. */
+ if (GET_MODE_CLASS (mode) == MODE_INT
+ && width == HOST_BITS_PER_WIDE_INT * 2
+ && (GET_CODE (op0) == CONST_DOUBLE || GET_CODE (op0) == CONST_INT)
+ && (GET_CODE (op1) == CONST_DOUBLE || GET_CODE (op1) == CONST_INT))
+ {
+ HOST_WIDE_INT l1, l2, h1, h2, lv, hv;
+
+ if (GET_CODE (op0) == CONST_DOUBLE)
+ l1 = CONST_DOUBLE_LOW (op0), h1 = CONST_DOUBLE_HIGH (op0);
+ else
+ l1 = INTVAL (op0), h1 = l1 < 0 ? -1 : 0;
+
+ if (GET_CODE (op1) == CONST_DOUBLE)
+ l2 = CONST_DOUBLE_LOW (op1), h2 = CONST_DOUBLE_HIGH (op1);
+ else
+ l2 = INTVAL (op1), h2 = l2 < 0 ? -1 : 0;
+
+ switch (code)
+ {
+ case MINUS:
+ /* A - B == A + (-B). */
+ neg_double (l2, h2, &lv, &hv);
+ l2 = lv, h2 = hv;
+
+ /* .. fall through ... */
+
+ case PLUS:
+ add_double (l1, h1, l2, h2, &lv, &hv);
+ break;
+
+ case MULT:
+ mul_double (l1, h1, l2, h2, &lv, &hv);
+ break;
+
+ case DIV: case MOD: case UDIV: case UMOD:
+ /* We'd need to include tree.h to do this and it doesn't seem worth
+ it. */
+ return 0;
+
+ case AND:
+ lv = l1 & l2, hv = h1 & h2;
+ break;
+
+ case IOR:
+ lv = l1 | l2, hv = h1 | h2;
+ break;
+
+ case XOR:
+ lv = l1 ^ l2, hv = h1 ^ h2;
+ break;
+
+ case SMIN:
+ if (h1 < h2
+ || (h1 == h2
+ && ((unsigned HOST_WIDE_INT) l1
+ < (unsigned HOST_WIDE_INT) l2)))
+ lv = l1, hv = h1;
+ else
+ lv = l2, hv = h2;
+ break;
+
+ case SMAX:
+ if (h1 > h2
+ || (h1 == h2
+ && ((unsigned HOST_WIDE_INT) l1
+ > (unsigned HOST_WIDE_INT) l2)))
+ lv = l1, hv = h1;
+ else
+ lv = l2, hv = h2;
+ break;
+
+ case UMIN:
+ if ((unsigned HOST_WIDE_INT) h1 < (unsigned HOST_WIDE_INT) h2
+ || (h1 == h2
+ && ((unsigned HOST_WIDE_INT) l1
+ < (unsigned HOST_WIDE_INT) l2)))
+ lv = l1, hv = h1;
+ else
+ lv = l2, hv = h2;
+ break;
+
+ case UMAX:
+ if ((unsigned HOST_WIDE_INT) h1 > (unsigned HOST_WIDE_INT) h2
+ || (h1 == h2
+ && ((unsigned HOST_WIDE_INT) l1
+ > (unsigned HOST_WIDE_INT) l2)))
+ lv = l1, hv = h1;
+ else
+ lv = l2, hv = h2;
+ break;
+
+ case LSHIFTRT: case ASHIFTRT:
+ case ASHIFT:
+ case ROTATE: case ROTATERT:
+#ifdef SHIFT_COUNT_TRUNCATED
+ if (SHIFT_COUNT_TRUNCATED)
+ l2 &= (GET_MODE_BITSIZE (mode) - 1), h2 = 0;
+#endif
+
+ if (h2 != 0 || l2 < 0 || l2 >= GET_MODE_BITSIZE (mode))
+ return 0;
+
+ if (code == LSHIFTRT || code == ASHIFTRT)
+ rshift_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv,
+ code == ASHIFTRT);
+ else if (code == ASHIFT)
+ lshift_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv, 1);
+ else if (code == ROTATE)
+ lrotate_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv);
+ else /* code == ROTATERT */
+ rrotate_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return immed_double_const (lv, hv, mode);
+ }
+
+ if (GET_CODE (op0) != CONST_INT || GET_CODE (op1) != CONST_INT
+ || width > HOST_BITS_PER_WIDE_INT || width == 0)
+ {
+ /* Even if we can't compute a constant result,
+ there are some cases worth simplifying. */
+
+ switch (code)
+ {
+ case PLUS:
+ /* In IEEE floating point, x+0 is not the same as x. Similarly
+ for the other optimizations below. */
+ if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
+ && FLOAT_MODE_P (mode) && ! flag_fast_math)
+ break;
+
+ if (op1 == CONST0_RTX (mode))
+ return op0;
+
+ /* ((-a) + b) -> (b - a) and similarly for (a + (-b)) */
+ if (GET_CODE (op0) == NEG)
+ return cse_gen_binary (MINUS, mode, op1, XEXP (op0, 0));
+ else if (GET_CODE (op1) == NEG)
+ return cse_gen_binary (MINUS, mode, op0, XEXP (op1, 0));
+
+ /* Handle both-operands-constant cases. We can only add
+ CONST_INTs to constants since the sum of relocatable symbols
+ can't be handled by most assemblers. Don't add CONST_INT
+ to CONST_INT since overflow won't be computed properly if wider
+ than HOST_BITS_PER_WIDE_INT. */
+
+ if (CONSTANT_P (op0) && GET_MODE (op0) != VOIDmode
+ && GET_CODE (op1) == CONST_INT)
+ return plus_constant (op0, INTVAL (op1));
+ else if (CONSTANT_P (op1) && GET_MODE (op1) != VOIDmode
+ && GET_CODE (op0) == CONST_INT)
+ return plus_constant (op1, INTVAL (op0));
+
+ /* See if this is something like X * C - X or vice versa or
+ if the multiplication is written as a shift. If so, we can
+ distribute and make a new multiply, shift, or maybe just
+ have X (if C is 2 in the example above). But don't make
+ real multiply if we didn't have one before. */
+
+ if (! FLOAT_MODE_P (mode))
+ {
+ HOST_WIDE_INT coeff0 = 1, coeff1 = 1;
+ rtx lhs = op0, rhs = op1;
+ int had_mult = 0;
+
+ if (GET_CODE (lhs) == NEG)
+ coeff0 = -1, lhs = XEXP (lhs, 0);
+ else if (GET_CODE (lhs) == MULT
+ && GET_CODE (XEXP (lhs, 1)) == CONST_INT)
+ {
+ coeff0 = INTVAL (XEXP (lhs, 1)), lhs = XEXP (lhs, 0);
+ had_mult = 1;
+ }
+ else if (GET_CODE (lhs) == ASHIFT
+ && GET_CODE (XEXP (lhs, 1)) == CONST_INT
+ && INTVAL (XEXP (lhs, 1)) >= 0
+ && INTVAL (XEXP (lhs, 1)) < HOST_BITS_PER_WIDE_INT)
+ {
+ coeff0 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (lhs, 1));
+ lhs = XEXP (lhs, 0);
+ }
+
+ if (GET_CODE (rhs) == NEG)
+ coeff1 = -1, rhs = XEXP (rhs, 0);
+ else if (GET_CODE (rhs) == MULT
+ && GET_CODE (XEXP (rhs, 1)) == CONST_INT)
+ {
+ coeff1 = INTVAL (XEXP (rhs, 1)), rhs = XEXP (rhs, 0);
+ had_mult = 1;
+ }
+ else if (GET_CODE (rhs) == ASHIFT
+ && GET_CODE (XEXP (rhs, 1)) == CONST_INT
+ && INTVAL (XEXP (rhs, 1)) >= 0
+ && INTVAL (XEXP (rhs, 1)) < HOST_BITS_PER_WIDE_INT)
+ {
+ coeff1 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (rhs, 1));
+ rhs = XEXP (rhs, 0);
+ }
+
+ if (rtx_equal_p (lhs, rhs))
+ {
+ tem = cse_gen_binary (MULT, mode, lhs,
+ GEN_INT (coeff0 + coeff1));
+ return (GET_CODE (tem) == MULT && ! had_mult) ? 0 : tem;
+ }
+ }
+
+ /* If one of the operands is a PLUS or a MINUS, see if we can
+ simplify this by the associative law.
+ Don't use the associative law for floating point.
+ The inaccuracy makes it nonassociative,
+ and subtle programs can break if operations are associated. */
+
+ if (INTEGRAL_MODE_P (mode)
+ && (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS
+ || GET_CODE (op1) == PLUS || GET_CODE (op1) == MINUS)
+ && (tem = simplify_plus_minus (code, mode, op0, op1)) != 0)
+ return tem;
+ break;
+
+ case COMPARE:
+#ifdef HAVE_cc0
+ /* Convert (compare FOO (const_int 0)) to FOO unless we aren't
+ using cc0, in which case we want to leave it as a COMPARE
+ so we can distinguish it from a register-register-copy.
+
+ In IEEE floating point, x-0 is not the same as x. */
+
+ if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+ || ! FLOAT_MODE_P (mode) || flag_fast_math)
+ && op1 == CONST0_RTX (mode))
+ return op0;
+#else
+ /* Do nothing here. */
+#endif
+ break;
+
+ case MINUS:
+ /* None of these optimizations can be done for IEEE
+ floating point. */
+ if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
+ && FLOAT_MODE_P (mode) && ! flag_fast_math)
+ break;
+
+ /* We can't assume x-x is 0 even with non-IEEE floating point,
+ but since it is zero except in very strange circumstances, we
+ will treat it as zero with -ffast-math. */
+ if (rtx_equal_p (op0, op1)
+ && ! side_effects_p (op0)
+ && (! FLOAT_MODE_P (mode) || flag_fast_math))
+ return CONST0_RTX (mode);
+
+ /* Change subtraction from zero into negation. */
+ if (op0 == CONST0_RTX (mode))
+ return gen_rtx (NEG, mode, op1);
+
+ /* (-1 - a) is ~a. */
+ if (op0 == constm1_rtx)
+ return gen_rtx (NOT, mode, op1);
+
+ /* Subtracting 0 has no effect. */
+ if (op1 == CONST0_RTX (mode))
+ return op0;
+
+ /* See if this is something like X * C - X or vice versa or
+ if the multiplication is written as a shift. If so, we can
+ distribute and make a new multiply, shift, or maybe just
+ have X (if C is 2 in the example above). But don't make
+ real multiply if we didn't have one before. */
+
+ if (! FLOAT_MODE_P (mode))
+ {
+ HOST_WIDE_INT coeff0 = 1, coeff1 = 1;
+ rtx lhs = op0, rhs = op1;
+ int had_mult = 0;
+
+ if (GET_CODE (lhs) == NEG)
+ coeff0 = -1, lhs = XEXP (lhs, 0);
+ else if (GET_CODE (lhs) == MULT
+ && GET_CODE (XEXP (lhs, 1)) == CONST_INT)
+ {
+ coeff0 = INTVAL (XEXP (lhs, 1)), lhs = XEXP (lhs, 0);
+ had_mult = 1;
+ }
+ else if (GET_CODE (lhs) == ASHIFT
+ && GET_CODE (XEXP (lhs, 1)) == CONST_INT
+ && INTVAL (XEXP (lhs, 1)) >= 0
+ && INTVAL (XEXP (lhs, 1)) < HOST_BITS_PER_WIDE_INT)
+ {
+ coeff0 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (lhs, 1));
+ lhs = XEXP (lhs, 0);
+ }
+
+ if (GET_CODE (rhs) == NEG)
+ coeff1 = - 1, rhs = XEXP (rhs, 0);
+ else if (GET_CODE (rhs) == MULT
+ && GET_CODE (XEXP (rhs, 1)) == CONST_INT)
+ {
+ coeff1 = INTVAL (XEXP (rhs, 1)), rhs = XEXP (rhs, 0);
+ had_mult = 1;
+ }
+ else if (GET_CODE (rhs) == ASHIFT
+ && GET_CODE (XEXP (rhs, 1)) == CONST_INT
+ && INTVAL (XEXP (rhs, 1)) >= 0
+ && INTVAL (XEXP (rhs, 1)) < HOST_BITS_PER_WIDE_INT)
+ {
+ coeff1 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (rhs, 1));
+ rhs = XEXP (rhs, 0);
+ }
+
+ if (rtx_equal_p (lhs, rhs))
+ {
+ tem = cse_gen_binary (MULT, mode, lhs,
+ GEN_INT (coeff0 - coeff1));
+ return (GET_CODE (tem) == MULT && ! had_mult) ? 0 : tem;
+ }
+ }
+
+ /* (a - (-b)) -> (a + b). */
+ if (GET_CODE (op1) == NEG)
+ return cse_gen_binary (PLUS, mode, op0, XEXP (op1, 0));
+
+ /* If one of the operands is a PLUS or a MINUS, see if we can
+ simplify this by the associative law.
+ Don't use the associative law for floating point.
+ The inaccuracy makes it nonassociative,
+ and subtle programs can break if operations are associated. */
+
+ if (INTEGRAL_MODE_P (mode)
+ && (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS
+ || GET_CODE (op1) == PLUS || GET_CODE (op1) == MINUS)
+ && (tem = simplify_plus_minus (code, mode, op0, op1)) != 0)
+ return tem;
+
+ /* Don't let a relocatable value get a negative coeff. */
+ if (GET_CODE (op1) == CONST_INT && GET_MODE (op0) != VOIDmode)
+ return plus_constant (op0, - INTVAL (op1));
+ break;
+
+ case MULT:
+ if (op1 == constm1_rtx)
+ {
+ tem = simplify_unary_operation (NEG, mode, op0, mode);
+
+ return tem ? tem : gen_rtx (NEG, mode, op0);
+ }
+
+ /* In IEEE floating point, x*0 is not always 0. */
+ if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+ || ! FLOAT_MODE_P (mode) || flag_fast_math)
+ && op1 == CONST0_RTX (mode)
+ && ! side_effects_p (op0))
+ return op1;
+
+ /* In IEEE floating point, x*1 is not equivalent to x for nans.
+ However, ANSI says we can drop signals,
+ so we can do this anyway. */
+ if (op1 == CONST1_RTX (mode))
+ return op0;
+
+ /* Convert multiply by constant power of two into shift unless
+ we are still generating RTL. This test is a kludge. */
+ if (GET_CODE (op1) == CONST_INT
+ && (val = exact_log2 (INTVAL (op1))) >= 0
+ && ! rtx_equal_function_value_matters)
+ return gen_rtx (ASHIFT, mode, op0, GEN_INT (val));
+
+ if (GET_CODE (op1) == CONST_DOUBLE
+ && GET_MODE_CLASS (GET_MODE (op1)) == MODE_FLOAT)
+ {
+ REAL_VALUE_TYPE d;
+ jmp_buf handler;
+ int op1is2, op1ism1;
+
+ if (setjmp (handler))
+ return 0;
+
+ set_float_handler (handler);
+ REAL_VALUE_FROM_CONST_DOUBLE (d, op1);
+ op1is2 = REAL_VALUES_EQUAL (d, dconst2);
+ op1ism1 = REAL_VALUES_EQUAL (d, dconstm1);
+ set_float_handler (NULL_PTR);
+
+ /* x*2 is x+x and x*(-1) is -x */
+ if (op1is2 && GET_MODE (op0) == mode)
+ return gen_rtx (PLUS, mode, op0, copy_rtx (op0));
+
+ else if (op1ism1 && GET_MODE (op0) == mode)
+ return gen_rtx (NEG, mode, op0);
+ }
+ break;
+
+ case IOR:
+ if (op1 == const0_rtx)
+ return op0;
+ if (GET_CODE (op1) == CONST_INT
+ && (INTVAL (op1) & GET_MODE_MASK (mode)) == GET_MODE_MASK (mode))
+ return op1;
+ if (rtx_equal_p (op0, op1) && ! side_effects_p (op0))
+ return op0;
+ /* A | (~A) -> -1 */
+ if (((GET_CODE (op0) == NOT && rtx_equal_p (XEXP (op0, 0), op1))
+ || (GET_CODE (op1) == NOT && rtx_equal_p (XEXP (op1, 0), op0)))
+ && ! side_effects_p (op0)
+ && GET_MODE_CLASS (mode) != MODE_CC)
+ return constm1_rtx;
+ break;
+
+ case XOR:
+ if (op1 == const0_rtx)
+ return op0;
+ if (GET_CODE (op1) == CONST_INT
+ && (INTVAL (op1) & GET_MODE_MASK (mode)) == GET_MODE_MASK (mode))
+ return gen_rtx (NOT, mode, op0);
+ if (op0 == op1 && ! side_effects_p (op0)
+ && GET_MODE_CLASS (mode) != MODE_CC)
+ return const0_rtx;
+ break;
+
+ case AND:
+ if (op1 == const0_rtx && ! side_effects_p (op0))
+ return const0_rtx;
+ if (GET_CODE (op1) == CONST_INT
+ && (INTVAL (op1) & GET_MODE_MASK (mode)) == GET_MODE_MASK (mode))
+ return op0;
+ if (op0 == op1 && ! side_effects_p (op0)
+ && GET_MODE_CLASS (mode) != MODE_CC)
+ return op0;
+ /* A & (~A) -> 0 */
+ if (((GET_CODE (op0) == NOT && rtx_equal_p (XEXP (op0, 0), op1))
+ || (GET_CODE (op1) == NOT && rtx_equal_p (XEXP (op1, 0), op0)))
+ && ! side_effects_p (op0)
+ && GET_MODE_CLASS (mode) != MODE_CC)
+ return const0_rtx;
+ break;
+
+ case UDIV:
+ /* Convert divide by power of two into shift (divide by 1 handled
+ below). */
+ if (GET_CODE (op1) == CONST_INT
+ && (arg1 = exact_log2 (INTVAL (op1))) > 0)
+ return gen_rtx (LSHIFTRT, mode, op0, GEN_INT (arg1));
+
+ /* ... fall through ... */
+
+ case DIV:
+ if (op1 == CONST1_RTX (mode))
+ return op0;
+
+ /* In IEEE floating point, 0/x is not always 0. */
+ if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+ || ! FLOAT_MODE_P (mode) || flag_fast_math)
+ && op0 == CONST0_RTX (mode)
+ && ! side_effects_p (op1))
+ return op0;
+
+#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
+ /* Change division by a constant into multiplication. Only do
+ this with -ffast-math until an expert says it is safe in
+ general. */
+ else if (GET_CODE (op1) == CONST_DOUBLE
+ && GET_MODE_CLASS (GET_MODE (op1)) == MODE_FLOAT
+ && op1 != CONST0_RTX (mode)
+ && flag_fast_math)
+ {
+ REAL_VALUE_TYPE d;
+ REAL_VALUE_FROM_CONST_DOUBLE (d, op1);
+
+ if (! REAL_VALUES_EQUAL (d, dconst0))
+ {
+#if defined (REAL_ARITHMETIC)
+ REAL_ARITHMETIC (d, rtx_to_tree_code (DIV), dconst1, d);
+ return gen_rtx (MULT, mode, op0,
+ CONST_DOUBLE_FROM_REAL_VALUE (d, mode));
+#else
+ return gen_rtx (MULT, mode, op0,
+ CONST_DOUBLE_FROM_REAL_VALUE (1./d, mode));
+#endif
+ }
+ }
+#endif
+ break;
+
+ case UMOD:
+ /* Handle modulus by power of two (mod with 1 handled below). */
+ if (GET_CODE (op1) == CONST_INT
+ && exact_log2 (INTVAL (op1)) > 0)
+ return gen_rtx (AND, mode, op0, GEN_INT (INTVAL (op1) - 1));
+
+ /* ... fall through ... */
+
+ case MOD:
+ if ((op0 == const0_rtx || op1 == const1_rtx)
+ && ! side_effects_p (op0) && ! side_effects_p (op1))
+ return const0_rtx;
+ break;
+
+ case ROTATERT:
+ case ROTATE:
+ /* Rotating ~0 always results in ~0. */
+ if (GET_CODE (op0) == CONST_INT && width <= HOST_BITS_PER_WIDE_INT
+ && INTVAL (op0) == GET_MODE_MASK (mode)
+ && ! side_effects_p (op1))
+ return op0;
+
+ /* ... fall through ... */
+
+ case ASHIFT:
+ case ASHIFTRT:
+ case LSHIFTRT:
+ if (op1 == const0_rtx)
+ return op0;
+ if (op0 == const0_rtx && ! side_effects_p (op1))
+ return op0;
+ break;
+
+ case SMIN:
+ if (width <= HOST_BITS_PER_WIDE_INT && GET_CODE (op1) == CONST_INT
+ && INTVAL (op1) == (HOST_WIDE_INT) 1 << (width -1)
+ && ! side_effects_p (op0))
+ return op1;
+ else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0))
+ return op0;
+ break;
+
+ case SMAX:
+ if (width <= HOST_BITS_PER_WIDE_INT && GET_CODE (op1) == CONST_INT
+ && (INTVAL (op1)
+ == (unsigned HOST_WIDE_INT) GET_MODE_MASK (mode) >> 1)
+ && ! side_effects_p (op0))
+ return op1;
+ else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0))
+ return op0;
+ break;
+
+ case UMIN:
+ if (op1 == const0_rtx && ! side_effects_p (op0))
+ return op1;
+ else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0))
+ return op0;
+ break;
+
+ case UMAX:
+ if (op1 == constm1_rtx && ! side_effects_p (op0))
+ return op1;
+ else if (rtx_equal_p (op0, op1) && ! side_effects_p (op0))
+ return op0;
+ break;
+
+ default:
+ abort ();
+ }
+
+ return 0;
+ }
+
+ /* Get the integer argument values in two forms:
+ zero-extended in ARG0, ARG1 and sign-extended in ARG0S, ARG1S. */
+
+ arg0 = INTVAL (op0);
+ arg1 = INTVAL (op1);
+
+ if (width < HOST_BITS_PER_WIDE_INT)
+ {
+ arg0 &= ((HOST_WIDE_INT) 1 << width) - 1;
+ arg1 &= ((HOST_WIDE_INT) 1 << width) - 1;
+
+ arg0s = arg0;
+ if (arg0s & ((HOST_WIDE_INT) 1 << (width - 1)))
+ arg0s |= ((HOST_WIDE_INT) (-1) << width);
+
+ arg1s = arg1;
+ if (arg1s & ((HOST_WIDE_INT) 1 << (width - 1)))
+ arg1s |= ((HOST_WIDE_INT) (-1) << width);
+ }
+ else
+ {
+ arg0s = arg0;
+ arg1s = arg1;
+ }
+
+ /* Compute the value of the arithmetic. */
+
+ switch (code)
+ {
+ case PLUS:
+ val = arg0s + arg1s;
+ break;
+
+ case MINUS:
+ val = arg0s - arg1s;
+ break;
+
+ case MULT:
+ val = arg0s * arg1s;
+ break;
+
+ case DIV:
+ if (arg1s == 0)
+ return 0;
+ val = arg0s / arg1s;
+ break;
+
+ case MOD:
+ if (arg1s == 0)
+ return 0;
+ val = arg0s % arg1s;
+ break;
+
+ case UDIV:
+ if (arg1 == 0)
+ return 0;
+ val = (unsigned HOST_WIDE_INT) arg0 / arg1;
+ break;
+
+ case UMOD:
+ if (arg1 == 0)
+ return 0;
+ val = (unsigned HOST_WIDE_INT) arg0 % arg1;
+ break;
+
+ case AND:
+ val = arg0 & arg1;
+ break;
+
+ case IOR:
+ val = arg0 | arg1;
+ break;
+
+ case XOR:
+ val = arg0 ^ arg1;
+ break;
+
+ case LSHIFTRT:
+ /* If shift count is undefined, don't fold it; let the machine do
+ what it wants. But truncate it if the machine will do that. */
+ if (arg1 < 0)
+ return 0;
+
+#ifdef SHIFT_COUNT_TRUNCATED
+ if (SHIFT_COUNT_TRUNCATED)
+ arg1 %= width;
+#endif
+
+ val = ((unsigned HOST_WIDE_INT) arg0) >> arg1;
+ break;
+
+ case ASHIFT:
+ if (arg1 < 0)
+ return 0;
+
+#ifdef SHIFT_COUNT_TRUNCATED
+ if (SHIFT_COUNT_TRUNCATED)
+ arg1 %= width;
+#endif
+
+ val = ((unsigned HOST_WIDE_INT) arg0) << arg1;
+ break;
+
+ case ASHIFTRT:
+ if (arg1 < 0)
+ return 0;
+
+#ifdef SHIFT_COUNT_TRUNCATED
+ if (SHIFT_COUNT_TRUNCATED)
+ arg1 %= width;
+#endif
+
+ val = arg0s >> arg1;
+
+ /* Bootstrap compiler may not have sign extended the right shift.
+ Manually extend the sign to insure bootstrap cc matches gcc. */
+ if (arg0s < 0 && arg1 > 0)
+ val |= ((HOST_WIDE_INT) -1) << (HOST_BITS_PER_WIDE_INT - arg1);
+
+ break;
+
+ case ROTATERT:
+ if (arg1 < 0)
+ return 0;
+
+ arg1 %= width;
+ val = ((((unsigned HOST_WIDE_INT) arg0) << (width - arg1))
+ | (((unsigned HOST_WIDE_INT) arg0) >> arg1));
+ break;
+
+ case ROTATE:
+ if (arg1 < 0)
+ return 0;
+
+ arg1 %= width;
+ val = ((((unsigned HOST_WIDE_INT) arg0) << arg1)
+ | (((unsigned HOST_WIDE_INT) arg0) >> (width - arg1)));
+ break;
+
+ case COMPARE:
+ /* Do nothing here. */
+ return 0;
+
+ case SMIN:
+ val = arg0s <= arg1s ? arg0s : arg1s;
+ break;
+
+ case UMIN:
+ val = ((unsigned HOST_WIDE_INT) arg0
+ <= (unsigned HOST_WIDE_INT) arg1 ? arg0 : arg1);
+ break;
+
+ case SMAX:
+ val = arg0s > arg1s ? arg0s : arg1s;
+ break;
+
+ case UMAX:
+ val = ((unsigned HOST_WIDE_INT) arg0
+ > (unsigned HOST_WIDE_INT) arg1 ? arg0 : arg1);
+ break;
+
+ default:
+ abort ();
+ }
+
+ /* Clear the bits that don't belong in our mode, unless they and our sign
+ bit are all one. So we get either a reasonable negative value or a
+ reasonable unsigned value for this mode. */
+ if (width < HOST_BITS_PER_WIDE_INT
+ && ((val & ((HOST_WIDE_INT) (-1) << (width - 1)))
+ != ((HOST_WIDE_INT) (-1) << (width - 1))))
+ val &= ((HOST_WIDE_INT) 1 << width) - 1;
+
+ return GEN_INT (val);
+}
+
+/* Simplify a PLUS or MINUS, at least one of whose operands may be another
+ PLUS or MINUS.
+
+ Rather than test for specific case, we do this by a brute-force method
+ and do all possible simplifications until no more changes occur. Then
+ we rebuild the operation. */
+
+static rtx
+simplify_plus_minus (code, mode, op0, op1)
+ enum rtx_code code;
+ enum machine_mode mode;
+ rtx op0, op1;
+{
+ rtx ops[8];
+ int negs[8];
+ rtx result, tem;
+ int n_ops = 2, input_ops = 2, input_consts = 0, n_consts = 0;
+ int first = 1, negate = 0, changed;
+ int i, j;
+
+ bzero ((char *) ops, sizeof ops);
+
+ /* Set up the two operands and then expand them until nothing has been
+ changed. If we run out of room in our array, give up; this should
+ almost never happen. */
+
+ ops[0] = op0, ops[1] = op1, negs[0] = 0, negs[1] = (code == MINUS);
+
+ changed = 1;
+ while (changed)
+ {
+ changed = 0;
+
+ for (i = 0; i < n_ops; i++)
+ switch (GET_CODE (ops[i]))
+ {
+ case PLUS:
+ case MINUS:
+ if (n_ops == 7)
+ return 0;
+
+ ops[n_ops] = XEXP (ops[i], 1);
+ negs[n_ops++] = GET_CODE (ops[i]) == MINUS ? !negs[i] : negs[i];
+ ops[i] = XEXP (ops[i], 0);
+ input_ops++;
+ changed = 1;
+ break;
+
+ case NEG:
+ ops[i] = XEXP (ops[i], 0);
+ negs[i] = ! negs[i];
+ changed = 1;
+ break;
+
+ case CONST:
+ ops[i] = XEXP (ops[i], 0);
+ input_consts++;
+ changed = 1;
+ break;
+
+ case NOT:
+ /* ~a -> (-a - 1) */
+ if (n_ops != 7)
+ {
+ ops[n_ops] = constm1_rtx;
+ negs[n_ops++] = negs[i];
+ ops[i] = XEXP (ops[i], 0);
+ negs[i] = ! negs[i];
+ changed = 1;
+ }
+ break;
+
+ case CONST_INT:
+ if (negs[i])
+ ops[i] = GEN_INT (- INTVAL (ops[i])), negs[i] = 0, changed = 1;
+ break;
+ }
+ }
+
+ /* If we only have two operands, we can't do anything. */
+ if (n_ops <= 2)
+ return 0;
+
+ /* Now simplify each pair of operands until nothing changes. The first
+ time through just simplify constants against each other. */
+
+ changed = 1;
+ while (changed)
+ {
+ changed = first;
+
+ for (i = 0; i < n_ops - 1; i++)
+ for (j = i + 1; j < n_ops; j++)
+ if (ops[i] != 0 && ops[j] != 0
+ && (! first || (CONSTANT_P (ops[i]) && CONSTANT_P (ops[j]))))
+ {
+ rtx lhs = ops[i], rhs = ops[j];
+ enum rtx_code ncode = PLUS;
+
+ if (negs[i] && ! negs[j])
+ lhs = ops[j], rhs = ops[i], ncode = MINUS;
+ else if (! negs[i] && negs[j])
+ ncode = MINUS;
+
+ tem = simplify_binary_operation (ncode, mode, lhs, rhs);
+ if (tem)
+ {
+ ops[i] = tem, ops[j] = 0;
+ negs[i] = negs[i] && negs[j];
+ if (GET_CODE (tem) == NEG)
+ ops[i] = XEXP (tem, 0), negs[i] = ! negs[i];
+
+ if (GET_CODE (ops[i]) == CONST_INT && negs[i])
+ ops[i] = GEN_INT (- INTVAL (ops[i])), negs[i] = 0;
+ changed = 1;
+ }
+ }
+
+ first = 0;
+ }
+
+ /* Pack all the operands to the lower-numbered entries and give up if
+ we didn't reduce the number of operands we had. Make sure we
+ count a CONST as two operands. If we have the same number of
+ operands, but have made more CONSTs than we had, this is also
+ an improvement, so accept it. */
+
+ for (i = 0, j = 0; j < n_ops; j++)
+ if (ops[j] != 0)
+ {
+ ops[i] = ops[j], negs[i++] = negs[j];
+ if (GET_CODE (ops[j]) == CONST)
+ n_consts++;
+ }
+
+ if (i + n_consts > input_ops
+ || (i + n_consts == input_ops && n_consts <= input_consts))
+ return 0;
+
+ n_ops = i;
+
+ /* If we have a CONST_INT, put it last. */
+ for (i = 0; i < n_ops - 1; i++)
+ if (GET_CODE (ops[i]) == CONST_INT)
+ {
+ tem = ops[n_ops - 1], ops[n_ops - 1] = ops[i] , ops[i] = tem;
+ j = negs[n_ops - 1], negs[n_ops - 1] = negs[i], negs[i] = j;
+ }
+
+ /* Put a non-negated operand first. If there aren't any, make all
+ operands positive and negate the whole thing later. */
+ for (i = 0; i < n_ops && negs[i]; i++)
+ ;
+
+ if (i == n_ops)
+ {
+ for (i = 0; i < n_ops; i++)
+ negs[i] = 0;
+ negate = 1;
+ }
+ else if (i != 0)
+ {
+ tem = ops[0], ops[0] = ops[i], ops[i] = tem;
+ j = negs[0], negs[0] = negs[i], negs[i] = j;
+ }
+
+ /* Now make the result by performing the requested operations. */
+ result = ops[0];
+ for (i = 1; i < n_ops; i++)
+ result = cse_gen_binary (negs[i] ? MINUS : PLUS, mode, result, ops[i]);
+
+ return negate ? gen_rtx (NEG, mode, result) : result;
+}
+
+/* Make a binary operation by properly ordering the operands and
+ seeing if the expression folds. */
+
+static rtx
+cse_gen_binary (code, mode, op0, op1)
+ enum rtx_code code;
+ enum machine_mode mode;
+ rtx op0, op1;
+{
+ rtx tem;
+
+ /* Put complex operands first and constants second if commutative. */
+ if (GET_RTX_CLASS (code) == 'c'
+ && ((CONSTANT_P (op0) && GET_CODE (op1) != CONST_INT)
+ || (GET_RTX_CLASS (GET_CODE (op0)) == 'o'
+ && GET_RTX_CLASS (GET_CODE (op1)) != 'o')
+ || (GET_CODE (op0) == SUBREG
+ && GET_RTX_CLASS (GET_CODE (SUBREG_REG (op0))) == 'o'
+ && GET_RTX_CLASS (GET_CODE (op1)) != 'o')))
+ tem = op0, op0 = op1, op1 = tem;
+
+ /* If this simplifies, do it. */
+ tem = simplify_binary_operation (code, mode, op0, op1);
+
+ if (tem)
+ return tem;
+
+ /* Handle addition and subtraction of CONST_INT specially. Otherwise,
+ just form the operation. */
+
+ if (code == PLUS && GET_CODE (op1) == CONST_INT
+ && GET_MODE (op0) != VOIDmode)
+ return plus_constant (op0, INTVAL (op1));
+ else if (code == MINUS && GET_CODE (op1) == CONST_INT
+ && GET_MODE (op0) != VOIDmode)
+ return plus_constant (op0, - INTVAL (op1));
+ else
+ return gen_rtx (code, mode, op0, op1);
+}
+
+/* Like simplify_binary_operation except used for relational operators.
+ MODE is the mode of the operands, not that of the result. If MODE
+ is VOIDmode, both operands must also be VOIDmode and we compare the
+ operands in "infinite precision".
+
+ If no simplification is possible, this function returns zero. Otherwise,
+ it returns either const_true_rtx or const0_rtx. */
+
+rtx
+simplify_relational_operation (code, mode, op0, op1)
+ enum rtx_code code;
+ enum machine_mode mode;
+ rtx op0, op1;
+{
+ int equal, op0lt, op0ltu, op1lt, op1ltu;
+ rtx tem;
+
+ /* If op0 is a compare, extract the comparison arguments from it. */
+ if (GET_CODE (op0) == COMPARE && op1 == const0_rtx)
+ op1 = XEXP (op0, 1), op0 = XEXP (op0, 0);
+
+ /* We can't simplify MODE_CC values since we don't know what the
+ actual comparison is. */
+ if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC
+#ifdef HAVE_cc0
+ || op0 == cc0_rtx
+#endif
+ )
+ return 0;
+
+ /* For integer comparisons of A and B maybe we can simplify A - B and can
+ then simplify a comparison of that with zero. If A and B are both either
+ a register or a CONST_INT, this can't help; testing for these cases will
+ prevent infinite recursion here and speed things up.
+
+ If CODE is an unsigned comparison, we can only do this if A - B is a
+ constant integer, and then we have to compare that integer with zero as a
+ signed comparison. Note that this will give the incorrect result from
+ comparisons that overflow. Since these are undefined, this is probably
+ OK. If it causes a problem, we can check for A or B being an address
+ (fp + const or SYMBOL_REF) and only do it in that case. */
+
+ if (INTEGRAL_MODE_P (mode) && op1 != const0_rtx
+ && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == CONST_INT)
+ && (GET_CODE (op1) == REG || GET_CODE (op1) == CONST_INT))
+ && 0 != (tem = simplify_binary_operation (MINUS, mode, op0, op1))
+ && (GET_CODE (tem) == CONST_INT
+ || (code != GTU && code != GEU &&
+ code != LTU && code != LEU)))
+ return simplify_relational_operation (signed_condition (code),
+ mode, tem, const0_rtx);
+
+ /* For non-IEEE floating-point, if the two operands are equal, we know the
+ result. */
+ if (rtx_equal_p (op0, op1)
+ && (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+ || ! FLOAT_MODE_P (GET_MODE (op0)) || flag_fast_math))
+ equal = 1, op0lt = 0, op0ltu = 0, op1lt = 0, op1ltu = 0;
+
+ /* If the operands are floating-point constants, see if we can fold
+ the result. */
+#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
+ else if (GET_CODE (op0) == CONST_DOUBLE && GET_CODE (op1) == CONST_DOUBLE
+ && GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT)
+ {
+ REAL_VALUE_TYPE d0, d1;
+ jmp_buf handler;
+
+ if (setjmp (handler))
+ return 0;
+
+ set_float_handler (handler);
+ REAL_VALUE_FROM_CONST_DOUBLE (d0, op0);
+ REAL_VALUE_FROM_CONST_DOUBLE (d1, op1);
+ equal = REAL_VALUES_EQUAL (d0, d1);
+ op0lt = op0ltu = REAL_VALUES_LESS (d0, d1);
+ op1lt = op1ltu = REAL_VALUES_LESS (d1, d0);
+ set_float_handler (NULL_PTR);
+ }
+#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
+
+ /* Otherwise, see if the operands are both integers. */
+ else if ((GET_MODE_CLASS (mode) == MODE_INT || mode == VOIDmode)
+ && (GET_CODE (op0) == CONST_DOUBLE || GET_CODE (op0) == CONST_INT)
+ && (GET_CODE (op1) == CONST_DOUBLE || GET_CODE (op1) == CONST_INT))
+ {
+ int width = GET_MODE_BITSIZE (mode);
+ HOST_WIDE_INT l0s, h0s, l1s, h1s;
+ unsigned HOST_WIDE_INT l0u, h0u, l1u, h1u;
+
+ /* Get the two words comprising each integer constant. */
+ if (GET_CODE (op0) == CONST_DOUBLE)
+ {
+ l0u = l0s = CONST_DOUBLE_LOW (op0);
+ h0u = h0s = CONST_DOUBLE_HIGH (op0);
+ }
+ else
+ {
+ l0u = l0s = INTVAL (op0);
+ h0u = 0, h0s = l0s < 0 ? -1 : 0;
+ }
+
+ if (GET_CODE (op1) == CONST_DOUBLE)
+ {
+ l1u = l1s = CONST_DOUBLE_LOW (op1);
+ h1u = h1s = CONST_DOUBLE_HIGH (op1);
+ }
+ else
+ {
+ l1u = l1s = INTVAL (op1);
+ h1u = 0, h1s = l1s < 0 ? -1 : 0;
+ }
+
+ /* If WIDTH is nonzero and smaller than HOST_BITS_PER_WIDE_INT,
+ we have to sign or zero-extend the values. */
+ if (width != 0 && width <= HOST_BITS_PER_WIDE_INT)
+ h0u = h1u = 0, h0s = l0s < 0 ? -1 : 0, h1s = l1s < 0 ? -1 : 0;
+
+ if (width != 0 && width < HOST_BITS_PER_WIDE_INT)
+ {
+ l0u &= ((HOST_WIDE_INT) 1 << width) - 1;
+ l1u &= ((HOST_WIDE_INT) 1 << width) - 1;
+
+ if (l0s & ((HOST_WIDE_INT) 1 << (width - 1)))
+ l0s |= ((HOST_WIDE_INT) (-1) << width);
+
+ if (l1s & ((HOST_WIDE_INT) 1 << (width - 1)))
+ l1s |= ((HOST_WIDE_INT) (-1) << width);
+ }
+
+ equal = (h0u == h1u && l0u == l1u);
+ op0lt = (h0s < h1s || (h0s == h1s && l0s < l1s));
+ op1lt = (h1s < h0s || (h1s == h0s && l1s < l0s));
+ op0ltu = (h0u < h1u || (h0u == h1u && l0u < l1u));
+ op1ltu = (h1u < h0u || (h1u == h0u && l1u < l0u));
+ }
+
+ /* Otherwise, there are some code-specific tests we can make. */
+ else
+ {
+ switch (code)
+ {
+ case EQ:
+ /* References to the frame plus a constant or labels cannot
+ be zero, but a SYMBOL_REF can due to #pragma weak. */
+ if (((NONZERO_BASE_PLUS_P (op0) && op1 == const0_rtx)
+ || GET_CODE (op0) == LABEL_REF)
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ /* On some machines, the ap reg can be 0 sometimes. */
+ && op0 != arg_pointer_rtx
+#endif
+ )
+ return const0_rtx;
+ break;
+
+ case NE:
+ if (((NONZERO_BASE_PLUS_P (op0) && op1 == const0_rtx)
+ || GET_CODE (op0) == LABEL_REF)
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ && op0 != arg_pointer_rtx
+#endif
+ )
+ return const_true_rtx;
+ break;
+
+ case GEU:
+ /* Unsigned values are never negative. */
+ if (op1 == const0_rtx)
+ return const_true_rtx;
+ break;
+
+ case LTU:
+ if (op1 == const0_rtx)
+ return const0_rtx;
+ break;
+
+ case LEU:
+ /* Unsigned values are never greater than the largest
+ unsigned value. */
+ if (GET_CODE (op1) == CONST_INT
+ && INTVAL (op1) == GET_MODE_MASK (mode)
+ && INTEGRAL_MODE_P (mode))
+ return const_true_rtx;
+ break;
+
+ case GTU:
+ if (GET_CODE (op1) == CONST_INT
+ && INTVAL (op1) == GET_MODE_MASK (mode)
+ && INTEGRAL_MODE_P (mode))
+ return const0_rtx;
+ break;
+ }
+
+ return 0;
+ }
+
+ /* If we reach here, EQUAL, OP0LT, OP0LTU, OP1LT, and OP1LTU are set
+ as appropriate. */
+ switch (code)
+ {
+ case EQ:
+ return equal ? const_true_rtx : const0_rtx;
+ case NE:
+ return ! equal ? const_true_rtx : const0_rtx;
+ case LT:
+ return op0lt ? const_true_rtx : const0_rtx;
+ case GT:
+ return op1lt ? const_true_rtx : const0_rtx;
+ case LTU:
+ return op0ltu ? const_true_rtx : const0_rtx;
+ case GTU:
+ return op1ltu ? const_true_rtx : const0_rtx;
+ case LE:
+ return equal || op0lt ? const_true_rtx : const0_rtx;
+ case GE:
+ return equal || op1lt ? const_true_rtx : const0_rtx;
+ case LEU:
+ return equal || op0ltu ? const_true_rtx : const0_rtx;
+ case GEU:
+ return equal || op1ltu ? const_true_rtx : const0_rtx;
+ }
+
+ abort ();
+}
+
+/* Simplify CODE, an operation with result mode MODE and three operands,
+ OP0, OP1, and OP2. OP0_MODE was the mode of OP0 before it became
+ a constant. Return 0 if no simplifications is possible. */
+
+rtx
+simplify_ternary_operation (code, mode, op0_mode, op0, op1, op2)
+ enum rtx_code code;
+ enum machine_mode mode, op0_mode;
+ rtx op0, op1, op2;
+{
+ int width = GET_MODE_BITSIZE (mode);
+
+ /* VOIDmode means "infinite" precision. */
+ if (width == 0)
+ width = HOST_BITS_PER_WIDE_INT;
+
+ switch (code)
+ {
+ case SIGN_EXTRACT:
+ case ZERO_EXTRACT:
+ if (GET_CODE (op0) == CONST_INT
+ && GET_CODE (op1) == CONST_INT
+ && GET_CODE (op2) == CONST_INT
+ && INTVAL (op1) + INTVAL (op2) <= GET_MODE_BITSIZE (op0_mode)
+ && width <= HOST_BITS_PER_WIDE_INT)
+ {
+ /* Extracting a bit-field from a constant */
+ HOST_WIDE_INT val = INTVAL (op0);
+
+#if BITS_BIG_ENDIAN
+ val >>= (GET_MODE_BITSIZE (op0_mode) - INTVAL (op2) - INTVAL (op1));
+#else
+ val >>= INTVAL (op2);
+#endif
+ if (HOST_BITS_PER_WIDE_INT != INTVAL (op1))
+ {
+ /* First zero-extend. */
+ val &= ((HOST_WIDE_INT) 1 << INTVAL (op1)) - 1;
+ /* If desired, propagate sign bit. */
+ if (code == SIGN_EXTRACT
+ && (val & ((HOST_WIDE_INT) 1 << (INTVAL (op1) - 1))))
+ val |= ~ (((HOST_WIDE_INT) 1 << INTVAL (op1)) - 1);
+ }
+
+ /* Clear the bits that don't belong in our mode,
+ unless they and our sign bit are all one.
+ So we get either a reasonable negative value or a reasonable
+ unsigned value for this mode. */
+ if (width < HOST_BITS_PER_WIDE_INT
+ && ((val & ((HOST_WIDE_INT) (-1) << (width - 1)))
+ != ((HOST_WIDE_INT) (-1) << (width - 1))))
+ val &= ((HOST_WIDE_INT) 1 << width) - 1;
+
+ return GEN_INT (val);
+ }
+ break;
+
+ case IF_THEN_ELSE:
+ if (GET_CODE (op0) == CONST_INT)
+ return op0 != const0_rtx ? op1 : op2;
+ break;
+
+ default:
+ abort ();
+ }
+
+ return 0;
+}
+
+/* If X is a nontrivial arithmetic operation on an argument
+ for which a constant value can be determined, return
+ the result of operating on that value, as a constant.
+ Otherwise, return X, possibly with one or more operands
+ modified by recursive calls to this function.
+
+ If X is a register whose contents are known, we do NOT
+ return those contents here. equiv_constant is called to
+ perform that task.
+
+ INSN is the insn that we may be modifying. If it is 0, make a copy
+ of X before modifying it. */
+
+static rtx
+fold_rtx (x, insn)
+ rtx x;
+ rtx insn;
+{
+ register enum rtx_code code;
+ register enum machine_mode mode;
+ register char *fmt;
+ register int i;
+ rtx new = 0;
+ int copied = 0;
+ int must_swap = 0;
+
+ /* Folded equivalents of first two operands of X. */
+ rtx folded_arg0;
+ rtx folded_arg1;
+
+ /* Constant equivalents of first three operands of X;
+ 0 when no such equivalent is known. */
+ rtx const_arg0;
+ rtx const_arg1;
+ rtx const_arg2;
+
+ /* The mode of the first operand of X. We need this for sign and zero
+ extends. */
+ enum machine_mode mode_arg0;
+
+ if (x == 0)
+ return x;
+
+ mode = GET_MODE (x);
+ code = GET_CODE (x);
+ switch (code)
+ {
+ case CONST:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case REG:
+ /* No use simplifying an EXPR_LIST
+ since they are used only for lists of args
+ in a function call's REG_EQUAL note. */
+ case EXPR_LIST:
+ return x;
+
+#ifdef HAVE_cc0
+ case CC0:
+ return prev_insn_cc0;
+#endif
+
+ case PC:
+ /* If the next insn is a CODE_LABEL followed by a jump table,
+ PC's value is a LABEL_REF pointing to that label. That
+ lets us fold switch statements on the Vax. */
+ if (insn && GET_CODE (insn) == JUMP_INSN)
+ {
+ rtx next = next_nonnote_insn (insn);
+
+ if (next && GET_CODE (next) == CODE_LABEL
+ && NEXT_INSN (next) != 0
+ && GET_CODE (NEXT_INSN (next)) == JUMP_INSN
+ && (GET_CODE (PATTERN (NEXT_INSN (next))) == ADDR_VEC
+ || GET_CODE (PATTERN (NEXT_INSN (next))) == ADDR_DIFF_VEC))
+ return gen_rtx (LABEL_REF, Pmode, next);
+ }
+ break;
+
+ case SUBREG:
+ /* See if we previously assigned a constant value to this SUBREG. */
+ if ((new = lookup_as_function (x, CONST_INT)) != 0
+ || (new = lookup_as_function (x, CONST_DOUBLE)) != 0)
+ return new;
+
+ /* If this is a paradoxical SUBREG, we have no idea what value the
+ extra bits would have. However, if the operand is equivalent
+ to a SUBREG whose operand is the same as our mode, and all the
+ modes are within a word, we can just use the inner operand
+ because these SUBREGs just say how to treat the register.
+
+ Similarly if we find an integer constant. */
+
+ if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+ {
+ enum machine_mode imode = GET_MODE (SUBREG_REG (x));
+ struct table_elt *elt;
+
+ if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD
+ && GET_MODE_SIZE (imode) <= UNITS_PER_WORD
+ && (elt = lookup (SUBREG_REG (x), HASH (SUBREG_REG (x), imode),
+ imode)) != 0)
+ for (elt = elt->first_same_value;
+ elt; elt = elt->next_same_value)
+ {
+ if (CONSTANT_P (elt->exp)
+ && GET_MODE (elt->exp) == VOIDmode)
+ return elt->exp;
+
+ if (GET_CODE (elt->exp) == SUBREG
+ && GET_MODE (SUBREG_REG (elt->exp)) == mode
+ && exp_equiv_p (elt->exp, elt->exp, 1, 0))
+ return copy_rtx (SUBREG_REG (elt->exp));
+ }
+
+ return x;
+ }
+
+ /* Fold SUBREG_REG. If it changed, see if we can simplify the SUBREG.
+ We might be able to if the SUBREG is extracting a single word in an
+ integral mode or extracting the low part. */
+
+ folded_arg0 = fold_rtx (SUBREG_REG (x), insn);
+ const_arg0 = equiv_constant (folded_arg0);
+ if (const_arg0)
+ folded_arg0 = const_arg0;
+
+ if (folded_arg0 != SUBREG_REG (x))
+ {
+ new = 0;
+
+ if (GET_MODE_CLASS (mode) == MODE_INT
+ && GET_MODE_SIZE (mode) == UNITS_PER_WORD
+ && GET_MODE (SUBREG_REG (x)) != VOIDmode)
+ new = operand_subword (folded_arg0, SUBREG_WORD (x), 0,
+ GET_MODE (SUBREG_REG (x)));
+ if (new == 0 && subreg_lowpart_p (x))
+ new = gen_lowpart_if_possible (mode, folded_arg0);
+ if (new)
+ return new;
+ }
+
+ /* If this is a narrowing SUBREG and our operand is a REG, see if
+ we can find an equivalence for REG that is an arithmetic operation
+ in a wider mode where both operands are paradoxical SUBREGs
+ from objects of our result mode. In that case, we couldn't report
+ an equivalent value for that operation, since we don't know what the
+ extra bits will be. But we can find an equivalence for this SUBREG
+ by folding that operation is the narrow mode. This allows us to
+ fold arithmetic in narrow modes when the machine only supports
+ word-sized arithmetic.
+
+ Also look for a case where we have a SUBREG whose operand is the
+ same as our result. If both modes are smaller than a word, we
+ are simply interpreting a register in different modes and we
+ can use the inner value. */
+
+ if (GET_CODE (folded_arg0) == REG
+ && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (folded_arg0))
+ && subreg_lowpart_p (x))
+ {
+ struct table_elt *elt;
+
+ /* We can use HASH here since we know that canon_hash won't be
+ called. */
+ elt = lookup (folded_arg0,
+ HASH (folded_arg0, GET_MODE (folded_arg0)),
+ GET_MODE (folded_arg0));
+
+ if (elt)
+ elt = elt->first_same_value;
+
+ for (; elt; elt = elt->next_same_value)
+ {
+ enum rtx_code eltcode = GET_CODE (elt->exp);
+
+ /* Just check for unary and binary operations. */
+ if (GET_RTX_CLASS (GET_CODE (elt->exp)) == '1'
+ && GET_CODE (elt->exp) != SIGN_EXTEND
+ && GET_CODE (elt->exp) != ZERO_EXTEND
+ && GET_CODE (XEXP (elt->exp, 0)) == SUBREG
+ && GET_MODE (SUBREG_REG (XEXP (elt->exp, 0))) == mode)
+ {
+ rtx op0 = SUBREG_REG (XEXP (elt->exp, 0));
+
+ if (GET_CODE (op0) != REG && ! CONSTANT_P (op0))
+ op0 = fold_rtx (op0, NULL_RTX);
+
+ op0 = equiv_constant (op0);
+ if (op0)
+ new = simplify_unary_operation (GET_CODE (elt->exp), mode,
+ op0, mode);
+ }
+ else if ((GET_RTX_CLASS (GET_CODE (elt->exp)) == '2'
+ || GET_RTX_CLASS (GET_CODE (elt->exp)) == 'c')
+ && eltcode != DIV && eltcode != MOD
+ && eltcode != UDIV && eltcode != UMOD
+ && eltcode != ASHIFTRT && eltcode != LSHIFTRT
+ && eltcode != ROTATE && eltcode != ROTATERT
+ && ((GET_CODE (XEXP (elt->exp, 0)) == SUBREG
+ && (GET_MODE (SUBREG_REG (XEXP (elt->exp, 0)))
+ == mode))
+ || CONSTANT_P (XEXP (elt->exp, 0)))
+ && ((GET_CODE (XEXP (elt->exp, 1)) == SUBREG
+ && (GET_MODE (SUBREG_REG (XEXP (elt->exp, 1)))
+ == mode))
+ || CONSTANT_P (XEXP (elt->exp, 1))))
+ {
+ rtx op0 = gen_lowpart_common (mode, XEXP (elt->exp, 0));
+ rtx op1 = gen_lowpart_common (mode, XEXP (elt->exp, 1));
+
+ if (op0 && GET_CODE (op0) != REG && ! CONSTANT_P (op0))
+ op0 = fold_rtx (op0, NULL_RTX);
+
+ if (op0)
+ op0 = equiv_constant (op0);
+
+ if (op1 && GET_CODE (op1) != REG && ! CONSTANT_P (op1))
+ op1 = fold_rtx (op1, NULL_RTX);
+
+ if (op1)
+ op1 = equiv_constant (op1);
+
+ /* If we are looking for the low SImode part of
+ (ashift:DI c (const_int 32)), it doesn't work
+ to compute that in SImode, because a 32-bit shift
+ in SImode is unpredictable. We know the value is 0. */
+ if (op0 && op1
+ && GET_CODE (elt->exp) == ASHIFT
+ && GET_CODE (op1) == CONST_INT
+ && INTVAL (op1) >= GET_MODE_BITSIZE (mode))
+ {
+ if (INTVAL (op1) < GET_MODE_BITSIZE (GET_MODE (elt->exp)))
+
+ /* If the count fits in the inner mode's width,
+ but exceeds the outer mode's width,
+ the value will get truncated to 0
+ by the subreg. */
+ new = const0_rtx;
+ else
+ /* If the count exceeds even the inner mode's width,
+ don't fold this expression. */
+ new = 0;
+ }
+ else if (op0 && op1)
+ new = simplify_binary_operation (GET_CODE (elt->exp), mode,
+ op0, op1);
+ }
+
+ else if (GET_CODE (elt->exp) == SUBREG
+ && GET_MODE (SUBREG_REG (elt->exp)) == mode
+ && (GET_MODE_SIZE (GET_MODE (folded_arg0))
+ <= UNITS_PER_WORD)
+ && exp_equiv_p (elt->exp, elt->exp, 1, 0))
+ new = copy_rtx (SUBREG_REG (elt->exp));
+
+ if (new)
+ return new;
+ }
+ }
+
+ return x;
+
+ case NOT:
+ case NEG:
+ /* If we have (NOT Y), see if Y is known to be (NOT Z).
+ If so, (NOT Y) simplifies to Z. Similarly for NEG. */
+ new = lookup_as_function (XEXP (x, 0), code);
+ if (new)
+ return fold_rtx (copy_rtx (XEXP (new, 0)), insn);
+ break;
+
+ case MEM:
+ /* If we are not actually processing an insn, don't try to find the
+ best address. Not only don't we care, but we could modify the
+ MEM in an invalid way since we have no insn to validate against. */
+ if (insn != 0)
+ find_best_addr (insn, &XEXP (x, 0));
+
+ {
+ /* Even if we don't fold in the insn itself,
+ we can safely do so here, in hopes of getting a constant. */
+ rtx addr = fold_rtx (XEXP (x, 0), NULL_RTX);
+ rtx base = 0;
+ HOST_WIDE_INT offset = 0;
+
+ if (GET_CODE (addr) == REG
+ && REGNO_QTY_VALID_P (REGNO (addr))
+ && GET_MODE (addr) == qty_mode[reg_qty[REGNO (addr)]]
+ && qty_const[reg_qty[REGNO (addr)]] != 0)
+ addr = qty_const[reg_qty[REGNO (addr)]];
+
+ /* If address is constant, split it into a base and integer offset. */
+ if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
+ base = addr;
+ else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT)
+ {
+ base = XEXP (XEXP (addr, 0), 0);
+ offset = INTVAL (XEXP (XEXP (addr, 0), 1));
+ }
+ else if (GET_CODE (addr) == LO_SUM
+ && GET_CODE (XEXP (addr, 1)) == SYMBOL_REF)
+ base = XEXP (addr, 1);
+
+ /* If this is a constant pool reference, we can fold it into its
+ constant to allow better value tracking. */
+ if (base && GET_CODE (base) == SYMBOL_REF
+ && CONSTANT_POOL_ADDRESS_P (base))
+ {
+ rtx constant = get_pool_constant (base);
+ enum machine_mode const_mode = get_pool_mode (base);
+ rtx new;
+
+ if (CONSTANT_P (constant) && GET_CODE (constant) != CONST_INT)
+ constant_pool_entries_cost = COST (constant);
+
+ /* If we are loading the full constant, we have an equivalence. */
+ if (offset == 0 && mode == const_mode)
+ return constant;
+
+ /* If this actually isn't a constant (wierd!), we can't do
+ anything. Otherwise, handle the two most common cases:
+ extracting a word from a multi-word constant, and extracting
+ the low-order bits. Other cases don't seem common enough to
+ worry about. */
+ if (! CONSTANT_P (constant))
+ return x;
+
+ if (GET_MODE_CLASS (mode) == MODE_INT
+ && GET_MODE_SIZE (mode) == UNITS_PER_WORD
+ && offset % UNITS_PER_WORD == 0
+ && (new = operand_subword (constant,
+ offset / UNITS_PER_WORD,
+ 0, const_mode)) != 0)
+ return new;
+
+ if (((BYTES_BIG_ENDIAN
+ && offset == GET_MODE_SIZE (GET_MODE (constant)) - 1)
+ || (! BYTES_BIG_ENDIAN && offset == 0))
+ && (new = gen_lowpart_if_possible (mode, constant)) != 0)
+ return new;
+ }
+
+ /* If this is a reference to a label at a known position in a jump
+ table, we also know its value. */
+ if (base && GET_CODE (base) == LABEL_REF)
+ {
+ rtx label = XEXP (base, 0);
+ rtx table_insn = NEXT_INSN (label);
+
+ if (table_insn && GET_CODE (table_insn) == JUMP_INSN
+ && GET_CODE (PATTERN (table_insn)) == ADDR_VEC)
+ {
+ rtx table = PATTERN (table_insn);
+
+ if (offset >= 0
+ && (offset / GET_MODE_SIZE (GET_MODE (table))
+ < XVECLEN (table, 0)))
+ return XVECEXP (table, 0,
+ offset / GET_MODE_SIZE (GET_MODE (table)));
+ }
+ if (table_insn && GET_CODE (table_insn) == JUMP_INSN
+ && GET_CODE (PATTERN (table_insn)) == ADDR_DIFF_VEC)
+ {
+ rtx table = PATTERN (table_insn);
+
+ if (offset >= 0
+ && (offset / GET_MODE_SIZE (GET_MODE (table))
+ < XVECLEN (table, 1)))
+ {
+ offset /= GET_MODE_SIZE (GET_MODE (table));
+ new = gen_rtx (MINUS, Pmode, XVECEXP (table, 1, offset),
+ XEXP (table, 0));
+
+ if (GET_MODE (table) != Pmode)
+ new = gen_rtx (TRUNCATE, GET_MODE (table), new);
+
+ return new;
+ }
+ }
+ }
+
+ return x;
+ }
+ }
+
+ const_arg0 = 0;
+ const_arg1 = 0;
+ const_arg2 = 0;
+ mode_arg0 = VOIDmode;
+
+ /* Try folding our operands.
+ Then see which ones have constant values known. */
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ if (fmt[i] == 'e')
+ {
+ rtx arg = XEXP (x, i);
+ rtx folded_arg = arg, const_arg = 0;
+ enum machine_mode mode_arg = GET_MODE (arg);
+ rtx cheap_arg, expensive_arg;
+ rtx replacements[2];
+ int j;
+
+ /* Most arguments are cheap, so handle them specially. */
+ switch (GET_CODE (arg))
+ {
+ case REG:
+ /* This is the same as calling equiv_constant; it is duplicated
+ here for speed. */
+ if (REGNO_QTY_VALID_P (REGNO (arg))
+ && qty_const[reg_qty[REGNO (arg)]] != 0
+ && GET_CODE (qty_const[reg_qty[REGNO (arg)]]) != REG
+ && GET_CODE (qty_const[reg_qty[REGNO (arg)]]) != PLUS)
+ const_arg
+ = gen_lowpart_if_possible (GET_MODE (arg),
+ qty_const[reg_qty[REGNO (arg)]]);
+ break;
+
+ case CONST:
+ case CONST_INT:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case CONST_DOUBLE:
+ const_arg = arg;
+ break;
+
+#ifdef HAVE_cc0
+ case CC0:
+ folded_arg = prev_insn_cc0;
+ mode_arg = prev_insn_cc0_mode;
+ const_arg = equiv_constant (folded_arg);
+ break;
+#endif
+
+ default:
+ folded_arg = fold_rtx (arg, insn);
+ const_arg = equiv_constant (folded_arg);
+ }
+
+ /* For the first three operands, see if the operand
+ is constant or equivalent to a constant. */
+ switch (i)
+ {
+ case 0:
+ folded_arg0 = folded_arg;
+ const_arg0 = const_arg;
+ mode_arg0 = mode_arg;
+ break;
+ case 1:
+ folded_arg1 = folded_arg;
+ const_arg1 = const_arg;
+ break;
+ case 2:
+ const_arg2 = const_arg;
+ break;
+ }
+
+ /* Pick the least expensive of the folded argument and an
+ equivalent constant argument. */
+ if (const_arg == 0 || const_arg == folded_arg
+ || COST (const_arg) > COST (folded_arg))
+ cheap_arg = folded_arg, expensive_arg = const_arg;
+ else
+ cheap_arg = const_arg, expensive_arg = folded_arg;
+
+ /* Try to replace the operand with the cheapest of the two
+ possibilities. If it doesn't work and this is either of the first
+ two operands of a commutative operation, try swapping them.
+ If THAT fails, try the more expensive, provided it is cheaper
+ than what is already there. */
+
+ if (cheap_arg == XEXP (x, i))
+ continue;
+
+ if (insn == 0 && ! copied)
+ {
+ x = copy_rtx (x);
+ copied = 1;
+ }
+
+ replacements[0] = cheap_arg, replacements[1] = expensive_arg;
+ for (j = 0;
+ j < 2 && replacements[j]
+ && COST (replacements[j]) < COST (XEXP (x, i));
+ j++)
+ {
+ if (validate_change (insn, &XEXP (x, i), replacements[j], 0))
+ break;
+
+ if (code == NE || code == EQ || GET_RTX_CLASS (code) == 'c')
+ {
+ validate_change (insn, &XEXP (x, i), XEXP (x, 1 - i), 1);
+ validate_change (insn, &XEXP (x, 1 - i), replacements[j], 1);
+
+ if (apply_change_group ())
+ {
+ /* Swap them back to be invalid so that this loop can
+ continue and flag them to be swapped back later. */
+ rtx tem;
+
+ tem = XEXP (x, 0); XEXP (x, 0) = XEXP (x, 1);
+ XEXP (x, 1) = tem;
+ must_swap = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ else if (fmt[i] == 'E')
+ /* Don't try to fold inside of a vector of expressions.
+ Doing nothing is harmless. */
+ ;
+
+ /* If a commutative operation, place a constant integer as the second
+ operand unless the first operand is also a constant integer. Otherwise,
+ place any constant second unless the first operand is also a constant. */
+
+ if (code == EQ || code == NE || GET_RTX_CLASS (code) == 'c')
+ {
+ if (must_swap || (const_arg0
+ && (const_arg1 == 0
+ || (GET_CODE (const_arg0) == CONST_INT
+ && GET_CODE (const_arg1) != CONST_INT))))
+ {
+ register rtx tem = XEXP (x, 0);
+
+ if (insn == 0 && ! copied)
+ {
+ x = copy_rtx (x);
+ copied = 1;
+ }
+
+ validate_change (insn, &XEXP (x, 0), XEXP (x, 1), 1);
+ validate_change (insn, &XEXP (x, 1), tem, 1);
+ if (apply_change_group ())
+ {
+ tem = const_arg0, const_arg0 = const_arg1, const_arg1 = tem;
+ tem = folded_arg0, folded_arg0 = folded_arg1, folded_arg1 = tem;
+ }
+ }
+ }
+
+ /* If X is an arithmetic operation, see if we can simplify it. */
+
+ switch (GET_RTX_CLASS (code))
+ {
+ case '1':
+ /* We can't simplify extension ops unless we know the original mode. */
+ if ((code == ZERO_EXTEND || code == SIGN_EXTEND)
+ && mode_arg0 == VOIDmode)
+ break;
+ new = simplify_unary_operation (code, mode,
+ const_arg0 ? const_arg0 : folded_arg0,
+ mode_arg0);
+ break;
+
+ case '<':
+ /* See what items are actually being compared and set FOLDED_ARG[01]
+ to those values and CODE to the actual comparison code. If any are
+ constant, set CONST_ARG0 and CONST_ARG1 appropriately. We needn't
+ do anything if both operands are already known to be constant. */
+
+ if (const_arg0 == 0 || const_arg1 == 0)
+ {
+ struct table_elt *p0, *p1;
+ rtx true = const_true_rtx, false = const0_rtx;
+ enum machine_mode mode_arg1;
+
+#ifdef FLOAT_STORE_FLAG_VALUE
+ if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+ {
+ true = CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE,
+ mode);
+ false = CONST0_RTX (mode);
+ }
+#endif
+
+ code = find_comparison_args (code, &folded_arg0, &folded_arg1,
+ &mode_arg0, &mode_arg1);
+ const_arg0 = equiv_constant (folded_arg0);
+ const_arg1 = equiv_constant (folded_arg1);
+
+ /* If the mode is VOIDmode or a MODE_CC mode, we don't know
+ what kinds of things are being compared, so we can't do
+ anything with this comparison. */
+
+ if (mode_arg0 == VOIDmode || GET_MODE_CLASS (mode_arg0) == MODE_CC)
+ break;
+
+ /* If we do not now have two constants being compared, see if we
+ can nevertheless deduce some things about the comparison. */
+ if (const_arg0 == 0 || const_arg1 == 0)
+ {
+ /* Is FOLDED_ARG0 frame-pointer plus a constant? Or non-explicit
+ constant? These aren't zero, but we don't know their sign. */
+ if (const_arg1 == const0_rtx
+ && (NONZERO_BASE_PLUS_P (folded_arg0)
+#if 0 /* Sad to say, on sysvr4, #pragma weak can make a symbol address
+ come out as 0. */
+ || GET_CODE (folded_arg0) == SYMBOL_REF
+#endif
+ || GET_CODE (folded_arg0) == LABEL_REF
+ || GET_CODE (folded_arg0) == CONST))
+ {
+ if (code == EQ)
+ return false;
+ else if (code == NE)
+ return true;
+ }
+
+ /* See if the two operands are the same. We don't do this
+ for IEEE floating-point since we can't assume x == x
+ since x might be a NaN. */
+
+ if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+ || ! FLOAT_MODE_P (mode_arg0) || flag_fast_math)
+ && (folded_arg0 == folded_arg1
+ || (GET_CODE (folded_arg0) == REG
+ && GET_CODE (folded_arg1) == REG
+ && (reg_qty[REGNO (folded_arg0)]
+ == reg_qty[REGNO (folded_arg1)]))
+ || ((p0 = lookup (folded_arg0,
+ (safe_hash (folded_arg0, mode_arg0)
+ % NBUCKETS), mode_arg0))
+ && (p1 = lookup (folded_arg1,
+ (safe_hash (folded_arg1, mode_arg0)
+ % NBUCKETS), mode_arg0))
+ && p0->first_same_value == p1->first_same_value)))
+ return ((code == EQ || code == LE || code == GE
+ || code == LEU || code == GEU)
+ ? true : false);
+
+ /* If FOLDED_ARG0 is a register, see if the comparison we are
+ doing now is either the same as we did before or the reverse
+ (we only check the reverse if not floating-point). */
+ else if (GET_CODE (folded_arg0) == REG)
+ {
+ int qty = reg_qty[REGNO (folded_arg0)];
+
+ if (REGNO_QTY_VALID_P (REGNO (folded_arg0))
+ && (comparison_dominates_p (qty_comparison_code[qty], code)
+ || (comparison_dominates_p (qty_comparison_code[qty],
+ reverse_condition (code))
+ && ! FLOAT_MODE_P (mode_arg0)))
+ && (rtx_equal_p (qty_comparison_const[qty], folded_arg1)
+ || (const_arg1
+ && rtx_equal_p (qty_comparison_const[qty],
+ const_arg1))
+ || (GET_CODE (folded_arg1) == REG
+ && (reg_qty[REGNO (folded_arg1)]
+ == qty_comparison_qty[qty]))))
+ return (comparison_dominates_p (qty_comparison_code[qty],
+ code)
+ ? true : false);
+ }
+ }
+ }
+
+ /* If we are comparing against zero, see if the first operand is
+ equivalent to an IOR with a constant. If so, we may be able to
+ determine the result of this comparison. */
+
+ if (const_arg1 == const0_rtx)
+ {
+ rtx y = lookup_as_function (folded_arg0, IOR);
+ rtx inner_const;
+
+ if (y != 0
+ && (inner_const = equiv_constant (XEXP (y, 1))) != 0
+ && GET_CODE (inner_const) == CONST_INT
+ && INTVAL (inner_const) != 0)
+ {
+ int sign_bitnum = GET_MODE_BITSIZE (mode_arg0) - 1;
+ int has_sign = (HOST_BITS_PER_WIDE_INT >= sign_bitnum
+ && (INTVAL (inner_const)
+ & ((HOST_WIDE_INT) 1 << sign_bitnum)));
+ rtx true = const_true_rtx, false = const0_rtx;
+
+#ifdef FLOAT_STORE_FLAG_VALUE
+ if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+ {
+ true = CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE,
+ mode);
+ false = CONST0_RTX (mode);
+ }
+#endif
+
+ switch (code)
+ {
+ case EQ:
+ return false;
+ case NE:
+ return true;
+ case LT: case LE:
+ if (has_sign)
+ return true;
+ break;
+ case GT: case GE:
+ if (has_sign)
+ return false;
+ break;
+ }
+ }
+ }
+
+ new = simplify_relational_operation (code, mode_arg0,
+ const_arg0 ? const_arg0 : folded_arg0,
+ const_arg1 ? const_arg1 : folded_arg1);
+#ifdef FLOAT_STORE_FLAG_VALUE
+ if (new != 0 && GET_MODE_CLASS (mode) == MODE_FLOAT)
+ new = ((new == const0_rtx) ? CONST0_RTX (mode)
+ : CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE, mode));
+#endif
+ break;
+
+ case '2':
+ case 'c':
+ switch (code)
+ {
+ case PLUS:
+ /* If the second operand is a LABEL_REF, see if the first is a MINUS
+ with that LABEL_REF as its second operand. If so, the result is
+ the first operand of that MINUS. This handles switches with an
+ ADDR_DIFF_VEC table. */
+ if (const_arg1 && GET_CODE (const_arg1) == LABEL_REF)
+ {
+ rtx y = lookup_as_function (folded_arg0, MINUS);
+
+ if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF
+ && XEXP (XEXP (y, 1), 0) == XEXP (const_arg1, 0))
+ return XEXP (y, 0);
+ }
+
+ /* If second operand is a register equivalent to a negative
+ CONST_INT, see if we can find a register equivalent to the
+ positive constant. Make a MINUS if so. Don't do this for
+ a negative constant since we might then alternate between
+ chosing positive and negative constants. Having the positive
+ constant previously-used is the more common case. */
+ if (const_arg1 && GET_CODE (const_arg1) == CONST_INT
+ && INTVAL (const_arg1) < 0 && GET_CODE (folded_arg1) == REG)
+ {
+ rtx new_const = GEN_INT (- INTVAL (const_arg1));
+ struct table_elt *p
+ = lookup (new_const, safe_hash (new_const, mode) % NBUCKETS,
+ mode);
+
+ if (p)
+ for (p = p->first_same_value; p; p = p->next_same_value)
+ if (GET_CODE (p->exp) == REG)
+ return cse_gen_binary (MINUS, mode, folded_arg0,
+ canon_reg (p->exp, NULL_RTX));
+ }
+ goto from_plus;
+
+ case MINUS:
+ /* If we have (MINUS Y C), see if Y is known to be (PLUS Z C2).
+ If so, produce (PLUS Z C2-C). */
+ if (const_arg1 != 0 && GET_CODE (const_arg1) == CONST_INT)
+ {
+ rtx y = lookup_as_function (XEXP (x, 0), PLUS);
+ if (y && GET_CODE (XEXP (y, 1)) == CONST_INT)
+ return fold_rtx (plus_constant (copy_rtx (y),
+ -INTVAL (const_arg1)),
+ NULL_RTX);
+ }
+
+ /* ... fall through ... */
+
+ from_plus:
+ case SMIN: case SMAX: case UMIN: case UMAX:
+ case IOR: case AND: case XOR:
+ case MULT: case DIV: case UDIV:
+ case ASHIFT: case LSHIFTRT: case ASHIFTRT:
+ /* If we have (<op> <reg> <const_int>) for an associative OP and REG
+ is known to be of similar form, we may be able to replace the
+ operation with a combined operation. This may eliminate the
+ intermediate operation if every use is simplified in this way.
+ Note that the similar optimization done by combine.c only works
+ if the intermediate operation's result has only one reference. */
+
+ if (GET_CODE (folded_arg0) == REG
+ && const_arg1 && GET_CODE (const_arg1) == CONST_INT)
+ {
+ int is_shift
+ = (code == ASHIFT || code == ASHIFTRT || code == LSHIFTRT);
+ rtx y = lookup_as_function (folded_arg0, code);
+ rtx inner_const;
+ enum rtx_code associate_code;
+ rtx new_const;
+
+ if (y == 0
+ || 0 == (inner_const
+ = equiv_constant (fold_rtx (XEXP (y, 1), 0)))
+ || GET_CODE (inner_const) != CONST_INT
+ /* If we have compiled a statement like
+ "if (x == (x & mask1))", and now are looking at
+ "x & mask2", we will have a case where the first operand
+ of Y is the same as our first operand. Unless we detect
+ this case, an infinite loop will result. */
+ || XEXP (y, 0) == folded_arg0)
+ break;
+
+ /* Don't associate these operations if they are a PLUS with the
+ same constant and it is a power of two. These might be doable
+ with a pre- or post-increment. Similarly for two subtracts of
+ identical powers of two with post decrement. */
+
+ if (code == PLUS && INTVAL (const_arg1) == INTVAL (inner_const)
+ && (0
+#if defined(HAVE_PRE_INCREMENT) || defined(HAVE_POST_INCREMENT)
+ || exact_log2 (INTVAL (const_arg1)) >= 0
+#endif
+#if defined(HAVE_PRE_DECREMENT) || defined(HAVE_POST_DECREMENT)
+ || exact_log2 (- INTVAL (const_arg1)) >= 0
+#endif
+ ))
+ break;
+
+ /* Compute the code used to compose the constants. For example,
+ A/C1/C2 is A/(C1 * C2), so if CODE == DIV, we want MULT. */
+
+ associate_code
+ = (code == MULT || code == DIV || code == UDIV ? MULT
+ : is_shift || code == PLUS || code == MINUS ? PLUS : code);
+
+ new_const = simplify_binary_operation (associate_code, mode,
+ const_arg1, inner_const);
+
+ if (new_const == 0)
+ break;
+
+ /* If we are associating shift operations, don't let this
+ produce a shift of the size of the object or larger.
+ This could occur when we follow a sign-extend by a right
+ shift on a machine that does a sign-extend as a pair
+ of shifts. */
+
+ if (is_shift && GET_CODE (new_const) == CONST_INT
+ && INTVAL (new_const) >= GET_MODE_BITSIZE (mode))
+ {
+ /* As an exception, we can turn an ASHIFTRT of this
+ form into a shift of the number of bits - 1. */
+ if (code == ASHIFTRT)
+ new_const = GEN_INT (GET_MODE_BITSIZE (mode) - 1);
+ else
+ break;
+ }
+
+ y = copy_rtx (XEXP (y, 0));
+
+ /* If Y contains our first operand (the most common way this
+ can happen is if Y is a MEM), we would do into an infinite
+ loop if we tried to fold it. So don't in that case. */
+
+ if (! reg_mentioned_p (folded_arg0, y))
+ y = fold_rtx (y, insn);
+
+ return cse_gen_binary (code, mode, y, new_const);
+ }
+ }
+
+ new = simplify_binary_operation (code, mode,
+ const_arg0 ? const_arg0 : folded_arg0,
+ const_arg1 ? const_arg1 : folded_arg1);
+ break;
+
+ case 'o':
+ /* (lo_sum (high X) X) is simply X. */
+ if (code == LO_SUM && const_arg0 != 0
+ && GET_CODE (const_arg0) == HIGH
+ && rtx_equal_p (XEXP (const_arg0, 0), const_arg1))
+ return const_arg1;
+ break;
+
+ case '3':
+ case 'b':
+ new = simplify_ternary_operation (code, mode, mode_arg0,
+ const_arg0 ? const_arg0 : folded_arg0,
+ const_arg1 ? const_arg1 : folded_arg1,
+ const_arg2 ? const_arg2 : XEXP (x, 2));
+ break;
+ }
+
+ return new ? new : x;
+}
+
+/* Return a constant value currently equivalent to X.
+ Return 0 if we don't know one. */
+
+static rtx
+equiv_constant (x)
+ rtx x;
+{
+ if (GET_CODE (x) == REG
+ && REGNO_QTY_VALID_P (REGNO (x))
+ && qty_const[reg_qty[REGNO (x)]])
+ x = gen_lowpart_if_possible (GET_MODE (x), qty_const[reg_qty[REGNO (x)]]);
+
+ if (x != 0 && CONSTANT_P (x))
+ return x;
+
+ /* If X is a MEM, try to fold it outside the context of any insn to see if
+ it might be equivalent to a constant. That handles the case where it
+ is a constant-pool reference. Then try to look it up in the hash table
+ in case it is something whose value we have seen before. */
+
+ if (GET_CODE (x) == MEM)
+ {
+ struct table_elt *elt;
+
+ x = fold_rtx (x, NULL_RTX);
+ if (CONSTANT_P (x))
+ return x;
+
+ elt = lookup (x, safe_hash (x, GET_MODE (x)) % NBUCKETS, GET_MODE (x));
+ if (elt == 0)
+ return 0;
+
+ for (elt = elt->first_same_value; elt; elt = elt->next_same_value)
+ if (elt->is_const && CONSTANT_P (elt->exp))
+ return elt->exp;
+ }
+
+ return 0;
+}
+
+/* Assuming that X is an rtx (e.g., MEM, REG or SUBREG) for a fixed-point
+ number, return an rtx (MEM, SUBREG, or CONST_INT) that refers to the
+ least-significant part of X.
+ MODE specifies how big a part of X to return.
+
+ If the requested operation cannot be done, 0 is returned.
+
+ This is similar to gen_lowpart in emit-rtl.c. */
+
+rtx
+gen_lowpart_if_possible (mode, x)
+ enum machine_mode mode;
+ register rtx x;
+{
+ rtx result = gen_lowpart_common (mode, x);
+
+ if (result)
+ return result;
+ else if (GET_CODE (x) == MEM)
+ {
+ /* This is the only other case we handle. */
+ register int offset = 0;
+ rtx new;
+
+#if WORDS_BIG_ENDIAN
+ offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD)
+ - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD));
+#endif
+#if BYTES_BIG_ENDIAN
+ /* Adjust the address so that the address-after-the-data
+ is unchanged. */
+ offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))
+ - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x))));
+#endif
+ new = gen_rtx (MEM, mode, plus_constant (XEXP (x, 0), offset));
+ if (! memory_address_p (mode, XEXP (new, 0)))
+ return 0;
+ MEM_VOLATILE_P (new) = MEM_VOLATILE_P (x);
+ RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (x);
+ MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (x);
+ return new;
+ }
+ else
+ return 0;
+}
+
+/* Given INSN, a jump insn, TAKEN indicates if we are following the "taken"
+ branch. It will be zero if not.
+
+ In certain cases, this can cause us to add an equivalence. For example,
+ if we are following the taken case of
+ if (i == 2)
+ we can add the fact that `i' and '2' are now equivalent.
+
+ In any case, we can record that this comparison was passed. If the same
+ comparison is seen later, we will know its value. */
+
+static void
+record_jump_equiv (insn, taken)
+ rtx insn;
+ int taken;
+{
+ int cond_known_true;
+ rtx op0, op1;
+ enum machine_mode mode, mode0, mode1;
+ int reversed_nonequality = 0;
+ enum rtx_code code;
+
+ /* Ensure this is the right kind of insn. */
+ if (! condjump_p (insn) || simplejump_p (insn))
+ return;
+
+ /* See if this jump condition is known true or false. */
+ if (taken)
+ cond_known_true = (XEXP (SET_SRC (PATTERN (insn)), 2) == pc_rtx);
+ else
+ cond_known_true = (XEXP (SET_SRC (PATTERN (insn)), 1) == pc_rtx);
+
+ /* Get the type of comparison being done and the operands being compared.
+ If we had to reverse a non-equality condition, record that fact so we
+ know that it isn't valid for floating-point. */
+ code = GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 0));
+ op0 = fold_rtx (XEXP (XEXP (SET_SRC (PATTERN (insn)), 0), 0), insn);
+ op1 = fold_rtx (XEXP (XEXP (SET_SRC (PATTERN (insn)), 0), 1), insn);
+
+ code = find_comparison_args (code, &op0, &op1, &mode0, &mode1);
+ if (! cond_known_true)
+ {
+ reversed_nonequality = (code != EQ && code != NE);
+ code = reverse_condition (code);
+ }
+
+ /* The mode is the mode of the non-constant. */
+ mode = mode0;
+ if (mode1 != VOIDmode)
+ mode = mode1;
+
+ record_jump_cond (code, mode, op0, op1, reversed_nonequality);
+}
+
+/* We know that comparison CODE applied to OP0 and OP1 in MODE is true.
+ REVERSED_NONEQUALITY is nonzero if CODE had to be swapped.
+ Make any useful entries we can with that information. Called from
+ above function and called recursively. */
+
+static void
+record_jump_cond (code, mode, op0, op1, reversed_nonequality)
+ enum rtx_code code;
+ enum machine_mode mode;
+ rtx op0, op1;
+ int reversed_nonequality;
+{
+ unsigned op0_hash, op1_hash;
+ int op0_in_memory, op0_in_struct, op1_in_memory, op1_in_struct;
+ struct table_elt *op0_elt, *op1_elt;
+
+ /* If OP0 and OP1 are known equal, and either is a paradoxical SUBREG,
+ we know that they are also equal in the smaller mode (this is also
+ true for all smaller modes whether or not there is a SUBREG, but
+ is not worth testing for with no SUBREG. */
+
+ /* Note that GET_MODE (op0) may not equal MODE. */
+ if (code == EQ && GET_CODE (op0) == SUBREG
+ && (GET_MODE_SIZE (GET_MODE (op0))
+ > GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0)))))
+ {
+ enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op0));
+ rtx tem = gen_lowpart_if_possible (inner_mode, op1);
+
+ record_jump_cond (code, mode, SUBREG_REG (op0),
+ tem ? tem : gen_rtx (SUBREG, inner_mode, op1, 0),
+ reversed_nonequality);
+ }
+
+ if (code == EQ && GET_CODE (op1) == SUBREG
+ && (GET_MODE_SIZE (GET_MODE (op1))
+ > GET_MODE_SIZE (GET_MODE (SUBREG_REG (op1)))))
+ {
+ enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op1));
+ rtx tem = gen_lowpart_if_possible (inner_mode, op0);
+
+ record_jump_cond (code, mode, SUBREG_REG (op1),
+ tem ? tem : gen_rtx (SUBREG, inner_mode, op0, 0),
+ reversed_nonequality);
+ }
+
+ /* Similarly, if this is an NE comparison, and either is a SUBREG
+ making a smaller mode, we know the whole thing is also NE. */
+
+ /* Note that GET_MODE (op0) may not equal MODE;
+ if we test MODE instead, we can get an infinite recursion
+ alternating between two modes each wider than MODE. */
+
+ if (code == NE && GET_CODE (op0) == SUBREG
+ && subreg_lowpart_p (op0)
+ && (GET_MODE_SIZE (GET_MODE (op0))
+ < GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0)))))
+ {
+ enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op0));
+ rtx tem = gen_lowpart_if_possible (inner_mode, op1);
+
+ record_jump_cond (code, mode, SUBREG_REG (op0),
+ tem ? tem : gen_rtx (SUBREG, inner_mode, op1, 0),
+ reversed_nonequality);
+ }
+
+ if (code == NE && GET_CODE (op1) == SUBREG
+ && subreg_lowpart_p (op1)
+ && (GET_MODE_SIZE (GET_MODE (op1))
+ < GET_MODE_SIZE (GET_MODE (SUBREG_REG (op1)))))
+ {
+ enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op1));
+ rtx tem = gen_lowpart_if_possible (inner_mode, op0);
+
+ record_jump_cond (code, mode, SUBREG_REG (op1),
+ tem ? tem : gen_rtx (SUBREG, inner_mode, op0, 0),
+ reversed_nonequality);
+ }
+
+ /* Hash both operands. */
+
+ do_not_record = 0;
+ hash_arg_in_memory = 0;
+ hash_arg_in_struct = 0;
+ op0_hash = HASH (op0, mode);
+ op0_in_memory = hash_arg_in_memory;
+ op0_in_struct = hash_arg_in_struct;
+
+ if (do_not_record)
+ return;
+
+ do_not_record = 0;
+ hash_arg_in_memory = 0;
+ hash_arg_in_struct = 0;
+ op1_hash = HASH (op1, mode);
+ op1_in_memory = hash_arg_in_memory;
+ op1_in_struct = hash_arg_in_struct;
+
+ if (do_not_record)
+ return;
+
+ /* Look up both operands. */
+ op0_elt = lookup (op0, op0_hash, mode);
+ op1_elt = lookup (op1, op1_hash, mode);
+
+ /* If we aren't setting two things equal all we can do is save this
+ comparison. Similarly if this is floating-point. In the latter
+ case, OP1 might be zero and both -0.0 and 0.0 are equal to it.
+ If we record the equality, we might inadvertently delete code
+ whose intent was to change -0 to +0. */
+
+ if (code != EQ || FLOAT_MODE_P (GET_MODE (op0)))
+ {
+ /* If we reversed a floating-point comparison, if OP0 is not a
+ register, or if OP1 is neither a register or constant, we can't
+ do anything. */
+
+ if (GET_CODE (op1) != REG)
+ op1 = equiv_constant (op1);
+
+ if ((reversed_nonequality && FLOAT_MODE_P (mode))
+ || GET_CODE (op0) != REG || op1 == 0)
+ return;
+
+ /* Put OP0 in the hash table if it isn't already. This gives it a
+ new quantity number. */
+ if (op0_elt == 0)
+ {
+ if (insert_regs (op0, NULL_PTR, 0))
+ {
+ rehash_using_reg (op0);
+ op0_hash = HASH (op0, mode);
+
+ /* If OP0 is contained in OP1, this changes its hash code
+ as well. Faster to rehash than to check, except
+ for the simple case of a constant. */
+ if (! CONSTANT_P (op1))
+ op1_hash = HASH (op1,mode);
+ }
+
+ op0_elt = insert (op0, NULL_PTR, op0_hash, mode);
+ op0_elt->in_memory = op0_in_memory;
+ op0_elt->in_struct = op0_in_struct;
+ }
+
+ qty_comparison_code[reg_qty[REGNO (op0)]] = code;
+ if (GET_CODE (op1) == REG)
+ {
+ /* Look it up again--in case op0 and op1 are the same. */
+ op1_elt = lookup (op1, op1_hash, mode);
+
+ /* Put OP1 in the hash table so it gets a new quantity number. */
+ if (op1_elt == 0)
+ {
+ if (insert_regs (op1, NULL_PTR, 0))
+ {
+ rehash_using_reg (op1);
+ op1_hash = HASH (op1, mode);
+ }
+
+ op1_elt = insert (op1, NULL_PTR, op1_hash, mode);
+ op1_elt->in_memory = op1_in_memory;
+ op1_elt->in_struct = op1_in_struct;
+ }
+
+ qty_comparison_qty[reg_qty[REGNO (op0)]] = reg_qty[REGNO (op1)];
+ qty_comparison_const[reg_qty[REGNO (op0)]] = 0;
+ }
+ else
+ {
+ qty_comparison_qty[reg_qty[REGNO (op0)]] = -1;
+ qty_comparison_const[reg_qty[REGNO (op0)]] = op1;
+ }
+
+ return;
+ }
+
+ /* If either side is still missing an equivalence, make it now,
+ then merge the equivalences. */
+
+ if (op0_elt == 0)
+ {
+ if (insert_regs (op0, NULL_PTR, 0))
+ {
+ rehash_using_reg (op0);
+ op0_hash = HASH (op0, mode);
+ }
+
+ op0_elt = insert (op0, NULL_PTR, op0_hash, mode);
+ op0_elt->in_memory = op0_in_memory;
+ op0_elt->in_struct = op0_in_struct;
+ }
+
+ if (op1_elt == 0)
+ {
+ if (insert_regs (op1, NULL_PTR, 0))
+ {
+ rehash_using_reg (op1);
+ op1_hash = HASH (op1, mode);
+ }
+
+ op1_elt = insert (op1, NULL_PTR, op1_hash, mode);
+ op1_elt->in_memory = op1_in_memory;
+ op1_elt->in_struct = op1_in_struct;
+ }
+
+ merge_equiv_classes (op0_elt, op1_elt);
+ last_jump_equiv_class = op0_elt;
+}
+
+/* CSE processing for one instruction.
+ First simplify sources and addresses of all assignments
+ in the instruction, using previously-computed equivalents values.
+ Then install the new sources and destinations in the table
+ of available values.
+
+ If IN_LIBCALL_BLOCK is nonzero, don't record any equivalence made in
+ the insn. */
+
+/* Data on one SET contained in the instruction. */
+
+struct set
+{
+ /* The SET rtx itself. */
+ rtx rtl;
+ /* The SET_SRC of the rtx (the original value, if it is changing). */
+ rtx src;
+ /* The hash-table element for the SET_SRC of the SET. */
+ struct table_elt *src_elt;
+ /* Hash value for the SET_SRC. */
+ unsigned src_hash;
+ /* Hash value for the SET_DEST. */
+ unsigned dest_hash;
+ /* The SET_DEST, with SUBREG, etc., stripped. */
+ rtx inner_dest;
+ /* Place where the pointer to the INNER_DEST was found. */
+ rtx *inner_dest_loc;
+ /* Nonzero if the SET_SRC is in memory. */
+ char src_in_memory;
+ /* Nonzero if the SET_SRC is in a structure. */
+ char src_in_struct;
+ /* Nonzero if the SET_SRC contains something
+ whose value cannot be predicted and understood. */
+ char src_volatile;
+ /* Original machine mode, in case it becomes a CONST_INT. */
+ enum machine_mode mode;
+ /* A constant equivalent for SET_SRC, if any. */
+ rtx src_const;
+ /* Hash value of constant equivalent for SET_SRC. */
+ unsigned src_const_hash;
+ /* Table entry for constant equivalent for SET_SRC, if any. */
+ struct table_elt *src_const_elt;
+};
+
+static void
+cse_insn (insn, in_libcall_block)
+ rtx insn;
+ int in_libcall_block;
+{
+ register rtx x = PATTERN (insn);
+ register int i;
+ rtx tem;
+ register int n_sets = 0;
+
+ /* Records what this insn does to set CC0. */
+ rtx this_insn_cc0 = 0;
+ enum machine_mode this_insn_cc0_mode;
+ struct write_data writes_memory;
+ static struct write_data init = {0, 0, 0, 0};
+
+ rtx src_eqv = 0;
+ struct table_elt *src_eqv_elt = 0;
+ int src_eqv_volatile;
+ int src_eqv_in_memory;
+ int src_eqv_in_struct;
+ unsigned src_eqv_hash;
+
+ struct set *sets;
+
+ this_insn = insn;
+ writes_memory = init;
+
+ /* Find all the SETs and CLOBBERs in this instruction.
+ Record all the SETs in the array `set' and count them.
+ Also determine whether there is a CLOBBER that invalidates
+ all memory references, or all references at varying addresses. */
+
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1))
+ if (GET_CODE (XEXP (tem, 0)) == CLOBBER)
+ invalidate (SET_DEST (XEXP (tem, 0)));
+ }
+
+ if (GET_CODE (x) == SET)
+ {
+ sets = (struct set *) alloca (sizeof (struct set));
+ sets[0].rtl = x;
+
+ /* Ignore SETs that are unconditional jumps.
+ They never need cse processing, so this does not hurt.
+ The reason is not efficiency but rather
+ so that we can test at the end for instructions
+ that have been simplified to unconditional jumps
+ and not be misled by unchanged instructions
+ that were unconditional jumps to begin with. */
+ if (SET_DEST (x) == pc_rtx
+ && GET_CODE (SET_SRC (x)) == LABEL_REF)
+ ;
+
+ /* Don't count call-insns, (set (reg 0) (call ...)), as a set.
+ The hard function value register is used only once, to copy to
+ someplace else, so it isn't worth cse'ing (and on 80386 is unsafe)!
+ Ensure we invalidate the destination register. On the 80386 no
+ other code would invalidate it since it is a fixed_reg.
+ We need not check the return of apply_change_group; see canon_reg. */
+
+ else if (GET_CODE (SET_SRC (x)) == CALL)
+ {
+ canon_reg (SET_SRC (x), insn);
+ apply_change_group ();
+ fold_rtx (SET_SRC (x), insn);
+ invalidate (SET_DEST (x));
+ }
+ else
+ n_sets = 1;
+ }
+ else if (GET_CODE (x) == PARALLEL)
+ {
+ register int lim = XVECLEN (x, 0);
+
+ sets = (struct set *) alloca (lim * sizeof (struct set));
+
+ /* Find all regs explicitly clobbered in this insn,
+ and ensure they are not replaced with any other regs
+ elsewhere in this insn.
+ When a reg that is clobbered is also used for input,
+ we should presume that that is for a reason,
+ and we should not substitute some other register
+ which is not supposed to be clobbered.
+ Therefore, this loop cannot be merged into the one below
+ because a CALL may precede a CLOBBER and refer to the
+ value clobbered. We must not let a canonicalization do
+ anything in that case. */
+ for (i = 0; i < lim; i++)
+ {
+ register rtx y = XVECEXP (x, 0, i);
+ if (GET_CODE (y) == CLOBBER)
+ {
+ rtx clobbered = XEXP (y, 0);
+
+ if (GET_CODE (clobbered) == REG
+ || GET_CODE (clobbered) == SUBREG)
+ invalidate (clobbered);
+ else if (GET_CODE (clobbered) == STRICT_LOW_PART
+ || GET_CODE (clobbered) == ZERO_EXTRACT)
+ invalidate (XEXP (clobbered, 0));
+ }
+ }
+
+ for (i = 0; i < lim; i++)
+ {
+ register rtx y = XVECEXP (x, 0, i);
+ if (GET_CODE (y) == SET)
+ {
+ /* As above, we ignore unconditional jumps and call-insns and
+ ignore the result of apply_change_group. */
+ if (GET_CODE (SET_SRC (y)) == CALL)
+ {
+ canon_reg (SET_SRC (y), insn);
+ apply_change_group ();
+ fold_rtx (SET_SRC (y), insn);
+ invalidate (SET_DEST (y));
+ }
+ else if (SET_DEST (y) == pc_rtx
+ && GET_CODE (SET_SRC (y)) == LABEL_REF)
+ ;
+ else
+ sets[n_sets++].rtl = y;
+ }
+ else if (GET_CODE (y) == CLOBBER)
+ {
+ /* If we clobber memory, take note of that,
+ and canon the address.
+ This does nothing when a register is clobbered
+ because we have already invalidated the reg. */
+ if (GET_CODE (XEXP (y, 0)) == MEM)
+ {
+ canon_reg (XEXP (y, 0), NULL_RTX);
+ note_mem_written (XEXP (y, 0), &writes_memory);
+ }
+ }
+ else if (GET_CODE (y) == USE
+ && ! (GET_CODE (XEXP (y, 0)) == REG
+ && REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER))
+ canon_reg (y, NULL_RTX);
+ else if (GET_CODE (y) == CALL)
+ {
+ /* The result of apply_change_group can be ignored; see
+ canon_reg. */
+ canon_reg (y, insn);
+ apply_change_group ();
+ fold_rtx (y, insn);
+ }
+ }
+ }
+ else if (GET_CODE (x) == CLOBBER)
+ {
+ if (GET_CODE (XEXP (x, 0)) == MEM)
+ {
+ canon_reg (XEXP (x, 0), NULL_RTX);
+ note_mem_written (XEXP (x, 0), &writes_memory);
+ }
+ }
+
+ /* Canonicalize a USE of a pseudo register or memory location. */
+ else if (GET_CODE (x) == USE
+ && ! (GET_CODE (XEXP (x, 0)) == REG
+ && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER))
+ canon_reg (XEXP (x, 0), NULL_RTX);
+ else if (GET_CODE (x) == CALL)
+ {
+ /* The result of apply_change_group can be ignored; see canon_reg. */
+ canon_reg (x, insn);
+ apply_change_group ();
+ fold_rtx (x, insn);
+ }
+
+ /* Store the equivalent value in SRC_EQV, if different, or if the DEST
+ is a STRICT_LOW_PART. The latter condition is necessary because SRC_EQV
+ is handled specially for this case, and if it isn't set, then there will
+ be no equivalence for the destinatation. */
+ if (n_sets == 1 && REG_NOTES (insn) != 0
+ && (tem = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0
+ && (! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl))
+ || GET_CODE (SET_DEST (sets[0].rtl)) == STRICT_LOW_PART))
+ src_eqv = canon_reg (XEXP (tem, 0), NULL_RTX);
+
+ /* Canonicalize sources and addresses of destinations.
+ We do this in a separate pass to avoid problems when a MATCH_DUP is
+ present in the insn pattern. In that case, we want to ensure that
+ we don't break the duplicate nature of the pattern. So we will replace
+ both operands at the same time. Otherwise, we would fail to find an
+ equivalent substitution in the loop calling validate_change below.
+
+ We used to suppress canonicalization of DEST if it appears in SRC,
+ but we don't do this any more. */
+
+ for (i = 0; i < n_sets; i++)
+ {
+ rtx dest = SET_DEST (sets[i].rtl);
+ rtx src = SET_SRC (sets[i].rtl);
+ rtx new = canon_reg (src, insn);
+
+ if ((GET_CODE (new) == REG && GET_CODE (src) == REG
+ && ((REGNO (new) < FIRST_PSEUDO_REGISTER)
+ != (REGNO (src) < FIRST_PSEUDO_REGISTER)))
+ || insn_n_dups[recog_memoized (insn)] > 0)
+ validate_change (insn, &SET_SRC (sets[i].rtl), new, 1);
+ else
+ SET_SRC (sets[i].rtl) = new;
+
+ if (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT)
+ {
+ validate_change (insn, &XEXP (dest, 1),
+ canon_reg (XEXP (dest, 1), insn), 1);
+ validate_change (insn, &XEXP (dest, 2),
+ canon_reg (XEXP (dest, 2), insn), 1);
+ }
+
+ while (GET_CODE (dest) == SUBREG || GET_CODE (dest) == STRICT_LOW_PART
+ || GET_CODE (dest) == ZERO_EXTRACT
+ || GET_CODE (dest) == SIGN_EXTRACT)
+ dest = XEXP (dest, 0);
+
+ if (GET_CODE (dest) == MEM)
+ canon_reg (dest, insn);
+ }
+
+ /* Now that we have done all the replacements, we can apply the change
+ group and see if they all work. Note that this will cause some
+ canonicalizations that would have worked individually not to be applied
+ because some other canonicalization didn't work, but this should not
+ occur often.
+
+ The result of apply_change_group can be ignored; see canon_reg. */
+
+ apply_change_group ();
+
+ /* Set sets[i].src_elt to the class each source belongs to.
+ Detect assignments from or to volatile things
+ and set set[i] to zero so they will be ignored
+ in the rest of this function.
+
+ Nothing in this loop changes the hash table or the register chains. */
+
+ for (i = 0; i < n_sets; i++)
+ {
+ register rtx src, dest;
+ register rtx src_folded;
+ register struct table_elt *elt = 0, *p;
+ enum machine_mode mode;
+ rtx src_eqv_here;
+ rtx src_const = 0;
+ rtx src_related = 0;
+ struct table_elt *src_const_elt = 0;
+ int src_cost = 10000, src_eqv_cost = 10000, src_folded_cost = 10000;
+ int src_related_cost = 10000, src_elt_cost = 10000;
+ /* Set non-zero if we need to call force_const_mem on with the
+ contents of src_folded before using it. */
+ int src_folded_force_flag = 0;
+
+ dest = SET_DEST (sets[i].rtl);
+ src = SET_SRC (sets[i].rtl);
+
+ /* If SRC is a constant that has no machine mode,
+ hash it with the destination's machine mode.
+ This way we can keep different modes separate. */
+
+ mode = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src);
+ sets[i].mode = mode;
+
+ if (src_eqv)
+ {
+ enum machine_mode eqvmode = mode;
+ if (GET_CODE (dest) == STRICT_LOW_PART)
+ eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0)));
+ do_not_record = 0;
+ hash_arg_in_memory = 0;
+ hash_arg_in_struct = 0;
+ src_eqv = fold_rtx (src_eqv, insn);
+ src_eqv_hash = HASH (src_eqv, eqvmode);
+
+ /* Find the equivalence class for the equivalent expression. */
+
+ if (!do_not_record)
+ src_eqv_elt = lookup (src_eqv, src_eqv_hash, eqvmode);
+
+ src_eqv_volatile = do_not_record;
+ src_eqv_in_memory = hash_arg_in_memory;
+ src_eqv_in_struct = hash_arg_in_struct;
+ }
+
+ /* If this is a STRICT_LOW_PART assignment, src_eqv corresponds to the
+ value of the INNER register, not the destination. So it is not
+ a legal substitution for the source. But save it for later. */
+ if (GET_CODE (dest) == STRICT_LOW_PART)
+ src_eqv_here = 0;
+ else
+ src_eqv_here = src_eqv;
+
+ /* Simplify and foldable subexpressions in SRC. Then get the fully-
+ simplified result, which may not necessarily be valid. */
+ src_folded = fold_rtx (src, insn);
+
+ /* If storing a constant in a bitfield, pre-truncate the constant
+ so we will be able to record it later. */
+ if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT
+ || GET_CODE (SET_DEST (sets[i].rtl)) == SIGN_EXTRACT)
+ {
+ rtx width = XEXP (SET_DEST (sets[i].rtl), 1);
+
+ if (GET_CODE (src) == CONST_INT
+ && GET_CODE (width) == CONST_INT
+ && INTVAL (width) < HOST_BITS_PER_WIDE_INT
+ && (INTVAL (src) & ((HOST_WIDE_INT) (-1) << INTVAL (width))))
+ src_folded
+ = GEN_INT (INTVAL (src) & (((HOST_WIDE_INT) 1
+ << INTVAL (width)) - 1));
+ }
+
+ /* Compute SRC's hash code, and also notice if it
+ should not be recorded at all. In that case,
+ prevent any further processing of this assignment. */
+ do_not_record = 0;
+ hash_arg_in_memory = 0;
+ hash_arg_in_struct = 0;
+
+ sets[i].src = src;
+ sets[i].src_hash = HASH (src, mode);
+ sets[i].src_volatile = do_not_record;
+ sets[i].src_in_memory = hash_arg_in_memory;
+ sets[i].src_in_struct = hash_arg_in_struct;
+
+#if 0
+ /* It is no longer clear why we used to do this, but it doesn't
+ appear to still be needed. So let's try without it since this
+ code hurts cse'ing widened ops. */
+ /* If source is a perverse subreg (such as QI treated as an SI),
+ treat it as volatile. It may do the work of an SI in one context
+ where the extra bits are not being used, but cannot replace an SI
+ in general. */
+ if (GET_CODE (src) == SUBREG
+ && (GET_MODE_SIZE (GET_MODE (src))
+ > GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))))
+ sets[i].src_volatile = 1;
+#endif
+
+ /* Locate all possible equivalent forms for SRC. Try to replace
+ SRC in the insn with each cheaper equivalent.
+
+ We have the following types of equivalents: SRC itself, a folded
+ version, a value given in a REG_EQUAL note, or a value related
+ to a constant.
+
+ Each of these equivalents may be part of an additional class
+ of equivalents (if more than one is in the table, they must be in
+ the same class; we check for this).
+
+ If the source is volatile, we don't do any table lookups.
+
+ We note any constant equivalent for possible later use in a
+ REG_NOTE. */
+
+ if (!sets[i].src_volatile)
+ elt = lookup (src, sets[i].src_hash, mode);
+
+ sets[i].src_elt = elt;
+
+ if (elt && src_eqv_here && src_eqv_elt)
+ {
+ if (elt->first_same_value != src_eqv_elt->first_same_value)
+ {
+ /* The REG_EQUAL is indicating that two formerly distinct
+ classes are now equivalent. So merge them. */
+ merge_equiv_classes (elt, src_eqv_elt);
+ src_eqv_hash = HASH (src_eqv, elt->mode);
+ src_eqv_elt = lookup (src_eqv, src_eqv_hash, elt->mode);
+ }
+
+ src_eqv_here = 0;
+ }
+
+ else if (src_eqv_elt)
+ elt = src_eqv_elt;
+
+ /* Try to find a constant somewhere and record it in `src_const'.
+ Record its table element, if any, in `src_const_elt'. Look in
+ any known equivalences first. (If the constant is not in the
+ table, also set `sets[i].src_const_hash'). */
+ if (elt)
+ for (p = elt->first_same_value; p; p = p->next_same_value)
+ if (p->is_const)
+ {
+ src_const = p->exp;
+ src_const_elt = elt;
+ break;
+ }
+
+ if (src_const == 0
+ && (CONSTANT_P (src_folded)
+ /* Consider (minus (label_ref L1) (label_ref L2)) as
+ "constant" here so we will record it. This allows us
+ to fold switch statements when an ADDR_DIFF_VEC is used. */
+ || (GET_CODE (src_folded) == MINUS
+ && GET_CODE (XEXP (src_folded, 0)) == LABEL_REF
+ && GET_CODE (XEXP (src_folded, 1)) == LABEL_REF)))
+ src_const = src_folded, src_const_elt = elt;
+ else if (src_const == 0 && src_eqv_here && CONSTANT_P (src_eqv_here))
+ src_const = src_eqv_here, src_const_elt = src_eqv_elt;
+
+ /* If we don't know if the constant is in the table, get its
+ hash code and look it up. */
+ if (src_const && src_const_elt == 0)
+ {
+ sets[i].src_const_hash = HASH (src_const, mode);
+ src_const_elt = lookup (src_const, sets[i].src_const_hash, mode);
+ }
+
+ sets[i].src_const = src_const;
+ sets[i].src_const_elt = src_const_elt;
+
+ /* If the constant and our source are both in the table, mark them as
+ equivalent. Otherwise, if a constant is in the table but the source
+ isn't, set ELT to it. */
+ if (src_const_elt && elt
+ && src_const_elt->first_same_value != elt->first_same_value)
+ merge_equiv_classes (elt, src_const_elt);
+ else if (src_const_elt && elt == 0)
+ elt = src_const_elt;
+
+ /* See if there is a register linearly related to a constant
+ equivalent of SRC. */
+ if (src_const
+ && (GET_CODE (src_const) == CONST
+ || (src_const_elt && src_const_elt->related_value != 0)))
+ {
+ src_related = use_related_value (src_const, src_const_elt);
+ if (src_related)
+ {
+ struct table_elt *src_related_elt
+ = lookup (src_related, HASH (src_related, mode), mode);
+ if (src_related_elt && elt)
+ {
+ if (elt->first_same_value
+ != src_related_elt->first_same_value)
+ /* This can occur when we previously saw a CONST
+ involving a SYMBOL_REF and then see the SYMBOL_REF
+ twice. Merge the involved classes. */
+ merge_equiv_classes (elt, src_related_elt);
+
+ src_related = 0;
+ src_related_elt = 0;
+ }
+ else if (src_related_elt && elt == 0)
+ elt = src_related_elt;
+ }
+ }
+
+ /* See if we have a CONST_INT that is already in a register in a
+ wider mode. */
+
+ if (src_const && src_related == 0 && GET_CODE (src_const) == CONST_INT
+ && GET_MODE_CLASS (mode) == MODE_INT
+ && GET_MODE_BITSIZE (mode) < BITS_PER_WORD)
+ {
+ enum machine_mode wider_mode;
+
+ for (wider_mode = GET_MODE_WIDER_MODE (mode);
+ GET_MODE_BITSIZE (wider_mode) <= BITS_PER_WORD
+ && src_related == 0;
+ wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+ {
+ struct table_elt *const_elt
+ = lookup (src_const, HASH (src_const, wider_mode), wider_mode);
+
+ if (const_elt == 0)
+ continue;
+
+ for (const_elt = const_elt->first_same_value;
+ const_elt; const_elt = const_elt->next_same_value)
+ if (GET_CODE (const_elt->exp) == REG)
+ {
+ src_related = gen_lowpart_if_possible (mode,
+ const_elt->exp);
+ break;
+ }
+ }
+ }
+
+ /* Another possibility is that we have an AND with a constant in
+ a mode narrower than a word. If so, it might have been generated
+ as part of an "if" which would narrow the AND. If we already
+ have done the AND in a wider mode, we can use a SUBREG of that
+ value. */
+
+ if (flag_expensive_optimizations && ! src_related
+ && GET_CODE (src) == AND && GET_CODE (XEXP (src, 1)) == CONST_INT
+ && GET_MODE_SIZE (mode) < UNITS_PER_WORD)
+ {
+ enum machine_mode tmode;
+ rtx new_and = gen_rtx (AND, VOIDmode, NULL_RTX, XEXP (src, 1));
+
+ for (tmode = GET_MODE_WIDER_MODE (mode);
+ GET_MODE_SIZE (tmode) <= UNITS_PER_WORD;
+ tmode = GET_MODE_WIDER_MODE (tmode))
+ {
+ rtx inner = gen_lowpart_if_possible (tmode, XEXP (src, 0));
+ struct table_elt *larger_elt;
+
+ if (inner)
+ {
+ PUT_MODE (new_and, tmode);
+ XEXP (new_and, 0) = inner;
+ larger_elt = lookup (new_and, HASH (new_and, tmode), tmode);
+ if (larger_elt == 0)
+ continue;
+
+ for (larger_elt = larger_elt->first_same_value;
+ larger_elt; larger_elt = larger_elt->next_same_value)
+ if (GET_CODE (larger_elt->exp) == REG)
+ {
+ src_related
+ = gen_lowpart_if_possible (mode, larger_elt->exp);
+ break;
+ }
+
+ if (src_related)
+ break;
+ }
+ }
+ }
+
+#ifdef LOAD_EXTEND_OP
+ /* See if a MEM has already been loaded with a widening operation;
+ if it has, we can use a subreg of that. Many CISC machines
+ also have such operations, but this is only likely to be
+ beneficial these machines. */
+
+ if (flag_expensive_optimizations && src_related == 0
+ && (GET_MODE_SIZE (mode) < UNITS_PER_WORD)
+ && GET_MODE_CLASS (mode) == MODE_INT
+ && GET_CODE (src) == MEM && ! do_not_record
+ && LOAD_EXTEND_OP (mode) != NIL)
+ {
+ enum machine_mode tmode;
+
+ /* Set what we are trying to extend and the operation it might
+ have been extended with. */
+ PUT_CODE (memory_extend_rtx, LOAD_EXTEND_OP (mode));
+ XEXP (memory_extend_rtx, 0) = src;
+
+ for (tmode = GET_MODE_WIDER_MODE (mode);
+ GET_MODE_SIZE (tmode) <= UNITS_PER_WORD;
+ tmode = GET_MODE_WIDER_MODE (tmode))
+ {
+ struct table_elt *larger_elt;
+
+ PUT_MODE (memory_extend_rtx, tmode);
+ larger_elt = lookup (memory_extend_rtx,
+ HASH (memory_extend_rtx, tmode), tmode);
+ if (larger_elt == 0)
+ continue;
+
+ for (larger_elt = larger_elt->first_same_value;
+ larger_elt; larger_elt = larger_elt->next_same_value)
+ if (GET_CODE (larger_elt->exp) == REG)
+ {
+ src_related = gen_lowpart_if_possible (mode,
+ larger_elt->exp);
+ break;
+ }
+
+ if (src_related)
+ break;
+ }
+ }
+#endif /* LOAD_EXTEND_OP */
+
+ if (src == src_folded)
+ src_folded = 0;
+
+ /* At this point, ELT, if non-zero, points to a class of expressions
+ equivalent to the source of this SET and SRC, SRC_EQV, SRC_FOLDED,
+ and SRC_RELATED, if non-zero, each contain additional equivalent
+ expressions. Prune these latter expressions by deleting expressions
+ already in the equivalence class.
+
+ Check for an equivalent identical to the destination. If found,
+ this is the preferred equivalent since it will likely lead to
+ elimination of the insn. Indicate this by placing it in
+ `src_related'. */
+
+ if (elt) elt = elt->first_same_value;
+ for (p = elt; p; p = p->next_same_value)
+ {
+ enum rtx_code code = GET_CODE (p->exp);
+
+ /* If the expression is not valid, ignore it. Then we do not
+ have to check for validity below. In most cases, we can use
+ `rtx_equal_p', since canonicalization has already been done. */
+ if (code != REG && ! exp_equiv_p (p->exp, p->exp, 1, 0))
+ continue;
+
+ if (src && GET_CODE (src) == code && rtx_equal_p (src, p->exp))
+ src = 0;
+ else if (src_folded && GET_CODE (src_folded) == code
+ && rtx_equal_p (src_folded, p->exp))
+ src_folded = 0;
+ else if (src_eqv_here && GET_CODE (src_eqv_here) == code
+ && rtx_equal_p (src_eqv_here, p->exp))
+ src_eqv_here = 0;
+ else if (src_related && GET_CODE (src_related) == code
+ && rtx_equal_p (src_related, p->exp))
+ src_related = 0;
+
+ /* This is the same as the destination of the insns, we want
+ to prefer it. Copy it to src_related. The code below will
+ then give it a negative cost. */
+ if (GET_CODE (dest) == code && rtx_equal_p (p->exp, dest))
+ src_related = dest;
+
+ }
+
+ /* Find the cheapest valid equivalent, trying all the available
+ possibilities. Prefer items not in the hash table to ones
+ that are when they are equal cost. Note that we can never
+ worsen an insn as the current contents will also succeed.
+ If we find an equivalent identical to the destination, use it as best,
+ since this insn will probably be eliminated in that case. */
+ if (src)
+ {
+ if (rtx_equal_p (src, dest))
+ src_cost = -1;
+ else
+ src_cost = COST (src);
+ }
+
+ if (src_eqv_here)
+ {
+ if (rtx_equal_p (src_eqv_here, dest))
+ src_eqv_cost = -1;
+ else
+ src_eqv_cost = COST (src_eqv_here);
+ }
+
+ if (src_folded)
+ {
+ if (rtx_equal_p (src_folded, dest))
+ src_folded_cost = -1;
+ else
+ src_folded_cost = COST (src_folded);
+ }
+
+ if (src_related)
+ {
+ if (rtx_equal_p (src_related, dest))
+ src_related_cost = -1;
+ else
+ src_related_cost = COST (src_related);
+ }
+
+ /* If this was an indirect jump insn, a known label will really be
+ cheaper even though it looks more expensive. */
+ if (dest == pc_rtx && src_const && GET_CODE (src_const) == LABEL_REF)
+ src_folded = src_const, src_folded_cost = -1;
+
+ /* Terminate loop when replacement made. This must terminate since
+ the current contents will be tested and will always be valid. */
+ while (1)
+ {
+ rtx trial;
+
+ /* Skip invalid entries. */
+ while (elt && GET_CODE (elt->exp) != REG
+ && ! exp_equiv_p (elt->exp, elt->exp, 1, 0))
+ elt = elt->next_same_value;
+
+ if (elt) src_elt_cost = elt->cost;
+
+ /* Find cheapest and skip it for the next time. For items
+ of equal cost, use this order:
+ src_folded, src, src_eqv, src_related and hash table entry. */
+ if (src_folded_cost <= src_cost
+ && src_folded_cost <= src_eqv_cost
+ && src_folded_cost <= src_related_cost
+ && src_folded_cost <= src_elt_cost)
+ {
+ trial = src_folded, src_folded_cost = 10000;
+ if (src_folded_force_flag)
+ trial = force_const_mem (mode, trial);
+ }
+ else if (src_cost <= src_eqv_cost
+ && src_cost <= src_related_cost
+ && src_cost <= src_elt_cost)
+ trial = src, src_cost = 10000;
+ else if (src_eqv_cost <= src_related_cost
+ && src_eqv_cost <= src_elt_cost)
+ trial = copy_rtx (src_eqv_here), src_eqv_cost = 10000;
+ else if (src_related_cost <= src_elt_cost)
+ trial = copy_rtx (src_related), src_related_cost = 10000;
+ else
+ {
+ trial = copy_rtx (elt->exp);
+ elt = elt->next_same_value;
+ src_elt_cost = 10000;
+ }
+
+ /* We don't normally have an insn matching (set (pc) (pc)), so
+ check for this separately here. We will delete such an
+ insn below.
+
+ Tablejump insns contain a USE of the table, so simply replacing
+ the operand with the constant won't match. This is simply an
+ unconditional branch, however, and is therefore valid. Just
+ insert the substitution here and we will delete and re-emit
+ the insn later. */
+
+ if (n_sets == 1 && dest == pc_rtx
+ && (trial == pc_rtx
+ || (GET_CODE (trial) == LABEL_REF
+ && ! condjump_p (insn))))
+ {
+ /* If TRIAL is a label in front of a jump table, we are
+ really falling through the switch (this is how casesi
+ insns work), so we must branch around the table. */
+ if (GET_CODE (trial) == CODE_LABEL
+ && NEXT_INSN (trial) != 0
+ && GET_CODE (NEXT_INSN (trial)) == JUMP_INSN
+ && (GET_CODE (PATTERN (NEXT_INSN (trial))) == ADDR_DIFF_VEC
+ || GET_CODE (PATTERN (NEXT_INSN (trial))) == ADDR_VEC))
+
+ trial = gen_rtx (LABEL_REF, Pmode, get_label_after (trial));
+
+ SET_SRC (sets[i].rtl) = trial;
+ cse_jumps_altered = 1;
+ break;
+ }
+
+ /* Look for a substitution that makes a valid insn. */
+ else if (validate_change (insn, &SET_SRC (sets[i].rtl), trial, 0))
+ {
+ /* The result of apply_change_group can be ignored; see
+ canon_reg. */
+
+ validate_change (insn, &SET_SRC (sets[i].rtl),
+ canon_reg (SET_SRC (sets[i].rtl), insn),
+ 1);
+ apply_change_group ();
+ break;
+ }
+
+ /* If we previously found constant pool entries for
+ constants and this is a constant, try making a
+ pool entry. Put it in src_folded unless we already have done
+ this since that is where it likely came from. */
+
+ else if (constant_pool_entries_cost
+ && CONSTANT_P (trial)
+ && (src_folded == 0 || GET_CODE (src_folded) != MEM)
+ && GET_MODE_CLASS (mode) != MODE_CC)
+ {
+ src_folded_force_flag = 1;
+ src_folded = trial;
+ src_folded_cost = constant_pool_entries_cost;
+ }
+ }
+
+ src = SET_SRC (sets[i].rtl);
+
+ /* In general, it is good to have a SET with SET_SRC == SET_DEST.
+ However, there is an important exception: If both are registers
+ that are not the head of their equivalence class, replace SET_SRC
+ with the head of the class. If we do not do this, we will have
+ both registers live over a portion of the basic block. This way,
+ their lifetimes will likely abut instead of overlapping. */
+ if (GET_CODE (dest) == REG
+ && REGNO_QTY_VALID_P (REGNO (dest))
+ && qty_mode[reg_qty[REGNO (dest)]] == GET_MODE (dest)
+ && qty_first_reg[reg_qty[REGNO (dest)]] != REGNO (dest)
+ && GET_CODE (src) == REG && REGNO (src) == REGNO (dest)
+ /* Don't do this if the original insn had a hard reg as
+ SET_SRC. */
+ && (GET_CODE (sets[i].src) != REG
+ || REGNO (sets[i].src) >= FIRST_PSEUDO_REGISTER))
+ /* We can't call canon_reg here because it won't do anything if
+ SRC is a hard register. */
+ {
+ int first = qty_first_reg[reg_qty[REGNO (src)]];
+
+ src = SET_SRC (sets[i].rtl)
+ = first >= FIRST_PSEUDO_REGISTER ? regno_reg_rtx[first]
+ : gen_rtx (REG, GET_MODE (src), first);
+
+ /* If we had a constant that is cheaper than what we are now
+ setting SRC to, use that constant. We ignored it when we
+ thought we could make this into a no-op. */
+ if (src_const && COST (src_const) < COST (src)
+ && validate_change (insn, &SET_SRC (sets[i].rtl), src_const, 0))
+ src = src_const;
+ }
+
+ /* If we made a change, recompute SRC values. */
+ if (src != sets[i].src)
+ {
+ do_not_record = 0;
+ hash_arg_in_memory = 0;
+ hash_arg_in_struct = 0;
+ sets[i].src = src;
+ sets[i].src_hash = HASH (src, mode);
+ sets[i].src_volatile = do_not_record;
+ sets[i].src_in_memory = hash_arg_in_memory;
+ sets[i].src_in_struct = hash_arg_in_struct;
+ sets[i].src_elt = lookup (src, sets[i].src_hash, mode);
+ }
+
+ /* If this is a single SET, we are setting a register, and we have an
+ equivalent constant, we want to add a REG_NOTE. We don't want
+ to write a REG_EQUAL note for a constant pseudo since verifying that
+ that pseudo hasn't been eliminated is a pain. Such a note also
+ won't help anything. */
+ if (n_sets == 1 && src_const && GET_CODE (dest) == REG
+ && GET_CODE (src_const) != REG)
+ {
+ tem = find_reg_note (insn, REG_EQUAL, NULL_RTX);
+
+ /* Record the actual constant value in a REG_EQUAL note, making
+ a new one if one does not already exist. */
+ if (tem)
+ XEXP (tem, 0) = src_const;
+ else
+ REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUAL,
+ src_const, REG_NOTES (insn));
+
+ /* If storing a constant value in a register that
+ previously held the constant value 0,
+ record this fact with a REG_WAS_0 note on this insn.
+
+ Note that the *register* is required to have previously held 0,
+ not just any register in the quantity and we must point to the
+ insn that set that register to zero.
+
+ Rather than track each register individually, we just see if
+ the last set for this quantity was for this register. */
+
+ if (REGNO_QTY_VALID_P (REGNO (dest))
+ && qty_const[reg_qty[REGNO (dest)]] == const0_rtx)
+ {
+ /* See if we previously had a REG_WAS_0 note. */
+ rtx note = find_reg_note (insn, REG_WAS_0, NULL_RTX);
+ rtx const_insn = qty_const_insn[reg_qty[REGNO (dest)]];
+
+ if ((tem = single_set (const_insn)) != 0
+ && rtx_equal_p (SET_DEST (tem), dest))
+ {
+ if (note)
+ XEXP (note, 0) = const_insn;
+ else
+ REG_NOTES (insn) = gen_rtx (INSN_LIST, REG_WAS_0,
+ const_insn, REG_NOTES (insn));
+ }
+ }
+ }
+
+ /* Now deal with the destination. */
+ do_not_record = 0;
+ sets[i].inner_dest_loc = &SET_DEST (sets[0].rtl);
+
+ /* Look within any SIGN_EXTRACT or ZERO_EXTRACT
+ to the MEM or REG within it. */
+ while (GET_CODE (dest) == SIGN_EXTRACT
+ || GET_CODE (dest) == ZERO_EXTRACT
+ || GET_CODE (dest) == SUBREG
+ || GET_CODE (dest) == STRICT_LOW_PART)
+ {
+ sets[i].inner_dest_loc = &XEXP (dest, 0);
+ dest = XEXP (dest, 0);
+ }
+
+ sets[i].inner_dest = dest;
+
+ if (GET_CODE (dest) == MEM)
+ {
+ dest = fold_rtx (dest, insn);
+
+ /* Decide whether we invalidate everything in memory,
+ or just things at non-fixed places.
+ Writing a large aggregate must invalidate everything
+ because we don't know how long it is. */
+ note_mem_written (dest, &writes_memory);
+ }
+
+ /* Compute the hash code of the destination now,
+ before the effects of this instruction are recorded,
+ since the register values used in the address computation
+ are those before this instruction. */
+ sets[i].dest_hash = HASH (dest, mode);
+
+ /* Don't enter a bit-field in the hash table
+ because the value in it after the store
+ may not equal what was stored, due to truncation. */
+
+ if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT
+ || GET_CODE (SET_DEST (sets[i].rtl)) == SIGN_EXTRACT)
+ {
+ rtx width = XEXP (SET_DEST (sets[i].rtl), 1);
+
+ if (src_const != 0 && GET_CODE (src_const) == CONST_INT
+ && GET_CODE (width) == CONST_INT
+ && INTVAL (width) < HOST_BITS_PER_WIDE_INT
+ && ! (INTVAL (src_const)
+ & ((HOST_WIDE_INT) (-1) << INTVAL (width))))
+ /* Exception: if the value is constant,
+ and it won't be truncated, record it. */
+ ;
+ else
+ {
+ /* This is chosen so that the destination will be invalidated
+ but no new value will be recorded.
+ We must invalidate because sometimes constant
+ values can be recorded for bitfields. */
+ sets[i].src_elt = 0;
+ sets[i].src_volatile = 1;
+ src_eqv = 0;
+ src_eqv_elt = 0;
+ }
+ }
+
+ /* If only one set in a JUMP_INSN and it is now a no-op, we can delete
+ the insn. */
+ else if (n_sets == 1 && dest == pc_rtx && src == pc_rtx)
+ {
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ cse_jumps_altered = 1;
+ /* One less use of the label this insn used to jump to. */
+ --LABEL_NUSES (JUMP_LABEL (insn));
+ /* No more processing for this set. */
+ sets[i].rtl = 0;
+ }
+
+ /* If this SET is now setting PC to a label, we know it used to
+ be a conditional or computed branch. So we see if we can follow
+ it. If it was a computed branch, delete it and re-emit. */
+ else if (dest == pc_rtx && GET_CODE (src) == LABEL_REF)
+ {
+ rtx p;
+
+ /* If this is not in the format for a simple branch and
+ we are the only SET in it, re-emit it. */
+ if (! simplejump_p (insn) && n_sets == 1)
+ {
+ rtx new = emit_jump_insn_before (gen_jump (XEXP (src, 0)), insn);
+ JUMP_LABEL (new) = XEXP (src, 0);
+ LABEL_NUSES (XEXP (src, 0))++;
+ delete_insn (insn);
+ insn = new;
+ }
+ else
+ /* Otherwise, force rerecognition, since it probably had
+ a different pattern before.
+ This shouldn't really be necessary, since whatever
+ changed the source value above should have done this.
+ Until the right place is found, might as well do this here. */
+ INSN_CODE (insn) = -1;
+
+ /* Now that we've converted this jump to an unconditional jump,
+ there is dead code after it. Delete the dead code until we
+ reach a BARRIER, the end of the function, or a label. Do
+ not delete NOTEs except for NOTE_INSN_DELETED since later
+ phases assume these notes are retained. */
+
+ p = insn;
+
+ while (NEXT_INSN (p) != 0
+ && GET_CODE (NEXT_INSN (p)) != BARRIER
+ && GET_CODE (NEXT_INSN (p)) != CODE_LABEL)
+ {
+ if (GET_CODE (NEXT_INSN (p)) != NOTE
+ || NOTE_LINE_NUMBER (NEXT_INSN (p)) == NOTE_INSN_DELETED)
+ delete_insn (NEXT_INSN (p));
+ else
+ p = NEXT_INSN (p);
+ }
+
+ /* If we don't have a BARRIER immediately after INSN, put one there.
+ Much code assumes that there are no NOTEs between a JUMP_INSN and
+ BARRIER. */
+
+ if (NEXT_INSN (insn) == 0
+ || GET_CODE (NEXT_INSN (insn)) != BARRIER)
+ emit_barrier_after (insn);
+
+ /* We might have two BARRIERs separated by notes. Delete the second
+ one if so. */
+
+ if (p != insn && NEXT_INSN (p) != 0
+ && GET_CODE (NEXT_INSN (p)) == BARRIER)
+ delete_insn (NEXT_INSN (p));
+
+ cse_jumps_altered = 1;
+ sets[i].rtl = 0;
+ }
+
+ /* If destination is volatile, invalidate it and then do no further
+ processing for this assignment. */
+
+ else if (do_not_record)
+ {
+ if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG
+ || GET_CODE (dest) == MEM)
+ invalidate (dest);
+ else if (GET_CODE (dest) == STRICT_LOW_PART
+ || GET_CODE (dest) == ZERO_EXTRACT)
+ invalidate (XEXP (dest, 0));
+ sets[i].rtl = 0;
+ }
+
+ if (sets[i].rtl != 0 && dest != SET_DEST (sets[i].rtl))
+ sets[i].dest_hash = HASH (SET_DEST (sets[i].rtl), mode);
+
+#ifdef HAVE_cc0
+ /* If setting CC0, record what it was set to, or a constant, if it
+ is equivalent to a constant. If it is being set to a floating-point
+ value, make a COMPARE with the appropriate constant of 0. If we
+ don't do this, later code can interpret this as a test against
+ const0_rtx, which can cause problems if we try to put it into an
+ insn as a floating-point operand. */
+ if (dest == cc0_rtx)
+ {
+ this_insn_cc0 = src_const && mode != VOIDmode ? src_const : src;
+ this_insn_cc0_mode = mode;
+ if (FLOAT_MODE_P (mode))
+ this_insn_cc0 = gen_rtx (COMPARE, VOIDmode, this_insn_cc0,
+ CONST0_RTX (mode));
+ }
+#endif
+ }
+
+ /* Now enter all non-volatile source expressions in the hash table
+ if they are not already present.
+ Record their equivalence classes in src_elt.
+ This way we can insert the corresponding destinations into
+ the same classes even if the actual sources are no longer in them
+ (having been invalidated). */
+
+ if (src_eqv && src_eqv_elt == 0 && sets[0].rtl != 0 && ! src_eqv_volatile
+ && ! rtx_equal_p (src_eqv, SET_DEST (sets[0].rtl)))
+ {
+ register struct table_elt *elt;
+ register struct table_elt *classp = sets[0].src_elt;
+ rtx dest = SET_DEST (sets[0].rtl);
+ enum machine_mode eqvmode = GET_MODE (dest);
+
+ if (GET_CODE (dest) == STRICT_LOW_PART)
+ {
+ eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0)));
+ classp = 0;
+ }
+ if (insert_regs (src_eqv, classp, 0))
+ src_eqv_hash = HASH (src_eqv, eqvmode);
+ elt = insert (src_eqv, classp, src_eqv_hash, eqvmode);
+ elt->in_memory = src_eqv_in_memory;
+ elt->in_struct = src_eqv_in_struct;
+ src_eqv_elt = elt;
+
+ /* Check to see if src_eqv_elt is the same as a set source which
+ does not yet have an elt, and if so set the elt of the set source
+ to src_eqv_elt. */
+ for (i = 0; i < n_sets; i++)
+ if (sets[i].rtl && sets[i].src_elt == 0
+ && rtx_equal_p (SET_SRC (sets[i].rtl), src_eqv))
+ sets[i].src_elt = src_eqv_elt;
+ }
+
+ for (i = 0; i < n_sets; i++)
+ if (sets[i].rtl && ! sets[i].src_volatile
+ && ! rtx_equal_p (SET_SRC (sets[i].rtl), SET_DEST (sets[i].rtl)))
+ {
+ if (GET_CODE (SET_DEST (sets[i].rtl)) == STRICT_LOW_PART)
+ {
+ /* REG_EQUAL in setting a STRICT_LOW_PART
+ gives an equivalent for the entire destination register,
+ not just for the subreg being stored in now.
+ This is a more interesting equivalence, so we arrange later
+ to treat the entire reg as the destination. */
+ sets[i].src_elt = src_eqv_elt;
+ sets[i].src_hash = src_eqv_hash;
+ }
+ else
+ {
+ /* Insert source and constant equivalent into hash table, if not
+ already present. */
+ register struct table_elt *classp = src_eqv_elt;
+ register rtx src = sets[i].src;
+ register rtx dest = SET_DEST (sets[i].rtl);
+ enum machine_mode mode
+ = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src);
+
+ if (sets[i].src_elt == 0)
+ {
+ register struct table_elt *elt;
+
+ /* Note that these insert_regs calls cannot remove
+ any of the src_elt's, because they would have failed to
+ match if not still valid. */
+ if (insert_regs (src, classp, 0))
+ sets[i].src_hash = HASH (src, mode);
+ elt = insert (src, classp, sets[i].src_hash, mode);
+ elt->in_memory = sets[i].src_in_memory;
+ elt->in_struct = sets[i].src_in_struct;
+ sets[i].src_elt = classp = elt;
+ }
+
+ if (sets[i].src_const && sets[i].src_const_elt == 0
+ && src != sets[i].src_const
+ && ! rtx_equal_p (sets[i].src_const, src))
+ sets[i].src_elt = insert (sets[i].src_const, classp,
+ sets[i].src_const_hash, mode);
+ }
+ }
+ else if (sets[i].src_elt == 0)
+ /* If we did not insert the source into the hash table (e.g., it was
+ volatile), note the equivalence class for the REG_EQUAL value, if any,
+ so that the destination goes into that class. */
+ sets[i].src_elt = src_eqv_elt;
+
+ invalidate_from_clobbers (&writes_memory, x);
+
+ /* Some registers are invalidated by subroutine calls. Memory is
+ invalidated by non-constant calls. */
+
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ static struct write_data everything = {0, 1, 1, 1};
+
+ if (! CONST_CALL_P (insn))
+ invalidate_memory (&everything);
+ invalidate_for_call ();
+ }
+
+ /* Now invalidate everything set by this instruction.
+ If a SUBREG or other funny destination is being set,
+ sets[i].rtl is still nonzero, so here we invalidate the reg
+ a part of which is being set. */
+
+ for (i = 0; i < n_sets; i++)
+ if (sets[i].rtl)
+ {
+ register rtx dest = sets[i].inner_dest;
+
+ /* Needed for registers to remove the register from its
+ previous quantity's chain.
+ Needed for memory if this is a nonvarying address, unless
+ we have just done an invalidate_memory that covers even those. */
+ if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG
+ || (! writes_memory.all && ! cse_rtx_addr_varies_p (dest)))
+ invalidate (dest);
+ else if (GET_CODE (dest) == STRICT_LOW_PART
+ || GET_CODE (dest) == ZERO_EXTRACT)
+ invalidate (XEXP (dest, 0));
+ }
+
+ /* Make sure registers mentioned in destinations
+ are safe for use in an expression to be inserted.
+ This removes from the hash table
+ any invalid entry that refers to one of these registers.
+
+ We don't care about the return value from mention_regs because
+ we are going to hash the SET_DEST values unconditionally. */
+
+ for (i = 0; i < n_sets; i++)
+ if (sets[i].rtl && GET_CODE (SET_DEST (sets[i].rtl)) != REG)
+ mention_regs (SET_DEST (sets[i].rtl));
+
+ /* We may have just removed some of the src_elt's from the hash table.
+ So replace each one with the current head of the same class. */
+
+ for (i = 0; i < n_sets; i++)
+ if (sets[i].rtl)
+ {
+ if (sets[i].src_elt && sets[i].src_elt->first_same_value == 0)
+ /* If elt was removed, find current head of same class,
+ or 0 if nothing remains of that class. */
+ {
+ register struct table_elt *elt = sets[i].src_elt;
+
+ while (elt && elt->prev_same_value)
+ elt = elt->prev_same_value;
+
+ while (elt && elt->first_same_value == 0)
+ elt = elt->next_same_value;
+ sets[i].src_elt = elt ? elt->first_same_value : 0;
+ }
+ }
+
+ /* Now insert the destinations into their equivalence classes. */
+
+ for (i = 0; i < n_sets; i++)
+ if (sets[i].rtl)
+ {
+ register rtx dest = SET_DEST (sets[i].rtl);
+ register struct table_elt *elt;
+
+ /* Don't record value if we are not supposed to risk allocating
+ floating-point values in registers that might be wider than
+ memory. */
+ if ((flag_float_store
+ && GET_CODE (dest) == MEM
+ && FLOAT_MODE_P (GET_MODE (dest)))
+ /* Don't record values of destinations set inside a libcall block
+ since we might delete the libcall. Things should have been set
+ up so we won't want to reuse such a value, but we play it safe
+ here. */
+ || in_libcall_block
+ /* If we didn't put a REG_EQUAL value or a source into the hash
+ table, there is no point is recording DEST. */
+ || sets[i].src_elt == 0)
+ continue;
+
+ /* STRICT_LOW_PART isn't part of the value BEING set,
+ and neither is the SUBREG inside it.
+ Note that in this case SETS[I].SRC_ELT is really SRC_EQV_ELT. */
+ if (GET_CODE (dest) == STRICT_LOW_PART)
+ dest = SUBREG_REG (XEXP (dest, 0));
+
+ if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG)
+ /* Registers must also be inserted into chains for quantities. */
+ if (insert_regs (dest, sets[i].src_elt, 1))
+ /* If `insert_regs' changes something, the hash code must be
+ recalculated. */
+ sets[i].dest_hash = HASH (dest, GET_MODE (dest));
+
+ elt = insert (dest, sets[i].src_elt,
+ sets[i].dest_hash, GET_MODE (dest));
+ elt->in_memory = GET_CODE (sets[i].inner_dest) == MEM;
+ if (elt->in_memory)
+ {
+ /* This implicitly assumes a whole struct
+ need not have MEM_IN_STRUCT_P.
+ But a whole struct is *supposed* to have MEM_IN_STRUCT_P. */
+ elt->in_struct = (MEM_IN_STRUCT_P (sets[i].inner_dest)
+ || sets[i].inner_dest != SET_DEST (sets[i].rtl));
+ }
+
+ /* If we have (set (subreg:m1 (reg:m2 foo) 0) (bar:m1)), M1 is no
+ narrower than M2, and both M1 and M2 are the same number of words,
+ we are also doing (set (reg:m2 foo) (subreg:m2 (bar:m1) 0)) so
+ make that equivalence as well.
+
+ However, BAR may have equivalences for which gen_lowpart_if_possible
+ will produce a simpler value than gen_lowpart_if_possible applied to
+ BAR (e.g., if BAR was ZERO_EXTENDed from M2), so we will scan all
+ BAR's equivalences. If we don't get a simplified form, make
+ the SUBREG. It will not be used in an equivalence, but will
+ cause two similar assignments to be detected.
+
+ Note the loop below will find SUBREG_REG (DEST) since we have
+ already entered SRC and DEST of the SET in the table. */
+
+ if (GET_CODE (dest) == SUBREG
+ && (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) - 1)
+ / UNITS_PER_WORD)
+ == (GET_MODE_SIZE (GET_MODE (dest)) - 1)/ UNITS_PER_WORD)
+ && (GET_MODE_SIZE (GET_MODE (dest))
+ >= GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))))
+ && sets[i].src_elt != 0)
+ {
+ enum machine_mode new_mode = GET_MODE (SUBREG_REG (dest));
+ struct table_elt *elt, *classp = 0;
+
+ for (elt = sets[i].src_elt->first_same_value; elt;
+ elt = elt->next_same_value)
+ {
+ rtx new_src = 0;
+ unsigned src_hash;
+ struct table_elt *src_elt;
+
+ /* Ignore invalid entries. */
+ if (GET_CODE (elt->exp) != REG
+ && ! exp_equiv_p (elt->exp, elt->exp, 1, 0))
+ continue;
+
+ new_src = gen_lowpart_if_possible (new_mode, elt->exp);
+ if (new_src == 0)
+ new_src = gen_rtx (SUBREG, new_mode, elt->exp, 0);
+
+ src_hash = HASH (new_src, new_mode);
+ src_elt = lookup (new_src, src_hash, new_mode);
+
+ /* Put the new source in the hash table is if isn't
+ already. */
+ if (src_elt == 0)
+ {
+ if (insert_regs (new_src, classp, 0))
+ src_hash = HASH (new_src, new_mode);
+ src_elt = insert (new_src, classp, src_hash, new_mode);
+ src_elt->in_memory = elt->in_memory;
+ src_elt->in_struct = elt->in_struct;
+ }
+ else if (classp && classp != src_elt->first_same_value)
+ /* Show that two things that we've seen before are
+ actually the same. */
+ merge_equiv_classes (src_elt, classp);
+
+ classp = src_elt->first_same_value;
+ }
+ }
+ }
+
+ /* Special handling for (set REG0 REG1)
+ where REG0 is the "cheapest", cheaper than REG1.
+ After cse, REG1 will probably not be used in the sequel,
+ so (if easily done) change this insn to (set REG1 REG0) and
+ replace REG1 with REG0 in the previous insn that computed their value.
+ Then REG1 will become a dead store and won't cloud the situation
+ for later optimizations.
+
+ Do not make this change if REG1 is a hard register, because it will
+ then be used in the sequel and we may be changing a two-operand insn
+ into a three-operand insn.
+
+ Also do not do this if we are operating on a copy of INSN. */
+
+ if (n_sets == 1 && sets[0].rtl && GET_CODE (SET_DEST (sets[0].rtl)) == REG
+ && NEXT_INSN (PREV_INSN (insn)) == insn
+ && GET_CODE (SET_SRC (sets[0].rtl)) == REG
+ && REGNO (SET_SRC (sets[0].rtl)) >= FIRST_PSEUDO_REGISTER
+ && REGNO_QTY_VALID_P (REGNO (SET_SRC (sets[0].rtl)))
+ && (qty_first_reg[reg_qty[REGNO (SET_SRC (sets[0].rtl))]]
+ == REGNO (SET_DEST (sets[0].rtl))))
+ {
+ rtx prev = PREV_INSN (insn);
+ while (prev && GET_CODE (prev) == NOTE)
+ prev = PREV_INSN (prev);
+
+ if (prev && GET_CODE (prev) == INSN && GET_CODE (PATTERN (prev)) == SET
+ && SET_DEST (PATTERN (prev)) == SET_SRC (sets[0].rtl))
+ {
+ rtx dest = SET_DEST (sets[0].rtl);
+ rtx note = find_reg_note (prev, REG_EQUIV, NULL_RTX);
+
+ validate_change (prev, & SET_DEST (PATTERN (prev)), dest, 1);
+ validate_change (insn, & SET_DEST (sets[0].rtl),
+ SET_SRC (sets[0].rtl), 1);
+ validate_change (insn, & SET_SRC (sets[0].rtl), dest, 1);
+ apply_change_group ();
+
+ /* If REG1 was equivalent to a constant, REG0 is not. */
+ if (note)
+ PUT_REG_NOTE_KIND (note, REG_EQUAL);
+
+ /* If there was a REG_WAS_0 note on PREV, remove it. Move
+ any REG_WAS_0 note on INSN to PREV. */
+ note = find_reg_note (prev, REG_WAS_0, NULL_RTX);
+ if (note)
+ remove_note (prev, note);
+
+ note = find_reg_note (insn, REG_WAS_0, NULL_RTX);
+ if (note)
+ {
+ remove_note (insn, note);
+ XEXP (note, 1) = REG_NOTES (prev);
+ REG_NOTES (prev) = note;
+ }
+ }
+ }
+
+ /* If this is a conditional jump insn, record any known equivalences due to
+ the condition being tested. */
+
+ last_jump_equiv_class = 0;
+ if (GET_CODE (insn) == JUMP_INSN
+ && n_sets == 1 && GET_CODE (x) == SET
+ && GET_CODE (SET_SRC (x)) == IF_THEN_ELSE)
+ record_jump_equiv (insn, 0);
+
+#ifdef HAVE_cc0
+ /* If the previous insn set CC0 and this insn no longer references CC0,
+ delete the previous insn. Here we use the fact that nothing expects CC0
+ to be valid over an insn, which is true until the final pass. */
+ if (prev_insn && GET_CODE (prev_insn) == INSN
+ && (tem = single_set (prev_insn)) != 0
+ && SET_DEST (tem) == cc0_rtx
+ && ! reg_mentioned_p (cc0_rtx, x))
+ {
+ PUT_CODE (prev_insn, NOTE);
+ NOTE_LINE_NUMBER (prev_insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (prev_insn) = 0;
+ }
+
+ prev_insn_cc0 = this_insn_cc0;
+ prev_insn_cc0_mode = this_insn_cc0_mode;
+#endif
+
+ prev_insn = insn;
+}
+
+/* Store 1 in *WRITES_PTR for those categories of memory ref
+ that must be invalidated when the expression WRITTEN is stored in.
+ If WRITTEN is null, say everything must be invalidated. */
+
+static void
+note_mem_written (written, writes_ptr)
+ rtx written;
+ struct write_data *writes_ptr;
+{
+ static struct write_data everything = {0, 1, 1, 1};
+
+ if (written == 0)
+ *writes_ptr = everything;
+ else if (GET_CODE (written) == MEM)
+ {
+ /* Pushing or popping the stack invalidates just the stack pointer. */
+ rtx addr = XEXP (written, 0);
+ if ((GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == PRE_INC
+ || GET_CODE (addr) == POST_DEC || GET_CODE (addr) == POST_INC)
+ && GET_CODE (XEXP (addr, 0)) == REG
+ && REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM)
+ {
+ writes_ptr->sp = 1;
+ return;
+ }
+ else if (GET_MODE (written) == BLKmode)
+ *writes_ptr = everything;
+ /* (mem (scratch)) means clobber everything. */
+ else if (GET_CODE (addr) == SCRATCH)
+ *writes_ptr = everything;
+ else if (cse_rtx_addr_varies_p (written))
+ {
+ /* A varying address that is a sum indicates an array element,
+ and that's just as good as a structure element
+ in implying that we need not invalidate scalar variables.
+ However, we must allow QImode aliasing of scalars, because the
+ ANSI C standard allows character pointers to alias anything. */
+ if (! ((MEM_IN_STRUCT_P (written)
+ || GET_CODE (XEXP (written, 0)) == PLUS)
+ && GET_MODE (written) != QImode))
+ writes_ptr->all = 1;
+ writes_ptr->nonscalar = 1;
+ }
+ writes_ptr->var = 1;
+ }
+}
+
+/* Perform invalidation on the basis of everything about an insn
+ except for invalidating the actual places that are SET in it.
+ This includes the places CLOBBERed, and anything that might
+ alias with something that is SET or CLOBBERed.
+
+ W points to the writes_memory for this insn, a struct write_data
+ saying which kinds of memory references must be invalidated.
+ X is the pattern of the insn. */
+
+static void
+invalidate_from_clobbers (w, x)
+ struct write_data *w;
+ rtx x;
+{
+ /* If W->var is not set, W specifies no action.
+ If W->all is set, this step gets all memory refs
+ so they can be ignored in the rest of this function. */
+ if (w->var)
+ invalidate_memory (w);
+
+ if (w->sp)
+ {
+ if (reg_tick[STACK_POINTER_REGNUM] >= 0)
+ reg_tick[STACK_POINTER_REGNUM]++;
+
+ /* This should be *very* rare. */
+ if (TEST_HARD_REG_BIT (hard_regs_in_table, STACK_POINTER_REGNUM))
+ invalidate (stack_pointer_rtx);
+ }
+
+ if (GET_CODE (x) == CLOBBER)
+ {
+ rtx ref = XEXP (x, 0);
+ if (ref)
+ {
+ if (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
+ || (GET_CODE (ref) == MEM && ! w->all))
+ invalidate (ref);
+ else if (GET_CODE (ref) == STRICT_LOW_PART
+ || GET_CODE (ref) == ZERO_EXTRACT)
+ invalidate (XEXP (ref, 0));
+ }
+ }
+ else if (GET_CODE (x) == PARALLEL)
+ {
+ register int i;
+ for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
+ {
+ register rtx y = XVECEXP (x, 0, i);
+ if (GET_CODE (y) == CLOBBER)
+ {
+ rtx ref = XEXP (y, 0);
+ if (ref)
+ {
+ if (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
+ || (GET_CODE (ref) == MEM && !w->all))
+ invalidate (ref);
+ else if (GET_CODE (ref) == STRICT_LOW_PART
+ || GET_CODE (ref) == ZERO_EXTRACT)
+ invalidate (XEXP (ref, 0));
+ }
+ }
+ }
+ }
+}
+
+/* Process X, part of the REG_NOTES of an insn. Look at any REG_EQUAL notes
+ and replace any registers in them with either an equivalent constant
+ or the canonical form of the register. If we are inside an address,
+ only do this if the address remains valid.
+
+ OBJECT is 0 except when within a MEM in which case it is the MEM.
+
+ Return the replacement for X. */
+
+static rtx
+cse_process_notes (x, object)
+ rtx x;
+ rtx object;
+{
+ enum rtx_code code = GET_CODE (x);
+ char *fmt = GET_RTX_FORMAT (code);
+ int i;
+
+ switch (code)
+ {
+ case CONST_INT:
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case CONST_DOUBLE:
+ case PC:
+ case CC0:
+ case LO_SUM:
+ return x;
+
+ case MEM:
+ XEXP (x, 0) = cse_process_notes (XEXP (x, 0), x);
+ return x;
+
+ case EXPR_LIST:
+ case INSN_LIST:
+ if (REG_NOTE_KIND (x) == REG_EQUAL)
+ XEXP (x, 0) = cse_process_notes (XEXP (x, 0), NULL_RTX);
+ if (XEXP (x, 1))
+ XEXP (x, 1) = cse_process_notes (XEXP (x, 1), NULL_RTX);
+ return x;
+
+ case SIGN_EXTEND:
+ case ZERO_EXTEND:
+ {
+ rtx new = cse_process_notes (XEXP (x, 0), object);
+ /* We don't substitute VOIDmode constants into these rtx,
+ since they would impede folding. */
+ if (GET_MODE (new) != VOIDmode)
+ validate_change (object, &XEXP (x, 0), new, 0);
+ return x;
+ }
+
+ case REG:
+ i = reg_qty[REGNO (x)];
+
+ /* Return a constant or a constant register. */
+ if (REGNO_QTY_VALID_P (REGNO (x))
+ && qty_const[i] != 0
+ && (CONSTANT_P (qty_const[i])
+ || GET_CODE (qty_const[i]) == REG))
+ {
+ rtx new = gen_lowpart_if_possible (GET_MODE (x), qty_const[i]);
+ if (new)
+ return new;
+ }
+
+ /* Otherwise, canonicalize this register. */
+ return canon_reg (x, NULL_RTX);
+ }
+
+ for (i = 0; i < GET_RTX_LENGTH (code); i++)
+ if (fmt[i] == 'e')
+ validate_change (object, &XEXP (x, i),
+ cse_process_notes (XEXP (x, i), object), 0);
+
+ return x;
+}
+
+/* Find common subexpressions between the end test of a loop and the beginning
+ of the loop. LOOP_START is the CODE_LABEL at the start of a loop.
+
+ Often we have a loop where an expression in the exit test is used
+ in the body of the loop. For example "while (*p) *q++ = *p++;".
+ Because of the way we duplicate the loop exit test in front of the loop,
+ however, we don't detect that common subexpression. This will be caught
+ when global cse is implemented, but this is a quite common case.
+
+ This function handles the most common cases of these common expressions.
+ It is called after we have processed the basic block ending with the
+ NOTE_INSN_LOOP_END note that ends a loop and the previous JUMP_INSN
+ jumps to a label used only once. */
+
+static void
+cse_around_loop (loop_start)
+ rtx loop_start;
+{
+ rtx insn;
+ int i;
+ struct table_elt *p;
+
+ /* If the jump at the end of the loop doesn't go to the start, we don't
+ do anything. */
+ for (insn = PREV_INSN (loop_start);
+ insn && (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) >= 0);
+ insn = PREV_INSN (insn))
+ ;
+
+ if (insn == 0
+ || GET_CODE (insn) != NOTE
+ || NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG)
+ return;
+
+ /* If the last insn of the loop (the end test) was an NE comparison,
+ we will interpret it as an EQ comparison, since we fell through
+ the loop. Any equivalences resulting from that comparison are
+ therefore not valid and must be invalidated. */
+ if (last_jump_equiv_class)
+ for (p = last_jump_equiv_class->first_same_value; p;
+ p = p->next_same_value)
+ if (GET_CODE (p->exp) == MEM || GET_CODE (p->exp) == REG
+ || GET_CODE (p->exp) == SUBREG)
+ invalidate (p->exp);
+ else if (GET_CODE (p->exp) == STRICT_LOW_PART
+ || GET_CODE (p->exp) == ZERO_EXTRACT)
+ invalidate (XEXP (p->exp, 0));
+
+ /* Process insns starting after LOOP_START until we hit a CALL_INSN or
+ a CODE_LABEL (we could handle a CALL_INSN, but it isn't worth it).
+
+ The only thing we do with SET_DEST is invalidate entries, so we
+ can safely process each SET in order. It is slightly less efficient
+ to do so, but we only want to handle the most common cases. */
+
+ for (insn = NEXT_INSN (loop_start);
+ GET_CODE (insn) != CALL_INSN && GET_CODE (insn) != CODE_LABEL
+ && ! (GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END);
+ insn = NEXT_INSN (insn))
+ {
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && (GET_CODE (PATTERN (insn)) == SET
+ || GET_CODE (PATTERN (insn)) == CLOBBER))
+ cse_set_around_loop (PATTERN (insn), insn, loop_start);
+ else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && GET_CODE (PATTERN (insn)) == PARALLEL)
+ for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
+ if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET
+ || GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == CLOBBER)
+ cse_set_around_loop (XVECEXP (PATTERN (insn), 0, i), insn,
+ loop_start);
+ }
+}
+
+/* Variable used for communications between the next two routines. */
+
+static struct write_data skipped_writes_memory;
+
+/* Process one SET of an insn that was skipped. We ignore CLOBBERs
+ since they are done elsewhere. This function is called via note_stores. */
+
+static void
+invalidate_skipped_set (dest, set)
+ rtx set;
+ rtx dest;
+{
+ if (GET_CODE (set) == CLOBBER
+#ifdef HAVE_cc0
+ || dest == cc0_rtx
+#endif
+ || dest == pc_rtx)
+ return;
+
+ if (GET_CODE (dest) == MEM)
+ note_mem_written (dest, &skipped_writes_memory);
+
+ /* There are times when an address can appear varying and be a PLUS
+ during this scan when it would be a fixed address were we to know
+ the proper equivalences. So promote "nonscalar" to be "all". */
+ if (skipped_writes_memory.nonscalar)
+ skipped_writes_memory.all = 1;
+
+ if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG
+ || (! skipped_writes_memory.all && ! cse_rtx_addr_varies_p (dest)))
+ invalidate (dest);
+ else if (GET_CODE (dest) == STRICT_LOW_PART
+ || GET_CODE (dest) == ZERO_EXTRACT)
+ invalidate (XEXP (dest, 0));
+}
+
+/* Invalidate all insns from START up to the end of the function or the
+ next label. This called when we wish to CSE around a block that is
+ conditionally executed. */
+
+static void
+invalidate_skipped_block (start)
+ rtx start;
+{
+ rtx insn;
+ static struct write_data init = {0, 0, 0, 0};
+ static struct write_data everything = {0, 1, 1, 1};
+
+ for (insn = start; insn && GET_CODE (insn) != CODE_LABEL;
+ insn = NEXT_INSN (insn))
+ {
+ if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+ continue;
+
+ skipped_writes_memory = init;
+
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ invalidate_for_call ();
+ skipped_writes_memory = everything;
+ }
+
+ note_stores (PATTERN (insn), invalidate_skipped_set);
+ invalidate_from_clobbers (&skipped_writes_memory, PATTERN (insn));
+ }
+}
+
+/* Used for communication between the following two routines; contains a
+ value to be checked for modification. */
+
+static rtx cse_check_loop_start_value;
+
+/* If modifying X will modify the value in CSE_CHECK_LOOP_START_VALUE,
+ indicate that fact by setting CSE_CHECK_LOOP_START_VALUE to 0. */
+
+static void
+cse_check_loop_start (x, set)
+ rtx x;
+ rtx set;
+{
+ if (cse_check_loop_start_value == 0
+ || GET_CODE (x) == CC0 || GET_CODE (x) == PC)
+ return;
+
+ if ((GET_CODE (x) == MEM && GET_CODE (cse_check_loop_start_value) == MEM)
+ || reg_overlap_mentioned_p (x, cse_check_loop_start_value))
+ cse_check_loop_start_value = 0;
+}
+
+/* X is a SET or CLOBBER contained in INSN that was found near the start of
+ a loop that starts with the label at LOOP_START.
+
+ If X is a SET, we see if its SET_SRC is currently in our hash table.
+ If so, we see if it has a value equal to some register used only in the
+ loop exit code (as marked by jump.c).
+
+ If those two conditions are true, we search backwards from the start of
+ the loop to see if that same value was loaded into a register that still
+ retains its value at the start of the loop.
+
+ If so, we insert an insn after the load to copy the destination of that
+ load into the equivalent register and (try to) replace our SET_SRC with that
+ register.
+
+ In any event, we invalidate whatever this SET or CLOBBER modifies. */
+
+static void
+cse_set_around_loop (x, insn, loop_start)
+ rtx x;
+ rtx insn;
+ rtx loop_start;
+{
+ struct table_elt *src_elt;
+ static struct write_data init = {0, 0, 0, 0};
+ struct write_data writes_memory;
+
+ writes_memory = init;
+
+ /* If this is a SET, see if we can replace SET_SRC, but ignore SETs that
+ are setting PC or CC0 or whose SET_SRC is already a register. */
+ if (GET_CODE (x) == SET
+ && GET_CODE (SET_DEST (x)) != PC && GET_CODE (SET_DEST (x)) != CC0
+ && GET_CODE (SET_SRC (x)) != REG)
+ {
+ src_elt = lookup (SET_SRC (x),
+ HASH (SET_SRC (x), GET_MODE (SET_DEST (x))),
+ GET_MODE (SET_DEST (x)));
+
+ if (src_elt)
+ for (src_elt = src_elt->first_same_value; src_elt;
+ src_elt = src_elt->next_same_value)
+ if (GET_CODE (src_elt->exp) == REG && REG_LOOP_TEST_P (src_elt->exp)
+ && COST (src_elt->exp) < COST (SET_SRC (x)))
+ {
+ rtx p, set;
+
+ /* Look for an insn in front of LOOP_START that sets
+ something in the desired mode to SET_SRC (x) before we hit
+ a label or CALL_INSN. */
+
+ for (p = prev_nonnote_insn (loop_start);
+ p && GET_CODE (p) != CALL_INSN
+ && GET_CODE (p) != CODE_LABEL;
+ p = prev_nonnote_insn (p))
+ if ((set = single_set (p)) != 0
+ && GET_CODE (SET_DEST (set)) == REG
+ && GET_MODE (SET_DEST (set)) == src_elt->mode
+ && rtx_equal_p (SET_SRC (set), SET_SRC (x)))
+ {
+ /* We now have to ensure that nothing between P
+ and LOOP_START modified anything referenced in
+ SET_SRC (x). We know that nothing within the loop
+ can modify it, or we would have invalidated it in
+ the hash table. */
+ rtx q;
+
+ cse_check_loop_start_value = SET_SRC (x);
+ for (q = p; q != loop_start; q = NEXT_INSN (q))
+ if (GET_RTX_CLASS (GET_CODE (q)) == 'i')
+ note_stores (PATTERN (q), cse_check_loop_start);
+
+ /* If nothing was changed and we can replace our
+ SET_SRC, add an insn after P to copy its destination
+ to what we will be replacing SET_SRC with. */
+ if (cse_check_loop_start_value
+ && validate_change (insn, &SET_SRC (x),
+ src_elt->exp, 0))
+ emit_insn_after (gen_move_insn (src_elt->exp,
+ SET_DEST (set)),
+ p);
+ break;
+ }
+ }
+ }
+
+ /* Now invalidate anything modified by X. */
+ note_mem_written (SET_DEST (x), &writes_memory);
+
+ if (writes_memory.var)
+ invalidate_memory (&writes_memory);
+
+ /* See comment on similar code in cse_insn for explanation of these tests. */
+ if (GET_CODE (SET_DEST (x)) == REG || GET_CODE (SET_DEST (x)) == SUBREG
+ || (GET_CODE (SET_DEST (x)) == MEM && ! writes_memory.all
+ && ! cse_rtx_addr_varies_p (SET_DEST (x))))
+ invalidate (SET_DEST (x));
+ else if (GET_CODE (SET_DEST (x)) == STRICT_LOW_PART
+ || GET_CODE (SET_DEST (x)) == ZERO_EXTRACT)
+ invalidate (XEXP (SET_DEST (x), 0));
+}
+
+/* Find the end of INSN's basic block and return its range,
+ the total number of SETs in all the insns of the block, the last insn of the
+ block, and the branch path.
+
+ The branch path indicates which branches should be followed. If a non-zero
+ path size is specified, the block should be rescanned and a different set
+ of branches will be taken. The branch path is only used if
+ FLAG_CSE_FOLLOW_JUMPS or FLAG_CSE_SKIP_BLOCKS is non-zero.
+
+ DATA is a pointer to a struct cse_basic_block_data, defined below, that is
+ used to describe the block. It is filled in with the information about
+ the current block. The incoming structure's branch path, if any, is used
+ to construct the output branch path. */
+
+void
+cse_end_of_basic_block (insn, data, follow_jumps, after_loop, skip_blocks)
+ rtx insn;
+ struct cse_basic_block_data *data;
+ int follow_jumps;
+ int after_loop;
+ int skip_blocks;
+{
+ rtx p = insn, q;
+ int nsets = 0;
+ int low_cuid = INSN_CUID (insn), high_cuid = INSN_CUID (insn);
+ rtx next = GET_RTX_CLASS (GET_CODE (insn)) == 'i' ? insn : next_real_insn (insn);
+ int path_size = data->path_size;
+ int path_entry = 0;
+ int i;
+
+ /* Update the previous branch path, if any. If the last branch was
+ previously TAKEN, mark it NOT_TAKEN. If it was previously NOT_TAKEN,
+ shorten the path by one and look at the previous branch. We know that
+ at least one branch must have been taken if PATH_SIZE is non-zero. */
+ while (path_size > 0)
+ {
+ if (data->path[path_size - 1].status != NOT_TAKEN)
+ {
+ data->path[path_size - 1].status = NOT_TAKEN;
+ break;
+ }
+ else
+ path_size--;
+ }
+
+ /* Scan to end of this basic block. */
+ while (p && GET_CODE (p) != CODE_LABEL)
+ {
+ /* Don't cse out the end of a loop. This makes a difference
+ only for the unusual loops that always execute at least once;
+ all other loops have labels there so we will stop in any case.
+ Cse'ing out the end of the loop is dangerous because it
+ might cause an invariant expression inside the loop
+ to be reused after the end of the loop. This would make it
+ hard to move the expression out of the loop in loop.c,
+ especially if it is one of several equivalent expressions
+ and loop.c would like to eliminate it.
+
+ If we are running after loop.c has finished, we can ignore
+ the NOTE_INSN_LOOP_END. */
+
+ if (! after_loop && GET_CODE (p) == NOTE
+ && NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)
+ break;
+
+ /* Don't cse over a call to setjmp; on some machines (eg vax)
+ the regs restored by the longjmp come from
+ a later time than the setjmp. */
+ if (GET_CODE (p) == NOTE
+ && NOTE_LINE_NUMBER (p) == NOTE_INSN_SETJMP)
+ break;
+
+ /* A PARALLEL can have lots of SETs in it,
+ especially if it is really an ASM_OPERANDS. */
+ if (GET_RTX_CLASS (GET_CODE (p)) == 'i'
+ && GET_CODE (PATTERN (p)) == PARALLEL)
+ nsets += XVECLEN (PATTERN (p), 0);
+ else if (GET_CODE (p) != NOTE)
+ nsets += 1;
+
+ /* Ignore insns made by CSE; they cannot affect the boundaries of
+ the basic block. */
+
+ if (INSN_UID (p) <= max_uid && INSN_CUID (p) > high_cuid)
+ high_cuid = INSN_CUID (p);
+ if (INSN_UID (p) <= max_uid && INSN_CUID (p) < low_cuid)
+ low_cuid = INSN_CUID (p);
+
+ /* See if this insn is in our branch path. If it is and we are to
+ take it, do so. */
+ if (path_entry < path_size && data->path[path_entry].branch == p)
+ {
+ if (data->path[path_entry].status != NOT_TAKEN)
+ p = JUMP_LABEL (p);
+
+ /* Point to next entry in path, if any. */
+ path_entry++;
+ }
+
+ /* If this is a conditional jump, we can follow it if -fcse-follow-jumps
+ was specified, we haven't reached our maximum path length, there are
+ insns following the target of the jump, this is the only use of the
+ jump label, and the target label is preceded by a BARRIER.
+
+ Alternatively, we can follow the jump if it branches around a
+ block of code and there are no other branches into the block.
+ In this case invalidate_skipped_block will be called to invalidate any
+ registers set in the block when following the jump. */
+
+ else if ((follow_jumps || skip_blocks) && path_size < PATHLENGTH - 1
+ && GET_CODE (p) == JUMP_INSN
+ && GET_CODE (PATTERN (p)) == SET
+ && GET_CODE (SET_SRC (PATTERN (p))) == IF_THEN_ELSE
+ && LABEL_NUSES (JUMP_LABEL (p)) == 1
+ && NEXT_INSN (JUMP_LABEL (p)) != 0)
+ {
+ for (q = PREV_INSN (JUMP_LABEL (p)); q; q = PREV_INSN (q))
+ if ((GET_CODE (q) != NOTE
+ || NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_END
+ || NOTE_LINE_NUMBER (q) == NOTE_INSN_SETJMP)
+ && (GET_CODE (q) != CODE_LABEL || LABEL_NUSES (q) != 0))
+ break;
+
+ /* If we ran into a BARRIER, this code is an extension of the
+ basic block when the branch is taken. */
+ if (follow_jumps && q != 0 && GET_CODE (q) == BARRIER)
+ {
+ /* Don't allow ourself to keep walking around an
+ always-executed loop. */
+ if (next_real_insn (q) == next)
+ {
+ p = NEXT_INSN (p);
+ continue;
+ }
+
+ /* Similarly, don't put a branch in our path more than once. */
+ for (i = 0; i < path_entry; i++)
+ if (data->path[i].branch == p)
+ break;
+
+ if (i != path_entry)
+ break;
+
+ data->path[path_entry].branch = p;
+ data->path[path_entry++].status = TAKEN;
+
+ /* This branch now ends our path. It was possible that we
+ didn't see this branch the last time around (when the
+ insn in front of the target was a JUMP_INSN that was
+ turned into a no-op). */
+ path_size = path_entry;
+
+ p = JUMP_LABEL (p);
+ /* Mark block so we won't scan it again later. */
+ PUT_MODE (NEXT_INSN (p), QImode);
+ }
+ /* Detect a branch around a block of code. */
+ else if (skip_blocks && q != 0 && GET_CODE (q) != CODE_LABEL)
+ {
+ register rtx tmp;
+
+ if (next_real_insn (q) == next)
+ {
+ p = NEXT_INSN (p);
+ continue;
+ }
+
+ for (i = 0; i < path_entry; i++)
+ if (data->path[i].branch == p)
+ break;
+
+ if (i != path_entry)
+ break;
+
+ /* This is no_labels_between_p (p, q) with an added check for
+ reaching the end of a function (in case Q precedes P). */
+ for (tmp = NEXT_INSN (p); tmp && tmp != q; tmp = NEXT_INSN (tmp))
+ if (GET_CODE (tmp) == CODE_LABEL)
+ break;
+
+ if (tmp == q)
+ {
+ data->path[path_entry].branch = p;
+ data->path[path_entry++].status = AROUND;
+
+ path_size = path_entry;
+
+ p = JUMP_LABEL (p);
+ /* Mark block so we won't scan it again later. */
+ PUT_MODE (NEXT_INSN (p), QImode);
+ }
+ }
+ }
+ p = NEXT_INSN (p);
+ }
+
+ data->low_cuid = low_cuid;
+ data->high_cuid = high_cuid;
+ data->nsets = nsets;
+ data->last = p;
+
+ /* If all jumps in the path are not taken, set our path length to zero
+ so a rescan won't be done. */
+ for (i = path_size - 1; i >= 0; i--)
+ if (data->path[i].status != NOT_TAKEN)
+ break;
+
+ if (i == -1)
+ data->path_size = 0;
+ else
+ data->path_size = path_size;
+
+ /* End the current branch path. */
+ data->path[path_size].branch = 0;
+}
+
+/* Perform cse on the instructions of a function.
+ F is the first instruction.
+ NREGS is one plus the highest pseudo-reg number used in the instruction.
+
+ AFTER_LOOP is 1 if this is the cse call done after loop optimization
+ (only if -frerun-cse-after-loop).
+
+ Returns 1 if jump_optimize should be redone due to simplifications
+ in conditional jump instructions. */
+
+int
+cse_main (f, nregs, after_loop, file)
+ rtx f;
+ int nregs;
+ int after_loop;
+ FILE *file;
+{
+ struct cse_basic_block_data val;
+ register rtx insn = f;
+ register int i;
+
+ cse_jumps_altered = 0;
+ constant_pool_entries_cost = 0;
+ val.path_size = 0;
+
+ init_recog ();
+
+ max_reg = nregs;
+
+ all_minus_one = (int *) alloca (nregs * sizeof (int));
+ consec_ints = (int *) alloca (nregs * sizeof (int));
+
+ for (i = 0; i < nregs; i++)
+ {
+ all_minus_one[i] = -1;
+ consec_ints[i] = i;
+ }
+
+ reg_next_eqv = (int *) alloca (nregs * sizeof (int));
+ reg_prev_eqv = (int *) alloca (nregs * sizeof (int));
+ reg_qty = (int *) alloca (nregs * sizeof (int));
+ reg_in_table = (int *) alloca (nregs * sizeof (int));
+ reg_tick = (int *) alloca (nregs * sizeof (int));
+
+#ifdef LOAD_EXTEND_OP
+
+ /* Allocate scratch rtl here. cse_insn will fill in the memory reference
+ and change the code and mode as appropriate. */
+ memory_extend_rtx = gen_rtx (ZERO_EXTEND, VOIDmode, 0);
+#endif
+
+ /* Discard all the free elements of the previous function
+ since they are allocated in the temporarily obstack. */
+ bzero ((char *) table, sizeof table);
+ free_element_chain = 0;
+ n_elements_made = 0;
+
+ /* Find the largest uid. */
+
+ max_uid = get_max_uid ();
+ uid_cuid = (int *) alloca ((max_uid + 1) * sizeof (int));
+ bzero ((char *) uid_cuid, (max_uid + 1) * sizeof (int));
+
+ /* Compute the mapping from uids to cuids.
+ CUIDs are numbers assigned to insns, like uids,
+ except that cuids increase monotonically through the code.
+ Don't assign cuids to line-number NOTEs, so that the distance in cuids
+ between two insns is not affected by -g. */
+
+ for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) != NOTE
+ || NOTE_LINE_NUMBER (insn) < 0)
+ INSN_CUID (insn) = ++i;
+ else
+ /* Give a line number note the same cuid as preceding insn. */
+ INSN_CUID (insn) = i;
+ }
+
+ /* Initialize which registers are clobbered by calls. */
+
+ CLEAR_HARD_REG_SET (regs_invalidated_by_call);
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if ((call_used_regs[i]
+ /* Used to check !fixed_regs[i] here, but that isn't safe;
+ fixed regs are still call-clobbered, and sched can get
+ confused if they can "live across calls".
+
+ The frame pointer is always preserved across calls. The arg
+ pointer is if it is fixed. The stack pointer usually is, unless
+ RETURN_POPS_ARGS, in which case an explicit CLOBBER
+ will be present. If we are generating PIC code, the PIC offset
+ table register is preserved across calls. */
+
+ && i != STACK_POINTER_REGNUM
+ && i != FRAME_POINTER_REGNUM
+#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ && i != HARD_FRAME_POINTER_REGNUM
+#endif
+#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ && ! (i == ARG_POINTER_REGNUM && fixed_regs[i])
+#endif
+#if defined (PIC_OFFSET_TABLE_REGNUM) && !defined (PIC_OFFSET_TABLE_REG_CALL_CLOBBERED)
+ && ! (i == PIC_OFFSET_TABLE_REGNUM && flag_pic)
+#endif
+ )
+ || global_regs[i])
+ SET_HARD_REG_BIT (regs_invalidated_by_call, i);
+
+ /* Loop over basic blocks.
+ Compute the maximum number of qty's needed for each basic block
+ (which is 2 for each SET). */
+ insn = f;
+ while (insn)
+ {
+ cse_end_of_basic_block (insn, &val, flag_cse_follow_jumps, after_loop,
+ flag_cse_skip_blocks);
+
+ /* If this basic block was already processed or has no sets, skip it. */
+ if (val.nsets == 0 || GET_MODE (insn) == QImode)
+ {
+ PUT_MODE (insn, VOIDmode);
+ insn = (val.last ? NEXT_INSN (val.last) : 0);
+ val.path_size = 0;
+ continue;
+ }
+
+ cse_basic_block_start = val.low_cuid;
+ cse_basic_block_end = val.high_cuid;
+ max_qty = val.nsets * 2;
+
+ if (file)
+ fprintf (file, ";; Processing block from %d to %d, %d sets.\n",
+ INSN_UID (insn), val.last ? INSN_UID (val.last) : 0,
+ val.nsets);
+
+ /* Make MAX_QTY bigger to give us room to optimize
+ past the end of this basic block, if that should prove useful. */
+ if (max_qty < 500)
+ max_qty = 500;
+
+ max_qty += max_reg;
+
+ /* If this basic block is being extended by following certain jumps,
+ (see `cse_end_of_basic_block'), we reprocess the code from the start.
+ Otherwise, we start after this basic block. */
+ if (val.path_size > 0)
+ cse_basic_block (insn, val.last, val.path, 0);
+ else
+ {
+ int old_cse_jumps_altered = cse_jumps_altered;
+ rtx temp;
+
+ /* When cse changes a conditional jump to an unconditional
+ jump, we want to reprocess the block, since it will give
+ us a new branch path to investigate. */
+ cse_jumps_altered = 0;
+ temp = cse_basic_block (insn, val.last, val.path, ! after_loop);
+ if (cse_jumps_altered == 0
+ || (flag_cse_follow_jumps == 0 && flag_cse_skip_blocks == 0))
+ insn = temp;
+
+ cse_jumps_altered |= old_cse_jumps_altered;
+ }
+
+#ifdef USE_C_ALLOCA
+ alloca (0);
+#endif
+ }
+
+ /* Tell refers_to_mem_p that qty_const info is not available. */
+ qty_const = 0;
+
+ if (max_elements_made < n_elements_made)
+ max_elements_made = n_elements_made;
+
+ return cse_jumps_altered;
+}
+
+/* Process a single basic block. FROM and TO and the limits of the basic
+ block. NEXT_BRANCH points to the branch path when following jumps or
+ a null path when not following jumps.
+
+ AROUND_LOOP is non-zero if we are to try to cse around to the start of a
+ loop. This is true when we are being called for the last time on a
+ block and this CSE pass is before loop.c. */
+
+static rtx
+cse_basic_block (from, to, next_branch, around_loop)
+ register rtx from, to;
+ struct branch_path *next_branch;
+ int around_loop;
+{
+ register rtx insn;
+ int to_usage = 0;
+ int in_libcall_block = 0;
+
+ /* Each of these arrays is undefined before max_reg, so only allocate
+ the space actually needed and adjust the start below. */
+
+ qty_first_reg = (int *) alloca ((max_qty - max_reg) * sizeof (int));
+ qty_last_reg = (int *) alloca ((max_qty - max_reg) * sizeof (int));
+ qty_mode= (enum machine_mode *) alloca ((max_qty - max_reg) * sizeof (enum machine_mode));
+ qty_const = (rtx *) alloca ((max_qty - max_reg) * sizeof (rtx));
+ qty_const_insn = (rtx *) alloca ((max_qty - max_reg) * sizeof (rtx));
+ qty_comparison_code
+ = (enum rtx_code *) alloca ((max_qty - max_reg) * sizeof (enum rtx_code));
+ qty_comparison_qty = (int *) alloca ((max_qty - max_reg) * sizeof (int));
+ qty_comparison_const = (rtx *) alloca ((max_qty - max_reg) * sizeof (rtx));
+
+ qty_first_reg -= max_reg;
+ qty_last_reg -= max_reg;
+ qty_mode -= max_reg;
+ qty_const -= max_reg;
+ qty_const_insn -= max_reg;
+ qty_comparison_code -= max_reg;
+ qty_comparison_qty -= max_reg;
+ qty_comparison_const -= max_reg;
+
+ new_basic_block ();
+
+ /* TO might be a label. If so, protect it from being deleted. */
+ if (to != 0 && GET_CODE (to) == CODE_LABEL)
+ ++LABEL_NUSES (to);
+
+ for (insn = from; insn != to; insn = NEXT_INSN (insn))
+ {
+ register enum rtx_code code;
+
+ /* See if this is a branch that is part of the path. If so, and it is
+ to be taken, do so. */
+ if (next_branch->branch == insn)
+ {
+ enum taken status = next_branch++->status;
+ if (status != NOT_TAKEN)
+ {
+ if (status == TAKEN)
+ record_jump_equiv (insn, 1);
+ else
+ invalidate_skipped_block (NEXT_INSN (insn));
+
+ /* Set the last insn as the jump insn; it doesn't affect cc0.
+ Then follow this branch. */
+#ifdef HAVE_cc0
+ prev_insn_cc0 = 0;
+#endif
+ prev_insn = insn;
+ insn = JUMP_LABEL (insn);
+ continue;
+ }
+ }
+
+ code = GET_CODE (insn);
+ if (GET_MODE (insn) == QImode)
+ PUT_MODE (insn, VOIDmode);
+
+ if (GET_RTX_CLASS (code) == 'i')
+ {
+ /* Process notes first so we have all notes in canonical forms when
+ looking for duplicate operations. */
+
+ if (REG_NOTES (insn))
+ REG_NOTES (insn) = cse_process_notes (REG_NOTES (insn), NULL_RTX);
+
+ /* Track when we are inside in LIBCALL block. Inside such a block,
+ we do not want to record destinations. The last insn of a
+ LIBCALL block is not considered to be part of the block, since
+ its destination is the result of the block and hence should be
+ recorded. */
+
+ if (find_reg_note (insn, REG_LIBCALL, NULL_RTX))
+ in_libcall_block = 1;
+ else if (find_reg_note (insn, REG_RETVAL, NULL_RTX))
+ in_libcall_block = 0;
+
+ cse_insn (insn, in_libcall_block);
+ }
+
+ /* If INSN is now an unconditional jump, skip to the end of our
+ basic block by pretending that we just did the last insn in the
+ basic block. If we are jumping to the end of our block, show
+ that we can have one usage of TO. */
+
+ if (simplejump_p (insn))
+ {
+ if (to == 0)
+ return 0;
+
+ if (JUMP_LABEL (insn) == to)
+ to_usage = 1;
+
+ /* Maybe TO was deleted because the jump is unconditional.
+ If so, there is nothing left in this basic block. */
+ /* ??? Perhaps it would be smarter to set TO
+ to whatever follows this insn,
+ and pretend the basic block had always ended here. */
+ if (INSN_DELETED_P (to))
+ break;
+
+ insn = PREV_INSN (to);
+ }
+
+ /* See if it is ok to keep on going past the label
+ which used to end our basic block. Remember that we incremented
+ the count of that label, so we decrement it here. If we made
+ a jump unconditional, TO_USAGE will be one; in that case, we don't
+ want to count the use in that jump. */
+
+ if (to != 0 && NEXT_INSN (insn) == to
+ && GET_CODE (to) == CODE_LABEL && --LABEL_NUSES (to) == to_usage)
+ {
+ struct cse_basic_block_data val;
+
+ insn = NEXT_INSN (to);
+
+ if (LABEL_NUSES (to) == 0)
+ delete_insn (to);
+
+ /* Find the end of the following block. Note that we won't be
+ following branches in this case. If TO was the last insn
+ in the function, we are done. Similarly, if we deleted the
+ insn after TO, it must have been because it was preceded by
+ a BARRIER. In that case, we are done with this block because it
+ has no continuation. */
+
+ if (insn == 0 || INSN_DELETED_P (insn))
+ return 0;
+
+ to_usage = 0;
+ val.path_size = 0;
+ cse_end_of_basic_block (insn, &val, 0, 0, 0);
+
+ /* If the tables we allocated have enough space left
+ to handle all the SETs in the next basic block,
+ continue through it. Otherwise, return,
+ and that block will be scanned individually. */
+ if (val.nsets * 2 + next_qty > max_qty)
+ break;
+
+ cse_basic_block_start = val.low_cuid;
+ cse_basic_block_end = val.high_cuid;
+ to = val.last;
+
+ /* Prevent TO from being deleted if it is a label. */
+ if (to != 0 && GET_CODE (to) == CODE_LABEL)
+ ++LABEL_NUSES (to);
+
+ /* Back up so we process the first insn in the extension. */
+ insn = PREV_INSN (insn);
+ }
+ }
+
+ if (next_qty > max_qty)
+ abort ();
+
+ /* If we are running before loop.c, we stopped on a NOTE_INSN_LOOP_END, and
+ the previous insn is the only insn that branches to the head of a loop,
+ we can cse into the loop. Don't do this if we changed the jump
+ structure of a loop unless we aren't going to be following jumps. */
+
+ if ((cse_jumps_altered == 0
+ || (flag_cse_follow_jumps == 0 && flag_cse_skip_blocks == 0))
+ && around_loop && to != 0
+ && GET_CODE (to) == NOTE && NOTE_LINE_NUMBER (to) == NOTE_INSN_LOOP_END
+ && GET_CODE (PREV_INSN (to)) == JUMP_INSN
+ && JUMP_LABEL (PREV_INSN (to)) != 0
+ && LABEL_NUSES (JUMP_LABEL (PREV_INSN (to))) == 1)
+ cse_around_loop (JUMP_LABEL (PREV_INSN (to)));
+
+ return to ? NEXT_INSN (to) : 0;
+}
+
+/* Count the number of times registers are used (not set) in X.
+ COUNTS is an array in which we accumulate the count, INCR is how much
+ we count each register usage.
+
+ Don't count a usage of DEST, which is the SET_DEST of a SET which
+ contains X in its SET_SRC. This is because such a SET does not
+ modify the liveness of DEST. */
+
+static void
+count_reg_usage (x, counts, dest, incr)
+ rtx x;
+ int *counts;
+ rtx dest;
+ int incr;
+{
+ enum rtx_code code;
+ char *fmt;
+ int i, j;
+
+ if (x == 0)
+ return;
+
+ switch (code = GET_CODE (x))
+ {
+ case REG:
+ if (x != dest)
+ counts[REGNO (x)] += incr;
+ return;
+
+ case PC:
+ case CC0:
+ case CONST:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case CLOBBER:
+ return;
+
+ case SET:
+ /* Unless we are setting a REG, count everything in SET_DEST. */
+ if (GET_CODE (SET_DEST (x)) != REG)
+ count_reg_usage (SET_DEST (x), counts, NULL_RTX, incr);
+
+ /* If SRC has side-effects, then we can't delete this insn, so the
+ usage of SET_DEST inside SRC counts.
+
+ ??? Strictly-speaking, we might be preserving this insn
+ because some other SET has side-effects, but that's hard
+ to do and can't happen now. */
+ count_reg_usage (SET_SRC (x), counts,
+ side_effects_p (SET_SRC (x)) ? NULL_RTX : SET_DEST (x),
+ incr);
+ return;
+
+ case CALL_INSN:
+ count_reg_usage (CALL_INSN_FUNCTION_USAGE (x), counts, NULL_RTX, incr);
+
+ /* ... falls through ... */
+ case INSN:
+ case JUMP_INSN:
+ count_reg_usage (PATTERN (x), counts, NULL_RTX, incr);
+
+ /* Things used in a REG_EQUAL note aren't dead since loop may try to
+ use them. */
+
+ count_reg_usage (REG_NOTES (x), counts, NULL_RTX, incr);
+ return;
+
+ case EXPR_LIST:
+ case INSN_LIST:
+ if (REG_NOTE_KIND (x) == REG_EQUAL
+ || GET_CODE (XEXP (x,0)) == USE)
+ count_reg_usage (XEXP (x, 0), counts, NULL_RTX, incr);
+ count_reg_usage (XEXP (x, 1), counts, NULL_RTX, incr);
+ return;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ count_reg_usage (XEXP (x, i), counts, dest, incr);
+ else if (fmt[i] == 'E')
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ count_reg_usage (XVECEXP (x, i, j), counts, dest, incr);
+ }
+}
+
+/* Scan all the insns and delete any that are dead; i.e., they store a register
+ that is never used or they copy a register to itself.
+
+ This is used to remove insns made obviously dead by cse. It improves the
+ heuristics in loop since it won't try to move dead invariants out of loops
+ or make givs for dead quantities. The remaining passes of the compilation
+ are also sped up. */
+
+void
+delete_dead_from_cse (insns, nreg)
+ rtx insns;
+ int nreg;
+{
+ int *counts = (int *) alloca (nreg * sizeof (int));
+ rtx insn, prev;
+ rtx tem;
+ int i;
+ int in_libcall = 0;
+
+ /* First count the number of times each register is used. */
+ bzero ((char *) counts, sizeof (int) * nreg);
+ for (insn = next_real_insn (insns); insn; insn = next_real_insn (insn))
+ count_reg_usage (insn, counts, NULL_RTX, 1);
+
+ /* Go from the last insn to the first and delete insns that only set unused
+ registers or copy a register to itself. As we delete an insn, remove
+ usage counts for registers it uses. */
+ for (insn = prev_real_insn (get_last_insn ()); insn; insn = prev)
+ {
+ int live_insn = 0;
+
+ prev = prev_real_insn (insn);
+
+ /* Don't delete any insns that are part of a libcall block.
+ Flow or loop might get confused if we did that. Remember
+ that we are scanning backwards. */
+ if (find_reg_note (insn, REG_RETVAL, NULL_RTX))
+ in_libcall = 1;
+
+ if (in_libcall)
+ live_insn = 1;
+ else if (GET_CODE (PATTERN (insn)) == SET)
+ {
+ if (GET_CODE (SET_DEST (PATTERN (insn))) == REG
+ && SET_DEST (PATTERN (insn)) == SET_SRC (PATTERN (insn)))
+ ;
+
+#ifdef HAVE_cc0
+ else if (GET_CODE (SET_DEST (PATTERN (insn))) == CC0
+ && ! side_effects_p (SET_SRC (PATTERN (insn)))
+ && ((tem = next_nonnote_insn (insn)) == 0
+ || GET_RTX_CLASS (GET_CODE (tem)) != 'i'
+ || ! reg_referenced_p (cc0_rtx, PATTERN (tem))))
+ ;
+#endif
+ else if (GET_CODE (SET_DEST (PATTERN (insn))) != REG
+ || REGNO (SET_DEST (PATTERN (insn))) < FIRST_PSEUDO_REGISTER
+ || counts[REGNO (SET_DEST (PATTERN (insn)))] != 0
+ || side_effects_p (SET_SRC (PATTERN (insn))))
+ live_insn = 1;
+ }
+ else if (GET_CODE (PATTERN (insn)) == PARALLEL)
+ for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
+ {
+ rtx elt = XVECEXP (PATTERN (insn), 0, i);
+
+ if (GET_CODE (elt) == SET)
+ {
+ if (GET_CODE (SET_DEST (elt)) == REG
+ && SET_DEST (elt) == SET_SRC (elt))
+ ;
+
+#ifdef HAVE_cc0
+ else if (GET_CODE (SET_DEST (elt)) == CC0
+ && ! side_effects_p (SET_SRC (elt))
+ && ((tem = next_nonnote_insn (insn)) == 0
+ || GET_RTX_CLASS (GET_CODE (tem)) != 'i'
+ || ! reg_referenced_p (cc0_rtx, PATTERN (tem))))
+ ;
+#endif
+ else if (GET_CODE (SET_DEST (elt)) != REG
+ || REGNO (SET_DEST (elt)) < FIRST_PSEUDO_REGISTER
+ || counts[REGNO (SET_DEST (elt))] != 0
+ || side_effects_p (SET_SRC (elt)))
+ live_insn = 1;
+ }
+ else if (GET_CODE (elt) != CLOBBER && GET_CODE (elt) != USE)
+ live_insn = 1;
+ }
+ else
+ live_insn = 1;
+
+ /* If this is a dead insn, delete it and show registers in it aren't
+ being used. */
+
+ if (! live_insn)
+ {
+ count_reg_usage (insn, counts, NULL_RTX, -1);
+ delete_insn (insn);
+ }
+
+ if (find_reg_note (insn, REG_LIBCALL, NULL_RTX))
+ in_libcall = 0;
+ }
+}
diff --git a/gnu/usr.bin/cc/cc_int/dbxout.c b/gnu/usr.bin/cc/cc_int/dbxout.c
new file mode 100644
index 0000000..d34497d
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/dbxout.c
@@ -0,0 +1,2585 @@
+/* Output dbx-format symbol table information from GNU compiler.
+ Copyright (C) 1987, 1988, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* Output dbx-format symbol table data.
+ This consists of many symbol table entries, each of them
+ a .stabs assembler pseudo-op with four operands:
+ a "name" which is really a description of one symbol and its type,
+ a "code", which is a symbol defined in stab.h whose name starts with N_,
+ an unused operand always 0,
+ and a "value" which is an address or an offset.
+ The name is enclosed in doublequote characters.
+
+ Each function, variable, typedef, and structure tag
+ has a symbol table entry to define it.
+ The beginning and end of each level of name scoping within
+ a function are also marked by special symbol table entries.
+
+ The "name" consists of the symbol name, a colon, a kind-of-symbol letter,
+ and a data type number. The data type number may be followed by
+ "=" and a type definition; normally this will happen the first time
+ the type number is mentioned. The type definition may refer to
+ other types by number, and those type numbers may be followed
+ by "=" and nested definitions.
+
+ This can make the "name" quite long.
+ When a name is more than 80 characters, we split the .stabs pseudo-op
+ into two .stabs pseudo-ops, both sharing the same "code" and "value".
+ The first one is marked as continued with a double-backslash at the
+ end of its "name".
+
+ The kind-of-symbol letter distinguished function names from global
+ variables from file-scope variables from parameters from auto
+ variables in memory from typedef names from register variables.
+ See `dbxout_symbol'.
+
+ The "code" is mostly redundant with the kind-of-symbol letter
+ that goes in the "name", but not entirely: for symbols located
+ in static storage, the "code" says which segment the address is in,
+ which controls how it is relocated.
+
+ The "value" for a symbol in static storage
+ is the core address of the symbol (actually, the assembler
+ label for the symbol). For a symbol located in a stack slot
+ it is the stack offset; for one in a register, the register number.
+ For a typedef symbol, it is zero.
+
+ If DEBUG_SYMS_TEXT is defined, all debugging symbols must be
+ output while in the text section.
+
+ For more on data type definitions, see `dbxout_type'. */
+
+/* Include these first, because they may define MIN and MAX. */
+#include <stdio.h>
+#include <errno.h>
+
+#include "config.h"
+#include "tree.h"
+#include "rtl.h"
+#include "flags.h"
+#include "regs.h"
+#include "insn-config.h"
+#include "reload.h"
+#include "defaults.h"
+#include "output.h" /* ASM_OUTPUT_SOURCE_LINE may refer to sdb functions. */
+
+#ifndef errno
+extern int errno;
+#endif
+
+#ifdef XCOFF_DEBUGGING_INFO
+#include "xcoffout.h"
+#endif
+
+#ifndef ASM_STABS_OP
+#define ASM_STABS_OP ".stabs"
+#endif
+
+#ifndef ASM_STABN_OP
+#define ASM_STABN_OP ".stabn"
+#endif
+
+#ifndef DBX_TYPE_DECL_STABS_CODE
+#define DBX_TYPE_DECL_STABS_CODE N_LSYM
+#endif
+
+#ifndef DBX_STATIC_CONST_VAR_CODE
+#define DBX_STATIC_CONST_VAR_CODE N_FUN
+#endif
+
+#ifndef DBX_REGPARM_STABS_CODE
+#define DBX_REGPARM_STABS_CODE N_RSYM
+#endif
+
+#ifndef DBX_REGPARM_STABS_LETTER
+#define DBX_REGPARM_STABS_LETTER 'P'
+#endif
+
+#ifndef DBX_MEMPARM_STABS_LETTER
+#define DBX_MEMPARM_STABS_LETTER 'p'
+#endif
+
+#ifndef FILE_NAME_JOINER
+#define FILE_NAME_JOINER "/"
+#endif
+
+/* Nonzero means if the type has methods, only output debugging
+ information if methods are actually written to the asm file. */
+
+static int flag_minimal_debug = 1;
+
+/* Nonzero if we have actually used any of the GDB extensions
+ to the debugging format. The idea is that we use them for the
+ first time only if there's a strong reason, but once we have done that,
+ we use them whenever convenient. */
+
+static int have_used_extensions = 0;
+
+/* Number for the next N_SOL filename stabs label. The number 0 is reserved
+ for the N_SO filename stabs label. */
+
+static int source_label_number = 1;
+
+char *getpwd ();
+
+/* Typical USG systems don't have stab.h, and they also have
+ no use for DBX-format debugging info. */
+
+#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO)
+
+#ifdef DEBUG_SYMS_TEXT
+#define FORCE_TEXT text_section ();
+#else
+#define FORCE_TEXT
+#endif
+
+#if defined (USG) || defined (NO_STAB_H)
+#include "gstab.h" /* If doing DBX on sysV, use our own stab.h. */
+#else
+#include <stab.h> /* On BSD, use the system's stab.h. */
+
+/* This is a GNU extension we need to reference in this file. */
+#ifndef N_CATCH
+#define N_CATCH 0x54
+#endif
+#endif /* not USG */
+
+#ifdef __GNU_STAB__
+#define STAB_CODE_TYPE enum __stab_debug_code
+#else
+#define STAB_CODE_TYPE int
+#endif
+
+/* 1 if PARM is passed to this function in memory. */
+
+#define PARM_PASSED_IN_MEMORY(PARM) \
+ (GET_CODE (DECL_INCOMING_RTL (PARM)) == MEM)
+
+/* A C expression for the integer offset value of an automatic variable
+ (N_LSYM) having address X (an RTX). */
+#ifndef DEBUGGER_AUTO_OFFSET
+#define DEBUGGER_AUTO_OFFSET(X) \
+ (GET_CODE (X) == PLUS ? INTVAL (XEXP (X, 1)) : 0)
+#endif
+
+/* A C expression for the integer offset value of an argument (N_PSYM)
+ having address X (an RTX). The nominal offset is OFFSET. */
+#ifndef DEBUGGER_ARG_OFFSET
+#define DEBUGGER_ARG_OFFSET(OFFSET, X) (OFFSET)
+#endif
+
+/* Stream for writing to assembler file. */
+
+static FILE *asmfile;
+
+/* Last source file name mentioned in a NOTE insn. */
+
+static char *lastfile;
+
+/* Current working directory. */
+
+static char *cwd;
+
+enum typestatus {TYPE_UNSEEN, TYPE_XREF, TYPE_DEFINED};
+
+/* Vector recording the status of describing C data types.
+ When we first notice a data type (a tree node),
+ we assign it a number using next_type_number.
+ That is its index in this vector.
+ The vector element says whether we have yet output
+ the definition of the type. TYPE_XREF says we have
+ output it as a cross-reference only. */
+
+enum typestatus *typevec;
+
+/* Number of elements of space allocated in `typevec'. */
+
+static int typevec_len;
+
+/* In dbx output, each type gets a unique number.
+ This is the number for the next type output.
+ The number, once assigned, is in the TYPE_SYMTAB_ADDRESS field. */
+
+static int next_type_number;
+
+/* In dbx output, we must assign symbol-blocks id numbers
+ in the order in which their beginnings are encountered.
+ We output debugging info that refers to the beginning and
+ end of the ranges of code in each block
+ with assembler labels LBBn and LBEn, where n is the block number.
+ The labels are generated in final, which assigns numbers to the
+ blocks in the same way. */
+
+static int next_block_number;
+
+/* These variables are for dbxout_symbol to communicate to
+ dbxout_finish_symbol.
+ current_sym_code is the symbol-type-code, a symbol N_... define in stab.h.
+ current_sym_value and current_sym_addr are two ways to address the
+ value to store in the symtab entry.
+ current_sym_addr if nonzero represents the value as an rtx.
+ If that is zero, current_sym_value is used. This is used
+ when the value is an offset (such as for auto variables,
+ register variables and parms). */
+
+static STAB_CODE_TYPE current_sym_code;
+static int current_sym_value;
+static rtx current_sym_addr;
+
+/* Number of chars of symbol-description generated so far for the
+ current symbol. Used by CHARS and CONTIN. */
+
+static int current_sym_nchars;
+
+/* Report having output N chars of the current symbol-description. */
+
+#define CHARS(N) (current_sym_nchars += (N))
+
+/* Break the current symbol-description, generating a continuation,
+ if it has become long. */
+
+#ifndef DBX_CONTIN_LENGTH
+#define DBX_CONTIN_LENGTH 80
+#endif
+
+#if DBX_CONTIN_LENGTH > 0
+#define CONTIN \
+ do {if (current_sym_nchars > DBX_CONTIN_LENGTH) dbxout_continue ();} while (0)
+#else
+#define CONTIN
+#endif
+
+void dbxout_types ();
+void dbxout_args ();
+void dbxout_symbol ();
+static void dbxout_type_name ();
+static void dbxout_type ();
+static void dbxout_typedefs ();
+static void dbxout_symbol_name ();
+static void dbxout_symbol_location ();
+static void dbxout_prepare_symbol ();
+static void dbxout_finish_symbol ();
+static void dbxout_continue ();
+static void print_int_cst_octal ();
+static void print_octal ();
+
+#if 0 /* Not clear we will actually need this. */
+
+/* Return the absolutized filename for the given relative
+ filename. Note that if that filename is already absolute, it may
+ still be returned in a modified form because this routine also
+ eliminates redundant slashes and single dots and eliminates double
+ dots to get a shortest possible filename from the given input
+ filename. The absolutization of relative filenames is made by
+ assuming that the given filename is to be taken as relative to
+ the first argument (cwd) or to the current directory if cwd is
+ NULL. */
+
+static char *
+abspath (rel_filename)
+ char *rel_filename;
+{
+ /* Setup the current working directory as needed. */
+ char *abs_buffer
+ = (char *) alloca (strlen (cwd) + strlen (rel_filename) + 1);
+ char *endp = abs_buffer;
+ char *outp, *inp;
+ char *value;
+
+ /* Copy the filename (possibly preceded by the current working
+ directory name) into the absolutization buffer. */
+
+ {
+ char *src_p;
+
+ if (rel_filename[0] != '/')
+ {
+ src_p = cwd;
+ while (*endp++ = *src_p++)
+ continue;
+ *(endp-1) = '/'; /* overwrite null */
+ }
+ src_p = rel_filename;
+ while (*endp++ = *src_p++)
+ continue;
+ if (endp[-1] == '/')
+ *endp = '\0';
+
+ /* Now make a copy of abs_buffer into abs_buffer, shortening the
+ filename (by taking out slashes and dots) as we go. */
+
+ outp = inp = abs_buffer;
+ *outp++ = *inp++; /* copy first slash */
+ for (;;)
+ {
+ if (!inp[0])
+ break;
+ else if (inp[0] == '/' && outp[-1] == '/')
+ {
+ inp++;
+ continue;
+ }
+ else if (inp[0] == '.' && outp[-1] == '/')
+ {
+ if (!inp[1])
+ break;
+ else if (inp[1] == '/')
+ {
+ inp += 2;
+ continue;
+ }
+ else if ((inp[1] == '.') && (inp[2] == 0 || inp[2] == '/'))
+ {
+ inp += (inp[2] == '/') ? 3 : 2;
+ outp -= 2;
+ while (outp >= abs_buffer && *outp != '/')
+ outp--;
+ if (outp < abs_buffer)
+ {
+ /* Catch cases like /.. where we try to backup to a
+ point above the absolute root of the logical file
+ system. */
+
+ fprintf (stderr, "%s: invalid file name: %s\n",
+ pname, rel_filename);
+ exit (1);
+ }
+ *++outp = '\0';
+ continue;
+ }
+ }
+ *outp++ = *inp++;
+ }
+
+ /* On exit, make sure that there is a trailing null, and make sure that
+ the last character of the returned string is *not* a slash. */
+
+ *outp = '\0';
+ if (outp[-1] == '/')
+ *--outp = '\0';
+
+ /* Make a copy (in the heap) of the stuff left in the absolutization
+ buffer and return a pointer to the copy. */
+
+ value = (char *) oballoc (strlen (abs_buffer) + 1);
+ strcpy (value, abs_buffer);
+ return value;
+}
+#endif /* 0 */
+
+/* At the beginning of compilation, start writing the symbol table.
+ Initialize `typevec' and output the standard data types of C. */
+
+void
+dbxout_init (asm_file, input_file_name, syms)
+ FILE *asm_file;
+ char *input_file_name;
+ tree syms;
+{
+ char ltext_label_name[100];
+
+ asmfile = asm_file;
+
+ typevec_len = 100;
+ typevec = (enum typestatus *) xmalloc (typevec_len * sizeof typevec[0]);
+ bzero ((char *) typevec, typevec_len * sizeof typevec[0]);
+
+ /* Convert Ltext into the appropriate format for local labels in case
+ the system doesn't insert underscores in front of user generated
+ labels. */
+ ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0);
+
+ /* Put the current working directory in an N_SO symbol. */
+#ifndef DBX_WORKING_DIRECTORY /* Only some versions of DBX want this,
+ but GDB always does. */
+ if (use_gnu_debug_info_extensions)
+#endif
+ {
+ if (!cwd && (cwd = getpwd ()) && (!*cwd || cwd[strlen (cwd) - 1] != '/'))
+ {
+ char *wdslash = xmalloc (strlen (cwd) + sizeof (FILE_NAME_JOINER));
+ sprintf (wdslash, "%s%s", cwd, FILE_NAME_JOINER);
+ cwd = wdslash;
+ }
+ if (cwd)
+ {
+#ifdef DBX_OUTPUT_MAIN_SOURCE_DIRECTORY
+ DBX_OUTPUT_MAIN_SOURCE_DIRECTORY (asmfile, cwd);
+#else /* no DBX_OUTPUT_MAIN_SOURCE_DIRECTORY */
+ fprintf (asmfile, "%s ", ASM_STABS_OP);
+ output_quoted_string (asmfile, cwd);
+ fprintf (asmfile, ",%d,0,0,%s\n", N_SO, &ltext_label_name[1]);
+#endif /* no DBX_OUTPUT_MAIN_SOURCE_DIRECTORY */
+ }
+ }
+
+#ifdef DBX_OUTPUT_MAIN_SOURCE_FILENAME
+ /* This should NOT be DBX_OUTPUT_SOURCE_FILENAME. That
+ would give us an N_SOL, and we want an N_SO. */
+ DBX_OUTPUT_MAIN_SOURCE_FILENAME (asmfile, input_file_name);
+#else /* no DBX_OUTPUT_MAIN_SOURCE_FILENAME */
+ /* We include outputting `Ltext:' here,
+ because that gives you a way to override it. */
+ /* Used to put `Ltext:' before the reference, but that loses on sun 4. */
+ fprintf (asmfile, "%s ", ASM_STABS_OP);
+ output_quoted_string (asmfile, input_file_name);
+ fprintf (asmfile, ",%d,0,0,%s\n",
+ N_SO, &ltext_label_name[1]);
+ text_section ();
+ ASM_OUTPUT_INTERNAL_LABEL (asmfile, "Ltext", 0);
+#endif /* no DBX_OUTPUT_MAIN_SOURCE_FILENAME */
+
+ /* Possibly output something to inform GDB that this compilation was by
+ GCC. It's easier for GDB to parse it when after the N_SO's. This
+ is used in Solaris 2. */
+#ifdef ASM_IDENTIFY_GCC_AFTER_SOURCE
+ ASM_IDENTIFY_GCC_AFTER_SOURCE (asmfile);
+#endif
+
+ lastfile = input_file_name;
+
+ next_type_number = 1;
+ next_block_number = 2;
+
+ /* Make sure that types `int' and `char' have numbers 1 and 2.
+ Definitions of other integer types will refer to those numbers.
+ (Actually it should no longer matter what their numbers are.
+ Also, if any types with tags have been defined, dbxout_symbol
+ will output them first, so the numbers won't be 1 and 2. That
+ happens in C++. So it's a good thing it should no longer matter). */
+
+#ifdef DBX_OUTPUT_STANDARD_TYPES
+ DBX_OUTPUT_STANDARD_TYPES (syms);
+#else
+ dbxout_symbol (TYPE_NAME (integer_type_node), 0);
+ dbxout_symbol (TYPE_NAME (char_type_node), 0);
+#endif
+
+ /* Get all permanent types that have typedef names,
+ and output them all, except for those already output. */
+
+ dbxout_typedefs (syms);
+}
+
+/* Output any typedef names for types described by TYPE_DECLs in SYMS,
+ in the reverse order from that which is found in SYMS. */
+
+static void
+dbxout_typedefs (syms)
+ tree syms;
+{
+ if (syms)
+ {
+ dbxout_typedefs (TREE_CHAIN (syms));
+ if (TREE_CODE (syms) == TYPE_DECL)
+ {
+ tree type = TREE_TYPE (syms);
+ if (TYPE_NAME (type)
+ && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
+ && ! TREE_ASM_WRITTEN (TYPE_NAME (type)))
+ dbxout_symbol (TYPE_NAME (type), 0);
+ }
+ }
+}
+
+/* Output debugging info to FILE to switch to sourcefile FILENAME. */
+
+void
+dbxout_source_file (file, filename)
+ FILE *file;
+ char *filename;
+{
+ char ltext_label_name[100];
+
+ if (filename && (lastfile == 0 || strcmp (filename, lastfile)))
+ {
+#ifdef DBX_OUTPUT_SOURCE_FILENAME
+ DBX_OUTPUT_SOURCE_FILENAME (file, filename);
+#else
+ ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext",
+ source_label_number);
+ fprintf (file, "%s ", ASM_STABS_OP);
+ output_quoted_string (file, filename);
+ fprintf (file, ",%d,0,0,%s\n", N_SOL, &ltext_label_name[1]);
+ text_section ();
+ ASM_OUTPUT_INTERNAL_LABEL (asmfile, "Ltext", source_label_number);
+ source_label_number++;
+#endif
+ lastfile = filename;
+ }
+}
+
+/* Output a line number symbol entry into output stream FILE,
+ for source file FILENAME and line number LINENO. */
+
+void
+dbxout_source_line (file, filename, lineno)
+ FILE *file;
+ char *filename;
+ int lineno;
+{
+ dbxout_source_file (file, filename);
+
+#ifdef ASM_OUTPUT_SOURCE_LINE
+ ASM_OUTPUT_SOURCE_LINE (file, lineno);
+#else
+ fprintf (file, "\t%s %d,0,%d\n", ASM_STABD_OP, N_SLINE, lineno);
+#endif
+}
+
+/* At the end of compilation, finish writing the symbol table.
+ Unless you define DBX_OUTPUT_MAIN_SOURCE_FILE_END, the default is
+ to do nothing. */
+
+void
+dbxout_finish (file, filename)
+ FILE *file;
+ char *filename;
+{
+#ifdef DBX_OUTPUT_MAIN_SOURCE_FILE_END
+ DBX_OUTPUT_MAIN_SOURCE_FILE_END (file, filename);
+#endif /* DBX_OUTPUT_MAIN_SOURCE_FILE_END */
+}
+
+/* Continue a symbol-description that gets too big.
+ End one symbol table entry with a double-backslash
+ and start a new one, eventually producing something like
+ .stabs "start......\\",code,0,value
+ .stabs "...rest",code,0,value */
+
+static void
+dbxout_continue ()
+{
+#ifdef DBX_CONTIN_CHAR
+ fprintf (asmfile, "%c", DBX_CONTIN_CHAR);
+#else
+ fprintf (asmfile, "\\\\");
+#endif
+ dbxout_finish_symbol (NULL_TREE);
+ fprintf (asmfile, "%s \"", ASM_STABS_OP);
+ current_sym_nchars = 0;
+}
+
+/* Subroutine of `dbxout_type'. Output the type fields of TYPE.
+ This must be a separate function because anonymous unions require
+ recursive calls. */
+
+static void
+dbxout_type_fields (type)
+ tree type;
+{
+ tree tem;
+ /* Output the name, type, position (in bits), size (in bits) of each
+ field. */
+ for (tem = TYPE_FIELDS (type); tem; tem = TREE_CHAIN (tem))
+ {
+ /* Omit here local type decls until we know how to support them. */
+ if (TREE_CODE (tem) == TYPE_DECL)
+ continue;
+ /* Omit fields whose position or size are variable. */
+ else if (TREE_CODE (tem) == FIELD_DECL
+ && (TREE_CODE (DECL_FIELD_BITPOS (tem)) != INTEGER_CST
+ || TREE_CODE (DECL_SIZE (tem)) != INTEGER_CST))
+ continue;
+ /* Omit here the nameless fields that are used to skip bits. */
+ else if (TREE_CODE (tem) != CONST_DECL)
+ {
+ /* Continue the line if necessary,
+ but not before the first field. */
+ if (tem != TYPE_FIELDS (type))
+ CONTIN;
+
+ if (use_gnu_debug_info_extensions
+ && flag_minimal_debug
+ && TREE_CODE (tem) == FIELD_DECL
+ && DECL_VIRTUAL_P (tem)
+ && DECL_ASSEMBLER_NAME (tem))
+ {
+ have_used_extensions = 1;
+ CHARS (3 + IDENTIFIER_LENGTH (DECL_NAME (TYPE_NAME (DECL_FCONTEXT (tem)))));
+ fputs (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (tem)), asmfile);
+ dbxout_type (DECL_FCONTEXT (tem), 0, 0);
+ fprintf (asmfile, ":");
+ dbxout_type (TREE_TYPE (tem), 0, 0);
+ fprintf (asmfile, ",%d;",
+ TREE_INT_CST_LOW (DECL_FIELD_BITPOS (tem)));
+ continue;
+ }
+
+ if (DECL_NAME (tem))
+ {
+ fprintf (asmfile, "%s:", IDENTIFIER_POINTER (DECL_NAME (tem)));
+ CHARS (2 + IDENTIFIER_LENGTH (DECL_NAME (tem)));
+ }
+ else
+ {
+ fprintf (asmfile, ":");
+ CHARS (2);
+ }
+
+ if (use_gnu_debug_info_extensions
+ && (TREE_PRIVATE (tem) || TREE_PROTECTED (tem)
+ || TREE_CODE (tem) != FIELD_DECL))
+ {
+ have_used_extensions = 1;
+ putc ('/', asmfile);
+ putc ((TREE_PRIVATE (tem) ? '0'
+ : TREE_PROTECTED (tem) ? '1' : '2'),
+ asmfile);
+ CHARS (2);
+ }
+
+ dbxout_type ((TREE_CODE (tem) == FIELD_DECL
+ && DECL_BIT_FIELD_TYPE (tem))
+ ? DECL_BIT_FIELD_TYPE (tem)
+ : TREE_TYPE (tem), 0, 0);
+
+ if (TREE_CODE (tem) == VAR_DECL)
+ {
+ if (TREE_STATIC (tem) && use_gnu_debug_info_extensions)
+ {
+ char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (tem));
+ have_used_extensions = 1;
+ fprintf (asmfile, ":%s;", name);
+ CHARS (strlen (name));
+ }
+ else
+ {
+ /* If TEM is non-static, GDB won't understand it. */
+ fprintf (asmfile, ",0,0;");
+ }
+ }
+ else if (TREE_CODE (DECL_FIELD_BITPOS (tem)) == INTEGER_CST)
+ {
+ fprintf (asmfile, ",%d,%d;",
+ TREE_INT_CST_LOW (DECL_FIELD_BITPOS (tem)),
+ TREE_INT_CST_LOW (DECL_SIZE (tem)));
+ }
+ CHARS (23);
+ }
+ }
+}
+
+/* Subroutine of `dbxout_type_methods'. Output debug info about the
+ method described DECL. DEBUG_NAME is an encoding of the method's
+ type signature. ??? We may be able to do without DEBUG_NAME altogether
+ now. */
+
+static void
+dbxout_type_method_1 (decl, debug_name)
+ tree decl;
+ char *debug_name;
+{
+ tree firstarg = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (decl)));
+ char c1 = 'A', c2;
+
+ if (TREE_CODE (TREE_TYPE (decl)) == FUNCTION_TYPE)
+ c2 = '?';
+ else /* it's a METHOD_TYPE. */
+ {
+ /* A for normal functions.
+ B for `const' member functions.
+ C for `volatile' member functions.
+ D for `const volatile' member functions. */
+ if (TYPE_READONLY (TREE_TYPE (firstarg)))
+ c1 += 1;
+ if (TYPE_VOLATILE (TREE_TYPE (firstarg)))
+ c1 += 2;
+
+ if (DECL_VINDEX (decl))
+ c2 = '*';
+ else
+ c2 = '.';
+ }
+
+ fprintf (asmfile, ":%s;%c%c%c", debug_name,
+ TREE_PRIVATE (decl) ? '0' : TREE_PROTECTED (decl) ? '1' : '2', c1, c2);
+ CHARS (IDENTIFIER_LENGTH (DECL_ASSEMBLER_NAME (decl)) + 6
+ - (debug_name - IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))));
+ if (DECL_VINDEX (decl))
+ {
+ fprintf (asmfile, "%d;",
+ TREE_INT_CST_LOW (DECL_VINDEX (decl)));
+ dbxout_type (DECL_CONTEXT (decl), 0, 0);
+ fprintf (asmfile, ";");
+ CHARS (8);
+ }
+}
+
+/* Subroutine of `dbxout_type'. Output debug info about the methods defined
+ in TYPE. */
+
+static void
+dbxout_type_methods (type)
+ register tree type;
+{
+ /* C++: put out the method names and their parameter lists */
+ tree methods = TYPE_METHODS (type);
+ tree type_encoding;
+ register tree fndecl;
+ register tree last;
+ char formatted_type_identifier_length[16];
+ register int type_identifier_length;
+
+ if (methods == NULL_TREE)
+ return;
+
+ type_encoding = DECL_NAME (TYPE_NAME (type));
+
+#if 0
+ /* C++: Template classes break some assumptions made by this code about
+ the class names, constructor names, and encodings for assembler
+ label names. For now, disable output of dbx info for them. */
+ {
+ char *ptr = IDENTIFIER_POINTER (type_encoding);
+ /* This should use index. (mrs) */
+ while (*ptr && *ptr != '<') ptr++;
+ if (*ptr != 0)
+ {
+ static int warned;
+ if (!warned)
+ {
+ warned = 1;
+#ifdef HAVE_TEMPLATES
+ if (warn_template_debugging)
+ warning ("dbx info for template class methods not yet supported");
+#endif
+ }
+ return;
+ }
+ }
+#endif
+
+ type_identifier_length = IDENTIFIER_LENGTH (type_encoding);
+
+ sprintf(formatted_type_identifier_length, "%d", type_identifier_length);
+
+ if (TREE_CODE (methods) == FUNCTION_DECL)
+ fndecl = methods;
+ else if (TREE_VEC_ELT (methods, 0) != NULL_TREE)
+ fndecl = TREE_VEC_ELT (methods, 0);
+ else
+ fndecl = TREE_VEC_ELT (methods, 1);
+
+ while (fndecl)
+ {
+ tree name = DECL_NAME (fndecl);
+ int need_prefix = 1;
+
+ /* Group together all the methods for the same operation.
+ These differ in the types of the arguments. */
+ for (last = NULL_TREE;
+ fndecl && (last == NULL_TREE || DECL_NAME (fndecl) == DECL_NAME (last));
+ fndecl = TREE_CHAIN (fndecl))
+ /* Output the name of the field (after overloading), as
+ well as the name of the field before overloading, along
+ with its parameter list */
+ {
+ /* This is the "mangled" name of the method.
+ It encodes the argument types. */
+ char *debug_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (fndecl));
+ int destructor = 0;
+
+ CONTIN;
+
+ last = fndecl;
+
+ if (DECL_IGNORED_P (fndecl))
+ continue;
+
+ if (flag_minimal_debug)
+ {
+ /* Detect ordinary methods because their mangled names
+ start with the operation name. */
+ if (!strncmp (IDENTIFIER_POINTER (name), debug_name,
+ IDENTIFIER_LENGTH (name)))
+ {
+ debug_name += IDENTIFIER_LENGTH (name);
+ if (debug_name[0] == '_' && debug_name[1] == '_')
+ {
+ char *method_name = debug_name + 2;
+ char *length_ptr = formatted_type_identifier_length;
+ /* Get past const and volatile qualifiers. */
+ while (*method_name == 'C' || *method_name == 'V')
+ method_name++;
+ /* Skip digits for length of type_encoding. */
+ while (*method_name == *length_ptr && *length_ptr)
+ length_ptr++, method_name++;
+ if (! strncmp (method_name,
+ IDENTIFIER_POINTER (type_encoding),
+ type_identifier_length))
+ method_name += type_identifier_length;
+ debug_name = method_name;
+ }
+ }
+ /* Detect constructors by their style of name mangling. */
+ else if (debug_name[0] == '_' && debug_name[1] == '_')
+ {
+ char *ctor_name = debug_name + 2;
+ char *length_ptr = formatted_type_identifier_length;
+ while (*ctor_name == 'C' || *ctor_name == 'V')
+ ctor_name++;
+ /* Skip digits for length of type_encoding. */
+ while (*ctor_name == *length_ptr && *length_ptr)
+ length_ptr++, ctor_name++;
+ if (!strncmp (IDENTIFIER_POINTER (type_encoding), ctor_name,
+ type_identifier_length))
+ debug_name = ctor_name + type_identifier_length;
+ }
+ /* The other alternative is a destructor. */
+ else
+ destructor = 1;
+
+ /* Output the operation name just once, for the first method
+ that we output. */
+ if (need_prefix)
+ {
+ fprintf (asmfile, "%s::", IDENTIFIER_POINTER (name));
+ CHARS (IDENTIFIER_LENGTH (name) + 2);
+ need_prefix = 0;
+ }
+ }
+
+ dbxout_type (TREE_TYPE (fndecl), 0, destructor);
+
+ dbxout_type_method_1 (fndecl, debug_name);
+ }
+ if (!need_prefix)
+ {
+ putc (';', asmfile);
+ CHARS (1);
+ }
+ }
+}
+
+/* Emit a "range" type specification, which has the form:
+ "r<index type>;<lower bound>;<upper bound>;".
+ TYPE is an INTEGER_TYPE. */
+
+static void
+dbxout_range_type (type)
+ tree type;
+{
+ fprintf (asmfile, "r");
+ if (TREE_TYPE (type))
+ dbxout_type (TREE_TYPE (type), 0, 0);
+ else if (TREE_CODE (type) != INTEGER_TYPE)
+ dbxout_type (type, 0, 0); /* E.g. Pascal's ARRAY [BOOLEAN] of INTEGER */
+ else
+ {
+ /* This used to say `r1' and we used to take care
+ to make sure that `int' was type number 1. */
+ fprintf (asmfile, "%d", TYPE_SYMTAB_ADDRESS (integer_type_node));
+ }
+ if (TREE_CODE (TYPE_MIN_VALUE (type)) == INTEGER_CST)
+ fprintf (asmfile, ";%d",
+ TREE_INT_CST_LOW (TYPE_MIN_VALUE (type)));
+ else
+ fprintf (asmfile, ";0");
+ if (TREE_CODE (TYPE_MAX_VALUE (type)) == INTEGER_CST)
+ fprintf (asmfile, ";%d;",
+ TREE_INT_CST_LOW (TYPE_MAX_VALUE (type)));
+ else
+ fprintf (asmfile, ";-1;");
+}
+
+/* Output a reference to a type. If the type has not yet been
+ described in the dbx output, output its definition now.
+ For a type already defined, just refer to its definition
+ using the type number.
+
+ If FULL is nonzero, and the type has been described only with
+ a forward-reference, output the definition now.
+ If FULL is zero in this case, just refer to the forward-reference
+ using the number previously allocated.
+
+ If SHOW_ARG_TYPES is nonzero, we output a description of the argument
+ types for a METHOD_TYPE. */
+
+static void
+dbxout_type (type, full, show_arg_types)
+ tree type;
+ int full;
+ int show_arg_types;
+{
+ register tree tem;
+ static int anonymous_type_number = 0;
+
+ /* If there was an input error and we don't really have a type,
+ avoid crashing and write something that is at least valid
+ by assuming `int'. */
+ if (type == error_mark_node)
+ type = integer_type_node;
+ else
+ {
+ type = TYPE_MAIN_VARIANT (type);
+ if (TYPE_NAME (type)
+ && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
+ && TYPE_DECL_SUPPRESS_DEBUG (TYPE_NAME (type)))
+ full = 0;
+ }
+
+ if (TYPE_SYMTAB_ADDRESS (type) == 0)
+ {
+ /* Type has no dbx number assigned. Assign next available number. */
+ TYPE_SYMTAB_ADDRESS (type) = next_type_number++;
+
+ /* Make sure type vector is long enough to record about this type. */
+
+ if (next_type_number == typevec_len)
+ {
+ typevec =
+ (enum typestatus *) xrealloc (typevec,
+ typevec_len * 2 * sizeof typevec[0]);
+ bzero ((char *) (typevec + typevec_len),
+ typevec_len * sizeof typevec[0]);
+ typevec_len *= 2;
+ }
+ }
+
+ /* Output the number of this type, to refer to it. */
+ fprintf (asmfile, "%d", TYPE_SYMTAB_ADDRESS (type));
+ CHARS (3);
+
+#ifdef DBX_TYPE_DEFINED
+ if (DBX_TYPE_DEFINED (type))
+ return;
+#endif
+
+ /* If this type's definition has been output or is now being output,
+ that is all. */
+
+ switch (typevec[TYPE_SYMTAB_ADDRESS (type)])
+ {
+ case TYPE_UNSEEN:
+ break;
+ case TYPE_XREF:
+ /* If we have already had a cross reference,
+ and either that's all we want or that's the best we could do,
+ don't repeat the cross reference.
+ Sun dbx crashes if we do. */
+ if (! full || TYPE_SIZE (type) == 0
+ /* No way in DBX fmt to describe a variable size. */
+ || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+ return;
+ break;
+ case TYPE_DEFINED:
+ return;
+ }
+
+#ifdef DBX_NO_XREFS
+ /* For systems where dbx output does not allow the `=xsNAME:' syntax,
+ leave the type-number completely undefined rather than output
+ a cross-reference. If we have already used GNU debug info extensions,
+ then it is OK to output a cross reference. This is necessary to get
+ proper C++ debug output. */
+ if ((TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE
+ || TREE_CODE (type) == QUAL_UNION_TYPE
+ || TREE_CODE (type) == ENUMERAL_TYPE)
+ && ! use_gnu_debug_info_extensions)
+ /* We must use the same test here as we use twice below when deciding
+ whether to emit a cross-reference. */
+ if ((TYPE_NAME (type) != 0
+ && ! (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
+ && DECL_IGNORED_P (TYPE_NAME (type)))
+ && !full)
+ || TYPE_SIZE (type) == 0
+ /* No way in DBX fmt to describe a variable size. */
+ || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+ {
+ typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_XREF;
+ return;
+ }
+#endif
+
+ /* Output a definition now. */
+
+ fprintf (asmfile, "=");
+ CHARS (1);
+
+ /* Mark it as defined, so that if it is self-referent
+ we will not get into an infinite recursion of definitions. */
+
+ typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_DEFINED;
+
+ switch (TREE_CODE (type))
+ {
+ case VOID_TYPE:
+ case LANG_TYPE:
+ /* For a void type, just define it as itself; ie, "5=5".
+ This makes us consider it defined
+ without saying what it is. The debugger will make it
+ a void type when the reference is seen, and nothing will
+ ever override that default. */
+ fprintf (asmfile, "%d", TYPE_SYMTAB_ADDRESS (type));
+ CHARS (3);
+ break;
+
+ case INTEGER_TYPE:
+ if (type == char_type_node && ! TREE_UNSIGNED (type))
+ /* Output the type `char' as a subrange of itself!
+ I don't understand this definition, just copied it
+ from the output of pcc.
+ This used to use `r2' explicitly and we used to
+ take care to make sure that `char' was type number 2. */
+ fprintf (asmfile, "r%d;0;127;", TYPE_SYMTAB_ADDRESS (type));
+ else if (use_gnu_debug_info_extensions
+ && (TYPE_PRECISION (type) > TYPE_PRECISION (integer_type_node)
+ || TYPE_PRECISION (type) > HOST_BITS_PER_WIDE_INT))
+ {
+ /* This used to say `r1' and we used to take care
+ to make sure that `int' was type number 1. */
+ fprintf (asmfile, "r%d;", TYPE_SYMTAB_ADDRESS (integer_type_node));
+ print_int_cst_octal (TYPE_MIN_VALUE (type));
+ fprintf (asmfile, ";");
+ print_int_cst_octal (TYPE_MAX_VALUE (type));
+ fprintf (asmfile, ";");
+ }
+ else /* Output other integer types as subranges of `int'. */
+ dbxout_range_type (type);
+ CHARS (25);
+ break;
+
+ case REAL_TYPE:
+ /* This used to say `r1' and we used to take care
+ to make sure that `int' was type number 1. */
+ fprintf (asmfile, "r%d;%d;0;", TYPE_SYMTAB_ADDRESS (integer_type_node),
+ int_size_in_bytes (type));
+ CHARS (16);
+ break;
+
+ case CHAR_TYPE:
+ if (use_gnu_debug_info_extensions)
+ fprintf (asmfile, "@s%d;-20;",
+ BITS_PER_UNIT * int_size_in_bytes (type));
+ else
+ /* Output the type `char' as a subrange of itself.
+ That is what pcc seems to do. */
+ fprintf (asmfile, "r%d;0;%d;", TYPE_SYMTAB_ADDRESS (char_type_node),
+ TREE_UNSIGNED (type) ? 255 : 127);
+ CHARS (9);
+ break;
+
+ case BOOLEAN_TYPE:
+ if (use_gnu_debug_info_extensions)
+ fprintf (asmfile, "@s%d;-16;",
+ BITS_PER_UNIT * int_size_in_bytes (type));
+ else /* Define as enumeral type (False, True) */
+ fprintf (asmfile, "eFalse:0,True:1,;");
+ CHARS (17);
+ break;
+
+ case FILE_TYPE:
+ putc ('d', asmfile);
+ CHARS (1);
+ dbxout_type (TREE_TYPE (type), 0, 0);
+ break;
+
+ case COMPLEX_TYPE:
+ /* Differs from the REAL_TYPE by its new data type number */
+
+ if (TREE_CODE (TREE_TYPE (type)) == REAL_TYPE)
+ {
+ fprintf (asmfile, "r%d;%d;0;",
+ TYPE_SYMTAB_ADDRESS (type),
+ int_size_in_bytes (TREE_TYPE (type)));
+ CHARS (15); /* The number is probably incorrect here. */
+ }
+ else
+ {
+ /* Output a complex integer type as a structure,
+ pending some other way to do it. */
+ fprintf (asmfile, "s%d", int_size_in_bytes (type));
+
+ fprintf (asmfile, "real:");
+ CHARS (10);
+ dbxout_type (TREE_TYPE (type), 0, 0);
+ fprintf (asmfile, ",%d,%d;",
+ 0, TYPE_PRECISION (TREE_TYPE (type)));
+ CHARS (8);
+ fprintf (asmfile, "imag:");
+ CHARS (5);
+ dbxout_type (TREE_TYPE (type), 0, 0);
+ fprintf (asmfile, ",%d,%d;;",
+ TYPE_PRECISION (TREE_TYPE (type)),
+ TYPE_PRECISION (TREE_TYPE (type)));
+ CHARS (9);
+ }
+ break;
+
+ case SET_TYPE:
+ if (use_gnu_debug_info_extensions)
+ {
+ have_used_extensions = 1;
+ fprintf (asmfile, "@s%d;",
+ BITS_PER_UNIT * int_size_in_bytes (type));
+ /* Check if a bitstring type, which in Chill is
+ different from a [power]set. */
+ if (TYPE_STRING_FLAG (type))
+ fprintf (asmfile, "@S;");
+ }
+ putc ('S', asmfile);
+ CHARS (1);
+ dbxout_type (TYPE_DOMAIN (type), 0, 0);
+ break;
+
+ case ARRAY_TYPE:
+ /* Output "a" followed by a range type definition
+ for the index type of the array
+ followed by a reference to the target-type.
+ ar1;0;N;M for a C array of type M and size N+1. */
+ /* Check if a character string type, which in Chill is
+ different from an array of characters. */
+ if (TYPE_STRING_FLAG (type) && use_gnu_debug_info_extensions)
+ {
+ have_used_extensions = 1;
+ fprintf (asmfile, "@S;");
+ }
+ tem = TYPE_DOMAIN (type);
+ if (tem == NULL)
+ fprintf (asmfile, "ar%d;0;-1;",
+ TYPE_SYMTAB_ADDRESS (integer_type_node));
+ else
+ {
+ fprintf (asmfile, "a");
+ dbxout_range_type (tem);
+ }
+ CHARS (17);
+ dbxout_type (TREE_TYPE (type), 0, 0);
+ break;
+
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ case QUAL_UNION_TYPE:
+ {
+ int i, n_baseclasses = 0;
+
+ if (TYPE_BINFO (type) != 0 && TYPE_BINFO_BASETYPES (type) != 0)
+ n_baseclasses = TREE_VEC_LENGTH (TYPE_BINFO_BASETYPES (type));
+
+ /* Output a structure type. We must use the same test here as we
+ use in the DBX_NO_XREFS case above. */
+ if ((TYPE_NAME (type) != 0
+ && ! (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
+ && DECL_IGNORED_P (TYPE_NAME (type)))
+ && !full)
+ || TYPE_SIZE (type) == 0
+ /* No way in DBX fmt to describe a variable size. */
+ || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+ {
+ /* If the type is just a cross reference, output one
+ and mark the type as partially described.
+ If it later becomes defined, we will output
+ its real definition.
+ If the type has a name, don't nest its definition within
+ another type's definition; instead, output an xref
+ and let the definition come when the name is defined. */
+ fprintf (asmfile, (TREE_CODE (type) == RECORD_TYPE) ? "xs" : "xu");
+ CHARS (3);
+#if 0 /* This assertion is legitimately false in C++. */
+ /* We shouldn't be outputting a reference to a type before its
+ definition unless the type has a tag name.
+ A typedef name without a tag name should be impossible. */
+ if (TREE_CODE (TYPE_NAME (type)) != IDENTIFIER_NODE)
+ abort ();
+#endif
+ if (TYPE_NAME (type) != 0)
+ dbxout_type_name (type);
+ else
+ fprintf (asmfile, "$$%d", anonymous_type_number++);
+ fprintf (asmfile, ":");
+ typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_XREF;
+ break;
+ }
+
+ /* Identify record or union, and print its size. */
+ fprintf (asmfile, (TREE_CODE (type) == RECORD_TYPE) ? "s%d" : "u%d",
+ int_size_in_bytes (type));
+
+ if (use_gnu_debug_info_extensions)
+ {
+ if (n_baseclasses)
+ {
+ have_used_extensions = 1;
+ fprintf (asmfile, "!%d,", n_baseclasses);
+ CHARS (8);
+ }
+ }
+ for (i = 0; i < n_baseclasses; i++)
+ {
+ tree child = TREE_VEC_ELT (BINFO_BASETYPES (TYPE_BINFO (type)), i);
+ if (use_gnu_debug_info_extensions)
+ {
+ have_used_extensions = 1;
+ putc (TREE_VIA_VIRTUAL (child) ? '1'
+ : '0',
+ asmfile);
+ putc (TREE_VIA_PUBLIC (child) ? '2'
+ : '0',
+ asmfile);
+ fprintf (asmfile, "%d,",
+ TREE_INT_CST_LOW (BINFO_OFFSET (child)) * BITS_PER_UNIT);
+ CHARS (15);
+ dbxout_type (BINFO_TYPE (child), 0, 0);
+ putc (';', asmfile);
+ }
+ else
+ {
+ /* Print out the base class information with fields
+ which have the same names at the types they hold. */
+ dbxout_type_name (BINFO_TYPE (child));
+ putc (':', asmfile);
+ dbxout_type (BINFO_TYPE (child), full, 0);
+ fprintf (asmfile, ",%d,%d;",
+ TREE_INT_CST_LOW (BINFO_OFFSET (child)) * BITS_PER_UNIT,
+ TREE_INT_CST_LOW (DECL_SIZE (TYPE_NAME (BINFO_TYPE (child)))) * BITS_PER_UNIT);
+ CHARS (20);
+ }
+ }
+ }
+
+ CHARS (11);
+
+ /* Write out the field declarations. */
+ dbxout_type_fields (type);
+ if (use_gnu_debug_info_extensions && TYPE_METHODS (type) != NULL_TREE)
+ {
+ have_used_extensions = 1;
+ dbxout_type_methods (type);
+ }
+ putc (';', asmfile);
+
+ if (use_gnu_debug_info_extensions && TREE_CODE (type) == RECORD_TYPE
+ /* Avoid the ~ if we don't really need it--it confuses dbx. */
+ && TYPE_VFIELD (type))
+ {
+ have_used_extensions = 1;
+
+ /* Tell GDB+ that it may keep reading. */
+ putc ('~', asmfile);
+
+ /* We need to write out info about what field this class
+ uses as its "main" vtable pointer field, because if this
+ field is inherited from a base class, GDB cannot necessarily
+ figure out which field it's using in time. */
+ if (TYPE_VFIELD (type))
+ {
+ putc ('%', asmfile);
+ dbxout_type (DECL_FCONTEXT (TYPE_VFIELD (type)), 0, 0);
+ }
+ putc (';', asmfile);
+ CHARS (3);
+ }
+ break;
+
+ case ENUMERAL_TYPE:
+ /* We must use the same test here as we use in the DBX_NO_XREFS case
+ above. We simplify it a bit since an enum will never have a variable
+ size. */
+ if ((TYPE_NAME (type) != 0
+ && ! (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
+ && DECL_IGNORED_P (TYPE_NAME (type)))
+ && !full)
+ || TYPE_SIZE (type) == 0)
+ {
+ fprintf (asmfile, "xe");
+ CHARS (3);
+ dbxout_type_name (type);
+ typevec[TYPE_SYMTAB_ADDRESS (type)] = TYPE_XREF;
+ fprintf (asmfile, ":");
+ return;
+ }
+#ifdef DBX_OUTPUT_ENUM
+ DBX_OUTPUT_ENUM (asmfile, type);
+#else
+ if (use_gnu_debug_info_extensions
+ && TYPE_PRECISION (type) != TYPE_PRECISION (integer_type_node))
+ fprintf (asmfile, "@s%d;", TYPE_PRECISION (type));
+ putc ('e', asmfile);
+ CHARS (1);
+ for (tem = TYPE_VALUES (type); tem; tem = TREE_CHAIN (tem))
+ {
+ fprintf (asmfile, "%s:", IDENTIFIER_POINTER (TREE_PURPOSE (tem)));
+ if (TREE_INT_CST_HIGH (TREE_VALUE (tem)) == 0)
+ fprintf (asmfile, "%lu",
+ (unsigned long) TREE_INT_CST_LOW (TREE_VALUE (tem)));
+ else if (TREE_INT_CST_HIGH (TREE_VALUE (tem)) == -1
+ && TREE_INT_CST_LOW (TREE_VALUE (tem)) < 0)
+ fprintf (asmfile, "%ld",
+ (long) TREE_INT_CST_LOW (TREE_VALUE (tem)));
+ else
+ print_int_cst_octal (TREE_VALUE (tem));
+ fprintf (asmfile, ",");
+ CHARS (20 + IDENTIFIER_LENGTH (TREE_PURPOSE (tem)));
+ if (TREE_CHAIN (tem) != 0)
+ CONTIN;
+ }
+ putc (';', asmfile);
+ CHARS (1);
+#endif
+ break;
+
+ case POINTER_TYPE:
+ putc ('*', asmfile);
+ CHARS (1);
+ dbxout_type (TREE_TYPE (type), 0, 0);
+ break;
+
+ case METHOD_TYPE:
+ if (use_gnu_debug_info_extensions)
+ {
+ have_used_extensions = 1;
+ putc ('#', asmfile);
+ CHARS (1);
+ if (flag_minimal_debug && !show_arg_types)
+ {
+ /* Normally, just output the return type.
+ The argument types are encoded in the method name. */
+ putc ('#', asmfile);
+ dbxout_type (TREE_TYPE (type), 0, 0);
+ putc (';', asmfile);
+ CHARS (1);
+ }
+ else
+ {
+ /* When outputting destructors, we need to write
+ the argument types out longhand. */
+ dbxout_type (TYPE_METHOD_BASETYPE (type), 0, 0);
+ putc (',', asmfile);
+ CHARS (1);
+ dbxout_type (TREE_TYPE (type), 0, 0);
+ dbxout_args (TYPE_ARG_TYPES (type));
+ putc (';', asmfile);
+ CHARS (1);
+ }
+ }
+ else
+ {
+ /* Treat it as a function type. */
+ dbxout_type (TREE_TYPE (type), 0, 0);
+ }
+ break;
+
+ case OFFSET_TYPE:
+ if (use_gnu_debug_info_extensions)
+ {
+ have_used_extensions = 1;
+ putc ('@', asmfile);
+ CHARS (1);
+ dbxout_type (TYPE_OFFSET_BASETYPE (type), 0, 0);
+ putc (',', asmfile);
+ CHARS (1);
+ dbxout_type (TREE_TYPE (type), 0, 0);
+ }
+ else
+ {
+ /* Should print as an int, because it is really
+ just an offset. */
+ dbxout_type (integer_type_node, 0, 0);
+ }
+ break;
+
+ case REFERENCE_TYPE:
+ if (use_gnu_debug_info_extensions)
+ have_used_extensions = 1;
+ putc (use_gnu_debug_info_extensions ? '&' : '*', asmfile);
+ CHARS (1);
+ dbxout_type (TREE_TYPE (type), 0, 0);
+ break;
+
+ case FUNCTION_TYPE:
+ putc ('f', asmfile);
+ CHARS (1);
+ dbxout_type (TREE_TYPE (type), 0, 0);
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+/* Print the value of integer constant C, in octal,
+ handling double precision. */
+
+static void
+print_int_cst_octal (c)
+ tree c;
+{
+ unsigned HOST_WIDE_INT high = TREE_INT_CST_HIGH (c);
+ unsigned HOST_WIDE_INT low = TREE_INT_CST_LOW (c);
+ int excess = (3 - (HOST_BITS_PER_WIDE_INT % 3));
+ int width = TYPE_PRECISION (TREE_TYPE (c));
+
+ /* GDB wants constants with no extra leading "1" bits, so
+ we need to remove any sign-extension that might be
+ present. */
+ if (width == HOST_BITS_PER_WIDE_INT * 2)
+ ;
+ else if (width > HOST_BITS_PER_WIDE_INT)
+ high &= (((HOST_WIDE_INT) 1 << (width - HOST_BITS_PER_WIDE_INT)) - 1);
+ else if (width == HOST_BITS_PER_WIDE_INT)
+ high = 0;
+ else
+ high = 0, low &= (((HOST_WIDE_INT) 1 << width) - 1);
+
+ fprintf (asmfile, "0");
+
+ if (excess == 3)
+ {
+ print_octal (high, HOST_BITS_PER_WIDE_INT / 3);
+ print_octal (low, HOST_BITS_PER_WIDE_INT / 3);
+ }
+ else
+ {
+ unsigned HOST_WIDE_INT beg = high >> excess;
+ unsigned HOST_WIDE_INT middle
+ = ((high & (((HOST_WIDE_INT) 1 << excess) - 1)) << (3 - excess)
+ | (low >> (HOST_BITS_PER_WIDE_INT / 3 * 3)));
+ unsigned HOST_WIDE_INT end
+ = low & (((unsigned HOST_WIDE_INT) 1
+ << (HOST_BITS_PER_WIDE_INT / 3 * 3))
+ - 1);
+
+ fprintf (asmfile, "%o%01o", beg, middle);
+ print_octal (end, HOST_BITS_PER_WIDE_INT / 3);
+ }
+}
+
+static void
+print_octal (value, digits)
+ unsigned HOST_WIDE_INT value;
+ int digits;
+{
+ int i;
+
+ for (i = digits - 1; i >= 0; i--)
+ fprintf (asmfile, "%01o", ((value >> (3 * i)) & 7));
+}
+
+/* Output the name of type TYPE, with no punctuation.
+ Such names can be set up either by typedef declarations
+ or by struct, enum and union tags. */
+
+static void
+dbxout_type_name (type)
+ register tree type;
+{
+ tree t;
+ if (TYPE_NAME (type) == 0)
+ abort ();
+ if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
+ {
+ t = TYPE_NAME (type);
+ }
+ else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL)
+ {
+ t = DECL_NAME (TYPE_NAME (type));
+ }
+ else
+ abort ();
+
+ fprintf (asmfile, "%s", IDENTIFIER_POINTER (t));
+ CHARS (IDENTIFIER_LENGTH (t));
+}
+
+/* Output a .stabs for the symbol defined by DECL,
+ which must be a ..._DECL node in the normal namespace.
+ It may be a CONST_DECL, a FUNCTION_DECL, a PARM_DECL or a VAR_DECL.
+ LOCAL is nonzero if the scope is less than the entire file. */
+
+void
+dbxout_symbol (decl, local)
+ tree decl;
+ int local;
+{
+ tree type = TREE_TYPE (decl);
+ tree context = NULL_TREE;
+
+ /* Cast avoids warning in old compilers. */
+ current_sym_code = (STAB_CODE_TYPE) 0;
+ current_sym_value = 0;
+ current_sym_addr = 0;
+
+ /* Ignore nameless syms, but don't ignore type tags. */
+
+ if ((DECL_NAME (decl) == 0 && TREE_CODE (decl) != TYPE_DECL)
+ || DECL_IGNORED_P (decl))
+ return;
+
+ dbxout_prepare_symbol (decl);
+
+ /* The output will always start with the symbol name,
+ so always count that in the length-output-so-far. */
+
+ if (DECL_NAME (decl) != 0)
+ current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (decl));
+
+ switch (TREE_CODE (decl))
+ {
+ case CONST_DECL:
+ /* Enum values are defined by defining the enum type. */
+ break;
+
+ case FUNCTION_DECL:
+ if (DECL_RTL (decl) == 0)
+ return;
+ if (DECL_EXTERNAL (decl))
+ break;
+ /* Don't mention a nested function under its parent. */
+ context = decl_function_context (decl);
+ if (context == current_function_decl)
+ break;
+ if (GET_CODE (DECL_RTL (decl)) != MEM
+ || GET_CODE (XEXP (DECL_RTL (decl), 0)) != SYMBOL_REF)
+ break;
+ FORCE_TEXT;
+
+ fprintf (asmfile, "%s \"%s:%c", ASM_STABS_OP,
+ IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)),
+ TREE_PUBLIC (decl) ? 'F' : 'f');
+
+ current_sym_code = N_FUN;
+ current_sym_addr = XEXP (DECL_RTL (decl), 0);
+
+ if (TREE_TYPE (type))
+ dbxout_type (TREE_TYPE (type), 0, 0);
+ else
+ dbxout_type (void_type_node, 0, 0);
+
+ /* For a nested function, when that function is compiled,
+ mention the containing function name
+ as well as (since dbx wants it) our own assembler-name. */
+ if (context != 0)
+ fprintf (asmfile, ",%s,%s",
+ IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)),
+ IDENTIFIER_POINTER (DECL_NAME (context)));
+
+ dbxout_finish_symbol (decl);
+ break;
+
+ case TYPE_DECL:
+#if 0
+ /* This seems all wrong. Outputting most kinds of types gives no name
+ at all. A true definition gives no name; a cross-ref for a
+ structure can give the tag name, but not a type name.
+ It seems that no typedef name is defined by outputting a type. */
+
+ /* If this typedef name was defined by outputting the type,
+ don't duplicate it. */
+ if (typevec[TYPE_SYMTAB_ADDRESS (type)] == TYPE_DEFINED
+ && TYPE_NAME (TREE_TYPE (decl)) == decl)
+ return;
+#endif
+ /* Don't output the same typedef twice.
+ And don't output what language-specific stuff doesn't want output. */
+ if (TREE_ASM_WRITTEN (decl) || TYPE_DECL_SUPPRESS_DEBUG (decl))
+ return;
+
+ FORCE_TEXT;
+
+ {
+ int tag_needed = 1;
+ int did_output = 0;
+
+ if (DECL_NAME (decl))
+ {
+ /* Nonzero means we must output a tag as well as a typedef. */
+ tag_needed = 0;
+
+ /* Handle the case of a C++ structure or union
+ where the TYPE_NAME is a TYPE_DECL
+ which gives both a typedef name and a tag. */
+ /* dbx requires the tag first and the typedef second. */
+ if ((TREE_CODE (type) == RECORD_TYPE
+ || TREE_CODE (type) == UNION_TYPE
+ || TREE_CODE (type) == QUAL_UNION_TYPE)
+ && TYPE_NAME (type) == decl
+ && !(use_gnu_debug_info_extensions && have_used_extensions)
+ && !TREE_ASM_WRITTEN (TYPE_NAME (type))
+ /* Distinguish the implicit typedefs of C++
+ from explicit ones that might be found in C. */
+ && (!strcmp (lang_identify (), "cplusplus")
+ /* The following line maybe unnecessary;
+ in 2.6, try removing it. */
+ || DECL_SOURCE_LINE (decl) == 0))
+ {
+ tree name = TYPE_NAME (type);
+ if (TREE_CODE (name) == TYPE_DECL)
+ name = DECL_NAME (name);
+
+ current_sym_code = DBX_TYPE_DECL_STABS_CODE;
+ current_sym_value = 0;
+ current_sym_addr = 0;
+ current_sym_nchars = 2 + IDENTIFIER_LENGTH (name);
+
+ fprintf (asmfile, "%s \"%s:T", ASM_STABS_OP,
+ IDENTIFIER_POINTER (name));
+ dbxout_type (type, 1, 0);
+ dbxout_finish_symbol (NULL_TREE);
+ }
+
+ /* Output typedef name. */
+ fprintf (asmfile, "%s \"%s:", ASM_STABS_OP,
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+
+ /* Short cut way to output a tag also. */
+ if ((TREE_CODE (type) == RECORD_TYPE
+ || TREE_CODE (type) == UNION_TYPE
+ || TREE_CODE (type) == QUAL_UNION_TYPE)
+ && TYPE_NAME (type) == decl)
+ {
+ if (use_gnu_debug_info_extensions && have_used_extensions)
+ {
+ putc ('T', asmfile);
+ TREE_ASM_WRITTEN (TYPE_NAME (type)) = 1;
+ }
+#if 0 /* Now we generate the tag for this case up above. */
+ else
+ tag_needed = 1;
+#endif
+ }
+
+ putc ('t', asmfile);
+ current_sym_code = DBX_TYPE_DECL_STABS_CODE;
+
+ dbxout_type (type, 1, 0);
+ dbxout_finish_symbol (decl);
+ did_output = 1;
+ }
+
+ /* Don't output a tag if this is an incomplete type (TYPE_SIZE is
+ zero). This prevents the sun4 Sun OS 4.x dbx from crashing. */
+
+ if (tag_needed && TYPE_NAME (type) != 0 && TYPE_SIZE (type) != 0
+ && !TREE_ASM_WRITTEN (TYPE_NAME (type)))
+ {
+ /* For a TYPE_DECL with no name, but the type has a name,
+ output a tag.
+ This is what represents `struct foo' with no typedef. */
+ /* In C++, the name of a type is the corresponding typedef.
+ In C, it is an IDENTIFIER_NODE. */
+ tree name = TYPE_NAME (type);
+ if (TREE_CODE (name) == TYPE_DECL)
+ name = DECL_NAME (name);
+
+ current_sym_code = DBX_TYPE_DECL_STABS_CODE;
+ current_sym_value = 0;
+ current_sym_addr = 0;
+ current_sym_nchars = 2 + IDENTIFIER_LENGTH (name);
+
+ fprintf (asmfile, "%s \"%s:T", ASM_STABS_OP,
+ IDENTIFIER_POINTER (name));
+ dbxout_type (type, 1, 0);
+ dbxout_finish_symbol (NULL_TREE);
+ did_output = 1;
+ }
+
+ /* If an enum type has no name, it cannot be referred to,
+ but we must output it anyway, since the enumeration constants
+ can be referred to. */
+ if (!did_output && TREE_CODE (type) == ENUMERAL_TYPE)
+ {
+ current_sym_code = DBX_TYPE_DECL_STABS_CODE;
+ current_sym_value = 0;
+ current_sym_addr = 0;
+ current_sym_nchars = 2;
+
+ /* Some debuggers fail when given NULL names, so give this a
+ harmless name of ` '. */
+ fprintf (asmfile, "%s \" :T", ASM_STABS_OP);
+ dbxout_type (type, 1, 0);
+ dbxout_finish_symbol (NULL_TREE);
+ }
+
+ /* Prevent duplicate output of a typedef. */
+ TREE_ASM_WRITTEN (decl) = 1;
+ break;
+ }
+
+ case PARM_DECL:
+ /* Parm decls go in their own separate chains
+ and are output by dbxout_reg_parms and dbxout_parms. */
+ abort ();
+
+ case RESULT_DECL:
+ /* Named return value, treat like a VAR_DECL. */
+ case VAR_DECL:
+ if (DECL_RTL (decl) == 0)
+ return;
+ /* Don't mention a variable that is external.
+ Let the file that defines it describe it. */
+ if (DECL_EXTERNAL (decl))
+ break;
+
+ /* If the variable is really a constant
+ and not written in memory, inform the debugger. */
+ if (TREE_STATIC (decl) && TREE_READONLY (decl)
+ && DECL_INITIAL (decl) != 0
+ && ! TREE_ASM_WRITTEN (decl)
+ && (DECL_FIELD_CONTEXT (decl) == NULL_TREE
+ || TREE_CODE (DECL_FIELD_CONTEXT (decl)) == BLOCK))
+ {
+ if (TREE_PUBLIC (decl) == 0)
+ {
+ /* The sun4 assembler does not grok this. */
+ char *name = IDENTIFIER_POINTER (DECL_NAME (decl));
+ if (TREE_CODE (TREE_TYPE (decl)) == INTEGER_TYPE
+ || TREE_CODE (TREE_TYPE (decl)) == ENUMERAL_TYPE)
+ {
+ HOST_WIDE_INT ival = TREE_INT_CST_LOW (DECL_INITIAL (decl));
+#ifdef DBX_OUTPUT_CONSTANT_SYMBOL
+ DBX_OUTPUT_CONSTANT_SYMBOL (asmfile, name, ival);
+#else
+ fprintf (asmfile, "%s \"%s:c=i%d\",0x%x,0,0,0\n",
+ ASM_STABS_OP, name, ival, N_LSYM);
+#endif
+ return;
+ }
+ else if (TREE_CODE (TREE_TYPE (decl)) == REAL_TYPE)
+ {
+ /* don't know how to do this yet. */
+ }
+ break;
+ }
+ /* else it is something we handle like a normal variable. */
+ }
+
+ DECL_RTL (decl) = eliminate_regs (DECL_RTL (decl), 0, NULL_RTX);
+#ifdef LEAF_REG_REMAP
+ if (leaf_function)
+ leaf_renumber_regs_insn (DECL_RTL (decl));
+#endif
+
+ dbxout_symbol_location (decl, type, 0, DECL_RTL (decl));
+ }
+}
+
+/* Output the stab for DECL, a VAR_DECL, RESULT_DECL or PARM_DECL.
+ Add SUFFIX to its name, if SUFFIX is not 0.
+ Describe the variable as residing in HOME
+ (usually HOME is DECL_RTL (DECL), but not always). */
+
+static void
+dbxout_symbol_location (decl, type, suffix, home)
+ tree decl, type;
+ char *suffix;
+ rtx home;
+{
+ int letter = 0;
+ int regno = -1;
+
+ /* Don't mention a variable at all
+ if it was completely optimized into nothingness.
+
+ If the decl was from an inline function, then it's rtl
+ is not identically the rtl that was used in this
+ particular compilation. */
+ if (GET_CODE (home) == REG)
+ {
+ regno = REGNO (home);
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ return;
+ }
+ else if (GET_CODE (home) == SUBREG)
+ {
+ rtx value = home;
+ int offset = 0;
+ while (GET_CODE (value) == SUBREG)
+ {
+ offset += SUBREG_WORD (value);
+ value = SUBREG_REG (value);
+ }
+ if (GET_CODE (value) == REG)
+ {
+ regno = REGNO (value);
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ return;
+ regno += offset;
+ }
+ alter_subreg (home);
+ }
+
+ /* The kind-of-variable letter depends on where
+ the variable is and on the scope of its name:
+ G and N_GSYM for static storage and global scope,
+ S for static storage and file scope,
+ V for static storage and local scope,
+ for those two, use N_LCSYM if data is in bss segment,
+ N_STSYM if in data segment, N_FUN otherwise.
+ (We used N_FUN originally, then changed to N_STSYM
+ to please GDB. However, it seems that confused ld.
+ Now GDB has been fixed to like N_FUN, says Kingdon.)
+ no letter at all, and N_LSYM, for auto variable,
+ r and N_RSYM for register variable. */
+
+ if (GET_CODE (home) == MEM
+ && GET_CODE (XEXP (home, 0)) == SYMBOL_REF)
+ {
+ if (TREE_PUBLIC (decl))
+ {
+ letter = 'G';
+ current_sym_code = N_GSYM;
+ }
+ else
+ {
+ current_sym_addr = XEXP (home, 0);
+
+ letter = decl_function_context (decl) ? 'V' : 'S';
+
+ if (!DECL_INITIAL (decl))
+ current_sym_code = N_LCSYM;
+ else if (DECL_IN_TEXT_SECTION (decl))
+ /* This is not quite right, but it's the closest
+ of all the codes that Unix defines. */
+ current_sym_code = DBX_STATIC_CONST_VAR_CODE;
+ else
+ {
+ /* Ultrix `as' seems to need this. */
+#ifdef DBX_STATIC_STAB_DATA_SECTION
+ data_section ();
+#endif
+ current_sym_code = N_STSYM;
+ }
+ }
+ }
+ else if (regno >= 0)
+ {
+ letter = 'r';
+ current_sym_code = N_RSYM;
+ current_sym_value = DBX_REGISTER_NUMBER (regno);
+ }
+ else if (GET_CODE (home) == MEM
+ && (GET_CODE (XEXP (home, 0)) == MEM
+ || (GET_CODE (XEXP (home, 0)) == REG
+ && REGNO (XEXP (home, 0)) != HARD_FRAME_POINTER_REGNUM)))
+ /* If the value is indirect by memory or by a register
+ that isn't the frame pointer
+ then it means the object is variable-sized and address through
+ that register or stack slot. DBX has no way to represent this
+ so all we can do is output the variable as a pointer.
+ If it's not a parameter, ignore it.
+ (VAR_DECLs like this can be made by integrate.c.) */
+ {
+ if (GET_CODE (XEXP (home, 0)) == REG)
+ {
+ letter = 'r';
+ current_sym_code = N_RSYM;
+ current_sym_value = DBX_REGISTER_NUMBER (REGNO (XEXP (home, 0)));
+ }
+ else
+ {
+ current_sym_code = N_LSYM;
+ /* RTL looks like (MEM (MEM (PLUS (REG...) (CONST_INT...)))).
+ We want the value of that CONST_INT. */
+ current_sym_value
+ = DEBUGGER_AUTO_OFFSET (XEXP (XEXP (home, 0), 0));
+ }
+
+ /* Effectively do build_pointer_type, but don't cache this type,
+ since it might be temporary whereas the type it points to
+ might have been saved for inlining. */
+ /* Don't use REFERENCE_TYPE because dbx can't handle that. */
+ type = make_node (POINTER_TYPE);
+ TREE_TYPE (type) = TREE_TYPE (decl);
+ }
+ else if (GET_CODE (home) == MEM
+ && GET_CODE (XEXP (home, 0)) == REG)
+ {
+ current_sym_code = N_LSYM;
+ current_sym_value = DEBUGGER_AUTO_OFFSET (XEXP (home, 0));
+ }
+ else if (GET_CODE (home) == MEM
+ && GET_CODE (XEXP (home, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (home, 0), 1)) == CONST_INT)
+ {
+ current_sym_code = N_LSYM;
+ /* RTL looks like (MEM (PLUS (REG...) (CONST_INT...)))
+ We want the value of that CONST_INT. */
+ current_sym_value = DEBUGGER_AUTO_OFFSET (XEXP (home, 0));
+ }
+ else if (GET_CODE (home) == MEM
+ && GET_CODE (XEXP (home, 0)) == CONST)
+ {
+ /* Handle an obscure case which can arise when optimizing and
+ when there are few available registers. (This is *always*
+ the case for i386/i486 targets). The RTL looks like
+ (MEM (CONST ...)) even though this variable is a local `auto'
+ or a local `register' variable. In effect, what has happened
+ is that the reload pass has seen that all assignments and
+ references for one such a local variable can be replaced by
+ equivalent assignments and references to some static storage
+ variable, thereby avoiding the need for a register. In such
+ cases we're forced to lie to debuggers and tell them that
+ this variable was itself `static'. */
+ current_sym_code = N_LCSYM;
+ letter = 'V';
+ current_sym_addr = XEXP (XEXP (home, 0), 0);
+ }
+ else if (GET_CODE (home) == CONCAT)
+ {
+ tree subtype = TREE_TYPE (type);
+
+ /* If the variable's storage is in two parts,
+ output each as a separate stab with a modified name. */
+ if (WORDS_BIG_ENDIAN)
+ dbxout_symbol_location (decl, subtype, "$imag", XEXP (home, 0));
+ else
+ dbxout_symbol_location (decl, subtype, "$real", XEXP (home, 0));
+
+ /* Cast avoids warning in old compilers. */
+ current_sym_code = (STAB_CODE_TYPE) 0;
+ current_sym_value = 0;
+ current_sym_addr = 0;
+ dbxout_prepare_symbol (decl);
+
+ if (WORDS_BIG_ENDIAN)
+ dbxout_symbol_location (decl, subtype, "$real", XEXP (home, 1));
+ else
+ dbxout_symbol_location (decl, subtype, "$imag", XEXP (home, 1));
+ return;
+ }
+ else
+ /* Address might be a MEM, when DECL is a variable-sized object.
+ Or it might be const0_rtx, meaning previous passes
+ want us to ignore this variable. */
+ return;
+
+ /* Ok, start a symtab entry and output the variable name. */
+ FORCE_TEXT;
+
+#ifdef DBX_STATIC_BLOCK_START
+ DBX_STATIC_BLOCK_START (asmfile, current_sym_code);
+#endif
+
+ dbxout_symbol_name (decl, suffix, letter);
+ dbxout_type (type, 0, 0);
+ dbxout_finish_symbol (decl);
+
+#ifdef DBX_STATIC_BLOCK_END
+ DBX_STATIC_BLOCK_END (asmfile, current_sym_code);
+#endif
+}
+
+/* Output the symbol name of DECL for a stabs, with suffix SUFFIX.
+ Then output LETTER to indicate the kind of location the symbol has. */
+
+static void
+dbxout_symbol_name (decl, suffix, letter)
+ tree decl;
+ char *suffix;
+ int letter;
+{
+ /* One slight hitch: if this is a VAR_DECL which is a static
+ class member, we must put out the mangled name instead of the
+ DECL_NAME. */
+
+ char *name;
+ /* Note also that static member (variable) names DO NOT begin
+ with underscores in .stabs directives. */
+ if (DECL_LANG_SPECIFIC (decl))
+ name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+ else
+ name = IDENTIFIER_POINTER (DECL_NAME (decl));
+ if (name == 0)
+ name = "(anon)";
+ fprintf (asmfile, "%s \"%s%s:", ASM_STABS_OP, name,
+ (suffix ? suffix : ""));
+
+ if (letter) putc (letter, asmfile);
+}
+
+static void
+dbxout_prepare_symbol (decl)
+ tree decl;
+{
+#ifdef WINNING_GDB
+ char *filename = DECL_SOURCE_FILE (decl);
+
+ dbxout_source_file (asmfile, filename);
+#endif
+}
+
+static void
+dbxout_finish_symbol (sym)
+ tree sym;
+{
+#ifdef DBX_FINISH_SYMBOL
+ DBX_FINISH_SYMBOL (sym);
+#else
+ int line = 0;
+ if (use_gnu_debug_info_extensions && sym != 0)
+ line = DECL_SOURCE_LINE (sym);
+
+ fprintf (asmfile, "\",%d,0,%d,", current_sym_code, line);
+ if (current_sym_addr)
+ output_addr_const (asmfile, current_sym_addr);
+ else
+ fprintf (asmfile, "%d", current_sym_value);
+ putc ('\n', asmfile);
+#endif
+}
+
+/* Output definitions of all the decls in a chain. */
+
+void
+dbxout_syms (syms)
+ tree syms;
+{
+ while (syms)
+ {
+ dbxout_symbol (syms, 1);
+ syms = TREE_CHAIN (syms);
+ }
+}
+
+/* The following two functions output definitions of function parameters.
+ Each parameter gets a definition locating it in the parameter list.
+ Each parameter that is a register variable gets a second definition
+ locating it in the register.
+
+ Printing or argument lists in gdb uses the definitions that
+ locate in the parameter list. But reference to the variable in
+ expressions uses preferentially the definition as a register. */
+
+/* Output definitions, referring to storage in the parmlist,
+ of all the parms in PARMS, which is a chain of PARM_DECL nodes. */
+
+void
+dbxout_parms (parms)
+ tree parms;
+{
+ for (; parms; parms = TREE_CHAIN (parms))
+ if (DECL_NAME (parms) && TREE_TYPE (parms) != error_mark_node)
+ {
+ dbxout_prepare_symbol (parms);
+
+ /* Perform any necessary register eliminations on the parameter's rtl,
+ so that the debugging output will be accurate. */
+ DECL_INCOMING_RTL (parms)
+ = eliminate_regs (DECL_INCOMING_RTL (parms), 0, NULL_RTX);
+ DECL_RTL (parms) = eliminate_regs (DECL_RTL (parms), 0, NULL_RTX);
+#ifdef LEAF_REG_REMAP
+ if (leaf_function)
+ {
+ leaf_renumber_regs_insn (DECL_INCOMING_RTL (parms));
+ leaf_renumber_regs_insn (DECL_RTL (parms));
+ }
+#endif
+
+ if (PARM_PASSED_IN_MEMORY (parms))
+ {
+ rtx addr = XEXP (DECL_INCOMING_RTL (parms), 0);
+
+ /* ??? Here we assume that the parm address is indexed
+ off the frame pointer or arg pointer.
+ If that is not true, we produce meaningless results,
+ but do not crash. */
+ if (GET_CODE (addr) == PLUS
+ && GET_CODE (XEXP (addr, 1)) == CONST_INT)
+ current_sym_value = INTVAL (XEXP (addr, 1));
+ else
+ current_sym_value = 0;
+
+ current_sym_code = N_PSYM;
+ current_sym_addr = 0;
+
+ FORCE_TEXT;
+ if (DECL_NAME (parms))
+ {
+ current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms));
+
+ fprintf (asmfile, "%s \"%s:%c", ASM_STABS_OP,
+ IDENTIFIER_POINTER (DECL_NAME (parms)),
+ DBX_MEMPARM_STABS_LETTER);
+ }
+ else
+ {
+ current_sym_nchars = 8;
+ fprintf (asmfile, "%s \"(anon):%c", ASM_STABS_OP,
+ DBX_MEMPARM_STABS_LETTER);
+ }
+
+ if (GET_CODE (DECL_RTL (parms)) == REG
+ && REGNO (DECL_RTL (parms)) >= 0
+ && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER)
+ dbxout_type (DECL_ARG_TYPE (parms), 0, 0);
+ else
+ {
+ int original_value = current_sym_value;
+
+ /* This is the case where the parm is passed as an int or double
+ and it is converted to a char, short or float and stored back
+ in the parmlist. In this case, describe the parm
+ with the variable's declared type, and adjust the address
+ if the least significant bytes (which we are using) are not
+ the first ones. */
+#if BYTES_BIG_ENDIAN
+ if (TREE_TYPE (parms) != DECL_ARG_TYPE (parms))
+ current_sym_value += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms)))
+ - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms))));
+#endif
+
+ if (GET_CODE (DECL_RTL (parms)) == MEM
+ && GET_CODE (XEXP (DECL_RTL (parms), 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == CONST_INT
+ && INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == current_sym_value)
+ dbxout_type (TREE_TYPE (parms), 0, 0);
+ else
+ {
+ current_sym_value = original_value;
+ dbxout_type (DECL_ARG_TYPE (parms), 0, 0);
+ }
+ }
+ current_sym_value = DEBUGGER_ARG_OFFSET (current_sym_value, addr);
+ dbxout_finish_symbol (parms);
+ }
+ else if (GET_CODE (DECL_RTL (parms)) == REG)
+ {
+ rtx best_rtl;
+ char regparm_letter;
+ tree parm_type;
+ /* Parm passed in registers and lives in registers or nowhere. */
+
+ current_sym_code = DBX_REGPARM_STABS_CODE;
+ regparm_letter = DBX_REGPARM_STABS_LETTER;
+ current_sym_addr = 0;
+
+ /* If parm lives in a register, use that register;
+ pretend the parm was passed there. It would be more consistent
+ to describe the register where the parm was passed,
+ but in practice that register usually holds something else.
+
+ If we use DECL_RTL, then we must use the declared type of
+ the variable, not the type that it arrived in. */
+ if (REGNO (DECL_RTL (parms)) >= 0
+ && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER)
+ {
+ best_rtl = DECL_RTL (parms);
+ parm_type = TREE_TYPE (parms);
+ }
+ /* If the parm lives nowhere, use the register where it was
+ passed. It is also better to use the declared type here. */
+ else
+ {
+ best_rtl = DECL_INCOMING_RTL (parms);
+ parm_type = TREE_TYPE (parms);
+ }
+ current_sym_value = DBX_REGISTER_NUMBER (REGNO (best_rtl));
+
+ FORCE_TEXT;
+ if (DECL_NAME (parms))
+ {
+ current_sym_nchars = 2 + IDENTIFIER_LENGTH (DECL_NAME (parms));
+ fprintf (asmfile, "%s \"%s:%c", ASM_STABS_OP,
+ IDENTIFIER_POINTER (DECL_NAME (parms)),
+ regparm_letter);
+ }
+ else
+ {
+ current_sym_nchars = 8;
+ fprintf (asmfile, "%s \"(anon):%c", ASM_STABS_OP,
+ regparm_letter);
+ }
+
+ dbxout_type (parm_type, 0, 0);
+ dbxout_finish_symbol (parms);
+ }
+ else if (GET_CODE (DECL_RTL (parms)) == MEM
+ && GET_CODE (XEXP (DECL_RTL (parms), 0)) == REG
+ && REGNO (XEXP (DECL_RTL (parms), 0)) != HARD_FRAME_POINTER_REGNUM
+ && REGNO (XEXP (DECL_RTL (parms), 0)) != STACK_POINTER_REGNUM
+#if ARG_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+ && REGNO (XEXP (DECL_RTL (parms), 0)) != ARG_POINTER_REGNUM
+#endif
+ )
+ {
+ /* Parm was passed via invisible reference.
+ That is, its address was passed in a register.
+ Output it as if it lived in that register.
+ The debugger will know from the type
+ that it was actually passed by invisible reference. */
+
+ char regparm_letter;
+ /* Parm passed in registers and lives in registers or nowhere. */
+
+ current_sym_code = DBX_REGPARM_STABS_CODE;
+ regparm_letter = DBX_REGPARM_STABS_LETTER;
+
+ /* DECL_RTL looks like (MEM (REG...). Get the register number.
+ If it is an unallocated pseudo-reg, then use the register where
+ it was passed instead. */
+ if (REGNO (XEXP (DECL_RTL (parms), 0)) >= 0
+ && REGNO (XEXP (DECL_RTL (parms), 0)) < FIRST_PSEUDO_REGISTER)
+ current_sym_value = REGNO (XEXP (DECL_RTL (parms), 0));
+ else
+ current_sym_value = REGNO (DECL_INCOMING_RTL (parms));
+
+ current_sym_addr = 0;
+
+ FORCE_TEXT;
+ if (DECL_NAME (parms))
+ {
+ current_sym_nchars = 2 + strlen (IDENTIFIER_POINTER (DECL_NAME (parms)));
+
+ fprintf (asmfile, "%s \"%s:%c", ASM_STABS_OP,
+ IDENTIFIER_POINTER (DECL_NAME (parms)),
+ DBX_REGPARM_STABS_LETTER);
+ }
+ else
+ {
+ current_sym_nchars = 8;
+ fprintf (asmfile, "%s \"(anon):%c", ASM_STABS_OP,
+ DBX_REGPARM_STABS_LETTER);
+ }
+
+ dbxout_type (TREE_TYPE (parms), 0, 0);
+ dbxout_finish_symbol (parms);
+ }
+ else if (GET_CODE (DECL_RTL (parms)) == MEM
+ && XEXP (DECL_RTL (parms), 0) != const0_rtx
+ /* ??? A constant address for a parm can happen
+ when the reg it lives in is equiv to a constant in memory.
+ Should make this not happen, after 2.4. */
+ && ! CONSTANT_P (XEXP (DECL_RTL (parms), 0)))
+ {
+ /* Parm was passed in registers but lives on the stack. */
+
+ current_sym_code = N_PSYM;
+ /* DECL_RTL looks like (MEM (PLUS (REG...) (CONST_INT...))),
+ in which case we want the value of that CONST_INT,
+ or (MEM (REG ...)) or (MEM (MEM ...)),
+ in which case we use a value of zero. */
+ if (GET_CODE (XEXP (DECL_RTL (parms), 0)) == REG
+ || GET_CODE (XEXP (DECL_RTL (parms), 0)) == MEM)
+ current_sym_value = 0;
+ else
+ current_sym_value = INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1));
+ current_sym_addr = 0;
+
+ FORCE_TEXT;
+ if (DECL_NAME (parms))
+ {
+ current_sym_nchars = 2 + strlen (IDENTIFIER_POINTER (DECL_NAME (parms)));
+
+ fprintf (asmfile, "%s \"%s:%c", ASM_STABS_OP,
+ IDENTIFIER_POINTER (DECL_NAME (parms)),
+ DBX_MEMPARM_STABS_LETTER);
+ }
+ else
+ {
+ current_sym_nchars = 8;
+ fprintf (asmfile, "%s \"(anon):%c", ASM_STABS_OP,
+ DBX_MEMPARM_STABS_LETTER);
+ }
+
+ current_sym_value
+ = DEBUGGER_ARG_OFFSET (current_sym_value,
+ XEXP (DECL_RTL (parms), 0));
+ dbxout_type (TREE_TYPE (parms), 0, 0);
+ dbxout_finish_symbol (parms);
+ }
+ }
+}
+
+/* Output definitions for the places where parms live during the function,
+ when different from where they were passed, when the parms were passed
+ in memory.
+
+ It is not useful to do this for parms passed in registers
+ that live during the function in different registers, because it is
+ impossible to look in the passed register for the passed value,
+ so we use the within-the-function register to begin with.
+
+ PARMS is a chain of PARM_DECL nodes. */
+
+void
+dbxout_reg_parms (parms)
+ tree parms;
+{
+ for (; parms; parms = TREE_CHAIN (parms))
+ if (DECL_NAME (parms))
+ {
+ dbxout_prepare_symbol (parms);
+
+ /* Report parms that live in registers during the function
+ but were passed in memory. */
+ if (GET_CODE (DECL_RTL (parms)) == REG
+ && REGNO (DECL_RTL (parms)) >= 0
+ && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER
+ && PARM_PASSED_IN_MEMORY (parms))
+ dbxout_symbol_location (parms, TREE_TYPE (parms),
+ 0, DECL_RTL (parms));
+ else if (GET_CODE (DECL_RTL (parms)) == CONCAT
+ && PARM_PASSED_IN_MEMORY (parms))
+ dbxout_symbol_location (parms, TREE_TYPE (parms),
+ 0, DECL_RTL (parms));
+ /* Report parms that live in memory but not where they were passed. */
+ else if (GET_CODE (DECL_RTL (parms)) == MEM
+ && GET_CODE (XEXP (DECL_RTL (parms), 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == CONST_INT
+ && PARM_PASSED_IN_MEMORY (parms)
+ && ! rtx_equal_p (DECL_RTL (parms), DECL_INCOMING_RTL (parms)))
+ {
+#if 0 /* ??? It is not clear yet what should replace this. */
+ int offset = DECL_OFFSET (parms) / BITS_PER_UNIT;
+ /* A parm declared char is really passed as an int,
+ so it occupies the least significant bytes.
+ On a big-endian machine those are not the low-numbered ones. */
+#if BYTES_BIG_ENDIAN
+ if (offset != -1 && TREE_TYPE (parms) != DECL_ARG_TYPE (parms))
+ offset += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms)))
+ - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms))));
+#endif
+ if (INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)) != offset) {...}
+#endif
+ dbxout_symbol_location (parms, TREE_TYPE (parms),
+ 0, DECL_RTL (parms));
+ }
+#if 0
+ else if (GET_CODE (DECL_RTL (parms)) == MEM
+ && GET_CODE (XEXP (DECL_RTL (parms), 0)) == REG)
+ {
+ /* Parm was passed via invisible reference.
+ That is, its address was passed in a register.
+ Output it as if it lived in that register.
+ The debugger will know from the type
+ that it was actually passed by invisible reference. */
+
+ current_sym_code = N_RSYM;
+
+ /* DECL_RTL looks like (MEM (REG...). Get the register number. */
+ current_sym_value = REGNO (XEXP (DECL_RTL (parms), 0));
+ current_sym_addr = 0;
+
+ FORCE_TEXT;
+ if (DECL_NAME (parms))
+ {
+ current_sym_nchars = 2 + strlen (IDENTIFIER_POINTER (DECL_NAME (parms)));
+
+ fprintf (asmfile, "%s \"%s:r", ASM_STABS_OP,
+ IDENTIFIER_POINTER (DECL_NAME (parms)));
+ }
+ else
+ {
+ current_sym_nchars = 8;
+ fprintf (asmfile, "%s \"(anon):r", ASM_STABS_OP);
+ }
+
+ dbxout_type (TREE_TYPE (parms), 0, 0);
+ dbxout_finish_symbol (parms);
+ }
+#endif
+ }
+}
+
+/* Given a chain of ..._TYPE nodes (as come in a parameter list),
+ output definitions of those names, in raw form */
+
+void
+dbxout_args (args)
+ tree args;
+{
+ while (args)
+ {
+ putc (',', asmfile);
+ dbxout_type (TREE_VALUE (args), 0, 0);
+ CHARS (1);
+ args = TREE_CHAIN (args);
+ }
+}
+
+/* Given a chain of ..._TYPE nodes,
+ find those which have typedef names and output those names.
+ This is to ensure those types get output. */
+
+void
+dbxout_types (types)
+ register tree types;
+{
+ while (types)
+ {
+ if (TYPE_NAME (types)
+ && TREE_CODE (TYPE_NAME (types)) == TYPE_DECL
+ && ! TREE_ASM_WRITTEN (TYPE_NAME (types)))
+ dbxout_symbol (TYPE_NAME (types), 1);
+ types = TREE_CHAIN (types);
+ }
+}
+
+/* Output everything about a symbol block (a BLOCK node
+ that represents a scope level),
+ including recursive output of contained blocks.
+
+ BLOCK is the BLOCK node.
+ DEPTH is its depth within containing symbol blocks.
+ ARGS is usually zero; but for the outermost block of the
+ body of a function, it is a chain of PARM_DECLs for the function parameters.
+ We output definitions of all the register parms
+ as if they were local variables of that block.
+
+ If -g1 was used, we count blocks just the same, but output nothing
+ except for the outermost block.
+
+ Actually, BLOCK may be several blocks chained together.
+ We handle them all in sequence. */
+
+static void
+dbxout_block (block, depth, args)
+ register tree block;
+ int depth;
+ tree args;
+{
+ int blocknum;
+
+ while (block)
+ {
+ /* Ignore blocks never expanded or otherwise marked as real. */
+ if (TREE_USED (block))
+ {
+#ifndef DBX_LBRAC_FIRST
+ /* In dbx format, the syms of a block come before the N_LBRAC. */
+ if (debug_info_level != DINFO_LEVEL_TERSE || depth == 0)
+ dbxout_syms (BLOCK_VARS (block));
+ if (args)
+ dbxout_reg_parms (args);
+#endif
+
+ /* Now output an N_LBRAC symbol to represent the beginning of
+ the block. Use the block's tree-walk order to generate
+ the assembler symbols LBBn and LBEn
+ that final will define around the code in this block. */
+ if (depth > 0 && debug_info_level != DINFO_LEVEL_TERSE)
+ {
+ char buf[20];
+ blocknum = next_block_number++;
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LBB", blocknum);
+
+ if (BLOCK_HANDLER_BLOCK (block))
+ {
+ /* A catch block. Must precede N_LBRAC. */
+ tree decl = BLOCK_VARS (block);
+ while (decl)
+ {
+#ifdef DBX_OUTPUT_CATCH
+ DBX_OUTPUT_CATCH (asmfile, decl, buf);
+#else
+ fprintf (asmfile, "%s \"%s:C1\",%d,0,0,", ASM_STABS_OP,
+ IDENTIFIER_POINTER (DECL_NAME (decl)), N_CATCH);
+ assemble_name (asmfile, buf);
+ fprintf (asmfile, "\n");
+#endif
+ decl = TREE_CHAIN (decl);
+ }
+ }
+
+#ifdef DBX_OUTPUT_LBRAC
+ DBX_OUTPUT_LBRAC (asmfile, buf);
+#else
+ fprintf (asmfile, "%s %d,0,0,", ASM_STABN_OP, N_LBRAC);
+ assemble_name (asmfile, buf);
+#if DBX_BLOCKS_FUNCTION_RELATIVE
+ fputc ('-', asmfile);
+ assemble_name (asmfile, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0));
+#endif
+ fprintf (asmfile, "\n");
+#endif
+ }
+ else if (depth > 0)
+ /* Count blocks the same way regardless of debug_info_level. */
+ next_block_number++;
+
+#ifdef DBX_LBRAC_FIRST
+ /* On some weird machines, the syms of a block
+ come after the N_LBRAC. */
+ if (debug_info_level != DINFO_LEVEL_TERSE || depth == 0)
+ dbxout_syms (BLOCK_VARS (block));
+ if (args)
+ dbxout_reg_parms (args);
+#endif
+
+ /* Output the subblocks. */
+ dbxout_block (BLOCK_SUBBLOCKS (block), depth + 1, NULL_TREE);
+
+ /* Refer to the marker for the end of the block. */
+ if (depth > 0 && debug_info_level != DINFO_LEVEL_TERSE)
+ {
+ char buf[20];
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LBE", blocknum);
+#ifdef DBX_OUTPUT_RBRAC
+ DBX_OUTPUT_RBRAC (asmfile, buf);
+#else
+ fprintf (asmfile, "%s %d,0,0,", ASM_STABN_OP, N_RBRAC);
+ assemble_name (asmfile, buf);
+#if DBX_BLOCKS_FUNCTION_RELATIVE
+ fputc ('-', asmfile);
+ assemble_name (asmfile, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0));
+#endif
+ fprintf (asmfile, "\n");
+#endif
+ }
+ }
+ block = BLOCK_CHAIN (block);
+ }
+}
+
+/* Output the information about a function and its arguments and result.
+ Usually this follows the function's code,
+ but on some systems, it comes before. */
+
+static void
+dbxout_really_begin_function (decl)
+ tree decl;
+{
+ dbxout_symbol (decl, 0);
+ dbxout_parms (DECL_ARGUMENTS (decl));
+ if (DECL_NAME (DECL_RESULT (decl)) != 0)
+ dbxout_symbol (DECL_RESULT (decl), 1);
+}
+
+/* Called at beginning of output of function definition. */
+
+void
+dbxout_begin_function (decl)
+ tree decl;
+{
+#ifdef DBX_FUNCTION_FIRST
+ dbxout_really_begin_function (decl);
+#endif
+}
+
+/* Output dbx data for a function definition.
+ This includes a definition of the function name itself (a symbol),
+ definitions of the parameters (locating them in the parameter list)
+ and then output the block that makes up the function's body
+ (including all the auto variables of the function). */
+
+void
+dbxout_function (decl)
+ tree decl;
+{
+#ifndef DBX_FUNCTION_FIRST
+ dbxout_really_begin_function (decl);
+#endif
+ dbxout_block (DECL_INITIAL (decl), 0, DECL_ARGUMENTS (decl));
+#ifdef DBX_OUTPUT_FUNCTION_END
+ DBX_OUTPUT_FUNCTION_END (asmfile, decl);
+#endif
+}
+#endif /* DBX_DEBUGGING_INFO */
diff --git a/gnu/usr.bin/cc/cc_int/dwarfout.c b/gnu/usr.bin/cc/cc_int/dwarfout.c
new file mode 100644
index 0000000..a48c9b97
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/dwarfout.c
@@ -0,0 +1,5667 @@
+/* Output Dwarf format symbol table information from the GNU C compiler.
+ Copyright (C) 1992, 1993 Free Software Foundation, Inc.
+
+ Written by Ron Guilmette (rfg@netcom.com) for
+ Network Computing Devices, August, September, October, November 1990.
+ Generously contributed by NCD to the Free Software Foundation.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "config.h"
+
+#ifdef DWARF_DEBUGGING_INFO
+#include <stdio.h>
+#include "dwarf.h"
+#include "tree.h"
+#include "flags.h"
+#include "rtl.h"
+#include "hard-reg-set.h"
+#include "insn-config.h"
+#include "reload.h"
+#include "output.h"
+#include "defaults.h"
+
+#ifndef DWARF_VERSION
+#define DWARF_VERSION 1
+#endif
+
+/* #define NDEBUG 1 */
+#include "assert.h"
+
+#if defined(DWARF_TIMESTAMPS)
+#if defined(POSIX)
+#include <time.h>
+#else /* !defined(POSIX) */
+#include <sys/types.h>
+#if defined(__STDC__)
+extern time_t time (time_t *);
+#else /* !defined(__STDC__) */
+extern time_t time ();
+#endif /* !defined(__STDC__) */
+#endif /* !defined(POSIX) */
+#endif /* defined(DWARF_TIMESTAMPS) */
+
+extern char *getpwd ();
+
+extern char *index ();
+extern char *rindex ();
+
+/* IMPORTANT NOTE: Please see the file README.DWARF for important details
+ regarding the GNU implementation of Dwarf. */
+
+/* NOTE: In the comments in this file, many references are made to
+ so called "Debugging Information Entries". For the sake of brevity,
+ this term is abbreviated to `DIE' throughout the remainder of this
+ file. */
+
+/* Note that the implementation of C++ support herein is (as yet) unfinished.
+ If you want to try to complete it, more power to you. */
+
+#if defined(__GNUC__) && (NDEBUG == 1)
+#define inline static inline
+#else
+#define inline static
+#endif
+
+/* How to start an assembler comment. */
+#ifndef ASM_COMMENT_START
+#define ASM_COMMENT_START ";#"
+#endif
+
+/* How to print out a register name. */
+#ifndef PRINT_REG
+#define PRINT_REG(RTX, CODE, FILE) \
+ fprintf ((FILE), "%s", reg_names[REGNO (RTX)])
+#endif
+
+/* Define a macro which returns non-zero for any tagged type which is
+ used (directly or indirectly) in the specification of either some
+ function's return type or some formal parameter of some function.
+ We use this macro when we are operating in "terse" mode to help us
+ know what tagged types have to be represented in Dwarf (even in
+ terse mode) and which ones don't.
+
+ A flag bit with this meaning really should be a part of the normal
+ GCC ..._TYPE nodes, but at the moment, there is no such bit defined
+ for these nodes. For now, we have to just fake it. It it safe for
+ us to simply return zero for all complete tagged types (which will
+ get forced out anyway if they were used in the specification of some
+ formal or return type) and non-zero for all incomplete tagged types.
+*/
+
+#define TYPE_USED_FOR_FUNCTION(tagged_type) (TYPE_SIZE (tagged_type) == 0)
+
+extern int flag_traditional;
+extern char *version_string;
+extern char *language_string;
+
+/* Maximum size (in bytes) of an artificially generated label. */
+
+#define MAX_ARTIFICIAL_LABEL_BYTES 30
+
+/* Make sure we know the sizes of the various types dwarf can describe.
+ These are only defaults. If the sizes are different for your target,
+ you should override these values by defining the appropriate symbols
+ in your tm.h file. */
+
+#ifndef CHAR_TYPE_SIZE
+#define CHAR_TYPE_SIZE BITS_PER_UNIT
+#endif
+
+#ifndef SHORT_TYPE_SIZE
+#define SHORT_TYPE_SIZE (BITS_PER_UNIT * 2)
+#endif
+
+#ifndef INT_TYPE_SIZE
+#define INT_TYPE_SIZE BITS_PER_WORD
+#endif
+
+#ifndef LONG_TYPE_SIZE
+#define LONG_TYPE_SIZE BITS_PER_WORD
+#endif
+
+#ifndef LONG_LONG_TYPE_SIZE
+#define LONG_LONG_TYPE_SIZE (BITS_PER_WORD * 2)
+#endif
+
+#ifndef WCHAR_TYPE_SIZE
+#define WCHAR_TYPE_SIZE INT_TYPE_SIZE
+#endif
+
+#ifndef WCHAR_UNSIGNED
+#define WCHAR_UNSIGNED 0
+#endif
+
+#ifndef FLOAT_TYPE_SIZE
+#define FLOAT_TYPE_SIZE BITS_PER_WORD
+#endif
+
+#ifndef DOUBLE_TYPE_SIZE
+#define DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2)
+#endif
+
+#ifndef LONG_DOUBLE_TYPE_SIZE
+#define LONG_DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2)
+#endif
+
+/* Structure to keep track of source filenames. */
+
+struct filename_entry {
+ unsigned number;
+ char * name;
+};
+
+typedef struct filename_entry filename_entry;
+
+/* Pointer to an array of elements, each one having the structure above. */
+
+static filename_entry *filename_table;
+
+/* Total number of entries in the table (i.e. array) pointed to by
+ `filename_table'. This is the *total* and includes both used and
+ unused slots. */
+
+static unsigned ft_entries_allocated;
+
+/* Number of entries in the filename_table which are actually in use. */
+
+static unsigned ft_entries;
+
+/* Size (in elements) of increments by which we may expand the filename
+ table. Actually, a single hunk of space of this size should be enough
+ for most typical programs. */
+
+#define FT_ENTRIES_INCREMENT 64
+
+/* Local pointer to the name of the main input file. Initialized in
+ dwarfout_init. */
+
+static char *primary_filename;
+
+/* Pointer to the most recent filename for which we produced some line info. */
+
+static char *last_filename;
+
+/* For Dwarf output, we must assign lexical-blocks id numbers
+ in the order in which their beginnings are encountered.
+ We output Dwarf debugging info that refers to the beginnings
+ and ends of the ranges of code for each lexical block with
+ assembler labels ..Bn and ..Bn.e, where n is the block number.
+ The labels themselves are generated in final.c, which assigns
+ numbers to the blocks in the same way. */
+
+static unsigned next_block_number = 2;
+
+/* Counter to generate unique names for DIEs. */
+
+static unsigned next_unused_dienum = 1;
+
+/* Number of the DIE which is currently being generated. */
+
+static unsigned current_dienum;
+
+/* Number to use for the special "pubname" label on the next DIE which
+ represents a function or data object defined in this compilation
+ unit which has "extern" linkage. */
+
+static next_pubname_number = 0;
+
+#define NEXT_DIE_NUM pending_sibling_stack[pending_siblings-1]
+
+/* Pointer to a dynamically allocated list of pre-reserved and still
+ pending sibling DIE numbers. Note that this list will grow as needed. */
+
+static unsigned *pending_sibling_stack;
+
+/* Counter to keep track of the number of pre-reserved and still pending
+ sibling DIE numbers. */
+
+static unsigned pending_siblings;
+
+/* The currently allocated size of the above list (expressed in number of
+ list elements). */
+
+static unsigned pending_siblings_allocated;
+
+/* Size (in elements) of increments by which we may expand the pending
+ sibling stack. Actually, a single hunk of space of this size should
+ be enough for most typical programs. */
+
+#define PENDING_SIBLINGS_INCREMENT 64
+
+/* Non-zero if we are performing our file-scope finalization pass and if
+ we should force out Dwarf descriptions of any and all file-scope
+ tagged types which are still incomplete types. */
+
+static int finalizing = 0;
+
+/* A pointer to the base of a list of pending types which we haven't
+ generated DIEs for yet, but which we will have to come back to
+ later on. */
+
+static tree *pending_types_list;
+
+/* Number of elements currently allocated for the pending_types_list. */
+
+static unsigned pending_types_allocated;
+
+/* Number of elements of pending_types_list currently in use. */
+
+static unsigned pending_types;
+
+/* Size (in elements) of increments by which we may expand the pending
+ types list. Actually, a single hunk of space of this size should
+ be enough for most typical programs. */
+
+#define PENDING_TYPES_INCREMENT 64
+
+/* Pointer to an artificial RECORD_TYPE which we create in dwarfout_init.
+ This is used in a hack to help us get the DIEs describing types of
+ formal parameters to come *after* all of the DIEs describing the formal
+ parameters themselves. That's necessary in order to be compatible
+ with what the brain-damaged svr4 SDB debugger requires. */
+
+static tree fake_containing_scope;
+
+/* The number of the current function definition that we are generating
+ debugging information for. These numbers range from 1 up to the maximum
+ number of function definitions contained within the current compilation
+ unit. These numbers are used to create unique labels for various things
+ contained within various function definitions. */
+
+static unsigned current_funcdef_number = 1;
+
+/* A pointer to the ..._DECL node which we have most recently been working
+ on. We keep this around just in case something about it looks screwy
+ and we want to tell the user what the source coordinates for the actual
+ declaration are. */
+
+static tree dwarf_last_decl;
+
+/* Forward declarations for functions defined in this file. */
+
+static void output_type ();
+static void type_attribute ();
+static void output_decls_for_scope ();
+static void output_decl ();
+static unsigned lookup_filename ();
+
+/* Definitions of defaults for assembler-dependent names of various
+ pseudo-ops and section names.
+
+ Theses may be overridden in your tm.h file (if necessary) for your
+ particular assembler. The default values provided here correspond to
+ what is expected by "standard" AT&T System V.4 assemblers. */
+
+#ifndef FILE_ASM_OP
+#define FILE_ASM_OP ".file"
+#endif
+#ifndef VERSION_ASM_OP
+#define VERSION_ASM_OP ".version"
+#endif
+#ifndef UNALIGNED_SHORT_ASM_OP
+#define UNALIGNED_SHORT_ASM_OP ".2byte"
+#endif
+#ifndef UNALIGNED_INT_ASM_OP
+#define UNALIGNED_INT_ASM_OP ".4byte"
+#endif
+#ifndef ASM_BYTE_OP
+#define ASM_BYTE_OP ".byte"
+#endif
+#ifndef SET_ASM_OP
+#define SET_ASM_OP ".set"
+#endif
+
+/* Pseudo-ops for pushing the current section onto the section stack (and
+ simultaneously changing to a new section) and for poping back to the
+ section we were in immediately before this one. Note that most svr4
+ assemblers only maintain a one level stack... you can push all the
+ sections you want, but you can only pop out one level. (The sparc
+ svr4 assembler is an exception to this general rule.) That's
+ OK because we only use at most one level of the section stack herein. */
+
+#ifndef PUSHSECTION_ASM_OP
+#define PUSHSECTION_ASM_OP ".section"
+#endif
+#ifndef POPSECTION_ASM_OP
+#define POPSECTION_ASM_OP ".previous"
+#endif
+
+/* The default format used by the ASM_OUTPUT_PUSH_SECTION macro (see below)
+ to print the PUSHSECTION_ASM_OP and the section name. The default here
+ works for almost all svr4 assemblers, except for the sparc, where the
+ section name must be enclosed in double quotes. (See sparcv4.h.) */
+
+#ifndef PUSHSECTION_FORMAT
+#define PUSHSECTION_FORMAT "%s\t%s\n"
+#endif
+
+#ifndef DEBUG_SECTION
+#define DEBUG_SECTION ".debug"
+#endif
+#ifndef LINE_SECTION
+#define LINE_SECTION ".line"
+#endif
+#ifndef SFNAMES_SECTION
+#define SFNAMES_SECTION ".debug_sfnames"
+#endif
+#ifndef SRCINFO_SECTION
+#define SRCINFO_SECTION ".debug_srcinfo"
+#endif
+#ifndef MACINFO_SECTION
+#define MACINFO_SECTION ".debug_macinfo"
+#endif
+#ifndef PUBNAMES_SECTION
+#define PUBNAMES_SECTION ".debug_pubnames"
+#endif
+#ifndef ARANGES_SECTION
+#define ARANGES_SECTION ".debug_aranges"
+#endif
+#ifndef TEXT_SECTION
+#define TEXT_SECTION ".text"
+#endif
+#ifndef DATA_SECTION
+#define DATA_SECTION ".data"
+#endif
+#ifndef DATA1_SECTION
+#define DATA1_SECTION ".data1"
+#endif
+#ifndef RODATA_SECTION
+#define RODATA_SECTION ".rodata"
+#endif
+#ifndef RODATA1_SECTION
+#define RODATA1_SECTION ".rodata1"
+#endif
+#ifndef BSS_SECTION
+#define BSS_SECTION ".bss"
+#endif
+
+/* Definitions of defaults for formats and names of various special
+ (artificial) labels which may be generated within this file (when
+ the -g options is used and DWARF_DEBUGGING_INFO is in effect.
+
+ If necessary, these may be overridden from within your tm.h file,
+ but typically, you should never need to override these.
+
+ These labels have been hacked (temporarily) so that they all begin with
+ a `.L' sequence so as to appease the stock sparc/svr4 assembler and the
+ stock m88k/svr4 assembler, both of which need to see .L at the start of
+ a label in order to prevent that label from going into the linker symbol
+ table). When I get time, I'll have to fix this the right way so that we
+ will use ASM_GENERATE_INTERNAL_LABEL and ASM_OUTPUT_INTERNAL_LABEL herein,
+ but that will require a rather massive set of changes. For the moment,
+ the following definitions out to produce the right results for all svr4
+ and svr3 assemblers. -- rfg
+*/
+
+#ifndef TEXT_BEGIN_LABEL
+#define TEXT_BEGIN_LABEL ".L_text_b"
+#endif
+#ifndef TEXT_END_LABEL
+#define TEXT_END_LABEL ".L_text_e"
+#endif
+
+#ifndef DATA_BEGIN_LABEL
+#define DATA_BEGIN_LABEL ".L_data_b"
+#endif
+#ifndef DATA_END_LABEL
+#define DATA_END_LABEL ".L_data_e"
+#endif
+
+#ifndef DATA1_BEGIN_LABEL
+#define DATA1_BEGIN_LABEL ".L_data1_b"
+#endif
+#ifndef DATA1_END_LABEL
+#define DATA1_END_LABEL ".L_data1_e"
+#endif
+
+#ifndef RODATA_BEGIN_LABEL
+#define RODATA_BEGIN_LABEL ".L_rodata_b"
+#endif
+#ifndef RODATA_END_LABEL
+#define RODATA_END_LABEL ".L_rodata_e"
+#endif
+
+#ifndef RODATA1_BEGIN_LABEL
+#define RODATA1_BEGIN_LABEL ".L_rodata1_b"
+#endif
+#ifndef RODATA1_END_LABEL
+#define RODATA1_END_LABEL ".L_rodata1_e"
+#endif
+
+#ifndef BSS_BEGIN_LABEL
+#define BSS_BEGIN_LABEL ".L_bss_b"
+#endif
+#ifndef BSS_END_LABEL
+#define BSS_END_LABEL ".L_bss_e"
+#endif
+
+#ifndef LINE_BEGIN_LABEL
+#define LINE_BEGIN_LABEL ".L_line_b"
+#endif
+#ifndef LINE_LAST_ENTRY_LABEL
+#define LINE_LAST_ENTRY_LABEL ".L_line_last"
+#endif
+#ifndef LINE_END_LABEL
+#define LINE_END_LABEL ".L_line_e"
+#endif
+
+#ifndef DEBUG_BEGIN_LABEL
+#define DEBUG_BEGIN_LABEL ".L_debug_b"
+#endif
+#ifndef SFNAMES_BEGIN_LABEL
+#define SFNAMES_BEGIN_LABEL ".L_sfnames_b"
+#endif
+#ifndef SRCINFO_BEGIN_LABEL
+#define SRCINFO_BEGIN_LABEL ".L_srcinfo_b"
+#endif
+#ifndef MACINFO_BEGIN_LABEL
+#define MACINFO_BEGIN_LABEL ".L_macinfo_b"
+#endif
+
+#ifndef DIE_BEGIN_LABEL_FMT
+#define DIE_BEGIN_LABEL_FMT ".L_D%u"
+#endif
+#ifndef DIE_END_LABEL_FMT
+#define DIE_END_LABEL_FMT ".L_D%u_e"
+#endif
+#ifndef PUB_DIE_LABEL_FMT
+#define PUB_DIE_LABEL_FMT ".L_P%u"
+#endif
+#ifndef INSN_LABEL_FMT
+#define INSN_LABEL_FMT ".L_I%u_%u"
+#endif
+#ifndef BLOCK_BEGIN_LABEL_FMT
+#define BLOCK_BEGIN_LABEL_FMT ".L_B%u"
+#endif
+#ifndef BLOCK_END_LABEL_FMT
+#define BLOCK_END_LABEL_FMT ".L_B%u_e"
+#endif
+#ifndef SS_BEGIN_LABEL_FMT
+#define SS_BEGIN_LABEL_FMT ".L_s%u"
+#endif
+#ifndef SS_END_LABEL_FMT
+#define SS_END_LABEL_FMT ".L_s%u_e"
+#endif
+#ifndef EE_BEGIN_LABEL_FMT
+#define EE_BEGIN_LABEL_FMT ".L_e%u"
+#endif
+#ifndef EE_END_LABEL_FMT
+#define EE_END_LABEL_FMT ".L_e%u_e"
+#endif
+#ifndef MT_BEGIN_LABEL_FMT
+#define MT_BEGIN_LABEL_FMT ".L_t%u"
+#endif
+#ifndef MT_END_LABEL_FMT
+#define MT_END_LABEL_FMT ".L_t%u_e"
+#endif
+#ifndef LOC_BEGIN_LABEL_FMT
+#define LOC_BEGIN_LABEL_FMT ".L_l%u"
+#endif
+#ifndef LOC_END_LABEL_FMT
+#define LOC_END_LABEL_FMT ".L_l%u_e"
+#endif
+#ifndef BOUND_BEGIN_LABEL_FMT
+#define BOUND_BEGIN_LABEL_FMT ".L_b%u_%u_%c"
+#endif
+#ifndef BOUND_END_LABEL_FMT
+#define BOUND_END_LABEL_FMT ".L_b%u_%u_%c_e"
+#endif
+#ifndef DERIV_BEGIN_LABEL_FMT
+#define DERIV_BEGIN_LABEL_FMT ".L_d%u"
+#endif
+#ifndef DERIV_END_LABEL_FMT
+#define DERIV_END_LABEL_FMT ".L_d%u_e"
+#endif
+#ifndef SL_BEGIN_LABEL_FMT
+#define SL_BEGIN_LABEL_FMT ".L_sl%u"
+#endif
+#ifndef SL_END_LABEL_FMT
+#define SL_END_LABEL_FMT ".L_sl%u_e"
+#endif
+#ifndef BODY_BEGIN_LABEL_FMT
+#define BODY_BEGIN_LABEL_FMT ".L_b%u"
+#endif
+#ifndef BODY_END_LABEL_FMT
+#define BODY_END_LABEL_FMT ".L_b%u_e"
+#endif
+#ifndef FUNC_END_LABEL_FMT
+#define FUNC_END_LABEL_FMT ".L_f%u_e"
+#endif
+#ifndef TYPE_NAME_FMT
+#define TYPE_NAME_FMT ".L_T%u"
+#endif
+#ifndef DECL_NAME_FMT
+#define DECL_NAME_FMT ".L_E%u"
+#endif
+#ifndef LINE_CODE_LABEL_FMT
+#define LINE_CODE_LABEL_FMT ".L_LC%u"
+#endif
+#ifndef SFNAMES_ENTRY_LABEL_FMT
+#define SFNAMES_ENTRY_LABEL_FMT ".L_F%u"
+#endif
+#ifndef LINE_ENTRY_LABEL_FMT
+#define LINE_ENTRY_LABEL_FMT ".L_LE%u"
+#endif
+
+/* Definitions of defaults for various types of primitive assembly language
+ output operations.
+
+ If necessary, these may be overridden from within your tm.h file,
+ but typically, you shouldn't need to override these. */
+
+#ifndef ASM_OUTPUT_PUSH_SECTION
+#define ASM_OUTPUT_PUSH_SECTION(FILE, SECTION) \
+ fprintf ((FILE), PUSHSECTION_FORMAT, PUSHSECTION_ASM_OP, SECTION)
+#endif
+
+#ifndef ASM_OUTPUT_POP_SECTION
+#define ASM_OUTPUT_POP_SECTION(FILE) \
+ fprintf ((FILE), "\t%s\n", POPSECTION_ASM_OP)
+#endif
+
+#ifndef ASM_OUTPUT_SOURCE_FILENAME
+#define ASM_OUTPUT_SOURCE_FILENAME(FILE,NAME) \
+ do { fprintf (FILE, "\t%s\t", FILE_ASM_OP); \
+ output_quoted_string (FILE, NAME); \
+ fputc ('\n', FILE); \
+ } while (0)
+#endif
+
+#ifndef ASM_OUTPUT_DWARF_DELTA2
+#define ASM_OUTPUT_DWARF_DELTA2(FILE,LABEL1,LABEL2) \
+ do { fprintf ((FILE), "\t%s\t", UNALIGNED_SHORT_ASM_OP); \
+ assemble_name (FILE, LABEL1); \
+ fprintf (FILE, "-"); \
+ assemble_name (FILE, LABEL2); \
+ fprintf (FILE, "\n"); \
+ } while (0)
+#endif
+
+#ifndef ASM_OUTPUT_DWARF_DELTA4
+#define ASM_OUTPUT_DWARF_DELTA4(FILE,LABEL1,LABEL2) \
+ do { fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \
+ assemble_name (FILE, LABEL1); \
+ fprintf (FILE, "-"); \
+ assemble_name (FILE, LABEL2); \
+ fprintf (FILE, "\n"); \
+ } while (0)
+#endif
+
+#ifndef ASM_OUTPUT_DWARF_TAG
+#define ASM_OUTPUT_DWARF_TAG(FILE,TAG) \
+ do { \
+ fprintf ((FILE), "\t%s\t0x%x", \
+ UNALIGNED_SHORT_ASM_OP, (unsigned) TAG); \
+ if (flag_verbose_asm) \
+ fprintf ((FILE), "\t%s %s", \
+ ASM_COMMENT_START, dwarf_tag_name (TAG)); \
+ fputc ('\n', (FILE)); \
+ } while (0)
+#endif
+
+#ifndef ASM_OUTPUT_DWARF_ATTRIBUTE
+#define ASM_OUTPUT_DWARF_ATTRIBUTE(FILE,ATTR) \
+ do { \
+ fprintf ((FILE), "\t%s\t0x%x", \
+ UNALIGNED_SHORT_ASM_OP, (unsigned) ATTR); \
+ if (flag_verbose_asm) \
+ fprintf ((FILE), "\t%s %s", \
+ ASM_COMMENT_START, dwarf_attr_name (ATTR)); \
+ fputc ('\n', (FILE)); \
+ } while (0)
+#endif
+
+#ifndef ASM_OUTPUT_DWARF_STACK_OP
+#define ASM_OUTPUT_DWARF_STACK_OP(FILE,OP) \
+ do { \
+ fprintf ((FILE), "\t%s\t0x%x", ASM_BYTE_OP, (unsigned) OP); \
+ if (flag_verbose_asm) \
+ fprintf ((FILE), "\t%s %s", \
+ ASM_COMMENT_START, dwarf_stack_op_name (OP)); \
+ fputc ('\n', (FILE)); \
+ } while (0)
+#endif
+
+#ifndef ASM_OUTPUT_DWARF_FUND_TYPE
+#define ASM_OUTPUT_DWARF_FUND_TYPE(FILE,FT) \
+ do { \
+ fprintf ((FILE), "\t%s\t0x%x", \
+ UNALIGNED_SHORT_ASM_OP, (unsigned) FT); \
+ if (flag_verbose_asm) \
+ fprintf ((FILE), "\t%s %s", \
+ ASM_COMMENT_START, dwarf_fund_type_name (FT)); \
+ fputc ('\n', (FILE)); \
+ } while (0)
+#endif
+
+#ifndef ASM_OUTPUT_DWARF_FMT_BYTE
+#define ASM_OUTPUT_DWARF_FMT_BYTE(FILE,FMT) \
+ do { \
+ fprintf ((FILE), "\t%s\t0x%x", ASM_BYTE_OP, (unsigned) FMT); \
+ if (flag_verbose_asm) \
+ fprintf ((FILE), "\t%s %s", \
+ ASM_COMMENT_START, dwarf_fmt_byte_name (FMT)); \
+ fputc ('\n', (FILE)); \
+ } while (0)
+#endif
+
+#ifndef ASM_OUTPUT_DWARF_TYPE_MODIFIER
+#define ASM_OUTPUT_DWARF_TYPE_MODIFIER(FILE,MOD) \
+ do { \
+ fprintf ((FILE), "\t%s\t0x%x", ASM_BYTE_OP, (unsigned) MOD); \
+ if (flag_verbose_asm) \
+ fprintf ((FILE), "\t%s %s", \
+ ASM_COMMENT_START, dwarf_typemod_name (MOD)); \
+ fputc ('\n', (FILE)); \
+ } while (0)
+#endif
+
+#ifndef ASM_OUTPUT_DWARF_ADDR
+#define ASM_OUTPUT_DWARF_ADDR(FILE,LABEL) \
+ do { fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \
+ assemble_name (FILE, LABEL); \
+ fprintf (FILE, "\n"); \
+ } while (0)
+#endif
+
+#ifndef ASM_OUTPUT_DWARF_ADDR_CONST
+#define ASM_OUTPUT_DWARF_ADDR_CONST(FILE,RTX) \
+ do { \
+ fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \
+ output_addr_const ((FILE), (RTX)); \
+ fputc ('\n', (FILE)); \
+ } while (0)
+#endif
+
+#ifndef ASM_OUTPUT_DWARF_REF
+#define ASM_OUTPUT_DWARF_REF(FILE,LABEL) \
+ do { fprintf ((FILE), "\t%s\t", UNALIGNED_INT_ASM_OP); \
+ assemble_name (FILE, LABEL); \
+ fprintf (FILE, "\n"); \
+ } while (0)
+#endif
+
+#ifndef ASM_OUTPUT_DWARF_DATA1
+#define ASM_OUTPUT_DWARF_DATA1(FILE,VALUE) \
+ fprintf ((FILE), "\t%s\t0x%x\n", ASM_BYTE_OP, VALUE)
+#endif
+
+#ifndef ASM_OUTPUT_DWARF_DATA2
+#define ASM_OUTPUT_DWARF_DATA2(FILE,VALUE) \
+ fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_SHORT_ASM_OP, (unsigned) VALUE)
+#endif
+
+#ifndef ASM_OUTPUT_DWARF_DATA4
+#define ASM_OUTPUT_DWARF_DATA4(FILE,VALUE) \
+ fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_INT_ASM_OP, (unsigned) VALUE)
+#endif
+
+#ifndef ASM_OUTPUT_DWARF_DATA8
+#define ASM_OUTPUT_DWARF_DATA8(FILE,HIGH_VALUE,LOW_VALUE) \
+ do { \
+ if (WORDS_BIG_ENDIAN) \
+ { \
+ fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_INT_ASM_OP, HIGH_VALUE); \
+ fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_INT_ASM_OP, LOW_VALUE);\
+ } \
+ else \
+ { \
+ fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_INT_ASM_OP, LOW_VALUE);\
+ fprintf ((FILE), "\t%s\t0x%x\n", UNALIGNED_INT_ASM_OP, HIGH_VALUE); \
+ } \
+ } while (0)
+#endif
+
+#ifndef ASM_OUTPUT_DWARF_STRING
+#define ASM_OUTPUT_DWARF_STRING(FILE,P) \
+ ASM_OUTPUT_ASCII ((FILE), P, strlen (P)+1)
+#endif
+
+/************************ general utility functions **************************/
+
+inline char *
+xstrdup (s)
+ register char *s;
+{
+ register char *p = (char *) xmalloc (strlen (s) + 1);
+
+ strcpy (p, s);
+ return p;
+}
+
+inline int
+is_pseudo_reg (rtl)
+ register rtx rtl;
+{
+ return (((GET_CODE (rtl) == REG) && (REGNO (rtl) >= FIRST_PSEUDO_REGISTER))
+ || ((GET_CODE (rtl) == SUBREG)
+ && (REGNO (XEXP (rtl, 0)) >= FIRST_PSEUDO_REGISTER)));
+}
+
+inline tree
+type_main_variant (type)
+ register tree type;
+{
+ type = TYPE_MAIN_VARIANT (type);
+
+ /* There really should be only one main variant among any group of variants
+ of a given type (and all of the MAIN_VARIANT values for all members of
+ the group should point to that one type) but sometimes the C front-end
+ messes this up for array types, so we work around that bug here. */
+
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ while (type != TYPE_MAIN_VARIANT (type))
+ type = TYPE_MAIN_VARIANT (type);
+ }
+
+ return type;
+}
+
+/* Return non-zero if the given type node represents a tagged type. */
+
+inline int
+is_tagged_type (type)
+ register tree type;
+{
+ register enum tree_code code = TREE_CODE (type);
+
+ return (code == RECORD_TYPE || code == UNION_TYPE
+ || code == QUAL_UNION_TYPE || code == ENUMERAL_TYPE);
+}
+
+static char *
+dwarf_tag_name (tag)
+ register unsigned tag;
+{
+ switch (tag)
+ {
+ case TAG_padding: return "TAG_padding";
+ case TAG_array_type: return "TAG_array_type";
+ case TAG_class_type: return "TAG_class_type";
+ case TAG_entry_point: return "TAG_entry_point";
+ case TAG_enumeration_type: return "TAG_enumeration_type";
+ case TAG_formal_parameter: return "TAG_formal_parameter";
+ case TAG_global_subroutine: return "TAG_global_subroutine";
+ case TAG_global_variable: return "TAG_global_variable";
+ case TAG_label: return "TAG_label";
+ case TAG_lexical_block: return "TAG_lexical_block";
+ case TAG_local_variable: return "TAG_local_variable";
+ case TAG_member: return "TAG_member";
+ case TAG_pointer_type: return "TAG_pointer_type";
+ case TAG_reference_type: return "TAG_reference_type";
+ case TAG_compile_unit: return "TAG_compile_unit";
+ case TAG_string_type: return "TAG_string_type";
+ case TAG_structure_type: return "TAG_structure_type";
+ case TAG_subroutine: return "TAG_subroutine";
+ case TAG_subroutine_type: return "TAG_subroutine_type";
+ case TAG_typedef: return "TAG_typedef";
+ case TAG_union_type: return "TAG_union_type";
+ case TAG_unspecified_parameters: return "TAG_unspecified_parameters";
+ case TAG_variant: return "TAG_variant";
+ case TAG_common_block: return "TAG_common_block";
+ case TAG_common_inclusion: return "TAG_common_inclusion";
+ case TAG_inheritance: return "TAG_inheritance";
+ case TAG_inlined_subroutine: return "TAG_inlined_subroutine";
+ case TAG_module: return "TAG_module";
+ case TAG_ptr_to_member_type: return "TAG_ptr_to_member_type";
+ case TAG_set_type: return "TAG_set_type";
+ case TAG_subrange_type: return "TAG_subrange_type";
+ case TAG_with_stmt: return "TAG_with_stmt";
+
+ /* GNU extensions. */
+
+ case TAG_format_label: return "TAG_format_label";
+ case TAG_namelist: return "TAG_namelist";
+ case TAG_function_template: return "TAG_function_template";
+ case TAG_class_template: return "TAG_class_template";
+
+ default: return "TAG_<unknown>";
+ }
+}
+
+static char *
+dwarf_attr_name (attr)
+ register unsigned attr;
+{
+ switch (attr)
+ {
+ case AT_sibling: return "AT_sibling";
+ case AT_location: return "AT_location";
+ case AT_name: return "AT_name";
+ case AT_fund_type: return "AT_fund_type";
+ case AT_mod_fund_type: return "AT_mod_fund_type";
+ case AT_user_def_type: return "AT_user_def_type";
+ case AT_mod_u_d_type: return "AT_mod_u_d_type";
+ case AT_ordering: return "AT_ordering";
+ case AT_subscr_data: return "AT_subscr_data";
+ case AT_byte_size: return "AT_byte_size";
+ case AT_bit_offset: return "AT_bit_offset";
+ case AT_bit_size: return "AT_bit_size";
+ case AT_element_list: return "AT_element_list";
+ case AT_stmt_list: return "AT_stmt_list";
+ case AT_low_pc: return "AT_low_pc";
+ case AT_high_pc: return "AT_high_pc";
+ case AT_language: return "AT_language";
+ case AT_member: return "AT_member";
+ case AT_discr: return "AT_discr";
+ case AT_discr_value: return "AT_discr_value";
+ case AT_string_length: return "AT_string_length";
+ case AT_common_reference: return "AT_common_reference";
+ case AT_comp_dir: return "AT_comp_dir";
+ case AT_const_value_string: return "AT_const_value_string";
+ case AT_const_value_data2: return "AT_const_value_data2";
+ case AT_const_value_data4: return "AT_const_value_data4";
+ case AT_const_value_data8: return "AT_const_value_data8";
+ case AT_const_value_block2: return "AT_const_value_block2";
+ case AT_const_value_block4: return "AT_const_value_block4";
+ case AT_containing_type: return "AT_containing_type";
+ case AT_default_value_addr: return "AT_default_value_addr";
+ case AT_default_value_data2: return "AT_default_value_data2";
+ case AT_default_value_data4: return "AT_default_value_data4";
+ case AT_default_value_data8: return "AT_default_value_data8";
+ case AT_default_value_string: return "AT_default_value_string";
+ case AT_friends: return "AT_friends";
+ case AT_inline: return "AT_inline";
+ case AT_is_optional: return "AT_is_optional";
+ case AT_lower_bound_ref: return "AT_lower_bound_ref";
+ case AT_lower_bound_data2: return "AT_lower_bound_data2";
+ case AT_lower_bound_data4: return "AT_lower_bound_data4";
+ case AT_lower_bound_data8: return "AT_lower_bound_data8";
+ case AT_private: return "AT_private";
+ case AT_producer: return "AT_producer";
+ case AT_program: return "AT_program";
+ case AT_protected: return "AT_protected";
+ case AT_prototyped: return "AT_prototyped";
+ case AT_public: return "AT_public";
+ case AT_pure_virtual: return "AT_pure_virtual";
+ case AT_return_addr: return "AT_return_addr";
+ case AT_abstract_origin: return "AT_abstract_origin";
+ case AT_start_scope: return "AT_start_scope";
+ case AT_stride_size: return "AT_stride_size";
+ case AT_upper_bound_ref: return "AT_upper_bound_ref";
+ case AT_upper_bound_data2: return "AT_upper_bound_data2";
+ case AT_upper_bound_data4: return "AT_upper_bound_data4";
+ case AT_upper_bound_data8: return "AT_upper_bound_data8";
+ case AT_virtual: return "AT_virtual";
+
+ /* GNU extensions */
+
+ case AT_sf_names: return "AT_sf_names";
+ case AT_src_info: return "AT_src_info";
+ case AT_mac_info: return "AT_mac_info";
+ case AT_src_coords: return "AT_src_coords";
+ case AT_body_begin: return "AT_body_begin";
+ case AT_body_end: return "AT_body_end";
+
+ default: return "AT_<unknown>";
+ }
+}
+
+static char *
+dwarf_stack_op_name (op)
+ register unsigned op;
+{
+ switch (op)
+ {
+ case OP_REG: return "OP_REG";
+ case OP_BASEREG: return "OP_BASEREG";
+ case OP_ADDR: return "OP_ADDR";
+ case OP_CONST: return "OP_CONST";
+ case OP_DEREF2: return "OP_DEREF2";
+ case OP_DEREF4: return "OP_DEREF4";
+ case OP_ADD: return "OP_ADD";
+ default: return "OP_<unknown>";
+ }
+}
+
+static char *
+dwarf_typemod_name (mod)
+ register unsigned mod;
+{
+ switch (mod)
+ {
+ case MOD_pointer_to: return "MOD_pointer_to";
+ case MOD_reference_to: return "MOD_reference_to";
+ case MOD_const: return "MOD_const";
+ case MOD_volatile: return "MOD_volatile";
+ default: return "MOD_<unknown>";
+ }
+}
+
+static char *
+dwarf_fmt_byte_name (fmt)
+ register unsigned fmt;
+{
+ switch (fmt)
+ {
+ case FMT_FT_C_C: return "FMT_FT_C_C";
+ case FMT_FT_C_X: return "FMT_FT_C_X";
+ case FMT_FT_X_C: return "FMT_FT_X_C";
+ case FMT_FT_X_X: return "FMT_FT_X_X";
+ case FMT_UT_C_C: return "FMT_UT_C_C";
+ case FMT_UT_C_X: return "FMT_UT_C_X";
+ case FMT_UT_X_C: return "FMT_UT_X_C";
+ case FMT_UT_X_X: return "FMT_UT_X_X";
+ case FMT_ET: return "FMT_ET";
+ default: return "FMT_<unknown>";
+ }
+}
+static char *
+dwarf_fund_type_name (ft)
+ register unsigned ft;
+{
+ switch (ft)
+ {
+ case FT_char: return "FT_char";
+ case FT_signed_char: return "FT_signed_char";
+ case FT_unsigned_char: return "FT_unsigned_char";
+ case FT_short: return "FT_short";
+ case FT_signed_short: return "FT_signed_short";
+ case FT_unsigned_short: return "FT_unsigned_short";
+ case FT_integer: return "FT_integer";
+ case FT_signed_integer: return "FT_signed_integer";
+ case FT_unsigned_integer: return "FT_unsigned_integer";
+ case FT_long: return "FT_long";
+ case FT_signed_long: return "FT_signed_long";
+ case FT_unsigned_long: return "FT_unsigned_long";
+ case FT_pointer: return "FT_pointer";
+ case FT_float: return "FT_float";
+ case FT_dbl_prec_float: return "FT_dbl_prec_float";
+ case FT_ext_prec_float: return "FT_ext_prec_float";
+ case FT_complex: return "FT_complex";
+ case FT_dbl_prec_complex: return "FT_dbl_prec_complex";
+ case FT_void: return "FT_void";
+ case FT_boolean: return "FT_boolean";
+ case FT_ext_prec_complex: return "FT_ext_prec_complex";
+ case FT_label: return "FT_label";
+
+ /* GNU extensions. */
+
+ case FT_long_long: return "FT_long_long";
+ case FT_signed_long_long: return "FT_signed_long_long";
+ case FT_unsigned_long_long: return "FT_unsigned_long_long";
+
+ case FT_int8: return "FT_int8";
+ case FT_signed_int8: return "FT_signed_int8";
+ case FT_unsigned_int8: return "FT_unsigned_int8";
+ case FT_int16: return "FT_int16";
+ case FT_signed_int16: return "FT_signed_int16";
+ case FT_unsigned_int16: return "FT_unsigned_int16";
+ case FT_int32: return "FT_int32";
+ case FT_signed_int32: return "FT_signed_int32";
+ case FT_unsigned_int32: return "FT_unsigned_int32";
+ case FT_int64: return "FT_int64";
+ case FT_signed_int64: return "FT_signed_int64";
+ case FT_unsigned_int64: return "FT_signed_int64";
+
+ case FT_real32: return "FT_real32";
+ case FT_real64: return "FT_real64";
+ case FT_real96: return "FT_real96";
+ case FT_real128: return "FT_real128";
+
+ default: return "FT_<unknown>";
+ }
+}
+
+/* Determine the "ultimate origin" of a decl. The decl may be an
+ inlined instance of an inlined instance of a decl which is local
+ to an inline function, so we have to trace all of the way back
+ through the origin chain to find out what sort of node actually
+ served as the original seed for the given block. */
+
+static tree
+decl_ultimate_origin (decl)
+ register tree decl;
+{
+ register tree immediate_origin = DECL_ABSTRACT_ORIGIN (decl);
+
+ if (immediate_origin == NULL)
+ return NULL;
+ else
+ {
+ register tree ret_val;
+ register tree lookahead = immediate_origin;
+
+ do
+ {
+ ret_val = lookahead;
+ lookahead = DECL_ABSTRACT_ORIGIN (ret_val);
+ }
+ while (lookahead != NULL && lookahead != ret_val);
+ return ret_val;
+ }
+}
+
+/* Determine the "ultimate origin" of a block. The block may be an
+ inlined instance of an inlined instance of a block which is local
+ to an inline function, so we have to trace all of the way back
+ through the origin chain to find out what sort of node actually
+ served as the original seed for the given block. */
+
+static tree
+block_ultimate_origin (block)
+ register tree block;
+{
+ register tree immediate_origin = BLOCK_ABSTRACT_ORIGIN (block);
+
+ if (immediate_origin == NULL)
+ return NULL;
+ else
+ {
+ register tree ret_val;
+ register tree lookahead = immediate_origin;
+
+ do
+ {
+ ret_val = lookahead;
+ lookahead = (TREE_CODE (ret_val) == BLOCK)
+ ? BLOCK_ABSTRACT_ORIGIN (ret_val)
+ : NULL;
+ }
+ while (lookahead != NULL && lookahead != ret_val);
+ return ret_val;
+ }
+}
+
+static void
+output_unsigned_leb128 (value)
+ register unsigned long value;
+{
+ register unsigned long orig_value = value;
+
+ do
+ {
+ register unsigned byte = (value & 0x7f);
+
+ value >>= 7;
+ if (value != 0) /* more bytes to follow */
+ byte |= 0x80;
+ fprintf (asm_out_file, "\t%s\t0x%x", ASM_BYTE_OP, (unsigned) byte);
+ if (flag_verbose_asm && value == 0)
+ fprintf (asm_out_file, "\t%s ULEB128 number - value = %u",
+ ASM_COMMENT_START, orig_value);
+ fputc ('\n', asm_out_file);
+ }
+ while (value != 0);
+}
+
+static void
+output_signed_leb128 (value)
+ register long value;
+{
+ register long orig_value = value;
+ register int negative = (value < 0);
+ register int more;
+
+ do
+ {
+ register unsigned byte = (value & 0x7f);
+
+ value >>= 7;
+ if (negative)
+ value |= 0xfe000000; /* manually sign extend */
+ if (((value == 0) && ((byte & 0x40) == 0))
+ || ((value == -1) && ((byte & 0x40) == 1)))
+ more = 0;
+ else
+ {
+ byte |= 0x80;
+ more = 1;
+ }
+ fprintf (asm_out_file, "\t%s\t0x%x", ASM_BYTE_OP, (unsigned) byte);
+ if (flag_verbose_asm && more == 0)
+ fprintf (asm_out_file, "\t%s SLEB128 number - value = %d",
+ ASM_COMMENT_START, orig_value);
+ fputc ('\n', asm_out_file);
+ }
+ while (more);
+}
+
+/**************** utility functions for attribute functions ******************/
+
+/* Given a pointer to a BLOCK node return non-zero if (and only if) the
+ node in question represents the outermost pair of curly braces (i.e.
+ the "body block") of a function or method.
+
+ For any BLOCK node representing a "body block" of a function or method,
+ the BLOCK_SUPERCONTEXT of the node will point to another BLOCK node
+ which represents the outermost (function) scope for the function or
+ method (i.e. the one which includes the formal parameters). The
+ BLOCK_SUPERCONTEXT of *that* node in turn will point to the relevant
+ FUNCTION_DECL node.
+*/
+
+inline int
+is_body_block (stmt)
+ register tree stmt;
+{
+ if (TREE_CODE (stmt) == BLOCK)
+ {
+ register tree parent = BLOCK_SUPERCONTEXT (stmt);
+
+ if (TREE_CODE (parent) == BLOCK)
+ {
+ register tree grandparent = BLOCK_SUPERCONTEXT (parent);
+
+ if (TREE_CODE (grandparent) == FUNCTION_DECL)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Given a pointer to a tree node for some type, return a Dwarf fundamental
+ type code for the given type.
+
+ This routine must only be called for GCC type nodes that correspond to
+ Dwarf fundamental types.
+
+ The current Dwarf draft specification calls for Dwarf fundamental types
+ to accurately reflect the fact that a given type was either a "plain"
+ integral type or an explicitly "signed" integral type. Unfortunately,
+ we can't always do this, because GCC may already have thrown away the
+ information about the precise way in which the type was originally
+ specified, as in:
+
+ typedef signed int my_type;
+
+ struct s { my_type f; };
+
+ Since we may be stuck here without enought information to do exactly
+ what is called for in the Dwarf draft specification, we do the best
+ that we can under the circumstances and always use the "plain" integral
+ fundamental type codes for int, short, and long types. That's probably
+ good enough. The additional accuracy called for in the current DWARF
+ draft specification is probably never even useful in practice. */
+
+static int
+fundamental_type_code (type)
+ register tree type;
+{
+ if (TREE_CODE (type) == ERROR_MARK)
+ return 0;
+
+ switch (TREE_CODE (type))
+ {
+ case ERROR_MARK:
+ return FT_void;
+
+ case VOID_TYPE:
+ return FT_void;
+
+ case INTEGER_TYPE:
+ /* Carefully distinguish all the standard types of C,
+ without messing up if the language is not C.
+ Note that we check only for the names that contain spaces;
+ other names might occur by coincidence in other languages. */
+ if (TYPE_NAME (type) != 0
+ && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
+ && DECL_NAME (TYPE_NAME (type)) != 0
+ && TREE_CODE (DECL_NAME (TYPE_NAME (type))) == IDENTIFIER_NODE)
+ {
+ char *name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)));
+
+ if (!strcmp (name, "unsigned char"))
+ return FT_unsigned_char;
+ if (!strcmp (name, "signed char"))
+ return FT_signed_char;
+ if (!strcmp (name, "unsigned int"))
+ return FT_unsigned_integer;
+ if (!strcmp (name, "short int"))
+ return FT_short;
+ if (!strcmp (name, "short unsigned int"))
+ return FT_unsigned_short;
+ if (!strcmp (name, "long int"))
+ return FT_long;
+ if (!strcmp (name, "long unsigned int"))
+ return FT_unsigned_long;
+ if (!strcmp (name, "long long int"))
+ return FT_long_long; /* Not grok'ed by svr4 SDB */
+ if (!strcmp (name, "long long unsigned int"))
+ return FT_unsigned_long_long; /* Not grok'ed by svr4 SDB */
+ }
+
+ /* Most integer types will be sorted out above, however, for the
+ sake of special `array index' integer types, the following code
+ is also provided. */
+
+ if (TYPE_PRECISION (type) == INT_TYPE_SIZE)
+ return (TREE_UNSIGNED (type) ? FT_unsigned_integer : FT_integer);
+
+ if (TYPE_PRECISION (type) == LONG_TYPE_SIZE)
+ return (TREE_UNSIGNED (type) ? FT_unsigned_long : FT_long);
+
+ if (TYPE_PRECISION (type) == LONG_LONG_TYPE_SIZE)
+ return (TREE_UNSIGNED (type) ? FT_unsigned_long_long : FT_long_long);
+
+ if (TYPE_PRECISION (type) == SHORT_TYPE_SIZE)
+ return (TREE_UNSIGNED (type) ? FT_unsigned_short : FT_short);
+
+ if (TYPE_PRECISION (type) == CHAR_TYPE_SIZE)
+ return (TREE_UNSIGNED (type) ? FT_unsigned_char : FT_char);
+
+ abort ();
+
+ case REAL_TYPE:
+ /* Carefully distinguish all the standard types of C,
+ without messing up if the language is not C. */
+ if (TYPE_NAME (type) != 0
+ && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
+ && DECL_NAME (TYPE_NAME (type)) != 0
+ && TREE_CODE (DECL_NAME (TYPE_NAME (type))) == IDENTIFIER_NODE)
+ {
+ char *name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)));
+
+ /* Note that here we can run afowl of a serious bug in "classic"
+ svr4 SDB debuggers. They don't seem to understand the
+ FT_ext_prec_float type (even though they should). */
+
+ if (!strcmp (name, "long double"))
+ return FT_ext_prec_float;
+ }
+
+ if (TYPE_PRECISION (type) == DOUBLE_TYPE_SIZE)
+ return FT_dbl_prec_float;
+ if (TYPE_PRECISION (type) == FLOAT_TYPE_SIZE)
+ return FT_float;
+
+ /* Note that here we can run afowl of a serious bug in "classic"
+ svr4 SDB debuggers. They don't seem to understand the
+ FT_ext_prec_float type (even though they should). */
+
+ if (TYPE_PRECISION (type) == LONG_DOUBLE_TYPE_SIZE)
+ return FT_ext_prec_float;
+ abort ();
+
+ case COMPLEX_TYPE:
+ return FT_complex; /* GNU FORTRAN COMPLEX type. */
+
+ case CHAR_TYPE:
+ return FT_char; /* GNU Pascal CHAR type. Not used in C. */
+
+ case BOOLEAN_TYPE:
+ return FT_boolean; /* GNU FORTRAN BOOLEAN type. */
+
+ default:
+ abort (); /* No other TREE_CODEs are Dwarf fundamental types. */
+ }
+ return 0;
+}
+
+/* Given a pointer to an arbitrary ..._TYPE tree node, return a pointer to
+ the Dwarf "root" type for the given input type. The Dwarf "root" type
+ of a given type is generally the same as the given type, except that if
+ the given type is a pointer or reference type, then the root type of
+ the given type is the root type of the "basis" type for the pointer or
+ reference type. (This definition of the "root" type is recursive.)
+ Also, the root type of a `const' qualified type or a `volatile'
+ qualified type is the root type of the given type without the
+ qualifiers. */
+
+static tree
+root_type (type)
+ register tree type;
+{
+ if (TREE_CODE (type) == ERROR_MARK)
+ return error_mark_node;
+
+ switch (TREE_CODE (type))
+ {
+ case ERROR_MARK:
+ return error_mark_node;
+
+ case POINTER_TYPE:
+ case REFERENCE_TYPE:
+ return type_main_variant (root_type (TREE_TYPE (type)));
+
+ default:
+ return type_main_variant (type);
+ }
+}
+
+/* Given a pointer to an arbitrary ..._TYPE tree node, write out a sequence
+ of zero or more Dwarf "type-modifier" bytes applicable to the type. */
+
+static void
+write_modifier_bytes (type, decl_const, decl_volatile)
+ register tree type;
+ register int decl_const;
+ register int decl_volatile;
+{
+ if (TREE_CODE (type) == ERROR_MARK)
+ return;
+
+ if (TYPE_READONLY (type) || decl_const)
+ ASM_OUTPUT_DWARF_TYPE_MODIFIER (asm_out_file, MOD_const);
+ if (TYPE_VOLATILE (type) || decl_volatile)
+ ASM_OUTPUT_DWARF_TYPE_MODIFIER (asm_out_file, MOD_volatile);
+ switch (TREE_CODE (type))
+ {
+ case POINTER_TYPE:
+ ASM_OUTPUT_DWARF_TYPE_MODIFIER (asm_out_file, MOD_pointer_to);
+ write_modifier_bytes (TREE_TYPE (type), 0, 0);
+ return;
+
+ case REFERENCE_TYPE:
+ ASM_OUTPUT_DWARF_TYPE_MODIFIER (asm_out_file, MOD_reference_to);
+ write_modifier_bytes (TREE_TYPE (type), 0, 0);
+ return;
+
+ case ERROR_MARK:
+ default:
+ return;
+ }
+}
+
+/* Given a pointer to an arbitrary ..._TYPE tree node, return non-zero if the
+ given input type is a Dwarf "fundamental" type. Otherwise return zero. */
+
+inline int
+type_is_fundamental (type)
+ register tree type;
+{
+ switch (TREE_CODE (type))
+ {
+ case ERROR_MARK:
+ case VOID_TYPE:
+ case INTEGER_TYPE:
+ case REAL_TYPE:
+ case COMPLEX_TYPE:
+ case BOOLEAN_TYPE:
+ case CHAR_TYPE:
+ return 1;
+
+ case SET_TYPE:
+ case ARRAY_TYPE:
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ case QUAL_UNION_TYPE:
+ case ENUMERAL_TYPE:
+ case FUNCTION_TYPE:
+ case METHOD_TYPE:
+ case POINTER_TYPE:
+ case REFERENCE_TYPE:
+ case FILE_TYPE:
+ case OFFSET_TYPE:
+ case LANG_TYPE:
+ return 0;
+
+ default:
+ abort ();
+ }
+ return 0;
+}
+
+/* Given a pointer to some ..._DECL tree node, generate an assembly language
+ equate directive which will associate a symbolic name with the current DIE.
+
+ The name used is an artificial label generated from the DECL_UID number
+ associated with the given decl node. The name it gets equated to is the
+ symbolic label that we (previously) output at the start of the DIE that
+ we are currently generating.
+
+ Calling this function while generating some "decl related" form of DIE
+ makes it possible to later refer to the DIE which represents the given
+ decl simply by re-generating the symbolic name from the ..._DECL node's
+ UID number. */
+
+static void
+equate_decl_number_to_die_number (decl)
+ register tree decl;
+{
+ /* In the case where we are generating a DIE for some ..._DECL node
+ which represents either some inline function declaration or some
+ entity declared within an inline function declaration/definition,
+ setup a symbolic name for the current DIE so that we have a name
+ for this DIE that we can easily refer to later on within
+ AT_abstract_origin attributes. */
+
+ char decl_label[MAX_ARTIFICIAL_LABEL_BYTES];
+ char die_label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ sprintf (decl_label, DECL_NAME_FMT, DECL_UID (decl));
+ sprintf (die_label, DIE_BEGIN_LABEL_FMT, current_dienum);
+ ASM_OUTPUT_DEF (asm_out_file, decl_label, die_label);
+}
+
+/* Given a pointer to some ..._TYPE tree node, generate an assembly language
+ equate directive which will associate a symbolic name with the current DIE.
+
+ The name used is an artificial label generated from the TYPE_UID number
+ associated with the given type node. The name it gets equated to is the
+ symbolic label that we (previously) output at the start of the DIE that
+ we are currently generating.
+
+ Calling this function while generating some "type related" form of DIE
+ makes it easy to later refer to the DIE which represents the given type
+ simply by re-generating the alternative name from the ..._TYPE node's
+ UID number. */
+
+inline void
+equate_type_number_to_die_number (type)
+ register tree type;
+{
+ char type_label[MAX_ARTIFICIAL_LABEL_BYTES];
+ char die_label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ /* We are generating a DIE to represent the main variant of this type
+ (i.e the type without any const or volatile qualifiers) so in order
+ to get the equate to come out right, we need to get the main variant
+ itself here. */
+
+ type = type_main_variant (type);
+
+ sprintf (type_label, TYPE_NAME_FMT, TYPE_UID (type));
+ sprintf (die_label, DIE_BEGIN_LABEL_FMT, current_dienum);
+ ASM_OUTPUT_DEF (asm_out_file, type_label, die_label);
+}
+
+static void
+output_reg_number (rtl)
+ register rtx rtl;
+{
+ register unsigned regno = REGNO (rtl);
+
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ {
+ warning_with_decl (dwarf_last_decl, "internal regno botch: regno = %d\n",
+ regno);
+ regno = 0;
+ }
+ fprintf (asm_out_file, "\t%s\t0x%x",
+ UNALIGNED_INT_ASM_OP, DBX_REGISTER_NUMBER (regno));
+ if (flag_verbose_asm)
+ {
+ fprintf (asm_out_file, "\t%s ", ASM_COMMENT_START);
+ PRINT_REG (rtl, 0, asm_out_file);
+ }
+ fputc ('\n', asm_out_file);
+}
+
+/* The following routine is a nice and simple transducer. It converts the
+ RTL for a variable or parameter (resident in memory) into an equivalent
+ Dwarf representation of a mechanism for getting the address of that same
+ variable onto the top of a hypothetical "address evaluation" stack.
+
+ When creating memory location descriptors, we are effectively trans-
+ forming the RTL for a memory-resident object into its Dwarf postfix
+ expression equivalent. This routine just recursively descends an
+ RTL tree, turning it into Dwarf postfix code as it goes. */
+
+static void
+output_mem_loc_descriptor (rtl)
+ register rtx rtl;
+{
+ /* Note that for a dynamically sized array, the location we will
+ generate a description of here will be the lowest numbered location
+ which is actually within the array. That's *not* necessarily the
+ same as the zeroth element of the array. */
+
+ switch (GET_CODE (rtl))
+ {
+ case SUBREG:
+
+ /* The case of a subreg may arise when we have a local (register)
+ variable or a formal (register) parameter which doesn't quite
+ fill up an entire register. For now, just assume that it is
+ legitimate to make the Dwarf info refer to the whole register
+ which contains the given subreg. */
+
+ rtl = XEXP (rtl, 0);
+ /* Drop thru. */
+
+ case REG:
+
+ /* Whenever a register number forms a part of the description of
+ the method for calculating the (dynamic) address of a memory
+ resident object, DWARF rules require the register number to
+ be referred to as a "base register". This distinction is not
+ based in any way upon what category of register the hardware
+ believes the given register belongs to. This is strictly
+ DWARF terminology we're dealing with here.
+
+ Note that in cases where the location of a memory-resident data
+ object could be expressed as:
+
+ OP_ADD (OP_BASEREG (basereg), OP_CONST (0))
+
+ the actual DWARF location descriptor that we generate may just
+ be OP_BASEREG (basereg). This may look deceptively like the
+ object in question was allocated to a register (rather than
+ in memory) so DWARF consumers need to be aware of the subtle
+ distinction between OP_REG and OP_BASEREG. */
+
+ ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_BASEREG);
+ output_reg_number (rtl);
+ break;
+
+ case MEM:
+ output_mem_loc_descriptor (XEXP (rtl, 0));
+ ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_DEREF4);
+ break;
+
+ case CONST:
+ case SYMBOL_REF:
+ ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_ADDR);
+ ASM_OUTPUT_DWARF_ADDR_CONST (asm_out_file, rtl);
+ break;
+
+ case PLUS:
+ output_mem_loc_descriptor (XEXP (rtl, 0));
+ output_mem_loc_descriptor (XEXP (rtl, 1));
+ ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_ADD);
+ break;
+
+ case CONST_INT:
+ ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_CONST);
+ ASM_OUTPUT_DWARF_DATA4 (asm_out_file, INTVAL (rtl));
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+/* Output a proper Dwarf location descriptor for a variable or parameter
+ which is either allocated in a register or in a memory location. For
+ a register, we just generate an OP_REG and the register number. For a
+ memory location we provide a Dwarf postfix expression describing how to
+ generate the (dynamic) address of the object onto the address stack. */
+
+static void
+output_loc_descriptor (rtl)
+ register rtx rtl;
+{
+ switch (GET_CODE (rtl))
+ {
+ case SUBREG:
+
+ /* The case of a subreg may arise when we have a local (register)
+ variable or a formal (register) parameter which doesn't quite
+ fill up an entire register. For now, just assume that it is
+ legitimate to make the Dwarf info refer to the whole register
+ which contains the given subreg. */
+
+ rtl = XEXP (rtl, 0);
+ /* Drop thru. */
+
+ case REG:
+ ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_REG);
+ output_reg_number (rtl);
+ break;
+
+ case MEM:
+ output_mem_loc_descriptor (XEXP (rtl, 0));
+ break;
+
+ default:
+ abort (); /* Should never happen */
+ }
+}
+
+/* Given a tree node describing an array bound (either lower or upper)
+ output a representation for that bound. */
+
+static void
+output_bound_representation (bound, dim_num, u_or_l)
+ register tree bound;
+ register unsigned dim_num; /* For multi-dimensional arrays. */
+ register char u_or_l; /* Designates upper or lower bound. */
+{
+ switch (TREE_CODE (bound))
+ {
+
+ case ERROR_MARK:
+ return;
+
+ /* All fixed-bounds are represented by INTEGER_CST nodes. */
+
+ case INTEGER_CST:
+ ASM_OUTPUT_DWARF_DATA4 (asm_out_file,
+ (unsigned) TREE_INT_CST_LOW (bound));
+ break;
+
+ /* Dynamic bounds may be represented by NOP_EXPR nodes containing
+ SAVE_EXPR nodes. */
+
+ case NOP_EXPR:
+ bound = TREE_OPERAND (bound, 0);
+ /* ... fall thru... */
+
+ case SAVE_EXPR:
+ {
+ char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
+ char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ sprintf (begin_label, BOUND_BEGIN_LABEL_FMT,
+ current_dienum, dim_num, u_or_l);
+
+ sprintf (end_label, BOUND_END_LABEL_FMT,
+ current_dienum, dim_num, u_or_l);
+
+ ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label);
+ ASM_OUTPUT_LABEL (asm_out_file, begin_label);
+
+ /* If we are working on a bound for a dynamic dimension in C,
+ the dynamic dimension in question had better have a static
+ (zero) lower bound and a dynamic *upper* bound. */
+
+ if (u_or_l != 'u')
+ abort ();
+
+ /* If optimization is turned on, the SAVE_EXPRs that describe
+ how to access the upper bound values are essentially bogus.
+ They only describe (at best) how to get at these values at
+ the points in the generated code right after they have just
+ been computed. Worse yet, in the typical case, the upper
+ bound values will not even *be* computed in the optimized
+ code, so these SAVE_EXPRs are entirely bogus.
+
+ In order to compensate for this fact, we check here to see
+ if optimization is enabled, and if so, we effectively create
+ an empty location description for the (unknown and unknowable)
+ upper bound.
+
+ This should not cause too much trouble for existing (stupid?)
+ debuggers because they have to deal with empty upper bounds
+ location descriptions anyway in order to be able to deal with
+ incomplete array types.
+
+ Of course an intelligent debugger (GDB?) should be able to
+ comprehend that a missing upper bound specification in a
+ array type used for a storage class `auto' local array variable
+ indicates that the upper bound is both unknown (at compile-
+ time) and unknowable (at run-time) due to optimization.
+ */
+
+ if (! optimize)
+ output_loc_descriptor
+ (eliminate_regs (SAVE_EXPR_RTL (bound), 0, NULL_RTX));
+
+ ASM_OUTPUT_LABEL (asm_out_file, end_label);
+ }
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+/* Recursive function to output a sequence of value/name pairs for
+ enumeration constants in reversed order. This is called from
+ enumeration_type_die. */
+
+static void
+output_enumeral_list (link)
+ register tree link;
+{
+ if (link)
+ {
+ output_enumeral_list (TREE_CHAIN (link));
+ ASM_OUTPUT_DWARF_DATA4 (asm_out_file,
+ (unsigned) TREE_INT_CST_LOW (TREE_VALUE (link)));
+ ASM_OUTPUT_DWARF_STRING (asm_out_file,
+ IDENTIFIER_POINTER (TREE_PURPOSE (link)));
+ }
+}
+
+/* Given an unsigned value, round it up to the lowest multiple of `boundary'
+ which is not less than the value itself. */
+
+inline unsigned
+ceiling (value, boundary)
+ register unsigned value;
+ register unsigned boundary;
+{
+ return (((value + boundary - 1) / boundary) * boundary);
+}
+
+/* Given a pointer to what is assumed to be a FIELD_DECL node, return a
+ pointer to the declared type for the relevant field variable, or return
+ `integer_type_node' if the given node turns out to be an ERROR_MARK node. */
+
+inline tree
+field_type (decl)
+ register tree decl;
+{
+ register tree type;
+
+ if (TREE_CODE (decl) == ERROR_MARK)
+ return integer_type_node;
+
+ type = DECL_BIT_FIELD_TYPE (decl);
+ if (type == NULL)
+ type = TREE_TYPE (decl);
+ return type;
+}
+
+/* Given a pointer to a tree node, assumed to be some kind of a ..._TYPE
+ node, return the alignment in bits for the type, or else return
+ BITS_PER_WORD if the node actually turns out to be an ERROR_MARK node. */
+
+inline unsigned
+simple_type_align_in_bits (type)
+ register tree type;
+{
+ return (TREE_CODE (type) != ERROR_MARK) ? TYPE_ALIGN (type) : BITS_PER_WORD;
+}
+
+/* Given a pointer to a tree node, assumed to be some kind of a ..._TYPE
+ node, return the size in bits for the type if it is a constant, or
+ else return the alignment for the type if the type's size is not
+ constant, or else return BITS_PER_WORD if the type actually turns out
+ to be an ERROR_MARK node. */
+
+inline unsigned
+simple_type_size_in_bits (type)
+ register tree type;
+{
+ if (TREE_CODE (type) == ERROR_MARK)
+ return BITS_PER_WORD;
+ else
+ {
+ register tree type_size_tree = TYPE_SIZE (type);
+
+ if (TREE_CODE (type_size_tree) != INTEGER_CST)
+ return TYPE_ALIGN (type);
+
+ return (unsigned) TREE_INT_CST_LOW (type_size_tree);
+ }
+}
+
+/* Given a pointer to what is assumed to be a FIELD_DECL node, compute and
+ return the byte offset of the lowest addressed byte of the "containing
+ object" for the given FIELD_DECL, or return 0 if we are unable to deter-
+ mine what that offset is, either because the argument turns out to be a
+ pointer to an ERROR_MARK node, or because the offset is actually variable.
+ (We can't handle the latter case just yet.) */
+
+static unsigned
+field_byte_offset (decl)
+ register tree decl;
+{
+ register unsigned type_align_in_bytes;
+ register unsigned type_align_in_bits;
+ register unsigned type_size_in_bits;
+ register unsigned object_offset_in_align_units;
+ register unsigned object_offset_in_bits;
+ register unsigned object_offset_in_bytes;
+ register tree type;
+ register tree bitpos_tree;
+ register tree field_size_tree;
+ register unsigned bitpos_int;
+ register unsigned deepest_bitpos;
+ register unsigned field_size_in_bits;
+
+ if (TREE_CODE (decl) == ERROR_MARK)
+ return 0;
+
+ if (TREE_CODE (decl) != FIELD_DECL)
+ abort ();
+
+ type = field_type (decl);
+
+ bitpos_tree = DECL_FIELD_BITPOS (decl);
+ field_size_tree = DECL_SIZE (decl);
+
+ /* We cannot yet cope with fields whose positions or sizes are variable,
+ so for now, when we see such things, we simply return 0. Someday,
+ we may be able to handle such cases, but it will be damn difficult. */
+
+ if (TREE_CODE (bitpos_tree) != INTEGER_CST)
+ return 0;
+ bitpos_int = (unsigned) TREE_INT_CST_LOW (bitpos_tree);
+
+ if (TREE_CODE (field_size_tree) != INTEGER_CST)
+ return 0;
+ field_size_in_bits = (unsigned) TREE_INT_CST_LOW (field_size_tree);
+
+ type_size_in_bits = simple_type_size_in_bits (type);
+
+ type_align_in_bits = simple_type_align_in_bits (type);
+ type_align_in_bytes = type_align_in_bits / BITS_PER_UNIT;
+
+ /* Note that the GCC front-end doesn't make any attempt to keep track
+ of the starting bit offset (relative to the start of the containing
+ structure type) of the hypothetical "containing object" for a bit-
+ field. Thus, when computing the byte offset value for the start of
+ the "containing object" of a bit-field, we must deduce this infor-
+ mation on our own.
+
+ This can be rather tricky to do in some cases. For example, handling
+ the following structure type definition when compiling for an i386/i486
+ target (which only aligns long long's to 32-bit boundaries) can be very
+ tricky:
+
+ struct S {
+ int field1;
+ long long field2:31;
+ };
+
+ Fortunately, there is a simple rule-of-thumb which can be used in such
+ cases. When compiling for an i386/i486, GCC will allocate 8 bytes for
+ the structure shown above. It decides to do this based upon one simple
+ rule for bit-field allocation. Quite simply, GCC allocates each "con-
+ taining object" for each bit-field at the first (i.e. lowest addressed)
+ legitimate alignment boundary (based upon the required minimum alignment
+ for the declared type of the field) which it can possibly use, subject
+ to the condition that there is still enough available space remaining
+ in the containing object (when allocated at the selected point) to
+ fully accommodate all of the bits of the bit-field itself.
+
+ This simple rule makes it obvious why GCC allocates 8 bytes for each
+ object of the structure type shown above. When looking for a place to
+ allocate the "containing object" for `field2', the compiler simply tries
+ to allocate a 64-bit "containing object" at each successive 32-bit
+ boundary (starting at zero) until it finds a place to allocate that 64-
+ bit field such that at least 31 contiguous (and previously unallocated)
+ bits remain within that selected 64 bit field. (As it turns out, for
+ the example above, the compiler finds that it is OK to allocate the
+ "containing object" 64-bit field at bit-offset zero within the
+ structure type.)
+
+ Here we attempt to work backwards from the limited set of facts we're
+ given, and we try to deduce from those facts, where GCC must have
+ believed that the containing object started (within the structure type).
+
+ The value we deduce is then used (by the callers of this routine) to
+ generate AT_location and AT_bit_offset attributes for fields (both
+ bit-fields and, in the case of AT_location, regular fields as well).
+ */
+
+ /* Figure out the bit-distance from the start of the structure to the
+ "deepest" bit of the bit-field. */
+ deepest_bitpos = bitpos_int + field_size_in_bits;
+
+ /* This is the tricky part. Use some fancy footwork to deduce where the
+ lowest addressed bit of the containing object must be. */
+ object_offset_in_bits
+ = ceiling (deepest_bitpos, type_align_in_bits) - type_size_in_bits;
+
+ /* Compute the offset of the containing object in "alignment units". */
+ object_offset_in_align_units = object_offset_in_bits / type_align_in_bits;
+
+ /* Compute the offset of the containing object in bytes. */
+ object_offset_in_bytes = object_offset_in_align_units * type_align_in_bytes;
+
+ return object_offset_in_bytes;
+}
+
+/****************************** attributes *********************************/
+
+/* The following routines are responsible for writing out the various types
+ of Dwarf attributes (and any following data bytes associated with them).
+ These routines are listed in order based on the numerical codes of their
+ associated attributes. */
+
+/* Generate an AT_sibling attribute. */
+
+inline void
+sibling_attribute ()
+{
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_sibling);
+ sprintf (label, DIE_BEGIN_LABEL_FMT, NEXT_DIE_NUM);
+ ASM_OUTPUT_DWARF_REF (asm_out_file, label);
+}
+
+/* Output the form of location attributes suitable for whole variables and
+ whole parameters. Note that the location attributes for struct fields
+ are generated by the routine `data_member_location_attribute' below. */
+
+static void
+location_attribute (rtl)
+ register rtx rtl;
+{
+ char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
+ char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_location);
+ sprintf (begin_label, LOC_BEGIN_LABEL_FMT, current_dienum);
+ sprintf (end_label, LOC_END_LABEL_FMT, current_dienum);
+ ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label);
+ ASM_OUTPUT_LABEL (asm_out_file, begin_label);
+
+ /* Handle a special case. If we are about to output a location descriptor
+ for a variable or parameter which has been optimized out of existence,
+ don't do that. Instead we output a zero-length location descriptor
+ value as part of the location attribute.
+
+ A variable which has been optimized out of existence will have a
+ DECL_RTL value which denotes a pseudo-reg.
+
+ Currently, in some rare cases, variables can have DECL_RTL values
+ which look like (MEM (REG pseudo-reg#)). These cases are due to
+ bugs elsewhere in the compiler. We treat such cases
+ as if the variable(s) in question had been optimized out of existence.
+
+ Note that in all cases where we wish to express the fact that a
+ variable has been optimized out of existence, we do not simply
+ suppress the generation of the entire location attribute because
+ the absence of a location attribute in certain kinds of DIEs is
+ used to indicate something else entirely... i.e. that the DIE
+ represents an object declaration, but not a definition. So sayeth
+ the PLSIG.
+ */
+
+ if (! is_pseudo_reg (rtl)
+ && (GET_CODE (rtl) != MEM || ! is_pseudo_reg (XEXP (rtl, 0))))
+ output_loc_descriptor (eliminate_regs (rtl, 0, NULL_RTX));
+
+ ASM_OUTPUT_LABEL (asm_out_file, end_label);
+}
+
+/* Output the specialized form of location attribute used for data members
+ of struct and union types.
+
+ In the special case of a FIELD_DECL node which represents a bit-field,
+ the "offset" part of this special location descriptor must indicate the
+ distance in bytes from the lowest-addressed byte of the containing
+ struct or union type to the lowest-addressed byte of the "containing
+ object" for the bit-field. (See the `field_byte_offset' function above.)
+
+ For any given bit-field, the "containing object" is a hypothetical
+ object (of some integral or enum type) within which the given bit-field
+ lives. The type of this hypothetical "containing object" is always the
+ same as the declared type of the individual bit-field itself (for GCC
+ anyway... the DWARF spec doesn't actually mandate this).
+
+ Note that it is the size (in bytes) of the hypothetical "containing
+ object" which will be given in the AT_byte_size attribute for this
+ bit-field. (See the `byte_size_attribute' function below.) It is
+ also used when calculating the value of the AT_bit_offset attribute.
+ (See the `bit_offset_attribute' function below.)
+*/
+
+static void
+data_member_location_attribute (decl)
+ register tree decl;
+{
+ register unsigned object_offset_in_bytes = field_byte_offset (decl);
+ char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
+ char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_location);
+ sprintf (begin_label, LOC_BEGIN_LABEL_FMT, current_dienum);
+ sprintf (end_label, LOC_END_LABEL_FMT, current_dienum);
+ ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label);
+ ASM_OUTPUT_LABEL (asm_out_file, begin_label);
+ ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_CONST);
+ ASM_OUTPUT_DWARF_DATA4 (asm_out_file, object_offset_in_bytes);
+ ASM_OUTPUT_DWARF_STACK_OP (asm_out_file, OP_ADD);
+ ASM_OUTPUT_LABEL (asm_out_file, end_label);
+}
+
+/* Output an AT_const_value attribute for a variable or a parameter which
+ does not have a "location" either in memory or in a register. These
+ things can arise in GNU C when a constant is passed as an actual
+ parameter to an inlined function. They can also arise in C++ where
+ declared constants do not necessarily get memory "homes". */
+
+static void
+const_value_attribute (rtl)
+ register rtx rtl;
+{
+ char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
+ char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_const_value_block4);
+ sprintf (begin_label, LOC_BEGIN_LABEL_FMT, current_dienum);
+ sprintf (end_label, LOC_END_LABEL_FMT, current_dienum);
+ ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, end_label, begin_label);
+ ASM_OUTPUT_LABEL (asm_out_file, begin_label);
+
+ switch (GET_CODE (rtl))
+ {
+ case CONST_INT:
+ /* Note that a CONST_INT rtx could represent either an integer or
+ a floating-point constant. A CONST_INT is used whenever the
+ constant will fit into a single word. In all such cases, the
+ original mode of the constant value is wiped out, and the
+ CONST_INT rtx is assigned VOIDmode. Since we no longer have
+ precise mode information for these constants, we always just
+ output them using 4 bytes. */
+
+ ASM_OUTPUT_DWARF_DATA4 (asm_out_file, (unsigned) INTVAL (rtl));
+ break;
+
+ case CONST_DOUBLE:
+ /* Note that a CONST_DOUBLE rtx could represent either an integer
+ or a floating-point constant. A CONST_DOUBLE is used whenever
+ the constant requires more than one word in order to be adequately
+ represented. In all such cases, the original mode of the constant
+ value is preserved as the mode of the CONST_DOUBLE rtx, but for
+ simplicity we always just output CONST_DOUBLEs using 8 bytes. */
+
+ ASM_OUTPUT_DWARF_DATA8 (asm_out_file,
+ (unsigned HOST_WIDE_INT) CONST_DOUBLE_HIGH (rtl),
+ (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (rtl));
+ break;
+
+ case CONST_STRING:
+ ASM_OUTPUT_DWARF_STRING (asm_out_file, XSTR (rtl, 0));
+ break;
+
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case CONST:
+ ASM_OUTPUT_DWARF_ADDR_CONST (asm_out_file, rtl);
+ break;
+
+ case PLUS:
+ /* In cases where an inlined instance of an inline function is passed
+ the address of an `auto' variable (which is local to the caller)
+ we can get a situation where the DECL_RTL of the artificial
+ local variable (for the inlining) which acts as a stand-in for
+ the corresponding formal parameter (of the inline function)
+ will look like (plus:SI (reg:SI FRAME_PTR) (const_int ...)).
+ This is not exactly a compile-time constant expression, but it
+ isn't the address of the (artificial) local variable either.
+ Rather, it represents the *value* which the artificial local
+ variable always has during its lifetime. We currently have no
+ way to represent such quasi-constant values in Dwarf, so for now
+ we just punt and generate an AT_const_value attribute with form
+ FORM_BLOCK4 and a length of zero. */
+ break;
+
+ default:
+ abort (); /* No other kinds of rtx should be possible here. */
+ }
+
+ ASM_OUTPUT_LABEL (asm_out_file, end_label);
+}
+
+/* Generate *either* an AT_location attribute or else an AT_const_value
+ data attribute for a variable or a parameter. We generate the
+ AT_const_value attribute only in those cases where the given
+ variable or parameter does not have a true "location" either in
+ memory or in a register. This can happen (for example) when a
+ constant is passed as an actual argument in a call to an inline
+ function. (It's possible that these things can crop up in other
+ ways also.) Note that one type of constant value which can be
+ passed into an inlined function is a constant pointer. This can
+ happen for example if an actual argument in an inlined function
+ call evaluates to a compile-time constant address. */
+
+static void
+location_or_const_value_attribute (decl)
+ register tree decl;
+{
+ register rtx rtl;
+
+ if (TREE_CODE (decl) == ERROR_MARK)
+ return;
+
+ if ((TREE_CODE (decl) != VAR_DECL) && (TREE_CODE (decl) != PARM_DECL))
+ {
+ /* Should never happen. */
+ abort ();
+ return;
+ }
+
+ /* Here we have to decide where we are going to say the parameter "lives"
+ (as far as the debugger is concerned). We only have a couple of choices.
+ GCC provides us with DECL_RTL and with DECL_INCOMING_RTL. DECL_RTL
+ normally indicates where the parameter lives during most of the activa-
+ tion of the function. If optimization is enabled however, this could
+ be either NULL or else a pseudo-reg. Both of those cases indicate that
+ the parameter doesn't really live anywhere (as far as the code generation
+ parts of GCC are concerned) during most of the function's activation.
+ That will happen (for example) if the parameter is never referenced
+ within the function.
+
+ We could just generate a location descriptor here for all non-NULL
+ non-pseudo values of DECL_RTL and ignore all of the rest, but we can
+ be a little nicer than that if we also consider DECL_INCOMING_RTL in
+ cases where DECL_RTL is NULL or is a pseudo-reg.
+
+ Note however that we can only get away with using DECL_INCOMING_RTL as
+ a backup substitute for DECL_RTL in certain limited cases. In cases
+ where DECL_ARG_TYPE(decl) indicates the same type as TREE_TYPE(decl)
+ we can be sure that the parameter was passed using the same type as it
+ is declared to have within the function, and that its DECL_INCOMING_RTL
+ points us to a place where a value of that type is passed. In cases
+ where DECL_ARG_TYPE(decl) and TREE_TYPE(decl) are different types
+ however, we cannot (in general) use DECL_INCOMING_RTL as a backup
+ substitute for DECL_RTL because in these cases, DECL_INCOMING_RTL
+ points us to a value of some type which is *different* from the type
+ of the parameter itself. Thus, if we tried to use DECL_INCOMING_RTL
+ to generate a location attribute in such cases, the debugger would
+ end up (for example) trying to fetch a `float' from a place which
+ actually contains the first part of a `double'. That would lead to
+ really incorrect and confusing output at debug-time, and we don't
+ want that now do we?
+
+ So in general, we DO NOT use DECL_INCOMING_RTL as a backup for DECL_RTL
+ in cases where DECL_ARG_TYPE(decl) != TREE_TYPE(decl). There are a
+ couple of cute exceptions however. On little-endian machines we can
+ get away with using DECL_INCOMING_RTL even when DECL_ARG_TYPE(decl) is
+ not the same as TREE_TYPE(decl) but only when DECL_ARG_TYPE(decl) is
+ an integral type which is smaller than TREE_TYPE(decl). These cases
+ arise when (on a little-endian machine) a non-prototyped function has
+ a parameter declared to be of type `short' or `char'. In such cases,
+ TREE_TYPE(decl) will be `short' or `char', DECL_ARG_TYPE(decl) will be
+ `int', and DECL_INCOMING_RTL will point to the lowest-order byte of the
+ passed `int' value. If the debugger then uses that address to fetch a
+ `short' or a `char' (on a little-endian machine) the result will be the
+ correct data, so we allow for such exceptional cases below.
+
+ Note that our goal here is to describe the place where the given formal
+ parameter lives during most of the function's activation (i.e. between
+ the end of the prologue and the start of the epilogue). We'll do that
+ as best as we can. Note however that if the given formal parameter is
+ modified sometime during the execution of the function, then a stack
+ backtrace (at debug-time) will show the function as having been called
+ with the *new* value rather than the value which was originally passed
+ in. This happens rarely enough that it is not a major problem, but it
+ *is* a problem, and I'd like to fix it. A future version of dwarfout.c
+ may generate two additional attributes for any given TAG_formal_parameter
+ DIE which will describe the "passed type" and the "passed location" for
+ the given formal parameter in addition to the attributes we now generate
+ to indicate the "declared type" and the "active location" for each
+ parameter. This additional set of attributes could be used by debuggers
+ for stack backtraces.
+
+ Separately, note that sometimes DECL_RTL can be NULL and DECL_INCOMING_RTL
+ can be NULL also. This happens (for example) for inlined-instances of
+ inline function formal parameters which are never referenced. This really
+ shouldn't be happening. All PARM_DECL nodes should get valid non-NULL
+ DECL_INCOMING_RTL values, but integrate.c doesn't currently generate
+ these values for inlined instances of inline function parameters, so
+ when we see such cases, we are just SOL (shit-out-of-luck) for the time
+ being (until integrate.c gets fixed).
+ */
+
+ /* Use DECL_RTL as the "location" unless we find something better. */
+ rtl = DECL_RTL (decl);
+
+ if (TREE_CODE (decl) == PARM_DECL)
+ if (rtl == NULL_RTX || is_pseudo_reg (rtl))
+ {
+ /* This decl represents a formal parameter which was optimized out. */
+ register tree declared_type = type_main_variant (TREE_TYPE (decl));
+ register tree passed_type = type_main_variant (DECL_ARG_TYPE (decl));
+
+ /* Note that DECL_INCOMING_RTL may be NULL in here, but we handle
+ *all* cases where (rtl == NULL_RTX) just below. */
+
+ if (declared_type == passed_type)
+ rtl = DECL_INCOMING_RTL (decl);
+#if (BYTES_BIG_ENDIAN == 0)
+ else
+ if (TREE_CODE (declared_type) == INTEGER_TYPE)
+ if (TYPE_SIZE (declared_type) <= TYPE_SIZE (passed_type))
+ rtl = DECL_INCOMING_RTL (decl);
+#endif /* (BYTES_BIG_ENDIAN == 0) */
+ }
+
+ if (rtl == NULL_RTX)
+ return;
+
+ switch (GET_CODE (rtl))
+ {
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case CONST_STRING:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case CONST:
+ case PLUS: /* DECL_RTL could be (plus (reg ...) (const_int ...)) */
+ const_value_attribute (rtl);
+ break;
+
+ case MEM:
+ case REG:
+ case SUBREG:
+ location_attribute (rtl);
+ break;
+
+ default:
+ abort (); /* Should never happen. */
+ }
+}
+
+/* Generate an AT_name attribute given some string value to be included as
+ the value of the attribute. */
+
+inline void
+name_attribute (name_string)
+ register char *name_string;
+{
+ if (name_string && *name_string)
+ {
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_name);
+ ASM_OUTPUT_DWARF_STRING (asm_out_file, name_string);
+ }
+}
+
+inline void
+fund_type_attribute (ft_code)
+ register unsigned ft_code;
+{
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_fund_type);
+ ASM_OUTPUT_DWARF_FUND_TYPE (asm_out_file, ft_code);
+}
+
+static void
+mod_fund_type_attribute (type, decl_const, decl_volatile)
+ register tree type;
+ register int decl_const;
+ register int decl_volatile;
+{
+ char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
+ char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_mod_fund_type);
+ sprintf (begin_label, MT_BEGIN_LABEL_FMT, current_dienum);
+ sprintf (end_label, MT_END_LABEL_FMT, current_dienum);
+ ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label);
+ ASM_OUTPUT_LABEL (asm_out_file, begin_label);
+ write_modifier_bytes (type, decl_const, decl_volatile);
+ ASM_OUTPUT_DWARF_FUND_TYPE (asm_out_file,
+ fundamental_type_code (root_type (type)));
+ ASM_OUTPUT_LABEL (asm_out_file, end_label);
+}
+
+inline void
+user_def_type_attribute (type)
+ register tree type;
+{
+ char ud_type_name[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_user_def_type);
+ sprintf (ud_type_name, TYPE_NAME_FMT, TYPE_UID (type));
+ ASM_OUTPUT_DWARF_REF (asm_out_file, ud_type_name);
+}
+
+static void
+mod_u_d_type_attribute (type, decl_const, decl_volatile)
+ register tree type;
+ register int decl_const;
+ register int decl_volatile;
+{
+ char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
+ char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
+ char ud_type_name[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_mod_u_d_type);
+ sprintf (begin_label, MT_BEGIN_LABEL_FMT, current_dienum);
+ sprintf (end_label, MT_END_LABEL_FMT, current_dienum);
+ ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label);
+ ASM_OUTPUT_LABEL (asm_out_file, begin_label);
+ write_modifier_bytes (type, decl_const, decl_volatile);
+ sprintf (ud_type_name, TYPE_NAME_FMT, TYPE_UID (root_type (type)));
+ ASM_OUTPUT_DWARF_REF (asm_out_file, ud_type_name);
+ ASM_OUTPUT_LABEL (asm_out_file, end_label);
+}
+
+#ifdef USE_ORDERING_ATTRIBUTE
+inline void
+ordering_attribute (ordering)
+ register unsigned ordering;
+{
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_ordering);
+ ASM_OUTPUT_DWARF_DATA2 (asm_out_file, ordering);
+}
+#endif /* defined(USE_ORDERING_ATTRIBUTE) */
+
+/* Note that the block of subscript information for an array type also
+ includes information about the element type of type given array type. */
+
+static void
+subscript_data_attribute (type)
+ register tree type;
+{
+ register unsigned dimension_number;
+ char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
+ char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_subscr_data);
+ sprintf (begin_label, SS_BEGIN_LABEL_FMT, current_dienum);
+ sprintf (end_label, SS_END_LABEL_FMT, current_dienum);
+ ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label);
+ ASM_OUTPUT_LABEL (asm_out_file, begin_label);
+
+ /* The GNU compilers represent multidimensional array types as sequences
+ of one dimensional array types whose element types are themselves array
+ types. Here we squish that down, so that each multidimensional array
+ type gets only one array_type DIE in the Dwarf debugging info. The
+ draft Dwarf specification say that we are allowed to do this kind
+ of compression in C (because there is no difference between an
+ array or arrays and a multidimensional array in C) but for other
+ source languages (e.g. Ada) we probably shouldn't do this. */
+
+ for (dimension_number = 0;
+ TREE_CODE (type) == ARRAY_TYPE;
+ type = TREE_TYPE (type), dimension_number++)
+ {
+ register tree domain = TYPE_DOMAIN (type);
+
+ /* Arrays come in three flavors. Unspecified bounds, fixed
+ bounds, and (in GNU C only) variable bounds. Handle all
+ three forms here. */
+
+ if (domain)
+ {
+ /* We have an array type with specified bounds. */
+
+ register tree lower = TYPE_MIN_VALUE (domain);
+ register tree upper = TYPE_MAX_VALUE (domain);
+
+ /* Handle only fundamental types as index types for now. */
+
+ if (! type_is_fundamental (domain))
+ abort ();
+
+ /* Output the representation format byte for this dimension. */
+
+ ASM_OUTPUT_DWARF_FMT_BYTE (asm_out_file,
+ FMT_CODE (1,
+ TREE_CODE (lower) == INTEGER_CST,
+ TREE_CODE (upper) == INTEGER_CST));
+
+ /* Output the index type for this dimension. */
+
+ ASM_OUTPUT_DWARF_FUND_TYPE (asm_out_file,
+ fundamental_type_code (domain));
+
+ /* Output the representation for the lower bound. */
+
+ output_bound_representation (lower, dimension_number, 'l');
+
+ /* Output the representation for the upper bound. */
+
+ output_bound_representation (upper, dimension_number, 'u');
+ }
+ else
+ {
+ /* We have an array type with an unspecified length. For C and
+ C++ we can assume that this really means that (a) the index
+ type is an integral type, and (b) the lower bound is zero.
+ Note that Dwarf defines the representation of an unspecified
+ (upper) bound as being a zero-length location description. */
+
+ /* Output the array-bounds format byte. */
+
+ ASM_OUTPUT_DWARF_FMT_BYTE (asm_out_file, FMT_FT_C_X);
+
+ /* Output the (assumed) index type. */
+
+ ASM_OUTPUT_DWARF_FUND_TYPE (asm_out_file, FT_integer);
+
+ /* Output the (assumed) lower bound (constant) value. */
+
+ ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0);
+
+ /* Output the (empty) location description for the upper bound. */
+
+ ASM_OUTPUT_DWARF_DATA2 (asm_out_file, 0);
+ }
+ }
+
+ /* Output the prefix byte that says that the element type is comming up. */
+
+ ASM_OUTPUT_DWARF_FMT_BYTE (asm_out_file, FMT_ET);
+
+ /* Output a representation of the type of the elements of this array type. */
+
+ type_attribute (type, 0, 0);
+
+ ASM_OUTPUT_LABEL (asm_out_file, end_label);
+}
+
+static void
+byte_size_attribute (tree_node)
+ register tree tree_node;
+{
+ register unsigned size;
+
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_byte_size);
+ switch (TREE_CODE (tree_node))
+ {
+ case ERROR_MARK:
+ size = 0;
+ break;
+
+ case ENUMERAL_TYPE:
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ case QUAL_UNION_TYPE:
+ size = int_size_in_bytes (tree_node);
+ break;
+
+ case FIELD_DECL:
+ /* For a data member of a struct or union, the AT_byte_size is
+ generally given as the number of bytes normally allocated for
+ an object of the *declared* type of the member itself. This
+ is true even for bit-fields. */
+ size = simple_type_size_in_bits (field_type (tree_node))
+ / BITS_PER_UNIT;
+ break;
+
+ default:
+ abort ();
+ }
+
+ /* Note that `size' might be -1 when we get to this point. If it
+ is, that indicates that the byte size of the entity in question
+ is variable. We have no good way of expressing this fact in Dwarf
+ at the present time, so just let the -1 pass on through. */
+
+ ASM_OUTPUT_DWARF_DATA4 (asm_out_file, size);
+}
+
+/* For a FIELD_DECL node which represents a bit-field, output an attribute
+ which specifies the distance in bits from the highest order bit of the
+ "containing object" for the bit-field to the highest order bit of the
+ bit-field itself.
+
+ For any given bit-field, the "containing object" is a hypothetical
+ object (of some integral or enum type) within which the given bit-field
+ lives. The type of this hypothetical "containing object" is always the
+ same as the declared type of the individual bit-field itself.
+
+ The determination of the exact location of the "containing object" for
+ a bit-field is rather complicated. It's handled by the `field_byte_offset'
+ function (above).
+
+ Note that it is the size (in bytes) of the hypothetical "containing
+ object" which will be given in the AT_byte_size attribute for this
+ bit-field. (See `byte_size_attribute' above.)
+*/
+
+inline void
+bit_offset_attribute (decl)
+ register tree decl;
+{
+ register unsigned object_offset_in_bytes = field_byte_offset (decl);
+ register tree type = DECL_BIT_FIELD_TYPE (decl);
+ register tree bitpos_tree = DECL_FIELD_BITPOS (decl);
+ register unsigned bitpos_int;
+ register unsigned highest_order_object_bit_offset;
+ register unsigned highest_order_field_bit_offset;
+ register unsigned bit_offset;
+
+ assert (TREE_CODE (decl) == FIELD_DECL); /* Must be a field. */
+ assert (type); /* Must be a bit field. */
+
+ /* We can't yet handle bit-fields whose offsets are variable, so if we
+ encounter such things, just return without generating any attribute
+ whatsoever. */
+
+ if (TREE_CODE (bitpos_tree) != INTEGER_CST)
+ return;
+ bitpos_int = (unsigned) TREE_INT_CST_LOW (bitpos_tree);
+
+ /* Note that the bit offset is always the distance (in bits) from the
+ highest-order bit of the "containing object" to the highest-order
+ bit of the bit-field itself. Since the "high-order end" of any
+ object or field is different on big-endian and little-endian machines,
+ the computation below must take account of these differences. */
+
+ highest_order_object_bit_offset = object_offset_in_bytes * BITS_PER_UNIT;
+ highest_order_field_bit_offset = bitpos_int;
+
+#if (BYTES_BIG_ENDIAN == 0)
+ highest_order_field_bit_offset
+ += (unsigned) TREE_INT_CST_LOW (DECL_SIZE (decl));
+
+ highest_order_object_bit_offset += simple_type_size_in_bits (type);
+#endif /* (BYTES_BIG_ENDIAN == 0) */
+
+ bit_offset =
+#if (BYTES_BIG_ENDIAN == 0)
+ highest_order_object_bit_offset - highest_order_field_bit_offset;
+#else /* (BYTES_BIG_ENDIAN != 0) */
+ highest_order_field_bit_offset - highest_order_object_bit_offset;
+#endif /* (BYTES_BIG_ENDIAN != 0) */
+
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_bit_offset);
+ ASM_OUTPUT_DWARF_DATA2 (asm_out_file, bit_offset);
+}
+
+/* For a FIELD_DECL node which represents a bit field, output an attribute
+ which specifies the length in bits of the given field. */
+
+inline void
+bit_size_attribute (decl)
+ register tree decl;
+{
+ assert (TREE_CODE (decl) == FIELD_DECL); /* Must be a field. */
+ assert (DECL_BIT_FIELD_TYPE (decl)); /* Must be a bit field. */
+
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_bit_size);
+ ASM_OUTPUT_DWARF_DATA4 (asm_out_file,
+ (unsigned) TREE_INT_CST_LOW (DECL_SIZE (decl)));
+}
+
+/* The following routine outputs the `element_list' attribute for enumeration
+ type DIEs. The element_lits attribute includes the names and values of
+ all of the enumeration constants associated with the given enumeration
+ type. */
+
+inline void
+element_list_attribute (element)
+ register tree element;
+{
+ char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
+ char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_element_list);
+ sprintf (begin_label, EE_BEGIN_LABEL_FMT, current_dienum);
+ sprintf (end_label, EE_END_LABEL_FMT, current_dienum);
+ ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, end_label, begin_label);
+ ASM_OUTPUT_LABEL (asm_out_file, begin_label);
+
+ /* Here we output a list of value/name pairs for each enumeration constant
+ defined for this enumeration type (as required), but we do it in REVERSE
+ order. The order is the one required by the draft #5 Dwarf specification
+ published by the UI/PLSIG. */
+
+ output_enumeral_list (element); /* Recursively output the whole list. */
+
+ ASM_OUTPUT_LABEL (asm_out_file, end_label);
+}
+
+/* Generate an AT_stmt_list attribute. These are normally present only in
+ DIEs with a TAG_compile_unit tag. */
+
+inline void
+stmt_list_attribute (label)
+ register char *label;
+{
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_stmt_list);
+ /* Don't use ASM_OUTPUT_DWARF_DATA4 here. */
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, label);
+}
+
+/* Generate an AT_low_pc attribute for a label DIE, a lexical_block DIE or
+ for a subroutine DIE. */
+
+inline void
+low_pc_attribute (asm_low_label)
+ register char *asm_low_label;
+{
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_low_pc);
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, asm_low_label);
+}
+
+/* Generate an AT_high_pc attribute for a lexical_block DIE or for a
+ subroutine DIE. */
+
+inline void
+high_pc_attribute (asm_high_label)
+ register char *asm_high_label;
+{
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_high_pc);
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, asm_high_label);
+}
+
+/* Generate an AT_body_begin attribute for a subroutine DIE. */
+
+inline void
+body_begin_attribute (asm_begin_label)
+ register char *asm_begin_label;
+{
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_body_begin);
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, asm_begin_label);
+}
+
+/* Generate an AT_body_end attribute for a subroutine DIE. */
+
+inline void
+body_end_attribute (asm_end_label)
+ register char *asm_end_label;
+{
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_body_end);
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, asm_end_label);
+}
+
+/* Generate an AT_language attribute given a LANG value. These attributes
+ are used only within TAG_compile_unit DIEs. */
+
+inline void
+language_attribute (language_code)
+ register unsigned language_code;
+{
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_language);
+ ASM_OUTPUT_DWARF_DATA4 (asm_out_file, language_code);
+}
+
+inline void
+member_attribute (context)
+ register tree context;
+{
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ /* Generate this attribute only for members in C++. */
+
+ if (context != NULL && is_tagged_type (context))
+ {
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_member);
+ sprintf (label, TYPE_NAME_FMT, TYPE_UID (context));
+ ASM_OUTPUT_DWARF_REF (asm_out_file, label);
+ }
+}
+
+inline void
+string_length_attribute (upper_bound)
+ register tree upper_bound;
+{
+ char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
+ char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_string_length);
+ sprintf (begin_label, SL_BEGIN_LABEL_FMT, current_dienum);
+ sprintf (end_label, SL_END_LABEL_FMT, current_dienum);
+ ASM_OUTPUT_DWARF_DELTA2 (asm_out_file, end_label, begin_label);
+ ASM_OUTPUT_LABEL (asm_out_file, begin_label);
+ output_bound_representation (upper_bound, 0, 'u');
+ ASM_OUTPUT_LABEL (asm_out_file, end_label);
+}
+
+inline void
+comp_dir_attribute (dirname)
+ register char *dirname;
+{
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_comp_dir);
+ ASM_OUTPUT_DWARF_STRING (asm_out_file, dirname);
+}
+
+inline void
+sf_names_attribute (sf_names_start_label)
+ register char *sf_names_start_label;
+{
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_sf_names);
+ /* Don't use ASM_OUTPUT_DWARF_DATA4 here. */
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, sf_names_start_label);
+}
+
+inline void
+src_info_attribute (src_info_start_label)
+ register char *src_info_start_label;
+{
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_src_info);
+ /* Don't use ASM_OUTPUT_DWARF_DATA4 here. */
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, src_info_start_label);
+}
+
+inline void
+mac_info_attribute (mac_info_start_label)
+ register char *mac_info_start_label;
+{
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_mac_info);
+ /* Don't use ASM_OUTPUT_DWARF_DATA4 here. */
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, mac_info_start_label);
+}
+
+inline void
+prototyped_attribute (func_type)
+ register tree func_type;
+{
+ if ((strcmp (language_string, "GNU C") == 0)
+ && (TYPE_ARG_TYPES (func_type) != NULL))
+ {
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_prototyped);
+ ASM_OUTPUT_DWARF_STRING (asm_out_file, "");
+ }
+}
+
+inline void
+producer_attribute (producer)
+ register char *producer;
+{
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_producer);
+ ASM_OUTPUT_DWARF_STRING (asm_out_file, producer);
+}
+
+inline void
+inline_attribute (decl)
+ register tree decl;
+{
+ if (DECL_INLINE (decl))
+ {
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_inline);
+ ASM_OUTPUT_DWARF_STRING (asm_out_file, "");
+ }
+}
+
+inline void
+containing_type_attribute (containing_type)
+ register tree containing_type;
+{
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_containing_type);
+ sprintf (label, TYPE_NAME_FMT, TYPE_UID (containing_type));
+ ASM_OUTPUT_DWARF_REF (asm_out_file, label);
+}
+
+inline void
+abstract_origin_attribute (origin)
+ register tree origin;
+{
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_abstract_origin);
+ switch (TREE_CODE_CLASS (TREE_CODE (origin)))
+ {
+ case 'd':
+ sprintf (label, DECL_NAME_FMT, DECL_UID (origin));
+ break;
+
+ case 't':
+ sprintf (label, TYPE_NAME_FMT, TYPE_UID (origin));
+ break;
+
+ default:
+ abort (); /* Should never happen. */
+
+ }
+ ASM_OUTPUT_DWARF_REF (asm_out_file, label);
+}
+
+#ifdef DWARF_DECL_COORDINATES
+inline void
+src_coords_attribute (src_fileno, src_lineno)
+ register unsigned src_fileno;
+ register unsigned src_lineno;
+{
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_src_coords);
+ ASM_OUTPUT_DWARF_DATA2 (asm_out_file, src_fileno);
+ ASM_OUTPUT_DWARF_DATA2 (asm_out_file, src_lineno);
+}
+#endif /* defined(DWARF_DECL_COORDINATES) */
+
+inline void
+pure_or_virtual_attribute (func_decl)
+ register tree func_decl;
+{
+ if (DECL_VIRTUAL_P (func_decl))
+ {
+#if 0 /* DECL_ABSTRACT_VIRTUAL_P is C++-specific. */
+ if (DECL_ABSTRACT_VIRTUAL_P (func_decl))
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_pure_virtual);
+ else
+#endif
+ ASM_OUTPUT_DWARF_ATTRIBUTE (asm_out_file, AT_virtual);
+ ASM_OUTPUT_DWARF_STRING (asm_out_file, "");
+ }
+}
+
+/************************* end of attributes *****************************/
+
+/********************* utility routines for DIEs *************************/
+
+/* Output an AT_name attribute and an AT_src_coords attribute for the
+ given decl, but only if it actually has a name. */
+
+static void
+name_and_src_coords_attributes (decl)
+ register tree decl;
+{
+ register tree decl_name = DECL_NAME (decl);
+
+ if (decl_name && IDENTIFIER_POINTER (decl_name))
+ {
+ name_attribute (IDENTIFIER_POINTER (decl_name));
+#ifdef DWARF_DECL_COORDINATES
+ {
+ register unsigned file_index;
+
+ /* This is annoying, but we have to pop out of the .debug section
+ for a moment while we call `lookup_filename' because calling it
+ may cause a temporary switch into the .debug_sfnames section and
+ most svr4 assemblers are not smart enough be be able to nest
+ section switches to any depth greater than one. Note that we
+ also can't skirt this issue by delaying all output to the
+ .debug_sfnames section unit the end of compilation because that
+ would cause us to have inter-section forward references and
+ Fred Fish sez that m68k/svr4 assemblers botch those. */
+
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+ file_index = lookup_filename (DECL_SOURCE_FILE (decl));
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, DEBUG_SECTION);
+
+ src_coords_attribute (file_index, DECL_SOURCE_LINE (decl));
+ }
+#endif /* defined(DWARF_DECL_COORDINATES) */
+ }
+}
+
+/* Many forms of DIEs contain a "type description" part. The following
+ routine writes out these "type descriptor" parts. */
+
+static void
+type_attribute (type, decl_const, decl_volatile)
+ register tree type;
+ register int decl_const;
+ register int decl_volatile;
+{
+ register enum tree_code code = TREE_CODE (type);
+ register int root_type_modified;
+
+ if (TREE_CODE (type) == ERROR_MARK)
+ return;
+
+ /* Handle a special case. For functions whose return type is void,
+ we generate *no* type attribute. (Note that no object may have
+ type `void', so this only applies to function return types. */
+
+ if (TREE_CODE (type) == VOID_TYPE)
+ return;
+
+ root_type_modified = (code == POINTER_TYPE || code == REFERENCE_TYPE
+ || decl_const || decl_volatile
+ || TYPE_READONLY (type) || TYPE_VOLATILE (type));
+
+ if (type_is_fundamental (root_type (type)))
+ if (root_type_modified)
+ mod_fund_type_attribute (type, decl_const, decl_volatile);
+ else
+ fund_type_attribute (fundamental_type_code (type));
+ else
+ if (root_type_modified)
+ mod_u_d_type_attribute (type, decl_const, decl_volatile);
+ else
+ /* We have to get the type_main_variant here (and pass that to the
+ `user_def_type_attribute' routine) because the ..._TYPE node we
+ have might simply be a *copy* of some original type node (where
+ the copy was created to help us keep track of typedef names)
+ and that copy might have a different TYPE_UID from the original
+ ..._TYPE node. (Note that when `equate_type_number_to_die_number'
+ is labeling a given type DIE for future reference, it always and
+ only creates labels for DIEs representing *main variants*, and it
+ never even knows about non-main-variants.) */
+ user_def_type_attribute (type_main_variant (type));
+}
+
+/* Given a tree pointer to a struct, class, union, or enum type node, return
+ a pointer to the (string) tag name for the given type, or zero if the
+ type was declared without a tag. */
+
+static char *
+type_tag (type)
+ register tree type;
+{
+ register char *name = 0;
+
+ if (TYPE_NAME (type) != 0)
+ {
+ register tree t = 0;
+
+ /* Find the IDENTIFIER_NODE for the type name. */
+ if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
+ t = TYPE_NAME (type);
+#if 0
+ /* The g++ front end makes the TYPE_NAME of *each* tagged type point
+ to a TYPE_DECL node, regardless of whether or not a `typedef' was
+ involved. This is distinctly different from what the gcc front-end
+ does. It always makes the TYPE_NAME for each tagged type be either
+ NULL (signifying an anonymous tagged type) or else a pointer to an
+ IDENTIFIER_NODE. Obviously, we would like to generate correct Dwarf
+ for both C and C++, but given this inconsistency in the TREE
+ representation of tagged types for C and C++ in the GNU front-ends,
+ we cannot support both languages correctly unless we introduce some
+ front-end specific code here, and rms objects to that, so we can
+ only generate correct Dwarf for one of these two languages. C is
+ more important, so for now we'll do the right thing for C and let
+ g++ go fish. */
+
+ else
+ if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL)
+ t = DECL_NAME (TYPE_NAME (type));
+#endif
+ /* Now get the name as a string, or invent one. */
+ if (t != 0)
+ name = IDENTIFIER_POINTER (t);
+ }
+
+ return (name == 0 || *name == '\0') ? 0 : name;
+}
+
+inline void
+dienum_push ()
+{
+ /* Start by checking if the pending_sibling_stack needs to be expanded.
+ If necessary, expand it. */
+
+ if (pending_siblings == pending_siblings_allocated)
+ {
+ pending_siblings_allocated += PENDING_SIBLINGS_INCREMENT;
+ pending_sibling_stack
+ = (unsigned *) xrealloc (pending_sibling_stack,
+ pending_siblings_allocated * sizeof(unsigned));
+ }
+
+ pending_siblings++;
+ NEXT_DIE_NUM = next_unused_dienum++;
+}
+
+/* Pop the sibling stack so that the most recently pushed DIEnum becomes the
+ NEXT_DIE_NUM. */
+
+inline void
+dienum_pop ()
+{
+ pending_siblings--;
+}
+
+inline tree
+member_declared_type (member)
+ register tree member;
+{
+ return (DECL_BIT_FIELD_TYPE (member))
+ ? DECL_BIT_FIELD_TYPE (member)
+ : TREE_TYPE (member);
+}
+
+/* Get the function's label, as described by its RTL.
+ This may be different from the DECL_NAME name used
+ in the source file. */
+
+static char *
+function_start_label (decl)
+ register tree decl;
+{
+ rtx x;
+ char *fnname;
+
+ x = DECL_RTL (decl);
+ if (GET_CODE (x) != MEM)
+ abort ();
+ x = XEXP (x, 0);
+ if (GET_CODE (x) != SYMBOL_REF)
+ abort ();
+ fnname = XSTR (x, 0);
+ return fnname;
+}
+
+
+/******************************* DIEs ************************************/
+
+/* Output routines for individual types of DIEs. */
+
+/* Note that every type of DIE (except a null DIE) gets a sibling. */
+
+static void
+output_array_type_die (arg)
+ register void *arg;
+{
+ register tree type = arg;
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_array_type);
+ sibling_attribute ();
+ equate_type_number_to_die_number (type);
+ member_attribute (TYPE_CONTEXT (type));
+
+ /* I believe that we can default the array ordering. SDB will probably
+ do the right things even if AT_ordering is not present. It's not
+ even an issue until we start to get into multidimensional arrays
+ anyway. If SDB is ever caught doing the Wrong Thing for multi-
+ dimensional arrays, then we'll have to put the AT_ordering attribute
+ back in. (But if and when we find out that we need to put these in,
+ we will only do so for multidimensional arrays. After all, we don't
+ want to waste space in the .debug section now do we?) */
+
+#ifdef USE_ORDERING_ATTRIBUTE
+ ordering_attribute (ORD_row_major);
+#endif /* defined(USE_ORDERING_ATTRIBUTE) */
+
+ subscript_data_attribute (type);
+}
+
+static void
+output_set_type_die (arg)
+ register void *arg;
+{
+ register tree type = arg;
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_set_type);
+ sibling_attribute ();
+ equate_type_number_to_die_number (type);
+ member_attribute (TYPE_CONTEXT (type));
+ type_attribute (TREE_TYPE (type), 0, 0);
+}
+
+#if 0
+/* Implement this when there is a GNU FORTRAN or GNU Ada front end. */
+static void
+output_entry_point_die (arg)
+ register void *arg;
+{
+ register tree decl = arg;
+ register tree origin = decl_ultimate_origin (decl);
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_entry_point);
+ sibling_attribute ();
+ dienum_push ();
+ if (origin != NULL)
+ abstract_origin_attribute (origin);
+ else
+ {
+ name_and_src_coords_attributes (decl);
+ member_attribute (DECL_CONTEXT (decl));
+ type_attribute (TREE_TYPE (TREE_TYPE (decl)), 0, 0);
+ }
+ if (DECL_ABSTRACT (decl))
+ equate_decl_number_to_die_number (decl);
+ else
+ low_pc_attribute (function_start_label (decl));
+}
+#endif
+
+/* Output a DIE to represent an inlined instance of an enumeration type. */
+
+static void
+output_inlined_enumeration_type_die (arg)
+ register void *arg;
+{
+ register tree type = arg;
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_enumeration_type);
+ sibling_attribute ();
+ assert (TREE_ASM_WRITTEN (type));
+ abstract_origin_attribute (type);
+}
+
+/* Output a DIE to represent an inlined instance of a structure type. */
+
+static void
+output_inlined_structure_type_die (arg)
+ register void *arg;
+{
+ register tree type = arg;
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_structure_type);
+ sibling_attribute ();
+ assert (TREE_ASM_WRITTEN (type));
+ abstract_origin_attribute (type);
+}
+
+/* Output a DIE to represent an inlined instance of a union type. */
+
+static void
+output_inlined_union_type_die (arg)
+ register void *arg;
+{
+ register tree type = arg;
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_union_type);
+ sibling_attribute ();
+ assert (TREE_ASM_WRITTEN (type));
+ abstract_origin_attribute (type);
+}
+
+/* Output a DIE to represent an enumeration type. Note that these DIEs
+ include all of the information about the enumeration values also.
+ This information is encoded into the element_list attribute. */
+
+static void
+output_enumeration_type_die (arg)
+ register void *arg;
+{
+ register tree type = arg;
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_enumeration_type);
+ sibling_attribute ();
+ equate_type_number_to_die_number (type);
+ name_attribute (type_tag (type));
+ member_attribute (TYPE_CONTEXT (type));
+
+ /* Handle a GNU C/C++ extension, i.e. incomplete enum types. If the
+ given enum type is incomplete, do not generate the AT_byte_size
+ attribute or the AT_element_list attribute. */
+
+ if (TYPE_SIZE (type))
+ {
+ byte_size_attribute (type);
+ element_list_attribute (TYPE_FIELDS (type));
+ }
+}
+
+/* Output a DIE to represent either a real live formal parameter decl or
+ to represent just the type of some formal parameter position in some
+ function type.
+
+ Note that this routine is a bit unusual because its argument may be
+ a ..._DECL node (i.e. either a PARM_DECL or perhaps a VAR_DECL which
+ represents an inlining of some PARM_DECL) or else some sort of a
+ ..._TYPE node. If it's the former then this function is being called
+ to output a DIE to represent a formal parameter object (or some inlining
+ thereof). If it's the latter, then this function is only being called
+ to output a TAG_formal_parameter DIE to stand as a placeholder for some
+ formal argument type of some subprogram type. */
+
+static void
+output_formal_parameter_die (arg)
+ register void *arg;
+{
+ register tree node = arg;
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_formal_parameter);
+ sibling_attribute ();
+
+ switch (TREE_CODE_CLASS (TREE_CODE (node)))
+ {
+ case 'd': /* We were called with some kind of a ..._DECL node. */
+ {
+ register tree origin = decl_ultimate_origin (node);
+
+ if (origin != NULL)
+ abstract_origin_attribute (origin);
+ else
+ {
+ name_and_src_coords_attributes (node);
+ type_attribute (TREE_TYPE (node),
+ TREE_READONLY (node), TREE_THIS_VOLATILE (node));
+ }
+ if (DECL_ABSTRACT (node))
+ equate_decl_number_to_die_number (node);
+ else
+ location_or_const_value_attribute (node);
+ }
+ break;
+
+ case 't': /* We were called with some kind of a ..._TYPE node. */
+ type_attribute (node, 0, 0);
+ break;
+
+ default:
+ abort (); /* Should never happen. */
+ }
+}
+
+/* Output a DIE to represent a declared function (either file-scope
+ or block-local) which has "external linkage" (according to ANSI-C). */
+
+static void
+output_global_subroutine_die (arg)
+ register void *arg;
+{
+ register tree decl = arg;
+ register tree origin = decl_ultimate_origin (decl);
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_global_subroutine);
+ sibling_attribute ();
+ dienum_push ();
+ if (origin != NULL)
+ abstract_origin_attribute (origin);
+ else
+ {
+ register tree type = TREE_TYPE (decl);
+
+ name_and_src_coords_attributes (decl);
+ inline_attribute (decl);
+ prototyped_attribute (type);
+ member_attribute (DECL_CONTEXT (decl));
+ type_attribute (TREE_TYPE (type), 0, 0);
+ pure_or_virtual_attribute (decl);
+ }
+ if (DECL_ABSTRACT (decl))
+ equate_decl_number_to_die_number (decl);
+ else
+ {
+ if (! DECL_EXTERNAL (decl))
+ {
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ low_pc_attribute (function_start_label (decl));
+ sprintf (label, FUNC_END_LABEL_FMT, current_funcdef_number);
+ high_pc_attribute (label);
+ sprintf (label, BODY_BEGIN_LABEL_FMT, current_funcdef_number);
+ body_begin_attribute (label);
+ sprintf (label, BODY_END_LABEL_FMT, current_funcdef_number);
+ body_end_attribute (label);
+ }
+ }
+}
+
+/* Output a DIE to represent a declared data object (either file-scope
+ or block-local) which has "external linkage" (according to ANSI-C). */
+
+static void
+output_global_variable_die (arg)
+ register void *arg;
+{
+ register tree decl = arg;
+ register tree origin = decl_ultimate_origin (decl);
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_global_variable);
+ sibling_attribute ();
+ if (origin != NULL)
+ abstract_origin_attribute (origin);
+ else
+ {
+ name_and_src_coords_attributes (decl);
+ member_attribute (DECL_CONTEXT (decl));
+ type_attribute (TREE_TYPE (decl),
+ TREE_READONLY (decl), TREE_THIS_VOLATILE (decl));
+ }
+ if (DECL_ABSTRACT (decl))
+ equate_decl_number_to_die_number (decl);
+ else
+ {
+ if (!DECL_EXTERNAL (decl))
+ location_or_const_value_attribute (decl);
+ }
+}
+
+static void
+output_label_die (arg)
+ register void *arg;
+{
+ register tree decl = arg;
+ register tree origin = decl_ultimate_origin (decl);
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_label);
+ sibling_attribute ();
+ if (origin != NULL)
+ abstract_origin_attribute (origin);
+ else
+ name_and_src_coords_attributes (decl);
+ if (DECL_ABSTRACT (decl))
+ equate_decl_number_to_die_number (decl);
+ else
+ {
+ register rtx insn = DECL_RTL (decl);
+
+ if (GET_CODE (insn) == CODE_LABEL)
+ {
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ /* When optimization is enabled (via -O) some parts of the compiler
+ (e.g. jump.c and cse.c) may try to delete CODE_LABEL insns which
+ represent source-level labels which were explicitly declared by
+ the user. This really shouldn't be happening though, so catch
+ it if it ever does happen. */
+
+ if (INSN_DELETED_P (insn))
+ abort (); /* Should never happen. */
+
+ sprintf (label, INSN_LABEL_FMT, current_funcdef_number,
+ (unsigned) INSN_UID (insn));
+ low_pc_attribute (label);
+ }
+ }
+}
+
+static void
+output_lexical_block_die (arg)
+ register void *arg;
+{
+ register tree stmt = arg;
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_lexical_block);
+ sibling_attribute ();
+ dienum_push ();
+ if (! BLOCK_ABSTRACT (stmt))
+ {
+ char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
+ char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ sprintf (begin_label, BLOCK_BEGIN_LABEL_FMT, next_block_number);
+ low_pc_attribute (begin_label);
+ sprintf (end_label, BLOCK_END_LABEL_FMT, next_block_number);
+ high_pc_attribute (end_label);
+ }
+}
+
+static void
+output_inlined_subroutine_die (arg)
+ register void *arg;
+{
+ register tree stmt = arg;
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_inlined_subroutine);
+ sibling_attribute ();
+ dienum_push ();
+ abstract_origin_attribute (block_ultimate_origin (stmt));
+ if (! BLOCK_ABSTRACT (stmt))
+ {
+ char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
+ char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ sprintf (begin_label, BLOCK_BEGIN_LABEL_FMT, next_block_number);
+ low_pc_attribute (begin_label);
+ sprintf (end_label, BLOCK_END_LABEL_FMT, next_block_number);
+ high_pc_attribute (end_label);
+ }
+}
+
+/* Output a DIE to represent a declared data object (either file-scope
+ or block-local) which has "internal linkage" (according to ANSI-C). */
+
+static void
+output_local_variable_die (arg)
+ register void *arg;
+{
+ register tree decl = arg;
+ register tree origin = decl_ultimate_origin (decl);
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_local_variable);
+ sibling_attribute ();
+ if (origin != NULL)
+ abstract_origin_attribute (origin);
+ else
+ {
+ name_and_src_coords_attributes (decl);
+ member_attribute (DECL_CONTEXT (decl));
+ type_attribute (TREE_TYPE (decl),
+ TREE_READONLY (decl), TREE_THIS_VOLATILE (decl));
+ }
+ if (DECL_ABSTRACT (decl))
+ equate_decl_number_to_die_number (decl);
+ else
+ location_or_const_value_attribute (decl);
+}
+
+static void
+output_member_die (arg)
+ register void *arg;
+{
+ register tree decl = arg;
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_member);
+ sibling_attribute ();
+ name_and_src_coords_attributes (decl);
+ member_attribute (DECL_CONTEXT (decl));
+ type_attribute (member_declared_type (decl),
+ TREE_READONLY (decl), TREE_THIS_VOLATILE (decl));
+ if (DECL_BIT_FIELD_TYPE (decl)) /* If this is a bit field... */
+ {
+ byte_size_attribute (decl);
+ bit_size_attribute (decl);
+ bit_offset_attribute (decl);
+ }
+ data_member_location_attribute (decl);
+}
+
+#if 0
+/* Don't generate either pointer_type DIEs or reference_type DIEs. Use
+ modified types instead.
+
+ We keep this code here just in case these types of DIEs may be needed
+ to represent certain things in other languages (e.g. Pascal) someday.
+*/
+
+static void
+output_pointer_type_die (arg)
+ register void *arg;
+{
+ register tree type = arg;
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_pointer_type);
+ sibling_attribute ();
+ equate_type_number_to_die_number (type);
+ member_attribute (TYPE_CONTEXT (type));
+ type_attribute (TREE_TYPE (type), 0, 0);
+}
+
+static void
+output_reference_type_die (arg)
+ register void *arg;
+{
+ register tree type = arg;
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_reference_type);
+ sibling_attribute ();
+ equate_type_number_to_die_number (type);
+ member_attribute (TYPE_CONTEXT (type));
+ type_attribute (TREE_TYPE (type), 0, 0);
+}
+#endif
+
+static void
+output_ptr_to_mbr_type_die (arg)
+ register void *arg;
+{
+ register tree type = arg;
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_ptr_to_member_type);
+ sibling_attribute ();
+ equate_type_number_to_die_number (type);
+ member_attribute (TYPE_CONTEXT (type));
+ containing_type_attribute (TYPE_OFFSET_BASETYPE (type));
+ type_attribute (TREE_TYPE (type), 0, 0);
+}
+
+static void
+output_compile_unit_die (arg)
+ register void *arg;
+{
+ register char *main_input_filename = arg;
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_compile_unit);
+ sibling_attribute ();
+ dienum_push ();
+ name_attribute (main_input_filename);
+
+ {
+ char producer[250];
+
+ sprintf (producer, "%s %s", language_string, version_string);
+ producer_attribute (producer);
+ }
+
+ if (strcmp (language_string, "GNU C++") == 0)
+ language_attribute (LANG_C_PLUS_PLUS);
+ else if (strcmp (language_string, "GNU Ada") == 0)
+ language_attribute (LANG_ADA83);
+ else if (flag_traditional)
+ language_attribute (LANG_C);
+ else
+ language_attribute (LANG_C89);
+ low_pc_attribute (TEXT_BEGIN_LABEL);
+ high_pc_attribute (TEXT_END_LABEL);
+ if (debug_info_level >= DINFO_LEVEL_NORMAL)
+ stmt_list_attribute (LINE_BEGIN_LABEL);
+ last_filename = xstrdup (main_input_filename);
+
+ {
+ char *wd = getpwd ();
+ if (wd)
+ comp_dir_attribute (wd);
+ }
+
+ if (debug_info_level >= DINFO_LEVEL_NORMAL)
+ {
+ sf_names_attribute (SFNAMES_BEGIN_LABEL);
+ src_info_attribute (SRCINFO_BEGIN_LABEL);
+ if (debug_info_level >= DINFO_LEVEL_VERBOSE)
+ mac_info_attribute (MACINFO_BEGIN_LABEL);
+ }
+}
+
+static void
+output_string_type_die (arg)
+ register void *arg;
+{
+ register tree type = arg;
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_string_type);
+ sibling_attribute ();
+ member_attribute (TYPE_CONTEXT (type));
+
+ /* Fudge the string length attribute for now. */
+
+ string_length_attribute (TYPE_MAX_VALUE (TYPE_DOMAIN (type)));
+}
+
+static void
+output_structure_type_die (arg)
+ register void *arg;
+{
+ register tree type = arg;
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_structure_type);
+ sibling_attribute ();
+ equate_type_number_to_die_number (type);
+ name_attribute (type_tag (type));
+ member_attribute (TYPE_CONTEXT (type));
+
+ /* If this type has been completed, then give it a byte_size attribute
+ and prepare to give a list of members. Otherwise, don't do either of
+ these things. In the latter case, we will not be generating a list
+ of members (since we don't have any idea what they might be for an
+ incomplete type). */
+
+ if (TYPE_SIZE (type))
+ {
+ dienum_push ();
+ byte_size_attribute (type);
+ }
+}
+
+/* Output a DIE to represent a declared function (either file-scope
+ or block-local) which has "internal linkage" (according to ANSI-C). */
+
+static void
+output_local_subroutine_die (arg)
+ register void *arg;
+{
+ register tree decl = arg;
+ register tree origin = decl_ultimate_origin (decl);
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_subroutine);
+ sibling_attribute ();
+ dienum_push ();
+ if (origin != NULL)
+ abstract_origin_attribute (origin);
+ else
+ {
+ register tree type = TREE_TYPE (decl);
+
+ name_and_src_coords_attributes (decl);
+ inline_attribute (decl);
+ prototyped_attribute (type);
+ member_attribute (DECL_CONTEXT (decl));
+ type_attribute (TREE_TYPE (type), 0, 0);
+ pure_or_virtual_attribute (decl);
+ }
+ if (DECL_ABSTRACT (decl))
+ equate_decl_number_to_die_number (decl);
+ else
+ {
+ /* Avoid getting screwed up in cases where a function was declared
+ static but where no definition was ever given for it. */
+
+ if (TREE_ASM_WRITTEN (decl))
+ {
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+ low_pc_attribute (function_start_label (decl));
+ sprintf (label, FUNC_END_LABEL_FMT, current_funcdef_number);
+ high_pc_attribute (label);
+ sprintf (label, BODY_BEGIN_LABEL_FMT, current_funcdef_number);
+ body_begin_attribute (label);
+ sprintf (label, BODY_END_LABEL_FMT, current_funcdef_number);
+ body_end_attribute (label);
+ }
+ }
+}
+
+static void
+output_subroutine_type_die (arg)
+ register void *arg;
+{
+ register tree type = arg;
+ register tree return_type = TREE_TYPE (type);
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_subroutine_type);
+ sibling_attribute ();
+ dienum_push ();
+ equate_type_number_to_die_number (type);
+ prototyped_attribute (type);
+ member_attribute (TYPE_CONTEXT (type));
+ type_attribute (return_type, 0, 0);
+}
+
+static void
+output_typedef_die (arg)
+ register void *arg;
+{
+ register tree decl = arg;
+ register tree origin = decl_ultimate_origin (decl);
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_typedef);
+ sibling_attribute ();
+ if (origin != NULL)
+ abstract_origin_attribute (origin);
+ else
+ {
+ name_and_src_coords_attributes (decl);
+ member_attribute (DECL_CONTEXT (decl));
+ type_attribute (TREE_TYPE (decl),
+ TREE_READONLY (decl), TREE_THIS_VOLATILE (decl));
+ }
+ if (DECL_ABSTRACT (decl))
+ equate_decl_number_to_die_number (decl);
+}
+
+static void
+output_union_type_die (arg)
+ register void *arg;
+{
+ register tree type = arg;
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_union_type);
+ sibling_attribute ();
+ equate_type_number_to_die_number (type);
+ name_attribute (type_tag (type));
+ member_attribute (TYPE_CONTEXT (type));
+
+ /* If this type has been completed, then give it a byte_size attribute
+ and prepare to give a list of members. Otherwise, don't do either of
+ these things. In the latter case, we will not be generating a list
+ of members (since we don't have any idea what they might be for an
+ incomplete type). */
+
+ if (TYPE_SIZE (type))
+ {
+ dienum_push ();
+ byte_size_attribute (type);
+ }
+}
+
+/* Generate a special type of DIE used as a stand-in for a trailing ellipsis
+ at the end of an (ANSI prototyped) formal parameters list. */
+
+static void
+output_unspecified_parameters_die (arg)
+ register void *arg;
+{
+ register tree decl_or_type = arg;
+
+ ASM_OUTPUT_DWARF_TAG (asm_out_file, TAG_unspecified_parameters);
+ sibling_attribute ();
+
+ /* This kludge is here only for the sake of being compatible with what
+ the USL CI5 C compiler does. The specification of Dwarf Version 1
+ doesn't say that TAG_unspecified_parameters DIEs should contain any
+ attributes other than the AT_sibling attribute, but they are certainly
+ allowed to contain additional attributes, and the CI5 compiler
+ generates AT_name, AT_fund_type, and AT_location attributes within
+ TAG_unspecified_parameters DIEs which appear in the child lists for
+ DIEs representing function definitions, so we do likewise here. */
+
+ if (TREE_CODE (decl_or_type) == FUNCTION_DECL && DECL_INITIAL (decl_or_type))
+ {
+ name_attribute ("...");
+ fund_type_attribute (FT_pointer);
+ /* location_attribute (?); */
+ }
+}
+
+static void
+output_padded_null_die (arg)
+ register void *arg;
+{
+ ASM_OUTPUT_ALIGN (asm_out_file, 2); /* 2**2 == 4 */
+}
+
+/*************************** end of DIEs *********************************/
+
+/* Generate some type of DIE. This routine generates the generic outer
+ wrapper stuff which goes around all types of DIE's (regardless of their
+ TAGs. All forms of DIEs start with a DIE-specific label, followed by a
+ DIE-length word, followed by the guts of the DIE itself. After the guts
+ of the DIE, there must always be a terminator label for the DIE. */
+
+static void
+output_die (die_specific_output_function, param)
+ register void (*die_specific_output_function)();
+ register void *param;
+{
+ char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
+ char end_label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ current_dienum = NEXT_DIE_NUM;
+ NEXT_DIE_NUM = next_unused_dienum;
+
+ sprintf (begin_label, DIE_BEGIN_LABEL_FMT, current_dienum);
+ sprintf (end_label, DIE_END_LABEL_FMT, current_dienum);
+
+ /* Write a label which will act as the name for the start of this DIE. */
+
+ ASM_OUTPUT_LABEL (asm_out_file, begin_label);
+
+ /* Write the DIE-length word. */
+
+ ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, end_label, begin_label);
+
+ /* Fill in the guts of the DIE. */
+
+ next_unused_dienum++;
+ die_specific_output_function (param);
+
+ /* Write a label which will act as the name for the end of this DIE. */
+
+ ASM_OUTPUT_LABEL (asm_out_file, end_label);
+}
+
+static void
+end_sibling_chain ()
+{
+ char begin_label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ current_dienum = NEXT_DIE_NUM;
+ NEXT_DIE_NUM = next_unused_dienum;
+
+ sprintf (begin_label, DIE_BEGIN_LABEL_FMT, current_dienum);
+
+ /* Write a label which will act as the name for the start of this DIE. */
+
+ ASM_OUTPUT_LABEL (asm_out_file, begin_label);
+
+ /* Write the DIE-length word. */
+
+ ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 4);
+
+ dienum_pop ();
+}
+
+/* Generate a list of nameless TAG_formal_parameter DIEs (and perhaps a
+ TAG_unspecified_parameters DIE) to represent the types of the formal
+ parameters as specified in some function type specification (except
+ for those which appear as part of a function *definition*).
+
+ Note that we must be careful here to output all of the parameter DIEs
+ *before* we output any DIEs needed to represent the types of the formal
+ parameters. This keeps svr4 SDB happy because it (incorrectly) thinks
+ that the first non-parameter DIE it sees ends the formal parameter list.
+*/
+
+static void
+output_formal_types (function_or_method_type)
+ register tree function_or_method_type;
+{
+ register tree link;
+ register tree formal_type = NULL;
+ register tree first_parm_type = TYPE_ARG_TYPES (function_or_method_type);
+
+ /* In the case where we are generating a formal types list for a C++
+ non-static member function type, skip over the first thing on the
+ TYPE_ARG_TYPES list because it only represents the type of the
+ hidden `this pointer'. The debugger should be able to figure
+ out (without being explicitly told) that this non-static member
+ function type takes a `this pointer' and should be able to figure
+ what the type of that hidden parameter is from the AT_member
+ attribute of the parent TAG_subroutine_type DIE. */
+
+ if (TREE_CODE (function_or_method_type) == METHOD_TYPE)
+ first_parm_type = TREE_CHAIN (first_parm_type);
+
+ /* Make our first pass over the list of formal parameter types and output
+ a TAG_formal_parameter DIE for each one. */
+
+ for (link = first_parm_type; link; link = TREE_CHAIN (link))
+ {
+ formal_type = TREE_VALUE (link);
+ if (formal_type == void_type_node)
+ break;
+
+ /* Output a (nameless) DIE to represent the formal parameter itself. */
+
+ output_die (output_formal_parameter_die, formal_type);
+ }
+
+ /* If this function type has an ellipsis, add a TAG_unspecified_parameters
+ DIE to the end of the parameter list. */
+
+ if (formal_type != void_type_node)
+ output_die (output_unspecified_parameters_die, function_or_method_type);
+
+ /* Make our second (and final) pass over the list of formal parameter types
+ and output DIEs to represent those types (as necessary). */
+
+ for (link = TYPE_ARG_TYPES (function_or_method_type);
+ link;
+ link = TREE_CHAIN (link))
+ {
+ formal_type = TREE_VALUE (link);
+ if (formal_type == void_type_node)
+ break;
+
+ output_type (formal_type, function_or_method_type);
+ }
+}
+
+/* Remember a type in the pending_types_list. */
+
+static void
+pend_type (type)
+ register tree type;
+{
+ if (pending_types == pending_types_allocated)
+ {
+ pending_types_allocated += PENDING_TYPES_INCREMENT;
+ pending_types_list
+ = (tree *) xrealloc (pending_types_list,
+ sizeof (tree) * pending_types_allocated);
+ }
+ pending_types_list[pending_types++] = type;
+
+ /* Mark the pending type as having been output already (even though
+ it hasn't been). This prevents the type from being added to the
+ pending_types_list more than once. */
+
+ TREE_ASM_WRITTEN (type) = 1;
+}
+
+/* Return non-zero if it is legitimate to output DIEs to represent a
+ given type while we are generating the list of child DIEs for some
+ DIE (e.g. a function or lexical block DIE) associated with a given scope.
+
+ See the comments within the function for a description of when it is
+ considered legitimate to output DIEs for various kinds of types.
+
+ Note that TYPE_CONTEXT(type) may be NULL (to indicate global scope)
+ or it may point to a BLOCK node (for types local to a block), or to a
+ FUNCTION_DECL node (for types local to the heading of some function
+ definition), or to a FUNCTION_TYPE node (for types local to the
+ prototyped parameter list of a function type specification), or to a
+ RECORD_TYPE, UNION_TYPE, or QUAL_UNION_TYPE node
+ (in the case of C++ nested types).
+
+ The `scope' parameter should likewise be NULL or should point to a
+ BLOCK node, a FUNCTION_DECL node, a FUNCTION_TYPE node, a RECORD_TYPE
+ node, a UNION_TYPE node, or a QUAL_UNION_TYPE node.
+
+ This function is used only for deciding when to "pend" and when to
+ "un-pend" types to/from the pending_types_list.
+
+ Note that we sometimes make use of this "type pending" feature in a
+ rather twisted way to temporarily delay the production of DIEs for the
+ types of formal parameters. (We do this just to make svr4 SDB happy.)
+ It order to delay the production of DIEs representing types of formal
+ parameters, callers of this function supply `fake_containing_scope' as
+ the `scope' parameter to this function. Given that fake_containing_scope
+ is a tagged type which is *not* the containing scope for *any* other type,
+ the desired effect is achieved, i.e. output of DIEs representing types
+ is temporarily suspended, and any type DIEs which would have otherwise
+ been output are instead placed onto the pending_types_list. Later on,
+ we force these (temporarily pended) types to be output simply by calling
+ `output_pending_types_for_scope' with an actual argument equal to the
+ true scope of the types we temporarily pended.
+*/
+
+inline int
+type_ok_for_scope (type, scope)
+ register tree type;
+ register tree scope;
+{
+ /* Tagged types (i.e. struct, union, and enum types) must always be
+ output only in the scopes where they actually belong (or else the
+ scoping of their own tag names and the scoping of their member
+ names will be incorrect). Non-tagged-types on the other hand can
+ generally be output anywhere, except that svr4 SDB really doesn't
+ want to see them nested within struct or union types, so here we
+ say it is always OK to immediately output any such a (non-tagged)
+ type, so long as we are not within such a context. Note that the
+ only kinds of non-tagged types which we will be dealing with here
+ (for C and C++ anyway) will be array types and function types. */
+
+ return is_tagged_type (type)
+ ? (TYPE_CONTEXT (type) == scope)
+ : (scope == NULL_TREE || ! is_tagged_type (scope));
+}
+
+/* Output any pending types (from the pending_types list) which we can output
+ now (taking into account the scope that we are working on now).
+
+ For each type output, remove the given type from the pending_types_list
+ *before* we try to output it.
+
+ Note that we have to process the list in beginning-to-end order,
+ because the call made here to output_type may cause yet more types
+ to be added to the end of the list, and we may have to output some
+ of them too.
+*/
+
+static void
+output_pending_types_for_scope (containing_scope)
+ register tree containing_scope;
+{
+ register unsigned i;
+
+ for (i = 0; i < pending_types; )
+ {
+ register tree type = pending_types_list[i];
+
+ if (type_ok_for_scope (type, containing_scope))
+ {
+ register tree *mover;
+ register tree *limit;
+
+ pending_types--;
+ limit = &pending_types_list[pending_types];
+ for (mover = &pending_types_list[i]; mover < limit; mover++)
+ *mover = *(mover+1);
+
+ /* Un-mark the type as having been output already (because it
+ hasn't been, really). Then call output_type to generate a
+ Dwarf representation of it. */
+
+ TREE_ASM_WRITTEN (type) = 0;
+ output_type (type, containing_scope);
+
+ /* Don't increment the loop counter in this case because we
+ have shifted all of the subsequent pending types down one
+ element in the pending_types_list array. */
+ }
+ else
+ i++;
+ }
+}
+
+static void
+output_type (type, containing_scope)
+ register tree type;
+ register tree containing_scope;
+{
+ if (type == 0 || type == error_mark_node)
+ return;
+
+ /* We are going to output a DIE to represent the unqualified version of
+ of this type (i.e. without any const or volatile qualifiers) so get
+ the main variant (i.e. the unqualified version) of this type now. */
+
+ type = type_main_variant (type);
+
+ if (TREE_ASM_WRITTEN (type))
+ return;
+
+ /* Don't generate any DIEs for this type now unless it is OK to do so
+ (based upon what `type_ok_for_scope' tells us). */
+
+ if (! type_ok_for_scope (type, containing_scope))
+ {
+ pend_type (type);
+ return;
+ }
+
+ switch (TREE_CODE (type))
+ {
+ case ERROR_MARK:
+ break;
+
+ case POINTER_TYPE:
+ case REFERENCE_TYPE:
+ /* For these types, all that is required is that we output a DIE
+ (or a set of DIEs) to represent the "basis" type. */
+ output_type (TREE_TYPE (type), containing_scope);
+ break;
+
+ case OFFSET_TYPE:
+ /* This code is used for C++ pointer-to-data-member types. */
+ /* Output a description of the relevant class type. */
+ output_type (TYPE_OFFSET_BASETYPE (type), containing_scope);
+ /* Output a description of the type of the object pointed to. */
+ output_type (TREE_TYPE (type), containing_scope);
+ /* Now output a DIE to represent this pointer-to-data-member type
+ itself. */
+ output_die (output_ptr_to_mbr_type_die, type);
+ break;
+
+ case SET_TYPE:
+ output_type (TYPE_DOMAIN (type), containing_scope);
+ output_die (output_set_type_die, type);
+ break;
+
+ case FILE_TYPE:
+ output_type (TREE_TYPE (type), containing_scope);
+ abort (); /* No way to represent these in Dwarf yet! */
+ break;
+
+ case FUNCTION_TYPE:
+ /* Force out return type (in case it wasn't forced out already). */
+ output_type (TREE_TYPE (type), containing_scope);
+ output_die (output_subroutine_type_die, type);
+ output_formal_types (type);
+ end_sibling_chain ();
+ break;
+
+ case METHOD_TYPE:
+ /* Force out return type (in case it wasn't forced out already). */
+ output_type (TREE_TYPE (type), containing_scope);
+ output_die (output_subroutine_type_die, type);
+ output_formal_types (type);
+ end_sibling_chain ();
+ break;
+
+ case ARRAY_TYPE:
+ if (TYPE_STRING_FLAG (type) && TREE_CODE(TREE_TYPE(type)) == CHAR_TYPE)
+ {
+ output_type (TREE_TYPE (type), containing_scope);
+ output_die (output_string_type_die, type);
+ }
+ else
+ {
+ register tree element_type;
+
+ element_type = TREE_TYPE (type);
+ while (TREE_CODE (element_type) == ARRAY_TYPE)
+ element_type = TREE_TYPE (element_type);
+
+ output_type (element_type, containing_scope);
+ output_die (output_array_type_die, type);
+ }
+ break;
+
+ case ENUMERAL_TYPE:
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ case QUAL_UNION_TYPE:
+
+ /* For a non-file-scope tagged type, we can always go ahead and
+ output a Dwarf description of this type right now, even if
+ the type in question is still incomplete, because if this
+ local type *was* ever completed anywhere within its scope,
+ that complete definition would already have been attached to
+ this RECORD_TYPE, UNION_TYPE, QUAL_UNION_TYPE or ENUMERAL_TYPE
+ node by the time we reach this point. That's true because of the
+ way the front-end does its processing of file-scope declarations (of
+ functions and class types) within which other types might be
+ nested. The C and C++ front-ends always gobble up such "local
+ scope" things en-mass before they try to output *any* debugging
+ information for any of the stuff contained inside them and thus,
+ we get the benefit here of what is (in effect) a pre-resolution
+ of forward references to tagged types in local scopes.
+
+ Note however that for file-scope tagged types we cannot assume
+ that such pre-resolution of forward references has taken place.
+ A given file-scope tagged type may appear to be incomplete when
+ we reach this point, but it may yet be given a full definition
+ (at file-scope) later on during compilation. In order to avoid
+ generating a premature (and possibly incorrect) set of Dwarf
+ DIEs for such (as yet incomplete) file-scope tagged types, we
+ generate nothing at all for as-yet incomplete file-scope tagged
+ types here unless we are making our special "finalization" pass
+ for file-scope things at the very end of compilation. At that
+ time, we will certainly know as much about each file-scope tagged
+ type as we are ever going to know, so at that point in time, we
+ can safely generate correct Dwarf descriptions for these file-
+ scope tagged types.
+ */
+
+ if (TYPE_SIZE (type) == 0 && TYPE_CONTEXT (type) == NULL && !finalizing)
+ return; /* EARLY EXIT! Avoid setting TREE_ASM_WRITTEN. */
+
+ /* Prevent infinite recursion in cases where the type of some
+ member of this type is expressed in terms of this type itself. */
+
+ TREE_ASM_WRITTEN (type) = 1;
+
+ /* Output a DIE to represent the tagged type itself. */
+
+ switch (TREE_CODE (type))
+ {
+ case ENUMERAL_TYPE:
+ output_die (output_enumeration_type_die, type);
+ return; /* a special case -- nothing left to do so just return */
+
+ case RECORD_TYPE:
+ output_die (output_structure_type_die, type);
+ break;
+
+ case UNION_TYPE:
+ case QUAL_UNION_TYPE:
+ output_die (output_union_type_die, type);
+ break;
+
+ default:
+ abort (); /* Should never happen. */
+ }
+
+ /* If this is not an incomplete type, output descriptions of
+ each of its members.
+
+ Note that as we output the DIEs necessary to represent the
+ members of this record or union type, we will also be trying
+ to output DIEs to represent the *types* of those members.
+ However the `output_type' function (above) will specifically
+ avoid generating type DIEs for member types *within* the list
+ of member DIEs for this (containing) type execpt for those
+ types (of members) which are explicitly marked as also being
+ members of this (containing) type themselves. The g++ front-
+ end can force any given type to be treated as a member of some
+ other (containing) type by setting the TYPE_CONTEXT of the
+ given (member) type to point to the TREE node representing the
+ appropriate (containing) type.
+ */
+
+ if (TYPE_SIZE (type))
+ {
+ {
+ register tree normal_member;
+
+ /* First output info about the data members and type members. */
+
+ for (normal_member = TYPE_FIELDS (type);
+ normal_member;
+ normal_member = TREE_CHAIN (normal_member))
+ output_decl (normal_member, type);
+ }
+
+ {
+ register tree vec_base;
+
+ /* Now output info about the function members (if any). */
+
+ vec_base = TYPE_METHODS (type);
+ if (vec_base)
+ {
+ register tree first_func_member = TREE_VEC_ELT (vec_base, 0);
+ register tree func_member;
+
+ /* This isn't documented, but the first element of the
+ vector of member functions can be NULL in cases where
+ the class type in question didn't have either a
+ constructor or a destructor declared for it. We have
+ to make allowances for that here. */
+
+ if (first_func_member == NULL)
+ first_func_member = TREE_VEC_ELT (vec_base, 1);
+
+ for (func_member = first_func_member;
+ func_member;
+ func_member = TREE_CHAIN (func_member))
+ output_decl (func_member, type);
+ }
+ }
+
+ /* RECORD_TYPEs, UNION_TYPEs, and QUAL_UNION_TYPEs are themselves
+ scopes (at least in C++) so we must now output any nested
+ pending types which are local just to this type. */
+
+ output_pending_types_for_scope (type);
+
+ end_sibling_chain (); /* Terminate member chain. */
+ }
+
+ break;
+
+ case VOID_TYPE:
+ case INTEGER_TYPE:
+ case REAL_TYPE:
+ case COMPLEX_TYPE:
+ case BOOLEAN_TYPE:
+ case CHAR_TYPE:
+ break; /* No DIEs needed for fundamental types. */
+
+ case LANG_TYPE: /* No Dwarf representation currently defined. */
+ break;
+
+ default:
+ abort ();
+ }
+
+ TREE_ASM_WRITTEN (type) = 1;
+}
+
+static void
+output_tagged_type_instantiation (type)
+ register tree type;
+{
+ if (type == 0 || type == error_mark_node)
+ return;
+
+ /* We are going to output a DIE to represent the unqualified version of
+ of this type (i.e. without any const or volatile qualifiers) so make
+ sure that we have the main variant (i.e. the unqualified version) of
+ this type now. */
+
+ assert (type == type_main_variant (type));
+
+ assert (TREE_ASM_WRITTEN (type));
+
+ switch (TREE_CODE (type))
+ {
+ case ERROR_MARK:
+ break;
+
+ case ENUMERAL_TYPE:
+ output_die (output_inlined_enumeration_type_die, type);
+ break;
+
+ case RECORD_TYPE:
+ output_die (output_inlined_structure_type_die, type);
+ break;
+
+ case UNION_TYPE:
+ case QUAL_UNION_TYPE:
+ output_die (output_inlined_union_type_die, type);
+ break;
+
+ default:
+ abort (); /* Should never happen. */
+ }
+}
+
+/* Output a TAG_lexical_block DIE followed by DIEs to represent all of
+ the things which are local to the given block. */
+
+static void
+output_block (stmt)
+ register tree stmt;
+{
+ register int must_output_die = 0;
+ register tree origin;
+ register enum tree_code origin_code;
+
+ /* Ignore blocks never really used to make RTL. */
+
+ if (! stmt || ! TREE_USED (stmt))
+ return;
+
+ /* Determine the "ultimate origin" of this block. This block may be an
+ inlined instance of an inlined instance of inline function, so we
+ have to trace all of the way back through the origin chain to find
+ out what sort of node actually served as the original seed for the
+ creation of the current block. */
+
+ origin = block_ultimate_origin (stmt);
+ origin_code = (origin != NULL) ? TREE_CODE (origin) : ERROR_MARK;
+
+ /* Determine if we need to output any Dwarf DIEs at all to represent this
+ block. */
+
+ if (origin_code == FUNCTION_DECL)
+ /* The outer scopes for inlinings *must* always be represented. We
+ generate TAG_inlined_subroutine DIEs for them. (See below.) */
+ must_output_die = 1;
+ else
+ {
+ /* In the case where the current block represents an inlining of the
+ "body block" of an inline function, we must *NOT* output any DIE
+ for this block because we have already output a DIE to represent
+ the whole inlined function scope and the "body block" of any
+ function doesn't really represent a different scope according to
+ ANSI C rules. So we check here to make sure that this block does
+ not represent a "body block inlining" before trying to set the
+ `must_output_die' flag. */
+
+ if (origin == NULL || ! is_body_block (origin))
+ {
+ /* Determine if this block directly contains any "significant"
+ local declarations which we will need to output DIEs for. */
+
+ if (debug_info_level > DINFO_LEVEL_TERSE)
+ /* We are not in terse mode so *any* local declaration counts
+ as being a "significant" one. */
+ must_output_die = (BLOCK_VARS (stmt) != NULL);
+ else
+ {
+ register tree decl;
+
+ /* We are in terse mode, so only local (nested) function
+ definitions count as "significant" local declarations. */
+
+ for (decl = BLOCK_VARS (stmt); decl; decl = TREE_CHAIN (decl))
+ if (TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl))
+ {
+ must_output_die = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ /* It would be a waste of space to generate a Dwarf TAG_lexical_block
+ DIE for any block which contains no significant local declarations
+ at all. Rather, in such cases we just call `output_decls_for_scope'
+ so that any needed Dwarf info for any sub-blocks will get properly
+ generated. Note that in terse mode, our definition of what constitutes
+ a "significant" local declaration gets restricted to include only
+ inlined function instances and local (nested) function definitions. */
+
+ if (must_output_die)
+ {
+ output_die ((origin_code == FUNCTION_DECL)
+ ? output_inlined_subroutine_die
+ : output_lexical_block_die,
+ stmt);
+ output_decls_for_scope (stmt);
+ end_sibling_chain ();
+ }
+ else
+ output_decls_for_scope (stmt);
+}
+
+/* Output all of the decls declared within a given scope (also called
+ a `binding contour') and (recursively) all of it's sub-blocks. */
+
+static void
+output_decls_for_scope (stmt)
+ register tree stmt;
+{
+ /* Ignore blocks never really used to make RTL. */
+
+ if (! stmt || ! TREE_USED (stmt))
+ return;
+
+ if (! BLOCK_ABSTRACT (stmt))
+ next_block_number++;
+
+ /* Output the DIEs to represent all of the data objects, functions,
+ typedefs, and tagged types declared directly within this block
+ but not within any nested sub-blocks. */
+
+ {
+ register tree decl;
+
+ for (decl = BLOCK_VARS (stmt); decl; decl = TREE_CHAIN (decl))
+ output_decl (decl, stmt);
+ }
+
+ output_pending_types_for_scope (stmt);
+
+ /* Output the DIEs to represent all sub-blocks (and the items declared
+ therein) of this block. */
+
+ {
+ register tree subblocks;
+
+ for (subblocks = BLOCK_SUBBLOCKS (stmt);
+ subblocks;
+ subblocks = BLOCK_CHAIN (subblocks))
+ output_block (subblocks);
+ }
+}
+
+/* Output Dwarf .debug information for a decl described by DECL. */
+
+static void
+output_decl (decl, containing_scope)
+ register tree decl;
+ register tree containing_scope;
+{
+ /* Make a note of the decl node we are going to be working on. We may
+ need to give the user the source coordinates of where it appeared in
+ case we notice (later on) that something about it looks screwy. */
+
+ dwarf_last_decl = decl;
+
+ if (TREE_CODE (decl) == ERROR_MARK)
+ return;
+
+ /* If this ..._DECL node is marked to be ignored, then ignore it.
+ But don't ignore a function definition, since that would screw
+ up our count of blocks, and that it turn will completely screw up the
+ the labels we will reference in subsequent AT_low_pc and AT_high_pc
+ attributes (for subsequent blocks). */
+
+ if (DECL_IGNORED_P (decl) && TREE_CODE (decl) != FUNCTION_DECL)
+ return;
+
+ switch (TREE_CODE (decl))
+ {
+ case CONST_DECL:
+ /* The individual enumerators of an enum type get output when we
+ output the Dwarf representation of the relevant enum type itself. */
+ break;
+
+ case FUNCTION_DECL:
+ /* If we are in terse mode, don't output any DIEs to represent
+ mere function declarations. Also, if we are conforming
+ to the DWARF version 1 specification, don't output DIEs for
+ mere function declarations. */
+
+ if (DECL_INITIAL (decl) == NULL_TREE)
+#if (DWARF_VERSION > 1)
+ if (debug_info_level <= DINFO_LEVEL_TERSE)
+#endif
+ break;
+
+ /* Before we describe the FUNCTION_DECL itself, make sure that we
+ have described its return type. */
+
+ output_type (TREE_TYPE (TREE_TYPE (decl)), containing_scope);
+
+ /* If the following DIE will represent a function definition for a
+ function with "extern" linkage, output a special "pubnames" DIE
+ label just ahead of the actual DIE. A reference to this label
+ was already generated in the .debug_pubnames section sub-entry
+ for this function definition. */
+
+ if (TREE_PUBLIC (decl))
+ {
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ sprintf (label, PUB_DIE_LABEL_FMT, next_pubname_number++);
+ ASM_OUTPUT_LABEL (asm_out_file, label);
+ }
+
+ /* Now output a DIE to represent the function itself. */
+
+ output_die (TREE_PUBLIC (decl) || DECL_EXTERNAL (decl)
+ ? output_global_subroutine_die
+ : output_local_subroutine_die,
+ decl);
+
+ /* Now output descriptions of the arguments for this function.
+ This gets (unnecessarily?) complex because of the fact that
+ the DECL_ARGUMENT list for a FUNCTION_DECL doesn't indicate
+ cases where there was a trailing `...' at the end of the formal
+ parameter list. In order to find out if there was a trailing
+ ellipsis or not, we must instead look at the type associated
+ with the FUNCTION_DECL. This will be a node of type FUNCTION_TYPE.
+ If the chain of type nodes hanging off of this FUNCTION_TYPE node
+ ends with a void_type_node then there should *not* be an ellipsis
+ at the end. */
+
+ /* In the case where we are describing a mere function declaration, all
+ we need to do here (and all we *can* do here) is to describe
+ the *types* of its formal parameters. */
+
+ if (DECL_INITIAL (decl) == NULL_TREE)
+ output_formal_types (TREE_TYPE (decl));
+ else
+ {
+ register tree arg_decls = DECL_ARGUMENTS (decl);
+
+ {
+ register tree last_arg;
+
+ last_arg = (arg_decls && TREE_CODE (arg_decls) != ERROR_MARK)
+ ? tree_last (arg_decls)
+ : NULL;
+
+ /* Generate DIEs to represent all known formal parameters, but
+ don't do it if this looks like a varargs function. A given
+ function is considered to be a varargs function if (and only
+ if) its last named argument is named `__builtin_va_alist'. */
+
+ if (! last_arg
+ || ! DECL_NAME (last_arg)
+ || strcmp (IDENTIFIER_POINTER (DECL_NAME (last_arg)),
+ "__builtin_va_alist"))
+ {
+ register tree parm;
+
+ /* WARNING! Kludge zone ahead! Here we have a special
+ hack for svr4 SDB compatibility. Instead of passing the
+ current FUNCTION_DECL node as the second parameter (i.e.
+ the `containing_scope' parameter) to `output_decl' (as
+ we ought to) we instead pass a pointer to our own private
+ fake_containing_scope node. That node is a RECORD_TYPE
+ node which NO OTHER TYPE may ever actually be a member of.
+
+ This pointer will ultimately get passed into `output_type'
+ as its `containing_scope' parameter. `Output_type' will
+ then perform its part in the hack... i.e. it will pend
+ the type of the formal parameter onto the pending_types
+ list. Later on, when we are done generating the whole
+ sequence of formal parameter DIEs for this function
+ definition, we will un-pend all previously pended types
+ of formal parameters for this function definition.
+
+ This whole kludge prevents any type DIEs from being
+ mixed in with the formal parameter DIEs. That's good
+ because svr4 SDB believes that the list of formal
+ parameter DIEs for a function ends wherever the first
+ non-formal-parameter DIE appears. Thus, we have to
+ keep the formal parameter DIEs segregated. They must
+ all appear (consecutively) at the start of the list of
+ children for the DIE representing the function definition.
+ Then (and only then) may we output any additional DIEs
+ needed to represent the types of these formal parameters.
+ */
+
+ for (parm = arg_decls; parm; parm = TREE_CHAIN (parm))
+ if (TREE_CODE (parm) == PARM_DECL)
+ output_decl (parm, fake_containing_scope);
+
+ /* Now that we have finished generating all of the DIEs to
+ represent the formal parameters themselves, force out
+ any DIEs needed to represent their types. We do this
+ simply by un-pending all previously pended types which
+ can legitimately go into the chain of children DIEs for
+ the current FUNCTION_DECL. */
+
+ output_pending_types_for_scope (decl);
+ }
+ }
+
+ /* Now try to decide if we should put an ellipsis at the end. */
+
+ {
+ register int has_ellipsis = TRUE; /* default assumption */
+ register tree fn_arg_types = TYPE_ARG_TYPES (TREE_TYPE (decl));
+
+ if (fn_arg_types)
+ {
+ /* This function declaration/definition was prototyped. */
+
+ /* If the list of formal argument types ends with a
+ void_type_node, then the formals list did *not* end
+ with an ellipsis. */
+
+ if (TREE_VALUE (tree_last (fn_arg_types)) == void_type_node)
+ has_ellipsis = FALSE;
+ }
+ else
+ {
+ /* This function declaration/definition was not prototyped. */
+
+ /* Note that all non-prototyped function *declarations* are
+ assumed to represent varargs functions (until proven
+ otherwise). */
+
+ if (DECL_INITIAL (decl)) /* if this is a func definition */
+ {
+ if (!arg_decls)
+ has_ellipsis = FALSE; /* no args == (void) */
+ else
+ {
+ /* For a non-prototyped function definition which
+ declares one or more formal parameters, if the name
+ of the first formal parameter is *not*
+ __builtin_va_alist then we must assume that this
+ is *not* a varargs function. */
+
+ if (DECL_NAME (arg_decls)
+ && strcmp (IDENTIFIER_POINTER (DECL_NAME (arg_decls)),
+ "__builtin_va_alist"))
+ has_ellipsis = FALSE;
+ }
+ }
+ }
+
+ if (has_ellipsis)
+ output_die (output_unspecified_parameters_die, decl);
+ }
+ }
+
+ /* Output Dwarf info for all of the stuff within the body of the
+ function (if it has one - it may be just a declaration). */
+
+ {
+ register tree outer_scope = DECL_INITIAL (decl);
+
+ if (outer_scope && TREE_CODE (outer_scope) != ERROR_MARK)
+ {
+ /* Note that here, `outer_scope' is a pointer to the outermost
+ BLOCK node created to represent a function.
+ This outermost BLOCK actually represents the outermost
+ binding contour for the function, i.e. the contour in which
+ the function's formal parameters and labels get declared.
+
+ Curiously, it appears that the front end doesn't actually
+ put the PARM_DECL nodes for the current function onto the
+ BLOCK_VARS list for this outer scope. (They are strung
+ off of the DECL_ARGUMENTS list for the function instead.)
+ The BLOCK_VARS list for the `outer_scope' does provide us
+ with a list of the LABEL_DECL nodes for the function however,
+ and we output DWARF info for those here.
+
+ Just within the `outer_scope' there will be another BLOCK
+ node representing the function's outermost pair of curly
+ braces. We musn't generate a lexical_block DIE for this
+ outermost pair of curly braces because that is not really an
+ independent scope according to ANSI C rules. Rather, it is
+ the same scope in which the parameters were declared. */
+
+ {
+ register tree label;
+
+ for (label = BLOCK_VARS (outer_scope);
+ label;
+ label = TREE_CHAIN (label))
+ output_decl (label, outer_scope);
+ }
+
+ /* Note here that `BLOCK_SUBBLOCKS (outer_scope)' points to a
+ list of BLOCK nodes which is always only one element long.
+ That one element represents the outermost pair of curley
+ braces for the function body. */
+
+ output_decls_for_scope (BLOCK_SUBBLOCKS (outer_scope));
+
+ /* Finally, force out any pending types which are local to the
+ outermost block of this function definition. These will
+ all have a TYPE_CONTEXT which points to the FUNCTION_DECL
+ node itself. */
+
+ output_pending_types_for_scope (decl);
+ }
+ }
+
+ /* Generate a terminator for the list of stuff `owned' by this
+ function. */
+
+ end_sibling_chain ();
+
+ break;
+
+ case TYPE_DECL:
+ /* If we are in terse mode, don't generate any DIEs to represent
+ any actual typedefs. Note that even when we are in terse mode,
+ we must still output DIEs to represent those tagged types which
+ are used (directly or indirectly) in the specification of either
+ a return type or a formal parameter type of some function. */
+
+ if (debug_info_level <= DINFO_LEVEL_TERSE)
+ if (DECL_NAME (decl) != NULL
+ || ! TYPE_USED_FOR_FUNCTION (TREE_TYPE (decl)))
+ return;
+
+ /* In the special case of a null-named TYPE_DECL node (representing
+ the declaration of some type tag), if the given TYPE_DECL is
+ marked as having been instantiated from some other (original)
+ TYPE_DECL node (e.g. one which was generated within the original
+ definition of an inline function) we have to generate a special
+ (abbreviated) TAG_structure_type, TAG_union_type, or
+ TAG_enumeration-type DIE here. */
+
+ if (! DECL_NAME (decl) && DECL_ABSTRACT_ORIGIN (decl))
+ {
+ output_tagged_type_instantiation (TREE_TYPE (decl));
+ return;
+ }
+
+ output_type (TREE_TYPE (decl), containing_scope);
+
+ /* Note that unlike the gcc front end (which generates a NULL named
+ TYPE_DECL node for each complete tagged type, each array type,
+ and each function type node created) the g++ front end generates
+ a *named* TYPE_DECL node for each tagged type node created.
+ Unfortunately, these g++ TYPE_DECL nodes cause us to output many
+ superfluous and unnecessary TAG_typedef DIEs here. When g++ is
+ fixed to stop generating these superfluous named TYPE_DECL nodes,
+ the superfluous TAG_typedef DIEs will likewise cease. */
+
+ if (DECL_NAME (decl))
+ /* Output a DIE to represent the typedef itself. */
+ output_die (output_typedef_die, decl);
+ break;
+
+ case LABEL_DECL:
+ if (debug_info_level >= DINFO_LEVEL_NORMAL)
+ output_die (output_label_die, decl);
+ break;
+
+ case VAR_DECL:
+ /* If we are conforming to the DWARF version 1 specification, don't
+ generated any DIEs to represent mere external object declarations. */
+
+#if (DWARF_VERSION <= 1)
+ if (DECL_EXTERNAL (decl) && ! TREE_PUBLIC (decl))
+ break;
+#endif
+
+ /* If we are in terse mode, don't generate any DIEs to represent
+ any variable declarations or definitions. */
+
+ if (debug_info_level <= DINFO_LEVEL_TERSE)
+ break;
+
+ /* Output any DIEs that are needed to specify the type of this data
+ object. */
+
+ output_type (TREE_TYPE (decl), containing_scope);
+
+ /* If the following DIE will represent a data object definition for a
+ data object with "extern" linkage, output a special "pubnames" DIE
+ label just ahead of the actual DIE. A reference to this label
+ was already generated in the .debug_pubnames section sub-entry
+ for this data object definition. */
+
+ if (TREE_PUBLIC (decl) && ! DECL_ABSTRACT (decl))
+ {
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ sprintf (label, PUB_DIE_LABEL_FMT, next_pubname_number++);
+ ASM_OUTPUT_LABEL (asm_out_file, label);
+ }
+
+ /* Now output the DIE to represent the data object itself. This gets
+ complicated because of the possibility that the VAR_DECL really
+ represents an inlined instance of a formal parameter for an inline
+ function. */
+
+ {
+ register void (*func) ();
+ register tree origin = decl_ultimate_origin (decl);
+
+ if (origin != NULL && TREE_CODE (origin) == PARM_DECL)
+ func = output_formal_parameter_die;
+ else
+ {
+ if (TREE_PUBLIC (decl) || DECL_EXTERNAL (decl))
+ func = output_global_variable_die;
+ else
+ func = output_local_variable_die;
+ }
+ output_die (func, decl);
+ }
+ break;
+
+ case FIELD_DECL:
+ /* Ignore the nameless fields that are used to skip bits. */
+ if (DECL_NAME (decl) != 0)
+ {
+ output_type (member_declared_type (decl), containing_scope);
+ output_die (output_member_die, decl);
+ }
+ break;
+
+ case PARM_DECL:
+ /* Force out the type of this formal, if it was not forced out yet.
+ Note that here we can run afowl of a bug in "classic" svr4 SDB.
+ It should be able to grok the presence of type DIEs within a list
+ of TAG_formal_parameter DIEs, but it doesn't. */
+
+ output_type (TREE_TYPE (decl), containing_scope);
+ output_die (output_formal_parameter_die, decl);
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+void
+dwarfout_file_scope_decl (decl, set_finalizing)
+ register tree decl;
+ register int set_finalizing;
+{
+ if (TREE_CODE (decl) == ERROR_MARK)
+ return;
+
+ /* If this ..._DECL node is marked to be ignored, then ignore it. We
+ gotta hope that the node in question doesn't represent a function
+ definition. If it does, then totally ignoring it is bound to screw
+ up our count of blocks, and that it turn will completely screw up the
+ the labels we will reference in subsequent AT_low_pc and AT_high_pc
+ attributes (for subsequent blocks). (It's too bad that BLOCK nodes
+ don't carry their own sequence numbers with them!) */
+
+ if (DECL_IGNORED_P (decl))
+ {
+ if (TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl) != NULL)
+ abort ();
+ return;
+ }
+
+ switch (TREE_CODE (decl))
+ {
+ case FUNCTION_DECL:
+
+ /* Ignore this FUNCTION_DECL if it refers to a builtin declaration of
+ a builtin function. Explicit programmer-supplied declarations of
+ these same functions should NOT be ignored however. */
+
+ if (DECL_EXTERNAL (decl) && DECL_FUNCTION_CODE (decl))
+ return;
+
+ /* What we would really like to do here is to filter out all mere
+ file-scope declarations of file-scope functions which are never
+ referenced later within this translation unit (and keep all of
+ ones that *are* referenced later on) but we aren't clarvoiant,
+ so we have no idea which functions will be referenced in the
+ future (i.e. later on within the current translation unit).
+ So here we just ignore all file-scope function declarations
+ which are not also definitions. If and when the debugger needs
+ to know something about these funcstion, it wil have to hunt
+ around and find the DWARF information associated with the
+ *definition* of the function.
+
+ Note that we can't just check `DECL_EXTERNAL' to find out which
+ FUNCTION_DECL nodes represent definitions and which ones represent
+ mere declarations. We have to check `DECL_INITIAL' instead. That's
+ because the C front-end supports some weird semantics for "extern
+ inline" function definitions. These can get inlined within the
+ current translation unit (an thus, we need to generate DWARF info
+ for their abstract instances so that the DWARF info for the
+ concrete inlined instances can have something to refer to) but
+ the compiler never generates any out-of-lines instances of such
+ things (despite the fact that they *are* definitions). The
+ important point is that the C front-end marks these "extern inline"
+ functions as DECL_EXTERNAL, but we need to generate DWARf for them
+ anyway.
+
+ Note that the C++ front-end also plays some similar games for inline
+ function definitions appearing within include files which also
+ contain `#pragma interface' pragmas. */
+
+ if (DECL_INITIAL (decl) == NULL_TREE)
+ return;
+
+ if (TREE_PUBLIC (decl)
+ && ! DECL_EXTERNAL (decl)
+ && ! DECL_ABSTRACT (decl))
+ {
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ /* Output a .debug_pubnames entry for a public function
+ defined in this compilation unit. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, PUBNAMES_SECTION);
+ sprintf (label, PUB_DIE_LABEL_FMT, next_pubname_number);
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, label);
+ ASM_OUTPUT_DWARF_STRING (asm_out_file,
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+ }
+
+ break;
+
+ case VAR_DECL:
+
+ /* Ignore this VAR_DECL if it refers to a file-scope extern data
+ object declaration and if the declaration was never even
+ referenced from within this entire compilation unit. We
+ suppress these DIEs in order to save space in the .debug section
+ (by eliminating entries which are probably useless). Note that
+ we must not suppress block-local extern declarations (whether
+ used or not) because that would screw-up the debugger's name
+ lookup mechanism and cause it to miss things which really ought
+ to be in scope at a given point. */
+
+ if (DECL_EXTERNAL (decl) && !TREE_USED (decl))
+ return;
+
+ if (TREE_PUBLIC (decl)
+ && ! DECL_EXTERNAL (decl)
+ && GET_CODE (DECL_RTL (decl)) == MEM
+ && ! DECL_ABSTRACT (decl))
+ {
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ if (debug_info_level >= DINFO_LEVEL_NORMAL)
+ {
+ /* Output a .debug_pubnames entry for a public variable
+ defined in this compilation unit. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, PUBNAMES_SECTION);
+ sprintf (label, PUB_DIE_LABEL_FMT, next_pubname_number);
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, label);
+ ASM_OUTPUT_DWARF_STRING (asm_out_file,
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+ }
+
+ if (DECL_INITIAL (decl) == NULL)
+ {
+ /* Output a .debug_aranges entry for a public variable
+ which is tentatively defined in this compilation unit. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, ARANGES_SECTION);
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file,
+ IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)));
+ ASM_OUTPUT_DWARF_DATA4 (asm_out_file,
+ (unsigned) int_size_in_bytes (TREE_TYPE (decl)));
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+ }
+ }
+
+ /* If we are in terse mode, don't generate any DIEs to represent
+ any variable declarations or definitions. */
+
+ if (debug_info_level <= DINFO_LEVEL_TERSE)
+ return;
+
+ break;
+
+ case TYPE_DECL:
+ /* Don't bother trying to generate any DIEs to represent any of the
+ normal built-in types for the language we are compiling, except
+ in cases where the types in question are *not* DWARF fundamental
+ types. We make an exception in the case of non-fundamental types
+ for the sake of objective C (and perhaps C++) because the GNU
+ front-ends for these languages may in fact create certain "built-in"
+ types which are (for example) RECORD_TYPEs. In such cases, we
+ really need to output these (non-fundamental) types because other
+ DIEs may contain references to them. */
+
+ if (DECL_SOURCE_LINE (decl) == 0
+ && type_is_fundamental (TREE_TYPE (decl)))
+ return;
+
+ /* If we are in terse mode, don't generate any DIEs to represent
+ any actual typedefs. Note that even when we are in terse mode,
+ we must still output DIEs to represent those tagged types which
+ are used (directly or indirectly) in the specification of either
+ a return type or a formal parameter type of some function. */
+
+ if (debug_info_level <= DINFO_LEVEL_TERSE)
+ if (DECL_NAME (decl) != NULL
+ || ! TYPE_USED_FOR_FUNCTION (TREE_TYPE (decl)))
+ return;
+
+ break;
+
+ default:
+ return;
+ }
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, DEBUG_SECTION);
+ finalizing = set_finalizing;
+ output_decl (decl, NULL_TREE);
+
+ /* NOTE: The call above to `output_decl' may have caused one or more
+ file-scope named types (i.e. tagged types) to be placed onto the
+ pending_types_list. We have to get those types off of that list
+ at some point, and this is the perfect time to do it. If we didn't
+ take them off now, they might still be on the list when cc1 finally
+ exits. That might be OK if it weren't for the fact that when we put
+ types onto the pending_types_list, we set the TREE_ASM_WRITTEN flag
+ for these types, and that causes them never to be output unless
+ `output_pending_types_for_scope' takes them off of the list and un-sets
+ their TREE_ASM_WRITTEN flags. */
+
+ output_pending_types_for_scope (NULL_TREE);
+
+ /* The above call should have totally emptied the pending_types_list. */
+
+ assert (pending_types == 0);
+
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+
+ if (TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl) != NULL)
+ current_funcdef_number++;
+}
+
+/* Output a marker (i.e. a label) for the beginning of the generated code
+ for a lexical block. */
+
+void
+dwarfout_begin_block (blocknum)
+ register unsigned blocknum;
+{
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ text_section ();
+ sprintf (label, BLOCK_BEGIN_LABEL_FMT, blocknum);
+ ASM_OUTPUT_LABEL (asm_out_file, label);
+}
+
+/* Output a marker (i.e. a label) for the end of the generated code
+ for a lexical block. */
+
+void
+dwarfout_end_block (blocknum)
+ register unsigned blocknum;
+{
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ text_section ();
+ sprintf (label, BLOCK_END_LABEL_FMT, blocknum);
+ ASM_OUTPUT_LABEL (asm_out_file, label);
+}
+
+/* Output a marker (i.e. a label) at a point in the assembly code which
+ corresponds to a given source level label. */
+
+void
+dwarfout_label (insn)
+ register rtx insn;
+{
+ if (debug_info_level >= DINFO_LEVEL_NORMAL)
+ {
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ text_section ();
+ sprintf (label, INSN_LABEL_FMT, current_funcdef_number,
+ (unsigned) INSN_UID (insn));
+ ASM_OUTPUT_LABEL (asm_out_file, label);
+ }
+}
+
+/* Output a marker (i.e. a label) for the point in the generated code where
+ the real body of the function begins (after parameters have been moved
+ to their home locations). */
+
+void
+dwarfout_begin_function ()
+{
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ text_section ();
+ sprintf (label, BODY_BEGIN_LABEL_FMT, current_funcdef_number);
+ ASM_OUTPUT_LABEL (asm_out_file, label);
+}
+
+/* Output a marker (i.e. a label) for the point in the generated code where
+ the real body of the function ends (just before the epilogue code). */
+
+void
+dwarfout_end_function ()
+{
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ text_section ();
+ sprintf (label, BODY_END_LABEL_FMT, current_funcdef_number);
+ ASM_OUTPUT_LABEL (asm_out_file, label);
+}
+
+/* Output a marker (i.e. a label) for the absolute end of the generated code
+ for a function definition. This gets called *after* the epilogue code
+ has been generated. */
+
+void
+dwarfout_end_epilogue ()
+{
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ /* Output a label to mark the endpoint of the code generated for this
+ function. */
+
+ sprintf (label, FUNC_END_LABEL_FMT, current_funcdef_number);
+ ASM_OUTPUT_LABEL (asm_out_file, label);
+}
+
+static void
+shuffle_filename_entry (new_zeroth)
+ register filename_entry *new_zeroth;
+{
+ filename_entry temp_entry;
+ register filename_entry *limit_p;
+ register filename_entry *move_p;
+
+ if (new_zeroth == &filename_table[0])
+ return;
+
+ temp_entry = *new_zeroth;
+
+ /* Shift entries up in the table to make room at [0]. */
+
+ limit_p = &filename_table[0];
+ for (move_p = new_zeroth; move_p > limit_p; move_p--)
+ *move_p = *(move_p-1);
+
+ /* Install the found entry at [0]. */
+
+ filename_table[0] = temp_entry;
+}
+
+/* Create a new (string) entry for the .debug_sfnames section. */
+
+static void
+generate_new_sfname_entry ()
+{
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, SFNAMES_SECTION);
+ sprintf (label, SFNAMES_ENTRY_LABEL_FMT, filename_table[0].number);
+ ASM_OUTPUT_LABEL (asm_out_file, label);
+ ASM_OUTPUT_DWARF_STRING (asm_out_file,
+ filename_table[0].name
+ ? filename_table[0].name
+ : "");
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+}
+
+/* Lookup a filename (in the list of filenames that we know about here in
+ dwarfout.c) and return its "index". The index of each (known) filename
+ is just a unique number which is associated with only that one filename.
+ We need such numbers for the sake of generating labels (in the
+ .debug_sfnames section) and references to those unique labels (in the
+ .debug_srcinfo and .debug_macinfo sections).
+
+ If the filename given as an argument is not found in our current list,
+ add it to the list and assign it the next available unique index number.
+
+ Whatever we do (i.e. whether we find a pre-existing filename or add a new
+ one), we shuffle the filename found (or added) up to the zeroth entry of
+ our list of filenames (which is always searched linearly). We do this so
+ as to optimize the most common case for these filename lookups within
+ dwarfout.c. The most common case by far is the case where we call
+ lookup_filename to lookup the very same filename that we did a lookup
+ on the last time we called lookup_filename. We make sure that this
+ common case is fast because such cases will constitute 99.9% of the
+ lookups we ever do (in practice).
+
+ If we add a new filename entry to our table, we go ahead and generate
+ the corresponding entry in the .debug_sfnames section right away.
+ Doing so allows us to avoid tickling an assembler bug (present in some
+ m68k assemblers) which yields assembly-time errors in cases where the
+ difference of two label addresses is taken and where the two labels
+ are in a section *other* than the one where the difference is being
+ calculated, and where at least one of the two symbol references is a
+ forward reference. (This bug could be tickled by our .debug_srcinfo
+ entries if we don't output their corresponding .debug_sfnames entries
+ before them.)
+*/
+
+static unsigned
+lookup_filename (file_name)
+ char *file_name;
+{
+ register filename_entry *search_p;
+ register filename_entry *limit_p = &filename_table[ft_entries];
+
+ for (search_p = filename_table; search_p < limit_p; search_p++)
+ if (!strcmp (file_name, search_p->name))
+ {
+ /* When we get here, we have found the filename that we were
+ looking for in the filename_table. Now we want to make sure
+ that it gets moved to the zero'th entry in the table (if it
+ is not already there) so that subsequent attempts to find the
+ same filename will find it as quickly as possible. */
+
+ shuffle_filename_entry (search_p);
+ return filename_table[0].number;
+ }
+
+ /* We come here whenever we have a new filename which is not registered
+ in the current table. Here we add it to the table. */
+
+ /* Prepare to add a new table entry by making sure there is enough space
+ in the table to do so. If not, expand the current table. */
+
+ if (ft_entries == ft_entries_allocated)
+ {
+ ft_entries_allocated += FT_ENTRIES_INCREMENT;
+ filename_table
+ = (filename_entry *)
+ xrealloc (filename_table,
+ ft_entries_allocated * sizeof (filename_entry));
+ }
+
+ /* Initially, add the new entry at the end of the filename table. */
+
+ filename_table[ft_entries].number = ft_entries;
+ filename_table[ft_entries].name = xstrdup (file_name);
+
+ /* Shuffle the new entry into filename_table[0]. */
+
+ shuffle_filename_entry (&filename_table[ft_entries]);
+
+ if (debug_info_level >= DINFO_LEVEL_NORMAL)
+ generate_new_sfname_entry ();
+
+ ft_entries++;
+ return filename_table[0].number;
+}
+
+static void
+generate_srcinfo_entry (line_entry_num, files_entry_num)
+ unsigned line_entry_num;
+ unsigned files_entry_num;
+{
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, SRCINFO_SECTION);
+ sprintf (label, LINE_ENTRY_LABEL_FMT, line_entry_num);
+ ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, label, LINE_BEGIN_LABEL);
+ sprintf (label, SFNAMES_ENTRY_LABEL_FMT, files_entry_num);
+ ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, label, SFNAMES_BEGIN_LABEL);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+}
+
+void
+dwarfout_line (filename, line)
+ register char *filename;
+ register unsigned line;
+{
+ if (debug_info_level >= DINFO_LEVEL_NORMAL)
+ {
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+ static unsigned last_line_entry_num = 0;
+ static unsigned prev_file_entry_num = (unsigned) -1;
+ register unsigned this_file_entry_num = lookup_filename (filename);
+
+ text_section ();
+ sprintf (label, LINE_CODE_LABEL_FMT, ++last_line_entry_num);
+ ASM_OUTPUT_LABEL (asm_out_file, label);
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, LINE_SECTION);
+
+ if (this_file_entry_num != prev_file_entry_num)
+ {
+ char line_entry_label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ sprintf (line_entry_label, LINE_ENTRY_LABEL_FMT, last_line_entry_num);
+ ASM_OUTPUT_LABEL (asm_out_file, line_entry_label);
+ }
+
+ {
+ register char *tail = rindex (filename, '/');
+
+ if (tail != NULL)
+ filename = tail;
+ }
+
+ fprintf (asm_out_file, "\t%s\t%u\t%s %s:%u\n",
+ UNALIGNED_INT_ASM_OP, line, ASM_COMMENT_START,
+ filename, line);
+ ASM_OUTPUT_DWARF_DATA2 (asm_out_file, 0xffff);
+ ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, label, TEXT_BEGIN_LABEL);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+
+ if (this_file_entry_num != prev_file_entry_num)
+ generate_srcinfo_entry (last_line_entry_num, this_file_entry_num);
+ prev_file_entry_num = this_file_entry_num;
+ }
+}
+
+/* Generate an entry in the .debug_macinfo section. */
+
+static void
+generate_macinfo_entry (type_and_offset, string)
+ register char *type_and_offset;
+ register char *string;
+{
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, MACINFO_SECTION);
+ fprintf (asm_out_file, "\t%s\t%s\n", UNALIGNED_INT_ASM_OP, type_and_offset);
+ ASM_OUTPUT_DWARF_STRING (asm_out_file, string);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+}
+
+void
+dwarfout_start_new_source_file (filename)
+ register char *filename;
+{
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+ char type_and_offset[MAX_ARTIFICIAL_LABEL_BYTES*3];
+
+ sprintf (label, SFNAMES_ENTRY_LABEL_FMT, lookup_filename (filename));
+ sprintf (type_and_offset, "0x%08x+%s-%s",
+ ((unsigned) MACINFO_start << 24), label, SFNAMES_BEGIN_LABEL);
+ generate_macinfo_entry (type_and_offset, "");
+}
+
+void
+dwarfout_resume_previous_source_file (lineno)
+ register unsigned lineno;
+{
+ char type_and_offset[MAX_ARTIFICIAL_LABEL_BYTES*2];
+
+ sprintf (type_and_offset, "0x%08x+%u",
+ ((unsigned) MACINFO_resume << 24), lineno);
+ generate_macinfo_entry (type_and_offset, "");
+}
+
+/* Called from check_newline in c-parse.y. The `buffer' parameter
+ contains the tail part of the directive line, i.e. the part which
+ is past the initial whitespace, #, whitespace, directive-name,
+ whitespace part. */
+
+void
+dwarfout_define (lineno, buffer)
+ register unsigned lineno;
+ register char *buffer;
+{
+ static int initialized = 0;
+ char type_and_offset[MAX_ARTIFICIAL_LABEL_BYTES*2];
+
+ if (!initialized)
+ {
+ dwarfout_start_new_source_file (primary_filename);
+ initialized = 1;
+ }
+ sprintf (type_and_offset, "0x%08x+%u",
+ ((unsigned) MACINFO_define << 24), lineno);
+ generate_macinfo_entry (type_and_offset, buffer);
+}
+
+/* Called from check_newline in c-parse.y. The `buffer' parameter
+ contains the tail part of the directive line, i.e. the part which
+ is past the initial whitespace, #, whitespace, directive-name,
+ whitespace part. */
+
+void
+dwarfout_undef (lineno, buffer)
+ register unsigned lineno;
+ register char *buffer;
+{
+ char type_and_offset[MAX_ARTIFICIAL_LABEL_BYTES*2];
+
+ sprintf (type_and_offset, "0x%08x+%u",
+ ((unsigned) MACINFO_undef << 24), lineno);
+ generate_macinfo_entry (type_and_offset, buffer);
+}
+
+/* Set up for Dwarf output at the start of compilation. */
+
+void
+dwarfout_init (asm_out_file, main_input_filename)
+ register FILE *asm_out_file;
+ register char *main_input_filename;
+{
+ /* Remember the name of the primary input file. */
+
+ primary_filename = main_input_filename;
+
+ /* Allocate the initial hunk of the pending_sibling_stack. */
+
+ pending_sibling_stack
+ = (unsigned *)
+ xmalloc (PENDING_SIBLINGS_INCREMENT * sizeof (unsigned));
+ pending_siblings_allocated = PENDING_SIBLINGS_INCREMENT;
+ pending_siblings = 1;
+
+ /* Allocate the initial hunk of the filename_table. */
+
+ filename_table
+ = (filename_entry *)
+ xmalloc (FT_ENTRIES_INCREMENT * sizeof (filename_entry));
+ ft_entries_allocated = FT_ENTRIES_INCREMENT;
+ ft_entries = 0;
+
+ /* Allocate the initial hunk of the pending_types_list. */
+
+ pending_types_list
+ = (tree *) xmalloc (PENDING_TYPES_INCREMENT * sizeof (tree));
+ pending_types_allocated = PENDING_TYPES_INCREMENT;
+ pending_types = 0;
+
+ /* Create an artificial RECORD_TYPE node which we can use in our hack
+ to get the DIEs representing types of formal parameters to come out
+ only *after* the DIEs for the formal parameters themselves. */
+
+ fake_containing_scope = make_node (RECORD_TYPE);
+
+ /* Output a starting label for the .text section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, TEXT_SECTION);
+ ASM_OUTPUT_LABEL (asm_out_file, TEXT_BEGIN_LABEL);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+
+ /* Output a starting label for the .data section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, DATA_SECTION);
+ ASM_OUTPUT_LABEL (asm_out_file, DATA_BEGIN_LABEL);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+
+#if 0 /* GNU C doesn't currently use .data1. */
+ /* Output a starting label for the .data1 section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, DATA1_SECTION);
+ ASM_OUTPUT_LABEL (asm_out_file, DATA1_BEGIN_LABEL);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+#endif
+
+ /* Output a starting label for the .rodata section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, RODATA_SECTION);
+ ASM_OUTPUT_LABEL (asm_out_file, RODATA_BEGIN_LABEL);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+
+#if 0 /* GNU C doesn't currently use .rodata1. */
+ /* Output a starting label for the .rodata1 section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, RODATA1_SECTION);
+ ASM_OUTPUT_LABEL (asm_out_file, RODATA1_BEGIN_LABEL);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+#endif
+
+ /* Output a starting label for the .bss section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, BSS_SECTION);
+ ASM_OUTPUT_LABEL (asm_out_file, BSS_BEGIN_LABEL);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+
+ if (debug_info_level >= DINFO_LEVEL_NORMAL)
+ {
+ /* Output a starting label and an initial (compilation directory)
+ entry for the .debug_sfnames section. The starting label will be
+ referenced by the initial entry in the .debug_srcinfo section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, SFNAMES_SECTION);
+ ASM_OUTPUT_LABEL (asm_out_file, SFNAMES_BEGIN_LABEL);
+ {
+ register char *pwd;
+ register unsigned len;
+ register char *dirname;
+
+ pwd = getpwd ();
+ if (!pwd)
+ pfatal_with_name ("getpwd");
+ len = strlen (pwd);
+ dirname = (char *) xmalloc (len + 2);
+
+ strcpy (dirname, pwd);
+ strcpy (dirname + len, "/");
+ ASM_OUTPUT_DWARF_STRING (asm_out_file, dirname);
+ free (dirname);
+ }
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+
+ if (debug_info_level >= DINFO_LEVEL_VERBOSE)
+ {
+ /* Output a starting label for the .debug_macinfo section. This
+ label will be referenced by the AT_mac_info attribute in the
+ TAG_compile_unit DIE. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, MACINFO_SECTION);
+ ASM_OUTPUT_LABEL (asm_out_file, MACINFO_BEGIN_LABEL);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+ }
+
+ /* Generate the initial entry for the .line section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, LINE_SECTION);
+ ASM_OUTPUT_LABEL (asm_out_file, LINE_BEGIN_LABEL);
+ ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, LINE_END_LABEL, LINE_BEGIN_LABEL);
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, TEXT_BEGIN_LABEL);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+
+ /* Generate the initial entry for the .debug_srcinfo section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, SRCINFO_SECTION);
+ ASM_OUTPUT_LABEL (asm_out_file, SRCINFO_BEGIN_LABEL);
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, LINE_BEGIN_LABEL);
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, SFNAMES_BEGIN_LABEL);
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, TEXT_BEGIN_LABEL);
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, TEXT_END_LABEL);
+#ifdef DWARF_TIMESTAMPS
+ ASM_OUTPUT_DWARF_DATA4 (asm_out_file, time (NULL));
+#else
+ ASM_OUTPUT_DWARF_DATA4 (asm_out_file, -1);
+#endif
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+
+ /* Generate the initial entry for the .debug_pubnames section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, PUBNAMES_SECTION);
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, DEBUG_BEGIN_LABEL);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+
+ /* Generate the initial entry for the .debug_aranges section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, ARANGES_SECTION);
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, DEBUG_BEGIN_LABEL);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+ }
+
+ /* Setup first DIE number == 1. */
+ NEXT_DIE_NUM = next_unused_dienum++;
+
+ /* Generate the initial DIE for the .debug section. Note that the
+ (string) value given in the AT_name attribute of the TAG_compile_unit
+ DIE will (typically) be a relative pathname and that this pathname
+ should be taken as being relative to the directory from which the
+ compiler was invoked when the given (base) source file was compiled. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, DEBUG_SECTION);
+ ASM_OUTPUT_LABEL (asm_out_file, DEBUG_BEGIN_LABEL);
+ output_die (output_compile_unit_die, main_input_filename);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+
+ fputc ('\n', asm_out_file);
+}
+
+/* Output stuff that dwarf requires at the end of every file. */
+
+void
+dwarfout_finish ()
+{
+ char label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, DEBUG_SECTION);
+
+ /* Mark the end of the chain of siblings which represent all file-scope
+ declarations in this compilation unit. */
+
+ /* The (null) DIE which represents the terminator for the (sibling linked)
+ list of file-scope items is *special*. Normally, we would just call
+ end_sibling_chain at this point in order to output a word with the
+ value `4' and that word would act as the terminator for the list of
+ DIEs describing file-scope items. Unfortunately, if we were to simply
+ do that, the label that would follow this DIE in the .debug section
+ (i.e. `..D2') would *not* be properly aligned (as it must be on some
+ machines) to a 4 byte boundary.
+
+ In order to force the label `..D2' to get aligned to a 4 byte boundary,
+ the trick used is to insert extra (otherwise useless) padding bytes
+ into the (null) DIE that we know must precede the ..D2 label in the
+ .debug section. The amount of padding required can be anywhere between
+ 0 and 3 bytes. The length word at the start of this DIE (i.e. the one
+ with the padding) would normally contain the value 4, but now it will
+ also have to include the padding bytes, so it will instead have some
+ value in the range 4..7.
+
+ Fortunately, the rules of Dwarf say that any DIE whose length word
+ contains *any* value less than 8 should be treated as a null DIE, so
+ this trick works out nicely. Clever, eh? Don't give me any credit
+ (or blame). I didn't think of this scheme. I just conformed to it.
+ */
+
+ output_die (output_padded_null_die, (void *)0);
+ dienum_pop ();
+
+ sprintf (label, DIE_BEGIN_LABEL_FMT, NEXT_DIE_NUM);
+ ASM_OUTPUT_LABEL (asm_out_file, label); /* should be ..D2 */
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+
+ /* Output a terminator label for the .text section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, TEXT_SECTION);
+ ASM_OUTPUT_LABEL (asm_out_file, TEXT_END_LABEL);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+
+ /* Output a terminator label for the .data section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, DATA_SECTION);
+ ASM_OUTPUT_LABEL (asm_out_file, DATA_END_LABEL);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+
+#if 0 /* GNU C doesn't currently use .data1. */
+ /* Output a terminator label for the .data1 section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, DATA1_SECTION);
+ ASM_OUTPUT_LABEL (asm_out_file, DATA1_END_LABEL);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+#endif
+
+ /* Output a terminator label for the .rodata section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, RODATA_SECTION);
+ ASM_OUTPUT_LABEL (asm_out_file, RODATA_END_LABEL);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+
+#if 0 /* GNU C doesn't currently use .rodata1. */
+ /* Output a terminator label for the .rodata1 section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, RODATA1_SECTION);
+ ASM_OUTPUT_LABEL (asm_out_file, RODATA1_END_LABEL);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+#endif
+
+ /* Output a terminator label for the .bss section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, BSS_SECTION);
+ ASM_OUTPUT_LABEL (asm_out_file, BSS_END_LABEL);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+
+ if (debug_info_level >= DINFO_LEVEL_NORMAL)
+ {
+ /* Output a terminating entry for the .line section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, LINE_SECTION);
+ ASM_OUTPUT_LABEL (asm_out_file, LINE_LAST_ENTRY_LABEL);
+ ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0);
+ ASM_OUTPUT_DWARF_DATA2 (asm_out_file, 0xffff);
+ ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, TEXT_END_LABEL, TEXT_BEGIN_LABEL);
+ ASM_OUTPUT_LABEL (asm_out_file, LINE_END_LABEL);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+
+ /* Output a terminating entry for the .debug_srcinfo section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, SRCINFO_SECTION);
+ ASM_OUTPUT_DWARF_DELTA4 (asm_out_file,
+ LINE_LAST_ENTRY_LABEL, LINE_BEGIN_LABEL);
+ ASM_OUTPUT_DWARF_DATA4 (asm_out_file, -1);
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+
+ if (debug_info_level >= DINFO_LEVEL_VERBOSE)
+ {
+ /* Output terminating entries for the .debug_macinfo section. */
+
+ dwarfout_resume_previous_source_file (0);
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, MACINFO_SECTION);
+ ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0);
+ ASM_OUTPUT_DWARF_STRING (asm_out_file, "");
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+ }
+
+ /* Generate the terminating entry for the .debug_pubnames section. */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, PUBNAMES_SECTION);
+ ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0);
+ ASM_OUTPUT_DWARF_STRING (asm_out_file, "");
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+
+ /* Generate the terminating entries for the .debug_aranges section.
+
+ Note that we want to do this only *after* we have output the end
+ labels (for the various program sections) which we are going to
+ refer to here. This allows us to work around a bug in the m68k
+ svr4 assembler. That assembler gives bogus assembly-time errors
+ if (within any given section) you try to take the difference of
+ two relocatable symbols, both of which are located within some
+ other section, and if one (or both?) of the symbols involved is
+ being forward-referenced. By generating the .debug_aranges
+ entries at this late point in the assembly output, we skirt the
+ issue simply by avoiding forward-references.
+ */
+
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_PUSH_SECTION (asm_out_file, ARANGES_SECTION);
+
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, TEXT_BEGIN_LABEL);
+ ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, TEXT_END_LABEL, TEXT_BEGIN_LABEL);
+
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, DATA_BEGIN_LABEL);
+ ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, DATA_END_LABEL, DATA_BEGIN_LABEL);
+
+#if 0 /* GNU C doesn't currently use .data1. */
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, DATA1_BEGIN_LABEL);
+ ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, DATA1_END_LABEL,
+ DATA1_BEGIN_LABEL);
+#endif
+
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, RODATA_BEGIN_LABEL);
+ ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, RODATA_END_LABEL,
+ RODATA_BEGIN_LABEL);
+
+#if 0 /* GNU C doesn't currently use .rodata1. */
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, RODATA1_BEGIN_LABEL);
+ ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, RODATA1_END_LABEL,
+ RODATA1_BEGIN_LABEL);
+#endif
+
+ ASM_OUTPUT_DWARF_ADDR (asm_out_file, BSS_BEGIN_LABEL);
+ ASM_OUTPUT_DWARF_DELTA4 (asm_out_file, BSS_END_LABEL, BSS_BEGIN_LABEL);
+
+ ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0);
+ ASM_OUTPUT_DWARF_DATA4 (asm_out_file, 0);
+
+ ASM_OUTPUT_POP_SECTION (asm_out_file);
+ }
+}
+
+#endif /* DWARF_DEBUGGING_INFO */
diff --git a/gnu/usr.bin/cc/cc_int/emit-rtl.c b/gnu/usr.bin/cc/cc_int/emit-rtl.c
new file mode 100644
index 0000000..3afcccb
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/emit-rtl.c
@@ -0,0 +1,3359 @@
+/* Emit RTL for the GNU C-Compiler expander.
+ Copyright (C) 1987, 1988, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* Middle-to-low level generation of rtx code and insns.
+
+ This file contains the functions `gen_rtx', `gen_reg_rtx'
+ and `gen_label_rtx' that are the usual ways of creating rtl
+ expressions for most purposes.
+
+ It also has the functions for creating insns and linking
+ them in the doubly-linked chain.
+
+ The patterns of the insns are created by machine-dependent
+ routines in insn-emit.c, which is generated automatically from
+ the machine description. These routines use `gen_rtx' to make
+ the individual rtx's of the pattern; what is machine dependent
+ is the kind of rtx's they make and what arguments they use. */
+
+#include "config.h"
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include "rtl.h"
+#include "tree.h"
+#include "flags.h"
+#include "function.h"
+#include "expr.h"
+#include "regs.h"
+#include "insn-config.h"
+#include "real.h"
+#include "obstack.h"
+
+#include "bytecode.h"
+#include "machmode.h"
+#include "bc-opcode.h"
+#include "bc-typecd.h"
+#include "bc-optab.h"
+#include "bc-emit.h"
+
+#include <stdio.h>
+
+
+/* Opcode names */
+#ifdef BCDEBUG_PRINT_CODE
+char *opcode_name[] =
+{
+#include "bc-opname.h"
+
+"***END***"
+};
+#endif
+
+
+/* Commonly used modes. */
+
+enum machine_mode byte_mode; /* Mode whose width is BITS_PER_UNIT */
+enum machine_mode word_mode; /* Mode whose width is BITS_PER_WORD */
+
+/* This is reset to LAST_VIRTUAL_REGISTER + 1 at the start of each function.
+ After rtl generation, it is 1 plus the largest register number used. */
+
+int reg_rtx_no = LAST_VIRTUAL_REGISTER + 1;
+
+/* This is *not* reset after each function. It gives each CODE_LABEL
+ in the entire compilation a unique label number. */
+
+static int label_num = 1;
+
+/* Lowest label number in current function. */
+
+static int first_label_num;
+
+/* Highest label number in current function.
+ Zero means use the value of label_num instead.
+ This is nonzero only when belatedly compiling an inline function. */
+
+static int last_label_num;
+
+/* Value label_num had when set_new_first_and_last_label_number was called.
+ If label_num has not changed since then, last_label_num is valid. */
+
+static int base_label_num;
+
+/* Nonzero means do not generate NOTEs for source line numbers. */
+
+static int no_line_numbers;
+
+/* Commonly used rtx's, so that we only need space for one copy.
+ These are initialized once for the entire compilation.
+ All of these except perhaps the floating-point CONST_DOUBLEs
+ are unique; no other rtx-object will be equal to any of these. */
+
+rtx pc_rtx; /* (PC) */
+rtx cc0_rtx; /* (CC0) */
+rtx cc1_rtx; /* (CC1) (not actually used nowadays) */
+rtx const0_rtx; /* (CONST_INT 0) */
+rtx const1_rtx; /* (CONST_INT 1) */
+rtx const2_rtx; /* (CONST_INT 2) */
+rtx constm1_rtx; /* (CONST_INT -1) */
+rtx const_true_rtx; /* (CONST_INT STORE_FLAG_VALUE) */
+
+/* We record floating-point CONST_DOUBLEs in each floating-point mode for
+ the values of 0, 1, and 2. For the integer entries and VOIDmode, we
+ record a copy of const[012]_rtx. */
+
+rtx const_tiny_rtx[3][(int) MAX_MACHINE_MODE];
+
+REAL_VALUE_TYPE dconst0;
+REAL_VALUE_TYPE dconst1;
+REAL_VALUE_TYPE dconst2;
+REAL_VALUE_TYPE dconstm1;
+
+/* All references to the following fixed hard registers go through
+ these unique rtl objects. On machines where the frame-pointer and
+ arg-pointer are the same register, they use the same unique object.
+
+ After register allocation, other rtl objects which used to be pseudo-regs
+ may be clobbered to refer to the frame-pointer register.
+ But references that were originally to the frame-pointer can be
+ distinguished from the others because they contain frame_pointer_rtx.
+
+ When to use frame_pointer_rtx and hard_frame_pointer_rtx is a little
+ tricky: until register elimination has taken place hard_frame_pointer_rtx
+ should be used if it is being set, and frame_pointer_rtx otherwise. After
+ register elimination hard_frame_pointer_rtx should always be used.
+ On machines where the two registers are same (most) then these are the
+ same.
+
+ In an inline procedure, the stack and frame pointer rtxs may not be
+ used for anything else. */
+rtx stack_pointer_rtx; /* (REG:Pmode STACK_POINTER_REGNUM) */
+rtx frame_pointer_rtx; /* (REG:Pmode FRAME_POINTER_REGNUM) */
+rtx hard_frame_pointer_rtx; /* (REG:Pmode HARD_FRAME_POINTER_REGNUM) */
+rtx arg_pointer_rtx; /* (REG:Pmode ARG_POINTER_REGNUM) */
+rtx struct_value_rtx; /* (REG:Pmode STRUCT_VALUE_REGNUM) */
+rtx struct_value_incoming_rtx; /* (REG:Pmode STRUCT_VALUE_INCOMING_REGNUM) */
+rtx static_chain_rtx; /* (REG:Pmode STATIC_CHAIN_REGNUM) */
+rtx static_chain_incoming_rtx; /* (REG:Pmode STATIC_CHAIN_INCOMING_REGNUM) */
+rtx pic_offset_table_rtx; /* (REG:Pmode PIC_OFFSET_TABLE_REGNUM) */
+
+rtx virtual_incoming_args_rtx; /* (REG:Pmode VIRTUAL_INCOMING_ARGS_REGNUM) */
+rtx virtual_stack_vars_rtx; /* (REG:Pmode VIRTUAL_STACK_VARS_REGNUM) */
+rtx virtual_stack_dynamic_rtx; /* (REG:Pmode VIRTUAL_STACK_DYNAMIC_REGNUM) */
+rtx virtual_outgoing_args_rtx; /* (REG:Pmode VIRTUAL_OUTGOING_ARGS_REGNUM) */
+
+/* We make one copy of (const_int C) where C is in
+ [- MAX_SAVED_CONST_INT, MAX_SAVED_CONST_INT]
+ to save space during the compilation and simplify comparisons of
+ integers. */
+
+#define MAX_SAVED_CONST_INT 64
+
+static rtx const_int_rtx[MAX_SAVED_CONST_INT * 2 + 1];
+
+/* The ends of the doubly-linked chain of rtl for the current function.
+ Both are reset to null at the start of rtl generation for the function.
+
+ start_sequence saves both of these on `sequence_stack' along with
+ `sequence_rtl_expr' and then starts a new, nested sequence of insns. */
+
+static rtx first_insn = NULL;
+static rtx last_insn = NULL;
+
+/* RTL_EXPR within which the current sequence will be placed. Use to
+ prevent reuse of any temporaries within the sequence until after the
+ RTL_EXPR is emitted. */
+
+tree sequence_rtl_expr = NULL;
+
+/* INSN_UID for next insn emitted.
+ Reset to 1 for each function compiled. */
+
+static int cur_insn_uid = 1;
+
+/* Line number and source file of the last line-number NOTE emitted.
+ This is used to avoid generating duplicates. */
+
+static int last_linenum = 0;
+static char *last_filename = 0;
+
+/* A vector indexed by pseudo reg number. The allocated length
+ of this vector is regno_pointer_flag_length. Since this
+ vector is needed during the expansion phase when the total
+ number of registers in the function is not yet known,
+ it is copied and made bigger when necessary. */
+
+char *regno_pointer_flag;
+int regno_pointer_flag_length;
+
+/* Indexed by pseudo register number, gives the rtx for that pseudo.
+ Allocated in parallel with regno_pointer_flag. */
+
+rtx *regno_reg_rtx;
+
+/* Stack of pending (incomplete) sequences saved by `start_sequence'.
+ Each element describes one pending sequence.
+ The main insn-chain is saved in the last element of the chain,
+ unless the chain is empty. */
+
+struct sequence_stack *sequence_stack;
+
+/* start_sequence and gen_sequence can make a lot of rtx expressions which are
+ shortly thrown away. We use two mechanisms to prevent this waste:
+
+ First, we keep a list of the expressions used to represent the sequence
+ stack in sequence_element_free_list.
+
+ Second, for sizes up to 5 elements, we keep a SEQUENCE and its associated
+ rtvec for use by gen_sequence. One entry for each size is sufficient
+ because most cases are calls to gen_sequence followed by immediately
+ emitting the SEQUENCE. Reuse is safe since emitting a sequence is
+ destructive on the insn in it anyway and hence can't be redone.
+
+ We do not bother to save this cached data over nested function calls.
+ Instead, we just reinitialize them. */
+
+#define SEQUENCE_RESULT_SIZE 5
+
+static struct sequence_stack *sequence_element_free_list;
+static rtx sequence_result[SEQUENCE_RESULT_SIZE];
+
+extern int rtx_equal_function_value_matters;
+
+/* Filename and line number of last line-number note,
+ whether we actually emitted it or not. */
+extern char *emit_filename;
+extern int emit_lineno;
+
+rtx change_address ();
+void init_emit ();
+
+extern struct obstack *rtl_obstack;
+
+extern int stack_depth;
+extern int max_stack_depth;
+
+/* rtx gen_rtx (code, mode, [element1, ..., elementn])
+**
+** This routine generates an RTX of the size specified by
+** <code>, which is an RTX code. The RTX structure is initialized
+** from the arguments <element1> through <elementn>, which are
+** interpreted according to the specific RTX type's format. The
+** special machine mode associated with the rtx (if any) is specified
+** in <mode>.
+**
+** gen_rtx can be invoked in a way which resembles the lisp-like
+** rtx it will generate. For example, the following rtx structure:
+**
+** (plus:QI (mem:QI (reg:SI 1))
+** (mem:QI (plusw:SI (reg:SI 2) (reg:SI 3))))
+**
+** ...would be generated by the following C code:
+**
+** gen_rtx (PLUS, QImode,
+** gen_rtx (MEM, QImode,
+** gen_rtx (REG, SImode, 1)),
+** gen_rtx (MEM, QImode,
+** gen_rtx (PLUS, SImode,
+** gen_rtx (REG, SImode, 2),
+** gen_rtx (REG, SImode, 3)))),
+*/
+
+/*VARARGS2*/
+rtx
+gen_rtx VPROTO((enum rtx_code code, enum machine_mode mode, ...))
+{
+#ifndef __STDC__
+ enum rtx_code code;
+ enum machine_mode mode;
+#endif
+ va_list p;
+ register int i; /* Array indices... */
+ register char *fmt; /* Current rtx's format... */
+ register rtx rt_val; /* RTX to return to caller... */
+
+ VA_START (p, mode);
+
+#ifndef __STDC__
+ code = va_arg (p, enum rtx_code);
+ mode = va_arg (p, enum machine_mode);
+#endif
+
+ if (code == CONST_INT)
+ {
+ HOST_WIDE_INT arg = va_arg (p, HOST_WIDE_INT);
+
+ if (arg >= - MAX_SAVED_CONST_INT && arg <= MAX_SAVED_CONST_INT)
+ return const_int_rtx[arg + MAX_SAVED_CONST_INT];
+
+ if (const_true_rtx && arg == STORE_FLAG_VALUE)
+ return const_true_rtx;
+
+ rt_val = rtx_alloc (code);
+ INTVAL (rt_val) = arg;
+ }
+ else if (code == REG)
+ {
+ int regno = va_arg (p, int);
+
+ /* In case the MD file explicitly references the frame pointer, have
+ all such references point to the same frame pointer. This is used
+ during frame pointer elimination to distinguish the explicit
+ references to these registers from pseudos that happened to be
+ assigned to them.
+
+ If we have eliminated the frame pointer or arg pointer, we will
+ be using it as a normal register, for example as a spill register.
+ In such cases, we might be accessing it in a mode that is not
+ Pmode and therefore cannot use the pre-allocated rtx.
+
+ Also don't do this when we are making new REGs in reload,
+ since we don't want to get confused with the real pointers. */
+
+ if (frame_pointer_rtx && regno == FRAME_POINTER_REGNUM && mode == Pmode
+ && ! reload_in_progress)
+ return frame_pointer_rtx;
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+ if (hard_frame_pointer_rtx && regno == HARD_FRAME_POINTER_REGNUM
+ && mode == Pmode && ! reload_in_progress)
+ return hard_frame_pointer_rtx;
+#endif
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM && HARD_FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ if (arg_pointer_rtx && regno == ARG_POINTER_REGNUM && mode == Pmode
+ && ! reload_in_progress)
+ return arg_pointer_rtx;
+#endif
+ if (stack_pointer_rtx && regno == STACK_POINTER_REGNUM && mode == Pmode
+ && ! reload_in_progress)
+ return stack_pointer_rtx;
+ else
+ {
+ rt_val = rtx_alloc (code);
+ rt_val->mode = mode;
+ REGNO (rt_val) = regno;
+ return rt_val;
+ }
+ }
+ else
+ {
+ rt_val = rtx_alloc (code); /* Allocate the storage space. */
+ rt_val->mode = mode; /* Store the machine mode... */
+
+ fmt = GET_RTX_FORMAT (code); /* Find the right format... */
+ for (i = 0; i < GET_RTX_LENGTH (code); i++)
+ {
+ switch (*fmt++)
+ {
+ case '0': /* Unused field. */
+ break;
+
+ case 'i': /* An integer? */
+ XINT (rt_val, i) = va_arg (p, int);
+ break;
+
+ case 'w': /* A wide integer? */
+ XWINT (rt_val, i) = va_arg (p, HOST_WIDE_INT);
+ break;
+
+ case 's': /* A string? */
+ XSTR (rt_val, i) = va_arg (p, char *);
+ break;
+
+ case 'e': /* An expression? */
+ case 'u': /* An insn? Same except when printing. */
+ XEXP (rt_val, i) = va_arg (p, rtx);
+ break;
+
+ case 'E': /* An RTX vector? */
+ XVEC (rt_val, i) = va_arg (p, rtvec);
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ }
+ va_end (p);
+ return rt_val; /* Return the new RTX... */
+}
+
+/* gen_rtvec (n, [rt1, ..., rtn])
+**
+** This routine creates an rtvec and stores within it the
+** pointers to rtx's which are its arguments.
+*/
+
+/*VARARGS1*/
+rtvec
+gen_rtvec VPROTO((int n, ...))
+{
+#ifndef __STDC__
+ int n;
+#endif
+ int i;
+ va_list p;
+ rtx *vector;
+
+ VA_START (p, n);
+
+#ifndef __STDC__
+ n = va_arg (p, int);
+#endif
+
+ if (n == 0)
+ return NULL_RTVEC; /* Don't allocate an empty rtvec... */
+
+ vector = (rtx *) alloca (n * sizeof (rtx));
+
+ for (i = 0; i < n; i++)
+ vector[i] = va_arg (p, rtx);
+ va_end (p);
+
+ return gen_rtvec_v (n, vector);
+}
+
+rtvec
+gen_rtvec_v (n, argp)
+ int n;
+ rtx *argp;
+{
+ register int i;
+ register rtvec rt_val;
+
+ if (n == 0)
+ return NULL_RTVEC; /* Don't allocate an empty rtvec... */
+
+ rt_val = rtvec_alloc (n); /* Allocate an rtvec... */
+
+ for (i = 0; i < n; i++)
+ rt_val->elem[i].rtx = *argp++;
+
+ return rt_val;
+}
+
+/* Generate a REG rtx for a new pseudo register of mode MODE.
+ This pseudo is assigned the next sequential register number. */
+
+rtx
+gen_reg_rtx (mode)
+ enum machine_mode mode;
+{
+ register rtx val;
+
+ /* Don't let anything called by or after reload create new registers
+ (actually, registers can't be created after flow, but this is a good
+ approximation). */
+
+ if (reload_in_progress || reload_completed)
+ abort ();
+
+ if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
+ || GET_MODE_CLASS (mode) == MODE_COMPLEX_INT)
+ {
+ /* For complex modes, don't make a single pseudo.
+ Instead, make a CONCAT of two pseudos.
+ This allows noncontiguous allocation of the real and imaginary parts,
+ which makes much better code. Besides, allocating DCmode
+ pseudos overstrains reload on some machines like the 386. */
+ rtx realpart, imagpart;
+ int size = GET_MODE_UNIT_SIZE (mode);
+ enum machine_mode partmode
+ = mode_for_size (size * BITS_PER_UNIT,
+ (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
+ ? MODE_FLOAT : MODE_INT),
+ 0);
+
+ realpart = gen_reg_rtx (partmode);
+ imagpart = gen_reg_rtx (partmode);
+ return gen_rtx (CONCAT, mode, realpart, imagpart);
+ }
+
+ /* Make sure regno_pointer_flag and regno_reg_rtx are large
+ enough to have an element for this pseudo reg number. */
+
+ if (reg_rtx_no == regno_pointer_flag_length)
+ {
+ rtx *new1;
+ char *new =
+ (char *) oballoc (regno_pointer_flag_length * 2);
+ bcopy (regno_pointer_flag, new, regno_pointer_flag_length);
+ bzero (&new[regno_pointer_flag_length], regno_pointer_flag_length);
+ regno_pointer_flag = new;
+
+ new1 = (rtx *) oballoc (regno_pointer_flag_length * 2 * sizeof (rtx));
+ bcopy ((char *) regno_reg_rtx, (char *) new1,
+ regno_pointer_flag_length * sizeof (rtx));
+ bzero ((char *) &new1[regno_pointer_flag_length],
+ regno_pointer_flag_length * sizeof (rtx));
+ regno_reg_rtx = new1;
+
+ regno_pointer_flag_length *= 2;
+ }
+
+ val = gen_rtx (REG, mode, reg_rtx_no);
+ regno_reg_rtx[reg_rtx_no++] = val;
+ return val;
+}
+
+/* Identify REG as a probable pointer register. */
+
+void
+mark_reg_pointer (reg)
+ rtx reg;
+{
+ REGNO_POINTER_FLAG (REGNO (reg)) = 1;
+}
+
+/* Return 1 plus largest pseudo reg number used in the current function. */
+
+int
+max_reg_num ()
+{
+ return reg_rtx_no;
+}
+
+/* Return 1 + the largest label number used so far in the current function. */
+
+int
+max_label_num ()
+{
+ if (last_label_num && label_num == base_label_num)
+ return last_label_num;
+ return label_num;
+}
+
+/* Return first label number used in this function (if any were used). */
+
+int
+get_first_label_num ()
+{
+ return first_label_num;
+}
+
+/* Return a value representing some low-order bits of X, where the number
+ of low-order bits is given by MODE. Note that no conversion is done
+ between floating-point and fixed-point values, rather, the bit
+ representation is returned.
+
+ This function handles the cases in common between gen_lowpart, below,
+ and two variants in cse.c and combine.c. These are the cases that can
+ be safely handled at all points in the compilation.
+
+ If this is not a case we can handle, return 0. */
+
+rtx
+gen_lowpart_common (mode, x)
+ enum machine_mode mode;
+ register rtx x;
+{
+ int word = 0;
+
+ if (GET_MODE (x) == mode)
+ return x;
+
+ /* MODE must occupy no more words than the mode of X. */
+ if (GET_MODE (x) != VOIDmode
+ && ((GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD
+ > ((GET_MODE_SIZE (GET_MODE (x)) + (UNITS_PER_WORD - 1))
+ / UNITS_PER_WORD)))
+ return 0;
+
+ if (WORDS_BIG_ENDIAN && GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD)
+ word = ((GET_MODE_SIZE (GET_MODE (x))
+ - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD))
+ / UNITS_PER_WORD);
+
+ if ((GET_CODE (x) == ZERO_EXTEND || GET_CODE (x) == SIGN_EXTEND)
+ && (GET_MODE_CLASS (mode) == MODE_INT
+ || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT))
+ {
+ /* If we are getting the low-order part of something that has been
+ sign- or zero-extended, we can either just use the object being
+ extended or make a narrower extension. If we want an even smaller
+ piece than the size of the object being extended, call ourselves
+ recursively.
+
+ This case is used mostly by combine and cse. */
+
+ if (GET_MODE (XEXP (x, 0)) == mode)
+ return XEXP (x, 0);
+ else if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (XEXP (x, 0))))
+ return gen_lowpart_common (mode, XEXP (x, 0));
+ else if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (x)))
+ return gen_rtx (GET_CODE (x), mode, XEXP (x, 0));
+ }
+ else if (GET_CODE (x) == SUBREG
+ && (GET_MODE_SIZE (mode) <= UNITS_PER_WORD
+ || GET_MODE_SIZE (mode) == GET_MODE_UNIT_SIZE (GET_MODE (x))))
+ return (GET_MODE (SUBREG_REG (x)) == mode && SUBREG_WORD (x) == 0
+ ? SUBREG_REG (x)
+ : gen_rtx (SUBREG, mode, SUBREG_REG (x), SUBREG_WORD (x)));
+ else if (GET_CODE (x) == REG)
+ {
+ /* If the register is not valid for MODE, return 0. If we don't
+ do this, there is no way to fix up the resulting REG later.
+ But we do do this if the current REG is not valid for its
+ mode. This latter is a kludge, but is required due to the
+ way that parameters are passed on some machines, most
+ notably Sparc. */
+ if (REGNO (x) < FIRST_PSEUDO_REGISTER
+ && ! HARD_REGNO_MODE_OK (REGNO (x) + word, mode)
+ && HARD_REGNO_MODE_OK (REGNO (x), GET_MODE (x)))
+ return 0;
+ else if (REGNO (x) < FIRST_PSEUDO_REGISTER
+ /* integrate.c can't handle parts of a return value register. */
+ && (! REG_FUNCTION_VALUE_P (x)
+ || ! rtx_equal_function_value_matters)
+ /* We want to keep the stack, frame, and arg pointers
+ special. */
+ && x != frame_pointer_rtx
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ && x != arg_pointer_rtx
+#endif
+ && x != stack_pointer_rtx)
+ return gen_rtx (REG, mode, REGNO (x) + word);
+ else
+ return gen_rtx (SUBREG, mode, x, word);
+ }
+ /* If X is a CONST_INT or a CONST_DOUBLE, extract the appropriate bits
+ from the low-order part of the constant. */
+ else if ((GET_MODE_CLASS (mode) == MODE_INT
+ || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT)
+ && GET_MODE (x) == VOIDmode
+ && (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE))
+ {
+ /* If MODE is twice the host word size, X is already the desired
+ representation. Otherwise, if MODE is wider than a word, we can't
+ do this. If MODE is exactly a word, return just one CONST_INT.
+ If MODE is smaller than a word, clear the bits that don't belong
+ in our mode, unless they and our sign bit are all one. So we get
+ either a reasonable negative value or a reasonable unsigned value
+ for this mode. */
+
+ if (GET_MODE_BITSIZE (mode) == 2 * HOST_BITS_PER_WIDE_INT)
+ return x;
+ else if (GET_MODE_BITSIZE (mode) > HOST_BITS_PER_WIDE_INT)
+ return 0;
+ else if (GET_MODE_BITSIZE (mode) == HOST_BITS_PER_WIDE_INT)
+ return (GET_CODE (x) == CONST_INT ? x
+ : GEN_INT (CONST_DOUBLE_LOW (x)));
+ else
+ {
+ /* MODE must be narrower than HOST_BITS_PER_INT. */
+ int width = GET_MODE_BITSIZE (mode);
+ HOST_WIDE_INT val = (GET_CODE (x) == CONST_INT ? INTVAL (x)
+ : CONST_DOUBLE_LOW (x));
+
+ if (((val & ((HOST_WIDE_INT) (-1) << (width - 1)))
+ != ((HOST_WIDE_INT) (-1) << (width - 1))))
+ val &= ((HOST_WIDE_INT) 1 << width) - 1;
+
+ return (GET_CODE (x) == CONST_INT && INTVAL (x) == val ? x
+ : GEN_INT (val));
+ }
+ }
+
+ /* If X is an integral constant but we want it in floating-point, it
+ must be the case that we have a union of an integer and a floating-point
+ value. If the machine-parameters allow it, simulate that union here
+ and return the result. The two-word and single-word cases are
+ different. */
+
+ else if (((HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
+ && HOST_BITS_PER_WIDE_INT == BITS_PER_WORD)
+ || flag_pretend_float)
+ && GET_MODE_CLASS (mode) == MODE_FLOAT
+ && GET_MODE_SIZE (mode) == UNITS_PER_WORD
+ && GET_CODE (x) == CONST_INT
+ && sizeof (float) * HOST_BITS_PER_CHAR == HOST_BITS_PER_WIDE_INT)
+#ifdef REAL_ARITHMETIC
+ {
+ REAL_VALUE_TYPE r;
+ HOST_WIDE_INT i;
+
+ i = INTVAL (x);
+ r = REAL_VALUE_FROM_TARGET_SINGLE (i);
+ return CONST_DOUBLE_FROM_REAL_VALUE (r, mode);
+ }
+#else
+ {
+ union {HOST_WIDE_INT i; float d; } u;
+
+ u.i = INTVAL (x);
+ return CONST_DOUBLE_FROM_REAL_VALUE (u.d, mode);
+ }
+#endif
+ else if (((HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
+ && HOST_BITS_PER_WIDE_INT == BITS_PER_WORD)
+ || flag_pretend_float)
+ && GET_MODE_CLASS (mode) == MODE_FLOAT
+ && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
+ && (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE)
+ && GET_MODE (x) == VOIDmode
+ && (sizeof (double) * HOST_BITS_PER_CHAR
+ == 2 * HOST_BITS_PER_WIDE_INT))
+#ifdef REAL_ARITHMETIC
+ {
+ REAL_VALUE_TYPE r;
+ HOST_WIDE_INT i[2];
+ HOST_WIDE_INT low, high;
+
+ if (GET_CODE (x) == CONST_INT)
+ low = INTVAL (x), high = low >> (HOST_BITS_PER_WIDE_INT -1);
+ else
+ low = CONST_DOUBLE_LOW (x), high = CONST_DOUBLE_HIGH (x);
+
+ /* REAL_VALUE_TARGET_DOUBLE takes the addressing order of the
+ target machine. */
+ if (WORDS_BIG_ENDIAN)
+ i[0] = high, i[1] = low;
+ else
+ i[0] = low, i[1] = high;
+
+ r = REAL_VALUE_FROM_TARGET_DOUBLE (i);
+ return CONST_DOUBLE_FROM_REAL_VALUE (r, mode);
+ }
+#else
+ {
+ union {HOST_WIDE_INT i[2]; double d; } u;
+ HOST_WIDE_INT low, high;
+
+ if (GET_CODE (x) == CONST_INT)
+ low = INTVAL (x), high = low >> (HOST_BITS_PER_WIDE_INT -1);
+ else
+ low = CONST_DOUBLE_LOW (x), high = CONST_DOUBLE_HIGH (x);
+
+#ifdef HOST_WORDS_BIG_ENDIAN
+ u.i[0] = high, u.i[1] = low;
+#else
+ u.i[0] = low, u.i[1] = high;
+#endif
+
+ return CONST_DOUBLE_FROM_REAL_VALUE (u.d, mode);
+ }
+#endif
+ /* Similarly, if this is converting a floating-point value into a
+ single-word integer. Only do this is the host and target parameters are
+ compatible. */
+
+ else if (((HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
+ && HOST_BITS_PER_WIDE_INT == BITS_PER_WORD)
+ || flag_pretend_float)
+ && (GET_MODE_CLASS (mode) == MODE_INT
+ || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT)
+ && GET_CODE (x) == CONST_DOUBLE
+ && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT
+ && GET_MODE_BITSIZE (mode) == BITS_PER_WORD)
+ return operand_subword (x, 0, 0, GET_MODE (x));
+
+ /* Similarly, if this is converting a floating-point value into a
+ two-word integer, we can do this one word at a time and make an
+ integer. Only do this is the host and target parameters are
+ compatible. */
+
+ else if (((HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
+ && HOST_BITS_PER_WIDE_INT == BITS_PER_WORD)
+ || flag_pretend_float)
+ && (GET_MODE_CLASS (mode) == MODE_INT
+ || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT)
+ && GET_CODE (x) == CONST_DOUBLE
+ && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT
+ && GET_MODE_BITSIZE (mode) == 2 * BITS_PER_WORD)
+ {
+ rtx lowpart = operand_subword (x, WORDS_BIG_ENDIAN, 0, GET_MODE (x));
+ rtx highpart = operand_subword (x, ! WORDS_BIG_ENDIAN, 0, GET_MODE (x));
+
+ if (lowpart && GET_CODE (lowpart) == CONST_INT
+ && highpart && GET_CODE (highpart) == CONST_INT)
+ return immed_double_const (INTVAL (lowpart), INTVAL (highpart), mode);
+ }
+
+ /* Otherwise, we can't do this. */
+ return 0;
+}
+
+/* Return the real part (which has mode MODE) of a complex value X.
+ This always comes at the low address in memory. */
+
+rtx
+gen_realpart (mode, x)
+ enum machine_mode mode;
+ register rtx x;
+{
+ if (GET_CODE (x) == CONCAT && GET_MODE (XEXP (x, 0)) == mode)
+ return XEXP (x, 0);
+ else if (WORDS_BIG_ENDIAN)
+ return gen_highpart (mode, x);
+ else
+ return gen_lowpart (mode, x);
+}
+
+/* Return the imaginary part (which has mode MODE) of a complex value X.
+ This always comes at the high address in memory. */
+
+rtx
+gen_imagpart (mode, x)
+ enum machine_mode mode;
+ register rtx x;
+{
+ if (GET_CODE (x) == CONCAT && GET_MODE (XEXP (x, 0)) == mode)
+ return XEXP (x, 1);
+ else if (WORDS_BIG_ENDIAN)
+ return gen_lowpart (mode, x);
+ else
+ return gen_highpart (mode, x);
+}
+
+/* Return 1 iff X, assumed to be a SUBREG,
+ refers to the real part of the complex value in its containing reg.
+ Complex values are always stored with the real part in the first word,
+ regardless of WORDS_BIG_ENDIAN. */
+
+int
+subreg_realpart_p (x)
+ rtx x;
+{
+ if (GET_CODE (x) != SUBREG)
+ abort ();
+
+ return SUBREG_WORD (x) == 0;
+}
+
+/* Assuming that X is an rtx (e.g., MEM, REG or SUBREG) for a value,
+ return an rtx (MEM, SUBREG, or CONST_INT) that refers to the
+ least-significant part of X.
+ MODE specifies how big a part of X to return;
+ it usually should not be larger than a word.
+ If X is a MEM whose address is a QUEUED, the value may be so also. */
+
+rtx
+gen_lowpart (mode, x)
+ enum machine_mode mode;
+ register rtx x;
+{
+ rtx result = gen_lowpart_common (mode, x);
+
+ if (result)
+ return result;
+ else if (GET_CODE (x) == MEM)
+ {
+ /* The only additional case we can do is MEM. */
+ register int offset = 0;
+ if (WORDS_BIG_ENDIAN)
+ offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD)
+ - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD));
+
+ if (BYTES_BIG_ENDIAN)
+ /* Adjust the address so that the address-after-the-data
+ is unchanged. */
+ offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))
+ - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x))));
+
+ return change_address (x, mode, plus_constant (XEXP (x, 0), offset));
+ }
+ else
+ abort ();
+}
+
+/* Like `gen_lowpart', but refer to the most significant part.
+ This is used to access the imaginary part of a complex number. */
+
+rtx
+gen_highpart (mode, x)
+ enum machine_mode mode;
+ register rtx x;
+{
+ /* This case loses if X is a subreg. To catch bugs early,
+ complain if an invalid MODE is used even in other cases. */
+ if (GET_MODE_SIZE (mode) > UNITS_PER_WORD
+ && GET_MODE_SIZE (mode) != GET_MODE_UNIT_SIZE (GET_MODE (x)))
+ abort ();
+ if (GET_CODE (x) == CONST_DOUBLE
+#if !(TARGET_FLOAT_FORMAT != HOST_FLOAT_FORMAT || defined (REAL_IS_NOT_DOUBLE))
+ && GET_MODE_CLASS (GET_MODE (x)) != MODE_FLOAT
+#endif
+ )
+ return gen_rtx (CONST_INT, VOIDmode,
+ CONST_DOUBLE_HIGH (x) & GET_MODE_MASK (mode));
+ else if (GET_CODE (x) == CONST_INT)
+ return const0_rtx;
+ else if (GET_CODE (x) == MEM)
+ {
+ register int offset = 0;
+ if (! WORDS_BIG_ENDIAN)
+ offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD)
+ - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD));
+
+ if (! BYTES_BIG_ENDIAN
+ && GET_MODE_SIZE (mode) < UNITS_PER_WORD)
+ offset -= (GET_MODE_SIZE (mode)
+ - MIN (UNITS_PER_WORD,
+ GET_MODE_SIZE (GET_MODE (x))));
+
+ return change_address (x, mode, plus_constant (XEXP (x, 0), offset));
+ }
+ else if (GET_CODE (x) == SUBREG)
+ {
+ /* The only time this should occur is when we are looking at a
+ multi-word item with a SUBREG whose mode is the same as that of the
+ item. It isn't clear what we would do if it wasn't. */
+ if (SUBREG_WORD (x) != 0)
+ abort ();
+ return gen_highpart (mode, SUBREG_REG (x));
+ }
+ else if (GET_CODE (x) == REG)
+ {
+ int word = 0;
+
+ if (! WORDS_BIG_ENDIAN
+ && GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD)
+ word = ((GET_MODE_SIZE (GET_MODE (x))
+ - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD))
+ / UNITS_PER_WORD);
+
+ if (REGNO (x) < FIRST_PSEUDO_REGISTER
+ /* integrate.c can't handle parts of a return value register. */
+ && (! REG_FUNCTION_VALUE_P (x)
+ || ! rtx_equal_function_value_matters)
+ /* We want to keep the stack, frame, and arg pointers special. */
+ && x != frame_pointer_rtx
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ && x != arg_pointer_rtx
+#endif
+ && x != stack_pointer_rtx)
+ return gen_rtx (REG, mode, REGNO (x) + word);
+ else
+ return gen_rtx (SUBREG, mode, x, word);
+ }
+ else
+ abort ();
+}
+
+/* Return 1 iff X, assumed to be a SUBREG,
+ refers to the least significant part of its containing reg.
+ If X is not a SUBREG, always return 1 (it is its own low part!). */
+
+int
+subreg_lowpart_p (x)
+ rtx x;
+{
+ if (GET_CODE (x) != SUBREG)
+ return 1;
+
+ if (WORDS_BIG_ENDIAN
+ && GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) > UNITS_PER_WORD)
+ return (SUBREG_WORD (x)
+ == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
+ - MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD))
+ / UNITS_PER_WORD));
+
+ return SUBREG_WORD (x) == 0;
+}
+
+/* Return subword I of operand OP.
+ The word number, I, is interpreted as the word number starting at the
+ low-order address. Word 0 is the low-order word if not WORDS_BIG_ENDIAN,
+ otherwise it is the high-order word.
+
+ If we cannot extract the required word, we return zero. Otherwise, an
+ rtx corresponding to the requested word will be returned.
+
+ VALIDATE_ADDRESS is nonzero if the address should be validated. Before
+ reload has completed, a valid address will always be returned. After
+ reload, if a valid address cannot be returned, we return zero.
+
+ If VALIDATE_ADDRESS is zero, we simply form the required address; validating
+ it is the responsibility of the caller.
+
+ MODE is the mode of OP in case it is a CONST_INT. */
+
+rtx
+operand_subword (op, i, validate_address, mode)
+ rtx op;
+ int i;
+ int validate_address;
+ enum machine_mode mode;
+{
+ HOST_WIDE_INT val;
+ int size_ratio = HOST_BITS_PER_WIDE_INT / BITS_PER_WORD;
+
+ if (mode == VOIDmode)
+ mode = GET_MODE (op);
+
+ if (mode == VOIDmode)
+ abort ();
+
+ /* If OP is narrower than a word or if we want a word outside OP, fail. */
+ if (mode != BLKmode
+ && (GET_MODE_SIZE (mode) < UNITS_PER_WORD
+ || (i + 1) * UNITS_PER_WORD > GET_MODE_SIZE (mode)))
+ return 0;
+
+ /* If OP is already an integer word, return it. */
+ if (GET_MODE_CLASS (mode) == MODE_INT
+ && GET_MODE_SIZE (mode) == UNITS_PER_WORD)
+ return op;
+
+ /* If OP is a REG or SUBREG, we can handle it very simply. */
+ if (GET_CODE (op) == REG)
+ {
+ /* If the register is not valid for MODE, return 0. If we don't
+ do this, there is no way to fix up the resulting REG later. */
+ if (REGNO (op) < FIRST_PSEUDO_REGISTER
+ && ! HARD_REGNO_MODE_OK (REGNO (op) + i, word_mode))
+ return 0;
+ else if (REGNO (op) >= FIRST_PSEUDO_REGISTER
+ || (REG_FUNCTION_VALUE_P (op)
+ && rtx_equal_function_value_matters)
+ /* We want to keep the stack, frame, and arg pointers
+ special. */
+ || op == frame_pointer_rtx
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ || op == arg_pointer_rtx
+#endif
+ || op == stack_pointer_rtx)
+ return gen_rtx (SUBREG, word_mode, op, i);
+ else
+ return gen_rtx (REG, word_mode, REGNO (op) + i);
+ }
+ else if (GET_CODE (op) == SUBREG)
+ return gen_rtx (SUBREG, word_mode, SUBREG_REG (op), i + SUBREG_WORD (op));
+ else if (GET_CODE (op) == CONCAT)
+ {
+ int partwords = GET_MODE_UNIT_SIZE (GET_MODE (op)) / UNITS_PER_WORD;
+ if (i < partwords)
+ return operand_subword (XEXP (op, 0), i, validate_address, mode);
+ return operand_subword (XEXP (op, 1), i - partwords,
+ validate_address, mode);
+ }
+
+ /* Form a new MEM at the requested address. */
+ if (GET_CODE (op) == MEM)
+ {
+ rtx addr = plus_constant (XEXP (op, 0), i * UNITS_PER_WORD);
+ rtx new;
+
+ if (validate_address)
+ {
+ if (reload_completed)
+ {
+ if (! strict_memory_address_p (word_mode, addr))
+ return 0;
+ }
+ else
+ addr = memory_address (word_mode, addr);
+ }
+
+ new = gen_rtx (MEM, word_mode, addr);
+
+ MEM_VOLATILE_P (new) = MEM_VOLATILE_P (op);
+ MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (op);
+ RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (op);
+
+ return new;
+ }
+
+ /* The only remaining cases are when OP is a constant. If the host and
+ target floating formats are the same, handling two-word floating
+ constants are easy. Note that REAL_VALUE_TO_TARGET_{SINGLE,DOUBLE}
+ are defined as returning one or two 32 bit values, respectively,
+ and not values of BITS_PER_WORD bits. */
+#ifdef REAL_ARITHMETIC
+/* The output is some bits, the width of the target machine's word.
+ A wider-word host can surely hold them in a CONST_INT. A narrower-word
+ host can't. */
+ if (HOST_BITS_PER_WIDE_INT >= BITS_PER_WORD
+ && GET_MODE_CLASS (mode) == MODE_FLOAT
+ && GET_MODE_BITSIZE (mode) == 64
+ && GET_CODE (op) == CONST_DOUBLE)
+ {
+ long k[2];
+ REAL_VALUE_TYPE rv;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
+ REAL_VALUE_TO_TARGET_DOUBLE (rv, k);
+
+ /* We handle 32-bit and >= 64-bit words here. Note that the order in
+ which the words are written depends on the word endianness.
+
+ ??? This is a potential portability problem and should
+ be fixed at some point. */
+ if (BITS_PER_WORD == 32)
+ return GEN_INT ((HOST_WIDE_INT) k[i]);
+#if HOST_BITS_PER_WIDE_INT > 32
+ else if (BITS_PER_WORD >= 64 && i == 0)
+ return GEN_INT ((((HOST_WIDE_INT) k[! WORDS_BIG_ENDIAN]) << 32)
+ | (HOST_WIDE_INT) k[WORDS_BIG_ENDIAN]);
+#endif
+ else
+ abort ();
+ }
+#else /* no REAL_ARITHMETIC */
+ if (((HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
+ && HOST_BITS_PER_WIDE_INT == BITS_PER_WORD)
+ || flag_pretend_float)
+ && GET_MODE_CLASS (mode) == MODE_FLOAT
+ && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
+ && GET_CODE (op) == CONST_DOUBLE)
+ {
+ /* The constant is stored in the host's word-ordering,
+ but we want to access it in the target's word-ordering. Some
+ compilers don't like a conditional inside macro args, so we have two
+ copies of the return. */
+#ifdef HOST_WORDS_BIG_ENDIAN
+ return GEN_INT (i == WORDS_BIG_ENDIAN
+ ? CONST_DOUBLE_HIGH (op) : CONST_DOUBLE_LOW (op));
+#else
+ return GEN_INT (i != WORDS_BIG_ENDIAN
+ ? CONST_DOUBLE_HIGH (op) : CONST_DOUBLE_LOW (op));
+#endif
+ }
+#endif /* no REAL_ARITHMETIC */
+
+ /* Single word float is a little harder, since single- and double-word
+ values often do not have the same high-order bits. We have already
+ verified that we want the only defined word of the single-word value. */
+#ifdef REAL_ARITHMETIC
+ if (GET_MODE_CLASS (mode) == MODE_FLOAT
+ && GET_MODE_BITSIZE (mode) == 32
+ && GET_CODE (op) == CONST_DOUBLE)
+ {
+ long l;
+ REAL_VALUE_TYPE rv;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
+ REAL_VALUE_TO_TARGET_SINGLE (rv, l);
+ return GEN_INT ((HOST_WIDE_INT) l);
+ }
+#else
+ if (((HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
+ && HOST_BITS_PER_WIDE_INT == BITS_PER_WORD)
+ || flag_pretend_float)
+ && GET_MODE_CLASS (mode) == MODE_FLOAT
+ && GET_MODE_SIZE (mode) == UNITS_PER_WORD
+ && GET_CODE (op) == CONST_DOUBLE)
+ {
+ double d;
+ union {float f; HOST_WIDE_INT i; } u;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (d, op);
+
+ u.f = d;
+ return GEN_INT (u.i);
+ }
+#endif /* no REAL_ARITHMETIC */
+
+ /* The only remaining cases that we can handle are integers.
+ Convert to proper endianness now since these cases need it.
+ At this point, i == 0 means the low-order word.
+
+ We do not want to handle the case when BITS_PER_WORD <= HOST_BITS_PER_INT
+ in general. However, if OP is (const_int 0), we can just return
+ it for any word. */
+
+ if (op == const0_rtx)
+ return op;
+
+ if (GET_MODE_CLASS (mode) != MODE_INT
+ || (GET_CODE (op) != CONST_INT && GET_CODE (op) != CONST_DOUBLE)
+ || BITS_PER_WORD > HOST_BITS_PER_INT)
+ return 0;
+
+ if (WORDS_BIG_ENDIAN)
+ i = GET_MODE_SIZE (mode) / UNITS_PER_WORD - 1 - i;
+
+ /* Find out which word on the host machine this value is in and get
+ it from the constant. */
+ val = (i / size_ratio == 0
+ ? (GET_CODE (op) == CONST_INT ? INTVAL (op) : CONST_DOUBLE_LOW (op))
+ : (GET_CODE (op) == CONST_INT
+ ? (INTVAL (op) < 0 ? ~0 : 0) : CONST_DOUBLE_HIGH (op)));
+
+ /* If BITS_PER_WORD is smaller than an int, get the appropriate bits. */
+ if (BITS_PER_WORD < HOST_BITS_PER_WIDE_INT)
+ val = ((val >> ((i % size_ratio) * BITS_PER_WORD))
+ & (((HOST_WIDE_INT) 1
+ << (BITS_PER_WORD % HOST_BITS_PER_WIDE_INT)) - 1));
+
+ return GEN_INT (val);
+}
+
+/* Similar to `operand_subword', but never return 0. If we can't extract
+ the required subword, put OP into a register and try again. If that fails,
+ abort. We always validate the address in this case. It is not valid
+ to call this function after reload; it is mostly meant for RTL
+ generation.
+
+ MODE is the mode of OP, in case it is CONST_INT. */
+
+rtx
+operand_subword_force (op, i, mode)
+ rtx op;
+ int i;
+ enum machine_mode mode;
+{
+ rtx result = operand_subword (op, i, 1, mode);
+
+ if (result)
+ return result;
+
+ if (mode != BLKmode && mode != VOIDmode)
+ op = force_reg (mode, op);
+
+ result = operand_subword (op, i, 1, mode);
+ if (result == 0)
+ abort ();
+
+ return result;
+}
+
+/* Given a compare instruction, swap the operands.
+ A test instruction is changed into a compare of 0 against the operand. */
+
+void
+reverse_comparison (insn)
+ rtx insn;
+{
+ rtx body = PATTERN (insn);
+ rtx comp;
+
+ if (GET_CODE (body) == SET)
+ comp = SET_SRC (body);
+ else
+ comp = SET_SRC (XVECEXP (body, 0, 0));
+
+ if (GET_CODE (comp) == COMPARE)
+ {
+ rtx op0 = XEXP (comp, 0);
+ rtx op1 = XEXP (comp, 1);
+ XEXP (comp, 0) = op1;
+ XEXP (comp, 1) = op0;
+ }
+ else
+ {
+ rtx new = gen_rtx (COMPARE, VOIDmode,
+ CONST0_RTX (GET_MODE (comp)), comp);
+ if (GET_CODE (body) == SET)
+ SET_SRC (body) = new;
+ else
+ SET_SRC (XVECEXP (body, 0, 0)) = new;
+ }
+}
+
+/* Return a memory reference like MEMREF, but with its mode changed
+ to MODE and its address changed to ADDR.
+ (VOIDmode means don't change the mode.
+ NULL for ADDR means don't change the address.) */
+
+rtx
+change_address (memref, mode, addr)
+ rtx memref;
+ enum machine_mode mode;
+ rtx addr;
+{
+ rtx new;
+
+ if (GET_CODE (memref) != MEM)
+ abort ();
+ if (mode == VOIDmode)
+ mode = GET_MODE (memref);
+ if (addr == 0)
+ addr = XEXP (memref, 0);
+
+ /* If reload is in progress or has completed, ADDR must be valid.
+ Otherwise, we can call memory_address to make it valid. */
+ if (reload_completed || reload_in_progress)
+ {
+ if (! memory_address_p (mode, addr))
+ abort ();
+ }
+ else
+ addr = memory_address (mode, addr);
+
+ new = gen_rtx (MEM, mode, addr);
+ MEM_VOLATILE_P (new) = MEM_VOLATILE_P (memref);
+ RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (memref);
+ MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (memref);
+ return new;
+}
+
+/* Return a newly created CODE_LABEL rtx with a unique label number. */
+
+rtx
+gen_label_rtx ()
+{
+ register rtx label;
+
+ label = (output_bytecode
+ ? gen_rtx (CODE_LABEL, VOIDmode, NULL, bc_get_bytecode_label ())
+ : gen_rtx (CODE_LABEL, VOIDmode, 0, 0, 0, label_num++, NULL_PTR));
+
+ LABEL_NUSES (label) = 0;
+ return label;
+}
+
+/* For procedure integration. */
+
+/* Return a newly created INLINE_HEADER rtx. Should allocate this
+ from a permanent obstack when the opportunity arises. */
+
+rtx
+gen_inline_header_rtx (first_insn, first_parm_insn, first_labelno,
+ last_labelno, max_parm_regnum, max_regnum, args_size,
+ pops_args, stack_slots, function_flags,
+ outgoing_args_size, original_arg_vector,
+ original_decl_initial)
+ rtx first_insn, first_parm_insn;
+ int first_labelno, last_labelno, max_parm_regnum, max_regnum, args_size;
+ int pops_args;
+ rtx stack_slots;
+ int function_flags;
+ int outgoing_args_size;
+ rtvec original_arg_vector;
+ rtx original_decl_initial;
+{
+ rtx header = gen_rtx (INLINE_HEADER, VOIDmode,
+ cur_insn_uid++, NULL_RTX,
+ first_insn, first_parm_insn,
+ first_labelno, last_labelno,
+ max_parm_regnum, max_regnum, args_size, pops_args,
+ stack_slots, function_flags, outgoing_args_size,
+ original_arg_vector, original_decl_initial);
+ return header;
+}
+
+/* Install new pointers to the first and last insns in the chain.
+ Used for an inline-procedure after copying the insn chain. */
+
+void
+set_new_first_and_last_insn (first, last)
+ rtx first, last;
+{
+ first_insn = first;
+ last_insn = last;
+}
+
+/* Set the range of label numbers found in the current function.
+ This is used when belatedly compiling an inline function. */
+
+void
+set_new_first_and_last_label_num (first, last)
+ int first, last;
+{
+ base_label_num = label_num;
+ first_label_num = first;
+ last_label_num = last;
+}
+
+/* Save all variables describing the current status into the structure *P.
+ This is used before starting a nested function. */
+
+void
+save_emit_status (p)
+ struct function *p;
+{
+ p->reg_rtx_no = reg_rtx_no;
+ p->first_label_num = first_label_num;
+ p->first_insn = first_insn;
+ p->last_insn = last_insn;
+ p->sequence_rtl_expr = sequence_rtl_expr;
+ p->sequence_stack = sequence_stack;
+ p->cur_insn_uid = cur_insn_uid;
+ p->last_linenum = last_linenum;
+ p->last_filename = last_filename;
+ p->regno_pointer_flag = regno_pointer_flag;
+ p->regno_pointer_flag_length = regno_pointer_flag_length;
+ p->regno_reg_rtx = regno_reg_rtx;
+}
+
+/* Restore all variables describing the current status from the structure *P.
+ This is used after a nested function. */
+
+void
+restore_emit_status (p)
+ struct function *p;
+{
+ int i;
+
+ reg_rtx_no = p->reg_rtx_no;
+ first_label_num = p->first_label_num;
+ last_label_num = 0;
+ first_insn = p->first_insn;
+ last_insn = p->last_insn;
+ sequence_rtl_expr = p->sequence_rtl_expr;
+ sequence_stack = p->sequence_stack;
+ cur_insn_uid = p->cur_insn_uid;
+ last_linenum = p->last_linenum;
+ last_filename = p->last_filename;
+ regno_pointer_flag = p->regno_pointer_flag;
+ regno_pointer_flag_length = p->regno_pointer_flag_length;
+ regno_reg_rtx = p->regno_reg_rtx;
+
+ /* Clear our cache of rtx expressions for start_sequence and gen_sequence. */
+ sequence_element_free_list = 0;
+ for (i = 0; i < SEQUENCE_RESULT_SIZE; i++)
+ sequence_result[i] = 0;
+}
+
+/* Go through all the RTL insn bodies and copy any invalid shared structure.
+ It does not work to do this twice, because the mark bits set here
+ are not cleared afterwards. */
+
+void
+unshare_all_rtl (insn)
+ register rtx insn;
+{
+ for (; insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN
+ || GET_CODE (insn) == CALL_INSN)
+ {
+ PATTERN (insn) = copy_rtx_if_shared (PATTERN (insn));
+ REG_NOTES (insn) = copy_rtx_if_shared (REG_NOTES (insn));
+ LOG_LINKS (insn) = copy_rtx_if_shared (LOG_LINKS (insn));
+ }
+
+ /* Make sure the addresses of stack slots found outside the insn chain
+ (such as, in DECL_RTL of a variable) are not shared
+ with the insn chain.
+
+ This special care is necessary when the stack slot MEM does not
+ actually appear in the insn chain. If it does appear, its address
+ is unshared from all else at that point. */
+
+ copy_rtx_if_shared (stack_slot_list);
+}
+
+/* Mark ORIG as in use, and return a copy of it if it was already in use.
+ Recursively does the same for subexpressions. */
+
+rtx
+copy_rtx_if_shared (orig)
+ rtx orig;
+{
+ register rtx x = orig;
+ register int i;
+ register enum rtx_code code;
+ register char *format_ptr;
+ int copied = 0;
+
+ if (x == 0)
+ return 0;
+
+ code = GET_CODE (x);
+
+ /* These types may be freely shared. */
+
+ switch (code)
+ {
+ case REG:
+ case QUEUED:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case CODE_LABEL:
+ case PC:
+ case CC0:
+ case SCRATCH:
+ /* SCRATCH must be shared because they represent distinct values. */
+ return x;
+
+ case CONST:
+ /* CONST can be shared if it contains a SYMBOL_REF. If it contains
+ a LABEL_REF, it isn't sharable. */
+ if (GET_CODE (XEXP (x, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)
+ return x;
+ break;
+
+ case INSN:
+ case JUMP_INSN:
+ case CALL_INSN:
+ case NOTE:
+ case BARRIER:
+ /* The chain of insns is not being copied. */
+ return x;
+
+ case MEM:
+ /* A MEM is allowed to be shared if its address is constant
+ or is a constant plus one of the special registers. */
+ if (CONSTANT_ADDRESS_P (XEXP (x, 0))
+ || XEXP (x, 0) == virtual_stack_vars_rtx
+ || XEXP (x, 0) == virtual_incoming_args_rtx)
+ return x;
+
+ if (GET_CODE (XEXP (x, 0)) == PLUS
+ && (XEXP (XEXP (x, 0), 0) == virtual_stack_vars_rtx
+ || XEXP (XEXP (x, 0), 0) == virtual_incoming_args_rtx)
+ && CONSTANT_ADDRESS_P (XEXP (XEXP (x, 0), 1)))
+ {
+ /* This MEM can appear in more than one place,
+ but its address better not be shared with anything else. */
+ if (! x->used)
+ XEXP (x, 0) = copy_rtx_if_shared (XEXP (x, 0));
+ x->used = 1;
+ return x;
+ }
+ }
+
+ /* This rtx may not be shared. If it has already been seen,
+ replace it with a copy of itself. */
+
+ if (x->used)
+ {
+ register rtx copy;
+
+ copy = rtx_alloc (code);
+ bcopy ((char *) x, (char *) copy,
+ (sizeof (*copy) - sizeof (copy->fld)
+ + sizeof (copy->fld[0]) * GET_RTX_LENGTH (code)));
+ x = copy;
+ copied = 1;
+ }
+ x->used = 1;
+
+ /* Now scan the subexpressions recursively.
+ We can store any replaced subexpressions directly into X
+ since we know X is not shared! Any vectors in X
+ must be copied if X was copied. */
+
+ format_ptr = GET_RTX_FORMAT (code);
+
+ for (i = 0; i < GET_RTX_LENGTH (code); i++)
+ {
+ switch (*format_ptr++)
+ {
+ case 'e':
+ XEXP (x, i) = copy_rtx_if_shared (XEXP (x, i));
+ break;
+
+ case 'E':
+ if (XVEC (x, i) != NULL)
+ {
+ register int j;
+ int len = XVECLEN (x, i);
+
+ if (copied && len > 0)
+ XVEC (x, i) = gen_rtvec_v (len, &XVECEXP (x, i, 0));
+ for (j = 0; j < len; j++)
+ XVECEXP (x, i, j) = copy_rtx_if_shared (XVECEXP (x, i, j));
+ }
+ break;
+ }
+ }
+ return x;
+}
+
+/* Clear all the USED bits in X to allow copy_rtx_if_shared to be used
+ to look for shared sub-parts. */
+
+void
+reset_used_flags (x)
+ rtx x;
+{
+ register int i, j;
+ register enum rtx_code code;
+ register char *format_ptr;
+
+ if (x == 0)
+ return;
+
+ code = GET_CODE (x);
+
+ /* These types may be freely shared so we needn't do any reseting
+ for them. */
+
+ switch (code)
+ {
+ case REG:
+ case QUEUED:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case CODE_LABEL:
+ case PC:
+ case CC0:
+ return;
+
+ case INSN:
+ case JUMP_INSN:
+ case CALL_INSN:
+ case NOTE:
+ case LABEL_REF:
+ case BARRIER:
+ /* The chain of insns is not being copied. */
+ return;
+ }
+
+ x->used = 0;
+
+ format_ptr = GET_RTX_FORMAT (code);
+ for (i = 0; i < GET_RTX_LENGTH (code); i++)
+ {
+ switch (*format_ptr++)
+ {
+ case 'e':
+ reset_used_flags (XEXP (x, i));
+ break;
+
+ case 'E':
+ for (j = 0; j < XVECLEN (x, i); j++)
+ reset_used_flags (XVECEXP (x, i, j));
+ break;
+ }
+ }
+}
+
+/* Copy X if necessary so that it won't be altered by changes in OTHER.
+ Return X or the rtx for the pseudo reg the value of X was copied into.
+ OTHER must be valid as a SET_DEST. */
+
+rtx
+make_safe_from (x, other)
+ rtx x, other;
+{
+ while (1)
+ switch (GET_CODE (other))
+ {
+ case SUBREG:
+ other = SUBREG_REG (other);
+ break;
+ case STRICT_LOW_PART:
+ case SIGN_EXTEND:
+ case ZERO_EXTEND:
+ other = XEXP (other, 0);
+ break;
+ default:
+ goto done;
+ }
+ done:
+ if ((GET_CODE (other) == MEM
+ && ! CONSTANT_P (x)
+ && GET_CODE (x) != REG
+ && GET_CODE (x) != SUBREG)
+ || (GET_CODE (other) == REG
+ && (REGNO (other) < FIRST_PSEUDO_REGISTER
+ || reg_mentioned_p (other, x))))
+ {
+ rtx temp = gen_reg_rtx (GET_MODE (x));
+ emit_move_insn (temp, x);
+ return temp;
+ }
+ return x;
+}
+
+/* Emission of insns (adding them to the doubly-linked list). */
+
+/* Return the first insn of the current sequence or current function. */
+
+rtx
+get_insns ()
+{
+ return first_insn;
+}
+
+/* Return the last insn emitted in current sequence or current function. */
+
+rtx
+get_last_insn ()
+{
+ return last_insn;
+}
+
+/* Specify a new insn as the last in the chain. */
+
+void
+set_last_insn (insn)
+ rtx insn;
+{
+ if (NEXT_INSN (insn) != 0)
+ abort ();
+ last_insn = insn;
+}
+
+/* Return the last insn emitted, even if it is in a sequence now pushed. */
+
+rtx
+get_last_insn_anywhere ()
+{
+ struct sequence_stack *stack;
+ if (last_insn)
+ return last_insn;
+ for (stack = sequence_stack; stack; stack = stack->next)
+ if (stack->last != 0)
+ return stack->last;
+ return 0;
+}
+
+/* Return a number larger than any instruction's uid in this function. */
+
+int
+get_max_uid ()
+{
+ return cur_insn_uid;
+}
+
+/* Return the next insn. If it is a SEQUENCE, return the first insn
+ of the sequence. */
+
+rtx
+next_insn (insn)
+ rtx insn;
+{
+ if (insn)
+ {
+ insn = NEXT_INSN (insn);
+ if (insn && GET_CODE (insn) == INSN
+ && GET_CODE (PATTERN (insn)) == SEQUENCE)
+ insn = XVECEXP (PATTERN (insn), 0, 0);
+ }
+
+ return insn;
+}
+
+/* Return the previous insn. If it is a SEQUENCE, return the last insn
+ of the sequence. */
+
+rtx
+previous_insn (insn)
+ rtx insn;
+{
+ if (insn)
+ {
+ insn = PREV_INSN (insn);
+ if (insn && GET_CODE (insn) == INSN
+ && GET_CODE (PATTERN (insn)) == SEQUENCE)
+ insn = XVECEXP (PATTERN (insn), 0, XVECLEN (PATTERN (insn), 0) - 1);
+ }
+
+ return insn;
+}
+
+/* Return the next insn after INSN that is not a NOTE. This routine does not
+ look inside SEQUENCEs. */
+
+rtx
+next_nonnote_insn (insn)
+ rtx insn;
+{
+ while (insn)
+ {
+ insn = NEXT_INSN (insn);
+ if (insn == 0 || GET_CODE (insn) != NOTE)
+ break;
+ }
+
+ return insn;
+}
+
+/* Return the previous insn before INSN that is not a NOTE. This routine does
+ not look inside SEQUENCEs. */
+
+rtx
+prev_nonnote_insn (insn)
+ rtx insn;
+{
+ while (insn)
+ {
+ insn = PREV_INSN (insn);
+ if (insn == 0 || GET_CODE (insn) != NOTE)
+ break;
+ }
+
+ return insn;
+}
+
+/* Return the next INSN, CALL_INSN or JUMP_INSN after INSN;
+ or 0, if there is none. This routine does not look inside
+ SEQUENCEs. */
+
+rtx
+next_real_insn (insn)
+ rtx insn;
+{
+ while (insn)
+ {
+ insn = NEXT_INSN (insn);
+ if (insn == 0 || GET_CODE (insn) == INSN
+ || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
+ break;
+ }
+
+ return insn;
+}
+
+/* Return the last INSN, CALL_INSN or JUMP_INSN before INSN;
+ or 0, if there is none. This routine does not look inside
+ SEQUENCEs. */
+
+rtx
+prev_real_insn (insn)
+ rtx insn;
+{
+ while (insn)
+ {
+ insn = PREV_INSN (insn);
+ if (insn == 0 || GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN
+ || GET_CODE (insn) == JUMP_INSN)
+ break;
+ }
+
+ return insn;
+}
+
+/* Find the next insn after INSN that really does something. This routine
+ does not look inside SEQUENCEs. Until reload has completed, this is the
+ same as next_real_insn. */
+
+rtx
+next_active_insn (insn)
+ rtx insn;
+{
+ while (insn)
+ {
+ insn = NEXT_INSN (insn);
+ if (insn == 0
+ || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN
+ || (GET_CODE (insn) == INSN
+ && (! reload_completed
+ || (GET_CODE (PATTERN (insn)) != USE
+ && GET_CODE (PATTERN (insn)) != CLOBBER))))
+ break;
+ }
+
+ return insn;
+}
+
+/* Find the last insn before INSN that really does something. This routine
+ does not look inside SEQUENCEs. Until reload has completed, this is the
+ same as prev_real_insn. */
+
+rtx
+prev_active_insn (insn)
+ rtx insn;
+{
+ while (insn)
+ {
+ insn = PREV_INSN (insn);
+ if (insn == 0
+ || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN
+ || (GET_CODE (insn) == INSN
+ && (! reload_completed
+ || (GET_CODE (PATTERN (insn)) != USE
+ && GET_CODE (PATTERN (insn)) != CLOBBER))))
+ break;
+ }
+
+ return insn;
+}
+
+/* Return the next CODE_LABEL after the insn INSN, or 0 if there is none. */
+
+rtx
+next_label (insn)
+ rtx insn;
+{
+ while (insn)
+ {
+ insn = NEXT_INSN (insn);
+ if (insn == 0 || GET_CODE (insn) == CODE_LABEL)
+ break;
+ }
+
+ return insn;
+}
+
+/* Return the last CODE_LABEL before the insn INSN, or 0 if there is none. */
+
+rtx
+prev_label (insn)
+ rtx insn;
+{
+ while (insn)
+ {
+ insn = PREV_INSN (insn);
+ if (insn == 0 || GET_CODE (insn) == CODE_LABEL)
+ break;
+ }
+
+ return insn;
+}
+
+#ifdef HAVE_cc0
+/* INSN uses CC0 and is being moved into a delay slot. Set up REG_CC_SETTER
+ and REG_CC_USER notes so we can find it. */
+
+void
+link_cc0_insns (insn)
+ rtx insn;
+{
+ rtx user = next_nonnote_insn (insn);
+
+ if (GET_CODE (user) == INSN && GET_CODE (PATTERN (user)) == SEQUENCE)
+ user = XVECEXP (PATTERN (user), 0, 0);
+
+ REG_NOTES (user) = gen_rtx (INSN_LIST, REG_CC_SETTER, insn,
+ REG_NOTES (user));
+ REG_NOTES (insn) = gen_rtx (INSN_LIST, REG_CC_USER, user, REG_NOTES (insn));
+}
+
+/* Return the next insn that uses CC0 after INSN, which is assumed to
+ set it. This is the inverse of prev_cc0_setter (i.e., prev_cc0_setter
+ applied to the result of this function should yield INSN).
+
+ Normally, this is simply the next insn. However, if a REG_CC_USER note
+ is present, it contains the insn that uses CC0.
+
+ Return 0 if we can't find the insn. */
+
+rtx
+next_cc0_user (insn)
+ rtx insn;
+{
+ rtx note = find_reg_note (insn, REG_CC_USER, NULL_RTX);
+
+ if (note)
+ return XEXP (note, 0);
+
+ insn = next_nonnote_insn (insn);
+ if (insn && GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE)
+ insn = XVECEXP (PATTERN (insn), 0, 0);
+
+ if (insn && GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && reg_mentioned_p (cc0_rtx, PATTERN (insn)))
+ return insn;
+
+ return 0;
+}
+
+/* Find the insn that set CC0 for INSN. Unless INSN has a REG_CC_SETTER
+ note, it is the previous insn. */
+
+rtx
+prev_cc0_setter (insn)
+ rtx insn;
+{
+ rtx note = find_reg_note (insn, REG_CC_SETTER, NULL_RTX);
+ rtx link;
+
+ if (note)
+ return XEXP (note, 0);
+
+ insn = prev_nonnote_insn (insn);
+ if (! sets_cc0_p (PATTERN (insn)))
+ abort ();
+
+ return insn;
+}
+#endif
+
+/* Try splitting insns that can be split for better scheduling.
+ PAT is the pattern which might split.
+ TRIAL is the insn providing PAT.
+ LAST is non-zero if we should return the last insn of the sequence produced.
+
+ If this routine succeeds in splitting, it returns the first or last
+ replacement insn depending on the value of LAST. Otherwise, it
+ returns TRIAL. If the insn to be returned can be split, it will be. */
+
+rtx
+try_split (pat, trial, last)
+ rtx pat, trial;
+ int last;
+{
+ rtx before = PREV_INSN (trial);
+ rtx after = NEXT_INSN (trial);
+ rtx seq = split_insns (pat, trial);
+ int has_barrier = 0;
+ rtx tem;
+
+ /* If we are splitting a JUMP_INSN, it might be followed by a BARRIER.
+ We may need to handle this specially. */
+ if (after && GET_CODE (after) == BARRIER)
+ {
+ has_barrier = 1;
+ after = NEXT_INSN (after);
+ }
+
+ if (seq)
+ {
+ /* SEQ can either be a SEQUENCE or the pattern of a single insn.
+ The latter case will normally arise only when being done so that
+ it, in turn, will be split (SFmode on the 29k is an example). */
+ if (GET_CODE (seq) == SEQUENCE)
+ {
+ /* If we are splitting a JUMP_INSN, look for the JUMP_INSN in
+ SEQ and copy our JUMP_LABEL to it. If JUMP_LABEL is non-zero,
+ increment the usage count so we don't delete the label. */
+ int i;
+
+ if (GET_CODE (trial) == JUMP_INSN)
+ for (i = XVECLEN (seq, 0) - 1; i >= 0; i--)
+ if (GET_CODE (XVECEXP (seq, 0, i)) == JUMP_INSN)
+ {
+ JUMP_LABEL (XVECEXP (seq, 0, i)) = JUMP_LABEL (trial);
+
+ if (JUMP_LABEL (trial))
+ LABEL_NUSES (JUMP_LABEL (trial))++;
+ }
+
+ tem = emit_insn_after (seq, before);
+
+ delete_insn (trial);
+ if (has_barrier)
+ emit_barrier_after (tem);
+
+ /* Recursively call try_split for each new insn created; by the
+ time control returns here that insn will be fully split, so
+ set LAST and continue from the insn after the one returned.
+ We can't use next_active_insn here since AFTER may be a note.
+ Ignore deleted insns, which can be occur if not optimizing. */
+ for (tem = NEXT_INSN (before); tem != after;
+ tem = NEXT_INSN (tem))
+ if (! INSN_DELETED_P (tem))
+ tem = try_split (PATTERN (tem), tem, 1);
+ }
+ /* Avoid infinite loop if the result matches the original pattern. */
+ else if (rtx_equal_p (seq, pat))
+ return trial;
+ else
+ {
+ PATTERN (trial) = seq;
+ INSN_CODE (trial) = -1;
+ try_split (seq, trial, last);
+ }
+
+ /* Return either the first or the last insn, depending on which was
+ requested. */
+ return last ? prev_active_insn (after) : next_active_insn (before);
+ }
+
+ return trial;
+}
+
+/* Make and return an INSN rtx, initializing all its slots.
+ Store PATTERN in the pattern slots. */
+
+rtx
+make_insn_raw (pattern)
+ rtx pattern;
+{
+ register rtx insn;
+
+ insn = rtx_alloc (INSN);
+ INSN_UID (insn) = cur_insn_uid++;
+
+ PATTERN (insn) = pattern;
+ INSN_CODE (insn) = -1;
+ LOG_LINKS (insn) = NULL;
+ REG_NOTES (insn) = NULL;
+
+ return insn;
+}
+
+/* Like `make_insn' but make a JUMP_INSN instead of an insn. */
+
+static rtx
+make_jump_insn_raw (pattern)
+ rtx pattern;
+{
+ register rtx insn;
+
+ insn = rtx_alloc (JUMP_INSN);
+ INSN_UID (insn) = cur_insn_uid++;
+
+ PATTERN (insn) = pattern;
+ INSN_CODE (insn) = -1;
+ LOG_LINKS (insn) = NULL;
+ REG_NOTES (insn) = NULL;
+ JUMP_LABEL (insn) = NULL;
+
+ return insn;
+}
+
+/* Like `make_insn' but make a CALL_INSN instead of an insn. */
+
+static rtx
+make_call_insn_raw (pattern)
+ rtx pattern;
+{
+ register rtx insn;
+
+ insn = rtx_alloc (CALL_INSN);
+ INSN_UID (insn) = cur_insn_uid++;
+
+ PATTERN (insn) = pattern;
+ INSN_CODE (insn) = -1;
+ LOG_LINKS (insn) = NULL;
+ REG_NOTES (insn) = NULL;
+ CALL_INSN_FUNCTION_USAGE (insn) = NULL;
+
+ return insn;
+}
+
+/* Add INSN to the end of the doubly-linked list.
+ INSN may be an INSN, JUMP_INSN, CALL_INSN, CODE_LABEL, BARRIER or NOTE. */
+
+void
+add_insn (insn)
+ register rtx insn;
+{
+ PREV_INSN (insn) = last_insn;
+ NEXT_INSN (insn) = 0;
+
+ if (NULL != last_insn)
+ NEXT_INSN (last_insn) = insn;
+
+ if (NULL == first_insn)
+ first_insn = insn;
+
+ last_insn = insn;
+}
+
+/* Add INSN into the doubly-linked list after insn AFTER. This should be the
+ only function called to insert an insn once delay slots have been filled
+ since only it knows how to update a SEQUENCE. */
+
+void
+add_insn_after (insn, after)
+ rtx insn, after;
+{
+ rtx next = NEXT_INSN (after);
+
+ NEXT_INSN (insn) = next;
+ PREV_INSN (insn) = after;
+
+ if (next)
+ {
+ PREV_INSN (next) = insn;
+ if (GET_CODE (next) == INSN && GET_CODE (PATTERN (next)) == SEQUENCE)
+ PREV_INSN (XVECEXP (PATTERN (next), 0, 0)) = insn;
+ }
+ else if (last_insn == after)
+ last_insn = insn;
+ else
+ {
+ struct sequence_stack *stack = sequence_stack;
+ /* Scan all pending sequences too. */
+ for (; stack; stack = stack->next)
+ if (after == stack->last)
+ stack->last = insn;
+ }
+
+ NEXT_INSN (after) = insn;
+ if (GET_CODE (after) == INSN && GET_CODE (PATTERN (after)) == SEQUENCE)
+ {
+ rtx sequence = PATTERN (after);
+ NEXT_INSN (XVECEXP (sequence, 0, XVECLEN (sequence, 0) - 1)) = insn;
+ }
+}
+
+/* Delete all insns made since FROM.
+ FROM becomes the new last instruction. */
+
+void
+delete_insns_since (from)
+ rtx from;
+{
+ if (from == 0)
+ first_insn = 0;
+ else
+ NEXT_INSN (from) = 0;
+ last_insn = from;
+}
+
+/* This function is deprecated, please use sequences instead.
+
+ Move a consecutive bunch of insns to a different place in the chain.
+ The insns to be moved are those between FROM and TO.
+ They are moved to a new position after the insn AFTER.
+ AFTER must not be FROM or TO or any insn in between.
+
+ This function does not know about SEQUENCEs and hence should not be
+ called after delay-slot filling has been done. */
+
+void
+reorder_insns (from, to, after)
+ rtx from, to, after;
+{
+ /* Splice this bunch out of where it is now. */
+ if (PREV_INSN (from))
+ NEXT_INSN (PREV_INSN (from)) = NEXT_INSN (to);
+ if (NEXT_INSN (to))
+ PREV_INSN (NEXT_INSN (to)) = PREV_INSN (from);
+ if (last_insn == to)
+ last_insn = PREV_INSN (from);
+ if (first_insn == from)
+ first_insn = NEXT_INSN (to);
+
+ /* Make the new neighbors point to it and it to them. */
+ if (NEXT_INSN (after))
+ PREV_INSN (NEXT_INSN (after)) = to;
+
+ NEXT_INSN (to) = NEXT_INSN (after);
+ PREV_INSN (from) = after;
+ NEXT_INSN (after) = from;
+ if (after == last_insn)
+ last_insn = to;
+}
+
+/* Return the line note insn preceding INSN. */
+
+static rtx
+find_line_note (insn)
+ rtx insn;
+{
+ if (no_line_numbers)
+ return 0;
+
+ for (; insn; insn = PREV_INSN (insn))
+ if (GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) >= 0)
+ break;
+
+ return insn;
+}
+
+/* Like reorder_insns, but inserts line notes to preserve the line numbers
+ of the moved insns when debugging. This may insert a note between AFTER
+ and FROM, and another one after TO. */
+
+void
+reorder_insns_with_line_notes (from, to, after)
+ rtx from, to, after;
+{
+ rtx from_line = find_line_note (from);
+ rtx after_line = find_line_note (after);
+
+ reorder_insns (from, to, after);
+
+ if (from_line == after_line)
+ return;
+
+ if (from_line)
+ emit_line_note_after (NOTE_SOURCE_FILE (from_line),
+ NOTE_LINE_NUMBER (from_line),
+ after);
+ if (after_line)
+ emit_line_note_after (NOTE_SOURCE_FILE (after_line),
+ NOTE_LINE_NUMBER (after_line),
+ to);
+}
+
+/* Emit an insn of given code and pattern
+ at a specified place within the doubly-linked list. */
+
+/* Make an instruction with body PATTERN
+ and output it before the instruction BEFORE. */
+
+rtx
+emit_insn_before (pattern, before)
+ register rtx pattern, before;
+{
+ register rtx insn = before;
+
+ if (GET_CODE (pattern) == SEQUENCE)
+ {
+ register int i;
+
+ for (i = 0; i < XVECLEN (pattern, 0); i++)
+ {
+ insn = XVECEXP (pattern, 0, i);
+ add_insn_after (insn, PREV_INSN (before));
+ }
+ if (XVECLEN (pattern, 0) < SEQUENCE_RESULT_SIZE)
+ sequence_result[XVECLEN (pattern, 0)] = pattern;
+ }
+ else
+ {
+ insn = make_insn_raw (pattern);
+ add_insn_after (insn, PREV_INSN (before));
+ }
+
+ return insn;
+}
+
+/* Make an instruction with body PATTERN and code JUMP_INSN
+ and output it before the instruction BEFORE. */
+
+rtx
+emit_jump_insn_before (pattern, before)
+ register rtx pattern, before;
+{
+ register rtx insn;
+
+ if (GET_CODE (pattern) == SEQUENCE)
+ insn = emit_insn_before (pattern, before);
+ else
+ {
+ insn = make_jump_insn_raw (pattern);
+ add_insn_after (insn, PREV_INSN (before));
+ }
+
+ return insn;
+}
+
+/* Make an instruction with body PATTERN and code CALL_INSN
+ and output it before the instruction BEFORE. */
+
+rtx
+emit_call_insn_before (pattern, before)
+ register rtx pattern, before;
+{
+ register rtx insn;
+
+ if (GET_CODE (pattern) == SEQUENCE)
+ insn = emit_insn_before (pattern, before);
+ else
+ {
+ insn = make_call_insn_raw (pattern);
+ add_insn_after (insn, PREV_INSN (before));
+ PUT_CODE (insn, CALL_INSN);
+ }
+
+ return insn;
+}
+
+/* Make an insn of code BARRIER
+ and output it before the insn AFTER. */
+
+rtx
+emit_barrier_before (before)
+ register rtx before;
+{
+ register rtx insn = rtx_alloc (BARRIER);
+
+ INSN_UID (insn) = cur_insn_uid++;
+
+ add_insn_after (insn, PREV_INSN (before));
+ return insn;
+}
+
+/* Emit a note of subtype SUBTYPE before the insn BEFORE. */
+
+rtx
+emit_note_before (subtype, before)
+ int subtype;
+ rtx before;
+{
+ register rtx note = rtx_alloc (NOTE);
+ INSN_UID (note) = cur_insn_uid++;
+ NOTE_SOURCE_FILE (note) = 0;
+ NOTE_LINE_NUMBER (note) = subtype;
+
+ add_insn_after (note, PREV_INSN (before));
+ return note;
+}
+
+/* Make an insn of code INSN with body PATTERN
+ and output it after the insn AFTER. */
+
+rtx
+emit_insn_after (pattern, after)
+ register rtx pattern, after;
+{
+ register rtx insn = after;
+
+ if (GET_CODE (pattern) == SEQUENCE)
+ {
+ register int i;
+
+ for (i = 0; i < XVECLEN (pattern, 0); i++)
+ {
+ insn = XVECEXP (pattern, 0, i);
+ add_insn_after (insn, after);
+ after = insn;
+ }
+ if (XVECLEN (pattern, 0) < SEQUENCE_RESULT_SIZE)
+ sequence_result[XVECLEN (pattern, 0)] = pattern;
+ }
+ else
+ {
+ insn = make_insn_raw (pattern);
+ add_insn_after (insn, after);
+ }
+
+ return insn;
+}
+
+/* Similar to emit_insn_after, except that line notes are to be inserted so
+ as to act as if this insn were at FROM. */
+
+void
+emit_insn_after_with_line_notes (pattern, after, from)
+ rtx pattern, after, from;
+{
+ rtx from_line = find_line_note (from);
+ rtx after_line = find_line_note (after);
+ rtx insn = emit_insn_after (pattern, after);
+
+ if (from_line)
+ emit_line_note_after (NOTE_SOURCE_FILE (from_line),
+ NOTE_LINE_NUMBER (from_line),
+ after);
+
+ if (after_line)
+ emit_line_note_after (NOTE_SOURCE_FILE (after_line),
+ NOTE_LINE_NUMBER (after_line),
+ insn);
+}
+
+/* Make an insn of code JUMP_INSN with body PATTERN
+ and output it after the insn AFTER. */
+
+rtx
+emit_jump_insn_after (pattern, after)
+ register rtx pattern, after;
+{
+ register rtx insn;
+
+ if (GET_CODE (pattern) == SEQUENCE)
+ insn = emit_insn_after (pattern, after);
+ else
+ {
+ insn = make_jump_insn_raw (pattern);
+ add_insn_after (insn, after);
+ }
+
+ return insn;
+}
+
+/* Make an insn of code BARRIER
+ and output it after the insn AFTER. */
+
+rtx
+emit_barrier_after (after)
+ register rtx after;
+{
+ register rtx insn = rtx_alloc (BARRIER);
+
+ INSN_UID (insn) = cur_insn_uid++;
+
+ add_insn_after (insn, after);
+ return insn;
+}
+
+/* Emit the label LABEL after the insn AFTER. */
+
+rtx
+emit_label_after (label, after)
+ rtx label, after;
+{
+ /* This can be called twice for the same label
+ as a result of the confusion that follows a syntax error!
+ So make it harmless. */
+ if (INSN_UID (label) == 0)
+ {
+ INSN_UID (label) = cur_insn_uid++;
+ add_insn_after (label, after);
+ }
+
+ return label;
+}
+
+/* Emit a note of subtype SUBTYPE after the insn AFTER. */
+
+rtx
+emit_note_after (subtype, after)
+ int subtype;
+ rtx after;
+{
+ register rtx note = rtx_alloc (NOTE);
+ INSN_UID (note) = cur_insn_uid++;
+ NOTE_SOURCE_FILE (note) = 0;
+ NOTE_LINE_NUMBER (note) = subtype;
+ add_insn_after (note, after);
+ return note;
+}
+
+/* Emit a line note for FILE and LINE after the insn AFTER. */
+
+rtx
+emit_line_note_after (file, line, after)
+ char *file;
+ int line;
+ rtx after;
+{
+ register rtx note;
+
+ if (no_line_numbers && line > 0)
+ {
+ cur_insn_uid++;
+ return 0;
+ }
+
+ note = rtx_alloc (NOTE);
+ INSN_UID (note) = cur_insn_uid++;
+ NOTE_SOURCE_FILE (note) = file;
+ NOTE_LINE_NUMBER (note) = line;
+ add_insn_after (note, after);
+ return note;
+}
+
+/* Make an insn of code INSN with pattern PATTERN
+ and add it to the end of the doubly-linked list.
+ If PATTERN is a SEQUENCE, take the elements of it
+ and emit an insn for each element.
+
+ Returns the last insn emitted. */
+
+rtx
+emit_insn (pattern)
+ rtx pattern;
+{
+ rtx insn = last_insn;
+
+ if (GET_CODE (pattern) == SEQUENCE)
+ {
+ register int i;
+
+ for (i = 0; i < XVECLEN (pattern, 0); i++)
+ {
+ insn = XVECEXP (pattern, 0, i);
+ add_insn (insn);
+ }
+ if (XVECLEN (pattern, 0) < SEQUENCE_RESULT_SIZE)
+ sequence_result[XVECLEN (pattern, 0)] = pattern;
+ }
+ else
+ {
+ insn = make_insn_raw (pattern);
+ add_insn (insn);
+ }
+
+ return insn;
+}
+
+/* Emit the insns in a chain starting with INSN.
+ Return the last insn emitted. */
+
+rtx
+emit_insns (insn)
+ rtx insn;
+{
+ rtx last = 0;
+
+ while (insn)
+ {
+ rtx next = NEXT_INSN (insn);
+ add_insn (insn);
+ last = insn;
+ insn = next;
+ }
+
+ return last;
+}
+
+/* Emit the insns in a chain starting with INSN and place them in front of
+ the insn BEFORE. Return the last insn emitted. */
+
+rtx
+emit_insns_before (insn, before)
+ rtx insn;
+ rtx before;
+{
+ rtx last = 0;
+
+ while (insn)
+ {
+ rtx next = NEXT_INSN (insn);
+ add_insn_after (insn, PREV_INSN (before));
+ last = insn;
+ insn = next;
+ }
+
+ return last;
+}
+
+/* Emit the insns in a chain starting with FIRST and place them in back of
+ the insn AFTER. Return the last insn emitted. */
+
+rtx
+emit_insns_after (first, after)
+ register rtx first;
+ register rtx after;
+{
+ register rtx last;
+ register rtx after_after;
+
+ if (!after)
+ abort ();
+
+ if (!first)
+ return first;
+
+ for (last = first; NEXT_INSN (last); last = NEXT_INSN (last))
+ continue;
+
+ after_after = NEXT_INSN (after);
+
+ NEXT_INSN (after) = first;
+ PREV_INSN (first) = after;
+ NEXT_INSN (last) = after_after;
+ if (after_after)
+ PREV_INSN (after_after) = last;
+
+ if (after == last_insn)
+ last_insn = last;
+ return last;
+}
+
+/* Make an insn of code JUMP_INSN with pattern PATTERN
+ and add it to the end of the doubly-linked list. */
+
+rtx
+emit_jump_insn (pattern)
+ rtx pattern;
+{
+ if (GET_CODE (pattern) == SEQUENCE)
+ return emit_insn (pattern);
+ else
+ {
+ register rtx insn = make_jump_insn_raw (pattern);
+ add_insn (insn);
+ return insn;
+ }
+}
+
+/* Make an insn of code CALL_INSN with pattern PATTERN
+ and add it to the end of the doubly-linked list. */
+
+rtx
+emit_call_insn (pattern)
+ rtx pattern;
+{
+ if (GET_CODE (pattern) == SEQUENCE)
+ return emit_insn (pattern);
+ else
+ {
+ register rtx insn = make_call_insn_raw (pattern);
+ add_insn (insn);
+ PUT_CODE (insn, CALL_INSN);
+ return insn;
+ }
+}
+
+/* Add the label LABEL to the end of the doubly-linked list. */
+
+rtx
+emit_label (label)
+ rtx label;
+{
+ /* This can be called twice for the same label
+ as a result of the confusion that follows a syntax error!
+ So make it harmless. */
+ if (INSN_UID (label) == 0)
+ {
+ INSN_UID (label) = cur_insn_uid++;
+ add_insn (label);
+ }
+ return label;
+}
+
+/* Make an insn of code BARRIER
+ and add it to the end of the doubly-linked list. */
+
+rtx
+emit_barrier ()
+{
+ register rtx barrier = rtx_alloc (BARRIER);
+ INSN_UID (barrier) = cur_insn_uid++;
+ add_insn (barrier);
+ return barrier;
+}
+
+/* Make an insn of code NOTE
+ with data-fields specified by FILE and LINE
+ and add it to the end of the doubly-linked list,
+ but only if line-numbers are desired for debugging info. */
+
+rtx
+emit_line_note (file, line)
+ char *file;
+ int line;
+{
+ if (output_bytecode)
+ {
+ /* FIXME: for now we do nothing, but eventually we will have to deal with
+ debugging information. */
+ return 0;
+ }
+
+ emit_filename = file;
+ emit_lineno = line;
+
+#if 0
+ if (no_line_numbers)
+ return 0;
+#endif
+
+ return emit_note (file, line);
+}
+
+/* Make an insn of code NOTE
+ with data-fields specified by FILE and LINE
+ and add it to the end of the doubly-linked list.
+ If it is a line-number NOTE, omit it if it matches the previous one. */
+
+rtx
+emit_note (file, line)
+ char *file;
+ int line;
+{
+ register rtx note;
+
+ if (line > 0)
+ {
+ if (file && last_filename && !strcmp (file, last_filename)
+ && line == last_linenum)
+ return 0;
+ last_filename = file;
+ last_linenum = line;
+ }
+
+ if (no_line_numbers && line > 0)
+ {
+ cur_insn_uid++;
+ return 0;
+ }
+
+ note = rtx_alloc (NOTE);
+ INSN_UID (note) = cur_insn_uid++;
+ NOTE_SOURCE_FILE (note) = file;
+ NOTE_LINE_NUMBER (note) = line;
+ add_insn (note);
+ return note;
+}
+
+/* Emit a NOTE, and don't omit it even if LINE it the previous note. */
+
+rtx
+emit_line_note_force (file, line)
+ char *file;
+ int line;
+{
+ last_linenum = -1;
+ return emit_line_note (file, line);
+}
+
+/* Cause next statement to emit a line note even if the line number
+ has not changed. This is used at the beginning of a function. */
+
+void
+force_next_line_note ()
+{
+ last_linenum = -1;
+}
+
+/* Return an indication of which type of insn should have X as a body.
+ The value is CODE_LABEL, INSN, CALL_INSN or JUMP_INSN. */
+
+enum rtx_code
+classify_insn (x)
+ rtx x;
+{
+ if (GET_CODE (x) == CODE_LABEL)
+ return CODE_LABEL;
+ if (GET_CODE (x) == CALL)
+ return CALL_INSN;
+ if (GET_CODE (x) == RETURN)
+ return JUMP_INSN;
+ if (GET_CODE (x) == SET)
+ {
+ if (SET_DEST (x) == pc_rtx)
+ return JUMP_INSN;
+ else if (GET_CODE (SET_SRC (x)) == CALL)
+ return CALL_INSN;
+ else
+ return INSN;
+ }
+ if (GET_CODE (x) == PARALLEL)
+ {
+ register int j;
+ for (j = XVECLEN (x, 0) - 1; j >= 0; j--)
+ if (GET_CODE (XVECEXP (x, 0, j)) == CALL)
+ return CALL_INSN;
+ else if (GET_CODE (XVECEXP (x, 0, j)) == SET
+ && SET_DEST (XVECEXP (x, 0, j)) == pc_rtx)
+ return JUMP_INSN;
+ else if (GET_CODE (XVECEXP (x, 0, j)) == SET
+ && GET_CODE (SET_SRC (XVECEXP (x, 0, j))) == CALL)
+ return CALL_INSN;
+ }
+ return INSN;
+}
+
+/* Emit the rtl pattern X as an appropriate kind of insn.
+ If X is a label, it is simply added into the insn chain. */
+
+rtx
+emit (x)
+ rtx x;
+{
+ enum rtx_code code = classify_insn (x);
+
+ if (code == CODE_LABEL)
+ return emit_label (x);
+ else if (code == INSN)
+ return emit_insn (x);
+ else if (code == JUMP_INSN)
+ {
+ register rtx insn = emit_jump_insn (x);
+ if (simplejump_p (insn) || GET_CODE (x) == RETURN)
+ return emit_barrier ();
+ return insn;
+ }
+ else if (code == CALL_INSN)
+ return emit_call_insn (x);
+ else
+ abort ();
+}
+
+/* Begin emitting insns to a sequence which can be packaged in an RTL_EXPR. */
+
+void
+start_sequence ()
+{
+ struct sequence_stack *tem;
+
+ if (sequence_element_free_list)
+ {
+ /* Reuse a previously-saved struct sequence_stack. */
+ tem = sequence_element_free_list;
+ sequence_element_free_list = tem->next;
+ }
+ else
+ tem = (struct sequence_stack *) permalloc (sizeof (struct sequence_stack));
+
+ tem->next = sequence_stack;
+ tem->first = first_insn;
+ tem->last = last_insn;
+ tem->sequence_rtl_expr = sequence_rtl_expr;
+
+ sequence_stack = tem;
+
+ first_insn = 0;
+ last_insn = 0;
+}
+
+/* Similarly, but indicate that this sequence will be placed in
+ T, an RTL_EXPR. */
+
+void
+start_sequence_for_rtl_expr (t)
+ tree t;
+{
+ start_sequence ();
+
+ sequence_rtl_expr = t;
+}
+
+/* Set up the insn chain starting with FIRST
+ as the current sequence, saving the previously current one. */
+
+void
+push_to_sequence (first)
+ rtx first;
+{
+ rtx last;
+
+ start_sequence ();
+
+ for (last = first; last && NEXT_INSN (last); last = NEXT_INSN (last));
+
+ first_insn = first;
+ last_insn = last;
+}
+
+/* Set up the outer-level insn chain
+ as the current sequence, saving the previously current one. */
+
+void
+push_topmost_sequence ()
+{
+ struct sequence_stack *stack, *top;
+
+ start_sequence ();
+
+ for (stack = sequence_stack; stack; stack = stack->next)
+ top = stack;
+
+ first_insn = top->first;
+ last_insn = top->last;
+ sequence_rtl_expr = top->sequence_rtl_expr;
+}
+
+/* After emitting to the outer-level insn chain, update the outer-level
+ insn chain, and restore the previous saved state. */
+
+void
+pop_topmost_sequence ()
+{
+ struct sequence_stack *stack, *top;
+
+ for (stack = sequence_stack; stack; stack = stack->next)
+ top = stack;
+
+ top->first = first_insn;
+ top->last = last_insn;
+ /* ??? Why don't we save sequence_rtl_expr here? */
+
+ end_sequence ();
+}
+
+/* After emitting to a sequence, restore previous saved state.
+
+ To get the contents of the sequence just made,
+ you must call `gen_sequence' *before* calling here. */
+
+void
+end_sequence ()
+{
+ struct sequence_stack *tem = sequence_stack;
+
+ first_insn = tem->first;
+ last_insn = tem->last;
+ sequence_rtl_expr = tem->sequence_rtl_expr;
+ sequence_stack = tem->next;
+
+ tem->next = sequence_element_free_list;
+ sequence_element_free_list = tem;
+}
+
+/* Return 1 if currently emitting into a sequence. */
+
+int
+in_sequence_p ()
+{
+ return sequence_stack != 0;
+}
+
+/* Generate a SEQUENCE rtx containing the insns already emitted
+ to the current sequence.
+
+ This is how the gen_... function from a DEFINE_EXPAND
+ constructs the SEQUENCE that it returns. */
+
+rtx
+gen_sequence ()
+{
+ rtx result;
+ rtx tem;
+ int i;
+ int len;
+
+ /* Count the insns in the chain. */
+ len = 0;
+ for (tem = first_insn; tem; tem = NEXT_INSN (tem))
+ len++;
+
+ /* If only one insn, return its pattern rather than a SEQUENCE.
+ (Now that we cache SEQUENCE expressions, it isn't worth special-casing
+ the case of an empty list.) */
+ if (len == 1
+ && (GET_CODE (first_insn) == INSN
+ || GET_CODE (first_insn) == JUMP_INSN
+ || GET_CODE (first_insn) == CALL_INSN))
+ return PATTERN (first_insn);
+
+ /* Put them in a vector. See if we already have a SEQUENCE of the
+ appropriate length around. */
+ if (len < SEQUENCE_RESULT_SIZE && (result = sequence_result[len]) != 0)
+ sequence_result[len] = 0;
+ else
+ {
+ /* Ensure that this rtl goes in saveable_obstack, since we may be
+ caching it. */
+ push_obstacks_nochange ();
+ rtl_in_saveable_obstack ();
+ result = gen_rtx (SEQUENCE, VOIDmode, rtvec_alloc (len));
+ pop_obstacks ();
+ }
+
+ for (i = 0, tem = first_insn; tem; tem = NEXT_INSN (tem), i++)
+ XVECEXP (result, 0, i) = tem;
+
+ return result;
+}
+
+/* Set up regno_reg_rtx, reg_rtx_no and regno_pointer_flag
+ according to the chain of insns starting with FIRST.
+
+ Also set cur_insn_uid to exceed the largest uid in that chain.
+
+ This is used when an inline function's rtl is saved
+ and passed to rest_of_compilation later. */
+
+static void restore_reg_data_1 ();
+
+void
+restore_reg_data (first)
+ rtx first;
+{
+ register rtx insn;
+ int i;
+ register int max_uid = 0;
+
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ if (INSN_UID (insn) >= max_uid)
+ max_uid = INSN_UID (insn);
+
+ switch (GET_CODE (insn))
+ {
+ case NOTE:
+ case CODE_LABEL:
+ case BARRIER:
+ break;
+
+ case JUMP_INSN:
+ case CALL_INSN:
+ case INSN:
+ restore_reg_data_1 (PATTERN (insn));
+ break;
+ }
+ }
+
+ /* Don't duplicate the uids already in use. */
+ cur_insn_uid = max_uid + 1;
+
+ /* If any regs are missing, make them up.
+
+ ??? word_mode is not necessarily the right mode. Most likely these REGs
+ are never used. At some point this should be checked. */
+
+ for (i = FIRST_PSEUDO_REGISTER; i < reg_rtx_no; i++)
+ if (regno_reg_rtx[i] == 0)
+ regno_reg_rtx[i] = gen_rtx (REG, word_mode, i);
+}
+
+static void
+restore_reg_data_1 (orig)
+ rtx orig;
+{
+ register rtx x = orig;
+ register int i;
+ register enum rtx_code code;
+ register char *format_ptr;
+
+ code = GET_CODE (x);
+
+ switch (code)
+ {
+ case QUEUED:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case CODE_LABEL:
+ case PC:
+ case CC0:
+ case LABEL_REF:
+ return;
+
+ case REG:
+ if (REGNO (x) >= FIRST_PSEUDO_REGISTER)
+ {
+ /* Make sure regno_pointer_flag and regno_reg_rtx are large
+ enough to have an element for this pseudo reg number. */
+ if (REGNO (x) >= reg_rtx_no)
+ {
+ reg_rtx_no = REGNO (x);
+
+ if (reg_rtx_no >= regno_pointer_flag_length)
+ {
+ int newlen = MAX (regno_pointer_flag_length * 2,
+ reg_rtx_no + 30);
+ rtx *new1;
+ char *new = (char *) oballoc (newlen);
+ bzero (new, newlen);
+ bcopy (regno_pointer_flag, new, regno_pointer_flag_length);
+
+ new1 = (rtx *) oballoc (newlen * sizeof (rtx));
+ bzero ((char *) new1, newlen * sizeof (rtx));
+ bcopy ((char *) regno_reg_rtx, (char *) new1,
+ regno_pointer_flag_length * sizeof (rtx));
+
+ regno_pointer_flag = new;
+ regno_reg_rtx = new1;
+ regno_pointer_flag_length = newlen;
+ }
+ reg_rtx_no ++;
+ }
+ regno_reg_rtx[REGNO (x)] = x;
+ }
+ return;
+
+ case MEM:
+ if (GET_CODE (XEXP (x, 0)) == REG)
+ mark_reg_pointer (XEXP (x, 0));
+ restore_reg_data_1 (XEXP (x, 0));
+ return;
+ }
+
+ /* Now scan the subexpressions recursively. */
+
+ format_ptr = GET_RTX_FORMAT (code);
+
+ for (i = 0; i < GET_RTX_LENGTH (code); i++)
+ {
+ switch (*format_ptr++)
+ {
+ case 'e':
+ restore_reg_data_1 (XEXP (x, i));
+ break;
+
+ case 'E':
+ if (XVEC (x, i) != NULL)
+ {
+ register int j;
+
+ for (j = 0; j < XVECLEN (x, i); j++)
+ restore_reg_data_1 (XVECEXP (x, i, j));
+ }
+ break;
+ }
+ }
+}
+
+/* Initialize data structures and variables in this file
+ before generating rtl for each function. */
+
+void
+init_emit ()
+{
+ int i;
+
+ first_insn = NULL;
+ last_insn = NULL;
+ sequence_rtl_expr = NULL;
+ cur_insn_uid = 1;
+ reg_rtx_no = LAST_VIRTUAL_REGISTER + 1;
+ last_linenum = 0;
+ last_filename = 0;
+ first_label_num = label_num;
+ last_label_num = 0;
+ sequence_stack = NULL;
+
+ /* Clear the start_sequence/gen_sequence cache. */
+ sequence_element_free_list = 0;
+ for (i = 0; i < SEQUENCE_RESULT_SIZE; i++)
+ sequence_result[i] = 0;
+
+ /* Init the tables that describe all the pseudo regs. */
+
+ regno_pointer_flag_length = LAST_VIRTUAL_REGISTER + 101;
+
+ regno_pointer_flag
+ = (char *) oballoc (regno_pointer_flag_length);
+ bzero (regno_pointer_flag, regno_pointer_flag_length);
+
+ regno_reg_rtx
+ = (rtx *) oballoc (regno_pointer_flag_length * sizeof (rtx));
+ bzero ((char *) regno_reg_rtx, regno_pointer_flag_length * sizeof (rtx));
+
+ /* Put copies of all the virtual register rtx into regno_reg_rtx. */
+ regno_reg_rtx[VIRTUAL_INCOMING_ARGS_REGNUM] = virtual_incoming_args_rtx;
+ regno_reg_rtx[VIRTUAL_STACK_VARS_REGNUM] = virtual_stack_vars_rtx;
+ regno_reg_rtx[VIRTUAL_STACK_DYNAMIC_REGNUM] = virtual_stack_dynamic_rtx;
+ regno_reg_rtx[VIRTUAL_OUTGOING_ARGS_REGNUM] = virtual_outgoing_args_rtx;
+
+ /* Indicate that the virtual registers and stack locations are
+ all pointers. */
+ REGNO_POINTER_FLAG (STACK_POINTER_REGNUM) = 1;
+ REGNO_POINTER_FLAG (FRAME_POINTER_REGNUM) = 1;
+ REGNO_POINTER_FLAG (ARG_POINTER_REGNUM) = 1;
+
+ REGNO_POINTER_FLAG (VIRTUAL_INCOMING_ARGS_REGNUM) = 1;
+ REGNO_POINTER_FLAG (VIRTUAL_STACK_VARS_REGNUM) = 1;
+ REGNO_POINTER_FLAG (VIRTUAL_STACK_DYNAMIC_REGNUM) = 1;
+ REGNO_POINTER_FLAG (VIRTUAL_OUTGOING_ARGS_REGNUM) = 1;
+
+#ifdef INIT_EXPANDERS
+ INIT_EXPANDERS;
+#endif
+}
+
+/* Create some permanent unique rtl objects shared between all functions.
+ LINE_NUMBERS is nonzero if line numbers are to be generated. */
+
+void
+init_emit_once (line_numbers)
+ int line_numbers;
+{
+ int i;
+ enum machine_mode mode;
+
+ no_line_numbers = ! line_numbers;
+
+ sequence_stack = NULL;
+
+ /* Compute the word and byte modes. */
+
+ byte_mode = VOIDmode;
+ word_mode = VOIDmode;
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ {
+ if (GET_MODE_BITSIZE (mode) == BITS_PER_UNIT
+ && byte_mode == VOIDmode)
+ byte_mode = mode;
+
+ if (GET_MODE_BITSIZE (mode) == BITS_PER_WORD
+ && word_mode == VOIDmode)
+ word_mode = mode;
+ }
+
+ /* Create the unique rtx's for certain rtx codes and operand values. */
+
+ pc_rtx = gen_rtx (PC, VOIDmode);
+ cc0_rtx = gen_rtx (CC0, VOIDmode);
+
+ /* Don't use gen_rtx here since gen_rtx in this case
+ tries to use these variables. */
+ for (i = - MAX_SAVED_CONST_INT; i <= MAX_SAVED_CONST_INT; i++)
+ {
+ const_int_rtx[i + MAX_SAVED_CONST_INT] = rtx_alloc (CONST_INT);
+ PUT_MODE (const_int_rtx[i + MAX_SAVED_CONST_INT], VOIDmode);
+ INTVAL (const_int_rtx[i + MAX_SAVED_CONST_INT]) = i;
+ }
+
+ /* These four calls obtain some of the rtx expressions made above. */
+ const0_rtx = GEN_INT (0);
+ const1_rtx = GEN_INT (1);
+ const2_rtx = GEN_INT (2);
+ constm1_rtx = GEN_INT (-1);
+
+ /* This will usually be one of the above constants, but may be a new rtx. */
+ const_true_rtx = GEN_INT (STORE_FLAG_VALUE);
+
+ dconst0 = REAL_VALUE_ATOF ("0", DFmode);
+ dconst1 = REAL_VALUE_ATOF ("1", DFmode);
+ dconst2 = REAL_VALUE_ATOF ("2", DFmode);
+ dconstm1 = REAL_VALUE_ATOF ("-1", DFmode);
+
+ for (i = 0; i <= 2; i++)
+ {
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ {
+ rtx tem = rtx_alloc (CONST_DOUBLE);
+ union real_extract u;
+
+ bzero ((char *) &u, sizeof u); /* Zero any holes in a structure. */
+ u.d = i == 0 ? dconst0 : i == 1 ? dconst1 : dconst2;
+
+ bcopy ((char *) &u, (char *) &CONST_DOUBLE_LOW (tem), sizeof u);
+ CONST_DOUBLE_MEM (tem) = cc0_rtx;
+ PUT_MODE (tem, mode);
+
+ const_tiny_rtx[i][(int) mode] = tem;
+ }
+
+ const_tiny_rtx[i][(int) VOIDmode] = GEN_INT (i);
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ const_tiny_rtx[i][(int) mode] = GEN_INT (i);
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_PARTIAL_INT);
+ mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ const_tiny_rtx[i][(int) mode] = GEN_INT (i);
+ }
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_CC); mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ const_tiny_rtx[0][(int) mode] = const0_rtx;
+
+ stack_pointer_rtx = gen_rtx (REG, Pmode, STACK_POINTER_REGNUM);
+ frame_pointer_rtx = gen_rtx (REG, Pmode, FRAME_POINTER_REGNUM);
+
+ if (HARD_FRAME_POINTER_REGNUM == FRAME_POINTER_REGNUM)
+ hard_frame_pointer_rtx = frame_pointer_rtx;
+ else
+ hard_frame_pointer_rtx = gen_rtx (REG, Pmode, HARD_FRAME_POINTER_REGNUM);
+
+ if (FRAME_POINTER_REGNUM == ARG_POINTER_REGNUM)
+ arg_pointer_rtx = frame_pointer_rtx;
+ else if (HARD_FRAME_POINTER_REGNUM == ARG_POINTER_REGNUM)
+ arg_pointer_rtx = hard_frame_pointer_rtx;
+ else if (STACK_POINTER_REGNUM == ARG_POINTER_REGNUM)
+ arg_pointer_rtx = stack_pointer_rtx;
+ else
+ arg_pointer_rtx = gen_rtx (REG, Pmode, ARG_POINTER_REGNUM);
+
+ /* Create the virtual registers. Do so here since the following objects
+ might reference them. */
+
+ virtual_incoming_args_rtx = gen_rtx (REG, Pmode,
+ VIRTUAL_INCOMING_ARGS_REGNUM);
+ virtual_stack_vars_rtx = gen_rtx (REG, Pmode,
+ VIRTUAL_STACK_VARS_REGNUM);
+ virtual_stack_dynamic_rtx = gen_rtx (REG, Pmode,
+ VIRTUAL_STACK_DYNAMIC_REGNUM);
+ virtual_outgoing_args_rtx = gen_rtx (REG, Pmode,
+ VIRTUAL_OUTGOING_ARGS_REGNUM);
+
+#ifdef STRUCT_VALUE
+ struct_value_rtx = STRUCT_VALUE;
+#else
+ struct_value_rtx = gen_rtx (REG, Pmode, STRUCT_VALUE_REGNUM);
+#endif
+
+#ifdef STRUCT_VALUE_INCOMING
+ struct_value_incoming_rtx = STRUCT_VALUE_INCOMING;
+#else
+#ifdef STRUCT_VALUE_INCOMING_REGNUM
+ struct_value_incoming_rtx
+ = gen_rtx (REG, Pmode, STRUCT_VALUE_INCOMING_REGNUM);
+#else
+ struct_value_incoming_rtx = struct_value_rtx;
+#endif
+#endif
+
+#ifdef STATIC_CHAIN_REGNUM
+ static_chain_rtx = gen_rtx (REG, Pmode, STATIC_CHAIN_REGNUM);
+
+#ifdef STATIC_CHAIN_INCOMING_REGNUM
+ if (STATIC_CHAIN_INCOMING_REGNUM != STATIC_CHAIN_REGNUM)
+ static_chain_incoming_rtx = gen_rtx (REG, Pmode, STATIC_CHAIN_INCOMING_REGNUM);
+ else
+#endif
+ static_chain_incoming_rtx = static_chain_rtx;
+#endif
+
+#ifdef STATIC_CHAIN
+ static_chain_rtx = STATIC_CHAIN;
+
+#ifdef STATIC_CHAIN_INCOMING
+ static_chain_incoming_rtx = STATIC_CHAIN_INCOMING;
+#else
+ static_chain_incoming_rtx = static_chain_rtx;
+#endif
+#endif
+
+#ifdef PIC_OFFSET_TABLE_REGNUM
+ pic_offset_table_rtx = gen_rtx (REG, Pmode, PIC_OFFSET_TABLE_REGNUM);
+#endif
+}
diff --git a/gnu/usr.bin/cc/cc_int/explow.c b/gnu/usr.bin/cc/cc_int/explow.c
new file mode 100644
index 0000000..b72e468
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/explow.c
@@ -0,0 +1,1152 @@
+/* Subroutines for manipulating rtx's in semantically interesting ways.
+ Copyright (C) 1987, 1991, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#include "rtl.h"
+#include "tree.h"
+#include "flags.h"
+#include "expr.h"
+#include "hard-reg-set.h"
+#include "insn-config.h"
+#include "recog.h"
+#include "insn-flags.h"
+#include "insn-codes.h"
+
+/* Return an rtx for the sum of X and the integer C.
+
+ This function should be used via the `plus_constant' macro. */
+
+rtx
+plus_constant_wide (x, c)
+ register rtx x;
+ register HOST_WIDE_INT c;
+{
+ register RTX_CODE code;
+ register enum machine_mode mode;
+ register rtx tem;
+ int all_constant = 0;
+
+ if (c == 0)
+ return x;
+
+ restart:
+
+ code = GET_CODE (x);
+ mode = GET_MODE (x);
+ switch (code)
+ {
+ case CONST_INT:
+ return GEN_INT (INTVAL (x) + c);
+
+ case CONST_DOUBLE:
+ {
+ HOST_WIDE_INT l1 = CONST_DOUBLE_LOW (x);
+ HOST_WIDE_INT h1 = CONST_DOUBLE_HIGH (x);
+ HOST_WIDE_INT l2 = c;
+ HOST_WIDE_INT h2 = c < 0 ? ~0 : 0;
+ HOST_WIDE_INT lv, hv;
+
+ add_double (l1, h1, l2, h2, &lv, &hv);
+
+ return immed_double_const (lv, hv, VOIDmode);
+ }
+
+ case MEM:
+ /* If this is a reference to the constant pool, try replacing it with
+ a reference to a new constant. If the resulting address isn't
+ valid, don't return it because we have no way to validize it. */
+ if (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
+ && CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)))
+ {
+ tem
+ = force_const_mem (GET_MODE (x),
+ plus_constant (get_pool_constant (XEXP (x, 0)),
+ c));
+ if (memory_address_p (GET_MODE (tem), XEXP (tem, 0)))
+ return tem;
+ }
+ break;
+
+ case CONST:
+ /* If adding to something entirely constant, set a flag
+ so that we can add a CONST around the result. */
+ x = XEXP (x, 0);
+ all_constant = 1;
+ goto restart;
+
+ case SYMBOL_REF:
+ case LABEL_REF:
+ all_constant = 1;
+ break;
+
+ case PLUS:
+ /* The interesting case is adding the integer to a sum.
+ Look for constant term in the sum and combine
+ with C. For an integer constant term, we make a combined
+ integer. For a constant term that is not an explicit integer,
+ we cannot really combine, but group them together anyway.
+
+ Use a recursive call in case the remaining operand is something
+ that we handle specially, such as a SYMBOL_REF. */
+
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+ return plus_constant (XEXP (x, 0), c + INTVAL (XEXP (x, 1)));
+ else if (CONSTANT_P (XEXP (x, 0)))
+ return gen_rtx (PLUS, mode,
+ plus_constant (XEXP (x, 0), c),
+ XEXP (x, 1));
+ else if (CONSTANT_P (XEXP (x, 1)))
+ return gen_rtx (PLUS, mode,
+ XEXP (x, 0),
+ plus_constant (XEXP (x, 1), c));
+ }
+
+ if (c != 0)
+ x = gen_rtx (PLUS, mode, x, GEN_INT (c));
+
+ if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
+ return x;
+ else if (all_constant)
+ return gen_rtx (CONST, mode, x);
+ else
+ return x;
+}
+
+/* This is the same as `plus_constant', except that it handles LO_SUM.
+
+ This function should be used via the `plus_constant_for_output' macro. */
+
+rtx
+plus_constant_for_output_wide (x, c)
+ register rtx x;
+ register HOST_WIDE_INT c;
+{
+ register RTX_CODE code = GET_CODE (x);
+ register enum machine_mode mode = GET_MODE (x);
+ int all_constant = 0;
+
+ if (GET_CODE (x) == LO_SUM)
+ return gen_rtx (LO_SUM, mode, XEXP (x, 0),
+ plus_constant_for_output (XEXP (x, 1), c));
+
+ else
+ return plus_constant (x, c);
+}
+
+/* If X is a sum, return a new sum like X but lacking any constant terms.
+ Add all the removed constant terms into *CONSTPTR.
+ X itself is not altered. The result != X if and only if
+ it is not isomorphic to X. */
+
+rtx
+eliminate_constant_term (x, constptr)
+ rtx x;
+ rtx *constptr;
+{
+ register rtx x0, x1;
+ rtx tem;
+
+ if (GET_CODE (x) != PLUS)
+ return x;
+
+ /* First handle constants appearing at this level explicitly. */
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && 0 != (tem = simplify_binary_operation (PLUS, GET_MODE (x), *constptr,
+ XEXP (x, 1)))
+ && GET_CODE (tem) == CONST_INT)
+ {
+ *constptr = tem;
+ return eliminate_constant_term (XEXP (x, 0), constptr);
+ }
+
+ tem = const0_rtx;
+ x0 = eliminate_constant_term (XEXP (x, 0), &tem);
+ x1 = eliminate_constant_term (XEXP (x, 1), &tem);
+ if ((x1 != XEXP (x, 1) || x0 != XEXP (x, 0))
+ && 0 != (tem = simplify_binary_operation (PLUS, GET_MODE (x),
+ *constptr, tem))
+ && GET_CODE (tem) == CONST_INT)
+ {
+ *constptr = tem;
+ return gen_rtx (PLUS, GET_MODE (x), x0, x1);
+ }
+
+ return x;
+}
+
+/* Returns the insn that next references REG after INSN, or 0
+ if REG is clobbered before next referenced or we cannot find
+ an insn that references REG in a straight-line piece of code. */
+
+rtx
+find_next_ref (reg, insn)
+ rtx reg;
+ rtx insn;
+{
+ rtx next;
+
+ for (insn = NEXT_INSN (insn); insn; insn = next)
+ {
+ next = NEXT_INSN (insn);
+ if (GET_CODE (insn) == NOTE)
+ continue;
+ if (GET_CODE (insn) == CODE_LABEL
+ || GET_CODE (insn) == BARRIER)
+ return 0;
+ if (GET_CODE (insn) == INSN
+ || GET_CODE (insn) == JUMP_INSN
+ || GET_CODE (insn) == CALL_INSN)
+ {
+ if (reg_set_p (reg, insn))
+ return 0;
+ if (reg_mentioned_p (reg, PATTERN (insn)))
+ return insn;
+ if (GET_CODE (insn) == JUMP_INSN)
+ {
+ if (simplejump_p (insn))
+ next = JUMP_LABEL (insn);
+ else
+ return 0;
+ }
+ if (GET_CODE (insn) == CALL_INSN
+ && REGNO (reg) < FIRST_PSEUDO_REGISTER
+ && call_used_regs[REGNO (reg)])
+ return 0;
+ }
+ else
+ abort ();
+ }
+ return 0;
+}
+
+/* Return an rtx for the size in bytes of the value of EXP. */
+
+rtx
+expr_size (exp)
+ tree exp;
+{
+ tree size = size_in_bytes (TREE_TYPE (exp));
+
+ if (TREE_CODE (size) != INTEGER_CST
+ && contains_placeholder_p (size))
+ size = build (WITH_RECORD_EXPR, sizetype, size, exp);
+
+ return expand_expr (size, NULL_RTX, TYPE_MODE (sizetype), 0);
+}
+
+/* Return a copy of X in which all memory references
+ and all constants that involve symbol refs
+ have been replaced with new temporary registers.
+ Also emit code to load the memory locations and constants
+ into those registers.
+
+ If X contains no such constants or memory references,
+ X itself (not a copy) is returned.
+
+ If a constant is found in the address that is not a legitimate constant
+ in an insn, it is left alone in the hope that it might be valid in the
+ address.
+
+ X may contain no arithmetic except addition, subtraction and multiplication.
+ Values returned by expand_expr with 1 for sum_ok fit this constraint. */
+
+static rtx
+break_out_memory_refs (x)
+ register rtx x;
+{
+ if (GET_CODE (x) == MEM
+ || (CONSTANT_P (x) && CONSTANT_ADDRESS_P (x)
+ && GET_MODE (x) != VOIDmode))
+ x = force_reg (GET_MODE (x), x);
+ else if (GET_CODE (x) == PLUS || GET_CODE (x) == MINUS
+ || GET_CODE (x) == MULT)
+ {
+ register rtx op0 = break_out_memory_refs (XEXP (x, 0));
+ register rtx op1 = break_out_memory_refs (XEXP (x, 1));
+
+ if (op0 != XEXP (x, 0) || op1 != XEXP (x, 1))
+ x = gen_rtx (GET_CODE (x), Pmode, op0, op1);
+ }
+
+ return x;
+}
+
+/* Given a memory address or facsimile X, construct a new address,
+ currently equivalent, that is stable: future stores won't change it.
+
+ X must be composed of constants, register and memory references
+ combined with addition, subtraction and multiplication:
+ in other words, just what you can get from expand_expr if sum_ok is 1.
+
+ Works by making copies of all regs and memory locations used
+ by X and combining them the same way X does.
+ You could also stabilize the reference to this address
+ by copying the address to a register with copy_to_reg;
+ but then you wouldn't get indexed addressing in the reference. */
+
+rtx
+copy_all_regs (x)
+ register rtx x;
+{
+ if (GET_CODE (x) == REG)
+ {
+ if (REGNO (x) != FRAME_POINTER_REGNUM
+#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ && REGNO (x) != HARD_FRAME_POINTER_REGNUM
+#endif
+ )
+ x = copy_to_reg (x);
+ }
+ else if (GET_CODE (x) == MEM)
+ x = copy_to_reg (x);
+ else if (GET_CODE (x) == PLUS || GET_CODE (x) == MINUS
+ || GET_CODE (x) == MULT)
+ {
+ register rtx op0 = copy_all_regs (XEXP (x, 0));
+ register rtx op1 = copy_all_regs (XEXP (x, 1));
+ if (op0 != XEXP (x, 0) || op1 != XEXP (x, 1))
+ x = gen_rtx (GET_CODE (x), Pmode, op0, op1);
+ }
+ return x;
+}
+
+/* Return something equivalent to X but valid as a memory address
+ for something of mode MODE. When X is not itself valid, this
+ works by copying X or subexpressions of it into registers. */
+
+rtx
+memory_address (mode, x)
+ enum machine_mode mode;
+ register rtx x;
+{
+ register rtx oldx = x;
+
+ /* By passing constant addresses thru registers
+ we get a chance to cse them. */
+ if (! cse_not_expected && CONSTANT_P (x) && CONSTANT_ADDRESS_P (x))
+ x = force_reg (Pmode, x);
+
+ /* Accept a QUEUED that refers to a REG
+ even though that isn't a valid address.
+ On attempting to put this in an insn we will call protect_from_queue
+ which will turn it into a REG, which is valid. */
+ else if (GET_CODE (x) == QUEUED
+ && GET_CODE (QUEUED_VAR (x)) == REG)
+ ;
+
+ /* We get better cse by rejecting indirect addressing at this stage.
+ Let the combiner create indirect addresses where appropriate.
+ For now, generate the code so that the subexpressions useful to share
+ are visible. But not if cse won't be done! */
+ else
+ {
+ if (! cse_not_expected && GET_CODE (x) != REG)
+ x = break_out_memory_refs (x);
+
+ /* At this point, any valid address is accepted. */
+ GO_IF_LEGITIMATE_ADDRESS (mode, x, win);
+
+ /* If it was valid before but breaking out memory refs invalidated it,
+ use it the old way. */
+ if (memory_address_p (mode, oldx))
+ goto win2;
+
+ /* Perform machine-dependent transformations on X
+ in certain cases. This is not necessary since the code
+ below can handle all possible cases, but machine-dependent
+ transformations can make better code. */
+ LEGITIMIZE_ADDRESS (x, oldx, mode, win);
+
+ /* PLUS and MULT can appear in special ways
+ as the result of attempts to make an address usable for indexing.
+ Usually they are dealt with by calling force_operand, below.
+ But a sum containing constant terms is special
+ if removing them makes the sum a valid address:
+ then we generate that address in a register
+ and index off of it. We do this because it often makes
+ shorter code, and because the addresses thus generated
+ in registers often become common subexpressions. */
+ if (GET_CODE (x) == PLUS)
+ {
+ rtx constant_term = const0_rtx;
+ rtx y = eliminate_constant_term (x, &constant_term);
+ if (constant_term == const0_rtx
+ || ! memory_address_p (mode, y))
+ x = force_operand (x, NULL_RTX);
+ else
+ {
+ y = gen_rtx (PLUS, GET_MODE (x), copy_to_reg (y), constant_term);
+ if (! memory_address_p (mode, y))
+ x = force_operand (x, NULL_RTX);
+ else
+ x = y;
+ }
+ }
+
+ if (GET_CODE (x) == MULT || GET_CODE (x) == MINUS)
+ x = force_operand (x, NULL_RTX);
+
+ /* If we have a register that's an invalid address,
+ it must be a hard reg of the wrong class. Copy it to a pseudo. */
+ else if (GET_CODE (x) == REG)
+ x = copy_to_reg (x);
+
+ /* Last resort: copy the value to a register, since
+ the register is a valid address. */
+ else
+ x = force_reg (Pmode, x);
+
+ goto done;
+
+ win2:
+ x = oldx;
+ win:
+ if (flag_force_addr && ! cse_not_expected && GET_CODE (x) != REG
+ /* Don't copy an addr via a reg if it is one of our stack slots. */
+ && ! (GET_CODE (x) == PLUS
+ && (XEXP (x, 0) == virtual_stack_vars_rtx
+ || XEXP (x, 0) == virtual_incoming_args_rtx)))
+ {
+ if (general_operand (x, Pmode))
+ x = force_reg (Pmode, x);
+ else
+ x = force_operand (x, NULL_RTX);
+ }
+ }
+
+ done:
+
+ /* If we didn't change the address, we are done. Otherwise, mark
+ a reg as a pointer if we have REG or REG + CONST_INT. */
+ if (oldx == x)
+ return x;
+ else if (GET_CODE (x) == REG)
+ mark_reg_pointer (x);
+ else if (GET_CODE (x) == PLUS
+ && GET_CODE (XEXP (x, 0)) == REG
+ && GET_CODE (XEXP (x, 1)) == CONST_INT)
+ mark_reg_pointer (XEXP (x, 0));
+
+ /* OLDX may have been the address on a temporary. Update the address
+ to indicate that X is now used. */
+ update_temp_slot_address (oldx, x);
+
+ return x;
+}
+
+/* Like `memory_address' but pretend `flag_force_addr' is 0. */
+
+rtx
+memory_address_noforce (mode, x)
+ enum machine_mode mode;
+ rtx x;
+{
+ int ambient_force_addr = flag_force_addr;
+ rtx val;
+
+ flag_force_addr = 0;
+ val = memory_address (mode, x);
+ flag_force_addr = ambient_force_addr;
+ return val;
+}
+
+/* Convert a mem ref into one with a valid memory address.
+ Pass through anything else unchanged. */
+
+rtx
+validize_mem (ref)
+ rtx ref;
+{
+ if (GET_CODE (ref) != MEM)
+ return ref;
+ if (memory_address_p (GET_MODE (ref), XEXP (ref, 0)))
+ return ref;
+ /* Don't alter REF itself, since that is probably a stack slot. */
+ return change_address (ref, GET_MODE (ref), XEXP (ref, 0));
+}
+
+/* Return a modified copy of X with its memory address copied
+ into a temporary register to protect it from side effects.
+ If X is not a MEM, it is returned unchanged (and not copied).
+ Perhaps even if it is a MEM, if there is no need to change it. */
+
+rtx
+stabilize (x)
+ rtx x;
+{
+ register rtx addr;
+ if (GET_CODE (x) != MEM)
+ return x;
+ addr = XEXP (x, 0);
+ if (rtx_unstable_p (addr))
+ {
+ rtx temp = copy_all_regs (addr);
+ rtx mem;
+ if (GET_CODE (temp) != REG)
+ temp = copy_to_reg (temp);
+ mem = gen_rtx (MEM, GET_MODE (x), temp);
+
+ /* Mark returned memref with in_struct if it's in an array or
+ structure. Copy const and volatile from original memref. */
+
+ MEM_IN_STRUCT_P (mem) = MEM_IN_STRUCT_P (x) || GET_CODE (addr) == PLUS;
+ RTX_UNCHANGING_P (mem) = RTX_UNCHANGING_P (x);
+ MEM_VOLATILE_P (mem) = MEM_VOLATILE_P (x);
+ return mem;
+ }
+ return x;
+}
+
+/* Copy the value or contents of X to a new temp reg and return that reg. */
+
+rtx
+copy_to_reg (x)
+ rtx x;
+{
+ register rtx temp = gen_reg_rtx (GET_MODE (x));
+
+ /* If not an operand, must be an address with PLUS and MULT so
+ do the computation. */
+ if (! general_operand (x, VOIDmode))
+ x = force_operand (x, temp);
+
+ if (x != temp)
+ emit_move_insn (temp, x);
+
+ return temp;
+}
+
+/* Like copy_to_reg but always give the new register mode Pmode
+ in case X is a constant. */
+
+rtx
+copy_addr_to_reg (x)
+ rtx x;
+{
+ return copy_to_mode_reg (Pmode, x);
+}
+
+/* Like copy_to_reg but always give the new register mode MODE
+ in case X is a constant. */
+
+rtx
+copy_to_mode_reg (mode, x)
+ enum machine_mode mode;
+ rtx x;
+{
+ register rtx temp = gen_reg_rtx (mode);
+
+ /* If not an operand, must be an address with PLUS and MULT so
+ do the computation. */
+ if (! general_operand (x, VOIDmode))
+ x = force_operand (x, temp);
+
+ if (GET_MODE (x) != mode && GET_MODE (x) != VOIDmode)
+ abort ();
+ if (x != temp)
+ emit_move_insn (temp, x);
+ return temp;
+}
+
+/* Load X into a register if it is not already one.
+ Use mode MODE for the register.
+ X should be valid for mode MODE, but it may be a constant which
+ is valid for all integer modes; that's why caller must specify MODE.
+
+ The caller must not alter the value in the register we return,
+ since we mark it as a "constant" register. */
+
+rtx
+force_reg (mode, x)
+ enum machine_mode mode;
+ rtx x;
+{
+ register rtx temp, insn, set;
+
+ if (GET_CODE (x) == REG)
+ return x;
+ temp = gen_reg_rtx (mode);
+ insn = emit_move_insn (temp, x);
+
+ /* Let optimizers know that TEMP's value never changes
+ and that X can be substituted for it. Don't get confused
+ if INSN set something else (such as a SUBREG of TEMP). */
+ if (CONSTANT_P (x)
+ && (set = single_set (insn)) != 0
+ && SET_DEST (set) == temp)
+ {
+ rtx note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
+
+ if (note)
+ XEXP (note, 0) = x;
+ else
+ REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUAL, x, REG_NOTES (insn));
+ }
+ return temp;
+}
+
+/* If X is a memory ref, copy its contents to a new temp reg and return
+ that reg. Otherwise, return X. */
+
+rtx
+force_not_mem (x)
+ rtx x;
+{
+ register rtx temp;
+ if (GET_CODE (x) != MEM || GET_MODE (x) == BLKmode)
+ return x;
+ temp = gen_reg_rtx (GET_MODE (x));
+ emit_move_insn (temp, x);
+ return temp;
+}
+
+/* Copy X to TARGET (if it's nonzero and a reg)
+ or to a new temp reg and return that reg.
+ MODE is the mode to use for X in case it is a constant. */
+
+rtx
+copy_to_suggested_reg (x, target, mode)
+ rtx x, target;
+ enum machine_mode mode;
+{
+ register rtx temp;
+
+ if (target && GET_CODE (target) == REG)
+ temp = target;
+ else
+ temp = gen_reg_rtx (mode);
+
+ emit_move_insn (temp, x);
+ return temp;
+}
+
+/* Return the mode to use to store a scalar of TYPE and MODE.
+ PUNSIGNEDP points to the signedness of the type and may be adjusted
+ to show what signedness to use on extension operations.
+
+ FOR_CALL is non-zero if this call is promoting args for a call. */
+
+enum machine_mode
+promote_mode (type, mode, punsignedp, for_call)
+ tree type;
+ enum machine_mode mode;
+ int *punsignedp;
+ int for_call;
+{
+ enum tree_code code = TREE_CODE (type);
+ int unsignedp = *punsignedp;
+
+#ifdef PROMOTE_FOR_CALL_ONLY
+ if (! for_call)
+ return mode;
+#endif
+
+ switch (code)
+ {
+#ifdef PROMOTE_MODE
+ case INTEGER_TYPE: case ENUMERAL_TYPE: case BOOLEAN_TYPE:
+ case CHAR_TYPE: case REAL_TYPE: case OFFSET_TYPE:
+ PROMOTE_MODE (mode, unsignedp, type);
+ break;
+#endif
+
+ case POINTER_TYPE:
+ break;
+ }
+
+ *punsignedp = unsignedp;
+ return mode;
+}
+
+/* Adjust the stack pointer by ADJUST (an rtx for a number of bytes).
+ This pops when ADJUST is positive. ADJUST need not be constant. */
+
+void
+adjust_stack (adjust)
+ rtx adjust;
+{
+ rtx temp;
+ adjust = protect_from_queue (adjust, 0);
+
+ if (adjust == const0_rtx)
+ return;
+
+ temp = expand_binop (Pmode,
+#ifdef STACK_GROWS_DOWNWARD
+ add_optab,
+#else
+ sub_optab,
+#endif
+ stack_pointer_rtx, adjust, stack_pointer_rtx, 0,
+ OPTAB_LIB_WIDEN);
+
+ if (temp != stack_pointer_rtx)
+ emit_move_insn (stack_pointer_rtx, temp);
+}
+
+/* Adjust the stack pointer by minus ADJUST (an rtx for a number of bytes).
+ This pushes when ADJUST is positive. ADJUST need not be constant. */
+
+void
+anti_adjust_stack (adjust)
+ rtx adjust;
+{
+ rtx temp;
+ adjust = protect_from_queue (adjust, 0);
+
+ if (adjust == const0_rtx)
+ return;
+
+ temp = expand_binop (Pmode,
+#ifdef STACK_GROWS_DOWNWARD
+ sub_optab,
+#else
+ add_optab,
+#endif
+ stack_pointer_rtx, adjust, stack_pointer_rtx, 0,
+ OPTAB_LIB_WIDEN);
+
+ if (temp != stack_pointer_rtx)
+ emit_move_insn (stack_pointer_rtx, temp);
+}
+
+/* Round the size of a block to be pushed up to the boundary required
+ by this machine. SIZE is the desired size, which need not be constant. */
+
+rtx
+round_push (size)
+ rtx size;
+{
+#ifdef STACK_BOUNDARY
+ int align = STACK_BOUNDARY / BITS_PER_UNIT;
+ if (align == 1)
+ return size;
+ if (GET_CODE (size) == CONST_INT)
+ {
+ int new = (INTVAL (size) + align - 1) / align * align;
+ if (INTVAL (size) != new)
+ size = GEN_INT (new);
+ }
+ else
+ {
+ /* CEIL_DIV_EXPR needs to worry about the addition overflowing,
+ but we know it can't. So add ourselves and then do TRUNC_DIV_EXPR. */
+ size = expand_binop (Pmode, add_optab, size, GEN_INT (align - 1),
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ size = expand_divmod (0, TRUNC_DIV_EXPR, Pmode, size, GEN_INT (align),
+ NULL_RTX, 1);
+ size = expand_mult (Pmode, size, GEN_INT (align), NULL_RTX, 1);
+ }
+#endif /* STACK_BOUNDARY */
+ return size;
+}
+
+/* Save the stack pointer for the purpose in SAVE_LEVEL. PSAVE is a pointer
+ to a previously-created save area. If no save area has been allocated,
+ this function will allocate one. If a save area is specified, it
+ must be of the proper mode.
+
+ The insns are emitted after insn AFTER, if nonzero, otherwise the insns
+ are emitted at the current position. */
+
+void
+emit_stack_save (save_level, psave, after)
+ enum save_level save_level;
+ rtx *psave;
+ rtx after;
+{
+ rtx sa = *psave;
+ /* The default is that we use a move insn and save in a Pmode object. */
+ rtx (*fcn) () = gen_move_insn;
+ enum machine_mode mode = Pmode;
+
+ /* See if this machine has anything special to do for this kind of save. */
+ switch (save_level)
+ {
+#ifdef HAVE_save_stack_block
+ case SAVE_BLOCK:
+ if (HAVE_save_stack_block)
+ {
+ fcn = gen_save_stack_block;
+ mode = insn_operand_mode[CODE_FOR_save_stack_block][0];
+ }
+ break;
+#endif
+#ifdef HAVE_save_stack_function
+ case SAVE_FUNCTION:
+ if (HAVE_save_stack_function)
+ {
+ fcn = gen_save_stack_function;
+ mode = insn_operand_mode[CODE_FOR_save_stack_function][0];
+ }
+ break;
+#endif
+#ifdef HAVE_save_stack_nonlocal
+ case SAVE_NONLOCAL:
+ if (HAVE_save_stack_nonlocal)
+ {
+ fcn = gen_save_stack_nonlocal;
+ mode = insn_operand_mode[(int) CODE_FOR_save_stack_nonlocal][0];
+ }
+ break;
+#endif
+ }
+
+ /* If there is no save area and we have to allocate one, do so. Otherwise
+ verify the save area is the proper mode. */
+
+ if (sa == 0)
+ {
+ if (mode != VOIDmode)
+ {
+ if (save_level == SAVE_NONLOCAL)
+ *psave = sa = assign_stack_local (mode, GET_MODE_SIZE (mode), 0);
+ else
+ *psave = sa = gen_reg_rtx (mode);
+ }
+ }
+ else
+ {
+ if (mode == VOIDmode || GET_MODE (sa) != mode)
+ abort ();
+ }
+
+ if (after)
+ {
+ rtx seq;
+
+ start_sequence ();
+ /* We must validize inside the sequence, to ensure that any instructions
+ created by the validize call also get moved to the right place. */
+ if (sa != 0)
+ sa = validize_mem (sa);
+ emit_insn (fcn (sa, stack_pointer_rtx));
+ seq = gen_sequence ();
+ end_sequence ();
+ emit_insn_after (seq, after);
+ }
+ else
+ {
+ if (sa != 0)
+ sa = validize_mem (sa);
+ emit_insn (fcn (sa, stack_pointer_rtx));
+ }
+}
+
+/* Restore the stack pointer for the purpose in SAVE_LEVEL. SA is the save
+ area made by emit_stack_save. If it is zero, we have nothing to do.
+
+ Put any emitted insns after insn AFTER, if nonzero, otherwise at
+ current position. */
+
+void
+emit_stack_restore (save_level, sa, after)
+ enum save_level save_level;
+ rtx after;
+ rtx sa;
+{
+ /* The default is that we use a move insn. */
+ rtx (*fcn) () = gen_move_insn;
+
+ /* See if this machine has anything special to do for this kind of save. */
+ switch (save_level)
+ {
+#ifdef HAVE_restore_stack_block
+ case SAVE_BLOCK:
+ if (HAVE_restore_stack_block)
+ fcn = gen_restore_stack_block;
+ break;
+#endif
+#ifdef HAVE_restore_stack_function
+ case SAVE_FUNCTION:
+ if (HAVE_restore_stack_function)
+ fcn = gen_restore_stack_function;
+ break;
+#endif
+#ifdef HAVE_restore_stack_nonlocal
+
+ case SAVE_NONLOCAL:
+ if (HAVE_restore_stack_nonlocal)
+ fcn = gen_restore_stack_nonlocal;
+ break;
+#endif
+ }
+
+ if (sa != 0)
+ sa = validize_mem (sa);
+
+ if (after)
+ {
+ rtx seq;
+
+ start_sequence ();
+ emit_insn (fcn (stack_pointer_rtx, sa));
+ seq = gen_sequence ();
+ end_sequence ();
+ emit_insn_after (seq, after);
+ }
+ else
+ emit_insn (fcn (stack_pointer_rtx, sa));
+}
+
+/* Return an rtx representing the address of an area of memory dynamically
+ pushed on the stack. This region of memory is always aligned to
+ a multiple of BIGGEST_ALIGNMENT.
+
+ Any required stack pointer alignment is preserved.
+
+ SIZE is an rtx representing the size of the area.
+ TARGET is a place in which the address can be placed.
+
+ KNOWN_ALIGN is the alignment (in bits) that we know SIZE has. */
+
+rtx
+allocate_dynamic_stack_space (size, target, known_align)
+ rtx size;
+ rtx target;
+ int known_align;
+{
+ /* If we're asking for zero bytes, it doesn't matter what we point
+ to since we can't derefference it. But return a reasonable
+ address anyway. */
+ if (size == const0_rtx)
+ return virtual_stack_dynamic_rtx;
+
+ /* Otherwise, show we're calling alloca or equivalent. */
+ current_function_calls_alloca = 1;
+
+ /* Ensure the size is in the proper mode. */
+ if (GET_MODE (size) != VOIDmode && GET_MODE (size) != Pmode)
+ size = convert_to_mode (Pmode, size, 1);
+
+ /* We will need to ensure that the address we return is aligned to
+ BIGGEST_ALIGNMENT. If STACK_DYNAMIC_OFFSET is defined, we don't
+ always know its final value at this point in the compilation (it
+ might depend on the size of the outgoing parameter lists, for
+ example), so we must align the value to be returned in that case.
+ (Note that STACK_DYNAMIC_OFFSET will have a default non-zero value if
+ STACK_POINTER_OFFSET or ACCUMULATE_OUTGOING_ARGS are defined).
+ We must also do an alignment operation on the returned value if
+ the stack pointer alignment is less strict that BIGGEST_ALIGNMENT.
+
+ If we have to align, we must leave space in SIZE for the hole
+ that might result from the alignment operation. */
+
+#if defined (STACK_DYNAMIC_OFFSET) || defined(STACK_POINTER_OFFSET) || defined (ALLOCATE_OUTGOING_ARGS)
+#define MUST_ALIGN
+#endif
+
+#if ! defined (MUST_ALIGN) && (!defined(STACK_BOUNDARY) || STACK_BOUNDARY < BIGGEST_ALIGNMENT)
+#define MUST_ALIGN
+#endif
+
+#ifdef MUST_ALIGN
+
+#if 0 /* It turns out we must always make extra space, if MUST_ALIGN
+ because we must always round the address up at the end,
+ because we don't know whether the dynamic offset
+ will mess up the desired alignment. */
+ /* If we have to round the address up regardless of known_align,
+ make extra space regardless, also. */
+ if (known_align % BIGGEST_ALIGNMENT != 0)
+#endif
+ {
+ if (GET_CODE (size) == CONST_INT)
+ size = GEN_INT (INTVAL (size)
+ + (BIGGEST_ALIGNMENT / BITS_PER_UNIT - 1));
+ else
+ size = expand_binop (Pmode, add_optab, size,
+ GEN_INT (BIGGEST_ALIGNMENT / BITS_PER_UNIT - 1),
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ }
+
+#endif
+
+#ifdef SETJMP_VIA_SAVE_AREA
+ /* If setjmp restores regs from a save area in the stack frame,
+ avoid clobbering the reg save area. Note that the offset of
+ virtual_incoming_args_rtx includes the preallocated stack args space.
+ It would be no problem to clobber that, but it's on the wrong side
+ of the old save area. */
+ {
+ rtx dynamic_offset
+ = expand_binop (Pmode, sub_optab, virtual_stack_dynamic_rtx,
+ stack_pointer_rtx, NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ size = expand_binop (Pmode, add_optab, size, dynamic_offset,
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ }
+#endif /* SETJMP_VIA_SAVE_AREA */
+
+ /* Round the size to a multiple of the required stack alignment.
+ Since the stack if presumed to be rounded before this allocation,
+ this will maintain the required alignment.
+
+ If the stack grows downward, we could save an insn by subtracting
+ SIZE from the stack pointer and then aligning the stack pointer.
+ The problem with this is that the stack pointer may be unaligned
+ between the execution of the subtraction and alignment insns and
+ some machines do not allow this. Even on those that do, some
+ signal handlers malfunction if a signal should occur between those
+ insns. Since this is an extremely rare event, we have no reliable
+ way of knowing which systems have this problem. So we avoid even
+ momentarily mis-aligning the stack. */
+
+#ifdef STACK_BOUNDARY
+ /* If we added a variable amount to SIZE,
+ we can no longer assume it is aligned. */
+#if !defined (SETJMP_VIA_SAVE_AREA) && !defined (MUST_ALIGN)
+ if (known_align % STACK_BOUNDARY != 0)
+#endif
+ size = round_push (size);
+#endif
+
+ do_pending_stack_adjust ();
+
+ /* Don't use a TARGET that isn't a pseudo. */
+ if (target == 0 || GET_CODE (target) != REG
+ || REGNO (target) < FIRST_PSEUDO_REGISTER)
+ target = gen_reg_rtx (Pmode);
+
+ mark_reg_pointer (target);
+
+#ifndef STACK_GROWS_DOWNWARD
+ emit_move_insn (target, virtual_stack_dynamic_rtx);
+#endif
+
+ /* Perform the required allocation from the stack. Some systems do
+ this differently than simply incrementing/decrementing from the
+ stack pointer. */
+#ifdef HAVE_allocate_stack
+ if (HAVE_allocate_stack)
+ {
+ enum machine_mode mode
+ = insn_operand_mode[(int) CODE_FOR_allocate_stack][0];
+
+ if (insn_operand_predicate[(int) CODE_FOR_allocate_stack][0]
+ && ! ((*insn_operand_predicate[(int) CODE_FOR_allocate_stack][0])
+ (size, mode)))
+ size = copy_to_mode_reg (mode, size);
+
+ emit_insn (gen_allocate_stack (size));
+ }
+ else
+#endif
+ anti_adjust_stack (size);
+
+#ifdef STACK_GROWS_DOWNWARD
+ emit_move_insn (target, virtual_stack_dynamic_rtx);
+#endif
+
+#ifdef MUST_ALIGN
+#if 0 /* Even if we know the stack pointer has enough alignment,
+ there's no way to tell whether virtual_stack_dynamic_rtx shares that
+ alignment, so we still need to round the address up. */
+ if (known_align % BIGGEST_ALIGNMENT != 0)
+#endif
+ {
+ /* CEIL_DIV_EXPR needs to worry about the addition overflowing,
+ but we know it can't. So add ourselves and then do TRUNC_DIV_EXPR. */
+ target = expand_binop (Pmode, add_optab, target,
+ GEN_INT (BIGGEST_ALIGNMENT / BITS_PER_UNIT - 1),
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ target = expand_divmod (0, TRUNC_DIV_EXPR, Pmode, target,
+ GEN_INT (BIGGEST_ALIGNMENT / BITS_PER_UNIT),
+ NULL_RTX, 1);
+ target = expand_mult (Pmode, target,
+ GEN_INT (BIGGEST_ALIGNMENT / BITS_PER_UNIT),
+ NULL_RTX, 1);
+ }
+#endif
+
+ /* Some systems require a particular insn to refer to the stack
+ to make the pages exist. */
+#ifdef HAVE_probe
+ if (HAVE_probe)
+ emit_insn (gen_probe ());
+#endif
+
+ /* Record the new stack level for nonlocal gotos. */
+ if (nonlocal_goto_handler_slot != 0)
+ emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX);
+
+ return target;
+}
+
+/* Return an rtx representing the register or memory location
+ in which a scalar value of data type VALTYPE
+ was returned by a function call to function FUNC.
+ FUNC is a FUNCTION_DECL node if the precise function is known,
+ otherwise 0. */
+
+rtx
+hard_function_value (valtype, func)
+ tree valtype;
+ tree func;
+{
+ return FUNCTION_VALUE (valtype, func);
+}
+
+/* Return an rtx representing the register or memory location
+ in which a scalar value of mode MODE was returned by a library call. */
+
+rtx
+hard_libcall_value (mode)
+ enum machine_mode mode;
+{
+ return LIBCALL_VALUE (mode);
+}
+
+/* Look up the tree code for a given rtx code
+ to provide the arithmetic operation for REAL_ARITHMETIC.
+ The function returns an int because the caller may not know
+ what `enum tree_code' means. */
+
+int
+rtx_to_tree_code (code)
+ enum rtx_code code;
+{
+ enum tree_code tcode;
+
+ switch (code)
+ {
+ case PLUS:
+ tcode = PLUS_EXPR;
+ break;
+ case MINUS:
+ tcode = MINUS_EXPR;
+ break;
+ case MULT:
+ tcode = MULT_EXPR;
+ break;
+ case DIV:
+ tcode = RDIV_EXPR;
+ break;
+ case SMIN:
+ tcode = MIN_EXPR;
+ break;
+ case SMAX:
+ tcode = MAX_EXPR;
+ break;
+ default:
+ tcode = LAST_AND_UNUSED_TREE_CODE;
+ break;
+ }
+ return ((int) tcode);
+}
diff --git a/gnu/usr.bin/cc/cc_int/expmed.c b/gnu/usr.bin/cc/cc_int/expmed.c
new file mode 100644
index 0000000..f3beae0
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/expmed.c
@@ -0,0 +1,3957 @@
+/* Medium-level subroutines: convert bit-field store and extract
+ and shifts, multiplies and divides to rtl instructions.
+ Copyright (C) 1987, 88, 89, 92, 93, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#include "rtl.h"
+#include "tree.h"
+#include "flags.h"
+#include "insn-flags.h"
+#include "insn-codes.h"
+#include "insn-config.h"
+#include "expr.h"
+#include "real.h"
+#include "recog.h"
+
+static void store_fixed_bit_field PROTO((rtx, int, int, int, rtx, int));
+static void store_split_bit_field PROTO((rtx, int, int, rtx, int));
+static rtx extract_fixed_bit_field PROTO((enum machine_mode, rtx, int,
+ int, int, rtx, int, int));
+static rtx mask_rtx PROTO((enum machine_mode, int,
+ int, int));
+static rtx lshift_value PROTO((enum machine_mode, rtx,
+ int, int));
+static rtx extract_split_bit_field PROTO((rtx, int, int, int, int));
+
+#define CEIL(x,y) (((x) + (y) - 1) / (y))
+
+/* Non-zero means divides or modulus operations are relatively cheap for
+ powers of two, so don't use branches; emit the operation instead.
+ Usually, this will mean that the MD file will emit non-branch
+ sequences. */
+
+static int sdiv_pow2_cheap, smod_pow2_cheap;
+
+#ifndef SLOW_UNALIGNED_ACCESS
+#define SLOW_UNALIGNED_ACCESS STRICT_ALIGNMENT
+#endif
+
+/* For compilers that support multiple targets with different word sizes,
+ MAX_BITS_PER_WORD contains the biggest value of BITS_PER_WORD. An example
+ is the H8/300(H) compiler. */
+
+#ifndef MAX_BITS_PER_WORD
+#define MAX_BITS_PER_WORD BITS_PER_WORD
+#endif
+
+/* Cost of various pieces of RTL. */
+static int add_cost, negate_cost, zero_cost;
+static int shift_cost[MAX_BITS_PER_WORD];
+static int shiftadd_cost[MAX_BITS_PER_WORD];
+static int shiftsub_cost[MAX_BITS_PER_WORD];
+
+void
+init_expmed ()
+{
+ char *free_point;
+ /* This is "some random pseudo register" for purposes of calling recog
+ to see what insns exist. */
+ rtx reg = gen_rtx (REG, word_mode, 10000);
+ rtx shift_insn, shiftadd_insn, shiftsub_insn;
+ int dummy;
+ int m;
+
+ start_sequence ();
+
+ /* Since we are on the permanent obstack, we must be sure we save this
+ spot AFTER we call start_sequence, since it will reuse the rtl it
+ makes. */
+
+ free_point = (char *) oballoc (0);
+
+ zero_cost = rtx_cost (const0_rtx, 0);
+ add_cost = rtx_cost (gen_rtx (PLUS, word_mode, reg, reg), SET);
+
+ shift_insn = emit_insn (gen_rtx (SET, VOIDmode, reg,
+ gen_rtx (ASHIFT, word_mode, reg,
+ const0_rtx)));
+
+ shiftadd_insn = emit_insn (gen_rtx (SET, VOIDmode, reg,
+ gen_rtx (PLUS, word_mode,
+ gen_rtx (MULT, word_mode,
+ reg, const0_rtx),
+ reg)));
+
+ shiftsub_insn = emit_insn (gen_rtx (SET, VOIDmode, reg,
+ gen_rtx (MINUS, word_mode,
+ gen_rtx (MULT, word_mode,
+ reg, const0_rtx),
+ reg)));
+
+ init_recog ();
+
+ shift_cost[0] = 0;
+ shiftadd_cost[0] = shiftsub_cost[0] = add_cost;
+
+ for (m = 1; m < BITS_PER_WORD; m++)
+ {
+ shift_cost[m] = shiftadd_cost[m] = shiftsub_cost[m] = 32000;
+
+ XEXP (SET_SRC (PATTERN (shift_insn)), 1) = GEN_INT (m);
+ if (recog (PATTERN (shift_insn), shift_insn, &dummy) >= 0)
+ shift_cost[m] = rtx_cost (SET_SRC (PATTERN (shift_insn)), SET);
+
+ XEXP (XEXP (SET_SRC (PATTERN (shiftadd_insn)), 0), 1)
+ = GEN_INT ((HOST_WIDE_INT) 1 << m);
+ if (recog (PATTERN (shiftadd_insn), shiftadd_insn, &dummy) >= 0)
+ shiftadd_cost[m] = rtx_cost (SET_SRC (PATTERN (shiftadd_insn)), SET);
+
+ XEXP (XEXP (SET_SRC (PATTERN (shiftsub_insn)), 0), 1)
+ = GEN_INT ((HOST_WIDE_INT) 1 << m);
+ if (recog (PATTERN (shiftsub_insn), shiftsub_insn, &dummy) >= 0)
+ shiftsub_cost[m] = rtx_cost (SET_SRC (PATTERN (shiftsub_insn)), SET);
+ }
+
+ negate_cost = rtx_cost (gen_rtx (NEG, word_mode, reg), SET);
+
+ sdiv_pow2_cheap
+ = (rtx_cost (gen_rtx (DIV, word_mode, reg, GEN_INT (32)), SET)
+ <= 2 * add_cost);
+ smod_pow2_cheap
+ = (rtx_cost (gen_rtx (MOD, word_mode, reg, GEN_INT (32)), SET)
+ <= 2 * add_cost);
+
+ /* Free the objects we just allocated. */
+ end_sequence ();
+ obfree (free_point);
+}
+
+/* Return an rtx representing minus the value of X.
+ MODE is the intended mode of the result,
+ useful if X is a CONST_INT. */
+
+rtx
+negate_rtx (mode, x)
+ enum machine_mode mode;
+ rtx x;
+{
+ if (GET_CODE (x) == CONST_INT)
+ {
+ HOST_WIDE_INT val = - INTVAL (x);
+ if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_WIDE_INT)
+ {
+ /* Sign extend the value from the bits that are significant. */
+ if (val & ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)))
+ val |= (HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (mode);
+ else
+ val &= ((HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (mode)) - 1;
+ }
+ return GEN_INT (val);
+ }
+ else
+ return expand_unop (GET_MODE (x), neg_optab, x, NULL_RTX, 0);
+}
+
+/* Generate code to store value from rtx VALUE
+ into a bit-field within structure STR_RTX
+ containing BITSIZE bits starting at bit BITNUM.
+ FIELDMODE is the machine-mode of the FIELD_DECL node for this field.
+ ALIGN is the alignment that STR_RTX is known to have, measured in bytes.
+ TOTAL_SIZE is the size of the structure in bytes, or -1 if varying. */
+
+/* ??? Note that there are two different ideas here for how
+ to determine the size to count bits within, for a register.
+ One is BITS_PER_WORD, and the other is the size of operand 3
+ of the insv pattern. (The latter assumes that an n-bit machine
+ will be able to insert bit fields up to n bits wide.)
+ It isn't certain that either of these is right.
+ extract_bit_field has the same quandary. */
+
+rtx
+store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
+ rtx str_rtx;
+ register int bitsize;
+ int bitnum;
+ enum machine_mode fieldmode;
+ rtx value;
+ int align;
+ int total_size;
+{
+ int unit = (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD;
+ register int offset = bitnum / unit;
+ register int bitpos = bitnum % unit;
+ register rtx op0 = str_rtx;
+
+ if (GET_CODE (str_rtx) == MEM && ! MEM_IN_STRUCT_P (str_rtx))
+ abort ();
+
+ /* Discount the part of the structure before the desired byte.
+ We need to know how many bytes are safe to reference after it. */
+ if (total_size >= 0)
+ total_size -= (bitpos / BIGGEST_ALIGNMENT
+ * (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
+
+ while (GET_CODE (op0) == SUBREG)
+ {
+ /* The following line once was done only if WORDS_BIG_ENDIAN,
+ but I think that is a mistake. WORDS_BIG_ENDIAN is
+ meaningful at a much higher level; when structures are copied
+ between memory and regs, the higher-numbered regs
+ always get higher addresses. */
+ offset += SUBREG_WORD (op0);
+ /* We used to adjust BITPOS here, but now we do the whole adjustment
+ right after the loop. */
+ op0 = SUBREG_REG (op0);
+ }
+
+#if BYTES_BIG_ENDIAN
+ /* If OP0 is a register, BITPOS must count within a word.
+ But as we have it, it counts within whatever size OP0 now has.
+ On a bigendian machine, these are not the same, so convert. */
+ if (GET_CODE (op0) != MEM && unit > GET_MODE_BITSIZE (GET_MODE (op0)))
+ bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));
+#endif
+
+ value = protect_from_queue (value, 0);
+
+ if (flag_force_mem)
+ value = force_not_mem (value);
+
+ /* Note that the adjustment of BITPOS above has no effect on whether
+ BITPOS is 0 in a REG bigger than a word. */
+ if (GET_MODE_SIZE (fieldmode) >= UNITS_PER_WORD
+ && (GET_CODE (op0) != MEM
+ || ! SLOW_UNALIGNED_ACCESS
+ || (offset * BITS_PER_UNIT % bitsize == 0
+ && align % GET_MODE_SIZE (fieldmode) == 0))
+ && bitpos == 0 && bitsize == GET_MODE_BITSIZE (fieldmode))
+ {
+ /* Storing in a full-word or multi-word field in a register
+ can be done with just SUBREG. */
+ if (GET_MODE (op0) != fieldmode)
+ {
+ if (GET_CODE (op0) == REG)
+ op0 = gen_rtx (SUBREG, fieldmode, op0, offset);
+ else
+ op0 = change_address (op0, fieldmode,
+ plus_constant (XEXP (op0, 0), offset));
+ }
+ emit_move_insn (op0, value);
+ return value;
+ }
+
+ /* Storing an lsb-aligned field in a register
+ can be done with a movestrict instruction. */
+
+ if (GET_CODE (op0) != MEM
+#if BYTES_BIG_ENDIAN
+ && bitpos + bitsize == unit
+#else
+ && bitpos == 0
+#endif
+ && bitsize == GET_MODE_BITSIZE (fieldmode)
+ && (GET_MODE (op0) == fieldmode
+ || (movstrict_optab->handlers[(int) fieldmode].insn_code
+ != CODE_FOR_nothing)))
+ {
+ /* Get appropriate low part of the value being stored. */
+ if (GET_CODE (value) == CONST_INT || GET_CODE (value) == REG)
+ value = gen_lowpart (fieldmode, value);
+ else if (!(GET_CODE (value) == SYMBOL_REF
+ || GET_CODE (value) == LABEL_REF
+ || GET_CODE (value) == CONST))
+ value = convert_to_mode (fieldmode, value, 0);
+
+ if (GET_MODE (op0) == fieldmode)
+ emit_move_insn (op0, value);
+ else
+ {
+ int icode = movstrict_optab->handlers[(int) fieldmode].insn_code;
+ if(! (*insn_operand_predicate[icode][1]) (value, fieldmode))
+ value = copy_to_mode_reg (fieldmode, value);
+ emit_insn (GEN_FCN (icode)
+ (gen_rtx (SUBREG, fieldmode, op0, offset), value));
+ }
+ return value;
+ }
+
+ /* Handle fields bigger than a word. */
+
+ if (bitsize > BITS_PER_WORD)
+ {
+ /* Here we transfer the words of the field
+ in the order least significant first.
+ This is because the most significant word is the one which may
+ be less than full. */
+
+ int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD;
+ int i;
+
+ /* This is the mode we must force value to, so that there will be enough
+ subwords to extract. Note that fieldmode will often (always?) be
+ VOIDmode, because that is what store_field uses to indicate that this
+ is a bit field, but passing VOIDmode to operand_subword_force will
+ result in an abort. */
+ fieldmode = mode_for_size (nwords * BITS_PER_WORD, MODE_INT, 0);
+
+ for (i = 0; i < nwords; i++)
+ {
+ /* If I is 0, use the low-order word in both field and target;
+ if I is 1, use the next to lowest word; and so on. */
+ int wordnum = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i);
+ int bit_offset = (WORDS_BIG_ENDIAN
+ ? MAX (bitsize - (i + 1) * BITS_PER_WORD, 0)
+ : i * BITS_PER_WORD);
+ store_bit_field (op0, MIN (BITS_PER_WORD,
+ bitsize - i * BITS_PER_WORD),
+ bitnum + bit_offset, word_mode,
+ operand_subword_force (value, wordnum,
+ (GET_MODE (value) == VOIDmode
+ ? fieldmode
+ : GET_MODE (value))),
+ align, total_size);
+ }
+ return value;
+ }
+
+ /* From here on we can assume that the field to be stored in is
+ a full-word (whatever type that is), since it is shorter than a word. */
+
+ /* OFFSET is the number of words or bytes (UNIT says which)
+ from STR_RTX to the first word or byte containing part of the field. */
+
+ if (GET_CODE (op0) == REG)
+ {
+ if (offset != 0
+ || GET_MODE_SIZE (GET_MODE (op0)) > UNITS_PER_WORD)
+ op0 = gen_rtx (SUBREG, TYPE_MODE (type_for_size (BITS_PER_WORD, 0)),
+ op0, offset);
+ offset = 0;
+ }
+ else
+ {
+ op0 = protect_from_queue (op0, 1);
+ }
+
+ /* If VALUE is a floating-point mode, access it as an integer of the
+ corresponding size. This can occur on a machine with 64 bit registers
+ that uses SFmode for float. This can also occur for unaligned float
+ structure fields. */
+ if (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT)
+ {
+ if (GET_CODE (value) != REG)
+ value = copy_to_reg (value);
+ value = gen_rtx (SUBREG, word_mode, value, 0);
+ }
+
+ /* Now OFFSET is nonzero only if OP0 is memory
+ and is therefore always measured in bytes. */
+
+#ifdef HAVE_insv
+ if (HAVE_insv
+ && !(bitsize == 1 && GET_CODE (value) == CONST_INT)
+ /* Ensure insv's size is wide enough for this field. */
+ && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_insv][3])
+ >= bitsize))
+ {
+ int xbitpos = bitpos;
+ rtx value1;
+ rtx xop0 = op0;
+ rtx last = get_last_insn ();
+ rtx pat;
+ enum machine_mode maxmode
+ = insn_operand_mode[(int) CODE_FOR_insv][3];
+
+ int save_volatile_ok = volatile_ok;
+ volatile_ok = 1;
+
+ /* If this machine's insv can only insert into a register, or if we
+ are to force MEMs into a register, copy OP0 into a register and
+ save it back later. */
+ if (GET_CODE (op0) == MEM
+ && (flag_force_mem
+ || ! ((*insn_operand_predicate[(int) CODE_FOR_insv][0])
+ (op0, VOIDmode))))
+ {
+ rtx tempreg;
+ enum machine_mode bestmode;
+
+ /* Get the mode to use for inserting into this field. If OP0 is
+ BLKmode, get the smallest mode consistent with the alignment. If
+ OP0 is a non-BLKmode object that is no wider than MAXMODE, use its
+ mode. Otherwise, use the smallest mode containing the field. */
+
+ if (GET_MODE (op0) == BLKmode
+ || GET_MODE_SIZE (GET_MODE (op0)) > GET_MODE_SIZE (maxmode))
+ bestmode
+ = get_best_mode (bitsize, bitnum, align * BITS_PER_UNIT, maxmode,
+ MEM_VOLATILE_P (op0));
+ else
+ bestmode = GET_MODE (op0);
+
+ if (bestmode == VOIDmode
+ || (STRICT_ALIGNMENT && GET_MODE_SIZE (bestmode) > align))
+ goto insv_loses;
+
+ /* Adjust address to point to the containing unit of that mode. */
+ unit = GET_MODE_BITSIZE (bestmode);
+ /* Compute offset as multiple of this unit, counting in bytes. */
+ offset = (bitnum / unit) * GET_MODE_SIZE (bestmode);
+ bitpos = bitnum % unit;
+ op0 = change_address (op0, bestmode,
+ plus_constant (XEXP (op0, 0), offset));
+
+ /* Fetch that unit, store the bitfield in it, then store the unit. */
+ tempreg = copy_to_reg (op0);
+ store_bit_field (tempreg, bitsize, bitpos, fieldmode, value,
+ align, total_size);
+ emit_move_insn (op0, tempreg);
+ return value;
+ }
+ volatile_ok = save_volatile_ok;
+
+ /* Add OFFSET into OP0's address. */
+ if (GET_CODE (xop0) == MEM)
+ xop0 = change_address (xop0, byte_mode,
+ plus_constant (XEXP (xop0, 0), offset));
+
+ /* If xop0 is a register, we need it in MAXMODE
+ to make it acceptable to the format of insv. */
+ if (GET_CODE (xop0) == SUBREG)
+ /* We can't just change the mode, because this might clobber op0,
+ and we will need the original value of op0 if insv fails. */
+ xop0 = gen_rtx (SUBREG, maxmode, SUBREG_REG (xop0), SUBREG_WORD (xop0));
+ if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode)
+ xop0 = gen_rtx (SUBREG, maxmode, xop0, 0);
+
+ /* On big-endian machines, we count bits from the most significant.
+ If the bit field insn does not, we must invert. */
+
+#if BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN
+ xbitpos = unit - bitsize - xbitpos;
+#endif
+ /* We have been counting XBITPOS within UNIT.
+ Count instead within the size of the register. */
+#if BITS_BIG_ENDIAN
+ if (GET_CODE (xop0) != MEM)
+ xbitpos += GET_MODE_BITSIZE (maxmode) - unit;
+#endif
+ unit = GET_MODE_BITSIZE (maxmode);
+
+ /* Convert VALUE to maxmode (which insv insn wants) in VALUE1. */
+ value1 = value;
+ if (GET_MODE (value) != maxmode)
+ {
+ if (GET_MODE_BITSIZE (GET_MODE (value)) >= bitsize)
+ {
+ /* Optimization: Don't bother really extending VALUE
+ if it has all the bits we will actually use. However,
+ if we must narrow it, be sure we do it correctly. */
+
+ if (GET_MODE_SIZE (GET_MODE (value)) < GET_MODE_SIZE (maxmode))
+ {
+ /* Avoid making subreg of a subreg, or of a mem. */
+ if (GET_CODE (value1) != REG)
+ value1 = copy_to_reg (value1);
+ value1 = gen_rtx (SUBREG, maxmode, value1, 0);
+ }
+ else
+ value1 = gen_lowpart (maxmode, value1);
+ }
+ else if (!CONSTANT_P (value))
+ /* Parse phase is supposed to make VALUE's data type
+ match that of the component reference, which is a type
+ at least as wide as the field; so VALUE should have
+ a mode that corresponds to that type. */
+ abort ();
+ }
+
+ /* If this machine's insv insists on a register,
+ get VALUE1 into a register. */
+ if (! ((*insn_operand_predicate[(int) CODE_FOR_insv][3])
+ (value1, maxmode)))
+ value1 = force_reg (maxmode, value1);
+
+ pat = gen_insv (xop0, GEN_INT (bitsize), GEN_INT (xbitpos), value1);
+ if (pat)
+ emit_insn (pat);
+ else
+ {
+ delete_insns_since (last);
+ store_fixed_bit_field (op0, offset, bitsize, bitpos, value, align);
+ }
+ }
+ else
+ insv_loses:
+#endif
+ /* Insv is not available; store using shifts and boolean ops. */
+ store_fixed_bit_field (op0, offset, bitsize, bitpos, value, align);
+ return value;
+}
+
+/* Use shifts and boolean operations to store VALUE
+ into a bit field of width BITSIZE
+ in a memory location specified by OP0 except offset by OFFSET bytes.
+ (OFFSET must be 0 if OP0 is a register.)
+ The field starts at position BITPOS within the byte.
+ (If OP0 is a register, it may be a full word or a narrower mode,
+ but BITPOS still counts within a full word,
+ which is significant on bigendian machines.)
+ STRUCT_ALIGN is the alignment the structure is known to have (in bytes).
+
+ Note that protect_from_queue has already been done on OP0 and VALUE. */
+
+static void
+store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align)
+ register rtx op0;
+ register int offset, bitsize, bitpos;
+ register rtx value;
+ int struct_align;
+{
+ register enum machine_mode mode;
+ int total_bits = BITS_PER_WORD;
+ rtx subtarget, temp;
+ int all_zero = 0;
+ int all_one = 0;
+
+ /* There is a case not handled here:
+ a structure with a known alignment of just a halfword
+ and a field split across two aligned halfwords within the structure.
+ Or likewise a structure with a known alignment of just a byte
+ and a field split across two bytes.
+ Such cases are not supposed to be able to occur. */
+
+ if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
+ {
+ if (offset != 0)
+ abort ();
+ /* Special treatment for a bit field split across two registers. */
+ if (bitsize + bitpos > BITS_PER_WORD)
+ {
+ store_split_bit_field (op0, bitsize, bitpos,
+ value, BITS_PER_WORD);
+ return;
+ }
+ }
+ else
+ {
+ /* Get the proper mode to use for this field. We want a mode that
+ includes the entire field. If such a mode would be larger than
+ a word, we won't be doing the extraction the normal way. */
+
+ mode = get_best_mode (bitsize, bitpos + offset * BITS_PER_UNIT,
+ struct_align * BITS_PER_UNIT, word_mode,
+ GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0));
+
+ if (mode == VOIDmode)
+ {
+ /* The only way this should occur is if the field spans word
+ boundaries. */
+ store_split_bit_field (op0,
+ bitsize, bitpos + offset * BITS_PER_UNIT,
+ value, struct_align);
+ return;
+ }
+
+ total_bits = GET_MODE_BITSIZE (mode);
+
+ /* Make sure bitpos is valid for the chosen mode. Adjust BITPOS to
+ be be in the range 0 to total_bits-1, and put any excess bytes in
+ OFFSET. */
+ if (bitpos >= total_bits)
+ {
+ offset += (bitpos / total_bits) * (total_bits / BITS_PER_UNIT);
+ bitpos -= ((bitpos / total_bits) * (total_bits / BITS_PER_UNIT)
+ * BITS_PER_UNIT);
+ }
+
+ /* Get ref to an aligned byte, halfword, or word containing the field.
+ Adjust BITPOS to be position within a word,
+ and OFFSET to be the offset of that word.
+ Then alter OP0 to refer to that word. */
+ bitpos += (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT;
+ offset -= (offset % (total_bits / BITS_PER_UNIT));
+ op0 = change_address (op0, mode,
+ plus_constant (XEXP (op0, 0), offset));
+ }
+
+ mode = GET_MODE (op0);
+
+ /* Now MODE is either some integral mode for a MEM as OP0,
+ or is a full-word for a REG as OP0. TOTAL_BITS corresponds.
+ The bit field is contained entirely within OP0.
+ BITPOS is the starting bit number within OP0.
+ (OP0's mode may actually be narrower than MODE.) */
+
+#if BYTES_BIG_ENDIAN
+ /* BITPOS is the distance between our msb
+ and that of the containing datum.
+ Convert it to the distance from the lsb. */
+
+ bitpos = total_bits - bitsize - bitpos;
+#endif
+ /* Now BITPOS is always the distance between our lsb
+ and that of OP0. */
+
+ /* Shift VALUE left by BITPOS bits. If VALUE is not constant,
+ we must first convert its mode to MODE. */
+
+ if (GET_CODE (value) == CONST_INT)
+ {
+ register HOST_WIDE_INT v = INTVAL (value);
+
+ if (bitsize < HOST_BITS_PER_WIDE_INT)
+ v &= ((HOST_WIDE_INT) 1 << bitsize) - 1;
+
+ if (v == 0)
+ all_zero = 1;
+ else if ((bitsize < HOST_BITS_PER_WIDE_INT
+ && v == ((HOST_WIDE_INT) 1 << bitsize) - 1)
+ || (bitsize == HOST_BITS_PER_WIDE_INT && v == -1))
+ all_one = 1;
+
+ value = lshift_value (mode, value, bitpos, bitsize);
+ }
+ else
+ {
+ int must_and = (GET_MODE_BITSIZE (GET_MODE (value)) != bitsize
+ && bitpos + bitsize != GET_MODE_BITSIZE (mode));
+
+ if (GET_MODE (value) != mode)
+ {
+ if ((GET_CODE (value) == REG || GET_CODE (value) == SUBREG)
+ && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (value)))
+ value = gen_lowpart (mode, value);
+ else
+ value = convert_to_mode (mode, value, 1);
+ }
+
+ if (must_and)
+ value = expand_binop (mode, and_optab, value,
+ mask_rtx (mode, 0, bitsize, 0),
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ if (bitpos > 0)
+ value = expand_shift (LSHIFT_EXPR, mode, value,
+ build_int_2 (bitpos, 0), NULL_RTX, 1);
+ }
+
+ /* Now clear the chosen bits in OP0,
+ except that if VALUE is -1 we need not bother. */
+
+ subtarget = (GET_CODE (op0) == REG || ! flag_force_mem) ? op0 : 0;
+
+ if (! all_one)
+ {
+ temp = expand_binop (mode, and_optab, op0,
+ mask_rtx (mode, bitpos, bitsize, 1),
+ subtarget, 1, OPTAB_LIB_WIDEN);
+ subtarget = temp;
+ }
+ else
+ temp = op0;
+
+ /* Now logical-or VALUE into OP0, unless it is zero. */
+
+ if (! all_zero)
+ temp = expand_binop (mode, ior_optab, temp, value,
+ subtarget, 1, OPTAB_LIB_WIDEN);
+ if (op0 != temp)
+ emit_move_insn (op0, temp);
+}
+
+/* Store a bit field that is split across multiple accessible memory objects.
+
+ OP0 is the REG, SUBREG or MEM rtx for the first of the objects.
+ BITSIZE is the field width; BITPOS the position of its first bit
+ (within the word).
+ VALUE is the value to store.
+ ALIGN is the known alignment of OP0, measured in bytes.
+ This is also the size of the memory objects to be used.
+
+ This does not yet handle fields wider than BITS_PER_WORD. */
+
+static void
+store_split_bit_field (op0, bitsize, bitpos, value, align)
+ rtx op0;
+ int bitsize, bitpos;
+ rtx value;
+ int align;
+{
+ int unit;
+ int bitsdone = 0;
+
+ /* Make sure UNIT isn't larger than BITS_PER_WORD, we can only handle that
+ much at a time. */
+ if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
+ unit = BITS_PER_WORD;
+ else
+ unit = MIN (align * BITS_PER_UNIT, BITS_PER_WORD);
+
+ /* If VALUE is a constant other than a CONST_INT, get it into a register in
+ WORD_MODE. If we can do this using gen_lowpart_common, do so. Note
+ that VALUE might be a floating-point constant. */
+ if (CONSTANT_P (value) && GET_CODE (value) != CONST_INT)
+ {
+ rtx word = gen_lowpart_common (word_mode, value);
+
+ if (word && (value != word))
+ value = word;
+ else
+ value = gen_lowpart_common (word_mode,
+ force_reg (GET_MODE (value), value));
+ }
+
+ while (bitsdone < bitsize)
+ {
+ int thissize;
+ rtx part, word;
+ int thispos;
+ int offset;
+
+ offset = (bitpos + bitsdone) / unit;
+ thispos = (bitpos + bitsdone) % unit;
+
+ /* THISSIZE must not overrun a word boundary. Otherwise,
+ store_fixed_bit_field will call us again, and we will mutually
+ recurse forever. */
+ thissize = MIN (bitsize - bitsdone, BITS_PER_WORD);
+ thissize = MIN (thissize, unit - thispos);
+
+#if BYTES_BIG_ENDIAN
+ /* Fetch successively less significant portions. */
+ if (GET_CODE (value) == CONST_INT)
+ part = GEN_INT (((unsigned HOST_WIDE_INT) (INTVAL (value))
+ >> (bitsize - bitsdone - thissize))
+ & (((HOST_WIDE_INT) 1 << thissize) - 1));
+ else
+ {
+ /* The args are chosen so that the last part
+ includes the lsb. */
+ int bit_offset = 0;
+ /* If the value isn't in memory, then it must be right aligned
+ if a register, so skip past the padding on the left. If it
+ is in memory, then there is no padding on the left. */
+ if (GET_CODE (value) != MEM)
+ bit_offset = BITS_PER_WORD - bitsize;
+ part = extract_fixed_bit_field (word_mode, value, 0, thissize,
+ bit_offset + bitsdone,
+ NULL_RTX, 1, align);
+ }
+#else
+ /* Fetch successively more significant portions. */
+ if (GET_CODE (value) == CONST_INT)
+ part = GEN_INT (((unsigned HOST_WIDE_INT) (INTVAL (value)) >> bitsdone)
+ & (((HOST_WIDE_INT) 1 << thissize) - 1));
+ else
+ part = extract_fixed_bit_field (word_mode, value, 0, thissize,
+ bitsdone, NULL_RTX, 1, align);
+#endif
+
+ /* If OP0 is a register, then handle OFFSET here.
+
+ When handling multiword bitfields, extract_bit_field may pass
+ down a word_mode SUBREG of a larger REG for a bitfield that actually
+ crosses a word boundary. Thus, for a SUBREG, we must find
+ the current word starting from the base register. */
+ if (GET_CODE (op0) == SUBREG)
+ {
+ word = operand_subword_force (SUBREG_REG (op0),
+ SUBREG_WORD (op0) + offset,
+ GET_MODE (SUBREG_REG (op0)));
+ offset = 0;
+ }
+ else if (GET_CODE (op0) == REG)
+ {
+ word = operand_subword_force (op0, offset, GET_MODE (op0));
+ offset = 0;
+ }
+ else
+ word = op0;
+
+ /* OFFSET is in UNITs, and UNIT is in bits.
+ store_fixed_bit_field wants offset in bytes. */
+ store_fixed_bit_field (word, offset * unit / BITS_PER_UNIT,
+ thissize, thispos, part, align);
+ bitsdone += thissize;
+ }
+}
+
+/* Generate code to extract a byte-field from STR_RTX
+ containing BITSIZE bits, starting at BITNUM,
+ and put it in TARGET if possible (if TARGET is nonzero).
+ Regardless of TARGET, we return the rtx for where the value is placed.
+ It may be a QUEUED.
+
+ STR_RTX is the structure containing the byte (a REG or MEM).
+ UNSIGNEDP is nonzero if this is an unsigned bit field.
+ MODE is the natural mode of the field value once extracted.
+ TMODE is the mode the caller would like the value to have;
+ but the value may be returned with type MODE instead.
+
+ ALIGN is the alignment that STR_RTX is known to have, measured in bytes.
+ TOTAL_SIZE is the size in bytes of the containing structure,
+ or -1 if varying.
+
+ If a TARGET is specified and we can store in it at no extra cost,
+ we do so, and return TARGET.
+ Otherwise, we return a REG of mode TMODE or MODE, with TMODE preferred
+ if they are equally easy. */
+
+rtx
+extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
+ target, mode, tmode, align, total_size)
+ rtx str_rtx;
+ register int bitsize;
+ int bitnum;
+ int unsignedp;
+ rtx target;
+ enum machine_mode mode, tmode;
+ int align;
+ int total_size;
+{
+ int unit = (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD;
+ register int offset = bitnum / unit;
+ register int bitpos = bitnum % unit;
+ register rtx op0 = str_rtx;
+ rtx spec_target = target;
+ rtx spec_target_subreg = 0;
+
+ if (GET_CODE (str_rtx) == MEM && ! MEM_IN_STRUCT_P (str_rtx))
+ abort ();
+
+ /* Discount the part of the structure before the desired byte.
+ We need to know how many bytes are safe to reference after it. */
+ if (total_size >= 0)
+ total_size -= (bitpos / BIGGEST_ALIGNMENT
+ * (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
+
+ if (tmode == VOIDmode)
+ tmode = mode;
+ while (GET_CODE (op0) == SUBREG)
+ {
+ offset += SUBREG_WORD (op0);
+ op0 = SUBREG_REG (op0);
+ }
+
+#if BYTES_BIG_ENDIAN
+ /* If OP0 is a register, BITPOS must count within a word.
+ But as we have it, it counts within whatever size OP0 now has.
+ On a bigendian machine, these are not the same, so convert. */
+ if (GET_CODE (op0) != MEM && unit > GET_MODE_BITSIZE (GET_MODE (op0)))
+ bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));
+#endif
+
+ /* Extracting a full-word or multi-word value
+ from a structure in a register or aligned memory.
+ This can be done with just SUBREG.
+ So too extracting a subword value in
+ the least significant part of the register. */
+
+ if ((GET_CODE (op0) == REG
+ || (GET_CODE (op0) == MEM
+ && (! SLOW_UNALIGNED_ACCESS
+ || (offset * BITS_PER_UNIT % bitsize == 0
+ && align * BITS_PER_UNIT % bitsize == 0))))
+ && ((bitsize >= BITS_PER_WORD && bitsize == GET_MODE_BITSIZE (mode)
+ && bitpos % BITS_PER_WORD == 0)
+ || (mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0) != BLKmode
+#if BYTES_BIG_ENDIAN
+ && bitpos + bitsize == BITS_PER_WORD
+#else
+ && bitpos == 0
+#endif
+ )))
+ {
+ enum machine_mode mode1
+ = mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0);
+
+ if (mode1 != GET_MODE (op0))
+ {
+ if (GET_CODE (op0) == REG)
+ op0 = gen_rtx (SUBREG, mode1, op0, offset);
+ else
+ op0 = change_address (op0, mode1,
+ plus_constant (XEXP (op0, 0), offset));
+ }
+ if (mode1 != mode)
+ return convert_to_mode (tmode, op0, unsignedp);
+ return op0;
+ }
+
+ /* Handle fields bigger than a word. */
+
+ if (bitsize > BITS_PER_WORD)
+ {
+ /* Here we transfer the words of the field
+ in the order least significant first.
+ This is because the most significant word is the one which may
+ be less than full. */
+
+ int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD;
+ int i;
+
+ if (target == 0 || GET_CODE (target) != REG)
+ target = gen_reg_rtx (mode);
+
+ for (i = 0; i < nwords; i++)
+ {
+ /* If I is 0, use the low-order word in both field and target;
+ if I is 1, use the next to lowest word; and so on. */
+ int wordnum = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i);
+ int bit_offset = (WORDS_BIG_ENDIAN
+ ? MAX (0, bitsize - (i + 1) * BITS_PER_WORD)
+ : i * BITS_PER_WORD);
+ rtx target_part = operand_subword (target, wordnum, 1, VOIDmode);
+ rtx result_part
+ = extract_bit_field (op0, MIN (BITS_PER_WORD,
+ bitsize - i * BITS_PER_WORD),
+ bitnum + bit_offset,
+ 1, target_part, mode, word_mode,
+ align, total_size);
+
+ if (target_part == 0)
+ abort ();
+
+ if (result_part != target_part)
+ emit_move_insn (target_part, result_part);
+ }
+
+ if (unsignedp)
+ return target;
+ /* Signed bit field: sign-extend with two arithmetic shifts. */
+ target = expand_shift (LSHIFT_EXPR, mode, target,
+ build_int_2 (GET_MODE_BITSIZE (mode) - bitsize, 0),
+ NULL_RTX, 0);
+ return expand_shift (RSHIFT_EXPR, mode, target,
+ build_int_2 (GET_MODE_BITSIZE (mode) - bitsize, 0),
+ NULL_RTX, 0);
+ }
+
+ /* From here on we know the desired field is smaller than a word
+ so we can assume it is an integer. So we can safely extract it as one
+ size of integer, if necessary, and then truncate or extend
+ to the size that is wanted. */
+
+ /* OFFSET is the number of words or bytes (UNIT says which)
+ from STR_RTX to the first word or byte containing part of the field. */
+
+ if (GET_CODE (op0) == REG)
+ {
+ if (offset != 0
+ || GET_MODE_SIZE (GET_MODE (op0)) > UNITS_PER_WORD)
+ op0 = gen_rtx (SUBREG, TYPE_MODE (type_for_size (BITS_PER_WORD, 0)),
+ op0, offset);
+ offset = 0;
+ }
+ else
+ {
+ op0 = protect_from_queue (str_rtx, 1);
+ }
+
+ /* Now OFFSET is nonzero only for memory operands. */
+
+ if (unsignedp)
+ {
+#ifdef HAVE_extzv
+ if (HAVE_extzv
+ && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extzv][0])
+ >= bitsize))
+ {
+ int xbitpos = bitpos, xoffset = offset;
+ rtx bitsize_rtx, bitpos_rtx;
+ rtx last = get_last_insn();
+ rtx xop0 = op0;
+ rtx xtarget = target;
+ rtx xspec_target = spec_target;
+ rtx xspec_target_subreg = spec_target_subreg;
+ rtx pat;
+ enum machine_mode maxmode
+ = insn_operand_mode[(int) CODE_FOR_extzv][0];
+
+ if (GET_CODE (xop0) == MEM)
+ {
+ int save_volatile_ok = volatile_ok;
+ volatile_ok = 1;
+
+ /* Is the memory operand acceptable? */
+ if (flag_force_mem
+ || ! ((*insn_operand_predicate[(int) CODE_FOR_extzv][1])
+ (xop0, GET_MODE (xop0))))
+ {
+ /* No, load into a reg and extract from there. */
+ enum machine_mode bestmode;
+
+ /* Get the mode to use for inserting into this field. If
+ OP0 is BLKmode, get the smallest mode consistent with the
+ alignment. If OP0 is a non-BLKmode object that is no
+ wider than MAXMODE, use its mode. Otherwise, use the
+ smallest mode containing the field. */
+
+ if (GET_MODE (xop0) == BLKmode
+ || (GET_MODE_SIZE (GET_MODE (op0))
+ > GET_MODE_SIZE (maxmode)))
+ bestmode = get_best_mode (bitsize, bitnum,
+ align * BITS_PER_UNIT, maxmode,
+ MEM_VOLATILE_P (xop0));
+ else
+ bestmode = GET_MODE (xop0);
+
+ if (bestmode == VOIDmode
+ || (STRICT_ALIGNMENT && GET_MODE_SIZE (bestmode) > align))
+ goto extzv_loses;
+
+ /* Compute offset as multiple of this unit,
+ counting in bytes. */
+ unit = GET_MODE_BITSIZE (bestmode);
+ xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode);
+ xbitpos = bitnum % unit;
+ xop0 = change_address (xop0, bestmode,
+ plus_constant (XEXP (xop0, 0),
+ xoffset));
+ /* Fetch it to a register in that size. */
+ xop0 = force_reg (bestmode, xop0);
+
+ /* XBITPOS counts within UNIT, which is what is expected. */
+ }
+ else
+ /* Get ref to first byte containing part of the field. */
+ xop0 = change_address (xop0, byte_mode,
+ plus_constant (XEXP (xop0, 0), xoffset));
+
+ volatile_ok = save_volatile_ok;
+ }
+
+ /* If op0 is a register, we need it in MAXMODE (which is usually
+ SImode). to make it acceptable to the format of extzv. */
+ if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != maxmode)
+ abort ();
+ if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode)
+ xop0 = gen_rtx (SUBREG, maxmode, xop0, 0);
+
+ /* On big-endian machines, we count bits from the most significant.
+ If the bit field insn does not, we must invert. */
+#if BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN
+ xbitpos = unit - bitsize - xbitpos;
+#endif
+ /* Now convert from counting within UNIT to counting in MAXMODE. */
+#if BITS_BIG_ENDIAN
+ if (GET_CODE (xop0) != MEM)
+ xbitpos += GET_MODE_BITSIZE (maxmode) - unit;
+#endif
+ unit = GET_MODE_BITSIZE (maxmode);
+
+ if (xtarget == 0
+ || (flag_force_mem && GET_CODE (xtarget) == MEM))
+ xtarget = xspec_target = gen_reg_rtx (tmode);
+
+ if (GET_MODE (xtarget) != maxmode)
+ {
+ if (GET_CODE (xtarget) == REG)
+ {
+ int wider = (GET_MODE_SIZE (maxmode)
+ > GET_MODE_SIZE (GET_MODE (xtarget)));
+ xtarget = gen_lowpart (maxmode, xtarget);
+ if (wider)
+ xspec_target_subreg = xtarget;
+ }
+ else
+ xtarget = gen_reg_rtx (maxmode);
+ }
+
+ /* If this machine's extzv insists on a register target,
+ make sure we have one. */
+ if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][0])
+ (xtarget, maxmode)))
+ xtarget = gen_reg_rtx (maxmode);
+
+ bitsize_rtx = GEN_INT (bitsize);
+ bitpos_rtx = GEN_INT (xbitpos);
+
+ pat = gen_extzv (protect_from_queue (xtarget, 1),
+ xop0, bitsize_rtx, bitpos_rtx);
+ if (pat)
+ {
+ emit_insn (pat);
+ target = xtarget;
+ spec_target = xspec_target;
+ spec_target_subreg = xspec_target_subreg;
+ }
+ else
+ {
+ delete_insns_since (last);
+ target = extract_fixed_bit_field (tmode, op0, offset, bitsize,
+ bitpos, target, 1, align);
+ }
+ }
+ else
+ extzv_loses:
+#endif
+ target = extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
+ target, 1, align);
+ }
+ else
+ {
+#ifdef HAVE_extv
+ if (HAVE_extv
+ && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extv][0])
+ >= bitsize))
+ {
+ int xbitpos = bitpos, xoffset = offset;
+ rtx bitsize_rtx, bitpos_rtx;
+ rtx last = get_last_insn();
+ rtx xop0 = op0, xtarget = target;
+ rtx xspec_target = spec_target;
+ rtx xspec_target_subreg = spec_target_subreg;
+ rtx pat;
+ enum machine_mode maxmode
+ = insn_operand_mode[(int) CODE_FOR_extv][0];
+
+ if (GET_CODE (xop0) == MEM)
+ {
+ /* Is the memory operand acceptable? */
+ if (! ((*insn_operand_predicate[(int) CODE_FOR_extv][1])
+ (xop0, GET_MODE (xop0))))
+ {
+ /* No, load into a reg and extract from there. */
+ enum machine_mode bestmode;
+
+ /* Get the mode to use for inserting into this field. If
+ OP0 is BLKmode, get the smallest mode consistent with the
+ alignment. If OP0 is a non-BLKmode object that is no
+ wider than MAXMODE, use its mode. Otherwise, use the
+ smallest mode containing the field. */
+
+ if (GET_MODE (xop0) == BLKmode
+ || (GET_MODE_SIZE (GET_MODE (op0))
+ > GET_MODE_SIZE (maxmode)))
+ bestmode = get_best_mode (bitsize, bitnum,
+ align * BITS_PER_UNIT, maxmode,
+ MEM_VOLATILE_P (xop0));
+ else
+ bestmode = GET_MODE (xop0);
+
+ if (bestmode == VOIDmode
+ || (STRICT_ALIGNMENT && GET_MODE_SIZE (bestmode) > align))
+ goto extv_loses;
+
+ /* Compute offset as multiple of this unit,
+ counting in bytes. */
+ unit = GET_MODE_BITSIZE (bestmode);
+ xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode);
+ xbitpos = bitnum % unit;
+ xop0 = change_address (xop0, bestmode,
+ plus_constant (XEXP (xop0, 0),
+ xoffset));
+ /* Fetch it to a register in that size. */
+ xop0 = force_reg (bestmode, xop0);
+
+ /* XBITPOS counts within UNIT, which is what is expected. */
+ }
+ else
+ /* Get ref to first byte containing part of the field. */
+ xop0 = change_address (xop0, byte_mode,
+ plus_constant (XEXP (xop0, 0), xoffset));
+ }
+
+ /* If op0 is a register, we need it in MAXMODE (which is usually
+ SImode) to make it acceptable to the format of extv. */
+ if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != maxmode)
+ abort ();
+ if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode)
+ xop0 = gen_rtx (SUBREG, maxmode, xop0, 0);
+
+ /* On big-endian machines, we count bits from the most significant.
+ If the bit field insn does not, we must invert. */
+#if BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN
+ xbitpos = unit - bitsize - xbitpos;
+#endif
+ /* XBITPOS counts within a size of UNIT.
+ Adjust to count within a size of MAXMODE. */
+#if BITS_BIG_ENDIAN
+ if (GET_CODE (xop0) != MEM)
+ xbitpos += (GET_MODE_BITSIZE (maxmode) - unit);
+#endif
+ unit = GET_MODE_BITSIZE (maxmode);
+
+ if (xtarget == 0
+ || (flag_force_mem && GET_CODE (xtarget) == MEM))
+ xtarget = xspec_target = gen_reg_rtx (tmode);
+
+ if (GET_MODE (xtarget) != maxmode)
+ {
+ if (GET_CODE (xtarget) == REG)
+ {
+ int wider = (GET_MODE_SIZE (maxmode)
+ > GET_MODE_SIZE (GET_MODE (xtarget)));
+ xtarget = gen_lowpart (maxmode, xtarget);
+ if (wider)
+ xspec_target_subreg = xtarget;
+ }
+ else
+ xtarget = gen_reg_rtx (maxmode);
+ }
+
+ /* If this machine's extv insists on a register target,
+ make sure we have one. */
+ if (! ((*insn_operand_predicate[(int) CODE_FOR_extv][0])
+ (xtarget, maxmode)))
+ xtarget = gen_reg_rtx (maxmode);
+
+ bitsize_rtx = GEN_INT (bitsize);
+ bitpos_rtx = GEN_INT (xbitpos);
+
+ pat = gen_extv (protect_from_queue (xtarget, 1),
+ xop0, bitsize_rtx, bitpos_rtx);
+ if (pat)
+ {
+ emit_insn (pat);
+ target = xtarget;
+ spec_target = xspec_target;
+ spec_target_subreg = xspec_target_subreg;
+ }
+ else
+ {
+ delete_insns_since (last);
+ target = extract_fixed_bit_field (tmode, op0, offset, bitsize,
+ bitpos, target, 0, align);
+ }
+ }
+ else
+ extv_loses:
+#endif
+ target = extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
+ target, 0, align);
+ }
+ if (target == spec_target)
+ return target;
+ if (target == spec_target_subreg)
+ return spec_target;
+ if (GET_MODE (target) != tmode && GET_MODE (target) != mode)
+ {
+ /* If the target mode is floating-point, first convert to the
+ integer mode of that size and then access it as a floating-point
+ value via a SUBREG. */
+ if (GET_MODE_CLASS (tmode) == MODE_FLOAT)
+ {
+ target = convert_to_mode (mode_for_size (GET_MODE_BITSIZE (tmode),
+ MODE_INT, 0),
+ target, unsignedp);
+ if (GET_CODE (target) != REG)
+ target = copy_to_reg (target);
+ return gen_rtx (SUBREG, tmode, target, 0);
+ }
+ else
+ return convert_to_mode (tmode, target, unsignedp);
+ }
+ return target;
+}
+
+/* Extract a bit field using shifts and boolean operations
+ Returns an rtx to represent the value.
+ OP0 addresses a register (word) or memory (byte).
+ BITPOS says which bit within the word or byte the bit field starts in.
+ OFFSET says how many bytes farther the bit field starts;
+ it is 0 if OP0 is a register.
+ BITSIZE says how many bits long the bit field is.
+ (If OP0 is a register, it may be narrower than a full word,
+ but BITPOS still counts within a full word,
+ which is significant on bigendian machines.)
+
+ UNSIGNEDP is nonzero for an unsigned bit field (don't sign-extend value).
+ If TARGET is nonzero, attempts to store the value there
+ and return TARGET, but this is not guaranteed.
+ If TARGET is not used, create a pseudo-reg of mode TMODE for the value.
+
+ ALIGN is the alignment that STR_RTX is known to have, measured in bytes. */
+
+static rtx
+extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
+ target, unsignedp, align)
+ enum machine_mode tmode;
+ register rtx op0, target;
+ register int offset, bitsize, bitpos;
+ int unsignedp;
+ int align;
+{
+ int total_bits = BITS_PER_WORD;
+ enum machine_mode mode;
+
+ if (GET_CODE (op0) == SUBREG || GET_CODE (op0) == REG)
+ {
+ /* Special treatment for a bit field split across two registers. */
+ if (bitsize + bitpos > BITS_PER_WORD)
+ return extract_split_bit_field (op0, bitsize, bitpos,
+ unsignedp, align);
+ }
+ else
+ {
+ /* Get the proper mode to use for this field. We want a mode that
+ includes the entire field. If such a mode would be larger than
+ a word, we won't be doing the extraction the normal way. */
+
+ mode = get_best_mode (bitsize, bitpos + offset * BITS_PER_UNIT,
+ align * BITS_PER_UNIT, word_mode,
+ GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0));
+
+ if (mode == VOIDmode)
+ /* The only way this should occur is if the field spans word
+ boundaries. */
+ return extract_split_bit_field (op0, bitsize,
+ bitpos + offset * BITS_PER_UNIT,
+ unsignedp, align);
+
+ total_bits = GET_MODE_BITSIZE (mode);
+
+ /* Make sure bitpos is valid for the chosen mode. Adjust BITPOS to
+ be be in the range 0 to total_bits-1, and put any excess bytes in
+ OFFSET. */
+ if (bitpos >= total_bits)
+ {
+ offset += (bitpos / total_bits) * (total_bits / BITS_PER_UNIT);
+ bitpos -= ((bitpos / total_bits) * (total_bits / BITS_PER_UNIT)
+ * BITS_PER_UNIT);
+ }
+
+ /* Get ref to an aligned byte, halfword, or word containing the field.
+ Adjust BITPOS to be position within a word,
+ and OFFSET to be the offset of that word.
+ Then alter OP0 to refer to that word. */
+ bitpos += (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT;
+ offset -= (offset % (total_bits / BITS_PER_UNIT));
+ op0 = change_address (op0, mode,
+ plus_constant (XEXP (op0, 0), offset));
+ }
+
+ mode = GET_MODE (op0);
+
+#if BYTES_BIG_ENDIAN
+ /* BITPOS is the distance between our msb and that of OP0.
+ Convert it to the distance from the lsb. */
+
+ bitpos = total_bits - bitsize - bitpos;
+#endif
+ /* Now BITPOS is always the distance between the field's lsb and that of OP0.
+ We have reduced the big-endian case to the little-endian case. */
+
+ if (unsignedp)
+ {
+ if (bitpos)
+ {
+ /* If the field does not already start at the lsb,
+ shift it so it does. */
+ tree amount = build_int_2 (bitpos, 0);
+ /* Maybe propagate the target for the shift. */
+ /* But not if we will return it--could confuse integrate.c. */
+ rtx subtarget = (target != 0 && GET_CODE (target) == REG
+ && !REG_FUNCTION_VALUE_P (target)
+ ? target : 0);
+ if (tmode != mode) subtarget = 0;
+ op0 = expand_shift (RSHIFT_EXPR, mode, op0, amount, subtarget, 1);
+ }
+ /* Convert the value to the desired mode. */
+ if (mode != tmode)
+ op0 = convert_to_mode (tmode, op0, 1);
+
+ /* Unless the msb of the field used to be the msb when we shifted,
+ mask out the upper bits. */
+
+ if (GET_MODE_BITSIZE (mode) != bitpos + bitsize
+#if 0
+#ifdef SLOW_ZERO_EXTEND
+ /* Always generate an `and' if
+ we just zero-extended op0 and SLOW_ZERO_EXTEND, since it
+ will combine fruitfully with the zero-extend. */
+ || tmode != mode
+#endif
+#endif
+ )
+ return expand_binop (GET_MODE (op0), and_optab, op0,
+ mask_rtx (GET_MODE (op0), 0, bitsize, 0),
+ target, 1, OPTAB_LIB_WIDEN);
+ return op0;
+ }
+
+ /* To extract a signed bit-field, first shift its msb to the msb of the word,
+ then arithmetic-shift its lsb to the lsb of the word. */
+ op0 = force_reg (mode, op0);
+ if (mode != tmode)
+ target = 0;
+
+ /* Find the narrowest integer mode that contains the field. */
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ if (GET_MODE_BITSIZE (mode) >= bitsize + bitpos)
+ {
+ op0 = convert_to_mode (mode, op0, 0);
+ break;
+ }
+
+ if (GET_MODE_BITSIZE (mode) != (bitsize + bitpos))
+ {
+ tree amount = build_int_2 (GET_MODE_BITSIZE (mode) - (bitsize + bitpos), 0);
+ /* Maybe propagate the target for the shift. */
+ /* But not if we will return the result--could confuse integrate.c. */
+ rtx subtarget = (target != 0 && GET_CODE (target) == REG
+ && ! REG_FUNCTION_VALUE_P (target)
+ ? target : 0);
+ op0 = expand_shift (LSHIFT_EXPR, mode, op0, amount, subtarget, 1);
+ }
+
+ return expand_shift (RSHIFT_EXPR, mode, op0,
+ build_int_2 (GET_MODE_BITSIZE (mode) - bitsize, 0),
+ target, 0);
+}
+
+/* Return a constant integer (CONST_INT or CONST_DOUBLE) mask value
+ of mode MODE with BITSIZE ones followed by BITPOS zeros, or the
+ complement of that if COMPLEMENT. The mask is truncated if
+ necessary to the width of mode MODE. */
+
+static rtx
+mask_rtx (mode, bitpos, bitsize, complement)
+ enum machine_mode mode;
+ int bitpos, bitsize, complement;
+{
+ HOST_WIDE_INT masklow, maskhigh;
+
+ if (bitpos < HOST_BITS_PER_WIDE_INT)
+ masklow = (HOST_WIDE_INT) -1 << bitpos;
+ else
+ masklow = 0;
+
+ if (bitpos + bitsize < HOST_BITS_PER_WIDE_INT)
+ masklow &= ((unsigned HOST_WIDE_INT) -1
+ >> (HOST_BITS_PER_WIDE_INT - bitpos - bitsize));
+
+ if (bitpos <= HOST_BITS_PER_WIDE_INT)
+ maskhigh = -1;
+ else
+ maskhigh = (HOST_WIDE_INT) -1 << (bitpos - HOST_BITS_PER_WIDE_INT);
+
+ if (bitpos + bitsize > HOST_BITS_PER_WIDE_INT)
+ maskhigh &= ((unsigned HOST_WIDE_INT) -1
+ >> (2 * HOST_BITS_PER_WIDE_INT - bitpos - bitsize));
+ else
+ maskhigh = 0;
+
+ if (complement)
+ {
+ maskhigh = ~maskhigh;
+ masklow = ~masklow;
+ }
+
+ return immed_double_const (masklow, maskhigh, mode);
+}
+
+/* Return a constant integer (CONST_INT or CONST_DOUBLE) rtx with the value
+ VALUE truncated to BITSIZE bits and then shifted left BITPOS bits. */
+
+static rtx
+lshift_value (mode, value, bitpos, bitsize)
+ enum machine_mode mode;
+ rtx value;
+ int bitpos, bitsize;
+{
+ unsigned HOST_WIDE_INT v = INTVAL (value);
+ HOST_WIDE_INT low, high;
+
+ if (bitsize < HOST_BITS_PER_WIDE_INT)
+ v &= ~((HOST_WIDE_INT) -1 << bitsize);
+
+ if (bitpos < HOST_BITS_PER_WIDE_INT)
+ {
+ low = v << bitpos;
+ high = (bitpos > 0 ? (v >> (HOST_BITS_PER_WIDE_INT - bitpos)) : 0);
+ }
+ else
+ {
+ low = 0;
+ high = v << (bitpos - HOST_BITS_PER_WIDE_INT);
+ }
+
+ return immed_double_const (low, high, mode);
+}
+
+/* Extract a bit field that is split across two words
+ and return an RTX for the result.
+
+ OP0 is the REG, SUBREG or MEM rtx for the first of the two words.
+ BITSIZE is the field width; BITPOS, position of its first bit, in the word.
+ UNSIGNEDP is 1 if should zero-extend the contents; else sign-extend.
+
+ ALIGN is the known alignment of OP0, measured in bytes.
+ This is also the size of the memory objects to be used. */
+
+static rtx
+extract_split_bit_field (op0, bitsize, bitpos, unsignedp, align)
+ rtx op0;
+ int bitsize, bitpos, unsignedp, align;
+{
+ int unit;
+ int bitsdone = 0;
+ rtx result;
+ int first = 1;
+
+ /* Make sure UNIT isn't larger than BITS_PER_WORD, we can only handle that
+ much at a time. */
+ if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
+ unit = BITS_PER_WORD;
+ else
+ unit = MIN (align * BITS_PER_UNIT, BITS_PER_WORD);
+
+ while (bitsdone < bitsize)
+ {
+ int thissize;
+ rtx part, word;
+ int thispos;
+ int offset;
+
+ offset = (bitpos + bitsdone) / unit;
+ thispos = (bitpos + bitsdone) % unit;
+
+ /* THISSIZE must not overrun a word boundary. Otherwise,
+ extract_fixed_bit_field will call us again, and we will mutually
+ recurse forever. */
+ thissize = MIN (bitsize - bitsdone, BITS_PER_WORD);
+ thissize = MIN (thissize, unit - thispos);
+
+ /* If OP0 is a register, then handle OFFSET here.
+
+ When handling multiword bitfields, extract_bit_field may pass
+ down a word_mode SUBREG of a larger REG for a bitfield that actually
+ crosses a word boundary. Thus, for a SUBREG, we must find
+ the current word starting from the base register. */
+ if (GET_CODE (op0) == SUBREG)
+ {
+ word = operand_subword_force (SUBREG_REG (op0),
+ SUBREG_WORD (op0) + offset,
+ GET_MODE (SUBREG_REG (op0)));
+ offset = 0;
+ }
+ else if (GET_CODE (op0) == REG)
+ {
+ word = operand_subword_force (op0, offset, GET_MODE (op0));
+ offset = 0;
+ }
+ else
+ word = op0;
+
+ /* Extract the parts in bit-counting order,
+ whose meaning is determined by BYTES_PER_UNIT.
+ OFFSET is in UNITs, and UNIT is in bits.
+ extract_fixed_bit_field wants offset in bytes. */
+ part = extract_fixed_bit_field (word_mode, word,
+ offset * unit / BITS_PER_UNIT,
+ thissize, thispos, 0, 1, align);
+ bitsdone += thissize;
+
+ /* Shift this part into place for the result. */
+#if BYTES_BIG_ENDIAN
+ if (bitsize != bitsdone)
+ part = expand_shift (LSHIFT_EXPR, word_mode, part,
+ build_int_2 (bitsize - bitsdone, 0), 0, 1);
+#else
+ if (bitsdone != thissize)
+ part = expand_shift (LSHIFT_EXPR, word_mode, part,
+ build_int_2 (bitsdone - thissize, 0), 0, 1);
+#endif
+
+ if (first)
+ result = part;
+ else
+ /* Combine the parts with bitwise or. This works
+ because we extracted each part as an unsigned bit field. */
+ result = expand_binop (word_mode, ior_optab, part, result, NULL_RTX, 1,
+ OPTAB_LIB_WIDEN);
+
+ first = 0;
+ }
+
+ /* Unsigned bit field: we are done. */
+ if (unsignedp)
+ return result;
+ /* Signed bit field: sign-extend with two arithmetic shifts. */
+ result = expand_shift (LSHIFT_EXPR, word_mode, result,
+ build_int_2 (BITS_PER_WORD - bitsize, 0),
+ NULL_RTX, 0);
+ return expand_shift (RSHIFT_EXPR, word_mode, result,
+ build_int_2 (BITS_PER_WORD - bitsize, 0), NULL_RTX, 0);
+}
+
+/* Add INC into TARGET. */
+
+void
+expand_inc (target, inc)
+ rtx target, inc;
+{
+ rtx value = expand_binop (GET_MODE (target), add_optab,
+ target, inc,
+ target, 0, OPTAB_LIB_WIDEN);
+ if (value != target)
+ emit_move_insn (target, value);
+}
+
+/* Subtract DEC from TARGET. */
+
+void
+expand_dec (target, dec)
+ rtx target, dec;
+{
+ rtx value = expand_binop (GET_MODE (target), sub_optab,
+ target, dec,
+ target, 0, OPTAB_LIB_WIDEN);
+ if (value != target)
+ emit_move_insn (target, value);
+}
+
+/* Output a shift instruction for expression code CODE,
+ with SHIFTED being the rtx for the value to shift,
+ and AMOUNT the tree for the amount to shift by.
+ Store the result in the rtx TARGET, if that is convenient.
+ If UNSIGNEDP is nonzero, do a logical shift; otherwise, arithmetic.
+ Return the rtx for where the value is. */
+
+rtx
+expand_shift (code, mode, shifted, amount, target, unsignedp)
+ enum tree_code code;
+ register enum machine_mode mode;
+ rtx shifted;
+ tree amount;
+ register rtx target;
+ int unsignedp;
+{
+ register rtx op1, temp = 0;
+ register int left = (code == LSHIFT_EXPR || code == LROTATE_EXPR);
+ register int rotate = (code == LROTATE_EXPR || code == RROTATE_EXPR);
+ int try;
+
+ /* Previously detected shift-counts computed by NEGATE_EXPR
+ and shifted in the other direction; but that does not work
+ on all machines. */
+
+ op1 = expand_expr (amount, NULL_RTX, VOIDmode, 0);
+
+#if 0 && SHIFT_COUNT_TRUNCATED
+ if (SHIFT_COUNT_TRUNCATED
+ && GET_CODE (op1) == CONST_INT
+ && (unsigned HOST_WIDE_INT) INTVAL (op1) >= GET_MODE_BITSIZE (mode))
+ op1 = GEN_INT ((unsigned HOST_WIDE_INT) INTVAL (op1)
+ % GET_MODE_BITSIZE (mode));
+#endif
+
+ if (op1 == const0_rtx)
+ return shifted;
+
+ for (try = 0; temp == 0 && try < 3; try++)
+ {
+ enum optab_methods methods;
+
+ if (try == 0)
+ methods = OPTAB_DIRECT;
+ else if (try == 1)
+ methods = OPTAB_WIDEN;
+ else
+ methods = OPTAB_LIB_WIDEN;
+
+ if (rotate)
+ {
+ /* Widening does not work for rotation. */
+ if (methods == OPTAB_WIDEN)
+ continue;
+ else if (methods == OPTAB_LIB_WIDEN)
+ {
+ /* If we are rotating by a constant that is valid and
+ we have been unable to open-code this by a rotation,
+ do it as the IOR of two shifts. I.e., to rotate A
+ by N bits, compute (A << N) | ((unsigned) A >> (C - N))
+ where C is the bitsize of A.
+
+ It is theoretically possible that the target machine might
+ not be able to perform either shift and hence we would
+ be making two libcalls rather than just the one for the
+ shift (similarly if IOR could not be done). We will allow
+ this extremely unlikely lossage to avoid complicating the
+ code below. */
+
+ if (GET_CODE (op1) == CONST_INT && INTVAL (op1) > 0
+ && INTVAL (op1) < GET_MODE_BITSIZE (mode))
+ {
+ rtx subtarget = target == shifted ? 0 : target;
+ rtx temp1;
+ tree other_amount
+ = build_int_2 (GET_MODE_BITSIZE (mode) - INTVAL (op1), 0);
+
+ shifted = force_reg (mode, shifted);
+
+ temp = expand_shift (left ? LSHIFT_EXPR : RSHIFT_EXPR,
+ mode, shifted, amount, subtarget, 1);
+ temp1 = expand_shift (left ? RSHIFT_EXPR : LSHIFT_EXPR,
+ mode, shifted, other_amount, 0, 1);
+ return expand_binop (mode, ior_optab, temp, temp1, target,
+ unsignedp, methods);
+ }
+ else
+ methods = OPTAB_LIB;
+ }
+
+ temp = expand_binop (mode,
+ left ? rotl_optab : rotr_optab,
+ shifted, op1, target, unsignedp, methods);
+
+ /* If we don't have the rotate, but we are rotating by a constant
+ that is in range, try a rotate in the opposite direction. */
+
+ if (temp == 0 && GET_CODE (op1) == CONST_INT
+ && INTVAL (op1) > 0 && INTVAL (op1) < GET_MODE_BITSIZE (mode))
+ temp = expand_binop (mode,
+ left ? rotr_optab : rotl_optab,
+ shifted,
+ GEN_INT (GET_MODE_BITSIZE (mode)
+ - INTVAL (op1)),
+ target, unsignedp, methods);
+ }
+ else if (unsignedp)
+ temp = expand_binop (mode,
+ left ? ashl_optab : lshr_optab,
+ shifted, op1, target, unsignedp, methods);
+
+ /* Do arithmetic shifts.
+ Also, if we are going to widen the operand, we can just as well
+ use an arithmetic right-shift instead of a logical one. */
+ if (temp == 0 && ! rotate
+ && (! unsignedp || (! left && methods == OPTAB_WIDEN)))
+ {
+ enum optab_methods methods1 = methods;
+
+ /* If trying to widen a log shift to an arithmetic shift,
+ don't accept an arithmetic shift of the same size. */
+ if (unsignedp)
+ methods1 = OPTAB_MUST_WIDEN;
+
+ /* Arithmetic shift */
+
+ temp = expand_binop (mode,
+ left ? ashl_optab : ashr_optab,
+ shifted, op1, target, unsignedp, methods1);
+ }
+
+ /* We used to try extzv here for logical right shifts, but that was
+ only useful for one machine, the VAX, and caused poor code
+ generation there for lshrdi3, so the code was deleted and a
+ define_expand for lshrsi3 was added to vax.md. */
+ }
+
+ if (temp == 0)
+ abort ();
+ return temp;
+}
+
+enum alg_code { alg_zero, alg_m, alg_shift,
+ alg_add_t_m2, alg_sub_t_m2,
+ alg_add_factor, alg_sub_factor,
+ alg_add_t2_m, alg_sub_t2_m,
+ alg_add, alg_subtract, alg_factor, alg_shiftop };
+
+/* This structure records a sequence of operations.
+ `ops' is the number of operations recorded.
+ `cost' is their total cost.
+ The operations are stored in `op' and the corresponding
+ logarithms of the integer coefficients in `log'.
+
+ These are the operations:
+ alg_zero total := 0;
+ alg_m total := multiplicand;
+ alg_shift total := total * coeff
+ alg_add_t_m2 total := total + multiplicand * coeff;
+ alg_sub_t_m2 total := total - multiplicand * coeff;
+ alg_add_factor total := total * coeff + total;
+ alg_sub_factor total := total * coeff - total;
+ alg_add_t2_m total := total * coeff + multiplicand;
+ alg_sub_t2_m total := total * coeff - multiplicand;
+
+ The first operand must be either alg_zero or alg_m. */
+
+struct algorithm
+{
+ short cost;
+ short ops;
+ /* The size of the OP and LOG fields are not directly related to the
+ word size, but the worst-case algorithms will be if we have few
+ consecutive ones or zeros, i.e., a multiplicand like 10101010101...
+ In that case we will generate shift-by-2, add, shift-by-2, add,...,
+ in total wordsize operations. */
+ enum alg_code op[MAX_BITS_PER_WORD];
+ char log[MAX_BITS_PER_WORD];
+};
+
+/* Compute and return the best algorithm for multiplying by T.
+ The algorithm must cost less than cost_limit
+ If retval.cost >= COST_LIMIT, no algorithm was found and all
+ other field of the returned struct are undefined. */
+
+static void
+synth_mult (alg_out, t, cost_limit)
+ struct algorithm *alg_out;
+ unsigned HOST_WIDE_INT t;
+ int cost_limit;
+{
+ int m;
+ struct algorithm *alg_in, *best_alg;
+ unsigned int cost;
+ unsigned HOST_WIDE_INT q;
+
+ /* Indicate that no algorithm is yet found. If no algorithm
+ is found, this value will be returned and indicate failure. */
+ alg_out->cost = cost_limit;
+
+ if (cost_limit <= 0)
+ return;
+
+ /* t == 1 can be done in zero cost. */
+ if (t == 1)
+ {
+ alg_out->ops = 1;
+ alg_out->cost = 0;
+ alg_out->op[0] = alg_m;
+ return;
+ }
+
+ /* t == 0 sometimes has a cost. If it does and it exceeds our limit,
+ fail now. */
+ if (t == 0)
+ {
+ if (zero_cost >= cost_limit)
+ return;
+ else
+ {
+ alg_out->ops = 1;
+ alg_out->cost = zero_cost;
+ alg_out->op[0] = alg_zero;
+ return;
+ }
+ }
+
+ /* We'll be needing a couple extra algorithm structures now. */
+
+ alg_in = (struct algorithm *)alloca (sizeof (struct algorithm));
+ best_alg = (struct algorithm *)alloca (sizeof (struct algorithm));
+
+ /* If we have a group of zero bits at the low-order part of T, try
+ multiplying by the remaining bits and then doing a shift. */
+
+ if ((t & 1) == 0)
+ {
+ m = floor_log2 (t & -t); /* m = number of low zero bits */
+ q = t >> m;
+ cost = shift_cost[m];
+ synth_mult (alg_in, q, cost_limit - cost);
+
+ cost += alg_in->cost;
+ if (cost < cost_limit)
+ {
+ struct algorithm *x;
+ x = alg_in, alg_in = best_alg, best_alg = x;
+ best_alg->log[best_alg->ops] = m;
+ best_alg->op[best_alg->ops] = alg_shift;
+ cost_limit = cost;
+ }
+ }
+
+ /* If we have an odd number, add or subtract one. */
+ if ((t & 1) != 0)
+ {
+ unsigned HOST_WIDE_INT w;
+
+ for (w = 1; (w & t) != 0; w <<= 1)
+ ;
+ if (w > 2
+ /* Reject the case where t is 3.
+ Thus we prefer addition in that case. */
+ && t != 3)
+ {
+ /* T ends with ...111. Multiply by (T + 1) and subtract 1. */
+
+ cost = add_cost;
+ synth_mult (alg_in, t + 1, cost_limit - cost);
+
+ cost += alg_in->cost;
+ if (cost < cost_limit)
+ {
+ struct algorithm *x;
+ x = alg_in, alg_in = best_alg, best_alg = x;
+ best_alg->log[best_alg->ops] = 0;
+ best_alg->op[best_alg->ops] = alg_sub_t_m2;
+ cost_limit = cost;
+ }
+ }
+ else
+ {
+ /* T ends with ...01 or ...011. Multiply by (T - 1) and add 1. */
+
+ cost = add_cost;
+ synth_mult (alg_in, t - 1, cost_limit - cost);
+
+ cost += alg_in->cost;
+ if (cost < cost_limit)
+ {
+ struct algorithm *x;
+ x = alg_in, alg_in = best_alg, best_alg = x;
+ best_alg->log[best_alg->ops] = 0;
+ best_alg->op[best_alg->ops] = alg_add_t_m2;
+ cost_limit = cost;
+ }
+ }
+ }
+
+ /* Look for factors of t of the form
+ t = q(2**m +- 1), 2 <= m <= floor(log2(t - 1)).
+ If we find such a factor, we can multiply by t using an algorithm that
+ multiplies by q, shift the result by m and add/subtract it to itself.
+
+ We search for large factors first and loop down, even if large factors
+ are less probable than small; if we find a large factor we will find a
+ good sequence quickly, and therefore be able to prune (by decreasing
+ COST_LIMIT) the search. */
+
+ for (m = floor_log2 (t - 1); m >= 2; m--)
+ {
+ unsigned HOST_WIDE_INT d;
+
+ d = ((unsigned HOST_WIDE_INT) 1 << m) + 1;
+ if (t % d == 0 && t > d)
+ {
+ cost = MIN (shiftadd_cost[m], add_cost + shift_cost[m]);
+ synth_mult (alg_in, t / d, cost_limit - cost);
+
+ cost += alg_in->cost;
+ if (cost < cost_limit)
+ {
+ struct algorithm *x;
+ x = alg_in, alg_in = best_alg, best_alg = x;
+ best_alg->log[best_alg->ops] = m;
+ best_alg->op[best_alg->ops] = alg_add_factor;
+ cost_limit = cost;
+ }
+ /* Other factors will have been taken care of in the recursion. */
+ break;
+ }
+
+ d = ((unsigned HOST_WIDE_INT) 1 << m) - 1;
+ if (t % d == 0 && t > d)
+ {
+ cost = MIN (shiftsub_cost[m], add_cost + shift_cost[m]);
+ synth_mult (alg_in, t / d, cost_limit - cost);
+
+ cost += alg_in->cost;
+ if (cost < cost_limit)
+ {
+ struct algorithm *x;
+ x = alg_in, alg_in = best_alg, best_alg = x;
+ best_alg->log[best_alg->ops] = m;
+ best_alg->op[best_alg->ops] = alg_sub_factor;
+ cost_limit = cost;
+ }
+ break;
+ }
+ }
+
+ /* Try shift-and-add (load effective address) instructions,
+ i.e. do a*3, a*5, a*9. */
+ if ((t & 1) != 0)
+ {
+ q = t - 1;
+ q = q & -q;
+ m = exact_log2 (q);
+ if (m >= 0)
+ {
+ cost = shiftadd_cost[m];
+ synth_mult (alg_in, (t - 1) >> m, cost_limit - cost);
+
+ cost += alg_in->cost;
+ if (cost < cost_limit)
+ {
+ struct algorithm *x;
+ x = alg_in, alg_in = best_alg, best_alg = x;
+ best_alg->log[best_alg->ops] = m;
+ best_alg->op[best_alg->ops] = alg_add_t2_m;
+ cost_limit = cost;
+ }
+ }
+
+ q = t + 1;
+ q = q & -q;
+ m = exact_log2 (q);
+ if (m >= 0)
+ {
+ cost = shiftsub_cost[m];
+ synth_mult (alg_in, (t + 1) >> m, cost_limit - cost);
+
+ cost += alg_in->cost;
+ if (cost < cost_limit)
+ {
+ struct algorithm *x;
+ x = alg_in, alg_in = best_alg, best_alg = x;
+ best_alg->log[best_alg->ops] = m;
+ best_alg->op[best_alg->ops] = alg_sub_t2_m;
+ cost_limit = cost;
+ }
+ }
+ }
+
+ /* If cost_limit has not decreased since we stored it in alg_out->cost,
+ we have not found any algorithm. */
+ if (cost_limit == alg_out->cost)
+ return;
+
+ /* If we are getting a too long sequence for `struct algorithm'
+ to record, make this search fail. */
+ if (best_alg->ops == MAX_BITS_PER_WORD)
+ return;
+
+ /* Copy the algorithm from temporary space to the space at alg_out.
+ We avoid using structure assignment because the majority of
+ best_alg is normally undefined, and this is a critical function. */
+ alg_out->ops = best_alg->ops + 1;
+ alg_out->cost = cost_limit;
+ bcopy ((char *) best_alg->op, (char *) alg_out->op,
+ alg_out->ops * sizeof *alg_out->op);
+ bcopy ((char *) best_alg->log, (char *) alg_out->log,
+ alg_out->ops * sizeof *alg_out->log);
+}
+
+/* Perform a multiplication and return an rtx for the result.
+ MODE is mode of value; OP0 and OP1 are what to multiply (rtx's);
+ TARGET is a suggestion for where to store the result (an rtx).
+
+ We check specially for a constant integer as OP1.
+ If you want this check for OP0 as well, then before calling
+ you should swap the two operands if OP0 would be constant. */
+
+rtx
+expand_mult (mode, op0, op1, target, unsignedp)
+ enum machine_mode mode;
+ register rtx op0, op1, target;
+ int unsignedp;
+{
+ rtx const_op1 = op1;
+
+ /* If we are multiplying in DImode, it may still be a win
+ to try to work with shifts and adds. */
+ if (GET_CODE (op1) == CONST_DOUBLE
+ && GET_MODE_CLASS (GET_MODE (op1)) == MODE_INT
+ && HOST_BITS_PER_INT <= BITS_PER_WORD)
+ {
+ if ((CONST_DOUBLE_HIGH (op1) == 0 && CONST_DOUBLE_LOW (op1) >= 0)
+ || (CONST_DOUBLE_HIGH (op1) == -1 && CONST_DOUBLE_LOW (op1) < 0))
+ const_op1 = GEN_INT (CONST_DOUBLE_LOW (op1));
+ }
+
+ /* We used to test optimize here, on the grounds that it's better to
+ produce a smaller program when -O is not used.
+ But this causes such a terrible slowdown sometimes
+ that it seems better to use synth_mult always. */
+
+ if (GET_CODE (const_op1) == CONST_INT)
+ {
+ struct algorithm alg;
+ struct algorithm alg2;
+ HOST_WIDE_INT val = INTVAL (op1);
+ HOST_WIDE_INT val_so_far;
+ rtx insn;
+ int mult_cost;
+ enum {basic_variant, negate_variant, add_variant} variant = basic_variant;
+
+ /* Try to do the computation three ways: multiply by the negative of OP1
+ and then negate, do the multiplication directly, or do multiplication
+ by OP1 - 1. */
+
+ mult_cost = rtx_cost (gen_rtx (MULT, mode, op0, op1), SET);
+ mult_cost = MIN (12 * add_cost, mult_cost);
+
+ synth_mult (&alg, val, mult_cost);
+ synth_mult (&alg2, - val,
+ (alg.cost < mult_cost ? alg.cost : mult_cost) - negate_cost);
+ if (alg2.cost + negate_cost < alg.cost)
+ alg = alg2, variant = negate_variant;
+
+ /* This proves very useful for division-by-constant. */
+ synth_mult (&alg2, val - 1, (alg.cost < mult_cost ? alg.cost : mult_cost) - add_cost);
+ if (alg2.cost + add_cost < alg.cost)
+ alg = alg2, variant = add_variant;
+
+ if (alg.cost < mult_cost)
+ {
+ /* We found something cheaper than a multiply insn. */
+ int opno;
+ rtx accum, tem;
+
+ op0 = protect_from_queue (op0, 0);
+
+ /* Avoid referencing memory over and over.
+ For speed, but also for correctness when mem is volatile. */
+ if (GET_CODE (op0) == MEM)
+ op0 = force_reg (mode, op0);
+
+ /* ACCUM starts out either as OP0 or as a zero, depending on
+ the first operation. */
+
+ if (alg.op[0] == alg_zero)
+ {
+ accum = copy_to_mode_reg (mode, const0_rtx);
+ val_so_far = 0;
+ }
+ else if (alg.op[0] == alg_m)
+ {
+ accum = copy_to_mode_reg (mode, op0);
+ val_so_far = 1;
+ }
+ else
+ abort ();
+
+ for (opno = 1; opno < alg.ops; opno++)
+ {
+ int log = alg.log[opno];
+ int preserve = preserve_subexpressions_p ();
+ rtx shift_subtarget = preserve ? 0 : accum;
+ rtx add_target = opno == alg.ops - 1 && target != 0 ? target : 0;
+ rtx accum_target = preserve ? 0 : accum;
+
+ switch (alg.op[opno])
+ {
+ case alg_shift:
+ accum = expand_shift (LSHIFT_EXPR, mode, accum,
+ build_int_2 (log, 0), NULL_RTX, 0);
+ val_so_far <<= log;
+ break;
+
+ case alg_add_t_m2:
+ tem = expand_shift (LSHIFT_EXPR, mode, op0,
+ build_int_2 (log, 0), NULL_RTX, 0);
+ accum = force_operand (gen_rtx (PLUS, mode, accum, tem),
+ add_target ? add_target : accum_target);
+ val_so_far += (HOST_WIDE_INT) 1 << log;
+ break;
+
+ case alg_sub_t_m2:
+ tem = expand_shift (LSHIFT_EXPR, mode, op0,
+ build_int_2 (log, 0), NULL_RTX, 0);
+ accum = force_operand (gen_rtx (MINUS, mode, accum, tem),
+ add_target ? add_target : accum_target);
+ val_so_far -= (HOST_WIDE_INT) 1 << log;
+ break;
+
+ case alg_add_t2_m:
+ accum = expand_shift (LSHIFT_EXPR, mode, accum,
+ build_int_2 (log, 0), shift_subtarget,
+ 0);
+ accum = force_operand (gen_rtx (PLUS, mode, accum, op0),
+ add_target ? add_target : accum_target);
+ val_so_far = (val_so_far << log) + 1;
+ break;
+
+ case alg_sub_t2_m:
+ accum = expand_shift (LSHIFT_EXPR, mode, accum,
+ build_int_2 (log, 0), shift_subtarget,
+ 0);
+ accum = force_operand (gen_rtx (MINUS, mode, accum, op0),
+ add_target ? add_target : accum_target);
+ val_so_far = (val_so_far << log) - 1;
+ break;
+
+ case alg_add_factor:
+ tem = expand_shift (LSHIFT_EXPR, mode, accum,
+ build_int_2 (log, 0), NULL_RTX, 0);
+ accum = force_operand (gen_rtx (PLUS, mode, accum, tem),
+ add_target ? add_target : accum_target);
+ val_so_far += val_so_far << log;
+ break;
+
+ case alg_sub_factor:
+ tem = expand_shift (LSHIFT_EXPR, mode, accum,
+ build_int_2 (log, 0), NULL_RTX, 0);
+ accum = force_operand (gen_rtx (MINUS, mode, tem, accum),
+ (add_target ? add_target
+ : preserve ? 0 : tem));
+ val_so_far = (val_so_far << log) - val_so_far;
+ break;
+
+ default:
+ abort ();;
+ }
+
+ /* Write a REG_EQUAL note on the last insn so that we can cse
+ multiplication sequences. */
+
+ insn = get_last_insn ();
+ REG_NOTES (insn)
+ = gen_rtx (EXPR_LIST, REG_EQUAL,
+ gen_rtx (MULT, mode, op0, GEN_INT (val_so_far)),
+ REG_NOTES (insn));
+ }
+
+ if (variant == negate_variant)
+ {
+ val_so_far = - val_so_far;
+ accum = expand_unop (mode, neg_optab, accum, target, 0);
+ }
+ else if (variant == add_variant)
+ {
+ val_so_far = val_so_far + 1;
+ accum = force_operand (gen_rtx (PLUS, mode, accum, op0), target);
+ }
+
+ if (val != val_so_far)
+ abort ();
+
+ return accum;
+ }
+ }
+
+ /* This used to use umul_optab if unsigned, but for non-widening multiply
+ there is no difference between signed and unsigned. */
+ op0 = expand_binop (mode, smul_optab,
+ op0, op1, target, unsignedp, OPTAB_LIB_WIDEN);
+ if (op0 == 0)
+ abort ();
+ return op0;
+}
+
+/* Return the smallest n such that 2**n >= X. */
+
+int
+ceil_log2 (x)
+ unsigned HOST_WIDE_INT x;
+{
+ return floor_log2 (x - 1) + 1;
+}
+
+/* Choose a minimal N + 1 bit approximation to 1/D that can be used to
+ replace division by D, and put the least significant N bits of the result
+ in *MULTIPLIER_PTR and return the most significant bit.
+
+ The width of operations is N (should be <= HOST_BITS_PER_WIDE_INT), the
+ needed precision is in PRECISION (should be <= N).
+
+ PRECISION should be as small as possible so this function can choose
+ multiplier more freely.
+
+ The rounded-up logarithm of D is placed in *lgup_ptr. A shift count that
+ is to be used for a final right shift is placed in *POST_SHIFT_PTR.
+
+ Using this function, x/D will be equal to (x * m) >> (*POST_SHIFT_PTR),
+ where m is the full HOST_BITS_PER_WIDE_INT + 1 bit multiplier. */
+
+static
+unsigned HOST_WIDE_INT
+choose_multiplier (d, n, precision, multiplier_ptr, post_shift_ptr, lgup_ptr)
+ unsigned HOST_WIDE_INT d;
+ int n;
+ int precision;
+ unsigned HOST_WIDE_INT *multiplier_ptr;
+ int *post_shift_ptr;
+ int *lgup_ptr;
+{
+ unsigned HOST_WIDE_INT mhigh_hi, mhigh_lo;
+ unsigned HOST_WIDE_INT mlow_hi, mlow_lo;
+ int lgup, post_shift;
+ int pow, pow2;
+ unsigned HOST_WIDE_INT nh, nl, dummy1, dummy2;
+
+ /* lgup = ceil(log2(divisor)); */
+ lgup = ceil_log2 (d);
+
+ if (lgup > n)
+ abort ();
+
+ pow = n + lgup;
+ pow2 = n + lgup - precision;
+
+ if (pow == 2 * HOST_BITS_PER_WIDE_INT)
+ {
+ /* We could handle this with some effort, but this case is much better
+ handled directly with a scc insn, so rely on caller using that. */
+ abort ();
+ }
+
+ /* mlow = 2^(N + lgup)/d */
+ if (pow >= HOST_BITS_PER_WIDE_INT)
+ {
+ nh = (unsigned HOST_WIDE_INT) 1 << (pow - HOST_BITS_PER_WIDE_INT);
+ nl = 0;
+ }
+ else
+ {
+ nh = 0;
+ nl = (unsigned HOST_WIDE_INT) 1 << pow;
+ }
+ div_and_round_double (TRUNC_DIV_EXPR, 1, nl, nh, d, (HOST_WIDE_INT) 0,
+ &mlow_lo, &mlow_hi, &dummy1, &dummy2);
+
+ /* mhigh = (2^(N + lgup) + 2^N + lgup - precision)/d */
+ if (pow2 >= HOST_BITS_PER_WIDE_INT)
+ nh |= (unsigned HOST_WIDE_INT) 1 << (pow2 - HOST_BITS_PER_WIDE_INT);
+ else
+ nl |= (unsigned HOST_WIDE_INT) 1 << pow2;
+ div_and_round_double (TRUNC_DIV_EXPR, 1, nl, nh, d, (HOST_WIDE_INT) 0,
+ &mhigh_lo, &mhigh_hi, &dummy1, &dummy2);
+
+ if (mhigh_hi && nh - d >= d)
+ abort ();
+ if (mhigh_hi > 1 || mlow_hi > 1)
+ abort ();
+ /* assert that mlow < mhigh. */
+ if (! (mlow_hi < mhigh_hi || (mlow_hi == mhigh_hi && mlow_lo < mhigh_lo)))
+ abort();
+
+ /* If precision == N, then mlow, mhigh exceed 2^N
+ (but they do not exceed 2^(N+1)). */
+
+ /* Reduce to lowest terms */
+ for (post_shift = lgup; post_shift > 0; post_shift--)
+ {
+ unsigned HOST_WIDE_INT ml_lo = (mlow_hi << (HOST_BITS_PER_WIDE_INT - 1)) | (mlow_lo >> 1);
+ unsigned HOST_WIDE_INT mh_lo = (mhigh_hi << (HOST_BITS_PER_WIDE_INT - 1)) | (mhigh_lo >> 1);
+ if (ml_lo >= mh_lo)
+ break;
+
+ mlow_hi = 0;
+ mlow_lo = ml_lo;
+ mhigh_hi = 0;
+ mhigh_lo = mh_lo;
+ }
+
+ *post_shift_ptr = post_shift;
+ *lgup_ptr = lgup;
+ if (n < HOST_BITS_PER_WIDE_INT)
+ {
+ unsigned HOST_WIDE_INT mask = ((unsigned HOST_WIDE_INT) 1 << n) - 1;
+ *multiplier_ptr = mhigh_lo & mask;
+ return mhigh_lo >= mask;
+ }
+ else
+ {
+ *multiplier_ptr = mhigh_lo;
+ return mhigh_hi;
+ }
+}
+
+/* Compute the inverse of X mod 2**n, i.e., find Y such that X * Y is
+ congruent to 1 (mod 2**N). */
+
+static unsigned HOST_WIDE_INT
+invert_mod2n (x, n)
+ unsigned HOST_WIDE_INT x;
+ int n;
+{
+ /* Solve x*y == 1 (mod 2^n), where x is odd. Return y. */
+
+ /* The algorithm notes that the choice y = x satisfies
+ x*y == 1 mod 2^3, since x is assumed odd.
+ Each iteration doubles the number of bits of significance in y. */
+
+ unsigned HOST_WIDE_INT mask;
+ unsigned HOST_WIDE_INT y = x;
+ int nbit = 3;
+
+ mask = (n == HOST_BITS_PER_WIDE_INT
+ ? ~(unsigned HOST_WIDE_INT) 0
+ : ((unsigned HOST_WIDE_INT) 1 << n) - 1);
+
+ while (nbit < n)
+ {
+ y = y * (2 - x*y) & mask; /* Modulo 2^N */
+ nbit *= 2;
+ }
+ return y;
+}
+
+/* Emit code to adjust ADJ_OPERAND after multiplication of wrong signedness
+ flavor of OP0 and OP1. ADJ_OPERAND is already the high half of the
+ product OP0 x OP1. If UNSIGNEDP is nonzero, adjust the signed product
+ to become unsigned, if UNSIGNEDP is zero, adjust the unsigned product to
+ become signed.
+
+ The result is put in TARGET if that is convenient.
+
+ MODE is the mode of operation. */
+
+rtx
+expand_mult_highpart_adjust (mode, adj_operand, op0, op1, target, unsignedp)
+ enum machine_mode mode;
+ register rtx adj_operand, op0, op1, target;
+ int unsignedp;
+{
+ rtx tem;
+ enum rtx_code adj_code = unsignedp ? PLUS : MINUS;
+
+ tem = expand_shift (RSHIFT_EXPR, mode, op0,
+ build_int_2 (GET_MODE_BITSIZE (mode) - 1, 0),
+ NULL_RTX, 0);
+ tem = expand_and (tem, op1, NULL_RTX);
+ adj_operand = force_operand (gen_rtx (adj_code, mode, adj_operand, tem),
+ adj_operand);
+
+ tem = expand_shift (RSHIFT_EXPR, mode, op1,
+ build_int_2 (GET_MODE_BITSIZE (mode) - 1, 0),
+ NULL_RTX, 0);
+ tem = expand_and (tem, op0, NULL_RTX);
+ target = force_operand (gen_rtx (adj_code, mode, adj_operand, tem), target);
+
+ return target;
+}
+
+/* Emit code to multiply OP0 and CNST1, putting the high half of the result
+ in TARGET if that is convenient, and return where the result is. If the
+ operation can not be performed, 0 is returned.
+
+ MODE is the mode of operation and result.
+
+ UNSIGNEDP nonzero means unsigned multiply. */
+
+rtx
+expand_mult_highpart (mode, op0, cnst1, target, unsignedp)
+ enum machine_mode mode;
+ register rtx op0, target;
+ unsigned HOST_WIDE_INT cnst1;
+ int unsignedp;
+{
+ enum machine_mode wider_mode = GET_MODE_WIDER_MODE (mode);
+ optab mul_highpart_optab;
+ optab moptab;
+ rtx tem;
+ int size = GET_MODE_BITSIZE (mode);
+ rtx op1, wide_op1;
+
+ /* We can't support modes wider than HOST_BITS_PER_INT. */
+ if (size > HOST_BITS_PER_WIDE_INT)
+ abort ();
+
+ op1 = GEN_INT (cnst1);
+
+ if (GET_MODE_BITSIZE (wider_mode) <= HOST_BITS_PER_INT)
+ wide_op1 = op1;
+ else
+ wide_op1
+ = immed_double_const (cnst1,
+ (unsignedp
+ ? (HOST_WIDE_INT) 0
+ : -(cnst1 >> (HOST_BITS_PER_WIDE_INT - 1))),
+ wider_mode);
+
+ /* expand_mult handles constant multiplication of word_mode
+ or narrower. It does a poor job for large modes. */
+ if (size < BITS_PER_WORD)
+ {
+ /* We have to do this, since expand_binop doesn't do conversion for
+ multiply. Maybe change expand_binop to handle widening multiply? */
+ op0 = convert_to_mode (wider_mode, op0, unsignedp);
+
+ tem = expand_mult (wider_mode, op0, wide_op1, NULL_RTX, unsignedp);
+ tem = expand_shift (RSHIFT_EXPR, wider_mode, tem,
+ build_int_2 (size, 0), NULL_RTX, 1);
+ return gen_lowpart (mode, tem);
+ }
+
+ if (target == 0)
+ target = gen_reg_rtx (mode);
+
+ /* Firstly, try using a multiplication insn that only generates the needed
+ high part of the product, and in the sign flavor of unsignedp. */
+ mul_highpart_optab = unsignedp ? umul_highpart_optab : smul_highpart_optab;
+ target = expand_binop (mode, mul_highpart_optab,
+ op0, op1, target, unsignedp, OPTAB_DIRECT);
+ if (target)
+ return target;
+
+ /* Secondly, same as above, but use sign flavor opposite of unsignedp.
+ Need to adjust the result after the multiplication. */
+ mul_highpart_optab = unsignedp ? smul_highpart_optab : umul_highpart_optab;
+ target = expand_binop (mode, mul_highpart_optab,
+ op0, op1, target, unsignedp, OPTAB_DIRECT);
+ if (target)
+ /* We used the wrong signedness. Adjust the result. */
+ return expand_mult_highpart_adjust (mode, target, op0,
+ op1, target, unsignedp);
+
+ /* Thirdly, we try to use a widening multiplication, or a wider mode
+ multiplication. */
+
+ moptab = unsignedp ? umul_widen_optab : smul_widen_optab;
+ if (moptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing)
+ ;
+ else if (smul_optab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing)
+ moptab = smul_optab;
+ else
+ {
+ /* Try widening multiplication of opposite signedness, and adjust. */
+ moptab = unsignedp ? smul_widen_optab : umul_widen_optab;
+ if (moptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing)
+ {
+ tem = expand_binop (wider_mode, moptab, op0, wide_op1,
+ NULL_RTX, ! unsignedp, OPTAB_WIDEN);
+ if (tem != 0)
+ {
+ /* Extract the high half of the just generated product. */
+ tem = expand_shift (RSHIFT_EXPR, wider_mode, tem,
+ build_int_2 (size, 0), NULL_RTX, 1);
+ tem = gen_lowpart (mode, tem);
+ /* We used the wrong signedness. Adjust the result. */
+ return expand_mult_highpart_adjust (mode, tem, op0, op1,
+ target, unsignedp);
+ }
+ }
+
+ /* As a last resort, try widening the mode and perform a
+ non-widening multiplication. */
+ moptab = smul_optab;
+ }
+
+ /* Pass NULL_RTX as target since TARGET has wrong mode. */
+ tem = expand_binop (wider_mode, moptab, op0, wide_op1,
+ NULL_RTX, unsignedp, OPTAB_WIDEN);
+ if (tem == 0)
+ return 0;
+
+ /* Extract the high half of the just generated product. */
+ tem = expand_shift (RSHIFT_EXPR, wider_mode, tem,
+ build_int_2 (size, 0), NULL_RTX, 1);
+ return gen_lowpart (mode, tem);
+}
+
+/* Emit the code to divide OP0 by OP1, putting the result in TARGET
+ if that is convenient, and returning where the result is.
+ You may request either the quotient or the remainder as the result;
+ specify REM_FLAG nonzero to get the remainder.
+
+ CODE is the expression code for which kind of division this is;
+ it controls how rounding is done. MODE is the machine mode to use.
+ UNSIGNEDP nonzero means do unsigned division. */
+
+/* ??? For CEIL_MOD_EXPR, can compute incorrect remainder with ANDI
+ and then correct it by or'ing in missing high bits
+ if result of ANDI is nonzero.
+ For ROUND_MOD_EXPR, can use ANDI and then sign-extend the result.
+ This could optimize to a bfexts instruction.
+ But C doesn't use these operations, so their optimizations are
+ left for later. */
+
+#define EXACT_POWER_OF_2_OR_ZERO_P(x) (((x) & ((x) - 1)) == 0)
+
+rtx
+expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
+ int rem_flag;
+ enum tree_code code;
+ enum machine_mode mode;
+ register rtx op0, op1, target;
+ int unsignedp;
+{
+ enum machine_mode compute_mode;
+ register rtx tquotient;
+ rtx quotient = 0, remainder = 0;
+ rtx last;
+ int size;
+ rtx insn;
+ optab optab1, optab2;
+ int op1_is_constant, op1_is_pow2;
+
+ op1_is_constant = GET_CODE (op1) == CONST_INT;
+ op1_is_pow2 = (op1_is_constant
+ && ((EXACT_POWER_OF_2_OR_ZERO_P (INTVAL (op1))
+ || EXACT_POWER_OF_2_OR_ZERO_P (-INTVAL (op1)))));
+
+ /*
+ This is the structure of expand_divmod:
+
+ First comes code to fix up the operands so we can perform the operations
+ correctly and efficiently.
+
+ Second comes a switch statement with code specific for each rounding mode.
+ For some special operands this code emits all RTL for the desired
+ operation, for other cases, it generates a quotient and stores it in
+ QUOTIENT. The case for trunc division/remainder might leave quotient = 0,
+ to indicate that it has not done anything.
+
+ Last comes code that finishes the operation. If QUOTIENT is set an
+ REM_FLAG, the remainder is computed as OP0 - QUOTIENT * OP1. If QUOTIENT
+ is not set, it is computed using trunc rounding.
+
+ We try to generate special code for division and remainder when OP1 is a
+ constant. If |OP1| = 2**n we can use shifts and some other fast
+ operations. For other values of OP1, we compute a carefully selected
+ fixed-point approximation m = 1/OP1, and generate code that multiplies OP0
+ by m.
+
+ In all cases but EXACT_DIV_EXPR, this multiplication requires the upper
+ half of the product. Different strategies for generating the product are
+ implemented in expand_mult_highpart.
+
+ If what we actually want is the remainder, we generate that by another
+ by-constant multiplication and a subtraction. */
+
+ /* We shouldn't be called with OP1 == const1_rtx, but some of the
+ code below will malfunction if we are, so check here and handle
+ the special case if so. */
+ if (op1 == const1_rtx)
+ return rem_flag ? const0_rtx : op0;
+
+ if (target
+ /* Don't use the function value register as a target
+ since we have to read it as well as write it,
+ and function-inlining gets confused by this. */
+ && ((REG_P (target) && REG_FUNCTION_VALUE_P (target))
+ /* Don't clobber an operand while doing a multi-step calculation. */
+ || ((rem_flag || op1_is_constant)
+ && (reg_mentioned_p (target, op0)
+ || (GET_CODE (op0) == MEM && GET_CODE (target) == MEM)))
+ || reg_mentioned_p (target, op1)
+ || (GET_CODE (op1) == MEM && GET_CODE (target) == MEM)))
+ target = 0;
+
+ /* Get the mode in which to perform this computation. Normally it will
+ be MODE, but sometimes we can't do the desired operation in MODE.
+ If so, pick a wider mode in which we can do the operation. Convert
+ to that mode at the start to avoid repeated conversions.
+
+ First see what operations we need. These depend on the expression
+ we are evaluating. (We assume that divxx3 insns exist under the
+ same conditions that modxx3 insns and that these insns don't normally
+ fail. If these assumptions are not correct, we may generate less
+ efficient code in some cases.)
+
+ Then see if we find a mode in which we can open-code that operation
+ (either a division, modulus, or shift). Finally, check for the smallest
+ mode for which we can do the operation with a library call. */
+
+ /* We might want to refine this now that we have division-by-constant
+ optimization. Since expand_mult_highpart tries so many variants, it is
+ not straightforward to generalize this. Maybe we should make an array
+ of possible modes in init_expmed? Save this for GCC 2.7. */
+
+ optab1 = (op1_is_pow2 ? (unsignedp ? lshr_optab : ashr_optab)
+ : (unsignedp ? udiv_optab : sdiv_optab));
+ optab2 = (op1_is_pow2 ? optab1 : (unsignedp ? udivmod_optab : sdivmod_optab));
+
+ for (compute_mode = mode; compute_mode != VOIDmode;
+ compute_mode = GET_MODE_WIDER_MODE (compute_mode))
+ if (optab1->handlers[(int) compute_mode].insn_code != CODE_FOR_nothing
+ || optab2->handlers[(int) compute_mode].insn_code != CODE_FOR_nothing)
+ break;
+
+ if (compute_mode == VOIDmode)
+ for (compute_mode = mode; compute_mode != VOIDmode;
+ compute_mode = GET_MODE_WIDER_MODE (compute_mode))
+ if (optab1->handlers[(int) compute_mode].libfunc
+ || optab2->handlers[(int) compute_mode].libfunc)
+ break;
+
+ /* If we still couldn't find a mode, use MODE, but we'll probably abort
+ in expand_binop. */
+ if (compute_mode == VOIDmode)
+ compute_mode = mode;
+
+ if (target && GET_MODE (target) == compute_mode)
+ tquotient = target;
+ else
+ tquotient = gen_reg_rtx (compute_mode);
+
+ size = GET_MODE_BITSIZE (compute_mode);
+#if 0
+ /* It should be possible to restrict the precision to GET_MODE_BITSIZE
+ (mode), and thereby get better code when OP1 is a constant. Do that for
+ GCC 2.7. It will require going over all usages of SIZE below. */
+ size = GET_MODE_BITSIZE (mode);
+#endif
+
+ /* Now convert to the best mode to use. */
+ if (compute_mode != mode)
+ {
+ op0 = convert_modes (compute_mode, mode, op0, unsignedp);
+ op1 = convert_modes (compute_mode, mode, op1, unsignedp);
+ }
+
+ /* If one of the operands is a volatile MEM, copy it into a register. */
+
+ if (GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0))
+ op0 = force_reg (compute_mode, op0);
+ if (GET_CODE (op1) == MEM && MEM_VOLATILE_P (op1))
+ op1 = force_reg (compute_mode, op1);
+
+ /* If we need the remainder or if OP1 is constant, we need to
+ put OP0 in a register in case it has any queued subexpressions. */
+ if (rem_flag || op1_is_constant)
+ op0 = force_reg (compute_mode, op0);
+
+ last = get_last_insn ();
+
+ /* Promote floor rouding to trunc rounding for unsigned operations. */
+ if (unsignedp)
+ {
+ if (code == FLOOR_DIV_EXPR)
+ code = TRUNC_DIV_EXPR;
+ if (code == FLOOR_MOD_EXPR)
+ code = TRUNC_MOD_EXPR;
+ }
+
+ if (op1 != const0_rtx)
+ switch (code)
+ {
+ case TRUNC_MOD_EXPR:
+ case TRUNC_DIV_EXPR:
+ if (op1_is_constant && HOST_BITS_PER_WIDE_INT >= size)
+ {
+ if (unsignedp)
+ {
+ unsigned HOST_WIDE_INT mh, ml;
+ int pre_shift, post_shift;
+ int dummy;
+ unsigned HOST_WIDE_INT d = INTVAL (op1);
+
+ if (EXACT_POWER_OF_2_OR_ZERO_P (d))
+ {
+ pre_shift = floor_log2 (d);
+ if (rem_flag)
+ {
+ remainder = expand_binop (compute_mode, and_optab, op0,
+ GEN_INT (((HOST_WIDE_INT) 1 << pre_shift) - 1),
+ remainder, 1,
+ OPTAB_LIB_WIDEN);
+ if (remainder)
+ return gen_lowpart (mode, remainder);
+ }
+ quotient = expand_shift (RSHIFT_EXPR, compute_mode, op0,
+ build_int_2 (pre_shift, 0),
+ tquotient, 1);
+ }
+ else if (d >= ((unsigned HOST_WIDE_INT) 1 << (size - 1)))
+ {
+ /* Most significant bit of divisor is set, emit a scc insn.
+ emit_store_flag needs to be passed a place for the
+ result. */
+ quotient = emit_store_flag (tquotient, GEU, op0, op1,
+ compute_mode, 1, 1);
+ /* Can emit_store_flag have failed? */
+ if (quotient == 0)
+ goto fail1;
+ }
+ else
+ {
+ /* Find a suitable multiplier and right shift count instead
+ of multiplying with D. */
+
+ mh = choose_multiplier (d, size, size,
+ &ml, &post_shift, &dummy);
+
+ /* If the suggested multiplier is more than SIZE bits, we
+ can do better for even divisors, using an initial right
+ shift. */
+ if (mh != 0 && (d & 1) == 0)
+ {
+ pre_shift = floor_log2 (d & -d);
+ mh = choose_multiplier (d >> pre_shift, size,
+ size - pre_shift,
+ &ml, &post_shift, &dummy);
+ if (mh)
+ abort ();
+ }
+ else
+ pre_shift = 0;
+
+ if (mh != 0)
+ {
+ rtx t1, t2, t3, t4;
+
+ t1 = expand_mult_highpart (compute_mode, op0, ml,
+ NULL_RTX, 1);
+ if (t1 == 0)
+ goto fail1;
+ t2 = force_operand (gen_rtx (MINUS, compute_mode,
+ op0, t1),
+ NULL_RTX);
+ t3 = expand_shift (RSHIFT_EXPR, compute_mode, t2,
+ build_int_2 (1, 0), NULL_RTX, 1);
+ t4 = force_operand (gen_rtx (PLUS, compute_mode,
+ t1, t3),
+ NULL_RTX);
+ quotient = expand_shift (RSHIFT_EXPR, compute_mode, t4,
+ build_int_2 (post_shift - 1,
+ 0),
+ tquotient, 1);
+ }
+ else
+ {
+ rtx t1, t2;
+
+ t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
+ build_int_2 (pre_shift, 0),
+ NULL_RTX, 1);
+ t2 = expand_mult_highpart (compute_mode, t1, ml,
+ NULL_RTX, 1);
+ if (t2 == 0)
+ goto fail1;
+ quotient = expand_shift (RSHIFT_EXPR, compute_mode, t2,
+ build_int_2 (post_shift, 0),
+ tquotient, 1);
+ }
+ }
+
+ insn = get_last_insn ();
+ REG_NOTES (insn)
+ = gen_rtx (EXPR_LIST, REG_EQUAL,
+ gen_rtx (UDIV, compute_mode, op0, op1),
+ REG_NOTES (insn));
+ }
+ else /* TRUNC_DIV, signed */
+ {
+ unsigned HOST_WIDE_INT ml;
+ int lgup, post_shift;
+ HOST_WIDE_INT d = INTVAL (op1);
+ unsigned HOST_WIDE_INT abs_d = d >= 0 ? d : -d;
+
+ /* n rem d = n rem -d */
+ if (rem_flag && d < 0)
+ {
+ d = abs_d;
+ op1 = GEN_INT (abs_d);
+ }
+
+ if (d == 1)
+ quotient = op0;
+ else if (d == -1)
+ quotient = expand_unop (compute_mode, neg_optab, op0,
+ tquotient, 0);
+ else if (EXACT_POWER_OF_2_OR_ZERO_P (d)
+ && (rem_flag ? smod_pow2_cheap : sdiv_pow2_cheap))
+ ;
+ else if (EXACT_POWER_OF_2_OR_ZERO_P (abs_d))
+ {
+ lgup = floor_log2 (abs_d);
+ if (abs_d != 2 && BRANCH_COST < 3)
+ {
+ rtx label = gen_label_rtx ();
+ rtx t1;
+
+ t1 = copy_to_mode_reg (compute_mode, op0);
+ emit_cmp_insn (t1, const0_rtx, GE,
+ NULL_RTX, compute_mode, 0, 0);
+ emit_jump_insn (gen_bge (label));
+ expand_inc (t1, GEN_INT (abs_d - 1));
+ emit_label (label);
+ quotient = expand_shift (RSHIFT_EXPR, compute_mode, t1,
+ build_int_2 (lgup, 0),
+ tquotient, 0);
+ }
+ else
+ {
+ rtx t1, t2, t3;
+ t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
+ build_int_2 (size - 1, 0),
+ NULL_RTX, 0);
+ t2 = expand_shift (RSHIFT_EXPR, compute_mode, t1,
+ build_int_2 (size - lgup, 0),
+ NULL_RTX, 1);
+ t3 = force_operand (gen_rtx (PLUS, compute_mode,
+ op0, t2),
+ NULL_RTX);
+ quotient = expand_shift (RSHIFT_EXPR, compute_mode, t3,
+ build_int_2 (lgup, 0),
+ tquotient, 0);
+ }
+
+ if (d < 0)
+ {
+ insn = get_last_insn ();
+ REG_NOTES (insn)
+ = gen_rtx (EXPR_LIST, REG_EQUAL,
+ gen_rtx (DIV, compute_mode, op0,
+ GEN_INT (abs_d)),
+ REG_NOTES (insn));
+
+ quotient = expand_unop (compute_mode, neg_optab,
+ quotient, quotient, 0);
+ }
+ }
+ else
+ {
+ choose_multiplier (abs_d, size, size - 1,
+ &ml, &post_shift, &lgup);
+ if (ml < (unsigned HOST_WIDE_INT) 1 << (size - 1))
+ {
+ rtx t1, t2, t3;
+
+ t1 = expand_mult_highpart (compute_mode, op0, ml,
+ NULL_RTX, 0);
+ if (t1 == 0)
+ goto fail1;
+ t2 = expand_shift (RSHIFT_EXPR, compute_mode, t1,
+ build_int_2 (post_shift, 0), NULL_RTX, 0);
+ t3 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
+ build_int_2 (size - 1, 0), NULL_RTX, 0);
+ if (d < 0)
+ quotient = force_operand (gen_rtx (MINUS, compute_mode, t3, t2),
+ tquotient);
+ else
+ quotient = force_operand (gen_rtx (MINUS, compute_mode, t2, t3),
+ tquotient);
+ }
+ else
+ {
+ rtx t1, t2, t3, t4;
+
+ ml |= (~(unsigned HOST_WIDE_INT) 0) << (size - 1);
+ t1 = expand_mult_highpart (compute_mode, op0, ml,
+ NULL_RTX, 0);
+ if (t1 == 0)
+ goto fail1;
+ t2 = force_operand (gen_rtx (PLUS, compute_mode, t1, op0),
+ NULL_RTX);
+ t3 = expand_shift (RSHIFT_EXPR, compute_mode, t2,
+ build_int_2 (post_shift, 0), NULL_RTX, 0);
+ t4 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
+ build_int_2 (size - 1, 0), NULL_RTX, 0);
+ if (d < 0)
+ quotient = force_operand (gen_rtx (MINUS, compute_mode, t4, t3),
+ tquotient);
+ else
+ quotient = force_operand (gen_rtx (MINUS, compute_mode, t3, t4),
+ tquotient);
+ }
+ }
+
+ if (quotient != 0)
+ {
+ insn = get_last_insn ();
+ REG_NOTES (insn)
+ = gen_rtx (EXPR_LIST, REG_EQUAL,
+ gen_rtx (DIV, compute_mode, op0, op1),
+ REG_NOTES (insn));
+ }
+ }
+ break;
+ }
+ fail1:
+ delete_insns_since (last);
+ break;
+
+ case FLOOR_DIV_EXPR:
+ case FLOOR_MOD_EXPR:
+ /* We will come here only for signed operations. */
+ if (op1_is_constant && HOST_BITS_PER_WIDE_INT >= size)
+ {
+ unsigned HOST_WIDE_INT mh, ml;
+ int pre_shift, lgup, post_shift;
+ HOST_WIDE_INT d = INTVAL (op1);
+
+ if (d > 0)
+ {
+ /* We could just as easily deal with negative constants here,
+ but it does not seem worth the trouble for GCC 2.6. */
+ if (EXACT_POWER_OF_2_OR_ZERO_P (d))
+ {
+ pre_shift = floor_log2 (d);
+ if (rem_flag)
+ {
+ remainder = expand_binop (compute_mode, and_optab, op0,
+ GEN_INT (((HOST_WIDE_INT) 1 << pre_shift) - 1),
+ remainder, 0, OPTAB_LIB_WIDEN);
+ if (remainder)
+ return gen_lowpart (mode, remainder);
+ }
+ quotient = expand_shift (RSHIFT_EXPR, compute_mode, op0,
+ build_int_2 (pre_shift, 0),
+ tquotient, 0);
+ }
+ else
+ {
+ rtx t1, t2, t3, t4;
+
+ mh = choose_multiplier (d, size, size - 1,
+ &ml, &post_shift, &lgup);
+ if (mh)
+ abort ();
+
+ t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
+ build_int_2 (size - 1, 0), NULL_RTX, 0);
+ t2 = expand_binop (compute_mode, xor_optab, op0, t1,
+ NULL_RTX, 0, OPTAB_WIDEN);
+ t3 = expand_mult_highpart (compute_mode, t2, ml,
+ NULL_RTX, 1);
+ if (t3 != 0)
+ {
+ t4 = expand_shift (RSHIFT_EXPR, compute_mode, t3,
+ build_int_2 (post_shift, 0),
+ NULL_RTX, 1);
+ quotient = expand_binop (compute_mode, xor_optab,
+ t4, t1, tquotient, 0,
+ OPTAB_WIDEN);
+ }
+ }
+ }
+ else
+ {
+ rtx nsign, t1, t2, t3, t4;
+ t1 = force_operand (gen_rtx (PLUS, compute_mode,
+ op0, constm1_rtx), NULL_RTX);
+ t2 = expand_binop (compute_mode, ior_optab, op0, t1, NULL_RTX,
+ 0, OPTAB_WIDEN);
+ nsign = expand_shift (RSHIFT_EXPR, compute_mode, t2,
+ build_int_2 (size - 1, 0), NULL_RTX, 0);
+ t3 = force_operand (gen_rtx (MINUS, compute_mode, t1, nsign),
+ NULL_RTX);
+ t4 = expand_divmod (0, TRUNC_DIV_EXPR, compute_mode, t3, op1,
+ NULL_RTX, 0);
+ if (t4)
+ {
+ rtx t5;
+ t5 = expand_unop (compute_mode, one_cmpl_optab, nsign,
+ NULL_RTX, 0);
+ quotient = force_operand (gen_rtx (PLUS, compute_mode,
+ t4, t5),
+ tquotient);
+ }
+ }
+ }
+
+ if (quotient != 0)
+ break;
+ delete_insns_since (last);
+
+ /* Try using an instruction that produces both the quotient and
+ remainder, using truncation. We can easily compensate the quotient
+ or remainder to get floor rounding, once we have the remainder.
+ Notice that we compute also the final remainder value here,
+ and return the result right away. */
+ if (target == 0)
+ target = gen_reg_rtx (compute_mode);
+ if (rem_flag)
+ {
+ remainder = target;
+ quotient = gen_reg_rtx (compute_mode);
+ }
+ else
+ {
+ quotient = target;
+ remainder = gen_reg_rtx (compute_mode);
+ }
+
+ if (expand_twoval_binop (sdivmod_optab, op0, op1,
+ quotient, remainder, 0))
+ {
+ /* This could be computed with a branch-less sequence.
+ Save that for later. */
+ rtx tem;
+ rtx label = gen_label_rtx ();
+ emit_cmp_insn (remainder, const0_rtx, EQ, NULL_RTX,
+ compute_mode, 0, 0);
+ emit_jump_insn (gen_beq (label));
+ tem = expand_binop (compute_mode, xor_optab, op0, op1,
+ NULL_RTX, 0, OPTAB_WIDEN);
+ emit_cmp_insn (tem, const0_rtx, GE, NULL_RTX, compute_mode, 0, 0);
+ emit_jump_insn (gen_bge (label));
+ expand_dec (quotient, const1_rtx);
+ expand_inc (remainder, op1);
+ emit_label (label);
+ return gen_lowpart (mode, rem_flag ? remainder : quotient);
+ }
+
+ /* No luck with division elimination or divmod. Have to do it
+ by conditionally adjusting op0 *and* the result. */
+ {
+ rtx label1, label2, label3, label4, label5;
+ rtx adjusted_op0;
+ rtx tem;
+
+ quotient = gen_reg_rtx (compute_mode);
+ adjusted_op0 = copy_to_mode_reg (compute_mode, op0);
+ label1 = gen_label_rtx ();
+ label2 = gen_label_rtx ();
+ label3 = gen_label_rtx ();
+ label4 = gen_label_rtx ();
+ label5 = gen_label_rtx ();
+ emit_cmp_insn (op1, const0_rtx, LT, NULL_RTX, compute_mode, 0, 0);
+ emit_jump_insn (gen_blt (label2));
+ emit_cmp_insn (adjusted_op0, const0_rtx, LT, NULL_RTX,
+ compute_mode, 0, 0);
+ emit_jump_insn (gen_blt (label1));
+ tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
+ quotient, 0, OPTAB_LIB_WIDEN);
+ if (tem != quotient)
+ emit_move_insn (quotient, tem);
+ emit_jump_insn (gen_jump (label5));
+ emit_barrier ();
+ emit_label (label1);
+ expand_inc (adjusted_op0, const1_rtx);
+ emit_jump_insn (gen_jump (label4));
+ emit_barrier ();
+ emit_label (label2);
+ emit_cmp_insn (adjusted_op0, const0_rtx, GT, NULL_RTX,
+ compute_mode, 0, 0);
+ emit_jump_insn (gen_bgt (label3));
+ tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
+ quotient, 0, OPTAB_LIB_WIDEN);
+ if (tem != quotient)
+ emit_move_insn (quotient, tem);
+ emit_jump_insn (gen_jump (label5));
+ emit_barrier ();
+ emit_label (label3);
+ expand_dec (adjusted_op0, const1_rtx);
+ emit_label (label4);
+ tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
+ quotient, 0, OPTAB_LIB_WIDEN);
+ if (tem != quotient)
+ emit_move_insn (quotient, tem);
+ expand_dec (quotient, const1_rtx);
+ emit_label (label5);
+ }
+ break;
+
+ case CEIL_DIV_EXPR:
+ case CEIL_MOD_EXPR:
+ if (unsignedp)
+ {
+ if (op1_is_constant && EXACT_POWER_OF_2_OR_ZERO_P (INTVAL (op1)))
+ {
+ rtx t1, t2, t3;
+ unsigned HOST_WIDE_INT d = INTVAL (op1);
+ t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
+ build_int_2 (floor_log2 (d), 0),
+ tquotient, 1);
+ t2 = expand_binop (compute_mode, and_optab, op0,
+ GEN_INT (d - 1),
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ t3 = gen_reg_rtx (compute_mode);
+ t3 = emit_store_flag (t3, NE, t2, const0_rtx,
+ compute_mode, 1, 1);
+ if (t3 == 0)
+ {
+ rtx lab;
+ lab = gen_label_rtx ();
+ emit_cmp_insn (t2, const0_rtx, EQ, NULL_RTX,
+ compute_mode, 0, 0);
+ emit_jump_insn (gen_beq (lab));
+ expand_inc (t1, const1_rtx);
+ emit_label (lab);
+ quotient = t1;
+ }
+ else
+ quotient = force_operand (gen_rtx (PLUS, compute_mode,
+ t1, t3),
+ tquotient);
+ break;
+ }
+
+ /* Try using an instruction that produces both the quotient and
+ remainder, using truncation. We can easily compensate the
+ quotient or remainder to get ceiling rounding, once we have the
+ remainder. Notice that we compute also the final remainder
+ value here, and return the result right away. */
+ if (target == 0)
+ target = gen_reg_rtx (compute_mode);
+ if (rem_flag)
+ {
+ remainder = target;
+ quotient = gen_reg_rtx (compute_mode);
+ }
+ else
+ {
+ quotient = target;
+ remainder = gen_reg_rtx (compute_mode);
+ }
+
+ if (expand_twoval_binop (udivmod_optab, op0, op1, quotient,
+ remainder, 1))
+ {
+ /* This could be computed with a branch-less sequence.
+ Save that for later. */
+ rtx label = gen_label_rtx ();
+ emit_cmp_insn (remainder, const0_rtx, EQ, NULL_RTX,
+ compute_mode, 0, 0);
+ emit_jump_insn (gen_beq (label));
+ expand_inc (quotient, const1_rtx);
+ expand_dec (remainder, op1);
+ emit_label (label);
+ return gen_lowpart (mode, rem_flag ? remainder : quotient);
+ }
+
+ /* No luck with division elimination or divmod. Have to do it
+ by conditionally adjusting op0 *and* the result. */
+ {
+ rtx label1, label2;
+ rtx adjusted_op0, tem;
+
+ quotient = gen_reg_rtx (compute_mode);
+ adjusted_op0 = copy_to_mode_reg (compute_mode, op0);
+ label1 = gen_label_rtx ();
+ label2 = gen_label_rtx ();
+ emit_cmp_insn (adjusted_op0, const0_rtx, NE, NULL_RTX,
+ compute_mode, 0, 0);
+ emit_jump_insn (gen_bne (label1));
+ emit_move_insn (quotient, const0_rtx);
+ emit_jump_insn (gen_jump (label2));
+ emit_barrier ();
+ emit_label (label1);
+ expand_dec (adjusted_op0, const1_rtx);
+ tem = expand_binop (compute_mode, udiv_optab, adjusted_op0, op1,
+ quotient, 1, OPTAB_LIB_WIDEN);
+ if (tem != quotient)
+ emit_move_insn (quotient, tem);
+ expand_inc (quotient, const1_rtx);
+ emit_label (label2);
+ }
+ }
+ else /* signed */
+ {
+ /* Try using an instruction that produces both the quotient and
+ remainder, using truncation. We can easily compensate the
+ quotient or remainder to get ceiling rounding, once we have the
+ remainder. Notice that we compute also the final remainder
+ value here, and return the result right away. */
+ if (target == 0)
+ target = gen_reg_rtx (compute_mode);
+ if (rem_flag)
+ {
+ remainder = target;
+ quotient = gen_reg_rtx (compute_mode);
+ }
+ else
+ {
+ quotient = target;
+ remainder = gen_reg_rtx (compute_mode);
+ }
+
+ if (expand_twoval_binop (sdivmod_optab, op0, op1, quotient,
+ remainder, 0))
+ {
+ /* This could be computed with a branch-less sequence.
+ Save that for later. */
+ rtx tem;
+ rtx label = gen_label_rtx ();
+ emit_cmp_insn (remainder, const0_rtx, EQ, NULL_RTX,
+ compute_mode, 0, 0);
+ emit_jump_insn (gen_beq (label));
+ tem = expand_binop (compute_mode, xor_optab, op0, op1,
+ NULL_RTX, 0, OPTAB_WIDEN);
+ emit_cmp_insn (tem, const0_rtx, LT, NULL_RTX,
+ compute_mode, 0, 0);
+ emit_jump_insn (gen_blt (label));
+ expand_inc (quotient, const1_rtx);
+ expand_dec (remainder, op1);
+ emit_label (label);
+ return gen_lowpart (mode, rem_flag ? remainder : quotient);
+ }
+
+ /* No luck with division elimination or divmod. Have to do it
+ by conditionally adjusting op0 *and* the result. */
+ {
+ rtx label1, label2, label3, label4, label5;
+ rtx adjusted_op0;
+ rtx tem;
+
+ quotient = gen_reg_rtx (compute_mode);
+ adjusted_op0 = copy_to_mode_reg (compute_mode, op0);
+ label1 = gen_label_rtx ();
+ label2 = gen_label_rtx ();
+ label3 = gen_label_rtx ();
+ label4 = gen_label_rtx ();
+ label5 = gen_label_rtx ();
+ emit_cmp_insn (op1, const0_rtx, LT, NULL_RTX,
+ compute_mode, 0, 0);
+ emit_jump_insn (gen_blt (label2));
+ emit_cmp_insn (adjusted_op0, const0_rtx, GT, NULL_RTX,
+ compute_mode, 0, 0);
+ emit_jump_insn (gen_bgt (label1));
+ tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
+ quotient, 0, OPTAB_LIB_WIDEN);
+ if (tem != quotient)
+ emit_move_insn (quotient, tem);
+ emit_jump_insn (gen_jump (label5));
+ emit_barrier ();
+ emit_label (label1);
+ expand_dec (adjusted_op0, const1_rtx);
+ emit_jump_insn (gen_jump (label4));
+ emit_barrier ();
+ emit_label (label2);
+ emit_cmp_insn (adjusted_op0, const0_rtx, LT, NULL_RTX,
+ compute_mode, 0, 0);
+ emit_jump_insn (gen_blt (label3));
+ tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
+ quotient, 0, OPTAB_LIB_WIDEN);
+ if (tem != quotient)
+ emit_move_insn (quotient, tem);
+ emit_jump_insn (gen_jump (label5));
+ emit_barrier ();
+ emit_label (label3);
+ expand_inc (adjusted_op0, const1_rtx);
+ emit_label (label4);
+ tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
+ quotient, 0, OPTAB_LIB_WIDEN);
+ if (tem != quotient)
+ emit_move_insn (quotient, tem);
+ expand_inc (quotient, const1_rtx);
+ emit_label (label5);
+ }
+ }
+ break;
+
+ case EXACT_DIV_EXPR:
+ if (op1_is_constant && HOST_BITS_PER_WIDE_INT >= size)
+ {
+ HOST_WIDE_INT d = INTVAL (op1);
+ unsigned HOST_WIDE_INT ml;
+ int post_shift;
+ rtx t1;
+
+ post_shift = floor_log2 (d & -d);
+ ml = invert_mod2n (d >> post_shift, size);
+ t1 = expand_mult (compute_mode, op0, GEN_INT (ml), NULL_RTX,
+ unsignedp);
+ quotient = expand_shift (RSHIFT_EXPR, compute_mode, t1,
+ build_int_2 (post_shift, 0),
+ NULL_RTX, unsignedp);
+
+ insn = get_last_insn ();
+ REG_NOTES (insn)
+ = gen_rtx (EXPR_LIST, REG_EQUAL,
+ gen_rtx (unsignedp ? UDIV : DIV, compute_mode,
+ op0, op1),
+ REG_NOTES (insn));
+ }
+ break;
+
+ case ROUND_DIV_EXPR:
+ case ROUND_MOD_EXPR:
+ /* The code that used to be here was wrong, and nothing really
+ depends on it. */
+ abort ();
+ break;
+ }
+
+ if (quotient == 0)
+ {
+ if (rem_flag)
+ {
+ /* Try to produce the remainder directly without a library call. */
+ remainder = sign_expand_binop (compute_mode, umod_optab, smod_optab,
+ op0, op1, target,
+ unsignedp, OPTAB_WIDEN);
+ if (remainder == 0)
+ {
+ /* No luck there. Can we do remainder and divide at once
+ without a library call? */
+ remainder = gen_reg_rtx (compute_mode);
+ if (! expand_twoval_binop ((unsignedp
+ ? udivmod_optab
+ : sdivmod_optab),
+ op0, op1,
+ NULL_RTX, remainder, unsignedp))
+ remainder = 0;
+ }
+
+ if (remainder)
+ return gen_lowpart (mode, remainder);
+ }
+
+ /* Produce the quotient. */
+ /* Try a quotient insn, but not a library call. */
+ quotient = sign_expand_binop (compute_mode, udiv_optab, sdiv_optab,
+ op0, op1, rem_flag ? NULL_RTX : target,
+ unsignedp, OPTAB_WIDEN);
+ if (quotient == 0)
+ {
+ /* No luck there. Try a quotient-and-remainder insn,
+ keeping the quotient alone. */
+ quotient = gen_reg_rtx (compute_mode);
+ if (! expand_twoval_binop (unsignedp ? udivmod_optab : sdivmod_optab,
+ op0, op1,
+ quotient, NULL_RTX, unsignedp))
+ {
+ quotient = 0;
+ if (! rem_flag)
+ /* Still no luck. If we are not computing the remainder,
+ use a library call for the quotient. */
+ quotient = sign_expand_binop (compute_mode,
+ udiv_optab, sdiv_optab,
+ op0, op1, target,
+ unsignedp, OPTAB_LIB_WIDEN);
+ }
+ }
+ }
+
+ if (rem_flag)
+ {
+ if (quotient == 0)
+ /* No divide instruction either. Use library for remainder. */
+ remainder = sign_expand_binop (compute_mode, umod_optab, smod_optab,
+ op0, op1, target,
+ unsignedp, OPTAB_LIB_WIDEN);
+ else
+ {
+ /* We divided. Now finish doing X - Y * (X / Y). */
+ remainder = expand_mult (compute_mode, quotient, op1,
+ NULL_RTX, unsignedp);
+ remainder = expand_binop (compute_mode, sub_optab, op0,
+ remainder, target, unsignedp,
+ OPTAB_LIB_WIDEN);
+ }
+ }
+
+ return gen_lowpart (mode, rem_flag ? remainder : quotient);
+}
+
+/* Return a tree node with data type TYPE, describing the value of X.
+ Usually this is an RTL_EXPR, if there is no obvious better choice.
+ X may be an expression, however we only support those expressions
+ generated by loop.c. */
+
+tree
+make_tree (type, x)
+ tree type;
+ rtx x;
+{
+ tree t;
+
+ switch (GET_CODE (x))
+ {
+ case CONST_INT:
+ t = build_int_2 (INTVAL (x),
+ TREE_UNSIGNED (type) || INTVAL (x) >= 0 ? 0 : -1);
+ TREE_TYPE (t) = type;
+ return t;
+
+ case CONST_DOUBLE:
+ if (GET_MODE (x) == VOIDmode)
+ {
+ t = build_int_2 (CONST_DOUBLE_LOW (x), CONST_DOUBLE_HIGH (x));
+ TREE_TYPE (t) = type;
+ }
+ else
+ {
+ REAL_VALUE_TYPE d;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (d, x);
+ t = build_real (type, d);
+ }
+
+ return t;
+
+ case PLUS:
+ return fold (build (PLUS_EXPR, type, make_tree (type, XEXP (x, 0)),
+ make_tree (type, XEXP (x, 1))));
+
+ case MINUS:
+ return fold (build (MINUS_EXPR, type, make_tree (type, XEXP (x, 0)),
+ make_tree (type, XEXP (x, 1))));
+
+ case NEG:
+ return fold (build1 (NEGATE_EXPR, type, make_tree (type, XEXP (x, 0))));
+
+ case MULT:
+ return fold (build (MULT_EXPR, type, make_tree (type, XEXP (x, 0)),
+ make_tree (type, XEXP (x, 1))));
+
+ case ASHIFT:
+ return fold (build (LSHIFT_EXPR, type, make_tree (type, XEXP (x, 0)),
+ make_tree (type, XEXP (x, 1))));
+
+ case LSHIFTRT:
+ return fold (convert (type,
+ build (RSHIFT_EXPR, unsigned_type (type),
+ make_tree (unsigned_type (type),
+ XEXP (x, 0)),
+ make_tree (type, XEXP (x, 1)))));
+
+ case ASHIFTRT:
+ return fold (convert (type,
+ build (RSHIFT_EXPR, signed_type (type),
+ make_tree (signed_type (type), XEXP (x, 0)),
+ make_tree (type, XEXP (x, 1)))));
+
+ case DIV:
+ if (TREE_CODE (type) != REAL_TYPE)
+ t = signed_type (type);
+ else
+ t = type;
+
+ return fold (convert (type,
+ build (TRUNC_DIV_EXPR, t,
+ make_tree (t, XEXP (x, 0)),
+ make_tree (t, XEXP (x, 1)))));
+ case UDIV:
+ t = unsigned_type (type);
+ return fold (convert (type,
+ build (TRUNC_DIV_EXPR, t,
+ make_tree (t, XEXP (x, 0)),
+ make_tree (t, XEXP (x, 1)))));
+ default:
+ t = make_node (RTL_EXPR);
+ TREE_TYPE (t) = type;
+ RTL_EXPR_RTL (t) = x;
+ /* There are no insns to be output
+ when this rtl_expr is used. */
+ RTL_EXPR_SEQUENCE (t) = 0;
+ return t;
+ }
+}
+
+/* Return an rtx representing the value of X * MULT + ADD.
+ TARGET is a suggestion for where to store the result (an rtx).
+ MODE is the machine mode for the computation.
+ X and MULT must have mode MODE. ADD may have a different mode.
+ So can X (defaults to same as MODE).
+ UNSIGNEDP is non-zero to do unsigned multiplication.
+ This may emit insns. */
+
+rtx
+expand_mult_add (x, target, mult, add, mode, unsignedp)
+ rtx x, target, mult, add;
+ enum machine_mode mode;
+ int unsignedp;
+{
+ tree type = type_for_mode (mode, unsignedp);
+ tree add_type = (GET_MODE (add) == VOIDmode
+ ? type : type_for_mode (GET_MODE (add), unsignedp));
+ tree result = fold (build (PLUS_EXPR, type,
+ fold (build (MULT_EXPR, type,
+ make_tree (type, x),
+ make_tree (type, mult))),
+ make_tree (add_type, add)));
+
+ return expand_expr (result, target, VOIDmode, 0);
+}
+
+/* Compute the logical-and of OP0 and OP1, storing it in TARGET
+ and returning TARGET.
+
+ If TARGET is 0, a pseudo-register or constant is returned. */
+
+rtx
+expand_and (op0, op1, target)
+ rtx op0, op1, target;
+{
+ enum machine_mode mode = VOIDmode;
+ rtx tem;
+
+ if (GET_MODE (op0) != VOIDmode)
+ mode = GET_MODE (op0);
+ else if (GET_MODE (op1) != VOIDmode)
+ mode = GET_MODE (op1);
+
+ if (mode != VOIDmode)
+ tem = expand_binop (mode, and_optab, op0, op1, target, 0, OPTAB_LIB_WIDEN);
+ else if (GET_CODE (op0) == CONST_INT && GET_CODE (op1) == CONST_INT)
+ tem = GEN_INT (INTVAL (op0) & INTVAL (op1));
+ else
+ abort ();
+
+ if (target == 0)
+ target = tem;
+ else if (tem != target)
+ emit_move_insn (target, tem);
+ return target;
+}
+
+/* Emit a store-flags instruction for comparison CODE on OP0 and OP1
+ and storing in TARGET. Normally return TARGET.
+ Return 0 if that cannot be done.
+
+ MODE is the mode to use for OP0 and OP1 should they be CONST_INTs. If
+ it is VOIDmode, they cannot both be CONST_INT.
+
+ UNSIGNEDP is for the case where we have to widen the operands
+ to perform the operation. It says to use zero-extension.
+
+ NORMALIZEP is 1 if we should convert the result to be either zero
+ or one one. Normalize is -1 if we should convert the result to be
+ either zero or -1. If NORMALIZEP is zero, the result will be left
+ "raw" out of the scc insn. */
+
+rtx
+emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
+ rtx target;
+ enum rtx_code code;
+ rtx op0, op1;
+ enum machine_mode mode;
+ int unsignedp;
+ int normalizep;
+{
+ rtx subtarget;
+ enum insn_code icode;
+ enum machine_mode compare_mode;
+ enum machine_mode target_mode = GET_MODE (target);
+ rtx tem;
+ rtx last = 0;
+ rtx pattern, comparison;
+
+ if (mode == VOIDmode)
+ mode = GET_MODE (op0);
+
+ /* If one operand is constant, make it the second one. Only do this
+ if the other operand is not constant as well. */
+
+ if ((CONSTANT_P (op0) && ! CONSTANT_P (op1))
+ || (GET_CODE (op0) == CONST_INT && GET_CODE (op1) != CONST_INT))
+ {
+ tem = op0;
+ op0 = op1;
+ op1 = tem;
+ code = swap_condition (code);
+ }
+
+ /* For some comparisons with 1 and -1, we can convert this to
+ comparisons with zero. This will often produce more opportunities for
+ store-flag insns. */
+
+ switch (code)
+ {
+ case LT:
+ if (op1 == const1_rtx)
+ op1 = const0_rtx, code = LE;
+ break;
+ case LE:
+ if (op1 == constm1_rtx)
+ op1 = const0_rtx, code = LT;
+ break;
+ case GE:
+ if (op1 == const1_rtx)
+ op1 = const0_rtx, code = GT;
+ break;
+ case GT:
+ if (op1 == constm1_rtx)
+ op1 = const0_rtx, code = GE;
+ break;
+ case GEU:
+ if (op1 == const1_rtx)
+ op1 = const0_rtx, code = NE;
+ break;
+ case LTU:
+ if (op1 == const1_rtx)
+ op1 = const0_rtx, code = EQ;
+ break;
+ }
+
+ /* From now on, we won't change CODE, so set ICODE now. */
+ icode = setcc_gen_code[(int) code];
+
+ /* If this is A < 0 or A >= 0, we can do this by taking the ones
+ complement of A (for GE) and shifting the sign bit to the low bit. */
+ if (op1 == const0_rtx && (code == LT || code == GE)
+ && GET_MODE_CLASS (mode) == MODE_INT
+ && (normalizep || STORE_FLAG_VALUE == 1
+ || (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+ && (STORE_FLAG_VALUE
+ == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)))))
+ {
+ subtarget = target;
+
+ /* If the result is to be wider than OP0, it is best to convert it
+ first. If it is to be narrower, it is *incorrect* to convert it
+ first. */
+ if (GET_MODE_SIZE (target_mode) > GET_MODE_SIZE (mode))
+ {
+ op0 = protect_from_queue (op0, 0);
+ op0 = convert_modes (target_mode, mode, op0, 0);
+ mode = target_mode;
+ }
+
+ if (target_mode != mode)
+ subtarget = 0;
+
+ if (code == GE)
+ op0 = expand_unop (mode, one_cmpl_optab, op0, subtarget, 0);
+
+ if (normalizep || STORE_FLAG_VALUE == 1)
+ /* If we are supposed to produce a 0/1 value, we want to do
+ a logical shift from the sign bit to the low-order bit; for
+ a -1/0 value, we do an arithmetic shift. */
+ op0 = expand_shift (RSHIFT_EXPR, mode, op0,
+ size_int (GET_MODE_BITSIZE (mode) - 1),
+ subtarget, normalizep != -1);
+
+ if (mode != target_mode)
+ op0 = convert_modes (target_mode, mode, op0, 0);
+
+ return op0;
+ }
+
+ if (icode != CODE_FOR_nothing)
+ {
+ /* We think we may be able to do this with a scc insn. Emit the
+ comparison and then the scc insn.
+
+ compare_from_rtx may call emit_queue, which would be deleted below
+ if the scc insn fails. So call it ourselves before setting LAST. */
+
+ emit_queue ();
+ last = get_last_insn ();
+
+ comparison
+ = compare_from_rtx (op0, op1, code, unsignedp, mode, NULL_RTX, 0);
+ if (GET_CODE (comparison) == CONST_INT)
+ return (comparison == const0_rtx ? const0_rtx
+ : normalizep == 1 ? const1_rtx
+ : normalizep == -1 ? constm1_rtx
+ : const_true_rtx);
+
+ /* If the code of COMPARISON doesn't match CODE, something is
+ wrong; we can no longer be sure that we have the operation.
+ We could handle this case, but it should not happen. */
+
+ if (GET_CODE (comparison) != code)
+ abort ();
+
+ /* Get a reference to the target in the proper mode for this insn. */
+ compare_mode = insn_operand_mode[(int) icode][0];
+ subtarget = target;
+ if (preserve_subexpressions_p ()
+ || ! (*insn_operand_predicate[(int) icode][0]) (subtarget, compare_mode))
+ subtarget = gen_reg_rtx (compare_mode);
+
+ pattern = GEN_FCN (icode) (subtarget);
+ if (pattern)
+ {
+ emit_insn (pattern);
+
+ /* If we are converting to a wider mode, first convert to
+ TARGET_MODE, then normalize. This produces better combining
+ opportunities on machines that have a SIGN_EXTRACT when we are
+ testing a single bit. This mostly benefits the 68k.
+
+ If STORE_FLAG_VALUE does not have the sign bit set when
+ interpreted in COMPARE_MODE, we can do this conversion as
+ unsigned, which is usually more efficient. */
+ if (GET_MODE_SIZE (target_mode) > GET_MODE_SIZE (compare_mode))
+ {
+ convert_move (target, subtarget,
+ (GET_MODE_BITSIZE (compare_mode)
+ <= HOST_BITS_PER_WIDE_INT)
+ && 0 == (STORE_FLAG_VALUE
+ & ((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (compare_mode) -1))));
+ op0 = target;
+ compare_mode = target_mode;
+ }
+ else
+ op0 = subtarget;
+
+ /* If we want to keep subexpressions around, don't reuse our
+ last target. */
+
+ if (preserve_subexpressions_p ())
+ subtarget = 0;
+
+ /* Now normalize to the proper value in COMPARE_MODE. Sometimes
+ we don't have to do anything. */
+ if (normalizep == 0 || normalizep == STORE_FLAG_VALUE)
+ ;
+ else if (normalizep == - STORE_FLAG_VALUE)
+ op0 = expand_unop (compare_mode, neg_optab, op0, subtarget, 0);
+
+ /* We don't want to use STORE_FLAG_VALUE < 0 below since this
+ makes it hard to use a value of just the sign bit due to
+ ANSI integer constant typing rules. */
+ else if (GET_MODE_BITSIZE (compare_mode) <= HOST_BITS_PER_WIDE_INT
+ && (STORE_FLAG_VALUE
+ & ((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (compare_mode) - 1))))
+ op0 = expand_shift (RSHIFT_EXPR, compare_mode, op0,
+ size_int (GET_MODE_BITSIZE (compare_mode) - 1),
+ subtarget, normalizep == 1);
+ else if (STORE_FLAG_VALUE & 1)
+ {
+ op0 = expand_and (op0, const1_rtx, subtarget);
+ if (normalizep == -1)
+ op0 = expand_unop (compare_mode, neg_optab, op0, op0, 0);
+ }
+ else
+ abort ();
+
+ /* If we were converting to a smaller mode, do the
+ conversion now. */
+ if (target_mode != compare_mode)
+ {
+ convert_move (target, op0, 0);
+ return target;
+ }
+ else
+ return op0;
+ }
+ }
+
+ if (last)
+ delete_insns_since (last);
+
+ subtarget = target_mode == mode ? target : 0;
+
+ /* If we reached here, we can't do this with a scc insn. However, there
+ are some comparisons that can be done directly. For example, if
+ this is an equality comparison of integers, we can try to exclusive-or
+ (or subtract) the two operands and use a recursive call to try the
+ comparison with zero. Don't do any of these cases if branches are
+ very cheap. */
+
+ if (BRANCH_COST > 0
+ && GET_MODE_CLASS (mode) == MODE_INT && (code == EQ || code == NE)
+ && op1 != const0_rtx)
+ {
+ tem = expand_binop (mode, xor_optab, op0, op1, subtarget, 1,
+ OPTAB_WIDEN);
+
+ if (tem == 0)
+ tem = expand_binop (mode, sub_optab, op0, op1, subtarget, 1,
+ OPTAB_WIDEN);
+ if (tem != 0)
+ tem = emit_store_flag (target, code, tem, const0_rtx,
+ mode, unsignedp, normalizep);
+ if (tem == 0)
+ delete_insns_since (last);
+ return tem;
+ }
+
+ /* Some other cases we can do are EQ, NE, LE, and GT comparisons with
+ the constant zero. Reject all other comparisons at this point. Only
+ do LE and GT if branches are expensive since they are expensive on
+ 2-operand machines. */
+
+ if (BRANCH_COST == 0
+ || GET_MODE_CLASS (mode) != MODE_INT || op1 != const0_rtx
+ || (code != EQ && code != NE
+ && (BRANCH_COST <= 1 || (code != LE && code != GT))))
+ return 0;
+
+ /* See what we need to return. We can only return a 1, -1, or the
+ sign bit. */
+
+ if (normalizep == 0)
+ {
+ if (STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1)
+ normalizep = STORE_FLAG_VALUE;
+
+ else if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+ && (STORE_FLAG_VALUE
+ == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)))
+ ;
+ else
+ return 0;
+ }
+
+ /* Try to put the result of the comparison in the sign bit. Assume we can't
+ do the necessary operation below. */
+
+ tem = 0;
+
+ /* To see if A <= 0, compute (A | (A - 1)). A <= 0 iff that result has
+ the sign bit set. */
+
+ if (code == LE)
+ {
+ /* This is destructive, so SUBTARGET can't be OP0. */
+ if (rtx_equal_p (subtarget, op0))
+ subtarget = 0;
+
+ tem = expand_binop (mode, sub_optab, op0, const1_rtx, subtarget, 0,
+ OPTAB_WIDEN);
+ if (tem)
+ tem = expand_binop (mode, ior_optab, op0, tem, subtarget, 0,
+ OPTAB_WIDEN);
+ }
+
+ /* To see if A > 0, compute (((signed) A) << BITS) - A, where BITS is the
+ number of bits in the mode of OP0, minus one. */
+
+ if (code == GT)
+ {
+ if (rtx_equal_p (subtarget, op0))
+ subtarget = 0;
+
+ tem = expand_shift (RSHIFT_EXPR, mode, op0,
+ size_int (GET_MODE_BITSIZE (mode) - 1),
+ subtarget, 0);
+ tem = expand_binop (mode, sub_optab, tem, op0, subtarget, 0,
+ OPTAB_WIDEN);
+ }
+
+ if (code == EQ || code == NE)
+ {
+ /* For EQ or NE, one way to do the comparison is to apply an operation
+ that converts the operand into a positive number if it is non-zero
+ or zero if it was originally zero. Then, for EQ, we subtract 1 and
+ for NE we negate. This puts the result in the sign bit. Then we
+ normalize with a shift, if needed.
+
+ Two operations that can do the above actions are ABS and FFS, so try
+ them. If that doesn't work, and MODE is smaller than a full word,
+ we can use zero-extension to the wider mode (an unsigned conversion)
+ as the operation. */
+
+ if (abs_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+ tem = expand_unop (mode, abs_optab, op0, subtarget, 1);
+ else if (ffs_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+ tem = expand_unop (mode, ffs_optab, op0, subtarget, 1);
+ else if (GET_MODE_SIZE (mode) < UNITS_PER_WORD)
+ {
+ op0 = protect_from_queue (op0, 0);
+ tem = convert_modes (word_mode, mode, op0, 1);
+ mode = word_mode;
+ }
+
+ if (tem != 0)
+ {
+ if (code == EQ)
+ tem = expand_binop (mode, sub_optab, tem, const1_rtx, subtarget,
+ 0, OPTAB_WIDEN);
+ else
+ tem = expand_unop (mode, neg_optab, tem, subtarget, 0);
+ }
+
+ /* If we couldn't do it that way, for NE we can "or" the two's complement
+ of the value with itself. For EQ, we take the one's complement of
+ that "or", which is an extra insn, so we only handle EQ if branches
+ are expensive. */
+
+ if (tem == 0 && (code == NE || BRANCH_COST > 1))
+ {
+ if (rtx_equal_p (subtarget, op0))
+ subtarget = 0;
+
+ tem = expand_unop (mode, neg_optab, op0, subtarget, 0);
+ tem = expand_binop (mode, ior_optab, tem, op0, subtarget, 0,
+ OPTAB_WIDEN);
+
+ if (tem && code == EQ)
+ tem = expand_unop (mode, one_cmpl_optab, tem, subtarget, 0);
+ }
+ }
+
+ if (tem && normalizep)
+ tem = expand_shift (RSHIFT_EXPR, mode, tem,
+ size_int (GET_MODE_BITSIZE (mode) - 1),
+ tem, normalizep == 1);
+
+ if (tem && GET_MODE (tem) != target_mode)
+ {
+ convert_move (target, tem, 0);
+ tem = target;
+ }
+
+ if (tem == 0)
+ delete_insns_since (last);
+
+ return tem;
+}
diff --git a/gnu/usr.bin/cc/cc_int/expr.c b/gnu/usr.bin/cc/cc_int/expr.c
new file mode 100644
index 0000000..e764986
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/expr.c
@@ -0,0 +1,10192 @@
+/* Convert tree expression to rtl instructions, for GNU compiler.
+ Copyright (C) 1988, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#include "machmode.h"
+#include "rtl.h"
+#include "tree.h"
+#include "obstack.h"
+#include "flags.h"
+#include "regs.h"
+#include "function.h"
+#include "insn-flags.h"
+#include "insn-codes.h"
+#include "expr.h"
+#include "insn-config.h"
+#include "recog.h"
+#include "output.h"
+#include "typeclass.h"
+
+#include "bytecode.h"
+#include "bc-opcode.h"
+#include "bc-typecd.h"
+#include "bc-optab.h"
+#include "bc-emit.h"
+
+
+#define CEIL(x,y) (((x) + (y) - 1) / (y))
+
+/* Decide whether a function's arguments should be processed
+ from first to last or from last to first.
+
+ They should if the stack and args grow in opposite directions, but
+ only if we have push insns. */
+
+#ifdef PUSH_ROUNDING
+
+#if defined (STACK_GROWS_DOWNWARD) != defined (ARGS_GROW_DOWNWARD)
+#define PUSH_ARGS_REVERSED /* If it's last to first */
+#endif
+
+#endif
+
+#ifndef STACK_PUSH_CODE
+#ifdef STACK_GROWS_DOWNWARD
+#define STACK_PUSH_CODE PRE_DEC
+#else
+#define STACK_PUSH_CODE PRE_INC
+#endif
+#endif
+
+/* Like STACK_BOUNDARY but in units of bytes, not bits. */
+#define STACK_BYTES (STACK_BOUNDARY / BITS_PER_UNIT)
+
+/* If this is nonzero, we do not bother generating VOLATILE
+ around volatile memory references, and we are willing to
+ output indirect addresses. If cse is to follow, we reject
+ indirect addresses so a useful potential cse is generated;
+ if it is used only once, instruction combination will produce
+ the same indirect address eventually. */
+int cse_not_expected;
+
+/* Nonzero to generate code for all the subroutines within an
+ expression before generating the upper levels of the expression.
+ Nowadays this is never zero. */
+int do_preexpand_calls = 1;
+
+/* Number of units that we should eventually pop off the stack.
+ These are the arguments to function calls that have already returned. */
+int pending_stack_adjust;
+
+/* Nonzero means stack pops must not be deferred, and deferred stack
+ pops must not be output. It is nonzero inside a function call,
+ inside a conditional expression, inside a statement expression,
+ and in other cases as well. */
+int inhibit_defer_pop;
+
+/* A list of all cleanups which belong to the arguments of
+ function calls being expanded by expand_call. */
+tree cleanups_this_call;
+
+/* When temporaries are created by TARGET_EXPRs, they are created at
+ this level of temp_slot_level, so that they can remain allocated
+ until no longer needed. CLEANUP_POINT_EXPRs define the lifetime
+ of TARGET_EXPRs. */
+int target_temp_slot_level;
+
+/* Nonzero means __builtin_saveregs has already been done in this function.
+ The value is the pseudoreg containing the value __builtin_saveregs
+ returned. */
+static rtx saveregs_value;
+
+/* Similarly for __builtin_apply_args. */
+static rtx apply_args_value;
+
+/* This structure is used by move_by_pieces to describe the move to
+ be performed. */
+
+struct move_by_pieces
+{
+ rtx to;
+ rtx to_addr;
+ int autinc_to;
+ int explicit_inc_to;
+ rtx from;
+ rtx from_addr;
+ int autinc_from;
+ int explicit_inc_from;
+ int len;
+ int offset;
+ int reverse;
+};
+
+/* Used to generate bytecodes: keep track of size of local variables,
+ as well as depth of arithmetic stack. (Notice that variables are
+ stored on the machine's stack, not the arithmetic stack.) */
+
+extern int local_vars_size;
+extern int stack_depth;
+extern int max_stack_depth;
+extern struct obstack permanent_obstack;
+
+
+static rtx enqueue_insn PROTO((rtx, rtx));
+static int queued_subexp_p PROTO((rtx));
+static void init_queue PROTO((void));
+static void move_by_pieces PROTO((rtx, rtx, int, int));
+static int move_by_pieces_ninsns PROTO((unsigned int, int));
+static void move_by_pieces_1 PROTO((rtx (*) (), enum machine_mode,
+ struct move_by_pieces *));
+static void store_constructor PROTO((tree, rtx));
+static rtx store_field PROTO((rtx, int, int, enum machine_mode, tree,
+ enum machine_mode, int, int, int));
+static int get_inner_unaligned_p PROTO((tree));
+static tree save_noncopied_parts PROTO((tree, tree));
+static tree init_noncopied_parts PROTO((tree, tree));
+static int safe_from_p PROTO((rtx, tree));
+static int fixed_type_p PROTO((tree));
+static int get_pointer_alignment PROTO((tree, unsigned));
+static tree string_constant PROTO((tree, tree *));
+static tree c_strlen PROTO((tree));
+static rtx expand_builtin PROTO((tree, rtx, rtx,
+ enum machine_mode, int));
+static int apply_args_size PROTO((void));
+static int apply_result_size PROTO((void));
+static rtx result_vector PROTO((int, rtx));
+static rtx expand_builtin_apply_args PROTO((void));
+static rtx expand_builtin_apply PROTO((rtx, rtx, rtx));
+static void expand_builtin_return PROTO((rtx));
+static rtx expand_increment PROTO((tree, int));
+rtx bc_expand_increment PROTO((struct increment_operator *, tree));
+tree bc_runtime_type_code PROTO((tree));
+rtx bc_allocate_local PROTO((int, int));
+void bc_store_memory PROTO((tree, tree));
+tree bc_expand_component_address PROTO((tree));
+tree bc_expand_address PROTO((tree));
+void bc_expand_constructor PROTO((tree));
+void bc_adjust_stack PROTO((int));
+tree bc_canonicalize_array_ref PROTO((tree));
+void bc_load_memory PROTO((tree, tree));
+void bc_load_externaddr PROTO((rtx));
+void bc_load_externaddr_id PROTO((tree, int));
+void bc_load_localaddr PROTO((rtx));
+void bc_load_parmaddr PROTO((rtx));
+static void preexpand_calls PROTO((tree));
+static void do_jump_by_parts_greater PROTO((tree, int, rtx, rtx));
+static void do_jump_by_parts_greater_rtx PROTO((enum machine_mode, int, rtx, rtx, rtx, rtx));
+static void do_jump_by_parts_equality PROTO((tree, rtx, rtx));
+static void do_jump_by_parts_equality_rtx PROTO((rtx, rtx, rtx));
+static void do_jump_for_compare PROTO((rtx, rtx, rtx));
+static rtx compare PROTO((tree, enum rtx_code, enum rtx_code));
+static rtx do_store_flag PROTO((tree, rtx, enum machine_mode, int));
+static tree defer_cleanups_to PROTO((tree));
+extern void (*interim_eh_hook) PROTO((tree));
+
+/* Record for each mode whether we can move a register directly to or
+ from an object of that mode in memory. If we can't, we won't try
+ to use that mode directly when accessing a field of that mode. */
+
+static char direct_load[NUM_MACHINE_MODES];
+static char direct_store[NUM_MACHINE_MODES];
+
+/* MOVE_RATIO is the number of move instructions that is better than
+ a block move. */
+
+#ifndef MOVE_RATIO
+#if defined (HAVE_movstrqi) || defined (HAVE_movstrhi) || defined (HAVE_movstrsi) || defined (HAVE_movstrdi) || defined (HAVE_movstrti)
+#define MOVE_RATIO 2
+#else
+/* A value of around 6 would minimize code size; infinity would minimize
+ execution time. */
+#define MOVE_RATIO 15
+#endif
+#endif
+
+/* This array records the insn_code of insns to perform block moves. */
+enum insn_code movstr_optab[NUM_MACHINE_MODES];
+
+/* SLOW_UNALIGNED_ACCESS is non-zero if unaligned accesses are very slow. */
+
+#ifndef SLOW_UNALIGNED_ACCESS
+#define SLOW_UNALIGNED_ACCESS 0
+#endif
+
+/* Register mappings for target machines without register windows. */
+#ifndef INCOMING_REGNO
+#define INCOMING_REGNO(OUT) (OUT)
+#endif
+#ifndef OUTGOING_REGNO
+#define OUTGOING_REGNO(IN) (IN)
+#endif
+
+/* Maps used to convert modes to const, load, and store bytecodes. */
+enum bytecode_opcode mode_to_const_map[MAX_MACHINE_MODE];
+enum bytecode_opcode mode_to_load_map[MAX_MACHINE_MODE];
+enum bytecode_opcode mode_to_store_map[MAX_MACHINE_MODE];
+
+/* Initialize maps used to convert modes to const, load, and store
+ bytecodes. */
+void
+bc_init_mode_to_opcode_maps ()
+{
+ int mode;
+
+ for (mode = 0; mode < (int) MAX_MACHINE_MODE; mode++)
+ mode_to_const_map[mode] =
+ mode_to_load_map[mode] =
+ mode_to_store_map[mode] = neverneverland;
+
+#define DEF_MODEMAP(SYM, CODE, UCODE, CONST, LOAD, STORE) \
+ mode_to_const_map[(int) SYM] = CONST; \
+ mode_to_load_map[(int) SYM] = LOAD; \
+ mode_to_store_map[(int) SYM] = STORE;
+
+#include "modemap.def"
+#undef DEF_MODEMAP
+}
+
+/* This is run once per compilation to set up which modes can be used
+ directly in memory and to initialize the block move optab. */
+
+void
+init_expr_once ()
+{
+ rtx insn, pat;
+ enum machine_mode mode;
+ /* Try indexing by frame ptr and try by stack ptr.
+ It is known that on the Convex the stack ptr isn't a valid index.
+ With luck, one or the other is valid on any machine. */
+ rtx mem = gen_rtx (MEM, VOIDmode, stack_pointer_rtx);
+ rtx mem1 = gen_rtx (MEM, VOIDmode, frame_pointer_rtx);
+
+ start_sequence ();
+ insn = emit_insn (gen_rtx (SET, 0, 0));
+ pat = PATTERN (insn);
+
+ for (mode = VOIDmode; (int) mode < NUM_MACHINE_MODES;
+ mode = (enum machine_mode) ((int) mode + 1))
+ {
+ int regno;
+ rtx reg;
+ int num_clobbers;
+
+ direct_load[(int) mode] = direct_store[(int) mode] = 0;
+ PUT_MODE (mem, mode);
+ PUT_MODE (mem1, mode);
+
+ /* See if there is some register that can be used in this mode and
+ directly loaded or stored from memory. */
+
+ if (mode != VOIDmode && mode != BLKmode)
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER
+ && (direct_load[(int) mode] == 0 || direct_store[(int) mode] == 0);
+ regno++)
+ {
+ if (! HARD_REGNO_MODE_OK (regno, mode))
+ continue;
+
+ reg = gen_rtx (REG, mode, regno);
+
+ SET_SRC (pat) = mem;
+ SET_DEST (pat) = reg;
+ if (recog (pat, insn, &num_clobbers) >= 0)
+ direct_load[(int) mode] = 1;
+
+ SET_SRC (pat) = mem1;
+ SET_DEST (pat) = reg;
+ if (recog (pat, insn, &num_clobbers) >= 0)
+ direct_load[(int) mode] = 1;
+
+ SET_SRC (pat) = reg;
+ SET_DEST (pat) = mem;
+ if (recog (pat, insn, &num_clobbers) >= 0)
+ direct_store[(int) mode] = 1;
+
+ SET_SRC (pat) = reg;
+ SET_DEST (pat) = mem1;
+ if (recog (pat, insn, &num_clobbers) >= 0)
+ direct_store[(int) mode] = 1;
+ }
+ }
+
+ end_sequence ();
+}
+
+/* This is run at the start of compiling a function. */
+
+void
+init_expr ()
+{
+ init_queue ();
+
+ pending_stack_adjust = 0;
+ inhibit_defer_pop = 0;
+ cleanups_this_call = 0;
+ saveregs_value = 0;
+ apply_args_value = 0;
+ forced_labels = 0;
+}
+
+/* Save all variables describing the current status into the structure *P.
+ This is used before starting a nested function. */
+
+void
+save_expr_status (p)
+ struct function *p;
+{
+ /* Instead of saving the postincrement queue, empty it. */
+ emit_queue ();
+
+ p->pending_stack_adjust = pending_stack_adjust;
+ p->inhibit_defer_pop = inhibit_defer_pop;
+ p->cleanups_this_call = cleanups_this_call;
+ p->saveregs_value = saveregs_value;
+ p->apply_args_value = apply_args_value;
+ p->forced_labels = forced_labels;
+
+ pending_stack_adjust = 0;
+ inhibit_defer_pop = 0;
+ cleanups_this_call = 0;
+ saveregs_value = 0;
+ apply_args_value = 0;
+ forced_labels = 0;
+}
+
+/* Restore all variables describing the current status from the structure *P.
+ This is used after a nested function. */
+
+void
+restore_expr_status (p)
+ struct function *p;
+{
+ pending_stack_adjust = p->pending_stack_adjust;
+ inhibit_defer_pop = p->inhibit_defer_pop;
+ cleanups_this_call = p->cleanups_this_call;
+ saveregs_value = p->saveregs_value;
+ apply_args_value = p->apply_args_value;
+ forced_labels = p->forced_labels;
+}
+
+/* Manage the queue of increment instructions to be output
+ for POSTINCREMENT_EXPR expressions, etc. */
+
+static rtx pending_chain;
+
+/* Queue up to increment (or change) VAR later. BODY says how:
+ BODY should be the same thing you would pass to emit_insn
+ to increment right away. It will go to emit_insn later on.
+
+ The value is a QUEUED expression to be used in place of VAR
+ where you want to guarantee the pre-incrementation value of VAR. */
+
+static rtx
+enqueue_insn (var, body)
+ rtx var, body;
+{
+ pending_chain = gen_rtx (QUEUED, GET_MODE (var),
+ var, NULL_RTX, NULL_RTX, body, pending_chain);
+ return pending_chain;
+}
+
+/* Use protect_from_queue to convert a QUEUED expression
+ into something that you can put immediately into an instruction.
+ If the queued incrementation has not happened yet,
+ protect_from_queue returns the variable itself.
+ If the incrementation has happened, protect_from_queue returns a temp
+ that contains a copy of the old value of the variable.
+
+ Any time an rtx which might possibly be a QUEUED is to be put
+ into an instruction, it must be passed through protect_from_queue first.
+ QUEUED expressions are not meaningful in instructions.
+
+ Do not pass a value through protect_from_queue and then hold
+ on to it for a while before putting it in an instruction!
+ If the queue is flushed in between, incorrect code will result. */
+
+rtx
+protect_from_queue (x, modify)
+ register rtx x;
+ int modify;
+{
+ register RTX_CODE code = GET_CODE (x);
+
+#if 0 /* A QUEUED can hang around after the queue is forced out. */
+ /* Shortcut for most common case. */
+ if (pending_chain == 0)
+ return x;
+#endif
+
+ if (code != QUEUED)
+ {
+ /* A special hack for read access to (MEM (QUEUED ...)) to facilitate
+ use of autoincrement. Make a copy of the contents of the memory
+ location rather than a copy of the address, but not if the value is
+ of mode BLKmode. Don't modify X in place since it might be
+ shared. */
+ if (code == MEM && GET_MODE (x) != BLKmode
+ && GET_CODE (XEXP (x, 0)) == QUEUED && !modify)
+ {
+ register rtx y = XEXP (x, 0);
+ register rtx new = gen_rtx (MEM, GET_MODE (x), QUEUED_VAR (y));
+
+ MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (x);
+ RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (x);
+ MEM_VOLATILE_P (new) = MEM_VOLATILE_P (x);
+
+ if (QUEUED_INSN (y))
+ {
+ register rtx temp = gen_reg_rtx (GET_MODE (new));
+ emit_insn_before (gen_move_insn (temp, new),
+ QUEUED_INSN (y));
+ return temp;
+ }
+ return new;
+ }
+ /* Otherwise, recursively protect the subexpressions of all
+ the kinds of rtx's that can contain a QUEUED. */
+ if (code == MEM)
+ {
+ rtx tem = protect_from_queue (XEXP (x, 0), 0);
+ if (tem != XEXP (x, 0))
+ {
+ x = copy_rtx (x);
+ XEXP (x, 0) = tem;
+ }
+ }
+ else if (code == PLUS || code == MULT)
+ {
+ rtx new0 = protect_from_queue (XEXP (x, 0), 0);
+ rtx new1 = protect_from_queue (XEXP (x, 1), 0);
+ if (new0 != XEXP (x, 0) || new1 != XEXP (x, 1))
+ {
+ x = copy_rtx (x);
+ XEXP (x, 0) = new0;
+ XEXP (x, 1) = new1;
+ }
+ }
+ return x;
+ }
+ /* If the increment has not happened, use the variable itself. */
+ if (QUEUED_INSN (x) == 0)
+ return QUEUED_VAR (x);
+ /* If the increment has happened and a pre-increment copy exists,
+ use that copy. */
+ if (QUEUED_COPY (x) != 0)
+ return QUEUED_COPY (x);
+ /* The increment has happened but we haven't set up a pre-increment copy.
+ Set one up now, and use it. */
+ QUEUED_COPY (x) = gen_reg_rtx (GET_MODE (QUEUED_VAR (x)));
+ emit_insn_before (gen_move_insn (QUEUED_COPY (x), QUEUED_VAR (x)),
+ QUEUED_INSN (x));
+ return QUEUED_COPY (x);
+}
+
+/* Return nonzero if X contains a QUEUED expression:
+ if it contains anything that will be altered by a queued increment.
+ We handle only combinations of MEM, PLUS, MINUS and MULT operators
+ since memory addresses generally contain only those. */
+
+static int
+queued_subexp_p (x)
+ rtx x;
+{
+ register enum rtx_code code = GET_CODE (x);
+ switch (code)
+ {
+ case QUEUED:
+ return 1;
+ case MEM:
+ return queued_subexp_p (XEXP (x, 0));
+ case MULT:
+ case PLUS:
+ case MINUS:
+ return queued_subexp_p (XEXP (x, 0))
+ || queued_subexp_p (XEXP (x, 1));
+ }
+ return 0;
+}
+
+/* Perform all the pending incrementations. */
+
+void
+emit_queue ()
+{
+ register rtx p;
+ while (p = pending_chain)
+ {
+ QUEUED_INSN (p) = emit_insn (QUEUED_BODY (p));
+ pending_chain = QUEUED_NEXT (p);
+ }
+}
+
+static void
+init_queue ()
+{
+ if (pending_chain)
+ abort ();
+}
+
+/* Copy data from FROM to TO, where the machine modes are not the same.
+ Both modes may be integer, or both may be floating.
+ UNSIGNEDP should be nonzero if FROM is an unsigned type.
+ This causes zero-extension instead of sign-extension. */
+
+void
+convert_move (to, from, unsignedp)
+ register rtx to, from;
+ int unsignedp;
+{
+ enum machine_mode to_mode = GET_MODE (to);
+ enum machine_mode from_mode = GET_MODE (from);
+ int to_real = GET_MODE_CLASS (to_mode) == MODE_FLOAT;
+ int from_real = GET_MODE_CLASS (from_mode) == MODE_FLOAT;
+ enum insn_code code;
+ rtx libcall;
+
+ /* rtx code for making an equivalent value. */
+ enum rtx_code equiv_code = (unsignedp ? ZERO_EXTEND : SIGN_EXTEND);
+
+ to = protect_from_queue (to, 1);
+ from = protect_from_queue (from, 0);
+
+ if (to_real != from_real)
+ abort ();
+
+ /* If FROM is a SUBREG that indicates that we have already done at least
+ the required extension, strip it. We don't handle such SUBREGs as
+ TO here. */
+
+ if (GET_CODE (from) == SUBREG && SUBREG_PROMOTED_VAR_P (from)
+ && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (from)))
+ >= GET_MODE_SIZE (to_mode))
+ && SUBREG_PROMOTED_UNSIGNED_P (from) == unsignedp)
+ from = gen_lowpart (to_mode, from), from_mode = to_mode;
+
+ if (GET_CODE (to) == SUBREG && SUBREG_PROMOTED_VAR_P (to))
+ abort ();
+
+ if (to_mode == from_mode
+ || (from_mode == VOIDmode && CONSTANT_P (from)))
+ {
+ emit_move_insn (to, from);
+ return;
+ }
+
+ if (to_real)
+ {
+ rtx value;
+
+#ifdef HAVE_extendqfhf2
+ if (HAVE_extendqfsf2 && from_mode == QFmode && to_mode == HFmode)
+ {
+ emit_unop_insn (CODE_FOR_extendqfsf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_extendqfsf2
+ if (HAVE_extendqfsf2 && from_mode == QFmode && to_mode == SFmode)
+ {
+ emit_unop_insn (CODE_FOR_extendqfsf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_extendqfdf2
+ if (HAVE_extendqfdf2 && from_mode == QFmode && to_mode == DFmode)
+ {
+ emit_unop_insn (CODE_FOR_extendqfdf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_extendqfxf2
+ if (HAVE_extendqfxf2 && from_mode == QFmode && to_mode == XFmode)
+ {
+ emit_unop_insn (CODE_FOR_extendqfxf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_extendqftf2
+ if (HAVE_extendqftf2 && from_mode == QFmode && to_mode == TFmode)
+ {
+ emit_unop_insn (CODE_FOR_extendqftf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+
+#ifdef HAVE_extendhftqf2
+ if (HAVE_extendhftqf2 && from_mode == HFmode && to_mode == TQFmode)
+ {
+ emit_unop_insn (CODE_FOR_extendhftqf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+
+#ifdef HAVE_extendhfsf2
+ if (HAVE_extendhfsf2 && from_mode == HFmode && to_mode == SFmode)
+ {
+ emit_unop_insn (CODE_FOR_extendhfsf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_extendhfdf2
+ if (HAVE_extendhfdf2 && from_mode == HFmode && to_mode == DFmode)
+ {
+ emit_unop_insn (CODE_FOR_extendhfdf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_extendhfxf2
+ if (HAVE_extendhfxf2 && from_mode == HFmode && to_mode == XFmode)
+ {
+ emit_unop_insn (CODE_FOR_extendhfxf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_extendhftf2
+ if (HAVE_extendhftf2 && from_mode == HFmode && to_mode == TFmode)
+ {
+ emit_unop_insn (CODE_FOR_extendhftf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+
+#ifdef HAVE_extendsfdf2
+ if (HAVE_extendsfdf2 && from_mode == SFmode && to_mode == DFmode)
+ {
+ emit_unop_insn (CODE_FOR_extendsfdf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_extendsfxf2
+ if (HAVE_extendsfxf2 && from_mode == SFmode && to_mode == XFmode)
+ {
+ emit_unop_insn (CODE_FOR_extendsfxf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_extendsftf2
+ if (HAVE_extendsftf2 && from_mode == SFmode && to_mode == TFmode)
+ {
+ emit_unop_insn (CODE_FOR_extendsftf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_extenddfxf2
+ if (HAVE_extenddfxf2 && from_mode == DFmode && to_mode == XFmode)
+ {
+ emit_unop_insn (CODE_FOR_extenddfxf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_extenddftf2
+ if (HAVE_extenddftf2 && from_mode == DFmode && to_mode == TFmode)
+ {
+ emit_unop_insn (CODE_FOR_extenddftf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+
+#ifdef HAVE_trunchfqf2
+ if (HAVE_trunchfqf2 && from_mode == HFmode && to_mode == QFmode)
+ {
+ emit_unop_insn (CODE_FOR_trunchfqf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_truncsfqf2
+ if (HAVE_truncsfqf2 && from_mode == SFmode && to_mode == QFmode)
+ {
+ emit_unop_insn (CODE_FOR_truncsfqf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_truncdfqf2
+ if (HAVE_truncdfqf2 && from_mode == DFmode && to_mode == QFmode)
+ {
+ emit_unop_insn (CODE_FOR_truncdfqf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_truncxfqf2
+ if (HAVE_truncxfqf2 && from_mode == XFmode && to_mode == QFmode)
+ {
+ emit_unop_insn (CODE_FOR_truncxfqf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_trunctfqf2
+ if (HAVE_trunctfqf2 && from_mode == TFmode && to_mode == QFmode)
+ {
+ emit_unop_insn (CODE_FOR_trunctfqf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+
+#ifdef HAVE_trunctqfhf2
+ if (HAVE_trunctqfhf2 && from_mode == TQFmode && to_mode == HFmode)
+ {
+ emit_unop_insn (CODE_FOR_trunctqfhf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_truncsfhf2
+ if (HAVE_truncsfhf2 && from_mode == SFmode && to_mode == HFmode)
+ {
+ emit_unop_insn (CODE_FOR_truncsfhf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_truncdfhf2
+ if (HAVE_truncdfhf2 && from_mode == DFmode && to_mode == HFmode)
+ {
+ emit_unop_insn (CODE_FOR_truncdfhf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_truncxfhf2
+ if (HAVE_truncxfhf2 && from_mode == XFmode && to_mode == HFmode)
+ {
+ emit_unop_insn (CODE_FOR_truncxfhf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_trunctfhf2
+ if (HAVE_trunctfhf2 && from_mode == TFmode && to_mode == HFmode)
+ {
+ emit_unop_insn (CODE_FOR_trunctfhf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_truncdfsf2
+ if (HAVE_truncdfsf2 && from_mode == DFmode && to_mode == SFmode)
+ {
+ emit_unop_insn (CODE_FOR_truncdfsf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_truncxfsf2
+ if (HAVE_truncxfsf2 && from_mode == XFmode && to_mode == SFmode)
+ {
+ emit_unop_insn (CODE_FOR_truncxfsf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_trunctfsf2
+ if (HAVE_trunctfsf2 && from_mode == TFmode && to_mode == SFmode)
+ {
+ emit_unop_insn (CODE_FOR_trunctfsf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_truncxfdf2
+ if (HAVE_truncxfdf2 && from_mode == XFmode && to_mode == DFmode)
+ {
+ emit_unop_insn (CODE_FOR_truncxfdf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+#ifdef HAVE_trunctfdf2
+ if (HAVE_trunctfdf2 && from_mode == TFmode && to_mode == DFmode)
+ {
+ emit_unop_insn (CODE_FOR_trunctfdf2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+
+ libcall = (rtx) 0;
+ switch (from_mode)
+ {
+ case SFmode:
+ switch (to_mode)
+ {
+ case DFmode:
+ libcall = extendsfdf2_libfunc;
+ break;
+
+ case XFmode:
+ libcall = extendsfxf2_libfunc;
+ break;
+
+ case TFmode:
+ libcall = extendsftf2_libfunc;
+ break;
+ }
+ break;
+
+ case DFmode:
+ switch (to_mode)
+ {
+ case SFmode:
+ libcall = truncdfsf2_libfunc;
+ break;
+
+ case XFmode:
+ libcall = extenddfxf2_libfunc;
+ break;
+
+ case TFmode:
+ libcall = extenddftf2_libfunc;
+ break;
+ }
+ break;
+
+ case XFmode:
+ switch (to_mode)
+ {
+ case SFmode:
+ libcall = truncxfsf2_libfunc;
+ break;
+
+ case DFmode:
+ libcall = truncxfdf2_libfunc;
+ break;
+ }
+ break;
+
+ case TFmode:
+ switch (to_mode)
+ {
+ case SFmode:
+ libcall = trunctfsf2_libfunc;
+ break;
+
+ case DFmode:
+ libcall = trunctfdf2_libfunc;
+ break;
+ }
+ break;
+ }
+
+ if (libcall == (rtx) 0)
+ /* This conversion is not implemented yet. */
+ abort ();
+
+ value = emit_library_call_value (libcall, NULL_RTX, 1, to_mode,
+ 1, from, from_mode);
+ emit_move_insn (to, value);
+ return;
+ }
+
+ /* Now both modes are integers. */
+
+ /* Handle expanding beyond a word. */
+ if (GET_MODE_BITSIZE (from_mode) < GET_MODE_BITSIZE (to_mode)
+ && GET_MODE_BITSIZE (to_mode) > BITS_PER_WORD)
+ {
+ rtx insns;
+ rtx lowpart;
+ rtx fill_value;
+ rtx lowfrom;
+ int i;
+ enum machine_mode lowpart_mode;
+ int nwords = CEIL (GET_MODE_SIZE (to_mode), UNITS_PER_WORD);
+
+ /* Try converting directly if the insn is supported. */
+ if ((code = can_extend_p (to_mode, from_mode, unsignedp))
+ != CODE_FOR_nothing)
+ {
+ /* If FROM is a SUBREG, put it into a register. Do this
+ so that we always generate the same set of insns for
+ better cse'ing; if an intermediate assignment occurred,
+ we won't be doing the operation directly on the SUBREG. */
+ if (optimize > 0 && GET_CODE (from) == SUBREG)
+ from = force_reg (from_mode, from);
+ emit_unop_insn (code, to, from, equiv_code);
+ return;
+ }
+ /* Next, try converting via full word. */
+ else if (GET_MODE_BITSIZE (from_mode) < BITS_PER_WORD
+ && ((code = can_extend_p (to_mode, word_mode, unsignedp))
+ != CODE_FOR_nothing))
+ {
+ if (GET_CODE (to) == REG)
+ emit_insn (gen_rtx (CLOBBER, VOIDmode, to));
+ convert_move (gen_lowpart (word_mode, to), from, unsignedp);
+ emit_unop_insn (code, to,
+ gen_lowpart (word_mode, to), equiv_code);
+ return;
+ }
+
+ /* No special multiword conversion insn; do it by hand. */
+ start_sequence ();
+
+ /* Get a copy of FROM widened to a word, if necessary. */
+ if (GET_MODE_BITSIZE (from_mode) < BITS_PER_WORD)
+ lowpart_mode = word_mode;
+ else
+ lowpart_mode = from_mode;
+
+ lowfrom = convert_to_mode (lowpart_mode, from, unsignedp);
+
+ lowpart = gen_lowpart (lowpart_mode, to);
+ emit_move_insn (lowpart, lowfrom);
+
+ /* Compute the value to put in each remaining word. */
+ if (unsignedp)
+ fill_value = const0_rtx;
+ else
+ {
+#ifdef HAVE_slt
+ if (HAVE_slt
+ && insn_operand_mode[(int) CODE_FOR_slt][0] == word_mode
+ && STORE_FLAG_VALUE == -1)
+ {
+ emit_cmp_insn (lowfrom, const0_rtx, NE, NULL_RTX,
+ lowpart_mode, 0, 0);
+ fill_value = gen_reg_rtx (word_mode);
+ emit_insn (gen_slt (fill_value));
+ }
+ else
+#endif
+ {
+ fill_value
+ = expand_shift (RSHIFT_EXPR, lowpart_mode, lowfrom,
+ size_int (GET_MODE_BITSIZE (lowpart_mode) - 1),
+ NULL_RTX, 0);
+ fill_value = convert_to_mode (word_mode, fill_value, 1);
+ }
+ }
+
+ /* Fill the remaining words. */
+ for (i = GET_MODE_SIZE (lowpart_mode) / UNITS_PER_WORD; i < nwords; i++)
+ {
+ int index = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i);
+ rtx subword = operand_subword (to, index, 1, to_mode);
+
+ if (subword == 0)
+ abort ();
+
+ if (fill_value != subword)
+ emit_move_insn (subword, fill_value);
+ }
+
+ insns = get_insns ();
+ end_sequence ();
+
+ emit_no_conflict_block (insns, to, from, NULL_RTX,
+ gen_rtx (equiv_code, to_mode, copy_rtx (from)));
+ return;
+ }
+
+ /* Truncating multi-word to a word or less. */
+ if (GET_MODE_BITSIZE (from_mode) > BITS_PER_WORD
+ && GET_MODE_BITSIZE (to_mode) <= BITS_PER_WORD)
+ {
+ if (!((GET_CODE (from) == MEM
+ && ! MEM_VOLATILE_P (from)
+ && direct_load[(int) to_mode]
+ && ! mode_dependent_address_p (XEXP (from, 0)))
+ || GET_CODE (from) == REG
+ || GET_CODE (from) == SUBREG))
+ from = force_reg (from_mode, from);
+ convert_move (to, gen_lowpart (word_mode, from), 0);
+ return;
+ }
+
+ /* Handle pointer conversion */ /* SPEE 900220 */
+ if (to_mode == PSImode)
+ {
+ if (from_mode != SImode)
+ from = convert_to_mode (SImode, from, unsignedp);
+
+#ifdef HAVE_truncsipsi2
+ if (HAVE_truncsipsi2)
+ {
+ emit_unop_insn (CODE_FOR_truncsipsi2, to, from, UNKNOWN);
+ return;
+ }
+#endif /* HAVE_truncsipsi2 */
+ abort ();
+ }
+
+ if (from_mode == PSImode)
+ {
+ if (to_mode != SImode)
+ {
+ from = convert_to_mode (SImode, from, unsignedp);
+ from_mode = SImode;
+ }
+ else
+ {
+#ifdef HAVE_extendpsisi2
+ if (HAVE_extendpsisi2)
+ {
+ emit_unop_insn (CODE_FOR_extendpsisi2, to, from, UNKNOWN);
+ return;
+ }
+#endif /* HAVE_extendpsisi2 */
+ abort ();
+ }
+ }
+
+ /* Now follow all the conversions between integers
+ no more than a word long. */
+
+ /* For truncation, usually we can just refer to FROM in a narrower mode. */
+ if (GET_MODE_BITSIZE (to_mode) < GET_MODE_BITSIZE (from_mode)
+ && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (to_mode),
+ GET_MODE_BITSIZE (from_mode)))
+ {
+ if (!((GET_CODE (from) == MEM
+ && ! MEM_VOLATILE_P (from)
+ && direct_load[(int) to_mode]
+ && ! mode_dependent_address_p (XEXP (from, 0)))
+ || GET_CODE (from) == REG
+ || GET_CODE (from) == SUBREG))
+ from = force_reg (from_mode, from);
+ emit_move_insn (to, gen_lowpart (to_mode, from));
+ return;
+ }
+
+ /* Handle extension. */
+ if (GET_MODE_BITSIZE (to_mode) > GET_MODE_BITSIZE (from_mode))
+ {
+ /* Convert directly if that works. */
+ if ((code = can_extend_p (to_mode, from_mode, unsignedp))
+ != CODE_FOR_nothing)
+ {
+ /* If FROM is a SUBREG, put it into a register. Do this
+ so that we always generate the same set of insns for
+ better cse'ing; if an intermediate assignment occurred,
+ we won't be doing the operation directly on the SUBREG. */
+ if (optimize > 0 && GET_CODE (from) == SUBREG)
+ from = force_reg (from_mode, from);
+ emit_unop_insn (code, to, from, equiv_code);
+ return;
+ }
+ else
+ {
+ enum machine_mode intermediate;
+
+ /* Search for a mode to convert via. */
+ for (intermediate = from_mode; intermediate != VOIDmode;
+ intermediate = GET_MODE_WIDER_MODE (intermediate))
+ if (((can_extend_p (to_mode, intermediate, unsignedp)
+ != CODE_FOR_nothing)
+ || (GET_MODE_SIZE (to_mode) < GET_MODE_SIZE (intermediate)
+ && TRULY_NOOP_TRUNCATION (to_mode, intermediate)))
+ && (can_extend_p (intermediate, from_mode, unsignedp)
+ != CODE_FOR_nothing))
+ {
+ convert_move (to, convert_to_mode (intermediate, from,
+ unsignedp), unsignedp);
+ return;
+ }
+
+ /* No suitable intermediate mode. */
+ abort ();
+ }
+ }
+
+ /* Support special truncate insns for certain modes. */
+
+ if (from_mode == DImode && to_mode == SImode)
+ {
+#ifdef HAVE_truncdisi2
+ if (HAVE_truncdisi2)
+ {
+ emit_unop_insn (CODE_FOR_truncdisi2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+ convert_move (to, force_reg (from_mode, from), unsignedp);
+ return;
+ }
+
+ if (from_mode == DImode && to_mode == HImode)
+ {
+#ifdef HAVE_truncdihi2
+ if (HAVE_truncdihi2)
+ {
+ emit_unop_insn (CODE_FOR_truncdihi2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+ convert_move (to, force_reg (from_mode, from), unsignedp);
+ return;
+ }
+
+ if (from_mode == DImode && to_mode == QImode)
+ {
+#ifdef HAVE_truncdiqi2
+ if (HAVE_truncdiqi2)
+ {
+ emit_unop_insn (CODE_FOR_truncdiqi2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+ convert_move (to, force_reg (from_mode, from), unsignedp);
+ return;
+ }
+
+ if (from_mode == SImode && to_mode == HImode)
+ {
+#ifdef HAVE_truncsihi2
+ if (HAVE_truncsihi2)
+ {
+ emit_unop_insn (CODE_FOR_truncsihi2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+ convert_move (to, force_reg (from_mode, from), unsignedp);
+ return;
+ }
+
+ if (from_mode == SImode && to_mode == QImode)
+ {
+#ifdef HAVE_truncsiqi2
+ if (HAVE_truncsiqi2)
+ {
+ emit_unop_insn (CODE_FOR_truncsiqi2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+ convert_move (to, force_reg (from_mode, from), unsignedp);
+ return;
+ }
+
+ if (from_mode == HImode && to_mode == QImode)
+ {
+#ifdef HAVE_trunchiqi2
+ if (HAVE_trunchiqi2)
+ {
+ emit_unop_insn (CODE_FOR_trunchiqi2, to, from, UNKNOWN);
+ return;
+ }
+#endif
+ convert_move (to, force_reg (from_mode, from), unsignedp);
+ return;
+ }
+
+ /* Handle truncation of volatile memrefs, and so on;
+ the things that couldn't be truncated directly,
+ and for which there was no special instruction. */
+ if (GET_MODE_BITSIZE (to_mode) < GET_MODE_BITSIZE (from_mode))
+ {
+ rtx temp = force_reg (to_mode, gen_lowpart (to_mode, from));
+ emit_move_insn (to, temp);
+ return;
+ }
+
+ /* Mode combination is not recognized. */
+ abort ();
+}
+
+/* Return an rtx for a value that would result
+ from converting X to mode MODE.
+ Both X and MODE may be floating, or both integer.
+ UNSIGNEDP is nonzero if X is an unsigned value.
+ This can be done by referring to a part of X in place
+ or by copying to a new temporary with conversion.
+
+ This function *must not* call protect_from_queue
+ except when putting X into an insn (in which case convert_move does it). */
+
+rtx
+convert_to_mode (mode, x, unsignedp)
+ enum machine_mode mode;
+ rtx x;
+ int unsignedp;
+{
+ return convert_modes (mode, VOIDmode, x, unsignedp);
+}
+
+/* Return an rtx for a value that would result
+ from converting X from mode OLDMODE to mode MODE.
+ Both modes may be floating, or both integer.
+ UNSIGNEDP is nonzero if X is an unsigned value.
+
+ This can be done by referring to a part of X in place
+ or by copying to a new temporary with conversion.
+
+ You can give VOIDmode for OLDMODE, if you are sure X has a nonvoid mode.
+
+ This function *must not* call protect_from_queue
+ except when putting X into an insn (in which case convert_move does it). */
+
+rtx
+convert_modes (mode, oldmode, x, unsignedp)
+ enum machine_mode mode, oldmode;
+ rtx x;
+ int unsignedp;
+{
+ register rtx temp;
+
+ /* If FROM is a SUBREG that indicates that we have already done at least
+ the required extension, strip it. */
+
+ if (GET_CODE (x) == SUBREG && SUBREG_PROMOTED_VAR_P (x)
+ && GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) >= GET_MODE_SIZE (mode)
+ && SUBREG_PROMOTED_UNSIGNED_P (x) == unsignedp)
+ x = gen_lowpart (mode, x);
+
+ if (GET_MODE (x) != VOIDmode)
+ oldmode = GET_MODE (x);
+
+ if (mode == oldmode)
+ return x;
+
+ /* There is one case that we must handle specially: If we are converting
+ a CONST_INT into a mode whose size is twice HOST_BITS_PER_WIDE_INT and
+ we are to interpret the constant as unsigned, gen_lowpart will do
+ the wrong if the constant appears negative. What we want to do is
+ make the high-order word of the constant zero, not all ones. */
+
+ if (unsignedp && GET_MODE_CLASS (mode) == MODE_INT
+ && GET_MODE_BITSIZE (mode) == 2 * HOST_BITS_PER_WIDE_INT
+ && GET_CODE (x) == CONST_INT && INTVAL (x) < 0)
+ return immed_double_const (INTVAL (x), (HOST_WIDE_INT) 0, mode);
+
+ /* We can do this with a gen_lowpart if both desired and current modes
+ are integer, and this is either a constant integer, a register, or a
+ non-volatile MEM. Except for the constant case where MODE is no
+ wider than HOST_BITS_PER_WIDE_INT, we must be narrowing the operand. */
+
+ if ((GET_CODE (x) == CONST_INT
+ && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
+ || (GET_MODE_CLASS (mode) == MODE_INT
+ && GET_MODE_CLASS (oldmode) == MODE_INT
+ && (GET_CODE (x) == CONST_DOUBLE
+ || (GET_MODE_SIZE (mode) <= GET_MODE_SIZE (oldmode)
+ && ((GET_CODE (x) == MEM && ! MEM_VOLATILE_P (x)
+ && direct_load[(int) mode])
+ || (GET_CODE (x) == REG
+ && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
+ GET_MODE_BITSIZE (GET_MODE (x)))))))))
+ {
+ /* ?? If we don't know OLDMODE, we have to assume here that
+ X does not need sign- or zero-extension. This may not be
+ the case, but it's the best we can do. */
+ if (GET_CODE (x) == CONST_INT && oldmode != VOIDmode
+ && GET_MODE_SIZE (mode) > GET_MODE_SIZE (oldmode))
+ {
+ HOST_WIDE_INT val = INTVAL (x);
+ int width = GET_MODE_BITSIZE (oldmode);
+
+ /* We must sign or zero-extend in this case. Start by
+ zero-extending, then sign extend if we need to. */
+ val &= ((HOST_WIDE_INT) 1 << width) - 1;
+ if (! unsignedp
+ && (val & ((HOST_WIDE_INT) 1 << (width - 1))))
+ val |= (HOST_WIDE_INT) (-1) << width;
+
+ return GEN_INT (val);
+ }
+
+ return gen_lowpart (mode, x);
+ }
+
+ temp = gen_reg_rtx (mode);
+ convert_move (temp, x, unsignedp);
+ return temp;
+}
+
+/* Generate several move instructions to copy LEN bytes
+ from block FROM to block TO. (These are MEM rtx's with BLKmode).
+ The caller must pass FROM and TO
+ through protect_from_queue before calling.
+ ALIGN (in bytes) is maximum alignment we can assume. */
+
+static void
+move_by_pieces (to, from, len, align)
+ rtx to, from;
+ int len, align;
+{
+ struct move_by_pieces data;
+ rtx to_addr = XEXP (to, 0), from_addr = XEXP (from, 0);
+ int max_size = MOVE_MAX + 1;
+
+ data.offset = 0;
+ data.to_addr = to_addr;
+ data.from_addr = from_addr;
+ data.to = to;
+ data.from = from;
+ data.autinc_to
+ = (GET_CODE (to_addr) == PRE_INC || GET_CODE (to_addr) == PRE_DEC
+ || GET_CODE (to_addr) == POST_INC || GET_CODE (to_addr) == POST_DEC);
+ data.autinc_from
+ = (GET_CODE (from_addr) == PRE_INC || GET_CODE (from_addr) == PRE_DEC
+ || GET_CODE (from_addr) == POST_INC
+ || GET_CODE (from_addr) == POST_DEC);
+
+ data.explicit_inc_from = 0;
+ data.explicit_inc_to = 0;
+ data.reverse
+ = (GET_CODE (to_addr) == PRE_DEC || GET_CODE (to_addr) == POST_DEC);
+ if (data.reverse) data.offset = len;
+ data.len = len;
+
+ /* If copying requires more than two move insns,
+ copy addresses to registers (to make displacements shorter)
+ and use post-increment if available. */
+ if (!(data.autinc_from && data.autinc_to)
+ && move_by_pieces_ninsns (len, align) > 2)
+ {
+#ifdef HAVE_PRE_DECREMENT
+ if (data.reverse && ! data.autinc_from)
+ {
+ data.from_addr = copy_addr_to_reg (plus_constant (from_addr, len));
+ data.autinc_from = 1;
+ data.explicit_inc_from = -1;
+ }
+#endif
+#ifdef HAVE_POST_INCREMENT
+ if (! data.autinc_from)
+ {
+ data.from_addr = copy_addr_to_reg (from_addr);
+ data.autinc_from = 1;
+ data.explicit_inc_from = 1;
+ }
+#endif
+ if (!data.autinc_from && CONSTANT_P (from_addr))
+ data.from_addr = copy_addr_to_reg (from_addr);
+#ifdef HAVE_PRE_DECREMENT
+ if (data.reverse && ! data.autinc_to)
+ {
+ data.to_addr = copy_addr_to_reg (plus_constant (to_addr, len));
+ data.autinc_to = 1;
+ data.explicit_inc_to = -1;
+ }
+#endif
+#ifdef HAVE_POST_INCREMENT
+ if (! data.reverse && ! data.autinc_to)
+ {
+ data.to_addr = copy_addr_to_reg (to_addr);
+ data.autinc_to = 1;
+ data.explicit_inc_to = 1;
+ }
+#endif
+ if (!data.autinc_to && CONSTANT_P (to_addr))
+ data.to_addr = copy_addr_to_reg (to_addr);
+ }
+
+ if (! (STRICT_ALIGNMENT || SLOW_UNALIGNED_ACCESS)
+ || align > MOVE_MAX || align >= BIGGEST_ALIGNMENT / BITS_PER_UNIT)
+ align = MOVE_MAX;
+
+ /* First move what we can in the largest integer mode, then go to
+ successively smaller modes. */
+
+ while (max_size > 1)
+ {
+ enum machine_mode mode = VOIDmode, tmode;
+ enum insn_code icode;
+
+ for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+ tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode))
+ if (GET_MODE_SIZE (tmode) < max_size)
+ mode = tmode;
+
+ if (mode == VOIDmode)
+ break;
+
+ icode = mov_optab->handlers[(int) mode].insn_code;
+ if (icode != CODE_FOR_nothing
+ && align >= MIN (BIGGEST_ALIGNMENT / BITS_PER_UNIT,
+ GET_MODE_SIZE (mode)))
+ move_by_pieces_1 (GEN_FCN (icode), mode, &data);
+
+ max_size = GET_MODE_SIZE (mode);
+ }
+
+ /* The code above should have handled everything. */
+ if (data.len != 0)
+ abort ();
+}
+
+/* Return number of insns required to move L bytes by pieces.
+ ALIGN (in bytes) is maximum alignment we can assume. */
+
+static int
+move_by_pieces_ninsns (l, align)
+ unsigned int l;
+ int align;
+{
+ register int n_insns = 0;
+ int max_size = MOVE_MAX + 1;
+
+ if (! (STRICT_ALIGNMENT || SLOW_UNALIGNED_ACCESS)
+ || align > MOVE_MAX || align >= BIGGEST_ALIGNMENT / BITS_PER_UNIT)
+ align = MOVE_MAX;
+
+ while (max_size > 1)
+ {
+ enum machine_mode mode = VOIDmode, tmode;
+ enum insn_code icode;
+
+ for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+ tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode))
+ if (GET_MODE_SIZE (tmode) < max_size)
+ mode = tmode;
+
+ if (mode == VOIDmode)
+ break;
+
+ icode = mov_optab->handlers[(int) mode].insn_code;
+ if (icode != CODE_FOR_nothing
+ && align >= MIN (BIGGEST_ALIGNMENT / BITS_PER_UNIT,
+ GET_MODE_SIZE (mode)))
+ n_insns += l / GET_MODE_SIZE (mode), l %= GET_MODE_SIZE (mode);
+
+ max_size = GET_MODE_SIZE (mode);
+ }
+
+ return n_insns;
+}
+
+/* Subroutine of move_by_pieces. Move as many bytes as appropriate
+ with move instructions for mode MODE. GENFUN is the gen_... function
+ to make a move insn for that mode. DATA has all the other info. */
+
+static void
+move_by_pieces_1 (genfun, mode, data)
+ rtx (*genfun) ();
+ enum machine_mode mode;
+ struct move_by_pieces *data;
+{
+ register int size = GET_MODE_SIZE (mode);
+ register rtx to1, from1;
+
+ while (data->len >= size)
+ {
+ if (data->reverse) data->offset -= size;
+
+ to1 = (data->autinc_to
+ ? gen_rtx (MEM, mode, data->to_addr)
+ : change_address (data->to, mode,
+ plus_constant (data->to_addr, data->offset)));
+ from1 =
+ (data->autinc_from
+ ? gen_rtx (MEM, mode, data->from_addr)
+ : change_address (data->from, mode,
+ plus_constant (data->from_addr, data->offset)));
+
+#ifdef HAVE_PRE_DECREMENT
+ if (data->explicit_inc_to < 0)
+ emit_insn (gen_add2_insn (data->to_addr, GEN_INT (-size)));
+ if (data->explicit_inc_from < 0)
+ emit_insn (gen_add2_insn (data->from_addr, GEN_INT (-size)));
+#endif
+
+ emit_insn ((*genfun) (to1, from1));
+#ifdef HAVE_POST_INCREMENT
+ if (data->explicit_inc_to > 0)
+ emit_insn (gen_add2_insn (data->to_addr, GEN_INT (size)));
+ if (data->explicit_inc_from > 0)
+ emit_insn (gen_add2_insn (data->from_addr, GEN_INT (size)));
+#endif
+
+ if (! data->reverse) data->offset += size;
+
+ data->len -= size;
+ }
+}
+
+/* Emit code to move a block Y to a block X.
+ This may be done with string-move instructions,
+ with multiple scalar move instructions, or with a library call.
+
+ Both X and Y must be MEM rtx's (perhaps inside VOLATILE)
+ with mode BLKmode.
+ SIZE is an rtx that says how long they are.
+ ALIGN is the maximum alignment we can assume they have,
+ measured in bytes. */
+
+void
+emit_block_move (x, y, size, align)
+ rtx x, y;
+ rtx size;
+ int align;
+{
+ if (GET_MODE (x) != BLKmode)
+ abort ();
+
+ if (GET_MODE (y) != BLKmode)
+ abort ();
+
+ x = protect_from_queue (x, 1);
+ y = protect_from_queue (y, 0);
+ size = protect_from_queue (size, 0);
+
+ if (GET_CODE (x) != MEM)
+ abort ();
+ if (GET_CODE (y) != MEM)
+ abort ();
+ if (size == 0)
+ abort ();
+
+ if (GET_CODE (size) == CONST_INT
+ && (move_by_pieces_ninsns (INTVAL (size), align) < MOVE_RATIO))
+ move_by_pieces (x, y, INTVAL (size), align);
+ else
+ {
+ /* Try the most limited insn first, because there's no point
+ including more than one in the machine description unless
+ the more limited one has some advantage. */
+
+ rtx opalign = GEN_INT (align);
+ enum machine_mode mode;
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ {
+ enum insn_code code = movstr_optab[(int) mode];
+
+ if (code != CODE_FOR_nothing
+ /* We don't need MODE to be narrower than BITS_PER_HOST_WIDE_INT
+ here because if SIZE is less than the mode mask, as it is
+ returned by the macro, it will definitely be less than the
+ actual mode mask. */
+ && (unsigned HOST_WIDE_INT) INTVAL (size) <= GET_MODE_MASK (mode)
+ && (insn_operand_predicate[(int) code][0] == 0
+ || (*insn_operand_predicate[(int) code][0]) (x, BLKmode))
+ && (insn_operand_predicate[(int) code][1] == 0
+ || (*insn_operand_predicate[(int) code][1]) (y, BLKmode))
+ && (insn_operand_predicate[(int) code][3] == 0
+ || (*insn_operand_predicate[(int) code][3]) (opalign,
+ VOIDmode)))
+ {
+ rtx op2;
+ rtx last = get_last_insn ();
+ rtx pat;
+
+ op2 = convert_to_mode (mode, size, 1);
+ if (insn_operand_predicate[(int) code][2] != 0
+ && ! (*insn_operand_predicate[(int) code][2]) (op2, mode))
+ op2 = copy_to_mode_reg (mode, op2);
+
+ pat = GEN_FCN ((int) code) (x, y, op2, opalign);
+ if (pat)
+ {
+ emit_insn (pat);
+ return;
+ }
+ else
+ delete_insns_since (last);
+ }
+ }
+
+#ifdef TARGET_MEM_FUNCTIONS
+ emit_library_call (memcpy_libfunc, 0,
+ VOIDmode, 3, XEXP (x, 0), Pmode,
+ XEXP (y, 0), Pmode,
+ convert_to_mode (TYPE_MODE (sizetype), size,
+ TREE_UNSIGNED (sizetype)),
+ TYPE_MODE (sizetype));
+#else
+ emit_library_call (bcopy_libfunc, 0,
+ VOIDmode, 3, XEXP (y, 0), Pmode,
+ XEXP (x, 0), Pmode,
+ convert_to_mode (TYPE_MODE (sizetype), size,
+ TREE_UNSIGNED (sizetype)),
+ TYPE_MODE (sizetype));
+#endif
+ }
+}
+
+/* Copy all or part of a value X into registers starting at REGNO.
+ The number of registers to be filled is NREGS. */
+
+void
+move_block_to_reg (regno, x, nregs, mode)
+ int regno;
+ rtx x;
+ int nregs;
+ enum machine_mode mode;
+{
+ int i;
+ rtx pat, last;
+
+ if (CONSTANT_P (x) && ! LEGITIMATE_CONSTANT_P (x))
+ x = validize_mem (force_const_mem (mode, x));
+
+ /* See if the machine can do this with a load multiple insn. */
+#ifdef HAVE_load_multiple
+ if (HAVE_load_multiple)
+ {
+ last = get_last_insn ();
+ pat = gen_load_multiple (gen_rtx (REG, word_mode, regno), x,
+ GEN_INT (nregs));
+ if (pat)
+ {
+ emit_insn (pat);
+ return;
+ }
+ else
+ delete_insns_since (last);
+ }
+#endif
+
+ for (i = 0; i < nregs; i++)
+ emit_move_insn (gen_rtx (REG, word_mode, regno + i),
+ operand_subword_force (x, i, mode));
+}
+
+/* Copy all or part of a BLKmode value X out of registers starting at REGNO.
+ The number of registers to be filled is NREGS. SIZE indicates the number
+ of bytes in the object X. */
+
+
+void
+move_block_from_reg (regno, x, nregs, size)
+ int regno;
+ rtx x;
+ int nregs;
+ int size;
+{
+ int i;
+ rtx pat, last;
+
+ /* Blocks smaller than a word on a BYTES_BIG_ENDIAN machine must be aligned
+ to the left before storing to memory. */
+ if (size < UNITS_PER_WORD && BYTES_BIG_ENDIAN)
+ {
+ rtx tem = operand_subword (x, 0, 1, BLKmode);
+ rtx shift;
+
+ if (tem == 0)
+ abort ();
+
+ shift = expand_shift (LSHIFT_EXPR, word_mode,
+ gen_rtx (REG, word_mode, regno),
+ build_int_2 ((UNITS_PER_WORD - size)
+ * BITS_PER_UNIT, 0), NULL_RTX, 0);
+ emit_move_insn (tem, shift);
+ return;
+ }
+
+ /* See if the machine can do this with a store multiple insn. */
+#ifdef HAVE_store_multiple
+ if (HAVE_store_multiple)
+ {
+ last = get_last_insn ();
+ pat = gen_store_multiple (x, gen_rtx (REG, word_mode, regno),
+ GEN_INT (nregs));
+ if (pat)
+ {
+ emit_insn (pat);
+ return;
+ }
+ else
+ delete_insns_since (last);
+ }
+#endif
+
+ for (i = 0; i < nregs; i++)
+ {
+ rtx tem = operand_subword (x, i, 1, BLKmode);
+
+ if (tem == 0)
+ abort ();
+
+ emit_move_insn (tem, gen_rtx (REG, word_mode, regno + i));
+ }
+}
+
+/* Add a USE expression for REG to the (possibly empty) list pointed
+ to by CALL_FUSAGE. REG must denote a hard register. */
+
+void
+use_reg (call_fusage, reg)
+ rtx *call_fusage, reg;
+{
+ if (GET_CODE (reg) != REG
+ || REGNO (reg) >= FIRST_PSEUDO_REGISTER)
+ abort();
+
+ *call_fusage
+ = gen_rtx (EXPR_LIST, VOIDmode,
+ gen_rtx (USE, VOIDmode, reg), *call_fusage);
+}
+
+/* Add USE expressions to *CALL_FUSAGE for each of NREGS consecutive regs,
+ starting at REGNO. All of these registers must be hard registers. */
+
+void
+use_regs (call_fusage, regno, nregs)
+ rtx *call_fusage;
+ int regno;
+ int nregs;
+{
+ int i;
+
+ if (regno + nregs > FIRST_PSEUDO_REGISTER)
+ abort ();
+
+ for (i = 0; i < nregs; i++)
+ use_reg (call_fusage, gen_rtx (REG, reg_raw_mode[regno + i], regno + i));
+}
+
+/* Write zeros through the storage of OBJECT.
+ If OBJECT has BLKmode, SIZE is its length in bytes. */
+
+void
+clear_storage (object, size)
+ rtx object;
+ int size;
+{
+ if (GET_MODE (object) == BLKmode)
+ {
+#ifdef TARGET_MEM_FUNCTIONS
+ emit_library_call (memset_libfunc, 0,
+ VOIDmode, 3,
+ XEXP (object, 0), Pmode, const0_rtx, Pmode,
+ GEN_INT (size), Pmode);
+#else
+ emit_library_call (bzero_libfunc, 0,
+ VOIDmode, 2,
+ XEXP (object, 0), Pmode,
+ GEN_INT (size), Pmode);
+#endif
+ }
+ else
+ emit_move_insn (object, const0_rtx);
+}
+
+/* Generate code to copy Y into X.
+ Both Y and X must have the same mode, except that
+ Y can be a constant with VOIDmode.
+ This mode cannot be BLKmode; use emit_block_move for that.
+
+ Return the last instruction emitted. */
+
+rtx
+emit_move_insn (x, y)
+ rtx x, y;
+{
+ enum machine_mode mode = GET_MODE (x);
+
+ x = protect_from_queue (x, 1);
+ y = protect_from_queue (y, 0);
+
+ if (mode == BLKmode || (GET_MODE (y) != mode && GET_MODE (y) != VOIDmode))
+ abort ();
+
+ if (CONSTANT_P (y) && ! LEGITIMATE_CONSTANT_P (y))
+ y = force_const_mem (mode, y);
+
+ /* If X or Y are memory references, verify that their addresses are valid
+ for the machine. */
+ if (GET_CODE (x) == MEM
+ && ((! memory_address_p (GET_MODE (x), XEXP (x, 0))
+ && ! push_operand (x, GET_MODE (x)))
+ || (flag_force_addr
+ && CONSTANT_ADDRESS_P (XEXP (x, 0)))))
+ x = change_address (x, VOIDmode, XEXP (x, 0));
+
+ if (GET_CODE (y) == MEM
+ && (! memory_address_p (GET_MODE (y), XEXP (y, 0))
+ || (flag_force_addr
+ && CONSTANT_ADDRESS_P (XEXP (y, 0)))))
+ y = change_address (y, VOIDmode, XEXP (y, 0));
+
+ if (mode == BLKmode)
+ abort ();
+
+ return emit_move_insn_1 (x, y);
+}
+
+/* Low level part of emit_move_insn.
+ Called just like emit_move_insn, but assumes X and Y
+ are basically valid. */
+
+rtx
+emit_move_insn_1 (x, y)
+ rtx x, y;
+{
+ enum machine_mode mode = GET_MODE (x);
+ enum machine_mode submode;
+ enum mode_class class = GET_MODE_CLASS (mode);
+ int i;
+
+ if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+ return
+ emit_insn (GEN_FCN (mov_optab->handlers[(int) mode].insn_code) (x, y));
+
+ /* Expand complex moves by moving real part and imag part, if possible. */
+ else if ((class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT)
+ && BLKmode != (submode = mode_for_size ((GET_MODE_UNIT_SIZE (mode)
+ * BITS_PER_UNIT),
+ (class == MODE_COMPLEX_INT
+ ? MODE_INT : MODE_FLOAT),
+ 0))
+ && (mov_optab->handlers[(int) submode].insn_code
+ != CODE_FOR_nothing))
+ {
+ /* Don't split destination if it is a stack push. */
+ int stack = push_operand (x, GET_MODE (x));
+ rtx insns;
+
+ start_sequence ();
+
+ /* If this is a stack, push the highpart first, so it
+ will be in the argument order.
+
+ In that case, change_address is used only to convert
+ the mode, not to change the address. */
+ if (stack)
+ {
+ /* Note that the real part always precedes the imag part in memory
+ regardless of machine's endianness. */
+#ifdef STACK_GROWS_DOWNWARD
+ emit_insn (GEN_FCN (mov_optab->handlers[(int) submode].insn_code)
+ (gen_rtx (MEM, submode, (XEXP (x, 0))),
+ gen_imagpart (submode, y)));
+ emit_insn (GEN_FCN (mov_optab->handlers[(int) submode].insn_code)
+ (gen_rtx (MEM, submode, (XEXP (x, 0))),
+ gen_realpart (submode, y)));
+#else
+ emit_insn (GEN_FCN (mov_optab->handlers[(int) submode].insn_code)
+ (gen_rtx (MEM, submode, (XEXP (x, 0))),
+ gen_realpart (submode, y)));
+ emit_insn (GEN_FCN (mov_optab->handlers[(int) submode].insn_code)
+ (gen_rtx (MEM, submode, (XEXP (x, 0))),
+ gen_imagpart (submode, y)));
+#endif
+ }
+ else
+ {
+ emit_insn (GEN_FCN (mov_optab->handlers[(int) submode].insn_code)
+ (gen_realpart (submode, x), gen_realpart (submode, y)));
+ emit_insn (GEN_FCN (mov_optab->handlers[(int) submode].insn_code)
+ (gen_imagpart (submode, x), gen_imagpart (submode, y)));
+ }
+
+ insns = get_insns ();
+ end_sequence ();
+
+ /* If X is a CONCAT, we got insns like RD = RS, ID = IS,
+ each with a separate pseudo as destination.
+ It's not correct for flow to treat them as a unit. */
+ if (GET_CODE (x) != CONCAT)
+ emit_no_conflict_block (insns, x, y, NULL_RTX, NULL_RTX);
+ else
+ emit_insns (insns);
+
+ return get_last_insn ();
+ }
+
+ /* This will handle any multi-word mode that lacks a move_insn pattern.
+ However, you will get better code if you define such patterns,
+ even if they must turn into multiple assembler instructions. */
+ else if (GET_MODE_SIZE (mode) > UNITS_PER_WORD)
+ {
+ rtx last_insn = 0;
+ rtx insns;
+
+ start_sequence ();
+
+ for (i = 0;
+ i < (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
+ i++)
+ {
+ rtx xpart = operand_subword (x, i, 1, mode);
+ rtx ypart = operand_subword (y, i, 1, mode);
+
+ /* If we can't get a part of Y, put Y into memory if it is a
+ constant. Otherwise, force it into a register. If we still
+ can't get a part of Y, abort. */
+ if (ypart == 0 && CONSTANT_P (y))
+ {
+ y = force_const_mem (mode, y);
+ ypart = operand_subword (y, i, 1, mode);
+ }
+ else if (ypart == 0)
+ ypart = operand_subword_force (y, i, mode);
+
+ if (xpart == 0 || ypart == 0)
+ abort ();
+
+ last_insn = emit_move_insn (xpart, ypart);
+ }
+
+ insns = get_insns ();
+ end_sequence ();
+ emit_no_conflict_block (insns, x, y, NULL_RTX, NULL_RTX);
+
+ return last_insn;
+ }
+ else
+ abort ();
+}
+
+/* Pushing data onto the stack. */
+
+/* Push a block of length SIZE (perhaps variable)
+ and return an rtx to address the beginning of the block.
+ Note that it is not possible for the value returned to be a QUEUED.
+ The value may be virtual_outgoing_args_rtx.
+
+ EXTRA is the number of bytes of padding to push in addition to SIZE.
+ BELOW nonzero means this padding comes at low addresses;
+ otherwise, the padding comes at high addresses. */
+
+rtx
+push_block (size, extra, below)
+ rtx size;
+ int extra, below;
+{
+ register rtx temp;
+ if (CONSTANT_P (size))
+ anti_adjust_stack (plus_constant (size, extra));
+ else if (GET_CODE (size) == REG && extra == 0)
+ anti_adjust_stack (size);
+ else
+ {
+ rtx temp = copy_to_mode_reg (Pmode, size);
+ if (extra != 0)
+ temp = expand_binop (Pmode, add_optab, temp, GEN_INT (extra),
+ temp, 0, OPTAB_LIB_WIDEN);
+ anti_adjust_stack (temp);
+ }
+
+#ifdef STACK_GROWS_DOWNWARD
+ temp = virtual_outgoing_args_rtx;
+ if (extra != 0 && below)
+ temp = plus_constant (temp, extra);
+#else
+ if (GET_CODE (size) == CONST_INT)
+ temp = plus_constant (virtual_outgoing_args_rtx,
+ - INTVAL (size) - (below ? 0 : extra));
+ else if (extra != 0 && !below)
+ temp = gen_rtx (PLUS, Pmode, virtual_outgoing_args_rtx,
+ negate_rtx (Pmode, plus_constant (size, extra)));
+ else
+ temp = gen_rtx (PLUS, Pmode, virtual_outgoing_args_rtx,
+ negate_rtx (Pmode, size));
+#endif
+
+ return memory_address (GET_CLASS_NARROWEST_MODE (MODE_INT), temp);
+}
+
+rtx
+gen_push_operand ()
+{
+ return gen_rtx (STACK_PUSH_CODE, Pmode, stack_pointer_rtx);
+}
+
+/* Generate code to push X onto the stack, assuming it has mode MODE and
+ type TYPE.
+ MODE is redundant except when X is a CONST_INT (since they don't
+ carry mode info).
+ SIZE is an rtx for the size of data to be copied (in bytes),
+ needed only if X is BLKmode.
+
+ ALIGN (in bytes) is maximum alignment we can assume.
+
+ If PARTIAL and REG are both nonzero, then copy that many of the first
+ words of X into registers starting with REG, and push the rest of X.
+ The amount of space pushed is decreased by PARTIAL words,
+ rounded *down* to a multiple of PARM_BOUNDARY.
+ REG must be a hard register in this case.
+ If REG is zero but PARTIAL is not, take any all others actions for an
+ argument partially in registers, but do not actually load any
+ registers.
+
+ EXTRA is the amount in bytes of extra space to leave next to this arg.
+ This is ignored if an argument block has already been allocated.
+
+ On a machine that lacks real push insns, ARGS_ADDR is the address of
+ the bottom of the argument block for this call. We use indexing off there
+ to store the arg. On machines with push insns, ARGS_ADDR is 0 when a
+ argument block has not been preallocated.
+
+ ARGS_SO_FAR is the size of args previously pushed for this call. */
+
+void
+emit_push_insn (x, mode, type, size, align, partial, reg, extra,
+ args_addr, args_so_far)
+ register rtx x;
+ enum machine_mode mode;
+ tree type;
+ rtx size;
+ int align;
+ int partial;
+ rtx reg;
+ int extra;
+ rtx args_addr;
+ rtx args_so_far;
+{
+ rtx xinner;
+ enum direction stack_direction
+#ifdef STACK_GROWS_DOWNWARD
+ = downward;
+#else
+ = upward;
+#endif
+
+ /* Decide where to pad the argument: `downward' for below,
+ `upward' for above, or `none' for don't pad it.
+ Default is below for small data on big-endian machines; else above. */
+ enum direction where_pad = FUNCTION_ARG_PADDING (mode, type);
+
+ /* Invert direction if stack is post-update. */
+ if (STACK_PUSH_CODE == POST_INC || STACK_PUSH_CODE == POST_DEC)
+ if (where_pad != none)
+ where_pad = (where_pad == downward ? upward : downward);
+
+ xinner = x = protect_from_queue (x, 0);
+
+ if (mode == BLKmode)
+ {
+ /* Copy a block into the stack, entirely or partially. */
+
+ register rtx temp;
+ int used = partial * UNITS_PER_WORD;
+ int offset = used % (PARM_BOUNDARY / BITS_PER_UNIT);
+ int skip;
+
+ if (size == 0)
+ abort ();
+
+ used -= offset;
+
+ /* USED is now the # of bytes we need not copy to the stack
+ because registers will take care of them. */
+
+ if (partial != 0)
+ xinner = change_address (xinner, BLKmode,
+ plus_constant (XEXP (xinner, 0), used));
+
+ /* If the partial register-part of the arg counts in its stack size,
+ skip the part of stack space corresponding to the registers.
+ Otherwise, start copying to the beginning of the stack space,
+ by setting SKIP to 0. */
+#ifndef REG_PARM_STACK_SPACE
+ skip = 0;
+#else
+ skip = used;
+#endif
+
+#ifdef PUSH_ROUNDING
+ /* Do it with several push insns if that doesn't take lots of insns
+ and if there is no difficulty with push insns that skip bytes
+ on the stack for alignment purposes. */
+ if (args_addr == 0
+ && GET_CODE (size) == CONST_INT
+ && skip == 0
+ && (move_by_pieces_ninsns ((unsigned) INTVAL (size) - used, align)
+ < MOVE_RATIO)
+ /* Here we avoid the case of a structure whose weak alignment
+ forces many pushes of a small amount of data,
+ and such small pushes do rounding that causes trouble. */
+ && ((! STRICT_ALIGNMENT && ! SLOW_UNALIGNED_ACCESS)
+ || align >= BIGGEST_ALIGNMENT / BITS_PER_UNIT
+ || PUSH_ROUNDING (align) == align)
+ && PUSH_ROUNDING (INTVAL (size)) == INTVAL (size))
+ {
+ /* Push padding now if padding above and stack grows down,
+ or if padding below and stack grows up.
+ But if space already allocated, this has already been done. */
+ if (extra && args_addr == 0
+ && where_pad != none && where_pad != stack_direction)
+ anti_adjust_stack (GEN_INT (extra));
+
+ move_by_pieces (gen_rtx (MEM, BLKmode, gen_push_operand ()), xinner,
+ INTVAL (size) - used, align);
+ }
+ else
+#endif /* PUSH_ROUNDING */
+ {
+ /* Otherwise make space on the stack and copy the data
+ to the address of that space. */
+
+ /* Deduct words put into registers from the size we must copy. */
+ if (partial != 0)
+ {
+ if (GET_CODE (size) == CONST_INT)
+ size = GEN_INT (INTVAL (size) - used);
+ else
+ size = expand_binop (GET_MODE (size), sub_optab, size,
+ GEN_INT (used), NULL_RTX, 0,
+ OPTAB_LIB_WIDEN);
+ }
+
+ /* Get the address of the stack space.
+ In this case, we do not deal with EXTRA separately.
+ A single stack adjust will do. */
+ if (! args_addr)
+ {
+ temp = push_block (size, extra, where_pad == downward);
+ extra = 0;
+ }
+ else if (GET_CODE (args_so_far) == CONST_INT)
+ temp = memory_address (BLKmode,
+ plus_constant (args_addr,
+ skip + INTVAL (args_so_far)));
+ else
+ temp = memory_address (BLKmode,
+ plus_constant (gen_rtx (PLUS, Pmode,
+ args_addr, args_so_far),
+ skip));
+
+ /* TEMP is the address of the block. Copy the data there. */
+ if (GET_CODE (size) == CONST_INT
+ && (move_by_pieces_ninsns ((unsigned) INTVAL (size), align)
+ < MOVE_RATIO))
+ {
+ move_by_pieces (gen_rtx (MEM, BLKmode, temp), xinner,
+ INTVAL (size), align);
+ goto ret;
+ }
+ /* Try the most limited insn first, because there's no point
+ including more than one in the machine description unless
+ the more limited one has some advantage. */
+#ifdef HAVE_movstrqi
+ if (HAVE_movstrqi
+ && GET_CODE (size) == CONST_INT
+ && ((unsigned) INTVAL (size)
+ < (1 << (GET_MODE_BITSIZE (QImode) - 1))))
+ {
+ rtx pat = gen_movstrqi (gen_rtx (MEM, BLKmode, temp),
+ xinner, size, GEN_INT (align));
+ if (pat != 0)
+ {
+ emit_insn (pat);
+ goto ret;
+ }
+ }
+#endif
+#ifdef HAVE_movstrhi
+ if (HAVE_movstrhi
+ && GET_CODE (size) == CONST_INT
+ && ((unsigned) INTVAL (size)
+ < (1 << (GET_MODE_BITSIZE (HImode) - 1))))
+ {
+ rtx pat = gen_movstrhi (gen_rtx (MEM, BLKmode, temp),
+ xinner, size, GEN_INT (align));
+ if (pat != 0)
+ {
+ emit_insn (pat);
+ goto ret;
+ }
+ }
+#endif
+#ifdef HAVE_movstrsi
+ if (HAVE_movstrsi)
+ {
+ rtx pat = gen_movstrsi (gen_rtx (MEM, BLKmode, temp),
+ xinner, size, GEN_INT (align));
+ if (pat != 0)
+ {
+ emit_insn (pat);
+ goto ret;
+ }
+ }
+#endif
+#ifdef HAVE_movstrdi
+ if (HAVE_movstrdi)
+ {
+ rtx pat = gen_movstrdi (gen_rtx (MEM, BLKmode, temp),
+ xinner, size, GEN_INT (align));
+ if (pat != 0)
+ {
+ emit_insn (pat);
+ goto ret;
+ }
+ }
+#endif
+
+#ifndef ACCUMULATE_OUTGOING_ARGS
+ /* If the source is referenced relative to the stack pointer,
+ copy it to another register to stabilize it. We do not need
+ to do this if we know that we won't be changing sp. */
+
+ if (reg_mentioned_p (virtual_stack_dynamic_rtx, temp)
+ || reg_mentioned_p (virtual_outgoing_args_rtx, temp))
+ temp = copy_to_reg (temp);
+#endif
+
+ /* Make inhibit_defer_pop nonzero around the library call
+ to force it to pop the bcopy-arguments right away. */
+ NO_DEFER_POP;
+#ifdef TARGET_MEM_FUNCTIONS
+ emit_library_call (memcpy_libfunc, 0,
+ VOIDmode, 3, temp, Pmode, XEXP (xinner, 0), Pmode,
+ convert_to_mode (TYPE_MODE (sizetype),
+ size, TREE_UNSIGNED (sizetype)),
+ TYPE_MODE (sizetype));
+#else
+ emit_library_call (bcopy_libfunc, 0,
+ VOIDmode, 3, XEXP (xinner, 0), Pmode, temp, Pmode,
+ convert_to_mode (TYPE_MODE (sizetype),
+ size, TREE_UNSIGNED (sizetype)),
+ TYPE_MODE (sizetype));
+#endif
+ OK_DEFER_POP;
+ }
+ }
+ else if (partial > 0)
+ {
+ /* Scalar partly in registers. */
+
+ int size = GET_MODE_SIZE (mode) / UNITS_PER_WORD;
+ int i;
+ int not_stack;
+ /* # words of start of argument
+ that we must make space for but need not store. */
+ int offset = partial % (PARM_BOUNDARY / BITS_PER_WORD);
+ int args_offset = INTVAL (args_so_far);
+ int skip;
+
+ /* Push padding now if padding above and stack grows down,
+ or if padding below and stack grows up.
+ But if space already allocated, this has already been done. */
+ if (extra && args_addr == 0
+ && where_pad != none && where_pad != stack_direction)
+ anti_adjust_stack (GEN_INT (extra));
+
+ /* If we make space by pushing it, we might as well push
+ the real data. Otherwise, we can leave OFFSET nonzero
+ and leave the space uninitialized. */
+ if (args_addr == 0)
+ offset = 0;
+
+ /* Now NOT_STACK gets the number of words that we don't need to
+ allocate on the stack. */
+ not_stack = partial - offset;
+
+ /* If the partial register-part of the arg counts in its stack size,
+ skip the part of stack space corresponding to the registers.
+ Otherwise, start copying to the beginning of the stack space,
+ by setting SKIP to 0. */
+#ifndef REG_PARM_STACK_SPACE
+ skip = 0;
+#else
+ skip = not_stack;
+#endif
+
+ if (CONSTANT_P (x) && ! LEGITIMATE_CONSTANT_P (x))
+ x = validize_mem (force_const_mem (mode, x));
+
+ /* If X is a hard register in a non-integer mode, copy it into a pseudo;
+ SUBREGs of such registers are not allowed. */
+ if ((GET_CODE (x) == REG && REGNO (x) < FIRST_PSEUDO_REGISTER
+ && GET_MODE_CLASS (GET_MODE (x)) != MODE_INT))
+ x = copy_to_reg (x);
+
+ /* Loop over all the words allocated on the stack for this arg. */
+ /* We can do it by words, because any scalar bigger than a word
+ has a size a multiple of a word. */
+#ifndef PUSH_ARGS_REVERSED
+ for (i = not_stack; i < size; i++)
+#else
+ for (i = size - 1; i >= not_stack; i--)
+#endif
+ if (i >= not_stack + offset)
+ emit_push_insn (operand_subword_force (x, i, mode),
+ word_mode, NULL_TREE, NULL_RTX, align, 0, NULL_RTX,
+ 0, args_addr,
+ GEN_INT (args_offset + ((i - not_stack + skip)
+ * UNITS_PER_WORD)));
+ }
+ else
+ {
+ rtx addr;
+
+ /* Push padding now if padding above and stack grows down,
+ or if padding below and stack grows up.
+ But if space already allocated, this has already been done. */
+ if (extra && args_addr == 0
+ && where_pad != none && where_pad != stack_direction)
+ anti_adjust_stack (GEN_INT (extra));
+
+#ifdef PUSH_ROUNDING
+ if (args_addr == 0)
+ addr = gen_push_operand ();
+ else
+#endif
+ if (GET_CODE (args_so_far) == CONST_INT)
+ addr
+ = memory_address (mode,
+ plus_constant (args_addr, INTVAL (args_so_far)));
+ else
+ addr = memory_address (mode, gen_rtx (PLUS, Pmode, args_addr,
+ args_so_far));
+
+ emit_move_insn (gen_rtx (MEM, mode, addr), x);
+ }
+
+ ret:
+ /* If part should go in registers, copy that part
+ into the appropriate registers. Do this now, at the end,
+ since mem-to-mem copies above may do function calls. */
+ if (partial > 0 && reg != 0)
+ move_block_to_reg (REGNO (reg), x, partial, mode);
+
+ if (extra && args_addr == 0 && where_pad == stack_direction)
+ anti_adjust_stack (GEN_INT (extra));
+}
+
+/* Expand an assignment that stores the value of FROM into TO.
+ If WANT_VALUE is nonzero, return an rtx for the value of TO.
+ (This may contain a QUEUED rtx;
+ if the value is constant, this rtx is a constant.)
+ Otherwise, the returned value is NULL_RTX.
+
+ SUGGEST_REG is no longer actually used.
+ It used to mean, copy the value through a register
+ and return that register, if that is possible.
+ We now use WANT_VALUE to decide whether to do this. */
+
+rtx
+expand_assignment (to, from, want_value, suggest_reg)
+ tree to, from;
+ int want_value;
+ int suggest_reg;
+{
+ register rtx to_rtx = 0;
+ rtx result;
+
+ /* Don't crash if the lhs of the assignment was erroneous. */
+
+ if (TREE_CODE (to) == ERROR_MARK)
+ {
+ result = expand_expr (from, NULL_RTX, VOIDmode, 0);
+ return want_value ? result : NULL_RTX;
+ }
+
+ if (output_bytecode)
+ {
+ tree dest_innermost;
+
+ bc_expand_expr (from);
+ bc_emit_instruction (duplicate);
+
+ dest_innermost = bc_expand_address (to);
+
+ /* Can't deduce from TYPE that we're dealing with a bitfield, so
+ take care of it here. */
+
+ bc_store_memory (TREE_TYPE (to), dest_innermost);
+ return NULL;
+ }
+
+ /* Assignment of a structure component needs special treatment
+ if the structure component's rtx is not simply a MEM.
+ Assignment of an array element at a constant index, and assignment of
+ an array element in an unaligned packed structure field, has the same
+ problem. */
+
+ if (TREE_CODE (to) == COMPONENT_REF
+ || TREE_CODE (to) == BIT_FIELD_REF
+ || (TREE_CODE (to) == ARRAY_REF
+ && ((TREE_CODE (TREE_OPERAND (to, 1)) == INTEGER_CST
+ && TREE_CODE (TYPE_SIZE (TREE_TYPE (to))) == INTEGER_CST)
+ || (STRICT_ALIGNMENT && get_inner_unaligned_p (to)))))
+ {
+ enum machine_mode mode1;
+ int bitsize;
+ int bitpos;
+ tree offset;
+ int unsignedp;
+ int volatilep = 0;
+ tree tem;
+ int alignment;
+
+ push_temp_slots ();
+ tem = get_inner_reference (to, &bitsize, &bitpos, &offset,
+ &mode1, &unsignedp, &volatilep);
+
+ /* If we are going to use store_bit_field and extract_bit_field,
+ make sure to_rtx will be safe for multiple use. */
+
+ if (mode1 == VOIDmode && want_value)
+ tem = stabilize_reference (tem);
+
+ alignment = TYPE_ALIGN (TREE_TYPE (tem)) / BITS_PER_UNIT;
+ to_rtx = expand_expr (tem, NULL_RTX, VOIDmode, 0);
+ if (offset != 0)
+ {
+ rtx offset_rtx = expand_expr (offset, NULL_RTX, VOIDmode, 0);
+
+ if (GET_CODE (to_rtx) != MEM)
+ abort ();
+ to_rtx = change_address (to_rtx, VOIDmode,
+ gen_rtx (PLUS, Pmode, XEXP (to_rtx, 0),
+ force_reg (Pmode, offset_rtx)));
+ /* If we have a variable offset, the known alignment
+ is only that of the innermost structure containing the field.
+ (Actually, we could sometimes do better by using the
+ align of an element of the innermost array, but no need.) */
+ if (TREE_CODE (to) == COMPONENT_REF
+ || TREE_CODE (to) == BIT_FIELD_REF)
+ alignment
+ = TYPE_ALIGN (TREE_TYPE (TREE_OPERAND (to, 0))) / BITS_PER_UNIT;
+ }
+ if (volatilep)
+ {
+ if (GET_CODE (to_rtx) == MEM)
+ MEM_VOLATILE_P (to_rtx) = 1;
+#if 0 /* This was turned off because, when a field is volatile
+ in an object which is not volatile, the object may be in a register,
+ and then we would abort over here. */
+ else
+ abort ();
+#endif
+ }
+
+ result = store_field (to_rtx, bitsize, bitpos, mode1, from,
+ (want_value
+ /* Spurious cast makes HPUX compiler happy. */
+ ? (enum machine_mode) TYPE_MODE (TREE_TYPE (to))
+ : VOIDmode),
+ unsignedp,
+ /* Required alignment of containing datum. */
+ alignment,
+ int_size_in_bytes (TREE_TYPE (tem)));
+ preserve_temp_slots (result);
+ free_temp_slots ();
+ pop_temp_slots ();
+
+ /* If the value is meaningful, convert RESULT to the proper mode.
+ Otherwise, return nothing. */
+ return (want_value ? convert_modes (TYPE_MODE (TREE_TYPE (to)),
+ TYPE_MODE (TREE_TYPE (from)),
+ result,
+ TREE_UNSIGNED (TREE_TYPE (to)))
+ : NULL_RTX);
+ }
+
+ /* If the rhs is a function call and its value is not an aggregate,
+ call the function before we start to compute the lhs.
+ This is needed for correct code for cases such as
+ val = setjmp (buf) on machines where reference to val
+ requires loading up part of an address in a separate insn.
+
+ Don't do this if TO is a VAR_DECL whose DECL_RTL is REG since it might be
+ a promoted variable where the zero- or sign- extension needs to be done.
+ Handling this in the normal way is safe because no computation is done
+ before the call. */
+ if (TREE_CODE (from) == CALL_EXPR && ! aggregate_value_p (from)
+ && ! (TREE_CODE (to) == VAR_DECL && GET_CODE (DECL_RTL (to)) == REG))
+ {
+ rtx value;
+
+ push_temp_slots ();
+ value = expand_expr (from, NULL_RTX, VOIDmode, 0);
+ if (to_rtx == 0)
+ to_rtx = expand_expr (to, NULL_RTX, VOIDmode, 0);
+ emit_move_insn (to_rtx, value);
+ preserve_temp_slots (to_rtx);
+ free_temp_slots ();
+ pop_temp_slots ();
+ return want_value ? to_rtx : NULL_RTX;
+ }
+
+ /* Ordinary treatment. Expand TO to get a REG or MEM rtx.
+ Don't re-expand if it was expanded already (in COMPONENT_REF case). */
+
+ if (to_rtx == 0)
+ to_rtx = expand_expr (to, NULL_RTX, VOIDmode, 0);
+
+ /* Don't move directly into a return register. */
+ if (TREE_CODE (to) == RESULT_DECL && GET_CODE (to_rtx) == REG)
+ {
+ rtx temp;
+
+ push_temp_slots ();
+ temp = expand_expr (from, 0, GET_MODE (to_rtx), 0);
+ emit_move_insn (to_rtx, temp);
+ preserve_temp_slots (to_rtx);
+ free_temp_slots ();
+ pop_temp_slots ();
+ return want_value ? to_rtx : NULL_RTX;
+ }
+
+ /* In case we are returning the contents of an object which overlaps
+ the place the value is being stored, use a safe function when copying
+ a value through a pointer into a structure value return block. */
+ if (TREE_CODE (to) == RESULT_DECL && TREE_CODE (from) == INDIRECT_REF
+ && current_function_returns_struct
+ && !current_function_returns_pcc_struct)
+ {
+ rtx from_rtx, size;
+
+ push_temp_slots ();
+ size = expr_size (from);
+ from_rtx = expand_expr (from, NULL_RTX, VOIDmode, 0);
+
+#ifdef TARGET_MEM_FUNCTIONS
+ emit_library_call (memcpy_libfunc, 0,
+ VOIDmode, 3, XEXP (to_rtx, 0), Pmode,
+ XEXP (from_rtx, 0), Pmode,
+ convert_to_mode (TYPE_MODE (sizetype),
+ size, TREE_UNSIGNED (sizetype)),
+ TYPE_MODE (sizetype));
+#else
+ emit_library_call (bcopy_libfunc, 0,
+ VOIDmode, 3, XEXP (from_rtx, 0), Pmode,
+ XEXP (to_rtx, 0), Pmode,
+ convert_to_mode (TYPE_MODE (sizetype),
+ size, TREE_UNSIGNED (sizetype)),
+ TYPE_MODE (sizetype));
+#endif
+
+ preserve_temp_slots (to_rtx);
+ free_temp_slots ();
+ pop_temp_slots ();
+ return want_value ? to_rtx : NULL_RTX;
+ }
+
+ /* Compute FROM and store the value in the rtx we got. */
+
+ push_temp_slots ();
+ result = store_expr (from, to_rtx, want_value);
+ preserve_temp_slots (result);
+ free_temp_slots ();
+ pop_temp_slots ();
+ return want_value ? result : NULL_RTX;
+}
+
+/* Generate code for computing expression EXP,
+ and storing the value into TARGET.
+ TARGET may contain a QUEUED rtx.
+
+ If WANT_VALUE is nonzero, return a copy of the value
+ not in TARGET, so that we can be sure to use the proper
+ value in a containing expression even if TARGET has something
+ else stored in it. If possible, we copy the value through a pseudo
+ and return that pseudo. Or, if the value is constant, we try to
+ return the constant. In some cases, we return a pseudo
+ copied *from* TARGET.
+
+ If the mode is BLKmode then we may return TARGET itself.
+ It turns out that in BLKmode it doesn't cause a problem.
+ because C has no operators that could combine two different
+ assignments into the same BLKmode object with different values
+ with no sequence point. Will other languages need this to
+ be more thorough?
+
+ If WANT_VALUE is 0, we return NULL, to make sure
+ to catch quickly any cases where the caller uses the value
+ and fails to set WANT_VALUE. */
+
+rtx
+store_expr (exp, target, want_value)
+ register tree exp;
+ register rtx target;
+ int want_value;
+{
+ register rtx temp;
+ int dont_return_target = 0;
+
+ if (TREE_CODE (exp) == COMPOUND_EXPR)
+ {
+ /* Perform first part of compound expression, then assign from second
+ part. */
+ expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, 0);
+ emit_queue ();
+ return store_expr (TREE_OPERAND (exp, 1), target, want_value);
+ }
+ else if (TREE_CODE (exp) == COND_EXPR && GET_MODE (target) == BLKmode)
+ {
+ /* For conditional expression, get safe form of the target. Then
+ test the condition, doing the appropriate assignment on either
+ side. This avoids the creation of unnecessary temporaries.
+ For non-BLKmode, it is more efficient not to do this. */
+
+ rtx lab1 = gen_label_rtx (), lab2 = gen_label_rtx ();
+
+ emit_queue ();
+ target = protect_from_queue (target, 1);
+
+ NO_DEFER_POP;
+ jumpifnot (TREE_OPERAND (exp, 0), lab1);
+ store_expr (TREE_OPERAND (exp, 1), target, 0);
+ emit_queue ();
+ emit_jump_insn (gen_jump (lab2));
+ emit_barrier ();
+ emit_label (lab1);
+ store_expr (TREE_OPERAND (exp, 2), target, 0);
+ emit_queue ();
+ emit_label (lab2);
+ OK_DEFER_POP;
+ return want_value ? target : NULL_RTX;
+ }
+ else if (want_value && GET_CODE (target) == MEM && ! MEM_VOLATILE_P (target)
+ && GET_MODE (target) != BLKmode)
+ /* If target is in memory and caller wants value in a register instead,
+ arrange that. Pass TARGET as target for expand_expr so that,
+ if EXP is another assignment, WANT_VALUE will be nonzero for it.
+ We know expand_expr will not use the target in that case.
+ Don't do this if TARGET is volatile because we are supposed
+ to write it and then read it. */
+ {
+ temp = expand_expr (exp, cse_not_expected ? NULL_RTX : target,
+ GET_MODE (target), 0);
+ if (GET_MODE (temp) != BLKmode && GET_MODE (temp) != VOIDmode)
+ temp = copy_to_reg (temp);
+ dont_return_target = 1;
+ }
+ else if (queued_subexp_p (target))
+ /* If target contains a postincrement, let's not risk
+ using it as the place to generate the rhs. */
+ {
+ if (GET_MODE (target) != BLKmode && GET_MODE (target) != VOIDmode)
+ {
+ /* Expand EXP into a new pseudo. */
+ temp = gen_reg_rtx (GET_MODE (target));
+ temp = expand_expr (exp, temp, GET_MODE (target), 0);
+ }
+ else
+ temp = expand_expr (exp, NULL_RTX, GET_MODE (target), 0);
+
+ /* If target is volatile, ANSI requires accessing the value
+ *from* the target, if it is accessed. So make that happen.
+ In no case return the target itself. */
+ if (! MEM_VOLATILE_P (target) && want_value)
+ dont_return_target = 1;
+ }
+ else if (GET_CODE (target) == SUBREG && SUBREG_PROMOTED_VAR_P (target))
+ /* If this is an scalar in a register that is stored in a wider mode
+ than the declared mode, compute the result into its declared mode
+ and then convert to the wider mode. Our value is the computed
+ expression. */
+ {
+ temp = expand_expr (exp, NULL_RTX, VOIDmode, 0);
+
+ /* If TEMP is a volatile MEM and we want a result value, make
+ the access now so it gets done only once. */
+ if (GET_CODE (temp) == MEM && MEM_VOLATILE_P (temp))
+ temp = copy_to_reg (temp);
+
+ /* If TEMP is a VOIDmode constant, use convert_modes to make
+ sure that we properly convert it. */
+ if (CONSTANT_P (temp) && GET_MODE (temp) == VOIDmode)
+ temp = convert_modes (GET_MODE (SUBREG_REG (target)),
+ TYPE_MODE (TREE_TYPE (exp)), temp,
+ SUBREG_PROMOTED_UNSIGNED_P (target));
+
+ convert_move (SUBREG_REG (target), temp,
+ SUBREG_PROMOTED_UNSIGNED_P (target));
+ return want_value ? temp : NULL_RTX;
+ }
+ else
+ {
+ temp = expand_expr (exp, target, GET_MODE (target), 0);
+ /* Return TARGET if it's a specified hardware register.
+ If TARGET is a volatile mem ref, either return TARGET
+ or return a reg copied *from* TARGET; ANSI requires this.
+
+ Otherwise, if TEMP is not TARGET, return TEMP
+ if it is constant (for efficiency),
+ or if we really want the correct value. */
+ if (!(target && GET_CODE (target) == REG
+ && REGNO (target) < FIRST_PSEUDO_REGISTER)
+ && !(GET_CODE (target) == MEM && MEM_VOLATILE_P (target))
+ && temp != target
+ && (CONSTANT_P (temp) || want_value))
+ dont_return_target = 1;
+ }
+
+ /* If TEMP is a VOIDmode constant and the mode of the type of EXP is not
+ the same as that of TARGET, adjust the constant. This is needed, for
+ example, in case it is a CONST_DOUBLE and we want only a word-sized
+ value. */
+ if (CONSTANT_P (temp) && GET_MODE (temp) == VOIDmode
+ && TREE_CODE (exp) != ERROR_MARK
+ && GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp)))
+ temp = convert_modes (GET_MODE (target), TYPE_MODE (TREE_TYPE (exp)),
+ temp, TREE_UNSIGNED (TREE_TYPE (exp)));
+
+ /* If value was not generated in the target, store it there.
+ Convert the value to TARGET's type first if nec. */
+
+ if (temp != target && TREE_CODE (exp) != ERROR_MARK)
+ {
+ target = protect_from_queue (target, 1);
+ if (GET_MODE (temp) != GET_MODE (target)
+ && GET_MODE (temp) != VOIDmode)
+ {
+ int unsignedp = TREE_UNSIGNED (TREE_TYPE (exp));
+ if (dont_return_target)
+ {
+ /* In this case, we will return TEMP,
+ so make sure it has the proper mode.
+ But don't forget to store the value into TARGET. */
+ temp = convert_to_mode (GET_MODE (target), temp, unsignedp);
+ emit_move_insn (target, temp);
+ }
+ else
+ convert_move (target, temp, unsignedp);
+ }
+
+ else if (GET_MODE (temp) == BLKmode && TREE_CODE (exp) == STRING_CST)
+ {
+ /* Handle copying a string constant into an array.
+ The string constant may be shorter than the array.
+ So copy just the string's actual length, and clear the rest. */
+ rtx size;
+
+ /* Get the size of the data type of the string,
+ which is actually the size of the target. */
+ size = expr_size (exp);
+ if (GET_CODE (size) == CONST_INT
+ && INTVAL (size) < TREE_STRING_LENGTH (exp))
+ emit_block_move (target, temp, size,
+ TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT);
+ else
+ {
+ /* Compute the size of the data to copy from the string. */
+ tree copy_size
+ = size_binop (MIN_EXPR,
+ make_tree (sizetype, size),
+ convert (sizetype,
+ build_int_2 (TREE_STRING_LENGTH (exp), 0)));
+ rtx copy_size_rtx = expand_expr (copy_size, NULL_RTX,
+ VOIDmode, 0);
+ rtx label = 0;
+
+ /* Copy that much. */
+ emit_block_move (target, temp, copy_size_rtx,
+ TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT);
+
+ /* Figure out how much is left in TARGET
+ that we have to clear. */
+ if (GET_CODE (copy_size_rtx) == CONST_INT)
+ {
+ temp = plus_constant (XEXP (target, 0),
+ TREE_STRING_LENGTH (exp));
+ size = plus_constant (size,
+ - TREE_STRING_LENGTH (exp));
+ }
+ else
+ {
+ enum machine_mode size_mode = Pmode;
+
+ temp = force_reg (Pmode, XEXP (target, 0));
+ temp = expand_binop (size_mode, add_optab, temp,
+ copy_size_rtx, NULL_RTX, 0,
+ OPTAB_LIB_WIDEN);
+
+ size = expand_binop (size_mode, sub_optab, size,
+ copy_size_rtx, NULL_RTX, 0,
+ OPTAB_LIB_WIDEN);
+
+ emit_cmp_insn (size, const0_rtx, LT, NULL_RTX,
+ GET_MODE (size), 0, 0);
+ label = gen_label_rtx ();
+ emit_jump_insn (gen_blt (label));
+ }
+
+ if (size != const0_rtx)
+ {
+#ifdef TARGET_MEM_FUNCTIONS
+ emit_library_call (memset_libfunc, 0, VOIDmode, 3,
+ temp, Pmode, const0_rtx, Pmode, size, Pmode);
+#else
+ emit_library_call (bzero_libfunc, 0, VOIDmode, 2,
+ temp, Pmode, size, Pmode);
+#endif
+ }
+ if (label)
+ emit_label (label);
+ }
+ }
+ else if (GET_MODE (temp) == BLKmode)
+ emit_block_move (target, temp, expr_size (exp),
+ TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT);
+ else
+ emit_move_insn (target, temp);
+ }
+
+ /* If we don't want a value, return NULL_RTX. */
+ if (! want_value)
+ return NULL_RTX;
+
+ /* If we are supposed to return TEMP, do so as long as it isn't a MEM.
+ ??? The latter test doesn't seem to make sense. */
+ else if (dont_return_target && GET_CODE (temp) != MEM)
+ return temp;
+
+ /* Return TARGET itself if it is a hard register. */
+ else if (want_value && GET_MODE (target) != BLKmode
+ && ! (GET_CODE (target) == REG
+ && REGNO (target) < FIRST_PSEUDO_REGISTER))
+ return copy_to_reg (target);
+
+ else
+ return target;
+}
+
+/* Store the value of constructor EXP into the rtx TARGET.
+ TARGET is either a REG or a MEM. */
+
+static void
+store_constructor (exp, target)
+ tree exp;
+ rtx target;
+{
+ tree type = TREE_TYPE (exp);
+
+ /* We know our target cannot conflict, since safe_from_p has been called. */
+#if 0
+ /* Don't try copying piece by piece into a hard register
+ since that is vulnerable to being clobbered by EXP.
+ Instead, construct in a pseudo register and then copy it all. */
+ if (GET_CODE (target) == REG && REGNO (target) < FIRST_PSEUDO_REGISTER)
+ {
+ rtx temp = gen_reg_rtx (GET_MODE (target));
+ store_constructor (exp, temp);
+ emit_move_insn (target, temp);
+ return;
+ }
+#endif
+
+ if (TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE
+ || TREE_CODE (type) == QUAL_UNION_TYPE)
+ {
+ register tree elt;
+
+ /* Inform later passes that the whole union value is dead. */
+ if (TREE_CODE (type) == UNION_TYPE
+ || TREE_CODE (type) == QUAL_UNION_TYPE)
+ emit_insn (gen_rtx (CLOBBER, VOIDmode, target));
+
+ /* If we are building a static constructor into a register,
+ set the initial value as zero so we can fold the value into
+ a constant. */
+ else if (GET_CODE (target) == REG && TREE_STATIC (exp))
+ emit_move_insn (target, const0_rtx);
+
+ /* If the constructor has fewer fields than the structure,
+ clear the whole structure first. */
+ else if (list_length (CONSTRUCTOR_ELTS (exp))
+ != list_length (TYPE_FIELDS (type)))
+ clear_storage (target, int_size_in_bytes (type));
+ else
+ /* Inform later passes that the old value is dead. */
+ emit_insn (gen_rtx (CLOBBER, VOIDmode, target));
+
+ /* Store each element of the constructor into
+ the corresponding field of TARGET. */
+
+ for (elt = CONSTRUCTOR_ELTS (exp); elt; elt = TREE_CHAIN (elt))
+ {
+ register tree field = TREE_PURPOSE (elt);
+ register enum machine_mode mode;
+ int bitsize;
+ int bitpos = 0;
+ int unsignedp;
+ tree pos, constant = 0, offset = 0;
+ rtx to_rtx = target;
+
+ /* Just ignore missing fields.
+ We cleared the whole structure, above,
+ if any fields are missing. */
+ if (field == 0)
+ continue;
+
+ bitsize = TREE_INT_CST_LOW (DECL_SIZE (field));
+ unsignedp = TREE_UNSIGNED (field);
+ mode = DECL_MODE (field);
+ if (DECL_BIT_FIELD (field))
+ mode = VOIDmode;
+
+ pos = DECL_FIELD_BITPOS (field);
+ if (TREE_CODE (pos) == INTEGER_CST)
+ constant = pos;
+ else if (TREE_CODE (pos) == PLUS_EXPR
+ && TREE_CODE (TREE_OPERAND (pos, 1)) == INTEGER_CST)
+ constant = TREE_OPERAND (pos, 1), offset = TREE_OPERAND (pos, 0);
+ else
+ offset = pos;
+
+ if (constant)
+ bitpos = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field));
+
+ if (offset)
+ {
+ rtx offset_rtx;
+
+ if (contains_placeholder_p (offset))
+ offset = build (WITH_RECORD_EXPR, sizetype,
+ offset, exp);
+
+ offset = size_binop (FLOOR_DIV_EXPR, offset,
+ size_int (BITS_PER_UNIT));
+
+ offset_rtx = expand_expr (offset, NULL_RTX, VOIDmode, 0);
+ if (GET_CODE (to_rtx) != MEM)
+ abort ();
+
+ to_rtx
+ = change_address (to_rtx, VOIDmode,
+ gen_rtx (PLUS, Pmode, XEXP (to_rtx, 0),
+ force_reg (Pmode, offset_rtx)));
+ }
+
+ store_field (to_rtx, bitsize, bitpos, mode, TREE_VALUE (elt),
+ /* The alignment of TARGET is
+ at least what its type requires. */
+ VOIDmode, 0,
+ TYPE_ALIGN (type) / BITS_PER_UNIT,
+ int_size_in_bytes (type));
+ }
+ }
+ else if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ register tree elt;
+ register int i;
+ tree domain = TYPE_DOMAIN (type);
+ HOST_WIDE_INT minelt = TREE_INT_CST_LOW (TYPE_MIN_VALUE (domain));
+ HOST_WIDE_INT maxelt = TREE_INT_CST_LOW (TYPE_MAX_VALUE (domain));
+ tree elttype = TREE_TYPE (type);
+
+ /* If the constructor has fewer fields than the structure,
+ clear the whole structure first. Similarly if this this is
+ static constructor of a non-BLKmode object. */
+
+ if (list_length (CONSTRUCTOR_ELTS (exp)) < maxelt - minelt + 1
+ || (GET_CODE (target) == REG && TREE_STATIC (exp)))
+ clear_storage (target, int_size_in_bytes (type));
+ else
+ /* Inform later passes that the old value is dead. */
+ emit_insn (gen_rtx (CLOBBER, VOIDmode, target));
+
+ /* Store each element of the constructor into
+ the corresponding element of TARGET, determined
+ by counting the elements. */
+ for (elt = CONSTRUCTOR_ELTS (exp), i = 0;
+ elt;
+ elt = TREE_CHAIN (elt), i++)
+ {
+ register enum machine_mode mode;
+ int bitsize;
+ int bitpos;
+ int unsignedp;
+ tree index = TREE_PURPOSE (elt);
+ rtx xtarget = target;
+
+ mode = TYPE_MODE (elttype);
+ bitsize = GET_MODE_BITSIZE (mode);
+ unsignedp = TREE_UNSIGNED (elttype);
+
+ if (index != 0 && TREE_CODE (index) != INTEGER_CST)
+ {
+ /* We don't currently allow variable indices in a
+ C initializer, but let's try here to support them. */
+ rtx pos_rtx, addr, xtarget;
+ tree position;
+
+ position = size_binop (MULT_EXPR, index, TYPE_SIZE (elttype));
+ pos_rtx = expand_expr (position, 0, VOIDmode, 0);
+ addr = gen_rtx (PLUS, Pmode, XEXP (target, 0), pos_rtx);
+ xtarget = change_address (target, mode, addr);
+ store_expr (TREE_VALUE (elt), xtarget, 0);
+ }
+ else
+ {
+ if (index != 0)
+ bitpos = ((TREE_INT_CST_LOW (index) - minelt)
+ * TREE_INT_CST_LOW (TYPE_SIZE (elttype)));
+ else
+ bitpos = (i * TREE_INT_CST_LOW (TYPE_SIZE (elttype)));
+
+ store_field (xtarget, bitsize, bitpos, mode, TREE_VALUE (elt),
+ /* The alignment of TARGET is
+ at least what its type requires. */
+ VOIDmode, 0,
+ TYPE_ALIGN (type) / BITS_PER_UNIT,
+ int_size_in_bytes (type));
+ }
+ }
+ }
+
+ else
+ abort ();
+}
+
+/* Store the value of EXP (an expression tree)
+ into a subfield of TARGET which has mode MODE and occupies
+ BITSIZE bits, starting BITPOS bits from the start of TARGET.
+ If MODE is VOIDmode, it means that we are storing into a bit-field.
+
+ If VALUE_MODE is VOIDmode, return nothing in particular.
+ UNSIGNEDP is not used in this case.
+
+ Otherwise, return an rtx for the value stored. This rtx
+ has mode VALUE_MODE if that is convenient to do.
+ In this case, UNSIGNEDP must be nonzero if the value is an unsigned type.
+
+ ALIGN is the alignment that TARGET is known to have, measured in bytes.
+ TOTAL_SIZE is the size in bytes of the structure, or -1 if varying. */
+
+static rtx
+store_field (target, bitsize, bitpos, mode, exp, value_mode,
+ unsignedp, align, total_size)
+ rtx target;
+ int bitsize, bitpos;
+ enum machine_mode mode;
+ tree exp;
+ enum machine_mode value_mode;
+ int unsignedp;
+ int align;
+ int total_size;
+{
+ HOST_WIDE_INT width_mask = 0;
+
+ if (bitsize < HOST_BITS_PER_WIDE_INT)
+ width_mask = ((HOST_WIDE_INT) 1 << bitsize) - 1;
+
+ /* If we are storing into an unaligned field of an aligned union that is
+ in a register, we may have the mode of TARGET being an integer mode but
+ MODE == BLKmode. In that case, get an aligned object whose size and
+ alignment are the same as TARGET and store TARGET into it (we can avoid
+ the store if the field being stored is the entire width of TARGET). Then
+ call ourselves recursively to store the field into a BLKmode version of
+ that object. Finally, load from the object into TARGET. This is not
+ very efficient in general, but should only be slightly more expensive
+ than the otherwise-required unaligned accesses. Perhaps this can be
+ cleaned up later. */
+
+ if (mode == BLKmode
+ && (GET_CODE (target) == REG || GET_CODE (target) == SUBREG))
+ {
+ rtx object = assign_stack_temp (GET_MODE (target),
+ GET_MODE_SIZE (GET_MODE (target)), 0);
+ rtx blk_object = copy_rtx (object);
+
+ MEM_IN_STRUCT_P (object) = 1;
+ MEM_IN_STRUCT_P (blk_object) = 1;
+ PUT_MODE (blk_object, BLKmode);
+
+ if (bitsize != GET_MODE_BITSIZE (GET_MODE (target)))
+ emit_move_insn (object, target);
+
+ store_field (blk_object, bitsize, bitpos, mode, exp, VOIDmode, 0,
+ align, total_size);
+
+ /* Even though we aren't returning target, we need to
+ give it the updated value. */
+ emit_move_insn (target, object);
+
+ return blk_object;
+ }
+
+ /* If the structure is in a register or if the component
+ is a bit field, we cannot use addressing to access it.
+ Use bit-field techniques or SUBREG to store in it. */
+
+ if (mode == VOIDmode
+ || (mode != BLKmode && ! direct_store[(int) mode])
+ || GET_CODE (target) == REG
+ || GET_CODE (target) == SUBREG
+ /* If the field isn't aligned enough to store as an ordinary memref,
+ store it as a bit field. */
+ || (STRICT_ALIGNMENT
+ && align * BITS_PER_UNIT < GET_MODE_ALIGNMENT (mode))
+ || (STRICT_ALIGNMENT && bitpos % GET_MODE_ALIGNMENT (mode) != 0))
+ {
+ rtx temp = expand_expr (exp, NULL_RTX, VOIDmode, 0);
+
+ /* Unless MODE is VOIDmode or BLKmode, convert TEMP to
+ MODE. */
+ if (mode != VOIDmode && mode != BLKmode
+ && mode != TYPE_MODE (TREE_TYPE (exp)))
+ temp = convert_modes (mode, TYPE_MODE (TREE_TYPE (exp)), temp, 1);
+
+ /* Store the value in the bitfield. */
+ store_bit_field (target, bitsize, bitpos, mode, temp, align, total_size);
+ if (value_mode != VOIDmode)
+ {
+ /* The caller wants an rtx for the value. */
+ /* If possible, avoid refetching from the bitfield itself. */
+ if (width_mask != 0
+ && ! (GET_CODE (target) == MEM && MEM_VOLATILE_P (target)))
+ {
+ tree count;
+ enum machine_mode tmode;
+
+ if (unsignedp)
+ return expand_and (temp, GEN_INT (width_mask), NULL_RTX);
+ tmode = GET_MODE (temp);
+ if (tmode == VOIDmode)
+ tmode = value_mode;
+ count = build_int_2 (GET_MODE_BITSIZE (tmode) - bitsize, 0);
+ temp = expand_shift (LSHIFT_EXPR, tmode, temp, count, 0, 0);
+ return expand_shift (RSHIFT_EXPR, tmode, temp, count, 0, 0);
+ }
+ return extract_bit_field (target, bitsize, bitpos, unsignedp,
+ NULL_RTX, value_mode, 0, align,
+ total_size);
+ }
+ return const0_rtx;
+ }
+ else
+ {
+ rtx addr = XEXP (target, 0);
+ rtx to_rtx;
+
+ /* If a value is wanted, it must be the lhs;
+ so make the address stable for multiple use. */
+
+ if (value_mode != VOIDmode && GET_CODE (addr) != REG
+ && ! CONSTANT_ADDRESS_P (addr)
+ /* A frame-pointer reference is already stable. */
+ && ! (GET_CODE (addr) == PLUS
+ && GET_CODE (XEXP (addr, 1)) == CONST_INT
+ && (XEXP (addr, 0) == virtual_incoming_args_rtx
+ || XEXP (addr, 0) == virtual_stack_vars_rtx)))
+ addr = copy_to_reg (addr);
+
+ /* Now build a reference to just the desired component. */
+
+ to_rtx = change_address (target, mode,
+ plus_constant (addr, (bitpos / BITS_PER_UNIT)));
+ MEM_IN_STRUCT_P (to_rtx) = 1;
+
+ return store_expr (exp, to_rtx, value_mode != VOIDmode);
+ }
+}
+
+/* Return true if any object containing the innermost array is an unaligned
+ packed structure field. */
+
+static int
+get_inner_unaligned_p (exp)
+ tree exp;
+{
+ int needed_alignment = TYPE_ALIGN (TREE_TYPE (exp));
+
+ while (1)
+ {
+ if (TREE_CODE (exp) == COMPONENT_REF || TREE_CODE (exp) == BIT_FIELD_REF)
+ {
+ if (TYPE_ALIGN (TREE_TYPE (TREE_OPERAND (exp, 0)))
+ < needed_alignment)
+ return 1;
+ }
+ else if (TREE_CODE (exp) != ARRAY_REF
+ && TREE_CODE (exp) != NON_LVALUE_EXPR
+ && ! ((TREE_CODE (exp) == NOP_EXPR
+ || TREE_CODE (exp) == CONVERT_EXPR)
+ && (TYPE_MODE (TREE_TYPE (exp))
+ == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))))
+ break;
+
+ exp = TREE_OPERAND (exp, 0);
+ }
+
+ return 0;
+}
+
+/* Given an expression EXP that may be a COMPONENT_REF, a BIT_FIELD_REF,
+ or an ARRAY_REF, look for nested COMPONENT_REFs, BIT_FIELD_REFs, or
+ ARRAY_REFs and find the ultimate containing object, which we return.
+
+ We set *PBITSIZE to the size in bits that we want, *PBITPOS to the
+ bit position, and *PUNSIGNEDP to the signedness of the field.
+ If the position of the field is variable, we store a tree
+ giving the variable offset (in units) in *POFFSET.
+ This offset is in addition to the bit position.
+ If the position is not variable, we store 0 in *POFFSET.
+
+ If any of the extraction expressions is volatile,
+ we store 1 in *PVOLATILEP. Otherwise we don't change that.
+
+ If the field is a bit-field, *PMODE is set to VOIDmode. Otherwise, it
+ is a mode that can be used to access the field. In that case, *PBITSIZE
+ is redundant.
+
+ If the field describes a variable-sized object, *PMODE is set to
+ VOIDmode and *PBITSIZE is set to -1. An access cannot be made in
+ this case, but the address of the object can be found. */
+
+tree
+get_inner_reference (exp, pbitsize, pbitpos, poffset, pmode,
+ punsignedp, pvolatilep)
+ tree exp;
+ int *pbitsize;
+ int *pbitpos;
+ tree *poffset;
+ enum machine_mode *pmode;
+ int *punsignedp;
+ int *pvolatilep;
+{
+ tree orig_exp = exp;
+ tree size_tree = 0;
+ enum machine_mode mode = VOIDmode;
+ tree offset = integer_zero_node;
+
+ if (TREE_CODE (exp) == COMPONENT_REF)
+ {
+ size_tree = DECL_SIZE (TREE_OPERAND (exp, 1));
+ if (! DECL_BIT_FIELD (TREE_OPERAND (exp, 1)))
+ mode = DECL_MODE (TREE_OPERAND (exp, 1));
+ *punsignedp = TREE_UNSIGNED (TREE_OPERAND (exp, 1));
+ }
+ else if (TREE_CODE (exp) == BIT_FIELD_REF)
+ {
+ size_tree = TREE_OPERAND (exp, 1);
+ *punsignedp = TREE_UNSIGNED (exp);
+ }
+ else
+ {
+ mode = TYPE_MODE (TREE_TYPE (exp));
+ *pbitsize = GET_MODE_BITSIZE (mode);
+ *punsignedp = TREE_UNSIGNED (TREE_TYPE (exp));
+ }
+
+ if (size_tree)
+ {
+ if (TREE_CODE (size_tree) != INTEGER_CST)
+ mode = BLKmode, *pbitsize = -1;
+ else
+ *pbitsize = TREE_INT_CST_LOW (size_tree);
+ }
+
+ /* Compute cumulative bit-offset for nested component-refs and array-refs,
+ and find the ultimate containing object. */
+
+ *pbitpos = 0;
+
+ while (1)
+ {
+ if (TREE_CODE (exp) == COMPONENT_REF || TREE_CODE (exp) == BIT_FIELD_REF)
+ {
+ tree pos = (TREE_CODE (exp) == COMPONENT_REF
+ ? DECL_FIELD_BITPOS (TREE_OPERAND (exp, 1))
+ : TREE_OPERAND (exp, 2));
+
+ /* If this field hasn't been filled in yet, don't go
+ past it. This should only happen when folding expressions
+ made during type construction. */
+ if (pos == 0)
+ break;
+
+ if (TREE_CODE (pos) == PLUS_EXPR)
+ {
+ tree constant, var;
+ if (TREE_CODE (TREE_OPERAND (pos, 0)) == INTEGER_CST)
+ {
+ constant = TREE_OPERAND (pos, 0);
+ var = TREE_OPERAND (pos, 1);
+ }
+ else if (TREE_CODE (TREE_OPERAND (pos, 1)) == INTEGER_CST)
+ {
+ constant = TREE_OPERAND (pos, 1);
+ var = TREE_OPERAND (pos, 0);
+ }
+ else
+ abort ();
+
+ *pbitpos += TREE_INT_CST_LOW (constant);
+ offset = size_binop (PLUS_EXPR, offset,
+ size_binop (FLOOR_DIV_EXPR, var,
+ size_int (BITS_PER_UNIT)));
+ }
+ else if (TREE_CODE (pos) == INTEGER_CST)
+ *pbitpos += TREE_INT_CST_LOW (pos);
+ else
+ {
+ /* Assume here that the offset is a multiple of a unit.
+ If not, there should be an explicitly added constant. */
+ offset = size_binop (PLUS_EXPR, offset,
+ size_binop (FLOOR_DIV_EXPR, pos,
+ size_int (BITS_PER_UNIT)));
+ }
+ }
+
+ else if (TREE_CODE (exp) == ARRAY_REF)
+ {
+ /* This code is based on the code in case ARRAY_REF in expand_expr
+ below. We assume here that the size of an array element is
+ always an integral multiple of BITS_PER_UNIT. */
+
+ tree index = TREE_OPERAND (exp, 1);
+ tree domain = TYPE_DOMAIN (TREE_TYPE (TREE_OPERAND (exp, 0)));
+ tree low_bound
+ = domain ? TYPE_MIN_VALUE (domain) : integer_zero_node;
+ tree index_type = TREE_TYPE (index);
+
+ if (! integer_zerop (low_bound))
+ index = fold (build (MINUS_EXPR, index_type, index, low_bound));
+
+ if (TYPE_PRECISION (index_type) != POINTER_SIZE)
+ {
+ index = convert (type_for_size (POINTER_SIZE, 0), index);
+ index_type = TREE_TYPE (index);
+ }
+
+ index = fold (build (MULT_EXPR, index_type, index,
+ TYPE_SIZE (TREE_TYPE (exp))));
+
+ if (TREE_CODE (index) == INTEGER_CST
+ && TREE_INT_CST_HIGH (index) == 0)
+ *pbitpos += TREE_INT_CST_LOW (index);
+ else
+ offset = size_binop (PLUS_EXPR, offset,
+ size_binop (FLOOR_DIV_EXPR, index,
+ size_int (BITS_PER_UNIT)));
+ }
+ else if (TREE_CODE (exp) != NON_LVALUE_EXPR
+ && ! ((TREE_CODE (exp) == NOP_EXPR
+ || TREE_CODE (exp) == CONVERT_EXPR)
+ && (TYPE_MODE (TREE_TYPE (exp))
+ == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))))
+ break;
+
+ /* If any reference in the chain is volatile, the effect is volatile. */
+ if (TREE_THIS_VOLATILE (exp))
+ *pvolatilep = 1;
+ exp = TREE_OPERAND (exp, 0);
+ }
+
+ /* If this was a bit-field, see if there is a mode that allows direct
+ access in case EXP is in memory. */
+ if (mode == VOIDmode && *pbitsize != 0 && *pbitpos % *pbitsize == 0)
+ {
+ mode = mode_for_size (*pbitsize, MODE_INT, 0);
+ if (mode == BLKmode)
+ mode = VOIDmode;
+ }
+
+ if (integer_zerop (offset))
+ offset = 0;
+
+ if (offset != 0 && contains_placeholder_p (offset))
+ offset = build (WITH_RECORD_EXPR, sizetype, offset, orig_exp);
+
+ *pmode = mode;
+ *poffset = offset;
+ return exp;
+}
+
+/* Given an rtx VALUE that may contain additions and multiplications,
+ return an equivalent value that just refers to a register or memory.
+ This is done by generating instructions to perform the arithmetic
+ and returning a pseudo-register containing the value.
+
+ The returned value may be a REG, SUBREG, MEM or constant. */
+
+rtx
+force_operand (value, target)
+ rtx value, target;
+{
+ register optab binoptab = 0;
+ /* Use a temporary to force order of execution of calls to
+ `force_operand'. */
+ rtx tmp;
+ register rtx op2;
+ /* Use subtarget as the target for operand 0 of a binary operation. */
+ register rtx subtarget = (target != 0 && GET_CODE (target) == REG ? target : 0);
+
+ if (GET_CODE (value) == PLUS)
+ binoptab = add_optab;
+ else if (GET_CODE (value) == MINUS)
+ binoptab = sub_optab;
+ else if (GET_CODE (value) == MULT)
+ {
+ op2 = XEXP (value, 1);
+ if (!CONSTANT_P (op2)
+ && !(GET_CODE (op2) == REG && op2 != subtarget))
+ subtarget = 0;
+ tmp = force_operand (XEXP (value, 0), subtarget);
+ return expand_mult (GET_MODE (value), tmp,
+ force_operand (op2, NULL_RTX),
+ target, 0);
+ }
+
+ if (binoptab)
+ {
+ op2 = XEXP (value, 1);
+ if (!CONSTANT_P (op2)
+ && !(GET_CODE (op2) == REG && op2 != subtarget))
+ subtarget = 0;
+ if (binoptab == sub_optab && GET_CODE (op2) == CONST_INT)
+ {
+ binoptab = add_optab;
+ op2 = negate_rtx (GET_MODE (value), op2);
+ }
+
+ /* Check for an addition with OP2 a constant integer and our first
+ operand a PLUS of a virtual register and something else. In that
+ case, we want to emit the sum of the virtual register and the
+ constant first and then add the other value. This allows virtual
+ register instantiation to simply modify the constant rather than
+ creating another one around this addition. */
+ if (binoptab == add_optab && GET_CODE (op2) == CONST_INT
+ && GET_CODE (XEXP (value, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (value, 0), 0)) == REG
+ && REGNO (XEXP (XEXP (value, 0), 0)) >= FIRST_VIRTUAL_REGISTER
+ && REGNO (XEXP (XEXP (value, 0), 0)) <= LAST_VIRTUAL_REGISTER)
+ {
+ rtx temp = expand_binop (GET_MODE (value), binoptab,
+ XEXP (XEXP (value, 0), 0), op2,
+ subtarget, 0, OPTAB_LIB_WIDEN);
+ return expand_binop (GET_MODE (value), binoptab, temp,
+ force_operand (XEXP (XEXP (value, 0), 1), 0),
+ target, 0, OPTAB_LIB_WIDEN);
+ }
+
+ tmp = force_operand (XEXP (value, 0), subtarget);
+ return expand_binop (GET_MODE (value), binoptab, tmp,
+ force_operand (op2, NULL_RTX),
+ target, 0, OPTAB_LIB_WIDEN);
+ /* We give UNSIGNEDP = 0 to expand_binop
+ because the only operations we are expanding here are signed ones. */
+ }
+ return value;
+}
+
+/* Subroutine of expand_expr:
+ save the non-copied parts (LIST) of an expr (LHS), and return a list
+ which can restore these values to their previous values,
+ should something modify their storage. */
+
+static tree
+save_noncopied_parts (lhs, list)
+ tree lhs;
+ tree list;
+{
+ tree tail;
+ tree parts = 0;
+
+ for (tail = list; tail; tail = TREE_CHAIN (tail))
+ if (TREE_CODE (TREE_VALUE (tail)) == TREE_LIST)
+ parts = chainon (parts, save_noncopied_parts (lhs, TREE_VALUE (tail)));
+ else
+ {
+ tree part = TREE_VALUE (tail);
+ tree part_type = TREE_TYPE (part);
+ tree to_be_saved = build (COMPONENT_REF, part_type, lhs, part);
+ rtx target = assign_stack_temp (TYPE_MODE (part_type),
+ int_size_in_bytes (part_type), 0);
+ if (! memory_address_p (TYPE_MODE (part_type), XEXP (target, 0)))
+ target = change_address (target, TYPE_MODE (part_type), NULL_RTX);
+ parts = tree_cons (to_be_saved,
+ build (RTL_EXPR, part_type, NULL_TREE,
+ (tree) target),
+ parts);
+ store_expr (TREE_PURPOSE (parts), RTL_EXPR_RTL (TREE_VALUE (parts)), 0);
+ }
+ return parts;
+}
+
+/* Subroutine of expand_expr:
+ record the non-copied parts (LIST) of an expr (LHS), and return a list
+ which specifies the initial values of these parts. */
+
+static tree
+init_noncopied_parts (lhs, list)
+ tree lhs;
+ tree list;
+{
+ tree tail;
+ tree parts = 0;
+
+ for (tail = list; tail; tail = TREE_CHAIN (tail))
+ if (TREE_CODE (TREE_VALUE (tail)) == TREE_LIST)
+ parts = chainon (parts, init_noncopied_parts (lhs, TREE_VALUE (tail)));
+ else
+ {
+ tree part = TREE_VALUE (tail);
+ tree part_type = TREE_TYPE (part);
+ tree to_be_initialized = build (COMPONENT_REF, part_type, lhs, part);
+ parts = tree_cons (TREE_PURPOSE (tail), to_be_initialized, parts);
+ }
+ return parts;
+}
+
+/* Subroutine of expand_expr: return nonzero iff there is no way that
+ EXP can reference X, which is being modified. */
+
+static int
+safe_from_p (x, exp)
+ rtx x;
+ tree exp;
+{
+ rtx exp_rtl = 0;
+ int i, nops;
+
+ if (x == 0)
+ return 1;
+
+ /* If this is a subreg of a hard register, declare it unsafe, otherwise,
+ find the underlying pseudo. */
+ if (GET_CODE (x) == SUBREG)
+ {
+ x = SUBREG_REG (x);
+ if (GET_CODE (x) == REG && REGNO (x) < FIRST_PSEUDO_REGISTER)
+ return 0;
+ }
+
+ /* If X is a location in the outgoing argument area, it is always safe. */
+ if (GET_CODE (x) == MEM
+ && (XEXP (x, 0) == virtual_outgoing_args_rtx
+ || (GET_CODE (XEXP (x, 0)) == PLUS
+ && XEXP (XEXP (x, 0), 0) == virtual_outgoing_args_rtx)))
+ return 1;
+
+ switch (TREE_CODE_CLASS (TREE_CODE (exp)))
+ {
+ case 'd':
+ exp_rtl = DECL_RTL (exp);
+ break;
+
+ case 'c':
+ return 1;
+
+ case 'x':
+ if (TREE_CODE (exp) == TREE_LIST)
+ return ((TREE_VALUE (exp) == 0
+ || safe_from_p (x, TREE_VALUE (exp)))
+ && (TREE_CHAIN (exp) == 0
+ || safe_from_p (x, TREE_CHAIN (exp))));
+ else
+ return 0;
+
+ case '1':
+ return safe_from_p (x, TREE_OPERAND (exp, 0));
+
+ case '2':
+ case '<':
+ return (safe_from_p (x, TREE_OPERAND (exp, 0))
+ && safe_from_p (x, TREE_OPERAND (exp, 1)));
+
+ case 'e':
+ case 'r':
+ /* Now do code-specific tests. EXP_RTL is set to any rtx we find in
+ the expression. If it is set, we conflict iff we are that rtx or
+ both are in memory. Otherwise, we check all operands of the
+ expression recursively. */
+
+ switch (TREE_CODE (exp))
+ {
+ case ADDR_EXPR:
+ return (staticp (TREE_OPERAND (exp, 0))
+ || safe_from_p (x, TREE_OPERAND (exp, 0)));
+
+ case INDIRECT_REF:
+ if (GET_CODE (x) == MEM)
+ return 0;
+ break;
+
+ case CALL_EXPR:
+ exp_rtl = CALL_EXPR_RTL (exp);
+ if (exp_rtl == 0)
+ {
+ /* Assume that the call will clobber all hard registers and
+ all of memory. */
+ if ((GET_CODE (x) == REG && REGNO (x) < FIRST_PSEUDO_REGISTER)
+ || GET_CODE (x) == MEM)
+ return 0;
+ }
+
+ break;
+
+ case RTL_EXPR:
+ exp_rtl = RTL_EXPR_RTL (exp);
+ if (exp_rtl == 0)
+ /* We don't know what this can modify. */
+ return 0;
+
+ break;
+
+ case WITH_CLEANUP_EXPR:
+ exp_rtl = RTL_EXPR_RTL (exp);
+ break;
+
+ case CLEANUP_POINT_EXPR:
+ return safe_from_p (x, TREE_OPERAND (exp, 0));
+
+ case SAVE_EXPR:
+ exp_rtl = SAVE_EXPR_RTL (exp);
+ break;
+
+ case BIND_EXPR:
+ /* The only operand we look at is operand 1. The rest aren't
+ part of the expression. */
+ return safe_from_p (x, TREE_OPERAND (exp, 1));
+
+ case METHOD_CALL_EXPR:
+ /* This takes a rtx argument, but shouldn't appear here. */
+ abort ();
+ }
+
+ /* If we have an rtx, we do not need to scan our operands. */
+ if (exp_rtl)
+ break;
+
+ nops = tree_code_length[(int) TREE_CODE (exp)];
+ for (i = 0; i < nops; i++)
+ if (TREE_OPERAND (exp, i) != 0
+ && ! safe_from_p (x, TREE_OPERAND (exp, i)))
+ return 0;
+ }
+
+ /* If we have an rtl, find any enclosed object. Then see if we conflict
+ with it. */
+ if (exp_rtl)
+ {
+ if (GET_CODE (exp_rtl) == SUBREG)
+ {
+ exp_rtl = SUBREG_REG (exp_rtl);
+ if (GET_CODE (exp_rtl) == REG
+ && REGNO (exp_rtl) < FIRST_PSEUDO_REGISTER)
+ return 0;
+ }
+
+ /* If the rtl is X, then it is not safe. Otherwise, it is unless both
+ are memory and EXP is not readonly. */
+ return ! (rtx_equal_p (x, exp_rtl)
+ || (GET_CODE (x) == MEM && GET_CODE (exp_rtl) == MEM
+ && ! TREE_READONLY (exp)));
+ }
+
+ /* If we reach here, it is safe. */
+ return 1;
+}
+
+/* Subroutine of expand_expr: return nonzero iff EXP is an
+ expression whose type is statically determinable. */
+
+static int
+fixed_type_p (exp)
+ tree exp;
+{
+ if (TREE_CODE (exp) == PARM_DECL
+ || TREE_CODE (exp) == VAR_DECL
+ || TREE_CODE (exp) == CALL_EXPR || TREE_CODE (exp) == TARGET_EXPR
+ || TREE_CODE (exp) == COMPONENT_REF
+ || TREE_CODE (exp) == ARRAY_REF)
+ return 1;
+ return 0;
+}
+
+/* expand_expr: generate code for computing expression EXP.
+ An rtx for the computed value is returned. The value is never null.
+ In the case of a void EXP, const0_rtx is returned.
+
+ The value may be stored in TARGET if TARGET is nonzero.
+ TARGET is just a suggestion; callers must assume that
+ the rtx returned may not be the same as TARGET.
+
+ If TARGET is CONST0_RTX, it means that the value will be ignored.
+
+ If TMODE is not VOIDmode, it suggests generating the
+ result in mode TMODE. But this is done only when convenient.
+ Otherwise, TMODE is ignored and the value generated in its natural mode.
+ TMODE is just a suggestion; callers must assume that
+ the rtx returned may not have mode TMODE.
+
+ Note that TARGET may have neither TMODE nor MODE. In that case, it
+ probably will not be used.
+
+ If MODIFIER is EXPAND_SUM then when EXP is an addition
+ we can return an rtx of the form (MULT (REG ...) (CONST_INT ...))
+ or a nest of (PLUS ...) and (MINUS ...) where the terms are
+ products as above, or REG or MEM, or constant.
+ Ordinarily in such cases we would output mul or add instructions
+ and then return a pseudo reg containing the sum.
+
+ EXPAND_INITIALIZER is much like EXPAND_SUM except that
+ it also marks a label as absolutely required (it can't be dead).
+ It also makes a ZERO_EXTEND or SIGN_EXTEND instead of emitting extend insns.
+ This is used for outputting expressions used in initializers.
+
+ EXPAND_CONST_ADDRESS says that it is okay to return a MEM
+ with a constant address even if that address is not normally legitimate.
+ EXPAND_INITIALIZER and EXPAND_SUM also have this effect. */
+
+rtx
+expand_expr (exp, target, tmode, modifier)
+ register tree exp;
+ rtx target;
+ enum machine_mode tmode;
+ enum expand_modifier modifier;
+{
+ /* Chain of pending expressions for PLACEHOLDER_EXPR to replace.
+ This is static so it will be accessible to our recursive callees. */
+ static tree placeholder_list = 0;
+ register rtx op0, op1, temp;
+ tree type = TREE_TYPE (exp);
+ int unsignedp = TREE_UNSIGNED (type);
+ register enum machine_mode mode = TYPE_MODE (type);
+ register enum tree_code code = TREE_CODE (exp);
+ optab this_optab;
+ /* Use subtarget as the target for operand 0 of a binary operation. */
+ rtx subtarget = (target != 0 && GET_CODE (target) == REG ? target : 0);
+ rtx original_target = target;
+ /* Maybe defer this until sure not doing bytecode? */
+ int ignore = (target == const0_rtx
+ || ((code == NON_LVALUE_EXPR || code == NOP_EXPR
+ || code == CONVERT_EXPR || code == REFERENCE_EXPR
+ || code == COND_EXPR)
+ && TREE_CODE (type) == VOID_TYPE));
+ tree context;
+
+
+ if (output_bytecode && modifier != EXPAND_INITIALIZER)
+ {
+ bc_expand_expr (exp);
+ return NULL;
+ }
+
+ /* Don't use hard regs as subtargets, because the combiner
+ can only handle pseudo regs. */
+ if (subtarget && REGNO (subtarget) < FIRST_PSEUDO_REGISTER)
+ subtarget = 0;
+ /* Avoid subtargets inside loops,
+ since they hide some invariant expressions. */
+ if (preserve_subexpressions_p ())
+ subtarget = 0;
+
+ /* If we are going to ignore this result, we need only do something
+ if there is a side-effect somewhere in the expression. If there
+ is, short-circuit the most common cases here. Note that we must
+ not call expand_expr with anything but const0_rtx in case this
+ is an initial expansion of a size that contains a PLACEHOLDER_EXPR. */
+
+ if (ignore)
+ {
+ if (! TREE_SIDE_EFFECTS (exp))
+ return const0_rtx;
+
+ /* Ensure we reference a volatile object even if value is ignored. */
+ if (TREE_THIS_VOLATILE (exp)
+ && TREE_CODE (exp) != FUNCTION_DECL
+ && mode != VOIDmode && mode != BLKmode)
+ {
+ temp = expand_expr (exp, NULL_RTX, VOIDmode, modifier);
+ if (GET_CODE (temp) == MEM)
+ temp = copy_to_reg (temp);
+ return const0_rtx;
+ }
+
+ if (TREE_CODE_CLASS (code) == '1')
+ return expand_expr (TREE_OPERAND (exp, 0), const0_rtx,
+ VOIDmode, modifier);
+ else if (TREE_CODE_CLASS (code) == '2'
+ || TREE_CODE_CLASS (code) == '<')
+ {
+ expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, modifier);
+ expand_expr (TREE_OPERAND (exp, 1), const0_rtx, VOIDmode, modifier);
+ return const0_rtx;
+ }
+ else if ((code == TRUTH_ANDIF_EXPR || code == TRUTH_ORIF_EXPR)
+ && ! TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 1)))
+ /* If the second operand has no side effects, just evaluate
+ the first. */
+ return expand_expr (TREE_OPERAND (exp, 0), const0_rtx,
+ VOIDmode, modifier);
+
+ target = 0;
+ }
+
+ /* If will do cse, generate all results into pseudo registers
+ since 1) that allows cse to find more things
+ and 2) otherwise cse could produce an insn the machine
+ cannot support. */
+
+ if (! cse_not_expected && mode != BLKmode && target
+ && (GET_CODE (target) != REG || REGNO (target) < FIRST_PSEUDO_REGISTER))
+ target = subtarget;
+
+ switch (code)
+ {
+ case LABEL_DECL:
+ {
+ tree function = decl_function_context (exp);
+ /* Handle using a label in a containing function. */
+ if (function != current_function_decl && function != 0)
+ {
+ struct function *p = find_function_data (function);
+ /* Allocate in the memory associated with the function
+ that the label is in. */
+ push_obstacks (p->function_obstack,
+ p->function_maybepermanent_obstack);
+
+ p->forced_labels = gen_rtx (EXPR_LIST, VOIDmode,
+ label_rtx (exp), p->forced_labels);
+ pop_obstacks ();
+ }
+ else if (modifier == EXPAND_INITIALIZER)
+ forced_labels = gen_rtx (EXPR_LIST, VOIDmode,
+ label_rtx (exp), forced_labels);
+ temp = gen_rtx (MEM, FUNCTION_MODE,
+ gen_rtx (LABEL_REF, Pmode, label_rtx (exp)));
+ if (function != current_function_decl && function != 0)
+ LABEL_REF_NONLOCAL_P (XEXP (temp, 0)) = 1;
+ return temp;
+ }
+
+ case PARM_DECL:
+ if (DECL_RTL (exp) == 0)
+ {
+ error_with_decl (exp, "prior parameter's size depends on `%s'");
+ return CONST0_RTX (mode);
+ }
+
+ /* ... fall through ... */
+
+ case VAR_DECL:
+ /* If a static var's type was incomplete when the decl was written,
+ but the type is complete now, lay out the decl now. */
+ if (DECL_SIZE (exp) == 0 && TYPE_SIZE (TREE_TYPE (exp)) != 0
+ && (TREE_STATIC (exp) || DECL_EXTERNAL (exp)))
+ {
+ push_obstacks_nochange ();
+ end_temporary_allocation ();
+ layout_decl (exp, 0);
+ PUT_MODE (DECL_RTL (exp), DECL_MODE (exp));
+ pop_obstacks ();
+ }
+
+ /* ... fall through ... */
+
+ case FUNCTION_DECL:
+ case RESULT_DECL:
+ if (DECL_RTL (exp) == 0)
+ abort ();
+
+ /* Ensure variable marked as used even if it doesn't go through
+ a parser. If it hasn't be used yet, write out an external
+ definition. */
+ if (! TREE_USED (exp))
+ {
+ assemble_external (exp);
+ TREE_USED (exp) = 1;
+ }
+
+ /* Handle variables inherited from containing functions. */
+ context = decl_function_context (exp);
+
+ /* We treat inline_function_decl as an alias for the current function
+ because that is the inline function whose vars, types, etc.
+ are being merged into the current function.
+ See expand_inline_function. */
+
+ if (context != 0 && context != current_function_decl
+ && context != inline_function_decl
+ /* If var is static, we don't need a static chain to access it. */
+ && ! (GET_CODE (DECL_RTL (exp)) == MEM
+ && CONSTANT_P (XEXP (DECL_RTL (exp), 0))))
+ {
+ rtx addr;
+
+ /* Mark as non-local and addressable. */
+ DECL_NONLOCAL (exp) = 1;
+ mark_addressable (exp);
+ if (GET_CODE (DECL_RTL (exp)) != MEM)
+ abort ();
+ addr = XEXP (DECL_RTL (exp), 0);
+ if (GET_CODE (addr) == MEM)
+ addr = gen_rtx (MEM, Pmode,
+ fix_lexical_addr (XEXP (addr, 0), exp));
+ else
+ addr = fix_lexical_addr (addr, exp);
+ return change_address (DECL_RTL (exp), mode, addr);
+ }
+
+ /* This is the case of an array whose size is to be determined
+ from its initializer, while the initializer is still being parsed.
+ See expand_decl. */
+
+ if (GET_CODE (DECL_RTL (exp)) == MEM
+ && GET_CODE (XEXP (DECL_RTL (exp), 0)) == REG)
+ return change_address (DECL_RTL (exp), GET_MODE (DECL_RTL (exp)),
+ XEXP (DECL_RTL (exp), 0));
+
+ /* If DECL_RTL is memory, we are in the normal case and either
+ the address is not valid or it is not a register and -fforce-addr
+ is specified, get the address into a register. */
+
+ if (GET_CODE (DECL_RTL (exp)) == MEM
+ && modifier != EXPAND_CONST_ADDRESS
+ && modifier != EXPAND_SUM
+ && modifier != EXPAND_INITIALIZER
+ && (! memory_address_p (DECL_MODE (exp), XEXP (DECL_RTL (exp), 0))
+ || (flag_force_addr
+ && GET_CODE (XEXP (DECL_RTL (exp), 0)) != REG)))
+ return change_address (DECL_RTL (exp), VOIDmode,
+ copy_rtx (XEXP (DECL_RTL (exp), 0)));
+
+ /* If the mode of DECL_RTL does not match that of the decl, it
+ must be a promoted value. We return a SUBREG of the wanted mode,
+ but mark it so that we know that it was already extended. */
+
+ if (GET_CODE (DECL_RTL (exp)) == REG
+ && GET_MODE (DECL_RTL (exp)) != mode)
+ {
+ /* Get the signedness used for this variable. Ensure we get the
+ same mode we got when the variable was declared. */
+ if (GET_MODE (DECL_RTL (exp))
+ != promote_mode (type, DECL_MODE (exp), &unsignedp, 0))
+ abort ();
+
+ temp = gen_rtx (SUBREG, mode, DECL_RTL (exp), 0);
+ SUBREG_PROMOTED_VAR_P (temp) = 1;
+ SUBREG_PROMOTED_UNSIGNED_P (temp) = unsignedp;
+ return temp;
+ }
+
+ return DECL_RTL (exp);
+
+ case INTEGER_CST:
+ return immed_double_const (TREE_INT_CST_LOW (exp),
+ TREE_INT_CST_HIGH (exp),
+ mode);
+
+ case CONST_DECL:
+ return expand_expr (DECL_INITIAL (exp), target, VOIDmode, 0);
+
+ case REAL_CST:
+ /* If optimized, generate immediate CONST_DOUBLE
+ which will be turned into memory by reload if necessary.
+
+ We used to force a register so that loop.c could see it. But
+ this does not allow gen_* patterns to perform optimizations with
+ the constants. It also produces two insns in cases like "x = 1.0;".
+ On most machines, floating-point constants are not permitted in
+ many insns, so we'd end up copying it to a register in any case.
+
+ Now, we do the copying in expand_binop, if appropriate. */
+ return immed_real_const (exp);
+
+ case COMPLEX_CST:
+ case STRING_CST:
+ if (! TREE_CST_RTL (exp))
+ output_constant_def (exp);
+
+ /* TREE_CST_RTL probably contains a constant address.
+ On RISC machines where a constant address isn't valid,
+ make some insns to get that address into a register. */
+ if (GET_CODE (TREE_CST_RTL (exp)) == MEM
+ && modifier != EXPAND_CONST_ADDRESS
+ && modifier != EXPAND_INITIALIZER
+ && modifier != EXPAND_SUM
+ && (! memory_address_p (mode, XEXP (TREE_CST_RTL (exp), 0))
+ || (flag_force_addr
+ && GET_CODE (XEXP (TREE_CST_RTL (exp), 0)) != REG)))
+ return change_address (TREE_CST_RTL (exp), VOIDmode,
+ copy_rtx (XEXP (TREE_CST_RTL (exp), 0)));
+ return TREE_CST_RTL (exp);
+
+ case SAVE_EXPR:
+ context = decl_function_context (exp);
+
+ /* We treat inline_function_decl as an alias for the current function
+ because that is the inline function whose vars, types, etc.
+ are being merged into the current function.
+ See expand_inline_function. */
+ if (context == current_function_decl || context == inline_function_decl)
+ context = 0;
+
+ /* If this is non-local, handle it. */
+ if (context)
+ {
+ temp = SAVE_EXPR_RTL (exp);
+ if (temp && GET_CODE (temp) == REG)
+ {
+ put_var_into_stack (exp);
+ temp = SAVE_EXPR_RTL (exp);
+ }
+ if (temp == 0 || GET_CODE (temp) != MEM)
+ abort ();
+ return change_address (temp, mode,
+ fix_lexical_addr (XEXP (temp, 0), exp));
+ }
+ if (SAVE_EXPR_RTL (exp) == 0)
+ {
+ if (mode == BLKmode)
+ {
+ temp
+ = assign_stack_temp (mode, int_size_in_bytes (type), 0);
+ MEM_IN_STRUCT_P (temp) = AGGREGATE_TYPE_P (type);
+ }
+ else
+ temp = gen_reg_rtx (promote_mode (type, mode, &unsignedp, 0));
+
+ SAVE_EXPR_RTL (exp) = temp;
+ if (!optimize && GET_CODE (temp) == REG)
+ save_expr_regs = gen_rtx (EXPR_LIST, VOIDmode, temp,
+ save_expr_regs);
+
+ /* If the mode of TEMP does not match that of the expression, it
+ must be a promoted value. We pass store_expr a SUBREG of the
+ wanted mode but mark it so that we know that it was already
+ extended. Note that `unsignedp' was modified above in
+ this case. */
+
+ if (GET_CODE (temp) == REG && GET_MODE (temp) != mode)
+ {
+ temp = gen_rtx (SUBREG, mode, SAVE_EXPR_RTL (exp), 0);
+ SUBREG_PROMOTED_VAR_P (temp) = 1;
+ SUBREG_PROMOTED_UNSIGNED_P (temp) = unsignedp;
+ }
+
+ store_expr (TREE_OPERAND (exp, 0), temp, 0);
+ }
+
+ /* If the mode of SAVE_EXPR_RTL does not match that of the expression, it
+ must be a promoted value. We return a SUBREG of the wanted mode,
+ but mark it so that we know that it was already extended. */
+
+ if (GET_CODE (SAVE_EXPR_RTL (exp)) == REG
+ && GET_MODE (SAVE_EXPR_RTL (exp)) != mode)
+ {
+ /* Compute the signedness and make the proper SUBREG. */
+ promote_mode (type, mode, &unsignedp, 0);
+ temp = gen_rtx (SUBREG, mode, SAVE_EXPR_RTL (exp), 0);
+ SUBREG_PROMOTED_VAR_P (temp) = 1;
+ SUBREG_PROMOTED_UNSIGNED_P (temp) = unsignedp;
+ return temp;
+ }
+
+ return SAVE_EXPR_RTL (exp);
+
+ case PLACEHOLDER_EXPR:
+ /* If there is an object on the head of the placeholder list,
+ see if some object in it's references is of type TYPE. For
+ further information, see tree.def. */
+ if (placeholder_list)
+ {
+ tree object;
+ tree old_list = placeholder_list;
+
+ for (object = TREE_PURPOSE (placeholder_list);
+ TREE_TYPE (object) != type
+ && (TREE_CODE_CLASS (TREE_CODE (object)) == 'r'
+ || TREE_CODE_CLASS (TREE_CODE (object)) == '1'
+ || TREE_CODE_CLASS (TREE_CODE (object)) == '2'
+ || TREE_CODE_CLASS (TREE_CODE (object)) == 'e');
+ object = TREE_OPERAND (object, 0))
+ ;
+
+ if (object && TREE_TYPE (object) == type)
+ {
+ /* Expand this object skipping the list entries before
+ it was found in case it is also a PLACEHOLDER_EXPR.
+ In that case, we want to translate it using subsequent
+ entries. */
+ placeholder_list = TREE_CHAIN (placeholder_list);
+ temp = expand_expr (object, original_target, tmode, modifier);
+ placeholder_list = old_list;
+ return temp;
+ }
+ }
+
+ /* We can't find the object or there was a missing WITH_RECORD_EXPR. */
+ abort ();
+
+ case WITH_RECORD_EXPR:
+ /* Put the object on the placeholder list, expand our first operand,
+ and pop the list. */
+ placeholder_list = tree_cons (TREE_OPERAND (exp, 1), NULL_TREE,
+ placeholder_list);
+ target = expand_expr (TREE_OPERAND (exp, 0), original_target,
+ tmode, modifier);
+ placeholder_list = TREE_CHAIN (placeholder_list);
+ return target;
+
+ case EXIT_EXPR:
+ expand_exit_loop_if_false (NULL_PTR,
+ invert_truthvalue (TREE_OPERAND (exp, 0)));
+ return const0_rtx;
+
+ case LOOP_EXPR:
+ push_temp_slots ();
+ expand_start_loop (1);
+ expand_expr_stmt (TREE_OPERAND (exp, 0));
+ expand_end_loop ();
+ pop_temp_slots ();
+
+ return const0_rtx;
+
+ case BIND_EXPR:
+ {
+ tree vars = TREE_OPERAND (exp, 0);
+ int vars_need_expansion = 0;
+
+ /* Need to open a binding contour here because
+ if there are any cleanups they most be contained here. */
+ expand_start_bindings (0);
+
+ /* Mark the corresponding BLOCK for output in its proper place. */
+ if (TREE_OPERAND (exp, 2) != 0
+ && ! TREE_USED (TREE_OPERAND (exp, 2)))
+ insert_block (TREE_OPERAND (exp, 2));
+
+ /* If VARS have not yet been expanded, expand them now. */
+ while (vars)
+ {
+ if (DECL_RTL (vars) == 0)
+ {
+ vars_need_expansion = 1;
+ expand_decl (vars);
+ }
+ expand_decl_init (vars);
+ vars = TREE_CHAIN (vars);
+ }
+
+ temp = expand_expr (TREE_OPERAND (exp, 1), target, tmode, modifier);
+
+ expand_end_bindings (TREE_OPERAND (exp, 0), 0, 0);
+
+ return temp;
+ }
+
+ case RTL_EXPR:
+ if (RTL_EXPR_SEQUENCE (exp) == const0_rtx)
+ abort ();
+ emit_insns (RTL_EXPR_SEQUENCE (exp));
+ RTL_EXPR_SEQUENCE (exp) = const0_rtx;
+ preserve_rtl_expr_result (RTL_EXPR_RTL (exp));
+ free_temps_for_rtl_expr (exp);
+ return RTL_EXPR_RTL (exp);
+
+ case CONSTRUCTOR:
+ /* If we don't need the result, just ensure we evaluate any
+ subexpressions. */
+ if (ignore)
+ {
+ tree elt;
+ for (elt = CONSTRUCTOR_ELTS (exp); elt; elt = TREE_CHAIN (elt))
+ expand_expr (TREE_VALUE (elt), const0_rtx, VOIDmode, 0);
+ return const0_rtx;
+ }
+
+ /* All elts simple constants => refer to a constant in memory. But
+ if this is a non-BLKmode mode, let it store a field at a time
+ since that should make a CONST_INT or CONST_DOUBLE when we
+ fold. Likewise, if we have a target we can use, it is best to
+ store directly into the target unless the type is large enough
+ that memcpy will be used. If we are making an initializer and
+ all operands are constant, put it in memory as well. */
+ else if ((TREE_STATIC (exp)
+ && ((mode == BLKmode
+ && ! (target != 0 && safe_from_p (target, exp)))
+ || TREE_ADDRESSABLE (exp)
+ || (TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
+ && (move_by_pieces_ninsns
+ (TREE_INT_CST_LOW (TYPE_SIZE (type)),
+ TYPE_ALIGN (type))
+ > MOVE_RATIO))))
+ || (modifier == EXPAND_INITIALIZER && TREE_CONSTANT (exp)))
+ {
+ rtx constructor = output_constant_def (exp);
+ if (modifier != EXPAND_CONST_ADDRESS
+ && modifier != EXPAND_INITIALIZER
+ && modifier != EXPAND_SUM
+ && (! memory_address_p (GET_MODE (constructor),
+ XEXP (constructor, 0))
+ || (flag_force_addr
+ && GET_CODE (XEXP (constructor, 0)) != REG)))
+ constructor = change_address (constructor, VOIDmode,
+ XEXP (constructor, 0));
+ return constructor;
+ }
+
+ else
+ {
+ if (target == 0 || ! safe_from_p (target, exp))
+ {
+ if (mode != BLKmode && ! TREE_ADDRESSABLE (exp))
+ target = gen_reg_rtx (tmode != VOIDmode ? tmode : mode);
+ else
+ {
+ target
+ = assign_stack_temp (mode, int_size_in_bytes (type), 0);
+ if (AGGREGATE_TYPE_P (type))
+ MEM_IN_STRUCT_P (target) = 1;
+ }
+ }
+ store_constructor (exp, target);
+ return target;
+ }
+
+ case INDIRECT_REF:
+ {
+ tree exp1 = TREE_OPERAND (exp, 0);
+ tree exp2;
+
+ /* A SAVE_EXPR as the address in an INDIRECT_EXPR is generated
+ for *PTR += ANYTHING where PTR is put inside the SAVE_EXPR.
+ This code has the same general effect as simply doing
+ expand_expr on the save expr, except that the expression PTR
+ is computed for use as a memory address. This means different
+ code, suitable for indexing, may be generated. */
+ if (TREE_CODE (exp1) == SAVE_EXPR
+ && SAVE_EXPR_RTL (exp1) == 0
+ && TREE_CODE (exp2 = TREE_OPERAND (exp1, 0)) != ERROR_MARK
+ && TYPE_MODE (TREE_TYPE (exp1)) == Pmode
+ && TYPE_MODE (TREE_TYPE (exp2)) == Pmode)
+ {
+ temp = expand_expr (TREE_OPERAND (exp1, 0), NULL_RTX,
+ VOIDmode, EXPAND_SUM);
+ op0 = memory_address (mode, temp);
+ op0 = copy_all_regs (op0);
+ SAVE_EXPR_RTL (exp1) = op0;
+ }
+ else
+ {
+ op0 = expand_expr (exp1, NULL_RTX, VOIDmode, EXPAND_SUM);
+ op0 = memory_address (mode, op0);
+ }
+
+ temp = gen_rtx (MEM, mode, op0);
+ /* If address was computed by addition,
+ mark this as an element of an aggregate. */
+ if (TREE_CODE (TREE_OPERAND (exp, 0)) == PLUS_EXPR
+ || (TREE_CODE (TREE_OPERAND (exp, 0)) == SAVE_EXPR
+ && TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) == PLUS_EXPR)
+ || AGGREGATE_TYPE_P (TREE_TYPE (exp))
+ || (TREE_CODE (exp1) == ADDR_EXPR
+ && (exp2 = TREE_OPERAND (exp1, 0))
+ && AGGREGATE_TYPE_P (TREE_TYPE (exp2))))
+ MEM_IN_STRUCT_P (temp) = 1;
+ MEM_VOLATILE_P (temp) = TREE_THIS_VOLATILE (exp) | flag_volatile;
+#if 0 /* It is incorrect to set RTX_UNCHANGING_P here, because the fact that
+ a location is accessed through a pointer to const does not mean
+ that the value there can never change. */
+ RTX_UNCHANGING_P (temp) = TREE_READONLY (exp);
+#endif
+ return temp;
+ }
+
+ case ARRAY_REF:
+ if (TREE_CODE (TREE_TYPE (TREE_OPERAND (exp, 0))) != ARRAY_TYPE)
+ abort ();
+
+ {
+ tree array = TREE_OPERAND (exp, 0);
+ tree domain = TYPE_DOMAIN (TREE_TYPE (array));
+ tree low_bound = domain ? TYPE_MIN_VALUE (domain) : integer_zero_node;
+ tree index = TREE_OPERAND (exp, 1);
+ tree index_type = TREE_TYPE (index);
+ int i;
+
+ if (TREE_CODE (low_bound) != INTEGER_CST
+ && contains_placeholder_p (low_bound))
+ low_bound = build (WITH_RECORD_EXPR, sizetype, low_bound, exp);
+
+ /* Optimize the special-case of a zero lower bound.
+
+ We convert the low_bound to sizetype to avoid some problems
+ with constant folding. (E.g. suppose the lower bound is 1,
+ and its mode is QI. Without the conversion, (ARRAY
+ +(INDEX-(unsigned char)1)) becomes ((ARRAY+(-(unsigned char)1))
+ +INDEX), which becomes (ARRAY+255+INDEX). Oops!)
+
+ But sizetype isn't quite right either (especially if
+ the lowbound is negative). FIXME */
+
+ if (! integer_zerop (low_bound))
+ index = fold (build (MINUS_EXPR, index_type, index,
+ convert (sizetype, low_bound)));
+
+ if ((TREE_CODE (index) != INTEGER_CST
+ || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+ && (! STRICT_ALIGNMENT || ! get_inner_unaligned_p (exp)))
+ {
+ /* Nonconstant array index or nonconstant element size, and
+ not an array in an unaligned (packed) structure field.
+ Generate the tree for *(&array+index) and expand that,
+ except do it in a language-independent way
+ and don't complain about non-lvalue arrays.
+ `mark_addressable' should already have been called
+ for any array for which this case will be reached. */
+
+ /* Don't forget the const or volatile flag from the array
+ element. */
+ tree variant_type = build_type_variant (type,
+ TREE_READONLY (exp),
+ TREE_THIS_VOLATILE (exp));
+ tree array_adr = build1 (ADDR_EXPR,
+ build_pointer_type (variant_type), array);
+ tree elt;
+ tree size = size_in_bytes (type);
+
+ /* Convert the integer argument to a type the same size as a
+ pointer so the multiply won't overflow spuriously. */
+ if (TYPE_PRECISION (index_type) != POINTER_SIZE)
+ index = convert (type_for_size (POINTER_SIZE, 0), index);
+
+ if (TREE_CODE (size) != INTEGER_CST
+ && contains_placeholder_p (size))
+ size = build (WITH_RECORD_EXPR, sizetype, size, exp);
+
+ /* Don't think the address has side effects
+ just because the array does.
+ (In some cases the address might have side effects,
+ and we fail to record that fact here. However, it should not
+ matter, since expand_expr should not care.) */
+ TREE_SIDE_EFFECTS (array_adr) = 0;
+
+ elt = build1 (INDIRECT_REF, type,
+ fold (build (PLUS_EXPR,
+ TYPE_POINTER_TO (variant_type),
+ array_adr,
+ fold (build (MULT_EXPR,
+ TYPE_POINTER_TO (variant_type),
+ index, size)))));
+
+ /* Volatility, etc., of new expression is same as old
+ expression. */
+ TREE_SIDE_EFFECTS (elt) = TREE_SIDE_EFFECTS (exp);
+ TREE_THIS_VOLATILE (elt) = TREE_THIS_VOLATILE (exp);
+ TREE_READONLY (elt) = TREE_READONLY (exp);
+
+ return expand_expr (elt, target, tmode, modifier);
+ }
+
+ /* Fold an expression like: "foo"[2].
+ This is not done in fold so it won't happen inside &. */
+
+ if (TREE_CODE (array) == STRING_CST
+ && TREE_CODE (index) == INTEGER_CST
+ && !TREE_INT_CST_HIGH (index)
+ && (i = TREE_INT_CST_LOW (index)) < TREE_STRING_LENGTH (array)
+ && GET_MODE_CLASS (mode) == MODE_INT)
+ return GEN_INT (TREE_STRING_POINTER (array)[i]);
+
+ /* If this is a constant index into a constant array,
+ just get the value from the array. Handle both the cases when
+ we have an explicit constructor and when our operand is a variable
+ that was declared const. */
+
+ if (TREE_CODE (array) == CONSTRUCTOR && ! TREE_SIDE_EFFECTS (array))
+ {
+ if (TREE_CODE (index) == INTEGER_CST
+ && TREE_INT_CST_HIGH (index) == 0)
+ {
+ tree elem = CONSTRUCTOR_ELTS (TREE_OPERAND (exp, 0));
+
+ i = TREE_INT_CST_LOW (index);
+ while (elem && i--)
+ elem = TREE_CHAIN (elem);
+ if (elem)
+ return expand_expr (fold (TREE_VALUE (elem)), target,
+ tmode, modifier);
+ }
+ }
+
+ else if (optimize >= 1
+ && TREE_READONLY (array) && ! TREE_SIDE_EFFECTS (array)
+ && TREE_CODE (array) == VAR_DECL && DECL_INITIAL (array)
+ && TREE_CODE (DECL_INITIAL (array)) != ERROR_MARK)
+ {
+ if (TREE_CODE (index) == INTEGER_CST
+ && TREE_INT_CST_HIGH (index) == 0)
+ {
+ tree init = DECL_INITIAL (array);
+
+ i = TREE_INT_CST_LOW (index);
+ if (TREE_CODE (init) == CONSTRUCTOR)
+ {
+ tree elem = CONSTRUCTOR_ELTS (init);
+
+ while (elem
+ && !tree_int_cst_equal (TREE_PURPOSE (elem), index))
+ elem = TREE_CHAIN (elem);
+ if (elem)
+ return expand_expr (fold (TREE_VALUE (elem)), target,
+ tmode, modifier);
+ }
+ else if (TREE_CODE (init) == STRING_CST
+ && i < TREE_STRING_LENGTH (init))
+ return GEN_INT (TREE_STRING_POINTER (init)[i]);
+ }
+ }
+ }
+
+ /* Treat array-ref with constant index as a component-ref. */
+
+ case COMPONENT_REF:
+ case BIT_FIELD_REF:
+ /* If the operand is a CONSTRUCTOR, we can just extract the
+ appropriate field if it is present. */
+ if (code != ARRAY_REF
+ && TREE_CODE (TREE_OPERAND (exp, 0)) == CONSTRUCTOR)
+ {
+ tree elt;
+
+ for (elt = CONSTRUCTOR_ELTS (TREE_OPERAND (exp, 0)); elt;
+ elt = TREE_CHAIN (elt))
+ if (TREE_PURPOSE (elt) == TREE_OPERAND (exp, 1))
+ return expand_expr (TREE_VALUE (elt), target, tmode, modifier);
+ }
+
+ {
+ enum machine_mode mode1;
+ int bitsize;
+ int bitpos;
+ tree offset;
+ int volatilep = 0;
+ tree tem = get_inner_reference (exp, &bitsize, &bitpos, &offset,
+ &mode1, &unsignedp, &volatilep);
+ int alignment;
+
+ /* If we got back the original object, something is wrong. Perhaps
+ we are evaluating an expression too early. In any event, don't
+ infinitely recurse. */
+ if (tem == exp)
+ abort ();
+
+ /* In some cases, we will be offsetting OP0's address by a constant.
+ So get it as a sum, if possible. If we will be using it
+ directly in an insn, we validate it. */
+ op0 = expand_expr (tem, NULL_RTX, VOIDmode, EXPAND_SUM);
+
+ /* If this is a constant, put it into a register if it is a
+ legitimate constant and memory if it isn't. */
+ if (CONSTANT_P (op0))
+ {
+ enum machine_mode mode = TYPE_MODE (TREE_TYPE (tem));
+ if (mode != BLKmode && LEGITIMATE_CONSTANT_P (op0))
+ op0 = force_reg (mode, op0);
+ else
+ op0 = validize_mem (force_const_mem (mode, op0));
+ }
+
+ alignment = TYPE_ALIGN (TREE_TYPE (tem)) / BITS_PER_UNIT;
+ if (offset != 0)
+ {
+ rtx offset_rtx = expand_expr (offset, NULL_RTX, VOIDmode, 0);
+
+ if (GET_CODE (op0) != MEM)
+ abort ();
+ op0 = change_address (op0, VOIDmode,
+ gen_rtx (PLUS, Pmode, XEXP (op0, 0),
+ force_reg (Pmode, offset_rtx)));
+ /* If we have a variable offset, the known alignment
+ is only that of the innermost structure containing the field.
+ (Actually, we could sometimes do better by using the
+ size of an element of the innermost array, but no need.) */
+ if (TREE_CODE (exp) == COMPONENT_REF
+ || TREE_CODE (exp) == BIT_FIELD_REF)
+ alignment = (TYPE_ALIGN (TREE_TYPE (TREE_OPERAND (exp, 0)))
+ / BITS_PER_UNIT);
+ }
+
+ /* Don't forget about volatility even if this is a bitfield. */
+ if (GET_CODE (op0) == MEM && volatilep && ! MEM_VOLATILE_P (op0))
+ {
+ op0 = copy_rtx (op0);
+ MEM_VOLATILE_P (op0) = 1;
+ }
+
+ /* In cases where an aligned union has an unaligned object
+ as a field, we might be extracting a BLKmode value from
+ an integer-mode (e.g., SImode) object. Handle this case
+ by doing the extract into an object as wide as the field
+ (which we know to be the width of a basic mode), then
+ storing into memory, and changing the mode to BLKmode. */
+ if (mode1 == VOIDmode
+ || (mode1 != BLKmode && ! direct_load[(int) mode1]
+ && modifier != EXPAND_CONST_ADDRESS
+ && modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
+ || GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG
+ /* If the field isn't aligned enough to fetch as a memref,
+ fetch it as a bit field. */
+ || (STRICT_ALIGNMENT
+ && TYPE_ALIGN (TREE_TYPE (tem)) < GET_MODE_ALIGNMENT (mode))
+ || (STRICT_ALIGNMENT && bitpos % GET_MODE_ALIGNMENT (mode) != 0))
+ {
+ enum machine_mode ext_mode = mode;
+
+ if (ext_mode == BLKmode)
+ ext_mode = mode_for_size (bitsize, MODE_INT, 1);
+
+ if (ext_mode == BLKmode)
+ abort ();
+
+ op0 = extract_bit_field (validize_mem (op0), bitsize, bitpos,
+ unsignedp, target, ext_mode, ext_mode,
+ alignment,
+ int_size_in_bytes (TREE_TYPE (tem)));
+ if (mode == BLKmode)
+ {
+ rtx new = assign_stack_temp (ext_mode,
+ bitsize / BITS_PER_UNIT, 0);
+
+ emit_move_insn (new, op0);
+ op0 = copy_rtx (new);
+ PUT_MODE (op0, BLKmode);
+ MEM_IN_STRUCT_P (op0) = 1;
+ }
+
+ return op0;
+ }
+
+ /* Get a reference to just this component. */
+ if (modifier == EXPAND_CONST_ADDRESS
+ || modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER)
+ op0 = gen_rtx (MEM, mode1, plus_constant (XEXP (op0, 0),
+ (bitpos / BITS_PER_UNIT)));
+ else
+ op0 = change_address (op0, mode1,
+ plus_constant (XEXP (op0, 0),
+ (bitpos / BITS_PER_UNIT)));
+ MEM_IN_STRUCT_P (op0) = 1;
+ MEM_VOLATILE_P (op0) |= volatilep;
+ if (mode == mode1 || mode1 == BLKmode || mode1 == tmode)
+ return op0;
+ if (target == 0)
+ target = gen_reg_rtx (tmode != VOIDmode ? tmode : mode);
+ convert_move (target, op0, unsignedp);
+ return target;
+ }
+
+ case OFFSET_REF:
+ {
+ tree base = build1 (ADDR_EXPR, type, TREE_OPERAND (exp, 0));
+ tree addr = build (PLUS_EXPR, type, base, TREE_OPERAND (exp, 1));
+ op0 = expand_expr (addr, NULL_RTX, VOIDmode, EXPAND_SUM);
+ temp = gen_rtx (MEM, mode, memory_address (mode, op0));
+ MEM_IN_STRUCT_P (temp) = 1;
+ MEM_VOLATILE_P (temp) = TREE_THIS_VOLATILE (exp);
+#if 0 /* It is incorrect to set RTX_UNCHANGING_P here, because the fact that
+ a location is accessed through a pointer to const does not mean
+ that the value there can never change. */
+ RTX_UNCHANGING_P (temp) = TREE_READONLY (exp);
+#endif
+ return temp;
+ }
+
+ /* Intended for a reference to a buffer of a file-object in Pascal.
+ But it's not certain that a special tree code will really be
+ necessary for these. INDIRECT_REF might work for them. */
+ case BUFFER_REF:
+ abort ();
+
+ case IN_EXPR:
+ {
+ /* Pascal set IN expression.
+
+ Algorithm:
+ rlo = set_low - (set_low%bits_per_word);
+ the_word = set [ (index - rlo)/bits_per_word ];
+ bit_index = index % bits_per_word;
+ bitmask = 1 << bit_index;
+ return !!(the_word & bitmask); */
+
+ tree set = TREE_OPERAND (exp, 0);
+ tree index = TREE_OPERAND (exp, 1);
+ int iunsignedp = TREE_UNSIGNED (TREE_TYPE (index));
+ tree set_type = TREE_TYPE (set);
+ tree set_low_bound = TYPE_MIN_VALUE (TYPE_DOMAIN (set_type));
+ tree set_high_bound = TYPE_MAX_VALUE (TYPE_DOMAIN (set_type));
+ rtx index_val = expand_expr (index, 0, VOIDmode, 0);
+ rtx lo_r = expand_expr (set_low_bound, 0, VOIDmode, 0);
+ rtx hi_r = expand_expr (set_high_bound, 0, VOIDmode, 0);
+ rtx setval = expand_expr (set, 0, VOIDmode, 0);
+ rtx setaddr = XEXP (setval, 0);
+ enum machine_mode index_mode = TYPE_MODE (TREE_TYPE (index));
+ rtx rlow;
+ rtx diff, quo, rem, addr, bit, result;
+
+ preexpand_calls (exp);
+
+ /* If domain is empty, answer is no. Likewise if index is constant
+ and out of bounds. */
+ if ((TREE_CODE (set_high_bound) == INTEGER_CST
+ && TREE_CODE (set_low_bound) == INTEGER_CST
+ && tree_int_cst_lt (set_high_bound, set_low_bound)
+ || (TREE_CODE (index) == INTEGER_CST
+ && TREE_CODE (set_low_bound) == INTEGER_CST
+ && tree_int_cst_lt (index, set_low_bound))
+ || (TREE_CODE (set_high_bound) == INTEGER_CST
+ && TREE_CODE (index) == INTEGER_CST
+ && tree_int_cst_lt (set_high_bound, index))))
+ return const0_rtx;
+
+ if (target == 0)
+ target = gen_reg_rtx (tmode != VOIDmode ? tmode : mode);
+
+ /* If we get here, we have to generate the code for both cases
+ (in range and out of range). */
+
+ op0 = gen_label_rtx ();
+ op1 = gen_label_rtx ();
+
+ if (! (GET_CODE (index_val) == CONST_INT
+ && GET_CODE (lo_r) == CONST_INT))
+ {
+ emit_cmp_insn (index_val, lo_r, LT, NULL_RTX,
+ GET_MODE (index_val), iunsignedp, 0);
+ emit_jump_insn (gen_blt (op1));
+ }
+
+ if (! (GET_CODE (index_val) == CONST_INT
+ && GET_CODE (hi_r) == CONST_INT))
+ {
+ emit_cmp_insn (index_val, hi_r, GT, NULL_RTX,
+ GET_MODE (index_val), iunsignedp, 0);
+ emit_jump_insn (gen_bgt (op1));
+ }
+
+ /* Calculate the element number of bit zero in the first word
+ of the set. */
+ if (GET_CODE (lo_r) == CONST_INT)
+ rlow = GEN_INT (INTVAL (lo_r)
+ & ~ ((HOST_WIDE_INT) 1 << BITS_PER_UNIT));
+ else
+ rlow = expand_binop (index_mode, and_optab, lo_r,
+ GEN_INT (~((HOST_WIDE_INT) 1 << BITS_PER_UNIT)),
+ NULL_RTX, iunsignedp, OPTAB_LIB_WIDEN);
+
+ diff = expand_binop (index_mode, sub_optab, index_val, rlow,
+ NULL_RTX, iunsignedp, OPTAB_LIB_WIDEN);
+
+ quo = expand_divmod (0, TRUNC_DIV_EXPR, index_mode, diff,
+ GEN_INT (BITS_PER_UNIT), NULL_RTX, iunsignedp);
+ rem = expand_divmod (1, TRUNC_MOD_EXPR, index_mode, index_val,
+ GEN_INT (BITS_PER_UNIT), NULL_RTX, iunsignedp);
+
+ addr = memory_address (byte_mode,
+ expand_binop (index_mode, add_optab, diff,
+ setaddr, NULL_RTX, iunsignedp,
+ OPTAB_LIB_WIDEN));
+
+ /* Extract the bit we want to examine */
+ bit = expand_shift (RSHIFT_EXPR, byte_mode,
+ gen_rtx (MEM, byte_mode, addr),
+ make_tree (TREE_TYPE (index), rem),
+ NULL_RTX, 1);
+ result = expand_binop (byte_mode, and_optab, bit, const1_rtx,
+ GET_MODE (target) == byte_mode ? target : 0,
+ 1, OPTAB_LIB_WIDEN);
+
+ if (result != target)
+ convert_move (target, result, 1);
+
+ /* Output the code to handle the out-of-range case. */
+ emit_jump (op0);
+ emit_label (op1);
+ emit_move_insn (target, const0_rtx);
+ emit_label (op0);
+ return target;
+ }
+
+ case WITH_CLEANUP_EXPR:
+ if (RTL_EXPR_RTL (exp) == 0)
+ {
+ RTL_EXPR_RTL (exp)
+ = expand_expr (TREE_OPERAND (exp, 0), target, tmode, modifier);
+ cleanups_this_call
+ = tree_cons (NULL_TREE, TREE_OPERAND (exp, 2), cleanups_this_call);
+ /* That's it for this cleanup. */
+ TREE_OPERAND (exp, 2) = 0;
+ (*interim_eh_hook) (NULL_TREE);
+ }
+ return RTL_EXPR_RTL (exp);
+
+ case CLEANUP_POINT_EXPR:
+ {
+ extern int temp_slot_level;
+ tree old_cleanups = cleanups_this_call;
+ int old_temp_level = target_temp_slot_level;
+ push_temp_slots ();
+ target_temp_slot_level = temp_slot_level;
+ op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode, modifier);
+ expand_cleanups_to (old_cleanups);
+ preserve_temp_slots (op0);
+ free_temp_slots ();
+ pop_temp_slots ();
+ target_temp_slot_level = old_temp_level;
+ }
+ return op0;
+
+ case CALL_EXPR:
+ /* Check for a built-in function. */
+ if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
+ && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
+ == FUNCTION_DECL)
+ && DECL_BUILT_IN (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
+ return expand_builtin (exp, target, subtarget, tmode, ignore);
+
+ /* If this call was expanded already by preexpand_calls,
+ just return the result we got. */
+ if (CALL_EXPR_RTL (exp) != 0)
+ return CALL_EXPR_RTL (exp);
+
+ return expand_call (exp, target, ignore);
+
+ case NON_LVALUE_EXPR:
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ case REFERENCE_EXPR:
+ if (mode == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))
+ {
+ op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode,
+ modifier);
+
+ /* If the signedness of the conversion differs and OP0 is
+ a promoted SUBREG, clear that indication since we now
+ have to do the proper extension. */
+ if (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))) != unsignedp
+ && GET_CODE (op0) == SUBREG)
+ SUBREG_PROMOTED_VAR_P (op0) = 0;
+
+ return op0;
+ }
+
+ if (TREE_CODE (type) == UNION_TYPE)
+ {
+ tree valtype = TREE_TYPE (TREE_OPERAND (exp, 0));
+ if (target == 0)
+ {
+ if (mode == BLKmode)
+ {
+ if (TYPE_SIZE (type) == 0
+ || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+ abort ();
+ target = assign_stack_temp (BLKmode,
+ (TREE_INT_CST_LOW (TYPE_SIZE (type))
+ + BITS_PER_UNIT - 1)
+ / BITS_PER_UNIT, 0);
+ }
+ else
+ target = gen_reg_rtx (tmode != VOIDmode ? tmode : mode);
+ }
+
+ if (GET_CODE (target) == MEM)
+ /* Store data into beginning of memory target. */
+ store_expr (TREE_OPERAND (exp, 0),
+ change_address (target, TYPE_MODE (valtype), 0), 0);
+
+ else if (GET_CODE (target) == REG)
+ /* Store this field into a union of the proper type. */
+ store_field (target, GET_MODE_BITSIZE (TYPE_MODE (valtype)), 0,
+ TYPE_MODE (valtype), TREE_OPERAND (exp, 0),
+ VOIDmode, 0, 1,
+ int_size_in_bytes (TREE_TYPE (TREE_OPERAND (exp, 0))));
+ else
+ abort ();
+
+ /* Return the entire union. */
+ return target;
+ }
+
+ op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, mode, 0);
+ if (GET_MODE (op0) == mode)
+ return op0;
+
+ /* If OP0 is a constant, just convert it into the proper mode. */
+ if (CONSTANT_P (op0))
+ return
+ convert_modes (mode, TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))),
+ op0, TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))));
+
+ if (modifier == EXPAND_INITIALIZER)
+ return gen_rtx (unsignedp ? ZERO_EXTEND : SIGN_EXTEND, mode, op0);
+
+ if (flag_force_mem && GET_CODE (op0) == MEM)
+ op0 = copy_to_reg (op0);
+
+ if (target == 0)
+ return
+ convert_to_mode (mode, op0,
+ TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))));
+ else
+ convert_move (target, op0,
+ TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))));
+ return target;
+
+ case PLUS_EXPR:
+ /* We come here from MINUS_EXPR when the second operand is a constant. */
+ plus_expr:
+ this_optab = add_optab;
+
+ /* If we are adding a constant, an RTL_EXPR that is sp, fp, or ap, and
+ something else, make sure we add the register to the constant and
+ then to the other thing. This case can occur during strength
+ reduction and doing it this way will produce better code if the
+ frame pointer or argument pointer is eliminated.
+
+ fold-const.c will ensure that the constant is always in the inner
+ PLUS_EXPR, so the only case we need to do anything about is if
+ sp, ap, or fp is our second argument, in which case we must swap
+ the innermost first argument and our second argument. */
+
+ if (TREE_CODE (TREE_OPERAND (exp, 0)) == PLUS_EXPR
+ && TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 1)) == INTEGER_CST
+ && TREE_CODE (TREE_OPERAND (exp, 1)) == RTL_EXPR
+ && (RTL_EXPR_RTL (TREE_OPERAND (exp, 1)) == frame_pointer_rtx
+ || RTL_EXPR_RTL (TREE_OPERAND (exp, 1)) == stack_pointer_rtx
+ || RTL_EXPR_RTL (TREE_OPERAND (exp, 1)) == arg_pointer_rtx))
+ {
+ tree t = TREE_OPERAND (exp, 1);
+
+ TREE_OPERAND (exp, 1) = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ TREE_OPERAND (TREE_OPERAND (exp, 0), 0) = t;
+ }
+
+ /* If the result is to be Pmode and we are adding an integer to
+ something, we might be forming a constant. So try to use
+ plus_constant. If it produces a sum and we can't accept it,
+ use force_operand. This allows P = &ARR[const] to generate
+ efficient code on machines where a SYMBOL_REF is not a valid
+ address.
+
+ If this is an EXPAND_SUM call, always return the sum. */
+ if (modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER
+ || mode == Pmode)
+ {
+ if (TREE_CODE (TREE_OPERAND (exp, 0)) == INTEGER_CST
+ && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+ && TREE_CONSTANT (TREE_OPERAND (exp, 1)))
+ {
+ op1 = expand_expr (TREE_OPERAND (exp, 1), subtarget, VOIDmode,
+ EXPAND_SUM);
+ op1 = plus_constant (op1, TREE_INT_CST_LOW (TREE_OPERAND (exp, 0)));
+ if (modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
+ op1 = force_operand (op1, target);
+ return op1;
+ }
+
+ else if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST
+ && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_INT
+ && TREE_CONSTANT (TREE_OPERAND (exp, 0)))
+ {
+ op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode,
+ EXPAND_SUM);
+ if (! CONSTANT_P (op0))
+ {
+ op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX,
+ VOIDmode, modifier);
+ /* Don't go to both_summands if modifier
+ says it's not right to return a PLUS. */
+ if (modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
+ goto binop2;
+ goto both_summands;
+ }
+ op0 = plus_constant (op0, TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)));
+ if (modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
+ op0 = force_operand (op0, target);
+ return op0;
+ }
+ }
+
+ /* No sense saving up arithmetic to be done
+ if it's all in the wrong mode to form part of an address.
+ And force_operand won't know whether to sign-extend or
+ zero-extend. */
+ if ((modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
+ || mode != Pmode)
+ goto binop;
+
+ preexpand_calls (exp);
+ if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1)))
+ subtarget = 0;
+
+ op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, modifier);
+ op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, modifier);
+
+ both_summands:
+ /* Make sure any term that's a sum with a constant comes last. */
+ if (GET_CODE (op0) == PLUS
+ && CONSTANT_P (XEXP (op0, 1)))
+ {
+ temp = op0;
+ op0 = op1;
+ op1 = temp;
+ }
+ /* If adding to a sum including a constant,
+ associate it to put the constant outside. */
+ if (GET_CODE (op1) == PLUS
+ && CONSTANT_P (XEXP (op1, 1)))
+ {
+ rtx constant_term = const0_rtx;
+
+ temp = simplify_binary_operation (PLUS, mode, XEXP (op1, 0), op0);
+ if (temp != 0)
+ op0 = temp;
+ /* Ensure that MULT comes first if there is one. */
+ else if (GET_CODE (op0) == MULT)
+ op0 = gen_rtx (PLUS, mode, op0, XEXP (op1, 0));
+ else
+ op0 = gen_rtx (PLUS, mode, XEXP (op1, 0), op0);
+
+ /* Let's also eliminate constants from op0 if possible. */
+ op0 = eliminate_constant_term (op0, &constant_term);
+
+ /* CONSTANT_TERM and XEXP (op1, 1) are known to be constant, so
+ their sum should be a constant. Form it into OP1, since the
+ result we want will then be OP0 + OP1. */
+
+ temp = simplify_binary_operation (PLUS, mode, constant_term,
+ XEXP (op1, 1));
+ if (temp != 0)
+ op1 = temp;
+ else
+ op1 = gen_rtx (PLUS, mode, constant_term, XEXP (op1, 1));
+ }
+
+ /* Put a constant term last and put a multiplication first. */
+ if (CONSTANT_P (op0) || GET_CODE (op1) == MULT)
+ temp = op1, op1 = op0, op0 = temp;
+
+ temp = simplify_binary_operation (PLUS, mode, op0, op1);
+ return temp ? temp : gen_rtx (PLUS, mode, op0, op1);
+
+ case MINUS_EXPR:
+ /* For initializers, we are allowed to return a MINUS of two
+ symbolic constants. Here we handle all cases when both operands
+ are constant. */
+ /* Handle difference of two symbolic constants,
+ for the sake of an initializer. */
+ if ((modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER)
+ && really_constant_p (TREE_OPERAND (exp, 0))
+ && really_constant_p (TREE_OPERAND (exp, 1)))
+ {
+ rtx op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX,
+ VOIDmode, modifier);
+ rtx op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX,
+ VOIDmode, modifier);
+
+ /* If one operand is a CONST_INT, put it last. */
+ if (GET_CODE (op0) == CONST_INT)
+ temp = op0, op0 = op1, op1 = temp;
+
+ /* If the last operand is a CONST_INT, use plus_constant of
+ the negated constant. Else make the MINUS. */
+ if (GET_CODE (op1) == CONST_INT)
+ return plus_constant (op0, - INTVAL (op1));
+ else
+ return gen_rtx (MINUS, mode, op0, op1);
+ }
+ /* Convert A - const to A + (-const). */
+ if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST)
+ {
+ exp = build (PLUS_EXPR, type, TREE_OPERAND (exp, 0),
+ fold (build1 (NEGATE_EXPR, type,
+ TREE_OPERAND (exp, 1))));
+ goto plus_expr;
+ }
+ this_optab = sub_optab;
+ goto binop;
+
+ case MULT_EXPR:
+ preexpand_calls (exp);
+ /* If first operand is constant, swap them.
+ Thus the following special case checks need only
+ check the second operand. */
+ if (TREE_CODE (TREE_OPERAND (exp, 0)) == INTEGER_CST)
+ {
+ register tree t1 = TREE_OPERAND (exp, 0);
+ TREE_OPERAND (exp, 0) = TREE_OPERAND (exp, 1);
+ TREE_OPERAND (exp, 1) = t1;
+ }
+
+ /* Attempt to return something suitable for generating an
+ indexed address, for machines that support that. */
+
+ if (modifier == EXPAND_SUM && mode == Pmode
+ && TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST
+ && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
+ {
+ op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, EXPAND_SUM);
+
+ /* Apply distributive law if OP0 is x+c. */
+ if (GET_CODE (op0) == PLUS
+ && GET_CODE (XEXP (op0, 1)) == CONST_INT)
+ return gen_rtx (PLUS, mode,
+ gen_rtx (MULT, mode, XEXP (op0, 0),
+ GEN_INT (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)))),
+ GEN_INT (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1))
+ * INTVAL (XEXP (op0, 1))));
+
+ if (GET_CODE (op0) != REG)
+ op0 = force_operand (op0, NULL_RTX);
+ if (GET_CODE (op0) != REG)
+ op0 = copy_to_mode_reg (mode, op0);
+
+ return gen_rtx (MULT, mode, op0,
+ GEN_INT (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1))));
+ }
+
+ if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1)))
+ subtarget = 0;
+
+ /* Check for multiplying things that have been extended
+ from a narrower type. If this machine supports multiplying
+ in that narrower type with a result in the desired type,
+ do it that way, and avoid the explicit type-conversion. */
+ if (TREE_CODE (TREE_OPERAND (exp, 0)) == NOP_EXPR
+ && TREE_CODE (type) == INTEGER_TYPE
+ && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
+ < TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (exp, 0))))
+ && ((TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST
+ && int_fits_type_p (TREE_OPERAND (exp, 1),
+ TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
+ /* Don't use a widening multiply if a shift will do. */
+ && ((GET_MODE_BITSIZE (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 1))))
+ > HOST_BITS_PER_WIDE_INT)
+ || exact_log2 (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1))) < 0))
+ ||
+ (TREE_CODE (TREE_OPERAND (exp, 1)) == NOP_EXPR
+ && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 1), 0)))
+ ==
+ TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))))
+ /* If both operands are extended, they must either both
+ be zero-extended or both be sign-extended. */
+ && (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 1), 0)))
+ ==
+ TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))))))
+ {
+ enum machine_mode innermode
+ = TYPE_MODE (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)));
+ this_optab = (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
+ ? umul_widen_optab : smul_widen_optab);
+ if (mode == GET_MODE_WIDER_MODE (innermode)
+ && this_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+ {
+ op0 = expand_expr (TREE_OPERAND (TREE_OPERAND (exp, 0), 0),
+ NULL_RTX, VOIDmode, 0);
+ if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST)
+ op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX,
+ VOIDmode, 0);
+ else
+ op1 = expand_expr (TREE_OPERAND (TREE_OPERAND (exp, 1), 0),
+ NULL_RTX, VOIDmode, 0);
+ goto binop2;
+ }
+ }
+ op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
+ op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0);
+ return expand_mult (mode, op0, op1, target, unsignedp);
+
+ case TRUNC_DIV_EXPR:
+ case FLOOR_DIV_EXPR:
+ case CEIL_DIV_EXPR:
+ case ROUND_DIV_EXPR:
+ case EXACT_DIV_EXPR:
+ preexpand_calls (exp);
+ if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1)))
+ subtarget = 0;
+ /* Possible optimization: compute the dividend with EXPAND_SUM
+ then if the divisor is constant can optimize the case
+ where some terms of the dividend have coeffs divisible by it. */
+ op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
+ op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0);
+ return expand_divmod (0, code, mode, op0, op1, target, unsignedp);
+
+ case RDIV_EXPR:
+ this_optab = flodiv_optab;
+ goto binop;
+
+ case TRUNC_MOD_EXPR:
+ case FLOOR_MOD_EXPR:
+ case CEIL_MOD_EXPR:
+ case ROUND_MOD_EXPR:
+ preexpand_calls (exp);
+ if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1)))
+ subtarget = 0;
+ op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
+ op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0);
+ return expand_divmod (1, code, mode, op0, op1, target, unsignedp);
+
+ case FIX_ROUND_EXPR:
+ case FIX_FLOOR_EXPR:
+ case FIX_CEIL_EXPR:
+ abort (); /* Not used for C. */
+
+ case FIX_TRUNC_EXPR:
+ op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0);
+ if (target == 0)
+ target = gen_reg_rtx (mode);
+ expand_fix (target, op0, unsignedp);
+ return target;
+
+ case FLOAT_EXPR:
+ op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0);
+ if (target == 0)
+ target = gen_reg_rtx (mode);
+ /* expand_float can't figure out what to do if FROM has VOIDmode.
+ So give it the correct mode. With -O, cse will optimize this. */
+ if (GET_MODE (op0) == VOIDmode)
+ op0 = copy_to_mode_reg (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))),
+ op0);
+ expand_float (target, op0,
+ TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))));
+ return target;
+
+ case NEGATE_EXPR:
+ op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
+ temp = expand_unop (mode, neg_optab, op0, target, 0);
+ if (temp == 0)
+ abort ();
+ return temp;
+
+ case ABS_EXPR:
+ op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
+
+ /* Handle complex values specially. */
+ if (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT
+ || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+ return expand_complex_abs (mode, op0, target, unsignedp);
+
+ /* Unsigned abs is simply the operand. Testing here means we don't
+ risk generating incorrect code below. */
+ if (TREE_UNSIGNED (type))
+ return op0;
+
+ /* First try to do it with a special abs instruction. */
+ temp = expand_unop (mode, abs_optab, op0, target, 0);
+ if (temp != 0)
+ return temp;
+
+ /* If this machine has expensive jumps, we can do integer absolute
+ value of X as (((signed) x >> (W-1)) ^ x) - ((signed) x >> (W-1)),
+ where W is the width of MODE. */
+
+ if (GET_MODE_CLASS (mode) == MODE_INT && BRANCH_COST >= 2)
+ {
+ rtx extended = expand_shift (RSHIFT_EXPR, mode, op0,
+ size_int (GET_MODE_BITSIZE (mode) - 1),
+ NULL_RTX, 0);
+
+ temp = expand_binop (mode, xor_optab, extended, op0, target, 0,
+ OPTAB_LIB_WIDEN);
+ if (temp != 0)
+ temp = expand_binop (mode, sub_optab, temp, extended, target, 0,
+ OPTAB_LIB_WIDEN);
+
+ if (temp != 0)
+ return temp;
+ }
+
+ /* If that does not win, use conditional jump and negate. */
+ target = original_target;
+ op1 = gen_label_rtx ();
+ if (target == 0 || ! safe_from_p (target, TREE_OPERAND (exp, 0))
+ || GET_MODE (target) != mode
+ || (GET_CODE (target) == MEM && MEM_VOLATILE_P (target))
+ || (GET_CODE (target) == REG
+ && REGNO (target) < FIRST_PSEUDO_REGISTER))
+ target = gen_reg_rtx (mode);
+
+ emit_move_insn (target, op0);
+ NO_DEFER_POP;
+
+ /* If this mode is an integer too wide to compare properly,
+ compare word by word. Rely on CSE to optimize constant cases. */
+ if (GET_MODE_CLASS (mode) == MODE_INT && ! can_compare_p (mode))
+ do_jump_by_parts_greater_rtx (mode, 0, target, const0_rtx,
+ NULL_RTX, op1);
+ else
+ {
+ temp = compare_from_rtx (target, CONST0_RTX (mode), GE, 0, mode,
+ NULL_RTX, 0);
+ if (temp == const1_rtx)
+ return target;
+ else if (temp != const0_rtx)
+ {
+ if (bcc_gen_fctn[(int) GET_CODE (temp)] != 0)
+ emit_jump_insn ((*bcc_gen_fctn[(int) GET_CODE (temp)]) (op1));
+ else
+ abort ();
+ }
+ }
+
+ op0 = expand_unop (mode, neg_optab, target, target, 0);
+ if (op0 != target)
+ emit_move_insn (target, op0);
+ emit_label (op1);
+ OK_DEFER_POP;
+ return target;
+
+ case MAX_EXPR:
+ case MIN_EXPR:
+ target = original_target;
+ if (target == 0 || ! safe_from_p (target, TREE_OPERAND (exp, 1))
+ || (GET_CODE (target) == MEM && MEM_VOLATILE_P (target))
+ || GET_MODE (target) != mode
+ || (GET_CODE (target) == REG
+ && REGNO (target) < FIRST_PSEUDO_REGISTER))
+ target = gen_reg_rtx (mode);
+ op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0);
+ op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode, 0);
+
+ /* First try to do it with a special MIN or MAX instruction.
+ If that does not win, use a conditional jump to select the proper
+ value. */
+ this_optab = (TREE_UNSIGNED (type)
+ ? (code == MIN_EXPR ? umin_optab : umax_optab)
+ : (code == MIN_EXPR ? smin_optab : smax_optab));
+
+ temp = expand_binop (mode, this_optab, op0, op1, target, unsignedp,
+ OPTAB_WIDEN);
+ if (temp != 0)
+ return temp;
+
+ if (target != op0)
+ emit_move_insn (target, op0);
+
+ op0 = gen_label_rtx ();
+
+ /* If this mode is an integer too wide to compare properly,
+ compare word by word. Rely on cse to optimize constant cases. */
+ if (GET_MODE_CLASS (mode) == MODE_INT && !can_compare_p (mode))
+ {
+ if (code == MAX_EXPR)
+ do_jump_by_parts_greater_rtx (mode, TREE_UNSIGNED (type),
+ target, op1, NULL_RTX, op0);
+ else
+ do_jump_by_parts_greater_rtx (mode, TREE_UNSIGNED (type),
+ op1, target, NULL_RTX, op0);
+ emit_move_insn (target, op1);
+ }
+ else
+ {
+ if (code == MAX_EXPR)
+ temp = (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 1)))
+ ? compare_from_rtx (target, op1, GEU, 1, mode, NULL_RTX, 0)
+ : compare_from_rtx (target, op1, GE, 0, mode, NULL_RTX, 0));
+ else
+ temp = (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 1)))
+ ? compare_from_rtx (target, op1, LEU, 1, mode, NULL_RTX, 0)
+ : compare_from_rtx (target, op1, LE, 0, mode, NULL_RTX, 0));
+ if (temp == const0_rtx)
+ emit_move_insn (target, op1);
+ else if (temp != const_true_rtx)
+ {
+ if (bcc_gen_fctn[(int) GET_CODE (temp)] != 0)
+ emit_jump_insn ((*bcc_gen_fctn[(int) GET_CODE (temp)]) (op0));
+ else
+ abort ();
+ emit_move_insn (target, op1);
+ }
+ }
+ emit_label (op0);
+ return target;
+
+ case BIT_NOT_EXPR:
+ op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
+ temp = expand_unop (mode, one_cmpl_optab, op0, target, 1);
+ if (temp == 0)
+ abort ();
+ return temp;
+
+ case FFS_EXPR:
+ op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
+ temp = expand_unop (mode, ffs_optab, op0, target, 1);
+ if (temp == 0)
+ abort ();
+ return temp;
+
+ /* ??? Can optimize bitwise operations with one arg constant.
+ Can optimize (a bitwise1 n) bitwise2 (a bitwise3 b)
+ and (a bitwise1 b) bitwise2 b (etc)
+ but that is probably not worth while. */
+
+ /* BIT_AND_EXPR is for bitwise anding. TRUTH_AND_EXPR is for anding two
+ boolean values when we want in all cases to compute both of them. In
+ general it is fastest to do TRUTH_AND_EXPR by computing both operands
+ as actual zero-or-1 values and then bitwise anding. In cases where
+ there cannot be any side effects, better code would be made by
+ treating TRUTH_AND_EXPR like TRUTH_ANDIF_EXPR; but the question is
+ how to recognize those cases. */
+
+ case TRUTH_AND_EXPR:
+ case BIT_AND_EXPR:
+ this_optab = and_optab;
+ goto binop;
+
+ case TRUTH_OR_EXPR:
+ case BIT_IOR_EXPR:
+ this_optab = ior_optab;
+ goto binop;
+
+ case TRUTH_XOR_EXPR:
+ case BIT_XOR_EXPR:
+ this_optab = xor_optab;
+ goto binop;
+
+ case LSHIFT_EXPR:
+ case RSHIFT_EXPR:
+ case LROTATE_EXPR:
+ case RROTATE_EXPR:
+ preexpand_calls (exp);
+ if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1)))
+ subtarget = 0;
+ op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
+ return expand_shift (code, mode, op0, TREE_OPERAND (exp, 1), target,
+ unsignedp);
+
+ /* Could determine the answer when only additive constants differ. Also,
+ the addition of one can be handled by changing the condition. */
+ case LT_EXPR:
+ case LE_EXPR:
+ case GT_EXPR:
+ case GE_EXPR:
+ case EQ_EXPR:
+ case NE_EXPR:
+ preexpand_calls (exp);
+ temp = do_store_flag (exp, target, tmode != VOIDmode ? tmode : mode, 0);
+ if (temp != 0)
+ return temp;
+
+ /* For foo != 0, load foo, and if it is nonzero load 1 instead. */
+ if (code == NE_EXPR && integer_zerop (TREE_OPERAND (exp, 1))
+ && original_target
+ && GET_CODE (original_target) == REG
+ && (GET_MODE (original_target)
+ == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))))
+ {
+ temp = expand_expr (TREE_OPERAND (exp, 0), original_target,
+ VOIDmode, 0);
+
+ if (temp != original_target)
+ temp = copy_to_reg (temp);
+
+ op1 = gen_label_rtx ();
+ emit_cmp_insn (temp, const0_rtx, EQ, NULL_RTX,
+ GET_MODE (temp), unsignedp, 0);
+ emit_jump_insn (gen_beq (op1));
+ emit_move_insn (temp, const1_rtx);
+ emit_label (op1);
+ return temp;
+ }
+
+ /* If no set-flag instruction, must generate a conditional
+ store into a temporary variable. Drop through
+ and handle this like && and ||. */
+
+ case TRUTH_ANDIF_EXPR:
+ case TRUTH_ORIF_EXPR:
+ if (! ignore
+ && (target == 0 || ! safe_from_p (target, exp)
+ /* Make sure we don't have a hard reg (such as function's return
+ value) live across basic blocks, if not optimizing. */
+ || (!optimize && GET_CODE (target) == REG
+ && REGNO (target) < FIRST_PSEUDO_REGISTER)))
+ target = gen_reg_rtx (tmode != VOIDmode ? tmode : mode);
+
+ if (target)
+ emit_clr_insn (target);
+
+ op1 = gen_label_rtx ();
+ jumpifnot (exp, op1);
+
+ if (target)
+ emit_0_to_1_insn (target);
+
+ emit_label (op1);
+ return ignore ? const0_rtx : target;
+
+ case TRUTH_NOT_EXPR:
+ op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode, 0);
+ /* The parser is careful to generate TRUTH_NOT_EXPR
+ only with operands that are always zero or one. */
+ temp = expand_binop (mode, xor_optab, op0, const1_rtx,
+ target, 1, OPTAB_LIB_WIDEN);
+ if (temp == 0)
+ abort ();
+ return temp;
+
+ case COMPOUND_EXPR:
+ expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, 0);
+ emit_queue ();
+ return expand_expr (TREE_OPERAND (exp, 1),
+ (ignore ? const0_rtx : target),
+ VOIDmode, 0);
+
+ case COND_EXPR:
+ {
+ rtx flag = NULL_RTX;
+ tree left_cleanups = NULL_TREE;
+ tree right_cleanups = NULL_TREE;
+
+ /* Used to save a pointer to the place to put the setting of
+ the flag that indicates if this side of the conditional was
+ taken. We backpatch the code, if we find out later that we
+ have any conditional cleanups that need to be performed. */
+ rtx dest_right_flag = NULL_RTX;
+ rtx dest_left_flag = NULL_RTX;
+
+ /* Note that COND_EXPRs whose type is a structure or union
+ are required to be constructed to contain assignments of
+ a temporary variable, so that we can evaluate them here
+ for side effect only. If type is void, we must do likewise. */
+
+ /* If an arm of the branch requires a cleanup,
+ only that cleanup is performed. */
+
+ tree singleton = 0;
+ tree binary_op = 0, unary_op = 0;
+ tree old_cleanups = cleanups_this_call;
+
+ /* If this is (A ? 1 : 0) and A is a condition, just evaluate it and
+ convert it to our mode, if necessary. */
+ if (integer_onep (TREE_OPERAND (exp, 1))
+ && integer_zerop (TREE_OPERAND (exp, 2))
+ && TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == '<')
+ {
+ if (ignore)
+ {
+ expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode,
+ modifier);
+ return const0_rtx;
+ }
+
+ op0 = expand_expr (TREE_OPERAND (exp, 0), target, mode, modifier);
+ if (GET_MODE (op0) == mode)
+ return op0;
+
+ if (target == 0)
+ target = gen_reg_rtx (mode);
+ convert_move (target, op0, unsignedp);
+ return target;
+ }
+
+ /* If we are not to produce a result, we have no target. Otherwise,
+ if a target was specified use it; it will not be used as an
+ intermediate target unless it is safe. If no target, use a
+ temporary. */
+
+ if (ignore)
+ temp = 0;
+ else if (original_target
+ && safe_from_p (original_target, TREE_OPERAND (exp, 0))
+ && GET_MODE (original_target) == mode)
+ temp = original_target;
+ else if (mode == BLKmode)
+ {
+ if (TYPE_SIZE (type) == 0
+ || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+ abort ();
+
+ temp = assign_stack_temp (BLKmode,
+ (TREE_INT_CST_LOW (TYPE_SIZE (type))
+ + BITS_PER_UNIT - 1)
+ / BITS_PER_UNIT, 0);
+ MEM_IN_STRUCT_P (temp) = AGGREGATE_TYPE_P (type);
+ }
+ else
+ temp = gen_reg_rtx (mode);
+
+ /* Check for X ? A + B : A. If we have this, we can copy
+ A to the output and conditionally add B. Similarly for unary
+ operations. Don't do this if X has side-effects because
+ those side effects might affect A or B and the "?" operation is
+ a sequence point in ANSI. (We test for side effects later.) */
+
+ if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 1))) == '2'
+ && operand_equal_p (TREE_OPERAND (exp, 2),
+ TREE_OPERAND (TREE_OPERAND (exp, 1), 0), 0))
+ singleton = TREE_OPERAND (exp, 2), binary_op = TREE_OPERAND (exp, 1);
+ else if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 2))) == '2'
+ && operand_equal_p (TREE_OPERAND (exp, 1),
+ TREE_OPERAND (TREE_OPERAND (exp, 2), 0), 0))
+ singleton = TREE_OPERAND (exp, 1), binary_op = TREE_OPERAND (exp, 2);
+ else if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 1))) == '1'
+ && operand_equal_p (TREE_OPERAND (exp, 2),
+ TREE_OPERAND (TREE_OPERAND (exp, 1), 0), 0))
+ singleton = TREE_OPERAND (exp, 2), unary_op = TREE_OPERAND (exp, 1);
+ else if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 2))) == '1'
+ && operand_equal_p (TREE_OPERAND (exp, 1),
+ TREE_OPERAND (TREE_OPERAND (exp, 2), 0), 0))
+ singleton = TREE_OPERAND (exp, 1), unary_op = TREE_OPERAND (exp, 2);
+
+ /* If we had X ? A + 1 : A and we can do the test of X as a store-flag
+ operation, do this as A + (X != 0). Similarly for other simple
+ binary operators. */
+ if (temp && singleton && binary_op
+ && ! TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 0))
+ && (TREE_CODE (binary_op) == PLUS_EXPR
+ || TREE_CODE (binary_op) == MINUS_EXPR
+ || TREE_CODE (binary_op) == BIT_IOR_EXPR
+ || TREE_CODE (binary_op) == BIT_XOR_EXPR
+ || TREE_CODE (binary_op) == BIT_AND_EXPR)
+ && integer_onep (TREE_OPERAND (binary_op, 1))
+ && TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == '<')
+ {
+ rtx result;
+ optab boptab = (TREE_CODE (binary_op) == PLUS_EXPR ? add_optab
+ : TREE_CODE (binary_op) == MINUS_EXPR ? sub_optab
+ : TREE_CODE (binary_op) == BIT_IOR_EXPR ? ior_optab
+ : TREE_CODE (binary_op) == BIT_XOR_EXPR ? xor_optab
+ : and_optab);
+
+ /* If we had X ? A : A + 1, do this as A + (X == 0).
+
+ We have to invert the truth value here and then put it
+ back later if do_store_flag fails. We cannot simply copy
+ TREE_OPERAND (exp, 0) to another variable and modify that
+ because invert_truthvalue can modify the tree pointed to
+ by its argument. */
+ if (singleton == TREE_OPERAND (exp, 1))
+ TREE_OPERAND (exp, 0)
+ = invert_truthvalue (TREE_OPERAND (exp, 0));
+
+ result = do_store_flag (TREE_OPERAND (exp, 0),
+ (safe_from_p (temp, singleton)
+ ? temp : NULL_RTX),
+ mode, BRANCH_COST <= 1);
+
+ if (result)
+ {
+ op1 = expand_expr (singleton, NULL_RTX, VOIDmode, 0);
+ return expand_binop (mode, boptab, op1, result, temp,
+ unsignedp, OPTAB_LIB_WIDEN);
+ }
+ else if (singleton == TREE_OPERAND (exp, 1))
+ TREE_OPERAND (exp, 0)
+ = invert_truthvalue (TREE_OPERAND (exp, 0));
+ }
+
+ NO_DEFER_POP;
+ op0 = gen_label_rtx ();
+
+ flag = gen_reg_rtx (word_mode);
+ if (singleton && ! TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 0)))
+ {
+ if (temp != 0)
+ {
+ /* If the target conflicts with the other operand of the
+ binary op, we can't use it. Also, we can't use the target
+ if it is a hard register, because evaluating the condition
+ might clobber it. */
+ if ((binary_op
+ && ! safe_from_p (temp, TREE_OPERAND (binary_op, 1)))
+ || (GET_CODE (temp) == REG
+ && REGNO (temp) < FIRST_PSEUDO_REGISTER))
+ temp = gen_reg_rtx (mode);
+ store_expr (singleton, temp, 0);
+ }
+ else
+ expand_expr (singleton,
+ ignore ? const0_rtx : NULL_RTX, VOIDmode, 0);
+ dest_left_flag = get_last_insn ();
+ if (singleton == TREE_OPERAND (exp, 1))
+ jumpif (TREE_OPERAND (exp, 0), op0);
+ else
+ jumpifnot (TREE_OPERAND (exp, 0), op0);
+
+ /* Allows cleanups up to here. */
+ old_cleanups = cleanups_this_call;
+ if (binary_op && temp == 0)
+ /* Just touch the other operand. */
+ expand_expr (TREE_OPERAND (binary_op, 1),
+ ignore ? const0_rtx : NULL_RTX, VOIDmode, 0);
+ else if (binary_op)
+ store_expr (build (TREE_CODE (binary_op), type,
+ make_tree (type, temp),
+ TREE_OPERAND (binary_op, 1)),
+ temp, 0);
+ else
+ store_expr (build1 (TREE_CODE (unary_op), type,
+ make_tree (type, temp)),
+ temp, 0);
+ op1 = op0;
+ dest_right_flag = get_last_insn ();
+ }
+#if 0
+ /* This is now done in jump.c and is better done there because it
+ produces shorter register lifetimes. */
+
+ /* Check for both possibilities either constants or variables
+ in registers (but not the same as the target!). If so, can
+ save branches by assigning one, branching, and assigning the
+ other. */
+ else if (temp && GET_MODE (temp) != BLKmode
+ && (TREE_CONSTANT (TREE_OPERAND (exp, 1))
+ || ((TREE_CODE (TREE_OPERAND (exp, 1)) == PARM_DECL
+ || TREE_CODE (TREE_OPERAND (exp, 1)) == VAR_DECL)
+ && DECL_RTL (TREE_OPERAND (exp, 1))
+ && GET_CODE (DECL_RTL (TREE_OPERAND (exp, 1))) == REG
+ && DECL_RTL (TREE_OPERAND (exp, 1)) != temp))
+ && (TREE_CONSTANT (TREE_OPERAND (exp, 2))
+ || ((TREE_CODE (TREE_OPERAND (exp, 2)) == PARM_DECL
+ || TREE_CODE (TREE_OPERAND (exp, 2)) == VAR_DECL)
+ && DECL_RTL (TREE_OPERAND (exp, 2))
+ && GET_CODE (DECL_RTL (TREE_OPERAND (exp, 2))) == REG
+ && DECL_RTL (TREE_OPERAND (exp, 2)) != temp)))
+ {
+ if (GET_CODE (temp) == REG && REGNO (temp) < FIRST_PSEUDO_REGISTER)
+ temp = gen_reg_rtx (mode);
+ store_expr (TREE_OPERAND (exp, 2), temp, 0);
+ dest_left_flag = get_last_insn ();
+ jumpifnot (TREE_OPERAND (exp, 0), op0);
+
+ /* Allows cleanups up to here. */
+ old_cleanups = cleanups_this_call;
+ store_expr (TREE_OPERAND (exp, 1), temp, 0);
+ op1 = op0;
+ dest_right_flag = get_last_insn ();
+ }
+#endif
+ /* Check for A op 0 ? A : FOO and A op 0 ? FOO : A where OP is any
+ comparison operator. If we have one of these cases, set the
+ output to A, branch on A (cse will merge these two references),
+ then set the output to FOO. */
+ else if (temp
+ && TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == '<'
+ && integer_zerop (TREE_OPERAND (TREE_OPERAND (exp, 0), 1))
+ && operand_equal_p (TREE_OPERAND (TREE_OPERAND (exp, 0), 0),
+ TREE_OPERAND (exp, 1), 0)
+ && ! TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 0))
+ && safe_from_p (temp, TREE_OPERAND (exp, 2)))
+ {
+ if (GET_CODE (temp) == REG && REGNO (temp) < FIRST_PSEUDO_REGISTER)
+ temp = gen_reg_rtx (mode);
+ store_expr (TREE_OPERAND (exp, 1), temp, 0);
+ dest_left_flag = get_last_insn ();
+ jumpif (TREE_OPERAND (exp, 0), op0);
+
+ /* Allows cleanups up to here. */
+ old_cleanups = cleanups_this_call;
+ store_expr (TREE_OPERAND (exp, 2), temp, 0);
+ op1 = op0;
+ dest_right_flag = get_last_insn ();
+ }
+ else if (temp
+ && TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == '<'
+ && integer_zerop (TREE_OPERAND (TREE_OPERAND (exp, 0), 1))
+ && operand_equal_p (TREE_OPERAND (TREE_OPERAND (exp, 0), 0),
+ TREE_OPERAND (exp, 2), 0)
+ && ! TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 0))
+ && safe_from_p (temp, TREE_OPERAND (exp, 1)))
+ {
+ if (GET_CODE (temp) == REG && REGNO (temp) < FIRST_PSEUDO_REGISTER)
+ temp = gen_reg_rtx (mode);
+ store_expr (TREE_OPERAND (exp, 2), temp, 0);
+ dest_left_flag = get_last_insn ();
+ jumpifnot (TREE_OPERAND (exp, 0), op0);
+
+ /* Allows cleanups up to here. */
+ old_cleanups = cleanups_this_call;
+ store_expr (TREE_OPERAND (exp, 1), temp, 0);
+ op1 = op0;
+ dest_right_flag = get_last_insn ();
+ }
+ else
+ {
+ op1 = gen_label_rtx ();
+ jumpifnot (TREE_OPERAND (exp, 0), op0);
+
+ /* Allows cleanups up to here. */
+ old_cleanups = cleanups_this_call;
+ if (temp != 0)
+ store_expr (TREE_OPERAND (exp, 1), temp, 0);
+ else
+ expand_expr (TREE_OPERAND (exp, 1),
+ ignore ? const0_rtx : NULL_RTX, VOIDmode, 0);
+ dest_left_flag = get_last_insn ();
+
+ /* Handle conditional cleanups, if any. */
+ left_cleanups = defer_cleanups_to (old_cleanups);
+
+ emit_queue ();
+ emit_jump_insn (gen_jump (op1));
+ emit_barrier ();
+ emit_label (op0);
+ if (temp != 0)
+ store_expr (TREE_OPERAND (exp, 2), temp, 0);
+ else
+ expand_expr (TREE_OPERAND (exp, 2),
+ ignore ? const0_rtx : NULL_RTX, VOIDmode, 0);
+ dest_right_flag = get_last_insn ();
+ }
+
+ /* Handle conditional cleanups, if any. */
+ right_cleanups = defer_cleanups_to (old_cleanups);
+
+ emit_queue ();
+ emit_label (op1);
+ OK_DEFER_POP;
+
+ /* Add back in, any conditional cleanups. */
+ if (left_cleanups || right_cleanups)
+ {
+ tree new_cleanups;
+ tree cond;
+ rtx last;
+
+ /* Now that we know that a flag is needed, go back and add in the
+ setting of the flag. */
+
+ /* Do the left side flag. */
+ last = get_last_insn ();
+ /* Flag left cleanups as needed. */
+ emit_move_insn (flag, const1_rtx);
+ /* ??? deprecated, use sequences instead. */
+ reorder_insns (NEXT_INSN (last), get_last_insn (), dest_left_flag);
+
+ /* Do the right side flag. */
+ last = get_last_insn ();
+ /* Flag left cleanups as needed. */
+ emit_move_insn (flag, const0_rtx);
+ /* ??? deprecated, use sequences instead. */
+ reorder_insns (NEXT_INSN (last), get_last_insn (), dest_right_flag);
+
+ /* convert flag, which is an rtx, into a tree. */
+ cond = make_node (RTL_EXPR);
+ TREE_TYPE (cond) = integer_type_node;
+ RTL_EXPR_RTL (cond) = flag;
+ RTL_EXPR_SEQUENCE (cond) = NULL_RTX;
+
+ if (! left_cleanups)
+ left_cleanups = integer_zero_node;
+ if (! right_cleanups)
+ right_cleanups = integer_zero_node;
+ new_cleanups = build (COND_EXPR, void_type_node, cond,
+ left_cleanups, right_cleanups);
+ new_cleanups = fold (new_cleanups);
+
+ /* Now add in the conditionalized cleanups. */
+ cleanups_this_call
+ = tree_cons (NULL_TREE, new_cleanups, cleanups_this_call);
+ (*interim_eh_hook) (NULL_TREE);
+ }
+ return temp;
+ }
+
+ case TARGET_EXPR:
+ {
+ int need_exception_region = 0;
+ /* Something needs to be initialized, but we didn't know
+ where that thing was when building the tree. For example,
+ it could be the return value of a function, or a parameter
+ to a function which lays down in the stack, or a temporary
+ variable which must be passed by reference.
+
+ We guarantee that the expression will either be constructed
+ or copied into our original target. */
+
+ tree slot = TREE_OPERAND (exp, 0);
+ tree exp1;
+ rtx temp;
+
+ if (TREE_CODE (slot) != VAR_DECL)
+ abort ();
+
+ if (target == 0)
+ {
+ if (DECL_RTL (slot) != 0)
+ {
+ target = DECL_RTL (slot);
+ /* If we have already expanded the slot, so don't do
+ it again. (mrs) */
+ if (TREE_OPERAND (exp, 1) == NULL_TREE)
+ return target;
+ }
+ else
+ {
+ target = assign_stack_temp (mode, int_size_in_bytes (type), 2);
+ /* All temp slots at this level must not conflict. */
+ preserve_temp_slots (target);
+ DECL_RTL (slot) = target;
+
+ /* Since SLOT is not known to the called function
+ to belong to its stack frame, we must build an explicit
+ cleanup. This case occurs when we must build up a reference
+ to pass the reference as an argument. In this case,
+ it is very likely that such a reference need not be
+ built here. */
+
+ if (TREE_OPERAND (exp, 2) == 0)
+ TREE_OPERAND (exp, 2) = maybe_build_cleanup (slot);
+ if (TREE_OPERAND (exp, 2))
+ {
+ cleanups_this_call = tree_cons (NULL_TREE,
+ TREE_OPERAND (exp, 2),
+ cleanups_this_call);
+ need_exception_region = 1;
+ }
+ }
+ }
+ else
+ {
+ /* This case does occur, when expanding a parameter which
+ needs to be constructed on the stack. The target
+ is the actual stack address that we want to initialize.
+ The function we call will perform the cleanup in this case. */
+
+ /* If we have already assigned it space, use that space,
+ not target that we were passed in, as our target
+ parameter is only a hint. */
+ if (DECL_RTL (slot) != 0)
+ {
+ target = DECL_RTL (slot);
+ /* If we have already expanded the slot, so don't do
+ it again. (mrs) */
+ if (TREE_OPERAND (exp, 1) == NULL_TREE)
+ return target;
+ }
+
+ DECL_RTL (slot) = target;
+ }
+
+ exp1 = TREE_OPERAND (exp, 1);
+ /* Mark it as expanded. */
+ TREE_OPERAND (exp, 1) = NULL_TREE;
+
+ temp = expand_expr (exp1, target, tmode, modifier);
+
+ if (need_exception_region)
+ (*interim_eh_hook) (NULL_TREE);
+
+ return temp;
+ }
+
+ case INIT_EXPR:
+ {
+ tree lhs = TREE_OPERAND (exp, 0);
+ tree rhs = TREE_OPERAND (exp, 1);
+ tree noncopied_parts = 0;
+ tree lhs_type = TREE_TYPE (lhs);
+
+ temp = expand_assignment (lhs, rhs, ! ignore, original_target != 0);
+ if (TYPE_NONCOPIED_PARTS (lhs_type) != 0 && !fixed_type_p (rhs))
+ noncopied_parts = init_noncopied_parts (stabilize_reference (lhs),
+ TYPE_NONCOPIED_PARTS (lhs_type));
+ while (noncopied_parts != 0)
+ {
+ expand_assignment (TREE_VALUE (noncopied_parts),
+ TREE_PURPOSE (noncopied_parts), 0, 0);
+ noncopied_parts = TREE_CHAIN (noncopied_parts);
+ }
+ return temp;
+ }
+
+ case MODIFY_EXPR:
+ {
+ /* If lhs is complex, expand calls in rhs before computing it.
+ That's so we don't compute a pointer and save it over a call.
+ If lhs is simple, compute it first so we can give it as a
+ target if the rhs is just a call. This avoids an extra temp and copy
+ and that prevents a partial-subsumption which makes bad code.
+ Actually we could treat component_ref's of vars like vars. */
+
+ tree lhs = TREE_OPERAND (exp, 0);
+ tree rhs = TREE_OPERAND (exp, 1);
+ tree noncopied_parts = 0;
+ tree lhs_type = TREE_TYPE (lhs);
+
+ temp = 0;
+
+ if (TREE_CODE (lhs) != VAR_DECL
+ && TREE_CODE (lhs) != RESULT_DECL
+ && TREE_CODE (lhs) != PARM_DECL)
+ preexpand_calls (exp);
+
+ /* Check for |= or &= of a bitfield of size one into another bitfield
+ of size 1. In this case, (unless we need the result of the
+ assignment) we can do this more efficiently with a
+ test followed by an assignment, if necessary.
+
+ ??? At this point, we can't get a BIT_FIELD_REF here. But if
+ things change so we do, this code should be enhanced to
+ support it. */
+ if (ignore
+ && TREE_CODE (lhs) == COMPONENT_REF
+ && (TREE_CODE (rhs) == BIT_IOR_EXPR
+ || TREE_CODE (rhs) == BIT_AND_EXPR)
+ && TREE_OPERAND (rhs, 0) == lhs
+ && TREE_CODE (TREE_OPERAND (rhs, 1)) == COMPONENT_REF
+ && TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (lhs, 1))) == 1
+ && TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (TREE_OPERAND (rhs, 1), 1))) == 1)
+ {
+ rtx label = gen_label_rtx ();
+
+ do_jump (TREE_OPERAND (rhs, 1),
+ TREE_CODE (rhs) == BIT_IOR_EXPR ? label : 0,
+ TREE_CODE (rhs) == BIT_AND_EXPR ? label : 0);
+ expand_assignment (lhs, convert (TREE_TYPE (rhs),
+ (TREE_CODE (rhs) == BIT_IOR_EXPR
+ ? integer_one_node
+ : integer_zero_node)),
+ 0, 0);
+ do_pending_stack_adjust ();
+ emit_label (label);
+ return const0_rtx;
+ }
+
+ if (TYPE_NONCOPIED_PARTS (lhs_type) != 0
+ && ! (fixed_type_p (lhs) && fixed_type_p (rhs)))
+ noncopied_parts = save_noncopied_parts (stabilize_reference (lhs),
+ TYPE_NONCOPIED_PARTS (lhs_type));
+
+ temp = expand_assignment (lhs, rhs, ! ignore, original_target != 0);
+ while (noncopied_parts != 0)
+ {
+ expand_assignment (TREE_PURPOSE (noncopied_parts),
+ TREE_VALUE (noncopied_parts), 0, 0);
+ noncopied_parts = TREE_CHAIN (noncopied_parts);
+ }
+ return temp;
+ }
+
+ case PREINCREMENT_EXPR:
+ case PREDECREMENT_EXPR:
+ return expand_increment (exp, 0);
+
+ case POSTINCREMENT_EXPR:
+ case POSTDECREMENT_EXPR:
+ /* Faster to treat as pre-increment if result is not used. */
+ return expand_increment (exp, ! ignore);
+
+ case ADDR_EXPR:
+ /* If nonzero, TEMP will be set to the address of something that might
+ be a MEM corresponding to a stack slot. */
+ temp = 0;
+
+ /* Are we taking the address of a nested function? */
+ if (TREE_CODE (TREE_OPERAND (exp, 0)) == FUNCTION_DECL
+ && decl_function_context (TREE_OPERAND (exp, 0)) != 0)
+ {
+ op0 = trampoline_address (TREE_OPERAND (exp, 0));
+ op0 = force_operand (op0, target);
+ }
+ /* If we are taking the address of something erroneous, just
+ return a zero. */
+ else if (TREE_CODE (TREE_OPERAND (exp, 0)) == ERROR_MARK)
+ return const0_rtx;
+ else
+ {
+ /* We make sure to pass const0_rtx down if we came in with
+ ignore set, to avoid doing the cleanups twice for something. */
+ op0 = expand_expr (TREE_OPERAND (exp, 0),
+ ignore ? const0_rtx : NULL_RTX, VOIDmode,
+ (modifier == EXPAND_INITIALIZER
+ ? modifier : EXPAND_CONST_ADDRESS));
+
+ /* If we are going to ignore the result, OP0 will have been set
+ to const0_rtx, so just return it. Don't get confused and
+ think we are taking the address of the constant. */
+ if (ignore)
+ return op0;
+
+ /* We would like the object in memory. If it is a constant,
+ we can have it be statically allocated into memory. For
+ a non-constant (REG, SUBREG or CONCAT), we need to allocate some
+ memory and store the value into it. */
+
+ if (CONSTANT_P (op0))
+ op0 = force_const_mem (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))),
+ op0);
+ else if (GET_CODE (op0) == MEM)
+ temp = XEXP (op0, 0);
+
+ else if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG
+ || GET_CODE (op0) == CONCAT)
+ {
+ /* If this object is in a register, it must be not
+ be BLKmode. */
+ tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0));
+ enum machine_mode inner_mode = TYPE_MODE (inner_type);
+ rtx memloc
+ = assign_stack_temp (inner_mode,
+ int_size_in_bytes (inner_type), 1);
+
+ emit_move_insn (memloc, op0);
+ op0 = memloc;
+ }
+
+ if (GET_CODE (op0) != MEM)
+ abort ();
+
+ if (modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER)
+ return XEXP (op0, 0);
+
+ op0 = force_operand (XEXP (op0, 0), target);
+ }
+
+ if (flag_force_addr && GET_CODE (op0) != REG)
+ op0 = force_reg (Pmode, op0);
+
+ if (GET_CODE (op0) == REG)
+ mark_reg_pointer (op0);
+
+ /* If we might have had a temp slot, add an equivalent address
+ for it. */
+ if (temp != 0)
+ update_temp_slot_address (temp, op0);
+
+ return op0;
+
+ case ENTRY_VALUE_EXPR:
+ abort ();
+
+ /* COMPLEX type for Extended Pascal & Fortran */
+ case COMPLEX_EXPR:
+ {
+ enum machine_mode mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (exp)));
+ rtx insns;
+
+ /* Get the rtx code of the operands. */
+ op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
+ op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0);
+
+ if (! target)
+ target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
+
+ start_sequence ();
+
+ /* Move the real (op0) and imaginary (op1) parts to their location. */
+ emit_move_insn (gen_realpart (mode, target), op0);
+ emit_move_insn (gen_imagpart (mode, target), op1);
+
+ insns = get_insns ();
+ end_sequence ();
+
+ /* Complex construction should appear as a single unit. */
+ /* If TARGET is a CONCAT, we got insns like RD = RS, ID = IS,
+ each with a separate pseudo as destination.
+ It's not correct for flow to treat them as a unit. */
+ if (GET_CODE (target) != CONCAT)
+ emit_no_conflict_block (insns, target, op0, op1, NULL_RTX);
+ else
+ emit_insns (insns);
+
+ return target;
+ }
+
+ case REALPART_EXPR:
+ op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
+ return gen_realpart (mode, op0);
+
+ case IMAGPART_EXPR:
+ op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
+ return gen_imagpart (mode, op0);
+
+ case CONJ_EXPR:
+ {
+ rtx imag_t;
+ rtx insns;
+
+ op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
+
+ if (! target)
+ target = gen_reg_rtx (mode);
+
+ start_sequence ();
+
+ /* Store the realpart and the negated imagpart to target. */
+ emit_move_insn (gen_realpart (mode, target), gen_realpart (mode, op0));
+
+ imag_t = gen_imagpart (mode, target);
+ temp = expand_unop (mode, neg_optab,
+ gen_imagpart (mode, op0), imag_t, 0);
+ if (temp != imag_t)
+ emit_move_insn (imag_t, temp);
+
+ insns = get_insns ();
+ end_sequence ();
+
+ /* Conjugate should appear as a single unit
+ If TARGET is a CONCAT, we got insns like RD = RS, ID = - IS,
+ each with a separate pseudo as destination.
+ It's not correct for flow to treat them as a unit. */
+ if (GET_CODE (target) != CONCAT)
+ emit_no_conflict_block (insns, target, op0, NULL_RTX, NULL_RTX);
+ else
+ emit_insns (insns);
+
+ return target;
+ }
+
+ case ERROR_MARK:
+ op0 = CONST0_RTX (tmode);
+ if (op0 != 0)
+ return op0;
+ return const0_rtx;
+
+ default:
+ return (*lang_expand_expr) (exp, original_target, tmode, modifier);
+ }
+
+ /* Here to do an ordinary binary operator, generating an instruction
+ from the optab already placed in `this_optab'. */
+ binop:
+ preexpand_calls (exp);
+ if (! safe_from_p (subtarget, TREE_OPERAND (exp, 1)))
+ subtarget = 0;
+ op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
+ op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0);
+ binop2:
+ temp = expand_binop (mode, this_optab, op0, op1, target,
+ unsignedp, OPTAB_LIB_WIDEN);
+ if (temp == 0)
+ abort ();
+ return temp;
+}
+
+
+/* Emit bytecode to evaluate the given expression EXP to the stack. */
+void
+bc_expand_expr (exp)
+ tree exp;
+{
+ enum tree_code code;
+ tree type, arg0;
+ rtx r;
+ struct binary_operator *binoptab;
+ struct unary_operator *unoptab;
+ struct increment_operator *incroptab;
+ struct bc_label *lab, *lab1;
+ enum bytecode_opcode opcode;
+
+
+ code = TREE_CODE (exp);
+
+ switch (code)
+ {
+ case PARM_DECL:
+
+ if (DECL_RTL (exp) == 0)
+ {
+ error_with_decl (exp, "prior parameter's size depends on `%s'");
+ return;
+ }
+
+ bc_load_parmaddr (DECL_RTL (exp));
+ bc_load_memory (TREE_TYPE (exp), exp);
+
+ return;
+
+ case VAR_DECL:
+
+ if (DECL_RTL (exp) == 0)
+ abort ();
+
+#if 0
+ if (BYTECODE_LABEL (DECL_RTL (exp)))
+ bc_load_externaddr (DECL_RTL (exp));
+ else
+ bc_load_localaddr (DECL_RTL (exp));
+#endif
+ if (TREE_PUBLIC (exp))
+ bc_load_externaddr_id (DECL_ASSEMBLER_NAME (exp),
+ BYTECODE_BC_LABEL (DECL_RTL (exp))->offset);
+ else
+ bc_load_localaddr (DECL_RTL (exp));
+
+ bc_load_memory (TREE_TYPE (exp), exp);
+ return;
+
+ case INTEGER_CST:
+
+#ifdef DEBUG_PRINT_CODE
+ fprintf (stderr, " [%x]\n", TREE_INT_CST_LOW (exp));
+#endif
+ bc_emit_instruction (mode_to_const_map[(int) (DECL_BIT_FIELD (exp)
+ ? SImode
+ : TYPE_MODE (TREE_TYPE (exp)))],
+ (HOST_WIDE_INT) TREE_INT_CST_LOW (exp));
+ return;
+
+ case REAL_CST:
+
+#if 0
+#ifdef DEBUG_PRINT_CODE
+ fprintf (stderr, " [%g]\n", (double) TREE_INT_CST_LOW (exp));
+#endif
+ /* FIX THIS: find a better way to pass real_cst's. -bson */
+ bc_emit_instruction (mode_to_const_map[TYPE_MODE (TREE_TYPE (exp))],
+ (double) TREE_REAL_CST (exp));
+#else
+ abort ();
+#endif
+
+ return;
+
+ case CALL_EXPR:
+
+ /* We build a call description vector describing the type of
+ the return value and of the arguments; this call vector,
+ together with a pointer to a location for the return value
+ and the base of the argument list, is passed to the low
+ level machine dependent call subroutine, which is responsible
+ for putting the arguments wherever real functions expect
+ them, as well as getting the return value back. */
+ {
+ tree calldesc = 0, arg;
+ int nargs = 0, i;
+ rtx retval;
+
+ /* Push the evaluated args on the evaluation stack in reverse
+ order. Also make an entry for each arg in the calldesc
+ vector while we're at it. */
+
+ TREE_OPERAND (exp, 1) = nreverse (TREE_OPERAND (exp, 1));
+
+ for (arg = TREE_OPERAND (exp, 1); arg; arg = TREE_CHAIN (arg))
+ {
+ ++nargs;
+ bc_expand_expr (TREE_VALUE (arg));
+
+ calldesc = tree_cons ((tree) 0,
+ size_in_bytes (TREE_TYPE (TREE_VALUE (arg))),
+ calldesc);
+ calldesc = tree_cons ((tree) 0,
+ bc_runtime_type_code (TREE_TYPE (TREE_VALUE (arg))),
+ calldesc);
+ }
+
+ TREE_OPERAND (exp, 1) = nreverse (TREE_OPERAND (exp, 1));
+
+ /* Allocate a location for the return value and push its
+ address on the evaluation stack. Also make an entry
+ at the front of the calldesc for the return value type. */
+
+ type = TREE_TYPE (TREE_TYPE (TREE_TYPE (TREE_OPERAND (exp, 0))));
+ retval = bc_allocate_local (int_size_in_bytes (type), TYPE_ALIGN (type));
+ bc_load_localaddr (retval);
+
+ calldesc = tree_cons ((tree) 0, size_in_bytes (type), calldesc);
+ calldesc = tree_cons ((tree) 0, bc_runtime_type_code (type), calldesc);
+
+ /* Prepend the argument count. */
+ calldesc = tree_cons ((tree) 0,
+ build_int_2 (nargs, 0),
+ calldesc);
+
+ /* Push the address of the call description vector on the stack. */
+ calldesc = build_nt (CONSTRUCTOR, (tree) 0, calldesc);
+ TREE_TYPE (calldesc) = build_array_type (integer_type_node,
+ build_index_type (build_int_2 (nargs * 2, 0)));
+ r = output_constant_def (calldesc);
+ bc_load_externaddr (r);
+
+ /* Push the address of the function to be called. */
+ bc_expand_expr (TREE_OPERAND (exp, 0));
+
+ /* Call the function, popping its address and the calldesc vector
+ address off the evaluation stack in the process. */
+ bc_emit_instruction (call);
+
+ /* Pop the arguments off the stack. */
+ bc_adjust_stack (nargs);
+
+ /* Load the return value onto the stack. */
+ bc_load_localaddr (retval);
+ bc_load_memory (type, TREE_OPERAND (exp, 0));
+ }
+ return;
+
+ case SAVE_EXPR:
+
+ if (!SAVE_EXPR_RTL (exp))
+ {
+ /* First time around: copy to local variable */
+ SAVE_EXPR_RTL (exp) = bc_allocate_local (int_size_in_bytes (TREE_TYPE (exp)),
+ TYPE_ALIGN (TREE_TYPE(exp)));
+ bc_expand_expr (TREE_OPERAND (exp, 0));
+ bc_emit_instruction (duplicate);
+
+ bc_load_localaddr (SAVE_EXPR_RTL (exp));
+ bc_store_memory (TREE_TYPE (exp), TREE_OPERAND (exp, 0));
+ }
+ else
+ {
+ /* Consecutive reference: use saved copy */
+ bc_load_localaddr (SAVE_EXPR_RTL (exp));
+ bc_load_memory (TREE_TYPE (exp), TREE_OPERAND (exp, 0));
+ }
+ return;
+
+#if 0
+ /* FIXME: the XXXX_STMT codes have been removed in GCC2, but
+ how are they handled instead? */
+ case LET_STMT:
+
+ TREE_USED (exp) = 1;
+ bc_expand_expr (STMT_BODY (exp));
+ return;
+#endif
+
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+
+ bc_expand_expr (TREE_OPERAND (exp, 0));
+ bc_expand_conversion (TREE_TYPE (TREE_OPERAND (exp, 0)), TREE_TYPE (exp));
+ return;
+
+ case MODIFY_EXPR:
+
+ expand_assignment (TREE_OPERAND (exp, 0), TREE_OPERAND (exp, 1), 0, 0);
+ return;
+
+ case ADDR_EXPR:
+
+ bc_expand_address (TREE_OPERAND (exp, 0));
+ return;
+
+ case INDIRECT_REF:
+
+ bc_expand_expr (TREE_OPERAND (exp, 0));
+ bc_load_memory (TREE_TYPE (exp), TREE_OPERAND (exp, 0));
+ return;
+
+ case ARRAY_REF:
+
+ bc_expand_expr (bc_canonicalize_array_ref (exp));
+ return;
+
+ case COMPONENT_REF:
+
+ bc_expand_component_address (exp);
+
+ /* If we have a bitfield, generate a proper load */
+ bc_load_memory (TREE_TYPE (TREE_OPERAND (exp, 1)), TREE_OPERAND (exp, 1));
+ return;
+
+ case COMPOUND_EXPR:
+
+ bc_expand_expr (TREE_OPERAND (exp, 0));
+ bc_emit_instruction (drop);
+ bc_expand_expr (TREE_OPERAND (exp, 1));
+ return;
+
+ case COND_EXPR:
+
+ bc_expand_expr (TREE_OPERAND (exp, 0));
+ bc_expand_truth_conversion (TREE_TYPE (TREE_OPERAND (exp, 0)));
+ lab = bc_get_bytecode_label ();
+ bc_emit_bytecode (xjumpifnot);
+ bc_emit_bytecode_labelref (lab);
+
+#ifdef DEBUG_PRINT_CODE
+ fputc ('\n', stderr);
+#endif
+ bc_expand_expr (TREE_OPERAND (exp, 1));
+ lab1 = bc_get_bytecode_label ();
+ bc_emit_bytecode (jump);
+ bc_emit_bytecode_labelref (lab1);
+
+#ifdef DEBUG_PRINT_CODE
+ fputc ('\n', stderr);
+#endif
+
+ bc_emit_bytecode_labeldef (lab);
+ bc_expand_expr (TREE_OPERAND (exp, 2));
+ bc_emit_bytecode_labeldef (lab1);
+ return;
+
+ case TRUTH_ANDIF_EXPR:
+
+ opcode = xjumpifnot;
+ goto andorif;
+
+ case TRUTH_ORIF_EXPR:
+
+ opcode = xjumpif;
+ goto andorif;
+
+ case PLUS_EXPR:
+
+ binoptab = optab_plus_expr;
+ goto binop;
+
+ case MINUS_EXPR:
+
+ binoptab = optab_minus_expr;
+ goto binop;
+
+ case MULT_EXPR:
+
+ binoptab = optab_mult_expr;
+ goto binop;
+
+ case TRUNC_DIV_EXPR:
+ case FLOOR_DIV_EXPR:
+ case CEIL_DIV_EXPR:
+ case ROUND_DIV_EXPR:
+ case EXACT_DIV_EXPR:
+
+ binoptab = optab_trunc_div_expr;
+ goto binop;
+
+ case TRUNC_MOD_EXPR:
+ case FLOOR_MOD_EXPR:
+ case CEIL_MOD_EXPR:
+ case ROUND_MOD_EXPR:
+
+ binoptab = optab_trunc_mod_expr;
+ goto binop;
+
+ case FIX_ROUND_EXPR:
+ case FIX_FLOOR_EXPR:
+ case FIX_CEIL_EXPR:
+ abort (); /* Not used for C. */
+
+ case FIX_TRUNC_EXPR:
+ case FLOAT_EXPR:
+ case MAX_EXPR:
+ case MIN_EXPR:
+ case FFS_EXPR:
+ case LROTATE_EXPR:
+ case RROTATE_EXPR:
+ abort (); /* FIXME */
+
+ case RDIV_EXPR:
+
+ binoptab = optab_rdiv_expr;
+ goto binop;
+
+ case BIT_AND_EXPR:
+
+ binoptab = optab_bit_and_expr;
+ goto binop;
+
+ case BIT_IOR_EXPR:
+
+ binoptab = optab_bit_ior_expr;
+ goto binop;
+
+ case BIT_XOR_EXPR:
+
+ binoptab = optab_bit_xor_expr;
+ goto binop;
+
+ case LSHIFT_EXPR:
+
+ binoptab = optab_lshift_expr;
+ goto binop;
+
+ case RSHIFT_EXPR:
+
+ binoptab = optab_rshift_expr;
+ goto binop;
+
+ case TRUTH_AND_EXPR:
+
+ binoptab = optab_truth_and_expr;
+ goto binop;
+
+ case TRUTH_OR_EXPR:
+
+ binoptab = optab_truth_or_expr;
+ goto binop;
+
+ case LT_EXPR:
+
+ binoptab = optab_lt_expr;
+ goto binop;
+
+ case LE_EXPR:
+
+ binoptab = optab_le_expr;
+ goto binop;
+
+ case GE_EXPR:
+
+ binoptab = optab_ge_expr;
+ goto binop;
+
+ case GT_EXPR:
+
+ binoptab = optab_gt_expr;
+ goto binop;
+
+ case EQ_EXPR:
+
+ binoptab = optab_eq_expr;
+ goto binop;
+
+ case NE_EXPR:
+
+ binoptab = optab_ne_expr;
+ goto binop;
+
+ case NEGATE_EXPR:
+
+ unoptab = optab_negate_expr;
+ goto unop;
+
+ case BIT_NOT_EXPR:
+
+ unoptab = optab_bit_not_expr;
+ goto unop;
+
+ case TRUTH_NOT_EXPR:
+
+ unoptab = optab_truth_not_expr;
+ goto unop;
+
+ case PREDECREMENT_EXPR:
+
+ incroptab = optab_predecrement_expr;
+ goto increment;
+
+ case PREINCREMENT_EXPR:
+
+ incroptab = optab_preincrement_expr;
+ goto increment;
+
+ case POSTDECREMENT_EXPR:
+
+ incroptab = optab_postdecrement_expr;
+ goto increment;
+
+ case POSTINCREMENT_EXPR:
+
+ incroptab = optab_postincrement_expr;
+ goto increment;
+
+ case CONSTRUCTOR:
+
+ bc_expand_constructor (exp);
+ return;
+
+ case ERROR_MARK:
+ case RTL_EXPR:
+
+ return;
+
+ case BIND_EXPR:
+ {
+ tree vars = TREE_OPERAND (exp, 0);
+ int vars_need_expansion = 0;
+
+ /* Need to open a binding contour here because
+ if there are any cleanups they most be contained here. */
+ expand_start_bindings (0);
+
+ /* Mark the corresponding BLOCK for output. */
+ if (TREE_OPERAND (exp, 2) != 0)
+ TREE_USED (TREE_OPERAND (exp, 2)) = 1;
+
+ /* If VARS have not yet been expanded, expand them now. */
+ while (vars)
+ {
+ if (DECL_RTL (vars) == 0)
+ {
+ vars_need_expansion = 1;
+ expand_decl (vars);
+ }
+ expand_decl_init (vars);
+ vars = TREE_CHAIN (vars);
+ }
+
+ bc_expand_expr (TREE_OPERAND (exp, 1));
+
+ expand_end_bindings (TREE_OPERAND (exp, 0), 0, 0);
+
+ return;
+ }
+ }
+
+ abort ();
+
+ binop:
+
+ bc_expand_binary_operation (binoptab, TREE_TYPE (exp),
+ TREE_OPERAND (exp, 0), TREE_OPERAND (exp, 1));
+ return;
+
+
+ unop:
+
+ bc_expand_unary_operation (unoptab, TREE_TYPE (exp), TREE_OPERAND (exp, 0));
+ return;
+
+
+ andorif:
+
+ bc_expand_expr (TREE_OPERAND (exp, 0));
+ bc_expand_truth_conversion (TREE_TYPE (TREE_OPERAND (exp, 0)));
+ lab = bc_get_bytecode_label ();
+
+ bc_emit_instruction (duplicate);
+ bc_emit_bytecode (opcode);
+ bc_emit_bytecode_labelref (lab);
+
+#ifdef DEBUG_PRINT_CODE
+ fputc ('\n', stderr);
+#endif
+
+ bc_emit_instruction (drop);
+
+ bc_expand_expr (TREE_OPERAND (exp, 1));
+ bc_expand_truth_conversion (TREE_TYPE (TREE_OPERAND (exp, 1)));
+ bc_emit_bytecode_labeldef (lab);
+ return;
+
+
+ increment:
+
+ type = TREE_TYPE (TREE_OPERAND (exp, 0));
+
+ /* Push the quantum. */
+ bc_expand_expr (TREE_OPERAND (exp, 1));
+
+ /* Convert it to the lvalue's type. */
+ bc_expand_conversion (TREE_TYPE (TREE_OPERAND (exp, 1)), type);
+
+ /* Push the address of the lvalue */
+ bc_expand_expr (build1 (ADDR_EXPR, TYPE_POINTER_TO (type), TREE_OPERAND (exp, 0)));
+
+ /* Perform actual increment */
+ bc_expand_increment (incroptab, type);
+ return;
+}
+
+/* Return the alignment in bits of EXP, a pointer valued expression.
+ But don't return more than MAX_ALIGN no matter what.
+ The alignment returned is, by default, the alignment of the thing that
+ EXP points to (if it is not a POINTER_TYPE, 0 is returned).
+
+ Otherwise, look at the expression to see if we can do better, i.e., if the
+ expression is actually pointing at an object whose alignment is tighter. */
+
+static int
+get_pointer_alignment (exp, max_align)
+ tree exp;
+ unsigned max_align;
+{
+ unsigned align, inner;
+
+ if (TREE_CODE (TREE_TYPE (exp)) != POINTER_TYPE)
+ return 0;
+
+ align = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp)));
+ align = MIN (align, max_align);
+
+ while (1)
+ {
+ switch (TREE_CODE (exp))
+ {
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ case NON_LVALUE_EXPR:
+ exp = TREE_OPERAND (exp, 0);
+ if (TREE_CODE (TREE_TYPE (exp)) != POINTER_TYPE)
+ return align;
+ inner = TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp)));
+ align = MIN (inner, max_align);
+ break;
+
+ case PLUS_EXPR:
+ /* If sum of pointer + int, restrict our maximum alignment to that
+ imposed by the integer. If not, we can't do any better than
+ ALIGN. */
+ if (TREE_CODE (TREE_OPERAND (exp, 1)) != INTEGER_CST)
+ return align;
+
+ while (((TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)) * BITS_PER_UNIT)
+ & (max_align - 1))
+ != 0)
+ max_align >>= 1;
+
+ exp = TREE_OPERAND (exp, 0);
+ break;
+
+ case ADDR_EXPR:
+ /* See what we are pointing at and look at its alignment. */
+ exp = TREE_OPERAND (exp, 0);
+ if (TREE_CODE (exp) == FUNCTION_DECL)
+ align = FUNCTION_BOUNDARY;
+ else if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'd')
+ align = DECL_ALIGN (exp);
+#ifdef CONSTANT_ALIGNMENT
+ else if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'c')
+ align = CONSTANT_ALIGNMENT (exp, align);
+#endif
+ return MIN (align, max_align);
+
+ default:
+ return align;
+ }
+ }
+}
+
+/* Return the tree node and offset if a given argument corresponds to
+ a string constant. */
+
+static tree
+string_constant (arg, ptr_offset)
+ tree arg;
+ tree *ptr_offset;
+{
+ STRIP_NOPS (arg);
+
+ if (TREE_CODE (arg) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (arg, 0)) == STRING_CST)
+ {
+ *ptr_offset = integer_zero_node;
+ return TREE_OPERAND (arg, 0);
+ }
+ else if (TREE_CODE (arg) == PLUS_EXPR)
+ {
+ tree arg0 = TREE_OPERAND (arg, 0);
+ tree arg1 = TREE_OPERAND (arg, 1);
+
+ STRIP_NOPS (arg0);
+ STRIP_NOPS (arg1);
+
+ if (TREE_CODE (arg0) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (arg0, 0)) == STRING_CST)
+ {
+ *ptr_offset = arg1;
+ return TREE_OPERAND (arg0, 0);
+ }
+ else if (TREE_CODE (arg1) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (arg1, 0)) == STRING_CST)
+ {
+ *ptr_offset = arg0;
+ return TREE_OPERAND (arg1, 0);
+ }
+ }
+
+ return 0;
+}
+
+/* Compute the length of a C string. TREE_STRING_LENGTH is not the right
+ way, because it could contain a zero byte in the middle.
+ TREE_STRING_LENGTH is the size of the character array, not the string.
+
+ Unfortunately, string_constant can't access the values of const char
+ arrays with initializers, so neither can we do so here. */
+
+static tree
+c_strlen (src)
+ tree src;
+{
+ tree offset_node;
+ int offset, max;
+ char *ptr;
+
+ src = string_constant (src, &offset_node);
+ if (src == 0)
+ return 0;
+ max = TREE_STRING_LENGTH (src);
+ ptr = TREE_STRING_POINTER (src);
+ if (offset_node && TREE_CODE (offset_node) != INTEGER_CST)
+ {
+ /* If the string has an internal zero byte (e.g., "foo\0bar"), we can't
+ compute the offset to the following null if we don't know where to
+ start searching for it. */
+ int i;
+ for (i = 0; i < max; i++)
+ if (ptr[i] == 0)
+ return 0;
+ /* We don't know the starting offset, but we do know that the string
+ has no internal zero bytes. We can assume that the offset falls
+ within the bounds of the string; otherwise, the programmer deserves
+ what he gets. Subtract the offset from the length of the string,
+ and return that. */
+ /* This would perhaps not be valid if we were dealing with named
+ arrays in addition to literal string constants. */
+ return size_binop (MINUS_EXPR, size_int (max), offset_node);
+ }
+
+ /* We have a known offset into the string. Start searching there for
+ a null character. */
+ if (offset_node == 0)
+ offset = 0;
+ else
+ {
+ /* Did we get a long long offset? If so, punt. */
+ if (TREE_INT_CST_HIGH (offset_node) != 0)
+ return 0;
+ offset = TREE_INT_CST_LOW (offset_node);
+ }
+ /* If the offset is known to be out of bounds, warn, and call strlen at
+ runtime. */
+ if (offset < 0 || offset > max)
+ {
+ warning ("offset outside bounds of constant string");
+ return 0;
+ }
+ /* Use strlen to search for the first zero byte. Since any strings
+ constructed with build_string will have nulls appended, we win even
+ if we get handed something like (char[4])"abcd".
+
+ Since OFFSET is our starting index into the string, no further
+ calculation is needed. */
+ return size_int (strlen (ptr + offset));
+}
+
+/* Expand an expression EXP that calls a built-in function,
+ with result going to TARGET if that's convenient
+ (and in mode MODE if that's convenient).
+ SUBTARGET may be used as the target for computing one of EXP's operands.
+ IGNORE is nonzero if the value is to be ignored. */
+
+#define CALLED_AS_BUILT_IN(NODE) \
+ (!strncmp (IDENTIFIER_POINTER (DECL_NAME (NODE)), "__builtin_", 10))
+
+static rtx
+expand_builtin (exp, target, subtarget, mode, ignore)
+ tree exp;
+ rtx target;
+ rtx subtarget;
+ enum machine_mode mode;
+ int ignore;
+{
+ tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+ tree arglist = TREE_OPERAND (exp, 1);
+ rtx op0;
+ rtx lab1, insns;
+ enum machine_mode value_mode = TYPE_MODE (TREE_TYPE (exp));
+ optab builtin_optab;
+
+ switch (DECL_FUNCTION_CODE (fndecl))
+ {
+ case BUILT_IN_ABS:
+ case BUILT_IN_LABS:
+ case BUILT_IN_FABS:
+ /* build_function_call changes these into ABS_EXPR. */
+ abort ();
+
+ case BUILT_IN_SIN:
+ case BUILT_IN_COS:
+ case BUILT_IN_FSQRT:
+ /* If not optimizing, call the library function. */
+ if (! optimize)
+ break;
+
+ if (arglist == 0
+ /* Arg could be wrong type if user redeclared this fcn wrong. */
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != REAL_TYPE)
+ break;
+
+ /* Stabilize and compute the argument. */
+ if (TREE_CODE (TREE_VALUE (arglist)) != VAR_DECL
+ && TREE_CODE (TREE_VALUE (arglist)) != PARM_DECL)
+ {
+ exp = copy_node (exp);
+ arglist = copy_node (arglist);
+ TREE_OPERAND (exp, 1) = arglist;
+ TREE_VALUE (arglist) = save_expr (TREE_VALUE (arglist));
+ }
+ op0 = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, 0);
+
+ /* Make a suitable register to place result in. */
+ target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
+
+ emit_queue ();
+ start_sequence ();
+
+ switch (DECL_FUNCTION_CODE (fndecl))
+ {
+ case BUILT_IN_SIN:
+ builtin_optab = sin_optab; break;
+ case BUILT_IN_COS:
+ builtin_optab = cos_optab; break;
+ case BUILT_IN_FSQRT:
+ builtin_optab = sqrt_optab; break;
+ default:
+ abort ();
+ }
+
+ /* Compute into TARGET.
+ Set TARGET to wherever the result comes back. */
+ target = expand_unop (TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))),
+ builtin_optab, op0, target, 0);
+
+ /* If we were unable to expand via the builtin, stop the
+ sequence (without outputting the insns) and break, causing
+ a call the the library function. */
+ if (target == 0)
+ {
+ end_sequence ();
+ break;
+ }
+
+ /* Check the results by default. But if flag_fast_math is turned on,
+ then assume sqrt will always be called with valid arguments. */
+
+ if (! flag_fast_math)
+ {
+ /* Don't define the builtin FP instructions
+ if your machine is not IEEE. */
+ if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT)
+ abort ();
+
+ lab1 = gen_label_rtx ();
+
+ /* Test the result; if it is NaN, set errno=EDOM because
+ the argument was not in the domain. */
+ emit_cmp_insn (target, target, EQ, 0, GET_MODE (target), 0, 0);
+ emit_jump_insn (gen_beq (lab1));
+
+#ifdef TARGET_EDOM
+ {
+#ifdef GEN_ERRNO_RTX
+ rtx errno_rtx = GEN_ERRNO_RTX;
+#else
+ rtx errno_rtx
+ = gen_rtx (MEM, word_mode, gen_rtx (SYMBOL_REF, Pmode, "errno"));
+#endif
+
+ emit_move_insn (errno_rtx, GEN_INT (TARGET_EDOM));
+ }
+#else
+ /* We can't set errno=EDOM directly; let the library call do it.
+ Pop the arguments right away in case the call gets deleted. */
+ NO_DEFER_POP;
+ expand_call (exp, target, 0);
+ OK_DEFER_POP;
+#endif
+
+ emit_label (lab1);
+ }
+
+ /* Output the entire sequence. */
+ insns = get_insns ();
+ end_sequence ();
+ emit_insns (insns);
+
+ return target;
+
+ /* __builtin_apply_args returns block of memory allocated on
+ the stack into which is stored the arg pointer, structure
+ value address, static chain, and all the registers that might
+ possibly be used in performing a function call. The code is
+ moved to the start of the function so the incoming values are
+ saved. */
+ case BUILT_IN_APPLY_ARGS:
+ /* Don't do __builtin_apply_args more than once in a function.
+ Save the result of the first call and reuse it. */
+ if (apply_args_value != 0)
+ return apply_args_value;
+ {
+ /* When this function is called, it means that registers must be
+ saved on entry to this function. So we migrate the
+ call to the first insn of this function. */
+ rtx temp;
+ rtx seq;
+
+ start_sequence ();
+ temp = expand_builtin_apply_args ();
+ seq = get_insns ();
+ end_sequence ();
+
+ apply_args_value = temp;
+
+ /* Put the sequence after the NOTE that starts the function.
+ If this is inside a SEQUENCE, make the outer-level insn
+ chain current, so the code is placed at the start of the
+ function. */
+ push_topmost_sequence ();
+ emit_insns_before (seq, NEXT_INSN (get_insns ()));
+ pop_topmost_sequence ();
+ return temp;
+ }
+
+ /* __builtin_apply (FUNCTION, ARGUMENTS, ARGSIZE) invokes
+ FUNCTION with a copy of the parameters described by
+ ARGUMENTS, and ARGSIZE. It returns a block of memory
+ allocated on the stack into which is stored all the registers
+ that might possibly be used for returning the result of a
+ function. ARGUMENTS is the value returned by
+ __builtin_apply_args. ARGSIZE is the number of bytes of
+ arguments that must be copied. ??? How should this value be
+ computed? We'll also need a safe worst case value for varargs
+ functions. */
+ case BUILT_IN_APPLY:
+ if (arglist == 0
+ /* Arg could be non-pointer if user redeclared this fcn wrong. */
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+ || TREE_CHAIN (arglist) == 0
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE
+ || TREE_CHAIN (TREE_CHAIN (arglist)) == 0
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))))) != INTEGER_TYPE)
+ return const0_rtx;
+ else
+ {
+ int i;
+ tree t;
+ rtx ops[3];
+
+ for (t = arglist, i = 0; t; t = TREE_CHAIN (t), i++)
+ ops[i] = expand_expr (TREE_VALUE (t), NULL_RTX, VOIDmode, 0);
+
+ return expand_builtin_apply (ops[0], ops[1], ops[2]);
+ }
+
+ /* __builtin_return (RESULT) causes the function to return the
+ value described by RESULT. RESULT is address of the block of
+ memory returned by __builtin_apply. */
+ case BUILT_IN_RETURN:
+ if (arglist
+ /* Arg could be non-pointer if user redeclared this fcn wrong. */
+ && TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) == POINTER_TYPE)
+ expand_builtin_return (expand_expr (TREE_VALUE (arglist),
+ NULL_RTX, VOIDmode, 0));
+ return const0_rtx;
+
+ case BUILT_IN_SAVEREGS:
+ /* Don't do __builtin_saveregs more than once in a function.
+ Save the result of the first call and reuse it. */
+ if (saveregs_value != 0)
+ return saveregs_value;
+ {
+ /* When this function is called, it means that registers must be
+ saved on entry to this function. So we migrate the
+ call to the first insn of this function. */
+ rtx temp;
+ rtx seq;
+
+ /* Now really call the function. `expand_call' does not call
+ expand_builtin, so there is no danger of infinite recursion here. */
+ start_sequence ();
+
+#ifdef EXPAND_BUILTIN_SAVEREGS
+ /* Do whatever the machine needs done in this case. */
+ temp = EXPAND_BUILTIN_SAVEREGS (arglist);
+#else
+ /* The register where the function returns its value
+ is likely to have something else in it, such as an argument.
+ So preserve that register around the call. */
+
+ if (value_mode != VOIDmode)
+ {
+ rtx valreg = hard_libcall_value (value_mode);
+ rtx saved_valreg = gen_reg_rtx (value_mode);
+
+ emit_move_insn (saved_valreg, valreg);
+ temp = expand_call (exp, target, ignore);
+ emit_move_insn (valreg, saved_valreg);
+ }
+ else
+ /* Generate the call, putting the value in a pseudo. */
+ temp = expand_call (exp, target, ignore);
+#endif
+
+ seq = get_insns ();
+ end_sequence ();
+
+ saveregs_value = temp;
+
+ /* Put the sequence after the NOTE that starts the function.
+ If this is inside a SEQUENCE, make the outer-level insn
+ chain current, so the code is placed at the start of the
+ function. */
+ push_topmost_sequence ();
+ emit_insns_before (seq, NEXT_INSN (get_insns ()));
+ pop_topmost_sequence ();
+ return temp;
+ }
+
+ /* __builtin_args_info (N) returns word N of the arg space info
+ for the current function. The number and meanings of words
+ is controlled by the definition of CUMULATIVE_ARGS. */
+ case BUILT_IN_ARGS_INFO:
+ {
+ int nwords = sizeof (CUMULATIVE_ARGS) / sizeof (int);
+ int i;
+ int *word_ptr = (int *) &current_function_args_info;
+ tree type, elts, result;
+
+ if (sizeof (CUMULATIVE_ARGS) % sizeof (int) != 0)
+ fatal ("CUMULATIVE_ARGS type defined badly; see %s, line %d",
+ __FILE__, __LINE__);
+
+ if (arglist != 0)
+ {
+ tree arg = TREE_VALUE (arglist);
+ if (TREE_CODE (arg) != INTEGER_CST)
+ error ("argument of `__builtin_args_info' must be constant");
+ else
+ {
+ int wordnum = TREE_INT_CST_LOW (arg);
+
+ if (wordnum < 0 || wordnum >= nwords || TREE_INT_CST_HIGH (arg))
+ error ("argument of `__builtin_args_info' out of range");
+ else
+ return GEN_INT (word_ptr[wordnum]);
+ }
+ }
+ else
+ error ("missing argument in `__builtin_args_info'");
+
+ return const0_rtx;
+
+#if 0
+ for (i = 0; i < nwords; i++)
+ elts = tree_cons (NULL_TREE, build_int_2 (word_ptr[i], 0));
+
+ type = build_array_type (integer_type_node,
+ build_index_type (build_int_2 (nwords, 0)));
+ result = build (CONSTRUCTOR, type, NULL_TREE, nreverse (elts));
+ TREE_CONSTANT (result) = 1;
+ TREE_STATIC (result) = 1;
+ result = build (INDIRECT_REF, build_pointer_type (type), result);
+ TREE_CONSTANT (result) = 1;
+ return expand_expr (result, NULL_RTX, VOIDmode, 0);
+#endif
+ }
+
+ /* Return the address of the first anonymous stack arg. */
+ case BUILT_IN_NEXT_ARG:
+ {
+ tree fntype = TREE_TYPE (current_function_decl);
+
+ if ((TYPE_ARG_TYPES (fntype) == 0
+ || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
+ == void_type_node))
+ && ! current_function_varargs)
+ {
+ error ("`va_start' used in function with fixed args");
+ return const0_rtx;
+ }
+
+ if (arglist)
+ {
+ tree last_parm = tree_last (DECL_ARGUMENTS (current_function_decl));
+ tree arg = TREE_VALUE (arglist);
+
+ /* Strip off all nops for the sake of the comparison. This
+ is not quite the same as STRIP_NOPS. It does more. */
+ while (TREE_CODE (arg) == NOP_EXPR
+ || TREE_CODE (arg) == CONVERT_EXPR
+ || TREE_CODE (arg) == NON_LVALUE_EXPR)
+ arg = TREE_OPERAND (arg, 0);
+ if (arg != last_parm)
+ warning ("second parameter of `va_start' not last named argument");
+ }
+ else
+ /* Evidently an out of date version of <stdarg.h>; can't validate
+ va_start's second argument, but can still work as intended. */
+ warning ("`__builtin_next_arg' called without an argument");
+ }
+
+ return expand_binop (Pmode, add_optab,
+ current_function_internal_arg_pointer,
+ current_function_arg_offset_rtx,
+ NULL_RTX, 0, OPTAB_LIB_WIDEN);
+
+ case BUILT_IN_CLASSIFY_TYPE:
+ if (arglist != 0)
+ {
+ tree type = TREE_TYPE (TREE_VALUE (arglist));
+ enum tree_code code = TREE_CODE (type);
+ if (code == VOID_TYPE)
+ return GEN_INT (void_type_class);
+ if (code == INTEGER_TYPE)
+ return GEN_INT (integer_type_class);
+ if (code == CHAR_TYPE)
+ return GEN_INT (char_type_class);
+ if (code == ENUMERAL_TYPE)
+ return GEN_INT (enumeral_type_class);
+ if (code == BOOLEAN_TYPE)
+ return GEN_INT (boolean_type_class);
+ if (code == POINTER_TYPE)
+ return GEN_INT (pointer_type_class);
+ if (code == REFERENCE_TYPE)
+ return GEN_INT (reference_type_class);
+ if (code == OFFSET_TYPE)
+ return GEN_INT (offset_type_class);
+ if (code == REAL_TYPE)
+ return GEN_INT (real_type_class);
+ if (code == COMPLEX_TYPE)
+ return GEN_INT (complex_type_class);
+ if (code == FUNCTION_TYPE)
+ return GEN_INT (function_type_class);
+ if (code == METHOD_TYPE)
+ return GEN_INT (method_type_class);
+ if (code == RECORD_TYPE)
+ return GEN_INT (record_type_class);
+ if (code == UNION_TYPE || code == QUAL_UNION_TYPE)
+ return GEN_INT (union_type_class);
+ if (code == ARRAY_TYPE)
+ {
+ if (TYPE_STRING_FLAG (type))
+ return GEN_INT (string_type_class);
+ else
+ return GEN_INT (array_type_class);
+ }
+ if (code == SET_TYPE)
+ return GEN_INT (set_type_class);
+ if (code == FILE_TYPE)
+ return GEN_INT (file_type_class);
+ if (code == LANG_TYPE)
+ return GEN_INT (lang_type_class);
+ }
+ return GEN_INT (no_type_class);
+
+ case BUILT_IN_CONSTANT_P:
+ if (arglist == 0)
+ return const0_rtx;
+ else
+ return (TREE_CODE_CLASS (TREE_CODE (TREE_VALUE (arglist))) == 'c'
+ ? const1_rtx : const0_rtx);
+
+ case BUILT_IN_FRAME_ADDRESS:
+ /* The argument must be a nonnegative integer constant.
+ It counts the number of frames to scan up the stack.
+ The value is the address of that frame. */
+ case BUILT_IN_RETURN_ADDRESS:
+ /* The argument must be a nonnegative integer constant.
+ It counts the number of frames to scan up the stack.
+ The value is the return address saved in that frame. */
+ if (arglist == 0)
+ /* Warning about missing arg was already issued. */
+ return const0_rtx;
+ else if (TREE_CODE (TREE_VALUE (arglist)) != INTEGER_CST)
+ {
+ error ("invalid arg to `__builtin_return_address'");
+ return const0_rtx;
+ }
+ else if (tree_int_cst_sgn (TREE_VALUE (arglist)) < 0)
+ {
+ error ("invalid arg to `__builtin_return_address'");
+ return const0_rtx;
+ }
+ else
+ {
+ int count = TREE_INT_CST_LOW (TREE_VALUE (arglist));
+ rtx tem = frame_pointer_rtx;
+ int i;
+
+ /* Some machines need special handling before we can access arbitrary
+ frames. For example, on the sparc, we must first flush all
+ register windows to the stack. */
+#ifdef SETUP_FRAME_ADDRESSES
+ SETUP_FRAME_ADDRESSES ();
+#endif
+
+ /* On the sparc, the return address is not in the frame, it is
+ in a register. There is no way to access it off of the current
+ frame pointer, but it can be accessed off the previous frame
+ pointer by reading the value from the register window save
+ area. */
+#ifdef RETURN_ADDR_IN_PREVIOUS_FRAME
+ if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_RETURN_ADDRESS)
+ count--;
+#endif
+
+ /* Scan back COUNT frames to the specified frame. */
+ for (i = 0; i < count; i++)
+ {
+ /* Assume the dynamic chain pointer is in the word that
+ the frame address points to, unless otherwise specified. */
+#ifdef DYNAMIC_CHAIN_ADDRESS
+ tem = DYNAMIC_CHAIN_ADDRESS (tem);
+#endif
+ tem = memory_address (Pmode, tem);
+ tem = copy_to_reg (gen_rtx (MEM, Pmode, tem));
+ }
+
+ /* For __builtin_frame_address, return what we've got. */
+ if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS)
+ return tem;
+
+ /* For __builtin_return_address,
+ Get the return address from that frame. */
+#ifdef RETURN_ADDR_RTX
+ return RETURN_ADDR_RTX (count, tem);
+#else
+ tem = memory_address (Pmode,
+ plus_constant (tem, GET_MODE_SIZE (Pmode)));
+ return copy_to_reg (gen_rtx (MEM, Pmode, tem));
+#endif
+ }
+
+ case BUILT_IN_ALLOCA:
+ if (arglist == 0
+ /* Arg could be non-integer if user redeclared this fcn wrong. */
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE)
+ break;
+
+ /* Compute the argument. */
+ op0 = expand_expr (TREE_VALUE (arglist), NULL_RTX, VOIDmode, 0);
+
+ /* Allocate the desired space. */
+ return allocate_dynamic_stack_space (op0, target, BITS_PER_UNIT);
+
+ case BUILT_IN_FFS:
+ /* If not optimizing, call the library function. */
+ if (!optimize && ! CALLED_AS_BUILT_IN (fndecl))
+ break;
+
+ if (arglist == 0
+ /* Arg could be non-integer if user redeclared this fcn wrong. */
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != INTEGER_TYPE)
+ break;
+
+ /* Compute the argument. */
+ op0 = expand_expr (TREE_VALUE (arglist), subtarget, VOIDmode, 0);
+ /* Compute ffs, into TARGET if possible.
+ Set TARGET to wherever the result comes back. */
+ target = expand_unop (TYPE_MODE (TREE_TYPE (TREE_VALUE (arglist))),
+ ffs_optab, op0, target, 1);
+ if (target == 0)
+ abort ();
+ return target;
+
+ case BUILT_IN_STRLEN:
+ /* If not optimizing, call the library function. */
+ if (!optimize && ! CALLED_AS_BUILT_IN (fndecl))
+ break;
+
+ if (arglist == 0
+ /* Arg could be non-pointer if user redeclared this fcn wrong. */
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE)
+ break;
+ else
+ {
+ tree src = TREE_VALUE (arglist);
+ tree len = c_strlen (src);
+
+ int align
+ = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
+
+ rtx result, src_rtx, char_rtx;
+ enum machine_mode insn_mode = value_mode, char_mode;
+ enum insn_code icode;
+
+ /* If the length is known, just return it. */
+ if (len != 0)
+ return expand_expr (len, target, mode, 0);
+
+ /* If SRC is not a pointer type, don't do this operation inline. */
+ if (align == 0)
+ break;
+
+ /* Call a function if we can't compute strlen in the right mode. */
+
+ while (insn_mode != VOIDmode)
+ {
+ icode = strlen_optab->handlers[(int) insn_mode].insn_code;
+ if (icode != CODE_FOR_nothing)
+ break;
+
+ insn_mode = GET_MODE_WIDER_MODE (insn_mode);
+ }
+ if (insn_mode == VOIDmode)
+ break;
+
+ /* Make a place to write the result of the instruction. */
+ result = target;
+ if (! (result != 0
+ && GET_CODE (result) == REG
+ && GET_MODE (result) == insn_mode
+ && REGNO (result) >= FIRST_PSEUDO_REGISTER))
+ result = gen_reg_rtx (insn_mode);
+
+ /* Make sure the operands are acceptable to the predicates. */
+
+ if (! (*insn_operand_predicate[(int)icode][0]) (result, insn_mode))
+ result = gen_reg_rtx (insn_mode);
+
+ src_rtx = memory_address (BLKmode,
+ expand_expr (src, NULL_RTX, Pmode,
+ EXPAND_NORMAL));
+ if (! (*insn_operand_predicate[(int)icode][1]) (src_rtx, Pmode))
+ src_rtx = copy_to_mode_reg (Pmode, src_rtx);
+
+ char_rtx = const0_rtx;
+ char_mode = insn_operand_mode[(int)icode][2];
+ if (! (*insn_operand_predicate[(int)icode][2]) (char_rtx, char_mode))
+ char_rtx = copy_to_mode_reg (char_mode, char_rtx);
+
+ emit_insn (GEN_FCN (icode) (result,
+ gen_rtx (MEM, BLKmode, src_rtx),
+ char_rtx, GEN_INT (align)));
+
+ /* Return the value in the proper mode for this function. */
+ if (GET_MODE (result) == value_mode)
+ return result;
+ else if (target != 0)
+ {
+ convert_move (target, result, 0);
+ return target;
+ }
+ else
+ return convert_to_mode (value_mode, result, 0);
+ }
+
+ case BUILT_IN_STRCPY:
+ /* If not optimizing, call the library function. */
+ if (!optimize && ! CALLED_AS_BUILT_IN (fndecl))
+ break;
+
+ if (arglist == 0
+ /* Arg could be non-pointer if user redeclared this fcn wrong. */
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+ || TREE_CHAIN (arglist) == 0
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE)
+ break;
+ else
+ {
+ tree len = c_strlen (TREE_VALUE (TREE_CHAIN (arglist)));
+
+ if (len == 0)
+ break;
+
+ len = size_binop (PLUS_EXPR, len, integer_one_node);
+
+ chainon (arglist, build_tree_list (NULL_TREE, len));
+ }
+
+ /* Drops in. */
+ case BUILT_IN_MEMCPY:
+ /* If not optimizing, call the library function. */
+ if (!optimize && ! CALLED_AS_BUILT_IN (fndecl))
+ break;
+
+ if (arglist == 0
+ /* Arg could be non-pointer if user redeclared this fcn wrong. */
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+ || TREE_CHAIN (arglist) == 0
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE
+ || TREE_CHAIN (TREE_CHAIN (arglist)) == 0
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))))) != INTEGER_TYPE)
+ break;
+ else
+ {
+ tree dest = TREE_VALUE (arglist);
+ tree src = TREE_VALUE (TREE_CHAIN (arglist));
+ tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+
+ int src_align
+ = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
+ int dest_align
+ = get_pointer_alignment (dest, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
+ rtx dest_rtx, dest_mem, src_mem;
+
+ /* If either SRC or DEST is not a pointer type, don't do
+ this operation in-line. */
+ if (src_align == 0 || dest_align == 0)
+ {
+ if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_STRCPY)
+ TREE_CHAIN (TREE_CHAIN (arglist)) = 0;
+ break;
+ }
+
+ dest_rtx = expand_expr (dest, NULL_RTX, Pmode, EXPAND_NORMAL);
+ dest_mem = gen_rtx (MEM, BLKmode,
+ memory_address (BLKmode, dest_rtx));
+ src_mem = gen_rtx (MEM, BLKmode,
+ memory_address (BLKmode,
+ expand_expr (src, NULL_RTX,
+ Pmode,
+ EXPAND_NORMAL)));
+
+ /* Copy word part most expediently. */
+ emit_block_move (dest_mem, src_mem,
+ expand_expr (len, NULL_RTX, VOIDmode, 0),
+ MIN (src_align, dest_align));
+ return dest_rtx;
+ }
+
+/* These comparison functions need an instruction that returns an actual
+ index. An ordinary compare that just sets the condition codes
+ is not enough. */
+#ifdef HAVE_cmpstrsi
+ case BUILT_IN_STRCMP:
+ /* If not optimizing, call the library function. */
+ if (!optimize && ! CALLED_AS_BUILT_IN (fndecl))
+ break;
+
+ if (arglist == 0
+ /* Arg could be non-pointer if user redeclared this fcn wrong. */
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+ || TREE_CHAIN (arglist) == 0
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE)
+ break;
+ else if (!HAVE_cmpstrsi)
+ break;
+ {
+ tree arg1 = TREE_VALUE (arglist);
+ tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+ tree offset;
+ tree len, len2;
+
+ len = c_strlen (arg1);
+ if (len)
+ len = size_binop (PLUS_EXPR, integer_one_node, len);
+ len2 = c_strlen (arg2);
+ if (len2)
+ len2 = size_binop (PLUS_EXPR, integer_one_node, len2);
+
+ /* If we don't have a constant length for the first, use the length
+ of the second, if we know it. We don't require a constant for
+ this case; some cost analysis could be done if both are available
+ but neither is constant. For now, assume they're equally cheap.
+
+ If both strings have constant lengths, use the smaller. This
+ could arise if optimization results in strcpy being called with
+ two fixed strings, or if the code was machine-generated. We should
+ add some code to the `memcmp' handler below to deal with such
+ situations, someday. */
+ if (!len || TREE_CODE (len) != INTEGER_CST)
+ {
+ if (len2)
+ len = len2;
+ else if (len == 0)
+ break;
+ }
+ else if (len2 && TREE_CODE (len2) == INTEGER_CST)
+ {
+ if (tree_int_cst_lt (len2, len))
+ len = len2;
+ }
+
+ chainon (arglist, build_tree_list (NULL_TREE, len));
+ }
+
+ /* Drops in. */
+ case BUILT_IN_MEMCMP:
+ /* If not optimizing, call the library function. */
+ if (!optimize && ! CALLED_AS_BUILT_IN (fndecl))
+ break;
+
+ if (arglist == 0
+ /* Arg could be non-pointer if user redeclared this fcn wrong. */
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE
+ || TREE_CHAIN (arglist) == 0
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (arglist)))) != POINTER_TYPE
+ || TREE_CHAIN (TREE_CHAIN (arglist)) == 0
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))))) != INTEGER_TYPE)
+ break;
+ else if (!HAVE_cmpstrsi)
+ break;
+ {
+ tree arg1 = TREE_VALUE (arglist);
+ tree arg2 = TREE_VALUE (TREE_CHAIN (arglist));
+ tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+ rtx result;
+
+ int arg1_align
+ = get_pointer_alignment (arg1, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
+ int arg2_align
+ = get_pointer_alignment (arg2, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
+ enum machine_mode insn_mode
+ = insn_operand_mode[(int) CODE_FOR_cmpstrsi][0];
+
+ /* If we don't have POINTER_TYPE, call the function. */
+ if (arg1_align == 0 || arg2_align == 0)
+ {
+ if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_STRCMP)
+ TREE_CHAIN (TREE_CHAIN (arglist)) = 0;
+ break;
+ }
+
+ /* Make a place to write the result of the instruction. */
+ result = target;
+ if (! (result != 0
+ && GET_CODE (result) == REG && GET_MODE (result) == insn_mode
+ && REGNO (result) >= FIRST_PSEUDO_REGISTER))
+ result = gen_reg_rtx (insn_mode);
+
+ emit_insn (gen_cmpstrsi (result,
+ gen_rtx (MEM, BLKmode,
+ expand_expr (arg1, NULL_RTX, Pmode,
+ EXPAND_NORMAL)),
+ gen_rtx (MEM, BLKmode,
+ expand_expr (arg2, NULL_RTX, Pmode,
+ EXPAND_NORMAL)),
+ expand_expr (len, NULL_RTX, VOIDmode, 0),
+ GEN_INT (MIN (arg1_align, arg2_align))));
+
+ /* Return the value in the proper mode for this function. */
+ mode = TYPE_MODE (TREE_TYPE (exp));
+ if (GET_MODE (result) == mode)
+ return result;
+ else if (target != 0)
+ {
+ convert_move (target, result, 0);
+ return target;
+ }
+ else
+ return convert_to_mode (mode, result, 0);
+ }
+#else
+ case BUILT_IN_STRCMP:
+ case BUILT_IN_MEMCMP:
+ break;
+#endif
+
+ default: /* just do library call, if unknown builtin */
+ error ("built-in function `%s' not currently supported",
+ IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+ }
+
+ /* The switch statement above can drop through to cause the function
+ to be called normally. */
+
+ return expand_call (exp, target, ignore);
+}
+
+/* Built-in functions to perform an untyped call and return. */
+
+/* For each register that may be used for calling a function, this
+ gives a mode used to copy the register's value. VOIDmode indicates
+ the register is not used for calling a function. If the machine
+ has register windows, this gives only the outbound registers.
+ INCOMING_REGNO gives the corresponding inbound register. */
+static enum machine_mode apply_args_mode[FIRST_PSEUDO_REGISTER];
+
+/* For each register that may be used for returning values, this gives
+ a mode used to copy the register's value. VOIDmode indicates the
+ register is not used for returning values. If the machine has
+ register windows, this gives only the outbound registers.
+ INCOMING_REGNO gives the corresponding inbound register. */
+static enum machine_mode apply_result_mode[FIRST_PSEUDO_REGISTER];
+
+/* For each register that may be used for calling a function, this
+ gives the offset of that register into the block returned by
+ __bultin_apply_args. 0 indicates that the register is not
+ used for calling a function. */
+static int apply_args_reg_offset[FIRST_PSEUDO_REGISTER];
+
+/* Return the offset of register REGNO into the block returned by
+ __builtin_apply_args. This is not declared static, since it is
+ needed in objc-act.c. */
+
+int
+apply_args_register_offset (regno)
+ int regno;
+{
+ apply_args_size ();
+
+ /* Arguments are always put in outgoing registers (in the argument
+ block) if such make sense. */
+#ifdef OUTGOING_REGNO
+ regno = OUTGOING_REGNO(regno);
+#endif
+ return apply_args_reg_offset[regno];
+}
+
+/* Return the size required for the block returned by __builtin_apply_args,
+ and initialize apply_args_mode. */
+
+static int
+apply_args_size ()
+{
+ static int size = -1;
+ int align, regno;
+ enum machine_mode mode;
+
+ /* The values computed by this function never change. */
+ if (size < 0)
+ {
+ /* The first value is the incoming arg-pointer. */
+ size = GET_MODE_SIZE (Pmode);
+
+ /* The second value is the structure value address unless this is
+ passed as an "invisible" first argument. */
+ if (struct_value_rtx)
+ size += GET_MODE_SIZE (Pmode);
+
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (FUNCTION_ARG_REGNO_P (regno))
+ {
+ /* Search for the proper mode for copying this register's
+ value. I'm not sure this is right, but it works so far. */
+ enum machine_mode best_mode = VOIDmode;
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+ mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ if (HARD_REGNO_MODE_OK (regno, mode)
+ && HARD_REGNO_NREGS (regno, mode) == 1)
+ best_mode = mode;
+
+ if (best_mode == VOIDmode)
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT);
+ mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ if (HARD_REGNO_MODE_OK (regno, mode)
+ && (mov_optab->handlers[(int) mode].insn_code
+ != CODE_FOR_nothing))
+ best_mode = mode;
+
+ mode = best_mode;
+ if (mode == VOIDmode)
+ abort ();
+
+ align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
+ if (size % align != 0)
+ size = CEIL (size, align) * align;
+ apply_args_reg_offset[regno] = size;
+ size += GET_MODE_SIZE (mode);
+ apply_args_mode[regno] = mode;
+ }
+ else
+ {
+ apply_args_mode[regno] = VOIDmode;
+ apply_args_reg_offset[regno] = 0;
+ }
+ }
+ return size;
+}
+
+/* Return the size required for the block returned by __builtin_apply,
+ and initialize apply_result_mode. */
+
+static int
+apply_result_size ()
+{
+ static int size = -1;
+ int align, regno;
+ enum machine_mode mode;
+
+ /* The values computed by this function never change. */
+ if (size < 0)
+ {
+ size = 0;
+
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (FUNCTION_VALUE_REGNO_P (regno))
+ {
+ /* Search for the proper mode for copying this register's
+ value. I'm not sure this is right, but it works so far. */
+ enum machine_mode best_mode = VOIDmode;
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+ mode != TImode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ if (HARD_REGNO_MODE_OK (regno, mode))
+ best_mode = mode;
+
+ if (best_mode == VOIDmode)
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT);
+ mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ if (HARD_REGNO_MODE_OK (regno, mode)
+ && (mov_optab->handlers[(int) mode].insn_code
+ != CODE_FOR_nothing))
+ best_mode = mode;
+
+ mode = best_mode;
+ if (mode == VOIDmode)
+ abort ();
+
+ align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
+ if (size % align != 0)
+ size = CEIL (size, align) * align;
+ size += GET_MODE_SIZE (mode);
+ apply_result_mode[regno] = mode;
+ }
+ else
+ apply_result_mode[regno] = VOIDmode;
+
+ /* Allow targets that use untyped_call and untyped_return to override
+ the size so that machine-specific information can be stored here. */
+#ifdef APPLY_RESULT_SIZE
+ size = APPLY_RESULT_SIZE;
+#endif
+ }
+ return size;
+}
+
+#if defined (HAVE_untyped_call) || defined (HAVE_untyped_return)
+/* Create a vector describing the result block RESULT. If SAVEP is true,
+ the result block is used to save the values; otherwise it is used to
+ restore the values. */
+
+static rtx
+result_vector (savep, result)
+ int savep;
+ rtx result;
+{
+ int regno, size, align, nelts;
+ enum machine_mode mode;
+ rtx reg, mem;
+ rtx *savevec = (rtx *) alloca (FIRST_PSEUDO_REGISTER * sizeof (rtx));
+
+ size = nelts = 0;
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if ((mode = apply_result_mode[regno]) != VOIDmode)
+ {
+ align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
+ if (size % align != 0)
+ size = CEIL (size, align) * align;
+ reg = gen_rtx (REG, mode, savep ? INCOMING_REGNO (regno) : regno);
+ mem = change_address (result, mode,
+ plus_constant (XEXP (result, 0), size));
+ savevec[nelts++] = (savep
+ ? gen_rtx (SET, VOIDmode, mem, reg)
+ : gen_rtx (SET, VOIDmode, reg, mem));
+ size += GET_MODE_SIZE (mode);
+ }
+ return gen_rtx (PARALLEL, VOIDmode, gen_rtvec_v (nelts, savevec));
+}
+#endif /* HAVE_untyped_call or HAVE_untyped_return */
+
+/* Save the state required to perform an untyped call with the same
+ arguments as were passed to the current function. */
+
+static rtx
+expand_builtin_apply_args ()
+{
+ rtx registers;
+ int size, align, regno;
+ enum machine_mode mode;
+
+ /* Create a block where the arg-pointer, structure value address,
+ and argument registers can be saved. */
+ registers = assign_stack_local (BLKmode, apply_args_size (), -1);
+
+ /* Walk past the arg-pointer and structure value address. */
+ size = GET_MODE_SIZE (Pmode);
+ if (struct_value_rtx)
+ size += GET_MODE_SIZE (Pmode);
+
+ /* Save each register used in calling a function to the block. */
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if ((mode = apply_args_mode[regno]) != VOIDmode)
+ {
+ align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
+ if (size % align != 0)
+ size = CEIL (size, align) * align;
+ emit_move_insn (change_address (registers, mode,
+ plus_constant (XEXP (registers, 0),
+ size)),
+ gen_rtx (REG, mode, INCOMING_REGNO (regno)));
+ size += GET_MODE_SIZE (mode);
+ }
+
+ /* Save the arg pointer to the block. */
+ emit_move_insn (change_address (registers, Pmode, XEXP (registers, 0)),
+ copy_to_reg (virtual_incoming_args_rtx));
+ size = GET_MODE_SIZE (Pmode);
+
+ /* Save the structure value address unless this is passed as an
+ "invisible" first argument. */
+ if (struct_value_incoming_rtx)
+ {
+ emit_move_insn (change_address (registers, Pmode,
+ plus_constant (XEXP (registers, 0),
+ size)),
+ copy_to_reg (struct_value_incoming_rtx));
+ size += GET_MODE_SIZE (Pmode);
+ }
+
+ /* Return the address of the block. */
+ return copy_addr_to_reg (XEXP (registers, 0));
+}
+
+/* Perform an untyped call and save the state required to perform an
+ untyped return of whatever value was returned by the given function. */
+
+static rtx
+expand_builtin_apply (function, arguments, argsize)
+ rtx function, arguments, argsize;
+{
+ int size, align, regno;
+ enum machine_mode mode;
+ rtx incoming_args, result, reg, dest, call_insn;
+ rtx old_stack_level = 0;
+ rtx call_fusage = 0;
+
+ /* Create a block where the return registers can be saved. */
+ result = assign_stack_local (BLKmode, apply_result_size (), -1);
+
+ /* ??? The argsize value should be adjusted here. */
+
+ /* Fetch the arg pointer from the ARGUMENTS block. */
+ incoming_args = gen_reg_rtx (Pmode);
+ emit_move_insn (incoming_args,
+ gen_rtx (MEM, Pmode, arguments));
+#ifndef STACK_GROWS_DOWNWARD
+ incoming_args = expand_binop (Pmode, sub_optab, incoming_args, argsize,
+ incoming_args, 0, OPTAB_LIB_WIDEN);
+#endif
+
+ /* Perform postincrements before actually calling the function. */
+ emit_queue ();
+
+ /* Push a new argument block and copy the arguments. */
+ do_pending_stack_adjust ();
+ emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
+
+ /* Push a block of memory onto the stack to store the memory arguments.
+ Save the address in a register, and copy the memory arguments. ??? I
+ haven't figured out how the calling convention macros effect this,
+ but it's likely that the source and/or destination addresses in
+ the block copy will need updating in machine specific ways. */
+ dest = copy_addr_to_reg (push_block (argsize, 0, 0));
+ emit_block_move (gen_rtx (MEM, BLKmode, dest),
+ gen_rtx (MEM, BLKmode, incoming_args),
+ argsize,
+ PARM_BOUNDARY / BITS_PER_UNIT);
+
+ /* Refer to the argument block. */
+ apply_args_size ();
+ arguments = gen_rtx (MEM, BLKmode, arguments);
+
+ /* Walk past the arg-pointer and structure value address. */
+ size = GET_MODE_SIZE (Pmode);
+ if (struct_value_rtx)
+ size += GET_MODE_SIZE (Pmode);
+
+ /* Restore each of the registers previously saved. Make USE insns
+ for each of these registers for use in making the call. */
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if ((mode = apply_args_mode[regno]) != VOIDmode)
+ {
+ align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
+ if (size % align != 0)
+ size = CEIL (size, align) * align;
+ reg = gen_rtx (REG, mode, regno);
+ emit_move_insn (reg,
+ change_address (arguments, mode,
+ plus_constant (XEXP (arguments, 0),
+ size)));
+
+ use_reg (&call_fusage, reg);
+ size += GET_MODE_SIZE (mode);
+ }
+
+ /* Restore the structure value address unless this is passed as an
+ "invisible" first argument. */
+ size = GET_MODE_SIZE (Pmode);
+ if (struct_value_rtx)
+ {
+ rtx value = gen_reg_rtx (Pmode);
+ emit_move_insn (value,
+ change_address (arguments, Pmode,
+ plus_constant (XEXP (arguments, 0),
+ size)));
+ emit_move_insn (struct_value_rtx, value);
+ if (GET_CODE (struct_value_rtx) == REG)
+ use_reg (&call_fusage, struct_value_rtx);
+ size += GET_MODE_SIZE (Pmode);
+ }
+
+ /* All arguments and registers used for the call are set up by now! */
+ function = prepare_call_address (function, NULL_TREE, &call_fusage, 0);
+
+ /* Ensure address is valid. SYMBOL_REF is already valid, so no need,
+ and we don't want to load it into a register as an optimization,
+ because prepare_call_address already did it if it should be done. */
+ if (GET_CODE (function) != SYMBOL_REF)
+ function = memory_address (FUNCTION_MODE, function);
+
+ /* Generate the actual call instruction and save the return value. */
+#ifdef HAVE_untyped_call
+ if (HAVE_untyped_call)
+ emit_call_insn (gen_untyped_call (gen_rtx (MEM, FUNCTION_MODE, function),
+ result, result_vector (1, result)));
+ else
+#endif
+#ifdef HAVE_call_value
+ if (HAVE_call_value)
+ {
+ rtx valreg = 0;
+
+ /* Locate the unique return register. It is not possible to
+ express a call that sets more than one return register using
+ call_value; use untyped_call for that. In fact, untyped_call
+ only needs to save the return registers in the given block. */
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if ((mode = apply_result_mode[regno]) != VOIDmode)
+ {
+ if (valreg)
+ abort (); /* HAVE_untyped_call required. */
+ valreg = gen_rtx (REG, mode, regno);
+ }
+
+ emit_call_insn (gen_call_value (valreg,
+ gen_rtx (MEM, FUNCTION_MODE, function),
+ const0_rtx, NULL_RTX, const0_rtx));
+
+ emit_move_insn (change_address (result, GET_MODE (valreg),
+ XEXP (result, 0)),
+ valreg);
+ }
+ else
+#endif
+ abort ();
+
+ /* Find the CALL insn we just emitted. */
+ for (call_insn = get_last_insn ();
+ call_insn && GET_CODE (call_insn) != CALL_INSN;
+ call_insn = PREV_INSN (call_insn))
+ ;
+
+ if (! call_insn)
+ abort ();
+
+ /* Put the register usage information on the CALL. If there is already
+ some usage information, put ours at the end. */
+ if (CALL_INSN_FUNCTION_USAGE (call_insn))
+ {
+ rtx link;
+
+ for (link = CALL_INSN_FUNCTION_USAGE (call_insn); XEXP (link, 1) != 0;
+ link = XEXP (link, 1))
+ ;
+
+ XEXP (link, 1) = call_fusage;
+ }
+ else
+ CALL_INSN_FUNCTION_USAGE (call_insn) = call_fusage;
+
+ /* Restore the stack. */
+ emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
+
+ /* Return the address of the result block. */
+ return copy_addr_to_reg (XEXP (result, 0));
+}
+
+/* Perform an untyped return. */
+
+static void
+expand_builtin_return (result)
+ rtx result;
+{
+ int size, align, regno;
+ enum machine_mode mode;
+ rtx reg;
+ rtx call_fusage = 0;
+
+ apply_result_size ();
+ result = gen_rtx (MEM, BLKmode, result);
+
+#ifdef HAVE_untyped_return
+ if (HAVE_untyped_return)
+ {
+ emit_jump_insn (gen_untyped_return (result, result_vector (0, result)));
+ emit_barrier ();
+ return;
+ }
+#endif
+
+ /* Restore the return value and note that each value is used. */
+ size = 0;
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if ((mode = apply_result_mode[regno]) != VOIDmode)
+ {
+ align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
+ if (size % align != 0)
+ size = CEIL (size, align) * align;
+ reg = gen_rtx (REG, mode, INCOMING_REGNO (regno));
+ emit_move_insn (reg,
+ change_address (result, mode,
+ plus_constant (XEXP (result, 0),
+ size)));
+
+ push_to_sequence (call_fusage);
+ emit_insn (gen_rtx (USE, VOIDmode, reg));
+ call_fusage = get_insns ();
+ end_sequence ();
+ size += GET_MODE_SIZE (mode);
+ }
+
+ /* Put the USE insns before the return. */
+ emit_insns (call_fusage);
+
+ /* Return whatever values was restored by jumping directly to the end
+ of the function. */
+ expand_null_return ();
+}
+
+/* Expand code for a post- or pre- increment or decrement
+ and return the RTX for the result.
+ POST is 1 for postinc/decrements and 0 for preinc/decrements. */
+
+static rtx
+expand_increment (exp, post)
+ register tree exp;
+ int post;
+{
+ register rtx op0, op1;
+ register rtx temp, value;
+ register tree incremented = TREE_OPERAND (exp, 0);
+ optab this_optab = add_optab;
+ int icode;
+ enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
+ int op0_is_copy = 0;
+ int single_insn = 0;
+ /* 1 means we can't store into OP0 directly,
+ because it is a subreg narrower than a word,
+ and we don't dare clobber the rest of the word. */
+ int bad_subreg = 0;
+
+ if (output_bytecode)
+ {
+ bc_expand_expr (exp);
+ return NULL_RTX;
+ }
+
+ /* Stabilize any component ref that might need to be
+ evaluated more than once below. */
+ if (!post
+ || TREE_CODE (incremented) == BIT_FIELD_REF
+ || (TREE_CODE (incremented) == COMPONENT_REF
+ && (TREE_CODE (TREE_OPERAND (incremented, 0)) != INDIRECT_REF
+ || DECL_BIT_FIELD (TREE_OPERAND (incremented, 1)))))
+ incremented = stabilize_reference (incremented);
+ /* Nested *INCREMENT_EXPRs can happen in C++. We must force innermost
+ ones into save exprs so that they don't accidentally get evaluated
+ more than once by the code below. */
+ if (TREE_CODE (incremented) == PREINCREMENT_EXPR
+ || TREE_CODE (incremented) == PREDECREMENT_EXPR)
+ incremented = save_expr (incremented);
+
+ /* Compute the operands as RTX.
+ Note whether OP0 is the actual lvalue or a copy of it:
+ I believe it is a copy iff it is a register or subreg
+ and insns were generated in computing it. */
+
+ temp = get_last_insn ();
+ op0 = expand_expr (incremented, NULL_RTX, VOIDmode, 0);
+
+ /* If OP0 is a SUBREG made for a promoted variable, we cannot increment
+ in place but intead must do sign- or zero-extension during assignment,
+ so we copy it into a new register and let the code below use it as
+ a copy.
+
+ Note that we can safely modify this SUBREG since it is know not to be
+ shared (it was made by the expand_expr call above). */
+
+ if (GET_CODE (op0) == SUBREG && SUBREG_PROMOTED_VAR_P (op0))
+ {
+ if (post)
+ SUBREG_REG (op0) = copy_to_reg (SUBREG_REG (op0));
+ else
+ bad_subreg = 1;
+ }
+ else if (GET_CODE (op0) == SUBREG
+ && GET_MODE_BITSIZE (GET_MODE (op0)) < BITS_PER_WORD)
+ {
+ /* We cannot increment this SUBREG in place. If we are
+ post-incrementing, get a copy of the old value. Otherwise,
+ just mark that we cannot increment in place. */
+ if (post)
+ op0 = copy_to_reg (op0);
+ else
+ bad_subreg = 1;
+ }
+
+ op0_is_copy = ((GET_CODE (op0) == SUBREG || GET_CODE (op0) == REG)
+ && temp != get_last_insn ());
+ op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0);
+
+ /* Decide whether incrementing or decrementing. */
+ if (TREE_CODE (exp) == POSTDECREMENT_EXPR
+ || TREE_CODE (exp) == PREDECREMENT_EXPR)
+ this_optab = sub_optab;
+
+ /* Convert decrement by a constant into a negative increment. */
+ if (this_optab == sub_optab
+ && GET_CODE (op1) == CONST_INT)
+ {
+ op1 = GEN_INT (- INTVAL (op1));
+ this_optab = add_optab;
+ }
+
+ /* For a preincrement, see if we can do this with a single instruction. */
+ if (!post)
+ {
+ icode = (int) this_optab->handlers[(int) mode].insn_code;
+ if (icode != (int) CODE_FOR_nothing
+ /* Make sure that OP0 is valid for operands 0 and 1
+ of the insn we want to queue. */
+ && (*insn_operand_predicate[icode][0]) (op0, mode)
+ && (*insn_operand_predicate[icode][1]) (op0, mode)
+ && (*insn_operand_predicate[icode][2]) (op1, mode))
+ single_insn = 1;
+ }
+
+ /* If OP0 is not the actual lvalue, but rather a copy in a register,
+ then we cannot just increment OP0. We must therefore contrive to
+ increment the original value. Then, for postincrement, we can return
+ OP0 since it is a copy of the old value. For preincrement, expand here
+ unless we can do it with a single insn.
+
+ Likewise if storing directly into OP0 would clobber high bits
+ we need to preserve (bad_subreg). */
+ if (op0_is_copy || (!post && !single_insn) || bad_subreg)
+ {
+ /* This is the easiest way to increment the value wherever it is.
+ Problems with multiple evaluation of INCREMENTED are prevented
+ because either (1) it is a component_ref or preincrement,
+ in which case it was stabilized above, or (2) it is an array_ref
+ with constant index in an array in a register, which is
+ safe to reevaluate. */
+ tree newexp = build (((TREE_CODE (exp) == POSTDECREMENT_EXPR
+ || TREE_CODE (exp) == PREDECREMENT_EXPR)
+ ? MINUS_EXPR : PLUS_EXPR),
+ TREE_TYPE (exp),
+ incremented,
+ TREE_OPERAND (exp, 1));
+ temp = expand_assignment (incremented, newexp, ! post, 0);
+ return post ? op0 : temp;
+ }
+
+ if (post)
+ {
+ /* We have a true reference to the value in OP0.
+ If there is an insn to add or subtract in this mode, queue it.
+ Queueing the increment insn avoids the register shuffling
+ that often results if we must increment now and first save
+ the old value for subsequent use. */
+
+#if 0 /* Turned off to avoid making extra insn for indexed memref. */
+ op0 = stabilize (op0);
+#endif
+
+ icode = (int) this_optab->handlers[(int) mode].insn_code;
+ if (icode != (int) CODE_FOR_nothing
+ /* Make sure that OP0 is valid for operands 0 and 1
+ of the insn we want to queue. */
+ && (*insn_operand_predicate[icode][0]) (op0, mode)
+ && (*insn_operand_predicate[icode][1]) (op0, mode))
+ {
+ if (! (*insn_operand_predicate[icode][2]) (op1, mode))
+ op1 = force_reg (mode, op1);
+
+ return enqueue_insn (op0, GEN_FCN (icode) (op0, op0, op1));
+ }
+ }
+
+ /* Preincrement, or we can't increment with one simple insn. */
+ if (post)
+ /* Save a copy of the value before inc or dec, to return it later. */
+ temp = value = copy_to_reg (op0);
+ else
+ /* Arrange to return the incremented value. */
+ /* Copy the rtx because expand_binop will protect from the queue,
+ and the results of that would be invalid for us to return
+ if our caller does emit_queue before using our result. */
+ temp = copy_rtx (value = op0);
+
+ /* Increment however we can. */
+ op1 = expand_binop (mode, this_optab, value, op1, op0,
+ TREE_UNSIGNED (TREE_TYPE (exp)), OPTAB_LIB_WIDEN);
+ /* Make sure the value is stored into OP0. */
+ if (op1 != op0)
+ emit_move_insn (op0, op1);
+
+ return temp;
+}
+
+/* Expand all function calls contained within EXP, innermost ones first.
+ But don't look within expressions that have sequence points.
+ For each CALL_EXPR, record the rtx for its value
+ in the CALL_EXPR_RTL field. */
+
+static void
+preexpand_calls (exp)
+ tree exp;
+{
+ register int nops, i;
+ int type = TREE_CODE_CLASS (TREE_CODE (exp));
+
+ if (! do_preexpand_calls)
+ return;
+
+ /* Only expressions and references can contain calls. */
+
+ if (type != 'e' && type != '<' && type != '1' && type != '2' && type != 'r')
+ return;
+
+ switch (TREE_CODE (exp))
+ {
+ case CALL_EXPR:
+ /* Do nothing if already expanded. */
+ if (CALL_EXPR_RTL (exp) != 0)
+ return;
+
+ /* Do nothing to built-in functions. */
+ if (TREE_CODE (TREE_OPERAND (exp, 0)) != ADDR_EXPR
+ || TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) != FUNCTION_DECL
+ || ! DECL_BUILT_IN (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
+ CALL_EXPR_RTL (exp) = expand_call (exp, NULL_RTX, 0);
+ return;
+
+ case COMPOUND_EXPR:
+ case COND_EXPR:
+ case TRUTH_ANDIF_EXPR:
+ case TRUTH_ORIF_EXPR:
+ /* If we find one of these, then we can be sure
+ the adjust will be done for it (since it makes jumps).
+ Do it now, so that if this is inside an argument
+ of a function, we don't get the stack adjustment
+ after some other args have already been pushed. */
+ do_pending_stack_adjust ();
+ return;
+
+ case BLOCK:
+ case RTL_EXPR:
+ case WITH_CLEANUP_EXPR:
+ return;
+
+ case SAVE_EXPR:
+ if (SAVE_EXPR_RTL (exp) != 0)
+ return;
+ }
+
+ nops = tree_code_length[(int) TREE_CODE (exp)];
+ for (i = 0; i < nops; i++)
+ if (TREE_OPERAND (exp, i) != 0)
+ {
+ type = TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, i)));
+ if (type == 'e' || type == '<' || type == '1' || type == '2'
+ || type == 'r')
+ preexpand_calls (TREE_OPERAND (exp, i));
+ }
+}
+
+/* At the start of a function, record that we have no previously-pushed
+ arguments waiting to be popped. */
+
+void
+init_pending_stack_adjust ()
+{
+ pending_stack_adjust = 0;
+}
+
+/* When exiting from function, if safe, clear out any pending stack adjust
+ so the adjustment won't get done. */
+
+void
+clear_pending_stack_adjust ()
+{
+#ifdef EXIT_IGNORE_STACK
+ if (! flag_omit_frame_pointer && EXIT_IGNORE_STACK
+ && ! (DECL_INLINE (current_function_decl) && ! flag_no_inline)
+ && ! flag_inline_functions)
+ pending_stack_adjust = 0;
+#endif
+}
+
+/* Pop any previously-pushed arguments that have not been popped yet. */
+
+void
+do_pending_stack_adjust ()
+{
+ if (inhibit_defer_pop == 0)
+ {
+ if (pending_stack_adjust != 0)
+ adjust_stack (GEN_INT (pending_stack_adjust));
+ pending_stack_adjust = 0;
+ }
+}
+
+/* Defer the expansion all cleanups up to OLD_CLEANUPS.
+ Returns the cleanups to be performed. */
+
+static tree
+defer_cleanups_to (old_cleanups)
+ tree old_cleanups;
+{
+ tree new_cleanups = NULL_TREE;
+ tree cleanups = cleanups_this_call;
+ tree last = NULL_TREE;
+
+ while (cleanups_this_call != old_cleanups)
+ {
+ (*interim_eh_hook) (TREE_VALUE (cleanups_this_call));
+ cleanups_this_call = TREE_CHAIN (cleanups_this_call);
+ }
+
+ if (last)
+ {
+ /* Remove the list from the chain of cleanups. */
+ TREE_CHAIN (last) = NULL_TREE;
+
+ /* reverse them so that we can build them in the right order. */
+ cleanups = nreverse (cleanups);
+
+ while (cleanups)
+ {
+ if (new_cleanups)
+ new_cleanups = build (COMPOUND_EXPR, TREE_TYPE (new_cleanups),
+ TREE_VALUE (cleanups), new_cleanups);
+ else
+ new_cleanups = TREE_VALUE (cleanups);
+
+ cleanups = TREE_CHAIN (cleanups);
+ }
+ }
+
+ return new_cleanups;
+}
+
+/* Expand all cleanups up to OLD_CLEANUPS.
+ Needed here, and also for language-dependent calls. */
+
+void
+expand_cleanups_to (old_cleanups)
+ tree old_cleanups;
+{
+ while (cleanups_this_call != old_cleanups)
+ {
+ (*interim_eh_hook) (TREE_VALUE (cleanups_this_call));
+ expand_expr (TREE_VALUE (cleanups_this_call), const0_rtx, VOIDmode, 0);
+ cleanups_this_call = TREE_CHAIN (cleanups_this_call);
+ }
+}
+
+/* Expand conditional expressions. */
+
+/* Generate code to evaluate EXP and jump to LABEL if the value is zero.
+ LABEL is an rtx of code CODE_LABEL, in this function and all the
+ functions here. */
+
+void
+jumpifnot (exp, label)
+ tree exp;
+ rtx label;
+{
+ do_jump (exp, label, NULL_RTX);
+}
+
+/* Generate code to evaluate EXP and jump to LABEL if the value is nonzero. */
+
+void
+jumpif (exp, label)
+ tree exp;
+ rtx label;
+{
+ do_jump (exp, NULL_RTX, label);
+}
+
+/* Generate code to evaluate EXP and jump to IF_FALSE_LABEL if
+ the result is zero, or IF_TRUE_LABEL if the result is one.
+ Either of IF_FALSE_LABEL and IF_TRUE_LABEL may be zero,
+ meaning fall through in that case.
+
+ do_jump always does any pending stack adjust except when it does not
+ actually perform a jump. An example where there is no jump
+ is when EXP is `(foo (), 0)' and IF_FALSE_LABEL is null.
+
+ This function is responsible for optimizing cases such as
+ &&, || and comparison operators in EXP. */
+
+void
+do_jump (exp, if_false_label, if_true_label)
+ tree exp;
+ rtx if_false_label, if_true_label;
+{
+ register enum tree_code code = TREE_CODE (exp);
+ /* Some cases need to create a label to jump to
+ in order to properly fall through.
+ These cases set DROP_THROUGH_LABEL nonzero. */
+ rtx drop_through_label = 0;
+ rtx temp;
+ rtx comparison = 0;
+ int i;
+ tree type;
+ enum machine_mode mode;
+
+ emit_queue ();
+
+ switch (code)
+ {
+ case ERROR_MARK:
+ break;
+
+ case INTEGER_CST:
+ temp = integer_zerop (exp) ? if_false_label : if_true_label;
+ if (temp)
+ emit_jump (temp);
+ break;
+
+#if 0
+ /* This is not true with #pragma weak */
+ case ADDR_EXPR:
+ /* The address of something can never be zero. */
+ if (if_true_label)
+ emit_jump (if_true_label);
+ break;
+#endif
+
+ case NOP_EXPR:
+ if (TREE_CODE (TREE_OPERAND (exp, 0)) == COMPONENT_REF
+ || TREE_CODE (TREE_OPERAND (exp, 0)) == BIT_FIELD_REF
+ || TREE_CODE (TREE_OPERAND (exp, 0)) == ARRAY_REF)
+ goto normal;
+ case CONVERT_EXPR:
+ /* If we are narrowing the operand, we have to do the compare in the
+ narrower mode. */
+ if ((TYPE_PRECISION (TREE_TYPE (exp))
+ < TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (exp, 0)))))
+ goto normal;
+ case NON_LVALUE_EXPR:
+ case REFERENCE_EXPR:
+ case ABS_EXPR:
+ case NEGATE_EXPR:
+ case LROTATE_EXPR:
+ case RROTATE_EXPR:
+ /* These cannot change zero->non-zero or vice versa. */
+ do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label);
+ break;
+
+#if 0
+ /* This is never less insns than evaluating the PLUS_EXPR followed by
+ a test and can be longer if the test is eliminated. */
+ case PLUS_EXPR:
+ /* Reduce to minus. */
+ exp = build (MINUS_EXPR, TREE_TYPE (exp),
+ TREE_OPERAND (exp, 0),
+ fold (build1 (NEGATE_EXPR, TREE_TYPE (TREE_OPERAND (exp, 1)),
+ TREE_OPERAND (exp, 1))));
+ /* Process as MINUS. */
+#endif
+
+ case MINUS_EXPR:
+ /* Non-zero iff operands of minus differ. */
+ comparison = compare (build (NE_EXPR, TREE_TYPE (exp),
+ TREE_OPERAND (exp, 0),
+ TREE_OPERAND (exp, 1)),
+ NE, NE);
+ break;
+
+ case BIT_AND_EXPR:
+ /* If we are AND'ing with a small constant, do this comparison in the
+ smallest type that fits. If the machine doesn't have comparisons
+ that small, it will be converted back to the wider comparison.
+ This helps if we are testing the sign bit of a narrower object.
+ combine can't do this for us because it can't know whether a
+ ZERO_EXTRACT or a compare in a smaller mode exists, but we do. */
+
+ if (! SLOW_BYTE_ACCESS
+ && TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST
+ && TYPE_PRECISION (TREE_TYPE (exp)) <= HOST_BITS_PER_WIDE_INT
+ && (i = floor_log2 (TREE_INT_CST_LOW (TREE_OPERAND (exp, 1)))) >= 0
+ && (mode = mode_for_size (i + 1, MODE_INT, 0)) != BLKmode
+ && (type = type_for_mode (mode, 1)) != 0
+ && TYPE_PRECISION (type) < TYPE_PRECISION (TREE_TYPE (exp))
+ && (cmp_optab->handlers[(int) TYPE_MODE (type)].insn_code
+ != CODE_FOR_nothing))
+ {
+ do_jump (convert (type, exp), if_false_label, if_true_label);
+ break;
+ }
+ goto normal;
+
+ case TRUTH_NOT_EXPR:
+ do_jump (TREE_OPERAND (exp, 0), if_true_label, if_false_label);
+ break;
+
+ case TRUTH_ANDIF_EXPR:
+ if (if_false_label == 0)
+ if_false_label = drop_through_label = gen_label_rtx ();
+ do_jump (TREE_OPERAND (exp, 0), if_false_label, NULL_RTX);
+ do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label);
+ break;
+
+ case TRUTH_ORIF_EXPR:
+ if (if_true_label == 0)
+ if_true_label = drop_through_label = gen_label_rtx ();
+ do_jump (TREE_OPERAND (exp, 0), NULL_RTX, if_true_label);
+ do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label);
+ break;
+
+ case COMPOUND_EXPR:
+ push_temp_slots ();
+ expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, 0);
+ free_temp_slots ();
+ pop_temp_slots ();
+ emit_queue ();
+ do_pending_stack_adjust ();
+ do_jump (TREE_OPERAND (exp, 1), if_false_label, if_true_label);
+ break;
+
+ case COMPONENT_REF:
+ case BIT_FIELD_REF:
+ case ARRAY_REF:
+ {
+ int bitsize, bitpos, unsignedp;
+ enum machine_mode mode;
+ tree type;
+ tree offset;
+ int volatilep = 0;
+
+ /* Get description of this reference. We don't actually care
+ about the underlying object here. */
+ get_inner_reference (exp, &bitsize, &bitpos, &offset,
+ &mode, &unsignedp, &volatilep);
+
+ type = type_for_size (bitsize, unsignedp);
+ if (! SLOW_BYTE_ACCESS
+ && type != 0 && bitsize >= 0
+ && TYPE_PRECISION (type) < TYPE_PRECISION (TREE_TYPE (exp))
+ && (cmp_optab->handlers[(int) TYPE_MODE (type)].insn_code
+ != CODE_FOR_nothing))
+ {
+ do_jump (convert (type, exp), if_false_label, if_true_label);
+ break;
+ }
+ goto normal;
+ }
+
+ case COND_EXPR:
+ /* Do (a ? 1 : 0) and (a ? 0 : 1) as special cases. */
+ if (integer_onep (TREE_OPERAND (exp, 1))
+ && integer_zerop (TREE_OPERAND (exp, 2)))
+ do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label);
+
+ else if (integer_zerop (TREE_OPERAND (exp, 1))
+ && integer_onep (TREE_OPERAND (exp, 2)))
+ do_jump (TREE_OPERAND (exp, 0), if_true_label, if_false_label);
+
+ else
+ {
+ register rtx label1 = gen_label_rtx ();
+ drop_through_label = gen_label_rtx ();
+ do_jump (TREE_OPERAND (exp, 0), label1, NULL_RTX);
+ /* Now the THEN-expression. */
+ do_jump (TREE_OPERAND (exp, 1),
+ if_false_label ? if_false_label : drop_through_label,
+ if_true_label ? if_true_label : drop_through_label);
+ /* In case the do_jump just above never jumps. */
+ do_pending_stack_adjust ();
+ emit_label (label1);
+ /* Now the ELSE-expression. */
+ do_jump (TREE_OPERAND (exp, 2),
+ if_false_label ? if_false_label : drop_through_label,
+ if_true_label ? if_true_label : drop_through_label);
+ }
+ break;
+
+ case EQ_EXPR:
+ if (integer_zerop (TREE_OPERAND (exp, 1)))
+ do_jump (TREE_OPERAND (exp, 0), if_true_label, if_false_label);
+ else if (((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))
+ == MODE_INT)
+ &&
+ !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))))
+ || GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) == MODE_COMPLEX_FLOAT
+ || GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) == MODE_COMPLEX_INT)
+ do_jump_by_parts_equality (exp, if_false_label, if_true_label);
+ else
+ comparison = compare (exp, EQ, EQ);
+ break;
+
+ case NE_EXPR:
+ if (integer_zerop (TREE_OPERAND (exp, 1)))
+ do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label);
+ else if (((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))
+ == MODE_INT)
+ &&
+ !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))))
+ || GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) == MODE_COMPLEX_FLOAT
+ || GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) == MODE_COMPLEX_INT)
+ do_jump_by_parts_equality (exp, if_true_label, if_false_label);
+ else
+ comparison = compare (exp, NE, NE);
+ break;
+
+ case LT_EXPR:
+ if ((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))
+ == MODE_INT)
+ && !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))))
+ do_jump_by_parts_greater (exp, 1, if_false_label, if_true_label);
+ else
+ comparison = compare (exp, LT, LTU);
+ break;
+
+ case LE_EXPR:
+ if ((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))
+ == MODE_INT)
+ && !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))))
+ do_jump_by_parts_greater (exp, 0, if_true_label, if_false_label);
+ else
+ comparison = compare (exp, LE, LEU);
+ break;
+
+ case GT_EXPR:
+ if ((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))
+ == MODE_INT)
+ && !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))))
+ do_jump_by_parts_greater (exp, 0, if_false_label, if_true_label);
+ else
+ comparison = compare (exp, GT, GTU);
+ break;
+
+ case GE_EXPR:
+ if ((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))
+ == MODE_INT)
+ && !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))))
+ do_jump_by_parts_greater (exp, 1, if_true_label, if_false_label);
+ else
+ comparison = compare (exp, GE, GEU);
+ break;
+
+ default:
+ normal:
+ temp = expand_expr (exp, NULL_RTX, VOIDmode, 0);
+#if 0
+ /* This is not needed any more and causes poor code since it causes
+ comparisons and tests from non-SI objects to have different code
+ sequences. */
+ /* Copy to register to avoid generating bad insns by cse
+ from (set (mem ...) (arithop)) (set (cc0) (mem ...)). */
+ if (!cse_not_expected && GET_CODE (temp) == MEM)
+ temp = copy_to_reg (temp);
+#endif
+ do_pending_stack_adjust ();
+ if (GET_CODE (temp) == CONST_INT)
+ comparison = (temp == const0_rtx ? const0_rtx : const_true_rtx);
+ else if (GET_CODE (temp) == LABEL_REF)
+ comparison = const_true_rtx;
+ else if (GET_MODE_CLASS (GET_MODE (temp)) == MODE_INT
+ && !can_compare_p (GET_MODE (temp)))
+ /* Note swapping the labels gives us not-equal. */
+ do_jump_by_parts_equality_rtx (temp, if_true_label, if_false_label);
+ else if (GET_MODE (temp) != VOIDmode)
+ comparison = compare_from_rtx (temp, CONST0_RTX (GET_MODE (temp)),
+ NE, TREE_UNSIGNED (TREE_TYPE (exp)),
+ GET_MODE (temp), NULL_RTX, 0);
+ else
+ abort ();
+ }
+
+ /* Do any postincrements in the expression that was tested. */
+ emit_queue ();
+
+ /* If COMPARISON is nonzero here, it is an rtx that can be substituted
+ straight into a conditional jump instruction as the jump condition.
+ Otherwise, all the work has been done already. */
+
+ if (comparison == const_true_rtx)
+ {
+ if (if_true_label)
+ emit_jump (if_true_label);
+ }
+ else if (comparison == const0_rtx)
+ {
+ if (if_false_label)
+ emit_jump (if_false_label);
+ }
+ else if (comparison)
+ do_jump_for_compare (comparison, if_false_label, if_true_label);
+
+ if (drop_through_label)
+ {
+ /* If do_jump produces code that might be jumped around,
+ do any stack adjusts from that code, before the place
+ where control merges in. */
+ do_pending_stack_adjust ();
+ emit_label (drop_through_label);
+ }
+}
+
+/* Given a comparison expression EXP for values too wide to be compared
+ with one insn, test the comparison and jump to the appropriate label.
+ The code of EXP is ignored; we always test GT if SWAP is 0,
+ and LT if SWAP is 1. */
+
+static void
+do_jump_by_parts_greater (exp, swap, if_false_label, if_true_label)
+ tree exp;
+ int swap;
+ rtx if_false_label, if_true_label;
+{
+ rtx op0 = expand_expr (TREE_OPERAND (exp, swap), NULL_RTX, VOIDmode, 0);
+ rtx op1 = expand_expr (TREE_OPERAND (exp, !swap), NULL_RTX, VOIDmode, 0);
+ enum machine_mode mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)));
+ int nwords = (GET_MODE_SIZE (mode) / UNITS_PER_WORD);
+ rtx drop_through_label = 0;
+ int unsignedp = TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0)));
+ int i;
+
+ if (! if_true_label || ! if_false_label)
+ drop_through_label = gen_label_rtx ();
+ if (! if_true_label)
+ if_true_label = drop_through_label;
+ if (! if_false_label)
+ if_false_label = drop_through_label;
+
+ /* Compare a word at a time, high order first. */
+ for (i = 0; i < nwords; i++)
+ {
+ rtx comp;
+ rtx op0_word, op1_word;
+
+ if (WORDS_BIG_ENDIAN)
+ {
+ op0_word = operand_subword_force (op0, i, mode);
+ op1_word = operand_subword_force (op1, i, mode);
+ }
+ else
+ {
+ op0_word = operand_subword_force (op0, nwords - 1 - i, mode);
+ op1_word = operand_subword_force (op1, nwords - 1 - i, mode);
+ }
+
+ /* All but high-order word must be compared as unsigned. */
+ comp = compare_from_rtx (op0_word, op1_word,
+ (unsignedp || i > 0) ? GTU : GT,
+ unsignedp, word_mode, NULL_RTX, 0);
+ if (comp == const_true_rtx)
+ emit_jump (if_true_label);
+ else if (comp != const0_rtx)
+ do_jump_for_compare (comp, NULL_RTX, if_true_label);
+
+ /* Consider lower words only if these are equal. */
+ comp = compare_from_rtx (op0_word, op1_word, NE, unsignedp, word_mode,
+ NULL_RTX, 0);
+ if (comp == const_true_rtx)
+ emit_jump (if_false_label);
+ else if (comp != const0_rtx)
+ do_jump_for_compare (comp, NULL_RTX, if_false_label);
+ }
+
+ if (if_false_label)
+ emit_jump (if_false_label);
+ if (drop_through_label)
+ emit_label (drop_through_label);
+}
+
+/* Compare OP0 with OP1, word at a time, in mode MODE.
+ UNSIGNEDP says to do unsigned comparison.
+ Jump to IF_TRUE_LABEL if OP0 is greater, IF_FALSE_LABEL otherwise. */
+
+static void
+do_jump_by_parts_greater_rtx (mode, unsignedp, op0, op1, if_false_label, if_true_label)
+ enum machine_mode mode;
+ int unsignedp;
+ rtx op0, op1;
+ rtx if_false_label, if_true_label;
+{
+ int nwords = (GET_MODE_SIZE (mode) / UNITS_PER_WORD);
+ rtx drop_through_label = 0;
+ int i;
+
+ if (! if_true_label || ! if_false_label)
+ drop_through_label = gen_label_rtx ();
+ if (! if_true_label)
+ if_true_label = drop_through_label;
+ if (! if_false_label)
+ if_false_label = drop_through_label;
+
+ /* Compare a word at a time, high order first. */
+ for (i = 0; i < nwords; i++)
+ {
+ rtx comp;
+ rtx op0_word, op1_word;
+
+ if (WORDS_BIG_ENDIAN)
+ {
+ op0_word = operand_subword_force (op0, i, mode);
+ op1_word = operand_subword_force (op1, i, mode);
+ }
+ else
+ {
+ op0_word = operand_subword_force (op0, nwords - 1 - i, mode);
+ op1_word = operand_subword_force (op1, nwords - 1 - i, mode);
+ }
+
+ /* All but high-order word must be compared as unsigned. */
+ comp = compare_from_rtx (op0_word, op1_word,
+ (unsignedp || i > 0) ? GTU : GT,
+ unsignedp, word_mode, NULL_RTX, 0);
+ if (comp == const_true_rtx)
+ emit_jump (if_true_label);
+ else if (comp != const0_rtx)
+ do_jump_for_compare (comp, NULL_RTX, if_true_label);
+
+ /* Consider lower words only if these are equal. */
+ comp = compare_from_rtx (op0_word, op1_word, NE, unsignedp, word_mode,
+ NULL_RTX, 0);
+ if (comp == const_true_rtx)
+ emit_jump (if_false_label);
+ else if (comp != const0_rtx)
+ do_jump_for_compare (comp, NULL_RTX, if_false_label);
+ }
+
+ if (if_false_label)
+ emit_jump (if_false_label);
+ if (drop_through_label)
+ emit_label (drop_through_label);
+}
+
+/* Given an EQ_EXPR expression EXP for values too wide to be compared
+ with one insn, test the comparison and jump to the appropriate label. */
+
+static void
+do_jump_by_parts_equality (exp, if_false_label, if_true_label)
+ tree exp;
+ rtx if_false_label, if_true_label;
+{
+ rtx op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0);
+ rtx op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0);
+ enum machine_mode mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)));
+ int nwords = (GET_MODE_SIZE (mode) / UNITS_PER_WORD);
+ int i;
+ rtx drop_through_label = 0;
+
+ if (! if_false_label)
+ drop_through_label = if_false_label = gen_label_rtx ();
+
+ for (i = 0; i < nwords; i++)
+ {
+ rtx comp = compare_from_rtx (operand_subword_force (op0, i, mode),
+ operand_subword_force (op1, i, mode),
+ EQ, TREE_UNSIGNED (TREE_TYPE (exp)),
+ word_mode, NULL_RTX, 0);
+ if (comp == const_true_rtx)
+ emit_jump (if_false_label);
+ else if (comp != const0_rtx)
+ do_jump_for_compare (comp, if_false_label, NULL_RTX);
+ }
+
+ if (if_true_label)
+ emit_jump (if_true_label);
+ if (drop_through_label)
+ emit_label (drop_through_label);
+}
+
+/* Jump according to whether OP0 is 0.
+ We assume that OP0 has an integer mode that is too wide
+ for the available compare insns. */
+
+static void
+do_jump_by_parts_equality_rtx (op0, if_false_label, if_true_label)
+ rtx op0;
+ rtx if_false_label, if_true_label;
+{
+ int nwords = GET_MODE_SIZE (GET_MODE (op0)) / UNITS_PER_WORD;
+ int i;
+ rtx drop_through_label = 0;
+
+ if (! if_false_label)
+ drop_through_label = if_false_label = gen_label_rtx ();
+
+ for (i = 0; i < nwords; i++)
+ {
+ rtx comp = compare_from_rtx (operand_subword_force (op0, i,
+ GET_MODE (op0)),
+ const0_rtx, EQ, 1, word_mode, NULL_RTX, 0);
+ if (comp == const_true_rtx)
+ emit_jump (if_false_label);
+ else if (comp != const0_rtx)
+ do_jump_for_compare (comp, if_false_label, NULL_RTX);
+ }
+
+ if (if_true_label)
+ emit_jump (if_true_label);
+ if (drop_through_label)
+ emit_label (drop_through_label);
+}
+
+/* Given a comparison expression in rtl form, output conditional branches to
+ IF_TRUE_LABEL, IF_FALSE_LABEL, or both. */
+
+static void
+do_jump_for_compare (comparison, if_false_label, if_true_label)
+ rtx comparison, if_false_label, if_true_label;
+{
+ if (if_true_label)
+ {
+ if (bcc_gen_fctn[(int) GET_CODE (comparison)] != 0)
+ emit_jump_insn ((*bcc_gen_fctn[(int) GET_CODE (comparison)]) (if_true_label));
+ else
+ abort ();
+
+ if (if_false_label)
+ emit_jump (if_false_label);
+ }
+ else if (if_false_label)
+ {
+ rtx insn;
+ rtx prev = get_last_insn ();
+ rtx branch = 0;
+
+ if (prev != 0)
+ prev = PREV_INSN (prev);
+
+ /* Output the branch with the opposite condition. Then try to invert
+ what is generated. If more than one insn is a branch, or if the
+ branch is not the last insn written, abort. If we can't invert
+ the branch, emit make a true label, redirect this jump to that,
+ emit a jump to the false label and define the true label. */
+
+ if (bcc_gen_fctn[(int) GET_CODE (comparison)] != 0)
+ emit_jump_insn ((*bcc_gen_fctn[(int) GET_CODE (comparison)]) (if_false_label));
+ else
+ abort ();
+
+ /* Here we get the insn before what was just emitted.
+ On some machines, emitting the branch can discard
+ the previous compare insn and emit a replacement. */
+ if (prev == 0)
+ /* If there's only one preceding insn... */
+ insn = get_insns ();
+ else
+ insn = NEXT_INSN (prev);
+
+ for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == JUMP_INSN)
+ {
+ if (branch)
+ abort ();
+ branch = insn;
+ }
+
+ if (branch != get_last_insn ())
+ abort ();
+
+ JUMP_LABEL (branch) = if_false_label;
+ if (! invert_jump (branch, if_false_label))
+ {
+ if_true_label = gen_label_rtx ();
+ redirect_jump (branch, if_true_label);
+ emit_jump (if_false_label);
+ emit_label (if_true_label);
+ }
+ }
+}
+
+/* Generate code for a comparison expression EXP
+ (including code to compute the values to be compared)
+ and set (CC0) according to the result.
+ SIGNED_CODE should be the rtx operation for this comparison for
+ signed data; UNSIGNED_CODE, likewise for use if data is unsigned.
+
+ We force a stack adjustment unless there are currently
+ things pushed on the stack that aren't yet used. */
+
+static rtx
+compare (exp, signed_code, unsigned_code)
+ register tree exp;
+ enum rtx_code signed_code, unsigned_code;
+{
+ register rtx op0
+ = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0);
+ register rtx op1
+ = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX, VOIDmode, 0);
+ register tree type = TREE_TYPE (TREE_OPERAND (exp, 0));
+ register enum machine_mode mode = TYPE_MODE (type);
+ int unsignedp = TREE_UNSIGNED (type);
+ enum rtx_code code = unsignedp ? unsigned_code : signed_code;
+
+ return compare_from_rtx (op0, op1, code, unsignedp, mode,
+ ((mode == BLKmode)
+ ? expr_size (TREE_OPERAND (exp, 0)) : NULL_RTX),
+ TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT);
+}
+
+/* Like compare but expects the values to compare as two rtx's.
+ The decision as to signed or unsigned comparison must be made by the caller.
+
+ If MODE is BLKmode, SIZE is an RTX giving the size of the objects being
+ compared.
+
+ If ALIGN is non-zero, it is the alignment of this type; if zero, the
+ size of MODE should be used. */
+
+rtx
+compare_from_rtx (op0, op1, code, unsignedp, mode, size, align)
+ register rtx op0, op1;
+ enum rtx_code code;
+ int unsignedp;
+ enum machine_mode mode;
+ rtx size;
+ int align;
+{
+ rtx tem;
+
+ /* If one operand is constant, make it the second one. Only do this
+ if the other operand is not constant as well. */
+
+ if ((CONSTANT_P (op0) && ! CONSTANT_P (op1))
+ || (GET_CODE (op0) == CONST_INT && GET_CODE (op1) != CONST_INT))
+ {
+ tem = op0;
+ op0 = op1;
+ op1 = tem;
+ code = swap_condition (code);
+ }
+
+ if (flag_force_mem)
+ {
+ op0 = force_not_mem (op0);
+ op1 = force_not_mem (op1);
+ }
+
+ do_pending_stack_adjust ();
+
+ if (GET_CODE (op0) == CONST_INT && GET_CODE (op1) == CONST_INT
+ && (tem = simplify_relational_operation (code, mode, op0, op1)) != 0)
+ return tem;
+
+#if 0
+ /* There's no need to do this now that combine.c can eliminate lots of
+ sign extensions. This can be less efficient in certain cases on other
+ machines. */
+
+ /* If this is a signed equality comparison, we can do it as an
+ unsigned comparison since zero-extension is cheaper than sign
+ extension and comparisons with zero are done as unsigned. This is
+ the case even on machines that can do fast sign extension, since
+ zero-extension is easier to combine with other operations than
+ sign-extension is. If we are comparing against a constant, we must
+ convert it to what it would look like unsigned. */
+ if ((code == EQ || code == NE) && ! unsignedp
+ && GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT)
+ {
+ if (GET_CODE (op1) == CONST_INT
+ && (INTVAL (op1) & GET_MODE_MASK (GET_MODE (op0))) != INTVAL (op1))
+ op1 = GEN_INT (INTVAL (op1) & GET_MODE_MASK (GET_MODE (op0)));
+ unsignedp = 1;
+ }
+#endif
+
+ emit_cmp_insn (op0, op1, code, size, mode, unsignedp, align);
+
+ return gen_rtx (code, VOIDmode, cc0_rtx, const0_rtx);
+}
+
+/* Generate code to calculate EXP using a store-flag instruction
+ and return an rtx for the result. EXP is either a comparison
+ or a TRUTH_NOT_EXPR whose operand is a comparison.
+
+ If TARGET is nonzero, store the result there if convenient.
+
+ If ONLY_CHEAP is non-zero, only do this if it is likely to be very
+ cheap.
+
+ Return zero if there is no suitable set-flag instruction
+ available on this machine.
+
+ Once expand_expr has been called on the arguments of the comparison,
+ we are committed to doing the store flag, since it is not safe to
+ re-evaluate the expression. We emit the store-flag insn by calling
+ emit_store_flag, but only expand the arguments if we have a reason
+ to believe that emit_store_flag will be successful. If we think that
+ it will, but it isn't, we have to simulate the store-flag with a
+ set/jump/set sequence. */
+
+static rtx
+do_store_flag (exp, target, mode, only_cheap)
+ tree exp;
+ rtx target;
+ enum machine_mode mode;
+ int only_cheap;
+{
+ enum rtx_code code;
+ tree arg0, arg1, type;
+ tree tem;
+ enum machine_mode operand_mode;
+ int invert = 0;
+ int unsignedp;
+ rtx op0, op1;
+ enum insn_code icode;
+ rtx subtarget = target;
+ rtx result, label, pattern, jump_pat;
+
+ /* If this is a TRUTH_NOT_EXPR, set a flag indicating we must invert the
+ result at the end. We can't simply invert the test since it would
+ have already been inverted if it were valid. This case occurs for
+ some floating-point comparisons. */
+
+ if (TREE_CODE (exp) == TRUTH_NOT_EXPR)
+ invert = 1, exp = TREE_OPERAND (exp, 0);
+
+ arg0 = TREE_OPERAND (exp, 0);
+ arg1 = TREE_OPERAND (exp, 1);
+ type = TREE_TYPE (arg0);
+ operand_mode = TYPE_MODE (type);
+ unsignedp = TREE_UNSIGNED (type);
+
+ /* We won't bother with BLKmode store-flag operations because it would mean
+ passing a lot of information to emit_store_flag. */
+ if (operand_mode == BLKmode)
+ return 0;
+
+ STRIP_NOPS (arg0);
+ STRIP_NOPS (arg1);
+
+ /* Get the rtx comparison code to use. We know that EXP is a comparison
+ operation of some type. Some comparisons against 1 and -1 can be
+ converted to comparisons with zero. Do so here so that the tests
+ below will be aware that we have a comparison with zero. These
+ tests will not catch constants in the first operand, but constants
+ are rarely passed as the first operand. */
+
+ switch (TREE_CODE (exp))
+ {
+ case EQ_EXPR:
+ code = EQ;
+ break;
+ case NE_EXPR:
+ code = NE;
+ break;
+ case LT_EXPR:
+ if (integer_onep (arg1))
+ arg1 = integer_zero_node, code = unsignedp ? LEU : LE;
+ else
+ code = unsignedp ? LTU : LT;
+ break;
+ case LE_EXPR:
+ if (! unsignedp && integer_all_onesp (arg1))
+ arg1 = integer_zero_node, code = LT;
+ else
+ code = unsignedp ? LEU : LE;
+ break;
+ case GT_EXPR:
+ if (! unsignedp && integer_all_onesp (arg1))
+ arg1 = integer_zero_node, code = GE;
+ else
+ code = unsignedp ? GTU : GT;
+ break;
+ case GE_EXPR:
+ if (integer_onep (arg1))
+ arg1 = integer_zero_node, code = unsignedp ? GTU : GT;
+ else
+ code = unsignedp ? GEU : GE;
+ break;
+ default:
+ abort ();
+ }
+
+ /* Put a constant second. */
+ if (TREE_CODE (arg0) == REAL_CST || TREE_CODE (arg0) == INTEGER_CST)
+ {
+ tem = arg0; arg0 = arg1; arg1 = tem;
+ code = swap_condition (code);
+ }
+
+ /* If this is an equality or inequality test of a single bit, we can
+ do this by shifting the bit being tested to the low-order bit and
+ masking the result with the constant 1. If the condition was EQ,
+ we xor it with 1. This does not require an scc insn and is faster
+ than an scc insn even if we have it. */
+
+ if ((code == NE || code == EQ)
+ && TREE_CODE (arg0) == BIT_AND_EXPR && integer_zerop (arg1)
+ && integer_pow2p (TREE_OPERAND (arg0, 1))
+ && TYPE_PRECISION (type) <= HOST_BITS_PER_WIDE_INT)
+ {
+ tree inner = TREE_OPERAND (arg0, 0);
+ int bitnum = exact_log2 (INTVAL (expand_expr (TREE_OPERAND (arg0, 1),
+ NULL_RTX, VOIDmode, 0)));
+ int ops_unsignedp;
+
+ /* If INNER is a right shift of a constant and it plus BITNUM does
+ not overflow, adjust BITNUM and INNER. */
+
+ if (TREE_CODE (inner) == RSHIFT_EXPR
+ && TREE_CODE (TREE_OPERAND (inner, 1)) == INTEGER_CST
+ && TREE_INT_CST_HIGH (TREE_OPERAND (inner, 1)) == 0
+ && (bitnum + TREE_INT_CST_LOW (TREE_OPERAND (inner, 1))
+ < TYPE_PRECISION (type)))
+ {
+ bitnum +=TREE_INT_CST_LOW (TREE_OPERAND (inner, 1));
+ inner = TREE_OPERAND (inner, 0);
+ }
+
+ /* If we are going to be able to omit the AND below, we must do our
+ operations as unsigned. If we must use the AND, we have a choice.
+ Normally unsigned is faster, but for some machines signed is. */
+ ops_unsignedp = (bitnum == TYPE_PRECISION (type) - 1 ? 1
+#ifdef LOAD_EXTEND_OP
+ : (LOAD_EXTEND_OP (operand_mode) == SIGN_EXTEND ? 0 : 1)
+#else
+ : 1
+#endif
+ );
+
+ if (subtarget == 0 || GET_CODE (subtarget) != REG
+ || GET_MODE (subtarget) != operand_mode
+ || ! safe_from_p (subtarget, inner))
+ subtarget = 0;
+
+ op0 = expand_expr (inner, subtarget, VOIDmode, 0);
+
+ if (bitnum != 0)
+ op0 = expand_shift (RSHIFT_EXPR, GET_MODE (op0), op0,
+ size_int (bitnum), subtarget, ops_unsignedp);
+
+ if (GET_MODE (op0) != mode)
+ op0 = convert_to_mode (mode, op0, ops_unsignedp);
+
+ if ((code == EQ && ! invert) || (code == NE && invert))
+ op0 = expand_binop (mode, xor_optab, op0, const1_rtx, subtarget,
+ ops_unsignedp, OPTAB_LIB_WIDEN);
+
+ /* Put the AND last so it can combine with more things. */
+ if (bitnum != TYPE_PRECISION (type) - 1)
+ op0 = expand_and (op0, const1_rtx, subtarget);
+
+ return op0;
+ }
+
+ /* Now see if we are likely to be able to do this. Return if not. */
+ if (! can_compare_p (operand_mode))
+ return 0;
+ icode = setcc_gen_code[(int) code];
+ if (icode == CODE_FOR_nothing
+ || (only_cheap && insn_operand_mode[(int) icode][0] != mode))
+ {
+ /* We can only do this if it is one of the special cases that
+ can be handled without an scc insn. */
+ if ((code == LT && integer_zerop (arg1))
+ || (! only_cheap && code == GE && integer_zerop (arg1)))
+ ;
+ else if (BRANCH_COST >= 0
+ && ! only_cheap && (code == NE || code == EQ)
+ && TREE_CODE (type) != REAL_TYPE
+ && ((abs_optab->handlers[(int) operand_mode].insn_code
+ != CODE_FOR_nothing)
+ || (ffs_optab->handlers[(int) operand_mode].insn_code
+ != CODE_FOR_nothing)))
+ ;
+ else
+ return 0;
+ }
+
+ preexpand_calls (exp);
+ if (subtarget == 0 || GET_CODE (subtarget) != REG
+ || GET_MODE (subtarget) != operand_mode
+ || ! safe_from_p (subtarget, arg1))
+ subtarget = 0;
+
+ op0 = expand_expr (arg0, subtarget, VOIDmode, 0);
+ op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0);
+
+ if (target == 0)
+ target = gen_reg_rtx (mode);
+
+ /* Pass copies of OP0 and OP1 in case they contain a QUEUED. This is safe
+ because, if the emit_store_flag does anything it will succeed and
+ OP0 and OP1 will not be used subsequently. */
+
+ result = emit_store_flag (target, code,
+ queued_subexp_p (op0) ? copy_rtx (op0) : op0,
+ queued_subexp_p (op1) ? copy_rtx (op1) : op1,
+ operand_mode, unsignedp, 1);
+
+ if (result)
+ {
+ if (invert)
+ result = expand_binop (mode, xor_optab, result, const1_rtx,
+ result, 0, OPTAB_LIB_WIDEN);
+ return result;
+ }
+
+ /* If this failed, we have to do this with set/compare/jump/set code. */
+ if (target == 0 || GET_CODE (target) != REG
+ || reg_mentioned_p (target, op0) || reg_mentioned_p (target, op1))
+ target = gen_reg_rtx (GET_MODE (target));
+
+ emit_move_insn (target, invert ? const0_rtx : const1_rtx);
+ result = compare_from_rtx (op0, op1, code, unsignedp,
+ operand_mode, NULL_RTX, 0);
+ if (GET_CODE (result) == CONST_INT)
+ return (((result == const0_rtx && ! invert)
+ || (result != const0_rtx && invert))
+ ? const0_rtx : const1_rtx);
+
+ label = gen_label_rtx ();
+ if (bcc_gen_fctn[(int) code] == 0)
+ abort ();
+
+ emit_jump_insn ((*bcc_gen_fctn[(int) code]) (label));
+ emit_move_insn (target, invert ? const1_rtx : const0_rtx);
+ emit_label (label);
+
+ return target;
+}
+
+/* Generate a tablejump instruction (used for switch statements). */
+
+#ifdef HAVE_tablejump
+
+/* INDEX is the value being switched on, with the lowest value
+ in the table already subtracted.
+ MODE is its expected mode (needed if INDEX is constant).
+ RANGE is the length of the jump table.
+ TABLE_LABEL is a CODE_LABEL rtx for the table itself.
+
+ DEFAULT_LABEL is a CODE_LABEL rtx to jump to if the
+ index value is out of range. */
+
+void
+do_tablejump (index, mode, range, table_label, default_label)
+ rtx index, range, table_label, default_label;
+ enum machine_mode mode;
+{
+ register rtx temp, vector;
+
+ /* Do an unsigned comparison (in the proper mode) between the index
+ expression and the value which represents the length of the range.
+ Since we just finished subtracting the lower bound of the range
+ from the index expression, this comparison allows us to simultaneously
+ check that the original index expression value is both greater than
+ or equal to the minimum value of the range and less than or equal to
+ the maximum value of the range. */
+
+ emit_cmp_insn (index, range, GTU, NULL_RTX, mode, 1, 0);
+ emit_jump_insn (gen_bgtu (default_label));
+
+ /* If index is in range, it must fit in Pmode.
+ Convert to Pmode so we can index with it. */
+ if (mode != Pmode)
+ index = convert_to_mode (Pmode, index, 1);
+
+ /* Don't let a MEM slip thru, because then INDEX that comes
+ out of PIC_CASE_VECTOR_ADDRESS won't be a valid address,
+ and break_out_memory_refs will go to work on it and mess it up. */
+#ifdef PIC_CASE_VECTOR_ADDRESS
+ if (flag_pic && GET_CODE (index) != REG)
+ index = copy_to_mode_reg (Pmode, index);
+#endif
+
+ /* If flag_force_addr were to affect this address
+ it could interfere with the tricky assumptions made
+ about addresses that contain label-refs,
+ which may be valid only very near the tablejump itself. */
+ /* ??? The only correct use of CASE_VECTOR_MODE is the one inside the
+ GET_MODE_SIZE, because this indicates how large insns are. The other
+ uses should all be Pmode, because they are addresses. This code
+ could fail if addresses and insns are not the same size. */
+ index = gen_rtx (PLUS, Pmode,
+ gen_rtx (MULT, Pmode, index,
+ GEN_INT (GET_MODE_SIZE (CASE_VECTOR_MODE))),
+ gen_rtx (LABEL_REF, Pmode, table_label));
+#ifdef PIC_CASE_VECTOR_ADDRESS
+ if (flag_pic)
+ index = PIC_CASE_VECTOR_ADDRESS (index);
+ else
+#endif
+ index = memory_address_noforce (CASE_VECTOR_MODE, index);
+ temp = gen_reg_rtx (CASE_VECTOR_MODE);
+ vector = gen_rtx (MEM, CASE_VECTOR_MODE, index);
+ RTX_UNCHANGING_P (vector) = 1;
+ convert_move (temp, vector, 0);
+
+ emit_jump_insn (gen_tablejump (temp, table_label));
+
+#ifndef CASE_VECTOR_PC_RELATIVE
+ /* If we are generating PIC code or if the table is PC-relative, the
+ table and JUMP_INSN must be adjacent, so don't output a BARRIER. */
+ if (! flag_pic)
+ emit_barrier ();
+#endif
+}
+
+#endif /* HAVE_tablejump */
+
+
+/* Emit a suitable bytecode to load a value from memory, assuming a pointer
+ to that value is on the top of the stack. The resulting type is TYPE, and
+ the source declaration is DECL. */
+
+void
+bc_load_memory (type, decl)
+ tree type, decl;
+{
+ enum bytecode_opcode opcode;
+
+
+ /* Bit fields are special. We only know about signed and
+ unsigned ints, and enums. The latter are treated as
+ signed integers. */
+
+ if (DECL_BIT_FIELD (decl))
+ if (TREE_CODE (type) == ENUMERAL_TYPE
+ || TREE_CODE (type) == INTEGER_TYPE)
+ opcode = TREE_UNSIGNED (type) ? zxloadBI : sxloadBI;
+ else
+ abort ();
+ else
+ /* See corresponding comment in bc_store_memory(). */
+ if (TYPE_MODE (type) == BLKmode
+ || TYPE_MODE (type) == VOIDmode)
+ return;
+ else
+ opcode = mode_to_load_map [(int) TYPE_MODE (type)];
+
+ if (opcode == neverneverland)
+ abort ();
+
+ bc_emit_bytecode (opcode);
+
+#ifdef DEBUG_PRINT_CODE
+ fputc ('\n', stderr);
+#endif
+}
+
+
+/* Store the contents of the second stack slot to the address in the
+ top stack slot. DECL is the declaration of the destination and is used
+ to determine whether we're dealing with a bitfield. */
+
+void
+bc_store_memory (type, decl)
+ tree type, decl;
+{
+ enum bytecode_opcode opcode;
+
+
+ if (DECL_BIT_FIELD (decl))
+ {
+ if (TREE_CODE (type) == ENUMERAL_TYPE
+ || TREE_CODE (type) == INTEGER_TYPE)
+ opcode = sstoreBI;
+ else
+ abort ();
+ }
+ else
+ if (TYPE_MODE (type) == BLKmode)
+ {
+ /* Copy structure. This expands to a block copy instruction, storeBLK.
+ In addition to the arguments expected by the other store instructions,
+ it also expects a type size (SImode) on top of the stack, which is the
+ structure size in size units (usually bytes). The two first arguments
+ are already on the stack; so we just put the size on level 1. For some
+ other languages, the size may be variable, this is why we don't encode
+ it as a storeBLK literal, but rather treat it as a full-fledged expression. */
+
+ bc_expand_expr (TYPE_SIZE (type));
+ opcode = storeBLK;
+ }
+ else
+ opcode = mode_to_store_map [(int) TYPE_MODE (type)];
+
+ if (opcode == neverneverland)
+ abort ();
+
+ bc_emit_bytecode (opcode);
+
+#ifdef DEBUG_PRINT_CODE
+ fputc ('\n', stderr);
+#endif
+}
+
+
+/* Allocate local stack space sufficient to hold a value of the given
+ SIZE at alignment boundary ALIGNMENT bits. ALIGNMENT must be an
+ integral power of 2. A special case is locals of type VOID, which
+ have size 0 and alignment 1 - any "voidish" SIZE or ALIGNMENT is
+ remapped into the corresponding attribute of SI. */
+
+rtx
+bc_allocate_local (size, alignment)
+ int size, alignment;
+{
+ rtx retval;
+ int byte_alignment;
+
+ if (size < 0)
+ abort ();
+
+ /* Normalize size and alignment */
+ if (!size)
+ size = UNITS_PER_WORD;
+
+ if (alignment < BITS_PER_UNIT)
+ byte_alignment = 1 << (INT_ALIGN - 1);
+ else
+ /* Align */
+ byte_alignment = alignment / BITS_PER_UNIT;
+
+ if (local_vars_size & (byte_alignment - 1))
+ local_vars_size += byte_alignment - (local_vars_size & (byte_alignment - 1));
+
+ retval = bc_gen_rtx ((char *) 0, local_vars_size, (struct bc_label *) 0);
+ local_vars_size += size;
+
+ return retval;
+}
+
+
+/* Allocate variable-sized local array. Variable-sized arrays are
+ actually pointers to the address in memory where they are stored. */
+
+rtx
+bc_allocate_variable_array (size)
+ tree size;
+{
+ rtx retval;
+ const int ptralign = (1 << (PTR_ALIGN - 1));
+
+ /* Align pointer */
+ if (local_vars_size & ptralign)
+ local_vars_size += ptralign - (local_vars_size & ptralign);
+
+ /* Note down local space needed: pointer to block; also return
+ dummy rtx */
+
+ retval = bc_gen_rtx ((char *) 0, local_vars_size, (struct bc_label *) 0);
+ local_vars_size += POINTER_SIZE / BITS_PER_UNIT;
+ return retval;
+}
+
+
+/* Push the machine address for the given external variable offset. */
+void
+bc_load_externaddr (externaddr)
+ rtx externaddr;
+{
+ bc_emit_bytecode (constP);
+ bc_emit_code_labelref (BYTECODE_LABEL (externaddr),
+ BYTECODE_BC_LABEL (externaddr)->offset);
+
+#ifdef DEBUG_PRINT_CODE
+ fputc ('\n', stderr);
+#endif
+}
+
+
+static char *
+bc_strdup (s)
+ char *s;
+{
+ char *new = (char *) xmalloc ((strlen (s) + 1) * sizeof *s);
+ strcpy (new, s);
+ return new;
+}
+
+
+/* Like above, but expects an IDENTIFIER. */
+void
+bc_load_externaddr_id (id, offset)
+ tree id;
+ int offset;
+{
+ if (!IDENTIFIER_POINTER (id))
+ abort ();
+
+ bc_emit_bytecode (constP);
+ bc_emit_code_labelref (bc_xstrdup (IDENTIFIER_POINTER (id)), offset);
+
+#ifdef DEBUG_PRINT_CODE
+ fputc ('\n', stderr);
+#endif
+}
+
+
+/* Push the machine address for the given local variable offset. */
+void
+bc_load_localaddr (localaddr)
+ rtx localaddr;
+{
+ bc_emit_instruction (localP, (HOST_WIDE_INT) BYTECODE_BC_LABEL (localaddr)->offset);
+}
+
+
+/* Push the machine address for the given parameter offset.
+ NOTE: offset is in bits. */
+void
+bc_load_parmaddr (parmaddr)
+ rtx parmaddr;
+{
+ bc_emit_instruction (argP, ((HOST_WIDE_INT) BYTECODE_BC_LABEL (parmaddr)->offset
+ / BITS_PER_UNIT));
+}
+
+
+/* Convert a[i] into *(a + i). */
+tree
+bc_canonicalize_array_ref (exp)
+ tree exp;
+{
+ tree type = TREE_TYPE (exp);
+ tree array_adr = build1 (ADDR_EXPR, TYPE_POINTER_TO (type),
+ TREE_OPERAND (exp, 0));
+ tree index = TREE_OPERAND (exp, 1);
+
+
+ /* Convert the integer argument to a type the same size as a pointer
+ so the multiply won't overflow spuriously. */
+
+ if (TYPE_PRECISION (TREE_TYPE (index)) != POINTER_SIZE)
+ index = convert (type_for_size (POINTER_SIZE, 0), index);
+
+ /* The array address isn't volatile even if the array is.
+ (Of course this isn't terribly relevant since the bytecode
+ translator treats nearly everything as volatile anyway.) */
+ TREE_THIS_VOLATILE (array_adr) = 0;
+
+ return build1 (INDIRECT_REF, type,
+ fold (build (PLUS_EXPR,
+ TYPE_POINTER_TO (type),
+ array_adr,
+ fold (build (MULT_EXPR,
+ TYPE_POINTER_TO (type),
+ index,
+ size_in_bytes (type))))));
+}
+
+
+/* Load the address of the component referenced by the given
+ COMPONENT_REF expression.
+
+ Returns innermost lvalue. */
+
+tree
+bc_expand_component_address (exp)
+ tree exp;
+{
+ tree tem, chain;
+ enum machine_mode mode;
+ int bitpos = 0;
+ HOST_WIDE_INT SIval;
+
+
+ tem = TREE_OPERAND (exp, 1);
+ mode = DECL_MODE (tem);
+
+
+ /* Compute cumulative bit offset for nested component refs
+ and array refs, and find the ultimate containing object. */
+
+ for (tem = exp;; tem = TREE_OPERAND (tem, 0))
+ {
+ if (TREE_CODE (tem) == COMPONENT_REF)
+ bitpos += TREE_INT_CST_LOW (DECL_FIELD_BITPOS (TREE_OPERAND (tem, 1)));
+ else
+ if (TREE_CODE (tem) == ARRAY_REF
+ && TREE_CODE (TREE_OPERAND (tem, 1)) == INTEGER_CST
+ && TREE_CODE (TYPE_SIZE (TREE_TYPE (tem))) == INTEGER_CST)
+
+ bitpos += (TREE_INT_CST_LOW (TREE_OPERAND (tem, 1))
+ * TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (tem)))
+ /* * TYPE_SIZE_UNIT (TREE_TYPE (tem)) */);
+ else
+ break;
+ }
+
+ bc_expand_expr (tem);
+
+
+ /* For bitfields also push their offset and size */
+ if (DECL_BIT_FIELD (TREE_OPERAND (exp, 1)))
+ bc_push_offset_and_size (bitpos, /* DECL_SIZE_UNIT */ (TREE_OPERAND (exp, 1)));
+ else
+ if (SIval = bitpos / BITS_PER_UNIT)
+ bc_emit_instruction (addconstPSI, SIval);
+
+ return (TREE_OPERAND (exp, 1));
+}
+
+
+/* Emit code to push two SI constants */
+void
+bc_push_offset_and_size (offset, size)
+ HOST_WIDE_INT offset, size;
+{
+ bc_emit_instruction (constSI, offset);
+ bc_emit_instruction (constSI, size);
+}
+
+
+/* Emit byte code to push the address of the given lvalue expression to
+ the stack. If it's a bit field, we also push offset and size info.
+
+ Returns innermost component, which allows us to determine not only
+ its type, but also whether it's a bitfield. */
+
+tree
+bc_expand_address (exp)
+ tree exp;
+{
+ /* Safeguard */
+ if (!exp || TREE_CODE (exp) == ERROR_MARK)
+ return (exp);
+
+
+ switch (TREE_CODE (exp))
+ {
+ case ARRAY_REF:
+
+ return (bc_expand_address (bc_canonicalize_array_ref (exp)));
+
+ case COMPONENT_REF:
+
+ return (bc_expand_component_address (exp));
+
+ case INDIRECT_REF:
+
+ bc_expand_expr (TREE_OPERAND (exp, 0));
+
+ /* For variable-sized types: retrieve pointer. Sometimes the
+ TYPE_SIZE tree is NULL. Is this a bug or a feature? Let's
+ also make sure we have an operand, just in case... */
+
+ if (TREE_OPERAND (exp, 0)
+ && TYPE_SIZE (TREE_TYPE (TREE_OPERAND (exp, 0)))
+ && TREE_CODE (TYPE_SIZE (TREE_TYPE (TREE_OPERAND (exp, 0)))) != INTEGER_CST)
+ bc_emit_instruction (loadP);
+
+ /* If packed, also return offset and size */
+ if (DECL_BIT_FIELD (TREE_OPERAND (exp, 0)))
+
+ bc_push_offset_and_size (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (TREE_OPERAND (exp, 0))),
+ TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (exp, 0))));
+
+ return (TREE_OPERAND (exp, 0));
+
+ case FUNCTION_DECL:
+
+ bc_load_externaddr_id (DECL_ASSEMBLER_NAME (exp),
+ BYTECODE_BC_LABEL (DECL_RTL (exp))->offset);
+ break;
+
+ case PARM_DECL:
+
+ bc_load_parmaddr (DECL_RTL (exp));
+
+ /* For variable-sized types: retrieve pointer */
+ if (TYPE_SIZE (TREE_TYPE (exp))
+ && TREE_CODE (TYPE_SIZE (TREE_TYPE (exp))) != INTEGER_CST)
+ bc_emit_instruction (loadP);
+
+ /* If packed, also return offset and size */
+ if (DECL_BIT_FIELD (exp))
+ bc_push_offset_and_size (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (exp)),
+ TREE_INT_CST_LOW (DECL_SIZE (exp)));
+
+ break;
+
+ case RESULT_DECL:
+
+ bc_emit_instruction (returnP);
+ break;
+
+ case VAR_DECL:
+
+#if 0
+ if (BYTECODE_LABEL (DECL_RTL (exp)))
+ bc_load_externaddr (DECL_RTL (exp));
+#endif
+
+ if (DECL_EXTERNAL (exp))
+ bc_load_externaddr_id (DECL_ASSEMBLER_NAME (exp),
+ (BYTECODE_BC_LABEL (DECL_RTL (exp)))->offset);
+ else
+ bc_load_localaddr (DECL_RTL (exp));
+
+ /* For variable-sized types: retrieve pointer */
+ if (TYPE_SIZE (TREE_TYPE (exp))
+ && TREE_CODE (TYPE_SIZE (TREE_TYPE (exp))) != INTEGER_CST)
+ bc_emit_instruction (loadP);
+
+ /* If packed, also return offset and size */
+ if (DECL_BIT_FIELD (exp))
+ bc_push_offset_and_size (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (exp)),
+ TREE_INT_CST_LOW (DECL_SIZE (exp)));
+
+ break;
+
+ case STRING_CST:
+ {
+ rtx r;
+
+ bc_emit_bytecode (constP);
+ r = output_constant_def (exp);
+ bc_emit_code_labelref (BYTECODE_LABEL (r), BYTECODE_BC_LABEL (r)->offset);
+
+#ifdef DEBUG_PRINT_CODE
+ fputc ('\n', stderr);
+#endif
+ }
+ break;
+
+ default:
+
+ abort();
+ break;
+ }
+
+ /* Most lvalues don't have components. */
+ return (exp);
+}
+
+
+/* Emit a type code to be used by the runtime support in handling
+ parameter passing. The type code consists of the machine mode
+ plus the minimal alignment shifted left 8 bits. */
+
+tree
+bc_runtime_type_code (type)
+ tree type;
+{
+ int val;
+
+ switch (TREE_CODE (type))
+ {
+ case VOID_TYPE:
+ case INTEGER_TYPE:
+ case REAL_TYPE:
+ case COMPLEX_TYPE:
+ case ENUMERAL_TYPE:
+ case POINTER_TYPE:
+ case RECORD_TYPE:
+
+ val = (int) TYPE_MODE (type) | TYPE_ALIGN (type) << 8;
+ break;
+
+ case ERROR_MARK:
+
+ val = 0;
+ break;
+
+ default:
+
+ abort ();
+ }
+ return build_int_2 (val, 0);
+}
+
+
+/* Generate constructor label */
+char *
+bc_gen_constr_label ()
+{
+ static int label_counter;
+ static char label[20];
+
+ sprintf (label, "*LR%d", label_counter++);
+
+ return (obstack_copy0 (&permanent_obstack, label, strlen (label)));
+}
+
+
+/* Evaluate constructor CONSTR and return pointer to it on level one. We
+ expand the constructor data as static data, and push a pointer to it.
+ The pointer is put in the pointer table and is retrieved by a constP
+ bytecode instruction. We then loop and store each constructor member in
+ the corresponding component. Finally, we return the original pointer on
+ the stack. */
+
+void
+bc_expand_constructor (constr)
+ tree constr;
+{
+ char *l;
+ HOST_WIDE_INT ptroffs;
+ rtx constr_rtx;
+
+
+ /* Literal constructors are handled as constants, whereas
+ non-literals are evaluated and stored element by element
+ into the data segment. */
+
+ /* Allocate space in proper segment and push pointer to space on stack.
+ */
+
+ l = bc_gen_constr_label ();
+
+ if (TREE_CONSTANT (constr))
+ {
+ text_section ();
+
+ bc_emit_const_labeldef (l);
+ bc_output_constructor (constr, int_size_in_bytes (TREE_TYPE (constr)));
+ }
+ else
+ {
+ data_section ();
+
+ bc_emit_data_labeldef (l);
+ bc_output_data_constructor (constr);
+ }
+
+
+ /* Add reference to pointer table and recall pointer to stack;
+ this code is common for both types of constructors: literals
+ and non-literals. */
+
+ ptroffs = bc_define_pointer (l);
+ bc_emit_instruction (constP, ptroffs);
+
+ /* This is all that has to be done if it's a literal. */
+ if (TREE_CONSTANT (constr))
+ return;
+
+
+ /* At this point, we have the pointer to the structure on top of the stack.
+ Generate sequences of store_memory calls for the constructor. */
+
+ /* constructor type is structure */
+ if (TREE_CODE (TREE_TYPE (constr)) == RECORD_TYPE)
+ {
+ register tree elt;
+
+ /* If the constructor has fewer fields than the structure,
+ clear the whole structure first. */
+
+ if (list_length (CONSTRUCTOR_ELTS (constr))
+ != list_length (TYPE_FIELDS (TREE_TYPE (constr))))
+ {
+ bc_emit_instruction (duplicate);
+ bc_emit_instruction (constSI, (HOST_WIDE_INT) int_size_in_bytes (TREE_TYPE (constr)));
+ bc_emit_instruction (clearBLK);
+ }
+
+ /* Store each element of the constructor into the corresponding
+ field of TARGET. */
+
+ for (elt = CONSTRUCTOR_ELTS (constr); elt; elt = TREE_CHAIN (elt))
+ {
+ register tree field = TREE_PURPOSE (elt);
+ register enum machine_mode mode;
+ int bitsize;
+ int bitpos;
+ int unsignedp;
+
+ bitsize = TREE_INT_CST_LOW (DECL_SIZE (field)) /* * DECL_SIZE_UNIT (field) */;
+ mode = DECL_MODE (field);
+ unsignedp = TREE_UNSIGNED (field);
+
+ bitpos = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field));
+
+ bc_store_field (elt, bitsize, bitpos, mode, TREE_VALUE (elt), TREE_TYPE (TREE_VALUE (elt)),
+ /* The alignment of TARGET is
+ at least what its type requires. */
+ VOIDmode, 0,
+ TYPE_ALIGN (TREE_TYPE (constr)) / BITS_PER_UNIT,
+ int_size_in_bytes (TREE_TYPE (constr)));
+ }
+ }
+ else
+
+ /* Constructor type is array */
+ if (TREE_CODE (TREE_TYPE (constr)) == ARRAY_TYPE)
+ {
+ register tree elt;
+ register int i;
+ tree domain = TYPE_DOMAIN (TREE_TYPE (constr));
+ int minelt = TREE_INT_CST_LOW (TYPE_MIN_VALUE (domain));
+ int maxelt = TREE_INT_CST_LOW (TYPE_MAX_VALUE (domain));
+ tree elttype = TREE_TYPE (TREE_TYPE (constr));
+
+ /* If the constructor has fewer fields than the structure,
+ clear the whole structure first. */
+
+ if (list_length (CONSTRUCTOR_ELTS (constr)) < maxelt - minelt + 1)
+ {
+ bc_emit_instruction (duplicate);
+ bc_emit_instruction (constSI, (HOST_WIDE_INT) int_size_in_bytes (TREE_TYPE (constr)));
+ bc_emit_instruction (clearBLK);
+ }
+
+
+ /* Store each element of the constructor into the corresponding
+ element of TARGET, determined by counting the elements. */
+
+ for (elt = CONSTRUCTOR_ELTS (constr), i = 0;
+ elt;
+ elt = TREE_CHAIN (elt), i++)
+ {
+ register enum machine_mode mode;
+ int bitsize;
+ int bitpos;
+ int unsignedp;
+
+ mode = TYPE_MODE (elttype);
+ bitsize = GET_MODE_BITSIZE (mode);
+ unsignedp = TREE_UNSIGNED (elttype);
+
+ bitpos = (i * TREE_INT_CST_LOW (TYPE_SIZE (elttype))
+ /* * TYPE_SIZE_UNIT (elttype) */ );
+
+ bc_store_field (elt, bitsize, bitpos, mode,
+ TREE_VALUE (elt), TREE_TYPE (TREE_VALUE (elt)),
+ /* The alignment of TARGET is
+ at least what its type requires. */
+ VOIDmode, 0,
+ TYPE_ALIGN (TREE_TYPE (constr)) / BITS_PER_UNIT,
+ int_size_in_bytes (TREE_TYPE (constr)));
+ }
+
+ }
+}
+
+
+/* Store the value of EXP (an expression tree) into member FIELD of
+ structure at address on stack, which has type TYPE, mode MODE and
+ occupies BITSIZE bits, starting BITPOS bits from the beginning of the
+ structure.
+
+ ALIGN is the alignment that TARGET is known to have, measured in bytes.
+ TOTAL_SIZE is its size in bytes, or -1 if variable. */
+
+void
+bc_store_field (field, bitsize, bitpos, mode, exp, type,
+ value_mode, unsignedp, align, total_size)
+ int bitsize, bitpos;
+ enum machine_mode mode;
+ tree field, exp, type;
+ enum machine_mode value_mode;
+ int unsignedp;
+ int align;
+ int total_size;
+{
+
+ /* Expand expression and copy pointer */
+ bc_expand_expr (exp);
+ bc_emit_instruction (over);
+
+
+ /* If the component is a bit field, we cannot use addressing to access
+ it. Use bit-field techniques to store in it. */
+
+ if (DECL_BIT_FIELD (field))
+ {
+ bc_store_bit_field (bitpos, bitsize, unsignedp);
+ return;
+ }
+ else
+ /* Not bit field */
+ {
+ HOST_WIDE_INT offset = bitpos / BITS_PER_UNIT;
+
+ /* Advance pointer to the desired member */
+ if (offset)
+ bc_emit_instruction (addconstPSI, offset);
+
+ /* Store */
+ bc_store_memory (type, field);
+ }
+}
+
+
+/* Store SI/SU in bitfield */
+void
+bc_store_bit_field (offset, size, unsignedp)
+ int offset, size, unsignedp;
+{
+ /* Push bitfield offset and size */
+ bc_push_offset_and_size (offset, size);
+
+ /* Store */
+ bc_emit_instruction (sstoreBI);
+}
+
+
+/* Load SI/SU from bitfield */
+void
+bc_load_bit_field (offset, size, unsignedp)
+ int offset, size, unsignedp;
+{
+ /* Push bitfield offset and size */
+ bc_push_offset_and_size (offset, size);
+
+ /* Load: sign-extend if signed, else zero-extend */
+ bc_emit_instruction (unsignedp ? zxloadBI : sxloadBI);
+}
+
+
+/* Adjust interpreter stack by NLEVELS. Positive means drop NLEVELS
+ (adjust stack pointer upwards), negative means add that number of
+ levels (adjust the stack pointer downwards). Only positive values
+ normally make sense. */
+
+void
+bc_adjust_stack (nlevels)
+ int nlevels;
+{
+ switch (nlevels)
+ {
+ case 0:
+ break;
+
+ case 2:
+ bc_emit_instruction (drop);
+
+ case 1:
+ bc_emit_instruction (drop);
+ break;
+
+ default:
+
+ bc_emit_instruction (adjstackSI, (HOST_WIDE_INT) nlevels);
+ stack_depth -= nlevels;
+ }
+
+#if defined (VALIDATE_STACK_FOR_BC)
+ VALIDATE_STACK_FOR_BC ();
+#endif
+}
diff --git a/gnu/usr.bin/cc/cc_int/final.c b/gnu/usr.bin/cc/cc_int/final.c
new file mode 100644
index 0000000..74d01e9
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/final.c
@@ -0,0 +1,3069 @@
+/* Convert RTL to assembler code and output it, for GNU compiler.
+ Copyright (C) 1987, 88, 89, 92, 93, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This is the final pass of the compiler.
+ It looks at the rtl code for a function and outputs assembler code.
+
+ Call `final_start_function' to output the assembler code for function entry,
+ `final' to output assembler code for some RTL code,
+ `final_end_function' to output assembler code for function exit.
+ If a function is compiled in several pieces, each piece is
+ output separately with `final'.
+
+ Some optimizations are also done at this level.
+ Move instructions that were made unnecessary by good register allocation
+ are detected and omitted from the output. (Though most of these
+ are removed by the last jump pass.)
+
+ Instructions to set the condition codes are omitted when it can be
+ seen that the condition codes already had the desired values.
+
+ In some cases it is sufficient if the inherited condition codes
+ have related values, but this may require the following insn
+ (the one that tests the condition codes) to be modified.
+
+ The code for the function prologue and epilogue are generated
+ directly as assembler code by the macros FUNCTION_PROLOGUE and
+ FUNCTION_EPILOGUE. Those instructions never exist as rtl. */
+
+#include "config.h"
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <stdio.h>
+#include <ctype.h>
+
+#include "tree.h"
+#include "rtl.h"
+#include "regs.h"
+#include "insn-config.h"
+#include "insn-flags.h"
+#include "insn-attr.h"
+#include "insn-codes.h"
+#include "recog.h"
+#include "conditions.h"
+#include "flags.h"
+#include "real.h"
+#include "hard-reg-set.h"
+#include "defaults.h"
+#include "output.h"
+
+/* Get N_SLINE and N_SOL from stab.h if we can expect the file to exist. */
+#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO)
+#if defined (USG) || defined (NO_STAB_H)
+#include "gstab.h" /* If doing DBX on sysV, use our own stab.h. */
+#else
+#include <stab.h> /* On BSD, use the system's stab.h. */
+#endif /* not USG */
+#endif /* DBX_DEBUGGING_INFO || XCOFF_DEBUGGING_INFO */
+
+#ifdef XCOFF_DEBUGGING_INFO
+#include "xcoffout.h"
+#endif
+
+/* .stabd code for line number. */
+#ifndef N_SLINE
+#define N_SLINE 0x44
+#endif
+
+/* .stabs code for included file name. */
+#ifndef N_SOL
+#define N_SOL 0x84
+#endif
+
+#ifndef INT_TYPE_SIZE
+#define INT_TYPE_SIZE BITS_PER_WORD
+#endif
+
+/* If we aren't using cc0, CC_STATUS_INIT shouldn't exist. So define a
+ null default for it to save conditionalization later. */
+#ifndef CC_STATUS_INIT
+#define CC_STATUS_INIT
+#endif
+
+/* How to start an assembler comment. */
+#ifndef ASM_COMMENT_START
+#define ASM_COMMENT_START ";#"
+#endif
+
+/* Is the given character a logical line separator for the assembler? */
+#ifndef IS_ASM_LOGICAL_LINE_SEPARATOR
+#define IS_ASM_LOGICAL_LINE_SEPARATOR(C) ((C) == ';')
+#endif
+
+/* Nonzero means this function is a leaf function, with no function calls.
+ This variable exists to be examined in FUNCTION_PROLOGUE
+ and FUNCTION_EPILOGUE. Always zero, unless set by some action. */
+int leaf_function;
+
+/* Last insn processed by final_scan_insn. */
+static rtx debug_insn = 0;
+
+/* Line number of last NOTE. */
+static int last_linenum;
+
+/* Filename of last NOTE. */
+static char *last_filename;
+
+/* Number of basic blocks seen so far;
+ used if profile_block_flag is set. */
+static int count_basic_blocks;
+
+/* Nonzero while outputting an `asm' with operands.
+ This means that inconsistencies are the user's fault, so don't abort.
+ The precise value is the insn being output, to pass to error_for_asm. */
+static rtx this_is_asm_operands;
+
+/* Number of operands of this insn, for an `asm' with operands. */
+static int insn_noperands;
+
+/* Compare optimization flag. */
+
+static rtx last_ignored_compare = 0;
+
+/* Flag indicating this insn is the start of a new basic block. */
+
+static int new_block = 1;
+
+/* All the symbol-blocks (levels of scoping) in the compilation
+ are assigned sequence numbers in order of appearance of the
+ beginnings of the symbol-blocks. Both final and dbxout do this,
+ and assume that they will both give the same number to each block.
+ Final uses these sequence numbers to generate assembler label names
+ LBBnnn and LBEnnn for the beginning and end of the symbol-block.
+ Dbxout uses the sequence numbers to generate references to the same labels
+ from the dbx debugging information.
+
+ Sdb records this level at the beginning of each function,
+ in order to find the current level when recursing down declarations.
+ It outputs the block beginning and endings
+ at the point in the asm file where the blocks would begin and end. */
+
+int next_block_index;
+
+/* Assign a unique number to each insn that is output.
+ This can be used to generate unique local labels. */
+
+static int insn_counter = 0;
+
+#ifdef HAVE_cc0
+/* This variable contains machine-dependent flags (defined in tm.h)
+ set and examined by output routines
+ that describe how to interpret the condition codes properly. */
+
+CC_STATUS cc_status;
+
+/* During output of an insn, this contains a copy of cc_status
+ from before the insn. */
+
+CC_STATUS cc_prev_status;
+#endif
+
+/* Indexed by hardware reg number, is 1 if that register is ever
+ used in the current function.
+
+ In life_analysis, or in stupid_life_analysis, this is set
+ up to record the hard regs used explicitly. Reload adds
+ in the hard regs used for holding pseudo regs. Final uses
+ it to generate the code in the function prologue and epilogue
+ to save and restore registers as needed. */
+
+char regs_ever_live[FIRST_PSEUDO_REGISTER];
+
+/* Nonzero means current function must be given a frame pointer.
+ Set in stmt.c if anything is allocated on the stack there.
+ Set in reload1.c if anything is allocated on the stack there. */
+
+int frame_pointer_needed;
+
+/* Assign unique numbers to labels generated for profiling. */
+
+int profile_label_no;
+
+/* Length so far allocated in PENDING_BLOCKS. */
+
+static int max_block_depth;
+
+/* Stack of sequence numbers of symbol-blocks of which we have seen the
+ beginning but not yet the end. Sequence numbers are assigned at
+ the beginning; this stack allows us to find the sequence number
+ of a block that is ending. */
+
+static int *pending_blocks;
+
+/* Number of elements currently in use in PENDING_BLOCKS. */
+
+static int block_depth;
+
+/* Nonzero if have enabled APP processing of our assembler output. */
+
+static int app_on;
+
+/* If we are outputting an insn sequence, this contains the sequence rtx.
+ Zero otherwise. */
+
+rtx final_sequence;
+
+#ifdef ASSEMBLER_DIALECT
+
+/* Number of the assembler dialect to use, starting at 0. */
+static int dialect_number;
+#endif
+
+/* Indexed by line number, nonzero if there is a note for that line. */
+
+static char *line_note_exists;
+
+/* Linked list to hold line numbers for each basic block. */
+
+struct bb_list {
+ struct bb_list *next; /* pointer to next basic block */
+ int line_num; /* line number */
+ int file_label_num; /* LPBC<n> label # for stored filename */
+ int func_label_num; /* LPBC<n> label # for stored function name */
+};
+
+static struct bb_list *bb_head = 0; /* Head of basic block list */
+static struct bb_list **bb_tail = &bb_head; /* Ptr to store next bb ptr */
+static int bb_file_label_num = -1; /* Current label # for file */
+static int bb_func_label_num = -1; /* Current label # for func */
+
+/* Linked list to hold the strings for each file and function name output. */
+
+struct bb_str {
+ struct bb_str *next; /* pointer to next string */
+ char *string; /* string */
+ int label_num; /* label number */
+ int length; /* string length */
+};
+
+extern rtx peephole PROTO((rtx));
+
+static struct bb_str *sbb_head = 0; /* Head of string list. */
+static struct bb_str **sbb_tail = &sbb_head; /* Ptr to store next bb str */
+static int sbb_label_num = 0; /* Last label used */
+
+static int asm_insn_count PROTO((rtx));
+static void profile_function PROTO((FILE *));
+static void profile_after_prologue PROTO((FILE *));
+static void add_bb PROTO((FILE *));
+static int add_bb_string PROTO((char *, int));
+static void output_source_line PROTO((FILE *, rtx));
+static rtx walk_alter_subreg PROTO((rtx));
+static int alter_cond PROTO((rtx));
+static void output_operand PROTO((rtx, int));
+static void leaf_renumber_regs PROTO((rtx));
+
+extern char *getpwd ();
+
+/* Initialize data in final at the beginning of a compilation. */
+
+void
+init_final (filename)
+ char *filename;
+{
+ next_block_index = 2;
+ app_on = 0;
+ max_block_depth = 20;
+ pending_blocks = (int *) xmalloc (20 * sizeof *pending_blocks);
+ final_sequence = 0;
+
+#ifdef ASSEMBLER_DIALECT
+ dialect_number = ASSEMBLER_DIALECT;
+#endif
+}
+
+/* Called at end of source file,
+ to output the block-profiling table for this entire compilation. */
+
+void
+end_final (filename)
+ char *filename;
+{
+ int i;
+
+ if (profile_block_flag)
+ {
+ char name[20];
+ int align = exact_log2 (BIGGEST_ALIGNMENT / BITS_PER_UNIT);
+ int size = (POINTER_SIZE / BITS_PER_UNIT) * count_basic_blocks;
+ int rounded = size;
+ struct bb_list *ptr;
+ struct bb_str *sptr;
+
+ rounded += (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1;
+ rounded = (rounded / (BIGGEST_ALIGNMENT / BITS_PER_UNIT)
+ * (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
+
+ data_section ();
+
+ /* Output the main header, of 10 words:
+ 0: 1 if this file's initialized, else 0.
+ 1: address of file name (LPBX1).
+ 2: address of table of counts (LPBX2).
+ 3: number of counts in the table.
+ 4: always 0, for compatibility with Sun.
+
+ The following are GNU extensions:
+
+ 5: address of table of start addrs of basic blocks (LPBX3).
+ 6: Number of bytes in this header.
+ 7: address of table of function names (LPBX4).
+ 8: address of table of line numbers (LPBX5) or 0.
+ 9: address of table of file names (LPBX6) or 0. */
+
+ ASM_OUTPUT_ALIGN (asm_out_file, align);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 0);
+ /* zero word */
+ assemble_integer (const0_rtx, UNITS_PER_WORD, 1);
+
+ /* address of filename */
+ ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 1);
+ assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1);
+
+ /* address of count table */
+ ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 2);
+ assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1);
+
+ /* count of the # of basic blocks */
+ assemble_integer (GEN_INT (count_basic_blocks), UNITS_PER_WORD, 1);
+
+ /* zero word (link field) */
+ assemble_integer (const0_rtx, UNITS_PER_WORD, 1);
+
+ /* address of basic block start address table */
+ ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 3);
+ assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1);
+
+ /* byte count for extended structure. */
+ assemble_integer (GEN_INT (10 * UNITS_PER_WORD), UNITS_PER_WORD, 1);
+
+ /* address of function name table */
+ ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 4);
+ assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1);
+
+ /* address of line number and filename tables if debugging. */
+ if (write_symbols != NO_DEBUG)
+ {
+ ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 5);
+ assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1);
+ ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 6);
+ assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1);
+ }
+ else
+ {
+ assemble_integer (const0_rtx, UNITS_PER_WORD, 1);
+ assemble_integer (const0_rtx, UNITS_PER_WORD, 1);
+ }
+
+ /* Output the file name changing the suffix to .d for Sun tcov
+ compatibility. */
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 1);
+ {
+ char *cwd = getpwd ();
+ int len = strlen (filename) + strlen (cwd) + 1;
+ char *data_file = (char *) alloca (len + 4);
+
+ strcpy (data_file, cwd);
+ strcat (data_file, "/");
+ strcat (data_file, filename);
+ strip_off_ending (data_file, len);
+ strcat (data_file, ".d");
+ assemble_string (data_file, strlen (data_file) + 1);
+ }
+
+ /* Make space for the table of counts. */
+ if (flag_no_common || size == 0)
+ {
+ /* Realign data section. */
+ ASM_OUTPUT_ALIGN (asm_out_file, align);
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 2);
+ if (size != 0)
+ assemble_zeros (size);
+ }
+ else
+ {
+ ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 2);
+#ifdef ASM_OUTPUT_SHARED_LOCAL
+ if (flag_shared_data)
+ ASM_OUTPUT_SHARED_LOCAL (asm_out_file, name, size, rounded);
+ else
+#endif
+#ifdef ASM_OUTPUT_ALIGNED_LOCAL
+ ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size,
+ BIGGEST_ALIGNMENT);
+#else
+ ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded);
+#endif
+ }
+
+ /* Output any basic block strings */
+ readonly_data_section ();
+ if (sbb_head)
+ {
+ ASM_OUTPUT_ALIGN (asm_out_file, align);
+ for (sptr = sbb_head; sptr != 0; sptr = sptr->next)
+ {
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBC", sptr->label_num);
+ assemble_string (sptr->string, sptr->length);
+ }
+ }
+
+ /* Output the table of addresses. */
+ /* Realign in new section */
+ ASM_OUTPUT_ALIGN (asm_out_file, align);
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 3);
+ for (i = 0; i < count_basic_blocks; i++)
+ {
+ ASM_GENERATE_INTERNAL_LABEL (name, "LPB", i);
+ assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name),
+ UNITS_PER_WORD, 1);
+ }
+
+ /* Output the table of function names. */
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 4);
+ for ((ptr = bb_head), (i = 0); ptr != 0; (ptr = ptr->next), i++)
+ {
+ if (ptr->func_label_num >= 0)
+ {
+ ASM_GENERATE_INTERNAL_LABEL (name, "LPBC", ptr->func_label_num);
+ assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name),
+ UNITS_PER_WORD, 1);
+ }
+ else
+ assemble_integer (const0_rtx, UNITS_PER_WORD, 1);
+ }
+
+ for ( ; i < count_basic_blocks; i++)
+ assemble_integer (const0_rtx, UNITS_PER_WORD, 1);
+
+ if (write_symbols != NO_DEBUG)
+ {
+ /* Output the table of line numbers. */
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 5);
+ for ((ptr = bb_head), (i = 0); ptr != 0; (ptr = ptr->next), i++)
+ assemble_integer (GEN_INT (ptr->line_num), UNITS_PER_WORD, 1);
+
+ for ( ; i < count_basic_blocks; i++)
+ assemble_integer (const0_rtx, UNITS_PER_WORD, 1);
+
+ /* Output the table of file names. */
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 6);
+ for ((ptr = bb_head), (i = 0); ptr != 0; (ptr = ptr->next), i++)
+ {
+ if (ptr->file_label_num >= 0)
+ {
+ ASM_GENERATE_INTERNAL_LABEL (name, "LPBC", ptr->file_label_num);
+ assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name),
+ UNITS_PER_WORD, 1);
+ }
+ else
+ assemble_integer (const0_rtx, UNITS_PER_WORD, 1);
+ }
+
+ for ( ; i < count_basic_blocks; i++)
+ assemble_integer (const0_rtx, UNITS_PER_WORD, 1);
+ }
+
+ /* End with the address of the table of addresses,
+ so we can find it easily, as the last word in the file's text. */
+ ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 3);
+ assemble_integer (gen_rtx (SYMBOL_REF, Pmode, name), UNITS_PER_WORD, 1);
+ }
+}
+
+/* Enable APP processing of subsequent output.
+ Used before the output from an `asm' statement. */
+
+void
+app_enable ()
+{
+ if (! app_on)
+ {
+ fprintf (asm_out_file, ASM_APP_ON);
+ app_on = 1;
+ }
+}
+
+/* Disable APP processing of subsequent output.
+ Called from varasm.c before most kinds of output. */
+
+void
+app_disable ()
+{
+ if (app_on)
+ {
+ fprintf (asm_out_file, ASM_APP_OFF);
+ app_on = 0;
+ }
+}
+
+/* Return the number of slots filled in the current
+ delayed branch sequence (we don't count the insn needing the
+ delay slot). Zero if not in a delayed branch sequence. */
+
+#ifdef DELAY_SLOTS
+int
+dbr_sequence_length ()
+{
+ if (final_sequence != 0)
+ return XVECLEN (final_sequence, 0) - 1;
+ else
+ return 0;
+}
+#endif
+
+/* The next two pages contain routines used to compute the length of an insn
+ and to shorten branches. */
+
+/* Arrays for insn lengths, and addresses. The latter is referenced by
+ `insn_current_length'. */
+
+static short *insn_lengths;
+int *insn_addresses;
+
+/* Address of insn being processed. Used by `insn_current_length'. */
+int insn_current_address;
+
+/* Indicate that branch shortening hasn't yet been done. */
+
+void
+init_insn_lengths ()
+{
+ insn_lengths = 0;
+}
+
+/* Obtain the current length of an insn. If branch shortening has been done,
+ get its actual length. Otherwise, get its maximum length. */
+
+int
+get_attr_length (insn)
+ rtx insn;
+{
+#ifdef HAVE_ATTR_length
+ rtx body;
+ int i;
+ int length = 0;
+
+ if (insn_lengths)
+ return insn_lengths[INSN_UID (insn)];
+ else
+ switch (GET_CODE (insn))
+ {
+ case NOTE:
+ case BARRIER:
+ case CODE_LABEL:
+ return 0;
+
+ case CALL_INSN:
+ length = insn_default_length (insn);
+ break;
+
+ case JUMP_INSN:
+ body = PATTERN (insn);
+ if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC)
+ {
+ /* This only takes room if jump tables go into the text section. */
+#if !defined(READONLY_DATA_SECTION) || defined(JUMP_TABLES_IN_TEXT_SECTION)
+ length = (XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC)
+ * GET_MODE_SIZE (GET_MODE (body)));
+
+ /* Be pessimistic and assume worst-case alignment. */
+ length += (GET_MODE_SIZE (GET_MODE (body)) - 1);
+#else
+ return 0;
+#endif
+ }
+ else
+ length = insn_default_length (insn);
+ break;
+
+ case INSN:
+ body = PATTERN (insn);
+ if (GET_CODE (body) == USE || GET_CODE (body) == CLOBBER)
+ return 0;
+
+ else if (GET_CODE (body) == ASM_INPUT || asm_noperands (body) >= 0)
+ length = asm_insn_count (body) * insn_default_length (insn);
+ else if (GET_CODE (body) == SEQUENCE)
+ for (i = 0; i < XVECLEN (body, 0); i++)
+ length += get_attr_length (XVECEXP (body, 0, i));
+ else
+ length = insn_default_length (insn);
+ }
+
+#ifdef ADJUST_INSN_LENGTH
+ ADJUST_INSN_LENGTH (insn, length);
+#endif
+ return length;
+#else /* not HAVE_ATTR_length */
+ return 0;
+#endif /* not HAVE_ATTR_length */
+}
+
+/* Make a pass over all insns and compute their actual lengths by shortening
+ any branches of variable length if possible. */
+
+/* Give a default value for the lowest address in a function. */
+
+#ifndef FIRST_INSN_ADDRESS
+#define FIRST_INSN_ADDRESS 0
+#endif
+
+void
+shorten_branches (first)
+ rtx first;
+{
+#ifdef HAVE_ATTR_length
+ rtx insn;
+ int something_changed = 1;
+ int max_uid = 0;
+ char *varying_length;
+ rtx body;
+ int uid;
+
+ /* Compute maximum UID and allocate arrays. */
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ if (INSN_UID (insn) > max_uid)
+ max_uid = INSN_UID (insn);
+
+ max_uid++;
+ insn_lengths = (short *) oballoc (max_uid * sizeof (short));
+ insn_addresses = (int *) oballoc (max_uid * sizeof (int));
+ varying_length = (char *) oballoc (max_uid * sizeof (char));
+
+ /* Compute initial lengths, addresses, and varying flags for each insn. */
+ for (insn_current_address = FIRST_INSN_ADDRESS, insn = first;
+ insn != 0;
+ insn_current_address += insn_lengths[uid], insn = NEXT_INSN (insn))
+ {
+ uid = INSN_UID (insn);
+ insn_addresses[uid] = insn_current_address;
+ insn_lengths[uid] = 0;
+ varying_length[uid] = 0;
+
+ if (GET_CODE (insn) == NOTE || GET_CODE (insn) == BARRIER
+ || GET_CODE (insn) == CODE_LABEL)
+ continue;
+
+ body = PATTERN (insn);
+ if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC)
+ {
+ /* This only takes room if read-only data goes into the text
+ section. */
+#if !defined(READONLY_DATA_SECTION) || defined(JUMP_TABLES_IN_TEXT_SECTION)
+ int unitsize = GET_MODE_SIZE (GET_MODE (body));
+
+ insn_lengths[uid] = (XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC)
+ * GET_MODE_SIZE (GET_MODE (body)));
+
+ /* Account for possible alignment. */
+ insn_lengths[uid]
+ += unitsize - (insn_current_address & (unitsize - 1));
+#else
+ ;
+#endif
+ }
+ else if (asm_noperands (body) >= 0)
+ insn_lengths[uid] = asm_insn_count (body) * insn_default_length (insn);
+ else if (GET_CODE (body) == SEQUENCE)
+ {
+ int i;
+ int const_delay_slots;
+#ifdef DELAY_SLOTS
+ const_delay_slots = const_num_delay_slots (XVECEXP (body, 0, 0));
+#else
+ const_delay_slots = 0;
+#endif
+ /* Inside a delay slot sequence, we do not do any branch shortening
+ if the shortening could change the number of delay slots
+ of the branch. */
+ for (i = 0; i < XVECLEN (body, 0); i++)
+ {
+ rtx inner_insn = XVECEXP (body, 0, i);
+ int inner_uid = INSN_UID (inner_insn);
+ int inner_length;
+
+ if (asm_noperands (PATTERN (XVECEXP (body, 0, i))) >= 0)
+ inner_length = (asm_insn_count (PATTERN (inner_insn))
+ * insn_default_length (inner_insn));
+ else
+ inner_length = insn_default_length (inner_insn);
+
+ insn_lengths[inner_uid] = inner_length;
+ if (const_delay_slots)
+ {
+ if ((varying_length[inner_uid]
+ = insn_variable_length_p (inner_insn)) != 0)
+ varying_length[uid] = 1;
+ insn_addresses[inner_uid] = (insn_current_address +
+ insn_lengths[uid]);
+ }
+ else
+ varying_length[inner_uid] = 0;
+ insn_lengths[uid] += inner_length;
+ }
+ }
+ else if (GET_CODE (body) != USE && GET_CODE (body) != CLOBBER)
+ {
+ insn_lengths[uid] = insn_default_length (insn);
+ varying_length[uid] = insn_variable_length_p (insn);
+ }
+
+ /* If needed, do any adjustment. */
+#ifdef ADJUST_INSN_LENGTH
+ ADJUST_INSN_LENGTH (insn, insn_lengths[uid]);
+#endif
+ }
+
+ /* Now loop over all the insns finding varying length insns. For each,
+ get the current insn length. If it has changed, reflect the change.
+ When nothing changes for a full pass, we are done. */
+
+ while (something_changed)
+ {
+ something_changed = 0;
+ for (insn_current_address = FIRST_INSN_ADDRESS, insn = first;
+ insn != 0;
+ insn = NEXT_INSN (insn))
+ {
+ int new_length;
+ int tmp_length;
+
+ uid = INSN_UID (insn);
+ insn_addresses[uid] = insn_current_address;
+ if (! varying_length[uid])
+ {
+ insn_current_address += insn_lengths[uid];
+ continue;
+ }
+ if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE)
+ {
+ int i;
+
+ body = PATTERN (insn);
+ new_length = 0;
+ for (i = 0; i < XVECLEN (body, 0); i++)
+ {
+ rtx inner_insn = XVECEXP (body, 0, i);
+ int inner_uid = INSN_UID (inner_insn);
+ int inner_length;
+
+ insn_addresses[inner_uid] = insn_current_address;
+
+ /* insn_current_length returns 0 for insns with a
+ non-varying length. */
+ if (! varying_length[inner_uid])
+ inner_length = insn_lengths[inner_uid];
+ else
+ inner_length = insn_current_length (inner_insn);
+
+ if (inner_length != insn_lengths[inner_uid])
+ {
+ insn_lengths[inner_uid] = inner_length;
+ something_changed = 1;
+ }
+ insn_current_address += insn_lengths[inner_uid];
+ new_length += inner_length;
+ }
+ }
+ else
+ {
+ new_length = insn_current_length (insn);
+ insn_current_address += new_length;
+ }
+
+#ifdef SHORTEN_WITH_ADJUST_INSN_LENGTH
+#ifdef ADJUST_INSN_LENGTH
+ /* If needed, do any adjustment. */
+ tmp_length = new_length;
+ ADJUST_INSN_LENGTH (insn, new_length);
+ insn_current_address += (new_length - tmp_length);
+#endif
+#endif
+
+ if (new_length != insn_lengths[uid])
+ {
+ insn_lengths[uid] = new_length;
+ something_changed = 1;
+ }
+ }
+ }
+#endif /* HAVE_ATTR_length */
+}
+
+#ifdef HAVE_ATTR_length
+/* Given the body of an INSN known to be generated by an ASM statement, return
+ the number of machine instructions likely to be generated for this insn.
+ This is used to compute its length. */
+
+static int
+asm_insn_count (body)
+ rtx body;
+{
+ char *template;
+ int count = 1;
+
+ for (template = decode_asm_operands (body, NULL_PTR, NULL_PTR,
+ NULL_PTR, NULL_PTR);
+ *template; template++)
+ if (IS_ASM_LOGICAL_LINE_SEPARATOR(*template) || *template == '\n')
+ count++;
+
+ return count;
+}
+#endif
+
+/* Output assembler code for the start of a function,
+ and initialize some of the variables in this file
+ for the new function. The label for the function and associated
+ assembler pseudo-ops have already been output in `assemble_start_function'.
+
+ FIRST is the first insn of the rtl for the function being compiled.
+ FILE is the file to write assembler code to.
+ OPTIMIZE is nonzero if we should eliminate redundant
+ test and compare insns. */
+
+void
+final_start_function (first, file, optimize)
+ rtx first;
+ FILE *file;
+ int optimize;
+{
+ block_depth = 0;
+
+ this_is_asm_operands = 0;
+
+#ifdef NON_SAVING_SETJMP
+ /* A function that calls setjmp should save and restore all the
+ call-saved registers on a system where longjmp clobbers them. */
+ if (NON_SAVING_SETJMP && current_function_calls_setjmp)
+ {
+ int i;
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (!call_used_regs[i] && !call_fixed_regs[i])
+ regs_ever_live[i] = 1;
+ }
+#endif
+
+ /* Initial line number is supposed to be output
+ before the function's prologue and label
+ so that the function's address will not appear to be
+ in the last statement of the preceding function. */
+ if (NOTE_LINE_NUMBER (first) != NOTE_INSN_DELETED)
+ {
+ if (write_symbols == SDB_DEBUG)
+ /* For sdb, let's not, but say we did.
+ We need to set last_linenum for sdbout_function_begin,
+ but we can't have an actual line number before the .bf symbol.
+ (sdb_begin_function_line is not set,
+ and other compilers don't do it.) */
+ last_linenum = NOTE_LINE_NUMBER (first);
+#ifdef XCOFF_DEBUGGING_INFO
+ else if (write_symbols == XCOFF_DEBUG)
+ {
+ last_linenum = NOTE_LINE_NUMBER (first);
+ xcoffout_output_first_source_line (file, last_linenum);
+ }
+#endif
+ else
+ output_source_line (file, first);
+ }
+
+#ifdef LEAF_REG_REMAP
+ if (leaf_function)
+ leaf_renumber_regs (first);
+#endif
+
+ /* The Sun386i and perhaps other machines don't work right
+ if the profiling code comes after the prologue. */
+#ifdef PROFILE_BEFORE_PROLOGUE
+ if (profile_flag)
+ profile_function (file);
+#endif /* PROFILE_BEFORE_PROLOGUE */
+
+#ifdef FUNCTION_PROLOGUE
+ /* First output the function prologue: code to set up the stack frame. */
+ FUNCTION_PROLOGUE (file, get_frame_size ());
+#endif
+
+#if defined (SDB_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO)
+ if (write_symbols == SDB_DEBUG || write_symbols == XCOFF_DEBUG)
+ next_block_index = 1;
+#endif
+
+ /* If the machine represents the prologue as RTL, the profiling code must
+ be emitted when NOTE_INSN_PROLOGUE_END is scanned. */
+#ifdef HAVE_prologue
+ if (! HAVE_prologue)
+#endif
+ profile_after_prologue (file);
+
+ profile_label_no++;
+
+ /* If we are doing basic block profiling, remember a printable version
+ of the function name. */
+ if (profile_block_flag)
+ {
+ char *junk = "function";
+ bb_func_label_num =
+ add_bb_string ((*decl_printable_name) (current_function_decl, &junk), FALSE);
+ }
+}
+
+static void
+profile_after_prologue (file)
+ FILE *file;
+{
+#ifdef FUNCTION_BLOCK_PROFILER
+ if (profile_block_flag)
+ {
+ FUNCTION_BLOCK_PROFILER (file, profile_label_no);
+ }
+#endif /* FUNCTION_BLOCK_PROFILER */
+
+#ifndef PROFILE_BEFORE_PROLOGUE
+ if (profile_flag)
+ profile_function (file);
+#endif /* not PROFILE_BEFORE_PROLOGUE */
+}
+
+static void
+profile_function (file)
+ FILE *file;
+{
+#ifndef NO_PROFILE_DATA
+ int align = MIN (BIGGEST_ALIGNMENT, POINTER_SIZE);
+#endif /* not NO_PROFILE_DATA */
+ int sval = current_function_returns_struct;
+ int cxt = current_function_needs_context;
+
+#ifndef NO_PROFILE_DATA
+ data_section ();
+ ASM_OUTPUT_ALIGN (file, floor_log2 (align / BITS_PER_UNIT));
+ ASM_OUTPUT_INTERNAL_LABEL (file, "LP", profile_label_no);
+ assemble_integer (const0_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
+#endif /* not NO_PROFILE_DATA */
+
+ text_section ();
+
+#ifdef STRUCT_VALUE_INCOMING_REGNUM
+ if (sval)
+ ASM_OUTPUT_REG_PUSH (file, STRUCT_VALUE_INCOMING_REGNUM);
+#else
+#ifdef STRUCT_VALUE_REGNUM
+ if (sval)
+ ASM_OUTPUT_REG_PUSH (file, STRUCT_VALUE_REGNUM);
+#endif
+#endif
+
+#if 0
+#ifdef STATIC_CHAIN_INCOMING_REGNUM
+ if (cxt)
+ ASM_OUTPUT_REG_PUSH (file, STATIC_CHAIN_INCOMING_REGNUM);
+#else
+#ifdef STATIC_CHAIN_REGNUM
+ if (cxt)
+ ASM_OUTPUT_REG_PUSH (file, STATIC_CHAIN_REGNUM);
+#endif
+#endif
+#endif /* 0 */
+
+ FUNCTION_PROFILER (file, profile_label_no);
+
+#if 0
+#ifdef STATIC_CHAIN_INCOMING_REGNUM
+ if (cxt)
+ ASM_OUTPUT_REG_POP (file, STATIC_CHAIN_INCOMING_REGNUM);
+#else
+#ifdef STATIC_CHAIN_REGNUM
+ if (cxt)
+ ASM_OUTPUT_REG_POP (file, STATIC_CHAIN_REGNUM);
+#endif
+#endif
+#endif /* 0 */
+
+#ifdef STRUCT_VALUE_INCOMING_REGNUM
+ if (sval)
+ ASM_OUTPUT_REG_POP (file, STRUCT_VALUE_INCOMING_REGNUM);
+#else
+#ifdef STRUCT_VALUE_REGNUM
+ if (sval)
+ ASM_OUTPUT_REG_POP (file, STRUCT_VALUE_REGNUM);
+#endif
+#endif
+}
+
+/* Output assembler code for the end of a function.
+ For clarity, args are same as those of `final_start_function'
+ even though not all of them are needed. */
+
+void
+final_end_function (first, file, optimize)
+ rtx first;
+ FILE *file;
+ int optimize;
+{
+ if (app_on)
+ {
+ fprintf (file, ASM_APP_OFF);
+ app_on = 0;
+ }
+
+#ifdef SDB_DEBUGGING_INFO
+ if (write_symbols == SDB_DEBUG)
+ sdbout_end_function (last_linenum);
+#endif
+
+#ifdef DWARF_DEBUGGING_INFO
+ if (write_symbols == DWARF_DEBUG)
+ dwarfout_end_function ();
+#endif
+
+#ifdef XCOFF_DEBUGGING_INFO
+ if (write_symbols == XCOFF_DEBUG)
+ xcoffout_end_function (file, last_linenum);
+#endif
+
+#ifdef FUNCTION_EPILOGUE
+ /* Finally, output the function epilogue:
+ code to restore the stack frame and return to the caller. */
+ FUNCTION_EPILOGUE (file, get_frame_size ());
+#endif
+
+#ifdef SDB_DEBUGGING_INFO
+ if (write_symbols == SDB_DEBUG)
+ sdbout_end_epilogue ();
+#endif
+
+#ifdef DWARF_DEBUGGING_INFO
+ if (write_symbols == DWARF_DEBUG)
+ dwarfout_end_epilogue ();
+#endif
+
+#ifdef XCOFF_DEBUGGING_INFO
+ if (write_symbols == XCOFF_DEBUG)
+ xcoffout_end_epilogue (file);
+#endif
+
+ bb_func_label_num = -1; /* not in function, nuke label # */
+
+ /* If FUNCTION_EPILOGUE is not defined, then the function body
+ itself contains return instructions wherever needed. */
+}
+
+/* Add a block to the linked list that remembers the current line/file/function
+ for basic block profiling. Emit the label in front of the basic block and
+ the instructions that increment the count field. */
+
+static void
+add_bb (file)
+ FILE *file;
+{
+ struct bb_list *ptr = (struct bb_list *) permalloc (sizeof (struct bb_list));
+
+ /* Add basic block to linked list. */
+ ptr->next = 0;
+ ptr->line_num = last_linenum;
+ ptr->file_label_num = bb_file_label_num;
+ ptr->func_label_num = bb_func_label_num;
+ *bb_tail = ptr;
+ bb_tail = &ptr->next;
+
+ /* Enable the table of basic-block use counts
+ to point at the code it applies to. */
+ ASM_OUTPUT_INTERNAL_LABEL (file, "LPB", count_basic_blocks);
+
+ /* Before first insn of this basic block, increment the
+ count of times it was entered. */
+#ifdef BLOCK_PROFILER
+ BLOCK_PROFILER (file, count_basic_blocks);
+ CC_STATUS_INIT;
+#endif
+
+ new_block = 0;
+ count_basic_blocks++;
+}
+
+/* Add a string to be used for basic block profiling. */
+
+static int
+add_bb_string (string, perm_p)
+ char *string;
+ int perm_p;
+{
+ int len;
+ struct bb_str *ptr = 0;
+
+ if (!string)
+ {
+ string = "<unknown>";
+ perm_p = TRUE;
+ }
+
+ /* Allocate a new string if the current string isn't permanent. If
+ the string is permanent search for the same string in other
+ allocations. */
+
+ len = strlen (string) + 1;
+ if (!perm_p)
+ {
+ char *p = (char *) permalloc (len);
+ bcopy (string, p, len);
+ string = p;
+ }
+ else
+ for (ptr = sbb_head; ptr != (struct bb_str *)0; ptr = ptr->next)
+ if (ptr->string == string)
+ break;
+
+ /* Allocate a new string block if we need to. */
+ if (!ptr)
+ {
+ ptr = (struct bb_str *) permalloc (sizeof (*ptr));
+ ptr->next = 0;
+ ptr->length = len;
+ ptr->label_num = sbb_label_num++;
+ ptr->string = string;
+ *sbb_tail = ptr;
+ sbb_tail = &ptr->next;
+ }
+
+ return ptr->label_num;
+}
+
+
+/* Output assembler code for some insns: all or part of a function.
+ For description of args, see `final_start_function', above.
+
+ PRESCAN is 1 if we are not really outputting,
+ just scanning as if we were outputting.
+ Prescanning deletes and rearranges insns just like ordinary output.
+ PRESCAN is -2 if we are outputting after having prescanned.
+ In this case, don't try to delete or rearrange insns
+ because that has already been done.
+ Prescanning is done only on certain machines. */
+
+void
+final (first, file, optimize, prescan)
+ rtx first;
+ FILE *file;
+ int optimize;
+ int prescan;
+{
+ register rtx insn;
+ int max_line = 0;
+
+ last_ignored_compare = 0;
+ new_block = 1;
+
+ /* Make a map indicating which line numbers appear in this function.
+ When producing SDB debugging info, delete troublesome line number
+ notes from inlined functions in other files as well as duplicate
+ line number notes. */
+#ifdef SDB_DEBUGGING_INFO
+ if (write_symbols == SDB_DEBUG)
+ {
+ rtx last = 0;
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
+ {
+ if ((RTX_INTEGRATED_P (insn)
+ && strcmp (NOTE_SOURCE_FILE (insn), main_input_filename) != 0)
+ || (last != 0
+ && NOTE_LINE_NUMBER (insn) == NOTE_LINE_NUMBER (last)
+ && NOTE_SOURCE_FILE (insn) == NOTE_SOURCE_FILE (last)))
+ {
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ continue;
+ }
+ last = insn;
+ if (NOTE_LINE_NUMBER (insn) > max_line)
+ max_line = NOTE_LINE_NUMBER (insn);
+ }
+ }
+ else
+#endif
+ {
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > max_line)
+ max_line = NOTE_LINE_NUMBER (insn);
+ }
+
+ line_note_exists = (char *) oballoc (max_line + 1);
+ bzero (line_note_exists, max_line + 1);
+
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
+ line_note_exists[NOTE_LINE_NUMBER (insn)] = 1;
+
+ init_recog ();
+
+ CC_STATUS_INIT;
+
+ /* Output the insns. */
+ for (insn = NEXT_INSN (first); insn;)
+ insn = final_scan_insn (insn, file, optimize, prescan, 0);
+
+ /* Do basic-block profiling here
+ if the last insn was a conditional branch. */
+ if (profile_block_flag && new_block)
+ add_bb (file);
+}
+
+/* The final scan for one insn, INSN.
+ Args are same as in `final', except that INSN
+ is the insn being scanned.
+ Value returned is the next insn to be scanned.
+
+ NOPEEPHOLES is the flag to disallow peephole processing (currently
+ used for within delayed branch sequence output). */
+
+rtx
+final_scan_insn (insn, file, optimize, prescan, nopeepholes)
+ rtx insn;
+ FILE *file;
+ int optimize;
+ int prescan;
+ int nopeepholes;
+{
+ register int i;
+ insn_counter++;
+
+ /* Ignore deleted insns. These can occur when we split insns (due to a
+ template of "#") while not optimizing. */
+ if (INSN_DELETED_P (insn))
+ return NEXT_INSN (insn);
+
+ switch (GET_CODE (insn))
+ {
+ case NOTE:
+ if (prescan > 0)
+ break;
+
+ /* Align the beginning of a loop, for higher speed
+ on certain machines. */
+
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG && optimize > 0)
+ {
+#ifdef ASM_OUTPUT_LOOP_ALIGN
+ rtx next = next_nonnote_insn (insn);
+ if (next && GET_CODE (next) == CODE_LABEL)
+ {
+ ASM_OUTPUT_LOOP_ALIGN (asm_out_file);
+ }
+#endif
+ break;
+ }
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
+ break;
+
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_PROLOGUE_END)
+ {
+#ifdef FUNCTION_END_PROLOGUE
+ FUNCTION_END_PROLOGUE (file);
+#endif
+ profile_after_prologue (file);
+ break;
+ }
+
+#ifdef FUNCTION_BEGIN_EPILOGUE
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EPILOGUE_BEG)
+ {
+ FUNCTION_BEGIN_EPILOGUE (file);
+ break;
+ }
+#endif
+
+ if (write_symbols == NO_DEBUG)
+ break;
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG)
+ {
+#ifdef SDB_DEBUGGING_INFO
+ if (write_symbols == SDB_DEBUG)
+ sdbout_begin_function (last_linenum);
+#endif
+#ifdef XCOFF_DEBUGGING_INFO
+ if (write_symbols == XCOFF_DEBUG)
+ xcoffout_begin_function (file, last_linenum);
+#endif
+#ifdef DWARF_DEBUGGING_INFO
+ if (write_symbols == DWARF_DEBUG)
+ dwarfout_begin_function ();
+#endif
+ break;
+ }
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED)
+ break; /* An insn that was "deleted" */
+ if (app_on)
+ {
+ fprintf (file, ASM_APP_OFF);
+ app_on = 0;
+ }
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG
+ && (debug_info_level == DINFO_LEVEL_NORMAL
+ || debug_info_level == DINFO_LEVEL_VERBOSE
+#ifdef DWARF_DEBUGGING_INFO
+ || write_symbols == DWARF_DEBUG
+#endif
+ )
+ )
+ {
+ /* Beginning of a symbol-block. Assign it a sequence number
+ and push the number onto the stack PENDING_BLOCKS. */
+
+ if (block_depth == max_block_depth)
+ {
+ /* PENDING_BLOCKS is full; make it longer. */
+ max_block_depth *= 2;
+ pending_blocks
+ = (int *) xrealloc (pending_blocks,
+ max_block_depth * sizeof (int));
+ }
+ pending_blocks[block_depth++] = next_block_index;
+
+ /* Output debugging info about the symbol-block beginning. */
+
+#ifdef SDB_DEBUGGING_INFO
+ if (write_symbols == SDB_DEBUG)
+ sdbout_begin_block (file, last_linenum, next_block_index);
+#endif
+#ifdef XCOFF_DEBUGGING_INFO
+ if (write_symbols == XCOFF_DEBUG)
+ xcoffout_begin_block (file, last_linenum, next_block_index);
+#endif
+#ifdef DBX_DEBUGGING_INFO
+ if (write_symbols == DBX_DEBUG)
+ ASM_OUTPUT_INTERNAL_LABEL (file, "LBB", next_block_index);
+#endif
+#ifdef DWARF_DEBUGGING_INFO
+ if (write_symbols == DWARF_DEBUG && block_depth > 1)
+ dwarfout_begin_block (next_block_index);
+#endif
+
+ next_block_index++;
+ }
+ else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END
+ && (debug_info_level == DINFO_LEVEL_NORMAL
+ || debug_info_level == DINFO_LEVEL_VERBOSE
+#ifdef DWARF_DEBUGGING_INFO
+ || write_symbols == DWARF_DEBUG
+#endif
+ )
+ )
+ {
+ /* End of a symbol-block. Pop its sequence number off
+ PENDING_BLOCKS and output debugging info based on that. */
+
+ --block_depth;
+
+#ifdef XCOFF_DEBUGGING_INFO
+ if (write_symbols == XCOFF_DEBUG && block_depth >= 0)
+ xcoffout_end_block (file, last_linenum, pending_blocks[block_depth]);
+#endif
+#ifdef DBX_DEBUGGING_INFO
+ if (write_symbols == DBX_DEBUG && block_depth >= 0)
+ ASM_OUTPUT_INTERNAL_LABEL (file, "LBE",
+ pending_blocks[block_depth]);
+#endif
+#ifdef SDB_DEBUGGING_INFO
+ if (write_symbols == SDB_DEBUG && block_depth >= 0)
+ sdbout_end_block (file, last_linenum, pending_blocks[block_depth]);
+#endif
+#ifdef DWARF_DEBUGGING_INFO
+ if (write_symbols == DWARF_DEBUG && block_depth >= 1)
+ dwarfout_end_block (pending_blocks[block_depth]);
+#endif
+ }
+ else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED_LABEL
+ && (debug_info_level == DINFO_LEVEL_NORMAL
+ || debug_info_level == DINFO_LEVEL_VERBOSE))
+ {
+#ifdef DWARF_DEBUGGING_INFO
+ if (write_symbols == DWARF_DEBUG)
+ dwarfout_label (insn);
+#endif
+ }
+ else if (NOTE_LINE_NUMBER (insn) > 0)
+ /* This note is a line-number. */
+ {
+ register rtx note;
+
+#if 0 /* This is what we used to do. */
+ output_source_line (file, insn);
+#endif
+ int note_after = 0;
+
+ /* If there is anything real after this note,
+ output it. If another line note follows, omit this one. */
+ for (note = NEXT_INSN (insn); note; note = NEXT_INSN (note))
+ {
+ if (GET_CODE (note) != NOTE && GET_CODE (note) != CODE_LABEL)
+ break;
+ /* These types of notes can be significant
+ so make sure the preceding line number stays. */
+ else if (GET_CODE (note) == NOTE
+ && (NOTE_LINE_NUMBER (note) == NOTE_INSN_BLOCK_BEG
+ || NOTE_LINE_NUMBER (note) == NOTE_INSN_BLOCK_END
+ || NOTE_LINE_NUMBER (note) == NOTE_INSN_FUNCTION_BEG))
+ break;
+ else if (GET_CODE (note) == NOTE && NOTE_LINE_NUMBER (note) > 0)
+ {
+ /* Another line note follows; we can delete this note
+ if no intervening line numbers have notes elsewhere. */
+ int num;
+ for (num = NOTE_LINE_NUMBER (insn) + 1;
+ num < NOTE_LINE_NUMBER (note);
+ num++)
+ if (line_note_exists[num])
+ break;
+
+ if (num >= NOTE_LINE_NUMBER (note))
+ note_after = 1;
+ break;
+ }
+ }
+
+ /* Output this line note
+ if it is the first or the last line note in a row. */
+ if (!note_after)
+ output_source_line (file, insn);
+ }
+ break;
+
+ case BARRIER:
+#ifdef ASM_OUTPUT_ALIGN_CODE
+ /* Don't litter the assembler output with needless alignments. A
+ BARRIER will be placed at the end of every function if HAVE_epilogue
+ is true. */
+ if (NEXT_INSN (insn))
+ ASM_OUTPUT_ALIGN_CODE (file);
+#endif
+ break;
+
+ case CODE_LABEL:
+ CC_STATUS_INIT;
+ if (prescan > 0)
+ break;
+ new_block = 1;
+#ifdef SDB_DEBUGGING_INFO
+ if (write_symbols == SDB_DEBUG && LABEL_NAME (insn))
+ sdbout_label (insn);
+#endif
+#ifdef DWARF_DEBUGGING_INFO
+ if (write_symbols == DWARF_DEBUG && LABEL_NAME (insn))
+ dwarfout_label (insn);
+#endif
+ if (app_on)
+ {
+ fprintf (file, ASM_APP_OFF);
+ app_on = 0;
+ }
+ if (NEXT_INSN (insn) != 0
+ && GET_CODE (NEXT_INSN (insn)) == JUMP_INSN)
+ {
+ rtx nextbody = PATTERN (NEXT_INSN (insn));
+
+ /* If this label is followed by a jump-table,
+ make sure we put the label in the read-only section. Also
+ possibly write the label and jump table together. */
+
+ if (GET_CODE (nextbody) == ADDR_VEC
+ || GET_CODE (nextbody) == ADDR_DIFF_VEC)
+ {
+#ifndef JUMP_TABLES_IN_TEXT_SECTION
+ readonly_data_section ();
+#ifdef READONLY_DATA_SECTION
+ ASM_OUTPUT_ALIGN (file,
+ exact_log2 (BIGGEST_ALIGNMENT
+ / BITS_PER_UNIT));
+#endif /* READONLY_DATA_SECTION */
+#else /* JUMP_TABLES_IN_TEXT_SECTION */
+ text_section ();
+#endif /* JUMP_TABLES_IN_TEXT_SECTION */
+#ifdef ASM_OUTPUT_CASE_LABEL
+ ASM_OUTPUT_CASE_LABEL (file, "L", CODE_LABEL_NUMBER (insn),
+ NEXT_INSN (insn));
+#else
+ ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (insn));
+#endif
+ break;
+ }
+ }
+
+ ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (insn));
+ break;
+
+ default:
+ {
+ register rtx body = PATTERN (insn);
+ int insn_code_number;
+ char *template;
+ rtx note;
+
+ /* An INSN, JUMP_INSN or CALL_INSN.
+ First check for special kinds that recog doesn't recognize. */
+
+ if (GET_CODE (body) == USE /* These are just declarations */
+ || GET_CODE (body) == CLOBBER)
+ break;
+
+#ifdef HAVE_cc0
+ /* If there is a REG_CC_SETTER note on this insn, it means that
+ the setting of the condition code was done in the delay slot
+ of the insn that branched here. So recover the cc status
+ from the insn that set it. */
+
+ note = find_reg_note (insn, REG_CC_SETTER, NULL_RTX);
+ if (note)
+ {
+ NOTICE_UPDATE_CC (PATTERN (XEXP (note, 0)), XEXP (note, 0));
+ cc_prev_status = cc_status;
+ }
+#endif
+
+ /* Detect insns that are really jump-tables
+ and output them as such. */
+
+ if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC)
+ {
+ register int vlen, idx;
+
+ if (prescan > 0)
+ break;
+
+ if (app_on)
+ {
+ fprintf (file, ASM_APP_OFF);
+ app_on = 0;
+ }
+
+ vlen = XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC);
+ for (idx = 0; idx < vlen; idx++)
+ {
+ if (GET_CODE (body) == ADDR_VEC)
+ {
+#ifdef ASM_OUTPUT_ADDR_VEC_ELT
+ ASM_OUTPUT_ADDR_VEC_ELT
+ (file, CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 0, idx), 0)));
+#else
+ abort ();
+#endif
+ }
+ else
+ {
+#ifdef ASM_OUTPUT_ADDR_DIFF_ELT
+ ASM_OUTPUT_ADDR_DIFF_ELT
+ (file,
+ CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 1, idx), 0)),
+ CODE_LABEL_NUMBER (XEXP (XEXP (body, 0), 0)));
+#else
+ abort ();
+#endif
+ }
+ }
+#ifdef ASM_OUTPUT_CASE_END
+ ASM_OUTPUT_CASE_END (file,
+ CODE_LABEL_NUMBER (PREV_INSN (insn)),
+ insn);
+#endif
+
+ text_section ();
+
+ break;
+ }
+
+ /* Do basic-block profiling when we reach a new block.
+ Done here to avoid jump tables. */
+ if (profile_block_flag && new_block)
+ add_bb (file);
+
+ if (GET_CODE (body) == ASM_INPUT)
+ {
+ /* There's no telling what that did to the condition codes. */
+ CC_STATUS_INIT;
+ if (prescan > 0)
+ break;
+ if (! app_on)
+ {
+ fprintf (file, ASM_APP_ON);
+ app_on = 1;
+ }
+ fprintf (asm_out_file, "\t%s\n", XSTR (body, 0));
+ break;
+ }
+
+ /* Detect `asm' construct with operands. */
+ if (asm_noperands (body) >= 0)
+ {
+ int noperands = asm_noperands (body);
+ rtx *ops = (rtx *) alloca (noperands * sizeof (rtx));
+ char *string;
+
+ /* There's no telling what that did to the condition codes. */
+ CC_STATUS_INIT;
+ if (prescan > 0)
+ break;
+
+ if (! app_on)
+ {
+ fprintf (file, ASM_APP_ON);
+ app_on = 1;
+ }
+
+ /* Get out the operand values. */
+ string = decode_asm_operands (body, ops, NULL_PTR,
+ NULL_PTR, NULL_PTR);
+ /* Inhibit aborts on what would otherwise be compiler bugs. */
+ insn_noperands = noperands;
+ this_is_asm_operands = insn;
+
+ /* Output the insn using them. */
+ output_asm_insn (string, ops);
+ this_is_asm_operands = 0;
+ break;
+ }
+
+ if (prescan <= 0 && app_on)
+ {
+ fprintf (file, ASM_APP_OFF);
+ app_on = 0;
+ }
+
+ if (GET_CODE (body) == SEQUENCE)
+ {
+ /* A delayed-branch sequence */
+ register int i;
+ rtx next;
+
+ if (prescan > 0)
+ break;
+ final_sequence = body;
+
+ /* The first insn in this SEQUENCE might be a JUMP_INSN that will
+ force the restoration of a comparison that was previously
+ thought unnecessary. If that happens, cancel this sequence
+ and cause that insn to be restored. */
+
+ next = final_scan_insn (XVECEXP (body, 0, 0), file, 0, prescan, 1);
+ if (next != XVECEXP (body, 0, 1))
+ {
+ final_sequence = 0;
+ return next;
+ }
+
+ for (i = 1; i < XVECLEN (body, 0); i++)
+ final_scan_insn (XVECEXP (body, 0, i), file, 0, prescan, 1);
+#ifdef DBR_OUTPUT_SEQEND
+ DBR_OUTPUT_SEQEND (file);
+#endif
+ final_sequence = 0;
+
+ /* If the insn requiring the delay slot was a CALL_INSN, the
+ insns in the delay slot are actually executed before the
+ called function. Hence we don't preserve any CC-setting
+ actions in these insns and the CC must be marked as being
+ clobbered by the function. */
+ if (GET_CODE (XVECEXP (body, 0, 0)) == CALL_INSN)
+ CC_STATUS_INIT;
+
+ /* Following a conditional branch sequence, we have a new basic
+ block. */
+ if (profile_block_flag)
+ {
+ rtx insn = XVECEXP (body, 0, 0);
+ rtx body = PATTERN (insn);
+
+ if ((GET_CODE (insn) == JUMP_INSN && GET_CODE (body) == SET
+ && GET_CODE (SET_SRC (body)) != LABEL_REF)
+ || (GET_CODE (insn) == JUMP_INSN
+ && GET_CODE (body) == PARALLEL
+ && GET_CODE (XVECEXP (body, 0, 0)) == SET
+ && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) != LABEL_REF))
+ new_block = 1;
+ }
+ break;
+ }
+
+ /* We have a real machine instruction as rtl. */
+
+ body = PATTERN (insn);
+
+#ifdef HAVE_cc0
+ /* Check for redundant test and compare instructions
+ (when the condition codes are already set up as desired).
+ This is done only when optimizing; if not optimizing,
+ it should be possible for the user to alter a variable
+ with the debugger in between statements
+ and the next statement should reexamine the variable
+ to compute the condition codes. */
+
+ if (optimize
+ && GET_CODE (body) == SET
+ && GET_CODE (SET_DEST (body)) == CC0
+ && insn != last_ignored_compare)
+ {
+ if (GET_CODE (SET_SRC (body)) == SUBREG)
+ SET_SRC (body) = alter_subreg (SET_SRC (body));
+ else if (GET_CODE (SET_SRC (body)) == COMPARE)
+ {
+ if (GET_CODE (XEXP (SET_SRC (body), 0)) == SUBREG)
+ XEXP (SET_SRC (body), 0)
+ = alter_subreg (XEXP (SET_SRC (body), 0));
+ if (GET_CODE (XEXP (SET_SRC (body), 1)) == SUBREG)
+ XEXP (SET_SRC (body), 1)
+ = alter_subreg (XEXP (SET_SRC (body), 1));
+ }
+ if ((cc_status.value1 != 0
+ && rtx_equal_p (SET_SRC (body), cc_status.value1))
+ || (cc_status.value2 != 0
+ && rtx_equal_p (SET_SRC (body), cc_status.value2)))
+ {
+ /* Don't delete insn if it has an addressing side-effect. */
+ if (! FIND_REG_INC_NOTE (insn, 0)
+ /* or if anything in it is volatile. */
+ && ! volatile_refs_p (PATTERN (insn)))
+ {
+ /* We don't really delete the insn; just ignore it. */
+ last_ignored_compare = insn;
+ break;
+ }
+ }
+ }
+#endif
+
+ /* Following a conditional branch, we have a new basic block.
+ But if we are inside a sequence, the new block starts after the
+ last insn of the sequence. */
+ if (profile_block_flag && final_sequence == 0
+ && ((GET_CODE (insn) == JUMP_INSN && GET_CODE (body) == SET
+ && GET_CODE (SET_SRC (body)) != LABEL_REF)
+ || (GET_CODE (insn) == JUMP_INSN && GET_CODE (body) == PARALLEL
+ && GET_CODE (XVECEXP (body, 0, 0)) == SET
+ && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) != LABEL_REF)))
+ new_block = 1;
+
+#ifndef STACK_REGS
+ /* Don't bother outputting obvious no-ops, even without -O.
+ This optimization is fast and doesn't interfere with debugging.
+ Don't do this if the insn is in a delay slot, since this
+ will cause an improper number of delay insns to be written. */
+ if (final_sequence == 0
+ && prescan >= 0
+ && GET_CODE (insn) == INSN && GET_CODE (body) == SET
+ && GET_CODE (SET_SRC (body)) == REG
+ && GET_CODE (SET_DEST (body)) == REG
+ && REGNO (SET_SRC (body)) == REGNO (SET_DEST (body)))
+ break;
+#endif
+
+#ifdef HAVE_cc0
+ /* If this is a conditional branch, maybe modify it
+ if the cc's are in a nonstandard state
+ so that it accomplishes the same thing that it would
+ do straightforwardly if the cc's were set up normally. */
+
+ if (cc_status.flags != 0
+ && GET_CODE (insn) == JUMP_INSN
+ && GET_CODE (body) == SET
+ && SET_DEST (body) == pc_rtx
+ && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE
+ /* This is done during prescan; it is not done again
+ in final scan when prescan has been done. */
+ && prescan >= 0)
+ {
+ /* This function may alter the contents of its argument
+ and clear some of the cc_status.flags bits.
+ It may also return 1 meaning condition now always true
+ or -1 meaning condition now always false
+ or 2 meaning condition nontrivial but altered. */
+ register int result = alter_cond (XEXP (SET_SRC (body), 0));
+ /* If condition now has fixed value, replace the IF_THEN_ELSE
+ with its then-operand or its else-operand. */
+ if (result == 1)
+ SET_SRC (body) = XEXP (SET_SRC (body), 1);
+ if (result == -1)
+ SET_SRC (body) = XEXP (SET_SRC (body), 2);
+
+ /* The jump is now either unconditional or a no-op.
+ If it has become a no-op, don't try to output it.
+ (It would not be recognized.) */
+ if (SET_SRC (body) == pc_rtx)
+ {
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ break;
+ }
+ else if (GET_CODE (SET_SRC (body)) == RETURN)
+ /* Replace (set (pc) (return)) with (return). */
+ PATTERN (insn) = body = SET_SRC (body);
+
+ /* Rerecognize the instruction if it has changed. */
+ if (result != 0)
+ INSN_CODE (insn) = -1;
+ }
+
+ /* Make same adjustments to instructions that examine the
+ condition codes without jumping (if this machine has them). */
+
+ if (cc_status.flags != 0
+ && GET_CODE (body) == SET)
+ {
+ switch (GET_CODE (SET_SRC (body)))
+ {
+ case GTU:
+ case GT:
+ case LTU:
+ case LT:
+ case GEU:
+ case GE:
+ case LEU:
+ case LE:
+ case EQ:
+ case NE:
+ {
+ register int result;
+ if (XEXP (SET_SRC (body), 0) != cc0_rtx)
+ break;
+ result = alter_cond (SET_SRC (body));
+ if (result == 1)
+ validate_change (insn, &SET_SRC (body), const_true_rtx, 0);
+ else if (result == -1)
+ validate_change (insn, &SET_SRC (body), const0_rtx, 0);
+ else if (result == 2)
+ INSN_CODE (insn) = -1;
+ }
+ }
+ }
+#endif
+
+ /* Do machine-specific peephole optimizations if desired. */
+
+ if (optimize && !flag_no_peephole && !nopeepholes)
+ {
+ rtx next = peephole (insn);
+ /* When peepholing, if there were notes within the peephole,
+ emit them before the peephole. */
+ if (next != 0 && next != NEXT_INSN (insn))
+ {
+ rtx prev = PREV_INSN (insn);
+ rtx note;
+
+ for (note = NEXT_INSN (insn); note != next;
+ note = NEXT_INSN (note))
+ final_scan_insn (note, file, optimize, prescan, nopeepholes);
+
+ /* In case this is prescan, put the notes
+ in proper position for later rescan. */
+ note = NEXT_INSN (insn);
+ PREV_INSN (note) = prev;
+ NEXT_INSN (prev) = note;
+ NEXT_INSN (PREV_INSN (next)) = insn;
+ PREV_INSN (insn) = PREV_INSN (next);
+ NEXT_INSN (insn) = next;
+ PREV_INSN (next) = insn;
+ }
+
+ /* PEEPHOLE might have changed this. */
+ body = PATTERN (insn);
+ }
+
+ /* Try to recognize the instruction.
+ If successful, verify that the operands satisfy the
+ constraints for the instruction. Crash if they don't,
+ since `reload' should have changed them so that they do. */
+
+ insn_code_number = recog_memoized (insn);
+ insn_extract (insn);
+ for (i = 0; i < insn_n_operands[insn_code_number]; i++)
+ {
+ if (GET_CODE (recog_operand[i]) == SUBREG)
+ recog_operand[i] = alter_subreg (recog_operand[i]);
+ else if (GET_CODE (recog_operand[i]) == PLUS
+ || GET_CODE (recog_operand[i]) == MULT)
+ recog_operand[i] = walk_alter_subreg (recog_operand[i]);
+ }
+
+ for (i = 0; i < insn_n_dups[insn_code_number]; i++)
+ {
+ if (GET_CODE (*recog_dup_loc[i]) == SUBREG)
+ *recog_dup_loc[i] = alter_subreg (*recog_dup_loc[i]);
+ else if (GET_CODE (*recog_dup_loc[i]) == PLUS
+ || GET_CODE (*recog_dup_loc[i]) == MULT)
+ *recog_dup_loc[i] = walk_alter_subreg (*recog_dup_loc[i]);
+ }
+
+#ifdef REGISTER_CONSTRAINTS
+ if (! constrain_operands (insn_code_number, 1))
+ fatal_insn_not_found (insn);
+#endif
+
+ /* Some target machines need to prescan each insn before
+ it is output. */
+
+#ifdef FINAL_PRESCAN_INSN
+ FINAL_PRESCAN_INSN (insn, recog_operand,
+ insn_n_operands[insn_code_number]);
+#endif
+
+#ifdef HAVE_cc0
+ cc_prev_status = cc_status;
+
+ /* Update `cc_status' for this instruction.
+ The instruction's output routine may change it further.
+ If the output routine for a jump insn needs to depend
+ on the cc status, it should look at cc_prev_status. */
+
+ NOTICE_UPDATE_CC (body, insn);
+#endif
+
+ debug_insn = insn;
+
+ /* If the proper template needs to be chosen by some C code,
+ run that code and get the real template. */
+
+ template = insn_template[insn_code_number];
+ if (template == 0)
+ {
+ template = (*insn_outfun[insn_code_number]) (recog_operand, insn);
+
+ /* If the C code returns 0, it means that it is a jump insn
+ which follows a deleted test insn, and that test insn
+ needs to be reinserted. */
+ if (template == 0)
+ {
+ if (prev_nonnote_insn (insn) != last_ignored_compare)
+ abort ();
+ new_block = 0;
+ return prev_nonnote_insn (insn);
+ }
+ }
+
+ /* If the template is the string "#", it means that this insn must
+ be split. */
+ if (template[0] == '#' && template[1] == '\0')
+ {
+ rtx new = try_split (body, insn, 0);
+
+ /* If we didn't split the insn, go away. */
+ if (new == insn && PATTERN (new) == body)
+ abort ();
+
+ new_block = 0;
+ return new;
+ }
+
+ if (prescan > 0)
+ break;
+
+ /* Output assembler code from the template. */
+
+ output_asm_insn (template, recog_operand);
+
+#if 0
+ /* It's not at all clear why we did this and doing so interferes
+ with tests we'd like to do to use REG_WAS_0 notes, so let's try
+ with this out. */
+
+ /* Mark this insn as having been output. */
+ INSN_DELETED_P (insn) = 1;
+#endif
+
+ debug_insn = 0;
+ }
+ }
+ return NEXT_INSN (insn);
+}
+
+/* Output debugging info to the assembler file FILE
+ based on the NOTE-insn INSN, assumed to be a line number. */
+
+static void
+output_source_line (file, insn)
+ FILE *file;
+ rtx insn;
+{
+ register char *filename = NOTE_SOURCE_FILE (insn);
+
+ /* Remember filename for basic block profiling.
+ Filenames are allocated on the permanent obstack
+ or are passed in ARGV, so we don't have to save
+ the string. */
+
+ if (profile_block_flag && last_filename != filename)
+ bb_file_label_num = add_bb_string (filename, TRUE);
+
+ last_filename = filename;
+ last_linenum = NOTE_LINE_NUMBER (insn);
+
+ if (write_symbols != NO_DEBUG)
+ {
+#ifdef SDB_DEBUGGING_INFO
+ if (write_symbols == SDB_DEBUG
+#if 0 /* People like having line numbers even in wrong file! */
+ /* COFF can't handle multiple source files--lose, lose. */
+ && !strcmp (filename, main_input_filename)
+#endif
+ /* COFF relative line numbers must be positive. */
+ && last_linenum > sdb_begin_function_line)
+ {
+#ifdef ASM_OUTPUT_SOURCE_LINE
+ ASM_OUTPUT_SOURCE_LINE (file, last_linenum);
+#else
+ fprintf (file, "\t.ln\t%d\n",
+ ((sdb_begin_function_line > -1)
+ ? last_linenum - sdb_begin_function_line : 1));
+#endif
+ }
+#endif
+
+#if defined (DBX_DEBUGGING_INFO)
+ if (write_symbols == DBX_DEBUG)
+ dbxout_source_line (file, filename, NOTE_LINE_NUMBER (insn));
+#endif
+
+#if defined (XCOFF_DEBUGGING_INFO)
+ if (write_symbols == XCOFF_DEBUG)
+ xcoffout_source_line (file, filename, insn);
+#endif
+
+#ifdef DWARF_DEBUGGING_INFO
+ if (write_symbols == DWARF_DEBUG)
+ dwarfout_line (filename, NOTE_LINE_NUMBER (insn));
+#endif
+ }
+}
+
+/* If X is a SUBREG, replace it with a REG or a MEM,
+ based on the thing it is a subreg of. */
+
+rtx
+alter_subreg (x)
+ register rtx x;
+{
+ register rtx y = SUBREG_REG (x);
+ if (GET_CODE (y) == SUBREG)
+ y = alter_subreg (y);
+
+ if (GET_CODE (y) == REG)
+ {
+ /* If the containing reg really gets a hard reg, so do we. */
+ PUT_CODE (x, REG);
+ REGNO (x) = REGNO (y) + SUBREG_WORD (x);
+ }
+ else if (GET_CODE (y) == MEM)
+ {
+ register int offset = SUBREG_WORD (x) * UNITS_PER_WORD;
+#if BYTES_BIG_ENDIAN
+ offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x)))
+ - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (y))));
+#endif
+ PUT_CODE (x, MEM);
+ MEM_VOLATILE_P (x) = MEM_VOLATILE_P (y);
+ XEXP (x, 0) = plus_constant (XEXP (y, 0), offset);
+ }
+
+ return x;
+}
+
+/* Do alter_subreg on all the SUBREGs contained in X. */
+
+static rtx
+walk_alter_subreg (x)
+ rtx x;
+{
+ switch (GET_CODE (x))
+ {
+ case PLUS:
+ case MULT:
+ XEXP (x, 0) = walk_alter_subreg (XEXP (x, 0));
+ XEXP (x, 1) = walk_alter_subreg (XEXP (x, 1));
+ break;
+
+ case MEM:
+ XEXP (x, 0) = walk_alter_subreg (XEXP (x, 0));
+ break;
+
+ case SUBREG:
+ return alter_subreg (x);
+ }
+
+ return x;
+}
+
+#ifdef HAVE_cc0
+
+/* Given BODY, the body of a jump instruction, alter the jump condition
+ as required by the bits that are set in cc_status.flags.
+ Not all of the bits there can be handled at this level in all cases.
+
+ The value is normally 0.
+ 1 means that the condition has become always true.
+ -1 means that the condition has become always false.
+ 2 means that COND has been altered. */
+
+static int
+alter_cond (cond)
+ register rtx cond;
+{
+ int value = 0;
+
+ if (cc_status.flags & CC_REVERSED)
+ {
+ value = 2;
+ PUT_CODE (cond, swap_condition (GET_CODE (cond)));
+ }
+
+ if (cc_status.flags & CC_INVERTED)
+ {
+ value = 2;
+ PUT_CODE (cond, reverse_condition (GET_CODE (cond)));
+ }
+
+ if (cc_status.flags & CC_NOT_POSITIVE)
+ switch (GET_CODE (cond))
+ {
+ case LE:
+ case LEU:
+ case GEU:
+ /* Jump becomes unconditional. */
+ return 1;
+
+ case GT:
+ case GTU:
+ case LTU:
+ /* Jump becomes no-op. */
+ return -1;
+
+ case GE:
+ PUT_CODE (cond, EQ);
+ value = 2;
+ break;
+
+ case LT:
+ PUT_CODE (cond, NE);
+ value = 2;
+ break;
+ }
+
+ if (cc_status.flags & CC_NOT_NEGATIVE)
+ switch (GET_CODE (cond))
+ {
+ case GE:
+ case GEU:
+ /* Jump becomes unconditional. */
+ return 1;
+
+ case LT:
+ case LTU:
+ /* Jump becomes no-op. */
+ return -1;
+
+ case LE:
+ case LEU:
+ PUT_CODE (cond, EQ);
+ value = 2;
+ break;
+
+ case GT:
+ case GTU:
+ PUT_CODE (cond, NE);
+ value = 2;
+ break;
+ }
+
+ if (cc_status.flags & CC_NO_OVERFLOW)
+ switch (GET_CODE (cond))
+ {
+ case GEU:
+ /* Jump becomes unconditional. */
+ return 1;
+
+ case LEU:
+ PUT_CODE (cond, EQ);
+ value = 2;
+ break;
+
+ case GTU:
+ PUT_CODE (cond, NE);
+ value = 2;
+ break;
+
+ case LTU:
+ /* Jump becomes no-op. */
+ return -1;
+ }
+
+ if (cc_status.flags & (CC_Z_IN_NOT_N | CC_Z_IN_N))
+ switch (GET_CODE (cond))
+ {
+ case LE:
+ case LEU:
+ case GE:
+ case GEU:
+ case LT:
+ case LTU:
+ case GT:
+ case GTU:
+ abort ();
+
+ case NE:
+ PUT_CODE (cond, cc_status.flags & CC_Z_IN_N ? GE : LT);
+ value = 2;
+ break;
+
+ case EQ:
+ PUT_CODE (cond, cc_status.flags & CC_Z_IN_N ? LT : GE);
+ value = 2;
+ break;
+ }
+
+ if (cc_status.flags & CC_NOT_SIGNED)
+ /* The flags are valid if signed condition operators are converted
+ to unsigned. */
+ switch (GET_CODE (cond))
+ {
+ case LE:
+ PUT_CODE (cond, LEU);
+ value = 2;
+ break;
+
+ case LT:
+ PUT_CODE (cond, LTU);
+ value = 2;
+ break;
+
+ case GT:
+ PUT_CODE (cond, GTU);
+ value = 2;
+ break;
+
+ case GE:
+ PUT_CODE (cond, GEU);
+ value = 2;
+ break;
+ }
+
+ return value;
+}
+#endif
+
+/* Report inconsistency between the assembler template and the operands.
+ In an `asm', it's the user's fault; otherwise, the compiler's fault. */
+
+void
+output_operand_lossage (str)
+ char *str;
+{
+ if (this_is_asm_operands)
+ error_for_asm (this_is_asm_operands, "invalid `asm': %s", str);
+ else
+ abort ();
+}
+
+/* Output of assembler code from a template, and its subroutines. */
+
+/* Output text from TEMPLATE to the assembler output file,
+ obeying %-directions to substitute operands taken from
+ the vector OPERANDS.
+
+ %N (for N a digit) means print operand N in usual manner.
+ %lN means require operand N to be a CODE_LABEL or LABEL_REF
+ and print the label name with no punctuation.
+ %cN means require operand N to be a constant
+ and print the constant expression with no punctuation.
+ %aN means expect operand N to be a memory address
+ (not a memory reference!) and print a reference
+ to that address.
+ %nN means expect operand N to be a constant
+ and print a constant expression for minus the value
+ of the operand, with no other punctuation. */
+
+void
+output_asm_insn (template, operands)
+ char *template;
+ rtx *operands;
+{
+ register char *p;
+ register int c, i;
+
+ /* An insn may return a null string template
+ in a case where no assembler code is needed. */
+ if (*template == 0)
+ return;
+
+ p = template;
+ putc ('\t', asm_out_file);
+
+#ifdef ASM_OUTPUT_OPCODE
+ ASM_OUTPUT_OPCODE (asm_out_file, p);
+#endif
+
+ while (c = *p++)
+ switch (c)
+ {
+#ifdef ASM_OUTPUT_OPCODE
+ case '\n':
+ putc (c, asm_out_file);
+ while ((c = *p) == '\t')
+ {
+ putc (c, asm_out_file);
+ p++;
+ }
+ ASM_OUTPUT_OPCODE (asm_out_file, p);
+ break;
+#endif
+
+#ifdef ASSEMBLER_DIALECT
+ case '{':
+ /* If we want the first dialect, do nothing. Otherwise, skip
+ DIALECT_NUMBER of strings ending with '|'. */
+ for (i = 0; i < dialect_number; i++)
+ {
+ while (*p && *p++ != '|')
+ ;
+
+ if (*p == '|')
+ p++;
+ }
+ break;
+
+ case '|':
+ /* Skip to close brace. */
+ while (*p && *p++ != '}')
+ ;
+ break;
+
+ case '}':
+ break;
+#endif
+
+ case '%':
+ /* %% outputs a single %. */
+ if (*p == '%')
+ {
+ p++;
+ putc (c, asm_out_file);
+ }
+ /* %= outputs a number which is unique to each insn in the entire
+ compilation. This is useful for making local labels that are
+ referred to more than once in a given insn. */
+ else if (*p == '=')
+ {
+ p++;
+ fprintf (asm_out_file, "%d", insn_counter);
+ }
+ /* % followed by a letter and some digits
+ outputs an operand in a special way depending on the letter.
+ Letters `acln' are implemented directly.
+ Other letters are passed to `output_operand' so that
+ the PRINT_OPERAND macro can define them. */
+ else if ((*p >= 'a' && *p <= 'z')
+ || (*p >= 'A' && *p <= 'Z'))
+ {
+ int letter = *p++;
+ c = atoi (p);
+
+ if (! (*p >= '0' && *p <= '9'))
+ output_operand_lossage ("operand number missing after %-letter");
+ else if (this_is_asm_operands && c >= (unsigned) insn_noperands)
+ output_operand_lossage ("operand number out of range");
+ else if (letter == 'l')
+ output_asm_label (operands[c]);
+ else if (letter == 'a')
+ output_address (operands[c]);
+ else if (letter == 'c')
+ {
+ if (CONSTANT_ADDRESS_P (operands[c]))
+ output_addr_const (asm_out_file, operands[c]);
+ else
+ output_operand (operands[c], 'c');
+ }
+ else if (letter == 'n')
+ {
+ if (GET_CODE (operands[c]) == CONST_INT)
+ fprintf (asm_out_file,
+#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT
+ "%d",
+#else
+ "%ld",
+#endif
+ - INTVAL (operands[c]));
+ else
+ {
+ putc ('-', asm_out_file);
+ output_addr_const (asm_out_file, operands[c]);
+ }
+ }
+ else
+ output_operand (operands[c], letter);
+
+ while ((c = *p) >= '0' && c <= '9') p++;
+ }
+ /* % followed by a digit outputs an operand the default way. */
+ else if (*p >= '0' && *p <= '9')
+ {
+ c = atoi (p);
+ if (this_is_asm_operands && c >= (unsigned) insn_noperands)
+ output_operand_lossage ("operand number out of range");
+ else
+ output_operand (operands[c], 0);
+ while ((c = *p) >= '0' && c <= '9') p++;
+ }
+ /* % followed by punctuation: output something for that
+ punctuation character alone, with no operand.
+ The PRINT_OPERAND macro decides what is actually done. */
+#ifdef PRINT_OPERAND_PUNCT_VALID_P
+ else if (PRINT_OPERAND_PUNCT_VALID_P (*p))
+ output_operand (NULL_RTX, *p++);
+#endif
+ else
+ output_operand_lossage ("invalid %%-code");
+ break;
+
+ default:
+ putc (c, asm_out_file);
+ }
+
+ if (flag_print_asm_name)
+ {
+ /* Annotate the assembly with a comment describing the pattern and
+ alternative used. */
+ if (debug_insn)
+ {
+ register int num = INSN_CODE (debug_insn);
+ fprintf (asm_out_file, " %s %d %s",
+ ASM_COMMENT_START, INSN_UID (debug_insn), insn_name[num]);
+ if (insn_n_alternatives[num] > 1)
+ fprintf (asm_out_file, "/%d", which_alternative + 1);
+
+ /* Clear this so only the first assembler insn
+ of any rtl insn will get the special comment for -dp. */
+ debug_insn = 0;
+ }
+ }
+
+ putc ('\n', asm_out_file);
+}
+
+/* Output a LABEL_REF, or a bare CODE_LABEL, as an assembler symbol. */
+
+void
+output_asm_label (x)
+ rtx x;
+{
+ char buf[256];
+
+ if (GET_CODE (x) == LABEL_REF)
+ ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (XEXP (x, 0)));
+ else if (GET_CODE (x) == CODE_LABEL)
+ ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x));
+ else
+ output_operand_lossage ("`%l' operand isn't a label");
+
+ assemble_name (asm_out_file, buf);
+}
+
+/* Print operand X using machine-dependent assembler syntax.
+ The macro PRINT_OPERAND is defined just to control this function.
+ CODE is a non-digit that preceded the operand-number in the % spec,
+ such as 'z' if the spec was `%z3'. CODE is 0 if there was no char
+ between the % and the digits.
+ When CODE is a non-letter, X is 0.
+
+ The meanings of the letters are machine-dependent and controlled
+ by PRINT_OPERAND. */
+
+static void
+output_operand (x, code)
+ rtx x;
+ int code;
+{
+ if (x && GET_CODE (x) == SUBREG)
+ x = alter_subreg (x);
+
+ /* If X is a pseudo-register, abort now rather than writing trash to the
+ assembler file. */
+
+ if (x && GET_CODE (x) == REG && REGNO (x) >= FIRST_PSEUDO_REGISTER)
+ abort ();
+
+ PRINT_OPERAND (asm_out_file, x, code);
+}
+
+/* Print a memory reference operand for address X
+ using machine-dependent assembler syntax.
+ The macro PRINT_OPERAND_ADDRESS exists just to control this function. */
+
+void
+output_address (x)
+ rtx x;
+{
+ walk_alter_subreg (x);
+ PRINT_OPERAND_ADDRESS (asm_out_file, x);
+}
+
+/* Print an integer constant expression in assembler syntax.
+ Addition and subtraction are the only arithmetic
+ that may appear in these expressions. */
+
+void
+output_addr_const (file, x)
+ FILE *file;
+ rtx x;
+{
+ char buf[256];
+
+ restart:
+ switch (GET_CODE (x))
+ {
+ case PC:
+ if (flag_pic)
+ putc ('.', file);
+ else
+ abort ();
+ break;
+
+ case SYMBOL_REF:
+ assemble_name (file, XSTR (x, 0));
+ break;
+
+ case LABEL_REF:
+ ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (XEXP (x, 0)));
+ assemble_name (file, buf);
+ break;
+
+ case CODE_LABEL:
+ ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x));
+ assemble_name (file, buf);
+ break;
+
+ case CONST_INT:
+ fprintf (file,
+#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT
+ "%d",
+#else
+ "%ld",
+#endif
+ INTVAL (x));
+ break;
+
+ case CONST:
+ /* This used to output parentheses around the expression,
+ but that does not work on the 386 (either ATT or BSD assembler). */
+ output_addr_const (file, XEXP (x, 0));
+ break;
+
+ case CONST_DOUBLE:
+ if (GET_MODE (x) == VOIDmode)
+ {
+ /* We can use %d if the number is one word and positive. */
+ if (CONST_DOUBLE_HIGH (x))
+ fprintf (file,
+#if HOST_BITS_PER_WIDE_INT == 64
+#if HOST_BITS_PER_WIDE_INT != HOST_BITS_PER_INT
+ "0x%lx%016lx",
+#else
+ "0x%x%016x",
+#endif
+#else
+#if HOST_BITS_PER_WIDE_INT != HOST_BITS_PER_INT
+ "0x%lx%08lx",
+#else
+ "0x%x%08x",
+#endif
+#endif
+ CONST_DOUBLE_HIGH (x), CONST_DOUBLE_LOW (x));
+ else if (CONST_DOUBLE_LOW (x) < 0)
+ fprintf (file,
+#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT
+ "0x%x",
+#else
+ "0x%lx",
+#endif
+ CONST_DOUBLE_LOW (x));
+ else
+ fprintf (file,
+#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT
+ "%d",
+#else
+ "%ld",
+#endif
+ CONST_DOUBLE_LOW (x));
+ }
+ else
+ /* We can't handle floating point constants;
+ PRINT_OPERAND must handle them. */
+ output_operand_lossage ("floating constant misused");
+ break;
+
+ case PLUS:
+ /* Some assemblers need integer constants to appear last (eg masm). */
+ if (GET_CODE (XEXP (x, 0)) == CONST_INT)
+ {
+ output_addr_const (file, XEXP (x, 1));
+ if (INTVAL (XEXP (x, 0)) >= 0)
+ fprintf (file, "+");
+ output_addr_const (file, XEXP (x, 0));
+ }
+ else
+ {
+ output_addr_const (file, XEXP (x, 0));
+ if (INTVAL (XEXP (x, 1)) >= 0)
+ fprintf (file, "+");
+ output_addr_const (file, XEXP (x, 1));
+ }
+ break;
+
+ case MINUS:
+ /* Avoid outputting things like x-x or x+5-x,
+ since some assemblers can't handle that. */
+ x = simplify_subtraction (x);
+ if (GET_CODE (x) != MINUS)
+ goto restart;
+
+ output_addr_const (file, XEXP (x, 0));
+ fprintf (file, "-");
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) < 0)
+ {
+ fprintf (file, ASM_OPEN_PAREN);
+ output_addr_const (file, XEXP (x, 1));
+ fprintf (file, ASM_CLOSE_PAREN);
+ }
+ else
+ output_addr_const (file, XEXP (x, 1));
+ break;
+
+ case ZERO_EXTEND:
+ case SIGN_EXTEND:
+ output_addr_const (file, XEXP (x, 0));
+ break;
+
+ default:
+ output_operand_lossage ("invalid expression as operand");
+ }
+}
+
+/* A poor man's fprintf, with the added features of %I, %R, %L, and %U.
+ %R prints the value of REGISTER_PREFIX.
+ %L prints the value of LOCAL_LABEL_PREFIX.
+ %U prints the value of USER_LABEL_PREFIX.
+ %I prints the value of IMMEDIATE_PREFIX.
+ %O runs ASM_OUTPUT_OPCODE to transform what follows in the string.
+ Also supported are %d, %x, %s, %e, %f, %g and %%.
+
+ We handle alternate assembler dialects here, just like output_asm_insn. */
+
+void
+asm_fprintf VPROTO((FILE *file, char *p, ...))
+{
+#ifndef __STDC__
+ FILE *file;
+ char *p;
+#endif
+ va_list argptr;
+ char buf[10];
+ char *q, c;
+ int i;
+
+ VA_START (argptr, p);
+
+#ifndef __STDC__
+ file = va_arg (argptr, FILE*);
+ p = va_arg (argptr, char*);
+#endif
+
+ buf[0] = '%';
+
+ while (c = *p++)
+ switch (c)
+ {
+#ifdef ASSEMBLER_DIALECT
+ case '{':
+ /* If we want the first dialect, do nothing. Otherwise, skip
+ DIALECT_NUMBER of strings ending with '|'. */
+ for (i = 0; i < dialect_number; i++)
+ {
+ while (*p && *p++ != '|')
+ ;
+
+ if (*p == '|')
+ p++;
+ }
+ break;
+
+ case '|':
+ /* Skip to close brace. */
+ while (*p && *p++ != '}')
+ ;
+ break;
+
+ case '}':
+ break;
+#endif
+
+ case '%':
+ c = *p++;
+ q = &buf[1];
+ while ((c >= '0' && c <= '9') || c == '.')
+ {
+ *q++ = c;
+ c = *p++;
+ }
+ switch (c)
+ {
+ case '%':
+ fprintf (file, "%%");
+ break;
+
+ case 'd': case 'i': case 'u':
+ case 'x': case 'p': case 'X':
+ case 'o':
+ *q++ = c;
+ *q = 0;
+ fprintf (file, buf, va_arg (argptr, int));
+ break;
+
+ case 'w':
+ /* This is a prefix to the 'd', 'i', 'u', 'x', 'p', and 'X' cases,
+ but we do not check for those cases. It means that the value
+ is a HOST_WIDE_INT, which may be either `int' or `long'. */
+
+#if HOST_BITS_PER_WIDE_INT != HOST_BITS_PER_INT
+ *q++ = 'l';
+#endif
+
+ *q++ = *p++;
+ *q = 0;
+ fprintf (file, buf, va_arg (argptr, HOST_WIDE_INT));
+ break;
+
+ case 'l':
+ *q++ = c;
+ *q++ = *p++;
+ *q = 0;
+ fprintf (file, buf, va_arg (argptr, long));
+ break;
+
+ case 'e':
+ case 'f':
+ case 'g':
+ *q++ = c;
+ *q = 0;
+ fprintf (file, buf, va_arg (argptr, double));
+ break;
+
+ case 's':
+ *q++ = c;
+ *q = 0;
+ fprintf (file, buf, va_arg (argptr, char *));
+ break;
+
+ case 'O':
+#ifdef ASM_OUTPUT_OPCODE
+ ASM_OUTPUT_OPCODE (asm_out_file, p);
+#endif
+ break;
+
+ case 'R':
+#ifdef REGISTER_PREFIX
+ fprintf (file, "%s", REGISTER_PREFIX);
+#endif
+ break;
+
+ case 'I':
+#ifdef IMMEDIATE_PREFIX
+ fprintf (file, "%s", IMMEDIATE_PREFIX);
+#endif
+ break;
+
+ case 'L':
+#ifdef LOCAL_LABEL_PREFIX
+ fprintf (file, "%s", LOCAL_LABEL_PREFIX);
+#endif
+ break;
+
+ case 'U':
+#ifdef USER_LABEL_PREFIX
+ fprintf (file, "%s", USER_LABEL_PREFIX);
+#endif
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ default:
+ fputc (c, file);
+ }
+}
+
+/* Split up a CONST_DOUBLE or integer constant rtx
+ into two rtx's for single words,
+ storing in *FIRST the word that comes first in memory in the target
+ and in *SECOND the other. */
+
+void
+split_double (value, first, second)
+ rtx value;
+ rtx *first, *second;
+{
+ if (GET_CODE (value) == CONST_INT)
+ {
+ /* The rule for using CONST_INT for a wider mode
+ is that we regard the value as signed.
+ So sign-extend it. */
+ rtx high = (INTVAL (value) < 0 ? constm1_rtx : const0_rtx);
+#if WORDS_BIG_ENDIAN
+ *first = high;
+ *second = value;
+#else
+ *first = value;
+ *second = high;
+#endif
+ }
+ else if (GET_CODE (value) != CONST_DOUBLE)
+ {
+#if WORDS_BIG_ENDIAN
+ *first = const0_rtx;
+ *second = value;
+#else
+ *first = value;
+ *second = const0_rtx;
+#endif
+ }
+ else if (GET_MODE (value) == VOIDmode
+ /* This is the old way we did CONST_DOUBLE integers. */
+ || GET_MODE_CLASS (GET_MODE (value)) == MODE_INT)
+ {
+ /* In an integer, the words are defined as most and least significant.
+ So order them by the target's convention. */
+#if WORDS_BIG_ENDIAN
+ *first = GEN_INT (CONST_DOUBLE_HIGH (value));
+ *second = GEN_INT (CONST_DOUBLE_LOW (value));
+#else
+ *first = GEN_INT (CONST_DOUBLE_LOW (value));
+ *second = GEN_INT (CONST_DOUBLE_HIGH (value));
+#endif
+ }
+ else
+ {
+#ifdef REAL_ARITHMETIC
+ REAL_VALUE_TYPE r; long l[2];
+ REAL_VALUE_FROM_CONST_DOUBLE (r, value);
+
+ /* Note, this converts the REAL_VALUE_TYPE to the target's
+ format, splits up the floating point double and outputs
+ exactly 32 bits of it into each of l[0] and l[1] --
+ not necessarily BITS_PER_WORD bits. */
+ REAL_VALUE_TO_TARGET_DOUBLE (r, l);
+
+ *first = GEN_INT ((HOST_WIDE_INT) l[0]);
+ *second = GEN_INT ((HOST_WIDE_INT) l[1]);
+#else
+ if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
+ || HOST_BITS_PER_WIDE_INT != BITS_PER_WORD)
+ && ! flag_pretend_float)
+ abort ();
+
+#if defined (HOST_WORDS_BIG_ENDIAN) == WORDS_BIG_ENDIAN
+ /* Host and target agree => no need to swap. */
+ *first = GEN_INT (CONST_DOUBLE_LOW (value));
+ *second = GEN_INT (CONST_DOUBLE_HIGH (value));
+#else
+ *second = GEN_INT (CONST_DOUBLE_LOW (value));
+ *first = GEN_INT (CONST_DOUBLE_HIGH (value));
+#endif
+#endif /* no REAL_ARITHMETIC */
+ }
+}
+
+/* Return nonzero if this function has no function calls. */
+
+int
+leaf_function_p ()
+{
+ rtx insn;
+
+ if (profile_flag || profile_block_flag)
+ return 0;
+
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == CALL_INSN)
+ return 0;
+ if (GET_CODE (insn) == INSN
+ && GET_CODE (PATTERN (insn)) == SEQUENCE
+ && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == CALL_INSN)
+ return 0;
+ }
+ for (insn = current_function_epilogue_delay_list; insn; insn = XEXP (insn, 1))
+ {
+ if (GET_CODE (XEXP (insn, 0)) == CALL_INSN)
+ return 0;
+ if (GET_CODE (XEXP (insn, 0)) == INSN
+ && GET_CODE (PATTERN (XEXP (insn, 0))) == SEQUENCE
+ && GET_CODE (XVECEXP (PATTERN (XEXP (insn, 0)), 0, 0)) == CALL_INSN)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* On some machines, a function with no call insns
+ can run faster if it doesn't create its own register window.
+ When output, the leaf function should use only the "output"
+ registers. Ordinarily, the function would be compiled to use
+ the "input" registers to find its arguments; it is a candidate
+ for leaf treatment if it uses only the "input" registers.
+ Leaf function treatment means renumbering so the function
+ uses the "output" registers instead. */
+
+#ifdef LEAF_REGISTERS
+
+static char permitted_reg_in_leaf_functions[] = LEAF_REGISTERS;
+
+/* Return 1 if this function uses only the registers that can be
+ safely renumbered. */
+
+int
+only_leaf_regs_used ()
+{
+ int i;
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ if ((regs_ever_live[i] || global_regs[i])
+ && ! permitted_reg_in_leaf_functions[i])
+ return 0;
+ }
+ return 1;
+}
+
+/* Scan all instructions and renumber all registers into those
+ available in leaf functions. */
+
+static void
+leaf_renumber_regs (first)
+ rtx first;
+{
+ rtx insn;
+
+ /* Renumber only the actual patterns.
+ The reg-notes can contain frame pointer refs,
+ and renumbering them could crash, and should not be needed. */
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ leaf_renumber_regs_insn (PATTERN (insn));
+ for (insn = current_function_epilogue_delay_list; insn; insn = XEXP (insn, 1))
+ if (GET_RTX_CLASS (GET_CODE (XEXP (insn, 0))) == 'i')
+ leaf_renumber_regs_insn (PATTERN (XEXP (insn, 0)));
+}
+
+/* Scan IN_RTX and its subexpressions, and renumber all regs into those
+ available in leaf functions. */
+
+void
+leaf_renumber_regs_insn (in_rtx)
+ register rtx in_rtx;
+{
+ register int i, j;
+ register char *format_ptr;
+
+ if (in_rtx == 0)
+ return;
+
+ /* Renumber all input-registers into output-registers.
+ renumbered_regs would be 1 for an output-register;
+ they */
+
+ if (GET_CODE (in_rtx) == REG)
+ {
+ int newreg;
+
+ /* Don't renumber the same reg twice. */
+ if (in_rtx->used)
+ return;
+
+ newreg = REGNO (in_rtx);
+ /* Don't try to renumber pseudo regs. It is possible for a pseudo reg
+ to reach here as part of a REG_NOTE. */
+ if (newreg >= FIRST_PSEUDO_REGISTER)
+ {
+ in_rtx->used = 1;
+ return;
+ }
+ newreg = LEAF_REG_REMAP (newreg);
+ if (newreg < 0)
+ abort ();
+ regs_ever_live[REGNO (in_rtx)] = 0;
+ regs_ever_live[newreg] = 1;
+ REGNO (in_rtx) = newreg;
+ in_rtx->used = 1;
+ }
+
+ if (GET_RTX_CLASS (GET_CODE (in_rtx)) == 'i')
+ {
+ /* Inside a SEQUENCE, we find insns.
+ Renumber just the patterns of these insns,
+ just as we do for the top-level insns. */
+ leaf_renumber_regs_insn (PATTERN (in_rtx));
+ return;
+ }
+
+ format_ptr = GET_RTX_FORMAT (GET_CODE (in_rtx));
+
+ for (i = 0; i < GET_RTX_LENGTH (GET_CODE (in_rtx)); i++)
+ switch (*format_ptr++)
+ {
+ case 'e':
+ leaf_renumber_regs_insn (XEXP (in_rtx, i));
+ break;
+
+ case 'E':
+ if (NULL != XVEC (in_rtx, i))
+ {
+ for (j = 0; j < XVECLEN (in_rtx, i); j++)
+ leaf_renumber_regs_insn (XVECEXP (in_rtx, i, j));
+ }
+ break;
+
+ case 'S':
+ case 's':
+ case '0':
+ case 'i':
+ case 'w':
+ case 'n':
+ case 'u':
+ break;
+
+ default:
+ abort ();
+ }
+}
+#endif
diff --git a/gnu/usr.bin/cc/cc_int/flow.c b/gnu/usr.bin/cc/cc_int/flow.c
new file mode 100644
index 0000000..cc9fed9
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/flow.c
@@ -0,0 +1,2793 @@
+/* Data flow analysis for GNU compiler.
+ Copyright (C) 1987, 1988, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file contains the data flow analysis pass of the compiler.
+ It computes data flow information
+ which tells combine_instructions which insns to consider combining
+ and controls register allocation.
+
+ Additional data flow information that is too bulky to record
+ is generated during the analysis, and is used at that time to
+ create autoincrement and autodecrement addressing.
+
+ The first step is dividing the function into basic blocks.
+ find_basic_blocks does this. Then life_analysis determines
+ where each register is live and where it is dead.
+
+ ** find_basic_blocks **
+
+ find_basic_blocks divides the current function's rtl
+ into basic blocks. It records the beginnings and ends of the
+ basic blocks in the vectors basic_block_head and basic_block_end,
+ and the number of blocks in n_basic_blocks.
+
+ find_basic_blocks also finds any unreachable loops
+ and deletes them.
+
+ ** life_analysis **
+
+ life_analysis is called immediately after find_basic_blocks.
+ It uses the basic block information to determine where each
+ hard or pseudo register is live.
+
+ ** live-register info **
+
+ The information about where each register is live is in two parts:
+ the REG_NOTES of insns, and the vector basic_block_live_at_start.
+
+ basic_block_live_at_start has an element for each basic block,
+ and the element is a bit-vector with a bit for each hard or pseudo
+ register. The bit is 1 if the register is live at the beginning
+ of the basic block.
+
+ Two types of elements can be added to an insn's REG_NOTES.
+ A REG_DEAD note is added to an insn's REG_NOTES for any register
+ that meets both of two conditions: The value in the register is not
+ needed in subsequent insns and the insn does not replace the value in
+ the register (in the case of multi-word hard registers, the value in
+ each register must be replaced by the insn to avoid a REG_DEAD note).
+
+ In the vast majority of cases, an object in a REG_DEAD note will be
+ used somewhere in the insn. The (rare) exception to this is if an
+ insn uses a multi-word hard register and only some of the registers are
+ needed in subsequent insns. In that case, REG_DEAD notes will be
+ provided for those hard registers that are not subsequently needed.
+ Partial REG_DEAD notes of this type do not occur when an insn sets
+ only some of the hard registers used in such a multi-word operand;
+ omitting REG_DEAD notes for objects stored in an insn is optional and
+ the desire to do so does not justify the complexity of the partial
+ REG_DEAD notes.
+
+ REG_UNUSED notes are added for each register that is set by the insn
+ but is unused subsequently (if every register set by the insn is unused
+ and the insn does not reference memory or have some other side-effect,
+ the insn is deleted instead). If only part of a multi-word hard
+ register is used in a subsequent insn, REG_UNUSED notes are made for
+ the parts that will not be used.
+
+ To determine which registers are live after any insn, one can
+ start from the beginning of the basic block and scan insns, noting
+ which registers are set by each insn and which die there.
+
+ ** Other actions of life_analysis **
+
+ life_analysis sets up the LOG_LINKS fields of insns because the
+ information needed to do so is readily available.
+
+ life_analysis deletes insns whose only effect is to store a value
+ that is never used.
+
+ life_analysis notices cases where a reference to a register as
+ a memory address can be combined with a preceding or following
+ incrementation or decrementation of the register. The separate
+ instruction to increment or decrement is deleted and the address
+ is changed to a POST_INC or similar rtx.
+
+ Each time an incrementing or decrementing address is created,
+ a REG_INC element is added to the insn's REG_NOTES list.
+
+ life_analysis fills in certain vectors containing information about
+ register usage: reg_n_refs, reg_n_deaths, reg_n_sets, reg_live_length,
+ reg_n_calls_crosses and reg_basic_block. */
+
+#include <stdio.h>
+#include "config.h"
+#include "rtl.h"
+#include "basic-block.h"
+#include "insn-config.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "flags.h"
+#include "output.h"
+
+#include "obstack.h"
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+/* List of labels that must never be deleted. */
+extern rtx forced_labels;
+
+/* Get the basic block number of an insn.
+ This info should not be expected to remain available
+ after the end of life_analysis. */
+
+/* This is the limit of the allocated space in the following two arrays. */
+
+static int max_uid_for_flow;
+
+#define BLOCK_NUM(INSN) uid_block_number[INSN_UID (INSN)]
+
+/* This is where the BLOCK_NUM values are really stored.
+ This is set up by find_basic_blocks and used there and in life_analysis,
+ and then freed. */
+
+static int *uid_block_number;
+
+/* INSN_VOLATILE (insn) is 1 if the insn refers to anything volatile. */
+
+#define INSN_VOLATILE(INSN) uid_volatile[INSN_UID (INSN)]
+static char *uid_volatile;
+
+/* Number of basic blocks in the current function. */
+
+int n_basic_blocks;
+
+/* Maximum register number used in this function, plus one. */
+
+int max_regno;
+
+/* Maximum number of SCRATCH rtx's used in any basic block of this function. */
+
+int max_scratch;
+
+/* Number of SCRATCH rtx's in the current block. */
+
+static int num_scratch;
+
+/* Indexed by n, gives number of basic block that (REG n) is used in.
+ If the value is REG_BLOCK_GLOBAL (-2),
+ it means (REG n) is used in more than one basic block.
+ REG_BLOCK_UNKNOWN (-1) means it hasn't been seen yet so we don't know.
+ This information remains valid for the rest of the compilation
+ of the current function; it is used to control register allocation. */
+
+int *reg_basic_block;
+
+/* Indexed by n, gives number of times (REG n) is used or set, each
+ weighted by its loop-depth.
+ This information remains valid for the rest of the compilation
+ of the current function; it is used to control register allocation. */
+
+int *reg_n_refs;
+
+/* Indexed by N, gives number of places register N dies.
+ This information remains valid for the rest of the compilation
+ of the current function; it is used to control register allocation. */
+
+short *reg_n_deaths;
+
+/* Indexed by N, gives 1 if that reg is live across any CALL_INSNs.
+ This information remains valid for the rest of the compilation
+ of the current function; it is used to control register allocation. */
+
+int *reg_n_calls_crossed;
+
+/* Total number of instructions at which (REG n) is live.
+ The larger this is, the less priority (REG n) gets for
+ allocation in a real register.
+ This information remains valid for the rest of the compilation
+ of the current function; it is used to control register allocation.
+
+ local-alloc.c may alter this number to change the priority.
+
+ Negative values are special.
+ -1 is used to mark a pseudo reg which has a constant or memory equivalent
+ and is used infrequently enough that it should not get a hard register.
+ -2 is used to mark a pseudo reg for a parameter, when a frame pointer
+ is not required. global.c makes an allocno for this but does
+ not try to assign a hard register to it. */
+
+int *reg_live_length;
+
+/* Element N is the next insn that uses (hard or pseudo) register number N
+ within the current basic block; or zero, if there is no such insn.
+ This is valid only during the final backward scan in propagate_block. */
+
+static rtx *reg_next_use;
+
+/* Size of a regset for the current function,
+ in (1) bytes and (2) elements. */
+
+int regset_bytes;
+int regset_size;
+
+/* Element N is first insn in basic block N.
+ This info lasts until we finish compiling the function. */
+
+rtx *basic_block_head;
+
+/* Element N is last insn in basic block N.
+ This info lasts until we finish compiling the function. */
+
+rtx *basic_block_end;
+
+/* Element N is a regset describing the registers live
+ at the start of basic block N.
+ This info lasts until we finish compiling the function. */
+
+regset *basic_block_live_at_start;
+
+/* Regset of regs live when calls to `setjmp'-like functions happen. */
+
+regset regs_live_at_setjmp;
+
+/* List made of EXPR_LIST rtx's which gives pairs of pseudo registers
+ that have to go in the same hard reg.
+ The first two regs in the list are a pair, and the next two
+ are another pair, etc. */
+rtx regs_may_share;
+
+/* Element N is nonzero if control can drop into basic block N
+ from the preceding basic block. Freed after life_analysis. */
+
+static char *basic_block_drops_in;
+
+/* Element N is depth within loops of the last insn in basic block number N.
+ Freed after life_analysis. */
+
+static short *basic_block_loop_depth;
+
+/* Element N nonzero if basic block N can actually be reached.
+ Vector exists only during find_basic_blocks. */
+
+static char *block_live_static;
+
+/* Depth within loops of basic block being scanned for lifetime analysis,
+ plus one. This is the weight attached to references to registers. */
+
+static int loop_depth;
+
+/* During propagate_block, this is non-zero if the value of CC0 is live. */
+
+static int cc0_live;
+
+/* During propagate_block, this contains the last MEM stored into. It
+ is used to eliminate consecutive stores to the same location. */
+
+static rtx last_mem_set;
+
+/* Set of registers that may be eliminable. These are handled specially
+ in updating regs_ever_live. */
+
+static HARD_REG_SET elim_reg_set;
+
+/* Forward declarations */
+static void find_basic_blocks PROTO((rtx, rtx));
+static int uses_reg_or_mem PROTO((rtx));
+static void mark_label_ref PROTO((rtx, rtx, int));
+static void life_analysis PROTO((rtx, int));
+void allocate_for_life_analysis PROTO((void));
+static void init_regset_vector PROTO((regset *, regset, int, int));
+static void propagate_block PROTO((regset, rtx, rtx, int,
+ regset, int));
+static int insn_dead_p PROTO((rtx, regset, int));
+static int libcall_dead_p PROTO((rtx, regset, rtx, rtx));
+static void mark_set_regs PROTO((regset, regset, rtx,
+ rtx, regset));
+static void mark_set_1 PROTO((regset, regset, rtx,
+ rtx, regset));
+static void find_auto_inc PROTO((regset, rtx, rtx));
+static void mark_used_regs PROTO((regset, regset, rtx, int, rtx));
+static int try_pre_increment_1 PROTO((rtx));
+static int try_pre_increment PROTO((rtx, rtx, HOST_WIDE_INT));
+static rtx find_use_as_address PROTO((rtx, rtx, HOST_WIDE_INT));
+void dump_flow_info PROTO((FILE *));
+
+/* Find basic blocks of the current function and perform data flow analysis.
+ F is the first insn of the function and NREGS the number of register numbers
+ in use. */
+
+void
+flow_analysis (f, nregs, file)
+ rtx f;
+ int nregs;
+ FILE *file;
+{
+ register rtx insn;
+ register int i;
+ rtx nonlocal_label_list = nonlocal_label_rtx_list ();
+
+#ifdef ELIMINABLE_REGS
+ static struct {int from, to; } eliminables[] = ELIMINABLE_REGS;
+#endif
+
+ /* Record which registers will be eliminated. We use this in
+ mark_used_regs. */
+
+ CLEAR_HARD_REG_SET (elim_reg_set);
+
+#ifdef ELIMINABLE_REGS
+ for (i = 0; i < sizeof eliminables / sizeof eliminables[0]; i++)
+ SET_HARD_REG_BIT (elim_reg_set, eliminables[i].from);
+#else
+ SET_HARD_REG_BIT (elim_reg_set, FRAME_POINTER_REGNUM);
+#endif
+
+ /* Count the basic blocks. Also find maximum insn uid value used. */
+
+ {
+ register RTX_CODE prev_code = JUMP_INSN;
+ register RTX_CODE code;
+
+ max_uid_for_flow = 0;
+
+ for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
+ {
+ code = GET_CODE (insn);
+ if (INSN_UID (insn) > max_uid_for_flow)
+ max_uid_for_flow = INSN_UID (insn);
+ if (code == CODE_LABEL
+ || (GET_RTX_CLASS (code) == 'i'
+ && (prev_code == JUMP_INSN
+ || (prev_code == CALL_INSN
+ && nonlocal_label_list != 0)
+ || prev_code == BARRIER)))
+ i++;
+ if (code != NOTE)
+ prev_code = code;
+ }
+ }
+
+#ifdef AUTO_INC_DEC
+ /* Leave space for insns we make in some cases for auto-inc. These cases
+ are rare, so we don't need too much space. */
+ max_uid_for_flow += max_uid_for_flow / 10;
+#endif
+
+ /* Allocate some tables that last till end of compiling this function
+ and some needed only in find_basic_blocks and life_analysis. */
+
+ n_basic_blocks = i;
+ basic_block_head = (rtx *) oballoc (n_basic_blocks * sizeof (rtx));
+ basic_block_end = (rtx *) oballoc (n_basic_blocks * sizeof (rtx));
+ basic_block_drops_in = (char *) alloca (n_basic_blocks);
+ basic_block_loop_depth = (short *) alloca (n_basic_blocks * sizeof (short));
+ uid_block_number
+ = (int *) alloca ((max_uid_for_flow + 1) * sizeof (int));
+ uid_volatile = (char *) alloca (max_uid_for_flow + 1);
+ bzero (uid_volatile, max_uid_for_flow + 1);
+
+ find_basic_blocks (f, nonlocal_label_list);
+ life_analysis (f, nregs);
+ if (file)
+ dump_flow_info (file);
+
+ basic_block_drops_in = 0;
+ uid_block_number = 0;
+ basic_block_loop_depth = 0;
+}
+
+/* Find all basic blocks of the function whose first insn is F.
+ Store the correct data in the tables that describe the basic blocks,
+ set up the chains of references for each CODE_LABEL, and
+ delete any entire basic blocks that cannot be reached.
+
+ NONLOCAL_LABEL_LIST is the same local variable from flow_analysis. */
+
+static void
+find_basic_blocks (f, nonlocal_label_list)
+ rtx f, nonlocal_label_list;
+{
+ register rtx insn;
+ register int i;
+ register char *block_live = (char *) alloca (n_basic_blocks);
+ register char *block_marked = (char *) alloca (n_basic_blocks);
+ /* List of label_refs to all labels whose addresses are taken
+ and used as data. */
+ rtx label_value_list = 0;
+ rtx x, note;
+ enum rtx_code prev_code, code;
+ int depth;
+
+ block_live_static = block_live;
+ bzero (block_live, n_basic_blocks);
+ bzero (block_marked, n_basic_blocks);
+
+ /* Initialize with just block 0 reachable and no blocks marked. */
+ if (n_basic_blocks > 0)
+ block_live[0] = 1;
+
+ /* Initialize the ref chain of each label to 0. Record where all the
+ blocks start and end and their depth in loops. For each insn, record
+ the block it is in. Also mark as reachable any blocks headed by labels
+ that must not be deleted. */
+
+ for (insn = f, i = -1, prev_code = JUMP_INSN, depth = 1;
+ insn; insn = NEXT_INSN (insn))
+ {
+ code = GET_CODE (insn);
+ if (code == NOTE)
+ {
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
+ depth++;
+ else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
+ depth--;
+ }
+
+ /* A basic block starts at label, or after something that can jump. */
+ else if (code == CODE_LABEL
+ || (GET_RTX_CLASS (code) == 'i'
+ && (prev_code == JUMP_INSN
+ || (prev_code == CALL_INSN
+ && nonlocal_label_list != 0)
+ || prev_code == BARRIER)))
+ {
+ basic_block_head[++i] = insn;
+ basic_block_end[i] = insn;
+ basic_block_loop_depth[i] = depth;
+
+ if (code == CODE_LABEL)
+ {
+ LABEL_REFS (insn) = insn;
+ /* Any label that cannot be deleted
+ is considered to start a reachable block. */
+ if (LABEL_PRESERVE_P (insn))
+ block_live[i] = 1;
+ }
+ }
+
+ else if (GET_RTX_CLASS (code) == 'i')
+ {
+ basic_block_end[i] = insn;
+ basic_block_loop_depth[i] = depth;
+ }
+
+ if (GET_RTX_CLASS (code) == 'i')
+ {
+ /* Make a list of all labels referred to other than by jumps. */
+ for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
+ if (REG_NOTE_KIND (note) == REG_LABEL)
+ label_value_list = gen_rtx (EXPR_LIST, VOIDmode, XEXP (note, 0),
+ label_value_list);
+ }
+
+ BLOCK_NUM (insn) = i;
+
+ if (code != NOTE)
+ prev_code = code;
+ }
+
+ if (i + 1 != n_basic_blocks)
+ abort ();
+
+ /* Don't delete the labels (in this function)
+ that are referenced by non-jump instructions. */
+
+ for (x = label_value_list; x; x = XEXP (x, 1))
+ if (! LABEL_REF_NONLOCAL_P (x))
+ block_live[BLOCK_NUM (XEXP (x, 0))] = 1;
+
+ for (x = forced_labels; x; x = XEXP (x, 1))
+ if (! LABEL_REF_NONLOCAL_P (x))
+ block_live[BLOCK_NUM (XEXP (x, 0))] = 1;
+
+ /* Record which basic blocks control can drop in to. */
+
+ for (i = 0; i < n_basic_blocks; i++)
+ {
+ for (insn = PREV_INSN (basic_block_head[i]);
+ insn && GET_CODE (insn) == NOTE; insn = PREV_INSN (insn))
+ ;
+
+ basic_block_drops_in[i] = insn && GET_CODE (insn) != BARRIER;
+ }
+
+ /* Now find which basic blocks can actually be reached
+ and put all jump insns' LABEL_REFS onto the ref-chains
+ of their target labels. */
+
+ if (n_basic_blocks > 0)
+ {
+ int something_marked = 1;
+
+ /* Find all indirect jump insns and mark them as possibly jumping to all
+ the labels whose addresses are explicitly used. This is because,
+ when there are computed gotos, we can't tell which labels they jump
+ to, of all the possibilities.
+
+ Tablejumps and casesi insns are OK and we can recognize them by
+ a (use (label_ref)). */
+
+ for (insn = f; insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == JUMP_INSN)
+ {
+ rtx pat = PATTERN (insn);
+ int computed_jump = 0;
+
+ if (GET_CODE (pat) == PARALLEL)
+ {
+ int len = XVECLEN (pat, 0);
+ int has_use_labelref = 0;
+
+ for (i = len - 1; i >= 0; i--)
+ if (GET_CODE (XVECEXP (pat, 0, i)) == USE
+ && (GET_CODE (XEXP (XVECEXP (pat, 0, i), 0))
+ == LABEL_REF))
+ has_use_labelref = 1;
+
+ if (! has_use_labelref)
+ for (i = len - 1; i >= 0; i--)
+ if (GET_CODE (XVECEXP (pat, 0, i)) == SET
+ && SET_DEST (XVECEXP (pat, 0, i)) == pc_rtx
+ && uses_reg_or_mem (SET_SRC (XVECEXP (pat, 0, i))))
+ computed_jump = 1;
+ }
+ else if (GET_CODE (pat) == SET
+ && SET_DEST (pat) == pc_rtx
+ && uses_reg_or_mem (SET_SRC (pat)))
+ computed_jump = 1;
+
+ if (computed_jump)
+ {
+ for (x = label_value_list; x; x = XEXP (x, 1))
+ mark_label_ref (gen_rtx (LABEL_REF, VOIDmode, XEXP (x, 0)),
+ insn, 0);
+
+ for (x = forced_labels; x; x = XEXP (x, 1))
+ mark_label_ref (gen_rtx (LABEL_REF, VOIDmode, XEXP (x, 0)),
+ insn, 0);
+ }
+ }
+
+ /* Find all call insns and mark them as possibly jumping
+ to all the nonlocal goto handler labels. */
+
+ for (insn = f; insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ for (x = nonlocal_label_list; x; x = XEXP (x, 1))
+ /* Don't try marking labels that
+ were deleted as unreferenced. */
+ if (GET_CODE (XEXP (x, 0)) == CODE_LABEL)
+ mark_label_ref (gen_rtx (LABEL_REF, VOIDmode, XEXP (x, 0)),
+ insn, 0);
+
+ /* ??? This could be made smarter:
+ in some cases it's possible to tell that certain
+ calls will not do a nonlocal goto.
+
+ For example, if the nested functions that do the
+ nonlocal gotos do not have their addresses taken, then
+ only calls to those functions or to other nested
+ functions that use them could possibly do nonlocal
+ gotos. */
+ }
+
+ /* Pass over all blocks, marking each block that is reachable
+ and has not yet been marked.
+ Keep doing this until, in one pass, no blocks have been marked.
+ Then blocks_live and blocks_marked are identical and correct.
+ In addition, all jumps actually reachable have been marked. */
+
+ while (something_marked)
+ {
+ something_marked = 0;
+ for (i = 0; i < n_basic_blocks; i++)
+ if (block_live[i] && !block_marked[i])
+ {
+ block_marked[i] = 1;
+ something_marked = 1;
+ if (i + 1 < n_basic_blocks && basic_block_drops_in[i + 1])
+ block_live[i + 1] = 1;
+ insn = basic_block_end[i];
+ if (GET_CODE (insn) == JUMP_INSN)
+ mark_label_ref (PATTERN (insn), insn, 0);
+ }
+ }
+
+ /* Now delete the code for any basic blocks that can't be reached.
+ They can occur because jump_optimize does not recognize
+ unreachable loops as unreachable. */
+
+ for (i = 0; i < n_basic_blocks; i++)
+ if (!block_live[i])
+ {
+ insn = basic_block_head[i];
+ while (1)
+ {
+ if (GET_CODE (insn) == BARRIER)
+ abort ();
+ if (GET_CODE (insn) != NOTE)
+ {
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ }
+ if (insn == basic_block_end[i])
+ {
+ /* BARRIERs are between basic blocks, not part of one.
+ Delete a BARRIER if the preceding jump is deleted.
+ We cannot alter a BARRIER into a NOTE
+ because it is too short; but we can really delete
+ it because it is not part of a basic block. */
+ if (NEXT_INSN (insn) != 0
+ && GET_CODE (NEXT_INSN (insn)) == BARRIER)
+ delete_insn (NEXT_INSN (insn));
+ break;
+ }
+ insn = NEXT_INSN (insn);
+ }
+ /* Each time we delete some basic blocks,
+ see if there is a jump around them that is
+ being turned into a no-op. If so, delete it. */
+
+ if (block_live[i - 1])
+ {
+ register int j;
+ for (j = i; j < n_basic_blocks; j++)
+ if (block_live[j])
+ {
+ rtx label;
+ insn = basic_block_end[i - 1];
+ if (GET_CODE (insn) == JUMP_INSN
+ /* An unconditional jump is the only possibility
+ we must check for, since a conditional one
+ would make these blocks live. */
+ && simplejump_p (insn)
+ && (label = XEXP (SET_SRC (PATTERN (insn)), 0), 1)
+ && INSN_UID (label) != 0
+ && BLOCK_NUM (label) == j)
+ {
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ if (GET_CODE (NEXT_INSN (insn)) != BARRIER)
+ abort ();
+ delete_insn (NEXT_INSN (insn));
+ }
+ break;
+ }
+ }
+ }
+ }
+}
+
+/* Return 1 if X contain a REG or MEM that is not in the constant pool. */
+
+static int
+uses_reg_or_mem (x)
+ rtx x;
+{
+ enum rtx_code code = GET_CODE (x);
+ int i, j;
+ char *fmt;
+
+ if (code == REG
+ || (code == MEM
+ && ! (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
+ && CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)))))
+ return 1;
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e'
+ && uses_reg_or_mem (XEXP (x, i)))
+ return 1;
+
+ if (fmt[i] == 'E')
+ for (j = 0; j < XVECLEN (x, i); j++)
+ if (uses_reg_or_mem (XVECEXP (x, i, j)))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Check expression X for label references;
+ if one is found, add INSN to the label's chain of references.
+
+ CHECKDUP means check for and avoid creating duplicate references
+ from the same insn. Such duplicates do no serious harm but
+ can slow life analysis. CHECKDUP is set only when duplicates
+ are likely. */
+
+static void
+mark_label_ref (x, insn, checkdup)
+ rtx x, insn;
+ int checkdup;
+{
+ register RTX_CODE code;
+ register int i;
+ register char *fmt;
+
+ /* We can be called with NULL when scanning label_value_list. */
+ if (x == 0)
+ return;
+
+ code = GET_CODE (x);
+ if (code == LABEL_REF)
+ {
+ register rtx label = XEXP (x, 0);
+ register rtx y;
+ if (GET_CODE (label) != CODE_LABEL)
+ abort ();
+ /* If the label was never emitted, this insn is junk,
+ but avoid a crash trying to refer to BLOCK_NUM (label).
+ This can happen as a result of a syntax error
+ and a diagnostic has already been printed. */
+ if (INSN_UID (label) == 0)
+ return;
+ CONTAINING_INSN (x) = insn;
+ /* if CHECKDUP is set, check for duplicate ref from same insn
+ and don't insert. */
+ if (checkdup)
+ for (y = LABEL_REFS (label); y != label; y = LABEL_NEXTREF (y))
+ if (CONTAINING_INSN (y) == insn)
+ return;
+ LABEL_NEXTREF (x) = LABEL_REFS (label);
+ LABEL_REFS (label) = x;
+ block_live_static[BLOCK_NUM (label)] = 1;
+ return;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ mark_label_ref (XEXP (x, i), insn, 0);
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ mark_label_ref (XVECEXP (x, i, j), insn, 1);
+ }
+ }
+}
+
+/* Determine which registers are live at the start of each
+ basic block of the function whose first insn is F.
+ NREGS is the number of registers used in F.
+ We allocate the vector basic_block_live_at_start
+ and the regsets that it points to, and fill them with the data.
+ regset_size and regset_bytes are also set here. */
+
+static void
+life_analysis (f, nregs)
+ rtx f;
+ int nregs;
+{
+ register regset tem;
+ int first_pass;
+ int changed;
+ /* For each basic block, a bitmask of regs
+ live on exit from the block. */
+ regset *basic_block_live_at_end;
+ /* For each basic block, a bitmask of regs
+ live on entry to a successor-block of this block.
+ If this does not match basic_block_live_at_end,
+ that must be updated, and the block must be rescanned. */
+ regset *basic_block_new_live_at_end;
+ /* For each basic block, a bitmask of regs
+ whose liveness at the end of the basic block
+ can make a difference in which regs are live on entry to the block.
+ These are the regs that are set within the basic block,
+ possibly excluding those that are used after they are set. */
+ regset *basic_block_significant;
+ register int i;
+ rtx insn;
+
+ struct obstack flow_obstack;
+
+ gcc_obstack_init (&flow_obstack);
+
+ max_regno = nregs;
+
+ bzero (regs_ever_live, sizeof regs_ever_live);
+
+ /* Allocate and zero out many data structures
+ that will record the data from lifetime analysis. */
+
+ allocate_for_life_analysis ();
+
+ reg_next_use = (rtx *) alloca (nregs * sizeof (rtx));
+ bzero ((char *) reg_next_use, nregs * sizeof (rtx));
+
+ /* Set up several regset-vectors used internally within this function.
+ Their meanings are documented above, with their declarations. */
+
+ basic_block_live_at_end
+ = (regset *) alloca (n_basic_blocks * sizeof (regset));
+
+ /* Don't use alloca since that leads to a crash rather than an error message
+ if there isn't enough space.
+ Don't use oballoc since we may need to allocate other things during
+ this function on the temporary obstack. */
+ tem = (regset) obstack_alloc (&flow_obstack, n_basic_blocks * regset_bytes);
+ bzero ((char *) tem, n_basic_blocks * regset_bytes);
+ init_regset_vector (basic_block_live_at_end, tem,
+ n_basic_blocks, regset_bytes);
+
+ basic_block_new_live_at_end
+ = (regset *) alloca (n_basic_blocks * sizeof (regset));
+ tem = (regset) obstack_alloc (&flow_obstack, n_basic_blocks * regset_bytes);
+ bzero ((char *) tem, n_basic_blocks * regset_bytes);
+ init_regset_vector (basic_block_new_live_at_end, tem,
+ n_basic_blocks, regset_bytes);
+
+ basic_block_significant
+ = (regset *) alloca (n_basic_blocks * sizeof (regset));
+ tem = (regset) obstack_alloc (&flow_obstack, n_basic_blocks * regset_bytes);
+ bzero ((char *) tem, n_basic_blocks * regset_bytes);
+ init_regset_vector (basic_block_significant, tem,
+ n_basic_blocks, regset_bytes);
+
+ /* Record which insns refer to any volatile memory
+ or for any reason can't be deleted just because they are dead stores.
+ Also, delete any insns that copy a register to itself. */
+
+ for (insn = f; insn; insn = NEXT_INSN (insn))
+ {
+ enum rtx_code code1 = GET_CODE (insn);
+ if (code1 == CALL_INSN)
+ INSN_VOLATILE (insn) = 1;
+ else if (code1 == INSN || code1 == JUMP_INSN)
+ {
+ /* Delete (in effect) any obvious no-op moves. */
+ if (GET_CODE (PATTERN (insn)) == SET
+ && GET_CODE (SET_DEST (PATTERN (insn))) == REG
+ && GET_CODE (SET_SRC (PATTERN (insn))) == REG
+ && REGNO (SET_DEST (PATTERN (insn))) ==
+ REGNO (SET_SRC (PATTERN (insn)))
+ /* Insns carrying these notes are useful later on. */
+ && ! find_reg_note (insn, REG_EQUAL, NULL_RTX))
+ {
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ }
+ else if (GET_CODE (PATTERN (insn)) == PARALLEL)
+ {
+ /* If nothing but SETs of registers to themselves,
+ this insn can also be deleted. */
+ for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
+ {
+ rtx tem = XVECEXP (PATTERN (insn), 0, i);
+
+ if (GET_CODE (tem) == USE
+ || GET_CODE (tem) == CLOBBER)
+ continue;
+
+ if (GET_CODE (tem) != SET
+ || GET_CODE (SET_DEST (tem)) != REG
+ || GET_CODE (SET_SRC (tem)) != REG
+ || REGNO (SET_DEST (tem)) != REGNO (SET_SRC (tem)))
+ break;
+ }
+
+ if (i == XVECLEN (PATTERN (insn), 0)
+ /* Insns carrying these notes are useful later on. */
+ && ! find_reg_note (insn, REG_EQUAL, NULL_RTX))
+ {
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ }
+ else
+ INSN_VOLATILE (insn) = volatile_refs_p (PATTERN (insn));
+ }
+ else if (GET_CODE (PATTERN (insn)) != USE)
+ INSN_VOLATILE (insn) = volatile_refs_p (PATTERN (insn));
+ /* A SET that makes space on the stack cannot be dead.
+ (Such SETs occur only for allocating variable-size data,
+ so they will always have a PLUS or MINUS according to the
+ direction of stack growth.)
+ Even if this function never uses this stack pointer value,
+ signal handlers do! */
+ else if (code1 == INSN && GET_CODE (PATTERN (insn)) == SET
+ && SET_DEST (PATTERN (insn)) == stack_pointer_rtx
+#ifdef STACK_GROWS_DOWNWARD
+ && GET_CODE (SET_SRC (PATTERN (insn))) == MINUS
+#else
+ && GET_CODE (SET_SRC (PATTERN (insn))) == PLUS
+#endif
+ && XEXP (SET_SRC (PATTERN (insn)), 0) == stack_pointer_rtx)
+ INSN_VOLATILE (insn) = 1;
+ }
+ }
+
+ if (n_basic_blocks > 0)
+#ifdef EXIT_IGNORE_STACK
+ if (! EXIT_IGNORE_STACK
+ || (! FRAME_POINTER_REQUIRED && flag_omit_frame_pointer))
+#endif
+ {
+ /* If exiting needs the right stack value,
+ consider the stack pointer live at the end of the function. */
+ basic_block_live_at_end[n_basic_blocks - 1]
+ [STACK_POINTER_REGNUM / REGSET_ELT_BITS]
+ |= (REGSET_ELT_TYPE) 1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS);
+ basic_block_new_live_at_end[n_basic_blocks - 1]
+ [STACK_POINTER_REGNUM / REGSET_ELT_BITS]
+ |= (REGSET_ELT_TYPE) 1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS);
+ }
+
+ /* Mark the frame pointer is needed at the end of the function. If
+ we end up eliminating it, it will be removed from the live list
+ of each basic block by reload. */
+
+ if (n_basic_blocks > 0)
+ {
+ basic_block_live_at_end[n_basic_blocks - 1]
+ [FRAME_POINTER_REGNUM / REGSET_ELT_BITS]
+ |= (REGSET_ELT_TYPE) 1 << (FRAME_POINTER_REGNUM % REGSET_ELT_BITS);
+ basic_block_new_live_at_end[n_basic_blocks - 1]
+ [FRAME_POINTER_REGNUM / REGSET_ELT_BITS]
+ |= (REGSET_ELT_TYPE) 1 << (FRAME_POINTER_REGNUM % REGSET_ELT_BITS);
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+ /* If they are different, also mark the hard frame pointer as live */
+ basic_block_live_at_end[n_basic_blocks - 1]
+ [HARD_FRAME_POINTER_REGNUM / REGSET_ELT_BITS]
+ |= (REGSET_ELT_TYPE) 1 << (HARD_FRAME_POINTER_REGNUM
+ % REGSET_ELT_BITS);
+ basic_block_new_live_at_end[n_basic_blocks - 1]
+ [HARD_FRAME_POINTER_REGNUM / REGSET_ELT_BITS]
+ |= (REGSET_ELT_TYPE) 1 << (HARD_FRAME_POINTER_REGNUM
+ % REGSET_ELT_BITS);
+#endif
+ }
+
+ /* Mark all global registers as being live at the end of the function
+ since they may be referenced by our caller. */
+
+ if (n_basic_blocks > 0)
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (global_regs[i])
+ {
+ basic_block_live_at_end[n_basic_blocks - 1]
+ [i / REGSET_ELT_BITS]
+ |= (REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS);
+ basic_block_new_live_at_end[n_basic_blocks - 1]
+ [i / REGSET_ELT_BITS]
+ |= (REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS);
+ }
+
+ /* Propagate life info through the basic blocks
+ around the graph of basic blocks.
+
+ This is a relaxation process: each time a new register
+ is live at the end of the basic block, we must scan the block
+ to determine which registers are, as a consequence, live at the beginning
+ of that block. These registers must then be marked live at the ends
+ of all the blocks that can transfer control to that block.
+ The process continues until it reaches a fixed point. */
+
+ first_pass = 1;
+ changed = 1;
+ while (changed)
+ {
+ changed = 0;
+ for (i = n_basic_blocks - 1; i >= 0; i--)
+ {
+ int consider = first_pass;
+ int must_rescan = first_pass;
+ register int j;
+
+ if (!first_pass)
+ {
+ /* Set CONSIDER if this block needs thinking about at all
+ (that is, if the regs live now at the end of it
+ are not the same as were live at the end of it when
+ we last thought about it).
+ Set must_rescan if it needs to be thought about
+ instruction by instruction (that is, if any additional
+ reg that is live at the end now but was not live there before
+ is one of the significant regs of this basic block). */
+
+ for (j = 0; j < regset_size; j++)
+ {
+ register REGSET_ELT_TYPE x
+ = (basic_block_new_live_at_end[i][j]
+ & ~basic_block_live_at_end[i][j]);
+ if (x)
+ consider = 1;
+ if (x & basic_block_significant[i][j])
+ {
+ must_rescan = 1;
+ consider = 1;
+ break;
+ }
+ }
+
+ if (! consider)
+ continue;
+ }
+
+ /* The live_at_start of this block may be changing,
+ so another pass will be required after this one. */
+ changed = 1;
+
+ if (! must_rescan)
+ {
+ /* No complete rescan needed;
+ just record those variables newly known live at end
+ as live at start as well. */
+ for (j = 0; j < regset_size; j++)
+ {
+ register REGSET_ELT_TYPE x
+ = (basic_block_new_live_at_end[i][j]
+ & ~basic_block_live_at_end[i][j]);
+ basic_block_live_at_start[i][j] |= x;
+ basic_block_live_at_end[i][j] |= x;
+ }
+ }
+ else
+ {
+ /* Update the basic_block_live_at_start
+ by propagation backwards through the block. */
+ bcopy ((char *) basic_block_new_live_at_end[i],
+ (char *) basic_block_live_at_end[i], regset_bytes);
+ bcopy ((char *) basic_block_live_at_end[i],
+ (char *) basic_block_live_at_start[i], regset_bytes);
+ propagate_block (basic_block_live_at_start[i],
+ basic_block_head[i], basic_block_end[i], 0,
+ first_pass ? basic_block_significant[i]
+ : (regset) 0,
+ i);
+ }
+
+ {
+ register rtx jump, head;
+ /* Update the basic_block_new_live_at_end's of the block
+ that falls through into this one (if any). */
+ head = basic_block_head[i];
+ jump = PREV_INSN (head);
+ if (basic_block_drops_in[i])
+ {
+ register int from_block = BLOCK_NUM (jump);
+ register int j;
+ for (j = 0; j < regset_size; j++)
+ basic_block_new_live_at_end[from_block][j]
+ |= basic_block_live_at_start[i][j];
+ }
+ /* Update the basic_block_new_live_at_end's of
+ all the blocks that jump to this one. */
+ if (GET_CODE (head) == CODE_LABEL)
+ for (jump = LABEL_REFS (head);
+ jump != head;
+ jump = LABEL_NEXTREF (jump))
+ {
+ register int from_block = BLOCK_NUM (CONTAINING_INSN (jump));
+ register int j;
+ for (j = 0; j < regset_size; j++)
+ basic_block_new_live_at_end[from_block][j]
+ |= basic_block_live_at_start[i][j];
+ }
+ }
+#ifdef USE_C_ALLOCA
+ alloca (0);
+#endif
+ }
+ first_pass = 0;
+ }
+
+ /* The only pseudos that are live at the beginning of the function are
+ those that were not set anywhere in the function. local-alloc doesn't
+ know how to handle these correctly, so mark them as not local to any
+ one basic block. */
+
+ if (n_basic_blocks > 0)
+ for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+ if (basic_block_live_at_start[0][i / REGSET_ELT_BITS]
+ & ((REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS)))
+ reg_basic_block[i] = REG_BLOCK_GLOBAL;
+
+ /* Now the life information is accurate.
+ Make one more pass over each basic block
+ to delete dead stores, create autoincrement addressing
+ and record how many times each register is used, is set, or dies.
+
+ To save time, we operate directly in basic_block_live_at_end[i],
+ thus destroying it (in fact, converting it into a copy of
+ basic_block_live_at_start[i]). This is ok now because
+ basic_block_live_at_end[i] is no longer used past this point. */
+
+ max_scratch = 0;
+
+ for (i = 0; i < n_basic_blocks; i++)
+ {
+ propagate_block (basic_block_live_at_end[i],
+ basic_block_head[i], basic_block_end[i], 1,
+ (regset) 0, i);
+#ifdef USE_C_ALLOCA
+ alloca (0);
+#endif
+ }
+
+#if 0
+ /* Something live during a setjmp should not be put in a register
+ on certain machines which restore regs from stack frames
+ rather than from the jmpbuf.
+ But we don't need to do this for the user's variables, since
+ ANSI says only volatile variables need this. */
+#ifdef LONGJMP_RESTORE_FROM_STACK
+ for (i = FIRST_PSEUDO_REGISTER; i < nregs; i++)
+ if (regs_live_at_setjmp[i / REGSET_ELT_BITS]
+ & ((REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS))
+ && regno_reg_rtx[i] != 0 && ! REG_USERVAR_P (regno_reg_rtx[i]))
+ {
+ reg_live_length[i] = -1;
+ reg_basic_block[i] = -1;
+ }
+#endif
+#endif
+
+ /* We have a problem with any pseudoreg that
+ lives across the setjmp. ANSI says that if a
+ user variable does not change in value
+ between the setjmp and the longjmp, then the longjmp preserves it.
+ This includes longjmp from a place where the pseudo appears dead.
+ (In principle, the value still exists if it is in scope.)
+ If the pseudo goes in a hard reg, some other value may occupy
+ that hard reg where this pseudo is dead, thus clobbering the pseudo.
+ Conclusion: such a pseudo must not go in a hard reg. */
+ for (i = FIRST_PSEUDO_REGISTER; i < nregs; i++)
+ if ((regs_live_at_setjmp[i / REGSET_ELT_BITS]
+ & ((REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS)))
+ && regno_reg_rtx[i] != 0)
+ {
+ reg_live_length[i] = -1;
+ reg_basic_block[i] = -1;
+ }
+
+ obstack_free (&flow_obstack, NULL_PTR);
+}
+
+/* Subroutines of life analysis. */
+
+/* Allocate the permanent data structures that represent the results
+ of life analysis. Not static since used also for stupid life analysis. */
+
+void
+allocate_for_life_analysis ()
+{
+ register int i;
+ register regset tem;
+
+ regset_size = ((max_regno + REGSET_ELT_BITS - 1) / REGSET_ELT_BITS);
+ regset_bytes = regset_size * sizeof (*(regset)0);
+
+ reg_n_refs = (int *) oballoc (max_regno * sizeof (int));
+ bzero ((char *) reg_n_refs, max_regno * sizeof (int));
+
+ reg_n_sets = (short *) oballoc (max_regno * sizeof (short));
+ bzero ((char *) reg_n_sets, max_regno * sizeof (short));
+
+ reg_n_deaths = (short *) oballoc (max_regno * sizeof (short));
+ bzero ((char *) reg_n_deaths, max_regno * sizeof (short));
+
+ reg_live_length = (int *) oballoc (max_regno * sizeof (int));
+ bzero ((char *) reg_live_length, max_regno * sizeof (int));
+
+ reg_n_calls_crossed = (int *) oballoc (max_regno * sizeof (int));
+ bzero ((char *) reg_n_calls_crossed, max_regno * sizeof (int));
+
+ reg_basic_block = (int *) oballoc (max_regno * sizeof (int));
+ for (i = 0; i < max_regno; i++)
+ reg_basic_block[i] = REG_BLOCK_UNKNOWN;
+
+ basic_block_live_at_start
+ = (regset *) oballoc (n_basic_blocks * sizeof (regset));
+ tem = (regset) oballoc (n_basic_blocks * regset_bytes);
+ bzero ((char *) tem, n_basic_blocks * regset_bytes);
+ init_regset_vector (basic_block_live_at_start, tem,
+ n_basic_blocks, regset_bytes);
+
+ regs_live_at_setjmp = (regset) oballoc (regset_bytes);
+ bzero ((char *) regs_live_at_setjmp, regset_bytes);
+}
+
+/* Make each element of VECTOR point at a regset,
+ taking the space for all those regsets from SPACE.
+ SPACE is of type regset, but it is really as long as NELTS regsets.
+ BYTES_PER_ELT is the number of bytes in one regset. */
+
+static void
+init_regset_vector (vector, space, nelts, bytes_per_elt)
+ regset *vector;
+ regset space;
+ int nelts;
+ int bytes_per_elt;
+{
+ register int i;
+ register regset p = space;
+
+ for (i = 0; i < nelts; i++)
+ {
+ vector[i] = p;
+ p += bytes_per_elt / sizeof (*p);
+ }
+}
+
+/* Compute the registers live at the beginning of a basic block
+ from those live at the end.
+
+ When called, OLD contains those live at the end.
+ On return, it contains those live at the beginning.
+ FIRST and LAST are the first and last insns of the basic block.
+
+ FINAL is nonzero if we are doing the final pass which is not
+ for computing the life info (since that has already been done)
+ but for acting on it. On this pass, we delete dead stores,
+ set up the logical links and dead-variables lists of instructions,
+ and merge instructions for autoincrement and autodecrement addresses.
+
+ SIGNIFICANT is nonzero only the first time for each basic block.
+ If it is nonzero, it points to a regset in which we store
+ a 1 for each register that is set within the block.
+
+ BNUM is the number of the basic block. */
+
+static void
+propagate_block (old, first, last, final, significant, bnum)
+ register regset old;
+ rtx first;
+ rtx last;
+ int final;
+ regset significant;
+ int bnum;
+{
+ register rtx insn;
+ rtx prev;
+ regset live;
+ regset dead;
+
+ /* The following variables are used only if FINAL is nonzero. */
+ /* This vector gets one element for each reg that has been live
+ at any point in the basic block that has been scanned so far.
+ SOMETIMES_MAX says how many elements are in use so far.
+ In each element, OFFSET is the byte-number within a regset
+ for the register described by the element, and BIT is a mask
+ for that register's bit within the byte. */
+ register struct sometimes { short offset; short bit; } *regs_sometimes_live;
+ int sometimes_max = 0;
+ /* This regset has 1 for each reg that we have seen live so far.
+ It and REGS_SOMETIMES_LIVE are updated together. */
+ regset maxlive;
+
+ /* The loop depth may change in the middle of a basic block. Since we
+ scan from end to beginning, we start with the depth at the end of the
+ current basic block, and adjust as we pass ends and starts of loops. */
+ loop_depth = basic_block_loop_depth[bnum];
+
+ dead = (regset) alloca (regset_bytes);
+ live = (regset) alloca (regset_bytes);
+
+ cc0_live = 0;
+ last_mem_set = 0;
+
+ /* Include any notes at the end of the block in the scan.
+ This is in case the block ends with a call to setjmp. */
+
+ while (NEXT_INSN (last) != 0 && GET_CODE (NEXT_INSN (last)) == NOTE)
+ {
+ /* Look for loop boundaries, we are going forward here. */
+ last = NEXT_INSN (last);
+ if (NOTE_LINE_NUMBER (last) == NOTE_INSN_LOOP_BEG)
+ loop_depth++;
+ else if (NOTE_LINE_NUMBER (last) == NOTE_INSN_LOOP_END)
+ loop_depth--;
+ }
+
+ if (final)
+ {
+ register int i, offset;
+ REGSET_ELT_TYPE bit;
+
+ num_scratch = 0;
+ maxlive = (regset) alloca (regset_bytes);
+ bcopy ((char *) old, (char *) maxlive, regset_bytes);
+ regs_sometimes_live
+ = (struct sometimes *) alloca (max_regno * sizeof (struct sometimes));
+
+ /* Process the regs live at the end of the block.
+ Enter them in MAXLIVE and REGS_SOMETIMES_LIVE.
+ Also mark them as not local to any one basic block. */
+
+ for (offset = 0, i = 0; offset < regset_size; offset++)
+ for (bit = 1; bit; bit <<= 1, i++)
+ {
+ if (i == max_regno)
+ break;
+ if (old[offset] & bit)
+ {
+ reg_basic_block[i] = REG_BLOCK_GLOBAL;
+ regs_sometimes_live[sometimes_max].offset = offset;
+ regs_sometimes_live[sometimes_max].bit = i % REGSET_ELT_BITS;
+ sometimes_max++;
+ }
+ }
+ }
+
+ /* Scan the block an insn at a time from end to beginning. */
+
+ for (insn = last; ; insn = prev)
+ {
+ prev = PREV_INSN (insn);
+
+ /* Look for loop boundaries, remembering that we are going backwards. */
+ if (GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
+ loop_depth++;
+ else if (GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
+ loop_depth--;
+
+ /* If we have LOOP_DEPTH == 0, there has been a bookkeeping error.
+ Abort now rather than setting register status incorrectly. */
+ if (loop_depth == 0)
+ abort ();
+
+ /* If this is a call to `setjmp' et al,
+ warn if any non-volatile datum is live. */
+
+ if (final && GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)
+ {
+ int i;
+ for (i = 0; i < regset_size; i++)
+ regs_live_at_setjmp[i] |= old[i];
+ }
+
+ /* Update the life-status of regs for this insn.
+ First DEAD gets which regs are set in this insn
+ then LIVE gets which regs are used in this insn.
+ Then the regs live before the insn
+ are those live after, with DEAD regs turned off,
+ and then LIVE regs turned on. */
+
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ register int i;
+ rtx note = find_reg_note (insn, REG_RETVAL, NULL_RTX);
+ int insn_is_dead
+ = (insn_dead_p (PATTERN (insn), old, 0)
+ /* Don't delete something that refers to volatile storage! */
+ && ! INSN_VOLATILE (insn));
+ int libcall_is_dead
+ = (insn_is_dead && note != 0
+ && libcall_dead_p (PATTERN (insn), old, note, insn));
+
+ /* If an instruction consists of just dead store(s) on final pass,
+ "delete" it by turning it into a NOTE of type NOTE_INSN_DELETED.
+ We could really delete it with delete_insn, but that
+ can cause trouble for first or last insn in a basic block. */
+ if (final && insn_is_dead)
+ {
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+
+ /* CC0 is now known to be dead. Either this insn used it,
+ in which case it doesn't anymore, or clobbered it,
+ so the next insn can't use it. */
+ cc0_live = 0;
+
+ /* If this insn is copying the return value from a library call,
+ delete the entire library call. */
+ if (libcall_is_dead)
+ {
+ rtx first = XEXP (note, 0);
+ rtx p = insn;
+ while (INSN_DELETED_P (first))
+ first = NEXT_INSN (first);
+ while (p != first)
+ {
+ p = PREV_INSN (p);
+ PUT_CODE (p, NOTE);
+ NOTE_LINE_NUMBER (p) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (p) = 0;
+ }
+ }
+ goto flushed;
+ }
+
+ for (i = 0; i < regset_size; i++)
+ {
+ dead[i] = 0; /* Faster than bzero here */
+ live[i] = 0; /* since regset_size is usually small */
+ }
+
+ /* See if this is an increment or decrement that can be
+ merged into a following memory address. */
+#ifdef AUTO_INC_DEC
+ {
+ register rtx x = PATTERN (insn);
+ /* Does this instruction increment or decrement a register? */
+ if (final && GET_CODE (x) == SET
+ && GET_CODE (SET_DEST (x)) == REG
+ && (GET_CODE (SET_SRC (x)) == PLUS
+ || GET_CODE (SET_SRC (x)) == MINUS)
+ && XEXP (SET_SRC (x), 0) == SET_DEST (x)
+ && GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT
+ /* Ok, look for a following memory ref we can combine with.
+ If one is found, change the memory ref to a PRE_INC
+ or PRE_DEC, cancel this insn, and return 1.
+ Return 0 if nothing has been done. */
+ && try_pre_increment_1 (insn))
+ goto flushed;
+ }
+#endif /* AUTO_INC_DEC */
+
+ /* If this is not the final pass, and this insn is copying the
+ value of a library call and it's dead, don't scan the
+ insns that perform the library call, so that the call's
+ arguments are not marked live. */
+ if (libcall_is_dead)
+ {
+ /* Mark the dest reg as `significant'. */
+ mark_set_regs (old, dead, PATTERN (insn), NULL_RTX, significant);
+
+ insn = XEXP (note, 0);
+ prev = PREV_INSN (insn);
+ }
+ else if (GET_CODE (PATTERN (insn)) == SET
+ && SET_DEST (PATTERN (insn)) == stack_pointer_rtx
+ && GET_CODE (SET_SRC (PATTERN (insn))) == PLUS
+ && XEXP (SET_SRC (PATTERN (insn)), 0) == stack_pointer_rtx
+ && GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 1)) == CONST_INT)
+ /* We have an insn to pop a constant amount off the stack.
+ (Such insns use PLUS regardless of the direction of the stack,
+ and any insn to adjust the stack by a constant is always a pop.)
+ These insns, if not dead stores, have no effect on life. */
+ ;
+ else
+ {
+ /* LIVE gets the regs used in INSN;
+ DEAD gets those set by it. Dead insns don't make anything
+ live. */
+
+ mark_set_regs (old, dead, PATTERN (insn),
+ final ? insn : NULL_RTX, significant);
+
+ /* If an insn doesn't use CC0, it becomes dead since we
+ assume that every insn clobbers it. So show it dead here;
+ mark_used_regs will set it live if it is referenced. */
+ cc0_live = 0;
+
+ if (! insn_is_dead)
+ mark_used_regs (old, live, PATTERN (insn), final, insn);
+
+ /* Sometimes we may have inserted something before INSN (such as
+ a move) when we make an auto-inc. So ensure we will scan
+ those insns. */
+#ifdef AUTO_INC_DEC
+ prev = PREV_INSN (insn);
+#endif
+
+ if (! insn_is_dead && GET_CODE (insn) == CALL_INSN)
+ {
+ register int i;
+
+ rtx note;
+
+ for (note = CALL_INSN_FUNCTION_USAGE (insn);
+ note;
+ note = XEXP (note, 1))
+ if (GET_CODE (XEXP (note, 0)) == USE)
+ mark_used_regs (old, live, SET_DEST (XEXP (note, 0)),
+ final, insn);
+
+ /* Each call clobbers all call-clobbered regs that are not
+ global. Note that the function-value reg is a
+ call-clobbered reg, and mark_set_regs has already had
+ a chance to handle it. */
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (call_used_regs[i] && ! global_regs[i])
+ dead[i / REGSET_ELT_BITS]
+ |= ((REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS));
+
+ /* The stack ptr is used (honorarily) by a CALL insn. */
+ live[STACK_POINTER_REGNUM / REGSET_ELT_BITS]
+ |= ((REGSET_ELT_TYPE) 1
+ << (STACK_POINTER_REGNUM % REGSET_ELT_BITS));
+
+ /* Calls may also reference any of the global registers,
+ so they are made live. */
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (global_regs[i])
+ live[i / REGSET_ELT_BITS]
+ |= ((REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS));
+
+ /* Calls also clobber memory. */
+ last_mem_set = 0;
+ }
+
+ /* Update OLD for the registers used or set. */
+ for (i = 0; i < regset_size; i++)
+ {
+ old[i] &= ~dead[i];
+ old[i] |= live[i];
+ }
+
+ if (GET_CODE (insn) == CALL_INSN && final)
+ {
+ /* Any regs live at the time of a call instruction
+ must not go in a register clobbered by calls.
+ Find all regs now live and record this for them. */
+
+ register struct sometimes *p = regs_sometimes_live;
+
+ for (i = 0; i < sometimes_max; i++, p++)
+ if (old[p->offset] & ((REGSET_ELT_TYPE) 1 << p->bit))
+ reg_n_calls_crossed[p->offset * REGSET_ELT_BITS + p->bit]+= 1;
+ }
+ }
+
+ /* On final pass, add any additional sometimes-live regs
+ into MAXLIVE and REGS_SOMETIMES_LIVE.
+ Also update counts of how many insns each reg is live at. */
+
+ if (final)
+ {
+ for (i = 0; i < regset_size; i++)
+ {
+ register REGSET_ELT_TYPE diff = live[i] & ~maxlive[i];
+
+ if (diff)
+ {
+ register int regno;
+ maxlive[i] |= diff;
+ for (regno = 0; diff && regno < REGSET_ELT_BITS; regno++)
+ if (diff & ((REGSET_ELT_TYPE) 1 << regno))
+ {
+ regs_sometimes_live[sometimes_max].offset = i;
+ regs_sometimes_live[sometimes_max].bit = regno;
+ diff &= ~ ((REGSET_ELT_TYPE) 1 << regno);
+ sometimes_max++;
+ }
+ }
+ }
+
+ {
+ register struct sometimes *p = regs_sometimes_live;
+ for (i = 0; i < sometimes_max; i++, p++)
+ {
+ if (old[p->offset] & ((REGSET_ELT_TYPE) 1 << p->bit))
+ reg_live_length[p->offset * REGSET_ELT_BITS + p->bit]++;
+ }
+ }
+ }
+ }
+ flushed: ;
+ if (insn == first)
+ break;
+ }
+
+ if (num_scratch > max_scratch)
+ max_scratch = num_scratch;
+}
+
+/* Return 1 if X (the body of an insn, or part of it) is just dead stores
+ (SET expressions whose destinations are registers dead after the insn).
+ NEEDED is the regset that says which regs are alive after the insn.
+
+ Unless CALL_OK is non-zero, an insn is needed if it contains a CALL. */
+
+static int
+insn_dead_p (x, needed, call_ok)
+ rtx x;
+ regset needed;
+ int call_ok;
+{
+ register RTX_CODE code = GET_CODE (x);
+ /* If setting something that's a reg or part of one,
+ see if that register's altered value will be live. */
+
+ if (code == SET)
+ {
+ register rtx r = SET_DEST (x);
+ /* A SET that is a subroutine call cannot be dead. */
+ if (! call_ok && GET_CODE (SET_SRC (x)) == CALL)
+ return 0;
+
+#ifdef HAVE_cc0
+ if (GET_CODE (r) == CC0)
+ return ! cc0_live;
+#endif
+
+ if (GET_CODE (r) == MEM && last_mem_set && ! MEM_VOLATILE_P (r)
+ && rtx_equal_p (r, last_mem_set))
+ return 1;
+
+ while (GET_CODE (r) == SUBREG
+ || GET_CODE (r) == STRICT_LOW_PART
+ || GET_CODE (r) == ZERO_EXTRACT
+ || GET_CODE (r) == SIGN_EXTRACT)
+ r = SUBREG_REG (r);
+
+ if (GET_CODE (r) == REG)
+ {
+ register int regno = REGNO (r);
+ register int offset = regno / REGSET_ELT_BITS;
+ register REGSET_ELT_TYPE bit
+ = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS);
+
+ /* Don't delete insns to set global regs. */
+ if ((regno < FIRST_PSEUDO_REGISTER && global_regs[regno])
+ /* Make sure insns to set frame pointer aren't deleted. */
+ || regno == FRAME_POINTER_REGNUM
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+ || regno == HARD_FRAME_POINTER_REGNUM
+#endif
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ /* Make sure insns to set arg pointer are never deleted
+ (if the arg pointer isn't fixed, there will be a USE for
+ it, so we can treat it normally). */
+ || (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
+#endif
+ || (needed[offset] & bit) != 0)
+ return 0;
+
+ /* If this is a hard register, verify that subsequent words are
+ not needed. */
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ int n = HARD_REGNO_NREGS (regno, GET_MODE (r));
+
+ while (--n > 0)
+ if ((needed[(regno + n) / REGSET_ELT_BITS]
+ & ((REGSET_ELT_TYPE) 1
+ << ((regno + n) % REGSET_ELT_BITS))) != 0)
+ return 0;
+ }
+
+ return 1;
+ }
+ }
+ /* If performing several activities,
+ insn is dead if each activity is individually dead.
+ Also, CLOBBERs and USEs can be ignored; a CLOBBER or USE
+ that's inside a PARALLEL doesn't make the insn worth keeping. */
+ else if (code == PARALLEL)
+ {
+ register int i = XVECLEN (x, 0);
+ for (i--; i >= 0; i--)
+ {
+ rtx elt = XVECEXP (x, 0, i);
+ if (!insn_dead_p (elt, needed, call_ok)
+ && GET_CODE (elt) != CLOBBER
+ && GET_CODE (elt) != USE)
+ return 0;
+ }
+ return 1;
+ }
+ /* We do not check CLOBBER or USE here.
+ An insn consisting of just a CLOBBER or just a USE
+ should not be deleted. */
+ return 0;
+}
+
+/* If X is the pattern of the last insn in a libcall, and assuming X is dead,
+ return 1 if the entire library call is dead.
+ This is true if X copies a register (hard or pseudo)
+ and if the hard return reg of the call insn is dead.
+ (The caller should have tested the destination of X already for death.)
+
+ If this insn doesn't just copy a register, then we don't
+ have an ordinary libcall. In that case, cse could not have
+ managed to substitute the source for the dest later on,
+ so we can assume the libcall is dead.
+
+ NEEDED is the bit vector of pseudoregs live before this insn.
+ NOTE is the REG_RETVAL note of the insn. INSN is the insn itself. */
+
+static int
+libcall_dead_p (x, needed, note, insn)
+ rtx x;
+ regset needed;
+ rtx note;
+ rtx insn;
+{
+ register RTX_CODE code = GET_CODE (x);
+
+ if (code == SET)
+ {
+ register rtx r = SET_SRC (x);
+ if (GET_CODE (r) == REG)
+ {
+ rtx call = XEXP (note, 0);
+ register int i;
+
+ /* Find the call insn. */
+ while (call != insn && GET_CODE (call) != CALL_INSN)
+ call = NEXT_INSN (call);
+
+ /* If there is none, do nothing special,
+ since ordinary death handling can understand these insns. */
+ if (call == insn)
+ return 0;
+
+ /* See if the hard reg holding the value is dead.
+ If this is a PARALLEL, find the call within it. */
+ call = PATTERN (call);
+ if (GET_CODE (call) == PARALLEL)
+ {
+ for (i = XVECLEN (call, 0) - 1; i >= 0; i--)
+ if (GET_CODE (XVECEXP (call, 0, i)) == SET
+ && GET_CODE (SET_SRC (XVECEXP (call, 0, i))) == CALL)
+ break;
+
+ /* This may be a library call that is returning a value
+ via invisible pointer. Do nothing special, since
+ ordinary death handling can understand these insns. */
+ if (i < 0)
+ return 0;
+
+ call = XVECEXP (call, 0, i);
+ }
+
+ return insn_dead_p (call, needed, 1);
+ }
+ }
+ return 1;
+}
+
+/* Return 1 if register REGNO was used before it was set.
+ In other words, if it is live at function entry.
+ Don't count global regster variables, though. */
+
+int
+regno_uninitialized (regno)
+ int regno;
+{
+ if (n_basic_blocks == 0
+ || (regno < FIRST_PSEUDO_REGISTER && global_regs[regno]))
+ return 0;
+
+ return (basic_block_live_at_start[0][regno / REGSET_ELT_BITS]
+ & ((REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS)));
+}
+
+/* 1 if register REGNO was alive at a place where `setjmp' was called
+ and was set more than once or is an argument.
+ Such regs may be clobbered by `longjmp'. */
+
+int
+regno_clobbered_at_setjmp (regno)
+ int regno;
+{
+ if (n_basic_blocks == 0)
+ return 0;
+
+ return ((reg_n_sets[regno] > 1
+ || (basic_block_live_at_start[0][regno / REGSET_ELT_BITS]
+ & ((REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS))))
+ && (regs_live_at_setjmp[regno / REGSET_ELT_BITS]
+ & ((REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS))));
+}
+
+/* Process the registers that are set within X.
+ Their bits are set to 1 in the regset DEAD,
+ because they are dead prior to this insn.
+
+ If INSN is nonzero, it is the insn being processed
+ and the fact that it is nonzero implies this is the FINAL pass
+ in propagate_block. In this case, various info about register
+ usage is stored, LOG_LINKS fields of insns are set up. */
+
+static void
+mark_set_regs (needed, dead, x, insn, significant)
+ regset needed;
+ regset dead;
+ rtx x;
+ rtx insn;
+ regset significant;
+{
+ register RTX_CODE code = GET_CODE (x);
+
+ if (code == SET || code == CLOBBER)
+ mark_set_1 (needed, dead, x, insn, significant);
+ else if (code == PARALLEL)
+ {
+ register int i;
+ for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
+ {
+ code = GET_CODE (XVECEXP (x, 0, i));
+ if (code == SET || code == CLOBBER)
+ mark_set_1 (needed, dead, XVECEXP (x, 0, i), insn, significant);
+ }
+ }
+}
+
+/* Process a single SET rtx, X. */
+
+static void
+mark_set_1 (needed, dead, x, insn, significant)
+ regset needed;
+ regset dead;
+ rtx x;
+ rtx insn;
+ regset significant;
+{
+ register int regno;
+ register rtx reg = SET_DEST (x);
+
+ /* Modifying just one hardware register of a multi-reg value
+ or just a byte field of a register
+ does not mean the value from before this insn is now dead.
+ But it does mean liveness of that register at the end of the block
+ is significant.
+
+ Within mark_set_1, however, we treat it as if the register is
+ indeed modified. mark_used_regs will, however, also treat this
+ register as being used. Thus, we treat these insns as setting a
+ new value for the register as a function of its old value. This
+ cases LOG_LINKS to be made appropriately and this will help combine. */
+
+ while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == ZERO_EXTRACT
+ || GET_CODE (reg) == SIGN_EXTRACT
+ || GET_CODE (reg) == STRICT_LOW_PART)
+ reg = XEXP (reg, 0);
+
+ /* If we are writing into memory or into a register mentioned in the
+ address of the last thing stored into memory, show we don't know
+ what the last store was. If we are writing memory, save the address
+ unless it is volatile. */
+ if (GET_CODE (reg) == MEM
+ || (GET_CODE (reg) == REG
+ && last_mem_set != 0 && reg_overlap_mentioned_p (reg, last_mem_set)))
+ last_mem_set = 0;
+
+ if (GET_CODE (reg) == MEM && ! side_effects_p (reg)
+ /* There are no REG_INC notes for SP, so we can't assume we'll see
+ everything that invalidates it. To be safe, don't eliminate any
+ stores though SP; none of them should be redundant anyway. */
+ && ! reg_mentioned_p (stack_pointer_rtx, reg))
+ last_mem_set = reg;
+
+ if (GET_CODE (reg) == REG
+ && (regno = REGNO (reg), regno != FRAME_POINTER_REGNUM)
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+ && regno != HARD_FRAME_POINTER_REGNUM
+#endif
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ && ! (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
+#endif
+ && ! (regno < FIRST_PSEUDO_REGISTER && global_regs[regno]))
+ /* && regno != STACK_POINTER_REGNUM) -- let's try without this. */
+ {
+ register int offset = regno / REGSET_ELT_BITS;
+ register REGSET_ELT_TYPE bit
+ = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS);
+ REGSET_ELT_TYPE all_needed = (needed[offset] & bit);
+ REGSET_ELT_TYPE some_needed = (needed[offset] & bit);
+
+ /* Mark it as a significant register for this basic block. */
+ if (significant)
+ significant[offset] |= bit;
+
+ /* Mark it as as dead before this insn. */
+ dead[offset] |= bit;
+
+ /* A hard reg in a wide mode may really be multiple registers.
+ If so, mark all of them just like the first. */
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ int n;
+
+ /* Nothing below is needed for the stack pointer; get out asap.
+ Eg, log links aren't needed, since combine won't use them. */
+ if (regno == STACK_POINTER_REGNUM)
+ return;
+
+ n = HARD_REGNO_NREGS (regno, GET_MODE (reg));
+ while (--n > 0)
+ {
+ if (significant)
+ significant[(regno + n) / REGSET_ELT_BITS]
+ |= (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS);
+ dead[(regno + n) / REGSET_ELT_BITS]
+ |= (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS);
+ some_needed
+ |= (needed[(regno + n) / REGSET_ELT_BITS]
+ & (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS));
+ all_needed
+ &= (needed[(regno + n) / REGSET_ELT_BITS]
+ & (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS));
+ }
+ }
+ /* Additional data to record if this is the final pass. */
+ if (insn)
+ {
+ register rtx y = reg_next_use[regno];
+ register int blocknum = BLOCK_NUM (insn);
+
+ /* The next use is no longer "next", since a store intervenes. */
+ reg_next_use[regno] = 0;
+
+ /* If this is a hard reg, record this function uses the reg. */
+
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ register int i;
+ int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (reg));
+
+ for (i = regno; i < endregno; i++)
+ {
+ regs_ever_live[i] = 1;
+ reg_n_sets[i]++;
+ }
+ }
+ else
+ {
+ /* Keep track of which basic blocks each reg appears in. */
+
+ if (reg_basic_block[regno] == REG_BLOCK_UNKNOWN)
+ reg_basic_block[regno] = blocknum;
+ else if (reg_basic_block[regno] != blocknum)
+ reg_basic_block[regno] = REG_BLOCK_GLOBAL;
+
+ /* Count (weighted) references, stores, etc. This counts a
+ register twice if it is modified, but that is correct. */
+ reg_n_sets[regno]++;
+
+ reg_n_refs[regno] += loop_depth;
+
+ /* The insns where a reg is live are normally counted
+ elsewhere, but we want the count to include the insn
+ where the reg is set, and the normal counting mechanism
+ would not count it. */
+ reg_live_length[regno]++;
+ }
+
+ if (all_needed)
+ {
+ /* Make a logical link from the next following insn
+ that uses this register, back to this insn.
+ The following insns have already been processed.
+
+ We don't build a LOG_LINK for hard registers containing
+ in ASM_OPERANDs. If these registers get replaced,
+ we might wind up changing the semantics of the insn,
+ even if reload can make what appear to be valid assignments
+ later. */
+ if (y && (BLOCK_NUM (y) == blocknum)
+ && (regno >= FIRST_PSEUDO_REGISTER
+ || asm_noperands (PATTERN (y)) < 0))
+ LOG_LINKS (y)
+ = gen_rtx (INSN_LIST, VOIDmode, insn, LOG_LINKS (y));
+ }
+ else if (! some_needed)
+ {
+ /* Note that dead stores have already been deleted when possible
+ If we get here, we have found a dead store that cannot
+ be eliminated (because the same insn does something useful).
+ Indicate this by marking the reg being set as dying here. */
+ REG_NOTES (insn)
+ = gen_rtx (EXPR_LIST, REG_UNUSED, reg, REG_NOTES (insn));
+ reg_n_deaths[REGNO (reg)]++;
+ }
+ else
+ {
+ /* This is a case where we have a multi-word hard register
+ and some, but not all, of the words of the register are
+ needed in subsequent insns. Write REG_UNUSED notes
+ for those parts that were not needed. This case should
+ be rare. */
+
+ int i;
+
+ for (i = HARD_REGNO_NREGS (regno, GET_MODE (reg)) - 1;
+ i >= 0; i--)
+ if ((needed[(regno + i) / REGSET_ELT_BITS]
+ & ((REGSET_ELT_TYPE) 1
+ << ((regno + i) % REGSET_ELT_BITS))) == 0)
+ REG_NOTES (insn)
+ = gen_rtx (EXPR_LIST, REG_UNUSED,
+ gen_rtx (REG, reg_raw_mode[regno + i],
+ regno + i),
+ REG_NOTES (insn));
+ }
+ }
+ }
+ else if (GET_CODE (reg) == REG)
+ reg_next_use[regno] = 0;
+
+ /* If this is the last pass and this is a SCRATCH, show it will be dying
+ here and count it. */
+ else if (GET_CODE (reg) == SCRATCH && insn != 0)
+ {
+ REG_NOTES (insn)
+ = gen_rtx (EXPR_LIST, REG_UNUSED, reg, REG_NOTES (insn));
+ num_scratch++;
+ }
+}
+
+#ifdef AUTO_INC_DEC
+
+/* X is a MEM found in INSN. See if we can convert it into an auto-increment
+ reference. */
+
+static void
+find_auto_inc (needed, x, insn)
+ regset needed;
+ rtx x;
+ rtx insn;
+{
+ rtx addr = XEXP (x, 0);
+ HOST_WIDE_INT offset = 0;
+ rtx set;
+
+ /* Here we detect use of an index register which might be good for
+ postincrement, postdecrement, preincrement, or predecrement. */
+
+ if (GET_CODE (addr) == PLUS && GET_CODE (XEXP (addr, 1)) == CONST_INT)
+ offset = INTVAL (XEXP (addr, 1)), addr = XEXP (addr, 0);
+
+ if (GET_CODE (addr) == REG)
+ {
+ register rtx y;
+ register int size = GET_MODE_SIZE (GET_MODE (x));
+ rtx use;
+ rtx incr;
+ int regno = REGNO (addr);
+
+ /* Is the next use an increment that might make auto-increment? */
+ if ((incr = reg_next_use[regno]) != 0
+ && (set = single_set (incr)) != 0
+ && GET_CODE (set) == SET
+ && BLOCK_NUM (incr) == BLOCK_NUM (insn)
+ /* Can't add side effects to jumps; if reg is spilled and
+ reloaded, there's no way to store back the altered value. */
+ && GET_CODE (insn) != JUMP_INSN
+ && (y = SET_SRC (set), GET_CODE (y) == PLUS)
+ && XEXP (y, 0) == addr
+ && GET_CODE (XEXP (y, 1)) == CONST_INT
+ && (0
+#ifdef HAVE_POST_INCREMENT
+ || (INTVAL (XEXP (y, 1)) == size && offset == 0)
+#endif
+#ifdef HAVE_POST_DECREMENT
+ || (INTVAL (XEXP (y, 1)) == - size && offset == 0)
+#endif
+#ifdef HAVE_PRE_INCREMENT
+ || (INTVAL (XEXP (y, 1)) == size && offset == size)
+#endif
+#ifdef HAVE_PRE_DECREMENT
+ || (INTVAL (XEXP (y, 1)) == - size && offset == - size)
+#endif
+ )
+ /* Make sure this reg appears only once in this insn. */
+ && (use = find_use_as_address (PATTERN (insn), addr, offset),
+ use != 0 && use != (rtx) 1))
+ {
+ int win = 0;
+ rtx q = SET_DEST (set);
+
+ if (dead_or_set_p (incr, addr))
+ win = 1;
+ else if (GET_CODE (q) == REG
+ /* PREV_INSN used here to check the semi-open interval
+ [insn,incr). */
+ && ! reg_used_between_p (q, PREV_INSN (insn), incr))
+ {
+ /* We have *p followed sometime later by q = p+size.
+ Both p and q must be live afterward,
+ and q is not used between INSN and it's assignment.
+ Change it to q = p, ...*q..., q = q+size.
+ Then fall into the usual case. */
+ rtx insns, temp;
+
+ start_sequence ();
+ emit_move_insn (q, addr);
+ insns = get_insns ();
+ end_sequence ();
+
+ /* If anything in INSNS have UID's that don't fit within the
+ extra space we allocate earlier, we can't make this auto-inc.
+ This should never happen. */
+ for (temp = insns; temp; temp = NEXT_INSN (temp))
+ {
+ if (INSN_UID (temp) > max_uid_for_flow)
+ return;
+ BLOCK_NUM (temp) = BLOCK_NUM (insn);
+ }
+
+ emit_insns_before (insns, insn);
+
+ if (basic_block_head[BLOCK_NUM (insn)] == insn)
+ basic_block_head[BLOCK_NUM (insn)] = insns;
+
+ XEXP (x, 0) = q;
+ XEXP (y, 0) = q;
+
+ /* INCR will become a NOTE and INSN won't contain a
+ use of ADDR. If a use of ADDR was just placed in
+ the insn before INSN, make that the next use.
+ Otherwise, invalidate it. */
+ if (GET_CODE (PREV_INSN (insn)) == INSN
+ && GET_CODE (PATTERN (PREV_INSN (insn))) == SET
+ && SET_SRC (PATTERN (PREV_INSN (insn))) == addr)
+ reg_next_use[regno] = PREV_INSN (insn);
+ else
+ reg_next_use[regno] = 0;
+
+ addr = q;
+ regno = REGNO (q);
+ win = 1;
+
+ /* REGNO is now used in INCR which is below INSN, but
+ it previously wasn't live here. If we don't mark
+ it as needed, we'll put a REG_DEAD note for it
+ on this insn, which is incorrect. */
+ needed[regno / REGSET_ELT_BITS]
+ |= (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS);
+
+ /* If there are any calls between INSN and INCR, show
+ that REGNO now crosses them. */
+ for (temp = insn; temp != incr; temp = NEXT_INSN (temp))
+ if (GET_CODE (temp) == CALL_INSN)
+ reg_n_calls_crossed[regno]++;
+ }
+
+ if (win
+ /* If we have found a suitable auto-increment, do
+ POST_INC around the register here, and patch out the
+ increment instruction that follows. */
+ && validate_change (insn, &XEXP (x, 0),
+ gen_rtx ((INTVAL (XEXP (y, 1)) == size
+ ? (offset ? PRE_INC : POST_INC)
+ : (offset ? PRE_DEC : POST_DEC)),
+ Pmode, addr), 0))
+ {
+ /* Record that this insn has an implicit side effect. */
+ REG_NOTES (insn)
+ = gen_rtx (EXPR_LIST, REG_INC, addr, REG_NOTES (insn));
+
+ /* Modify the old increment-insn to simply copy
+ the already-incremented value of our register. */
+ SET_SRC (set) = addr;
+ /* Indicate insn must be re-recognized. */
+ INSN_CODE (incr) = -1;
+
+ /* If that makes it a no-op (copying the register into itself)
+ then delete it so it won't appear to be a "use" and a "set"
+ of this register. */
+ if (SET_DEST (set) == addr)
+ {
+ PUT_CODE (incr, NOTE);
+ NOTE_LINE_NUMBER (incr) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (incr) = 0;
+ }
+
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ {
+ /* Count an extra reference to the reg. When a reg is
+ incremented, spilling it is worse, so we want to make
+ that less likely. */
+ reg_n_refs[regno] += loop_depth;
+ /* Count the increment as a setting of the register,
+ even though it isn't a SET in rtl. */
+ reg_n_sets[regno]++;
+ }
+ }
+ }
+ }
+}
+#endif /* AUTO_INC_DEC */
+
+/* Scan expression X and store a 1-bit in LIVE for each reg it uses.
+ This is done assuming the registers needed from X
+ are those that have 1-bits in NEEDED.
+
+ On the final pass, FINAL is 1. This means try for autoincrement
+ and count the uses and deaths of each pseudo-reg.
+
+ INSN is the containing instruction. If INSN is dead, this function is not
+ called. */
+
+static void
+mark_used_regs (needed, live, x, final, insn)
+ regset needed;
+ regset live;
+ rtx x;
+ int final;
+ rtx insn;
+{
+ register RTX_CODE code;
+ register int regno;
+ int i;
+
+ retry:
+ code = GET_CODE (x);
+ switch (code)
+ {
+ case LABEL_REF:
+ case SYMBOL_REF:
+ case CONST_INT:
+ case CONST:
+ case CONST_DOUBLE:
+ case PC:
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ case ASM_INPUT:
+ return;
+
+#ifdef HAVE_cc0
+ case CC0:
+ cc0_live = 1;
+ return;
+#endif
+
+ case CLOBBER:
+ /* If we are clobbering a MEM, mark any registers inside the address
+ as being used. */
+ if (GET_CODE (XEXP (x, 0)) == MEM)
+ mark_used_regs (needed, live, XEXP (XEXP (x, 0), 0), final, insn);
+ return;
+
+ case MEM:
+ /* Invalidate the data for the last MEM stored. We could do this only
+ if the addresses conflict, but this doesn't seem worthwhile. */
+ last_mem_set = 0;
+
+#ifdef AUTO_INC_DEC
+ if (final)
+ find_auto_inc (needed, x, insn);
+#endif
+ break;
+
+ case REG:
+ /* See a register other than being set
+ => mark it as needed. */
+
+ regno = REGNO (x);
+ {
+ register int offset = regno / REGSET_ELT_BITS;
+ register REGSET_ELT_TYPE bit
+ = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS);
+ REGSET_ELT_TYPE all_needed = needed[offset] & bit;
+ REGSET_ELT_TYPE some_needed = needed[offset] & bit;
+
+ live[offset] |= bit;
+ /* A hard reg in a wide mode may really be multiple registers.
+ If so, mark all of them just like the first. */
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ int n;
+
+ /* For stack ptr or fixed arg pointer,
+ nothing below can be necessary, so waste no more time. */
+ if (regno == STACK_POINTER_REGNUM
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+ || regno == HARD_FRAME_POINTER_REGNUM
+#endif
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ || (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
+#endif
+ || regno == FRAME_POINTER_REGNUM)
+ {
+ /* If this is a register we are going to try to eliminate,
+ don't mark it live here. If we are successful in
+ eliminating it, it need not be live unless it is used for
+ pseudos, in which case it will have been set live when
+ it was allocated to the pseudos. If the register will not
+ be eliminated, reload will set it live at that point. */
+
+ if (! TEST_HARD_REG_BIT (elim_reg_set, regno))
+ regs_ever_live[regno] = 1;
+ return;
+ }
+ /* No death notes for global register variables;
+ their values are live after this function exits. */
+ if (global_regs[regno])
+ {
+ if (final)
+ reg_next_use[regno] = insn;
+ return;
+ }
+
+ n = HARD_REGNO_NREGS (regno, GET_MODE (x));
+ while (--n > 0)
+ {
+ live[(regno + n) / REGSET_ELT_BITS]
+ |= (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS);
+ some_needed
+ |= (needed[(regno + n) / REGSET_ELT_BITS]
+ & (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS));
+ all_needed
+ &= (needed[(regno + n) / REGSET_ELT_BITS]
+ & (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS));
+ }
+ }
+ if (final)
+ {
+ /* Record where each reg is used, so when the reg
+ is set we know the next insn that uses it. */
+
+ reg_next_use[regno] = insn;
+
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ /* If a hard reg is being used,
+ record that this function does use it. */
+
+ i = HARD_REGNO_NREGS (regno, GET_MODE (x));
+ if (i == 0)
+ i = 1;
+ do
+ regs_ever_live[regno + --i] = 1;
+ while (i > 0);
+ }
+ else
+ {
+ /* Keep track of which basic block each reg appears in. */
+
+ register int blocknum = BLOCK_NUM (insn);
+
+ if (reg_basic_block[regno] == REG_BLOCK_UNKNOWN)
+ reg_basic_block[regno] = blocknum;
+ else if (reg_basic_block[regno] != blocknum)
+ reg_basic_block[regno] = REG_BLOCK_GLOBAL;
+
+ /* Count (weighted) number of uses of each reg. */
+
+ reg_n_refs[regno] += loop_depth;
+ }
+
+ /* Record and count the insns in which a reg dies.
+ If it is used in this insn and was dead below the insn
+ then it dies in this insn. If it was set in this insn,
+ we do not make a REG_DEAD note; likewise if we already
+ made such a note. */
+
+ if (! all_needed
+ && ! dead_or_set_p (insn, x)
+#if 0
+ && (regno >= FIRST_PSEUDO_REGISTER || ! fixed_regs[regno])
+#endif
+ )
+ {
+ /* If none of the words in X is needed, make a REG_DEAD
+ note. Otherwise, we must make partial REG_DEAD notes. */
+ if (! some_needed)
+ {
+ REG_NOTES (insn)
+ = gen_rtx (EXPR_LIST, REG_DEAD, x, REG_NOTES (insn));
+ reg_n_deaths[regno]++;
+ }
+ else
+ {
+ int i;
+
+ /* Don't make a REG_DEAD note for a part of a register
+ that is set in the insn. */
+
+ for (i = HARD_REGNO_NREGS (regno, GET_MODE (x)) - 1;
+ i >= 0; i--)
+ if ((needed[(regno + i) / REGSET_ELT_BITS]
+ & ((REGSET_ELT_TYPE) 1
+ << ((regno + i) % REGSET_ELT_BITS))) == 0
+ && ! dead_or_set_regno_p (insn, regno + i))
+ REG_NOTES (insn)
+ = gen_rtx (EXPR_LIST, REG_DEAD,
+ gen_rtx (REG, reg_raw_mode[regno + i],
+ regno + i),
+ REG_NOTES (insn));
+ }
+ }
+ }
+ }
+ return;
+
+ case SET:
+ {
+ register rtx testreg = SET_DEST (x);
+ int mark_dest = 0;
+
+ /* If storing into MEM, don't show it as being used. But do
+ show the address as being used. */
+ if (GET_CODE (testreg) == MEM)
+ {
+#ifdef AUTO_INC_DEC
+ if (final)
+ find_auto_inc (needed, testreg, insn);
+#endif
+ mark_used_regs (needed, live, XEXP (testreg, 0), final, insn);
+ mark_used_regs (needed, live, SET_SRC (x), final, insn);
+ return;
+ }
+
+ /* Storing in STRICT_LOW_PART is like storing in a reg
+ in that this SET might be dead, so ignore it in TESTREG.
+ but in some other ways it is like using the reg.
+
+ Storing in a SUBREG or a bit field is like storing the entire
+ register in that if the register's value is not used
+ then this SET is not needed. */
+ while (GET_CODE (testreg) == STRICT_LOW_PART
+ || GET_CODE (testreg) == ZERO_EXTRACT
+ || GET_CODE (testreg) == SIGN_EXTRACT
+ || GET_CODE (testreg) == SUBREG)
+ {
+ /* Modifying a single register in an alternate mode
+ does not use any of the old value. But these other
+ ways of storing in a register do use the old value. */
+ if (GET_CODE (testreg) == SUBREG
+ && !(REG_SIZE (SUBREG_REG (testreg)) > REG_SIZE (testreg)))
+ ;
+ else
+ mark_dest = 1;
+
+ testreg = XEXP (testreg, 0);
+ }
+
+ /* If this is a store into a register,
+ recursively scan the value being stored. */
+
+ if (GET_CODE (testreg) == REG
+ && (regno = REGNO (testreg), regno != FRAME_POINTER_REGNUM)
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+ && regno != HARD_FRAME_POINTER_REGNUM
+#endif
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ && ! (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
+#endif
+ )
+ /* We used to exclude global_regs here, but that seems wrong.
+ Storing in them is like storing in mem. */
+ {
+ mark_used_regs (needed, live, SET_SRC (x), final, insn);
+ if (mark_dest)
+ mark_used_regs (needed, live, SET_DEST (x), final, insn);
+ return;
+ }
+ }
+ break;
+
+ case RETURN:
+ /* If exiting needs the right stack value, consider this insn as
+ using the stack pointer. In any event, consider it as using
+ all global registers. */
+
+#ifdef EXIT_IGNORE_STACK
+ if (! EXIT_IGNORE_STACK
+ || (! FRAME_POINTER_REQUIRED && flag_omit_frame_pointer))
+#endif
+ live[STACK_POINTER_REGNUM / REGSET_ELT_BITS]
+ |= (REGSET_ELT_TYPE) 1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS);
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (global_regs[i])
+ live[i / REGSET_ELT_BITS]
+ |= (REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS);
+ break;
+ }
+
+ /* Recursively scan the operands of this expression. */
+
+ {
+ register char *fmt = GET_RTX_FORMAT (code);
+ register int i;
+
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ {
+ /* Tail recursive case: save a function call level. */
+ if (i == 0)
+ {
+ x = XEXP (x, 0);
+ goto retry;
+ }
+ mark_used_regs (needed, live, XEXP (x, i), final, insn);
+ }
+ else if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ mark_used_regs (needed, live, XVECEXP (x, i, j), final, insn);
+ }
+ }
+ }
+}
+
+#ifdef AUTO_INC_DEC
+
+static int
+try_pre_increment_1 (insn)
+ rtx insn;
+{
+ /* Find the next use of this reg. If in same basic block,
+ make it do pre-increment or pre-decrement if appropriate. */
+ rtx x = PATTERN (insn);
+ HOST_WIDE_INT amount = ((GET_CODE (SET_SRC (x)) == PLUS ? 1 : -1)
+ * INTVAL (XEXP (SET_SRC (x), 1)));
+ int regno = REGNO (SET_DEST (x));
+ rtx y = reg_next_use[regno];
+ if (y != 0
+ && BLOCK_NUM (y) == BLOCK_NUM (insn)
+ /* Don't do this if the reg dies, or gets set in y; a standard addressing
+ mode would be better. */
+ && ! dead_or_set_p (y, SET_DEST (x))
+ && try_pre_increment (y, SET_DEST (PATTERN (insn)),
+ amount))
+ {
+ /* We have found a suitable auto-increment
+ and already changed insn Y to do it.
+ So flush this increment-instruction. */
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ /* Count a reference to this reg for the increment
+ insn we are deleting. When a reg is incremented.
+ spilling it is worse, so we want to make that
+ less likely. */
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ {
+ reg_n_refs[regno] += loop_depth;
+ reg_n_sets[regno]++;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/* Try to change INSN so that it does pre-increment or pre-decrement
+ addressing on register REG in order to add AMOUNT to REG.
+ AMOUNT is negative for pre-decrement.
+ Returns 1 if the change could be made.
+ This checks all about the validity of the result of modifying INSN. */
+
+static int
+try_pre_increment (insn, reg, amount)
+ rtx insn, reg;
+ HOST_WIDE_INT amount;
+{
+ register rtx use;
+
+ /* Nonzero if we can try to make a pre-increment or pre-decrement.
+ For example, addl $4,r1; movl (r1),... can become movl +(r1),... */
+ int pre_ok = 0;
+ /* Nonzero if we can try to make a post-increment or post-decrement.
+ For example, addl $4,r1; movl -4(r1),... can become movl (r1)+,...
+ It is possible for both PRE_OK and POST_OK to be nonzero if the machine
+ supports both pre-inc and post-inc, or both pre-dec and post-dec. */
+ int post_ok = 0;
+
+ /* Nonzero if the opportunity actually requires post-inc or post-dec. */
+ int do_post = 0;
+
+ /* From the sign of increment, see which possibilities are conceivable
+ on this target machine. */
+#ifdef HAVE_PRE_INCREMENT
+ if (amount > 0)
+ pre_ok = 1;
+#endif
+#ifdef HAVE_POST_INCREMENT
+ if (amount > 0)
+ post_ok = 1;
+#endif
+
+#ifdef HAVE_PRE_DECREMENT
+ if (amount < 0)
+ pre_ok = 1;
+#endif
+#ifdef HAVE_POST_DECREMENT
+ if (amount < 0)
+ post_ok = 1;
+#endif
+
+ if (! (pre_ok || post_ok))
+ return 0;
+
+ /* It is not safe to add a side effect to a jump insn
+ because if the incremented register is spilled and must be reloaded
+ there would be no way to store the incremented value back in memory. */
+
+ if (GET_CODE (insn) == JUMP_INSN)
+ return 0;
+
+ use = 0;
+ if (pre_ok)
+ use = find_use_as_address (PATTERN (insn), reg, 0);
+ if (post_ok && (use == 0 || use == (rtx) 1))
+ {
+ use = find_use_as_address (PATTERN (insn), reg, -amount);
+ do_post = 1;
+ }
+
+ if (use == 0 || use == (rtx) 1)
+ return 0;
+
+ if (GET_MODE_SIZE (GET_MODE (use)) != (amount > 0 ? amount : - amount))
+ return 0;
+
+ /* See if this combination of instruction and addressing mode exists. */
+ if (! validate_change (insn, &XEXP (use, 0),
+ gen_rtx (amount > 0
+ ? (do_post ? POST_INC : PRE_INC)
+ : (do_post ? POST_DEC : PRE_DEC),
+ Pmode, reg), 0))
+ return 0;
+
+ /* Record that this insn now has an implicit side effect on X. */
+ REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_INC, reg, REG_NOTES (insn));
+ return 1;
+}
+
+#endif /* AUTO_INC_DEC */
+
+/* Find the place in the rtx X where REG is used as a memory address.
+ Return the MEM rtx that so uses it.
+ If PLUSCONST is nonzero, search instead for a memory address equivalent to
+ (plus REG (const_int PLUSCONST)).
+
+ If such an address does not appear, return 0.
+ If REG appears more than once, or is used other than in such an address,
+ return (rtx)1. */
+
+static rtx
+find_use_as_address (x, reg, plusconst)
+ register rtx x;
+ rtx reg;
+ HOST_WIDE_INT plusconst;
+{
+ enum rtx_code code = GET_CODE (x);
+ char *fmt = GET_RTX_FORMAT (code);
+ register int i;
+ register rtx value = 0;
+ register rtx tem;
+
+ if (code == MEM && XEXP (x, 0) == reg && plusconst == 0)
+ return x;
+
+ if (code == MEM && GET_CODE (XEXP (x, 0)) == PLUS
+ && XEXP (XEXP (x, 0), 0) == reg
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+ && INTVAL (XEXP (XEXP (x, 0), 1)) == plusconst)
+ return x;
+
+ if (code == SIGN_EXTRACT || code == ZERO_EXTRACT)
+ {
+ /* If REG occurs inside a MEM used in a bit-field reference,
+ that is unacceptable. */
+ if (find_use_as_address (XEXP (x, 0), reg, 0) != 0)
+ return (rtx) (HOST_WIDE_INT) 1;
+ }
+
+ if (x == reg)
+ return (rtx) (HOST_WIDE_INT) 1;
+
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ {
+ tem = find_use_as_address (XEXP (x, i), reg, plusconst);
+ if (value == 0)
+ value = tem;
+ else if (tem != 0)
+ return (rtx) (HOST_WIDE_INT) 1;
+ }
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ {
+ tem = find_use_as_address (XVECEXP (x, i, j), reg, plusconst);
+ if (value == 0)
+ value = tem;
+ else if (tem != 0)
+ return (rtx) (HOST_WIDE_INT) 1;
+ }
+ }
+ }
+
+ return value;
+}
+
+/* Write information about registers and basic blocks into FILE.
+ This is part of making a debugging dump. */
+
+void
+dump_flow_info (file)
+ FILE *file;
+{
+ register int i;
+ static char *reg_class_names[] = REG_CLASS_NAMES;
+
+ fprintf (file, "%d registers.\n", max_regno);
+
+ for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+ if (reg_n_refs[i])
+ {
+ enum reg_class class, altclass;
+ fprintf (file, "\nRegister %d used %d times across %d insns",
+ i, reg_n_refs[i], reg_live_length[i]);
+ if (reg_basic_block[i] >= 0)
+ fprintf (file, " in block %d", reg_basic_block[i]);
+ if (reg_n_deaths[i] != 1)
+ fprintf (file, "; dies in %d places", reg_n_deaths[i]);
+ if (reg_n_calls_crossed[i] == 1)
+ fprintf (file, "; crosses 1 call");
+ else if (reg_n_calls_crossed[i])
+ fprintf (file, "; crosses %d calls", reg_n_calls_crossed[i]);
+ if (PSEUDO_REGNO_BYTES (i) != UNITS_PER_WORD)
+ fprintf (file, "; %d bytes", PSEUDO_REGNO_BYTES (i));
+ class = reg_preferred_class (i);
+ altclass = reg_alternate_class (i);
+ if (class != GENERAL_REGS || altclass != ALL_REGS)
+ {
+ if (altclass == ALL_REGS || class == ALL_REGS)
+ fprintf (file, "; pref %s", reg_class_names[(int) class]);
+ else if (altclass == NO_REGS)
+ fprintf (file, "; %s or none", reg_class_names[(int) class]);
+ else
+ fprintf (file, "; pref %s, else %s",
+ reg_class_names[(int) class],
+ reg_class_names[(int) altclass]);
+ }
+ if (REGNO_POINTER_FLAG (i))
+ fprintf (file, "; pointer");
+ fprintf (file, ".\n");
+ }
+ fprintf (file, "\n%d basic blocks.\n", n_basic_blocks);
+ for (i = 0; i < n_basic_blocks; i++)
+ {
+ register rtx head, jump;
+ register int regno;
+ fprintf (file, "\nBasic block %d: first insn %d, last %d.\n",
+ i,
+ INSN_UID (basic_block_head[i]),
+ INSN_UID (basic_block_end[i]));
+ /* The control flow graph's storage is freed
+ now when flow_analysis returns.
+ Don't try to print it if it is gone. */
+ if (basic_block_drops_in)
+ {
+ fprintf (file, "Reached from blocks: ");
+ head = basic_block_head[i];
+ if (GET_CODE (head) == CODE_LABEL)
+ for (jump = LABEL_REFS (head);
+ jump != head;
+ jump = LABEL_NEXTREF (jump))
+ {
+ register int from_block = BLOCK_NUM (CONTAINING_INSN (jump));
+ fprintf (file, " %d", from_block);
+ }
+ if (basic_block_drops_in[i])
+ fprintf (file, " previous");
+ }
+ fprintf (file, "\nRegisters live at start:");
+ for (regno = 0; regno < max_regno; regno++)
+ {
+ register int offset = regno / REGSET_ELT_BITS;
+ register REGSET_ELT_TYPE bit
+ = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS);
+ if (basic_block_live_at_start[i][offset] & bit)
+ fprintf (file, " %d", regno);
+ }
+ fprintf (file, "\n");
+ }
+ fprintf (file, "\n");
+}
diff --git a/gnu/usr.bin/cc/cc_int/fold-const.c b/gnu/usr.bin/cc/cc_int/fold-const.c
new file mode 100644
index 0000000..0417aab
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/fold-const.c
@@ -0,0 +1,4889 @@
+/* Fold a constant sub-tree into a single node for C-compiler
+ Copyright (C) 1987, 1988, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*@@ This file should be rewritten to use an arbitrary precision
+ @@ representation for "struct tree_int_cst" and "struct tree_real_cst".
+ @@ Perhaps the routines could also be used for bc/dc, and made a lib.
+ @@ The routines that translate from the ap rep should
+ @@ warn if precision et. al. is lost.
+ @@ This would also make life easier when this technology is used
+ @@ for cross-compilers. */
+
+
+/* The entry points in this file are fold, size_int and size_binop.
+
+ fold takes a tree as argument and returns a simplified tree.
+
+ size_binop takes a tree code for an arithmetic operation
+ and two operands that are trees, and produces a tree for the
+ result, assuming the type comes from `sizetype'.
+
+ size_int takes an integer value, and creates a tree constant
+ with type from `sizetype'. */
+
+#include <stdio.h>
+#include <setjmp.h>
+#include "config.h"
+#include "flags.h"
+#include "tree.h"
+
+/* Handle floating overflow for `const_binop'. */
+static jmp_buf float_error;
+
+static void encode PROTO((HOST_WIDE_INT *, HOST_WIDE_INT, HOST_WIDE_INT));
+static void decode PROTO((HOST_WIDE_INT *, HOST_WIDE_INT *, HOST_WIDE_INT *));
+int div_and_round_double PROTO((enum tree_code, int, HOST_WIDE_INT,
+ HOST_WIDE_INT, HOST_WIDE_INT,
+ HOST_WIDE_INT, HOST_WIDE_INT *,
+ HOST_WIDE_INT *, HOST_WIDE_INT *,
+ HOST_WIDE_INT *));
+static int split_tree PROTO((tree, enum tree_code, tree *, tree *, int *));
+static tree const_binop PROTO((enum tree_code, tree, tree, int));
+static tree fold_convert PROTO((tree, tree));
+static enum tree_code invert_tree_comparison PROTO((enum tree_code));
+static enum tree_code swap_tree_comparison PROTO((enum tree_code));
+static int truth_value_p PROTO((enum tree_code));
+static int operand_equal_for_comparison_p PROTO((tree, tree, tree));
+static int twoval_comparison_p PROTO((tree, tree *, tree *, int *));
+static tree eval_subst PROTO((tree, tree, tree, tree, tree));
+static tree omit_one_operand PROTO((tree, tree, tree));
+static tree distribute_bit_expr PROTO((enum tree_code, tree, tree, tree));
+static tree make_bit_field_ref PROTO((tree, tree, int, int, int));
+static tree optimize_bit_field_compare PROTO((enum tree_code, tree,
+ tree, tree));
+static tree decode_field_reference PROTO((tree, int *, int *,
+ enum machine_mode *, int *,
+ int *, tree *));
+static int all_ones_mask_p PROTO((tree, int));
+static int simple_operand_p PROTO((tree));
+static tree range_test PROTO((enum tree_code, tree, enum tree_code,
+ enum tree_code, tree, tree, tree));
+static tree fold_truthop PROTO((enum tree_code, tree, tree, tree));
+static tree strip_compound_expr PROTO((tree, tree));
+
+#ifndef BRANCH_COST
+#define BRANCH_COST 1
+#endif
+
+/* Yield nonzero if a signed left shift of A by B bits overflows. */
+#define left_shift_overflows(a, b) ((a) != ((a) << (b)) >> (b))
+
+/* Suppose A1 + B1 = SUM1, using 2's complement arithmetic ignoring overflow.
+ Suppose A, B and SUM have the same respective signs as A1, B1, and SUM1.
+ Then this yields nonzero if overflow occurred during the addition.
+ Overflow occurs if A and B have the same sign, but A and SUM differ in sign.
+ Use `^' to test whether signs differ, and `< 0' to isolate the sign. */
+#define overflow_sum_sign(a, b, sum) ((~((a) ^ (b)) & ((a) ^ (sum))) < 0)
+
+/* To do constant folding on INTEGER_CST nodes requires two-word arithmetic.
+ We do that by representing the two-word integer in 4 words, with only
+ HOST_BITS_PER_WIDE_INT/2 bits stored in each word, as a positive number. */
+
+#define LOWPART(x) \
+ ((x) & (((unsigned HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT/2)) - 1))
+#define HIGHPART(x) \
+ ((unsigned HOST_WIDE_INT) (x) >> HOST_BITS_PER_WIDE_INT/2)
+#define BASE ((unsigned HOST_WIDE_INT) 1 << HOST_BITS_PER_WIDE_INT/2)
+
+/* Unpack a two-word integer into 4 words.
+ LOW and HI are the integer, as two `HOST_WIDE_INT' pieces.
+ WORDS points to the array of HOST_WIDE_INTs. */
+
+static void
+encode (words, low, hi)
+ HOST_WIDE_INT *words;
+ HOST_WIDE_INT low, hi;
+{
+ words[0] = LOWPART (low);
+ words[1] = HIGHPART (low);
+ words[2] = LOWPART (hi);
+ words[3] = HIGHPART (hi);
+}
+
+/* Pack an array of 4 words into a two-word integer.
+ WORDS points to the array of words.
+ The integer is stored into *LOW and *HI as two `HOST_WIDE_INT' pieces. */
+
+static void
+decode (words, low, hi)
+ HOST_WIDE_INT *words;
+ HOST_WIDE_INT *low, *hi;
+{
+ *low = words[0] | words[1] * BASE;
+ *hi = words[2] | words[3] * BASE;
+}
+
+/* Make the integer constant T valid for its type
+ by setting to 0 or 1 all the bits in the constant
+ that don't belong in the type.
+ Yield 1 if a signed overflow occurs, 0 otherwise.
+ If OVERFLOW is nonzero, a signed overflow has already occurred
+ in calculating T, so propagate it.
+
+ Make the real constant T valid for its type by calling CHECK_FLOAT_VALUE,
+ if it exists. */
+
+int
+force_fit_type (t, overflow)
+ tree t;
+ int overflow;
+{
+ HOST_WIDE_INT low, high;
+ register int prec;
+
+ if (TREE_CODE (t) == REAL_CST)
+ {
+#ifdef CHECK_FLOAT_VALUE
+ CHECK_FLOAT_VALUE (TYPE_MODE (TREE_TYPE (t)), TREE_REAL_CST (t),
+ overflow);
+#endif
+ return overflow;
+ }
+
+ else if (TREE_CODE (t) != INTEGER_CST)
+ return overflow;
+
+ low = TREE_INT_CST_LOW (t);
+ high = TREE_INT_CST_HIGH (t);
+
+ if (TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE)
+ prec = POINTER_SIZE;
+ else
+ prec = TYPE_PRECISION (TREE_TYPE (t));
+
+ /* First clear all bits that are beyond the type's precision. */
+
+ if (prec == 2 * HOST_BITS_PER_WIDE_INT)
+ ;
+ else if (prec > HOST_BITS_PER_WIDE_INT)
+ {
+ TREE_INT_CST_HIGH (t)
+ &= ~((HOST_WIDE_INT) (-1) << (prec - HOST_BITS_PER_WIDE_INT));
+ }
+ else
+ {
+ TREE_INT_CST_HIGH (t) = 0;
+ if (prec < HOST_BITS_PER_WIDE_INT)
+ TREE_INT_CST_LOW (t) &= ~((HOST_WIDE_INT) (-1) << prec);
+ }
+
+ /* Unsigned types do not suffer sign extension or overflow. */
+ if (TREE_UNSIGNED (TREE_TYPE (t)))
+ return 0;
+
+ /* If the value's sign bit is set, extend the sign. */
+ if (prec != 2 * HOST_BITS_PER_WIDE_INT
+ && (prec > HOST_BITS_PER_WIDE_INT
+ ? (TREE_INT_CST_HIGH (t)
+ & ((HOST_WIDE_INT) 1 << (prec - HOST_BITS_PER_WIDE_INT - 1)))
+ : TREE_INT_CST_LOW (t) & ((HOST_WIDE_INT) 1 << (prec - 1))))
+ {
+ /* Value is negative:
+ set to 1 all the bits that are outside this type's precision. */
+ if (prec > HOST_BITS_PER_WIDE_INT)
+ {
+ TREE_INT_CST_HIGH (t)
+ |= ((HOST_WIDE_INT) (-1) << (prec - HOST_BITS_PER_WIDE_INT));
+ }
+ else
+ {
+ TREE_INT_CST_HIGH (t) = -1;
+ if (prec < HOST_BITS_PER_WIDE_INT)
+ TREE_INT_CST_LOW (t) |= ((HOST_WIDE_INT) (-1) << prec);
+ }
+ }
+
+ /* Yield nonzero if signed overflow occurred. */
+ return
+ ((overflow | (low ^ TREE_INT_CST_LOW (t)) | (high ^ TREE_INT_CST_HIGH (t)))
+ != 0);
+}
+
+/* Add two doubleword integers with doubleword result.
+ Each argument is given as two `HOST_WIDE_INT' pieces.
+ One argument is L1 and H1; the other, L2 and H2.
+ The value is stored as two `HOST_WIDE_INT' pieces in *LV and *HV. */
+
+int
+add_double (l1, h1, l2, h2, lv, hv)
+ HOST_WIDE_INT l1, h1, l2, h2;
+ HOST_WIDE_INT *lv, *hv;
+{
+ HOST_WIDE_INT l, h;
+
+ l = l1 + l2;
+ h = h1 + h2 + ((unsigned HOST_WIDE_INT) l < l1);
+
+ *lv = l;
+ *hv = h;
+ return overflow_sum_sign (h1, h2, h);
+}
+
+/* Negate a doubleword integer with doubleword result.
+ Return nonzero if the operation overflows, assuming it's signed.
+ The argument is given as two `HOST_WIDE_INT' pieces in L1 and H1.
+ The value is stored as two `HOST_WIDE_INT' pieces in *LV and *HV. */
+
+int
+neg_double (l1, h1, lv, hv)
+ HOST_WIDE_INT l1, h1;
+ HOST_WIDE_INT *lv, *hv;
+{
+ if (l1 == 0)
+ {
+ *lv = 0;
+ *hv = - h1;
+ return (*hv & h1) < 0;
+ }
+ else
+ {
+ *lv = - l1;
+ *hv = ~ h1;
+ return 0;
+ }
+}
+
+/* Multiply two doubleword integers with doubleword result.
+ Return nonzero if the operation overflows, assuming it's signed.
+ Each argument is given as two `HOST_WIDE_INT' pieces.
+ One argument is L1 and H1; the other, L2 and H2.
+ The value is stored as two `HOST_WIDE_INT' pieces in *LV and *HV. */
+
+int
+mul_double (l1, h1, l2, h2, lv, hv)
+ HOST_WIDE_INT l1, h1, l2, h2;
+ HOST_WIDE_INT *lv, *hv;
+{
+ HOST_WIDE_INT arg1[4];
+ HOST_WIDE_INT arg2[4];
+ HOST_WIDE_INT prod[4 * 2];
+ register unsigned HOST_WIDE_INT carry;
+ register int i, j, k;
+ HOST_WIDE_INT toplow, tophigh, neglow, neghigh;
+
+ encode (arg1, l1, h1);
+ encode (arg2, l2, h2);
+
+ bzero ((char *) prod, sizeof prod);
+
+ for (i = 0; i < 4; i++)
+ {
+ carry = 0;
+ for (j = 0; j < 4; j++)
+ {
+ k = i + j;
+ /* This product is <= 0xFFFE0001, the sum <= 0xFFFF0000. */
+ carry += arg1[i] * arg2[j];
+ /* Since prod[p] < 0xFFFF, this sum <= 0xFFFFFFFF. */
+ carry += prod[k];
+ prod[k] = LOWPART (carry);
+ carry = HIGHPART (carry);
+ }
+ prod[i + 4] = carry;
+ }
+
+ decode (prod, lv, hv); /* This ignores prod[4] through prod[4*2-1] */
+
+ /* Check for overflow by calculating the top half of the answer in full;
+ it should agree with the low half's sign bit. */
+ decode (prod+4, &toplow, &tophigh);
+ if (h1 < 0)
+ {
+ neg_double (l2, h2, &neglow, &neghigh);
+ add_double (neglow, neghigh, toplow, tophigh, &toplow, &tophigh);
+ }
+ if (h2 < 0)
+ {
+ neg_double (l1, h1, &neglow, &neghigh);
+ add_double (neglow, neghigh, toplow, tophigh, &toplow, &tophigh);
+ }
+ return (*hv < 0 ? ~(toplow & tophigh) : toplow | tophigh) != 0;
+}
+
+/* Shift the doubleword integer in L1, H1 left by COUNT places
+ keeping only PREC bits of result.
+ Shift right if COUNT is negative.
+ ARITH nonzero specifies arithmetic shifting; otherwise use logical shift.
+ Store the value as two `HOST_WIDE_INT' pieces in *LV and *HV. */
+
+void
+lshift_double (l1, h1, count, prec, lv, hv, arith)
+ HOST_WIDE_INT l1, h1, count;
+ int prec;
+ HOST_WIDE_INT *lv, *hv;
+ int arith;
+{
+ if (count < 0)
+ {
+ rshift_double (l1, h1, - count, prec, lv, hv, arith);
+ return;
+ }
+
+ if (count >= prec)
+ count = (unsigned HOST_WIDE_INT) count & prec;
+
+ if (count >= HOST_BITS_PER_WIDE_INT)
+ {
+ *hv = (unsigned HOST_WIDE_INT) l1 << count - HOST_BITS_PER_WIDE_INT;
+ *lv = 0;
+ }
+ else
+ {
+ *hv = (((unsigned HOST_WIDE_INT) h1 << count)
+ | ((unsigned HOST_WIDE_INT) l1 >> HOST_BITS_PER_WIDE_INT - count - 1 >> 1));
+ *lv = (unsigned HOST_WIDE_INT) l1 << count;
+ }
+}
+
+/* Shift the doubleword integer in L1, H1 right by COUNT places
+ keeping only PREC bits of result. COUNT must be positive.
+ ARITH nonzero specifies arithmetic shifting; otherwise use logical shift.
+ Store the value as two `HOST_WIDE_INT' pieces in *LV and *HV. */
+
+void
+rshift_double (l1, h1, count, prec, lv, hv, arith)
+ HOST_WIDE_INT l1, h1, count;
+ int prec;
+ HOST_WIDE_INT *lv, *hv;
+ int arith;
+{
+ unsigned HOST_WIDE_INT signmask;
+ signmask = (arith
+ ? -((unsigned HOST_WIDE_INT) h1 >> (HOST_BITS_PER_WIDE_INT - 1))
+ : 0);
+
+ if (count >= prec)
+ count = (unsigned HOST_WIDE_INT) count % prec;
+
+ if (count >= HOST_BITS_PER_WIDE_INT)
+ {
+ *hv = signmask;
+ *lv = ((signmask << 2 * HOST_BITS_PER_WIDE_INT - count - 1 << 1)
+ | ((unsigned HOST_WIDE_INT) h1 >> count - HOST_BITS_PER_WIDE_INT));
+ }
+ else
+ {
+ *lv = (((unsigned HOST_WIDE_INT) l1 >> count)
+ | ((unsigned HOST_WIDE_INT) h1 << HOST_BITS_PER_WIDE_INT - count - 1 << 1));
+ *hv = ((signmask << HOST_BITS_PER_WIDE_INT - count)
+ | ((unsigned HOST_WIDE_INT) h1 >> count));
+ }
+}
+
+/* Rotate the doubleword integer in L1, H1 left by COUNT places
+ keeping only PREC bits of result.
+ Rotate right if COUNT is negative.
+ Store the value as two `HOST_WIDE_INT' pieces in *LV and *HV. */
+
+void
+lrotate_double (l1, h1, count, prec, lv, hv)
+ HOST_WIDE_INT l1, h1, count;
+ int prec;
+ HOST_WIDE_INT *lv, *hv;
+{
+ HOST_WIDE_INT arg1[4];
+ register int i;
+ register int carry;
+
+ if (count < 0)
+ {
+ rrotate_double (l1, h1, - count, prec, lv, hv);
+ return;
+ }
+
+ encode (arg1, l1, h1);
+
+ if (count > prec)
+ count = prec;
+
+ carry = arg1[4 - 1] >> 16 - 1;
+ while (count > 0)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ carry += arg1[i] << 1;
+ arg1[i] = LOWPART (carry);
+ carry = HIGHPART (carry);
+ }
+ count--;
+ }
+
+ decode (arg1, lv, hv);
+}
+
+/* Rotate the doubleword integer in L1, H1 left by COUNT places
+ keeping only PREC bits of result. COUNT must be positive.
+ Store the value as two `HOST_WIDE_INT' pieces in *LV and *HV. */
+
+void
+rrotate_double (l1, h1, count, prec, lv, hv)
+ HOST_WIDE_INT l1, h1, count;
+ int prec;
+ HOST_WIDE_INT *lv, *hv;
+{
+ HOST_WIDE_INT arg1[4];
+ register int i;
+ register int carry;
+
+ encode (arg1, l1, h1);
+
+ if (count > prec)
+ count = prec;
+
+ carry = arg1[0] & 1;
+ while (count > 0)
+ {
+ for (i = 4 - 1; i >= 0; i--)
+ {
+ carry *= BASE;
+ carry += arg1[i];
+ arg1[i] = LOWPART (carry >> 1);
+ }
+ count--;
+ }
+
+ decode (arg1, lv, hv);
+}
+
+/* Divide doubleword integer LNUM, HNUM by doubleword integer LDEN, HDEN
+ for a quotient (stored in *LQUO, *HQUO) and remainder (in *LREM, *HREM).
+ CODE is a tree code for a kind of division, one of
+ TRUNC_DIV_EXPR, FLOOR_DIV_EXPR, CEIL_DIV_EXPR, ROUND_DIV_EXPR
+ or EXACT_DIV_EXPR
+ It controls how the quotient is rounded to a integer.
+ Return nonzero if the operation overflows.
+ UNS nonzero says do unsigned division. */
+
+int
+div_and_round_double (code, uns,
+ lnum_orig, hnum_orig, lden_orig, hden_orig,
+ lquo, hquo, lrem, hrem)
+ enum tree_code code;
+ int uns;
+ HOST_WIDE_INT lnum_orig, hnum_orig; /* num == numerator == dividend */
+ HOST_WIDE_INT lden_orig, hden_orig; /* den == denominator == divisor */
+ HOST_WIDE_INT *lquo, *hquo, *lrem, *hrem;
+{
+ int quo_neg = 0;
+ HOST_WIDE_INT num[4 + 1]; /* extra element for scaling. */
+ HOST_WIDE_INT den[4], quo[4];
+ register int i, j;
+ unsigned HOST_WIDE_INT work;
+ register int carry = 0;
+ HOST_WIDE_INT lnum = lnum_orig;
+ HOST_WIDE_INT hnum = hnum_orig;
+ HOST_WIDE_INT lden = lden_orig;
+ HOST_WIDE_INT hden = hden_orig;
+ int overflow = 0;
+
+ if ((hden == 0) && (lden == 0))
+ abort ();
+
+ /* calculate quotient sign and convert operands to unsigned. */
+ if (!uns)
+ {
+ if (hnum < 0)
+ {
+ quo_neg = ~ quo_neg;
+ /* (minimum integer) / (-1) is the only overflow case. */
+ if (neg_double (lnum, hnum, &lnum, &hnum) && (lden & hden) == -1)
+ overflow = 1;
+ }
+ if (hden < 0)
+ {
+ quo_neg = ~ quo_neg;
+ neg_double (lden, hden, &lden, &hden);
+ }
+ }
+
+ if (hnum == 0 && hden == 0)
+ { /* single precision */
+ *hquo = *hrem = 0;
+ /* This unsigned division rounds toward zero. */
+ *lquo = lnum / (unsigned HOST_WIDE_INT) lden;
+ goto finish_up;
+ }
+
+ if (hnum == 0)
+ { /* trivial case: dividend < divisor */
+ /* hden != 0 already checked. */
+ *hquo = *lquo = 0;
+ *hrem = hnum;
+ *lrem = lnum;
+ goto finish_up;
+ }
+
+ bzero ((char *) quo, sizeof quo);
+
+ bzero ((char *) num, sizeof num); /* to zero 9th element */
+ bzero ((char *) den, sizeof den);
+
+ encode (num, lnum, hnum);
+ encode (den, lden, hden);
+
+ /* Special code for when the divisor < BASE. */
+ if (hden == 0 && lden < BASE)
+ {
+ /* hnum != 0 already checked. */
+ for (i = 4 - 1; i >= 0; i--)
+ {
+ work = num[i] + carry * BASE;
+ quo[i] = work / (unsigned HOST_WIDE_INT) lden;
+ carry = work % (unsigned HOST_WIDE_INT) lden;
+ }
+ }
+ else
+ {
+ /* Full double precision division,
+ with thanks to Don Knuth's "Seminumerical Algorithms". */
+ int quo_est, scale, num_hi_sig, den_hi_sig;
+
+ /* Find the highest non-zero divisor digit. */
+ for (i = 4 - 1; ; i--)
+ if (den[i] != 0) {
+ den_hi_sig = i;
+ break;
+ }
+
+ /* Insure that the first digit of the divisor is at least BASE/2.
+ This is required by the quotient digit estimation algorithm. */
+
+ scale = BASE / (den[den_hi_sig] + 1);
+ if (scale > 1) { /* scale divisor and dividend */
+ carry = 0;
+ for (i = 0; i <= 4 - 1; i++) {
+ work = (num[i] * scale) + carry;
+ num[i] = LOWPART (work);
+ carry = HIGHPART (work);
+ } num[4] = carry;
+ carry = 0;
+ for (i = 0; i <= 4 - 1; i++) {
+ work = (den[i] * scale) + carry;
+ den[i] = LOWPART (work);
+ carry = HIGHPART (work);
+ if (den[i] != 0) den_hi_sig = i;
+ }
+ }
+
+ num_hi_sig = 4;
+
+ /* Main loop */
+ for (i = num_hi_sig - den_hi_sig - 1; i >= 0; i--) {
+ /* guess the next quotient digit, quo_est, by dividing the first
+ two remaining dividend digits by the high order quotient digit.
+ quo_est is never low and is at most 2 high. */
+ unsigned HOST_WIDE_INT tmp;
+
+ num_hi_sig = i + den_hi_sig + 1;
+ work = num[num_hi_sig] * BASE + num[num_hi_sig - 1];
+ if (num[num_hi_sig] != den[den_hi_sig])
+ quo_est = work / den[den_hi_sig];
+ else
+ quo_est = BASE - 1;
+
+ /* refine quo_est so it's usually correct, and at most one high. */
+ tmp = work - quo_est * den[den_hi_sig];
+ if (tmp < BASE
+ && den[den_hi_sig - 1] * quo_est > (tmp * BASE + num[num_hi_sig - 2]))
+ quo_est--;
+
+ /* Try QUO_EST as the quotient digit, by multiplying the
+ divisor by QUO_EST and subtracting from the remaining dividend.
+ Keep in mind that QUO_EST is the I - 1st digit. */
+
+ carry = 0;
+ for (j = 0; j <= den_hi_sig; j++)
+ {
+ work = quo_est * den[j] + carry;
+ carry = HIGHPART (work);
+ work = num[i + j] - LOWPART (work);
+ num[i + j] = LOWPART (work);
+ carry += HIGHPART (work) != 0;
+ }
+
+ /* if quo_est was high by one, then num[i] went negative and
+ we need to correct things. */
+
+ if (num[num_hi_sig] < carry)
+ {
+ quo_est--;
+ carry = 0; /* add divisor back in */
+ for (j = 0; j <= den_hi_sig; j++)
+ {
+ work = num[i + j] + den[j] + carry;
+ carry = HIGHPART (work);
+ num[i + j] = LOWPART (work);
+ }
+ num [num_hi_sig] += carry;
+ }
+
+ /* store the quotient digit. */
+ quo[i] = quo_est;
+ }
+ }
+
+ decode (quo, lquo, hquo);
+
+ finish_up:
+ /* if result is negative, make it so. */
+ if (quo_neg)
+ neg_double (*lquo, *hquo, lquo, hquo);
+
+ /* compute trial remainder: rem = num - (quo * den) */
+ mul_double (*lquo, *hquo, lden_orig, hden_orig, lrem, hrem);
+ neg_double (*lrem, *hrem, lrem, hrem);
+ add_double (lnum_orig, hnum_orig, *lrem, *hrem, lrem, hrem);
+
+ switch (code)
+ {
+ case TRUNC_DIV_EXPR:
+ case TRUNC_MOD_EXPR: /* round toward zero */
+ case EXACT_DIV_EXPR: /* for this one, it shouldn't matter */
+ return overflow;
+
+ case FLOOR_DIV_EXPR:
+ case FLOOR_MOD_EXPR: /* round toward negative infinity */
+ if (quo_neg && (*lrem != 0 || *hrem != 0)) /* ratio < 0 && rem != 0 */
+ {
+ /* quo = quo - 1; */
+ add_double (*lquo, *hquo, (HOST_WIDE_INT) -1, (HOST_WIDE_INT) -1,
+ lquo, hquo);
+ }
+ else return overflow;
+ break;
+
+ case CEIL_DIV_EXPR:
+ case CEIL_MOD_EXPR: /* round toward positive infinity */
+ if (!quo_neg && (*lrem != 0 || *hrem != 0)) /* ratio > 0 && rem != 0 */
+ {
+ add_double (*lquo, *hquo, (HOST_WIDE_INT) 1, (HOST_WIDE_INT) 0,
+ lquo, hquo);
+ }
+ else return overflow;
+ break;
+
+ case ROUND_DIV_EXPR:
+ case ROUND_MOD_EXPR: /* round to closest integer */
+ {
+ HOST_WIDE_INT labs_rem = *lrem, habs_rem = *hrem;
+ HOST_WIDE_INT labs_den = lden, habs_den = hden, ltwice, htwice;
+
+ /* get absolute values */
+ if (*hrem < 0) neg_double (*lrem, *hrem, &labs_rem, &habs_rem);
+ if (hden < 0) neg_double (lden, hden, &labs_den, &habs_den);
+
+ /* if (2 * abs (lrem) >= abs (lden)) */
+ mul_double ((HOST_WIDE_INT) 2, (HOST_WIDE_INT) 0,
+ labs_rem, habs_rem, &ltwice, &htwice);
+ if (((unsigned HOST_WIDE_INT) habs_den
+ < (unsigned HOST_WIDE_INT) htwice)
+ || (((unsigned HOST_WIDE_INT) habs_den
+ == (unsigned HOST_WIDE_INT) htwice)
+ && ((HOST_WIDE_INT unsigned) labs_den
+ < (unsigned HOST_WIDE_INT) ltwice)))
+ {
+ if (*hquo < 0)
+ /* quo = quo - 1; */
+ add_double (*lquo, *hquo,
+ (HOST_WIDE_INT) -1, (HOST_WIDE_INT) -1, lquo, hquo);
+ else
+ /* quo = quo + 1; */
+ add_double (*lquo, *hquo, (HOST_WIDE_INT) 1, (HOST_WIDE_INT) 0,
+ lquo, hquo);
+ }
+ else return overflow;
+ }
+ break;
+
+ default:
+ abort ();
+ }
+
+ /* compute true remainder: rem = num - (quo * den) */
+ mul_double (*lquo, *hquo, lden_orig, hden_orig, lrem, hrem);
+ neg_double (*lrem, *hrem, lrem, hrem);
+ add_double (lnum_orig, hnum_orig, *lrem, *hrem, lrem, hrem);
+ return overflow;
+}
+
+#ifndef REAL_ARITHMETIC
+/* Effectively truncate a real value to represent the nearest possible value
+ in a narrower mode. The result is actually represented in the same data
+ type as the argument, but its value is usually different.
+
+ A trap may occur during the FP operations and it is the responsibility
+ of the calling function to have a handler established. */
+
+REAL_VALUE_TYPE
+real_value_truncate (mode, arg)
+ enum machine_mode mode;
+ REAL_VALUE_TYPE arg;
+{
+ return REAL_VALUE_TRUNCATE (mode, arg);
+}
+
+#if TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
+
+/* Check for infinity in an IEEE double precision number. */
+
+int
+target_isinf (x)
+ REAL_VALUE_TYPE x;
+{
+ /* The IEEE 64-bit double format. */
+ union {
+ REAL_VALUE_TYPE d;
+ struct {
+ unsigned sign : 1;
+ unsigned exponent : 11;
+ unsigned mantissa1 : 20;
+ unsigned mantissa2;
+ } little_endian;
+ struct {
+ unsigned mantissa2;
+ unsigned mantissa1 : 20;
+ unsigned exponent : 11;
+ unsigned sign : 1;
+ } big_endian;
+ } u;
+
+ u.d = dconstm1;
+ if (u.big_endian.sign == 1)
+ {
+ u.d = x;
+ return (u.big_endian.exponent == 2047
+ && u.big_endian.mantissa1 == 0
+ && u.big_endian.mantissa2 == 0);
+ }
+ else
+ {
+ u.d = x;
+ return (u.little_endian.exponent == 2047
+ && u.little_endian.mantissa1 == 0
+ && u.little_endian.mantissa2 == 0);
+ }
+}
+
+/* Check whether an IEEE double precision number is a NaN. */
+
+int
+target_isnan (x)
+ REAL_VALUE_TYPE x;
+{
+ /* The IEEE 64-bit double format. */
+ union {
+ REAL_VALUE_TYPE d;
+ struct {
+ unsigned sign : 1;
+ unsigned exponent : 11;
+ unsigned mantissa1 : 20;
+ unsigned mantissa2;
+ } little_endian;
+ struct {
+ unsigned mantissa2;
+ unsigned mantissa1 : 20;
+ unsigned exponent : 11;
+ unsigned sign : 1;
+ } big_endian;
+ } u;
+
+ u.d = dconstm1;
+ if (u.big_endian.sign == 1)
+ {
+ u.d = x;
+ return (u.big_endian.exponent == 2047
+ && (u.big_endian.mantissa1 != 0
+ || u.big_endian.mantissa2 != 0));
+ }
+ else
+ {
+ u.d = x;
+ return (u.little_endian.exponent == 2047
+ && (u.little_endian.mantissa1 != 0
+ || u.little_endian.mantissa2 != 0));
+ }
+}
+
+/* Check for a negative IEEE double precision number. */
+
+int
+target_negative (x)
+ REAL_VALUE_TYPE x;
+{
+ /* The IEEE 64-bit double format. */
+ union {
+ REAL_VALUE_TYPE d;
+ struct {
+ unsigned sign : 1;
+ unsigned exponent : 11;
+ unsigned mantissa1 : 20;
+ unsigned mantissa2;
+ } little_endian;
+ struct {
+ unsigned mantissa2;
+ unsigned mantissa1 : 20;
+ unsigned exponent : 11;
+ unsigned sign : 1;
+ } big_endian;
+ } u;
+
+ u.d = dconstm1;
+ if (u.big_endian.sign == 1)
+ {
+ u.d = x;
+ return u.big_endian.sign;
+ }
+ else
+ {
+ u.d = x;
+ return u.little_endian.sign;
+ }
+}
+#else /* Target not IEEE */
+
+/* Let's assume other float formats don't have infinity.
+ (This can be overridden by redefining REAL_VALUE_ISINF.) */
+
+target_isinf (x)
+ REAL_VALUE_TYPE x;
+{
+ return 0;
+}
+
+/* Let's assume other float formats don't have NaNs.
+ (This can be overridden by redefining REAL_VALUE_ISNAN.) */
+
+target_isnan (x)
+ REAL_VALUE_TYPE x;
+{
+ return 0;
+}
+
+/* Let's assume other float formats don't have minus zero.
+ (This can be overridden by redefining REAL_VALUE_NEGATIVE.) */
+
+target_negative (x)
+ REAL_VALUE_TYPE x;
+{
+ return x < 0;
+}
+#endif /* Target not IEEE */
+#endif /* no REAL_ARITHMETIC */
+
+/* Split a tree IN into a constant and a variable part
+ that could be combined with CODE to make IN.
+ CODE must be a commutative arithmetic operation.
+ Store the constant part into *CONP and the variable in &VARP.
+ Return 1 if this was done; zero means the tree IN did not decompose
+ this way.
+
+ If CODE is PLUS_EXPR we also split trees that use MINUS_EXPR.
+ Therefore, we must tell the caller whether the variable part
+ was subtracted. We do this by storing 1 or -1 into *VARSIGNP.
+ The value stored is the coefficient for the variable term.
+ The constant term we return should always be added;
+ we negate it if necessary. */
+
+static int
+split_tree (in, code, varp, conp, varsignp)
+ tree in;
+ enum tree_code code;
+ tree *varp, *conp;
+ int *varsignp;
+{
+ register tree outtype = TREE_TYPE (in);
+ *varp = 0;
+ *conp = 0;
+
+ /* Strip any conversions that don't change the machine mode. */
+ while ((TREE_CODE (in) == NOP_EXPR
+ || TREE_CODE (in) == CONVERT_EXPR)
+ && (TYPE_MODE (TREE_TYPE (in))
+ == TYPE_MODE (TREE_TYPE (TREE_OPERAND (in, 0)))))
+ in = TREE_OPERAND (in, 0);
+
+ if (TREE_CODE (in) == code
+ || (! FLOAT_TYPE_P (TREE_TYPE (in))
+ /* We can associate addition and subtraction together
+ (even though the C standard doesn't say so)
+ for integers because the value is not affected.
+ For reals, the value might be affected, so we can't. */
+ && ((code == PLUS_EXPR && TREE_CODE (in) == MINUS_EXPR)
+ || (code == MINUS_EXPR && TREE_CODE (in) == PLUS_EXPR))))
+ {
+ enum tree_code code = TREE_CODE (TREE_OPERAND (in, 0));
+ if (code == INTEGER_CST)
+ {
+ *conp = TREE_OPERAND (in, 0);
+ *varp = TREE_OPERAND (in, 1);
+ if (TYPE_MODE (TREE_TYPE (*varp)) != TYPE_MODE (outtype)
+ && TREE_TYPE (*varp) != outtype)
+ *varp = convert (outtype, *varp);
+ *varsignp = (TREE_CODE (in) == MINUS_EXPR) ? -1 : 1;
+ return 1;
+ }
+ if (TREE_CONSTANT (TREE_OPERAND (in, 1)))
+ {
+ *conp = TREE_OPERAND (in, 1);
+ *varp = TREE_OPERAND (in, 0);
+ *varsignp = 1;
+ if (TYPE_MODE (TREE_TYPE (*varp)) != TYPE_MODE (outtype)
+ && TREE_TYPE (*varp) != outtype)
+ *varp = convert (outtype, *varp);
+ if (TREE_CODE (in) == MINUS_EXPR)
+ {
+ /* If operation is subtraction and constant is second,
+ must negate it to get an additive constant.
+ And this cannot be done unless it is a manifest constant.
+ It could also be the address of a static variable.
+ We cannot negate that, so give up. */
+ if (TREE_CODE (*conp) == INTEGER_CST)
+ /* Subtracting from integer_zero_node loses for long long. */
+ *conp = fold (build1 (NEGATE_EXPR, TREE_TYPE (*conp), *conp));
+ else
+ return 0;
+ }
+ return 1;
+ }
+ if (TREE_CONSTANT (TREE_OPERAND (in, 0)))
+ {
+ *conp = TREE_OPERAND (in, 0);
+ *varp = TREE_OPERAND (in, 1);
+ if (TYPE_MODE (TREE_TYPE (*varp)) != TYPE_MODE (outtype)
+ && TREE_TYPE (*varp) != outtype)
+ *varp = convert (outtype, *varp);
+ *varsignp = (TREE_CODE (in) == MINUS_EXPR) ? -1 : 1;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Combine two constants NUM and ARG2 under operation CODE
+ to produce a new constant.
+ We assume ARG1 and ARG2 have the same data type,
+ or at least are the same kind of constant and the same machine mode.
+
+ If NOTRUNC is nonzero, do not truncate the result to fit the data type. */
+
+static tree
+const_binop (code, arg1, arg2, notrunc)
+ enum tree_code code;
+ register tree arg1, arg2;
+ int notrunc;
+{
+ if (TREE_CODE (arg1) == INTEGER_CST)
+ {
+ register HOST_WIDE_INT int1l = TREE_INT_CST_LOW (arg1);
+ register HOST_WIDE_INT int1h = TREE_INT_CST_HIGH (arg1);
+ HOST_WIDE_INT int2l = TREE_INT_CST_LOW (arg2);
+ HOST_WIDE_INT int2h = TREE_INT_CST_HIGH (arg2);
+ HOST_WIDE_INT low, hi;
+ HOST_WIDE_INT garbagel, garbageh;
+ register tree t;
+ int uns = TREE_UNSIGNED (TREE_TYPE (arg1));
+ int overflow = 0;
+
+ switch (code)
+ {
+ case BIT_IOR_EXPR:
+ t = build_int_2 (int1l | int2l, int1h | int2h);
+ break;
+
+ case BIT_XOR_EXPR:
+ t = build_int_2 (int1l ^ int2l, int1h ^ int2h);
+ break;
+
+ case BIT_AND_EXPR:
+ t = build_int_2 (int1l & int2l, int1h & int2h);
+ break;
+
+ case BIT_ANDTC_EXPR:
+ t = build_int_2 (int1l & ~int2l, int1h & ~int2h);
+ break;
+
+ case RSHIFT_EXPR:
+ int2l = - int2l;
+ case LSHIFT_EXPR:
+ /* It's unclear from the C standard whether shifts can overflow.
+ The following code ignores overflow; perhaps a C standard
+ interpretation ruling is needed. */
+ lshift_double (int1l, int1h, int2l,
+ TYPE_PRECISION (TREE_TYPE (arg1)),
+ &low, &hi,
+ !uns);
+ t = build_int_2 (low, hi);
+ TREE_TYPE (t) = TREE_TYPE (arg1);
+ if (!notrunc)
+ force_fit_type (t, 0);
+ TREE_OVERFLOW (t) = TREE_OVERFLOW (arg1) | TREE_OVERFLOW (arg2);
+ TREE_CONSTANT_OVERFLOW (t)
+ = TREE_CONSTANT_OVERFLOW (arg1) | TREE_CONSTANT_OVERFLOW (arg2);
+ return t;
+
+ case RROTATE_EXPR:
+ int2l = - int2l;
+ case LROTATE_EXPR:
+ lrotate_double (int1l, int1h, int2l,
+ TYPE_PRECISION (TREE_TYPE (arg1)),
+ &low, &hi);
+ t = build_int_2 (low, hi);
+ break;
+
+ case PLUS_EXPR:
+ if (int1h == 0)
+ {
+ int2l += int1l;
+ if ((unsigned HOST_WIDE_INT) int2l < int1l)
+ {
+ hi = int2h++;
+ overflow = int2h < hi;
+ }
+ t = build_int_2 (int2l, int2h);
+ break;
+ }
+ if (int2h == 0)
+ {
+ int1l += int2l;
+ if ((unsigned HOST_WIDE_INT) int1l < int2l)
+ {
+ hi = int1h++;
+ overflow = int1h < hi;
+ }
+ t = build_int_2 (int1l, int1h);
+ break;
+ }
+ overflow = add_double (int1l, int1h, int2l, int2h, &low, &hi);
+ t = build_int_2 (low, hi);
+ break;
+
+ case MINUS_EXPR:
+ if (int2h == 0 && int2l == 0)
+ {
+ t = build_int_2 (int1l, int1h);
+ break;
+ }
+ neg_double (int2l, int2h, &low, &hi);
+ add_double (int1l, int1h, low, hi, &low, &hi);
+ overflow = overflow_sum_sign (hi, int2h, int1h);
+ t = build_int_2 (low, hi);
+ break;
+
+ case MULT_EXPR:
+ overflow = mul_double (int1l, int1h, int2l, int2h, &low, &hi);
+ t = build_int_2 (low, hi);
+ break;
+
+ case TRUNC_DIV_EXPR:
+ case FLOOR_DIV_EXPR: case CEIL_DIV_EXPR:
+ case EXACT_DIV_EXPR:
+ /* This is a shortcut for a common special case.
+ It reduces the number of tree nodes generated
+ and saves time. */
+ if (int2h == 0 && int2l > 0
+ && TREE_TYPE (arg1) == sizetype
+ && int1h == 0 && int1l >= 0)
+ {
+ if (code == CEIL_DIV_EXPR)
+ int1l += int2l-1;
+ return size_int (int1l / int2l);
+ }
+ case ROUND_DIV_EXPR:
+ if (int2h == 0 && int2l == 1)
+ {
+ t = build_int_2 (int1l, int1h);
+ break;
+ }
+ if (int1l == int2l && int1h == int2h)
+ {
+ if ((int1l | int1h) == 0)
+ abort ();
+ t = build_int_2 (1, 0);
+ break;
+ }
+ overflow = div_and_round_double (code, uns,
+ int1l, int1h, int2l, int2h,
+ &low, &hi, &garbagel, &garbageh);
+ t = build_int_2 (low, hi);
+ break;
+
+ case TRUNC_MOD_EXPR: case ROUND_MOD_EXPR:
+ case FLOOR_MOD_EXPR: case CEIL_MOD_EXPR:
+ overflow = div_and_round_double (code, uns,
+ int1l, int1h, int2l, int2h,
+ &garbagel, &garbageh, &low, &hi);
+ t = build_int_2 (low, hi);
+ break;
+
+ case MIN_EXPR:
+ case MAX_EXPR:
+ if (uns)
+ {
+ low = (((unsigned HOST_WIDE_INT) int1h
+ < (unsigned HOST_WIDE_INT) int2h)
+ || (((unsigned HOST_WIDE_INT) int1h
+ == (unsigned HOST_WIDE_INT) int2h)
+ && ((unsigned HOST_WIDE_INT) int1l
+ < (unsigned HOST_WIDE_INT) int2l)));
+ }
+ else
+ {
+ low = ((int1h < int2h)
+ || ((int1h == int2h)
+ && ((unsigned HOST_WIDE_INT) int1l
+ < (unsigned HOST_WIDE_INT) int2l)));
+ }
+ if (low == (code == MIN_EXPR))
+ t = build_int_2 (int1l, int1h);
+ else
+ t = build_int_2 (int2l, int2h);
+ break;
+
+ default:
+ abort ();
+ }
+ got_it:
+ TREE_TYPE (t) = TREE_TYPE (arg1);
+ TREE_OVERFLOW (t)
+ = ((notrunc ? !uns && overflow : force_fit_type (t, overflow))
+ | TREE_OVERFLOW (arg1)
+ | TREE_OVERFLOW (arg2));
+ TREE_CONSTANT_OVERFLOW (t) = (TREE_OVERFLOW (t)
+ | TREE_CONSTANT_OVERFLOW (arg1)
+ | TREE_CONSTANT_OVERFLOW (arg2));
+ return t;
+ }
+#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
+ if (TREE_CODE (arg1) == REAL_CST)
+ {
+ REAL_VALUE_TYPE d1;
+ REAL_VALUE_TYPE d2;
+ int overflow = 0;
+ REAL_VALUE_TYPE value;
+ tree t;
+
+ d1 = TREE_REAL_CST (arg1);
+ d2 = TREE_REAL_CST (arg2);
+
+ /* If either operand is a NaN, just return it. Otherwise, set up
+ for floating-point trap; we return an overflow. */
+ if (REAL_VALUE_ISNAN (d1))
+ return arg1;
+ else if (REAL_VALUE_ISNAN (d2))
+ return arg2;
+ else if (setjmp (float_error))
+ {
+ t = copy_node (arg1);
+ overflow = 1;
+ goto got_float;
+ }
+
+ set_float_handler (float_error);
+
+#ifdef REAL_ARITHMETIC
+ REAL_ARITHMETIC (value, code, d1, d2);
+#else
+ switch (code)
+ {
+ case PLUS_EXPR:
+ value = d1 + d2;
+ break;
+
+ case MINUS_EXPR:
+ value = d1 - d2;
+ break;
+
+ case MULT_EXPR:
+ value = d1 * d2;
+ break;
+
+ case RDIV_EXPR:
+#ifndef REAL_INFINITY
+ if (d2 == 0)
+ abort ();
+#endif
+
+ value = d1 / d2;
+ break;
+
+ case MIN_EXPR:
+ value = MIN (d1, d2);
+ break;
+
+ case MAX_EXPR:
+ value = MAX (d1, d2);
+ break;
+
+ default:
+ abort ();
+ }
+#endif /* no REAL_ARITHMETIC */
+ t = build_real (TREE_TYPE (arg1),
+ real_value_truncate (TYPE_MODE (TREE_TYPE (arg1)), value));
+ got_float:
+ set_float_handler (NULL_PTR);
+
+ TREE_OVERFLOW (t)
+ = (force_fit_type (t, overflow)
+ | TREE_OVERFLOW (arg1) | TREE_OVERFLOW (arg2));
+ TREE_CONSTANT_OVERFLOW (t)
+ = TREE_OVERFLOW (t)
+ | TREE_CONSTANT_OVERFLOW (arg1)
+ | TREE_CONSTANT_OVERFLOW (arg2);
+ return t;
+ }
+#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
+ if (TREE_CODE (arg1) == COMPLEX_CST)
+ {
+ register tree r1 = TREE_REALPART (arg1);
+ register tree i1 = TREE_IMAGPART (arg1);
+ register tree r2 = TREE_REALPART (arg2);
+ register tree i2 = TREE_IMAGPART (arg2);
+ register tree t;
+
+ switch (code)
+ {
+ case PLUS_EXPR:
+ t = build_complex (const_binop (PLUS_EXPR, r1, r2, notrunc),
+ const_binop (PLUS_EXPR, i1, i2, notrunc));
+ break;
+
+ case MINUS_EXPR:
+ t = build_complex (const_binop (MINUS_EXPR, r1, r2, notrunc),
+ const_binop (MINUS_EXPR, i1, i2, notrunc));
+ break;
+
+ case MULT_EXPR:
+ t = build_complex (const_binop (MINUS_EXPR,
+ const_binop (MULT_EXPR,
+ r1, r2, notrunc),
+ const_binop (MULT_EXPR,
+ i1, i2, notrunc),
+ notrunc),
+ const_binop (PLUS_EXPR,
+ const_binop (MULT_EXPR,
+ r1, i2, notrunc),
+ const_binop (MULT_EXPR,
+ i1, r2, notrunc),
+ notrunc));
+ break;
+
+ case RDIV_EXPR:
+ {
+ register tree magsquared
+ = const_binop (PLUS_EXPR,
+ const_binop (MULT_EXPR, r2, r2, notrunc),
+ const_binop (MULT_EXPR, i2, i2, notrunc),
+ notrunc);
+
+ t = build_complex
+ (const_binop (INTEGRAL_TYPE_P (TREE_TYPE (r1))
+ ? TRUNC_DIV_EXPR : RDIV_EXPR,
+ const_binop (PLUS_EXPR,
+ const_binop (MULT_EXPR, r1, r2,
+ notrunc),
+ const_binop (MULT_EXPR, i1, i2,
+ notrunc),
+ notrunc),
+ magsquared, notrunc),
+ const_binop (INTEGRAL_TYPE_P (TREE_TYPE (r1))
+ ? TRUNC_DIV_EXPR : RDIV_EXPR,
+ const_binop (MINUS_EXPR,
+ const_binop (MULT_EXPR, i1, r2,
+ notrunc),
+ const_binop (MULT_EXPR, r1, i2,
+ notrunc),
+ notrunc),
+ magsquared, notrunc));
+ }
+ break;
+
+ default:
+ abort ();
+ }
+ TREE_TYPE (t) = TREE_TYPE (arg1);
+ return t;
+ }
+ return 0;
+}
+
+/* Return an INTEGER_CST with value V and type from `sizetype'. */
+
+tree
+size_int (number)
+ unsigned int number;
+{
+ register tree t;
+ /* Type-size nodes already made for small sizes. */
+ static tree size_table[2*HOST_BITS_PER_WIDE_INT + 1];
+
+ if (number < 2*HOST_BITS_PER_WIDE_INT + 1
+ && size_table[number] != 0)
+ return size_table[number];
+ if (number < 2*HOST_BITS_PER_WIDE_INT + 1)
+ {
+ push_obstacks_nochange ();
+ /* Make this a permanent node. */
+ end_temporary_allocation ();
+ t = build_int_2 (number, 0);
+ TREE_TYPE (t) = sizetype;
+ size_table[number] = t;
+ pop_obstacks ();
+ }
+ else
+ {
+ t = build_int_2 (number, 0);
+ TREE_TYPE (t) = sizetype;
+ }
+ return t;
+}
+
+/* Combine operands OP1 and OP2 with arithmetic operation CODE.
+ CODE is a tree code. Data type is taken from `sizetype',
+ If the operands are constant, so is the result. */
+
+tree
+size_binop (code, arg0, arg1)
+ enum tree_code code;
+ tree arg0, arg1;
+{
+ /* Handle the special case of two integer constants faster. */
+ if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
+ {
+ /* And some specific cases even faster than that. */
+ if (code == PLUS_EXPR
+ && TREE_INT_CST_LOW (arg0) == 0
+ && TREE_INT_CST_HIGH (arg0) == 0)
+ return arg1;
+ if (code == MINUS_EXPR
+ && TREE_INT_CST_LOW (arg1) == 0
+ && TREE_INT_CST_HIGH (arg1) == 0)
+ return arg0;
+ if (code == MULT_EXPR
+ && TREE_INT_CST_LOW (arg0) == 1
+ && TREE_INT_CST_HIGH (arg0) == 0)
+ return arg1;
+ /* Handle general case of two integer constants. */
+ return const_binop (code, arg0, arg1, 1);
+ }
+
+ if (arg0 == error_mark_node || arg1 == error_mark_node)
+ return error_mark_node;
+
+ return fold (build (code, sizetype, arg0, arg1));
+}
+
+/* Given T, a tree representing type conversion of ARG1, a constant,
+ return a constant tree representing the result of conversion. */
+
+static tree
+fold_convert (t, arg1)
+ register tree t;
+ register tree arg1;
+{
+ register tree type = TREE_TYPE (t);
+ int overflow = 0;
+
+ if (TREE_CODE (type) == POINTER_TYPE || INTEGRAL_TYPE_P (type))
+ {
+ if (TREE_CODE (arg1) == INTEGER_CST)
+ {
+ /* Given an integer constant, make new constant with new type,
+ appropriately sign-extended or truncated. */
+ t = build_int_2 (TREE_INT_CST_LOW (arg1),
+ TREE_INT_CST_HIGH (arg1));
+ TREE_TYPE (t) = type;
+ /* Indicate an overflow if (1) ARG1 already overflowed,
+ or (2) force_fit_type indicates an overflow.
+ Tell force_fit_type that an overflow has already occurred
+ if ARG1 is a too-large unsigned value and T is signed. */
+ TREE_OVERFLOW (t)
+ = (TREE_OVERFLOW (arg1)
+ | force_fit_type (t,
+ (TREE_INT_CST_HIGH (arg1) < 0
+ & (TREE_UNSIGNED (type)
+ < TREE_UNSIGNED (TREE_TYPE (arg1))))));
+ TREE_CONSTANT_OVERFLOW (t)
+ = TREE_OVERFLOW (t) | TREE_CONSTANT_OVERFLOW (arg1);
+ }
+#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
+ else if (TREE_CODE (arg1) == REAL_CST)
+ {
+ /* Don't initialize these, use assignments.
+ Initialized local aggregates don't work on old compilers. */
+ REAL_VALUE_TYPE x;
+ REAL_VALUE_TYPE l;
+ REAL_VALUE_TYPE u;
+
+ x = TREE_REAL_CST (arg1);
+ l = real_value_from_int_cst (TYPE_MIN_VALUE (type));
+ u = real_value_from_int_cst (TYPE_MAX_VALUE (type));
+ /* See if X will be in range after truncation towards 0.
+ To compensate for truncation, move the bounds away from 0,
+ but reject if X exactly equals the adjusted bounds. */
+#ifdef REAL_ARITHMETIC
+ REAL_ARITHMETIC (l, MINUS_EXPR, l, dconst1);
+ REAL_ARITHMETIC (u, PLUS_EXPR, u, dconst1);
+#else
+ l--;
+ u++;
+#endif
+ /* If X is a NaN, use zero instead and show we have an overflow.
+ Otherwise, range check. */
+ if (REAL_VALUE_ISNAN (x))
+ overflow = 1, x = dconst0;
+ else if (! (REAL_VALUES_LESS (l, x) && REAL_VALUES_LESS (x, u)))
+ overflow = 1;
+
+#ifndef REAL_ARITHMETIC
+ {
+ HOST_WIDE_INT low, high;
+ HOST_WIDE_INT half_word
+ = (HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2);
+
+ if (x < 0)
+ x = -x;
+
+ high = (HOST_WIDE_INT) (x / half_word / half_word);
+ x -= (REAL_VALUE_TYPE) high * half_word * half_word;
+ if (x >= (REAL_VALUE_TYPE) half_word * half_word / 2)
+ {
+ low = x - (REAL_VALUE_TYPE) half_word * half_word / 2;
+ low |= (HOST_WIDE_INT) -1 << (HOST_BITS_PER_WIDE_INT - 1);
+ }
+ else
+ low = (HOST_WIDE_INT) x;
+ if (TREE_REAL_CST (arg1) < 0)
+ neg_double (low, high, &low, &high);
+ t = build_int_2 (low, high);
+ }
+#else
+ {
+ HOST_WIDE_INT low, high;
+ REAL_VALUE_TO_INT (&low, &high, x);
+ t = build_int_2 (low, high);
+ }
+#endif
+ TREE_TYPE (t) = type;
+ TREE_OVERFLOW (t)
+ = TREE_OVERFLOW (arg1) | force_fit_type (t, overflow);
+ TREE_CONSTANT_OVERFLOW (t)
+ = TREE_OVERFLOW (t) | TREE_CONSTANT_OVERFLOW (arg1);
+ }
+#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
+ TREE_TYPE (t) = type;
+ }
+ else if (TREE_CODE (type) == REAL_TYPE)
+ {
+#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
+ if (TREE_CODE (arg1) == INTEGER_CST)
+ return build_real_from_int_cst (type, arg1);
+#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
+ if (TREE_CODE (arg1) == REAL_CST)
+ {
+ if (REAL_VALUE_ISNAN (TREE_REAL_CST (arg1)))
+ return arg1;
+ else if (setjmp (float_error))
+ {
+ overflow = 1;
+ t = copy_node (arg1);
+ goto got_it;
+ }
+ set_float_handler (float_error);
+
+ t = build_real (type, real_value_truncate (TYPE_MODE (type),
+ TREE_REAL_CST (arg1)));
+ set_float_handler (NULL_PTR);
+
+ got_it:
+ TREE_OVERFLOW (t)
+ = TREE_OVERFLOW (arg1) | force_fit_type (t, overflow);
+ TREE_CONSTANT_OVERFLOW (t)
+ = TREE_OVERFLOW (t) | TREE_CONSTANT_OVERFLOW (arg1);
+ return t;
+ }
+ }
+ TREE_CONSTANT (t) = 1;
+ return t;
+}
+
+/* Return an expr equal to X but certainly not valid as an lvalue.
+ Also make sure it is not valid as an null pointer constant. */
+
+tree
+non_lvalue (x)
+ tree x;
+{
+ tree result;
+
+ /* These things are certainly not lvalues. */
+ if (TREE_CODE (x) == NON_LVALUE_EXPR
+ || TREE_CODE (x) == INTEGER_CST
+ || TREE_CODE (x) == REAL_CST
+ || TREE_CODE (x) == STRING_CST
+ || TREE_CODE (x) == ADDR_EXPR)
+ {
+ if (TREE_CODE (x) == INTEGER_CST && integer_zerop (x))
+ {
+ /* Use NOP_EXPR instead of NON_LVALUE_EXPR
+ so convert_for_assignment won't strip it.
+ This is so this 0 won't be treated as a null pointer constant. */
+ result = build1 (NOP_EXPR, TREE_TYPE (x), x);
+ TREE_CONSTANT (result) = TREE_CONSTANT (x);
+ return result;
+ }
+ return x;
+ }
+
+ result = build1 (NON_LVALUE_EXPR, TREE_TYPE (x), x);
+ TREE_CONSTANT (result) = TREE_CONSTANT (x);
+ return result;
+}
+
+/* When pedantic, return an expr equal to X but certainly not valid as a
+ pedantic lvalue. Otherwise, return X. */
+
+tree
+pedantic_non_lvalue (x)
+ tree x;
+{
+ if (pedantic)
+ return non_lvalue (x);
+ else
+ return x;
+}
+
+/* Given a tree comparison code, return the code that is the logical inverse
+ of the given code. It is not safe to do this for floating-point
+ comparisons, except for NE_EXPR and EQ_EXPR. */
+
+static enum tree_code
+invert_tree_comparison (code)
+ enum tree_code code;
+{
+ switch (code)
+ {
+ case EQ_EXPR:
+ return NE_EXPR;
+ case NE_EXPR:
+ return EQ_EXPR;
+ case GT_EXPR:
+ return LE_EXPR;
+ case GE_EXPR:
+ return LT_EXPR;
+ case LT_EXPR:
+ return GE_EXPR;
+ case LE_EXPR:
+ return GT_EXPR;
+ default:
+ abort ();
+ }
+}
+
+/* Similar, but return the comparison that results if the operands are
+ swapped. This is safe for floating-point. */
+
+static enum tree_code
+swap_tree_comparison (code)
+ enum tree_code code;
+{
+ switch (code)
+ {
+ case EQ_EXPR:
+ case NE_EXPR:
+ return code;
+ case GT_EXPR:
+ return LT_EXPR;
+ case GE_EXPR:
+ return LE_EXPR;
+ case LT_EXPR:
+ return GT_EXPR;
+ case LE_EXPR:
+ return GE_EXPR;
+ default:
+ abort ();
+ }
+}
+
+/* Return nonzero if CODE is a tree code that represents a truth value. */
+
+static int
+truth_value_p (code)
+ enum tree_code code;
+{
+ return (TREE_CODE_CLASS (code) == '<'
+ || code == TRUTH_AND_EXPR || code == TRUTH_ANDIF_EXPR
+ || code == TRUTH_OR_EXPR || code == TRUTH_ORIF_EXPR
+ || code == TRUTH_XOR_EXPR || code == TRUTH_NOT_EXPR);
+}
+
+/* Return nonzero if two operands are necessarily equal.
+ If ONLY_CONST is non-zero, only return non-zero for constants.
+ This function tests whether the operands are indistinguishable;
+ it does not test whether they are equal using C's == operation.
+ The distinction is important for IEEE floating point, because
+ (1) -0.0 and 0.0 are distinguishable, but -0.0==0.0, and
+ (2) two NaNs may be indistinguishable, but NaN!=NaN. */
+
+int
+operand_equal_p (arg0, arg1, only_const)
+ tree arg0, arg1;
+ int only_const;
+{
+ /* If both types don't have the same signedness, then we can't consider
+ them equal. We must check this before the STRIP_NOPS calls
+ because they may change the signedness of the arguments. */
+ if (TREE_UNSIGNED (TREE_TYPE (arg0)) != TREE_UNSIGNED (TREE_TYPE (arg1)))
+ return 0;
+
+ STRIP_NOPS (arg0);
+ STRIP_NOPS (arg1);
+
+ /* If ARG0 and ARG1 are the same SAVE_EXPR, they are necessarily equal.
+ We don't care about side effects in that case because the SAVE_EXPR
+ takes care of that for us. */
+ if (TREE_CODE (arg0) == SAVE_EXPR && arg0 == arg1)
+ return ! only_const;
+
+ if (TREE_SIDE_EFFECTS (arg0) || TREE_SIDE_EFFECTS (arg1))
+ return 0;
+
+ if (TREE_CODE (arg0) == TREE_CODE (arg1)
+ && TREE_CODE (arg0) == ADDR_EXPR
+ && TREE_OPERAND (arg0, 0) == TREE_OPERAND (arg1, 0))
+ return 1;
+
+ if (TREE_CODE (arg0) == TREE_CODE (arg1)
+ && TREE_CODE (arg0) == INTEGER_CST
+ && TREE_INT_CST_LOW (arg0) == TREE_INT_CST_LOW (arg1)
+ && TREE_INT_CST_HIGH (arg0) == TREE_INT_CST_HIGH (arg1))
+ return 1;
+
+ /* Detect when real constants are equal. */
+ if (TREE_CODE (arg0) == TREE_CODE (arg1)
+ && TREE_CODE (arg0) == REAL_CST)
+ return !bcmp ((char *) &TREE_REAL_CST (arg0),
+ (char *) &TREE_REAL_CST (arg1),
+ sizeof (REAL_VALUE_TYPE));
+
+ if (only_const)
+ return 0;
+
+ if (arg0 == arg1)
+ return 1;
+
+ if (TREE_CODE (arg0) != TREE_CODE (arg1))
+ return 0;
+ /* This is needed for conversions and for COMPONENT_REF.
+ Might as well play it safe and always test this. */
+ if (TYPE_MODE (TREE_TYPE (arg0)) != TYPE_MODE (TREE_TYPE (arg1)))
+ return 0;
+
+ switch (TREE_CODE_CLASS (TREE_CODE (arg0)))
+ {
+ case '1':
+ /* Two conversions are equal only if signedness and modes match. */
+ if ((TREE_CODE (arg0) == NOP_EXPR || TREE_CODE (arg0) == CONVERT_EXPR)
+ && (TREE_UNSIGNED (TREE_TYPE (arg0))
+ != TREE_UNSIGNED (TREE_TYPE (arg1))))
+ return 0;
+
+ return operand_equal_p (TREE_OPERAND (arg0, 0),
+ TREE_OPERAND (arg1, 0), 0);
+
+ case '<':
+ case '2':
+ return (operand_equal_p (TREE_OPERAND (arg0, 0),
+ TREE_OPERAND (arg1, 0), 0)
+ && operand_equal_p (TREE_OPERAND (arg0, 1),
+ TREE_OPERAND (arg1, 1), 0));
+
+ case 'r':
+ switch (TREE_CODE (arg0))
+ {
+ case INDIRECT_REF:
+ return operand_equal_p (TREE_OPERAND (arg0, 0),
+ TREE_OPERAND (arg1, 0), 0);
+
+ case COMPONENT_REF:
+ case ARRAY_REF:
+ return (operand_equal_p (TREE_OPERAND (arg0, 0),
+ TREE_OPERAND (arg1, 0), 0)
+ && operand_equal_p (TREE_OPERAND (arg0, 1),
+ TREE_OPERAND (arg1, 1), 0));
+
+ case BIT_FIELD_REF:
+ return (operand_equal_p (TREE_OPERAND (arg0, 0),
+ TREE_OPERAND (arg1, 0), 0)
+ && operand_equal_p (TREE_OPERAND (arg0, 1),
+ TREE_OPERAND (arg1, 1), 0)
+ && operand_equal_p (TREE_OPERAND (arg0, 2),
+ TREE_OPERAND (arg1, 2), 0));
+ }
+ break;
+ }
+
+ return 0;
+}
+
+/* Similar to operand_equal_p, but see if ARG0 might have been made by
+ shorten_compare from ARG1 when ARG1 was being compared with OTHER.
+
+ When in doubt, return 0. */
+
+static int
+operand_equal_for_comparison_p (arg0, arg1, other)
+ tree arg0, arg1;
+ tree other;
+{
+ int unsignedp1, unsignedpo;
+ tree primarg1, primother;
+ unsigned correct_width;
+
+ if (operand_equal_p (arg0, arg1, 0))
+ return 1;
+
+ if (! INTEGRAL_TYPE_P (TREE_TYPE (arg0)))
+ return 0;
+
+ /* Duplicate what shorten_compare does to ARG1 and see if that gives the
+ actual comparison operand, ARG0.
+
+ First throw away any conversions to wider types
+ already present in the operands. */
+
+ primarg1 = get_narrower (arg1, &unsignedp1);
+ primother = get_narrower (other, &unsignedpo);
+
+ correct_width = TYPE_PRECISION (TREE_TYPE (arg1));
+ if (unsignedp1 == unsignedpo
+ && TYPE_PRECISION (TREE_TYPE (primarg1)) < correct_width
+ && TYPE_PRECISION (TREE_TYPE (primother)) < correct_width)
+ {
+ tree type = TREE_TYPE (arg0);
+
+ /* Make sure shorter operand is extended the right way
+ to match the longer operand. */
+ primarg1 = convert (signed_or_unsigned_type (unsignedp1,
+ TREE_TYPE (primarg1)),
+ primarg1);
+
+ if (operand_equal_p (arg0, convert (type, primarg1), 0))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* See if ARG is an expression that is either a comparison or is performing
+ arithmetic on comparisons. The comparisons must only be comparing
+ two different values, which will be stored in *CVAL1 and *CVAL2; if
+ they are non-zero it means that some operands have already been found.
+ No variables may be used anywhere else in the expression except in the
+ comparisons. If SAVE_P is true it means we removed a SAVE_EXPR around
+ the expression and save_expr needs to be called with CVAL1 and CVAL2.
+
+ If this is true, return 1. Otherwise, return zero. */
+
+static int
+twoval_comparison_p (arg, cval1, cval2, save_p)
+ tree arg;
+ tree *cval1, *cval2;
+ int *save_p;
+{
+ enum tree_code code = TREE_CODE (arg);
+ char class = TREE_CODE_CLASS (code);
+
+ /* We can handle some of the 'e' cases here. */
+ if (class == 'e' && code == TRUTH_NOT_EXPR)
+ class = '1';
+ else if (class == 'e'
+ && (code == TRUTH_ANDIF_EXPR || code == TRUTH_ORIF_EXPR
+ || code == COMPOUND_EXPR))
+ class = '2';
+
+ /* ??? Disable this since the SAVE_EXPR might already be in use outside
+ the expression. There may be no way to make this work, but it needs
+ to be looked at again for 2.6. */
+#if 0
+ else if (class == 'e' && code == SAVE_EXPR && SAVE_EXPR_RTL (arg) == 0)
+ {
+ /* If we've already found a CVAL1 or CVAL2, this expression is
+ two complex to handle. */
+ if (*cval1 || *cval2)
+ return 0;
+
+ class = '1';
+ *save_p = 1;
+ }
+#endif
+
+ switch (class)
+ {
+ case '1':
+ return twoval_comparison_p (TREE_OPERAND (arg, 0), cval1, cval2, save_p);
+
+ case '2':
+ return (twoval_comparison_p (TREE_OPERAND (arg, 0), cval1, cval2, save_p)
+ && twoval_comparison_p (TREE_OPERAND (arg, 1),
+ cval1, cval2, save_p));
+
+ case 'c':
+ return 1;
+
+ case 'e':
+ if (code == COND_EXPR)
+ return (twoval_comparison_p (TREE_OPERAND (arg, 0),
+ cval1, cval2, save_p)
+ && twoval_comparison_p (TREE_OPERAND (arg, 1),
+ cval1, cval2, save_p)
+ && twoval_comparison_p (TREE_OPERAND (arg, 2),
+ cval1, cval2, save_p));
+ return 0;
+
+ case '<':
+ /* First see if we can handle the first operand, then the second. For
+ the second operand, we know *CVAL1 can't be zero. It must be that
+ one side of the comparison is each of the values; test for the
+ case where this isn't true by failing if the two operands
+ are the same. */
+
+ if (operand_equal_p (TREE_OPERAND (arg, 0),
+ TREE_OPERAND (arg, 1), 0))
+ return 0;
+
+ if (*cval1 == 0)
+ *cval1 = TREE_OPERAND (arg, 0);
+ else if (operand_equal_p (*cval1, TREE_OPERAND (arg, 0), 0))
+ ;
+ else if (*cval2 == 0)
+ *cval2 = TREE_OPERAND (arg, 0);
+ else if (operand_equal_p (*cval2, TREE_OPERAND (arg, 0), 0))
+ ;
+ else
+ return 0;
+
+ if (operand_equal_p (*cval1, TREE_OPERAND (arg, 1), 0))
+ ;
+ else if (*cval2 == 0)
+ *cval2 = TREE_OPERAND (arg, 1);
+ else if (operand_equal_p (*cval2, TREE_OPERAND (arg, 1), 0))
+ ;
+ else
+ return 0;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/* ARG is a tree that is known to contain just arithmetic operations and
+ comparisons. Evaluate the operations in the tree substituting NEW0 for
+ any occurrence of OLD0 as an operand of a comparison and likewise for
+ NEW1 and OLD1. */
+
+static tree
+eval_subst (arg, old0, new0, old1, new1)
+ tree arg;
+ tree old0, new0, old1, new1;
+{
+ tree type = TREE_TYPE (arg);
+ enum tree_code code = TREE_CODE (arg);
+ char class = TREE_CODE_CLASS (code);
+
+ /* We can handle some of the 'e' cases here. */
+ if (class == 'e' && code == TRUTH_NOT_EXPR)
+ class = '1';
+ else if (class == 'e'
+ && (code == TRUTH_ANDIF_EXPR || code == TRUTH_ORIF_EXPR))
+ class = '2';
+
+ switch (class)
+ {
+ case '1':
+ return fold (build1 (code, type,
+ eval_subst (TREE_OPERAND (arg, 0),
+ old0, new0, old1, new1)));
+
+ case '2':
+ return fold (build (code, type,
+ eval_subst (TREE_OPERAND (arg, 0),
+ old0, new0, old1, new1),
+ eval_subst (TREE_OPERAND (arg, 1),
+ old0, new0, old1, new1)));
+
+ case 'e':
+ switch (code)
+ {
+ case SAVE_EXPR:
+ return eval_subst (TREE_OPERAND (arg, 0), old0, new0, old1, new1);
+
+ case COMPOUND_EXPR:
+ return eval_subst (TREE_OPERAND (arg, 1), old0, new0, old1, new1);
+
+ case COND_EXPR:
+ return fold (build (code, type,
+ eval_subst (TREE_OPERAND (arg, 0),
+ old0, new0, old1, new1),
+ eval_subst (TREE_OPERAND (arg, 1),
+ old0, new0, old1, new1),
+ eval_subst (TREE_OPERAND (arg, 2),
+ old0, new0, old1, new1)));
+ }
+
+ case '<':
+ {
+ tree arg0 = TREE_OPERAND (arg, 0);
+ tree arg1 = TREE_OPERAND (arg, 1);
+
+ /* We need to check both for exact equality and tree equality. The
+ former will be true if the operand has a side-effect. In that
+ case, we know the operand occurred exactly once. */
+
+ if (arg0 == old0 || operand_equal_p (arg0, old0, 0))
+ arg0 = new0;
+ else if (arg0 == old1 || operand_equal_p (arg0, old1, 0))
+ arg0 = new1;
+
+ if (arg1 == old0 || operand_equal_p (arg1, old0, 0))
+ arg1 = new0;
+ else if (arg1 == old1 || operand_equal_p (arg1, old1, 0))
+ arg1 = new1;
+
+ return fold (build (code, type, arg0, arg1));
+ }
+ }
+
+ return arg;
+}
+
+/* Return a tree for the case when the result of an expression is RESULT
+ converted to TYPE and OMITTED was previously an operand of the expression
+ but is now not needed (e.g., we folded OMITTED * 0).
+
+ If OMITTED has side effects, we must evaluate it. Otherwise, just do
+ the conversion of RESULT to TYPE. */
+
+static tree
+omit_one_operand (type, result, omitted)
+ tree type, result, omitted;
+{
+ tree t = convert (type, result);
+
+ if (TREE_SIDE_EFFECTS (omitted))
+ return build (COMPOUND_EXPR, type, omitted, t);
+
+ return non_lvalue (t);
+}
+
+/* Return a simplified tree node for the truth-negation of ARG. This
+ never alters ARG itself. We assume that ARG is an operation that
+ returns a truth value (0 or 1). */
+
+tree
+invert_truthvalue (arg)
+ tree arg;
+{
+ tree type = TREE_TYPE (arg);
+ enum tree_code code = TREE_CODE (arg);
+
+ if (code == ERROR_MARK)
+ return arg;
+
+ /* If this is a comparison, we can simply invert it, except for
+ floating-point non-equality comparisons, in which case we just
+ enclose a TRUTH_NOT_EXPR around what we have. */
+
+ if (TREE_CODE_CLASS (code) == '<')
+ {
+ if (FLOAT_TYPE_P (TREE_TYPE (TREE_OPERAND (arg, 0)))
+ && code != NE_EXPR && code != EQ_EXPR)
+ return build1 (TRUTH_NOT_EXPR, type, arg);
+ else
+ return build (invert_tree_comparison (code), type,
+ TREE_OPERAND (arg, 0), TREE_OPERAND (arg, 1));
+ }
+
+ switch (code)
+ {
+ case INTEGER_CST:
+ return convert (type, build_int_2 (TREE_INT_CST_LOW (arg) == 0
+ && TREE_INT_CST_HIGH (arg) == 0, 0));
+
+ case TRUTH_AND_EXPR:
+ return build (TRUTH_OR_EXPR, type,
+ invert_truthvalue (TREE_OPERAND (arg, 0)),
+ invert_truthvalue (TREE_OPERAND (arg, 1)));
+
+ case TRUTH_OR_EXPR:
+ return build (TRUTH_AND_EXPR, type,
+ invert_truthvalue (TREE_OPERAND (arg, 0)),
+ invert_truthvalue (TREE_OPERAND (arg, 1)));
+
+ case TRUTH_XOR_EXPR:
+ /* Here we can invert either operand. We invert the first operand
+ unless the second operand is a TRUTH_NOT_EXPR in which case our
+ result is the XOR of the first operand with the inside of the
+ negation of the second operand. */
+
+ if (TREE_CODE (TREE_OPERAND (arg, 1)) == TRUTH_NOT_EXPR)
+ return build (TRUTH_XOR_EXPR, type, TREE_OPERAND (arg, 0),
+ TREE_OPERAND (TREE_OPERAND (arg, 1), 0));
+ else
+ return build (TRUTH_XOR_EXPR, type,
+ invert_truthvalue (TREE_OPERAND (arg, 0)),
+ TREE_OPERAND (arg, 1));
+
+ case TRUTH_ANDIF_EXPR:
+ return build (TRUTH_ORIF_EXPR, type,
+ invert_truthvalue (TREE_OPERAND (arg, 0)),
+ invert_truthvalue (TREE_OPERAND (arg, 1)));
+
+ case TRUTH_ORIF_EXPR:
+ return build (TRUTH_ANDIF_EXPR, type,
+ invert_truthvalue (TREE_OPERAND (arg, 0)),
+ invert_truthvalue (TREE_OPERAND (arg, 1)));
+
+ case TRUTH_NOT_EXPR:
+ return TREE_OPERAND (arg, 0);
+
+ case COND_EXPR:
+ return build (COND_EXPR, type, TREE_OPERAND (arg, 0),
+ invert_truthvalue (TREE_OPERAND (arg, 1)),
+ invert_truthvalue (TREE_OPERAND (arg, 2)));
+
+ case COMPOUND_EXPR:
+ return build (COMPOUND_EXPR, type, TREE_OPERAND (arg, 0),
+ invert_truthvalue (TREE_OPERAND (arg, 1)));
+
+ case NON_LVALUE_EXPR:
+ return invert_truthvalue (TREE_OPERAND (arg, 0));
+
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ case FLOAT_EXPR:
+ return build1 (TREE_CODE (arg), type,
+ invert_truthvalue (TREE_OPERAND (arg, 0)));
+
+ case BIT_AND_EXPR:
+ if (!integer_onep (TREE_OPERAND (arg, 1)))
+ break;
+ return build (EQ_EXPR, type, arg, convert (type, integer_zero_node));
+
+ case SAVE_EXPR:
+ return build1 (TRUTH_NOT_EXPR, type, arg);
+ }
+ if (TREE_CODE (TREE_TYPE (arg)) != BOOLEAN_TYPE)
+ abort ();
+ return build1 (TRUTH_NOT_EXPR, type, arg);
+}
+
+/* Given a bit-wise operation CODE applied to ARG0 and ARG1, see if both
+ operands are another bit-wise operation with a common input. If so,
+ distribute the bit operations to save an operation and possibly two if
+ constants are involved. For example, convert
+ (A | B) & (A | C) into A | (B & C)
+ Further simplification will occur if B and C are constants.
+
+ If this optimization cannot be done, 0 will be returned. */
+
+static tree
+distribute_bit_expr (code, type, arg0, arg1)
+ enum tree_code code;
+ tree type;
+ tree arg0, arg1;
+{
+ tree common;
+ tree left, right;
+
+ if (TREE_CODE (arg0) != TREE_CODE (arg1)
+ || TREE_CODE (arg0) == code
+ || (TREE_CODE (arg0) != BIT_AND_EXPR
+ && TREE_CODE (arg0) != BIT_IOR_EXPR))
+ return 0;
+
+ if (operand_equal_p (TREE_OPERAND (arg0, 0), TREE_OPERAND (arg1, 0), 0))
+ {
+ common = TREE_OPERAND (arg0, 0);
+ left = TREE_OPERAND (arg0, 1);
+ right = TREE_OPERAND (arg1, 1);
+ }
+ else if (operand_equal_p (TREE_OPERAND (arg0, 0), TREE_OPERAND (arg1, 1), 0))
+ {
+ common = TREE_OPERAND (arg0, 0);
+ left = TREE_OPERAND (arg0, 1);
+ right = TREE_OPERAND (arg1, 0);
+ }
+ else if (operand_equal_p (TREE_OPERAND (arg0, 1), TREE_OPERAND (arg1, 0), 0))
+ {
+ common = TREE_OPERAND (arg0, 1);
+ left = TREE_OPERAND (arg0, 0);
+ right = TREE_OPERAND (arg1, 1);
+ }
+ else if (operand_equal_p (TREE_OPERAND (arg0, 1), TREE_OPERAND (arg1, 1), 0))
+ {
+ common = TREE_OPERAND (arg0, 1);
+ left = TREE_OPERAND (arg0, 0);
+ right = TREE_OPERAND (arg1, 0);
+ }
+ else
+ return 0;
+
+ return fold (build (TREE_CODE (arg0), type, common,
+ fold (build (code, type, left, right))));
+}
+
+/* Return a BIT_FIELD_REF of type TYPE to refer to BITSIZE bits of INNER
+ starting at BITPOS. The field is unsigned if UNSIGNEDP is non-zero. */
+
+static tree
+make_bit_field_ref (inner, type, bitsize, bitpos, unsignedp)
+ tree inner;
+ tree type;
+ int bitsize, bitpos;
+ int unsignedp;
+{
+ tree result = build (BIT_FIELD_REF, type, inner,
+ size_int (bitsize), size_int (bitpos));
+
+ TREE_UNSIGNED (result) = unsignedp;
+
+ return result;
+}
+
+/* Optimize a bit-field compare.
+
+ There are two cases: First is a compare against a constant and the
+ second is a comparison of two items where the fields are at the same
+ bit position relative to the start of a chunk (byte, halfword, word)
+ large enough to contain it. In these cases we can avoid the shift
+ implicit in bitfield extractions.
+
+ For constants, we emit a compare of the shifted constant with the
+ BIT_AND_EXPR of a mask and a byte, halfword, or word of the operand being
+ compared. For two fields at the same position, we do the ANDs with the
+ similar mask and compare the result of the ANDs.
+
+ CODE is the comparison code, known to be either NE_EXPR or EQ_EXPR.
+ COMPARE_TYPE is the type of the comparison, and LHS and RHS
+ are the left and right operands of the comparison, respectively.
+
+ If the optimization described above can be done, we return the resulting
+ tree. Otherwise we return zero. */
+
+static tree
+optimize_bit_field_compare (code, compare_type, lhs, rhs)
+ enum tree_code code;
+ tree compare_type;
+ tree lhs, rhs;
+{
+ int lbitpos, lbitsize, rbitpos, rbitsize;
+ int lnbitpos, lnbitsize, rnbitpos, rnbitsize;
+ tree type = TREE_TYPE (lhs);
+ tree signed_type, unsigned_type;
+ int const_p = TREE_CODE (rhs) == INTEGER_CST;
+ enum machine_mode lmode, rmode, lnmode, rnmode;
+ int lunsignedp, runsignedp;
+ int lvolatilep = 0, rvolatilep = 0;
+ tree linner, rinner;
+ tree mask;
+ tree offset;
+
+ /* Get all the information about the extractions being done. If the bit size
+ if the same as the size of the underlying object, we aren't doing an
+ extraction at all and so can do nothing. */
+ linner = get_inner_reference (lhs, &lbitsize, &lbitpos, &offset, &lmode,
+ &lunsignedp, &lvolatilep);
+ if (linner == lhs || lbitsize == GET_MODE_BITSIZE (lmode) || lbitsize < 0
+ || offset != 0)
+ return 0;
+
+ if (!const_p)
+ {
+ /* If this is not a constant, we can only do something if bit positions,
+ sizes, and signedness are the same. */
+ rinner = get_inner_reference (rhs, &rbitsize, &rbitpos, &offset,
+ &rmode, &runsignedp, &rvolatilep);
+
+ if (rinner == rhs || lbitpos != rbitpos || lbitsize != rbitsize
+ || lunsignedp != runsignedp || offset != 0)
+ return 0;
+ }
+
+ /* See if we can find a mode to refer to this field. We should be able to,
+ but fail if we can't. */
+ lnmode = get_best_mode (lbitsize, lbitpos,
+ TYPE_ALIGN (TREE_TYPE (linner)), word_mode,
+ lvolatilep);
+ if (lnmode == VOIDmode)
+ return 0;
+
+ /* Set signed and unsigned types of the precision of this mode for the
+ shifts below. */
+ signed_type = type_for_mode (lnmode, 0);
+ unsigned_type = type_for_mode (lnmode, 1);
+
+ if (! const_p)
+ {
+ rnmode = get_best_mode (rbitsize, rbitpos,
+ TYPE_ALIGN (TREE_TYPE (rinner)), word_mode,
+ rvolatilep);
+ if (rnmode == VOIDmode)
+ return 0;
+ }
+
+ /* Compute the bit position and size for the new reference and our offset
+ within it. If the new reference is the same size as the original, we
+ won't optimize anything, so return zero. */
+ lnbitsize = GET_MODE_BITSIZE (lnmode);
+ lnbitpos = lbitpos & ~ (lnbitsize - 1);
+ lbitpos -= lnbitpos;
+ if (lnbitsize == lbitsize)
+ return 0;
+
+ if (! const_p)
+ {
+ rnbitsize = GET_MODE_BITSIZE (rnmode);
+ rnbitpos = rbitpos & ~ (rnbitsize - 1);
+ rbitpos -= rnbitpos;
+ if (rnbitsize == rbitsize)
+ return 0;
+ }
+
+#if BYTES_BIG_ENDIAN
+ lbitpos = lnbitsize - lbitsize - lbitpos;
+#endif
+
+ /* Make the mask to be used against the extracted field. */
+ mask = build_int_2 (~0, ~0);
+ TREE_TYPE (mask) = unsigned_type;
+ force_fit_type (mask, 0);
+ mask = convert (unsigned_type, mask);
+ mask = const_binop (LSHIFT_EXPR, mask, size_int (lnbitsize - lbitsize), 0);
+ mask = const_binop (RSHIFT_EXPR, mask,
+ size_int (lnbitsize - lbitsize - lbitpos), 0);
+
+ if (! const_p)
+ /* If not comparing with constant, just rework the comparison
+ and return. */
+ return build (code, compare_type,
+ build (BIT_AND_EXPR, unsigned_type,
+ make_bit_field_ref (linner, unsigned_type,
+ lnbitsize, lnbitpos, 1),
+ mask),
+ build (BIT_AND_EXPR, unsigned_type,
+ make_bit_field_ref (rinner, unsigned_type,
+ rnbitsize, rnbitpos, 1),
+ mask));
+
+ /* Otherwise, we are handling the constant case. See if the constant is too
+ big for the field. Warn and return a tree of for 0 (false) if so. We do
+ this not only for its own sake, but to avoid having to test for this
+ error case below. If we didn't, we might generate wrong code.
+
+ For unsigned fields, the constant shifted right by the field length should
+ be all zero. For signed fields, the high-order bits should agree with
+ the sign bit. */
+
+ if (lunsignedp)
+ {
+ if (! integer_zerop (const_binop (RSHIFT_EXPR,
+ convert (unsigned_type, rhs),
+ size_int (lbitsize), 0)))
+ {
+ warning ("comparison is always %s due to width of bitfield",
+ code == NE_EXPR ? "one" : "zero");
+ return convert (compare_type,
+ (code == NE_EXPR
+ ? integer_one_node : integer_zero_node));
+ }
+ }
+ else
+ {
+ tree tem = const_binop (RSHIFT_EXPR, convert (signed_type, rhs),
+ size_int (lbitsize - 1), 0);
+ if (! integer_zerop (tem) && ! integer_all_onesp (tem))
+ {
+ warning ("comparison is always %s due to width of bitfield",
+ code == NE_EXPR ? "one" : "zero");
+ return convert (compare_type,
+ (code == NE_EXPR
+ ? integer_one_node : integer_zero_node));
+ }
+ }
+
+ /* Single-bit compares should always be against zero. */
+ if (lbitsize == 1 && ! integer_zerop (rhs))
+ {
+ code = code == EQ_EXPR ? NE_EXPR : EQ_EXPR;
+ rhs = convert (type, integer_zero_node);
+ }
+
+ /* Make a new bitfield reference, shift the constant over the
+ appropriate number of bits and mask it with the computed mask
+ (in case this was a signed field). If we changed it, make a new one. */
+ lhs = make_bit_field_ref (linner, unsigned_type, lnbitsize, lnbitpos, 1);
+ if (lvolatilep)
+ {
+ TREE_SIDE_EFFECTS (lhs) = 1;
+ TREE_THIS_VOLATILE (lhs) = 1;
+ }
+
+ rhs = fold (const_binop (BIT_AND_EXPR,
+ const_binop (LSHIFT_EXPR,
+ convert (unsigned_type, rhs),
+ size_int (lbitpos), 0),
+ mask, 0));
+
+ return build (code, compare_type,
+ build (BIT_AND_EXPR, unsigned_type, lhs, mask),
+ rhs);
+}
+
+/* Subroutine for fold_truthop: decode a field reference.
+
+ If EXP is a comparison reference, we return the innermost reference.
+
+ *PBITSIZE is set to the number of bits in the reference, *PBITPOS is
+ set to the starting bit number.
+
+ If the innermost field can be completely contained in a mode-sized
+ unit, *PMODE is set to that mode. Otherwise, it is set to VOIDmode.
+
+ *PVOLATILEP is set to 1 if the any expression encountered is volatile;
+ otherwise it is not changed.
+
+ *PUNSIGNEDP is set to the signedness of the field.
+
+ *PMASK is set to the mask used. This is either contained in a
+ BIT_AND_EXPR or derived from the width of the field.
+
+ Return 0 if this is not a component reference or is one that we can't
+ do anything with. */
+
+static tree
+decode_field_reference (exp, pbitsize, pbitpos, pmode, punsignedp,
+ pvolatilep, pmask)
+ tree exp;
+ int *pbitsize, *pbitpos;
+ enum machine_mode *pmode;
+ int *punsignedp, *pvolatilep;
+ tree *pmask;
+{
+ tree and_mask = 0;
+ tree mask, inner, offset;
+ tree unsigned_type;
+ int precision;
+
+ /* All the optimizations using this function assume integer fields.
+ There are problems with FP fields since the type_for_size call
+ below can fail for, e.g., XFmode. */
+ if (! INTEGRAL_TYPE_P (TREE_TYPE (exp)))
+ return 0;
+
+ STRIP_NOPS (exp);
+
+ if (TREE_CODE (exp) == BIT_AND_EXPR)
+ {
+ and_mask = TREE_OPERAND (exp, 1);
+ exp = TREE_OPERAND (exp, 0);
+ STRIP_NOPS (exp); STRIP_NOPS (and_mask);
+ if (TREE_CODE (and_mask) != INTEGER_CST)
+ return 0;
+ }
+
+ if (TREE_CODE (exp) != COMPONENT_REF && TREE_CODE (exp) != ARRAY_REF
+ && TREE_CODE (exp) != BIT_FIELD_REF)
+ return 0;
+
+ inner = get_inner_reference (exp, pbitsize, pbitpos, &offset, pmode,
+ punsignedp, pvolatilep);
+ if (inner == exp || *pbitsize < 0 || offset != 0)
+ return 0;
+
+ /* Compute the mask to access the bitfield. */
+ unsigned_type = type_for_size (*pbitsize, 1);
+ precision = TYPE_PRECISION (unsigned_type);
+
+ mask = build_int_2 (~0, ~0);
+ TREE_TYPE (mask) = unsigned_type;
+ force_fit_type (mask, 0);
+ mask = const_binop (LSHIFT_EXPR, mask, size_int (precision - *pbitsize), 0);
+ mask = const_binop (RSHIFT_EXPR, mask, size_int (precision - *pbitsize), 0);
+
+ /* Merge it with the mask we found in the BIT_AND_EXPR, if any. */
+ if (and_mask != 0)
+ mask = fold (build (BIT_AND_EXPR, unsigned_type,
+ convert (unsigned_type, and_mask), mask));
+
+ *pmask = mask;
+ return inner;
+}
+
+/* Return non-zero if MASK represents a mask of SIZE ones in the low-order
+ bit positions. */
+
+static int
+all_ones_mask_p (mask, size)
+ tree mask;
+ int size;
+{
+ tree type = TREE_TYPE (mask);
+ int precision = TYPE_PRECISION (type);
+ tree tmask;
+
+ tmask = build_int_2 (~0, ~0);
+ TREE_TYPE (tmask) = signed_type (type);
+ force_fit_type (tmask, 0);
+ return
+ operand_equal_p (mask,
+ const_binop (RSHIFT_EXPR,
+ const_binop (LSHIFT_EXPR, tmask,
+ size_int (precision - size), 0),
+ size_int (precision - size), 0),
+ 0);
+}
+
+/* Subroutine for fold_truthop: determine if an operand is simple enough
+ to be evaluated unconditionally. */
+
+static int
+simple_operand_p (exp)
+ tree exp;
+{
+ /* Strip any conversions that don't change the machine mode. */
+ while ((TREE_CODE (exp) == NOP_EXPR
+ || TREE_CODE (exp) == CONVERT_EXPR)
+ && (TYPE_MODE (TREE_TYPE (exp))
+ == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))))
+ exp = TREE_OPERAND (exp, 0);
+
+ return (TREE_CODE_CLASS (TREE_CODE (exp)) == 'c'
+ || (TREE_CODE_CLASS (TREE_CODE (exp)) == 'd'
+ && ! TREE_ADDRESSABLE (exp)
+ && ! TREE_THIS_VOLATILE (exp)
+ && ! DECL_NONLOCAL (exp)
+ /* Don't regard global variables as simple. They may be
+ allocated in ways unknown to the compiler (shared memory,
+ #pragma weak, etc). */
+ && ! TREE_PUBLIC (exp)
+ && ! DECL_EXTERNAL (exp)
+ /* Loading a static variable is unduly expensive, but global
+ registers aren't expensive. */
+ && (! TREE_STATIC (exp) || DECL_REGISTER (exp))));
+}
+
+/* Subroutine for fold_truthop: try to optimize a range test.
+
+ For example, "i >= 2 && i =< 9" can be done as "(unsigned) (i - 2) <= 7".
+
+ JCODE is the logical combination of the two terms. It is TRUTH_AND_EXPR
+ (representing TRUTH_ANDIF_EXPR and TRUTH_AND_EXPR) or TRUTH_OR_EXPR
+ (representing TRUTH_ORIF_EXPR and TRUTH_OR_EXPR). TYPE is the type of
+ the result.
+
+ VAR is the value being tested. LO_CODE and HI_CODE are the comparison
+ operators comparing VAR to LO_CST and HI_CST. LO_CST is known to be no
+ larger than HI_CST (they may be equal).
+
+ We return the simplified tree or 0 if no optimization is possible. */
+
+static tree
+range_test (jcode, type, lo_code, hi_code, var, lo_cst, hi_cst)
+ enum tree_code jcode, lo_code, hi_code;
+ tree type, var, lo_cst, hi_cst;
+{
+ tree utype;
+ enum tree_code rcode;
+
+ /* See if this is a range test and normalize the constant terms. */
+
+ if (jcode == TRUTH_AND_EXPR)
+ {
+ switch (lo_code)
+ {
+ case NE_EXPR:
+ /* See if we have VAR != CST && VAR != CST+1. */
+ if (! (hi_code == NE_EXPR
+ && TREE_INT_CST_LOW (hi_cst) - TREE_INT_CST_LOW (lo_cst) == 1
+ && tree_int_cst_equal (integer_one_node,
+ const_binop (MINUS_EXPR,
+ hi_cst, lo_cst, 0))))
+ return 0;
+
+ rcode = GT_EXPR;
+ break;
+
+ case GT_EXPR:
+ case GE_EXPR:
+ if (hi_code == LT_EXPR)
+ hi_cst = const_binop (MINUS_EXPR, hi_cst, integer_one_node, 0);
+ else if (hi_code != LE_EXPR)
+ return 0;
+
+ if (lo_code == GT_EXPR)
+ lo_cst = const_binop (PLUS_EXPR, lo_cst, integer_one_node, 0);
+
+ /* We now have VAR >= LO_CST && VAR <= HI_CST. */
+ rcode = LE_EXPR;
+ break;
+
+ default:
+ return 0;
+ }
+ }
+ else
+ {
+ switch (lo_code)
+ {
+ case EQ_EXPR:
+ /* See if we have VAR == CST || VAR == CST+1. */
+ if (! (hi_code == EQ_EXPR
+ && TREE_INT_CST_LOW (hi_cst) - TREE_INT_CST_LOW (lo_cst) == 1
+ && tree_int_cst_equal (integer_one_node,
+ const_binop (MINUS_EXPR,
+ hi_cst, lo_cst, 0))))
+ return 0;
+
+ rcode = LE_EXPR;
+ break;
+
+ case LE_EXPR:
+ case LT_EXPR:
+ if (hi_code == GE_EXPR)
+ hi_cst = const_binop (MINUS_EXPR, hi_cst, integer_one_node, 0);
+ else if (hi_code != GT_EXPR)
+ return 0;
+
+ if (lo_code == LE_EXPR)
+ lo_cst = const_binop (PLUS_EXPR, lo_cst, integer_one_node, 0);
+
+ /* We now have VAR < LO_CST || VAR > HI_CST. */
+ rcode = GT_EXPR;
+ break;
+
+ default:
+ return 0;
+ }
+ }
+
+ /* When normalizing, it is possible to both increment the smaller constant
+ and decrement the larger constant. See if they are still ordered. */
+ if (tree_int_cst_lt (hi_cst, lo_cst))
+ return 0;
+
+ /* Fail if VAR isn't an integer. */
+ utype = TREE_TYPE (var);
+ if (! INTEGRAL_TYPE_P (utype))
+ return 0;
+
+ /* The range test is invalid if subtracting the two constants results
+ in overflow. This can happen in traditional mode. */
+ if (! int_fits_type_p (hi_cst, TREE_TYPE (var))
+ || ! int_fits_type_p (lo_cst, TREE_TYPE (var)))
+ return 0;
+
+ if (! TREE_UNSIGNED (utype))
+ {
+ utype = unsigned_type (utype);
+ var = convert (utype, var);
+ lo_cst = convert (utype, lo_cst);
+ hi_cst = convert (utype, hi_cst);
+ }
+
+ return fold (convert (type,
+ build (rcode, utype,
+ build (MINUS_EXPR, utype, var, lo_cst),
+ const_binop (MINUS_EXPR, hi_cst, lo_cst, 0))));
+}
+
+/* Find ways of folding logical expressions of LHS and RHS:
+ Try to merge two comparisons to the same innermost item.
+ Look for range tests like "ch >= '0' && ch <= '9'".
+ Look for combinations of simple terms on machines with expensive branches
+ and evaluate the RHS unconditionally.
+
+ For example, if we have p->a == 2 && p->b == 4 and we can make an
+ object large enough to span both A and B, we can do this with a comparison
+ against the object ANDed with the a mask.
+
+ If we have p->a == q->a && p->b == q->b, we may be able to use bit masking
+ operations to do this with one comparison.
+
+ We check for both normal comparisons and the BIT_AND_EXPRs made this by
+ function and the one above.
+
+ CODE is the logical operation being done. It can be TRUTH_ANDIF_EXPR,
+ TRUTH_AND_EXPR, TRUTH_ORIF_EXPR, or TRUTH_OR_EXPR.
+
+ TRUTH_TYPE is the type of the logical operand and LHS and RHS are its
+ two operands.
+
+ We return the simplified tree or 0 if no optimization is possible. */
+
+static tree
+fold_truthop (code, truth_type, lhs, rhs)
+ enum tree_code code;
+ tree truth_type, lhs, rhs;
+{
+ /* If this is the "or" of two comparisons, we can do something if we
+ the comparisons are NE_EXPR. If this is the "and", we can do something
+ if the comparisons are EQ_EXPR. I.e.,
+ (a->b == 2 && a->c == 4) can become (a->new == NEW).
+
+ WANTED_CODE is this operation code. For single bit fields, we can
+ convert EQ_EXPR to NE_EXPR so we need not reject the "wrong"
+ comparison for one-bit fields. */
+
+ enum tree_code wanted_code;
+ enum tree_code lcode, rcode;
+ tree ll_arg, lr_arg, rl_arg, rr_arg;
+ tree ll_inner, lr_inner, rl_inner, rr_inner;
+ int ll_bitsize, ll_bitpos, lr_bitsize, lr_bitpos;
+ int rl_bitsize, rl_bitpos, rr_bitsize, rr_bitpos;
+ int xll_bitpos, xlr_bitpos, xrl_bitpos, xrr_bitpos;
+ int lnbitsize, lnbitpos, rnbitsize, rnbitpos;
+ int ll_unsignedp, lr_unsignedp, rl_unsignedp, rr_unsignedp;
+ enum machine_mode ll_mode, lr_mode, rl_mode, rr_mode;
+ enum machine_mode lnmode, rnmode;
+ tree ll_mask, lr_mask, rl_mask, rr_mask;
+ tree l_const, r_const;
+ tree type, result;
+ int first_bit, end_bit;
+ int volatilep;
+
+ /* Start by getting the comparison codes and seeing if this looks like
+ a range test. Fail if anything is volatile. If one operand is a
+ BIT_AND_EXPR with the constant one, treat it as if it were surrounded
+ with a NE_EXPR. */
+
+ if (TREE_SIDE_EFFECTS (lhs)
+ || TREE_SIDE_EFFECTS (rhs))
+ return 0;
+
+ lcode = TREE_CODE (lhs);
+ rcode = TREE_CODE (rhs);
+
+ if (lcode == BIT_AND_EXPR && integer_onep (TREE_OPERAND (lhs, 1)))
+ lcode = NE_EXPR, lhs = build (NE_EXPR, truth_type, lhs, integer_zero_node);
+
+ if (rcode == BIT_AND_EXPR && integer_onep (TREE_OPERAND (rhs, 1)))
+ rcode = NE_EXPR, rhs = build (NE_EXPR, truth_type, rhs, integer_zero_node);
+
+ if (TREE_CODE_CLASS (lcode) != '<'
+ || TREE_CODE_CLASS (rcode) != '<')
+ return 0;
+
+ code = ((code == TRUTH_AND_EXPR || code == TRUTH_ANDIF_EXPR)
+ ? TRUTH_AND_EXPR : TRUTH_OR_EXPR);
+
+ ll_arg = TREE_OPERAND (lhs, 0);
+ lr_arg = TREE_OPERAND (lhs, 1);
+ rl_arg = TREE_OPERAND (rhs, 0);
+ rr_arg = TREE_OPERAND (rhs, 1);
+
+ if (TREE_CODE (lr_arg) == INTEGER_CST
+ && TREE_CODE (rr_arg) == INTEGER_CST
+ && operand_equal_p (ll_arg, rl_arg, 0))
+ {
+ if (tree_int_cst_lt (lr_arg, rr_arg))
+ result = range_test (code, truth_type, lcode, rcode,
+ ll_arg, lr_arg, rr_arg);
+ else
+ result = range_test (code, truth_type, rcode, lcode,
+ ll_arg, rr_arg, lr_arg);
+
+ /* If this isn't a range test, it also isn't a comparison that
+ can be merged. However, it wins to evaluate the RHS unconditionally
+ on machines with expensive branches. */
+
+ if (result == 0 && BRANCH_COST >= 2)
+ {
+ if (TREE_CODE (ll_arg) != VAR_DECL
+ && TREE_CODE (ll_arg) != PARM_DECL)
+ {
+ /* Avoid evaluating the variable part twice. */
+ ll_arg = save_expr (ll_arg);
+ lhs = build (lcode, TREE_TYPE (lhs), ll_arg, lr_arg);
+ rhs = build (rcode, TREE_TYPE (rhs), ll_arg, rr_arg);
+ }
+ return build (code, truth_type, lhs, rhs);
+ }
+ return result;
+ }
+
+ /* If the RHS can be evaluated unconditionally and its operands are
+ simple, it wins to evaluate the RHS unconditionally on machines
+ with expensive branches. In this case, this isn't a comparison
+ that can be merged. */
+
+ /* @@ I'm not sure it wins on the m88110 to do this if the comparisons
+ are with zero (tmw). */
+
+ if (BRANCH_COST >= 2
+ && INTEGRAL_TYPE_P (TREE_TYPE (rhs))
+ && simple_operand_p (rl_arg)
+ && simple_operand_p (rr_arg))
+ return build (code, truth_type, lhs, rhs);
+
+ /* See if the comparisons can be merged. Then get all the parameters for
+ each side. */
+
+ if ((lcode != EQ_EXPR && lcode != NE_EXPR)
+ || (rcode != EQ_EXPR && rcode != NE_EXPR))
+ return 0;
+
+ volatilep = 0;
+ ll_inner = decode_field_reference (ll_arg,
+ &ll_bitsize, &ll_bitpos, &ll_mode,
+ &ll_unsignedp, &volatilep, &ll_mask);
+ lr_inner = decode_field_reference (lr_arg,
+ &lr_bitsize, &lr_bitpos, &lr_mode,
+ &lr_unsignedp, &volatilep, &lr_mask);
+ rl_inner = decode_field_reference (rl_arg,
+ &rl_bitsize, &rl_bitpos, &rl_mode,
+ &rl_unsignedp, &volatilep, &rl_mask);
+ rr_inner = decode_field_reference (rr_arg,
+ &rr_bitsize, &rr_bitpos, &rr_mode,
+ &rr_unsignedp, &volatilep, &rr_mask);
+
+ /* It must be true that the inner operation on the lhs of each
+ comparison must be the same if we are to be able to do anything.
+ Then see if we have constants. If not, the same must be true for
+ the rhs's. */
+ if (volatilep || ll_inner == 0 || rl_inner == 0
+ || ! operand_equal_p (ll_inner, rl_inner, 0))
+ return 0;
+
+ if (TREE_CODE (lr_arg) == INTEGER_CST
+ && TREE_CODE (rr_arg) == INTEGER_CST)
+ l_const = lr_arg, r_const = rr_arg;
+ else if (lr_inner == 0 || rr_inner == 0
+ || ! operand_equal_p (lr_inner, rr_inner, 0))
+ return 0;
+ else
+ l_const = r_const = 0;
+
+ /* If either comparison code is not correct for our logical operation,
+ fail. However, we can convert a one-bit comparison against zero into
+ the opposite comparison against that bit being set in the field. */
+
+ wanted_code = (code == TRUTH_AND_EXPR ? EQ_EXPR : NE_EXPR);
+ if (lcode != wanted_code)
+ {
+ if (l_const && integer_zerop (l_const) && integer_pow2p (ll_mask))
+ l_const = ll_mask;
+ else
+ return 0;
+ }
+
+ if (rcode != wanted_code)
+ {
+ if (r_const && integer_zerop (r_const) && integer_pow2p (rl_mask))
+ r_const = rl_mask;
+ else
+ return 0;
+ }
+
+ /* See if we can find a mode that contains both fields being compared on
+ the left. If we can't, fail. Otherwise, update all constants and masks
+ to be relative to a field of that size. */
+ first_bit = MIN (ll_bitpos, rl_bitpos);
+ end_bit = MAX (ll_bitpos + ll_bitsize, rl_bitpos + rl_bitsize);
+ lnmode = get_best_mode (end_bit - first_bit, first_bit,
+ TYPE_ALIGN (TREE_TYPE (ll_inner)), word_mode,
+ volatilep);
+ if (lnmode == VOIDmode)
+ return 0;
+
+ lnbitsize = GET_MODE_BITSIZE (lnmode);
+ lnbitpos = first_bit & ~ (lnbitsize - 1);
+ type = type_for_size (lnbitsize, 1);
+ xll_bitpos = ll_bitpos - lnbitpos, xrl_bitpos = rl_bitpos - lnbitpos;
+
+#if BYTES_BIG_ENDIAN
+ xll_bitpos = lnbitsize - xll_bitpos - ll_bitsize;
+ xrl_bitpos = lnbitsize - xrl_bitpos - rl_bitsize;
+#endif
+
+ ll_mask = const_binop (LSHIFT_EXPR, convert (type, ll_mask),
+ size_int (xll_bitpos), 0);
+ rl_mask = const_binop (LSHIFT_EXPR, convert (type, rl_mask),
+ size_int (xrl_bitpos), 0);
+
+ /* Make sure the constants are interpreted as unsigned, so we
+ don't have sign bits outside the range of their type. */
+
+ if (l_const)
+ {
+ l_const = convert (unsigned_type (TREE_TYPE (l_const)), l_const);
+ l_const = const_binop (LSHIFT_EXPR, convert (type, l_const),
+ size_int (xll_bitpos), 0);
+ }
+ if (r_const)
+ {
+ r_const = convert (unsigned_type (TREE_TYPE (r_const)), r_const);
+ r_const = const_binop (LSHIFT_EXPR, convert (type, r_const),
+ size_int (xrl_bitpos), 0);
+ }
+
+ /* If the right sides are not constant, do the same for it. Also,
+ disallow this optimization if a size or signedness mismatch occurs
+ between the left and right sides. */
+ if (l_const == 0)
+ {
+ if (ll_bitsize != lr_bitsize || rl_bitsize != rr_bitsize
+ || ll_unsignedp != lr_unsignedp || rl_unsignedp != rr_unsignedp
+ /* Make sure the two fields on the right
+ correspond to the left without being swapped. */
+ || ll_bitpos - rl_bitpos != lr_bitpos - rr_bitpos)
+ return 0;
+
+ first_bit = MIN (lr_bitpos, rr_bitpos);
+ end_bit = MAX (lr_bitpos + lr_bitsize, rr_bitpos + rr_bitsize);
+ rnmode = get_best_mode (end_bit - first_bit, first_bit,
+ TYPE_ALIGN (TREE_TYPE (lr_inner)), word_mode,
+ volatilep);
+ if (rnmode == VOIDmode)
+ return 0;
+
+ rnbitsize = GET_MODE_BITSIZE (rnmode);
+ rnbitpos = first_bit & ~ (rnbitsize - 1);
+ xlr_bitpos = lr_bitpos - rnbitpos, xrr_bitpos = rr_bitpos - rnbitpos;
+
+#if BYTES_BIG_ENDIAN
+ xlr_bitpos = rnbitsize - xlr_bitpos - lr_bitsize;
+ xrr_bitpos = rnbitsize - xrr_bitpos - rr_bitsize;
+#endif
+
+ lr_mask = const_binop (LSHIFT_EXPR, convert (type, lr_mask),
+ size_int (xlr_bitpos), 0);
+ rr_mask = const_binop (LSHIFT_EXPR, convert (type, rr_mask),
+ size_int (xrr_bitpos), 0);
+
+ /* Make a mask that corresponds to both fields being compared.
+ Do this for both items being compared. If the masks agree,
+ we can do this by masking both and comparing the masked
+ results. */
+ ll_mask = const_binop (BIT_IOR_EXPR, ll_mask, rl_mask, 0);
+ lr_mask = const_binop (BIT_IOR_EXPR, lr_mask, rr_mask, 0);
+ if (operand_equal_p (ll_mask, lr_mask, 0) && lnbitsize == rnbitsize)
+ {
+ lhs = make_bit_field_ref (ll_inner, type, lnbitsize, lnbitpos,
+ ll_unsignedp || rl_unsignedp);
+ rhs = make_bit_field_ref (lr_inner, type, rnbitsize, rnbitpos,
+ lr_unsignedp || rr_unsignedp);
+ if (! all_ones_mask_p (ll_mask, lnbitsize))
+ {
+ lhs = build (BIT_AND_EXPR, type, lhs, ll_mask);
+ rhs = build (BIT_AND_EXPR, type, rhs, ll_mask);
+ }
+ return build (wanted_code, truth_type, lhs, rhs);
+ }
+
+ /* There is still another way we can do something: If both pairs of
+ fields being compared are adjacent, we may be able to make a wider
+ field containing them both. */
+ if ((ll_bitsize + ll_bitpos == rl_bitpos
+ && lr_bitsize + lr_bitpos == rr_bitpos)
+ || (ll_bitpos == rl_bitpos + rl_bitsize
+ && lr_bitpos == rr_bitpos + rr_bitsize))
+ return build (wanted_code, truth_type,
+ make_bit_field_ref (ll_inner, type,
+ ll_bitsize + rl_bitsize,
+ MIN (ll_bitpos, rl_bitpos),
+ ll_unsignedp),
+ make_bit_field_ref (lr_inner, type,
+ lr_bitsize + rr_bitsize,
+ MIN (lr_bitpos, rr_bitpos),
+ lr_unsignedp));
+
+ return 0;
+ }
+
+ /* Handle the case of comparisons with constants. If there is something in
+ common between the masks, those bits of the constants must be the same.
+ If not, the condition is always false. Test for this to avoid generating
+ incorrect code below. */
+ result = const_binop (BIT_AND_EXPR, ll_mask, rl_mask, 0);
+ if (! integer_zerop (result)
+ && simple_cst_equal (const_binop (BIT_AND_EXPR, result, l_const, 0),
+ const_binop (BIT_AND_EXPR, result, r_const, 0)) != 1)
+ {
+ if (wanted_code == NE_EXPR)
+ {
+ warning ("`or' of unmatched not-equal tests is always 1");
+ return convert (truth_type, integer_one_node);
+ }
+ else
+ {
+ warning ("`and' of mutually exclusive equal-tests is always zero");
+ return convert (truth_type, integer_zero_node);
+ }
+ }
+
+ /* Construct the expression we will return. First get the component
+ reference we will make. Unless the mask is all ones the width of
+ that field, perform the mask operation. Then compare with the
+ merged constant. */
+ result = make_bit_field_ref (ll_inner, type, lnbitsize, lnbitpos,
+ ll_unsignedp || rl_unsignedp);
+
+ ll_mask = const_binop (BIT_IOR_EXPR, ll_mask, rl_mask, 0);
+ if (! all_ones_mask_p (ll_mask, lnbitsize))
+ result = build (BIT_AND_EXPR, type, result, ll_mask);
+
+ return build (wanted_code, truth_type, result,
+ const_binop (BIT_IOR_EXPR, l_const, r_const, 0));
+}
+
+/* If T contains a COMPOUND_EXPR which was inserted merely to evaluate
+ S, a SAVE_EXPR, return the expression actually being evaluated. Note
+ that we may sometimes modify the tree. */
+
+static tree
+strip_compound_expr (t, s)
+ tree t;
+ tree s;
+{
+ tree type = TREE_TYPE (t);
+ enum tree_code code = TREE_CODE (t);
+
+ /* See if this is the COMPOUND_EXPR we want to eliminate. */
+ if (code == COMPOUND_EXPR && TREE_CODE (TREE_OPERAND (t, 0)) == CONVERT_EXPR
+ && TREE_OPERAND (TREE_OPERAND (t, 0), 0) == s)
+ return TREE_OPERAND (t, 1);
+
+ /* See if this is a COND_EXPR or a simple arithmetic operator. We
+ don't bother handling any other types. */
+ else if (code == COND_EXPR)
+ {
+ TREE_OPERAND (t, 0) = strip_compound_expr (TREE_OPERAND (t, 0), s);
+ TREE_OPERAND (t, 1) = strip_compound_expr (TREE_OPERAND (t, 1), s);
+ TREE_OPERAND (t, 2) = strip_compound_expr (TREE_OPERAND (t, 2), s);
+ }
+ else if (TREE_CODE_CLASS (code) == '1')
+ TREE_OPERAND (t, 0) = strip_compound_expr (TREE_OPERAND (t, 0), s);
+ else if (TREE_CODE_CLASS (code) == '<'
+ || TREE_CODE_CLASS (code) == '2')
+ {
+ TREE_OPERAND (t, 0) = strip_compound_expr (TREE_OPERAND (t, 0), s);
+ TREE_OPERAND (t, 1) = strip_compound_expr (TREE_OPERAND (t, 1), s);
+ }
+
+ return t;
+}
+
+/* Perform constant folding and related simplification of EXPR.
+ The related simplifications include x*1 => x, x*0 => 0, etc.,
+ and application of the associative law.
+ NOP_EXPR conversions may be removed freely (as long as we
+ are careful not to change the C type of the overall expression)
+ We cannot simplify through a CONVERT_EXPR, FIX_EXPR or FLOAT_EXPR,
+ but we can constant-fold them if they have constant operands. */
+
+tree
+fold (expr)
+ tree expr;
+{
+ register tree t = expr;
+ tree t1 = NULL_TREE;
+ tree tem;
+ tree type = TREE_TYPE (expr);
+ register tree arg0, arg1;
+ register enum tree_code code = TREE_CODE (t);
+ register int kind;
+ int invert;
+
+ /* WINS will be nonzero when the switch is done
+ if all operands are constant. */
+
+ int wins = 1;
+
+ /* Don't try to process an RTL_EXPR since its operands aren't trees. */
+ if (code == RTL_EXPR)
+ return t;
+
+ /* Return right away if already constant. */
+ if (TREE_CONSTANT (t))
+ {
+ if (code == CONST_DECL)
+ return DECL_INITIAL (t);
+ return t;
+ }
+
+ kind = TREE_CODE_CLASS (code);
+ if (code == NOP_EXPR || code == FLOAT_EXPR || code == CONVERT_EXPR)
+ {
+ tree subop;
+
+ /* Special case for conversion ops that can have fixed point args. */
+ arg0 = TREE_OPERAND (t, 0);
+
+ /* Don't use STRIP_NOPS, because signedness of argument type matters. */
+ if (arg0 != 0)
+ STRIP_TYPE_NOPS (arg0);
+
+ if (arg0 != 0 && TREE_CODE (arg0) == COMPLEX_CST)
+ subop = TREE_REALPART (arg0);
+ else
+ subop = arg0;
+
+ if (subop != 0 && TREE_CODE (subop) != INTEGER_CST
+#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
+ && TREE_CODE (subop) != REAL_CST
+#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
+ )
+ /* Note that TREE_CONSTANT isn't enough:
+ static var addresses are constant but we can't
+ do arithmetic on them. */
+ wins = 0;
+ }
+ else if (kind == 'e' || kind == '<'
+ || kind == '1' || kind == '2' || kind == 'r')
+ {
+ register int len = tree_code_length[(int) code];
+ register int i;
+ for (i = 0; i < len; i++)
+ {
+ tree op = TREE_OPERAND (t, i);
+ tree subop;
+
+ if (op == 0)
+ continue; /* Valid for CALL_EXPR, at least. */
+
+ if (kind == '<' || code == RSHIFT_EXPR)
+ {
+ /* Signedness matters here. Perhaps we can refine this
+ later. */
+ STRIP_TYPE_NOPS (op);
+ }
+ else
+ {
+ /* Strip any conversions that don't change the mode. */
+ STRIP_NOPS (op);
+ }
+
+ if (TREE_CODE (op) == COMPLEX_CST)
+ subop = TREE_REALPART (op);
+ else
+ subop = op;
+
+ if (TREE_CODE (subop) != INTEGER_CST
+#if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
+ && TREE_CODE (subop) != REAL_CST
+#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
+ )
+ /* Note that TREE_CONSTANT isn't enough:
+ static var addresses are constant but we can't
+ do arithmetic on them. */
+ wins = 0;
+
+ if (i == 0)
+ arg0 = op;
+ else if (i == 1)
+ arg1 = op;
+ }
+ }
+
+ /* If this is a commutative operation, and ARG0 is a constant, move it
+ to ARG1 to reduce the number of tests below. */
+ if ((code == PLUS_EXPR || code == MULT_EXPR || code == MIN_EXPR
+ || code == MAX_EXPR || code == BIT_IOR_EXPR || code == BIT_XOR_EXPR
+ || code == BIT_AND_EXPR)
+ && (TREE_CODE (arg0) == INTEGER_CST || TREE_CODE (arg0) == REAL_CST))
+ {
+ tem = arg0; arg0 = arg1; arg1 = tem;
+
+ tem = TREE_OPERAND (t, 0); TREE_OPERAND (t, 0) = TREE_OPERAND (t, 1);
+ TREE_OPERAND (t, 1) = tem;
+ }
+
+ /* Now WINS is set as described above,
+ ARG0 is the first operand of EXPR,
+ and ARG1 is the second operand (if it has more than one operand).
+
+ First check for cases where an arithmetic operation is applied to a
+ compound, conditional, or comparison operation. Push the arithmetic
+ operation inside the compound or conditional to see if any folding
+ can then be done. Convert comparison to conditional for this purpose.
+ The also optimizes non-constant cases that used to be done in
+ expand_expr.
+
+ Before we do that, see if this is a BIT_AND_EXPR or a BIT_OR_EXPR,
+ one of the operands is a comparison and the other is a comparison, a
+ BIT_AND_EXPR with the constant 1, or a truth value. In that case, the
+ code below would make the expression more complex. Change it to a
+ TRUTH_{AND,OR}_EXPR. Likewise, convert a similar NE_EXPR to
+ TRUTH_XOR_EXPR and an EQ_EXPR to the inversion of a TRUTH_XOR_EXPR. */
+
+ if ((code == BIT_AND_EXPR || code == BIT_IOR_EXPR
+ || code == EQ_EXPR || code == NE_EXPR)
+ && ((truth_value_p (TREE_CODE (arg0))
+ && (truth_value_p (TREE_CODE (arg1))
+ || (TREE_CODE (arg1) == BIT_AND_EXPR
+ && integer_onep (TREE_OPERAND (arg1, 1)))))
+ || (truth_value_p (TREE_CODE (arg1))
+ && (truth_value_p (TREE_CODE (arg0))
+ || (TREE_CODE (arg0) == BIT_AND_EXPR
+ && integer_onep (TREE_OPERAND (arg0, 1)))))))
+ {
+ t = fold (build (code == BIT_AND_EXPR ? TRUTH_AND_EXPR
+ : code == BIT_IOR_EXPR ? TRUTH_OR_EXPR
+ : TRUTH_XOR_EXPR,
+ type, arg0, arg1));
+
+ if (code == EQ_EXPR)
+ t = invert_truthvalue (t);
+
+ return t;
+ }
+
+ if (TREE_CODE_CLASS (code) == '1')
+ {
+ if (TREE_CODE (arg0) == COMPOUND_EXPR)
+ return build (COMPOUND_EXPR, type, TREE_OPERAND (arg0, 0),
+ fold (build1 (code, type, TREE_OPERAND (arg0, 1))));
+ else if (TREE_CODE (arg0) == COND_EXPR)
+ {
+ t = fold (build (COND_EXPR, type, TREE_OPERAND (arg0, 0),
+ fold (build1 (code, type, TREE_OPERAND (arg0, 1))),
+ fold (build1 (code, type, TREE_OPERAND (arg0, 2)))));
+
+ /* If this was a conversion, and all we did was to move into
+ inside the COND_EXPR, bring it back out. Then return so we
+ don't get into an infinite recursion loop taking the conversion
+ out and then back in. */
+
+ if ((code == NOP_EXPR || code == CONVERT_EXPR
+ || code == NON_LVALUE_EXPR)
+ && TREE_CODE (t) == COND_EXPR
+ && TREE_CODE (TREE_OPERAND (t, 1)) == code
+ && TREE_CODE (TREE_OPERAND (t, 2)) == code
+ && (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (t, 1), 0))
+ == TREE_TYPE (TREE_OPERAND (TREE_OPERAND (t, 2), 0))))
+ t = build1 (code, type,
+ build (COND_EXPR,
+ TREE_TYPE (TREE_OPERAND (TREE_OPERAND (t, 1), 0)),
+ TREE_OPERAND (t, 0),
+ TREE_OPERAND (TREE_OPERAND (t, 1), 0),
+ TREE_OPERAND (TREE_OPERAND (t, 2), 0)));
+ return t;
+ }
+ else if (TREE_CODE_CLASS (TREE_CODE (arg0)) == '<')
+ return fold (build (COND_EXPR, type, arg0,
+ fold (build1 (code, type, integer_one_node)),
+ fold (build1 (code, type, integer_zero_node))));
+ }
+ else if (TREE_CODE_CLASS (code) == '2'
+ || TREE_CODE_CLASS (code) == '<')
+ {
+ if (TREE_CODE (arg1) == COMPOUND_EXPR)
+ return build (COMPOUND_EXPR, type, TREE_OPERAND (arg1, 0),
+ fold (build (code, type,
+ arg0, TREE_OPERAND (arg1, 1))));
+ else if (TREE_CODE (arg1) == COND_EXPR
+ || TREE_CODE_CLASS (TREE_CODE (arg1)) == '<')
+ {
+ tree test, true_value, false_value;
+
+ if (TREE_CODE (arg1) == COND_EXPR)
+ {
+ test = TREE_OPERAND (arg1, 0);
+ true_value = TREE_OPERAND (arg1, 1);
+ false_value = TREE_OPERAND (arg1, 2);
+ }
+ else
+ {
+ test = arg1;
+ true_value = integer_one_node;
+ false_value = integer_zero_node;
+ }
+
+ /* If ARG0 is complex we want to make sure we only evaluate
+ it once. Though this is only required if it is volatile, it
+ might be more efficient even if it is not. However, if we
+ succeed in folding one part to a constant, we do not need
+ to make this SAVE_EXPR. Since we do this optimization
+ primarily to see if we do end up with constant and this
+ SAVE_EXPR interfers with later optimizations, suppressing
+ it when we can is important. */
+
+ if (TREE_CODE (arg0) != SAVE_EXPR
+ && ((TREE_CODE (arg0) != VAR_DECL
+ && TREE_CODE (arg0) != PARM_DECL)
+ || TREE_SIDE_EFFECTS (arg0)))
+ {
+ tree lhs = fold (build (code, type, arg0, true_value));
+ tree rhs = fold (build (code, type, arg0, false_value));
+
+ if (TREE_CONSTANT (lhs) || TREE_CONSTANT (rhs))
+ return fold (build (COND_EXPR, type, test, lhs, rhs));
+
+ arg0 = save_expr (arg0);
+ }
+
+ test = fold (build (COND_EXPR, type, test,
+ fold (build (code, type, arg0, true_value)),
+ fold (build (code, type, arg0, false_value))));
+ if (TREE_CODE (arg0) == SAVE_EXPR)
+ return build (COMPOUND_EXPR, type,
+ convert (void_type_node, arg0),
+ strip_compound_expr (test, arg0));
+ else
+ return convert (type, test);
+ }
+
+ else if (TREE_CODE (arg0) == COMPOUND_EXPR)
+ return build (COMPOUND_EXPR, type, TREE_OPERAND (arg0, 0),
+ fold (build (code, type, TREE_OPERAND (arg0, 1), arg1)));
+ else if (TREE_CODE (arg0) == COND_EXPR
+ || TREE_CODE_CLASS (TREE_CODE (arg0)) == '<')
+ {
+ tree test, true_value, false_value;
+
+ if (TREE_CODE (arg0) == COND_EXPR)
+ {
+ test = TREE_OPERAND (arg0, 0);
+ true_value = TREE_OPERAND (arg0, 1);
+ false_value = TREE_OPERAND (arg0, 2);
+ }
+ else
+ {
+ test = arg0;
+ true_value = integer_one_node;
+ false_value = integer_zero_node;
+ }
+
+ if (TREE_CODE (arg1) != SAVE_EXPR
+ && ((TREE_CODE (arg1) != VAR_DECL
+ && TREE_CODE (arg1) != PARM_DECL)
+ || TREE_SIDE_EFFECTS (arg1)))
+ {
+ tree lhs = fold (build (code, type, true_value, arg1));
+ tree rhs = fold (build (code, type, false_value, arg1));
+
+ if (TREE_CONSTANT (lhs) || TREE_CONSTANT (rhs)
+ || TREE_CONSTANT (arg1))
+ return fold (build (COND_EXPR, type, test, lhs, rhs));
+
+ arg1 = save_expr (arg1);
+ }
+
+ test = fold (build (COND_EXPR, type, test,
+ fold (build (code, type, true_value, arg1)),
+ fold (build (code, type, false_value, arg1))));
+ if (TREE_CODE (arg1) == SAVE_EXPR)
+ return build (COMPOUND_EXPR, type,
+ convert (void_type_node, arg1),
+ strip_compound_expr (test, arg1));
+ else
+ return convert (type, test);
+ }
+ }
+ else if (TREE_CODE_CLASS (code) == '<'
+ && TREE_CODE (arg0) == COMPOUND_EXPR)
+ return build (COMPOUND_EXPR, type, TREE_OPERAND (arg0, 0),
+ fold (build (code, type, TREE_OPERAND (arg0, 1), arg1)));
+ else if (TREE_CODE_CLASS (code) == '<'
+ && TREE_CODE (arg1) == COMPOUND_EXPR)
+ return build (COMPOUND_EXPR, type, TREE_OPERAND (arg1, 0),
+ fold (build (code, type, arg0, TREE_OPERAND (arg1, 1))));
+
+ switch (code)
+ {
+ case INTEGER_CST:
+ case REAL_CST:
+ case STRING_CST:
+ case COMPLEX_CST:
+ case CONSTRUCTOR:
+ return t;
+
+ case CONST_DECL:
+ return fold (DECL_INITIAL (t));
+
+ case NOP_EXPR:
+ case FLOAT_EXPR:
+ case CONVERT_EXPR:
+ case FIX_TRUNC_EXPR:
+ /* Other kinds of FIX are not handled properly by fold_convert. */
+
+ /* In addition to the cases of two conversions in a row
+ handled below, if we are converting something to its own
+ type via an object of identical or wider precision, neither
+ conversion is needed. */
+ if ((TREE_CODE (TREE_OPERAND (t, 0)) == NOP_EXPR
+ || TREE_CODE (TREE_OPERAND (t, 0)) == CONVERT_EXPR)
+ && TREE_TYPE (TREE_OPERAND (TREE_OPERAND (t, 0), 0)) == TREE_TYPE (t)
+ && ((INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (t, 0)))
+ && INTEGRAL_TYPE_P (TREE_TYPE (t)))
+ || (FLOAT_TYPE_P (TREE_TYPE (TREE_OPERAND (t, 0)))
+ && FLOAT_TYPE_P (TREE_TYPE (t))))
+ && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (t, 0)))
+ >= TYPE_PRECISION (TREE_TYPE (t))))
+ return TREE_OPERAND (TREE_OPERAND (t, 0), 0);
+
+ /* Two conversions in a row are not needed unless:
+ - the intermediate type is narrower than both initial and final, or
+ - the intermediate type and innermost type differ in signedness,
+ and the outermost type is wider than the intermediate, or
+ - the initial type is a pointer type and the precisions of the
+ intermediate and final types differ, or
+ - the final type is a pointer type and the precisions of the
+ initial and intermediate types differ. */
+ if ((TREE_CODE (TREE_OPERAND (t, 0)) == NOP_EXPR
+ || TREE_CODE (TREE_OPERAND (t, 0)) == CONVERT_EXPR)
+ && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (t, 0)))
+ > TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (t, 0), 0)))
+ ||
+ TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (t, 0)))
+ > TYPE_PRECISION (TREE_TYPE (t)))
+ && ! ((TREE_CODE (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (t, 0), 0)))
+ == INTEGER_TYPE)
+ && (TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0)))
+ == INTEGER_TYPE)
+ && (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (t, 0)))
+ != TREE_UNSIGNED (TREE_OPERAND (TREE_OPERAND (t, 0), 0)))
+ && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (t, 0)))
+ < TYPE_PRECISION (TREE_TYPE (t))))
+ && ((TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (t, 0)))
+ && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (t, 0)))
+ > TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (t, 0), 0)))))
+ ==
+ (TREE_UNSIGNED (TREE_TYPE (t))
+ && (TYPE_PRECISION (TREE_TYPE (t))
+ > TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (t, 0))))))
+ && ! ((TREE_CODE (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (t, 0), 0)))
+ == POINTER_TYPE)
+ && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (t, 0)))
+ != TYPE_PRECISION (TREE_TYPE (t))))
+ && ! (TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE
+ && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (t, 0), 0)))
+ != TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (t, 0))))))
+ return convert (TREE_TYPE (t), TREE_OPERAND (TREE_OPERAND (t, 0), 0));
+
+ if (TREE_CODE (TREE_OPERAND (t, 0)) == MODIFY_EXPR
+ && TREE_CONSTANT (TREE_OPERAND (TREE_OPERAND (t, 0), 1))
+ /* Detect assigning a bitfield. */
+ && !(TREE_CODE (TREE_OPERAND (TREE_OPERAND (t, 0), 0)) == COMPONENT_REF
+ && DECL_BIT_FIELD (TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (t, 0), 0), 1))))
+ {
+ /* Don't leave an assignment inside a conversion
+ unless assigning a bitfield. */
+ tree prev = TREE_OPERAND (t, 0);
+ TREE_OPERAND (t, 0) = TREE_OPERAND (prev, 1);
+ /* First do the assignment, then return converted constant. */
+ t = build (COMPOUND_EXPR, TREE_TYPE (t), prev, fold (t));
+ TREE_USED (t) = 1;
+ return t;
+ }
+ if (!wins)
+ {
+ TREE_CONSTANT (t) = TREE_CONSTANT (arg0);
+ return t;
+ }
+ return fold_convert (t, arg0);
+
+#if 0 /* This loses on &"foo"[0]. */
+ case ARRAY_REF:
+ {
+ int i;
+
+ /* Fold an expression like: "foo"[2] */
+ if (TREE_CODE (arg0) == STRING_CST
+ && TREE_CODE (arg1) == INTEGER_CST
+ && !TREE_INT_CST_HIGH (arg1)
+ && (i = TREE_INT_CST_LOW (arg1)) < TREE_STRING_LENGTH (arg0))
+ {
+ t = build_int_2 (TREE_STRING_POINTER (arg0)[i], 0);
+ TREE_TYPE (t) = TREE_TYPE (TREE_TYPE (arg0));
+ force_fit_type (t, 0);
+ }
+ }
+ return t;
+#endif /* 0 */
+
+ case RANGE_EXPR:
+ TREE_CONSTANT (t) = wins;
+ return t;
+
+ case NEGATE_EXPR:
+ if (wins)
+ {
+ if (TREE_CODE (arg0) == INTEGER_CST)
+ {
+ HOST_WIDE_INT low, high;
+ int overflow = neg_double (TREE_INT_CST_LOW (arg0),
+ TREE_INT_CST_HIGH (arg0),
+ &low, &high);
+ t = build_int_2 (low, high);
+ TREE_TYPE (t) = type;
+ TREE_OVERFLOW (t)
+ = (TREE_OVERFLOW (arg0)
+ | force_fit_type (t, overflow));
+ TREE_CONSTANT_OVERFLOW (t)
+ = TREE_OVERFLOW (t) | TREE_CONSTANT_OVERFLOW (arg0);
+ }
+ else if (TREE_CODE (arg0) == REAL_CST)
+ t = build_real (type, REAL_VALUE_NEGATE (TREE_REAL_CST (arg0)));
+ TREE_TYPE (t) = type;
+ }
+ else if (TREE_CODE (arg0) == NEGATE_EXPR)
+ return TREE_OPERAND (arg0, 0);
+
+ /* Convert - (a - b) to (b - a) for non-floating-point. */
+ else if (TREE_CODE (arg0) == MINUS_EXPR && ! FLOAT_TYPE_P (type))
+ return build (MINUS_EXPR, type, TREE_OPERAND (arg0, 1),
+ TREE_OPERAND (arg0, 0));
+
+ return t;
+
+ case ABS_EXPR:
+ if (wins)
+ {
+ if (TREE_CODE (arg0) == INTEGER_CST)
+ {
+ if (! TREE_UNSIGNED (type)
+ && TREE_INT_CST_HIGH (arg0) < 0)
+ {
+ HOST_WIDE_INT low, high;
+ int overflow = neg_double (TREE_INT_CST_LOW (arg0),
+ TREE_INT_CST_HIGH (arg0),
+ &low, &high);
+ t = build_int_2 (low, high);
+ TREE_TYPE (t) = type;
+ TREE_OVERFLOW (t)
+ = (TREE_OVERFLOW (arg0)
+ | force_fit_type (t, overflow));
+ TREE_CONSTANT_OVERFLOW (t)
+ = TREE_OVERFLOW (t) | TREE_CONSTANT_OVERFLOW (arg0);
+ }
+ }
+ else if (TREE_CODE (arg0) == REAL_CST)
+ {
+ if (REAL_VALUE_NEGATIVE (TREE_REAL_CST (arg0)))
+ t = build_real (type,
+ REAL_VALUE_NEGATE (TREE_REAL_CST (arg0)));
+ }
+ TREE_TYPE (t) = type;
+ }
+ else if (TREE_CODE (arg0) == ABS_EXPR || TREE_CODE (arg0) == NEGATE_EXPR)
+ return build1 (ABS_EXPR, type, TREE_OPERAND (arg0, 0));
+ return t;
+
+ case CONJ_EXPR:
+ if (TREE_CODE (TREE_TYPE (arg0)) != COMPLEX_TYPE)
+ return arg0;
+ else if (TREE_CODE (arg0) == COMPLEX_EXPR)
+ return build (COMPLEX_EXPR, TREE_TYPE (arg0),
+ TREE_OPERAND (arg0, 0),
+ fold (build1 (NEGATE_EXPR,
+ TREE_TYPE (TREE_TYPE (arg0)),
+ TREE_OPERAND (arg0, 1))));
+ else if (TREE_CODE (arg0) == COMPLEX_CST)
+ return build_complex (TREE_OPERAND (arg0, 0),
+ fold (build1 (NEGATE_EXPR,
+ TREE_TYPE (TREE_TYPE (arg0)),
+ TREE_OPERAND (arg0, 1))));
+ else if (TREE_CODE (arg0) == PLUS_EXPR || TREE_CODE (arg0) == MINUS_EXPR)
+ return fold (build (TREE_CODE (arg0), type,
+ fold (build1 (CONJ_EXPR, type,
+ TREE_OPERAND (arg0, 0))),
+ fold (build1 (CONJ_EXPR,
+ type, TREE_OPERAND (arg0, 1)))));
+ else if (TREE_CODE (arg0) == CONJ_EXPR)
+ return TREE_OPERAND (arg0, 0);
+ return t;
+
+ case BIT_NOT_EXPR:
+ if (wins)
+ {
+ if (TREE_CODE (arg0) == INTEGER_CST)
+ t = build_int_2 (~ TREE_INT_CST_LOW (arg0),
+ ~ TREE_INT_CST_HIGH (arg0));
+ TREE_TYPE (t) = type;
+ force_fit_type (t, 0);
+ TREE_OVERFLOW (t) = TREE_OVERFLOW (arg0);
+ TREE_CONSTANT_OVERFLOW (t) = TREE_CONSTANT_OVERFLOW (arg0);
+ }
+ else if (TREE_CODE (arg0) == BIT_NOT_EXPR)
+ return TREE_OPERAND (arg0, 0);
+ return t;
+
+ case PLUS_EXPR:
+ /* A + (-B) -> A - B */
+ if (TREE_CODE (arg1) == NEGATE_EXPR)
+ return fold (build (MINUS_EXPR, type, arg0, TREE_OPERAND (arg1, 0)));
+ else if (! FLOAT_TYPE_P (type))
+ {
+ if (integer_zerop (arg1))
+ return non_lvalue (convert (type, arg0));
+
+ /* If we are adding two BIT_AND_EXPR's, both of which are and'ing
+ with a constant, and the two constants have no bits in common,
+ we should treat this as a BIT_IOR_EXPR since this may produce more
+ simplifications. */
+ if (TREE_CODE (arg0) == BIT_AND_EXPR
+ && TREE_CODE (arg1) == BIT_AND_EXPR
+ && TREE_CODE (TREE_OPERAND (arg0, 1)) == INTEGER_CST
+ && TREE_CODE (TREE_OPERAND (arg1, 1)) == INTEGER_CST
+ && integer_zerop (const_binop (BIT_AND_EXPR,
+ TREE_OPERAND (arg0, 1),
+ TREE_OPERAND (arg1, 1), 0)))
+ {
+ code = BIT_IOR_EXPR;
+ goto bit_ior;
+ }
+
+ /* (A * C) + (B * C) -> (A+B) * C. Since we are most concerned
+ about the case where C is a constant, just try one of the
+ four possibilities. */
+
+ if (TREE_CODE (arg0) == MULT_EXPR && TREE_CODE (arg1) == MULT_EXPR
+ && operand_equal_p (TREE_OPERAND (arg0, 1),
+ TREE_OPERAND (arg1, 1), 0))
+ return fold (build (MULT_EXPR, type,
+ fold (build (PLUS_EXPR, type,
+ TREE_OPERAND (arg0, 0),
+ TREE_OPERAND (arg1, 0))),
+ TREE_OPERAND (arg0, 1)));
+ }
+ /* In IEEE floating point, x+0 may not equal x. */
+ else if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+ || flag_fast_math)
+ && real_zerop (arg1))
+ return non_lvalue (convert (type, arg0));
+ associate:
+ /* In most languages, can't associate operations on floats
+ through parentheses. Rather than remember where the parentheses
+ were, we don't associate floats at all. It shouldn't matter much.
+ However, associating multiplications is only very slightly
+ inaccurate, so do that if -ffast-math is specified. */
+ if (FLOAT_TYPE_P (type)
+ && ! (flag_fast_math && code == MULT_EXPR))
+ goto binary;
+
+ /* The varsign == -1 cases happen only for addition and subtraction.
+ It says that the arg that was split was really CON minus VAR.
+ The rest of the code applies to all associative operations. */
+ if (!wins)
+ {
+ tree var, con;
+ int varsign;
+
+ if (split_tree (arg0, code, &var, &con, &varsign))
+ {
+ if (varsign == -1)
+ {
+ /* EXPR is (CON-VAR) +- ARG1. */
+ /* If it is + and VAR==ARG1, return just CONST. */
+ if (code == PLUS_EXPR && operand_equal_p (var, arg1, 0))
+ return convert (TREE_TYPE (t), con);
+
+ /* If ARG0 is a constant, don't change things around;
+ instead keep all the constant computations together. */
+
+ if (TREE_CONSTANT (arg0))
+ return t;
+
+ /* Otherwise return (CON +- ARG1) - VAR. */
+ TREE_SET_CODE (t, MINUS_EXPR);
+ TREE_OPERAND (t, 1) = var;
+ TREE_OPERAND (t, 0)
+ = fold (build (code, TREE_TYPE (t), con, arg1));
+ }
+ else
+ {
+ /* EXPR is (VAR+CON) +- ARG1. */
+ /* If it is - and VAR==ARG1, return just CONST. */
+ if (code == MINUS_EXPR && operand_equal_p (var, arg1, 0))
+ return convert (TREE_TYPE (t), con);
+
+ /* If ARG0 is a constant, don't change things around;
+ instead keep all the constant computations together. */
+
+ if (TREE_CONSTANT (arg0))
+ return t;
+
+ /* Otherwise return VAR +- (ARG1 +- CON). */
+ TREE_OPERAND (t, 1) = tem
+ = fold (build (code, TREE_TYPE (t), arg1, con));
+ TREE_OPERAND (t, 0) = var;
+ if (integer_zerop (tem)
+ && (code == PLUS_EXPR || code == MINUS_EXPR))
+ return convert (type, var);
+ /* If we have x +/- (c - d) [c an explicit integer]
+ change it to x -/+ (d - c) since if d is relocatable
+ then the latter can be a single immediate insn
+ and the former cannot. */
+ if (TREE_CODE (tem) == MINUS_EXPR
+ && TREE_CODE (TREE_OPERAND (tem, 0)) == INTEGER_CST)
+ {
+ tree tem1 = TREE_OPERAND (tem, 1);
+ TREE_OPERAND (tem, 1) = TREE_OPERAND (tem, 0);
+ TREE_OPERAND (tem, 0) = tem1;
+ TREE_SET_CODE (t,
+ (code == PLUS_EXPR ? MINUS_EXPR : PLUS_EXPR));
+ }
+ }
+ return t;
+ }
+
+ if (split_tree (arg1, code, &var, &con, &varsign))
+ {
+ if (TREE_CONSTANT (arg1))
+ return t;
+
+ if (varsign == -1)
+ TREE_SET_CODE (t,
+ (code == PLUS_EXPR ? MINUS_EXPR : PLUS_EXPR));
+
+ /* EXPR is ARG0 +- (CON +- VAR). */
+ if (TREE_CODE (t) == MINUS_EXPR
+ && operand_equal_p (var, arg0, 0))
+ {
+ /* If VAR and ARG0 cancel, return just CON or -CON. */
+ if (code == PLUS_EXPR)
+ return convert (TREE_TYPE (t), con);
+ return fold (build1 (NEGATE_EXPR, TREE_TYPE (t),
+ convert (TREE_TYPE (t), con)));
+ }
+
+ TREE_OPERAND (t, 0)
+ = fold (build (code, TREE_TYPE (t), arg0, con));
+ TREE_OPERAND (t, 1) = var;
+ if (integer_zerop (TREE_OPERAND (t, 0))
+ && TREE_CODE (t) == PLUS_EXPR)
+ return convert (TREE_TYPE (t), var);
+ return t;
+ }
+ }
+ binary:
+#if defined (REAL_IS_NOT_DOUBLE) && ! defined (REAL_ARITHMETIC)
+ if (TREE_CODE (arg1) == REAL_CST)
+ return t;
+#endif /* REAL_IS_NOT_DOUBLE, and no REAL_ARITHMETIC */
+ if (wins)
+ t1 = const_binop (code, arg0, arg1, 0);
+ if (t1 != NULL_TREE)
+ {
+ /* The return value should always have
+ the same type as the original expression. */
+ TREE_TYPE (t1) = TREE_TYPE (t);
+ return t1;
+ }
+ return t;
+
+ case MINUS_EXPR:
+ if (! FLOAT_TYPE_P (type))
+ {
+ if (! wins && integer_zerop (arg0))
+ return build1 (NEGATE_EXPR, type, arg1);
+ if (integer_zerop (arg1))
+ return non_lvalue (convert (type, arg0));
+
+ /* (A * C) - (B * C) -> (A-B) * C. Since we are most concerned
+ about the case where C is a constant, just try one of the
+ four possibilities. */
+
+ if (TREE_CODE (arg0) == MULT_EXPR && TREE_CODE (arg1) == MULT_EXPR
+ && operand_equal_p (TREE_OPERAND (arg0, 1),
+ TREE_OPERAND (arg1, 1), 0))
+ return fold (build (MULT_EXPR, type,
+ fold (build (MINUS_EXPR, type,
+ TREE_OPERAND (arg0, 0),
+ TREE_OPERAND (arg1, 0))),
+ TREE_OPERAND (arg0, 1)));
+ }
+ /* Convert A - (-B) to A + B. */
+ else if (TREE_CODE (arg1) == NEGATE_EXPR)
+ return fold (build (PLUS_EXPR, type, arg0, TREE_OPERAND (arg1, 0)));
+
+ else if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+ || flag_fast_math)
+ {
+ /* Except with IEEE floating point, 0-x equals -x. */
+ if (! wins && real_zerop (arg0))
+ return build1 (NEGATE_EXPR, type, arg1);
+ /* Except with IEEE floating point, x-0 equals x. */
+ if (real_zerop (arg1))
+ return non_lvalue (convert (type, arg0));
+ }
+
+ /* Fold &x - &x. This can happen from &x.foo - &x.
+ This is unsafe for certain floats even in non-IEEE formats.
+ In IEEE, it is unsafe because it does wrong for NaNs.
+ Also note that operand_equal_p is always false if an operand
+ is volatile. */
+
+ if (operand_equal_p (arg0, arg1,
+ FLOAT_TYPE_P (type) && ! flag_fast_math))
+ return convert (type, integer_zero_node);
+
+ goto associate;
+
+ case MULT_EXPR:
+ if (! FLOAT_TYPE_P (type))
+ {
+ if (integer_zerop (arg1))
+ return omit_one_operand (type, arg1, arg0);
+ if (integer_onep (arg1))
+ return non_lvalue (convert (type, arg0));
+
+ /* ((A / C) * C) is A if the division is an
+ EXACT_DIV_EXPR. Since C is normally a constant,
+ just check for one of the four possibilities. */
+
+ if (TREE_CODE (arg0) == EXACT_DIV_EXPR
+ && operand_equal_p (TREE_OPERAND (arg0, 1), arg1, 0))
+ return TREE_OPERAND (arg0, 0);
+
+ /* (a * (1 << b)) is (a << b) */
+ if (TREE_CODE (arg1) == LSHIFT_EXPR
+ && integer_onep (TREE_OPERAND (arg1, 0)))
+ return fold (build (LSHIFT_EXPR, type, arg0,
+ TREE_OPERAND (arg1, 1)));
+ if (TREE_CODE (arg0) == LSHIFT_EXPR
+ && integer_onep (TREE_OPERAND (arg0, 0)))
+ return fold (build (LSHIFT_EXPR, type, arg1,
+ TREE_OPERAND (arg0, 1)));
+ }
+ else
+ {
+ /* x*0 is 0, except for IEEE floating point. */
+ if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+ || flag_fast_math)
+ && real_zerop (arg1))
+ return omit_one_operand (type, arg1, arg0);
+ /* In IEEE floating point, x*1 is not equivalent to x for snans.
+ However, ANSI says we can drop signals,
+ so we can do this anyway. */
+ if (real_onep (arg1))
+ return non_lvalue (convert (type, arg0));
+ /* x*2 is x+x */
+ if (! wins && real_twop (arg1))
+ {
+ tree arg = save_expr (arg0);
+ return build (PLUS_EXPR, type, arg, arg);
+ }
+ }
+ goto associate;
+
+ case BIT_IOR_EXPR:
+ bit_ior:
+ if (integer_all_onesp (arg1))
+ return omit_one_operand (type, arg1, arg0);
+ if (integer_zerop (arg1))
+ return non_lvalue (convert (type, arg0));
+ t1 = distribute_bit_expr (code, type, arg0, arg1);
+ if (t1 != NULL_TREE)
+ return t1;
+
+ /* (a << C1) | (a >> C2) if A is unsigned and C1+C2 is the size of A
+ is a rotate of A by C1 bits. */
+
+ if ((TREE_CODE (arg0) == RSHIFT_EXPR
+ || TREE_CODE (arg0) == LSHIFT_EXPR)
+ && (TREE_CODE (arg1) == RSHIFT_EXPR
+ || TREE_CODE (arg1) == LSHIFT_EXPR)
+ && TREE_CODE (arg0) != TREE_CODE (arg1)
+ && operand_equal_p (TREE_OPERAND (arg0, 0), TREE_OPERAND (arg1,0), 0)
+ && TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (arg0, 0)))
+ && TREE_CODE (TREE_OPERAND (arg0, 1)) == INTEGER_CST
+ && TREE_CODE (TREE_OPERAND (arg1, 1)) == INTEGER_CST
+ && TREE_INT_CST_HIGH (TREE_OPERAND (arg0, 1)) == 0
+ && TREE_INT_CST_HIGH (TREE_OPERAND (arg1, 1)) == 0
+ && ((TREE_INT_CST_LOW (TREE_OPERAND (arg0, 1))
+ + TREE_INT_CST_LOW (TREE_OPERAND (arg1, 1)))
+ == TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (arg0, 0)))))
+ return build (LROTATE_EXPR, type, TREE_OPERAND (arg0, 0),
+ TREE_CODE (arg0) == LSHIFT_EXPR
+ ? TREE_OPERAND (arg0, 1) : TREE_OPERAND (arg1, 1));
+
+ goto associate;
+
+ case BIT_XOR_EXPR:
+ if (integer_zerop (arg1))
+ return non_lvalue (convert (type, arg0));
+ if (integer_all_onesp (arg1))
+ return fold (build1 (BIT_NOT_EXPR, type, arg0));
+ goto associate;
+
+ case BIT_AND_EXPR:
+ bit_and:
+ if (integer_all_onesp (arg1))
+ return non_lvalue (convert (type, arg0));
+ if (integer_zerop (arg1))
+ return omit_one_operand (type, arg1, arg0);
+ t1 = distribute_bit_expr (code, type, arg0, arg1);
+ if (t1 != NULL_TREE)
+ return t1;
+ /* Simplify ((int)c & 0x377) into (int)c, if c is unsigned char. */
+ if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == NOP_EXPR
+ && TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (arg1, 0))))
+ {
+ int prec = TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (arg1, 0)));
+ if (prec < BITS_PER_WORD && prec < HOST_BITS_PER_WIDE_INT
+ && (~TREE_INT_CST_LOW (arg0)
+ & (((HOST_WIDE_INT) 1 << prec) - 1)) == 0)
+ return build1 (NOP_EXPR, type, TREE_OPERAND (arg1, 0));
+ }
+ if (TREE_CODE (arg1) == INTEGER_CST && TREE_CODE (arg0) == NOP_EXPR
+ && TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (arg0, 0))))
+ {
+ int prec = TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (arg0, 0)));
+ if (prec < BITS_PER_WORD && prec < HOST_BITS_PER_WIDE_INT
+ && (~TREE_INT_CST_LOW (arg1)
+ & (((HOST_WIDE_INT) 1 << prec) - 1)) == 0)
+ return build1 (NOP_EXPR, type, TREE_OPERAND (arg0, 0));
+ }
+ goto associate;
+
+ case BIT_ANDTC_EXPR:
+ if (integer_all_onesp (arg0))
+ return non_lvalue (convert (type, arg1));
+ if (integer_zerop (arg0))
+ return omit_one_operand (type, arg0, arg1);
+ if (TREE_CODE (arg1) == INTEGER_CST)
+ {
+ arg1 = fold (build1 (BIT_NOT_EXPR, type, arg1));
+ code = BIT_AND_EXPR;
+ goto bit_and;
+ }
+ goto binary;
+
+ case RDIV_EXPR:
+ /* In most cases, do nothing with a divide by zero. */
+#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
+#ifndef REAL_INFINITY
+ if (TREE_CODE (arg1) == REAL_CST && real_zerop (arg1))
+ return t;
+#endif
+#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
+
+ /* In IEEE floating point, x/1 is not equivalent to x for snans.
+ However, ANSI says we can drop signals, so we can do this anyway. */
+ if (real_onep (arg1))
+ return non_lvalue (convert (type, arg0));
+
+ /* If ARG1 is a constant, we can convert this to a multiply by the
+ reciprocal. This does not have the same rounding properties,
+ so only do this if -ffast-math. We can actually always safely
+ do it if ARG1 is a power of two, but it's hard to tell if it is
+ or not in a portable manner. */
+ if (TREE_CODE (arg1) == REAL_CST && flag_fast_math
+ && 0 != (tem = const_binop (code, build_real (type, dconst1),
+ arg1, 0)))
+ return fold (build (MULT_EXPR, type, arg0, tem));
+
+ goto binary;
+
+ case TRUNC_DIV_EXPR:
+ case ROUND_DIV_EXPR:
+ case FLOOR_DIV_EXPR:
+ case CEIL_DIV_EXPR:
+ case EXACT_DIV_EXPR:
+ if (integer_onep (arg1))
+ return non_lvalue (convert (type, arg0));
+ if (integer_zerop (arg1))
+ return t;
+
+ /* If we have ((a / C1) / C2) where both division are the same type, try
+ to simplify. First see if C1 * C2 overflows or not. */
+ if (TREE_CODE (arg0) == code && TREE_CODE (arg1) == INTEGER_CST
+ && TREE_CODE (TREE_OPERAND (arg0, 1)) == INTEGER_CST)
+ {
+ tree new_divisor;
+
+ new_divisor = const_binop (MULT_EXPR, TREE_OPERAND (arg0, 1), arg1, 0);
+ tem = const_binop (FLOOR_DIV_EXPR, new_divisor, arg1, 0);
+
+ if (TREE_INT_CST_LOW (TREE_OPERAND (arg0, 1)) == TREE_INT_CST_LOW (tem)
+ && TREE_INT_CST_HIGH (TREE_OPERAND (arg0, 1)) == TREE_INT_CST_HIGH (tem))
+ {
+ /* If no overflow, divide by C1*C2. */
+ return fold (build (code, type, TREE_OPERAND (arg0, 0), new_divisor));
+ }
+ }
+
+ /* Look for ((a * C1) / C3) or (((a * C1) + C2) / C3),
+ where C1 % C3 == 0 or C3 % C1 == 0. We can simplify these
+ expressions, which often appear in the offsets or sizes of
+ objects with a varying size. Only deal with positive divisors
+ and multiplicands. If C2 is negative, we must have C2 % C3 == 0.
+
+ Look for NOPs and SAVE_EXPRs inside. */
+
+ if (TREE_CODE (arg1) == INTEGER_CST
+ && tree_int_cst_sgn (arg1) >= 0)
+ {
+ int have_save_expr = 0;
+ tree c2 = integer_zero_node;
+ tree xarg0 = arg0;
+
+ if (TREE_CODE (xarg0) == SAVE_EXPR)
+ have_save_expr = 1, xarg0 = TREE_OPERAND (xarg0, 0);
+
+ STRIP_NOPS (xarg0);
+
+ if (TREE_CODE (xarg0) == PLUS_EXPR
+ && TREE_CODE (TREE_OPERAND (xarg0, 1)) == INTEGER_CST)
+ c2 = TREE_OPERAND (xarg0, 1), xarg0 = TREE_OPERAND (xarg0, 0);
+ else if (TREE_CODE (xarg0) == MINUS_EXPR
+ && TREE_CODE (TREE_OPERAND (xarg0, 1)) == INTEGER_CST
+ /* If we are doing this computation unsigned, the negate
+ is incorrect. */
+ && ! TREE_UNSIGNED (type))
+ {
+ c2 = fold (build1 (NEGATE_EXPR, type, TREE_OPERAND (xarg0, 1)));
+ xarg0 = TREE_OPERAND (xarg0, 0);
+ }
+
+ if (TREE_CODE (xarg0) == SAVE_EXPR)
+ have_save_expr = 1, xarg0 = TREE_OPERAND (xarg0, 0);
+
+ STRIP_NOPS (xarg0);
+
+ if (TREE_CODE (xarg0) == MULT_EXPR
+ && TREE_CODE (TREE_OPERAND (xarg0, 1)) == INTEGER_CST
+ && tree_int_cst_sgn (TREE_OPERAND (xarg0, 1)) >= 0
+ && (integer_zerop (const_binop (TRUNC_MOD_EXPR,
+ TREE_OPERAND (xarg0, 1), arg1, 1))
+ || integer_zerop (const_binop (TRUNC_MOD_EXPR, arg1,
+ TREE_OPERAND (xarg0, 1), 1)))
+ && (tree_int_cst_sgn (c2) >= 0
+ || integer_zerop (const_binop (TRUNC_MOD_EXPR, c2,
+ arg1, 1))))
+ {
+ tree outer_div = integer_one_node;
+ tree c1 = TREE_OPERAND (xarg0, 1);
+ tree c3 = arg1;
+
+ /* If C3 > C1, set them equal and do a divide by
+ C3/C1 at the end of the operation. */
+ if (tree_int_cst_lt (c1, c3))
+ outer_div = const_binop (code, c3, c1, 0), c3 = c1;
+
+ /* The result is A * (C1/C3) + (C2/C3). */
+ t = fold (build (PLUS_EXPR, type,
+ fold (build (MULT_EXPR, type,
+ TREE_OPERAND (xarg0, 0),
+ const_binop (code, c1, c3, 1))),
+ const_binop (code, c2, c3, 1)));
+
+ if (! integer_onep (outer_div))
+ t = fold (build (code, type, t, convert (type, outer_div)));
+
+ if (have_save_expr)
+ t = save_expr (t);
+
+ return t;
+ }
+ }
+
+ goto binary;
+
+ case CEIL_MOD_EXPR:
+ case FLOOR_MOD_EXPR:
+ case ROUND_MOD_EXPR:
+ case TRUNC_MOD_EXPR:
+ if (integer_onep (arg1))
+ return omit_one_operand (type, integer_zero_node, arg0);
+ if (integer_zerop (arg1))
+ return t;
+
+ /* Look for ((a * C1) % C3) or (((a * C1) + C2) % C3),
+ where C1 % C3 == 0. Handle similarly to the division case,
+ but don't bother with SAVE_EXPRs. */
+
+ if (TREE_CODE (arg1) == INTEGER_CST
+ && ! integer_zerop (arg1))
+ {
+ tree c2 = integer_zero_node;
+ tree xarg0 = arg0;
+
+ if (TREE_CODE (xarg0) == PLUS_EXPR
+ && TREE_CODE (TREE_OPERAND (xarg0, 1)) == INTEGER_CST)
+ c2 = TREE_OPERAND (xarg0, 1), xarg0 = TREE_OPERAND (xarg0, 0);
+ else if (TREE_CODE (xarg0) == MINUS_EXPR
+ && TREE_CODE (TREE_OPERAND (xarg0, 1)) == INTEGER_CST
+ && ! TREE_UNSIGNED (type))
+ {
+ c2 = fold (build1 (NEGATE_EXPR, type, TREE_OPERAND (xarg0, 1)));
+ xarg0 = TREE_OPERAND (xarg0, 0);
+ }
+
+ STRIP_NOPS (xarg0);
+
+ if (TREE_CODE (xarg0) == MULT_EXPR
+ && TREE_CODE (TREE_OPERAND (xarg0, 1)) == INTEGER_CST
+ && integer_zerop (const_binop (TRUNC_MOD_EXPR,
+ TREE_OPERAND (xarg0, 1),
+ arg1, 1))
+ && tree_int_cst_sgn (c2) >= 0)
+ /* The result is (C2%C3). */
+ return omit_one_operand (type, const_binop (code, c2, arg1, 1),
+ TREE_OPERAND (xarg0, 0));
+ }
+
+ goto binary;
+
+ case LSHIFT_EXPR:
+ case RSHIFT_EXPR:
+ case LROTATE_EXPR:
+ case RROTATE_EXPR:
+ if (integer_zerop (arg1))
+ return non_lvalue (convert (type, arg0));
+ /* Since negative shift count is not well-defined,
+ don't try to compute it in the compiler. */
+ if (tree_int_cst_sgn (arg1) < 0)
+ return t;
+ goto binary;
+
+ case MIN_EXPR:
+ if (operand_equal_p (arg0, arg1, 0))
+ return arg0;
+ if (INTEGRAL_TYPE_P (type)
+ && operand_equal_p (arg1, TYPE_MIN_VALUE (type), 1))
+ return omit_one_operand (type, arg1, arg0);
+ goto associate;
+
+ case MAX_EXPR:
+ if (operand_equal_p (arg0, arg1, 0))
+ return arg0;
+ if (INTEGRAL_TYPE_P (type)
+ && operand_equal_p (arg1, TYPE_MAX_VALUE (type), 1))
+ return omit_one_operand (type, arg1, arg0);
+ goto associate;
+
+ case TRUTH_NOT_EXPR:
+ /* Note that the operand of this must be an int
+ and its values must be 0 or 1.
+ ("true" is a fixed value perhaps depending on the language,
+ but we don't handle values other than 1 correctly yet.) */
+ return invert_truthvalue (arg0);
+
+ case TRUTH_ANDIF_EXPR:
+ /* Note that the operands of this must be ints
+ and their values must be 0 or 1.
+ ("true" is a fixed value perhaps depending on the language.) */
+ /* If first arg is constant zero, return it. */
+ if (integer_zerop (arg0))
+ return arg0;
+ case TRUTH_AND_EXPR:
+ /* If either arg is constant true, drop it. */
+ if (TREE_CODE (arg0) == INTEGER_CST && ! integer_zerop (arg0))
+ return non_lvalue (arg1);
+ if (TREE_CODE (arg1) == INTEGER_CST && ! integer_zerop (arg1))
+ return non_lvalue (arg0);
+ /* If second arg is constant zero, result is zero, but first arg
+ must be evaluated. */
+ if (integer_zerop (arg1))
+ return omit_one_operand (type, arg1, arg0);
+
+ truth_andor:
+ /* We only do these simplifications if we are optimizing. */
+ if (!optimize)
+ return t;
+
+ /* Check for things like (A || B) && (A || C). We can convert this
+ to A || (B && C). Note that either operator can be any of the four
+ truth and/or operations and the transformation will still be
+ valid. Also note that we only care about order for the
+ ANDIF and ORIF operators. */
+ if (TREE_CODE (arg0) == TREE_CODE (arg1)
+ && (TREE_CODE (arg0) == TRUTH_ANDIF_EXPR
+ || TREE_CODE (arg0) == TRUTH_ORIF_EXPR
+ || TREE_CODE (arg0) == TRUTH_AND_EXPR
+ || TREE_CODE (arg0) == TRUTH_OR_EXPR))
+ {
+ tree a00 = TREE_OPERAND (arg0, 0);
+ tree a01 = TREE_OPERAND (arg0, 1);
+ tree a10 = TREE_OPERAND (arg1, 0);
+ tree a11 = TREE_OPERAND (arg1, 1);
+ int commutative = ((TREE_CODE (arg0) == TRUTH_OR_EXPR
+ || TREE_CODE (arg0) == TRUTH_AND_EXPR)
+ && (code == TRUTH_AND_EXPR
+ || code == TRUTH_OR_EXPR));
+
+ if (operand_equal_p (a00, a10, 0))
+ return fold (build (TREE_CODE (arg0), type, a00,
+ fold (build (code, type, a01, a11))));
+ else if (commutative && operand_equal_p (a00, a11, 0))
+ return fold (build (TREE_CODE (arg0), type, a00,
+ fold (build (code, type, a01, a10))));
+ else if (commutative && operand_equal_p (a01, a10, 0))
+ return fold (build (TREE_CODE (arg0), type, a01,
+ fold (build (code, type, a00, a11))));
+
+ /* This case if tricky because we must either have commutative
+ operators or else A10 must not have side-effects. */
+
+ else if ((commutative || ! TREE_SIDE_EFFECTS (a10))
+ && operand_equal_p (a01, a11, 0))
+ return fold (build (TREE_CODE (arg0), type,
+ fold (build (code, type, a00, a10)),
+ a01));
+ }
+
+ /* Check for the possibility of merging component references. If our
+ lhs is another similar operation, try to merge its rhs with our
+ rhs. Then try to merge our lhs and rhs. */
+ if (TREE_CODE (arg0) == code
+ && 0 != (tem = fold_truthop (code, type,
+ TREE_OPERAND (arg0, 1), arg1)))
+ return fold (build (code, type, TREE_OPERAND (arg0, 0), tem));
+
+ if ((tem = fold_truthop (code, type, arg0, arg1)) != 0)
+ return tem;
+
+ return t;
+
+ case TRUTH_ORIF_EXPR:
+ /* Note that the operands of this must be ints
+ and their values must be 0 or true.
+ ("true" is a fixed value perhaps depending on the language.) */
+ /* If first arg is constant true, return it. */
+ if (TREE_CODE (arg0) == INTEGER_CST && ! integer_zerop (arg0))
+ return arg0;
+ case TRUTH_OR_EXPR:
+ /* If either arg is constant zero, drop it. */
+ if (TREE_CODE (arg0) == INTEGER_CST && integer_zerop (arg0))
+ return non_lvalue (arg1);
+ if (TREE_CODE (arg1) == INTEGER_CST && integer_zerop (arg1))
+ return non_lvalue (arg0);
+ /* If second arg is constant true, result is true, but we must
+ evaluate first arg. */
+ if (TREE_CODE (arg1) == INTEGER_CST && ! integer_zerop (arg1))
+ return omit_one_operand (type, arg1, arg0);
+ goto truth_andor;
+
+ case TRUTH_XOR_EXPR:
+ /* If either arg is constant zero, drop it. */
+ if (integer_zerop (arg0))
+ return non_lvalue (arg1);
+ if (integer_zerop (arg1))
+ return non_lvalue (arg0);
+ /* If either arg is constant true, this is a logical inversion. */
+ if (integer_onep (arg0))
+ return non_lvalue (invert_truthvalue (arg1));
+ if (integer_onep (arg1))
+ return non_lvalue (invert_truthvalue (arg0));
+ return t;
+
+ case EQ_EXPR:
+ case NE_EXPR:
+ case LT_EXPR:
+ case GT_EXPR:
+ case LE_EXPR:
+ case GE_EXPR:
+ /* If one arg is a constant integer, put it last. */
+ if (TREE_CODE (arg0) == INTEGER_CST
+ && TREE_CODE (arg1) != INTEGER_CST)
+ {
+ TREE_OPERAND (t, 0) = arg1;
+ TREE_OPERAND (t, 1) = arg0;
+ arg0 = TREE_OPERAND (t, 0);
+ arg1 = TREE_OPERAND (t, 1);
+ code = swap_tree_comparison (code);
+ TREE_SET_CODE (t, code);
+ }
+
+ /* Convert foo++ == CONST into ++foo == CONST + INCR.
+ First, see if one arg is constant; find the constant arg
+ and the other one. */
+ {
+ tree constop = 0, varop;
+ tree *constoploc;
+
+ if (TREE_CONSTANT (arg1))
+ constoploc = &TREE_OPERAND (t, 1), constop = arg1, varop = arg0;
+ if (TREE_CONSTANT (arg0))
+ constoploc = &TREE_OPERAND (t, 0), constop = arg0, varop = arg1;
+
+ if (constop && TREE_CODE (varop) == POSTINCREMENT_EXPR)
+ {
+ /* This optimization is invalid for ordered comparisons
+ if CONST+INCR overflows or if foo+incr might overflow.
+ This optimization is invalid for floating point due to rounding.
+ For pointer types we assume overflow doesn't happen. */
+ if (TREE_CODE (TREE_TYPE (varop)) == POINTER_TYPE
+ || (! FLOAT_TYPE_P (TREE_TYPE (varop))
+ && (code == EQ_EXPR || code == NE_EXPR)))
+ {
+ tree newconst
+ = fold (build (PLUS_EXPR, TREE_TYPE (varop),
+ constop, TREE_OPERAND (varop, 1)));
+ TREE_SET_CODE (varop, PREINCREMENT_EXPR);
+ *constoploc = newconst;
+ return t;
+ }
+ }
+ else if (constop && TREE_CODE (varop) == POSTDECREMENT_EXPR)
+ {
+ if (TREE_CODE (TREE_TYPE (varop)) == POINTER_TYPE
+ || (! FLOAT_TYPE_P (TREE_TYPE (varop))
+ && (code == EQ_EXPR || code == NE_EXPR)))
+ {
+ tree newconst
+ = fold (build (MINUS_EXPR, TREE_TYPE (varop),
+ constop, TREE_OPERAND (varop, 1)));
+ TREE_SET_CODE (varop, PREDECREMENT_EXPR);
+ *constoploc = newconst;
+ return t;
+ }
+ }
+ }
+
+ /* Change X >= CST to X > (CST - 1) if CST is positive. */
+ if (TREE_CODE (arg1) == INTEGER_CST
+ && TREE_CODE (arg0) != INTEGER_CST
+ && tree_int_cst_sgn (arg1) > 0)
+ {
+ switch (TREE_CODE (t))
+ {
+ case GE_EXPR:
+ code = GT_EXPR;
+ TREE_SET_CODE (t, code);
+ arg1 = const_binop (MINUS_EXPR, arg1, integer_one_node, 0);
+ TREE_OPERAND (t, 1) = arg1;
+ break;
+
+ case LT_EXPR:
+ code = LE_EXPR;
+ TREE_SET_CODE (t, code);
+ arg1 = const_binop (MINUS_EXPR, arg1, integer_one_node, 0);
+ TREE_OPERAND (t, 1) = arg1;
+ }
+ }
+
+ /* If this is an EQ or NE comparison with zero and ARG0 is
+ (1 << foo) & bar, convert it to (bar >> foo) & 1. Both require
+ two operations, but the latter can be done in one less insn
+ one machine that have only two-operand insns or on which a
+ constant cannot be the first operand. */
+ if (integer_zerop (arg1) && (code == EQ_EXPR || code == NE_EXPR)
+ && TREE_CODE (arg0) == BIT_AND_EXPR)
+ {
+ if (TREE_CODE (TREE_OPERAND (arg0, 0)) == LSHIFT_EXPR
+ && integer_onep (TREE_OPERAND (TREE_OPERAND (arg0, 0), 0)))
+ return
+ fold (build (code, type,
+ build (BIT_AND_EXPR, TREE_TYPE (arg0),
+ build (RSHIFT_EXPR,
+ TREE_TYPE (TREE_OPERAND (arg0, 0)),
+ TREE_OPERAND (arg0, 1),
+ TREE_OPERAND (TREE_OPERAND (arg0, 0), 1)),
+ convert (TREE_TYPE (arg0),
+ integer_one_node)),
+ arg1));
+ else if (TREE_CODE (TREE_OPERAND (arg0, 1)) == LSHIFT_EXPR
+ && integer_onep (TREE_OPERAND (TREE_OPERAND (arg0, 1), 0)))
+ return
+ fold (build (code, type,
+ build (BIT_AND_EXPR, TREE_TYPE (arg0),
+ build (RSHIFT_EXPR,
+ TREE_TYPE (TREE_OPERAND (arg0, 1)),
+ TREE_OPERAND (arg0, 0),
+ TREE_OPERAND (TREE_OPERAND (arg0, 1), 1)),
+ convert (TREE_TYPE (arg0),
+ integer_one_node)),
+ arg1));
+ }
+
+ /* If this is an NE or EQ comparison of zero against the result of a
+ signed MOD operation whose second operand is a power of 2, make
+ the MOD operation unsigned since it is simpler and equivalent. */
+ if ((code == NE_EXPR || code == EQ_EXPR)
+ && integer_zerop (arg1)
+ && ! TREE_UNSIGNED (TREE_TYPE (arg0))
+ && (TREE_CODE (arg0) == TRUNC_MOD_EXPR
+ || TREE_CODE (arg0) == CEIL_MOD_EXPR
+ || TREE_CODE (arg0) == FLOOR_MOD_EXPR
+ || TREE_CODE (arg0) == ROUND_MOD_EXPR)
+ && integer_pow2p (TREE_OPERAND (arg0, 1)))
+ {
+ tree newtype = unsigned_type (TREE_TYPE (arg0));
+ tree newmod = build (TREE_CODE (arg0), newtype,
+ convert (newtype, TREE_OPERAND (arg0, 0)),
+ convert (newtype, TREE_OPERAND (arg0, 1)));
+
+ return build (code, type, newmod, convert (newtype, arg1));
+ }
+
+ /* If this is an NE comparison of zero with an AND of one, remove the
+ comparison since the AND will give the correct value. */
+ if (code == NE_EXPR && integer_zerop (arg1)
+ && TREE_CODE (arg0) == BIT_AND_EXPR
+ && integer_onep (TREE_OPERAND (arg0, 1)))
+ return convert (type, arg0);
+
+ /* If we have (A & C) == C where C is a power of 2, convert this into
+ (A & C) != 0. Similarly for NE_EXPR. */
+ if ((code == EQ_EXPR || code == NE_EXPR)
+ && TREE_CODE (arg0) == BIT_AND_EXPR
+ && integer_pow2p (TREE_OPERAND (arg0, 1))
+ && operand_equal_p (TREE_OPERAND (arg0, 1), arg1, 0))
+ return build (code == EQ_EXPR ? NE_EXPR : EQ_EXPR, type,
+ arg0, integer_zero_node);
+
+ /* If X is unsigned, convert X < (1 << Y) into X >> Y == 0
+ and similarly for >= into !=. */
+ if ((code == LT_EXPR || code == GE_EXPR)
+ && TREE_UNSIGNED (TREE_TYPE (arg0))
+ && TREE_CODE (arg1) == LSHIFT_EXPR
+ && integer_onep (TREE_OPERAND (arg1, 0)))
+ return build (code == LT_EXPR ? EQ_EXPR : NE_EXPR, type,
+ build (RSHIFT_EXPR, TREE_TYPE (arg0), arg0,
+ TREE_OPERAND (arg1, 1)),
+ convert (TREE_TYPE (arg0), integer_zero_node));
+
+ else if ((code == LT_EXPR || code == GE_EXPR)
+ && TREE_UNSIGNED (TREE_TYPE (arg0))
+ && (TREE_CODE (arg1) == NOP_EXPR
+ || TREE_CODE (arg1) == CONVERT_EXPR)
+ && TREE_CODE (TREE_OPERAND (arg1, 0)) == LSHIFT_EXPR
+ && integer_onep (TREE_OPERAND (TREE_OPERAND (arg1, 0), 0)))
+ return
+ build (code == LT_EXPR ? EQ_EXPR : NE_EXPR, type,
+ convert (TREE_TYPE (arg0),
+ build (RSHIFT_EXPR, TREE_TYPE (arg0), arg0,
+ TREE_OPERAND (TREE_OPERAND (arg1, 0), 1))),
+ convert (TREE_TYPE (arg0), integer_zero_node));
+
+ /* Simplify comparison of something with itself. (For IEEE
+ floating-point, we can only do some of these simplifications.) */
+ if (operand_equal_p (arg0, arg1, 0))
+ {
+ switch (code)
+ {
+ case EQ_EXPR:
+ case GE_EXPR:
+ case LE_EXPR:
+ if (INTEGRAL_TYPE_P (TREE_TYPE (arg0)))
+ {
+ t = build_int_2 (1, 0);
+ TREE_TYPE (t) = type;
+ return t;
+ }
+ code = EQ_EXPR;
+ TREE_SET_CODE (t, code);
+ break;
+
+ case NE_EXPR:
+ /* For NE, we can only do this simplification if integer. */
+ if (! INTEGRAL_TYPE_P (TREE_TYPE (arg0)))
+ break;
+ /* ... fall through ... */
+ case GT_EXPR:
+ case LT_EXPR:
+ t = build_int_2 (0, 0);
+ TREE_TYPE (t) = type;
+ return t;
+ }
+ }
+
+ /* An unsigned comparison against 0 can be simplified. */
+ if (integer_zerop (arg1)
+ && (INTEGRAL_TYPE_P (TREE_TYPE (arg1))
+ || TREE_CODE (TREE_TYPE (arg1)) == POINTER_TYPE)
+ && TREE_UNSIGNED (TREE_TYPE (arg1)))
+ {
+ switch (TREE_CODE (t))
+ {
+ case GT_EXPR:
+ code = NE_EXPR;
+ TREE_SET_CODE (t, NE_EXPR);
+ break;
+ case LE_EXPR:
+ code = EQ_EXPR;
+ TREE_SET_CODE (t, EQ_EXPR);
+ break;
+ case GE_EXPR:
+ return omit_one_operand (type,
+ convert (type, integer_one_node),
+ arg0);
+ case LT_EXPR:
+ return omit_one_operand (type,
+ convert (type, integer_zero_node),
+ arg0);
+ }
+ }
+
+ /* If we are comparing an expression that just has comparisons
+ of two integer values, arithmetic expressions of those comparisons,
+ and constants, we can simplify it. There are only three cases
+ to check: the two values can either be equal, the first can be
+ greater, or the second can be greater. Fold the expression for
+ those three values. Since each value must be 0 or 1, we have
+ eight possibilities, each of which corresponds to the constant 0
+ or 1 or one of the six possible comparisons.
+
+ This handles common cases like (a > b) == 0 but also handles
+ expressions like ((x > y) - (y > x)) > 0, which supposedly
+ occur in macroized code. */
+
+ if (TREE_CODE (arg1) == INTEGER_CST && TREE_CODE (arg0) != INTEGER_CST)
+ {
+ tree cval1 = 0, cval2 = 0;
+ int save_p = 0;
+
+ if (twoval_comparison_p (arg0, &cval1, &cval2, &save_p)
+ /* Don't handle degenerate cases here; they should already
+ have been handled anyway. */
+ && cval1 != 0 && cval2 != 0
+ && ! (TREE_CONSTANT (cval1) && TREE_CONSTANT (cval2))
+ && TREE_TYPE (cval1) == TREE_TYPE (cval2)
+ && INTEGRAL_TYPE_P (TREE_TYPE (cval1))
+ && ! operand_equal_p (TYPE_MIN_VALUE (TREE_TYPE (cval1)),
+ TYPE_MAX_VALUE (TREE_TYPE (cval2)), 0))
+ {
+ tree maxval = TYPE_MAX_VALUE (TREE_TYPE (cval1));
+ tree minval = TYPE_MIN_VALUE (TREE_TYPE (cval1));
+
+ /* We can't just pass T to eval_subst in case cval1 or cval2
+ was the same as ARG1. */
+
+ tree high_result
+ = fold (build (code, type,
+ eval_subst (arg0, cval1, maxval, cval2, minval),
+ arg1));
+ tree equal_result
+ = fold (build (code, type,
+ eval_subst (arg0, cval1, maxval, cval2, maxval),
+ arg1));
+ tree low_result
+ = fold (build (code, type,
+ eval_subst (arg0, cval1, minval, cval2, maxval),
+ arg1));
+
+ /* All three of these results should be 0 or 1. Confirm they
+ are. Then use those values to select the proper code
+ to use. */
+
+ if ((integer_zerop (high_result)
+ || integer_onep (high_result))
+ && (integer_zerop (equal_result)
+ || integer_onep (equal_result))
+ && (integer_zerop (low_result)
+ || integer_onep (low_result)))
+ {
+ /* Make a 3-bit mask with the high-order bit being the
+ value for `>', the next for '=', and the low for '<'. */
+ switch ((integer_onep (high_result) * 4)
+ + (integer_onep (equal_result) * 2)
+ + integer_onep (low_result))
+ {
+ case 0:
+ /* Always false. */
+ return omit_one_operand (type, integer_zero_node, arg0);
+ case 1:
+ code = LT_EXPR;
+ break;
+ case 2:
+ code = EQ_EXPR;
+ break;
+ case 3:
+ code = LE_EXPR;
+ break;
+ case 4:
+ code = GT_EXPR;
+ break;
+ case 5:
+ code = NE_EXPR;
+ break;
+ case 6:
+ code = GE_EXPR;
+ break;
+ case 7:
+ /* Always true. */
+ return omit_one_operand (type, integer_one_node, arg0);
+ }
+
+ t = build (code, type, cval1, cval2);
+ if (save_p)
+ return save_expr (t);
+ else
+ return fold (t);
+ }
+ }
+ }
+
+ /* If this is a comparison of a field, we may be able to simplify it. */
+ if ((TREE_CODE (arg0) == COMPONENT_REF
+ || TREE_CODE (arg0) == BIT_FIELD_REF)
+ && (code == EQ_EXPR || code == NE_EXPR)
+ /* Handle the constant case even without -O
+ to make sure the warnings are given. */
+ && (optimize || TREE_CODE (arg1) == INTEGER_CST))
+ {
+ t1 = optimize_bit_field_compare (code, type, arg0, arg1);
+ return t1 ? t1 : t;
+ }
+
+ /* If this is a comparison of complex values and either or both
+ sizes are a COMPLEX_EXPR, it is best to split up the comparisons
+ and join them with a TRUTH_ANDIF_EXPR or TRUTH_ORIF_EXPR. This
+ may prevent needless evaluations. */
+ if ((code == EQ_EXPR || code == NE_EXPR)
+ && TREE_CODE (TREE_TYPE (arg0)) == COMPLEX_TYPE
+ && (TREE_CODE (arg0) == COMPLEX_EXPR
+ || TREE_CODE (arg1) == COMPLEX_EXPR))
+ {
+ tree subtype = TREE_TYPE (TREE_TYPE (arg0));
+ tree real0 = fold (build1 (REALPART_EXPR, subtype, arg0));
+ tree imag0 = fold (build1 (IMAGPART_EXPR, subtype, arg0));
+ tree real1 = fold (build1 (REALPART_EXPR, subtype, arg1));
+ tree imag1 = fold (build1 (IMAGPART_EXPR, subtype, arg1));
+
+ return fold (build ((code == EQ_EXPR ? TRUTH_ANDIF_EXPR
+ : TRUTH_ORIF_EXPR),
+ type,
+ fold (build (code, type, real0, real1)),
+ fold (build (code, type, imag0, imag1))));
+ }
+
+ /* From here on, the only cases we handle are when the result is
+ known to be a constant.
+
+ To compute GT, swap the arguments and do LT.
+ To compute GE, do LT and invert the result.
+ To compute LE, swap the arguments, do LT and invert the result.
+ To compute NE, do EQ and invert the result.
+
+ Therefore, the code below must handle only EQ and LT. */
+
+ if (code == LE_EXPR || code == GT_EXPR)
+ {
+ tem = arg0, arg0 = arg1, arg1 = tem;
+ code = swap_tree_comparison (code);
+ }
+
+ /* Note that it is safe to invert for real values here because we
+ will check below in the one case that it matters. */
+
+ invert = 0;
+ if (code == NE_EXPR || code == GE_EXPR)
+ {
+ invert = 1;
+ code = invert_tree_comparison (code);
+ }
+
+ /* Compute a result for LT or EQ if args permit;
+ otherwise return T. */
+ if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
+ {
+ if (code == EQ_EXPR)
+ t1 = build_int_2 ((TREE_INT_CST_LOW (arg0)
+ == TREE_INT_CST_LOW (arg1))
+ && (TREE_INT_CST_HIGH (arg0)
+ == TREE_INT_CST_HIGH (arg1)),
+ 0);
+ else
+ t1 = build_int_2 ((TREE_UNSIGNED (TREE_TYPE (arg0))
+ ? INT_CST_LT_UNSIGNED (arg0, arg1)
+ : INT_CST_LT (arg0, arg1)),
+ 0);
+ }
+
+ /* Assume a nonexplicit constant cannot equal an explicit one,
+ since such code would be undefined anyway.
+ Exception: on sysvr4, using #pragma weak,
+ a label can come out as 0. */
+ else if (TREE_CODE (arg1) == INTEGER_CST
+ && !integer_zerop (arg1)
+ && TREE_CONSTANT (arg0)
+ && TREE_CODE (arg0) == ADDR_EXPR
+ && code == EQ_EXPR)
+ t1 = build_int_2 (0, 0);
+
+ /* Two real constants can be compared explicitly. */
+ else if (TREE_CODE (arg0) == REAL_CST && TREE_CODE (arg1) == REAL_CST)
+ {
+ /* If either operand is a NaN, the result is false with two
+ exceptions: First, an NE_EXPR is true on NaNs, but that case
+ is already handled correctly since we will be inverting the
+ result for NE_EXPR. Second, if we had inverted a LE_EXPR
+ or a GE_EXPR into a LT_EXPR, we must return true so that it
+ will be inverted into false. */
+
+ if (REAL_VALUE_ISNAN (TREE_REAL_CST (arg0))
+ || REAL_VALUE_ISNAN (TREE_REAL_CST (arg1)))
+ t1 = build_int_2 (invert && code == LT_EXPR, 0);
+
+ else if (code == EQ_EXPR)
+ t1 = build_int_2 (REAL_VALUES_EQUAL (TREE_REAL_CST (arg0),
+ TREE_REAL_CST (arg1)),
+ 0);
+ else
+ t1 = build_int_2 (REAL_VALUES_LESS (TREE_REAL_CST (arg0),
+ TREE_REAL_CST (arg1)),
+ 0);
+ }
+
+ if (t1 == NULL_TREE)
+ return t;
+
+ if (invert)
+ TREE_INT_CST_LOW (t1) ^= 1;
+
+ TREE_TYPE (t1) = type;
+ return t1;
+
+ case COND_EXPR:
+ /* Pedantic ANSI C says that a conditional expression is never an lvalue,
+ so all simple results must be passed through pedantic_non_lvalue. */
+ if (TREE_CODE (arg0) == INTEGER_CST)
+ return pedantic_non_lvalue
+ (TREE_OPERAND (t, (integer_zerop (arg0) ? 2 : 1)));
+ else if (operand_equal_p (arg1, TREE_OPERAND (expr, 2), 0))
+ return pedantic_non_lvalue (omit_one_operand (type, arg1, arg0));
+
+ /* If the second operand is zero, invert the comparison and swap
+ the second and third operands. Likewise if the second operand
+ is constant and the third is not or if the third operand is
+ equivalent to the first operand of the comparison. */
+
+ if (integer_zerop (arg1)
+ || (TREE_CONSTANT (arg1) && ! TREE_CONSTANT (TREE_OPERAND (t, 2)))
+ || (TREE_CODE_CLASS (TREE_CODE (arg0)) == '<'
+ && operand_equal_for_comparison_p (TREE_OPERAND (arg0, 0),
+ TREE_OPERAND (t, 2),
+ TREE_OPERAND (arg0, 1))))
+ {
+ /* See if this can be inverted. If it can't, possibly because
+ it was a floating-point inequality comparison, don't do
+ anything. */
+ tem = invert_truthvalue (arg0);
+
+ if (TREE_CODE (tem) != TRUTH_NOT_EXPR)
+ {
+ arg0 = TREE_OPERAND (t, 0) = tem;
+ TREE_OPERAND (t, 1) = TREE_OPERAND (t, 2);
+ TREE_OPERAND (t, 2) = arg1;
+ arg1 = TREE_OPERAND (t, 1);
+ }
+ }
+
+ /* If we have A op B ? A : C, we may be able to convert this to a
+ simpler expression, depending on the operation and the values
+ of B and C. IEEE floating point prevents this though,
+ because A or B might be -0.0 or a NaN. */
+
+ if (TREE_CODE_CLASS (TREE_CODE (arg0)) == '<'
+ && (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+ || ! FLOAT_TYPE_P (TREE_TYPE (TREE_OPERAND (arg0, 0)))
+ || flag_fast_math)
+ && operand_equal_for_comparison_p (TREE_OPERAND (arg0, 0),
+ arg1, TREE_OPERAND (arg0, 1)))
+ {
+ tree arg2 = TREE_OPERAND (t, 2);
+ enum tree_code comp_code = TREE_CODE (arg0);
+
+ /* If we have A op 0 ? A : -A, this is A, -A, abs (A), or abs (-A),
+ depending on the comparison operation. */
+ if (integer_zerop (TREE_OPERAND (arg0, 1))
+ && TREE_CODE (arg2) == NEGATE_EXPR
+ && operand_equal_p (TREE_OPERAND (arg2, 0), arg1, 0))
+ switch (comp_code)
+ {
+ case EQ_EXPR:
+ return pedantic_non_lvalue
+ (fold (build1 (NEGATE_EXPR, type, arg1)));
+ case NE_EXPR:
+ return pedantic_non_lvalue (convert (type, arg1));
+ case GE_EXPR:
+ case GT_EXPR:
+ return pedantic_non_lvalue
+ (fold (build1 (ABS_EXPR, type, arg1)));
+ case LE_EXPR:
+ case LT_EXPR:
+ return pedantic_non_lvalue
+ (fold (build1 (NEGATE_EXPR, type,
+ fold (build1 (ABS_EXPR, type, arg1)))));
+ }
+
+ /* If this is A != 0 ? A : 0, this is simply A. For ==, it is
+ always zero. */
+
+ if (integer_zerop (TREE_OPERAND (arg0, 1)) && integer_zerop (arg2))
+ {
+ if (comp_code == NE_EXPR)
+ return pedantic_non_lvalue (convert (type, arg1));
+ else if (comp_code == EQ_EXPR)
+ return pedantic_non_lvalue (convert (type, integer_zero_node));
+ }
+
+ /* If this is A op B ? A : B, this is either A, B, min (A, B),
+ or max (A, B), depending on the operation. */
+
+ if (operand_equal_for_comparison_p (TREE_OPERAND (arg0, 1),
+ arg2, TREE_OPERAND (arg0, 0)))
+ switch (comp_code)
+ {
+ case EQ_EXPR:
+ return pedantic_non_lvalue (convert (type, arg2));
+ case NE_EXPR:
+ return pedantic_non_lvalue (convert (type, arg1));
+ case LE_EXPR:
+ case LT_EXPR:
+ return pedantic_non_lvalue
+ (fold (build (MIN_EXPR, type, arg1, arg2)));
+ case GE_EXPR:
+ case GT_EXPR:
+ return pedantic_non_lvalue
+ (fold (build (MAX_EXPR, type, arg1, arg2)));
+ }
+
+ /* If this is A op C1 ? A : C2 with C1 and C2 constant integers,
+ we might still be able to simplify this. For example,
+ if C1 is one less or one more than C2, this might have started
+ out as a MIN or MAX and been transformed by this function.
+ Only good for INTEGER_TYPEs, because we need TYPE_MAX_VALUE. */
+
+ if (INTEGRAL_TYPE_P (type)
+ && TREE_CODE (TREE_OPERAND (arg0, 1)) == INTEGER_CST
+ && TREE_CODE (arg2) == INTEGER_CST)
+ switch (comp_code)
+ {
+ case EQ_EXPR:
+ /* We can replace A with C1 in this case. */
+ arg1 = TREE_OPERAND (t, 1)
+ = convert (type, TREE_OPERAND (arg0, 1));
+ break;
+
+ case LT_EXPR:
+ /* If C1 is C2 + 1, this is min(A, C2). */
+ if (! operand_equal_p (arg2, TYPE_MAX_VALUE (type), 1)
+ && operand_equal_p (TREE_OPERAND (arg0, 1),
+ const_binop (PLUS_EXPR, arg2,
+ integer_one_node, 0), 1))
+ return pedantic_non_lvalue
+ (fold (build (MIN_EXPR, type, arg1, arg2)));
+ break;
+
+ case LE_EXPR:
+ /* If C1 is C2 - 1, this is min(A, C2). */
+ if (! operand_equal_p (arg2, TYPE_MIN_VALUE (type), 1)
+ && operand_equal_p (TREE_OPERAND (arg0, 1),
+ const_binop (MINUS_EXPR, arg2,
+ integer_one_node, 0), 1))
+ return pedantic_non_lvalue
+ (fold (build (MIN_EXPR, type, arg1, arg2)));
+ break;
+
+ case GT_EXPR:
+ /* If C1 is C2 - 1, this is max(A, C2). */
+ if (! operand_equal_p (arg2, TYPE_MIN_VALUE (type), 1)
+ && operand_equal_p (TREE_OPERAND (arg0, 1),
+ const_binop (MINUS_EXPR, arg2,
+ integer_one_node, 0), 1))
+ return pedantic_non_lvalue
+ (fold (build (MAX_EXPR, type, arg1, arg2)));
+ break;
+
+ case GE_EXPR:
+ /* If C1 is C2 + 1, this is max(A, C2). */
+ if (! operand_equal_p (arg2, TYPE_MAX_VALUE (type), 1)
+ && operand_equal_p (TREE_OPERAND (arg0, 1),
+ const_binop (PLUS_EXPR, arg2,
+ integer_one_node, 0), 1))
+ return pedantic_non_lvalue
+ (fold (build (MAX_EXPR, type, arg1, arg2)));
+ break;
+ }
+ }
+
+ /* Convert A ? 1 : 0 to simply A. */
+ if (integer_onep (TREE_OPERAND (t, 1))
+ && integer_zerop (TREE_OPERAND (t, 2))
+ /* If we try to convert TREE_OPERAND (t, 0) to our type, the
+ call to fold will try to move the conversion inside
+ a COND, which will recurse. In that case, the COND_EXPR
+ is probably the best choice, so leave it alone. */
+ && type == TREE_TYPE (arg0))
+ return pedantic_non_lvalue (arg0);
+
+
+ /* Look for expressions of the form A & 2 ? 2 : 0. The result of this
+ operation is simply A & 2. */
+
+ if (integer_zerop (TREE_OPERAND (t, 2))
+ && TREE_CODE (arg0) == NE_EXPR
+ && integer_zerop (TREE_OPERAND (arg0, 1))
+ && integer_pow2p (arg1)
+ && TREE_CODE (TREE_OPERAND (arg0, 0)) == BIT_AND_EXPR
+ && operand_equal_p (TREE_OPERAND (TREE_OPERAND (arg0, 0), 1),
+ arg1, 1))
+ return pedantic_non_lvalue (convert (type, TREE_OPERAND (arg0, 0)));
+
+ return t;
+
+ case COMPOUND_EXPR:
+ /* When pedantic, a compound expression can be neither an lvalue
+ nor an integer constant expression. */
+ if (TREE_SIDE_EFFECTS (arg0) || pedantic)
+ return t;
+ /* Don't let (0, 0) be null pointer constant. */
+ if (integer_zerop (arg1))
+ return non_lvalue (arg1);
+ return arg1;
+
+ case COMPLEX_EXPR:
+ if (wins)
+ return build_complex (arg0, arg1);
+ return t;
+
+ case REALPART_EXPR:
+ if (TREE_CODE (TREE_TYPE (arg0)) != COMPLEX_TYPE)
+ return t;
+ else if (TREE_CODE (arg0) == COMPLEX_EXPR)
+ return omit_one_operand (type, TREE_OPERAND (arg0, 0),
+ TREE_OPERAND (arg0, 1));
+ else if (TREE_CODE (arg0) == COMPLEX_CST)
+ return TREE_REALPART (arg0);
+ else if (TREE_CODE (arg0) == PLUS_EXPR || TREE_CODE (arg0) == MINUS_EXPR)
+ return fold (build (TREE_CODE (arg0), type,
+ fold (build1 (REALPART_EXPR, type,
+ TREE_OPERAND (arg0, 0))),
+ fold (build1 (REALPART_EXPR,
+ type, TREE_OPERAND (arg0, 1)))));
+ return t;
+
+ case IMAGPART_EXPR:
+ if (TREE_CODE (TREE_TYPE (arg0)) != COMPLEX_TYPE)
+ return convert (type, integer_zero_node);
+ else if (TREE_CODE (arg0) == COMPLEX_EXPR)
+ return omit_one_operand (type, TREE_OPERAND (arg0, 1),
+ TREE_OPERAND (arg0, 0));
+ else if (TREE_CODE (arg0) == COMPLEX_CST)
+ return TREE_IMAGPART (arg0);
+ else if (TREE_CODE (arg0) == PLUS_EXPR || TREE_CODE (arg0) == MINUS_EXPR)
+ return fold (build (TREE_CODE (arg0), type,
+ fold (build1 (IMAGPART_EXPR, type,
+ TREE_OPERAND (arg0, 0))),
+ fold (build1 (IMAGPART_EXPR, type,
+ TREE_OPERAND (arg0, 1)))));
+ return t;
+
+ default:
+ return t;
+ } /* switch (code) */
+}
diff --git a/gnu/usr.bin/cc/cc_int/function.c b/gnu/usr.bin/cc/cc_int/function.c
new file mode 100644
index 0000000..9f33396
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/function.c
@@ -0,0 +1,5496 @@
+/* Expands front end tree to back end RTL for GNU C-Compiler
+ Copyright (C) 1987, 88, 89, 91, 92, 93, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file handles the generation of rtl code from tree structure
+ at the level of the function as a whole.
+ It creates the rtl expressions for parameters and auto variables
+ and has full responsibility for allocating stack slots.
+
+ `expand_function_start' is called at the beginning of a function,
+ before the function body is parsed, and `expand_function_end' is
+ called after parsing the body.
+
+ Call `assign_stack_local' to allocate a stack slot for a local variable.
+ This is usually done during the RTL generation for the function body,
+ but it can also be done in the reload pass when a pseudo-register does
+ not get a hard register.
+
+ Call `put_var_into_stack' when you learn, belatedly, that a variable
+ previously given a pseudo-register must in fact go in the stack.
+ This function changes the DECL_RTL to be a stack slot instead of a reg
+ then scans all the RTL instructions so far generated to correct them. */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#include "rtl.h"
+#include "tree.h"
+#include "flags.h"
+#include "function.h"
+#include "insn-flags.h"
+#include "expr.h"
+#include "insn-codes.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "insn-config.h"
+#include "recog.h"
+#include "output.h"
+#include "basic-block.h"
+#include "obstack.h"
+#include "bytecode.h"
+
+/* Some systems use __main in a way incompatible with its use in gcc, in these
+ cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to
+ give the same symbol without quotes for an alternative entry point. You
+ must define both, or niether. */
+#ifndef NAME__MAIN
+#define NAME__MAIN "__main"
+#define SYMBOL__MAIN __main
+#endif
+
+/* Round a value to the lowest integer less than it that is a multiple of
+ the required alignment. Avoid using division in case the value is
+ negative. Assume the alignment is a power of two. */
+#define FLOOR_ROUND(VALUE,ALIGN) ((VALUE) & ~((ALIGN) - 1))
+
+/* Similar, but round to the next highest integer that meets the
+ alignment. */
+#define CEIL_ROUND(VALUE,ALIGN) (((VALUE) + (ALIGN) - 1) & ~((ALIGN)- 1))
+
+/* NEED_SEPARATE_AP means that we cannot derive ap from the value of fp
+ during rtl generation. If they are different register numbers, this is
+ always true. It may also be true if
+ FIRST_PARM_OFFSET - STARTING_FRAME_OFFSET is not a constant during rtl
+ generation. See fix_lexical_addr for details. */
+
+#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
+#define NEED_SEPARATE_AP
+#endif
+
+/* Number of bytes of args popped by function being compiled on its return.
+ Zero if no bytes are to be popped.
+ May affect compilation of return insn or of function epilogue. */
+
+int current_function_pops_args;
+
+/* Nonzero if function being compiled needs to be given an address
+ where the value should be stored. */
+
+int current_function_returns_struct;
+
+/* Nonzero if function being compiled needs to
+ return the address of where it has put a structure value. */
+
+int current_function_returns_pcc_struct;
+
+/* Nonzero if function being compiled needs to be passed a static chain. */
+
+int current_function_needs_context;
+
+/* Nonzero if function being compiled can call setjmp. */
+
+int current_function_calls_setjmp;
+
+/* Nonzero if function being compiled can call longjmp. */
+
+int current_function_calls_longjmp;
+
+/* Nonzero if function being compiled receives nonlocal gotos
+ from nested functions. */
+
+int current_function_has_nonlocal_label;
+
+/* Nonzero if function being compiled has nonlocal gotos to parent
+ function. */
+
+int current_function_has_nonlocal_goto;
+
+/* Nonzero if function being compiled contains nested functions. */
+
+int current_function_contains_functions;
+
+/* Nonzero if function being compiled can call alloca,
+ either as a subroutine or builtin. */
+
+int current_function_calls_alloca;
+
+/* Nonzero if the current function returns a pointer type */
+
+int current_function_returns_pointer;
+
+/* If some insns can be deferred to the delay slots of the epilogue, the
+ delay list for them is recorded here. */
+
+rtx current_function_epilogue_delay_list;
+
+/* If function's args have a fixed size, this is that size, in bytes.
+ Otherwise, it is -1.
+ May affect compilation of return insn or of function epilogue. */
+
+int current_function_args_size;
+
+/* # bytes the prologue should push and pretend that the caller pushed them.
+ The prologue must do this, but only if parms can be passed in registers. */
+
+int current_function_pretend_args_size;
+
+/* # of bytes of outgoing arguments required to be pushed by the prologue.
+ If this is non-zero, it means that ACCUMULATE_OUTGOING_ARGS was defined
+ and no stack adjusts will be done on function calls. */
+
+int current_function_outgoing_args_size;
+
+/* This is the offset from the arg pointer to the place where the first
+ anonymous arg can be found, if there is one. */
+
+rtx current_function_arg_offset_rtx;
+
+/* Nonzero if current function uses varargs.h or equivalent.
+ Zero for functions that use stdarg.h. */
+
+int current_function_varargs;
+
+/* Quantities of various kinds of registers
+ used for the current function's args. */
+
+CUMULATIVE_ARGS current_function_args_info;
+
+/* Name of function now being compiled. */
+
+char *current_function_name;
+
+/* If non-zero, an RTL expression for that location at which the current
+ function returns its result. Always equal to
+ DECL_RTL (DECL_RESULT (current_function_decl)), but provided
+ independently of the tree structures. */
+
+rtx current_function_return_rtx;
+
+/* Nonzero if the current function uses the constant pool. */
+
+int current_function_uses_const_pool;
+
+/* Nonzero if the current function uses pic_offset_table_rtx. */
+int current_function_uses_pic_offset_table;
+
+/* The arg pointer hard register, or the pseudo into which it was copied. */
+rtx current_function_internal_arg_pointer;
+
+/* The FUNCTION_DECL for an inline function currently being expanded. */
+tree inline_function_decl;
+
+/* Number of function calls seen so far in current function. */
+
+int function_call_count;
+
+/* List (chain of TREE_LIST) of LABEL_DECLs for all nonlocal labels
+ (labels to which there can be nonlocal gotos from nested functions)
+ in this function. */
+
+tree nonlocal_labels;
+
+/* RTX for stack slot that holds the current handler for nonlocal gotos.
+ Zero when function does not have nonlocal labels. */
+
+rtx nonlocal_goto_handler_slot;
+
+/* RTX for stack slot that holds the stack pointer value to restore
+ for a nonlocal goto.
+ Zero when function does not have nonlocal labels. */
+
+rtx nonlocal_goto_stack_level;
+
+/* Label that will go on parm cleanup code, if any.
+ Jumping to this label runs cleanup code for parameters, if
+ such code must be run. Following this code is the logical return label. */
+
+rtx cleanup_label;
+
+/* Label that will go on function epilogue.
+ Jumping to this label serves as a "return" instruction
+ on machines which require execution of the epilogue on all returns. */
+
+rtx return_label;
+
+/* List (chain of EXPR_LISTs) of pseudo-regs of SAVE_EXPRs.
+ So we can mark them all live at the end of the function, if nonopt. */
+rtx save_expr_regs;
+
+/* List (chain of EXPR_LISTs) of all stack slots in this function.
+ Made for the sake of unshare_all_rtl. */
+rtx stack_slot_list;
+
+/* Chain of all RTL_EXPRs that have insns in them. */
+tree rtl_expr_chain;
+
+/* Label to jump back to for tail recursion, or 0 if we have
+ not yet needed one for this function. */
+rtx tail_recursion_label;
+
+/* Place after which to insert the tail_recursion_label if we need one. */
+rtx tail_recursion_reentry;
+
+/* Location at which to save the argument pointer if it will need to be
+ referenced. There are two cases where this is done: if nonlocal gotos
+ exist, or if vars stored at an offset from the argument pointer will be
+ needed by inner routines. */
+
+rtx arg_pointer_save_area;
+
+/* Offset to end of allocated area of stack frame.
+ If stack grows down, this is the address of the last stack slot allocated.
+ If stack grows up, this is the address for the next slot. */
+int frame_offset;
+
+/* List (chain of TREE_LISTs) of static chains for containing functions.
+ Each link has a FUNCTION_DECL in the TREE_PURPOSE and a reg rtx
+ in an RTL_EXPR in the TREE_VALUE. */
+static tree context_display;
+
+/* List (chain of TREE_LISTs) of trampolines for nested functions.
+ The trampoline sets up the static chain and jumps to the function.
+ We supply the trampoline's address when the function's address is requested.
+
+ Each link has a FUNCTION_DECL in the TREE_PURPOSE and a reg rtx
+ in an RTL_EXPR in the TREE_VALUE. */
+static tree trampoline_list;
+
+/* Insn after which register parms and SAVE_EXPRs are born, if nonopt. */
+static rtx parm_birth_insn;
+
+#if 0
+/* Nonzero if a stack slot has been generated whose address is not
+ actually valid. It means that the generated rtl must all be scanned
+ to detect and correct the invalid addresses where they occur. */
+static int invalid_stack_slot;
+#endif
+
+/* Last insn of those whose job was to put parms into their nominal homes. */
+static rtx last_parm_insn;
+
+/* 1 + last pseudo register number used for loading a copy
+ of a parameter of this function. */
+static int max_parm_reg;
+
+/* Vector indexed by REGNO, containing location on stack in which
+ to put the parm which is nominally in pseudo register REGNO,
+ if we discover that that parm must go in the stack. */
+static rtx *parm_reg_stack_loc;
+
+#if 0 /* Turned off because 0 seems to work just as well. */
+/* Cleanup lists are required for binding levels regardless of whether
+ that binding level has cleanups or not. This node serves as the
+ cleanup list whenever an empty list is required. */
+static tree empty_cleanup_list;
+#endif
+
+/* Nonzero once virtual register instantiation has been done.
+ assign_stack_local uses frame_pointer_rtx when this is nonzero. */
+static int virtuals_instantiated;
+
+/* These variables hold pointers to functions to
+ save and restore machine-specific data,
+ in push_function_context and pop_function_context. */
+void (*save_machine_status) ();
+void (*restore_machine_status) ();
+
+/* Nonzero if we need to distinguish between the return value of this function
+ and the return value of a function called by this function. This helps
+ integrate.c */
+
+extern int rtx_equal_function_value_matters;
+extern tree sequence_rtl_expr;
+extern tree bc_runtime_type_code ();
+extern rtx bc_build_calldesc ();
+extern char *bc_emit_trampoline ();
+extern char *bc_end_function ();
+
+/* In order to evaluate some expressions, such as function calls returning
+ structures in memory, we need to temporarily allocate stack locations.
+ We record each allocated temporary in the following structure.
+
+ Associated with each temporary slot is a nesting level. When we pop up
+ one level, all temporaries associated with the previous level are freed.
+ Normally, all temporaries are freed after the execution of the statement
+ in which they were created. However, if we are inside a ({...}) grouping,
+ the result may be in a temporary and hence must be preserved. If the
+ result could be in a temporary, we preserve it if we can determine which
+ one it is in. If we cannot determine which temporary may contain the
+ result, all temporaries are preserved. A temporary is preserved by
+ pretending it was allocated at the previous nesting level.
+
+ Automatic variables are also assigned temporary slots, at the nesting
+ level where they are defined. They are marked a "kept" so that
+ free_temp_slots will not free them. */
+
+struct temp_slot
+{
+ /* Points to next temporary slot. */
+ struct temp_slot *next;
+ /* The rtx to used to reference the slot. */
+ rtx slot;
+ /* The rtx used to represent the address if not the address of the
+ slot above. May be an EXPR_LIST if multiple addresses exist. */
+ rtx address;
+ /* The size, in units, of the slot. */
+ int size;
+ /* The value of `sequence_rtl_expr' when this temporary is allocated. */
+ tree rtl_expr;
+ /* Non-zero if this temporary is currently in use. */
+ char in_use;
+ /* Nesting level at which this slot is being used. */
+ int level;
+ /* Non-zero if this should survive a call to free_temp_slots. */
+ int keep;
+};
+
+/* List of all temporaries allocated, both available and in use. */
+
+struct temp_slot *temp_slots;
+
+/* Current nesting level for temporaries. */
+
+int temp_slot_level;
+
+/* The FUNCTION_DECL node for the current function. */
+static tree this_function_decl;
+
+/* Callinfo pointer for the current function. */
+static rtx this_function_callinfo;
+
+/* The label in the bytecode file of this function's actual bytecode.
+ Not an rtx. */
+static char *this_function_bytecode;
+
+/* The call description vector for the current function. */
+static rtx this_function_calldesc;
+
+/* Size of the local variables allocated for the current function. */
+int local_vars_size;
+
+/* Current depth of the bytecode evaluation stack. */
+int stack_depth;
+
+/* Maximum depth of the evaluation stack in this function. */
+int max_stack_depth;
+
+/* Current depth in statement expressions. */
+static int stmt_expr_depth;
+
+/* This structure is used to record MEMs or pseudos used to replace VAR, any
+ SUBREGs of VAR, and any MEMs containing VAR as an address. We need to
+ maintain this list in case two operands of an insn were required to match;
+ in that case we must ensure we use the same replacement. */
+
+struct fixup_replacement
+{
+ rtx old;
+ rtx new;
+ struct fixup_replacement *next;
+};
+
+/* Forward declarations. */
+
+static struct temp_slot *find_temp_slot_from_address PROTO((rtx));
+static void put_reg_into_stack PROTO((struct function *, rtx, tree,
+ enum machine_mode, enum machine_mode));
+static void fixup_var_refs PROTO((rtx, enum machine_mode, int));
+static struct fixup_replacement
+ *find_fixup_replacement PROTO((struct fixup_replacement **, rtx));
+static void fixup_var_refs_insns PROTO((rtx, enum machine_mode, int,
+ rtx, int));
+static void fixup_var_refs_1 PROTO((rtx, enum machine_mode, rtx *, rtx,
+ struct fixup_replacement **));
+static rtx fixup_memory_subreg PROTO((rtx, rtx, int));
+static rtx walk_fixup_memory_subreg PROTO((rtx, rtx, int));
+static rtx fixup_stack_1 PROTO((rtx, rtx));
+static void optimize_bit_field PROTO((rtx, rtx, rtx *));
+static void instantiate_decls PROTO((tree, int));
+static void instantiate_decls_1 PROTO((tree, int));
+static void instantiate_decl PROTO((rtx, int, int));
+static int instantiate_virtual_regs_1 PROTO((rtx *, rtx, int));
+static void delete_handlers PROTO((void));
+static void pad_to_arg_alignment PROTO((struct args_size *, int));
+static void pad_below PROTO((struct args_size *, enum machine_mode,
+ tree));
+static tree round_down PROTO((tree, int));
+static rtx round_trampoline_addr PROTO((rtx));
+static tree blocks_nreverse PROTO((tree));
+static int all_blocks PROTO((tree, tree *));
+static int *record_insns PROTO((rtx));
+static int contains PROTO((rtx, int *));
+
+/* Pointer to chain of `struct function' for containing functions. */
+struct function *outer_function_chain;
+
+/* Given a function decl for a containing function,
+ return the `struct function' for it. */
+
+struct function *
+find_function_data (decl)
+ tree decl;
+{
+ struct function *p;
+ for (p = outer_function_chain; p; p = p->next)
+ if (p->decl == decl)
+ return p;
+ abort ();
+}
+
+/* Save the current context for compilation of a nested function.
+ This is called from language-specific code.
+ The caller is responsible for saving any language-specific status,
+ since this function knows only about language-independent variables. */
+
+void
+push_function_context ()
+{
+ struct function *p = (struct function *) xmalloc (sizeof (struct function));
+
+ p->next = outer_function_chain;
+ outer_function_chain = p;
+
+ p->name = current_function_name;
+ p->decl = current_function_decl;
+ p->pops_args = current_function_pops_args;
+ p->returns_struct = current_function_returns_struct;
+ p->returns_pcc_struct = current_function_returns_pcc_struct;
+ p->needs_context = current_function_needs_context;
+ p->calls_setjmp = current_function_calls_setjmp;
+ p->calls_longjmp = current_function_calls_longjmp;
+ p->calls_alloca = current_function_calls_alloca;
+ p->has_nonlocal_label = current_function_has_nonlocal_label;
+ p->has_nonlocal_goto = current_function_has_nonlocal_goto;
+ p->args_size = current_function_args_size;
+ p->pretend_args_size = current_function_pretend_args_size;
+ p->arg_offset_rtx = current_function_arg_offset_rtx;
+ p->varargs = current_function_varargs;
+ p->uses_const_pool = current_function_uses_const_pool;
+ p->uses_pic_offset_table = current_function_uses_pic_offset_table;
+ p->internal_arg_pointer = current_function_internal_arg_pointer;
+ p->max_parm_reg = max_parm_reg;
+ p->parm_reg_stack_loc = parm_reg_stack_loc;
+ p->outgoing_args_size = current_function_outgoing_args_size;
+ p->return_rtx = current_function_return_rtx;
+ p->nonlocal_goto_handler_slot = nonlocal_goto_handler_slot;
+ p->nonlocal_goto_stack_level = nonlocal_goto_stack_level;
+ p->nonlocal_labels = nonlocal_labels;
+ p->cleanup_label = cleanup_label;
+ p->return_label = return_label;
+ p->save_expr_regs = save_expr_regs;
+ p->stack_slot_list = stack_slot_list;
+ p->parm_birth_insn = parm_birth_insn;
+ p->frame_offset = frame_offset;
+ p->tail_recursion_label = tail_recursion_label;
+ p->tail_recursion_reentry = tail_recursion_reentry;
+ p->arg_pointer_save_area = arg_pointer_save_area;
+ p->rtl_expr_chain = rtl_expr_chain;
+ p->last_parm_insn = last_parm_insn;
+ p->context_display = context_display;
+ p->trampoline_list = trampoline_list;
+ p->function_call_count = function_call_count;
+ p->temp_slots = temp_slots;
+ p->temp_slot_level = temp_slot_level;
+ p->fixup_var_refs_queue = 0;
+ p->epilogue_delay_list = current_function_epilogue_delay_list;
+
+ save_tree_status (p);
+ save_storage_status (p);
+ save_emit_status (p);
+ init_emit ();
+ save_expr_status (p);
+ save_stmt_status (p);
+ save_varasm_status (p);
+
+ if (save_machine_status)
+ (*save_machine_status) (p);
+}
+
+/* Restore the last saved context, at the end of a nested function.
+ This function is called from language-specific code. */
+
+void
+pop_function_context ()
+{
+ struct function *p = outer_function_chain;
+
+ outer_function_chain = p->next;
+
+ current_function_name = p->name;
+ current_function_decl = p->decl;
+ current_function_pops_args = p->pops_args;
+ current_function_returns_struct = p->returns_struct;
+ current_function_returns_pcc_struct = p->returns_pcc_struct;
+ current_function_needs_context = p->needs_context;
+ current_function_calls_setjmp = p->calls_setjmp;
+ current_function_calls_longjmp = p->calls_longjmp;
+ current_function_calls_alloca = p->calls_alloca;
+ current_function_has_nonlocal_label = p->has_nonlocal_label;
+ current_function_has_nonlocal_goto = p->has_nonlocal_goto;
+ current_function_contains_functions = 1;
+ current_function_args_size = p->args_size;
+ current_function_pretend_args_size = p->pretend_args_size;
+ current_function_arg_offset_rtx = p->arg_offset_rtx;
+ current_function_varargs = p->varargs;
+ current_function_uses_const_pool = p->uses_const_pool;
+ current_function_uses_pic_offset_table = p->uses_pic_offset_table;
+ current_function_internal_arg_pointer = p->internal_arg_pointer;
+ max_parm_reg = p->max_parm_reg;
+ parm_reg_stack_loc = p->parm_reg_stack_loc;
+ current_function_outgoing_args_size = p->outgoing_args_size;
+ current_function_return_rtx = p->return_rtx;
+ nonlocal_goto_handler_slot = p->nonlocal_goto_handler_slot;
+ nonlocal_goto_stack_level = p->nonlocal_goto_stack_level;
+ nonlocal_labels = p->nonlocal_labels;
+ cleanup_label = p->cleanup_label;
+ return_label = p->return_label;
+ save_expr_regs = p->save_expr_regs;
+ stack_slot_list = p->stack_slot_list;
+ parm_birth_insn = p->parm_birth_insn;
+ frame_offset = p->frame_offset;
+ tail_recursion_label = p->tail_recursion_label;
+ tail_recursion_reentry = p->tail_recursion_reentry;
+ arg_pointer_save_area = p->arg_pointer_save_area;
+ rtl_expr_chain = p->rtl_expr_chain;
+ last_parm_insn = p->last_parm_insn;
+ context_display = p->context_display;
+ trampoline_list = p->trampoline_list;
+ function_call_count = p->function_call_count;
+ temp_slots = p->temp_slots;
+ temp_slot_level = p->temp_slot_level;
+ current_function_epilogue_delay_list = p->epilogue_delay_list;
+
+ restore_tree_status (p);
+ restore_storage_status (p);
+ restore_expr_status (p);
+ restore_emit_status (p);
+ restore_stmt_status (p);
+ restore_varasm_status (p);
+
+ if (restore_machine_status)
+ (*restore_machine_status) (p);
+
+ /* Finish doing put_var_into_stack for any of our variables
+ which became addressable during the nested function. */
+ {
+ struct var_refs_queue *queue = p->fixup_var_refs_queue;
+ for (; queue; queue = queue->next)
+ fixup_var_refs (queue->modified, queue->promoted_mode, queue->unsignedp);
+ }
+
+ free (p);
+
+ /* Reset variables that have known state during rtx generation. */
+ rtx_equal_function_value_matters = 1;
+ virtuals_instantiated = 0;
+}
+
+/* Allocate fixed slots in the stack frame of the current function. */
+
+/* Return size needed for stack frame based on slots so far allocated.
+ This size counts from zero. It is not rounded to STACK_BOUNDARY;
+ the caller may have to do that. */
+
+int
+get_frame_size ()
+{
+#ifdef FRAME_GROWS_DOWNWARD
+ return -frame_offset;
+#else
+ return frame_offset;
+#endif
+}
+
+/* Allocate a stack slot of SIZE bytes and return a MEM rtx for it
+ with machine mode MODE.
+
+ ALIGN controls the amount of alignment for the address of the slot:
+ 0 means according to MODE,
+ -1 means use BIGGEST_ALIGNMENT and round size to multiple of that,
+ positive specifies alignment boundary in bits.
+
+ We do not round to stack_boundary here. */
+
+rtx
+assign_stack_local (mode, size, align)
+ enum machine_mode mode;
+ int size;
+ int align;
+{
+ register rtx x, addr;
+ int bigend_correction = 0;
+ int alignment;
+
+ if (align == 0)
+ {
+ alignment = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
+ if (mode == BLKmode)
+ alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
+ }
+ else if (align == -1)
+ {
+ alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
+ size = CEIL_ROUND (size, alignment);
+ }
+ else
+ alignment = align / BITS_PER_UNIT;
+
+ /* Round frame offset to that alignment.
+ We must be careful here, since FRAME_OFFSET might be negative and
+ division with a negative dividend isn't as well defined as we might
+ like. So we instead assume that ALIGNMENT is a power of two and
+ use logical operations which are unambiguous. */
+#ifdef FRAME_GROWS_DOWNWARD
+ frame_offset = FLOOR_ROUND (frame_offset, alignment);
+#else
+ frame_offset = CEIL_ROUND (frame_offset, alignment);
+#endif
+
+ /* On a big-endian machine, if we are allocating more space than we will use,
+ use the least significant bytes of those that are allocated. */
+#if BYTES_BIG_ENDIAN
+ if (mode != BLKmode)
+ bigend_correction = size - GET_MODE_SIZE (mode);
+#endif
+
+#ifdef FRAME_GROWS_DOWNWARD
+ frame_offset -= size;
+#endif
+
+ /* If we have already instantiated virtual registers, return the actual
+ address relative to the frame pointer. */
+ if (virtuals_instantiated)
+ addr = plus_constant (frame_pointer_rtx,
+ (frame_offset + bigend_correction
+ + STARTING_FRAME_OFFSET));
+ else
+ addr = plus_constant (virtual_stack_vars_rtx,
+ frame_offset + bigend_correction);
+
+#ifndef FRAME_GROWS_DOWNWARD
+ frame_offset += size;
+#endif
+
+ x = gen_rtx (MEM, mode, addr);
+
+ stack_slot_list = gen_rtx (EXPR_LIST, VOIDmode, x, stack_slot_list);
+
+ return x;
+}
+
+/* Assign a stack slot in a containing function.
+ First three arguments are same as in preceding function.
+ The last argument specifies the function to allocate in. */
+
+rtx
+assign_outer_stack_local (mode, size, align, function)
+ enum machine_mode mode;
+ int size;
+ int align;
+ struct function *function;
+{
+ register rtx x, addr;
+ int bigend_correction = 0;
+ int alignment;
+
+ /* Allocate in the memory associated with the function in whose frame
+ we are assigning. */
+ push_obstacks (function->function_obstack,
+ function->function_maybepermanent_obstack);
+
+ if (align == 0)
+ {
+ alignment = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
+ if (mode == BLKmode)
+ alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
+ }
+ else if (align == -1)
+ {
+ alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
+ size = CEIL_ROUND (size, alignment);
+ }
+ else
+ alignment = align / BITS_PER_UNIT;
+
+ /* Round frame offset to that alignment. */
+#ifdef FRAME_GROWS_DOWNWARD
+ function->frame_offset = FLOOR_ROUND (function->frame_offset, alignment);
+#else
+ function->frame_offset = CEIL_ROUND (function->frame_offset, alignment);
+#endif
+
+ /* On a big-endian machine, if we are allocating more space than we will use,
+ use the least significant bytes of those that are allocated. */
+#if BYTES_BIG_ENDIAN
+ if (mode != BLKmode)
+ bigend_correction = size - GET_MODE_SIZE (mode);
+#endif
+
+#ifdef FRAME_GROWS_DOWNWARD
+ function->frame_offset -= size;
+#endif
+ addr = plus_constant (virtual_stack_vars_rtx,
+ function->frame_offset + bigend_correction);
+#ifndef FRAME_GROWS_DOWNWARD
+ function->frame_offset += size;
+#endif
+
+ x = gen_rtx (MEM, mode, addr);
+
+ function->stack_slot_list
+ = gen_rtx (EXPR_LIST, VOIDmode, x, function->stack_slot_list);
+
+ pop_obstacks ();
+
+ return x;
+}
+
+/* Allocate a temporary stack slot and record it for possible later
+ reuse.
+
+ MODE is the machine mode to be given to the returned rtx.
+
+ SIZE is the size in units of the space required. We do no rounding here
+ since assign_stack_local will do any required rounding.
+
+ KEEP is 1 if this slot is to be retained after a call to
+ free_temp_slots. Automatic variables for a block are allocated
+ with this flag. KEEP is 2, if we allocate a longer term temporary,
+ whose lifetime is controlled by CLEANUP_POINT_EXPRs. */
+
+rtx
+assign_stack_temp (mode, size, keep)
+ enum machine_mode mode;
+ int size;
+ int keep;
+{
+ struct temp_slot *p, *best_p = 0;
+
+ /* First try to find an available, already-allocated temporary that is the
+ exact size we require. */
+ for (p = temp_slots; p; p = p->next)
+ if (p->size == size && GET_MODE (p->slot) == mode && ! p->in_use)
+ break;
+
+ /* If we didn't find, one, try one that is larger than what we want. We
+ find the smallest such. */
+ if (p == 0)
+ for (p = temp_slots; p; p = p->next)
+ if (p->size > size && GET_MODE (p->slot) == mode && ! p->in_use
+ && (best_p == 0 || best_p->size > p->size))
+ best_p = p;
+
+ /* Make our best, if any, the one to use. */
+ if (best_p)
+ {
+ /* If there are enough aligned bytes left over, make them into a new
+ temp_slot so that the extra bytes don't get wasted. Do this only
+ for BLKmode slots, so that we can be sure of the alignment. */
+ if (GET_MODE (best_p->slot) == BLKmode)
+ {
+ int alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
+ int rounded_size = CEIL_ROUND (size, alignment);
+
+ if (best_p->size - rounded_size >= alignment)
+ {
+ p = (struct temp_slot *) oballoc (sizeof (struct temp_slot));
+ p->in_use = 0;
+ p->size = best_p->size - rounded_size;
+ p->slot = gen_rtx (MEM, BLKmode,
+ plus_constant (XEXP (best_p->slot, 0),
+ rounded_size));
+ p->address = 0;
+ p->next = temp_slots;
+ temp_slots = p;
+
+ stack_slot_list = gen_rtx (EXPR_LIST, VOIDmode, p->slot,
+ stack_slot_list);
+
+ best_p->size = rounded_size;
+ }
+ }
+
+ p = best_p;
+ }
+
+ /* If we still didn't find one, make a new temporary. */
+ if (p == 0)
+ {
+ p = (struct temp_slot *) oballoc (sizeof (struct temp_slot));
+ p->size = size;
+ /* If the temp slot mode doesn't indicate the alignment,
+ use the largest possible, so no one will be disappointed. */
+ p->slot = assign_stack_local (mode, size, mode == BLKmode ? -1 : 0);
+ p->address = 0;
+ p->next = temp_slots;
+ temp_slots = p;
+ }
+
+ p->in_use = 1;
+ p->rtl_expr = sequence_rtl_expr;
+ if (keep == 2)
+ {
+ p->level = target_temp_slot_level;
+ p->keep = 0;
+ }
+ else
+ {
+ p->level = temp_slot_level;
+ p->keep = keep;
+ }
+ return p->slot;
+}
+
+/* Combine temporary stack slots which are adjacent on the stack.
+
+ This allows for better use of already allocated stack space. This is only
+ done for BLKmode slots because we can be sure that we won't have alignment
+ problems in this case. */
+
+void
+combine_temp_slots ()
+{
+ struct temp_slot *p, *q;
+ struct temp_slot *prev_p, *prev_q;
+ /* Determine where to free back to after this function. */
+ rtx free_pointer = rtx_alloc (CONST_INT);
+
+ for (p = temp_slots, prev_p = 0; p; p = prev_p ? prev_p->next : temp_slots)
+ {
+ int delete_p = 0;
+ if (! p->in_use && GET_MODE (p->slot) == BLKmode)
+ for (q = p->next, prev_q = p; q; q = prev_q->next)
+ {
+ int delete_q = 0;
+ if (! q->in_use && GET_MODE (q->slot) == BLKmode)
+ {
+ if (rtx_equal_p (plus_constant (XEXP (p->slot, 0), p->size),
+ XEXP (q->slot, 0)))
+ {
+ /* Q comes after P; combine Q into P. */
+ p->size += q->size;
+ delete_q = 1;
+ }
+ else if (rtx_equal_p (plus_constant (XEXP (q->slot, 0), q->size),
+ XEXP (p->slot, 0)))
+ {
+ /* P comes after Q; combine P into Q. */
+ q->size += p->size;
+ delete_p = 1;
+ break;
+ }
+ }
+ /* Either delete Q or advance past it. */
+ if (delete_q)
+ prev_q->next = q->next;
+ else
+ prev_q = q;
+ }
+ /* Either delete P or advance past it. */
+ if (delete_p)
+ {
+ if (prev_p)
+ prev_p->next = p->next;
+ else
+ temp_slots = p->next;
+ }
+ else
+ prev_p = p;
+ }
+
+ /* Free all the RTL made by plus_constant. */
+ rtx_free (free_pointer);
+}
+
+/* Find the temp slot corresponding to the object at address X. */
+
+static struct temp_slot *
+find_temp_slot_from_address (x)
+ rtx x;
+{
+ struct temp_slot *p;
+ rtx next;
+
+ for (p = temp_slots; p; p = p->next)
+ {
+ if (! p->in_use)
+ continue;
+ else if (XEXP (p->slot, 0) == x
+ || p->address == x)
+ return p;
+
+ else if (p->address != 0 && GET_CODE (p->address) == EXPR_LIST)
+ for (next = p->address; next; next = XEXP (next, 1))
+ if (XEXP (next, 0) == x)
+ return p;
+ }
+
+ return 0;
+}
+
+/* Indicate that NEW is an alternate way of refering to the temp slot
+ that previous was known by OLD. */
+
+void
+update_temp_slot_address (old, new)
+ rtx old, new;
+{
+ struct temp_slot *p = find_temp_slot_from_address (old);
+
+ /* If none, return. Else add NEW as an alias. */
+ if (p == 0)
+ return;
+ else if (p->address == 0)
+ p->address = new;
+ else
+ {
+ if (GET_CODE (p->address) != EXPR_LIST)
+ p->address = gen_rtx (EXPR_LIST, VOIDmode, p->address, NULL_RTX);
+
+ p->address = gen_rtx (EXPR_LIST, VOIDmode, new, p->address);
+ }
+}
+
+/* If X could be a reference to a temporary slot, mark that slot as belonging
+ to the to one level higher. If X matched one of our slots, just mark that
+ one. Otherwise, we can't easily predict which it is, so upgrade all of
+ them. Kept slots need not be touched.
+
+ This is called when an ({...}) construct occurs and a statement
+ returns a value in memory. */
+
+void
+preserve_temp_slots (x)
+ rtx x;
+{
+ struct temp_slot *p;
+
+ if (x == 0)
+ return;
+
+ /* If X is a register that is being used as a pointer, see if we have
+ a temporary slot we know it points to. To be consistent with
+ the code below, we really should preserve all non-kept slots
+ if we can't find a match, but that seems to be much too costly. */
+ if (GET_CODE (x) == REG && REGNO_POINTER_FLAG (REGNO (x))
+ && (p = find_temp_slot_from_address (x)) != 0)
+ {
+ p->level--;
+ return;
+ }
+
+ /* If X is not in memory or is at a constant address, it cannot be in
+ a temporary slot. */
+ if (GET_CODE (x) != MEM || CONSTANT_P (XEXP (x, 0)))
+ return;
+
+ /* First see if we can find a match. */
+ p = find_temp_slot_from_address (XEXP (x, 0));
+ if (p != 0)
+ {
+ p->level--;
+ return;
+ }
+
+ /* Otherwise, preserve all non-kept slots at this level. */
+ for (p = temp_slots; p; p = p->next)
+ if (p->in_use && p->level == temp_slot_level && ! p->keep)
+ p->level--;
+}
+
+/* X is the result of an RTL_EXPR. If it is a temporary slot associated
+ with that RTL_EXPR, promote it into a temporary slot at the present
+ level so it will not be freed when we free slots made in the
+ RTL_EXPR. */
+
+void
+preserve_rtl_expr_result (x)
+ rtx x;
+{
+ struct temp_slot *p;
+
+ /* If X is not in memory or is at a constant address, it cannot be in
+ a temporary slot. */
+ if (x == 0 || GET_CODE (x) != MEM || CONSTANT_P (XEXP (x, 0)))
+ return;
+
+ /* If we can find a match, move it to our level. */
+ for (p = temp_slots; p; p = p->next)
+ if (p->in_use && rtx_equal_p (x, p->slot))
+ {
+ p->level = temp_slot_level;
+ p->rtl_expr = 0;
+ return;
+ }
+
+ return;
+}
+
+/* Free all temporaries used so far. This is normally called at the end
+ of generating code for a statement. Don't free any temporaries
+ currently in use for an RTL_EXPR that hasn't yet been emitted.
+ We could eventually do better than this since it can be reused while
+ generating the same RTL_EXPR, but this is complex and probably not
+ worthwhile. */
+
+void
+free_temp_slots ()
+{
+ struct temp_slot *p;
+
+ for (p = temp_slots; p; p = p->next)
+ if (p->in_use && p->level == temp_slot_level && ! p->keep
+ && p->rtl_expr == 0)
+ p->in_use = 0;
+
+ combine_temp_slots ();
+}
+
+/* Free all temporary slots used in T, an RTL_EXPR node. */
+
+void
+free_temps_for_rtl_expr (t)
+ tree t;
+{
+ struct temp_slot *p;
+
+ for (p = temp_slots; p; p = p->next)
+ if (p->rtl_expr == t)
+ p->in_use = 0;
+
+ combine_temp_slots ();
+}
+
+/* Push deeper into the nesting level for stack temporaries. */
+
+void
+push_temp_slots ()
+{
+ temp_slot_level++;
+}
+
+/* Pop a temporary nesting level. All slots in use in the current level
+ are freed. */
+
+void
+pop_temp_slots ()
+{
+ struct temp_slot *p;
+
+ for (p = temp_slots; p; p = p->next)
+ if (p->in_use && p->level == temp_slot_level && p->rtl_expr == 0)
+ p->in_use = 0;
+
+ combine_temp_slots ();
+
+ temp_slot_level--;
+}
+
+/* Retroactively move an auto variable from a register to a stack slot.
+ This is done when an address-reference to the variable is seen. */
+
+void
+put_var_into_stack (decl)
+ tree decl;
+{
+ register rtx reg;
+ enum machine_mode promoted_mode, decl_mode;
+ struct function *function = 0;
+ tree context;
+
+ if (output_bytecode)
+ return;
+
+ context = decl_function_context (decl);
+
+ /* Get the current rtl used for this object and it's original mode. */
+ reg = TREE_CODE (decl) == SAVE_EXPR ? SAVE_EXPR_RTL (decl) : DECL_RTL (decl);
+
+ /* No need to do anything if decl has no rtx yet
+ since in that case caller is setting TREE_ADDRESSABLE
+ and a stack slot will be assigned when the rtl is made. */
+ if (reg == 0)
+ return;
+
+ /* Get the declared mode for this object. */
+ decl_mode = (TREE_CODE (decl) == SAVE_EXPR ? TYPE_MODE (TREE_TYPE (decl))
+ : DECL_MODE (decl));
+ /* Get the mode it's actually stored in. */
+ promoted_mode = GET_MODE (reg);
+
+ /* If this variable comes from an outer function,
+ find that function's saved context. */
+ if (context != current_function_decl)
+ for (function = outer_function_chain; function; function = function->next)
+ if (function->decl == context)
+ break;
+
+ /* If this is a variable-size object with a pseudo to address it,
+ put that pseudo into the stack, if the var is nonlocal. */
+ if (DECL_NONLOCAL (decl)
+ && GET_CODE (reg) == MEM
+ && GET_CODE (XEXP (reg, 0)) == REG
+ && REGNO (XEXP (reg, 0)) > LAST_VIRTUAL_REGISTER)
+ {
+ reg = XEXP (reg, 0);
+ decl_mode = promoted_mode = GET_MODE (reg);
+ }
+
+ /* Now we should have a value that resides in one or more pseudo regs. */
+
+ if (GET_CODE (reg) == REG)
+ put_reg_into_stack (function, reg, TREE_TYPE (decl),
+ promoted_mode, decl_mode);
+ else if (GET_CODE (reg) == CONCAT)
+ {
+ /* A CONCAT contains two pseudos; put them both in the stack.
+ We do it so they end up consecutive. */
+ enum machine_mode part_mode = GET_MODE (XEXP (reg, 0));
+ tree part_type = TREE_TYPE (TREE_TYPE (decl));
+#ifdef STACK_GROWS_DOWNWARD
+ /* Since part 0 should have a lower address, do it second. */
+ put_reg_into_stack (function, XEXP (reg, 1),
+ part_type, part_mode, part_mode);
+ put_reg_into_stack (function, XEXP (reg, 0),
+ part_type, part_mode, part_mode);
+#else
+ put_reg_into_stack (function, XEXP (reg, 0),
+ part_type, part_mode, part_mode);
+ put_reg_into_stack (function, XEXP (reg, 1),
+ part_type, part_mode, part_mode);
+#endif
+
+ /* Change the CONCAT into a combined MEM for both parts. */
+ PUT_CODE (reg, MEM);
+ /* The two parts are in memory order already.
+ Use the lower parts address as ours. */
+ XEXP (reg, 0) = XEXP (XEXP (reg, 0), 0);
+ /* Prevent sharing of rtl that might lose. */
+ if (GET_CODE (XEXP (reg, 0)) == PLUS)
+ XEXP (reg, 0) = copy_rtx (XEXP (reg, 0));
+ }
+}
+
+/* Subroutine of put_var_into_stack. This puts a single pseudo reg REG
+ into the stack frame of FUNCTION (0 means the current function).
+ DECL_MODE is the machine mode of the user-level data type.
+ PROMOTED_MODE is the machine mode of the register. */
+
+static void
+put_reg_into_stack (function, reg, type, promoted_mode, decl_mode)
+ struct function *function;
+ rtx reg;
+ tree type;
+ enum machine_mode promoted_mode, decl_mode;
+{
+ rtx new = 0;
+
+ if (function)
+ {
+ if (REGNO (reg) < function->max_parm_reg)
+ new = function->parm_reg_stack_loc[REGNO (reg)];
+ if (new == 0)
+ new = assign_outer_stack_local (decl_mode, GET_MODE_SIZE (decl_mode),
+ 0, function);
+ }
+ else
+ {
+ if (REGNO (reg) < max_parm_reg)
+ new = parm_reg_stack_loc[REGNO (reg)];
+ if (new == 0)
+ new = assign_stack_local (decl_mode, GET_MODE_SIZE (decl_mode), 0);
+ }
+
+ XEXP (reg, 0) = XEXP (new, 0);
+ /* `volatil' bit means one thing for MEMs, another entirely for REGs. */
+ REG_USERVAR_P (reg) = 0;
+ PUT_CODE (reg, MEM);
+ PUT_MODE (reg, decl_mode);
+
+ /* If this is a memory ref that contains aggregate components,
+ mark it as such for cse and loop optimize. */
+ MEM_IN_STRUCT_P (reg) = AGGREGATE_TYPE_P (type);
+
+ /* Now make sure that all refs to the variable, previously made
+ when it was a register, are fixed up to be valid again. */
+ if (function)
+ {
+ struct var_refs_queue *temp;
+
+ /* Variable is inherited; fix it up when we get back to its function. */
+ push_obstacks (function->function_obstack,
+ function->function_maybepermanent_obstack);
+
+ /* See comment in restore_tree_status in tree.c for why this needs to be
+ on saveable obstack. */
+ temp
+ = (struct var_refs_queue *) savealloc (sizeof (struct var_refs_queue));
+ temp->modified = reg;
+ temp->promoted_mode = promoted_mode;
+ temp->unsignedp = TREE_UNSIGNED (type);
+ temp->next = function->fixup_var_refs_queue;
+ function->fixup_var_refs_queue = temp;
+ pop_obstacks ();
+ }
+ else
+ /* Variable is local; fix it up now. */
+ fixup_var_refs (reg, promoted_mode, TREE_UNSIGNED (type));
+}
+
+static void
+fixup_var_refs (var, promoted_mode, unsignedp)
+ rtx var;
+ enum machine_mode promoted_mode;
+ int unsignedp;
+{
+ tree pending;
+ rtx first_insn = get_insns ();
+ struct sequence_stack *stack = sequence_stack;
+ tree rtl_exps = rtl_expr_chain;
+
+ /* Must scan all insns for stack-refs that exceed the limit. */
+ fixup_var_refs_insns (var, promoted_mode, unsignedp, first_insn, stack == 0);
+
+ /* Scan all pending sequences too. */
+ for (; stack; stack = stack->next)
+ {
+ push_to_sequence (stack->first);
+ fixup_var_refs_insns (var, promoted_mode, unsignedp,
+ stack->first, stack->next != 0);
+ /* Update remembered end of sequence
+ in case we added an insn at the end. */
+ stack->last = get_last_insn ();
+ end_sequence ();
+ }
+
+ /* Scan all waiting RTL_EXPRs too. */
+ for (pending = rtl_exps; pending; pending = TREE_CHAIN (pending))
+ {
+ rtx seq = RTL_EXPR_SEQUENCE (TREE_VALUE (pending));
+ if (seq != const0_rtx && seq != 0)
+ {
+ push_to_sequence (seq);
+ fixup_var_refs_insns (var, promoted_mode, unsignedp, seq, 0);
+ end_sequence ();
+ }
+ }
+}
+
+/* REPLACEMENTS is a pointer to a list of the struct fixup_replacement and X is
+ some part of an insn. Return a struct fixup_replacement whose OLD
+ value is equal to X. Allocate a new structure if no such entry exists. */
+
+static struct fixup_replacement *
+find_fixup_replacement (replacements, x)
+ struct fixup_replacement **replacements;
+ rtx x;
+{
+ struct fixup_replacement *p;
+
+ /* See if we have already replaced this. */
+ for (p = *replacements; p && p->old != x; p = p->next)
+ ;
+
+ if (p == 0)
+ {
+ p = (struct fixup_replacement *) oballoc (sizeof (struct fixup_replacement));
+ p->old = x;
+ p->new = 0;
+ p->next = *replacements;
+ *replacements = p;
+ }
+
+ return p;
+}
+
+/* Scan the insn-chain starting with INSN for refs to VAR
+ and fix them up. TOPLEVEL is nonzero if this chain is the
+ main chain of insns for the current function. */
+
+static void
+fixup_var_refs_insns (var, promoted_mode, unsignedp, insn, toplevel)
+ rtx var;
+ enum machine_mode promoted_mode;
+ int unsignedp;
+ rtx insn;
+ int toplevel;
+{
+ rtx call_dest = 0;
+
+ while (insn)
+ {
+ rtx next = NEXT_INSN (insn);
+ rtx note;
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ /* If this is a CLOBBER of VAR, delete it.
+
+ If it has a REG_LIBCALL note, delete the REG_LIBCALL
+ and REG_RETVAL notes too. */
+ if (GET_CODE (PATTERN (insn)) == CLOBBER
+ && XEXP (PATTERN (insn), 0) == var)
+ {
+ if ((note = find_reg_note (insn, REG_LIBCALL, NULL_RTX)) != 0)
+ /* The REG_LIBCALL note will go away since we are going to
+ turn INSN into a NOTE, so just delete the
+ corresponding REG_RETVAL note. */
+ remove_note (XEXP (note, 0),
+ find_reg_note (XEXP (note, 0), REG_RETVAL,
+ NULL_RTX));
+
+ /* In unoptimized compilation, we shouldn't call delete_insn
+ except in jump.c doing warnings. */
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ }
+
+ /* The insn to load VAR from a home in the arglist
+ is now a no-op. When we see it, just delete it. */
+ else if (toplevel
+ && GET_CODE (PATTERN (insn)) == SET
+ && SET_DEST (PATTERN (insn)) == var
+ /* If this represents the result of an insn group,
+ don't delete the insn. */
+ && find_reg_note (insn, REG_RETVAL, NULL_RTX) == 0
+ && rtx_equal_p (SET_SRC (PATTERN (insn)), var))
+ {
+ /* In unoptimized compilation, we shouldn't call delete_insn
+ except in jump.c doing warnings. */
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ if (insn == last_parm_insn)
+ last_parm_insn = PREV_INSN (next);
+ }
+ else
+ {
+ struct fixup_replacement *replacements = 0;
+ rtx next_insn = NEXT_INSN (insn);
+
+#ifdef SMALL_REGISTER_CLASSES
+ /* If the insn that copies the results of a CALL_INSN
+ into a pseudo now references VAR, we have to use an
+ intermediate pseudo since we want the life of the
+ return value register to be only a single insn.
+
+ If we don't use an intermediate pseudo, such things as
+ address computations to make the address of VAR valid
+ if it is not can be placed beween the CALL_INSN and INSN.
+
+ To make sure this doesn't happen, we record the destination
+ of the CALL_INSN and see if the next insn uses both that
+ and VAR. */
+
+ if (call_dest != 0 && GET_CODE (insn) == INSN
+ && reg_mentioned_p (var, PATTERN (insn))
+ && reg_mentioned_p (call_dest, PATTERN (insn)))
+ {
+ rtx temp = gen_reg_rtx (GET_MODE (call_dest));
+
+ emit_insn_before (gen_move_insn (temp, call_dest), insn);
+
+ PATTERN (insn) = replace_rtx (PATTERN (insn),
+ call_dest, temp);
+ }
+
+ if (GET_CODE (insn) == CALL_INSN
+ && GET_CODE (PATTERN (insn)) == SET)
+ call_dest = SET_DEST (PATTERN (insn));
+ else if (GET_CODE (insn) == CALL_INSN
+ && GET_CODE (PATTERN (insn)) == PARALLEL
+ && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET)
+ call_dest = SET_DEST (XVECEXP (PATTERN (insn), 0, 0));
+ else
+ call_dest = 0;
+#endif
+
+ /* See if we have to do anything to INSN now that VAR is in
+ memory. If it needs to be loaded into a pseudo, use a single
+ pseudo for the entire insn in case there is a MATCH_DUP
+ between two operands. We pass a pointer to the head of
+ a list of struct fixup_replacements. If fixup_var_refs_1
+ needs to allocate pseudos or replacement MEMs (for SUBREGs),
+ it will record them in this list.
+
+ If it allocated a pseudo for any replacement, we copy into
+ it here. */
+
+ fixup_var_refs_1 (var, promoted_mode, &PATTERN (insn), insn,
+ &replacements);
+
+ /* If this is last_parm_insn, and any instructions were output
+ after it to fix it up, then we must set last_parm_insn to
+ the last such instruction emitted. */
+ if (insn == last_parm_insn)
+ last_parm_insn = PREV_INSN (next_insn);
+
+ while (replacements)
+ {
+ if (GET_CODE (replacements->new) == REG)
+ {
+ rtx insert_before;
+ rtx seq;
+
+ /* OLD might be a (subreg (mem)). */
+ if (GET_CODE (replacements->old) == SUBREG)
+ replacements->old
+ = fixup_memory_subreg (replacements->old, insn, 0);
+ else
+ replacements->old
+ = fixup_stack_1 (replacements->old, insn);
+
+ insert_before = insn;
+
+ /* If we are changing the mode, do a conversion.
+ This might be wasteful, but combine.c will
+ eliminate much of the waste. */
+
+ if (GET_MODE (replacements->new)
+ != GET_MODE (replacements->old))
+ {
+ start_sequence ();
+ convert_move (replacements->new,
+ replacements->old, unsignedp);
+ seq = gen_sequence ();
+ end_sequence ();
+ }
+ else
+ seq = gen_move_insn (replacements->new,
+ replacements->old);
+
+ emit_insn_before (seq, insert_before);
+ }
+
+ replacements = replacements->next;
+ }
+ }
+
+ /* Also fix up any invalid exprs in the REG_NOTES of this insn.
+ But don't touch other insns referred to by reg-notes;
+ we will get them elsewhere. */
+ for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
+ if (GET_CODE (note) != INSN_LIST)
+ XEXP (note, 0)
+ = walk_fixup_memory_subreg (XEXP (note, 0), insn, 1);
+ }
+ insn = next;
+ }
+}
+
+/* VAR is a MEM that used to be a pseudo register with mode PROMOTED_MODE.
+ See if the rtx expression at *LOC in INSN needs to be changed.
+
+ REPLACEMENTS is a pointer to a list head that starts out zero, but may
+ contain a list of original rtx's and replacements. If we find that we need
+ to modify this insn by replacing a memory reference with a pseudo or by
+ making a new MEM to implement a SUBREG, we consult that list to see if
+ we have already chosen a replacement. If none has already been allocated,
+ we allocate it and update the list. fixup_var_refs_insns will copy VAR
+ or the SUBREG, as appropriate, to the pseudo. */
+
+static void
+fixup_var_refs_1 (var, promoted_mode, loc, insn, replacements)
+ register rtx var;
+ enum machine_mode promoted_mode;
+ register rtx *loc;
+ rtx insn;
+ struct fixup_replacement **replacements;
+{
+ register int i;
+ register rtx x = *loc;
+ RTX_CODE code = GET_CODE (x);
+ register char *fmt;
+ register rtx tem, tem1;
+ struct fixup_replacement *replacement;
+
+ switch (code)
+ {
+ case MEM:
+ if (var == x)
+ {
+ /* If we already have a replacement, use it. Otherwise,
+ try to fix up this address in case it is invalid. */
+
+ replacement = find_fixup_replacement (replacements, var);
+ if (replacement->new)
+ {
+ *loc = replacement->new;
+ return;
+ }
+
+ *loc = replacement->new = x = fixup_stack_1 (x, insn);
+
+ /* Unless we are forcing memory to register or we changed the mode,
+ we can leave things the way they are if the insn is valid. */
+
+ INSN_CODE (insn) = -1;
+ if (! flag_force_mem && GET_MODE (x) == promoted_mode
+ && recog_memoized (insn) >= 0)
+ return;
+
+ *loc = replacement->new = gen_reg_rtx (promoted_mode);
+ return;
+ }
+
+ /* If X contains VAR, we need to unshare it here so that we update
+ each occurrence separately. But all identical MEMs in one insn
+ must be replaced with the same rtx because of the possibility of
+ MATCH_DUPs. */
+
+ if (reg_mentioned_p (var, x))
+ {
+ replacement = find_fixup_replacement (replacements, x);
+ if (replacement->new == 0)
+ replacement->new = copy_most_rtx (x, var);
+
+ *loc = x = replacement->new;
+ }
+ break;
+
+ case REG:
+ case CC0:
+ case PC:
+ case CONST_INT:
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case CONST_DOUBLE:
+ return;
+
+ case SIGN_EXTRACT:
+ case ZERO_EXTRACT:
+ /* Note that in some cases those types of expressions are altered
+ by optimize_bit_field, and do not survive to get here. */
+ if (XEXP (x, 0) == var
+ || (GET_CODE (XEXP (x, 0)) == SUBREG
+ && SUBREG_REG (XEXP (x, 0)) == var))
+ {
+ /* Get TEM as a valid MEM in the mode presently in the insn.
+
+ We don't worry about the possibility of MATCH_DUP here; it
+ is highly unlikely and would be tricky to handle. */
+
+ tem = XEXP (x, 0);
+ if (GET_CODE (tem) == SUBREG)
+ tem = fixup_memory_subreg (tem, insn, 1);
+ tem = fixup_stack_1 (tem, insn);
+
+ /* Unless we want to load from memory, get TEM into the proper mode
+ for an extract from memory. This can only be done if the
+ extract is at a constant position and length. */
+
+ if (! flag_force_mem && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && GET_CODE (XEXP (x, 2)) == CONST_INT
+ && ! mode_dependent_address_p (XEXP (tem, 0))
+ && ! MEM_VOLATILE_P (tem))
+ {
+ enum machine_mode wanted_mode = VOIDmode;
+ enum machine_mode is_mode = GET_MODE (tem);
+ int width = INTVAL (XEXP (x, 1));
+ int pos = INTVAL (XEXP (x, 2));
+
+#ifdef HAVE_extzv
+ if (GET_CODE (x) == ZERO_EXTRACT)
+ wanted_mode = insn_operand_mode[(int) CODE_FOR_extzv][1];
+#endif
+#ifdef HAVE_extv
+ if (GET_CODE (x) == SIGN_EXTRACT)
+ wanted_mode = insn_operand_mode[(int) CODE_FOR_extv][1];
+#endif
+ /* If we have a narrower mode, we can do something. */
+ if (wanted_mode != VOIDmode
+ && GET_MODE_SIZE (wanted_mode) < GET_MODE_SIZE (is_mode))
+ {
+ int offset = pos / BITS_PER_UNIT;
+ rtx old_pos = XEXP (x, 2);
+ rtx newmem;
+
+ /* If the bytes and bits are counted differently, we
+ must adjust the offset. */
+#if BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN
+ offset = (GET_MODE_SIZE (is_mode)
+ - GET_MODE_SIZE (wanted_mode) - offset);
+#endif
+
+ pos %= GET_MODE_BITSIZE (wanted_mode);
+
+ newmem = gen_rtx (MEM, wanted_mode,
+ plus_constant (XEXP (tem, 0), offset));
+ RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (tem);
+ MEM_VOLATILE_P (newmem) = MEM_VOLATILE_P (tem);
+ MEM_IN_STRUCT_P (newmem) = MEM_IN_STRUCT_P (tem);
+
+ /* Make the change and see if the insn remains valid. */
+ INSN_CODE (insn) = -1;
+ XEXP (x, 0) = newmem;
+ XEXP (x, 2) = GEN_INT (pos);
+
+ if (recog_memoized (insn) >= 0)
+ return;
+
+ /* Otherwise, restore old position. XEXP (x, 0) will be
+ restored later. */
+ XEXP (x, 2) = old_pos;
+ }
+ }
+
+ /* If we get here, the bitfield extract insn can't accept a memory
+ reference. Copy the input into a register. */
+
+ tem1 = gen_reg_rtx (GET_MODE (tem));
+ emit_insn_before (gen_move_insn (tem1, tem), insn);
+ XEXP (x, 0) = tem1;
+ return;
+ }
+ break;
+
+ case SUBREG:
+ if (SUBREG_REG (x) == var)
+ {
+ /* If this is a special SUBREG made because VAR was promoted
+ from a wider mode, replace it with VAR and call ourself
+ recursively, this time saying that the object previously
+ had its current mode (by virtue of the SUBREG). */
+
+ if (SUBREG_PROMOTED_VAR_P (x))
+ {
+ *loc = var;
+ fixup_var_refs_1 (var, GET_MODE (var), loc, insn, replacements);
+ return;
+ }
+
+ /* If this SUBREG makes VAR wider, it has become a paradoxical
+ SUBREG with VAR in memory, but these aren't allowed at this
+ stage of the compilation. So load VAR into a pseudo and take
+ a SUBREG of that pseudo. */
+ if (GET_MODE_SIZE (GET_MODE (x)) > GET_MODE_SIZE (GET_MODE (var)))
+ {
+ replacement = find_fixup_replacement (replacements, var);
+ if (replacement->new == 0)
+ replacement->new = gen_reg_rtx (GET_MODE (var));
+ SUBREG_REG (x) = replacement->new;
+ return;
+ }
+
+ /* See if we have already found a replacement for this SUBREG.
+ If so, use it. Otherwise, make a MEM and see if the insn
+ is recognized. If not, or if we should force MEM into a register,
+ make a pseudo for this SUBREG. */
+ replacement = find_fixup_replacement (replacements, x);
+ if (replacement->new)
+ {
+ *loc = replacement->new;
+ return;
+ }
+
+ replacement->new = *loc = fixup_memory_subreg (x, insn, 0);
+
+ INSN_CODE (insn) = -1;
+ if (! flag_force_mem && recog_memoized (insn) >= 0)
+ return;
+
+ *loc = replacement->new = gen_reg_rtx (GET_MODE (x));
+ return;
+ }
+ break;
+
+ case SET:
+ /* First do special simplification of bit-field references. */
+ if (GET_CODE (SET_DEST (x)) == SIGN_EXTRACT
+ || GET_CODE (SET_DEST (x)) == ZERO_EXTRACT)
+ optimize_bit_field (x, insn, 0);
+ if (GET_CODE (SET_SRC (x)) == SIGN_EXTRACT
+ || GET_CODE (SET_SRC (x)) == ZERO_EXTRACT)
+ optimize_bit_field (x, insn, NULL_PTR);
+
+ /* If SET_DEST is now a paradoxical SUBREG, put the result of this
+ insn into a pseudo and store the low part of the pseudo into VAR. */
+ if (GET_CODE (SET_DEST (x)) == SUBREG
+ && SUBREG_REG (SET_DEST (x)) == var
+ && (GET_MODE_SIZE (GET_MODE (SET_DEST (x)))
+ > GET_MODE_SIZE (GET_MODE (var))))
+ {
+ SET_DEST (x) = tem = gen_reg_rtx (GET_MODE (SET_DEST (x)));
+ emit_insn_after (gen_move_insn (var, gen_lowpart (GET_MODE (var),
+ tem)),
+ insn);
+ break;
+ }
+
+ {
+ rtx dest = SET_DEST (x);
+ rtx src = SET_SRC (x);
+ rtx outerdest = dest;
+
+ while (GET_CODE (dest) == SUBREG || GET_CODE (dest) == STRICT_LOW_PART
+ || GET_CODE (dest) == SIGN_EXTRACT
+ || GET_CODE (dest) == ZERO_EXTRACT)
+ dest = XEXP (dest, 0);
+
+ if (GET_CODE (src) == SUBREG)
+ src = XEXP (src, 0);
+
+ /* If VAR does not appear at the top level of the SET
+ just scan the lower levels of the tree. */
+
+ if (src != var && dest != var)
+ break;
+
+ /* We will need to rerecognize this insn. */
+ INSN_CODE (insn) = -1;
+
+#ifdef HAVE_insv
+ if (GET_CODE (outerdest) == ZERO_EXTRACT && dest == var)
+ {
+ /* Since this case will return, ensure we fixup all the
+ operands here. */
+ fixup_var_refs_1 (var, promoted_mode, &XEXP (outerdest, 1),
+ insn, replacements);
+ fixup_var_refs_1 (var, promoted_mode, &XEXP (outerdest, 2),
+ insn, replacements);
+ fixup_var_refs_1 (var, promoted_mode, &SET_SRC (x),
+ insn, replacements);
+
+ tem = XEXP (outerdest, 0);
+
+ /* Clean up (SUBREG:SI (MEM:mode ...) 0)
+ that may appear inside a ZERO_EXTRACT.
+ This was legitimate when the MEM was a REG. */
+ if (GET_CODE (tem) == SUBREG
+ && SUBREG_REG (tem) == var)
+ tem = fixup_memory_subreg (tem, insn, 1);
+ else
+ tem = fixup_stack_1 (tem, insn);
+
+ if (GET_CODE (XEXP (outerdest, 1)) == CONST_INT
+ && GET_CODE (XEXP (outerdest, 2)) == CONST_INT
+ && ! mode_dependent_address_p (XEXP (tem, 0))
+ && ! MEM_VOLATILE_P (tem))
+ {
+ enum machine_mode wanted_mode
+ = insn_operand_mode[(int) CODE_FOR_insv][0];
+ enum machine_mode is_mode = GET_MODE (tem);
+ int width = INTVAL (XEXP (outerdest, 1));
+ int pos = INTVAL (XEXP (outerdest, 2));
+
+ /* If we have a narrower mode, we can do something. */
+ if (GET_MODE_SIZE (wanted_mode) < GET_MODE_SIZE (is_mode))
+ {
+ int offset = pos / BITS_PER_UNIT;
+ rtx old_pos = XEXP (outerdest, 2);
+ rtx newmem;
+
+#if BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN
+ offset = (GET_MODE_SIZE (is_mode)
+ - GET_MODE_SIZE (wanted_mode) - offset);
+#endif
+
+ pos %= GET_MODE_BITSIZE (wanted_mode);
+
+ newmem = gen_rtx (MEM, wanted_mode,
+ plus_constant (XEXP (tem, 0), offset));
+ RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (tem);
+ MEM_VOLATILE_P (newmem) = MEM_VOLATILE_P (tem);
+ MEM_IN_STRUCT_P (newmem) = MEM_IN_STRUCT_P (tem);
+
+ /* Make the change and see if the insn remains valid. */
+ INSN_CODE (insn) = -1;
+ XEXP (outerdest, 0) = newmem;
+ XEXP (outerdest, 2) = GEN_INT (pos);
+
+ if (recog_memoized (insn) >= 0)
+ return;
+
+ /* Otherwise, restore old position. XEXP (x, 0) will be
+ restored later. */
+ XEXP (outerdest, 2) = old_pos;
+ }
+ }
+
+ /* If we get here, the bit-field store doesn't allow memory
+ or isn't located at a constant position. Load the value into
+ a register, do the store, and put it back into memory. */
+
+ tem1 = gen_reg_rtx (GET_MODE (tem));
+ emit_insn_before (gen_move_insn (tem1, tem), insn);
+ emit_insn_after (gen_move_insn (tem, tem1), insn);
+ XEXP (outerdest, 0) = tem1;
+ return;
+ }
+#endif
+
+ /* STRICT_LOW_PART is a no-op on memory references
+ and it can cause combinations to be unrecognizable,
+ so eliminate it. */
+
+ if (dest == var && GET_CODE (SET_DEST (x)) == STRICT_LOW_PART)
+ SET_DEST (x) = XEXP (SET_DEST (x), 0);
+
+ /* A valid insn to copy VAR into or out of a register
+ must be left alone, to avoid an infinite loop here.
+ If the reference to VAR is by a subreg, fix that up,
+ since SUBREG is not valid for a memref.
+ Also fix up the address of the stack slot.
+
+ Note that we must not try to recognize the insn until
+ after we know that we have valid addresses and no
+ (subreg (mem ...) ...) constructs, since these interfere
+ with determining the validity of the insn. */
+
+ if ((SET_SRC (x) == var
+ || (GET_CODE (SET_SRC (x)) == SUBREG
+ && SUBREG_REG (SET_SRC (x)) == var))
+ && (GET_CODE (SET_DEST (x)) == REG
+ || (GET_CODE (SET_DEST (x)) == SUBREG
+ && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG))
+ && x == single_set (PATTERN (insn)))
+ {
+ rtx pat;
+
+ replacement = find_fixup_replacement (replacements, SET_SRC (x));
+ if (replacement->new)
+ SET_SRC (x) = replacement->new;
+ else if (GET_CODE (SET_SRC (x)) == SUBREG)
+ SET_SRC (x) = replacement->new
+ = fixup_memory_subreg (SET_SRC (x), insn, 0);
+ else
+ SET_SRC (x) = replacement->new
+ = fixup_stack_1 (SET_SRC (x), insn);
+
+ if (recog_memoized (insn) >= 0)
+ return;
+
+ /* INSN is not valid, but we know that we want to
+ copy SET_SRC (x) to SET_DEST (x) in some way. So
+ we generate the move and see whether it requires more
+ than one insn. If it does, we emit those insns and
+ delete INSN. Otherwise, we an just replace the pattern
+ of INSN; we have already verified above that INSN has
+ no other function that to do X. */
+
+ pat = gen_move_insn (SET_DEST (x), SET_SRC (x));
+ if (GET_CODE (pat) == SEQUENCE)
+ {
+ emit_insn_after (pat, insn);
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ }
+ else
+ PATTERN (insn) = pat;
+
+ return;
+ }
+
+ if ((SET_DEST (x) == var
+ || (GET_CODE (SET_DEST (x)) == SUBREG
+ && SUBREG_REG (SET_DEST (x)) == var))
+ && (GET_CODE (SET_SRC (x)) == REG
+ || (GET_CODE (SET_SRC (x)) == SUBREG
+ && GET_CODE (SUBREG_REG (SET_SRC (x))) == REG))
+ && x == single_set (PATTERN (insn)))
+ {
+ rtx pat;
+
+ if (GET_CODE (SET_DEST (x)) == SUBREG)
+ SET_DEST (x) = fixup_memory_subreg (SET_DEST (x), insn, 0);
+ else
+ SET_DEST (x) = fixup_stack_1 (SET_DEST (x), insn);
+
+ if (recog_memoized (insn) >= 0)
+ return;
+
+ pat = gen_move_insn (SET_DEST (x), SET_SRC (x));
+ if (GET_CODE (pat) == SEQUENCE)
+ {
+ emit_insn_after (pat, insn);
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ }
+ else
+ PATTERN (insn) = pat;
+
+ return;
+ }
+
+ /* Otherwise, storing into VAR must be handled specially
+ by storing into a temporary and copying that into VAR
+ with a new insn after this one. Note that this case
+ will be used when storing into a promoted scalar since
+ the insn will now have different modes on the input
+ and output and hence will be invalid (except for the case
+ of setting it to a constant, which does not need any
+ change if it is valid). We generate extra code in that case,
+ but combine.c will eliminate it. */
+
+ if (dest == var)
+ {
+ rtx temp;
+ rtx fixeddest = SET_DEST (x);
+
+ /* STRICT_LOW_PART can be discarded, around a MEM. */
+ if (GET_CODE (fixeddest) == STRICT_LOW_PART)
+ fixeddest = XEXP (fixeddest, 0);
+ /* Convert (SUBREG (MEM)) to a MEM in a changed mode. */
+ if (GET_CODE (fixeddest) == SUBREG)
+ fixeddest = fixup_memory_subreg (fixeddest, insn, 0);
+ else
+ fixeddest = fixup_stack_1 (fixeddest, insn);
+
+ temp = gen_reg_rtx (GET_MODE (SET_SRC (x)) == VOIDmode
+ ? GET_MODE (fixeddest)
+ : GET_MODE (SET_SRC (x)));
+
+ emit_insn_after (gen_move_insn (fixeddest,
+ gen_lowpart (GET_MODE (fixeddest),
+ temp)),
+ insn);
+
+ SET_DEST (x) = temp;
+ }
+ }
+ }
+
+ /* Nothing special about this RTX; fix its operands. */
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ fixup_var_refs_1 (var, promoted_mode, &XEXP (x, i), insn, replacements);
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ fixup_var_refs_1 (var, promoted_mode, &XVECEXP (x, i, j),
+ insn, replacements);
+ }
+ }
+}
+
+/* Given X, an rtx of the form (SUBREG:m1 (MEM:m2 addr)),
+ return an rtx (MEM:m1 newaddr) which is equivalent.
+ If any insns must be emitted to compute NEWADDR, put them before INSN.
+
+ UNCRITICAL nonzero means accept paradoxical subregs.
+ This is used for subregs found inside of ZERO_EXTRACTs and in REG_NOTES. */
+
+static rtx
+fixup_memory_subreg (x, insn, uncritical)
+ rtx x;
+ rtx insn;
+ int uncritical;
+{
+ int offset = SUBREG_WORD (x) * UNITS_PER_WORD;
+ rtx addr = XEXP (SUBREG_REG (x), 0);
+ enum machine_mode mode = GET_MODE (x);
+ rtx saved, result;
+
+ /* Paradoxical SUBREGs are usually invalid during RTL generation. */
+ if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
+ && ! uncritical)
+ abort ();
+
+#if BYTES_BIG_ENDIAN
+ offset += (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+ - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)));
+#endif
+ addr = plus_constant (addr, offset);
+ if (!flag_force_addr && memory_address_p (mode, addr))
+ /* Shortcut if no insns need be emitted. */
+ return change_address (SUBREG_REG (x), mode, addr);
+ start_sequence ();
+ result = change_address (SUBREG_REG (x), mode, addr);
+ emit_insn_before (gen_sequence (), insn);
+ end_sequence ();
+ return result;
+}
+
+/* Do fixup_memory_subreg on all (SUBREG (MEM ...) ...) contained in X.
+ Replace subexpressions of X in place.
+ If X itself is a (SUBREG (MEM ...) ...), return the replacement expression.
+ Otherwise return X, with its contents possibly altered.
+
+ If any insns must be emitted to compute NEWADDR, put them before INSN.
+
+ UNCRITICAL is as in fixup_memory_subreg. */
+
+static rtx
+walk_fixup_memory_subreg (x, insn, uncritical)
+ register rtx x;
+ rtx insn;
+ int uncritical;
+{
+ register enum rtx_code code;
+ register char *fmt;
+ register int i;
+
+ if (x == 0)
+ return 0;
+
+ code = GET_CODE (x);
+
+ if (code == SUBREG && GET_CODE (SUBREG_REG (x)) == MEM)
+ return fixup_memory_subreg (x, insn, uncritical);
+
+ /* Nothing special about this RTX; fix its operands. */
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ XEXP (x, i) = walk_fixup_memory_subreg (XEXP (x, i), insn, uncritical);
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ XVECEXP (x, i, j)
+ = walk_fixup_memory_subreg (XVECEXP (x, i, j), insn, uncritical);
+ }
+ }
+ return x;
+}
+
+/* For each memory ref within X, if it refers to a stack slot
+ with an out of range displacement, put the address in a temp register
+ (emitting new insns before INSN to load these registers)
+ and alter the memory ref to use that register.
+ Replace each such MEM rtx with a copy, to avoid clobberage. */
+
+static rtx
+fixup_stack_1 (x, insn)
+ rtx x;
+ rtx insn;
+{
+ register int i;
+ register RTX_CODE code = GET_CODE (x);
+ register char *fmt;
+
+ if (code == MEM)
+ {
+ register rtx ad = XEXP (x, 0);
+ /* If we have address of a stack slot but it's not valid
+ (displacement is too large), compute the sum in a register. */
+ if (GET_CODE (ad) == PLUS
+ && GET_CODE (XEXP (ad, 0)) == REG
+ && ((REGNO (XEXP (ad, 0)) >= FIRST_VIRTUAL_REGISTER
+ && REGNO (XEXP (ad, 0)) <= LAST_VIRTUAL_REGISTER)
+ || XEXP (ad, 0) == current_function_internal_arg_pointer)
+ && GET_CODE (XEXP (ad, 1)) == CONST_INT)
+ {
+ rtx temp, seq;
+ if (memory_address_p (GET_MODE (x), ad))
+ return x;
+
+ start_sequence ();
+ temp = copy_to_reg (ad);
+ seq = gen_sequence ();
+ end_sequence ();
+ emit_insn_before (seq, insn);
+ return change_address (x, VOIDmode, temp);
+ }
+ return x;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ XEXP (x, i) = fixup_stack_1 (XEXP (x, i), insn);
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ XVECEXP (x, i, j) = fixup_stack_1 (XVECEXP (x, i, j), insn);
+ }
+ }
+ return x;
+}
+
+/* Optimization: a bit-field instruction whose field
+ happens to be a byte or halfword in memory
+ can be changed to a move instruction.
+
+ We call here when INSN is an insn to examine or store into a bit-field.
+ BODY is the SET-rtx to be altered.
+
+ EQUIV_MEM is the table `reg_equiv_mem' if that is available; else 0.
+ (Currently this is called only from function.c, and EQUIV_MEM
+ is always 0.) */
+
+static void
+optimize_bit_field (body, insn, equiv_mem)
+ rtx body;
+ rtx insn;
+ rtx *equiv_mem;
+{
+ register rtx bitfield;
+ int destflag;
+ rtx seq = 0;
+ enum machine_mode mode;
+
+ if (GET_CODE (SET_DEST (body)) == SIGN_EXTRACT
+ || GET_CODE (SET_DEST (body)) == ZERO_EXTRACT)
+ bitfield = SET_DEST (body), destflag = 1;
+ else
+ bitfield = SET_SRC (body), destflag = 0;
+
+ /* First check that the field being stored has constant size and position
+ and is in fact a byte or halfword suitably aligned. */
+
+ if (GET_CODE (XEXP (bitfield, 1)) == CONST_INT
+ && GET_CODE (XEXP (bitfield, 2)) == CONST_INT
+ && ((mode = mode_for_size (INTVAL (XEXP (bitfield, 1)), MODE_INT, 1))
+ != BLKmode)
+ && INTVAL (XEXP (bitfield, 2)) % INTVAL (XEXP (bitfield, 1)) == 0)
+ {
+ register rtx memref = 0;
+
+ /* Now check that the containing word is memory, not a register,
+ and that it is safe to change the machine mode. */
+
+ if (GET_CODE (XEXP (bitfield, 0)) == MEM)
+ memref = XEXP (bitfield, 0);
+ else if (GET_CODE (XEXP (bitfield, 0)) == REG
+ && equiv_mem != 0)
+ memref = equiv_mem[REGNO (XEXP (bitfield, 0))];
+ else if (GET_CODE (XEXP (bitfield, 0)) == SUBREG
+ && GET_CODE (SUBREG_REG (XEXP (bitfield, 0))) == MEM)
+ memref = SUBREG_REG (XEXP (bitfield, 0));
+ else if (GET_CODE (XEXP (bitfield, 0)) == SUBREG
+ && equiv_mem != 0
+ && GET_CODE (SUBREG_REG (XEXP (bitfield, 0))) == REG)
+ memref = equiv_mem[REGNO (SUBREG_REG (XEXP (bitfield, 0)))];
+
+ if (memref
+ && ! mode_dependent_address_p (XEXP (memref, 0))
+ && ! MEM_VOLATILE_P (memref))
+ {
+ /* Now adjust the address, first for any subreg'ing
+ that we are now getting rid of,
+ and then for which byte of the word is wanted. */
+
+ register int offset = INTVAL (XEXP (bitfield, 2));
+ /* Adjust OFFSET to count bits from low-address byte. */
+#if BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN
+ offset = (GET_MODE_BITSIZE (GET_MODE (XEXP (bitfield, 0)))
+ - offset - INTVAL (XEXP (bitfield, 1)));
+#endif
+ /* Adjust OFFSET to count bytes from low-address byte. */
+ offset /= BITS_PER_UNIT;
+ if (GET_CODE (XEXP (bitfield, 0)) == SUBREG)
+ {
+ offset += SUBREG_WORD (XEXP (bitfield, 0)) * UNITS_PER_WORD;
+#if BYTES_BIG_ENDIAN
+ offset -= (MIN (UNITS_PER_WORD,
+ GET_MODE_SIZE (GET_MODE (XEXP (bitfield, 0))))
+ - MIN (UNITS_PER_WORD,
+ GET_MODE_SIZE (GET_MODE (memref))));
+#endif
+ }
+
+ memref = change_address (memref, mode,
+ plus_constant (XEXP (memref, 0), offset));
+
+ /* Store this memory reference where
+ we found the bit field reference. */
+
+ if (destflag)
+ {
+ validate_change (insn, &SET_DEST (body), memref, 1);
+ if (! CONSTANT_ADDRESS_P (SET_SRC (body)))
+ {
+ rtx src = SET_SRC (body);
+ while (GET_CODE (src) == SUBREG
+ && SUBREG_WORD (src) == 0)
+ src = SUBREG_REG (src);
+ if (GET_MODE (src) != GET_MODE (memref))
+ src = gen_lowpart (GET_MODE (memref), SET_SRC (body));
+ validate_change (insn, &SET_SRC (body), src, 1);
+ }
+ else if (GET_MODE (SET_SRC (body)) != VOIDmode
+ && GET_MODE (SET_SRC (body)) != GET_MODE (memref))
+ /* This shouldn't happen because anything that didn't have
+ one of these modes should have got converted explicitly
+ and then referenced through a subreg.
+ This is so because the original bit-field was
+ handled by agg_mode and so its tree structure had
+ the same mode that memref now has. */
+ abort ();
+ }
+ else
+ {
+ rtx dest = SET_DEST (body);
+
+ while (GET_CODE (dest) == SUBREG
+ && SUBREG_WORD (dest) == 0)
+ dest = SUBREG_REG (dest);
+
+ validate_change (insn, &SET_DEST (body), dest, 1);
+
+ if (GET_MODE (dest) == GET_MODE (memref))
+ validate_change (insn, &SET_SRC (body), memref, 1);
+ else
+ {
+ /* Convert the mem ref to the destination mode. */
+ rtx newreg = gen_reg_rtx (GET_MODE (dest));
+
+ start_sequence ();
+ convert_move (newreg, memref,
+ GET_CODE (SET_SRC (body)) == ZERO_EXTRACT);
+ seq = get_insns ();
+ end_sequence ();
+
+ validate_change (insn, &SET_SRC (body), newreg, 1);
+ }
+ }
+
+ /* See if we can convert this extraction or insertion into
+ a simple move insn. We might not be able to do so if this
+ was, for example, part of a PARALLEL.
+
+ If we succeed, write out any needed conversions. If we fail,
+ it is hard to guess why we failed, so don't do anything
+ special; just let the optimization be suppressed. */
+
+ if (apply_change_group () && seq)
+ emit_insns_before (seq, insn);
+ }
+ }
+}
+
+/* These routines are responsible for converting virtual register references
+ to the actual hard register references once RTL generation is complete.
+
+ The following four variables are used for communication between the
+ routines. They contain the offsets of the virtual registers from their
+ respective hard registers. */
+
+static int in_arg_offset;
+static int var_offset;
+static int dynamic_offset;
+static int out_arg_offset;
+
+/* In most machines, the stack pointer register is equivalent to the bottom
+ of the stack. */
+
+#ifndef STACK_POINTER_OFFSET
+#define STACK_POINTER_OFFSET 0
+#endif
+
+/* If not defined, pick an appropriate default for the offset of dynamically
+ allocated memory depending on the value of ACCUMULATE_OUTGOING_ARGS,
+ REG_PARM_STACK_SPACE, and OUTGOING_REG_PARM_STACK_SPACE. */
+
+#ifndef STACK_DYNAMIC_OFFSET
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+/* The bottom of the stack points to the actual arguments. If
+ REG_PARM_STACK_SPACE is defined, this includes the space for the register
+ parameters. However, if OUTGOING_REG_PARM_STACK space is not defined,
+ stack space for register parameters is not pushed by the caller, but
+ rather part of the fixed stack areas and hence not included in
+ `current_function_outgoing_args_size'. Nevertheless, we must allow
+ for it when allocating stack dynamic objects. */
+
+#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE)
+#define STACK_DYNAMIC_OFFSET(FNDECL) \
+(current_function_outgoing_args_size \
+ + REG_PARM_STACK_SPACE (FNDECL) + (STACK_POINTER_OFFSET))
+
+#else
+#define STACK_DYNAMIC_OFFSET(FNDECL) \
+(current_function_outgoing_args_size + (STACK_POINTER_OFFSET))
+#endif
+
+#else
+#define STACK_DYNAMIC_OFFSET(FNDECL) STACK_POINTER_OFFSET
+#endif
+#endif
+
+/* Pass through the INSNS of function FNDECL and convert virtual register
+ references to hard register references. */
+
+void
+instantiate_virtual_regs (fndecl, insns)
+ tree fndecl;
+ rtx insns;
+{
+ rtx insn;
+
+ /* Compute the offsets to use for this function. */
+ in_arg_offset = FIRST_PARM_OFFSET (fndecl);
+ var_offset = STARTING_FRAME_OFFSET;
+ dynamic_offset = STACK_DYNAMIC_OFFSET (fndecl);
+ out_arg_offset = STACK_POINTER_OFFSET;
+
+ /* Scan all variables and parameters of this function. For each that is
+ in memory, instantiate all virtual registers if the result is a valid
+ address. If not, we do it later. That will handle most uses of virtual
+ regs on many machines. */
+ instantiate_decls (fndecl, 1);
+
+ /* Initialize recognition, indicating that volatile is OK. */
+ init_recog ();
+
+ /* Scan through all the insns, instantiating every virtual register still
+ present. */
+ for (insn = insns; insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN
+ || GET_CODE (insn) == CALL_INSN)
+ {
+ instantiate_virtual_regs_1 (&PATTERN (insn), insn, 1);
+ instantiate_virtual_regs_1 (&REG_NOTES (insn), NULL_RTX, 0);
+ }
+
+ /* Now instantiate the remaining register equivalences for debugging info.
+ These will not be valid addresses. */
+ instantiate_decls (fndecl, 0);
+
+ /* Indicate that, from now on, assign_stack_local should use
+ frame_pointer_rtx. */
+ virtuals_instantiated = 1;
+}
+
+/* Scan all decls in FNDECL (both variables and parameters) and instantiate
+ all virtual registers in their DECL_RTL's.
+
+ If VALID_ONLY, do this only if the resulting address is still valid.
+ Otherwise, always do it. */
+
+static void
+instantiate_decls (fndecl, valid_only)
+ tree fndecl;
+ int valid_only;
+{
+ tree decl;
+
+ if (DECL_INLINE (fndecl))
+ /* When compiling an inline function, the obstack used for
+ rtl allocation is the maybepermanent_obstack. Calling
+ `resume_temporary_allocation' switches us back to that
+ obstack while we process this function's parameters. */
+ resume_temporary_allocation ();
+
+ /* Process all parameters of the function. */
+ for (decl = DECL_ARGUMENTS (fndecl); decl; decl = TREE_CHAIN (decl))
+ {
+ instantiate_decl (DECL_RTL (decl), int_size_in_bytes (TREE_TYPE (decl)),
+ valid_only);
+ instantiate_decl (DECL_INCOMING_RTL (decl),
+ int_size_in_bytes (TREE_TYPE (decl)), valid_only);
+ }
+
+ /* Now process all variables defined in the function or its subblocks. */
+ instantiate_decls_1 (DECL_INITIAL (fndecl), valid_only);
+
+ if (DECL_INLINE (fndecl))
+ {
+ /* Save all rtl allocated for this function by raising the
+ high-water mark on the maybepermanent_obstack. */
+ preserve_data ();
+ /* All further rtl allocation is now done in the current_obstack. */
+ rtl_in_current_obstack ();
+ }
+}
+
+/* Subroutine of instantiate_decls: Process all decls in the given
+ BLOCK node and all its subblocks. */
+
+static void
+instantiate_decls_1 (let, valid_only)
+ tree let;
+ int valid_only;
+{
+ tree t;
+
+ for (t = BLOCK_VARS (let); t; t = TREE_CHAIN (t))
+ instantiate_decl (DECL_RTL (t), int_size_in_bytes (TREE_TYPE (t)),
+ valid_only);
+
+ /* Process all subblocks. */
+ for (t = BLOCK_SUBBLOCKS (let); t; t = TREE_CHAIN (t))
+ instantiate_decls_1 (t, valid_only);
+}
+
+/* Subroutine of the preceding procedures: Given RTL representing a
+ decl and the size of the object, do any instantiation required.
+
+ If VALID_ONLY is non-zero, it means that the RTL should only be
+ changed if the new address is valid. */
+
+static void
+instantiate_decl (x, size, valid_only)
+ rtx x;
+ int size;
+ int valid_only;
+{
+ enum machine_mode mode;
+ rtx addr;
+
+ /* If this is not a MEM, no need to do anything. Similarly if the
+ address is a constant or a register that is not a virtual register. */
+
+ if (x == 0 || GET_CODE (x) != MEM)
+ return;
+
+ addr = XEXP (x, 0);
+ if (CONSTANT_P (addr)
+ || (GET_CODE (addr) == REG
+ && (REGNO (addr) < FIRST_VIRTUAL_REGISTER
+ || REGNO (addr) > LAST_VIRTUAL_REGISTER)))
+ return;
+
+ /* If we should only do this if the address is valid, copy the address.
+ We need to do this so we can undo any changes that might make the
+ address invalid. This copy is unfortunate, but probably can't be
+ avoided. */
+
+ if (valid_only)
+ addr = copy_rtx (addr);
+
+ instantiate_virtual_regs_1 (&addr, NULL_RTX, 0);
+
+ if (! valid_only)
+ return;
+
+ /* Now verify that the resulting address is valid for every integer or
+ floating-point mode up to and including SIZE bytes long. We do this
+ since the object might be accessed in any mode and frame addresses
+ are shared. */
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+ mode != VOIDmode && GET_MODE_SIZE (mode) <= size;
+ mode = GET_MODE_WIDER_MODE (mode))
+ if (! memory_address_p (mode, addr))
+ return;
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT);
+ mode != VOIDmode && GET_MODE_SIZE (mode) <= size;
+ mode = GET_MODE_WIDER_MODE (mode))
+ if (! memory_address_p (mode, addr))
+ return;
+
+ /* Otherwise, put back the address, now that we have updated it and we
+ know it is valid. */
+
+ XEXP (x, 0) = addr;
+}
+
+/* Given a pointer to a piece of rtx and an optional pointer to the
+ containing object, instantiate any virtual registers present in it.
+
+ If EXTRA_INSNS, we always do the replacement and generate
+ any extra insns before OBJECT. If it zero, we do nothing if replacement
+ is not valid.
+
+ Return 1 if we either had nothing to do or if we were able to do the
+ needed replacement. Return 0 otherwise; we only return zero if
+ EXTRA_INSNS is zero.
+
+ We first try some simple transformations to avoid the creation of extra
+ pseudos. */
+
+static int
+instantiate_virtual_regs_1 (loc, object, extra_insns)
+ rtx *loc;
+ rtx object;
+ int extra_insns;
+{
+ rtx x;
+ RTX_CODE code;
+ rtx new = 0;
+ int offset;
+ rtx temp;
+ rtx seq;
+ int i, j;
+ char *fmt;
+
+ /* Re-start here to avoid recursion in common cases. */
+ restart:
+
+ x = *loc;
+ if (x == 0)
+ return 1;
+
+ code = GET_CODE (x);
+
+ /* Check for some special cases. */
+ switch (code)
+ {
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case CONST:
+ case SYMBOL_REF:
+ case CODE_LABEL:
+ case PC:
+ case CC0:
+ case ASM_INPUT:
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ case RETURN:
+ return 1;
+
+ case SET:
+ /* We are allowed to set the virtual registers. This means that
+ that the actual register should receive the source minus the
+ appropriate offset. This is used, for example, in the handling
+ of non-local gotos. */
+ if (SET_DEST (x) == virtual_incoming_args_rtx)
+ new = arg_pointer_rtx, offset = - in_arg_offset;
+ else if (SET_DEST (x) == virtual_stack_vars_rtx)
+ new = frame_pointer_rtx, offset = - var_offset;
+ else if (SET_DEST (x) == virtual_stack_dynamic_rtx)
+ new = stack_pointer_rtx, offset = - dynamic_offset;
+ else if (SET_DEST (x) == virtual_outgoing_args_rtx)
+ new = stack_pointer_rtx, offset = - out_arg_offset;
+
+ if (new)
+ {
+ /* The only valid sources here are PLUS or REG. Just do
+ the simplest possible thing to handle them. */
+ if (GET_CODE (SET_SRC (x)) != REG
+ && GET_CODE (SET_SRC (x)) != PLUS)
+ abort ();
+
+ start_sequence ();
+ if (GET_CODE (SET_SRC (x)) != REG)
+ temp = force_operand (SET_SRC (x), NULL_RTX);
+ else
+ temp = SET_SRC (x);
+ temp = force_operand (plus_constant (temp, offset), NULL_RTX);
+ seq = get_insns ();
+ end_sequence ();
+
+ emit_insns_before (seq, object);
+ SET_DEST (x) = new;
+
+ if (!validate_change (object, &SET_SRC (x), temp, 0)
+ || ! extra_insns)
+ abort ();
+
+ return 1;
+ }
+
+ instantiate_virtual_regs_1 (&SET_DEST (x), object, extra_insns);
+ loc = &SET_SRC (x);
+ goto restart;
+
+ case PLUS:
+ /* Handle special case of virtual register plus constant. */
+ if (CONSTANT_P (XEXP (x, 1)))
+ {
+ rtx old;
+
+ /* Check for (plus (plus VIRT foo) (const_int)) first. */
+ if (GET_CODE (XEXP (x, 0)) == PLUS)
+ {
+ rtx inner = XEXP (XEXP (x, 0), 0);
+
+ if (inner == virtual_incoming_args_rtx)
+ new = arg_pointer_rtx, offset = in_arg_offset;
+ else if (inner == virtual_stack_vars_rtx)
+ new = frame_pointer_rtx, offset = var_offset;
+ else if (inner == virtual_stack_dynamic_rtx)
+ new = stack_pointer_rtx, offset = dynamic_offset;
+ else if (inner == virtual_outgoing_args_rtx)
+ new = stack_pointer_rtx, offset = out_arg_offset;
+ else
+ {
+ loc = &XEXP (x, 0);
+ goto restart;
+ }
+
+ instantiate_virtual_regs_1 (&XEXP (XEXP (x, 0), 1), object,
+ extra_insns);
+ new = gen_rtx (PLUS, Pmode, new, XEXP (XEXP (x, 0), 1));
+ }
+
+ else if (XEXP (x, 0) == virtual_incoming_args_rtx)
+ new = arg_pointer_rtx, offset = in_arg_offset;
+ else if (XEXP (x, 0) == virtual_stack_vars_rtx)
+ new = frame_pointer_rtx, offset = var_offset;
+ else if (XEXP (x, 0) == virtual_stack_dynamic_rtx)
+ new = stack_pointer_rtx, offset = dynamic_offset;
+ else if (XEXP (x, 0) == virtual_outgoing_args_rtx)
+ new = stack_pointer_rtx, offset = out_arg_offset;
+ else
+ {
+ /* We know the second operand is a constant. Unless the
+ first operand is a REG (which has been already checked),
+ it needs to be checked. */
+ if (GET_CODE (XEXP (x, 0)) != REG)
+ {
+ loc = &XEXP (x, 0);
+ goto restart;
+ }
+ return 1;
+ }
+
+ old = XEXP (x, 0);
+ XEXP (x, 0) = new;
+ new = plus_constant (XEXP (x, 1), offset);
+
+ /* If the new constant is zero, try to replace the sum with its
+ first operand. */
+ if (new == const0_rtx
+ && validate_change (object, loc, XEXP (x, 0), 0))
+ return 1;
+
+ /* Next try to replace constant with new one. */
+ if (!validate_change (object, &XEXP (x, 1), new, 0))
+ {
+ if (! extra_insns)
+ {
+ XEXP (x, 0) = old;
+ return 0;
+ }
+
+ /* Otherwise copy the new constant into a register and replace
+ constant with that register. */
+ temp = gen_reg_rtx (Pmode);
+ if (validate_change (object, &XEXP (x, 1), temp, 0))
+ emit_insn_before (gen_move_insn (temp, new), object);
+ else
+ {
+ /* If that didn't work, replace this expression with a
+ register containing the sum. */
+
+ new = gen_rtx (PLUS, Pmode, XEXP (x, 0), new);
+ XEXP (x, 0) = old;
+
+ start_sequence ();
+ temp = force_operand (new, NULL_RTX);
+ seq = get_insns ();
+ end_sequence ();
+
+ emit_insns_before (seq, object);
+ if (! validate_change (object, loc, temp, 0)
+ && ! validate_replace_rtx (x, temp, object))
+ abort ();
+ }
+ }
+
+ return 1;
+ }
+
+ /* Fall through to generic two-operand expression case. */
+ case EXPR_LIST:
+ case CALL:
+ case COMPARE:
+ case MINUS:
+ case MULT:
+ case DIV: case UDIV:
+ case MOD: case UMOD:
+ case AND: case IOR: case XOR:
+ case ROTATERT: case ROTATE:
+ case ASHIFTRT: case LSHIFTRT: case ASHIFT:
+ case NE: case EQ:
+ case GE: case GT: case GEU: case GTU:
+ case LE: case LT: case LEU: case LTU:
+ if (XEXP (x, 1) && ! CONSTANT_P (XEXP (x, 1)))
+ instantiate_virtual_regs_1 (&XEXP (x, 1), object, extra_insns);
+ loc = &XEXP (x, 0);
+ goto restart;
+
+ case MEM:
+ /* Most cases of MEM that convert to valid addresses have already been
+ handled by our scan of regno_reg_rtx. The only special handling we
+ need here is to make a copy of the rtx to ensure it isn't being
+ shared if we have to change it to a pseudo.
+
+ If the rtx is a simple reference to an address via a virtual register,
+ it can potentially be shared. In such cases, first try to make it
+ a valid address, which can also be shared. Otherwise, copy it and
+ proceed normally.
+
+ First check for common cases that need no processing. These are
+ usually due to instantiation already being done on a previous instance
+ of a shared rtx. */
+
+ temp = XEXP (x, 0);
+ if (CONSTANT_ADDRESS_P (temp)
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ || temp == arg_pointer_rtx
+#endif
+#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ || temp == hard_frame_pointer_rtx
+#endif
+ || temp == frame_pointer_rtx)
+ return 1;
+
+ if (GET_CODE (temp) == PLUS
+ && CONSTANT_ADDRESS_P (XEXP (temp, 1))
+ && (XEXP (temp, 0) == frame_pointer_rtx
+#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ || XEXP (temp, 0) == hard_frame_pointer_rtx
+#endif
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ || XEXP (temp, 0) == arg_pointer_rtx
+#endif
+ ))
+ return 1;
+
+ if (temp == virtual_stack_vars_rtx
+ || temp == virtual_incoming_args_rtx
+ || (GET_CODE (temp) == PLUS
+ && CONSTANT_ADDRESS_P (XEXP (temp, 1))
+ && (XEXP (temp, 0) == virtual_stack_vars_rtx
+ || XEXP (temp, 0) == virtual_incoming_args_rtx)))
+ {
+ /* This MEM may be shared. If the substitution can be done without
+ the need to generate new pseudos, we want to do it in place
+ so all copies of the shared rtx benefit. The call below will
+ only make substitutions if the resulting address is still
+ valid.
+
+ Note that we cannot pass X as the object in the recursive call
+ since the insn being processed may not allow all valid
+ addresses. However, if we were not passed on object, we can
+ only modify X without copying it if X will have a valid
+ address.
+
+ ??? Also note that this can still lose if OBJECT is an insn that
+ has less restrictions on an address that some other insn.
+ In that case, we will modify the shared address. This case
+ doesn't seem very likely, though. */
+
+ if (instantiate_virtual_regs_1 (&XEXP (x, 0),
+ object ? object : x, 0))
+ return 1;
+
+ /* Otherwise make a copy and process that copy. We copy the entire
+ RTL expression since it might be a PLUS which could also be
+ shared. */
+ *loc = x = copy_rtx (x);
+ }
+
+ /* Fall through to generic unary operation case. */
+ case USE:
+ case CLOBBER:
+ case SUBREG:
+ case STRICT_LOW_PART:
+ case NEG: case NOT:
+ case PRE_DEC: case PRE_INC: case POST_DEC: case POST_INC:
+ case SIGN_EXTEND: case ZERO_EXTEND:
+ case TRUNCATE: case FLOAT_EXTEND: case FLOAT_TRUNCATE:
+ case FLOAT: case FIX:
+ case UNSIGNED_FIX: case UNSIGNED_FLOAT:
+ case ABS:
+ case SQRT:
+ case FFS:
+ /* These case either have just one operand or we know that we need not
+ check the rest of the operands. */
+ loc = &XEXP (x, 0);
+ goto restart;
+
+ case REG:
+ /* Try to replace with a PLUS. If that doesn't work, compute the sum
+ in front of this insn and substitute the temporary. */
+ if (x == virtual_incoming_args_rtx)
+ new = arg_pointer_rtx, offset = in_arg_offset;
+ else if (x == virtual_stack_vars_rtx)
+ new = frame_pointer_rtx, offset = var_offset;
+ else if (x == virtual_stack_dynamic_rtx)
+ new = stack_pointer_rtx, offset = dynamic_offset;
+ else if (x == virtual_outgoing_args_rtx)
+ new = stack_pointer_rtx, offset = out_arg_offset;
+
+ if (new)
+ {
+ temp = plus_constant (new, offset);
+ if (!validate_change (object, loc, temp, 0))
+ {
+ if (! extra_insns)
+ return 0;
+
+ start_sequence ();
+ temp = force_operand (temp, NULL_RTX);
+ seq = get_insns ();
+ end_sequence ();
+
+ emit_insns_before (seq, object);
+ if (! validate_change (object, loc, temp, 0)
+ && ! validate_replace_rtx (x, temp, object))
+ abort ();
+ }
+ }
+
+ return 1;
+ }
+
+ /* Scan all subexpressions. */
+ fmt = GET_RTX_FORMAT (code);
+ for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
+ if (*fmt == 'e')
+ {
+ if (!instantiate_virtual_regs_1 (&XEXP (x, i), object, extra_insns))
+ return 0;
+ }
+ else if (*fmt == 'E')
+ for (j = 0; j < XVECLEN (x, i); j++)
+ if (! instantiate_virtual_regs_1 (&XVECEXP (x, i, j), object,
+ extra_insns))
+ return 0;
+
+ return 1;
+}
+
+/* Optimization: assuming this function does not receive nonlocal gotos,
+ delete the handlers for such, as well as the insns to establish
+ and disestablish them. */
+
+static void
+delete_handlers ()
+{
+ rtx insn;
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ /* Delete the handler by turning off the flag that would
+ prevent jump_optimize from deleting it.
+ Also permit deletion of the nonlocal labels themselves
+ if nothing local refers to them. */
+ if (GET_CODE (insn) == CODE_LABEL)
+ LABEL_PRESERVE_P (insn) = 0;
+ if (GET_CODE (insn) == INSN
+ && ((nonlocal_goto_handler_slot != 0
+ && reg_mentioned_p (nonlocal_goto_handler_slot, PATTERN (insn)))
+ || (nonlocal_goto_stack_level != 0
+ && reg_mentioned_p (nonlocal_goto_stack_level,
+ PATTERN (insn)))))
+ delete_insn (insn);
+ }
+}
+
+/* Return a list (chain of EXPR_LIST nodes) for the nonlocal labels
+ of the current function. */
+
+rtx
+nonlocal_label_rtx_list ()
+{
+ tree t;
+ rtx x = 0;
+
+ for (t = nonlocal_labels; t; t = TREE_CHAIN (t))
+ x = gen_rtx (EXPR_LIST, VOIDmode, label_rtx (TREE_VALUE (t)), x);
+
+ return x;
+}
+
+/* Output a USE for any register use in RTL.
+ This is used with -noreg to mark the extent of lifespan
+ of any registers used in a user-visible variable's DECL_RTL. */
+
+void
+use_variable (rtl)
+ rtx rtl;
+{
+ if (GET_CODE (rtl) == REG)
+ /* This is a register variable. */
+ emit_insn (gen_rtx (USE, VOIDmode, rtl));
+ else if (GET_CODE (rtl) == MEM
+ && GET_CODE (XEXP (rtl, 0)) == REG
+ && (REGNO (XEXP (rtl, 0)) < FIRST_VIRTUAL_REGISTER
+ || REGNO (XEXP (rtl, 0)) > LAST_VIRTUAL_REGISTER)
+ && XEXP (rtl, 0) != current_function_internal_arg_pointer)
+ /* This is a variable-sized structure. */
+ emit_insn (gen_rtx (USE, VOIDmode, XEXP (rtl, 0)));
+}
+
+/* Like use_variable except that it outputs the USEs after INSN
+ instead of at the end of the insn-chain. */
+
+void
+use_variable_after (rtl, insn)
+ rtx rtl, insn;
+{
+ if (GET_CODE (rtl) == REG)
+ /* This is a register variable. */
+ emit_insn_after (gen_rtx (USE, VOIDmode, rtl), insn);
+ else if (GET_CODE (rtl) == MEM
+ && GET_CODE (XEXP (rtl, 0)) == REG
+ && (REGNO (XEXP (rtl, 0)) < FIRST_VIRTUAL_REGISTER
+ || REGNO (XEXP (rtl, 0)) > LAST_VIRTUAL_REGISTER)
+ && XEXP (rtl, 0) != current_function_internal_arg_pointer)
+ /* This is a variable-sized structure. */
+ emit_insn_after (gen_rtx (USE, VOIDmode, XEXP (rtl, 0)), insn);
+}
+
+int
+max_parm_reg_num ()
+{
+ return max_parm_reg;
+}
+
+/* Return the first insn following those generated by `assign_parms'. */
+
+rtx
+get_first_nonparm_insn ()
+{
+ if (last_parm_insn)
+ return NEXT_INSN (last_parm_insn);
+ return get_insns ();
+}
+
+/* Return the first NOTE_INSN_BLOCK_BEG note in the function.
+ Crash if there is none. */
+
+rtx
+get_first_block_beg ()
+{
+ register rtx searcher;
+ register rtx insn = get_first_nonparm_insn ();
+
+ for (searcher = insn; searcher; searcher = NEXT_INSN (searcher))
+ if (GET_CODE (searcher) == NOTE
+ && NOTE_LINE_NUMBER (searcher) == NOTE_INSN_BLOCK_BEG)
+ return searcher;
+
+ abort (); /* Invalid call to this function. (See comments above.) */
+ return NULL_RTX;
+}
+
+/* Return 1 if EXP is an aggregate type (or a value with aggregate type).
+ This means a type for which function calls must pass an address to the
+ function or get an address back from the function.
+ EXP may be a type node or an expression (whose type is tested). */
+
+int
+aggregate_value_p (exp)
+ tree exp;
+{
+ int i, regno, nregs;
+ rtx reg;
+ tree type;
+ if (TREE_CODE_CLASS (TREE_CODE (exp)) == 't')
+ type = exp;
+ else
+ type = TREE_TYPE (exp);
+
+ if (RETURN_IN_MEMORY (type))
+ return 1;
+ if (flag_pcc_struct_return && AGGREGATE_TYPE_P (type))
+ return 1;
+ /* Make sure we have suitable call-clobbered regs to return
+ the value in; if not, we must return it in memory. */
+ reg = hard_function_value (type, 0);
+ regno = REGNO (reg);
+ nregs = HARD_REGNO_NREGS (regno, TYPE_MODE (type));
+ for (i = 0; i < nregs; i++)
+ if (! call_used_regs[regno + i])
+ return 1;
+ return 0;
+}
+
+/* Assign RTL expressions to the function's parameters.
+ This may involve copying them into registers and using
+ those registers as the RTL for them.
+
+ If SECOND_TIME is non-zero it means that this function is being
+ called a second time. This is done by integrate.c when a function's
+ compilation is deferred. We need to come back here in case the
+ FUNCTION_ARG macro computes items needed for the rest of the compilation
+ (such as changing which registers are fixed or caller-saved). But suppress
+ writing any insns or setting DECL_RTL of anything in this case. */
+
+void
+assign_parms (fndecl, second_time)
+ tree fndecl;
+ int second_time;
+{
+ register tree parm;
+ register rtx entry_parm = 0;
+ register rtx stack_parm = 0;
+ CUMULATIVE_ARGS args_so_far;
+ enum machine_mode promoted_mode, passed_mode, nominal_mode;
+ int unsignedp;
+ /* Total space needed so far for args on the stack,
+ given as a constant and a tree-expression. */
+ struct args_size stack_args_size;
+ tree fntype = TREE_TYPE (fndecl);
+ tree fnargs = DECL_ARGUMENTS (fndecl);
+ /* This is used for the arg pointer when referring to stack args. */
+ rtx internal_arg_pointer;
+ /* This is a dummy PARM_DECL that we used for the function result if
+ the function returns a structure. */
+ tree function_result_decl = 0;
+ int nparmregs = list_length (fnargs) + LAST_VIRTUAL_REGISTER + 1;
+ int varargs_setup = 0;
+ rtx conversion_insns = 0;
+ /* FUNCTION_ARG may look at this variable. Since this is not
+ expanding a call it will always be zero in this function. */
+ int current_call_is_indirect = 0;
+
+ /* Nonzero if the last arg is named `__builtin_va_alist',
+ which is used on some machines for old-fashioned non-ANSI varargs.h;
+ this should be stuck onto the stack as if it had arrived there. */
+ int hide_last_arg
+ = (current_function_varargs
+ && fnargs
+ && (parm = tree_last (fnargs)) != 0
+ && DECL_NAME (parm)
+ && (! strcmp (IDENTIFIER_POINTER (DECL_NAME (parm)),
+ "__builtin_va_alist")));
+
+ /* Nonzero if function takes extra anonymous args.
+ This means the last named arg must be on the stack
+ right before the anonymous ones. */
+ int stdarg
+ = (TYPE_ARG_TYPES (fntype) != 0
+ && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
+ != void_type_node));
+
+ /* If the reg that the virtual arg pointer will be translated into is
+ not a fixed reg or is the stack pointer, make a copy of the virtual
+ arg pointer, and address parms via the copy. The frame pointer is
+ considered fixed even though it is not marked as such.
+
+ The second time through, simply use ap to avoid generating rtx. */
+
+ if ((ARG_POINTER_REGNUM == STACK_POINTER_REGNUM
+ || ! (fixed_regs[ARG_POINTER_REGNUM]
+ || ARG_POINTER_REGNUM == FRAME_POINTER_REGNUM))
+ && ! second_time)
+ internal_arg_pointer = copy_to_reg (virtual_incoming_args_rtx);
+ else
+ internal_arg_pointer = virtual_incoming_args_rtx;
+ current_function_internal_arg_pointer = internal_arg_pointer;
+
+ stack_args_size.constant = 0;
+ stack_args_size.var = 0;
+
+ /* If struct value address is treated as the first argument, make it so. */
+ if (aggregate_value_p (DECL_RESULT (fndecl))
+ && ! current_function_returns_pcc_struct
+ && struct_value_incoming_rtx == 0)
+ {
+ tree type = build_pointer_type (fntype);
+
+ function_result_decl = build_decl (PARM_DECL, NULL_TREE, type);
+
+ DECL_ARG_TYPE (function_result_decl) = type;
+ TREE_CHAIN (function_result_decl) = fnargs;
+ fnargs = function_result_decl;
+ }
+
+ parm_reg_stack_loc = (rtx *) oballoc (nparmregs * sizeof (rtx));
+ bzero ((char *) parm_reg_stack_loc, nparmregs * sizeof (rtx));
+
+#ifdef INIT_CUMULATIVE_INCOMING_ARGS
+ INIT_CUMULATIVE_INCOMING_ARGS (args_so_far, fntype, NULL_RTX);
+#else
+ INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX);
+#endif
+
+ /* We haven't yet found an argument that we must push and pretend the
+ caller did. */
+ current_function_pretend_args_size = 0;
+
+ for (parm = fnargs; parm; parm = TREE_CHAIN (parm))
+ {
+ int aggregate = AGGREGATE_TYPE_P (TREE_TYPE (parm));
+ struct args_size stack_offset;
+ struct args_size arg_size;
+ int passed_pointer = 0;
+ tree passed_type = DECL_ARG_TYPE (parm);
+
+ /* Set LAST_NAMED if this is last named arg before some
+ anonymous args. We treat it as if it were anonymous too. */
+ int last_named = ((TREE_CHAIN (parm) == 0
+ || DECL_NAME (TREE_CHAIN (parm)) == 0)
+ && (stdarg || current_function_varargs));
+
+ if (TREE_TYPE (parm) == error_mark_node
+ /* This can happen after weird syntax errors
+ or if an enum type is defined among the parms. */
+ || TREE_CODE (parm) != PARM_DECL
+ || passed_type == NULL)
+ {
+ DECL_INCOMING_RTL (parm) = DECL_RTL (parm) = gen_rtx (MEM, BLKmode,
+ const0_rtx);
+ TREE_USED (parm) = 1;
+ continue;
+ }
+
+ /* For varargs.h function, save info about regs and stack space
+ used by the individual args, not including the va_alist arg. */
+ if (hide_last_arg && last_named)
+ current_function_args_info = args_so_far;
+
+ /* Find mode of arg as it is passed, and mode of arg
+ as it should be during execution of this function. */
+ passed_mode = TYPE_MODE (passed_type);
+ nominal_mode = TYPE_MODE (TREE_TYPE (parm));
+
+ /* If the parm's mode is VOID, its value doesn't matter,
+ and avoid the usual things like emit_move_insn that could crash. */
+ if (nominal_mode == VOIDmode)
+ {
+ DECL_INCOMING_RTL (parm) = DECL_RTL (parm) = const0_rtx;
+ continue;
+ }
+
+ /* See if this arg was passed by invisible reference. It is if
+ it is an object whose size depends on the contents of the
+ object itself or if the machine requires these objects be passed
+ that way. */
+
+ if ((TREE_CODE (TYPE_SIZE (passed_type)) != INTEGER_CST
+ && contains_placeholder_p (TYPE_SIZE (passed_type)))
+ || TYPE_NEEDS_CONSTRUCTING (passed_type)
+#ifdef FUNCTION_ARG_PASS_BY_REFERENCE
+ || FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, passed_mode,
+ passed_type, ! last_named)
+#endif
+ )
+ {
+ passed_type = build_pointer_type (passed_type);
+ passed_pointer = 1;
+ passed_mode = nominal_mode = Pmode;
+ }
+
+ promoted_mode = passed_mode;
+
+#ifdef PROMOTE_FUNCTION_ARGS
+ /* Compute the mode in which the arg is actually extended to. */
+ promoted_mode = promote_mode (passed_type, promoted_mode, &unsignedp, 1);
+#endif
+
+ /* Let machine desc say which reg (if any) the parm arrives in.
+ 0 means it arrives on the stack. */
+#ifdef FUNCTION_INCOMING_ARG
+ entry_parm = FUNCTION_INCOMING_ARG (args_so_far, promoted_mode,
+ passed_type, ! last_named);
+#else
+ entry_parm = FUNCTION_ARG (args_so_far, promoted_mode,
+ passed_type, ! last_named);
+#endif
+
+ if (entry_parm)
+ passed_mode = promoted_mode;
+
+#ifdef SETUP_INCOMING_VARARGS
+ /* If this is the last named parameter, do any required setup for
+ varargs or stdargs. We need to know about the case of this being an
+ addressable type, in which case we skip the registers it
+ would have arrived in.
+
+ For stdargs, LAST_NAMED will be set for two parameters, the one that
+ is actually the last named, and the dummy parameter. We only
+ want to do this action once.
+
+ Also, indicate when RTL generation is to be suppressed. */
+ if (last_named && !varargs_setup)
+ {
+ SETUP_INCOMING_VARARGS (args_so_far, passed_mode, passed_type,
+ current_function_pretend_args_size,
+ second_time);
+ varargs_setup = 1;
+ }
+#endif
+
+ /* Determine parm's home in the stack,
+ in case it arrives in the stack or we should pretend it did.
+
+ Compute the stack position and rtx where the argument arrives
+ and its size.
+
+ There is one complexity here: If this was a parameter that would
+ have been passed in registers, but wasn't only because it is
+ __builtin_va_alist, we want locate_and_pad_parm to treat it as if
+ it came in a register so that REG_PARM_STACK_SPACE isn't skipped.
+ In this case, we call FUNCTION_ARG with NAMED set to 1 instead of
+ 0 as it was the previous time. */
+
+ locate_and_pad_parm (passed_mode, passed_type,
+#ifdef STACK_PARMS_IN_REG_PARM_AREA
+ 1,
+#else
+#ifdef FUNCTION_INCOMING_ARG
+ FUNCTION_INCOMING_ARG (args_so_far, passed_mode,
+ passed_type,
+ (! last_named
+ || varargs_setup)) != 0,
+#else
+ FUNCTION_ARG (args_so_far, passed_mode,
+ passed_type,
+ ! last_named || varargs_setup) != 0,
+#endif
+#endif
+ fndecl, &stack_args_size, &stack_offset, &arg_size);
+
+ if (! second_time)
+ {
+ rtx offset_rtx = ARGS_SIZE_RTX (stack_offset);
+
+ if (offset_rtx == const0_rtx)
+ stack_parm = gen_rtx (MEM, passed_mode, internal_arg_pointer);
+ else
+ stack_parm = gen_rtx (MEM, passed_mode,
+ gen_rtx (PLUS, Pmode,
+ internal_arg_pointer, offset_rtx));
+
+ /* If this is a memory ref that contains aggregate components,
+ mark it as such for cse and loop optimize. */
+ MEM_IN_STRUCT_P (stack_parm) = aggregate;
+ }
+
+ /* If this parameter was passed both in registers and in the stack,
+ use the copy on the stack. */
+ if (MUST_PASS_IN_STACK (passed_mode, passed_type))
+ entry_parm = 0;
+
+#ifdef FUNCTION_ARG_PARTIAL_NREGS
+ /* If this parm was passed part in regs and part in memory,
+ pretend it arrived entirely in memory
+ by pushing the register-part onto the stack.
+
+ In the special case of a DImode or DFmode that is split,
+ we could put it together in a pseudoreg directly,
+ but for now that's not worth bothering with. */
+
+ if (entry_parm)
+ {
+ int nregs = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, passed_mode,
+ passed_type, ! last_named);
+
+ if (nregs > 0)
+ {
+ current_function_pretend_args_size
+ = (((nregs * UNITS_PER_WORD) + (PARM_BOUNDARY / BITS_PER_UNIT) - 1)
+ / (PARM_BOUNDARY / BITS_PER_UNIT)
+ * (PARM_BOUNDARY / BITS_PER_UNIT));
+
+ if (! second_time)
+ move_block_from_reg (REGNO (entry_parm),
+ validize_mem (stack_parm), nregs,
+ int_size_in_bytes (TREE_TYPE (parm)));
+ entry_parm = stack_parm;
+ }
+ }
+#endif
+
+ /* If we didn't decide this parm came in a register,
+ by default it came on the stack. */
+ if (entry_parm == 0)
+ entry_parm = stack_parm;
+
+ /* Record permanently how this parm was passed. */
+ if (! second_time)
+ DECL_INCOMING_RTL (parm) = entry_parm;
+
+ /* If there is actually space on the stack for this parm,
+ count it in stack_args_size; otherwise set stack_parm to 0
+ to indicate there is no preallocated stack slot for the parm. */
+
+ if (entry_parm == stack_parm
+#if defined (REG_PARM_STACK_SPACE) && ! defined (MAYBE_REG_PARM_STACK_SPACE)
+ /* On some machines, even if a parm value arrives in a register
+ there is still an (uninitialized) stack slot allocated for it.
+
+ ??? When MAYBE_REG_PARM_STACK_SPACE is defined, we can't tell
+ whether this parameter already has a stack slot allocated,
+ because an arg block exists only if current_function_args_size
+ is larger than some threshhold, and we haven't calculated that
+ yet. So, for now, we just assume that stack slots never exist
+ in this case. */
+ || REG_PARM_STACK_SPACE (fndecl) > 0
+#endif
+ )
+ {
+ stack_args_size.constant += arg_size.constant;
+ if (arg_size.var)
+ ADD_PARM_SIZE (stack_args_size, arg_size.var);
+ }
+ else
+ /* No stack slot was pushed for this parm. */
+ stack_parm = 0;
+
+ /* Update info on where next arg arrives in registers. */
+
+ FUNCTION_ARG_ADVANCE (args_so_far, passed_mode,
+ passed_type, ! last_named);
+
+ /* If this is our second time through, we are done with this parm. */
+ if (second_time)
+ continue;
+
+ /* If we can't trust the parm stack slot to be aligned enough
+ for its ultimate type, don't use that slot after entry.
+ We'll make another stack slot, if we need one. */
+ {
+ int thisparm_boundary
+ = FUNCTION_ARG_BOUNDARY (passed_mode, passed_type);
+
+ if (GET_MODE_ALIGNMENT (nominal_mode) > thisparm_boundary)
+ stack_parm = 0;
+ }
+
+ /* If parm was passed in memory, and we need to convert it on entry,
+ don't store it back in that same slot. */
+ if (entry_parm != 0
+ && nominal_mode != BLKmode && nominal_mode != passed_mode)
+ stack_parm = 0;
+
+#if 0
+ /* Now adjust STACK_PARM to the mode and precise location
+ where this parameter should live during execution,
+ if we discover that it must live in the stack during execution.
+ To make debuggers happier on big-endian machines, we store
+ the value in the last bytes of the space available. */
+
+ if (nominal_mode != BLKmode && nominal_mode != passed_mode
+ && stack_parm != 0)
+ {
+ rtx offset_rtx;
+
+#if BYTES_BIG_ENDIAN
+ if (GET_MODE_SIZE (nominal_mode) < UNITS_PER_WORD)
+ stack_offset.constant += (GET_MODE_SIZE (passed_mode)
+ - GET_MODE_SIZE (nominal_mode));
+#endif
+
+ offset_rtx = ARGS_SIZE_RTX (stack_offset);
+ if (offset_rtx == const0_rtx)
+ stack_parm = gen_rtx (MEM, nominal_mode, internal_arg_pointer);
+ else
+ stack_parm = gen_rtx (MEM, nominal_mode,
+ gen_rtx (PLUS, Pmode,
+ internal_arg_pointer, offset_rtx));
+
+ /* If this is a memory ref that contains aggregate components,
+ mark it as such for cse and loop optimize. */
+ MEM_IN_STRUCT_P (stack_parm) = aggregate;
+ }
+#endif /* 0 */
+
+ /* ENTRY_PARM is an RTX for the parameter as it arrives,
+ in the mode in which it arrives.
+ STACK_PARM is an RTX for a stack slot where the parameter can live
+ during the function (in case we want to put it there).
+ STACK_PARM is 0 if no stack slot was pushed for it.
+
+ Now output code if necessary to convert ENTRY_PARM to
+ the type in which this function declares it,
+ and store that result in an appropriate place,
+ which may be a pseudo reg, may be STACK_PARM,
+ or may be a local stack slot if STACK_PARM is 0.
+
+ Set DECL_RTL to that place. */
+
+ if (nominal_mode == BLKmode)
+ {
+ /* If a BLKmode arrives in registers, copy it to a stack slot. */
+ if (GET_CODE (entry_parm) == REG)
+ {
+ int size_stored = CEIL_ROUND (int_size_in_bytes (TREE_TYPE (parm)),
+ UNITS_PER_WORD);
+
+ /* Note that we will be storing an integral number of words.
+ So we have to be careful to ensure that we allocate an
+ integral number of words. We do this below in the
+ assign_stack_local if space was not allocated in the argument
+ list. If it was, this will not work if PARM_BOUNDARY is not
+ a multiple of BITS_PER_WORD. It isn't clear how to fix this
+ if it becomes a problem. */
+
+ if (stack_parm == 0)
+ {
+ stack_parm
+ = assign_stack_local (GET_MODE (entry_parm), size_stored, 0);
+ /* If this is a memory ref that contains aggregate components,
+ mark it as such for cse and loop optimize. */
+ MEM_IN_STRUCT_P (stack_parm) = aggregate;
+ }
+
+ else if (PARM_BOUNDARY % BITS_PER_WORD != 0)
+ abort ();
+
+ move_block_from_reg (REGNO (entry_parm),
+ validize_mem (stack_parm),
+ size_stored / UNITS_PER_WORD,
+ int_size_in_bytes (TREE_TYPE (parm)));
+ }
+ DECL_RTL (parm) = stack_parm;
+ }
+ else if (! ((obey_regdecls && ! DECL_REGISTER (parm)
+ && ! DECL_INLINE (fndecl))
+ /* layout_decl may set this. */
+ || TREE_ADDRESSABLE (parm)
+ || TREE_SIDE_EFFECTS (parm)
+ /* If -ffloat-store specified, don't put explicit
+ float variables into registers. */
+ || (flag_float_store
+ && TREE_CODE (TREE_TYPE (parm)) == REAL_TYPE))
+ /* Always assign pseudo to structure return or item passed
+ by invisible reference. */
+ || passed_pointer || parm == function_result_decl)
+ {
+ /* Store the parm in a pseudoregister during the function, but we
+ may need to do it in a wider mode. */
+
+ register rtx parmreg;
+ int regno, regnoi, regnor;
+
+ unsignedp = TREE_UNSIGNED (TREE_TYPE (parm));
+ nominal_mode = promote_mode (TREE_TYPE (parm), nominal_mode,
+ &unsignedp, 1);
+
+ parmreg = gen_reg_rtx (nominal_mode);
+ REG_USERVAR_P (parmreg) = 1;
+
+ /* If this was an item that we received a pointer to, set DECL_RTL
+ appropriately. */
+ if (passed_pointer)
+ {
+ DECL_RTL (parm) = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (passed_type)), parmreg);
+ MEM_IN_STRUCT_P (DECL_RTL (parm)) = aggregate;
+ }
+ else
+ DECL_RTL (parm) = parmreg;
+
+ /* Copy the value into the register. */
+ if (GET_MODE (parmreg) != GET_MODE (entry_parm))
+ {
+ /* If ENTRY_PARM is a hard register, it might be in a register
+ not valid for operating in its mode (e.g., an odd-numbered
+ register for a DFmode). In that case, moves are the only
+ thing valid, so we can't do a convert from there. This
+ occurs when the calling sequence allow such misaligned
+ usages.
+
+ In addition, the conversion may involve a call, which could
+ clobber parameters which haven't been copied to pseudo
+ registers yet. Therefore, we must first copy the parm to
+ a pseudo reg here, and save the conversion until after all
+ parameters have been moved. */
+
+ rtx tempreg = gen_reg_rtx (GET_MODE (entry_parm));
+
+ emit_move_insn (tempreg, validize_mem (entry_parm));
+
+ push_to_sequence (conversion_insns);
+ convert_move (parmreg, tempreg, unsignedp);
+ conversion_insns = get_insns ();
+ end_sequence ();
+ }
+ else
+ emit_move_insn (parmreg, validize_mem (entry_parm));
+
+ /* If we were passed a pointer but the actual value
+ can safely live in a register, put it in one. */
+ if (passed_pointer && TYPE_MODE (TREE_TYPE (parm)) != BLKmode
+ && ! ((obey_regdecls && ! DECL_REGISTER (parm)
+ && ! DECL_INLINE (fndecl))
+ /* layout_decl may set this. */
+ || TREE_ADDRESSABLE (parm)
+ || TREE_SIDE_EFFECTS (parm)
+ /* If -ffloat-store specified, don't put explicit
+ float variables into registers. */
+ || (flag_float_store
+ && TREE_CODE (TREE_TYPE (parm)) == REAL_TYPE)))
+ {
+ /* We can't use nominal_mode, because it will have been set to
+ Pmode above. We must use the actual mode of the parm. */
+ parmreg = gen_reg_rtx (TYPE_MODE (TREE_TYPE (parm)));
+ REG_USERVAR_P (parmreg) = 1;
+ emit_move_insn (parmreg, DECL_RTL (parm));
+ DECL_RTL (parm) = parmreg;
+ /* STACK_PARM is the pointer, not the parm, and PARMREG is
+ now the parm. */
+ stack_parm = 0;
+ }
+#ifdef FUNCTION_ARG_CALLEE_COPIES
+ /* If we are passed an arg by reference and it is our responsibility
+ to make a copy, do it now.
+ PASSED_TYPE and PASSED mode now refer to the pointer, not the
+ original argument, so we must recreate them in the call to
+ FUNCTION_ARG_CALLEE_COPIES. */
+ /* ??? Later add code to handle the case that if the argument isn't
+ modified, don't do the copy. */
+
+ else if (passed_pointer
+ && FUNCTION_ARG_CALLEE_COPIES (args_so_far,
+ TYPE_MODE (DECL_ARG_TYPE (parm)),
+ DECL_ARG_TYPE (parm),
+ ! last_named))
+ {
+ rtx copy;
+ tree type = DECL_ARG_TYPE (parm);
+
+ /* This sequence may involve a library call perhaps clobbering
+ registers that haven't been copied to pseudos yet. */
+
+ push_to_sequence (conversion_insns);
+
+ if (TYPE_SIZE (type) == 0
+ || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+ /* This is a variable sized object. */
+ copy = gen_rtx (MEM, BLKmode,
+ allocate_dynamic_stack_space
+ (expr_size (parm), NULL_RTX,
+ TYPE_ALIGN (type)));
+ else
+ copy = assign_stack_temp (TYPE_MODE (type),
+ int_size_in_bytes (type), 1);
+
+ store_expr (parm, copy, 0);
+ emit_move_insn (parmreg, XEXP (copy, 0));
+ conversion_insns = get_insns ();
+ end_sequence ();
+ }
+#endif /* FUNCTION_ARG_CALLEE_COPIES */
+
+ /* In any case, record the parm's desired stack location
+ in case we later discover it must live in the stack.
+
+ If it is a COMPLEX value, store the stack location for both
+ halves. */
+
+ if (GET_CODE (parmreg) == CONCAT)
+ regno = MAX (REGNO (XEXP (parmreg, 0)), REGNO (XEXP (parmreg, 1)));
+ else
+ regno = REGNO (parmreg);
+
+ if (regno >= nparmregs)
+ {
+ rtx *new;
+ int old_nparmregs = nparmregs;
+
+ nparmregs = regno + 5;
+ new = (rtx *) oballoc (nparmregs * sizeof (rtx));
+ bcopy ((char *) parm_reg_stack_loc, (char *) new,
+ old_nparmregs * sizeof (rtx));
+ bzero ((char *) (new + old_nparmregs),
+ (nparmregs - old_nparmregs) * sizeof (rtx));
+ parm_reg_stack_loc = new;
+ }
+
+ if (GET_CODE (parmreg) == CONCAT)
+ {
+ enum machine_mode submode = GET_MODE (XEXP (parmreg, 0));
+
+ regnor = REGNO (gen_realpart (submode, parmreg));
+ regnoi = REGNO (gen_imagpart (submode, parmreg));
+
+ if (stack_parm != 0)
+ {
+ parm_reg_stack_loc[regnor]
+ = gen_realpart (submode, stack_parm);
+ parm_reg_stack_loc[regnoi]
+ = gen_imagpart (submode, stack_parm);
+ }
+ else
+ {
+ parm_reg_stack_loc[regnor] = 0;
+ parm_reg_stack_loc[regnoi] = 0;
+ }
+ }
+ else
+ parm_reg_stack_loc[REGNO (parmreg)] = stack_parm;
+
+ /* Mark the register as eliminable if we did no conversion
+ and it was copied from memory at a fixed offset,
+ and the arg pointer was not copied to a pseudo-reg.
+ If the arg pointer is a pseudo reg or the offset formed
+ an invalid address, such memory-equivalences
+ as we make here would screw up life analysis for it. */
+ if (nominal_mode == passed_mode
+ && ! conversion_insns
+ && GET_CODE (entry_parm) == MEM
+ && entry_parm == stack_parm
+ && stack_offset.var == 0
+ && reg_mentioned_p (virtual_incoming_args_rtx,
+ XEXP (entry_parm, 0)))
+ {
+ rtx linsn = get_last_insn ();
+
+ /* Mark complex types separately. */
+ if (GET_CODE (parmreg) == CONCAT)
+ {
+ REG_NOTES (linsn)
+ = gen_rtx (EXPR_LIST, REG_EQUIV,
+ parm_reg_stack_loc[regnoi], REG_NOTES (linsn));
+
+ /* Now search backward for where we set the real part. */
+ for (; linsn != 0
+ && ! reg_referenced_p (parm_reg_stack_loc[regnor],
+ PATTERN (linsn));
+ linsn = prev_nonnote_insn (linsn))
+ ;
+
+ REG_NOTES (linsn)
+ = gen_rtx (EXPR_LIST, REG_EQUIV,
+ parm_reg_stack_loc[regnor], REG_NOTES (linsn));
+ }
+ else
+ REG_NOTES (linsn)
+ = gen_rtx (EXPR_LIST, REG_EQUIV,
+ entry_parm, REG_NOTES (linsn));
+ }
+
+ /* For pointer data type, suggest pointer register. */
+ if (TREE_CODE (TREE_TYPE (parm)) == POINTER_TYPE)
+ mark_reg_pointer (parmreg);
+ }
+ else
+ {
+ /* Value must be stored in the stack slot STACK_PARM
+ during function execution. */
+
+ if (passed_mode != nominal_mode)
+ {
+ /* Conversion is required. */
+ rtx tempreg = gen_reg_rtx (GET_MODE (entry_parm));
+
+ emit_move_insn (tempreg, validize_mem (entry_parm));
+
+ push_to_sequence (conversion_insns);
+ entry_parm = convert_to_mode (nominal_mode, tempreg,
+ TREE_UNSIGNED (TREE_TYPE (parm)));
+ conversion_insns = get_insns ();
+ end_sequence ();
+ }
+
+ if (entry_parm != stack_parm)
+ {
+ if (stack_parm == 0)
+ {
+ stack_parm
+ = assign_stack_local (GET_MODE (entry_parm),
+ GET_MODE_SIZE (GET_MODE (entry_parm)), 0);
+ /* If this is a memory ref that contains aggregate components,
+ mark it as such for cse and loop optimize. */
+ MEM_IN_STRUCT_P (stack_parm) = aggregate;
+ }
+
+ if (passed_mode != nominal_mode)
+ {
+ push_to_sequence (conversion_insns);
+ emit_move_insn (validize_mem (stack_parm),
+ validize_mem (entry_parm));
+ conversion_insns = get_insns ();
+ end_sequence ();
+ }
+ else
+ emit_move_insn (validize_mem (stack_parm),
+ validize_mem (entry_parm));
+ }
+
+ DECL_RTL (parm) = stack_parm;
+ }
+
+ /* If this "parameter" was the place where we are receiving the
+ function's incoming structure pointer, set up the result. */
+ if (parm == function_result_decl)
+ {
+ tree result = DECL_RESULT (fndecl);
+ tree restype = TREE_TYPE (result);
+
+ DECL_RTL (result)
+ = gen_rtx (MEM, DECL_MODE (result), DECL_RTL (parm));
+
+ MEM_IN_STRUCT_P (DECL_RTL (result)) = AGGREGATE_TYPE_P (restype);
+ }
+
+ if (TREE_THIS_VOLATILE (parm))
+ MEM_VOLATILE_P (DECL_RTL (parm)) = 1;
+ if (TREE_READONLY (parm))
+ RTX_UNCHANGING_P (DECL_RTL (parm)) = 1;
+ }
+
+ /* Output all parameter conversion instructions (possibly including calls)
+ now that all parameters have been copied out of hard registers. */
+ emit_insns (conversion_insns);
+
+ max_parm_reg = max_reg_num ();
+ last_parm_insn = get_last_insn ();
+
+ current_function_args_size = stack_args_size.constant;
+
+ /* Adjust function incoming argument size for alignment and
+ minimum length. */
+
+#ifdef REG_PARM_STACK_SPACE
+#ifndef MAYBE_REG_PARM_STACK_SPACE
+ current_function_args_size = MAX (current_function_args_size,
+ REG_PARM_STACK_SPACE (fndecl));
+#endif
+#endif
+
+#ifdef STACK_BOUNDARY
+#define STACK_BYTES (STACK_BOUNDARY / BITS_PER_UNIT)
+
+ current_function_args_size
+ = ((current_function_args_size + STACK_BYTES - 1)
+ / STACK_BYTES) * STACK_BYTES;
+#endif
+
+#ifdef ARGS_GROW_DOWNWARD
+ current_function_arg_offset_rtx
+ = (stack_args_size.var == 0 ? GEN_INT (-stack_args_size.constant)
+ : expand_expr (size_binop (MINUS_EXPR, stack_args_size.var,
+ size_int (-stack_args_size.constant)),
+ NULL_RTX, VOIDmode, 0));
+#else
+ current_function_arg_offset_rtx = ARGS_SIZE_RTX (stack_args_size);
+#endif
+
+ /* See how many bytes, if any, of its args a function should try to pop
+ on return. */
+
+ current_function_pops_args = RETURN_POPS_ARGS (TREE_TYPE (fndecl),
+ current_function_args_size);
+
+ /* For stdarg.h function, save info about
+ regs and stack space used by the named args. */
+
+ if (!hide_last_arg)
+ current_function_args_info = args_so_far;
+
+ /* Set the rtx used for the function return value. Put this in its
+ own variable so any optimizers that need this information don't have
+ to include tree.h. Do this here so it gets done when an inlined
+ function gets output. */
+
+ current_function_return_rtx = DECL_RTL (DECL_RESULT (fndecl));
+}
+
+/* Indicate whether REGNO is an incoming argument to the current function
+ that was promoted to a wider mode. If so, return the RTX for the
+ register (to get its mode). PMODE and PUNSIGNEDP are set to the mode
+ that REGNO is promoted from and whether the promotion was signed or
+ unsigned. */
+
+#ifdef PROMOTE_FUNCTION_ARGS
+
+rtx
+promoted_input_arg (regno, pmode, punsignedp)
+ int regno;
+ enum machine_mode *pmode;
+ int *punsignedp;
+{
+ tree arg;
+
+ for (arg = DECL_ARGUMENTS (current_function_decl); arg;
+ arg = TREE_CHAIN (arg))
+ if (GET_CODE (DECL_INCOMING_RTL (arg)) == REG
+ && REGNO (DECL_INCOMING_RTL (arg)) == regno)
+ {
+ enum machine_mode mode = TYPE_MODE (TREE_TYPE (arg));
+ int unsignedp = TREE_UNSIGNED (TREE_TYPE (arg));
+
+ mode = promote_mode (TREE_TYPE (arg), mode, &unsignedp, 1);
+ if (mode == GET_MODE (DECL_INCOMING_RTL (arg))
+ && mode != DECL_MODE (arg))
+ {
+ *pmode = DECL_MODE (arg);
+ *punsignedp = unsignedp;
+ return DECL_INCOMING_RTL (arg);
+ }
+ }
+
+ return 0;
+}
+
+#endif
+
+/* Compute the size and offset from the start of the stacked arguments for a
+ parm passed in mode PASSED_MODE and with type TYPE.
+
+ INITIAL_OFFSET_PTR points to the current offset into the stacked
+ arguments.
+
+ The starting offset and size for this parm are returned in *OFFSET_PTR
+ and *ARG_SIZE_PTR, respectively.
+
+ IN_REGS is non-zero if the argument will be passed in registers. It will
+ never be set if REG_PARM_STACK_SPACE is not defined.
+
+ FNDECL is the function in which the argument was defined.
+
+ There are two types of rounding that are done. The first, controlled by
+ FUNCTION_ARG_BOUNDARY, forces the offset from the start of the argument
+ list to be aligned to the specific boundary (in bits). This rounding
+ affects the initial and starting offsets, but not the argument size.
+
+ The second, controlled by FUNCTION_ARG_PADDING and PARM_BOUNDARY,
+ optionally rounds the size of the parm to PARM_BOUNDARY. The
+ initial offset is not affected by this rounding, while the size always
+ is and the starting offset may be. */
+
+/* offset_ptr will be negative for ARGS_GROW_DOWNWARD case;
+ initial_offset_ptr is positive because locate_and_pad_parm's
+ callers pass in the total size of args so far as
+ initial_offset_ptr. arg_size_ptr is always positive.*/
+
+void
+locate_and_pad_parm (passed_mode, type, in_regs, fndecl,
+ initial_offset_ptr, offset_ptr, arg_size_ptr)
+ enum machine_mode passed_mode;
+ tree type;
+ int in_regs;
+ tree fndecl;
+ struct args_size *initial_offset_ptr;
+ struct args_size *offset_ptr;
+ struct args_size *arg_size_ptr;
+{
+ tree sizetree
+ = type ? size_in_bytes (type) : size_int (GET_MODE_SIZE (passed_mode));
+ enum direction where_pad = FUNCTION_ARG_PADDING (passed_mode, type);
+ int boundary = FUNCTION_ARG_BOUNDARY (passed_mode, type);
+ int boundary_in_bytes = boundary / BITS_PER_UNIT;
+ int reg_parm_stack_space = 0;
+
+#ifdef REG_PARM_STACK_SPACE
+ /* If we have found a stack parm before we reach the end of the
+ area reserved for registers, skip that area. */
+ if (! in_regs)
+ {
+#ifdef MAYBE_REG_PARM_STACK_SPACE
+ reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
+#else
+ reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
+#endif
+ if (reg_parm_stack_space > 0)
+ {
+ if (initial_offset_ptr->var)
+ {
+ initial_offset_ptr->var
+ = size_binop (MAX_EXPR, ARGS_SIZE_TREE (*initial_offset_ptr),
+ size_int (reg_parm_stack_space));
+ initial_offset_ptr->constant = 0;
+ }
+ else if (initial_offset_ptr->constant < reg_parm_stack_space)
+ initial_offset_ptr->constant = reg_parm_stack_space;
+ }
+ }
+#endif /* REG_PARM_STACK_SPACE */
+
+ arg_size_ptr->var = 0;
+ arg_size_ptr->constant = 0;
+
+#ifdef ARGS_GROW_DOWNWARD
+ if (initial_offset_ptr->var)
+ {
+ offset_ptr->constant = 0;
+ offset_ptr->var = size_binop (MINUS_EXPR, integer_zero_node,
+ initial_offset_ptr->var);
+ }
+ else
+ {
+ offset_ptr->constant = - initial_offset_ptr->constant;
+ offset_ptr->var = 0;
+ }
+ if (where_pad != none
+ && (TREE_CODE (sizetree) != INTEGER_CST
+ || ((TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY)))
+ sizetree = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT);
+ SUB_PARM_SIZE (*offset_ptr, sizetree);
+ if (where_pad != downward)
+ pad_to_arg_alignment (offset_ptr, boundary);
+ if (initial_offset_ptr->var)
+ {
+ arg_size_ptr->var = size_binop (MINUS_EXPR,
+ size_binop (MINUS_EXPR,
+ integer_zero_node,
+ initial_offset_ptr->var),
+ offset_ptr->var);
+ }
+ else
+ {
+ arg_size_ptr->constant = (- initial_offset_ptr->constant -
+ offset_ptr->constant);
+ }
+#else /* !ARGS_GROW_DOWNWARD */
+ pad_to_arg_alignment (initial_offset_ptr, boundary);
+ *offset_ptr = *initial_offset_ptr;
+
+#ifdef PUSH_ROUNDING
+ if (passed_mode != BLKmode)
+ sizetree = size_int (PUSH_ROUNDING (TREE_INT_CST_LOW (sizetree)));
+#endif
+
+ if (where_pad != none
+ && (TREE_CODE (sizetree) != INTEGER_CST
+ || ((TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY)))
+ sizetree = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT);
+
+ /* This must be done after rounding sizetree, so that it will subtract
+ the same value that we explicitly add below. */
+ if (where_pad == downward)
+ pad_below (offset_ptr, passed_mode, sizetree);
+ ADD_PARM_SIZE (*arg_size_ptr, sizetree);
+#endif /* ARGS_GROW_DOWNWARD */
+}
+
+/* Round the stack offset in *OFFSET_PTR up to a multiple of BOUNDARY.
+ BOUNDARY is measured in bits, but must be a multiple of a storage unit. */
+
+static void
+pad_to_arg_alignment (offset_ptr, boundary)
+ struct args_size *offset_ptr;
+ int boundary;
+{
+ int boundary_in_bytes = boundary / BITS_PER_UNIT;
+
+ if (boundary > BITS_PER_UNIT)
+ {
+ if (offset_ptr->var)
+ {
+ offset_ptr->var =
+#ifdef ARGS_GROW_DOWNWARD
+ round_down
+#else
+ round_up
+#endif
+ (ARGS_SIZE_TREE (*offset_ptr),
+ boundary / BITS_PER_UNIT);
+ offset_ptr->constant = 0; /*?*/
+ }
+ else
+ offset_ptr->constant =
+#ifdef ARGS_GROW_DOWNWARD
+ FLOOR_ROUND (offset_ptr->constant, boundary_in_bytes);
+#else
+ CEIL_ROUND (offset_ptr->constant, boundary_in_bytes);
+#endif
+ }
+}
+
+static void
+pad_below (offset_ptr, passed_mode, sizetree)
+ struct args_size *offset_ptr;
+ enum machine_mode passed_mode;
+ tree sizetree;
+{
+ if (passed_mode != BLKmode)
+ {
+ if (GET_MODE_BITSIZE (passed_mode) % PARM_BOUNDARY)
+ offset_ptr->constant
+ += (((GET_MODE_BITSIZE (passed_mode) + PARM_BOUNDARY - 1)
+ / PARM_BOUNDARY * PARM_BOUNDARY / BITS_PER_UNIT)
+ - GET_MODE_SIZE (passed_mode));
+ }
+ else
+ {
+ if (TREE_CODE (sizetree) != INTEGER_CST
+ || (TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY)
+ {
+ /* Round the size up to multiple of PARM_BOUNDARY bits. */
+ tree s2 = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT);
+ /* Add it in. */
+ ADD_PARM_SIZE (*offset_ptr, s2);
+ SUB_PARM_SIZE (*offset_ptr, sizetree);
+ }
+ }
+}
+
+static tree
+round_down (value, divisor)
+ tree value;
+ int divisor;
+{
+ return size_binop (MULT_EXPR,
+ size_binop (FLOOR_DIV_EXPR, value, size_int (divisor)),
+ size_int (divisor));
+}
+
+/* Walk the tree of blocks describing the binding levels within a function
+ and warn about uninitialized variables.
+ This is done after calling flow_analysis and before global_alloc
+ clobbers the pseudo-regs to hard regs. */
+
+void
+uninitialized_vars_warning (block)
+ tree block;
+{
+ register tree decl, sub;
+ for (decl = BLOCK_VARS (block); decl; decl = TREE_CHAIN (decl))
+ {
+ if (TREE_CODE (decl) == VAR_DECL
+ /* These warnings are unreliable for and aggregates
+ because assigning the fields one by one can fail to convince
+ flow.c that the entire aggregate was initialized.
+ Unions are troublesome because members may be shorter. */
+ && ! AGGREGATE_TYPE_P (TREE_TYPE (decl))
+ && DECL_RTL (decl) != 0
+ && GET_CODE (DECL_RTL (decl)) == REG
+ && regno_uninitialized (REGNO (DECL_RTL (decl))))
+ warning_with_decl (decl,
+ "`%s' might be used uninitialized in this function");
+ if (TREE_CODE (decl) == VAR_DECL
+ && DECL_RTL (decl) != 0
+ && GET_CODE (DECL_RTL (decl)) == REG
+ && regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl))))
+ warning_with_decl (decl,
+ "variable `%s' might be clobbered by `longjmp' or `vfork'");
+ }
+ for (sub = BLOCK_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub))
+ uninitialized_vars_warning (sub);
+}
+
+/* Do the appropriate part of uninitialized_vars_warning
+ but for arguments instead of local variables. */
+
+void
+setjmp_args_warning (block)
+ tree block;
+{
+ register tree decl;
+ for (decl = DECL_ARGUMENTS (current_function_decl);
+ decl; decl = TREE_CHAIN (decl))
+ if (DECL_RTL (decl) != 0
+ && GET_CODE (DECL_RTL (decl)) == REG
+ && regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl))))
+ warning_with_decl (decl, "argument `%s' might be clobbered by `longjmp' or `vfork'");
+}
+
+/* If this function call setjmp, put all vars into the stack
+ unless they were declared `register'. */
+
+void
+setjmp_protect (block)
+ tree block;
+{
+ register tree decl, sub;
+ for (decl = BLOCK_VARS (block); decl; decl = TREE_CHAIN (decl))
+ if ((TREE_CODE (decl) == VAR_DECL
+ || TREE_CODE (decl) == PARM_DECL)
+ && DECL_RTL (decl) != 0
+ && GET_CODE (DECL_RTL (decl)) == REG
+ /* If this variable came from an inline function, it must be
+ that it's life doesn't overlap the setjmp. If there was a
+ setjmp in the function, it would already be in memory. We
+ must exclude such variable because their DECL_RTL might be
+ set to strange things such as virtual_stack_vars_rtx. */
+ && ! DECL_FROM_INLINE (decl)
+ && (
+#ifdef NON_SAVING_SETJMP
+ /* If longjmp doesn't restore the registers,
+ don't put anything in them. */
+ NON_SAVING_SETJMP
+ ||
+#endif
+ ! DECL_REGISTER (decl)))
+ put_var_into_stack (decl);
+ for (sub = BLOCK_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub))
+ setjmp_protect (sub);
+}
+
+/* Like the previous function, but for args instead of local variables. */
+
+void
+setjmp_protect_args ()
+{
+ register tree decl, sub;
+ for (decl = DECL_ARGUMENTS (current_function_decl);
+ decl; decl = TREE_CHAIN (decl))
+ if ((TREE_CODE (decl) == VAR_DECL
+ || TREE_CODE (decl) == PARM_DECL)
+ && DECL_RTL (decl) != 0
+ && GET_CODE (DECL_RTL (decl)) == REG
+ && (
+ /* If longjmp doesn't restore the registers,
+ don't put anything in them. */
+#ifdef NON_SAVING_SETJMP
+ NON_SAVING_SETJMP
+ ||
+#endif
+ ! DECL_REGISTER (decl)))
+ put_var_into_stack (decl);
+}
+
+/* Return the context-pointer register corresponding to DECL,
+ or 0 if it does not need one. */
+
+rtx
+lookup_static_chain (decl)
+ tree decl;
+{
+ tree context = decl_function_context (decl);
+ tree link;
+
+ if (context == 0)
+ return 0;
+
+ /* We treat inline_function_decl as an alias for the current function
+ because that is the inline function whose vars, types, etc.
+ are being merged into the current function.
+ See expand_inline_function. */
+ if (context == current_function_decl || context == inline_function_decl)
+ return virtual_stack_vars_rtx;
+
+ for (link = context_display; link; link = TREE_CHAIN (link))
+ if (TREE_PURPOSE (link) == context)
+ return RTL_EXPR_RTL (TREE_VALUE (link));
+
+ abort ();
+}
+
+/* Convert a stack slot address ADDR for variable VAR
+ (from a containing function)
+ into an address valid in this function (using a static chain). */
+
+rtx
+fix_lexical_addr (addr, var)
+ rtx addr;
+ tree var;
+{
+ rtx basereg;
+ int displacement;
+ tree context = decl_function_context (var);
+ struct function *fp;
+ rtx base = 0;
+
+ /* If this is the present function, we need not do anything. */
+ if (context == current_function_decl || context == inline_function_decl)
+ return addr;
+
+ for (fp = outer_function_chain; fp; fp = fp->next)
+ if (fp->decl == context)
+ break;
+
+ if (fp == 0)
+ abort ();
+
+ /* Decode given address as base reg plus displacement. */
+ if (GET_CODE (addr) == REG)
+ basereg = addr, displacement = 0;
+ else if (GET_CODE (addr) == PLUS && GET_CODE (XEXP (addr, 1)) == CONST_INT)
+ basereg = XEXP (addr, 0), displacement = INTVAL (XEXP (addr, 1));
+ else
+ abort ();
+
+ /* We accept vars reached via the containing function's
+ incoming arg pointer and via its stack variables pointer. */
+ if (basereg == fp->internal_arg_pointer)
+ {
+ /* If reached via arg pointer, get the arg pointer value
+ out of that function's stack frame.
+
+ There are two cases: If a separate ap is needed, allocate a
+ slot in the outer function for it and dereference it that way.
+ This is correct even if the real ap is actually a pseudo.
+ Otherwise, just adjust the offset from the frame pointer to
+ compensate. */
+
+#ifdef NEED_SEPARATE_AP
+ rtx addr;
+
+ if (fp->arg_pointer_save_area == 0)
+ fp->arg_pointer_save_area
+ = assign_outer_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0, fp);
+
+ addr = fix_lexical_addr (XEXP (fp->arg_pointer_save_area, 0), var);
+ addr = memory_address (Pmode, addr);
+
+ base = copy_to_reg (gen_rtx (MEM, Pmode, addr));
+#else
+ displacement += (FIRST_PARM_OFFSET (context) - STARTING_FRAME_OFFSET);
+ base = lookup_static_chain (var);
+#endif
+ }
+
+ else if (basereg == virtual_stack_vars_rtx)
+ {
+ /* This is the same code as lookup_static_chain, duplicated here to
+ avoid an extra call to decl_function_context. */
+ tree link;
+
+ for (link = context_display; link; link = TREE_CHAIN (link))
+ if (TREE_PURPOSE (link) == context)
+ {
+ base = RTL_EXPR_RTL (TREE_VALUE (link));
+ break;
+ }
+ }
+
+ if (base == 0)
+ abort ();
+
+ /* Use same offset, relative to appropriate static chain or argument
+ pointer. */
+ return plus_constant (base, displacement);
+}
+
+/* Return the address of the trampoline for entering nested fn FUNCTION.
+ If necessary, allocate a trampoline (in the stack frame)
+ and emit rtl to initialize its contents (at entry to this function). */
+
+rtx
+trampoline_address (function)
+ tree function;
+{
+ tree link;
+ tree rtlexp;
+ rtx tramp;
+ struct function *fp;
+ tree fn_context;
+
+ /* Find an existing trampoline and return it. */
+ for (link = trampoline_list; link; link = TREE_CHAIN (link))
+ if (TREE_PURPOSE (link) == function)
+ return XEXP (RTL_EXPR_RTL (TREE_VALUE (link)), 0);
+ for (fp = outer_function_chain; fp; fp = fp->next)
+ for (link = fp->trampoline_list; link; link = TREE_CHAIN (link))
+ if (TREE_PURPOSE (link) == function)
+ {
+ tramp = fix_lexical_addr (XEXP (RTL_EXPR_RTL (TREE_VALUE (link)), 0),
+ function);
+ return round_trampoline_addr (tramp);
+ }
+
+ /* None exists; we must make one. */
+
+ /* Find the `struct function' for the function containing FUNCTION. */
+ fp = 0;
+ fn_context = decl_function_context (function);
+ if (fn_context != current_function_decl)
+ for (fp = outer_function_chain; fp; fp = fp->next)
+ if (fp->decl == fn_context)
+ break;
+
+ /* Allocate run-time space for this trampoline
+ (usually in the defining function's stack frame). */
+#ifdef ALLOCATE_TRAMPOLINE
+ tramp = ALLOCATE_TRAMPOLINE (fp);
+#else
+ /* If rounding needed, allocate extra space
+ to ensure we have TRAMPOLINE_SIZE bytes left after rounding up. */
+#ifdef TRAMPOLINE_ALIGNMENT
+#define TRAMPOLINE_REAL_SIZE (TRAMPOLINE_SIZE + TRAMPOLINE_ALIGNMENT - 1)
+#else
+#define TRAMPOLINE_REAL_SIZE (TRAMPOLINE_SIZE)
+#endif
+ if (fp != 0)
+ tramp = assign_outer_stack_local (BLKmode, TRAMPOLINE_REAL_SIZE, 0, fp);
+ else
+ tramp = assign_stack_local (BLKmode, TRAMPOLINE_REAL_SIZE, 0);
+#endif
+
+ /* Record the trampoline for reuse and note it for later initialization
+ by expand_function_end. */
+ if (fp != 0)
+ {
+ push_obstacks (fp->function_maybepermanent_obstack,
+ fp->function_maybepermanent_obstack);
+ rtlexp = make_node (RTL_EXPR);
+ RTL_EXPR_RTL (rtlexp) = tramp;
+ fp->trampoline_list = tree_cons (function, rtlexp, fp->trampoline_list);
+ pop_obstacks ();
+ }
+ else
+ {
+ /* Make the RTL_EXPR node temporary, not momentary, so that the
+ trampoline_list doesn't become garbage. */
+ int momentary = suspend_momentary ();
+ rtlexp = make_node (RTL_EXPR);
+ resume_momentary (momentary);
+
+ RTL_EXPR_RTL (rtlexp) = tramp;
+ trampoline_list = tree_cons (function, rtlexp, trampoline_list);
+ }
+
+ tramp = fix_lexical_addr (XEXP (tramp, 0), function);
+ return round_trampoline_addr (tramp);
+}
+
+/* Given a trampoline address,
+ round it to multiple of TRAMPOLINE_ALIGNMENT. */
+
+static rtx
+round_trampoline_addr (tramp)
+ rtx tramp;
+{
+#ifdef TRAMPOLINE_ALIGNMENT
+ /* Round address up to desired boundary. */
+ rtx temp = gen_reg_rtx (Pmode);
+ temp = expand_binop (Pmode, add_optab, tramp,
+ GEN_INT (TRAMPOLINE_ALIGNMENT - 1),
+ temp, 0, OPTAB_LIB_WIDEN);
+ tramp = expand_binop (Pmode, and_optab, temp,
+ GEN_INT (- TRAMPOLINE_ALIGNMENT),
+ temp, 0, OPTAB_LIB_WIDEN);
+#endif
+ return tramp;
+}
+
+/* The functions identify_blocks and reorder_blocks provide a way to
+ reorder the tree of BLOCK nodes, for optimizers that reshuffle or
+ duplicate portions of the RTL code. Call identify_blocks before
+ changing the RTL, and call reorder_blocks after. */
+
+/* Put all this function's BLOCK nodes into a vector, and return it.
+ Also store in each NOTE for the beginning or end of a block
+ the index of that block in the vector.
+ The arguments are TOP_BLOCK, the top-level block of the function,
+ and INSNS, the insn chain of the function. */
+
+tree *
+identify_blocks (top_block, insns)
+ tree top_block;
+ rtx insns;
+{
+ int n_blocks;
+ tree *block_vector;
+ int *block_stack;
+ int depth = 0;
+ int next_block_number = 0;
+ int current_block_number = 0;
+ rtx insn;
+
+ if (top_block == 0)
+ return 0;
+
+ n_blocks = all_blocks (top_block, 0);
+ block_vector = (tree *) xmalloc (n_blocks * sizeof (tree));
+ block_stack = (int *) alloca (n_blocks * sizeof (int));
+
+ all_blocks (top_block, block_vector);
+
+ for (insn = insns; insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == NOTE)
+ {
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG)
+ {
+ block_stack[depth++] = current_block_number;
+ current_block_number = next_block_number;
+ NOTE_BLOCK_NUMBER (insn) = next_block_number++;
+ }
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END)
+ {
+ current_block_number = block_stack[--depth];
+ NOTE_BLOCK_NUMBER (insn) = current_block_number;
+ }
+ }
+
+ return block_vector;
+}
+
+/* Given BLOCK_VECTOR which was returned by identify_blocks,
+ and a revised instruction chain, rebuild the tree structure
+ of BLOCK nodes to correspond to the new order of RTL.
+ The new block tree is inserted below TOP_BLOCK.
+ Returns the current top-level block. */
+
+tree
+reorder_blocks (block_vector, top_block, insns)
+ tree *block_vector;
+ tree top_block;
+ rtx insns;
+{
+ tree current_block = top_block;
+ rtx insn;
+
+ if (block_vector == 0)
+ return top_block;
+
+ /* Prune the old tree away, so that it doesn't get in the way. */
+ BLOCK_SUBBLOCKS (current_block) = 0;
+
+ for (insn = insns; insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == NOTE)
+ {
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG)
+ {
+ tree block = block_vector[NOTE_BLOCK_NUMBER (insn)];
+ /* If we have seen this block before, copy it. */
+ if (TREE_ASM_WRITTEN (block))
+ block = copy_node (block);
+ BLOCK_SUBBLOCKS (block) = 0;
+ TREE_ASM_WRITTEN (block) = 1;
+ BLOCK_SUPERCONTEXT (block) = current_block;
+ BLOCK_CHAIN (block) = BLOCK_SUBBLOCKS (current_block);
+ BLOCK_SUBBLOCKS (current_block) = block;
+ current_block = block;
+ NOTE_SOURCE_FILE (insn) = 0;
+ }
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END)
+ {
+ BLOCK_SUBBLOCKS (current_block)
+ = blocks_nreverse (BLOCK_SUBBLOCKS (current_block));
+ current_block = BLOCK_SUPERCONTEXT (current_block);
+ NOTE_SOURCE_FILE (insn) = 0;
+ }
+ }
+
+ return current_block;
+}
+
+/* Reverse the order of elements in the chain T of blocks,
+ and return the new head of the chain (old last element). */
+
+static tree
+blocks_nreverse (t)
+ tree t;
+{
+ register tree prev = 0, decl, next;
+ for (decl = t; decl; decl = next)
+ {
+ next = BLOCK_CHAIN (decl);
+ BLOCK_CHAIN (decl) = prev;
+ prev = decl;
+ }
+ return prev;
+}
+
+/* Count the subblocks of BLOCK, and list them all into the vector VECTOR.
+ Also clear TREE_ASM_WRITTEN in all blocks. */
+
+static int
+all_blocks (block, vector)
+ tree block;
+ tree *vector;
+{
+ int n_blocks = 1;
+ tree subblocks;
+
+ TREE_ASM_WRITTEN (block) = 0;
+ /* Record this block. */
+ if (vector)
+ vector[0] = block;
+
+ /* Record the subblocks, and their subblocks. */
+ for (subblocks = BLOCK_SUBBLOCKS (block);
+ subblocks; subblocks = BLOCK_CHAIN (subblocks))
+ n_blocks += all_blocks (subblocks, vector ? vector + n_blocks : 0);
+
+ return n_blocks;
+}
+
+/* Build bytecode call descriptor for function SUBR. */
+
+rtx
+bc_build_calldesc (subr)
+ tree subr;
+{
+ tree calldesc = 0, arg;
+ int nargs = 0;
+
+ /* Build the argument description vector in reverse order. */
+ DECL_ARGUMENTS (subr) = nreverse (DECL_ARGUMENTS (subr));
+ nargs = 0;
+
+ for (arg = DECL_ARGUMENTS (subr); arg; arg = TREE_CHAIN (arg))
+ {
+ ++nargs;
+
+ calldesc = tree_cons ((tree) 0, size_in_bytes (TREE_TYPE (arg)), calldesc);
+ calldesc = tree_cons ((tree) 0, bc_runtime_type_code (TREE_TYPE (arg)), calldesc);
+ }
+
+ DECL_ARGUMENTS (subr) = nreverse (DECL_ARGUMENTS (subr));
+
+ /* Prepend the function's return type. */
+ calldesc = tree_cons ((tree) 0,
+ size_in_bytes (TREE_TYPE (TREE_TYPE (subr))),
+ calldesc);
+
+ calldesc = tree_cons ((tree) 0,
+ bc_runtime_type_code (TREE_TYPE (TREE_TYPE (subr))),
+ calldesc);
+
+ /* Prepend the arg count. */
+ calldesc = tree_cons ((tree) 0, build_int_2 (nargs, 0), calldesc);
+
+ /* Output the call description vector and get its address. */
+ calldesc = build_nt (CONSTRUCTOR, (tree) 0, calldesc);
+ TREE_TYPE (calldesc) = build_array_type (integer_type_node,
+ build_index_type (build_int_2 (nargs * 2, 0)));
+
+ return output_constant_def (calldesc);
+}
+
+
+/* Generate RTL for the start of the function SUBR (a FUNCTION_DECL tree node)
+ and initialize static variables for generating RTL for the statements
+ of the function. */
+
+void
+init_function_start (subr, filename, line)
+ tree subr;
+ char *filename;
+ int line;
+{
+ char *junk;
+
+ if (output_bytecode)
+ {
+ this_function_decl = subr;
+ this_function_calldesc = bc_build_calldesc (subr);
+ local_vars_size = 0;
+ stack_depth = 0;
+ max_stack_depth = 0;
+ stmt_expr_depth = 0;
+ return;
+ }
+
+ init_stmt_for_function ();
+
+ cse_not_expected = ! optimize;
+
+ /* Caller save not needed yet. */
+ caller_save_needed = 0;
+
+ /* No stack slots have been made yet. */
+ stack_slot_list = 0;
+
+ /* There is no stack slot for handling nonlocal gotos. */
+ nonlocal_goto_handler_slot = 0;
+ nonlocal_goto_stack_level = 0;
+
+ /* No labels have been declared for nonlocal use. */
+ nonlocal_labels = 0;
+
+ /* No function calls so far in this function. */
+ function_call_count = 0;
+
+ /* No parm regs have been allocated.
+ (This is important for output_inline_function.) */
+ max_parm_reg = LAST_VIRTUAL_REGISTER + 1;
+
+ /* Initialize the RTL mechanism. */
+ init_emit ();
+
+ /* Initialize the queue of pending postincrement and postdecrements,
+ and some other info in expr.c. */
+ init_expr ();
+
+ /* We haven't done register allocation yet. */
+ reg_renumber = 0;
+
+ init_const_rtx_hash_table ();
+
+ current_function_name = (*decl_printable_name) (subr, &junk);
+
+ /* Nonzero if this is a nested function that uses a static chain. */
+
+ current_function_needs_context
+ = (decl_function_context (current_function_decl) != 0);
+
+ /* Set if a call to setjmp is seen. */
+ current_function_calls_setjmp = 0;
+
+ /* Set if a call to longjmp is seen. */
+ current_function_calls_longjmp = 0;
+
+ current_function_calls_alloca = 0;
+ current_function_has_nonlocal_label = 0;
+ current_function_has_nonlocal_goto = 0;
+ current_function_contains_functions = 0;
+
+ current_function_returns_pcc_struct = 0;
+ current_function_returns_struct = 0;
+ current_function_epilogue_delay_list = 0;
+ current_function_uses_const_pool = 0;
+ current_function_uses_pic_offset_table = 0;
+
+ /* We have not yet needed to make a label to jump to for tail-recursion. */
+ tail_recursion_label = 0;
+
+ /* We haven't had a need to make a save area for ap yet. */
+
+ arg_pointer_save_area = 0;
+
+ /* No stack slots allocated yet. */
+ frame_offset = 0;
+
+ /* No SAVE_EXPRs in this function yet. */
+ save_expr_regs = 0;
+
+ /* No RTL_EXPRs in this function yet. */
+ rtl_expr_chain = 0;
+
+ /* We have not allocated any temporaries yet. */
+ temp_slots = 0;
+ temp_slot_level = 0;
+ target_temp_slot_level = 0;
+
+ /* Within function body, compute a type's size as soon it is laid out. */
+ immediate_size_expand++;
+
+ /* We haven't made any trampolines for this function yet. */
+ trampoline_list = 0;
+
+ init_pending_stack_adjust ();
+ inhibit_defer_pop = 0;
+
+ current_function_outgoing_args_size = 0;
+
+ /* Initialize the insn lengths. */
+ init_insn_lengths ();
+
+ /* Prevent ever trying to delete the first instruction of a function.
+ Also tell final how to output a linenum before the function prologue. */
+ emit_line_note (filename, line);
+
+ /* Make sure first insn is a note even if we don't want linenums.
+ This makes sure the first insn will never be deleted.
+ Also, final expects a note to appear there. */
+ emit_note (NULL_PTR, NOTE_INSN_DELETED);
+
+ /* Set flags used by final.c. */
+ if (aggregate_value_p (DECL_RESULT (subr)))
+ {
+#ifdef PCC_STATIC_STRUCT_RETURN
+ current_function_returns_pcc_struct = 1;
+#endif
+ current_function_returns_struct = 1;
+ }
+
+ /* Warn if this value is an aggregate type,
+ regardless of which calling convention we are using for it. */
+ if (warn_aggregate_return
+ && AGGREGATE_TYPE_P (TREE_TYPE (DECL_RESULT (subr))))
+ warning ("function returns an aggregate");
+
+ current_function_returns_pointer
+ = (TREE_CODE (TREE_TYPE (DECL_RESULT (subr))) == POINTER_TYPE);
+
+ /* Indicate that we need to distinguish between the return value of the
+ present function and the return value of a function being called. */
+ rtx_equal_function_value_matters = 1;
+
+ /* Indicate that we have not instantiated virtual registers yet. */
+ virtuals_instantiated = 0;
+
+ /* Indicate we have no need of a frame pointer yet. */
+ frame_pointer_needed = 0;
+
+ /* By default assume not varargs. */
+ current_function_varargs = 0;
+}
+
+/* Indicate that the current function uses extra args
+ not explicitly mentioned in the argument list in any fashion. */
+
+void
+mark_varargs ()
+{
+ current_function_varargs = 1;
+}
+
+/* Expand a call to __main at the beginning of a possible main function. */
+
+void
+expand_main_function ()
+{
+ if (!output_bytecode)
+ {
+ /* The zero below avoids a possible parse error */
+ 0;
+#if !defined (INIT_SECTION_ASM_OP) || defined (INVOKE__main)
+ emit_library_call (gen_rtx (SYMBOL_REF, Pmode, NAME__MAIN), 0,
+ VOIDmode, 0);
+#endif /* not INIT_SECTION_ASM_OP or INVOKE__main */
+ }
+}
+
+extern struct obstack permanent_obstack;
+
+/* Expand start of bytecode function. See comment at
+ expand_function_start below for details. */
+
+void
+bc_expand_function_start (subr, parms_have_cleanups)
+ tree subr;
+ int parms_have_cleanups;
+{
+ char label[20], *name;
+ static int nlab;
+ tree thisarg;
+ int argsz;
+
+ if (TREE_PUBLIC (subr))
+ bc_globalize_label (IDENTIFIER_POINTER (DECL_NAME (subr)));
+
+#ifdef DEBUG_PRINT_CODE
+ fprintf (stderr, "\n<func %s>\n", IDENTIFIER_POINTER (DECL_NAME (subr)));
+#endif
+
+ for (argsz = 0, thisarg = DECL_ARGUMENTS (subr); thisarg; thisarg = TREE_CHAIN (thisarg))
+ {
+ if (DECL_RTL (thisarg))
+ abort (); /* Should be NULL here I think. */
+ else if (TREE_CONSTANT (DECL_SIZE (thisarg)))
+ {
+ DECL_RTL (thisarg) = bc_gen_rtx ((char *) 0, argsz, (struct bc_label *) 0);
+ argsz += TREE_INT_CST_LOW (DECL_SIZE (thisarg));
+ }
+ else
+ {
+ /* Variable-sized objects are pointers to their storage. */
+ DECL_RTL (thisarg) = bc_gen_rtx ((char *) 0, argsz, (struct bc_label *) 0);
+ argsz += POINTER_SIZE;
+ }
+ }
+
+ bc_begin_function (bc_xstrdup (IDENTIFIER_POINTER (DECL_NAME (subr))));
+
+ ASM_GENERATE_INTERNAL_LABEL (label, "LX", nlab);
+
+ ++nlab;
+ name = (char *) obstack_copy0 (&permanent_obstack, label, strlen (label));
+ this_function_callinfo = bc_gen_rtx (name, 0, (struct bc_label *) 0);
+ this_function_bytecode =
+ bc_emit_trampoline (BYTECODE_LABEL (this_function_callinfo));
+}
+
+
+/* Expand end of bytecode function. See details the comment of
+ expand_function_end(), below. */
+
+void
+bc_expand_function_end ()
+{
+ char *ptrconsts;
+
+ expand_null_return ();
+
+ /* Emit any fixup code. This must be done before the call to
+ to BC_END_FUNCTION (), since that will cause the bytecode
+ segment to be finished off and closed. */
+
+ expand_fixups (NULL_RTX);
+
+ ptrconsts = bc_end_function ();
+
+ bc_align_const (2 /* INT_ALIGN */);
+
+ /* If this changes also make sure to change bc-interp.h! */
+
+ bc_emit_const_labeldef (BYTECODE_LABEL (this_function_callinfo));
+ bc_emit_const ((char *) &max_stack_depth, sizeof max_stack_depth);
+ bc_emit_const ((char *) &local_vars_size, sizeof local_vars_size);
+ bc_emit_const_labelref (this_function_bytecode, 0);
+ bc_emit_const_labelref (ptrconsts, 0);
+ bc_emit_const_labelref (BYTECODE_LABEL (this_function_calldesc), 0);
+}
+
+
+/* Start the RTL for a new function, and set variables used for
+ emitting RTL.
+ SUBR is the FUNCTION_DECL node.
+ PARMS_HAVE_CLEANUPS is nonzero if there are cleanups associated with
+ the function's parameters, which must be run at any return statement. */
+
+void
+expand_function_start (subr, parms_have_cleanups)
+ tree subr;
+ int parms_have_cleanups;
+{
+ register int i;
+ tree tem;
+ rtx last_ptr;
+
+ if (output_bytecode)
+ {
+ bc_expand_function_start (subr, parms_have_cleanups);
+ return;
+ }
+
+ /* Make sure volatile mem refs aren't considered
+ valid operands of arithmetic insns. */
+ init_recog_no_volatile ();
+
+ /* If function gets a static chain arg, store it in the stack frame.
+ Do this first, so it gets the first stack slot offset. */
+ if (current_function_needs_context)
+ {
+ last_ptr = assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0);
+
+#ifdef SMALL_REGISTER_CLASSES
+ /* Delay copying static chain if it is not a register to avoid
+ conflicts with regs used for parameters. */
+ if (GET_CODE (static_chain_incoming_rtx) == REG)
+#endif
+ emit_move_insn (last_ptr, static_chain_incoming_rtx);
+ }
+
+ /* If the parameters of this function need cleaning up, get a label
+ for the beginning of the code which executes those cleanups. This must
+ be done before doing anything with return_label. */
+ if (parms_have_cleanups)
+ cleanup_label = gen_label_rtx ();
+ else
+ cleanup_label = 0;
+
+ /* Make the label for return statements to jump to, if this machine
+ does not have a one-instruction return and uses an epilogue,
+ or if it returns a structure, or if it has parm cleanups. */
+#ifdef HAVE_return
+ if (cleanup_label == 0 && HAVE_return
+ && ! current_function_returns_pcc_struct
+ && ! (current_function_returns_struct && ! optimize))
+ return_label = 0;
+ else
+ return_label = gen_label_rtx ();
+#else
+ return_label = gen_label_rtx ();
+#endif
+
+ /* Initialize rtx used to return the value. */
+ /* Do this before assign_parms so that we copy the struct value address
+ before any library calls that assign parms might generate. */
+
+ /* Decide whether to return the value in memory or in a register. */
+ if (aggregate_value_p (DECL_RESULT (subr)))
+ {
+ /* Returning something that won't go in a register. */
+ register rtx value_address;
+
+#ifdef PCC_STATIC_STRUCT_RETURN
+ if (current_function_returns_pcc_struct)
+ {
+ int size = int_size_in_bytes (TREE_TYPE (DECL_RESULT (subr)));
+ value_address = assemble_static_space (size);
+ }
+ else
+#endif
+ {
+ /* Expect to be passed the address of a place to store the value.
+ If it is passed as an argument, assign_parms will take care of
+ it. */
+ if (struct_value_incoming_rtx)
+ {
+ value_address = gen_reg_rtx (Pmode);
+ emit_move_insn (value_address, struct_value_incoming_rtx);
+ }
+ }
+ if (value_address)
+ {
+ DECL_RTL (DECL_RESULT (subr))
+ = gen_rtx (MEM, DECL_MODE (DECL_RESULT (subr)), value_address);
+ MEM_IN_STRUCT_P (DECL_RTL (DECL_RESULT (subr)))
+ = AGGREGATE_TYPE_P (TREE_TYPE (DECL_RESULT (subr)));
+ }
+ }
+ else if (DECL_MODE (DECL_RESULT (subr)) == VOIDmode)
+ /* If return mode is void, this decl rtl should not be used. */
+ DECL_RTL (DECL_RESULT (subr)) = 0;
+ else if (parms_have_cleanups)
+ {
+ /* If function will end with cleanup code for parms,
+ compute the return values into a pseudo reg,
+ which we will copy into the true return register
+ after the cleanups are done. */
+
+ enum machine_mode mode = DECL_MODE (DECL_RESULT (subr));
+
+#ifdef PROMOTE_FUNCTION_RETURN
+ tree type = TREE_TYPE (DECL_RESULT (subr));
+ int unsignedp = TREE_UNSIGNED (type);
+
+ mode = promote_mode (type, mode, &unsignedp, 1);
+#endif
+
+ DECL_RTL (DECL_RESULT (subr)) = gen_reg_rtx (mode);
+ }
+ else
+ /* Scalar, returned in a register. */
+ {
+#ifdef FUNCTION_OUTGOING_VALUE
+ DECL_RTL (DECL_RESULT (subr))
+ = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (subr)), subr);
+#else
+ DECL_RTL (DECL_RESULT (subr))
+ = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (subr)), subr);
+#endif
+
+ /* Mark this reg as the function's return value. */
+ if (GET_CODE (DECL_RTL (DECL_RESULT (subr))) == REG)
+ {
+ REG_FUNCTION_VALUE_P (DECL_RTL (DECL_RESULT (subr))) = 1;
+ /* Needed because we may need to move this to memory
+ in case it's a named return value whose address is taken. */
+ DECL_REGISTER (DECL_RESULT (subr)) = 1;
+ }
+ }
+
+ /* Initialize rtx for parameters and local variables.
+ In some cases this requires emitting insns. */
+
+ assign_parms (subr, 0);
+
+#ifdef SMALL_REGISTER_CLASSES
+ /* Copy the static chain now if it wasn't a register. The delay is to
+ avoid conflicts with the parameter passing registers. */
+
+ if (current_function_needs_context)
+ if (GET_CODE (static_chain_incoming_rtx) != REG)
+ emit_move_insn (last_ptr, static_chain_incoming_rtx);
+#endif
+
+ /* The following was moved from init_function_start.
+ The move is supposed to make sdb output more accurate. */
+ /* Indicate the beginning of the function body,
+ as opposed to parm setup. */
+ emit_note (NULL_PTR, NOTE_INSN_FUNCTION_BEG);
+
+ /* If doing stupid allocation, mark parms as born here. */
+
+ if (GET_CODE (get_last_insn ()) != NOTE)
+ emit_note (NULL_PTR, NOTE_INSN_DELETED);
+ parm_birth_insn = get_last_insn ();
+
+ if (obey_regdecls)
+ {
+ for (i = LAST_VIRTUAL_REGISTER + 1; i < max_parm_reg; i++)
+ use_variable (regno_reg_rtx[i]);
+
+ if (current_function_internal_arg_pointer != virtual_incoming_args_rtx)
+ use_variable (current_function_internal_arg_pointer);
+ }
+
+ /* Fetch static chain values for containing functions. */
+ tem = decl_function_context (current_function_decl);
+ /* If not doing stupid register allocation, then start off with the static
+ chain pointer in a pseudo register. Otherwise, we use the stack
+ address that was generated above. */
+ if (tem && ! obey_regdecls)
+ last_ptr = copy_to_reg (static_chain_incoming_rtx);
+ context_display = 0;
+ while (tem)
+ {
+ tree rtlexp = make_node (RTL_EXPR);
+
+ RTL_EXPR_RTL (rtlexp) = last_ptr;
+ context_display = tree_cons (tem, rtlexp, context_display);
+ tem = decl_function_context (tem);
+ if (tem == 0)
+ break;
+ /* Chain thru stack frames, assuming pointer to next lexical frame
+ is found at the place we always store it. */
+#ifdef FRAME_GROWS_DOWNWARD
+ last_ptr = plus_constant (last_ptr, - GET_MODE_SIZE (Pmode));
+#endif
+ last_ptr = copy_to_reg (gen_rtx (MEM, Pmode,
+ memory_address (Pmode, last_ptr)));
+
+ /* If we are not optimizing, ensure that we know that this
+ piece of context is live over the entire function. */
+ if (! optimize)
+ save_expr_regs = gen_rtx (EXPR_LIST, VOIDmode, last_ptr,
+ save_expr_regs);
+ }
+
+ /* After the display initializations is where the tail-recursion label
+ should go, if we end up needing one. Ensure we have a NOTE here
+ since some things (like trampolines) get placed before this. */
+ tail_recursion_reentry = emit_note (NULL_PTR, NOTE_INSN_DELETED);
+
+ /* Evaluate now the sizes of any types declared among the arguments. */
+ for (tem = nreverse (get_pending_sizes ()); tem; tem = TREE_CHAIN (tem))
+ expand_expr (TREE_VALUE (tem), const0_rtx, VOIDmode, 0);
+
+ /* Make sure there is a line number after the function entry setup code. */
+ force_next_line_note ();
+}
+
+/* Generate RTL for the end of the current function.
+ FILENAME and LINE are the current position in the source file.
+
+ It is up to language-specific callers to do cleanups for parameters--
+ or else, supply 1 for END_BINDINGS and we will call expand_end_bindings. */
+
+void
+expand_function_end (filename, line, end_bindings)
+ char *filename;
+ int line;
+ int end_bindings;
+{
+ register int i;
+ tree link;
+
+ static rtx initial_trampoline;
+
+ if (output_bytecode)
+ {
+ bc_expand_function_end ();
+ return;
+ }
+
+#ifdef NON_SAVING_SETJMP
+ /* Don't put any variables in registers if we call setjmp
+ on a machine that fails to restore the registers. */
+ if (NON_SAVING_SETJMP && current_function_calls_setjmp)
+ {
+ setjmp_protect (DECL_INITIAL (current_function_decl));
+ setjmp_protect_args ();
+ }
+#endif
+
+ /* Save the argument pointer if a save area was made for it. */
+ if (arg_pointer_save_area)
+ {
+ rtx x = gen_move_insn (arg_pointer_save_area, virtual_incoming_args_rtx);
+ emit_insn_before (x, tail_recursion_reentry);
+ }
+
+ /* Initialize any trampolines required by this function. */
+ for (link = trampoline_list; link; link = TREE_CHAIN (link))
+ {
+ tree function = TREE_PURPOSE (link);
+ rtx context = lookup_static_chain (function);
+ rtx tramp = RTL_EXPR_RTL (TREE_VALUE (link));
+ rtx seq;
+
+ /* First make sure this compilation has a template for
+ initializing trampolines. */
+ if (initial_trampoline == 0)
+ {
+ end_temporary_allocation ();
+ initial_trampoline
+ = gen_rtx (MEM, BLKmode, assemble_trampoline_template ());
+ resume_temporary_allocation ();
+ }
+
+ /* Generate insns to initialize the trampoline. */
+ start_sequence ();
+ tramp = change_address (initial_trampoline, BLKmode,
+ round_trampoline_addr (XEXP (tramp, 0)));
+ emit_block_move (tramp, initial_trampoline, GEN_INT (TRAMPOLINE_SIZE),
+ FUNCTION_BOUNDARY / BITS_PER_UNIT);
+ INITIALIZE_TRAMPOLINE (XEXP (tramp, 0),
+ XEXP (DECL_RTL (function), 0), context);
+ seq = get_insns ();
+ end_sequence ();
+
+ /* Put those insns at entry to the containing function (this one). */
+ emit_insns_before (seq, tail_recursion_reentry);
+ }
+
+#if 0 /* I think unused parms are legitimate enough. */
+ /* Warn about unused parms. */
+ if (warn_unused)
+ {
+ rtx decl;
+
+ for (decl = DECL_ARGUMENTS (current_function_decl);
+ decl; decl = TREE_CHAIN (decl))
+ if (! TREE_USED (decl) && TREE_CODE (decl) == VAR_DECL)
+ warning_with_decl (decl, "unused parameter `%s'");
+ }
+#endif
+
+ /* Delete handlers for nonlocal gotos if nothing uses them. */
+ if (nonlocal_goto_handler_slot != 0 && !current_function_has_nonlocal_label)
+ delete_handlers ();
+
+ /* End any sequences that failed to be closed due to syntax errors. */
+ while (in_sequence_p ())
+ end_sequence ();
+
+ /* Outside function body, can't compute type's actual size
+ until next function's body starts. */
+ immediate_size_expand--;
+
+ /* If doing stupid register allocation,
+ mark register parms as dying here. */
+
+ if (obey_regdecls)
+ {
+ rtx tem;
+ for (i = LAST_VIRTUAL_REGISTER + 1; i < max_parm_reg; i++)
+ use_variable (regno_reg_rtx[i]);
+
+ /* Likewise for the regs of all the SAVE_EXPRs in the function. */
+
+ for (tem = save_expr_regs; tem; tem = XEXP (tem, 1))
+ {
+ use_variable (XEXP (tem, 0));
+ use_variable_after (XEXP (tem, 0), parm_birth_insn);
+ }
+
+ if (current_function_internal_arg_pointer != virtual_incoming_args_rtx)
+ use_variable (current_function_internal_arg_pointer);
+ }
+
+ clear_pending_stack_adjust ();
+ do_pending_stack_adjust ();
+
+ /* Mark the end of the function body.
+ If control reaches this insn, the function can drop through
+ without returning a value. */
+ emit_note (NULL_PTR, NOTE_INSN_FUNCTION_END);
+
+ /* Output a linenumber for the end of the function.
+ SDB depends on this. */
+ emit_line_note_force (filename, line);
+
+ /* Output the label for the actual return from the function,
+ if one is expected. This happens either because a function epilogue
+ is used instead of a return instruction, or because a return was done
+ with a goto in order to run local cleanups, or because of pcc-style
+ structure returning. */
+
+ if (return_label)
+ emit_label (return_label);
+
+ /* C++ uses this. */
+ if (end_bindings)
+ expand_end_bindings (0, 0, 0);
+
+ /* If we had calls to alloca, and this machine needs
+ an accurate stack pointer to exit the function,
+ insert some code to save and restore the stack pointer. */
+#ifdef EXIT_IGNORE_STACK
+ if (! EXIT_IGNORE_STACK)
+#endif
+ if (current_function_calls_alloca)
+ {
+ rtx tem = 0;
+
+ emit_stack_save (SAVE_FUNCTION, &tem, parm_birth_insn);
+ emit_stack_restore (SAVE_FUNCTION, tem, NULL_RTX);
+ }
+
+ /* If scalar return value was computed in a pseudo-reg,
+ copy that to the hard return register. */
+ if (DECL_RTL (DECL_RESULT (current_function_decl)) != 0
+ && GET_CODE (DECL_RTL (DECL_RESULT (current_function_decl))) == REG
+ && (REGNO (DECL_RTL (DECL_RESULT (current_function_decl)))
+ >= FIRST_PSEUDO_REGISTER))
+ {
+ rtx real_decl_result;
+
+#ifdef FUNCTION_OUTGOING_VALUE
+ real_decl_result
+ = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (current_function_decl)),
+ current_function_decl);
+#else
+ real_decl_result
+ = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (current_function_decl)),
+ current_function_decl);
+#endif
+ REG_FUNCTION_VALUE_P (real_decl_result) = 1;
+ emit_move_insn (real_decl_result,
+ DECL_RTL (DECL_RESULT (current_function_decl)));
+ emit_insn (gen_rtx (USE, VOIDmode, real_decl_result));
+ }
+
+ /* If returning a structure, arrange to return the address of the value
+ in a place where debuggers expect to find it.
+
+ If returning a structure PCC style,
+ the caller also depends on this value.
+ And current_function_returns_pcc_struct is not necessarily set. */
+ if (current_function_returns_struct
+ || current_function_returns_pcc_struct)
+ {
+ rtx value_address = XEXP (DECL_RTL (DECL_RESULT (current_function_decl)), 0);
+ tree type = TREE_TYPE (DECL_RESULT (current_function_decl));
+#ifdef FUNCTION_OUTGOING_VALUE
+ rtx outgoing
+ = FUNCTION_OUTGOING_VALUE (build_pointer_type (type),
+ current_function_decl);
+#else
+ rtx outgoing
+ = FUNCTION_VALUE (build_pointer_type (type),
+ current_function_decl);
+#endif
+
+ /* Mark this as a function return value so integrate will delete the
+ assignment and USE below when inlining this function. */
+ REG_FUNCTION_VALUE_P (outgoing) = 1;
+
+ emit_move_insn (outgoing, value_address);
+ use_variable (outgoing);
+ }
+
+ /* Output a return insn if we are using one.
+ Otherwise, let the rtl chain end here, to drop through
+ into the epilogue. */
+
+#ifdef HAVE_return
+ if (HAVE_return)
+ {
+ emit_jump_insn (gen_return ());
+ emit_barrier ();
+ }
+#endif
+
+ /* Fix up any gotos that jumped out to the outermost
+ binding level of the function.
+ Must follow emitting RETURN_LABEL. */
+
+ /* If you have any cleanups to do at this point,
+ and they need to create temporary variables,
+ then you will lose. */
+ expand_fixups (get_insns ());
+}
+
+/* These arrays record the INSN_UIDs of the prologue and epilogue insns. */
+
+static int *prologue;
+static int *epilogue;
+
+/* Create an array that records the INSN_UIDs of INSNS (either a sequence
+ or a single insn). */
+
+static int *
+record_insns (insns)
+ rtx insns;
+{
+ int *vec;
+
+ if (GET_CODE (insns) == SEQUENCE)
+ {
+ int len = XVECLEN (insns, 0);
+ vec = (int *) oballoc ((len + 1) * sizeof (int));
+ vec[len] = 0;
+ while (--len >= 0)
+ vec[len] = INSN_UID (XVECEXP (insns, 0, len));
+ }
+ else
+ {
+ vec = (int *) oballoc (2 * sizeof (int));
+ vec[0] = INSN_UID (insns);
+ vec[1] = 0;
+ }
+ return vec;
+}
+
+/* Determine how many INSN_UIDs in VEC are part of INSN. */
+
+static int
+contains (insn, vec)
+ rtx insn;
+ int *vec;
+{
+ register int i, j;
+
+ if (GET_CODE (insn) == INSN
+ && GET_CODE (PATTERN (insn)) == SEQUENCE)
+ {
+ int count = 0;
+ for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
+ for (j = 0; vec[j]; j++)
+ if (INSN_UID (XVECEXP (PATTERN (insn), 0, i)) == vec[j])
+ count++;
+ return count;
+ }
+ else
+ {
+ for (j = 0; vec[j]; j++)
+ if (INSN_UID (insn) == vec[j])
+ return 1;
+ }
+ return 0;
+}
+
+/* Generate the prologe and epilogue RTL if the machine supports it. Thread
+ this into place with notes indicating where the prologue ends and where
+ the epilogue begins. Update the basic block information when possible. */
+
+void
+thread_prologue_and_epilogue_insns (f)
+ rtx f;
+{
+#ifdef HAVE_prologue
+ if (HAVE_prologue)
+ {
+ rtx head, seq, insn;
+
+ /* The first insn (a NOTE_INSN_DELETED) is followed by zero or more
+ prologue insns and a NOTE_INSN_PROLOGUE_END. */
+ emit_note_after (NOTE_INSN_PROLOGUE_END, f);
+ seq = gen_prologue ();
+ head = emit_insn_after (seq, f);
+
+ /* Include the new prologue insns in the first block. Ignore them
+ if they form a basic block unto themselves. */
+ if (basic_block_head && n_basic_blocks
+ && GET_CODE (basic_block_head[0]) != CODE_LABEL)
+ basic_block_head[0] = NEXT_INSN (f);
+
+ /* Retain a map of the prologue insns. */
+ prologue = record_insns (GET_CODE (seq) == SEQUENCE ? seq : head);
+ }
+ else
+#endif
+ prologue = 0;
+
+#ifdef HAVE_epilogue
+ if (HAVE_epilogue)
+ {
+ rtx insn = get_last_insn ();
+ rtx prev = prev_nonnote_insn (insn);
+
+ /* If we end with a BARRIER, we don't need an epilogue. */
+ if (! (prev && GET_CODE (prev) == BARRIER))
+ {
+ rtx tail, seq, tem;
+ rtx first_use = 0;
+ rtx last_use = 0;
+
+ /* The last basic block ends with a NOTE_INSN_EPILOGUE_BEG, the
+ epilogue insns, the USE insns at the end of a function,
+ the jump insn that returns, and then a BARRIER. */
+
+ /* Move the USE insns at the end of a function onto a list. */
+ while (prev
+ && GET_CODE (prev) == INSN
+ && GET_CODE (PATTERN (prev)) == USE)
+ {
+ tem = prev;
+ prev = prev_nonnote_insn (prev);
+
+ NEXT_INSN (PREV_INSN (tem)) = NEXT_INSN (tem);
+ PREV_INSN (NEXT_INSN (tem)) = PREV_INSN (tem);
+ if (first_use)
+ {
+ NEXT_INSN (tem) = first_use;
+ PREV_INSN (first_use) = tem;
+ }
+ first_use = tem;
+ if (!last_use)
+ last_use = tem;
+ }
+
+ emit_barrier_after (insn);
+
+ seq = gen_epilogue ();
+ tail = emit_jump_insn_after (seq, insn);
+
+ /* Insert the USE insns immediately before the return insn, which
+ must be the first instruction before the final barrier. */
+ if (first_use)
+ {
+ tem = prev_nonnote_insn (get_last_insn ());
+ NEXT_INSN (PREV_INSN (tem)) = first_use;
+ PREV_INSN (first_use) = PREV_INSN (tem);
+ PREV_INSN (tem) = last_use;
+ NEXT_INSN (last_use) = tem;
+ }
+
+ emit_note_after (NOTE_INSN_EPILOGUE_BEG, insn);
+
+ /* Include the new epilogue insns in the last block. Ignore
+ them if they form a basic block unto themselves. */
+ if (basic_block_end && n_basic_blocks
+ && GET_CODE (basic_block_end[n_basic_blocks - 1]) != JUMP_INSN)
+ basic_block_end[n_basic_blocks - 1] = tail;
+
+ /* Retain a map of the epilogue insns. */
+ epilogue = record_insns (GET_CODE (seq) == SEQUENCE ? seq : tail);
+ return;
+ }
+ }
+#endif
+ epilogue = 0;
+}
+
+/* Reposition the prologue-end and epilogue-begin notes after instruction
+ scheduling and delayed branch scheduling. */
+
+void
+reposition_prologue_and_epilogue_notes (f)
+ rtx f;
+{
+#if defined (HAVE_prologue) || defined (HAVE_epilogue)
+ /* Reposition the prologue and epilogue notes. */
+ if (n_basic_blocks)
+ {
+ rtx next, prev;
+ int len;
+
+ if (prologue)
+ {
+ register rtx insn, note = 0;
+
+ /* Scan from the beginning until we reach the last prologue insn.
+ We apparently can't depend on basic_block_{head,end} after
+ reorg has run. */
+ for (len = 0; prologue[len]; len++)
+ ;
+ for (insn = f; len && insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == NOTE)
+ {
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_PROLOGUE_END)
+ note = insn;
+ }
+ else if ((len -= contains (insn, prologue)) == 0)
+ {
+ /* Find the prologue-end note if we haven't already, and
+ move it to just after the last prologue insn. */
+ if (note == 0)
+ {
+ for (note = insn; note = NEXT_INSN (note);)
+ if (GET_CODE (note) == NOTE
+ && NOTE_LINE_NUMBER (note) == NOTE_INSN_PROLOGUE_END)
+ break;
+ }
+ next = NEXT_INSN (note);
+ prev = PREV_INSN (note);
+ if (prev)
+ NEXT_INSN (prev) = next;
+ if (next)
+ PREV_INSN (next) = prev;
+ add_insn_after (note, insn);
+ }
+ }
+ }
+
+ if (epilogue)
+ {
+ register rtx insn, note = 0;
+
+ /* Scan from the end until we reach the first epilogue insn.
+ We apparently can't depend on basic_block_{head,end} after
+ reorg has run. */
+ for (len = 0; epilogue[len]; len++)
+ ;
+ for (insn = get_last_insn (); len && insn; insn = PREV_INSN (insn))
+ {
+ if (GET_CODE (insn) == NOTE)
+ {
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EPILOGUE_BEG)
+ note = insn;
+ }
+ else if ((len -= contains (insn, epilogue)) == 0)
+ {
+ /* Find the epilogue-begin note if we haven't already, and
+ move it to just before the first epilogue insn. */
+ if (note == 0)
+ {
+ for (note = insn; note = PREV_INSN (note);)
+ if (GET_CODE (note) == NOTE
+ && NOTE_LINE_NUMBER (note) == NOTE_INSN_EPILOGUE_BEG)
+ break;
+ }
+ next = NEXT_INSN (note);
+ prev = PREV_INSN (note);
+ if (prev)
+ NEXT_INSN (prev) = next;
+ if (next)
+ PREV_INSN (next) = prev;
+ add_insn_after (note, PREV_INSN (insn));
+ }
+ }
+ }
+ }
+#endif /* HAVE_prologue or HAVE_epilogue */
+}
diff --git a/gnu/usr.bin/cc/cc_int/getpwd.c b/gnu/usr.bin/cc/cc_int/getpwd.c
new file mode 100644
index 0000000..922a9ed
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/getpwd.c
@@ -0,0 +1,94 @@
+/* getpwd.c - get the working directory */
+
+#include "config.h"
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef errno
+extern int errno;
+#endif
+
+/* Virtually every UN*X system now in common use (except for pre-4.3-tahoe
+ BSD systems) now provides getcwd as called for by POSIX. Allow for
+ the few exceptions to the general rule here. */
+
+#if !(defined (POSIX) || defined (USG) || defined (VMS))
+#include <sys/param.h>
+extern char *getwd ();
+#define getcwd(buf,len) getwd(buf)
+#define GUESSPATHLEN (MAXPATHLEN + 1)
+#else /* (defined (USG) || defined (VMS)) */
+extern char *getcwd ();
+/* We actually use this as a starting point, not a limit. */
+#define GUESSPATHLEN 100
+#endif /* (defined (USG) || defined (VMS)) */
+
+char *getenv ();
+char *xmalloc ();
+
+#ifndef VMS
+
+/* Get the working directory. Use the PWD environment variable if it's
+ set correctly, since this is faster and gives more uniform answers
+ to the user. Yield the working directory if successful; otherwise,
+ yield 0 and set errno. */
+
+char *
+getpwd ()
+{
+ static char *pwd;
+ static int failure_errno;
+
+ char *p = pwd;
+ size_t s;
+ struct stat dotstat, pwdstat;
+
+ if (!p && !(errno = failure_errno))
+ {
+ if (! ((p = getenv ("PWD")) != 0
+ && *p == '/'
+ && stat (p, &pwdstat) == 0
+ && stat (".", &dotstat) == 0
+ && dotstat.st_ino == pwdstat.st_ino
+ && dotstat.st_dev == pwdstat.st_dev))
+
+ /* The shortcut didn't work. Try the slow, ``sure'' way. */
+ for (s = GUESSPATHLEN; ! getcwd (p = xmalloc (s), s); s *= 2)
+ {
+ int e = errno;
+ free (p);
+#ifdef ERANGE
+ if (e != ERANGE)
+#endif
+ {
+ errno = failure_errno = e;
+ p = 0;
+ break;
+ }
+ }
+
+ /* Cache the result. This assumes that the program does
+ not invoke chdir between calls to getpwd. */
+ pwd = p;
+ }
+ return p;
+}
+
+#else /* VMS */
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 255
+#endif
+
+char *
+getpwd ()
+{
+ static char *pwd = 0;
+
+ if (!pwd) pwd = getcwd (xmalloc (MAXPATHLEN+1), MAXPATHLEN+1);
+ return pwd;
+}
+
+#endif /* VMS */
diff --git a/gnu/usr.bin/cc/cc_int/global.c b/gnu/usr.bin/cc/cc_int/global.c
new file mode 100644
index 0000000..297e930
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/global.c
@@ -0,0 +1,1680 @@
+/* Allocate registers for pseudo-registers that span basic blocks.
+ Copyright (C) 1987, 1988, 1991, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include <stdio.h>
+#include "config.h"
+#include "rtl.h"
+#include "flags.h"
+#include "basic-block.h"
+#include "hard-reg-set.h"
+#include "regs.h"
+#include "insn-config.h"
+#include "output.h"
+
+/* This pass of the compiler performs global register allocation.
+ It assigns hard register numbers to all the pseudo registers
+ that were not handled in local_alloc. Assignments are recorded
+ in the vector reg_renumber, not by changing the rtl code.
+ (Such changes are made by final). The entry point is
+ the function global_alloc.
+
+ After allocation is complete, the reload pass is run as a subroutine
+ of this pass, so that when a pseudo reg loses its hard reg due to
+ spilling it is possible to make a second attempt to find a hard
+ reg for it. The reload pass is independent in other respects
+ and it is run even when stupid register allocation is in use.
+
+ 1. count the pseudo-registers still needing allocation
+ and assign allocation-numbers (allocnos) to them.
+ Set up tables reg_allocno and allocno_reg to map
+ reg numbers to allocnos and vice versa.
+ max_allocno gets the number of allocnos in use.
+
+ 2. Allocate a max_allocno by max_allocno conflict bit matrix and clear it.
+ Allocate a max_allocno by FIRST_PSEUDO_REGISTER conflict matrix
+ for conflicts between allocnos and explicit hard register use
+ (which includes use of pseudo-registers allocated by local_alloc).
+
+ 3. for each basic block
+ walk forward through the block, recording which
+ unallocated registers and which hardware registers are live.
+ Build the conflict matrix between the unallocated registers
+ and another of unallocated registers versus hardware registers.
+ Also record the preferred hardware registers
+ for each unallocated one.
+
+ 4. Sort a table of the allocnos into order of
+ desirability of the variables.
+
+ 5. Allocate the variables in that order; each if possible into
+ a preferred register, else into another register. */
+
+/* Number of pseudo-registers still requiring allocation
+ (not allocated by local_allocate). */
+
+static int max_allocno;
+
+/* Indexed by (pseudo) reg number, gives the allocno, or -1
+ for pseudo registers already allocated by local_allocate. */
+
+static int *reg_allocno;
+
+/* Indexed by allocno, gives the reg number. */
+
+static int *allocno_reg;
+
+/* A vector of the integers from 0 to max_allocno-1,
+ sorted in the order of first-to-be-allocated first. */
+
+static int *allocno_order;
+
+/* Indexed by an allocno, gives the number of consecutive
+ hard registers needed by that pseudo reg. */
+
+static int *allocno_size;
+
+/* Indexed by (pseudo) reg number, gives the number of another
+ lower-numbered pseudo reg which can share a hard reg with this pseudo
+ *even if the two pseudos would otherwise appear to conflict*. */
+
+static int *reg_may_share;
+
+/* Define the number of bits in each element of `conflicts' and what
+ type that element has. We use the largest integer format on the
+ host machine. */
+
+#define INT_BITS HOST_BITS_PER_WIDE_INT
+#define INT_TYPE HOST_WIDE_INT
+
+/* max_allocno by max_allocno array of bits,
+ recording whether two allocno's conflict (can't go in the same
+ hardware register).
+
+ `conflicts' is not symmetric; a conflict between allocno's i and j
+ is recorded either in element i,j or in element j,i. */
+
+static INT_TYPE *conflicts;
+
+/* Number of ints require to hold max_allocno bits.
+ This is the length of a row in `conflicts'. */
+
+static int allocno_row_words;
+
+/* Two macros to test or store 1 in an element of `conflicts'. */
+
+#define CONFLICTP(I, J) \
+ (conflicts[(I) * allocno_row_words + (J) / INT_BITS] \
+ & ((INT_TYPE) 1 << ((J) % INT_BITS)))
+
+#define SET_CONFLICT(I, J) \
+ (conflicts[(I) * allocno_row_words + (J) / INT_BITS] \
+ |= ((INT_TYPE) 1 << ((J) % INT_BITS)))
+
+/* Set of hard regs currently live (during scan of all insns). */
+
+static HARD_REG_SET hard_regs_live;
+
+/* Indexed by N, set of hard regs conflicting with allocno N. */
+
+static HARD_REG_SET *hard_reg_conflicts;
+
+/* Indexed by N, set of hard regs preferred by allocno N.
+ This is used to make allocnos go into regs that are copied to or from them,
+ when possible, to reduce register shuffling. */
+
+static HARD_REG_SET *hard_reg_preferences;
+
+/* Similar, but just counts register preferences made in simple copy
+ operations, rather than arithmetic. These are given priority because
+ we can always eliminate an insn by using these, but using a register
+ in the above list won't always eliminate an insn. */
+
+static HARD_REG_SET *hard_reg_copy_preferences;
+
+/* Similar to hard_reg_preferences, but includes bits for subsequent
+ registers when an allocno is multi-word. The above variable is used for
+ allocation while this is used to build reg_someone_prefers, below. */
+
+static HARD_REG_SET *hard_reg_full_preferences;
+
+/* Indexed by N, set of hard registers that some later allocno has a
+ preference for. */
+
+static HARD_REG_SET *regs_someone_prefers;
+
+/* Set of registers that global-alloc isn't supposed to use. */
+
+static HARD_REG_SET no_global_alloc_regs;
+
+/* Set of registers used so far. */
+
+static HARD_REG_SET regs_used_so_far;
+
+/* Number of calls crossed by each allocno. */
+
+static int *allocno_calls_crossed;
+
+/* Number of refs (weighted) to each allocno. */
+
+static int *allocno_n_refs;
+
+/* Guess at live length of each allocno.
+ This is actually the max of the live lengths of the regs. */
+
+static int *allocno_live_length;
+
+/* Number of refs (weighted) to each hard reg, as used by local alloc.
+ It is zero for a reg that contains global pseudos or is explicitly used. */
+
+static int local_reg_n_refs[FIRST_PSEUDO_REGISTER];
+
+/* Guess at live length of each hard reg, as used by local alloc.
+ This is actually the sum of the live lengths of the specific regs. */
+
+static int local_reg_live_length[FIRST_PSEUDO_REGISTER];
+
+/* Test a bit in TABLE, a vector of HARD_REG_SETs,
+ for vector element I, and hard register number J. */
+
+#define REGBITP(TABLE, I, J) TEST_HARD_REG_BIT (TABLE[I], J)
+
+/* Set to 1 a bit in a vector of HARD_REG_SETs. Works like REGBITP. */
+
+#define SET_REGBIT(TABLE, I, J) SET_HARD_REG_BIT (TABLE[I], J)
+
+/* Bit mask for allocnos live at current point in the scan. */
+
+static INT_TYPE *allocnos_live;
+
+/* Test, set or clear bit number I in allocnos_live,
+ a bit vector indexed by allocno. */
+
+#define ALLOCNO_LIVE_P(I) \
+ (allocnos_live[(I) / INT_BITS] & ((INT_TYPE) 1 << ((I) % INT_BITS)))
+
+#define SET_ALLOCNO_LIVE(I) \
+ (allocnos_live[(I) / INT_BITS] |= ((INT_TYPE) 1 << ((I) % INT_BITS)))
+
+#define CLEAR_ALLOCNO_LIVE(I) \
+ (allocnos_live[(I) / INT_BITS] &= ~((INT_TYPE) 1 << ((I) % INT_BITS)))
+
+/* This is turned off because it doesn't work right for DImode.
+ (And it is only used for DImode, so the other cases are worthless.)
+ The problem is that it isn't true that there is NO possibility of conflict;
+ only that there is no conflict if the two pseudos get the exact same regs.
+ If they were allocated with a partial overlap, there would be a conflict.
+ We can't safely turn off the conflict unless we have another way to
+ prevent the partial overlap.
+
+ Idea: change hard_reg_conflicts so that instead of recording which
+ hard regs the allocno may not overlap, it records where the allocno
+ may not start. Change both where it is used and where it is updated.
+ Then there is a way to record that (reg:DI 108) may start at 10
+ but not at 9 or 11. There is still the question of how to record
+ this semi-conflict between two pseudos. */
+#if 0
+/* Reg pairs for which conflict after the current insn
+ is inhibited by a REG_NO_CONFLICT note.
+ If the table gets full, we ignore any other notes--that is conservative. */
+#define NUM_NO_CONFLICT_PAIRS 4
+/* Number of pairs in use in this insn. */
+int n_no_conflict_pairs;
+static struct { int allocno1, allocno2;}
+ no_conflict_pairs[NUM_NO_CONFLICT_PAIRS];
+#endif /* 0 */
+
+/* Record all regs that are set in any one insn.
+ Communication from mark_reg_{store,clobber} and global_conflicts. */
+
+static rtx *regs_set;
+static int n_regs_set;
+
+/* All registers that can be eliminated. */
+
+static HARD_REG_SET eliminable_regset;
+
+static int allocno_compare PROTO((int *, int *));
+static void global_conflicts PROTO((void));
+static void expand_preferences PROTO((void));
+static void prune_preferences PROTO((void));
+static void find_reg PROTO((int, HARD_REG_SET, int, int, int));
+static void record_one_conflict PROTO((int));
+static void record_conflicts PROTO((short *, int));
+static void mark_reg_store PROTO((rtx, rtx));
+static void mark_reg_clobber PROTO((rtx, rtx));
+static void mark_reg_conflicts PROTO((rtx));
+static void mark_reg_death PROTO((rtx));
+static void mark_reg_live_nc PROTO((int, enum machine_mode));
+static void set_preference PROTO((rtx, rtx));
+static void dump_conflicts PROTO((FILE *));
+
+/* Perform allocation of pseudo-registers not allocated by local_alloc.
+ FILE is a file to output debugging information on,
+ or zero if such output is not desired.
+
+ Return value is nonzero if reload failed
+ and we must not do any more for this function. */
+
+int
+global_alloc (file)
+ FILE *file;
+{
+#ifdef ELIMINABLE_REGS
+ static struct {int from, to; } eliminables[] = ELIMINABLE_REGS;
+#endif
+ int need_fp
+ = (! flag_omit_frame_pointer
+#ifdef EXIT_IGNORE_STACK
+ || (current_function_calls_alloca && EXIT_IGNORE_STACK)
+#endif
+ || FRAME_POINTER_REQUIRED);
+
+ register int i;
+ rtx x;
+
+ max_allocno = 0;
+
+ /* A machine may have certain hard registers that
+ are safe to use only within a basic block. */
+
+ CLEAR_HARD_REG_SET (no_global_alloc_regs);
+#ifdef OVERLAPPING_REGNO_P
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (OVERLAPPING_REGNO_P (i))
+ SET_HARD_REG_BIT (no_global_alloc_regs, i);
+#endif
+
+ /* Build the regset of all eliminable registers and show we can't use those
+ that we already know won't be eliminated. */
+#ifdef ELIMINABLE_REGS
+ for (i = 0; i < sizeof eliminables / sizeof eliminables[0]; i++)
+ {
+ SET_HARD_REG_BIT (eliminable_regset, eliminables[i].from);
+
+ if (! CAN_ELIMINATE (eliminables[i].from, eliminables[i].to)
+ || (eliminables[i].to == STACK_POINTER_REGNUM && need_fp))
+ SET_HARD_REG_BIT (no_global_alloc_regs, eliminables[i].from);
+ }
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+ SET_HARD_REG_BIT (eliminable_regset, HARD_FRAME_POINTER_REGNUM);
+ if (need_fp)
+ SET_HARD_REG_BIT (no_global_alloc_regs, HARD_FRAME_POINTER_REGNUM);
+#endif
+
+#else
+ SET_HARD_REG_BIT (eliminable_regset, FRAME_POINTER_REGNUM);
+ if (need_fp)
+ SET_HARD_REG_BIT (no_global_alloc_regs, FRAME_POINTER_REGNUM);
+#endif
+
+ /* Track which registers have already been used. Start with registers
+ explicitly in the rtl, then registers allocated by local register
+ allocation. */
+
+ CLEAR_HARD_REG_SET (regs_used_so_far);
+#ifdef LEAF_REGISTERS
+ /* If we are doing the leaf function optimization, and this is a leaf
+ function, it means that the registers that take work to save are those
+ that need a register window. So prefer the ones that can be used in
+ a leaf function. */
+ {
+ char *cheap_regs;
+ static char leaf_regs[] = LEAF_REGISTERS;
+
+ if (only_leaf_regs_used () && leaf_function_p ())
+ cheap_regs = leaf_regs;
+ else
+ cheap_regs = call_used_regs;
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (regs_ever_live[i] || cheap_regs[i])
+ SET_HARD_REG_BIT (regs_used_so_far, i);
+ }
+#else
+ /* We consider registers that do not have to be saved over calls as if
+ they were already used since there is no cost in using them. */
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (regs_ever_live[i] || call_used_regs[i])
+ SET_HARD_REG_BIT (regs_used_so_far, i);
+#endif
+
+ for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+ if (reg_renumber[i] >= 0)
+ SET_HARD_REG_BIT (regs_used_so_far, reg_renumber[i]);
+
+ /* Establish mappings from register number to allocation number
+ and vice versa. In the process, count the allocnos. */
+
+ reg_allocno = (int *) alloca (max_regno * sizeof (int));
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ reg_allocno[i] = -1;
+
+ /* Initialize the shared-hard-reg mapping
+ from the list of pairs that may share. */
+ reg_may_share = (int *) alloca (max_regno * sizeof (int));
+ bzero ((char *) reg_may_share, max_regno * sizeof (int));
+ for (x = regs_may_share; x; x = XEXP (XEXP (x, 1), 1))
+ {
+ int r1 = REGNO (XEXP (x, 0));
+ int r2 = REGNO (XEXP (XEXP (x, 1), 0));
+ if (r1 > r2)
+ reg_may_share[r1] = r2;
+ else
+ reg_may_share[r2] = r1;
+ }
+
+ for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+ /* Note that reg_live_length[i] < 0 indicates a "constant" reg
+ that we are supposed to refrain from putting in a hard reg.
+ -2 means do make an allocno but don't allocate it. */
+ if (reg_n_refs[i] != 0 && reg_renumber[i] < 0 && reg_live_length[i] != -1
+ /* Don't allocate pseudos that cross calls,
+ if this function receives a nonlocal goto. */
+ && (! current_function_has_nonlocal_label
+ || reg_n_calls_crossed[i] == 0))
+ {
+ if (reg_may_share[i] && reg_allocno[reg_may_share[i]] >= 0)
+ reg_allocno[i] = reg_allocno[reg_may_share[i]];
+ else
+ reg_allocno[i] = max_allocno++;
+ if (reg_live_length[i] == 0)
+ abort ();
+ }
+ else
+ reg_allocno[i] = -1;
+
+ allocno_reg = (int *) alloca (max_allocno * sizeof (int));
+ allocno_size = (int *) alloca (max_allocno * sizeof (int));
+ allocno_calls_crossed = (int *) alloca (max_allocno * sizeof (int));
+ allocno_n_refs = (int *) alloca (max_allocno * sizeof (int));
+ allocno_live_length = (int *) alloca (max_allocno * sizeof (int));
+ bzero ((char *) allocno_size, max_allocno * sizeof (int));
+ bzero ((char *) allocno_calls_crossed, max_allocno * sizeof (int));
+ bzero ((char *) allocno_n_refs, max_allocno * sizeof (int));
+ bzero ((char *) allocno_live_length, max_allocno * sizeof (int));
+
+ for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+ if (reg_allocno[i] >= 0)
+ {
+ int allocno = reg_allocno[i];
+ allocno_reg[allocno] = i;
+ allocno_size[allocno] = PSEUDO_REGNO_SIZE (i);
+ allocno_calls_crossed[allocno] += reg_n_calls_crossed[i];
+ allocno_n_refs[allocno] += reg_n_refs[i];
+ if (allocno_live_length[allocno] < reg_live_length[i])
+ allocno_live_length[allocno] = reg_live_length[i];
+ }
+
+ /* Calculate amount of usage of each hard reg by pseudos
+ allocated by local-alloc. This is to see if we want to
+ override it. */
+ bzero ((char *) local_reg_live_length, sizeof local_reg_live_length);
+ bzero ((char *) local_reg_n_refs, sizeof local_reg_n_refs);
+ for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+ if (reg_allocno[i] < 0 && reg_renumber[i] >= 0)
+ {
+ int regno = reg_renumber[i];
+ int endregno = regno + HARD_REGNO_NREGS (regno, PSEUDO_REGNO_MODE (i));
+ int j;
+
+ for (j = regno; j < endregno; j++)
+ {
+ local_reg_n_refs[j] += reg_n_refs[i];
+ local_reg_live_length[j] += reg_live_length[i];
+ }
+ }
+
+ /* We can't override local-alloc for a reg used not just by local-alloc. */
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (regs_ever_live[i])
+ local_reg_n_refs[i] = 0;
+
+ /* Allocate the space for the conflict and preference tables and
+ initialize them. */
+
+ hard_reg_conflicts
+ = (HARD_REG_SET *) alloca (max_allocno * sizeof (HARD_REG_SET));
+ bzero ((char *) hard_reg_conflicts, max_allocno * sizeof (HARD_REG_SET));
+
+ hard_reg_preferences
+ = (HARD_REG_SET *) alloca (max_allocno * sizeof (HARD_REG_SET));
+ bzero ((char *) hard_reg_preferences, max_allocno * sizeof (HARD_REG_SET));
+
+ hard_reg_copy_preferences
+ = (HARD_REG_SET *) alloca (max_allocno * sizeof (HARD_REG_SET));
+ bzero ((char *) hard_reg_copy_preferences,
+ max_allocno * sizeof (HARD_REG_SET));
+
+ hard_reg_full_preferences
+ = (HARD_REG_SET *) alloca (max_allocno * sizeof (HARD_REG_SET));
+ bzero ((char *) hard_reg_full_preferences,
+ max_allocno * sizeof (HARD_REG_SET));
+
+ regs_someone_prefers
+ = (HARD_REG_SET *) alloca (max_allocno * sizeof (HARD_REG_SET));
+ bzero ((char *) regs_someone_prefers, max_allocno * sizeof (HARD_REG_SET));
+
+ allocno_row_words = (max_allocno + INT_BITS - 1) / INT_BITS;
+
+ conflicts = (INT_TYPE *) alloca (max_allocno * allocno_row_words
+ * sizeof (INT_TYPE));
+ bzero ((char *) conflicts,
+ max_allocno * allocno_row_words * sizeof (INT_TYPE));
+
+ allocnos_live = (INT_TYPE *) alloca (allocno_row_words * sizeof (INT_TYPE));
+
+ /* If there is work to be done (at least one reg to allocate),
+ perform global conflict analysis and allocate the regs. */
+
+ if (max_allocno > 0)
+ {
+ /* Scan all the insns and compute the conflicts among allocnos
+ and between allocnos and hard regs. */
+
+ global_conflicts ();
+
+ /* Eliminate conflicts between pseudos and eliminable registers. If
+ the register is not eliminated, the pseudo won't really be able to
+ live in the eliminable register, so the conflict doesn't matter.
+ If we do eliminate the register, the conflict will no longer exist.
+ So in either case, we can ignore the conflict. Likewise for
+ preferences. */
+
+ for (i = 0; i < max_allocno; i++)
+ {
+ AND_COMPL_HARD_REG_SET (hard_reg_conflicts[i], eliminable_regset);
+ AND_COMPL_HARD_REG_SET (hard_reg_copy_preferences[i],
+ eliminable_regset);
+ AND_COMPL_HARD_REG_SET (hard_reg_preferences[i], eliminable_regset);
+ }
+
+ /* Try to expand the preferences by merging them between allocnos. */
+
+ expand_preferences ();
+
+ /* Determine the order to allocate the remaining pseudo registers. */
+
+ allocno_order = (int *) alloca (max_allocno * sizeof (int));
+ for (i = 0; i < max_allocno; i++)
+ allocno_order[i] = i;
+
+ /* Default the size to 1, since allocno_compare uses it to divide by.
+ Also convert allocno_live_length of zero to -1. A length of zero
+ can occur when all the registers for that allocno have reg_live_length
+ equal to -2. In this case, we want to make an allocno, but not
+ allocate it. So avoid the divide-by-zero and set it to a low
+ priority. */
+
+ for (i = 0; i < max_allocno; i++)
+ {
+ if (allocno_size[i] == 0)
+ allocno_size[i] = 1;
+ if (allocno_live_length[i] == 0)
+ allocno_live_length[i] = -1;
+ }
+
+ qsort (allocno_order, max_allocno, sizeof (int), allocno_compare);
+
+ prune_preferences ();
+
+ if (file)
+ dump_conflicts (file);
+
+ /* Try allocating them, one by one, in that order,
+ except for parameters marked with reg_live_length[regno] == -2. */
+
+ for (i = 0; i < max_allocno; i++)
+ if (reg_live_length[allocno_reg[allocno_order[i]]] >= 0)
+ {
+ /* If we have more than one register class,
+ first try allocating in the class that is cheapest
+ for this pseudo-reg. If that fails, try any reg. */
+ if (N_REG_CLASSES > 1)
+ {
+ find_reg (allocno_order[i], HARD_CONST (0), 0, 0, 0);
+ if (reg_renumber[allocno_reg[allocno_order[i]]] >= 0)
+ continue;
+ }
+ if (reg_alternate_class (allocno_reg[allocno_order[i]]) != NO_REGS)
+ find_reg (allocno_order[i], HARD_CONST (0), 1, 0, 0);
+ }
+ }
+
+ /* Do the reloads now while the allocno data still exist, so that we can
+ try to assign new hard regs to any pseudo regs that are spilled. */
+
+#if 0 /* We need to eliminate regs even if there is no rtl code,
+ for the sake of debugging information. */
+ if (n_basic_blocks > 0)
+#endif
+ return reload (get_insns (), 1, file);
+}
+
+/* Sort predicate for ordering the allocnos.
+ Returns -1 (1) if *v1 should be allocated before (after) *v2. */
+
+static int
+allocno_compare (v1, v2)
+ int *v1, *v2;
+{
+ /* Note that the quotient will never be bigger than
+ the value of floor_log2 times the maximum number of
+ times a register can occur in one insn (surely less than 100).
+ Multiplying this by 10000 can't overflow. */
+ register int pri1
+ = (((double) (floor_log2 (allocno_n_refs[*v1]) * allocno_n_refs[*v1])
+ / allocno_live_length[*v1])
+ * 10000 * allocno_size[*v1]);
+ register int pri2
+ = (((double) (floor_log2 (allocno_n_refs[*v2]) * allocno_n_refs[*v2])
+ / allocno_live_length[*v2])
+ * 10000 * allocno_size[*v2]);
+ if (pri2 - pri1)
+ return pri2 - pri1;
+
+ /* If regs are equally good, sort by allocno,
+ so that the results of qsort leave nothing to chance. */
+ return *v1 - *v2;
+}
+
+/* Scan the rtl code and record all conflicts and register preferences in the
+ conflict matrices and preference tables. */
+
+static void
+global_conflicts ()
+{
+ register int b, i;
+ register rtx insn;
+ short *block_start_allocnos;
+
+ /* Make a vector that mark_reg_{store,clobber} will store in. */
+ regs_set = (rtx *) alloca (max_parallel * sizeof (rtx) * 2);
+
+ block_start_allocnos = (short *) alloca (max_allocno * sizeof (short));
+
+ for (b = 0; b < n_basic_blocks; b++)
+ {
+ bzero ((char *) allocnos_live, allocno_row_words * sizeof (INT_TYPE));
+
+ /* Initialize table of registers currently live
+ to the state at the beginning of this basic block.
+ This also marks the conflicts among them.
+
+ For pseudo-regs, there is only one bit for each one
+ no matter how many hard regs it occupies.
+ This is ok; we know the size from PSEUDO_REGNO_SIZE.
+ For explicit hard regs, we cannot know the size that way
+ since one hard reg can be used with various sizes.
+ Therefore, we must require that all the hard regs
+ implicitly live as part of a multi-word hard reg
+ are explicitly marked in basic_block_live_at_start. */
+
+ {
+ register int offset;
+ REGSET_ELT_TYPE bit;
+ register regset old = basic_block_live_at_start[b];
+ int ax = 0;
+
+#ifdef HARD_REG_SET
+ hard_regs_live = old[0];
+#else
+ COPY_HARD_REG_SET (hard_regs_live, old);
+#endif
+ for (offset = 0, i = 0; offset < regset_size; offset++)
+ if (old[offset] == 0)
+ i += REGSET_ELT_BITS;
+ else
+ for (bit = 1; bit; bit <<= 1, i++)
+ {
+ if (i >= max_regno)
+ break;
+ if (old[offset] & bit)
+ {
+ register int a = reg_allocno[i];
+ if (a >= 0)
+ {
+ SET_ALLOCNO_LIVE (a);
+ block_start_allocnos[ax++] = a;
+ }
+ else if ((a = reg_renumber[i]) >= 0)
+ mark_reg_live_nc (a, PSEUDO_REGNO_MODE (i));
+ }
+ }
+
+ /* Record that each allocno now live conflicts with each other
+ allocno now live, and with each hard reg now live. */
+
+ record_conflicts (block_start_allocnos, ax);
+ }
+
+ insn = basic_block_head[b];
+
+ /* Scan the code of this basic block, noting which allocnos
+ and hard regs are born or die. When one is born,
+ record a conflict with all others currently live. */
+
+ while (1)
+ {
+ register RTX_CODE code = GET_CODE (insn);
+ register rtx link;
+
+ /* Make regs_set an empty set. */
+
+ n_regs_set = 0;
+
+ if (code == INSN || code == CALL_INSN || code == JUMP_INSN)
+ {
+
+#if 0
+ int i = 0;
+ for (link = REG_NOTES (insn);
+ link && i < NUM_NO_CONFLICT_PAIRS;
+ link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_NO_CONFLICT)
+ {
+ no_conflict_pairs[i].allocno1
+ = reg_allocno[REGNO (SET_DEST (PATTERN (insn)))];
+ no_conflict_pairs[i].allocno2
+ = reg_allocno[REGNO (XEXP (link, 0))];
+ i++;
+ }
+#endif /* 0 */
+
+ /* Mark any registers clobbered by INSN as live,
+ so they conflict with the inputs. */
+
+ note_stores (PATTERN (insn), mark_reg_clobber);
+
+ /* Mark any registers dead after INSN as dead now. */
+
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_DEAD)
+ mark_reg_death (XEXP (link, 0));
+
+ /* Mark any registers set in INSN as live,
+ and mark them as conflicting with all other live regs.
+ Clobbers are processed again, so they conflict with
+ the registers that are set. */
+
+ note_stores (PATTERN (insn), mark_reg_store);
+
+#ifdef AUTO_INC_DEC
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_INC)
+ mark_reg_store (XEXP (link, 0), NULL_RTX);
+#endif
+
+ /* If INSN has multiple outputs, then any reg that dies here
+ and is used inside of an output
+ must conflict with the other outputs. */
+
+ if (GET_CODE (PATTERN (insn)) == PARALLEL && !single_set (insn))
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_DEAD)
+ {
+ int used_in_output = 0;
+ int i;
+ rtx reg = XEXP (link, 0);
+
+ for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
+ {
+ rtx set = XVECEXP (PATTERN (insn), 0, i);
+ if (GET_CODE (set) == SET
+ && GET_CODE (SET_DEST (set)) != REG
+ && !rtx_equal_p (reg, SET_DEST (set))
+ && reg_overlap_mentioned_p (reg, SET_DEST (set)))
+ used_in_output = 1;
+ }
+ if (used_in_output)
+ mark_reg_conflicts (reg);
+ }
+
+ /* Mark any registers set in INSN and then never used. */
+
+ while (n_regs_set > 0)
+ if (find_regno_note (insn, REG_UNUSED,
+ REGNO (regs_set[--n_regs_set])))
+ mark_reg_death (regs_set[n_regs_set]);
+ }
+
+ if (insn == basic_block_end[b])
+ break;
+ insn = NEXT_INSN (insn);
+ }
+ }
+}
+/* Expand the preference information by looking for cases where one allocno
+ dies in an insn that sets an allocno. If those two allocnos don't conflict,
+ merge any preferences between those allocnos. */
+
+static void
+expand_preferences ()
+{
+ rtx insn;
+ rtx link;
+ rtx set;
+
+ /* We only try to handle the most common cases here. Most of the cases
+ where this wins are reg-reg copies. */
+
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && (set = single_set (insn)) != 0
+ && GET_CODE (SET_DEST (set)) == REG
+ && reg_allocno[REGNO (SET_DEST (set))] >= 0)
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_DEAD
+ && GET_CODE (XEXP (link, 0)) == REG
+ && reg_allocno[REGNO (XEXP (link, 0))] >= 0
+ && ! CONFLICTP (reg_allocno[REGNO (SET_DEST (set))],
+ reg_allocno[REGNO (XEXP (link, 0))])
+ && ! CONFLICTP (reg_allocno[REGNO (XEXP (link, 0))],
+ reg_allocno[REGNO (SET_DEST (set))]))
+ {
+ int a1 = reg_allocno[REGNO (SET_DEST (set))];
+ int a2 = reg_allocno[REGNO (XEXP (link, 0))];
+
+ if (XEXP (link, 0) == SET_SRC (set))
+ {
+ IOR_HARD_REG_SET (hard_reg_copy_preferences[a1],
+ hard_reg_copy_preferences[a2]);
+ IOR_HARD_REG_SET (hard_reg_copy_preferences[a2],
+ hard_reg_copy_preferences[a1]);
+ }
+
+ IOR_HARD_REG_SET (hard_reg_preferences[a1],
+ hard_reg_preferences[a2]);
+ IOR_HARD_REG_SET (hard_reg_preferences[a2],
+ hard_reg_preferences[a1]);
+ IOR_HARD_REG_SET (hard_reg_full_preferences[a1],
+ hard_reg_full_preferences[a2]);
+ IOR_HARD_REG_SET (hard_reg_full_preferences[a2],
+ hard_reg_full_preferences[a1]);
+ }
+}
+
+/* Prune the preferences for global registers to exclude registers that cannot
+ be used.
+
+ Compute `regs_someone_prefers', which is a bitmask of the hard registers
+ that are preferred by conflicting registers of lower priority. If possible,
+ we will avoid using these registers. */
+
+static void
+prune_preferences ()
+{
+ int i, j;
+ int allocno;
+
+ /* Scan least most important to most important.
+ For each allocno, remove from preferences registers that cannot be used,
+ either because of conflicts or register type. Then compute all registers
+ preferred by each lower-priority register that conflicts. */
+
+ for (i = max_allocno - 1; i >= 0; i--)
+ {
+ HARD_REG_SET temp;
+
+ allocno = allocno_order[i];
+ COPY_HARD_REG_SET (temp, hard_reg_conflicts[allocno]);
+
+ if (allocno_calls_crossed[allocno] == 0)
+ IOR_HARD_REG_SET (temp, fixed_reg_set);
+ else
+ IOR_HARD_REG_SET (temp, call_used_reg_set);
+
+ IOR_COMPL_HARD_REG_SET
+ (temp,
+ reg_class_contents[(int) reg_preferred_class (allocno_reg[allocno])]);
+
+ AND_COMPL_HARD_REG_SET (hard_reg_preferences[allocno], temp);
+ AND_COMPL_HARD_REG_SET (hard_reg_copy_preferences[allocno], temp);
+ AND_COMPL_HARD_REG_SET (hard_reg_full_preferences[allocno], temp);
+
+ CLEAR_HARD_REG_SET (regs_someone_prefers[allocno]);
+
+ /* Merge in the preferences of lower-priority registers (they have
+ already been pruned). If we also prefer some of those registers,
+ don't exclude them unless we are of a smaller size (in which case
+ we want to give the lower-priority allocno the first chance for
+ these registers). */
+ for (j = i + 1; j < max_allocno; j++)
+ if (CONFLICTP (allocno, allocno_order[j]))
+ {
+ COPY_HARD_REG_SET (temp,
+ hard_reg_full_preferences[allocno_order[j]]);
+ if (allocno_size[allocno_order[j]] <= allocno_size[allocno])
+ AND_COMPL_HARD_REG_SET (temp,
+ hard_reg_full_preferences[allocno]);
+
+ IOR_HARD_REG_SET (regs_someone_prefers[allocno], temp);
+ }
+ }
+}
+
+/* Assign a hard register to ALLOCNO; look for one that is the beginning
+ of a long enough stretch of hard regs none of which conflicts with ALLOCNO.
+ The registers marked in PREFREGS are tried first.
+
+ LOSERS, if non-zero, is a HARD_REG_SET indicating registers that cannot
+ be used for this allocation.
+
+ If ALT_REGS_P is zero, consider only the preferred class of ALLOCNO's reg.
+ Otherwise ignore that preferred class and use the alternate class.
+
+ If ACCEPT_CALL_CLOBBERED is nonzero, accept a call-clobbered hard reg that
+ will have to be saved and restored at calls.
+
+ RETRYING is nonzero if this is called from retry_global_alloc.
+
+ If we find one, record it in reg_renumber.
+ If not, do nothing. */
+
+static void
+find_reg (allocno, losers, alt_regs_p, accept_call_clobbered, retrying)
+ int allocno;
+ HARD_REG_SET losers;
+ int alt_regs_p;
+ int accept_call_clobbered;
+ int retrying;
+{
+ register int i, best_reg, pass;
+#ifdef HARD_REG_SET
+ register /* Declare it register if it's a scalar. */
+#endif
+ HARD_REG_SET used, used1, used2;
+
+ enum reg_class class = (alt_regs_p
+ ? reg_alternate_class (allocno_reg[allocno])
+ : reg_preferred_class (allocno_reg[allocno]));
+ enum machine_mode mode = PSEUDO_REGNO_MODE (allocno_reg[allocno]);
+
+ if (accept_call_clobbered)
+ COPY_HARD_REG_SET (used1, call_fixed_reg_set);
+ else if (allocno_calls_crossed[allocno] == 0)
+ COPY_HARD_REG_SET (used1, fixed_reg_set);
+ else
+ COPY_HARD_REG_SET (used1, call_used_reg_set);
+
+ /* Some registers should not be allocated in global-alloc. */
+ IOR_HARD_REG_SET (used1, no_global_alloc_regs);
+ if (losers)
+ IOR_HARD_REG_SET (used1, losers);
+
+ IOR_COMPL_HARD_REG_SET (used1, reg_class_contents[(int) class]);
+ COPY_HARD_REG_SET (used2, used1);
+
+ IOR_HARD_REG_SET (used1, hard_reg_conflicts[allocno]);
+
+ /* Try each hard reg to see if it fits. Do this in two passes.
+ In the first pass, skip registers that are preferred by some other pseudo
+ to give it a better chance of getting one of those registers. Only if
+ we can't get a register when excluding those do we take one of them.
+ However, we never allocate a register for the first time in pass 0. */
+
+ COPY_HARD_REG_SET (used, used1);
+ IOR_COMPL_HARD_REG_SET (used, regs_used_so_far);
+ IOR_HARD_REG_SET (used, regs_someone_prefers[allocno]);
+
+ best_reg = -1;
+ for (i = FIRST_PSEUDO_REGISTER, pass = 0;
+ pass <= 1 && i >= FIRST_PSEUDO_REGISTER;
+ pass++)
+ {
+ if (pass == 1)
+ COPY_HARD_REG_SET (used, used1);
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+#ifdef REG_ALLOC_ORDER
+ int regno = reg_alloc_order[i];
+#else
+ int regno = i;
+#endif
+ if (! TEST_HARD_REG_BIT (used, regno)
+ && HARD_REGNO_MODE_OK (regno, mode))
+ {
+ register int j;
+ register int lim = regno + HARD_REGNO_NREGS (regno, mode);
+ for (j = regno + 1;
+ (j < lim
+ && ! TEST_HARD_REG_BIT (used, j));
+ j++);
+ if (j == lim)
+ {
+ best_reg = regno;
+ break;
+ }
+#ifndef REG_ALLOC_ORDER
+ i = j; /* Skip starting points we know will lose */
+#endif
+ }
+ }
+ }
+
+ /* See if there is a preferred register with the same class as the register
+ we allocated above. Making this restriction prevents register
+ preferencing from creating worse register allocation.
+
+ Remove from the preferred registers and conflicting registers. Note that
+ additional conflicts may have been added after `prune_preferences' was
+ called.
+
+ First do this for those register with copy preferences, then all
+ preferred registers. */
+
+ AND_COMPL_HARD_REG_SET (hard_reg_copy_preferences[allocno], used);
+ GO_IF_HARD_REG_SUBSET (hard_reg_copy_preferences[allocno],
+ reg_class_contents[(int) NO_REGS], no_copy_prefs);
+
+ if (best_reg >= 0)
+ {
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (TEST_HARD_REG_BIT (hard_reg_copy_preferences[allocno], i)
+ && HARD_REGNO_MODE_OK (i, mode)
+ && (REGNO_REG_CLASS (i) == REGNO_REG_CLASS (best_reg)
+ || reg_class_subset_p (REGNO_REG_CLASS (i),
+ REGNO_REG_CLASS (best_reg))
+ || reg_class_subset_p (REGNO_REG_CLASS (best_reg),
+ REGNO_REG_CLASS (i))))
+ {
+ register int j;
+ register int lim = i + HARD_REGNO_NREGS (i, mode);
+ for (j = i + 1;
+ (j < lim
+ && ! TEST_HARD_REG_BIT (used, j)
+ && (REGNO_REG_CLASS (j)
+ == REGNO_REG_CLASS (best_reg + (j - i))
+ || reg_class_subset_p (REGNO_REG_CLASS (j),
+ REGNO_REG_CLASS (best_reg + (j - i)))
+ || reg_class_subset_p (REGNO_REG_CLASS (best_reg + (j - i)),
+ REGNO_REG_CLASS (j))));
+ j++);
+ if (j == lim)
+ {
+ best_reg = i;
+ goto no_prefs;
+ }
+ }
+ }
+ no_copy_prefs:
+
+ AND_COMPL_HARD_REG_SET (hard_reg_preferences[allocno], used);
+ GO_IF_HARD_REG_SUBSET (hard_reg_preferences[allocno],
+ reg_class_contents[(int) NO_REGS], no_prefs);
+
+ if (best_reg >= 0)
+ {
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (TEST_HARD_REG_BIT (hard_reg_preferences[allocno], i)
+ && HARD_REGNO_MODE_OK (i, mode)
+ && (REGNO_REG_CLASS (i) == REGNO_REG_CLASS (best_reg)
+ || reg_class_subset_p (REGNO_REG_CLASS (i),
+ REGNO_REG_CLASS (best_reg))
+ || reg_class_subset_p (REGNO_REG_CLASS (best_reg),
+ REGNO_REG_CLASS (i))))
+ {
+ register int j;
+ register int lim = i + HARD_REGNO_NREGS (i, mode);
+ for (j = i + 1;
+ (j < lim
+ && ! TEST_HARD_REG_BIT (used, j)
+ && (REGNO_REG_CLASS (j)
+ == REGNO_REG_CLASS (best_reg + (j - i))
+ || reg_class_subset_p (REGNO_REG_CLASS (j),
+ REGNO_REG_CLASS (best_reg + (j - i)))
+ || reg_class_subset_p (REGNO_REG_CLASS (best_reg + (j - i)),
+ REGNO_REG_CLASS (j))));
+ j++);
+ if (j == lim)
+ {
+ best_reg = i;
+ break;
+ }
+ }
+ }
+ no_prefs:
+
+ /* If we haven't succeeded yet, try with caller-saves.
+ We need not check to see if the current function has nonlocal
+ labels because we don't put any pseudos that are live over calls in
+ registers in that case. */
+
+ if (flag_caller_saves && best_reg < 0)
+ {
+ /* Did not find a register. If it would be profitable to
+ allocate a call-clobbered register and save and restore it
+ around calls, do that. */
+ if (! accept_call_clobbered
+ && allocno_calls_crossed[allocno] != 0
+ && CALLER_SAVE_PROFITABLE (allocno_n_refs[allocno],
+ allocno_calls_crossed[allocno]))
+ {
+ find_reg (allocno, losers, alt_regs_p, 1, retrying);
+ if (reg_renumber[allocno_reg[allocno]] >= 0)
+ {
+ caller_save_needed = 1;
+ return;
+ }
+ }
+ }
+
+ /* If we haven't succeeded yet,
+ see if some hard reg that conflicts with us
+ was utilized poorly by local-alloc.
+ If so, kick out the regs that were put there by local-alloc
+ so we can use it instead. */
+ if (best_reg < 0 && !retrying
+ /* Let's not bother with multi-reg allocnos. */
+ && allocno_size[allocno] == 1)
+ {
+ /* Count from the end, to find the least-used ones first. */
+ for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
+ {
+#ifdef REG_ALLOC_ORDER
+ int regno = reg_alloc_order[i];
+#else
+ int regno = i;
+#endif
+
+ if (local_reg_n_refs[regno] != 0
+ /* Don't use a reg no good for this pseudo. */
+ && ! TEST_HARD_REG_BIT (used2, regno)
+ && HARD_REGNO_MODE_OK (regno, mode)
+ && (((double) local_reg_n_refs[regno]
+ / local_reg_live_length[regno])
+ < ((double) allocno_n_refs[allocno]
+ / allocno_live_length[allocno])))
+ {
+ /* Hard reg REGNO was used less in total by local regs
+ than it would be used by this one allocno! */
+ int k;
+ for (k = 0; k < max_regno; k++)
+ if (reg_renumber[k] >= 0)
+ {
+ int r = reg_renumber[k];
+ int endregno
+ = r + HARD_REGNO_NREGS (r, PSEUDO_REGNO_MODE (k));
+
+ if (regno >= r && regno < endregno)
+ reg_renumber[k] = -1;
+ }
+
+ best_reg = regno;
+ break;
+ }
+ }
+ }
+
+ /* Did we find a register? */
+
+ if (best_reg >= 0)
+ {
+ register int lim, j;
+ HARD_REG_SET this_reg;
+
+ /* Yes. Record it as the hard register of this pseudo-reg. */
+ reg_renumber[allocno_reg[allocno]] = best_reg;
+ /* Also of any pseudo-regs that share with it. */
+ if (reg_may_share[allocno_reg[allocno]])
+ for (j = FIRST_PSEUDO_REGISTER; j < max_regno; j++)
+ if (reg_allocno[j] == allocno)
+ reg_renumber[j] = best_reg;
+
+ /* Make a set of the hard regs being allocated. */
+ CLEAR_HARD_REG_SET (this_reg);
+ lim = best_reg + HARD_REGNO_NREGS (best_reg, mode);
+ for (j = best_reg; j < lim; j++)
+ {
+ SET_HARD_REG_BIT (this_reg, j);
+ SET_HARD_REG_BIT (regs_used_so_far, j);
+ /* This is no longer a reg used just by local regs. */
+ local_reg_n_refs[j] = 0;
+ }
+ /* For each other pseudo-reg conflicting with this one,
+ mark it as conflicting with the hard regs this one occupies. */
+ lim = allocno;
+ for (j = 0; j < max_allocno; j++)
+ if (CONFLICTP (lim, j) || CONFLICTP (j, lim))
+ {
+ IOR_HARD_REG_SET (hard_reg_conflicts[j], this_reg);
+ }
+ }
+}
+
+/* Called from `reload' to look for a hard reg to put pseudo reg REGNO in.
+ Perhaps it had previously seemed not worth a hard reg,
+ or perhaps its old hard reg has been commandeered for reloads.
+ FORBIDDEN_REGS indicates certain hard regs that may not be used, even if
+ they do not appear to be allocated.
+ If FORBIDDEN_REGS is zero, no regs are forbidden. */
+
+void
+retry_global_alloc (regno, forbidden_regs)
+ int regno;
+ HARD_REG_SET forbidden_regs;
+{
+ int allocno = reg_allocno[regno];
+ if (allocno >= 0)
+ {
+ /* If we have more than one register class,
+ first try allocating in the class that is cheapest
+ for this pseudo-reg. If that fails, try any reg. */
+ if (N_REG_CLASSES > 1)
+ find_reg (allocno, forbidden_regs, 0, 0, 1);
+ if (reg_renumber[regno] < 0
+ && reg_alternate_class (regno) != NO_REGS)
+ find_reg (allocno, forbidden_regs, 1, 0, 1);
+
+ /* If we found a register, modify the RTL for the register to
+ show the hard register, and mark that register live. */
+ if (reg_renumber[regno] >= 0)
+ {
+ REGNO (regno_reg_rtx[regno]) = reg_renumber[regno];
+ mark_home_live (regno);
+ }
+ }
+}
+
+/* Record a conflict between register REGNO
+ and everything currently live.
+ REGNO must not be a pseudo reg that was allocated
+ by local_alloc; such numbers must be translated through
+ reg_renumber before calling here. */
+
+static void
+record_one_conflict (regno)
+ int regno;
+{
+ register int j;
+
+ if (regno < FIRST_PSEUDO_REGISTER)
+ /* When a hard register becomes live,
+ record conflicts with live pseudo regs. */
+ for (j = 0; j < max_allocno; j++)
+ {
+ if (ALLOCNO_LIVE_P (j))
+ SET_HARD_REG_BIT (hard_reg_conflicts[j], regno);
+ }
+ else
+ /* When a pseudo-register becomes live,
+ record conflicts first with hard regs,
+ then with other pseudo regs. */
+ {
+ register int ialloc = reg_allocno[regno];
+ register int ialloc_prod = ialloc * allocno_row_words;
+ IOR_HARD_REG_SET (hard_reg_conflicts[ialloc], hard_regs_live);
+ for (j = allocno_row_words - 1; j >= 0; j--)
+ {
+#if 0
+ int k;
+ for (k = 0; k < n_no_conflict_pairs; k++)
+ if (! ((j == no_conflict_pairs[k].allocno1
+ && ialloc == no_conflict_pairs[k].allocno2)
+ ||
+ (j == no_conflict_pairs[k].allocno2
+ && ialloc == no_conflict_pairs[k].allocno1)))
+#endif /* 0 */
+ conflicts[ialloc_prod + j] |= allocnos_live[j];
+ }
+ }
+}
+
+/* Record all allocnos currently live as conflicting
+ with each other and with all hard regs currently live.
+ ALLOCNO_VEC is a vector of LEN allocnos, all allocnos that
+ are currently live. Their bits are also flagged in allocnos_live. */
+
+static void
+record_conflicts (allocno_vec, len)
+ register short *allocno_vec;
+ register int len;
+{
+ register int allocno;
+ register int j;
+ register int ialloc_prod;
+
+ while (--len >= 0)
+ {
+ allocno = allocno_vec[len];
+ ialloc_prod = allocno * allocno_row_words;
+ IOR_HARD_REG_SET (hard_reg_conflicts[allocno], hard_regs_live);
+ for (j = allocno_row_words - 1; j >= 0; j--)
+ conflicts[ialloc_prod + j] |= allocnos_live[j];
+ }
+}
+
+/* Handle the case where REG is set by the insn being scanned,
+ during the forward scan to accumulate conflicts.
+ Store a 1 in regs_live or allocnos_live for this register, record how many
+ consecutive hardware registers it actually needs,
+ and record a conflict with all other registers already live.
+
+ Note that even if REG does not remain alive after this insn,
+ we must mark it here as live, to ensure a conflict between
+ REG and any other regs set in this insn that really do live.
+ This is because those other regs could be considered after this.
+
+ REG might actually be something other than a register;
+ if so, we do nothing.
+
+ SETTER is 0 if this register was modified by an auto-increment (i.e.,
+ a REG_INC note was found for it).
+
+ CLOBBERs are processed here by calling mark_reg_clobber. */
+
+static void
+mark_reg_store (orig_reg, setter)
+ rtx orig_reg, setter;
+{
+ register int regno;
+ register rtx reg = orig_reg;
+
+ /* WORD is which word of a multi-register group is being stored.
+ For the case where the store is actually into a SUBREG of REG.
+ Except we don't use it; I believe the entire REG needs to be
+ made live. */
+ int word = 0;
+
+ if (GET_CODE (reg) == SUBREG)
+ {
+ word = SUBREG_WORD (reg);
+ reg = SUBREG_REG (reg);
+ }
+
+ if (GET_CODE (reg) != REG)
+ return;
+
+ if (setter && GET_CODE (setter) == CLOBBER)
+ {
+ /* A clobber of a register should be processed here too. */
+ mark_reg_clobber (orig_reg, setter);
+ return;
+ }
+
+ regs_set[n_regs_set++] = reg;
+
+ if (setter)
+ set_preference (reg, SET_SRC (setter));
+
+ regno = REGNO (reg);
+
+ if (reg_renumber[regno] >= 0)
+ regno = reg_renumber[regno] /* + word */;
+
+ /* Either this is one of the max_allocno pseudo regs not allocated,
+ or it is or has a hardware reg. First handle the pseudo-regs. */
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ {
+ if (reg_allocno[regno] >= 0)
+ {
+ SET_ALLOCNO_LIVE (reg_allocno[regno]);
+ record_one_conflict (regno);
+ }
+ }
+ /* Handle hardware regs (and pseudos allocated to hard regs). */
+ else if (! fixed_regs[regno])
+ {
+ register int last = regno + HARD_REGNO_NREGS (regno, GET_MODE (reg));
+ while (regno < last)
+ {
+ record_one_conflict (regno);
+ SET_HARD_REG_BIT (hard_regs_live, regno);
+ regno++;
+ }
+ }
+}
+
+/* Like mark_reg_set except notice just CLOBBERs; ignore SETs. */
+
+static void
+mark_reg_clobber (reg, setter)
+ rtx reg, setter;
+{
+ register int regno;
+
+ /* WORD is which word of a multi-register group is being stored.
+ For the case where the store is actually into a SUBREG of REG.
+ Except we don't use it; I believe the entire REG needs to be
+ made live. */
+ int word = 0;
+
+ if (GET_CODE (setter) != CLOBBER)
+ return;
+
+ if (GET_CODE (reg) == SUBREG)
+ {
+ word = SUBREG_WORD (reg);
+ reg = SUBREG_REG (reg);
+ }
+
+ if (GET_CODE (reg) != REG)
+ return;
+
+ regs_set[n_regs_set++] = reg;
+
+ regno = REGNO (reg);
+
+ if (reg_renumber[regno] >= 0)
+ regno = reg_renumber[regno] /* + word */;
+
+ /* Either this is one of the max_allocno pseudo regs not allocated,
+ or it is or has a hardware reg. First handle the pseudo-regs. */
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ {
+ if (reg_allocno[regno] >= 0)
+ {
+ SET_ALLOCNO_LIVE (reg_allocno[regno]);
+ record_one_conflict (regno);
+ }
+ }
+ /* Handle hardware regs (and pseudos allocated to hard regs). */
+ else if (! fixed_regs[regno])
+ {
+ register int last = regno + HARD_REGNO_NREGS (regno, GET_MODE (reg));
+ while (regno < last)
+ {
+ record_one_conflict (regno);
+ SET_HARD_REG_BIT (hard_regs_live, regno);
+ regno++;
+ }
+ }
+}
+
+/* Record that REG has conflicts with all the regs currently live.
+ Do not mark REG itself as live. */
+
+static void
+mark_reg_conflicts (reg)
+ rtx reg;
+{
+ register int regno;
+
+ if (GET_CODE (reg) == SUBREG)
+ reg = SUBREG_REG (reg);
+
+ if (GET_CODE (reg) != REG)
+ return;
+
+ regno = REGNO (reg);
+
+ if (reg_renumber[regno] >= 0)
+ regno = reg_renumber[regno];
+
+ /* Either this is one of the max_allocno pseudo regs not allocated,
+ or it is or has a hardware reg. First handle the pseudo-regs. */
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ {
+ if (reg_allocno[regno] >= 0)
+ record_one_conflict (regno);
+ }
+ /* Handle hardware regs (and pseudos allocated to hard regs). */
+ else if (! fixed_regs[regno])
+ {
+ register int last = regno + HARD_REGNO_NREGS (regno, GET_MODE (reg));
+ while (regno < last)
+ {
+ record_one_conflict (regno);
+ regno++;
+ }
+ }
+}
+
+/* Mark REG as being dead (following the insn being scanned now).
+ Store a 0 in regs_live or allocnos_live for this register. */
+
+static void
+mark_reg_death (reg)
+ rtx reg;
+{
+ register int regno = REGNO (reg);
+
+ /* For pseudo reg, see if it has been assigned a hardware reg. */
+ if (reg_renumber[regno] >= 0)
+ regno = reg_renumber[regno];
+
+ /* Either this is one of the max_allocno pseudo regs not allocated,
+ or it is a hardware reg. First handle the pseudo-regs. */
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ {
+ if (reg_allocno[regno] >= 0)
+ CLEAR_ALLOCNO_LIVE (reg_allocno[regno]);
+ }
+ /* Handle hardware regs (and pseudos allocated to hard regs). */
+ else if (! fixed_regs[regno])
+ {
+ /* Pseudo regs already assigned hardware regs are treated
+ almost the same as explicit hardware regs. */
+ register int last = regno + HARD_REGNO_NREGS (regno, GET_MODE (reg));
+ while (regno < last)
+ {
+ CLEAR_HARD_REG_BIT (hard_regs_live, regno);
+ regno++;
+ }
+ }
+}
+
+/* Mark hard reg REGNO as currently live, assuming machine mode MODE
+ for the value stored in it. MODE determines how many consecutive
+ registers are actually in use. Do not record conflicts;
+ it is assumed that the caller will do that. */
+
+static void
+mark_reg_live_nc (regno, mode)
+ register int regno;
+ enum machine_mode mode;
+{
+ register int last = regno + HARD_REGNO_NREGS (regno, mode);
+ while (regno < last)
+ {
+ SET_HARD_REG_BIT (hard_regs_live, regno);
+ regno++;
+ }
+}
+
+/* Try to set a preference for an allocno to a hard register.
+ We are passed DEST and SRC which are the operands of a SET. It is known
+ that SRC is a register. If SRC or the first operand of SRC is a register,
+ try to set a preference. If one of the two is a hard register and the other
+ is a pseudo-register, mark the preference.
+
+ Note that we are not as aggressive as local-alloc in trying to tie a
+ pseudo-register to a hard register. */
+
+static void
+set_preference (dest, src)
+ rtx dest, src;
+{
+ int src_regno, dest_regno;
+ /* Amount to add to the hard regno for SRC, or subtract from that for DEST,
+ to compensate for subregs in SRC or DEST. */
+ int offset = 0;
+ int i;
+ int copy = 1;
+
+ if (GET_RTX_FORMAT (GET_CODE (src))[0] == 'e')
+ src = XEXP (src, 0), copy = 0;
+
+ /* Get the reg number for both SRC and DEST.
+ If neither is a reg, give up. */
+
+ if (GET_CODE (src) == REG)
+ src_regno = REGNO (src);
+ else if (GET_CODE (src) == SUBREG && GET_CODE (SUBREG_REG (src)) == REG)
+ {
+ src_regno = REGNO (SUBREG_REG (src));
+ offset += SUBREG_WORD (src);
+ }
+ else
+ return;
+
+ if (GET_CODE (dest) == REG)
+ dest_regno = REGNO (dest);
+ else if (GET_CODE (dest) == SUBREG && GET_CODE (SUBREG_REG (dest)) == REG)
+ {
+ dest_regno = REGNO (SUBREG_REG (dest));
+ offset -= SUBREG_WORD (dest);
+ }
+ else
+ return;
+
+ /* Convert either or both to hard reg numbers. */
+
+ if (reg_renumber[src_regno] >= 0)
+ src_regno = reg_renumber[src_regno];
+
+ if (reg_renumber[dest_regno] >= 0)
+ dest_regno = reg_renumber[dest_regno];
+
+ /* Now if one is a hard reg and the other is a global pseudo
+ then give the other a preference. */
+
+ if (dest_regno < FIRST_PSEUDO_REGISTER && src_regno >= FIRST_PSEUDO_REGISTER
+ && reg_allocno[src_regno] >= 0)
+ {
+ dest_regno -= offset;
+ if (dest_regno >= 0 && dest_regno < FIRST_PSEUDO_REGISTER)
+ {
+ if (copy)
+ SET_REGBIT (hard_reg_copy_preferences,
+ reg_allocno[src_regno], dest_regno);
+
+ SET_REGBIT (hard_reg_preferences,
+ reg_allocno[src_regno], dest_regno);
+ for (i = dest_regno;
+ i < dest_regno + HARD_REGNO_NREGS (dest_regno, GET_MODE (dest));
+ i++)
+ SET_REGBIT (hard_reg_full_preferences, reg_allocno[src_regno], i);
+ }
+ }
+
+ if (src_regno < FIRST_PSEUDO_REGISTER && dest_regno >= FIRST_PSEUDO_REGISTER
+ && reg_allocno[dest_regno] >= 0)
+ {
+ src_regno += offset;
+ if (src_regno >= 0 && src_regno < FIRST_PSEUDO_REGISTER)
+ {
+ if (copy)
+ SET_REGBIT (hard_reg_copy_preferences,
+ reg_allocno[dest_regno], src_regno);
+
+ SET_REGBIT (hard_reg_preferences,
+ reg_allocno[dest_regno], src_regno);
+ for (i = src_regno;
+ i < src_regno + HARD_REGNO_NREGS (src_regno, GET_MODE (src));
+ i++)
+ SET_REGBIT (hard_reg_full_preferences, reg_allocno[dest_regno], i);
+ }
+ }
+}
+
+/* Indicate that hard register number FROM was eliminated and replaced with
+ an offset from hard register number TO. The status of hard registers live
+ at the start of a basic block is updated by replacing a use of FROM with
+ a use of TO. */
+
+void
+mark_elimination (from, to)
+ int from, to;
+{
+ int i;
+
+ for (i = 0; i < n_basic_blocks; i++)
+ if ((basic_block_live_at_start[i][from / REGSET_ELT_BITS]
+ & ((REGSET_ELT_TYPE) 1 << (from % REGSET_ELT_BITS))) != 0)
+ {
+ basic_block_live_at_start[i][from / REGSET_ELT_BITS]
+ &= ~ ((REGSET_ELT_TYPE) 1 << (from % REGSET_ELT_BITS));
+ basic_block_live_at_start[i][to / REGSET_ELT_BITS]
+ |= ((REGSET_ELT_TYPE) 1 << (to % REGSET_ELT_BITS));
+ }
+}
+
+/* Print debugging trace information if -greg switch is given,
+ showing the information on which the allocation decisions are based. */
+
+static void
+dump_conflicts (file)
+ FILE *file;
+{
+ register int i;
+ register int has_preferences;
+ fprintf (file, ";; %d regs to allocate:", max_allocno);
+ for (i = 0; i < max_allocno; i++)
+ {
+ int j;
+ fprintf (file, " %d", allocno_reg[allocno_order[i]]);
+ for (j = 0; j < max_regno; j++)
+ if (reg_allocno[j] == allocno_order[i]
+ && j != allocno_reg[allocno_order[i]])
+ fprintf (file, "+%d", j);
+ if (allocno_size[allocno_order[i]] != 1)
+ fprintf (file, " (%d)", allocno_size[allocno_order[i]]);
+ }
+ fprintf (file, "\n");
+
+ for (i = 0; i < max_allocno; i++)
+ {
+ register int j;
+ fprintf (file, ";; %d conflicts:", allocno_reg[i]);
+ for (j = 0; j < max_allocno; j++)
+ if (CONFLICTP (i, j) || CONFLICTP (j, i))
+ fprintf (file, " %d", allocno_reg[j]);
+ for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
+ if (TEST_HARD_REG_BIT (hard_reg_conflicts[i], j))
+ fprintf (file, " %d", j);
+ fprintf (file, "\n");
+
+ has_preferences = 0;
+ for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
+ if (TEST_HARD_REG_BIT (hard_reg_preferences[i], j))
+ has_preferences = 1;
+
+ if (! has_preferences)
+ continue;
+ fprintf (file, ";; %d preferences:", allocno_reg[i]);
+ for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
+ if (TEST_HARD_REG_BIT (hard_reg_preferences[i], j))
+ fprintf (file, " %d", j);
+ fprintf (file, "\n");
+ }
+ fprintf (file, "\n");
+}
+
+void
+dump_global_regs (file)
+ FILE *file;
+{
+ register int i, j;
+
+ fprintf (file, ";; Register dispositions:\n");
+ for (i = FIRST_PSEUDO_REGISTER, j = 0; i < max_regno; i++)
+ if (reg_renumber[i] >= 0)
+ {
+ fprintf (file, "%d in %d ", i, reg_renumber[i]);
+ if (++j % 6 == 0)
+ fprintf (file, "\n");
+ }
+
+ fprintf (file, "\n\n;; Hard regs used: ");
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (regs_ever_live[i])
+ fprintf (file, " %d", i);
+ fprintf (file, "\n\n");
+}
diff --git a/gnu/usr.bin/cc/cc_int/insn-attrtab.c b/gnu/usr.bin/cc/cc_int/insn-attrtab.c
new file mode 100644
index 0000000..0e86d1f
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/insn-attrtab.c
@@ -0,0 +1,14 @@
+/* Generated automatically by the program `genattrtab'
+from the machine description file `md'. */
+
+#include "config.h"
+#include "rtl.h"
+#include "insn-config.h"
+#include "recog.h"
+#include "regs.h"
+#include "real.h"
+#include "output.h"
+#include "insn-attr.h"
+
+#define operands recog_operand
+
diff --git a/gnu/usr.bin/cc/cc_int/insn-emit.c b/gnu/usr.bin/cc/cc_int/insn-emit.c
new file mode 100644
index 0000000..01463d0
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/insn-emit.c
@@ -0,0 +1,3973 @@
+/* Generated automatically by the program `genemit'
+from the machine description file `md'. */
+
+#include "config.h"
+#include "rtl.h"
+#include "expr.h"
+#include "real.h"
+#include "output.h"
+#include "insn-config.h"
+
+#include "insn-flags.h"
+
+#include "insn-codes.h"
+
+extern char *insn_operand_constraint[][MAX_RECOG_OPERANDS];
+
+extern rtx recog_operand[];
+#define operands emit_operand
+
+#define FAIL goto _fail
+
+#define DONE goto _done
+
+rtx
+gen_tstsi_1 (operand0)
+ rtx operand0;
+{
+ return gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ operand0);
+}
+
+rtx
+gen_tstsi (operand0)
+ rtx operand0;
+{
+ rtx operands[1];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+
+{
+ i386_compare_gen = gen_tstsi_1;
+ i386_compare_op0 = operands[0];
+ DONE;
+}
+ operand0 = operands[0];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ operand0));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_tsthi_1 (operand0)
+ rtx operand0;
+{
+ return gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ operand0);
+}
+
+rtx
+gen_tsthi (operand0)
+ rtx operand0;
+{
+ rtx operands[1];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+
+{
+ i386_compare_gen = gen_tsthi_1;
+ i386_compare_op0 = operands[0];
+ DONE;
+}
+ operand0 = operands[0];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ operand0));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_tstqi_1 (operand0)
+ rtx operand0;
+{
+ return gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ operand0);
+}
+
+rtx
+gen_tstqi (operand0)
+ rtx operand0;
+{
+ rtx operands[1];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+
+{
+ i386_compare_gen = gen_tstqi_1;
+ i386_compare_op0 = operands[0];
+ DONE;
+}
+ operand0 = operands[0];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ operand0));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_tstsf_cc (operand0)
+ rtx operand0;
+{
+ return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ operand0),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, HImode, 0))));
+}
+
+rtx
+gen_tstsf (operand0)
+ rtx operand0;
+{
+ rtx operands[1];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+
+{
+ i386_compare_gen = gen_tstsf_cc;
+ i386_compare_op0 = operands[0];
+ DONE;
+}
+ operand0 = operands[0];
+ emit (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ operand0),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, HImode, 0)))));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_tstdf_cc (operand0)
+ rtx operand0;
+{
+ return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ operand0),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, HImode, 0))));
+}
+
+rtx
+gen_tstdf (operand0)
+ rtx operand0;
+{
+ rtx operands[1];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+
+{
+ i386_compare_gen = gen_tstdf_cc;
+ i386_compare_op0 = operands[0];
+ DONE;
+}
+ operand0 = operands[0];
+ emit (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ operand0),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, HImode, 0)))));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_tstxf_cc (operand0)
+ rtx operand0;
+{
+ return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ operand0),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, HImode, 0))));
+}
+
+rtx
+gen_tstxf (operand0)
+ rtx operand0;
+{
+ rtx operands[1];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+
+{
+ i386_compare_gen = gen_tstxf_cc;
+ i386_compare_op0 = operands[0];
+ DONE;
+}
+ operand0 = operands[0];
+ emit (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ operand0),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, HImode, 0)))));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_cmpsi_1 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ gen_rtx (COMPARE, VOIDmode,
+ operand0,
+ operand1));
+}
+
+rtx
+gen_cmpsi (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
+ operands[0] = force_reg (SImode, operands[0]);
+
+ i386_compare_gen = gen_cmpsi_1;
+ i386_compare_op0 = operands[0];
+ i386_compare_op1 = operands[1];
+ DONE;
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ gen_rtx (COMPARE, VOIDmode,
+ operand0,
+ operand1)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_cmphi_1 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ gen_rtx (COMPARE, VOIDmode,
+ operand0,
+ operand1));
+}
+
+rtx
+gen_cmphi (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
+ operands[0] = force_reg (HImode, operands[0]);
+
+ i386_compare_gen = gen_cmphi_1;
+ i386_compare_op0 = operands[0];
+ i386_compare_op1 = operands[1];
+ DONE;
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ gen_rtx (COMPARE, VOIDmode,
+ operand0,
+ operand1)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_cmpqi_1 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ gen_rtx (COMPARE, VOIDmode,
+ operand0,
+ operand1));
+}
+
+rtx
+gen_cmpqi (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
+ operands[0] = force_reg (QImode, operands[0]);
+
+ i386_compare_gen = gen_cmpqi_1;
+ i386_compare_op0 = operands[0];
+ i386_compare_op1 = operands[1];
+ DONE;
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ gen_rtx (COMPARE, VOIDmode,
+ operand0,
+ operand1)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_cmpsf_cc_1 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ gen_rtx (GET_CODE (operand2), VOIDmode,
+ operand0,
+ operand1)),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, HImode, 0))));
+}
+
+rtx
+gen_cmpxf (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ i386_compare_gen = gen_cmpxf_cc;
+ i386_compare_gen_eq = gen_cmpxf_ccfpeq;
+ i386_compare_op0 = operands[0];
+ i386_compare_op1 = operands[1];
+ DONE;
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ gen_rtx (COMPARE, VOIDmode,
+ operand0,
+ operand1)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_cmpdf (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ i386_compare_gen = gen_cmpdf_cc;
+ i386_compare_gen_eq = gen_cmpdf_ccfpeq;
+ i386_compare_op0 = operands[0];
+ i386_compare_op1 = operands[1];
+ DONE;
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ gen_rtx (COMPARE, VOIDmode,
+ operand0,
+ operand1)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_cmpsf (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ i386_compare_gen = gen_cmpsf_cc;
+ i386_compare_gen_eq = gen_cmpsf_ccfpeq;
+ i386_compare_op0 = operands[0];
+ i386_compare_op1 = operands[1];
+ DONE;
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ gen_rtx (COMPARE, VOIDmode,
+ operand0,
+ operand1)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_cmpxf_cc (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ gen_rtx (COMPARE, VOIDmode,
+ operand0,
+ operand1)),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, HImode, 0))));
+}
+
+rtx
+gen_cmpxf_ccfpeq (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ if (! register_operand (operands[1], XFmode))
+ operands[1] = copy_to_mode_reg (XFmode, operands[1]);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ gen_rtx (COMPARE, CCFPEQmode,
+ operand0,
+ operand1)),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, HImode, 0)))));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_cmpdf_cc (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ gen_rtx (COMPARE, VOIDmode,
+ operand0,
+ operand1)),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, HImode, 0))));
+}
+
+rtx
+gen_cmpdf_ccfpeq (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ if (! register_operand (operands[1], DFmode))
+ operands[1] = copy_to_mode_reg (DFmode, operands[1]);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ gen_rtx (COMPARE, CCFPEQmode,
+ operand0,
+ operand1)),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, HImode, 0)))));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_cmpsf_cc (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ gen_rtx (COMPARE, VOIDmode,
+ operand0,
+ operand1)),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, HImode, 0))));
+}
+
+rtx
+gen_cmpsf_ccfpeq (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ if (! register_operand (operands[1], SFmode))
+ operands[1] = copy_to_mode_reg (SFmode, operands[1]);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ gen_rtx (COMPARE, CCFPEQmode,
+ operand0,
+ operand1)),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, HImode, 0)))));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_movsi (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ extern int flag_pic;
+
+ if (flag_pic && SYMBOLIC_CONST (operands[1]))
+ emit_pic_move (operands, SImode);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ operand1));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_movhi (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ operand1);
+}
+
+rtx
+gen_movstricthi (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ gen_rtx (STRICT_LOW_PART, VOIDmode,
+ operand0),
+ operand1);
+}
+
+rtx
+gen_movqi (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ operand1);
+}
+
+rtx
+gen_movstrictqi (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ gen_rtx (STRICT_LOW_PART, VOIDmode,
+ operand0),
+ operand1);
+}
+
+rtx
+gen_movsf (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ operand1);
+}
+
+rtx
+gen_swapdf (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ operand0,
+ operand1),
+ gen_rtx (SET, VOIDmode,
+ operand1,
+ operand0)));
+}
+
+rtx
+gen_movdf (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ operand1);
+}
+
+rtx
+gen_swapxf (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ operand0,
+ operand1),
+ gen_rtx (SET, VOIDmode,
+ operand1,
+ operand0)));
+}
+
+rtx
+gen_movxf (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ operand1);
+}
+
+rtx
+gen_movdi (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ operand1);
+}
+
+rtx
+gen_zero_extendhisi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ZERO_EXTEND, SImode,
+ operand1));
+}
+
+rtx
+gen_zero_extendqihi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ZERO_EXTEND, HImode,
+ operand1));
+}
+
+rtx
+gen_zero_extendqisi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ZERO_EXTEND, SImode,
+ operand1));
+}
+
+rtx
+gen_zero_extendsidi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ZERO_EXTEND, DImode,
+ operand1));
+}
+
+rtx
+gen_extendsidi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (SIGN_EXTEND, DImode,
+ operand1));
+}
+
+rtx
+gen_extendhisi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (SIGN_EXTEND, SImode,
+ operand1));
+}
+
+rtx
+gen_extendqihi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (SIGN_EXTEND, HImode,
+ operand1));
+}
+
+rtx
+gen_extendqisi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (SIGN_EXTEND, SImode,
+ operand1));
+}
+
+rtx
+gen_extendsfdf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (FLOAT_EXTEND, DFmode,
+ operand1));
+}
+
+rtx
+gen_extenddfxf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (FLOAT_EXTEND, XFmode,
+ operand1));
+}
+
+rtx
+gen_extendsfxf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (FLOAT_EXTEND, XFmode,
+ operand1));
+}
+
+rtx
+gen_truncdfsf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operand2;
+ rtx operands[3];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ operands[2] = (rtx) assign_386_stack_local (SFmode, 0);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ emit (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (FLOAT_TRUNCATE, SFmode,
+ operand1)),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand2))));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_truncxfsf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (FLOAT_TRUNCATE, SFmode,
+ operand1));
+}
+
+rtx
+gen_truncxfdf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (FLOAT_TRUNCATE, DFmode,
+ operand1));
+}
+
+rtx
+gen_fixuns_truncxfsi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operand2;
+ rtx operand3;
+ rtx operand4;
+ rtx operand5;
+ rtx operand6;
+ rtx operands[7];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ operands[2] = gen_reg_rtx (DImode);
+ operands[3] = gen_lowpart (SImode, operands[2]);
+ operands[4] = gen_reg_rtx (XFmode);
+ operands[5] = (rtx) assign_386_stack_local (SImode, 0);
+ operands[6] = (rtx) assign_386_stack_local (SImode, 1);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ operand3 = operands[3];
+ operand4 = operands[4];
+ operand5 = operands[5];
+ operand6 = operands[6];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand4,
+ operand1));
+ emit (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (5,
+ gen_rtx (SET, VOIDmode,
+ operand2,
+ gen_rtx (FIX, DImode,
+ gen_rtx (FIX, XFmode,
+ operand4))),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand4),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand5),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand6),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, SImode, 0)))));
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ operand3));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_fixuns_truncdfsi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operand2;
+ rtx operand3;
+ rtx operand4;
+ rtx operand5;
+ rtx operand6;
+ rtx operands[7];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ operands[2] = gen_reg_rtx (DImode);
+ operands[3] = gen_lowpart (SImode, operands[2]);
+ operands[4] = gen_reg_rtx (DFmode);
+ operands[5] = (rtx) assign_386_stack_local (SImode, 0);
+ operands[6] = (rtx) assign_386_stack_local (SImode, 1);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ operand3 = operands[3];
+ operand4 = operands[4];
+ operand5 = operands[5];
+ operand6 = operands[6];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand4,
+ operand1));
+ emit (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (5,
+ gen_rtx (SET, VOIDmode,
+ operand2,
+ gen_rtx (FIX, DImode,
+ gen_rtx (FIX, DFmode,
+ operand4))),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand4),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand5),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand6),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, SImode, 0)))));
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ operand3));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_fixuns_truncsfsi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operand2;
+ rtx operand3;
+ rtx operand4;
+ rtx operand5;
+ rtx operand6;
+ rtx operands[7];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ operands[2] = gen_reg_rtx (DImode);
+ operands[3] = gen_lowpart (SImode, operands[2]);
+ operands[4] = gen_reg_rtx (SFmode);
+ operands[5] = (rtx) assign_386_stack_local (SImode, 0);
+ operands[6] = (rtx) assign_386_stack_local (SImode, 1);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ operand3 = operands[3];
+ operand4 = operands[4];
+ operand5 = operands[5];
+ operand6 = operands[6];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand4,
+ operand1));
+ emit (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (5,
+ gen_rtx (SET, VOIDmode,
+ operand2,
+ gen_rtx (FIX, DImode,
+ gen_rtx (FIX, SFmode,
+ operand4))),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand4),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand5),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand6),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, SImode, 0)))));
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ operand3));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_fix_truncxfdi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operand2;
+ rtx operand3;
+ rtx operand4;
+ rtx operands[5];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ operands[1] = copy_to_mode_reg (XFmode, operands[1]);
+ operands[2] = gen_reg_rtx (XFmode);
+ operands[3] = (rtx) assign_386_stack_local (SImode, 0);
+ operands[4] = (rtx) assign_386_stack_local (SImode, 1);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ operand3 = operands[3];
+ operand4 = operands[4];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand2,
+ operand1));
+ emit (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (5,
+ gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (FIX, DImode,
+ gen_rtx (FIX, XFmode,
+ operand2))),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand2),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand3),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand4),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, SImode, 0)))));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_fix_truncdfdi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operand2;
+ rtx operand3;
+ rtx operand4;
+ rtx operands[5];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ operands[1] = copy_to_mode_reg (DFmode, operands[1]);
+ operands[2] = gen_reg_rtx (DFmode);
+ operands[3] = (rtx) assign_386_stack_local (SImode, 0);
+ operands[4] = (rtx) assign_386_stack_local (SImode, 1);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ operand3 = operands[3];
+ operand4 = operands[4];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand2,
+ operand1));
+ emit (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (5,
+ gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (FIX, DImode,
+ gen_rtx (FIX, DFmode,
+ operand2))),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand2),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand3),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand4),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, SImode, 0)))));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_fix_truncsfdi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operand2;
+ rtx operand3;
+ rtx operand4;
+ rtx operands[5];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ operands[1] = copy_to_mode_reg (SFmode, operands[1]);
+ operands[2] = gen_reg_rtx (SFmode);
+ operands[3] = (rtx) assign_386_stack_local (SImode, 0);
+ operands[4] = (rtx) assign_386_stack_local (SImode, 1);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ operand3 = operands[3];
+ operand4 = operands[4];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand2,
+ operand1));
+ emit (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (5,
+ gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (FIX, DImode,
+ gen_rtx (FIX, SFmode,
+ operand2))),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand2),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand3),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand4),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, SImode, 0)))));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_fix_truncxfsi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operand2;
+ rtx operand3;
+ rtx operands[4];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ operands[2] = (rtx) assign_386_stack_local (SImode, 0);
+ operands[3] = (rtx) assign_386_stack_local (SImode, 1);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ operand3 = operands[3];
+ emit (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (4,
+ gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (FIX, SImode,
+ gen_rtx (FIX, XFmode,
+ operand1))),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand2),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand3),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, SImode, 0)))));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_fix_truncdfsi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operand2;
+ rtx operand3;
+ rtx operands[4];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ operands[2] = (rtx) assign_386_stack_local (SImode, 0);
+ operands[3] = (rtx) assign_386_stack_local (SImode, 1);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ operand3 = operands[3];
+ emit (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (4,
+ gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (FIX, SImode,
+ gen_rtx (FIX, DFmode,
+ operand1))),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand2),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand3),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, SImode, 0)))));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_fix_truncsfsi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operand2;
+ rtx operand3;
+ rtx operands[4];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ operands[2] = (rtx) assign_386_stack_local (SImode, 0);
+ operands[3] = (rtx) assign_386_stack_local (SImode, 1);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ operand3 = operands[3];
+ emit (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (4,
+ gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (FIX, SImode,
+ gen_rtx (FIX, SFmode,
+ operand1))),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand2),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand3),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, SImode, 0)))));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_floatsisf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (FLOAT, SFmode,
+ operand1));
+}
+
+rtx
+gen_floatdisf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (FLOAT, SFmode,
+ operand1));
+}
+
+rtx
+gen_floatsidf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (FLOAT, DFmode,
+ operand1));
+}
+
+rtx
+gen_floatdidf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (FLOAT, DFmode,
+ operand1));
+}
+
+rtx
+gen_floatsixf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (FLOAT, XFmode,
+ operand1));
+}
+
+rtx
+gen_floatdixf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (FLOAT, XFmode,
+ operand1));
+}
+
+rtx
+gen_adddi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (PLUS, DImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_addsi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (PLUS, SImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_addhi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (PLUS, HImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_addqi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (PLUS, QImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_addxf3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (PLUS, XFmode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_adddf3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (PLUS, DFmode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_addsf3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (PLUS, SFmode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_subdi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (MINUS, DImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_subsi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (MINUS, SImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_subhi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (MINUS, HImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_subqi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (MINUS, QImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_subxf3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (MINUS, XFmode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_subdf3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (MINUS, DFmode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_subsf3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (MINUS, SFmode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_mulhi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (MULT, HImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_mulsi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (MULT, SImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_umulqihi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (MULT, HImode,
+ gen_rtx (ZERO_EXTEND, HImode,
+ operand1),
+ gen_rtx (ZERO_EXTEND, HImode,
+ operand2)));
+}
+
+rtx
+gen_mulqihi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (MULT, HImode,
+ gen_rtx (SIGN_EXTEND, HImode,
+ operand1),
+ gen_rtx (SIGN_EXTEND, HImode,
+ operand2)));
+}
+
+rtx
+gen_umulsidi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (MULT, DImode,
+ gen_rtx (ZERO_EXTEND, DImode,
+ operand1),
+ gen_rtx (ZERO_EXTEND, DImode,
+ operand2)));
+}
+
+rtx
+gen_mulsidi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (MULT, DImode,
+ gen_rtx (SIGN_EXTEND, DImode,
+ operand1),
+ gen_rtx (SIGN_EXTEND, DImode,
+ operand2)));
+}
+
+rtx
+gen_mulxf3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (MULT, XFmode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_muldf3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (MULT, DFmode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_mulsf3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (MULT, SFmode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_divqi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (DIV, QImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_udivqi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (UDIV, QImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_divxf3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (DIV, XFmode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_divdf3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (DIV, DFmode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_divsf3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (DIV, SFmode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_divmodsi4 (operand0, operand1, operand2, operand3)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+ rtx operand3;
+{
+ return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (DIV, SImode,
+ operand1,
+ operand2)),
+ gen_rtx (SET, VOIDmode,
+ operand3,
+ gen_rtx (MOD, SImode,
+ operand1,
+ operand2))));
+}
+
+rtx
+gen_divmodhi4 (operand0, operand1, operand2, operand3)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+ rtx operand3;
+{
+ return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (DIV, HImode,
+ operand1,
+ operand2)),
+ gen_rtx (SET, VOIDmode,
+ operand3,
+ gen_rtx (MOD, HImode,
+ operand1,
+ operand2))));
+}
+
+rtx
+gen_udivmodsi4 (operand0, operand1, operand2, operand3)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+ rtx operand3;
+{
+ return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (UDIV, SImode,
+ operand1,
+ operand2)),
+ gen_rtx (SET, VOIDmode,
+ operand3,
+ gen_rtx (UMOD, SImode,
+ operand1,
+ operand2))));
+}
+
+rtx
+gen_udivmodhi4 (operand0, operand1, operand2, operand3)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+ rtx operand3;
+{
+ return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (UDIV, HImode,
+ operand1,
+ operand2)),
+ gen_rtx (SET, VOIDmode,
+ operand3,
+ gen_rtx (UMOD, HImode,
+ operand1,
+ operand2))));
+}
+
+rtx
+gen_andsi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (AND, SImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_andhi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (AND, HImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_andqi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (AND, QImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_iorsi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (IOR, SImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_iorhi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (IOR, HImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_iorqi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (IOR, QImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_xorsi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (XOR, SImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_xorhi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (XOR, HImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_xorqi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (XOR, QImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_negdi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (NEG, DImode,
+ operand1));
+}
+
+rtx
+gen_negsi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (NEG, SImode,
+ operand1));
+}
+
+rtx
+gen_neghi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (NEG, HImode,
+ operand1));
+}
+
+rtx
+gen_negqi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (NEG, QImode,
+ operand1));
+}
+
+rtx
+gen_negsf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (NEG, SFmode,
+ operand1));
+}
+
+rtx
+gen_negdf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (NEG, DFmode,
+ operand1));
+}
+
+rtx
+gen_negxf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (NEG, XFmode,
+ operand1));
+}
+
+rtx
+gen_abssf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ABS, SFmode,
+ operand1));
+}
+
+rtx
+gen_absdf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ABS, DFmode,
+ operand1));
+}
+
+rtx
+gen_absxf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ABS, XFmode,
+ operand1));
+}
+
+rtx
+gen_sqrtsf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (SQRT, SFmode,
+ operand1));
+}
+
+rtx
+gen_sqrtdf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (SQRT, DFmode,
+ operand1));
+}
+
+rtx
+gen_sqrtxf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (SQRT, XFmode,
+ operand1));
+}
+
+rtx
+gen_sindf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (UNSPEC, DFmode,
+ gen_rtvec (1,
+ operand1),
+ 1));
+}
+
+rtx
+gen_sinsf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (UNSPEC, SFmode,
+ gen_rtvec (1,
+ operand1),
+ 1));
+}
+
+rtx
+gen_cosdf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (UNSPEC, DFmode,
+ gen_rtvec (1,
+ operand1),
+ 2));
+}
+
+rtx
+gen_cossf2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (UNSPEC, SFmode,
+ gen_rtvec (1,
+ operand1),
+ 2));
+}
+
+rtx
+gen_one_cmplsi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (NOT, SImode,
+ operand1));
+}
+
+rtx
+gen_one_cmplhi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (NOT, HImode,
+ operand1));
+}
+
+rtx
+gen_one_cmplqi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (NOT, QImode,
+ operand1));
+}
+
+rtx
+gen_ashldi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ rtx operands[3];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+ operands[2] = operand2;
+
+{
+ if (GET_CODE (operands[2]) != CONST_INT
+ || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J'))
+ {
+ operands[2] = copy_to_mode_reg (QImode, operands[2]);
+ emit_insn (gen_ashldi3_non_const_int (operands[0], operands[1],
+ operands[2]));
+ }
+ else
+ emit_insn (gen_ashldi3_const_int (operands[0], operands[1], operands[2]));
+
+ DONE;
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ASHIFT, DImode,
+ operand1,
+ operand2)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_ashldi3_const_int (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ASHIFT, DImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_ashldi3_non_const_int (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ASHIFT, DImode,
+ operand1,
+ operand2)),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand2)));
+}
+
+rtx
+gen_ashlsi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ASHIFT, SImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_ashlhi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ASHIFT, HImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_ashlqi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ASHIFT, QImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_ashrdi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ rtx operands[3];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+ operands[2] = operand2;
+
+{
+ if (GET_CODE (operands[2]) != CONST_INT
+ || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J'))
+ {
+ operands[2] = copy_to_mode_reg (QImode, operands[2]);
+ emit_insn (gen_ashrdi3_non_const_int (operands[0], operands[1],
+ operands[2]));
+ }
+ else
+ emit_insn (gen_ashrdi3_const_int (operands[0], operands[1], operands[2]));
+
+ DONE;
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ASHIFTRT, DImode,
+ operand1,
+ operand2)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_ashrdi3_const_int (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ASHIFTRT, DImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_ashrdi3_non_const_int (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ASHIFTRT, DImode,
+ operand1,
+ operand2)),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand2)));
+}
+
+rtx
+gen_ashrsi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ASHIFTRT, SImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_ashrhi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ASHIFTRT, HImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_ashrqi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ASHIFTRT, QImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_lshrdi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ rtx operands[3];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+ operands[2] = operand2;
+
+{
+ if (GET_CODE (operands[2]) != CONST_INT
+ || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J'))
+ {
+ operands[2] = copy_to_mode_reg (QImode, operands[2]);
+ emit_insn (gen_lshrdi3_non_const_int (operands[0], operands[1],
+ operands[2]));
+ }
+ else
+ emit_insn (gen_lshrdi3_const_int (operands[0], operands[1], operands[2]));
+
+ DONE;
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (LSHIFTRT, DImode,
+ operand1,
+ operand2)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_lshrdi3_const_int (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (LSHIFTRT, DImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_lshrdi3_non_const_int (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (LSHIFTRT, DImode,
+ operand1,
+ operand2)),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand2)));
+}
+
+rtx
+gen_lshrsi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (LSHIFTRT, SImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_lshrhi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (LSHIFTRT, HImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_lshrqi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (LSHIFTRT, QImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_rotlsi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ROTATE, SImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_rotlhi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ROTATE, HImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_rotlqi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ROTATE, QImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_rotrsi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ROTATERT, SImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_rotrhi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ROTATERT, HImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_rotrqi3 (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ return gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (ROTATERT, QImode,
+ operand1,
+ operand2));
+}
+
+rtx
+gen_seq (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+
+{
+ if (TARGET_IEEE_FP
+ && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT)
+ operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1);
+ else
+ operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (EQ, QImode,
+ cc0_rtx,
+ const0_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_sne (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+
+{
+ if (TARGET_IEEE_FP
+ && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT)
+ operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1);
+ else
+ operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (NE, QImode,
+ cc0_rtx,
+ const0_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_sgt (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (GT, QImode,
+ cc0_rtx,
+ const0_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_sgtu (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (GTU, QImode,
+ cc0_rtx,
+ const0_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_slt (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (LT, QImode,
+ cc0_rtx,
+ const0_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_sltu (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (LTU, QImode,
+ cc0_rtx,
+ const0_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_sge (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (GE, QImode,
+ cc0_rtx,
+ const0_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_sgeu (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (GEU, QImode,
+ cc0_rtx,
+ const0_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_sle (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (LE, QImode,
+ cc0_rtx,
+ const0_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_sleu (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (LEU, QImode,
+ cc0_rtx,
+ const0_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_beq (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+
+{
+ if (TARGET_IEEE_FP
+ && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT)
+ operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1);
+ else
+ operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_jump_insn (gen_rtx (SET, VOIDmode,
+ pc_rtx,
+ gen_rtx (IF_THEN_ELSE, VOIDmode,
+ gen_rtx (EQ, VOIDmode,
+ cc0_rtx,
+ const0_rtx),
+ gen_rtx (LABEL_REF, VOIDmode,
+ operand0),
+ pc_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_bne (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+
+{
+ if (TARGET_IEEE_FP
+ && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT)
+ operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1);
+ else
+ operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_jump_insn (gen_rtx (SET, VOIDmode,
+ pc_rtx,
+ gen_rtx (IF_THEN_ELSE, VOIDmode,
+ gen_rtx (NE, VOIDmode,
+ cc0_rtx,
+ const0_rtx),
+ gen_rtx (LABEL_REF, VOIDmode,
+ operand0),
+ pc_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_bgt (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_jump_insn (gen_rtx (SET, VOIDmode,
+ pc_rtx,
+ gen_rtx (IF_THEN_ELSE, VOIDmode,
+ gen_rtx (GT, VOIDmode,
+ cc0_rtx,
+ const0_rtx),
+ gen_rtx (LABEL_REF, VOIDmode,
+ operand0),
+ pc_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_bgtu (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_jump_insn (gen_rtx (SET, VOIDmode,
+ pc_rtx,
+ gen_rtx (IF_THEN_ELSE, VOIDmode,
+ gen_rtx (GTU, VOIDmode,
+ cc0_rtx,
+ const0_rtx),
+ gen_rtx (LABEL_REF, VOIDmode,
+ operand0),
+ pc_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_blt (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_jump_insn (gen_rtx (SET, VOIDmode,
+ pc_rtx,
+ gen_rtx (IF_THEN_ELSE, VOIDmode,
+ gen_rtx (LT, VOIDmode,
+ cc0_rtx,
+ const0_rtx),
+ gen_rtx (LABEL_REF, VOIDmode,
+ operand0),
+ pc_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_bltu (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_jump_insn (gen_rtx (SET, VOIDmode,
+ pc_rtx,
+ gen_rtx (IF_THEN_ELSE, VOIDmode,
+ gen_rtx (LTU, VOIDmode,
+ cc0_rtx,
+ const0_rtx),
+ gen_rtx (LABEL_REF, VOIDmode,
+ operand0),
+ pc_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_bge (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_jump_insn (gen_rtx (SET, VOIDmode,
+ pc_rtx,
+ gen_rtx (IF_THEN_ELSE, VOIDmode,
+ gen_rtx (GE, VOIDmode,
+ cc0_rtx,
+ const0_rtx),
+ gen_rtx (LABEL_REF, VOIDmode,
+ operand0),
+ pc_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_bgeu (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_jump_insn (gen_rtx (SET, VOIDmode,
+ pc_rtx,
+ gen_rtx (IF_THEN_ELSE, VOIDmode,
+ gen_rtx (GEU, VOIDmode,
+ cc0_rtx,
+ const0_rtx),
+ gen_rtx (LABEL_REF, VOIDmode,
+ operand0),
+ pc_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_ble (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_jump_insn (gen_rtx (SET, VOIDmode,
+ pc_rtx,
+ gen_rtx (IF_THEN_ELSE, VOIDmode,
+ gen_rtx (LE, VOIDmode,
+ cc0_rtx,
+ const0_rtx),
+ gen_rtx (LABEL_REF, VOIDmode,
+ operand0),
+ pc_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_bleu (operand0)
+ rtx operand0;
+{
+ rtx operand1;
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand1);
+ emit_jump_insn (gen_rtx (SET, VOIDmode,
+ pc_rtx,
+ gen_rtx (IF_THEN_ELSE, VOIDmode,
+ gen_rtx (LEU, VOIDmode,
+ cc0_rtx,
+ const0_rtx),
+ gen_rtx (LABEL_REF, VOIDmode,
+ operand0),
+ pc_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_jump (operand0)
+ rtx operand0;
+{
+ return gen_rtx (SET, VOIDmode,
+ pc_rtx,
+ gen_rtx (LABEL_REF, VOIDmode,
+ operand0));
+}
+
+rtx
+gen_indirect_jump (operand0)
+ rtx operand0;
+{
+ return gen_rtx (SET, VOIDmode,
+ pc_rtx,
+ operand0);
+}
+
+rtx
+gen_casesi (operand0, operand1, operand2, operand3, operand4)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+ rtx operand3;
+ rtx operand4;
+{
+ rtx operand5;
+ rtx operands[6];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+ operands[2] = operand2;
+ operands[3] = operand3;
+ operands[4] = operand4;
+
+{
+ operands[5] = gen_reg_rtx (SImode);
+ current_function_uses_pic_offset_table = 1;
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ operand3 = operands[3];
+ operand4 = operands[4];
+ operand5 = operands[5];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand5,
+ gen_rtx (MINUS, SImode,
+ operand0,
+ operand1)));
+ emit_insn (gen_rtx (SET, VOIDmode,
+ cc0_rtx,
+ gen_rtx (COMPARE, CCmode,
+ operand5,
+ operand2)));
+ emit_jump_insn (gen_rtx (SET, VOIDmode,
+ pc_rtx,
+ gen_rtx (IF_THEN_ELSE, VOIDmode,
+ gen_rtx (GTU, VOIDmode,
+ cc0_rtx,
+ const0_rtx),
+ gen_rtx (LABEL_REF, VOIDmode,
+ operand4),
+ pc_rtx)));
+ emit_jump_insn (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ pc_rtx,
+ gen_rtx (MINUS, SImode,
+ gen_rtx (REG, SImode,
+ 3),
+ gen_rtx (MEM, SImode,
+ gen_rtx (PLUS, SImode,
+ gen_rtx (MULT, SImode,
+ operand5,
+ GEN_INT (4)),
+ gen_rtx (LABEL_REF, VOIDmode,
+ operand3))))),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, SImode, 0)))));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_tablejump (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ return gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ pc_rtx,
+ operand0),
+ gen_rtx (USE, VOIDmode,
+ gen_rtx (LABEL_REF, VOIDmode,
+ operand1))));
+}
+
+rtx
+gen_call_pop (operand0, operand1, operand2, operand3)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+ rtx operand3;
+{
+ rtx operands[4];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+ operands[2] = operand2;
+ operands[3] = operand3;
+
+{
+ rtx addr;
+
+ if (flag_pic)
+ current_function_uses_pic_offset_table = 1;
+
+ /* With half-pic, force the address into a register. */
+ addr = XEXP (operands[0], 0);
+ if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr))
+ XEXP (operands[0], 0) = force_reg (Pmode, addr);
+
+ if (! expander_call_insn_operand (operands[0], QImode))
+ operands[0]
+ = change_address (operands[0], VOIDmode,
+ copy_to_mode_reg (Pmode, XEXP (operands[0], 0)));
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ operand3 = operands[3];
+ emit_call_insn (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (2,
+ gen_rtx (CALL, VOIDmode,
+ operand0,
+ operand1),
+ gen_rtx (SET, VOIDmode,
+ gen_rtx (REG, SImode,
+ 7),
+ gen_rtx (PLUS, SImode,
+ gen_rtx (REG, SImode,
+ 7),
+ operand3)))));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_call (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ rtx addr;
+
+ if (flag_pic)
+ current_function_uses_pic_offset_table = 1;
+
+ /* With half-pic, force the address into a register. */
+ addr = XEXP (operands[0], 0);
+ if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr))
+ XEXP (operands[0], 0) = force_reg (Pmode, addr);
+
+ if (! expander_call_insn_operand (operands[0], QImode))
+ operands[0]
+ = change_address (operands[0], VOIDmode,
+ copy_to_mode_reg (Pmode, XEXP (operands[0], 0)));
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit_call_insn (gen_rtx (CALL, VOIDmode,
+ operand0,
+ operand1));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_call_value_pop (operand0, operand1, operand2, operand3, operand4)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+ rtx operand3;
+ rtx operand4;
+{
+ rtx operands[5];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+ operands[2] = operand2;
+ operands[3] = operand3;
+ operands[4] = operand4;
+
+{
+ rtx addr;
+
+ if (flag_pic)
+ current_function_uses_pic_offset_table = 1;
+
+ /* With half-pic, force the address into a register. */
+ addr = XEXP (operands[1], 0);
+ if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr))
+ XEXP (operands[1], 0) = force_reg (Pmode, addr);
+
+ if (! expander_call_insn_operand (operands[1], QImode))
+ operands[1]
+ = change_address (operands[1], VOIDmode,
+ copy_to_mode_reg (Pmode, XEXP (operands[1], 0)));
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ operand3 = operands[3];
+ operand4 = operands[4];
+ emit_call_insn (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (CALL, VOIDmode,
+ operand1,
+ operand2)),
+ gen_rtx (SET, VOIDmode,
+ gen_rtx (REG, SImode,
+ 7),
+ gen_rtx (PLUS, SImode,
+ gen_rtx (REG, SImode,
+ 7),
+ operand4)))));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_call_value (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ rtx operands[3];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+ operands[2] = operand2;
+
+{
+ rtx addr;
+
+ if (flag_pic)
+ current_function_uses_pic_offset_table = 1;
+
+ /* With half-pic, force the address into a register. */
+ addr = XEXP (operands[1], 0);
+ if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr))
+ XEXP (operands[1], 0) = force_reg (Pmode, addr);
+
+ if (! expander_call_insn_operand (operands[1], QImode))
+ operands[1]
+ = change_address (operands[1], VOIDmode,
+ copy_to_mode_reg (Pmode, XEXP (operands[1], 0)));
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ emit_call_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (CALL, VOIDmode,
+ operand1,
+ operand2)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_untyped_call (operand0, operand1, operand2)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+{
+ rtx operands[3];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+ operands[2] = operand2;
+
+{
+ rtx addr;
+
+ if (flag_pic)
+ current_function_uses_pic_offset_table = 1;
+
+ /* With half-pic, force the address into a register. */
+ addr = XEXP (operands[0], 0);
+ if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr))
+ XEXP (operands[0], 0) = force_reg (Pmode, addr);
+
+ operands[1] = change_address (operands[1], DImode, XEXP (operands[1], 0));
+ if (! expander_call_insn_operand (operands[1], QImode))
+ operands[1]
+ = change_address (operands[1], VOIDmode,
+ copy_to_mode_reg (Pmode, XEXP (operands[1], 0)));
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ emit_call_insn (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (3,
+ gen_rtx (CALL, VOIDmode,
+ operand0,
+ const0_rtx),
+ operand1,
+ operand2)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_untyped_return (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operands[2];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+
+{
+ rtx valreg1 = gen_rtx (REG, SImode, 0);
+ rtx valreg2 = gen_rtx (REG, SImode, 1);
+ rtx result = operands[0];
+
+ /* Restore the FPU state. */
+ emit_insn (gen_update_return (change_address (result, SImode,
+ plus_constant (XEXP (result, 0),
+ 8))));
+
+ /* Reload the function value registers. */
+ emit_move_insn (valreg1, change_address (result, SImode, XEXP (result, 0)));
+ emit_move_insn (valreg2,
+ change_address (result, SImode,
+ plus_constant (XEXP (result, 0), 4)));
+
+ /* Put USE insns before the return. */
+ emit_insn (gen_rtx (USE, VOIDmode, valreg1));
+ emit_insn (gen_rtx (USE, VOIDmode, valreg2));
+
+ /* Construct the return. */
+ expand_null_return ();
+
+ DONE;
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ emit (operand0);
+ emit (operand1);
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_update_return (operand0)
+ rtx operand0;
+{
+ return gen_rtx (UNSPEC, SImode,
+ gen_rtvec (1,
+ operand0),
+ 0);
+}
+
+rtx
+gen_return ()
+{
+ return gen_rtx (RETURN, VOIDmode);
+}
+
+rtx
+gen_nop ()
+{
+ return const0_rtx;
+}
+
+rtx
+gen_movstrsi (operand0, operand1, operand2, operand3)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+ rtx operand3;
+{
+ rtx operand4;
+ rtx operand5;
+ rtx operand6;
+ rtx operands[7];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+ operands[2] = operand2;
+ operands[3] = operand3;
+
+{
+ rtx addr0, addr1;
+
+ if (GET_CODE (operands[2]) != CONST_INT)
+ FAIL;
+
+ addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
+ addr1 = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
+
+ operands[5] = addr0;
+ operands[6] = addr1;
+
+ operands[0] = gen_rtx (MEM, BLKmode, addr0);
+ operands[1] = gen_rtx (MEM, BLKmode, addr1);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ operand3 = operands[3];
+ operand4 = operands[4];
+ operand5 = operands[5];
+ operand6 = operands[6];
+ emit (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (6,
+ gen_rtx (SET, VOIDmode,
+ operand0,
+ operand1),
+ gen_rtx (USE, VOIDmode,
+ operand2),
+ gen_rtx (USE, VOIDmode,
+ operand3),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, SImode, 0)),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand5),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand6))));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_cmpstrsi (operand0, operand1, operand2, operand3, operand4)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+ rtx operand3;
+ rtx operand4;
+{
+ rtx operand5;
+ rtx operand6;
+ rtx operands[7];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+ operands[2] = operand2;
+ operands[3] = operand3;
+ operands[4] = operand4;
+
+{
+ rtx addr1, addr2;
+
+ addr1 = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
+ addr2 = copy_to_mode_reg (Pmode, XEXP (operands[2], 0));
+ operands[3] = copy_to_mode_reg (SImode, operands[3]);
+
+ operands[5] = addr1;
+ operands[6] = addr2;
+
+ operands[1] = gen_rtx (MEM, BLKmode, addr1);
+ operands[2] = gen_rtx (MEM, BLKmode, addr2);
+
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ operand3 = operands[3];
+ operand4 = operands[4];
+ operand5 = operands[5];
+ operand6 = operands[6];
+ emit (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (6,
+ gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (COMPARE, SImode,
+ operand1,
+ operand2)),
+ gen_rtx (USE, VOIDmode,
+ operand3),
+ gen_rtx (USE, VOIDmode,
+ operand4),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand5),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand6),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand3))));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_ffssi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operand2;
+ rtx operands[3];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+operands[2] = gen_reg_rtx (SImode);
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand2,
+ gen_rtx (PLUS, SImode,
+ gen_rtx (FFS, SImode,
+ operand1),
+ constm1_rtx)));
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (PLUS, SImode,
+ operand2,
+ const1_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_ffshi2 (operand0, operand1)
+ rtx operand0;
+ rtx operand1;
+{
+ rtx operand2;
+ rtx operands[3];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+operands[2] = gen_reg_rtx (HImode);
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand2,
+ gen_rtx (PLUS, HImode,
+ gen_rtx (FFS, HImode,
+ operand1),
+ constm1_rtx)));
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (PLUS, HImode,
+ operand2,
+ const1_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+rtx
+gen_strlensi (operand0, operand1, operand2, operand3)
+ rtx operand0;
+ rtx operand1;
+ rtx operand2;
+ rtx operand3;
+{
+ rtx operand4;
+ rtx operand5;
+ rtx operands[6];
+ rtx _val = 0;
+ start_sequence ();
+ operands[0] = operand0;
+ operands[1] = operand1;
+ operands[2] = operand2;
+ operands[3] = operand3;
+
+{
+ operands[1] = copy_to_mode_reg (SImode, XEXP (operands[1], 0));
+ operands[4] = gen_reg_rtx (SImode);
+ operands[5] = gen_reg_rtx (SImode);
+}
+ operand0 = operands[0];
+ operand1 = operands[1];
+ operand2 = operands[2];
+ operand3 = operands[3];
+ operand4 = operands[4];
+ operand5 = operands[5];
+ emit (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (2,
+ gen_rtx (SET, VOIDmode,
+ operand4,
+ gen_rtx (UNSPEC, SImode,
+ gen_rtvec (3,
+ gen_rtx (MEM, BLKmode,
+ operand1),
+ operand2,
+ operand3),
+ 0)),
+ gen_rtx (CLOBBER, VOIDmode,
+ operand1))));
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand5,
+ gen_rtx (NOT, SImode,
+ operand4)));
+ emit_insn (gen_rtx (SET, VOIDmode,
+ operand0,
+ gen_rtx (MINUS, SImode,
+ operand5,
+ const1_rtx)));
+ _done:
+ _val = gen_sequence ();
+ _fail:
+ end_sequence ();
+ return _val;
+}
+
+
+
+void
+add_clobbers (pattern, insn_code_number)
+ rtx pattern;
+ int insn_code_number;
+{
+ int i;
+
+ switch (insn_code_number)
+ {
+ case 264:
+ XVECEXP (pattern, 0, 1) = gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, SImode, 0));
+ break;
+
+ case 95:
+ case 94:
+ case 93:
+ XVECEXP (pattern, 0, 3) = gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, SImode, 0));
+ break;
+
+ case 89:
+ case 88:
+ case 87:
+ XVECEXP (pattern, 0, 4) = gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, SImode, 0));
+ break;
+
+ case 33:
+ case 32:
+ case 31:
+ case 30:
+ case 29:
+ case 28:
+ case 27:
+ case 26:
+ case 25:
+ case 24:
+ case 23:
+ case 22:
+ case 21:
+ case 20:
+ case 19:
+ case 18:
+ case 10:
+ case 8:
+ case 6:
+ XVECEXP (pattern, 0, 1) = gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (SCRATCH, HImode, 0));
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+void
+init_mov_optab ()
+{
+#ifdef HAVE_movccfpeq
+ if (HAVE_movccfpeq)
+ mov_optab->handlers[(int) CCFPEQmode].insn_code = CODE_FOR_movccfpeq;
+#endif
+}
diff --git a/gnu/usr.bin/cc/cc_int/insn-extract.c b/gnu/usr.bin/cc/cc_int/insn-extract.c
new file mode 100644
index 0000000..7f6f1a4
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/insn-extract.c
@@ -0,0 +1,533 @@
+/* Generated automatically by the program `genextract'
+from the machine description file `md'. */
+
+#include "config.h"
+#include "rtl.h"
+
+static rtx junk;
+extern rtx recog_operand[];
+extern rtx *recog_operand_loc[];
+extern rtx *recog_dup_loc[];
+extern char recog_dup_num[];
+extern
+#ifdef __GNUC__
+__volatile__
+#endif
+void fatal_insn_not_found ();
+
+void
+insn_extract (insn)
+ rtx insn;
+{
+ register rtx *ro = recog_operand;
+ register rtx **ro_loc = recog_operand_loc;
+ rtx pat = PATTERN (insn);
+ switch (INSN_CODE (insn))
+ {
+ case -1:
+ fatal_insn_not_found (insn);
+
+ case 308:
+ ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XVECEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0, 0), 0));
+ ro[2] = *(ro_loc[2] = &XVECEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0, 1));
+ ro[3] = *(ro_loc[3] = &XVECEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0, 2));
+ recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 1), 0);
+ recog_dup_num[0] = 1;
+ break;
+
+ case 306:
+ case 303:
+ case 302:
+ case 300:
+ case 299:
+ ro[0] = *(ro_loc[0] = &XEXP (pat, 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 0));
+ ro[2] = *(ro_loc[2] = &XEXP (XEXP (XEXP (pat, 1), 1), 0));
+ ro[3] = *(ro_loc[3] = &XEXP (pat, 1));
+ break;
+
+ case 305:
+ case 301:
+ case 298:
+ case 297:
+ case 295:
+ ro[0] = *(ro_loc[0] = &XEXP (pat, 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (pat, 1), 0), 0));
+ ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 1), 1));
+ ro[3] = *(ro_loc[3] = &XEXP (pat, 1));
+ break;
+
+ case 304:
+ case 296:
+ case 294:
+ ro[0] = *(ro_loc[0] = &XEXP (pat, 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 0));
+ ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 1), 1));
+ ro[3] = *(ro_loc[3] = &XEXP (pat, 1));
+ break;
+
+ case 289:
+ ro[0] = *(ro_loc[0] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1), 0));
+ ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 1), 0));
+ ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 2), 0));
+ recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 5), 0);
+ recog_dup_num[0] = 2;
+ recog_dup_loc[1] = &XEXP (XVECEXP (pat, 0, 4), 0);
+ recog_dup_num[1] = 1;
+ recog_dup_loc[2] = &XEXP (XVECEXP (pat, 0, 3), 0);
+ recog_dup_num[2] = 0;
+ break;
+
+ case 288:
+ ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0));
+ ro[2] = *(ro_loc[2] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1), 0));
+ ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 1), 0));
+ ro[4] = *(ro_loc[4] = &XEXP (XVECEXP (pat, 0, 2), 0));
+ recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 5), 0);
+ recog_dup_num[0] = 3;
+ recog_dup_loc[1] = &XEXP (XVECEXP (pat, 0, 4), 0);
+ recog_dup_num[1] = 2;
+ recog_dup_loc[2] = &XEXP (XVECEXP (pat, 0, 3), 0);
+ recog_dup_num[2] = 1;
+ break;
+
+ case 286:
+ ro[0] = *(ro_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 0), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0));
+ ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 1), 0));
+ ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 2), 0));
+ ro[4] = *(ro_loc[4] = &XEXP (XVECEXP (pat, 0, 3), 0));
+ recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 5), 0);
+ recog_dup_num[0] = 1;
+ recog_dup_loc[1] = &XEXP (XVECEXP (pat, 0, 4), 0);
+ recog_dup_num[1] = 0;
+ break;
+
+ case 284:
+ case 283:
+ break;
+
+ case 282:
+ ro[0] = *(ro_loc[0] = &XVECEXP (pat, 0, 0));
+ break;
+
+ case 280:
+ ro[0] = *(ro_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 0), 0));
+ ro[1] = *(ro_loc[1] = &XVECEXP (pat, 0, 1));
+ ro[2] = *(ro_loc[2] = &XVECEXP (pat, 0, 2));
+ break;
+
+ case 279:
+ ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0));
+ ro[1] = *(ro_loc[1] = &XVECEXP (pat, 0, 1));
+ ro[2] = *(ro_loc[2] = &XVECEXP (pat, 0, 2));
+ break;
+
+ case 277:
+ ro[0] = *(ro_loc[0] = &XEXP (pat, 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (pat, 1), 0), 0));
+ ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 1), 1));
+ break;
+
+ case 274:
+ ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0));
+ ro[2] = *(ro_loc[2] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1));
+ ro[3] = const0_rtx;
+ ro_loc[3] = &junk;
+ ro[4] = *(ro_loc[4] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 1), 1));
+ break;
+
+ case 273:
+ ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0));
+ ro[2] = *(ro_loc[2] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1));
+ ro[3] = const0_rtx;
+ ro_loc[3] = &junk;
+ ro[4] = *(ro_loc[4] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 1), 1));
+ break;
+
+ case 268:
+ ro[0] = *(ro_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 0), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XVECEXP (pat, 0, 0), 1));
+ ro[2] = const0_rtx;
+ ro_loc[2] = &junk;
+ ro[3] = *(ro_loc[3] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 1), 1));
+ break;
+
+ case 267:
+ ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XVECEXP (pat, 0, 0), 1));
+ ro[2] = const0_rtx;
+ ro_loc[2] = &junk;
+ ro[3] = *(ro_loc[3] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 1), 1));
+ break;
+
+ case 265:
+ ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 1));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 0), 0));
+ break;
+
+ case 264:
+ ro[0] = *(ro_loc[0] = &XEXP (XEXP (XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1), 0), 0), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1), 0), 1), 0));
+ ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 1), 0));
+ break;
+
+ case 261:
+ ro[0] = *(ro_loc[0] = &XEXP (XEXP (pat, 1), 0));
+ break;
+
+ case 260:
+ case 259:
+ case 258:
+ case 257:
+ case 256:
+ case 255:
+ case 254:
+ case 253:
+ case 252:
+ case 251:
+ ro[0] = *(ro_loc[0] = &XEXP (XEXP (XEXP (pat, 1), 2), 0));
+ break;
+
+ case 250:
+ case 248:
+ case 246:
+ case 244:
+ case 242:
+ case 240:
+ case 238:
+ case 236:
+ case 234:
+ case 232:
+ ro[0] = *(ro_loc[0] = &XEXP (XEXP (XEXP (pat, 1), 1), 0));
+ break;
+
+ case 230:
+ case 228:
+ case 226:
+ case 224:
+ case 222:
+ case 220:
+ case 218:
+ case 216:
+ case 214:
+ case 212:
+ ro[0] = *(ro_loc[0] = &XEXP (pat, 0));
+ break;
+
+ case 210:
+ case 209:
+ ro[0] = *(ro_loc[0] = &XEXP (XEXP (pat, 1), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 1));
+ ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 1), 2));
+ break;
+
+ case 208:
+ ro[0] = *(ro_loc[0] = &XEXP (XEXP (pat, 1), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 2));
+ break;
+
+ case 207:
+ ro[0] = *(ro_loc[0] = &XEXP (pat, 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 0));
+ ro[2] = *(ro_loc[2] = &XEXP (XEXP (XEXP (pat, 1), 1), 1));
+ break;
+
+ case 206:
+ ro[0] = *(ro_loc[0] = &XEXP (pat, 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (pat, 1), 0), 1));
+ ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 1), 1));
+ break;
+
+ case 205:
+ ro[0] = *(ro_loc[0] = &XEXP (XEXP (pat, 0), 0));
+ ro[1] = const0_rtx;
+ ro_loc[1] = &junk;
+ ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 0), 2));
+ ro[3] = *(ro_loc[3] = &XEXP (pat, 1));
+ break;
+
+ case 195:
+ case 189:
+ case 183:
+ ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0));
+ ro[2] = *(ro_loc[2] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1));
+ recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 1), 0);
+ recog_dup_num[0] = 2;
+ break;
+
+ case 177:
+ case 174:
+ ro[0] = *(ro_loc[0] = &XEXP (pat, 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XVECEXP (XEXP (pat, 1), 0, 0), 0));
+ break;
+
+ case 176:
+ case 175:
+ case 173:
+ case 172:
+ ro[0] = *(ro_loc[0] = &XEXP (pat, 0));
+ ro[1] = *(ro_loc[1] = &XVECEXP (XEXP (pat, 1), 0, 0));
+ break;
+
+ case 293:
+ case 291:
+ case 171:
+ case 170:
+ case 168:
+ case 165:
+ case 163:
+ case 160:
+ case 158:
+ ro[0] = *(ro_loc[0] = &XEXP (pat, 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (pat, 1), 0), 0));
+ break;
+
+ case 142:
+ case 141:
+ case 140:
+ case 139:
+ ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0));
+ ro[2] = *(ro_loc[2] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1));
+ ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 1), 0));
+ recog_dup_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 1), 0);
+ recog_dup_num[0] = 1;
+ recog_dup_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 1), 1), 1);
+ recog_dup_num[1] = 2;
+ break;
+
+ case 130:
+ case 129:
+ case 128:
+ case 127:
+ ro[0] = *(ro_loc[0] = &XEXP (pat, 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (pat, 1), 0), 0));
+ ro[2] = *(ro_loc[2] = &XEXP (XEXP (XEXP (pat, 1), 1), 0));
+ break;
+
+ case 276:
+ case 204:
+ case 203:
+ case 202:
+ case 201:
+ case 200:
+ case 199:
+ case 198:
+ case 197:
+ case 196:
+ case 194:
+ case 192:
+ case 191:
+ case 190:
+ case 188:
+ case 186:
+ case 185:
+ case 184:
+ case 182:
+ case 151:
+ case 150:
+ case 149:
+ case 148:
+ case 147:
+ case 146:
+ case 145:
+ case 144:
+ case 143:
+ case 135:
+ case 134:
+ case 126:
+ case 125:
+ case 124:
+ case 123:
+ case 119:
+ case 118:
+ case 117:
+ case 116:
+ case 111:
+ case 110:
+ case 109:
+ case 108:
+ ro[0] = *(ro_loc[0] = &XEXP (pat, 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 0));
+ ro[2] = *(ro_loc[2] = &XEXP (XEXP (pat, 1), 1));
+ break;
+
+ case 95:
+ case 94:
+ case 93:
+ ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0));
+ ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 1), 0));
+ ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 2), 0));
+ ro[4] = *(ro_loc[4] = &XEXP (XVECEXP (pat, 0, 3), 0));
+ break;
+
+ case 89:
+ case 88:
+ case 87:
+ ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0));
+ ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 2), 0));
+ ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 3), 0));
+ ro[4] = *(ro_loc[4] = &XEXP (XVECEXP (pat, 0, 4), 0));
+ recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 1), 0);
+ recog_dup_num[0] = 1;
+ break;
+
+ case 78:
+ ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0));
+ ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 1), 0));
+ break;
+
+ case 180:
+ case 179:
+ case 178:
+ case 169:
+ case 167:
+ case 166:
+ case 164:
+ case 162:
+ case 161:
+ case 159:
+ case 157:
+ case 156:
+ case 155:
+ case 154:
+ case 153:
+ case 152:
+ case 107:
+ case 106:
+ case 105:
+ case 104:
+ case 103:
+ case 102:
+ case 80:
+ case 79:
+ case 76:
+ case 75:
+ case 74:
+ case 73:
+ case 72:
+ case 71:
+ case 70:
+ case 69:
+ case 68:
+ case 67:
+ case 66:
+ ro[0] = *(ro_loc[0] = &XEXP (pat, 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 0));
+ break;
+
+ case 62:
+ case 59:
+ ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XVECEXP (pat, 0, 0), 1));
+ recog_dup_loc[0] = &XEXP (XVECEXP (pat, 0, 1), 0);
+ recog_dup_num[0] = 1;
+ recog_dup_loc[1] = &XEXP (XVECEXP (pat, 0, 1), 1);
+ recog_dup_num[1] = 0;
+ break;
+
+ case 271:
+ case 55:
+ case 52:
+ ro[0] = *(ro_loc[0] = &XEXP (XEXP (pat, 0), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (pat, 1));
+ break;
+
+ case 270:
+ case 112:
+ case 65:
+ case 64:
+ case 63:
+ case 61:
+ case 60:
+ case 58:
+ case 57:
+ case 56:
+ case 54:
+ case 53:
+ case 51:
+ case 50:
+ case 49:
+ case 47:
+ case 46:
+ ro[0] = *(ro_loc[0] = &XEXP (pat, 0));
+ ro[1] = *(ro_loc[1] = &XEXP (pat, 1));
+ break;
+
+ case 33:
+ case 29:
+ case 23:
+ ro[0] = *(ro_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1));
+ ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 1), 0));
+ break;
+
+ case 32:
+ case 28:
+ case 26:
+ case 20:
+ ro[0] = *(ro_loc[0] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1));
+ ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 0), 1));
+ ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 1), 0));
+ break;
+
+ case 31:
+ case 27:
+ case 25:
+ case 22:
+ case 21:
+ case 19:
+ ro[0] = *(ro_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1), 0));
+ ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 0), 1));
+ ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 1), 0));
+ break;
+
+ case 30:
+ case 24:
+ case 18:
+ ro[0] = *(ro_loc[0] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 1));
+ ro[2] = *(ro_loc[2] = &XEXP (XVECEXP (pat, 0, 0), 1));
+ ro[3] = *(ro_loc[3] = &XEXP (XVECEXP (pat, 0, 1), 0));
+ break;
+
+ case 45:
+ case 44:
+ case 43:
+ case 16:
+ case 14:
+ case 12:
+ ro[0] = *(ro_loc[0] = &XEXP (XEXP (pat, 1), 0));
+ ro[1] = *(ro_loc[1] = &XEXP (XEXP (pat, 1), 1));
+ break;
+
+ case 10:
+ case 8:
+ case 6:
+ ro[0] = *(ro_loc[0] = &XEXP (XVECEXP (pat, 0, 0), 1));
+ ro[1] = *(ro_loc[1] = &XEXP (XVECEXP (pat, 0, 1), 0));
+ break;
+
+ case 262:
+ case 4:
+ case 2:
+ case 0:
+ ro[0] = *(ro_loc[0] = &XEXP (pat, 1));
+ break;
+
+ default:
+ abort ();
+ }
+}
diff --git a/gnu/usr.bin/cc/cc_int/insn-opinit.c b/gnu/usr.bin/cc/cc_int/insn-opinit.c
new file mode 100644
index 0000000..8ad7929
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/insn-opinit.c
@@ -0,0 +1,216 @@
+/* Generated automatically by the program `genopinit'
+from the machine description file `md'. */
+
+#include "config.h"
+#include "rtl.h"
+#include "flags.h"
+#include "insn-flags.h"
+#include "insn-codes.h"
+#include "insn-config.h"
+#include "recog.h"
+#include "expr.h"
+#include "reload.h"
+
+void
+init_all_optabs ()
+{
+ tst_optab->handlers[(int) SImode].insn_code = CODE_FOR_tstsi;
+ tst_optab->handlers[(int) HImode].insn_code = CODE_FOR_tsthi;
+ tst_optab->handlers[(int) QImode].insn_code = CODE_FOR_tstqi;
+ if (HAVE_tstsf)
+ tst_optab->handlers[(int) SFmode].insn_code = CODE_FOR_tstsf;
+ if (HAVE_tstdf)
+ tst_optab->handlers[(int) DFmode].insn_code = CODE_FOR_tstdf;
+ if (HAVE_tstxf)
+ tst_optab->handlers[(int) XFmode].insn_code = CODE_FOR_tstxf;
+ cmp_optab->handlers[(int) SImode].insn_code = CODE_FOR_cmpsi;
+ cmp_optab->handlers[(int) HImode].insn_code = CODE_FOR_cmphi;
+ cmp_optab->handlers[(int) QImode].insn_code = CODE_FOR_cmpqi;
+ if (HAVE_cmpxf)
+ cmp_optab->handlers[(int) XFmode].insn_code = CODE_FOR_cmpxf;
+ if (HAVE_cmpdf)
+ cmp_optab->handlers[(int) DFmode].insn_code = CODE_FOR_cmpdf;
+ if (HAVE_cmpsf)
+ cmp_optab->handlers[(int) SFmode].insn_code = CODE_FOR_cmpsf;
+ mov_optab->handlers[(int) SImode].insn_code = CODE_FOR_movsi;
+ mov_optab->handlers[(int) HImode].insn_code = CODE_FOR_movhi;
+ movstrict_optab->handlers[(int) HImode].insn_code = CODE_FOR_movstricthi;
+ mov_optab->handlers[(int) QImode].insn_code = CODE_FOR_movqi;
+ movstrict_optab->handlers[(int) QImode].insn_code = CODE_FOR_movstrictqi;
+ mov_optab->handlers[(int) SFmode].insn_code = CODE_FOR_movsf;
+ mov_optab->handlers[(int) DFmode].insn_code = CODE_FOR_movdf;
+ mov_optab->handlers[(int) XFmode].insn_code = CODE_FOR_movxf;
+ mov_optab->handlers[(int) DImode].insn_code = CODE_FOR_movdi;
+ extendtab[(int) SImode][(int) HImode][1] = CODE_FOR_zero_extendhisi2;
+ extendtab[(int) HImode][(int) QImode][1] = CODE_FOR_zero_extendqihi2;
+ extendtab[(int) SImode][(int) QImode][1] = CODE_FOR_zero_extendqisi2;
+ extendtab[(int) DImode][(int) SImode][1] = CODE_FOR_zero_extendsidi2;
+ extendtab[(int) DImode][(int) SImode][0] = CODE_FOR_extendsidi2;
+ extendtab[(int) SImode][(int) HImode][0] = CODE_FOR_extendhisi2;
+ extendtab[(int) HImode][(int) QImode][0] = CODE_FOR_extendqihi2;
+ extendtab[(int) SImode][(int) QImode][0] = CODE_FOR_extendqisi2;
+ if (HAVE_extendsfdf2)
+ extendtab[(int) DFmode][(int) SFmode][0] = CODE_FOR_extendsfdf2;
+ if (HAVE_extenddfxf2)
+ extendtab[(int) XFmode][(int) DFmode][0] = CODE_FOR_extenddfxf2;
+ if (HAVE_extendsfxf2)
+ extendtab[(int) XFmode][(int) SFmode][0] = CODE_FOR_extendsfxf2;
+ if (HAVE_fixuns_truncxfsi2)
+ fixtrunctab[(int) XFmode][(int) SImode][1] = CODE_FOR_fixuns_truncxfsi2;
+ if (HAVE_fixuns_truncdfsi2)
+ fixtrunctab[(int) DFmode][(int) SImode][1] = CODE_FOR_fixuns_truncdfsi2;
+ if (HAVE_fixuns_truncsfsi2)
+ fixtrunctab[(int) SFmode][(int) SImode][1] = CODE_FOR_fixuns_truncsfsi2;
+ if (HAVE_fix_truncxfdi2)
+ fixtrunctab[(int) XFmode][(int) DImode][0] = CODE_FOR_fix_truncxfdi2;
+ if (HAVE_fix_truncdfdi2)
+ fixtrunctab[(int) DFmode][(int) DImode][0] = CODE_FOR_fix_truncdfdi2;
+ if (HAVE_fix_truncsfdi2)
+ fixtrunctab[(int) SFmode][(int) DImode][0] = CODE_FOR_fix_truncsfdi2;
+ if (HAVE_fix_truncxfsi2)
+ fixtrunctab[(int) XFmode][(int) SImode][0] = CODE_FOR_fix_truncxfsi2;
+ if (HAVE_fix_truncdfsi2)
+ fixtrunctab[(int) DFmode][(int) SImode][0] = CODE_FOR_fix_truncdfsi2;
+ if (HAVE_fix_truncsfsi2)
+ fixtrunctab[(int) SFmode][(int) SImode][0] = CODE_FOR_fix_truncsfsi2;
+ if (HAVE_floatsisf2)
+ floattab[(int) SFmode][(int) SImode][0] = CODE_FOR_floatsisf2;
+ if (HAVE_floatdisf2)
+ floattab[(int) SFmode][(int) DImode][0] = CODE_FOR_floatdisf2;
+ if (HAVE_floatsidf2)
+ floattab[(int) DFmode][(int) SImode][0] = CODE_FOR_floatsidf2;
+ if (HAVE_floatdidf2)
+ floattab[(int) DFmode][(int) DImode][0] = CODE_FOR_floatdidf2;
+ if (HAVE_floatsixf2)
+ floattab[(int) XFmode][(int) SImode][0] = CODE_FOR_floatsixf2;
+ if (HAVE_floatdixf2)
+ floattab[(int) XFmode][(int) DImode][0] = CODE_FOR_floatdixf2;
+ add_optab->handlers[(int) DImode].insn_code = CODE_FOR_adddi3;
+ add_optab->handlers[(int) SImode].insn_code = CODE_FOR_addsi3;
+ add_optab->handlers[(int) HImode].insn_code = CODE_FOR_addhi3;
+ add_optab->handlers[(int) QImode].insn_code = CODE_FOR_addqi3;
+ if (HAVE_addxf3)
+ add_optab->handlers[(int) XFmode].insn_code = CODE_FOR_addxf3;
+ if (HAVE_adddf3)
+ add_optab->handlers[(int) DFmode].insn_code = CODE_FOR_adddf3;
+ if (HAVE_addsf3)
+ add_optab->handlers[(int) SFmode].insn_code = CODE_FOR_addsf3;
+ sub_optab->handlers[(int) DImode].insn_code = CODE_FOR_subdi3;
+ sub_optab->handlers[(int) SImode].insn_code = CODE_FOR_subsi3;
+ sub_optab->handlers[(int) HImode].insn_code = CODE_FOR_subhi3;
+ sub_optab->handlers[(int) QImode].insn_code = CODE_FOR_subqi3;
+ if (HAVE_subxf3)
+ sub_optab->handlers[(int) XFmode].insn_code = CODE_FOR_subxf3;
+ if (HAVE_subdf3)
+ sub_optab->handlers[(int) DFmode].insn_code = CODE_FOR_subdf3;
+ if (HAVE_subsf3)
+ sub_optab->handlers[(int) SFmode].insn_code = CODE_FOR_subsf3;
+ smul_optab->handlers[(int) HImode].insn_code = CODE_FOR_mulhi3;
+ smul_optab->handlers[(int) SImode].insn_code = CODE_FOR_mulsi3;
+ umul_widen_optab->handlers[(int) HImode].insn_code = CODE_FOR_umulqihi3;
+ smul_widen_optab->handlers[(int) HImode].insn_code = CODE_FOR_mulqihi3;
+ umul_widen_optab->handlers[(int) DImode].insn_code = CODE_FOR_umulsidi3;
+ smul_widen_optab->handlers[(int) DImode].insn_code = CODE_FOR_mulsidi3;
+ if (HAVE_mulxf3)
+ smul_optab->handlers[(int) XFmode].insn_code = CODE_FOR_mulxf3;
+ if (HAVE_muldf3)
+ smul_optab->handlers[(int) DFmode].insn_code = CODE_FOR_muldf3;
+ if (HAVE_mulsf3)
+ smul_optab->handlers[(int) SFmode].insn_code = CODE_FOR_mulsf3;
+ sdiv_optab->handlers[(int) QImode].insn_code = CODE_FOR_divqi3;
+ udiv_optab->handlers[(int) QImode].insn_code = CODE_FOR_udivqi3;
+ if (HAVE_divxf3)
+ flodiv_optab->handlers[(int) XFmode].insn_code = CODE_FOR_divxf3;
+ if (HAVE_divdf3)
+ flodiv_optab->handlers[(int) DFmode].insn_code = CODE_FOR_divdf3;
+ if (HAVE_divsf3)
+ flodiv_optab->handlers[(int) SFmode].insn_code = CODE_FOR_divsf3;
+ sdivmod_optab->handlers[(int) SImode].insn_code = CODE_FOR_divmodsi4;
+ sdivmod_optab->handlers[(int) HImode].insn_code = CODE_FOR_divmodhi4;
+ udivmod_optab->handlers[(int) SImode].insn_code = CODE_FOR_udivmodsi4;
+ udivmod_optab->handlers[(int) HImode].insn_code = CODE_FOR_udivmodhi4;
+ and_optab->handlers[(int) SImode].insn_code = CODE_FOR_andsi3;
+ and_optab->handlers[(int) HImode].insn_code = CODE_FOR_andhi3;
+ and_optab->handlers[(int) QImode].insn_code = CODE_FOR_andqi3;
+ ior_optab->handlers[(int) SImode].insn_code = CODE_FOR_iorsi3;
+ ior_optab->handlers[(int) HImode].insn_code = CODE_FOR_iorhi3;
+ ior_optab->handlers[(int) QImode].insn_code = CODE_FOR_iorqi3;
+ xor_optab->handlers[(int) SImode].insn_code = CODE_FOR_xorsi3;
+ xor_optab->handlers[(int) HImode].insn_code = CODE_FOR_xorhi3;
+ xor_optab->handlers[(int) QImode].insn_code = CODE_FOR_xorqi3;
+ neg_optab->handlers[(int) DImode].insn_code = CODE_FOR_negdi2;
+ neg_optab->handlers[(int) SImode].insn_code = CODE_FOR_negsi2;
+ neg_optab->handlers[(int) HImode].insn_code = CODE_FOR_neghi2;
+ neg_optab->handlers[(int) QImode].insn_code = CODE_FOR_negqi2;
+ if (HAVE_negsf2)
+ neg_optab->handlers[(int) SFmode].insn_code = CODE_FOR_negsf2;
+ if (HAVE_negdf2)
+ neg_optab->handlers[(int) DFmode].insn_code = CODE_FOR_negdf2;
+ if (HAVE_negxf2)
+ neg_optab->handlers[(int) XFmode].insn_code = CODE_FOR_negxf2;
+ if (HAVE_abssf2)
+ abs_optab->handlers[(int) SFmode].insn_code = CODE_FOR_abssf2;
+ if (HAVE_absdf2)
+ abs_optab->handlers[(int) DFmode].insn_code = CODE_FOR_absdf2;
+ if (HAVE_absxf2)
+ abs_optab->handlers[(int) XFmode].insn_code = CODE_FOR_absxf2;
+ if (HAVE_sqrtsf2)
+ sqrt_optab->handlers[(int) SFmode].insn_code = CODE_FOR_sqrtsf2;
+ if (HAVE_sqrtdf2)
+ sqrt_optab->handlers[(int) DFmode].insn_code = CODE_FOR_sqrtdf2;
+ if (HAVE_sqrtxf2)
+ sqrt_optab->handlers[(int) XFmode].insn_code = CODE_FOR_sqrtxf2;
+ if (HAVE_sindf2)
+ sin_optab->handlers[(int) DFmode].insn_code = CODE_FOR_sindf2;
+ if (HAVE_sinsf2)
+ sin_optab->handlers[(int) SFmode].insn_code = CODE_FOR_sinsf2;
+ if (HAVE_cosdf2)
+ cos_optab->handlers[(int) DFmode].insn_code = CODE_FOR_cosdf2;
+ if (HAVE_cossf2)
+ cos_optab->handlers[(int) SFmode].insn_code = CODE_FOR_cossf2;
+ one_cmpl_optab->handlers[(int) SImode].insn_code = CODE_FOR_one_cmplsi2;
+ one_cmpl_optab->handlers[(int) HImode].insn_code = CODE_FOR_one_cmplhi2;
+ one_cmpl_optab->handlers[(int) QImode].insn_code = CODE_FOR_one_cmplqi2;
+ ashl_optab->handlers[(int) DImode].insn_code = CODE_FOR_ashldi3;
+ ashl_optab->handlers[(int) SImode].insn_code = CODE_FOR_ashlsi3;
+ ashl_optab->handlers[(int) HImode].insn_code = CODE_FOR_ashlhi3;
+ ashl_optab->handlers[(int) QImode].insn_code = CODE_FOR_ashlqi3;
+ ashr_optab->handlers[(int) DImode].insn_code = CODE_FOR_ashrdi3;
+ ashr_optab->handlers[(int) SImode].insn_code = CODE_FOR_ashrsi3;
+ ashr_optab->handlers[(int) HImode].insn_code = CODE_FOR_ashrhi3;
+ ashr_optab->handlers[(int) QImode].insn_code = CODE_FOR_ashrqi3;
+ lshr_optab->handlers[(int) DImode].insn_code = CODE_FOR_lshrdi3;
+ lshr_optab->handlers[(int) SImode].insn_code = CODE_FOR_lshrsi3;
+ lshr_optab->handlers[(int) HImode].insn_code = CODE_FOR_lshrhi3;
+ lshr_optab->handlers[(int) QImode].insn_code = CODE_FOR_lshrqi3;
+ rotl_optab->handlers[(int) SImode].insn_code = CODE_FOR_rotlsi3;
+ rotl_optab->handlers[(int) HImode].insn_code = CODE_FOR_rotlhi3;
+ rotl_optab->handlers[(int) QImode].insn_code = CODE_FOR_rotlqi3;
+ rotr_optab->handlers[(int) SImode].insn_code = CODE_FOR_rotrsi3;
+ rotr_optab->handlers[(int) HImode].insn_code = CODE_FOR_rotrhi3;
+ rotr_optab->handlers[(int) QImode].insn_code = CODE_FOR_rotrqi3;
+ setcc_gen_code[(int) EQ] = CODE_FOR_seq;
+ setcc_gen_code[(int) NE] = CODE_FOR_sne;
+ setcc_gen_code[(int) GT] = CODE_FOR_sgt;
+ setcc_gen_code[(int) GTU] = CODE_FOR_sgtu;
+ setcc_gen_code[(int) LT] = CODE_FOR_slt;
+ setcc_gen_code[(int) LTU] = CODE_FOR_sltu;
+ setcc_gen_code[(int) GE] = CODE_FOR_sge;
+ setcc_gen_code[(int) GEU] = CODE_FOR_sgeu;
+ setcc_gen_code[(int) LE] = CODE_FOR_sle;
+ setcc_gen_code[(int) LEU] = CODE_FOR_sleu;
+ bcc_gen_fctn[(int) EQ] = gen_beq;
+ bcc_gen_fctn[(int) NE] = gen_bne;
+ bcc_gen_fctn[(int) GT] = gen_bgt;
+ bcc_gen_fctn[(int) GTU] = gen_bgtu;
+ bcc_gen_fctn[(int) LT] = gen_blt;
+ bcc_gen_fctn[(int) LTU] = gen_bltu;
+ bcc_gen_fctn[(int) GE] = gen_bge;
+ bcc_gen_fctn[(int) GEU] = gen_bgeu;
+ bcc_gen_fctn[(int) LE] = gen_ble;
+ bcc_gen_fctn[(int) LEU] = gen_bleu;
+ movstr_optab[(int) SImode] = CODE_FOR_movstrsi;
+ ffs_optab->handlers[(int) SImode].insn_code = CODE_FOR_ffssi2;
+ ffs_optab->handlers[(int) HImode].insn_code = CODE_FOR_ffshi2;
+ strlen_optab->handlers[(int) SImode].insn_code = CODE_FOR_strlensi;
+}
diff --git a/gnu/usr.bin/cc/cc_int/insn-output.c b/gnu/usr.bin/cc/cc_int/insn-output.c
new file mode 100644
index 0000000..b354cf4
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/insn-output.c
@@ -0,0 +1,6865 @@
+/* Generated automatically by the program `genoutput'
+from the machine description file `md'. */
+
+#include "config.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "real.h"
+#include "insn-config.h"
+
+#include "conditions.h"
+#include "insn-flags.h"
+#include "insn-attr.h"
+
+#include "insn-codes.h"
+
+#include "recog.h"
+
+#include <stdio.h>
+#include "output.h"
+
+static char *
+output_0 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REG_P (operands[0]))
+ return AS2 (test%L0,%0,%0);
+
+ operands[1] = const0_rtx;
+ return AS2 (cmp%L0,%1,%0);
+}
+}
+
+static char *
+output_2 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REG_P (operands[0]))
+ return AS2 (test%W0,%0,%0);
+
+ operands[1] = const0_rtx;
+ return AS2 (cmp%W0,%1,%0);
+}
+}
+
+static char *
+output_4 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REG_P (operands[0]))
+ return AS2 (test%B0,%0,%0);
+
+ operands[1] = const0_rtx;
+ return AS2 (cmp%B0,%1,%0);
+}
+}
+
+static char *
+output_6 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (! STACK_TOP_P (operands[0]))
+ abort ();
+
+ output_asm_insn ("ftst", operands);
+
+ if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
+ output_asm_insn (AS1 (fstp,%y0), operands);
+
+ return (char *) output_fp_cc0_set (insn);
+}
+}
+
+static char *
+output_8 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (! STACK_TOP_P (operands[0]))
+ abort ();
+
+ output_asm_insn ("ftst", operands);
+
+ if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
+ output_asm_insn (AS1 (fstp,%y0), operands);
+
+ return (char *) output_fp_cc0_set (insn);
+}
+}
+
+static char *
+output_10 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (! STACK_TOP_P (operands[0]))
+ abort ();
+
+ output_asm_insn ("ftst", operands);
+
+ if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
+ output_asm_insn (AS1 (fstp,%y0), operands);
+
+ return (char *) output_fp_cc0_set (insn);
+}
+}
+
+static char *
+output_12 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (CONSTANT_P (operands[0]) || GET_CODE (operands[1]) == MEM)
+ {
+ cc_status.flags |= CC_REVERSED;
+ return AS2 (cmp%L0,%0,%1);
+ }
+ return AS2 (cmp%L0,%1,%0);
+}
+}
+
+static char *
+output_14 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (CONSTANT_P (operands[0]) || GET_CODE (operands[1]) == MEM)
+ {
+ cc_status.flags |= CC_REVERSED;
+ return AS2 (cmp%W0,%0,%1);
+ }
+ return AS2 (cmp%W0,%1,%0);
+}
+}
+
+static char *
+output_16 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (CONSTANT_P (operands[0]) || GET_CODE (operands[1]) == MEM)
+ {
+ cc_status.flags |= CC_REVERSED;
+ return AS2 (cmp%B0,%0,%1);
+ }
+ return AS2 (cmp%B0,%1,%0);
+}
+}
+
+static char *
+output_18 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_float_compare (insn, operands);
+}
+
+static char *
+output_19 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_float_compare (insn, operands);
+}
+
+static char *
+output_20 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_float_compare (insn, operands);
+}
+
+static char *
+output_21 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_float_compare (insn, operands);
+}
+
+static char *
+output_22 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_float_compare (insn, operands);
+}
+
+static char *
+output_23 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_float_compare (insn, operands);
+}
+
+static char *
+output_24 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_float_compare (insn, operands);
+}
+
+static char *
+output_25 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_float_compare (insn, operands);
+}
+
+static char *
+output_26 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_float_compare (insn, operands);
+}
+
+static char *
+output_27 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_float_compare (insn, operands);
+}
+
+static char *
+output_28 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_float_compare (insn, operands);
+}
+
+static char *
+output_29 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_float_compare (insn, operands);
+}
+
+static char *
+output_30 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_float_compare (insn, operands);
+}
+
+static char *
+output_31 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_float_compare (insn, operands);
+}
+
+static char *
+output_32 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_float_compare (insn, operands);
+}
+
+static char *
+output_33 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_float_compare (insn, operands);
+}
+
+static char *
+output_43 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ /* For small integers, we may actually use testb. */
+ if (GET_CODE (operands[1]) == CONST_INT
+ && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))
+ && (! REG_P (operands[0]) || QI_REG_P (operands[0])))
+ {
+ /* We may set the sign bit spuriously. */
+
+ if ((INTVAL (operands[1]) & ~0xff) == 0)
+ {
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ return AS2 (test%B0,%1,%b0);
+ }
+
+ if ((INTVAL (operands[1]) & ~0xff00) == 0)
+ {
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ operands[1] = GEN_INT (INTVAL (operands[1]) >> 8);
+
+ if (QI_REG_P (operands[0]))
+ return AS2 (test%B0,%1,%h0);
+ else
+ {
+ operands[0] = adj_offsettable_operand (operands[0], 1);
+ return AS2 (test%B0,%1,%b0);
+ }
+ }
+
+ if (GET_CODE (operands[0]) == MEM
+ && (INTVAL (operands[1]) & ~0xff0000) == 0)
+ {
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ operands[1] = GEN_INT (INTVAL (operands[1]) >> 16);
+ operands[0] = adj_offsettable_operand (operands[0], 2);
+ return AS2 (test%B0,%1,%b0);
+ }
+
+ if (GET_CODE (operands[0]) == MEM
+ && (INTVAL (operands[1]) & ~0xff000000) == 0)
+ {
+ operands[1] = GEN_INT ((INTVAL (operands[1]) >> 24) & 0xff);
+ operands[0] = adj_offsettable_operand (operands[0], 3);
+ return AS2 (test%B0,%1,%b0);
+ }
+ }
+
+ if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM)
+ return AS2 (test%L0,%1,%0);
+
+ return AS2 (test%L1,%0,%1);
+}
+}
+
+static char *
+output_44 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (GET_CODE (operands[1]) == CONST_INT
+ && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))
+ && (! REG_P (operands[0]) || QI_REG_P (operands[0])))
+ {
+ if ((INTVAL (operands[1]) & 0xff00) == 0)
+ {
+ /* ??? This might not be necessary. */
+ if (INTVAL (operands[1]) & 0xffff0000)
+ operands[1] = GEN_INT (INTVAL (operands[1]) & 0xff);
+
+ /* We may set the sign bit spuriously. */
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ return AS2 (test%B0,%1,%b0);
+ }
+
+ if ((INTVAL (operands[1]) & 0xff) == 0)
+ {
+ operands[1] = GEN_INT ((INTVAL (operands[1]) >> 8) & 0xff);
+
+ if (QI_REG_P (operands[0]))
+ return AS2 (test%B0,%1,%h0);
+ else
+ {
+ operands[0] = adj_offsettable_operand (operands[0], 1);
+ return AS2 (test%B0,%1,%b0);
+ }
+ }
+ }
+
+ if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM)
+ return AS2 (test%W0,%1,%0);
+
+ return AS2 (test%W1,%0,%1);
+}
+}
+
+static char *
+output_45 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM)
+ return AS2 (test%B0,%1,%0);
+
+ return AS2 (test%B1,%0,%1);
+}
+}
+
+static char *
+output_49 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx link;
+ if (operands[1] == const0_rtx && REG_P (operands[0]))
+ return AS2 (xor%L0,%0,%0);
+
+ if (operands[1] == const1_rtx
+ && (link = find_reg_note (insn, REG_WAS_0, 0))
+ /* Make sure the insn that stored the 0 is still present. */
+ && ! INSN_DELETED_P (XEXP (link, 0))
+ && GET_CODE (XEXP (link, 0)) != NOTE
+ /* Make sure cross jumping didn't happen here. */
+ && no_labels_between_p (XEXP (link, 0), insn)
+ /* Make sure the reg hasn't been clobbered. */
+ && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
+ /* Fastest way to change a 0 to a 1. */
+ return AS1 (inc%L0,%0);
+
+ return AS2 (mov%L0,%1,%0);
+}
+}
+
+static char *
+output_51 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx link;
+ if (REG_P (operands[0]) && operands[1] == const0_rtx)
+ return AS2 (xor%L0,%k0,%k0);
+
+ if (REG_P (operands[0]) && operands[1] == const1_rtx
+ && (link = find_reg_note (insn, REG_WAS_0, 0))
+ /* Make sure the insn that stored the 0 is still present. */
+ && ! INSN_DELETED_P (XEXP (link, 0))
+ && GET_CODE (XEXP (link, 0)) != NOTE
+ /* Make sure cross jumping didn't happen here. */
+ && no_labels_between_p (XEXP (link, 0), insn)
+ /* Make sure the reg hasn't been clobbered. */
+ && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
+ /* Fastest way to change a 0 to a 1. */
+ return AS1 (inc%L0,%k0);
+
+ if (REG_P (operands[0]))
+ {
+ if (REG_P (operands[1]))
+ return AS2 (mov%L0,%k1,%k0);
+ else if (CONSTANT_P (operands[1]))
+ return AS2 (mov%L0,%1,%k0);
+ }
+
+ return AS2 (mov%W0,%1,%0);
+}
+}
+
+static char *
+output_52 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx link;
+ if (operands[1] == const0_rtx && REG_P (operands[0]))
+ return AS2 (xor%W0,%0,%0);
+
+ if (operands[1] == const1_rtx
+ && (link = find_reg_note (insn, REG_WAS_0, 0))
+ /* Make sure the insn that stored the 0 is still present. */
+ && ! INSN_DELETED_P (XEXP (link, 0))
+ && GET_CODE (XEXP (link, 0)) != NOTE
+ /* Make sure cross jumping didn't happen here. */
+ && no_labels_between_p (XEXP (link, 0), insn)
+ /* Make sure the reg hasn't been clobbered. */
+ && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
+ /* Fastest way to change a 0 to a 1. */
+ return AS1 (inc%W0,%0);
+
+ return AS2 (mov%W0,%1,%0);
+}
+}
+
+static char *
+output_53 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ operands[1] = gen_rtx (REG, HImode, REGNO (operands[1]));
+ return AS1 (push%W0,%1);
+}
+}
+
+static char *
+output_54 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx link;
+ if (operands[1] == const0_rtx && REG_P (operands[0]))
+ return AS2 (xor%B0,%0,%0);
+
+ if (operands[1] == const1_rtx
+ && (link = find_reg_note (insn, REG_WAS_0, 0))
+ /* Make sure the insn that stored the 0 is still present. */
+ && ! INSN_DELETED_P (XEXP (link, 0))
+ && GET_CODE (XEXP (link, 0)) != NOTE
+ /* Make sure cross jumping didn't happen here. */
+ && no_labels_between_p (XEXP (link, 0), insn)
+ /* Make sure the reg hasn't been clobbered. */
+ && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
+ /* Fastest way to change a 0 to a 1. */
+ return AS1 (inc%B0,%0);
+
+ /* If mov%B0 isn't allowed for one of these regs, use mov%L0. */
+ if (NON_QI_REG_P (operands[0]) || NON_QI_REG_P (operands[1]))
+ return (AS2 (mov%L0,%k1,%k0));
+
+ return (AS2 (mov%B0,%1,%0));
+}
+}
+
+static char *
+output_55 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx link;
+ if (operands[1] == const0_rtx && REG_P (operands[0]))
+ return AS2 (xor%B0,%0,%0);
+
+ if (operands[1] == const1_rtx
+ && (link = find_reg_note (insn, REG_WAS_0, 0))
+ /* Make sure the insn that stored the 0 is still present. */
+ && ! INSN_DELETED_P (XEXP (link, 0))
+ && GET_CODE (XEXP (link, 0)) != NOTE
+ /* Make sure cross jumping didn't happen here. */
+ && no_labels_between_p (XEXP (link, 0), insn)
+ /* Make sure the reg hasn't been clobbered. */
+ && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
+ /* Fastest way to change a 0 to a 1. */
+ return AS1 (inc%B0,%0);
+
+ /* If mov%B0 isn't allowed for one of these regs, use mov%L0. */
+ if (NON_QI_REG_P (operands[0]) || NON_QI_REG_P (operands[1]))
+ {
+ abort ();
+ return (AS2 (mov%L0,%k1,%k0));
+ }
+
+ return AS2 (mov%B0,%1,%0);
+}
+}
+
+static char *
+output_56 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (STACK_REG_P (operands[1]))
+ {
+ rtx xops[3];
+
+ if (! STACK_TOP_P (operands[1]))
+ abort ();
+
+ xops[0] = AT_SP (SFmode);
+ xops[1] = GEN_INT (4);
+ xops[2] = stack_pointer_rtx;
+
+ output_asm_insn (AS2 (sub%L2,%1,%2), xops);
+
+ if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
+ output_asm_insn (AS1 (fstp%S0,%0), xops);
+ else
+ output_asm_insn (AS1 (fst%S0,%0), xops);
+ RET;
+ }
+ return AS1 (push%L1,%1);
+}
+}
+
+static char *
+output_57 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ /* First handle a `pop' insn or a `fld %st(0)' */
+
+ if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1]))
+ {
+ if (stack_top_dies)
+ return AS1 (fstp,%y0);
+ else
+ return AS1 (fld,%y0);
+ }
+
+ /* Handle a transfer between the 387 and a 386 register */
+
+ if (STACK_TOP_P (operands[0]) && NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fld%z0,%y1));
+ RET;
+ }
+
+ if (STACK_TOP_P (operands[1]) && NON_STACK_REG_P (operands[0]))
+ {
+ output_to_reg (operands[0], stack_top_dies);
+ RET;
+ }
+
+ /* Handle other kinds of writes from the 387 */
+
+ if (STACK_TOP_P (operands[1]))
+ {
+ if (stack_top_dies)
+ return AS1 (fstp%z0,%y0);
+ else
+ return AS1 (fst%z0,%y0);
+ }
+
+ /* Handle other kinds of reads to the 387 */
+
+ if (STACK_TOP_P (operands[0]) && GET_CODE (operands[1]) == CONST_DOUBLE)
+ return (char *) output_move_const_single (operands);
+
+ if (STACK_TOP_P (operands[0]))
+ return AS1 (fld%z1,%y1);
+
+ /* Handle all SFmode moves not involving the 387 */
+
+ return (char *) singlemove_string (operands);
+}
+}
+
+static char *
+output_58 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (STACK_REG_P (operands[1]))
+ {
+ rtx xops[3];
+
+ xops[0] = AT_SP (SFmode);
+ xops[1] = GEN_INT (8);
+ xops[2] = stack_pointer_rtx;
+
+ output_asm_insn (AS2 (sub%L2,%1,%2), xops);
+
+ if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
+ output_asm_insn (AS1 (fstp%Q0,%0), xops);
+ else
+ output_asm_insn (AS1 (fst%Q0,%0), xops);
+
+ RET;
+ }
+ else
+ return (char *) output_move_double (operands);
+}
+}
+
+static char *
+output_59 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (STACK_TOP_P (operands[0]))
+ return AS1 (fxch,%1);
+ else
+ return AS1 (fxch,%0);
+}
+}
+
+static char *
+output_60 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ /* First handle a `pop' insn or a `fld %st(0)' */
+
+ if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1]))
+ {
+ if (stack_top_dies)
+ return AS1 (fstp,%y0);
+ else
+ return AS1 (fld,%y0);
+ }
+
+ /* Handle a transfer between the 387 and a 386 register */
+
+ if (STACK_TOP_P (operands[0]) && NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fld%z0,%y1));
+ RET;
+ }
+
+ if (STACK_TOP_P (operands[1]) && NON_STACK_REG_P (operands[0]))
+ {
+ output_to_reg (operands[0], stack_top_dies);
+ RET;
+ }
+
+ /* Handle other kinds of writes from the 387 */
+
+ if (STACK_TOP_P (operands[1]))
+ {
+ if (stack_top_dies)
+ return AS1 (fstp%z0,%y0);
+ else
+ return AS1 (fst%z0,%y0);
+ }
+
+ /* Handle other kinds of reads to the 387 */
+
+ if (STACK_TOP_P (operands[0]) && GET_CODE (operands[1]) == CONST_DOUBLE)
+ return (char *) output_move_const_single (operands);
+
+ if (STACK_TOP_P (operands[0]))
+ return AS1 (fld%z1,%y1);
+
+ /* Handle all DFmode moves not involving the 387 */
+
+ return (char *) output_move_double (operands);
+}
+}
+
+static char *
+output_61 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (STACK_REG_P (operands[1]))
+ {
+ rtx xops[3];
+
+ xops[0] = AT_SP (SFmode);
+ xops[1] = GEN_INT (12);
+ xops[2] = stack_pointer_rtx;
+
+ output_asm_insn (AS2 (sub%L2,%1,%2), xops);
+ output_asm_insn (AS1 (fstp%T0,%0), xops);
+ if (! find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
+ output_asm_insn (AS1 (fld%T0,%0), xops);
+
+ RET;
+ }
+ else
+ return (char *) output_move_double (operands);
+ }
+}
+
+static char *
+output_62 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (STACK_TOP_P (operands[0]))
+ return AS1 (fxch,%1);
+ else
+ return AS1 (fxch,%0);
+}
+}
+
+static char *
+output_63 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ /* First handle a `pop' insn or a `fld %st(0)' */
+
+ if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1]))
+ {
+ if (stack_top_dies)
+ return AS1 (fstp,%y0);
+ else
+ return AS1 (fld,%y0);
+ }
+
+ /* Handle a transfer between the 387 and a 386 register */
+
+ if (STACK_TOP_P (operands[0]) && NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fld%z0,%y1));
+ RET;
+ }
+
+ if (STACK_TOP_P (operands[1]) && NON_STACK_REG_P (operands[0]))
+ {
+ output_to_reg (operands[0], stack_top_dies);
+ RET;
+ }
+
+ /* Handle other kinds of writes from the 387 */
+
+ if (STACK_TOP_P (operands[1]))
+ {
+ output_asm_insn (AS1 (fstp%z0,%y0), operands);
+ if (! stack_top_dies)
+ return AS1 (fld%z0,%y0);
+
+ RET;
+ }
+
+ /* Handle other kinds of reads to the 387 */
+
+ if (STACK_TOP_P (operands[0]) && GET_CODE (operands[1]) == CONST_DOUBLE)
+ return (char *) output_move_const_single (operands);
+
+ if (STACK_TOP_P (operands[0]))
+ return AS1 (fld%z1,%y1);
+
+ /* Handle all XFmode moves not involving the 387 */
+
+ return (char *) output_move_double (operands);
+}
+}
+
+static char *
+output_64 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ return (char *) output_move_double (operands);
+}
+}
+
+static char *
+output_65 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ return (char *) output_move_double (operands);
+}
+}
+
+static char *
+output_66 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if ((TARGET_486 || REGNO (operands[0]) == 0)
+ && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1]))
+ {
+ rtx xops[2];
+ xops[0] = operands[0];
+ xops[1] = GEN_INT (0xffff);
+ output_asm_insn (AS2 (and%L0,%1,%k0), xops);
+ RET;
+ }
+
+#ifdef INTEL_SYNTAX
+ return AS2 (movzx,%1,%0);
+#else
+ return AS2 (movz%W0%L0,%1,%0);
+#endif
+}
+}
+
+static char *
+output_67 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if ((TARGET_486 || REGNO (operands[0]) == 0)
+ && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1]))
+ {
+ rtx xops[2];
+ xops[0] = operands[0];
+ xops[1] = GEN_INT (0xff);
+ output_asm_insn (AS2 (and%L0,%1,%k0), xops);
+ RET;
+ }
+
+#ifdef INTEL_SYNTAX
+ return AS2 (movzx,%1,%0);
+#else
+ return AS2 (movz%B0%W0,%1,%0);
+#endif
+}
+}
+
+static char *
+output_68 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if ((TARGET_486 || REGNO (operands[0]) == 0)
+ && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1]))
+ {
+ rtx xops[2];
+ xops[0] = operands[0];
+ xops[1] = GEN_INT (0xff);
+ output_asm_insn (AS2 (and%L0,%1,%k0), xops);
+ RET;
+ }
+
+#ifdef INTEL_SYNTAX
+ return AS2 (movzx,%1,%0);
+#else
+ return AS2 (movz%B0%L0,%1,%0);
+#endif
+}
+}
+
+static char *
+output_69 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
+ return AS2 (xor%L0,%0,%0);
+}
+}
+
+static char *
+output_70 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REGNO (operands[0]) == 0)
+ {
+ /* This used to be cwtl, but that extends HI to SI somehow. */
+#ifdef INTEL_SYNTAX
+ return "cdq";
+#else
+ return "cltd";
+#endif
+ }
+
+ operands[1] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
+ output_asm_insn (AS2 (mov%L0,%0,%1), operands);
+
+ operands[0] = GEN_INT (31);
+ return AS2 (sar%L1,%0,%1);
+}
+}
+
+static char *
+output_71 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REGNO (operands[0]) == 0
+ && REG_P (operands[1]) && REGNO (operands[1]) == 0)
+#ifdef INTEL_SYNTAX
+ return "cwde";
+#else
+ return "cwtl";
+#endif
+
+#ifdef INTEL_SYNTAX
+ return AS2 (movsx,%1,%0);
+#else
+ return AS2 (movs%W0%L0,%1,%0);
+#endif
+}
+}
+
+static char *
+output_72 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REGNO (operands[0]) == 0
+ && REG_P (operands[1]) && REGNO (operands[1]) == 0)
+ return "cbtw";
+
+#ifdef INTEL_SYNTAX
+ return AS2 (movsx,%1,%0);
+#else
+ return AS2 (movs%B0%W0,%1,%0);
+#endif
+}
+}
+
+static char *
+output_73 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+#ifdef INTEL_SYNTAX
+ return AS2 (movsx,%1,%0);
+#else
+ return AS2 (movs%B0%L0,%1,%0);
+#endif
+}
+}
+
+static char *
+output_74 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fld%z0,%y1));
+ RET;
+ }
+
+ if (NON_STACK_REG_P (operands[0]))
+ {
+ output_to_reg (operands[0], stack_top_dies);
+ RET;
+ }
+
+ if (STACK_TOP_P (operands[0]))
+ return AS1 (fld%z1,%y1);
+
+ if (GET_CODE (operands[0]) == MEM)
+ {
+ if (stack_top_dies)
+ return AS1 (fstp%z0,%y0);
+ else
+ return AS1 (fst%z0,%y0);
+ }
+
+ abort ();
+}
+}
+
+static char *
+output_75 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fld%z0,%y1));
+ RET;
+ }
+
+ if (NON_STACK_REG_P (operands[0]))
+ {
+ output_to_reg (operands[0], stack_top_dies);
+ RET;
+ }
+
+ if (STACK_TOP_P (operands[0]))
+ return AS1 (fld%z1,%y1);
+
+ if (GET_CODE (operands[0]) == MEM)
+ {
+ output_asm_insn (AS1 (fstp%z0,%y0), operands);
+ if (! stack_top_dies)
+ return AS1 (fld%z0,%y0);
+ RET;
+ }
+
+ abort ();
+}
+}
+
+static char *
+output_76 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fld%z0,%y1));
+ RET;
+ }
+
+ if (NON_STACK_REG_P (operands[0]))
+ {
+ output_to_reg (operands[0], stack_top_dies);
+ RET;
+ }
+
+ if (STACK_TOP_P (operands[0]))
+ return AS1 (fld%z1,%y1);
+
+ if (GET_CODE (operands[0]) == MEM)
+ {
+ output_asm_insn (AS1 (fstp%z0,%y0), operands);
+ if (! stack_top_dies)
+ return AS1 (fld%z0,%y0);
+ RET;
+ }
+
+ abort ();
+}
+}
+
+static char *
+output_78 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ if (GET_CODE (operands[0]) == MEM)
+ {
+ if (stack_top_dies)
+ return AS1 (fstp%z0,%0);
+ else
+ return AS1 (fst%z0,%0);
+ }
+ else if (STACK_TOP_P (operands[0]))
+ {
+ output_asm_insn (AS1 (fstp%z2,%y2), operands);
+ return AS1 (fld%z2,%y2);
+ }
+ else
+ abort ();
+}
+}
+
+static char *
+output_79 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ if (NON_STACK_REG_P (operands[0]))
+ {
+ if (stack_top_dies == 0)
+ {
+ output_asm_insn (AS1 (fld,%y1), operands);
+ stack_top_dies = 1;
+ }
+ output_to_reg (operands[0], stack_top_dies);
+ RET;
+ }
+ else if (GET_CODE (operands[0]) == MEM)
+ {
+ if (stack_top_dies)
+ return AS1 (fstp%z0,%0);
+ else
+ {
+ output_asm_insn (AS1 (fld,%y1), operands);
+ return AS1 (fstp%z0,%0);
+ }
+ }
+ else
+ abort ();
+}
+}
+
+static char *
+output_80 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ if (NON_STACK_REG_P (operands[0]))
+ {
+ if (stack_top_dies == 0)
+ {
+ output_asm_insn (AS1 (fld,%y1), operands);
+ stack_top_dies = 1;
+ }
+ output_to_reg (operands[0], stack_top_dies);
+ RET;
+ }
+ else if (GET_CODE (operands[0]) == MEM)
+ {
+ if (stack_top_dies)
+ return AS1 (fstp%z0,%0);
+ else
+ {
+ output_asm_insn (AS1 (fld,%y1), operands);
+ return AS1 (fstp%z0,%0);
+ }
+ }
+ else
+ abort ();
+}
+}
+
+static char *
+output_87 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_fix_trunc (insn, operands);
+}
+
+static char *
+output_88 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_fix_trunc (insn, operands);
+}
+
+static char *
+output_89 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_fix_trunc (insn, operands);
+}
+
+static char *
+output_93 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_fix_trunc (insn, operands);
+}
+
+static char *
+output_94 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_fix_trunc (insn, operands);
+}
+
+static char *
+output_95 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_fix_trunc (insn, operands);
+}
+
+static char *
+output_102 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fild%z0,%1));
+ RET;
+ }
+ else if (GET_CODE (operands[1]) == MEM)
+ return AS1 (fild%z1,%1);
+ else
+ abort ();
+}
+}
+
+static char *
+output_103 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fild%z0,%1));
+ RET;
+ }
+ else if (GET_CODE (operands[1]) == MEM)
+ return AS1 (fild%z1,%1);
+ else
+ abort ();
+}
+}
+
+static char *
+output_104 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fild%z0,%1));
+ RET;
+ }
+ else if (GET_CODE (operands[1]) == MEM)
+ return AS1 (fild%z1,%1);
+ else
+ abort ();
+}
+}
+
+static char *
+output_105 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fild%z0,%1));
+ RET;
+ }
+ else if (GET_CODE (operands[1]) == MEM)
+ return AS1 (fild%z1,%1);
+ else
+ abort ();
+}
+}
+
+static char *
+output_106 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fild%z0,%1));
+ RET;
+ }
+ else if (GET_CODE (operands[1]) == MEM)
+ return AS1 (fild%z1,%1);
+ else
+ abort ();
+}
+}
+
+static char *
+output_107 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fild%z0,%1));
+ RET;
+ }
+ else if (GET_CODE (operands[1]) == MEM)
+ return AS1 (fild%z1,%1);
+ else
+ abort ();
+}
+}
+
+static char *
+output_108 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx low[3], high[3];
+
+ CC_STATUS_INIT;
+
+ split_di (operands, 3, low, high);
+
+ if (GET_CODE (low[2]) != CONST_INT || INTVAL (low[2]) != 0)
+ {
+ output_asm_insn (AS2 (add%L0,%2,%0), low);
+ output_asm_insn (AS2 (adc%L0,%2,%0), high);
+ }
+ else
+ output_asm_insn (AS2 (add%L0,%2,%0), high);
+ RET;
+}
+}
+
+static char *
+output_109 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REG_P (operands[0]) && REGNO (operands[0]) != REGNO (operands[1]))
+ {
+ if (REG_P (operands[2]) && REGNO (operands[0]) == REGNO (operands[2]))
+ return AS2 (add%L0,%1,%0);
+
+ if (! TARGET_486 || ! REG_P (operands[2]))
+ {
+ CC_STATUS_INIT;
+
+ if (operands[2] == stack_pointer_rtx)
+ {
+ rtx temp;
+
+ temp = operands[1];
+ operands[1] = operands[2];
+ operands[2] = temp;
+ }
+ if (operands[2] != stack_pointer_rtx)
+ {
+ operands[1] = SET_SRC (PATTERN (insn));
+ return AS2 (lea%L0,%a1,%0);
+ }
+ }
+
+ output_asm_insn (AS2 (mov%L0,%1,%0), operands);
+ }
+
+ if (operands[2] == const1_rtx)
+ return AS1 (inc%L0,%0);
+
+ if (operands[2] == constm1_rtx)
+ return AS1 (dec%L0,%0);
+
+ return AS2 (add%L0,%2,%0);
+}
+}
+
+static char *
+output_110 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ /* ??? what about offsettable memory references? */
+ if (QI_REG_P (operands[0])
+ && GET_CODE (operands[2]) == CONST_INT
+ && (INTVAL (operands[2]) & 0xff) == 0)
+ {
+ int byteval = (INTVAL (operands[2]) >> 8) & 0xff;
+ CC_STATUS_INIT;
+
+ if (byteval == 1)
+ return AS1 (inc%B0,%h0);
+ else if (byteval == 255)
+ return AS1 (dec%B0,%h0);
+
+ operands[2] = GEN_INT (byteval);
+ return AS2 (add%B0,%2,%h0);
+ }
+
+ if (operands[2] == const1_rtx)
+ return AS1 (inc%W0,%0);
+
+ if (operands[2] == constm1_rtx
+ || (GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) == 65535))
+ return AS1 (dec%W0,%0);
+
+ return AS2 (add%W0,%2,%0);
+}
+}
+
+static char *
+output_111 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (operands[2] == const1_rtx)
+ return AS1 (inc%B0,%0);
+
+ if (operands[2] == constm1_rtx
+ || (GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) == 255))
+ return AS1 (dec%B0,%0);
+
+ return AS2 (add%B0,%2,%0);
+}
+}
+
+static char *
+output_112 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ CC_STATUS_INIT;
+ /* Adding a constant to a register is faster with an add. */
+ /* ??? can this ever happen? */
+ if (GET_CODE (operands[1]) == PLUS
+ && GET_CODE (XEXP (operands[1], 1)) == CONST_INT
+ && rtx_equal_p (operands[0], XEXP (operands[1], 0)))
+ {
+ operands[1] = XEXP (operands[1], 1);
+
+ if (operands[1] == const1_rtx)
+ return AS1 (inc%L0,%0);
+
+ if (operands[1] == constm1_rtx)
+ return AS1 (dec%L0,%0);
+
+ return AS2 (add%L0,%1,%0);
+ }
+ return AS2 (lea%L0,%a1,%0);
+}
+}
+
+static char *
+output_116 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx low[3], high[3];
+
+ CC_STATUS_INIT;
+
+ split_di (operands, 3, low, high);
+
+ if (GET_CODE (low[2]) != CONST_INT || INTVAL (low[2]) != 0)
+ {
+ output_asm_insn (AS2 (sub%L0,%2,%0), low);
+ output_asm_insn (AS2 (sbb%L0,%2,%0), high);
+ }
+ else
+ output_asm_insn (AS2 (sub%L0,%2,%0), high);
+
+ RET;
+}
+}
+
+static char *
+output_117 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return AS2 (sub%L0,%2,%0);
+}
+
+static char *
+output_118 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return AS2 (sub%W0,%2,%0);
+}
+
+static char *
+output_119 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return AS2 (sub%B0,%2,%0);
+}
+
+static char *
+output_123 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return AS2 (imul%W0,%2,%0);
+}
+
+static char *
+output_124 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (GET_CODE (operands[1]) == REG
+ && REGNO (operands[1]) == REGNO (operands[0])
+ && (GET_CODE (operands[2]) == MEM || GET_CODE (operands[2]) == REG))
+ /* Assembler has weird restrictions. */
+ return AS2 (imul%W0,%2,%0);
+ return AS3 (imul%W0,%2,%1,%0);
+}
+}
+
+static char *
+output_125 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return AS2 (imul%L0,%2,%0);
+}
+
+static char *
+output_126 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (GET_CODE (operands[1]) == REG
+ && REGNO (operands[1]) == REGNO (operands[0])
+ && (GET_CODE (operands[2]) == MEM || GET_CODE (operands[2]) == REG))
+ /* Assembler has weird restrictions. */
+ return AS2 (imul%L0,%2,%0);
+ return AS3 (imul%L0,%2,%1,%0);
+}
+}
+
+static char *
+output_139 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+#ifdef INTEL_SYNTAX
+ output_asm_insn ("cdq", operands);
+#else
+ output_asm_insn ("cltd", operands);
+#endif
+ return AS1 (idiv%L0,%2);
+}
+}
+
+static char *
+output_141 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ output_asm_insn (AS2 (xor%L3,%3,%3), operands);
+ return AS1 (div%L0,%2);
+}
+}
+
+static char *
+output_142 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ output_asm_insn (AS2 (xor%W0,%3,%3), operands);
+ return AS1 (div%W0,%2);
+}
+}
+
+static char *
+output_143 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (GET_CODE (operands[2]) == CONST_INT
+ && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
+ {
+ if (INTVAL (operands[2]) == 0xffff && REG_P (operands[0])
+ && (! REG_P (operands[1])
+ || REGNO (operands[0]) != 0 || REGNO (operands[1]) != 0)
+ && (! TARGET_486 || ! rtx_equal_p (operands[0], operands[1])))
+ {
+ /* ??? tege: Should forget CC_STATUS only if we clobber a
+ remembered operand. Fix that later. */
+ CC_STATUS_INIT;
+#ifdef INTEL_SYNTAX
+ return AS2 (movzx,%w1,%0);
+#else
+ return AS2 (movz%W0%L0,%w1,%0);
+#endif
+ }
+
+ if (INTVAL (operands[2]) == 0xff && REG_P (operands[0])
+ && !(REG_P (operands[1]) && NON_QI_REG_P (operands[1]))
+ && (! REG_P (operands[1])
+ || REGNO (operands[0]) != 0 || REGNO (operands[1]) != 0)
+ && (! TARGET_486 || ! rtx_equal_p (operands[0], operands[1])))
+ {
+ /* ??? tege: Should forget CC_STATUS only if we clobber a
+ remembered operand. Fix that later. */
+ CC_STATUS_INIT;
+#ifdef INTEL_SYNTAX
+ return AS2 (movzx,%b1,%0);
+#else
+ return AS2 (movz%B0%L0,%b1,%0);
+#endif
+ }
+
+ if (QI_REG_P (operands[0]) && ~(INTVAL (operands[2]) | 0xff) == 0)
+ {
+ CC_STATUS_INIT;
+
+ if (INTVAL (operands[2]) == 0xffffff00)
+ {
+ operands[2] = const0_rtx;
+ return AS2 (mov%B0,%2,%b0);
+ }
+
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0xff);
+ return AS2 (and%B0,%2,%b0);
+ }
+
+ if (QI_REG_P (operands[0]) && ~(INTVAL (operands[2]) | 0xff00) == 0)
+ {
+ CC_STATUS_INIT;
+
+ if (INTVAL (operands[2]) == 0xffff00ff)
+ {
+ operands[2] = const0_rtx;
+ return AS2 (mov%B0,%2,%h0);
+ }
+
+ operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff);
+ return AS2 (and%B0,%2,%h0);
+ }
+
+ if (GET_CODE (operands[0]) == MEM && INTVAL (operands[2]) == 0xffff0000)
+ {
+ operands[2] = const0_rtx;
+ return AS2 (mov%W0,%2,%w0);
+ }
+ }
+
+ return AS2 (and%L0,%2,%0);
+}
+}
+
+static char *
+output_144 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (GET_CODE (operands[2]) == CONST_INT
+ && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
+ {
+ /* Can we ignore the upper byte? */
+ if ((! REG_P (operands[0]) || QI_REG_P (operands[0]))
+ && (INTVAL (operands[2]) & 0xff00) == 0xff00)
+ {
+ CC_STATUS_INIT;
+
+ if ((INTVAL (operands[2]) & 0xff) == 0)
+ {
+ operands[2] = const0_rtx;
+ return AS2 (mov%B0,%2,%b0);
+ }
+
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0xff);
+ return AS2 (and%B0,%2,%b0);
+ }
+
+ /* Can we ignore the lower byte? */
+ /* ??? what about offsettable memory references? */
+ if (QI_REG_P (operands[0]) && (INTVAL (operands[2]) & 0xff) == 0xff)
+ {
+ CC_STATUS_INIT;
+
+ if ((INTVAL (operands[2]) & 0xff00) == 0)
+ {
+ operands[2] = const0_rtx;
+ return AS2 (mov%B0,%2,%h0);
+ }
+
+ operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff);
+ return AS2 (and%B0,%2,%h0);
+ }
+ }
+
+ return AS2 (and%W0,%2,%0);
+}
+}
+
+static char *
+output_145 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return AS2 (and%B0,%2,%0);
+}
+
+static char *
+output_146 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (GET_CODE (operands[2]) == CONST_INT
+ && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
+ {
+ if ((! REG_P (operands[0]) || QI_REG_P (operands[0]))
+ && (INTVAL (operands[2]) & ~0xff) == 0)
+ {
+ CC_STATUS_INIT;
+
+ if (INTVAL (operands[2]) == 0xff)
+ return AS2 (mov%B0,%2,%b0);
+
+ return AS2 (or%B0,%2,%b0);
+ }
+
+ if (QI_REG_P (operands[0]) && (INTVAL (operands[2]) & ~0xff00) == 0)
+ {
+ CC_STATUS_INIT;
+ operands[2] = GEN_INT (INTVAL (operands[2]) >> 8);
+
+ if (INTVAL (operands[2]) == 0xff)
+ return AS2 (mov%B0,%2,%h0);
+
+ return AS2 (or%B0,%2,%h0);
+ }
+ }
+
+ return AS2 (or%L0,%2,%0);
+}
+}
+
+static char *
+output_147 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (GET_CODE (operands[2]) == CONST_INT
+ && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
+ {
+ /* Can we ignore the upper byte? */
+ if ((! REG_P (operands[0]) || QI_REG_P (operands[0]))
+ && (INTVAL (operands[2]) & 0xff00) == 0)
+ {
+ CC_STATUS_INIT;
+ if (INTVAL (operands[2]) & 0xffff0000)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff);
+
+ if (INTVAL (operands[2]) == 0xff)
+ return AS2 (mov%B0,%2,%b0);
+
+ return AS2 (or%B0,%2,%b0);
+ }
+
+ /* Can we ignore the lower byte? */
+ /* ??? what about offsettable memory references? */
+ if (QI_REG_P (operands[0])
+ && (INTVAL (operands[2]) & 0xff) == 0)
+ {
+ CC_STATUS_INIT;
+ operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff);
+
+ if (INTVAL (operands[2]) == 0xff)
+ return AS2 (mov%B0,%2,%h0);
+
+ return AS2 (or%B0,%2,%h0);
+ }
+ }
+
+ return AS2 (or%W0,%2,%0);
+}
+}
+
+static char *
+output_148 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return AS2 (or%B0,%2,%0);
+}
+
+static char *
+output_149 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (GET_CODE (operands[2]) == CONST_INT
+ && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
+ {
+ if ((! REG_P (operands[0]) || QI_REG_P (operands[0]))
+ && (INTVAL (operands[2]) & ~0xff) == 0)
+ {
+ CC_STATUS_INIT;
+
+ if (INTVAL (operands[2]) == 0xff)
+ return AS1 (not%B0,%b0);
+
+ return AS2 (xor%B0,%2,%b0);
+ }
+
+ if (QI_REG_P (operands[0]) && (INTVAL (operands[2]) & ~0xff00) == 0)
+ {
+ CC_STATUS_INIT;
+ operands[2] = GEN_INT (INTVAL (operands[2]) >> 8);
+
+ if (INTVAL (operands[2]) == 0xff)
+ return AS1 (not%B0,%h0);
+
+ return AS2 (xor%B0,%2,%h0);
+ }
+ }
+
+ return AS2 (xor%L0,%2,%0);
+}
+}
+
+static char *
+output_150 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (GET_CODE (operands[2]) == CONST_INT
+ && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
+ {
+ /* Can we ignore the upper byte? */
+ if ((! REG_P (operands[0]) || QI_REG_P (operands[0]))
+ && (INTVAL (operands[2]) & 0xff00) == 0)
+ {
+ CC_STATUS_INIT;
+ if (INTVAL (operands[2]) & 0xffff0000)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff);
+
+ if (INTVAL (operands[2]) == 0xff)
+ return AS1 (not%B0,%b0);
+
+ return AS2 (xor%B0,%2,%b0);
+ }
+
+ /* Can we ignore the lower byte? */
+ /* ??? what about offsettable memory references? */
+ if (QI_REG_P (operands[0])
+ && (INTVAL (operands[2]) & 0xff) == 0)
+ {
+ CC_STATUS_INIT;
+ operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff);
+
+ if (INTVAL (operands[2]) == 0xff)
+ return AS1 (not%B0,%h0);
+
+ return AS2 (xor%B0,%2,%h0);
+ }
+ }
+
+ return AS2 (xor%W0,%2,%0);
+}
+}
+
+static char *
+output_151 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return AS2 (xor%B0,%2,%0);
+}
+
+static char *
+output_152 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx xops[2], low[1], high[1];
+
+ CC_STATUS_INIT;
+
+ split_di (operands, 1, low, high);
+ xops[0] = const0_rtx;
+ xops[1] = high[0];
+
+ output_asm_insn (AS1 (neg%L0,%0), low);
+ output_asm_insn (AS2 (adc%L1,%0,%1), xops);
+ output_asm_insn (AS1 (neg%L0,%0), high);
+ RET;
+}
+}
+
+static char *
+output_182 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx xops[4], low[1], high[1];
+
+ CC_STATUS_INIT;
+
+ split_di (operands, 1, low, high);
+ xops[0] = operands[2];
+ xops[1] = const1_rtx;
+ xops[2] = low[0];
+ xops[3] = high[0];
+
+ if (INTVAL (xops[0]) > 31)
+ {
+ output_asm_insn (AS2 (mov%L3,%2,%3), xops); /* Fast shift by 32 */
+ output_asm_insn (AS2 (xor%L2,%2,%2), xops);
+
+ if (INTVAL (xops[0]) > 32)
+ {
+ xops[0] = GEN_INT (INTVAL (xops[0]) - 32);
+ output_asm_insn (AS2 (sal%L3,%0,%3), xops); /* Remaining shift */
+ }
+ }
+ else
+ {
+ output_asm_insn (AS3 (shld%L3,%0,%2,%3), xops);
+ output_asm_insn (AS2 (sal%L2,%0,%2), xops);
+ }
+ RET;
+}
+}
+
+static char *
+output_183 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx xops[4], low[1], high[1];
+
+ CC_STATUS_INIT;
+
+ split_di (operands, 1, low, high);
+ xops[0] = operands[2];
+ xops[1] = const1_rtx;
+ xops[2] = low[0];
+ xops[3] = high[0];
+
+ output_asm_insn (AS2 (ror%B0,%1,%0), xops); /* shift count / 2 */
+
+ output_asm_insn (AS3_SHIFT_DOUBLE (shld%L3,%0,%2,%3), xops);
+ output_asm_insn (AS2 (sal%L2,%0,%2), xops);
+ output_asm_insn (AS3_SHIFT_DOUBLE (shld%L3,%0,%2,%3), xops);
+ output_asm_insn (AS2 (sal%L2,%0,%2), xops);
+
+ xops[1] = GEN_INT (7); /* shift count & 1 */
+
+ output_asm_insn (AS2 (shr%B0,%1,%0), xops);
+
+ output_asm_insn (AS3_SHIFT_DOUBLE (shld%L3,%0,%2,%3), xops);
+ output_asm_insn (AS2 (sal%L2,%0,%2), xops);
+
+ RET;
+}
+}
+
+static char *
+output_184 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REG_P (operands[0]) && REGNO (operands[0]) != REGNO (operands[1]))
+ {
+ if (TARGET_486 && INTVAL (operands[2]) == 1)
+ {
+ output_asm_insn (AS2 (mov%L0,%1,%0), operands);
+ return AS2 (add%L0,%1,%0);
+ }
+ else
+ {
+ CC_STATUS_INIT;
+
+ if (operands[1] == stack_pointer_rtx)
+ {
+ output_asm_insn (AS2 (mov%L0,%1,%0), operands);
+ operands[1] = operands[0];
+ }
+ operands[1] = gen_rtx (MULT, SImode, operands[1],
+ GEN_INT (1 << INTVAL (operands[2])));
+ return AS2 (lea%L0,%a1,%0);
+ }
+ }
+
+ if (REG_P (operands[2]))
+ return AS2 (sal%L0,%b2,%0);
+
+ if (REG_P (operands[0]) && operands[2] == const1_rtx)
+ return AS2 (add%L0,%0,%0);
+
+ return AS2 (sal%L0,%2,%0);
+}
+}
+
+static char *
+output_185 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REG_P (operands[2]))
+ return AS2 (sal%W0,%b2,%0);
+
+ if (REG_P (operands[0]) && operands[2] == const1_rtx)
+ return AS2 (add%W0,%0,%0);
+
+ return AS2 (sal%W0,%2,%0);
+}
+}
+
+static char *
+output_186 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REG_P (operands[2]))
+ return AS2 (sal%B0,%b2,%0);
+
+ if (REG_P (operands[0]) && operands[2] == const1_rtx)
+ return AS2 (add%B0,%0,%0);
+
+ return AS2 (sal%B0,%2,%0);
+}
+}
+
+static char *
+output_188 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx xops[4], low[1], high[1];
+
+ CC_STATUS_INIT;
+
+ split_di (operands, 1, low, high);
+ xops[0] = operands[2];
+ xops[1] = const1_rtx;
+ xops[2] = low[0];
+ xops[3] = high[0];
+
+ if (INTVAL (xops[0]) > 31)
+ {
+ xops[1] = GEN_INT (31);
+ output_asm_insn (AS2 (mov%L2,%3,%2), xops);
+ output_asm_insn (AS2 (sar%L3,%1,%3), xops); /* shift by 32 */
+
+ if (INTVAL (xops[0]) > 32)
+ {
+ xops[0] = GEN_INT (INTVAL (xops[0]) - 32);
+ output_asm_insn (AS2 (sar%L2,%0,%2), xops); /* Remaining shift */
+ }
+ }
+ else
+ {
+ output_asm_insn (AS3 (shrd%L2,%0,%3,%2), xops);
+ output_asm_insn (AS2 (sar%L3,%0,%3), xops);
+ }
+
+ RET;
+}
+}
+
+static char *
+output_189 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx xops[4], low[1], high[1];
+
+ CC_STATUS_INIT;
+
+ split_di (operands, 1, low, high);
+ xops[0] = operands[2];
+ xops[1] = const1_rtx;
+ xops[2] = low[0];
+ xops[3] = high[0];
+
+ output_asm_insn (AS2 (ror%B0,%1,%0), xops); /* shift count / 2 */
+
+ output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
+ output_asm_insn (AS2 (sar%L3,%0,%3), xops);
+ output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
+ output_asm_insn (AS2 (sar%L3,%0,%3), xops);
+
+ xops[1] = GEN_INT (7); /* shift count & 1 */
+
+ output_asm_insn (AS2 (shr%B0,%1,%0), xops);
+
+ output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
+ output_asm_insn (AS2 (sar%L3,%0,%3), xops);
+
+ RET;
+}
+}
+
+static char *
+output_190 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REG_P (operands[2]))
+ return AS2 (sar%L0,%b2,%0);
+ else
+ return AS2 (sar%L0,%2,%0);
+}
+}
+
+static char *
+output_191 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REG_P (operands[2]))
+ return AS2 (sar%W0,%b2,%0);
+ else
+ return AS2 (sar%W0,%2,%0);
+}
+}
+
+static char *
+output_192 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REG_P (operands[2]))
+ return AS2 (sar%B0,%b2,%0);
+ else
+ return AS2 (sar%B0,%2,%0);
+}
+}
+
+static char *
+output_194 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx xops[4], low[1], high[1];
+
+ CC_STATUS_INIT;
+
+ split_di (operands, 1, low, high);
+ xops[0] = operands[2];
+ xops[1] = const1_rtx;
+ xops[2] = low[0];
+ xops[3] = high[0];
+
+ if (INTVAL (xops[0]) > 31)
+ {
+ output_asm_insn (AS2 (mov%L2,%3,%2), xops); /* Fast shift by 32 */
+ output_asm_insn (AS2 (xor%L3,%3,%3), xops);
+
+ if (INTVAL (xops[0]) > 32)
+ {
+ xops[0] = GEN_INT (INTVAL (xops[0]) - 32);
+ output_asm_insn (AS2 (shr%L2,%0,%2), xops); /* Remaining shift */
+ }
+ }
+ else
+ {
+ output_asm_insn (AS3 (shrd%L2,%0,%3,%2), xops);
+ output_asm_insn (AS2 (shr%L3,%0,%3), xops);
+ }
+
+ RET;
+}
+}
+
+static char *
+output_195 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx xops[4], low[1], high[1];
+
+ CC_STATUS_INIT;
+
+ split_di (operands, 1, low, high);
+ xops[0] = operands[2];
+ xops[1] = const1_rtx;
+ xops[2] = low[0];
+ xops[3] = high[0];
+
+ output_asm_insn (AS2 (ror%B0,%1,%0), xops); /* shift count / 2 */
+
+ output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
+ output_asm_insn (AS2 (shr%L3,%0,%3), xops);
+ output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
+ output_asm_insn (AS2 (shr%L3,%0,%3), xops);
+
+ xops[1] = GEN_INT (7); /* shift count & 1 */
+
+ output_asm_insn (AS2 (shr%B0,%1,%0), xops);
+
+ output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
+ output_asm_insn (AS2 (shr%L3,%0,%3), xops);
+
+ RET;
+}
+}
+
+static char *
+output_196 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REG_P (operands[2]))
+ return AS2 (shr%L0,%b2,%0);
+ else
+ return AS2 (shr%L0,%2,%1);
+}
+}
+
+static char *
+output_197 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REG_P (operands[2]))
+ return AS2 (shr%W0,%b2,%0);
+ else
+ return AS2 (shr%W0,%2,%0);
+}
+}
+
+static char *
+output_198 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REG_P (operands[2]))
+ return AS2 (shr%B0,%b2,%0);
+ else
+ return AS2 (shr%B0,%2,%0);
+}
+}
+
+static char *
+output_199 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REG_P (operands[2]))
+ return AS2 (rol%L0,%b2,%0);
+ else
+ return AS2 (rol%L0,%2,%0);
+}
+}
+
+static char *
+output_200 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REG_P (operands[2]))
+ return AS2 (rol%W0,%b2,%0);
+ else
+ return AS2 (rol%W0,%2,%0);
+}
+}
+
+static char *
+output_201 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REG_P (operands[2]))
+ return AS2 (rol%B0,%b2,%0);
+ else
+ return AS2 (rol%B0,%2,%0);
+}
+}
+
+static char *
+output_202 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REG_P (operands[2]))
+ return AS2 (ror%L0,%b2,%0);
+ else
+ return AS2 (ror%L0,%2,%0);
+}
+}
+
+static char *
+output_203 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REG_P (operands[2]))
+ return AS2 (ror%W0,%b2,%0);
+ else
+ return AS2 (ror%W0,%2,%0);
+}
+}
+
+static char *
+output_204 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (REG_P (operands[2]))
+ return AS2 (ror%B0,%b2,%0);
+ else
+ return AS2 (ror%B0,%2,%0);
+}
+}
+
+static char *
+output_205 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ CC_STATUS_INIT;
+
+ if (INTVAL (operands[3]) == 1)
+ return AS2 (bts%L0,%2,%0);
+ else
+ return AS2 (btr%L0,%2,%0);
+}
+}
+
+static char *
+output_206 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ CC_STATUS_INIT;
+
+ return AS2 (btc%L0,%1,%0);
+}
+}
+
+static char *
+output_207 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ CC_STATUS_INIT;
+
+ return AS2 (btc%L0,%2,%0);
+}
+}
+
+static char *
+output_208 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ cc_status.flags |= CC_Z_IN_NOT_C;
+ return AS2 (bt%L0,%1,%0);
+}
+}
+
+static char *
+output_209 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ unsigned int mask;
+
+ mask = ((1 << INTVAL (operands[1])) - 1) << INTVAL (operands[2]);
+ operands[1] = GEN_INT (mask);
+
+ if (QI_REG_P (operands[0]))
+ {
+ if ((mask & ~0xff) == 0)
+ {
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ return AS2 (test%B0,%1,%b0);
+ }
+
+ if ((mask & ~0xff00) == 0)
+ {
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ operands[1] = GEN_INT (mask >> 8);
+ return AS2 (test%B0,%1,%h0);
+ }
+ }
+
+ return AS2 (test%L0,%1,%0);
+}
+}
+
+static char *
+output_210 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ unsigned int mask;
+
+ mask = ((1 << INTVAL (operands[1])) - 1) << INTVAL (operands[2]);
+ operands[1] = GEN_INT (mask);
+
+ if (! REG_P (operands[0]) || QI_REG_P (operands[0]))
+ {
+ if ((mask & ~0xff) == 0)
+ {
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ return AS2 (test%B0,%1,%b0);
+ }
+
+ if ((mask & ~0xff00) == 0)
+ {
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ operands[1] = GEN_INT (mask >> 8);
+
+ if (QI_REG_P (operands[0]))
+ return AS2 (test%B0,%1,%h0);
+ else
+ {
+ operands[0] = adj_offsettable_operand (operands[0], 1);
+ return AS2 (test%B0,%1,%b0);
+ }
+ }
+
+ if (GET_CODE (operands[0]) == MEM && (mask & ~0xff0000) == 0)
+ {
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ operands[1] = GEN_INT (mask >> 16);
+ operands[0] = adj_offsettable_operand (operands[0], 2);
+ return AS2 (test%B0,%1,%b0);
+ }
+
+ if (GET_CODE (operands[0]) == MEM && (mask & ~0xff000000) == 0)
+ {
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ operands[1] = GEN_INT (mask >> 24);
+ operands[0] = adj_offsettable_operand (operands[0], 3);
+ return AS2 (test%B0,%1,%b0);
+ }
+ }
+
+ if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM)
+ return AS2 (test%L0,%1,%0);
+
+ return AS2 (test%L1,%0,%1);
+}
+}
+
+static char *
+output_212 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (cc_prev_status.flags & CC_Z_IN_NOT_C)
+ return AS1 (setnb,%0);
+ else
+ return AS1 (sete,%0);
+}
+}
+
+static char *
+output_214 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (cc_prev_status.flags & CC_Z_IN_NOT_C)
+ return AS1 (setb,%0);
+ else
+ return AS1 (setne,%0);
+}
+
+}
+
+static char *
+output_216 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (sete,%0);
+
+ OUTPUT_JUMP ("setg %0", "seta %0", NULL_PTR);
+}
+}
+
+static char *
+output_218 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return "seta %0";
+}
+
+static char *
+output_220 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (sete,%0);
+
+ OUTPUT_JUMP ("setl %0", "setb %0", "sets %0");
+}
+}
+
+static char *
+output_222 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return "setb %0";
+}
+
+static char *
+output_224 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (sete,%0);
+
+ OUTPUT_JUMP ("setge %0", "setae %0", "setns %0");
+}
+}
+
+static char *
+output_226 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return "setae %0";
+}
+
+static char *
+output_228 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (setb,%0);
+
+ OUTPUT_JUMP ("setle %0", "setbe %0", NULL_PTR);
+}
+}
+
+static char *
+output_230 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return "setbe %0";
+}
+
+static char *
+output_232 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (cc_prev_status.flags & CC_Z_IN_NOT_C)
+ return "jnc %l0";
+ else
+ return "je %l0";
+}
+}
+
+static char *
+output_234 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (cc_prev_status.flags & CC_Z_IN_NOT_C)
+ return "jc %l0";
+ else
+ return "jne %l0";
+}
+}
+
+static char *
+output_236 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (je,%l0);
+
+ OUTPUT_JUMP ("jg %l0", "ja %l0", NULL_PTR);
+}
+}
+
+static char *
+output_240 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (je,%l0);
+
+ OUTPUT_JUMP ("jl %l0", "jb %l0", "js %l0");
+}
+}
+
+static char *
+output_244 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (je,%l0);
+
+ OUTPUT_JUMP ("jge %l0", "jae %l0", "jns %l0");
+}
+}
+
+static char *
+output_248 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (jb,%l0);
+
+ OUTPUT_JUMP ("jle %l0", "jbe %l0", NULL_PTR);
+}
+}
+
+static char *
+output_251 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (cc_prev_status.flags & CC_Z_IN_NOT_C)
+ return "jc %l0";
+ else
+ return "jne %l0";
+}
+}
+
+static char *
+output_252 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (cc_prev_status.flags & CC_Z_IN_NOT_C)
+ return "jnc %l0";
+ else
+ return "je %l0";
+}
+}
+
+static char *
+output_253 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (jne,%l0);
+
+ OUTPUT_JUMP ("jle %l0", "jbe %l0", NULL_PTR);
+}
+}
+
+static char *
+output_255 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (jne,%l0);
+
+ OUTPUT_JUMP ("jge %l0", "jae %l0", "jns %l0");
+}
+}
+
+static char *
+output_257 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (jne,%l0);
+
+ OUTPUT_JUMP ("jl %l0", "jb %l0", "js %l0");
+}
+}
+
+static char *
+output_259 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (jae,%l0);
+
+ OUTPUT_JUMP ("jg %l0", "ja %l0", NULL_PTR);
+}
+}
+
+static char *
+output_262 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ CC_STATUS_INIT;
+
+ return AS1 (jmp,%*%0);
+}
+}
+
+static char *
+output_264 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx xops[4];
+
+ xops[0] = operands[0];
+ xops[1] = operands[1];
+ xops[2] = operands[2];
+ xops[3] = pic_offset_table_rtx;
+
+ output_asm_insn (AS2 (mov%L2,%3,%2), xops);
+ output_asm_insn ("sub%L2 %l1@GOTOFF(%3,%0,4),%2", xops);
+ output_asm_insn (AS1 (jmp,%*%2), xops);
+ ASM_OUTPUT_ALIGN_CODE (asm_out_file);
+ RET;
+}
+}
+
+static char *
+output_265 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ CC_STATUS_INIT;
+
+ return AS1 (jmp,%*%0);
+}
+}
+
+static char *
+output_267 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (GET_CODE (operands[0]) == MEM
+ && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
+ {
+ operands[0] = XEXP (operands[0], 0);
+ return AS1 (call,%*%0);
+ }
+ else
+ return AS1 (call,%P0);
+}
+}
+
+static char *
+output_270 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (GET_CODE (operands[0]) == MEM
+ && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
+ {
+ operands[0] = XEXP (operands[0], 0);
+ return AS1 (call,%*%0);
+ }
+ else
+ return AS1 (call,%P0);
+}
+}
+
+static char *
+output_273 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (GET_CODE (operands[1]) == MEM
+ && ! CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
+ {
+ operands[1] = XEXP (operands[1], 0);
+ output_asm_insn (AS1 (call,%*%1), operands);
+ }
+ else
+ output_asm_insn (AS1 (call,%P1), operands);
+
+ RET;
+}
+}
+
+static char *
+output_276 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ if (GET_CODE (operands[1]) == MEM
+ && ! CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
+ {
+ operands[1] = XEXP (operands[1], 0);
+ output_asm_insn (AS1 (call,%*%1), operands);
+ }
+ else
+ output_asm_insn (AS1 (call,%P1), operands);
+
+ RET;
+}
+}
+
+static char *
+output_279 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx addr = operands[1];
+
+ if (GET_CODE (operands[0]) == MEM
+ && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
+ {
+ operands[0] = XEXP (operands[0], 0);
+ output_asm_insn (AS1 (call,%*%0), operands);
+ }
+ else
+ output_asm_insn (AS1 (call,%P0), operands);
+
+ operands[2] = gen_rtx (REG, SImode, 0);
+ output_asm_insn (AS2 (mov%L2,%2,%1), operands);
+
+ operands[2] = gen_rtx (REG, SImode, 1);
+ operands[1] = adj_offsettable_operand (addr, 4);
+ output_asm_insn (AS2 (mov%L2,%2,%1), operands);
+
+ operands[1] = adj_offsettable_operand (addr, 8);
+ return AS1 (fnsave,%1);
+}
+}
+
+static char *
+output_280 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx addr = operands[1];
+
+ output_asm_insn (AS1 (call,%P0), operands);
+
+ operands[2] = gen_rtx (REG, SImode, 0);
+ output_asm_insn (AS2 (mov%L2,%2,%1), operands);
+
+ operands[2] = gen_rtx (REG, SImode, 1);
+ operands[1] = adj_offsettable_operand (addr, 4);
+ output_asm_insn (AS2 (mov%L2,%2,%1), operands);
+
+ operands[1] = adj_offsettable_operand (addr, 8);
+ return AS1 (fnsave,%1);
+}
+}
+
+static char *
+output_283 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ function_epilogue (asm_out_file, get_frame_size ());
+ RET;
+}
+}
+
+static char *
+output_286 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx xops[2];
+
+ output_asm_insn ("cld", operands);
+ if (GET_CODE (operands[2]) == CONST_INT)
+ {
+ if (INTVAL (operands[2]) & ~0x03)
+ {
+ xops[0] = GEN_INT ((INTVAL (operands[2]) >> 2) & 0x3fffffff);
+ xops[1] = operands[4];
+
+ output_asm_insn (AS2 (mov%L1,%0,%1), xops);
+#ifdef INTEL_SYNTAX
+ output_asm_insn ("rep movsd", xops);
+#else
+ output_asm_insn ("rep\n\tmovsl", xops);
+#endif
+ }
+ if (INTVAL (operands[2]) & 0x02)
+ output_asm_insn ("movsw", operands);
+ if (INTVAL (operands[2]) & 0x01)
+ output_asm_insn ("movsb", operands);
+ }
+ else
+ abort ();
+ RET;
+}
+}
+
+static char *
+output_288 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx xops[4], label;
+
+ label = gen_label_rtx ();
+
+ output_asm_insn ("cld", operands);
+ output_asm_insn (AS2 (xor%L0,%0,%0), operands);
+ output_asm_insn ("repz\n\tcmps%B2", operands);
+ output_asm_insn ("je %l0", &label);
+
+ xops[0] = operands[0];
+ xops[1] = gen_rtx (MEM, QImode,
+ gen_rtx (PLUS, SImode, operands[1], constm1_rtx));
+ xops[2] = gen_rtx (MEM, QImode,
+ gen_rtx (PLUS, SImode, operands[2], constm1_rtx));
+ xops[3] = operands[3];
+
+ output_asm_insn (AS2 (movz%B1%L0,%1,%0), xops);
+ output_asm_insn (AS2 (movz%B2%L3,%2,%3), xops);
+
+ output_asm_insn (AS2 (sub%L0,%3,%0), xops);
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (label));
+ RET;
+}
+}
+
+static char *
+output_289 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx xops[2];
+
+ cc_status.flags |= CC_NOT_SIGNED;
+
+ xops[0] = gen_rtx (REG, QImode, 0);
+ xops[1] = CONST0_RTX (QImode);
+
+ output_asm_insn ("cld", operands);
+ output_asm_insn (AS2 (test%B0,%1,%0), xops);
+ return "repz\n\tcmps%B2";
+}
+}
+
+static char *
+output_291 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx xops[3];
+ static int ffssi_label_number;
+ char buffer[30];
+
+ xops[0] = operands[0];
+ xops[1] = operands[1];
+ xops[2] = constm1_rtx;
+ /* Can there be a way to avoid the jump here? */
+ output_asm_insn (AS2 (bsf%L0,%1,%0), xops);
+#ifdef LOCAL_LABEL_PREFIX
+ sprintf (buffer, "jnz %sLFFSSI%d",
+ LOCAL_LABEL_PREFIX, ffssi_label_number);
+#else
+ sprintf (buffer, "jnz %sLFFSSI%d",
+ "", ffssi_label_number);
+#endif
+ output_asm_insn (buffer, xops);
+ output_asm_insn (AS2 (mov%L0,%2,%0), xops);
+#ifdef LOCAL_LABEL_PREFIX
+ sprintf (buffer, "%sLFFSSI%d:",
+ LOCAL_LABEL_PREFIX, ffssi_label_number);
+#else
+ sprintf (buffer, "%sLFFSSI%d:",
+ "", ffssi_label_number);
+#endif
+ output_asm_insn (buffer, xops);
+
+ ffssi_label_number++;
+ return "";
+}
+}
+
+static char *
+output_293 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx xops[3];
+ static int ffshi_label_number;
+ char buffer[30];
+
+ xops[0] = operands[0];
+ xops[1] = operands[1];
+ xops[2] = constm1_rtx;
+ output_asm_insn (AS2 (bsf%W0,%1,%0), xops);
+#ifdef LOCAL_LABEL_PREFIX
+ sprintf (buffer, "jnz %sLFFSHI%d",
+ LOCAL_LABEL_PREFIX, ffshi_label_number);
+#else
+ sprintf (buffer, "jnz %sLFFSHI%d",
+ "", ffshi_label_number);
+#endif
+ output_asm_insn (buffer, xops);
+ output_asm_insn (AS2 (mov%W0,%2,%0), xops);
+#ifdef LOCAL_LABEL_PREFIX
+ sprintf (buffer, "%sLFFSHI%d:",
+ LOCAL_LABEL_PREFIX, ffshi_label_number);
+#else
+ sprintf (buffer, "%sLFFSHI%d:",
+ "", ffshi_label_number);
+#endif
+ output_asm_insn (buffer, xops);
+
+ ffshi_label_number++;
+ return "";
+}
+}
+
+static char *
+output_294 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_387_binary_op (insn, operands);
+}
+
+static char *
+output_295 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_387_binary_op (insn, operands);
+}
+
+static char *
+output_296 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_387_binary_op (insn, operands);
+}
+
+static char *
+output_297 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_387_binary_op (insn, operands);
+}
+
+static char *
+output_298 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_387_binary_op (insn, operands);
+}
+
+static char *
+output_299 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_387_binary_op (insn, operands);
+}
+
+static char *
+output_300 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_387_binary_op (insn, operands);
+}
+
+static char *
+output_301 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_387_binary_op (insn, operands);
+}
+
+static char *
+output_302 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_387_binary_op (insn, operands);
+}
+
+static char *
+output_303 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_387_binary_op (insn, operands);
+}
+
+static char *
+output_304 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_387_binary_op (insn, operands);
+}
+
+static char *
+output_305 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_387_binary_op (insn, operands);
+}
+
+static char *
+output_306 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+ return (char *) output_387_binary_op (insn, operands);
+}
+
+static char *
+output_308 (operands, insn)
+ rtx *operands;
+ rtx insn;
+{
+
+{
+ rtx xops[2];
+
+ xops[0] = operands[0];
+ xops[1] = constm1_rtx;
+ output_asm_insn ("cld", operands);
+ output_asm_insn (AS2 (mov%L0,%1,%0), xops);
+ return "repnz\n\tscas%B2";
+}
+}
+
+char * const insn_template[] =
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ "push%L0 %1",
+ "push%L0 %1",
+ 0,
+ 0,
+ "push%W0 %1",
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ "mul%B0 %2",
+ "imul%B0 %2",
+ "mul%L0 %2",
+ "imul%L0 %2",
+ 0,
+ 0,
+ 0,
+ "idiv%B0 %2",
+ "div%B0 %2",
+ 0,
+ 0,
+ 0,
+ 0,
+ "cwtd\n\tidiv%W0 %2",
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ "neg%L0 %0",
+ "neg%W0 %0",
+ "neg%B0 %0",
+ "fchs",
+ "fchs",
+ "fchs",
+ "fchs",
+ "fchs",
+ "fabs",
+ "fabs",
+ "fabs",
+ "fabs",
+ "fabs",
+ "fsqrt",
+ "fsqrt",
+ "fsqrt",
+ "fsqrt",
+ "fsqrt",
+ "fsqrt",
+ "fsin",
+ "fsin",
+ "fsin",
+ "fcos",
+ "fcos",
+ "fcos",
+ "not%L0 %0",
+ "not%W0 %0",
+ "not%B0 %0",
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ "ja %l0",
+ 0,
+ 0,
+ 0,
+ "jb %l0",
+ 0,
+ 0,
+ 0,
+ "jae %l0",
+ 0,
+ 0,
+ 0,
+ "jbe %l0",
+ 0,
+ 0,
+ 0,
+ "jbe %l0",
+ 0,
+ "jae %l0",
+ 0,
+ "jb %l0",
+ 0,
+ "ja %l0",
+ "jmp %l0",
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ "call %P0",
+ 0,
+ 0,
+ "call %P0",
+ 0,
+ 0,
+ "call %P1",
+ 0,
+ 0,
+ "call %P1",
+ 0,
+ 0,
+ 0,
+ 0,
+ "frstor %0",
+ 0,
+ "nop",
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ };
+
+char *(*const insn_outfun[])() =
+ {
+ output_0,
+ 0,
+ output_2,
+ 0,
+ output_4,
+ 0,
+ output_6,
+ 0,
+ output_8,
+ 0,
+ output_10,
+ 0,
+ output_12,
+ 0,
+ output_14,
+ 0,
+ output_16,
+ 0,
+ output_18,
+ output_19,
+ output_20,
+ output_21,
+ output_22,
+ output_23,
+ output_24,
+ output_25,
+ output_26,
+ output_27,
+ output_28,
+ output_29,
+ output_30,
+ output_31,
+ output_32,
+ output_33,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ output_43,
+ output_44,
+ output_45,
+ 0,
+ 0,
+ 0,
+ output_49,
+ 0,
+ output_51,
+ output_52,
+ output_53,
+ output_54,
+ output_55,
+ output_56,
+ output_57,
+ output_58,
+ output_59,
+ output_60,
+ output_61,
+ output_62,
+ output_63,
+ output_64,
+ output_65,
+ output_66,
+ output_67,
+ output_68,
+ output_69,
+ output_70,
+ output_71,
+ output_72,
+ output_73,
+ output_74,
+ output_75,
+ output_76,
+ 0,
+ output_78,
+ output_79,
+ output_80,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ output_87,
+ output_88,
+ output_89,
+ 0,
+ 0,
+ 0,
+ output_93,
+ output_94,
+ output_95,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ output_102,
+ output_103,
+ output_104,
+ output_105,
+ output_106,
+ output_107,
+ output_108,
+ output_109,
+ output_110,
+ output_111,
+ output_112,
+ 0,
+ 0,
+ 0,
+ output_116,
+ output_117,
+ output_118,
+ output_119,
+ 0,
+ 0,
+ 0,
+ output_123,
+ output_124,
+ output_125,
+ output_126,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ output_139,
+ 0,
+ output_141,
+ output_142,
+ output_143,
+ output_144,
+ output_145,
+ output_146,
+ output_147,
+ output_148,
+ output_149,
+ output_150,
+ output_151,
+ output_152,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ output_182,
+ output_183,
+ output_184,
+ output_185,
+ output_186,
+ 0,
+ output_188,
+ output_189,
+ output_190,
+ output_191,
+ output_192,
+ 0,
+ output_194,
+ output_195,
+ output_196,
+ output_197,
+ output_198,
+ output_199,
+ output_200,
+ output_201,
+ output_202,
+ output_203,
+ output_204,
+ output_205,
+ output_206,
+ output_207,
+ output_208,
+ output_209,
+ output_210,
+ 0,
+ output_212,
+ 0,
+ output_214,
+ 0,
+ output_216,
+ 0,
+ output_218,
+ 0,
+ output_220,
+ 0,
+ output_222,
+ 0,
+ output_224,
+ 0,
+ output_226,
+ 0,
+ output_228,
+ 0,
+ output_230,
+ 0,
+ output_232,
+ 0,
+ output_234,
+ 0,
+ output_236,
+ 0,
+ 0,
+ 0,
+ output_240,
+ 0,
+ 0,
+ 0,
+ output_244,
+ 0,
+ 0,
+ 0,
+ output_248,
+ 0,
+ 0,
+ output_251,
+ output_252,
+ output_253,
+ 0,
+ output_255,
+ 0,
+ output_257,
+ 0,
+ output_259,
+ 0,
+ 0,
+ output_262,
+ 0,
+ output_264,
+ output_265,
+ 0,
+ output_267,
+ 0,
+ 0,
+ output_270,
+ 0,
+ 0,
+ output_273,
+ 0,
+ 0,
+ output_276,
+ 0,
+ 0,
+ output_279,
+ output_280,
+ 0,
+ 0,
+ output_283,
+ 0,
+ 0,
+ output_286,
+ 0,
+ output_288,
+ output_289,
+ 0,
+ output_291,
+ 0,
+ output_293,
+ output_294,
+ output_295,
+ output_296,
+ output_297,
+ output_298,
+ output_299,
+ output_300,
+ output_301,
+ output_302,
+ output_303,
+ output_304,
+ output_305,
+ output_306,
+ 0,
+ output_308,
+ };
+
+rtx (*const insn_gen_function[]) () =
+ {
+ gen_tstsi_1,
+ gen_tstsi,
+ gen_tsthi_1,
+ gen_tsthi,
+ gen_tstqi_1,
+ gen_tstqi,
+ gen_tstsf_cc,
+ gen_tstsf,
+ gen_tstdf_cc,
+ gen_tstdf,
+ gen_tstxf_cc,
+ gen_tstxf,
+ gen_cmpsi_1,
+ gen_cmpsi,
+ gen_cmphi_1,
+ gen_cmphi,
+ gen_cmpqi_1,
+ gen_cmpqi,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ gen_cmpsf_cc_1,
+ 0,
+ 0,
+ 0,
+ gen_cmpxf,
+ gen_cmpdf,
+ gen_cmpsf,
+ gen_cmpxf_cc,
+ gen_cmpxf_ccfpeq,
+ gen_cmpdf_cc,
+ gen_cmpdf_ccfpeq,
+ gen_cmpsf_cc,
+ gen_cmpsf_ccfpeq,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ gen_movsi,
+ 0,
+ 0,
+ gen_movhi,
+ gen_movstricthi,
+ 0,
+ gen_movqi,
+ gen_movstrictqi,
+ 0,
+ gen_movsf,
+ 0,
+ gen_swapdf,
+ gen_movdf,
+ 0,
+ gen_swapxf,
+ gen_movxf,
+ 0,
+ gen_movdi,
+ gen_zero_extendhisi2,
+ gen_zero_extendqihi2,
+ gen_zero_extendqisi2,
+ gen_zero_extendsidi2,
+ gen_extendsidi2,
+ gen_extendhisi2,
+ gen_extendqihi2,
+ gen_extendqisi2,
+ gen_extendsfdf2,
+ gen_extenddfxf2,
+ gen_extendsfxf2,
+ gen_truncdfsf2,
+ 0,
+ gen_truncxfsf2,
+ gen_truncxfdf2,
+ gen_fixuns_truncxfsi2,
+ gen_fixuns_truncdfsi2,
+ gen_fixuns_truncsfsi2,
+ gen_fix_truncxfdi2,
+ gen_fix_truncdfdi2,
+ gen_fix_truncsfdi2,
+ 0,
+ 0,
+ 0,
+ gen_fix_truncxfsi2,
+ gen_fix_truncdfsi2,
+ gen_fix_truncsfsi2,
+ 0,
+ 0,
+ 0,
+ gen_floatsisf2,
+ gen_floatdisf2,
+ gen_floatsidf2,
+ gen_floatdidf2,
+ gen_floatsixf2,
+ gen_floatdixf2,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ gen_adddi3,
+ gen_addsi3,
+ gen_addhi3,
+ gen_addqi3,
+ 0,
+ gen_addxf3,
+ gen_adddf3,
+ gen_addsf3,
+ gen_subdi3,
+ gen_subsi3,
+ gen_subhi3,
+ gen_subqi3,
+ gen_subxf3,
+ gen_subdf3,
+ gen_subsf3,
+ 0,
+ gen_mulhi3,
+ 0,
+ gen_mulsi3,
+ gen_umulqihi3,
+ gen_mulqihi3,
+ gen_umulsidi3,
+ gen_mulsidi3,
+ gen_mulxf3,
+ gen_muldf3,
+ gen_mulsf3,
+ gen_divqi3,
+ gen_udivqi3,
+ gen_divxf3,
+ gen_divdf3,
+ gen_divsf3,
+ gen_divmodsi4,
+ gen_divmodhi4,
+ gen_udivmodsi4,
+ gen_udivmodhi4,
+ gen_andsi3,
+ gen_andhi3,
+ gen_andqi3,
+ gen_iorsi3,
+ gen_iorhi3,
+ gen_iorqi3,
+ gen_xorsi3,
+ gen_xorhi3,
+ gen_xorqi3,
+ gen_negdi2,
+ gen_negsi2,
+ gen_neghi2,
+ gen_negqi2,
+ gen_negsf2,
+ gen_negdf2,
+ 0,
+ gen_negxf2,
+ 0,
+ gen_abssf2,
+ gen_absdf2,
+ 0,
+ gen_absxf2,
+ 0,
+ gen_sqrtsf2,
+ gen_sqrtdf2,
+ 0,
+ gen_sqrtxf2,
+ 0,
+ 0,
+ gen_sindf2,
+ gen_sinsf2,
+ 0,
+ gen_cosdf2,
+ gen_cossf2,
+ 0,
+ gen_one_cmplsi2,
+ gen_one_cmplhi2,
+ gen_one_cmplqi2,
+ gen_ashldi3,
+ gen_ashldi3_const_int,
+ gen_ashldi3_non_const_int,
+ gen_ashlsi3,
+ gen_ashlhi3,
+ gen_ashlqi3,
+ gen_ashrdi3,
+ gen_ashrdi3_const_int,
+ gen_ashrdi3_non_const_int,
+ gen_ashrsi3,
+ gen_ashrhi3,
+ gen_ashrqi3,
+ gen_lshrdi3,
+ gen_lshrdi3_const_int,
+ gen_lshrdi3_non_const_int,
+ gen_lshrsi3,
+ gen_lshrhi3,
+ gen_lshrqi3,
+ gen_rotlsi3,
+ gen_rotlhi3,
+ gen_rotlqi3,
+ gen_rotrsi3,
+ gen_rotrhi3,
+ gen_rotrqi3,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ gen_seq,
+ 0,
+ gen_sne,
+ 0,
+ gen_sgt,
+ 0,
+ gen_sgtu,
+ 0,
+ gen_slt,
+ 0,
+ gen_sltu,
+ 0,
+ gen_sge,
+ 0,
+ gen_sgeu,
+ 0,
+ gen_sle,
+ 0,
+ gen_sleu,
+ 0,
+ gen_beq,
+ 0,
+ gen_bne,
+ 0,
+ gen_bgt,
+ 0,
+ gen_bgtu,
+ 0,
+ gen_blt,
+ 0,
+ gen_bltu,
+ 0,
+ gen_bge,
+ 0,
+ gen_bgeu,
+ 0,
+ gen_ble,
+ 0,
+ gen_bleu,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ gen_jump,
+ gen_indirect_jump,
+ gen_casesi,
+ 0,
+ gen_tablejump,
+ gen_call_pop,
+ 0,
+ 0,
+ gen_call,
+ 0,
+ 0,
+ gen_call_value_pop,
+ 0,
+ 0,
+ gen_call_value,
+ 0,
+ 0,
+ gen_untyped_call,
+ 0,
+ 0,
+ gen_untyped_return,
+ gen_update_return,
+ gen_return,
+ gen_nop,
+ gen_movstrsi,
+ 0,
+ gen_cmpstrsi,
+ 0,
+ 0,
+ gen_ffssi2,
+ 0,
+ gen_ffshi2,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ gen_strlensi,
+ 0,
+ };
+
+char *insn_name[] =
+ {
+ "tstsi_1",
+ "tstsi",
+ "tsthi_1",
+ "tsthi",
+ "tstqi_1",
+ "tstqi",
+ "tstsf_cc",
+ "tstsf",
+ "tstdf_cc",
+ "tstdf",
+ "tstxf_cc",
+ "tstxf",
+ "cmpsi_1",
+ "cmpsi",
+ "cmphi_1",
+ "cmphi",
+ "cmpqi_1",
+ "cmpqi",
+ "cmpqi+1",
+ "cmpqi+2",
+ "cmpqi+3",
+ "cmpqi+4",
+ "cmpqi+5",
+ "cmpqi+6",
+ "cmpsf_cc_1-6",
+ "cmpsf_cc_1-5",
+ "cmpsf_cc_1-4",
+ "cmpsf_cc_1-3",
+ "cmpsf_cc_1-2",
+ "cmpsf_cc_1-1",
+ "cmpsf_cc_1",
+ "cmpsf_cc_1+1",
+ "cmpsf_cc_1+2",
+ "cmpxf-1",
+ "cmpxf",
+ "cmpdf",
+ "cmpsf",
+ "cmpxf_cc",
+ "cmpxf_ccfpeq",
+ "cmpdf_cc",
+ "cmpdf_ccfpeq",
+ "cmpsf_cc",
+ "cmpsf_ccfpeq",
+ "cmpsf_ccfpeq+1",
+ "cmpsf_ccfpeq+2",
+ "cmpsf_ccfpeq+3",
+ "movsi-2",
+ "movsi-1",
+ "movsi",
+ "movsi+1",
+ "movhi-1",
+ "movhi",
+ "movstricthi",
+ "movstricthi+1",
+ "movqi",
+ "movstrictqi",
+ "movstrictqi+1",
+ "movsf",
+ "movsf+1",
+ "swapdf",
+ "movdf",
+ "movdf+1",
+ "swapxf",
+ "movxf",
+ "movxf+1",
+ "movdi",
+ "zero_extendhisi2",
+ "zero_extendqihi2",
+ "zero_extendqisi2",
+ "zero_extendsidi2",
+ "extendsidi2",
+ "extendhisi2",
+ "extendqihi2",
+ "extendqisi2",
+ "extendsfdf2",
+ "extenddfxf2",
+ "extendsfxf2",
+ "truncdfsf2",
+ "truncdfsf2+1",
+ "truncxfsf2",
+ "truncxfdf2",
+ "fixuns_truncxfsi2",
+ "fixuns_truncdfsi2",
+ "fixuns_truncsfsi2",
+ "fix_truncxfdi2",
+ "fix_truncdfdi2",
+ "fix_truncsfdi2",
+ "fix_truncsfdi2+1",
+ "fix_truncsfdi2+2",
+ "fix_truncxfsi2-1",
+ "fix_truncxfsi2",
+ "fix_truncdfsi2",
+ "fix_truncsfsi2",
+ "fix_truncsfsi2+1",
+ "fix_truncsfsi2+2",
+ "floatsisf2-1",
+ "floatsisf2",
+ "floatdisf2",
+ "floatsidf2",
+ "floatdidf2",
+ "floatsixf2",
+ "floatdixf2",
+ "floatdixf2+1",
+ "floatdixf2+2",
+ "floatdixf2+3",
+ "adddi3-3",
+ "adddi3-2",
+ "adddi3-1",
+ "adddi3",
+ "addsi3",
+ "addhi3",
+ "addqi3",
+ "addqi3+1",
+ "addxf3",
+ "adddf3",
+ "addsf3",
+ "subdi3",
+ "subsi3",
+ "subhi3",
+ "subqi3",
+ "subxf3",
+ "subdf3",
+ "subsf3",
+ "subsf3+1",
+ "mulhi3",
+ "mulhi3+1",
+ "mulsi3",
+ "umulqihi3",
+ "mulqihi3",
+ "umulsidi3",
+ "mulsidi3",
+ "mulxf3",
+ "muldf3",
+ "mulsf3",
+ "divqi3",
+ "udivqi3",
+ "divxf3",
+ "divdf3",
+ "divsf3",
+ "divmodsi4",
+ "divmodhi4",
+ "udivmodsi4",
+ "udivmodhi4",
+ "andsi3",
+ "andhi3",
+ "andqi3",
+ "iorsi3",
+ "iorhi3",
+ "iorqi3",
+ "xorsi3",
+ "xorhi3",
+ "xorqi3",
+ "negdi2",
+ "negsi2",
+ "neghi2",
+ "negqi2",
+ "negsf2",
+ "negdf2",
+ "negdf2+1",
+ "negxf2",
+ "negxf2+1",
+ "abssf2",
+ "absdf2",
+ "absdf2+1",
+ "absxf2",
+ "absxf2+1",
+ "sqrtsf2",
+ "sqrtdf2",
+ "sqrtdf2+1",
+ "sqrtxf2",
+ "sqrtxf2+1",
+ "sindf2-1",
+ "sindf2",
+ "sinsf2",
+ "sinsf2+1",
+ "cosdf2",
+ "cossf2",
+ "cossf2+1",
+ "one_cmplsi2",
+ "one_cmplhi2",
+ "one_cmplqi2",
+ "ashldi3",
+ "ashldi3_const_int",
+ "ashldi3_non_const_int",
+ "ashlsi3",
+ "ashlhi3",
+ "ashlqi3",
+ "ashrdi3",
+ "ashrdi3_const_int",
+ "ashrdi3_non_const_int",
+ "ashrsi3",
+ "ashrhi3",
+ "ashrqi3",
+ "lshrdi3",
+ "lshrdi3_const_int",
+ "lshrdi3_non_const_int",
+ "lshrsi3",
+ "lshrhi3",
+ "lshrqi3",
+ "rotlsi3",
+ "rotlhi3",
+ "rotlqi3",
+ "rotrsi3",
+ "rotrhi3",
+ "rotrqi3",
+ "rotrqi3+1",
+ "rotrqi3+2",
+ "rotrqi3+3",
+ "seq-3",
+ "seq-2",
+ "seq-1",
+ "seq",
+ "seq+1",
+ "sne",
+ "sne+1",
+ "sgt",
+ "sgt+1",
+ "sgtu",
+ "sgtu+1",
+ "slt",
+ "slt+1",
+ "sltu",
+ "sltu+1",
+ "sge",
+ "sge+1",
+ "sgeu",
+ "sgeu+1",
+ "sle",
+ "sle+1",
+ "sleu",
+ "sleu+1",
+ "beq",
+ "beq+1",
+ "bne",
+ "bne+1",
+ "bgt",
+ "bgt+1",
+ "bgtu",
+ "bgtu+1",
+ "blt",
+ "blt+1",
+ "bltu",
+ "bltu+1",
+ "bge",
+ "bge+1",
+ "bgeu",
+ "bgeu+1",
+ "ble",
+ "ble+1",
+ "bleu",
+ "bleu+1",
+ "bleu+2",
+ "bleu+3",
+ "bleu+4",
+ "bleu+5",
+ "bleu+6",
+ "jump-5",
+ "jump-4",
+ "jump-3",
+ "jump-2",
+ "jump-1",
+ "jump",
+ "indirect_jump",
+ "casesi",
+ "casesi+1",
+ "tablejump",
+ "call_pop",
+ "call_pop+1",
+ "call-1",
+ "call",
+ "call+1",
+ "call_value_pop-1",
+ "call_value_pop",
+ "call_value_pop+1",
+ "call_value-1",
+ "call_value",
+ "call_value+1",
+ "untyped_call-1",
+ "untyped_call",
+ "untyped_call+1",
+ "untyped_return-1",
+ "untyped_return",
+ "update_return",
+ "return",
+ "nop",
+ "movstrsi",
+ "movstrsi+1",
+ "cmpstrsi",
+ "cmpstrsi+1",
+ "ffssi2-1",
+ "ffssi2",
+ "ffssi2+1",
+ "ffshi2",
+ "ffshi2+1",
+ "ffshi2+2",
+ "ffshi2+3",
+ "ffshi2+4",
+ "ffshi2+5",
+ "ffshi2+6",
+ "ffshi2+7",
+ "strlensi-7",
+ "strlensi-6",
+ "strlensi-5",
+ "strlensi-4",
+ "strlensi-3",
+ "strlensi-2",
+ "strlensi-1",
+ "strlensi",
+ "strlensi+1",
+ };
+char **insn_name_ptr = insn_name;
+
+const int insn_n_operands[] =
+ {
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 3,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 3,
+ 4,
+ 4,
+ 4,
+ 3,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 3,
+ 2,
+ 2,
+ 8,
+ 8,
+ 8,
+ 6,
+ 6,
+ 6,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 4,
+ 4,
+ 4,
+ 4,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 4,
+ 3,
+ 3,
+ 2,
+ 3,
+ 3,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 7,
+ 3,
+ 1,
+ 4,
+ 4,
+ 4,
+ 2,
+ 2,
+ 2,
+ 5,
+ 5,
+ 5,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 2,
+ 1,
+ 0,
+ 0,
+ 5,
+ 5,
+ 5,
+ 5,
+ 4,
+ 2,
+ 2,
+ 2,
+ 2,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ };
+
+const int insn_n_dups[] =
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 2,
+ 0,
+ 0,
+ 2,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 7,
+ 7,
+ 7,
+ 5,
+ 5,
+ 5,
+ 1,
+ 1,
+ 1,
+ 2,
+ 2,
+ 2,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 2,
+ 2,
+ 2,
+ 2,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 3,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 2,
+ 0,
+ 2,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 5,
+ 1,
+ };
+
+char *const insn_operand_constraint[][MAX_RECOG_OPERANDS] =
+ {
+ { "rm", },
+ { "", },
+ { "rm", },
+ { "", },
+ { "qm", },
+ { "", },
+ { "f", "=a", },
+ { "", "", },
+ { "f", "=a", },
+ { "", "", },
+ { "f", "=a", },
+ { "", "", },
+ { "mr,r", "ri,mr", },
+ { "", "", },
+ { "mr,r", "ri,mr", },
+ { "", "", },
+ { "q,mq", "qm,nq", },
+ { "", "", },
+ { "f", "f", "", "=a", },
+ { "f", "rm", "", "=a", },
+ { "rm", "f", "", "=a", },
+ { "f", "fm", "", "=a", },
+ { "f", "fm", "", "=a", },
+ { "f", "f", "=a", },
+ { "f,fm", "fm,f", "", "=a,a", },
+ { "f", "rm", "", "=a", },
+ { "rm", "f", "", "=a", },
+ { "f", "fm", "", "=a", },
+ { "fm", "f", "", "=a", },
+ { "f", "f", "=a", },
+ { "f,fm", "fm,f", "", "=a,a", },
+ { "f", "rm", "", "=a", },
+ { "rm", "f", "", "=a", },
+ { "f", "f", "=a", },
+ { "", "", },
+ { "", "", },
+ { "", "", },
+ { "", "", "", },
+ { "", "", "", },
+ { "", "", "", },
+ { "", "", "", },
+ { "", "", "", },
+ { "", "", "", },
+ { "%ro", "ri", },
+ { "%ro", "ri", },
+ { "%qm", "qi", },
+ { "=<", "g", },
+ { "=<", "ri", },
+ { "", "", },
+ { "=g,r", "ri,m", },
+ { "=<", "g", },
+ { "=g,r", "ri,m", },
+ { "+g,r", "ri,m", },
+ { "=<", "q", },
+ { "=q,*r,qm", "*g,q,qn", },
+ { "+qm,q", "*qn,m", },
+ { "=<,<", "gF,f", },
+ { "=*rfm,*rf,f,!*rm", "*rf,*rfm,fG,fF", },
+ { "=<,<", "gF,f", },
+ { "f", "f", },
+ { "=*rfm,*rf,f,!*rm", "*rf,*rfm,fG,fF", },
+ { "=<,<", "gF,f", },
+ { "f", "f", },
+ { "=f,fm,!*rf,!*rm", "fmG,f,*rfm,*rfF", },
+ { "=<", "roiF", },
+ { "=r,rm", "m,riF", },
+ { "=r", "rm", },
+ { "=r", "qm", },
+ { "=r", "qm", },
+ { "=r", "0", },
+ { "=r", "0", },
+ { "=r", "rm", },
+ { "=r", "qm", },
+ { "=r", "qm", },
+ { "=fm,f", "f,fm", },
+ { "=fm,f,f,!*r", "f,fm,!*r,f", },
+ { "=fm,f,f,!*r", "f,fm,!*r,f", },
+ { "", "", },
+ { "=f,m", "0,f", "m,m", },
+ { "=m,!*r", "f,f", },
+ { "=m,!*r", "f,f", },
+ { "", "", "", "", "", "", "", "", },
+ { "", "", "", "", "", "", "", "", },
+ { "", "", "", "", "", "", "", "", },
+ { "", "", "", "", "", "", },
+ { "", "", "", "", "", "", },
+ { "", "", "", "", "", "", },
+ { "=rm", "f", "m", "m", "=&q", },
+ { "=rm", "f", "m", "m", "=&q", },
+ { "=rm", "f", "m", "m", "=&q", },
+ { "", "", "", "", "", },
+ { "", "", "", "", "", },
+ { "", "", "", "", "", },
+ { "=rm", "f", "m", "m", "=&q", },
+ { "=rm", "f", "m", "m", "=&q", },
+ { "=rm", "f", "m", "m", "=&q", },
+ { "", "", },
+ { "", "", },
+ { "", "", },
+ { "", "", },
+ { "", "", },
+ { "", "", },
+ { "=f", "rm", },
+ { "=f", "rm", },
+ { "=f", "rm", },
+ { "=f", "rm", },
+ { "=f,f", "m,!*r", },
+ { "=f", "rm", },
+ { "=&r,ro", "%0,0", "o,riF", },
+ { "=?r,rm,r", "%r,0,0", "ri,ri,rm", },
+ { "=rm,r", "%0,0", "ri,rm", },
+ { "=qm,q", "%0,0", "qn,qmn", },
+ { "=r", "p", },
+ { "", "", "", },
+ { "", "", "", },
+ { "", "", "", },
+ { "=&r,ro", "0,0", "o,riF", },
+ { "=rm,r", "0,0", "ri,rm", },
+ { "=rm,r", "0,0", "ri,rm", },
+ { "=qm,q", "0,0", "qn,qmn", },
+ { "", "", "", },
+ { "", "", "", },
+ { "", "", "", },
+ { "=r", "%0", "r", },
+ { "=r,r", "%0,rm", "g,i", },
+ { "=r", "%0", "r", },
+ { "=r,r", "%0,rm", "g,i", },
+ { "=a", "%0", "qm", },
+ { "=a", "%0", "qm", },
+ { "=A", "%0", "rm", },
+ { "=A", "%0", "rm", },
+ { "", "", "", },
+ { "", "", "", },
+ { "", "", "", },
+ { "=a", "0", "qm", },
+ { "=a", "0", "qm", },
+ { "", "", "", },
+ { "", "", "", },
+ { "", "", "", },
+ { "=a", "0", "rm", "=&d", },
+ { "=a", "0", "rm", "=&d", },
+ { "=a", "0", "rm", "=&d", },
+ { "=a", "0", "rm", "=&d", },
+ { "=r,r,rm,r", "%rm,qm,0,0", "L,K,ri,rm", },
+ { "=rm,r", "%0,0", "ri,rm", },
+ { "=qm,q", "%0,0", "qn,qmn", },
+ { "=rm,r", "%0,0", "ri,rm", },
+ { "=rm,r", "%0,0", "ri,rm", },
+ { "=qm,q", "%0,0", "qn,qmn", },
+ { "=rm,r", "%0,0", "ri,rm", },
+ { "=rm,r", "%0,0", "ri,rm", },
+ { "=qm,q", "%0,0", "qn,qm", },
+ { "=&ro", "0", },
+ { "=rm", "0", },
+ { "=rm", "0", },
+ { "=qm", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=f", "0", },
+ { "=rm", "0", },
+ { "=rm", "0", },
+ { "=qm", "0", },
+ { "", "", "", },
+ { "=&r", "0", "J", },
+ { "=&r", "0", "c", },
+ { "=r,rm", "r,0", "M,cI", },
+ { "=rm", "0", "cI", },
+ { "=qm", "0", "cI", },
+ { "", "", "", },
+ { "=&r", "0", "J", },
+ { "=&r", "0", "c", },
+ { "=rm", "0", "cI", },
+ { "=rm", "0", "cI", },
+ { "=qm", "0", "cI", },
+ { "", "", "", },
+ { "=&r", "0", "J", },
+ { "=&r", "0", "c", },
+ { "=rm", "0", "cI", },
+ { "=rm", "0", "cI", },
+ { "=qm", "0", "cI", },
+ { "=rm", "0", "cI", },
+ { "=rm", "0", "cI", },
+ { "=qm", "0", "cI", },
+ { "=rm", "0", "cI", },
+ { "=rm", "0", "cI", },
+ { "=qm", "0", "cI", },
+ { "+rm", "", "r", "n", },
+ { "=rm", "r", "0", },
+ { "=rm", "0", "r", },
+ { "r", "r", },
+ { "r", "n", "n", },
+ { "rm", "n", "n", },
+ { "", },
+ { "=q", },
+ { "", },
+ { "=q", },
+ { "", },
+ { "=q", },
+ { "", },
+ { "=q", },
+ { "", },
+ { "=q", },
+ { "", },
+ { "=q", },
+ { "", },
+ { "=q", },
+ { "", },
+ { "=q", },
+ { "", },
+ { "=q", },
+ { "", },
+ { "=q", },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { "rm", },
+ { "", "", "", "", "", "", "", },
+ { "r", "", "=&r", },
+ { "rm", },
+ { "", "", "", "", },
+ { "m", "g", "", "i", },
+ { "", "g", "", "i", },
+ { "", "", },
+ { "m", "g", },
+ { "", "g", },
+ { "", "", "", "", "", },
+ { "=rf", "m", "g", "", "i", },
+ { "=rf", "", "g", "", "i", },
+ { "", "", "", },
+ { "=rf", "m", "g", },
+ { "=rf", "", "g", },
+ { "", "", "", },
+ { "m", "o", "", },
+ { "", "o", "", },
+ { "", "", },
+ { "m", },
+ { 0 },
+ { 0 },
+ { "", "", "", "", "", },
+ { "D", "S", "n", "i", "=&c", },
+ { "", "", "", "", "", },
+ { "=&r", "S", "D", "c", "i", },
+ { "S", "D", "c", "i", },
+ { "", "", },
+ { "=&r", "rm", },
+ { "", "", },
+ { "=&r", "rm", },
+ { "=f,f", "0,fm", "fm,0", "", },
+ { "=f", "rm", "0", "", },
+ { "=f,f", "0,f", "f,0", "", },
+ { "=f", "rm", "0", "", },
+ { "=f,f", "fm,0", "0,f", "", },
+ { "=f", "0", "rm", "", },
+ { "=f,f", "0,f", "fm,0", "", },
+ { "=f,f", "fm,0", "0,f", "", },
+ { "=f", "0", "rm", "", },
+ { "=f,f", "0,f", "fm,0", "", },
+ { "=f,f", "0,fm", "fm,0", "", },
+ { "=f", "rm", "0", "", },
+ { "=f", "0", "rm", "", },
+ { "", "", "", "", },
+ { "=&c", "D", "a", "i", },
+ };
+
+const enum machine_mode insn_operand_mode[][MAX_RECOG_OPERANDS] =
+ {
+ { SImode, },
+ { SImode, },
+ { HImode, },
+ { HImode, },
+ { QImode, },
+ { QImode, },
+ { SFmode, HImode, },
+ { SFmode, HImode, },
+ { DFmode, HImode, },
+ { DFmode, HImode, },
+ { XFmode, HImode, },
+ { XFmode, HImode, },
+ { SImode, SImode, },
+ { SImode, SImode, },
+ { HImode, HImode, },
+ { HImode, HImode, },
+ { QImode, QImode, },
+ { QImode, QImode, },
+ { XFmode, XFmode, VOIDmode, HImode, },
+ { XFmode, SImode, VOIDmode, HImode, },
+ { SImode, XFmode, VOIDmode, HImode, },
+ { XFmode, DFmode, VOIDmode, HImode, },
+ { XFmode, SFmode, VOIDmode, HImode, },
+ { XFmode, XFmode, HImode, },
+ { DFmode, DFmode, VOIDmode, HImode, },
+ { DFmode, SImode, VOIDmode, HImode, },
+ { SImode, DFmode, VOIDmode, HImode, },
+ { DFmode, SFmode, VOIDmode, HImode, },
+ { SFmode, DFmode, VOIDmode, HImode, },
+ { DFmode, DFmode, HImode, },
+ { SFmode, SFmode, VOIDmode, HImode, },
+ { SFmode, SImode, VOIDmode, HImode, },
+ { SImode, SFmode, VOIDmode, HImode, },
+ { SFmode, SFmode, HImode, },
+ { XFmode, XFmode, },
+ { DFmode, DFmode, },
+ { SFmode, SFmode, },
+ { XFmode, XFmode, HImode, },
+ { XFmode, XFmode, HImode, },
+ { DFmode, DFmode, HImode, },
+ { DFmode, DFmode, HImode, },
+ { SFmode, SFmode, HImode, },
+ { SFmode, SFmode, HImode, },
+ { SImode, SImode, },
+ { HImode, HImode, },
+ { QImode, QImode, },
+ { SImode, SImode, },
+ { SImode, SImode, },
+ { SImode, SImode, },
+ { SImode, SImode, },
+ { HImode, HImode, },
+ { HImode, HImode, },
+ { HImode, HImode, },
+ { QImode, QImode, },
+ { QImode, QImode, },
+ { QImode, QImode, },
+ { SFmode, SFmode, },
+ { SFmode, SFmode, },
+ { DFmode, DFmode, },
+ { DFmode, DFmode, },
+ { DFmode, DFmode, },
+ { XFmode, XFmode, },
+ { XFmode, XFmode, },
+ { XFmode, XFmode, },
+ { DImode, DImode, },
+ { DImode, DImode, },
+ { SImode, HImode, },
+ { HImode, QImode, },
+ { SImode, QImode, },
+ { DImode, SImode, },
+ { DImode, SImode, },
+ { SImode, HImode, },
+ { HImode, QImode, },
+ { SImode, QImode, },
+ { DFmode, SFmode, },
+ { XFmode, DFmode, },
+ { XFmode, SFmode, },
+ { SFmode, DFmode, },
+ { SFmode, DFmode, SFmode, },
+ { SFmode, XFmode, },
+ { DFmode, XFmode, },
+ { SImode, XFmode, VOIDmode, VOIDmode, VOIDmode, VOIDmode, VOIDmode, SImode, },
+ { SImode, DFmode, VOIDmode, VOIDmode, VOIDmode, VOIDmode, VOIDmode, SImode, },
+ { SImode, SFmode, VOIDmode, VOIDmode, VOIDmode, VOIDmode, VOIDmode, SImode, },
+ { DImode, XFmode, VOIDmode, VOIDmode, VOIDmode, SImode, },
+ { DImode, DFmode, VOIDmode, VOIDmode, VOIDmode, SImode, },
+ { DImode, SFmode, VOIDmode, VOIDmode, VOIDmode, SImode, },
+ { DImode, XFmode, SImode, SImode, SImode, },
+ { DImode, DFmode, SImode, SImode, SImode, },
+ { DImode, SFmode, SImode, SImode, SImode, },
+ { SImode, XFmode, VOIDmode, VOIDmode, SImode, },
+ { SImode, DFmode, VOIDmode, VOIDmode, SImode, },
+ { SImode, SFmode, VOIDmode, VOIDmode, SImode, },
+ { SImode, XFmode, SImode, SImode, SImode, },
+ { SImode, DFmode, SImode, SImode, SImode, },
+ { SImode, SFmode, SImode, SImode, SImode, },
+ { SFmode, SImode, },
+ { SFmode, DImode, },
+ { DFmode, SImode, },
+ { DFmode, DImode, },
+ { XFmode, SImode, },
+ { XFmode, DImode, },
+ { XFmode, DImode, },
+ { DFmode, DImode, },
+ { SFmode, DImode, },
+ { DFmode, SImode, },
+ { XFmode, SImode, },
+ { SFmode, SImode, },
+ { DImode, DImode, DImode, },
+ { SImode, SImode, SImode, },
+ { HImode, HImode, HImode, },
+ { QImode, QImode, QImode, },
+ { SImode, QImode, },
+ { XFmode, XFmode, XFmode, },
+ { DFmode, DFmode, DFmode, },
+ { SFmode, SFmode, SFmode, },
+ { DImode, DImode, DImode, },
+ { SImode, SImode, SImode, },
+ { HImode, HImode, HImode, },
+ { QImode, QImode, QImode, },
+ { XFmode, XFmode, XFmode, },
+ { DFmode, DFmode, DFmode, },
+ { SFmode, SFmode, SFmode, },
+ { HImode, HImode, HImode, },
+ { HImode, HImode, HImode, },
+ { SImode, SImode, SImode, },
+ { SImode, SImode, SImode, },
+ { HImode, QImode, QImode, },
+ { HImode, QImode, QImode, },
+ { DImode, SImode, SImode, },
+ { DImode, SImode, SImode, },
+ { XFmode, XFmode, XFmode, },
+ { DFmode, DFmode, DFmode, },
+ { SFmode, SFmode, SFmode, },
+ { QImode, HImode, QImode, },
+ { QImode, HImode, QImode, },
+ { XFmode, XFmode, XFmode, },
+ { DFmode, DFmode, DFmode, },
+ { SFmode, SFmode, SFmode, },
+ { SImode, SImode, SImode, SImode, },
+ { HImode, HImode, HImode, HImode, },
+ { SImode, SImode, SImode, SImode, },
+ { HImode, HImode, HImode, HImode, },
+ { SImode, SImode, SImode, },
+ { HImode, HImode, HImode, },
+ { QImode, QImode, QImode, },
+ { SImode, SImode, SImode, },
+ { HImode, HImode, HImode, },
+ { QImode, QImode, QImode, },
+ { SImode, SImode, SImode, },
+ { HImode, HImode, HImode, },
+ { QImode, QImode, QImode, },
+ { DImode, DImode, },
+ { SImode, SImode, },
+ { HImode, HImode, },
+ { QImode, QImode, },
+ { SFmode, SFmode, },
+ { DFmode, DFmode, },
+ { DFmode, SFmode, },
+ { XFmode, XFmode, },
+ { XFmode, DFmode, },
+ { SFmode, SFmode, },
+ { DFmode, DFmode, },
+ { DFmode, SFmode, },
+ { XFmode, XFmode, },
+ { XFmode, DFmode, },
+ { SFmode, SFmode, },
+ { DFmode, DFmode, },
+ { DFmode, SFmode, },
+ { XFmode, XFmode, },
+ { XFmode, DFmode, },
+ { XFmode, SFmode, },
+ { DFmode, DFmode, },
+ { SFmode, SFmode, },
+ { DFmode, SFmode, },
+ { DFmode, DFmode, },
+ { SFmode, SFmode, },
+ { DFmode, SFmode, },
+ { SImode, SImode, },
+ { HImode, HImode, },
+ { QImode, QImode, },
+ { DImode, DImode, QImode, },
+ { DImode, DImode, QImode, },
+ { DImode, DImode, QImode, },
+ { SImode, SImode, SImode, },
+ { HImode, HImode, HImode, },
+ { QImode, QImode, QImode, },
+ { DImode, DImode, QImode, },
+ { DImode, DImode, QImode, },
+ { DImode, DImode, QImode, },
+ { SImode, SImode, SImode, },
+ { HImode, HImode, HImode, },
+ { QImode, QImode, QImode, },
+ { DImode, DImode, QImode, },
+ { DImode, DImode, QImode, },
+ { DImode, DImode, QImode, },
+ { SImode, SImode, SImode, },
+ { HImode, HImode, HImode, },
+ { QImode, QImode, QImode, },
+ { SImode, SImode, SImode, },
+ { HImode, HImode, HImode, },
+ { QImode, QImode, QImode, },
+ { SImode, SImode, SImode, },
+ { HImode, HImode, HImode, },
+ { QImode, QImode, QImode, },
+ { SImode, VOIDmode, SImode, SImode, },
+ { SImode, SImode, SImode, },
+ { SImode, SImode, SImode, },
+ { SImode, SImode, },
+ { SImode, SImode, SImode, },
+ { QImode, SImode, SImode, },
+ { QImode, },
+ { QImode, },
+ { QImode, },
+ { QImode, },
+ { QImode, },
+ { QImode, },
+ { QImode, },
+ { QImode, },
+ { QImode, },
+ { QImode, },
+ { QImode, },
+ { QImode, },
+ { QImode, },
+ { QImode, },
+ { QImode, },
+ { QImode, },
+ { QImode, },
+ { QImode, },
+ { QImode, },
+ { QImode, },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { VOIDmode },
+ { SImode, },
+ { SImode, SImode, SImode, VOIDmode, VOIDmode, VOIDmode, SImode, },
+ { SImode, VOIDmode, SImode, },
+ { SImode, },
+ { QImode, SImode, VOIDmode, SImode, },
+ { QImode, SImode, VOIDmode, SImode, },
+ { SImode, SImode, VOIDmode, SImode, },
+ { QImode, SImode, },
+ { QImode, SImode, },
+ { SImode, SImode, },
+ { VOIDmode, QImode, SImode, VOIDmode, SImode, },
+ { VOIDmode, QImode, SImode, VOIDmode, SImode, },
+ { VOIDmode, SImode, SImode, VOIDmode, SImode, },
+ { VOIDmode, QImode, SImode, },
+ { VOIDmode, QImode, SImode, },
+ { VOIDmode, SImode, SImode, },
+ { QImode, BLKmode, VOIDmode, },
+ { QImode, DImode, VOIDmode, },
+ { SImode, DImode, VOIDmode, },
+ { BLKmode, VOIDmode, },
+ { SImode, },
+ { VOIDmode },
+ { VOIDmode },
+ { BLKmode, BLKmode, SImode, SImode, SImode, },
+ { SImode, SImode, SImode, SImode, SImode, },
+ { SImode, BLKmode, BLKmode, SImode, SImode, },
+ { SImode, SImode, SImode, SImode, SImode, },
+ { SImode, SImode, SImode, SImode, },
+ { SImode, SImode, },
+ { SImode, SImode, },
+ { HImode, HImode, },
+ { HImode, SImode, },
+ { DFmode, DFmode, DFmode, DFmode, },
+ { DFmode, SImode, DFmode, DFmode, },
+ { XFmode, XFmode, XFmode, XFmode, },
+ { XFmode, SImode, XFmode, XFmode, },
+ { XFmode, SFmode, XFmode, XFmode, },
+ { XFmode, XFmode, SImode, XFmode, },
+ { XFmode, XFmode, SFmode, XFmode, },
+ { DFmode, SFmode, DFmode, DFmode, },
+ { DFmode, DFmode, SImode, DFmode, },
+ { DFmode, DFmode, SFmode, DFmode, },
+ { SFmode, SFmode, SFmode, SFmode, },
+ { SFmode, SImode, SFmode, SFmode, },
+ { SFmode, SFmode, SImode, SFmode, },
+ { SImode, BLKmode, QImode, SImode, },
+ { SImode, SImode, QImode, SImode, },
+ };
+
+const char insn_operand_strict_low[][MAX_RECOG_OPERANDS] =
+ {
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 1, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 1, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, 0, 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0, },
+ { 0, 0, 0, 0, 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, 0, },
+ { 0, 0, },
+ { 0, },
+ { 0 },
+ { 0 },
+ { 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ { 0, 0, 0, 0, },
+ };
+
+extern int nonimmediate_operand ();
+extern int register_operand ();
+extern int scratch_operand ();
+extern int general_operand ();
+extern int VOIDmode_compare_op ();
+extern int push_operand ();
+extern int memory_operand ();
+extern int address_operand ();
+extern int nonmemory_operand ();
+extern int const_int_operand ();
+extern int indirect_operand ();
+extern int immediate_operand ();
+extern int call_insn_operand ();
+extern int symbolic_operand ();
+extern int binary_387_op ();
+
+int (*const insn_operand_predicate[][MAX_RECOG_OPERANDS])() =
+ {
+ { nonimmediate_operand, },
+ { nonimmediate_operand, },
+ { nonimmediate_operand, },
+ { nonimmediate_operand, },
+ { nonimmediate_operand, },
+ { nonimmediate_operand, },
+ { register_operand, scratch_operand, },
+ { register_operand, scratch_operand, },
+ { register_operand, scratch_operand, },
+ { register_operand, scratch_operand, },
+ { register_operand, scratch_operand, },
+ { register_operand, scratch_operand, },
+ { nonimmediate_operand, general_operand, },
+ { nonimmediate_operand, general_operand, },
+ { nonimmediate_operand, general_operand, },
+ { nonimmediate_operand, general_operand, },
+ { nonimmediate_operand, general_operand, },
+ { nonimmediate_operand, general_operand, },
+ { nonimmediate_operand, nonimmediate_operand, VOIDmode_compare_op, scratch_operand, },
+ { register_operand, nonimmediate_operand, VOIDmode_compare_op, scratch_operand, },
+ { nonimmediate_operand, register_operand, VOIDmode_compare_op, scratch_operand, },
+ { register_operand, nonimmediate_operand, VOIDmode_compare_op, scratch_operand, },
+ { register_operand, nonimmediate_operand, VOIDmode_compare_op, scratch_operand, },
+ { register_operand, register_operand, scratch_operand, },
+ { nonimmediate_operand, nonimmediate_operand, VOIDmode_compare_op, scratch_operand, },
+ { register_operand, nonimmediate_operand, VOIDmode_compare_op, scratch_operand, },
+ { nonimmediate_operand, register_operand, VOIDmode_compare_op, scratch_operand, },
+ { register_operand, nonimmediate_operand, VOIDmode_compare_op, scratch_operand, },
+ { nonimmediate_operand, register_operand, VOIDmode_compare_op, scratch_operand, },
+ { register_operand, register_operand, scratch_operand, },
+ { nonimmediate_operand, nonimmediate_operand, VOIDmode_compare_op, scratch_operand, },
+ { register_operand, nonimmediate_operand, VOIDmode_compare_op, scratch_operand, },
+ { nonimmediate_operand, register_operand, VOIDmode_compare_op, scratch_operand, },
+ { register_operand, register_operand, scratch_operand, },
+ { register_operand, nonimmediate_operand, },
+ { register_operand, nonimmediate_operand, },
+ { register_operand, nonimmediate_operand, },
+ { register_operand, register_operand, scratch_operand, },
+ { register_operand, register_operand, scratch_operand, },
+ { register_operand, register_operand, scratch_operand, },
+ { register_operand, register_operand, scratch_operand, },
+ { register_operand, register_operand, scratch_operand, },
+ { register_operand, register_operand, scratch_operand, },
+ { general_operand, general_operand, },
+ { general_operand, general_operand, },
+ { general_operand, general_operand, },
+ { push_operand, general_operand, },
+ { push_operand, general_operand, },
+ { general_operand, general_operand, },
+ { general_operand, general_operand, },
+ { push_operand, general_operand, },
+ { general_operand, general_operand, },
+ { general_operand, general_operand, },
+ { push_operand, general_operand, },
+ { general_operand, general_operand, },
+ { general_operand, general_operand, },
+ { push_operand, general_operand, },
+ { general_operand, general_operand, },
+ { push_operand, general_operand, },
+ { register_operand, register_operand, },
+ { general_operand, general_operand, },
+ { push_operand, general_operand, },
+ { register_operand, register_operand, },
+ { general_operand, general_operand, },
+ { push_operand, general_operand, },
+ { general_operand, general_operand, },
+ { general_operand, nonimmediate_operand, },
+ { general_operand, nonimmediate_operand, },
+ { general_operand, nonimmediate_operand, },
+ { register_operand, register_operand, },
+ { register_operand, register_operand, },
+ { general_operand, nonimmediate_operand, },
+ { general_operand, nonimmediate_operand, },
+ { general_operand, nonimmediate_operand, },
+ { general_operand, general_operand, },
+ { general_operand, general_operand, },
+ { general_operand, general_operand, },
+ { nonimmediate_operand, register_operand, },
+ { nonimmediate_operand, register_operand, memory_operand, },
+ { general_operand, register_operand, },
+ { general_operand, register_operand, },
+ { general_operand, register_operand, 0, 0, 0, 0, 0, scratch_operand, },
+ { general_operand, register_operand, 0, 0, 0, 0, 0, scratch_operand, },
+ { general_operand, register_operand, 0, 0, 0, 0, 0, scratch_operand, },
+ { general_operand, register_operand, 0, 0, 0, scratch_operand, },
+ { general_operand, register_operand, 0, 0, 0, scratch_operand, },
+ { general_operand, register_operand, 0, 0, 0, scratch_operand, },
+ { general_operand, register_operand, memory_operand, memory_operand, scratch_operand, },
+ { general_operand, register_operand, memory_operand, memory_operand, scratch_operand, },
+ { general_operand, register_operand, memory_operand, memory_operand, scratch_operand, },
+ { general_operand, register_operand, 0, 0, scratch_operand, },
+ { general_operand, register_operand, 0, 0, scratch_operand, },
+ { general_operand, register_operand, 0, 0, scratch_operand, },
+ { general_operand, register_operand, memory_operand, memory_operand, scratch_operand, },
+ { general_operand, register_operand, memory_operand, memory_operand, scratch_operand, },
+ { general_operand, register_operand, memory_operand, memory_operand, scratch_operand, },
+ { register_operand, nonimmediate_operand, },
+ { register_operand, nonimmediate_operand, },
+ { register_operand, nonimmediate_operand, },
+ { register_operand, nonimmediate_operand, },
+ { register_operand, nonimmediate_operand, },
+ { register_operand, nonimmediate_operand, },
+ { register_operand, general_operand, },
+ { register_operand, nonimmediate_operand, },
+ { register_operand, nonimmediate_operand, },
+ { register_operand, nonimmediate_operand, },
+ { register_operand, general_operand, },
+ { register_operand, nonimmediate_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, general_operand, },
+ { register_operand, address_operand, },
+ { register_operand, nonimmediate_operand, nonimmediate_operand, },
+ { register_operand, nonimmediate_operand, nonimmediate_operand, },
+ { register_operand, nonimmediate_operand, nonimmediate_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, general_operand, },
+ { register_operand, nonimmediate_operand, nonimmediate_operand, },
+ { register_operand, nonimmediate_operand, nonimmediate_operand, },
+ { register_operand, nonimmediate_operand, nonimmediate_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, nonimmediate_operand, nonimmediate_operand, },
+ { general_operand, nonimmediate_operand, nonimmediate_operand, },
+ { register_operand, register_operand, nonimmediate_operand, },
+ { register_operand, register_operand, nonimmediate_operand, },
+ { register_operand, nonimmediate_operand, nonimmediate_operand, },
+ { register_operand, nonimmediate_operand, nonimmediate_operand, },
+ { register_operand, nonimmediate_operand, nonimmediate_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, general_operand, },
+ { register_operand, nonimmediate_operand, nonimmediate_operand, },
+ { register_operand, nonimmediate_operand, nonimmediate_operand, },
+ { register_operand, nonimmediate_operand, nonimmediate_operand, },
+ { register_operand, register_operand, general_operand, register_operand, },
+ { register_operand, register_operand, general_operand, register_operand, },
+ { register_operand, register_operand, general_operand, register_operand, },
+ { register_operand, register_operand, general_operand, register_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, },
+ { general_operand, general_operand, },
+ { general_operand, general_operand, },
+ { general_operand, general_operand, },
+ { register_operand, general_operand, },
+ { register_operand, general_operand, },
+ { register_operand, general_operand, },
+ { register_operand, general_operand, },
+ { register_operand, general_operand, },
+ { register_operand, general_operand, },
+ { register_operand, general_operand, },
+ { register_operand, general_operand, },
+ { register_operand, general_operand, },
+ { register_operand, general_operand, },
+ { register_operand, general_operand, },
+ { register_operand, general_operand, },
+ { register_operand, general_operand, },
+ { register_operand, general_operand, },
+ { register_operand, general_operand, },
+ { register_operand, general_operand, },
+ { register_operand, register_operand, },
+ { register_operand, register_operand, },
+ { register_operand, register_operand, },
+ { register_operand, register_operand, },
+ { register_operand, register_operand, },
+ { register_operand, register_operand, },
+ { general_operand, general_operand, },
+ { general_operand, general_operand, },
+ { general_operand, general_operand, },
+ { register_operand, register_operand, nonmemory_operand, },
+ { register_operand, register_operand, const_int_operand, },
+ { register_operand, register_operand, register_operand, },
+ { general_operand, general_operand, nonmemory_operand, },
+ { general_operand, general_operand, nonmemory_operand, },
+ { general_operand, general_operand, nonmemory_operand, },
+ { register_operand, register_operand, nonmemory_operand, },
+ { register_operand, register_operand, const_int_operand, },
+ { register_operand, register_operand, register_operand, },
+ { general_operand, general_operand, nonmemory_operand, },
+ { general_operand, general_operand, nonmemory_operand, },
+ { general_operand, general_operand, nonmemory_operand, },
+ { register_operand, register_operand, nonmemory_operand, },
+ { register_operand, register_operand, const_int_operand, },
+ { register_operand, register_operand, register_operand, },
+ { general_operand, general_operand, nonmemory_operand, },
+ { general_operand, general_operand, nonmemory_operand, },
+ { general_operand, general_operand, nonmemory_operand, },
+ { general_operand, general_operand, nonmemory_operand, },
+ { general_operand, general_operand, nonmemory_operand, },
+ { general_operand, general_operand, nonmemory_operand, },
+ { general_operand, general_operand, nonmemory_operand, },
+ { general_operand, general_operand, nonmemory_operand, },
+ { general_operand, general_operand, nonmemory_operand, },
+ { general_operand, 0, general_operand, const_int_operand, },
+ { general_operand, general_operand, general_operand, },
+ { general_operand, general_operand, general_operand, },
+ { register_operand, general_operand, },
+ { register_operand, const_int_operand, const_int_operand, },
+ { general_operand, const_int_operand, const_int_operand, },
+ { register_operand, },
+ { register_operand, },
+ { register_operand, },
+ { register_operand, },
+ { register_operand, },
+ { register_operand, },
+ { register_operand, },
+ { register_operand, },
+ { register_operand, },
+ { register_operand, },
+ { register_operand, },
+ { register_operand, },
+ { register_operand, },
+ { register_operand, },
+ { register_operand, },
+ { register_operand, },
+ { register_operand, },
+ { register_operand, },
+ { register_operand, },
+ { register_operand, },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { general_operand, },
+ { general_operand, general_operand, general_operand, 0, 0, 0, scratch_operand, },
+ { register_operand, 0, scratch_operand, },
+ { general_operand, },
+ { indirect_operand, general_operand, 0, immediate_operand, },
+ { call_insn_operand, general_operand, 0, immediate_operand, },
+ { symbolic_operand, general_operand, 0, immediate_operand, },
+ { indirect_operand, general_operand, },
+ { call_insn_operand, general_operand, },
+ { symbolic_operand, general_operand, },
+ { 0, indirect_operand, general_operand, 0, immediate_operand, },
+ { 0, call_insn_operand, general_operand, 0, immediate_operand, },
+ { 0, symbolic_operand, general_operand, 0, immediate_operand, },
+ { 0, indirect_operand, general_operand, },
+ { 0, call_insn_operand, general_operand, },
+ { 0, symbolic_operand, general_operand, },
+ { indirect_operand, memory_operand, 0, },
+ { call_insn_operand, memory_operand, 0, },
+ { symbolic_operand, memory_operand, 0, },
+ { memory_operand, 0, },
+ { memory_operand, },
+ { 0 },
+ { 0 },
+ { memory_operand, memory_operand, const_int_operand, const_int_operand, scratch_operand, },
+ { address_operand, address_operand, const_int_operand, immediate_operand, scratch_operand, },
+ { general_operand, general_operand, general_operand, general_operand, immediate_operand, },
+ { general_operand, address_operand, address_operand, register_operand, immediate_operand, },
+ { address_operand, address_operand, register_operand, immediate_operand, },
+ { general_operand, general_operand, },
+ { general_operand, general_operand, },
+ { general_operand, general_operand, },
+ { general_operand, general_operand, },
+ { register_operand, nonimmediate_operand, nonimmediate_operand, binary_387_op, },
+ { register_operand, general_operand, general_operand, binary_387_op, },
+ { register_operand, nonimmediate_operand, nonimmediate_operand, binary_387_op, },
+ { register_operand, general_operand, general_operand, binary_387_op, },
+ { register_operand, general_operand, general_operand, binary_387_op, },
+ { register_operand, general_operand, general_operand, binary_387_op, },
+ { register_operand, general_operand, general_operand, binary_387_op, },
+ { register_operand, general_operand, general_operand, binary_387_op, },
+ { register_operand, general_operand, general_operand, binary_387_op, },
+ { register_operand, general_operand, general_operand, binary_387_op, },
+ { register_operand, nonimmediate_operand, nonimmediate_operand, binary_387_op, },
+ { register_operand, general_operand, general_operand, binary_387_op, },
+ { register_operand, general_operand, general_operand, binary_387_op, },
+ { register_operand, general_operand, register_operand, immediate_operand, },
+ { register_operand, address_operand, register_operand, immediate_operand, },
+ };
+
+const int insn_n_alternatives[] =
+ {
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 2,
+ 0,
+ 2,
+ 0,
+ 2,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 2,
+ 1,
+ 2,
+ 2,
+ 1,
+ 3,
+ 2,
+ 2,
+ 4,
+ 2,
+ 1,
+ 4,
+ 2,
+ 1,
+ 4,
+ 1,
+ 2,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2,
+ 4,
+ 4,
+ 0,
+ 2,
+ 2,
+ 2,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2,
+ 1,
+ 2,
+ 3,
+ 2,
+ 2,
+ 1,
+ 0,
+ 0,
+ 0,
+ 2,
+ 2,
+ 2,
+ 2,
+ 0,
+ 0,
+ 0,
+ 1,
+ 2,
+ 1,
+ 2,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 4,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 2,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1,
+ 0,
+ 1,
+ 2,
+ 1,
+ 2,
+ 1,
+ 2,
+ 1,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 1,
+ 1,
+ 0,
+ 1,
+ };
diff --git a/gnu/usr.bin/cc/cc_int/insn-peep.c b/gnu/usr.bin/cc/cc_int/insn-peep.c
new file mode 100644
index 0000000..37136c4
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/insn-peep.c
@@ -0,0 +1,28 @@
+/* Generated automatically by the program `genpeep'
+from the machine description file `md'. */
+
+#include "config.h"
+#include "rtl.h"
+#include "regs.h"
+#include "output.h"
+#include "real.h"
+
+extern rtx peep_operand[];
+
+#define operands peep_operand
+
+rtx
+peephole (ins1)
+ rtx ins1;
+{
+ rtx insn, x, pat;
+ int i;
+
+ if (NEXT_INSN (ins1)
+ && GET_CODE (NEXT_INSN (ins1)) == BARRIER)
+ return 0;
+
+ return 0;
+}
+
+rtx peep_operand[2];
diff --git a/gnu/usr.bin/cc/cc_int/insn-recog.c b/gnu/usr.bin/cc/cc_int/insn-recog.c
new file mode 100644
index 0000000..293dd7e
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/insn-recog.c
@@ -0,0 +1,7138 @@
+/* Generated automatically by the program `genrecog'
+from the machine description file `md'. */
+
+#include "config.h"
+#include "rtl.h"
+#include "insn-config.h"
+#include "recog.h"
+#include "real.h"
+#include "output.h"
+#include "flags.h"
+
+
+/* `recog' contains a decision tree
+ that recognizes whether the rtx X0 is a valid instruction.
+
+ recog returns -1 if the rtx is not valid.
+ If the rtx is valid, recog returns a nonnegative number
+ which is the insn code number for the pattern that matched.
+ This is the same as the order in the machine description of
+ the entry that matched. This number can be used as an index into
+ entry that matched. This number can be used as an index into various
+ insn_* tables, such as insn_templates, insn_outfun, and insn_n_operands
+ (found in insn-output.c).
+
+ The third argument to recog is an optional pointer to an int.
+ If present, recog will accept a pattern if it matches except for
+ missing CLOBBER expressions at the end. In that case, the value
+ pointed to by the optional pointer will be set to the number of
+ CLOBBERs that need to be added (it should be initialized to zero by
+ the caller). If it is set nonzero, the caller should allocate a
+ PARALLEL of the appropriate size, copy the initial entries, and call
+ add_clobbers (found in insn-emit.c) to fill in the CLOBBERs.*/
+
+rtx recog_operand[MAX_RECOG_OPERANDS];
+
+rtx *recog_operand_loc[MAX_RECOG_OPERANDS];
+
+rtx *recog_dup_loc[MAX_DUP_OPERANDS];
+
+char recog_dup_num[MAX_DUP_OPERANDS];
+
+#define operands recog_operand
+
+int
+recog_1 (x0, insn, pnum_clobbers)
+ register rtx x0;
+ rtx insn;
+ int *pnum_clobbers;
+{
+ register rtx *ro = &recog_operand[0];
+ register rtx x1, x2, x3, x4, x5, x6;
+ int tem;
+
+ x1 = XEXP (x0, 1);
+ switch (GET_MODE (x1))
+ {
+ case HImode:
+ switch (GET_CODE (x1))
+ {
+ case ZERO_EXTEND:
+ goto L370;
+ case SIGN_EXTEND:
+ goto L390;
+ case PLUS:
+ goto L603;
+ case MINUS:
+ goto L626;
+ case MULT:
+ goto L660;
+ case AND:
+ goto L747;
+ case IOR:
+ goto L762;
+ case XOR:
+ goto L777;
+ case NEG:
+ goto L795;
+ case NOT:
+ goto L904;
+ case ASHIFT:
+ goto L930;
+ case ASHIFTRT:
+ goto L958;
+ case LSHIFTRT:
+ goto L986;
+ case ROTATE:
+ goto L1001;
+ case ROTATERT:
+ goto L1016;
+ }
+ }
+ if (general_operand (x1, HImode))
+ {
+ ro[1] = x1;
+ return 51;
+ }
+ goto ret0;
+
+ L370:
+ x2 = XEXP (x1, 0);
+ if (nonimmediate_operand (x2, QImode))
+ {
+ ro[1] = x2;
+ return 67;
+ }
+ goto ret0;
+
+ L390:
+ x2 = XEXP (x1, 0);
+ if (nonimmediate_operand (x2, QImode))
+ {
+ ro[1] = x2;
+ return 72;
+ }
+ goto ret0;
+
+ L603:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ goto L604;
+ }
+ goto ret0;
+
+ L604:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, HImode))
+ {
+ ro[2] = x2;
+ return 110;
+ }
+ goto ret0;
+
+ L626:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ goto L627;
+ }
+ goto ret0;
+
+ L627:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, HImode))
+ {
+ ro[2] = x2;
+ return 118;
+ }
+ goto ret0;
+
+ L660:
+ x2 = XEXP (x1, 0);
+ switch (GET_MODE (x2))
+ {
+ case HImode:
+ switch (GET_CODE (x2))
+ {
+ case ZERO_EXTEND:
+ goto L661;
+ case SIGN_EXTEND:
+ goto L668;
+ }
+ }
+ if (general_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ goto L637;
+ }
+ goto ret0;
+
+ L661:
+ x3 = XEXP (x2, 0);
+ if (nonimmediate_operand (x3, QImode))
+ {
+ ro[1] = x3;
+ goto L662;
+ }
+ goto ret0;
+
+ L662:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == HImode && GET_CODE (x2) == ZERO_EXTEND && 1)
+ goto L663;
+ goto ret0;
+
+ L663:
+ x3 = XEXP (x2, 0);
+ if (nonimmediate_operand (x3, QImode))
+ {
+ ro[2] = x3;
+ return 127;
+ }
+ goto ret0;
+
+ L668:
+ x3 = XEXP (x2, 0);
+ if (nonimmediate_operand (x3, QImode))
+ {
+ ro[1] = x3;
+ goto L669;
+ }
+ goto ret0;
+
+ L669:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == HImode && GET_CODE (x2) == SIGN_EXTEND && 1)
+ goto L670;
+ goto ret0;
+
+ L670:
+ x3 = XEXP (x2, 0);
+ if (nonimmediate_operand (x3, QImode))
+ {
+ ro[2] = x3;
+ return 128;
+ }
+ goto ret0;
+
+ L637:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, HImode))
+ goto L643;
+ goto ret0;
+
+ L643:
+ ro[2] = x2;
+ if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x80)
+ return 123;
+ L644:
+ ro[2] = x2;
+ return 124;
+
+ L747:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ goto L748;
+ }
+ goto ret0;
+
+ L748:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, HImode))
+ {
+ ro[2] = x2;
+ return 144;
+ }
+ goto ret0;
+
+ L762:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ goto L763;
+ }
+ goto ret0;
+
+ L763:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, HImode))
+ {
+ ro[2] = x2;
+ return 147;
+ }
+ goto ret0;
+
+ L777:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ goto L778;
+ }
+ goto ret0;
+
+ L778:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, HImode))
+ {
+ ro[2] = x2;
+ return 150;
+ }
+ goto ret0;
+
+ L795:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ return 154;
+ }
+ goto ret0;
+
+ L904:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ return 179;
+ }
+ goto ret0;
+
+ L930:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ goto L931;
+ }
+ goto ret0;
+
+ L931:
+ x2 = XEXP (x1, 1);
+ if (nonmemory_operand (x2, HImode))
+ {
+ ro[2] = x2;
+ return 185;
+ }
+ goto ret0;
+
+ L958:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ goto L959;
+ }
+ goto ret0;
+
+ L959:
+ x2 = XEXP (x1, 1);
+ if (nonmemory_operand (x2, HImode))
+ {
+ ro[2] = x2;
+ return 191;
+ }
+ goto ret0;
+
+ L986:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ goto L987;
+ }
+ goto ret0;
+
+ L987:
+ x2 = XEXP (x1, 1);
+ if (nonmemory_operand (x2, HImode))
+ {
+ ro[2] = x2;
+ return 197;
+ }
+ goto ret0;
+
+ L1001:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ goto L1002;
+ }
+ goto ret0;
+
+ L1002:
+ x2 = XEXP (x1, 1);
+ if (nonmemory_operand (x2, HImode))
+ {
+ ro[2] = x2;
+ return 200;
+ }
+ goto ret0;
+
+ L1016:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ goto L1017;
+ }
+ goto ret0;
+
+ L1017:
+ x2 = XEXP (x1, 1);
+ if (nonmemory_operand (x2, HImode))
+ {
+ ro[2] = x2;
+ return 203;
+ }
+ goto ret0;
+ ret0: return -1;
+}
+
+int
+recog_2 (x0, insn, pnum_clobbers)
+ register rtx x0;
+ rtx insn;
+ int *pnum_clobbers;
+{
+ register rtx *ro = &recog_operand[0];
+ register rtx x1, x2, x3, x4, x5, x6;
+ int tem;
+
+ x1 = XEXP (x0, 1);
+ switch (GET_MODE (x1))
+ {
+ case SImode:
+ if (nonimmediate_operand (x1, SImode))
+ {
+ ro[0] = x1;
+ return 0;
+ }
+ break;
+ case HImode:
+ if (nonimmediate_operand (x1, HImode))
+ {
+ ro[0] = x1;
+ return 2;
+ }
+ break;
+ case QImode:
+ if (nonimmediate_operand (x1, QImode))
+ {
+ ro[0] = x1;
+ return 4;
+ }
+ break;
+ case SFmode:
+ if (pnum_clobbers != 0 && register_operand (x1, SFmode))
+ {
+ ro[0] = x1;
+ if (TARGET_80387 && ! TARGET_IEEE_FP)
+ {
+ *pnum_clobbers = 1;
+ return 6;
+ }
+ }
+ break;
+ case DFmode:
+ if (pnum_clobbers != 0 && register_operand (x1, DFmode))
+ {
+ ro[0] = x1;
+ if (TARGET_80387 && ! TARGET_IEEE_FP)
+ {
+ *pnum_clobbers = 1;
+ return 8;
+ }
+ }
+ break;
+ case XFmode:
+ if (pnum_clobbers != 0 && register_operand (x1, XFmode))
+ {
+ ro[0] = x1;
+ if (TARGET_80387 && ! TARGET_IEEE_FP)
+ {
+ *pnum_clobbers = 1;
+ return 10;
+ }
+ }
+ }
+ switch (GET_CODE (x1))
+ {
+ case COMPARE:
+ goto L39;
+ case ZERO_EXTRACT:
+ goto L1046;
+ }
+ L61:
+ if (VOIDmode_compare_op (x1, VOIDmode))
+ {
+ ro[2] = x1;
+ goto L91;
+ }
+ L134:
+ switch (GET_MODE (x1))
+ {
+ case CCFPEQmode:
+ switch (GET_CODE (x1))
+ {
+ case COMPARE:
+ goto L135;
+ }
+ break;
+ case SImode:
+ switch (GET_CODE (x1))
+ {
+ case AND:
+ goto L282;
+ }
+ break;
+ case HImode:
+ switch (GET_CODE (x1))
+ {
+ case AND:
+ goto L287;
+ }
+ break;
+ case QImode:
+ if (GET_CODE (x1) == AND && 1)
+ goto L292;
+ }
+ goto ret0;
+
+ L39:
+ x2 = XEXP (x1, 0);
+ switch (GET_MODE (x2))
+ {
+ case SImode:
+ if (nonimmediate_operand (x2, SImode))
+ {
+ ro[0] = x2;
+ goto L40;
+ }
+ break;
+ case HImode:
+ if (nonimmediate_operand (x2, HImode))
+ {
+ ro[0] = x2;
+ goto L45;
+ }
+ break;
+ case QImode:
+ if (nonimmediate_operand (x2, QImode))
+ {
+ ro[0] = x2;
+ goto L50;
+ }
+ }
+ goto L61;
+
+ L40:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ if (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)
+ return 12;
+ }
+ goto L61;
+
+ L45:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ if (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)
+ return 14;
+ }
+ goto L61;
+
+ L50:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, QImode))
+ {
+ ro[1] = x2;
+ if (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)
+ return 16;
+ }
+ goto L61;
+
+ L1046:
+ x2 = XEXP (x1, 0);
+ switch (GET_MODE (x2))
+ {
+ case SImode:
+ if (register_operand (x2, SImode))
+ {
+ ro[0] = x2;
+ goto L1047;
+ }
+ break;
+ case QImode:
+ if (general_operand (x2, QImode))
+ {
+ ro[0] = x2;
+ goto L1059;
+ }
+ }
+ goto L61;
+
+ L1047:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) != CONST_INT)
+ {
+ goto L61;
+ }
+ if (XWINT (x2, 0) == 1 && 1)
+ goto L1048;
+ L1053:
+ ro[1] = x2;
+ goto L1054;
+
+ L1048:
+ x2 = XEXP (x1, 2);
+ if (general_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ if (GET_CODE (operands[1]) != CONST_INT)
+ return 208;
+ }
+ x2 = XEXP (x1, 1);
+ goto L1053;
+
+ L1054:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == CONST_INT && 1)
+ {
+ ro[2] = x2;
+ return 209;
+ }
+ goto L61;
+
+ L1059:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CONST_INT && 1)
+ {
+ ro[1] = x2;
+ goto L1060;
+ }
+ goto L61;
+
+ L1060:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == CONST_INT && 1)
+ {
+ ro[2] = x2;
+ if (GET_CODE (operands[0]) != MEM || ! MEM_VOLATILE_P (operands[0]))
+ return 210;
+ }
+ goto L61;
+
+ L91:
+ x2 = XEXP (x1, 0);
+ switch (GET_MODE (x2))
+ {
+ case XFmode:
+ if (GET_CODE (x2) == FLOAT && 1)
+ goto L92;
+ if (nonimmediate_operand (x2, XFmode))
+ {
+ ro[0] = x2;
+ goto L63;
+ }
+ L76:
+ if (register_operand (x2, XFmode))
+ {
+ ro[0] = x2;
+ goto L77;
+ }
+ break;
+ case DFmode:
+ switch (GET_CODE (x2))
+ {
+ case FLOAT:
+ goto L178;
+ case FLOAT_EXTEND:
+ goto L208;
+ case SUBREG:
+ case REG:
+ case MEM:
+ if (nonimmediate_operand (x2, DFmode))
+ {
+ ro[0] = x2;
+ goto L149;
+ }
+ }
+ L162:
+ if (register_operand (x2, DFmode))
+ {
+ ro[0] = x2;
+ goto L163;
+ }
+ break;
+ case SFmode:
+ if (GET_CODE (x2) == FLOAT && 1)
+ goto L264;
+ if (nonimmediate_operand (x2, SFmode))
+ {
+ ro[0] = x2;
+ goto L235;
+ }
+ L248:
+ if (register_operand (x2, SFmode))
+ {
+ ro[0] = x2;
+ goto L249;
+ }
+ }
+ goto L134;
+
+ L92:
+ x3 = XEXP (x2, 0);
+ if (nonimmediate_operand (x3, SImode))
+ {
+ ro[0] = x3;
+ goto L93;
+ }
+ goto L134;
+
+ L93:
+ x2 = XEXP (x1, 1);
+ if (pnum_clobbers != 0 && register_operand (x2, XFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ {
+ *pnum_clobbers = 1;
+ return 20;
+ }
+ }
+ goto L134;
+
+ L63:
+ x2 = XEXP (x1, 1);
+ if (pnum_clobbers != 0 && nonimmediate_operand (x2, XFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387
+ && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM))
+ {
+ *pnum_clobbers = 1;
+ return 18;
+ }
+ }
+ x2 = XEXP (x1, 0);
+ goto L76;
+
+ L77:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) != XFmode)
+ {
+ goto L134;
+ }
+ switch (GET_CODE (x2))
+ {
+ case FLOAT:
+ goto L78;
+ case FLOAT_EXTEND:
+ goto L108;
+ }
+ goto L134;
+
+ L78:
+ x3 = XEXP (x2, 0);
+ if (pnum_clobbers != 0 && nonimmediate_operand (x3, SImode))
+ {
+ ro[1] = x3;
+ if (TARGET_80387)
+ {
+ *pnum_clobbers = 1;
+ return 19;
+ }
+ }
+ goto L134;
+
+ L108:
+ x3 = XEXP (x2, 0);
+ switch (GET_MODE (x3))
+ {
+ case DFmode:
+ if (pnum_clobbers != 0 && nonimmediate_operand (x3, DFmode))
+ {
+ ro[1] = x3;
+ if (TARGET_80387)
+ {
+ *pnum_clobbers = 1;
+ return 21;
+ }
+ }
+ break;
+ case SFmode:
+ if (pnum_clobbers != 0 && nonimmediate_operand (x3, SFmode))
+ {
+ ro[1] = x3;
+ if (TARGET_80387)
+ {
+ *pnum_clobbers = 1;
+ return 22;
+ }
+ }
+ }
+ goto L134;
+
+ L178:
+ x3 = XEXP (x2, 0);
+ if (nonimmediate_operand (x3, SImode))
+ {
+ ro[0] = x3;
+ goto L179;
+ }
+ goto L134;
+
+ L179:
+ x2 = XEXP (x1, 1);
+ if (pnum_clobbers != 0 && register_operand (x2, DFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ {
+ *pnum_clobbers = 1;
+ return 26;
+ }
+ }
+ goto L134;
+
+ L208:
+ x3 = XEXP (x2, 0);
+ if (nonimmediate_operand (x3, SFmode))
+ {
+ ro[0] = x3;
+ goto L209;
+ }
+ goto L134;
+
+ L209:
+ x2 = XEXP (x1, 1);
+ if (pnum_clobbers != 0 && register_operand (x2, DFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ {
+ *pnum_clobbers = 1;
+ return 28;
+ }
+ }
+ goto L134;
+
+ L149:
+ x2 = XEXP (x1, 1);
+ if (pnum_clobbers != 0 && nonimmediate_operand (x2, DFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387
+ && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM))
+ {
+ *pnum_clobbers = 1;
+ return 24;
+ }
+ }
+ x2 = XEXP (x1, 0);
+ goto L162;
+
+ L163:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) != DFmode)
+ {
+ goto L134;
+ }
+ switch (GET_CODE (x2))
+ {
+ case FLOAT:
+ goto L164;
+ case FLOAT_EXTEND:
+ goto L194;
+ }
+ goto L134;
+
+ L164:
+ x3 = XEXP (x2, 0);
+ if (pnum_clobbers != 0 && nonimmediate_operand (x3, SImode))
+ {
+ ro[1] = x3;
+ if (TARGET_80387)
+ {
+ *pnum_clobbers = 1;
+ return 25;
+ }
+ }
+ goto L134;
+
+ L194:
+ x3 = XEXP (x2, 0);
+ if (pnum_clobbers != 0 && nonimmediate_operand (x3, SFmode))
+ {
+ ro[1] = x3;
+ if (TARGET_80387)
+ {
+ *pnum_clobbers = 1;
+ return 27;
+ }
+ }
+ goto L134;
+
+ L264:
+ x3 = XEXP (x2, 0);
+ if (nonimmediate_operand (x3, SImode))
+ {
+ ro[0] = x3;
+ goto L265;
+ }
+ goto L134;
+
+ L265:
+ x2 = XEXP (x1, 1);
+ if (pnum_clobbers != 0 && register_operand (x2, SFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ {
+ *pnum_clobbers = 1;
+ return 32;
+ }
+ }
+ goto L134;
+
+ L235:
+ x2 = XEXP (x1, 1);
+ if (pnum_clobbers != 0 && nonimmediate_operand (x2, SFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387
+ && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM))
+ {
+ *pnum_clobbers = 1;
+ return 30;
+ }
+ }
+ x2 = XEXP (x1, 0);
+ goto L248;
+
+ L249:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == SFmode && GET_CODE (x2) == FLOAT && 1)
+ goto L250;
+ goto L134;
+
+ L250:
+ x3 = XEXP (x2, 0);
+ if (pnum_clobbers != 0 && nonimmediate_operand (x3, SImode))
+ {
+ ro[1] = x3;
+ if (TARGET_80387)
+ {
+ *pnum_clobbers = 1;
+ return 31;
+ }
+ }
+ goto L134;
+
+ L135:
+ x2 = XEXP (x1, 0);
+ switch (GET_MODE (x2))
+ {
+ case XFmode:
+ if (register_operand (x2, XFmode))
+ {
+ ro[0] = x2;
+ goto L136;
+ }
+ break;
+ case DFmode:
+ if (register_operand (x2, DFmode))
+ {
+ ro[0] = x2;
+ goto L222;
+ }
+ break;
+ case SFmode:
+ if (register_operand (x2, SFmode))
+ {
+ ro[0] = x2;
+ goto L278;
+ }
+ }
+ goto ret0;
+
+ L136:
+ x2 = XEXP (x1, 1);
+ if (pnum_clobbers != 0 && register_operand (x2, XFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ {
+ *pnum_clobbers = 1;
+ return 23;
+ }
+ }
+ goto ret0;
+
+ L222:
+ x2 = XEXP (x1, 1);
+ if (pnum_clobbers != 0 && register_operand (x2, DFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ {
+ *pnum_clobbers = 1;
+ return 29;
+ }
+ }
+ goto ret0;
+
+ L278:
+ x2 = XEXP (x1, 1);
+ if (pnum_clobbers != 0 && register_operand (x2, SFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ {
+ *pnum_clobbers = 1;
+ return 33;
+ }
+ }
+ goto ret0;
+
+ L282:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, SImode))
+ {
+ ro[0] = x2;
+ goto L283;
+ }
+ goto ret0;
+
+ L283:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ return 43;
+ }
+ goto ret0;
+
+ L287:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, HImode))
+ {
+ ro[0] = x2;
+ goto L288;
+ }
+ goto ret0;
+
+ L288:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ return 44;
+ }
+ goto ret0;
+
+ L292:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, QImode))
+ {
+ ro[0] = x2;
+ goto L293;
+ }
+ goto ret0;
+
+ L293:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, QImode))
+ {
+ ro[1] = x2;
+ return 45;
+ }
+ goto ret0;
+ ret0: return -1;
+}
+
+int
+recog_3 (x0, insn, pnum_clobbers)
+ register rtx x0;
+ rtx insn;
+ int *pnum_clobbers;
+{
+ register rtx *ro = &recog_operand[0];
+ register rtx x1, x2, x3, x4, x5, x6;
+ int tem;
+
+ x1 = XEXP (x0, 1);
+ x2 = XEXP (x1, 0);
+ switch (GET_CODE (x2))
+ {
+ case EQ:
+ goto L1115;
+ case NE:
+ goto L1124;
+ case GT:
+ goto L1133;
+ case GTU:
+ goto L1142;
+ case LT:
+ goto L1151;
+ case LTU:
+ goto L1160;
+ case GE:
+ goto L1169;
+ case GEU:
+ goto L1178;
+ case LE:
+ goto L1187;
+ case LEU:
+ goto L1196;
+ }
+ goto ret0;
+
+ L1115:
+ x3 = XEXP (x2, 0);
+ if (GET_CODE (x3) == CC0 && 1)
+ goto L1116;
+ goto ret0;
+
+ L1116:
+ x3 = XEXP (x2, 1);
+ if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1)
+ goto L1117;
+ goto ret0;
+
+ L1117:
+ x2 = XEXP (x1, 1);
+ switch (GET_CODE (x2))
+ {
+ case LABEL_REF:
+ goto L1118;
+ case PC:
+ goto L1208;
+ }
+ goto ret0;
+
+ L1118:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ goto L1119;
+
+ L1119:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == PC && 1)
+ return 232;
+ goto ret0;
+
+ L1208:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == LABEL_REF && 1)
+ goto L1209;
+ goto ret0;
+
+ L1209:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ return 251;
+
+ L1124:
+ x3 = XEXP (x2, 0);
+ if (GET_CODE (x3) == CC0 && 1)
+ goto L1125;
+ goto ret0;
+
+ L1125:
+ x3 = XEXP (x2, 1);
+ if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1)
+ goto L1126;
+ goto ret0;
+
+ L1126:
+ x2 = XEXP (x1, 1);
+ switch (GET_CODE (x2))
+ {
+ case LABEL_REF:
+ goto L1127;
+ case PC:
+ goto L1217;
+ }
+ goto ret0;
+
+ L1127:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ goto L1128;
+
+ L1128:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == PC && 1)
+ return 234;
+ goto ret0;
+
+ L1217:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == LABEL_REF && 1)
+ goto L1218;
+ goto ret0;
+
+ L1218:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ return 252;
+
+ L1133:
+ x3 = XEXP (x2, 0);
+ if (GET_CODE (x3) == CC0 && 1)
+ goto L1134;
+ goto ret0;
+
+ L1134:
+ x3 = XEXP (x2, 1);
+ if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1)
+ goto L1135;
+ goto ret0;
+
+ L1135:
+ x2 = XEXP (x1, 1);
+ switch (GET_CODE (x2))
+ {
+ case LABEL_REF:
+ goto L1136;
+ case PC:
+ goto L1226;
+ }
+ goto ret0;
+
+ L1136:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ goto L1137;
+
+ L1137:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == PC && 1)
+ return 236;
+ goto ret0;
+
+ L1226:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == LABEL_REF && 1)
+ goto L1227;
+ goto ret0;
+
+ L1227:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ return 253;
+
+ L1142:
+ x3 = XEXP (x2, 0);
+ if (GET_CODE (x3) == CC0 && 1)
+ goto L1143;
+ goto ret0;
+
+ L1143:
+ x3 = XEXP (x2, 1);
+ if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1)
+ goto L1144;
+ goto ret0;
+
+ L1144:
+ x2 = XEXP (x1, 1);
+ switch (GET_CODE (x2))
+ {
+ case LABEL_REF:
+ goto L1145;
+ case PC:
+ goto L1235;
+ }
+ goto ret0;
+
+ L1145:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ goto L1146;
+
+ L1146:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == PC && 1)
+ return 238;
+ goto ret0;
+
+ L1235:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == LABEL_REF && 1)
+ goto L1236;
+ goto ret0;
+
+ L1236:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ return 254;
+
+ L1151:
+ x3 = XEXP (x2, 0);
+ if (GET_CODE (x3) == CC0 && 1)
+ goto L1152;
+ goto ret0;
+
+ L1152:
+ x3 = XEXP (x2, 1);
+ if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1)
+ goto L1153;
+ goto ret0;
+
+ L1153:
+ x2 = XEXP (x1, 1);
+ switch (GET_CODE (x2))
+ {
+ case LABEL_REF:
+ goto L1154;
+ case PC:
+ goto L1244;
+ }
+ goto ret0;
+
+ L1154:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ goto L1155;
+
+ L1155:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == PC && 1)
+ return 240;
+ goto ret0;
+
+ L1244:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == LABEL_REF && 1)
+ goto L1245;
+ goto ret0;
+
+ L1245:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ return 255;
+
+ L1160:
+ x3 = XEXP (x2, 0);
+ if (GET_CODE (x3) == CC0 && 1)
+ goto L1161;
+ goto ret0;
+
+ L1161:
+ x3 = XEXP (x2, 1);
+ if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1)
+ goto L1162;
+ goto ret0;
+
+ L1162:
+ x2 = XEXP (x1, 1);
+ switch (GET_CODE (x2))
+ {
+ case LABEL_REF:
+ goto L1163;
+ case PC:
+ goto L1253;
+ }
+ goto ret0;
+
+ L1163:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ goto L1164;
+
+ L1164:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == PC && 1)
+ return 242;
+ goto ret0;
+
+ L1253:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == LABEL_REF && 1)
+ goto L1254;
+ goto ret0;
+
+ L1254:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ return 256;
+
+ L1169:
+ x3 = XEXP (x2, 0);
+ if (GET_CODE (x3) == CC0 && 1)
+ goto L1170;
+ goto ret0;
+
+ L1170:
+ x3 = XEXP (x2, 1);
+ if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1)
+ goto L1171;
+ goto ret0;
+
+ L1171:
+ x2 = XEXP (x1, 1);
+ switch (GET_CODE (x2))
+ {
+ case LABEL_REF:
+ goto L1172;
+ case PC:
+ goto L1262;
+ }
+ goto ret0;
+
+ L1172:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ goto L1173;
+
+ L1173:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == PC && 1)
+ return 244;
+ goto ret0;
+
+ L1262:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == LABEL_REF && 1)
+ goto L1263;
+ goto ret0;
+
+ L1263:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ return 257;
+
+ L1178:
+ x3 = XEXP (x2, 0);
+ if (GET_CODE (x3) == CC0 && 1)
+ goto L1179;
+ goto ret0;
+
+ L1179:
+ x3 = XEXP (x2, 1);
+ if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1)
+ goto L1180;
+ goto ret0;
+
+ L1180:
+ x2 = XEXP (x1, 1);
+ switch (GET_CODE (x2))
+ {
+ case LABEL_REF:
+ goto L1181;
+ case PC:
+ goto L1271;
+ }
+ goto ret0;
+
+ L1181:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ goto L1182;
+
+ L1182:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == PC && 1)
+ return 246;
+ goto ret0;
+
+ L1271:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == LABEL_REF && 1)
+ goto L1272;
+ goto ret0;
+
+ L1272:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ return 258;
+
+ L1187:
+ x3 = XEXP (x2, 0);
+ if (GET_CODE (x3) == CC0 && 1)
+ goto L1188;
+ goto ret0;
+
+ L1188:
+ x3 = XEXP (x2, 1);
+ if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1)
+ goto L1189;
+ goto ret0;
+
+ L1189:
+ x2 = XEXP (x1, 1);
+ switch (GET_CODE (x2))
+ {
+ case LABEL_REF:
+ goto L1190;
+ case PC:
+ goto L1280;
+ }
+ goto ret0;
+
+ L1190:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ goto L1191;
+
+ L1191:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == PC && 1)
+ return 248;
+ goto ret0;
+
+ L1280:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == LABEL_REF && 1)
+ goto L1281;
+ goto ret0;
+
+ L1281:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ return 259;
+
+ L1196:
+ x3 = XEXP (x2, 0);
+ if (GET_CODE (x3) == CC0 && 1)
+ goto L1197;
+ goto ret0;
+
+ L1197:
+ x3 = XEXP (x2, 1);
+ if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 0 && 1)
+ goto L1198;
+ goto ret0;
+
+ L1198:
+ x2 = XEXP (x1, 1);
+ switch (GET_CODE (x2))
+ {
+ case LABEL_REF:
+ goto L1199;
+ case PC:
+ goto L1289;
+ }
+ goto ret0;
+
+ L1199:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ goto L1200;
+
+ L1200:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == PC && 1)
+ return 250;
+ goto ret0;
+
+ L1289:
+ x2 = XEXP (x1, 2);
+ if (GET_CODE (x2) == LABEL_REF && 1)
+ goto L1290;
+ goto ret0;
+
+ L1290:
+ x3 = XEXP (x2, 0);
+ ro[0] = x3;
+ return 260;
+ ret0: return -1;
+}
+
+int
+recog_4 (x0, insn, pnum_clobbers)
+ register rtx x0;
+ rtx insn;
+ int *pnum_clobbers;
+{
+ register rtx *ro = &recog_operand[0];
+ register rtx x1, x2, x3, x4, x5, x6;
+ int tem;
+
+ x1 = XEXP (x0, 0);
+ switch (GET_MODE (x1))
+ {
+ case SImode:
+ switch (GET_CODE (x1))
+ {
+ case MEM:
+ if (push_operand (x1, SImode))
+ {
+ ro[0] = x1;
+ goto L296;
+ }
+ break;
+ case ZERO_EXTRACT:
+ goto L1025;
+ }
+ L303:
+ if (general_operand (x1, SImode))
+ {
+ ro[0] = x1;
+ goto L365;
+ }
+ L611:
+ if (register_operand (x1, SImode))
+ {
+ ro[0] = x1;
+ goto L612;
+ }
+ L619:
+ if (general_operand (x1, SImode))
+ {
+ ro[0] = x1;
+ goto L620;
+ }
+ break;
+ case HImode:
+ if (GET_CODE (x1) == MEM && push_operand (x1, HImode))
+ {
+ ro[0] = x1;
+ goto L307;
+ }
+ L309:
+ if (general_operand (x1, HImode))
+ {
+ ro[0] = x1;
+ goto L369;
+ }
+ break;
+ case QImode:
+ if (GET_CODE (x1) == MEM && push_operand (x1, QImode))
+ {
+ ro[0] = x1;
+ goto L317;
+ }
+ L319:
+ if (general_operand (x1, QImode))
+ {
+ ro[0] = x1;
+ goto L607;
+ }
+ L1062:
+ if (register_operand (x1, QImode))
+ {
+ ro[0] = x1;
+ goto L1063;
+ }
+ break;
+ case SFmode:
+ if (GET_CODE (x1) == MEM && push_operand (x1, SFmode))
+ {
+ ro[0] = x1;
+ goto L327;
+ }
+ L329:
+ if (general_operand (x1, SFmode))
+ {
+ ro[0] = x1;
+ goto L416;
+ }
+ L575:
+ if (register_operand (x1, SFmode))
+ {
+ ro[0] = x1;
+ goto L576;
+ }
+ break;
+ case DFmode:
+ if (GET_CODE (x1) == MEM && push_operand (x1, DFmode))
+ {
+ ro[0] = x1;
+ goto L333;
+ }
+ L342:
+ if (general_operand (x1, DFmode))
+ {
+ ro[0] = x1;
+ goto L397;
+ }
+ L571:
+ if (register_operand (x1, DFmode))
+ {
+ ro[0] = x1;
+ goto L572;
+ }
+ break;
+ case XFmode:
+ if (GET_CODE (x1) == MEM && push_operand (x1, XFmode))
+ {
+ ro[0] = x1;
+ goto L346;
+ }
+ L355:
+ if (general_operand (x1, XFmode))
+ {
+ ro[0] = x1;
+ goto L401;
+ }
+ L567:
+ if (register_operand (x1, XFmode))
+ {
+ ro[0] = x1;
+ goto L568;
+ }
+ break;
+ case DImode:
+ if (GET_CODE (x1) == MEM && push_operand (x1, DImode))
+ {
+ ro[0] = x1;
+ goto L359;
+ }
+ L361:
+ if (general_operand (x1, DImode))
+ {
+ ro[0] = x1;
+ goto L592;
+ }
+ L376:
+ if (register_operand (x1, DImode))
+ {
+ ro[0] = x1;
+ goto L377;
+ }
+ }
+ switch (GET_CODE (x1))
+ {
+ case CC0:
+ goto L2;
+ case STRICT_LOW_PART:
+ goto L313;
+ case PC:
+ goto L1314;
+ }
+ L1380:
+ ro[0] = x1;
+ goto L1381;
+ L1460:
+ switch (GET_MODE (x1))
+ {
+ case SImode:
+ if (general_operand (x1, SImode))
+ {
+ ro[0] = x1;
+ goto L1461;
+ }
+ break;
+ case HImode:
+ if (general_operand (x1, HImode))
+ {
+ ro[0] = x1;
+ goto L1467;
+ }
+ break;
+ case DFmode:
+ if (register_operand (x1, DFmode))
+ {
+ ro[0] = x1;
+ goto L1473;
+ }
+ break;
+ case XFmode:
+ if (register_operand (x1, XFmode))
+ {
+ ro[0] = x1;
+ goto L1484;
+ }
+ break;
+ case SFmode:
+ if (register_operand (x1, SFmode))
+ {
+ ro[0] = x1;
+ goto L1531;
+ }
+ }
+ goto ret0;
+
+ L296:
+ x1 = XEXP (x0, 1);
+ if (general_operand (x1, SImode))
+ goto L300;
+ x1 = XEXP (x0, 0);
+ goto L303;
+
+ L300:
+ ro[1] = x1;
+ if (! TARGET_486)
+ return 46;
+ L301:
+ ro[1] = x1;
+ if (TARGET_486)
+ return 47;
+ x1 = XEXP (x0, 0);
+ goto L303;
+
+ L1025:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) == SImode && general_operand (x2, SImode))
+ {
+ ro[0] = x2;
+ goto L1026;
+ }
+ goto L1380;
+
+ L1026:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 1 && 1)
+ goto L1027;
+ goto L1380;
+
+ L1027:
+ x2 = XEXP (x1, 2);
+ if (general_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ goto L1028;
+ }
+ goto L1380;
+
+ L1028:
+ x1 = XEXP (x0, 1);
+ if (GET_CODE (x1) == CONST_INT && 1)
+ {
+ ro[3] = x1;
+ if (! TARGET_486 && GET_CODE (operands[2]) != CONST_INT)
+ return 205;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L365:
+ x1 = XEXP (x0, 1);
+ switch (GET_MODE (x1))
+ {
+ case SImode:
+ switch (GET_CODE (x1))
+ {
+ case ZERO_EXTEND:
+ goto L366;
+ case SIGN_EXTEND:
+ goto L386;
+ case PLUS:
+ goto L598;
+ }
+ }
+ if (general_operand (x1, SImode))
+ {
+ ro[1] = x1;
+ return 49;
+ }
+ x1 = XEXP (x0, 0);
+ goto L611;
+
+ L366:
+ x2 = XEXP (x1, 0);
+ switch (GET_MODE (x2))
+ {
+ case HImode:
+ if (nonimmediate_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ return 66;
+ }
+ break;
+ case QImode:
+ if (nonimmediate_operand (x2, QImode))
+ {
+ ro[1] = x2;
+ return 68;
+ }
+ }
+ x1 = XEXP (x0, 0);
+ goto L611;
+
+ L386:
+ x2 = XEXP (x1, 0);
+ switch (GET_MODE (x2))
+ {
+ case HImode:
+ if (nonimmediate_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ return 71;
+ }
+ break;
+ case QImode:
+ if (nonimmediate_operand (x2, QImode))
+ {
+ ro[1] = x2;
+ return 73;
+ }
+ }
+ x1 = XEXP (x0, 0);
+ goto L611;
+
+ L598:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ goto L599;
+ }
+ x1 = XEXP (x0, 0);
+ goto L611;
+
+ L599:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ return 109;
+ }
+ x1 = XEXP (x0, 0);
+ goto L611;
+
+ L612:
+ x1 = XEXP (x0, 1);
+ if (address_operand (x1, QImode))
+ {
+ ro[1] = x1;
+ return 112;
+ }
+ x1 = XEXP (x0, 0);
+ goto L619;
+
+ L620:
+ x1 = XEXP (x0, 1);
+ if (GET_MODE (x1) != SImode)
+ {
+ x1 = XEXP (x0, 0);
+ goto L1380;
+ }
+ switch (GET_CODE (x1))
+ {
+ case MINUS:
+ goto L621;
+ case MULT:
+ goto L648;
+ case AND:
+ goto L742;
+ case IOR:
+ goto L757;
+ case XOR:
+ goto L1032;
+ case NEG:
+ goto L791;
+ case NOT:
+ goto L900;
+ case ASHIFT:
+ goto L925;
+ case ASHIFTRT:
+ goto L953;
+ case LSHIFTRT:
+ goto L981;
+ case ROTATE:
+ goto L996;
+ case ROTATERT:
+ goto L1011;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L621:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ goto L622;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L622:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ return 117;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L648:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ goto L649;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L649:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, SImode))
+ goto L655;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L655:
+ ro[2] = x2;
+ if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x80)
+ return 125;
+ L656:
+ ro[2] = x2;
+ return 126;
+
+ L742:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ goto L743;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L743:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ return 143;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L757:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ goto L758;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L758:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ return 146;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1032:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == ASHIFT && 1)
+ goto L1033;
+ if (general_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ goto L1040;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1033:
+ x3 = XEXP (x2, 0);
+ if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 1 && 1)
+ goto L1034;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1034:
+ x3 = XEXP (x2, 1);
+ if (general_operand (x3, SImode))
+ {
+ ro[1] = x3;
+ goto L1035;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1035:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ if (! TARGET_486 && GET_CODE (operands[1]) != CONST_INT)
+ return 206;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1040:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == ASHIFT && 1)
+ goto L1041;
+ if (general_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ return 149;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1041:
+ x3 = XEXP (x2, 0);
+ if (GET_CODE (x3) == CONST_INT && XWINT (x3, 0) == 1 && 1)
+ goto L1042;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1042:
+ x3 = XEXP (x2, 1);
+ if (general_operand (x3, SImode))
+ {
+ ro[2] = x3;
+ if (! TARGET_486 && GET_CODE (operands[2]) != CONST_INT)
+ return 207;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L791:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ return 153;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L900:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ return 178;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L925:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ goto L926;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L926:
+ x2 = XEXP (x1, 1);
+ if (nonmemory_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ return 184;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L953:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ goto L954;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L954:
+ x2 = XEXP (x1, 1);
+ if (nonmemory_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ return 190;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L981:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ goto L982;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L982:
+ x2 = XEXP (x1, 1);
+ if (nonmemory_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ return 196;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L996:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ goto L997;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L997:
+ x2 = XEXP (x1, 1);
+ if (nonmemory_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ return 199;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1011:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ goto L1012;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1012:
+ x2 = XEXP (x1, 1);
+ if (nonmemory_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ return 202;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L307:
+ x1 = XEXP (x0, 1);
+ if (general_operand (x1, HImode))
+ {
+ ro[1] = x1;
+ return 50;
+ }
+ x1 = XEXP (x0, 0);
+ goto L309;
+ L369:
+ tem = recog_1 (x0, insn, pnum_clobbers);
+ if (tem >= 0) return tem;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L317:
+ x1 = XEXP (x0, 1);
+ if (general_operand (x1, QImode))
+ {
+ ro[1] = x1;
+ return 53;
+ }
+ x1 = XEXP (x0, 0);
+ goto L319;
+
+ L607:
+ x1 = XEXP (x0, 1);
+ switch (GET_MODE (x1))
+ {
+ case QImode:
+ switch (GET_CODE (x1))
+ {
+ case PLUS:
+ goto L608;
+ case MINUS:
+ goto L631;
+ case DIV:
+ goto L688;
+ case UDIV:
+ goto L693;
+ case AND:
+ goto L752;
+ case IOR:
+ goto L767;
+ case XOR:
+ goto L782;
+ case NEG:
+ goto L799;
+ case NOT:
+ goto L908;
+ case ASHIFT:
+ goto L935;
+ case ASHIFTRT:
+ goto L963;
+ case LSHIFTRT:
+ goto L991;
+ case ROTATE:
+ goto L1006;
+ case ROTATERT:
+ goto L1021;
+ }
+ }
+ if (general_operand (x1, QImode))
+ {
+ ro[1] = x1;
+ return 54;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L608:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, QImode))
+ {
+ ro[1] = x2;
+ goto L609;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L609:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, QImode))
+ {
+ ro[2] = x2;
+ return 111;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L631:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, QImode))
+ {
+ ro[1] = x2;
+ goto L632;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L632:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, QImode))
+ {
+ ro[2] = x2;
+ return 119;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L688:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ goto L689;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L689:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, QImode))
+ {
+ ro[2] = x2;
+ return 134;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L693:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ goto L694;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L694:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, QImode))
+ {
+ ro[2] = x2;
+ return 135;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L752:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, QImode))
+ {
+ ro[1] = x2;
+ goto L753;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L753:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, QImode))
+ {
+ ro[2] = x2;
+ return 145;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L767:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, QImode))
+ {
+ ro[1] = x2;
+ goto L768;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L768:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, QImode))
+ {
+ ro[2] = x2;
+ return 148;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L782:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, QImode))
+ {
+ ro[1] = x2;
+ goto L783;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L783:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, QImode))
+ {
+ ro[2] = x2;
+ return 151;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L799:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, QImode))
+ {
+ ro[1] = x2;
+ return 155;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L908:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, QImode))
+ {
+ ro[1] = x2;
+ return 180;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L935:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, QImode))
+ {
+ ro[1] = x2;
+ goto L936;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L936:
+ x2 = XEXP (x1, 1);
+ if (nonmemory_operand (x2, QImode))
+ {
+ ro[2] = x2;
+ return 186;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L963:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, QImode))
+ {
+ ro[1] = x2;
+ goto L964;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L964:
+ x2 = XEXP (x1, 1);
+ if (nonmemory_operand (x2, QImode))
+ {
+ ro[2] = x2;
+ return 192;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L991:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, QImode))
+ {
+ ro[1] = x2;
+ goto L992;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L992:
+ x2 = XEXP (x1, 1);
+ if (nonmemory_operand (x2, QImode))
+ {
+ ro[2] = x2;
+ return 198;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L1006:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, QImode))
+ {
+ ro[1] = x2;
+ goto L1007;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L1007:
+ x2 = XEXP (x1, 1);
+ if (nonmemory_operand (x2, QImode))
+ {
+ ro[2] = x2;
+ return 201;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L1021:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, QImode))
+ {
+ ro[1] = x2;
+ goto L1022;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L1022:
+ x2 = XEXP (x1, 1);
+ if (nonmemory_operand (x2, QImode))
+ {
+ ro[2] = x2;
+ return 204;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1062;
+
+ L1063:
+ x1 = XEXP (x0, 1);
+ if (GET_MODE (x1) != QImode)
+ {
+ x1 = XEXP (x0, 0);
+ goto L1380;
+ }
+ switch (GET_CODE (x1))
+ {
+ case EQ:
+ goto L1064;
+ case NE:
+ goto L1069;
+ case GT:
+ goto L1074;
+ case GTU:
+ goto L1079;
+ case LT:
+ goto L1084;
+ case LTU:
+ goto L1089;
+ case GE:
+ goto L1094;
+ case GEU:
+ goto L1099;
+ case LE:
+ goto L1104;
+ case LEU:
+ goto L1109;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1064:
+ x2 = XEXP (x1, 0);
+ if (GET_CODE (x2) == CC0 && 1)
+ goto L1065;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1065:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1)
+ return 212;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1069:
+ x2 = XEXP (x1, 0);
+ if (GET_CODE (x2) == CC0 && 1)
+ goto L1070;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1070:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1)
+ return 214;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1074:
+ x2 = XEXP (x1, 0);
+ if (GET_CODE (x2) == CC0 && 1)
+ goto L1075;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1075:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1)
+ return 216;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1079:
+ x2 = XEXP (x1, 0);
+ if (GET_CODE (x2) == CC0 && 1)
+ goto L1080;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1080:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1)
+ return 218;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1084:
+ x2 = XEXP (x1, 0);
+ if (GET_CODE (x2) == CC0 && 1)
+ goto L1085;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1085:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1)
+ return 220;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1089:
+ x2 = XEXP (x1, 0);
+ if (GET_CODE (x2) == CC0 && 1)
+ goto L1090;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1090:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1)
+ return 222;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1094:
+ x2 = XEXP (x1, 0);
+ if (GET_CODE (x2) == CC0 && 1)
+ goto L1095;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1095:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1)
+ return 224;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1099:
+ x2 = XEXP (x1, 0);
+ if (GET_CODE (x2) == CC0 && 1)
+ goto L1100;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1100:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1)
+ return 226;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1104:
+ x2 = XEXP (x1, 0);
+ if (GET_CODE (x2) == CC0 && 1)
+ goto L1105;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1105:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1)
+ return 228;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1109:
+ x2 = XEXP (x1, 0);
+ if (GET_CODE (x2) == CC0 && 1)
+ goto L1110;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1110:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1)
+ return 230;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L327:
+ x1 = XEXP (x0, 1);
+ if (general_operand (x1, SFmode))
+ {
+ ro[1] = x1;
+ return 56;
+ }
+ x1 = XEXP (x0, 0);
+ goto L329;
+
+ L416:
+ x1 = XEXP (x0, 1);
+ if (GET_MODE (x1) == SFmode && GET_CODE (x1) == FLOAT_TRUNCATE && 1)
+ goto L417;
+ if (general_operand (x1, SFmode))
+ {
+ ro[1] = x1;
+ return 57;
+ }
+ x1 = XEXP (x0, 0);
+ goto L575;
+
+ L417:
+ x2 = XEXP (x1, 0);
+ if (register_operand (x2, XFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ return 79;
+ }
+ x1 = XEXP (x0, 0);
+ goto L575;
+
+ L576:
+ x1 = XEXP (x0, 1);
+ if (GET_MODE (x1) != SFmode)
+ {
+ x1 = XEXP (x0, 0);
+ goto L1380;
+ }
+ switch (GET_CODE (x1))
+ {
+ case FLOAT:
+ goto L577;
+ case NEG:
+ goto L803;
+ case ABS:
+ goto L825;
+ case SQRT:
+ goto L847;
+ case UNSPEC:
+ if (XINT (x1, 1) == 1 && XVECLEN (x1, 0) == 1 && 1)
+ goto L878;
+ if (XINT (x1, 1) == 2 && XVECLEN (x1, 0) == 1 && 1)
+ goto L891;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L577:
+ x2 = XEXP (x1, 0);
+ switch (GET_MODE (x2))
+ {
+ case DImode:
+ if (nonimmediate_operand (x2, DImode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ return 104;
+ }
+ break;
+ case SImode:
+ if (nonimmediate_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ return 107;
+ }
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L803:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, SFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ return 156;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L825:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, SFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ return 161;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L847:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, SFmode))
+ {
+ ro[1] = x2;
+ if (! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) )
+ return 166;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L878:
+ x2 = XVECEXP (x1, 0, 0);
+ if (register_operand (x2, SFmode))
+ {
+ ro[1] = x2;
+ if (! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) )
+ return 173;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L891:
+ x2 = XVECEXP (x1, 0, 0);
+ if (register_operand (x2, SFmode))
+ {
+ ro[1] = x2;
+ if (! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) )
+ return 176;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L333:
+ x1 = XEXP (x0, 1);
+ if (general_operand (x1, DFmode))
+ {
+ ro[1] = x1;
+ return 58;
+ }
+ x1 = XEXP (x0, 0);
+ goto L342;
+
+ L397:
+ x1 = XEXP (x0, 1);
+ switch (GET_MODE (x1))
+ {
+ case DFmode:
+ switch (GET_CODE (x1))
+ {
+ case FLOAT_EXTEND:
+ goto L398;
+ case FLOAT_TRUNCATE:
+ goto L421;
+ }
+ }
+ if (general_operand (x1, DFmode))
+ {
+ ro[1] = x1;
+ return 60;
+ }
+ x1 = XEXP (x0, 0);
+ goto L571;
+
+ L398:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, SFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ return 74;
+ }
+ x1 = XEXP (x0, 0);
+ goto L571;
+
+ L421:
+ x2 = XEXP (x1, 0);
+ if (register_operand (x2, XFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ return 80;
+ }
+ x1 = XEXP (x0, 0);
+ goto L571;
+
+ L572:
+ x1 = XEXP (x0, 1);
+ if (GET_MODE (x1) != DFmode)
+ {
+ x1 = XEXP (x0, 0);
+ goto L1380;
+ }
+ switch (GET_CODE (x1))
+ {
+ case FLOAT:
+ goto L573;
+ case NEG:
+ goto L811;
+ case ABS:
+ goto L833;
+ case SQRT:
+ goto L855;
+ case UNSPEC:
+ if (XINT (x1, 1) == 1 && XVECLEN (x1, 0) == 1 && 1)
+ goto L882;
+ if (XINT (x1, 1) == 2 && XVECLEN (x1, 0) == 1 && 1)
+ goto L895;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L573:
+ x2 = XEXP (x1, 0);
+ switch (GET_MODE (x2))
+ {
+ case DImode:
+ if (nonimmediate_operand (x2, DImode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ return 103;
+ }
+ break;
+ case SImode:
+ if (nonimmediate_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ return 105;
+ }
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L811:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) == DFmode && GET_CODE (x2) == FLOAT_EXTEND && 1)
+ goto L812;
+ if (general_operand (x2, DFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ return 157;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L812:
+ x3 = XEXP (x2, 0);
+ if (general_operand (x3, SFmode))
+ {
+ ro[1] = x3;
+ if (TARGET_80387)
+ return 158;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L833:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) == DFmode && GET_CODE (x2) == FLOAT_EXTEND && 1)
+ goto L834;
+ if (general_operand (x2, DFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ return 162;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L834:
+ x3 = XEXP (x2, 0);
+ if (general_operand (x3, SFmode))
+ {
+ ro[1] = x3;
+ if (TARGET_80387)
+ return 163;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L855:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) == DFmode && GET_CODE (x2) == FLOAT_EXTEND && 1)
+ goto L856;
+ if (general_operand (x2, DFmode))
+ {
+ ro[1] = x2;
+ if (! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) )
+ return 167;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L856:
+ x3 = XEXP (x2, 0);
+ if (general_operand (x3, SFmode))
+ {
+ ro[1] = x3;
+ if (! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) )
+ return 168;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L882:
+ x2 = XVECEXP (x1, 0, 0);
+ if (GET_MODE (x2) != DFmode)
+ {
+ x1 = XEXP (x0, 0);
+ goto L1380;
+ }
+ if (GET_CODE (x2) == FLOAT_EXTEND && 1)
+ goto L883;
+ if (register_operand (x2, DFmode))
+ {
+ ro[1] = x2;
+ if (! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) )
+ return 172;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L883:
+ x3 = XEXP (x2, 0);
+ if (register_operand (x3, SFmode))
+ {
+ ro[1] = x3;
+ if (! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) )
+ return 174;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L895:
+ x2 = XVECEXP (x1, 0, 0);
+ if (GET_MODE (x2) != DFmode)
+ {
+ x1 = XEXP (x0, 0);
+ goto L1380;
+ }
+ if (GET_CODE (x2) == FLOAT_EXTEND && 1)
+ goto L896;
+ if (register_operand (x2, DFmode))
+ {
+ ro[1] = x2;
+ if (! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) )
+ return 175;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L896:
+ x3 = XEXP (x2, 0);
+ if (register_operand (x3, SFmode))
+ {
+ ro[1] = x3;
+ if (! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) )
+ return 177;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L346:
+ x1 = XEXP (x0, 1);
+ if (general_operand (x1, XFmode))
+ {
+ ro[1] = x1;
+ return 61;
+ }
+ x1 = XEXP (x0, 0);
+ goto L355;
+
+ L401:
+ x1 = XEXP (x0, 1);
+ if (GET_MODE (x1) == XFmode && GET_CODE (x1) == FLOAT_EXTEND && 1)
+ goto L402;
+ if (general_operand (x1, XFmode))
+ {
+ ro[1] = x1;
+ return 63;
+ }
+ x1 = XEXP (x0, 0);
+ goto L567;
+
+ L402:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, DFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ return 75;
+ }
+ L406:
+ if (general_operand (x2, SFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ return 76;
+ }
+ x1 = XEXP (x0, 0);
+ goto L567;
+
+ L568:
+ x1 = XEXP (x0, 1);
+ if (GET_MODE (x1) != XFmode)
+ {
+ x1 = XEXP (x0, 0);
+ goto L1380;
+ }
+ switch (GET_CODE (x1))
+ {
+ case FLOAT:
+ goto L569;
+ case NEG:
+ goto L820;
+ case ABS:
+ goto L842;
+ case SQRT:
+ goto L864;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L569:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, DImode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ return 102;
+ }
+ L585:
+ if (general_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ return 106;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L820:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) == XFmode && GET_CODE (x2) == FLOAT_EXTEND && 1)
+ goto L821;
+ if (general_operand (x2, XFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ return 159;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L821:
+ x3 = XEXP (x2, 0);
+ if (general_operand (x3, DFmode))
+ {
+ ro[1] = x3;
+ if (TARGET_80387)
+ return 160;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L842:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) == XFmode && GET_CODE (x2) == FLOAT_EXTEND && 1)
+ goto L843;
+ if (general_operand (x2, XFmode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387)
+ return 164;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L843:
+ x3 = XEXP (x2, 0);
+ if (general_operand (x3, DFmode))
+ {
+ ro[1] = x3;
+ if (TARGET_80387)
+ return 165;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L864:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) == XFmode && GET_CODE (x2) == FLOAT_EXTEND && 1)
+ goto L865;
+ if (general_operand (x2, XFmode))
+ {
+ ro[1] = x2;
+ if (! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) )
+ return 169;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L865:
+ x3 = XEXP (x2, 0);
+ if (general_operand (x3, DFmode))
+ {
+ ro[1] = x3;
+ if (! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) )
+ return 170;
+ }
+ L870:
+ if (general_operand (x3, SFmode))
+ {
+ ro[1] = x3;
+ if (! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) )
+ return 171;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L359:
+ x1 = XEXP (x0, 1);
+ if (general_operand (x1, DImode))
+ {
+ ro[1] = x1;
+ return 64;
+ }
+ x1 = XEXP (x0, 0);
+ goto L361;
+
+ L592:
+ x1 = XEXP (x0, 1);
+ switch (GET_MODE (x1))
+ {
+ case DImode:
+ switch (GET_CODE (x1))
+ {
+ case PLUS:
+ goto L593;
+ case MINUS:
+ goto L616;
+ case NEG:
+ goto L787;
+ }
+ }
+ if (general_operand (x1, DImode))
+ {
+ ro[1] = x1;
+ return 65;
+ }
+ x1 = XEXP (x0, 0);
+ goto L376;
+
+ L593:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, DImode))
+ {
+ ro[1] = x2;
+ goto L594;
+ }
+ x1 = XEXP (x0, 0);
+ goto L376;
+
+ L594:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, DImode))
+ {
+ ro[2] = x2;
+ return 108;
+ }
+ x1 = XEXP (x0, 0);
+ goto L376;
+
+ L616:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, DImode))
+ {
+ ro[1] = x2;
+ goto L617;
+ }
+ x1 = XEXP (x0, 0);
+ goto L376;
+
+ L617:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, DImode))
+ {
+ ro[2] = x2;
+ return 116;
+ }
+ x1 = XEXP (x0, 0);
+ goto L376;
+
+ L787:
+ x2 = XEXP (x1, 0);
+ if (general_operand (x2, DImode))
+ {
+ ro[1] = x2;
+ return 152;
+ }
+ x1 = XEXP (x0, 0);
+ goto L376;
+
+ L377:
+ x1 = XEXP (x0, 1);
+ if (GET_MODE (x1) != DImode)
+ {
+ x1 = XEXP (x0, 0);
+ goto L1380;
+ }
+ switch (GET_CODE (x1))
+ {
+ case ZERO_EXTEND:
+ goto L378;
+ case SIGN_EXTEND:
+ goto L382;
+ case MULT:
+ goto L674;
+ case ASHIFT:
+ goto L912;
+ case ASHIFTRT:
+ goto L940;
+ case LSHIFTRT:
+ goto L968;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L378:
+ x2 = XEXP (x1, 0);
+ if (register_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ return 69;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L382:
+ x2 = XEXP (x1, 0);
+ if (register_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ return 70;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L674:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) != DImode)
+ {
+ x1 = XEXP (x0, 0);
+ goto L1380;
+ }
+ switch (GET_CODE (x2))
+ {
+ case ZERO_EXTEND:
+ goto L675;
+ case SIGN_EXTEND:
+ goto L682;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L675:
+ x3 = XEXP (x2, 0);
+ if (register_operand (x3, SImode))
+ {
+ ro[1] = x3;
+ goto L676;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L676:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == DImode && GET_CODE (x2) == ZERO_EXTEND && 1)
+ goto L677;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L677:
+ x3 = XEXP (x2, 0);
+ if (nonimmediate_operand (x3, SImode))
+ {
+ ro[2] = x3;
+ return 129;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L682:
+ x3 = XEXP (x2, 0);
+ if (register_operand (x3, SImode))
+ {
+ ro[1] = x3;
+ goto L683;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L683:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == DImode && GET_CODE (x2) == SIGN_EXTEND && 1)
+ goto L684;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L684:
+ x3 = XEXP (x2, 0);
+ if (nonimmediate_operand (x3, SImode))
+ {
+ ro[2] = x3;
+ return 130;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L912:
+ x2 = XEXP (x1, 0);
+ if (register_operand (x2, DImode))
+ {
+ ro[1] = x2;
+ goto L913;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L913:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CONST_INT && 1)
+ {
+ ro[2] = x2;
+ return 182;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L940:
+ x2 = XEXP (x1, 0);
+ if (register_operand (x2, DImode))
+ {
+ ro[1] = x2;
+ goto L941;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L941:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CONST_INT && 1)
+ {
+ ro[2] = x2;
+ return 188;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L968:
+ x2 = XEXP (x1, 0);
+ if (register_operand (x2, DImode))
+ {
+ ro[1] = x2;
+ goto L969;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L969:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CONST_INT && 1)
+ {
+ ro[2] = x2;
+ return 194;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+ L2:
+ tem = recog_2 (x0, insn, pnum_clobbers);
+ if (tem >= 0) return tem;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L313:
+ x2 = XEXP (x1, 0);
+ switch (GET_MODE (x2))
+ {
+ case HImode:
+ if (general_operand (x2, HImode))
+ {
+ ro[0] = x2;
+ goto L314;
+ }
+ break;
+ case QImode:
+ if (general_operand (x2, QImode))
+ {
+ ro[0] = x2;
+ goto L324;
+ }
+ }
+ goto L1380;
+
+ L314:
+ x1 = XEXP (x0, 1);
+ if (general_operand (x1, HImode))
+ {
+ ro[1] = x1;
+ return 52;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L324:
+ x1 = XEXP (x0, 1);
+ if (general_operand (x1, QImode))
+ {
+ ro[1] = x1;
+ return 55;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1314:
+ x1 = XEXP (x0, 1);
+ switch (GET_CODE (x1))
+ {
+ case MINUS:
+ if (GET_MODE (x1) == SImode && 1)
+ goto L1315;
+ break;
+ case IF_THEN_ELSE:
+ goto L1114;
+ case LABEL_REF:
+ goto L1294;
+ }
+ L1297:
+ if (general_operand (x1, SImode))
+ {
+ ro[0] = x1;
+ return 262;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1315:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == REG && XINT (x2, 0) == 3 && 1)
+ goto L1316;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1316:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == MEM && 1)
+ goto L1317;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1317:
+ x3 = XEXP (x2, 0);
+ if (GET_MODE (x3) == SImode && GET_CODE (x3) == PLUS && 1)
+ goto L1318;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1318:
+ x4 = XEXP (x3, 0);
+ if (GET_MODE (x4) == SImode && GET_CODE (x4) == MULT && 1)
+ goto L1319;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1319:
+ x5 = XEXP (x4, 0);
+ if (register_operand (x5, SImode))
+ {
+ ro[0] = x5;
+ goto L1320;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1320:
+ x5 = XEXP (x4, 1);
+ if (GET_CODE (x5) == CONST_INT && XWINT (x5, 0) == 4 && 1)
+ goto L1321;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1321:
+ x4 = XEXP (x3, 1);
+ if (GET_CODE (x4) == LABEL_REF && 1)
+ goto L1322;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1322:
+ x5 = XEXP (x4, 0);
+ if (pnum_clobbers != 0 && 1)
+ {
+ ro[1] = x5;
+ *pnum_clobbers = 1;
+ return 264;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1380;
+ L1114:
+ tem = recog_3 (x0, insn, pnum_clobbers);
+ if (tem >= 0) return tem;
+ x1 = XEXP (x0, 0);
+ goto L1380;
+
+ L1294:
+ x2 = XEXP (x1, 0);
+ ro[0] = x2;
+ return 261;
+
+ L1381:
+ x1 = XEXP (x0, 1);
+ if (GET_CODE (x1) == CALL && 1)
+ goto L1382;
+ x1 = XEXP (x0, 0);
+ goto L1460;
+
+ L1382:
+ x2 = XEXP (x1, 0);
+ if (call_insn_operand (x2, QImode))
+ {
+ ro[1] = x2;
+ goto L1383;
+ }
+ L1387:
+ if (GET_MODE (x2) == QImode && GET_CODE (x2) == MEM && 1)
+ goto L1388;
+ x1 = XEXP (x0, 0);
+ goto L1460;
+
+ L1383:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ return 276;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1387;
+
+ L1388:
+ x3 = XEXP (x2, 0);
+ if (symbolic_operand (x3, SImode))
+ {
+ ro[1] = x3;
+ goto L1389;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1460;
+
+ L1389:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ if (!HALF_PIC_P ())
+ return 277;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1460;
+
+ L1461:
+ x1 = XEXP (x0, 1);
+ if (GET_MODE (x1) == SImode && GET_CODE (x1) == PLUS && 1)
+ goto L1462;
+ goto ret0;
+
+ L1462:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == FFS && 1)
+ goto L1463;
+ goto ret0;
+
+ L1463:
+ x3 = XEXP (x2, 0);
+ if (general_operand (x3, SImode))
+ {
+ ro[1] = x3;
+ goto L1464;
+ }
+ goto ret0;
+
+ L1464:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == -1 && 1)
+ return 291;
+ goto ret0;
+
+ L1467:
+ x1 = XEXP (x0, 1);
+ if (GET_MODE (x1) == HImode && GET_CODE (x1) == PLUS && 1)
+ goto L1468;
+ goto ret0;
+
+ L1468:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) == HImode && GET_CODE (x2) == FFS && 1)
+ goto L1469;
+ goto ret0;
+
+ L1469:
+ x3 = XEXP (x2, 0);
+ if (general_operand (x3, SImode))
+ {
+ ro[1] = x3;
+ goto L1470;
+ }
+ goto ret0;
+
+ L1470:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == -1 && 1)
+ return 293;
+ goto ret0;
+
+ L1473:
+ x1 = XEXP (x0, 1);
+ if (binary_387_op (x1, DFmode))
+ {
+ ro[3] = x1;
+ goto L1479;
+ }
+ goto ret0;
+
+ L1479:
+ x2 = XEXP (x1, 0);
+ switch (GET_MODE (x2))
+ {
+ case DFmode:
+ switch (GET_CODE (x2))
+ {
+ case FLOAT:
+ goto L1480;
+ case FLOAT_EXTEND:
+ goto L1515;
+ case SUBREG:
+ case REG:
+ case MEM:
+ if (nonimmediate_operand (x2, DFmode))
+ {
+ ro[1] = x2;
+ goto L1475;
+ }
+ }
+ }
+ L1520:
+ if (general_operand (x2, DFmode))
+ {
+ ro[1] = x2;
+ goto L1521;
+ }
+ goto ret0;
+
+ L1480:
+ x3 = XEXP (x2, 0);
+ if (general_operand (x3, SImode))
+ {
+ ro[1] = x3;
+ goto L1481;
+ }
+ goto ret0;
+
+ L1481:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, DFmode))
+ {
+ ro[2] = x2;
+ if (TARGET_80387)
+ return 295;
+ }
+ goto ret0;
+
+ L1515:
+ x3 = XEXP (x2, 0);
+ if (general_operand (x3, SFmode))
+ {
+ ro[1] = x3;
+ goto L1516;
+ }
+ goto ret0;
+
+ L1516:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, DFmode))
+ {
+ ro[2] = x2;
+ if (TARGET_80387)
+ return 301;
+ }
+ goto ret0;
+
+ L1475:
+ x2 = XEXP (x1, 1);
+ if (nonimmediate_operand (x2, DFmode))
+ {
+ ro[2] = x2;
+ if (TARGET_80387)
+ return 294;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1520;
+
+ L1521:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) != DFmode)
+ goto ret0;
+ switch (GET_CODE (x2))
+ {
+ case FLOAT:
+ goto L1522;
+ case FLOAT_EXTEND:
+ goto L1528;
+ }
+ goto ret0;
+
+ L1522:
+ x3 = XEXP (x2, 0);
+ if (general_operand (x3, SImode))
+ {
+ ro[2] = x3;
+ if (TARGET_80387)
+ return 302;
+ }
+ goto ret0;
+
+ L1528:
+ x3 = XEXP (x2, 0);
+ if (general_operand (x3, SFmode))
+ {
+ ro[2] = x3;
+ if (TARGET_80387)
+ return 303;
+ }
+ goto ret0;
+
+ L1484:
+ x1 = XEXP (x0, 1);
+ if (binary_387_op (x1, XFmode))
+ {
+ ro[3] = x1;
+ goto L1490;
+ }
+ goto ret0;
+
+ L1490:
+ x2 = XEXP (x1, 0);
+ switch (GET_MODE (x2))
+ {
+ case XFmode:
+ switch (GET_CODE (x2))
+ {
+ case FLOAT:
+ goto L1491;
+ case FLOAT_EXTEND:
+ goto L1497;
+ case SUBREG:
+ case REG:
+ case MEM:
+ if (nonimmediate_operand (x2, XFmode))
+ {
+ ro[1] = x2;
+ goto L1486;
+ }
+ }
+ }
+ L1502:
+ if (general_operand (x2, XFmode))
+ {
+ ro[1] = x2;
+ goto L1503;
+ }
+ goto ret0;
+
+ L1491:
+ x3 = XEXP (x2, 0);
+ if (general_operand (x3, SImode))
+ {
+ ro[1] = x3;
+ goto L1492;
+ }
+ goto ret0;
+
+ L1492:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, XFmode))
+ {
+ ro[2] = x2;
+ if (TARGET_80387)
+ return 297;
+ }
+ goto ret0;
+
+ L1497:
+ x3 = XEXP (x2, 0);
+ if (general_operand (x3, SFmode))
+ {
+ ro[1] = x3;
+ goto L1498;
+ }
+ goto ret0;
+
+ L1498:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, XFmode))
+ {
+ ro[2] = x2;
+ if (TARGET_80387)
+ return 298;
+ }
+ goto ret0;
+
+ L1486:
+ x2 = XEXP (x1, 1);
+ if (nonimmediate_operand (x2, XFmode))
+ {
+ ro[2] = x2;
+ if (TARGET_80387)
+ return 296;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1502;
+
+ L1503:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) != XFmode)
+ goto ret0;
+ switch (GET_CODE (x2))
+ {
+ case FLOAT:
+ goto L1504;
+ case FLOAT_EXTEND:
+ goto L1510;
+ }
+ goto ret0;
+
+ L1504:
+ x3 = XEXP (x2, 0);
+ if (general_operand (x3, SImode))
+ {
+ ro[2] = x3;
+ if (TARGET_80387)
+ return 299;
+ }
+ goto ret0;
+
+ L1510:
+ x3 = XEXP (x2, 0);
+ if (general_operand (x3, SFmode))
+ {
+ ro[2] = x3;
+ if (TARGET_80387)
+ return 300;
+ }
+ goto ret0;
+
+ L1531:
+ x1 = XEXP (x0, 1);
+ if (binary_387_op (x1, SFmode))
+ {
+ ro[3] = x1;
+ goto L1537;
+ }
+ goto ret0;
+
+ L1537:
+ x2 = XEXP (x1, 0);
+ switch (GET_MODE (x2))
+ {
+ case SFmode:
+ if (GET_CODE (x2) == FLOAT && 1)
+ goto L1538;
+ if (nonimmediate_operand (x2, SFmode))
+ {
+ ro[1] = x2;
+ goto L1533;
+ }
+ }
+ L1543:
+ if (general_operand (x2, SFmode))
+ {
+ ro[1] = x2;
+ goto L1544;
+ }
+ goto ret0;
+
+ L1538:
+ x3 = XEXP (x2, 0);
+ if (general_operand (x3, SImode))
+ {
+ ro[1] = x3;
+ goto L1539;
+ }
+ goto ret0;
+
+ L1539:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, SFmode))
+ {
+ ro[2] = x2;
+ if (TARGET_80387)
+ return 305;
+ }
+ goto ret0;
+
+ L1533:
+ x2 = XEXP (x1, 1);
+ if (nonimmediate_operand (x2, SFmode))
+ {
+ ro[2] = x2;
+ if (TARGET_80387)
+ return 304;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1543;
+
+ L1544:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == SFmode && GET_CODE (x2) == FLOAT && 1)
+ goto L1545;
+ goto ret0;
+
+ L1545:
+ x3 = XEXP (x2, 0);
+ if (general_operand (x3, SImode))
+ {
+ ro[2] = x3;
+ if (TARGET_80387)
+ return 306;
+ }
+ goto ret0;
+ ret0: return -1;
+}
+
+int
+recog_5 (x0, insn, pnum_clobbers)
+ register rtx x0;
+ rtx insn;
+ int *pnum_clobbers;
+{
+ register rtx *ro = &recog_operand[0];
+ register rtx x1, x2, x3, x4, x5, x6;
+ int tem;
+
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 1);
+ x3 = XEXP (x2, 0);
+ switch (GET_MODE (x3))
+ {
+ case XFmode:
+ if (GET_CODE (x3) == FLOAT && 1)
+ goto L84;
+ if (nonimmediate_operand (x3, XFmode))
+ {
+ ro[0] = x3;
+ goto L56;
+ }
+ L68:
+ if (register_operand (x3, XFmode))
+ {
+ ro[0] = x3;
+ goto L69;
+ }
+ break;
+ case DFmode:
+ switch (GET_CODE (x3))
+ {
+ case FLOAT:
+ goto L170;
+ case FLOAT_EXTEND:
+ goto L200;
+ case SUBREG:
+ case REG:
+ case MEM:
+ if (nonimmediate_operand (x3, DFmode))
+ {
+ ro[0] = x3;
+ goto L142;
+ }
+ }
+ L154:
+ if (register_operand (x3, DFmode))
+ {
+ ro[0] = x3;
+ goto L155;
+ }
+ break;
+ case SFmode:
+ if (GET_CODE (x3) == FLOAT && 1)
+ goto L256;
+ if (nonimmediate_operand (x3, SFmode))
+ {
+ ro[0] = x3;
+ goto L228;
+ }
+ L240:
+ if (register_operand (x3, SFmode))
+ {
+ ro[0] = x3;
+ goto L241;
+ }
+ }
+ goto ret0;
+
+ L84:
+ x4 = XEXP (x3, 0);
+ if (nonimmediate_operand (x4, SImode))
+ {
+ ro[0] = x4;
+ goto L85;
+ }
+ goto ret0;
+
+ L85:
+ x3 = XEXP (x2, 1);
+ if (register_operand (x3, XFmode))
+ {
+ ro[1] = x3;
+ goto L86;
+ }
+ goto ret0;
+
+ L86:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L87;
+ goto ret0;
+
+ L87:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, HImode))
+ {
+ ro[3] = x2;
+ if (TARGET_80387)
+ return 20;
+ }
+ goto ret0;
+
+ L56:
+ x3 = XEXP (x2, 1);
+ if (nonimmediate_operand (x3, XFmode))
+ {
+ ro[1] = x3;
+ goto L57;
+ }
+ x3 = XEXP (x2, 0);
+ goto L68;
+
+ L57:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L58;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 1);
+ x3 = XEXP (x2, 0);
+ goto L68;
+
+ L58:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, HImode))
+ {
+ ro[3] = x2;
+ if (TARGET_80387
+ && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM))
+ return 18;
+ }
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 1);
+ x3 = XEXP (x2, 0);
+ goto L68;
+
+ L69:
+ x3 = XEXP (x2, 1);
+ if (GET_MODE (x3) != XFmode)
+ goto ret0;
+ switch (GET_CODE (x3))
+ {
+ case FLOAT:
+ goto L70;
+ case FLOAT_EXTEND:
+ goto L100;
+ }
+ goto ret0;
+
+ L70:
+ x4 = XEXP (x3, 0);
+ if (nonimmediate_operand (x4, SImode))
+ {
+ ro[1] = x4;
+ goto L71;
+ }
+ goto ret0;
+
+ L71:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L72;
+ goto ret0;
+
+ L72:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, HImode))
+ {
+ ro[3] = x2;
+ if (TARGET_80387)
+ return 19;
+ }
+ goto ret0;
+
+ L100:
+ x4 = XEXP (x3, 0);
+ switch (GET_MODE (x4))
+ {
+ case DFmode:
+ if (nonimmediate_operand (x4, DFmode))
+ {
+ ro[1] = x4;
+ goto L101;
+ }
+ break;
+ case SFmode:
+ if (nonimmediate_operand (x4, SFmode))
+ {
+ ro[1] = x4;
+ goto L116;
+ }
+ }
+ goto ret0;
+
+ L101:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L102;
+ goto ret0;
+
+ L102:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, HImode))
+ {
+ ro[3] = x2;
+ if (TARGET_80387)
+ return 21;
+ }
+ goto ret0;
+
+ L116:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L117;
+ goto ret0;
+
+ L117:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, HImode))
+ {
+ ro[3] = x2;
+ if (TARGET_80387)
+ return 22;
+ }
+ goto ret0;
+
+ L170:
+ x4 = XEXP (x3, 0);
+ if (nonimmediate_operand (x4, SImode))
+ {
+ ro[0] = x4;
+ goto L171;
+ }
+ goto ret0;
+
+ L171:
+ x3 = XEXP (x2, 1);
+ if (register_operand (x3, DFmode))
+ {
+ ro[1] = x3;
+ goto L172;
+ }
+ goto ret0;
+
+ L172:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L173;
+ goto ret0;
+
+ L173:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, HImode))
+ {
+ ro[3] = x2;
+ if (TARGET_80387)
+ return 26;
+ }
+ goto ret0;
+
+ L200:
+ x4 = XEXP (x3, 0);
+ if (nonimmediate_operand (x4, SFmode))
+ {
+ ro[0] = x4;
+ goto L201;
+ }
+ goto ret0;
+
+ L201:
+ x3 = XEXP (x2, 1);
+ if (register_operand (x3, DFmode))
+ {
+ ro[1] = x3;
+ goto L202;
+ }
+ goto ret0;
+
+ L202:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L203;
+ goto ret0;
+
+ L203:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, HImode))
+ {
+ ro[3] = x2;
+ if (TARGET_80387)
+ return 28;
+ }
+ goto ret0;
+
+ L142:
+ x3 = XEXP (x2, 1);
+ if (nonimmediate_operand (x3, DFmode))
+ {
+ ro[1] = x3;
+ goto L143;
+ }
+ x3 = XEXP (x2, 0);
+ goto L154;
+
+ L143:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L144;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 1);
+ x3 = XEXP (x2, 0);
+ goto L154;
+
+ L144:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, HImode))
+ {
+ ro[3] = x2;
+ if (TARGET_80387
+ && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM))
+ return 24;
+ }
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 1);
+ x3 = XEXP (x2, 0);
+ goto L154;
+
+ L155:
+ x3 = XEXP (x2, 1);
+ if (GET_MODE (x3) != DFmode)
+ goto ret0;
+ switch (GET_CODE (x3))
+ {
+ case FLOAT:
+ goto L156;
+ case FLOAT_EXTEND:
+ goto L186;
+ }
+ goto ret0;
+
+ L156:
+ x4 = XEXP (x3, 0);
+ if (nonimmediate_operand (x4, SImode))
+ {
+ ro[1] = x4;
+ goto L157;
+ }
+ goto ret0;
+
+ L157:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L158;
+ goto ret0;
+
+ L158:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, HImode))
+ {
+ ro[3] = x2;
+ if (TARGET_80387)
+ return 25;
+ }
+ goto ret0;
+
+ L186:
+ x4 = XEXP (x3, 0);
+ if (nonimmediate_operand (x4, SFmode))
+ {
+ ro[1] = x4;
+ goto L187;
+ }
+ goto ret0;
+
+ L187:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L188;
+ goto ret0;
+
+ L188:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, HImode))
+ {
+ ro[3] = x2;
+ if (TARGET_80387)
+ return 27;
+ }
+ goto ret0;
+
+ L256:
+ x4 = XEXP (x3, 0);
+ if (nonimmediate_operand (x4, SImode))
+ {
+ ro[0] = x4;
+ goto L257;
+ }
+ goto ret0;
+
+ L257:
+ x3 = XEXP (x2, 1);
+ if (register_operand (x3, SFmode))
+ {
+ ro[1] = x3;
+ goto L258;
+ }
+ goto ret0;
+
+ L258:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L259;
+ goto ret0;
+
+ L259:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, HImode))
+ {
+ ro[3] = x2;
+ if (TARGET_80387)
+ return 32;
+ }
+ goto ret0;
+
+ L228:
+ x3 = XEXP (x2, 1);
+ if (nonimmediate_operand (x3, SFmode))
+ {
+ ro[1] = x3;
+ goto L229;
+ }
+ x3 = XEXP (x2, 0);
+ goto L240;
+
+ L229:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L230;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 1);
+ x3 = XEXP (x2, 0);
+ goto L240;
+
+ L230:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, HImode))
+ {
+ ro[3] = x2;
+ if (TARGET_80387
+ && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM))
+ return 30;
+ }
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 1);
+ x3 = XEXP (x2, 0);
+ goto L240;
+
+ L241:
+ x3 = XEXP (x2, 1);
+ if (GET_MODE (x3) == SFmode && GET_CODE (x3) == FLOAT && 1)
+ goto L242;
+ goto ret0;
+
+ L242:
+ x4 = XEXP (x3, 0);
+ if (nonimmediate_operand (x4, SImode))
+ {
+ ro[1] = x4;
+ goto L243;
+ }
+ goto ret0;
+
+ L243:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L244;
+ goto ret0;
+
+ L244:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, HImode))
+ {
+ ro[3] = x2;
+ if (TARGET_80387)
+ return 31;
+ }
+ goto ret0;
+ ret0: return -1;
+}
+
+int
+recog_6 (x0, insn, pnum_clobbers)
+ register rtx x0;
+ rtx insn;
+ int *pnum_clobbers;
+{
+ register rtx *ro = &recog_operand[0];
+ register rtx x1, x2, x3, x4, x5, x6;
+ int tem;
+
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ switch (GET_MODE (x2))
+ {
+ case DFmode:
+ if (register_operand (x2, DFmode))
+ {
+ ro[0] = x2;
+ goto L337;
+ }
+ break;
+ case XFmode:
+ if (register_operand (x2, XFmode))
+ {
+ ro[0] = x2;
+ goto L350;
+ }
+ break;
+ case SFmode:
+ if (nonimmediate_operand (x2, SFmode))
+ {
+ ro[0] = x2;
+ goto L410;
+ }
+ break;
+ case SImode:
+ if (register_operand (x2, SImode))
+ {
+ ro[0] = x2;
+ goto L698;
+ }
+ break;
+ case HImode:
+ if (register_operand (x2, HImode))
+ {
+ ro[0] = x2;
+ goto L709;
+ }
+ break;
+ case DImode:
+ if (register_operand (x2, DImode))
+ {
+ ro[0] = x2;
+ goto L917;
+ }
+ }
+ switch (GET_CODE (x2))
+ {
+ case CC0:
+ goto L12;
+ case PC:
+ goto L1301;
+ }
+ L1358:
+ ro[0] = x2;
+ goto L1359;
+ L1548:
+ if (register_operand (x2, SImode))
+ {
+ ro[0] = x2;
+ goto L1549;
+ }
+ goto ret0;
+
+ L337:
+ x2 = XEXP (x1, 1);
+ if (register_operand (x2, DFmode))
+ {
+ ro[1] = x2;
+ goto L338;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L338:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == SET && 1)
+ goto L339;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L339:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[1]) && 1)
+ goto L340;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L340:
+ x2 = XEXP (x1, 1);
+ if (rtx_equal_p (x2, ro[0]) && 1)
+ return 59;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L350:
+ x2 = XEXP (x1, 1);
+ if (register_operand (x2, XFmode))
+ {
+ ro[1] = x2;
+ goto L351;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L351:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == SET && 1)
+ goto L352;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L352:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[1]) && 1)
+ goto L353;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L353:
+ x2 = XEXP (x1, 1);
+ if (rtx_equal_p (x2, ro[0]) && 1)
+ return 62;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L410:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == SFmode && GET_CODE (x2) == FLOAT_TRUNCATE && 1)
+ goto L411;
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L411:
+ x3 = XEXP (x2, 0);
+ if (register_operand (x3, DFmode))
+ {
+ ro[1] = x3;
+ goto L412;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L412:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L413;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L413:
+ x2 = XEXP (x1, 0);
+ if (memory_operand (x2, SFmode))
+ {
+ ro[2] = x2;
+ if (TARGET_80387)
+ return 78;
+ }
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L698:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) != SImode)
+ {
+ x2 = XEXP (x1, 0);
+ goto L1358;
+ }
+ switch (GET_CODE (x2))
+ {
+ case DIV:
+ goto L699;
+ case UDIV:
+ goto L721;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L699:
+ x3 = XEXP (x2, 0);
+ if (register_operand (x3, SImode))
+ {
+ ro[1] = x3;
+ goto L700;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L700:
+ x3 = XEXP (x2, 1);
+ if (general_operand (x3, SImode))
+ {
+ ro[2] = x3;
+ goto L701;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L701:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == SET && 1)
+ goto L702;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L702:
+ x2 = XEXP (x1, 0);
+ if (register_operand (x2, SImode))
+ {
+ ro[3] = x2;
+ goto L703;
+ }
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L703:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == MOD && 1)
+ goto L704;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L704:
+ x3 = XEXP (x2, 0);
+ if (rtx_equal_p (x3, ro[1]) && 1)
+ goto L705;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L705:
+ x3 = XEXP (x2, 1);
+ if (rtx_equal_p (x3, ro[2]) && 1)
+ return 139;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L721:
+ x3 = XEXP (x2, 0);
+ if (register_operand (x3, SImode))
+ {
+ ro[1] = x3;
+ goto L722;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L722:
+ x3 = XEXP (x2, 1);
+ if (general_operand (x3, SImode))
+ {
+ ro[2] = x3;
+ goto L723;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L723:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == SET && 1)
+ goto L724;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L724:
+ x2 = XEXP (x1, 0);
+ if (register_operand (x2, SImode))
+ {
+ ro[3] = x2;
+ goto L725;
+ }
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L725:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == UMOD && 1)
+ goto L726;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L726:
+ x3 = XEXP (x2, 0);
+ if (rtx_equal_p (x3, ro[1]) && 1)
+ goto L727;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L727:
+ x3 = XEXP (x2, 1);
+ if (rtx_equal_p (x3, ro[2]) && 1)
+ return 141;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L709:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) != HImode)
+ {
+ x2 = XEXP (x1, 0);
+ goto L1358;
+ }
+ switch (GET_CODE (x2))
+ {
+ case DIV:
+ goto L710;
+ case UDIV:
+ goto L732;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L710:
+ x3 = XEXP (x2, 0);
+ if (register_operand (x3, HImode))
+ {
+ ro[1] = x3;
+ goto L711;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L711:
+ x3 = XEXP (x2, 1);
+ if (general_operand (x3, HImode))
+ {
+ ro[2] = x3;
+ goto L712;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L712:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == SET && 1)
+ goto L713;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L713:
+ x2 = XEXP (x1, 0);
+ if (register_operand (x2, HImode))
+ {
+ ro[3] = x2;
+ goto L714;
+ }
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L714:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == HImode && GET_CODE (x2) == MOD && 1)
+ goto L715;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L715:
+ x3 = XEXP (x2, 0);
+ if (rtx_equal_p (x3, ro[1]) && 1)
+ goto L716;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L716:
+ x3 = XEXP (x2, 1);
+ if (rtx_equal_p (x3, ro[2]) && 1)
+ return 140;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L732:
+ x3 = XEXP (x2, 0);
+ if (register_operand (x3, HImode))
+ {
+ ro[1] = x3;
+ goto L733;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L733:
+ x3 = XEXP (x2, 1);
+ if (general_operand (x3, HImode))
+ {
+ ro[2] = x3;
+ goto L734;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L734:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == SET && 1)
+ goto L735;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L735:
+ x2 = XEXP (x1, 0);
+ if (register_operand (x2, HImode))
+ {
+ ro[3] = x2;
+ goto L736;
+ }
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L736:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == HImode && GET_CODE (x2) == UMOD && 1)
+ goto L737;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L737:
+ x3 = XEXP (x2, 0);
+ if (rtx_equal_p (x3, ro[1]) && 1)
+ goto L738;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L738:
+ x3 = XEXP (x2, 1);
+ if (rtx_equal_p (x3, ro[2]) && 1)
+ return 142;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L917:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) != DImode)
+ {
+ x2 = XEXP (x1, 0);
+ goto L1358;
+ }
+ switch (GET_CODE (x2))
+ {
+ case ASHIFT:
+ goto L918;
+ case ASHIFTRT:
+ goto L946;
+ case LSHIFTRT:
+ goto L974;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L918:
+ x3 = XEXP (x2, 0);
+ if (register_operand (x3, DImode))
+ {
+ ro[1] = x3;
+ goto L919;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L919:
+ x3 = XEXP (x2, 1);
+ if (register_operand (x3, QImode))
+ {
+ ro[2] = x3;
+ goto L920;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L920:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L921;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L921:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[2]) && 1)
+ return 183;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L946:
+ x3 = XEXP (x2, 0);
+ if (register_operand (x3, DImode))
+ {
+ ro[1] = x3;
+ goto L947;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L947:
+ x3 = XEXP (x2, 1);
+ if (register_operand (x3, QImode))
+ {
+ ro[2] = x3;
+ goto L948;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L948:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L949;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L949:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[2]) && 1)
+ return 189;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L974:
+ x3 = XEXP (x2, 0);
+ if (register_operand (x3, DImode))
+ {
+ ro[1] = x3;
+ goto L975;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L975:
+ x3 = XEXP (x2, 1);
+ if (register_operand (x3, QImode))
+ {
+ ro[2] = x3;
+ goto L976;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L976:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L977;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L977:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[2]) && 1)
+ return 195;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L12:
+ x2 = XEXP (x1, 1);
+ switch (GET_MODE (x2))
+ {
+ case SFmode:
+ if (register_operand (x2, SFmode))
+ {
+ ro[0] = x2;
+ goto L13;
+ }
+ break;
+ case DFmode:
+ if (register_operand (x2, DFmode))
+ {
+ ro[0] = x2;
+ goto L22;
+ }
+ break;
+ case XFmode:
+ if (register_operand (x2, XFmode))
+ {
+ ro[0] = x2;
+ goto L31;
+ }
+ }
+ L54:
+ if (VOIDmode_compare_op (x2, VOIDmode))
+ {
+ ro[2] = x2;
+ goto L83;
+ }
+ L127:
+ if (GET_MODE (x2) == CCFPEQmode && GET_CODE (x2) == COMPARE && 1)
+ goto L128;
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L13:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L14;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 1);
+ goto L54;
+
+ L14:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387 && ! TARGET_IEEE_FP)
+ return 6;
+ }
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 1);
+ goto L54;
+
+ L22:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L23;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 1);
+ goto L54;
+
+ L23:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387 && ! TARGET_IEEE_FP)
+ return 8;
+ }
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 1);
+ goto L54;
+
+ L31:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L32;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 1);
+ goto L54;
+
+ L32:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, HImode))
+ {
+ ro[1] = x2;
+ if (TARGET_80387 && ! TARGET_IEEE_FP)
+ return 10;
+ }
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 1);
+ goto L54;
+ L83:
+ tem = recog_5 (x0, insn, pnum_clobbers);
+ if (tem >= 0) return tem;
+ goto L127;
+
+ L128:
+ x3 = XEXP (x2, 0);
+ switch (GET_MODE (x3))
+ {
+ case XFmode:
+ if (register_operand (x3, XFmode))
+ {
+ ro[0] = x3;
+ goto L129;
+ }
+ break;
+ case DFmode:
+ if (register_operand (x3, DFmode))
+ {
+ ro[0] = x3;
+ goto L215;
+ }
+ break;
+ case SFmode:
+ if (register_operand (x3, SFmode))
+ {
+ ro[0] = x3;
+ goto L271;
+ }
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L129:
+ x3 = XEXP (x2, 1);
+ if (register_operand (x3, XFmode))
+ {
+ ro[1] = x3;
+ goto L130;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L130:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L131;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L131:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, HImode))
+ {
+ ro[2] = x2;
+ if (TARGET_80387)
+ return 23;
+ }
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L215:
+ x3 = XEXP (x2, 1);
+ if (register_operand (x3, DFmode))
+ {
+ ro[1] = x3;
+ goto L216;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L216:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L217;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L217:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, HImode))
+ {
+ ro[2] = x2;
+ if (TARGET_80387)
+ return 29;
+ }
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L271:
+ x3 = XEXP (x2, 1);
+ if (register_operand (x3, SFmode))
+ {
+ ro[1] = x3;
+ goto L272;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L272:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L273;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L273:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, HImode))
+ {
+ ro[2] = x2;
+ if (TARGET_80387)
+ return 33;
+ }
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L1301:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == MINUS && 1)
+ goto L1302;
+ if (general_operand (x2, SImode))
+ {
+ ro[0] = x2;
+ goto L1327;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L1302:
+ x3 = XEXP (x2, 0);
+ if (GET_MODE (x3) == SImode && GET_CODE (x3) == REG && XINT (x3, 0) == 3 && 1)
+ goto L1303;
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L1303:
+ x3 = XEXP (x2, 1);
+ if (GET_MODE (x3) == SImode && GET_CODE (x3) == MEM && 1)
+ goto L1304;
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L1304:
+ x4 = XEXP (x3, 0);
+ if (GET_MODE (x4) == SImode && GET_CODE (x4) == PLUS && 1)
+ goto L1305;
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L1305:
+ x5 = XEXP (x4, 0);
+ if (GET_MODE (x5) == SImode && GET_CODE (x5) == MULT && 1)
+ goto L1306;
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L1306:
+ x6 = XEXP (x5, 0);
+ if (register_operand (x6, SImode))
+ {
+ ro[0] = x6;
+ goto L1307;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L1307:
+ x6 = XEXP (x5, 1);
+ if (GET_CODE (x6) == CONST_INT && XWINT (x6, 0) == 4 && 1)
+ goto L1308;
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L1308:
+ x5 = XEXP (x4, 1);
+ if (GET_CODE (x5) == LABEL_REF && 1)
+ goto L1309;
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L1309:
+ x6 = XEXP (x5, 0);
+ ro[1] = x6;
+ goto L1310;
+
+ L1310:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L1311;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L1311:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ return 264;
+ }
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L1327:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == USE && 1)
+ goto L1328;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L1328:
+ x2 = XEXP (x1, 0);
+ if (GET_CODE (x2) == LABEL_REF && 1)
+ goto L1329;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1358;
+
+ L1329:
+ x3 = XEXP (x2, 0);
+ ro[1] = x3;
+ return 265;
+
+ L1359:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CALL && 1)
+ goto L1371;
+ x2 = XEXP (x1, 0);
+ goto L1548;
+
+ L1371:
+ x3 = XEXP (x2, 0);
+ if (GET_MODE (x3) == QImode && GET_CODE (x3) == MEM && 1)
+ goto L1372;
+ L1360:
+ if (call_insn_operand (x3, QImode))
+ {
+ ro[1] = x3;
+ goto L1361;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1548;
+
+ L1372:
+ x4 = XEXP (x3, 0);
+ if (symbolic_operand (x4, SImode))
+ {
+ ro[1] = x4;
+ goto L1373;
+ }
+ goto L1360;
+
+ L1373:
+ x3 = XEXP (x2, 1);
+ if (general_operand (x3, SImode))
+ {
+ ro[2] = x3;
+ goto L1374;
+ }
+ x3 = XEXP (x2, 0);
+ goto L1360;
+
+ L1374:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == SET && 1)
+ goto L1375;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 1);
+ x3 = XEXP (x2, 0);
+ goto L1360;
+
+ L1375:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == REG && XINT (x2, 0) == 7 && 1)
+ goto L1376;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 1);
+ x3 = XEXP (x2, 0);
+ goto L1360;
+
+ L1376:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == PLUS && 1)
+ goto L1377;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 1);
+ x3 = XEXP (x2, 0);
+ goto L1360;
+
+ L1377:
+ x3 = XEXP (x2, 0);
+ if (GET_MODE (x3) == SImode && GET_CODE (x3) == REG && XINT (x3, 0) == 7 && 1)
+ goto L1378;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 1);
+ x3 = XEXP (x2, 0);
+ goto L1360;
+
+ L1378:
+ x3 = XEXP (x2, 1);
+ if (immediate_operand (x3, SImode))
+ {
+ ro[4] = x3;
+ if (!HALF_PIC_P ())
+ return 274;
+ }
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 1);
+ x3 = XEXP (x2, 0);
+ goto L1360;
+
+ L1361:
+ x3 = XEXP (x2, 1);
+ if (general_operand (x3, SImode))
+ {
+ ro[2] = x3;
+ goto L1362;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1548;
+
+ L1362:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == SET && 1)
+ goto L1363;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1548;
+
+ L1363:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == REG && XINT (x2, 0) == 7 && 1)
+ goto L1364;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1548;
+
+ L1364:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == PLUS && 1)
+ goto L1365;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1548;
+
+ L1365:
+ x3 = XEXP (x2, 0);
+ if (GET_MODE (x3) == SImode && GET_CODE (x3) == REG && XINT (x3, 0) == 7 && 1)
+ goto L1366;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1548;
+
+ L1366:
+ x3 = XEXP (x2, 1);
+ if (immediate_operand (x3, SImode))
+ {
+ ro[4] = x3;
+ return 273;
+ }
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1548;
+
+ L1549:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == UNSPEC && XINT (x2, 1) == 0 && XVECLEN (x2, 0) == 3 && 1)
+ goto L1550;
+ goto ret0;
+
+ L1550:
+ x3 = XVECEXP (x2, 0, 0);
+ if (GET_MODE (x3) == BLKmode && GET_CODE (x3) == MEM && 1)
+ goto L1551;
+ goto ret0;
+
+ L1551:
+ x4 = XEXP (x3, 0);
+ if (address_operand (x4, SImode))
+ {
+ ro[1] = x4;
+ goto L1552;
+ }
+ goto ret0;
+
+ L1552:
+ x3 = XVECEXP (x2, 0, 1);
+ if (register_operand (x3, QImode))
+ {
+ ro[2] = x3;
+ goto L1553;
+ }
+ goto ret0;
+
+ L1553:
+ x3 = XVECEXP (x2, 0, 2);
+ if (immediate_operand (x3, SImode))
+ {
+ ro[3] = x3;
+ goto L1554;
+ }
+ goto ret0;
+
+ L1554:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L1555;
+ goto ret0;
+
+ L1555:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[1]) && 1)
+ return 308;
+ goto ret0;
+ ret0: return -1;
+}
+
+int
+recog_7 (x0, insn, pnum_clobbers)
+ register rtx x0;
+ rtx insn;
+ int *pnum_clobbers;
+{
+ register rtx *ro = &recog_operand[0];
+ register rtx x1, x2, x3, x4, x5, x6;
+ int tem;
+
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ switch (GET_MODE (x2))
+ {
+ case DImode:
+ if (general_operand (x2, DImode))
+ {
+ ro[0] = x2;
+ goto L439;
+ }
+ break;
+ case SImode:
+ if (general_operand (x2, SImode))
+ {
+ ro[0] = x2;
+ goto L503;
+ }
+ }
+ goto ret0;
+
+ L439:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == DImode && GET_CODE (x2) == FIX && 1)
+ goto L440;
+ goto ret0;
+
+ L440:
+ x3 = XEXP (x2, 0);
+ if (GET_CODE (x3) != FIX)
+ goto ret0;
+ switch (GET_MODE (x3))
+ {
+ case XFmode:
+ goto L441;
+ case DFmode:
+ goto L467;
+ case SFmode:
+ goto L493;
+ }
+ goto ret0;
+
+ L441:
+ x4 = XEXP (x3, 0);
+ if (register_operand (x4, XFmode))
+ {
+ ro[1] = x4;
+ goto L442;
+ }
+ goto ret0;
+
+ L442:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L443;
+ goto ret0;
+
+ L443:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[1]) && 1)
+ goto L444;
+ goto ret0;
+
+ L444:
+ x1 = XVECEXP (x0, 0, 2);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L445;
+ goto ret0;
+
+ L445:
+ x2 = XEXP (x1, 0);
+ if (memory_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ goto L446;
+ }
+ goto ret0;
+
+ L446:
+ x1 = XVECEXP (x0, 0, 3);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L447;
+ goto ret0;
+
+ L447:
+ x2 = XEXP (x1, 0);
+ if (pnum_clobbers != 0 && memory_operand (x2, SImode))
+ {
+ ro[3] = x2;
+ if (TARGET_80387)
+ {
+ *pnum_clobbers = 1;
+ return 87;
+ }
+ }
+ goto ret0;
+
+ L467:
+ x4 = XEXP (x3, 0);
+ if (register_operand (x4, DFmode))
+ {
+ ro[1] = x4;
+ goto L468;
+ }
+ goto ret0;
+
+ L468:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L469;
+ goto ret0;
+
+ L469:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[1]) && 1)
+ goto L470;
+ goto ret0;
+
+ L470:
+ x1 = XVECEXP (x0, 0, 2);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L471;
+ goto ret0;
+
+ L471:
+ x2 = XEXP (x1, 0);
+ if (memory_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ goto L472;
+ }
+ goto ret0;
+
+ L472:
+ x1 = XVECEXP (x0, 0, 3);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L473;
+ goto ret0;
+
+ L473:
+ x2 = XEXP (x1, 0);
+ if (pnum_clobbers != 0 && memory_operand (x2, SImode))
+ {
+ ro[3] = x2;
+ if (TARGET_80387)
+ {
+ *pnum_clobbers = 1;
+ return 88;
+ }
+ }
+ goto ret0;
+
+ L493:
+ x4 = XEXP (x3, 0);
+ if (register_operand (x4, SFmode))
+ {
+ ro[1] = x4;
+ goto L494;
+ }
+ goto ret0;
+
+ L494:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L495;
+ goto ret0;
+
+ L495:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[1]) && 1)
+ goto L496;
+ goto ret0;
+
+ L496:
+ x1 = XVECEXP (x0, 0, 2);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L497;
+ goto ret0;
+
+ L497:
+ x2 = XEXP (x1, 0);
+ if (memory_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ goto L498;
+ }
+ goto ret0;
+
+ L498:
+ x1 = XVECEXP (x0, 0, 3);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L499;
+ goto ret0;
+
+ L499:
+ x2 = XEXP (x1, 0);
+ if (pnum_clobbers != 0 && memory_operand (x2, SImode))
+ {
+ ro[3] = x2;
+ if (TARGET_80387)
+ {
+ *pnum_clobbers = 1;
+ return 89;
+ }
+ }
+ goto ret0;
+
+ L503:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == FIX && 1)
+ goto L504;
+ goto ret0;
+
+ L504:
+ x3 = XEXP (x2, 0);
+ if (GET_CODE (x3) != FIX)
+ goto ret0;
+ switch (GET_MODE (x3))
+ {
+ case XFmode:
+ goto L505;
+ case DFmode:
+ goto L527;
+ case SFmode:
+ goto L549;
+ }
+ goto ret0;
+
+ L505:
+ x4 = XEXP (x3, 0);
+ if (register_operand (x4, XFmode))
+ {
+ ro[1] = x4;
+ goto L506;
+ }
+ goto ret0;
+
+ L506:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L507;
+ goto ret0;
+
+ L507:
+ x2 = XEXP (x1, 0);
+ if (memory_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ goto L508;
+ }
+ goto ret0;
+
+ L508:
+ x1 = XVECEXP (x0, 0, 2);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L509;
+ goto ret0;
+
+ L509:
+ x2 = XEXP (x1, 0);
+ if (memory_operand (x2, SImode))
+ {
+ ro[3] = x2;
+ goto L510;
+ }
+ goto ret0;
+
+ L510:
+ x1 = XVECEXP (x0, 0, 3);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L511;
+ goto ret0;
+
+ L511:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, SImode))
+ {
+ ro[4] = x2;
+ if (TARGET_80387)
+ return 93;
+ }
+ goto ret0;
+
+ L527:
+ x4 = XEXP (x3, 0);
+ if (register_operand (x4, DFmode))
+ {
+ ro[1] = x4;
+ goto L528;
+ }
+ goto ret0;
+
+ L528:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L529;
+ goto ret0;
+
+ L529:
+ x2 = XEXP (x1, 0);
+ if (memory_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ goto L530;
+ }
+ goto ret0;
+
+ L530:
+ x1 = XVECEXP (x0, 0, 2);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L531;
+ goto ret0;
+
+ L531:
+ x2 = XEXP (x1, 0);
+ if (memory_operand (x2, SImode))
+ {
+ ro[3] = x2;
+ goto L532;
+ }
+ goto ret0;
+
+ L532:
+ x1 = XVECEXP (x0, 0, 3);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L533;
+ goto ret0;
+
+ L533:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, SImode))
+ {
+ ro[4] = x2;
+ if (TARGET_80387)
+ return 94;
+ }
+ goto ret0;
+
+ L549:
+ x4 = XEXP (x3, 0);
+ if (register_operand (x4, SFmode))
+ {
+ ro[1] = x4;
+ goto L550;
+ }
+ goto ret0;
+
+ L550:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L551;
+ goto ret0;
+
+ L551:
+ x2 = XEXP (x1, 0);
+ if (memory_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ goto L552;
+ }
+ goto ret0;
+
+ L552:
+ x1 = XVECEXP (x0, 0, 2);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L553;
+ goto ret0;
+
+ L553:
+ x2 = XEXP (x1, 0);
+ if (memory_operand (x2, SImode))
+ {
+ ro[3] = x2;
+ goto L554;
+ }
+ goto ret0;
+
+ L554:
+ x1 = XVECEXP (x0, 0, 3);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L555;
+ goto ret0;
+
+ L555:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, SImode))
+ {
+ ro[4] = x2;
+ if (TARGET_80387)
+ return 95;
+ }
+ goto ret0;
+ ret0: return -1;
+}
+
+int
+recog (x0, insn, pnum_clobbers)
+ register rtx x0;
+ rtx insn;
+ int *pnum_clobbers;
+{
+ register rtx *ro = &recog_operand[0];
+ register rtx x1, x2, x3, x4, x5, x6;
+ int tem;
+
+ L1403:
+ switch (GET_CODE (x0))
+ {
+ case UNSPEC:
+ if (GET_MODE (x0) == SImode && XINT (x0, 1) == 0 && XVECLEN (x0, 0) == 1 && 1)
+ goto L1404;
+ break;
+ case SET:
+ goto L295;
+ case PARALLEL:
+ if (XVECLEN (x0, 0) == 2 && 1)
+ goto L10;
+ if (XVECLEN (x0, 0) == 5 && 1)
+ goto L423;
+ if (XVECLEN (x0, 0) == 4 && 1)
+ goto L437;
+ if (XVECLEN (x0, 0) == 3 && 1)
+ goto L513;
+ if (XVECLEN (x0, 0) == 6 && 1)
+ goto L1408;
+ break;
+ case CALL:
+ goto L1350;
+ case RETURN:
+ if (simple_386_epilogue ())
+ return 283;
+ break;
+ case CONST_INT:
+ if (XWINT (x0, 0) == 0 && 1)
+ return 284;
+ }
+ goto ret0;
+
+ L1404:
+ x1 = XVECEXP (x0, 0, 0);
+ if (memory_operand (x1, SImode))
+ {
+ ro[0] = x1;
+ return 282;
+ }
+ goto ret0;
+ L295:
+ return recog_4 (x0, insn, pnum_clobbers);
+
+ L10:
+ x1 = XVECEXP (x0, 0, 0);
+ switch (GET_CODE (x1))
+ {
+ case SET:
+ goto L336;
+ case CALL:
+ goto L1341;
+ }
+ goto ret0;
+ L336:
+ return recog_6 (x0, insn, pnum_clobbers);
+
+ L1341:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) == QImode && GET_CODE (x2) == MEM && 1)
+ goto L1342;
+ L1332:
+ if (call_insn_operand (x2, QImode))
+ {
+ ro[0] = x2;
+ goto L1333;
+ }
+ goto ret0;
+
+ L1342:
+ x3 = XEXP (x2, 0);
+ if (symbolic_operand (x3, SImode))
+ {
+ ro[0] = x3;
+ goto L1343;
+ }
+ goto L1332;
+
+ L1343:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ goto L1344;
+ }
+ x2 = XEXP (x1, 0);
+ goto L1332;
+
+ L1344:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == SET && 1)
+ goto L1345;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1332;
+
+ L1345:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == REG && XINT (x2, 0) == 7 && 1)
+ goto L1346;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1332;
+
+ L1346:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == PLUS && 1)
+ goto L1347;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1332;
+
+ L1347:
+ x3 = XEXP (x2, 0);
+ if (GET_MODE (x3) == SImode && GET_CODE (x3) == REG && XINT (x3, 0) == 7 && 1)
+ goto L1348;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1332;
+
+ L1348:
+ x3 = XEXP (x2, 1);
+ if (immediate_operand (x3, SImode))
+ {
+ ro[3] = x3;
+ if (!HALF_PIC_P ())
+ return 268;
+ }
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1332;
+
+ L1333:
+ x2 = XEXP (x1, 1);
+ if (general_operand (x2, SImode))
+ {
+ ro[1] = x2;
+ goto L1334;
+ }
+ goto ret0;
+
+ L1334:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == SET && 1)
+ goto L1335;
+ goto ret0;
+
+ L1335:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == REG && XINT (x2, 0) == 7 && 1)
+ goto L1336;
+ goto ret0;
+
+ L1336:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == PLUS && 1)
+ goto L1337;
+ goto ret0;
+
+ L1337:
+ x3 = XEXP (x2, 0);
+ if (GET_MODE (x3) == SImode && GET_CODE (x3) == REG && XINT (x3, 0) == 7 && 1)
+ goto L1338;
+ goto ret0;
+
+ L1338:
+ x3 = XEXP (x2, 1);
+ if (immediate_operand (x3, SImode))
+ {
+ ro[3] = x3;
+ return 267;
+ }
+ goto ret0;
+
+ L423:
+ x1 = XVECEXP (x0, 0, 0);
+ if (GET_CODE (x1) == SET && 1)
+ goto L424;
+ goto ret0;
+
+ L424:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) == DImode && general_operand (x2, DImode))
+ {
+ ro[0] = x2;
+ goto L425;
+ }
+ goto ret0;
+
+ L425:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == DImode && GET_CODE (x2) == FIX && 1)
+ goto L426;
+ goto ret0;
+
+ L426:
+ x3 = XEXP (x2, 0);
+ if (GET_CODE (x3) != FIX)
+ goto ret0;
+ switch (GET_MODE (x3))
+ {
+ case XFmode:
+ goto L427;
+ case DFmode:
+ goto L453;
+ case SFmode:
+ goto L479;
+ }
+ goto ret0;
+
+ L427:
+ x4 = XEXP (x3, 0);
+ if (register_operand (x4, XFmode))
+ {
+ ro[1] = x4;
+ goto L428;
+ }
+ goto ret0;
+
+ L428:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L429;
+ goto ret0;
+
+ L429:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[1]) && 1)
+ goto L430;
+ goto ret0;
+
+ L430:
+ x1 = XVECEXP (x0, 0, 2);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L431;
+ goto ret0;
+
+ L431:
+ x2 = XEXP (x1, 0);
+ if (memory_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ goto L432;
+ }
+ goto ret0;
+
+ L432:
+ x1 = XVECEXP (x0, 0, 3);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L433;
+ goto ret0;
+
+ L433:
+ x2 = XEXP (x1, 0);
+ if (memory_operand (x2, SImode))
+ {
+ ro[3] = x2;
+ goto L434;
+ }
+ goto ret0;
+
+ L434:
+ x1 = XVECEXP (x0, 0, 4);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L435;
+ goto ret0;
+
+ L435:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, SImode))
+ {
+ ro[4] = x2;
+ if (TARGET_80387)
+ return 87;
+ }
+ goto ret0;
+
+ L453:
+ x4 = XEXP (x3, 0);
+ if (register_operand (x4, DFmode))
+ {
+ ro[1] = x4;
+ goto L454;
+ }
+ goto ret0;
+
+ L454:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L455;
+ goto ret0;
+
+ L455:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[1]) && 1)
+ goto L456;
+ goto ret0;
+
+ L456:
+ x1 = XVECEXP (x0, 0, 2);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L457;
+ goto ret0;
+
+ L457:
+ x2 = XEXP (x1, 0);
+ if (memory_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ goto L458;
+ }
+ goto ret0;
+
+ L458:
+ x1 = XVECEXP (x0, 0, 3);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L459;
+ goto ret0;
+
+ L459:
+ x2 = XEXP (x1, 0);
+ if (memory_operand (x2, SImode))
+ {
+ ro[3] = x2;
+ goto L460;
+ }
+ goto ret0;
+
+ L460:
+ x1 = XVECEXP (x0, 0, 4);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L461;
+ goto ret0;
+
+ L461:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, SImode))
+ {
+ ro[4] = x2;
+ if (TARGET_80387)
+ return 88;
+ }
+ goto ret0;
+
+ L479:
+ x4 = XEXP (x3, 0);
+ if (register_operand (x4, SFmode))
+ {
+ ro[1] = x4;
+ goto L480;
+ }
+ goto ret0;
+
+ L480:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L481;
+ goto ret0;
+
+ L481:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[1]) && 1)
+ goto L482;
+ goto ret0;
+
+ L482:
+ x1 = XVECEXP (x0, 0, 2);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L483;
+ goto ret0;
+
+ L483:
+ x2 = XEXP (x1, 0);
+ if (memory_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ goto L484;
+ }
+ goto ret0;
+
+ L484:
+ x1 = XVECEXP (x0, 0, 3);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L485;
+ goto ret0;
+
+ L485:
+ x2 = XEXP (x1, 0);
+ if (memory_operand (x2, SImode))
+ {
+ ro[3] = x2;
+ goto L486;
+ }
+ goto ret0;
+
+ L486:
+ x1 = XVECEXP (x0, 0, 4);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L487;
+ goto ret0;
+
+ L487:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, SImode))
+ {
+ ro[4] = x2;
+ if (TARGET_80387)
+ return 89;
+ }
+ goto ret0;
+
+ L437:
+ x1 = XVECEXP (x0, 0, 0);
+ if (GET_CODE (x1) == SET && 1)
+ goto L438;
+ goto ret0;
+ L438:
+ return recog_7 (x0, insn, pnum_clobbers);
+
+ L513:
+ x1 = XVECEXP (x0, 0, 0);
+ switch (GET_CODE (x1))
+ {
+ case SET:
+ goto L514;
+ case CALL:
+ goto L1398;
+ }
+ goto ret0;
+
+ L514:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) == SImode && general_operand (x2, SImode))
+ {
+ ro[0] = x2;
+ goto L515;
+ }
+ goto ret0;
+
+ L515:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == FIX && 1)
+ goto L516;
+ goto ret0;
+
+ L516:
+ x3 = XEXP (x2, 0);
+ if (GET_CODE (x3) != FIX)
+ goto ret0;
+ switch (GET_MODE (x3))
+ {
+ case XFmode:
+ goto L517;
+ case DFmode:
+ goto L539;
+ case SFmode:
+ goto L561;
+ }
+ goto ret0;
+
+ L517:
+ x4 = XEXP (x3, 0);
+ if (register_operand (x4, XFmode))
+ {
+ ro[1] = x4;
+ goto L518;
+ }
+ goto ret0;
+
+ L518:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L519;
+ goto ret0;
+
+ L519:
+ x2 = XEXP (x1, 0);
+ if (memory_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ goto L520;
+ }
+ goto ret0;
+
+ L520:
+ x1 = XVECEXP (x0, 0, 2);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L521;
+ goto ret0;
+
+ L521:
+ x2 = XEXP (x1, 0);
+ if (pnum_clobbers != 0 && memory_operand (x2, SImode))
+ {
+ ro[3] = x2;
+ if (TARGET_80387)
+ {
+ *pnum_clobbers = 1;
+ return 93;
+ }
+ }
+ goto ret0;
+
+ L539:
+ x4 = XEXP (x3, 0);
+ if (register_operand (x4, DFmode))
+ {
+ ro[1] = x4;
+ goto L540;
+ }
+ goto ret0;
+
+ L540:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L541;
+ goto ret0;
+
+ L541:
+ x2 = XEXP (x1, 0);
+ if (memory_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ goto L542;
+ }
+ goto ret0;
+
+ L542:
+ x1 = XVECEXP (x0, 0, 2);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L543;
+ goto ret0;
+
+ L543:
+ x2 = XEXP (x1, 0);
+ if (pnum_clobbers != 0 && memory_operand (x2, SImode))
+ {
+ ro[3] = x2;
+ if (TARGET_80387)
+ {
+ *pnum_clobbers = 1;
+ return 94;
+ }
+ }
+ goto ret0;
+
+ L561:
+ x4 = XEXP (x3, 0);
+ if (register_operand (x4, SFmode))
+ {
+ ro[1] = x4;
+ goto L562;
+ }
+ goto ret0;
+
+ L562:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L563;
+ goto ret0;
+
+ L563:
+ x2 = XEXP (x1, 0);
+ if (memory_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ goto L564;
+ }
+ goto ret0;
+
+ L564:
+ x1 = XVECEXP (x0, 0, 2);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L565;
+ goto ret0;
+
+ L565:
+ x2 = XEXP (x1, 0);
+ if (pnum_clobbers != 0 && memory_operand (x2, SImode))
+ {
+ ro[3] = x2;
+ if (TARGET_80387)
+ {
+ *pnum_clobbers = 1;
+ return 95;
+ }
+ }
+ goto ret0;
+
+ L1398:
+ x2 = XEXP (x1, 0);
+ if (GET_MODE (x2) == QImode && GET_CODE (x2) == MEM && 1)
+ goto L1399;
+ L1392:
+ if (call_insn_operand (x2, QImode))
+ {
+ ro[0] = x2;
+ goto L1393;
+ }
+ goto ret0;
+
+ L1399:
+ x3 = XEXP (x2, 0);
+ if (symbolic_operand (x3, SImode))
+ {
+ ro[0] = x3;
+ goto L1400;
+ }
+ goto L1392;
+
+ L1400:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1)
+ goto L1401;
+ x2 = XEXP (x1, 0);
+ goto L1392;
+
+ L1401:
+ x1 = XVECEXP (x0, 0, 1);
+ if (memory_operand (x1, DImode))
+ {
+ ro[1] = x1;
+ goto L1402;
+ }
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1392;
+
+ L1402:
+ x1 = XVECEXP (x0, 0, 2);
+ ro[2] = x1;
+ if (!HALF_PIC_P ())
+ return 280;
+ x1 = XVECEXP (x0, 0, 0);
+ x2 = XEXP (x1, 0);
+ goto L1392;
+
+ L1393:
+ x2 = XEXP (x1, 1);
+ if (GET_CODE (x2) == CONST_INT && XWINT (x2, 0) == 0 && 1)
+ goto L1394;
+ goto ret0;
+
+ L1394:
+ x1 = XVECEXP (x0, 0, 1);
+ if (memory_operand (x1, DImode))
+ {
+ ro[1] = x1;
+ goto L1395;
+ }
+ goto ret0;
+
+ L1395:
+ x1 = XVECEXP (x0, 0, 2);
+ ro[2] = x1;
+ return 279;
+
+ L1408:
+ x1 = XVECEXP (x0, 0, 0);
+ if (GET_CODE (x1) == SET && 1)
+ goto L1409;
+ goto ret0;
+
+ L1409:
+ x2 = XEXP (x1, 0);
+ switch (GET_MODE (x2))
+ {
+ case BLKmode:
+ if (GET_CODE (x2) == MEM && 1)
+ goto L1410;
+ break;
+ case SImode:
+ if (general_operand (x2, SImode))
+ {
+ ro[0] = x2;
+ goto L1426;
+ }
+ }
+ if (GET_CODE (x2) == CC0 && 1)
+ goto L1444;
+ goto ret0;
+
+ L1410:
+ x3 = XEXP (x2, 0);
+ if (address_operand (x3, SImode))
+ {
+ ro[0] = x3;
+ goto L1411;
+ }
+ goto ret0;
+
+ L1411:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == BLKmode && GET_CODE (x2) == MEM && 1)
+ goto L1412;
+ goto ret0;
+
+ L1412:
+ x3 = XEXP (x2, 0);
+ if (address_operand (x3, SImode))
+ {
+ ro[1] = x3;
+ goto L1413;
+ }
+ goto ret0;
+
+ L1413:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == USE && 1)
+ goto L1414;
+ goto ret0;
+
+ L1414:
+ x2 = XEXP (x1, 0);
+ if (GET_CODE (x2) == CONST_INT && 1)
+ {
+ ro[2] = x2;
+ goto L1415;
+ }
+ goto ret0;
+
+ L1415:
+ x1 = XVECEXP (x0, 0, 2);
+ if (GET_CODE (x1) == USE && 1)
+ goto L1416;
+ goto ret0;
+
+ L1416:
+ x2 = XEXP (x1, 0);
+ if (immediate_operand (x2, SImode))
+ {
+ ro[3] = x2;
+ goto L1417;
+ }
+ goto ret0;
+
+ L1417:
+ x1 = XVECEXP (x0, 0, 3);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L1418;
+ goto ret0;
+
+ L1418:
+ x2 = XEXP (x1, 0);
+ if (scratch_operand (x2, SImode))
+ {
+ ro[4] = x2;
+ goto L1419;
+ }
+ goto ret0;
+
+ L1419:
+ x1 = XVECEXP (x0, 0, 4);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L1420;
+ goto ret0;
+
+ L1420:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[0]) && 1)
+ goto L1421;
+ goto ret0;
+
+ L1421:
+ x1 = XVECEXP (x0, 0, 5);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L1422;
+ goto ret0;
+
+ L1422:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[1]) && 1)
+ return 286;
+ goto ret0;
+
+ L1426:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == COMPARE && 1)
+ goto L1427;
+ goto ret0;
+
+ L1427:
+ x3 = XEXP (x2, 0);
+ if (GET_MODE (x3) == BLKmode && GET_CODE (x3) == MEM && 1)
+ goto L1428;
+ goto ret0;
+
+ L1428:
+ x4 = XEXP (x3, 0);
+ if (address_operand (x4, SImode))
+ {
+ ro[1] = x4;
+ goto L1429;
+ }
+ goto ret0;
+
+ L1429:
+ x3 = XEXP (x2, 1);
+ if (GET_MODE (x3) == BLKmode && GET_CODE (x3) == MEM && 1)
+ goto L1430;
+ goto ret0;
+
+ L1430:
+ x4 = XEXP (x3, 0);
+ if (address_operand (x4, SImode))
+ {
+ ro[2] = x4;
+ goto L1431;
+ }
+ goto ret0;
+
+ L1431:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == USE && 1)
+ goto L1432;
+ goto ret0;
+
+ L1432:
+ x2 = XEXP (x1, 0);
+ if (register_operand (x2, SImode))
+ {
+ ro[3] = x2;
+ goto L1433;
+ }
+ goto ret0;
+
+ L1433:
+ x1 = XVECEXP (x0, 0, 2);
+ if (GET_CODE (x1) == USE && 1)
+ goto L1434;
+ goto ret0;
+
+ L1434:
+ x2 = XEXP (x1, 0);
+ if (immediate_operand (x2, SImode))
+ {
+ ro[4] = x2;
+ goto L1435;
+ }
+ goto ret0;
+
+ L1435:
+ x1 = XVECEXP (x0, 0, 3);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L1436;
+ goto ret0;
+
+ L1436:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[1]) && 1)
+ goto L1437;
+ goto ret0;
+
+ L1437:
+ x1 = XVECEXP (x0, 0, 4);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L1438;
+ goto ret0;
+
+ L1438:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[2]) && 1)
+ goto L1439;
+ goto ret0;
+
+ L1439:
+ x1 = XVECEXP (x0, 0, 5);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L1440;
+ goto ret0;
+
+ L1440:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[3]) && 1)
+ return 288;
+ goto ret0;
+
+ L1444:
+ x2 = XEXP (x1, 1);
+ if (GET_MODE (x2) == SImode && GET_CODE (x2) == COMPARE && 1)
+ goto L1445;
+ goto ret0;
+
+ L1445:
+ x3 = XEXP (x2, 0);
+ if (GET_MODE (x3) == BLKmode && GET_CODE (x3) == MEM && 1)
+ goto L1446;
+ goto ret0;
+
+ L1446:
+ x4 = XEXP (x3, 0);
+ if (address_operand (x4, SImode))
+ {
+ ro[0] = x4;
+ goto L1447;
+ }
+ goto ret0;
+
+ L1447:
+ x3 = XEXP (x2, 1);
+ if (GET_MODE (x3) == BLKmode && GET_CODE (x3) == MEM && 1)
+ goto L1448;
+ goto ret0;
+
+ L1448:
+ x4 = XEXP (x3, 0);
+ if (address_operand (x4, SImode))
+ {
+ ro[1] = x4;
+ goto L1449;
+ }
+ goto ret0;
+
+ L1449:
+ x1 = XVECEXP (x0, 0, 1);
+ if (GET_CODE (x1) == USE && 1)
+ goto L1450;
+ goto ret0;
+
+ L1450:
+ x2 = XEXP (x1, 0);
+ if (register_operand (x2, SImode))
+ {
+ ro[2] = x2;
+ goto L1451;
+ }
+ goto ret0;
+
+ L1451:
+ x1 = XVECEXP (x0, 0, 2);
+ if (GET_CODE (x1) == USE && 1)
+ goto L1452;
+ goto ret0;
+
+ L1452:
+ x2 = XEXP (x1, 0);
+ if (immediate_operand (x2, SImode))
+ {
+ ro[3] = x2;
+ goto L1453;
+ }
+ goto ret0;
+
+ L1453:
+ x1 = XVECEXP (x0, 0, 3);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L1454;
+ goto ret0;
+
+ L1454:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[0]) && 1)
+ goto L1455;
+ goto ret0;
+
+ L1455:
+ x1 = XVECEXP (x0, 0, 4);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L1456;
+ goto ret0;
+
+ L1456:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[1]) && 1)
+ goto L1457;
+ goto ret0;
+
+ L1457:
+ x1 = XVECEXP (x0, 0, 5);
+ if (GET_CODE (x1) == CLOBBER && 1)
+ goto L1458;
+ goto ret0;
+
+ L1458:
+ x2 = XEXP (x1, 0);
+ if (rtx_equal_p (x2, ro[2]) && 1)
+ return 289;
+ goto ret0;
+
+ L1350:
+ x1 = XEXP (x0, 0);
+ if (call_insn_operand (x1, QImode))
+ {
+ ro[0] = x1;
+ goto L1351;
+ }
+ L1353:
+ if (GET_MODE (x1) == QImode && GET_CODE (x1) == MEM && 1)
+ goto L1354;
+ goto ret0;
+
+ L1351:
+ x1 = XEXP (x0, 1);
+ if (general_operand (x1, SImode))
+ {
+ ro[1] = x1;
+ return 270;
+ }
+ x1 = XEXP (x0, 0);
+ goto L1353;
+
+ L1354:
+ x2 = XEXP (x1, 0);
+ if (symbolic_operand (x2, SImode))
+ {
+ ro[0] = x2;
+ goto L1355;
+ }
+ goto ret0;
+
+ L1355:
+ x1 = XEXP (x0, 1);
+ if (general_operand (x1, SImode))
+ {
+ ro[1] = x1;
+ if (!HALF_PIC_P ())
+ return 271;
+ }
+ goto ret0;
+ ret0: return -1;
+}
+
+rtx
+split_insns (x0, insn)
+ register rtx x0;
+ rtx insn;
+{
+ register rtx *ro = &recog_operand[0];
+ register rtx x1, x2, x3, x4, x5, x6;
+ rtx tem;
+
+ goto ret0;
+ ret0: return 0;
+}
+
diff --git a/gnu/usr.bin/cc/cc_int/integrate.c b/gnu/usr.bin/cc/cc_int/integrate.c
new file mode 100644
index 0000000..63b150d
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/integrate.c
@@ -0,0 +1,3035 @@
+/* Procedure integration for GNU CC.
+ Copyright (C) 1988, 1991, 1993, 1994 Free Software Foundation, Inc.
+ Contributed by Michael Tiemann (tiemann@cygnus.com)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include <stdio.h>
+
+#include "config.h"
+#include "rtl.h"
+#include "tree.h"
+#include "flags.h"
+#include "insn-config.h"
+#include "insn-flags.h"
+#include "expr.h"
+#include "output.h"
+#include "integrate.h"
+#include "real.h"
+#include "function.h"
+#include "bytecode.h"
+
+#include "obstack.h"
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+extern struct obstack *function_maybepermanent_obstack;
+
+extern tree pushdecl ();
+extern tree poplevel ();
+
+/* Similar, but round to the next highest integer that meets the
+ alignment. */
+#define CEIL_ROUND(VALUE,ALIGN) (((VALUE) + (ALIGN) - 1) & ~((ALIGN)- 1))
+
+/* Default max number of insns a function can have and still be inline.
+ This is overridden on RISC machines. */
+#ifndef INTEGRATE_THRESHOLD
+#define INTEGRATE_THRESHOLD(DECL) \
+ (8 * (8 + list_length (DECL_ARGUMENTS (DECL))))
+#endif
+
+static rtx initialize_for_inline PROTO((tree, int, int, int, int));
+static void finish_inline PROTO((tree, rtx));
+static void adjust_copied_decl_tree PROTO((tree));
+static tree copy_decl_list PROTO((tree));
+static tree copy_decl_tree PROTO((tree));
+static void copy_decl_rtls PROTO((tree));
+static void save_constants PROTO((rtx *));
+static void note_modified_parmregs PROTO((rtx, rtx));
+static rtx copy_for_inline PROTO((rtx));
+static void integrate_parm_decls PROTO((tree, struct inline_remap *, rtvec));
+static void integrate_decl_tree PROTO((tree, int, struct inline_remap *));
+static void subst_constants PROTO((rtx *, rtx, struct inline_remap *));
+static void restore_constants PROTO((rtx *));
+static void set_block_origin_self PROTO((tree));
+static void set_decl_origin_self PROTO((tree));
+static void set_block_abstract_flags PROTO((tree, int));
+
+void set_decl_abstract_flags PROTO((tree, int));
+
+/* Zero if the current function (whose FUNCTION_DECL is FNDECL)
+ is safe and reasonable to integrate into other functions.
+ Nonzero means value is a warning message with a single %s
+ for the function's name. */
+
+char *
+function_cannot_inline_p (fndecl)
+ register tree fndecl;
+{
+ register rtx insn;
+ tree last = tree_last (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
+ int max_insns = INTEGRATE_THRESHOLD (fndecl);
+ register int ninsns = 0;
+ register tree parms;
+
+ /* No inlines with varargs. `grokdeclarator' gives a warning
+ message about that if `inline' is specified. This code
+ it put in to catch the volunteers. */
+ if ((last && TREE_VALUE (last) != void_type_node)
+ || current_function_varargs)
+ return "varargs function cannot be inline";
+
+ if (current_function_calls_alloca)
+ return "function using alloca cannot be inline";
+
+ if (current_function_contains_functions)
+ return "function with nested functions cannot be inline";
+
+ /* If its not even close, don't even look. */
+ if (!DECL_INLINE (fndecl) && get_max_uid () > 3 * max_insns)
+ return "function too large to be inline";
+
+#if 0
+ /* Large stacks are OK now that inlined functions can share them. */
+ /* Don't inline functions with large stack usage,
+ since they can make other recursive functions burn up stack. */
+ if (!DECL_INLINE (fndecl) && get_frame_size () > 100)
+ return "function stack frame for inlining";
+#endif
+
+#if 0
+ /* Don't inline functions which do not specify a function prototype and
+ have BLKmode argument or take the address of a parameter. */
+ for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms))
+ {
+ if (TYPE_MODE (TREE_TYPE (parms)) == BLKmode)
+ TREE_ADDRESSABLE (parms) = 1;
+ if (last == NULL_TREE && TREE_ADDRESSABLE (parms))
+ return "no prototype, and parameter address used; cannot be inline";
+ }
+#endif
+
+ /* We can't inline functions that return structures
+ the old-fashioned PCC way, copying into a static block. */
+ if (current_function_returns_pcc_struct)
+ return "inline functions not supported for this return value type";
+
+ /* We can't inline functions that return structures of varying size. */
+ if (int_size_in_bytes (TREE_TYPE (TREE_TYPE (fndecl))) < 0)
+ return "function with varying-size return value cannot be inline";
+
+ /* Cannot inline a function with a varying size argument. */
+ for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms))
+ if (int_size_in_bytes (TREE_TYPE (parms)) < 0)
+ return "function with varying-size parameter cannot be inline";
+
+ if (!DECL_INLINE (fndecl) && get_max_uid () > max_insns)
+ {
+ for (ninsns = 0, insn = get_first_nonparm_insn (); insn && ninsns < max_insns;
+ insn = NEXT_INSN (insn))
+ {
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ ninsns++;
+ }
+
+ if (ninsns >= max_insns)
+ return "function too large to be inline";
+ }
+
+ /* We cannot inline this function if forced_labels is non-zero. This
+ implies that a label in this function was used as an initializer.
+ Because labels can not be duplicated, all labels in the function
+ will be renamed when it is inlined. However, there is no way to find
+ and fix all variables initialized with addresses of labels in this
+ function, hence inlining is impossible. */
+
+ if (forced_labels)
+ return "function with label addresses used in initializers cannot inline";
+
+ /* We cannot inline a nested function that jumps to a nonlocal label. */
+ if (current_function_has_nonlocal_goto)
+ return "function with nonlocal goto cannot be inline";
+
+ return 0;
+}
+
+/* Variables used within save_for_inline. */
+
+/* Mapping from old pseudo-register to new pseudo-registers.
+ The first element of this map is reg_map[FIRST_PSEUDO_REGISTER].
+ It is allocated in `save_for_inline' and `expand_inline_function',
+ and deallocated on exit from each of those routines. */
+static rtx *reg_map;
+
+/* Mapping from old code-labels to new code-labels.
+ The first element of this map is label_map[min_labelno].
+ It is allocated in `save_for_inline' and `expand_inline_function',
+ and deallocated on exit from each of those routines. */
+static rtx *label_map;
+
+/* Mapping from old insn uid's to copied insns.
+ It is allocated in `save_for_inline' and `expand_inline_function',
+ and deallocated on exit from each of those routines. */
+static rtx *insn_map;
+
+/* Map pseudo reg number into the PARM_DECL for the parm living in the reg.
+ Zero for a reg that isn't a parm's home.
+ Only reg numbers less than max_parm_reg are mapped here. */
+static tree *parmdecl_map;
+
+/* Keep track of first pseudo-register beyond those that are parms. */
+static int max_parm_reg;
+
+/* When an insn is being copied by copy_for_inline,
+ this is nonzero if we have copied an ASM_OPERANDS.
+ In that case, it is the original input-operand vector. */
+static rtvec orig_asm_operands_vector;
+
+/* When an insn is being copied by copy_for_inline,
+ this is nonzero if we have copied an ASM_OPERANDS.
+ In that case, it is the copied input-operand vector. */
+static rtvec copy_asm_operands_vector;
+
+/* Likewise, this is the copied constraints vector. */
+static rtvec copy_asm_constraints_vector;
+
+/* In save_for_inline, nonzero if past the parm-initialization insns. */
+static int in_nonparm_insns;
+
+/* Subroutine for `save_for_inline{copying,nocopy}'. Performs initialization
+ needed to save FNDECL's insns and info for future inline expansion. */
+
+static rtx
+initialize_for_inline (fndecl, min_labelno, max_labelno, max_reg, copy)
+ tree fndecl;
+ int min_labelno;
+ int max_labelno;
+ int max_reg;
+ int copy;
+{
+ int function_flags, i;
+ rtvec arg_vector;
+ tree parms;
+
+ /* Compute the values of any flags we must restore when inlining this. */
+
+ function_flags
+ = (current_function_calls_alloca * FUNCTION_FLAGS_CALLS_ALLOCA
+ + current_function_calls_setjmp * FUNCTION_FLAGS_CALLS_SETJMP
+ + current_function_calls_longjmp * FUNCTION_FLAGS_CALLS_LONGJMP
+ + current_function_returns_struct * FUNCTION_FLAGS_RETURNS_STRUCT
+ + current_function_returns_pcc_struct * FUNCTION_FLAGS_RETURNS_PCC_STRUCT
+ + current_function_needs_context * FUNCTION_FLAGS_NEEDS_CONTEXT
+ + current_function_has_nonlocal_label * FUNCTION_FLAGS_HAS_NONLOCAL_LABEL
+ + current_function_returns_pointer * FUNCTION_FLAGS_RETURNS_POINTER
+ + current_function_uses_const_pool * FUNCTION_FLAGS_USES_CONST_POOL
+ + current_function_uses_pic_offset_table * FUNCTION_FLAGS_USES_PIC_OFFSET_TABLE);
+
+ /* Clear out PARMDECL_MAP. It was allocated in the caller's frame. */
+ bzero ((char *) parmdecl_map, max_parm_reg * sizeof (tree));
+ arg_vector = rtvec_alloc (list_length (DECL_ARGUMENTS (fndecl)));
+
+ for (parms = DECL_ARGUMENTS (fndecl), i = 0;
+ parms;
+ parms = TREE_CHAIN (parms), i++)
+ {
+ rtx p = DECL_RTL (parms);
+
+ if (GET_CODE (p) == MEM && copy)
+ {
+ /* Copy the rtl so that modifications of the addresses
+ later in compilation won't affect this arg_vector.
+ Virtual register instantiation can screw the address
+ of the rtl. */
+ rtx new = copy_rtx (p);
+
+ /* Don't leave the old copy anywhere in this decl. */
+ if (DECL_RTL (parms) == DECL_INCOMING_RTL (parms)
+ || (GET_CODE (DECL_RTL (parms)) == MEM
+ && GET_CODE (DECL_INCOMING_RTL (parms)) == MEM
+ && (XEXP (DECL_RTL (parms), 0)
+ == XEXP (DECL_INCOMING_RTL (parms), 0))))
+ DECL_INCOMING_RTL (parms) = new;
+ DECL_RTL (parms) = new;
+ }
+
+ RTVEC_ELT (arg_vector, i) = p;
+
+ if (GET_CODE (p) == REG)
+ parmdecl_map[REGNO (p)] = parms;
+ else if (GET_CODE (p) == CONCAT)
+ {
+ rtx preal = gen_realpart (GET_MODE (XEXP (p, 0)), p);
+ rtx pimag = gen_imagpart (GET_MODE (preal), p);
+
+ if (GET_CODE (preal) == REG)
+ parmdecl_map[REGNO (preal)] = parms;
+ if (GET_CODE (pimag) == REG)
+ parmdecl_map[REGNO (pimag)] = parms;
+ }
+
+ /* This flag is cleared later
+ if the function ever modifies the value of the parm. */
+ TREE_READONLY (parms) = 1;
+ }
+
+ /* Assume we start out in the insns that set up the parameters. */
+ in_nonparm_insns = 0;
+
+ /* The list of DECL_SAVED_INSNS, starts off with a header which
+ contains the following information:
+
+ the first insn of the function (not including the insns that copy
+ parameters into registers).
+ the first parameter insn of the function,
+ the first label used by that function,
+ the last label used by that function,
+ the highest register number used for parameters,
+ the total number of registers used,
+ the size of the incoming stack area for parameters,
+ the number of bytes popped on return,
+ the stack slot list,
+ some flags that are used to restore compiler globals,
+ the value of current_function_outgoing_args_size,
+ the original argument vector,
+ and the original DECL_INITIAL. */
+
+ return gen_inline_header_rtx (NULL_RTX, NULL_RTX, min_labelno, max_labelno,
+ max_parm_reg, max_reg,
+ current_function_args_size,
+ current_function_pops_args,
+ stack_slot_list, function_flags,
+ current_function_outgoing_args_size,
+ arg_vector, (rtx) DECL_INITIAL (fndecl));
+}
+
+/* Subroutine for `save_for_inline{copying,nocopy}'. Finishes up the
+ things that must be done to make FNDECL expandable as an inline function.
+ HEAD contains the chain of insns to which FNDECL will expand. */
+
+static void
+finish_inline (fndecl, head)
+ tree fndecl;
+ rtx head;
+{
+ NEXT_INSN (head) = get_first_nonparm_insn ();
+ FIRST_PARM_INSN (head) = get_insns ();
+ DECL_SAVED_INSNS (fndecl) = head;
+ DECL_FRAME_SIZE (fndecl) = get_frame_size ();
+ DECL_INLINE (fndecl) = 1;
+}
+
+/* Adjust the BLOCK_END_NOTE pointers in a given copied DECL tree so that
+ they all point to the new (copied) rtxs. */
+
+static void
+adjust_copied_decl_tree (block)
+ register tree block;
+{
+ register tree subblock;
+ register rtx original_end;
+
+ original_end = BLOCK_END_NOTE (block);
+ if (original_end)
+ {
+ BLOCK_END_NOTE (block) = (rtx) NOTE_SOURCE_FILE (original_end);
+ NOTE_SOURCE_FILE (original_end) = 0;
+ }
+
+ /* Process all subblocks. */
+ for (subblock = BLOCK_SUBBLOCKS (block);
+ subblock;
+ subblock = TREE_CHAIN (subblock))
+ adjust_copied_decl_tree (subblock);
+}
+
+/* Make the insns and PARM_DECLs of the current function permanent
+ and record other information in DECL_SAVED_INSNS to allow inlining
+ of this function in subsequent calls.
+
+ This function is called when we are going to immediately compile
+ the insns for FNDECL. The insns in maybepermanent_obstack cannot be
+ modified by the compilation process, so we copy all of them to
+ new storage and consider the new insns to be the insn chain to be
+ compiled. Our caller (rest_of_compilation) saves the original
+ DECL_INITIAL and DECL_ARGUMENTS; here we copy them. */
+
+void
+save_for_inline_copying (fndecl)
+ tree fndecl;
+{
+ rtx first_insn, last_insn, insn;
+ rtx head, copy;
+ int max_labelno, min_labelno, i, len;
+ int max_reg;
+ int max_uid;
+ rtx first_nonparm_insn;
+
+ /* Make and emit a return-label if we have not already done so.
+ Do this before recording the bounds on label numbers. */
+
+ if (return_label == 0)
+ {
+ return_label = gen_label_rtx ();
+ emit_label (return_label);
+ }
+
+ /* Get some bounds on the labels and registers used. */
+
+ max_labelno = max_label_num ();
+ min_labelno = get_first_label_num ();
+ max_reg = max_reg_num ();
+
+ /* Set up PARMDECL_MAP which maps pseudo-reg number to its PARM_DECL.
+ Later we set TREE_READONLY to 0 if the parm is modified inside the fn.
+ Also set up ARG_VECTOR, which holds the unmodified DECL_RTX values
+ for the parms, prior to elimination of virtual registers.
+ These values are needed for substituting parms properly. */
+
+ max_parm_reg = max_parm_reg_num ();
+ parmdecl_map = (tree *) alloca (max_parm_reg * sizeof (tree));
+
+ head = initialize_for_inline (fndecl, min_labelno, max_labelno, max_reg, 1);
+
+ if (current_function_uses_const_pool)
+ {
+ /* Replace any constant pool references with the actual constant. We
+ will put the constants back in the copy made below. */
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ save_constants (&PATTERN (insn));
+ if (REG_NOTES (insn))
+ save_constants (&REG_NOTES (insn));
+ }
+
+ /* Clear out the constant pool so that we can recreate it with the
+ copied constants below. */
+ init_const_rtx_hash_table ();
+ clear_const_double_mem ();
+ }
+
+ max_uid = INSN_UID (head);
+
+ /* We have now allocated all that needs to be allocated permanently
+ on the rtx obstack. Set our high-water mark, so that we
+ can free the rest of this when the time comes. */
+
+ preserve_data ();
+
+ /* Copy the chain insns of this function.
+ Install the copied chain as the insns of this function,
+ for continued compilation;
+ the original chain is recorded as the DECL_SAVED_INSNS
+ for inlining future calls. */
+
+ /* If there are insns that copy parms from the stack into pseudo registers,
+ those insns are not copied. `expand_inline_function' must
+ emit the correct code to handle such things. */
+
+ insn = get_insns ();
+ if (GET_CODE (insn) != NOTE)
+ abort ();
+ first_insn = rtx_alloc (NOTE);
+ NOTE_SOURCE_FILE (first_insn) = NOTE_SOURCE_FILE (insn);
+ NOTE_LINE_NUMBER (first_insn) = NOTE_LINE_NUMBER (insn);
+ INSN_UID (first_insn) = INSN_UID (insn);
+ PREV_INSN (first_insn) = NULL;
+ NEXT_INSN (first_insn) = NULL;
+ last_insn = first_insn;
+
+ /* Each pseudo-reg in the old insn chain must have a unique rtx in the copy.
+ Make these new rtx's now, and install them in regno_reg_rtx, so they
+ will be the official pseudo-reg rtx's for the rest of compilation. */
+
+ reg_map = (rtx *) alloca ((max_reg + 1) * sizeof (rtx));
+
+ len = sizeof (struct rtx_def) + (GET_RTX_LENGTH (REG) - 1) * sizeof (rtunion);
+ for (i = max_reg - 1; i > LAST_VIRTUAL_REGISTER; i--)
+ reg_map[i] = (rtx)obstack_copy (function_maybepermanent_obstack,
+ regno_reg_rtx[i], len);
+
+ bcopy ((char *) (reg_map + LAST_VIRTUAL_REGISTER + 1),
+ (char *) (regno_reg_rtx + LAST_VIRTUAL_REGISTER + 1),
+ (max_reg - (LAST_VIRTUAL_REGISTER + 1)) * sizeof (rtx));
+
+ /* Likewise each label rtx must have a unique rtx as its copy. */
+
+ label_map = (rtx *)alloca ((max_labelno - min_labelno) * sizeof (rtx));
+ label_map -= min_labelno;
+
+ for (i = min_labelno; i < max_labelno; i++)
+ label_map[i] = gen_label_rtx ();
+
+ /* Record the mapping of old insns to copied insns. */
+
+ insn_map = (rtx *) alloca (max_uid * sizeof (rtx));
+ bzero ((char *) insn_map, max_uid * sizeof (rtx));
+
+ /* Get the insn which signals the end of parameter setup code. */
+ first_nonparm_insn = get_first_nonparm_insn ();
+
+ /* Copy any entries in regno_reg_rtx or DECL_RTLs that reference MEM
+ (the former occurs when a variable has its address taken)
+ since these may be shared and can be changed by virtual
+ register instantiation. DECL_RTL values for our arguments
+ have already been copied by initialize_for_inline. */
+ for (i = LAST_VIRTUAL_REGISTER + 1; i < max_reg; i++)
+ if (GET_CODE (regno_reg_rtx[i]) == MEM)
+ XEXP (regno_reg_rtx[i], 0)
+ = copy_for_inline (XEXP (regno_reg_rtx[i], 0));
+
+ /* Copy the tree of subblocks of the function, and the decls in them.
+ We will use the copy for compiling this function, then restore the original
+ subblocks and decls for use when inlining this function.
+
+ Several parts of the compiler modify BLOCK trees. In particular,
+ instantiate_virtual_regs will instantiate any virtual regs
+ mentioned in the DECL_RTLs of the decls, and loop
+ unrolling will replicate any BLOCK trees inside an unrolled loop.
+
+ The modified subblocks or DECL_RTLs would be incorrect for the original rtl
+ which we will use for inlining. The rtl might even contain pseudoregs
+ whose space has been freed. */
+
+ DECL_INITIAL (fndecl) = copy_decl_tree (DECL_INITIAL (fndecl));
+ DECL_ARGUMENTS (fndecl) = copy_decl_list (DECL_ARGUMENTS (fndecl));
+
+ /* Now copy each DECL_RTL which is a MEM,
+ so it is safe to modify their addresses. */
+ copy_decl_rtls (DECL_INITIAL (fndecl));
+
+ /* The fndecl node acts as its own progenitor, so mark it as such. */
+ DECL_ABSTRACT_ORIGIN (fndecl) = fndecl;
+
+ /* Now copy the chain of insns. Do this twice. The first copy the insn
+ itself and its body. The second time copy of REG_NOTES. This is because
+ a REG_NOTE may have a forward pointer to another insn. */
+
+ for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn))
+ {
+ orig_asm_operands_vector = 0;
+
+ if (insn == first_nonparm_insn)
+ in_nonparm_insns = 1;
+
+ switch (GET_CODE (insn))
+ {
+ case NOTE:
+ /* No need to keep these. */
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED)
+ continue;
+
+ copy = rtx_alloc (NOTE);
+ NOTE_LINE_NUMBER (copy) = NOTE_LINE_NUMBER (insn);
+ if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_BLOCK_END)
+ NOTE_SOURCE_FILE (copy) = NOTE_SOURCE_FILE (insn);
+ else
+ {
+ NOTE_SOURCE_FILE (insn) = (char *) copy;
+ NOTE_SOURCE_FILE (copy) = 0;
+ }
+ break;
+
+ case INSN:
+ case JUMP_INSN:
+ case CALL_INSN:
+ copy = rtx_alloc (GET_CODE (insn));
+
+ if (GET_CODE (insn) == CALL_INSN)
+ CALL_INSN_FUNCTION_USAGE (copy) =
+ copy_for_inline (CALL_INSN_FUNCTION_USAGE (insn));
+
+ PATTERN (copy) = copy_for_inline (PATTERN (insn));
+ INSN_CODE (copy) = -1;
+ LOG_LINKS (copy) = NULL_RTX;
+ RTX_INTEGRATED_P (copy) = RTX_INTEGRATED_P (insn);
+ break;
+
+ case CODE_LABEL:
+ copy = label_map[CODE_LABEL_NUMBER (insn)];
+ LABEL_NAME (copy) = LABEL_NAME (insn);
+ break;
+
+ case BARRIER:
+ copy = rtx_alloc (BARRIER);
+ break;
+
+ default:
+ abort ();
+ }
+ INSN_UID (copy) = INSN_UID (insn);
+ insn_map[INSN_UID (insn)] = copy;
+ NEXT_INSN (last_insn) = copy;
+ PREV_INSN (copy) = last_insn;
+ last_insn = copy;
+ }
+
+ adjust_copied_decl_tree (DECL_INITIAL (fndecl));
+
+ /* Now copy the REG_NOTES. */
+ for (insn = NEXT_INSN (get_insns ()); insn; insn = NEXT_INSN (insn))
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && insn_map[INSN_UID(insn)])
+ REG_NOTES (insn_map[INSN_UID (insn)])
+ = copy_for_inline (REG_NOTES (insn));
+
+ NEXT_INSN (last_insn) = NULL;
+
+ finish_inline (fndecl, head);
+
+ set_new_first_and_last_insn (first_insn, last_insn);
+}
+
+/* Return a copy of a chain of nodes, chained through the TREE_CHAIN field.
+ For example, this can copy a list made of TREE_LIST nodes. While copying,
+ for each node copied which doesn't already have is DECL_ABSTRACT_ORIGIN
+ set to some non-zero value, set the DECL_ABSTRACT_ORIGIN of the copy to
+ point to the corresponding (abstract) original node. */
+
+static tree
+copy_decl_list (list)
+ tree list;
+{
+ tree head;
+ register tree prev, next;
+
+ if (list == 0)
+ return 0;
+
+ head = prev = copy_node (list);
+ if (DECL_ABSTRACT_ORIGIN (head) == NULL_TREE)
+ DECL_ABSTRACT_ORIGIN (head) = list;
+ next = TREE_CHAIN (list);
+ while (next)
+ {
+ register tree copy;
+
+ copy = copy_node (next);
+ if (DECL_ABSTRACT_ORIGIN (copy) == NULL_TREE)
+ DECL_ABSTRACT_ORIGIN (copy) = next;
+ TREE_CHAIN (prev) = copy;
+ prev = copy;
+ next = TREE_CHAIN (next);
+ }
+ return head;
+}
+
+/* Make a copy of the entire tree of blocks BLOCK, and return it. */
+
+static tree
+copy_decl_tree (block)
+ tree block;
+{
+ tree t, vars, subblocks;
+
+ vars = copy_decl_list (BLOCK_VARS (block));
+ subblocks = 0;
+
+ /* Process all subblocks. */
+ for (t = BLOCK_SUBBLOCKS (block); t; t = TREE_CHAIN (t))
+ {
+ tree copy = copy_decl_tree (t);
+ TREE_CHAIN (copy) = subblocks;
+ subblocks = copy;
+ }
+
+ t = copy_node (block);
+ BLOCK_VARS (t) = vars;
+ BLOCK_SUBBLOCKS (t) = nreverse (subblocks);
+ /* If the BLOCK being cloned is already marked as having been instantiated
+ from something else, then leave that `origin' marking alone. Elsewise,
+ mark the clone as having originated from the BLOCK we are cloning. */
+ if (BLOCK_ABSTRACT_ORIGIN (t) == NULL_TREE)
+ BLOCK_ABSTRACT_ORIGIN (t) = block;
+ return t;
+}
+
+/* Copy DECL_RTLs in all decls in the given BLOCK node. */
+
+static void
+copy_decl_rtls (block)
+ tree block;
+{
+ tree t;
+
+ for (t = BLOCK_VARS (block); t; t = TREE_CHAIN (t))
+ if (DECL_RTL (t) && GET_CODE (DECL_RTL (t)) == MEM)
+ DECL_RTL (t) = copy_for_inline (DECL_RTL (t));
+
+ /* Process all subblocks. */
+ for (t = BLOCK_SUBBLOCKS (block); t; t = TREE_CHAIN (t))
+ copy_decl_rtls (t);
+}
+
+/* Make the insns and PARM_DECLs of the current function permanent
+ and record other information in DECL_SAVED_INSNS to allow inlining
+ of this function in subsequent calls.
+
+ This routine need not copy any insns because we are not going
+ to immediately compile the insns in the insn chain. There
+ are two cases when we would compile the insns for FNDECL:
+ (1) when FNDECL is expanded inline, and (2) when FNDECL needs to
+ be output at the end of other compilation, because somebody took
+ its address. In the first case, the insns of FNDECL are copied
+ as it is expanded inline, so FNDECL's saved insns are not
+ modified. In the second case, FNDECL is used for the last time,
+ so modifying the rtl is not a problem.
+
+ ??? Actually, we do not verify that FNDECL is not inline expanded
+ by other functions which must also be written down at the end
+ of compilation. We could set flag_no_inline to nonzero when
+ the time comes to write down such functions. */
+
+void
+save_for_inline_nocopy (fndecl)
+ tree fndecl;
+{
+ rtx insn;
+ rtx head;
+ rtx first_nonparm_insn;
+
+ /* Set up PARMDECL_MAP which maps pseudo-reg number to its PARM_DECL.
+ Later we set TREE_READONLY to 0 if the parm is modified inside the fn.
+ Also set up ARG_VECTOR, which holds the unmodified DECL_RTX values
+ for the parms, prior to elimination of virtual registers.
+ These values are needed for substituting parms properly. */
+
+ max_parm_reg = max_parm_reg_num ();
+ parmdecl_map = (tree *) alloca (max_parm_reg * sizeof (tree));
+
+ /* Make and emit a return-label if we have not already done so. */
+
+ if (return_label == 0)
+ {
+ return_label = gen_label_rtx ();
+ emit_label (return_label);
+ }
+
+ head = initialize_for_inline (fndecl, get_first_label_num (),
+ max_label_num (), max_reg_num (), 0);
+
+ /* If there are insns that copy parms from the stack into pseudo registers,
+ those insns are not copied. `expand_inline_function' must
+ emit the correct code to handle such things. */
+
+ insn = get_insns ();
+ if (GET_CODE (insn) != NOTE)
+ abort ();
+
+ /* Get the insn which signals the end of parameter setup code. */
+ first_nonparm_insn = get_first_nonparm_insn ();
+
+ /* Now just scan the chain of insns to see what happens to our
+ PARM_DECLs. If a PARM_DECL is used but never modified, we
+ can substitute its rtl directly when expanding inline (and
+ perform constant folding when its incoming value is constant).
+ Otherwise, we have to copy its value into a new register and track
+ the new register's life. */
+
+ for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn))
+ {
+ if (insn == first_nonparm_insn)
+ in_nonparm_insns = 1;
+
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ if (current_function_uses_const_pool)
+ {
+ /* Replace any constant pool references with the actual constant.
+ We will put the constant back if we need to write the
+ function out after all. */
+ save_constants (&PATTERN (insn));
+ if (REG_NOTES (insn))
+ save_constants (&REG_NOTES (insn));
+ }
+
+ /* Record what interesting things happen to our parameters. */
+ note_stores (PATTERN (insn), note_modified_parmregs);
+ }
+ }
+
+ /* We have now allocated all that needs to be allocated permanently
+ on the rtx obstack. Set our high-water mark, so that we
+ can free the rest of this when the time comes. */
+
+ preserve_data ();
+
+ finish_inline (fndecl, head);
+}
+
+/* Given PX, a pointer into an insn, search for references to the constant
+ pool. Replace each with a CONST that has the mode of the original
+ constant, contains the constant, and has RTX_INTEGRATED_P set.
+ Similarly, constant pool addresses not enclosed in a MEM are replaced
+ with an ADDRESS rtx which also gives the constant, mode, and has
+ RTX_INTEGRATED_P set. */
+
+static void
+save_constants (px)
+ rtx *px;
+{
+ rtx x;
+ int i, j;
+
+ again:
+ x = *px;
+
+ /* If this is a CONST_DOUBLE, don't try to fix things up in
+ CONST_DOUBLE_MEM, because this is an infinite recursion. */
+ if (GET_CODE (x) == CONST_DOUBLE)
+ return;
+ else if (GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == SYMBOL_REF
+ && CONSTANT_POOL_ADDRESS_P (XEXP (x,0)))
+ {
+ enum machine_mode const_mode = get_pool_mode (XEXP (x, 0));
+ rtx new = gen_rtx (CONST, const_mode, get_pool_constant (XEXP (x, 0)));
+ RTX_INTEGRATED_P (new) = 1;
+
+ /* If the MEM was in a different mode than the constant (perhaps we
+ were only looking at the low-order part), surround it with a
+ SUBREG so we can save both modes. */
+
+ if (GET_MODE (x) != const_mode)
+ {
+ new = gen_rtx (SUBREG, GET_MODE (x), new, 0);
+ RTX_INTEGRATED_P (new) = 1;
+ }
+
+ *px = new;
+ save_constants (&XEXP (*px, 0));
+ }
+ else if (GET_CODE (x) == SYMBOL_REF
+ && CONSTANT_POOL_ADDRESS_P (x))
+ {
+ *px = gen_rtx (ADDRESS, get_pool_mode (x), get_pool_constant (x));
+ save_constants (&XEXP (*px, 0));
+ RTX_INTEGRATED_P (*px) = 1;
+ }
+
+ else
+ {
+ char *fmt = GET_RTX_FORMAT (GET_CODE (x));
+ int len = GET_RTX_LENGTH (GET_CODE (x));
+
+ for (i = len-1; i >= 0; i--)
+ {
+ switch (fmt[i])
+ {
+ case 'E':
+ for (j = 0; j < XVECLEN (x, i); j++)
+ save_constants (&XVECEXP (x, i, j));
+ break;
+
+ case 'e':
+ if (XEXP (x, i) == 0)
+ continue;
+ if (i == 0)
+ {
+ /* Hack tail-recursion here. */
+ px = &XEXP (x, 0);
+ goto again;
+ }
+ save_constants (&XEXP (x, i));
+ break;
+ }
+ }
+ }
+}
+
+/* Note whether a parameter is modified or not. */
+
+static void
+note_modified_parmregs (reg, x)
+ rtx reg;
+ rtx x;
+{
+ if (GET_CODE (reg) == REG && in_nonparm_insns
+ && REGNO (reg) < max_parm_reg
+ && REGNO (reg) >= FIRST_PSEUDO_REGISTER
+ && parmdecl_map[REGNO (reg)] != 0)
+ TREE_READONLY (parmdecl_map[REGNO (reg)]) = 0;
+}
+
+/* Copy the rtx ORIG recursively, replacing pseudo-regs and labels
+ according to `reg_map' and `label_map'. The original rtl insns
+ will be saved for inlining; this is used to make a copy
+ which is used to finish compiling the inline function itself.
+
+ If we find a "saved" constant pool entry, one which was replaced with
+ the value of the constant, convert it back to a constant pool entry.
+ Since the pool wasn't touched, this should simply restore the old
+ address.
+
+ All other kinds of rtx are copied except those that can never be
+ changed during compilation. */
+
+static rtx
+copy_for_inline (orig)
+ rtx orig;
+{
+ register rtx x = orig;
+ register int i;
+ register enum rtx_code code;
+ register char *format_ptr;
+
+ if (x == 0)
+ return x;
+
+ code = GET_CODE (x);
+
+ /* These types may be freely shared. */
+
+ switch (code)
+ {
+ case QUEUED:
+ case CONST_INT:
+ case SYMBOL_REF:
+ case PC:
+ case CC0:
+ return x;
+
+ case CONST_DOUBLE:
+ /* We have to make a new CONST_DOUBLE to ensure that we account for
+ it correctly. Using the old CONST_DOUBLE_MEM data is wrong. */
+ if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+ {
+ REAL_VALUE_TYPE d;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (d, x);
+ return CONST_DOUBLE_FROM_REAL_VALUE (d, GET_MODE (x));
+ }
+ else
+ return immed_double_const (CONST_DOUBLE_LOW (x), CONST_DOUBLE_HIGH (x),
+ VOIDmode);
+
+ case CONST:
+ /* Get constant pool entry for constant in the pool. */
+ if (RTX_INTEGRATED_P (x))
+ return validize_mem (force_const_mem (GET_MODE (x),
+ copy_for_inline (XEXP (x, 0))));
+ break;
+
+ case SUBREG:
+ /* Get constant pool entry, but access in different mode. */
+ if (RTX_INTEGRATED_P (x))
+ {
+ rtx new
+ = force_const_mem (GET_MODE (SUBREG_REG (x)),
+ copy_for_inline (XEXP (SUBREG_REG (x), 0)));
+
+ PUT_MODE (new, GET_MODE (x));
+ return validize_mem (new);
+ }
+ break;
+
+ case ADDRESS:
+ /* If not special for constant pool error. Else get constant pool
+ address. */
+ if (! RTX_INTEGRATED_P (x))
+ abort ();
+
+ return XEXP (force_const_mem (GET_MODE (x),
+ copy_for_inline (XEXP (x, 0))), 0);
+
+ case ASM_OPERANDS:
+ /* If a single asm insn contains multiple output operands
+ then it contains multiple ASM_OPERANDS rtx's that share operand 3.
+ We must make sure that the copied insn continues to share it. */
+ if (orig_asm_operands_vector == XVEC (orig, 3))
+ {
+ x = rtx_alloc (ASM_OPERANDS);
+ x->volatil = orig->volatil;
+ XSTR (x, 0) = XSTR (orig, 0);
+ XSTR (x, 1) = XSTR (orig, 1);
+ XINT (x, 2) = XINT (orig, 2);
+ XVEC (x, 3) = copy_asm_operands_vector;
+ XVEC (x, 4) = copy_asm_constraints_vector;
+ XSTR (x, 5) = XSTR (orig, 5);
+ XINT (x, 6) = XINT (orig, 6);
+ return x;
+ }
+ break;
+
+ case MEM:
+ /* A MEM is usually allowed to be shared if its address is constant
+ or is a constant plus one of the special registers.
+
+ We do not allow sharing of addresses that are either a special
+ register or the sum of a constant and a special register because
+ it is possible for unshare_all_rtl to copy the address, into memory
+ that won't be saved. Although the MEM can safely be shared, and
+ won't be copied there, the address itself cannot be shared, and may
+ need to be copied.
+
+ There are also two exceptions with constants: The first is if the
+ constant is a LABEL_REF or the sum of the LABEL_REF
+ and an integer. This case can happen if we have an inline
+ function that supplies a constant operand to the call of another
+ inline function that uses it in a switch statement. In this case,
+ we will be replacing the LABEL_REF, so we have to replace this MEM
+ as well.
+
+ The second case is if we have a (const (plus (address ..) ...)).
+ In that case we need to put back the address of the constant pool
+ entry. */
+
+ if (CONSTANT_ADDRESS_P (XEXP (x, 0))
+ && GET_CODE (XEXP (x, 0)) != LABEL_REF
+ && ! (GET_CODE (XEXP (x, 0)) == CONST
+ && (GET_CODE (XEXP (XEXP (x, 0), 0)) == PLUS
+ && ((GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0))
+ == LABEL_REF)
+ || (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0))
+ == ADDRESS)))))
+ return x;
+ break;
+
+ case LABEL_REF:
+ /* If this is a non-local label, just make a new LABEL_REF.
+ Otherwise, use the new label as well. */
+ x = gen_rtx (LABEL_REF, GET_MODE (orig),
+ LABEL_REF_NONLOCAL_P (orig) ? XEXP (orig, 0)
+ : label_map[CODE_LABEL_NUMBER (XEXP (orig, 0))]);
+ LABEL_REF_NONLOCAL_P (x) = LABEL_REF_NONLOCAL_P (orig);
+ LABEL_OUTSIDE_LOOP_P (x) = LABEL_OUTSIDE_LOOP_P (orig);
+ return x;
+
+ case REG:
+ if (REGNO (x) > LAST_VIRTUAL_REGISTER)
+ return reg_map [REGNO (x)];
+ else
+ return x;
+
+ case SET:
+ /* If a parm that gets modified lives in a pseudo-reg,
+ clear its TREE_READONLY to prevent certain optimizations. */
+ {
+ rtx dest = SET_DEST (x);
+
+ while (GET_CODE (dest) == STRICT_LOW_PART
+ || GET_CODE (dest) == ZERO_EXTRACT
+ || GET_CODE (dest) == SUBREG)
+ dest = XEXP (dest, 0);
+
+ if (GET_CODE (dest) == REG
+ && REGNO (dest) < max_parm_reg
+ && REGNO (dest) >= FIRST_PSEUDO_REGISTER
+ && parmdecl_map[REGNO (dest)] != 0
+ /* The insn to load an arg pseudo from a stack slot
+ does not count as modifying it. */
+ && in_nonparm_insns)
+ TREE_READONLY (parmdecl_map[REGNO (dest)]) = 0;
+ }
+ break;
+
+#if 0 /* This is a good idea, but here is the wrong place for it. */
+ /* Arrange that CONST_INTs always appear as the second operand
+ if they appear, and that `frame_pointer_rtx' or `arg_pointer_rtx'
+ always appear as the first. */
+ case PLUS:
+ if (GET_CODE (XEXP (x, 0)) == CONST_INT
+ || (XEXP (x, 1) == frame_pointer_rtx
+ || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ && XEXP (x, 1) == arg_pointer_rtx)))
+ {
+ rtx t = XEXP (x, 0);
+ XEXP (x, 0) = XEXP (x, 1);
+ XEXP (x, 1) = t;
+ }
+ break;
+#endif
+ }
+
+ /* Replace this rtx with a copy of itself. */
+
+ x = rtx_alloc (code);
+ bcopy ((char *) orig, (char *) x,
+ (sizeof (*x) - sizeof (x->fld)
+ + sizeof (x->fld[0]) * GET_RTX_LENGTH (code)));
+
+ /* Now scan the subexpressions recursively.
+ We can store any replaced subexpressions directly into X
+ since we know X is not shared! Any vectors in X
+ must be copied if X was copied. */
+
+ format_ptr = GET_RTX_FORMAT (code);
+
+ for (i = 0; i < GET_RTX_LENGTH (code); i++)
+ {
+ switch (*format_ptr++)
+ {
+ case 'e':
+ XEXP (x, i) = copy_for_inline (XEXP (x, i));
+ break;
+
+ case 'u':
+ /* Change any references to old-insns to point to the
+ corresponding copied insns. */
+ XEXP (x, i) = insn_map[INSN_UID (XEXP (x, i))];
+ break;
+
+ case 'E':
+ if (XVEC (x, i) != NULL && XVECLEN (x, i) != 0)
+ {
+ register int j;
+
+ XVEC (x, i) = gen_rtvec_v (XVECLEN (x, i), &XVECEXP (x, i, 0));
+ for (j = 0; j < XVECLEN (x, i); j++)
+ XVECEXP (x, i, j)
+ = copy_for_inline (XVECEXP (x, i, j));
+ }
+ break;
+ }
+ }
+
+ if (code == ASM_OPERANDS && orig_asm_operands_vector == 0)
+ {
+ orig_asm_operands_vector = XVEC (orig, 3);
+ copy_asm_operands_vector = XVEC (x, 3);
+ copy_asm_constraints_vector = XVEC (x, 4);
+ }
+
+ return x;
+}
+
+/* Unfortunately, we need a global copy of const_equiv map for communication
+ with a function called from note_stores. Be *very* careful that this
+ is used properly in the presence of recursion. */
+
+rtx *global_const_equiv_map;
+int global_const_equiv_map_size;
+
+#define FIXED_BASE_PLUS_P(X) \
+ (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \
+ && GET_CODE (XEXP (X, 0)) == REG \
+ && REGNO (XEXP (X, 0)) >= FIRST_VIRTUAL_REGISTER \
+ && REGNO (XEXP (X, 0)) <= LAST_VIRTUAL_REGISTER)
+
+/* Integrate the procedure defined by FNDECL. Note that this function
+ may wind up calling itself. Since the static variables are not
+ reentrant, we do not assign them until after the possibility
+ of recursion is eliminated.
+
+ If IGNORE is nonzero, do not produce a value.
+ Otherwise store the value in TARGET if it is nonzero and that is convenient.
+
+ Value is:
+ (rtx)-1 if we could not substitute the function
+ 0 if we substituted it and it does not produce a value
+ else an rtx for where the value is stored. */
+
+rtx
+expand_inline_function (fndecl, parms, target, ignore, type, structure_value_addr)
+ tree fndecl, parms;
+ rtx target;
+ int ignore;
+ tree type;
+ rtx structure_value_addr;
+{
+ tree formal, actual, block;
+ rtx header = DECL_SAVED_INSNS (fndecl);
+ rtx insns = FIRST_FUNCTION_INSN (header);
+ rtx parm_insns = FIRST_PARM_INSN (header);
+ tree *arg_trees;
+ rtx *arg_vals;
+ rtx insn;
+ int max_regno;
+ register int i;
+ int min_labelno = FIRST_LABELNO (header);
+ int max_labelno = LAST_LABELNO (header);
+ int nargs;
+ rtx local_return_label = 0;
+ rtx loc;
+ rtx temp;
+ struct inline_remap *map;
+ rtx cc0_insn = 0;
+ rtvec arg_vector = ORIGINAL_ARG_VECTOR (header);
+ rtx static_chain_value = 0;
+
+ /* Allow for equivalences of the pseudos we make for virtual fp and ap. */
+ max_regno = MAX_REGNUM (header) + 3;
+ if (max_regno < FIRST_PSEUDO_REGISTER)
+ abort ();
+
+ nargs = list_length (DECL_ARGUMENTS (fndecl));
+
+ /* Check that the parms type match and that sufficient arguments were
+ passed. Since the appropriate conversions or default promotions have
+ already been applied, the machine modes should match exactly. */
+
+ for (formal = DECL_ARGUMENTS (fndecl),
+ actual = parms;
+ formal;
+ formal = TREE_CHAIN (formal),
+ actual = TREE_CHAIN (actual))
+ {
+ tree arg;
+ enum machine_mode mode;
+
+ if (actual == 0)
+ return (rtx) (HOST_WIDE_INT) -1;
+
+ arg = TREE_VALUE (actual);
+ mode= TYPE_MODE (DECL_ARG_TYPE (formal));
+
+ if (mode != TYPE_MODE (TREE_TYPE (arg))
+ /* If they are block mode, the types should match exactly.
+ They don't match exactly if TREE_TYPE (FORMAL) == ERROR_MARK_NODE,
+ which could happen if the parameter has incomplete type. */
+ || (mode == BLKmode && TREE_TYPE (arg) != TREE_TYPE (formal)))
+ return (rtx) (HOST_WIDE_INT) -1;
+ }
+
+ /* Extra arguments are valid, but will be ignored below, so we must
+ evaluate them here for side-effects. */
+ for (; actual; actual = TREE_CHAIN (actual))
+ expand_expr (TREE_VALUE (actual), const0_rtx,
+ TYPE_MODE (TREE_TYPE (TREE_VALUE (actual))), 0);
+
+ /* Make a binding contour to keep inline cleanups called at
+ outer function-scope level from looking like they are shadowing
+ parameter declarations. */
+ pushlevel (0);
+
+ /* Make a fresh binding contour that we can easily remove. */
+ pushlevel (0);
+ expand_start_bindings (0);
+ if (GET_CODE (parm_insns) == NOTE
+ && NOTE_LINE_NUMBER (parm_insns) > 0)
+ {
+ rtx note = emit_note (NOTE_SOURCE_FILE (parm_insns),
+ NOTE_LINE_NUMBER (parm_insns));
+ if (note)
+ RTX_INTEGRATED_P (note) = 1;
+ }
+
+ /* Expand the function arguments. Do this first so that any
+ new registers get created before we allocate the maps. */
+
+ arg_vals = (rtx *) alloca (nargs * sizeof (rtx));
+ arg_trees = (tree *) alloca (nargs * sizeof (tree));
+
+ for (formal = DECL_ARGUMENTS (fndecl), actual = parms, i = 0;
+ formal;
+ formal = TREE_CHAIN (formal), actual = TREE_CHAIN (actual), i++)
+ {
+ /* Actual parameter, converted to the type of the argument within the
+ function. */
+ tree arg = convert (TREE_TYPE (formal), TREE_VALUE (actual));
+ /* Mode of the variable used within the function. */
+ enum machine_mode mode = TYPE_MODE (TREE_TYPE (formal));
+
+ /* Make sure this formal has some correspondence in the users code
+ * before emitting any line notes for it. */
+ if (DECL_SOURCE_LINE (formal))
+ {
+ rtx note = emit_note (DECL_SOURCE_FILE (formal),
+ DECL_SOURCE_LINE (formal));
+ if (note)
+ RTX_INTEGRATED_P (note) = 1;
+ }
+
+ arg_trees[i] = arg;
+ loc = RTVEC_ELT (arg_vector, i);
+
+ /* If this is an object passed by invisible reference, we copy the
+ object into a stack slot and save its address. If this will go
+ into memory, we do nothing now. Otherwise, we just expand the
+ argument. */
+ if (GET_CODE (loc) == MEM && GET_CODE (XEXP (loc, 0)) == REG
+ && REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER)
+ {
+ rtx stack_slot
+ = assign_stack_temp (TYPE_MODE (TREE_TYPE (arg)),
+ int_size_in_bytes (TREE_TYPE (arg)), 1);
+
+ store_expr (arg, stack_slot, 0);
+
+ arg_vals[i] = XEXP (stack_slot, 0);
+ }
+ else if (GET_CODE (loc) != MEM)
+ {
+ if (GET_MODE (loc) != TYPE_MODE (TREE_TYPE (arg)))
+ /* The mode if LOC and ARG can differ if LOC was a variable
+ that had its mode promoted via PROMOTED_MODE. */
+ arg_vals[i] = convert_modes (GET_MODE (loc),
+ TYPE_MODE (TREE_TYPE (arg)),
+ expand_expr (arg, NULL_RTX, mode,
+ EXPAND_SUM),
+ TREE_UNSIGNED (TREE_TYPE (formal)));
+ else
+ arg_vals[i] = expand_expr (arg, NULL_RTX, mode, EXPAND_SUM);
+ }
+ else
+ arg_vals[i] = 0;
+
+ if (arg_vals[i] != 0
+ && (! TREE_READONLY (formal)
+ /* If the parameter is not read-only, copy our argument through
+ a register. Also, we cannot use ARG_VALS[I] if it overlaps
+ TARGET in any way. In the inline function, they will likely
+ be two different pseudos, and `safe_from_p' will make all
+ sorts of smart assumptions about their not conflicting.
+ But if ARG_VALS[I] overlaps TARGET, these assumptions are
+ wrong, so put ARG_VALS[I] into a fresh register. */
+ || (target != 0
+ && (GET_CODE (arg_vals[i]) == REG
+ || GET_CODE (arg_vals[i]) == SUBREG
+ || GET_CODE (arg_vals[i]) == MEM)
+ && reg_overlap_mentioned_p (arg_vals[i], target))
+ /* ??? We must always copy a SUBREG into a REG, because it might
+ get substituted into an address, and not all ports correctly
+ handle SUBREGs in addresses. */
+ || (GET_CODE (arg_vals[i]) == SUBREG)))
+ arg_vals[i] = copy_to_mode_reg (GET_MODE (loc), arg_vals[i]);
+ }
+
+ /* Allocate the structures we use to remap things. */
+
+ map = (struct inline_remap *) alloca (sizeof (struct inline_remap));
+ map->fndecl = fndecl;
+
+ map->reg_map = (rtx *) alloca (max_regno * sizeof (rtx));
+ bzero ((char *) map->reg_map, max_regno * sizeof (rtx));
+
+ map->label_map = (rtx *)alloca ((max_labelno - min_labelno) * sizeof (rtx));
+ map->label_map -= min_labelno;
+
+ map->insn_map = (rtx *) alloca (INSN_UID (header) * sizeof (rtx));
+ bzero ((char *) map->insn_map, INSN_UID (header) * sizeof (rtx));
+ map->min_insnno = 0;
+ map->max_insnno = INSN_UID (header);
+
+ map->integrating = 1;
+
+ /* const_equiv_map maps pseudos in our routine to constants, so it needs to
+ be large enough for all our pseudos. This is the number we are currently
+ using plus the number in the called routine, plus 15 for each arg,
+ five to compute the virtual frame pointer, and five for the return value.
+ This should be enough for most cases. We do not reference entries
+ outside the range of the map.
+
+ ??? These numbers are quite arbitrary and were obtained by
+ experimentation. At some point, we should try to allocate the
+ table after all the parameters are set up so we an more accurately
+ estimate the number of pseudos we will need. */
+
+ map->const_equiv_map_size
+ = max_reg_num () + (max_regno - FIRST_PSEUDO_REGISTER) + 15 * nargs + 10;
+
+ map->const_equiv_map
+ = (rtx *)alloca (map->const_equiv_map_size * sizeof (rtx));
+ bzero ((char *) map->const_equiv_map,
+ map->const_equiv_map_size * sizeof (rtx));
+
+ map->const_age_map
+ = (unsigned *)alloca (map->const_equiv_map_size * sizeof (unsigned));
+ bzero ((char *) map->const_age_map,
+ map->const_equiv_map_size * sizeof (unsigned));
+ map->const_age = 0;
+
+ /* Record the current insn in case we have to set up pointers to frame
+ and argument memory blocks. */
+ map->insns_at_start = get_last_insn ();
+
+ /* Update the outgoing argument size to allow for those in the inlined
+ function. */
+ if (OUTGOING_ARGS_SIZE (header) > current_function_outgoing_args_size)
+ current_function_outgoing_args_size = OUTGOING_ARGS_SIZE (header);
+
+ /* If the inline function needs to make PIC references, that means
+ that this function's PIC offset table must be used. */
+ if (FUNCTION_FLAGS (header) & FUNCTION_FLAGS_USES_PIC_OFFSET_TABLE)
+ current_function_uses_pic_offset_table = 1;
+
+ /* If this function needs a context, set it up. */
+ if (FUNCTION_FLAGS (header) & FUNCTION_FLAGS_NEEDS_CONTEXT)
+ static_chain_value = lookup_static_chain (fndecl);
+
+ /* Process each argument. For each, set up things so that the function's
+ reference to the argument will refer to the argument being passed.
+ We only replace REG with REG here. Any simplifications are done
+ via const_equiv_map.
+
+ We make two passes: In the first, we deal with parameters that will
+ be placed into registers, since we need to ensure that the allocated
+ register number fits in const_equiv_map. Then we store all non-register
+ parameters into their memory location. */
+
+ /* Don't try to free temp stack slots here, because we may put one of the
+ parameters into a temp stack slot. */
+
+ for (i = 0; i < nargs; i++)
+ {
+ rtx copy = arg_vals[i];
+
+ loc = RTVEC_ELT (arg_vector, i);
+
+ /* There are three cases, each handled separately. */
+ if (GET_CODE (loc) == MEM && GET_CODE (XEXP (loc, 0)) == REG
+ && REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER)
+ {
+ /* This must be an object passed by invisible reference (it could
+ also be a variable-sized object, but we forbid inlining functions
+ with variable-sized arguments). COPY is the address of the
+ actual value (this computation will cause it to be copied). We
+ map that address for the register, noting the actual address as
+ an equivalent in case it can be substituted into the insns. */
+
+ if (GET_CODE (copy) != REG)
+ {
+ temp = copy_addr_to_reg (copy);
+ if ((CONSTANT_P (copy) || FIXED_BASE_PLUS_P (copy))
+ && REGNO (temp) < map->const_equiv_map_size)
+ {
+ map->const_equiv_map[REGNO (temp)] = copy;
+ map->const_age_map[REGNO (temp)] = CONST_AGE_PARM;
+ }
+ copy = temp;
+ }
+ map->reg_map[REGNO (XEXP (loc, 0))] = copy;
+ }
+ else if (GET_CODE (loc) == MEM)
+ {
+ /* This is the case of a parameter that lives in memory.
+ It will live in the block we allocate in the called routine's
+ frame that simulates the incoming argument area. Do nothing
+ now; we will call store_expr later. */
+ ;
+ }
+ else if (GET_CODE (loc) == REG)
+ {
+ /* This is the good case where the parameter is in a register.
+ If it is read-only and our argument is a constant, set up the
+ constant equivalence.
+
+ If LOC is REG_USERVAR_P, the usual case, COPY must also have
+ that flag set if it is a register.
+
+ Also, don't allow hard registers here; they might not be valid
+ when substituted into insns. */
+
+ if ((GET_CODE (copy) != REG && GET_CODE (copy) != SUBREG)
+ || (GET_CODE (copy) == REG && REG_USERVAR_P (loc)
+ && ! REG_USERVAR_P (copy))
+ || (GET_CODE (copy) == REG
+ && REGNO (copy) < FIRST_PSEUDO_REGISTER))
+ {
+ temp = copy_to_mode_reg (GET_MODE (loc), copy);
+ REG_USERVAR_P (temp) = REG_USERVAR_P (loc);
+ if ((CONSTANT_P (copy) || FIXED_BASE_PLUS_P (copy))
+ && REGNO (temp) < map->const_equiv_map_size)
+ {
+ map->const_equiv_map[REGNO (temp)] = copy;
+ map->const_age_map[REGNO (temp)] = CONST_AGE_PARM;
+ }
+ copy = temp;
+ }
+ map->reg_map[REGNO (loc)] = copy;
+ }
+ else if (GET_CODE (loc) == CONCAT)
+ {
+ /* This is the good case where the parameter is in a
+ pair of separate pseudos.
+ If it is read-only and our argument is a constant, set up the
+ constant equivalence.
+
+ If LOC is REG_USERVAR_P, the usual case, COPY must also have
+ that flag set if it is a register.
+
+ Also, don't allow hard registers here; they might not be valid
+ when substituted into insns. */
+ rtx locreal = gen_realpart (GET_MODE (XEXP (loc, 0)), loc);
+ rtx locimag = gen_imagpart (GET_MODE (XEXP (loc, 0)), loc);
+ rtx copyreal = gen_realpart (GET_MODE (locreal), copy);
+ rtx copyimag = gen_imagpart (GET_MODE (locimag), copy);
+
+ if ((GET_CODE (copyreal) != REG && GET_CODE (copyreal) != SUBREG)
+ || (GET_CODE (copyreal) == REG && REG_USERVAR_P (locreal)
+ && ! REG_USERVAR_P (copyreal))
+ || (GET_CODE (copyreal) == REG
+ && REGNO (copyreal) < FIRST_PSEUDO_REGISTER))
+ {
+ temp = copy_to_mode_reg (GET_MODE (locreal), copyreal);
+ REG_USERVAR_P (temp) = REG_USERVAR_P (locreal);
+ if ((CONSTANT_P (copyreal) || FIXED_BASE_PLUS_P (copyreal))
+ && REGNO (temp) < map->const_equiv_map_size)
+ {
+ map->const_equiv_map[REGNO (temp)] = copyreal;
+ map->const_age_map[REGNO (temp)] = CONST_AGE_PARM;
+ }
+ copyreal = temp;
+ }
+ map->reg_map[REGNO (locreal)] = copyreal;
+
+ if ((GET_CODE (copyimag) != REG && GET_CODE (copyimag) != SUBREG)
+ || (GET_CODE (copyimag) == REG && REG_USERVAR_P (locimag)
+ && ! REG_USERVAR_P (copyimag))
+ || (GET_CODE (copyimag) == REG
+ && REGNO (copyimag) < FIRST_PSEUDO_REGISTER))
+ {
+ temp = copy_to_mode_reg (GET_MODE (locimag), copyimag);
+ REG_USERVAR_P (temp) = REG_USERVAR_P (locimag);
+ if ((CONSTANT_P (copyimag) || FIXED_BASE_PLUS_P (copyimag))
+ && REGNO (temp) < map->const_equiv_map_size)
+ {
+ map->const_equiv_map[REGNO (temp)] = copyimag;
+ map->const_age_map[REGNO (temp)] = CONST_AGE_PARM;
+ }
+ copyimag = temp;
+ }
+ map->reg_map[REGNO (locimag)] = copyimag;
+ }
+ else
+ abort ();
+ }
+
+ /* Now do the parameters that will be placed in memory. */
+
+ for (formal = DECL_ARGUMENTS (fndecl), i = 0;
+ formal; formal = TREE_CHAIN (formal), i++)
+ {
+ loc = RTVEC_ELT (arg_vector, i);
+
+ if (GET_CODE (loc) == MEM
+ /* Exclude case handled above. */
+ && ! (GET_CODE (XEXP (loc, 0)) == REG
+ && REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER))
+ {
+ rtx note = emit_note (DECL_SOURCE_FILE (formal),
+ DECL_SOURCE_LINE (formal));
+ if (note)
+ RTX_INTEGRATED_P (note) = 1;
+
+ /* Compute the address in the area we reserved and store the
+ value there. */
+ temp = copy_rtx_and_substitute (loc, map);
+ subst_constants (&temp, NULL_RTX, map);
+ apply_change_group ();
+ if (! memory_address_p (GET_MODE (temp), XEXP (temp, 0)))
+ temp = change_address (temp, VOIDmode, XEXP (temp, 0));
+ store_expr (arg_trees[i], temp, 0);
+ }
+ }
+
+ /* Deal with the places that the function puts its result.
+ We are driven by what is placed into DECL_RESULT.
+
+ Initially, we assume that we don't have anything special handling for
+ REG_FUNCTION_RETURN_VALUE_P. */
+
+ map->inline_target = 0;
+ loc = DECL_RTL (DECL_RESULT (fndecl));
+ if (TYPE_MODE (type) == VOIDmode)
+ /* There is no return value to worry about. */
+ ;
+ else if (GET_CODE (loc) == MEM)
+ {
+ if (! structure_value_addr || ! aggregate_value_p (DECL_RESULT (fndecl)))
+ abort ();
+
+ /* Pass the function the address in which to return a structure value.
+ Note that a constructor can cause someone to call us with
+ STRUCTURE_VALUE_ADDR, but the initialization takes place
+ via the first parameter, rather than the struct return address.
+
+ We have two cases: If the address is a simple register indirect,
+ use the mapping mechanism to point that register to our structure
+ return address. Otherwise, store the structure return value into
+ the place that it will be referenced from. */
+
+ if (GET_CODE (XEXP (loc, 0)) == REG)
+ {
+ temp = force_reg (Pmode, structure_value_addr);
+ map->reg_map[REGNO (XEXP (loc, 0))] = temp;
+ if ((CONSTANT_P (structure_value_addr)
+ || (GET_CODE (structure_value_addr) == PLUS
+ && XEXP (structure_value_addr, 0) == virtual_stack_vars_rtx
+ && GET_CODE (XEXP (structure_value_addr, 1)) == CONST_INT))
+ && REGNO (temp) < map->const_equiv_map_size)
+ {
+ map->const_equiv_map[REGNO (temp)] = structure_value_addr;
+ map->const_age_map[REGNO (temp)] = CONST_AGE_PARM;
+ }
+ }
+ else
+ {
+ temp = copy_rtx_and_substitute (loc, map);
+ subst_constants (&temp, NULL_RTX, map);
+ apply_change_group ();
+ emit_move_insn (temp, structure_value_addr);
+ }
+ }
+ else if (ignore)
+ /* We will ignore the result value, so don't look at its structure.
+ Note that preparations for an aggregate return value
+ do need to be made (above) even if it will be ignored. */
+ ;
+ else if (GET_CODE (loc) == REG)
+ {
+ /* The function returns an object in a register and we use the return
+ value. Set up our target for remapping. */
+
+ /* Machine mode function was declared to return. */
+ enum machine_mode departing_mode = TYPE_MODE (type);
+ /* (Possibly wider) machine mode it actually computes
+ (for the sake of callers that fail to declare it right). */
+ enum machine_mode arriving_mode
+ = TYPE_MODE (TREE_TYPE (DECL_RESULT (fndecl)));
+ rtx reg_to_map;
+
+ /* Don't use MEMs as direct targets because on some machines
+ substituting a MEM for a REG makes invalid insns.
+ Let the combiner substitute the MEM if that is valid. */
+ if (target == 0 || GET_CODE (target) != REG
+ || GET_MODE (target) != departing_mode)
+ target = gen_reg_rtx (departing_mode);
+
+ /* If function's value was promoted before return,
+ avoid machine mode mismatch when we substitute INLINE_TARGET.
+ But TARGET is what we will return to the caller. */
+ if (arriving_mode != departing_mode)
+ reg_to_map = gen_rtx (SUBREG, arriving_mode, target, 0);
+ else
+ reg_to_map = target;
+
+ /* Usually, the result value is the machine's return register.
+ Sometimes it may be a pseudo. Handle both cases. */
+ if (REG_FUNCTION_VALUE_P (loc))
+ map->inline_target = reg_to_map;
+ else
+ map->reg_map[REGNO (loc)] = reg_to_map;
+ }
+
+ /* Make new label equivalences for the labels in the called function. */
+ for (i = min_labelno; i < max_labelno; i++)
+ map->label_map[i] = gen_label_rtx ();
+
+ /* Perform postincrements before actually calling the function. */
+ emit_queue ();
+
+ /* Clean up stack so that variables might have smaller offsets. */
+ do_pending_stack_adjust ();
+
+ /* Save a copy of the location of const_equiv_map for mark_stores, called
+ via note_stores. */
+ global_const_equiv_map = map->const_equiv_map;
+ global_const_equiv_map_size = map->const_equiv_map_size;
+
+ /* Now copy the insns one by one. Do this in two passes, first the insns and
+ then their REG_NOTES, just like save_for_inline. */
+
+ /* This loop is very similar to the loop in copy_loop_body in unroll.c. */
+
+ for (insn = insns; insn; insn = NEXT_INSN (insn))
+ {
+ rtx copy, pattern;
+
+ map->orig_asm_operands_vector = 0;
+
+ switch (GET_CODE (insn))
+ {
+ case INSN:
+ pattern = PATTERN (insn);
+ copy = 0;
+ if (GET_CODE (pattern) == USE
+ && GET_CODE (XEXP (pattern, 0)) == REG
+ && REG_FUNCTION_VALUE_P (XEXP (pattern, 0)))
+ /* The (USE (REG n)) at return from the function should
+ be ignored since we are changing (REG n) into
+ inline_target. */
+ break;
+
+ /* Ignore setting a function value that we don't want to use. */
+ if (map->inline_target == 0
+ && GET_CODE (pattern) == SET
+ && GET_CODE (SET_DEST (pattern)) == REG
+ && REG_FUNCTION_VALUE_P (SET_DEST (pattern)))
+ {
+ if (volatile_refs_p (SET_SRC (pattern)))
+ {
+ /* If we must not delete the source,
+ load it into a new temporary. */
+ copy = emit_insn (copy_rtx_and_substitute (pattern, map));
+ SET_DEST (PATTERN (copy))
+ = gen_reg_rtx (GET_MODE (SET_DEST (PATTERN (copy))));
+ }
+ else
+ break;
+ }
+ /* If this is setting the static chain pseudo, set it from
+ the value we want to give it instead. */
+ else if (static_chain_value != 0
+ && GET_CODE (pattern) == SET
+ && rtx_equal_p (SET_SRC (pattern),
+ static_chain_incoming_rtx))
+ {
+ rtx newdest = copy_rtx_and_substitute (SET_DEST (pattern), map);
+
+ copy = emit_insn (gen_rtx (SET, VOIDmode, newdest,
+ static_chain_value));
+
+ static_chain_value = 0;
+ }
+ else
+ copy = emit_insn (copy_rtx_and_substitute (pattern, map));
+ /* REG_NOTES will be copied later. */
+
+#ifdef HAVE_cc0
+ /* If this insn is setting CC0, it may need to look at
+ the insn that uses CC0 to see what type of insn it is.
+ In that case, the call to recog via validate_change will
+ fail. So don't substitute constants here. Instead,
+ do it when we emit the following insn.
+
+ For example, see the pyr.md file. That machine has signed and
+ unsigned compares. The compare patterns must check the
+ following branch insn to see which what kind of compare to
+ emit.
+
+ If the previous insn set CC0, substitute constants on it as
+ well. */
+ if (sets_cc0_p (PATTERN (copy)) != 0)
+ cc0_insn = copy;
+ else
+ {
+ if (cc0_insn)
+ try_constants (cc0_insn, map);
+ cc0_insn = 0;
+ try_constants (copy, map);
+ }
+#else
+ try_constants (copy, map);
+#endif
+ break;
+
+ case JUMP_INSN:
+ if (GET_CODE (PATTERN (insn)) == RETURN)
+ {
+ if (local_return_label == 0)
+ local_return_label = gen_label_rtx ();
+ pattern = gen_jump (local_return_label);
+ }
+ else
+ pattern = copy_rtx_and_substitute (PATTERN (insn), map);
+
+ copy = emit_jump_insn (pattern);
+
+#ifdef HAVE_cc0
+ if (cc0_insn)
+ try_constants (cc0_insn, map);
+ cc0_insn = 0;
+#endif
+ try_constants (copy, map);
+
+ /* If this used to be a conditional jump insn but whose branch
+ direction is now know, we must do something special. */
+ if (condjump_p (insn) && ! simplejump_p (insn) && map->last_pc_value)
+ {
+#ifdef HAVE_cc0
+ /* The previous insn set cc0 for us. So delete it. */
+ delete_insn (PREV_INSN (copy));
+#endif
+
+ /* If this is now a no-op, delete it. */
+ if (map->last_pc_value == pc_rtx)
+ {
+ delete_insn (copy);
+ copy = 0;
+ }
+ else
+ /* Otherwise, this is unconditional jump so we must put a
+ BARRIER after it. We could do some dead code elimination
+ here, but jump.c will do it just as well. */
+ emit_barrier ();
+ }
+ break;
+
+ case CALL_INSN:
+ pattern = copy_rtx_and_substitute (PATTERN (insn), map);
+ copy = emit_call_insn (pattern);
+
+ /* Because the USAGE information potentially contains objects other
+ than hard registers, we need to copy it. */
+ CALL_INSN_FUNCTION_USAGE (copy) =
+ copy_rtx_and_substitute (CALL_INSN_FUNCTION_USAGE (insn), map);
+
+#ifdef HAVE_cc0
+ if (cc0_insn)
+ try_constants (cc0_insn, map);
+ cc0_insn = 0;
+#endif
+ try_constants (copy, map);
+
+ /* Be lazy and assume CALL_INSNs clobber all hard registers. */
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ map->const_equiv_map[i] = 0;
+ break;
+
+ case CODE_LABEL:
+ copy = emit_label (map->label_map[CODE_LABEL_NUMBER (insn)]);
+ LABEL_NAME (copy) = LABEL_NAME (insn);
+ map->const_age++;
+ break;
+
+ case BARRIER:
+ copy = emit_barrier ();
+ break;
+
+ case NOTE:
+ /* It is important to discard function-end and function-beg notes,
+ so we have only one of each in the current function.
+ Also, NOTE_INSN_DELETED notes aren't useful (save_for_inline
+ deleted these in the copy used for continuing compilation,
+ not the copy used for inlining). */
+ if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END
+ && NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_BEG
+ && NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED)
+ copy = emit_note (NOTE_SOURCE_FILE (insn), NOTE_LINE_NUMBER (insn));
+ else
+ copy = 0;
+ break;
+
+ default:
+ abort ();
+ break;
+ }
+
+ if (copy)
+ RTX_INTEGRATED_P (copy) = 1;
+
+ map->insn_map[INSN_UID (insn)] = copy;
+ }
+
+ /* Now copy the REG_NOTES. Increment const_age, so that only constants
+ from parameters can be substituted in. These are the only ones that
+ are valid across the entire function. */
+ map->const_age++;
+ for (insn = insns; insn; insn = NEXT_INSN (insn))
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && map->insn_map[INSN_UID (insn)]
+ && REG_NOTES (insn))
+ {
+ rtx tem = copy_rtx_and_substitute (REG_NOTES (insn), map);
+ /* We must also do subst_constants, in case one of our parameters
+ has const type and constant value. */
+ subst_constants (&tem, NULL_RTX, map);
+ apply_change_group ();
+ REG_NOTES (map->insn_map[INSN_UID (insn)]) = tem;
+ }
+
+ if (local_return_label)
+ emit_label (local_return_label);
+
+ /* Make copies of the decls of the symbols in the inline function, so that
+ the copies of the variables get declared in the current function. Set
+ up things so that lookup_static_chain knows that to interpret registers
+ in SAVE_EXPRs for TYPE_SIZEs as local. */
+
+ inline_function_decl = fndecl;
+ integrate_parm_decls (DECL_ARGUMENTS (fndecl), map, arg_vector);
+ integrate_decl_tree ((tree) ORIGINAL_DECL_INITIAL (header), 0, map);
+ inline_function_decl = 0;
+
+ /* End the scope containing the copied formal parameter variables
+ and copied LABEL_DECLs. */
+
+ expand_end_bindings (getdecls (), 1, 1);
+ block = poplevel (1, 1, 0);
+ BLOCK_ABSTRACT_ORIGIN (block) = (DECL_ABSTRACT_ORIGIN (fndecl) == NULL
+ ? fndecl : DECL_ABSTRACT_ORIGIN (fndecl));
+ poplevel (0, 0, 0);
+ emit_line_note (input_filename, lineno);
+
+ if (structure_value_addr)
+ {
+ target = gen_rtx (MEM, TYPE_MODE (type),
+ memory_address (TYPE_MODE (type), structure_value_addr));
+ MEM_IN_STRUCT_P (target) = 1;
+ }
+ return target;
+}
+
+/* Given a chain of PARM_DECLs, ARGS, copy each decl into a VAR_DECL,
+ push all of those decls and give each one the corresponding home. */
+
+static void
+integrate_parm_decls (args, map, arg_vector)
+ tree args;
+ struct inline_remap *map;
+ rtvec arg_vector;
+{
+ register tree tail;
+ register int i;
+
+ for (tail = args, i = 0; tail; tail = TREE_CHAIN (tail), i++)
+ {
+ register tree decl = build_decl (VAR_DECL, DECL_NAME (tail),
+ TREE_TYPE (tail));
+ rtx new_decl_rtl
+ = copy_rtx_and_substitute (RTVEC_ELT (arg_vector, i), map);
+
+ DECL_ARG_TYPE (decl) = DECL_ARG_TYPE (tail);
+ /* We really should be setting DECL_INCOMING_RTL to something reasonable
+ here, but that's going to require some more work. */
+ /* DECL_INCOMING_RTL (decl) = ?; */
+ /* These args would always appear unused, if not for this. */
+ TREE_USED (decl) = 1;
+ /* Prevent warning for shadowing with these. */
+ DECL_ABSTRACT_ORIGIN (decl) = tail;
+ pushdecl (decl);
+ /* Fully instantiate the address with the equivalent form so that the
+ debugging information contains the actual register, instead of the
+ virtual register. Do this by not passing an insn to
+ subst_constants. */
+ subst_constants (&new_decl_rtl, NULL_RTX, map);
+ apply_change_group ();
+ DECL_RTL (decl) = new_decl_rtl;
+ }
+}
+
+/* Given a BLOCK node LET, push decls and levels so as to construct in the
+ current function a tree of contexts isomorphic to the one that is given.
+
+ LEVEL indicates how far down into the BLOCK tree is the node we are
+ currently traversing. It is always zero except for recursive calls.
+
+ MAP, if nonzero, is a pointer to an inline_remap map which indicates how
+ registers used in the DECL_RTL field should be remapped. If it is zero,
+ no mapping is necessary. */
+
+static void
+integrate_decl_tree (let, level, map)
+ tree let;
+ int level;
+ struct inline_remap *map;
+{
+ tree t, node;
+
+ if (level > 0)
+ pushlevel (0);
+
+ for (t = BLOCK_VARS (let); t; t = TREE_CHAIN (t))
+ {
+ tree d;
+
+ push_obstacks_nochange ();
+ saveable_allocation ();
+ d = copy_node (t);
+ pop_obstacks ();
+
+ if (DECL_RTL (t) != 0)
+ {
+ DECL_RTL (d) = copy_rtx_and_substitute (DECL_RTL (t), map);
+ /* Fully instantiate the address with the equivalent form so that the
+ debugging information contains the actual register, instead of the
+ virtual register. Do this by not passing an insn to
+ subst_constants. */
+ subst_constants (&DECL_RTL (d), NULL_RTX, map);
+ apply_change_group ();
+ }
+ /* These args would always appear unused, if not for this. */
+ TREE_USED (d) = 1;
+ /* Prevent warning for shadowing with these. */
+ DECL_ABSTRACT_ORIGIN (d) = t;
+
+ if (DECL_LANG_SPECIFIC (d))
+ copy_lang_decl (d);
+
+ pushdecl (d);
+ }
+
+ for (t = BLOCK_SUBBLOCKS (let); t; t = TREE_CHAIN (t))
+ integrate_decl_tree (t, level + 1, map);
+
+ if (level > 0)
+ {
+ node = poplevel (1, 0, 0);
+ if (node)
+ {
+ TREE_USED (node) = TREE_USED (let);
+ BLOCK_ABSTRACT_ORIGIN (node) = let;
+ }
+ }
+}
+
+/* Create a new copy of an rtx.
+ Recursively copies the operands of the rtx,
+ except for those few rtx codes that are sharable.
+
+ We always return an rtx that is similar to that incoming rtx, with the
+ exception of possibly changing a REG to a SUBREG or vice versa. No
+ rtl is ever emitted.
+
+ Handle constants that need to be placed in the constant pool by
+ calling `force_const_mem'. */
+
+rtx
+copy_rtx_and_substitute (orig, map)
+ register rtx orig;
+ struct inline_remap *map;
+{
+ register rtx copy, temp;
+ register int i, j;
+ register RTX_CODE code;
+ register enum machine_mode mode;
+ register char *format_ptr;
+ int regno;
+
+ if (orig == 0)
+ return 0;
+
+ code = GET_CODE (orig);
+ mode = GET_MODE (orig);
+
+ switch (code)
+ {
+ case REG:
+ /* If the stack pointer register shows up, it must be part of
+ stack-adjustments (*not* because we eliminated the frame pointer!).
+ Small hard registers are returned as-is. Pseudo-registers
+ go through their `reg_map'. */
+ regno = REGNO (orig);
+ if (regno <= LAST_VIRTUAL_REGISTER)
+ {
+ /* Some hard registers are also mapped,
+ but others are not translated. */
+ if (map->reg_map[regno] != 0)
+ return map->reg_map[regno];
+
+ /* If this is the virtual frame pointer, make space in current
+ function's stack frame for the stack frame of the inline function.
+
+ Copy the address of this area into a pseudo. Map
+ virtual_stack_vars_rtx to this pseudo and set up a constant
+ equivalence for it to be the address. This will substitute the
+ address into insns where it can be substituted and use the new
+ pseudo where it can't. */
+ if (regno == VIRTUAL_STACK_VARS_REGNUM)
+ {
+ rtx loc, seq;
+ int size = DECL_FRAME_SIZE (map->fndecl);
+ int rounded;
+
+ start_sequence ();
+ loc = assign_stack_temp (BLKmode, size, 1);
+ loc = XEXP (loc, 0);
+#ifdef FRAME_GROWS_DOWNWARD
+ /* In this case, virtual_stack_vars_rtx points to one byte
+ higher than the top of the frame area. So compute the offset
+ to one byte higher than our substitute frame.
+ Keep the fake frame pointer aligned like a real one. */
+ rounded = CEIL_ROUND (size, BIGGEST_ALIGNMENT / BITS_PER_UNIT);
+ loc = plus_constant (loc, rounded);
+#endif
+ map->reg_map[regno] = temp
+ = force_reg (Pmode, force_operand (loc, NULL_RTX));
+
+ if (REGNO (temp) < map->const_equiv_map_size)
+ {
+ map->const_equiv_map[REGNO (temp)] = loc;
+ map->const_age_map[REGNO (temp)] = CONST_AGE_PARM;
+ }
+
+ seq = gen_sequence ();
+ end_sequence ();
+ emit_insn_after (seq, map->insns_at_start);
+ return temp;
+ }
+ else if (regno == VIRTUAL_INCOMING_ARGS_REGNUM)
+ {
+ /* Do the same for a block to contain any arguments referenced
+ in memory. */
+ rtx loc, seq;
+ int size = FUNCTION_ARGS_SIZE (DECL_SAVED_INSNS (map->fndecl));
+
+ start_sequence ();
+ loc = assign_stack_temp (BLKmode, size, 1);
+ loc = XEXP (loc, 0);
+ /* When arguments grow downward, the virtual incoming
+ args pointer points to the top of the argument block,
+ so the remapped location better do the same. */
+#ifdef ARGS_GROW_DOWNWARD
+ loc = plus_constant (loc, size);
+#endif
+ map->reg_map[regno] = temp
+ = force_reg (Pmode, force_operand (loc, NULL_RTX));
+
+ if (REGNO (temp) < map->const_equiv_map_size)
+ {
+ map->const_equiv_map[REGNO (temp)] = loc;
+ map->const_age_map[REGNO (temp)] = CONST_AGE_PARM;
+ }
+
+ seq = gen_sequence ();
+ end_sequence ();
+ emit_insn_after (seq, map->insns_at_start);
+ return temp;
+ }
+ else if (REG_FUNCTION_VALUE_P (orig))
+ {
+ /* This is a reference to the function return value. If
+ the function doesn't have a return value, error. If the
+ mode doesn't agree, make a SUBREG. */
+ if (map->inline_target == 0)
+ /* Must be unrolling loops or replicating code if we
+ reach here, so return the register unchanged. */
+ return orig;
+ else if (mode != GET_MODE (map->inline_target))
+ return gen_lowpart (mode, map->inline_target);
+ else
+ return map->inline_target;
+ }
+ return orig;
+ }
+ if (map->reg_map[regno] == NULL)
+ {
+ map->reg_map[regno] = gen_reg_rtx (mode);
+ REG_USERVAR_P (map->reg_map[regno]) = REG_USERVAR_P (orig);
+ REG_LOOP_TEST_P (map->reg_map[regno]) = REG_LOOP_TEST_P (orig);
+ RTX_UNCHANGING_P (map->reg_map[regno]) = RTX_UNCHANGING_P (orig);
+ /* A reg with REG_FUNCTION_VALUE_P true will never reach here. */
+ }
+ return map->reg_map[regno];
+
+ case SUBREG:
+ copy = copy_rtx_and_substitute (SUBREG_REG (orig), map);
+ /* SUBREG is ordinary, but don't make nested SUBREGs. */
+ if (GET_CODE (copy) == SUBREG)
+ return gen_rtx (SUBREG, GET_MODE (orig), SUBREG_REG (copy),
+ SUBREG_WORD (orig) + SUBREG_WORD (copy));
+ else if (GET_CODE (copy) == CONCAT)
+ return (subreg_realpart_p (orig) ? XEXP (copy, 0) : XEXP (copy, 1));
+ else
+ return gen_rtx (SUBREG, GET_MODE (orig), copy,
+ SUBREG_WORD (orig));
+
+ case USE:
+ case CLOBBER:
+ /* USE and CLOBBER are ordinary, but we convert (use (subreg foo))
+ to (use foo) if the original insn didn't have a subreg.
+ Removing the subreg distorts the VAX movstrhi pattern
+ by changing the mode of an operand. */
+ copy = copy_rtx_and_substitute (XEXP (orig, 0), map);
+ if (GET_CODE (copy) == SUBREG && GET_CODE (XEXP (orig, 0)) != SUBREG)
+ copy = SUBREG_REG (copy);
+ return gen_rtx (code, VOIDmode, copy);
+
+ case CODE_LABEL:
+ LABEL_PRESERVE_P (map->label_map[CODE_LABEL_NUMBER (orig)])
+ = LABEL_PRESERVE_P (orig);
+ return map->label_map[CODE_LABEL_NUMBER (orig)];
+
+ case LABEL_REF:
+ copy = gen_rtx (LABEL_REF, mode,
+ LABEL_REF_NONLOCAL_P (orig) ? XEXP (orig, 0)
+ : map->label_map[CODE_LABEL_NUMBER (XEXP (orig, 0))]);
+ LABEL_OUTSIDE_LOOP_P (copy) = LABEL_OUTSIDE_LOOP_P (orig);
+
+ /* The fact that this label was previously nonlocal does not mean
+ it still is, so we must check if it is within the range of
+ this function's labels. */
+ LABEL_REF_NONLOCAL_P (copy)
+ = (LABEL_REF_NONLOCAL_P (orig)
+ && ! (CODE_LABEL_NUMBER (XEXP (copy, 0)) >= get_first_label_num ()
+ && CODE_LABEL_NUMBER (XEXP (copy, 0)) < max_label_num ()));
+
+ /* If we have made a nonlocal label local, it means that this
+ inlined call will be refering to our nonlocal goto handler.
+ So make sure we create one for this block; we normally would
+ not since this is not otherwise considered a "call". */
+ if (LABEL_REF_NONLOCAL_P (orig) && ! LABEL_REF_NONLOCAL_P (copy))
+ function_call_count++;
+
+ return copy;
+
+ case PC:
+ case CC0:
+ case CONST_INT:
+ return orig;
+
+ case SYMBOL_REF:
+ /* Symbols which represent the address of a label stored in the constant
+ pool must be modified to point to a constant pool entry for the
+ remapped label. Otherwise, symbols are returned unchanged. */
+ if (CONSTANT_POOL_ADDRESS_P (orig))
+ {
+ rtx constant = get_pool_constant (orig);
+ if (GET_CODE (constant) == LABEL_REF)
+ return XEXP (force_const_mem (Pmode,
+ copy_rtx_and_substitute (constant,
+ map)),
+ 0);
+ }
+
+ return orig;
+
+ case CONST_DOUBLE:
+ /* We have to make a new copy of this CONST_DOUBLE because don't want
+ to use the old value of CONST_DOUBLE_MEM. Also, this may be a
+ duplicate of a CONST_DOUBLE we have already seen. */
+ if (GET_MODE_CLASS (GET_MODE (orig)) == MODE_FLOAT)
+ {
+ REAL_VALUE_TYPE d;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (d, orig);
+ return CONST_DOUBLE_FROM_REAL_VALUE (d, GET_MODE (orig));
+ }
+ else
+ return immed_double_const (CONST_DOUBLE_LOW (orig),
+ CONST_DOUBLE_HIGH (orig), VOIDmode);
+
+ case CONST:
+ /* Make new constant pool entry for a constant
+ that was in the pool of the inline function. */
+ if (RTX_INTEGRATED_P (orig))
+ {
+ /* If this was an address of a constant pool entry that itself
+ had to be placed in the constant pool, it might not be a
+ valid address. So the recursive call below might turn it
+ into a register. In that case, it isn't a constant any
+ more, so return it. This has the potential of changing a
+ MEM into a REG, but we'll assume that it safe. */
+ temp = copy_rtx_and_substitute (XEXP (orig, 0), map);
+ if (! CONSTANT_P (temp))
+ return temp;
+ return validize_mem (force_const_mem (GET_MODE (orig), temp));
+ }
+ break;
+
+ case ADDRESS:
+ /* If from constant pool address, make new constant pool entry and
+ return its address. */
+ if (! RTX_INTEGRATED_P (orig))
+ abort ();
+
+ temp = force_const_mem (GET_MODE (orig),
+ copy_rtx_and_substitute (XEXP (orig, 0), map));
+
+#if 0
+ /* Legitimizing the address here is incorrect.
+
+ The only ADDRESS rtx's that can reach here are ones created by
+ save_constants. Hence the operand of the ADDRESS is always legal
+ in this position of the instruction, since the original rtx without
+ the ADDRESS was legal.
+
+ The reason we don't legitimize the address here is that on the
+ Sparc, the caller may have a (high ...) surrounding this ADDRESS.
+ This code forces the operand of the address to a register, which
+ fails because we can not take the HIGH part of a register.
+
+ Also, change_address may create new registers. These registers
+ will not have valid reg_map entries. This can cause try_constants()
+ to fail because assumes that all registers in the rtx have valid
+ reg_map entries, and it may end up replacing one of these new
+ registers with junk. */
+
+ if (! memory_address_p (GET_MODE (temp), XEXP (temp, 0)))
+ temp = change_address (temp, GET_MODE (temp), XEXP (temp, 0));
+#endif
+
+ return XEXP (temp, 0);
+
+ case ASM_OPERANDS:
+ /* If a single asm insn contains multiple output operands
+ then it contains multiple ASM_OPERANDS rtx's that share operand 3.
+ We must make sure that the copied insn continues to share it. */
+ if (map->orig_asm_operands_vector == XVEC (orig, 3))
+ {
+ copy = rtx_alloc (ASM_OPERANDS);
+ copy->volatil = orig->volatil;
+ XSTR (copy, 0) = XSTR (orig, 0);
+ XSTR (copy, 1) = XSTR (orig, 1);
+ XINT (copy, 2) = XINT (orig, 2);
+ XVEC (copy, 3) = map->copy_asm_operands_vector;
+ XVEC (copy, 4) = map->copy_asm_constraints_vector;
+ XSTR (copy, 5) = XSTR (orig, 5);
+ XINT (copy, 6) = XINT (orig, 6);
+ return copy;
+ }
+ break;
+
+ case CALL:
+ /* This is given special treatment because the first
+ operand of a CALL is a (MEM ...) which may get
+ forced into a register for cse. This is undesirable
+ if function-address cse isn't wanted or if we won't do cse. */
+#ifndef NO_FUNCTION_CSE
+ if (! (optimize && ! flag_no_function_cse))
+#endif
+ return gen_rtx (CALL, GET_MODE (orig),
+ gen_rtx (MEM, GET_MODE (XEXP (orig, 0)),
+ copy_rtx_and_substitute (XEXP (XEXP (orig, 0), 0), map)),
+ copy_rtx_and_substitute (XEXP (orig, 1), map));
+ break;
+
+#if 0
+ /* Must be ifdefed out for loop unrolling to work. */
+ case RETURN:
+ abort ();
+#endif
+
+ case SET:
+ /* If this is setting fp or ap, it means that we have a nonlocal goto.
+ Don't alter that.
+ If the nonlocal goto is into the current function,
+ this will result in unnecessarily bad code, but should work. */
+ if (SET_DEST (orig) == virtual_stack_vars_rtx
+ || SET_DEST (orig) == virtual_incoming_args_rtx)
+ return gen_rtx (SET, VOIDmode, SET_DEST (orig),
+ copy_rtx_and_substitute (SET_SRC (orig), map));
+ break;
+
+ case MEM:
+ copy = rtx_alloc (MEM);
+ PUT_MODE (copy, mode);
+ XEXP (copy, 0) = copy_rtx_and_substitute (XEXP (orig, 0), map);
+ MEM_IN_STRUCT_P (copy) = MEM_IN_STRUCT_P (orig);
+ MEM_VOLATILE_P (copy) = MEM_VOLATILE_P (orig);
+
+ /* If doing function inlining, this MEM might not be const in the
+ function that it is being inlined into, and thus may not be
+ unchanging after function inlining. Constant pool references are
+ handled elsewhere, so this doesn't lose RTX_UNCHANGING_P bits
+ for them. */
+ if (! map->integrating)
+ RTX_UNCHANGING_P (copy) = RTX_UNCHANGING_P (orig);
+
+ return copy;
+ }
+
+ copy = rtx_alloc (code);
+ PUT_MODE (copy, mode);
+ copy->in_struct = orig->in_struct;
+ copy->volatil = orig->volatil;
+ copy->unchanging = orig->unchanging;
+
+ format_ptr = GET_RTX_FORMAT (GET_CODE (copy));
+
+ for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++)
+ {
+ switch (*format_ptr++)
+ {
+ case '0':
+ break;
+
+ case 'e':
+ XEXP (copy, i) = copy_rtx_and_substitute (XEXP (orig, i), map);
+ break;
+
+ case 'u':
+ /* Change any references to old-insns to point to the
+ corresponding copied insns. */
+ XEXP (copy, i) = map->insn_map[INSN_UID (XEXP (orig, i))];
+ break;
+
+ case 'E':
+ XVEC (copy, i) = XVEC (orig, i);
+ if (XVEC (orig, i) != NULL && XVECLEN (orig, i) != 0)
+ {
+ XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i));
+ for (j = 0; j < XVECLEN (copy, i); j++)
+ XVECEXP (copy, i, j)
+ = copy_rtx_and_substitute (XVECEXP (orig, i, j), map);
+ }
+ break;
+
+ case 'w':
+ XWINT (copy, i) = XWINT (orig, i);
+ break;
+
+ case 'i':
+ XINT (copy, i) = XINT (orig, i);
+ break;
+
+ case 's':
+ XSTR (copy, i) = XSTR (orig, i);
+ break;
+
+ default:
+ abort ();
+ }
+ }
+
+ if (code == ASM_OPERANDS && map->orig_asm_operands_vector == 0)
+ {
+ map->orig_asm_operands_vector = XVEC (orig, 3);
+ map->copy_asm_operands_vector = XVEC (copy, 3);
+ map->copy_asm_constraints_vector = XVEC (copy, 4);
+ }
+
+ return copy;
+}
+
+/* Substitute known constant values into INSN, if that is valid. */
+
+void
+try_constants (insn, map)
+ rtx insn;
+ struct inline_remap *map;
+{
+ int i;
+
+ map->num_sets = 0;
+ subst_constants (&PATTERN (insn), insn, map);
+
+ /* Apply the changes if they are valid; otherwise discard them. */
+ apply_change_group ();
+
+ /* Show we don't know the value of anything stored or clobbered. */
+ note_stores (PATTERN (insn), mark_stores);
+ map->last_pc_value = 0;
+#ifdef HAVE_cc0
+ map->last_cc0_value = 0;
+#endif
+
+ /* Set up any constant equivalences made in this insn. */
+ for (i = 0; i < map->num_sets; i++)
+ {
+ if (GET_CODE (map->equiv_sets[i].dest) == REG)
+ {
+ int regno = REGNO (map->equiv_sets[i].dest);
+
+ if (regno < map->const_equiv_map_size
+ && (map->const_equiv_map[regno] == 0
+ /* Following clause is a hack to make case work where GNU C++
+ reassigns a variable to make cse work right. */
+ || ! rtx_equal_p (map->const_equiv_map[regno],
+ map->equiv_sets[i].equiv)))
+ {
+ map->const_equiv_map[regno] = map->equiv_sets[i].equiv;
+ map->const_age_map[regno] = map->const_age;
+ }
+ }
+ else if (map->equiv_sets[i].dest == pc_rtx)
+ map->last_pc_value = map->equiv_sets[i].equiv;
+#ifdef HAVE_cc0
+ else if (map->equiv_sets[i].dest == cc0_rtx)
+ map->last_cc0_value = map->equiv_sets[i].equiv;
+#endif
+ }
+}
+
+/* Substitute known constants for pseudo regs in the contents of LOC,
+ which are part of INSN.
+ If INSN is zero, the substitution should always be done (this is used to
+ update DECL_RTL).
+ These changes are taken out by try_constants if the result is not valid.
+
+ Note that we are more concerned with determining when the result of a SET
+ is a constant, for further propagation, than actually inserting constants
+ into insns; cse will do the latter task better.
+
+ This function is also used to adjust address of items previously addressed
+ via the virtual stack variable or virtual incoming arguments registers. */
+
+static void
+subst_constants (loc, insn, map)
+ rtx *loc;
+ rtx insn;
+ struct inline_remap *map;
+{
+ rtx x = *loc;
+ register int i;
+ register enum rtx_code code;
+ register char *format_ptr;
+ int num_changes = num_validated_changes ();
+ rtx new = 0;
+ enum machine_mode op0_mode;
+
+ code = GET_CODE (x);
+
+ switch (code)
+ {
+ case PC:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case CONST:
+ case LABEL_REF:
+ case ADDRESS:
+ return;
+
+#ifdef HAVE_cc0
+ case CC0:
+ validate_change (insn, loc, map->last_cc0_value, 1);
+ return;
+#endif
+
+ case USE:
+ case CLOBBER:
+ /* The only thing we can do with a USE or CLOBBER is possibly do
+ some substitutions in a MEM within it. */
+ if (GET_CODE (XEXP (x, 0)) == MEM)
+ subst_constants (&XEXP (XEXP (x, 0), 0), insn, map);
+ return;
+
+ case REG:
+ /* Substitute for parms and known constants. Don't replace
+ hard regs used as user variables with constants. */
+ {
+ int regno = REGNO (x);
+
+ if (! (regno < FIRST_PSEUDO_REGISTER && REG_USERVAR_P (x))
+ && regno < map->const_equiv_map_size
+ && map->const_equiv_map[regno] != 0
+ && map->const_age_map[regno] >= map->const_age)
+ validate_change (insn, loc, map->const_equiv_map[regno], 1);
+ return;
+ }
+
+ case SUBREG:
+ /* SUBREG applied to something other than a reg
+ should be treated as ordinary, since that must
+ be a special hack and we don't know how to treat it specially.
+ Consider for example mulsidi3 in m68k.md.
+ Ordinary SUBREG of a REG needs this special treatment. */
+ if (GET_CODE (SUBREG_REG (x)) == REG)
+ {
+ rtx inner = SUBREG_REG (x);
+ rtx new = 0;
+
+ /* We can't call subst_constants on &SUBREG_REG (x) because any
+ constant or SUBREG wouldn't be valid inside our SUBEG. Instead,
+ see what is inside, try to form the new SUBREG and see if that is
+ valid. We handle two cases: extracting a full word in an
+ integral mode and extracting the low part. */
+ subst_constants (&inner, NULL_RTX, map);
+
+ if (GET_MODE_CLASS (GET_MODE (x)) == MODE_INT
+ && GET_MODE_SIZE (GET_MODE (x)) == UNITS_PER_WORD
+ && GET_MODE (SUBREG_REG (x)) != VOIDmode)
+ new = operand_subword (inner, SUBREG_WORD (x), 0,
+ GET_MODE (SUBREG_REG (x)));
+
+ if (new == 0 && subreg_lowpart_p (x))
+ new = gen_lowpart_common (GET_MODE (x), inner);
+
+ if (new)
+ validate_change (insn, loc, new, 1);
+
+ return;
+ }
+ break;
+
+ case MEM:
+ subst_constants (&XEXP (x, 0), insn, map);
+
+ /* If a memory address got spoiled, change it back. */
+ if (insn != 0 && num_validated_changes () != num_changes
+ && !memory_address_p (GET_MODE (x), XEXP (x, 0)))
+ cancel_changes (num_changes);
+ return;
+
+ case SET:
+ {
+ /* Substitute constants in our source, and in any arguments to a
+ complex (e..g, ZERO_EXTRACT) destination, but not in the destination
+ itself. */
+ rtx *dest_loc = &SET_DEST (x);
+ rtx dest = *dest_loc;
+ rtx src, tem;
+
+ subst_constants (&SET_SRC (x), insn, map);
+ src = SET_SRC (x);
+
+ while (GET_CODE (*dest_loc) == ZERO_EXTRACT
+ /* By convention, we always use ZERO_EXTRACT in the dest. */
+/* || GET_CODE (*dest_loc) == SIGN_EXTRACT */
+ || GET_CODE (*dest_loc) == SUBREG
+ || GET_CODE (*dest_loc) == STRICT_LOW_PART)
+ {
+ if (GET_CODE (*dest_loc) == ZERO_EXTRACT)
+ {
+ subst_constants (&XEXP (*dest_loc, 1), insn, map);
+ subst_constants (&XEXP (*dest_loc, 2), insn, map);
+ }
+ dest_loc = &XEXP (*dest_loc, 0);
+ }
+
+ /* Do substitute in the address of a destination in memory. */
+ if (GET_CODE (*dest_loc) == MEM)
+ subst_constants (&XEXP (*dest_loc, 0), insn, map);
+
+ /* Check for the case of DEST a SUBREG, both it and the underlying
+ register are less than one word, and the SUBREG has the wider mode.
+ In the case, we are really setting the underlying register to the
+ source converted to the mode of DEST. So indicate that. */
+ if (GET_CODE (dest) == SUBREG
+ && GET_MODE_SIZE (GET_MODE (dest)) <= UNITS_PER_WORD
+ && GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) <= UNITS_PER_WORD
+ && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest)))
+ <= GET_MODE_SIZE (GET_MODE (dest)))
+ && (tem = gen_lowpart_if_possible (GET_MODE (SUBREG_REG (dest)),
+ src)))
+ src = tem, dest = SUBREG_REG (dest);
+
+ /* If storing a recognizable value save it for later recording. */
+ if ((map->num_sets < MAX_RECOG_OPERANDS)
+ && (CONSTANT_P (src)
+ || (GET_CODE (src) == PLUS
+ && GET_CODE (XEXP (src, 0)) == REG
+ && REGNO (XEXP (src, 0)) >= FIRST_VIRTUAL_REGISTER
+ && REGNO (XEXP (src, 0)) <= LAST_VIRTUAL_REGISTER
+ && CONSTANT_P (XEXP (src, 1)))
+ || GET_CODE (src) == COMPARE
+#ifdef HAVE_cc0
+ || dest == cc0_rtx
+#endif
+ || (dest == pc_rtx
+ && (src == pc_rtx || GET_CODE (src) == RETURN
+ || GET_CODE (src) == LABEL_REF))))
+ {
+ /* Normally, this copy won't do anything. But, if SRC is a COMPARE
+ it will cause us to save the COMPARE with any constants
+ substituted, which is what we want for later. */
+ map->equiv_sets[map->num_sets].equiv = copy_rtx (src);
+ map->equiv_sets[map->num_sets++].dest = dest;
+ }
+
+ return;
+ }
+ }
+
+ format_ptr = GET_RTX_FORMAT (code);
+
+ /* If the first operand is an expression, save its mode for later. */
+ if (*format_ptr == 'e')
+ op0_mode = GET_MODE (XEXP (x, 0));
+
+ for (i = 0; i < GET_RTX_LENGTH (code); i++)
+ {
+ switch (*format_ptr++)
+ {
+ case '0':
+ break;
+
+ case 'e':
+ if (XEXP (x, i))
+ subst_constants (&XEXP (x, i), insn, map);
+ break;
+
+ case 'u':
+ case 'i':
+ case 's':
+ case 'w':
+ break;
+
+ case 'E':
+ if (XVEC (x, i) != NULL && XVECLEN (x, i) != 0)
+ {
+ int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ subst_constants (&XVECEXP (x, i, j), insn, map);
+ }
+ break;
+
+ default:
+ abort ();
+ }
+ }
+
+ /* If this is a commutative operation, move a constant to the second
+ operand unless the second operand is already a CONST_INT. */
+ if ((GET_RTX_CLASS (code) == 'c' || code == NE || code == EQ)
+ && CONSTANT_P (XEXP (x, 0)) && GET_CODE (XEXP (x, 1)) != CONST_INT)
+ {
+ rtx tem = XEXP (x, 0);
+ validate_change (insn, &XEXP (x, 0), XEXP (x, 1), 1);
+ validate_change (insn, &XEXP (x, 1), tem, 1);
+ }
+
+ /* Simplify the expression in case we put in some constants. */
+ switch (GET_RTX_CLASS (code))
+ {
+ case '1':
+ new = simplify_unary_operation (code, GET_MODE (x),
+ XEXP (x, 0), op0_mode);
+ break;
+
+ case '<':
+ {
+ enum machine_mode op_mode = GET_MODE (XEXP (x, 0));
+ if (op_mode == VOIDmode)
+ op_mode = GET_MODE (XEXP (x, 1));
+ new = simplify_relational_operation (code, op_mode,
+ XEXP (x, 0), XEXP (x, 1));
+#ifdef FLOAT_STORE_FLAG_VALUE
+ if (new != 0 && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+ new = ((new == const0_rtx) ? CONST0_RTX (GET_MODE (x))
+ : CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE,
+ GET_MODE (x)));
+#endif
+ break;
+ }
+
+ case '2':
+ case 'c':
+ new = simplify_binary_operation (code, GET_MODE (x),
+ XEXP (x, 0), XEXP (x, 1));
+ break;
+
+ case 'b':
+ case '3':
+ new = simplify_ternary_operation (code, GET_MODE (x), op0_mode,
+ XEXP (x, 0), XEXP (x, 1), XEXP (x, 2));
+ break;
+ }
+
+ if (new)
+ validate_change (insn, loc, new, 1);
+}
+
+/* Show that register modified no longer contain known constants. We are
+ called from note_stores with parts of the new insn. */
+
+void
+mark_stores (dest, x)
+ rtx dest;
+ rtx x;
+{
+ int regno = -1;
+ enum machine_mode mode;
+
+ /* DEST is always the innermost thing set, except in the case of
+ SUBREGs of hard registers. */
+
+ if (GET_CODE (dest) == REG)
+ regno = REGNO (dest), mode = GET_MODE (dest);
+ else if (GET_CODE (dest) == SUBREG && GET_CODE (SUBREG_REG (dest)) == REG)
+ {
+ regno = REGNO (SUBREG_REG (dest)) + SUBREG_WORD (dest);
+ mode = GET_MODE (SUBREG_REG (dest));
+ }
+
+ if (regno >= 0)
+ {
+ int last_reg = (regno >= FIRST_PSEUDO_REGISTER ? regno
+ : regno + HARD_REGNO_NREGS (regno, mode) - 1);
+ int i;
+
+ for (i = regno; i <= last_reg; i++)
+ if (i < global_const_equiv_map_size)
+ global_const_equiv_map[i] = 0;
+ }
+}
+
+/* If any CONST expressions with RTX_INTEGRATED_P are present in the rtx
+ pointed to by PX, they represent constants in the constant pool.
+ Replace these with a new memory reference obtained from force_const_mem.
+ Similarly, ADDRESS expressions with RTX_INTEGRATED_P represent the
+ address of a constant pool entry. Replace them with the address of
+ a new constant pool entry obtained from force_const_mem. */
+
+static void
+restore_constants (px)
+ rtx *px;
+{
+ rtx x = *px;
+ int i, j;
+ char *fmt;
+
+ if (x == 0)
+ return;
+
+ if (GET_CODE (x) == CONST_DOUBLE)
+ {
+ /* We have to make a new CONST_DOUBLE to ensure that we account for
+ it correctly. Using the old CONST_DOUBLE_MEM data is wrong. */
+ if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+ {
+ REAL_VALUE_TYPE d;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (d, x);
+ *px = CONST_DOUBLE_FROM_REAL_VALUE (d, GET_MODE (x));
+ }
+ else
+ *px = immed_double_const (CONST_DOUBLE_LOW (x), CONST_DOUBLE_HIGH (x),
+ VOIDmode);
+ }
+
+ else if (RTX_INTEGRATED_P (x) && GET_CODE (x) == CONST)
+ {
+ restore_constants (&XEXP (x, 0));
+ *px = validize_mem (force_const_mem (GET_MODE (x), XEXP (x, 0)));
+ }
+ else if (RTX_INTEGRATED_P (x) && GET_CODE (x) == SUBREG)
+ {
+ /* This must be (subreg/i:M1 (const/i:M2 ...) 0). */
+ rtx new = XEXP (SUBREG_REG (x), 0);
+
+ restore_constants (&new);
+ new = force_const_mem (GET_MODE (SUBREG_REG (x)), new);
+ PUT_MODE (new, GET_MODE (x));
+ *px = validize_mem (new);
+ }
+ else if (RTX_INTEGRATED_P (x) && GET_CODE (x) == ADDRESS)
+ {
+ restore_constants (&XEXP (x, 0));
+ *px = XEXP (force_const_mem (GET_MODE (x), XEXP (x, 0)), 0);
+ }
+ else
+ {
+ fmt = GET_RTX_FORMAT (GET_CODE (x));
+ for (i = 0; i < GET_RTX_LENGTH (GET_CODE (x)); i++)
+ {
+ switch (*fmt++)
+ {
+ case 'E':
+ for (j = 0; j < XVECLEN (x, i); j++)
+ restore_constants (&XVECEXP (x, i, j));
+ break;
+
+ case 'e':
+ restore_constants (&XEXP (x, i));
+ break;
+ }
+ }
+ }
+}
+
+/* Given a pointer to some BLOCK node, if the BLOCK_ABSTRACT_ORIGIN for the
+ given BLOCK node is NULL, set the BLOCK_ABSTRACT_ORIGIN for the node so
+ that it points to the node itself, thus indicating that the node is its
+ own (abstract) origin. Additionally, if the BLOCK_ABSTRACT_ORIGIN for
+ the given node is NULL, recursively descend the decl/block tree which
+ it is the root of, and for each other ..._DECL or BLOCK node contained
+ therein whose DECL_ABSTRACT_ORIGINs or BLOCK_ABSTRACT_ORIGINs are also
+ still NULL, set *their* DECL_ABSTRACT_ORIGIN or BLOCK_ABSTRACT_ORIGIN
+ values to point to themselves. */
+
+static void
+set_block_origin_self (stmt)
+ register tree stmt;
+{
+ if (BLOCK_ABSTRACT_ORIGIN (stmt) == NULL_TREE)
+ {
+ BLOCK_ABSTRACT_ORIGIN (stmt) = stmt;
+
+ {
+ register tree local_decl;
+
+ for (local_decl = BLOCK_VARS (stmt);
+ local_decl != NULL_TREE;
+ local_decl = TREE_CHAIN (local_decl))
+ set_decl_origin_self (local_decl); /* Potential recursion. */
+ }
+
+ {
+ register tree subblock;
+
+ for (subblock = BLOCK_SUBBLOCKS (stmt);
+ subblock != NULL_TREE;
+ subblock = BLOCK_CHAIN (subblock))
+ set_block_origin_self (subblock); /* Recurse. */
+ }
+ }
+}
+
+/* Given a pointer to some ..._DECL node, if the DECL_ABSTRACT_ORIGIN for
+ the given ..._DECL node is NULL, set the DECL_ABSTRACT_ORIGIN for the
+ node to so that it points to the node itself, thus indicating that the
+ node represents its own (abstract) origin. Additionally, if the
+ DECL_ABSTRACT_ORIGIN for the given node is NULL, recursively descend
+ the decl/block tree of which the given node is the root of, and for
+ each other ..._DECL or BLOCK node contained therein whose
+ DECL_ABSTRACT_ORIGINs or BLOCK_ABSTRACT_ORIGINs are also still NULL,
+ set *their* DECL_ABSTRACT_ORIGIN or BLOCK_ABSTRACT_ORIGIN values to
+ point to themselves. */
+
+static void
+set_decl_origin_self (decl)
+ register tree decl;
+{
+ if (DECL_ABSTRACT_ORIGIN (decl) == NULL_TREE)
+ {
+ DECL_ABSTRACT_ORIGIN (decl) = decl;
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ register tree arg;
+
+ for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg))
+ DECL_ABSTRACT_ORIGIN (arg) = arg;
+ if (DECL_INITIAL (decl) != NULL_TREE)
+ set_block_origin_self (DECL_INITIAL (decl));
+ }
+ }
+}
+
+/* Given a pointer to some BLOCK node, and a boolean value to set the
+ "abstract" flags to, set that value into the BLOCK_ABSTRACT flag for
+ the given block, and for all local decls and all local sub-blocks
+ (recursively) which are contained therein. */
+
+static void
+set_block_abstract_flags (stmt, setting)
+ register tree stmt;
+ register int setting;
+{
+ BLOCK_ABSTRACT (stmt) = setting;
+
+ {
+ register tree local_decl;
+
+ for (local_decl = BLOCK_VARS (stmt);
+ local_decl != NULL_TREE;
+ local_decl = TREE_CHAIN (local_decl))
+ set_decl_abstract_flags (local_decl, setting);
+ }
+
+ {
+ register tree subblock;
+
+ for (subblock = BLOCK_SUBBLOCKS (stmt);
+ subblock != NULL_TREE;
+ subblock = BLOCK_CHAIN (subblock))
+ set_block_abstract_flags (subblock, setting);
+ }
+}
+
+/* Given a pointer to some ..._DECL node, and a boolean value to set the
+ "abstract" flags to, set that value into the DECL_ABSTRACT flag for the
+ given decl, and (in the case where the decl is a FUNCTION_DECL) also
+ set the abstract flags for all of the parameters, local vars, local
+ blocks and sub-blocks (recursively) to the same setting. */
+
+void
+set_decl_abstract_flags (decl, setting)
+ register tree decl;
+ register int setting;
+{
+ DECL_ABSTRACT (decl) = setting;
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ register tree arg;
+
+ for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg))
+ DECL_ABSTRACT (arg) = setting;
+ if (DECL_INITIAL (decl) != NULL_TREE)
+ set_block_abstract_flags (DECL_INITIAL (decl), setting);
+ }
+}
+
+/* Output the assembly language code for the function FNDECL
+ from its DECL_SAVED_INSNS. Used for inline functions that are output
+ at end of compilation instead of where they came in the source. */
+
+void
+output_inline_function (fndecl)
+ tree fndecl;
+{
+ rtx head;
+ rtx last;
+
+ if (output_bytecode)
+ {
+ warning ("`inline' ignored for bytecode output");
+ return;
+ }
+
+ head = DECL_SAVED_INSNS (fndecl);
+ current_function_decl = fndecl;
+
+ /* This call is only used to initialize global variables. */
+ init_function_start (fndecl, "lossage", 1);
+
+ /* Redo parameter determinations in case the FUNCTION_...
+ macros took machine-specific actions that need to be redone. */
+ assign_parms (fndecl, 1);
+
+ /* Set stack frame size. */
+ assign_stack_local (BLKmode, DECL_FRAME_SIZE (fndecl), 0);
+
+ restore_reg_data (FIRST_PARM_INSN (head));
+
+ stack_slot_list = STACK_SLOT_LIST (head);
+
+ if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_CALLS_ALLOCA)
+ current_function_calls_alloca = 1;
+
+ if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_CALLS_SETJMP)
+ current_function_calls_setjmp = 1;
+
+ if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_CALLS_LONGJMP)
+ current_function_calls_longjmp = 1;
+
+ if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_RETURNS_STRUCT)
+ current_function_returns_struct = 1;
+
+ if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_RETURNS_PCC_STRUCT)
+ current_function_returns_pcc_struct = 1;
+
+ if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_NEEDS_CONTEXT)
+ current_function_needs_context = 1;
+
+ if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_HAS_NONLOCAL_LABEL)
+ current_function_has_nonlocal_label = 1;
+
+ if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_RETURNS_POINTER)
+ current_function_returns_pointer = 1;
+
+ if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_USES_CONST_POOL)
+ current_function_uses_const_pool = 1;
+
+ if (FUNCTION_FLAGS (head) & FUNCTION_FLAGS_USES_PIC_OFFSET_TABLE)
+ current_function_uses_pic_offset_table = 1;
+
+ current_function_outgoing_args_size = OUTGOING_ARGS_SIZE (head);
+ current_function_pops_args = POPS_ARGS (head);
+
+ /* There is no need to output a return label again. */
+ return_label = 0;
+
+ expand_function_end (DECL_SOURCE_FILE (fndecl), DECL_SOURCE_LINE (fndecl), 0);
+
+ /* Find last insn and rebuild the constant pool. */
+ for (last = FIRST_PARM_INSN (head);
+ NEXT_INSN (last); last = NEXT_INSN (last))
+ {
+ if (GET_RTX_CLASS (GET_CODE (last)) == 'i')
+ {
+ restore_constants (&PATTERN (last));
+ restore_constants (&REG_NOTES (last));
+ }
+ }
+
+ set_new_first_and_last_insn (FIRST_PARM_INSN (head), last);
+ set_new_first_and_last_label_num (FIRST_LABELNO (head), LAST_LABELNO (head));
+
+ /* We must have already output DWARF debugging information for the
+ original (abstract) inline function declaration/definition, so
+ we want to make sure that the debugging information we generate
+ for this special instance of the inline function refers back to
+ the information we already generated. To make sure that happens,
+ we simply have to set the DECL_ABSTRACT_ORIGIN for the function
+ node (and for all of the local ..._DECL nodes which are its children)
+ so that they all point to themselves. */
+
+ set_decl_origin_self (fndecl);
+
+ /* We're not deferring this any longer. */
+ DECL_DEFER_OUTPUT (fndecl) = 0;
+
+ /* Compile this function all the way down to assembly code. */
+ rest_of_compilation (fndecl);
+
+ current_function_decl = 0;
+}
diff --git a/gnu/usr.bin/cc/cc_int/jump.c b/gnu/usr.bin/cc/cc_int/jump.c
new file mode 100644
index 0000000..0792f17
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/jump.c
@@ -0,0 +1,4395 @@
+/* Optimize jump instructions, for GNU compiler.
+ Copyright (C) 1987, 88, 89, 91, 92, 93, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This is the jump-optimization pass of the compiler.
+ It is run two or three times: once before cse, sometimes once after cse,
+ and once after reload (before final).
+
+ jump_optimize deletes unreachable code and labels that are not used.
+ It also deletes jumps that jump to the following insn,
+ and simplifies jumps around unconditional jumps and jumps
+ to unconditional jumps.
+
+ Each CODE_LABEL has a count of the times it is used
+ stored in the LABEL_NUSES internal field, and each JUMP_INSN
+ has one label that it refers to stored in the
+ JUMP_LABEL internal field. With this we can detect labels that
+ become unused because of the deletion of all the jumps that
+ formerly used them. The JUMP_LABEL info is sometimes looked
+ at by later passes.
+
+ Optionally, cross-jumping can be done. Currently it is done
+ only the last time (when after reload and before final).
+ In fact, the code for cross-jumping now assumes that register
+ allocation has been done, since it uses `rtx_renumbered_equal_p'.
+
+ Jump optimization is done after cse when cse's constant-propagation
+ causes jumps to become unconditional or to be deleted.
+
+ Unreachable loops are not detected here, because the labels
+ have references and the insns appear reachable from the labels.
+ find_basic_blocks in flow.c finds and deletes such loops.
+
+ The subroutines delete_insn, redirect_jump, and invert_jump are used
+ from other passes as well. */
+
+#include "config.h"
+#include "rtl.h"
+#include "flags.h"
+#include "hard-reg-set.h"
+#include "regs.h"
+#include "expr.h"
+#include "insn-config.h"
+#include "insn-flags.h"
+#include "real.h"
+
+/* ??? Eventually must record somehow the labels used by jumps
+ from nested functions. */
+/* Pre-record the next or previous real insn for each label?
+ No, this pass is very fast anyway. */
+/* Condense consecutive labels?
+ This would make life analysis faster, maybe. */
+/* Optimize jump y; x: ... y: jumpif... x?
+ Don't know if it is worth bothering with. */
+/* Optimize two cases of conditional jump to conditional jump?
+ This can never delete any instruction or make anything dead,
+ or even change what is live at any point.
+ So perhaps let combiner do it. */
+
+/* Vector indexed by uid.
+ For each CODE_LABEL, index by its uid to get first unconditional jump
+ that jumps to the label.
+ For each JUMP_INSN, index by its uid to get the next unconditional jump
+ that jumps to the same label.
+ Element 0 is the start of a chain of all return insns.
+ (It is safe to use element 0 because insn uid 0 is not used. */
+
+static rtx *jump_chain;
+
+/* List of labels referred to from initializers.
+ These can never be deleted. */
+rtx forced_labels;
+
+/* Maximum index in jump_chain. */
+
+static int max_jump_chain;
+
+/* Set nonzero by jump_optimize if control can fall through
+ to the end of the function. */
+int can_reach_end;
+
+/* Indicates whether death notes are significant in cross jump analysis.
+ Normally they are not significant, because of A and B jump to C,
+ and R dies in A, it must die in B. But this might not be true after
+ stack register conversion, and we must compare death notes in that
+ case. */
+
+static int cross_jump_death_matters = 0;
+
+static int duplicate_loop_exit_test PROTO((rtx));
+static void find_cross_jump PROTO((rtx, rtx, int, rtx *, rtx *));
+static void do_cross_jump PROTO((rtx, rtx, rtx));
+static int jump_back_p PROTO((rtx, rtx));
+static int tension_vector_labels PROTO((rtx, int));
+static void mark_jump_label PROTO((rtx, rtx, int));
+static void delete_computation PROTO((rtx));
+static void delete_from_jump_chain PROTO((rtx));
+static int delete_labelref_insn PROTO((rtx, rtx, int));
+static void redirect_tablejump PROTO((rtx, rtx));
+
+/* Delete no-op jumps and optimize jumps to jumps
+ and jumps around jumps.
+ Delete unused labels and unreachable code.
+
+ If CROSS_JUMP is 1, detect matching code
+ before a jump and its destination and unify them.
+ If CROSS_JUMP is 2, do cross-jumping, but pay attention to death notes.
+
+ If NOOP_MOVES is nonzero, delete no-op move insns.
+
+ If AFTER_REGSCAN is nonzero, then this jump pass is being run immediately
+ after regscan, and it is safe to use regno_first_uid and regno_last_uid.
+
+ If `optimize' is zero, don't change any code,
+ just determine whether control drops off the end of the function.
+ This case occurs when we have -W and not -O.
+ It works because `delete_insn' checks the value of `optimize'
+ and refrains from actually deleting when that is 0. */
+
+void
+jump_optimize (f, cross_jump, noop_moves, after_regscan)
+ rtx f;
+ int cross_jump;
+ int noop_moves;
+ int after_regscan;
+{
+ register rtx insn, next, note;
+ int changed;
+ int first = 1;
+ int max_uid = 0;
+ rtx last_insn;
+
+ cross_jump_death_matters = (cross_jump == 2);
+
+ /* Initialize LABEL_NUSES and JUMP_LABEL fields. Delete any REG_LABEL
+ notes whose labels don't occur in the insn any more. */
+
+ for (insn = f; insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == CODE_LABEL)
+ LABEL_NUSES (insn) = (LABEL_PRESERVE_P (insn) != 0);
+ else if (GET_CODE (insn) == JUMP_INSN)
+ JUMP_LABEL (insn) = 0;
+ else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
+ for (note = REG_NOTES (insn); note; note = next)
+ {
+ next = XEXP (note, 1);
+ if (REG_NOTE_KIND (note) == REG_LABEL
+ && ! reg_mentioned_p (XEXP (note, 0), PATTERN (insn)))
+ remove_note (insn, note);
+ }
+
+ if (INSN_UID (insn) > max_uid)
+ max_uid = INSN_UID (insn);
+ }
+
+ max_uid++;
+
+ /* Delete insns following barriers, up to next label. */
+
+ for (insn = f; insn;)
+ {
+ if (GET_CODE (insn) == BARRIER)
+ {
+ insn = NEXT_INSN (insn);
+ while (insn != 0 && GET_CODE (insn) != CODE_LABEL)
+ {
+ if (GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END)
+ insn = NEXT_INSN (insn);
+ else
+ insn = delete_insn (insn);
+ }
+ /* INSN is now the code_label. */
+ }
+ else
+ insn = NEXT_INSN (insn);
+ }
+
+ /* Leave some extra room for labels and duplicate exit test insns
+ we make. */
+ max_jump_chain = max_uid * 14 / 10;
+ jump_chain = (rtx *) alloca (max_jump_chain * sizeof (rtx));
+ bzero ((char *) jump_chain, max_jump_chain * sizeof (rtx));
+
+ /* Mark the label each jump jumps to.
+ Combine consecutive labels, and count uses of labels.
+
+ For each label, make a chain (using `jump_chain')
+ of all the *unconditional* jumps that jump to it;
+ also make a chain of all returns. */
+
+ for (insn = f; insn; insn = NEXT_INSN (insn))
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && ! INSN_DELETED_P (insn))
+ {
+ mark_jump_label (PATTERN (insn), insn, cross_jump);
+ if (GET_CODE (insn) == JUMP_INSN)
+ {
+ if (JUMP_LABEL (insn) != 0 && simplejump_p (insn))
+ {
+ jump_chain[INSN_UID (insn)]
+ = jump_chain[INSN_UID (JUMP_LABEL (insn))];
+ jump_chain[INSN_UID (JUMP_LABEL (insn))] = insn;
+ }
+ if (GET_CODE (PATTERN (insn)) == RETURN)
+ {
+ jump_chain[INSN_UID (insn)] = jump_chain[0];
+ jump_chain[0] = insn;
+ }
+ }
+ }
+
+ /* Keep track of labels used from static data;
+ they cannot ever be deleted. */
+
+ for (insn = forced_labels; insn; insn = XEXP (insn, 1))
+ LABEL_NUSES (XEXP (insn, 0))++;
+
+ /* Delete all labels already not referenced.
+ Also find the last insn. */
+
+ last_insn = 0;
+ for (insn = f; insn; )
+ {
+ if (GET_CODE (insn) == CODE_LABEL && LABEL_NUSES (insn) == 0)
+ insn = delete_insn (insn);
+ else
+ {
+ last_insn = insn;
+ insn = NEXT_INSN (insn);
+ }
+ }
+
+ if (!optimize)
+ {
+ /* See if there is still a NOTE_INSN_FUNCTION_END in this function.
+ If so record that this function can drop off the end. */
+
+ insn = last_insn;
+ {
+ int n_labels = 1;
+ while (insn
+ /* One label can follow the end-note: the return label. */
+ && ((GET_CODE (insn) == CODE_LABEL && n_labels-- > 0)
+ /* Ordinary insns can follow it if returning a structure. */
+ || GET_CODE (insn) == INSN
+ /* If machine uses explicit RETURN insns, no epilogue,
+ then one of them follows the note. */
+ || (GET_CODE (insn) == JUMP_INSN
+ && GET_CODE (PATTERN (insn)) == RETURN)
+ /* Other kinds of notes can follow also. */
+ || (GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END)))
+ insn = PREV_INSN (insn);
+ }
+
+ /* Report if control can fall through at the end of the function. */
+ if (insn && GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_END
+ && ! INSN_DELETED_P (insn))
+ can_reach_end = 1;
+
+ /* Zero the "deleted" flag of all the "deleted" insns. */
+ for (insn = f; insn; insn = NEXT_INSN (insn))
+ INSN_DELETED_P (insn) = 0;
+ return;
+ }
+
+#ifdef HAVE_return
+ if (HAVE_return)
+ {
+ /* If we fall through to the epilogue, see if we can insert a RETURN insn
+ in front of it. If the machine allows it at this point (we might be
+ after reload for a leaf routine), it will improve optimization for it
+ to be there. */
+ insn = get_last_insn ();
+ while (insn && GET_CODE (insn) == NOTE)
+ insn = PREV_INSN (insn);
+
+ if (insn && GET_CODE (insn) != BARRIER)
+ {
+ emit_jump_insn (gen_return ());
+ emit_barrier ();
+ }
+ }
+#endif
+
+ if (noop_moves)
+ for (insn = f; insn; )
+ {
+ next = NEXT_INSN (insn);
+
+ if (GET_CODE (insn) == INSN)
+ {
+ register rtx body = PATTERN (insn);
+
+/* Combine stack_adjusts with following push_insns. */
+#ifdef PUSH_ROUNDING
+ if (GET_CODE (body) == SET
+ && SET_DEST (body) == stack_pointer_rtx
+ && GET_CODE (SET_SRC (body)) == PLUS
+ && XEXP (SET_SRC (body), 0) == stack_pointer_rtx
+ && GET_CODE (XEXP (SET_SRC (body), 1)) == CONST_INT
+ && INTVAL (XEXP (SET_SRC (body), 1)) > 0)
+ {
+ rtx p;
+ rtx stack_adjust_insn = insn;
+ int stack_adjust_amount = INTVAL (XEXP (SET_SRC (body), 1));
+ int total_pushed = 0;
+ int pushes = 0;
+
+ /* Find all successive push insns. */
+ p = insn;
+ /* Don't convert more than three pushes;
+ that starts adding too many displaced addresses
+ and the whole thing starts becoming a losing
+ proposition. */
+ while (pushes < 3)
+ {
+ rtx pbody, dest;
+ p = next_nonnote_insn (p);
+ if (p == 0 || GET_CODE (p) != INSN)
+ break;
+ pbody = PATTERN (p);
+ if (GET_CODE (pbody) != SET)
+ break;
+ dest = SET_DEST (pbody);
+ /* Allow a no-op move between the adjust and the push. */
+ if (GET_CODE (dest) == REG
+ && GET_CODE (SET_SRC (pbody)) == REG
+ && REGNO (dest) == REGNO (SET_SRC (pbody)))
+ continue;
+ if (! (GET_CODE (dest) == MEM
+ && GET_CODE (XEXP (dest, 0)) == POST_INC
+ && XEXP (XEXP (dest, 0), 0) == stack_pointer_rtx))
+ break;
+ pushes++;
+ if (total_pushed + GET_MODE_SIZE (GET_MODE (SET_DEST (pbody)))
+ > stack_adjust_amount)
+ break;
+ total_pushed += GET_MODE_SIZE (GET_MODE (SET_DEST (pbody)));
+ }
+
+ /* Discard the amount pushed from the stack adjust;
+ maybe eliminate it entirely. */
+ if (total_pushed >= stack_adjust_amount)
+ {
+ delete_computation (stack_adjust_insn);
+ total_pushed = stack_adjust_amount;
+ }
+ else
+ XEXP (SET_SRC (PATTERN (stack_adjust_insn)), 1)
+ = GEN_INT (stack_adjust_amount - total_pushed);
+
+ /* Change the appropriate push insns to ordinary stores. */
+ p = insn;
+ while (total_pushed > 0)
+ {
+ rtx pbody, dest;
+ p = next_nonnote_insn (p);
+ if (GET_CODE (p) != INSN)
+ break;
+ pbody = PATTERN (p);
+ if (GET_CODE (pbody) == SET)
+ break;
+ dest = SET_DEST (pbody);
+ if (! (GET_CODE (dest) == MEM
+ && GET_CODE (XEXP (dest, 0)) == POST_INC
+ && XEXP (XEXP (dest, 0), 0) == stack_pointer_rtx))
+ break;
+ total_pushed -= GET_MODE_SIZE (GET_MODE (SET_DEST (pbody)));
+ /* If this push doesn't fully fit in the space
+ of the stack adjust that we deleted,
+ make another stack adjust here for what we
+ didn't use up. There should be peepholes
+ to recognize the resulting sequence of insns. */
+ if (total_pushed < 0)
+ {
+ emit_insn_before (gen_add2_insn (stack_pointer_rtx,
+ GEN_INT (- total_pushed)),
+ p);
+ break;
+ }
+ XEXP (dest, 0)
+ = plus_constant (stack_pointer_rtx, total_pushed);
+ }
+ }
+#endif
+
+ /* Detect and delete no-op move instructions
+ resulting from not allocating a parameter in a register. */
+
+ if (GET_CODE (body) == SET
+ && (SET_DEST (body) == SET_SRC (body)
+ || (GET_CODE (SET_DEST (body)) == MEM
+ && GET_CODE (SET_SRC (body)) == MEM
+ && rtx_equal_p (SET_SRC (body), SET_DEST (body))))
+ && ! (GET_CODE (SET_DEST (body)) == MEM
+ && MEM_VOLATILE_P (SET_DEST (body)))
+ && ! (GET_CODE (SET_SRC (body)) == MEM
+ && MEM_VOLATILE_P (SET_SRC (body))))
+ delete_computation (insn);
+
+ /* Detect and ignore no-op move instructions
+ resulting from smart or fortuitous register allocation. */
+
+ else if (GET_CODE (body) == SET)
+ {
+ int sreg = true_regnum (SET_SRC (body));
+ int dreg = true_regnum (SET_DEST (body));
+
+ if (sreg == dreg && sreg >= 0)
+ delete_insn (insn);
+ else if (sreg >= 0 && dreg >= 0)
+ {
+ rtx trial;
+ rtx tem = find_equiv_reg (NULL_RTX, insn, 0,
+ sreg, NULL_PTR, dreg,
+ GET_MODE (SET_SRC (body)));
+
+#ifdef PRESERVE_DEATH_INFO_REGNO_P
+ /* Deleting insn could lose a death-note for SREG or DREG
+ so don't do it if final needs accurate death-notes. */
+ if (! PRESERVE_DEATH_INFO_REGNO_P (sreg)
+ && ! PRESERVE_DEATH_INFO_REGNO_P (dreg))
+#endif
+ {
+ /* DREG may have been the target of a REG_DEAD note in
+ the insn which makes INSN redundant. If so, reorg
+ would still think it is dead. So search for such a
+ note and delete it if we find it. */
+ for (trial = prev_nonnote_insn (insn);
+ trial && GET_CODE (trial) != CODE_LABEL;
+ trial = prev_nonnote_insn (trial))
+ if (find_regno_note (trial, REG_DEAD, dreg))
+ {
+ remove_death (dreg, trial);
+ break;
+ }
+
+ if (tem != 0
+ && GET_MODE (tem) == GET_MODE (SET_DEST (body)))
+ delete_insn (insn);
+ }
+ }
+ else if (dreg >= 0 && CONSTANT_P (SET_SRC (body))
+ && find_equiv_reg (SET_SRC (body), insn, 0, dreg,
+ NULL_PTR, 0,
+ GET_MODE (SET_DEST (body))))
+ {
+ /* This handles the case where we have two consecutive
+ assignments of the same constant to pseudos that didn't
+ get a hard reg. Each SET from the constant will be
+ converted into a SET of the spill register and an
+ output reload will be made following it. This produces
+ two loads of the same constant into the same spill
+ register. */
+
+ rtx in_insn = insn;
+
+ /* Look back for a death note for the first reg.
+ If there is one, it is no longer accurate. */
+ while (in_insn && GET_CODE (in_insn) != CODE_LABEL)
+ {
+ if ((GET_CODE (in_insn) == INSN
+ || GET_CODE (in_insn) == JUMP_INSN)
+ && find_regno_note (in_insn, REG_DEAD, dreg))
+ {
+ remove_death (dreg, in_insn);
+ break;
+ }
+ in_insn = PREV_INSN (in_insn);
+ }
+
+ /* Delete the second load of the value. */
+ delete_insn (insn);
+ }
+ }
+ else if (GET_CODE (body) == PARALLEL)
+ {
+ /* If each part is a set between two identical registers or
+ a USE or CLOBBER, delete the insn. */
+ int i, sreg, dreg;
+ rtx tem;
+
+ for (i = XVECLEN (body, 0) - 1; i >= 0; i--)
+ {
+ tem = XVECEXP (body, 0, i);
+ if (GET_CODE (tem) == USE || GET_CODE (tem) == CLOBBER)
+ continue;
+
+ if (GET_CODE (tem) != SET
+ || (sreg = true_regnum (SET_SRC (tem))) < 0
+ || (dreg = true_regnum (SET_DEST (tem))) < 0
+ || dreg != sreg)
+ break;
+ }
+
+ if (i < 0)
+ delete_insn (insn);
+ }
+#if !BYTES_BIG_ENDIAN /* Not worth the hair to detect this
+ in the big-endian case. */
+ /* Also delete insns to store bit fields if they are no-ops. */
+ else if (GET_CODE (body) == SET
+ && GET_CODE (SET_DEST (body)) == ZERO_EXTRACT
+ && XEXP (SET_DEST (body), 2) == const0_rtx
+ && XEXP (SET_DEST (body), 0) == SET_SRC (body)
+ && ! (GET_CODE (SET_SRC (body)) == MEM
+ && MEM_VOLATILE_P (SET_SRC (body))))
+ delete_insn (insn);
+#endif /* not BYTES_BIG_ENDIAN */
+ }
+ insn = next;
+ }
+
+ /* If we haven't yet gotten to reload and we have just run regscan,
+ delete any insn that sets a register that isn't used elsewhere.
+ This helps some of the optimizations below by having less insns
+ being jumped around. */
+
+ if (! reload_completed && after_regscan)
+ for (insn = f; insn; insn = next)
+ {
+ rtx set = single_set (insn);
+
+ next = NEXT_INSN (insn);
+
+ if (set && GET_CODE (SET_DEST (set)) == REG
+ && REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER
+ && regno_first_uid[REGNO (SET_DEST (set))] == INSN_UID (insn)
+ /* We use regno_last_note_uid so as not to delete the setting
+ of a reg that's used in notes. A subsequent optimization
+ might arrange to use that reg for real. */
+ && regno_last_note_uid[REGNO (SET_DEST (set))] == INSN_UID (insn)
+ && ! side_effects_p (SET_SRC (set))
+ && ! find_reg_note (insn, REG_RETVAL, 0))
+ delete_insn (insn);
+ }
+
+ /* Now iterate optimizing jumps until nothing changes over one pass. */
+ changed = 1;
+ while (changed)
+ {
+ changed = 0;
+
+ for (insn = f; insn; insn = next)
+ {
+ rtx reallabelprev;
+ rtx temp, temp1, temp2, temp3, temp4, temp5, temp6;
+ rtx nlabel;
+ int this_is_simplejump, this_is_condjump, reversep;
+ int this_is_condjump_in_parallel;
+#if 0
+ /* If NOT the first iteration, if this is the last jump pass
+ (just before final), do the special peephole optimizations.
+ Avoiding the first iteration gives ordinary jump opts
+ a chance to work before peephole opts. */
+
+ if (reload_completed && !first && !flag_no_peephole)
+ if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN)
+ peephole (insn);
+#endif
+
+ /* That could have deleted some insns after INSN, so check now
+ what the following insn is. */
+
+ next = NEXT_INSN (insn);
+
+ /* See if this is a NOTE_INSN_LOOP_BEG followed by an unconditional
+ jump. Try to optimize by duplicating the loop exit test if so.
+ This is only safe immediately after regscan, because it uses
+ the values of regno_first_uid and regno_last_uid. */
+ if (after_regscan && GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG
+ && (temp1 = next_nonnote_insn (insn)) != 0
+ && simplejump_p (temp1))
+ {
+ temp = PREV_INSN (insn);
+ if (duplicate_loop_exit_test (insn))
+ {
+ changed = 1;
+ next = NEXT_INSN (temp);
+ continue;
+ }
+ }
+
+ if (GET_CODE (insn) != JUMP_INSN)
+ continue;
+
+ this_is_simplejump = simplejump_p (insn);
+ this_is_condjump = condjump_p (insn);
+ this_is_condjump_in_parallel = condjump_in_parallel_p (insn);
+
+ /* Tension the labels in dispatch tables. */
+
+ if (GET_CODE (PATTERN (insn)) == ADDR_VEC)
+ changed |= tension_vector_labels (PATTERN (insn), 0);
+ if (GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC)
+ changed |= tension_vector_labels (PATTERN (insn), 1);
+
+ /* If a dispatch table always goes to the same place,
+ get rid of it and replace the insn that uses it. */
+
+ if (GET_CODE (PATTERN (insn)) == ADDR_VEC
+ || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC)
+ {
+ int i;
+ rtx pat = PATTERN (insn);
+ int diff_vec_p = GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC;
+ int len = XVECLEN (pat, diff_vec_p);
+ rtx dispatch = prev_real_insn (insn);
+
+ for (i = 0; i < len; i++)
+ if (XEXP (XVECEXP (pat, diff_vec_p, i), 0)
+ != XEXP (XVECEXP (pat, diff_vec_p, 0), 0))
+ break;
+ if (i == len
+ && dispatch != 0
+ && GET_CODE (dispatch) == JUMP_INSN
+ && JUMP_LABEL (dispatch) != 0
+ /* Don't mess with a casesi insn. */
+ && !(GET_CODE (PATTERN (dispatch)) == SET
+ && (GET_CODE (SET_SRC (PATTERN (dispatch)))
+ == IF_THEN_ELSE))
+ && next_real_insn (JUMP_LABEL (dispatch)) == insn)
+ {
+ redirect_tablejump (dispatch,
+ XEXP (XVECEXP (pat, diff_vec_p, 0), 0));
+ changed = 1;
+ }
+ }
+
+ reallabelprev = prev_active_insn (JUMP_LABEL (insn));
+
+ /* If a jump references the end of the function, try to turn
+ it into a RETURN insn, possibly a conditional one. */
+ if (JUMP_LABEL (insn)
+ && (next_active_insn (JUMP_LABEL (insn)) == 0
+ || GET_CODE (PATTERN (next_active_insn (JUMP_LABEL (insn))))
+ == RETURN))
+ changed |= redirect_jump (insn, NULL_RTX);
+
+ /* Detect jump to following insn. */
+ if (reallabelprev == insn && condjump_p (insn))
+ {
+ delete_jump (insn);
+ changed = 1;
+ continue;
+ }
+
+ /* If we have an unconditional jump preceded by a USE, try to put
+ the USE before the target and jump there. This simplifies many
+ of the optimizations below since we don't have to worry about
+ dealing with these USE insns. We only do this if the label
+ being branch to already has the identical USE or if code
+ never falls through to that label. */
+
+ if (this_is_simplejump
+ && (temp = prev_nonnote_insn (insn)) != 0
+ && GET_CODE (temp) == INSN && GET_CODE (PATTERN (temp)) == USE
+ && (temp1 = prev_nonnote_insn (JUMP_LABEL (insn))) != 0
+ && (GET_CODE (temp1) == BARRIER
+ || (GET_CODE (temp1) == INSN
+ && rtx_equal_p (PATTERN (temp), PATTERN (temp1)))))
+ {
+ if (GET_CODE (temp1) == BARRIER)
+ {
+ emit_insn_after (PATTERN (temp), temp1);
+ temp1 = NEXT_INSN (temp1);
+ }
+
+ delete_insn (temp);
+ redirect_jump (insn, get_label_before (temp1));
+ reallabelprev = prev_real_insn (temp1);
+ changed = 1;
+ }
+
+ /* Simplify if (...) x = a; else x = b; by converting it
+ to x = b; if (...) x = a;
+ if B is sufficiently simple, the test doesn't involve X,
+ and nothing in the test modifies B or X.
+
+ If we have small register classes, we also can't do this if X
+ is a hard register.
+
+ If the "x = b;" insn has any REG_NOTES, we don't do this because
+ of the possibility that we are running after CSE and there is a
+ REG_EQUAL note that is only valid if the branch has already been
+ taken. If we move the insn with the REG_EQUAL note, we may
+ fold the comparison to always be false in a later CSE pass.
+ (We could also delete the REG_NOTES when moving the insn, but it
+ seems simpler to not move it.) An exception is that we can move
+ the insn if the only note is a REG_EQUAL or REG_EQUIV whose
+ value is the same as "b".
+
+ INSN is the branch over the `else' part.
+
+ We set:
+
+ TEMP to the jump insn preceding "x = a;"
+ TEMP1 to X
+ TEMP2 to the insn that sets "x = b;"
+ TEMP3 to the insn that sets "x = a;"
+ TEMP4 to the set of "x = b"; */
+
+ if (this_is_simplejump
+ && (temp3 = prev_active_insn (insn)) != 0
+ && GET_CODE (temp3) == INSN
+ && (temp4 = single_set (temp3)) != 0
+ && GET_CODE (temp1 = SET_DEST (temp4)) == REG
+#ifdef SMALL_REGISTER_CLASSES
+ && REGNO (temp1) >= FIRST_PSEUDO_REGISTER
+#endif
+ && (temp2 = next_active_insn (insn)) != 0
+ && GET_CODE (temp2) == INSN
+ && (temp4 = single_set (temp2)) != 0
+ && rtx_equal_p (SET_DEST (temp4), temp1)
+ && (GET_CODE (SET_SRC (temp4)) == REG
+ || GET_CODE (SET_SRC (temp4)) == SUBREG
+ || CONSTANT_P (SET_SRC (temp4)))
+ && (REG_NOTES (temp2) == 0
+ || ((REG_NOTE_KIND (REG_NOTES (temp2)) == REG_EQUAL
+ || REG_NOTE_KIND (REG_NOTES (temp2)) == REG_EQUIV)
+ && XEXP (REG_NOTES (temp2), 1) == 0
+ && rtx_equal_p (XEXP (REG_NOTES (temp2), 0),
+ SET_SRC (temp4))))
+ && (temp = prev_active_insn (temp3)) != 0
+ && condjump_p (temp) && ! simplejump_p (temp)
+ /* TEMP must skip over the "x = a;" insn */
+ && prev_real_insn (JUMP_LABEL (temp)) == insn
+ && no_labels_between_p (insn, JUMP_LABEL (temp))
+ /* There must be no other entries to the "x = b;" insn. */
+ && no_labels_between_p (JUMP_LABEL (temp), temp2)
+ /* INSN must either branch to the insn after TEMP2 or the insn
+ after TEMP2 must branch to the same place as INSN. */
+ && (reallabelprev == temp2
+ || ((temp5 = next_active_insn (temp2)) != 0
+ && simplejump_p (temp5)
+ && JUMP_LABEL (temp5) == JUMP_LABEL (insn))))
+ {
+ /* The test expression, X, may be a complicated test with
+ multiple branches. See if we can find all the uses of
+ the label that TEMP branches to without hitting a CALL_INSN
+ or a jump to somewhere else. */
+ rtx target = JUMP_LABEL (temp);
+ int nuses = LABEL_NUSES (target);
+ rtx p, q;
+
+ /* Set P to the first jump insn that goes around "x = a;". */
+ for (p = temp; nuses && p; p = prev_nonnote_insn (p))
+ {
+ if (GET_CODE (p) == JUMP_INSN)
+ {
+ if (condjump_p (p) && ! simplejump_p (p)
+ && JUMP_LABEL (p) == target)
+ {
+ nuses--;
+ if (nuses == 0)
+ break;
+ }
+ else
+ break;
+ }
+ else if (GET_CODE (p) == CALL_INSN)
+ break;
+ }
+
+#ifdef HAVE_cc0
+ /* We cannot insert anything between a set of cc and its use
+ so if P uses cc0, we must back up to the previous insn. */
+ q = prev_nonnote_insn (p);
+ if (q && GET_RTX_CLASS (GET_CODE (q)) == 'i'
+ && sets_cc0_p (PATTERN (q)))
+ p = q;
+#endif
+
+ if (p)
+ p = PREV_INSN (p);
+
+ /* If we found all the uses and there was no data conflict, we
+ can move the assignment unless we can branch into the middle
+ from somewhere. */
+ if (nuses == 0 && p
+ && no_labels_between_p (p, insn)
+ && ! reg_referenced_between_p (temp1, p, NEXT_INSN (temp3))
+ && ! reg_set_between_p (temp1, p, temp3)
+ && (GET_CODE (SET_SRC (temp4)) == CONST_INT
+ || ! reg_set_between_p (SET_SRC (temp4), p, temp2)))
+ {
+ emit_insn_after_with_line_notes (PATTERN (temp2), p, temp2);
+ delete_insn (temp2);
+
+ /* Set NEXT to an insn that we know won't go away. */
+ next = next_active_insn (insn);
+
+ /* Delete the jump around the set. Note that we must do
+ this before we redirect the test jumps so that it won't
+ delete the code immediately following the assignment
+ we moved (which might be a jump). */
+
+ delete_insn (insn);
+
+ /* We either have two consecutive labels or a jump to
+ a jump, so adjust all the JUMP_INSNs to branch to where
+ INSN branches to. */
+ for (p = NEXT_INSN (p); p != next; p = NEXT_INSN (p))
+ if (GET_CODE (p) == JUMP_INSN)
+ redirect_jump (p, target);
+
+ changed = 1;
+ continue;
+ }
+ }
+
+#ifndef HAVE_cc0
+ /* If we have if (...) x = exp; and branches are expensive,
+ EXP is a single insn, does not have any side effects, cannot
+ trap, and is not too costly, convert this to
+ t = exp; if (...) x = t;
+
+ Don't do this when we have CC0 because it is unlikely to help
+ and we'd need to worry about where to place the new insn and
+ the potential for conflicts. We also can't do this when we have
+ notes on the insn for the same reason as above.
+
+ We set:
+
+ TEMP to the "x = exp;" insn.
+ TEMP1 to the single set in the "x = exp; insn.
+ TEMP2 to "x". */
+
+ if (! reload_completed
+ && this_is_condjump && ! this_is_simplejump
+ && BRANCH_COST >= 3
+ && (temp = next_nonnote_insn (insn)) != 0
+ && GET_CODE (temp) == INSN
+ && REG_NOTES (temp) == 0
+ && (reallabelprev == temp
+ || ((temp2 = next_active_insn (temp)) != 0
+ && simplejump_p (temp2)
+ && JUMP_LABEL (temp2) == JUMP_LABEL (insn)))
+ && (temp1 = single_set (temp)) != 0
+ && (temp2 = SET_DEST (temp1), GET_CODE (temp2) == REG)
+ && GET_MODE_CLASS (GET_MODE (temp2)) == MODE_INT
+#ifdef SMALL_REGISTER_CLASSES
+ && REGNO (temp2) >= FIRST_PSEUDO_REGISTER
+#endif
+ && GET_CODE (SET_SRC (temp1)) != REG
+ && GET_CODE (SET_SRC (temp1)) != SUBREG
+ && GET_CODE (SET_SRC (temp1)) != CONST_INT
+ && ! side_effects_p (SET_SRC (temp1))
+ && ! may_trap_p (SET_SRC (temp1))
+ && rtx_cost (SET_SRC (temp1)) < 10)
+ {
+ rtx new = gen_reg_rtx (GET_MODE (temp2));
+
+ if (validate_change (temp, &SET_DEST (temp1), new, 0))
+ {
+ next = emit_insn_after (gen_move_insn (temp2, new), insn);
+ emit_insn_after_with_line_notes (PATTERN (temp),
+ PREV_INSN (insn), temp);
+ delete_insn (temp);
+ reallabelprev = prev_active_insn (JUMP_LABEL (insn));
+ }
+ }
+
+ /* Similarly, if it takes two insns to compute EXP but they
+ have the same destination. Here TEMP3 will be the second
+ insn and TEMP4 the SET from that insn. */
+
+ if (! reload_completed
+ && this_is_condjump && ! this_is_simplejump
+ && BRANCH_COST >= 4
+ && (temp = next_nonnote_insn (insn)) != 0
+ && GET_CODE (temp) == INSN
+ && REG_NOTES (temp) == 0
+ && (temp3 = next_nonnote_insn (temp)) != 0
+ && GET_CODE (temp3) == INSN
+ && REG_NOTES (temp3) == 0
+ && (reallabelprev == temp3
+ || ((temp2 = next_active_insn (temp3)) != 0
+ && simplejump_p (temp2)
+ && JUMP_LABEL (temp2) == JUMP_LABEL (insn)))
+ && (temp1 = single_set (temp)) != 0
+ && (temp2 = SET_DEST (temp1), GET_CODE (temp2) == REG)
+ && GET_MODE_CLASS (GET_MODE (temp2)) == MODE_INT
+#ifdef SMALL_REGISTER_CLASSES
+ && REGNO (temp2) >= FIRST_PSEUDO_REGISTER
+#endif
+ && ! side_effects_p (SET_SRC (temp1))
+ && ! may_trap_p (SET_SRC (temp1))
+ && rtx_cost (SET_SRC (temp1)) < 10
+ && (temp4 = single_set (temp3)) != 0
+ && rtx_equal_p (SET_DEST (temp4), temp2)
+ && ! side_effects_p (SET_SRC (temp4))
+ && ! may_trap_p (SET_SRC (temp4))
+ && rtx_cost (SET_SRC (temp4)) < 10)
+ {
+ rtx new = gen_reg_rtx (GET_MODE (temp2));
+
+ if (validate_change (temp, &SET_DEST (temp1), new, 0))
+ {
+ next = emit_insn_after (gen_move_insn (temp2, new), insn);
+ emit_insn_after_with_line_notes (PATTERN (temp),
+ PREV_INSN (insn), temp);
+ emit_insn_after_with_line_notes
+ (replace_rtx (PATTERN (temp3), temp2, new),
+ PREV_INSN (insn), temp3);
+ delete_insn (temp);
+ delete_insn (temp3);
+ reallabelprev = prev_active_insn (JUMP_LABEL (insn));
+ }
+ }
+
+ /* Finally, handle the case where two insns are used to
+ compute EXP but a temporary register is used. Here we must
+ ensure that the temporary register is not used anywhere else. */
+
+ if (! reload_completed
+ && after_regscan
+ && this_is_condjump && ! this_is_simplejump
+ && BRANCH_COST >= 4
+ && (temp = next_nonnote_insn (insn)) != 0
+ && GET_CODE (temp) == INSN
+ && REG_NOTES (temp) == 0
+ && (temp3 = next_nonnote_insn (temp)) != 0
+ && GET_CODE (temp3) == INSN
+ && REG_NOTES (temp3) == 0
+ && (reallabelprev == temp3
+ || ((temp2 = next_active_insn (temp3)) != 0
+ && simplejump_p (temp2)
+ && JUMP_LABEL (temp2) == JUMP_LABEL (insn)))
+ && (temp1 = single_set (temp)) != 0
+ && (temp5 = SET_DEST (temp1),
+ (GET_CODE (temp5) == REG
+ || (GET_CODE (temp5) == SUBREG
+ && (temp5 = SUBREG_REG (temp5),
+ GET_CODE (temp5) == REG))))
+ && REGNO (temp5) >= FIRST_PSEUDO_REGISTER
+ && regno_first_uid[REGNO (temp5)] == INSN_UID (temp)
+ && regno_last_uid[REGNO (temp5)] == INSN_UID (temp3)
+ && ! side_effects_p (SET_SRC (temp1))
+ && ! may_trap_p (SET_SRC (temp1))
+ && rtx_cost (SET_SRC (temp1)) < 10
+ && (temp4 = single_set (temp3)) != 0
+ && (temp2 = SET_DEST (temp4), GET_CODE (temp2) == REG)
+ && GET_MODE_CLASS (GET_MODE (temp2)) == MODE_INT
+#ifdef SMALL_REGISTER_CLASSES
+ && REGNO (temp2) >= FIRST_PSEUDO_REGISTER
+#endif
+ && rtx_equal_p (SET_DEST (temp4), temp2)
+ && ! side_effects_p (SET_SRC (temp4))
+ && ! may_trap_p (SET_SRC (temp4))
+ && rtx_cost (SET_SRC (temp4)) < 10)
+ {
+ rtx new = gen_reg_rtx (GET_MODE (temp2));
+
+ if (validate_change (temp3, &SET_DEST (temp4), new, 0))
+ {
+ next = emit_insn_after (gen_move_insn (temp2, new), insn);
+ emit_insn_after_with_line_notes (PATTERN (temp),
+ PREV_INSN (insn), temp);
+ emit_insn_after_with_line_notes (PATTERN (temp3),
+ PREV_INSN (insn), temp3);
+ delete_insn (temp);
+ delete_insn (temp3);
+ reallabelprev = prev_active_insn (JUMP_LABEL (insn));
+ }
+ }
+#endif /* HAVE_cc0 */
+
+ /* We deal with four cases:
+
+ 1) x = a; if (...) x = b; and either A or B is zero,
+ 2) if (...) x = 0; and jumps are expensive,
+ 3) x = a; if (...) x = b; and A and B are constants where all the
+ set bits in A are also set in B and jumps are expensive, and
+ 4) x = a; if (...) x = b; and A and B non-zero, and jumps are
+ more expensive.
+ 5) if (...) x = b; if jumps are even more expensive.
+
+ In each of these try to use a store-flag insn to avoid the jump.
+ (If the jump would be faster, the machine should not have
+ defined the scc insns!). These cases are often made by the
+ previous optimization.
+
+ INSN here is the jump around the store. We set:
+
+ TEMP to the "x = b;" insn.
+ TEMP1 to X.
+ TEMP2 to B (const0_rtx in the second case).
+ TEMP3 to A (X in the second case).
+ TEMP4 to the condition being tested.
+ TEMP5 to the earliest insn used to find the condition. */
+
+ if (/* We can't do this after reload has completed. */
+ ! reload_completed
+ && this_is_condjump && ! this_is_simplejump
+ /* Set TEMP to the "x = b;" insn. */
+ && (temp = next_nonnote_insn (insn)) != 0
+ && GET_CODE (temp) == INSN
+ && GET_CODE (PATTERN (temp)) == SET
+ && GET_CODE (temp1 = SET_DEST (PATTERN (temp))) == REG
+#ifdef SMALL_REGISTER_CLASSES
+ && REGNO (temp1) >= FIRST_PSEUDO_REGISTER
+#endif
+ && GET_MODE_CLASS (GET_MODE (temp1)) == MODE_INT
+ && (GET_CODE (temp2 = SET_SRC (PATTERN (temp))) == REG
+ || GET_CODE (temp2) == SUBREG
+ || GET_CODE (temp2) == CONST_INT)
+ /* Allow either form, but prefer the former if both apply.
+ There is no point in using the old value of TEMP1 if
+ it is a register, since cse will alias them. It can
+ lose if the old value were a hard register since CSE
+ won't replace hard registers. */
+ && (((temp3 = reg_set_last (temp1, insn)) != 0
+ && GET_CODE (temp3) == CONST_INT)
+ /* Make the latter case look like x = x; if (...) x = 0; */
+ || (temp3 = temp1,
+ ((BRANCH_COST >= 2
+ && temp2 == const0_rtx)
+#ifdef HAVE_conditional_move
+ || HAVE_conditional_move
+#endif
+ || BRANCH_COST >= 3)))
+ /* INSN must either branch to the insn after TEMP or the insn
+ after TEMP must branch to the same place as INSN. */
+ && (reallabelprev == temp
+ || ((temp4 = next_active_insn (temp)) != 0
+ && simplejump_p (temp4)
+ && JUMP_LABEL (temp4) == JUMP_LABEL (insn)))
+ && (temp4 = get_condition (insn, &temp5)) != 0
+ /* We must be comparing objects whose modes imply the size.
+ We could handle BLKmode if (1) emit_store_flag could
+ and (2) we could find the size reliably. */
+ && GET_MODE (XEXP (temp4, 0)) != BLKmode
+
+ /* If B is zero, OK; if A is zero, can only do (1) if we
+ can reverse the condition. See if (3) applies possibly
+ by reversing the condition. Prefer reversing to (4) when
+ branches are very expensive. */
+ && ((reversep = 0, temp2 == const0_rtx)
+ || (temp3 == const0_rtx
+ && (reversep = can_reverse_comparison_p (temp4, insn)))
+ || (BRANCH_COST >= 2
+ && GET_CODE (temp2) == CONST_INT
+ && GET_CODE (temp3) == CONST_INT
+ && ((INTVAL (temp2) & INTVAL (temp3)) == INTVAL (temp2)
+ || ((INTVAL (temp2) & INTVAL (temp3)) == INTVAL (temp3)
+ && (reversep = can_reverse_comparison_p (temp4,
+ insn)))))
+#ifdef HAVE_conditional_move
+ || HAVE_conditional_move
+#endif
+ || BRANCH_COST >= 3)
+#ifdef HAVE_cc0
+ /* If the previous insn sets CC0 and something else, we can't
+ do this since we are going to delete that insn. */
+
+ && ! ((temp6 = prev_nonnote_insn (insn)) != 0
+ && GET_CODE (temp6) == INSN
+ && (sets_cc0_p (PATTERN (temp6)) == -1
+ || (sets_cc0_p (PATTERN (temp6)) == 1
+ && FIND_REG_INC_NOTE (temp6, NULL_RTX))))
+#endif
+ )
+ {
+ enum rtx_code code = GET_CODE (temp4);
+ rtx uval, cval, var = temp1;
+ int normalizep;
+ rtx target;
+
+ /* If necessary, reverse the condition. */
+ if (reversep)
+ code = reverse_condition (code), uval = temp2, cval = temp3;
+ else
+ uval = temp3, cval = temp2;
+
+ /* See if we can do this with a store-flag insn. */
+ start_sequence ();
+
+ /* If CVAL is non-zero, normalize to -1. Otherwise,
+ if UVAL is the constant 1, it is best to just compute
+ the result directly. If UVAL is constant and STORE_FLAG_VALUE
+ includes all of its bits, it is best to compute the flag
+ value unnormalized and `and' it with UVAL. Otherwise,
+ normalize to -1 and `and' with UVAL. */
+ normalizep = (cval != const0_rtx ? -1
+ : (uval == const1_rtx ? 1
+ : (GET_CODE (uval) == CONST_INT
+ && (INTVAL (uval) & ~STORE_FLAG_VALUE) == 0)
+ ? 0 : -1));
+
+ /* We will be putting the store-flag insn immediately in
+ front of the comparison that was originally being done,
+ so we know all the variables in TEMP4 will be valid.
+ However, this might be in front of the assignment of
+ A to VAR. If it is, it would clobber the store-flag
+ we will be emitting.
+
+ Therefore, emit into a temporary which will be copied to
+ VAR immediately after TEMP. */
+
+ target = emit_store_flag (gen_reg_rtx (GET_MODE (var)), code,
+ XEXP (temp4, 0), XEXP (temp4, 1),
+ VOIDmode,
+ (code == LTU || code == LEU
+ || code == GEU || code == GTU),
+ normalizep);
+ if (target)
+ {
+ rtx before = insn;
+ rtx seq;
+
+ /* Put the store-flag insns in front of the first insn
+ used to compute the condition to ensure that we
+ use the same values of them as the current
+ comparison. However, the remainder of the insns we
+ generate will be placed directly in front of the
+ jump insn, in case any of the pseudos we use
+ are modified earlier. */
+
+ seq = get_insns ();
+ end_sequence ();
+
+ emit_insns_before (seq, temp5);
+
+ start_sequence ();
+
+ /* Both CVAL and UVAL are non-zero. */
+ if (cval != const0_rtx && uval != const0_rtx)
+ {
+ rtx tem1, tem2;
+
+ tem1 = expand_and (uval, target, NULL_RTX);
+ if (GET_CODE (cval) == CONST_INT
+ && GET_CODE (uval) == CONST_INT
+ && (INTVAL (cval) & INTVAL (uval)) == INTVAL (cval))
+ tem2 = cval;
+ else
+ {
+ tem2 = expand_unop (GET_MODE (var), one_cmpl_optab,
+ target, NULL_RTX, 0);
+ tem2 = expand_and (cval, tem2,
+ (GET_CODE (tem2) == REG
+ ? tem2 : 0));
+ }
+
+ /* If we usually make new pseudos, do so here. This
+ turns out to help machines that have conditional
+ move insns. */
+
+ if (flag_expensive_optimizations)
+ target = 0;
+
+ target = expand_binop (GET_MODE (var), ior_optab,
+ tem1, tem2, target,
+ 1, OPTAB_WIDEN);
+ }
+ else if (normalizep != 1)
+ {
+ /* We know that either CVAL or UVAL is zero. If
+ UVAL is zero, negate TARGET and `and' with CVAL.
+ Otherwise, `and' with UVAL. */
+ if (uval == const0_rtx)
+ {
+ target = expand_unop (GET_MODE (var), one_cmpl_optab,
+ target, NULL_RTX, 0);
+ uval = cval;
+ }
+
+ target = expand_and (uval, target,
+ (GET_CODE (target) == REG
+ && ! preserve_subexpressions_p ()
+ ? target : NULL_RTX));
+ }
+
+ emit_move_insn (var, target);
+ seq = get_insns ();
+ end_sequence ();
+
+#ifdef HAVE_cc0
+ /* If INSN uses CC0, we must not separate it from the
+ insn that sets cc0. */
+
+ if (reg_mentioned_p (cc0_rtx, PATTERN (before)))
+ before = prev_nonnote_insn (before);
+#endif
+
+ emit_insns_before (seq, before);
+
+ delete_insn (temp);
+ next = NEXT_INSN (insn);
+
+ delete_jump (insn);
+ changed = 1;
+ continue;
+ }
+ else
+ end_sequence ();
+ }
+
+ /* If branches are expensive, convert
+ if (foo) bar++; to bar += (foo != 0);
+ and similarly for "bar--;"
+
+ INSN is the conditional branch around the arithmetic. We set:
+
+ TEMP is the arithmetic insn.
+ TEMP1 is the SET doing the arithmetic.
+ TEMP2 is the operand being incremented or decremented.
+ TEMP3 to the condition being tested.
+ TEMP4 to the earliest insn used to find the condition. */
+
+ if ((BRANCH_COST >= 2
+#ifdef HAVE_incscc
+ || HAVE_incscc
+#endif
+#ifdef HAVE_decscc
+ || HAVE_decscc
+#endif
+ )
+ && ! reload_completed
+ && this_is_condjump && ! this_is_simplejump
+ && (temp = next_nonnote_insn (insn)) != 0
+ && (temp1 = single_set (temp)) != 0
+ && (temp2 = SET_DEST (temp1),
+ GET_MODE_CLASS (GET_MODE (temp2)) == MODE_INT)
+ && GET_CODE (SET_SRC (temp1)) == PLUS
+ && (XEXP (SET_SRC (temp1), 1) == const1_rtx
+ || XEXP (SET_SRC (temp1), 1) == constm1_rtx)
+ && rtx_equal_p (temp2, XEXP (SET_SRC (temp1), 0))
+ && ! side_effects_p (temp2)
+ && ! may_trap_p (temp2)
+ /* INSN must either branch to the insn after TEMP or the insn
+ after TEMP must branch to the same place as INSN. */
+ && (reallabelprev == temp
+ || ((temp3 = next_active_insn (temp)) != 0
+ && simplejump_p (temp3)
+ && JUMP_LABEL (temp3) == JUMP_LABEL (insn)))
+ && (temp3 = get_condition (insn, &temp4)) != 0
+ /* We must be comparing objects whose modes imply the size.
+ We could handle BLKmode if (1) emit_store_flag could
+ and (2) we could find the size reliably. */
+ && GET_MODE (XEXP (temp3, 0)) != BLKmode
+ && can_reverse_comparison_p (temp3, insn))
+ {
+ rtx temp6, target = 0, seq, init_insn = 0, init = temp2;
+ enum rtx_code code = reverse_condition (GET_CODE (temp3));
+
+ start_sequence ();
+
+ /* It must be the case that TEMP2 is not modified in the range
+ [TEMP4, INSN). The one exception we make is if the insn
+ before INSN sets TEMP2 to something which is also unchanged
+ in that range. In that case, we can move the initialization
+ into our sequence. */
+
+ if ((temp5 = prev_active_insn (insn)) != 0
+ && GET_CODE (temp5) == INSN
+ && (temp6 = single_set (temp5)) != 0
+ && rtx_equal_p (temp2, SET_DEST (temp6))
+ && (CONSTANT_P (SET_SRC (temp6))
+ || GET_CODE (SET_SRC (temp6)) == REG
+ || GET_CODE (SET_SRC (temp6)) == SUBREG))
+ {
+ emit_insn (PATTERN (temp5));
+ init_insn = temp5;
+ init = SET_SRC (temp6);
+ }
+
+ if (CONSTANT_P (init)
+ || ! reg_set_between_p (init, PREV_INSN (temp4), insn))
+ target = emit_store_flag (gen_reg_rtx (GET_MODE (temp2)), code,
+ XEXP (temp3, 0), XEXP (temp3, 1),
+ VOIDmode,
+ (code == LTU || code == LEU
+ || code == GTU || code == GEU), 1);
+
+ /* If we can do the store-flag, do the addition or
+ subtraction. */
+
+ if (target)
+ target = expand_binop (GET_MODE (temp2),
+ (XEXP (SET_SRC (temp1), 1) == const1_rtx
+ ? add_optab : sub_optab),
+ temp2, target, temp2, 0, OPTAB_WIDEN);
+
+ if (target != 0)
+ {
+ /* Put the result back in temp2 in case it isn't already.
+ Then replace the jump, possible a CC0-setting insn in
+ front of the jump, and TEMP, with the sequence we have
+ made. */
+
+ if (target != temp2)
+ emit_move_insn (temp2, target);
+
+ seq = get_insns ();
+ end_sequence ();
+
+ emit_insns_before (seq, temp4);
+ delete_insn (temp);
+
+ if (init_insn)
+ delete_insn (init_insn);
+
+ next = NEXT_INSN (insn);
+#ifdef HAVE_cc0
+ delete_insn (prev_nonnote_insn (insn));
+#endif
+ delete_insn (insn);
+ changed = 1;
+ continue;
+ }
+ else
+ end_sequence ();
+ }
+
+ /* Simplify if (...) x = 1; else {...} if (x) ...
+ We recognize this case scanning backwards as well.
+
+ TEMP is the assignment to x;
+ TEMP1 is the label at the head of the second if. */
+ /* ?? This should call get_condition to find the values being
+ compared, instead of looking for a COMPARE insn when HAVE_cc0
+ is not defined. This would allow it to work on the m88k. */
+ /* ?? This optimization is only safe before cse is run if HAVE_cc0
+ is not defined and the condition is tested by a separate compare
+ insn. This is because the code below assumes that the result
+ of the compare dies in the following branch.
+
+ Not only that, but there might be other insns between the
+ compare and branch whose results are live. Those insns need
+ to be executed.
+
+ A way to fix this is to move the insns at JUMP_LABEL (insn)
+ to before INSN. If we are running before flow, they will
+ be deleted if they aren't needed. But this doesn't work
+ well after flow.
+
+ This is really a special-case of jump threading, anyway. The
+ right thing to do is to replace this and jump threading with
+ much simpler code in cse.
+
+ This code has been turned off in the non-cc0 case in the
+ meantime. */
+
+#ifdef HAVE_cc0
+ else if (this_is_simplejump
+ /* Safe to skip USE and CLOBBER insns here
+ since they will not be deleted. */
+ && (temp = prev_active_insn (insn))
+ && no_labels_between_p (temp, insn)
+ && GET_CODE (temp) == INSN
+ && GET_CODE (PATTERN (temp)) == SET
+ && GET_CODE (SET_DEST (PATTERN (temp))) == REG
+ && CONSTANT_P (SET_SRC (PATTERN (temp)))
+ && (temp1 = next_active_insn (JUMP_LABEL (insn)))
+ /* If we find that the next value tested is `x'
+ (TEMP1 is the insn where this happens), win. */
+ && GET_CODE (temp1) == INSN
+ && GET_CODE (PATTERN (temp1)) == SET
+#ifdef HAVE_cc0
+ /* Does temp1 `tst' the value of x? */
+ && SET_SRC (PATTERN (temp1)) == SET_DEST (PATTERN (temp))
+ && SET_DEST (PATTERN (temp1)) == cc0_rtx
+ && (temp1 = next_nonnote_insn (temp1))
+#else
+ /* Does temp1 compare the value of x against zero? */
+ && GET_CODE (SET_SRC (PATTERN (temp1))) == COMPARE
+ && XEXP (SET_SRC (PATTERN (temp1)), 1) == const0_rtx
+ && (XEXP (SET_SRC (PATTERN (temp1)), 0)
+ == SET_DEST (PATTERN (temp)))
+ && GET_CODE (SET_DEST (PATTERN (temp1))) == REG
+ && (temp1 = find_next_ref (SET_DEST (PATTERN (temp1)), temp1))
+#endif
+ && condjump_p (temp1))
+ {
+ /* Get the if_then_else from the condjump. */
+ rtx choice = SET_SRC (PATTERN (temp1));
+ if (GET_CODE (choice) == IF_THEN_ELSE)
+ {
+ enum rtx_code code = GET_CODE (XEXP (choice, 0));
+ rtx val = SET_SRC (PATTERN (temp));
+ rtx cond
+ = simplify_relational_operation (code, GET_MODE (SET_DEST (PATTERN (temp))),
+ val, const0_rtx);
+ rtx ultimate;
+
+ if (cond == const_true_rtx)
+ ultimate = XEXP (choice, 1);
+ else if (cond == const0_rtx)
+ ultimate = XEXP (choice, 2);
+ else
+ ultimate = 0;
+
+ if (ultimate == pc_rtx)
+ ultimate = get_label_after (temp1);
+ else if (ultimate && GET_CODE (ultimate) != RETURN)
+ ultimate = XEXP (ultimate, 0);
+
+ if (ultimate)
+ changed |= redirect_jump (insn, ultimate);
+ }
+ }
+#endif
+
+#if 0
+ /* @@ This needs a bit of work before it will be right.
+
+ Any type of comparison can be accepted for the first and
+ second compare. When rewriting the first jump, we must
+ compute the what conditions can reach label3, and use the
+ appropriate code. We can not simply reverse/swap the code
+ of the first jump. In some cases, the second jump must be
+ rewritten also.
+
+ For example,
+ < == converts to > ==
+ < != converts to == >
+ etc.
+
+ If the code is written to only accept an '==' test for the second
+ compare, then all that needs to be done is to swap the condition
+ of the first branch.
+
+ It is questionable whether we want this optimization anyways,
+ since if the user wrote code like this because he/she knew that
+ the jump to label1 is taken most of the time, then rewriting
+ this gives slower code. */
+ /* @@ This should call get_condition to find the values being
+ compared, instead of looking for a COMPARE insn when HAVE_cc0
+ is not defined. This would allow it to work on the m88k. */
+ /* @@ This optimization is only safe before cse is run if HAVE_cc0
+ is not defined and the condition is tested by a separate compare
+ insn. This is because the code below assumes that the result
+ of the compare dies in the following branch. */
+
+ /* Simplify test a ~= b
+ condjump label1;
+ test a == b
+ condjump label2;
+ jump label3;
+ label1:
+
+ rewriting as
+ test a ~~= b
+ condjump label3
+ test a == b
+ condjump label2
+ label1:
+
+ where ~= is an inequality, e.g. >, and ~~= is the swapped
+ inequality, e.g. <.
+
+ We recognize this case scanning backwards.
+
+ TEMP is the conditional jump to `label2';
+ TEMP1 is the test for `a == b';
+ TEMP2 is the conditional jump to `label1';
+ TEMP3 is the test for `a ~= b'. */
+ else if (this_is_simplejump
+ && (temp = prev_active_insn (insn))
+ && no_labels_between_p (temp, insn)
+ && condjump_p (temp)
+ && (temp1 = prev_active_insn (temp))
+ && no_labels_between_p (temp1, temp)
+ && GET_CODE (temp1) == INSN
+ && GET_CODE (PATTERN (temp1)) == SET
+#ifdef HAVE_cc0
+ && sets_cc0_p (PATTERN (temp1)) == 1
+#else
+ && GET_CODE (SET_SRC (PATTERN (temp1))) == COMPARE
+ && GET_CODE (SET_DEST (PATTERN (temp1))) == REG
+ && (temp == find_next_ref (SET_DEST (PATTERN (temp1)), temp1))
+#endif
+ && (temp2 = prev_active_insn (temp1))
+ && no_labels_between_p (temp2, temp1)
+ && condjump_p (temp2)
+ && JUMP_LABEL (temp2) == next_nonnote_insn (NEXT_INSN (insn))
+ && (temp3 = prev_active_insn (temp2))
+ && no_labels_between_p (temp3, temp2)
+ && GET_CODE (PATTERN (temp3)) == SET
+ && rtx_equal_p (SET_DEST (PATTERN (temp3)),
+ SET_DEST (PATTERN (temp1)))
+ && rtx_equal_p (SET_SRC (PATTERN (temp1)),
+ SET_SRC (PATTERN (temp3)))
+ && ! inequality_comparisons_p (PATTERN (temp))
+ && inequality_comparisons_p (PATTERN (temp2)))
+ {
+ rtx fallthrough_label = JUMP_LABEL (temp2);
+
+ ++LABEL_NUSES (fallthrough_label);
+ if (swap_jump (temp2, JUMP_LABEL (insn)))
+ {
+ delete_insn (insn);
+ changed = 1;
+ }
+
+ if (--LABEL_NUSES (fallthrough_label) == 0)
+ delete_insn (fallthrough_label);
+ }
+#endif
+ /* Simplify if (...) {... x = 1;} if (x) ...
+
+ We recognize this case backwards.
+
+ TEMP is the test of `x';
+ TEMP1 is the assignment to `x' at the end of the
+ previous statement. */
+ /* @@ This should call get_condition to find the values being
+ compared, instead of looking for a COMPARE insn when HAVE_cc0
+ is not defined. This would allow it to work on the m88k. */
+ /* @@ This optimization is only safe before cse is run if HAVE_cc0
+ is not defined and the condition is tested by a separate compare
+ insn. This is because the code below assumes that the result
+ of the compare dies in the following branch. */
+
+ /* ??? This has to be turned off. The problem is that the
+ unconditional jump might indirectly end up branching to the
+ label between TEMP1 and TEMP. We can't detect this, in general,
+ since it may become a jump to there after further optimizations.
+ If that jump is done, it will be deleted, so we will retry
+ this optimization in the next pass, thus an infinite loop.
+
+ The present code prevents this by putting the jump after the
+ label, but this is not logically correct. */
+#if 0
+ else if (this_is_condjump
+ /* Safe to skip USE and CLOBBER insns here
+ since they will not be deleted. */
+ && (temp = prev_active_insn (insn))
+ && no_labels_between_p (temp, insn)
+ && GET_CODE (temp) == INSN
+ && GET_CODE (PATTERN (temp)) == SET
+#ifdef HAVE_cc0
+ && sets_cc0_p (PATTERN (temp)) == 1
+ && GET_CODE (SET_SRC (PATTERN (temp))) == REG
+#else
+ /* Temp must be a compare insn, we can not accept a register
+ to register move here, since it may not be simply a
+ tst insn. */
+ && GET_CODE (SET_SRC (PATTERN (temp))) == COMPARE
+ && XEXP (SET_SRC (PATTERN (temp)), 1) == const0_rtx
+ && GET_CODE (XEXP (SET_SRC (PATTERN (temp)), 0)) == REG
+ && GET_CODE (SET_DEST (PATTERN (temp))) == REG
+ && insn == find_next_ref (SET_DEST (PATTERN (temp)), temp)
+#endif
+ /* May skip USE or CLOBBER insns here
+ for checking for opportunity, since we
+ take care of them later. */
+ && (temp1 = prev_active_insn (temp))
+ && GET_CODE (temp1) == INSN
+ && GET_CODE (PATTERN (temp1)) == SET
+#ifdef HAVE_cc0
+ && SET_SRC (PATTERN (temp)) == SET_DEST (PATTERN (temp1))
+#else
+ && (XEXP (SET_SRC (PATTERN (temp)), 0)
+ == SET_DEST (PATTERN (temp1)))
+#endif
+ && CONSTANT_P (SET_SRC (PATTERN (temp1)))
+ /* If this isn't true, cse will do the job. */
+ && ! no_labels_between_p (temp1, temp))
+ {
+ /* Get the if_then_else from the condjump. */
+ rtx choice = SET_SRC (PATTERN (insn));
+ if (GET_CODE (choice) == IF_THEN_ELSE
+ && (GET_CODE (XEXP (choice, 0)) == EQ
+ || GET_CODE (XEXP (choice, 0)) == NE))
+ {
+ int want_nonzero = (GET_CODE (XEXP (choice, 0)) == NE);
+ rtx last_insn;
+ rtx ultimate;
+ rtx p;
+
+ /* Get the place that condjump will jump to
+ if it is reached from here. */
+ if ((SET_SRC (PATTERN (temp1)) != const0_rtx)
+ == want_nonzero)
+ ultimate = XEXP (choice, 1);
+ else
+ ultimate = XEXP (choice, 2);
+ /* Get it as a CODE_LABEL. */
+ if (ultimate == pc_rtx)
+ ultimate = get_label_after (insn);
+ else
+ /* Get the label out of the LABEL_REF. */
+ ultimate = XEXP (ultimate, 0);
+
+ /* Insert the jump immediately before TEMP, specifically
+ after the label that is between TEMP1 and TEMP. */
+ last_insn = PREV_INSN (temp);
+
+ /* If we would be branching to the next insn, the jump
+ would immediately be deleted and the re-inserted in
+ a subsequent pass over the code. So don't do anything
+ in that case. */
+ if (next_active_insn (last_insn)
+ != next_active_insn (ultimate))
+ {
+ emit_barrier_after (last_insn);
+ p = emit_jump_insn_after (gen_jump (ultimate),
+ last_insn);
+ JUMP_LABEL (p) = ultimate;
+ ++LABEL_NUSES (ultimate);
+ if (INSN_UID (ultimate) < max_jump_chain
+ && INSN_CODE (p) < max_jump_chain)
+ {
+ jump_chain[INSN_UID (p)]
+ = jump_chain[INSN_UID (ultimate)];
+ jump_chain[INSN_UID (ultimate)] = p;
+ }
+ changed = 1;
+ continue;
+ }
+ }
+ }
+#endif
+ /* Detect a conditional jump going to the same place
+ as an immediately following unconditional jump. */
+ else if (this_is_condjump
+ && (temp = next_active_insn (insn)) != 0
+ && simplejump_p (temp)
+ && (next_active_insn (JUMP_LABEL (insn))
+ == next_active_insn (JUMP_LABEL (temp))))
+ {
+ delete_jump (insn);
+ changed = 1;
+ continue;
+ }
+ /* Detect a conditional jump jumping over an unconditional jump. */
+
+ else if ((this_is_condjump || this_is_condjump_in_parallel)
+ && ! this_is_simplejump
+ && reallabelprev != 0
+ && GET_CODE (reallabelprev) == JUMP_INSN
+ && prev_active_insn (reallabelprev) == insn
+ && no_labels_between_p (insn, reallabelprev)
+ && simplejump_p (reallabelprev))
+ {
+ /* When we invert the unconditional jump, we will be
+ decrementing the usage count of its old label.
+ Make sure that we don't delete it now because that
+ might cause the following code to be deleted. */
+ rtx prev_uses = prev_nonnote_insn (reallabelprev);
+ rtx prev_label = JUMP_LABEL (insn);
+
+ if (prev_label)
+ ++LABEL_NUSES (prev_label);
+
+ if (invert_jump (insn, JUMP_LABEL (reallabelprev)))
+ {
+ /* It is very likely that if there are USE insns before
+ this jump, they hold REG_DEAD notes. These REG_DEAD
+ notes are no longer valid due to this optimization,
+ and will cause the life-analysis that following passes
+ (notably delayed-branch scheduling) to think that
+ these registers are dead when they are not.
+
+ To prevent this trouble, we just remove the USE insns
+ from the insn chain. */
+
+ while (prev_uses && GET_CODE (prev_uses) == INSN
+ && GET_CODE (PATTERN (prev_uses)) == USE)
+ {
+ rtx useless = prev_uses;
+ prev_uses = prev_nonnote_insn (prev_uses);
+ delete_insn (useless);
+ }
+
+ delete_insn (reallabelprev);
+ next = insn;
+ changed = 1;
+ }
+
+ /* We can now safely delete the label if it is unreferenced
+ since the delete_insn above has deleted the BARRIER. */
+ if (prev_label && --LABEL_NUSES (prev_label) == 0)
+ delete_insn (prev_label);
+ continue;
+ }
+ else
+ {
+ /* Detect a jump to a jump. */
+
+ nlabel = follow_jumps (JUMP_LABEL (insn));
+ if (nlabel != JUMP_LABEL (insn)
+ && redirect_jump (insn, nlabel))
+ {
+ changed = 1;
+ next = insn;
+ }
+
+ /* Look for if (foo) bar; else break; */
+ /* The insns look like this:
+ insn = condjump label1;
+ ...range1 (some insns)...
+ jump label2;
+ label1:
+ ...range2 (some insns)...
+ jump somewhere unconditionally
+ label2: */
+ {
+ rtx label1 = next_label (insn);
+ rtx range1end = label1 ? prev_active_insn (label1) : 0;
+ /* Don't do this optimization on the first round, so that
+ jump-around-a-jump gets simplified before we ask here
+ whether a jump is unconditional.
+
+ Also don't do it when we are called after reload since
+ it will confuse reorg. */
+ if (! first
+ && (reload_completed ? ! flag_delayed_branch : 1)
+ /* Make sure INSN is something we can invert. */
+ && condjump_p (insn)
+ && label1 != 0
+ && JUMP_LABEL (insn) == label1
+ && LABEL_NUSES (label1) == 1
+ && GET_CODE (range1end) == JUMP_INSN
+ && simplejump_p (range1end))
+ {
+ rtx label2 = next_label (label1);
+ rtx range2end = label2 ? prev_active_insn (label2) : 0;
+ if (range1end != range2end
+ && JUMP_LABEL (range1end) == label2
+ && GET_CODE (range2end) == JUMP_INSN
+ && GET_CODE (NEXT_INSN (range2end)) == BARRIER
+ /* Invert the jump condition, so we
+ still execute the same insns in each case. */
+ && invert_jump (insn, label1))
+ {
+ rtx range1beg = next_active_insn (insn);
+ rtx range2beg = next_active_insn (label1);
+ rtx range1after, range2after;
+ rtx range1before, range2before;
+ rtx rangenext;
+
+ /* Include in each range any notes before it, to be
+ sure that we get the line number note if any, even
+ if there are other notes here. */
+ while (PREV_INSN (range1beg)
+ && GET_CODE (PREV_INSN (range1beg)) == NOTE)
+ range1beg = PREV_INSN (range1beg);
+
+ while (PREV_INSN (range2beg)
+ && GET_CODE (PREV_INSN (range2beg)) == NOTE)
+ range2beg = PREV_INSN (range2beg);
+
+ /* Don't move NOTEs for blocks or loops; shift them
+ outside the ranges, where they'll stay put. */
+ range1beg = squeeze_notes (range1beg, range1end);
+ range2beg = squeeze_notes (range2beg, range2end);
+
+ /* Get current surrounds of the 2 ranges. */
+ range1before = PREV_INSN (range1beg);
+ range2before = PREV_INSN (range2beg);
+ range1after = NEXT_INSN (range1end);
+ range2after = NEXT_INSN (range2end);
+
+ /* Splice range2 where range1 was. */
+ NEXT_INSN (range1before) = range2beg;
+ PREV_INSN (range2beg) = range1before;
+ NEXT_INSN (range2end) = range1after;
+ PREV_INSN (range1after) = range2end;
+ /* Splice range1 where range2 was. */
+ NEXT_INSN (range2before) = range1beg;
+ PREV_INSN (range1beg) = range2before;
+ NEXT_INSN (range1end) = range2after;
+ PREV_INSN (range2after) = range1end;
+
+ /* Check for a loop end note between the end of
+ range2, and the next code label. If there is one,
+ then what we have really seen is
+ if (foo) break; end_of_loop;
+ and moved the break sequence outside the loop.
+ We must move the LOOP_END note to where the
+ loop really ends now, or we will confuse loop
+ optimization. */
+ for (;range2after != label2; range2after = rangenext)
+ {
+ rangenext = NEXT_INSN (range2after);
+ if (GET_CODE (range2after) == NOTE
+ && (NOTE_LINE_NUMBER (range2after)
+ == NOTE_INSN_LOOP_END))
+ {
+ NEXT_INSN (PREV_INSN (range2after))
+ = rangenext;
+ PREV_INSN (rangenext)
+ = PREV_INSN (range2after);
+ PREV_INSN (range2after)
+ = PREV_INSN (range1beg);
+ NEXT_INSN (range2after) = range1beg;
+ NEXT_INSN (PREV_INSN (range1beg))
+ = range2after;
+ PREV_INSN (range1beg) = range2after;
+ }
+ }
+ changed = 1;
+ continue;
+ }
+ }
+ }
+
+ /* Now that the jump has been tensioned,
+ try cross jumping: check for identical code
+ before the jump and before its target label. */
+
+ /* First, cross jumping of conditional jumps: */
+
+ if (cross_jump && condjump_p (insn))
+ {
+ rtx newjpos, newlpos;
+ rtx x = prev_real_insn (JUMP_LABEL (insn));
+
+ /* A conditional jump may be crossjumped
+ only if the place it jumps to follows
+ an opposing jump that comes back here. */
+
+ if (x != 0 && ! jump_back_p (x, insn))
+ /* We have no opposing jump;
+ cannot cross jump this insn. */
+ x = 0;
+
+ newjpos = 0;
+ /* TARGET is nonzero if it is ok to cross jump
+ to code before TARGET. If so, see if matches. */
+ if (x != 0)
+ find_cross_jump (insn, x, 2,
+ &newjpos, &newlpos);
+
+ if (newjpos != 0)
+ {
+ do_cross_jump (insn, newjpos, newlpos);
+ /* Make the old conditional jump
+ into an unconditional one. */
+ SET_SRC (PATTERN (insn))
+ = gen_rtx (LABEL_REF, VOIDmode, JUMP_LABEL (insn));
+ INSN_CODE (insn) = -1;
+ emit_barrier_after (insn);
+ /* Add to jump_chain unless this is a new label
+ whose UID is too large. */
+ if (INSN_UID (JUMP_LABEL (insn)) < max_jump_chain)
+ {
+ jump_chain[INSN_UID (insn)]
+ = jump_chain[INSN_UID (JUMP_LABEL (insn))];
+ jump_chain[INSN_UID (JUMP_LABEL (insn))] = insn;
+ }
+ changed = 1;
+ next = insn;
+ }
+ }
+
+ /* Cross jumping of unconditional jumps:
+ a few differences. */
+
+ if (cross_jump && simplejump_p (insn))
+ {
+ rtx newjpos, newlpos;
+ rtx target;
+
+ newjpos = 0;
+
+ /* TARGET is nonzero if it is ok to cross jump
+ to code before TARGET. If so, see if matches. */
+ find_cross_jump (insn, JUMP_LABEL (insn), 1,
+ &newjpos, &newlpos);
+
+ /* If cannot cross jump to code before the label,
+ see if we can cross jump to another jump to
+ the same label. */
+ /* Try each other jump to this label. */
+ if (INSN_UID (JUMP_LABEL (insn)) < max_uid)
+ for (target = jump_chain[INSN_UID (JUMP_LABEL (insn))];
+ target != 0 && newjpos == 0;
+ target = jump_chain[INSN_UID (target)])
+ if (target != insn
+ && JUMP_LABEL (target) == JUMP_LABEL (insn)
+ /* Ignore TARGET if it's deleted. */
+ && ! INSN_DELETED_P (target))
+ find_cross_jump (insn, target, 2,
+ &newjpos, &newlpos);
+
+ if (newjpos != 0)
+ {
+ do_cross_jump (insn, newjpos, newlpos);
+ changed = 1;
+ next = insn;
+ }
+ }
+
+ /* This code was dead in the previous jump.c! */
+ if (cross_jump && GET_CODE (PATTERN (insn)) == RETURN)
+ {
+ /* Return insns all "jump to the same place"
+ so we can cross-jump between any two of them. */
+
+ rtx newjpos, newlpos, target;
+
+ newjpos = 0;
+
+ /* If cannot cross jump to code before the label,
+ see if we can cross jump to another jump to
+ the same label. */
+ /* Try each other jump to this label. */
+ for (target = jump_chain[0];
+ target != 0 && newjpos == 0;
+ target = jump_chain[INSN_UID (target)])
+ if (target != insn
+ && ! INSN_DELETED_P (target)
+ && GET_CODE (PATTERN (target)) == RETURN)
+ find_cross_jump (insn, target, 2,
+ &newjpos, &newlpos);
+
+ if (newjpos != 0)
+ {
+ do_cross_jump (insn, newjpos, newlpos);
+ changed = 1;
+ next = insn;
+ }
+ }
+ }
+ }
+
+ first = 0;
+ }
+
+ /* Delete extraneous line number notes.
+ Note that two consecutive notes for different lines are not really
+ extraneous. There should be some indication where that line belonged,
+ even if it became empty. */
+
+ {
+ rtx last_note = 0;
+
+ for (insn = f; insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) >= 0)
+ {
+ /* Delete this note if it is identical to previous note. */
+ if (last_note
+ && NOTE_SOURCE_FILE (insn) == NOTE_SOURCE_FILE (last_note)
+ && NOTE_LINE_NUMBER (insn) == NOTE_LINE_NUMBER (last_note))
+ {
+ delete_insn (insn);
+ continue;
+ }
+
+ last_note = insn;
+ }
+ }
+
+#ifdef HAVE_return
+ if (HAVE_return)
+ {
+ /* If we fall through to the epilogue, see if we can insert a RETURN insn
+ in front of it. If the machine allows it at this point (we might be
+ after reload for a leaf routine), it will improve optimization for it
+ to be there. We do this both here and at the start of this pass since
+ the RETURN might have been deleted by some of our optimizations. */
+ insn = get_last_insn ();
+ while (insn && GET_CODE (insn) == NOTE)
+ insn = PREV_INSN (insn);
+
+ if (insn && GET_CODE (insn) != BARRIER)
+ {
+ emit_jump_insn (gen_return ());
+ emit_barrier ();
+ }
+ }
+#endif
+
+ /* See if there is still a NOTE_INSN_FUNCTION_END in this function.
+ If so, delete it, and record that this function can drop off the end. */
+
+ insn = last_insn;
+ {
+ int n_labels = 1;
+ while (insn
+ /* One label can follow the end-note: the return label. */
+ && ((GET_CODE (insn) == CODE_LABEL && n_labels-- > 0)
+ /* Ordinary insns can follow it if returning a structure. */
+ || GET_CODE (insn) == INSN
+ /* If machine uses explicit RETURN insns, no epilogue,
+ then one of them follows the note. */
+ || (GET_CODE (insn) == JUMP_INSN
+ && GET_CODE (PATTERN (insn)) == RETURN)
+ /* Other kinds of notes can follow also. */
+ || (GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END)))
+ insn = PREV_INSN (insn);
+ }
+
+ /* Report if control can fall through at the end of the function. */
+ if (insn && GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_END)
+ {
+ can_reach_end = 1;
+ delete_insn (insn);
+ }
+
+ /* Show JUMP_CHAIN no longer valid. */
+ jump_chain = 0;
+}
+
+/* LOOP_START is a NOTE_INSN_LOOP_BEG note that is followed by an unconditional
+ jump. Assume that this unconditional jump is to the exit test code. If
+ the code is sufficiently simple, make a copy of it before INSN,
+ followed by a jump to the exit of the loop. Then delete the unconditional
+ jump after INSN.
+
+ Note that it is possible we can get confused here if the jump immediately
+ after the loop start branches outside the loop but within an outer loop.
+ If we are near the exit of that loop, we will copy its exit test. This
+ will not generate incorrect code, but could suppress some optimizations.
+ However, such cases are degenerate loops anyway.
+
+ Return 1 if we made the change, else 0.
+
+ This is only safe immediately after a regscan pass because it uses the
+ values of regno_first_uid and regno_last_uid. */
+
+static int
+duplicate_loop_exit_test (loop_start)
+ rtx loop_start;
+{
+ rtx insn, set, reg, p, link;
+ rtx copy = 0;
+ int num_insns = 0;
+ rtx exitcode = NEXT_INSN (JUMP_LABEL (next_nonnote_insn (loop_start)));
+ rtx lastexit;
+ int max_reg = max_reg_num ();
+ rtx *reg_map = 0;
+
+ /* Scan the exit code. We do not perform this optimization if any insn:
+
+ is a CALL_INSN
+ is a CODE_LABEL
+ has a REG_RETVAL or REG_LIBCALL note (hard to adjust)
+ is a NOTE_INSN_LOOP_BEG because this means we have a nested loop
+ is a NOTE_INSN_BLOCK_{BEG,END} because duplicating these notes
+ are not valid
+
+ Also, don't do this if the exit code is more than 20 insns. */
+
+ for (insn = exitcode;
+ insn
+ && ! (GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END);
+ insn = NEXT_INSN (insn))
+ {
+ switch (GET_CODE (insn))
+ {
+ case CODE_LABEL:
+ case CALL_INSN:
+ return 0;
+ case NOTE:
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG
+ || NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG
+ || NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END)
+ return 0;
+ break;
+ case JUMP_INSN:
+ case INSN:
+ if (++num_insns > 20
+ || find_reg_note (insn, REG_RETVAL, NULL_RTX)
+ || find_reg_note (insn, REG_LIBCALL, NULL_RTX))
+ return 0;
+ break;
+ }
+ }
+
+ /* Unless INSN is zero, we can do the optimization. */
+ if (insn == 0)
+ return 0;
+
+ lastexit = insn;
+
+ /* See if any insn sets a register only used in the loop exit code and
+ not a user variable. If so, replace it with a new register. */
+ for (insn = exitcode; insn != lastexit; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == INSN
+ && (set = single_set (insn)) != 0
+ && ((reg = SET_DEST (set), GET_CODE (reg) == REG)
+ || (GET_CODE (reg) == SUBREG
+ && (reg = SUBREG_REG (reg), GET_CODE (reg) == REG)))
+ && REGNO (reg) >= FIRST_PSEUDO_REGISTER
+ && regno_first_uid[REGNO (reg)] == INSN_UID (insn))
+ {
+ for (p = NEXT_INSN (insn); p != lastexit; p = NEXT_INSN (p))
+ if (regno_last_uid[REGNO (reg)] == INSN_UID (p))
+ break;
+
+ if (p != lastexit)
+ {
+ /* We can do the replacement. Allocate reg_map if this is the
+ first replacement we found. */
+ if (reg_map == 0)
+ {
+ reg_map = (rtx *) alloca (max_reg * sizeof (rtx));
+ bzero ((char *) reg_map, max_reg * sizeof (rtx));
+ }
+
+ REG_LOOP_TEST_P (reg) = 1;
+
+ reg_map[REGNO (reg)] = gen_reg_rtx (GET_MODE (reg));
+ }
+ }
+
+ /* Now copy each insn. */
+ for (insn = exitcode; insn != lastexit; insn = NEXT_INSN (insn))
+ switch (GET_CODE (insn))
+ {
+ case BARRIER:
+ copy = emit_barrier_before (loop_start);
+ break;
+ case NOTE:
+ /* Only copy line-number notes. */
+ if (NOTE_LINE_NUMBER (insn) >= 0)
+ {
+ copy = emit_note_before (NOTE_LINE_NUMBER (insn), loop_start);
+ NOTE_SOURCE_FILE (copy) = NOTE_SOURCE_FILE (insn);
+ }
+ break;
+
+ case INSN:
+ copy = emit_insn_before (copy_rtx (PATTERN (insn)), loop_start);
+ if (reg_map)
+ replace_regs (PATTERN (copy), reg_map, max_reg, 1);
+
+ mark_jump_label (PATTERN (copy), copy, 0);
+
+ /* Copy all REG_NOTES except REG_LABEL since mark_jump_label will
+ make them. */
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) != REG_LABEL)
+ REG_NOTES (copy)
+ = copy_rtx (gen_rtx (EXPR_LIST, REG_NOTE_KIND (link),
+ XEXP (link, 0), REG_NOTES (copy)));
+ if (reg_map && REG_NOTES (copy))
+ replace_regs (REG_NOTES (copy), reg_map, max_reg, 1);
+ break;
+
+ case JUMP_INSN:
+ copy = emit_jump_insn_before (copy_rtx (PATTERN (insn)), loop_start);
+ if (reg_map)
+ replace_regs (PATTERN (copy), reg_map, max_reg, 1);
+ mark_jump_label (PATTERN (copy), copy, 0);
+ if (REG_NOTES (insn))
+ {
+ REG_NOTES (copy) = copy_rtx (REG_NOTES (insn));
+ if (reg_map)
+ replace_regs (REG_NOTES (copy), reg_map, max_reg, 1);
+ }
+
+ /* If this is a simple jump, add it to the jump chain. */
+
+ if (INSN_UID (copy) < max_jump_chain && JUMP_LABEL (copy)
+ && simplejump_p (copy))
+ {
+ jump_chain[INSN_UID (copy)]
+ = jump_chain[INSN_UID (JUMP_LABEL (copy))];
+ jump_chain[INSN_UID (JUMP_LABEL (copy))] = copy;
+ }
+ break;
+
+ default:
+ abort ();
+ }
+
+ /* Now clean up by emitting a jump to the end label and deleting the jump
+ at the start of the loop. */
+ if (! copy || GET_CODE (copy) != BARRIER)
+ {
+ copy = emit_jump_insn_before (gen_jump (get_label_after (insn)),
+ loop_start);
+ mark_jump_label (PATTERN (copy), copy, 0);
+ if (INSN_UID (copy) < max_jump_chain
+ && INSN_UID (JUMP_LABEL (copy)) < max_jump_chain)
+ {
+ jump_chain[INSN_UID (copy)]
+ = jump_chain[INSN_UID (JUMP_LABEL (copy))];
+ jump_chain[INSN_UID (JUMP_LABEL (copy))] = copy;
+ }
+ emit_barrier_before (loop_start);
+ }
+
+ delete_insn (next_nonnote_insn (loop_start));
+
+ /* Mark the exit code as the virtual top of the converted loop. */
+ emit_note_before (NOTE_INSN_LOOP_VTOP, exitcode);
+
+ return 1;
+}
+
+/* Move all block-beg, block-end, loop-beg, loop-cont, loop-vtop, and
+ loop-end notes between START and END out before START. Assume that
+ END is not such a note. START may be such a note. Returns the value
+ of the new starting insn, which may be different if the original start
+ was such a note. */
+
+rtx
+squeeze_notes (start, end)
+ rtx start, end;
+{
+ rtx insn;
+ rtx next;
+
+ for (insn = start; insn != end; insn = next)
+ {
+ next = NEXT_INSN (insn);
+ if (GET_CODE (insn) == NOTE
+ && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END
+ || NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG
+ || NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG
+ || NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END
+ || NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_CONT
+ || NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_VTOP))
+ {
+ if (insn == start)
+ start = next;
+ else
+ {
+ rtx prev = PREV_INSN (insn);
+ PREV_INSN (insn) = PREV_INSN (start);
+ NEXT_INSN (insn) = start;
+ NEXT_INSN (PREV_INSN (insn)) = insn;
+ PREV_INSN (NEXT_INSN (insn)) = insn;
+ NEXT_INSN (prev) = next;
+ PREV_INSN (next) = prev;
+ }
+ }
+ }
+
+ return start;
+}
+
+/* Compare the instructions before insn E1 with those before E2
+ to find an opportunity for cross jumping.
+ (This means detecting identical sequences of insns followed by
+ jumps to the same place, or followed by a label and a jump
+ to that label, and replacing one with a jump to the other.)
+
+ Assume E1 is a jump that jumps to label E2
+ (that is not always true but it might as well be).
+ Find the longest possible equivalent sequences
+ and store the first insns of those sequences into *F1 and *F2.
+ Store zero there if no equivalent preceding instructions are found.
+
+ We give up if we find a label in stream 1.
+ Actually we could transfer that label into stream 2. */
+
+static void
+find_cross_jump (e1, e2, minimum, f1, f2)
+ rtx e1, e2;
+ int minimum;
+ rtx *f1, *f2;
+{
+ register rtx i1 = e1, i2 = e2;
+ register rtx p1, p2;
+ int lose = 0;
+
+ rtx last1 = 0, last2 = 0;
+ rtx afterlast1 = 0, afterlast2 = 0;
+ rtx prev1;
+
+ *f1 = 0;
+ *f2 = 0;
+
+ while (1)
+ {
+ i1 = prev_nonnote_insn (i1);
+
+ i2 = PREV_INSN (i2);
+ while (i2 && (GET_CODE (i2) == NOTE || GET_CODE (i2) == CODE_LABEL))
+ i2 = PREV_INSN (i2);
+
+ if (i1 == 0)
+ break;
+
+ /* Don't allow the range of insns preceding E1 or E2
+ to include the other (E2 or E1). */
+ if (i2 == e1 || i1 == e2)
+ break;
+
+ /* If we will get to this code by jumping, those jumps will be
+ tensioned to go directly to the new label (before I2),
+ so this cross-jumping won't cost extra. So reduce the minimum. */
+ if (GET_CODE (i1) == CODE_LABEL)
+ {
+ --minimum;
+ break;
+ }
+
+ if (i2 == 0 || GET_CODE (i1) != GET_CODE (i2))
+ break;
+
+ p1 = PATTERN (i1);
+ p2 = PATTERN (i2);
+
+ /* If this is a CALL_INSN, compare register usage information.
+ If we don't check this on stack register machines, the two
+ CALL_INSNs might be merged leaving reg-stack.c with mismatching
+ numbers of stack registers in the same basic block.
+ If we don't check this on machines with delay slots, a delay slot may
+ be filled that clobbers a parameter expected by the subroutine.
+
+ ??? We take the simple route for now and assume that if they're
+ equal, they were constructed identically. */
+
+ if (GET_CODE (i1) == CALL_INSN
+ && ! rtx_equal_p (CALL_INSN_FUNCTION_USAGE (i1),
+ CALL_INSN_FUNCTION_USAGE (i2)))
+ lose = 1;
+
+#ifdef STACK_REGS
+ /* If cross_jump_death_matters is not 0, the insn's mode
+ indicates whether or not the insn contains any stack-like
+ regs. */
+
+ if (!lose && cross_jump_death_matters && GET_MODE (i1) == QImode)
+ {
+ /* If register stack conversion has already been done, then
+ death notes must also be compared before it is certain that
+ the two instruction streams match. */
+
+ rtx note;
+ HARD_REG_SET i1_regset, i2_regset;
+
+ CLEAR_HARD_REG_SET (i1_regset);
+ CLEAR_HARD_REG_SET (i2_regset);
+
+ for (note = REG_NOTES (i1); note; note = XEXP (note, 1))
+ if (REG_NOTE_KIND (note) == REG_DEAD
+ && STACK_REG_P (XEXP (note, 0)))
+ SET_HARD_REG_BIT (i1_regset, REGNO (XEXP (note, 0)));
+
+ for (note = REG_NOTES (i2); note; note = XEXP (note, 1))
+ if (REG_NOTE_KIND (note) == REG_DEAD
+ && STACK_REG_P (XEXP (note, 0)))
+ SET_HARD_REG_BIT (i2_regset, REGNO (XEXP (note, 0)));
+
+ GO_IF_HARD_REG_EQUAL (i1_regset, i2_regset, done);
+
+ lose = 1;
+
+ done:
+ ;
+ }
+#endif
+
+ if (lose || GET_CODE (p1) != GET_CODE (p2)
+ || ! rtx_renumbered_equal_p (p1, p2))
+ {
+ /* The following code helps take care of G++ cleanups. */
+ rtx equiv1;
+ rtx equiv2;
+
+ if (!lose && GET_CODE (p1) == GET_CODE (p2)
+ && ((equiv1 = find_reg_note (i1, REG_EQUAL, NULL_RTX)) != 0
+ || (equiv1 = find_reg_note (i1, REG_EQUIV, NULL_RTX)) != 0)
+ && ((equiv2 = find_reg_note (i2, REG_EQUAL, NULL_RTX)) != 0
+ || (equiv2 = find_reg_note (i2, REG_EQUIV, NULL_RTX)) != 0)
+ /* If the equivalences are not to a constant, they may
+ reference pseudos that no longer exist, so we can't
+ use them. */
+ && CONSTANT_P (XEXP (equiv1, 0))
+ && rtx_equal_p (XEXP (equiv1, 0), XEXP (equiv2, 0)))
+ {
+ rtx s1 = single_set (i1);
+ rtx s2 = single_set (i2);
+ if (s1 != 0 && s2 != 0
+ && rtx_renumbered_equal_p (SET_DEST (s1), SET_DEST (s2)))
+ {
+ validate_change (i1, &SET_SRC (s1), XEXP (equiv1, 0), 1);
+ validate_change (i2, &SET_SRC (s2), XEXP (equiv2, 0), 1);
+ if (! rtx_renumbered_equal_p (p1, p2))
+ cancel_changes (0);
+ else if (apply_change_group ())
+ goto win;
+ }
+ }
+
+ /* Insns fail to match; cross jumping is limited to the following
+ insns. */
+
+#ifdef HAVE_cc0
+ /* Don't allow the insn after a compare to be shared by
+ cross-jumping unless the compare is also shared.
+ Here, if either of these non-matching insns is a compare,
+ exclude the following insn from possible cross-jumping. */
+ if (sets_cc0_p (p1) || sets_cc0_p (p2))
+ last1 = afterlast1, last2 = afterlast2, ++minimum;
+#endif
+
+ /* If cross-jumping here will feed a jump-around-jump
+ optimization, this jump won't cost extra, so reduce
+ the minimum. */
+ if (GET_CODE (i1) == JUMP_INSN
+ && JUMP_LABEL (i1)
+ && prev_real_insn (JUMP_LABEL (i1)) == e1)
+ --minimum;
+ break;
+ }
+
+ win:
+ if (GET_CODE (p1) != USE && GET_CODE (p1) != CLOBBER)
+ {
+ /* Ok, this insn is potentially includable in a cross-jump here. */
+ afterlast1 = last1, afterlast2 = last2;
+ last1 = i1, last2 = i2, --minimum;
+ }
+ }
+
+ if (minimum <= 0 && last1 != 0 && last1 != e1)
+ *f1 = last1, *f2 = last2;
+}
+
+static void
+do_cross_jump (insn, newjpos, newlpos)
+ rtx insn, newjpos, newlpos;
+{
+ /* Find an existing label at this point
+ or make a new one if there is none. */
+ register rtx label = get_label_before (newlpos);
+
+ /* Make the same jump insn jump to the new point. */
+ if (GET_CODE (PATTERN (insn)) == RETURN)
+ {
+ /* Remove from jump chain of returns. */
+ delete_from_jump_chain (insn);
+ /* Change the insn. */
+ PATTERN (insn) = gen_jump (label);
+ INSN_CODE (insn) = -1;
+ JUMP_LABEL (insn) = label;
+ LABEL_NUSES (label)++;
+ /* Add to new the jump chain. */
+ if (INSN_UID (label) < max_jump_chain
+ && INSN_UID (insn) < max_jump_chain)
+ {
+ jump_chain[INSN_UID (insn)] = jump_chain[INSN_UID (label)];
+ jump_chain[INSN_UID (label)] = insn;
+ }
+ }
+ else
+ redirect_jump (insn, label);
+
+ /* Delete the matching insns before the jump. Also, remove any REG_EQUAL
+ or REG_EQUIV note in the NEWLPOS stream that isn't also present in
+ the NEWJPOS stream. */
+
+ while (newjpos != insn)
+ {
+ rtx lnote;
+
+ for (lnote = REG_NOTES (newlpos); lnote; lnote = XEXP (lnote, 1))
+ if ((REG_NOTE_KIND (lnote) == REG_EQUAL
+ || REG_NOTE_KIND (lnote) == REG_EQUIV)
+ && ! find_reg_note (newjpos, REG_EQUAL, XEXP (lnote, 0))
+ && ! find_reg_note (newjpos, REG_EQUIV, XEXP (lnote, 0)))
+ remove_note (newlpos, lnote);
+
+ delete_insn (newjpos);
+ newjpos = next_real_insn (newjpos);
+ newlpos = next_real_insn (newlpos);
+ }
+}
+
+/* Return the label before INSN, or put a new label there. */
+
+rtx
+get_label_before (insn)
+ rtx insn;
+{
+ rtx label;
+
+ /* Find an existing label at this point
+ or make a new one if there is none. */
+ label = prev_nonnote_insn (insn);
+
+ if (label == 0 || GET_CODE (label) != CODE_LABEL)
+ {
+ rtx prev = PREV_INSN (insn);
+
+ label = gen_label_rtx ();
+ emit_label_after (label, prev);
+ LABEL_NUSES (label) = 0;
+ }
+ return label;
+}
+
+/* Return the label after INSN, or put a new label there. */
+
+rtx
+get_label_after (insn)
+ rtx insn;
+{
+ rtx label;
+
+ /* Find an existing label at this point
+ or make a new one if there is none. */
+ label = next_nonnote_insn (insn);
+
+ if (label == 0 || GET_CODE (label) != CODE_LABEL)
+ {
+ label = gen_label_rtx ();
+ emit_label_after (label, insn);
+ LABEL_NUSES (label) = 0;
+ }
+ return label;
+}
+
+/* Return 1 if INSN is a jump that jumps to right after TARGET
+ only on the condition that TARGET itself would drop through.
+ Assumes that TARGET is a conditional jump. */
+
+static int
+jump_back_p (insn, target)
+ rtx insn, target;
+{
+ rtx cinsn, ctarget;
+ enum rtx_code codei, codet;
+
+ if (simplejump_p (insn) || ! condjump_p (insn)
+ || simplejump_p (target)
+ || target != prev_real_insn (JUMP_LABEL (insn)))
+ return 0;
+
+ cinsn = XEXP (SET_SRC (PATTERN (insn)), 0);
+ ctarget = XEXP (SET_SRC (PATTERN (target)), 0);
+
+ codei = GET_CODE (cinsn);
+ codet = GET_CODE (ctarget);
+
+ if (XEXP (SET_SRC (PATTERN (insn)), 1) == pc_rtx)
+ {
+ if (! can_reverse_comparison_p (cinsn, insn))
+ return 0;
+ codei = reverse_condition (codei);
+ }
+
+ if (XEXP (SET_SRC (PATTERN (target)), 2) == pc_rtx)
+ {
+ if (! can_reverse_comparison_p (ctarget, target))
+ return 0;
+ codet = reverse_condition (codet);
+ }
+
+ return (codei == codet
+ && rtx_renumbered_equal_p (XEXP (cinsn, 0), XEXP (ctarget, 0))
+ && rtx_renumbered_equal_p (XEXP (cinsn, 1), XEXP (ctarget, 1)));
+}
+
+/* Given a comparison, COMPARISON, inside a conditional jump insn, INSN,
+ return non-zero if it is safe to reverse this comparison. It is if our
+ floating-point is not IEEE, if this is an NE or EQ comparison, or if
+ this is known to be an integer comparison. */
+
+int
+can_reverse_comparison_p (comparison, insn)
+ rtx comparison;
+ rtx insn;
+{
+ rtx arg0;
+
+ /* If this is not actually a comparison, we can't reverse it. */
+ if (GET_RTX_CLASS (GET_CODE (comparison)) != '<')
+ return 0;
+
+ if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+ /* If this is an NE comparison, it is safe to reverse it to an EQ
+ comparison and vice versa, even for floating point. If no operands
+ are NaNs, the reversal is valid. If some operand is a NaN, EQ is
+ always false and NE is always true, so the reversal is also valid. */
+ || flag_fast_math
+ || GET_CODE (comparison) == NE
+ || GET_CODE (comparison) == EQ)
+ return 1;
+
+ arg0 = XEXP (comparison, 0);
+
+ /* Make sure ARG0 is one of the actual objects being compared. If we
+ can't do this, we can't be sure the comparison can be reversed.
+
+ Handle cc0 and a MODE_CC register. */
+ if ((GET_CODE (arg0) == REG && GET_MODE_CLASS (GET_MODE (arg0)) == MODE_CC)
+#ifdef HAVE_cc0
+ || arg0 == cc0_rtx
+#endif
+ )
+ {
+ rtx prev = prev_nonnote_insn (insn);
+ rtx set = single_set (prev);
+
+ if (set == 0 || SET_DEST (set) != arg0)
+ return 0;
+
+ arg0 = SET_SRC (set);
+
+ if (GET_CODE (arg0) == COMPARE)
+ arg0 = XEXP (arg0, 0);
+ }
+
+ /* We can reverse this if ARG0 is a CONST_INT or if its mode is
+ not VOIDmode and neither a MODE_CC nor MODE_FLOAT type. */
+ return (GET_CODE (arg0) == CONST_INT
+ || (GET_MODE (arg0) != VOIDmode
+ && GET_MODE_CLASS (GET_MODE (arg0)) != MODE_CC
+ && GET_MODE_CLASS (GET_MODE (arg0)) != MODE_FLOAT));
+}
+
+/* Given an rtx-code for a comparison, return the code
+ for the negated comparison.
+ WATCH OUT! reverse_condition is not safe to use on a jump
+ that might be acting on the results of an IEEE floating point comparison,
+ because of the special treatment of non-signaling nans in comparisons.
+ Use can_reverse_comparison_p to be sure. */
+
+enum rtx_code
+reverse_condition (code)
+ enum rtx_code code;
+{
+ switch (code)
+ {
+ case EQ:
+ return NE;
+
+ case NE:
+ return EQ;
+
+ case GT:
+ return LE;
+
+ case GE:
+ return LT;
+
+ case LT:
+ return GE;
+
+ case LE:
+ return GT;
+
+ case GTU:
+ return LEU;
+
+ case GEU:
+ return LTU;
+
+ case LTU:
+ return GEU;
+
+ case LEU:
+ return GTU;
+
+ default:
+ abort ();
+ return UNKNOWN;
+ }
+}
+
+/* Similar, but return the code when two operands of a comparison are swapped.
+ This IS safe for IEEE floating-point. */
+
+enum rtx_code
+swap_condition (code)
+ enum rtx_code code;
+{
+ switch (code)
+ {
+ case EQ:
+ case NE:
+ return code;
+
+ case GT:
+ return LT;
+
+ case GE:
+ return LE;
+
+ case LT:
+ return GT;
+
+ case LE:
+ return GE;
+
+ case GTU:
+ return LTU;
+
+ case GEU:
+ return LEU;
+
+ case LTU:
+ return GTU;
+
+ case LEU:
+ return GEU;
+
+ default:
+ abort ();
+ return UNKNOWN;
+ }
+}
+
+/* Given a comparison CODE, return the corresponding unsigned comparison.
+ If CODE is an equality comparison or already an unsigned comparison,
+ CODE is returned. */
+
+enum rtx_code
+unsigned_condition (code)
+ enum rtx_code code;
+{
+ switch (code)
+ {
+ case EQ:
+ case NE:
+ case GTU:
+ case GEU:
+ case LTU:
+ case LEU:
+ return code;
+
+ case GT:
+ return GTU;
+
+ case GE:
+ return GEU;
+
+ case LT:
+ return LTU;
+
+ case LE:
+ return LEU;
+
+ default:
+ abort ();
+ }
+}
+
+/* Similarly, return the signed version of a comparison. */
+
+enum rtx_code
+signed_condition (code)
+ enum rtx_code code;
+{
+ switch (code)
+ {
+ case EQ:
+ case NE:
+ case GT:
+ case GE:
+ case LT:
+ case LE:
+ return code;
+
+ case GTU:
+ return GT;
+
+ case GEU:
+ return GE;
+
+ case LTU:
+ return LT;
+
+ case LEU:
+ return LE;
+
+ default:
+ abort ();
+ }
+}
+
+/* Return non-zero if CODE1 is more strict than CODE2, i.e., if the
+ truth of CODE1 implies the truth of CODE2. */
+
+int
+comparison_dominates_p (code1, code2)
+ enum rtx_code code1, code2;
+{
+ if (code1 == code2)
+ return 1;
+
+ switch (code1)
+ {
+ case EQ:
+ if (code2 == LE || code2 == LEU || code2 == GE || code2 == GEU)
+ return 1;
+ break;
+
+ case LT:
+ if (code2 == LE || code2 == NE)
+ return 1;
+ break;
+
+ case GT:
+ if (code2 == GE || code2 == NE)
+ return 1;
+ break;
+
+ case LTU:
+ if (code2 == LEU || code2 == NE)
+ return 1;
+ break;
+
+ case GTU:
+ if (code2 == GEU || code2 == NE)
+ return 1;
+ break;
+ }
+
+ return 0;
+}
+
+/* Return 1 if INSN is an unconditional jump and nothing else. */
+
+int
+simplejump_p (insn)
+ rtx insn;
+{
+ return (GET_CODE (insn) == JUMP_INSN
+ && GET_CODE (PATTERN (insn)) == SET
+ && GET_CODE (SET_DEST (PATTERN (insn))) == PC
+ && GET_CODE (SET_SRC (PATTERN (insn))) == LABEL_REF);
+}
+
+/* Return nonzero if INSN is a (possibly) conditional jump
+ and nothing more. */
+
+int
+condjump_p (insn)
+ rtx insn;
+{
+ register rtx x = PATTERN (insn);
+ if (GET_CODE (x) != SET)
+ return 0;
+ if (GET_CODE (SET_DEST (x)) != PC)
+ return 0;
+ if (GET_CODE (SET_SRC (x)) == LABEL_REF)
+ return 1;
+ if (GET_CODE (SET_SRC (x)) != IF_THEN_ELSE)
+ return 0;
+ if (XEXP (SET_SRC (x), 2) == pc_rtx
+ && (GET_CODE (XEXP (SET_SRC (x), 1)) == LABEL_REF
+ || GET_CODE (XEXP (SET_SRC (x), 1)) == RETURN))
+ return 1;
+ if (XEXP (SET_SRC (x), 1) == pc_rtx
+ && (GET_CODE (XEXP (SET_SRC (x), 2)) == LABEL_REF
+ || GET_CODE (XEXP (SET_SRC (x), 2)) == RETURN))
+ return 1;
+ return 0;
+}
+
+/* Return nonzero if INSN is a (possibly) conditional jump
+ and nothing more. */
+
+int
+condjump_in_parallel_p (insn)
+ rtx insn;
+{
+ register rtx x = PATTERN (insn);
+
+ if (GET_CODE (x) != PARALLEL)
+ return 0;
+ else
+ x = XVECEXP (x, 0, 0);
+
+ if (GET_CODE (x) != SET)
+ return 0;
+ if (GET_CODE (SET_DEST (x)) != PC)
+ return 0;
+ if (GET_CODE (SET_SRC (x)) == LABEL_REF)
+ return 1;
+ if (GET_CODE (SET_SRC (x)) != IF_THEN_ELSE)
+ return 0;
+ if (XEXP (SET_SRC (x), 2) == pc_rtx
+ && (GET_CODE (XEXP (SET_SRC (x), 1)) == LABEL_REF
+ || GET_CODE (XEXP (SET_SRC (x), 1)) == RETURN))
+ return 1;
+ if (XEXP (SET_SRC (x), 1) == pc_rtx
+ && (GET_CODE (XEXP (SET_SRC (x), 2)) == LABEL_REF
+ || GET_CODE (XEXP (SET_SRC (x), 2)) == RETURN))
+ return 1;
+ return 0;
+}
+
+/* Return 1 if X is an RTX that does nothing but set the condition codes
+ and CLOBBER or USE registers.
+ Return -1 if X does explicitly set the condition codes,
+ but also does other things. */
+
+int
+sets_cc0_p (x)
+ rtx x;
+{
+#ifdef HAVE_cc0
+ if (GET_CODE (x) == SET && SET_DEST (x) == cc0_rtx)
+ return 1;
+ if (GET_CODE (x) == PARALLEL)
+ {
+ int i;
+ int sets_cc0 = 0;
+ int other_things = 0;
+ for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
+ {
+ if (GET_CODE (XVECEXP (x, 0, i)) == SET
+ && SET_DEST (XVECEXP (x, 0, i)) == cc0_rtx)
+ sets_cc0 = 1;
+ else if (GET_CODE (XVECEXP (x, 0, i)) == SET)
+ other_things = 1;
+ }
+ return ! sets_cc0 ? 0 : other_things ? -1 : 1;
+ }
+ return 0;
+#else
+ abort ();
+#endif
+}
+
+/* Follow any unconditional jump at LABEL;
+ return the ultimate label reached by any such chain of jumps.
+ If LABEL is not followed by a jump, return LABEL.
+ If the chain loops or we can't find end, return LABEL,
+ since that tells caller to avoid changing the insn.
+
+ If RELOAD_COMPLETED is 0, we do not chain across a NOTE_INSN_LOOP_BEG or
+ a USE or CLOBBER. */
+
+rtx
+follow_jumps (label)
+ rtx label;
+{
+ register rtx insn;
+ register rtx next;
+ register rtx value = label;
+ register int depth;
+
+ for (depth = 0;
+ (depth < 10
+ && (insn = next_active_insn (value)) != 0
+ && GET_CODE (insn) == JUMP_INSN
+ && (JUMP_LABEL (insn) != 0 || GET_CODE (PATTERN (insn)) == RETURN)
+ && (next = NEXT_INSN (insn))
+ && GET_CODE (next) == BARRIER);
+ depth++)
+ {
+ /* Don't chain through the insn that jumps into a loop
+ from outside the loop,
+ since that would create multiple loop entry jumps
+ and prevent loop optimization. */
+ rtx tem;
+ if (!reload_completed)
+ for (tem = value; tem != insn; tem = NEXT_INSN (tem))
+ if (GET_CODE (tem) == NOTE
+ && NOTE_LINE_NUMBER (tem) == NOTE_INSN_LOOP_BEG)
+ return value;
+
+ /* If we have found a cycle, make the insn jump to itself. */
+ if (JUMP_LABEL (insn) == label)
+ return label;
+
+ tem = next_active_insn (JUMP_LABEL (insn));
+ if (tem && (GET_CODE (PATTERN (tem)) == ADDR_VEC
+ || GET_CODE (PATTERN (tem)) == ADDR_DIFF_VEC))
+ break;
+
+ value = JUMP_LABEL (insn);
+ }
+ if (depth == 10)
+ return label;
+ return value;
+}
+
+/* Assuming that field IDX of X is a vector of label_refs,
+ replace each of them by the ultimate label reached by it.
+ Return nonzero if a change is made.
+ If IGNORE_LOOPS is 0, we do not chain across a NOTE_INSN_LOOP_BEG. */
+
+static int
+tension_vector_labels (x, idx)
+ register rtx x;
+ register int idx;
+{
+ int changed = 0;
+ register int i;
+ for (i = XVECLEN (x, idx) - 1; i >= 0; i--)
+ {
+ register rtx olabel = XEXP (XVECEXP (x, idx, i), 0);
+ register rtx nlabel = follow_jumps (olabel);
+ if (nlabel && nlabel != olabel)
+ {
+ XEXP (XVECEXP (x, idx, i), 0) = nlabel;
+ ++LABEL_NUSES (nlabel);
+ if (--LABEL_NUSES (olabel) == 0)
+ delete_insn (olabel);
+ changed = 1;
+ }
+ }
+ return changed;
+}
+
+/* Find all CODE_LABELs referred to in X, and increment their use counts.
+ If INSN is a JUMP_INSN and there is at least one CODE_LABEL referenced
+ in INSN, then store one of them in JUMP_LABEL (INSN).
+ If INSN is an INSN or a CALL_INSN and there is at least one CODE_LABEL
+ referenced in INSN, add a REG_LABEL note containing that label to INSN.
+ Also, when there are consecutive labels, canonicalize on the last of them.
+
+ Note that two labels separated by a loop-beginning note
+ must be kept distinct if we have not yet done loop-optimization,
+ because the gap between them is where loop-optimize
+ will want to move invariant code to. CROSS_JUMP tells us
+ that loop-optimization is done with.
+
+ Once reload has completed (CROSS_JUMP non-zero), we need not consider
+ two labels distinct if they are separated by only USE or CLOBBER insns. */
+
+static void
+mark_jump_label (x, insn, cross_jump)
+ register rtx x;
+ rtx insn;
+ int cross_jump;
+{
+ register RTX_CODE code = GET_CODE (x);
+ register int i;
+ register char *fmt;
+
+ switch (code)
+ {
+ case PC:
+ case CC0:
+ case REG:
+ case SUBREG:
+ case CONST_INT:
+ case SYMBOL_REF:
+ case CONST_DOUBLE:
+ case CLOBBER:
+ case CALL:
+ return;
+
+ case MEM:
+ /* If this is a constant-pool reference, see if it is a label. */
+ if (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
+ && CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)))
+ mark_jump_label (get_pool_constant (XEXP (x, 0)), insn, cross_jump);
+ break;
+
+ case LABEL_REF:
+ {
+ rtx label = XEXP (x, 0);
+ rtx olabel = label;
+ rtx note;
+ rtx next;
+
+ if (GET_CODE (label) != CODE_LABEL)
+ abort ();
+
+ /* Ignore references to labels of containing functions. */
+ if (LABEL_REF_NONLOCAL_P (x))
+ break;
+
+ /* If there are other labels following this one,
+ replace it with the last of the consecutive labels. */
+ for (next = NEXT_INSN (label); next; next = NEXT_INSN (next))
+ {
+ if (GET_CODE (next) == CODE_LABEL)
+ label = next;
+ else if (cross_jump && GET_CODE (next) == INSN
+ && (GET_CODE (PATTERN (next)) == USE
+ || GET_CODE (PATTERN (next)) == CLOBBER))
+ continue;
+ else if (GET_CODE (next) != NOTE)
+ break;
+ else if (! cross_jump
+ && (NOTE_LINE_NUMBER (next) == NOTE_INSN_LOOP_BEG
+ || NOTE_LINE_NUMBER (next) == NOTE_INSN_FUNCTION_END))
+ break;
+ }
+
+ XEXP (x, 0) = label;
+ ++LABEL_NUSES (label);
+
+ if (insn)
+ {
+ if (GET_CODE (insn) == JUMP_INSN)
+ JUMP_LABEL (insn) = label;
+
+ /* If we've changed OLABEL and we had a REG_LABEL note
+ for it, update it as well. */
+ else if (label != olabel
+ && (note = find_reg_note (insn, REG_LABEL, olabel)) != 0)
+ XEXP (note, 0) = label;
+
+ /* Otherwise, add a REG_LABEL note for LABEL unless there already
+ is one. */
+ else if (! find_reg_note (insn, REG_LABEL, label))
+ {
+ rtx next = next_real_insn (label);
+ /* Don't record labels that refer to dispatch tables.
+ This is not necessary, since the tablejump
+ references the same label.
+ And if we did record them, flow.c would make worse code. */
+ if (next == 0
+ || ! (GET_CODE (next) == JUMP_INSN
+ && (GET_CODE (PATTERN (next)) == ADDR_VEC
+ || GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC)))
+ REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_LABEL, label,
+ REG_NOTES (insn));
+ }
+ }
+ return;
+ }
+
+ /* Do walk the labels in a vector, but not the first operand of an
+ ADDR_DIFF_VEC. Don't set the JUMP_LABEL of a vector. */
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ {
+ int eltnum = code == ADDR_DIFF_VEC ? 1 : 0;
+
+ for (i = 0; i < XVECLEN (x, eltnum); i++)
+ mark_jump_label (XVECEXP (x, eltnum, i), NULL_RTX, cross_jump);
+ return;
+ }
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ mark_jump_label (XEXP (x, i), insn, cross_jump);
+ else if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ mark_jump_label (XVECEXP (x, i, j), insn, cross_jump);
+ }
+ }
+}
+
+/* If all INSN does is set the pc, delete it,
+ and delete the insn that set the condition codes for it
+ if that's what the previous thing was. */
+
+void
+delete_jump (insn)
+ rtx insn;
+{
+ register rtx set = single_set (insn);
+
+ if (set && GET_CODE (SET_DEST (set)) == PC)
+ delete_computation (insn);
+}
+
+/* Delete INSN and recursively delete insns that compute values used only
+ by INSN. This uses the REG_DEAD notes computed during flow analysis.
+ If we are running before flow.c, we need do nothing since flow.c will
+ delete dead code. We also can't know if the registers being used are
+ dead or not at this point.
+
+ Otherwise, look at all our REG_DEAD notes. If a previous insn does
+ nothing other than set a register that dies in this insn, we can delete
+ that insn as well.
+
+ On machines with CC0, if CC0 is used in this insn, we may be able to
+ delete the insn that set it. */
+
+static void
+delete_computation (insn)
+ rtx insn;
+{
+ rtx note, next;
+
+#ifdef HAVE_cc0
+ if (reg_referenced_p (cc0_rtx, PATTERN (insn)))
+ {
+ rtx prev = prev_nonnote_insn (insn);
+ /* We assume that at this stage
+ CC's are always set explicitly
+ and always immediately before the jump that
+ will use them. So if the previous insn
+ exists to set the CC's, delete it
+ (unless it performs auto-increments, etc.). */
+ if (prev && GET_CODE (prev) == INSN
+ && sets_cc0_p (PATTERN (prev)))
+ {
+ if (sets_cc0_p (PATTERN (prev)) > 0
+ && !FIND_REG_INC_NOTE (prev, NULL_RTX))
+ delete_computation (prev);
+ else
+ /* Otherwise, show that cc0 won't be used. */
+ REG_NOTES (prev) = gen_rtx (EXPR_LIST, REG_UNUSED,
+ cc0_rtx, REG_NOTES (prev));
+ }
+ }
+#endif
+
+ for (note = REG_NOTES (insn); note; note = next)
+ {
+ rtx our_prev;
+
+ next = XEXP (note, 1);
+
+ if (REG_NOTE_KIND (note) != REG_DEAD
+ /* Verify that the REG_NOTE is legitimate. */
+ || GET_CODE (XEXP (note, 0)) != REG)
+ continue;
+
+ for (our_prev = prev_nonnote_insn (insn);
+ our_prev && GET_CODE (our_prev) == INSN;
+ our_prev = prev_nonnote_insn (our_prev))
+ {
+ /* If we reach a SEQUENCE, it is too complex to try to
+ do anything with it, so give up. */
+ if (GET_CODE (PATTERN (our_prev)) == SEQUENCE)
+ break;
+
+ if (GET_CODE (PATTERN (our_prev)) == USE
+ && GET_CODE (XEXP (PATTERN (our_prev), 0)) == INSN)
+ /* reorg creates USEs that look like this. We leave them
+ alone because reorg needs them for its own purposes. */
+ break;
+
+ if (reg_set_p (XEXP (note, 0), PATTERN (our_prev)))
+ {
+ if (FIND_REG_INC_NOTE (our_prev, NULL_RTX))
+ break;
+
+ if (GET_CODE (PATTERN (our_prev)) == PARALLEL)
+ {
+ /* If we find a SET of something else, we can't
+ delete the insn. */
+
+ int i;
+
+ for (i = 0; i < XVECLEN (PATTERN (our_prev), 0); i++)
+ {
+ rtx part = XVECEXP (PATTERN (our_prev), 0, i);
+
+ if (GET_CODE (part) == SET
+ && SET_DEST (part) != XEXP (note, 0))
+ break;
+ }
+
+ if (i == XVECLEN (PATTERN (our_prev), 0))
+ delete_computation (our_prev);
+ }
+ else if (GET_CODE (PATTERN (our_prev)) == SET
+ && SET_DEST (PATTERN (our_prev)) == XEXP (note, 0))
+ delete_computation (our_prev);
+
+ break;
+ }
+
+ /* If OUR_PREV references the register that dies here, it is an
+ additional use. Hence any prior SET isn't dead. However, this
+ insn becomes the new place for the REG_DEAD note. */
+ if (reg_overlap_mentioned_p (XEXP (note, 0),
+ PATTERN (our_prev)))
+ {
+ XEXP (note, 1) = REG_NOTES (our_prev);
+ REG_NOTES (our_prev) = note;
+ break;
+ }
+ }
+ }
+
+ delete_insn (insn);
+}
+
+/* Delete insn INSN from the chain of insns and update label ref counts.
+ May delete some following insns as a consequence; may even delete
+ a label elsewhere and insns that follow it.
+
+ Returns the first insn after INSN that was not deleted. */
+
+rtx
+delete_insn (insn)
+ register rtx insn;
+{
+ register rtx next = NEXT_INSN (insn);
+ register rtx prev = PREV_INSN (insn);
+ register int was_code_label = (GET_CODE (insn) == CODE_LABEL);
+ register int dont_really_delete = 0;
+
+ while (next && INSN_DELETED_P (next))
+ next = NEXT_INSN (next);
+
+ /* This insn is already deleted => return first following nondeleted. */
+ if (INSN_DELETED_P (insn))
+ return next;
+
+ /* Don't delete user-declared labels. Convert them to special NOTEs
+ instead. */
+ if (was_code_label && LABEL_NAME (insn) != 0
+ && optimize && ! dont_really_delete)
+ {
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED_LABEL;
+ NOTE_SOURCE_FILE (insn) = 0;
+ dont_really_delete = 1;
+ }
+ else
+ /* Mark this insn as deleted. */
+ INSN_DELETED_P (insn) = 1;
+
+ /* If this is an unconditional jump, delete it from the jump chain. */
+ if (simplejump_p (insn))
+ delete_from_jump_chain (insn);
+
+ /* If instruction is followed by a barrier,
+ delete the barrier too. */
+
+ if (next != 0 && GET_CODE (next) == BARRIER)
+ {
+ INSN_DELETED_P (next) = 1;
+ next = NEXT_INSN (next);
+ }
+
+ /* Patch out INSN (and the barrier if any) */
+
+ if (optimize && ! dont_really_delete)
+ {
+ if (prev)
+ {
+ NEXT_INSN (prev) = next;
+ if (GET_CODE (prev) == INSN && GET_CODE (PATTERN (prev)) == SEQUENCE)
+ NEXT_INSN (XVECEXP (PATTERN (prev), 0,
+ XVECLEN (PATTERN (prev), 0) - 1)) = next;
+ }
+
+ if (next)
+ {
+ PREV_INSN (next) = prev;
+ if (GET_CODE (next) == INSN && GET_CODE (PATTERN (next)) == SEQUENCE)
+ PREV_INSN (XVECEXP (PATTERN (next), 0, 0)) = prev;
+ }
+
+ if (prev && NEXT_INSN (prev) == 0)
+ set_last_insn (prev);
+ }
+
+ /* If deleting a jump, decrement the count of the label,
+ and delete the label if it is now unused. */
+
+ if (GET_CODE (insn) == JUMP_INSN && JUMP_LABEL (insn))
+ if (--LABEL_NUSES (JUMP_LABEL (insn)) == 0)
+ {
+ /* This can delete NEXT or PREV,
+ either directly if NEXT is JUMP_LABEL (INSN),
+ or indirectly through more levels of jumps. */
+ delete_insn (JUMP_LABEL (insn));
+ /* I feel a little doubtful about this loop,
+ but I see no clean and sure alternative way
+ to find the first insn after INSN that is not now deleted.
+ I hope this works. */
+ while (next && INSN_DELETED_P (next))
+ next = NEXT_INSN (next);
+ return next;
+ }
+
+ while (prev && (INSN_DELETED_P (prev) || GET_CODE (prev) == NOTE))
+ prev = PREV_INSN (prev);
+
+ /* If INSN was a label and a dispatch table follows it,
+ delete the dispatch table. The tablejump must have gone already.
+ It isn't useful to fall through into a table. */
+
+ if (was_code_label
+ && NEXT_INSN (insn) != 0
+ && GET_CODE (NEXT_INSN (insn)) == JUMP_INSN
+ && (GET_CODE (PATTERN (NEXT_INSN (insn))) == ADDR_VEC
+ || GET_CODE (PATTERN (NEXT_INSN (insn))) == ADDR_DIFF_VEC))
+ next = delete_insn (NEXT_INSN (insn));
+
+ /* If INSN was a label, delete insns following it if now unreachable. */
+
+ if (was_code_label && prev && GET_CODE (prev) == BARRIER)
+ {
+ register RTX_CODE code;
+ while (next != 0
+ && (GET_RTX_CLASS (code = GET_CODE (next)) == 'i'
+ || code == NOTE
+ || (code == CODE_LABEL && INSN_DELETED_P (next))))
+ {
+ if (code == NOTE
+ && NOTE_LINE_NUMBER (next) != NOTE_INSN_FUNCTION_END)
+ next = NEXT_INSN (next);
+ /* Keep going past other deleted labels to delete what follows. */
+ else if (code == CODE_LABEL && INSN_DELETED_P (next))
+ next = NEXT_INSN (next);
+ else
+ /* Note: if this deletes a jump, it can cause more
+ deletion of unreachable code, after a different label.
+ As long as the value from this recursive call is correct,
+ this invocation functions correctly. */
+ next = delete_insn (next);
+ }
+ }
+
+ return next;
+}
+
+/* Advance from INSN till reaching something not deleted
+ then return that. May return INSN itself. */
+
+rtx
+next_nondeleted_insn (insn)
+ rtx insn;
+{
+ while (INSN_DELETED_P (insn))
+ insn = NEXT_INSN (insn);
+ return insn;
+}
+
+/* Delete a range of insns from FROM to TO, inclusive.
+ This is for the sake of peephole optimization, so assume
+ that whatever these insns do will still be done by a new
+ peephole insn that will replace them. */
+
+void
+delete_for_peephole (from, to)
+ register rtx from, to;
+{
+ register rtx insn = from;
+
+ while (1)
+ {
+ register rtx next = NEXT_INSN (insn);
+ register rtx prev = PREV_INSN (insn);
+
+ if (GET_CODE (insn) != NOTE)
+ {
+ INSN_DELETED_P (insn) = 1;
+
+ /* Patch this insn out of the chain. */
+ /* We don't do this all at once, because we
+ must preserve all NOTEs. */
+ if (prev)
+ NEXT_INSN (prev) = next;
+
+ if (next)
+ PREV_INSN (next) = prev;
+ }
+
+ if (insn == to)
+ break;
+ insn = next;
+ }
+
+ /* Note that if TO is an unconditional jump
+ we *do not* delete the BARRIER that follows,
+ since the peephole that replaces this sequence
+ is also an unconditional jump in that case. */
+}
+
+/* Invert the condition of the jump JUMP, and make it jump
+ to label NLABEL instead of where it jumps now. */
+
+int
+invert_jump (jump, nlabel)
+ rtx jump, nlabel;
+{
+ /* We have to either invert the condition and change the label or
+ do neither. Either operation could fail. We first try to invert
+ the jump. If that succeeds, we try changing the label. If that fails,
+ we invert the jump back to what it was. */
+
+ if (! invert_exp (PATTERN (jump), jump))
+ return 0;
+
+ if (redirect_jump (jump, nlabel))
+ return 1;
+
+ if (! invert_exp (PATTERN (jump), jump))
+ /* This should just be putting it back the way it was. */
+ abort ();
+
+ return 0;
+}
+
+/* Invert the jump condition of rtx X contained in jump insn, INSN.
+
+ Return 1 if we can do so, 0 if we cannot find a way to do so that
+ matches a pattern. */
+
+int
+invert_exp (x, insn)
+ rtx x;
+ rtx insn;
+{
+ register RTX_CODE code;
+ register int i;
+ register char *fmt;
+
+ code = GET_CODE (x);
+
+ if (code == IF_THEN_ELSE)
+ {
+ register rtx comp = XEXP (x, 0);
+ register rtx tem;
+
+ /* We can do this in two ways: The preferable way, which can only
+ be done if this is not an integer comparison, is to reverse
+ the comparison code. Otherwise, swap the THEN-part and ELSE-part
+ of the IF_THEN_ELSE. If we can't do either, fail. */
+
+ if (can_reverse_comparison_p (comp, insn)
+ && validate_change (insn, &XEXP (x, 0),
+ gen_rtx (reverse_condition (GET_CODE (comp)),
+ GET_MODE (comp), XEXP (comp, 0),
+ XEXP (comp, 1)), 0))
+ return 1;
+
+ tem = XEXP (x, 1);
+ validate_change (insn, &XEXP (x, 1), XEXP (x, 2), 1);
+ validate_change (insn, &XEXP (x, 2), tem, 1);
+ return apply_change_group ();
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ if (! invert_exp (XEXP (x, i), insn))
+ return 0;
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ if (!invert_exp (XVECEXP (x, i, j), insn))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Make jump JUMP jump to label NLABEL instead of where it jumps now.
+ If the old jump target label is unused as a result,
+ it and the code following it may be deleted.
+
+ If NLABEL is zero, we are to turn the jump into a (possibly conditional)
+ RETURN insn.
+
+ The return value will be 1 if the change was made, 0 if it wasn't (this
+ can only occur for NLABEL == 0). */
+
+int
+redirect_jump (jump, nlabel)
+ rtx jump, nlabel;
+{
+ register rtx olabel = JUMP_LABEL (jump);
+
+ if (nlabel == olabel)
+ return 1;
+
+ if (! redirect_exp (&PATTERN (jump), olabel, nlabel, jump))
+ return 0;
+
+ /* If this is an unconditional branch, delete it from the jump_chain of
+ OLABEL and add it to the jump_chain of NLABEL (assuming both labels
+ have UID's in range and JUMP_CHAIN is valid). */
+ if (jump_chain && (simplejump_p (jump)
+ || GET_CODE (PATTERN (jump)) == RETURN))
+ {
+ int label_index = nlabel ? INSN_UID (nlabel) : 0;
+
+ delete_from_jump_chain (jump);
+ if (label_index < max_jump_chain
+ && INSN_UID (jump) < max_jump_chain)
+ {
+ jump_chain[INSN_UID (jump)] = jump_chain[label_index];
+ jump_chain[label_index] = jump;
+ }
+ }
+
+ JUMP_LABEL (jump) = nlabel;
+ if (nlabel)
+ ++LABEL_NUSES (nlabel);
+
+ if (olabel && --LABEL_NUSES (olabel) == 0)
+ delete_insn (olabel);
+
+ return 1;
+}
+
+/* Delete the instruction JUMP from any jump chain it might be on. */
+
+static void
+delete_from_jump_chain (jump)
+ rtx jump;
+{
+ int index;
+ rtx olabel = JUMP_LABEL (jump);
+
+ /* Handle unconditional jumps. */
+ if (jump_chain && olabel != 0
+ && INSN_UID (olabel) < max_jump_chain
+ && simplejump_p (jump))
+ index = INSN_UID (olabel);
+ /* Handle return insns. */
+ else if (jump_chain && GET_CODE (PATTERN (jump)) == RETURN)
+ index = 0;
+ else return;
+
+ if (jump_chain[index] == jump)
+ jump_chain[index] = jump_chain[INSN_UID (jump)];
+ else
+ {
+ rtx insn;
+
+ for (insn = jump_chain[index];
+ insn != 0;
+ insn = jump_chain[INSN_UID (insn)])
+ if (jump_chain[INSN_UID (insn)] == jump)
+ {
+ jump_chain[INSN_UID (insn)] = jump_chain[INSN_UID (jump)];
+ break;
+ }
+ }
+}
+
+/* If NLABEL is nonzero, throughout the rtx at LOC,
+ alter (LABEL_REF OLABEL) to (LABEL_REF NLABEL). If OLABEL is
+ zero, alter (RETURN) to (LABEL_REF NLABEL).
+
+ If NLABEL is zero, alter (LABEL_REF OLABEL) to (RETURN) and check
+ validity with validate_change. Convert (set (pc) (label_ref olabel))
+ to (return).
+
+ Return 0 if we found a change we would like to make but it is invalid.
+ Otherwise, return 1. */
+
+int
+redirect_exp (loc, olabel, nlabel, insn)
+ rtx *loc;
+ rtx olabel, nlabel;
+ rtx insn;
+{
+ register rtx x = *loc;
+ register RTX_CODE code = GET_CODE (x);
+ register int i;
+ register char *fmt;
+
+ if (code == LABEL_REF)
+ {
+ if (XEXP (x, 0) == olabel)
+ {
+ if (nlabel)
+ XEXP (x, 0) = nlabel;
+ else
+ return validate_change (insn, loc, gen_rtx (RETURN, VOIDmode), 0);
+ return 1;
+ }
+ }
+ else if (code == RETURN && olabel == 0)
+ {
+ x = gen_rtx (LABEL_REF, VOIDmode, nlabel);
+ if (loc == &PATTERN (insn))
+ x = gen_rtx (SET, VOIDmode, pc_rtx, x);
+ return validate_change (insn, loc, x, 0);
+ }
+
+ if (code == SET && nlabel == 0 && SET_DEST (x) == pc_rtx
+ && GET_CODE (SET_SRC (x)) == LABEL_REF
+ && XEXP (SET_SRC (x), 0) == olabel)
+ return validate_change (insn, loc, gen_rtx (RETURN, VOIDmode), 0);
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ if (! redirect_exp (&XEXP (x, i), olabel, nlabel, insn))
+ return 0;
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ if (! redirect_exp (&XVECEXP (x, i, j), olabel, nlabel, insn))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Make jump JUMP jump to label NLABEL, assuming it used to be a tablejump.
+
+ If the old jump target label (before the dispatch table) becomes unused,
+ it and the dispatch table may be deleted. In that case, find the insn
+ before the jump references that label and delete it and logical successors
+ too. */
+
+static void
+redirect_tablejump (jump, nlabel)
+ rtx jump, nlabel;
+{
+ register rtx olabel = JUMP_LABEL (jump);
+
+ /* Add this jump to the jump_chain of NLABEL. */
+ if (jump_chain && INSN_UID (nlabel) < max_jump_chain
+ && INSN_UID (jump) < max_jump_chain)
+ {
+ jump_chain[INSN_UID (jump)] = jump_chain[INSN_UID (nlabel)];
+ jump_chain[INSN_UID (nlabel)] = jump;
+ }
+
+ PATTERN (jump) = gen_jump (nlabel);
+ JUMP_LABEL (jump) = nlabel;
+ ++LABEL_NUSES (nlabel);
+ INSN_CODE (jump) = -1;
+
+ if (--LABEL_NUSES (olabel) == 0)
+ {
+ delete_labelref_insn (jump, olabel, 0);
+ delete_insn (olabel);
+ }
+}
+
+/* Find the insn referencing LABEL that is a logical predecessor of INSN.
+ If we found one, delete it and then delete this insn if DELETE_THIS is
+ non-zero. Return non-zero if INSN or a predecessor references LABEL. */
+
+static int
+delete_labelref_insn (insn, label, delete_this)
+ rtx insn, label;
+ int delete_this;
+{
+ int deleted = 0;
+ rtx link;
+
+ if (GET_CODE (insn) != NOTE
+ && reg_mentioned_p (label, PATTERN (insn)))
+ {
+ if (delete_this)
+ {
+ delete_insn (insn);
+ deleted = 1;
+ }
+ else
+ return 1;
+ }
+
+ for (link = LOG_LINKS (insn); link; link = XEXP (link, 1))
+ if (delete_labelref_insn (XEXP (link, 0), label, 1))
+ {
+ if (delete_this)
+ {
+ delete_insn (insn);
+ deleted = 1;
+ }
+ else
+ return 1;
+ }
+
+ return deleted;
+}
+
+/* Like rtx_equal_p except that it considers two REGs as equal
+ if they renumber to the same value and considers two commutative
+ operations to be the same if the order of the operands has been
+ reversed. */
+
+int
+rtx_renumbered_equal_p (x, y)
+ rtx x, y;
+{
+ register int i;
+ register RTX_CODE code = GET_CODE (x);
+ register char *fmt;
+
+ if (x == y)
+ return 1;
+
+ if ((code == REG || (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG))
+ && (GET_CODE (y) == REG || (GET_CODE (y) == SUBREG
+ && GET_CODE (SUBREG_REG (y)) == REG)))
+ {
+ int reg_x = -1, reg_y = -1;
+ int word_x = 0, word_y = 0;
+
+ if (GET_MODE (x) != GET_MODE (y))
+ return 0;
+
+ /* If we haven't done any renumbering, don't
+ make any assumptions. */
+ if (reg_renumber == 0)
+ return rtx_equal_p (x, y);
+
+ if (code == SUBREG)
+ {
+ reg_x = REGNO (SUBREG_REG (x));
+ word_x = SUBREG_WORD (x);
+
+ if (reg_renumber[reg_x] >= 0)
+ {
+ reg_x = reg_renumber[reg_x] + word_x;
+ word_x = 0;
+ }
+ }
+
+ else
+ {
+ reg_x = REGNO (x);
+ if (reg_renumber[reg_x] >= 0)
+ reg_x = reg_renumber[reg_x];
+ }
+
+ if (GET_CODE (y) == SUBREG)
+ {
+ reg_y = REGNO (SUBREG_REG (y));
+ word_y = SUBREG_WORD (y);
+
+ if (reg_renumber[reg_y] >= 0)
+ {
+ reg_y = reg_renumber[reg_y];
+ word_y = 0;
+ }
+ }
+
+ else
+ {
+ reg_y = REGNO (y);
+ if (reg_renumber[reg_y] >= 0)
+ reg_y = reg_renumber[reg_y];
+ }
+
+ return reg_x >= 0 && reg_x == reg_y && word_x == word_y;
+ }
+
+ /* Now we have disposed of all the cases
+ in which different rtx codes can match. */
+ if (code != GET_CODE (y))
+ return 0;
+
+ switch (code)
+ {
+ case PC:
+ case CC0:
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ return 0;
+
+ case CONST_INT:
+ return INTVAL (x) == INTVAL (y);
+
+ case LABEL_REF:
+ /* We can't assume nonlocal labels have their following insns yet. */
+ if (LABEL_REF_NONLOCAL_P (x) || LABEL_REF_NONLOCAL_P (y))
+ return XEXP (x, 0) == XEXP (y, 0);
+
+ /* Two label-refs are equivalent if they point at labels
+ in the same position in the instruction stream. */
+ return (next_real_insn (XEXP (x, 0))
+ == next_real_insn (XEXP (y, 0)));
+
+ case SYMBOL_REF:
+ return XSTR (x, 0) == XSTR (y, 0);
+ }
+
+ /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. */
+
+ if (GET_MODE (x) != GET_MODE (y))
+ return 0;
+
+ /* For commutative operations, the RTX match if the operand match in any
+ order. Also handle the simple binary and unary cases without a loop. */
+ if (code == EQ || code == NE || GET_RTX_CLASS (code) == 'c')
+ return ((rtx_renumbered_equal_p (XEXP (x, 0), XEXP (y, 0))
+ && rtx_renumbered_equal_p (XEXP (x, 1), XEXP (y, 1)))
+ || (rtx_renumbered_equal_p (XEXP (x, 0), XEXP (y, 1))
+ && rtx_renumbered_equal_p (XEXP (x, 1), XEXP (y, 0))));
+ else if (GET_RTX_CLASS (code) == '<' || GET_RTX_CLASS (code) == '2')
+ return (rtx_renumbered_equal_p (XEXP (x, 0), XEXP (y, 0))
+ && rtx_renumbered_equal_p (XEXP (x, 1), XEXP (y, 1)));
+ else if (GET_RTX_CLASS (code) == '1')
+ return rtx_renumbered_equal_p (XEXP (x, 0), XEXP (y, 0));
+
+ /* Compare the elements. If any pair of corresponding elements
+ fail to match, return 0 for the whole things. */
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ register int j;
+ switch (fmt[i])
+ {
+ case 'w':
+ if (XWINT (x, i) != XWINT (y, i))
+ return 0;
+ break;
+
+ case 'i':
+ if (XINT (x, i) != XINT (y, i))
+ return 0;
+ break;
+
+ case 's':
+ if (strcmp (XSTR (x, i), XSTR (y, i)))
+ return 0;
+ break;
+
+ case 'e':
+ if (! rtx_renumbered_equal_p (XEXP (x, i), XEXP (y, i)))
+ return 0;
+ break;
+
+ case 'u':
+ if (XEXP (x, i) != XEXP (y, i))
+ return 0;
+ /* fall through. */
+ case '0':
+ break;
+
+ case 'E':
+ if (XVECLEN (x, i) != XVECLEN (y, i))
+ return 0;
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (!rtx_renumbered_equal_p (XVECEXP (x, i, j), XVECEXP (y, i, j)))
+ return 0;
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ return 1;
+}
+
+/* If X is a hard register or equivalent to one or a subregister of one,
+ return the hard register number. If X is a pseudo register that was not
+ assigned a hard register, return the pseudo register number. Otherwise,
+ return -1. Any rtx is valid for X. */
+
+int
+true_regnum (x)
+ rtx x;
+{
+ if (GET_CODE (x) == REG)
+ {
+ if (REGNO (x) >= FIRST_PSEUDO_REGISTER && reg_renumber[REGNO (x)] >= 0)
+ return reg_renumber[REGNO (x)];
+ return REGNO (x);
+ }
+ if (GET_CODE (x) == SUBREG)
+ {
+ int base = true_regnum (SUBREG_REG (x));
+ if (base >= 0 && base < FIRST_PSEUDO_REGISTER)
+ return SUBREG_WORD (x) + base;
+ }
+ return -1;
+}
+
+/* Optimize code of the form:
+
+ for (x = a[i]; x; ...)
+ ...
+ for (x = a[i]; x; ...)
+ ...
+ foo:
+
+ Loop optimize will change the above code into
+
+ if (x = a[i])
+ for (;;)
+ { ...; if (! (x = ...)) break; }
+ if (x = a[i])
+ for (;;)
+ { ...; if (! (x = ...)) break; }
+ foo:
+
+ In general, if the first test fails, the program can branch
+ directly to `foo' and skip the second try which is doomed to fail.
+ We run this after loop optimization and before flow analysis. */
+
+/* When comparing the insn patterns, we track the fact that different
+ pseudo-register numbers may have been used in each computation.
+ The following array stores an equivalence -- same_regs[I] == J means
+ that pseudo register I was used in the first set of tests in a context
+ where J was used in the second set. We also count the number of such
+ pending equivalences. If nonzero, the expressions really aren't the
+ same. */
+
+static int *same_regs;
+
+static int num_same_regs;
+
+/* Track any registers modified between the target of the first jump and
+ the second jump. They never compare equal. */
+
+static char *modified_regs;
+
+/* Record if memory was modified. */
+
+static int modified_mem;
+
+/* Called via note_stores on each insn between the target of the first
+ branch and the second branch. It marks any changed registers. */
+
+static void
+mark_modified_reg (dest, x)
+ rtx dest;
+ rtx x;
+{
+ int regno, i;
+
+ if (GET_CODE (dest) == SUBREG)
+ dest = SUBREG_REG (dest);
+
+ if (GET_CODE (dest) == MEM)
+ modified_mem = 1;
+
+ if (GET_CODE (dest) != REG)
+ return;
+
+ regno = REGNO (dest);
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ modified_regs[regno] = 1;
+ else
+ for (i = 0; i < HARD_REGNO_NREGS (regno, GET_MODE (dest)); i++)
+ modified_regs[regno + i] = 1;
+}
+
+/* F is the first insn in the chain of insns. */
+
+void
+thread_jumps (f, max_reg, flag_before_loop)
+ rtx f;
+ int max_reg;
+ int flag_before_loop;
+{
+ /* Basic algorithm is to find a conditional branch,
+ the label it may branch to, and the branch after
+ that label. If the two branches test the same condition,
+ walk back from both branch paths until the insn patterns
+ differ, or code labels are hit. If we make it back to
+ the target of the first branch, then we know that the first branch
+ will either always succeed or always fail depending on the relative
+ senses of the two branches. So adjust the first branch accordingly
+ in this case. */
+
+ rtx label, b1, b2, t1, t2;
+ enum rtx_code code1, code2;
+ rtx b1op0, b1op1, b2op0, b2op1;
+ int changed = 1;
+ int i;
+ int *all_reset;
+
+ /* Allocate register tables and quick-reset table. */
+ modified_regs = (char *) alloca (max_reg * sizeof (char));
+ same_regs = (int *) alloca (max_reg * sizeof (int));
+ all_reset = (int *) alloca (max_reg * sizeof (int));
+ for (i = 0; i < max_reg; i++)
+ all_reset[i] = -1;
+
+ while (changed)
+ {
+ changed = 0;
+
+ for (b1 = f; b1; b1 = NEXT_INSN (b1))
+ {
+ /* Get to a candidate branch insn. */
+ if (GET_CODE (b1) != JUMP_INSN
+ || ! condjump_p (b1) || simplejump_p (b1)
+ || JUMP_LABEL (b1) == 0)
+ continue;
+
+ bzero (modified_regs, max_reg * sizeof (char));
+ modified_mem = 0;
+
+ bcopy ((char *) all_reset, (char *) same_regs,
+ max_reg * sizeof (int));
+ num_same_regs = 0;
+
+ label = JUMP_LABEL (b1);
+
+ /* Look for a branch after the target. Record any registers and
+ memory modified between the target and the branch. Stop when we
+ get to a label since we can't know what was changed there. */
+ for (b2 = NEXT_INSN (label); b2; b2 = NEXT_INSN (b2))
+ {
+ if (GET_CODE (b2) == CODE_LABEL)
+ break;
+
+ else if (GET_CODE (b2) == JUMP_INSN)
+ {
+ /* If this is an unconditional jump and is the only use of
+ its target label, we can follow it. */
+ if (simplejump_p (b2)
+ && JUMP_LABEL (b2) != 0
+ && LABEL_NUSES (JUMP_LABEL (b2)) == 1)
+ {
+ b2 = JUMP_LABEL (b2);
+ continue;
+ }
+ else
+ break;
+ }
+
+ if (GET_CODE (b2) != CALL_INSN && GET_CODE (b2) != INSN)
+ continue;
+
+ if (GET_CODE (b2) == CALL_INSN)
+ {
+ modified_mem = 1;
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (call_used_regs[i] && ! fixed_regs[i]
+ && i != STACK_POINTER_REGNUM
+ && i != FRAME_POINTER_REGNUM
+ && i != HARD_FRAME_POINTER_REGNUM
+ && i != ARG_POINTER_REGNUM)
+ modified_regs[i] = 1;
+ }
+
+ note_stores (PATTERN (b2), mark_modified_reg);
+ }
+
+ /* Check the next candidate branch insn from the label
+ of the first. */
+ if (b2 == 0
+ || GET_CODE (b2) != JUMP_INSN
+ || b2 == b1
+ || ! condjump_p (b2)
+ || simplejump_p (b2))
+ continue;
+
+ /* Get the comparison codes and operands, reversing the
+ codes if appropriate. If we don't have comparison codes,
+ we can't do anything. */
+ b1op0 = XEXP (XEXP (SET_SRC (PATTERN (b1)), 0), 0);
+ b1op1 = XEXP (XEXP (SET_SRC (PATTERN (b1)), 0), 1);
+ code1 = GET_CODE (XEXP (SET_SRC (PATTERN (b1)), 0));
+ if (XEXP (SET_SRC (PATTERN (b1)), 1) == pc_rtx)
+ code1 = reverse_condition (code1);
+
+ b2op0 = XEXP (XEXP (SET_SRC (PATTERN (b2)), 0), 0);
+ b2op1 = XEXP (XEXP (SET_SRC (PATTERN (b2)), 0), 1);
+ code2 = GET_CODE (XEXP (SET_SRC (PATTERN (b2)), 0));
+ if (XEXP (SET_SRC (PATTERN (b2)), 1) == pc_rtx)
+ code2 = reverse_condition (code2);
+
+ /* If they test the same things and knowing that B1 branches
+ tells us whether or not B2 branches, check if we
+ can thread the branch. */
+ if (rtx_equal_for_thread_p (b1op0, b2op0, b2)
+ && rtx_equal_for_thread_p (b1op1, b2op1, b2)
+ && (comparison_dominates_p (code1, code2)
+ || comparison_dominates_p (code1, reverse_condition (code2))))
+ {
+ t1 = prev_nonnote_insn (b1);
+ t2 = prev_nonnote_insn (b2);
+
+ while (t1 != 0 && t2 != 0)
+ {
+ if (t2 == label)
+ {
+ /* We have reached the target of the first branch.
+ If there are no pending register equivalents,
+ we know that this branch will either always
+ succeed (if the senses of the two branches are
+ the same) or always fail (if not). */
+ rtx new_label;
+
+ if (num_same_regs != 0)
+ break;
+
+ if (comparison_dominates_p (code1, code2))
+ new_label = JUMP_LABEL (b2);
+ else
+ new_label = get_label_after (b2);
+
+ if (JUMP_LABEL (b1) != new_label)
+ {
+ rtx prev = PREV_INSN (new_label);
+
+ if (flag_before_loop
+ && NOTE_LINE_NUMBER (prev) == NOTE_INSN_LOOP_BEG)
+ {
+ /* Don't thread to the loop label. If a loop
+ label is reused, loop optimization will
+ be disabled for that loop. */
+ new_label = gen_label_rtx ();
+ emit_label_after (new_label, PREV_INSN (prev));
+ }
+ changed |= redirect_jump (b1, new_label);
+ }
+ break;
+ }
+
+ /* If either of these is not a normal insn (it might be
+ a JUMP_INSN, CALL_INSN, or CODE_LABEL) we fail. (NOTEs
+ have already been skipped above.) Similarly, fail
+ if the insns are different. */
+ if (GET_CODE (t1) != INSN || GET_CODE (t2) != INSN
+ || recog_memoized (t1) != recog_memoized (t2)
+ || ! rtx_equal_for_thread_p (PATTERN (t1),
+ PATTERN (t2), t2))
+ break;
+
+ t1 = prev_nonnote_insn (t1);
+ t2 = prev_nonnote_insn (t2);
+ }
+ }
+ }
+ }
+}
+
+/* This is like RTX_EQUAL_P except that it knows about our handling of
+ possibly equivalent registers and knows to consider volatile and
+ modified objects as not equal.
+
+ YINSN is the insn containing Y. */
+
+int
+rtx_equal_for_thread_p (x, y, yinsn)
+ rtx x, y;
+ rtx yinsn;
+{
+ register int i;
+ register int j;
+ register enum rtx_code code;
+ register char *fmt;
+
+ code = GET_CODE (x);
+ /* Rtx's of different codes cannot be equal. */
+ if (code != GET_CODE (y))
+ return 0;
+
+ /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent.
+ (REG:SI x) and (REG:HI x) are NOT equivalent. */
+
+ if (GET_MODE (x) != GET_MODE (y))
+ return 0;
+
+ /* For commutative operations, the RTX match if the operand match in any
+ order. Also handle the simple binary and unary cases without a loop. */
+ if (code == EQ || code == NE || GET_RTX_CLASS (code) == 'c')
+ return ((rtx_equal_for_thread_p (XEXP (x, 0), XEXP (y, 0), yinsn)
+ && rtx_equal_for_thread_p (XEXP (x, 1), XEXP (y, 1), yinsn))
+ || (rtx_equal_for_thread_p (XEXP (x, 0), XEXP (y, 1), yinsn)
+ && rtx_equal_for_thread_p (XEXP (x, 1), XEXP (y, 0), yinsn)));
+ else if (GET_RTX_CLASS (code) == '<' || GET_RTX_CLASS (code) == '2')
+ return (rtx_equal_for_thread_p (XEXP (x, 0), XEXP (y, 0), yinsn)
+ && rtx_equal_for_thread_p (XEXP (x, 1), XEXP (y, 1), yinsn));
+ else if (GET_RTX_CLASS (code) == '1')
+ return rtx_equal_for_thread_p (XEXP (x, 0), XEXP (y, 0), yinsn);
+
+ /* Handle special-cases first. */
+ switch (code)
+ {
+ case REG:
+ if (REGNO (x) == REGNO (y) && ! modified_regs[REGNO (x)])
+ return 1;
+
+ /* If neither is user variable or hard register, check for possible
+ equivalence. */
+ if (REG_USERVAR_P (x) || REG_USERVAR_P (y)
+ || REGNO (x) < FIRST_PSEUDO_REGISTER
+ || REGNO (y) < FIRST_PSEUDO_REGISTER)
+ return 0;
+
+ if (same_regs[REGNO (x)] == -1)
+ {
+ same_regs[REGNO (x)] = REGNO (y);
+ num_same_regs++;
+
+ /* If this is the first time we are seeing a register on the `Y'
+ side, see if it is the last use. If not, we can't thread the
+ jump, so mark it as not equivalent. */
+ if (regno_last_uid[REGNO (y)] != INSN_UID (yinsn))
+ return 0;
+
+ return 1;
+ }
+ else
+ return (same_regs[REGNO (x)] == REGNO (y));
+
+ break;
+
+ case MEM:
+ /* If memory modified or either volatile, not equivalent.
+ Else, check address. */
+ if (modified_mem || MEM_VOLATILE_P (x) || MEM_VOLATILE_P (y))
+ return 0;
+
+ return rtx_equal_for_thread_p (XEXP (x, 0), XEXP (y, 0), yinsn);
+
+ case ASM_INPUT:
+ if (MEM_VOLATILE_P (x) || MEM_VOLATILE_P (y))
+ return 0;
+
+ break;
+
+ case SET:
+ /* Cancel a pending `same_regs' if setting equivalenced registers.
+ Then process source. */
+ if (GET_CODE (SET_DEST (x)) == REG
+ && GET_CODE (SET_DEST (y)) == REG)
+ {
+ if (same_regs[REGNO (SET_DEST (x))] == REGNO (SET_DEST (y)))
+ {
+ same_regs[REGNO (SET_DEST (x))] = -1;
+ num_same_regs--;
+ }
+ else if (REGNO (SET_DEST (x)) != REGNO (SET_DEST (y)))
+ return 0;
+ }
+ else
+ if (rtx_equal_for_thread_p (SET_DEST (x), SET_DEST (y), yinsn) == 0)
+ return 0;
+
+ return rtx_equal_for_thread_p (SET_SRC (x), SET_SRC (y), yinsn);
+
+ case LABEL_REF:
+ return XEXP (x, 0) == XEXP (y, 0);
+
+ case SYMBOL_REF:
+ return XSTR (x, 0) == XSTR (y, 0);
+ }
+
+ if (x == y)
+ return 1;
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ switch (fmt[i])
+ {
+ case 'w':
+ if (XWINT (x, i) != XWINT (y, i))
+ return 0;
+ break;
+
+ case 'n':
+ case 'i':
+ if (XINT (x, i) != XINT (y, i))
+ return 0;
+ break;
+
+ case 'V':
+ case 'E':
+ /* Two vectors must have the same length. */
+ if (XVECLEN (x, i) != XVECLEN (y, i))
+ return 0;
+
+ /* And the corresponding elements must match. */
+ for (j = 0; j < XVECLEN (x, i); j++)
+ if (rtx_equal_for_thread_p (XVECEXP (x, i, j),
+ XVECEXP (y, i, j), yinsn) == 0)
+ return 0;
+ break;
+
+ case 'e':
+ if (rtx_equal_for_thread_p (XEXP (x, i), XEXP (y, i), yinsn) == 0)
+ return 0;
+ break;
+
+ case 'S':
+ case 's':
+ if (strcmp (XSTR (x, i), XSTR (y, i)))
+ return 0;
+ break;
+
+ case 'u':
+ /* These are just backpointers, so they don't matter. */
+ break;
+
+ case '0':
+ break;
+
+ /* It is believed that rtx's at this level will never
+ contain anything but integers and other rtx's,
+ except for within LABEL_REFs and SYMBOL_REFs. */
+ default:
+ abort ();
+ }
+ }
+ return 1;
+}
diff --git a/gnu/usr.bin/cc/cc_int/local-alloc.c b/gnu/usr.bin/cc/cc_int/local-alloc.c
new file mode 100644
index 0000000..3b2d81e
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/local-alloc.c
@@ -0,0 +1,2355 @@
+/* Allocate registers within a basic block, for GNU compiler.
+ Copyright (C) 1987, 1988, 1991, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* Allocation of hard register numbers to pseudo registers is done in
+ two passes. In this pass we consider only regs that are born and
+ die once within one basic block. We do this one basic block at a
+ time. Then the next pass allocates the registers that remain.
+ Two passes are used because this pass uses methods that work only
+ on linear code, but that do a better job than the general methods
+ used in global_alloc, and more quickly too.
+
+ The assignments made are recorded in the vector reg_renumber
+ whose space is allocated here. The rtl code itself is not altered.
+
+ We assign each instruction in the basic block a number
+ which is its order from the beginning of the block.
+ Then we can represent the lifetime of a pseudo register with
+ a pair of numbers, and check for conflicts easily.
+ We can record the availability of hard registers with a
+ HARD_REG_SET for each instruction. The HARD_REG_SET
+ contains 0 or 1 for each hard reg.
+
+ To avoid register shuffling, we tie registers together when one
+ dies by being copied into another, or dies in an instruction that
+ does arithmetic to produce another. The tied registers are
+ allocated as one. Registers with different reg class preferences
+ can never be tied unless the class preferred by one is a subclass
+ of the one preferred by the other.
+
+ Tying is represented with "quantity numbers".
+ A non-tied register is given a new quantity number.
+ Tied registers have the same quantity number.
+
+ We have provision to exempt registers, even when they are contained
+ within the block, that can be tied to others that are not contained in it.
+ This is so that global_alloc could process them both and tie them then.
+ But this is currently disabled since tying in global_alloc is not
+ yet implemented. */
+
+#include <stdio.h>
+#include "config.h"
+#include "rtl.h"
+#include "flags.h"
+#include "basic-block.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "insn-config.h"
+#include "recog.h"
+#include "output.h"
+
+/* Pseudos allocated here cannot be reallocated by global.c if the hard
+ register is used as a spill register. So we don't allocate such pseudos
+ here if their preferred class is likely to be used by spills.
+
+ On most machines, the appropriate test is if the class has one
+ register, so we default to that. */
+
+#ifndef CLASS_LIKELY_SPILLED_P
+#define CLASS_LIKELY_SPILLED_P(CLASS) (reg_class_size[(int) (CLASS)] == 1)
+#endif
+
+/* Next quantity number available for allocation. */
+
+static int next_qty;
+
+/* In all the following vectors indexed by quantity number. */
+
+/* Element Q is the hard reg number chosen for quantity Q,
+ or -1 if none was found. */
+
+static short *qty_phys_reg;
+
+/* We maintain two hard register sets that indicate suggested hard registers
+ for each quantity. The first, qty_phys_copy_sugg, contains hard registers
+ that are tied to the quantity by a simple copy. The second contains all
+ hard registers that are tied to the quantity via an arithmetic operation.
+
+ The former register set is given priority for allocation. This tends to
+ eliminate copy insns. */
+
+/* Element Q is a set of hard registers that are suggested for quantity Q by
+ copy insns. */
+
+static HARD_REG_SET *qty_phys_copy_sugg;
+
+/* Element Q is a set of hard registers that are suggested for quantity Q by
+ arithmetic insns. */
+
+static HARD_REG_SET *qty_phys_sugg;
+
+/* Element Q is the number of suggested registers in qty_phys_copy_sugg. */
+
+static short *qty_phys_num_copy_sugg;
+
+/* Element Q is the number of suggested registers in qty_phys_sugg. */
+
+static short *qty_phys_num_sugg;
+
+/* Element Q is the number of refs to quantity Q. */
+
+static int *qty_n_refs;
+
+/* Element Q is a reg class contained in (smaller than) the
+ preferred classes of all the pseudo regs that are tied in quantity Q.
+ This is the preferred class for allocating that quantity. */
+
+static enum reg_class *qty_min_class;
+
+/* Insn number (counting from head of basic block)
+ where quantity Q was born. -1 if birth has not been recorded. */
+
+static int *qty_birth;
+
+/* Insn number (counting from head of basic block)
+ where quantity Q died. Due to the way tying is done,
+ and the fact that we consider in this pass only regs that die but once,
+ a quantity can die only once. Each quantity's life span
+ is a set of consecutive insns. -1 if death has not been recorded. */
+
+static int *qty_death;
+
+/* Number of words needed to hold the data in quantity Q.
+ This depends on its machine mode. It is used for these purposes:
+ 1. It is used in computing the relative importances of qtys,
+ which determines the order in which we look for regs for them.
+ 2. It is used in rules that prevent tying several registers of
+ different sizes in a way that is geometrically impossible
+ (see combine_regs). */
+
+static int *qty_size;
+
+/* This holds the mode of the registers that are tied to qty Q,
+ or VOIDmode if registers with differing modes are tied together. */
+
+static enum machine_mode *qty_mode;
+
+/* Number of times a reg tied to qty Q lives across a CALL_INSN. */
+
+static int *qty_n_calls_crossed;
+
+/* Register class within which we allocate qty Q if we can't get
+ its preferred class. */
+
+static enum reg_class *qty_alternate_class;
+
+/* Element Q is the SCRATCH expression for which this quantity is being
+ allocated or 0 if this quantity is allocating registers. */
+
+static rtx *qty_scratch_rtx;
+
+/* Element Q is the register number of one pseudo register whose
+ reg_qty value is Q, or -1 is this quantity is for a SCRATCH. This
+ register should be the head of the chain maintained in reg_next_in_qty. */
+
+static int *qty_first_reg;
+
+/* If (REG N) has been assigned a quantity number, is a register number
+ of another register assigned the same quantity number, or -1 for the
+ end of the chain. qty_first_reg point to the head of this chain. */
+
+static int *reg_next_in_qty;
+
+/* reg_qty[N] (where N is a pseudo reg number) is the qty number of that reg
+ if it is >= 0,
+ of -1 if this register cannot be allocated by local-alloc,
+ or -2 if not known yet.
+
+ Note that if we see a use or death of pseudo register N with
+ reg_qty[N] == -2, register N must be local to the current block. If
+ it were used in more than one block, we would have reg_qty[N] == -1.
+ This relies on the fact that if reg_basic_block[N] is >= 0, register N
+ will not appear in any other block. We save a considerable number of
+ tests by exploiting this.
+
+ If N is < FIRST_PSEUDO_REGISTER, reg_qty[N] is undefined and should not
+ be referenced. */
+
+static int *reg_qty;
+
+/* The offset (in words) of register N within its quantity.
+ This can be nonzero if register N is SImode, and has been tied
+ to a subreg of a DImode register. */
+
+static char *reg_offset;
+
+/* Vector of substitutions of register numbers,
+ used to map pseudo regs into hardware regs.
+ This is set up as a result of register allocation.
+ Element N is the hard reg assigned to pseudo reg N,
+ or is -1 if no hard reg was assigned.
+ If N is a hard reg number, element N is N. */
+
+short *reg_renumber;
+
+/* Set of hard registers live at the current point in the scan
+ of the instructions in a basic block. */
+
+static HARD_REG_SET regs_live;
+
+/* Each set of hard registers indicates registers live at a particular
+ point in the basic block. For N even, regs_live_at[N] says which
+ hard registers are needed *after* insn N/2 (i.e., they may not
+ conflict with the outputs of insn N/2 or the inputs of insn N/2 + 1.
+
+ If an object is to conflict with the inputs of insn J but not the
+ outputs of insn J + 1, we say it is born at index J*2 - 1. Similarly,
+ if it is to conflict with the outputs of insn J but not the inputs of
+ insn J + 1, it is said to die at index J*2 + 1. */
+
+static HARD_REG_SET *regs_live_at;
+
+int *scratch_block;
+rtx *scratch_list;
+int scratch_list_length;
+static int scratch_index;
+
+/* Communicate local vars `insn_number' and `insn'
+ from `block_alloc' to `reg_is_set', `wipe_dead_reg', and `alloc_qty'. */
+static int this_insn_number;
+static rtx this_insn;
+
+static void alloc_qty PROTO((int, enum machine_mode, int, int));
+static void alloc_qty_for_scratch PROTO((rtx, int, rtx, int, int));
+static void validate_equiv_mem_from_store PROTO((rtx, rtx));
+static int validate_equiv_mem PROTO((rtx, rtx, rtx));
+static int memref_referenced_p PROTO((rtx, rtx));
+static int memref_used_between_p PROTO((rtx, rtx, rtx));
+static void optimize_reg_copy_1 PROTO((rtx, rtx, rtx));
+static void optimize_reg_copy_2 PROTO((rtx, rtx, rtx));
+static void update_equiv_regs PROTO((void));
+static void block_alloc PROTO((int));
+static int qty_sugg_compare PROTO((int, int));
+static int qty_sugg_compare_1 PROTO((int *, int *));
+static int qty_compare PROTO((int, int));
+static int qty_compare_1 PROTO((int *, int *));
+static int combine_regs PROTO((rtx, rtx, int, int, rtx, int));
+static int reg_meets_class_p PROTO((int, enum reg_class));
+static int reg_classes_overlap_p PROTO((enum reg_class, enum reg_class,
+ int));
+static void update_qty_class PROTO((int, int));
+static void reg_is_set PROTO((rtx, rtx));
+static void reg_is_born PROTO((rtx, int));
+static void wipe_dead_reg PROTO((rtx, int));
+static int find_free_reg PROTO((enum reg_class, enum machine_mode,
+ int, int, int, int, int));
+static void mark_life PROTO((int, enum machine_mode, int));
+static void post_mark_life PROTO((int, enum machine_mode, int, int, int));
+static int no_conflict_p PROTO((rtx, rtx, rtx));
+static int requires_inout PROTO((char *));
+
+/* Allocate a new quantity (new within current basic block)
+ for register number REGNO which is born at index BIRTH
+ within the block. MODE and SIZE are info on reg REGNO. */
+
+static void
+alloc_qty (regno, mode, size, birth)
+ int regno;
+ enum machine_mode mode;
+ int size, birth;
+{
+ register int qty = next_qty++;
+
+ reg_qty[regno] = qty;
+ reg_offset[regno] = 0;
+ reg_next_in_qty[regno] = -1;
+
+ qty_first_reg[qty] = regno;
+ qty_size[qty] = size;
+ qty_mode[qty] = mode;
+ qty_birth[qty] = birth;
+ qty_n_calls_crossed[qty] = reg_n_calls_crossed[regno];
+ qty_min_class[qty] = reg_preferred_class (regno);
+ qty_alternate_class[qty] = reg_alternate_class (regno);
+ qty_n_refs[qty] = reg_n_refs[regno];
+}
+
+/* Similar to `alloc_qty', but allocates a quantity for a SCRATCH rtx
+ used as operand N in INSN. We assume here that the SCRATCH is used in
+ a CLOBBER. */
+
+static void
+alloc_qty_for_scratch (scratch, n, insn, insn_code_num, insn_number)
+ rtx scratch;
+ int n;
+ rtx insn;
+ int insn_code_num, insn_number;
+{
+ register int qty;
+ enum reg_class class;
+ char *p, c;
+ int i;
+
+#ifdef REGISTER_CONSTRAINTS
+ /* If we haven't yet computed which alternative will be used, do so now.
+ Then set P to the constraints for that alternative. */
+ if (which_alternative == -1)
+ if (! constrain_operands (insn_code_num, 0))
+ return;
+
+ for (p = insn_operand_constraint[insn_code_num][n], i = 0;
+ *p && i < which_alternative; p++)
+ if (*p == ',')
+ i++;
+
+ /* Compute the class required for this SCRATCH. If we don't need a
+ register, the class will remain NO_REGS. If we guessed the alternative
+ number incorrectly, reload will fix things up for us. */
+
+ class = NO_REGS;
+ while ((c = *p++) != '\0' && c != ',')
+ switch (c)
+ {
+ case '=': case '+': case '?':
+ case '#': case '&': case '!':
+ case '*': case '%':
+ case '0': case '1': case '2': case '3': case '4':
+ case 'm': case '<': case '>': case 'V': case 'o':
+ case 'E': case 'F': case 'G': case 'H':
+ case 's': case 'i': case 'n':
+ case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P':
+#ifdef EXTRA_CONSTRAINT
+ case 'Q': case 'R': case 'S': case 'T': case 'U':
+#endif
+ case 'p':
+ /* These don't say anything we care about. */
+ break;
+
+ case 'X':
+ /* We don't need to allocate this SCRATCH. */
+ return;
+
+ case 'g': case 'r':
+ class = reg_class_subunion[(int) class][(int) GENERAL_REGS];
+ break;
+
+ default:
+ class
+ = reg_class_subunion[(int) class][(int) REG_CLASS_FROM_LETTER (c)];
+ break;
+ }
+
+ if (class == NO_REGS)
+ return;
+
+#else /* REGISTER_CONSTRAINTS */
+
+ class = GENERAL_REGS;
+#endif
+
+
+ qty = next_qty++;
+
+ qty_first_reg[qty] = -1;
+ qty_scratch_rtx[qty] = scratch;
+ qty_size[qty] = GET_MODE_SIZE (GET_MODE (scratch));
+ qty_mode[qty] = GET_MODE (scratch);
+ qty_birth[qty] = 2 * insn_number - 1;
+ qty_death[qty] = 2 * insn_number + 1;
+ qty_n_calls_crossed[qty] = 0;
+ qty_min_class[qty] = class;
+ qty_alternate_class[qty] = NO_REGS;
+ qty_n_refs[qty] = 1;
+}
+
+/* Main entry point of this file. */
+
+void
+local_alloc ()
+{
+ register int b, i;
+ int max_qty;
+
+ /* Leaf functions and non-leaf functions have different needs.
+ If defined, let the machine say what kind of ordering we
+ should use. */
+#ifdef ORDER_REGS_FOR_LOCAL_ALLOC
+ ORDER_REGS_FOR_LOCAL_ALLOC;
+#endif
+
+ /* Promote REG_EQUAL notes to REG_EQUIV notes and adjust status of affected
+ registers. */
+ update_equiv_regs ();
+
+ /* This sets the maximum number of quantities we can have. Quantity
+ numbers start at zero and we can have one for each pseudo plus the
+ number of SCRATCHes in the largest block, in the worst case. */
+ max_qty = (max_regno - FIRST_PSEUDO_REGISTER) + max_scratch;
+
+ /* Allocate vectors of temporary data.
+ See the declarations of these variables, above,
+ for what they mean. */
+
+ /* There can be up to MAX_SCRATCH * N_BASIC_BLOCKS SCRATCHes to allocate.
+ Instead of allocating this much memory from now until the end of
+ reload, only allocate space for MAX_QTY SCRATCHes. If there are more
+ reload will allocate them. */
+
+ scratch_list_length = max_qty;
+ scratch_list = (rtx *) xmalloc (scratch_list_length * sizeof (rtx));
+ bzero ((char *) scratch_list, scratch_list_length * sizeof (rtx));
+ scratch_block = (int *) xmalloc (scratch_list_length * sizeof (int));
+ bzero ((char *) scratch_block, scratch_list_length * sizeof (int));
+ scratch_index = 0;
+
+ qty_phys_reg = (short *) alloca (max_qty * sizeof (short));
+ qty_phys_copy_sugg
+ = (HARD_REG_SET *) alloca (max_qty * sizeof (HARD_REG_SET));
+ qty_phys_num_copy_sugg = (short *) alloca (max_qty * sizeof (short));
+ qty_phys_sugg = (HARD_REG_SET *) alloca (max_qty * sizeof (HARD_REG_SET));
+ qty_phys_num_sugg = (short *) alloca (max_qty * sizeof (short));
+ qty_birth = (int *) alloca (max_qty * sizeof (int));
+ qty_death = (int *) alloca (max_qty * sizeof (int));
+ qty_scratch_rtx = (rtx *) alloca (max_qty * sizeof (rtx));
+ qty_first_reg = (int *) alloca (max_qty * sizeof (int));
+ qty_size = (int *) alloca (max_qty * sizeof (int));
+ qty_mode
+ = (enum machine_mode *) alloca (max_qty * sizeof (enum machine_mode));
+ qty_n_calls_crossed = (int *) alloca (max_qty * sizeof (int));
+ qty_min_class
+ = (enum reg_class *) alloca (max_qty * sizeof (enum reg_class));
+ qty_alternate_class
+ = (enum reg_class *) alloca (max_qty * sizeof (enum reg_class));
+ qty_n_refs = (int *) alloca (max_qty * sizeof (int));
+
+ reg_qty = (int *) alloca (max_regno * sizeof (int));
+ reg_offset = (char *) alloca (max_regno * sizeof (char));
+ reg_next_in_qty = (int *) alloca (max_regno * sizeof (int));
+
+ reg_renumber = (short *) oballoc (max_regno * sizeof (short));
+ for (i = 0; i < max_regno; i++)
+ reg_renumber[i] = -1;
+
+ /* Determine which pseudo-registers can be allocated by local-alloc.
+ In general, these are the registers used only in a single block and
+ which only die once. However, if a register's preferred class has only
+ a few entries, don't allocate this register here unless it is preferred
+ or nothing since retry_global_alloc won't be able to move it to
+ GENERAL_REGS if a reload register of this class is needed.
+
+ We need not be concerned with which block actually uses the register
+ since we will never see it outside that block. */
+
+ for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+ {
+ if (reg_basic_block[i] >= 0 && reg_n_deaths[i] == 1
+ && (reg_alternate_class (i) == NO_REGS
+ || ! CLASS_LIKELY_SPILLED_P (reg_preferred_class (i))))
+ reg_qty[i] = -2;
+ else
+ reg_qty[i] = -1;
+ }
+
+ /* Force loop below to initialize entire quantity array. */
+ next_qty = max_qty;
+
+ /* Allocate each block's local registers, block by block. */
+
+ for (b = 0; b < n_basic_blocks; b++)
+ {
+ /* NEXT_QTY indicates which elements of the `qty_...'
+ vectors might need to be initialized because they were used
+ for the previous block; it is set to the entire array before
+ block 0. Initialize those, with explicit loop if there are few,
+ else with bzero and bcopy. Do not initialize vectors that are
+ explicit set by `alloc_qty'. */
+
+ if (next_qty < 6)
+ {
+ for (i = 0; i < next_qty; i++)
+ {
+ qty_scratch_rtx[i] = 0;
+ CLEAR_HARD_REG_SET (qty_phys_copy_sugg[i]);
+ qty_phys_num_copy_sugg[i] = 0;
+ CLEAR_HARD_REG_SET (qty_phys_sugg[i]);
+ qty_phys_num_sugg[i] = 0;
+ }
+ }
+ else
+ {
+#define CLEAR(vector) \
+ bzero ((char *) (vector), (sizeof (*(vector))) * next_qty);
+
+ CLEAR (qty_scratch_rtx);
+ CLEAR (qty_phys_copy_sugg);
+ CLEAR (qty_phys_num_copy_sugg);
+ CLEAR (qty_phys_sugg);
+ CLEAR (qty_phys_num_sugg);
+ }
+
+ next_qty = 0;
+
+ block_alloc (b);
+#ifdef USE_C_ALLOCA
+ alloca (0);
+#endif
+ }
+}
+
+/* Depth of loops we are in while in update_equiv_regs. */
+static int loop_depth;
+
+/* Used for communication between the following two functions: contains
+ a MEM that we wish to ensure remains unchanged. */
+static rtx equiv_mem;
+
+/* Set nonzero if EQUIV_MEM is modified. */
+static int equiv_mem_modified;
+
+/* If EQUIV_MEM is modified by modifying DEST, indicate that it is modified.
+ Called via note_stores. */
+
+static void
+validate_equiv_mem_from_store (dest, set)
+ rtx dest;
+ rtx set;
+{
+ if ((GET_CODE (dest) == REG
+ && reg_overlap_mentioned_p (dest, equiv_mem))
+ || (GET_CODE (dest) == MEM
+ && true_dependence (dest, equiv_mem)))
+ equiv_mem_modified = 1;
+}
+
+/* Verify that no store between START and the death of REG invalidates
+ MEMREF. MEMREF is invalidated by modifying a register used in MEMREF,
+ by storing into an overlapping memory location, or with a non-const
+ CALL_INSN.
+
+ Return 1 if MEMREF remains valid. */
+
+static int
+validate_equiv_mem (start, reg, memref)
+ rtx start;
+ rtx reg;
+ rtx memref;
+{
+ rtx insn;
+ rtx note;
+
+ equiv_mem = memref;
+ equiv_mem_modified = 0;
+
+ /* If the memory reference has side effects or is volatile, it isn't a
+ valid equivalence. */
+ if (side_effects_p (memref))
+ return 0;
+
+ for (insn = start; insn && ! equiv_mem_modified; insn = NEXT_INSN (insn))
+ {
+ if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+ continue;
+
+ if (find_reg_note (insn, REG_DEAD, reg))
+ return 1;
+
+ if (GET_CODE (insn) == CALL_INSN && ! RTX_UNCHANGING_P (memref)
+ && ! CONST_CALL_P (insn))
+ return 0;
+
+ note_stores (PATTERN (insn), validate_equiv_mem_from_store);
+
+ /* If a register mentioned in MEMREF is modified via an
+ auto-increment, we lose the equivalence. Do the same if one
+ dies; although we could extend the life, it doesn't seem worth
+ the trouble. */
+
+ for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
+ if ((REG_NOTE_KIND (note) == REG_INC
+ || REG_NOTE_KIND (note) == REG_DEAD)
+ && GET_CODE (XEXP (note, 0)) == REG
+ && reg_overlap_mentioned_p (XEXP (note, 0), memref))
+ return 0;
+ }
+
+ return 0;
+}
+
+/* TRUE if X references a memory location that would be affected by a store
+ to MEMREF. */
+
+static int
+memref_referenced_p (memref, x)
+ rtx x;
+ rtx memref;
+{
+ int i, j;
+ char *fmt;
+ enum rtx_code code = GET_CODE (x);
+
+ switch (code)
+ {
+ case REG:
+ case CONST_INT:
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ case CONST_DOUBLE:
+ case PC:
+ case CC0:
+ case HIGH:
+ case LO_SUM:
+ return 0;
+
+ case MEM:
+ if (true_dependence (memref, x))
+ return 1;
+ break;
+
+ case SET:
+ /* If we are setting a MEM, it doesn't count (its address does), but any
+ other SET_DEST that has a MEM in it is referencing the MEM. */
+ if (GET_CODE (SET_DEST (x)) == MEM)
+ {
+ if (memref_referenced_p (memref, XEXP (SET_DEST (x), 0)))
+ return 1;
+ }
+ else if (memref_referenced_p (memref, SET_DEST (x)))
+ return 1;
+
+ return memref_referenced_p (memref, SET_SRC (x));
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ switch (fmt[i])
+ {
+ case 'e':
+ if (memref_referenced_p (memref, XEXP (x, i)))
+ return 1;
+ break;
+ case 'E':
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (memref_referenced_p (memref, XVECEXP (x, i, j)))
+ return 1;
+ break;
+ }
+
+ return 0;
+}
+
+/* TRUE if some insn in the range (START, END] references a memory location
+ that would be affected by a store to MEMREF. */
+
+static int
+memref_used_between_p (memref, start, end)
+ rtx memref;
+ rtx start;
+ rtx end;
+{
+ rtx insn;
+
+ for (insn = NEXT_INSN (start); insn != NEXT_INSN (end);
+ insn = NEXT_INSN (insn))
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && memref_referenced_p (memref, PATTERN (insn)))
+ return 1;
+
+ return 0;
+}
+
+/* INSN is a copy from SRC to DEST, both registers, and SRC does not die
+ in INSN.
+
+ Search forward to see if SRC dies before either it or DEST is modified,
+ but don't scan past the end of a basic block. If so, we can replace SRC
+ with DEST and let SRC die in INSN.
+
+ This will reduce the number of registers live in that range and may enable
+ DEST to be tied to SRC, thus often saving one register in addition to a
+ register-register copy. */
+
+static void
+optimize_reg_copy_1 (insn, dest, src)
+ rtx insn;
+ rtx dest;
+ rtx src;
+{
+ rtx p, q;
+ rtx note;
+ rtx dest_death = 0;
+ int sregno = REGNO (src);
+ int dregno = REGNO (dest);
+
+ if (sregno == dregno
+#ifdef SMALL_REGISTER_CLASSES
+ /* We don't want to mess with hard regs if register classes are small. */
+ || sregno < FIRST_PSEUDO_REGISTER || dregno < FIRST_PSEUDO_REGISTER
+#endif
+ /* We don't see all updates to SP if they are in an auto-inc memory
+ reference, so we must disallow this optimization on them. */
+ || sregno == STACK_POINTER_REGNUM || dregno == STACK_POINTER_REGNUM)
+ return;
+
+ for (p = NEXT_INSN (insn); p; p = NEXT_INSN (p))
+ {
+ if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN
+ || (GET_CODE (p) == NOTE
+ && (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG
+ || NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)))
+ break;
+
+ if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+ continue;
+
+ if (reg_set_p (src, p) || reg_set_p (dest, p)
+ /* Don't change a USE of a register. */
+ || (GET_CODE (PATTERN (p)) == USE
+ && reg_overlap_mentioned_p (src, XEXP (PATTERN (p), 0))))
+ break;
+
+ /* See if all of SRC dies in P. This test is slightly more
+ conservative than it needs to be. */
+ if ((note = find_regno_note (p, REG_DEAD, sregno)) != 0
+ && GET_MODE (XEXP (note, 0)) == GET_MODE (src))
+ {
+ int failed = 0;
+ int length = 0;
+ int d_length = 0;
+ int n_calls = 0;
+ int d_n_calls = 0;
+
+ /* We can do the optimization. Scan forward from INSN again,
+ replacing regs as we go. Set FAILED if a replacement can't
+ be done. In that case, we can't move the death note for SRC.
+ This should be rare. */
+
+ /* Set to stop at next insn. */
+ for (q = next_real_insn (insn);
+ q != next_real_insn (p);
+ q = next_real_insn (q))
+ {
+ if (reg_overlap_mentioned_p (src, PATTERN (q)))
+ {
+ /* If SRC is a hard register, we might miss some
+ overlapping registers with validate_replace_rtx,
+ so we would have to undo it. We can't if DEST is
+ present in the insn, so fail in that combination
+ of cases. */
+ if (sregno < FIRST_PSEUDO_REGISTER
+ && reg_mentioned_p (dest, PATTERN (q)))
+ failed = 1;
+
+ /* Replace all uses and make sure that the register
+ isn't still present. */
+ else if (validate_replace_rtx (src, dest, q)
+ && (sregno >= FIRST_PSEUDO_REGISTER
+ || ! reg_overlap_mentioned_p (src,
+ PATTERN (q))))
+ {
+ /* We assume that a register is used exactly once per
+ insn in the updates below. If this is not correct,
+ no great harm is done. */
+ if (sregno >= FIRST_PSEUDO_REGISTER)
+ reg_n_refs[sregno] -= loop_depth;
+ if (dregno >= FIRST_PSEUDO_REGISTER)
+ reg_n_refs[dregno] += loop_depth;
+ }
+ else
+ {
+ validate_replace_rtx (dest, src, q);
+ failed = 1;
+ }
+ }
+
+ /* Count the insns and CALL_INSNs passed. If we passed the
+ death note of DEST, show increased live length. */
+ length++;
+ if (dest_death)
+ d_length++;
+
+ /* If the insn in which SRC dies is a CALL_INSN, don't count it
+ as a call that has been crossed. Otherwise, count it. */
+ if (q != p && GET_CODE (q) == CALL_INSN)
+ {
+ n_calls++;
+ if (dest_death)
+ d_n_calls++;
+ }
+
+ /* If DEST dies here, remove the death note and save it for
+ later. Make sure ALL of DEST dies here; again, this is
+ overly conservative. */
+ if (dest_death == 0
+ && (dest_death = find_regno_note (q, REG_DEAD, dregno)) != 0
+ && GET_MODE (XEXP (dest_death, 0)) == GET_MODE (dest))
+ remove_note (q, dest_death);
+ }
+
+ if (! failed)
+ {
+ if (sregno >= FIRST_PSEUDO_REGISTER)
+ {
+ reg_live_length[sregno] -= length;
+ /* reg_live_length is only an approximation after combine
+ if sched is not run, so make sure that we still have
+ a reasonable value. */
+ if (reg_live_length[sregno] < 2)
+ reg_live_length[sregno] = 2;
+ reg_n_calls_crossed[sregno] -= n_calls;
+ }
+
+ if (dregno >= FIRST_PSEUDO_REGISTER)
+ {
+ reg_live_length[dregno] += d_length;
+ reg_n_calls_crossed[dregno] += d_n_calls;
+ }
+
+ /* Move death note of SRC from P to INSN. */
+ remove_note (p, note);
+ XEXP (note, 1) = REG_NOTES (insn);
+ REG_NOTES (insn) = note;
+ }
+
+ /* Put death note of DEST on P if we saw it die. */
+ if (dest_death)
+ {
+ XEXP (dest_death, 1) = REG_NOTES (p);
+ REG_NOTES (p) = dest_death;
+ }
+
+ return;
+ }
+
+ /* If SRC is a hard register which is set or killed in some other
+ way, we can't do this optimization. */
+ else if (sregno < FIRST_PSEUDO_REGISTER
+ && dead_or_set_p (p, src))
+ break;
+ }
+}
+
+/* INSN is a copy of SRC to DEST, in which SRC dies. See if we now have
+ a sequence of insns that modify DEST followed by an insn that sets
+ SRC to DEST in which DEST dies, with no prior modification of DEST.
+ (There is no need to check if the insns in between actually modify
+ DEST. We should not have cases where DEST is not modified, but
+ the optimization is safe if no such modification is detected.)
+ In that case, we can replace all uses of DEST, starting with INSN and
+ ending with the set of SRC to DEST, with SRC. We do not do this
+ optimization if a CALL_INSN is crossed unless SRC already crosses a
+ call.
+
+ It is assumed that DEST and SRC are pseudos; it is too complicated to do
+ this for hard registers since the substitutions we may make might fail. */
+
+static void
+optimize_reg_copy_2 (insn, dest, src)
+ rtx insn;
+ rtx dest;
+ rtx src;
+{
+ rtx p, q;
+ rtx set;
+ int sregno = REGNO (src);
+ int dregno = REGNO (dest);
+
+ for (p = NEXT_INSN (insn); p; p = NEXT_INSN (p))
+ {
+ if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN
+ || (GET_CODE (p) == NOTE
+ && (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG
+ || NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)))
+ break;
+
+ if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+ continue;
+
+ set = single_set (p);
+ if (set && SET_SRC (set) == dest && SET_DEST (set) == src
+ && find_reg_note (p, REG_DEAD, dest))
+ {
+ /* We can do the optimization. Scan forward from INSN again,
+ replacing regs as we go. */
+
+ /* Set to stop at next insn. */
+ for (q = insn; q != NEXT_INSN (p); q = NEXT_INSN (q))
+ if (GET_RTX_CLASS (GET_CODE (q)) == 'i')
+ {
+ if (reg_mentioned_p (dest, PATTERN (q)))
+ {
+ PATTERN (q) = replace_rtx (PATTERN (q), dest, src);
+
+ /* We assume that a register is used exactly once per
+ insn in the updates below. If this is not correct,
+ no great harm is done. */
+ reg_n_refs[dregno] -= loop_depth;
+ reg_n_refs[sregno] += loop_depth;
+ }
+
+
+ if (GET_CODE (q) == CALL_INSN)
+ {
+ reg_n_calls_crossed[dregno]--;
+ reg_n_calls_crossed[sregno]++;
+ }
+ }
+
+ remove_note (p, find_reg_note (p, REG_DEAD, dest));
+ reg_n_deaths[dregno]--;
+ remove_note (insn, find_reg_note (insn, REG_DEAD, src));
+ reg_n_deaths[sregno]--;
+ return;
+ }
+
+ if (reg_set_p (src, p)
+ || (GET_CODE (p) == CALL_INSN && reg_n_calls_crossed[sregno] == 0))
+ break;
+ }
+}
+
+/* Find registers that are equivalent to a single value throughout the
+ compilation (either because they can be referenced in memory or are set once
+ from a single constant). Lower their priority for a register.
+
+ If such a register is only referenced once, try substituting its value
+ into the using insn. If it succeeds, we can eliminate the register
+ completely. */
+
+static void
+update_equiv_regs ()
+{
+ rtx *reg_equiv_init_insn = (rtx *) alloca (max_regno * sizeof (rtx *));
+ rtx *reg_equiv_replacement = (rtx *) alloca (max_regno * sizeof (rtx *));
+ rtx insn;
+
+ bzero ((char *) reg_equiv_init_insn, max_regno * sizeof (rtx *));
+ bzero ((char *) reg_equiv_replacement, max_regno * sizeof (rtx *));
+
+ init_alias_analysis ();
+
+ loop_depth = 1;
+
+ /* Scan the insns and find which registers have equivalences. Do this
+ in a separate scan of the insns because (due to -fcse-follow-jumps)
+ a register can be set below its use. */
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ rtx note;
+ rtx set = single_set (insn);
+ rtx dest;
+ int regno;
+
+ if (GET_CODE (insn) == NOTE)
+ {
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
+ loop_depth++;
+ else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
+ loop_depth--;
+ }
+
+ /* If this insn contains more (or less) than a single SET, ignore it. */
+ if (set == 0)
+ continue;
+
+ dest = SET_DEST (set);
+
+ /* If this sets a MEM to the contents of a REG that is only used
+ in a single basic block, see if the register is always equivalent
+ to that memory location and if moving the store from INSN to the
+ insn that set REG is safe. If so, put a REG_EQUIV note on the
+ initializing insn. */
+
+ if (GET_CODE (dest) == MEM && GET_CODE (SET_SRC (set)) == REG
+ && (regno = REGNO (SET_SRC (set))) >= FIRST_PSEUDO_REGISTER
+ && reg_basic_block[regno] >= 0
+ && reg_equiv_init_insn[regno] != 0
+ && validate_equiv_mem (reg_equiv_init_insn[regno], SET_SRC (set),
+ dest)
+ && ! memref_used_between_p (SET_DEST (set),
+ reg_equiv_init_insn[regno], insn))
+ REG_NOTES (reg_equiv_init_insn[regno])
+ = gen_rtx (EXPR_LIST, REG_EQUIV, dest,
+ REG_NOTES (reg_equiv_init_insn[regno]));
+
+ /* If this is a register-register copy where SRC is not dead, see if we
+ can optimize it. */
+ if (flag_expensive_optimizations && GET_CODE (dest) == REG
+ && GET_CODE (SET_SRC (set)) == REG
+ && ! find_reg_note (insn, REG_DEAD, SET_SRC (set)))
+ optimize_reg_copy_1 (insn, dest, SET_SRC (set));
+
+ /* Similarly for a pseudo-pseudo copy when SRC is dead. */
+ else if (flag_expensive_optimizations && GET_CODE (dest) == REG
+ && REGNO (dest) >= FIRST_PSEUDO_REGISTER
+ && GET_CODE (SET_SRC (set)) == REG
+ && REGNO (SET_SRC (set)) >= FIRST_PSEUDO_REGISTER
+ && find_reg_note (insn, REG_DEAD, SET_SRC (set)))
+ optimize_reg_copy_2 (insn, dest, SET_SRC (set));
+
+ /* Otherwise, we only handle the case of a pseudo register being set
+ once. */
+ if (GET_CODE (dest) != REG
+ || (regno = REGNO (dest)) < FIRST_PSEUDO_REGISTER
+ || reg_n_sets[regno] != 1)
+ continue;
+
+ note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
+
+ /* Record this insn as initializing this register. */
+ reg_equiv_init_insn[regno] = insn;
+
+ /* If this register is known to be equal to a constant, record that
+ it is always equivalent to the constant. */
+ if (note && CONSTANT_P (XEXP (note, 0)))
+ PUT_MODE (note, (enum machine_mode) REG_EQUIV);
+
+ /* If this insn introduces a "constant" register, decrease the priority
+ of that register. Record this insn if the register is only used once
+ more and the equivalence value is the same as our source.
+
+ The latter condition is checked for two reasons: First, it is an
+ indication that it may be more efficient to actually emit the insn
+ as written (if no registers are available, reload will substitute
+ the equivalence). Secondly, it avoids problems with any registers
+ dying in this insn whose death notes would be missed.
+
+ If we don't have a REG_EQUIV note, see if this insn is loading
+ a register used only in one basic block from a MEM. If so, and the
+ MEM remains unchanged for the life of the register, add a REG_EQUIV
+ note. */
+
+ note = find_reg_note (insn, REG_EQUIV, NULL_RTX);
+
+ if (note == 0 && reg_basic_block[regno] >= 0
+ && GET_CODE (SET_SRC (set)) == MEM
+ && validate_equiv_mem (insn, dest, SET_SRC (set)))
+ REG_NOTES (insn) = note = gen_rtx (EXPR_LIST, REG_EQUIV, SET_SRC (set),
+ REG_NOTES (insn));
+
+ /* Don't mess with things live during setjmp. */
+ if (note && reg_live_length[regno] >= 0)
+ {
+ int regno = REGNO (dest);
+
+ /* Note that the statement below does not affect the priority
+ in local-alloc! */
+ reg_live_length[regno] *= 2;
+
+ /* If the register is referenced exactly twice, meaning it is set
+ once and used once, indicate that the reference may be replaced
+ by the equivalence we computed above. If the register is only
+ used in one basic block, this can't succeed or combine would
+ have done it.
+
+ It would be nice to use "loop_depth * 2" in the compare
+ below. Unfortunately, LOOP_DEPTH need not be constant within
+ a basic block so this would be too complicated.
+
+ This case normally occurs when a parameter is read from memory
+ and then used exactly once, not in a loop. */
+
+ if (reg_n_refs[regno] == 2
+ && reg_basic_block[regno] < 0
+ && rtx_equal_p (XEXP (note, 0), SET_SRC (set)))
+ reg_equiv_replacement[regno] = SET_SRC (set);
+ }
+ }
+
+ /* Now scan all regs killed in an insn to see if any of them are registers
+ only used that once. If so, see if we can replace the reference with
+ the equivalent from. If we can, delete the initializing reference
+ and this register will go away. */
+ for (insn = next_active_insn (get_insns ());
+ insn;
+ insn = next_active_insn (insn))
+ {
+ rtx link;
+
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_DEAD
+ /* Make sure this insn still refers to the register. */
+ && reg_mentioned_p (XEXP (link, 0), PATTERN (insn)))
+ {
+ int regno = REGNO (XEXP (link, 0));
+
+ if (reg_equiv_replacement[regno]
+ && validate_replace_rtx (regno_reg_rtx[regno],
+ reg_equiv_replacement[regno], insn))
+ {
+ rtx equiv_insn = reg_equiv_init_insn[regno];
+
+ remove_death (regno, insn);
+ reg_n_refs[regno] = 0;
+ PUT_CODE (equiv_insn, NOTE);
+ NOTE_LINE_NUMBER (equiv_insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (equiv_insn) = 0;
+ }
+ }
+ }
+}
+
+/* Allocate hard regs to the pseudo regs used only within block number B.
+ Only the pseudos that die but once can be handled. */
+
+static void
+block_alloc (b)
+ int b;
+{
+ register int i, q;
+ register rtx insn;
+ rtx note;
+ int insn_number = 0;
+ int insn_count = 0;
+ int max_uid = get_max_uid ();
+ int *qty_order;
+ int no_conflict_combined_regno = -1;
+ /* Counter to prevent allocating more SCRATCHes than can be stored
+ in SCRATCH_LIST. */
+ int scratches_allocated = scratch_index;
+
+ /* Count the instructions in the basic block. */
+
+ insn = basic_block_end[b];
+ while (1)
+ {
+ if (GET_CODE (insn) != NOTE)
+ if (++insn_count > max_uid)
+ abort ();
+ if (insn == basic_block_head[b])
+ break;
+ insn = PREV_INSN (insn);
+ }
+
+ /* +2 to leave room for a post_mark_life at the last insn and for
+ the birth of a CLOBBER in the first insn. */
+ regs_live_at = (HARD_REG_SET *) alloca ((2 * insn_count + 2)
+ * sizeof (HARD_REG_SET));
+ bzero ((char *) regs_live_at, (2 * insn_count + 2) * sizeof (HARD_REG_SET));
+
+ /* Initialize table of hardware registers currently live. */
+
+#ifdef HARD_REG_SET
+ regs_live = *basic_block_live_at_start[b];
+#else
+ COPY_HARD_REG_SET (regs_live, basic_block_live_at_start[b]);
+#endif
+
+ /* This loop scans the instructions of the basic block
+ and assigns quantities to registers.
+ It computes which registers to tie. */
+
+ insn = basic_block_head[b];
+ while (1)
+ {
+ register rtx body = PATTERN (insn);
+
+ if (GET_CODE (insn) != NOTE)
+ insn_number++;
+
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ register rtx link, set;
+ register int win = 0;
+ register rtx r0, r1;
+ int combined_regno = -1;
+ int i;
+ int insn_code_number = recog_memoized (insn);
+
+ this_insn_number = insn_number;
+ this_insn = insn;
+
+ if (insn_code_number >= 0)
+ insn_extract (insn);
+ which_alternative = -1;
+
+ /* Is this insn suitable for tying two registers?
+ If so, try doing that.
+ Suitable insns are those with at least two operands and where
+ operand 0 is an output that is a register that is not
+ earlyclobber.
+
+ We can tie operand 0 with some operand that dies in this insn.
+ First look for operands that are required to be in the same
+ register as operand 0. If we find such, only try tying that
+ operand or one that can be put into that operand if the
+ operation is commutative. If we don't find an operand
+ that is required to be in the same register as operand 0,
+ we can tie with any operand.
+
+ Subregs in place of regs are also ok.
+
+ If tying is done, WIN is set nonzero. */
+
+ if (insn_code_number >= 0
+#ifdef REGISTER_CONSTRAINTS
+ && insn_n_operands[insn_code_number] > 1
+ && insn_operand_constraint[insn_code_number][0][0] == '='
+ && insn_operand_constraint[insn_code_number][0][1] != '&'
+#else
+ && GET_CODE (PATTERN (insn)) == SET
+ && rtx_equal_p (SET_DEST (PATTERN (insn)), recog_operand[0])
+#endif
+ )
+ {
+#ifdef REGISTER_CONSTRAINTS
+ /* If non-negative, is an operand that must match operand 0. */
+ int must_match_0 = -1;
+ /* Counts number of alternatives that require a match with
+ operand 0. */
+ int n_matching_alts = 0;
+
+ for (i = 1; i < insn_n_operands[insn_code_number]; i++)
+ {
+ char *p = insn_operand_constraint[insn_code_number][i];
+ int this_match = (requires_inout (p));
+
+ n_matching_alts += this_match;
+ if (this_match == insn_n_alternatives[insn_code_number])
+ must_match_0 = i;
+ }
+#endif
+
+ r0 = recog_operand[0];
+ for (i = 1; i < insn_n_operands[insn_code_number]; i++)
+ {
+#ifdef REGISTER_CONSTRAINTS
+ /* Skip this operand if we found an operand that
+ must match operand 0 and this operand isn't it
+ and can't be made to be it by commutativity. */
+
+ if (must_match_0 >= 0 && i != must_match_0
+ && ! (i == must_match_0 + 1
+ && insn_operand_constraint[insn_code_number][i-1][0] == '%')
+ && ! (i == must_match_0 - 1
+ && insn_operand_constraint[insn_code_number][i][0] == '%'))
+ continue;
+
+ /* Likewise if each alternative has some operand that
+ must match operand zero. In that case, skip any
+ operand that doesn't list operand 0 since we know that
+ the operand always conflicts with operand 0. We
+ ignore commutatity in this case to keep things simple. */
+ if (n_matching_alts == insn_n_alternatives[insn_code_number]
+ && (0 == requires_inout
+ (insn_operand_constraint[insn_code_number][i])))
+ continue;
+#endif
+
+ r1 = recog_operand[i];
+
+ /* If the operand is an address, find a register in it.
+ There may be more than one register, but we only try one
+ of them. */
+ if (
+#ifdef REGISTER_CONSTRAINTS
+ insn_operand_constraint[insn_code_number][i][0] == 'p'
+#else
+ insn_operand_address_p[insn_code_number][i]
+#endif
+ )
+ while (GET_CODE (r1) == PLUS || GET_CODE (r1) == MULT)
+ r1 = XEXP (r1, 0);
+
+ if (GET_CODE (r0) == REG || GET_CODE (r0) == SUBREG)
+ {
+ /* We have two priorities for hard register preferences.
+ If we have a move insn or an insn whose first input
+ can only be in the same register as the output, give
+ priority to an equivalence found from that insn. */
+ int may_save_copy
+ = ((SET_DEST (body) == r0 && SET_SRC (body) == r1)
+#ifdef REGISTER_CONSTRAINTS
+ || (r1 == recog_operand[i] && must_match_0 >= 0)
+#endif
+ );
+
+ if (GET_CODE (r1) == REG || GET_CODE (r1) == SUBREG)
+ win = combine_regs (r1, r0, may_save_copy,
+ insn_number, insn, 0);
+ }
+ }
+ }
+
+ /* Recognize an insn sequence with an ultimate result
+ which can safely overlap one of the inputs.
+ The sequence begins with a CLOBBER of its result,
+ and ends with an insn that copies the result to itself
+ and has a REG_EQUAL note for an equivalent formula.
+ That note indicates what the inputs are.
+ The result and the input can overlap if each insn in
+ the sequence either doesn't mention the input
+ or has a REG_NO_CONFLICT note to inhibit the conflict.
+
+ We do the combining test at the CLOBBER so that the
+ destination register won't have had a quantity number
+ assigned, since that would prevent combining. */
+
+ if (GET_CODE (PATTERN (insn)) == CLOBBER
+ && (r0 = XEXP (PATTERN (insn), 0),
+ GET_CODE (r0) == REG)
+ && (link = find_reg_note (insn, REG_LIBCALL, NULL_RTX)) != 0
+ && XEXP (link, 0) != 0
+ && GET_CODE (XEXP (link, 0)) == INSN
+ && (set = single_set (XEXP (link, 0))) != 0
+ && SET_DEST (set) == r0 && SET_SRC (set) == r0
+ && (note = find_reg_note (XEXP (link, 0), REG_EQUAL,
+ NULL_RTX)) != 0)
+ {
+ if (r1 = XEXP (note, 0), GET_CODE (r1) == REG
+ /* Check that we have such a sequence. */
+ && no_conflict_p (insn, r0, r1))
+ win = combine_regs (r1, r0, 1, insn_number, insn, 1);
+ else if (GET_RTX_FORMAT (GET_CODE (XEXP (note, 0)))[0] == 'e'
+ && (r1 = XEXP (XEXP (note, 0), 0),
+ GET_CODE (r1) == REG || GET_CODE (r1) == SUBREG)
+ && no_conflict_p (insn, r0, r1))
+ win = combine_regs (r1, r0, 0, insn_number, insn, 1);
+
+ /* Here we care if the operation to be computed is
+ commutative. */
+ else if ((GET_CODE (XEXP (note, 0)) == EQ
+ || GET_CODE (XEXP (note, 0)) == NE
+ || GET_RTX_CLASS (GET_CODE (XEXP (note, 0))) == 'c')
+ && (r1 = XEXP (XEXP (note, 0), 1),
+ (GET_CODE (r1) == REG || GET_CODE (r1) == SUBREG))
+ && no_conflict_p (insn, r0, r1))
+ win = combine_regs (r1, r0, 0, insn_number, insn, 1);
+
+ /* If we did combine something, show the register number
+ in question so that we know to ignore its death. */
+ if (win)
+ no_conflict_combined_regno = REGNO (r1);
+ }
+
+ /* If registers were just tied, set COMBINED_REGNO
+ to the number of the register used in this insn
+ that was tied to the register set in this insn.
+ This register's qty should not be "killed". */
+
+ if (win)
+ {
+ while (GET_CODE (r1) == SUBREG)
+ r1 = SUBREG_REG (r1);
+ combined_regno = REGNO (r1);
+ }
+
+ /* Mark the death of everything that dies in this instruction,
+ except for anything that was just combined. */
+
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_DEAD
+ && GET_CODE (XEXP (link, 0)) == REG
+ && combined_regno != REGNO (XEXP (link, 0))
+ && (no_conflict_combined_regno != REGNO (XEXP (link, 0))
+ || ! find_reg_note (insn, REG_NO_CONFLICT, XEXP (link, 0))))
+ wipe_dead_reg (XEXP (link, 0), 0);
+
+ /* Allocate qty numbers for all registers local to this block
+ that are born (set) in this instruction.
+ A pseudo that already has a qty is not changed. */
+
+ note_stores (PATTERN (insn), reg_is_set);
+
+ /* If anything is set in this insn and then unused, mark it as dying
+ after this insn, so it will conflict with our outputs. This
+ can't match with something that combined, and it doesn't matter
+ if it did. Do this after the calls to reg_is_set since these
+ die after, not during, the current insn. */
+
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_UNUSED
+ && GET_CODE (XEXP (link, 0)) == REG)
+ wipe_dead_reg (XEXP (link, 0), 1);
+
+ /* Allocate quantities for any SCRATCH operands of this insn. */
+
+ if (insn_code_number >= 0)
+ for (i = 0; i < insn_n_operands[insn_code_number]; i++)
+ if (GET_CODE (recog_operand[i]) == SCRATCH
+ && scratches_allocated++ < scratch_list_length)
+ alloc_qty_for_scratch (recog_operand[i], i, insn,
+ insn_code_number, insn_number);
+
+ /* If this is an insn that has a REG_RETVAL note pointing at a
+ CLOBBER insn, we have reached the end of a REG_NO_CONFLICT
+ block, so clear any register number that combined within it. */
+ if ((note = find_reg_note (insn, REG_RETVAL, NULL_RTX)) != 0
+ && GET_CODE (XEXP (note, 0)) == INSN
+ && GET_CODE (PATTERN (XEXP (note, 0))) == CLOBBER)
+ no_conflict_combined_regno = -1;
+ }
+
+ /* Set the registers live after INSN_NUMBER. Note that we never
+ record the registers live before the block's first insn, since no
+ pseudos we care about are live before that insn. */
+
+ IOR_HARD_REG_SET (regs_live_at[2 * insn_number], regs_live);
+ IOR_HARD_REG_SET (regs_live_at[2 * insn_number + 1], regs_live);
+
+ if (insn == basic_block_end[b])
+ break;
+
+ insn = NEXT_INSN (insn);
+ }
+
+ /* Now every register that is local to this basic block
+ should have been given a quantity, or else -1 meaning ignore it.
+ Every quantity should have a known birth and death.
+
+ Order the qtys so we assign them registers in order of the
+ number of suggested registers they need so we allocate those with
+ the most restrictive needs first. */
+
+ qty_order = (int *) alloca (next_qty * sizeof (int));
+ for (i = 0; i < next_qty; i++)
+ qty_order[i] = i;
+
+#define EXCHANGE(I1, I2) \
+ { i = qty_order[I1]; qty_order[I1] = qty_order[I2]; qty_order[I2] = i; }
+
+ switch (next_qty)
+ {
+ case 3:
+ /* Make qty_order[2] be the one to allocate last. */
+ if (qty_sugg_compare (0, 1) > 0)
+ EXCHANGE (0, 1);
+ if (qty_sugg_compare (1, 2) > 0)
+ EXCHANGE (2, 1);
+
+ /* ... Fall through ... */
+ case 2:
+ /* Put the best one to allocate in qty_order[0]. */
+ if (qty_sugg_compare (0, 1) > 0)
+ EXCHANGE (0, 1);
+
+ /* ... Fall through ... */
+
+ case 1:
+ case 0:
+ /* Nothing to do here. */
+ break;
+
+ default:
+ qsort (qty_order, next_qty, sizeof (int), qty_sugg_compare_1);
+ }
+
+ /* Try to put each quantity in a suggested physical register, if it has one.
+ This may cause registers to be allocated that otherwise wouldn't be, but
+ this seems acceptable in local allocation (unlike global allocation). */
+ for (i = 0; i < next_qty; i++)
+ {
+ q = qty_order[i];
+ if (qty_phys_num_sugg[q] != 0 || qty_phys_num_copy_sugg[q] != 0)
+ qty_phys_reg[q] = find_free_reg (qty_min_class[q], qty_mode[q], q,
+ 0, 1, qty_birth[q], qty_death[q]);
+ else
+ qty_phys_reg[q] = -1;
+ }
+
+ /* Order the qtys so we assign them registers in order of
+ decreasing length of life. Normally call qsort, but if we
+ have only a very small number of quantities, sort them ourselves. */
+
+ for (i = 0; i < next_qty; i++)
+ qty_order[i] = i;
+
+#define EXCHANGE(I1, I2) \
+ { i = qty_order[I1]; qty_order[I1] = qty_order[I2]; qty_order[I2] = i; }
+
+ switch (next_qty)
+ {
+ case 3:
+ /* Make qty_order[2] be the one to allocate last. */
+ if (qty_compare (0, 1) > 0)
+ EXCHANGE (0, 1);
+ if (qty_compare (1, 2) > 0)
+ EXCHANGE (2, 1);
+
+ /* ... Fall through ... */
+ case 2:
+ /* Put the best one to allocate in qty_order[0]. */
+ if (qty_compare (0, 1) > 0)
+ EXCHANGE (0, 1);
+
+ /* ... Fall through ... */
+
+ case 1:
+ case 0:
+ /* Nothing to do here. */
+ break;
+
+ default:
+ qsort (qty_order, next_qty, sizeof (int), qty_compare_1);
+ }
+
+ /* Now for each qty that is not a hardware register,
+ look for a hardware register to put it in.
+ First try the register class that is cheapest for this qty,
+ if there is more than one class. */
+
+ for (i = 0; i < next_qty; i++)
+ {
+ q = qty_order[i];
+ if (qty_phys_reg[q] < 0)
+ {
+ if (N_REG_CLASSES > 1)
+ {
+ qty_phys_reg[q] = find_free_reg (qty_min_class[q],
+ qty_mode[q], q, 0, 0,
+ qty_birth[q], qty_death[q]);
+ if (qty_phys_reg[q] >= 0)
+ continue;
+ }
+
+ if (qty_alternate_class[q] != NO_REGS)
+ qty_phys_reg[q] = find_free_reg (qty_alternate_class[q],
+ qty_mode[q], q, 0, 0,
+ qty_birth[q], qty_death[q]);
+ }
+ }
+
+ /* Now propagate the register assignments
+ to the pseudo regs belonging to the qtys. */
+
+ for (q = 0; q < next_qty; q++)
+ if (qty_phys_reg[q] >= 0)
+ {
+ for (i = qty_first_reg[q]; i >= 0; i = reg_next_in_qty[i])
+ reg_renumber[i] = qty_phys_reg[q] + reg_offset[i];
+ if (qty_scratch_rtx[q])
+ {
+ if (GET_CODE (qty_scratch_rtx[q]) == REG)
+ abort ();
+ PUT_CODE (qty_scratch_rtx[q], REG);
+ REGNO (qty_scratch_rtx[q]) = qty_phys_reg[q];
+
+ scratch_block[scratch_index] = b;
+ scratch_list[scratch_index++] = qty_scratch_rtx[q];
+
+ /* Must clear the USED field, because it will have been set by
+ copy_rtx_if_shared, but the leaf_register code expects that
+ it is zero in all REG rtx. copy_rtx_if_shared does not set the
+ used bit for REGs, but does for SCRATCHes. */
+ qty_scratch_rtx[q]->used = 0;
+ }
+ }
+}
+
+/* Compare two quantities' priority for getting real registers.
+ We give shorter-lived quantities higher priority.
+ Quantities with more references are also preferred, as are quantities that
+ require multiple registers. This is the identical prioritization as
+ done by global-alloc.
+
+ We used to give preference to registers with *longer* lives, but using
+ the same algorithm in both local- and global-alloc can speed up execution
+ of some programs by as much as a factor of three! */
+
+static int
+qty_compare (q1, q2)
+ int q1, q2;
+{
+ /* Note that the quotient will never be bigger than
+ the value of floor_log2 times the maximum number of
+ times a register can occur in one insn (surely less than 100).
+ Multiplying this by 10000 can't overflow. */
+ register int pri1
+ = (((double) (floor_log2 (qty_n_refs[q1]) * qty_n_refs[q1] * qty_size[q1])
+ / (qty_death[q1] - qty_birth[q1]))
+ * 10000);
+ register int pri2
+ = (((double) (floor_log2 (qty_n_refs[q2]) * qty_n_refs[q2] * qty_size[q2])
+ / (qty_death[q2] - qty_birth[q2]))
+ * 10000);
+ return pri2 - pri1;
+}
+
+static int
+qty_compare_1 (q1, q2)
+ int *q1, *q2;
+{
+ register int tem;
+
+ /* Note that the quotient will never be bigger than
+ the value of floor_log2 times the maximum number of
+ times a register can occur in one insn (surely less than 100).
+ Multiplying this by 10000 can't overflow. */
+ register int pri1
+ = (((double) (floor_log2 (qty_n_refs[*q1]) * qty_n_refs[*q1]
+ * qty_size[*q1])
+ / (qty_death[*q1] - qty_birth[*q1]))
+ * 10000);
+ register int pri2
+ = (((double) (floor_log2 (qty_n_refs[*q2]) * qty_n_refs[*q2]
+ * qty_size[*q2])
+ / (qty_death[*q2] - qty_birth[*q2]))
+ * 10000);
+
+ tem = pri2 - pri1;
+ if (tem != 0) return tem;
+ /* If qtys are equally good, sort by qty number,
+ so that the results of qsort leave nothing to chance. */
+ return *q1 - *q2;
+}
+
+/* Compare two quantities' priority for getting real registers. This version
+ is called for quantities that have suggested hard registers. First priority
+ goes to quantities that have copy preferences, then to those that have
+ normal preferences. Within those groups, quantities with the lower
+ number of preferenes have the highest priority. Of those, we use the same
+ algorithm as above. */
+
+static int
+qty_sugg_compare (q1, q2)
+ int q1, q2;
+{
+ register int sugg1 = (qty_phys_num_copy_sugg[q1]
+ ? qty_phys_num_copy_sugg[q1]
+ : qty_phys_num_sugg[q1] * FIRST_PSEUDO_REGISTER);
+ register int sugg2 = (qty_phys_num_copy_sugg[q2]
+ ? qty_phys_num_copy_sugg[q2]
+ : qty_phys_num_sugg[q2] * FIRST_PSEUDO_REGISTER);
+ /* Note that the quotient will never be bigger than
+ the value of floor_log2 times the maximum number of
+ times a register can occur in one insn (surely less than 100).
+ Multiplying this by 10000 can't overflow. */
+ register int pri1
+ = (((double) (floor_log2 (qty_n_refs[q1]) * qty_n_refs[q1] * qty_size[q1])
+ / (qty_death[q1] - qty_birth[q1]))
+ * 10000);
+ register int pri2
+ = (((double) (floor_log2 (qty_n_refs[q2]) * qty_n_refs[q2] * qty_size[q2])
+ / (qty_death[q2] - qty_birth[q2]))
+ * 10000);
+
+ if (sugg1 != sugg2)
+ return sugg1 - sugg2;
+
+ return pri2 - pri1;
+}
+
+static int
+qty_sugg_compare_1 (q1, q2)
+ int *q1, *q2;
+{
+ register int sugg1 = (qty_phys_num_copy_sugg[*q1]
+ ? qty_phys_num_copy_sugg[*q1]
+ : qty_phys_num_sugg[*q1] * FIRST_PSEUDO_REGISTER);
+ register int sugg2 = (qty_phys_num_copy_sugg[*q2]
+ ? qty_phys_num_copy_sugg[*q2]
+ : qty_phys_num_sugg[*q2] * FIRST_PSEUDO_REGISTER);
+
+ /* Note that the quotient will never be bigger than
+ the value of floor_log2 times the maximum number of
+ times a register can occur in one insn (surely less than 100).
+ Multiplying this by 10000 can't overflow. */
+ register int pri1
+ = (((double) (floor_log2 (qty_n_refs[*q1]) * qty_n_refs[*q1]
+ * qty_size[*q1])
+ / (qty_death[*q1] - qty_birth[*q1]))
+ * 10000);
+ register int pri2
+ = (((double) (floor_log2 (qty_n_refs[*q2]) * qty_n_refs[*q2]
+ * qty_size[*q2])
+ / (qty_death[*q2] - qty_birth[*q2]))
+ * 10000);
+
+ if (sugg1 != sugg2)
+ return sugg1 - sugg2;
+
+ if (pri1 != pri2)
+ return pri2 - pri1;
+
+ /* If qtys are equally good, sort by qty number,
+ so that the results of qsort leave nothing to chance. */
+ return *q1 - *q2;
+}
+
+/* Attempt to combine the two registers (rtx's) USEDREG and SETREG.
+ Returns 1 if have done so, or 0 if cannot.
+
+ Combining registers means marking them as having the same quantity
+ and adjusting the offsets within the quantity if either of
+ them is a SUBREG).
+
+ We don't actually combine a hard reg with a pseudo; instead
+ we just record the hard reg as the suggestion for the pseudo's quantity.
+ If we really combined them, we could lose if the pseudo lives
+ across an insn that clobbers the hard reg (eg, movstr).
+
+ ALREADY_DEAD is non-zero if USEDREG is known to be dead even though
+ there is no REG_DEAD note on INSN. This occurs during the processing
+ of REG_NO_CONFLICT blocks.
+
+ MAY_SAVE_COPYCOPY is non-zero if this insn is simply copying USEDREG to
+ SETREG or if the input and output must share a register.
+ In that case, we record a hard reg suggestion in QTY_PHYS_COPY_SUGG.
+
+ There are elaborate checks for the validity of combining. */
+
+
+static int
+combine_regs (usedreg, setreg, may_save_copy, insn_number, insn, already_dead)
+ rtx usedreg, setreg;
+ int may_save_copy;
+ int insn_number;
+ rtx insn;
+ int already_dead;
+{
+ register int ureg, sreg;
+ register int offset = 0;
+ int usize, ssize;
+ register int sqty;
+
+ /* Determine the numbers and sizes of registers being used. If a subreg
+ is present that does not change the entire register, don't consider
+ this a copy insn. */
+
+ while (GET_CODE (usedreg) == SUBREG)
+ {
+ if (GET_MODE_SIZE (GET_MODE (SUBREG_REG (usedreg))) > UNITS_PER_WORD)
+ may_save_copy = 0;
+ offset += SUBREG_WORD (usedreg);
+ usedreg = SUBREG_REG (usedreg);
+ }
+ if (GET_CODE (usedreg) != REG)
+ return 0;
+ ureg = REGNO (usedreg);
+ usize = REG_SIZE (usedreg);
+
+ while (GET_CODE (setreg) == SUBREG)
+ {
+ if (GET_MODE_SIZE (GET_MODE (SUBREG_REG (setreg))) > UNITS_PER_WORD)
+ may_save_copy = 0;
+ offset -= SUBREG_WORD (setreg);
+ setreg = SUBREG_REG (setreg);
+ }
+ if (GET_CODE (setreg) != REG)
+ return 0;
+ sreg = REGNO (setreg);
+ ssize = REG_SIZE (setreg);
+
+ /* If UREG is a pseudo-register that hasn't already been assigned a
+ quantity number, it means that it is not local to this block or dies
+ more than once. In either event, we can't do anything with it. */
+ if ((ureg >= FIRST_PSEUDO_REGISTER && reg_qty[ureg] < 0)
+ /* Do not combine registers unless one fits within the other. */
+ || (offset > 0 && usize + offset > ssize)
+ || (offset < 0 && usize + offset < ssize)
+ /* Do not combine with a smaller already-assigned object
+ if that smaller object is already combined with something bigger. */
+ || (ssize > usize && ureg >= FIRST_PSEUDO_REGISTER
+ && usize < qty_size[reg_qty[ureg]])
+ /* Can't combine if SREG is not a register we can allocate. */
+ || (sreg >= FIRST_PSEUDO_REGISTER && reg_qty[sreg] == -1)
+ /* Don't combine with a pseudo mentioned in a REG_NO_CONFLICT note.
+ These have already been taken care of. This probably wouldn't
+ combine anyway, but don't take any chances. */
+ || (ureg >= FIRST_PSEUDO_REGISTER
+ && find_reg_note (insn, REG_NO_CONFLICT, usedreg))
+ /* Don't tie something to itself. In most cases it would make no
+ difference, but it would screw up if the reg being tied to itself
+ also dies in this insn. */
+ || ureg == sreg
+ /* Don't try to connect two different hardware registers. */
+ || (ureg < FIRST_PSEUDO_REGISTER && sreg < FIRST_PSEUDO_REGISTER)
+ /* Don't connect two different machine modes if they have different
+ implications as to which registers may be used. */
+ || !MODES_TIEABLE_P (GET_MODE (usedreg), GET_MODE (setreg)))
+ return 0;
+
+ /* Now, if UREG is a hard reg and SREG is a pseudo, record the hard reg in
+ qty_phys_sugg for the pseudo instead of tying them.
+
+ Return "failure" so that the lifespan of UREG is terminated here;
+ that way the two lifespans will be disjoint and nothing will prevent
+ the pseudo reg from being given this hard reg. */
+
+ if (ureg < FIRST_PSEUDO_REGISTER)
+ {
+ /* Allocate a quantity number so we have a place to put our
+ suggestions. */
+ if (reg_qty[sreg] == -2)
+ reg_is_born (setreg, 2 * insn_number);
+
+ if (reg_qty[sreg] >= 0)
+ {
+ if (may_save_copy
+ && ! TEST_HARD_REG_BIT (qty_phys_copy_sugg[reg_qty[sreg]], ureg))
+ {
+ SET_HARD_REG_BIT (qty_phys_copy_sugg[reg_qty[sreg]], ureg);
+ qty_phys_num_copy_sugg[reg_qty[sreg]]++;
+ }
+ else if (! TEST_HARD_REG_BIT (qty_phys_sugg[reg_qty[sreg]], ureg))
+ {
+ SET_HARD_REG_BIT (qty_phys_sugg[reg_qty[sreg]], ureg);
+ qty_phys_num_sugg[reg_qty[sreg]]++;
+ }
+ }
+ return 0;
+ }
+
+ /* Similarly for SREG a hard register and UREG a pseudo register. */
+
+ if (sreg < FIRST_PSEUDO_REGISTER)
+ {
+ if (may_save_copy
+ && ! TEST_HARD_REG_BIT (qty_phys_copy_sugg[reg_qty[ureg]], sreg))
+ {
+ SET_HARD_REG_BIT (qty_phys_copy_sugg[reg_qty[ureg]], sreg);
+ qty_phys_num_copy_sugg[reg_qty[ureg]]++;
+ }
+ else if (! TEST_HARD_REG_BIT (qty_phys_sugg[reg_qty[ureg]], sreg))
+ {
+ SET_HARD_REG_BIT (qty_phys_sugg[reg_qty[ureg]], sreg);
+ qty_phys_num_sugg[reg_qty[ureg]]++;
+ }
+ return 0;
+ }
+
+ /* At this point we know that SREG and UREG are both pseudos.
+ Do nothing if SREG already has a quantity or is a register that we
+ don't allocate. */
+ if (reg_qty[sreg] >= -1
+ /* If we are not going to let any regs live across calls,
+ don't tie a call-crossing reg to a non-call-crossing reg. */
+ || (current_function_has_nonlocal_label
+ && ((reg_n_calls_crossed[ureg] > 0)
+ != (reg_n_calls_crossed[sreg] > 0))))
+ return 0;
+
+ /* We don't already know about SREG, so tie it to UREG
+ if this is the last use of UREG, provided the classes they want
+ are compatible. */
+
+ if ((already_dead || find_regno_note (insn, REG_DEAD, ureg))
+ && reg_meets_class_p (sreg, qty_min_class[reg_qty[ureg]]))
+ {
+ /* Add SREG to UREG's quantity. */
+ sqty = reg_qty[ureg];
+ reg_qty[sreg] = sqty;
+ reg_offset[sreg] = reg_offset[ureg] + offset;
+ reg_next_in_qty[sreg] = qty_first_reg[sqty];
+ qty_first_reg[sqty] = sreg;
+
+ /* If SREG's reg class is smaller, set qty_min_class[SQTY]. */
+ update_qty_class (sqty, sreg);
+
+ /* Update info about quantity SQTY. */
+ qty_n_calls_crossed[sqty] += reg_n_calls_crossed[sreg];
+ qty_n_refs[sqty] += reg_n_refs[sreg];
+ if (usize < ssize)
+ {
+ register int i;
+
+ for (i = qty_first_reg[sqty]; i >= 0; i = reg_next_in_qty[i])
+ reg_offset[i] -= offset;
+
+ qty_size[sqty] = ssize;
+ qty_mode[sqty] = GET_MODE (setreg);
+ }
+ }
+ else
+ return 0;
+
+ return 1;
+}
+
+/* Return 1 if the preferred class of REG allows it to be tied
+ to a quantity or register whose class is CLASS.
+ True if REG's reg class either contains or is contained in CLASS. */
+
+static int
+reg_meets_class_p (reg, class)
+ int reg;
+ enum reg_class class;
+{
+ register enum reg_class rclass = reg_preferred_class (reg);
+ return (reg_class_subset_p (rclass, class)
+ || reg_class_subset_p (class, rclass));
+}
+
+/* Return 1 if the two specified classes have registers in common.
+ If CALL_SAVED, then consider only call-saved registers. */
+
+static int
+reg_classes_overlap_p (c1, c2, call_saved)
+ register enum reg_class c1;
+ register enum reg_class c2;
+ int call_saved;
+{
+ HARD_REG_SET c;
+ int i;
+
+ COPY_HARD_REG_SET (c, reg_class_contents[(int) c1]);
+ AND_HARD_REG_SET (c, reg_class_contents[(int) c2]);
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (TEST_HARD_REG_BIT (c, i)
+ && (! call_saved || ! call_used_regs[i]))
+ return 1;
+
+ return 0;
+}
+
+/* Update the class of QTY assuming that REG is being tied to it. */
+
+static void
+update_qty_class (qty, reg)
+ int qty;
+ int reg;
+{
+ enum reg_class rclass = reg_preferred_class (reg);
+ if (reg_class_subset_p (rclass, qty_min_class[qty]))
+ qty_min_class[qty] = rclass;
+
+ rclass = reg_alternate_class (reg);
+ if (reg_class_subset_p (rclass, qty_alternate_class[qty]))
+ qty_alternate_class[qty] = rclass;
+}
+
+/* Handle something which alters the value of an rtx REG.
+
+ REG is whatever is set or clobbered. SETTER is the rtx that
+ is modifying the register.
+
+ If it is not really a register, we do nothing.
+ The file-global variables `this_insn' and `this_insn_number'
+ carry info from `block_alloc'. */
+
+static void
+reg_is_set (reg, setter)
+ rtx reg;
+ rtx setter;
+{
+ /* Note that note_stores will only pass us a SUBREG if it is a SUBREG of
+ a hard register. These may actually not exist any more. */
+
+ if (GET_CODE (reg) != SUBREG
+ && GET_CODE (reg) != REG)
+ return;
+
+ /* Mark this register as being born. If it is used in a CLOBBER, mark
+ it as being born halfway between the previous insn and this insn so that
+ it conflicts with our inputs but not the outputs of the previous insn. */
+
+ reg_is_born (reg, 2 * this_insn_number - (GET_CODE (setter) == CLOBBER));
+}
+
+/* Handle beginning of the life of register REG.
+ BIRTH is the index at which this is happening. */
+
+static void
+reg_is_born (reg, birth)
+ rtx reg;
+ int birth;
+{
+ register int regno;
+
+ if (GET_CODE (reg) == SUBREG)
+ regno = REGNO (SUBREG_REG (reg)) + SUBREG_WORD (reg);
+ else
+ regno = REGNO (reg);
+
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ mark_life (regno, GET_MODE (reg), 1);
+
+ /* If the register was to have been born earlier that the present
+ insn, mark it as live where it is actually born. */
+ if (birth < 2 * this_insn_number)
+ post_mark_life (regno, GET_MODE (reg), 1, birth, 2 * this_insn_number);
+ }
+ else
+ {
+ if (reg_qty[regno] == -2)
+ alloc_qty (regno, GET_MODE (reg), PSEUDO_REGNO_SIZE (regno), birth);
+
+ /* If this register has a quantity number, show that it isn't dead. */
+ if (reg_qty[regno] >= 0)
+ qty_death[reg_qty[regno]] = -1;
+ }
+}
+
+/* Record the death of REG in the current insn. If OUTPUT_P is non-zero,
+ REG is an output that is dying (i.e., it is never used), otherwise it
+ is an input (the normal case).
+ If OUTPUT_P is 1, then we extend the life past the end of this insn. */
+
+static void
+wipe_dead_reg (reg, output_p)
+ register rtx reg;
+ int output_p;
+{
+ register int regno = REGNO (reg);
+
+ /* If this insn has multiple results,
+ and the dead reg is used in one of the results,
+ extend its life to after this insn,
+ so it won't get allocated together with any other result of this insn. */
+ if (GET_CODE (PATTERN (this_insn)) == PARALLEL
+ && !single_set (this_insn))
+ {
+ int i;
+ for (i = XVECLEN (PATTERN (this_insn), 0) - 1; i >= 0; i--)
+ {
+ rtx set = XVECEXP (PATTERN (this_insn), 0, i);
+ if (GET_CODE (set) == SET
+ && GET_CODE (SET_DEST (set)) != REG
+ && !rtx_equal_p (reg, SET_DEST (set))
+ && reg_overlap_mentioned_p (reg, SET_DEST (set)))
+ output_p = 1;
+ }
+ }
+
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ mark_life (regno, GET_MODE (reg), 0);
+
+ /* If a hard register is dying as an output, mark it as in use at
+ the beginning of this insn (the above statement would cause this
+ not to happen). */
+ if (output_p)
+ post_mark_life (regno, GET_MODE (reg), 1,
+ 2 * this_insn_number, 2 * this_insn_number+ 1);
+ }
+
+ else if (reg_qty[regno] >= 0)
+ qty_death[reg_qty[regno]] = 2 * this_insn_number + output_p;
+}
+
+/* Find a block of SIZE words of hard regs in reg_class CLASS
+ that can hold something of machine-mode MODE
+ (but actually we test only the first of the block for holding MODE)
+ and still free between insn BORN_INDEX and insn DEAD_INDEX,
+ and return the number of the first of them.
+ Return -1 if such a block cannot be found.
+ If QTY crosses calls, insist on a register preserved by calls,
+ unless ACCEPT_CALL_CLOBBERED is nonzero.
+
+ If JUST_TRY_SUGGESTED is non-zero, only try to see if the suggested
+ register is available. If not, return -1. */
+
+static int
+find_free_reg (class, mode, qty, accept_call_clobbered, just_try_suggested,
+ born_index, dead_index)
+ enum reg_class class;
+ enum machine_mode mode;
+ int qty;
+ int accept_call_clobbered;
+ int just_try_suggested;
+ int born_index, dead_index;
+{
+ register int i, ins;
+#ifdef HARD_REG_SET
+ register /* Declare it register if it's a scalar. */
+#endif
+ HARD_REG_SET used, first_used;
+#ifdef ELIMINABLE_REGS
+ static struct {int from, to; } eliminables[] = ELIMINABLE_REGS;
+#endif
+
+ /* Validate our parameters. */
+ if (born_index < 0 || born_index > dead_index)
+ abort ();
+
+ /* Don't let a pseudo live in a reg across a function call
+ if we might get a nonlocal goto. */
+ if (current_function_has_nonlocal_label
+ && qty_n_calls_crossed[qty] > 0)
+ return -1;
+
+ if (accept_call_clobbered)
+ COPY_HARD_REG_SET (used, call_fixed_reg_set);
+ else if (qty_n_calls_crossed[qty] == 0)
+ COPY_HARD_REG_SET (used, fixed_reg_set);
+ else
+ COPY_HARD_REG_SET (used, call_used_reg_set);
+
+ for (ins = born_index; ins < dead_index; ins++)
+ IOR_HARD_REG_SET (used, regs_live_at[ins]);
+
+ IOR_COMPL_HARD_REG_SET (used, reg_class_contents[(int) class]);
+
+ /* Don't use the frame pointer reg in local-alloc even if
+ we may omit the frame pointer, because if we do that and then we
+ need a frame pointer, reload won't know how to move the pseudo
+ to another hard reg. It can move only regs made by global-alloc.
+
+ This is true of any register that can be eliminated. */
+#ifdef ELIMINABLE_REGS
+ for (i = 0; i < sizeof eliminables / sizeof eliminables[0]; i++)
+ SET_HARD_REG_BIT (used, eliminables[i].from);
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+ /* If FRAME_POINTER_REGNUM is not a real register, then protect the one
+ that it might be eliminated into. */
+ SET_HARD_REG_BIT (used, HARD_FRAME_POINTER_REGNUM);
+#endif
+#else
+ SET_HARD_REG_BIT (used, FRAME_POINTER_REGNUM);
+#endif
+
+ /* Normally, the registers that can be used for the first register in
+ a multi-register quantity are the same as those that can be used for
+ subsequent registers. However, if just trying suggested registers,
+ restrict our consideration to them. If there are copy-suggested
+ register, try them. Otherwise, try the arithmetic-suggested
+ registers. */
+ COPY_HARD_REG_SET (first_used, used);
+
+ if (just_try_suggested)
+ {
+ if (qty_phys_num_copy_sugg[qty] != 0)
+ IOR_COMPL_HARD_REG_SET (first_used, qty_phys_copy_sugg[qty]);
+ else
+ IOR_COMPL_HARD_REG_SET (first_used, qty_phys_sugg[qty]);
+ }
+
+ /* If all registers are excluded, we can't do anything. */
+ GO_IF_HARD_REG_SUBSET (reg_class_contents[(int) ALL_REGS], first_used, fail);
+
+ /* If at least one would be suitable, test each hard reg. */
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+#ifdef REG_ALLOC_ORDER
+ int regno = reg_alloc_order[i];
+#else
+ int regno = i;
+#endif
+ if (! TEST_HARD_REG_BIT (first_used, regno)
+ && HARD_REGNO_MODE_OK (regno, mode))
+ {
+ register int j;
+ register int size1 = HARD_REGNO_NREGS (regno, mode);
+ for (j = 1; j < size1 && ! TEST_HARD_REG_BIT (used, regno + j); j++);
+ if (j == size1)
+ {
+ /* Mark that this register is in use between its birth and death
+ insns. */
+ post_mark_life (regno, mode, 1, born_index, dead_index);
+ return regno;
+ }
+#ifndef REG_ALLOC_ORDER
+ i += j; /* Skip starting points we know will lose */
+#endif
+ }
+ }
+
+ fail:
+
+ /* If we are just trying suggested register, we have just tried copy-
+ suggested registers, and there are arithmetic-suggested registers,
+ try them. */
+
+ /* If it would be profitable to allocate a call-clobbered register
+ and save and restore it around calls, do that. */
+ if (just_try_suggested && qty_phys_num_copy_sugg[qty] != 0
+ && qty_phys_num_sugg[qty] != 0)
+ {
+ /* Don't try the copy-suggested regs again. */
+ qty_phys_num_copy_sugg[qty] = 0;
+ return find_free_reg (class, mode, qty, accept_call_clobbered, 1,
+ born_index, dead_index);
+ }
+
+ /* We need not check to see if the current function has nonlocal
+ labels because we don't put any pseudos that are live over calls in
+ registers in that case. */
+
+ if (! accept_call_clobbered
+ && flag_caller_saves
+ && ! just_try_suggested
+ && qty_n_calls_crossed[qty] != 0
+ && CALLER_SAVE_PROFITABLE (qty_n_refs[qty], qty_n_calls_crossed[qty]))
+ {
+ i = find_free_reg (class, mode, qty, 1, 0, born_index, dead_index);
+ if (i >= 0)
+ caller_save_needed = 1;
+ return i;
+ }
+ return -1;
+}
+
+/* Mark that REGNO with machine-mode MODE is live starting from the current
+ insn (if LIFE is non-zero) or dead starting at the current insn (if LIFE
+ is zero). */
+
+static void
+mark_life (regno, mode, life)
+ register int regno;
+ enum machine_mode mode;
+ int life;
+{
+ register int j = HARD_REGNO_NREGS (regno, mode);
+ if (life)
+ while (--j >= 0)
+ SET_HARD_REG_BIT (regs_live, regno + j);
+ else
+ while (--j >= 0)
+ CLEAR_HARD_REG_BIT (regs_live, regno + j);
+}
+
+/* Mark register number REGNO (with machine-mode MODE) as live (if LIFE
+ is non-zero) or dead (if LIFE is zero) from insn number BIRTH (inclusive)
+ to insn number DEATH (exclusive). */
+
+static void
+post_mark_life (regno, mode, life, birth, death)
+ int regno;
+ enum machine_mode mode;
+ int life, birth, death;
+{
+ register int j = HARD_REGNO_NREGS (regno, mode);
+#ifdef HARD_REG_SET
+ register /* Declare it register if it's a scalar. */
+#endif
+ HARD_REG_SET this_reg;
+
+ CLEAR_HARD_REG_SET (this_reg);
+ while (--j >= 0)
+ SET_HARD_REG_BIT (this_reg, regno + j);
+
+ if (life)
+ while (birth < death)
+ {
+ IOR_HARD_REG_SET (regs_live_at[birth], this_reg);
+ birth++;
+ }
+ else
+ while (birth < death)
+ {
+ AND_COMPL_HARD_REG_SET (regs_live_at[birth], this_reg);
+ birth++;
+ }
+}
+
+/* INSN is the CLOBBER insn that starts a REG_NO_NOCONFLICT block, R0
+ is the register being clobbered, and R1 is a register being used in
+ the equivalent expression.
+
+ If R1 dies in the block and has a REG_NO_CONFLICT note on every insn
+ in which it is used, return 1.
+
+ Otherwise, return 0. */
+
+static int
+no_conflict_p (insn, r0, r1)
+ rtx insn, r0, r1;
+{
+ int ok = 0;
+ rtx note = find_reg_note (insn, REG_LIBCALL, NULL_RTX);
+ rtx p, last;
+
+ /* If R1 is a hard register, return 0 since we handle this case
+ when we scan the insns that actually use it. */
+
+ if (note == 0
+ || (GET_CODE (r1) == REG && REGNO (r1) < FIRST_PSEUDO_REGISTER)
+ || (GET_CODE (r1) == SUBREG && GET_CODE (SUBREG_REG (r1)) == REG
+ && REGNO (SUBREG_REG (r1)) < FIRST_PSEUDO_REGISTER))
+ return 0;
+
+ last = XEXP (note, 0);
+
+ for (p = NEXT_INSN (insn); p && p != last; p = NEXT_INSN (p))
+ if (GET_RTX_CLASS (GET_CODE (p)) == 'i')
+ {
+ if (find_reg_note (p, REG_DEAD, r1))
+ ok = 1;
+
+ if (reg_mentioned_p (r1, PATTERN (p))
+ && ! find_reg_note (p, REG_NO_CONFLICT, r1))
+ return 0;
+ }
+
+ return ok;
+}
+
+#ifdef REGISTER_CONSTRAINTS
+
+/* Return the number of alternatives for which the constraint string P
+ indicates that the operand must be equal to operand 0 and that no register
+ is acceptable. */
+
+static int
+requires_inout (p)
+ char *p;
+{
+ char c;
+ int found_zero = 0;
+ int reg_allowed = 0;
+ int num_matching_alts = 0;
+
+ while (c = *p++)
+ switch (c)
+ {
+ case '=': case '+': case '?':
+ case '#': case '&': case '!':
+ case '*': case '%':
+ case '1': case '2': case '3': case '4':
+ case 'm': case '<': case '>': case 'V': case 'o':
+ case 'E': case 'F': case 'G': case 'H':
+ case 's': case 'i': case 'n':
+ case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P':
+#ifdef EXTRA_CONSTRAINT
+ case 'Q': case 'R': case 'S': case 'T': case 'U':
+#endif
+ case 'X':
+ /* These don't say anything we care about. */
+ break;
+
+ case ',':
+ if (found_zero && ! reg_allowed)
+ num_matching_alts++;
+
+ found_zero = reg_allowed = 0;
+ break;
+
+ case '0':
+ found_zero = 1;
+ break;
+
+ case 'p':
+ case 'g': case 'r':
+ default:
+ reg_allowed = 1;
+ break;
+ }
+
+ if (found_zero && ! reg_allowed)
+ num_matching_alts++;
+
+ return num_matching_alts;
+}
+#endif /* REGISTER_CONSTRAINTS */
+
+void
+dump_local_alloc (file)
+ FILE *file;
+{
+ register int i;
+ for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+ if (reg_renumber[i] != -1)
+ fprintf (file, ";; Register %d in %d.\n", i, reg_renumber[i]);
+}
diff --git a/gnu/usr.bin/cc/cc_int/loop.c b/gnu/usr.bin/cc/cc_int/loop.c
new file mode 100644
index 0000000..c6caefe
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/loop.c
@@ -0,0 +1,6587 @@
+/* Move constant computations out of loops.
+ Copyright (C) 1987, 88, 89, 91, 92, 93, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This is the loop optimization pass of the compiler.
+ It finds invariant computations within loops and moves them
+ to the beginning of the loop. Then it identifies basic and
+ general induction variables. Strength reduction is applied to the general
+ induction variables, and induction variable elimination is applied to
+ the basic induction variables.
+
+ It also finds cases where
+ a register is set within the loop by zero-extending a narrower value
+ and changes these to zero the entire register once before the loop
+ and merely copy the low part within the loop.
+
+ Most of the complexity is in heuristics to decide when it is worth
+ while to do these things. */
+
+#include <stdio.h>
+#include "config.h"
+#include "rtl.h"
+#include "obstack.h"
+#include "expr.h"
+#include "insn-config.h"
+#include "insn-flags.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "recog.h"
+#include "flags.h"
+#include "real.h"
+#include "loop.h"
+
+/* Vector mapping INSN_UIDs to luids.
+ The luids are like uids but increase monotonically always.
+ We use them to see whether a jump comes from outside a given loop. */
+
+int *uid_luid;
+
+/* Indexed by INSN_UID, contains the ordinal giving the (innermost) loop
+ number the insn is contained in. */
+
+int *uid_loop_num;
+
+/* 1 + largest uid of any insn. */
+
+int max_uid_for_loop;
+
+/* 1 + luid of last insn. */
+
+static int max_luid;
+
+/* Number of loops detected in current function. Used as index to the
+ next few tables. */
+
+static int max_loop_num;
+
+/* Indexed by loop number, contains the first and last insn of each loop. */
+
+static rtx *loop_number_loop_starts, *loop_number_loop_ends;
+
+/* For each loop, gives the containing loop number, -1 if none. */
+
+int *loop_outer_loop;
+
+/* Indexed by loop number, contains a nonzero value if the "loop" isn't
+ really a loop (an insn outside the loop branches into it). */
+
+static char *loop_invalid;
+
+/* Indexed by loop number, links together all LABEL_REFs which refer to
+ code labels outside the loop. Used by routines that need to know all
+ loop exits, such as final_biv_value and final_giv_value.
+
+ This does not include loop exits due to return instructions. This is
+ because all bivs and givs are pseudos, and hence must be dead after a
+ return, so the presense of a return does not affect any of the
+ optimizations that use this info. It is simpler to just not include return
+ instructions on this list. */
+
+rtx *loop_number_exit_labels;
+
+/* Holds the number of loop iterations. It is zero if the number could not be
+ calculated. Must be unsigned since the number of iterations can
+ be as high as 2^wordsize-1. For loops with a wider iterator, this number
+ will will be zero if the number of loop iterations is too large for an
+ unsigned integer to hold. */
+
+unsigned HOST_WIDE_INT loop_n_iterations;
+
+/* Nonzero if there is a subroutine call in the current loop.
+ (unknown_address_altered is also nonzero in this case.) */
+
+static int loop_has_call;
+
+/* Nonzero if there is a volatile memory reference in the current
+ loop. */
+
+static int loop_has_volatile;
+
+/* Added loop_continue which is the NOTE_INSN_LOOP_CONT of the
+ current loop. A continue statement will generate a branch to
+ NEXT_INSN (loop_continue). */
+
+static rtx loop_continue;
+
+/* Indexed by register number, contains the number of times the reg
+ is set during the loop being scanned.
+ During code motion, a negative value indicates a reg that has been
+ made a candidate; in particular -2 means that it is an candidate that
+ we know is equal to a constant and -1 means that it is an candidate
+ not known equal to a constant.
+ After code motion, regs moved have 0 (which is accurate now)
+ while the failed candidates have the original number of times set.
+
+ Therefore, at all times, == 0 indicates an invariant register;
+ < 0 a conditionally invariant one. */
+
+static short *n_times_set;
+
+/* Original value of n_times_set; same except that this value
+ is not set negative for a reg whose sets have been made candidates
+ and not set to 0 for a reg that is moved. */
+
+static short *n_times_used;
+
+/* Index by register number, 1 indicates that the register
+ cannot be moved or strength reduced. */
+
+static char *may_not_optimize;
+
+/* Nonzero means reg N has already been moved out of one loop.
+ This reduces the desire to move it out of another. */
+
+static char *moved_once;
+
+/* Array of MEMs that are stored in this loop. If there are too many to fit
+ here, we just turn on unknown_address_altered. */
+
+#define NUM_STORES 20
+static rtx loop_store_mems[NUM_STORES];
+
+/* Index of first available slot in above array. */
+static int loop_store_mems_idx;
+
+/* Nonzero if we don't know what MEMs were changed in the current loop.
+ This happens if the loop contains a call (in which case `loop_has_call'
+ will also be set) or if we store into more than NUM_STORES MEMs. */
+
+static int unknown_address_altered;
+
+/* Count of movable (i.e. invariant) instructions discovered in the loop. */
+static int num_movables;
+
+/* Count of memory write instructions discovered in the loop. */
+static int num_mem_sets;
+
+/* Number of loops contained within the current one, including itself. */
+static int loops_enclosed;
+
+/* Bound on pseudo register number before loop optimization.
+ A pseudo has valid regscan info if its number is < max_reg_before_loop. */
+int max_reg_before_loop;
+
+/* This obstack is used in product_cheap_p to allocate its rtl. It
+ may call gen_reg_rtx which, in turn, may reallocate regno_reg_rtx.
+ If we used the same obstack that it did, we would be deallocating
+ that array. */
+
+static struct obstack temp_obstack;
+
+/* This is where the pointer to the obstack being used for RTL is stored. */
+
+extern struct obstack *rtl_obstack;
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+extern char *oballoc ();
+
+/* During the analysis of a loop, a chain of `struct movable's
+ is made to record all the movable insns found.
+ Then the entire chain can be scanned to decide which to move. */
+
+struct movable
+{
+ rtx insn; /* A movable insn */
+ rtx set_src; /* The expression this reg is set from. */
+ rtx set_dest; /* The destination of this SET. */
+ rtx dependencies; /* When INSN is libcall, this is an EXPR_LIST
+ of any registers used within the LIBCALL. */
+ int consec; /* Number of consecutive following insns
+ that must be moved with this one. */
+ int regno; /* The register it sets */
+ short lifetime; /* lifetime of that register;
+ may be adjusted when matching movables
+ that load the same value are found. */
+ short savings; /* Number of insns we can move for this reg,
+ including other movables that force this
+ or match this one. */
+ unsigned int cond : 1; /* 1 if only conditionally movable */
+ unsigned int force : 1; /* 1 means MUST move this insn */
+ unsigned int global : 1; /* 1 means reg is live outside this loop */
+ /* If PARTIAL is 1, GLOBAL means something different:
+ that the reg is live outside the range from where it is set
+ to the following label. */
+ unsigned int done : 1; /* 1 inhibits further processing of this */
+
+ unsigned int partial : 1; /* 1 means this reg is used for zero-extending.
+ In particular, moving it does not make it
+ invariant. */
+ unsigned int move_insn : 1; /* 1 means that we call emit_move_insn to
+ load SRC, rather than copying INSN. */
+ unsigned int is_equiv : 1; /* 1 means a REG_EQUIV is present on INSN. */
+ enum machine_mode savemode; /* Nonzero means it is a mode for a low part
+ that we should avoid changing when clearing
+ the rest of the reg. */
+ struct movable *match; /* First entry for same value */
+ struct movable *forces; /* An insn that must be moved if this is */
+ struct movable *next;
+};
+
+FILE *loop_dump_stream;
+
+/* Forward declarations. */
+
+static void find_and_verify_loops ();
+static void mark_loop_jump ();
+static void prescan_loop ();
+static int reg_in_basic_block_p ();
+static int consec_sets_invariant_p ();
+static rtx libcall_other_reg ();
+static int labels_in_range_p ();
+static void count_loop_regs_set ();
+static void note_addr_stored ();
+static int loop_reg_used_before_p ();
+static void scan_loop ();
+static void replace_call_address ();
+static rtx skip_consec_insns ();
+static int libcall_benefit ();
+static void ignore_some_movables ();
+static void force_movables ();
+static void combine_movables ();
+static int rtx_equal_for_loop_p ();
+static void move_movables ();
+static void strength_reduce ();
+static int valid_initial_value_p ();
+static void find_mem_givs ();
+static void record_biv ();
+static void check_final_value ();
+static void record_giv ();
+static void update_giv_derive ();
+static int basic_induction_var ();
+static rtx simplify_giv_expr ();
+static int general_induction_var ();
+static int consec_sets_giv ();
+static int check_dbra_loop ();
+static rtx express_from ();
+static int combine_givs_p ();
+static void combine_givs ();
+static int product_cheap_p ();
+static int maybe_eliminate_biv ();
+static int maybe_eliminate_biv_1 ();
+static int last_use_this_basic_block ();
+static void record_initial ();
+static void update_reg_last_use ();
+
+/* Relative gain of eliminating various kinds of operations. */
+int add_cost;
+#if 0
+int shift_cost;
+int mult_cost;
+#endif
+
+/* Benefit penalty, if a giv is not replaceable, i.e. must emit an insn to
+ copy the value of the strength reduced giv to its original register. */
+int copy_cost;
+
+void
+init_loop ()
+{
+ char *free_point = (char *) oballoc (1);
+ rtx reg = gen_rtx (REG, word_mode, 0);
+
+ add_cost = rtx_cost (gen_rtx (PLUS, word_mode, reg, reg), SET);
+
+ /* We multiply by 2 to reconcile the difference in scale between
+ these two ways of computing costs. Otherwise the cost of a copy
+ will be far less than the cost of an add. */
+
+ copy_cost = 2 * 2;
+
+ /* Free the objects we just allocated. */
+ obfree (free_point);
+
+ /* Initialize the obstack used for rtl in product_cheap_p. */
+ gcc_obstack_init (&temp_obstack);
+}
+
+/* Entry point of this file. Perform loop optimization
+ on the current function. F is the first insn of the function
+ and DUMPFILE is a stream for output of a trace of actions taken
+ (or 0 if none should be output). */
+
+void
+loop_optimize (f, dumpfile)
+ /* f is the first instruction of a chain of insns for one function */
+ rtx f;
+ FILE *dumpfile;
+{
+ register rtx insn;
+ register int i;
+ rtx last_insn;
+
+ loop_dump_stream = dumpfile;
+
+ init_recog_no_volatile ();
+ init_alias_analysis ();
+
+ max_reg_before_loop = max_reg_num ();
+
+ moved_once = (char *) alloca (max_reg_before_loop);
+ bzero (moved_once, max_reg_before_loop);
+
+ regs_may_share = 0;
+
+ /* Count the number of loops. */
+
+ max_loop_num = 0;
+ for (insn = f; insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
+ max_loop_num++;
+ }
+
+ /* Don't waste time if no loops. */
+ if (max_loop_num == 0)
+ return;
+
+ /* Get size to use for tables indexed by uids.
+ Leave some space for labels allocated by find_and_verify_loops. */
+ max_uid_for_loop = get_max_uid () + 1 + max_loop_num * 32;
+
+ uid_luid = (int *) alloca (max_uid_for_loop * sizeof (int));
+ uid_loop_num = (int *) alloca (max_uid_for_loop * sizeof (int));
+
+ bzero ((char *) uid_luid, max_uid_for_loop * sizeof (int));
+ bzero ((char *) uid_loop_num, max_uid_for_loop * sizeof (int));
+
+ /* Allocate tables for recording each loop. We set each entry, so they need
+ not be zeroed. */
+ loop_number_loop_starts = (rtx *) alloca (max_loop_num * sizeof (rtx));
+ loop_number_loop_ends = (rtx *) alloca (max_loop_num * sizeof (rtx));
+ loop_outer_loop = (int *) alloca (max_loop_num * sizeof (int));
+ loop_invalid = (char *) alloca (max_loop_num * sizeof (char));
+ loop_number_exit_labels = (rtx *) alloca (max_loop_num * sizeof (rtx));
+
+ /* Find and process each loop.
+ First, find them, and record them in order of their beginnings. */
+ find_and_verify_loops (f);
+
+ /* Now find all register lifetimes. This must be done after
+ find_and_verify_loops, because it might reorder the insns in the
+ function. */
+ reg_scan (f, max_reg_num (), 1);
+
+ /* See if we went too far. */
+ if (get_max_uid () > max_uid_for_loop)
+ abort ();
+
+ /* Compute the mapping from uids to luids.
+ LUIDs are numbers assigned to insns, like uids,
+ except that luids increase monotonically through the code.
+ Don't assign luids to line-number NOTEs, so that the distance in luids
+ between two insns is not affected by -g. */
+
+ for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
+ {
+ last_insn = insn;
+ if (GET_CODE (insn) != NOTE
+ || NOTE_LINE_NUMBER (insn) <= 0)
+ uid_luid[INSN_UID (insn)] = ++i;
+ else
+ /* Give a line number note the same luid as preceding insn. */
+ uid_luid[INSN_UID (insn)] = i;
+ }
+
+ max_luid = i + 1;
+
+ /* Don't leave gaps in uid_luid for insns that have been
+ deleted. It is possible that the first or last insn
+ using some register has been deleted by cross-jumping.
+ Make sure that uid_luid for that former insn's uid
+ points to the general area where that insn used to be. */
+ for (i = 0; i < max_uid_for_loop; i++)
+ {
+ uid_luid[0] = uid_luid[i];
+ if (uid_luid[0] != 0)
+ break;
+ }
+ for (i = 0; i < max_uid_for_loop; i++)
+ if (uid_luid[i] == 0)
+ uid_luid[i] = uid_luid[i - 1];
+
+ /* Create a mapping from loops to BLOCK tree nodes. */
+ if (flag_unroll_loops && write_symbols != NO_DEBUG)
+ find_loop_tree_blocks ();
+
+ /* Now scan the loops, last ones first, since this means inner ones are done
+ before outer ones. */
+ for (i = max_loop_num-1; i >= 0; i--)
+ if (! loop_invalid[i] && loop_number_loop_ends[i])
+ scan_loop (loop_number_loop_starts[i], loop_number_loop_ends[i],
+ max_reg_num ());
+
+ /* If debugging and unrolling loops, we must replicate the tree nodes
+ corresponding to the blocks inside the loop, so that the original one
+ to one mapping will remain. */
+ if (flag_unroll_loops && write_symbols != NO_DEBUG)
+ unroll_block_trees ();
+}
+
+/* Optimize one loop whose start is LOOP_START and end is END.
+ LOOP_START is the NOTE_INSN_LOOP_BEG and END is the matching
+ NOTE_INSN_LOOP_END. */
+
+/* ??? Could also move memory writes out of loops if the destination address
+ is invariant, the source is invariant, the memory write is not volatile,
+ and if we can prove that no read inside the loop can read this address
+ before the write occurs. If there is a read of this address after the
+ write, then we can also mark the memory read as invariant. */
+
+static void
+scan_loop (loop_start, end, nregs)
+ rtx loop_start, end;
+ int nregs;
+{
+ register int i;
+ register rtx p;
+ /* 1 if we are scanning insns that could be executed zero times. */
+ int maybe_never = 0;
+ /* 1 if we are scanning insns that might never be executed
+ due to a subroutine call which might exit before they are reached. */
+ int call_passed = 0;
+ /* For a rotated loop that is entered near the bottom,
+ this is the label at the top. Otherwise it is zero. */
+ rtx loop_top = 0;
+ /* Jump insn that enters the loop, or 0 if control drops in. */
+ rtx loop_entry_jump = 0;
+ /* Place in the loop where control enters. */
+ rtx scan_start;
+ /* Number of insns in the loop. */
+ int insn_count;
+ int in_libcall = 0;
+ int tem;
+ rtx temp;
+ /* The SET from an insn, if it is the only SET in the insn. */
+ rtx set, set1;
+ /* Chain describing insns movable in current loop. */
+ struct movable *movables = 0;
+ /* Last element in `movables' -- so we can add elements at the end. */
+ struct movable *last_movable = 0;
+ /* Ratio of extra register life span we can justify
+ for saving an instruction. More if loop doesn't call subroutines
+ since in that case saving an insn makes more difference
+ and more registers are available. */
+ int threshold;
+ /* If we have calls, contains the insn in which a register was used
+ if it was used exactly once; contains const0_rtx if it was used more
+ than once. */
+ rtx *reg_single_usage = 0;
+ /* Nonzero if we are scanning instructions in a sub-loop. */
+ int loop_depth = 0;
+
+ n_times_set = (short *) alloca (nregs * sizeof (short));
+ n_times_used = (short *) alloca (nregs * sizeof (short));
+ may_not_optimize = (char *) alloca (nregs);
+
+ /* Determine whether this loop starts with a jump down to a test at
+ the end. This will occur for a small number of loops with a test
+ that is too complex to duplicate in front of the loop.
+
+ We search for the first insn or label in the loop, skipping NOTEs.
+ However, we must be careful not to skip past a NOTE_INSN_LOOP_BEG
+ (because we might have a loop executed only once that contains a
+ loop which starts with a jump to its exit test) or a NOTE_INSN_LOOP_END
+ (in case we have a degenerate loop).
+
+ Note that if we mistakenly think that a loop is entered at the top
+ when, in fact, it is entered at the exit test, the only effect will be
+ slightly poorer optimization. Making the opposite error can generate
+ incorrect code. Since very few loops now start with a jump to the
+ exit test, the code here to detect that case is very conservative. */
+
+ for (p = NEXT_INSN (loop_start);
+ p != end
+ && GET_CODE (p) != CODE_LABEL && GET_RTX_CLASS (GET_CODE (p)) != 'i'
+ && (GET_CODE (p) != NOTE
+ || (NOTE_LINE_NUMBER (p) != NOTE_INSN_LOOP_BEG
+ && NOTE_LINE_NUMBER (p) != NOTE_INSN_LOOP_END));
+ p = NEXT_INSN (p))
+ ;
+
+ scan_start = p;
+
+ /* Set up variables describing this loop. */
+ prescan_loop (loop_start, end);
+ threshold = (loop_has_call ? 1 : 2) * (1 + n_non_fixed_regs);
+
+ /* If loop has a jump before the first label,
+ the true entry is the target of that jump.
+ Start scan from there.
+ But record in LOOP_TOP the place where the end-test jumps
+ back to so we can scan that after the end of the loop. */
+ if (GET_CODE (p) == JUMP_INSN)
+ {
+ loop_entry_jump = p;
+
+ /* Loop entry must be unconditional jump (and not a RETURN) */
+ if (simplejump_p (p)
+ && JUMP_LABEL (p) != 0
+ /* Check to see whether the jump actually
+ jumps out of the loop (meaning it's no loop).
+ This case can happen for things like
+ do {..} while (0). If this label was generated previously
+ by loop, we can't tell anything about it and have to reject
+ the loop. */
+ && INSN_UID (JUMP_LABEL (p)) < max_uid_for_loop
+ && INSN_LUID (JUMP_LABEL (p)) >= INSN_LUID (loop_start)
+ && INSN_LUID (JUMP_LABEL (p)) < INSN_LUID (end))
+ {
+ loop_top = next_label (scan_start);
+ scan_start = JUMP_LABEL (p);
+ }
+ }
+
+ /* If SCAN_START was an insn created by loop, we don't know its luid
+ as required by loop_reg_used_before_p. So skip such loops. (This
+ test may never be true, but it's best to play it safe.)
+
+ Also, skip loops where we do not start scanning at a label. This
+ test also rejects loops starting with a JUMP_INSN that failed the
+ test above. */
+
+ if (INSN_UID (scan_start) >= max_uid_for_loop
+ || GET_CODE (scan_start) != CODE_LABEL)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "\nLoop from %d to %d is phony.\n\n",
+ INSN_UID (loop_start), INSN_UID (end));
+ return;
+ }
+
+ /* Count number of times each reg is set during this loop.
+ Set may_not_optimize[I] if it is not safe to move out
+ the setting of register I. If this loop has calls, set
+ reg_single_usage[I]. */
+
+ bzero ((char *) n_times_set, nregs * sizeof (short));
+ bzero (may_not_optimize, nregs);
+
+ if (loop_has_call)
+ {
+ reg_single_usage = (rtx *) alloca (nregs * sizeof (rtx));
+ bzero ((char *) reg_single_usage, nregs * sizeof (rtx));
+ }
+
+ count_loop_regs_set (loop_top ? loop_top : loop_start, end,
+ may_not_optimize, reg_single_usage, &insn_count, nregs);
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ may_not_optimize[i] = 1, n_times_set[i] = 1;
+ bcopy ((char *) n_times_set, (char *) n_times_used, nregs * sizeof (short));
+
+ if (loop_dump_stream)
+ {
+ fprintf (loop_dump_stream, "\nLoop from %d to %d: %d real insns.\n",
+ INSN_UID (loop_start), INSN_UID (end), insn_count);
+ if (loop_continue)
+ fprintf (loop_dump_stream, "Continue at insn %d.\n",
+ INSN_UID (loop_continue));
+ }
+
+ /* Scan through the loop finding insns that are safe to move.
+ Set n_times_set negative for the reg being set, so that
+ this reg will be considered invariant for subsequent insns.
+ We consider whether subsequent insns use the reg
+ in deciding whether it is worth actually moving.
+
+ MAYBE_NEVER is nonzero if we have passed a conditional jump insn
+ and therefore it is possible that the insns we are scanning
+ would never be executed. At such times, we must make sure
+ that it is safe to execute the insn once instead of zero times.
+ When MAYBE_NEVER is 0, all insns will be executed at least once
+ so that is not a problem. */
+
+ p = scan_start;
+ while (1)
+ {
+ p = NEXT_INSN (p);
+ /* At end of a straight-in loop, we are done.
+ At end of a loop entered at the bottom, scan the top. */
+ if (p == scan_start)
+ break;
+ if (p == end)
+ {
+ if (loop_top != 0)
+ p = loop_top;
+ else
+ break;
+ if (p == scan_start)
+ break;
+ }
+
+ if (GET_RTX_CLASS (GET_CODE (p)) == 'i'
+ && find_reg_note (p, REG_LIBCALL, NULL_RTX))
+ in_libcall = 1;
+ else if (GET_RTX_CLASS (GET_CODE (p)) == 'i'
+ && find_reg_note (p, REG_RETVAL, NULL_RTX))
+ in_libcall = 0;
+
+ if (GET_CODE (p) == INSN
+ && (set = single_set (p))
+ && GET_CODE (SET_DEST (set)) == REG
+ && ! may_not_optimize[REGNO (SET_DEST (set))])
+ {
+ int tem1 = 0;
+ int tem2 = 0;
+ int move_insn = 0;
+ rtx src = SET_SRC (set);
+ rtx dependencies = 0;
+
+ /* Figure out what to use as a source of this insn. If a REG_EQUIV
+ note is given or if a REG_EQUAL note with a constant operand is
+ specified, use it as the source and mark that we should move
+ this insn by calling emit_move_insn rather that duplicating the
+ insn.
+
+ Otherwise, only use the REG_EQUAL contents if a REG_RETVAL note
+ is present. */
+ temp = find_reg_note (p, REG_EQUIV, NULL_RTX);
+ if (temp)
+ src = XEXP (temp, 0), move_insn = 1;
+ else
+ {
+ temp = find_reg_note (p, REG_EQUAL, NULL_RTX);
+ if (temp && CONSTANT_P (XEXP (temp, 0)))
+ src = XEXP (temp, 0), move_insn = 1;
+ if (temp && find_reg_note (p, REG_RETVAL, NULL_RTX))
+ {
+ src = XEXP (temp, 0);
+ /* A libcall block can use regs that don't appear in
+ the equivalent expression. To move the libcall,
+ we must move those regs too. */
+ dependencies = libcall_other_reg (p, src);
+ }
+ }
+
+ /* Don't try to optimize a register that was made
+ by loop-optimization for an inner loop.
+ We don't know its life-span, so we can't compute the benefit. */
+ if (REGNO (SET_DEST (set)) >= max_reg_before_loop)
+ ;
+ /* In order to move a register, we need to have one of three cases:
+ (1) it is used only in the same basic block as the set
+ (2) it is not a user variable and it is not used in the
+ exit test (this can cause the variable to be used
+ before it is set just like a user-variable).
+ (3) the set is guaranteed to be executed once the loop starts,
+ and the reg is not used until after that. */
+ else if (! ((! maybe_never
+ && ! loop_reg_used_before_p (set, p, loop_start,
+ scan_start, end))
+ || (! REG_USERVAR_P (SET_DEST (set))
+ && ! REG_LOOP_TEST_P (SET_DEST (set)))
+ || reg_in_basic_block_p (p, SET_DEST (set))))
+ ;
+ else if ((tem = invariant_p (src))
+ && (dependencies == 0
+ || (tem2 = invariant_p (dependencies)) != 0)
+ && (n_times_set[REGNO (SET_DEST (set))] == 1
+ || (tem1
+ = consec_sets_invariant_p (SET_DEST (set),
+ n_times_set[REGNO (SET_DEST (set))],
+ p)))
+ /* If the insn can cause a trap (such as divide by zero),
+ can't move it unless it's guaranteed to be executed
+ once loop is entered. Even a function call might
+ prevent the trap insn from being reached
+ (since it might exit!) */
+ && ! ((maybe_never || call_passed)
+ && may_trap_p (src)))
+ {
+ register struct movable *m;
+ register int regno = REGNO (SET_DEST (set));
+
+ /* A potential lossage is where we have a case where two insns
+ can be combined as long as they are both in the loop, but
+ we move one of them outside the loop. For large loops,
+ this can lose. The most common case of this is the address
+ of a function being called.
+
+ Therefore, if this register is marked as being used exactly
+ once if we are in a loop with calls (a "large loop"), see if
+ we can replace the usage of this register with the source
+ of this SET. If we can, delete this insn.
+
+ Don't do this if P has a REG_RETVAL note or if we have
+ SMALL_REGISTER_CLASSES and SET_SRC is a hard register. */
+
+ if (reg_single_usage && reg_single_usage[regno] != 0
+ && reg_single_usage[regno] != const0_rtx
+ && regno_first_uid[regno] == INSN_UID (p)
+ && (regno_last_uid[regno]
+ == INSN_UID (reg_single_usage[regno]))
+ && n_times_set[REGNO (SET_DEST (set))] == 1
+ && ! side_effects_p (SET_SRC (set))
+ && ! find_reg_note (p, REG_RETVAL, NULL_RTX)
+#ifdef SMALL_REGISTER_CLASSES
+ && ! (GET_CODE (SET_SRC (set)) == REG
+ && REGNO (SET_SRC (set)) < FIRST_PSEUDO_REGISTER)
+#endif
+ /* This test is not redundant; SET_SRC (set) might be
+ a call-clobbered register and the life of REGNO
+ might span a call. */
+ && ! modified_between_p (SET_SRC (set), p,
+ reg_single_usage[regno])
+ && no_labels_between_p (p, reg_single_usage[regno])
+ && validate_replace_rtx (SET_DEST (set), SET_SRC (set),
+ reg_single_usage[regno]))
+ {
+ /* Replace any usage in a REG_EQUAL note. */
+ REG_NOTES (reg_single_usage[regno])
+ = replace_rtx (REG_NOTES (reg_single_usage[regno]),
+ SET_DEST (set), SET_SRC (set));
+
+ PUT_CODE (p, NOTE);
+ NOTE_LINE_NUMBER (p) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (p) = 0;
+ n_times_set[regno] = 0;
+ continue;
+ }
+
+ m = (struct movable *) alloca (sizeof (struct movable));
+ m->next = 0;
+ m->insn = p;
+ m->set_src = src;
+ m->dependencies = dependencies;
+ m->set_dest = SET_DEST (set);
+ m->force = 0;
+ m->consec = n_times_set[REGNO (SET_DEST (set))] - 1;
+ m->done = 0;
+ m->forces = 0;
+ m->partial = 0;
+ m->move_insn = move_insn;
+ m->is_equiv = (find_reg_note (p, REG_EQUIV, NULL_RTX) != 0);
+ m->savemode = VOIDmode;
+ m->regno = regno;
+ /* Set M->cond if either invariant_p or consec_sets_invariant_p
+ returned 2 (only conditionally invariant). */
+ m->cond = ((tem | tem1 | tem2) > 1);
+ m->global = (uid_luid[regno_last_uid[regno]] > INSN_LUID (end)
+ || uid_luid[regno_first_uid[regno]] < INSN_LUID (loop_start));
+ m->match = 0;
+ m->lifetime = (uid_luid[regno_last_uid[regno]]
+ - uid_luid[regno_first_uid[regno]]);
+ m->savings = n_times_used[regno];
+ if (find_reg_note (p, REG_RETVAL, NULL_RTX))
+ m->savings += libcall_benefit (p);
+ n_times_set[regno] = move_insn ? -2 : -1;
+ /* Add M to the end of the chain MOVABLES. */
+ if (movables == 0)
+ movables = m;
+ else
+ last_movable->next = m;
+ last_movable = m;
+
+ if (m->consec > 0)
+ {
+ /* Skip this insn, not checking REG_LIBCALL notes. */
+ p = next_nonnote_insn (p);
+ /* Skip the consecutive insns, if there are any. */
+ p = skip_consec_insns (p, m->consec);
+ /* Back up to the last insn of the consecutive group. */
+ p = prev_nonnote_insn (p);
+
+ /* We must now reset m->move_insn, m->is_equiv, and possibly
+ m->set_src to correspond to the effects of all the
+ insns. */
+ temp = find_reg_note (p, REG_EQUIV, NULL_RTX);
+ if (temp)
+ m->set_src = XEXP (temp, 0), m->move_insn = 1;
+ else
+ {
+ temp = find_reg_note (p, REG_EQUAL, NULL_RTX);
+ if (temp && CONSTANT_P (XEXP (temp, 0)))
+ m->set_src = XEXP (temp, 0), m->move_insn = 1;
+ else
+ m->move_insn = 0;
+
+ }
+ m->is_equiv = (find_reg_note (p, REG_EQUIV, NULL_RTX) != 0);
+ }
+ }
+ /* If this register is always set within a STRICT_LOW_PART
+ or set to zero, then its high bytes are constant.
+ So clear them outside the loop and within the loop
+ just load the low bytes.
+ We must check that the machine has an instruction to do so.
+ Also, if the value loaded into the register
+ depends on the same register, this cannot be done. */
+ else if (SET_SRC (set) == const0_rtx
+ && GET_CODE (NEXT_INSN (p)) == INSN
+ && (set1 = single_set (NEXT_INSN (p)))
+ && GET_CODE (set1) == SET
+ && (GET_CODE (SET_DEST (set1)) == STRICT_LOW_PART)
+ && (GET_CODE (XEXP (SET_DEST (set1), 0)) == SUBREG)
+ && (SUBREG_REG (XEXP (SET_DEST (set1), 0))
+ == SET_DEST (set))
+ && !reg_mentioned_p (SET_DEST (set), SET_SRC (set1)))
+ {
+ register int regno = REGNO (SET_DEST (set));
+ if (n_times_set[regno] == 2)
+ {
+ register struct movable *m;
+ m = (struct movable *) alloca (sizeof (struct movable));
+ m->next = 0;
+ m->insn = p;
+ m->set_dest = SET_DEST (set);
+ m->dependencies = 0;
+ m->force = 0;
+ m->consec = 0;
+ m->done = 0;
+ m->forces = 0;
+ m->move_insn = 0;
+ m->partial = 1;
+ /* If the insn may not be executed on some cycles,
+ we can't clear the whole reg; clear just high part.
+ Not even if the reg is used only within this loop.
+ Consider this:
+ while (1)
+ while (s != t) {
+ if (foo ()) x = *s;
+ use (x);
+ }
+ Clearing x before the inner loop could clobber a value
+ being saved from the last time around the outer loop.
+ However, if the reg is not used outside this loop
+ and all uses of the register are in the same
+ basic block as the store, there is no problem.
+
+ If this insn was made by loop, we don't know its
+ INSN_LUID and hence must make a conservative
+ assumption. */
+ m->global = (INSN_UID (p) >= max_uid_for_loop
+ || (uid_luid[regno_last_uid[regno]]
+ > INSN_LUID (end))
+ || (uid_luid[regno_first_uid[regno]]
+ < INSN_LUID (p))
+ || (labels_in_range_p
+ (p, uid_luid[regno_first_uid[regno]])));
+ if (maybe_never && m->global)
+ m->savemode = GET_MODE (SET_SRC (set1));
+ else
+ m->savemode = VOIDmode;
+ m->regno = regno;
+ m->cond = 0;
+ m->match = 0;
+ m->lifetime = (uid_luid[regno_last_uid[regno]]
+ - uid_luid[regno_first_uid[regno]]);
+ m->savings = 1;
+ n_times_set[regno] = -1;
+ /* Add M to the end of the chain MOVABLES. */
+ if (movables == 0)
+ movables = m;
+ else
+ last_movable->next = m;
+ last_movable = m;
+ }
+ }
+ }
+ /* Past a call insn, we get to insns which might not be executed
+ because the call might exit. This matters for insns that trap.
+ Call insns inside a REG_LIBCALL/REG_RETVAL block always return,
+ so they don't count. */
+ else if (GET_CODE (p) == CALL_INSN && ! in_libcall)
+ call_passed = 1;
+ /* Past a label or a jump, we get to insns for which we
+ can't count on whether or how many times they will be
+ executed during each iteration. Therefore, we can
+ only move out sets of trivial variables
+ (those not used after the loop). */
+ /* This code appears in three places, once in scan_loop, and twice
+ in strength_reduce. */
+ else if ((GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN)
+ /* If we enter the loop in the middle, and scan around to the
+ beginning, don't set maybe_never for that. This must be an
+ unconditional jump, otherwise the code at the top of the
+ loop might never be executed. Unconditional jumps are
+ followed a by barrier then loop end. */
+ && ! (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p) == loop_top
+ && NEXT_INSN (NEXT_INSN (p)) == end
+ && simplejump_p (p)))
+ maybe_never = 1;
+ else if (GET_CODE (p) == NOTE)
+ {
+ /* At the virtual top of a converted loop, insns are again known to
+ be executed: logically, the loop begins here even though the exit
+ code has been duplicated. */
+ if (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_VTOP && loop_depth == 0)
+ maybe_never = call_passed = 0;
+ else if (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG)
+ loop_depth++;
+ else if (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)
+ loop_depth--;
+ }
+ }
+
+ /* If one movable subsumes another, ignore that other. */
+
+ ignore_some_movables (movables);
+
+ /* For each movable insn, see if the reg that it loads
+ leads when it dies right into another conditionally movable insn.
+ If so, record that the second insn "forces" the first one,
+ since the second can be moved only if the first is. */
+
+ force_movables (movables);
+
+ /* See if there are multiple movable insns that load the same value.
+ If there are, make all but the first point at the first one
+ through the `match' field, and add the priorities of them
+ all together as the priority of the first. */
+
+ combine_movables (movables, nregs);
+
+ /* Now consider each movable insn to decide whether it is worth moving.
+ Store 0 in n_times_set for each reg that is moved. */
+
+ move_movables (movables, threshold,
+ insn_count, loop_start, end, nregs);
+
+ /* Now candidates that still are negative are those not moved.
+ Change n_times_set to indicate that those are not actually invariant. */
+ for (i = 0; i < nregs; i++)
+ if (n_times_set[i] < 0)
+ n_times_set[i] = n_times_used[i];
+
+ if (flag_strength_reduce)
+ strength_reduce (scan_start, end, loop_top,
+ insn_count, loop_start, end);
+}
+
+/* Add elements to *OUTPUT to record all the pseudo-regs
+ mentioned in IN_THIS but not mentioned in NOT_IN_THIS. */
+
+void
+record_excess_regs (in_this, not_in_this, output)
+ rtx in_this, not_in_this;
+ rtx *output;
+{
+ enum rtx_code code;
+ char *fmt;
+ int i;
+
+ code = GET_CODE (in_this);
+
+ switch (code)
+ {
+ case PC:
+ case CC0:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return;
+
+ case REG:
+ if (REGNO (in_this) >= FIRST_PSEUDO_REGISTER
+ && ! reg_mentioned_p (in_this, not_in_this))
+ *output = gen_rtx (EXPR_LIST, VOIDmode, in_this, *output);
+ return;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ int j;
+
+ switch (fmt[i])
+ {
+ case 'E':
+ for (j = 0; j < XVECLEN (in_this, i); j++)
+ record_excess_regs (XVECEXP (in_this, i, j), not_in_this, output);
+ break;
+
+ case 'e':
+ record_excess_regs (XEXP (in_this, i), not_in_this, output);
+ break;
+ }
+ }
+}
+
+/* Check what regs are referred to in the libcall block ending with INSN,
+ aside from those mentioned in the equivalent value.
+ If there are none, return 0.
+ If there are one or more, return an EXPR_LIST containing all of them. */
+
+static rtx
+libcall_other_reg (insn, equiv)
+ rtx insn, equiv;
+{
+ rtx note = find_reg_note (insn, REG_RETVAL, NULL_RTX);
+ rtx p = XEXP (note, 0);
+ rtx output = 0;
+
+ /* First, find all the regs used in the libcall block
+ that are not mentioned as inputs to the result. */
+
+ while (p != insn)
+ {
+ if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN
+ || GET_CODE (p) == CALL_INSN)
+ record_excess_regs (PATTERN (p), equiv, &output);
+ p = NEXT_INSN (p);
+ }
+
+ return output;
+}
+
+/* Return 1 if all uses of REG
+ are between INSN and the end of the basic block. */
+
+static int
+reg_in_basic_block_p (insn, reg)
+ rtx insn, reg;
+{
+ int regno = REGNO (reg);
+ rtx p;
+
+ if (regno_first_uid[regno] != INSN_UID (insn))
+ return 0;
+
+ /* Search this basic block for the already recorded last use of the reg. */
+ for (p = insn; p; p = NEXT_INSN (p))
+ {
+ switch (GET_CODE (p))
+ {
+ case NOTE:
+ break;
+
+ case INSN:
+ case CALL_INSN:
+ /* Ordinary insn: if this is the last use, we win. */
+ if (regno_last_uid[regno] == INSN_UID (p))
+ return 1;
+ break;
+
+ case JUMP_INSN:
+ /* Jump insn: if this is the last use, we win. */
+ if (regno_last_uid[regno] == INSN_UID (p))
+ return 1;
+ /* Otherwise, it's the end of the basic block, so we lose. */
+ return 0;
+
+ case CODE_LABEL:
+ case BARRIER:
+ /* It's the end of the basic block, so we lose. */
+ return 0;
+ }
+ }
+
+ /* The "last use" doesn't follow the "first use"?? */
+ abort ();
+}
+
+/* Compute the benefit of eliminating the insns in the block whose
+ last insn is LAST. This may be a group of insns used to compute a
+ value directly or can contain a library call. */
+
+static int
+libcall_benefit (last)
+ rtx last;
+{
+ rtx insn;
+ int benefit = 0;
+
+ for (insn = XEXP (find_reg_note (last, REG_RETVAL, NULL_RTX), 0);
+ insn != last; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == CALL_INSN)
+ benefit += 10; /* Assume at least this many insns in a library
+ routine. */
+ else if (GET_CODE (insn) == INSN
+ && GET_CODE (PATTERN (insn)) != USE
+ && GET_CODE (PATTERN (insn)) != CLOBBER)
+ benefit++;
+ }
+
+ return benefit;
+}
+
+/* Skip COUNT insns from INSN, counting library calls as 1 insn. */
+
+static rtx
+skip_consec_insns (insn, count)
+ rtx insn;
+ int count;
+{
+ for (; count > 0; count--)
+ {
+ rtx temp;
+
+ /* If first insn of libcall sequence, skip to end. */
+ /* Do this at start of loop, since INSN is guaranteed to
+ be an insn here. */
+ if (GET_CODE (insn) != NOTE
+ && (temp = find_reg_note (insn, REG_LIBCALL, NULL_RTX)))
+ insn = XEXP (temp, 0);
+
+ do insn = NEXT_INSN (insn);
+ while (GET_CODE (insn) == NOTE);
+ }
+
+ return insn;
+}
+
+/* Ignore any movable whose insn falls within a libcall
+ which is part of another movable.
+ We make use of the fact that the movable for the libcall value
+ was made later and so appears later on the chain. */
+
+static void
+ignore_some_movables (movables)
+ struct movable *movables;
+{
+ register struct movable *m, *m1;
+
+ for (m = movables; m; m = m->next)
+ {
+ /* Is this a movable for the value of a libcall? */
+ rtx note = find_reg_note (m->insn, REG_RETVAL, NULL_RTX);
+ if (note)
+ {
+ rtx insn;
+ /* Check for earlier movables inside that range,
+ and mark them invalid. We cannot use LUIDs here because
+ insns created by loop.c for prior loops don't have LUIDs.
+ Rather than reject all such insns from movables, we just
+ explicitly check each insn in the libcall (since invariant
+ libcalls aren't that common). */
+ for (insn = XEXP (note, 0); insn != m->insn; insn = NEXT_INSN (insn))
+ for (m1 = movables; m1 != m; m1 = m1->next)
+ if (m1->insn == insn)
+ m1->done = 1;
+ }
+ }
+}
+
+/* For each movable insn, see if the reg that it loads
+ leads when it dies right into another conditionally movable insn.
+ If so, record that the second insn "forces" the first one,
+ since the second can be moved only if the first is. */
+
+static void
+force_movables (movables)
+ struct movable *movables;
+{
+ register struct movable *m, *m1;
+ for (m1 = movables; m1; m1 = m1->next)
+ /* Omit this if moving just the (SET (REG) 0) of a zero-extend. */
+ if (!m1->partial && !m1->done)
+ {
+ int regno = m1->regno;
+ for (m = m1->next; m; m = m->next)
+ /* ??? Could this be a bug? What if CSE caused the
+ register of M1 to be used after this insn?
+ Since CSE does not update regno_last_uid,
+ this insn M->insn might not be where it dies.
+ But very likely this doesn't matter; what matters is
+ that M's reg is computed from M1's reg. */
+ if (INSN_UID (m->insn) == regno_last_uid[regno]
+ && !m->done)
+ break;
+ if (m != 0 && m->set_src == m1->set_dest
+ /* If m->consec, m->set_src isn't valid. */
+ && m->consec == 0)
+ m = 0;
+
+ /* Increase the priority of the moving the first insn
+ since it permits the second to be moved as well. */
+ if (m != 0)
+ {
+ m->forces = m1;
+ m1->lifetime += m->lifetime;
+ m1->savings += m1->savings;
+ }
+ }
+}
+
+/* Find invariant expressions that are equal and can be combined into
+ one register. */
+
+static void
+combine_movables (movables, nregs)
+ struct movable *movables;
+ int nregs;
+{
+ register struct movable *m;
+ char *matched_regs = (char *) alloca (nregs);
+ enum machine_mode mode;
+
+ /* Regs that are set more than once are not allowed to match
+ or be matched. I'm no longer sure why not. */
+ /* Perhaps testing m->consec_sets would be more appropriate here? */
+
+ for (m = movables; m; m = m->next)
+ if (m->match == 0 && n_times_used[m->regno] == 1 && !m->partial)
+ {
+ register struct movable *m1;
+ int regno = m->regno;
+
+ bzero (matched_regs, nregs);
+ matched_regs[regno] = 1;
+
+ for (m1 = movables; m1; m1 = m1->next)
+ if (m != m1 && m1->match == 0 && n_times_used[m1->regno] == 1
+ /* A reg used outside the loop mustn't be eliminated. */
+ && !m1->global
+ /* A reg used for zero-extending mustn't be eliminated. */
+ && !m1->partial
+ && (matched_regs[m1->regno]
+ ||
+ (
+ /* Can combine regs with different modes loaded from the
+ same constant only if the modes are the same or
+ if both are integer modes with M wider or the same
+ width as M1. The check for integer is redundant, but
+ safe, since the only case of differing destination
+ modes with equal sources is when both sources are
+ VOIDmode, i.e., CONST_INT. */
+ (GET_MODE (m->set_dest) == GET_MODE (m1->set_dest)
+ || (GET_MODE_CLASS (GET_MODE (m->set_dest)) == MODE_INT
+ && GET_MODE_CLASS (GET_MODE (m1->set_dest)) == MODE_INT
+ && (GET_MODE_BITSIZE (GET_MODE (m->set_dest))
+ >= GET_MODE_BITSIZE (GET_MODE (m1->set_dest)))))
+ /* See if the source of M1 says it matches M. */
+ && ((GET_CODE (m1->set_src) == REG
+ && matched_regs[REGNO (m1->set_src)])
+ || rtx_equal_for_loop_p (m->set_src, m1->set_src,
+ movables))))
+ && ((m->dependencies == m1->dependencies)
+ || rtx_equal_p (m->dependencies, m1->dependencies)))
+ {
+ m->lifetime += m1->lifetime;
+ m->savings += m1->savings;
+ m1->done = 1;
+ m1->match = m;
+ matched_regs[m1->regno] = 1;
+ }
+ }
+
+ /* Now combine the regs used for zero-extension.
+ This can be done for those not marked `global'
+ provided their lives don't overlap. */
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ {
+ register struct movable *m0 = 0;
+
+ /* Combine all the registers for extension from mode MODE.
+ Don't combine any that are used outside this loop. */
+ for (m = movables; m; m = m->next)
+ if (m->partial && ! m->global
+ && mode == GET_MODE (SET_SRC (PATTERN (NEXT_INSN (m->insn)))))
+ {
+ register struct movable *m1;
+ int first = uid_luid[regno_first_uid[m->regno]];
+ int last = uid_luid[regno_last_uid[m->regno]];
+
+ if (m0 == 0)
+ {
+ /* First one: don't check for overlap, just record it. */
+ m0 = m;
+ continue;
+ }
+
+ /* Make sure they extend to the same mode.
+ (Almost always true.) */
+ if (GET_MODE (m->set_dest) != GET_MODE (m0->set_dest))
+ continue;
+
+ /* We already have one: check for overlap with those
+ already combined together. */
+ for (m1 = movables; m1 != m; m1 = m1->next)
+ if (m1 == m0 || (m1->partial && m1->match == m0))
+ if (! (uid_luid[regno_first_uid[m1->regno]] > last
+ || uid_luid[regno_last_uid[m1->regno]] < first))
+ goto overlap;
+
+ /* No overlap: we can combine this with the others. */
+ m0->lifetime += m->lifetime;
+ m0->savings += m->savings;
+ m->done = 1;
+ m->match = m0;
+
+ overlap: ;
+ }
+ }
+}
+
+/* Return 1 if regs X and Y will become the same if moved. */
+
+static int
+regs_match_p (x, y, movables)
+ rtx x, y;
+ struct movable *movables;
+{
+ int xn = REGNO (x);
+ int yn = REGNO (y);
+ struct movable *mx, *my;
+
+ for (mx = movables; mx; mx = mx->next)
+ if (mx->regno == xn)
+ break;
+
+ for (my = movables; my; my = my->next)
+ if (my->regno == yn)
+ break;
+
+ return (mx && my
+ && ((mx->match == my->match && mx->match != 0)
+ || mx->match == my
+ || mx == my->match));
+}
+
+/* Return 1 if X and Y are identical-looking rtx's.
+ This is the Lisp function EQUAL for rtx arguments.
+
+ If two registers are matching movables or a movable register and an
+ equivalent constant, consider them equal. */
+
+static int
+rtx_equal_for_loop_p (x, y, movables)
+ rtx x, y;
+ struct movable *movables;
+{
+ register int i;
+ register int j;
+ register struct movable *m;
+ register enum rtx_code code;
+ register char *fmt;
+
+ if (x == y)
+ return 1;
+ if (x == 0 || y == 0)
+ return 0;
+
+ code = GET_CODE (x);
+
+ /* If we have a register and a constant, they may sometimes be
+ equal. */
+ if (GET_CODE (x) == REG && n_times_set[REGNO (x)] == -2
+ && CONSTANT_P (y))
+ for (m = movables; m; m = m->next)
+ if (m->move_insn && m->regno == REGNO (x)
+ && rtx_equal_p (m->set_src, y))
+ return 1;
+
+ else if (GET_CODE (y) == REG && n_times_set[REGNO (y)] == -2
+ && CONSTANT_P (x))
+ for (m = movables; m; m = m->next)
+ if (m->move_insn && m->regno == REGNO (y)
+ && rtx_equal_p (m->set_src, x))
+ return 1;
+
+ /* Otherwise, rtx's of different codes cannot be equal. */
+ if (code != GET_CODE (y))
+ return 0;
+
+ /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent.
+ (REG:SI x) and (REG:HI x) are NOT equivalent. */
+
+ if (GET_MODE (x) != GET_MODE (y))
+ return 0;
+
+ /* These three types of rtx's can be compared nonrecursively. */
+ if (code == REG)
+ return (REGNO (x) == REGNO (y) || regs_match_p (x, y, movables));
+
+ if (code == LABEL_REF)
+ return XEXP (x, 0) == XEXP (y, 0);
+ if (code == SYMBOL_REF)
+ return XSTR (x, 0) == XSTR (y, 0);
+
+ /* Compare the elements. If any pair of corresponding elements
+ fail to match, return 0 for the whole things. */
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ switch (fmt[i])
+ {
+ case 'w':
+ if (XWINT (x, i) != XWINT (y, i))
+ return 0;
+ break;
+
+ case 'i':
+ if (XINT (x, i) != XINT (y, i))
+ return 0;
+ break;
+
+ case 'E':
+ /* Two vectors must have the same length. */
+ if (XVECLEN (x, i) != XVECLEN (y, i))
+ return 0;
+
+ /* And the corresponding elements must match. */
+ for (j = 0; j < XVECLEN (x, i); j++)
+ if (rtx_equal_for_loop_p (XVECEXP (x, i, j), XVECEXP (y, i, j), movables) == 0)
+ return 0;
+ break;
+
+ case 'e':
+ if (rtx_equal_for_loop_p (XEXP (x, i), XEXP (y, i), movables) == 0)
+ return 0;
+ break;
+
+ case 's':
+ if (strcmp (XSTR (x, i), XSTR (y, i)))
+ return 0;
+ break;
+
+ case 'u':
+ /* These are just backpointers, so they don't matter. */
+ break;
+
+ case '0':
+ break;
+
+ /* It is believed that rtx's at this level will never
+ contain anything but integers and other rtx's,
+ except for within LABEL_REFs and SYMBOL_REFs. */
+ default:
+ abort ();
+ }
+ }
+ return 1;
+}
+
+/* If X contains any LABEL_REF's, add REG_LABEL notes for them to all
+ insns in INSNS which use thet reference. */
+
+static void
+add_label_notes (x, insns)
+ rtx x;
+ rtx insns;
+{
+ enum rtx_code code = GET_CODE (x);
+ int i, j;
+ char *fmt;
+ rtx insn;
+
+ if (code == LABEL_REF && !LABEL_REF_NONLOCAL_P (x))
+ {
+ rtx next = next_real_insn (XEXP (x, 0));
+
+ /* Don't record labels that refer to dispatch tables.
+ This is not necessary, since the tablejump references the same label.
+ And if we did record them, flow.c would make worse code. */
+ if (next == 0
+ || ! (GET_CODE (next) == JUMP_INSN
+ && (GET_CODE (PATTERN (next)) == ADDR_VEC
+ || GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC)))
+ {
+ for (insn = insns; insn; insn = NEXT_INSN (insn))
+ if (reg_mentioned_p (XEXP (x, 0), insn))
+ REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_LABEL, XEXP (x, 0),
+ REG_NOTES (insn));
+ }
+ return;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ add_label_notes (XEXP (x, i), insns);
+ else if (fmt[i] == 'E')
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ add_label_notes (XVECEXP (x, i, j), insns);
+ }
+}
+
+/* Scan MOVABLES, and move the insns that deserve to be moved.
+ If two matching movables are combined, replace one reg with the
+ other throughout. */
+
+static void
+move_movables (movables, threshold, insn_count, loop_start, end, nregs)
+ struct movable *movables;
+ int threshold;
+ int insn_count;
+ rtx loop_start;
+ rtx end;
+ int nregs;
+{
+ rtx new_start = 0;
+ register struct movable *m;
+ register rtx p;
+ /* Map of pseudo-register replacements to handle combining
+ when we move several insns that load the same value
+ into different pseudo-registers. */
+ rtx *reg_map = (rtx *) alloca (nregs * sizeof (rtx));
+ char *already_moved = (char *) alloca (nregs);
+
+ bzero (already_moved, nregs);
+ bzero ((char *) reg_map, nregs * sizeof (rtx));
+
+ num_movables = 0;
+
+ for (m = movables; m; m = m->next)
+ {
+ /* Describe this movable insn. */
+
+ if (loop_dump_stream)
+ {
+ fprintf (loop_dump_stream, "Insn %d: regno %d (life %d), ",
+ INSN_UID (m->insn), m->regno, m->lifetime);
+ if (m->consec > 0)
+ fprintf (loop_dump_stream, "consec %d, ", m->consec);
+ if (m->cond)
+ fprintf (loop_dump_stream, "cond ");
+ if (m->force)
+ fprintf (loop_dump_stream, "force ");
+ if (m->global)
+ fprintf (loop_dump_stream, "global ");
+ if (m->done)
+ fprintf (loop_dump_stream, "done ");
+ if (m->move_insn)
+ fprintf (loop_dump_stream, "move-insn ");
+ if (m->match)
+ fprintf (loop_dump_stream, "matches %d ",
+ INSN_UID (m->match->insn));
+ if (m->forces)
+ fprintf (loop_dump_stream, "forces %d ",
+ INSN_UID (m->forces->insn));
+ }
+
+ /* Count movables. Value used in heuristics in strength_reduce. */
+ num_movables++;
+
+ /* Ignore the insn if it's already done (it matched something else).
+ Otherwise, see if it is now safe to move. */
+
+ if (!m->done
+ && (! m->cond
+ || (1 == invariant_p (m->set_src)
+ && (m->dependencies == 0
+ || 1 == invariant_p (m->dependencies))
+ && (m->consec == 0
+ || 1 == consec_sets_invariant_p (m->set_dest,
+ m->consec + 1,
+ m->insn))))
+ && (! m->forces || m->forces->done))
+ {
+ register int regno;
+ register rtx p;
+ int savings = m->savings;
+
+ /* We have an insn that is safe to move.
+ Compute its desirability. */
+
+ p = m->insn;
+ regno = m->regno;
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "savings %d ", savings);
+
+ if (moved_once[regno])
+ {
+ insn_count *= 2;
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "halved since already moved ");
+ }
+
+ /* An insn MUST be moved if we already moved something else
+ which is safe only if this one is moved too: that is,
+ if already_moved[REGNO] is nonzero. */
+
+ /* An insn is desirable to move if the new lifetime of the
+ register is no more than THRESHOLD times the old lifetime.
+ If it's not desirable, it means the loop is so big
+ that moving won't speed things up much,
+ and it is liable to make register usage worse. */
+
+ /* It is also desirable to move if it can be moved at no
+ extra cost because something else was already moved. */
+
+ if (already_moved[regno]
+ || (threshold * savings * m->lifetime) >= insn_count
+ || (m->forces && m->forces->done
+ && n_times_used[m->forces->regno] == 1))
+ {
+ int count;
+ register struct movable *m1;
+ rtx first;
+
+ /* Now move the insns that set the reg. */
+
+ if (m->partial && m->match)
+ {
+ rtx newpat, i1;
+ rtx r1, r2;
+ /* Find the end of this chain of matching regs.
+ Thus, we load each reg in the chain from that one reg.
+ And that reg is loaded with 0 directly,
+ since it has ->match == 0. */
+ for (m1 = m; m1->match; m1 = m1->match);
+ newpat = gen_move_insn (SET_DEST (PATTERN (m->insn)),
+ SET_DEST (PATTERN (m1->insn)));
+ i1 = emit_insn_before (newpat, loop_start);
+
+ /* Mark the moved, invariant reg as being allowed to
+ share a hard reg with the other matching invariant. */
+ REG_NOTES (i1) = REG_NOTES (m->insn);
+ r1 = SET_DEST (PATTERN (m->insn));
+ r2 = SET_DEST (PATTERN (m1->insn));
+ regs_may_share = gen_rtx (EXPR_LIST, VOIDmode, r1,
+ gen_rtx (EXPR_LIST, VOIDmode, r2,
+ regs_may_share));
+ delete_insn (m->insn);
+
+ if (new_start == 0)
+ new_start = i1;
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, " moved to %d", INSN_UID (i1));
+ }
+ /* If we are to re-generate the item being moved with a
+ new move insn, first delete what we have and then emit
+ the move insn before the loop. */
+ else if (m->move_insn)
+ {
+ rtx i1, temp;
+
+ for (count = m->consec; count >= 0; count--)
+ {
+ /* If this is the first insn of a library call sequence,
+ skip to the end. */
+ if (GET_CODE (p) != NOTE
+ && (temp = find_reg_note (p, REG_LIBCALL, NULL_RTX)))
+ p = XEXP (temp, 0);
+
+ /* If this is the last insn of a libcall sequence, then
+ delete every insn in the sequence except the last.
+ The last insn is handled in the normal manner. */
+ if (GET_CODE (p) != NOTE
+ && (temp = find_reg_note (p, REG_RETVAL, NULL_RTX)))
+ {
+ temp = XEXP (temp, 0);
+ while (temp != p)
+ temp = delete_insn (temp);
+ }
+
+ p = delete_insn (p);
+ }
+
+ start_sequence ();
+ emit_move_insn (m->set_dest, m->set_src);
+ temp = get_insns ();
+ end_sequence ();
+
+ add_label_notes (m->set_src, temp);
+
+ i1 = emit_insns_before (temp, loop_start);
+ if (! find_reg_note (i1, REG_EQUAL, NULL_RTX))
+ REG_NOTES (i1)
+ = gen_rtx (EXPR_LIST,
+ m->is_equiv ? REG_EQUIV : REG_EQUAL,
+ m->set_src, REG_NOTES (i1));
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, " moved to %d", INSN_UID (i1));
+
+ /* The more regs we move, the less we like moving them. */
+ threshold -= 3;
+ }
+ else
+ {
+ for (count = m->consec; count >= 0; count--)
+ {
+ rtx i1, temp;
+
+ /* If first insn of libcall sequence, skip to end. */
+ /* Do this at start of loop, since p is guaranteed to
+ be an insn here. */
+ if (GET_CODE (p) != NOTE
+ && (temp = find_reg_note (p, REG_LIBCALL, NULL_RTX)))
+ p = XEXP (temp, 0);
+
+ /* If last insn of libcall sequence, move all
+ insns except the last before the loop. The last
+ insn is handled in the normal manner. */
+ if (GET_CODE (p) != NOTE
+ && (temp = find_reg_note (p, REG_RETVAL, NULL_RTX)))
+ {
+ rtx fn_address = 0;
+ rtx fn_reg = 0;
+ rtx fn_address_insn = 0;
+
+ first = 0;
+ for (temp = XEXP (temp, 0); temp != p;
+ temp = NEXT_INSN (temp))
+ {
+ rtx body;
+ rtx n;
+ rtx next;
+
+ if (GET_CODE (temp) == NOTE)
+ continue;
+
+ body = PATTERN (temp);
+
+ /* Find the next insn after TEMP,
+ not counting USE or NOTE insns. */
+ for (next = NEXT_INSN (temp); next != p;
+ next = NEXT_INSN (next))
+ if (! (GET_CODE (next) == INSN
+ && GET_CODE (PATTERN (next)) == USE)
+ && GET_CODE (next) != NOTE)
+ break;
+
+ /* If that is the call, this may be the insn
+ that loads the function address.
+
+ Extract the function address from the insn
+ that loads it into a register.
+ If this insn was cse'd, we get incorrect code.
+
+ So emit a new move insn that copies the
+ function address into the register that the
+ call insn will use. flow.c will delete any
+ redundant stores that we have created. */
+ if (GET_CODE (next) == CALL_INSN
+ && GET_CODE (body) == SET
+ && GET_CODE (SET_DEST (body)) == REG
+ && (n = find_reg_note (temp, REG_EQUAL,
+ NULL_RTX)))
+ {
+ fn_reg = SET_SRC (body);
+ if (GET_CODE (fn_reg) != REG)
+ fn_reg = SET_DEST (body);
+ fn_address = XEXP (n, 0);
+ fn_address_insn = temp;
+ }
+ /* We have the call insn.
+ If it uses the register we suspect it might,
+ load it with the correct address directly. */
+ if (GET_CODE (temp) == CALL_INSN
+ && fn_address != 0
+ && reg_referenced_p (fn_reg, body))
+ emit_insn_after (gen_move_insn (fn_reg,
+ fn_address),
+ fn_address_insn);
+
+ if (GET_CODE (temp) == CALL_INSN)
+ {
+ i1 = emit_call_insn_before (body, loop_start);
+ /* Because the USAGE information potentially
+ contains objects other than hard registers
+ we need to copy it. */
+ CALL_INSN_FUNCTION_USAGE (i1) =
+ copy_rtx (CALL_INSN_FUNCTION_USAGE (temp));
+ }
+ else
+ i1 = emit_insn_before (body, loop_start);
+ if (first == 0)
+ first = i1;
+ if (temp == fn_address_insn)
+ fn_address_insn = i1;
+ REG_NOTES (i1) = REG_NOTES (temp);
+ delete_insn (temp);
+ }
+ }
+ if (m->savemode != VOIDmode)
+ {
+ /* P sets REG to zero; but we should clear only
+ the bits that are not covered by the mode
+ m->savemode. */
+ rtx reg = m->set_dest;
+ rtx sequence;
+ rtx tem;
+
+ start_sequence ();
+ tem = expand_binop
+ (GET_MODE (reg), and_optab, reg,
+ GEN_INT ((((HOST_WIDE_INT) 1
+ << GET_MODE_BITSIZE (m->savemode)))
+ - 1),
+ reg, 1, OPTAB_LIB_WIDEN);
+ if (tem == 0)
+ abort ();
+ if (tem != reg)
+ emit_move_insn (reg, tem);
+ sequence = gen_sequence ();
+ end_sequence ();
+ i1 = emit_insn_before (sequence, loop_start);
+ }
+ else if (GET_CODE (p) == CALL_INSN)
+ {
+ i1 = emit_call_insn_before (PATTERN (p), loop_start);
+ /* Because the USAGE information potentially
+ contains objects other than hard registers
+ we need to copy it. */
+ CALL_INSN_FUNCTION_USAGE (i1) =
+ copy_rtx (CALL_INSN_FUNCTION_USAGE (p));
+ }
+ else
+ i1 = emit_insn_before (PATTERN (p), loop_start);
+
+ REG_NOTES (i1) = REG_NOTES (p);
+
+ /* If there is a REG_EQUAL note present whose value is
+ not loop invariant, then delete it, since it may
+ cause problems with later optimization passes.
+ It is possible for cse to create such notes
+ like this as a result of record_jump_cond. */
+
+ if ((temp = find_reg_note (i1, REG_EQUAL, NULL_RTX))
+ && ! invariant_p (XEXP (temp, 0)))
+ remove_note (i1, temp);
+
+ if (new_start == 0)
+ new_start = i1;
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, " moved to %d",
+ INSN_UID (i1));
+
+#if 0
+ /* This isn't needed because REG_NOTES is copied
+ below and is wrong since P might be a PARALLEL. */
+ if (REG_NOTES (i1) == 0
+ && ! m->partial /* But not if it's a zero-extend clr. */
+ && ! m->global /* and not if used outside the loop
+ (since it might get set outside). */
+ && CONSTANT_P (SET_SRC (PATTERN (p))))
+ REG_NOTES (i1)
+ = gen_rtx (EXPR_LIST, REG_EQUAL,
+ SET_SRC (PATTERN (p)), REG_NOTES (i1));
+#endif
+
+ /* If library call, now fix the REG_NOTES that contain
+ insn pointers, namely REG_LIBCALL on FIRST
+ and REG_RETVAL on I1. */
+ if (temp = find_reg_note (i1, REG_RETVAL, NULL_RTX))
+ {
+ XEXP (temp, 0) = first;
+ temp = find_reg_note (first, REG_LIBCALL, NULL_RTX);
+ XEXP (temp, 0) = i1;
+ }
+
+ delete_insn (p);
+ do p = NEXT_INSN (p);
+ while (p && GET_CODE (p) == NOTE);
+ }
+
+ /* The more regs we move, the less we like moving them. */
+ threshold -= 3;
+ }
+
+ /* Any other movable that loads the same register
+ MUST be moved. */
+ already_moved[regno] = 1;
+
+ /* This reg has been moved out of one loop. */
+ moved_once[regno] = 1;
+
+ /* The reg set here is now invariant. */
+ if (! m->partial)
+ n_times_set[regno] = 0;
+
+ m->done = 1;
+
+ /* Change the length-of-life info for the register
+ to say it lives at least the full length of this loop.
+ This will help guide optimizations in outer loops. */
+
+ if (uid_luid[regno_first_uid[regno]] > INSN_LUID (loop_start))
+ /* This is the old insn before all the moved insns.
+ We can't use the moved insn because it is out of range
+ in uid_luid. Only the old insns have luids. */
+ regno_first_uid[regno] = INSN_UID (loop_start);
+ if (uid_luid[regno_last_uid[regno]] < INSN_LUID (end))
+ regno_last_uid[regno] = INSN_UID (end);
+
+ /* Combine with this moved insn any other matching movables. */
+
+ if (! m->partial)
+ for (m1 = movables; m1; m1 = m1->next)
+ if (m1->match == m)
+ {
+ rtx temp;
+
+ /* Schedule the reg loaded by M1
+ for replacement so that shares the reg of M.
+ If the modes differ (only possible in restricted
+ circumstances, make a SUBREG. */
+ if (GET_MODE (m->set_dest) == GET_MODE (m1->set_dest))
+ reg_map[m1->regno] = m->set_dest;
+ else
+ reg_map[m1->regno]
+ = gen_lowpart_common (GET_MODE (m1->set_dest),
+ m->set_dest);
+
+ /* Get rid of the matching insn
+ and prevent further processing of it. */
+ m1->done = 1;
+
+ /* if library call, delete all insn except last, which
+ is deleted below */
+ if (temp = find_reg_note (m1->insn, REG_RETVAL,
+ NULL_RTX))
+ {
+ for (temp = XEXP (temp, 0); temp != m1->insn;
+ temp = NEXT_INSN (temp))
+ delete_insn (temp);
+ }
+ delete_insn (m1->insn);
+
+ /* Any other movable that loads the same register
+ MUST be moved. */
+ already_moved[m1->regno] = 1;
+
+ /* The reg merged here is now invariant,
+ if the reg it matches is invariant. */
+ if (! m->partial)
+ n_times_set[m1->regno] = 0;
+ }
+ }
+ else if (loop_dump_stream)
+ fprintf (loop_dump_stream, "not desirable");
+ }
+ else if (loop_dump_stream && !m->match)
+ fprintf (loop_dump_stream, "not safe");
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "\n");
+ }
+
+ if (new_start == 0)
+ new_start = loop_start;
+
+ /* Go through all the instructions in the loop, making
+ all the register substitutions scheduled in REG_MAP. */
+ for (p = new_start; p != end; p = NEXT_INSN (p))
+ if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN
+ || GET_CODE (p) == CALL_INSN)
+ {
+ replace_regs (PATTERN (p), reg_map, nregs, 0);
+ replace_regs (REG_NOTES (p), reg_map, nregs, 0);
+ INSN_CODE (p) = -1;
+ }
+}
+
+#if 0
+/* Scan X and replace the address of any MEM in it with ADDR.
+ REG is the address that MEM should have before the replacement. */
+
+static void
+replace_call_address (x, reg, addr)
+ rtx x, reg, addr;
+{
+ register enum rtx_code code;
+ register int i;
+ register char *fmt;
+
+ if (x == 0)
+ return;
+ code = GET_CODE (x);
+ switch (code)
+ {
+ case PC:
+ case CC0:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case REG:
+ return;
+
+ case SET:
+ /* Short cut for very common case. */
+ replace_call_address (XEXP (x, 1), reg, addr);
+ return;
+
+ case CALL:
+ /* Short cut for very common case. */
+ replace_call_address (XEXP (x, 0), reg, addr);
+ return;
+
+ case MEM:
+ /* If this MEM uses a reg other than the one we expected,
+ something is wrong. */
+ if (XEXP (x, 0) != reg)
+ abort ();
+ XEXP (x, 0) = addr;
+ return;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ replace_call_address (XEXP (x, i), reg, addr);
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ replace_call_address (XVECEXP (x, i, j), reg, addr);
+ }
+ }
+}
+#endif
+
+/* Return the number of memory refs to addresses that vary
+ in the rtx X. */
+
+static int
+count_nonfixed_reads (x)
+ rtx x;
+{
+ register enum rtx_code code;
+ register int i;
+ register char *fmt;
+ int value;
+
+ if (x == 0)
+ return 0;
+
+ code = GET_CODE (x);
+ switch (code)
+ {
+ case PC:
+ case CC0:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case REG:
+ return 0;
+
+ case MEM:
+ return ((invariant_p (XEXP (x, 0)) != 1)
+ + count_nonfixed_reads (XEXP (x, 0)));
+ }
+
+ value = 0;
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ value += count_nonfixed_reads (XEXP (x, i));
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ value += count_nonfixed_reads (XVECEXP (x, i, j));
+ }
+ }
+ return value;
+}
+
+
+#if 0
+/* P is an instruction that sets a register to the result of a ZERO_EXTEND.
+ Replace it with an instruction to load just the low bytes
+ if the machine supports such an instruction,
+ and insert above LOOP_START an instruction to clear the register. */
+
+static void
+constant_high_bytes (p, loop_start)
+ rtx p, loop_start;
+{
+ register rtx new;
+ register int insn_code_number;
+
+ /* Try to change (SET (REG ...) (ZERO_EXTEND (..:B ...)))
+ to (SET (STRICT_LOW_PART (SUBREG:B (REG...))) ...). */
+
+ new = gen_rtx (SET, VOIDmode,
+ gen_rtx (STRICT_LOW_PART, VOIDmode,
+ gen_rtx (SUBREG, GET_MODE (XEXP (SET_SRC (PATTERN (p)), 0)),
+ SET_DEST (PATTERN (p)),
+ 0)),
+ XEXP (SET_SRC (PATTERN (p)), 0));
+ insn_code_number = recog (new, p);
+
+ if (insn_code_number)
+ {
+ register int i;
+
+ /* Clear destination register before the loop. */
+ emit_insn_before (gen_rtx (SET, VOIDmode,
+ SET_DEST (PATTERN (p)),
+ const0_rtx),
+ loop_start);
+
+ /* Inside the loop, just load the low part. */
+ PATTERN (p) = new;
+ }
+}
+#endif
+
+/* Scan a loop setting the variables `unknown_address_altered',
+ `num_mem_sets', `loop_continue', loops_enclosed', `loop_has_call',
+ and `loop_has_volatile'.
+ Also, fill in the array `loop_store_mems'. */
+
+static void
+prescan_loop (start, end)
+ rtx start, end;
+{
+ register int level = 1;
+ register rtx insn;
+
+ unknown_address_altered = 0;
+ loop_has_call = 0;
+ loop_has_volatile = 0;
+ loop_store_mems_idx = 0;
+
+ num_mem_sets = 0;
+ loops_enclosed = 1;
+ loop_continue = 0;
+
+ for (insn = NEXT_INSN (start); insn != NEXT_INSN (end);
+ insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == NOTE)
+ {
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
+ {
+ ++level;
+ /* Count number of loops contained in this one. */
+ loops_enclosed++;
+ }
+ else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
+ {
+ --level;
+ if (level == 0)
+ {
+ end = insn;
+ break;
+ }
+ }
+ else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_CONT)
+ {
+ if (level == 1)
+ loop_continue = insn;
+ }
+ }
+ else if (GET_CODE (insn) == CALL_INSN)
+ {
+ unknown_address_altered = 1;
+ loop_has_call = 1;
+ }
+ else
+ {
+ if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN)
+ {
+ if (volatile_refs_p (PATTERN (insn)))
+ loop_has_volatile = 1;
+
+ note_stores (PATTERN (insn), note_addr_stored);
+ }
+ }
+ }
+}
+
+/* Scan the function looking for loops. Record the start and end of each loop.
+ Also mark as invalid loops any loops that contain a setjmp or are branched
+ to from outside the loop. */
+
+static void
+find_and_verify_loops (f)
+ rtx f;
+{
+ rtx insn, label;
+ int current_loop = -1;
+ int next_loop = -1;
+ int loop;
+
+ /* If there are jumps to undefined labels,
+ treat them as jumps out of any/all loops.
+ This also avoids writing past end of tables when there are no loops. */
+ uid_loop_num[0] = -1;
+
+ /* Find boundaries of loops, mark which loops are contained within
+ loops, and invalidate loops that have setjmp. */
+
+ for (insn = f; insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == NOTE)
+ switch (NOTE_LINE_NUMBER (insn))
+ {
+ case NOTE_INSN_LOOP_BEG:
+ loop_number_loop_starts[++next_loop] = insn;
+ loop_number_loop_ends[next_loop] = 0;
+ loop_outer_loop[next_loop] = current_loop;
+ loop_invalid[next_loop] = 0;
+ loop_number_exit_labels[next_loop] = 0;
+ current_loop = next_loop;
+ break;
+
+ case NOTE_INSN_SETJMP:
+ /* In this case, we must invalidate our current loop and any
+ enclosing loop. */
+ for (loop = current_loop; loop != -1; loop = loop_outer_loop[loop])
+ {
+ loop_invalid[loop] = 1;
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "\nLoop at %d ignored due to setjmp.\n",
+ INSN_UID (loop_number_loop_starts[loop]));
+ }
+ break;
+
+ case NOTE_INSN_LOOP_END:
+ if (current_loop == -1)
+ abort ();
+
+ loop_number_loop_ends[current_loop] = insn;
+ current_loop = loop_outer_loop[current_loop];
+ break;
+
+ }
+
+ /* Note that this will mark the NOTE_INSN_LOOP_END note as being in the
+ enclosing loop, but this doesn't matter. */
+ uid_loop_num[INSN_UID (insn)] = current_loop;
+ }
+
+ /* Any loop containing a label used in an initializer must be invalidated,
+ because it can be jumped into from anywhere. */
+
+ for (label = forced_labels; label; label = XEXP (label, 1))
+ {
+ int loop_num;
+
+ for (loop_num = uid_loop_num[INSN_UID (XEXP (label, 0))];
+ loop_num != -1;
+ loop_num = loop_outer_loop[loop_num])
+ loop_invalid[loop_num] = 1;
+ }
+
+ /* Now scan all insn's in the function. If any JUMP_INSN branches into a
+ loop that it is not contained within, that loop is marked invalid.
+ If any INSN or CALL_INSN uses a label's address, then the loop containing
+ that label is marked invalid, because it could be jumped into from
+ anywhere.
+
+ Also look for blocks of code ending in an unconditional branch that
+ exits the loop. If such a block is surrounded by a conditional
+ branch around the block, move the block elsewhere (see below) and
+ invert the jump to point to the code block. This may eliminate a
+ label in our loop and will simplify processing by both us and a
+ possible second cse pass. */
+
+ for (insn = f; insn; insn = NEXT_INSN (insn))
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ int this_loop_num = uid_loop_num[INSN_UID (insn)];
+
+ if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
+ {
+ rtx note = find_reg_note (insn, REG_LABEL, NULL_RTX);
+ if (note)
+ {
+ int loop_num;
+
+ for (loop_num = uid_loop_num[INSN_UID (XEXP (note, 0))];
+ loop_num != -1;
+ loop_num = loop_outer_loop[loop_num])
+ loop_invalid[loop_num] = 1;
+ }
+ }
+
+ if (GET_CODE (insn) != JUMP_INSN)
+ continue;
+
+ mark_loop_jump (PATTERN (insn), this_loop_num);
+
+ /* See if this is an unconditional branch outside the loop. */
+ if (this_loop_num != -1
+ && (GET_CODE (PATTERN (insn)) == RETURN
+ || (simplejump_p (insn)
+ && (uid_loop_num[INSN_UID (JUMP_LABEL (insn))]
+ != this_loop_num)))
+ && get_max_uid () < max_uid_for_loop)
+ {
+ rtx p;
+ rtx our_next = next_real_insn (insn);
+
+ /* Go backwards until we reach the start of the loop, a label,
+ or a JUMP_INSN. */
+ for (p = PREV_INSN (insn);
+ GET_CODE (p) != CODE_LABEL
+ && ! (GET_CODE (p) == NOTE
+ && NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG)
+ && GET_CODE (p) != JUMP_INSN;
+ p = PREV_INSN (p))
+ ;
+
+ /* If we stopped on a JUMP_INSN to the next insn after INSN,
+ we have a block of code to try to move.
+
+ We look backward and then forward from the target of INSN
+ to find a BARRIER at the same loop depth as the target.
+ If we find such a BARRIER, we make a new label for the start
+ of the block, invert the jump in P and point it to that label,
+ and move the block of code to the spot we found. */
+
+ if (GET_CODE (p) == JUMP_INSN
+ && JUMP_LABEL (p) != 0
+ /* Just ignore jumps to labels that were never emitted.
+ These always indicate compilation errors. */
+ && INSN_UID (JUMP_LABEL (p)) != 0
+ && condjump_p (p)
+ && ! simplejump_p (p)
+ && next_real_insn (JUMP_LABEL (p)) == our_next)
+ {
+ rtx target
+ = JUMP_LABEL (insn) ? JUMP_LABEL (insn) : get_last_insn ();
+ int target_loop_num = uid_loop_num[INSN_UID (target)];
+ rtx loc;
+
+ for (loc = target; loc; loc = PREV_INSN (loc))
+ if (GET_CODE (loc) == BARRIER
+ && uid_loop_num[INSN_UID (loc)] == target_loop_num)
+ break;
+
+ if (loc == 0)
+ for (loc = target; loc; loc = NEXT_INSN (loc))
+ if (GET_CODE (loc) == BARRIER
+ && uid_loop_num[INSN_UID (loc)] == target_loop_num)
+ break;
+
+ if (loc)
+ {
+ rtx cond_label = JUMP_LABEL (p);
+ rtx new_label = get_label_after (p);
+
+ /* Ensure our label doesn't go away. */
+ LABEL_NUSES (cond_label)++;
+
+ /* Verify that uid_loop_num is large enough and that
+ we can invert P. */
+ if (invert_jump (p, new_label))
+ {
+ rtx q, r;
+
+ /* Include the BARRIER after INSN and copy the
+ block after LOC. */
+ new_label = squeeze_notes (new_label, NEXT_INSN (insn));
+ reorder_insns (new_label, NEXT_INSN (insn), loc);
+
+ /* All those insns are now in TARGET_LOOP_NUM. */
+ for (q = new_label; q != NEXT_INSN (NEXT_INSN (insn));
+ q = NEXT_INSN (q))
+ uid_loop_num[INSN_UID (q)] = target_loop_num;
+
+ /* The label jumped to by INSN is no longer a loop exit.
+ Unless INSN does not have a label (e.g., it is a
+ RETURN insn), search loop_number_exit_labels to find
+ its label_ref, and remove it. Also turn off
+ LABEL_OUTSIDE_LOOP_P bit. */
+ if (JUMP_LABEL (insn))
+ {
+ for (q = 0,
+ r = loop_number_exit_labels[this_loop_num];
+ r; q = r, r = LABEL_NEXTREF (r))
+ if (XEXP (r, 0) == JUMP_LABEL (insn))
+ {
+ LABEL_OUTSIDE_LOOP_P (r) = 0;
+ if (q)
+ LABEL_NEXTREF (q) = LABEL_NEXTREF (r);
+ else
+ loop_number_exit_labels[this_loop_num]
+ = LABEL_NEXTREF (r);
+ break;
+ }
+
+ /* If we didn't find it, then something is wrong. */
+ if (! r)
+ abort ();
+ }
+
+ /* P is now a jump outside the loop, so it must be put
+ in loop_number_exit_labels, and marked as such.
+ The easiest way to do this is to just call
+ mark_loop_jump again for P. */
+ mark_loop_jump (PATTERN (p), this_loop_num);
+
+ /* If INSN now jumps to the insn after it,
+ delete INSN. */
+ if (JUMP_LABEL (insn) != 0
+ && (next_real_insn (JUMP_LABEL (insn))
+ == next_real_insn (insn)))
+ delete_insn (insn);
+ }
+
+ /* Continue the loop after where the conditional
+ branch used to jump, since the only branch insn
+ in the block (if it still remains) is an inter-loop
+ branch and hence needs no processing. */
+ insn = NEXT_INSN (cond_label);
+
+ if (--LABEL_NUSES (cond_label) == 0)
+ delete_insn (cond_label);
+
+ /* This loop will be continued with NEXT_INSN (insn). */
+ insn = PREV_INSN (insn);
+ }
+ }
+ }
+ }
+}
+
+/* If any label in X jumps to a loop different from LOOP_NUM and any of the
+ loops it is contained in, mark the target loop invalid.
+
+ For speed, we assume that X is part of a pattern of a JUMP_INSN. */
+
+static void
+mark_loop_jump (x, loop_num)
+ rtx x;
+ int loop_num;
+{
+ int dest_loop;
+ int outer_loop;
+ int i;
+
+ switch (GET_CODE (x))
+ {
+ case PC:
+ case USE:
+ case CLOBBER:
+ case REG:
+ case MEM:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case RETURN:
+ return;
+
+ case CONST:
+ /* There could be a label reference in here. */
+ mark_loop_jump (XEXP (x, 0), loop_num);
+ return;
+
+ case PLUS:
+ case MINUS:
+ case MULT:
+ mark_loop_jump (XEXP (x, 0), loop_num);
+ mark_loop_jump (XEXP (x, 1), loop_num);
+ return;
+
+ case SIGN_EXTEND:
+ case ZERO_EXTEND:
+ mark_loop_jump (XEXP (x, 0), loop_num);
+ return;
+
+ case LABEL_REF:
+ dest_loop = uid_loop_num[INSN_UID (XEXP (x, 0))];
+
+ /* Link together all labels that branch outside the loop. This
+ is used by final_[bg]iv_value and the loop unrolling code. Also
+ mark this LABEL_REF so we know that this branch should predict
+ false. */
+
+ if (dest_loop != loop_num && loop_num != -1)
+ {
+ LABEL_OUTSIDE_LOOP_P (x) = 1;
+ LABEL_NEXTREF (x) = loop_number_exit_labels[loop_num];
+ loop_number_exit_labels[loop_num] = x;
+ }
+
+ /* If this is inside a loop, but not in the current loop or one enclosed
+ by it, it invalidates at least one loop. */
+
+ if (dest_loop == -1)
+ return;
+
+ /* We must invalidate every nested loop containing the target of this
+ label, except those that also contain the jump insn. */
+
+ for (; dest_loop != -1; dest_loop = loop_outer_loop[dest_loop])
+ {
+ /* Stop when we reach a loop that also contains the jump insn. */
+ for (outer_loop = loop_num; outer_loop != -1;
+ outer_loop = loop_outer_loop[outer_loop])
+ if (dest_loop == outer_loop)
+ return;
+
+ /* If we get here, we know we need to invalidate a loop. */
+ if (loop_dump_stream && ! loop_invalid[dest_loop])
+ fprintf (loop_dump_stream,
+ "\nLoop at %d ignored due to multiple entry points.\n",
+ INSN_UID (loop_number_loop_starts[dest_loop]));
+
+ loop_invalid[dest_loop] = 1;
+ }
+ return;
+
+ case SET:
+ /* If this is not setting pc, ignore. */
+ if (SET_DEST (x) == pc_rtx)
+ mark_loop_jump (SET_SRC (x), loop_num);
+ return;
+
+ case IF_THEN_ELSE:
+ mark_loop_jump (XEXP (x, 1), loop_num);
+ mark_loop_jump (XEXP (x, 2), loop_num);
+ return;
+
+ case PARALLEL:
+ case ADDR_VEC:
+ for (i = 0; i < XVECLEN (x, 0); i++)
+ mark_loop_jump (XVECEXP (x, 0, i), loop_num);
+ return;
+
+ case ADDR_DIFF_VEC:
+ for (i = 0; i < XVECLEN (x, 1); i++)
+ mark_loop_jump (XVECEXP (x, 1, i), loop_num);
+ return;
+
+ default:
+ /* Treat anything else (such as a symbol_ref)
+ as a branch out of this loop, but not into any loop. */
+
+ if (loop_num != -1)
+ {
+ LABEL_OUTSIDE_LOOP_P (x) = 1;
+ LABEL_NEXTREF (x) = loop_number_exit_labels[loop_num];
+ loop_number_exit_labels[loop_num] = x;
+ }
+
+ return;
+ }
+}
+
+/* Return nonzero if there is a label in the range from
+ insn INSN to and including the insn whose luid is END
+ INSN must have an assigned luid (i.e., it must not have
+ been previously created by loop.c). */
+
+static int
+labels_in_range_p (insn, end)
+ rtx insn;
+ int end;
+{
+ while (insn && INSN_LUID (insn) <= end)
+ {
+ if (GET_CODE (insn) == CODE_LABEL)
+ return 1;
+ insn = NEXT_INSN (insn);
+ }
+
+ return 0;
+}
+
+/* Record that a memory reference X is being set. */
+
+static void
+note_addr_stored (x)
+ rtx x;
+{
+ register int i;
+
+ if (x == 0 || GET_CODE (x) != MEM)
+ return;
+
+ /* Count number of memory writes.
+ This affects heuristics in strength_reduce. */
+ num_mem_sets++;
+
+ /* BLKmode MEM means all memory is clobbered. */
+ if (GET_MODE (x) == BLKmode)
+ unknown_address_altered = 1;
+
+ if (unknown_address_altered)
+ return;
+
+ for (i = 0; i < loop_store_mems_idx; i++)
+ if (rtx_equal_p (XEXP (loop_store_mems[i], 0), XEXP (x, 0))
+ && MEM_IN_STRUCT_P (x) == MEM_IN_STRUCT_P (loop_store_mems[i]))
+ {
+ /* We are storing at the same address as previously noted. Save the
+ wider reference. */
+ if (GET_MODE_SIZE (GET_MODE (x))
+ > GET_MODE_SIZE (GET_MODE (loop_store_mems[i])))
+ loop_store_mems[i] = x;
+ break;
+ }
+
+ if (i == NUM_STORES)
+ unknown_address_altered = 1;
+
+ else if (i == loop_store_mems_idx)
+ loop_store_mems[loop_store_mems_idx++] = x;
+}
+
+/* Return nonzero if the rtx X is invariant over the current loop.
+
+ The value is 2 if we refer to something only conditionally invariant.
+
+ If `unknown_address_altered' is nonzero, no memory ref is invariant.
+ Otherwise, a memory ref is invariant if it does not conflict with
+ anything stored in `loop_store_mems'. */
+
+int
+invariant_p (x)
+ register rtx x;
+{
+ register int i;
+ register enum rtx_code code;
+ register char *fmt;
+ int conditional = 0;
+
+ if (x == 0)
+ return 1;
+ code = GET_CODE (x);
+ switch (code)
+ {
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case CONST:
+ return 1;
+
+ case LABEL_REF:
+ /* A LABEL_REF is normally invariant, however, if we are unrolling
+ loops, and this label is inside the loop, then it isn't invariant.
+ This is because each unrolled copy of the loop body will have
+ a copy of this label. If this was invariant, then an insn loading
+ the address of this label into a register might get moved outside
+ the loop, and then each loop body would end up using the same label.
+
+ We don't know the loop bounds here though, so just fail for all
+ labels. */
+ if (flag_unroll_loops)
+ return 0;
+ else
+ return 1;
+
+ case PC:
+ case CC0:
+ case UNSPEC_VOLATILE:
+ return 0;
+
+ case REG:
+ /* We used to check RTX_UNCHANGING_P (x) here, but that is invalid
+ since the reg might be set by initialization within the loop. */
+ if (x == frame_pointer_rtx || x == hard_frame_pointer_rtx
+ || x == arg_pointer_rtx)
+ return 1;
+ if (loop_has_call
+ && REGNO (x) < FIRST_PSEUDO_REGISTER && call_used_regs[REGNO (x)])
+ return 0;
+ if (n_times_set[REGNO (x)] < 0)
+ return 2;
+ return n_times_set[REGNO (x)] == 0;
+
+ case MEM:
+ /* Read-only items (such as constants in a constant pool) are
+ invariant if their address is. */
+ if (RTX_UNCHANGING_P (x))
+ break;
+
+ /* If we filled the table (or had a subroutine call), any location
+ in memory could have been clobbered. */
+ if (unknown_address_altered
+ /* Don't mess with volatile memory references. */
+ || MEM_VOLATILE_P (x))
+ return 0;
+
+ /* See if there is any dependence between a store and this load. */
+ for (i = loop_store_mems_idx - 1; i >= 0; i--)
+ if (true_dependence (loop_store_mems[i], x))
+ return 0;
+
+ /* It's not invalidated by a store in memory
+ but we must still verify the address is invariant. */
+ break;
+
+ case ASM_OPERANDS:
+ /* Don't mess with insns declared volatile. */
+ if (MEM_VOLATILE_P (x))
+ return 0;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ {
+ int tem = invariant_p (XEXP (x, i));
+ if (tem == 0)
+ return 0;
+ if (tem == 2)
+ conditional = 1;
+ }
+ else if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ {
+ int tem = invariant_p (XVECEXP (x, i, j));
+ if (tem == 0)
+ return 0;
+ if (tem == 2)
+ conditional = 1;
+ }
+
+ }
+ }
+
+ return 1 + conditional;
+}
+
+
+/* Return nonzero if all the insns in the loop that set REG
+ are INSN and the immediately following insns,
+ and if each of those insns sets REG in an invariant way
+ (not counting uses of REG in them).
+
+ The value is 2 if some of these insns are only conditionally invariant.
+
+ We assume that INSN itself is the first set of REG
+ and that its source is invariant. */
+
+static int
+consec_sets_invariant_p (reg, n_sets, insn)
+ int n_sets;
+ rtx reg, insn;
+{
+ register rtx p = insn;
+ register int regno = REGNO (reg);
+ rtx temp;
+ /* Number of sets we have to insist on finding after INSN. */
+ int count = n_sets - 1;
+ int old = n_times_set[regno];
+ int value = 0;
+ int this;
+
+ /* If N_SETS hit the limit, we can't rely on its value. */
+ if (n_sets == 127)
+ return 0;
+
+ n_times_set[regno] = 0;
+
+ while (count > 0)
+ {
+ register enum rtx_code code;
+ rtx set;
+
+ p = NEXT_INSN (p);
+ code = GET_CODE (p);
+
+ /* If library call, skip to end of of it. */
+ if (code == INSN && (temp = find_reg_note (p, REG_LIBCALL, NULL_RTX)))
+ p = XEXP (temp, 0);
+
+ this = 0;
+ if (code == INSN
+ && (set = single_set (p))
+ && GET_CODE (SET_DEST (set)) == REG
+ && REGNO (SET_DEST (set)) == regno)
+ {
+ this = invariant_p (SET_SRC (set));
+ if (this != 0)
+ value |= this;
+ else if (temp = find_reg_note (p, REG_EQUAL, NULL_RTX))
+ {
+ /* If this is a libcall, then any invariant REG_EQUAL note is OK.
+ If this is an ordinary insn, then only CONSTANT_P REG_EQUAL
+ notes are OK. */
+ this = (CONSTANT_P (XEXP (temp, 0))
+ || (find_reg_note (p, REG_RETVAL, NULL_RTX)
+ && invariant_p (XEXP (temp, 0))));
+ if (this != 0)
+ value |= this;
+ }
+ }
+ if (this != 0)
+ count--;
+ else if (code != NOTE)
+ {
+ n_times_set[regno] = old;
+ return 0;
+ }
+ }
+
+ n_times_set[regno] = old;
+ /* If invariant_p ever returned 2, we return 2. */
+ return 1 + (value & 2);
+}
+
+#if 0
+/* I don't think this condition is sufficient to allow INSN
+ to be moved, so we no longer test it. */
+
+/* Return 1 if all insns in the basic block of INSN and following INSN
+ that set REG are invariant according to TABLE. */
+
+static int
+all_sets_invariant_p (reg, insn, table)
+ rtx reg, insn;
+ short *table;
+{
+ register rtx p = insn;
+ register int regno = REGNO (reg);
+
+ while (1)
+ {
+ register enum rtx_code code;
+ p = NEXT_INSN (p);
+ code = GET_CODE (p);
+ if (code == CODE_LABEL || code == JUMP_INSN)
+ return 1;
+ if (code == INSN && GET_CODE (PATTERN (p)) == SET
+ && GET_CODE (SET_DEST (PATTERN (p))) == REG
+ && REGNO (SET_DEST (PATTERN (p))) == regno)
+ {
+ if (!invariant_p (SET_SRC (PATTERN (p)), table))
+ return 0;
+ }
+ }
+}
+#endif /* 0 */
+
+/* Look at all uses (not sets) of registers in X. For each, if it is
+ the single use, set USAGE[REGNO] to INSN; if there was a previous use in
+ a different insn, set USAGE[REGNO] to const0_rtx. */
+
+static void
+find_single_use_in_loop (insn, x, usage)
+ rtx insn;
+ rtx x;
+ rtx *usage;
+{
+ enum rtx_code code = GET_CODE (x);
+ char *fmt = GET_RTX_FORMAT (code);
+ int i, j;
+
+ if (code == REG)
+ usage[REGNO (x)]
+ = (usage[REGNO (x)] != 0 && usage[REGNO (x)] != insn)
+ ? const0_rtx : insn;
+
+ else if (code == SET)
+ {
+ /* Don't count SET_DEST if it is a REG; otherwise count things
+ in SET_DEST because if a register is partially modified, it won't
+ show up as a potential movable so we don't care how USAGE is set
+ for it. */
+ if (GET_CODE (SET_DEST (x)) != REG)
+ find_single_use_in_loop (insn, SET_DEST (x), usage);
+ find_single_use_in_loop (insn, SET_SRC (x), usage);
+ }
+ else
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e' && XEXP (x, i) != 0)
+ find_single_use_in_loop (insn, XEXP (x, i), usage);
+ else if (fmt[i] == 'E')
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ find_single_use_in_loop (insn, XVECEXP (x, i, j), usage);
+ }
+}
+
+/* Increment N_TIMES_SET at the index of each register
+ that is modified by an insn between FROM and TO.
+ If the value of an element of N_TIMES_SET becomes 127 or more,
+ stop incrementing it, to avoid overflow.
+
+ Store in SINGLE_USAGE[I] the single insn in which register I is
+ used, if it is only used once. Otherwise, it is set to 0 (for no
+ uses) or const0_rtx for more than one use. This parameter may be zero,
+ in which case this processing is not done.
+
+ Store in *COUNT_PTR the number of actual instruction
+ in the loop. We use this to decide what is worth moving out. */
+
+/* last_set[n] is nonzero iff reg n has been set in the current basic block.
+ In that case, it is the insn that last set reg n. */
+
+static void
+count_loop_regs_set (from, to, may_not_move, single_usage, count_ptr, nregs)
+ register rtx from, to;
+ char *may_not_move;
+ rtx *single_usage;
+ int *count_ptr;
+ int nregs;
+{
+ register rtx *last_set = (rtx *) alloca (nregs * sizeof (rtx));
+ register rtx insn;
+ register int count = 0;
+ register rtx dest;
+
+ bzero ((char *) last_set, nregs * sizeof (rtx));
+ for (insn = from; insn != to; insn = NEXT_INSN (insn))
+ {
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ ++count;
+
+ /* If requested, record registers that have exactly one use. */
+ if (single_usage)
+ {
+ find_single_use_in_loop (insn, PATTERN (insn), single_usage);
+
+ /* Include uses in REG_EQUAL notes. */
+ if (REG_NOTES (insn))
+ find_single_use_in_loop (insn, REG_NOTES (insn), single_usage);
+ }
+
+ if (GET_CODE (PATTERN (insn)) == CLOBBER
+ && GET_CODE (XEXP (PATTERN (insn), 0)) == REG)
+ /* Don't move a reg that has an explicit clobber.
+ We might do so sometimes, but it's not worth the pain. */
+ may_not_move[REGNO (XEXP (PATTERN (insn), 0))] = 1;
+
+ if (GET_CODE (PATTERN (insn)) == SET
+ || GET_CODE (PATTERN (insn)) == CLOBBER)
+ {
+ dest = SET_DEST (PATTERN (insn));
+ while (GET_CODE (dest) == SUBREG
+ || GET_CODE (dest) == ZERO_EXTRACT
+ || GET_CODE (dest) == SIGN_EXTRACT
+ || GET_CODE (dest) == STRICT_LOW_PART)
+ dest = XEXP (dest, 0);
+ if (GET_CODE (dest) == REG)
+ {
+ register int regno = REGNO (dest);
+ /* If this is the first setting of this reg
+ in current basic block, and it was set before,
+ it must be set in two basic blocks, so it cannot
+ be moved out of the loop. */
+ if (n_times_set[regno] > 0 && last_set[regno] == 0)
+ may_not_move[regno] = 1;
+ /* If this is not first setting in current basic block,
+ see if reg was used in between previous one and this.
+ If so, neither one can be moved. */
+ if (last_set[regno] != 0
+ && reg_used_between_p (dest, last_set[regno], insn))
+ may_not_move[regno] = 1;
+ if (n_times_set[regno] < 127)
+ ++n_times_set[regno];
+ last_set[regno] = insn;
+ }
+ }
+ else if (GET_CODE (PATTERN (insn)) == PARALLEL)
+ {
+ register int i;
+ for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
+ {
+ register rtx x = XVECEXP (PATTERN (insn), 0, i);
+ if (GET_CODE (x) == CLOBBER && GET_CODE (XEXP (x, 0)) == REG)
+ /* Don't move a reg that has an explicit clobber.
+ It's not worth the pain to try to do it correctly. */
+ may_not_move[REGNO (XEXP (x, 0))] = 1;
+
+ if (GET_CODE (x) == SET || GET_CODE (x) == CLOBBER)
+ {
+ dest = SET_DEST (x);
+ while (GET_CODE (dest) == SUBREG
+ || GET_CODE (dest) == ZERO_EXTRACT
+ || GET_CODE (dest) == SIGN_EXTRACT
+ || GET_CODE (dest) == STRICT_LOW_PART)
+ dest = XEXP (dest, 0);
+ if (GET_CODE (dest) == REG)
+ {
+ register int regno = REGNO (dest);
+ if (n_times_set[regno] > 0 && last_set[regno] == 0)
+ may_not_move[regno] = 1;
+ if (last_set[regno] != 0
+ && reg_used_between_p (dest, last_set[regno], insn))
+ may_not_move[regno] = 1;
+ if (n_times_set[regno] < 127)
+ ++n_times_set[regno];
+ last_set[regno] = insn;
+ }
+ }
+ }
+ }
+ }
+
+ if (GET_CODE (insn) == CODE_LABEL || GET_CODE (insn) == JUMP_INSN)
+ bzero ((char *) last_set, nregs * sizeof (rtx));
+ }
+ *count_ptr = count;
+}
+
+/* Given a loop that is bounded by LOOP_START and LOOP_END
+ and that is entered at SCAN_START,
+ return 1 if the register set in SET contained in insn INSN is used by
+ any insn that precedes INSN in cyclic order starting
+ from the loop entry point.
+
+ We don't want to use INSN_LUID here because if we restrict INSN to those
+ that have a valid INSN_LUID, it means we cannot move an invariant out
+ from an inner loop past two loops. */
+
+static int
+loop_reg_used_before_p (set, insn, loop_start, scan_start, loop_end)
+ rtx set, insn, loop_start, scan_start, loop_end;
+{
+ rtx reg = SET_DEST (set);
+ rtx p;
+
+ /* Scan forward checking for register usage. If we hit INSN, we
+ are done. Otherwise, if we hit LOOP_END, wrap around to LOOP_START. */
+ for (p = scan_start; p != insn; p = NEXT_INSN (p))
+ {
+ if (GET_RTX_CLASS (GET_CODE (p)) == 'i'
+ && reg_overlap_mentioned_p (reg, PATTERN (p)))
+ return 1;
+
+ if (p == loop_end)
+ p = loop_start;
+ }
+
+ return 0;
+}
+
+/* A "basic induction variable" or biv is a pseudo reg that is set
+ (within this loop) only by incrementing or decrementing it. */
+/* A "general induction variable" or giv is a pseudo reg whose
+ value is a linear function of a biv. */
+
+/* Bivs are recognized by `basic_induction_var';
+ Givs by `general_induct_var'. */
+
+/* Indexed by register number, indicates whether or not register is an
+ induction variable, and if so what type. */
+
+enum iv_mode *reg_iv_type;
+
+/* Indexed by register number, contains pointer to `struct induction'
+ if register is an induction variable. This holds general info for
+ all induction variables. */
+
+struct induction **reg_iv_info;
+
+/* Indexed by register number, contains pointer to `struct iv_class'
+ if register is a basic induction variable. This holds info describing
+ the class (a related group) of induction variables that the biv belongs
+ to. */
+
+struct iv_class **reg_biv_class;
+
+/* The head of a list which links together (via the next field)
+ every iv class for the current loop. */
+
+struct iv_class *loop_iv_list;
+
+/* Communication with routines called via `note_stores'. */
+
+static rtx note_insn;
+
+/* Dummy register to have non-zero DEST_REG for DEST_ADDR type givs. */
+
+static rtx addr_placeholder;
+
+/* ??? Unfinished optimizations, and possible future optimizations,
+ for the strength reduction code. */
+
+/* ??? There is one more optimization you might be interested in doing: to
+ allocate pseudo registers for frequently-accessed memory locations.
+ If the same memory location is referenced each time around, it might
+ be possible to copy it into a register before and out after.
+ This is especially useful when the memory location is a variable which
+ is in a stack slot because somewhere its address is taken. If the
+ loop doesn't contain a function call and the variable isn't volatile,
+ it is safe to keep the value in a register for the duration of the
+ loop. One tricky thing is that the copying of the value back from the
+ register has to be done on all exits from the loop. You need to check that
+ all the exits from the loop go to the same place. */
+
+/* ??? The interaction of biv elimination, and recognition of 'constant'
+ bivs, may cause problems. */
+
+/* ??? Add heuristics so that DEST_ADDR strength reduction does not cause
+ performance problems.
+
+ Perhaps don't eliminate things that can be combined with an addressing
+ mode. Find all givs that have the same biv, mult_val, and add_val;
+ then for each giv, check to see if its only use dies in a following
+ memory address. If so, generate a new memory address and check to see
+ if it is valid. If it is valid, then store the modified memory address,
+ otherwise, mark the giv as not done so that it will get its own iv. */
+
+/* ??? Could try to optimize branches when it is known that a biv is always
+ positive. */
+
+/* ??? When replace a biv in a compare insn, we should replace with closest
+ giv so that an optimized branch can still be recognized by the combiner,
+ e.g. the VAX acb insn. */
+
+/* ??? Many of the checks involving uid_luid could be simplified if regscan
+ was rerun in loop_optimize whenever a register was added or moved.
+ Also, some of the optimizations could be a little less conservative. */
+
+/* Perform strength reduction and induction variable elimination. */
+
+/* Pseudo registers created during this function will be beyond the last
+ valid index in several tables including n_times_set and regno_last_uid.
+ This does not cause a problem here, because the added registers cannot be
+ givs outside of their loop, and hence will never be reconsidered.
+ But scan_loop must check regnos to make sure they are in bounds. */
+
+static void
+strength_reduce (scan_start, end, loop_top, insn_count,
+ loop_start, loop_end)
+ rtx scan_start;
+ rtx end;
+ rtx loop_top;
+ int insn_count;
+ rtx loop_start;
+ rtx loop_end;
+{
+ rtx p;
+ rtx set;
+ rtx inc_val;
+ rtx mult_val;
+ rtx dest_reg;
+ /* This is 1 if current insn is not executed at least once for every loop
+ iteration. */
+ int not_every_iteration = 0;
+ /* This is 1 if current insn may be executed more than once for every
+ loop iteration. */
+ int maybe_multiple = 0;
+ /* Temporary list pointers for traversing loop_iv_list. */
+ struct iv_class *bl, **backbl;
+ /* Ratio of extra register life span we can justify
+ for saving an instruction. More if loop doesn't call subroutines
+ since in that case saving an insn makes more difference
+ and more registers are available. */
+ /* ??? could set this to last value of threshold in move_movables */
+ int threshold = (loop_has_call ? 1 : 2) * (3 + n_non_fixed_regs);
+ /* Map of pseudo-register replacements. */
+ rtx *reg_map;
+ int call_seen;
+ rtx test;
+ rtx end_insert_before;
+ int loop_depth = 0;
+
+ reg_iv_type = (enum iv_mode *) alloca (max_reg_before_loop
+ * sizeof (enum iv_mode *));
+ bzero ((char *) reg_iv_type, max_reg_before_loop * sizeof (enum iv_mode *));
+ reg_iv_info = (struct induction **)
+ alloca (max_reg_before_loop * sizeof (struct induction *));
+ bzero ((char *) reg_iv_info, (max_reg_before_loop
+ * sizeof (struct induction *)));
+ reg_biv_class = (struct iv_class **)
+ alloca (max_reg_before_loop * sizeof (struct iv_class *));
+ bzero ((char *) reg_biv_class, (max_reg_before_loop
+ * sizeof (struct iv_class *)));
+
+ loop_iv_list = 0;
+ addr_placeholder = gen_reg_rtx (Pmode);
+
+ /* Save insn immediately after the loop_end. Insns inserted after loop_end
+ must be put before this insn, so that they will appear in the right
+ order (i.e. loop order).
+
+ If loop_end is the end of the current function, then emit a
+ NOTE_INSN_DELETED after loop_end and set end_insert_before to the
+ dummy note insn. */
+ if (NEXT_INSN (loop_end) != 0)
+ end_insert_before = NEXT_INSN (loop_end);
+ else
+ end_insert_before = emit_note_after (NOTE_INSN_DELETED, loop_end);
+
+ /* Scan through loop to find all possible bivs. */
+
+ p = scan_start;
+ while (1)
+ {
+ p = NEXT_INSN (p);
+ /* At end of a straight-in loop, we are done.
+ At end of a loop entered at the bottom, scan the top. */
+ if (p == scan_start)
+ break;
+ if (p == end)
+ {
+ if (loop_top != 0)
+ p = loop_top;
+ else
+ break;
+ if (p == scan_start)
+ break;
+ }
+
+ if (GET_CODE (p) == INSN
+ && (set = single_set (p))
+ && GET_CODE (SET_DEST (set)) == REG)
+ {
+ dest_reg = SET_DEST (set);
+ if (REGNO (dest_reg) < max_reg_before_loop
+ && REGNO (dest_reg) >= FIRST_PSEUDO_REGISTER
+ && reg_iv_type[REGNO (dest_reg)] != NOT_BASIC_INDUCT)
+ {
+ if (basic_induction_var (SET_SRC (set), GET_MODE (SET_SRC (set)),
+ dest_reg, p, &inc_val, &mult_val))
+ {
+ /* It is a possible basic induction variable.
+ Create and initialize an induction structure for it. */
+
+ struct induction *v
+ = (struct induction *) alloca (sizeof (struct induction));
+
+ record_biv (v, p, dest_reg, inc_val, mult_val,
+ not_every_iteration, maybe_multiple);
+ reg_iv_type[REGNO (dest_reg)] = BASIC_INDUCT;
+ }
+ else if (REGNO (dest_reg) < max_reg_before_loop)
+ reg_iv_type[REGNO (dest_reg)] = NOT_BASIC_INDUCT;
+ }
+ }
+
+ /* Past CODE_LABEL, we get to insns that may be executed multiple
+ times. The only way we can be sure that they can't is if every
+ every jump insn between here and the end of the loop either
+ returns, exits the loop, or is a forward jump. */
+
+ if (GET_CODE (p) == CODE_LABEL)
+ {
+ rtx insn = p;
+
+ maybe_multiple = 0;
+
+ while (1)
+ {
+ insn = NEXT_INSN (insn);
+ if (insn == scan_start)
+ break;
+ if (insn == end)
+ {
+ if (loop_top != 0)
+ insn = loop_top;
+ else
+ break;
+ if (insn == scan_start)
+ break;
+ }
+
+ if (GET_CODE (insn) == JUMP_INSN
+ && GET_CODE (PATTERN (insn)) != RETURN
+ && (! condjump_p (insn)
+ || (JUMP_LABEL (insn) != 0
+ && (INSN_UID (JUMP_LABEL (insn)) >= max_uid_for_loop
+ || INSN_UID (insn) >= max_uid_for_loop
+ || (INSN_LUID (JUMP_LABEL (insn))
+ < INSN_LUID (insn))))))
+ {
+ maybe_multiple = 1;
+ break;
+ }
+ }
+ }
+
+ /* Past a label or a jump, we get to insns for which we can't count
+ on whether or how many times they will be executed during each
+ iteration. */
+ /* This code appears in three places, once in scan_loop, and twice
+ in strength_reduce. */
+ if ((GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN)
+ /* If we enter the loop in the middle, and scan around to the
+ beginning, don't set not_every_iteration for that.
+ This can be any kind of jump, since we want to know if insns
+ will be executed if the loop is executed. */
+ && ! (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p) == loop_top
+ && ((NEXT_INSN (NEXT_INSN (p)) == loop_end && simplejump_p (p))
+ || (NEXT_INSN (p) == loop_end && condjump_p (p)))))
+ not_every_iteration = 1;
+
+ else if (GET_CODE (p) == NOTE)
+ {
+ /* At the virtual top of a converted loop, insns are again known to
+ be executed each iteration: logically, the loop begins here
+ even though the exit code has been duplicated. */
+ if (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_VTOP && loop_depth == 0)
+ not_every_iteration = 0;
+ else if (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG)
+ loop_depth++;
+ else if (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)
+ loop_depth--;
+ }
+
+ /* Unlike in the code motion pass where MAYBE_NEVER indicates that
+ an insn may never be executed, NOT_EVERY_ITERATION indicates whether
+ or not an insn is known to be executed each iteration of the
+ loop, whether or not any iterations are known to occur.
+
+ Therefore, if we have just passed a label and have no more labels
+ between here and the test insn of the loop, we know these insns
+ will be executed each iteration. This can also happen if we
+ have just passed a jump, for example, when there are nested loops. */
+
+ if (not_every_iteration && GET_CODE (p) == CODE_LABEL
+ && no_labels_between_p (p, loop_end))
+ not_every_iteration = 0;
+ }
+
+ /* Scan loop_iv_list to remove all regs that proved not to be bivs.
+ Make a sanity check against n_times_set. */
+ for (backbl = &loop_iv_list, bl = *backbl; bl; bl = bl->next)
+ {
+ if (reg_iv_type[bl->regno] != BASIC_INDUCT
+ /* Above happens if register modified by subreg, etc. */
+ /* Make sure it is not recognized as a basic induction var: */
+ || n_times_set[bl->regno] != bl->biv_count
+ /* If never incremented, it is invariant that we decided not to
+ move. So leave it alone. */
+ || ! bl->incremented)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "Reg %d: biv discarded, %s\n",
+ bl->regno,
+ (reg_iv_type[bl->regno] != BASIC_INDUCT
+ ? "not induction variable"
+ : (! bl->incremented ? "never incremented"
+ : "count error")));
+
+ reg_iv_type[bl->regno] = NOT_BASIC_INDUCT;
+ *backbl = bl->next;
+ }
+ else
+ {
+ backbl = &bl->next;
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "Reg %d: biv verified\n", bl->regno);
+ }
+ }
+
+ /* Exit if there are no bivs. */
+ if (! loop_iv_list)
+ {
+ /* Can still unroll the loop anyways, but indicate that there is no
+ strength reduction info available. */
+ if (flag_unroll_loops)
+ unroll_loop (loop_end, insn_count, loop_start, end_insert_before, 0);
+
+ return;
+ }
+
+ /* Find initial value for each biv by searching backwards from loop_start,
+ halting at first label. Also record any test condition. */
+
+ call_seen = 0;
+ for (p = loop_start; p && GET_CODE (p) != CODE_LABEL; p = PREV_INSN (p))
+ {
+ note_insn = p;
+
+ if (GET_CODE (p) == CALL_INSN)
+ call_seen = 1;
+
+ if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN
+ || GET_CODE (p) == CALL_INSN)
+ note_stores (PATTERN (p), record_initial);
+
+ /* Record any test of a biv that branches around the loop if no store
+ between it and the start of loop. We only care about tests with
+ constants and registers and only certain of those. */
+ if (GET_CODE (p) == JUMP_INSN
+ && JUMP_LABEL (p) != 0
+ && next_real_insn (JUMP_LABEL (p)) == next_real_insn (loop_end)
+ && (test = get_condition_for_loop (p)) != 0
+ && GET_CODE (XEXP (test, 0)) == REG
+ && REGNO (XEXP (test, 0)) < max_reg_before_loop
+ && (bl = reg_biv_class[REGNO (XEXP (test, 0))]) != 0
+ && valid_initial_value_p (XEXP (test, 1), p, call_seen, loop_start)
+ && bl->init_insn == 0)
+ {
+ /* If an NE test, we have an initial value! */
+ if (GET_CODE (test) == NE)
+ {
+ bl->init_insn = p;
+ bl->init_set = gen_rtx (SET, VOIDmode,
+ XEXP (test, 0), XEXP (test, 1));
+ }
+ else
+ bl->initial_test = test;
+ }
+ }
+
+ /* Look at the each biv and see if we can say anything better about its
+ initial value from any initializing insns set up above. (This is done
+ in two passes to avoid missing SETs in a PARALLEL.) */
+ for (bl = loop_iv_list; bl; bl = bl->next)
+ {
+ rtx src;
+
+ if (! bl->init_insn)
+ continue;
+
+ src = SET_SRC (bl->init_set);
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Biv %d initialized at insn %d: initial value ",
+ bl->regno, INSN_UID (bl->init_insn));
+
+ if ((GET_MODE (src) == GET_MODE (regno_reg_rtx[bl->regno])
+ || GET_MODE (src) == VOIDmode)
+ && valid_initial_value_p (src, bl->init_insn, call_seen, loop_start))
+ {
+ bl->initial_value = src;
+
+ if (loop_dump_stream)
+ {
+ if (GET_CODE (src) == CONST_INT)
+ fprintf (loop_dump_stream, "%d\n", INTVAL (src));
+ else
+ {
+ print_rtl (loop_dump_stream, src);
+ fprintf (loop_dump_stream, "\n");
+ }
+ }
+ }
+ else
+ {
+ /* Biv initial value is not simple move,
+ so let it keep initial value of "itself". */
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "is complex\n");
+ }
+ }
+
+ /* Search the loop for general induction variables. */
+
+ /* A register is a giv if: it is only set once, it is a function of a
+ biv and a constant (or invariant), and it is not a biv. */
+
+ not_every_iteration = 0;
+ loop_depth = 0;
+ p = scan_start;
+ while (1)
+ {
+ p = NEXT_INSN (p);
+ /* At end of a straight-in loop, we are done.
+ At end of a loop entered at the bottom, scan the top. */
+ if (p == scan_start)
+ break;
+ if (p == end)
+ {
+ if (loop_top != 0)
+ p = loop_top;
+ else
+ break;
+ if (p == scan_start)
+ break;
+ }
+
+ /* Look for a general induction variable in a register. */
+ if (GET_CODE (p) == INSN
+ && (set = single_set (p))
+ && GET_CODE (SET_DEST (set)) == REG
+ && ! may_not_optimize[REGNO (SET_DEST (set))])
+ {
+ rtx src_reg;
+ rtx add_val;
+ rtx mult_val;
+ int benefit;
+ rtx regnote = 0;
+
+ dest_reg = SET_DEST (set);
+ if (REGNO (dest_reg) < FIRST_PSEUDO_REGISTER)
+ continue;
+
+ if (/* SET_SRC is a giv. */
+ ((benefit = general_induction_var (SET_SRC (set),
+ &src_reg, &add_val,
+ &mult_val))
+ /* Equivalent expression is a giv. */
+ || ((regnote = find_reg_note (p, REG_EQUAL, NULL_RTX))
+ && (benefit = general_induction_var (XEXP (regnote, 0),
+ &src_reg,
+ &add_val, &mult_val))))
+ /* Don't try to handle any regs made by loop optimization.
+ We have nothing on them in regno_first_uid, etc. */
+ && REGNO (dest_reg) < max_reg_before_loop
+ /* Don't recognize a BASIC_INDUCT_VAR here. */
+ && dest_reg != src_reg
+ /* This must be the only place where the register is set. */
+ && (n_times_set[REGNO (dest_reg)] == 1
+ /* or all sets must be consecutive and make a giv. */
+ || (benefit = consec_sets_giv (benefit, p,
+ src_reg, dest_reg,
+ &add_val, &mult_val))))
+ {
+ int count;
+ struct induction *v
+ = (struct induction *) alloca (sizeof (struct induction));
+ rtx temp;
+
+ /* If this is a library call, increase benefit. */
+ if (find_reg_note (p, REG_RETVAL, NULL_RTX))
+ benefit += libcall_benefit (p);
+
+ /* Skip the consecutive insns, if there are any. */
+ for (count = n_times_set[REGNO (dest_reg)] - 1;
+ count > 0; count--)
+ {
+ /* If first insn of libcall sequence, skip to end.
+ Do this at start of loop, since INSN is guaranteed to
+ be an insn here. */
+ if (GET_CODE (p) != NOTE
+ && (temp = find_reg_note (p, REG_LIBCALL, NULL_RTX)))
+ p = XEXP (temp, 0);
+
+ do p = NEXT_INSN (p);
+ while (GET_CODE (p) == NOTE);
+ }
+
+ record_giv (v, p, src_reg, dest_reg, mult_val, add_val, benefit,
+ DEST_REG, not_every_iteration, NULL_PTR, loop_start,
+ loop_end);
+
+ }
+ }
+
+#ifndef DONT_REDUCE_ADDR
+ /* Look for givs which are memory addresses. */
+ /* This resulted in worse code on a VAX 8600. I wonder if it
+ still does. */
+ if (GET_CODE (p) == INSN)
+ find_mem_givs (PATTERN (p), p, not_every_iteration, loop_start,
+ loop_end);
+#endif
+
+ /* Update the status of whether giv can derive other givs. This can
+ change when we pass a label or an insn that updates a biv. */
+ if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN
+ || GET_CODE (p) == CODE_LABEL)
+ update_giv_derive (p);
+
+ /* Past a label or a jump, we get to insns for which we can't count
+ on whether or how many times they will be executed during each
+ iteration. */
+ /* This code appears in three places, once in scan_loop, and twice
+ in strength_reduce. */
+ if ((GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN)
+ /* If we enter the loop in the middle, and scan around
+ to the beginning, don't set not_every_iteration for that.
+ This can be any kind of jump, since we want to know if insns
+ will be executed if the loop is executed. */
+ && ! (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p) == loop_top
+ && ((NEXT_INSN (NEXT_INSN (p)) == loop_end && simplejump_p (p))
+ || (NEXT_INSN (p) == loop_end && condjump_p (p)))))
+ not_every_iteration = 1;
+
+ else if (GET_CODE (p) == NOTE)
+ {
+ /* At the virtual top of a converted loop, insns are again known to
+ be executed each iteration: logically, the loop begins here
+ even though the exit code has been duplicated. */
+ if (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_VTOP && loop_depth == 0)
+ not_every_iteration = 0;
+ else if (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG)
+ loop_depth++;
+ else if (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)
+ loop_depth--;
+ }
+
+ /* Unlike in the code motion pass where MAYBE_NEVER indicates that
+ an insn may never be executed, NOT_EVERY_ITERATION indicates whether
+ or not an insn is known to be executed each iteration of the
+ loop, whether or not any iterations are known to occur.
+
+ Therefore, if we have just passed a label and have no more labels
+ between here and the test insn of the loop, we know these insns
+ will be executed each iteration. */
+
+ if (not_every_iteration && GET_CODE (p) == CODE_LABEL
+ && no_labels_between_p (p, loop_end))
+ not_every_iteration = 0;
+ }
+
+ /* Try to calculate and save the number of loop iterations. This is
+ set to zero if the actual number can not be calculated. This must
+ be called after all giv's have been identified, since otherwise it may
+ fail if the iteration variable is a giv. */
+
+ loop_n_iterations = loop_iterations (loop_start, loop_end);
+
+ /* Now for each giv for which we still don't know whether or not it is
+ replaceable, check to see if it is replaceable because its final value
+ can be calculated. This must be done after loop_iterations is called,
+ so that final_giv_value will work correctly. */
+
+ for (bl = loop_iv_list; bl; bl = bl->next)
+ {
+ struct induction *v;
+
+ for (v = bl->giv; v; v = v->next_iv)
+ if (! v->replaceable && ! v->not_replaceable)
+ check_final_value (v, loop_start, loop_end);
+ }
+
+ /* Try to prove that the loop counter variable (if any) is always
+ nonnegative; if so, record that fact with a REG_NONNEG note
+ so that "decrement and branch until zero" insn can be used. */
+ check_dbra_loop (loop_end, insn_count, loop_start);
+
+ /* Create reg_map to hold substitutions for replaceable giv regs. */
+ reg_map = (rtx *) alloca (max_reg_before_loop * sizeof (rtx));
+ bzero ((char *) reg_map, max_reg_before_loop * sizeof (rtx));
+
+ /* Examine each iv class for feasibility of strength reduction/induction
+ variable elimination. */
+
+ for (bl = loop_iv_list; bl; bl = bl->next)
+ {
+ struct induction *v;
+ int benefit;
+ int all_reduced;
+ rtx final_value = 0;
+
+ /* Test whether it will be possible to eliminate this biv
+ provided all givs are reduced. This is possible if either
+ the reg is not used outside the loop, or we can compute
+ what its final value will be.
+
+ For architectures with a decrement_and_branch_until_zero insn,
+ don't do this if we put a REG_NONNEG note on the endtest for
+ this biv. */
+
+ /* Compare against bl->init_insn rather than loop_start.
+ We aren't concerned with any uses of the biv between
+ init_insn and loop_start since these won't be affected
+ by the value of the biv elsewhere in the function, so
+ long as init_insn doesn't use the biv itself.
+ March 14, 1989 -- self@bayes.arc.nasa.gov */
+
+ if ((uid_luid[regno_last_uid[bl->regno]] < INSN_LUID (loop_end)
+ && bl->init_insn
+ && INSN_UID (bl->init_insn) < max_uid_for_loop
+ && uid_luid[regno_first_uid[bl->regno]] >= INSN_LUID (bl->init_insn)
+#ifdef HAVE_decrement_and_branch_until_zero
+ && ! bl->nonneg
+#endif
+ && ! reg_mentioned_p (bl->biv->dest_reg, SET_SRC (bl->init_set)))
+ || ((final_value = final_biv_value (bl, loop_start, loop_end))
+#ifdef HAVE_decrement_and_branch_until_zero
+ && ! bl->nonneg
+#endif
+ ))
+ bl->eliminable = maybe_eliminate_biv (bl, loop_start, end, 0,
+ threshold, insn_count);
+ else
+ {
+ if (loop_dump_stream)
+ {
+ fprintf (loop_dump_stream,
+ "Cannot eliminate biv %d.\n",
+ bl->regno);
+ fprintf (loop_dump_stream,
+ "First use: insn %d, last use: insn %d.\n",
+ regno_first_uid[bl->regno],
+ regno_last_uid[bl->regno]);
+ }
+ }
+
+ /* Combine all giv's for this iv_class. */
+ combine_givs (bl);
+
+ /* This will be true at the end, if all givs which depend on this
+ biv have been strength reduced.
+ We can't (currently) eliminate the biv unless this is so. */
+ all_reduced = 1;
+
+ /* Check each giv in this class to see if we will benefit by reducing
+ it. Skip giv's combined with others. */
+ for (v = bl->giv; v; v = v->next_iv)
+ {
+ struct induction *tv;
+
+ if (v->ignore || v->same)
+ continue;
+
+ benefit = v->benefit;
+
+ /* Reduce benefit if not replaceable, since we will insert
+ a move-insn to replace the insn that calculates this giv.
+ Don't do this unless the giv is a user variable, since it
+ will often be marked non-replaceable because of the duplication
+ of the exit code outside the loop. In such a case, the copies
+ we insert are dead and will be deleted. So they don't have
+ a cost. Similar situations exist. */
+ /* ??? The new final_[bg]iv_value code does a much better job
+ of finding replaceable giv's, and hence this code may no longer
+ be necessary. */
+ if (! v->replaceable && ! bl->eliminable
+ && REG_USERVAR_P (v->dest_reg))
+ benefit -= copy_cost;
+
+ /* Decrease the benefit to count the add-insns that we will
+ insert to increment the reduced reg for the giv. */
+ benefit -= add_cost * bl->biv_count;
+
+ /* Decide whether to strength-reduce this giv or to leave the code
+ unchanged (recompute it from the biv each time it is used).
+ This decision can be made independently for each giv. */
+
+ /* ??? Perhaps attempt to guess whether autoincrement will handle
+ some of the new add insns; if so, can increase BENEFIT
+ (undo the subtraction of add_cost that was done above). */
+
+ /* If an insn is not to be strength reduced, then set its ignore
+ flag, and clear all_reduced. */
+
+ /* A giv that depends on a reversed biv must be reduced if it is
+ used after the loop exit, otherwise, it would have the wrong
+ value after the loop exit. To make it simple, just reduce all
+ of such giv's whether or not we know they are used after the loop
+ exit. */
+
+ if (v->lifetime * threshold * benefit < insn_count
+ && ! bl->reversed)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "giv of insn %d not worth while, %d vs %d.\n",
+ INSN_UID (v->insn),
+ v->lifetime * threshold * benefit, insn_count);
+ v->ignore = 1;
+ all_reduced = 0;
+ }
+ else
+ {
+ /* Check that we can increment the reduced giv without a
+ multiply insn. If not, reject it. */
+
+ for (tv = bl->biv; tv; tv = tv->next_iv)
+ if (tv->mult_val == const1_rtx
+ && ! product_cheap_p (tv->add_val, v->mult_val))
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "giv of insn %d: would need a multiply.\n",
+ INSN_UID (v->insn));
+ v->ignore = 1;
+ all_reduced = 0;
+ break;
+ }
+ }
+ }
+
+ /* Reduce each giv that we decided to reduce. */
+
+ for (v = bl->giv; v; v = v->next_iv)
+ {
+ struct induction *tv;
+ if (! v->ignore && v->same == 0)
+ {
+ v->new_reg = gen_reg_rtx (v->mode);
+
+ /* For each place where the biv is incremented,
+ add an insn to increment the new, reduced reg for the giv. */
+ for (tv = bl->biv; tv; tv = tv->next_iv)
+ {
+ if (tv->mult_val == const1_rtx)
+ emit_iv_add_mult (tv->add_val, v->mult_val,
+ v->new_reg, v->new_reg, tv->insn);
+ else /* tv->mult_val == const0_rtx */
+ /* A multiply is acceptable here
+ since this is presumed to be seldom executed. */
+ emit_iv_add_mult (tv->add_val, v->mult_val,
+ v->add_val, v->new_reg, tv->insn);
+ }
+
+ /* Add code at loop start to initialize giv's reduced reg. */
+
+ emit_iv_add_mult (bl->initial_value, v->mult_val,
+ v->add_val, v->new_reg, loop_start);
+ }
+ }
+
+ /* Rescan all givs. If a giv is the same as a giv not reduced, mark it
+ as not reduced.
+
+ For each giv register that can be reduced now: if replaceable,
+ substitute reduced reg wherever the old giv occurs;
+ else add new move insn "giv_reg = reduced_reg".
+
+ Also check for givs whose first use is their definition and whose
+ last use is the definition of another giv. If so, it is likely
+ dead and should not be used to eliminate a biv. */
+ for (v = bl->giv; v; v = v->next_iv)
+ {
+ if (v->same && v->same->ignore)
+ v->ignore = 1;
+
+ if (v->ignore)
+ continue;
+
+ if (v->giv_type == DEST_REG
+ && regno_first_uid[REGNO (v->dest_reg)] == INSN_UID (v->insn))
+ {
+ struct induction *v1;
+
+ for (v1 = bl->giv; v1; v1 = v1->next_iv)
+ if (regno_last_uid[REGNO (v->dest_reg)] == INSN_UID (v1->insn))
+ v->maybe_dead = 1;
+ }
+
+ /* Update expression if this was combined, in case other giv was
+ replaced. */
+ if (v->same)
+ v->new_reg = replace_rtx (v->new_reg,
+ v->same->dest_reg, v->same->new_reg);
+
+ if (v->giv_type == DEST_ADDR)
+ /* Store reduced reg as the address in the memref where we found
+ this giv. */
+ validate_change (v->insn, v->location, v->new_reg, 0);
+ else if (v->replaceable)
+ {
+ reg_map[REGNO (v->dest_reg)] = v->new_reg;
+
+#if 0
+ /* I can no longer duplicate the original problem. Perhaps
+ this is unnecessary now? */
+
+ /* Replaceable; it isn't strictly necessary to delete the old
+ insn and emit a new one, because v->dest_reg is now dead.
+
+ However, especially when unrolling loops, the special
+ handling for (set REG0 REG1) in the second cse pass may
+ make v->dest_reg live again. To avoid this problem, emit
+ an insn to set the original giv reg from the reduced giv.
+ We can not delete the original insn, since it may be part
+ of a LIBCALL, and the code in flow that eliminates dead
+ libcalls will fail if it is deleted. */
+ emit_insn_after (gen_move_insn (v->dest_reg, v->new_reg),
+ v->insn);
+#endif
+ }
+ else
+ {
+ /* Not replaceable; emit an insn to set the original giv reg from
+ the reduced giv, same as above. */
+ emit_insn_after (gen_move_insn (v->dest_reg, v->new_reg),
+ v->insn);
+ }
+
+ /* When a loop is reversed, givs which depend on the reversed
+ biv, and which are live outside the loop, must be set to their
+ correct final value. This insn is only needed if the giv is
+ not replaceable. The correct final value is the same as the
+ value that the giv starts the reversed loop with. */
+ if (bl->reversed && ! v->replaceable)
+ emit_iv_add_mult (bl->initial_value, v->mult_val,
+ v->add_val, v->dest_reg, end_insert_before);
+ else if (v->final_value)
+ {
+ rtx insert_before;
+
+ /* If the loop has multiple exits, emit the insn before the
+ loop to ensure that it will always be executed no matter
+ how the loop exits. Otherwise, emit the insn after the loop,
+ since this is slightly more efficient. */
+ if (loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]])
+ insert_before = loop_start;
+ else
+ insert_before = end_insert_before;
+ emit_insn_before (gen_move_insn (v->dest_reg, v->final_value),
+ insert_before);
+
+#if 0
+ /* If the insn to set the final value of the giv was emitted
+ before the loop, then we must delete the insn inside the loop
+ that sets it. If this is a LIBCALL, then we must delete
+ every insn in the libcall. Note, however, that
+ final_giv_value will only succeed when there are multiple
+ exits if the giv is dead at each exit, hence it does not
+ matter that the original insn remains because it is dead
+ anyways. */
+ /* Delete the insn inside the loop that sets the giv since
+ the giv is now set before (or after) the loop. */
+ delete_insn (v->insn);
+#endif
+ }
+
+ if (loop_dump_stream)
+ {
+ fprintf (loop_dump_stream, "giv at %d reduced to ",
+ INSN_UID (v->insn));
+ print_rtl (loop_dump_stream, v->new_reg);
+ fprintf (loop_dump_stream, "\n");
+ }
+ }
+
+ /* All the givs based on the biv bl have been reduced if they
+ merit it. */
+
+ /* For each giv not marked as maybe dead that has been combined with a
+ second giv, clear any "maybe dead" mark on that second giv.
+ v->new_reg will either be or refer to the register of the giv it
+ combined with.
+
+ Doing this clearing avoids problems in biv elimination where a
+ giv's new_reg is a complex value that can't be put in the insn but
+ the giv combined with (with a reg as new_reg) is marked maybe_dead.
+ Since the register will be used in either case, we'd prefer it be
+ used from the simpler giv. */
+
+ for (v = bl->giv; v; v = v->next_iv)
+ if (! v->maybe_dead && v->same)
+ v->same->maybe_dead = 0;
+
+ /* Try to eliminate the biv, if it is a candidate.
+ This won't work if ! all_reduced,
+ since the givs we planned to use might not have been reduced.
+
+ We have to be careful that we didn't initially think we could eliminate
+ this biv because of a giv that we now think may be dead and shouldn't
+ be used as a biv replacement.
+
+ Also, there is the possibility that we may have a giv that looks
+ like it can be used to eliminate a biv, but the resulting insn
+ isn't valid. This can happen, for example, on the 88k, where a
+ JUMP_INSN can compare a register only with zero. Attempts to
+ replace it with a compare with a constant will fail.
+
+ Note that in cases where this call fails, we may have replaced some
+ of the occurrences of the biv with a giv, but no harm was done in
+ doing so in the rare cases where it can occur. */
+
+ if (all_reduced == 1 && bl->eliminable
+ && maybe_eliminate_biv (bl, loop_start, end, 1,
+ threshold, insn_count))
+
+ {
+ /* ?? If we created a new test to bypass the loop entirely,
+ or otherwise drop straight in, based on this test, then
+ we might want to rewrite it also. This way some later
+ pass has more hope of removing the initialization of this
+ biv entirely. */
+
+ /* If final_value != 0, then the biv may be used after loop end
+ and we must emit an insn to set it just in case.
+
+ Reversed bivs already have an insn after the loop setting their
+ value, so we don't need another one. We can't calculate the
+ proper final value for such a biv here anyways. */
+ if (final_value != 0 && ! bl->reversed)
+ {
+ rtx insert_before;
+
+ /* If the loop has multiple exits, emit the insn before the
+ loop to ensure that it will always be executed no matter
+ how the loop exits. Otherwise, emit the insn after the
+ loop, since this is slightly more efficient. */
+ if (loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]])
+ insert_before = loop_start;
+ else
+ insert_before = end_insert_before;
+
+ emit_insn_before (gen_move_insn (bl->biv->dest_reg, final_value),
+ end_insert_before);
+ }
+
+#if 0
+ /* Delete all of the instructions inside the loop which set
+ the biv, as they are all dead. If is safe to delete them,
+ because an insn setting a biv will never be part of a libcall. */
+ /* However, deleting them will invalidate the regno_last_uid info,
+ so keeping them around is more convenient. Final_biv_value
+ will only succeed when there are multiple exits if the biv
+ is dead at each exit, hence it does not matter that the original
+ insn remains, because it is dead anyways. */
+ for (v = bl->biv; v; v = v->next_iv)
+ delete_insn (v->insn);
+#endif
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "Reg %d: biv eliminated\n",
+ bl->regno);
+ }
+ }
+
+ /* Go through all the instructions in the loop, making all the
+ register substitutions scheduled in REG_MAP. */
+
+ for (p = loop_start; p != end; p = NEXT_INSN (p))
+ if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN
+ || GET_CODE (p) == CALL_INSN)
+ {
+ replace_regs (PATTERN (p), reg_map, max_reg_before_loop, 0);
+ replace_regs (REG_NOTES (p), reg_map, max_reg_before_loop, 0);
+ INSN_CODE (p) = -1;
+ }
+
+ /* Unroll loops from within strength reduction so that we can use the
+ induction variable information that strength_reduce has already
+ collected. */
+
+ if (flag_unroll_loops)
+ unroll_loop (loop_end, insn_count, loop_start, end_insert_before, 1);
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "\n");
+}
+
+/* Return 1 if X is a valid source for an initial value (or as value being
+ compared against in an initial test).
+
+ X must be either a register or constant and must not be clobbered between
+ the current insn and the start of the loop.
+
+ INSN is the insn containing X. */
+
+static int
+valid_initial_value_p (x, insn, call_seen, loop_start)
+ rtx x;
+ rtx insn;
+ int call_seen;
+ rtx loop_start;
+{
+ if (CONSTANT_P (x))
+ return 1;
+
+ /* Only consider pseudos we know about initialized in insns whose luids
+ we know. */
+ if (GET_CODE (x) != REG
+ || REGNO (x) >= max_reg_before_loop)
+ return 0;
+
+ /* Don't use call-clobbered registers across a call which clobbers it. On
+ some machines, don't use any hard registers at all. */
+ if (REGNO (x) < FIRST_PSEUDO_REGISTER
+#ifndef SMALL_REGISTER_CLASSES
+ && call_used_regs[REGNO (x)] && call_seen
+#endif
+ )
+ return 0;
+
+ /* Don't use registers that have been clobbered before the start of the
+ loop. */
+ if (reg_set_between_p (x, insn, loop_start))
+ return 0;
+
+ return 1;
+}
+
+/* Scan X for memory refs and check each memory address
+ as a possible giv. INSN is the insn whose pattern X comes from.
+ NOT_EVERY_ITERATION is 1 if the insn might not be executed during
+ every loop iteration. */
+
+static void
+find_mem_givs (x, insn, not_every_iteration, loop_start, loop_end)
+ rtx x;
+ rtx insn;
+ int not_every_iteration;
+ rtx loop_start, loop_end;
+{
+ register int i, j;
+ register enum rtx_code code;
+ register char *fmt;
+
+ if (x == 0)
+ return;
+
+ code = GET_CODE (x);
+ switch (code)
+ {
+ case REG:
+ case CONST_INT:
+ case CONST:
+ case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case PC:
+ case CC0:
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ case USE:
+ case CLOBBER:
+ return;
+
+ case MEM:
+ {
+ rtx src_reg;
+ rtx add_val;
+ rtx mult_val;
+ int benefit;
+
+ benefit = general_induction_var (XEXP (x, 0),
+ &src_reg, &add_val, &mult_val);
+
+ /* Don't make a DEST_ADDR giv with mult_val == 1 && add_val == 0.
+ Such a giv isn't useful. */
+ if (benefit > 0 && (mult_val != const1_rtx || add_val != const0_rtx))
+ {
+ /* Found one; record it. */
+ struct induction *v
+ = (struct induction *) oballoc (sizeof (struct induction));
+
+ record_giv (v, insn, src_reg, addr_placeholder, mult_val,
+ add_val, benefit, DEST_ADDR, not_every_iteration,
+ &XEXP (x, 0), loop_start, loop_end);
+
+ v->mem_mode = GET_MODE (x);
+ }
+ return;
+ }
+ }
+
+ /* Recursively scan the subexpressions for other mem refs. */
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ if (fmt[i] == 'e')
+ find_mem_givs (XEXP (x, i), insn, not_every_iteration, loop_start,
+ loop_end);
+ else if (fmt[i] == 'E')
+ for (j = 0; j < XVECLEN (x, i); j++)
+ find_mem_givs (XVECEXP (x, i, j), insn, not_every_iteration,
+ loop_start, loop_end);
+}
+
+/* Fill in the data about one biv update.
+ V is the `struct induction' in which we record the biv. (It is
+ allocated by the caller, with alloca.)
+ INSN is the insn that sets it.
+ DEST_REG is the biv's reg.
+
+ MULT_VAL is const1_rtx if the biv is being incremented here, in which case
+ INC_VAL is the increment. Otherwise, MULT_VAL is const0_rtx and the biv is
+ being set to INC_VAL.
+
+ NOT_EVERY_ITERATION is nonzero if this biv update is not know to be
+ executed every iteration; MAYBE_MULTIPLE is nonzero if this biv update
+ can be executed more than once per iteration. If MAYBE_MULTIPLE
+ and NOT_EVERY_ITERATION are both zero, we know that the biv update is
+ executed exactly once per iteration. */
+
+static void
+record_biv (v, insn, dest_reg, inc_val, mult_val,
+ not_every_iteration, maybe_multiple)
+ struct induction *v;
+ rtx insn;
+ rtx dest_reg;
+ rtx inc_val;
+ rtx mult_val;
+ int not_every_iteration;
+ int maybe_multiple;
+{
+ struct iv_class *bl;
+
+ v->insn = insn;
+ v->src_reg = dest_reg;
+ v->dest_reg = dest_reg;
+ v->mult_val = mult_val;
+ v->add_val = inc_val;
+ v->mode = GET_MODE (dest_reg);
+ v->always_computable = ! not_every_iteration;
+ v->maybe_multiple = maybe_multiple;
+
+ /* Add this to the reg's iv_class, creating a class
+ if this is the first incrementation of the reg. */
+
+ bl = reg_biv_class[REGNO (dest_reg)];
+ if (bl == 0)
+ {
+ /* Create and initialize new iv_class. */
+
+ bl = (struct iv_class *) oballoc (sizeof (struct iv_class));
+
+ bl->regno = REGNO (dest_reg);
+ bl->biv = 0;
+ bl->giv = 0;
+ bl->biv_count = 0;
+ bl->giv_count = 0;
+
+ /* Set initial value to the reg itself. */
+ bl->initial_value = dest_reg;
+ /* We haven't seen the initializing insn yet */
+ bl->init_insn = 0;
+ bl->init_set = 0;
+ bl->initial_test = 0;
+ bl->incremented = 0;
+ bl->eliminable = 0;
+ bl->nonneg = 0;
+ bl->reversed = 0;
+ bl->total_benefit = 0;
+
+ /* Add this class to loop_iv_list. */
+ bl->next = loop_iv_list;
+ loop_iv_list = bl;
+
+ /* Put it in the array of biv register classes. */
+ reg_biv_class[REGNO (dest_reg)] = bl;
+ }
+
+ /* Update IV_CLASS entry for this biv. */
+ v->next_iv = bl->biv;
+ bl->biv = v;
+ bl->biv_count++;
+ if (mult_val == const1_rtx)
+ bl->incremented = 1;
+
+ if (loop_dump_stream)
+ {
+ fprintf (loop_dump_stream,
+ "Insn %d: possible biv, reg %d,",
+ INSN_UID (insn), REGNO (dest_reg));
+ if (GET_CODE (inc_val) == CONST_INT)
+ fprintf (loop_dump_stream, " const = %d\n",
+ INTVAL (inc_val));
+ else
+ {
+ fprintf (loop_dump_stream, " const = ");
+ print_rtl (loop_dump_stream, inc_val);
+ fprintf (loop_dump_stream, "\n");
+ }
+ }
+}
+
+/* Fill in the data about one giv.
+ V is the `struct induction' in which we record the giv. (It is
+ allocated by the caller, with alloca.)
+ INSN is the insn that sets it.
+ BENEFIT estimates the savings from deleting this insn.
+ TYPE is DEST_REG or DEST_ADDR; it says whether the giv is computed
+ into a register or is used as a memory address.
+
+ SRC_REG is the biv reg which the giv is computed from.
+ DEST_REG is the giv's reg (if the giv is stored in a reg).
+ MULT_VAL and ADD_VAL are the coefficients used to compute the giv.
+ LOCATION points to the place where this giv's value appears in INSN. */
+
+static void
+record_giv (v, insn, src_reg, dest_reg, mult_val, add_val, benefit,
+ type, not_every_iteration, location, loop_start, loop_end)
+ struct induction *v;
+ rtx insn;
+ rtx src_reg;
+ rtx dest_reg;
+ rtx mult_val, add_val;
+ int benefit;
+ enum g_types type;
+ int not_every_iteration;
+ rtx *location;
+ rtx loop_start, loop_end;
+{
+ struct induction *b;
+ struct iv_class *bl;
+ rtx set = single_set (insn);
+ rtx p;
+
+ v->insn = insn;
+ v->src_reg = src_reg;
+ v->giv_type = type;
+ v->dest_reg = dest_reg;
+ v->mult_val = mult_val;
+ v->add_val = add_val;
+ v->benefit = benefit;
+ v->location = location;
+ v->cant_derive = 0;
+ v->combined_with = 0;
+ v->maybe_multiple = 0;
+ v->maybe_dead = 0;
+ v->derive_adjustment = 0;
+ v->same = 0;
+ v->ignore = 0;
+ v->new_reg = 0;
+ v->final_value = 0;
+
+ /* The v->always_computable field is used in update_giv_derive, to
+ determine whether a giv can be used to derive another giv. For a
+ DEST_REG giv, INSN computes a new value for the giv, so its value
+ isn't computable if INSN insn't executed every iteration.
+ However, for a DEST_ADDR giv, INSN merely uses the value of the giv;
+ it does not compute a new value. Hence the value is always computable
+ regardless of whether INSN is executed each iteration. */
+
+ if (type == DEST_ADDR)
+ v->always_computable = 1;
+ else
+ v->always_computable = ! not_every_iteration;
+
+ if (type == DEST_ADDR)
+ {
+ v->mode = GET_MODE (*location);
+ v->lifetime = 1;
+ v->times_used = 1;
+ }
+ else /* type == DEST_REG */
+ {
+ v->mode = GET_MODE (SET_DEST (set));
+
+ v->lifetime = (uid_luid[regno_last_uid[REGNO (dest_reg)]]
+ - uid_luid[regno_first_uid[REGNO (dest_reg)]]);
+
+ v->times_used = n_times_used[REGNO (dest_reg)];
+
+ /* If the lifetime is zero, it means that this register is
+ really a dead store. So mark this as a giv that can be
+ ignored. This will not prevent the biv from being eliminated. */
+ if (v->lifetime == 0)
+ v->ignore = 1;
+
+ reg_iv_type[REGNO (dest_reg)] = GENERAL_INDUCT;
+ reg_iv_info[REGNO (dest_reg)] = v;
+ }
+
+ /* Add the giv to the class of givs computed from one biv. */
+
+ bl = reg_biv_class[REGNO (src_reg)];
+ if (bl)
+ {
+ v->next_iv = bl->giv;
+ bl->giv = v;
+ /* Don't count DEST_ADDR. This is supposed to count the number of
+ insns that calculate givs. */
+ if (type == DEST_REG)
+ bl->giv_count++;
+ bl->total_benefit += benefit;
+ }
+ else
+ /* Fatal error, biv missing for this giv? */
+ abort ();
+
+ if (type == DEST_ADDR)
+ v->replaceable = 1;
+ else
+ {
+ /* The giv can be replaced outright by the reduced register only if all
+ of the following conditions are true:
+ - the insn that sets the giv is always executed on any iteration
+ on which the giv is used at all
+ (there are two ways to deduce this:
+ either the insn is executed on every iteration,
+ or all uses follow that insn in the same basic block),
+ - the giv is not used outside the loop
+ - no assignments to the biv occur during the giv's lifetime. */
+
+ if (regno_first_uid[REGNO (dest_reg)] == INSN_UID (insn)
+ /* Previous line always fails if INSN was moved by loop opt. */
+ && uid_luid[regno_last_uid[REGNO (dest_reg)]] < INSN_LUID (loop_end)
+ && (! not_every_iteration
+ || last_use_this_basic_block (dest_reg, insn)))
+ {
+ /* Now check that there are no assignments to the biv within the
+ giv's lifetime. This requires two separate checks. */
+
+ /* Check each biv update, and fail if any are between the first
+ and last use of the giv.
+
+ If this loop contains an inner loop that was unrolled, then
+ the insn modifying the biv may have been emitted by the loop
+ unrolling code, and hence does not have a valid luid. Just
+ mark the biv as not replaceable in this case. It is not very
+ useful as a biv, because it is used in two different loops.
+ It is very unlikely that we would be able to optimize the giv
+ using this biv anyways. */
+
+ v->replaceable = 1;
+ for (b = bl->biv; b; b = b->next_iv)
+ {
+ if (INSN_UID (b->insn) >= max_uid_for_loop
+ || ((uid_luid[INSN_UID (b->insn)]
+ >= uid_luid[regno_first_uid[REGNO (dest_reg)]])
+ && (uid_luid[INSN_UID (b->insn)]
+ <= uid_luid[regno_last_uid[REGNO (dest_reg)]])))
+ {
+ v->replaceable = 0;
+ v->not_replaceable = 1;
+ break;
+ }
+ }
+
+ /* Check each insn between the first and last use of the giv,
+ and fail if any of them are branches that jump to a named label
+ outside this range, but still inside the loop. This catches
+ cases of spaghetti code where the execution order of insns
+ is not linear, and hence the above test fails. For example,
+ in the following code, j is not replaceable:
+ for (i = 0; i < 100; ) {
+ L0: j = 4*i; goto L1;
+ L2: k = j; goto L3;
+ L1: i++; goto L2;
+ L3: ; }
+ printf ("k = %d\n", k); }
+ This test is conservative, but this test succeeds rarely enough
+ that it isn't a problem. See also check_final_value below. */
+
+ if (v->replaceable)
+ for (p = insn;
+ INSN_UID (p) >= max_uid_for_loop
+ || INSN_LUID (p) < uid_luid[regno_last_uid[REGNO (dest_reg)]];
+ p = NEXT_INSN (p))
+ {
+ if (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p)
+ && LABEL_NAME (JUMP_LABEL (p))
+ && ((INSN_LUID (JUMP_LABEL (p)) > INSN_LUID (loop_start)
+ && (INSN_LUID (JUMP_LABEL (p))
+ < uid_luid[regno_first_uid[REGNO (dest_reg)]]))
+ || (INSN_LUID (JUMP_LABEL (p)) < INSN_LUID (loop_end)
+ && (INSN_LUID (JUMP_LABEL (p))
+ > uid_luid[regno_last_uid[REGNO (dest_reg)]]))))
+ {
+ v->replaceable = 0;
+ v->not_replaceable = 1;
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Found branch outside giv lifetime.\n");
+
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* May still be replaceable, we don't have enough info here to
+ decide. */
+ v->replaceable = 0;
+ v->not_replaceable = 0;
+ }
+ }
+
+ if (loop_dump_stream)
+ {
+ if (type == DEST_REG)
+ fprintf (loop_dump_stream, "Insn %d: giv reg %d",
+ INSN_UID (insn), REGNO (dest_reg));
+ else
+ fprintf (loop_dump_stream, "Insn %d: dest address",
+ INSN_UID (insn));
+
+ fprintf (loop_dump_stream, " src reg %d benefit %d",
+ REGNO (src_reg), v->benefit);
+ fprintf (loop_dump_stream, " used %d lifetime %d",
+ v->times_used, v->lifetime);
+
+ if (v->replaceable)
+ fprintf (loop_dump_stream, " replaceable");
+
+ if (GET_CODE (mult_val) == CONST_INT)
+ fprintf (loop_dump_stream, " mult %d",
+ INTVAL (mult_val));
+ else
+ {
+ fprintf (loop_dump_stream, " mult ");
+ print_rtl (loop_dump_stream, mult_val);
+ }
+
+ if (GET_CODE (add_val) == CONST_INT)
+ fprintf (loop_dump_stream, " add %d",
+ INTVAL (add_val));
+ else
+ {
+ fprintf (loop_dump_stream, " add ");
+ print_rtl (loop_dump_stream, add_val);
+ }
+ }
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "\n");
+
+}
+
+
+/* All this does is determine whether a giv can be made replaceable because
+ its final value can be calculated. This code can not be part of record_giv
+ above, because final_giv_value requires that the number of loop iterations
+ be known, and that can not be accurately calculated until after all givs
+ have been identified. */
+
+static void
+check_final_value (v, loop_start, loop_end)
+ struct induction *v;
+ rtx loop_start, loop_end;
+{
+ struct iv_class *bl;
+ rtx final_value = 0;
+
+ bl = reg_biv_class[REGNO (v->src_reg)];
+
+ /* DEST_ADDR givs will never reach here, because they are always marked
+ replaceable above in record_giv. */
+
+ /* The giv can be replaced outright by the reduced register only if all
+ of the following conditions are true:
+ - the insn that sets the giv is always executed on any iteration
+ on which the giv is used at all
+ (there are two ways to deduce this:
+ either the insn is executed on every iteration,
+ or all uses follow that insn in the same basic block),
+ - its final value can be calculated (this condition is different
+ than the one above in record_giv)
+ - no assignments to the biv occur during the giv's lifetime. */
+
+#if 0
+ /* This is only called now when replaceable is known to be false. */
+ /* Clear replaceable, so that it won't confuse final_giv_value. */
+ v->replaceable = 0;
+#endif
+
+ if ((final_value = final_giv_value (v, loop_start, loop_end))
+ && (v->always_computable || last_use_this_basic_block (v->dest_reg, v->insn)))
+ {
+ int biv_increment_seen = 0;
+ rtx p = v->insn;
+ rtx last_giv_use;
+
+ v->replaceable = 1;
+
+ /* When trying to determine whether or not a biv increment occurs
+ during the lifetime of the giv, we can ignore uses of the variable
+ outside the loop because final_value is true. Hence we can not
+ use regno_last_uid and regno_first_uid as above in record_giv. */
+
+ /* Search the loop to determine whether any assignments to the
+ biv occur during the giv's lifetime. Start with the insn
+ that sets the giv, and search around the loop until we come
+ back to that insn again.
+
+ Also fail if there is a jump within the giv's lifetime that jumps
+ to somewhere outside the lifetime but still within the loop. This
+ catches spaghetti code where the execution order is not linear, and
+ hence the above test fails. Here we assume that the giv lifetime
+ does not extend from one iteration of the loop to the next, so as
+ to make the test easier. Since the lifetime isn't known yet,
+ this requires two loops. See also record_giv above. */
+
+ last_giv_use = v->insn;
+
+ while (1)
+ {
+ p = NEXT_INSN (p);
+ if (p == loop_end)
+ p = NEXT_INSN (loop_start);
+ if (p == v->insn)
+ break;
+
+ if (GET_CODE (p) == INSN || GET_CODE (p) == JUMP_INSN
+ || GET_CODE (p) == CALL_INSN)
+ {
+ if (biv_increment_seen)
+ {
+ if (reg_mentioned_p (v->dest_reg, PATTERN (p)))
+ {
+ v->replaceable = 0;
+ v->not_replaceable = 1;
+ break;
+ }
+ }
+ else if (GET_CODE (PATTERN (p)) == SET
+ && SET_DEST (PATTERN (p)) == v->src_reg)
+ biv_increment_seen = 1;
+ else if (reg_mentioned_p (v->dest_reg, PATTERN (p)))
+ last_giv_use = p;
+ }
+ }
+
+ /* Now that the lifetime of the giv is known, check for branches
+ from within the lifetime to outside the lifetime if it is still
+ replaceable. */
+
+ if (v->replaceable)
+ {
+ p = v->insn;
+ while (1)
+ {
+ p = NEXT_INSN (p);
+ if (p == loop_end)
+ p = NEXT_INSN (loop_start);
+ if (p == last_giv_use)
+ break;
+
+ if (GET_CODE (p) == JUMP_INSN && JUMP_LABEL (p)
+ && LABEL_NAME (JUMP_LABEL (p))
+ && ((INSN_LUID (JUMP_LABEL (p)) < INSN_LUID (v->insn)
+ && INSN_LUID (JUMP_LABEL (p)) > INSN_LUID (loop_start))
+ || (INSN_LUID (JUMP_LABEL (p)) > INSN_LUID (last_giv_use)
+ && INSN_LUID (JUMP_LABEL (p)) < INSN_LUID (loop_end))))
+ {
+ v->replaceable = 0;
+ v->not_replaceable = 1;
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Found branch outside giv lifetime.\n");
+
+ break;
+ }
+ }
+ }
+
+ /* If it is replaceable, then save the final value. */
+ if (v->replaceable)
+ v->final_value = final_value;
+ }
+
+ if (loop_dump_stream && v->replaceable)
+ fprintf (loop_dump_stream, "Insn %d: giv reg %d final_value replaceable\n",
+ INSN_UID (v->insn), REGNO (v->dest_reg));
+}
+
+/* Update the status of whether a giv can derive other givs.
+
+ We need to do something special if there is or may be an update to the biv
+ between the time the giv is defined and the time it is used to derive
+ another giv.
+
+ In addition, a giv that is only conditionally set is not allowed to
+ derive another giv once a label has been passed.
+
+ The cases we look at are when a label or an update to a biv is passed. */
+
+static void
+update_giv_derive (p)
+ rtx p;
+{
+ struct iv_class *bl;
+ struct induction *biv, *giv;
+ rtx tem;
+ int dummy;
+
+ /* Search all IV classes, then all bivs, and finally all givs.
+
+ There are three cases we are concerned with. First we have the situation
+ of a giv that is only updated conditionally. In that case, it may not
+ derive any givs after a label is passed.
+
+ The second case is when a biv update occurs, or may occur, after the
+ definition of a giv. For certain biv updates (see below) that are
+ known to occur between the giv definition and use, we can adjust the
+ giv definition. For others, or when the biv update is conditional,
+ we must prevent the giv from deriving any other givs. There are two
+ sub-cases within this case.
+
+ If this is a label, we are concerned with any biv update that is done
+ conditionally, since it may be done after the giv is defined followed by
+ a branch here (actually, we need to pass both a jump and a label, but
+ this extra tracking doesn't seem worth it).
+
+ If this is a jump, we are concerned about any biv update that may be
+ executed multiple times. We are actually only concerned about
+ backward jumps, but it is probably not worth performing the test
+ on the jump again here.
+
+ If this is a biv update, we must adjust the giv status to show that a
+ subsequent biv update was performed. If this adjustment cannot be done,
+ the giv cannot derive further givs. */
+
+ for (bl = loop_iv_list; bl; bl = bl->next)
+ for (biv = bl->biv; biv; biv = biv->next_iv)
+ if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN
+ || biv->insn == p)
+ {
+ for (giv = bl->giv; giv; giv = giv->next_iv)
+ {
+ /* If cant_derive is already true, there is no point in
+ checking all of these conditions again. */
+ if (giv->cant_derive)
+ continue;
+
+ /* If this giv is conditionally set and we have passed a label,
+ it cannot derive anything. */
+ if (GET_CODE (p) == CODE_LABEL && ! giv->always_computable)
+ giv->cant_derive = 1;
+
+ /* Skip givs that have mult_val == 0, since
+ they are really invariants. Also skip those that are
+ replaceable, since we know their lifetime doesn't contain
+ any biv update. */
+ else if (giv->mult_val == const0_rtx || giv->replaceable)
+ continue;
+
+ /* The only way we can allow this giv to derive another
+ is if this is a biv increment and we can form the product
+ of biv->add_val and giv->mult_val. In this case, we will
+ be able to compute a compensation. */
+ else if (biv->insn == p)
+ {
+ tem = 0;
+
+ if (biv->mult_val == const1_rtx)
+ tem = simplify_giv_expr (gen_rtx (MULT, giv->mode,
+ biv->add_val,
+ giv->mult_val),
+ &dummy);
+
+ if (tem && giv->derive_adjustment)
+ tem = simplify_giv_expr (gen_rtx (PLUS, giv->mode, tem,
+ giv->derive_adjustment),
+ &dummy);
+ if (tem)
+ giv->derive_adjustment = tem;
+ else
+ giv->cant_derive = 1;
+ }
+ else if ((GET_CODE (p) == CODE_LABEL && ! biv->always_computable)
+ || (GET_CODE (p) == JUMP_INSN && biv->maybe_multiple))
+ giv->cant_derive = 1;
+ }
+ }
+}
+
+/* Check whether an insn is an increment legitimate for a basic induction var.
+ X is the source of insn P, or a part of it.
+ MODE is the mode in which X should be interpreted.
+
+ DEST_REG is the putative biv, also the destination of the insn.
+ We accept patterns of these forms:
+ REG = REG + INVARIANT (includes REG = REG - CONSTANT)
+ REG = INVARIANT + REG
+
+ If X is suitable, we return 1, set *MULT_VAL to CONST1_RTX,
+ and store the additive term into *INC_VAL.
+
+ If X is an assignment of an invariant into DEST_REG, we set
+ *MULT_VAL to CONST0_RTX, and store the invariant into *INC_VAL.
+
+ We also want to detect a BIV when it corresponds to a variable
+ whose mode was promoted via PROMOTED_MODE. In that case, an increment
+ of the variable may be a PLUS that adds a SUBREG of that variable to
+ an invariant and then sign- or zero-extends the result of the PLUS
+ into the variable.
+
+ Most GIVs in such cases will be in the promoted mode, since that is the
+ probably the natural computation mode (and almost certainly the mode
+ used for addresses) on the machine. So we view the pseudo-reg containing
+ the variable as the BIV, as if it were simply incremented.
+
+ Note that treating the entire pseudo as a BIV will result in making
+ simple increments to any GIVs based on it. However, if the variable
+ overflows in its declared mode but not its promoted mode, the result will
+ be incorrect. This is acceptable if the variable is signed, since
+ overflows in such cases are undefined, but not if it is unsigned, since
+ those overflows are defined. So we only check for SIGN_EXTEND and
+ not ZERO_EXTEND.
+
+ If we cannot find a biv, we return 0. */
+
+static int
+basic_induction_var (x, mode, dest_reg, p, inc_val, mult_val)
+ register rtx x;
+ enum machine_mode mode;
+ rtx p;
+ rtx dest_reg;
+ rtx *inc_val;
+ rtx *mult_val;
+{
+ register enum rtx_code code;
+ rtx arg;
+ rtx insn, set = 0;
+
+ code = GET_CODE (x);
+ switch (code)
+ {
+ case PLUS:
+ if (XEXP (x, 0) == dest_reg
+ || (GET_CODE (XEXP (x, 0)) == SUBREG
+ && SUBREG_PROMOTED_VAR_P (XEXP (x, 0))
+ && SUBREG_REG (XEXP (x, 0)) == dest_reg))
+ arg = XEXP (x, 1);
+ else if (XEXP (x, 1) == dest_reg
+ || (GET_CODE (XEXP (x, 1)) == SUBREG
+ && SUBREG_PROMOTED_VAR_P (XEXP (x, 1))
+ && SUBREG_REG (XEXP (x, 1)) == dest_reg))
+ arg = XEXP (x, 0);
+ else
+ return 0;
+
+ if (invariant_p (arg) != 1)
+ return 0;
+
+ *inc_val = convert_modes (GET_MODE (dest_reg), GET_MODE (x), arg, 0);
+ *mult_val = const1_rtx;
+ return 1;
+
+ case SUBREG:
+ /* If this is a SUBREG for a promoted variable, check the inner
+ value. */
+ if (SUBREG_PROMOTED_VAR_P (x))
+ return basic_induction_var (SUBREG_REG (x), GET_MODE (SUBREG_REG (x)),
+ dest_reg, p, inc_val, mult_val);
+
+ case REG:
+ /* If this register is assigned in the previous insn, look at its
+ source, but don't go outside the loop or past a label. */
+
+ for (insn = PREV_INSN (p);
+ (insn && GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG);
+ insn = PREV_INSN (insn))
+ ;
+
+ if (insn)
+ set = single_set (insn);
+
+ if (set != 0
+ && (SET_DEST (set) == x
+ || (GET_CODE (SET_DEST (set)) == SUBREG
+ && (GET_MODE_SIZE (GET_MODE (SET_DEST (set)))
+ <= UNITS_PER_WORD)
+ && SUBREG_REG (SET_DEST (set)) == x)))
+ return basic_induction_var (SET_SRC (set),
+ (GET_MODE (SET_SRC (set)) == VOIDmode
+ ? GET_MODE (x)
+ : GET_MODE (SET_SRC (set))),
+ dest_reg, insn,
+ inc_val, mult_val);
+ /* ... fall through ... */
+
+ /* Can accept constant setting of biv only when inside inner most loop.
+ Otherwise, a biv of an inner loop may be incorrectly recognized
+ as a biv of the outer loop,
+ causing code to be moved INTO the inner loop. */
+ case MEM:
+ if (invariant_p (x) != 1)
+ return 0;
+ case CONST_INT:
+ case SYMBOL_REF:
+ case CONST:
+ if (loops_enclosed == 1)
+ {
+ /* Possible bug here? Perhaps we don't know the mode of X. */
+ *inc_val = convert_modes (GET_MODE (dest_reg), mode, x, 0);
+ *mult_val = const0_rtx;
+ return 1;
+ }
+ else
+ return 0;
+
+ case SIGN_EXTEND:
+ return basic_induction_var (XEXP (x, 0), GET_MODE (XEXP (x, 0)),
+ dest_reg, p, inc_val, mult_val);
+ case ASHIFTRT:
+ /* Similar, since this can be a sign extension. */
+ for (insn = PREV_INSN (p);
+ (insn && GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG);
+ insn = PREV_INSN (insn))
+ ;
+
+ if (insn)
+ set = single_set (insn);
+
+ if (set && SET_DEST (set) == XEXP (x, 0)
+ && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) >= 0
+ && GET_CODE (SET_SRC (set)) == ASHIFT
+ && XEXP (x, 1) == XEXP (SET_SRC (set), 1))
+ return basic_induction_var (XEXP (SET_SRC (set), 0),
+ GET_MODE (XEXP (x, 0)),
+ dest_reg, insn, inc_val, mult_val);
+ return 0;
+
+ default:
+ return 0;
+ }
+}
+
+/* A general induction variable (giv) is any quantity that is a linear
+ function of a basic induction variable,
+ i.e. giv = biv * mult_val + add_val.
+ The coefficients can be any loop invariant quantity.
+ A giv need not be computed directly from the biv;
+ it can be computed by way of other givs. */
+
+/* Determine whether X computes a giv.
+ If it does, return a nonzero value
+ which is the benefit from eliminating the computation of X;
+ set *SRC_REG to the register of the biv that it is computed from;
+ set *ADD_VAL and *MULT_VAL to the coefficients,
+ such that the value of X is biv * mult + add; */
+
+static int
+general_induction_var (x, src_reg, add_val, mult_val)
+ rtx x;
+ rtx *src_reg;
+ rtx *add_val;
+ rtx *mult_val;
+{
+ rtx orig_x = x;
+ int benefit = 0;
+ char *storage;
+
+ /* If this is an invariant, forget it, it isn't a giv. */
+ if (invariant_p (x) == 1)
+ return 0;
+
+ /* See if the expression could be a giv and get its form.
+ Mark our place on the obstack in case we don't find a giv. */
+ storage = (char *) oballoc (0);
+ x = simplify_giv_expr (x, &benefit);
+ if (x == 0)
+ {
+ obfree (storage);
+ return 0;
+ }
+
+ switch (GET_CODE (x))
+ {
+ case USE:
+ case CONST_INT:
+ /* Since this is now an invariant and wasn't before, it must be a giv
+ with MULT_VAL == 0. It doesn't matter which BIV we associate this
+ with. */
+ *src_reg = loop_iv_list->biv->dest_reg;
+ *mult_val = const0_rtx;
+ *add_val = x;
+ break;
+
+ case REG:
+ /* This is equivalent to a BIV. */
+ *src_reg = x;
+ *mult_val = const1_rtx;
+ *add_val = const0_rtx;
+ break;
+
+ case PLUS:
+ /* Either (plus (biv) (invar)) or
+ (plus (mult (biv) (invar_1)) (invar_2)). */
+ if (GET_CODE (XEXP (x, 0)) == MULT)
+ {
+ *src_reg = XEXP (XEXP (x, 0), 0);
+ *mult_val = XEXP (XEXP (x, 0), 1);
+ }
+ else
+ {
+ *src_reg = XEXP (x, 0);
+ *mult_val = const1_rtx;
+ }
+ *add_val = XEXP (x, 1);
+ break;
+
+ case MULT:
+ /* ADD_VAL is zero. */
+ *src_reg = XEXP (x, 0);
+ *mult_val = XEXP (x, 1);
+ *add_val = const0_rtx;
+ break;
+
+ default:
+ abort ();
+ }
+
+ /* Remove any enclosing USE from ADD_VAL and MULT_VAL (there will be
+ unless they are CONST_INT). */
+ if (GET_CODE (*add_val) == USE)
+ *add_val = XEXP (*add_val, 0);
+ if (GET_CODE (*mult_val) == USE)
+ *mult_val = XEXP (*mult_val, 0);
+
+ benefit += rtx_cost (orig_x, SET);
+
+ /* Always return some benefit if this is a giv so it will be detected
+ as such. This allows elimination of bivs that might otherwise
+ not be eliminated. */
+ return benefit == 0 ? 1 : benefit;
+}
+
+/* Given an expression, X, try to form it as a linear function of a biv.
+ We will canonicalize it to be of the form
+ (plus (mult (BIV) (invar_1))
+ (invar_2))
+ with possible degeneracies.
+
+ The invariant expressions must each be of a form that can be used as a
+ machine operand. We surround then with a USE rtx (a hack, but localized
+ and certainly unambiguous!) if not a CONST_INT for simplicity in this
+ routine; it is the caller's responsibility to strip them.
+
+ If no such canonicalization is possible (i.e., two biv's are used or an
+ expression that is neither invariant nor a biv or giv), this routine
+ returns 0.
+
+ For a non-zero return, the result will have a code of CONST_INT, USE,
+ REG (for a BIV), PLUS, or MULT. No other codes will occur.
+
+ *BENEFIT will be incremented by the benefit of any sub-giv encountered. */
+
+static rtx
+simplify_giv_expr (x, benefit)
+ rtx x;
+ int *benefit;
+{
+ enum machine_mode mode = GET_MODE (x);
+ rtx arg0, arg1;
+ rtx tem;
+
+ /* If this is not an integer mode, or if we cannot do arithmetic in this
+ mode, this can't be a giv. */
+ if (mode != VOIDmode
+ && (GET_MODE_CLASS (mode) != MODE_INT
+ || GET_MODE_BITSIZE (mode) > HOST_BITS_PER_WIDE_INT))
+ return 0;
+
+ switch (GET_CODE (x))
+ {
+ case PLUS:
+ arg0 = simplify_giv_expr (XEXP (x, 0), benefit);
+ arg1 = simplify_giv_expr (XEXP (x, 1), benefit);
+ if (arg0 == 0 || arg1 == 0)
+ return 0;
+
+ /* Put constant last, CONST_INT last if both constant. */
+ if ((GET_CODE (arg0) == USE
+ || GET_CODE (arg0) == CONST_INT)
+ && GET_CODE (arg1) != CONST_INT)
+ tem = arg0, arg0 = arg1, arg1 = tem;
+
+ /* Handle addition of zero, then addition of an invariant. */
+ if (arg1 == const0_rtx)
+ return arg0;
+ else if (GET_CODE (arg1) == CONST_INT || GET_CODE (arg1) == USE)
+ switch (GET_CODE (arg0))
+ {
+ case CONST_INT:
+ case USE:
+ /* Both invariant. Only valid if sum is machine operand.
+ First strip off possible USE on first operand. */
+ if (GET_CODE (arg0) == USE)
+ arg0 = XEXP (arg0, 0);
+
+ tem = 0;
+ if (CONSTANT_P (arg0) && GET_CODE (arg1) == CONST_INT)
+ {
+ tem = plus_constant (arg0, INTVAL (arg1));
+ if (GET_CODE (tem) != CONST_INT)
+ tem = gen_rtx (USE, mode, tem);
+ }
+
+ return tem;
+
+ case REG:
+ case MULT:
+ /* biv + invar or mult + invar. Return sum. */
+ return gen_rtx (PLUS, mode, arg0, arg1);
+
+ case PLUS:
+ /* (a + invar_1) + invar_2. Associate. */
+ return simplify_giv_expr (gen_rtx (PLUS, mode,
+ XEXP (arg0, 0),
+ gen_rtx (PLUS, mode,
+ XEXP (arg0, 1), arg1)),
+ benefit);
+
+ default:
+ abort ();
+ }
+
+ /* Each argument must be either REG, PLUS, or MULT. Convert REG to
+ MULT to reduce cases. */
+ if (GET_CODE (arg0) == REG)
+ arg0 = gen_rtx (MULT, mode, arg0, const1_rtx);
+ if (GET_CODE (arg1) == REG)
+ arg1 = gen_rtx (MULT, mode, arg1, const1_rtx);
+
+ /* Now have PLUS + PLUS, PLUS + MULT, MULT + PLUS, or MULT + MULT.
+ Put a MULT first, leaving PLUS + PLUS, MULT + PLUS, or MULT + MULT.
+ Recurse to associate the second PLUS. */
+ if (GET_CODE (arg1) == MULT)
+ tem = arg0, arg0 = arg1, arg1 = tem;
+
+ if (GET_CODE (arg1) == PLUS)
+ return simplify_giv_expr (gen_rtx (PLUS, mode,
+ gen_rtx (PLUS, mode,
+ arg0, XEXP (arg1, 0)),
+ XEXP (arg1, 1)),
+ benefit);
+
+ /* Now must have MULT + MULT. Distribute if same biv, else not giv. */
+ if (GET_CODE (arg0) != MULT || GET_CODE (arg1) != MULT)
+ abort ();
+
+ if (XEXP (arg0, 0) != XEXP (arg1, 0))
+ return 0;
+
+ return simplify_giv_expr (gen_rtx (MULT, mode,
+ XEXP (arg0, 0),
+ gen_rtx (PLUS, mode,
+ XEXP (arg0, 1),
+ XEXP (arg1, 1))),
+ benefit);
+
+ case MINUS:
+ /* Handle "a - b" as "a + b * (-1)". */
+ return simplify_giv_expr (gen_rtx (PLUS, mode,
+ XEXP (x, 0),
+ gen_rtx (MULT, mode,
+ XEXP (x, 1), constm1_rtx)),
+ benefit);
+
+ case MULT:
+ arg0 = simplify_giv_expr (XEXP (x, 0), benefit);
+ arg1 = simplify_giv_expr (XEXP (x, 1), benefit);
+ if (arg0 == 0 || arg1 == 0)
+ return 0;
+
+ /* Put constant last, CONST_INT last if both constant. */
+ if ((GET_CODE (arg0) == USE || GET_CODE (arg0) == CONST_INT)
+ && GET_CODE (arg1) != CONST_INT)
+ tem = arg0, arg0 = arg1, arg1 = tem;
+
+ /* If second argument is not now constant, not giv. */
+ if (GET_CODE (arg1) != USE && GET_CODE (arg1) != CONST_INT)
+ return 0;
+
+ /* Handle multiply by 0 or 1. */
+ if (arg1 == const0_rtx)
+ return const0_rtx;
+
+ else if (arg1 == const1_rtx)
+ return arg0;
+
+ switch (GET_CODE (arg0))
+ {
+ case REG:
+ /* biv * invar. Done. */
+ return gen_rtx (MULT, mode, arg0, arg1);
+
+ case CONST_INT:
+ /* Product of two constants. */
+ return GEN_INT (INTVAL (arg0) * INTVAL (arg1));
+
+ case USE:
+ /* invar * invar. Not giv. */
+ return 0;
+
+ case MULT:
+ /* (a * invar_1) * invar_2. Associate. */
+ return simplify_giv_expr (gen_rtx (MULT, mode,
+ XEXP (arg0, 0),
+ gen_rtx (MULT, mode,
+ XEXP (arg0, 1), arg1)),
+ benefit);
+
+ case PLUS:
+ /* (a + invar_1) * invar_2. Distribute. */
+ return simplify_giv_expr (gen_rtx (PLUS, mode,
+ gen_rtx (MULT, mode,
+ XEXP (arg0, 0), arg1),
+ gen_rtx (MULT, mode,
+ XEXP (arg0, 1), arg1)),
+ benefit);
+
+ default:
+ abort ();
+ }
+
+ case ASHIFT:
+ /* Shift by constant is multiply by power of two. */
+ if (GET_CODE (XEXP (x, 1)) != CONST_INT)
+ return 0;
+
+ return simplify_giv_expr (gen_rtx (MULT, mode,
+ XEXP (x, 0),
+ GEN_INT ((HOST_WIDE_INT) 1
+ << INTVAL (XEXP (x, 1)))),
+ benefit);
+
+ case NEG:
+ /* "-a" is "a * (-1)" */
+ return simplify_giv_expr (gen_rtx (MULT, mode, XEXP (x, 0), constm1_rtx),
+ benefit);
+
+ case NOT:
+ /* "~a" is "-a - 1". Silly, but easy. */
+ return simplify_giv_expr (gen_rtx (MINUS, mode,
+ gen_rtx (NEG, mode, XEXP (x, 0)),
+ const1_rtx),
+ benefit);
+
+ case USE:
+ /* Already in proper form for invariant. */
+ return x;
+
+ case REG:
+ /* If this is a new register, we can't deal with it. */
+ if (REGNO (x) >= max_reg_before_loop)
+ return 0;
+
+ /* Check for biv or giv. */
+ switch (reg_iv_type[REGNO (x)])
+ {
+ case BASIC_INDUCT:
+ return x;
+ case GENERAL_INDUCT:
+ {
+ struct induction *v = reg_iv_info[REGNO (x)];
+
+ /* Form expression from giv and add benefit. Ensure this giv
+ can derive another and subtract any needed adjustment if so. */
+ *benefit += v->benefit;
+ if (v->cant_derive)
+ return 0;
+
+ tem = gen_rtx (PLUS, mode, gen_rtx (MULT, mode,
+ v->src_reg, v->mult_val),
+ v->add_val);
+ if (v->derive_adjustment)
+ tem = gen_rtx (MINUS, mode, tem, v->derive_adjustment);
+ return simplify_giv_expr (tem, benefit);
+ }
+ }
+
+ /* Fall through to general case. */
+ default:
+ /* If invariant, return as USE (unless CONST_INT).
+ Otherwise, not giv. */
+ if (GET_CODE (x) == USE)
+ x = XEXP (x, 0);
+
+ if (invariant_p (x) == 1)
+ {
+ if (GET_CODE (x) == CONST_INT)
+ return x;
+ else
+ return gen_rtx (USE, mode, x);
+ }
+ else
+ return 0;
+ }
+}
+
+/* Help detect a giv that is calculated by several consecutive insns;
+ for example,
+ giv = biv * M
+ giv = giv + A
+ The caller has already identified the first insn P as having a giv as dest;
+ we check that all other insns that set the same register follow
+ immediately after P, that they alter nothing else,
+ and that the result of the last is still a giv.
+
+ The value is 0 if the reg set in P is not really a giv.
+ Otherwise, the value is the amount gained by eliminating
+ all the consecutive insns that compute the value.
+
+ FIRST_BENEFIT is the amount gained by eliminating the first insn, P.
+ SRC_REG is the reg of the biv; DEST_REG is the reg of the giv.
+
+ The coefficients of the ultimate giv value are stored in
+ *MULT_VAL and *ADD_VAL. */
+
+static int
+consec_sets_giv (first_benefit, p, src_reg, dest_reg,
+ add_val, mult_val)
+ int first_benefit;
+ rtx p;
+ rtx src_reg;
+ rtx dest_reg;
+ rtx *add_val;
+ rtx *mult_val;
+{
+ int count;
+ enum rtx_code code;
+ int benefit;
+ rtx temp;
+ rtx set;
+
+ /* Indicate that this is a giv so that we can update the value produced in
+ each insn of the multi-insn sequence.
+
+ This induction structure will be used only by the call to
+ general_induction_var below, so we can allocate it on our stack.
+ If this is a giv, our caller will replace the induct var entry with
+ a new induction structure. */
+ struct induction *v
+ = (struct induction *) alloca (sizeof (struct induction));
+ v->src_reg = src_reg;
+ v->mult_val = *mult_val;
+ v->add_val = *add_val;
+ v->benefit = first_benefit;
+ v->cant_derive = 0;
+ v->derive_adjustment = 0;
+
+ reg_iv_type[REGNO (dest_reg)] = GENERAL_INDUCT;
+ reg_iv_info[REGNO (dest_reg)] = v;
+
+ count = n_times_set[REGNO (dest_reg)] - 1;
+
+ while (count > 0)
+ {
+ p = NEXT_INSN (p);
+ code = GET_CODE (p);
+
+ /* If libcall, skip to end of call sequence. */
+ if (code == INSN && (temp = find_reg_note (p, REG_LIBCALL, NULL_RTX)))
+ p = XEXP (temp, 0);
+
+ if (code == INSN
+ && (set = single_set (p))
+ && GET_CODE (SET_DEST (set)) == REG
+ && SET_DEST (set) == dest_reg
+ && ((benefit = general_induction_var (SET_SRC (set), &src_reg,
+ add_val, mult_val))
+ /* Giv created by equivalent expression. */
+ || ((temp = find_reg_note (p, REG_EQUAL, NULL_RTX))
+ && (benefit = general_induction_var (XEXP (temp, 0), &src_reg,
+ add_val, mult_val))))
+ && src_reg == v->src_reg)
+ {
+ if (find_reg_note (p, REG_RETVAL, NULL_RTX))
+ benefit += libcall_benefit (p);
+
+ count--;
+ v->mult_val = *mult_val;
+ v->add_val = *add_val;
+ v->benefit = benefit;
+ }
+ else if (code != NOTE)
+ {
+ /* Allow insns that set something other than this giv to a
+ constant. Such insns are needed on machines which cannot
+ include long constants and should not disqualify a giv. */
+ if (code == INSN
+ && (set = single_set (p))
+ && SET_DEST (set) != dest_reg
+ && CONSTANT_P (SET_SRC (set)))
+ continue;
+
+ reg_iv_type[REGNO (dest_reg)] = UNKNOWN_INDUCT;
+ return 0;
+ }
+ }
+
+ return v->benefit;
+}
+
+/* Return an rtx, if any, that expresses giv G2 as a function of the register
+ represented by G1. If no such expression can be found, or it is clear that
+ it cannot possibly be a valid address, 0 is returned.
+
+ To perform the computation, we note that
+ G1 = a * v + b and
+ G2 = c * v + d
+ where `v' is the biv.
+
+ So G2 = (c/a) * G1 + (d - b*c/a) */
+
+#ifdef ADDRESS_COST
+static rtx
+express_from (g1, g2)
+ struct induction *g1, *g2;
+{
+ rtx mult, add;
+
+ /* The value that G1 will be multiplied by must be a constant integer. Also,
+ the only chance we have of getting a valid address is if b*c/a (see above
+ for notation) is also an integer. */
+ if (GET_CODE (g1->mult_val) != CONST_INT
+ || GET_CODE (g2->mult_val) != CONST_INT
+ || GET_CODE (g1->add_val) != CONST_INT
+ || g1->mult_val == const0_rtx
+ || INTVAL (g2->mult_val) % INTVAL (g1->mult_val) != 0)
+ return 0;
+
+ mult = GEN_INT (INTVAL (g2->mult_val) / INTVAL (g1->mult_val));
+ add = plus_constant (g2->add_val, - INTVAL (g1->add_val) * INTVAL (mult));
+
+ /* Form simplified final result. */
+ if (mult == const0_rtx)
+ return add;
+ else if (mult == const1_rtx)
+ mult = g1->dest_reg;
+ else
+ mult = gen_rtx (MULT, g2->mode, g1->dest_reg, mult);
+
+ if (add == const0_rtx)
+ return mult;
+ else
+ return gen_rtx (PLUS, g2->mode, mult, add);
+}
+#endif
+
+/* Return 1 if giv G2 can be combined with G1. This means that G2 can use
+ (either directly or via an address expression) a register used to represent
+ G1. Set g2->new_reg to a represtation of G1 (normally just
+ g1->dest_reg). */
+
+static int
+combine_givs_p (g1, g2)
+ struct induction *g1, *g2;
+{
+ rtx tem;
+
+ /* If these givs are identical, they can be combined. */
+ if (rtx_equal_p (g1->mult_val, g2->mult_val)
+ && rtx_equal_p (g1->add_val, g2->add_val))
+ {
+ g2->new_reg = g1->dest_reg;
+ return 1;
+ }
+
+#ifdef ADDRESS_COST
+ /* If G2 can be expressed as a function of G1 and that function is valid
+ as an address and no more expensive than using a register for G2,
+ the expression of G2 in terms of G1 can be used. */
+ if (g2->giv_type == DEST_ADDR
+ && (tem = express_from (g1, g2)) != 0
+ && memory_address_p (g2->mem_mode, tem)
+ && ADDRESS_COST (tem) <= ADDRESS_COST (*g2->location))
+ {
+ g2->new_reg = tem;
+ return 1;
+ }
+#endif
+
+ return 0;
+}
+
+/* Check all pairs of givs for iv_class BL and see if any can be combined with
+ any other. If so, point SAME to the giv combined with and set NEW_REG to
+ be an expression (in terms of the other giv's DEST_REG) equivalent to the
+ giv. Also, update BENEFIT and related fields for cost/benefit analysis. */
+
+static void
+combine_givs (bl)
+ struct iv_class *bl;
+{
+ struct induction *g1, *g2;
+ int pass;
+
+ for (g1 = bl->giv; g1; g1 = g1->next_iv)
+ for (pass = 0; pass <= 1; pass++)
+ for (g2 = bl->giv; g2; g2 = g2->next_iv)
+ if (g1 != g2
+ /* First try to combine with replaceable givs, then all givs. */
+ && (g1->replaceable || pass == 1)
+ /* If either has already been combined or is to be ignored, can't
+ combine. */
+ && ! g1->ignore && ! g2->ignore && ! g1->same && ! g2->same
+ /* If something has been based on G2, G2 cannot itself be based
+ on something else. */
+ && ! g2->combined_with
+ && combine_givs_p (g1, g2))
+ {
+ /* g2->new_reg set by `combine_givs_p' */
+ g2->same = g1;
+ g1->combined_with = 1;
+ g1->benefit += g2->benefit;
+ /* ??? The new final_[bg]iv_value code does a much better job
+ of finding replaceable giv's, and hence this code may no
+ longer be necessary. */
+ if (! g2->replaceable && REG_USERVAR_P (g2->dest_reg))
+ g1->benefit -= copy_cost;
+ g1->lifetime += g2->lifetime;
+ g1->times_used += g2->times_used;
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "giv at %d combined with giv at %d\n",
+ INSN_UID (g2->insn), INSN_UID (g1->insn));
+ }
+}
+
+/* EMIT code before INSERT_BEFORE to set REG = B * M + A. */
+
+void
+emit_iv_add_mult (b, m, a, reg, insert_before)
+ rtx b; /* initial value of basic induction variable */
+ rtx m; /* multiplicative constant */
+ rtx a; /* additive constant */
+ rtx reg; /* destination register */
+ rtx insert_before;
+{
+ rtx seq;
+ rtx result;
+
+ /* Prevent unexpected sharing of these rtx. */
+ a = copy_rtx (a);
+ b = copy_rtx (b);
+
+ /* Increase the lifetime of any invariants moved further in code. */
+ update_reg_last_use (a, insert_before);
+ update_reg_last_use (b, insert_before);
+ update_reg_last_use (m, insert_before);
+
+ start_sequence ();
+ result = expand_mult_add (b, reg, m, a, GET_MODE (reg), 0);
+ if (reg != result)
+ emit_move_insn (reg, result);
+ seq = gen_sequence ();
+ end_sequence ();
+
+ emit_insn_before (seq, insert_before);
+}
+
+/* Test whether A * B can be computed without
+ an actual multiply insn. Value is 1 if so. */
+
+static int
+product_cheap_p (a, b)
+ rtx a;
+ rtx b;
+{
+ int i;
+ rtx tmp;
+ struct obstack *old_rtl_obstack = rtl_obstack;
+ char *storage = (char *) obstack_alloc (&temp_obstack, 0);
+ int win = 1;
+
+ /* If only one is constant, make it B. */
+ if (GET_CODE (a) == CONST_INT)
+ tmp = a, a = b, b = tmp;
+
+ /* If first constant, both constant, so don't need multiply. */
+ if (GET_CODE (a) == CONST_INT)
+ return 1;
+
+ /* If second not constant, neither is constant, so would need multiply. */
+ if (GET_CODE (b) != CONST_INT)
+ return 0;
+
+ /* One operand is constant, so might not need multiply insn. Generate the
+ code for the multiply and see if a call or multiply, or long sequence
+ of insns is generated. */
+
+ rtl_obstack = &temp_obstack;
+ start_sequence ();
+ expand_mult (GET_MODE (a), a, b, NULL_RTX, 0);
+ tmp = gen_sequence ();
+ end_sequence ();
+
+ if (GET_CODE (tmp) == SEQUENCE)
+ {
+ if (XVEC (tmp, 0) == 0)
+ win = 1;
+ else if (XVECLEN (tmp, 0) > 3)
+ win = 0;
+ else
+ for (i = 0; i < XVECLEN (tmp, 0); i++)
+ {
+ rtx insn = XVECEXP (tmp, 0, i);
+
+ if (GET_CODE (insn) != INSN
+ || (GET_CODE (PATTERN (insn)) == SET
+ && GET_CODE (SET_SRC (PATTERN (insn))) == MULT)
+ || (GET_CODE (PATTERN (insn)) == PARALLEL
+ && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET
+ && GET_CODE (SET_SRC (XVECEXP (PATTERN (insn), 0, 0))) == MULT))
+ {
+ win = 0;
+ break;
+ }
+ }
+ }
+ else if (GET_CODE (tmp) == SET
+ && GET_CODE (SET_SRC (tmp)) == MULT)
+ win = 0;
+ else if (GET_CODE (tmp) == PARALLEL
+ && GET_CODE (XVECEXP (tmp, 0, 0)) == SET
+ && GET_CODE (SET_SRC (XVECEXP (tmp, 0, 0))) == MULT)
+ win = 0;
+
+ /* Free any storage we obtained in generating this multiply and restore rtl
+ allocation to its normal obstack. */
+ obstack_free (&temp_obstack, storage);
+ rtl_obstack = old_rtl_obstack;
+
+ return win;
+}
+
+/* Check to see if loop can be terminated by a "decrement and branch until
+ zero" instruction. If so, add a REG_NONNEG note to the branch insn if so.
+ Also try reversing an increment loop to a decrement loop
+ to see if the optimization can be performed.
+ Value is nonzero if optimization was performed. */
+
+/* This is useful even if the architecture doesn't have such an insn,
+ because it might change a loops which increments from 0 to n to a loop
+ which decrements from n to 0. A loop that decrements to zero is usually
+ faster than one that increments from zero. */
+
+/* ??? This could be rewritten to use some of the loop unrolling procedures,
+ such as approx_final_value, biv_total_increment, loop_iterations, and
+ final_[bg]iv_value. */
+
+static int
+check_dbra_loop (loop_end, insn_count, loop_start)
+ rtx loop_end;
+ int insn_count;
+ rtx loop_start;
+{
+ struct iv_class *bl;
+ rtx reg;
+ rtx jump_label;
+ rtx final_value;
+ rtx start_value;
+ rtx new_add_val;
+ rtx comparison;
+ rtx before_comparison;
+ rtx p;
+
+ /* If last insn is a conditional branch, and the insn before tests a
+ register value, try to optimize it. Otherwise, we can't do anything. */
+
+ comparison = get_condition_for_loop (PREV_INSN (loop_end));
+ if (comparison == 0)
+ return 0;
+
+ /* Check all of the bivs to see if the compare uses one of them.
+ Skip biv's set more than once because we can't guarantee that
+ it will be zero on the last iteration. Also skip if the biv is
+ used between its update and the test insn. */
+
+ for (bl = loop_iv_list; bl; bl = bl->next)
+ {
+ if (bl->biv_count == 1
+ && bl->biv->dest_reg == XEXP (comparison, 0)
+ && ! reg_used_between_p (regno_reg_rtx[bl->regno], bl->biv->insn,
+ PREV_INSN (PREV_INSN (loop_end))))
+ break;
+ }
+
+ if (! bl)
+ return 0;
+
+ /* Look for the case where the basic induction variable is always
+ nonnegative, and equals zero on the last iteration.
+ In this case, add a reg_note REG_NONNEG, which allows the
+ m68k DBRA instruction to be used. */
+
+ if (((GET_CODE (comparison) == GT
+ && GET_CODE (XEXP (comparison, 1)) == CONST_INT
+ && INTVAL (XEXP (comparison, 1)) == -1)
+ || (GET_CODE (comparison) == NE && XEXP (comparison, 1) == const0_rtx))
+ && GET_CODE (bl->biv->add_val) == CONST_INT
+ && INTVAL (bl->biv->add_val) < 0)
+ {
+ /* Initial value must be greater than 0,
+ init_val % -dec_value == 0 to ensure that it equals zero on
+ the last iteration */
+
+ if (GET_CODE (bl->initial_value) == CONST_INT
+ && INTVAL (bl->initial_value) > 0
+ && (INTVAL (bl->initial_value) %
+ (-INTVAL (bl->biv->add_val))) == 0)
+ {
+ /* register always nonnegative, add REG_NOTE to branch */
+ REG_NOTES (PREV_INSN (loop_end))
+ = gen_rtx (EXPR_LIST, REG_NONNEG, NULL_RTX,
+ REG_NOTES (PREV_INSN (loop_end)));
+ bl->nonneg = 1;
+
+ return 1;
+ }
+
+ /* If the decrement is 1 and the value was tested as >= 0 before
+ the loop, then we can safely optimize. */
+ for (p = loop_start; p; p = PREV_INSN (p))
+ {
+ if (GET_CODE (p) == CODE_LABEL)
+ break;
+ if (GET_CODE (p) != JUMP_INSN)
+ continue;
+
+ before_comparison = get_condition_for_loop (p);
+ if (before_comparison
+ && XEXP (before_comparison, 0) == bl->biv->dest_reg
+ && GET_CODE (before_comparison) == LT
+ && XEXP (before_comparison, 1) == const0_rtx
+ && ! reg_set_between_p (bl->biv->dest_reg, p, loop_start)
+ && INTVAL (bl->biv->add_val) == -1)
+ {
+ REG_NOTES (PREV_INSN (loop_end))
+ = gen_rtx (EXPR_LIST, REG_NONNEG, NULL_RTX,
+ REG_NOTES (PREV_INSN (loop_end)));
+ bl->nonneg = 1;
+
+ return 1;
+ }
+ }
+ }
+ else if (num_mem_sets <= 1)
+ {
+ /* Try to change inc to dec, so can apply above optimization. */
+ /* Can do this if:
+ all registers modified are induction variables or invariant,
+ all memory references have non-overlapping addresses
+ (obviously true if only one write)
+ allow 2 insns for the compare/jump at the end of the loop. */
+ int num_nonfixed_reads = 0;
+ /* 1 if the iteration var is used only to count iterations. */
+ int no_use_except_counting = 0;
+ /* 1 if the loop has no memory store, or it has a single memory store
+ which is reversible. */
+ int reversible_mem_store = 1;
+
+ for (p = loop_start; p != loop_end; p = NEXT_INSN (p))
+ if (GET_RTX_CLASS (GET_CODE (p)) == 'i')
+ num_nonfixed_reads += count_nonfixed_reads (PATTERN (p));
+
+ if (bl->giv_count == 0
+ && ! loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]])
+ {
+ rtx bivreg = regno_reg_rtx[bl->regno];
+
+ /* If there are no givs for this biv, and the only exit is the
+ fall through at the end of the the loop, then
+ see if perhaps there are no uses except to count. */
+ no_use_except_counting = 1;
+ for (p = loop_start; p != loop_end; p = NEXT_INSN (p))
+ if (GET_RTX_CLASS (GET_CODE (p)) == 'i')
+ {
+ rtx set = single_set (p);
+
+ if (set && GET_CODE (SET_DEST (set)) == REG
+ && REGNO (SET_DEST (set)) == bl->regno)
+ /* An insn that sets the biv is okay. */
+ ;
+ else if (p == prev_nonnote_insn (prev_nonnote_insn (loop_end))
+ || p == prev_nonnote_insn (loop_end))
+ /* Don't bother about the end test. */
+ ;
+ else if (reg_mentioned_p (bivreg, PATTERN (p)))
+ /* Any other use of the biv is no good. */
+ {
+ no_use_except_counting = 0;
+ break;
+ }
+ }
+ }
+
+ /* If the loop has a single store, and the destination address is
+ invariant, then we can't reverse the loop, because this address
+ might then have the wrong value at loop exit.
+ This would work if the source was invariant also, however, in that
+ case, the insn should have been moved out of the loop. */
+
+ if (num_mem_sets == 1)
+ reversible_mem_store
+ = (! unknown_address_altered
+ && ! invariant_p (XEXP (loop_store_mems[0], 0)));
+
+ /* This code only acts for innermost loops. Also it simplifies
+ the memory address check by only reversing loops with
+ zero or one memory access.
+ Two memory accesses could involve parts of the same array,
+ and that can't be reversed. */
+
+ if (num_nonfixed_reads <= 1
+ && !loop_has_call
+ && !loop_has_volatile
+ && reversible_mem_store
+ && (no_use_except_counting
+ || (bl->giv_count + bl->biv_count + num_mem_sets
+ + num_movables + 2 == insn_count)))
+ {
+ rtx tem;
+
+ /* Loop can be reversed. */
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "Can reverse loop\n");
+
+ /* Now check other conditions:
+ initial_value must be zero,
+ final_value % add_val == 0, so that when reversed, the
+ biv will be zero on the last iteration.
+
+ This test can probably be improved since +/- 1 in the constant
+ can be obtained by changing LT to LE and vice versa; this is
+ confusing. */
+
+ if (comparison && bl->initial_value == const0_rtx
+ && GET_CODE (XEXP (comparison, 1)) == CONST_INT
+ /* LE gets turned into LT */
+ && GET_CODE (comparison) == LT
+ && (INTVAL (XEXP (comparison, 1))
+ % INTVAL (bl->biv->add_val)) == 0)
+ {
+ /* Register will always be nonnegative, with value
+ 0 on last iteration if loop reversed */
+
+ /* Save some info needed to produce the new insns. */
+ reg = bl->biv->dest_reg;
+ jump_label = XEXP (SET_SRC (PATTERN (PREV_INSN (loop_end))), 1);
+ new_add_val = GEN_INT (- INTVAL (bl->biv->add_val));
+
+ final_value = XEXP (comparison, 1);
+ start_value = GEN_INT (INTVAL (XEXP (comparison, 1))
+ - INTVAL (bl->biv->add_val));
+
+ /* Initialize biv to start_value before loop start.
+ The old initializing insn will be deleted as a
+ dead store by flow.c. */
+ emit_insn_before (gen_move_insn (reg, start_value), loop_start);
+
+ /* Add insn to decrement register, and delete insn
+ that incremented the register. */
+ p = emit_insn_before (gen_add2_insn (reg, new_add_val),
+ bl->biv->insn);
+ delete_insn (bl->biv->insn);
+
+ /* Update biv info to reflect its new status. */
+ bl->biv->insn = p;
+ bl->initial_value = start_value;
+ bl->biv->add_val = new_add_val;
+
+ /* Inc LABEL_NUSES so that delete_insn will
+ not delete the label. */
+ LABEL_NUSES (XEXP (jump_label, 0)) ++;
+
+ /* Emit an insn after the end of the loop to set the biv's
+ proper exit value if it is used anywhere outside the loop. */
+ if ((regno_last_uid[bl->regno]
+ != INSN_UID (PREV_INSN (PREV_INSN (loop_end))))
+ || ! bl->init_insn
+ || regno_first_uid[bl->regno] != INSN_UID (bl->init_insn))
+ emit_insn_after (gen_move_insn (reg, final_value),
+ loop_end);
+
+ /* Delete compare/branch at end of loop. */
+ delete_insn (PREV_INSN (loop_end));
+ delete_insn (PREV_INSN (loop_end));
+
+ /* Add new compare/branch insn at end of loop. */
+ start_sequence ();
+ emit_cmp_insn (reg, const0_rtx, GE, NULL_RTX,
+ GET_MODE (reg), 0, 0);
+ emit_jump_insn (gen_bge (XEXP (jump_label, 0)));
+ tem = gen_sequence ();
+ end_sequence ();
+ emit_jump_insn_before (tem, loop_end);
+
+ for (tem = PREV_INSN (loop_end);
+ tem && GET_CODE (tem) != JUMP_INSN; tem = PREV_INSN (tem))
+ ;
+ if (tem)
+ {
+ JUMP_LABEL (tem) = XEXP (jump_label, 0);
+
+ /* Increment of LABEL_NUSES done above. */
+ /* Register is now always nonnegative,
+ so add REG_NONNEG note to the branch. */
+ REG_NOTES (tem) = gen_rtx (EXPR_LIST, REG_NONNEG, NULL_RTX,
+ REG_NOTES (tem));
+ }
+
+ bl->nonneg = 1;
+
+ /* Mark that this biv has been reversed. Each giv which depends
+ on this biv, and which is also live past the end of the loop
+ will have to be fixed up. */
+
+ bl->reversed = 1;
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Reversed loop and added reg_nonneg\n");
+
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Verify whether the biv BL appears to be eliminable,
+ based on the insns in the loop that refer to it.
+ LOOP_START is the first insn of the loop, and END is the end insn.
+
+ If ELIMINATE_P is non-zero, actually do the elimination.
+
+ THRESHOLD and INSN_COUNT are from loop_optimize and are used to
+ determine whether invariant insns should be placed inside or at the
+ start of the loop. */
+
+static int
+maybe_eliminate_biv (bl, loop_start, end, eliminate_p, threshold, insn_count)
+ struct iv_class *bl;
+ rtx loop_start;
+ rtx end;
+ int eliminate_p;
+ int threshold, insn_count;
+{
+ rtx reg = bl->biv->dest_reg;
+ rtx p;
+
+ /* Scan all insns in the loop, stopping if we find one that uses the
+ biv in a way that we cannot eliminate. */
+
+ for (p = loop_start; p != end; p = NEXT_INSN (p))
+ {
+ enum rtx_code code = GET_CODE (p);
+ rtx where = threshold >= insn_count ? loop_start : p;
+
+ if ((code == INSN || code == JUMP_INSN || code == CALL_INSN)
+ && reg_mentioned_p (reg, PATTERN (p))
+ && ! maybe_eliminate_biv_1 (PATTERN (p), p, bl, eliminate_p, where))
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Cannot eliminate biv %d: biv used in insn %d.\n",
+ bl->regno, INSN_UID (p));
+ break;
+ }
+ }
+
+ if (p == end)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "biv %d %s eliminated.\n",
+ bl->regno, eliminate_p ? "was" : "can be");
+ return 1;
+ }
+
+ return 0;
+}
+
+/* If BL appears in X (part of the pattern of INSN), see if we can
+ eliminate its use. If so, return 1. If not, return 0.
+
+ If BIV does not appear in X, return 1.
+
+ If ELIMINATE_P is non-zero, actually do the elimination. WHERE indicates
+ where extra insns should be added. Depending on how many items have been
+ moved out of the loop, it will either be before INSN or at the start of
+ the loop. */
+
+static int
+maybe_eliminate_biv_1 (x, insn, bl, eliminate_p, where)
+ rtx x, insn;
+ struct iv_class *bl;
+ int eliminate_p;
+ rtx where;
+{
+ enum rtx_code code = GET_CODE (x);
+ rtx reg = bl->biv->dest_reg;
+ enum machine_mode mode = GET_MODE (reg);
+ struct induction *v;
+ rtx arg, new, tem;
+ int arg_operand;
+ char *fmt;
+ int i, j;
+
+ switch (code)
+ {
+ case REG:
+ /* If we haven't already been able to do something with this BIV,
+ we can't eliminate it. */
+ if (x == reg)
+ return 0;
+ return 1;
+
+ case SET:
+ /* If this sets the BIV, it is not a problem. */
+ if (SET_DEST (x) == reg)
+ return 1;
+
+ /* If this is an insn that defines a giv, it is also ok because
+ it will go away when the giv is reduced. */
+ for (v = bl->giv; v; v = v->next_iv)
+ if (v->giv_type == DEST_REG && SET_DEST (x) == v->dest_reg)
+ return 1;
+
+#ifdef HAVE_cc0
+ if (SET_DEST (x) == cc0_rtx && SET_SRC (x) == reg)
+ {
+ /* Can replace with any giv that was reduced and
+ that has (MULT_VAL != 0) and (ADD_VAL == 0).
+ Require a constant for MULT_VAL, so we know it's nonzero. */
+
+ for (v = bl->giv; v; v = v->next_iv)
+ if (CONSTANT_P (v->mult_val) && v->mult_val != const0_rtx
+ && v->add_val == const0_rtx
+ && ! v->ignore && ! v->maybe_dead && v->always_computable
+ && v->mode == mode)
+ {
+ if (! eliminate_p)
+ return 1;
+
+ /* If the giv has the opposite direction of change,
+ then reverse the comparison. */
+ if (INTVAL (v->mult_val) < 0)
+ new = gen_rtx (COMPARE, GET_MODE (v->new_reg),
+ const0_rtx, v->new_reg);
+ else
+ new = v->new_reg;
+
+ /* We can probably test that giv's reduced reg. */
+ if (validate_change (insn, &SET_SRC (x), new, 0))
+ return 1;
+ }
+
+ /* Look for a giv with (MULT_VAL != 0) and (ADD_VAL != 0);
+ replace test insn with a compare insn (cmp REDUCED_GIV ADD_VAL).
+ Require a constant for MULT_VAL, so we know it's nonzero. */
+
+ for (v = bl->giv; v; v = v->next_iv)
+ if (CONSTANT_P (v->mult_val) && v->mult_val != const0_rtx
+ && ! v->ignore && ! v->maybe_dead && v->always_computable
+ && v->mode == mode)
+ {
+ if (! eliminate_p)
+ return 1;
+
+ /* If the giv has the opposite direction of change,
+ then reverse the comparison. */
+ if (INTVAL (v->mult_val) < 0)
+ new = gen_rtx (COMPARE, VOIDmode, copy_rtx (v->add_val),
+ v->new_reg);
+ else
+ new = gen_rtx (COMPARE, VOIDmode, v->new_reg,
+ copy_rtx (v->add_val));
+
+ /* Replace biv with the giv's reduced register. */
+ update_reg_last_use (v->add_val, insn);
+ if (validate_change (insn, &SET_SRC (PATTERN (insn)), new, 0))
+ return 1;
+
+ /* Insn doesn't support that constant or invariant. Copy it
+ into a register (it will be a loop invariant.) */
+ tem = gen_reg_rtx (GET_MODE (v->new_reg));
+
+ emit_insn_before (gen_move_insn (tem, copy_rtx (v->add_val)),
+ where);
+
+ if (validate_change (insn, &SET_SRC (PATTERN (insn)),
+ gen_rtx (COMPARE, VOIDmode,
+ v->new_reg, tem), 0))
+ return 1;
+ }
+ }
+#endif
+ break;
+
+ case COMPARE:
+ case EQ: case NE:
+ case GT: case GE: case GTU: case GEU:
+ case LT: case LE: case LTU: case LEU:
+ /* See if either argument is the biv. */
+ if (XEXP (x, 0) == reg)
+ arg = XEXP (x, 1), arg_operand = 1;
+ else if (XEXP (x, 1) == reg)
+ arg = XEXP (x, 0), arg_operand = 0;
+ else
+ break;
+
+ if (CONSTANT_P (arg))
+ {
+ /* First try to replace with any giv that has constant positive
+ mult_val and constant add_val. We might be able to support
+ negative mult_val, but it seems complex to do it in general. */
+
+ for (v = bl->giv; v; v = v->next_iv)
+ if (CONSTANT_P (v->mult_val) && INTVAL (v->mult_val) > 0
+ && CONSTANT_P (v->add_val)
+ && ! v->ignore && ! v->maybe_dead && v->always_computable
+ && v->mode == mode)
+ {
+ if (! eliminate_p)
+ return 1;
+
+ /* Replace biv with the giv's reduced reg. */
+ XEXP (x, 1-arg_operand) = v->new_reg;
+
+ /* If all constants are actually constant integers and
+ the derived constant can be directly placed in the COMPARE,
+ do so. */
+ if (GET_CODE (arg) == CONST_INT
+ && GET_CODE (v->mult_val) == CONST_INT
+ && GET_CODE (v->add_val) == CONST_INT
+ && validate_change (insn, &XEXP (x, arg_operand),
+ GEN_INT (INTVAL (arg)
+ * INTVAL (v->mult_val)
+ + INTVAL (v->add_val)), 0))
+ return 1;
+
+ /* Otherwise, load it into a register. */
+ tem = gen_reg_rtx (mode);
+ emit_iv_add_mult (arg, v->mult_val, v->add_val, tem, where);
+ if (validate_change (insn, &XEXP (x, arg_operand), tem, 0))
+ return 1;
+
+ /* If that failed, put back the change we made above. */
+ XEXP (x, 1-arg_operand) = reg;
+ }
+
+ /* Look for giv with positive constant mult_val and nonconst add_val.
+ Insert insns to calculate new compare value. */
+
+ for (v = bl->giv; v; v = v->next_iv)
+ if (CONSTANT_P (v->mult_val) && INTVAL (v->mult_val) > 0
+ && ! v->ignore && ! v->maybe_dead && v->always_computable
+ && v->mode == mode)
+ {
+ rtx tem;
+
+ if (! eliminate_p)
+ return 1;
+
+ tem = gen_reg_rtx (mode);
+
+ /* Replace biv with giv's reduced register. */
+ validate_change (insn, &XEXP (x, 1 - arg_operand),
+ v->new_reg, 1);
+
+ /* Compute value to compare against. */
+ emit_iv_add_mult (arg, v->mult_val, v->add_val, tem, where);
+ /* Use it in this insn. */
+ validate_change (insn, &XEXP (x, arg_operand), tem, 1);
+ if (apply_change_group ())
+ return 1;
+ }
+ }
+ else if (GET_CODE (arg) == REG || GET_CODE (arg) == MEM)
+ {
+ if (invariant_p (arg) == 1)
+ {
+ /* Look for giv with constant positive mult_val and nonconst
+ add_val. Insert insns to compute new compare value. */
+
+ for (v = bl->giv; v; v = v->next_iv)
+ if (CONSTANT_P (v->mult_val) && INTVAL (v->mult_val) > 0
+ && ! v->ignore && ! v->maybe_dead && v->always_computable
+ && v->mode == mode)
+ {
+ rtx tem;
+
+ if (! eliminate_p)
+ return 1;
+
+ tem = gen_reg_rtx (mode);
+
+ /* Replace biv with giv's reduced register. */
+ validate_change (insn, &XEXP (x, 1 - arg_operand),
+ v->new_reg, 1);
+
+ /* Compute value to compare against. */
+ emit_iv_add_mult (arg, v->mult_val, v->add_val,
+ tem, where);
+ validate_change (insn, &XEXP (x, arg_operand), tem, 1);
+ if (apply_change_group ())
+ return 1;
+ }
+ }
+
+ /* This code has problems. Basically, you can't know when
+ seeing if we will eliminate BL, whether a particular giv
+ of ARG will be reduced. If it isn't going to be reduced,
+ we can't eliminate BL. We can try forcing it to be reduced,
+ but that can generate poor code.
+
+ The problem is that the benefit of reducing TV, below should
+ be increased if BL can actually be eliminated, but this means
+ we might have to do a topological sort of the order in which
+ we try to process biv. It doesn't seem worthwhile to do
+ this sort of thing now. */
+
+#if 0
+ /* Otherwise the reg compared with had better be a biv. */
+ if (GET_CODE (arg) != REG
+ || reg_iv_type[REGNO (arg)] != BASIC_INDUCT)
+ return 0;
+
+ /* Look for a pair of givs, one for each biv,
+ with identical coefficients. */
+ for (v = bl->giv; v; v = v->next_iv)
+ {
+ struct induction *tv;
+
+ if (v->ignore || v->maybe_dead || v->mode != mode)
+ continue;
+
+ for (tv = reg_biv_class[REGNO (arg)]->giv; tv; tv = tv->next_iv)
+ if (! tv->ignore && ! tv->maybe_dead
+ && rtx_equal_p (tv->mult_val, v->mult_val)
+ && rtx_equal_p (tv->add_val, v->add_val)
+ && tv->mode == mode)
+ {
+ if (! eliminate_p)
+ return 1;
+
+ /* Replace biv with its giv's reduced reg. */
+ XEXP (x, 1-arg_operand) = v->new_reg;
+ /* Replace other operand with the other giv's
+ reduced reg. */
+ XEXP (x, arg_operand) = tv->new_reg;
+ return 1;
+ }
+ }
+#endif
+ }
+
+ /* If we get here, the biv can't be eliminated. */
+ return 0;
+
+ case MEM:
+ /* If this address is a DEST_ADDR giv, it doesn't matter if the
+ biv is used in it, since it will be replaced. */
+ for (v = bl->giv; v; v = v->next_iv)
+ if (v->giv_type == DEST_ADDR && v->location == &XEXP (x, 0))
+ return 1;
+ break;
+ }
+
+ /* See if any subexpression fails elimination. */
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ switch (fmt[i])
+ {
+ case 'e':
+ if (! maybe_eliminate_biv_1 (XEXP (x, i), insn, bl,
+ eliminate_p, where))
+ return 0;
+ break;
+
+ case 'E':
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (! maybe_eliminate_biv_1 (XVECEXP (x, i, j), insn, bl,
+ eliminate_p, where))
+ return 0;
+ break;
+ }
+ }
+
+ return 1;
+}
+
+/* Return nonzero if the last use of REG
+ is in an insn following INSN in the same basic block. */
+
+static int
+last_use_this_basic_block (reg, insn)
+ rtx reg;
+ rtx insn;
+{
+ rtx n;
+ for (n = insn;
+ n && GET_CODE (n) != CODE_LABEL && GET_CODE (n) != JUMP_INSN;
+ n = NEXT_INSN (n))
+ {
+ if (regno_last_uid[REGNO (reg)] == INSN_UID (n))
+ return 1;
+ }
+ return 0;
+}
+
+/* Called via `note_stores' to record the initial value of a biv. Here we
+ just record the location of the set and process it later. */
+
+static void
+record_initial (dest, set)
+ rtx dest;
+ rtx set;
+{
+ struct iv_class *bl;
+
+ if (GET_CODE (dest) != REG
+ || REGNO (dest) >= max_reg_before_loop
+ || reg_iv_type[REGNO (dest)] != BASIC_INDUCT)
+ return;
+
+ bl = reg_biv_class[REGNO (dest)];
+
+ /* If this is the first set found, record it. */
+ if (bl->init_insn == 0)
+ {
+ bl->init_insn = note_insn;
+ bl->init_set = set;
+ }
+}
+
+/* If any of the registers in X are "old" and currently have a last use earlier
+ than INSN, update them to have a last use of INSN. Their actual last use
+ will be the previous insn but it will not have a valid uid_luid so we can't
+ use it. */
+
+static void
+update_reg_last_use (x, insn)
+ rtx x;
+ rtx insn;
+{
+ /* Check for the case where INSN does not have a valid luid. In this case,
+ there is no need to modify the regno_last_uid, as this can only happen
+ when code is inserted after the loop_end to set a pseudo's final value,
+ and hence this insn will never be the last use of x. */
+ if (GET_CODE (x) == REG && REGNO (x) < max_reg_before_loop
+ && INSN_UID (insn) < max_uid_for_loop
+ && uid_luid[regno_last_uid[REGNO (x)]] < uid_luid[INSN_UID (insn)])
+ regno_last_uid[REGNO (x)] = INSN_UID (insn);
+ else
+ {
+ register int i, j;
+ register char *fmt = GET_RTX_FORMAT (GET_CODE (x));
+ for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ update_reg_last_use (XEXP (x, i), insn);
+ else if (fmt[i] == 'E')
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ update_reg_last_use (XVECEXP (x, i, j), insn);
+ }
+ }
+}
+
+/* Given a jump insn JUMP, return the condition that will cause it to branch
+ to its JUMP_LABEL. If the condition cannot be understood, or is an
+ inequality floating-point comparison which needs to be reversed, 0 will
+ be returned.
+
+ If EARLIEST is non-zero, it is a pointer to a place where the earliest
+ insn used in locating the condition was found. If a replacement test
+ of the condition is desired, it should be placed in front of that
+ insn and we will be sure that the inputs are still valid.
+
+ The condition will be returned in a canonical form to simplify testing by
+ callers. Specifically:
+
+ (1) The code will always be a comparison operation (EQ, NE, GT, etc.).
+ (2) Both operands will be machine operands; (cc0) will have been replaced.
+ (3) If an operand is a constant, it will be the second operand.
+ (4) (LE x const) will be replaced with (LT x <const+1>) and similarly
+ for GE, GEU, and LEU. */
+
+rtx
+get_condition (jump, earliest)
+ rtx jump;
+ rtx *earliest;
+{
+ enum rtx_code code;
+ rtx prev = jump;
+ rtx set;
+ rtx tem;
+ rtx op0, op1;
+ int reverse_code = 0;
+ int did_reverse_condition = 0;
+
+ /* If this is not a standard conditional jump, we can't parse it. */
+ if (GET_CODE (jump) != JUMP_INSN
+ || ! condjump_p (jump) || simplejump_p (jump))
+ return 0;
+
+ code = GET_CODE (XEXP (SET_SRC (PATTERN (jump)), 0));
+ op0 = XEXP (XEXP (SET_SRC (PATTERN (jump)), 0), 0);
+ op1 = XEXP (XEXP (SET_SRC (PATTERN (jump)), 0), 1);
+
+ if (earliest)
+ *earliest = jump;
+
+ /* If this branches to JUMP_LABEL when the condition is false, reverse
+ the condition. */
+ if (GET_CODE (XEXP (SET_SRC (PATTERN (jump)), 2)) == LABEL_REF
+ && XEXP (XEXP (SET_SRC (PATTERN (jump)), 2), 0) == JUMP_LABEL (jump))
+ code = reverse_condition (code), did_reverse_condition ^= 1;
+
+ /* If we are comparing a register with zero, see if the register is set
+ in the previous insn to a COMPARE or a comparison operation. Perform
+ the same tests as a function of STORE_FLAG_VALUE as find_comparison_args
+ in cse.c */
+
+ while (GET_RTX_CLASS (code) == '<' && op1 == const0_rtx)
+ {
+ /* Set non-zero when we find something of interest. */
+ rtx x = 0;
+
+#ifdef HAVE_cc0
+ /* If comparison with cc0, import actual comparison from compare
+ insn. */
+ if (op0 == cc0_rtx)
+ {
+ if ((prev = prev_nonnote_insn (prev)) == 0
+ || GET_CODE (prev) != INSN
+ || (set = single_set (prev)) == 0
+ || SET_DEST (set) != cc0_rtx)
+ return 0;
+
+ op0 = SET_SRC (set);
+ op1 = CONST0_RTX (GET_MODE (op0));
+ if (earliest)
+ *earliest = prev;
+ }
+#endif
+
+ /* If this is a COMPARE, pick up the two things being compared. */
+ if (GET_CODE (op0) == COMPARE)
+ {
+ op1 = XEXP (op0, 1);
+ op0 = XEXP (op0, 0);
+ continue;
+ }
+ else if (GET_CODE (op0) != REG)
+ break;
+
+ /* Go back to the previous insn. Stop if it is not an INSN. We also
+ stop if it isn't a single set or if it has a REG_INC note because
+ we don't want to bother dealing with it. */
+
+ if ((prev = prev_nonnote_insn (prev)) == 0
+ || GET_CODE (prev) != INSN
+ || FIND_REG_INC_NOTE (prev, 0)
+ || (set = single_set (prev)) == 0)
+ break;
+
+ /* If this is setting OP0, get what it sets it to if it looks
+ relevant. */
+ if (SET_DEST (set) == op0)
+ {
+ enum machine_mode inner_mode = GET_MODE (SET_SRC (set));
+
+ if ((GET_CODE (SET_SRC (set)) == COMPARE
+ || (((code == NE
+ || (code == LT
+ && GET_MODE_CLASS (inner_mode) == MODE_INT
+ && (GET_MODE_BITSIZE (inner_mode)
+ <= HOST_BITS_PER_WIDE_INT)
+ && (STORE_FLAG_VALUE
+ & ((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (inner_mode) - 1))))
+#ifdef FLOAT_STORE_FLAG_VALUE
+ || (code == LT
+ && GET_MODE_CLASS (inner_mode) == MODE_FLOAT
+ && FLOAT_STORE_FLAG_VALUE < 0)
+#endif
+ ))
+ && GET_RTX_CLASS (GET_CODE (SET_SRC (set))) == '<')))
+ x = SET_SRC (set);
+ else if (((code == EQ
+ || (code == GE
+ && (GET_MODE_BITSIZE (inner_mode)
+ <= HOST_BITS_PER_WIDE_INT)
+ && GET_MODE_CLASS (inner_mode) == MODE_INT
+ && (STORE_FLAG_VALUE
+ & ((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (inner_mode) - 1))))
+#ifdef FLOAT_STORE_FLAG_VALUE
+ || (code == GE
+ && GET_MODE_CLASS (inner_mode) == MODE_FLOAT
+ && FLOAT_STORE_FLAG_VALUE < 0)
+#endif
+ ))
+ && GET_RTX_CLASS (GET_CODE (SET_SRC (set))) == '<')
+ {
+ /* We might have reversed a LT to get a GE here. But this wasn't
+ actually the comparison of data, so we don't flag that we
+ have had to reverse the condition. */
+ did_reverse_condition ^= 1;
+ reverse_code = 1;
+ x = SET_SRC (set);
+ }
+ else
+ break;
+ }
+
+ else if (reg_set_p (op0, prev))
+ /* If this sets OP0, but not directly, we have to give up. */
+ break;
+
+ if (x)
+ {
+ if (GET_RTX_CLASS (GET_CODE (x)) == '<')
+ code = GET_CODE (x);
+ if (reverse_code)
+ {
+ code = reverse_condition (code);
+ did_reverse_condition ^= 1;
+ reverse_code = 0;
+ }
+
+ op0 = XEXP (x, 0), op1 = XEXP (x, 1);
+ if (earliest)
+ *earliest = prev;
+ }
+ }
+
+ /* If constant is first, put it last. */
+ if (CONSTANT_P (op0))
+ code = swap_condition (code), tem = op0, op0 = op1, op1 = tem;
+
+ /* If OP0 is the result of a comparison, we weren't able to find what
+ was really being compared, so fail. */
+ if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC)
+ return 0;
+
+ /* Canonicalize any ordered comparison with integers involving equality
+ if we can do computations in the relevant mode and we do not
+ overflow. */
+
+ if (GET_CODE (op1) == CONST_INT
+ && GET_MODE (op0) != VOIDmode
+ && GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT)
+ {
+ HOST_WIDE_INT const_val = INTVAL (op1);
+ unsigned HOST_WIDE_INT uconst_val = const_val;
+ unsigned HOST_WIDE_INT max_val
+ = (unsigned HOST_WIDE_INT) GET_MODE_MASK (GET_MODE (op0));
+
+ switch (code)
+ {
+ case LE:
+ if (const_val != max_val >> 1)
+ code = LT, op1 = GEN_INT (const_val + 1);
+ break;
+
+ case GE:
+ if (const_val
+ != (((HOST_WIDE_INT) 1
+ << (GET_MODE_BITSIZE (GET_MODE (op0)) - 1))))
+ code = GT, op1 = GEN_INT (const_val - 1);
+ break;
+
+ case LEU:
+ if (uconst_val != max_val)
+ code = LTU, op1 = GEN_INT (uconst_val + 1);
+ break;
+
+ case GEU:
+ if (uconst_val != 0)
+ code = GTU, op1 = GEN_INT (uconst_val - 1);
+ break;
+ }
+ }
+
+ /* If this was floating-point and we reversed anything other than an
+ EQ or NE, return zero. */
+ if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
+ && did_reverse_condition && code != NE && code != EQ
+ && ! flag_fast_math
+ && GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT)
+ return 0;
+
+#ifdef HAVE_cc0
+ /* Never return CC0; return zero instead. */
+ if (op0 == cc0_rtx)
+ return 0;
+#endif
+
+ return gen_rtx (code, VOIDmode, op0, op1);
+}
+
+/* Similar to above routine, except that we also put an invariant last
+ unless both operands are invariants. */
+
+rtx
+get_condition_for_loop (x)
+ rtx x;
+{
+ rtx comparison = get_condition (x, NULL_PTR);
+
+ if (comparison == 0
+ || ! invariant_p (XEXP (comparison, 0))
+ || invariant_p (XEXP (comparison, 1)))
+ return comparison;
+
+ return gen_rtx (swap_condition (GET_CODE (comparison)), VOIDmode,
+ XEXP (comparison, 1), XEXP (comparison, 0));
+}
diff --git a/gnu/usr.bin/cc/cc_int/obstack.c b/gnu/usr.bin/cc/cc_int/obstack.c
new file mode 100644
index 0000000..a8a4500
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/obstack.c
@@ -0,0 +1,485 @@
+/* obstack.c - subroutines used implicitly by object stack macros
+ Copyright (C) 1988, 89, 90, 91, 92, 93, 94 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "obstack.h"
+
+/* This is just to get __GNU_LIBRARY__ defined. */
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+#ifdef __STDC__
+#define POINTER void *
+#else
+#define POINTER char *
+#endif
+
+/* Determine default alignment. */
+struct fooalign {char x; double d;};
+#define DEFAULT_ALIGNMENT \
+ ((PTR_INT_TYPE) ((char *)&((struct fooalign *) 0)->d - (char *)0))
+/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT.
+ But in fact it might be less smart and round addresses to as much as
+ DEFAULT_ROUNDING. So we prepare for it to do that. */
+union fooround {long x; double d;};
+#define DEFAULT_ROUNDING (sizeof (union fooround))
+
+/* When we copy a long block of data, this is the unit to do it with.
+ On some machines, copying successive ints does not work;
+ in such a case, redefine COPYING_UNIT to `long' (if that works)
+ or `char' as a last resort. */
+#ifndef COPYING_UNIT
+#define COPYING_UNIT int
+#endif
+
+/* The non-GNU-C macros copy the obstack into this global variable
+ to avoid multiple evaluation. */
+
+struct obstack *_obstack;
+
+/* Define a macro that either calls functions with the traditional malloc/free
+ calling interface, or calls functions with the mmalloc/mfree interface
+ (that adds an extra first argument), based on the state of use_extra_arg.
+ For free, do not use ?:, since some compilers, like the MIPS compilers,
+ do not allow (expr) ? void : void. */
+
+#define CALL_CHUNKFUN(h, size) \
+ (((h) -> use_extra_arg) \
+ ? (*(h)->chunkfun) ((h)->extra_arg, (size)) \
+ : (*(h)->chunkfun) ((size)))
+
+#define CALL_FREEFUN(h, old_chunk) \
+ do { \
+ if ((h) -> use_extra_arg) \
+ (*(h)->freefun) ((h)->extra_arg, (old_chunk)); \
+ else \
+ (*(h)->freefun) ((old_chunk)); \
+ } while (0)
+
+
+/* Initialize an obstack H for use. Specify chunk size SIZE (0 means default).
+ Objects start on multiples of ALIGNMENT (0 means use default).
+ CHUNKFUN is the function to use to allocate chunks,
+ and FREEFUN the function to free them.
+
+ Return nonzero if successful, zero if out of memory.
+ To recover from an out of memory error,
+ free up some memory, then call this again. */
+
+int
+_obstack_begin (h, size, alignment, chunkfun, freefun)
+ struct obstack *h;
+ int size;
+ int alignment;
+ POINTER (*chunkfun) ();
+ void (*freefun) ();
+{
+ register struct _obstack_chunk* chunk; /* points to new chunk */
+
+ if (alignment == 0)
+ alignment = DEFAULT_ALIGNMENT;
+ if (size == 0)
+ /* Default size is what GNU malloc can fit in a 4096-byte block. */
+ {
+ /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
+ Use the values for range checking, because if range checking is off,
+ the extra bytes won't be missed terribly, but if range checking is on
+ and we used a larger request, a whole extra 4096 bytes would be
+ allocated.
+
+ These number are irrelevant to the new GNU malloc. I suspect it is
+ less sensitive to the size of the request. */
+ int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
+ + 4 + DEFAULT_ROUNDING - 1)
+ & ~(DEFAULT_ROUNDING - 1));
+ size = 4096 - extra;
+ }
+
+ h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun;
+ h->freefun = freefun;
+ h->chunk_size = size;
+ h->alignment_mask = alignment - 1;
+ h->use_extra_arg = 0;
+
+ chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
+ if (!chunk)
+ {
+ h->alloc_failed = 1;
+ return 0;
+ }
+ h->alloc_failed = 0;
+ h->next_free = h->object_base = chunk->contents;
+ h->chunk_limit = chunk->limit
+ = (char *) chunk + h->chunk_size;
+ chunk->prev = 0;
+ /* The initial chunk now contains no empty object. */
+ h->maybe_empty_object = 0;
+ return 1;
+}
+
+int
+_obstack_begin_1 (h, size, alignment, chunkfun, freefun, arg)
+ struct obstack *h;
+ int size;
+ int alignment;
+ POINTER (*chunkfun) ();
+ void (*freefun) ();
+ POINTER arg;
+{
+ register struct _obstack_chunk* chunk; /* points to new chunk */
+
+ if (alignment == 0)
+ alignment = DEFAULT_ALIGNMENT;
+ if (size == 0)
+ /* Default size is what GNU malloc can fit in a 4096-byte block. */
+ {
+ /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
+ Use the values for range checking, because if range checking is off,
+ the extra bytes won't be missed terribly, but if range checking is on
+ and we used a larger request, a whole extra 4096 bytes would be
+ allocated.
+
+ These number are irrelevant to the new GNU malloc. I suspect it is
+ less sensitive to the size of the request. */
+ int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
+ + 4 + DEFAULT_ROUNDING - 1)
+ & ~(DEFAULT_ROUNDING - 1));
+ size = 4096 - extra;
+ }
+
+ h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun;
+ h->freefun = freefun;
+ h->chunk_size = size;
+ h->alignment_mask = alignment - 1;
+ h->extra_arg = arg;
+ h->use_extra_arg = 1;
+
+ chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
+ if (!chunk)
+ {
+ h->alloc_failed = 1;
+ return 0;
+ }
+ h->alloc_failed = 0;
+ h->next_free = h->object_base = chunk->contents;
+ h->chunk_limit = chunk->limit
+ = (char *) chunk + h->chunk_size;
+ chunk->prev = 0;
+ /* The initial chunk now contains no empty object. */
+ h->maybe_empty_object = 0;
+ return 1;
+}
+
+/* Allocate a new current chunk for the obstack *H
+ on the assumption that LENGTH bytes need to be added
+ to the current object, or a new object of length LENGTH allocated.
+ Copies any partial object from the end of the old chunk
+ to the beginning of the new one. */
+
+void
+_obstack_newchunk (h, length)
+ struct obstack *h;
+ int length;
+{
+ register struct _obstack_chunk* old_chunk = h->chunk;
+ register struct _obstack_chunk* new_chunk;
+ register long new_size;
+ register int obj_size = h->next_free - h->object_base;
+ register int i;
+ int already;
+
+ /* Compute size for new chunk. */
+ new_size = (obj_size + length) + (obj_size >> 3) + 100;
+ if (new_size < h->chunk_size)
+ new_size = h->chunk_size;
+
+ /* Allocate and initialize the new chunk. */
+ new_chunk = CALL_CHUNKFUN (h, new_size);
+ if (!new_chunk)
+ {
+ h->alloc_failed = 1;
+ return;
+ }
+ h->alloc_failed = 0;
+ h->chunk = new_chunk;
+ new_chunk->prev = old_chunk;
+ new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size;
+
+ /* Move the existing object to the new chunk.
+ Word at a time is fast and is safe if the object
+ is sufficiently aligned. */
+ if (h->alignment_mask + 1 >= DEFAULT_ALIGNMENT)
+ {
+ for (i = obj_size / sizeof (COPYING_UNIT) - 1;
+ i >= 0; i--)
+ ((COPYING_UNIT *)new_chunk->contents)[i]
+ = ((COPYING_UNIT *)h->object_base)[i];
+ /* We used to copy the odd few remaining bytes as one extra COPYING_UNIT,
+ but that can cross a page boundary on a machine
+ which does not do strict alignment for COPYING_UNITS. */
+ already = obj_size / sizeof (COPYING_UNIT) * sizeof (COPYING_UNIT);
+ }
+ else
+ already = 0;
+ /* Copy remaining bytes one by one. */
+ for (i = already; i < obj_size; i++)
+ new_chunk->contents[i] = h->object_base[i];
+
+ /* If the object just copied was the only data in OLD_CHUNK,
+ free that chunk and remove it from the chain.
+ But not if that chunk might contain an empty object. */
+ if (h->object_base == old_chunk->contents && ! h->maybe_empty_object)
+ {
+ new_chunk->prev = old_chunk->prev;
+ CALL_FREEFUN (h, old_chunk);
+ }
+
+ h->object_base = new_chunk->contents;
+ h->next_free = h->object_base + obj_size;
+ /* The new chunk certainly contains no empty object yet. */
+ h->maybe_empty_object = 0;
+}
+
+/* Return nonzero if object OBJ has been allocated from obstack H.
+ This is here for debugging.
+ If you use it in a program, you are probably losing. */
+
+#ifdef __STDC__
+/* Suppress -Wmissing-prototypes warning. We don't want to declare this in
+ obstack.h because it is just for debugging. */
+int _obstack_allocated_p (struct obstack *h, POINTER obj);
+#endif
+
+int
+_obstack_allocated_p (h, obj)
+ struct obstack *h;
+ POINTER obj;
+{
+ register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk* plp; /* point to previous chunk if any */
+
+ lp = (h)->chunk;
+ /* We use >= rather than > since the object cannot be exactly at
+ the beginning of the chunk but might be an empty object exactly
+ at the end of an adjacent chunk. */
+ while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj))
+ {
+ plp = lp->prev;
+ lp = plp;
+ }
+ return lp != 0;
+}
+
+/* Free objects in obstack H, including OBJ and everything allocate
+ more recently than OBJ. If OBJ is zero, free everything in H. */
+
+#undef obstack_free
+
+/* This function has two names with identical definitions.
+ This is the first one, called from non-ANSI code. */
+
+void
+_obstack_free (h, obj)
+ struct obstack *h;
+ POINTER obj;
+{
+ register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk* plp; /* point to previous chunk if any */
+
+ lp = h->chunk;
+ /* We use >= because there cannot be an object at the beginning of a chunk.
+ But there can be an empty object at that address
+ at the end of another chunk. */
+ while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj))
+ {
+ plp = lp->prev;
+ CALL_FREEFUN (h, lp);
+ lp = plp;
+ /* If we switch chunks, we can't tell whether the new current
+ chunk contains an empty object, so assume that it may. */
+ h->maybe_empty_object = 1;
+ }
+ if (lp)
+ {
+ h->object_base = h->next_free = (char *)(obj);
+ h->chunk_limit = lp->limit;
+ h->chunk = lp;
+ }
+ else if (obj != 0)
+ /* obj is not in any of the chunks! */
+ abort ();
+}
+
+/* This function is used from ANSI code. */
+
+void
+obstack_free (h, obj)
+ struct obstack *h;
+ POINTER obj;
+{
+ register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk* plp; /* point to previous chunk if any */
+
+ lp = h->chunk;
+ /* We use >= because there cannot be an object at the beginning of a chunk.
+ But there can be an empty object at that address
+ at the end of another chunk. */
+ while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj))
+ {
+ plp = lp->prev;
+ CALL_FREEFUN (h, lp);
+ lp = plp;
+ /* If we switch chunks, we can't tell whether the new current
+ chunk contains an empty object, so assume that it may. */
+ h->maybe_empty_object = 1;
+ }
+ if (lp)
+ {
+ h->object_base = h->next_free = (char *)(obj);
+ h->chunk_limit = lp->limit;
+ h->chunk = lp;
+ }
+ else if (obj != 0)
+ /* obj is not in any of the chunks! */
+ abort ();
+}
+
+#if 0
+/* These are now turned off because the applications do not use it
+ and it uses bcopy via obstack_grow, which causes trouble on sysV. */
+
+/* Now define the functional versions of the obstack macros.
+ Define them to simply use the corresponding macros to do the job. */
+
+#ifdef __STDC__
+/* These function definitions do not work with non-ANSI preprocessors;
+ they won't pass through the macro names in parentheses. */
+
+/* The function names appear in parentheses in order to prevent
+ the macro-definitions of the names from being expanded there. */
+
+POINTER (obstack_base) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_base (obstack);
+}
+
+POINTER (obstack_next_free) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_next_free (obstack);
+}
+
+int (obstack_object_size) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_object_size (obstack);
+}
+
+int (obstack_room) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_room (obstack);
+}
+
+void (obstack_grow) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ obstack_grow (obstack, pointer, length);
+}
+
+void (obstack_grow0) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ obstack_grow0 (obstack, pointer, length);
+}
+
+void (obstack_1grow) (obstack, character)
+ struct obstack *obstack;
+ int character;
+{
+ obstack_1grow (obstack, character);
+}
+
+void (obstack_blank) (obstack, length)
+ struct obstack *obstack;
+ int length;
+{
+ obstack_blank (obstack, length);
+}
+
+void (obstack_1grow_fast) (obstack, character)
+ struct obstack *obstack;
+ int character;
+{
+ obstack_1grow_fast (obstack, character);
+}
+
+void (obstack_blank_fast) (obstack, length)
+ struct obstack *obstack;
+ int length;
+{
+ obstack_blank_fast (obstack, length);
+}
+
+POINTER (obstack_finish) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_finish (obstack);
+}
+
+POINTER (obstack_alloc) (obstack, length)
+ struct obstack *obstack;
+ int length;
+{
+ return obstack_alloc (obstack, length);
+}
+
+POINTER (obstack_copy) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ return obstack_copy (obstack, pointer, length);
+}
+
+POINTER (obstack_copy0) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ return obstack_copy0 (obstack, pointer, length);
+}
+
+#endif /* __STDC__ */
+
+#endif /* 0 */
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
diff --git a/gnu/usr.bin/cc/cc_int/optabs.c b/gnu/usr.bin/cc/cc_int/optabs.c
new file mode 100644
index 0000000..427ccf4
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/optabs.c
@@ -0,0 +1,4100 @@
+/* Expand the basic unary and binary arithmetic operations, for GNU compiler.
+ Copyright (C) 1987, 1988, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#include "rtl.h"
+#include "tree.h"
+#include "flags.h"
+#include "insn-flags.h"
+#include "insn-codes.h"
+#include "expr.h"
+#include "insn-config.h"
+#include "recog.h"
+#include "reload.h"
+#include <ctype.h>
+
+/* Each optab contains info on how this target machine
+ can perform a particular operation
+ for all sizes and kinds of operands.
+
+ The operation to be performed is often specified
+ by passing one of these optabs as an argument.
+
+ See expr.h for documentation of these optabs. */
+
+optab add_optab;
+optab sub_optab;
+optab smul_optab;
+optab smul_highpart_optab;
+optab umul_highpart_optab;
+optab smul_widen_optab;
+optab umul_widen_optab;
+optab sdiv_optab;
+optab sdivmod_optab;
+optab udiv_optab;
+optab udivmod_optab;
+optab smod_optab;
+optab umod_optab;
+optab flodiv_optab;
+optab ftrunc_optab;
+optab and_optab;
+optab ior_optab;
+optab xor_optab;
+optab ashl_optab;
+optab lshr_optab;
+optab ashr_optab;
+optab rotl_optab;
+optab rotr_optab;
+optab smin_optab;
+optab smax_optab;
+optab umin_optab;
+optab umax_optab;
+
+optab mov_optab;
+optab movstrict_optab;
+
+optab neg_optab;
+optab abs_optab;
+optab one_cmpl_optab;
+optab ffs_optab;
+optab sqrt_optab;
+optab sin_optab;
+optab cos_optab;
+
+optab cmp_optab;
+optab ucmp_optab; /* Used only for libcalls for unsigned comparisons. */
+optab tst_optab;
+
+optab strlen_optab;
+
+/* Tables of patterns for extending one integer mode to another. */
+enum insn_code extendtab[MAX_MACHINE_MODE][MAX_MACHINE_MODE][2];
+
+/* Tables of patterns for converting between fixed and floating point. */
+enum insn_code fixtab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
+enum insn_code fixtrunctab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
+enum insn_code floattab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
+
+/* Contains the optab used for each rtx code. */
+optab code_to_optab[NUM_RTX_CODE + 1];
+
+/* SYMBOL_REF rtx's for the library functions that are called
+ implicitly and not via optabs. */
+
+rtx extendsfdf2_libfunc;
+rtx extendsfxf2_libfunc;
+rtx extendsftf2_libfunc;
+rtx extenddfxf2_libfunc;
+rtx extenddftf2_libfunc;
+
+rtx truncdfsf2_libfunc;
+rtx truncxfsf2_libfunc;
+rtx trunctfsf2_libfunc;
+rtx truncxfdf2_libfunc;
+rtx trunctfdf2_libfunc;
+
+rtx memcpy_libfunc;
+rtx bcopy_libfunc;
+rtx memcmp_libfunc;
+rtx bcmp_libfunc;
+rtx memset_libfunc;
+rtx bzero_libfunc;
+
+rtx eqsf2_libfunc;
+rtx nesf2_libfunc;
+rtx gtsf2_libfunc;
+rtx gesf2_libfunc;
+rtx ltsf2_libfunc;
+rtx lesf2_libfunc;
+
+rtx eqdf2_libfunc;
+rtx nedf2_libfunc;
+rtx gtdf2_libfunc;
+rtx gedf2_libfunc;
+rtx ltdf2_libfunc;
+rtx ledf2_libfunc;
+
+rtx eqxf2_libfunc;
+rtx nexf2_libfunc;
+rtx gtxf2_libfunc;
+rtx gexf2_libfunc;
+rtx ltxf2_libfunc;
+rtx lexf2_libfunc;
+
+rtx eqtf2_libfunc;
+rtx netf2_libfunc;
+rtx gttf2_libfunc;
+rtx getf2_libfunc;
+rtx lttf2_libfunc;
+rtx letf2_libfunc;
+
+rtx floatsisf_libfunc;
+rtx floatdisf_libfunc;
+rtx floattisf_libfunc;
+
+rtx floatsidf_libfunc;
+rtx floatdidf_libfunc;
+rtx floattidf_libfunc;
+
+rtx floatsixf_libfunc;
+rtx floatdixf_libfunc;
+rtx floattixf_libfunc;
+
+rtx floatsitf_libfunc;
+rtx floatditf_libfunc;
+rtx floattitf_libfunc;
+
+rtx fixsfsi_libfunc;
+rtx fixsfdi_libfunc;
+rtx fixsfti_libfunc;
+
+rtx fixdfsi_libfunc;
+rtx fixdfdi_libfunc;
+rtx fixdfti_libfunc;
+
+rtx fixxfsi_libfunc;
+rtx fixxfdi_libfunc;
+rtx fixxfti_libfunc;
+
+rtx fixtfsi_libfunc;
+rtx fixtfdi_libfunc;
+rtx fixtfti_libfunc;
+
+rtx fixunssfsi_libfunc;
+rtx fixunssfdi_libfunc;
+rtx fixunssfti_libfunc;
+
+rtx fixunsdfsi_libfunc;
+rtx fixunsdfdi_libfunc;
+rtx fixunsdfti_libfunc;
+
+rtx fixunsxfsi_libfunc;
+rtx fixunsxfdi_libfunc;
+rtx fixunsxfti_libfunc;
+
+rtx fixunstfsi_libfunc;
+rtx fixunstfdi_libfunc;
+rtx fixunstfti_libfunc;
+
+/* Indexed by the rtx-code for a conditional (eg. EQ, LT,...)
+ gives the gen_function to make a branch to test that condition. */
+
+rtxfun bcc_gen_fctn[NUM_RTX_CODE];
+
+/* Indexed by the rtx-code for a conditional (eg. EQ, LT,...)
+ gives the insn code to make a store-condition insn
+ to test that condition. */
+
+enum insn_code setcc_gen_code[NUM_RTX_CODE];
+
+static int add_equal_note PROTO((rtx, rtx, enum rtx_code, rtx, rtx));
+static rtx widen_operand PROTO((rtx, enum machine_mode,
+ enum machine_mode, int, int));
+static enum insn_code can_fix_p PROTO((enum machine_mode, enum machine_mode,
+ int, int *));
+static enum insn_code can_float_p PROTO((enum machine_mode, enum machine_mode,
+ int));
+static rtx ftruncify PROTO((rtx));
+static optab init_optab PROTO((enum rtx_code));
+static void init_libfuncs PROTO((optab, int, int, char *, int));
+static void init_integral_libfuncs PROTO((optab, char *, int));
+static void init_floating_libfuncs PROTO((optab, char *, int));
+static void init_complex_libfuncs PROTO((optab, char *, int));
+
+/* Add a REG_EQUAL note to the last insn in SEQ. TARGET is being set to
+ the result of operation CODE applied to OP0 (and OP1 if it is a binary
+ operation).
+
+ If the last insn does not set TARGET, don't do anything, but return 1.
+
+ If a previous insn sets TARGET and TARGET is one of OP0 or OP1,
+ don't add the REG_EQUAL note but return 0. Our caller can then try
+ again, ensuring that TARGET is not one of the operands. */
+
+static int
+add_equal_note (seq, target, code, op0, op1)
+ rtx seq;
+ rtx target;
+ enum rtx_code code;
+ rtx op0, op1;
+{
+ rtx set;
+ int i;
+ rtx note;
+
+ if ((GET_RTX_CLASS (code) != '1' && GET_RTX_CLASS (code) != '2'
+ && GET_RTX_CLASS (code) != 'c' && GET_RTX_CLASS (code) != '<')
+ || GET_CODE (seq) != SEQUENCE
+ || (set = single_set (XVECEXP (seq, 0, XVECLEN (seq, 0) - 1))) == 0
+ || GET_CODE (target) == ZERO_EXTRACT
+ || (! rtx_equal_p (SET_DEST (set), target)
+ /* For a STRICT_LOW_PART, the REG_NOTE applies to what is inside the
+ SUBREG. */
+ && (GET_CODE (SET_DEST (set)) != STRICT_LOW_PART
+ || ! rtx_equal_p (SUBREG_REG (XEXP (SET_DEST (set), 0)),
+ target))))
+ return 1;
+
+ /* If TARGET is in OP0 or OP1, check if anything in SEQ sets TARGET
+ besides the last insn. */
+ if (reg_overlap_mentioned_p (target, op0)
+ || (op1 && reg_overlap_mentioned_p (target, op1)))
+ for (i = XVECLEN (seq, 0) - 2; i >= 0; i--)
+ if (reg_set_p (target, XVECEXP (seq, 0, i)))
+ return 0;
+
+ if (GET_RTX_CLASS (code) == '1')
+ note = gen_rtx (code, GET_MODE (target), copy_rtx (op0));
+ else
+ note = gen_rtx (code, GET_MODE (target), copy_rtx (op0), copy_rtx (op1));
+
+ REG_NOTES (XVECEXP (seq, 0, XVECLEN (seq, 0) - 1))
+ = gen_rtx (EXPR_LIST, REG_EQUAL, note,
+ REG_NOTES (XVECEXP (seq, 0, XVECLEN (seq, 0) - 1)));
+
+ return 1;
+}
+
+/* Widen OP to MODE and return the rtx for the widened operand. UNSIGNEDP
+ says whether OP is signed or unsigned. NO_EXTEND is nonzero if we need
+ not actually do a sign-extend or zero-extend, but can leave the
+ higher-order bits of the result rtx undefined, for example, in the case
+ of logical operations, but not right shifts. */
+
+static rtx
+widen_operand (op, mode, oldmode, unsignedp, no_extend)
+ rtx op;
+ enum machine_mode mode, oldmode;
+ int unsignedp;
+ int no_extend;
+{
+ rtx result;
+
+ /* If we must extend do so. If OP is either a constant or a SUBREG
+ for a promoted object, also extend since it will be more efficient to
+ do so. */
+ if (! no_extend
+ || GET_MODE (op) == VOIDmode
+ || (GET_CODE (op) == SUBREG && SUBREG_PROMOTED_VAR_P (op)))
+ return convert_modes (mode, oldmode, op, unsignedp);
+
+ /* If MODE is no wider than a single word, we return a paradoxical
+ SUBREG. */
+ if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD)
+ return gen_rtx (SUBREG, mode, force_reg (GET_MODE (op), op), 0);
+
+ /* Otherwise, get an object of MODE, clobber it, and set the low-order
+ part to OP. */
+
+ result = gen_reg_rtx (mode);
+ emit_insn (gen_rtx (CLOBBER, VOIDmode, result));
+ emit_move_insn (gen_lowpart (GET_MODE (op), result), op);
+ return result;
+}
+
+/* Generate code to perform an operation specified by BINOPTAB
+ on operands OP0 and OP1, with result having machine-mode MODE.
+
+ UNSIGNEDP is for the case where we have to widen the operands
+ to perform the operation. It says to use zero-extension.
+
+ If TARGET is nonzero, the value
+ is generated there, if it is convenient to do so.
+ In all cases an rtx is returned for the locus of the value;
+ this may or may not be TARGET. */
+
+rtx
+expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
+ enum machine_mode mode;
+ optab binoptab;
+ rtx op0, op1;
+ rtx target;
+ int unsignedp;
+ enum optab_methods methods;
+{
+ enum optab_methods next_methods
+ = (methods == OPTAB_LIB || methods == OPTAB_LIB_WIDEN
+ ? OPTAB_WIDEN : methods);
+ enum mode_class class;
+ enum machine_mode wider_mode;
+ register rtx temp;
+ int commutative_op = 0;
+ int shift_op = (binoptab->code == ASHIFT
+ || binoptab->code == ASHIFTRT
+ || binoptab->code == LSHIFTRT
+ || binoptab->code == ROTATE
+ || binoptab->code == ROTATERT);
+ rtx entry_last = get_last_insn ();
+ rtx last;
+
+ class = GET_MODE_CLASS (mode);
+
+ op0 = protect_from_queue (op0, 0);
+ op1 = protect_from_queue (op1, 0);
+ if (target)
+ target = protect_from_queue (target, 1);
+
+ if (flag_force_mem)
+ {
+ op0 = force_not_mem (op0);
+ op1 = force_not_mem (op1);
+ }
+
+ /* If subtracting an integer constant, convert this into an addition of
+ the negated constant. */
+
+ if (binoptab == sub_optab && GET_CODE (op1) == CONST_INT)
+ {
+ op1 = negate_rtx (mode, op1);
+ binoptab = add_optab;
+ }
+
+ /* If we are inside an appropriately-short loop and one operand is an
+ expensive constant, force it into a register. */
+ if (CONSTANT_P (op0) && preserve_subexpressions_p ()
+ && rtx_cost (op0, binoptab->code) > 2)
+ op0 = force_reg (mode, op0);
+
+ if (CONSTANT_P (op1) && preserve_subexpressions_p ()
+ && rtx_cost (op1, binoptab->code) > 2)
+ op1 = force_reg (shift_op ? word_mode : mode, op1);
+
+ /* Record where to delete back to if we backtrack. */
+ last = get_last_insn ();
+
+ /* If operation is commutative,
+ try to make the first operand a register.
+ Even better, try to make it the same as the target.
+ Also try to make the last operand a constant. */
+ if (GET_RTX_CLASS (binoptab->code) == 'c'
+ || binoptab == smul_widen_optab
+ || binoptab == umul_widen_optab
+ || binoptab == smul_highpart_optab
+ || binoptab == umul_highpart_optab)
+ {
+ commutative_op = 1;
+
+ if (((target == 0 || GET_CODE (target) == REG)
+ ? ((GET_CODE (op1) == REG
+ && GET_CODE (op0) != REG)
+ || target == op1)
+ : rtx_equal_p (op1, target))
+ || GET_CODE (op0) == CONST_INT)
+ {
+ temp = op1;
+ op1 = op0;
+ op0 = temp;
+ }
+ }
+
+ /* If we can do it with a three-operand insn, do so. */
+
+ if (methods != OPTAB_MUST_WIDEN
+ && binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+ {
+ int icode = (int) binoptab->handlers[(int) mode].insn_code;
+ enum machine_mode mode0 = insn_operand_mode[icode][1];
+ enum machine_mode mode1 = insn_operand_mode[icode][2];
+ rtx pat;
+ rtx xop0 = op0, xop1 = op1;
+
+ if (target)
+ temp = target;
+ else
+ temp = gen_reg_rtx (mode);
+
+ /* If it is a commutative operator and the modes would match
+ if we would swap the operands, we can save the conversions. */
+ if (commutative_op)
+ {
+ if (GET_MODE (op0) != mode0 && GET_MODE (op1) != mode1
+ && GET_MODE (op0) == mode1 && GET_MODE (op1) == mode0)
+ {
+ register rtx tmp;
+
+ tmp = op0; op0 = op1; op1 = tmp;
+ tmp = xop0; xop0 = xop1; xop1 = tmp;
+ }
+ }
+
+ /* In case the insn wants input operands in modes different from
+ the result, convert the operands. */
+
+ if (GET_MODE (op0) != VOIDmode
+ && GET_MODE (op0) != mode0)
+ xop0 = convert_to_mode (mode0, xop0, unsignedp);
+
+ if (GET_MODE (xop1) != VOIDmode
+ && GET_MODE (xop1) != mode1)
+ xop1 = convert_to_mode (mode1, xop1, unsignedp);
+
+ /* Now, if insn's predicates don't allow our operands, put them into
+ pseudo regs. */
+
+ if (! (*insn_operand_predicate[icode][1]) (xop0, mode0))
+ xop0 = copy_to_mode_reg (mode0, xop0);
+
+ if (! (*insn_operand_predicate[icode][2]) (xop1, mode1))
+ xop1 = copy_to_mode_reg (mode1, xop1);
+
+ if (! (*insn_operand_predicate[icode][0]) (temp, mode))
+ temp = gen_reg_rtx (mode);
+
+ pat = GEN_FCN (icode) (temp, xop0, xop1);
+ if (pat)
+ {
+ /* If PAT is a multi-insn sequence, try to add an appropriate
+ REG_EQUAL note to it. If we can't because TEMP conflicts with an
+ operand, call ourselves again, this time without a target. */
+ if (GET_CODE (pat) == SEQUENCE
+ && ! add_equal_note (pat, temp, binoptab->code, xop0, xop1))
+ {
+ delete_insns_since (last);
+ return expand_binop (mode, binoptab, op0, op1, NULL_RTX,
+ unsignedp, methods);
+ }
+
+ emit_insn (pat);
+ return temp;
+ }
+ else
+ delete_insns_since (last);
+ }
+
+ /* If this is a multiply, see if we can do a widening operation that
+ takes operands of this mode and makes a wider mode. */
+
+ if (binoptab == smul_optab && GET_MODE_WIDER_MODE (mode) != VOIDmode
+ && (((unsignedp ? umul_widen_optab : smul_widen_optab)
+ ->handlers[(int) GET_MODE_WIDER_MODE (mode)].insn_code)
+ != CODE_FOR_nothing))
+ {
+ temp = expand_binop (GET_MODE_WIDER_MODE (mode),
+ unsignedp ? umul_widen_optab : smul_widen_optab,
+ op0, op1, NULL_RTX, unsignedp, OPTAB_DIRECT);
+
+ if (temp != 0)
+ {
+ if (GET_MODE_CLASS (mode) == MODE_INT)
+ return gen_lowpart (mode, temp);
+ else
+ return convert_to_mode (mode, temp, unsignedp);
+ }
+ }
+
+ /* Look for a wider mode of the same class for which we think we
+ can open-code the operation. Check for a widening multiply at the
+ wider mode as well. */
+
+ if ((class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+ && methods != OPTAB_DIRECT && methods != OPTAB_LIB)
+ for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+ wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+ {
+ if (binoptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing
+ || (binoptab == smul_optab
+ && GET_MODE_WIDER_MODE (wider_mode) != VOIDmode
+ && (((unsignedp ? umul_widen_optab : smul_widen_optab)
+ ->handlers[(int) GET_MODE_WIDER_MODE (wider_mode)].insn_code)
+ != CODE_FOR_nothing)))
+ {
+ rtx xop0 = op0, xop1 = op1;
+ int no_extend = 0;
+
+ /* For certain integer operations, we need not actually extend
+ the narrow operands, as long as we will truncate
+ the results to the same narrowness. */
+
+ if ((binoptab == ior_optab || binoptab == and_optab
+ || binoptab == xor_optab
+ || binoptab == add_optab || binoptab == sub_optab
+ || binoptab == smul_optab || binoptab == ashl_optab)
+ && class == MODE_INT)
+ no_extend = 1;
+
+ xop0 = widen_operand (xop0, wider_mode, mode, unsignedp, no_extend);
+
+ /* The second operand of a shift must always be extended. */
+ xop1 = widen_operand (xop1, wider_mode, mode, unsignedp,
+ no_extend && binoptab != ashl_optab);
+
+ temp = expand_binop (wider_mode, binoptab, xop0, xop1, NULL_RTX,
+ unsignedp, OPTAB_DIRECT);
+ if (temp)
+ {
+ if (class != MODE_INT)
+ {
+ if (target == 0)
+ target = gen_reg_rtx (mode);
+ convert_move (target, temp, 0);
+ return target;
+ }
+ else
+ return gen_lowpart (mode, temp);
+ }
+ else
+ delete_insns_since (last);
+ }
+ }
+
+ /* These can be done a word at a time. */
+ if ((binoptab == and_optab || binoptab == ior_optab || binoptab == xor_optab)
+ && class == MODE_INT
+ && GET_MODE_SIZE (mode) > UNITS_PER_WORD
+ && binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing)
+ {
+ int i;
+ rtx insns;
+ rtx equiv_value;
+
+ /* If TARGET is the same as one of the operands, the REG_EQUAL note
+ won't be accurate, so use a new target. */
+ if (target == 0 || target == op0 || target == op1)
+ target = gen_reg_rtx (mode);
+
+ start_sequence ();
+
+ /* Do the actual arithmetic. */
+ for (i = 0; i < GET_MODE_BITSIZE (mode) / BITS_PER_WORD; i++)
+ {
+ rtx target_piece = operand_subword (target, i, 1, mode);
+ rtx x = expand_binop (word_mode, binoptab,
+ operand_subword_force (op0, i, mode),
+ operand_subword_force (op1, i, mode),
+ target_piece, unsignedp, next_methods);
+
+ if (x == 0)
+ break;
+
+ if (target_piece != x)
+ emit_move_insn (target_piece, x);
+ }
+
+ insns = get_insns ();
+ end_sequence ();
+
+ if (i == GET_MODE_BITSIZE (mode) / BITS_PER_WORD)
+ {
+ if (binoptab->code != UNKNOWN)
+ equiv_value
+ = gen_rtx (binoptab->code, mode, copy_rtx (op0), copy_rtx (op1));
+ else
+ equiv_value = 0;
+
+ emit_no_conflict_block (insns, target, op0, op1, equiv_value);
+ return target;
+ }
+ }
+
+ /* Synthesize double word shifts from single word shifts. */
+ if ((binoptab == lshr_optab || binoptab == ashl_optab
+ || binoptab == ashr_optab)
+ && class == MODE_INT
+ && GET_CODE (op1) == CONST_INT
+ && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
+ && binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
+ && ashl_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
+ && lshr_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing)
+ {
+ rtx insns, inter, equiv_value;
+ rtx into_target, outof_target;
+ rtx into_input, outof_input;
+ int shift_count, left_shift, outof_word;
+
+ /* If TARGET is the same as one of the operands, the REG_EQUAL note
+ won't be accurate, so use a new target. */
+ if (target == 0 || target == op0 || target == op1)
+ target = gen_reg_rtx (mode);
+
+ start_sequence ();
+
+ shift_count = INTVAL (op1);
+
+ /* OUTOF_* is the word we are shifting bits away from, and
+ INTO_* is the word that we are shifting bits towards, thus
+ they differ depending on the direction of the shift and
+ WORDS_BIG_ENDIAN. */
+
+ left_shift = binoptab == ashl_optab;
+ outof_word = left_shift ^ ! WORDS_BIG_ENDIAN;
+
+ outof_target = operand_subword (target, outof_word, 1, mode);
+ into_target = operand_subword (target, 1 - outof_word, 1, mode);
+
+ outof_input = operand_subword_force (op0, outof_word, mode);
+ into_input = operand_subword_force (op0, 1 - outof_word, mode);
+
+ if (shift_count >= BITS_PER_WORD)
+ {
+ inter = expand_binop (word_mode, binoptab,
+ outof_input,
+ GEN_INT (shift_count - BITS_PER_WORD),
+ into_target, unsignedp, next_methods);
+
+ if (inter != 0 && inter != into_target)
+ emit_move_insn (into_target, inter);
+
+ /* For a signed right shift, we must fill the word we are shifting
+ out of with copies of the sign bit. Otherwise it is zeroed. */
+ if (inter != 0 && binoptab != ashr_optab)
+ inter = CONST0_RTX (word_mode);
+ else if (inter != 0)
+ inter = expand_binop (word_mode, binoptab,
+ outof_input,
+ GEN_INT (BITS_PER_WORD - 1),
+ outof_target, unsignedp, next_methods);
+
+ if (inter != 0 && inter != outof_target)
+ emit_move_insn (outof_target, inter);
+ }
+ else
+ {
+ rtx carries;
+ optab reverse_unsigned_shift, unsigned_shift;
+
+ /* For a shift of less then BITS_PER_WORD, to compute the carry,
+ we must do a logical shift in the opposite direction of the
+ desired shift. */
+
+ reverse_unsigned_shift = (left_shift ? lshr_optab : ashl_optab);
+
+ /* For a shift of less than BITS_PER_WORD, to compute the word
+ shifted towards, we need to unsigned shift the orig value of
+ that word. */
+
+ unsigned_shift = (left_shift ? ashl_optab : lshr_optab);
+
+ carries = expand_binop (word_mode, reverse_unsigned_shift,
+ outof_input,
+ GEN_INT (BITS_PER_WORD - shift_count),
+ 0, unsignedp, next_methods);
+
+ if (carries == 0)
+ inter = 0;
+ else
+ inter = expand_binop (word_mode, binoptab, outof_input,
+ op1, outof_target, unsignedp, next_methods);
+
+ if (inter != 0 && inter != outof_target)
+ emit_move_insn (outof_target, inter);
+
+ if (inter != 0)
+ inter = expand_binop (word_mode, unsigned_shift, into_input,
+ op1, 0, unsignedp, next_methods);
+
+ if (inter != 0)
+ inter = expand_binop (word_mode, ior_optab, carries, inter,
+ into_target, unsignedp, next_methods);
+
+ if (inter != 0 && inter != into_target)
+ emit_move_insn (into_target, inter);
+ }
+
+ insns = get_insns ();
+ end_sequence ();
+
+ if (inter != 0)
+ {
+ if (binoptab->code != UNKNOWN)
+ equiv_value = gen_rtx (binoptab->code, mode, op0, op1);
+ else
+ equiv_value = 0;
+
+ emit_no_conflict_block (insns, target, op0, op1, equiv_value);
+ return target;
+ }
+ }
+
+ /* Synthesize double word rotates from single word shifts. */
+ if ((binoptab == rotl_optab || binoptab == rotr_optab)
+ && class == MODE_INT
+ && GET_CODE (op1) == CONST_INT
+ && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
+ && ashl_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
+ && lshr_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing)
+ {
+ rtx insns, equiv_value;
+ rtx into_target, outof_target;
+ rtx into_input, outof_input;
+ rtx inter;
+ int shift_count, left_shift, outof_word;
+
+ /* If TARGET is the same as one of the operands, the REG_EQUAL note
+ won't be accurate, so use a new target. */
+ if (target == 0 || target == op0 || target == op1)
+ target = gen_reg_rtx (mode);
+
+ start_sequence ();
+
+ shift_count = INTVAL (op1);
+
+ /* OUTOF_* is the word we are shifting bits away from, and
+ INTO_* is the word that we are shifting bits towards, thus
+ they differ depending on the direction of the shift and
+ WORDS_BIG_ENDIAN. */
+
+ left_shift = (binoptab == rotl_optab);
+ outof_word = left_shift ^ ! WORDS_BIG_ENDIAN;
+
+ outof_target = operand_subword (target, outof_word, 1, mode);
+ into_target = operand_subword (target, 1 - outof_word, 1, mode);
+
+ outof_input = operand_subword_force (op0, outof_word, mode);
+ into_input = operand_subword_force (op0, 1 - outof_word, mode);
+
+ if (shift_count == BITS_PER_WORD)
+ {
+ /* This is just a word swap. */
+ emit_move_insn (outof_target, into_input);
+ emit_move_insn (into_target, outof_input);
+ inter = const0_rtx;
+ }
+ else
+ {
+ rtx into_temp1, into_temp2, outof_temp1, outof_temp2;
+ rtx first_shift_count, second_shift_count;
+ optab reverse_unsigned_shift, unsigned_shift;
+
+ reverse_unsigned_shift = (left_shift ^ (shift_count < BITS_PER_WORD)
+ ? lshr_optab : ashl_optab);
+
+ unsigned_shift = (left_shift ^ (shift_count < BITS_PER_WORD)
+ ? ashl_optab : lshr_optab);
+
+ if (shift_count > BITS_PER_WORD)
+ {
+ first_shift_count = GEN_INT (shift_count - BITS_PER_WORD);
+ second_shift_count = GEN_INT (2*BITS_PER_WORD - shift_count);
+ }
+ else
+ {
+ first_shift_count = GEN_INT (BITS_PER_WORD - shift_count);
+ second_shift_count = GEN_INT (shift_count);
+ }
+
+ into_temp1 = expand_binop (word_mode, unsigned_shift,
+ outof_input, first_shift_count,
+ NULL_RTX, unsignedp, next_methods);
+ into_temp2 = expand_binop (word_mode, reverse_unsigned_shift,
+ into_input, second_shift_count,
+ into_target, unsignedp, next_methods);
+
+ if (into_temp1 != 0 && into_temp2 != 0)
+ inter = expand_binop (word_mode, ior_optab, into_temp1, into_temp2,
+ into_target, unsignedp, next_methods);
+ else
+ inter = 0;
+
+ if (inter != 0 && inter != into_target)
+ emit_move_insn (into_target, inter);
+
+ outof_temp1 = expand_binop (word_mode, unsigned_shift,
+ into_input, first_shift_count,
+ NULL_RTX, unsignedp, next_methods);
+ outof_temp2 = expand_binop (word_mode, reverse_unsigned_shift,
+ outof_input, second_shift_count,
+ outof_target, unsignedp, next_methods);
+
+ if (inter != 0 && outof_temp1 != 0 && outof_temp2 != 0)
+ inter = expand_binop (word_mode, ior_optab,
+ outof_temp1, outof_temp2,
+ outof_target, unsignedp, next_methods);
+
+ if (inter != 0 && inter != outof_target)
+ emit_move_insn (outof_target, inter);
+ }
+
+ insns = get_insns ();
+ end_sequence ();
+
+ if (inter != 0)
+ {
+ if (binoptab->code != UNKNOWN)
+ equiv_value = gen_rtx (binoptab->code, mode, op0, op1);
+ else
+ equiv_value = 0;
+
+ /* We can't make this a no conflict block if this is a word swap,
+ because the word swap case fails if the input and output values
+ are in the same register. */
+ if (shift_count != BITS_PER_WORD)
+ emit_no_conflict_block (insns, target, op0, op1, equiv_value);
+ else
+ emit_insns (insns);
+
+
+ return target;
+ }
+ }
+
+ /* These can be done a word at a time by propagating carries. */
+ if ((binoptab == add_optab || binoptab == sub_optab)
+ && class == MODE_INT
+ && GET_MODE_SIZE (mode) >= 2 * UNITS_PER_WORD
+ && binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing)
+ {
+ int i;
+ rtx carry_tmp = gen_reg_rtx (word_mode);
+ optab otheroptab = binoptab == add_optab ? sub_optab : add_optab;
+ int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
+ rtx carry_in, carry_out;
+ rtx xop0, xop1;
+
+ /* We can handle either a 1 or -1 value for the carry. If STORE_FLAG
+ value is one of those, use it. Otherwise, use 1 since it is the
+ one easiest to get. */
+#if STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1
+ int normalizep = STORE_FLAG_VALUE;
+#else
+ int normalizep = 1;
+#endif
+
+ /* Prepare the operands. */
+ xop0 = force_reg (mode, op0);
+ xop1 = force_reg (mode, op1);
+
+ if (target == 0 || GET_CODE (target) != REG
+ || target == xop0 || target == xop1)
+ target = gen_reg_rtx (mode);
+
+ /* Indicate for flow that the entire target reg is being set. */
+ if (GET_CODE (target) == REG)
+ emit_insn (gen_rtx (CLOBBER, VOIDmode, target));
+
+ /* Do the actual arithmetic. */
+ for (i = 0; i < nwords; i++)
+ {
+ int index = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i);
+ rtx target_piece = operand_subword (target, index, 1, mode);
+ rtx op0_piece = operand_subword_force (xop0, index, mode);
+ rtx op1_piece = operand_subword_force (xop1, index, mode);
+ rtx x;
+
+ /* Main add/subtract of the input operands. */
+ x = expand_binop (word_mode, binoptab,
+ op0_piece, op1_piece,
+ target_piece, unsignedp, next_methods);
+ if (x == 0)
+ break;
+
+ if (i + 1 < nwords)
+ {
+ /* Store carry from main add/subtract. */
+ carry_out = gen_reg_rtx (word_mode);
+ carry_out = emit_store_flag (carry_out,
+ binoptab == add_optab ? LTU : GTU,
+ x, op0_piece,
+ word_mode, 1, normalizep);
+ if (carry_out == 0)
+ break;
+ }
+
+ if (i > 0)
+ {
+ /* Add/subtract previous carry to main result. */
+ x = expand_binop (word_mode,
+ normalizep == 1 ? binoptab : otheroptab,
+ x, carry_in,
+ target_piece, 1, next_methods);
+ if (x == 0)
+ break;
+ else if (target_piece != x)
+ emit_move_insn (target_piece, x);
+
+ if (i + 1 < nwords)
+ {
+ /* THIS CODE HAS NOT BEEN TESTED. */
+ /* Get out carry from adding/subtracting carry in. */
+ carry_tmp = emit_store_flag (carry_tmp,
+ binoptab == add_optab
+ ? LTU : GTU,
+ x, carry_in,
+ word_mode, 1, normalizep);
+
+ /* Logical-ior the two poss. carry together. */
+ carry_out = expand_binop (word_mode, ior_optab,
+ carry_out, carry_tmp,
+ carry_out, 0, next_methods);
+ if (carry_out == 0)
+ break;
+ }
+ }
+
+ carry_in = carry_out;
+ }
+
+ if (i == GET_MODE_BITSIZE (mode) / BITS_PER_WORD)
+ {
+ rtx temp = emit_move_insn (target, target);
+
+ REG_NOTES (temp) = gen_rtx (EXPR_LIST, REG_EQUAL,
+ gen_rtx (binoptab->code, mode,
+ copy_rtx (xop0),
+ copy_rtx (xop1)),
+ REG_NOTES (temp));
+ return target;
+ }
+ else
+ delete_insns_since (last);
+ }
+
+ /* If we want to multiply two two-word values and have normal and widening
+ multiplies of single-word values, we can do this with three smaller
+ multiplications. Note that we do not make a REG_NO_CONFLICT block here
+ because we are not operating on one word at a time.
+
+ The multiplication proceeds as follows:
+ _______________________
+ [__op0_high_|__op0_low__]
+ _______________________
+ * [__op1_high_|__op1_low__]
+ _______________________________________________
+ _______________________
+ (1) [__op0_low__*__op1_low__]
+ _______________________
+ (2a) [__op0_low__*__op1_high_]
+ _______________________
+ (2b) [__op0_high_*__op1_low__]
+ _______________________
+ (3) [__op0_high_*__op1_high_]
+
+
+ This gives a 4-word result. Since we are only interested in the
+ lower 2 words, partial result (3) and the upper words of (2a) and
+ (2b) don't need to be calculated. Hence (2a) and (2b) can be
+ calculated using non-widening multiplication.
+
+ (1), however, needs to be calculated with an unsigned widening
+ multiplication. If this operation is not directly supported we
+ try using a signed widening multiplication and adjust the result.
+ This adjustment works as follows:
+
+ If both operands are positive then no adjustment is needed.
+
+ If the operands have different signs, for example op0_low < 0 and
+ op1_low >= 0, the instruction treats the most significant bit of
+ op0_low as a sign bit instead of a bit with significance
+ 2**(BITS_PER_WORD-1), i.e. the instruction multiplies op1_low
+ with 2**BITS_PER_WORD - op0_low, and two's complements the
+ result. Conclusion: We need to add op1_low * 2**BITS_PER_WORD to
+ the result.
+
+ Similarly, if both operands are negative, we need to add
+ (op0_low + op1_low) * 2**BITS_PER_WORD.
+
+ We use a trick to adjust quickly. We logically shift op0_low right
+ (op1_low) BITS_PER_WORD-1 steps to get 0 or 1, and add this to
+ op0_high (op1_high) before it is used to calculate 2b (2a). If no
+ logical shift exists, we do an arithmetic right shift and subtract
+ the 0 or -1. */
+
+ if (binoptab == smul_optab
+ && class == MODE_INT
+ && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
+ && smul_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
+ && add_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
+ && ((umul_widen_optab->handlers[(int) mode].insn_code
+ != CODE_FOR_nothing)
+ || (smul_widen_optab->handlers[(int) mode].insn_code
+ != CODE_FOR_nothing)))
+ {
+ int low = (WORDS_BIG_ENDIAN ? 1 : 0);
+ int high = (WORDS_BIG_ENDIAN ? 0 : 1);
+ rtx op0_high = operand_subword_force (op0, high, mode);
+ rtx op0_low = operand_subword_force (op0, low, mode);
+ rtx op1_high = operand_subword_force (op1, high, mode);
+ rtx op1_low = operand_subword_force (op1, low, mode);
+ rtx product = 0;
+ rtx op0_xhigh;
+ rtx op1_xhigh;
+
+ /* If the target is the same as one of the inputs, don't use it. This
+ prevents problems with the REG_EQUAL note. */
+ if (target == op0 || target == op1)
+ target = 0;
+
+ /* Multiply the two lower words to get a double-word product.
+ If unsigned widening multiplication is available, use that;
+ otherwise use the signed form and compensate. */
+
+ if (umul_widen_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+ {
+ product = expand_binop (mode, umul_widen_optab, op0_low, op1_low,
+ target, 1, OPTAB_DIRECT);
+
+ /* If we didn't succeed, delete everything we did so far. */
+ if (product == 0)
+ delete_insns_since (last);
+ else
+ op0_xhigh = op0_high, op1_xhigh = op1_high;
+ }
+
+ if (product == 0
+ && smul_widen_optab->handlers[(int) mode].insn_code
+ != CODE_FOR_nothing)
+ {
+ rtx wordm1 = GEN_INT (BITS_PER_WORD - 1);
+ product = expand_binop (mode, smul_widen_optab, op0_low, op1_low,
+ target, 1, OPTAB_DIRECT);
+ op0_xhigh = expand_binop (word_mode, lshr_optab, op0_low, wordm1,
+ NULL_RTX, 1, next_methods);
+ if (op0_xhigh)
+ op0_xhigh = expand_binop (word_mode, add_optab, op0_high,
+ op0_xhigh, op0_xhigh, 0, next_methods);
+ else
+ {
+ op0_xhigh = expand_binop (word_mode, ashr_optab, op0_low, wordm1,
+ NULL_RTX, 0, next_methods);
+ if (op0_xhigh)
+ op0_xhigh = expand_binop (word_mode, sub_optab, op0_high,
+ op0_xhigh, op0_xhigh, 0,
+ next_methods);
+ }
+
+ op1_xhigh = expand_binop (word_mode, lshr_optab, op1_low, wordm1,
+ NULL_RTX, 1, next_methods);
+ if (op1_xhigh)
+ op1_xhigh = expand_binop (word_mode, add_optab, op1_high,
+ op1_xhigh, op1_xhigh, 0, next_methods);
+ else
+ {
+ op1_xhigh = expand_binop (word_mode, ashr_optab, op1_low, wordm1,
+ NULL_RTX, 0, next_methods);
+ if (op1_xhigh)
+ op1_xhigh = expand_binop (word_mode, sub_optab, op1_high,
+ op1_xhigh, op1_xhigh, 0,
+ next_methods);
+ }
+ }
+
+ /* If we have been able to directly compute the product of the
+ low-order words of the operands and perform any required adjustments
+ of the operands, we proceed by trying two more multiplications
+ and then computing the appropriate sum.
+
+ We have checked above that the required addition is provided.
+ Full-word addition will normally always succeed, especially if
+ it is provided at all, so we don't worry about its failure. The
+ multiplication may well fail, however, so we do handle that. */
+
+ if (product && op0_xhigh && op1_xhigh)
+ {
+ rtx product_high = operand_subword (product, high, 1, mode);
+ rtx temp = expand_binop (word_mode, binoptab, op0_low, op1_xhigh,
+ NULL_RTX, 0, OPTAB_DIRECT);
+
+ if (temp != 0)
+ temp = expand_binop (word_mode, add_optab, temp, product_high,
+ product_high, 0, next_methods);
+
+ if (temp != 0 && temp != product_high)
+ emit_move_insn (product_high, temp);
+
+ if (temp != 0)
+ temp = expand_binop (word_mode, binoptab, op1_low, op0_xhigh,
+ NULL_RTX, 0, OPTAB_DIRECT);
+
+ if (temp != 0)
+ temp = expand_binop (word_mode, add_optab, temp,
+ product_high, product_high,
+ 0, next_methods);
+
+ if (temp != 0 && temp != product_high)
+ emit_move_insn (product_high, temp);
+
+ if (temp != 0)
+ {
+ temp = emit_move_insn (product, product);
+ REG_NOTES (temp) = gen_rtx (EXPR_LIST, REG_EQUAL,
+ gen_rtx (MULT, mode, copy_rtx (op0),
+ copy_rtx (op1)),
+ REG_NOTES (temp));
+
+ return product;
+ }
+ }
+
+ /* If we get here, we couldn't do it for some reason even though we
+ originally thought we could. Delete anything we've emitted in
+ trying to do it. */
+
+ delete_insns_since (last);
+ }
+
+ /* We need to open-code the complex type operations: '+, -, * and /' */
+
+ /* At this point we allow operations between two similar complex
+ numbers, and also if one of the operands is not a complex number
+ but rather of MODE_FLOAT or MODE_INT. However, the caller
+ must make sure that the MODE of the non-complex operand matches
+ the SUBMODE of the complex operand. */
+
+ if (class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT)
+ {
+ rtx real0 = 0, imag0 = 0;
+ rtx real1 = 0, imag1 = 0;
+ rtx realr, imagr, res;
+ rtx seq;
+ rtx equiv_value;
+ int ok = 0;
+
+ /* Find the correct mode for the real and imaginary parts */
+ enum machine_mode submode
+ = mode_for_size (GET_MODE_UNIT_SIZE (mode) * BITS_PER_UNIT,
+ class == MODE_COMPLEX_INT ? MODE_INT : MODE_FLOAT,
+ 0);
+
+ if (submode == BLKmode)
+ abort ();
+
+ if (! target)
+ target = gen_reg_rtx (mode);
+
+ start_sequence ();
+
+ realr = gen_realpart (submode, target);
+ imagr = gen_imagpart (submode, target);
+
+ if (GET_MODE (op0) == mode)
+ {
+ real0 = gen_realpart (submode, op0);
+ imag0 = gen_imagpart (submode, op0);
+ }
+ else
+ real0 = op0;
+
+ if (GET_MODE (op1) == mode)
+ {
+ real1 = gen_realpart (submode, op1);
+ imag1 = gen_imagpart (submode, op1);
+ }
+ else
+ real1 = op1;
+
+ if (real0 == 0 || real1 == 0 || ! (imag0 != 0|| imag1 != 0))
+ abort ();
+
+ switch (binoptab->code)
+ {
+ case PLUS:
+ /* (a+ib) + (c+id) = (a+c) + i(b+d) */
+ case MINUS:
+ /* (a+ib) - (c+id) = (a-c) + i(b-d) */
+ res = expand_binop (submode, binoptab, real0, real1,
+ realr, unsignedp, methods);
+
+ if (res == 0)
+ break;
+ else if (res != realr)
+ emit_move_insn (realr, res);
+
+ if (imag0 && imag1)
+ res = expand_binop (submode, binoptab, imag0, imag1,
+ imagr, unsignedp, methods);
+ else if (imag0)
+ res = imag0;
+ else if (binoptab->code == MINUS)
+ res = expand_unop (submode, neg_optab, imag1, imagr, unsignedp);
+ else
+ res = imag1;
+
+ if (res == 0)
+ break;
+ else if (res != imagr)
+ emit_move_insn (imagr, res);
+
+ ok = 1;
+ break;
+
+ case MULT:
+ /* (a+ib) * (c+id) = (ac-bd) + i(ad+cb) */
+
+ if (imag0 && imag1)
+ {
+ rtx temp1, temp2;
+
+ /* Don't fetch these from memory more than once. */
+ real0 = force_reg (submode, real0);
+ real1 = force_reg (submode, real1);
+ imag0 = force_reg (submode, imag0);
+ imag1 = force_reg (submode, imag1);
+
+ temp1 = expand_binop (submode, binoptab, real0, real1, NULL_RTX,
+ unsignedp, methods);
+
+ temp2 = expand_binop (submode, binoptab, imag0, imag1, NULL_RTX,
+ unsignedp, methods);
+
+ if (temp1 == 0 || temp2 == 0)
+ break;
+
+ res = expand_binop (submode, sub_optab, temp1, temp2,
+ realr, unsignedp, methods);
+
+ if (res == 0)
+ break;
+ else if (res != realr)
+ emit_move_insn (realr, res);
+
+ temp1 = expand_binop (submode, binoptab, real0, imag1,
+ NULL_RTX, unsignedp, methods);
+
+ temp2 = expand_binop (submode, binoptab, real1, imag0,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0 || temp2 == 0)
+ res = expand_binop (submode, add_optab, temp1, temp2,
+ imagr, unsignedp, methods);
+
+ if (res == 0)
+ break;
+ else if (res != imagr)
+ emit_move_insn (imagr, res);
+
+ ok = 1;
+ }
+ else
+ {
+ /* Don't fetch these from memory more than once. */
+ real0 = force_reg (submode, real0);
+ real1 = force_reg (submode, real1);
+
+ res = expand_binop (submode, binoptab, real0, real1,
+ realr, unsignedp, methods);
+ if (res == 0)
+ break;
+ else if (res != realr)
+ emit_move_insn (realr, res);
+
+ if (imag0 != 0)
+ res = expand_binop (submode, binoptab,
+ real1, imag0, imagr, unsignedp, methods);
+ else
+ res = expand_binop (submode, binoptab,
+ real0, imag1, imagr, unsignedp, methods);
+
+ if (res == 0)
+ break;
+ else if (res != imagr)
+ emit_move_insn (imagr, res);
+
+ ok = 1;
+ }
+ break;
+
+ case DIV:
+ /* (a+ib) / (c+id) = ((ac+bd)/(cc+dd)) + i((bc-ad)/(cc+dd)) */
+
+ if (imag1 == 0)
+ {
+ /* (a+ib) / (c+i0) = (a/c) + i(b/c) */
+
+ /* Don't fetch these from memory more than once. */
+ real1 = force_reg (submode, real1);
+
+ /* Simply divide the real and imaginary parts by `c' */
+ if (class == MODE_COMPLEX_FLOAT)
+ res = expand_binop (submode, binoptab, real0, real1,
+ realr, unsignedp, methods);
+ else
+ res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+ real0, real1, realr, unsignedp);
+
+ if (res == 0)
+ break;
+ else if (res != realr)
+ emit_move_insn (realr, res);
+
+ if (class == MODE_COMPLEX_FLOAT)
+ res = expand_binop (submode, binoptab, imag0, real1,
+ imagr, unsignedp, methods);
+ else
+ res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+ imag0, real1, imagr, unsignedp);
+
+ if (res == 0)
+ break;
+ else if (res != imagr)
+ emit_move_insn (imagr, res);
+
+ ok = 1;
+ }
+ else
+ {
+ /* Divisor is of complex type:
+ X/(a+ib) */
+ rtx divisor;
+ rtx real_t, imag_t;
+ rtx lhs, rhs;
+ rtx temp1, temp2;
+
+ /* Don't fetch these from memory more than once. */
+ real0 = force_reg (submode, real0);
+ real1 = force_reg (submode, real1);
+
+ if (imag0 != 0)
+ imag0 = force_reg (submode, imag0);
+
+ imag1 = force_reg (submode, imag1);
+
+ /* Divisor: c*c + d*d */
+ temp1 = expand_binop (submode, smul_optab, real1, real1,
+ NULL_RTX, unsignedp, methods);
+
+ temp2 = expand_binop (submode, smul_optab, imag1, imag1,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0 || temp2 == 0)
+ break;
+
+ divisor = expand_binop (submode, add_optab, temp1, temp2,
+ NULL_RTX, unsignedp, methods);
+ if (divisor == 0)
+ break;
+
+ if (imag0 == 0)
+ {
+ /* ((a)(c-id))/divisor */
+ /* (a+i0) / (c+id) = (ac/(cc+dd)) + i(-ad/(cc+dd)) */
+
+ /* Calculate the dividend */
+ real_t = expand_binop (submode, smul_optab, real0, real1,
+ NULL_RTX, unsignedp, methods);
+
+ imag_t = expand_binop (submode, smul_optab, real0, imag1,
+ NULL_RTX, unsignedp, methods);
+
+ if (real_t == 0 || imag_t == 0)
+ break;
+
+ imag_t = expand_unop (submode, neg_optab, imag_t,
+ NULL_RTX, unsignedp);
+ }
+ else
+ {
+ /* ((a+ib)(c-id))/divider */
+ /* Calculate the dividend */
+ temp1 = expand_binop (submode, smul_optab, real0, real1,
+ NULL_RTX, unsignedp, methods);
+
+ temp2 = expand_binop (submode, smul_optab, imag0, imag1,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0 || temp2 == 0)
+ break;
+
+ real_t = expand_binop (submode, add_optab, temp1, temp2,
+ NULL_RTX, unsignedp, methods);
+
+ temp1 = expand_binop (submode, smul_optab, imag0, real1,
+ NULL_RTX, unsignedp, methods);
+
+ temp2 = expand_binop (submode, smul_optab, real0, imag1,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0 || temp2 == 0)
+ break;
+
+ imag_t = expand_binop (submode, sub_optab, temp1, temp2,
+ NULL_RTX, unsignedp, methods);
+
+ if (real_t == 0 || imag_t == 0)
+ break;
+ }
+
+ if (class == MODE_COMPLEX_FLOAT)
+ res = expand_binop (submode, binoptab, real_t, divisor,
+ realr, unsignedp, methods);
+ else
+ res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+ real_t, divisor, realr, unsignedp);
+
+ if (res == 0)
+ break;
+ else if (res != realr)
+ emit_move_insn (realr, res);
+
+ if (class == MODE_COMPLEX_FLOAT)
+ res = expand_binop (submode, binoptab, imag_t, divisor,
+ imagr, unsignedp, methods);
+ else
+ res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+ imag_t, divisor, imagr, unsignedp);
+
+ if (res == 0)
+ break;
+ else if (res != imagr)
+ emit_move_insn (imagr, res);
+
+ ok = 1;
+ }
+ break;
+
+ default:
+ abort ();
+ }
+
+ seq = get_insns ();
+ end_sequence ();
+
+ if (ok)
+ {
+ if (binoptab->code != UNKNOWN)
+ equiv_value
+ = gen_rtx (binoptab->code, mode, copy_rtx (op0), copy_rtx (op1));
+ else
+ equiv_value = 0;
+
+ emit_no_conflict_block (seq, target, op0, op1, equiv_value);
+
+ return target;
+ }
+ }
+
+ /* It can't be open-coded in this mode.
+ Use a library call if one is available and caller says that's ok. */
+
+ if (binoptab->handlers[(int) mode].libfunc
+ && (methods == OPTAB_LIB || methods == OPTAB_LIB_WIDEN))
+ {
+ rtx insns;
+ rtx funexp = binoptab->handlers[(int) mode].libfunc;
+ rtx op1x = op1;
+ enum machine_mode op1_mode = mode;
+ rtx value;
+
+ start_sequence ();
+
+ if (shift_op)
+ {
+ op1_mode = word_mode;
+ /* Specify unsigned here,
+ since negative shift counts are meaningless. */
+ op1x = convert_to_mode (word_mode, op1, 1);
+ }
+
+ if (GET_MODE (op0) != mode)
+ op0 = convert_to_mode (mode, op0, unsignedp);
+
+ /* Pass 1 for NO_QUEUE so we don't lose any increments
+ if the libcall is cse'd or moved. */
+ value = emit_library_call_value (binoptab->handlers[(int) mode].libfunc,
+ NULL_RTX, 1, mode, 2,
+ op0, mode, op1x, op1_mode);
+
+ insns = get_insns ();
+ end_sequence ();
+
+ target = gen_reg_rtx (mode);
+ emit_libcall_block (insns, target, value,
+ gen_rtx (binoptab->code, mode, op0, op1));
+
+ return target;
+ }
+
+ delete_insns_since (last);
+
+ /* It can't be done in this mode. Can we do it in a wider mode? */
+
+ if (! (methods == OPTAB_WIDEN || methods == OPTAB_LIB_WIDEN
+ || methods == OPTAB_MUST_WIDEN))
+ {
+ /* Caller says, don't even try. */
+ delete_insns_since (entry_last);
+ return 0;
+ }
+
+ /* Compute the value of METHODS to pass to recursive calls.
+ Don't allow widening to be tried recursively. */
+
+ methods = (methods == OPTAB_LIB_WIDEN ? OPTAB_LIB : OPTAB_DIRECT);
+
+ /* Look for a wider mode of the same class for which it appears we can do
+ the operation. */
+
+ if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+ {
+ for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+ wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+ {
+ if ((binoptab->handlers[(int) wider_mode].insn_code
+ != CODE_FOR_nothing)
+ || (methods == OPTAB_LIB
+ && binoptab->handlers[(int) wider_mode].libfunc))
+ {
+ rtx xop0 = op0, xop1 = op1;
+ int no_extend = 0;
+
+ /* For certain integer operations, we need not actually extend
+ the narrow operands, as long as we will truncate
+ the results to the same narrowness. */
+
+ if ((binoptab == ior_optab || binoptab == and_optab
+ || binoptab == xor_optab
+ || binoptab == add_optab || binoptab == sub_optab
+ || binoptab == smul_optab || binoptab == ashl_optab)
+ && class == MODE_INT)
+ no_extend = 1;
+
+ xop0 = widen_operand (xop0, wider_mode, mode,
+ unsignedp, no_extend);
+
+ /* The second operand of a shift must always be extended. */
+ xop1 = widen_operand (xop1, wider_mode, mode, unsignedp,
+ no_extend && binoptab != ashl_optab);
+
+ temp = expand_binop (wider_mode, binoptab, xop0, xop1, NULL_RTX,
+ unsignedp, methods);
+ if (temp)
+ {
+ if (class != MODE_INT)
+ {
+ if (target == 0)
+ target = gen_reg_rtx (mode);
+ convert_move (target, temp, 0);
+ return target;
+ }
+ else
+ return gen_lowpart (mode, temp);
+ }
+ else
+ delete_insns_since (last);
+ }
+ }
+ }
+
+ delete_insns_since (entry_last);
+ return 0;
+}
+
+/* Expand a binary operator which has both signed and unsigned forms.
+ UOPTAB is the optab for unsigned operations, and SOPTAB is for
+ signed operations.
+
+ If we widen unsigned operands, we may use a signed wider operation instead
+ of an unsigned wider operation, since the result would be the same. */
+
+rtx
+sign_expand_binop (mode, uoptab, soptab, op0, op1, target, unsignedp, methods)
+ enum machine_mode mode;
+ optab uoptab, soptab;
+ rtx op0, op1, target;
+ int unsignedp;
+ enum optab_methods methods;
+{
+ register rtx temp;
+ optab direct_optab = unsignedp ? uoptab : soptab;
+ struct optab wide_soptab;
+
+ /* Do it without widening, if possible. */
+ temp = expand_binop (mode, direct_optab, op0, op1, target,
+ unsignedp, OPTAB_DIRECT);
+ if (temp || methods == OPTAB_DIRECT)
+ return temp;
+
+ /* Try widening to a signed int. Make a fake signed optab that
+ hides any signed insn for direct use. */
+ wide_soptab = *soptab;
+ wide_soptab.handlers[(int) mode].insn_code = CODE_FOR_nothing;
+ wide_soptab.handlers[(int) mode].libfunc = 0;
+
+ temp = expand_binop (mode, &wide_soptab, op0, op1, target,
+ unsignedp, OPTAB_WIDEN);
+
+ /* For unsigned operands, try widening to an unsigned int. */
+ if (temp == 0 && unsignedp)
+ temp = expand_binop (mode, uoptab, op0, op1, target,
+ unsignedp, OPTAB_WIDEN);
+ if (temp || methods == OPTAB_WIDEN)
+ return temp;
+
+ /* Use the right width lib call if that exists. */
+ temp = expand_binop (mode, direct_optab, op0, op1, target, unsignedp, OPTAB_LIB);
+ if (temp || methods == OPTAB_LIB)
+ return temp;
+
+ /* Must widen and use a lib call, use either signed or unsigned. */
+ temp = expand_binop (mode, &wide_soptab, op0, op1, target,
+ unsignedp, methods);
+ if (temp != 0)
+ return temp;
+ if (unsignedp)
+ return expand_binop (mode, uoptab, op0, op1, target,
+ unsignedp, methods);
+ return 0;
+}
+
+/* Generate code to perform an operation specified by BINOPTAB
+ on operands OP0 and OP1, with two results to TARG1 and TARG2.
+ We assume that the order of the operands for the instruction
+ is TARG0, OP0, OP1, TARG1, which would fit a pattern like
+ [(set TARG0 (operate OP0 OP1)) (set TARG1 (operate ...))].
+
+ Either TARG0 or TARG1 may be zero, but what that means is that
+ that result is not actually wanted. We will generate it into
+ a dummy pseudo-reg and discard it. They may not both be zero.
+
+ Returns 1 if this operation can be performed; 0 if not. */
+
+int
+expand_twoval_binop (binoptab, op0, op1, targ0, targ1, unsignedp)
+ optab binoptab;
+ rtx op0, op1;
+ rtx targ0, targ1;
+ int unsignedp;
+{
+ enum machine_mode mode = GET_MODE (targ0 ? targ0 : targ1);
+ enum mode_class class;
+ enum machine_mode wider_mode;
+ rtx entry_last = get_last_insn ();
+ rtx last;
+
+ class = GET_MODE_CLASS (mode);
+
+ op0 = protect_from_queue (op0, 0);
+ op1 = protect_from_queue (op1, 0);
+
+ if (flag_force_mem)
+ {
+ op0 = force_not_mem (op0);
+ op1 = force_not_mem (op1);
+ }
+
+ /* If we are inside an appropriately-short loop and one operand is an
+ expensive constant, force it into a register. */
+ if (CONSTANT_P (op0) && preserve_subexpressions_p ()
+ && rtx_cost (op0, binoptab->code) > 2)
+ op0 = force_reg (mode, op0);
+
+ if (CONSTANT_P (op1) && preserve_subexpressions_p ()
+ && rtx_cost (op1, binoptab->code) > 2)
+ op1 = force_reg (mode, op1);
+
+ if (targ0)
+ targ0 = protect_from_queue (targ0, 1);
+ else
+ targ0 = gen_reg_rtx (mode);
+ if (targ1)
+ targ1 = protect_from_queue (targ1, 1);
+ else
+ targ1 = gen_reg_rtx (mode);
+
+ /* Record where to go back to if we fail. */
+ last = get_last_insn ();
+
+ if (binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+ {
+ int icode = (int) binoptab->handlers[(int) mode].insn_code;
+ enum machine_mode mode0 = insn_operand_mode[icode][1];
+ enum machine_mode mode1 = insn_operand_mode[icode][2];
+ rtx pat;
+ rtx xop0 = op0, xop1 = op1;
+
+ /* In case this insn wants input operands in modes different from the
+ result, convert the operands. */
+ if (GET_MODE (op0) != VOIDmode && GET_MODE (op0) != mode0)
+ xop0 = convert_to_mode (mode0, xop0, unsignedp);
+
+ if (GET_MODE (op1) != VOIDmode && GET_MODE (op1) != mode1)
+ xop1 = convert_to_mode (mode1, xop1, unsignedp);
+
+ /* Now, if insn doesn't accept these operands, put them into pseudos. */
+ if (! (*insn_operand_predicate[icode][1]) (xop0, mode0))
+ xop0 = copy_to_mode_reg (mode0, xop0);
+
+ if (! (*insn_operand_predicate[icode][2]) (xop1, mode1))
+ xop1 = copy_to_mode_reg (mode1, xop1);
+
+ /* We could handle this, but we should always be called with a pseudo
+ for our targets and all insns should take them as outputs. */
+ if (! (*insn_operand_predicate[icode][0]) (targ0, mode)
+ || ! (*insn_operand_predicate[icode][3]) (targ1, mode))
+ abort ();
+
+ pat = GEN_FCN (icode) (targ0, xop0, xop1, targ1);
+ if (pat)
+ {
+ emit_insn (pat);
+ return 1;
+ }
+ else
+ delete_insns_since (last);
+ }
+
+ /* It can't be done in this mode. Can we do it in a wider mode? */
+
+ if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+ {
+ for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+ wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+ {
+ if (binoptab->handlers[(int) wider_mode].insn_code
+ != CODE_FOR_nothing)
+ {
+ register rtx t0 = gen_reg_rtx (wider_mode);
+ register rtx t1 = gen_reg_rtx (wider_mode);
+
+ if (expand_twoval_binop (binoptab,
+ convert_modes (wider_mode, mode, op0,
+ unsignedp),
+ convert_modes (wider_mode, mode, op1,
+ unsignedp),
+ t0, t1, unsignedp))
+ {
+ convert_move (targ0, t0, unsignedp);
+ convert_move (targ1, t1, unsignedp);
+ return 1;
+ }
+ else
+ delete_insns_since (last);
+ }
+ }
+ }
+
+ delete_insns_since (entry_last);
+ return 0;
+}
+
+/* Generate code to perform an operation specified by UNOPTAB
+ on operand OP0, with result having machine-mode MODE.
+
+ UNSIGNEDP is for the case where we have to widen the operands
+ to perform the operation. It says to use zero-extension.
+
+ If TARGET is nonzero, the value
+ is generated there, if it is convenient to do so.
+ In all cases an rtx is returned for the locus of the value;
+ this may or may not be TARGET. */
+
+rtx
+expand_unop (mode, unoptab, op0, target, unsignedp)
+ enum machine_mode mode;
+ optab unoptab;
+ rtx op0;
+ rtx target;
+ int unsignedp;
+{
+ enum mode_class class;
+ enum machine_mode wider_mode;
+ register rtx temp;
+ rtx last = get_last_insn ();
+ rtx pat;
+
+ class = GET_MODE_CLASS (mode);
+
+ op0 = protect_from_queue (op0, 0);
+
+ if (flag_force_mem)
+ {
+ op0 = force_not_mem (op0);
+ }
+
+ if (target)
+ target = protect_from_queue (target, 1);
+
+ if (unoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+ {
+ int icode = (int) unoptab->handlers[(int) mode].insn_code;
+ enum machine_mode mode0 = insn_operand_mode[icode][1];
+ rtx xop0 = op0;
+
+ if (target)
+ temp = target;
+ else
+ temp = gen_reg_rtx (mode);
+
+ if (GET_MODE (xop0) != VOIDmode
+ && GET_MODE (xop0) != mode0)
+ xop0 = convert_to_mode (mode0, xop0, unsignedp);
+
+ /* Now, if insn doesn't accept our operand, put it into a pseudo. */
+
+ if (! (*insn_operand_predicate[icode][1]) (xop0, mode0))
+ xop0 = copy_to_mode_reg (mode0, xop0);
+
+ if (! (*insn_operand_predicate[icode][0]) (temp, mode))
+ temp = gen_reg_rtx (mode);
+
+ pat = GEN_FCN (icode) (temp, xop0);
+ if (pat)
+ {
+ if (GET_CODE (pat) == SEQUENCE
+ && ! add_equal_note (pat, temp, unoptab->code, xop0, NULL_RTX))
+ {
+ delete_insns_since (last);
+ return expand_unop (mode, unoptab, op0, NULL_RTX, unsignedp);
+ }
+
+ emit_insn (pat);
+
+ return temp;
+ }
+ else
+ delete_insns_since (last);
+ }
+
+ /* It can't be done in this mode. Can we open-code it in a wider mode? */
+
+ if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+ for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+ wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+ {
+ if (unoptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing)
+ {
+ rtx xop0 = op0;
+
+ /* For certain operations, we need not actually extend
+ the narrow operand, as long as we will truncate the
+ results to the same narrowness. */
+
+ xop0 = widen_operand (xop0, wider_mode, mode, unsignedp,
+ (unoptab == neg_optab
+ || unoptab == one_cmpl_optab)
+ && class == MODE_INT);
+
+ temp = expand_unop (wider_mode, unoptab, xop0, NULL_RTX,
+ unsignedp);
+
+ if (temp)
+ {
+ if (class != MODE_INT)
+ {
+ if (target == 0)
+ target = gen_reg_rtx (mode);
+ convert_move (target, temp, 0);
+ return target;
+ }
+ else
+ return gen_lowpart (mode, temp);
+ }
+ else
+ delete_insns_since (last);
+ }
+ }
+
+ /* These can be done a word at a time. */
+ if (unoptab == one_cmpl_optab
+ && class == MODE_INT
+ && GET_MODE_SIZE (mode) > UNITS_PER_WORD
+ && unoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing)
+ {
+ int i;
+ rtx insns;
+
+ if (target == 0 || target == op0)
+ target = gen_reg_rtx (mode);
+
+ start_sequence ();
+
+ /* Do the actual arithmetic. */
+ for (i = 0; i < GET_MODE_BITSIZE (mode) / BITS_PER_WORD; i++)
+ {
+ rtx target_piece = operand_subword (target, i, 1, mode);
+ rtx x = expand_unop (word_mode, unoptab,
+ operand_subword_force (op0, i, mode),
+ target_piece, unsignedp);
+ if (target_piece != x)
+ emit_move_insn (target_piece, x);
+ }
+
+ insns = get_insns ();
+ end_sequence ();
+
+ emit_no_conflict_block (insns, target, op0, NULL_RTX,
+ gen_rtx (unoptab->code, mode, copy_rtx (op0)));
+ return target;
+ }
+
+ /* Open-code the complex negation operation. */
+ else if (unoptab == neg_optab
+ && (class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT))
+ {
+ rtx target_piece;
+ rtx x;
+ rtx seq;
+
+ /* Find the correct mode for the real and imaginary parts */
+ enum machine_mode submode
+ = mode_for_size (GET_MODE_UNIT_SIZE (mode) * BITS_PER_UNIT,
+ class == MODE_COMPLEX_INT ? MODE_INT : MODE_FLOAT,
+ 0);
+
+ if (submode == BLKmode)
+ abort ();
+
+ if (target == 0)
+ target = gen_reg_rtx (mode);
+
+ start_sequence ();
+
+ target_piece = gen_imagpart (submode, target);
+ x = expand_unop (submode, unoptab,
+ gen_imagpart (submode, op0),
+ target_piece, unsignedp);
+ if (target_piece != x)
+ emit_move_insn (target_piece, x);
+
+ target_piece = gen_realpart (submode, target);
+ x = expand_unop (submode, unoptab,
+ gen_realpart (submode, op0),
+ target_piece, unsignedp);
+ if (target_piece != x)
+ emit_move_insn (target_piece, x);
+
+ seq = get_insns ();
+ end_sequence ();
+
+ emit_no_conflict_block (seq, target, op0, 0,
+ gen_rtx (unoptab->code, mode, copy_rtx (op0)));
+ return target;
+ }
+
+ /* Now try a library call in this mode. */
+ if (unoptab->handlers[(int) mode].libfunc)
+ {
+ rtx insns;
+ rtx funexp = unoptab->handlers[(int) mode].libfunc;
+ rtx value;
+
+ start_sequence ();
+
+ /* Pass 1 for NO_QUEUE so we don't lose any increments
+ if the libcall is cse'd or moved. */
+ value = emit_library_call_value (unoptab->handlers[(int) mode].libfunc,
+ NULL_RTX, 1, mode, 1, op0, mode);
+ insns = get_insns ();
+ end_sequence ();
+
+ target = gen_reg_rtx (mode);
+ emit_libcall_block (insns, target, value,
+ gen_rtx (unoptab->code, mode, op0));
+
+ return target;
+ }
+
+ /* It can't be done in this mode. Can we do it in a wider mode? */
+
+ if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+ {
+ for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+ wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+ {
+ if ((unoptab->handlers[(int) wider_mode].insn_code
+ != CODE_FOR_nothing)
+ || unoptab->handlers[(int) wider_mode].libfunc)
+ {
+ rtx xop0 = op0;
+
+ /* For certain operations, we need not actually extend
+ the narrow operand, as long as we will truncate the
+ results to the same narrowness. */
+
+ xop0 = widen_operand (xop0, wider_mode, mode, unsignedp,
+ (unoptab == neg_optab
+ || unoptab == one_cmpl_optab)
+ && class == MODE_INT);
+
+ temp = expand_unop (wider_mode, unoptab, xop0, NULL_RTX,
+ unsignedp);
+
+ if (temp)
+ {
+ if (class != MODE_INT)
+ {
+ if (target == 0)
+ target = gen_reg_rtx (mode);
+ convert_move (target, temp, 0);
+ return target;
+ }
+ else
+ return gen_lowpart (mode, temp);
+ }
+ else
+ delete_insns_since (last);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Emit code to compute the absolute value of OP0, with result to
+ TARGET if convenient. (TARGET may be 0.) The return value says
+ where the result actually is to be found.
+
+ MODE is the mode of the operand; the mode of the result is
+ different but can be deduced from MODE.
+
+ UNSIGNEDP is relevant for complex integer modes. */
+
+rtx
+expand_complex_abs (mode, op0, target, unsignedp)
+ enum machine_mode mode;
+ rtx op0;
+ rtx target;
+ int unsignedp;
+{
+ enum mode_class class = GET_MODE_CLASS (mode);
+ enum machine_mode wider_mode;
+ register rtx temp;
+ rtx entry_last = get_last_insn ();
+ rtx last;
+ rtx pat;
+
+ /* Find the correct mode for the real and imaginary parts. */
+ enum machine_mode submode
+ = mode_for_size (GET_MODE_UNIT_SIZE (mode) * BITS_PER_UNIT,
+ class == MODE_COMPLEX_INT ? MODE_INT : MODE_FLOAT,
+ 0);
+
+ if (submode == BLKmode)
+ abort ();
+
+ op0 = protect_from_queue (op0, 0);
+
+ if (flag_force_mem)
+ {
+ op0 = force_not_mem (op0);
+ }
+
+ last = get_last_insn ();
+
+ if (target)
+ target = protect_from_queue (target, 1);
+
+ if (abs_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+ {
+ int icode = (int) abs_optab->handlers[(int) mode].insn_code;
+ enum machine_mode mode0 = insn_operand_mode[icode][1];
+ rtx xop0 = op0;
+
+ if (target)
+ temp = target;
+ else
+ temp = gen_reg_rtx (submode);
+
+ if (GET_MODE (xop0) != VOIDmode
+ && GET_MODE (xop0) != mode0)
+ xop0 = convert_to_mode (mode0, xop0, unsignedp);
+
+ /* Now, if insn doesn't accept our operand, put it into a pseudo. */
+
+ if (! (*insn_operand_predicate[icode][1]) (xop0, mode0))
+ xop0 = copy_to_mode_reg (mode0, xop0);
+
+ if (! (*insn_operand_predicate[icode][0]) (temp, submode))
+ temp = gen_reg_rtx (submode);
+
+ pat = GEN_FCN (icode) (temp, xop0);
+ if (pat)
+ {
+ if (GET_CODE (pat) == SEQUENCE
+ && ! add_equal_note (pat, temp, abs_optab->code, xop0, NULL_RTX))
+ {
+ delete_insns_since (last);
+ return expand_unop (mode, abs_optab, op0, NULL_RTX, unsignedp);
+ }
+
+ emit_insn (pat);
+
+ return temp;
+ }
+ else
+ delete_insns_since (last);
+ }
+
+ /* It can't be done in this mode. Can we open-code it in a wider mode? */
+
+ for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+ wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+ {
+ if (abs_optab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing)
+ {
+ rtx xop0 = op0;
+
+ xop0 = convert_modes (wider_mode, mode, xop0, unsignedp);
+ temp = expand_complex_abs (wider_mode, xop0, NULL_RTX, unsignedp);
+
+ if (temp)
+ {
+ if (class != MODE_COMPLEX_INT)
+ {
+ if (target == 0)
+ target = gen_reg_rtx (submode);
+ convert_move (target, temp, 0);
+ return target;
+ }
+ else
+ return gen_lowpart (submode, temp);
+ }
+ else
+ delete_insns_since (last);
+ }
+ }
+
+ /* Open-code the complex absolute-value operation
+ if we can open-code sqrt. Otherwise it's not worth while. */
+ if (sqrt_optab->handlers[(int) submode].insn_code != CODE_FOR_nothing)
+ {
+ rtx real, imag, total;
+
+ real = gen_realpart (submode, op0);
+ imag = gen_imagpart (submode, op0);
+
+ /* Square both parts. */
+ real = expand_mult (submode, real, real, NULL_RTX, 0);
+ imag = expand_mult (submode, imag, imag, NULL_RTX, 0);
+
+ /* Sum the parts. */
+ total = expand_binop (submode, add_optab, real, imag, NULL_RTX,
+ 0, OPTAB_LIB_WIDEN);
+
+ /* Get sqrt in TARGET. Set TARGET to where the result is. */
+ target = expand_unop (submode, sqrt_optab, total, target, 0);
+ if (target == 0)
+ delete_insns_since (last);
+ else
+ return target;
+ }
+
+ /* Now try a library call in this mode. */
+ if (abs_optab->handlers[(int) mode].libfunc)
+ {
+ rtx insns;
+ rtx funexp = abs_optab->handlers[(int) mode].libfunc;
+ rtx value;
+
+ start_sequence ();
+
+ /* Pass 1 for NO_QUEUE so we don't lose any increments
+ if the libcall is cse'd or moved. */
+ value = emit_library_call_value (abs_optab->handlers[(int) mode].libfunc,
+ NULL_RTX, 1, submode, 1, op0, mode);
+ insns = get_insns ();
+ end_sequence ();
+
+ target = gen_reg_rtx (submode);
+ emit_libcall_block (insns, target, value,
+ gen_rtx (abs_optab->code, mode, op0));
+
+ return target;
+ }
+
+ /* It can't be done in this mode. Can we do it in a wider mode? */
+
+ for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+ wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+ {
+ if ((abs_optab->handlers[(int) wider_mode].insn_code
+ != CODE_FOR_nothing)
+ || abs_optab->handlers[(int) wider_mode].libfunc)
+ {
+ rtx xop0 = op0;
+
+ xop0 = convert_modes (wider_mode, mode, xop0, unsignedp);
+
+ temp = expand_complex_abs (wider_mode, xop0, NULL_RTX, unsignedp);
+
+ if (temp)
+ {
+ if (class != MODE_COMPLEX_INT)
+ {
+ if (target == 0)
+ target = gen_reg_rtx (submode);
+ convert_move (target, temp, 0);
+ return target;
+ }
+ else
+ return gen_lowpart (submode, temp);
+ }
+ else
+ delete_insns_since (last);
+ }
+ }
+
+ delete_insns_since (entry_last);
+ return 0;
+}
+
+/* Generate an instruction whose insn-code is INSN_CODE,
+ with two operands: an output TARGET and an input OP0.
+ TARGET *must* be nonzero, and the output is always stored there.
+ CODE is an rtx code such that (CODE OP0) is an rtx that describes
+ the value that is stored into TARGET. */
+
+void
+emit_unop_insn (icode, target, op0, code)
+ int icode;
+ rtx target;
+ rtx op0;
+ enum rtx_code code;
+{
+ register rtx temp;
+ enum machine_mode mode0 = insn_operand_mode[icode][1];
+ rtx pat;
+
+ temp = target = protect_from_queue (target, 1);
+
+ op0 = protect_from_queue (op0, 0);
+
+ if (flag_force_mem)
+ op0 = force_not_mem (op0);
+
+ /* Now, if insn does not accept our operands, put them into pseudos. */
+
+ if (! (*insn_operand_predicate[icode][1]) (op0, mode0))
+ op0 = copy_to_mode_reg (mode0, op0);
+
+ if (! (*insn_operand_predicate[icode][0]) (temp, GET_MODE (temp))
+ || (flag_force_mem && GET_CODE (temp) == MEM))
+ temp = gen_reg_rtx (GET_MODE (temp));
+
+ pat = GEN_FCN (icode) (temp, op0);
+
+ if (GET_CODE (pat) == SEQUENCE && code != UNKNOWN)
+ add_equal_note (pat, temp, code, op0, NULL_RTX);
+
+ emit_insn (pat);
+
+ if (temp != target)
+ emit_move_insn (target, temp);
+}
+
+/* Emit code to perform a series of operations on a multi-word quantity, one
+ word at a time.
+
+ Such a block is preceded by a CLOBBER of the output, consists of multiple
+ insns, each setting one word of the output, and followed by a SET copying
+ the output to itself.
+
+ Each of the insns setting words of the output receives a REG_NO_CONFLICT
+ note indicating that it doesn't conflict with the (also multi-word)
+ inputs. The entire block is surrounded by REG_LIBCALL and REG_RETVAL
+ notes.
+
+ INSNS is a block of code generated to perform the operation, not including
+ the CLOBBER and final copy. All insns that compute intermediate values
+ are first emitted, followed by the block as described above. Only
+ INSNs are allowed in the block; no library calls or jumps may be
+ present.
+
+ TARGET, OP0, and OP1 are the output and inputs of the operations,
+ respectively. OP1 may be zero for a unary operation.
+
+ EQUIV, if non-zero, is an expression to be placed into a REG_EQUAL note
+ on the last insn.
+
+ If TARGET is not a register, INSNS is simply emitted with no special
+ processing.
+
+ The final insn emitted is returned. */
+
+rtx
+emit_no_conflict_block (insns, target, op0, op1, equiv)
+ rtx insns;
+ rtx target;
+ rtx op0, op1;
+ rtx equiv;
+{
+ rtx prev, next, first, last, insn;
+
+ if (GET_CODE (target) != REG || reload_in_progress)
+ return emit_insns (insns);
+
+ /* First emit all insns that do not store into words of the output and remove
+ these from the list. */
+ for (insn = insns; insn; insn = next)
+ {
+ rtx set = 0;
+ int i;
+
+ next = NEXT_INSN (insn);
+
+ if (GET_CODE (insn) != INSN)
+ abort ();
+
+ if (GET_CODE (PATTERN (insn)) == SET)
+ set = PATTERN (insn);
+ else if (GET_CODE (PATTERN (insn)) == PARALLEL)
+ {
+ for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
+ if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET)
+ {
+ set = XVECEXP (PATTERN (insn), 0, i);
+ break;
+ }
+ }
+
+ if (set == 0)
+ abort ();
+
+ if (! reg_overlap_mentioned_p (target, SET_DEST (set)))
+ {
+ if (PREV_INSN (insn))
+ NEXT_INSN (PREV_INSN (insn)) = next;
+ else
+ insns = next;
+
+ if (next)
+ PREV_INSN (next) = PREV_INSN (insn);
+
+ add_insn (insn);
+ }
+ }
+
+ prev = get_last_insn ();
+
+ /* Now write the CLOBBER of the output, followed by the setting of each
+ of the words, followed by the final copy. */
+ if (target != op0 && target != op1)
+ emit_insn (gen_rtx (CLOBBER, VOIDmode, target));
+
+ for (insn = insns; insn; insn = next)
+ {
+ next = NEXT_INSN (insn);
+ add_insn (insn);
+
+ if (op1 && GET_CODE (op1) == REG)
+ REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_NO_CONFLICT, op1,
+ REG_NOTES (insn));
+
+ if (op0 && GET_CODE (op0) == REG)
+ REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_NO_CONFLICT, op0,
+ REG_NOTES (insn));
+ }
+
+ if (mov_optab->handlers[(int) GET_MODE (target)].insn_code
+ != CODE_FOR_nothing)
+ {
+ last = emit_move_insn (target, target);
+ if (equiv)
+ REG_NOTES (last)
+ = gen_rtx (EXPR_LIST, REG_EQUAL, equiv, REG_NOTES (last));
+ }
+ else
+ last = get_last_insn ();
+
+ if (prev == 0)
+ first = get_insns ();
+ else
+ first = NEXT_INSN (prev);
+
+ /* Encapsulate the block so it gets manipulated as a unit. */
+ REG_NOTES (first) = gen_rtx (INSN_LIST, REG_LIBCALL, last,
+ REG_NOTES (first));
+ REG_NOTES (last) = gen_rtx (INSN_LIST, REG_RETVAL, first, REG_NOTES (last));
+
+ return last;
+}
+
+/* Emit code to make a call to a constant function or a library call.
+
+ INSNS is a list containing all insns emitted in the call.
+ These insns leave the result in RESULT. Our block is to copy RESULT
+ to TARGET, which is logically equivalent to EQUIV.
+
+ We first emit any insns that set a pseudo on the assumption that these are
+ loading constants into registers; doing so allows them to be safely cse'ed
+ between blocks. Then we emit all the other insns in the block, followed by
+ an insn to move RESULT to TARGET. This last insn will have a REQ_EQUAL
+ note with an operand of EQUIV.
+
+ Moving assignments to pseudos outside of the block is done to improve
+ the generated code, but is not required to generate correct code,
+ hence being unable to move an assignment is not grounds for not making
+ a libcall block. There are two reasons why it is safe to leave these
+ insns inside the block: First, we know that these pseudos cannot be
+ used in generated RTL outside the block since they are created for
+ temporary purposes within the block. Second, CSE will not record the
+ values of anything set inside a libcall block, so we know they must
+ be dead at the end of the block.
+
+ Except for the first group of insns (the ones setting pseudos), the
+ block is delimited by REG_RETVAL and REG_LIBCALL notes. */
+
+void
+emit_libcall_block (insns, target, result, equiv)
+ rtx insns;
+ rtx target;
+ rtx result;
+ rtx equiv;
+{
+ rtx prev, next, first, last, insn;
+
+ /* First emit all insns that set pseudos. Remove them from the list as
+ we go. Avoid insns that set pseudos which were referenced in previous
+ insns. These can be generated by move_by_pieces, for example,
+ to update an address. Similarly, avoid insns that reference things
+ set in previous insns. */
+
+ for (insn = insns; insn; insn = next)
+ {
+ rtx set = single_set (insn);
+
+ next = NEXT_INSN (insn);
+
+ if (set != 0 && GET_CODE (SET_DEST (set)) == REG
+ && REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER
+ && (insn == insns
+ || (! reg_mentioned_p (SET_DEST (set), PATTERN (insns))
+ && ! reg_used_between_p (SET_DEST (set), insns, insn)
+ && ! modified_in_p (SET_SRC (set), insns)
+ && ! modified_between_p (SET_SRC (set), insns, insn))))
+ {
+ if (PREV_INSN (insn))
+ NEXT_INSN (PREV_INSN (insn)) = next;
+ else
+ insns = next;
+
+ if (next)
+ PREV_INSN (next) = PREV_INSN (insn);
+
+ add_insn (insn);
+ }
+ }
+
+ prev = get_last_insn ();
+
+ /* Write the remaining insns followed by the final copy. */
+
+ for (insn = insns; insn; insn = next)
+ {
+ next = NEXT_INSN (insn);
+
+ add_insn (insn);
+ }
+
+ last = emit_move_insn (target, result);
+ REG_NOTES (last) = gen_rtx (EXPR_LIST,
+ REG_EQUAL, copy_rtx (equiv), REG_NOTES (last));
+
+ if (prev == 0)
+ first = get_insns ();
+ else
+ first = NEXT_INSN (prev);
+
+ /* Encapsulate the block so it gets manipulated as a unit. */
+ REG_NOTES (first) = gen_rtx (INSN_LIST, REG_LIBCALL, last,
+ REG_NOTES (first));
+ REG_NOTES (last) = gen_rtx (INSN_LIST, REG_RETVAL, first, REG_NOTES (last));
+}
+
+/* Generate code to store zero in X. */
+
+void
+emit_clr_insn (x)
+ rtx x;
+{
+ emit_move_insn (x, const0_rtx);
+}
+
+/* Generate code to store 1 in X
+ assuming it contains zero beforehand. */
+
+void
+emit_0_to_1_insn (x)
+ rtx x;
+{
+ emit_move_insn (x, const1_rtx);
+}
+
+/* Generate code to compare X with Y
+ so that the condition codes are set.
+
+ MODE is the mode of the inputs (in case they are const_int).
+ UNSIGNEDP nonzero says that X and Y are unsigned;
+ this matters if they need to be widened.
+
+ If they have mode BLKmode, then SIZE specifies the size of both X and Y,
+ and ALIGN specifies the known shared alignment of X and Y.
+
+ COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.).
+ It is ignored for fixed-point and block comparisons;
+ it is used only for floating-point comparisons. */
+
+void
+emit_cmp_insn (x, y, comparison, size, mode, unsignedp, align)
+ rtx x, y;
+ enum rtx_code comparison;
+ rtx size;
+ enum machine_mode mode;
+ int unsignedp;
+ int align;
+{
+ enum mode_class class;
+ enum machine_mode wider_mode;
+
+ class = GET_MODE_CLASS (mode);
+
+ /* They could both be VOIDmode if both args are immediate constants,
+ but we should fold that at an earlier stage.
+ With no special code here, this will call abort,
+ reminding the programmer to implement such folding. */
+
+ if (mode != BLKmode && flag_force_mem)
+ {
+ x = force_not_mem (x);
+ y = force_not_mem (y);
+ }
+
+ /* If we are inside an appropriately-short loop and one operand is an
+ expensive constant, force it into a register. */
+ if (CONSTANT_P (x) && preserve_subexpressions_p () && rtx_cost (x, COMPARE) > 2)
+ x = force_reg (mode, x);
+
+ if (CONSTANT_P (y) && preserve_subexpressions_p () && rtx_cost (y, COMPARE) > 2)
+ y = force_reg (mode, y);
+
+ /* Don't let both operands fail to indicate the mode. */
+ if (GET_MODE (x) == VOIDmode && GET_MODE (y) == VOIDmode)
+ x = force_reg (mode, x);
+
+ /* Handle all BLKmode compares. */
+
+ if (mode == BLKmode)
+ {
+ emit_queue ();
+ x = protect_from_queue (x, 0);
+ y = protect_from_queue (y, 0);
+
+ if (size == 0)
+ abort ();
+#ifdef HAVE_cmpstrqi
+ if (HAVE_cmpstrqi
+ && GET_CODE (size) == CONST_INT
+ && INTVAL (size) < (1 << GET_MODE_BITSIZE (QImode)))
+ {
+ enum machine_mode result_mode
+ = insn_operand_mode[(int) CODE_FOR_cmpstrqi][0];
+ rtx result = gen_reg_rtx (result_mode);
+ emit_insn (gen_cmpstrqi (result, x, y, size, GEN_INT (align)));
+ emit_cmp_insn (result, const0_rtx, comparison, NULL_RTX,
+ result_mode, 0, 0);
+ }
+ else
+#endif
+#ifdef HAVE_cmpstrhi
+ if (HAVE_cmpstrhi
+ && GET_CODE (size) == CONST_INT
+ && INTVAL (size) < (1 << GET_MODE_BITSIZE (HImode)))
+ {
+ enum machine_mode result_mode
+ = insn_operand_mode[(int) CODE_FOR_cmpstrhi][0];
+ rtx result = gen_reg_rtx (result_mode);
+ emit_insn (gen_cmpstrhi (result, x, y, size, GEN_INT (align)));
+ emit_cmp_insn (result, const0_rtx, comparison, NULL_RTX,
+ result_mode, 0, 0);
+ }
+ else
+#endif
+#ifdef HAVE_cmpstrsi
+ if (HAVE_cmpstrsi)
+ {
+ enum machine_mode result_mode
+ = insn_operand_mode[(int) CODE_FOR_cmpstrsi][0];
+ rtx result = gen_reg_rtx (result_mode);
+ size = protect_from_queue (size, 0);
+ emit_insn (gen_cmpstrsi (result, x, y,
+ convert_to_mode (SImode, size, 1),
+ GEN_INT (align)));
+ emit_cmp_insn (result, const0_rtx, comparison, NULL_RTX,
+ result_mode, 0, 0);
+ }
+ else
+#endif
+ {
+#ifdef TARGET_MEM_FUNCTIONS
+ emit_library_call (memcmp_libfunc, 0,
+ TYPE_MODE (integer_type_node), 3,
+ XEXP (x, 0), Pmode, XEXP (y, 0), Pmode,
+ size, Pmode);
+#else
+ emit_library_call (bcmp_libfunc, 0,
+ TYPE_MODE (integer_type_node), 3,
+ XEXP (x, 0), Pmode, XEXP (y, 0), Pmode,
+ size, Pmode);
+#endif
+ emit_cmp_insn (hard_libcall_value (TYPE_MODE (integer_type_node)),
+ const0_rtx, comparison, NULL_RTX,
+ TYPE_MODE (integer_type_node), 0, 0);
+ }
+ return;
+ }
+
+ /* Handle some compares against zero. */
+
+ if (y == CONST0_RTX (mode)
+ && tst_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+ {
+ int icode = (int) tst_optab->handlers[(int) mode].insn_code;
+
+ emit_queue ();
+ x = protect_from_queue (x, 0);
+ y = protect_from_queue (y, 0);
+
+ /* Now, if insn does accept these operands, put them into pseudos. */
+ if (! (*insn_operand_predicate[icode][0])
+ (x, insn_operand_mode[icode][0]))
+ x = copy_to_mode_reg (insn_operand_mode[icode][0], x);
+
+ emit_insn (GEN_FCN (icode) (x));
+ return;
+ }
+
+ /* Handle compares for which there is a directly suitable insn. */
+
+ if (cmp_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+ {
+ int icode = (int) cmp_optab->handlers[(int) mode].insn_code;
+
+ emit_queue ();
+ x = protect_from_queue (x, 0);
+ y = protect_from_queue (y, 0);
+
+ /* Now, if insn doesn't accept these operands, put them into pseudos. */
+ if (! (*insn_operand_predicate[icode][0])
+ (x, insn_operand_mode[icode][0]))
+ x = copy_to_mode_reg (insn_operand_mode[icode][0], x);
+
+ if (! (*insn_operand_predicate[icode][1])
+ (y, insn_operand_mode[icode][1]))
+ y = copy_to_mode_reg (insn_operand_mode[icode][1], y);
+
+ emit_insn (GEN_FCN (icode) (x, y));
+ return;
+ }
+
+ /* Try widening if we can find a direct insn that way. */
+
+ if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+ {
+ for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+ wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+ {
+ if (cmp_optab->handlers[(int) wider_mode].insn_code
+ != CODE_FOR_nothing)
+ {
+ x = protect_from_queue (x, 0);
+ y = protect_from_queue (y, 0);
+ x = convert_modes (wider_mode, mode, x, unsignedp);
+ y = convert_modes (wider_mode, mode, y, unsignedp);
+ emit_cmp_insn (x, y, comparison, NULL_RTX,
+ wider_mode, unsignedp, align);
+ return;
+ }
+ }
+ }
+
+ /* Handle a lib call just for the mode we are using. */
+
+ if (cmp_optab->handlers[(int) mode].libfunc
+ && class != MODE_FLOAT)
+ {
+ rtx libfunc = cmp_optab->handlers[(int) mode].libfunc;
+ /* If we want unsigned, and this mode has a distinct unsigned
+ comparison routine, use that. */
+ if (unsignedp && ucmp_optab->handlers[(int) mode].libfunc)
+ libfunc = ucmp_optab->handlers[(int) mode].libfunc;
+
+ emit_library_call (libfunc, 1,
+ word_mode, 2, x, mode, y, mode);
+
+ /* Integer comparison returns a result that must be compared against 1,
+ so that even if we do an unsigned compare afterward,
+ there is still a value that can represent the result "less than". */
+
+ emit_cmp_insn (hard_libcall_value (word_mode), const1_rtx,
+ comparison, NULL_RTX, word_mode, unsignedp, 0);
+ return;
+ }
+
+ if (class == MODE_FLOAT)
+ emit_float_lib_cmp (x, y, comparison);
+
+ else
+ abort ();
+}
+
+/* Nonzero if a compare of mode MODE can be done straightforwardly
+ (without splitting it into pieces). */
+
+int
+can_compare_p (mode)
+ enum machine_mode mode;
+{
+ do
+ {
+ if (cmp_optab->handlers[(int)mode].insn_code != CODE_FOR_nothing)
+ return 1;
+ mode = GET_MODE_WIDER_MODE (mode);
+ } while (mode != VOIDmode);
+
+ return 0;
+}
+
+/* Emit a library call comparison between floating point X and Y.
+ COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.). */
+
+void
+emit_float_lib_cmp (x, y, comparison)
+ rtx x, y;
+ enum rtx_code comparison;
+{
+ enum machine_mode mode = GET_MODE (x);
+ rtx libfunc = 0;
+
+ if (mode == SFmode)
+ switch (comparison)
+ {
+ case EQ:
+ libfunc = eqsf2_libfunc;
+ break;
+
+ case NE:
+ libfunc = nesf2_libfunc;
+ break;
+
+ case GT:
+ libfunc = gtsf2_libfunc;
+ break;
+
+ case GE:
+ libfunc = gesf2_libfunc;
+ break;
+
+ case LT:
+ libfunc = ltsf2_libfunc;
+ break;
+
+ case LE:
+ libfunc = lesf2_libfunc;
+ break;
+ }
+ else if (mode == DFmode)
+ switch (comparison)
+ {
+ case EQ:
+ libfunc = eqdf2_libfunc;
+ break;
+
+ case NE:
+ libfunc = nedf2_libfunc;
+ break;
+
+ case GT:
+ libfunc = gtdf2_libfunc;
+ break;
+
+ case GE:
+ libfunc = gedf2_libfunc;
+ break;
+
+ case LT:
+ libfunc = ltdf2_libfunc;
+ break;
+
+ case LE:
+ libfunc = ledf2_libfunc;
+ break;
+ }
+ else if (mode == XFmode)
+ switch (comparison)
+ {
+ case EQ:
+ libfunc = eqxf2_libfunc;
+ break;
+
+ case NE:
+ libfunc = nexf2_libfunc;
+ break;
+
+ case GT:
+ libfunc = gtxf2_libfunc;
+ break;
+
+ case GE:
+ libfunc = gexf2_libfunc;
+ break;
+
+ case LT:
+ libfunc = ltxf2_libfunc;
+ break;
+
+ case LE:
+ libfunc = lexf2_libfunc;
+ break;
+ }
+ else if (mode == TFmode)
+ switch (comparison)
+ {
+ case EQ:
+ libfunc = eqtf2_libfunc;
+ break;
+
+ case NE:
+ libfunc = netf2_libfunc;
+ break;
+
+ case GT:
+ libfunc = gttf2_libfunc;
+ break;
+
+ case GE:
+ libfunc = getf2_libfunc;
+ break;
+
+ case LT:
+ libfunc = lttf2_libfunc;
+ break;
+
+ case LE:
+ libfunc = letf2_libfunc;
+ break;
+ }
+ else
+ {
+ enum machine_mode wider_mode;
+
+ for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+ wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+ {
+ if ((cmp_optab->handlers[(int) wider_mode].insn_code
+ != CODE_FOR_nothing)
+ || (cmp_optab->handlers[(int) wider_mode].libfunc != 0))
+ {
+ x = protect_from_queue (x, 0);
+ y = protect_from_queue (y, 0);
+ x = convert_to_mode (wider_mode, x, 0);
+ y = convert_to_mode (wider_mode, y, 0);
+ emit_float_lib_cmp (x, y, comparison);
+ return;
+ }
+ }
+ abort ();
+ }
+
+ if (libfunc == 0)
+ abort ();
+
+ emit_library_call (libfunc, 1,
+ word_mode, 2, x, mode, y, mode);
+
+ emit_cmp_insn (hard_libcall_value (word_mode), const0_rtx, comparison,
+ NULL_RTX, word_mode, 0, 0);
+}
+
+/* Generate code to indirectly jump to a location given in the rtx LOC. */
+
+void
+emit_indirect_jump (loc)
+ rtx loc;
+{
+ if (! ((*insn_operand_predicate[(int)CODE_FOR_indirect_jump][0])
+ (loc, Pmode)))
+ loc = copy_to_mode_reg (Pmode, loc);
+
+ emit_jump_insn (gen_indirect_jump (loc));
+ emit_barrier ();
+}
+
+/* These three functions generate an insn body and return it
+ rather than emitting the insn.
+
+ They do not protect from queued increments,
+ because they may be used 1) in protect_from_queue itself
+ and 2) in other passes where there is no queue. */
+
+/* Generate and return an insn body to add Y to X. */
+
+rtx
+gen_add2_insn (x, y)
+ rtx x, y;
+{
+ int icode = (int) add_optab->handlers[(int) GET_MODE (x)].insn_code;
+
+ if (! (*insn_operand_predicate[icode][0]) (x, insn_operand_mode[icode][0])
+ || ! (*insn_operand_predicate[icode][1]) (x, insn_operand_mode[icode][1])
+ || ! (*insn_operand_predicate[icode][2]) (y, insn_operand_mode[icode][2]))
+ abort ();
+
+ return (GEN_FCN (icode) (x, x, y));
+}
+
+int
+have_add2_insn (mode)
+ enum machine_mode mode;
+{
+ return add_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing;
+}
+
+/* Generate and return an insn body to subtract Y from X. */
+
+rtx
+gen_sub2_insn (x, y)
+ rtx x, y;
+{
+ int icode = (int) sub_optab->handlers[(int) GET_MODE (x)].insn_code;
+
+ if (! (*insn_operand_predicate[icode][0]) (x, insn_operand_mode[icode][0])
+ || ! (*insn_operand_predicate[icode][1]) (x, insn_operand_mode[icode][1])
+ || ! (*insn_operand_predicate[icode][2]) (y, insn_operand_mode[icode][2]))
+ abort ();
+
+ return (GEN_FCN (icode) (x, x, y));
+}
+
+int
+have_sub2_insn (mode)
+ enum machine_mode mode;
+{
+ return sub_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing;
+}
+
+/* Generate the body of an instruction to copy Y into X.
+ It may be a SEQUENCE, if one insn isn't enough. */
+
+rtx
+gen_move_insn (x, y)
+ rtx x, y;
+{
+ register enum machine_mode mode = GET_MODE (x);
+ enum insn_code insn_code;
+ rtx seq;
+
+ if (mode == VOIDmode)
+ mode = GET_MODE (y);
+
+ insn_code = mov_optab->handlers[(int) mode].insn_code;
+
+ /* Handle MODE_CC modes: If we don't have a special move insn for this mode,
+ find a mode to do it in. If we have a movcc, use it. Otherwise,
+ find the MODE_INT mode of the same width. */
+
+ if (GET_MODE_CLASS (mode) == MODE_CC && insn_code == CODE_FOR_nothing)
+ {
+ enum machine_mode tmode = VOIDmode;
+ rtx x1 = x, y1 = y;
+
+ if (mode != CCmode
+ && mov_optab->handlers[(int) CCmode].insn_code != CODE_FOR_nothing)
+ tmode = CCmode;
+ else
+ for (tmode = QImode; tmode != VOIDmode;
+ tmode = GET_MODE_WIDER_MODE (tmode))
+ if (GET_MODE_SIZE (tmode) == GET_MODE_SIZE (mode))
+ break;
+
+ if (tmode == VOIDmode)
+ abort ();
+
+ /* Get X and Y in TMODE. We can't use gen_lowpart here because it
+ may call change_address which is not appropriate if we were
+ called when a reload was in progress. We don't have to worry
+ about changing the address since the size in bytes is supposed to
+ be the same. Copy the MEM to change the mode and move any
+ substitutions from the old MEM to the new one. */
+
+ if (reload_in_progress)
+ {
+ x = gen_lowpart_common (tmode, x1);
+ if (x == 0 && GET_CODE (x1) == MEM)
+ {
+ x = gen_rtx (MEM, tmode, XEXP (x1, 0));
+ RTX_UNCHANGING_P (x) = RTX_UNCHANGING_P (x1);
+ MEM_IN_STRUCT_P (x) = MEM_IN_STRUCT_P (x1);
+ MEM_VOLATILE_P (x) = MEM_VOLATILE_P (x1);
+ copy_replacements (x1, x);
+ }
+
+ y = gen_lowpart_common (tmode, y1);
+ if (y == 0 && GET_CODE (y1) == MEM)
+ {
+ y = gen_rtx (MEM, tmode, XEXP (y1, 0));
+ RTX_UNCHANGING_P (y) = RTX_UNCHANGING_P (y1);
+ MEM_IN_STRUCT_P (y) = MEM_IN_STRUCT_P (y1);
+ MEM_VOLATILE_P (y) = MEM_VOLATILE_P (y1);
+ copy_replacements (y1, y);
+ }
+ }
+ else
+ {
+ x = gen_lowpart (tmode, x);
+ y = gen_lowpart (tmode, y);
+ }
+
+ insn_code = mov_optab->handlers[(int) tmode].insn_code;
+ return (GEN_FCN (insn_code) (x, y));
+ }
+
+ start_sequence ();
+ emit_move_insn_1 (x, y);
+ seq = gen_sequence ();
+ end_sequence ();
+ return seq;
+}
+
+/* Return the insn code used to extend FROM_MODE to TO_MODE.
+ UNSIGNEDP specifies zero-extension instead of sign-extension. If
+ no such operation exists, CODE_FOR_nothing will be returned. */
+
+enum insn_code
+can_extend_p (to_mode, from_mode, unsignedp)
+ enum machine_mode to_mode, from_mode;
+ int unsignedp;
+{
+ return extendtab[(int) to_mode][(int) from_mode][unsignedp];
+}
+
+/* Generate the body of an insn to extend Y (with mode MFROM)
+ into X (with mode MTO). Do zero-extension if UNSIGNEDP is nonzero. */
+
+rtx
+gen_extend_insn (x, y, mto, mfrom, unsignedp)
+ rtx x, y;
+ enum machine_mode mto, mfrom;
+ int unsignedp;
+{
+ return (GEN_FCN (extendtab[(int) mto][(int) mfrom][unsignedp]) (x, y));
+}
+
+/* can_fix_p and can_float_p say whether the target machine
+ can directly convert a given fixed point type to
+ a given floating point type, or vice versa.
+ The returned value is the CODE_FOR_... value to use,
+ or CODE_FOR_nothing if these modes cannot be directly converted.
+
+ *TRUNCP_PTR is set to 1 if it is necessary to output
+ an explicit FTRUNC insn before the fix insn; otherwise 0. */
+
+static enum insn_code
+can_fix_p (fixmode, fltmode, unsignedp, truncp_ptr)
+ enum machine_mode fltmode, fixmode;
+ int unsignedp;
+ int *truncp_ptr;
+{
+ *truncp_ptr = 0;
+ if (fixtrunctab[(int) fltmode][(int) fixmode][unsignedp] != CODE_FOR_nothing)
+ return fixtrunctab[(int) fltmode][(int) fixmode][unsignedp];
+
+ if (ftrunc_optab->handlers[(int) fltmode].insn_code != CODE_FOR_nothing)
+ {
+ *truncp_ptr = 1;
+ return fixtab[(int) fltmode][(int) fixmode][unsignedp];
+ }
+ return CODE_FOR_nothing;
+}
+
+static enum insn_code
+can_float_p (fltmode, fixmode, unsignedp)
+ enum machine_mode fixmode, fltmode;
+ int unsignedp;
+{
+ return floattab[(int) fltmode][(int) fixmode][unsignedp];
+}
+
+/* Generate code to convert FROM to floating point
+ and store in TO. FROM must be fixed point and not VOIDmode.
+ UNSIGNEDP nonzero means regard FROM as unsigned.
+ Normally this is done by correcting the final value
+ if it is negative. */
+
+void
+expand_float (to, from, unsignedp)
+ rtx to, from;
+ int unsignedp;
+{
+ enum insn_code icode;
+ register rtx target = to;
+ enum machine_mode fmode, imode;
+
+ /* Crash now, because we won't be able to decide which mode to use. */
+ if (GET_MODE (from) == VOIDmode)
+ abort ();
+
+ /* Look for an insn to do the conversion. Do it in the specified
+ modes if possible; otherwise convert either input, output or both to
+ wider mode. If the integer mode is wider than the mode of FROM,
+ we can do the conversion signed even if the input is unsigned. */
+
+ for (imode = GET_MODE (from); imode != VOIDmode;
+ imode = GET_MODE_WIDER_MODE (imode))
+ for (fmode = GET_MODE (to); fmode != VOIDmode;
+ fmode = GET_MODE_WIDER_MODE (fmode))
+ {
+ int doing_unsigned = unsignedp;
+
+ icode = can_float_p (fmode, imode, unsignedp);
+ if (icode == CODE_FOR_nothing && imode != GET_MODE (from) && unsignedp)
+ icode = can_float_p (fmode, imode, 0), doing_unsigned = 0;
+
+ if (icode != CODE_FOR_nothing)
+ {
+ to = protect_from_queue (to, 1);
+ from = protect_from_queue (from, 0);
+
+ if (imode != GET_MODE (from))
+ from = convert_to_mode (imode, from, unsignedp);
+
+ if (fmode != GET_MODE (to))
+ target = gen_reg_rtx (fmode);
+
+ emit_unop_insn (icode, target, from,
+ doing_unsigned ? UNSIGNED_FLOAT : FLOAT);
+
+ if (target != to)
+ convert_move (to, target, 0);
+ return;
+ }
+ }
+
+#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
+
+ /* Unsigned integer, and no way to convert directly.
+ Convert as signed, then conditionally adjust the result. */
+ if (unsignedp)
+ {
+ rtx label = gen_label_rtx ();
+ rtx temp;
+ REAL_VALUE_TYPE offset;
+
+ emit_queue ();
+
+ to = protect_from_queue (to, 1);
+ from = protect_from_queue (from, 0);
+
+ if (flag_force_mem)
+ from = force_not_mem (from);
+
+ /* Look for a usable floating mode FMODE wider than the source and at
+ least as wide as the target. Using FMODE will avoid rounding woes
+ with unsigned values greater than the signed maximum value. */
+
+ for (fmode = GET_MODE (to); fmode != VOIDmode;
+ fmode = GET_MODE_WIDER_MODE (fmode))
+ if (GET_MODE_BITSIZE (GET_MODE (from)) < GET_MODE_BITSIZE (fmode)
+ && can_float_p (fmode, GET_MODE (from), 0) != CODE_FOR_nothing)
+ break;
+
+ if (fmode == VOIDmode)
+ {
+ /* There is no such mode. Pretend the target is wide enough. */
+ fmode = GET_MODE (to);
+
+ /* Avoid double-rounding when TO is narrower than FROM. */
+ if ((significand_size (fmode) + 1)
+ < GET_MODE_BITSIZE (GET_MODE (from)))
+ {
+ rtx temp1;
+ rtx neglabel = gen_label_rtx ();
+
+ /* Don't use TARGET if it isn't a register, is a hard register,
+ or is the wrong mode. */
+ if (GET_CODE (target) != REG
+ || REGNO (target) < FIRST_PSEUDO_REGISTER
+ || GET_MODE (target) != fmode)
+ target = gen_reg_rtx (fmode);
+
+ imode = GET_MODE (from);
+ do_pending_stack_adjust ();
+
+ /* Test whether the sign bit is set. */
+ emit_cmp_insn (from, const0_rtx, GE, NULL_RTX, imode, 0, 0);
+ emit_jump_insn (gen_blt (neglabel));
+
+ /* The sign bit is not set. Convert as signed. */
+ expand_float (target, from, 0);
+ emit_jump_insn (gen_jump (label));
+
+ /* The sign bit is set.
+ Convert to a usable (positive signed) value by shifting right
+ one bit, while remembering if a nonzero bit was shifted
+ out; i.e., compute (from & 1) | (from >> 1). */
+
+ emit_label (neglabel);
+ temp = expand_binop (imode, and_optab, from, const1_rtx,
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ temp1 = expand_shift (RSHIFT_EXPR, imode, from, integer_one_node,
+ NULL_RTX, 1);
+ temp = expand_binop (imode, ior_optab, temp, temp1, temp, 1,
+ OPTAB_LIB_WIDEN);
+ expand_float (target, temp, 0);
+
+ /* Multiply by 2 to undo the shift above. */
+ target = expand_binop (fmode, add_optab, target, target,
+ target, 0, OPTAB_LIB_WIDEN);
+ do_pending_stack_adjust ();
+ emit_label (label);
+ goto done;
+ }
+ }
+
+ /* If we are about to do some arithmetic to correct for an
+ unsigned operand, do it in a pseudo-register. */
+
+ if (GET_MODE (to) != fmode
+ || GET_CODE (to) != REG || REGNO (to) < FIRST_PSEUDO_REGISTER)
+ target = gen_reg_rtx (fmode);
+
+ /* Convert as signed integer to floating. */
+ expand_float (target, from, 0);
+
+ /* If FROM is negative (and therefore TO is negative),
+ correct its value by 2**bitwidth. */
+
+ do_pending_stack_adjust ();
+ emit_cmp_insn (from, const0_rtx, GE, NULL_RTX, GET_MODE (from), 0, 0);
+ emit_jump_insn (gen_bge (label));
+
+ /* On SCO 3.2.1, ldexp rejects values outside [0.5, 1).
+ Rather than setting up a dconst_dot_5, let's hope SCO
+ fixes the bug. */
+ offset = REAL_VALUE_LDEXP (dconst1, GET_MODE_BITSIZE (GET_MODE (from)));
+ temp = expand_binop (fmode, add_optab, target,
+ CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode),
+ target, 0, OPTAB_LIB_WIDEN);
+ if (temp != target)
+ emit_move_insn (target, temp);
+
+ do_pending_stack_adjust ();
+ emit_label (label);
+ goto done;
+ }
+#endif
+
+ /* No hardware instruction available; call a library rotine to convert from
+ SImode, DImode, or TImode into SFmode, DFmode, XFmode, or TFmode. */
+ {
+ rtx libfcn;
+ rtx insns;
+ rtx value;
+
+ to = protect_from_queue (to, 1);
+ from = protect_from_queue (from, 0);
+
+ if (GET_MODE_SIZE (GET_MODE (from)) < GET_MODE_SIZE (SImode))
+ from = convert_to_mode (SImode, from, unsignedp);
+
+ if (flag_force_mem)
+ from = force_not_mem (from);
+
+ if (GET_MODE (to) == SFmode)
+ {
+ if (GET_MODE (from) == SImode)
+ libfcn = floatsisf_libfunc;
+ else if (GET_MODE (from) == DImode)
+ libfcn = floatdisf_libfunc;
+ else if (GET_MODE (from) == TImode)
+ libfcn = floattisf_libfunc;
+ else
+ abort ();
+ }
+ else if (GET_MODE (to) == DFmode)
+ {
+ if (GET_MODE (from) == SImode)
+ libfcn = floatsidf_libfunc;
+ else if (GET_MODE (from) == DImode)
+ libfcn = floatdidf_libfunc;
+ else if (GET_MODE (from) == TImode)
+ libfcn = floattidf_libfunc;
+ else
+ abort ();
+ }
+ else if (GET_MODE (to) == XFmode)
+ {
+ if (GET_MODE (from) == SImode)
+ libfcn = floatsixf_libfunc;
+ else if (GET_MODE (from) == DImode)
+ libfcn = floatdixf_libfunc;
+ else if (GET_MODE (from) == TImode)
+ libfcn = floattixf_libfunc;
+ else
+ abort ();
+ }
+ else if (GET_MODE (to) == TFmode)
+ {
+ if (GET_MODE (from) == SImode)
+ libfcn = floatsitf_libfunc;
+ else if (GET_MODE (from) == DImode)
+ libfcn = floatditf_libfunc;
+ else if (GET_MODE (from) == TImode)
+ libfcn = floattitf_libfunc;
+ else
+ abort ();
+ }
+ else
+ abort ();
+
+ start_sequence ();
+
+ value = emit_library_call_value (libfcn, NULL_RTX, 1,
+ GET_MODE (to),
+ 1, from, GET_MODE (from));
+ insns = get_insns ();
+ end_sequence ();
+
+ emit_libcall_block (insns, target, value,
+ gen_rtx (FLOAT, GET_MODE (to), from));
+ }
+
+ done:
+
+ /* Copy result to requested destination
+ if we have been computing in a temp location. */
+
+ if (target != to)
+ {
+ if (GET_MODE (target) == GET_MODE (to))
+ emit_move_insn (to, target);
+ else
+ convert_move (to, target, 0);
+ }
+}
+
+/* expand_fix: generate code to convert FROM to fixed point
+ and store in TO. FROM must be floating point. */
+
+static rtx
+ftruncify (x)
+ rtx x;
+{
+ rtx temp = gen_reg_rtx (GET_MODE (x));
+ return expand_unop (GET_MODE (x), ftrunc_optab, x, temp, 0);
+}
+
+void
+expand_fix (to, from, unsignedp)
+ register rtx to, from;
+ int unsignedp;
+{
+ enum insn_code icode;
+ register rtx target = to;
+ enum machine_mode fmode, imode;
+ int must_trunc = 0;
+ rtx libfcn = 0;
+
+ /* We first try to find a pair of modes, one real and one integer, at
+ least as wide as FROM and TO, respectively, in which we can open-code
+ this conversion. If the integer mode is wider than the mode of TO,
+ we can do the conversion either signed or unsigned. */
+
+ for (imode = GET_MODE (to); imode != VOIDmode;
+ imode = GET_MODE_WIDER_MODE (imode))
+ for (fmode = GET_MODE (from); fmode != VOIDmode;
+ fmode = GET_MODE_WIDER_MODE (fmode))
+ {
+ int doing_unsigned = unsignedp;
+
+ icode = can_fix_p (imode, fmode, unsignedp, &must_trunc);
+ if (icode == CODE_FOR_nothing && imode != GET_MODE (to) && unsignedp)
+ icode = can_fix_p (imode, fmode, 0, &must_trunc), doing_unsigned = 0;
+
+ if (icode != CODE_FOR_nothing)
+ {
+ to = protect_from_queue (to, 1);
+ from = protect_from_queue (from, 0);
+
+ if (fmode != GET_MODE (from))
+ from = convert_to_mode (fmode, from, 0);
+
+ if (must_trunc)
+ from = ftruncify (from);
+
+ if (imode != GET_MODE (to))
+ target = gen_reg_rtx (imode);
+
+ emit_unop_insn (icode, target, from,
+ doing_unsigned ? UNSIGNED_FIX : FIX);
+ if (target != to)
+ convert_move (to, target, unsignedp);
+ return;
+ }
+ }
+
+#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
+ /* For an unsigned conversion, there is one more way to do it.
+ If we have a signed conversion, we generate code that compares
+ the real value to the largest representable positive number. If if
+ is smaller, the conversion is done normally. Otherwise, subtract
+ one plus the highest signed number, convert, and add it back.
+
+ We only need to check all real modes, since we know we didn't find
+ anything with a wider integer mode. */
+
+ if (unsignedp && GET_MODE_BITSIZE (GET_MODE (to)) <= HOST_BITS_PER_WIDE_INT)
+ for (fmode = GET_MODE (from); fmode != VOIDmode;
+ fmode = GET_MODE_WIDER_MODE (fmode))
+ /* Make sure we won't lose significant bits doing this. */
+ if (GET_MODE_BITSIZE (fmode) > GET_MODE_BITSIZE (GET_MODE (to))
+ && CODE_FOR_nothing != can_fix_p (GET_MODE (to), fmode, 0,
+ &must_trunc))
+ {
+ int bitsize;
+ REAL_VALUE_TYPE offset;
+ rtx limit, lab1, lab2, insn;
+
+ bitsize = GET_MODE_BITSIZE (GET_MODE (to));
+ offset = REAL_VALUE_LDEXP (dconst1, bitsize - 1);
+ limit = CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode);
+ lab1 = gen_label_rtx ();
+ lab2 = gen_label_rtx ();
+
+ emit_queue ();
+ to = protect_from_queue (to, 1);
+ from = protect_from_queue (from, 0);
+
+ if (flag_force_mem)
+ from = force_not_mem (from);
+
+ if (fmode != GET_MODE (from))
+ from = convert_to_mode (fmode, from, 0);
+
+ /* See if we need to do the subtraction. */
+ do_pending_stack_adjust ();
+ emit_cmp_insn (from, limit, GE, NULL_RTX, GET_MODE (from), 0, 0);
+ emit_jump_insn (gen_bge (lab1));
+
+ /* If not, do the signed "fix" and branch around fixup code. */
+ expand_fix (to, from, 0);
+ emit_jump_insn (gen_jump (lab2));
+ emit_barrier ();
+
+ /* Otherwise, subtract 2**(N-1), convert to signed number,
+ then add 2**(N-1). Do the addition using XOR since this
+ will often generate better code. */
+ emit_label (lab1);
+ target = expand_binop (GET_MODE (from), sub_optab, from, limit,
+ NULL_RTX, 0, OPTAB_LIB_WIDEN);
+ expand_fix (to, target, 0);
+ target = expand_binop (GET_MODE (to), xor_optab, to,
+ GEN_INT ((HOST_WIDE_INT) 1 << (bitsize - 1)),
+ to, 1, OPTAB_LIB_WIDEN);
+
+ if (target != to)
+ emit_move_insn (to, target);
+
+ emit_label (lab2);
+
+ /* Make a place for a REG_NOTE and add it. */
+ insn = emit_move_insn (to, to);
+ REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUAL,
+ gen_rtx (UNSIGNED_FIX, GET_MODE (to),
+ copy_rtx (from)),
+ REG_NOTES (insn));
+
+ return;
+ }
+#endif
+
+ /* We can't do it with an insn, so use a library call. But first ensure
+ that the mode of TO is at least as wide as SImode, since those are the
+ only library calls we know about. */
+
+ if (GET_MODE_SIZE (GET_MODE (to)) < GET_MODE_SIZE (SImode))
+ {
+ target = gen_reg_rtx (SImode);
+
+ expand_fix (target, from, unsignedp);
+ }
+ else if (GET_MODE (from) == SFmode)
+ {
+ if (GET_MODE (to) == SImode)
+ libfcn = unsignedp ? fixunssfsi_libfunc : fixsfsi_libfunc;
+ else if (GET_MODE (to) == DImode)
+ libfcn = unsignedp ? fixunssfdi_libfunc : fixsfdi_libfunc;
+ else if (GET_MODE (to) == TImode)
+ libfcn = unsignedp ? fixunssfti_libfunc : fixsfti_libfunc;
+ else
+ abort ();
+ }
+ else if (GET_MODE (from) == DFmode)
+ {
+ if (GET_MODE (to) == SImode)
+ libfcn = unsignedp ? fixunsdfsi_libfunc : fixdfsi_libfunc;
+ else if (GET_MODE (to) == DImode)
+ libfcn = unsignedp ? fixunsdfdi_libfunc : fixdfdi_libfunc;
+ else if (GET_MODE (to) == TImode)
+ libfcn = unsignedp ? fixunsdfti_libfunc : fixdfti_libfunc;
+ else
+ abort ();
+ }
+ else if (GET_MODE (from) == XFmode)
+ {
+ if (GET_MODE (to) == SImode)
+ libfcn = unsignedp ? fixunsxfsi_libfunc : fixxfsi_libfunc;
+ else if (GET_MODE (to) == DImode)
+ libfcn = unsignedp ? fixunsxfdi_libfunc : fixxfdi_libfunc;
+ else if (GET_MODE (to) == TImode)
+ libfcn = unsignedp ? fixunsxfti_libfunc : fixxfti_libfunc;
+ else
+ abort ();
+ }
+ else if (GET_MODE (from) == TFmode)
+ {
+ if (GET_MODE (to) == SImode)
+ libfcn = unsignedp ? fixunstfsi_libfunc : fixtfsi_libfunc;
+ else if (GET_MODE (to) == DImode)
+ libfcn = unsignedp ? fixunstfdi_libfunc : fixtfdi_libfunc;
+ else if (GET_MODE (to) == TImode)
+ libfcn = unsignedp ? fixunstfti_libfunc : fixtfti_libfunc;
+ else
+ abort ();
+ }
+ else
+ abort ();
+
+ if (libfcn)
+ {
+ rtx insns;
+
+ to = protect_from_queue (to, 1);
+ from = protect_from_queue (from, 0);
+
+ if (flag_force_mem)
+ from = force_not_mem (from);
+
+ start_sequence ();
+
+ emit_library_call (libfcn, 1, GET_MODE (to), 1, from, GET_MODE (from));
+ insns = get_insns ();
+ end_sequence ();
+
+ emit_libcall_block (insns, target, hard_libcall_value (GET_MODE (to)),
+ gen_rtx (unsignedp ? FIX : UNSIGNED_FIX,
+ GET_MODE (to), from));
+ }
+
+ if (GET_MODE (to) == GET_MODE (target))
+ emit_move_insn (to, target);
+ else
+ convert_move (to, target, 0);
+}
+
+static optab
+init_optab (code)
+ enum rtx_code code;
+{
+ int i;
+ optab op = (optab) xmalloc (sizeof (struct optab));
+ op->code = code;
+ for (i = 0; i < NUM_MACHINE_MODES; i++)
+ {
+ op->handlers[i].insn_code = CODE_FOR_nothing;
+ op->handlers[i].libfunc = 0;
+ }
+
+ if (code != UNKNOWN)
+ code_to_optab[(int) code] = op;
+
+ return op;
+}
+
+/* Initialize the libfunc fields of an entire group of entries in some
+ optab. Each entry is set equal to a string consisting of a leading
+ pair of underscores followed by a generic operation name followed by
+ a mode name (downshifted to lower case) followed by a single character
+ representing the number of operands for the given operation (which is
+ usually one of the characters '2', '3', or '4').
+
+ OPTABLE is the table in which libfunc fields are to be initialized.
+ FIRST_MODE is the first machine mode index in the given optab to
+ initialize.
+ LAST_MODE is the last machine mode index in the given optab to
+ initialize.
+ OPNAME is the generic (string) name of the operation.
+ SUFFIX is the character which specifies the number of operands for
+ the given generic operation.
+*/
+
+static void
+init_libfuncs (optable, first_mode, last_mode, opname, suffix)
+ register optab optable;
+ register int first_mode;
+ register int last_mode;
+ register char *opname;
+ register char suffix;
+{
+ register int mode;
+ register unsigned opname_len = strlen (opname);
+
+ for (mode = first_mode; (int) mode <= (int) last_mode;
+ mode = (enum machine_mode) ((int) mode + 1))
+ {
+ register char *mname = mode_name[(int) mode];
+ register unsigned mname_len = strlen (mname);
+ register char *libfunc_name
+ = (char *) xmalloc (2 + opname_len + mname_len + 1 + 1);
+ register char *p;
+ register char *q;
+
+ p = libfunc_name;
+ *p++ = '_';
+ *p++ = '_';
+ for (q = opname; *q; )
+ *p++ = *q++;
+ for (q = mname; *q; q++)
+ *p++ = tolower (*q);
+ *p++ = suffix;
+ *p++ = '\0';
+ optable->handlers[(int) mode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, libfunc_name);
+ }
+}
+
+/* Initialize the libfunc fields of an entire group of entries in some
+ optab which correspond to all integer mode operations. The parameters
+ have the same meaning as similarly named ones for the `init_libfuncs'
+ routine. (See above). */
+
+static void
+init_integral_libfuncs (optable, opname, suffix)
+ register optab optable;
+ register char *opname;
+ register char suffix;
+{
+ init_libfuncs (optable, SImode, TImode, opname, suffix);
+}
+
+/* Initialize the libfunc fields of an entire group of entries in some
+ optab which correspond to all real mode operations. The parameters
+ have the same meaning as similarly named ones for the `init_libfuncs'
+ routine. (See above). */
+
+static void
+init_floating_libfuncs (optable, opname, suffix)
+ register optab optable;
+ register char *opname;
+ register char suffix;
+{
+ init_libfuncs (optable, SFmode, TFmode, opname, suffix);
+}
+
+/* Initialize the libfunc fields of an entire group of entries in some
+ optab which correspond to all complex floating modes. The parameters
+ have the same meaning as similarly named ones for the `init_libfuncs'
+ routine. (See above). */
+
+static void
+init_complex_libfuncs (optable, opname, suffix)
+ register optab optable;
+ register char *opname;
+ register char suffix;
+{
+ init_libfuncs (optable, SCmode, TCmode, opname, suffix);
+}
+
+/* Call this once to initialize the contents of the optabs
+ appropriately for the current target machine. */
+
+void
+init_optabs ()
+{
+ int i, j;
+ enum insn_code *p;
+
+ /* Start by initializing all tables to contain CODE_FOR_nothing. */
+
+ for (p = fixtab[0][0];
+ p < fixtab[0][0] + sizeof fixtab / sizeof (fixtab[0][0][0]);
+ p++)
+ *p = CODE_FOR_nothing;
+
+ for (p = fixtrunctab[0][0];
+ p < fixtrunctab[0][0] + sizeof fixtrunctab / sizeof (fixtrunctab[0][0][0]);
+ p++)
+ *p = CODE_FOR_nothing;
+
+ for (p = floattab[0][0];
+ p < floattab[0][0] + sizeof floattab / sizeof (floattab[0][0][0]);
+ p++)
+ *p = CODE_FOR_nothing;
+
+ for (p = extendtab[0][0];
+ p < extendtab[0][0] + sizeof extendtab / sizeof extendtab[0][0][0];
+ p++)
+ *p = CODE_FOR_nothing;
+
+ for (i = 0; i < NUM_RTX_CODE; i++)
+ setcc_gen_code[i] = CODE_FOR_nothing;
+
+ add_optab = init_optab (PLUS);
+ sub_optab = init_optab (MINUS);
+ smul_optab = init_optab (MULT);
+ smul_highpart_optab = init_optab (UNKNOWN);
+ umul_highpart_optab = init_optab (UNKNOWN);
+ smul_widen_optab = init_optab (UNKNOWN);
+ umul_widen_optab = init_optab (UNKNOWN);
+ sdiv_optab = init_optab (DIV);
+ sdivmod_optab = init_optab (UNKNOWN);
+ udiv_optab = init_optab (UDIV);
+ udivmod_optab = init_optab (UNKNOWN);
+ smod_optab = init_optab (MOD);
+ umod_optab = init_optab (UMOD);
+ flodiv_optab = init_optab (DIV);
+ ftrunc_optab = init_optab (UNKNOWN);
+ and_optab = init_optab (AND);
+ ior_optab = init_optab (IOR);
+ xor_optab = init_optab (XOR);
+ ashl_optab = init_optab (ASHIFT);
+ ashr_optab = init_optab (ASHIFTRT);
+ lshr_optab = init_optab (LSHIFTRT);
+ rotl_optab = init_optab (ROTATE);
+ rotr_optab = init_optab (ROTATERT);
+ smin_optab = init_optab (SMIN);
+ smax_optab = init_optab (SMAX);
+ umin_optab = init_optab (UMIN);
+ umax_optab = init_optab (UMAX);
+ mov_optab = init_optab (UNKNOWN);
+ movstrict_optab = init_optab (UNKNOWN);
+ cmp_optab = init_optab (UNKNOWN);
+ ucmp_optab = init_optab (UNKNOWN);
+ tst_optab = init_optab (UNKNOWN);
+ neg_optab = init_optab (NEG);
+ abs_optab = init_optab (ABS);
+ one_cmpl_optab = init_optab (NOT);
+ ffs_optab = init_optab (FFS);
+ sqrt_optab = init_optab (SQRT);
+ sin_optab = init_optab (UNKNOWN);
+ cos_optab = init_optab (UNKNOWN);
+ strlen_optab = init_optab (UNKNOWN);
+
+ for (i = 0; i < NUM_MACHINE_MODES; i++)
+ {
+ movstr_optab[i] = CODE_FOR_nothing;
+
+#ifdef HAVE_SECONDARY_RELOADS
+ reload_in_optab[i] = reload_out_optab[i] = CODE_FOR_nothing;
+#endif
+ }
+
+ /* Fill in the optabs with the insns we support. */
+ init_all_optabs ();
+
+#ifdef FIXUNS_TRUNC_LIKE_FIX_TRUNC
+ /* This flag says the same insns that convert to a signed fixnum
+ also convert validly to an unsigned one. */
+ for (i = 0; i < NUM_MACHINE_MODES; i++)
+ for (j = 0; j < NUM_MACHINE_MODES; j++)
+ fixtrunctab[i][j][1] = fixtrunctab[i][j][0];
+#endif
+
+#ifdef EXTRA_CC_MODES
+ init_mov_optab ();
+#endif
+
+ /* Initialize the optabs with the names of the library functions. */
+ init_integral_libfuncs (add_optab, "add", '3');
+ init_floating_libfuncs (add_optab, "add", '3');
+ init_integral_libfuncs (sub_optab, "sub", '3');
+ init_floating_libfuncs (sub_optab, "sub", '3');
+ init_integral_libfuncs (smul_optab, "mul", '3');
+ init_floating_libfuncs (smul_optab, "mul", '3');
+ init_integral_libfuncs (sdiv_optab, "div", '3');
+ init_integral_libfuncs (udiv_optab, "udiv", '3');
+ init_integral_libfuncs (sdivmod_optab, "divmod", '4');
+ init_integral_libfuncs (udivmod_optab, "udivmod", '4');
+ init_integral_libfuncs (smod_optab, "mod", '3');
+ init_integral_libfuncs (umod_optab, "umod", '3');
+ init_floating_libfuncs (flodiv_optab, "div", '3');
+ init_floating_libfuncs (ftrunc_optab, "ftrunc", '2');
+ init_integral_libfuncs (and_optab, "and", '3');
+ init_integral_libfuncs (ior_optab, "ior", '3');
+ init_integral_libfuncs (xor_optab, "xor", '3');
+ init_integral_libfuncs (ashl_optab, "ashl", '3');
+ init_integral_libfuncs (ashr_optab, "ashr", '3');
+ init_integral_libfuncs (lshr_optab, "lshr", '3');
+ init_integral_libfuncs (rotl_optab, "rotl", '3');
+ init_integral_libfuncs (rotr_optab, "rotr", '3');
+ init_integral_libfuncs (smin_optab, "min", '3');
+ init_floating_libfuncs (smin_optab, "min", '3');
+ init_integral_libfuncs (smax_optab, "max", '3');
+ init_floating_libfuncs (smax_optab, "max", '3');
+ init_integral_libfuncs (umin_optab, "umin", '3');
+ init_integral_libfuncs (umax_optab, "umax", '3');
+ init_integral_libfuncs (neg_optab, "neg", '2');
+ init_floating_libfuncs (neg_optab, "neg", '2');
+ init_integral_libfuncs (one_cmpl_optab, "one_cmpl", '2');
+ init_integral_libfuncs (ffs_optab, "ffs", '2');
+
+ /* Comparison libcalls for integers MUST come in pairs, signed/unsigned. */
+ init_integral_libfuncs (cmp_optab, "cmp", '2');
+ init_integral_libfuncs (ucmp_optab, "ucmp", '2');
+ init_floating_libfuncs (cmp_optab, "cmp", '2');
+
+#ifdef MULSI3_LIBCALL
+ smul_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, MULSI3_LIBCALL);
+#endif
+#ifdef MULDI3_LIBCALL
+ smul_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, MULDI3_LIBCALL);
+#endif
+#ifdef MULTI3_LIBCALL
+ smul_optab->handlers[(int) TImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, MULTI3_LIBCALL);
+#endif
+
+#ifdef DIVSI3_LIBCALL
+ sdiv_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, DIVSI3_LIBCALL);
+#endif
+#ifdef DIVDI3_LIBCALL
+ sdiv_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, DIVDI3_LIBCALL);
+#endif
+#ifdef DIVTI3_LIBCALL
+ sdiv_optab->handlers[(int) TImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, DIVTI3_LIBCALL);
+#endif
+
+#ifdef UDIVSI3_LIBCALL
+ udiv_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, UDIVSI3_LIBCALL);
+#endif
+#ifdef UDIVDI3_LIBCALL
+ udiv_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, UDIVDI3_LIBCALL);
+#endif
+#ifdef UDIVTI3_LIBCALL
+ udiv_optab->handlers[(int) TImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, UDIVTI3_LIBCALL);
+#endif
+
+
+#ifdef MODSI3_LIBCALL
+ smod_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, MODSI3_LIBCALL);
+#endif
+#ifdef MODDI3_LIBCALL
+ smod_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, MODDI3_LIBCALL);
+#endif
+#ifdef MODTI3_LIBCALL
+ smod_optab->handlers[(int) TImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, MODTI3_LIBCALL);
+#endif
+
+
+#ifdef UMODSI3_LIBCALL
+ umod_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, UMODSI3_LIBCALL);
+#endif
+#ifdef UMODDI3_LIBCALL
+ umod_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, UMODDI3_LIBCALL);
+#endif
+#ifdef UMODTI3_LIBCALL
+ umod_optab->handlers[(int) TImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, UMODTI3_LIBCALL);
+#endif
+
+/* Define library calls for quad FP instructions */
+#ifdef ADDTF3_LIBCALL
+ add_optab->handlers[(int) TFmode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, ADDTF3_LIBCALL);
+#endif
+#ifdef SUBTF3_LIBCALL
+ sub_optab->handlers[(int) TFmode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, SUBTF3_LIBCALL);
+#endif
+#ifdef MULTF3_LIBCALL
+ smul_optab->handlers[(int) TFmode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, MULTF3_LIBCALL);
+#endif
+#ifdef DIVTF3_LIBCALL
+ flodiv_optab->handlers[(int) TFmode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, DIVTF3_LIBCALL);
+#endif
+#ifdef SQRTTF2_LIBCALL
+ sqrt_optab->handlers[(int) TFmode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, SQRTTF2_LIBCALL);
+#endif
+
+ /* Use cabs for DC complex abs, since systems generally have cabs.
+ Don't define any libcall for SCmode, so that cabs will be used. */
+ abs_optab->handlers[(int) DCmode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "cabs");
+
+ /* The ffs function operates on `int'. */
+#ifndef INT_TYPE_SIZE
+#define INT_TYPE_SIZE BITS_PER_WORD
+#endif
+ ffs_optab->handlers[(int) mode_for_size (INT_TYPE_SIZE, MODE_INT, 0)] .libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "ffs");
+
+ extendsfdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__extendsfdf2");
+ extendsfxf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__extendsfxf2");
+ extendsftf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__extendsftf2");
+ extenddfxf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__extenddfxf2");
+ extenddftf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__extenddftf2");
+
+ truncdfsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__truncdfsf2");
+ truncxfsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__truncxfsf2");
+ trunctfsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__trunctfsf2");
+ truncxfdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__truncxfdf2");
+ trunctfdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__trunctfdf2");
+
+ memcpy_libfunc = gen_rtx (SYMBOL_REF, Pmode, "memcpy");
+ bcopy_libfunc = gen_rtx (SYMBOL_REF, Pmode, "bcopy");
+ memcmp_libfunc = gen_rtx (SYMBOL_REF, Pmode, "memcmp");
+ bcmp_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gcc_bcmp");
+ memset_libfunc = gen_rtx (SYMBOL_REF, Pmode, "memset");
+ bzero_libfunc = gen_rtx (SYMBOL_REF, Pmode, "bzero");
+
+ eqsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__eqsf2");
+ nesf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__nesf2");
+ gtsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gtsf2");
+ gesf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gesf2");
+ ltsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__ltsf2");
+ lesf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__lesf2");
+
+ eqdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__eqdf2");
+ nedf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__nedf2");
+ gtdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gtdf2");
+ gedf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gedf2");
+ ltdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__ltdf2");
+ ledf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__ledf2");
+
+ eqxf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__eqxf2");
+ nexf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__nexf2");
+ gtxf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gtxf2");
+ gexf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gexf2");
+ ltxf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__ltxf2");
+ lexf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__lexf2");
+
+ eqtf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__eqtf2");
+ netf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__netf2");
+ gttf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gttf2");
+ getf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__getf2");
+ lttf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__lttf2");
+ letf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__letf2");
+
+/* Define library calls for quad FP instructions */
+#ifdef EQTF2_LIBCALL
+ eqtf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, EQTF2_LIBCALL);
+#endif
+#ifdef NETF2_LIBCALL
+ netf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, NETF2_LIBCALL);
+#endif
+#ifdef GTTF2_LIBCALL
+ gttf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, GTTF2_LIBCALL);
+#endif
+#ifdef GETF2_LIBCALL
+ getf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, GETF2_LIBCALL);
+#endif
+#ifdef LTTF2_LIBCALL
+ lttf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, LTTF2_LIBCALL);
+#endif
+#ifdef LETF2_LIBCALL
+ letf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, LETF2_LIBCALL);
+#endif
+
+ floatsisf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floatsisf");
+ floatdisf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floatdisf");
+ floattisf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floattisf");
+
+ floatsidf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floatsidf");
+ floatdidf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floatdidf");
+ floattidf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floattidf");
+
+ floatsixf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floatsixf");
+ floatdixf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floatdixf");
+ floattixf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floattixf");
+
+ floatsitf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floatsitf");
+ floatditf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floatditf");
+ floattitf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floattitf");
+
+ fixsfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixsfsi");
+ fixsfdi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixsfdi");
+ fixsfti_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixsfti");
+
+ fixdfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixdfsi");
+ fixdfdi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixdfdi");
+ fixdfti_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixdfti");
+
+ fixxfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixxfsi");
+ fixxfdi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixxfdi");
+ fixxfti_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixxfti");
+
+ fixtfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixtfsi");
+ fixtfdi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixtfdi");
+ fixtfti_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixtfti");
+
+ fixunssfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunssfsi");
+ fixunssfdi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunssfdi");
+ fixunssfti_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunssfti");
+
+ fixunsdfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunsdfsi");
+ fixunsdfdi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunsdfdi");
+ fixunsdfti_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunsdfti");
+
+ fixunsxfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunsxfsi");
+ fixunsxfdi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunsxfdi");
+ fixunsxfti_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunsxfti");
+
+ fixunstfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunstfsi");
+ fixunstfdi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunstfdi");
+ fixunstfti_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunstfti");
+
+/* Define library calls for quad FP instructions */
+#ifdef TRUNCTFSF2_LIBCALL
+ trunctfsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, TRUNCTFSF2_LIBCALL);
+#endif
+#ifdef TRUNCTFDF2_LIBCALL
+ trunctfdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, TRUNCTFDF2_LIBCALL);
+#endif
+#ifdef EXTENDSFTF2_LIBCALL
+ extendsftf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, EXTENDSFTF2_LIBCALL);
+#endif
+#ifdef EXTENDDFTF2_LIBCALL
+ extenddftf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, EXTENDDFTF2_LIBCALL);
+#endif
+#ifdef FLOATSITF2_LIBCALL
+ floatsitf_libfunc = gen_rtx (SYMBOL_REF, Pmode, FLOATSITF2_LIBCALL);
+#endif
+#ifdef FIX_TRUNCTFSI2_LIBCALL
+ fixtfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, FIX_TRUNCTFSI2_LIBCALL);
+#endif
+#ifdef FIXUNS_TRUNCTFSI2_LIBCALL
+ fixunstfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, FIXUNS_TRUNCTFSI2_LIBCALL);
+#endif
+
+#ifdef INIT_TARGET_OPTABS
+ /* Allow the target to add more libcalls or rename some, etc. */
+ INIT_TARGET_OPTABS;
+#endif
+}
+
+#ifdef BROKEN_LDEXP
+
+/* SCO 3.2 apparently has a broken ldexp. */
+
+double
+ldexp(x,n)
+ double x;
+ int n;
+{
+ if (n > 0)
+ while (n--)
+ x *= 2;
+
+ return x;
+}
+#endif /* BROKEN_LDEXP */
diff --git a/gnu/usr.bin/cc/cc_int/print-rtl.c b/gnu/usr.bin/cc/cc_int/print-rtl.c
new file mode 100644
index 0000000..5570639
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/print-rtl.c
@@ -0,0 +1,328 @@
+/* Print RTL for GNU C Compiler.
+ Copyright (C) 1987, 1988, 1992 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#include <ctype.h>
+#include <stdio.h>
+#include "rtl.h"
+
+
+/* How to print out a register name.
+ We don't use PRINT_REG because some definitions of PRINT_REG
+ don't work here. */
+#ifndef DEBUG_PRINT_REG
+#define DEBUG_PRINT_REG(RTX, CODE, FILE) \
+ fprintf ((FILE), "%d %s", REGNO (RTX), reg_names[REGNO (RTX)])
+#endif
+
+/* Array containing all of the register names */
+
+#ifdef DEBUG_REGISTER_NAMES
+static char *reg_names[] = DEBUG_REGISTER_NAMES;
+#else
+static char *reg_names[] = REGISTER_NAMES;
+#endif
+
+static FILE *outfile;
+
+char spaces[] = " ";
+
+static int sawclose = 0;
+
+/* Names for patterns. Non-zero only when linked with insn-output.c. */
+
+extern char **insn_name_ptr;
+
+/* Print IN_RTX onto OUTFILE. This is the recursive part of printing. */
+
+static void
+print_rtx (in_rtx)
+ register rtx in_rtx;
+{
+ static int indent;
+ register int i, j;
+ register char *format_ptr;
+ register int is_insn;
+
+ if (sawclose)
+ {
+ fprintf (outfile, "\n%s",
+ (spaces + (sizeof spaces - 1 - indent * 2)));
+ sawclose = 0;
+ }
+
+ if (in_rtx == 0)
+ {
+ fprintf (outfile, "(nil)");
+ sawclose = 1;
+ return;
+ }
+
+ /* print name of expression code */
+ fprintf (outfile, "(%s", GET_RTX_NAME (GET_CODE (in_rtx)));
+
+ if (in_rtx->in_struct)
+ fprintf (outfile, "/s");
+
+ if (in_rtx->volatil)
+ fprintf (outfile, "/v");
+
+ if (in_rtx->unchanging)
+ fprintf (outfile, "/u");
+
+ if (in_rtx->integrated)
+ fprintf (outfile, "/i");
+
+ if (GET_MODE (in_rtx) != VOIDmode)
+ {
+ /* Print REG_NOTE names for EXPR_LIST and INSN_LIST. */
+ if (GET_CODE (in_rtx) == EXPR_LIST || GET_CODE (in_rtx) == INSN_LIST)
+ fprintf (outfile, ":%s", GET_REG_NOTE_NAME (GET_MODE (in_rtx)));
+ else
+ fprintf (outfile, ":%s", GET_MODE_NAME (GET_MODE (in_rtx)));
+ }
+
+ is_insn = (GET_RTX_CLASS (GET_CODE (in_rtx)) == 'i');
+ format_ptr = GET_RTX_FORMAT (GET_CODE (in_rtx));
+
+ for (i = 0; i < GET_RTX_LENGTH (GET_CODE (in_rtx)); i++)
+ switch (*format_ptr++)
+ {
+ case 'S':
+ case 's':
+ if (XSTR (in_rtx, i) == 0)
+ fprintf (outfile, " \"\"");
+ else
+ fprintf (outfile, " (\"%s\")", XSTR (in_rtx, i));
+ sawclose = 1;
+ break;
+
+ /* 0 indicates a field for internal use that should not be printed. */
+ case '0':
+ break;
+
+ case 'e':
+ indent += 2;
+ if (!sawclose)
+ fprintf (outfile, " ");
+ print_rtx (XEXP (in_rtx, i));
+ indent -= 2;
+ break;
+
+ case 'E':
+ case 'V':
+ indent += 2;
+ if (sawclose)
+ {
+ fprintf (outfile, "\n%s",
+ (spaces + (sizeof spaces - 1 - indent * 2)));
+ sawclose = 0;
+ }
+ fprintf (outfile, "[ ");
+ if (NULL != XVEC (in_rtx, i))
+ {
+ indent += 2;
+ if (XVECLEN (in_rtx, i))
+ sawclose = 1;
+
+ for (j = 0; j < XVECLEN (in_rtx, i); j++)
+ print_rtx (XVECEXP (in_rtx, i, j));
+
+ indent -= 2;
+ }
+ if (sawclose)
+ fprintf (outfile, "\n%s",
+ (spaces + (sizeof spaces - 1 - indent * 2)));
+
+ fprintf (outfile, "] ");
+ sawclose = 1;
+ indent -= 2;
+ break;
+
+ case 'w':
+ fprintf (outfile,
+#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT
+ " %d",
+#else
+ " %ld",
+#endif
+ XWINT (in_rtx, i));
+ break;
+
+ case 'i':
+ {
+ register int value = XINT (in_rtx, i);
+
+ if (GET_CODE (in_rtx) == REG && value < FIRST_PSEUDO_REGISTER)
+ {
+ fputc (' ', outfile);
+ DEBUG_PRINT_REG (in_rtx, 0, outfile);
+ }
+ else
+ fprintf (outfile, " %d", value);
+ }
+ if (is_insn && &INSN_CODE (in_rtx) == &XINT (in_rtx, i)
+ && insn_name_ptr
+ && XINT (in_rtx, i) >= 0)
+ fprintf (outfile, " {%s}", insn_name_ptr[XINT (in_rtx, i)]);
+ sawclose = 0;
+ break;
+
+ /* Print NOTE_INSN names rather than integer codes. */
+
+ case 'n':
+ if (XINT (in_rtx, i) <= 0)
+ fprintf (outfile, " %s", GET_NOTE_INSN_NAME (XINT (in_rtx, i)));
+ else
+ fprintf (outfile, " %d", XINT (in_rtx, i));
+ sawclose = 0;
+ break;
+
+ case 'u':
+ if (XEXP (in_rtx, i) != NULL)
+ fprintf (outfile, " %d", INSN_UID (XEXP (in_rtx, i)));
+ else
+ fprintf (outfile, " 0");
+ sawclose = 0;
+ break;
+
+ case '*':
+ fprintf (outfile, " Unknown");
+ sawclose = 0;
+ break;
+
+ default:
+ fprintf (stderr,
+ "switch format wrong in rtl.print_rtx(). format was: %c.\n",
+ format_ptr[-1]);
+ abort ();
+ }
+
+ fprintf (outfile, ")");
+ sawclose = 1;
+}
+
+/* Call this function from the debugger to see what X looks like. */
+
+void
+debug_rtx (x)
+ rtx x;
+{
+ outfile = stderr;
+ print_rtx (x);
+ fprintf (stderr, "\n");
+}
+
+/* Count of rtx's to print with debug_rtx_list.
+ This global exists because gdb user defined commands have no arguments. */
+
+int debug_rtx_count = 0; /* 0 is treated as equivalent to 1 */
+
+/* Call this function to print list from X on.
+
+ N is a count of the rtx's to print. Positive values print from the specified
+ rtx on. Negative values print a window around the rtx.
+ EG: -5 prints 2 rtx's on either side (in addition to the specified rtx). */
+
+void
+debug_rtx_list (x, n)
+ rtx x;
+ int n;
+{
+ int i,count;
+ rtx insn;
+
+ count = n == 0 ? 1 : n < 0 ? -n : n;
+
+ /* If we are printing a window, back up to the start. */
+
+ if (n < 0)
+ for (i = count / 2; i > 0; i--)
+ {
+ if (PREV_INSN (x) == 0)
+ break;
+ x = PREV_INSN (x);
+ }
+
+ for (i = count, insn = x; i > 0 && insn != 0; i--, insn = NEXT_INSN (insn))
+ debug_rtx (insn);
+}
+
+/* Call this function to search an rtx list to find one with insn uid UID,
+ and then call debug_rtx_list to print it, using DEBUG_RTX_COUNT.
+ The found insn is returned to enable further debugging analysis. */
+
+rtx
+debug_rtx_find(x, uid)
+ rtx x;
+ int uid;
+{
+ while (x != 0 && INSN_UID (x) != uid)
+ x = NEXT_INSN (x);
+ if (x != 0)
+ {
+ debug_rtx_list (x, debug_rtx_count);
+ return x;
+ }
+ else
+ {
+ fprintf (stderr, "insn uid %d not found\n", uid);
+ return 0;
+ }
+}
+
+/* External entry point for printing a chain of insns
+ starting with RTX_FIRST onto file OUTF.
+ A blank line separates insns.
+
+ If RTX_FIRST is not an insn, then it alone is printed, with no newline. */
+
+void
+print_rtl (outf, rtx_first)
+ FILE *outf;
+ rtx rtx_first;
+{
+ register rtx tmp_rtx;
+
+ outfile = outf;
+ sawclose = 0;
+
+ if (rtx_first == 0)
+ fprintf (outf, "(nil)\n");
+ else
+ switch (GET_CODE (rtx_first))
+ {
+ case INSN:
+ case JUMP_INSN:
+ case CALL_INSN:
+ case NOTE:
+ case CODE_LABEL:
+ case BARRIER:
+ for (tmp_rtx = rtx_first; NULL != tmp_rtx; tmp_rtx = NEXT_INSN (tmp_rtx))
+ {
+ print_rtx (tmp_rtx);
+ fprintf (outfile, "\n");
+ }
+ break;
+
+ default:
+ print_rtx (rtx_first);
+ }
+}
diff --git a/gnu/usr.bin/cc/cc_int/print-tree.c b/gnu/usr.bin/cc/cc_int/print-tree.c
new file mode 100644
index 0000000..f4f878f
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/print-tree.c
@@ -0,0 +1,642 @@
+/* Prints out tree in human readable form - GNU C-compiler
+ Copyright (C) 1990, 1991, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#include "tree.h"
+#include <stdio.h>
+
+extern char **tree_code_name;
+
+extern char *mode_name[];
+
+void print_node ();
+void indent_to ();
+
+/* Define the hash table of nodes already seen.
+ Such nodes are not repeated; brief cross-references are used. */
+
+#define HASH_SIZE 37
+
+struct bucket
+{
+ tree node;
+ struct bucket *next;
+};
+
+static struct bucket **table;
+
+/* Print the node NODE on standard error, for debugging.
+ Most nodes referred to by this one are printed recursively
+ down to a depth of six. */
+
+void
+debug_tree (node)
+ tree node;
+{
+ char *object = (char *) oballoc (0);
+
+ table = (struct bucket **) oballoc (HASH_SIZE * sizeof (struct bucket *));
+ bzero ((char *) table, HASH_SIZE * sizeof (struct bucket *));
+ print_node (stderr, "", node, 0);
+ table = 0;
+ obfree (object);
+ fprintf (stderr, "\n");
+}
+
+/* Print a node in brief fashion, with just the code, address and name. */
+
+void
+print_node_brief (file, prefix, node, indent)
+ FILE *file;
+ char *prefix;
+ tree node;
+ int indent;
+{
+ char class;
+
+ if (node == 0)
+ return;
+
+ class = TREE_CODE_CLASS (TREE_CODE (node));
+
+ /* Always print the slot this node is in, and its code, address and
+ name if any. */
+ if (indent > 0)
+ fprintf (file, " ");
+ fprintf (file, "%s <%s ", prefix, tree_code_name[(int) TREE_CODE (node)]);
+ fprintf (file, HOST_PTR_PRINTF, (HOST_WIDE_INT) node);
+
+ if (class == 'd')
+ {
+ if (DECL_NAME (node))
+ fprintf (file, " %s", IDENTIFIER_POINTER (DECL_NAME (node)));
+ }
+ else if (class == 't')
+ {
+ if (TYPE_NAME (node))
+ {
+ if (TREE_CODE (TYPE_NAME (node)) == IDENTIFIER_NODE)
+ fprintf (file, " %s", IDENTIFIER_POINTER (TYPE_NAME (node)));
+ else if (TREE_CODE (TYPE_NAME (node)) == TYPE_DECL
+ && DECL_NAME (TYPE_NAME (node)))
+ fprintf (file, " %s",
+ IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (node))));
+ }
+ }
+ if (TREE_CODE (node) == IDENTIFIER_NODE)
+ fprintf (file, " %s", IDENTIFIER_POINTER (node));
+ /* We might as well always print the value of an integer. */
+ if (TREE_CODE (node) == INTEGER_CST)
+ {
+ if (TREE_CONSTANT_OVERFLOW (node))
+ fprintf (file, " overflow");
+
+ if (TREE_INT_CST_HIGH (node) == 0)
+ fprintf (file, " %1u", TREE_INT_CST_LOW (node));
+ else if (TREE_INT_CST_HIGH (node) == -1
+ && TREE_INT_CST_LOW (node) != 0)
+ fprintf (file, " -%1u", -TREE_INT_CST_LOW (node));
+ else
+ fprintf (file,
+#if HOST_BITS_PER_WIDE_INT == 64
+#if HOST_BITS_PER_WIDE_INT != HOST_BITS_PER_INT
+ " 0x%lx%016lx",
+#else
+ " 0x%x%016x",
+#endif
+#else
+#if HOST_BITS_PER_WIDE_INT != HOST_BITS_PER_INT
+ " 0x%lx%08lx",
+#else
+ " 0x%x%08x",
+#endif
+#endif
+ TREE_INT_CST_HIGH (node), TREE_INT_CST_LOW (node));
+ }
+ if (TREE_CODE (node) == REAL_CST)
+ {
+#ifndef REAL_IS_NOT_DOUBLE
+ fprintf (file, " %e", TREE_REAL_CST (node));
+#else
+ {
+ int i;
+ unsigned char *p = (unsigned char *) &TREE_REAL_CST (node);
+ fprintf (file, " 0x");
+ for (i = 0; i < sizeof TREE_REAL_CST (node); i++)
+ fprintf (file, "%02x", *p++);
+ fprintf (file, "");
+ }
+#endif /* REAL_IS_NOT_DOUBLE */
+ }
+
+ fprintf (file, ">");
+}
+
+void
+indent_to (file, column)
+ FILE *file;
+ int column;
+{
+ int i;
+
+ /* Since this is the long way, indent to desired column. */
+ if (column > 0)
+ fprintf (file, "\n");
+ for (i = 0; i < column; i++)
+ fprintf (file, " ");
+}
+
+/* Print the node NODE in full on file FILE, preceded by PREFIX,
+ starting in column INDENT. */
+
+void
+print_node (file, prefix, node, indent)
+ FILE *file;
+ char *prefix;
+ tree node;
+ int indent;
+{
+ int hash;
+ struct bucket *b;
+ enum machine_mode mode;
+ char class;
+ int len;
+ int first_rtl;
+ int i;
+
+ if (node == 0)
+ return;
+
+ class = TREE_CODE_CLASS (TREE_CODE (node));
+
+ /* Don't get too deep in nesting. If the user wants to see deeper,
+ it is easy to use the address of a lowest-level node
+ as an argument in another call to debug_tree. */
+
+ if (indent > 24)
+ {
+ print_node_brief (file, prefix, node, indent);
+ return;
+ }
+
+ if (indent > 8 && (class == 't' || class == 'd'))
+ {
+ print_node_brief (file, prefix, node, indent);
+ return;
+ }
+
+ /* It is unsafe to look at any other filds of an ERROR_MARK node. */
+ if (TREE_CODE (node) == ERROR_MARK)
+ {
+ print_node_brief (file, prefix, node, indent);
+ return;
+ }
+
+ hash = ((unsigned HOST_WIDE_INT) node) % HASH_SIZE;
+
+ /* If node is in the table, just mention its address. */
+ for (b = table[hash]; b; b = b->next)
+ if (b->node == node)
+ {
+ print_node_brief (file, prefix, node, indent);
+ return;
+ }
+
+ /* Add this node to the table. */
+ b = (struct bucket *) oballoc (sizeof (struct bucket));
+ b->node = node;
+ b->next = table[hash];
+ table[hash] = b;
+
+ /* Indent to the specified column, since this is the long form. */
+ indent_to (file, indent);
+
+ /* Print the slot this node is in, and its code, and address. */
+ fprintf (file, "%s <%s ", prefix, tree_code_name[(int) TREE_CODE (node)]);
+ fprintf (file, HOST_PTR_PRINTF, (HOST_WIDE_INT) node);
+
+ /* Print the name, if any. */
+ if (class == 'd')
+ {
+ if (DECL_NAME (node))
+ fprintf (file, " %s", IDENTIFIER_POINTER (DECL_NAME (node)));
+ }
+ else if (class == 't')
+ {
+ if (TYPE_NAME (node))
+ {
+ if (TREE_CODE (TYPE_NAME (node)) == IDENTIFIER_NODE)
+ fprintf (file, " %s", IDENTIFIER_POINTER (TYPE_NAME (node)));
+ else if (TREE_CODE (TYPE_NAME (node)) == TYPE_DECL
+ && DECL_NAME (TYPE_NAME (node)))
+ fprintf (file, " %s",
+ IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (node))));
+ }
+ }
+ if (TREE_CODE (node) == IDENTIFIER_NODE)
+ fprintf (file, " %s", IDENTIFIER_POINTER (node));
+
+ if (TREE_CODE (node) == INTEGER_CST)
+ {
+ if (indent <= 4)
+ print_node_brief (file, "type", TREE_TYPE (node), indent + 4);
+ }
+ else
+ {
+ print_node (file, "type", TREE_TYPE (node), indent + 4);
+ if (TREE_TYPE (node))
+ indent_to (file, indent + 3);
+
+ print_obstack_name ((char *) node, file, "");
+ indent_to (file, indent + 3);
+ }
+
+ /* If a permanent object is in the wrong obstack, or the reverse, warn. */
+ if (object_permanent_p (node) != TREE_PERMANENT (node))
+ {
+ if (TREE_PERMANENT (node))
+ fputs (" !!permanent object in non-permanent obstack!!", file);
+ else
+ fputs (" !!non-permanent object in permanent obstack!!", file);
+ indent_to (file, indent + 3);
+ }
+
+ if (TREE_SIDE_EFFECTS (node))
+ fputs (" side-effects", file);
+ if (TREE_READONLY (node))
+ fputs (" readonly", file);
+ if (TREE_CONSTANT (node))
+ fputs (" constant", file);
+ if (TREE_ADDRESSABLE (node))
+ fputs (" addressable", file);
+ if (TREE_THIS_VOLATILE (node))
+ fputs (" volatile", file);
+ if (TREE_UNSIGNED (node))
+ fputs (" unsigned", file);
+ if (TREE_ASM_WRITTEN (node))
+ fputs (" asm_written", file);
+ if (TREE_USED (node))
+ fputs (" used", file);
+ if (TREE_RAISES (node))
+ fputs (" raises", file);
+ if (TREE_PERMANENT (node))
+ fputs (" permanent", file);
+ if (TREE_PUBLIC (node))
+ fputs (" public", file);
+ if (TREE_STATIC (node))
+ fputs (" static", file);
+ if (TREE_LANG_FLAG_0 (node))
+ fputs (" tree_0", file);
+ if (TREE_LANG_FLAG_1 (node))
+ fputs (" tree_1", file);
+ if (TREE_LANG_FLAG_2 (node))
+ fputs (" tree_2", file);
+ if (TREE_LANG_FLAG_3 (node))
+ fputs (" tree_3", file);
+ if (TREE_LANG_FLAG_4 (node))
+ fputs (" tree_4", file);
+ if (TREE_LANG_FLAG_5 (node))
+ fputs (" tree_5", file);
+ if (TREE_LANG_FLAG_6 (node))
+ fputs (" tree_6", file);
+
+ /* DECL_ nodes have additional attributes. */
+
+ switch (TREE_CODE_CLASS (TREE_CODE (node)))
+ {
+ case 'd':
+ mode = DECL_MODE (node);
+
+ if (DECL_EXTERNAL (node))
+ fputs (" external", file);
+ if (DECL_NONLOCAL (node))
+ fputs (" nonlocal", file);
+ if (DECL_REGISTER (node))
+ fputs (" regdecl", file);
+ if (DECL_INLINE (node))
+ fputs (" inline", file);
+ if (DECL_BIT_FIELD (node))
+ fputs (" bit-field", file);
+ if (DECL_VIRTUAL_P (node))
+ fputs (" virtual", file);
+ if (DECL_IGNORED_P (node))
+ fputs (" ignored", file);
+ if (DECL_IN_SYSTEM_HEADER (node))
+ fputs (" in_system_header", file);
+ if (DECL_LANG_FLAG_0 (node))
+ fputs (" decl_0", file);
+ if (DECL_LANG_FLAG_1 (node))
+ fputs (" decl_1", file);
+ if (DECL_LANG_FLAG_2 (node))
+ fputs (" decl_2", file);
+ if (DECL_LANG_FLAG_3 (node))
+ fputs (" decl_3", file);
+ if (DECL_LANG_FLAG_4 (node))
+ fputs (" decl_4", file);
+ if (DECL_LANG_FLAG_5 (node))
+ fputs (" decl_5", file);
+ if (DECL_LANG_FLAG_6 (node))
+ fputs (" decl_6", file);
+ if (DECL_LANG_FLAG_7 (node))
+ fputs (" decl_7", file);
+
+ fprintf (file, " %s", mode_name[(int) mode]);
+
+ fprintf (file, " file %s line %d",
+ DECL_SOURCE_FILE (node), DECL_SOURCE_LINE (node));
+
+ print_node (file, "size", DECL_SIZE (node), indent + 4);
+ indent_to (file, indent + 3);
+ if (TREE_CODE (node) != FUNCTION_DECL)
+ fprintf (file, " align %d", DECL_ALIGN (node));
+ else if (DECL_INLINE (node))
+ fprintf (file, " frame_size %d", DECL_FRAME_SIZE (node));
+ else if (DECL_BUILT_IN (node))
+ fprintf (file, " built-in code %d", DECL_FUNCTION_CODE (node));
+ if (TREE_CODE (node) == FIELD_DECL)
+ print_node (file, "bitpos", DECL_FIELD_BITPOS (node), indent + 4);
+ print_node_brief (file, "context", DECL_CONTEXT (node), indent + 4);
+ print_node_brief (file, "abstract_origin",
+ DECL_ABSTRACT_ORIGIN (node), indent + 4);
+
+ print_node (file, "arguments", DECL_ARGUMENTS (node), indent + 4);
+ print_node (file, "result", DECL_RESULT (node), indent + 4);
+ print_node_brief (file, "initial", DECL_INITIAL (node), indent + 4);
+
+ print_lang_decl (file, node, indent);
+
+ if (DECL_RTL (node) != 0)
+ {
+ indent_to (file, indent + 4);
+ print_rtl (file, DECL_RTL (node));
+ }
+
+ if (DECL_SAVED_INSNS (node) != 0)
+ {
+ indent_to (file, indent + 4);
+ if (TREE_CODE (node) == PARM_DECL)
+ {
+ fprintf (file, "incoming-rtl ");
+ print_rtl (file, DECL_INCOMING_RTL (node));
+ }
+ else if (TREE_CODE (node) == FUNCTION_DECL)
+ {
+ fprintf (file, "saved-insns ");
+ fprintf (file, HOST_PTR_PRINTF,
+ (HOST_WIDE_INT) DECL_SAVED_INSNS (node));
+ }
+ }
+
+ /* Print the decl chain only if decl is at second level. */
+ if (indent == 4)
+ print_node (file, "chain", TREE_CHAIN (node), indent + 4);
+ else
+ print_node_brief (file, "chain", TREE_CHAIN (node), indent + 4);
+ break;
+
+ case 't':
+ if (TYPE_NO_FORCE_BLK (node))
+ fputs (" no_force_blk", file);
+ if (TYPE_LANG_FLAG_0 (node))
+ fputs (" type_0", file);
+ if (TYPE_LANG_FLAG_1 (node))
+ fputs (" type_1", file);
+ if (TYPE_LANG_FLAG_2 (node))
+ fputs (" type_2", file);
+ if (TYPE_LANG_FLAG_3 (node))
+ fputs (" type_3", file);
+ if (TYPE_LANG_FLAG_4 (node))
+ fputs (" type_4", file);
+ if (TYPE_LANG_FLAG_5 (node))
+ fputs (" type_5", file);
+ if (TYPE_LANG_FLAG_6 (node))
+ fputs (" type_6", file);
+
+ mode = TYPE_MODE (node);
+ fprintf (file, " %s", mode_name[(int) mode]);
+
+ print_node (file, "size", TYPE_SIZE (node), indent + 4);
+ indent_to (file, indent + 3);
+
+ fprintf (file, " align %d", TYPE_ALIGN (node));
+ fprintf (file, " symtab %d", TYPE_SYMTAB_ADDRESS (node));
+
+ print_node (file, "attributes", TYPE_ATTRIBUTES (node), indent + 4);
+
+ if (TREE_CODE (node) == ARRAY_TYPE || TREE_CODE (node) == SET_TYPE)
+ print_node (file, "domain", TYPE_DOMAIN (node), indent + 4);
+ else if (TREE_CODE (node) == INTEGER_TYPE
+ || TREE_CODE (node) == BOOLEAN_TYPE
+ || TREE_CODE (node) == CHAR_TYPE)
+ {
+ fprintf (file, " precision %d", TYPE_PRECISION (node));
+ print_node (file, "min", TYPE_MIN_VALUE (node), indent + 4);
+ print_node (file, "max", TYPE_MAX_VALUE (node), indent + 4);
+ }
+ else if (TREE_CODE (node) == ENUMERAL_TYPE)
+ {
+ fprintf (file, " precision %d", TYPE_PRECISION (node));
+ print_node (file, "min", TYPE_MIN_VALUE (node), indent + 4);
+ print_node (file, "max", TYPE_MAX_VALUE (node), indent + 4);
+ print_node (file, "values", TYPE_VALUES (node), indent + 4);
+ }
+ else if (TREE_CODE (node) == REAL_TYPE)
+ fprintf (file, " precision %d", TYPE_PRECISION (node));
+ else if (TREE_CODE (node) == RECORD_TYPE
+ || TREE_CODE (node) == UNION_TYPE
+ || TREE_CODE (node) == QUAL_UNION_TYPE)
+ print_node (file, "fields", TYPE_FIELDS (node), indent + 4);
+ else if (TREE_CODE (node) == FUNCTION_TYPE || TREE_CODE (node) == METHOD_TYPE)
+ {
+ if (TYPE_METHOD_BASETYPE (node))
+ print_node_brief (file, "method basetype", TYPE_METHOD_BASETYPE (node), indent + 4);
+ print_node (file, "arg-types", TYPE_ARG_TYPES (node), indent + 4);
+ }
+ if (TYPE_CONTEXT (node))
+ print_node_brief (file, "context", TYPE_CONTEXT (node), indent + 4);
+
+ print_lang_type (file, node, indent);
+
+ if (TYPE_POINTER_TO (node) || TREE_CHAIN (node))
+ indent_to (file, indent + 3);
+ print_node_brief (file, "pointer_to_this", TYPE_POINTER_TO (node), indent + 4);
+ print_node_brief (file, "reference_to_this", TYPE_REFERENCE_TO (node), indent + 4);
+ print_node_brief (file, "chain", TREE_CHAIN (node), indent + 4);
+ break;
+
+ case 'b':
+ print_node (file, "vars", BLOCK_VARS (node), indent + 4);
+ print_node (file, "tags", BLOCK_TYPE_TAGS (node), indent + 4);
+ print_node (file, "supercontext", BLOCK_SUPERCONTEXT (node), indent + 4);
+ print_node (file, "subblocks", BLOCK_SUBBLOCKS (node), indent + 4);
+ print_node (file, "chain", BLOCK_CHAIN (node), indent + 4);
+ print_node (file, "abstract_origin",
+ BLOCK_ABSTRACT_ORIGIN (node), indent + 4);
+ return;
+
+ case 'e':
+ case '<':
+ case '1':
+ case '2':
+ case 'r':
+ case 's':
+ switch (TREE_CODE (node))
+ {
+ case BIND_EXPR:
+ print_node (file, "vars", TREE_OPERAND (node, 0), indent + 4);
+ print_node (file, "body", TREE_OPERAND (node, 1), indent + 4);
+ print_node (file, "block", TREE_OPERAND (node, 2), indent + 4);
+ return;
+ }
+
+ first_rtl = len = tree_code_length[(int) TREE_CODE (node)];
+ /* These kinds of nodes contain rtx's, not trees,
+ after a certain point. Print the rtx's as rtx's. */
+ switch (TREE_CODE (node))
+ {
+ case SAVE_EXPR:
+ first_rtl = 2;
+ break;
+ case CALL_EXPR:
+ first_rtl = 2;
+ break;
+ case METHOD_CALL_EXPR:
+ first_rtl = 3;
+ break;
+ case WITH_CLEANUP_EXPR:
+ /* Should be defined to be 2. */
+ first_rtl = 1;
+ break;
+ case RTL_EXPR:
+ first_rtl = 0;
+ }
+ for (i = 0; i < len; i++)
+ {
+ if (i >= first_rtl)
+ {
+ indent_to (file, indent + 4);
+ fprintf (file, "rtl %d ", i);
+ if (TREE_OPERAND (node, i))
+ print_rtl (file, (struct rtx_def *) TREE_OPERAND (node, i));
+ else
+ fprintf (file, "(nil)");
+ fprintf (file, "\n");
+ }
+ else
+ {
+ char temp[10];
+
+ sprintf (temp, "arg %d", i);
+ print_node (file, temp, TREE_OPERAND (node, i), indent + 4);
+ }
+ }
+ break;
+
+ case 'c':
+ case 'x':
+ switch (TREE_CODE (node))
+ {
+ case INTEGER_CST:
+ if (TREE_CONSTANT_OVERFLOW (node))
+ fprintf (file, " overflow");
+
+ if (TREE_INT_CST_HIGH (node) == 0)
+ fprintf (file, " %1u", TREE_INT_CST_LOW (node));
+ else if (TREE_INT_CST_HIGH (node) == -1
+ && TREE_INT_CST_LOW (node) != 0)
+ fprintf (file, " -%1u", -TREE_INT_CST_LOW (node));
+ else
+ fprintf (file,
+#if HOST_BITS_PER_WIDE_INT == 64
+#if HOST_BITS_PER_WIDE_INT != HOST_BITS_PER_INT
+ " 0x%lx%016lx",
+#else
+ " 0x%x%016x",
+#endif
+#else
+#if HOST_BITS_PER_WIDE_INT != HOST_BITS_PER_INT
+ " 0x%lx%08lx",
+#else
+ " 0x%x%08x",
+#endif
+#endif
+ TREE_INT_CST_HIGH (node), TREE_INT_CST_LOW (node));
+ break;
+
+ case REAL_CST:
+#ifndef REAL_IS_NOT_DOUBLE
+ fprintf (file, " %e", TREE_REAL_CST (node));
+#else
+ {
+ char *p = (char *) &TREE_REAL_CST (node);
+ fprintf (file, " 0x");
+ for (i = 0; i < sizeof TREE_REAL_CST (node); i++)
+ fprintf (file, "%02x", *p++);
+ fprintf (file, "");
+ }
+#endif /* REAL_IS_NOT_DOUBLE */
+ break;
+
+ case COMPLEX_CST:
+ print_node (file, "real", TREE_REALPART (node), indent + 4);
+ print_node (file, "imag", TREE_IMAGPART (node), indent + 4);
+ break;
+
+ case STRING_CST:
+ fprintf (file, " \"%s\"", TREE_STRING_POINTER (node));
+ /* Print the chain at second level. */
+ if (indent == 4)
+ print_node (file, "chain", TREE_CHAIN (node), indent + 4);
+ else
+ print_node_brief (file, "chain", TREE_CHAIN (node), indent + 4);
+ break;
+
+ case IDENTIFIER_NODE:
+ print_lang_identifier (file, node, indent);
+ break;
+
+ case TREE_LIST:
+ print_node (file, "purpose", TREE_PURPOSE (node), indent + 4);
+ print_node (file, "value", TREE_VALUE (node), indent + 4);
+ print_node (file, "chain", TREE_CHAIN (node), indent + 4);
+ break;
+
+ case TREE_VEC:
+ len = TREE_VEC_LENGTH (node);
+ for (i = 0; i < len; i++)
+ if (TREE_VEC_ELT (node, i))
+ {
+ char temp[10];
+ sprintf (temp, "elt %d", i);
+ indent_to (file, indent + 4);
+ print_node_brief (file, temp, TREE_VEC_ELT (node, i), 0);
+ }
+ break;
+
+ case OP_IDENTIFIER:
+ print_node (file, "op1", TREE_PURPOSE (node), indent + 4);
+ print_node (file, "op2", TREE_VALUE (node), indent + 4);
+ }
+
+ break;
+ }
+
+ fprintf (file, ">");
+}
diff --git a/gnu/usr.bin/cc/cc_int/real.c b/gnu/usr.bin/cc/cc_int/real.c
new file mode 100644
index 0000000..cad4343
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/real.c
@@ -0,0 +1,5969 @@
+/* real.c - implementation of REAL_ARITHMETIC, REAL_VALUE_ATOF,
+ and support for XFmode IEEE extended real floating point arithmetic.
+ Copyright (C) 1993, 1994 Free Software Foundation, Inc.
+ Contributed by Stephen L. Moshier (moshier@world.std.com).
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <errno.h>
+#include "config.h"
+#include "tree.h"
+
+#ifndef errno
+extern int errno;
+#endif
+
+/* To enable support of XFmode extended real floating point, define
+LONG_DOUBLE_TYPE_SIZE 96 in the tm.h file (m68k.h or i386.h).
+
+To support cross compilation between IEEE, VAX and IBM floating
+point formats, define REAL_ARITHMETIC in the tm.h file.
+
+In either case the machine files (tm.h) must not contain any code
+that tries to use host floating point arithmetic to convert
+REAL_VALUE_TYPEs from `double' to `float', pass them to fprintf,
+etc. In cross-compile situations a REAL_VALUE_TYPE may not
+be intelligible to the host computer's native arithmetic.
+
+The emulator defaults to the host's floating point format so that
+its decimal conversion functions can be used if desired (see
+real.h).
+
+The first part of this file interfaces gcc to ieee.c, which is a
+floating point arithmetic suite that was not written with gcc in
+mind. The interface is followed by ieee.c itself and related
+items. Avoid changing ieee.c unless you have suitable test
+programs available. A special version of the PARANOIA floating
+point arithmetic tester, modified for this purpose, can be found
+on usc.edu : /pub/C-numanal/ieeetest.zoo. Some tutorial
+information on ieee.c is given in my book: S. L. Moshier,
+_Methods and Programs for Mathematical Functions_, Prentice-Hall
+or Simon & Schuster Int'l, 1989. A library of XFmode elementary
+transcendental functions can be obtained by ftp from
+research.att.com: netlib/cephes/ldouble.shar.Z */
+
+/* Type of computer arithmetic.
+ Only one of DEC, IBM, MIEEE, IBMPC, or UNK should get defined.
+
+ `MIEEE' refers generically to big-endian IEEE floating-point data
+ structure. This definition should work in SFmode `float' type and
+ DFmode `double' type on virtually all big-endian IEEE machines.
+ If LONG_DOUBLE_TYPE_SIZE has been defined to be 96, then MIEEE
+ also invokes the particular XFmode (`long double' type) data
+ structure used by the Motorola 680x0 series processors.
+
+ `IBMPC' refers generally to little-endian IEEE machines. In this
+ case, if LONG_DOUBLE_TYPE_SIZE has been defined to be 96, then
+ IBMPC also invokes the particular XFmode `long double' data
+ structure used by the Intel 80x86 series processors.
+
+ `DEC' refers specifically to the Digital Equipment Corp PDP-11
+ and VAX floating point data structure. This model currently
+ supports no type wider than DFmode.
+
+ `IBM' refers specifically to the IBM System/370 and compatible
+ floating point data structure. This model currently supports
+ no type wider than DFmode. The IBM conversions were contributed by
+ frank@atom.ansto.gov.au (Frank Crawford).
+
+ If LONG_DOUBLE_TYPE_SIZE = 64 (the default, unless tm.h defines it)
+ then `long double' and `double' are both implemented, but they
+ both mean DFmode. In this case, the software floating-point
+ support available here is activated by writing
+ #define REAL_ARITHMETIC
+ in tm.h.
+
+ The case LONG_DOUBLE_TYPE_SIZE = 128 activates TFmode support
+ and may deactivate XFmode since `long double' is used to refer
+ to both modes.
+
+ The macros FLOAT_WORDS_BIG_ENDIAN, HOST_FLOAT_WORDS_BIG_ENDIAN,
+ contributed by Richard Earnshaw <Richard.Earnshaw@cl.cam.ac.uk>,
+ separate the floating point unit's endian-ness from that of
+ the integer addressing. This permits one to define a big-endian
+ FPU on a little-endian machine (e.g., ARM). An extension to
+ BYTES_BIG_ENDIAN may be required for some machines in the future.
+ These optional macros may be defined in tm.h. In real.h, they
+ default to WORDS_BIG_ENDIAN, etc., so there is no need to define
+ them for any normal host or target machine on which the floats
+ and the integers have the same endian-ness. */
+
+
+/* The following converts gcc macros into the ones used by this file. */
+
+/* REAL_ARITHMETIC defined means that macros in real.h are
+ defined to call emulator functions. */
+#ifdef REAL_ARITHMETIC
+
+#if TARGET_FLOAT_FORMAT == VAX_FLOAT_FORMAT
+/* PDP-11, Pro350, VAX: */
+#define DEC 1
+#else /* it's not VAX */
+#if TARGET_FLOAT_FORMAT == IBM_FLOAT_FORMAT
+/* IBM System/370 style */
+#define IBM 1
+#else /* it's also not an IBM */
+#if TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
+#if FLOAT_WORDS_BIG_ENDIAN
+/* Motorola IEEE, high order words come first (Sun workstation): */
+#define MIEEE 1
+#else /* not big-endian */
+/* Intel IEEE, low order words come first:
+ */
+#define IBMPC 1
+#endif /* big-endian */
+#else /* it's not IEEE either */
+/* UNKnown arithmetic. We don't support this and can't go on. */
+unknown arithmetic type
+#define UNK 1
+#endif /* not IEEE */
+#endif /* not IBM */
+#endif /* not VAX */
+
+#else
+/* REAL_ARITHMETIC not defined means that the *host's* data
+ structure will be used. It may differ by endian-ness from the
+ target machine's structure and will get its ends swapped
+ accordingly (but not here). Probably only the decimal <-> binary
+ functions in this file will actually be used in this case. */
+
+#if HOST_FLOAT_FORMAT == VAX_FLOAT_FORMAT
+#define DEC 1
+#else /* it's not VAX */
+#if HOST_FLOAT_FORMAT == IBM_FLOAT_FORMAT
+/* IBM System/370 style */
+#define IBM 1
+#else /* it's also not an IBM */
+#if HOST_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
+#if HOST_FLOAT_WORDS_BIG_ENDIAN
+#define MIEEE 1
+#else /* not big-endian */
+#define IBMPC 1
+#endif /* big-endian */
+#else /* it's not IEEE either */
+unknown arithmetic type
+#define UNK 1
+#endif /* not IEEE */
+#endif /* not IBM */
+#endif /* not VAX */
+
+#endif /* REAL_ARITHMETIC not defined */
+
+/* Define INFINITY for support of infinity.
+ Define NANS for support of Not-a-Number's (NaN's). */
+#if !defined(DEC) && !defined(IBM)
+#define INFINITY
+#define NANS
+#endif
+
+/* Support of NaNs requires support of infinity. */
+#ifdef NANS
+#ifndef INFINITY
+#define INFINITY
+#endif
+#endif
+
+/* Find a host integer type that is at least 16 bits wide,
+ and another type at least twice whatever that size is. */
+
+#if HOST_BITS_PER_CHAR >= 16
+#define EMUSHORT char
+#define EMUSHORT_SIZE HOST_BITS_PER_CHAR
+#define EMULONG_SIZE (2 * HOST_BITS_PER_CHAR)
+#else
+#if HOST_BITS_PER_SHORT >= 16
+#define EMUSHORT short
+#define EMUSHORT_SIZE HOST_BITS_PER_SHORT
+#define EMULONG_SIZE (2 * HOST_BITS_PER_SHORT)
+#else
+#if HOST_BITS_PER_INT >= 16
+#define EMUSHORT int
+#define EMUSHORT_SIZE HOST_BITS_PER_INT
+#define EMULONG_SIZE (2 * HOST_BITS_PER_INT)
+#else
+#if HOST_BITS_PER_LONG >= 16
+#define EMUSHORT long
+#define EMUSHORT_SIZE HOST_BITS_PER_LONG
+#define EMULONG_SIZE (2 * HOST_BITS_PER_LONG)
+#else
+/* You will have to modify this program to have a smaller unit size. */
+#define EMU_NON_COMPILE
+#endif
+#endif
+#endif
+#endif
+
+#if HOST_BITS_PER_SHORT >= EMULONG_SIZE
+#define EMULONG short
+#else
+#if HOST_BITS_PER_INT >= EMULONG_SIZE
+#define EMULONG int
+#else
+#if HOST_BITS_PER_LONG >= EMULONG_SIZE
+#define EMULONG long
+#else
+#if HOST_BITS_PER_LONG_LONG >= EMULONG_SIZE
+#define EMULONG long long int
+#else
+/* You will have to modify this program to have a smaller unit size. */
+#define EMU_NON_COMPILE
+#endif
+#endif
+#endif
+#endif
+
+
+/* The host interface doesn't work if no 16-bit size exists. */
+#if EMUSHORT_SIZE != 16
+#define EMU_NON_COMPILE
+#endif
+
+/* OK to continue compilation. */
+#ifndef EMU_NON_COMPILE
+
+/* Construct macros to translate between REAL_VALUE_TYPE and e type.
+ In GET_REAL and PUT_REAL, r and e are pointers.
+ A REAL_VALUE_TYPE is guaranteed to occupy contiguous locations
+ in memory, with no holes. */
+
+#if LONG_DOUBLE_TYPE_SIZE == 96
+/* Number of 16 bit words in external e type format */
+#define NE 6
+#define MAXDECEXP 4932
+#define MINDECEXP -4956
+#define GET_REAL(r,e) bcopy (r, e, 2*NE)
+#define PUT_REAL(e,r) bcopy (e, r, 2*NE)
+#else /* no XFmode */
+#if LONG_DOUBLE_TYPE_SIZE == 128
+#define NE 10
+#define MAXDECEXP 4932
+#define MINDECEXP -4977
+#define GET_REAL(r,e) bcopy (r, e, 2*NE)
+#define PUT_REAL(e,r) bcopy (e, r, 2*NE)
+#else
+#define NE 6
+#define MAXDECEXP 4932
+#define MINDECEXP -4956
+#ifdef REAL_ARITHMETIC
+/* Emulator uses target format internally
+ but host stores it in host endian-ness. */
+
+#if HOST_FLOAT_WORDS_BIG_ENDIAN == FLOAT_WORDS_BIG_ENDIAN
+#define GET_REAL(r,e) e53toe ((unsigned EMUSHORT*) (r), (e))
+#define PUT_REAL(e,r) etoe53 ((e), (unsigned EMUSHORT *) (r))
+
+#else /* endian-ness differs */
+/* emulator uses target endian-ness internally */
+#define GET_REAL(r,e) \
+do { unsigned EMUSHORT w[4]; \
+ w[3] = ((EMUSHORT *) r)[0]; \
+ w[2] = ((EMUSHORT *) r)[1]; \
+ w[1] = ((EMUSHORT *) r)[2]; \
+ w[0] = ((EMUSHORT *) r)[3]; \
+ e53toe (w, (e)); } while (0)
+
+#define PUT_REAL(e,r) \
+do { unsigned EMUSHORT w[4]; \
+ etoe53 ((e), w); \
+ *((EMUSHORT *) r) = w[3]; \
+ *((EMUSHORT *) r + 1) = w[2]; \
+ *((EMUSHORT *) r + 2) = w[1]; \
+ *((EMUSHORT *) r + 3) = w[0]; } while (0)
+
+#endif /* endian-ness differs */
+
+#else /* not REAL_ARITHMETIC */
+
+/* emulator uses host format */
+#define GET_REAL(r,e) e53toe ((unsigned EMUSHORT *) (r), (e))
+#define PUT_REAL(e,r) etoe53 ((e), (unsigned EMUSHORT *) (r))
+
+#endif /* not REAL_ARITHMETIC */
+#endif /* not TFmode */
+#endif /* no XFmode */
+
+
+/* Number of 16 bit words in internal format */
+#define NI (NE+3)
+
+/* Array offset to exponent */
+#define E 1
+
+/* Array offset to high guard word */
+#define M 2
+
+/* Number of bits of precision */
+#define NBITS ((NI-4)*16)
+
+/* Maximum number of decimal digits in ASCII conversion
+ * = NBITS*log10(2)
+ */
+#define NDEC (NBITS*8/27)
+
+/* The exponent of 1.0 */
+#define EXONE (0x3fff)
+
+extern int extra_warnings;
+extern unsigned EMUSHORT ezero[], ehalf[], eone[], etwo[];
+extern unsigned EMUSHORT elog2[], esqrt2[];
+
+static void endian PROTO((unsigned EMUSHORT *, long *,
+ enum machine_mode));
+static void eclear PROTO((unsigned EMUSHORT *));
+static void emov PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void eabs PROTO((unsigned EMUSHORT *));
+static void eneg PROTO((unsigned EMUSHORT *));
+static int eisneg PROTO((unsigned EMUSHORT *));
+static int eisinf PROTO((unsigned EMUSHORT *));
+static int eisnan PROTO((unsigned EMUSHORT *));
+static void einfin PROTO((unsigned EMUSHORT *));
+static void enan PROTO((unsigned EMUSHORT *, int));
+static void emovi PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void emovo PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void ecleaz PROTO((unsigned EMUSHORT *));
+static void ecleazs PROTO((unsigned EMUSHORT *));
+static void emovz PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void einan PROTO((unsigned EMUSHORT *));
+static int eiisnan PROTO((unsigned EMUSHORT *));
+static int eiisneg PROTO((unsigned EMUSHORT *));
+static void eiinfin PROTO((unsigned EMUSHORT *));
+static int eiisinf PROTO((unsigned EMUSHORT *));
+static int ecmpm PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void eshdn1 PROTO((unsigned EMUSHORT *));
+static void eshup1 PROTO((unsigned EMUSHORT *));
+static void eshdn8 PROTO((unsigned EMUSHORT *));
+static void eshup8 PROTO((unsigned EMUSHORT *));
+static void eshup6 PROTO((unsigned EMUSHORT *));
+static void eshdn6 PROTO((unsigned EMUSHORT *));
+static void eaddm PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void esubm PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void m16m PROTO((unsigned int, unsigned short *,
+ unsigned short *));
+static int edivm PROTO((unsigned short *, unsigned short *));
+static int emulm PROTO((unsigned short *, unsigned short *));
+static void emdnorm PROTO((unsigned EMUSHORT *, int, int, EMULONG, int));
+static void esub PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *,
+ unsigned EMUSHORT *));
+static void eadd PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *,
+ unsigned EMUSHORT *));
+static void eadd1 PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *,
+ unsigned EMUSHORT *));
+static void ediv PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *,
+ unsigned EMUSHORT *));
+static void emul PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *,
+ unsigned EMUSHORT *));
+static void e53toe PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void e64toe PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void e113toe PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void e24toe PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void etoe113 PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void toe113 PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void etoe64 PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void toe64 PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void etoe53 PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void toe53 PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void etoe24 PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void toe24 PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static int ecmp PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void eround PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void ltoe PROTO((HOST_WIDE_INT *, unsigned EMUSHORT *));
+static void ultoe PROTO((unsigned HOST_WIDE_INT *, unsigned EMUSHORT *));
+static void eifrac PROTO((unsigned EMUSHORT *, HOST_WIDE_INT *,
+ unsigned EMUSHORT *));
+static void euifrac PROTO((unsigned EMUSHORT *, unsigned HOST_WIDE_INT *,
+ unsigned EMUSHORT *));
+static int eshift PROTO((unsigned EMUSHORT *, int));
+static int enormlz PROTO((unsigned EMUSHORT *));
+static void e24toasc PROTO((unsigned EMUSHORT *, char *, int));
+static void e53toasc PROTO((unsigned EMUSHORT *, char *, int));
+static void e64toasc PROTO((unsigned EMUSHORT *, char *, int));
+static void e113toasc PROTO((unsigned EMUSHORT *, char *, int));
+static void etoasc PROTO((unsigned EMUSHORT *, char *, int));
+static void asctoe24 PROTO((char *, unsigned EMUSHORT *));
+static void asctoe53 PROTO((char *, unsigned EMUSHORT *));
+static void asctoe64 PROTO((char *, unsigned EMUSHORT *));
+static void asctoe113 PROTO((char *, unsigned EMUSHORT *));
+static void asctoe PROTO((char *, unsigned EMUSHORT *));
+static void asctoeg PROTO((char *, unsigned EMUSHORT *, int));
+static void efloor PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void efrexp PROTO((unsigned EMUSHORT *, int *,
+ unsigned EMUSHORT *));
+static void eldexp PROTO((unsigned EMUSHORT *, int, unsigned EMUSHORT *));
+static void eremain PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *,
+ unsigned EMUSHORT *));
+static void eiremain PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void mtherr PROTO((char *, int));
+static void dectoe PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void etodec PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void todec PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void ibmtoe PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *,
+ enum machine_mode));
+static void etoibm PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *,
+ enum machine_mode));
+static void toibm PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *,
+ enum machine_mode));
+static void make_nan PROTO((unsigned EMUSHORT *, int, enum machine_mode));
+static void uditoe PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void ditoe PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void etoudi PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void etodi PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+static void esqrt PROTO((unsigned EMUSHORT *, unsigned EMUSHORT *));
+
+/* Copy 32-bit numbers obtained from array containing 16-bit numbers,
+ swapping ends if required, into output array of longs. The
+ result is normally passed to fprintf by the ASM_OUTPUT_ macros. */
+
+static void
+endian (e, x, mode)
+ unsigned EMUSHORT e[];
+ long x[];
+ enum machine_mode mode;
+{
+ unsigned long th, t;
+
+#if FLOAT_WORDS_BIG_ENDIAN
+ switch (mode)
+ {
+
+ case TFmode:
+ /* Swap halfwords in the fourth long. */
+ th = (unsigned long) e[6] & 0xffff;
+ t = (unsigned long) e[7] & 0xffff;
+ t |= th << 16;
+ x[3] = (long) t;
+
+ case XFmode:
+
+ /* Swap halfwords in the third long. */
+ th = (unsigned long) e[4] & 0xffff;
+ t = (unsigned long) e[5] & 0xffff;
+ t |= th << 16;
+ x[2] = (long) t;
+ /* fall into the double case */
+
+ case DFmode:
+
+ /* swap halfwords in the second word */
+ th = (unsigned long) e[2] & 0xffff;
+ t = (unsigned long) e[3] & 0xffff;
+ t |= th << 16;
+ x[1] = (long) t;
+ /* fall into the float case */
+
+ case HFmode:
+ case SFmode:
+
+ /* swap halfwords in the first word */
+ th = (unsigned long) e[0] & 0xffff;
+ t = (unsigned long) e[1] & 0xffff;
+ t |= th << 16;
+ x[0] = t;
+ break;
+
+ default:
+ abort ();
+ }
+
+#else
+
+ /* Pack the output array without swapping. */
+
+ switch (mode)
+ {
+
+ case TFmode:
+
+ /* Pack the fourth long. */
+ th = (unsigned long) e[7] & 0xffff;
+ t = (unsigned long) e[6] & 0xffff;
+ t |= th << 16;
+ x[3] = (long) t;
+
+ case XFmode:
+
+ /* Pack the third long.
+ Each element of the input REAL_VALUE_TYPE array has 16 useful bits
+ in it. */
+ th = (unsigned long) e[5] & 0xffff;
+ t = (unsigned long) e[4] & 0xffff;
+ t |= th << 16;
+ x[2] = (long) t;
+ /* fall into the double case */
+
+ case DFmode:
+
+ /* pack the second long */
+ th = (unsigned long) e[3] & 0xffff;
+ t = (unsigned long) e[2] & 0xffff;
+ t |= th << 16;
+ x[1] = (long) t;
+ /* fall into the float case */
+
+ case HFmode:
+ case SFmode:
+
+ /* pack the first long */
+ th = (unsigned long) e[1] & 0xffff;
+ t = (unsigned long) e[0] & 0xffff;
+ t |= th << 16;
+ x[0] = t;
+ break;
+
+ default:
+ abort ();
+ }
+
+#endif
+}
+
+
+/* This is the implementation of the REAL_ARITHMETIC macro. */
+
+void
+earith (value, icode, r1, r2)
+ REAL_VALUE_TYPE *value;
+ int icode;
+ REAL_VALUE_TYPE *r1;
+ REAL_VALUE_TYPE *r2;
+{
+ unsigned EMUSHORT d1[NE], d2[NE], v[NE];
+ enum tree_code code;
+
+ GET_REAL (r1, d1);
+ GET_REAL (r2, d2);
+#ifdef NANS
+/* Return NaN input back to the caller. */
+ if (eisnan (d1))
+ {
+ PUT_REAL (d1, value);
+ return;
+ }
+ if (eisnan (d2))
+ {
+ PUT_REAL (d2, value);
+ return;
+ }
+#endif
+ code = (enum tree_code) icode;
+ switch (code)
+ {
+ case PLUS_EXPR:
+ eadd (d2, d1, v);
+ break;
+
+ case MINUS_EXPR:
+ esub (d2, d1, v); /* d1 - d2 */
+ break;
+
+ case MULT_EXPR:
+ emul (d2, d1, v);
+ break;
+
+ case RDIV_EXPR:
+#ifndef REAL_INFINITY
+ if (ecmp (d2, ezero) == 0)
+ {
+#ifdef NANS
+ enan (v, eisneg (d1) ^ eisneg (d2));
+ break;
+#else
+ abort ();
+#endif
+ }
+#endif
+ ediv (d2, d1, v); /* d1/d2 */
+ break;
+
+ case MIN_EXPR: /* min (d1,d2) */
+ if (ecmp (d1, d2) < 0)
+ emov (d1, v);
+ else
+ emov (d2, v);
+ break;
+
+ case MAX_EXPR: /* max (d1,d2) */
+ if (ecmp (d1, d2) > 0)
+ emov (d1, v);
+ else
+ emov (d2, v);
+ break;
+ default:
+ emov (ezero, v);
+ break;
+ }
+PUT_REAL (v, value);
+}
+
+
+/* Truncate REAL_VALUE_TYPE toward zero to signed HOST_WIDE_INT.
+ implements REAL_VALUE_RNDZINT (x) (etrunci (x)). */
+
+REAL_VALUE_TYPE
+etrunci (x)
+ REAL_VALUE_TYPE x;
+{
+ unsigned EMUSHORT f[NE], g[NE];
+ REAL_VALUE_TYPE r;
+ HOST_WIDE_INT l;
+
+ GET_REAL (&x, g);
+#ifdef NANS
+ if (eisnan (g))
+ return (x);
+#endif
+ eifrac (g, &l, f);
+ ltoe (&l, g);
+ PUT_REAL (g, &r);
+ return (r);
+}
+
+
+/* Truncate REAL_VALUE_TYPE toward zero to unsigned HOST_WIDE_INT;
+ implements REAL_VALUE_UNSIGNED_RNDZINT (x) (etruncui (x)). */
+
+REAL_VALUE_TYPE
+etruncui (x)
+ REAL_VALUE_TYPE x;
+{
+ unsigned EMUSHORT f[NE], g[NE];
+ REAL_VALUE_TYPE r;
+ unsigned HOST_WIDE_INT l;
+
+ GET_REAL (&x, g);
+#ifdef NANS
+ if (eisnan (g))
+ return (x);
+#endif
+ euifrac (g, &l, f);
+ ultoe (&l, g);
+ PUT_REAL (g, &r);
+ return (r);
+}
+
+
+/* This is the REAL_VALUE_ATOF function. It converts a decimal string to
+ binary, rounding off as indicated by the machine_mode argument. Then it
+ promotes the rounded value to REAL_VALUE_TYPE. */
+
+REAL_VALUE_TYPE
+ereal_atof (s, t)
+ char *s;
+ enum machine_mode t;
+{
+ unsigned EMUSHORT tem[NE], e[NE];
+ REAL_VALUE_TYPE r;
+
+ switch (t)
+ {
+ case HFmode:
+ case SFmode:
+ asctoe24 (s, tem);
+ e24toe (tem, e);
+ break;
+ case DFmode:
+ asctoe53 (s, tem);
+ e53toe (tem, e);
+ break;
+ case XFmode:
+ asctoe64 (s, tem);
+ e64toe (tem, e);
+ break;
+ case TFmode:
+ asctoe113 (s, tem);
+ e113toe (tem, e);
+ break;
+ default:
+ asctoe (s, e);
+ }
+ PUT_REAL (e, &r);
+ return (r);
+}
+
+
+/* Expansion of REAL_NEGATE. */
+
+REAL_VALUE_TYPE
+ereal_negate (x)
+ REAL_VALUE_TYPE x;
+{
+ unsigned EMUSHORT e[NE];
+ REAL_VALUE_TYPE r;
+
+ GET_REAL (&x, e);
+ eneg (e);
+ PUT_REAL (e, &r);
+ return (r);
+}
+
+
+/* Round real toward zero to HOST_WIDE_INT;
+ implements REAL_VALUE_FIX (x). */
+
+HOST_WIDE_INT
+efixi (x)
+ REAL_VALUE_TYPE x;
+{
+ unsigned EMUSHORT f[NE], g[NE];
+ HOST_WIDE_INT l;
+
+ GET_REAL (&x, f);
+#ifdef NANS
+ if (eisnan (f))
+ {
+ warning ("conversion from NaN to int");
+ return (-1);
+ }
+#endif
+ eifrac (f, &l, g);
+ return l;
+}
+
+/* Round real toward zero to unsigned HOST_WIDE_INT
+ implements REAL_VALUE_UNSIGNED_FIX (x).
+ Negative input returns zero. */
+
+unsigned HOST_WIDE_INT
+efixui (x)
+ REAL_VALUE_TYPE x;
+{
+ unsigned EMUSHORT f[NE], g[NE];
+ unsigned HOST_WIDE_INT l;
+
+ GET_REAL (&x, f);
+#ifdef NANS
+ if (eisnan (f))
+ {
+ warning ("conversion from NaN to unsigned int");
+ return (-1);
+ }
+#endif
+ euifrac (f, &l, g);
+ return l;
+}
+
+
+/* REAL_VALUE_FROM_INT macro. */
+
+void
+ereal_from_int (d, i, j)
+ REAL_VALUE_TYPE *d;
+ HOST_WIDE_INT i, j;
+{
+ unsigned EMUSHORT df[NE], dg[NE];
+ HOST_WIDE_INT low, high;
+ int sign;
+
+ sign = 0;
+ low = i;
+ if ((high = j) < 0)
+ {
+ sign = 1;
+ /* complement and add 1 */
+ high = ~high;
+ if (low)
+ low = -low;
+ else
+ high += 1;
+ }
+ eldexp (eone, HOST_BITS_PER_WIDE_INT, df);
+ ultoe ((unsigned HOST_WIDE_INT *) &high, dg);
+ emul (dg, df, dg);
+ ultoe ((unsigned HOST_WIDE_INT *) &low, df);
+ eadd (df, dg, dg);
+ if (sign)
+ eneg (dg);
+ PUT_REAL (dg, d);
+}
+
+
+/* REAL_VALUE_FROM_UNSIGNED_INT macro. */
+
+void
+ereal_from_uint (d, i, j)
+ REAL_VALUE_TYPE *d;
+ unsigned HOST_WIDE_INT i, j;
+{
+ unsigned EMUSHORT df[NE], dg[NE];
+ unsigned HOST_WIDE_INT low, high;
+
+ low = i;
+ high = j;
+ eldexp (eone, HOST_BITS_PER_WIDE_INT, df);
+ ultoe (&high, dg);
+ emul (dg, df, dg);
+ ultoe (&low, df);
+ eadd (df, dg, dg);
+ PUT_REAL (dg, d);
+}
+
+
+/* REAL_VALUE_TO_INT macro. */
+
+void
+ereal_to_int (low, high, rr)
+ HOST_WIDE_INT *low, *high;
+ REAL_VALUE_TYPE rr;
+{
+ unsigned EMUSHORT d[NE], df[NE], dg[NE], dh[NE];
+ int s;
+
+ GET_REAL (&rr, d);
+#ifdef NANS
+ if (eisnan (d))
+ {
+ warning ("conversion from NaN to int");
+ *low = -1;
+ *high = -1;
+ return;
+ }
+#endif
+ /* convert positive value */
+ s = 0;
+ if (eisneg (d))
+ {
+ eneg (d);
+ s = 1;
+ }
+ eldexp (eone, HOST_BITS_PER_WIDE_INT, df);
+ ediv (df, d, dg); /* dg = d / 2^32 is the high word */
+ euifrac (dg, (unsigned HOST_WIDE_INT *) high, dh);
+ emul (df, dh, dg); /* fractional part is the low word */
+ euifrac (dg, (unsigned HOST_WIDE_INT *)low, dh);
+ if (s)
+ {
+ /* complement and add 1 */
+ *high = ~(*high);
+ if (*low)
+ *low = -(*low);
+ else
+ *high += 1;
+ }
+}
+
+
+/* REAL_VALUE_LDEXP macro. */
+
+REAL_VALUE_TYPE
+ereal_ldexp (x, n)
+ REAL_VALUE_TYPE x;
+ int n;
+{
+ unsigned EMUSHORT e[NE], y[NE];
+ REAL_VALUE_TYPE r;
+
+ GET_REAL (&x, e);
+#ifdef NANS
+ if (eisnan (e))
+ return (x);
+#endif
+ eldexp (e, n, y);
+ PUT_REAL (y, &r);
+ return (r);
+}
+
+/* These routines are conditionally compiled because functions
+ of the same names may be defined in fold-const.c. */
+
+#ifdef REAL_ARITHMETIC
+
+/* Check for infinity in a REAL_VALUE_TYPE. */
+
+int
+target_isinf (x)
+ REAL_VALUE_TYPE x;
+{
+ unsigned EMUSHORT e[NE];
+
+#ifdef INFINITY
+ GET_REAL (&x, e);
+ return (eisinf (e));
+#else
+ return 0;
+#endif
+}
+
+
+/* Check whether a REAL_VALUE_TYPE item is a NaN. */
+
+int
+target_isnan (x)
+ REAL_VALUE_TYPE x;
+{
+ unsigned EMUSHORT e[NE];
+
+#ifdef NANS
+ GET_REAL (&x, e);
+ return (eisnan (e));
+#else
+ return (0);
+#endif
+}
+
+
+/* Check for a negative REAL_VALUE_TYPE number.
+ This just checks the sign bit, so that -0 counts as negative. */
+
+int
+target_negative (x)
+ REAL_VALUE_TYPE x;
+{
+ return ereal_isneg (x);
+}
+
+/* Expansion of REAL_VALUE_TRUNCATE.
+ The result is in floating point, rounded to nearest or even. */
+
+REAL_VALUE_TYPE
+real_value_truncate (mode, arg)
+ enum machine_mode mode;
+ REAL_VALUE_TYPE arg;
+{
+ unsigned EMUSHORT e[NE], t[NE];
+ REAL_VALUE_TYPE r;
+
+ GET_REAL (&arg, e);
+#ifdef NANS
+ if (eisnan (e))
+ return (arg);
+#endif
+ eclear (t);
+ switch (mode)
+ {
+ case TFmode:
+ etoe113 (e, t);
+ e113toe (t, t);
+ break;
+
+ case XFmode:
+ etoe64 (e, t);
+ e64toe (t, t);
+ break;
+
+ case DFmode:
+ etoe53 (e, t);
+ e53toe (t, t);
+ break;
+
+ case HFmode:
+ case SFmode:
+ etoe24 (e, t);
+ e24toe (t, t);
+ break;
+
+ case SImode:
+ r = etrunci (arg);
+ return (r);
+
+ /* If an unsupported type was requested, presume that
+ the machine files know something useful to do with
+ the unmodified value. */
+
+ default:
+ return (arg);
+ }
+ PUT_REAL (t, &r);
+ return (r);
+}
+
+#endif /* REAL_ARITHMETIC defined */
+
+/* Used for debugging--print the value of R in human-readable format
+ on stderr. */
+
+void
+debug_real (r)
+ REAL_VALUE_TYPE r;
+{
+ char dstr[30];
+
+ REAL_VALUE_TO_DECIMAL (r, "%.20g", dstr);
+ fprintf (stderr, "%s", dstr);
+}
+
+
+/* Target values are arrays of host longs. A long is guaranteed
+ to be at least 32 bits wide. */
+
+/* 128-bit long double */
+
+void
+etartdouble (r, l)
+ REAL_VALUE_TYPE r;
+ long l[];
+{
+ unsigned EMUSHORT e[NE];
+
+ GET_REAL (&r, e);
+ etoe113 (e, e);
+ endian (e, l, TFmode);
+}
+
+/* 80-bit long double */
+
+void
+etarldouble (r, l)
+ REAL_VALUE_TYPE r;
+ long l[];
+{
+ unsigned EMUSHORT e[NE];
+
+ GET_REAL (&r, e);
+ etoe64 (e, e);
+ endian (e, l, XFmode);
+}
+
+void
+etardouble (r, l)
+ REAL_VALUE_TYPE r;
+ long l[];
+{
+ unsigned EMUSHORT e[NE];
+
+ GET_REAL (&r, e);
+ etoe53 (e, e);
+ endian (e, l, DFmode);
+}
+
+long
+etarsingle (r)
+ REAL_VALUE_TYPE r;
+{
+ unsigned EMUSHORT e[NE];
+ long l;
+
+ GET_REAL (&r, e);
+ etoe24 (e, e);
+ endian (e, &l, SFmode);
+ return ((long) l);
+}
+
+void
+ereal_to_decimal (x, s)
+ REAL_VALUE_TYPE x;
+ char *s;
+{
+ unsigned EMUSHORT e[NE];
+
+ GET_REAL (&x, e);
+ etoasc (e, s, 20);
+}
+
+int
+ereal_cmp (x, y)
+ REAL_VALUE_TYPE x, y;
+{
+ unsigned EMUSHORT ex[NE], ey[NE];
+
+ GET_REAL (&x, ex);
+ GET_REAL (&y, ey);
+ return (ecmp (ex, ey));
+}
+
+int
+ereal_isneg (x)
+ REAL_VALUE_TYPE x;
+{
+ unsigned EMUSHORT ex[NE];
+
+ GET_REAL (&x, ex);
+ return (eisneg (ex));
+}
+
+/* End of REAL_ARITHMETIC interface */
+
+/*
+ Extended precision IEEE binary floating point arithmetic routines
+
+ Numbers are stored in C language as arrays of 16-bit unsigned
+ short integers. The arguments of the routines are pointers to
+ the arrays.
+
+ External e type data structure, simulates Intel 8087 chip
+ temporary real format but possibly with a larger significand:
+
+ NE-1 significand words (least significant word first,
+ most significant bit is normally set)
+ exponent (value = EXONE for 1.0,
+ top bit is the sign)
+
+
+ Internal data structure of a number (a "word" is 16 bits):
+
+ ei[0] sign word (0 for positive, 0xffff for negative)
+ ei[1] biased exponent (value = EXONE for the number 1.0)
+ ei[2] high guard word (always zero after normalization)
+ ei[3]
+ to ei[NI-2] significand (NI-4 significand words,
+ most significant word first,
+ most significant bit is set)
+ ei[NI-1] low guard word (0x8000 bit is rounding place)
+
+
+
+ Routines for external format numbers
+
+ asctoe (string, e) ASCII string to extended double e type
+ asctoe64 (string, &d) ASCII string to long double
+ asctoe53 (string, &d) ASCII string to double
+ asctoe24 (string, &f) ASCII string to single
+ asctoeg (string, e, prec) ASCII string to specified precision
+ e24toe (&f, e) IEEE single precision to e type
+ e53toe (&d, e) IEEE double precision to e type
+ e64toe (&d, e) IEEE long double precision to e type
+ e113toe (&d, e) 128-bit long double precision to e type
+ eabs (e) absolute value
+ eadd (a, b, c) c = b + a
+ eclear (e) e = 0
+ ecmp (a, b) Returns 1 if a > b, 0 if a == b,
+ -1 if a < b, -2 if either a or b is a NaN.
+ ediv (a, b, c) c = b / a
+ efloor (a, b) truncate to integer, toward -infinity
+ efrexp (a, exp, s) extract exponent and significand
+ eifrac (e, &l, frac) e to HOST_WIDE_INT and e type fraction
+ euifrac (e, &l, frac) e to unsigned HOST_WIDE_INT and e type fraction
+ einfin (e) set e to infinity, leaving its sign alone
+ eldexp (a, n, b) multiply by 2**n
+ emov (a, b) b = a
+ emul (a, b, c) c = b * a
+ eneg (e) e = -e
+ eround (a, b) b = nearest integer value to a
+ esub (a, b, c) c = b - a
+ e24toasc (&f, str, n) single to ASCII string, n digits after decimal
+ e53toasc (&d, str, n) double to ASCII string, n digits after decimal
+ e64toasc (&d, str, n) 80-bit long double to ASCII string
+ e113toasc (&d, str, n) 128-bit long double to ASCII string
+ etoasc (e, str, n) e to ASCII string, n digits after decimal
+ etoe24 (e, &f) convert e type to IEEE single precision
+ etoe53 (e, &d) convert e type to IEEE double precision
+ etoe64 (e, &d) convert e type to IEEE long double precision
+ ltoe (&l, e) HOST_WIDE_INT to e type
+ ultoe (&l, e) unsigned HOST_WIDE_INT to e type
+ eisneg (e) 1 if sign bit of e != 0, else 0
+ eisinf (e) 1 if e has maximum exponent (non-IEEE)
+ or is infinite (IEEE)
+ eisnan (e) 1 if e is a NaN
+
+
+ Routines for internal format numbers
+
+ eaddm (ai, bi) add significands, bi = bi + ai
+ ecleaz (ei) ei = 0
+ ecleazs (ei) set ei = 0 but leave its sign alone
+ ecmpm (ai, bi) compare significands, return 1, 0, or -1
+ edivm (ai, bi) divide significands, bi = bi / ai
+ emdnorm (ai,l,s,exp) normalize and round off
+ emovi (a, ai) convert external a to internal ai
+ emovo (ai, a) convert internal ai to external a
+ emovz (ai, bi) bi = ai, low guard word of bi = 0
+ emulm (ai, bi) multiply significands, bi = bi * ai
+ enormlz (ei) left-justify the significand
+ eshdn1 (ai) shift significand and guards down 1 bit
+ eshdn8 (ai) shift down 8 bits
+ eshdn6 (ai) shift down 16 bits
+ eshift (ai, n) shift ai n bits up (or down if n < 0)
+ eshup1 (ai) shift significand and guards up 1 bit
+ eshup8 (ai) shift up 8 bits
+ eshup6 (ai) shift up 16 bits
+ esubm (ai, bi) subtract significands, bi = bi - ai
+ eiisinf (ai) 1 if infinite
+ eiisnan (ai) 1 if a NaN
+ eiisneg (ai) 1 if sign bit of ai != 0, else 0
+ einan (ai) set ai = NaN
+ eiinfin (ai) set ai = infinity
+
+ The result is always normalized and rounded to NI-4 word precision
+ after each arithmetic operation.
+
+ Exception flags are NOT fully supported.
+
+ Signaling NaN's are NOT supported; they are treated the same
+ as quiet NaN's.
+
+ Define INFINITY for support of infinity; otherwise a
+ saturation arithmetic is implemented.
+
+ Define NANS for support of Not-a-Number items; otherwise the
+ arithmetic will never produce a NaN output, and might be confused
+ by a NaN input.
+ If NaN's are supported, the output of `ecmp (a,b)' is -2 if
+ either a or b is a NaN. This means asking `if (ecmp (a,b) < 0)'
+ may not be legitimate. Use `if (ecmp (a,b) == -1)' for `less than'
+ if in doubt.
+
+ Denormals are always supported here where appropriate (e.g., not
+ for conversion to DEC numbers). */
+
+/* Definitions for error codes that are passed to the common error handling
+ routine mtherr.
+
+ For Digital Equipment PDP-11 and VAX computers, certain
+ IBM systems, and others that use numbers with a 56-bit
+ significand, the symbol DEC should be defined. In this
+ mode, most floating point constants are given as arrays
+ of octal integers to eliminate decimal to binary conversion
+ errors that might be introduced by the compiler.
+
+ For computers, such as IBM PC, that follow the IEEE
+ Standard for Binary Floating Point Arithmetic (ANSI/IEEE
+ Std 754-1985), the symbol IBMPC or MIEEE should be defined.
+ These numbers have 53-bit significands. In this mode, constants
+ are provided as arrays of hexadecimal 16 bit integers.
+
+ To accommodate other types of computer arithmetic, all
+ constants are also provided in a normal decimal radix
+ which one can hope are correctly converted to a suitable
+ format by the available C language compiler. To invoke
+ this mode, the symbol UNK is defined.
+
+ An important difference among these modes is a predefined
+ set of machine arithmetic constants for each. The numbers
+ MACHEP (the machine roundoff error), MAXNUM (largest number
+ represented), and several other parameters are preset by
+ the configuration symbol. Check the file const.c to
+ ensure that these values are correct for your computer.
+
+ For ANSI C compatibility, define ANSIC equal to 1. Currently
+ this affects only the atan2 function and others that use it. */
+
+/* Constant definitions for math error conditions. */
+
+#define DOMAIN 1 /* argument domain error */
+#define SING 2 /* argument singularity */
+#define OVERFLOW 3 /* overflow range error */
+#define UNDERFLOW 4 /* underflow range error */
+#define TLOSS 5 /* total loss of precision */
+#define PLOSS 6 /* partial loss of precision */
+#define INVALID 7 /* NaN-producing operation */
+
+/* e type constants used by high precision check routines */
+
+#if LONG_DOUBLE_TYPE_SIZE == 128
+/* 0.0 */
+unsigned EMUSHORT ezero[NE] =
+ {0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,};
+extern unsigned EMUSHORT ezero[];
+
+/* 5.0E-1 */
+unsigned EMUSHORT ehalf[NE] =
+ {0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x8000, 0x3ffe,};
+extern unsigned EMUSHORT ehalf[];
+
+/* 1.0E0 */
+unsigned EMUSHORT eone[NE] =
+ {0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x8000, 0x3fff,};
+extern unsigned EMUSHORT eone[];
+
+/* 2.0E0 */
+unsigned EMUSHORT etwo[NE] =
+ {0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x8000, 0x4000,};
+extern unsigned EMUSHORT etwo[];
+
+/* 3.2E1 */
+unsigned EMUSHORT e32[NE] =
+ {0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x8000, 0x4004,};
+extern unsigned EMUSHORT e32[];
+
+/* 6.93147180559945309417232121458176568075500134360255E-1 */
+unsigned EMUSHORT elog2[NE] =
+ {0x40f3, 0xf6af, 0x03f2, 0xb398,
+ 0xc9e3, 0x79ab, 0150717, 0013767, 0130562, 0x3ffe,};
+extern unsigned EMUSHORT elog2[];
+
+/* 1.41421356237309504880168872420969807856967187537695E0 */
+unsigned EMUSHORT esqrt2[NE] =
+ {0x1d6f, 0xbe9f, 0x754a, 0x89b3,
+ 0x597d, 0x6484, 0174736, 0171463, 0132404, 0x3fff,};
+extern unsigned EMUSHORT esqrt2[];
+
+/* 3.14159265358979323846264338327950288419716939937511E0 */
+unsigned EMUSHORT epi[NE] =
+ {0x2902, 0x1cd1, 0x80dc, 0x628b,
+ 0xc4c6, 0xc234, 0020550, 0155242, 0144417, 0040000,};
+extern unsigned EMUSHORT epi[];
+
+#else
+/* LONG_DOUBLE_TYPE_SIZE is other than 128 */
+unsigned EMUSHORT ezero[NE] =
+ {0, 0000000, 0000000, 0000000, 0000000, 0000000,};
+unsigned EMUSHORT ehalf[NE] =
+ {0, 0000000, 0000000, 0000000, 0100000, 0x3ffe,};
+unsigned EMUSHORT eone[NE] =
+ {0, 0000000, 0000000, 0000000, 0100000, 0x3fff,};
+unsigned EMUSHORT etwo[NE] =
+ {0, 0000000, 0000000, 0000000, 0100000, 0040000,};
+unsigned EMUSHORT e32[NE] =
+ {0, 0000000, 0000000, 0000000, 0100000, 0040004,};
+unsigned EMUSHORT elog2[NE] =
+ {0xc9e4, 0x79ab, 0150717, 0013767, 0130562, 0x3ffe,};
+unsigned EMUSHORT esqrt2[NE] =
+ {0x597e, 0x6484, 0174736, 0171463, 0132404, 0x3fff,};
+unsigned EMUSHORT epi[NE] =
+ {0xc4c6, 0xc234, 0020550, 0155242, 0144417, 0040000,};
+#endif
+
+
+
+/* Control register for rounding precision.
+ This can be set to 113 (if NE=10), 80 (if NE=6), 64, 56, 53, or 24 bits. */
+
+int rndprc = NBITS;
+extern int rndprc;
+
+/* Clear out entire external format number. */
+
+static void
+eclear (x)
+ register unsigned EMUSHORT *x;
+{
+ register int i;
+
+ for (i = 0; i < NE; i++)
+ *x++ = 0;
+}
+
+
+
+/* Move external format number from a to b. */
+
+static void
+emov (a, b)
+ register unsigned EMUSHORT *a, *b;
+{
+ register int i;
+
+ for (i = 0; i < NE; i++)
+ *b++ = *a++;
+}
+
+
+/* Absolute value of external format number. */
+
+static void
+eabs (x)
+ unsigned EMUSHORT x[];
+{
+ /* sign is top bit of last word of external format */
+ x[NE - 1] &= 0x7fff;
+}
+
+/* Negate external format number. */
+
+static void
+eneg (x)
+ unsigned EMUSHORT x[];
+{
+
+ x[NE - 1] ^= 0x8000; /* Toggle the sign bit */
+}
+
+
+
+/* Return 1 if sign bit of external format number is nonzero, else zero. */
+
+static int
+eisneg (x)
+ unsigned EMUSHORT x[];
+{
+
+ if (x[NE - 1] & 0x8000)
+ return (1);
+ else
+ return (0);
+}
+
+
+/* Return 1 if external format number is infinity, else return zero. */
+
+static int
+eisinf (x)
+ unsigned EMUSHORT x[];
+{
+
+#ifdef NANS
+ if (eisnan (x))
+ return (0);
+#endif
+ if ((x[NE - 1] & 0x7fff) == 0x7fff)
+ return (1);
+ else
+ return (0);
+}
+
+
+/* Check if e-type number is not a number. The bit pattern is one that we
+ defined, so we know for sure how to detect it. */
+
+static int
+eisnan (x)
+ unsigned EMUSHORT x[];
+{
+#ifdef NANS
+ int i;
+
+ /* NaN has maximum exponent */
+ if ((x[NE - 1] & 0x7fff) != 0x7fff)
+ return (0);
+ /* ... and non-zero significand field. */
+ for (i = 0; i < NE - 1; i++)
+ {
+ if (*x++ != 0)
+ return (1);
+ }
+#endif
+
+ return (0);
+}
+
+/* Fill external format number with infinity pattern (IEEE)
+ or largest possible number (non-IEEE). */
+
+static void
+einfin (x)
+ register unsigned EMUSHORT *x;
+{
+ register int i;
+
+#ifdef INFINITY
+ for (i = 0; i < NE - 1; i++)
+ *x++ = 0;
+ *x |= 32767;
+#else
+ for (i = 0; i < NE - 1; i++)
+ *x++ = 0xffff;
+ *x |= 32766;
+ if (rndprc < NBITS)
+ {
+ if (rndprc == 113)
+ {
+ *(x - 9) = 0;
+ *(x - 8) = 0;
+ }
+ if (rndprc == 64)
+ {
+ *(x - 5) = 0;
+ }
+ if (rndprc == 53)
+ {
+ *(x - 4) = 0xf800;
+ }
+ else
+ {
+ *(x - 4) = 0;
+ *(x - 3) = 0;
+ *(x - 2) = 0xff00;
+ }
+ }
+#endif
+}
+
+
+/* Output an e-type NaN.
+ This generates Intel's quiet NaN pattern for extended real.
+ The exponent is 7fff, the leading mantissa word is c000. */
+
+static void
+enan (x, sign)
+ register unsigned EMUSHORT *x;
+ int sign;
+{
+ register int i;
+
+ for (i = 0; i < NE - 2; i++)
+ *x++ = 0;
+ *x++ = 0xc000;
+ *x = (sign << 15) | 0x7fff;
+}
+
+
+/* Move in external format number, converting it to internal format. */
+
+static void
+emovi (a, b)
+ unsigned EMUSHORT *a, *b;
+{
+ register unsigned EMUSHORT *p, *q;
+ int i;
+
+ q = b;
+ p = a + (NE - 1); /* point to last word of external number */
+ /* get the sign bit */
+ if (*p & 0x8000)
+ *q++ = 0xffff;
+ else
+ *q++ = 0;
+ /* get the exponent */
+ *q = *p--;
+ *q++ &= 0x7fff; /* delete the sign bit */
+#ifdef INFINITY
+ if ((*(q - 1) & 0x7fff) == 0x7fff)
+ {
+#ifdef NANS
+ if (eisnan (a))
+ {
+ *q++ = 0;
+ for (i = 3; i < NI; i++)
+ *q++ = *p--;
+ return;
+ }
+#endif
+
+ for (i = 2; i < NI; i++)
+ *q++ = 0;
+ return;
+ }
+#endif
+
+ /* clear high guard word */
+ *q++ = 0;
+ /* move in the significand */
+ for (i = 0; i < NE - 1; i++)
+ *q++ = *p--;
+ /* clear low guard word */
+ *q = 0;
+}
+
+
+/* Move internal format number out, converting it to external format. */
+
+static void
+emovo (a, b)
+ unsigned EMUSHORT *a, *b;
+{
+ register unsigned EMUSHORT *p, *q;
+ unsigned EMUSHORT i;
+ int j;
+
+ p = a;
+ q = b + (NE - 1); /* point to output exponent */
+ /* combine sign and exponent */
+ i = *p++;
+ if (i)
+ *q-- = *p++ | 0x8000;
+ else
+ *q-- = *p++;
+#ifdef INFINITY
+ if (*(p - 1) == 0x7fff)
+ {
+#ifdef NANS
+ if (eiisnan (a))
+ {
+ enan (b, eiisneg (a));
+ return;
+ }
+#endif
+ einfin (b);
+ return;
+ }
+#endif
+ /* skip over guard word */
+ ++p;
+ /* move the significand */
+ for (j = 0; j < NE - 1; j++)
+ *q-- = *p++;
+}
+
+/* Clear out internal format number. */
+
+static void
+ecleaz (xi)
+ register unsigned EMUSHORT *xi;
+{
+ register int i;
+
+ for (i = 0; i < NI; i++)
+ *xi++ = 0;
+}
+
+
+/* Same, but don't touch the sign. */
+
+static void
+ecleazs (xi)
+ register unsigned EMUSHORT *xi;
+{
+ register int i;
+
+ ++xi;
+ for (i = 0; i < NI - 1; i++)
+ *xi++ = 0;
+}
+
+
+
+/* Move internal format number from a to b. */
+
+static void
+emovz (a, b)
+ register unsigned EMUSHORT *a, *b;
+{
+ register int i;
+
+ for (i = 0; i < NI - 1; i++)
+ *b++ = *a++;
+ /* clear low guard word */
+ *b = 0;
+}
+
+/* Generate internal format NaN.
+ The explicit pattern for this is maximum exponent and
+ top two significant bits set. */
+
+static void
+einan (x)
+ unsigned EMUSHORT x[];
+{
+
+ ecleaz (x);
+ x[E] = 0x7fff;
+ x[M + 1] = 0xc000;
+}
+
+/* Return nonzero if internal format number is a NaN. */
+
+static int
+eiisnan (x)
+ unsigned EMUSHORT x[];
+{
+ int i;
+
+ if ((x[E] & 0x7fff) == 0x7fff)
+ {
+ for (i = M + 1; i < NI; i++)
+ {
+ if (x[i] != 0)
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/* Return nonzero if sign of internal format number is nonzero. */
+
+static int
+eiisneg (x)
+ unsigned EMUSHORT x[];
+{
+
+ return x[0] != 0;
+}
+
+/* Fill internal format number with infinity pattern.
+ This has maximum exponent and significand all zeros. */
+
+static void
+eiinfin (x)
+ unsigned EMUSHORT x[];
+{
+
+ ecleaz (x);
+ x[E] = 0x7fff;
+}
+
+/* Return nonzero if internal format number is infinite. */
+
+static int
+eiisinf (x)
+ unsigned EMUSHORT x[];
+{
+
+#ifdef NANS
+ if (eiisnan (x))
+ return (0);
+#endif
+ if ((x[E] & 0x7fff) == 0x7fff)
+ return (1);
+ return (0);
+}
+
+
+/* Compare significands of numbers in internal format.
+ Guard words are included in the comparison.
+
+ Returns +1 if a > b
+ 0 if a == b
+ -1 if a < b */
+
+static int
+ecmpm (a, b)
+ register unsigned EMUSHORT *a, *b;
+{
+ int i;
+
+ a += M; /* skip up to significand area */
+ b += M;
+ for (i = M; i < NI; i++)
+ {
+ if (*a++ != *b++)
+ goto difrnt;
+ }
+ return (0);
+
+ difrnt:
+ if (*(--a) > *(--b))
+ return (1);
+ else
+ return (-1);
+}
+
+
+/* Shift significand down by 1 bit. */
+
+static void
+eshdn1 (x)
+ register unsigned EMUSHORT *x;
+{
+ register unsigned EMUSHORT bits;
+ int i;
+
+ x += M; /* point to significand area */
+
+ bits = 0;
+ for (i = M; i < NI; i++)
+ {
+ if (*x & 1)
+ bits |= 1;
+ *x >>= 1;
+ if (bits & 2)
+ *x |= 0x8000;
+ bits <<= 1;
+ ++x;
+ }
+}
+
+
+
+/* Shift significand up by 1 bit. */
+
+static void
+eshup1 (x)
+ register unsigned EMUSHORT *x;
+{
+ register unsigned EMUSHORT bits;
+ int i;
+
+ x += NI - 1;
+ bits = 0;
+
+ for (i = M; i < NI; i++)
+ {
+ if (*x & 0x8000)
+ bits |= 1;
+ *x <<= 1;
+ if (bits & 2)
+ *x |= 1;
+ bits <<= 1;
+ --x;
+ }
+}
+
+
+/* Shift significand down by 8 bits. */
+
+static void
+eshdn8 (x)
+ register unsigned EMUSHORT *x;
+{
+ register unsigned EMUSHORT newbyt, oldbyt;
+ int i;
+
+ x += M;
+ oldbyt = 0;
+ for (i = M; i < NI; i++)
+ {
+ newbyt = *x << 8;
+ *x >>= 8;
+ *x |= oldbyt;
+ oldbyt = newbyt;
+ ++x;
+ }
+}
+
+/* Shift significand up by 8 bits. */
+
+static void
+eshup8 (x)
+ register unsigned EMUSHORT *x;
+{
+ int i;
+ register unsigned EMUSHORT newbyt, oldbyt;
+
+ x += NI - 1;
+ oldbyt = 0;
+
+ for (i = M; i < NI; i++)
+ {
+ newbyt = *x >> 8;
+ *x <<= 8;
+ *x |= oldbyt;
+ oldbyt = newbyt;
+ --x;
+ }
+}
+
+/* Shift significand up by 16 bits. */
+
+static void
+eshup6 (x)
+ register unsigned EMUSHORT *x;
+{
+ int i;
+ register unsigned EMUSHORT *p;
+
+ p = x + M;
+ x += M + 1;
+
+ for (i = M; i < NI - 1; i++)
+ *p++ = *x++;
+
+ *p = 0;
+}
+
+/* Shift significand down by 16 bits. */
+
+static void
+eshdn6 (x)
+ register unsigned EMUSHORT *x;
+{
+ int i;
+ register unsigned EMUSHORT *p;
+
+ x += NI - 1;
+ p = x + 1;
+
+ for (i = M; i < NI - 1; i++)
+ *(--p) = *(--x);
+
+ *(--p) = 0;
+}
+
+/* Add significands. x + y replaces y. */
+
+static void
+eaddm (x, y)
+ unsigned EMUSHORT *x, *y;
+{
+ register unsigned EMULONG a;
+ int i;
+ unsigned int carry;
+
+ x += NI - 1;
+ y += NI - 1;
+ carry = 0;
+ for (i = M; i < NI; i++)
+ {
+ a = (unsigned EMULONG) (*x) + (unsigned EMULONG) (*y) + carry;
+ if (a & 0x10000)
+ carry = 1;
+ else
+ carry = 0;
+ *y = (unsigned EMUSHORT) a;
+ --x;
+ --y;
+ }
+}
+
+/* Subtract significands. y - x replaces y. */
+
+static void
+esubm (x, y)
+ unsigned EMUSHORT *x, *y;
+{
+ unsigned EMULONG a;
+ int i;
+ unsigned int carry;
+
+ x += NI - 1;
+ y += NI - 1;
+ carry = 0;
+ for (i = M; i < NI; i++)
+ {
+ a = (unsigned EMULONG) (*y) - (unsigned EMULONG) (*x) - carry;
+ if (a & 0x10000)
+ carry = 1;
+ else
+ carry = 0;
+ *y = (unsigned EMUSHORT) a;
+ --x;
+ --y;
+ }
+}
+
+
+static unsigned EMUSHORT equot[NI];
+
+
+#if 0
+/* Radix 2 shift-and-add versions of multiply and divide */
+
+
+/* Divide significands */
+
+int
+edivm (den, num)
+ unsigned EMUSHORT den[], num[];
+{
+ int i;
+ register unsigned EMUSHORT *p, *q;
+ unsigned EMUSHORT j;
+
+ p = &equot[0];
+ *p++ = num[0];
+ *p++ = num[1];
+
+ for (i = M; i < NI; i++)
+ {
+ *p++ = 0;
+ }
+
+ /* Use faster compare and subtraction if denominator has only 15 bits of
+ significance. */
+
+ p = &den[M + 2];
+ if (*p++ == 0)
+ {
+ for (i = M + 3; i < NI; i++)
+ {
+ if (*p++ != 0)
+ goto fulldiv;
+ }
+ if ((den[M + 1] & 1) != 0)
+ goto fulldiv;
+ eshdn1 (num);
+ eshdn1 (den);
+
+ p = &den[M + 1];
+ q = &num[M + 1];
+
+ for (i = 0; i < NBITS + 2; i++)
+ {
+ if (*p <= *q)
+ {
+ *q -= *p;
+ j = 1;
+ }
+ else
+ {
+ j = 0;
+ }
+ eshup1 (equot);
+ equot[NI - 2] |= j;
+ eshup1 (num);
+ }
+ goto divdon;
+ }
+
+ /* The number of quotient bits to calculate is NBITS + 1 scaling guard
+ bit + 1 roundoff bit. */
+
+ fulldiv:
+
+ p = &equot[NI - 2];
+ for (i = 0; i < NBITS + 2; i++)
+ {
+ if (ecmpm (den, num) <= 0)
+ {
+ esubm (den, num);
+ j = 1; /* quotient bit = 1 */
+ }
+ else
+ j = 0;
+ eshup1 (equot);
+ *p |= j;
+ eshup1 (num);
+ }
+
+ divdon:
+
+ eshdn1 (equot);
+ eshdn1 (equot);
+
+ /* test for nonzero remainder after roundoff bit */
+ p = &num[M];
+ j = 0;
+ for (i = M; i < NI; i++)
+ {
+ j |= *p++;
+ }
+ if (j)
+ j = 1;
+
+
+ for (i = 0; i < NI; i++)
+ num[i] = equot[i];
+ return ((int) j);
+}
+
+
+/* Multiply significands */
+int
+emulm (a, b)
+ unsigned EMUSHORT a[], b[];
+{
+ unsigned EMUSHORT *p, *q;
+ int i, j, k;
+
+ equot[0] = b[0];
+ equot[1] = b[1];
+ for (i = M; i < NI; i++)
+ equot[i] = 0;
+
+ p = &a[NI - 2];
+ k = NBITS;
+ while (*p == 0) /* significand is not supposed to be zero */
+ {
+ eshdn6 (a);
+ k -= 16;
+ }
+ if ((*p & 0xff) == 0)
+ {
+ eshdn8 (a);
+ k -= 8;
+ }
+
+ q = &equot[NI - 1];
+ j = 0;
+ for (i = 0; i < k; i++)
+ {
+ if (*p & 1)
+ eaddm (b, equot);
+ /* remember if there were any nonzero bits shifted out */
+ if (*q & 1)
+ j |= 1;
+ eshdn1 (a);
+ eshdn1 (equot);
+ }
+
+ for (i = 0; i < NI; i++)
+ b[i] = equot[i];
+
+ /* return flag for lost nonzero bits */
+ return (j);
+}
+
+#else
+
+/* Radix 65536 versions of multiply and divide */
+
+
+/* Multiply significand of e-type number b
+ by 16-bit quantity a, e-type result to c. */
+
+static void
+m16m (a, b, c)
+ unsigned int a;
+ unsigned short b[], c[];
+{
+ register unsigned short *pp;
+ register unsigned long carry;
+ unsigned short *ps;
+ unsigned short p[NI];
+ unsigned long aa, m;
+ int i;
+
+ aa = a;
+ pp = &p[NI-2];
+ *pp++ = 0;
+ *pp = 0;
+ ps = &b[NI-1];
+
+ for (i=M+1; i<NI; i++)
+ {
+ if (*ps == 0)
+ {
+ --ps;
+ --pp;
+ *(pp-1) = 0;
+ }
+ else
+ {
+ m = (unsigned long) aa * *ps--;
+ carry = (m & 0xffff) + *pp;
+ *pp-- = (unsigned short)carry;
+ carry = (carry >> 16) + (m >> 16) + *pp;
+ *pp = (unsigned short)carry;
+ *(pp-1) = carry >> 16;
+ }
+ }
+ for (i=M; i<NI; i++)
+ c[i] = p[i];
+}
+
+
+/* Divide significands. Neither the numerator nor the denominator
+ is permitted to have its high guard word nonzero. */
+
+static int
+edivm (den, num)
+ unsigned short den[], num[];
+{
+ int i;
+ register unsigned short *p;
+ unsigned long tnum;
+ unsigned short j, tdenm, tquot;
+ unsigned short tprod[NI+1];
+
+ p = &equot[0];
+ *p++ = num[0];
+ *p++ = num[1];
+
+ for (i=M; i<NI; i++)
+ {
+ *p++ = 0;
+ }
+ eshdn1 (num);
+ tdenm = den[M+1];
+ for (i=M; i<NI; i++)
+ {
+ /* Find trial quotient digit (the radix is 65536). */
+ tnum = (((unsigned long) num[M]) << 16) + num[M+1];
+
+ /* Do not execute the divide instruction if it will overflow. */
+ if ((tdenm * 0xffffL) < tnum)
+ tquot = 0xffff;
+ else
+ tquot = tnum / tdenm;
+ /* Multiply denominator by trial quotient digit. */
+ m16m ((unsigned int)tquot, den, tprod);
+ /* The quotient digit may have been overestimated. */
+ if (ecmpm (tprod, num) > 0)
+ {
+ tquot -= 1;
+ esubm (den, tprod);
+ if (ecmpm (tprod, num) > 0)
+ {
+ tquot -= 1;
+ esubm (den, tprod);
+ }
+ }
+ esubm (tprod, num);
+ equot[i] = tquot;
+ eshup6(num);
+ }
+ /* test for nonzero remainder after roundoff bit */
+ p = &num[M];
+ j = 0;
+ for (i=M; i<NI; i++)
+ {
+ j |= *p++;
+ }
+ if (j)
+ j = 1;
+
+ for (i=0; i<NI; i++)
+ num[i] = equot[i];
+
+ return ((int)j);
+}
+
+
+
+/* Multiply significands */
+static int
+emulm (a, b)
+ unsigned short a[], b[];
+{
+ unsigned short *p, *q;
+ unsigned short pprod[NI];
+ unsigned short j;
+ int i;
+
+ equot[0] = b[0];
+ equot[1] = b[1];
+ for (i=M; i<NI; i++)
+ equot[i] = 0;
+
+ j = 0;
+ p = &a[NI-1];
+ q = &equot[NI-1];
+ for (i=M+1; i<NI; i++)
+ {
+ if (*p == 0)
+ {
+ --p;
+ }
+ else
+ {
+ m16m ((unsigned int) *p--, b, pprod);
+ eaddm(pprod, equot);
+ }
+ j |= *q;
+ eshdn6(equot);
+ }
+
+ for (i=0; i<NI; i++)
+ b[i] = equot[i];
+
+ /* return flag for lost nonzero bits */
+ return ((int)j);
+}
+#endif
+
+
+/* Normalize and round off.
+
+ The internal format number to be rounded is "s".
+ Input "lost" indicates whether or not the number is exact.
+ This is the so-called sticky bit.
+
+ Input "subflg" indicates whether the number was obtained
+ by a subtraction operation. In that case if lost is nonzero
+ then the number is slightly smaller than indicated.
+
+ Input "exp" is the biased exponent, which may be negative.
+ the exponent field of "s" is ignored but is replaced by
+ "exp" as adjusted by normalization and rounding.
+
+ Input "rcntrl" is the rounding control.
+
+ For future reference: In order for emdnorm to round off denormal
+ significands at the right point, the input exponent must be
+ adjusted to be the actual value it would have after conversion to
+ the final floating point type. This adjustment has been
+ implemented for all type conversions (etoe53, etc.) and decimal
+ conversions, but not for the arithmetic functions (eadd, etc.).
+ Data types having standard 15-bit exponents are not affected by
+ this, but SFmode and DFmode are affected. For example, ediv with
+ rndprc = 24 will not round correctly to 24-bit precision if the
+ result is denormal. */
+
+static int rlast = -1;
+static int rw = 0;
+static unsigned EMUSHORT rmsk = 0;
+static unsigned EMUSHORT rmbit = 0;
+static unsigned EMUSHORT rebit = 0;
+static int re = 0;
+static unsigned EMUSHORT rbit[NI];
+
+static void
+emdnorm (s, lost, subflg, exp, rcntrl)
+ unsigned EMUSHORT s[];
+ int lost;
+ int subflg;
+ EMULONG exp;
+ int rcntrl;
+{
+ int i, j;
+ unsigned EMUSHORT r;
+
+ /* Normalize */
+ j = enormlz (s);
+
+ /* a blank significand could mean either zero or infinity. */
+#ifndef INFINITY
+ if (j > NBITS)
+ {
+ ecleazs (s);
+ return;
+ }
+#endif
+ exp -= j;
+#ifndef INFINITY
+ if (exp >= 32767L)
+ goto overf;
+#else
+ if ((j > NBITS) && (exp < 32767))
+ {
+ ecleazs (s);
+ return;
+ }
+#endif
+ if (exp < 0L)
+ {
+ if (exp > (EMULONG) (-NBITS - 1))
+ {
+ j = (int) exp;
+ i = eshift (s, j);
+ if (i)
+ lost = 1;
+ }
+ else
+ {
+ ecleazs (s);
+ return;
+ }
+ }
+ /* Round off, unless told not to by rcntrl. */
+ if (rcntrl == 0)
+ goto mdfin;
+ /* Set up rounding parameters if the control register changed. */
+ if (rndprc != rlast)
+ {
+ ecleaz (rbit);
+ switch (rndprc)
+ {
+ default:
+ case NBITS:
+ rw = NI - 1; /* low guard word */
+ rmsk = 0xffff;
+ rmbit = 0x8000;
+ re = rw - 1;
+ rebit = 1;
+ break;
+ case 113:
+ rw = 10;
+ rmsk = 0x7fff;
+ rmbit = 0x4000;
+ rebit = 0x8000;
+ re = rw;
+ break;
+ case 64:
+ rw = 7;
+ rmsk = 0xffff;
+ rmbit = 0x8000;
+ re = rw - 1;
+ rebit = 1;
+ break;
+ /* For DEC or IBM arithmetic */
+ case 56:
+ rw = 6;
+ rmsk = 0xff;
+ rmbit = 0x80;
+ rebit = 0x100;
+ re = rw;
+ break;
+ case 53:
+ rw = 6;
+ rmsk = 0x7ff;
+ rmbit = 0x0400;
+ rebit = 0x800;
+ re = rw;
+ break;
+ case 24:
+ rw = 4;
+ rmsk = 0xff;
+ rmbit = 0x80;
+ rebit = 0x100;
+ re = rw;
+ break;
+ }
+ rbit[re] = rebit;
+ rlast = rndprc;
+ }
+
+ /* Shift down 1 temporarily if the data structure has an implied
+ most significant bit and the number is denormal. */
+ if ((exp <= 0) && (rndprc != 64) && (rndprc != NBITS))
+ {
+ lost |= s[NI - 1] & 1;
+ eshdn1 (s);
+ }
+ /* Clear out all bits below the rounding bit,
+ remembering in r if any were nonzero. */
+ r = s[rw] & rmsk;
+ if (rndprc < NBITS)
+ {
+ i = rw + 1;
+ while (i < NI)
+ {
+ if (s[i])
+ r |= 1;
+ s[i] = 0;
+ ++i;
+ }
+ }
+ s[rw] &= ~rmsk;
+ if ((r & rmbit) != 0)
+ {
+ if (r == rmbit)
+ {
+ if (lost == 0)
+ { /* round to even */
+ if ((s[re] & rebit) == 0)
+ goto mddone;
+ }
+ else
+ {
+ if (subflg != 0)
+ goto mddone;
+ }
+ }
+ eaddm (rbit, s);
+ }
+ mddone:
+ if ((exp <= 0) && (rndprc != 64) && (rndprc != NBITS))
+ {
+ eshup1 (s);
+ }
+ if (s[2] != 0)
+ { /* overflow on roundoff */
+ eshdn1 (s);
+ exp += 1;
+ }
+ mdfin:
+ s[NI - 1] = 0;
+ if (exp >= 32767L)
+ {
+#ifndef INFINITY
+ overf:
+#endif
+#ifdef INFINITY
+ s[1] = 32767;
+ for (i = 2; i < NI - 1; i++)
+ s[i] = 0;
+ if (extra_warnings)
+ warning ("floating point overflow");
+#else
+ s[1] = 32766;
+ s[2] = 0;
+ for (i = M + 1; i < NI - 1; i++)
+ s[i] = 0xffff;
+ s[NI - 1] = 0;
+ if ((rndprc < 64) || (rndprc == 113))
+ {
+ s[rw] &= ~rmsk;
+ if (rndprc == 24)
+ {
+ s[5] = 0;
+ s[6] = 0;
+ }
+ }
+#endif
+ return;
+ }
+ if (exp < 0)
+ s[1] = 0;
+ else
+ s[1] = (unsigned EMUSHORT) exp;
+}
+
+
+
+/* Subtract external format numbers. */
+
+static int subflg = 0;
+
+static void
+esub (a, b, c)
+ unsigned EMUSHORT *a, *b, *c;
+{
+
+#ifdef NANS
+ if (eisnan (a))
+ {
+ emov (a, c);
+ return;
+ }
+ if (eisnan (b))
+ {
+ emov (b, c);
+ return;
+ }
+/* Infinity minus infinity is a NaN.
+ Test for subtracting infinities of the same sign. */
+ if (eisinf (a) && eisinf (b)
+ && ((eisneg (a) ^ eisneg (b)) == 0))
+ {
+ mtherr ("esub", INVALID);
+ enan (c, 0);
+ return;
+ }
+#endif
+ subflg = 1;
+ eadd1 (a, b, c);
+}
+
+
+/* Add. */
+
+static void
+eadd (a, b, c)
+ unsigned EMUSHORT *a, *b, *c;
+{
+
+#ifdef NANS
+/* NaN plus anything is a NaN. */
+ if (eisnan (a))
+ {
+ emov (a, c);
+ return;
+ }
+ if (eisnan (b))
+ {
+ emov (b, c);
+ return;
+ }
+/* Infinity minus infinity is a NaN.
+ Test for adding infinities of opposite signs. */
+ if (eisinf (a) && eisinf (b)
+ && ((eisneg (a) ^ eisneg (b)) != 0))
+ {
+ mtherr ("esub", INVALID);
+ enan (c, 0);
+ return;
+ }
+#endif
+ subflg = 0;
+ eadd1 (a, b, c);
+}
+
+static void
+eadd1 (a, b, c)
+ unsigned EMUSHORT *a, *b, *c;
+{
+ unsigned EMUSHORT ai[NI], bi[NI], ci[NI];
+ int i, lost, j, k;
+ EMULONG lt, lta, ltb;
+
+#ifdef INFINITY
+ if (eisinf (a))
+ {
+ emov (a, c);
+ if (subflg)
+ eneg (c);
+ return;
+ }
+ if (eisinf (b))
+ {
+ emov (b, c);
+ return;
+ }
+#endif
+ emovi (a, ai);
+ emovi (b, bi);
+ if (subflg)
+ ai[0] = ~ai[0];
+
+ /* compare exponents */
+ lta = ai[E];
+ ltb = bi[E];
+ lt = lta - ltb;
+ if (lt > 0L)
+ { /* put the larger number in bi */
+ emovz (bi, ci);
+ emovz (ai, bi);
+ emovz (ci, ai);
+ ltb = bi[E];
+ lt = -lt;
+ }
+ lost = 0;
+ if (lt != 0L)
+ {
+ if (lt < (EMULONG) (-NBITS - 1))
+ goto done; /* answer same as larger addend */
+ k = (int) lt;
+ lost = eshift (ai, k); /* shift the smaller number down */
+ }
+ else
+ {
+ /* exponents were the same, so must compare significands */
+ i = ecmpm (ai, bi);
+ if (i == 0)
+ { /* the numbers are identical in magnitude */
+ /* if different signs, result is zero */
+ if (ai[0] != bi[0])
+ {
+ eclear (c);
+ return;
+ }
+ /* if same sign, result is double */
+ /* double denomalized tiny number */
+ if ((bi[E] == 0) && ((bi[3] & 0x8000) == 0))
+ {
+ eshup1 (bi);
+ goto done;
+ }
+ /* add 1 to exponent unless both are zero! */
+ for (j = 1; j < NI - 1; j++)
+ {
+ if (bi[j] != 0)
+ {
+ /* This could overflow, but let emovo take care of that. */
+ ltb += 1;
+ break;
+ }
+ }
+ bi[E] = (unsigned EMUSHORT) ltb;
+ goto done;
+ }
+ if (i > 0)
+ { /* put the larger number in bi */
+ emovz (bi, ci);
+ emovz (ai, bi);
+ emovz (ci, ai);
+ }
+ }
+ if (ai[0] == bi[0])
+ {
+ eaddm (ai, bi);
+ subflg = 0;
+ }
+ else
+ {
+ esubm (ai, bi);
+ subflg = 1;
+ }
+ emdnorm (bi, lost, subflg, ltb, 64);
+
+ done:
+ emovo (bi, c);
+}
+
+
+
+/* Divide. */
+
+static void
+ediv (a, b, c)
+ unsigned EMUSHORT *a, *b, *c;
+{
+ unsigned EMUSHORT ai[NI], bi[NI];
+ int i;
+ EMULONG lt, lta, ltb;
+
+#ifdef NANS
+/* Return any NaN input. */
+ if (eisnan (a))
+ {
+ emov (a, c);
+ return;
+ }
+ if (eisnan (b))
+ {
+ emov (b, c);
+ return;
+ }
+/* Zero over zero, or infinity over infinity, is a NaN. */
+ if (((ecmp (a, ezero) == 0) && (ecmp (b, ezero) == 0))
+ || (eisinf (a) && eisinf (b)))
+ {
+ mtherr ("ediv", INVALID);
+ enan (c, eisneg (a) ^ eisneg (b));
+ return;
+ }
+#endif
+/* Infinity over anything else is infinity. */
+#ifdef INFINITY
+ if (eisinf (b))
+ {
+ if (eisneg (a) ^ eisneg (b))
+ *(c + (NE - 1)) = 0x8000;
+ else
+ *(c + (NE - 1)) = 0;
+ einfin (c);
+ return;
+ }
+/* Anything else over infinity is zero. */
+ if (eisinf (a))
+ {
+ eclear (c);
+ return;
+ }
+#endif
+ emovi (a, ai);
+ emovi (b, bi);
+ lta = ai[E];
+ ltb = bi[E];
+ if (bi[E] == 0)
+ { /* See if numerator is zero. */
+ for (i = 1; i < NI - 1; i++)
+ {
+ if (bi[i] != 0)
+ {
+ ltb -= enormlz (bi);
+ goto dnzro1;
+ }
+ }
+ eclear (c);
+ return;
+ }
+ dnzro1:
+
+ if (ai[E] == 0)
+ { /* possible divide by zero */
+ for (i = 1; i < NI - 1; i++)
+ {
+ if (ai[i] != 0)
+ {
+ lta -= enormlz (ai);
+ goto dnzro2;
+ }
+ }
+ if (ai[0] == bi[0])
+ *(c + (NE - 1)) = 0;
+ else
+ *(c + (NE - 1)) = 0x8000;
+/* Divide by zero is not an invalid operation.
+ It is a divide-by-zero operation! */
+ einfin (c);
+ mtherr ("ediv", SING);
+ return;
+ }
+ dnzro2:
+
+ i = edivm (ai, bi);
+ /* calculate exponent */
+ lt = ltb - lta + EXONE;
+ emdnorm (bi, i, 0, lt, 64);
+ /* set the sign */
+ if (ai[0] == bi[0])
+ bi[0] = 0;
+ else
+ bi[0] = 0Xffff;
+ emovo (bi, c);
+}
+
+
+
+/* Multiply. */
+
+static void
+emul (a, b, c)
+ unsigned EMUSHORT *a, *b, *c;
+{
+ unsigned EMUSHORT ai[NI], bi[NI];
+ int i, j;
+ EMULONG lt, lta, ltb;
+
+#ifdef NANS
+/* NaN times anything is the same NaN. */
+ if (eisnan (a))
+ {
+ emov (a, c);
+ return;
+ }
+ if (eisnan (b))
+ {
+ emov (b, c);
+ return;
+ }
+/* Zero times infinity is a NaN. */
+ if ((eisinf (a) && (ecmp (b, ezero) == 0))
+ || (eisinf (b) && (ecmp (a, ezero) == 0)))
+ {
+ mtherr ("emul", INVALID);
+ enan (c, eisneg (a) ^ eisneg (b));
+ return;
+ }
+#endif
+/* Infinity times anything else is infinity. */
+#ifdef INFINITY
+ if (eisinf (a) || eisinf (b))
+ {
+ if (eisneg (a) ^ eisneg (b))
+ *(c + (NE - 1)) = 0x8000;
+ else
+ *(c + (NE - 1)) = 0;
+ einfin (c);
+ return;
+ }
+#endif
+ emovi (a, ai);
+ emovi (b, bi);
+ lta = ai[E];
+ ltb = bi[E];
+ if (ai[E] == 0)
+ {
+ for (i = 1; i < NI - 1; i++)
+ {
+ if (ai[i] != 0)
+ {
+ lta -= enormlz (ai);
+ goto mnzer1;
+ }
+ }
+ eclear (c);
+ return;
+ }
+ mnzer1:
+
+ if (bi[E] == 0)
+ {
+ for (i = 1; i < NI - 1; i++)
+ {
+ if (bi[i] != 0)
+ {
+ ltb -= enormlz (bi);
+ goto mnzer2;
+ }
+ }
+ eclear (c);
+ return;
+ }
+ mnzer2:
+
+ /* Multiply significands */
+ j = emulm (ai, bi);
+ /* calculate exponent */
+ lt = lta + ltb - (EXONE - 1);
+ emdnorm (bi, j, 0, lt, 64);
+ /* calculate sign of product */
+ if (ai[0] == bi[0])
+ bi[0] = 0;
+ else
+ bi[0] = 0xffff;
+ emovo (bi, c);
+}
+
+
+
+
+/* Convert IEEE double precision to e type. */
+
+static void
+e53toe (pe, y)
+ unsigned EMUSHORT *pe, *y;
+{
+#ifdef DEC
+
+ dectoe (pe, y); /* see etodec.c */
+
+#else
+#ifdef IBM
+
+ ibmtoe (pe, y, DFmode);
+
+#else
+ register unsigned EMUSHORT r;
+ register unsigned EMUSHORT *e, *p;
+ unsigned EMUSHORT yy[NI];
+ int denorm, k;
+
+ e = pe;
+ denorm = 0; /* flag if denormalized number */
+ ecleaz (yy);
+#ifdef IBMPC
+ e += 3;
+#endif
+ r = *e;
+ yy[0] = 0;
+ if (r & 0x8000)
+ yy[0] = 0xffff;
+ yy[M] = (r & 0x0f) | 0x10;
+ r &= ~0x800f; /* strip sign and 4 significand bits */
+#ifdef INFINITY
+ if (r == 0x7ff0)
+ {
+#ifdef NANS
+#ifdef IBMPC
+ if (((pe[3] & 0xf) != 0) || (pe[2] != 0)
+ || (pe[1] != 0) || (pe[0] != 0))
+ {
+ enan (y, yy[0] != 0);
+ return;
+ }
+#else
+ if (((pe[0] & 0xf) != 0) || (pe[1] != 0)
+ || (pe[2] != 0) || (pe[3] != 0))
+ {
+ enan (y, yy[0] != 0);
+ return;
+ }
+#endif
+#endif /* NANS */
+ eclear (y);
+ einfin (y);
+ if (yy[0])
+ eneg (y);
+ return;
+ }
+#endif /* INFINITY */
+ r >>= 4;
+ /* If zero exponent, then the significand is denormalized.
+ So take back the understood high significand bit. */
+
+ if (r == 0)
+ {
+ denorm = 1;
+ yy[M] &= ~0x10;
+ }
+ r += EXONE - 01777;
+ yy[E] = r;
+ p = &yy[M + 1];
+#ifdef IBMPC
+ *p++ = *(--e);
+ *p++ = *(--e);
+ *p++ = *(--e);
+#endif
+#ifdef MIEEE
+ ++e;
+ *p++ = *e++;
+ *p++ = *e++;
+ *p++ = *e++;
+#endif
+ eshift (yy, -5);
+ if (denorm)
+ { /* if zero exponent, then normalize the significand */
+ if ((k = enormlz (yy)) > NBITS)
+ ecleazs (yy);
+ else
+ yy[E] -= (unsigned EMUSHORT) (k - 1);
+ }
+ emovo (yy, y);
+#endif /* not IBM */
+#endif /* not DEC */
+}
+
+static void
+e64toe (pe, y)
+ unsigned EMUSHORT *pe, *y;
+{
+ unsigned EMUSHORT yy[NI];
+ unsigned EMUSHORT *e, *p, *q;
+ int i;
+
+ e = pe;
+ p = yy;
+ for (i = 0; i < NE - 5; i++)
+ *p++ = 0;
+#ifdef IBMPC
+ for (i = 0; i < 5; i++)
+ *p++ = *e++;
+#endif
+/* This precision is not ordinarily supported on DEC or IBM. */
+#ifdef DEC
+ for (i = 0; i < 5; i++)
+ *p++ = *e++;
+#endif
+#ifdef IBM
+ p = &yy[0] + (NE - 1);
+ *p-- = *e++;
+ ++e;
+ for (i = 0; i < 5; i++)
+ *p-- = *e++;
+#endif
+#ifdef MIEEE
+ p = &yy[0] + (NE - 1);
+ *p-- = *e++;
+ ++e;
+ for (i = 0; i < 4; i++)
+ *p-- = *e++;
+#endif
+ p = yy;
+ q = y;
+#ifdef INFINITY
+ if (*p == 0x7fff)
+ {
+#ifdef NANS
+#ifdef IBMPC
+ for (i = 0; i < 4; i++)
+ {
+ if (pe[i] != 0)
+ {
+ enan (y, (*p & 0x8000) != 0);
+ return;
+ }
+ }
+#else
+ for (i = 1; i <= 4; i++)
+ {
+ if (pe[i] != 0)
+ {
+ enan (y, (*p & 0x8000) != 0);
+ return;
+ }
+ }
+#endif
+#endif /* NANS */
+ eclear (y);
+ einfin (y);
+ if (*p & 0x8000)
+ eneg (y);
+ return;
+ }
+#endif /* INFINITY */
+ for (i = 0; i < NE; i++)
+ *q++ = *p++;
+}
+
+
+static void
+e113toe (pe, y)
+ unsigned EMUSHORT *pe, *y;
+{
+ register unsigned EMUSHORT r;
+ unsigned EMUSHORT *e, *p;
+ unsigned EMUSHORT yy[NI];
+ int denorm, i;
+
+ e = pe;
+ denorm = 0;
+ ecleaz (yy);
+#ifdef IBMPC
+ e += 7;
+#endif
+ r = *e;
+ yy[0] = 0;
+ if (r & 0x8000)
+ yy[0] = 0xffff;
+ r &= 0x7fff;
+#ifdef INFINITY
+ if (r == 0x7fff)
+ {
+#ifdef NANS
+#ifdef IBMPC
+ for (i = 0; i < 7; i++)
+ {
+ if (pe[i] != 0)
+ {
+ enan (y, yy[0] != 0);
+ return;
+ }
+ }
+#else
+ for (i = 1; i < 8; i++)
+ {
+ if (pe[i] != 0)
+ {
+ enan (y, yy[0] != 0);
+ return;
+ }
+ }
+#endif
+#endif /* NANS */
+ eclear (y);
+ einfin (y);
+ if (yy[0])
+ eneg (y);
+ return;
+ }
+#endif /* INFINITY */
+ yy[E] = r;
+ p = &yy[M + 1];
+#ifdef IBMPC
+ for (i = 0; i < 7; i++)
+ *p++ = *(--e);
+#endif
+#ifdef MIEEE
+ ++e;
+ for (i = 0; i < 7; i++)
+ *p++ = *e++;
+#endif
+/* If denormal, remove the implied bit; else shift down 1. */
+ if (r == 0)
+ {
+ yy[M] = 0;
+ }
+ else
+ {
+ yy[M] = 1;
+ eshift (yy, -1);
+ }
+ emovo (yy, y);
+}
+
+
+/* Convert IEEE single precision to e type. */
+
+static void
+e24toe (pe, y)
+ unsigned EMUSHORT *pe, *y;
+{
+#ifdef IBM
+
+ ibmtoe (pe, y, SFmode);
+
+#else
+ register unsigned EMUSHORT r;
+ register unsigned EMUSHORT *e, *p;
+ unsigned EMUSHORT yy[NI];
+ int denorm, k;
+
+ e = pe;
+ denorm = 0; /* flag if denormalized number */
+ ecleaz (yy);
+#ifdef IBMPC
+ e += 1;
+#endif
+#ifdef DEC
+ e += 1;
+#endif
+ r = *e;
+ yy[0] = 0;
+ if (r & 0x8000)
+ yy[0] = 0xffff;
+ yy[M] = (r & 0x7f) | 0200;
+ r &= ~0x807f; /* strip sign and 7 significand bits */
+#ifdef INFINITY
+ if (r == 0x7f80)
+ {
+#ifdef NANS
+#ifdef MIEEE
+ if (((pe[0] & 0x7f) != 0) || (pe[1] != 0))
+ {
+ enan (y, yy[0] != 0);
+ return;
+ }
+#else
+ if (((pe[1] & 0x7f) != 0) || (pe[0] != 0))
+ {
+ enan (y, yy[0] != 0);
+ return;
+ }
+#endif
+#endif /* NANS */
+ eclear (y);
+ einfin (y);
+ if (yy[0])
+ eneg (y);
+ return;
+ }
+#endif /* INFINITY */
+ r >>= 7;
+ /* If zero exponent, then the significand is denormalized.
+ So take back the understood high significand bit. */
+ if (r == 0)
+ {
+ denorm = 1;
+ yy[M] &= ~0200;
+ }
+ r += EXONE - 0177;
+ yy[E] = r;
+ p = &yy[M + 1];
+#ifdef IBMPC
+ *p++ = *(--e);
+#endif
+#ifdef DEC
+ *p++ = *(--e);
+#endif
+#ifdef MIEEE
+ ++e;
+ *p++ = *e++;
+#endif
+ eshift (yy, -8);
+ if (denorm)
+ { /* if zero exponent, then normalize the significand */
+ if ((k = enormlz (yy)) > NBITS)
+ ecleazs (yy);
+ else
+ yy[E] -= (unsigned EMUSHORT) (k - 1);
+ }
+ emovo (yy, y);
+#endif /* not IBM */
+}
+
+
+static void
+etoe113 (x, e)
+ unsigned EMUSHORT *x, *e;
+{
+ unsigned EMUSHORT xi[NI];
+ EMULONG exp;
+ int rndsav;
+
+#ifdef NANS
+ if (eisnan (x))
+ {
+ make_nan (e, eisneg (x), TFmode);
+ return;
+ }
+#endif
+ emovi (x, xi);
+ exp = (EMULONG) xi[E];
+#ifdef INFINITY
+ if (eisinf (x))
+ goto nonorm;
+#endif
+ /* round off to nearest or even */
+ rndsav = rndprc;
+ rndprc = 113;
+ emdnorm (xi, 0, 0, exp, 64);
+ rndprc = rndsav;
+ nonorm:
+ toe113 (xi, e);
+}
+
+/* Move out internal format to ieee long double */
+
+static void
+toe113 (a, b)
+ unsigned EMUSHORT *a, *b;
+{
+ register unsigned EMUSHORT *p, *q;
+ unsigned EMUSHORT i;
+
+#ifdef NANS
+ if (eiisnan (a))
+ {
+ make_nan (b, eiisneg (a), TFmode);
+ return;
+ }
+#endif
+ p = a;
+#ifdef MIEEE
+ q = b;
+#else
+ q = b + 7; /* point to output exponent */
+#endif
+
+ /* If not denormal, delete the implied bit. */
+ if (a[E] != 0)
+ {
+ eshup1 (a);
+ }
+ /* combine sign and exponent */
+ i = *p++;
+#ifdef MIEEE
+ if (i)
+ *q++ = *p++ | 0x8000;
+ else
+ *q++ = *p++;
+#else
+ if (i)
+ *q-- = *p++ | 0x8000;
+ else
+ *q-- = *p++;
+#endif
+ /* skip over guard word */
+ ++p;
+ /* move the significand */
+#ifdef MIEEE
+ for (i = 0; i < 7; i++)
+ *q++ = *p++;
+#else
+ for (i = 0; i < 7; i++)
+ *q-- = *p++;
+#endif
+}
+
+static void
+etoe64 (x, e)
+ unsigned EMUSHORT *x, *e;
+{
+ unsigned EMUSHORT xi[NI];
+ EMULONG exp;
+ int rndsav;
+
+#ifdef NANS
+ if (eisnan (x))
+ {
+ make_nan (e, eisneg (x), XFmode);
+ return;
+ }
+#endif
+ emovi (x, xi);
+ /* adjust exponent for offset */
+ exp = (EMULONG) xi[E];
+#ifdef INFINITY
+ if (eisinf (x))
+ goto nonorm;
+#endif
+ /* round off to nearest or even */
+ rndsav = rndprc;
+ rndprc = 64;
+ emdnorm (xi, 0, 0, exp, 64);
+ rndprc = rndsav;
+ nonorm:
+ toe64 (xi, e);
+}
+
+
+/* Move out internal format to ieee long double. */
+
+static void
+toe64 (a, b)
+ unsigned EMUSHORT *a, *b;
+{
+ register unsigned EMUSHORT *p, *q;
+ unsigned EMUSHORT i;
+
+#ifdef NANS
+ if (eiisnan (a))
+ {
+ make_nan (b, eiisneg (a), XFmode);
+ return;
+ }
+#endif
+ p = a;
+#if defined(MIEEE) || defined(IBM)
+ q = b;
+#else
+ q = b + 4; /* point to output exponent */
+#if LONG_DOUBLE_TYPE_SIZE == 96
+ /* Clear the last two bytes of 12-byte Intel format */
+ *(q+1) = 0;
+#endif
+#endif
+
+ /* combine sign and exponent */
+ i = *p++;
+#if defined(MIEEE) || defined(IBM)
+ if (i)
+ *q++ = *p++ | 0x8000;
+ else
+ *q++ = *p++;
+ *q++ = 0;
+#else
+ if (i)
+ *q-- = *p++ | 0x8000;
+ else
+ *q-- = *p++;
+#endif
+ /* skip over guard word */
+ ++p;
+ /* move the significand */
+#if defined(MIEEE) || defined(IBM)
+ for (i = 0; i < 4; i++)
+ *q++ = *p++;
+#else
+ for (i = 0; i < 4; i++)
+ *q-- = *p++;
+#endif
+}
+
+
+/* e type to IEEE double precision. */
+
+#ifdef DEC
+
+static void
+etoe53 (x, e)
+ unsigned EMUSHORT *x, *e;
+{
+ etodec (x, e); /* see etodec.c */
+}
+
+static void
+toe53 (x, y)
+ unsigned EMUSHORT *x, *y;
+{
+ todec (x, y);
+}
+
+#else
+#ifdef IBM
+
+static void
+etoe53 (x, e)
+ unsigned EMUSHORT *x, *e;
+{
+ etoibm (x, e, DFmode);
+}
+
+static void
+toe53 (x, y)
+ unsigned EMUSHORT *x, *y;
+{
+ toibm (x, y, DFmode);
+}
+
+#else /* it's neither DEC nor IBM */
+
+static void
+etoe53 (x, e)
+ unsigned EMUSHORT *x, *e;
+{
+ unsigned EMUSHORT xi[NI];
+ EMULONG exp;
+ int rndsav;
+
+#ifdef NANS
+ if (eisnan (x))
+ {
+ make_nan (e, eisneg (x), DFmode);
+ return;
+ }
+#endif
+ emovi (x, xi);
+ /* adjust exponent for offsets */
+ exp = (EMULONG) xi[E] - (EXONE - 0x3ff);
+#ifdef INFINITY
+ if (eisinf (x))
+ goto nonorm;
+#endif
+ /* round off to nearest or even */
+ rndsav = rndprc;
+ rndprc = 53;
+ emdnorm (xi, 0, 0, exp, 64);
+ rndprc = rndsav;
+ nonorm:
+ toe53 (xi, e);
+}
+
+
+static void
+toe53 (x, y)
+ unsigned EMUSHORT *x, *y;
+{
+ unsigned EMUSHORT i;
+ unsigned EMUSHORT *p;
+
+#ifdef NANS
+ if (eiisnan (x))
+ {
+ make_nan (y, eiisneg (x), DFmode);
+ return;
+ }
+#endif
+ p = &x[0];
+#ifdef IBMPC
+ y += 3;
+#endif
+ *y = 0; /* output high order */
+ if (*p++)
+ *y = 0x8000; /* output sign bit */
+
+ i = *p++;
+ if (i >= (unsigned int) 2047)
+ { /* Saturate at largest number less than infinity. */
+#ifdef INFINITY
+ *y |= 0x7ff0;
+#ifdef IBMPC
+ *(--y) = 0;
+ *(--y) = 0;
+ *(--y) = 0;
+#endif
+#ifdef MIEEE
+ ++y;
+ *y++ = 0;
+ *y++ = 0;
+ *y++ = 0;
+#endif
+#else
+ *y |= (unsigned EMUSHORT) 0x7fef;
+#ifdef IBMPC
+ *(--y) = 0xffff;
+ *(--y) = 0xffff;
+ *(--y) = 0xffff;
+#endif
+#ifdef MIEEE
+ ++y;
+ *y++ = 0xffff;
+ *y++ = 0xffff;
+ *y++ = 0xffff;
+#endif
+#endif
+ return;
+ }
+ if (i == 0)
+ {
+ eshift (x, 4);
+ }
+ else
+ {
+ i <<= 4;
+ eshift (x, 5);
+ }
+ i |= *p++ & (unsigned EMUSHORT) 0x0f; /* *p = xi[M] */
+ *y |= (unsigned EMUSHORT) i; /* high order output already has sign bit set */
+#ifdef IBMPC
+ *(--y) = *p++;
+ *(--y) = *p++;
+ *(--y) = *p;
+#endif
+#ifdef MIEEE
+ ++y;
+ *y++ = *p++;
+ *y++ = *p++;
+ *y++ = *p++;
+#endif
+}
+
+#endif /* not IBM */
+#endif /* not DEC */
+
+
+
+/* e type to IEEE single precision. */
+
+#ifdef IBM
+
+static void
+etoe24 (x, e)
+ unsigned EMUSHORT *x, *e;
+{
+ etoibm (x, e, SFmode);
+}
+
+static void
+toe24 (x, y)
+ unsigned EMUSHORT *x, *y;
+{
+ toibm (x, y, SFmode);
+}
+
+#else
+
+static void
+etoe24 (x, e)
+ unsigned EMUSHORT *x, *e;
+{
+ EMULONG exp;
+ unsigned EMUSHORT xi[NI];
+ int rndsav;
+
+#ifdef NANS
+ if (eisnan (x))
+ {
+ make_nan (e, eisneg (x), SFmode);
+ return;
+ }
+#endif
+ emovi (x, xi);
+ /* adjust exponent for offsets */
+ exp = (EMULONG) xi[E] - (EXONE - 0177);
+#ifdef INFINITY
+ if (eisinf (x))
+ goto nonorm;
+#endif
+ /* round off to nearest or even */
+ rndsav = rndprc;
+ rndprc = 24;
+ emdnorm (xi, 0, 0, exp, 64);
+ rndprc = rndsav;
+ nonorm:
+ toe24 (xi, e);
+}
+
+static void
+toe24 (x, y)
+ unsigned EMUSHORT *x, *y;
+{
+ unsigned EMUSHORT i;
+ unsigned EMUSHORT *p;
+
+#ifdef NANS
+ if (eiisnan (x))
+ {
+ make_nan (y, eiisneg (x), SFmode);
+ return;
+ }
+#endif
+ p = &x[0];
+#ifdef IBMPC
+ y += 1;
+#endif
+#ifdef DEC
+ y += 1;
+#endif
+ *y = 0; /* output high order */
+ if (*p++)
+ *y = 0x8000; /* output sign bit */
+
+ i = *p++;
+/* Handle overflow cases. */
+ if (i >= 255)
+ {
+#ifdef INFINITY
+ *y |= (unsigned EMUSHORT) 0x7f80;
+#ifdef IBMPC
+ *(--y) = 0;
+#endif
+#ifdef DEC
+ *(--y) = 0;
+#endif
+#ifdef MIEEE
+ ++y;
+ *y = 0;
+#endif
+#else /* no INFINITY */
+ *y |= (unsigned EMUSHORT) 0x7f7f;
+#ifdef IBMPC
+ *(--y) = 0xffff;
+#endif
+#ifdef DEC
+ *(--y) = 0xffff;
+#endif
+#ifdef MIEEE
+ ++y;
+ *y = 0xffff;
+#endif
+#ifdef ERANGE
+ errno = ERANGE;
+#endif
+#endif /* no INFINITY */
+ return;
+ }
+ if (i == 0)
+ {
+ eshift (x, 7);
+ }
+ else
+ {
+ i <<= 7;
+ eshift (x, 8);
+ }
+ i |= *p++ & (unsigned EMUSHORT) 0x7f; /* *p = xi[M] */
+ *y |= i; /* high order output already has sign bit set */
+#ifdef IBMPC
+ *(--y) = *p;
+#endif
+#ifdef DEC
+ *(--y) = *p;
+#endif
+#ifdef MIEEE
+ ++y;
+ *y = *p;
+#endif
+}
+#endif /* not IBM */
+
+/* Compare two e type numbers.
+ Return +1 if a > b
+ 0 if a == b
+ -1 if a < b
+ -2 if either a or b is a NaN. */
+
+static int
+ecmp (a, b)
+ unsigned EMUSHORT *a, *b;
+{
+ unsigned EMUSHORT ai[NI], bi[NI];
+ register unsigned EMUSHORT *p, *q;
+ register int i;
+ int msign;
+
+#ifdef NANS
+ if (eisnan (a) || eisnan (b))
+ return (-2);
+#endif
+ emovi (a, ai);
+ p = ai;
+ emovi (b, bi);
+ q = bi;
+
+ if (*p != *q)
+ { /* the signs are different */
+ /* -0 equals + 0 */
+ for (i = 1; i < NI - 1; i++)
+ {
+ if (ai[i] != 0)
+ goto nzro;
+ if (bi[i] != 0)
+ goto nzro;
+ }
+ return (0);
+ nzro:
+ if (*p == 0)
+ return (1);
+ else
+ return (-1);
+ }
+ /* both are the same sign */
+ if (*p == 0)
+ msign = 1;
+ else
+ msign = -1;
+ i = NI - 1;
+ do
+ {
+ if (*p++ != *q++)
+ {
+ goto diff;
+ }
+ }
+ while (--i > 0);
+
+ return (0); /* equality */
+
+
+
+ diff:
+
+ if (*(--p) > *(--q))
+ return (msign); /* p is bigger */
+ else
+ return (-msign); /* p is littler */
+}
+
+
+
+
+/* Find nearest integer to x = floor (x + 0.5). */
+
+static void
+eround (x, y)
+ unsigned EMUSHORT *x, *y;
+{
+ eadd (ehalf, x, y);
+ efloor (y, y);
+}
+
+
+
+
+/* Convert HOST_WIDE_INT to e type. */
+
+static void
+ltoe (lp, y)
+ HOST_WIDE_INT *lp;
+ unsigned EMUSHORT *y;
+{
+ unsigned EMUSHORT yi[NI];
+ unsigned HOST_WIDE_INT ll;
+ int k;
+
+ ecleaz (yi);
+ if (*lp < 0)
+ {
+ /* make it positive */
+ ll = (unsigned HOST_WIDE_INT) (-(*lp));
+ yi[0] = 0xffff; /* put correct sign in the e type number */
+ }
+ else
+ {
+ ll = (unsigned HOST_WIDE_INT) (*lp);
+ }
+ /* move the long integer to yi significand area */
+#if HOST_BITS_PER_WIDE_INT == 64
+ yi[M] = (unsigned EMUSHORT) (ll >> 48);
+ yi[M + 1] = (unsigned EMUSHORT) (ll >> 32);
+ yi[M + 2] = (unsigned EMUSHORT) (ll >> 16);
+ yi[M + 3] = (unsigned EMUSHORT) ll;
+ yi[E] = EXONE + 47; /* exponent if normalize shift count were 0 */
+#else
+ yi[M] = (unsigned EMUSHORT) (ll >> 16);
+ yi[M + 1] = (unsigned EMUSHORT) ll;
+ yi[E] = EXONE + 15; /* exponent if normalize shift count were 0 */
+#endif
+
+ if ((k = enormlz (yi)) > NBITS)/* normalize the significand */
+ ecleaz (yi); /* it was zero */
+ else
+ yi[E] -= (unsigned EMUSHORT) k;/* subtract shift count from exponent */
+ emovo (yi, y); /* output the answer */
+}
+
+/* Convert unsigned HOST_WIDE_INT to e type. */
+
+static void
+ultoe (lp, y)
+ unsigned HOST_WIDE_INT *lp;
+ unsigned EMUSHORT *y;
+{
+ unsigned EMUSHORT yi[NI];
+ unsigned HOST_WIDE_INT ll;
+ int k;
+
+ ecleaz (yi);
+ ll = *lp;
+
+ /* move the long integer to ayi significand area */
+#if HOST_BITS_PER_WIDE_INT == 64
+ yi[M] = (unsigned EMUSHORT) (ll >> 48);
+ yi[M + 1] = (unsigned EMUSHORT) (ll >> 32);
+ yi[M + 2] = (unsigned EMUSHORT) (ll >> 16);
+ yi[M + 3] = (unsigned EMUSHORT) ll;
+ yi[E] = EXONE + 47; /* exponent if normalize shift count were 0 */
+#else
+ yi[M] = (unsigned EMUSHORT) (ll >> 16);
+ yi[M + 1] = (unsigned EMUSHORT) ll;
+ yi[E] = EXONE + 15; /* exponent if normalize shift count were 0 */
+#endif
+
+ if ((k = enormlz (yi)) > NBITS)/* normalize the significand */
+ ecleaz (yi); /* it was zero */
+ else
+ yi[E] -= (unsigned EMUSHORT) k; /* subtract shift count from exponent */
+ emovo (yi, y); /* output the answer */
+}
+
+
+/* Find signed HOST_WIDE_INT integer and floating point fractional
+ parts of e-type (packed internal format) floating point input X.
+ The integer output I has the sign of the input, except that
+ positive overflow is permitted if FIXUNS_TRUNC_LIKE_FIX_TRUNC.
+ The output e-type fraction FRAC is the positive fractional
+ part of abs (X). */
+
+static void
+eifrac (x, i, frac)
+ unsigned EMUSHORT *x;
+ HOST_WIDE_INT *i;
+ unsigned EMUSHORT *frac;
+{
+ unsigned EMUSHORT xi[NI];
+ int j, k;
+ unsigned HOST_WIDE_INT ll;
+
+ emovi (x, xi);
+ k = (int) xi[E] - (EXONE - 1);
+ if (k <= 0)
+ {
+ /* if exponent <= 0, integer = 0 and real output is fraction */
+ *i = 0L;
+ emovo (xi, frac);
+ return;
+ }
+ if (k > (HOST_BITS_PER_WIDE_INT - 1))
+ {
+ /* long integer overflow: output large integer
+ and correct fraction */
+ if (xi[0])
+ *i = ((unsigned HOST_WIDE_INT) 1) << (HOST_BITS_PER_WIDE_INT - 1);
+ else
+ {
+#ifdef FIXUNS_TRUNC_LIKE_FIX_TRUNC
+ /* In this case, let it overflow and convert as if unsigned. */
+ euifrac (x, &ll, frac);
+ *i = (HOST_WIDE_INT) ll;
+ return;
+#else
+ /* In other cases, return the largest positive integer. */
+ *i = (((unsigned HOST_WIDE_INT) 1) << (HOST_BITS_PER_WIDE_INT - 1)) - 1;
+#endif
+ }
+ eshift (xi, k);
+ if (extra_warnings)
+ warning ("overflow on truncation to integer");
+ }
+ else if (k > 16)
+ {
+ /* Shift more than 16 bits: first shift up k-16 mod 16,
+ then shift up by 16's. */
+ j = k - ((k >> 4) << 4);
+ eshift (xi, j);
+ ll = xi[M];
+ k -= j;
+ do
+ {
+ eshup6 (xi);
+ ll = (ll << 16) | xi[M];
+ }
+ while ((k -= 16) > 0);
+ *i = ll;
+ if (xi[0])
+ *i = -(*i);
+ }
+ else
+ {
+ /* shift not more than 16 bits */
+ eshift (xi, k);
+ *i = (HOST_WIDE_INT) xi[M] & 0xffff;
+ if (xi[0])
+ *i = -(*i);
+ }
+ xi[0] = 0;
+ xi[E] = EXONE - 1;
+ xi[M] = 0;
+ if ((k = enormlz (xi)) > NBITS)
+ ecleaz (xi);
+ else
+ xi[E] -= (unsigned EMUSHORT) k;
+
+ emovo (xi, frac);
+}
+
+
+/* Find unsigned HOST_WIDE_INT integer and floating point fractional parts.
+ A negative e type input yields integer output = 0
+ but correct fraction. */
+
+static void
+euifrac (x, i, frac)
+ unsigned EMUSHORT *x;
+ unsigned HOST_WIDE_INT *i;
+ unsigned EMUSHORT *frac;
+{
+ unsigned HOST_WIDE_INT ll;
+ unsigned EMUSHORT xi[NI];
+ int j, k;
+
+ emovi (x, xi);
+ k = (int) xi[E] - (EXONE - 1);
+ if (k <= 0)
+ {
+ /* if exponent <= 0, integer = 0 and argument is fraction */
+ *i = 0L;
+ emovo (xi, frac);
+ return;
+ }
+ if (k > HOST_BITS_PER_WIDE_INT)
+ {
+ /* Long integer overflow: output large integer
+ and correct fraction.
+ Note, the BSD microvax compiler says that ~(0UL)
+ is a syntax error. */
+ *i = ~(0L);
+ eshift (xi, k);
+ if (extra_warnings)
+ warning ("overflow on truncation to unsigned integer");
+ }
+ else if (k > 16)
+ {
+ /* Shift more than 16 bits: first shift up k-16 mod 16,
+ then shift up by 16's. */
+ j = k - ((k >> 4) << 4);
+ eshift (xi, j);
+ ll = xi[M];
+ k -= j;
+ do
+ {
+ eshup6 (xi);
+ ll = (ll << 16) | xi[M];
+ }
+ while ((k -= 16) > 0);
+ *i = ll;
+ }
+ else
+ {
+ /* shift not more than 16 bits */
+ eshift (xi, k);
+ *i = (HOST_WIDE_INT) xi[M] & 0xffff;
+ }
+
+ if (xi[0]) /* A negative value yields unsigned integer 0. */
+ *i = 0L;
+
+ xi[0] = 0;
+ xi[E] = EXONE - 1;
+ xi[M] = 0;
+ if ((k = enormlz (xi)) > NBITS)
+ ecleaz (xi);
+ else
+ xi[E] -= (unsigned EMUSHORT) k;
+
+ emovo (xi, frac);
+}
+
+
+
+/* Shift significand area up or down by the number of bits given by SC. */
+
+static int
+eshift (x, sc)
+ unsigned EMUSHORT *x;
+ int sc;
+{
+ unsigned EMUSHORT lost;
+ unsigned EMUSHORT *p;
+
+ if (sc == 0)
+ return (0);
+
+ lost = 0;
+ p = x + NI - 1;
+
+ if (sc < 0)
+ {
+ sc = -sc;
+ while (sc >= 16)
+ {
+ lost |= *p; /* remember lost bits */
+ eshdn6 (x);
+ sc -= 16;
+ }
+
+ while (sc >= 8)
+ {
+ lost |= *p & 0xff;
+ eshdn8 (x);
+ sc -= 8;
+ }
+
+ while (sc > 0)
+ {
+ lost |= *p & 1;
+ eshdn1 (x);
+ sc -= 1;
+ }
+ }
+ else
+ {
+ while (sc >= 16)
+ {
+ eshup6 (x);
+ sc -= 16;
+ }
+
+ while (sc >= 8)
+ {
+ eshup8 (x);
+ sc -= 8;
+ }
+
+ while (sc > 0)
+ {
+ eshup1 (x);
+ sc -= 1;
+ }
+ }
+ if (lost)
+ lost = 1;
+ return ((int) lost);
+}
+
+
+
+/* Shift normalize the significand area pointed to by argument.
+ Shift count (up = positive) is returned. */
+
+static int
+enormlz (x)
+ unsigned EMUSHORT x[];
+{
+ register unsigned EMUSHORT *p;
+ int sc;
+
+ sc = 0;
+ p = &x[M];
+ if (*p != 0)
+ goto normdn;
+ ++p;
+ if (*p & 0x8000)
+ return (0); /* already normalized */
+ while (*p == 0)
+ {
+ eshup6 (x);
+ sc += 16;
+
+ /* With guard word, there are NBITS+16 bits available.
+ Return true if all are zero. */
+ if (sc > NBITS)
+ return (sc);
+ }
+ /* see if high byte is zero */
+ while ((*p & 0xff00) == 0)
+ {
+ eshup8 (x);
+ sc += 8;
+ }
+ /* now shift 1 bit at a time */
+ while ((*p & 0x8000) == 0)
+ {
+ eshup1 (x);
+ sc += 1;
+ if (sc > NBITS)
+ {
+ mtherr ("enormlz", UNDERFLOW);
+ return (sc);
+ }
+ }
+ return (sc);
+
+ /* Normalize by shifting down out of the high guard word
+ of the significand */
+ normdn:
+
+ if (*p & 0xff00)
+ {
+ eshdn8 (x);
+ sc -= 8;
+ }
+ while (*p != 0)
+ {
+ eshdn1 (x);
+ sc -= 1;
+
+ if (sc < -NBITS)
+ {
+ mtherr ("enormlz", OVERFLOW);
+ return (sc);
+ }
+ }
+ return (sc);
+}
+
+
+
+
+/* Convert e type number to decimal format ASCII string.
+ The constants are for 64 bit precision. */
+
+#define NTEN 12
+#define MAXP 4096
+
+#if LONG_DOUBLE_TYPE_SIZE == 128
+static unsigned EMUSHORT etens[NTEN + 1][NE] =
+{
+ {0x6576, 0x4a92, 0x804a, 0x153f,
+ 0xc94c, 0x979a, 0x8a20, 0x5202, 0xc460, 0x7525,}, /* 10**4096 */
+ {0x6a32, 0xce52, 0x329a, 0x28ce,
+ 0xa74d, 0x5de4, 0xc53d, 0x3b5d, 0x9e8b, 0x5a92,}, /* 10**2048 */
+ {0x526c, 0x50ce, 0xf18b, 0x3d28,
+ 0x650d, 0x0c17, 0x8175, 0x7586, 0xc976, 0x4d48,},
+ {0x9c66, 0x58f8, 0xbc50, 0x5c54,
+ 0xcc65, 0x91c6, 0xa60e, 0xa0ae, 0xe319, 0x46a3,},
+ {0x851e, 0xeab7, 0x98fe, 0x901b,
+ 0xddbb, 0xde8d, 0x9df9, 0xebfb, 0xaa7e, 0x4351,},
+ {0x0235, 0x0137, 0x36b1, 0x336c,
+ 0xc66f, 0x8cdf, 0x80e9, 0x47c9, 0x93ba, 0x41a8,},
+ {0x50f8, 0x25fb, 0xc76b, 0x6b71,
+ 0x3cbf, 0xa6d5, 0xffcf, 0x1f49, 0xc278, 0x40d3,},
+ {0x0000, 0x0000, 0x0000, 0x0000,
+ 0xf020, 0xb59d, 0x2b70, 0xada8, 0x9dc5, 0x4069,},
+ {0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0400, 0xc9bf, 0x8e1b, 0x4034,},
+ {0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x2000, 0xbebc, 0x4019,},
+ {0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x9c40, 0x400c,},
+ {0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xc800, 0x4005,},
+ {0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xa000, 0x4002,}, /* 10**1 */
+};
+
+static unsigned EMUSHORT emtens[NTEN + 1][NE] =
+{
+ {0x2030, 0xcffc, 0xa1c3, 0x8123,
+ 0x2de3, 0x9fde, 0xd2ce, 0x04c8, 0xa6dd, 0x0ad8,}, /* 10**-4096 */
+ {0x8264, 0xd2cb, 0xf2ea, 0x12d4,
+ 0x4925, 0x2de4, 0x3436, 0x534f, 0xceae, 0x256b,}, /* 10**-2048 */
+ {0xf53f, 0xf698, 0x6bd3, 0x0158,
+ 0x87a6, 0xc0bd, 0xda57, 0x82a5, 0xa2a6, 0x32b5,},
+ {0xe731, 0x04d4, 0xe3f2, 0xd332,
+ 0x7132, 0xd21c, 0xdb23, 0xee32, 0x9049, 0x395a,},
+ {0xa23e, 0x5308, 0xfefb, 0x1155,
+ 0xfa91, 0x1939, 0x637a, 0x4325, 0xc031, 0x3cac,},
+ {0xe26d, 0xdbde, 0xd05d, 0xb3f6,
+ 0xac7c, 0xe4a0, 0x64bc, 0x467c, 0xddd0, 0x3e55,},
+ {0x2a20, 0x6224, 0x47b3, 0x98d7,
+ 0x3f23, 0xe9a5, 0xa539, 0xea27, 0xa87f, 0x3f2a,},
+ {0x0b5b, 0x4af2, 0xa581, 0x18ed,
+ 0x67de, 0x94ba, 0x4539, 0x1ead, 0xcfb1, 0x3f94,},
+ {0xbf71, 0xa9b3, 0x7989, 0xbe68,
+ 0x4c2e, 0xe15b, 0xc44d, 0x94be, 0xe695, 0x3fc9,},
+ {0x3d4d, 0x7c3d, 0x36ba, 0x0d2b,
+ 0xfdc2, 0xcefc, 0x8461, 0x7711, 0xabcc, 0x3fe4,},
+ {0xc155, 0xa4a8, 0x404e, 0x6113,
+ 0xd3c3, 0x652b, 0xe219, 0x1758, 0xd1b7, 0x3ff1,},
+ {0xd70a, 0x70a3, 0x0a3d, 0xa3d7,
+ 0x3d70, 0xd70a, 0x70a3, 0x0a3d, 0xa3d7, 0x3ff8,},
+ {0xcccd, 0xcccc, 0xcccc, 0xcccc,
+ 0xcccc, 0xcccc, 0xcccc, 0xcccc, 0xcccc, 0x3ffb,}, /* 10**-1 */
+};
+#else
+/* LONG_DOUBLE_TYPE_SIZE is other than 128 */
+static unsigned EMUSHORT etens[NTEN + 1][NE] =
+{
+ {0xc94c, 0x979a, 0x8a20, 0x5202, 0xc460, 0x7525,}, /* 10**4096 */
+ {0xa74d, 0x5de4, 0xc53d, 0x3b5d, 0x9e8b, 0x5a92,}, /* 10**2048 */
+ {0x650d, 0x0c17, 0x8175, 0x7586, 0xc976, 0x4d48,},
+ {0xcc65, 0x91c6, 0xa60e, 0xa0ae, 0xe319, 0x46a3,},
+ {0xddbc, 0xde8d, 0x9df9, 0xebfb, 0xaa7e, 0x4351,},
+ {0xc66f, 0x8cdf, 0x80e9, 0x47c9, 0x93ba, 0x41a8,},
+ {0x3cbf, 0xa6d5, 0xffcf, 0x1f49, 0xc278, 0x40d3,},
+ {0xf020, 0xb59d, 0x2b70, 0xada8, 0x9dc5, 0x4069,},
+ {0x0000, 0x0000, 0x0400, 0xc9bf, 0x8e1b, 0x4034,},
+ {0x0000, 0x0000, 0x0000, 0x2000, 0xbebc, 0x4019,},
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x9c40, 0x400c,},
+ {0x0000, 0x0000, 0x0000, 0x0000, 0xc800, 0x4005,},
+ {0x0000, 0x0000, 0x0000, 0x0000, 0xa000, 0x4002,}, /* 10**1 */
+};
+
+static unsigned EMUSHORT emtens[NTEN + 1][NE] =
+{
+ {0x2de4, 0x9fde, 0xd2ce, 0x04c8, 0xa6dd, 0x0ad8,}, /* 10**-4096 */
+ {0x4925, 0x2de4, 0x3436, 0x534f, 0xceae, 0x256b,}, /* 10**-2048 */
+ {0x87a6, 0xc0bd, 0xda57, 0x82a5, 0xa2a6, 0x32b5,},
+ {0x7133, 0xd21c, 0xdb23, 0xee32, 0x9049, 0x395a,},
+ {0xfa91, 0x1939, 0x637a, 0x4325, 0xc031, 0x3cac,},
+ {0xac7d, 0xe4a0, 0x64bc, 0x467c, 0xddd0, 0x3e55,},
+ {0x3f24, 0xe9a5, 0xa539, 0xea27, 0xa87f, 0x3f2a,},
+ {0x67de, 0x94ba, 0x4539, 0x1ead, 0xcfb1, 0x3f94,},
+ {0x4c2f, 0xe15b, 0xc44d, 0x94be, 0xe695, 0x3fc9,},
+ {0xfdc2, 0xcefc, 0x8461, 0x7711, 0xabcc, 0x3fe4,},
+ {0xd3c3, 0x652b, 0xe219, 0x1758, 0xd1b7, 0x3ff1,},
+ {0x3d71, 0xd70a, 0x70a3, 0x0a3d, 0xa3d7, 0x3ff8,},
+ {0xcccd, 0xcccc, 0xcccc, 0xcccc, 0xcccc, 0x3ffb,}, /* 10**-1 */
+};
+#endif
+
+static void
+e24toasc (x, string, ndigs)
+ unsigned EMUSHORT x[];
+ char *string;
+ int ndigs;
+{
+ unsigned EMUSHORT w[NI];
+
+ e24toe (x, w);
+ etoasc (w, string, ndigs);
+}
+
+
+static void
+e53toasc (x, string, ndigs)
+ unsigned EMUSHORT x[];
+ char *string;
+ int ndigs;
+{
+ unsigned EMUSHORT w[NI];
+
+ e53toe (x, w);
+ etoasc (w, string, ndigs);
+}
+
+
+static void
+e64toasc (x, string, ndigs)
+ unsigned EMUSHORT x[];
+ char *string;
+ int ndigs;
+{
+ unsigned EMUSHORT w[NI];
+
+ e64toe (x, w);
+ etoasc (w, string, ndigs);
+}
+
+static void
+e113toasc (x, string, ndigs)
+ unsigned EMUSHORT x[];
+ char *string;
+ int ndigs;
+{
+ unsigned EMUSHORT w[NI];
+
+ e113toe (x, w);
+ etoasc (w, string, ndigs);
+}
+
+
+static char wstring[80]; /* working storage for ASCII output */
+
+static void
+etoasc (x, string, ndigs)
+ unsigned EMUSHORT x[];
+ char *string;
+ int ndigs;
+{
+ EMUSHORT digit;
+ unsigned EMUSHORT y[NI], t[NI], u[NI], w[NI];
+ unsigned EMUSHORT *p, *r, *ten;
+ unsigned EMUSHORT sign;
+ int i, j, k, expon, rndsav;
+ char *s, *ss;
+ unsigned EMUSHORT m;
+
+
+ rndsav = rndprc;
+ ss = string;
+ s = wstring;
+ *ss = '\0';
+ *s = '\0';
+#ifdef NANS
+ if (eisnan (x))
+ {
+ sprintf (wstring, " NaN ");
+ goto bxit;
+ }
+#endif
+ rndprc = NBITS; /* set to full precision */
+ emov (x, y); /* retain external format */
+ if (y[NE - 1] & 0x8000)
+ {
+ sign = 0xffff;
+ y[NE - 1] &= 0x7fff;
+ }
+ else
+ {
+ sign = 0;
+ }
+ expon = 0;
+ ten = &etens[NTEN][0];
+ emov (eone, t);
+ /* Test for zero exponent */
+ if (y[NE - 1] == 0)
+ {
+ for (k = 0; k < NE - 1; k++)
+ {
+ if (y[k] != 0)
+ goto tnzro; /* denormalized number */
+ }
+ goto isone; /* legal all zeros */
+ }
+ tnzro:
+
+ /* Test for infinity. */
+ if (y[NE - 1] == 0x7fff)
+ {
+ if (sign)
+ sprintf (wstring, " -Infinity ");
+ else
+ sprintf (wstring, " Infinity ");
+ goto bxit;
+ }
+
+ /* Test for exponent nonzero but significand denormalized.
+ * This is an error condition.
+ */
+ if ((y[NE - 1] != 0) && ((y[NE - 2] & 0x8000) == 0))
+ {
+ mtherr ("etoasc", DOMAIN);
+ sprintf (wstring, "NaN");
+ goto bxit;
+ }
+
+ /* Compare to 1.0 */
+ i = ecmp (eone, y);
+ if (i == 0)
+ goto isone;
+
+ if (i == -2)
+ abort ();
+
+ if (i < 0)
+ { /* Number is greater than 1 */
+ /* Convert significand to an integer and strip trailing decimal zeros. */
+ emov (y, u);
+ u[NE - 1] = EXONE + NBITS - 1;
+
+ p = &etens[NTEN - 4][0];
+ m = 16;
+ do
+ {
+ ediv (p, u, t);
+ efloor (t, w);
+ for (j = 0; j < NE - 1; j++)
+ {
+ if (t[j] != w[j])
+ goto noint;
+ }
+ emov (t, u);
+ expon += (int) m;
+ noint:
+ p += NE;
+ m >>= 1;
+ }
+ while (m != 0);
+
+ /* Rescale from integer significand */
+ u[NE - 1] += y[NE - 1] - (unsigned int) (EXONE + NBITS - 1);
+ emov (u, y);
+ /* Find power of 10 */
+ emov (eone, t);
+ m = MAXP;
+ p = &etens[0][0];
+ /* An unordered compare result shouldn't happen here. */
+ while (ecmp (ten, u) <= 0)
+ {
+ if (ecmp (p, u) <= 0)
+ {
+ ediv (p, u, u);
+ emul (p, t, t);
+ expon += (int) m;
+ }
+ m >>= 1;
+ if (m == 0)
+ break;
+ p += NE;
+ }
+ }
+ else
+ { /* Number is less than 1.0 */
+ /* Pad significand with trailing decimal zeros. */
+ if (y[NE - 1] == 0)
+ {
+ while ((y[NE - 2] & 0x8000) == 0)
+ {
+ emul (ten, y, y);
+ expon -= 1;
+ }
+ }
+ else
+ {
+ emovi (y, w);
+ for (i = 0; i < NDEC + 1; i++)
+ {
+ if ((w[NI - 1] & 0x7) != 0)
+ break;
+ /* multiply by 10 */
+ emovz (w, u);
+ eshdn1 (u);
+ eshdn1 (u);
+ eaddm (w, u);
+ u[1] += 3;
+ while (u[2] != 0)
+ {
+ eshdn1 (u);
+ u[1] += 1;
+ }
+ if (u[NI - 1] != 0)
+ break;
+ if (eone[NE - 1] <= u[1])
+ break;
+ emovz (u, w);
+ expon -= 1;
+ }
+ emovo (w, y);
+ }
+ k = -MAXP;
+ p = &emtens[0][0];
+ r = &etens[0][0];
+ emov (y, w);
+ emov (eone, t);
+ while (ecmp (eone, w) > 0)
+ {
+ if (ecmp (p, w) >= 0)
+ {
+ emul (r, w, w);
+ emul (r, t, t);
+ expon += k;
+ }
+ k /= 2;
+ if (k == 0)
+ break;
+ p += NE;
+ r += NE;
+ }
+ ediv (t, eone, t);
+ }
+ isone:
+ /* Find the first (leading) digit. */
+ emovi (t, w);
+ emovz (w, t);
+ emovi (y, w);
+ emovz (w, y);
+ eiremain (t, y);
+ digit = equot[NI - 1];
+ while ((digit == 0) && (ecmp (y, ezero) != 0))
+ {
+ eshup1 (y);
+ emovz (y, u);
+ eshup1 (u);
+ eshup1 (u);
+ eaddm (u, y);
+ eiremain (t, y);
+ digit = equot[NI - 1];
+ expon -= 1;
+ }
+ s = wstring;
+ if (sign)
+ *s++ = '-';
+ else
+ *s++ = ' ';
+ /* Examine number of digits requested by caller. */
+ if (ndigs < 0)
+ ndigs = 0;
+ if (ndigs > NDEC)
+ ndigs = NDEC;
+ if (digit == 10)
+ {
+ *s++ = '1';
+ *s++ = '.';
+ if (ndigs > 0)
+ {
+ *s++ = '0';
+ ndigs -= 1;
+ }
+ expon += 1;
+ }
+ else
+ {
+ *s++ = (char)digit + '0';
+ *s++ = '.';
+ }
+ /* Generate digits after the decimal point. */
+ for (k = 0; k <= ndigs; k++)
+ {
+ /* multiply current number by 10, without normalizing */
+ eshup1 (y);
+ emovz (y, u);
+ eshup1 (u);
+ eshup1 (u);
+ eaddm (u, y);
+ eiremain (t, y);
+ *s++ = (char) equot[NI - 1] + '0';
+ }
+ digit = equot[NI - 1];
+ --s;
+ ss = s;
+ /* round off the ASCII string */
+ if (digit > 4)
+ {
+ /* Test for critical rounding case in ASCII output. */
+ if (digit == 5)
+ {
+ emovo (y, t);
+ if (ecmp (t, ezero) != 0)
+ goto roun; /* round to nearest */
+ if ((*(s - 1) & 1) == 0)
+ goto doexp; /* round to even */
+ }
+ /* Round up and propagate carry-outs */
+ roun:
+ --s;
+ k = *s & 0x7f;
+ /* Carry out to most significant digit? */
+ if (k == '.')
+ {
+ --s;
+ k = *s;
+ k += 1;
+ *s = (char) k;
+ /* Most significant digit carries to 10? */
+ if (k > '9')
+ {
+ expon += 1;
+ *s = '1';
+ }
+ goto doexp;
+ }
+ /* Round up and carry out from less significant digits */
+ k += 1;
+ *s = (char) k;
+ if (k > '9')
+ {
+ *s = '0';
+ goto roun;
+ }
+ }
+ doexp:
+ /*
+ if (expon >= 0)
+ sprintf (ss, "e+%d", expon);
+ else
+ sprintf (ss, "e%d", expon);
+ */
+ sprintf (ss, "e%d", expon);
+ bxit:
+ rndprc = rndsav;
+ /* copy out the working string */
+ s = string;
+ ss = wstring;
+ while (*ss == ' ') /* strip possible leading space */
+ ++ss;
+ while ((*s++ = *ss++) != '\0')
+ ;
+}
+
+
+/* Convert ASCII string to quadruple precision floating point
+
+ Numeric input is free field decimal number with max of 15 digits with or
+ without decimal point entered as ASCII from teletype. Entering E after
+ the number followed by a second number causes the second number to be
+ interpreted as a power of 10 to be multiplied by the first number
+ (i.e., "scientific" notation). */
+
+/* ASCII to single */
+
+static void
+asctoe24 (s, y)
+ char *s;
+ unsigned EMUSHORT *y;
+{
+ asctoeg (s, y, 24);
+}
+
+
+/* ASCII to double */
+
+static void
+asctoe53 (s, y)
+ char *s;
+ unsigned EMUSHORT *y;
+{
+#if defined(DEC) || defined(IBM)
+ asctoeg (s, y, 56);
+#else
+ asctoeg (s, y, 53);
+#endif
+}
+
+
+/* ASCII to long double */
+
+static void
+asctoe64 (s, y)
+ char *s;
+ unsigned EMUSHORT *y;
+{
+ asctoeg (s, y, 64);
+}
+
+/* ASCII to 128-bit long double */
+
+static void
+asctoe113 (s, y)
+ char *s;
+ unsigned EMUSHORT *y;
+{
+ asctoeg (s, y, 113);
+}
+
+/* ASCII to super double */
+
+static void
+asctoe (s, y)
+ char *s;
+ unsigned EMUSHORT *y;
+{
+ asctoeg (s, y, NBITS);
+}
+
+
+/* ASCII to e type, with specified rounding precision = oprec. */
+
+static void
+asctoeg (ss, y, oprec)
+ char *ss;
+ unsigned EMUSHORT *y;
+ int oprec;
+{
+ unsigned EMUSHORT yy[NI], xt[NI], tt[NI];
+ int esign, decflg, sgnflg, nexp, exp, prec, lost;
+ int k, trail, c, rndsav;
+ EMULONG lexp;
+ unsigned EMUSHORT nsign, *p;
+ char *sp, *s, *lstr;
+
+ /* Copy the input string. */
+ lstr = (char *) alloca (strlen (ss) + 1);
+ s = ss;
+ while (*s == ' ') /* skip leading spaces */
+ ++s;
+ sp = lstr;
+ while ((*sp++ = *s++) != '\0')
+ ;
+ s = lstr;
+
+ rndsav = rndprc;
+ rndprc = NBITS; /* Set to full precision */
+ lost = 0;
+ nsign = 0;
+ decflg = 0;
+ sgnflg = 0;
+ nexp = 0;
+ exp = 0;
+ prec = 0;
+ ecleaz (yy);
+ trail = 0;
+
+ nxtcom:
+ k = *s - '0';
+ if ((k >= 0) && (k <= 9))
+ {
+ /* Ignore leading zeros */
+ if ((prec == 0) && (decflg == 0) && (k == 0))
+ goto donchr;
+ /* Identify and strip trailing zeros after the decimal point. */
+ if ((trail == 0) && (decflg != 0))
+ {
+ sp = s;
+ while ((*sp >= '0') && (*sp <= '9'))
+ ++sp;
+ /* Check for syntax error */
+ c = *sp & 0x7f;
+ if ((c != 'e') && (c != 'E') && (c != '\0')
+ && (c != '\n') && (c != '\r') && (c != ' ')
+ && (c != ','))
+ goto error;
+ --sp;
+ while (*sp == '0')
+ *sp-- = 'z';
+ trail = 1;
+ if (*s == 'z')
+ goto donchr;
+ }
+
+ /* If enough digits were given to more than fill up the yy register,
+ continuing until overflow into the high guard word yy[2]
+ guarantees that there will be a roundoff bit at the top
+ of the low guard word after normalization. */
+
+ if (yy[2] == 0)
+ {
+ if (decflg)
+ nexp += 1; /* count digits after decimal point */
+ eshup1 (yy); /* multiply current number by 10 */
+ emovz (yy, xt);
+ eshup1 (xt);
+ eshup1 (xt);
+ eaddm (xt, yy);
+ ecleaz (xt);
+ xt[NI - 2] = (unsigned EMUSHORT) k;
+ eaddm (xt, yy);
+ }
+ else
+ {
+ /* Mark any lost non-zero digit. */
+ lost |= k;
+ /* Count lost digits before the decimal point. */
+ if (decflg == 0)
+ nexp -= 1;
+ }
+ prec += 1;
+ goto donchr;
+ }
+
+ switch (*s)
+ {
+ case 'z':
+ break;
+ case 'E':
+ case 'e':
+ goto expnt;
+ case '.': /* decimal point */
+ if (decflg)
+ goto error;
+ ++decflg;
+ break;
+ case '-':
+ nsign = 0xffff;
+ if (sgnflg)
+ goto error;
+ ++sgnflg;
+ break;
+ case '+':
+ if (sgnflg)
+ goto error;
+ ++sgnflg;
+ break;
+ case ',':
+ case ' ':
+ case '\0':
+ case '\n':
+ case '\r':
+ goto daldone;
+ case 'i':
+ case 'I':
+ goto infinite;
+ default:
+ error:
+#ifdef NANS
+ einan (yy);
+#else
+ mtherr ("asctoe", DOMAIN);
+ eclear (yy);
+#endif
+ goto aexit;
+ }
+ donchr:
+ ++s;
+ goto nxtcom;
+
+ /* Exponent interpretation */
+ expnt:
+
+ esign = 1;
+ exp = 0;
+ ++s;
+ /* check for + or - */
+ if (*s == '-')
+ {
+ esign = -1;
+ ++s;
+ }
+ if (*s == '+')
+ ++s;
+ while ((*s >= '0') && (*s <= '9'))
+ {
+ exp *= 10;
+ exp += *s++ - '0';
+ if (exp > -(MINDECEXP))
+ {
+ if (esign < 0)
+ goto zero;
+ else
+ goto infinite;
+ }
+ }
+ if (esign < 0)
+ exp = -exp;
+ if (exp > MAXDECEXP)
+ {
+ infinite:
+ ecleaz (yy);
+ yy[E] = 0x7fff; /* infinity */
+ goto aexit;
+ }
+ if (exp < MINDECEXP)
+ {
+ zero:
+ ecleaz (yy);
+ goto aexit;
+ }
+
+ daldone:
+ nexp = exp - nexp;
+ /* Pad trailing zeros to minimize power of 10, per IEEE spec. */
+ while ((nexp > 0) && (yy[2] == 0))
+ {
+ emovz (yy, xt);
+ eshup1 (xt);
+ eshup1 (xt);
+ eaddm (yy, xt);
+ eshup1 (xt);
+ if (xt[2] != 0)
+ break;
+ nexp -= 1;
+ emovz (xt, yy);
+ }
+ if ((k = enormlz (yy)) > NBITS)
+ {
+ ecleaz (yy);
+ goto aexit;
+ }
+ lexp = (EXONE - 1 + NBITS) - k;
+ emdnorm (yy, lost, 0, lexp, 64);
+
+ /* Convert to external format:
+
+ Multiply by 10**nexp. If precision is 64 bits,
+ the maximum relative error incurred in forming 10**n
+ for 0 <= n <= 324 is 8.2e-20, at 10**180.
+ For 0 <= n <= 999, the peak relative error is 1.4e-19 at 10**947.
+ For 0 >= n >= -999, it is -1.55e-19 at 10**-435. */
+
+ lexp = yy[E];
+ if (nexp == 0)
+ {
+ k = 0;
+ goto expdon;
+ }
+ esign = 1;
+ if (nexp < 0)
+ {
+ nexp = -nexp;
+ esign = -1;
+ if (nexp > 4096)
+ {
+ /* Punt. Can't handle this without 2 divides. */
+ emovi (etens[0], tt);
+ lexp -= tt[E];
+ k = edivm (tt, yy);
+ lexp += EXONE;
+ nexp -= 4096;
+ }
+ }
+ p = &etens[NTEN][0];
+ emov (eone, xt);
+ exp = 1;
+ do
+ {
+ if (exp & nexp)
+ emul (p, xt, xt);
+ p -= NE;
+ exp = exp + exp;
+ }
+ while (exp <= MAXP);
+
+ emovi (xt, tt);
+ if (esign < 0)
+ {
+ lexp -= tt[E];
+ k = edivm (tt, yy);
+ lexp += EXONE;
+ }
+ else
+ {
+ lexp += tt[E];
+ k = emulm (tt, yy);
+ lexp -= EXONE - 1;
+ }
+
+ expdon:
+
+ /* Round and convert directly to the destination type */
+ if (oprec == 53)
+ lexp -= EXONE - 0x3ff;
+#ifdef IBM
+ else if (oprec == 24 || oprec == 56)
+ lexp -= EXONE - (0x41 << 2);
+#else
+ else if (oprec == 24)
+ lexp -= EXONE - 0177;
+#endif
+#ifdef DEC
+ else if (oprec == 56)
+ lexp -= EXONE - 0201;
+#endif
+ rndprc = oprec;
+ emdnorm (yy, k, 0, lexp, 64);
+
+ aexit:
+
+ rndprc = rndsav;
+ yy[0] = nsign;
+ switch (oprec)
+ {
+#ifdef DEC
+ case 56:
+ todec (yy, y); /* see etodec.c */
+ break;
+#endif
+#ifdef IBM
+ case 56:
+ toibm (yy, y, DFmode);
+ break;
+#endif
+ case 53:
+ toe53 (yy, y);
+ break;
+ case 24:
+ toe24 (yy, y);
+ break;
+ case 64:
+ toe64 (yy, y);
+ break;
+ case 113:
+ toe113 (yy, y);
+ break;
+ case NBITS:
+ emovo (yy, y);
+ break;
+ }
+}
+
+
+
+/* y = largest integer not greater than x (truncated toward minus infinity) */
+
+static unsigned EMUSHORT bmask[] =
+{
+ 0xffff,
+ 0xfffe,
+ 0xfffc,
+ 0xfff8,
+ 0xfff0,
+ 0xffe0,
+ 0xffc0,
+ 0xff80,
+ 0xff00,
+ 0xfe00,
+ 0xfc00,
+ 0xf800,
+ 0xf000,
+ 0xe000,
+ 0xc000,
+ 0x8000,
+ 0x0000,
+};
+
+static void
+efloor (x, y)
+ unsigned EMUSHORT x[], y[];
+{
+ register unsigned EMUSHORT *p;
+ int e, expon, i;
+ unsigned EMUSHORT f[NE];
+
+ emov (x, f); /* leave in external format */
+ expon = (int) f[NE - 1];
+ e = (expon & 0x7fff) - (EXONE - 1);
+ if (e <= 0)
+ {
+ eclear (y);
+ goto isitneg;
+ }
+ /* number of bits to clear out */
+ e = NBITS - e;
+ emov (f, y);
+ if (e <= 0)
+ return;
+
+ p = &y[0];
+ while (e >= 16)
+ {
+ *p++ = 0;
+ e -= 16;
+ }
+ /* clear the remaining bits */
+ *p &= bmask[e];
+ /* truncate negatives toward minus infinity */
+ isitneg:
+
+ if ((unsigned EMUSHORT) expon & (unsigned EMUSHORT) 0x8000)
+ {
+ for (i = 0; i < NE - 1; i++)
+ {
+ if (f[i] != y[i])
+ {
+ esub (eone, y, y);
+ break;
+ }
+ }
+ }
+}
+
+
+/* Returns s and exp such that s * 2**exp = x and .5 <= s < 1.
+ For example, 1.1 = 0.55 * 2**1
+ Handles denormalized numbers properly using long integer exp. */
+
+static void
+efrexp (x, exp, s)
+ unsigned EMUSHORT x[];
+ int *exp;
+ unsigned EMUSHORT s[];
+{
+ unsigned EMUSHORT xi[NI];
+ EMULONG li;
+
+ emovi (x, xi);
+ li = (EMULONG) ((EMUSHORT) xi[1]);
+
+ if (li == 0)
+ {
+ li -= enormlz (xi);
+ }
+ xi[1] = 0x3ffe;
+ emovo (xi, s);
+ *exp = (int) (li - 0x3ffe);
+}
+
+
+
+/* Return y = x * 2**pwr2. */
+
+static void
+eldexp (x, pwr2, y)
+ unsigned EMUSHORT x[];
+ int pwr2;
+ unsigned EMUSHORT y[];
+{
+ unsigned EMUSHORT xi[NI];
+ EMULONG li;
+ int i;
+
+ emovi (x, xi);
+ li = xi[1];
+ li += pwr2;
+ i = 0;
+ emdnorm (xi, i, i, li, 64);
+ emovo (xi, y);
+}
+
+
+/* c = remainder after dividing b by a
+ Least significant integer quotient bits left in equot[]. */
+
+static void
+eremain (a, b, c)
+ unsigned EMUSHORT a[], b[], c[];
+{
+ unsigned EMUSHORT den[NI], num[NI];
+
+#ifdef NANS
+ if (eisinf (b)
+ || (ecmp (a, ezero) == 0)
+ || eisnan (a)
+ || eisnan (b))
+ {
+ enan (c, 0);
+ return;
+ }
+#endif
+ if (ecmp (a, ezero) == 0)
+ {
+ mtherr ("eremain", SING);
+ eclear (c);
+ return;
+ }
+ emovi (a, den);
+ emovi (b, num);
+ eiremain (den, num);
+ /* Sign of remainder = sign of quotient */
+ if (a[0] == b[0])
+ num[0] = 0;
+ else
+ num[0] = 0xffff;
+ emovo (num, c);
+}
+
+static void
+eiremain (den, num)
+ unsigned EMUSHORT den[], num[];
+{
+ EMULONG ld, ln;
+ unsigned EMUSHORT j;
+
+ ld = den[E];
+ ld -= enormlz (den);
+ ln = num[E];
+ ln -= enormlz (num);
+ ecleaz (equot);
+ while (ln >= ld)
+ {
+ if (ecmpm (den, num) <= 0)
+ {
+ esubm (den, num);
+ j = 1;
+ }
+ else
+ {
+ j = 0;
+ }
+ eshup1 (equot);
+ equot[NI - 1] |= j;
+ eshup1 (num);
+ ln -= 1;
+ }
+ emdnorm (num, 0, 0, ln, 0);
+}
+
+/* This routine may be called to report one of the following
+ error conditions (in the include file mconf.h).
+
+ Mnemonic Value Significance
+
+ DOMAIN 1 argument domain error
+ SING 2 function singularity
+ OVERFLOW 3 overflow range error
+ UNDERFLOW 4 underflow range error
+ TLOSS 5 total loss of precision
+ PLOSS 6 partial loss of precision
+ INVALID 7 NaN - producing operation
+ EDOM 33 Unix domain error code
+ ERANGE 34 Unix range error code
+
+ The default version of the file prints the function name,
+ passed to it by the pointer fctnam, followed by the
+ error condition. The display is directed to the standard
+ output device. The routine then returns to the calling
+ program. Users may wish to modify the program to abort by
+ calling exit under severe error conditions such as domain
+ errors.
+
+ Since all error conditions pass control to this function,
+ the display may be easily changed, eliminated, or directed
+ to an error logging device. */
+
+/* Note: the order of appearance of the following messages is bound to the
+ error codes defined above. */
+
+#define NMSGS 8
+static char *ermsg[NMSGS] =
+{
+ "unknown", /* error code 0 */
+ "domain", /* error code 1 */
+ "singularity", /* et seq. */
+ "overflow",
+ "underflow",
+ "total loss of precision",
+ "partial loss of precision",
+ "invalid operation"
+};
+
+int merror = 0;
+extern int merror;
+
+static void
+mtherr (name, code)
+ char *name;
+ int code;
+{
+ char errstr[80];
+
+ /* Display string passed by calling program, which is supposed to be the
+ name of the function in which the error occurred.
+
+ Display error message defined by the code argument. */
+
+ if ((code <= 0) || (code >= NMSGS))
+ code = 0;
+ sprintf (errstr, " %s %s error", name, ermsg[code]);
+ if (extra_warnings)
+ warning (errstr);
+ /* Set global error message word */
+ merror = code + 1;
+}
+
+#ifdef DEC
+/* Convert DEC double precision to e type. */
+
+static void
+dectoe (d, e)
+ unsigned EMUSHORT *d;
+ unsigned EMUSHORT *e;
+{
+ unsigned EMUSHORT y[NI];
+ register unsigned EMUSHORT r, *p;
+
+ ecleaz (y); /* start with a zero */
+ p = y; /* point to our number */
+ r = *d; /* get DEC exponent word */
+ if (*d & (unsigned int) 0x8000)
+ *p = 0xffff; /* fill in our sign */
+ ++p; /* bump pointer to our exponent word */
+ r &= 0x7fff; /* strip the sign bit */
+ if (r == 0) /* answer = 0 if high order DEC word = 0 */
+ goto done;
+
+
+ r >>= 7; /* shift exponent word down 7 bits */
+ r += EXONE - 0201; /* subtract DEC exponent offset */
+ /* add our e type exponent offset */
+ *p++ = r; /* to form our exponent */
+
+ r = *d++; /* now do the high order mantissa */
+ r &= 0177; /* strip off the DEC exponent and sign bits */
+ r |= 0200; /* the DEC understood high order mantissa bit */
+ *p++ = r; /* put result in our high guard word */
+
+ *p++ = *d++; /* fill in the rest of our mantissa */
+ *p++ = *d++;
+ *p = *d;
+
+ eshdn8 (y); /* shift our mantissa down 8 bits */
+ done:
+ emovo (y, e);
+}
+
+
+
+/*
+; convert e type to DEC double precision
+; double d;
+; EMUSHORT e[NE];
+; etodec (e, &d);
+*/
+
+static void
+etodec (x, d)
+ unsigned EMUSHORT *x, *d;
+{
+ unsigned EMUSHORT xi[NI];
+ EMULONG exp;
+ int rndsav;
+
+ emovi (x, xi);
+ exp = (EMULONG) xi[E] - (EXONE - 0201); /* adjust exponent for offsets */
+/* round off to nearest or even */
+ rndsav = rndprc;
+ rndprc = 56;
+ emdnorm (xi, 0, 0, exp, 64);
+ rndprc = rndsav;
+ todec (xi, d);
+}
+
+static void
+todec (x, y)
+ unsigned EMUSHORT *x, *y;
+{
+ unsigned EMUSHORT i;
+ unsigned EMUSHORT *p;
+
+ p = x;
+ *y = 0;
+ if (*p++)
+ *y = 0100000;
+ i = *p++;
+ if (i == 0)
+ {
+ *y++ = 0;
+ *y++ = 0;
+ *y++ = 0;
+ *y++ = 0;
+ return;
+ }
+ if (i > 0377)
+ {
+ *y++ |= 077777;
+ *y++ = 0xffff;
+ *y++ = 0xffff;
+ *y++ = 0xffff;
+#ifdef ERANGE
+ errno = ERANGE;
+#endif
+ return;
+ }
+ i &= 0377;
+ i <<= 7;
+ eshup8 (x);
+ x[M] &= 0177;
+ i |= x[M];
+ *y++ |= i;
+ *y++ = x[M + 1];
+ *y++ = x[M + 2];
+ *y++ = x[M + 3];
+}
+#endif /* DEC */
+
+#ifdef IBM
+/* Convert IBM single/double precision to e type. */
+
+static void
+ibmtoe (d, e, mode)
+ unsigned EMUSHORT *d;
+ unsigned EMUSHORT *e;
+ enum machine_mode mode;
+{
+ unsigned EMUSHORT y[NI];
+ register unsigned EMUSHORT r, *p;
+ int rndsav;
+
+ ecleaz (y); /* start with a zero */
+ p = y; /* point to our number */
+ r = *d; /* get IBM exponent word */
+ if (*d & (unsigned int) 0x8000)
+ *p = 0xffff; /* fill in our sign */
+ ++p; /* bump pointer to our exponent word */
+ r &= 0x7f00; /* strip the sign bit */
+ r >>= 6; /* shift exponent word down 6 bits */
+ /* in fact shift by 8 right and 2 left */
+ r += EXONE - (0x41 << 2); /* subtract IBM exponent offset */
+ /* add our e type exponent offset */
+ *p++ = r; /* to form our exponent */
+
+ *p++ = *d++ & 0xff; /* now do the high order mantissa */
+ /* strip off the IBM exponent and sign bits */
+ if (mode != SFmode) /* there are only 2 words in SFmode */
+ {
+ *p++ = *d++; /* fill in the rest of our mantissa */
+ *p++ = *d++;
+ }
+ *p = *d;
+
+ if (y[M] == 0 && y[M+1] == 0 && y[M+2] == 0 && y[M+3] == 0)
+ y[0] = y[E] = 0;
+ else
+ y[E] -= 5 + enormlz (y); /* now normalise the mantissa */
+ /* handle change in RADIX */
+ emovo (y, e);
+}
+
+
+
+/* Convert e type to IBM single/double precision. */
+
+static void
+etoibm (x, d, mode)
+ unsigned EMUSHORT *x, *d;
+ enum machine_mode mode;
+{
+ unsigned EMUSHORT xi[NI];
+ EMULONG exp;
+ int rndsav;
+
+ emovi (x, xi);
+ exp = (EMULONG) xi[E] - (EXONE - (0x41 << 2)); /* adjust exponent for offsets */
+ /* round off to nearest or even */
+ rndsav = rndprc;
+ rndprc = 56;
+ emdnorm (xi, 0, 0, exp, 64);
+ rndprc = rndsav;
+ toibm (xi, d, mode);
+}
+
+static void
+toibm (x, y, mode)
+ unsigned EMUSHORT *x, *y;
+ enum machine_mode mode;
+{
+ unsigned EMUSHORT i;
+ unsigned EMUSHORT *p;
+ int r;
+
+ p = x;
+ *y = 0;
+ if (*p++)
+ *y = 0x8000;
+ i = *p++;
+ if (i == 0)
+ {
+ *y++ = 0;
+ *y++ = 0;
+ if (mode != SFmode)
+ {
+ *y++ = 0;
+ *y++ = 0;
+ }
+ return;
+ }
+ r = i & 0x3;
+ i >>= 2;
+ if (i > 0x7f)
+ {
+ *y++ |= 0x7fff;
+ *y++ = 0xffff;
+ if (mode != SFmode)
+ {
+ *y++ = 0xffff;
+ *y++ = 0xffff;
+ }
+#ifdef ERANGE
+ errno = ERANGE;
+#endif
+ return;
+ }
+ i &= 0x7f;
+ *y |= (i << 8);
+ eshift (x, r + 5);
+ *y++ |= x[M];
+ *y++ = x[M + 1];
+ if (mode != SFmode)
+ {
+ *y++ = x[M + 2];
+ *y++ = x[M + 3];
+ }
+}
+#endif /* IBM */
+
+/* Output a binary NaN bit pattern in the target machine's format. */
+
+/* If special NaN bit patterns are required, define them in tm.h
+ as arrays of unsigned 16-bit shorts. Otherwise, use the default
+ patterns here. */
+#ifdef TFMODE_NAN
+TFMODE_NAN;
+#else
+#ifdef MIEEE
+unsigned EMUSHORT TFnan[8] =
+ {0x7fff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff};
+#endif
+#ifdef IBMPC
+unsigned EMUSHORT TFnan[8] = {0, 0, 0, 0, 0, 0, 0x8000, 0xffff};
+#endif
+#endif
+
+#ifdef XFMODE_NAN
+XFMODE_NAN;
+#else
+#ifdef MIEEE
+unsigned EMUSHORT XFnan[6] = {0x7fff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff};
+#endif
+#ifdef IBMPC
+unsigned EMUSHORT XFnan[6] = {0, 0, 0, 0xc000, 0xffff, 0};
+#endif
+#endif
+
+#ifdef DFMODE_NAN
+DFMODE_NAN;
+#else
+#ifdef MIEEE
+unsigned EMUSHORT DFnan[4] = {0x7fff, 0xffff, 0xffff, 0xffff};
+#endif
+#ifdef IBMPC
+unsigned EMUSHORT DFnan[4] = {0, 0, 0, 0xfff8};
+#endif
+#endif
+
+#ifdef SFMODE_NAN
+SFMODE_NAN;
+#else
+#ifdef MIEEE
+unsigned EMUSHORT SFnan[2] = {0x7fff, 0xffff};
+#endif
+#ifdef IBMPC
+unsigned EMUSHORT SFnan[2] = {0, 0xffc0};
+#endif
+#endif
+
+
+static void
+make_nan (nan, sign, mode)
+ unsigned EMUSHORT *nan;
+ int sign;
+ enum machine_mode mode;
+{
+ int n;
+ unsigned EMUSHORT *p;
+
+ switch (mode)
+ {
+/* Possibly the `reserved operand' patterns on a VAX can be
+ used like NaN's, but probably not in the same way as IEEE. */
+#if !defined(DEC) && !defined(IBM)
+ case TFmode:
+ n = 8;
+ p = TFnan;
+ break;
+ case XFmode:
+ n = 6;
+ p = XFnan;
+ break;
+ case DFmode:
+ n = 4;
+ p = DFnan;
+ break;
+ case HFmode:
+ case SFmode:
+ n = 2;
+ p = SFnan;
+ break;
+#endif
+ default:
+ abort ();
+ }
+#ifdef MIEEE
+ *nan++ = (sign << 15) | *p++;
+#endif
+ while (--n != 0)
+ *nan++ = *p++;
+#ifndef MIEEE
+ *nan = (sign << 15) | *p;
+#endif
+}
+
+/* Convert an SFmode target `float' value to a REAL_VALUE_TYPE.
+ This is the inverse of the function `etarsingle' invoked by
+ REAL_VALUE_TO_TARGET_SINGLE. */
+
+REAL_VALUE_TYPE
+ereal_from_float (f)
+ HOST_WIDE_INT f;
+{
+ REAL_VALUE_TYPE r;
+ unsigned EMUSHORT s[2];
+ unsigned EMUSHORT e[NE];
+
+ /* Convert 32 bit integer to array of 16 bit pieces in target machine order.
+ This is the inverse operation to what the function `endian' does. */
+#if FLOAT_WORDS_BIG_ENDIAN
+ s[0] = (unsigned EMUSHORT) (f >> 16);
+ s[1] = (unsigned EMUSHORT) f;
+#else
+ s[0] = (unsigned EMUSHORT) f;
+ s[1] = (unsigned EMUSHORT) (f >> 16);
+#endif
+ /* Convert and promote the target float to E-type. */
+ e24toe (s, e);
+ /* Output E-type to REAL_VALUE_TYPE. */
+ PUT_REAL (e, &r);
+ return r;
+}
+
+
+/* Convert a DFmode target `double' value to a REAL_VALUE_TYPE.
+ This is the inverse of the function `etardouble' invoked by
+ REAL_VALUE_TO_TARGET_DOUBLE.
+
+ The DFmode is stored as an array of HOST_WIDE_INT in the target's
+ data format, with no holes in the bit packing. The first element
+ of the input array holds the bits that would come first in the
+ target computer's memory. */
+
+REAL_VALUE_TYPE
+ereal_from_double (d)
+ HOST_WIDE_INT d[];
+{
+ REAL_VALUE_TYPE r;
+ unsigned EMUSHORT s[4];
+ unsigned EMUSHORT e[NE];
+
+ /* Convert array of HOST_WIDE_INT to equivalent array of 16-bit pieces. */
+#if FLOAT_WORDS_BIG_ENDIAN
+ s[0] = (unsigned EMUSHORT) (d[0] >> 16);
+ s[1] = (unsigned EMUSHORT) d[0];
+#if HOST_BITS_PER_WIDE_INT == 32
+ s[2] = (unsigned EMUSHORT) (d[1] >> 16);
+ s[3] = (unsigned EMUSHORT) d[1];
+#else
+ /* In this case the entire target double is contained in the
+ first array element. The second element of the input is ignored. */
+ s[2] = (unsigned EMUSHORT) (d[0] >> 48);
+ s[3] = (unsigned EMUSHORT) (d[0] >> 32);
+#endif
+#else
+/* Target float words are little-endian. */
+ s[0] = (unsigned EMUSHORT) d[0];
+ s[1] = (unsigned EMUSHORT) (d[0] >> 16);
+#if HOST_BITS_PER_WIDE_INT == 32
+ s[2] = (unsigned EMUSHORT) d[1];
+ s[3] = (unsigned EMUSHORT) (d[1] >> 16);
+#else
+ s[2] = (unsigned EMUSHORT) (d[0] >> 32);
+ s[3] = (unsigned EMUSHORT) (d[0] >> 48);
+#endif
+#endif
+ /* Convert target double to E-type. */
+ e53toe (s, e);
+ /* Output E-type to REAL_VALUE_TYPE. */
+ PUT_REAL (e, &r);
+ return r;
+}
+
+
+/* Convert target computer unsigned 64-bit integer to e-type.
+ The endian-ness of DImode follows the convention for integers,
+ so we use WORDS_BIG_ENDIAN here, not FLOAT_WORDS_BIG_ENDIAN. */
+
+static void
+uditoe (di, e)
+ unsigned EMUSHORT *di; /* Address of the 64-bit int. */
+ unsigned EMUSHORT *e;
+{
+ unsigned EMUSHORT yi[NI];
+ int k;
+
+ ecleaz (yi);
+#if WORDS_BIG_ENDIAN
+ for (k = M; k < M + 4; k++)
+ yi[k] = *di++;
+#else
+ for (k = M + 3; k >= M; k--)
+ yi[k] = *di++;
+#endif
+ yi[E] = EXONE + 47; /* exponent if normalize shift count were 0 */
+ if ((k = enormlz (yi)) > NBITS)/* normalize the significand */
+ ecleaz (yi); /* it was zero */
+ else
+ yi[E] -= (unsigned EMUSHORT) k;/* subtract shift count from exponent */
+ emovo (yi, e);
+}
+
+/* Convert target computer signed 64-bit integer to e-type. */
+
+static void
+ditoe (di, e)
+ unsigned EMUSHORT *di; /* Address of the 64-bit int. */
+ unsigned EMUSHORT *e;
+{
+ unsigned EMULONG acc;
+ unsigned EMUSHORT yi[NI];
+ unsigned EMUSHORT carry;
+ int k, sign;
+
+ ecleaz (yi);
+#if WORDS_BIG_ENDIAN
+ for (k = M; k < M + 4; k++)
+ yi[k] = *di++;
+#else
+ for (k = M + 3; k >= M; k--)
+ yi[k] = *di++;
+#endif
+ /* Take absolute value */
+ sign = 0;
+ if (yi[M] & 0x8000)
+ {
+ sign = 1;
+ carry = 0;
+ for (k = M + 3; k >= M; k--)
+ {
+ acc = (unsigned EMULONG) (~yi[k] & 0xffff) + carry;
+ yi[k] = acc;
+ carry = 0;
+ if (acc & 0x10000)
+ carry = 1;
+ }
+ }
+ yi[E] = EXONE + 47; /* exponent if normalize shift count were 0 */
+ if ((k = enormlz (yi)) > NBITS)/* normalize the significand */
+ ecleaz (yi); /* it was zero */
+ else
+ yi[E] -= (unsigned EMUSHORT) k;/* subtract shift count from exponent */
+ emovo (yi, e);
+ if (sign)
+ eneg (e);
+}
+
+
+/* Convert e-type to unsigned 64-bit int. */
+
+static void
+etoudi (x, i)
+ unsigned EMUSHORT *x;
+ unsigned EMUSHORT *i;
+{
+ unsigned EMUSHORT xi[NI];
+ int j, k;
+
+ emovi (x, xi);
+ if (xi[0])
+ {
+ xi[M] = 0;
+ goto noshift;
+ }
+ k = (int) xi[E] - (EXONE - 1);
+ if (k <= 0)
+ {
+ for (j = 0; j < 4; j++)
+ *i++ = 0;
+ return;
+ }
+ if (k > 64)
+ {
+ for (j = 0; j < 4; j++)
+ *i++ = 0xffff;
+ if (extra_warnings)
+ warning ("overflow on truncation to integer");
+ return;
+ }
+ if (k > 16)
+ {
+ /* Shift more than 16 bits: first shift up k-16 mod 16,
+ then shift up by 16's. */
+ j = k - ((k >> 4) << 4);
+ if (j == 0)
+ j = 16;
+ eshift (xi, j);
+#if WORDS_BIG_ENDIAN
+ *i++ = xi[M];
+#else
+ i += 3;
+ *i-- = xi[M];
+#endif
+ k -= j;
+ do
+ {
+ eshup6 (xi);
+#if WORDS_BIG_ENDIAN
+ *i++ = xi[M];
+#else
+ *i-- = xi[M];
+#endif
+ }
+ while ((k -= 16) > 0);
+ }
+ else
+ {
+ /* shift not more than 16 bits */
+ eshift (xi, k);
+
+noshift:
+
+#if WORDS_BIG_ENDIAN
+ i += 3;
+ *i-- = xi[M];
+ *i-- = 0;
+ *i-- = 0;
+ *i = 0;
+#else
+ *i++ = xi[M];
+ *i++ = 0;
+ *i++ = 0;
+ *i = 0;
+#endif
+ }
+}
+
+
+/* Convert e-type to signed 64-bit int. */
+
+static void
+etodi (x, i)
+ unsigned EMUSHORT *x;
+ unsigned EMUSHORT *i;
+{
+ unsigned EMULONG acc;
+ unsigned EMUSHORT xi[NI];
+ unsigned EMUSHORT carry;
+ unsigned EMUSHORT *isave;
+ int j, k;
+
+ emovi (x, xi);
+ k = (int) xi[E] - (EXONE - 1);
+ if (k <= 0)
+ {
+ for (j = 0; j < 4; j++)
+ *i++ = 0;
+ return;
+ }
+ if (k > 64)
+ {
+ for (j = 0; j < 4; j++)
+ *i++ = 0xffff;
+ if (extra_warnings)
+ warning ("overflow on truncation to integer");
+ return;
+ }
+ isave = i;
+ if (k > 16)
+ {
+ /* Shift more than 16 bits: first shift up k-16 mod 16,
+ then shift up by 16's. */
+ j = k - ((k >> 4) << 4);
+ if (j == 0)
+ j = 16;
+ eshift (xi, j);
+#if WORDS_BIG_ENDIAN
+ *i++ = xi[M];
+#else
+ i += 3;
+ *i-- = xi[M];
+#endif
+ k -= j;
+ do
+ {
+ eshup6 (xi);
+#if WORDS_BIG_ENDIAN
+ *i++ = xi[M];
+#else
+ *i-- = xi[M];
+#endif
+ }
+ while ((k -= 16) > 0);
+ }
+ else
+ {
+ /* shift not more than 16 bits */
+ eshift (xi, k);
+
+#if WORDS_BIG_ENDIAN
+ i += 3;
+ *i = xi[M];
+ *i-- = 0;
+ *i-- = 0;
+ *i = 0;
+#else
+ *i++ = xi[M];
+ *i++ = 0;
+ *i++ = 0;
+ *i = 0;
+#endif
+ }
+ /* Negate if negative */
+ if (xi[0])
+ {
+ carry = 0;
+#if WORDS_BIG_ENDIAN
+ isave += 3;
+#endif
+ for (k = 0; k < 4; k++)
+ {
+ acc = (unsigned EMULONG) (~(*isave) & 0xffff) + carry;
+#if WORDS_BIG_ENDIAN
+ *isave-- = acc;
+#else
+ *isave++ = acc;
+#endif
+ carry = 0;
+ if (acc & 0x10000)
+ carry = 1;
+ }
+ }
+}
+
+
+/* Longhand square root routine. */
+
+
+static int esqinited = 0;
+static unsigned short sqrndbit[NI];
+
+static void
+esqrt (x, y)
+ unsigned EMUSHORT *x, *y;
+{
+ unsigned EMUSHORT temp[NI], num[NI], sq[NI], xx[NI];
+ EMULONG m, exp;
+ int i, j, k, n, nlups;
+
+ if (esqinited == 0)
+ {
+ ecleaz (sqrndbit);
+ sqrndbit[NI - 2] = 1;
+ esqinited = 1;
+ }
+ /* Check for arg <= 0 */
+ i = ecmp (x, ezero);
+ if (i <= 0)
+ {
+ if (i == -1)
+ {
+ mtherr ("esqrt", DOMAIN);
+ eclear (y);
+ }
+ else
+ emov (x, y);
+ return;
+ }
+
+#ifdef INFINITY
+ if (eisinf (x))
+ {
+ eclear (y);
+ einfin (y);
+ return;
+ }
+#endif
+ /* Bring in the arg and renormalize if it is denormal. */
+ emovi (x, xx);
+ m = (EMULONG) xx[1]; /* local long word exponent */
+ if (m == 0)
+ m -= enormlz (xx);
+
+ /* Divide exponent by 2 */
+ m -= 0x3ffe;
+ exp = (unsigned short) ((m / 2) + 0x3ffe);
+
+ /* Adjust if exponent odd */
+ if ((m & 1) != 0)
+ {
+ if (m > 0)
+ exp += 1;
+ eshdn1 (xx);
+ }
+
+ ecleaz (sq);
+ ecleaz (num);
+ n = 8; /* get 8 bits of result per inner loop */
+ nlups = rndprc;
+ j = 0;
+
+ while (nlups > 0)
+ {
+ /* bring in next word of arg */
+ if (j < NE)
+ num[NI - 1] = xx[j + 3];
+ /* Do additional bit on last outer loop, for roundoff. */
+ if (nlups <= 8)
+ n = nlups + 1;
+ for (i = 0; i < n; i++)
+ {
+ /* Next 2 bits of arg */
+ eshup1 (num);
+ eshup1 (num);
+ /* Shift up answer */
+ eshup1 (sq);
+ /* Make trial divisor */
+ for (k = 0; k < NI; k++)
+ temp[k] = sq[k];
+ eshup1 (temp);
+ eaddm (sqrndbit, temp);
+ /* Subtract and insert answer bit if it goes in */
+ if (ecmpm (temp, num) <= 0)
+ {
+ esubm (temp, num);
+ sq[NI - 2] |= 1;
+ }
+ }
+ nlups -= n;
+ j += 1;
+ }
+
+ /* Adjust for extra, roundoff loop done. */
+ exp += (NBITS - 1) - rndprc;
+
+ /* Sticky bit = 1 if the remainder is nonzero. */
+ k = 0;
+ for (i = 3; i < NI; i++)
+ k |= (int) num[i];
+
+ /* Renormalize and round off. */
+ emdnorm (sq, k, 0, exp, 64);
+ emovo (sq, y);
+}
+#endif /* EMU_NON_COMPILE not defined */
+
+/* Return the binary precision of the significand for a given
+ floating point mode. The mode can hold an integer value
+ that many bits wide, without losing any bits. */
+
+int
+significand_size (mode)
+ enum machine_mode mode;
+{
+
+switch (mode)
+ {
+ case SFmode:
+ return 24;
+
+ case DFmode:
+#if TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
+ return 53;
+#else
+#if TARGET_FLOAT_FORMAT == IBM_FLOAT_FORMAT
+ return 56;
+#else
+#if TARGET_FLOAT_FORMAT == VAX_FLOAT_FORMAT
+ return 56;
+#else
+ abort ();
+#endif
+#endif
+#endif
+
+ case XFmode:
+ return 64;
+ case TFmode:
+ return 113;
+
+ default:
+ abort ();
+ }
+}
diff --git a/gnu/usr.bin/cc/cc_int/recog.c b/gnu/usr.bin/cc/cc_int/recog.c
new file mode 100644
index 0000000..1072fe9
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/recog.c
@@ -0,0 +1,1970 @@
+/* Subroutines used by or related to instruction recognition.
+ Copyright (C) 1987, 88, 91, 92, 93, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#include "rtl.h"
+#include <stdio.h>
+#include "insn-config.h"
+#include "insn-attr.h"
+#include "insn-flags.h"
+#include "insn-codes.h"
+#include "recog.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "flags.h"
+#include "real.h"
+
+#ifndef STACK_PUSH_CODE
+#ifdef STACK_GROWS_DOWNWARD
+#define STACK_PUSH_CODE PRE_DEC
+#else
+#define STACK_PUSH_CODE PRE_INC
+#endif
+#endif
+
+/* Import from final.c: */
+extern rtx alter_subreg ();
+
+int strict_memory_address_p ();
+int memory_address_p ();
+
+/* Nonzero means allow operands to be volatile.
+ This should be 0 if you are generating rtl, such as if you are calling
+ the functions in optabs.c and expmed.c (most of the time).
+ This should be 1 if all valid insns need to be recognized,
+ such as in regclass.c and final.c and reload.c.
+
+ init_recog and init_recog_no_volatile are responsible for setting this. */
+
+int volatile_ok;
+
+/* On return from `constrain_operands', indicate which alternative
+ was satisfied. */
+
+int which_alternative;
+
+/* Nonzero after end of reload pass.
+ Set to 1 or 0 by toplev.c.
+ Controls the significance of (SUBREG (MEM)). */
+
+int reload_completed;
+
+/* Initialize data used by the function `recog'.
+ This must be called once in the compilation of a function
+ before any insn recognition may be done in the function. */
+
+void
+init_recog_no_volatile ()
+{
+ volatile_ok = 0;
+}
+
+void
+init_recog ()
+{
+ volatile_ok = 1;
+}
+
+/* Try recognizing the instruction INSN,
+ and return the code number that results.
+ Remeber the code so that repeated calls do not
+ need to spend the time for actual rerecognition.
+
+ This function is the normal interface to instruction recognition.
+ The automatically-generated function `recog' is normally called
+ through this one. (The only exception is in combine.c.) */
+
+int
+recog_memoized (insn)
+ rtx insn;
+{
+ if (INSN_CODE (insn) < 0)
+ INSN_CODE (insn) = recog (PATTERN (insn), insn, NULL_PTR);
+ return INSN_CODE (insn);
+}
+
+/* Check that X is an insn-body for an `asm' with operands
+ and that the operands mentioned in it are legitimate. */
+
+int
+check_asm_operands (x)
+ rtx x;
+{
+ int noperands = asm_noperands (x);
+ rtx *operands;
+ int i;
+
+ if (noperands < 0)
+ return 0;
+ if (noperands == 0)
+ return 1;
+
+ operands = (rtx *) alloca (noperands * sizeof (rtx));
+ decode_asm_operands (x, operands, NULL_PTR, NULL_PTR, NULL_PTR);
+
+ for (i = 0; i < noperands; i++)
+ if (!general_operand (operands[i], VOIDmode))
+ return 0;
+
+ return 1;
+}
+
+/* Static data for the next two routines.
+
+ The maximum number of changes supported is defined as the maximum
+ number of operands times 5. This allows for repeated substitutions
+ inside complex indexed address, or, alternatively, changes in up
+ to 5 insns. */
+
+#define MAX_CHANGE_LOCS (MAX_RECOG_OPERANDS * 5)
+
+static rtx change_objects[MAX_CHANGE_LOCS];
+static int change_old_codes[MAX_CHANGE_LOCS];
+static rtx *change_locs[MAX_CHANGE_LOCS];
+static rtx change_olds[MAX_CHANGE_LOCS];
+
+static int num_changes = 0;
+
+/* Validate a proposed change to OBJECT. LOC is the location in the rtl for
+ at which NEW will be placed. If OBJECT is zero, no validation is done,
+ the change is simply made.
+
+ Two types of objects are supported: If OBJECT is a MEM, memory_address_p
+ will be called with the address and mode as parameters. If OBJECT is
+ an INSN, CALL_INSN, or JUMP_INSN, the insn will be re-recognized with
+ the change in place.
+
+ IN_GROUP is non-zero if this is part of a group of changes that must be
+ performed as a group. In that case, the changes will be stored. The
+ function `apply_change_group' will validate and apply the changes.
+
+ If IN_GROUP is zero, this is a single change. Try to recognize the insn
+ or validate the memory reference with the change applied. If the result
+ is not valid for the machine, suppress the change and return zero.
+ Otherwise, perform the change and return 1. */
+
+int
+validate_change (object, loc, new, in_group)
+ rtx object;
+ rtx *loc;
+ rtx new;
+ int in_group;
+{
+ rtx old = *loc;
+
+ if (old == new || rtx_equal_p (old, new))
+ return 1;
+
+ if (num_changes >= MAX_CHANGE_LOCS
+ || (in_group == 0 && num_changes != 0))
+ abort ();
+
+ *loc = new;
+
+ /* Save the information describing this change. */
+ change_objects[num_changes] = object;
+ change_locs[num_changes] = loc;
+ change_olds[num_changes] = old;
+
+ if (object && GET_CODE (object) != MEM)
+ {
+ /* Set INSN_CODE to force rerecognition of insn. Save old code in
+ case invalid. */
+ change_old_codes[num_changes] = INSN_CODE (object);
+ INSN_CODE (object) = -1;
+ }
+
+ num_changes++;
+
+ /* If we are making a group of changes, return 1. Otherwise, validate the
+ change group we made. */
+
+ if (in_group)
+ return 1;
+ else
+ return apply_change_group ();
+}
+
+/* Apply a group of changes previously issued with `validate_change'.
+ Return 1 if all changes are valid, zero otherwise. */
+
+int
+apply_change_group ()
+{
+ int i;
+
+ /* The changes have been applied and all INSN_CODEs have been reset to force
+ rerecognition.
+
+ The changes are valid if we aren't given an object, or if we are
+ given a MEM and it still is a valid address, or if this is in insn
+ and it is recognized. In the latter case, if reload has completed,
+ we also require that the operands meet the constraints for
+ the insn. We do not allow modifying an ASM_OPERANDS after reload
+ has completed because verifying the constraints is too difficult. */
+
+ for (i = 0; i < num_changes; i++)
+ {
+ rtx object = change_objects[i];
+
+ if (object == 0)
+ continue;
+
+ if (GET_CODE (object) == MEM)
+ {
+ if (! memory_address_p (GET_MODE (object), XEXP (object, 0)))
+ break;
+ }
+ else if ((recog_memoized (object) < 0
+ && (asm_noperands (PATTERN (object)) < 0
+ || ! check_asm_operands (PATTERN (object))
+ || reload_completed))
+ || (reload_completed
+ && (insn_extract (object),
+ ! constrain_operands (INSN_CODE (object), 1))))
+ {
+ rtx pat = PATTERN (object);
+
+ /* Perhaps we couldn't recognize the insn because there were
+ extra CLOBBERs at the end. If so, try to re-recognize
+ without the last CLOBBER (later iterations will cause each of
+ them to be eliminated, in turn). But don't do this if we
+ have an ASM_OPERAND. */
+ if (GET_CODE (pat) == PARALLEL
+ && GET_CODE (XVECEXP (pat, 0, XVECLEN (pat, 0) - 1)) == CLOBBER
+ && asm_noperands (PATTERN (object)) < 0)
+ {
+ rtx newpat;
+
+ if (XVECLEN (pat, 0) == 2)
+ newpat = XVECEXP (pat, 0, 0);
+ else
+ {
+ int j;
+
+ newpat = gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (XVECLEN (pat, 0) - 1));
+ for (j = 0; j < XVECLEN (newpat, 0); j++)
+ XVECEXP (newpat, 0, j) = XVECEXP (pat, 0, j);
+ }
+
+ /* Add a new change to this group to replace the pattern
+ with this new pattern. Then consider this change
+ as having succeeded. The change we added will
+ cause the entire call to fail if things remain invalid.
+
+ Note that this can lose if a later change than the one
+ we are processing specified &XVECEXP (PATTERN (object), 0, X)
+ but this shouldn't occur. */
+
+ validate_change (object, &PATTERN (object), newpat, 1);
+ }
+ else if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER)
+ /* If this insn is a CLOBBER or USE, it is always valid, but is
+ never recognized. */
+ continue;
+ else
+ break;
+ }
+ }
+
+ if (i == num_changes)
+ {
+ num_changes = 0;
+ return 1;
+ }
+ else
+ {
+ cancel_changes (0);
+ return 0;
+ }
+}
+
+/* Return the number of changes so far in the current group. */
+
+int
+num_validated_changes ()
+{
+ return num_changes;
+}
+
+/* Retract the changes numbered NUM and up. */
+
+void
+cancel_changes (num)
+ int num;
+{
+ int i;
+
+ /* Back out all the changes. Do this in the opposite order in which
+ they were made. */
+ for (i = num_changes - 1; i >= num; i--)
+ {
+ *change_locs[i] = change_olds[i];
+ if (change_objects[i] && GET_CODE (change_objects[i]) != MEM)
+ INSN_CODE (change_objects[i]) = change_old_codes[i];
+ }
+ num_changes = num;
+}
+
+/* Replace every occurrence of FROM in X with TO. Mark each change with
+ validate_change passing OBJECT. */
+
+static void
+validate_replace_rtx_1 (loc, from, to, object)
+ rtx *loc;
+ rtx from, to, object;
+{
+ register int i, j;
+ register char *fmt;
+ register rtx x = *loc;
+ enum rtx_code code = GET_CODE (x);
+
+ /* X matches FROM if it is the same rtx or they are both referring to the
+ same register in the same mode. Avoid calling rtx_equal_p unless the
+ operands look similar. */
+
+ if (x == from
+ || (GET_CODE (x) == REG && GET_CODE (from) == REG
+ && GET_MODE (x) == GET_MODE (from)
+ && REGNO (x) == REGNO (from))
+ || (GET_CODE (x) == GET_CODE (from) && GET_MODE (x) == GET_MODE (from)
+ && rtx_equal_p (x, from)))
+ {
+ validate_change (object, loc, to, 1);
+ return;
+ }
+
+ /* For commutative or comparison operations, try replacing each argument
+ separately and seeing if we made any changes. If so, put a constant
+ argument last.*/
+ if (GET_RTX_CLASS (code) == '<' || GET_RTX_CLASS (code) == 'c')
+ {
+ int prev_changes = num_changes;
+
+ validate_replace_rtx_1 (&XEXP (x, 0), from, to, object);
+ validate_replace_rtx_1 (&XEXP (x, 1), from, to, object);
+ if (prev_changes != num_changes && CONSTANT_P (XEXP (x, 0)))
+ {
+ validate_change (object, loc,
+ gen_rtx (GET_RTX_CLASS (code) == 'c' ? code
+ : swap_condition (code),
+ GET_MODE (x), XEXP (x, 1), XEXP (x, 0)),
+ 1);
+ x = *loc;
+ code = GET_CODE (x);
+ }
+ }
+
+ switch (code)
+ {
+ case PLUS:
+ /* If we have have a PLUS whose second operand is now a CONST_INT, use
+ plus_constant to try to simplify it. */
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT && XEXP (x, 1) == to)
+ validate_change (object, loc,
+ plus_constant (XEXP (x, 0), INTVAL (XEXP (x, 1))), 1);
+ return;
+
+ case ZERO_EXTEND:
+ case SIGN_EXTEND:
+ /* In these cases, the operation to be performed depends on the mode
+ of the operand. If we are replacing the operand with a VOIDmode
+ constant, we lose the information. So try to simplify the operation
+ in that case. If it fails, substitute in something that we know
+ won't be recognized. */
+ if (GET_MODE (to) == VOIDmode
+ && (XEXP (x, 0) == from
+ || (GET_CODE (XEXP (x, 0)) == REG && GET_CODE (from) == REG
+ && GET_MODE (XEXP (x, 0)) == GET_MODE (from)
+ && REGNO (XEXP (x, 0)) == REGNO (from))))
+ {
+ rtx new = simplify_unary_operation (code, GET_MODE (x), to,
+ GET_MODE (from));
+ if (new == 0)
+ new = gen_rtx (CLOBBER, GET_MODE (x), const0_rtx);
+
+ validate_change (object, loc, new, 1);
+ return;
+ }
+ break;
+
+ case SUBREG:
+ /* If we have a SUBREG of a register that we are replacing and we are
+ replacing it with a MEM, make a new MEM and try replacing the
+ SUBREG with it. Don't do this if the MEM has a mode-dependent address
+ or if we would be widening it. */
+
+ if (SUBREG_REG (x) == from
+ && GET_CODE (from) == REG
+ && GET_CODE (to) == MEM
+ && ! mode_dependent_address_p (XEXP (to, 0))
+ && ! MEM_VOLATILE_P (to)
+ && GET_MODE_SIZE (GET_MODE (x)) <= GET_MODE_SIZE (GET_MODE (to)))
+ {
+ int offset = SUBREG_WORD (x) * UNITS_PER_WORD;
+ enum machine_mode mode = GET_MODE (x);
+ rtx new;
+
+#if BYTES_BIG_ENDIAN
+ offset += (MIN (UNITS_PER_WORD,
+ GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+ - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)));
+#endif
+
+ new = gen_rtx (MEM, mode, plus_constant (XEXP (to, 0), offset));
+ MEM_VOLATILE_P (new) = MEM_VOLATILE_P (to);
+ RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (to);
+ MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (to);
+ validate_change (object, loc, new, 1);
+ return;
+ }
+ break;
+
+ case ZERO_EXTRACT:
+ case SIGN_EXTRACT:
+ /* If we are replacing a register with memory, try to change the memory
+ to be the mode required for memory in extract operations (this isn't
+ likely to be an insertion operation; if it was, nothing bad will
+ happen, we might just fail in some cases). */
+
+ if (XEXP (x, 0) == from && GET_CODE (from) == REG && GET_CODE (to) == MEM
+ && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && GET_CODE (XEXP (x, 2)) == CONST_INT
+ && ! mode_dependent_address_p (XEXP (to, 0))
+ && ! MEM_VOLATILE_P (to))
+ {
+ enum machine_mode wanted_mode = VOIDmode;
+ enum machine_mode is_mode = GET_MODE (to);
+ int width = INTVAL (XEXP (x, 1));
+ int pos = INTVAL (XEXP (x, 2));
+
+#ifdef HAVE_extzv
+ if (code == ZERO_EXTRACT)
+ wanted_mode = insn_operand_mode[(int) CODE_FOR_extzv][1];
+#endif
+#ifdef HAVE_extv
+ if (code == SIGN_EXTRACT)
+ wanted_mode = insn_operand_mode[(int) CODE_FOR_extv][1];
+#endif
+
+ /* If we have a narrower mode, we can do something. */
+ if (wanted_mode != VOIDmode
+ && GET_MODE_SIZE (wanted_mode) < GET_MODE_SIZE (is_mode))
+ {
+ int offset = pos / BITS_PER_UNIT;
+ rtx newmem;
+
+ /* If the bytes and bits are counted differently, we
+ must adjust the offset. */
+#if BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN
+ offset = (GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (wanted_mode)
+ - offset);
+#endif
+
+ pos %= GET_MODE_BITSIZE (wanted_mode);
+
+ newmem = gen_rtx (MEM, wanted_mode,
+ plus_constant (XEXP (to, 0), offset));
+ RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (to);
+ MEM_VOLATILE_P (newmem) = MEM_VOLATILE_P (to);
+ MEM_IN_STRUCT_P (newmem) = MEM_IN_STRUCT_P (to);
+
+ validate_change (object, &XEXP (x, 2), GEN_INT (pos), 1);
+ validate_change (object, &XEXP (x, 0), newmem, 1);
+ }
+ }
+
+ break;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ validate_replace_rtx_1 (&XEXP (x, i), from, to, object);
+ else if (fmt[i] == 'E')
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ validate_replace_rtx_1 (&XVECEXP (x, i, j), from, to, object);
+ }
+}
+
+/* Try replacing every occurrence of FROM in INSN with TO. After all
+ changes have been made, validate by seeing if INSN is still valid. */
+
+int
+validate_replace_rtx (from, to, insn)
+ rtx from, to, insn;
+{
+ validate_replace_rtx_1 (&PATTERN (insn), from, to, insn);
+ return apply_change_group ();
+}
+
+#ifdef HAVE_cc0
+/* Return 1 if the insn using CC0 set by INSN does not contain
+ any ordered tests applied to the condition codes.
+ EQ and NE tests do not count. */
+
+int
+next_insn_tests_no_inequality (insn)
+ rtx insn;
+{
+ register rtx next = next_cc0_user (insn);
+
+ /* If there is no next insn, we have to take the conservative choice. */
+ if (next == 0)
+ return 0;
+
+ return ((GET_CODE (next) == JUMP_INSN
+ || GET_CODE (next) == INSN
+ || GET_CODE (next) == CALL_INSN)
+ && ! inequality_comparisons_p (PATTERN (next)));
+}
+
+#if 0 /* This is useless since the insn that sets the cc's
+ must be followed immediately by the use of them. */
+/* Return 1 if the CC value set up by INSN is not used. */
+
+int
+next_insns_test_no_inequality (insn)
+ rtx insn;
+{
+ register rtx next = NEXT_INSN (insn);
+
+ for (; next != 0; next = NEXT_INSN (next))
+ {
+ if (GET_CODE (next) == CODE_LABEL
+ || GET_CODE (next) == BARRIER)
+ return 1;
+ if (GET_CODE (next) == NOTE)
+ continue;
+ if (inequality_comparisons_p (PATTERN (next)))
+ return 0;
+ if (sets_cc0_p (PATTERN (next)) == 1)
+ return 1;
+ if (! reg_mentioned_p (cc0_rtx, PATTERN (next)))
+ return 1;
+ }
+ return 1;
+}
+#endif
+#endif
+
+/* This is used by find_single_use to locate an rtx that contains exactly one
+ use of DEST, which is typically either a REG or CC0. It returns a
+ pointer to the innermost rtx expression containing DEST. Appearances of
+ DEST that are being used to totally replace it are not counted. */
+
+static rtx *
+find_single_use_1 (dest, loc)
+ rtx dest;
+ rtx *loc;
+{
+ rtx x = *loc;
+ enum rtx_code code = GET_CODE (x);
+ rtx *result = 0;
+ rtx *this_result;
+ int i;
+ char *fmt;
+
+ switch (code)
+ {
+ case CONST_INT:
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ case CONST_DOUBLE:
+ case CLOBBER:
+ return 0;
+
+ case SET:
+ /* If the destination is anything other than CC0, PC, a REG or a SUBREG
+ of a REG that occupies all of the REG, the insn uses DEST if
+ it is mentioned in the destination or the source. Otherwise, we
+ need just check the source. */
+ if (GET_CODE (SET_DEST (x)) != CC0
+ && GET_CODE (SET_DEST (x)) != PC
+ && GET_CODE (SET_DEST (x)) != REG
+ && ! (GET_CODE (SET_DEST (x)) == SUBREG
+ && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG
+ && (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (x))))
+ + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)
+ == ((GET_MODE_SIZE (GET_MODE (SET_DEST (x)))
+ + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD))))
+ break;
+
+ return find_single_use_1 (dest, &SET_SRC (x));
+
+ case MEM:
+ case SUBREG:
+ return find_single_use_1 (dest, &XEXP (x, 0));
+ }
+
+ /* If it wasn't one of the common cases above, check each expression and
+ vector of this code. Look for a unique usage of DEST. */
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ {
+ if (dest == XEXP (x, i)
+ || (GET_CODE (dest) == REG && GET_CODE (XEXP (x, i)) == REG
+ && REGNO (dest) == REGNO (XEXP (x, i))))
+ this_result = loc;
+ else
+ this_result = find_single_use_1 (dest, &XEXP (x, i));
+
+ if (result == 0)
+ result = this_result;
+ else if (this_result)
+ /* Duplicate usage. */
+ return 0;
+ }
+ else if (fmt[i] == 'E')
+ {
+ int j;
+
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ {
+ if (XVECEXP (x, i, j) == dest
+ || (GET_CODE (dest) == REG
+ && GET_CODE (XVECEXP (x, i, j)) == REG
+ && REGNO (XVECEXP (x, i, j)) == REGNO (dest)))
+ this_result = loc;
+ else
+ this_result = find_single_use_1 (dest, &XVECEXP (x, i, j));
+
+ if (result == 0)
+ result = this_result;
+ else if (this_result)
+ return 0;
+ }
+ }
+ }
+
+ return result;
+}
+
+/* See if DEST, produced in INSN, is used only a single time in the
+ sequel. If so, return a pointer to the innermost rtx expression in which
+ it is used.
+
+ If PLOC is non-zero, *PLOC is set to the insn containing the single use.
+
+ This routine will return usually zero either before flow is called (because
+ there will be no LOG_LINKS notes) or after reload (because the REG_DEAD
+ note can't be trusted).
+
+ If DEST is cc0_rtx, we look only at the next insn. In that case, we don't
+ care about REG_DEAD notes or LOG_LINKS.
+
+ Otherwise, we find the single use by finding an insn that has a
+ LOG_LINKS pointing at INSN and has a REG_DEAD note for DEST. If DEST is
+ only referenced once in that insn, we know that it must be the first
+ and last insn referencing DEST. */
+
+rtx *
+find_single_use (dest, insn, ploc)
+ rtx dest;
+ rtx insn;
+ rtx *ploc;
+{
+ rtx next;
+ rtx *result;
+ rtx link;
+
+#ifdef HAVE_cc0
+ if (dest == cc0_rtx)
+ {
+ next = NEXT_INSN (insn);
+ if (next == 0
+ || (GET_CODE (next) != INSN && GET_CODE (next) != JUMP_INSN))
+ return 0;
+
+ result = find_single_use_1 (dest, &PATTERN (next));
+ if (result && ploc)
+ *ploc = next;
+ return result;
+ }
+#endif
+
+ if (reload_completed || reload_in_progress || GET_CODE (dest) != REG)
+ return 0;
+
+ for (next = next_nonnote_insn (insn);
+ next != 0 && GET_CODE (next) != CODE_LABEL;
+ next = next_nonnote_insn (next))
+ if (GET_RTX_CLASS (GET_CODE (next)) == 'i' && dead_or_set_p (next, dest))
+ {
+ for (link = LOG_LINKS (next); link; link = XEXP (link, 1))
+ if (XEXP (link, 0) == insn)
+ break;
+
+ if (link)
+ {
+ result = find_single_use_1 (dest, &PATTERN (next));
+ if (ploc)
+ *ploc = next;
+ return result;
+ }
+ }
+
+ return 0;
+}
+
+/* Return 1 if OP is a valid general operand for machine mode MODE.
+ This is either a register reference, a memory reference,
+ or a constant. In the case of a memory reference, the address
+ is checked for general validity for the target machine.
+
+ Register and memory references must have mode MODE in order to be valid,
+ but some constants have no machine mode and are valid for any mode.
+
+ If MODE is VOIDmode, OP is checked for validity for whatever mode
+ it has.
+
+ The main use of this function is as a predicate in match_operand
+ expressions in the machine description.
+
+ For an explanation of this function's behavior for registers of
+ class NO_REGS, see the comment for `register_operand'. */
+
+int
+general_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ register enum rtx_code code = GET_CODE (op);
+ int mode_altering_drug = 0;
+
+ if (mode == VOIDmode)
+ mode = GET_MODE (op);
+
+ /* Don't accept CONST_INT or anything similar
+ if the caller wants something floating. */
+ if (GET_MODE (op) == VOIDmode && mode != VOIDmode
+ && GET_MODE_CLASS (mode) != MODE_INT
+ && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
+ return 0;
+
+ if (CONSTANT_P (op))
+ return ((GET_MODE (op) == VOIDmode || GET_MODE (op) == mode)
+#ifdef LEGITIMATE_PIC_OPERAND_P
+ && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op))
+#endif
+ && LEGITIMATE_CONSTANT_P (op));
+
+ /* Except for certain constants with VOIDmode, already checked for,
+ OP's mode must match MODE if MODE specifies a mode. */
+
+ if (GET_MODE (op) != mode)
+ return 0;
+
+ if (code == SUBREG)
+ {
+#ifdef INSN_SCHEDULING
+ /* On machines that have insn scheduling, we want all memory
+ reference to be explicit, so outlaw paradoxical SUBREGs. */
+ if (GET_CODE (SUBREG_REG (op)) == MEM
+ && GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (op))))
+ return 0;
+#endif
+
+ op = SUBREG_REG (op);
+ code = GET_CODE (op);
+#if 0
+ /* No longer needed, since (SUBREG (MEM...))
+ will load the MEM into a reload reg in the MEM's own mode. */
+ mode_altering_drug = 1;
+#endif
+ }
+
+ if (code == REG)
+ /* A register whose class is NO_REGS is not a general operand. */
+ return (REGNO (op) >= FIRST_PSEUDO_REGISTER
+ || REGNO_REG_CLASS (REGNO (op)) != NO_REGS);
+
+ if (code == MEM)
+ {
+ register rtx y = XEXP (op, 0);
+ if (! volatile_ok && MEM_VOLATILE_P (op))
+ return 0;
+ /* Use the mem's mode, since it will be reloaded thus. */
+ mode = GET_MODE (op);
+ GO_IF_LEGITIMATE_ADDRESS (mode, y, win);
+ }
+ return 0;
+
+ win:
+ if (mode_altering_drug)
+ return ! mode_dependent_address_p (XEXP (op, 0));
+ return 1;
+}
+
+/* Return 1 if OP is a valid memory address for a memory reference
+ of mode MODE.
+
+ The main use of this function is as a predicate in match_operand
+ expressions in the machine description. */
+
+int
+address_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ return memory_address_p (mode, op);
+}
+
+/* Return 1 if OP is a register reference of mode MODE.
+ If MODE is VOIDmode, accept a register in any mode.
+
+ The main use of this function is as a predicate in match_operand
+ expressions in the machine description.
+
+ As a special exception, registers whose class is NO_REGS are
+ not accepted by `register_operand'. The reason for this change
+ is to allow the representation of special architecture artifacts
+ (such as a condition code register) without extending the rtl
+ definitions. Since registers of class NO_REGS cannot be used
+ as registers in any case where register classes are examined,
+ it is most consistent to keep this function from accepting them. */
+
+int
+register_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (GET_MODE (op) != mode && mode != VOIDmode)
+ return 0;
+
+ if (GET_CODE (op) == SUBREG)
+ {
+ /* Before reload, we can allow (SUBREG (MEM...)) as a register operand
+ because it is guaranteed to be reloaded into one.
+ Just make sure the MEM is valid in itself.
+ (Ideally, (SUBREG (MEM)...) should not exist after reload,
+ but currently it does result from (SUBREG (REG)...) where the
+ reg went on the stack.) */
+ if (! reload_completed && GET_CODE (SUBREG_REG (op)) == MEM)
+ return general_operand (op, mode);
+ op = SUBREG_REG (op);
+ }
+
+ /* We don't consider registers whose class is NO_REGS
+ to be a register operand. */
+ return (GET_CODE (op) == REG
+ && (REGNO (op) >= FIRST_PSEUDO_REGISTER
+ || REGNO_REG_CLASS (REGNO (op)) != NO_REGS));
+}
+
+/* Return 1 if OP should match a MATCH_SCRATCH, i.e., if it is a SCRATCH
+ or a hard register. */
+
+int
+scratch_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ return (GET_MODE (op) == mode
+ && (GET_CODE (op) == SCRATCH
+ || (GET_CODE (op) == REG
+ && REGNO (op) < FIRST_PSEUDO_REGISTER)));
+}
+
+/* Return 1 if OP is a valid immediate operand for mode MODE.
+
+ The main use of this function is as a predicate in match_operand
+ expressions in the machine description. */
+
+int
+immediate_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ /* Don't accept CONST_INT or anything similar
+ if the caller wants something floating. */
+ if (GET_MODE (op) == VOIDmode && mode != VOIDmode
+ && GET_MODE_CLASS (mode) != MODE_INT
+ && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
+ return 0;
+
+ return (CONSTANT_P (op)
+ && (GET_MODE (op) == mode || mode == VOIDmode
+ || GET_MODE (op) == VOIDmode)
+#ifdef LEGITIMATE_PIC_OPERAND_P
+ && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op))
+#endif
+ && LEGITIMATE_CONSTANT_P (op));
+}
+
+/* Returns 1 if OP is an operand that is a CONST_INT. */
+
+int
+const_int_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ return GET_CODE (op) == CONST_INT;
+}
+
+/* Returns 1 if OP is an operand that is a constant integer or constant
+ floating-point number. */
+
+int
+const_double_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ /* Don't accept CONST_INT or anything similar
+ if the caller wants something floating. */
+ if (GET_MODE (op) == VOIDmode && mode != VOIDmode
+ && GET_MODE_CLASS (mode) != MODE_INT
+ && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
+ return 0;
+
+ return ((GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT)
+ && (mode == VOIDmode || GET_MODE (op) == mode
+ || GET_MODE (op) == VOIDmode));
+}
+
+/* Return 1 if OP is a general operand that is not an immediate operand. */
+
+int
+nonimmediate_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ return (general_operand (op, mode) && ! CONSTANT_P (op));
+}
+
+/* Return 1 if OP is a register reference or immediate value of mode MODE. */
+
+int
+nonmemory_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (CONSTANT_P (op))
+ {
+ /* Don't accept CONST_INT or anything similar
+ if the caller wants something floating. */
+ if (GET_MODE (op) == VOIDmode && mode != VOIDmode
+ && GET_MODE_CLASS (mode) != MODE_INT
+ && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
+ return 0;
+
+ return ((GET_MODE (op) == VOIDmode || GET_MODE (op) == mode)
+#ifdef LEGITIMATE_PIC_OPERAND_P
+ && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op))
+#endif
+ && LEGITIMATE_CONSTANT_P (op));
+ }
+
+ if (GET_MODE (op) != mode && mode != VOIDmode)
+ return 0;
+
+ if (GET_CODE (op) == SUBREG)
+ {
+ /* Before reload, we can allow (SUBREG (MEM...)) as a register operand
+ because it is guaranteed to be reloaded into one.
+ Just make sure the MEM is valid in itself.
+ (Ideally, (SUBREG (MEM)...) should not exist after reload,
+ but currently it does result from (SUBREG (REG)...) where the
+ reg went on the stack.) */
+ if (! reload_completed && GET_CODE (SUBREG_REG (op)) == MEM)
+ return general_operand (op, mode);
+ op = SUBREG_REG (op);
+ }
+
+ /* We don't consider registers whose class is NO_REGS
+ to be a register operand. */
+ return (GET_CODE (op) == REG
+ && (REGNO (op) >= FIRST_PSEUDO_REGISTER
+ || REGNO_REG_CLASS (REGNO (op)) != NO_REGS));
+}
+
+/* Return 1 if OP is a valid operand that stands for pushing a
+ value of mode MODE onto the stack.
+
+ The main use of this function is as a predicate in match_operand
+ expressions in the machine description. */
+
+int
+push_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) != MEM)
+ return 0;
+
+ if (GET_MODE (op) != mode)
+ return 0;
+
+ op = XEXP (op, 0);
+
+ if (GET_CODE (op) != STACK_PUSH_CODE)
+ return 0;
+
+ return XEXP (op, 0) == stack_pointer_rtx;
+}
+
+/* Return 1 if ADDR is a valid memory address for mode MODE. */
+
+int
+memory_address_p (mode, addr)
+ enum machine_mode mode;
+ register rtx addr;
+{
+ GO_IF_LEGITIMATE_ADDRESS (mode, addr, win);
+ return 0;
+
+ win:
+ return 1;
+}
+
+/* Return 1 if OP is a valid memory reference with mode MODE,
+ including a valid address.
+
+ The main use of this function is as a predicate in match_operand
+ expressions in the machine description. */
+
+int
+memory_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ rtx inner;
+
+ if (! reload_completed)
+ /* Note that no SUBREG is a memory operand before end of reload pass,
+ because (SUBREG (MEM...)) forces reloading into a register. */
+ return GET_CODE (op) == MEM && general_operand (op, mode);
+
+ if (mode != VOIDmode && GET_MODE (op) != mode)
+ return 0;
+
+ inner = op;
+ if (GET_CODE (inner) == SUBREG)
+ inner = SUBREG_REG (inner);
+
+ return (GET_CODE (inner) == MEM && general_operand (op, mode));
+}
+
+/* Return 1 if OP is a valid indirect memory reference with mode MODE;
+ that is, a memory reference whose address is a general_operand. */
+
+int
+indirect_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ /* Before reload, a SUBREG isn't in memory (see memory_operand, above). */
+ if (! reload_completed
+ && GET_CODE (op) == SUBREG && GET_CODE (SUBREG_REG (op)) == MEM)
+ {
+ register int offset = SUBREG_WORD (op) * UNITS_PER_WORD;
+ rtx inner = SUBREG_REG (op);
+
+#if BYTES_BIG_ENDIAN
+ offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (op)))
+ - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (inner))));
+#endif
+
+ if (mode != VOIDmode && GET_MODE (op) != mode)
+ return 0;
+
+ /* The only way that we can have a general_operand as the resulting
+ address is if OFFSET is zero and the address already is an operand
+ or if the address is (plus Y (const_int -OFFSET)) and Y is an
+ operand. */
+
+ return ((offset == 0 && general_operand (XEXP (inner, 0), Pmode))
+ || (GET_CODE (XEXP (inner, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (inner, 0), 1)) == CONST_INT
+ && INTVAL (XEXP (XEXP (inner, 0), 1)) == -offset
+ && general_operand (XEXP (XEXP (inner, 0), 0), Pmode)));
+ }
+
+ return (GET_CODE (op) == MEM
+ && memory_operand (op, mode)
+ && general_operand (XEXP (op, 0), Pmode));
+}
+
+/* Return 1 if this is a comparison operator. This allows the use of
+ MATCH_OPERATOR to recognize all the branch insns. */
+
+int
+comparison_operator (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ return ((mode == VOIDmode || GET_MODE (op) == mode)
+ && GET_RTX_CLASS (GET_CODE (op)) == '<');
+}
+
+/* If BODY is an insn body that uses ASM_OPERANDS,
+ return the number of operands (both input and output) in the insn.
+ Otherwise return -1. */
+
+int
+asm_noperands (body)
+ rtx body;
+{
+ if (GET_CODE (body) == ASM_OPERANDS)
+ /* No output operands: return number of input operands. */
+ return ASM_OPERANDS_INPUT_LENGTH (body);
+ if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS)
+ /* Single output operand: BODY is (set OUTPUT (asm_operands ...)). */
+ return ASM_OPERANDS_INPUT_LENGTH (SET_SRC (body)) + 1;
+ else if (GET_CODE (body) == PARALLEL
+ && GET_CODE (XVECEXP (body, 0, 0)) == SET
+ && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) == ASM_OPERANDS)
+ {
+ /* Multiple output operands, or 1 output plus some clobbers:
+ body is [(set OUTPUT (asm_operands ...))... (clobber (reg ...))...]. */
+ int i;
+ int n_sets;
+
+ /* Count backwards through CLOBBERs to determine number of SETs. */
+ for (i = XVECLEN (body, 0); i > 0; i--)
+ {
+ if (GET_CODE (XVECEXP (body, 0, i - 1)) == SET)
+ break;
+ if (GET_CODE (XVECEXP (body, 0, i - 1)) != CLOBBER)
+ return -1;
+ }
+
+ /* N_SETS is now number of output operands. */
+ n_sets = i;
+
+ /* Verify that all the SETs we have
+ came from a single original asm_operands insn
+ (so that invalid combinations are blocked). */
+ for (i = 0; i < n_sets; i++)
+ {
+ rtx elt = XVECEXP (body, 0, i);
+ if (GET_CODE (elt) != SET)
+ return -1;
+ if (GET_CODE (SET_SRC (elt)) != ASM_OPERANDS)
+ return -1;
+ /* If these ASM_OPERANDS rtx's came from different original insns
+ then they aren't allowed together. */
+ if (ASM_OPERANDS_INPUT_VEC (SET_SRC (elt))
+ != ASM_OPERANDS_INPUT_VEC (SET_SRC (XVECEXP (body, 0, 0))))
+ return -1;
+ }
+ return (ASM_OPERANDS_INPUT_LENGTH (SET_SRC (XVECEXP (body, 0, 0)))
+ + n_sets);
+ }
+ else if (GET_CODE (body) == PARALLEL
+ && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS)
+ {
+ /* 0 outputs, but some clobbers:
+ body is [(asm_operands ...) (clobber (reg ...))...]. */
+ int i;
+
+ /* Make sure all the other parallel things really are clobbers. */
+ for (i = XVECLEN (body, 0) - 1; i > 0; i--)
+ if (GET_CODE (XVECEXP (body, 0, i)) != CLOBBER)
+ return -1;
+
+ return ASM_OPERANDS_INPUT_LENGTH (XVECEXP (body, 0, 0));
+ }
+ else
+ return -1;
+}
+
+/* Assuming BODY is an insn body that uses ASM_OPERANDS,
+ copy its operands (both input and output) into the vector OPERANDS,
+ the locations of the operands within the insn into the vector OPERAND_LOCS,
+ and the constraints for the operands into CONSTRAINTS.
+ Write the modes of the operands into MODES.
+ Return the assembler-template.
+
+ If MODES, OPERAND_LOCS, CONSTRAINTS or OPERANDS is 0,
+ we don't store that info. */
+
+char *
+decode_asm_operands (body, operands, operand_locs, constraints, modes)
+ rtx body;
+ rtx *operands;
+ rtx **operand_locs;
+ char **constraints;
+ enum machine_mode *modes;
+{
+ register int i;
+ int noperands;
+ char *template = 0;
+
+ if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS)
+ {
+ rtx asmop = SET_SRC (body);
+ /* Single output operand: BODY is (set OUTPUT (asm_operands ....)). */
+
+ noperands = ASM_OPERANDS_INPUT_LENGTH (asmop) + 1;
+
+ for (i = 1; i < noperands; i++)
+ {
+ if (operand_locs)
+ operand_locs[i] = &ASM_OPERANDS_INPUT (asmop, i - 1);
+ if (operands)
+ operands[i] = ASM_OPERANDS_INPUT (asmop, i - 1);
+ if (constraints)
+ constraints[i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i - 1);
+ if (modes)
+ modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i - 1);
+ }
+
+ /* The output is in the SET.
+ Its constraint is in the ASM_OPERANDS itself. */
+ if (operands)
+ operands[0] = SET_DEST (body);
+ if (operand_locs)
+ operand_locs[0] = &SET_DEST (body);
+ if (constraints)
+ constraints[0] = ASM_OPERANDS_OUTPUT_CONSTRAINT (asmop);
+ if (modes)
+ modes[0] = GET_MODE (SET_DEST (body));
+ template = ASM_OPERANDS_TEMPLATE (asmop);
+ }
+ else if (GET_CODE (body) == ASM_OPERANDS)
+ {
+ rtx asmop = body;
+ /* No output operands: BODY is (asm_operands ....). */
+
+ noperands = ASM_OPERANDS_INPUT_LENGTH (asmop);
+
+ /* The input operands are found in the 1st element vector. */
+ /* Constraints for inputs are in the 2nd element vector. */
+ for (i = 0; i < noperands; i++)
+ {
+ if (operand_locs)
+ operand_locs[i] = &ASM_OPERANDS_INPUT (asmop, i);
+ if (operands)
+ operands[i] = ASM_OPERANDS_INPUT (asmop, i);
+ if (constraints)
+ constraints[i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i);
+ if (modes)
+ modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i);
+ }
+ template = ASM_OPERANDS_TEMPLATE (asmop);
+ }
+ else if (GET_CODE (body) == PARALLEL
+ && GET_CODE (XVECEXP (body, 0, 0)) == SET)
+ {
+ rtx asmop = SET_SRC (XVECEXP (body, 0, 0));
+ int nparallel = XVECLEN (body, 0); /* Includes CLOBBERs. */
+ int nin = ASM_OPERANDS_INPUT_LENGTH (asmop);
+ int nout = 0; /* Does not include CLOBBERs. */
+
+ /* At least one output, plus some CLOBBERs. */
+
+ /* The outputs are in the SETs.
+ Their constraints are in the ASM_OPERANDS itself. */
+ for (i = 0; i < nparallel; i++)
+ {
+ if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER)
+ break; /* Past last SET */
+
+ if (operands)
+ operands[i] = SET_DEST (XVECEXP (body, 0, i));
+ if (operand_locs)
+ operand_locs[i] = &SET_DEST (XVECEXP (body, 0, i));
+ if (constraints)
+ constraints[i] = XSTR (SET_SRC (XVECEXP (body, 0, i)), 1);
+ if (modes)
+ modes[i] = GET_MODE (SET_DEST (XVECEXP (body, 0, i)));
+ nout++;
+ }
+
+ for (i = 0; i < nin; i++)
+ {
+ if (operand_locs)
+ operand_locs[i + nout] = &ASM_OPERANDS_INPUT (asmop, i);
+ if (operands)
+ operands[i + nout] = ASM_OPERANDS_INPUT (asmop, i);
+ if (constraints)
+ constraints[i + nout] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i);
+ if (modes)
+ modes[i + nout] = ASM_OPERANDS_INPUT_MODE (asmop, i);
+ }
+
+ template = ASM_OPERANDS_TEMPLATE (asmop);
+ }
+ else if (GET_CODE (body) == PARALLEL
+ && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS)
+ {
+ /* No outputs, but some CLOBBERs. */
+
+ rtx asmop = XVECEXP (body, 0, 0);
+ int nin = ASM_OPERANDS_INPUT_LENGTH (asmop);
+
+ for (i = 0; i < nin; i++)
+ {
+ if (operand_locs)
+ operand_locs[i] = &ASM_OPERANDS_INPUT (asmop, i);
+ if (operands)
+ operands[i] = ASM_OPERANDS_INPUT (asmop, i);
+ if (constraints)
+ constraints[i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i);
+ if (modes)
+ modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i);
+ }
+
+ template = ASM_OPERANDS_TEMPLATE (asmop);
+ }
+
+ return template;
+}
+
+/* Given an rtx *P, if it is a sum containing an integer constant term,
+ return the location (type rtx *) of the pointer to that constant term.
+ Otherwise, return a null pointer. */
+
+static rtx *
+find_constant_term_loc (p)
+ rtx *p;
+{
+ register rtx *tem;
+ register enum rtx_code code = GET_CODE (*p);
+
+ /* If *P IS such a constant term, P is its location. */
+
+ if (code == CONST_INT || code == SYMBOL_REF || code == LABEL_REF
+ || code == CONST)
+ return p;
+
+ /* Otherwise, if not a sum, it has no constant term. */
+
+ if (GET_CODE (*p) != PLUS)
+ return 0;
+
+ /* If one of the summands is constant, return its location. */
+
+ if (XEXP (*p, 0) && CONSTANT_P (XEXP (*p, 0))
+ && XEXP (*p, 1) && CONSTANT_P (XEXP (*p, 1)))
+ return p;
+
+ /* Otherwise, check each summand for containing a constant term. */
+
+ if (XEXP (*p, 0) != 0)
+ {
+ tem = find_constant_term_loc (&XEXP (*p, 0));
+ if (tem != 0)
+ return tem;
+ }
+
+ if (XEXP (*p, 1) != 0)
+ {
+ tem = find_constant_term_loc (&XEXP (*p, 1));
+ if (tem != 0)
+ return tem;
+ }
+
+ return 0;
+}
+
+/* Return 1 if OP is a memory reference
+ whose address contains no side effects
+ and remains valid after the addition
+ of a positive integer less than the
+ size of the object being referenced.
+
+ We assume that the original address is valid and do not check it.
+
+ This uses strict_memory_address_p as a subroutine, so
+ don't use it before reload. */
+
+int
+offsettable_memref_p (op)
+ rtx op;
+{
+ return ((GET_CODE (op) == MEM)
+ && offsettable_address_p (1, GET_MODE (op), XEXP (op, 0)));
+}
+
+/* Similar, but don't require a strictly valid mem ref:
+ consider pseudo-regs valid as index or base regs. */
+
+int
+offsettable_nonstrict_memref_p (op)
+ rtx op;
+{
+ return ((GET_CODE (op) == MEM)
+ && offsettable_address_p (0, GET_MODE (op), XEXP (op, 0)));
+}
+
+/* Return 1 if Y is a memory address which contains no side effects
+ and would remain valid after the addition of a positive integer
+ less than the size of that mode.
+
+ We assume that the original address is valid and do not check it.
+ We do check that it is valid for narrower modes.
+
+ If STRICTP is nonzero, we require a strictly valid address,
+ for the sake of use in reload.c. */
+
+int
+offsettable_address_p (strictp, mode, y)
+ int strictp;
+ enum machine_mode mode;
+ register rtx y;
+{
+ register enum rtx_code ycode = GET_CODE (y);
+ register rtx z;
+ rtx y1 = y;
+ rtx *y2;
+ int (*addressp) () = (strictp ? strict_memory_address_p : memory_address_p);
+
+ if (CONSTANT_ADDRESS_P (y))
+ return 1;
+
+ /* Adjusting an offsettable address involves changing to a narrower mode.
+ Make sure that's OK. */
+
+ if (mode_dependent_address_p (y))
+ return 0;
+
+ /* If the expression contains a constant term,
+ see if it remains valid when max possible offset is added. */
+
+ if ((ycode == PLUS) && (y2 = find_constant_term_loc (&y1)))
+ {
+ int good;
+
+ y1 = *y2;
+ *y2 = plus_constant (*y2, GET_MODE_SIZE (mode) - 1);
+ /* Use QImode because an odd displacement may be automatically invalid
+ for any wider mode. But it should be valid for a single byte. */
+ good = (*addressp) (QImode, y);
+
+ /* In any case, restore old contents of memory. */
+ *y2 = y1;
+ return good;
+ }
+
+ if (ycode == PRE_DEC || ycode == PRE_INC
+ || ycode == POST_DEC || ycode == POST_INC)
+ return 0;
+
+ /* The offset added here is chosen as the maximum offset that
+ any instruction could need to add when operating on something
+ of the specified mode. We assume that if Y and Y+c are
+ valid addresses then so is Y+d for all 0<d<c. */
+
+ z = plus_constant_for_output (y, GET_MODE_SIZE (mode) - 1);
+
+ /* Use QImode because an odd displacement may be automatically invalid
+ for any wider mode. But it should be valid for a single byte. */
+ return (*addressp) (QImode, z);
+}
+
+/* Return 1 if ADDR is an address-expression whose effect depends
+ on the mode of the memory reference it is used in.
+
+ Autoincrement addressing is a typical example of mode-dependence
+ because the amount of the increment depends on the mode. */
+
+int
+mode_dependent_address_p (addr)
+ rtx addr;
+{
+ GO_IF_MODE_DEPENDENT_ADDRESS (addr, win);
+ return 0;
+ win:
+ return 1;
+}
+
+/* Return 1 if OP is a general operand
+ other than a memory ref with a mode dependent address. */
+
+int
+mode_independent_operand (op, mode)
+ enum machine_mode mode;
+ rtx op;
+{
+ rtx addr;
+
+ if (! general_operand (op, mode))
+ return 0;
+
+ if (GET_CODE (op) != MEM)
+ return 1;
+
+ addr = XEXP (op, 0);
+ GO_IF_MODE_DEPENDENT_ADDRESS (addr, lose);
+ return 1;
+ lose:
+ return 0;
+}
+
+/* Given an operand OP that is a valid memory reference
+ which satisfies offsettable_memref_p,
+ return a new memory reference whose address has been adjusted by OFFSET.
+ OFFSET should be positive and less than the size of the object referenced.
+*/
+
+rtx
+adj_offsettable_operand (op, offset)
+ rtx op;
+ int offset;
+{
+ register enum rtx_code code = GET_CODE (op);
+
+ if (code == MEM)
+ {
+ register rtx y = XEXP (op, 0);
+ register rtx new;
+
+ if (CONSTANT_ADDRESS_P (y))
+ {
+ new = gen_rtx (MEM, GET_MODE (op), plus_constant_for_output (y, offset));
+ RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (op);
+ return new;
+ }
+
+ if (GET_CODE (y) == PLUS)
+ {
+ rtx z = y;
+ register rtx *const_loc;
+
+ op = copy_rtx (op);
+ z = XEXP (op, 0);
+ const_loc = find_constant_term_loc (&z);
+ if (const_loc)
+ {
+ *const_loc = plus_constant_for_output (*const_loc, offset);
+ return op;
+ }
+ }
+
+ new = gen_rtx (MEM, GET_MODE (op), plus_constant_for_output (y, offset));
+ RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (op);
+ return new;
+ }
+ abort ();
+}
+
+#ifdef REGISTER_CONSTRAINTS
+
+/* Check the operands of an insn (found in recog_operands)
+ against the insn's operand constraints (found via INSN_CODE_NUM)
+ and return 1 if they are valid.
+
+ WHICH_ALTERNATIVE is set to a number which indicates which
+ alternative of constraints was matched: 0 for the first alternative,
+ 1 for the next, etc.
+
+ In addition, when two operands are match
+ and it happens that the output operand is (reg) while the
+ input operand is --(reg) or ++(reg) (a pre-inc or pre-dec),
+ make the output operand look like the input.
+ This is because the output operand is the one the template will print.
+
+ This is used in final, just before printing the assembler code and by
+ the routines that determine an insn's attribute.
+
+ If STRICT is a positive non-zero value, it means that we have been
+ called after reload has been completed. In that case, we must
+ do all checks strictly. If it is zero, it means that we have been called
+ before reload has completed. In that case, we first try to see if we can
+ find an alternative that matches strictly. If not, we try again, this
+ time assuming that reload will fix up the insn. This provides a "best
+ guess" for the alternative and is used to compute attributes of insns prior
+ to reload. A negative value of STRICT is used for this internal call. */
+
+struct funny_match
+{
+ int this, other;
+};
+
+int
+constrain_operands (insn_code_num, strict)
+ int insn_code_num;
+ int strict;
+{
+ char *constraints[MAX_RECOG_OPERANDS];
+ int matching_operands[MAX_RECOG_OPERANDS];
+ enum op_type {OP_IN, OP_OUT, OP_INOUT} op_types[MAX_RECOG_OPERANDS];
+ int earlyclobber[MAX_RECOG_OPERANDS];
+ register int c;
+ int noperands = insn_n_operands[insn_code_num];
+
+ struct funny_match funny_match[MAX_RECOG_OPERANDS];
+ int funny_match_index;
+ int nalternatives = insn_n_alternatives[insn_code_num];
+
+ if (noperands == 0 || nalternatives == 0)
+ return 1;
+
+ for (c = 0; c < noperands; c++)
+ {
+ constraints[c] = insn_operand_constraint[insn_code_num][c];
+ matching_operands[c] = -1;
+ op_types[c] = OP_IN;
+ }
+
+ which_alternative = 0;
+
+ while (which_alternative < nalternatives)
+ {
+ register int opno;
+ int lose = 0;
+ funny_match_index = 0;
+
+ for (opno = 0; opno < noperands; opno++)
+ {
+ register rtx op = recog_operand[opno];
+ enum machine_mode mode = GET_MODE (op);
+ register char *p = constraints[opno];
+ int offset = 0;
+ int win = 0;
+ int val;
+
+ earlyclobber[opno] = 0;
+
+ if (GET_CODE (op) == SUBREG)
+ {
+ if (GET_CODE (SUBREG_REG (op)) == REG
+ && REGNO (SUBREG_REG (op)) < FIRST_PSEUDO_REGISTER)
+ offset = SUBREG_WORD (op);
+ op = SUBREG_REG (op);
+ }
+
+ /* An empty constraint or empty alternative
+ allows anything which matched the pattern. */
+ if (*p == 0 || *p == ',')
+ win = 1;
+
+ while (*p && (c = *p++) != ',')
+ switch (c)
+ {
+ case '?':
+ case '!':
+ case '*':
+ case '%':
+ break;
+
+ case '#':
+ /* Ignore rest of this alternative as far as
+ constraint checking is concerned. */
+ while (*p && *p != ',')
+ p++;
+ break;
+
+ case '=':
+ op_types[opno] = OP_OUT;
+ break;
+
+ case '+':
+ op_types[opno] = OP_INOUT;
+ break;
+
+ case '&':
+ earlyclobber[opno] = 1;
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ /* This operand must be the same as a previous one.
+ This kind of constraint is used for instructions such
+ as add when they take only two operands.
+
+ Note that the lower-numbered operand is passed first.
+
+ If we are not testing strictly, assume that this constraint
+ will be satisfied. */
+ if (strict < 0)
+ val = 1;
+ else
+ val = operands_match_p (recog_operand[c - '0'],
+ recog_operand[opno]);
+
+ matching_operands[opno] = c - '0';
+ matching_operands[c - '0'] = opno;
+
+ if (val != 0)
+ win = 1;
+ /* If output is *x and input is *--x,
+ arrange later to change the output to *--x as well,
+ since the output op is the one that will be printed. */
+ if (val == 2 && strict > 0)
+ {
+ funny_match[funny_match_index].this = opno;
+ funny_match[funny_match_index++].other = c - '0';
+ }
+ break;
+
+ case 'p':
+ /* p is used for address_operands. When we are called by
+ gen_input_reload, no one will have checked that the
+ address is strictly valid, i.e., that all pseudos
+ requiring hard regs have gotten them. */
+ if (strict <= 0
+ || (strict_memory_address_p
+ (insn_operand_mode[insn_code_num][opno], op)))
+ win = 1;
+ break;
+
+ /* No need to check general_operand again;
+ it was done in insn-recog.c. */
+ case 'g':
+ /* Anything goes unless it is a REG and really has a hard reg
+ but the hard reg is not in the class GENERAL_REGS. */
+ if (strict < 0
+ || GENERAL_REGS == ALL_REGS
+ || GET_CODE (op) != REG
+ || (reload_in_progress
+ && REGNO (op) >= FIRST_PSEUDO_REGISTER)
+ || reg_fits_class_p (op, GENERAL_REGS, offset, mode))
+ win = 1;
+ break;
+
+ case 'r':
+ if (strict < 0
+ || (strict == 0
+ && GET_CODE (op) == REG
+ && REGNO (op) >= FIRST_PSEUDO_REGISTER)
+ || (strict == 0 && GET_CODE (op) == SCRATCH)
+ || (GET_CODE (op) == REG
+ && ((GENERAL_REGS == ALL_REGS
+ && REGNO (op) < FIRST_PSEUDO_REGISTER)
+ || reg_fits_class_p (op, GENERAL_REGS,
+ offset, mode))))
+ win = 1;
+ break;
+
+ case 'X':
+ /* This is used for a MATCH_SCRATCH in the cases when we
+ don't actually need anything. So anything goes any time. */
+ win = 1;
+ break;
+
+ case 'm':
+ if (GET_CODE (op) == MEM
+ /* Before reload, accept what reload can turn into mem. */
+ || (strict < 0 && CONSTANT_P (op))
+ /* During reload, accept a pseudo */
+ || (reload_in_progress && GET_CODE (op) == REG
+ && REGNO (op) >= FIRST_PSEUDO_REGISTER))
+ win = 1;
+ break;
+
+ case '<':
+ if (GET_CODE (op) == MEM
+ && (GET_CODE (XEXP (op, 0)) == PRE_DEC
+ || GET_CODE (XEXP (op, 0)) == POST_DEC))
+ win = 1;
+ break;
+
+ case '>':
+ if (GET_CODE (op) == MEM
+ && (GET_CODE (XEXP (op, 0)) == PRE_INC
+ || GET_CODE (XEXP (op, 0)) == POST_INC))
+ win = 1;
+ break;
+
+ case 'E':
+ /* Match any CONST_DOUBLE, but only if
+ we can examine the bits of it reliably. */
+ if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
+ || HOST_BITS_PER_WIDE_INT != BITS_PER_WORD)
+ && GET_MODE (op) != VOIDmode && ! flag_pretend_float)
+ break;
+ if (GET_CODE (op) == CONST_DOUBLE)
+ win = 1;
+ break;
+
+ case 'F':
+ if (GET_CODE (op) == CONST_DOUBLE)
+ win = 1;
+ break;
+
+ case 'G':
+ case 'H':
+ if (GET_CODE (op) == CONST_DOUBLE
+ && CONST_DOUBLE_OK_FOR_LETTER_P (op, c))
+ win = 1;
+ break;
+
+ case 's':
+ if (GET_CODE (op) == CONST_INT
+ || (GET_CODE (op) == CONST_DOUBLE
+ && GET_MODE (op) == VOIDmode))
+ break;
+ case 'i':
+ if (CONSTANT_P (op))
+ win = 1;
+ break;
+
+ case 'n':
+ if (GET_CODE (op) == CONST_INT
+ || (GET_CODE (op) == CONST_DOUBLE
+ && GET_MODE (op) == VOIDmode))
+ win = 1;
+ break;
+
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ if (GET_CODE (op) == CONST_INT
+ && CONST_OK_FOR_LETTER_P (INTVAL (op), c))
+ win = 1;
+ break;
+
+#ifdef EXTRA_CONSTRAINT
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ if (EXTRA_CONSTRAINT (op, c))
+ win = 1;
+ break;
+#endif
+
+ case 'V':
+ if (GET_CODE (op) == MEM
+ && ! offsettable_memref_p (op))
+ win = 1;
+ break;
+
+ case 'o':
+ if ((strict > 0 && offsettable_memref_p (op))
+ || (strict == 0 && offsettable_nonstrict_memref_p (op))
+ /* Before reload, accept what reload can handle. */
+ || (strict < 0
+ && (CONSTANT_P (op) || GET_CODE (op) == MEM))
+ /* During reload, accept a pseudo */
+ || (reload_in_progress && GET_CODE (op) == REG
+ && REGNO (op) >= FIRST_PSEUDO_REGISTER))
+ win = 1;
+ break;
+
+ default:
+ if (strict < 0
+ || (strict == 0
+ && GET_CODE (op) == REG
+ && REGNO (op) >= FIRST_PSEUDO_REGISTER)
+ || (strict == 0 && GET_CODE (op) == SCRATCH)
+ || (GET_CODE (op) == REG
+ && reg_fits_class_p (op, REG_CLASS_FROM_LETTER (c),
+ offset, mode)))
+ win = 1;
+ }
+
+ constraints[opno] = p;
+ /* If this operand did not win somehow,
+ this alternative loses. */
+ if (! win)
+ lose = 1;
+ }
+ /* This alternative won; the operands are ok.
+ Change whichever operands this alternative says to change. */
+ if (! lose)
+ {
+ int opno, eopno;
+
+ /* See if any earlyclobber operand conflicts with some other
+ operand. */
+
+ if (strict > 0)
+ for (eopno = 0; eopno < noperands; eopno++)
+ /* Ignore earlyclobber operands now in memory,
+ because we would often report failure when we have
+ two memory operands, one of which was formerly a REG. */
+ if (earlyclobber[eopno]
+ && GET_CODE (recog_operand[eopno]) == REG)
+ for (opno = 0; opno < noperands; opno++)
+ if ((GET_CODE (recog_operand[opno]) == MEM
+ || op_types[opno] != OP_OUT)
+ && opno != eopno
+ /* Ignore things like match_operator operands. */
+ && *constraints[opno] != 0
+ && ! (matching_operands[opno] == eopno
+ && rtx_equal_p (recog_operand[opno],
+ recog_operand[eopno]))
+ && ! safe_from_earlyclobber (recog_operand[opno],
+ recog_operand[eopno]))
+ lose = 1;
+
+ if (! lose)
+ {
+ while (--funny_match_index >= 0)
+ {
+ recog_operand[funny_match[funny_match_index].other]
+ = recog_operand[funny_match[funny_match_index].this];
+ }
+
+ return 1;
+ }
+ }
+
+ which_alternative++;
+ }
+
+ /* If we are about to reject this, but we are not to test strictly,
+ try a very loose test. Only return failure if it fails also. */
+ if (strict == 0)
+ return constrain_operands (insn_code_num, -1);
+ else
+ return 0;
+}
+
+/* Return 1 iff OPERAND (assumed to be a REG rtx)
+ is a hard reg in class CLASS when its regno is offsetted by OFFSET
+ and changed to mode MODE.
+ If REG occupies multiple hard regs, all of them must be in CLASS. */
+
+int
+reg_fits_class_p (operand, class, offset, mode)
+ rtx operand;
+ register enum reg_class class;
+ int offset;
+ enum machine_mode mode;
+{
+ register int regno = REGNO (operand);
+ if (regno < FIRST_PSEUDO_REGISTER
+ && TEST_HARD_REG_BIT (reg_class_contents[(int) class],
+ regno + offset))
+ {
+ register int sr;
+ regno += offset;
+ for (sr = HARD_REGNO_NREGS (regno, mode) - 1;
+ sr > 0; sr--)
+ if (! TEST_HARD_REG_BIT (reg_class_contents[(int) class],
+ regno + sr))
+ break;
+ return sr == 0;
+ }
+
+ return 0;
+}
+
+#endif /* REGISTER_CONSTRAINTS */
diff --git a/gnu/usr.bin/cc/cc_int/reg-stack.c b/gnu/usr.bin/cc/cc_int/reg-stack.c
new file mode 100644
index 0000000..dd30344
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/reg-stack.c
@@ -0,0 +1,3008 @@
+/* Register to Stack convert for GNU compiler.
+ Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This pass converts stack-like registers from the "flat register
+ file" model that gcc uses, to a stack convention that the 387 uses.
+
+ * The form of the input:
+
+ On input, the function consists of insn that have had their
+ registers fully allocated to a set of "virtual" registers. Note that
+ the word "virtual" is used differently here than elsewhere in gcc: for
+ each virtual stack reg, there is a hard reg, but the mapping between
+ them is not known until this pass is run. On output, hard register
+ numbers have been substituted, and various pop and exchange insns have
+ been emitted. The hard register numbers and the virtual register
+ numbers completely overlap - before this pass, all stack register
+ numbers are virtual, and afterward they are all hard.
+
+ The virtual registers can be manipulated normally by gcc, and their
+ semantics are the same as for normal registers. After the hard
+ register numbers are substituted, the semantics of an insn containing
+ stack-like regs are not the same as for an insn with normal regs: for
+ instance, it is not safe to delete an insn that appears to be a no-op
+ move. In general, no insn containing hard regs should be changed
+ after this pass is done.
+
+ * The form of the output:
+
+ After this pass, hard register numbers represent the distance from
+ the current top of stack to the desired register. A reference to
+ FIRST_STACK_REG references the top of stack, FIRST_STACK_REG + 1,
+ represents the register just below that, and so forth. Also, REG_DEAD
+ notes indicate whether or not a stack register should be popped.
+
+ A "swap" insn looks like a parallel of two patterns, where each
+ pattern is a SET: one sets A to B, the other B to A.
+
+ A "push" or "load" insn is a SET whose SET_DEST is FIRST_STACK_REG
+ and whose SET_DEST is REG or MEM. Any other SET_DEST, such as PLUS,
+ will replace the existing stack top, not push a new value.
+
+ A store insn is a SET whose SET_DEST is FIRST_STACK_REG, and whose
+ SET_SRC is REG or MEM.
+
+ The case where the SET_SRC and SET_DEST are both FIRST_STACK_REG
+ appears ambiguous. As a special case, the presence of a REG_DEAD note
+ for FIRST_STACK_REG differentiates between a load insn and a pop.
+
+ If a REG_DEAD is present, the insn represents a "pop" that discards
+ the top of the register stack. If there is no REG_DEAD note, then the
+ insn represents a "dup" or a push of the current top of stack onto the
+ stack.
+
+ * Methodology:
+
+ Existing REG_DEAD and REG_UNUSED notes for stack registers are
+ deleted and recreated from scratch. REG_DEAD is never created for a
+ SET_DEST, only REG_UNUSED.
+
+ Before life analysis, the mode of each insn is set based on whether
+ or not any stack registers are mentioned within that insn. VOIDmode
+ means that no regs are mentioned anyway, and QImode means that at
+ least one pattern within the insn mentions stack registers. This
+ information is valid until after reg_to_stack returns, and is used
+ from jump_optimize.
+
+ * asm_operands:
+
+ There are several rules on the usage of stack-like regs in
+ asm_operands insns. These rules apply only to the operands that are
+ stack-like regs:
+
+ 1. Given a set of input regs that die in an asm_operands, it is
+ necessary to know which are implicitly popped by the asm, and
+ which must be explicitly popped by gcc.
+
+ An input reg that is implicitly popped by the asm must be
+ explicitly clobbered, unless it is constrained to match an
+ output operand.
+
+ 2. For any input reg that is implicitly popped by an asm, it is
+ necessary to know how to adjust the stack to compensate for the pop.
+ If any non-popped input is closer to the top of the reg-stack than
+ the implicitly popped reg, it would not be possible to know what the
+ stack looked like - it's not clear how the rest of the stack "slides
+ up".
+
+ All implicitly popped input regs must be closer to the top of
+ the reg-stack than any input that is not implicitly popped.
+
+ 3. It is possible that if an input dies in an insn, reload might
+ use the input reg for an output reload. Consider this example:
+
+ asm ("foo" : "=t" (a) : "f" (b));
+
+ This asm says that input B is not popped by the asm, and that
+ the asm pushes a result onto the reg-stack, ie, the stack is one
+ deeper after the asm than it was before. But, it is possible that
+ reload will think that it can use the same reg for both the input and
+ the output, if input B dies in this insn.
+
+ If any input operand uses the "f" constraint, all output reg
+ constraints must use the "&" earlyclobber.
+
+ The asm above would be written as
+
+ asm ("foo" : "=&t" (a) : "f" (b));
+
+ 4. Some operands need to be in particular places on the stack. All
+ output operands fall in this category - there is no other way to
+ know which regs the outputs appear in unless the user indicates
+ this in the constraints.
+
+ Output operands must specifically indicate which reg an output
+ appears in after an asm. "=f" is not allowed: the operand
+ constraints must select a class with a single reg.
+
+ 5. Output operands may not be "inserted" between existing stack regs.
+ Since no 387 opcode uses a read/write operand, all output operands
+ are dead before the asm_operands, and are pushed by the asm_operands.
+ It makes no sense to push anywhere but the top of the reg-stack.
+
+ Output operands must start at the top of the reg-stack: output
+ operands may not "skip" a reg.
+
+ 6. Some asm statements may need extra stack space for internal
+ calculations. This can be guaranteed by clobbering stack registers
+ unrelated to the inputs and outputs.
+
+ Here are a couple of reasonable asms to want to write. This asm
+ takes one input, which is internally popped, and produces two outputs.
+
+ asm ("fsincos" : "=t" (cos), "=u" (sin) : "0" (inp));
+
+ This asm takes two inputs, which are popped by the fyl2xp1 opcode,
+ and replaces them with one output. The user must code the "st(1)"
+ clobber for reg-stack.c to know that fyl2xp1 pops both inputs.
+
+ asm ("fyl2xp1" : "=t" (result) : "0" (x), "u" (y) : "st(1)");
+
+ */
+
+#include <stdio.h>
+#include "config.h"
+#include "tree.h"
+#include "rtl.h"
+#include "insn-config.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "flags.h"
+
+#ifdef STACK_REGS
+
+#define REG_STACK_SIZE (LAST_STACK_REG - FIRST_STACK_REG + 1)
+
+/* True if the current function returns a real value. */
+static int current_function_returns_real;
+
+/* This is the basic stack record. TOP is an index into REG[] such
+ that REG[TOP] is the top of stack. If TOP is -1 the stack is empty.
+
+ If TOP is -2, REG[] is not yet initialized. Stack initialization
+ consists of placing each live reg in array `reg' and setting `top'
+ appropriately.
+
+ REG_SET indicates which registers are live. */
+
+typedef struct stack_def
+{
+ int top; /* index to top stack element */
+ HARD_REG_SET reg_set; /* set of live registers */
+ char reg[REG_STACK_SIZE]; /* register - stack mapping */
+} *stack;
+
+/* highest instruction uid */
+static int max_uid = 0;
+
+/* Number of basic blocks in the current function. */
+static int blocks;
+
+/* Element N is first insn in basic block N.
+ This info lasts until we finish compiling the function. */
+static rtx *block_begin;
+
+/* Element N is last insn in basic block N.
+ This info lasts until we finish compiling the function. */
+static rtx *block_end;
+
+/* Element N is nonzero if control can drop into basic block N */
+static char *block_drops_in;
+
+/* Element N says all about the stack at entry block N */
+static stack block_stack_in;
+
+/* Element N says all about the stack life at the end of block N */
+static HARD_REG_SET *block_out_reg_set;
+
+/* This is where the BLOCK_NUM values are really stored. This is set
+ up by find_blocks and used there and in life_analysis. It can be used
+ later, but only to look up an insn that is the head or tail of some
+ block. life_analysis and the stack register conversion process can
+ add insns within a block. */
+static int *block_number;
+
+/* This is the register file for all register after conversion */
+static rtx FP_mode_reg[FIRST_PSEUDO_REGISTER][(int) MAX_MACHINE_MODE];
+
+/* Get the basic block number of an insn. See note at block_number
+ definition are validity of this information. */
+
+#define BLOCK_NUM(INSN) \
+ (((INSN_UID (INSN) > max_uid) \
+ ? (int *)(abort() , 0) \
+ : block_number)[INSN_UID (INSN)])
+
+extern rtx forced_labels;
+extern rtx gen_jump ();
+extern rtx gen_movdf (), gen_movxf ();
+extern rtx find_regno_note ();
+extern rtx emit_jump_insn_before ();
+extern rtx emit_label_after ();
+
+/* Forward declarations */
+
+static void find_blocks ();
+static uses_reg_or_mem ();
+static void stack_reg_life_analysis ();
+static void change_stack ();
+static void convert_regs ();
+static void dump_stack_info ();
+
+/* Return non-zero if any stack register is mentioned somewhere within PAT. */
+
+int
+stack_regs_mentioned_p (pat)
+ rtx pat;
+{
+ register char *fmt;
+ register int i;
+
+ if (STACK_REG_P (pat))
+ return 1;
+
+ fmt = GET_RTX_FORMAT (GET_CODE (pat));
+ for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ register int j;
+
+ for (j = XVECLEN (pat, i) - 1; j >= 0; j--)
+ if (stack_regs_mentioned_p (XVECEXP (pat, i, j)))
+ return 1;
+ }
+ else if (fmt[i] == 'e' && stack_regs_mentioned_p (XEXP (pat, i)))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Convert register usage from "flat" register file usage to a "stack
+ register file. FIRST is the first insn in the function, FILE is the
+ dump file, if used.
+
+ First compute the beginning and end of each basic block. Do a
+ register life analysis on the stack registers, recording the result
+ for the head and tail of each basic block. The convert each insn one
+ by one. Run a last jump_optimize() pass, if optimizing, to eliminate
+ any cross-jumping created when the converter inserts pop insns.*/
+
+void
+reg_to_stack (first, file)
+ rtx first;
+ FILE *file;
+{
+ register rtx insn;
+ register int i;
+ int stack_reg_seen = 0;
+ enum machine_mode mode;
+
+ current_function_returns_real
+ = TREE_CODE (TREE_TYPE (DECL_RESULT (current_function_decl))) == REAL_TYPE;
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ FP_mode_reg[i][(int) mode] = gen_rtx (REG, mode, i);
+
+ /* Count the basic blocks. Also find maximum insn uid. */
+ {
+ register RTX_CODE prev_code = BARRIER;
+ register RTX_CODE code;
+
+ max_uid = 0;
+ blocks = 0;
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ /* Note that this loop must select the same block boundaries
+ as code in find_blocks. Also note that this code is not the
+ same as that used in flow.c. */
+
+ if (INSN_UID (insn) > max_uid)
+ max_uid = INSN_UID (insn);
+
+ code = GET_CODE (insn);
+
+ if (code == CODE_LABEL
+ || (prev_code != INSN
+ && prev_code != CALL_INSN
+ && prev_code != CODE_LABEL
+ && GET_RTX_CLASS (code) == 'i'))
+ blocks++;
+
+ /* Remember whether or not this insn mentions an FP regs.
+ Check JUMP_INSNs too, in case someone creates a funny PARALLEL. */
+
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && stack_regs_mentioned_p (PATTERN (insn)))
+ {
+ stack_reg_seen = 1;
+ PUT_MODE (insn, QImode);
+ }
+ else
+ PUT_MODE (insn, VOIDmode);
+
+ if (code == CODE_LABEL)
+ LABEL_REFS (insn) = insn; /* delete old chain */
+
+ if (code != NOTE)
+ prev_code = code;
+ }
+ }
+
+ /* If no stack register reference exists in this insn, there isn't
+ anything to convert. */
+
+ if (! stack_reg_seen)
+ return;
+
+ /* If there are stack registers, there must be at least one block. */
+
+ if (! blocks)
+ abort ();
+
+ /* Allocate some tables that last till end of compiling this function
+ and some needed only in find_blocks and life_analysis. */
+
+ block_begin = (rtx *) alloca (blocks * sizeof (rtx));
+ block_end = (rtx *) alloca (blocks * sizeof (rtx));
+ block_drops_in = (char *) alloca (blocks);
+
+ block_stack_in = (stack) alloca (blocks * sizeof (struct stack_def));
+ block_out_reg_set = (HARD_REG_SET *) alloca (blocks * sizeof (HARD_REG_SET));
+ bzero (block_stack_in, blocks * sizeof (struct stack_def));
+ bzero (block_out_reg_set, blocks * sizeof (HARD_REG_SET));
+
+ block_number = (int *) alloca ((max_uid + 1) * sizeof (int));
+
+ find_blocks (first);
+ stack_reg_life_analysis (first);
+
+ /* Dump the life analysis debug information before jump
+ optimization, as that will destroy the LABEL_REFS we keep the
+ information in. */
+
+ if (file)
+ dump_stack_info (file);
+
+ convert_regs ();
+
+ if (optimize)
+ jump_optimize (first, 2, 0, 0);
+}
+
+/* Check PAT, which is in INSN, for LABEL_REFs. Add INSN to the
+ label's chain of references, and note which insn contains each
+ reference. */
+
+static void
+record_label_references (insn, pat)
+ rtx insn, pat;
+{
+ register enum rtx_code code = GET_CODE (pat);
+ register int i;
+ register char *fmt;
+
+ if (code == LABEL_REF)
+ {
+ register rtx label = XEXP (pat, 0);
+ register rtx ref;
+
+ if (GET_CODE (label) != CODE_LABEL)
+ abort ();
+
+ /* Don't make a duplicate in the code_label's chain. */
+
+ for (ref = LABEL_REFS (label);
+ ref && ref != label;
+ ref = LABEL_NEXTREF (ref))
+ if (CONTAINING_INSN (ref) == insn)
+ return;
+
+ CONTAINING_INSN (pat) = insn;
+ LABEL_NEXTREF (pat) = LABEL_REFS (label);
+ LABEL_REFS (label) = pat;
+
+ return;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ record_label_references (insn, XEXP (pat, i));
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (pat, i); j++)
+ record_label_references (insn, XVECEXP (pat, i, j));
+ }
+ }
+}
+
+/* Return a pointer to the REG expression within PAT. If PAT is not a
+ REG, possible enclosed by a conversion rtx, return the inner part of
+ PAT that stopped the search. */
+
+static rtx *
+get_true_reg (pat)
+ rtx *pat;
+{
+ while (GET_CODE (*pat) == SUBREG
+ || GET_CODE (*pat) == FLOAT
+ || GET_CODE (*pat) == FIX
+ || GET_CODE (*pat) == FLOAT_EXTEND)
+ pat = & XEXP (*pat, 0);
+
+ return pat;
+}
+
+/* Scan the OPERANDS and OPERAND_CONSTRAINTS of an asm_operands.
+ N_OPERANDS is the total number of operands. Return which alternative
+ matched, or -1 is no alternative matches.
+
+ OPERAND_MATCHES is an array which indicates which operand this
+ operand matches due to the constraints, or -1 if no match is required.
+ If two operands match by coincidence, but are not required to match by
+ the constraints, -1 is returned.
+
+ OPERAND_CLASS is an array which indicates the smallest class
+ required by the constraints. If the alternative that matches calls
+ for some class `class', and the operand matches a subclass of `class',
+ OPERAND_CLASS is set to `class' as required by the constraints, not to
+ the subclass. If an alternative allows more than one class,
+ OPERAND_CLASS is set to the smallest class that is a union of the
+ allowed classes. */
+
+static int
+constrain_asm_operands (n_operands, operands, operand_constraints,
+ operand_matches, operand_class)
+ int n_operands;
+ rtx *operands;
+ char **operand_constraints;
+ int *operand_matches;
+ enum reg_class *operand_class;
+{
+ char **constraints = (char **) alloca (n_operands * sizeof (char *));
+ char *q;
+ int this_alternative, this_operand;
+ int n_alternatives;
+ int j;
+
+ for (j = 0; j < n_operands; j++)
+ constraints[j] = operand_constraints[j];
+
+ /* Compute the number of alternatives in the operands. reload has
+ already guaranteed that all operands have the same number of
+ alternatives. */
+
+ n_alternatives = 1;
+ for (q = constraints[0]; *q; q++)
+ n_alternatives += (*q == ',');
+
+ this_alternative = 0;
+ while (this_alternative < n_alternatives)
+ {
+ int lose = 0;
+ int i;
+
+ /* No operands match, no narrow class requirements yet. */
+ for (i = 0; i < n_operands; i++)
+ {
+ operand_matches[i] = -1;
+ operand_class[i] = NO_REGS;
+ }
+
+ for (this_operand = 0; this_operand < n_operands; this_operand++)
+ {
+ rtx op = operands[this_operand];
+ enum machine_mode mode = GET_MODE (op);
+ char *p = constraints[this_operand];
+ int offset = 0;
+ int win = 0;
+ int c;
+
+ if (GET_CODE (op) == SUBREG)
+ {
+ if (GET_CODE (SUBREG_REG (op)) == REG
+ && REGNO (SUBREG_REG (op)) < FIRST_PSEUDO_REGISTER)
+ offset = SUBREG_WORD (op);
+ op = SUBREG_REG (op);
+ }
+
+ /* An empty constraint or empty alternative
+ allows anything which matched the pattern. */
+ if (*p == 0 || *p == ',')
+ win = 1;
+
+ while (*p && (c = *p++) != ',')
+ switch (c)
+ {
+ case '=':
+ case '+':
+ case '?':
+ case '&':
+ case '!':
+ case '*':
+ case '%':
+ /* Ignore these. */
+ break;
+
+ case '#':
+ /* Ignore rest of this alternative. */
+ while (*p && *p != ',') p++;
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ /* This operand must be the same as a previous one.
+ This kind of constraint is used for instructions such
+ as add when they take only two operands.
+
+ Note that the lower-numbered operand is passed first. */
+
+ if (operands_match_p (operands[c - '0'],
+ operands[this_operand]))
+ {
+ operand_matches[this_operand] = c - '0';
+ win = 1;
+ }
+ break;
+
+ case 'p':
+ /* p is used for address_operands. Since this is an asm,
+ just to make sure that the operand is valid for Pmode. */
+
+ if (strict_memory_address_p (Pmode, op))
+ win = 1;
+ break;
+
+ case 'g':
+ /* Anything goes unless it is a REG and really has a hard reg
+ but the hard reg is not in the class GENERAL_REGS. */
+ if (GENERAL_REGS == ALL_REGS
+ || GET_CODE (op) != REG
+ || reg_fits_class_p (op, GENERAL_REGS, offset, mode))
+ {
+ if (GET_CODE (op) == REG)
+ operand_class[this_operand]
+ = reg_class_subunion[(int) operand_class[this_operand]][(int) GENERAL_REGS];
+ win = 1;
+ }
+ break;
+
+ case 'r':
+ if (GET_CODE (op) == REG
+ && (GENERAL_REGS == ALL_REGS
+ || reg_fits_class_p (op, GENERAL_REGS, offset, mode)))
+ {
+ operand_class[this_operand]
+ = reg_class_subunion[(int) operand_class[this_operand]][(int) GENERAL_REGS];
+ win = 1;
+ }
+ break;
+
+ case 'X':
+ /* This is used for a MATCH_SCRATCH in the cases when we
+ don't actually need anything. So anything goes any time. */
+ win = 1;
+ break;
+
+ case 'm':
+ if (GET_CODE (op) == MEM)
+ win = 1;
+ break;
+
+ case '<':
+ if (GET_CODE (op) == MEM
+ && (GET_CODE (XEXP (op, 0)) == PRE_DEC
+ || GET_CODE (XEXP (op, 0)) == POST_DEC))
+ win = 1;
+ break;
+
+ case '>':
+ if (GET_CODE (op) == MEM
+ && (GET_CODE (XEXP (op, 0)) == PRE_INC
+ || GET_CODE (XEXP (op, 0)) == POST_INC))
+ win = 1;
+ break;
+
+ case 'E':
+ /* Match any CONST_DOUBLE, but only if
+ we can examine the bits of it reliably. */
+ if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
+ || HOST_BITS_PER_WIDE_INT != BITS_PER_WORD)
+ && GET_CODE (op) != VOIDmode && ! flag_pretend_float)
+ break;
+ if (GET_CODE (op) == CONST_DOUBLE)
+ win = 1;
+ break;
+
+ case 'F':
+ if (GET_CODE (op) == CONST_DOUBLE)
+ win = 1;
+ break;
+
+ case 'G':
+ case 'H':
+ if (GET_CODE (op) == CONST_DOUBLE
+ && CONST_DOUBLE_OK_FOR_LETTER_P (op, c))
+ win = 1;
+ break;
+
+ case 's':
+ if (GET_CODE (op) == CONST_INT
+ || (GET_CODE (op) == CONST_DOUBLE
+ && GET_MODE (op) == VOIDmode))
+ break;
+ /* Fall through */
+ case 'i':
+ if (CONSTANT_P (op))
+ win = 1;
+ break;
+
+ case 'n':
+ if (GET_CODE (op) == CONST_INT
+ || (GET_CODE (op) == CONST_DOUBLE
+ && GET_MODE (op) == VOIDmode))
+ win = 1;
+ break;
+
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ if (GET_CODE (op) == CONST_INT
+ && CONST_OK_FOR_LETTER_P (INTVAL (op), c))
+ win = 1;
+ break;
+
+#ifdef EXTRA_CONSTRAINT
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ if (EXTRA_CONSTRAINT (op, c))
+ win = 1;
+ break;
+#endif
+
+ case 'V':
+ if (GET_CODE (op) == MEM && ! offsettable_memref_p (op))
+ win = 1;
+ break;
+
+ case 'o':
+ if (offsettable_memref_p (op))
+ win = 1;
+ break;
+
+ default:
+ if (GET_CODE (op) == REG
+ && reg_fits_class_p (op, REG_CLASS_FROM_LETTER (c),
+ offset, mode))
+ {
+ operand_class[this_operand]
+ = reg_class_subunion[(int)operand_class[this_operand]][(int) REG_CLASS_FROM_LETTER (c)];
+ win = 1;
+ }
+ }
+
+ constraints[this_operand] = p;
+ /* If this operand did not win somehow,
+ this alternative loses. */
+ if (! win)
+ lose = 1;
+ }
+ /* This alternative won; the operands are ok.
+ Change whichever operands this alternative says to change. */
+ if (! lose)
+ break;
+
+ this_alternative++;
+ }
+
+ /* For operands constrained to match another operand, copy the other
+ operand's class to this operand's class. */
+ for (j = 0; j < n_operands; j++)
+ if (operand_matches[j] >= 0)
+ operand_class[j] = operand_class[operand_matches[j]];
+
+ return this_alternative == n_alternatives ? -1 : this_alternative;
+}
+
+/* Record the life info of each stack reg in INSN, updating REGSTACK.
+ N_INPUTS is the number of inputs; N_OUTPUTS the outputs. CONSTRAINTS
+ is an array of the constraint strings used in the asm statement.
+ OPERANDS is an array of all operands for the insn, and is assumed to
+ contain all output operands, then all inputs operands.
+
+ There are many rules that an asm statement for stack-like regs must
+ follow. Those rules are explained at the top of this file: the rule
+ numbers below refer to that explanation. */
+
+static void
+record_asm_reg_life (insn, regstack, operands, constraints,
+ n_inputs, n_outputs)
+ rtx insn;
+ stack regstack;
+ rtx *operands;
+ char **constraints;
+ int n_inputs, n_outputs;
+{
+ int i;
+ int n_operands = n_inputs + n_outputs;
+ int first_input = n_outputs;
+ int n_clobbers;
+ int malformed_asm = 0;
+ rtx body = PATTERN (insn);
+
+ int *operand_matches = (int *) alloca (n_operands * sizeof (int *));
+
+ enum reg_class *operand_class
+ = (enum reg_class *) alloca (n_operands * sizeof (enum reg_class *));
+
+ int reg_used_as_output[FIRST_PSEUDO_REGISTER];
+ int implicitly_dies[FIRST_PSEUDO_REGISTER];
+
+ rtx *clobber_reg;
+
+ /* Find out what the constraints require. If no constraint
+ alternative matches, this asm is malformed. */
+ i = constrain_asm_operands (n_operands, operands, constraints,
+ operand_matches, operand_class);
+ if (i < 0)
+ malformed_asm = 1;
+
+ /* Strip SUBREGs here to make the following code simpler. */
+ for (i = 0; i < n_operands; i++)
+ if (GET_CODE (operands[i]) == SUBREG
+ && GET_CODE (SUBREG_REG (operands[i])) == REG)
+ operands[i] = SUBREG_REG (operands[i]);
+
+ /* Set up CLOBBER_REG. */
+
+ n_clobbers = 0;
+
+ if (GET_CODE (body) == PARALLEL)
+ {
+ clobber_reg = (rtx *) alloca (XVECLEN (body, 0) * sizeof (rtx *));
+
+ for (i = 0; i < XVECLEN (body, 0); i++)
+ if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER)
+ {
+ rtx clobber = XVECEXP (body, 0, i);
+ rtx reg = XEXP (clobber, 0);
+
+ if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG)
+ reg = SUBREG_REG (reg);
+
+ if (STACK_REG_P (reg))
+ {
+ clobber_reg[n_clobbers] = reg;
+ n_clobbers++;
+ }
+ }
+ }
+
+ /* Enforce rule #4: Output operands must specifically indicate which
+ reg an output appears in after an asm. "=f" is not allowed: the
+ operand constraints must select a class with a single reg.
+
+ Also enforce rule #5: Output operands must start at the top of
+ the reg-stack: output operands may not "skip" a reg. */
+
+ bzero (reg_used_as_output, sizeof (reg_used_as_output));
+ for (i = 0; i < n_outputs; i++)
+ if (STACK_REG_P (operands[i]))
+ if (reg_class_size[(int) operand_class[i]] != 1)
+ {
+ error_for_asm
+ (insn, "Output constraint %d must specify a single register", i);
+ malformed_asm = 1;
+ }
+ else
+ reg_used_as_output[REGNO (operands[i])] = 1;
+
+
+ /* Search for first non-popped reg. */
+ for (i = FIRST_STACK_REG; i < LAST_STACK_REG + 1; i++)
+ if (! reg_used_as_output[i])
+ break;
+
+ /* If there are any other popped regs, that's an error. */
+ for (; i < LAST_STACK_REG + 1; i++)
+ if (reg_used_as_output[i])
+ break;
+
+ if (i != LAST_STACK_REG + 1)
+ {
+ error_for_asm (insn, "Output regs must be grouped at top of stack");
+ malformed_asm = 1;
+ }
+
+ /* Enforce rule #2: All implicitly popped input regs must be closer
+ to the top of the reg-stack than any input that is not implicitly
+ popped. */
+
+ bzero (implicitly_dies, sizeof (implicitly_dies));
+ for (i = first_input; i < first_input + n_inputs; i++)
+ if (STACK_REG_P (operands[i]))
+ {
+ /* An input reg is implicitly popped if it is tied to an
+ output, or if there is a CLOBBER for it. */
+ int j;
+
+ for (j = 0; j < n_clobbers; j++)
+ if (operands_match_p (clobber_reg[j], operands[i]))
+ break;
+
+ if (j < n_clobbers || operand_matches[i] >= 0)
+ implicitly_dies[REGNO (operands[i])] = 1;
+ }
+
+ /* Search for first non-popped reg. */
+ for (i = FIRST_STACK_REG; i < LAST_STACK_REG + 1; i++)
+ if (! implicitly_dies[i])
+ break;
+
+ /* If there are any other popped regs, that's an error. */
+ for (; i < LAST_STACK_REG + 1; i++)
+ if (implicitly_dies[i])
+ break;
+
+ if (i != LAST_STACK_REG + 1)
+ {
+ error_for_asm (insn,
+ "Implicitly popped regs must be grouped at top of stack");
+ malformed_asm = 1;
+ }
+
+ /* Enfore rule #3: If any input operand uses the "f" constraint, all
+ output constraints must use the "&" earlyclobber.
+
+ ??? Detect this more deterministically by having constraint_asm_operands
+ record any earlyclobber. */
+
+ for (i = first_input; i < first_input + n_inputs; i++)
+ if (operand_matches[i] == -1)
+ {
+ int j;
+
+ for (j = 0; j < n_outputs; j++)
+ if (operands_match_p (operands[j], operands[i]))
+ {
+ error_for_asm (insn,
+ "Output operand %d must use `&' constraint", j);
+ malformed_asm = 1;
+ }
+ }
+
+ if (malformed_asm)
+ {
+ /* Avoid further trouble with this insn. */
+ PATTERN (insn) = gen_rtx (USE, VOIDmode, const0_rtx);
+ PUT_MODE (insn, VOIDmode);
+ return;
+ }
+
+ /* Process all outputs */
+ for (i = 0; i < n_outputs; i++)
+ {
+ rtx op = operands[i];
+
+ if (! STACK_REG_P (op))
+ if (stack_regs_mentioned_p (op))
+ abort ();
+ else
+ continue;
+
+ /* Each destination is dead before this insn. If the
+ destination is not used after this insn, record this with
+ REG_UNUSED. */
+
+ if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (op)))
+ REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_UNUSED, op,
+ REG_NOTES (insn));
+
+ CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (op));
+ }
+
+ /* Process all inputs */
+ for (i = first_input; i < first_input + n_inputs; i++)
+ {
+ if (! STACK_REG_P (operands[i]))
+ if (stack_regs_mentioned_p (operands[i]))
+ abort ();
+ else
+ continue;
+
+ /* If an input is dead after the insn, record a death note.
+ But don't record a death note if there is already a death note,
+ or if the input is also an output. */
+
+ if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (operands[i]))
+ && operand_matches[i] == -1
+ && find_regno_note (insn, REG_DEAD, REGNO (operands[i])) == NULL_RTX)
+ REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_DEAD, operands[i],
+ REG_NOTES (insn));
+
+ SET_HARD_REG_BIT (regstack->reg_set, REGNO (operands[i]));
+ }
+}
+
+/* Scan PAT, which is part of INSN, and record registers appearing in
+ a SET_DEST in DEST, and other registers in SRC.
+
+ This function does not know about SET_DESTs that are both input and
+ output (such as ZERO_EXTRACT) - this cannot happen on a 387. */
+
+void
+record_reg_life_pat (pat, src, dest)
+ rtx pat;
+ HARD_REG_SET *src, *dest;
+{
+ register char *fmt;
+ register int i;
+
+ if (STACK_REG_P (pat))
+ {
+ if (src)
+ SET_HARD_REG_BIT (*src, REGNO (pat));
+
+ if (dest)
+ SET_HARD_REG_BIT (*dest, REGNO (pat));
+
+ return;
+ }
+
+ if (GET_CODE (pat) == SET)
+ {
+ record_reg_life_pat (XEXP (pat, 0), NULL_PTR, dest);
+ record_reg_life_pat (XEXP (pat, 1), src, NULL_PTR);
+ return;
+ }
+
+ /* We don't need to consider either of these cases. */
+ if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER)
+ return;
+
+ fmt = GET_RTX_FORMAT (GET_CODE (pat));
+ for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ register int j;
+
+ for (j = XVECLEN (pat, i) - 1; j >= 0; j--)
+ record_reg_life_pat (XVECEXP (pat, i, j), src, dest);
+ }
+ else if (fmt[i] == 'e')
+ record_reg_life_pat (XEXP (pat, i), src, dest);
+ }
+}
+
+/* Calculate the number of inputs and outputs in BODY, an
+ asm_operands. N_OPERANDS is the total number of operands, and
+ N_INPUTS and N_OUTPUTS are pointers to ints into which the results are
+ placed. */
+
+static void
+get_asm_operand_lengths (body, n_operands, n_inputs, n_outputs)
+ rtx body;
+ int n_operands;
+ int *n_inputs, *n_outputs;
+{
+ if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS)
+ *n_inputs = ASM_OPERANDS_INPUT_LENGTH (SET_SRC (body));
+
+ else if (GET_CODE (body) == ASM_OPERANDS)
+ *n_inputs = ASM_OPERANDS_INPUT_LENGTH (body);
+
+ else if (GET_CODE (body) == PARALLEL
+ && GET_CODE (XVECEXP (body, 0, 0)) == SET)
+ *n_inputs = ASM_OPERANDS_INPUT_LENGTH (SET_SRC (XVECEXP (body, 0, 0)));
+
+ else if (GET_CODE (body) == PARALLEL
+ && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS)
+ *n_inputs = ASM_OPERANDS_INPUT_LENGTH (XVECEXP (body, 0, 0));
+ else
+ abort ();
+
+ *n_outputs = n_operands - *n_inputs;
+}
+
+/* Scan INSN, which is in BLOCK, and record the life & death of stack
+ registers in REGSTACK. This function is called to process insns from
+ the last insn in a block to the first. The actual scanning is done in
+ record_reg_life_pat.
+
+ If a register is live after a CALL_INSN, but is not a value return
+ register for that CALL_INSN, then code is emitted to initialize that
+ register. The block_end[] data is kept accurate.
+
+ Existing death and unset notes for stack registers are deleted
+ before processing the insn. */
+
+static void
+record_reg_life (insn, block, regstack)
+ rtx insn;
+ int block;
+ stack regstack;
+{
+ rtx note, *note_link;
+ int n_operands;
+
+ if ((GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN)
+ || INSN_DELETED_P (insn))
+ return;
+
+ /* Strip death notes for stack regs from this insn */
+
+ note_link = &REG_NOTES(insn);
+ for (note = *note_link; note; note = XEXP (note, 1))
+ if (STACK_REG_P (XEXP (note, 0))
+ && (REG_NOTE_KIND (note) == REG_DEAD
+ || REG_NOTE_KIND (note) == REG_UNUSED))
+ *note_link = XEXP (note, 1);
+ else
+ note_link = &XEXP (note, 1);
+
+ /* Process all patterns in the insn. */
+
+ n_operands = asm_noperands (PATTERN (insn));
+ if (n_operands >= 0)
+ {
+ /* This insn is an `asm' with operands. Decode the operands,
+ decide how many are inputs, and record the life information. */
+
+ rtx operands[MAX_RECOG_OPERANDS];
+ rtx body = PATTERN (insn);
+ int n_inputs, n_outputs;
+ char **constraints = (char **) alloca (n_operands * sizeof (char *));
+
+ decode_asm_operands (body, operands, NULL_PTR, constraints, NULL_PTR);
+ get_asm_operand_lengths (body, n_operands, &n_inputs, &n_outputs);
+ record_asm_reg_life (insn, regstack, operands, constraints,
+ n_inputs, n_outputs);
+ return;
+ }
+
+ /* An insn referencing a stack reg has a mode of QImode. */
+ if (GET_MODE (insn) == QImode)
+ {
+ HARD_REG_SET src, dest;
+ int regno;
+
+ CLEAR_HARD_REG_SET (src);
+ CLEAR_HARD_REG_SET (dest);
+ record_reg_life_pat (PATTERN (insn), &src, &dest);
+
+ for (regno = FIRST_STACK_REG; regno <= LAST_STACK_REG; regno++)
+ if (! TEST_HARD_REG_BIT (regstack->reg_set, regno))
+ {
+ if (TEST_HARD_REG_BIT (src, regno)
+ && ! TEST_HARD_REG_BIT (dest, regno))
+ REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_DEAD,
+ FP_mode_reg[regno][(int) DFmode],
+ REG_NOTES (insn));
+ else if (TEST_HARD_REG_BIT (dest, regno))
+ REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_UNUSED,
+ FP_mode_reg[regno][(int) DFmode],
+ REG_NOTES (insn));
+ }
+
+ AND_COMPL_HARD_REG_SET (regstack->reg_set, dest);
+ IOR_HARD_REG_SET (regstack->reg_set, src);
+ }
+
+ /* There might be a reg that is live after a function call.
+ Initialize it to zero so that the program does not crash. See comment
+ towards the end of stack_reg_life_analysis(). */
+
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ int reg = FIRST_FLOAT_REG;
+
+ /* If a stack reg is mentioned in a CALL_INSN, it must be as the
+ return value. */
+
+ if (stack_regs_mentioned_p (PATTERN (insn)))
+ reg++;
+
+ for (; reg <= LAST_STACK_REG; reg++)
+ if (TEST_HARD_REG_BIT (regstack->reg_set, reg))
+ {
+ rtx init, pat;
+
+ /* The insn will use virtual register numbers, and so
+ convert_regs is expected to process these. But BLOCK_NUM
+ cannot be used on these insns, because they do not appear in
+ block_number[]. */
+
+ pat = gen_rtx (SET, VOIDmode, FP_mode_reg[reg][(int) DFmode],
+ CONST0_RTX (DFmode));
+ init = emit_insn_after (pat, insn);
+ PUT_MODE (init, QImode);
+
+ CLEAR_HARD_REG_BIT (regstack->reg_set, reg);
+
+ /* If the CALL_INSN was the end of a block, move the
+ block_end to point to the new insn. */
+
+ if (block_end[block] == insn)
+ block_end[block] = init;
+ }
+
+ /* Some regs do not survive a CALL */
+
+ AND_COMPL_HARD_REG_SET (regstack->reg_set, call_used_reg_set);
+ }
+}
+
+/* Find all basic blocks of the function, which starts with FIRST.
+ For each JUMP_INSN, build the chain of LABEL_REFS on each CODE_LABEL. */
+
+static void
+find_blocks (first)
+ rtx first;
+{
+ register rtx insn;
+ register int block;
+ register RTX_CODE prev_code = BARRIER;
+ register RTX_CODE code;
+ rtx label_value_list = 0;
+
+ /* Record where all the blocks start and end.
+ Record which basic blocks control can drop in to. */
+
+ block = -1;
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ /* Note that this loop must select the same block boundaries
+ as code in reg_to_stack, but that these are not the same
+ as those selected in flow.c. */
+
+ code = GET_CODE (insn);
+
+ if (code == CODE_LABEL
+ || (prev_code != INSN
+ && prev_code != CALL_INSN
+ && prev_code != CODE_LABEL
+ && GET_RTX_CLASS (code) == 'i'))
+ {
+ block_begin[++block] = insn;
+ block_end[block] = insn;
+ block_drops_in[block] = prev_code != BARRIER;
+ }
+ else if (GET_RTX_CLASS (code) == 'i')
+ block_end[block] = insn;
+
+ if (GET_RTX_CLASS (code) == 'i')
+ {
+ rtx note;
+
+ /* Make a list of all labels referred to other than by jumps. */
+ for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
+ if (REG_NOTE_KIND (note) == REG_LABEL)
+ label_value_list = gen_rtx (EXPR_LIST, VOIDmode, XEXP (note, 0),
+ label_value_list);
+ }
+
+ BLOCK_NUM (insn) = block;
+
+ if (code != NOTE)
+ prev_code = code;
+ }
+
+ if (block + 1 != blocks)
+ abort ();
+
+ /* generate all label references to the corresponding jump insn */
+ for (block = 0; block < blocks; block++)
+ {
+ insn = block_end[block];
+
+ if (GET_CODE (insn) == JUMP_INSN)
+ {
+ rtx pat = PATTERN (insn);
+ int computed_jump = 0;
+ rtx x;
+
+ if (GET_CODE (pat) == PARALLEL)
+ {
+ int len = XVECLEN (pat, 0);
+ int has_use_labelref = 0;
+ int i;
+
+ for (i = len - 1; i >= 0; i--)
+ if (GET_CODE (XVECEXP (pat, 0, i)) == USE
+ && GET_CODE (XEXP (XVECEXP (pat, 0, i), 0)) == LABEL_REF)
+ has_use_labelref = 1;
+
+ if (! has_use_labelref)
+ for (i = len - 1; i >= 0; i--)
+ if (GET_CODE (XVECEXP (pat, 0, i)) == SET
+ && SET_DEST (XVECEXP (pat, 0, i)) == pc_rtx
+ && uses_reg_or_mem (SET_SRC (XVECEXP (pat, 0, i))))
+ computed_jump = 1;
+ }
+ else if (GET_CODE (pat) == SET
+ && SET_DEST (pat) == pc_rtx
+ && uses_reg_or_mem (SET_SRC (pat)))
+ computed_jump = 1;
+
+ if (computed_jump)
+ {
+ for (x = label_value_list; x; x = XEXP (x, 1))
+ record_label_references (insn,
+ gen_rtx (LABEL_REF, VOIDmode,
+ XEXP (x, 0)));
+
+ for (x = forced_labels; x; x = XEXP (x, 1))
+ record_label_references (insn,
+ gen_rtx (LABEL_REF, VOIDmode,
+ XEXP (x, 0)));
+ }
+
+ record_label_references (insn, pat);
+ }
+ }
+}
+
+/* Return 1 if X contain a REG or MEM that is not in the constant pool. */
+
+static int
+uses_reg_or_mem (x)
+ rtx x;
+{
+ enum rtx_code code = GET_CODE (x);
+ int i, j;
+ char *fmt;
+
+ if (code == REG
+ || (code == MEM
+ && ! (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
+ && CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)))))
+ return 1;
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e'
+ && uses_reg_or_mem (XEXP (x, i)))
+ return 1;
+
+ if (fmt[i] == 'E')
+ for (j = 0; j < XVECLEN (x, i); j++)
+ if (uses_reg_or_mem (XVECEXP (x, i, j)))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* If current function returns its result in an fp stack register,
+ return the register number. Otherwise return -1. */
+
+static int
+stack_result_p (decl)
+ tree decl;
+{
+ rtx result = DECL_RTL (DECL_RESULT (decl));
+
+ if (result != 0
+ && !(GET_CODE (result) == REG
+ && REGNO (result) < FIRST_PSEUDO_REGISTER))
+ {
+#ifdef FUNCTION_OUTGOING_VALUE
+ result
+ = FUNCTION_OUTGOING_VALUE (TREE_TYPE (DECL_RESULT (decl)), decl);
+#else
+ result = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (decl)), decl);
+#endif
+ }
+
+ return STACK_REG_P (result) ? REGNO (result) : -1;
+}
+
+/* Determine the which registers are live at the start of each basic
+ block of the function whose first insn is FIRST.
+
+ First, if the function returns a real_type, mark the function
+ return type as live at each return point, as the RTL may not give any
+ hint that the register is live.
+
+ Then, start with the last block and work back to the first block.
+ Similarly, work backwards within each block, insn by insn, recording
+ which regs are die and which are used (and therefore live) in the
+ hard reg set of block_stack_in[].
+
+ After processing each basic block, if there is a label at the start
+ of the block, propagate the live registers to all jumps to this block.
+
+ As a special case, if there are regs live in this block, that are
+ not live in a block containing a jump to this label, and the block
+ containing the jump has already been processed, we must propagate this
+ block's entry register life back to the block containing the jump, and
+ restart life analysis from there.
+
+ In the worst case, this function may traverse the insns
+ REG_STACK_SIZE times. This is necessary, since a jump towards the end
+ of the insns may not know that a reg is live at a target that is early
+ in the insns. So we back up and start over with the new reg live.
+
+ If there are registers that are live at the start of the function,
+ insns are emitted to initialize these registers. Something similar is
+ done after CALL_INSNs in record_reg_life. */
+
+static void
+stack_reg_life_analysis (first)
+ rtx first;
+{
+ int reg, block;
+ struct stack_def regstack;
+
+ if (current_function_returns_real
+ && stack_result_p (current_function_decl) >= 0)
+ {
+ /* Find all RETURN insns and mark them. */
+
+ int value_regno = stack_result_p (current_function_decl);
+
+ for (block = blocks - 1; block >= 0; block--)
+ if (GET_CODE (block_end[block]) == JUMP_INSN
+ && GET_CODE (PATTERN (block_end[block])) == RETURN)
+ SET_HARD_REG_BIT (block_out_reg_set[block], value_regno);
+
+ /* Mark of the end of last block if we "fall off" the end of the
+ function into the epilogue. */
+
+ if (GET_CODE (block_end[blocks-1]) != JUMP_INSN
+ || GET_CODE (PATTERN (block_end[blocks-1])) == RETURN)
+ SET_HARD_REG_BIT (block_out_reg_set[blocks-1], value_regno);
+ }
+
+ /* now scan all blocks backward for stack register use */
+
+ block = blocks - 1;
+ while (block >= 0)
+ {
+ register rtx insn, prev;
+
+ /* current register status at last instruction */
+
+ COPY_HARD_REG_SET (regstack.reg_set, block_out_reg_set[block]);
+
+ prev = block_end[block];
+ do
+ {
+ insn = prev;
+ prev = PREV_INSN (insn);
+
+ /* If the insn is a CALL_INSN, we need to ensure that
+ everything dies. But otherwise don't process unless there
+ are some stack regs present. */
+
+ if (GET_MODE (insn) == QImode || GET_CODE (insn) == CALL_INSN)
+ record_reg_life (insn, block, &regstack);
+
+ } while (insn != block_begin[block]);
+
+ /* Set the state at the start of the block. Mark that no
+ register mapping information known yet. */
+
+ COPY_HARD_REG_SET (block_stack_in[block].reg_set, regstack.reg_set);
+ block_stack_in[block].top = -2;
+
+ /* If there is a label, propagate our register life to all jumps
+ to this label. */
+
+ if (GET_CODE (insn) == CODE_LABEL)
+ {
+ register rtx label;
+ int must_restart = 0;
+
+ for (label = LABEL_REFS (insn); label != insn;
+ label = LABEL_NEXTREF (label))
+ {
+ int jump_block = BLOCK_NUM (CONTAINING_INSN (label));
+
+ if (jump_block < block)
+ IOR_HARD_REG_SET (block_out_reg_set[jump_block],
+ block_stack_in[block].reg_set);
+ else
+ {
+ /* The block containing the jump has already been
+ processed. If there are registers that were not known
+ to be live then, but are live now, we must back up
+ and restart life analysis from that point with the new
+ life information. */
+
+ GO_IF_HARD_REG_SUBSET (block_stack_in[block].reg_set,
+ block_out_reg_set[jump_block],
+ win);
+
+ IOR_HARD_REG_SET (block_out_reg_set[jump_block],
+ block_stack_in[block].reg_set);
+
+ block = jump_block;
+ must_restart = 1;
+
+ win:
+ ;
+ }
+ }
+ if (must_restart)
+ continue;
+ }
+
+ if (block_drops_in[block])
+ IOR_HARD_REG_SET (block_out_reg_set[block-1],
+ block_stack_in[block].reg_set);
+
+ block -= 1;
+ }
+
+ {
+ /* If any reg is live at the start of the first block of a
+ function, then we must guarantee that the reg holds some value by
+ generating our own "load" of that register. Otherwise a 387 would
+ fault trying to access an empty register. */
+
+ HARD_REG_SET empty_regs;
+ CLEAR_HARD_REG_SET (empty_regs);
+ GO_IF_HARD_REG_SUBSET (block_stack_in[0].reg_set, empty_regs,
+ no_live_regs);
+ }
+
+ /* Load zero into each live register. The fact that a register
+ appears live at the function start does not necessarily imply an error
+ in the user program: it merely means that we could not determine that
+ there wasn't such an error, just as -Wunused sometimes gives
+ "incorrect" warnings. In those cases, these initializations will do
+ no harm.
+
+ Note that we are inserting virtual register references here:
+ these insns must be processed by convert_regs later. Also, these
+ insns will not be in block_number, so BLOCK_NUM() will fail for them. */
+
+ for (reg = LAST_STACK_REG; reg >= FIRST_STACK_REG; reg--)
+ if (TEST_HARD_REG_BIT (block_stack_in[0].reg_set, reg))
+ {
+ rtx init_rtx;
+
+ init_rtx = gen_rtx (SET, VOIDmode, FP_mode_reg[reg][(int) DFmode],
+ CONST0_RTX (DFmode));
+ block_begin[0] = emit_insn_after (init_rtx, first);
+ PUT_MODE (block_begin[0], QImode);
+
+ CLEAR_HARD_REG_BIT (block_stack_in[0].reg_set, reg);
+ }
+
+ no_live_regs:
+ ;
+}
+
+/*****************************************************************************
+ This section deals with stack register substitution, and forms the second
+ pass over the RTL.
+ *****************************************************************************/
+
+/* Replace REG, which is a pointer to a stack reg RTX, with an RTX for
+ the desired hard REGNO. */
+
+static void
+replace_reg (reg, regno)
+ rtx *reg;
+ int regno;
+{
+ if (regno < FIRST_STACK_REG || regno > LAST_STACK_REG
+ || ! STACK_REG_P (*reg))
+ abort ();
+
+ if (GET_MODE_CLASS (GET_MODE (*reg)) != MODE_FLOAT)
+ abort ();
+
+ *reg = FP_mode_reg[regno][(int) GET_MODE (*reg)];
+}
+
+/* Remove a note of type NOTE, which must be found, for register
+ number REGNO from INSN. Remove only one such note. */
+
+static void
+remove_regno_note (insn, note, regno)
+ rtx insn;
+ enum reg_note note;
+ int regno;
+{
+ register rtx *note_link, this;
+
+ note_link = &REG_NOTES(insn);
+ for (this = *note_link; this; this = XEXP (this, 1))
+ if (REG_NOTE_KIND (this) == note
+ && REG_P (XEXP (this, 0)) && REGNO (XEXP (this, 0)) == regno)
+ {
+ *note_link = XEXP (this, 1);
+ return;
+ }
+ else
+ note_link = &XEXP (this, 1);
+
+ abort ();
+}
+
+/* Find the hard register number of virtual register REG in REGSTACK.
+ The hard register number is relative to the top of the stack. -1 is
+ returned if the register is not found. */
+
+static int
+get_hard_regnum (regstack, reg)
+ stack regstack;
+ rtx reg;
+{
+ int i;
+
+ if (! STACK_REG_P (reg))
+ abort ();
+
+ for (i = regstack->top; i >= 0; i--)
+ if (regstack->reg[i] == REGNO (reg))
+ break;
+
+ return i >= 0 ? (FIRST_STACK_REG + regstack->top - i) : -1;
+}
+
+/* Delete INSN from the RTL. Mark the insn, but don't remove it from
+ the chain of insns. Doing so could confuse block_begin and block_end
+ if this were the only insn in the block. */
+
+static void
+delete_insn_for_stacker (insn)
+ rtx insn;
+{
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ INSN_DELETED_P (insn) = 1;
+}
+
+/* Emit an insn to pop virtual register REG before or after INSN.
+ REGSTACK is the stack state after INSN and is updated to reflect this
+ pop. WHEN is either emit_insn_before or emit_insn_after. A pop insn
+ is represented as a SET whose destination is the register to be popped
+ and source is the top of stack. A death note for the top of stack
+ cases the movdf pattern to pop. */
+
+static rtx
+emit_pop_insn (insn, regstack, reg, when)
+ rtx insn;
+ stack regstack;
+ rtx reg;
+ rtx (*when)();
+{
+ rtx pop_insn, pop_rtx;
+ int hard_regno;
+
+ hard_regno = get_hard_regnum (regstack, reg);
+
+ if (hard_regno < FIRST_STACK_REG)
+ abort ();
+
+ pop_rtx = gen_rtx (SET, VOIDmode, FP_mode_reg[hard_regno][(int) DFmode],
+ FP_mode_reg[FIRST_STACK_REG][(int) DFmode]);
+
+ pop_insn = (*when) (pop_rtx, insn);
+ /* ??? This used to be VOIDmode, but that seems wrong. */
+ PUT_MODE (pop_insn, QImode);
+
+ REG_NOTES (pop_insn) = gen_rtx (EXPR_LIST, REG_DEAD,
+ FP_mode_reg[FIRST_STACK_REG][(int) DFmode],
+ REG_NOTES (pop_insn));
+
+ regstack->reg[regstack->top - (hard_regno - FIRST_STACK_REG)]
+ = regstack->reg[regstack->top];
+ regstack->top -= 1;
+ CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (reg));
+
+ return pop_insn;
+}
+
+/* Emit an insn before or after INSN to swap virtual register REG with the
+ top of stack. WHEN should be `emit_insn_before' or `emit_insn_before'
+ REGSTACK is the stack state before the swap, and is updated to reflect
+ the swap. A swap insn is represented as a PARALLEL of two patterns:
+ each pattern moves one reg to the other.
+
+ If REG is already at the top of the stack, no insn is emitted. */
+
+static void
+emit_swap_insn (insn, regstack, reg)
+ rtx insn;
+ stack regstack;
+ rtx reg;
+{
+ int hard_regno;
+ rtx gen_swapdf();
+ rtx swap_rtx, swap_insn;
+ int tmp, other_reg; /* swap regno temps */
+ rtx i1; /* the stack-reg insn prior to INSN */
+ rtx i1set = NULL_RTX; /* the SET rtx within I1 */
+
+ hard_regno = get_hard_regnum (regstack, reg);
+
+ if (hard_regno < FIRST_STACK_REG)
+ abort ();
+ if (hard_regno == FIRST_STACK_REG)
+ return;
+
+ other_reg = regstack->top - (hard_regno - FIRST_STACK_REG);
+
+ tmp = regstack->reg[other_reg];
+ regstack->reg[other_reg] = regstack->reg[regstack->top];
+ regstack->reg[regstack->top] = tmp;
+
+ /* Find the previous insn involving stack regs, but don't go past
+ any labels, calls or jumps. */
+ i1 = prev_nonnote_insn (insn);
+ while (i1 && GET_CODE (i1) == INSN && GET_MODE (i1) != QImode)
+ i1 = prev_nonnote_insn (i1);
+
+ if (i1)
+ i1set = single_set (i1);
+
+ if (i1set)
+ {
+ rtx i2; /* the stack-reg insn prior to I1 */
+ rtx i1src = *get_true_reg (&SET_SRC (i1set));
+ rtx i1dest = *get_true_reg (&SET_DEST (i1set));
+
+ /* If the previous register stack push was from the reg we are to
+ swap with, omit the swap. */
+
+ if (GET_CODE (i1dest) == REG && REGNO (i1dest) == FIRST_STACK_REG
+ && GET_CODE (i1src) == REG && REGNO (i1src) == hard_regno - 1
+ && find_regno_note (i1, REG_DEAD, FIRST_STACK_REG) == NULL_RTX)
+ return;
+
+ /* If the previous insn wrote to the reg we are to swap with,
+ omit the swap. */
+
+ if (GET_CODE (i1dest) == REG && REGNO (i1dest) == hard_regno
+ && GET_CODE (i1src) == REG && REGNO (i1src) == FIRST_STACK_REG
+ && find_regno_note (i1, REG_DEAD, FIRST_STACK_REG) == NULL_RTX)
+ return;
+ }
+
+ if (GET_RTX_CLASS (GET_CODE (i1)) == 'i' && sets_cc0_p (PATTERN (i1)))
+ {
+ i1 = next_nonnote_insn (i1);
+ if (i1 == insn)
+ abort ();
+ }
+
+ swap_rtx = gen_swapdf (FP_mode_reg[hard_regno][(int) DFmode],
+ FP_mode_reg[FIRST_STACK_REG][(int) DFmode]);
+ swap_insn = emit_insn_after (swap_rtx, i1);
+ /* ??? This used to be VOIDmode, but that seems wrong. */
+ PUT_MODE (swap_insn, QImode);
+}
+
+/* Handle a move to or from a stack register in PAT, which is in INSN.
+ REGSTACK is the current stack. */
+
+static void
+move_for_stack_reg (insn, regstack, pat)
+ rtx insn;
+ stack regstack;
+ rtx pat;
+{
+ rtx *src = get_true_reg (&SET_SRC (pat));
+ rtx *dest = get_true_reg (&SET_DEST (pat));
+ rtx note;
+
+ if (STACK_REG_P (*src) && STACK_REG_P (*dest))
+ {
+ /* Write from one stack reg to another. If SRC dies here, then
+ just change the register mapping and delete the insn. */
+
+ note = find_regno_note (insn, REG_DEAD, REGNO (*src));
+ if (note)
+ {
+ int i;
+
+ /* If this is a no-op move, there must not be a REG_DEAD note. */
+ if (REGNO (*src) == REGNO (*dest))
+ abort ();
+
+ for (i = regstack->top; i >= 0; i--)
+ if (regstack->reg[i] == REGNO (*src))
+ break;
+
+ /* The source must be live, and the dest must be dead. */
+ if (i < 0 || get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG)
+ abort ();
+
+ /* It is possible that the dest is unused after this insn.
+ If so, just pop the src. */
+
+ if (find_regno_note (insn, REG_UNUSED, REGNO (*dest)))
+ {
+ emit_pop_insn (insn, regstack, *src, emit_insn_after);
+
+ delete_insn_for_stacker (insn);
+ return;
+ }
+
+ regstack->reg[i] = REGNO (*dest);
+
+ SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
+ CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src));
+
+ delete_insn_for_stacker (insn);
+
+ return;
+ }
+
+ /* The source reg does not die. */
+
+ /* If this appears to be a no-op move, delete it, or else it
+ will confuse the machine description output patterns. But if
+ it is REG_UNUSED, we must pop the reg now, as per-insn processing
+ for REG_UNUSED will not work for deleted insns. */
+
+ if (REGNO (*src) == REGNO (*dest))
+ {
+ if (find_regno_note (insn, REG_UNUSED, REGNO (*dest)))
+ emit_pop_insn (insn, regstack, *dest, emit_insn_after);
+
+ delete_insn_for_stacker (insn);
+ return;
+ }
+
+ /* The destination ought to be dead */
+ if (get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG)
+ abort ();
+
+ replace_reg (src, get_hard_regnum (regstack, *src));
+
+ regstack->reg[++regstack->top] = REGNO (*dest);
+ SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
+ replace_reg (dest, FIRST_STACK_REG);
+ }
+ else if (STACK_REG_P (*src))
+ {
+ /* Save from a stack reg to MEM, or possibly integer reg. Since
+ only top of stack may be saved, emit an exchange first if
+ needs be. */
+
+ emit_swap_insn (insn, regstack, *src);
+
+ note = find_regno_note (insn, REG_DEAD, REGNO (*src));
+ if (note)
+ {
+ replace_reg (&XEXP (note, 0), FIRST_STACK_REG);
+ regstack->top--;
+ CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src));
+ }
+ else if (GET_MODE (*src) == XFmode && regstack->top != REG_STACK_SIZE)
+ {
+ /* A 387 cannot write an XFmode value to a MEM without
+ clobbering the source reg. The output code can handle
+ this by reading back the value from the MEM.
+ But it is more efficient to use a temp register if one is
+ available. Push the source value here if the register
+ stack is not full, and then write the value to memory via
+ a pop. */
+ rtx push_rtx, push_insn;
+ rtx top_stack_reg = FP_mode_reg[FIRST_STACK_REG][(int) XFmode];
+
+ push_rtx = gen_movxf (top_stack_reg, top_stack_reg);
+ push_insn = emit_insn_before (push_rtx, insn);
+ PUT_MODE (push_insn, QImode);
+ REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_DEAD, top_stack_reg,
+ REG_NOTES (insn));
+ }
+
+ replace_reg (src, FIRST_STACK_REG);
+ }
+ else if (STACK_REG_P (*dest))
+ {
+ /* Load from MEM, or possibly integer REG or constant, into the
+ stack regs. The actual target is always the top of the
+ stack. The stack mapping is changed to reflect that DEST is
+ now at top of stack. */
+
+ /* The destination ought to be dead */
+ if (get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG)
+ abort ();
+
+ if (regstack->top >= REG_STACK_SIZE)
+ abort ();
+
+ regstack->reg[++regstack->top] = REGNO (*dest);
+ SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
+ replace_reg (dest, FIRST_STACK_REG);
+ }
+ else
+ abort ();
+}
+
+void
+swap_rtx_condition (pat)
+ rtx pat;
+{
+ register char *fmt;
+ register int i;
+
+ if (GET_RTX_CLASS (GET_CODE (pat)) == '<')
+ {
+ PUT_CODE (pat, swap_condition (GET_CODE (pat)));
+ return;
+ }
+
+ fmt = GET_RTX_FORMAT (GET_CODE (pat));
+ for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ register int j;
+
+ for (j = XVECLEN (pat, i) - 1; j >= 0; j--)
+ swap_rtx_condition (XVECEXP (pat, i, j));
+ }
+ else if (fmt[i] == 'e')
+ swap_rtx_condition (XEXP (pat, i));
+ }
+}
+
+/* Handle a comparison. Special care needs to be taken to avoid
+ causing comparisons that a 387 cannot do correctly, such as EQ.
+
+ Also, a pop insn may need to be emitted. The 387 does have an
+ `fcompp' insn that can pop two regs, but it is sometimes too expensive
+ to do this - a `fcomp' followed by a `fstpl %st(0)' may be easier to
+ set up. */
+
+static void
+compare_for_stack_reg (insn, regstack, pat)
+ rtx insn;
+ stack regstack;
+ rtx pat;
+{
+ rtx *src1, *src2;
+ rtx src1_note, src2_note;
+
+ src1 = get_true_reg (&XEXP (SET_SRC (pat), 0));
+ src2 = get_true_reg (&XEXP (SET_SRC (pat), 1));
+
+ /* ??? If fxch turns out to be cheaper than fstp, give priority to
+ registers that die in this insn - move those to stack top first. */
+ if (! STACK_REG_P (*src1)
+ || (STACK_REG_P (*src2)
+ && get_hard_regnum (regstack, *src2) == FIRST_STACK_REG))
+ {
+ rtx temp, next;
+
+ temp = XEXP (SET_SRC (pat), 0);
+ XEXP (SET_SRC (pat), 0) = XEXP (SET_SRC (pat), 1);
+ XEXP (SET_SRC (pat), 1) = temp;
+
+ src1 = get_true_reg (&XEXP (SET_SRC (pat), 0));
+ src2 = get_true_reg (&XEXP (SET_SRC (pat), 1));
+
+ next = next_cc0_user (insn);
+ if (next == NULL_RTX)
+ abort ();
+
+ swap_rtx_condition (PATTERN (next));
+ INSN_CODE (next) = -1;
+ INSN_CODE (insn) = -1;
+ }
+
+ /* We will fix any death note later. */
+
+ src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
+
+ if (STACK_REG_P (*src2))
+ src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2));
+ else
+ src2_note = NULL_RTX;
+
+ emit_swap_insn (insn, regstack, *src1);
+
+ replace_reg (src1, FIRST_STACK_REG);
+
+ if (STACK_REG_P (*src2))
+ replace_reg (src2, get_hard_regnum (regstack, *src2));
+
+ if (src1_note)
+ {
+ CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (XEXP (src1_note, 0)));
+ replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
+ regstack->top--;
+ }
+
+ /* If the second operand dies, handle that. But if the operands are
+ the same stack register, don't bother, because only one death is
+ needed, and it was just handled. */
+
+ if (src2_note
+ && ! (STACK_REG_P (*src1) && STACK_REG_P (*src2)
+ && REGNO (*src1) == REGNO (*src2)))
+ {
+ /* As a special case, two regs may die in this insn if src2 is
+ next to top of stack and the top of stack also dies. Since
+ we have already popped src1, "next to top of stack" is really
+ at top (FIRST_STACK_REG) now. */
+
+ if (get_hard_regnum (regstack, XEXP (src2_note, 0)) == FIRST_STACK_REG
+ && src1_note)
+ {
+ CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (XEXP (src2_note, 0)));
+ replace_reg (&XEXP (src2_note, 0), FIRST_STACK_REG + 1);
+ regstack->top--;
+ }
+ else
+ {
+ /* The 386 can only represent death of the first operand in
+ the case handled above. In all other cases, emit a separate
+ pop and remove the death note from here. */
+
+ link_cc0_insns (insn);
+
+ remove_regno_note (insn, REG_DEAD, REGNO (XEXP (src2_note, 0)));
+
+ emit_pop_insn (insn, regstack, XEXP (src2_note, 0),
+ emit_insn_after);
+ }
+ }
+}
+
+/* Substitute new registers in PAT, which is part of INSN. REGSTACK
+ is the current register layout. */
+
+static void
+subst_stack_regs_pat (insn, regstack, pat)
+ rtx insn;
+ stack regstack;
+ rtx pat;
+{
+ rtx *dest, *src;
+ rtx *src1 = (rtx *) NULL_PTR, *src2;
+ rtx src1_note, src2_note;
+
+ if (GET_CODE (pat) != SET)
+ return;
+
+ dest = get_true_reg (&SET_DEST (pat));
+ src = get_true_reg (&SET_SRC (pat));
+
+ /* See if this is a `movM' pattern, and handle elsewhere if so. */
+
+ if (*dest != cc0_rtx
+ && (STACK_REG_P (*src)
+ || (STACK_REG_P (*dest)
+ && (GET_CODE (*src) == REG || GET_CODE (*src) == MEM
+ || GET_CODE (*src) == CONST_DOUBLE))))
+ move_for_stack_reg (insn, regstack, pat);
+ else
+ switch (GET_CODE (SET_SRC (pat)))
+ {
+ case COMPARE:
+ compare_for_stack_reg (insn, regstack, pat);
+ break;
+
+ case CALL:
+ regstack->reg[++regstack->top] = REGNO (*dest);
+ SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
+ replace_reg (dest, FIRST_STACK_REG);
+ break;
+
+ case REG:
+ /* This is a `tstM2' case. */
+ if (*dest != cc0_rtx)
+ abort ();
+
+ src1 = src;
+
+ /* Fall through. */
+
+ case FLOAT_TRUNCATE:
+ case SQRT:
+ case ABS:
+ case NEG:
+ /* These insns only operate on the top of the stack. DEST might
+ be cc0_rtx if we're processing a tstM pattern. Also, it's
+ possible that the tstM case results in a REG_DEAD note on the
+ source. */
+
+ if (src1 == 0)
+ src1 = get_true_reg (&XEXP (SET_SRC (pat), 0));
+
+ emit_swap_insn (insn, regstack, *src1);
+
+ src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
+
+ if (STACK_REG_P (*dest))
+ replace_reg (dest, FIRST_STACK_REG);
+
+ if (src1_note)
+ {
+ replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
+ regstack->top--;
+ CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src1));
+ }
+
+ replace_reg (src1, FIRST_STACK_REG);
+
+ break;
+
+ case MINUS:
+ case DIV:
+ /* On i386, reversed forms of subM3 and divM3 exist for
+ MODE_FLOAT, so the same code that works for addM3 and mulM3
+ can be used. */
+ case MULT:
+ case PLUS:
+ /* These insns can accept the top of stack as a destination
+ from a stack reg or mem, or can use the top of stack as a
+ source and some other stack register (possibly top of stack)
+ as a destination. */
+
+ src1 = get_true_reg (&XEXP (SET_SRC (pat), 0));
+ src2 = get_true_reg (&XEXP (SET_SRC (pat), 1));
+
+ /* We will fix any death note later. */
+
+ if (STACK_REG_P (*src1))
+ src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
+ else
+ src1_note = NULL_RTX;
+ if (STACK_REG_P (*src2))
+ src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2));
+ else
+ src2_note = NULL_RTX;
+
+ /* If either operand is not a stack register, then the dest
+ must be top of stack. */
+
+ if (! STACK_REG_P (*src1) || ! STACK_REG_P (*src2))
+ emit_swap_insn (insn, regstack, *dest);
+ else
+ {
+ /* Both operands are REG. If neither operand is already
+ at the top of stack, choose to make the one that is the dest
+ the new top of stack. */
+
+ int src1_hard_regnum, src2_hard_regnum;
+
+ src1_hard_regnum = get_hard_regnum (regstack, *src1);
+ src2_hard_regnum = get_hard_regnum (regstack, *src2);
+ if (src1_hard_regnum == -1 || src2_hard_regnum == -1)
+ abort ();
+
+ if (src1_hard_regnum != FIRST_STACK_REG
+ && src2_hard_regnum != FIRST_STACK_REG)
+ emit_swap_insn (insn, regstack, *dest);
+ }
+
+ if (STACK_REG_P (*src1))
+ replace_reg (src1, get_hard_regnum (regstack, *src1));
+ if (STACK_REG_P (*src2))
+ replace_reg (src2, get_hard_regnum (regstack, *src2));
+
+ if (src1_note)
+ {
+ /* If the register that dies is at the top of stack, then
+ the destination is somewhere else - merely substitute it.
+ But if the reg that dies is not at top of stack, then
+ move the top of stack to the dead reg, as though we had
+ done the insn and then a store-with-pop. */
+
+ if (REGNO (XEXP (src1_note, 0)) == regstack->reg[regstack->top])
+ {
+ SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
+ replace_reg (dest, get_hard_regnum (regstack, *dest));
+ }
+ else
+ {
+ int regno = get_hard_regnum (regstack, XEXP (src1_note, 0));
+
+ SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
+ replace_reg (dest, regno);
+
+ regstack->reg[regstack->top - (regno - FIRST_STACK_REG)]
+ = regstack->reg[regstack->top];
+ }
+
+ CLEAR_HARD_REG_BIT (regstack->reg_set,
+ REGNO (XEXP (src1_note, 0)));
+ replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
+ regstack->top--;
+ }
+ else if (src2_note)
+ {
+ if (REGNO (XEXP (src2_note, 0)) == regstack->reg[regstack->top])
+ {
+ SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
+ replace_reg (dest, get_hard_regnum (regstack, *dest));
+ }
+ else
+ {
+ int regno = get_hard_regnum (regstack, XEXP (src2_note, 0));
+
+ SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
+ replace_reg (dest, regno);
+
+ regstack->reg[regstack->top - (regno - FIRST_STACK_REG)]
+ = regstack->reg[regstack->top];
+ }
+
+ CLEAR_HARD_REG_BIT (regstack->reg_set,
+ REGNO (XEXP (src2_note, 0)));
+ replace_reg (&XEXP (src2_note, 0), FIRST_STACK_REG);
+ regstack->top--;
+ }
+ else
+ {
+ SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
+ replace_reg (dest, get_hard_regnum (regstack, *dest));
+ }
+
+ break;
+
+ case UNSPEC:
+ switch (XINT (SET_SRC (pat), 1))
+ {
+ case 1: /* sin */
+ case 2: /* cos */
+ /* These insns only operate on the top of the stack. */
+
+ src1 = get_true_reg (&XVECEXP (SET_SRC (pat), 0, 0));
+
+ emit_swap_insn (insn, regstack, *src1);
+
+ src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
+
+ if (STACK_REG_P (*dest))
+ replace_reg (dest, FIRST_STACK_REG);
+
+ if (src1_note)
+ {
+ replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
+ regstack->top--;
+ CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src1));
+ }
+
+ replace_reg (src1, FIRST_STACK_REG);
+
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+/* Substitute hard regnums for any stack regs in INSN, which has
+ N_INPUTS inputs and N_OUTPUTS outputs. REGSTACK is the stack info
+ before the insn, and is updated with changes made here. CONSTRAINTS is
+ an array of the constraint strings used in the asm statement.
+
+ OPERANDS is an array of the operands, and OPERANDS_LOC is a
+ parallel array of where the operands were found. The output operands
+ all precede the input operands.
+
+ There are several requirements and assumptions about the use of
+ stack-like regs in asm statements. These rules are enforced by
+ record_asm_stack_regs; see comments there for details. Any
+ asm_operands left in the RTL at this point may be assume to meet the
+ requirements, since record_asm_stack_regs removes any problem asm. */
+
+static void
+subst_asm_stack_regs (insn, regstack, operands, operands_loc, constraints,
+ n_inputs, n_outputs)
+ rtx insn;
+ stack regstack;
+ rtx *operands, **operands_loc;
+ char **constraints;
+ int n_inputs, n_outputs;
+{
+ int n_operands = n_inputs + n_outputs;
+ int first_input = n_outputs;
+ rtx body = PATTERN (insn);
+
+ int *operand_matches = (int *) alloca (n_operands * sizeof (int *));
+ enum reg_class *operand_class
+ = (enum reg_class *) alloca (n_operands * sizeof (enum reg_class *));
+
+ rtx *note_reg; /* Array of note contents */
+ rtx **note_loc; /* Address of REG field of each note */
+ enum reg_note *note_kind; /* The type of each note */
+
+ rtx *clobber_reg;
+ rtx **clobber_loc;
+
+ struct stack_def temp_stack;
+ int n_notes;
+ int n_clobbers;
+ rtx note;
+ int i;
+
+ /* Find out what the constraints required. If no constraint
+ alternative matches, that is a compiler bug: we should have caught
+ such an insn during the life analysis pass (and reload should have
+ caught it regardless). */
+
+ i = constrain_asm_operands (n_operands, operands, constraints,
+ operand_matches, operand_class);
+ if (i < 0)
+ abort ();
+
+ /* Strip SUBREGs here to make the following code simpler. */
+ for (i = 0; i < n_operands; i++)
+ if (GET_CODE (operands[i]) == SUBREG
+ && GET_CODE (SUBREG_REG (operands[i])) == REG)
+ {
+ operands_loc[i] = & SUBREG_REG (operands[i]);
+ operands[i] = SUBREG_REG (operands[i]);
+ }
+
+ /* Set up NOTE_REG, NOTE_LOC and NOTE_KIND. */
+
+ for (i = 0, note = REG_NOTES (insn); note; note = XEXP (note, 1))
+ i++;
+
+ note_reg = (rtx *) alloca (i * sizeof (rtx));
+ note_loc = (rtx **) alloca (i * sizeof (rtx *));
+ note_kind = (enum reg_note *) alloca (i * sizeof (enum reg_note));
+
+ n_notes = 0;
+ for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
+ {
+ rtx reg = XEXP (note, 0);
+ rtx *loc = & XEXP (note, 0);
+
+ if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG)
+ {
+ loc = & SUBREG_REG (reg);
+ reg = SUBREG_REG (reg);
+ }
+
+ if (STACK_REG_P (reg)
+ && (REG_NOTE_KIND (note) == REG_DEAD
+ || REG_NOTE_KIND (note) == REG_UNUSED))
+ {
+ note_reg[n_notes] = reg;
+ note_loc[n_notes] = loc;
+ note_kind[n_notes] = REG_NOTE_KIND (note);
+ n_notes++;
+ }
+ }
+
+ /* Set up CLOBBER_REG and CLOBBER_LOC. */
+
+ n_clobbers = 0;
+
+ if (GET_CODE (body) == PARALLEL)
+ {
+ clobber_reg = (rtx *) alloca (XVECLEN (body, 0) * sizeof (rtx *));
+ clobber_loc = (rtx **) alloca (XVECLEN (body, 0) * sizeof (rtx **));
+
+ for (i = 0; i < XVECLEN (body, 0); i++)
+ if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER)
+ {
+ rtx clobber = XVECEXP (body, 0, i);
+ rtx reg = XEXP (clobber, 0);
+ rtx *loc = & XEXP (clobber, 0);
+
+ if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG)
+ {
+ loc = & SUBREG_REG (reg);
+ reg = SUBREG_REG (reg);
+ }
+
+ if (STACK_REG_P (reg))
+ {
+ clobber_reg[n_clobbers] = reg;
+ clobber_loc[n_clobbers] = loc;
+ n_clobbers++;
+ }
+ }
+ }
+
+ bcopy (regstack, &temp_stack, sizeof (temp_stack));
+
+ /* Put the input regs into the desired place in TEMP_STACK. */
+
+ for (i = first_input; i < first_input + n_inputs; i++)
+ if (STACK_REG_P (operands[i])
+ && reg_class_subset_p (operand_class[i], FLOAT_REGS)
+ && operand_class[i] != FLOAT_REGS)
+ {
+ /* If an operand needs to be in a particular reg in
+ FLOAT_REGS, the constraint was either 't' or 'u'. Since
+ these constraints are for single register classes, and reload
+ guaranteed that operand[i] is already in that class, we can
+ just use REGNO (operands[i]) to know which actual reg this
+ operand needs to be in. */
+
+ int regno = get_hard_regnum (&temp_stack, operands[i]);
+
+ if (regno < 0)
+ abort ();
+
+ if (regno != REGNO (operands[i]))
+ {
+ /* operands[i] is not in the right place. Find it
+ and swap it with whatever is already in I's place.
+ K is where operands[i] is now. J is where it should
+ be. */
+ int j, k, temp;
+
+ k = temp_stack.top - (regno - FIRST_STACK_REG);
+ j = (temp_stack.top
+ - (REGNO (operands[i]) - FIRST_STACK_REG));
+
+ temp = temp_stack.reg[k];
+ temp_stack.reg[k] = temp_stack.reg[j];
+ temp_stack.reg[j] = temp;
+ }
+ }
+
+ /* emit insns before INSN to make sure the reg-stack is in the right
+ order. */
+
+ change_stack (insn, regstack, &temp_stack, emit_insn_before);
+
+ /* Make the needed input register substitutions. Do death notes and
+ clobbers too, because these are for inputs, not outputs. */
+
+ for (i = first_input; i < first_input + n_inputs; i++)
+ if (STACK_REG_P (operands[i]))
+ {
+ int regnum = get_hard_regnum (regstack, operands[i]);
+
+ if (regnum < 0)
+ abort ();
+
+ replace_reg (operands_loc[i], regnum);
+ }
+
+ for (i = 0; i < n_notes; i++)
+ if (note_kind[i] == REG_DEAD)
+ {
+ int regnum = get_hard_regnum (regstack, note_reg[i]);
+
+ if (regnum < 0)
+ abort ();
+
+ replace_reg (note_loc[i], regnum);
+ }
+
+ for (i = 0; i < n_clobbers; i++)
+ {
+ /* It's OK for a CLOBBER to reference a reg that is not live.
+ Don't try to replace it in that case. */
+ int regnum = get_hard_regnum (regstack, clobber_reg[i]);
+
+ if (regnum >= 0)
+ {
+ /* Sigh - clobbers always have QImode. But replace_reg knows
+ that these regs can't be MODE_INT and will abort. Just put
+ the right reg there without calling replace_reg. */
+
+ *clobber_loc[i] = FP_mode_reg[regnum][(int) DFmode];
+ }
+ }
+
+ /* Now remove from REGSTACK any inputs that the asm implicitly popped. */
+
+ for (i = first_input; i < first_input + n_inputs; i++)
+ if (STACK_REG_P (operands[i]))
+ {
+ /* An input reg is implicitly popped if it is tied to an
+ output, or if there is a CLOBBER for it. */
+ int j;
+
+ for (j = 0; j < n_clobbers; j++)
+ if (operands_match_p (clobber_reg[j], operands[i]))
+ break;
+
+ if (j < n_clobbers || operand_matches[i] >= 0)
+ {
+ /* operands[i] might not be at the top of stack. But that's OK,
+ because all we need to do is pop the right number of regs
+ off of the top of the reg-stack. record_asm_stack_regs
+ guaranteed that all implicitly popped regs were grouped
+ at the top of the reg-stack. */
+
+ CLEAR_HARD_REG_BIT (regstack->reg_set,
+ regstack->reg[regstack->top]);
+ regstack->top--;
+ }
+ }
+
+ /* Now add to REGSTACK any outputs that the asm implicitly pushed.
+ Note that there isn't any need to substitute register numbers.
+ ??? Explain why this is true. */
+
+ for (i = LAST_STACK_REG; i >= FIRST_STACK_REG; i--)
+ {
+ /* See if there is an output for this hard reg. */
+ int j;
+
+ for (j = 0; j < n_outputs; j++)
+ if (STACK_REG_P (operands[j]) && REGNO (operands[j]) == i)
+ {
+ regstack->reg[++regstack->top] = i;
+ SET_HARD_REG_BIT (regstack->reg_set, i);
+ break;
+ }
+ }
+
+ /* Now emit a pop insn for any REG_UNUSED output, or any REG_DEAD
+ input that the asm didn't implicitly pop. If the asm didn't
+ implicitly pop an input reg, that reg will still be live.
+
+ Note that we can't use find_regno_note here: the register numbers
+ in the death notes have already been substituted. */
+
+ for (i = 0; i < n_outputs; i++)
+ if (STACK_REG_P (operands[i]))
+ {
+ int j;
+
+ for (j = 0; j < n_notes; j++)
+ if (REGNO (operands[i]) == REGNO (note_reg[j])
+ && note_kind[j] == REG_UNUSED)
+ {
+ insn = emit_pop_insn (insn, regstack, operands[i],
+ emit_insn_after);
+ break;
+ }
+ }
+
+ for (i = first_input; i < first_input + n_inputs; i++)
+ if (STACK_REG_P (operands[i]))
+ {
+ int j;
+
+ for (j = 0; j < n_notes; j++)
+ if (REGNO (operands[i]) == REGNO (note_reg[j])
+ && note_kind[j] == REG_DEAD
+ && TEST_HARD_REG_BIT (regstack->reg_set, REGNO (operands[i])))
+ {
+ insn = emit_pop_insn (insn, regstack, operands[i],
+ emit_insn_after);
+ break;
+ }
+ }
+}
+
+/* Substitute stack hard reg numbers for stack virtual registers in
+ INSN. Non-stack register numbers are not changed. REGSTACK is the
+ current stack content. Insns may be emitted as needed to arrange the
+ stack for the 387 based on the contents of the insn. */
+
+static void
+subst_stack_regs (insn, regstack)
+ rtx insn;
+ stack regstack;
+{
+ register rtx *note_link, note;
+ register int i;
+ int n_operands;
+
+ if ((GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN)
+ || INSN_DELETED_P (insn))
+ return;
+
+ /* The stack should be empty at a call. */
+
+ if (GET_CODE (insn) == CALL_INSN)
+ for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++)
+ if (TEST_HARD_REG_BIT (regstack->reg_set, i))
+ abort ();
+
+ /* Do the actual substitution if any stack regs are mentioned.
+ Since we only record whether entire insn mentions stack regs, and
+ subst_stack_regs_pat only works for patterns that contain stack regs,
+ we must check each pattern in a parallel here. A call_value_pop could
+ fail otherwise. */
+
+ if (GET_MODE (insn) == QImode)
+ {
+ n_operands = asm_noperands (PATTERN (insn));
+ if (n_operands >= 0)
+ {
+ /* This insn is an `asm' with operands. Decode the operands,
+ decide how many are inputs, and do register substitution.
+ Any REG_UNUSED notes will be handled by subst_asm_stack_regs. */
+
+ rtx operands[MAX_RECOG_OPERANDS];
+ rtx *operands_loc[MAX_RECOG_OPERANDS];
+ rtx body = PATTERN (insn);
+ int n_inputs, n_outputs;
+ char **constraints
+ = (char **) alloca (n_operands * sizeof (char *));
+
+ decode_asm_operands (body, operands, operands_loc,
+ constraints, NULL_PTR);
+ get_asm_operand_lengths (body, n_operands, &n_inputs, &n_outputs);
+ subst_asm_stack_regs (insn, regstack, operands, operands_loc,
+ constraints, n_inputs, n_outputs);
+ return;
+ }
+
+ if (GET_CODE (PATTERN (insn)) == PARALLEL)
+ for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
+ {
+ if (stack_regs_mentioned_p (XVECEXP (PATTERN (insn), 0, i)))
+ subst_stack_regs_pat (insn, regstack,
+ XVECEXP (PATTERN (insn), 0, i));
+ }
+ else
+ subst_stack_regs_pat (insn, regstack, PATTERN (insn));
+ }
+
+ /* subst_stack_regs_pat may have deleted a no-op insn. If so, any
+ REG_UNUSED will already have been dealt with, so just return. */
+
+ if (INSN_DELETED_P (insn))
+ return;
+
+ /* If there is a REG_UNUSED note on a stack register on this insn,
+ the indicated reg must be popped. The REG_UNUSED note is removed,
+ since the form of the newly emitted pop insn references the reg,
+ making it no longer `unset'. */
+
+ note_link = &REG_NOTES(insn);
+ for (note = *note_link; note; note = XEXP (note, 1))
+ if (REG_NOTE_KIND (note) == REG_UNUSED && STACK_REG_P (XEXP (note, 0)))
+ {
+ *note_link = XEXP (note, 1);
+ insn = emit_pop_insn (insn, regstack, XEXP (note, 0), emit_insn_after);
+ }
+ else
+ note_link = &XEXP (note, 1);
+}
+
+/* Change the organization of the stack so that it fits a new basic
+ block. Some registers might have to be popped, but there can never be
+ a register live in the new block that is not now live.
+
+ Insert any needed insns before or after INSN. WHEN is emit_insn_before
+ or emit_insn_after. OLD is the original stack layout, and NEW is
+ the desired form. OLD is updated to reflect the code emitted, ie, it
+ will be the same as NEW upon return.
+
+ This function will not preserve block_end[]. But that information
+ is no longer needed once this has executed. */
+
+static void
+change_stack (insn, old, new, when)
+ rtx insn;
+ stack old;
+ stack new;
+ rtx (*when)();
+{
+ int reg;
+
+ /* We will be inserting new insns "backwards", by calling emit_insn_before.
+ If we are to insert after INSN, find the next insn, and insert before
+ it. */
+
+ if (when == emit_insn_after)
+ insn = NEXT_INSN (insn);
+
+ /* Pop any registers that are not needed in the new block. */
+
+ for (reg = old->top; reg >= 0; reg--)
+ if (! TEST_HARD_REG_BIT (new->reg_set, old->reg[reg]))
+ emit_pop_insn (insn, old, FP_mode_reg[old->reg[reg]][(int) DFmode],
+ emit_insn_before);
+
+ if (new->top == -2)
+ {
+ /* If the new block has never been processed, then it can inherit
+ the old stack order. */
+
+ new->top = old->top;
+ bcopy (old->reg, new->reg, sizeof (new->reg));
+ }
+ else
+ {
+ /* This block has been entered before, and we must match the
+ previously selected stack order. */
+
+ /* By now, the only difference should be the order of the stack,
+ not their depth or liveliness. */
+
+ GO_IF_HARD_REG_EQUAL (old->reg_set, new->reg_set, win);
+
+ abort ();
+
+ win:
+
+ if (old->top != new->top)
+ abort ();
+
+ /* Loop here emitting swaps until the stack is correct. The
+ worst case number of swaps emitted is N + 2, where N is the
+ depth of the stack. In some cases, the reg at the top of
+ stack may be correct, but swapped anyway in order to fix
+ other regs. But since we never swap any other reg away from
+ its correct slot, this algorithm will converge. */
+
+ do
+ {
+ /* Swap the reg at top of stack into the position it is
+ supposed to be in, until the correct top of stack appears. */
+
+ while (old->reg[old->top] != new->reg[new->top])
+ {
+ for (reg = new->top; reg >= 0; reg--)
+ if (new->reg[reg] == old->reg[old->top])
+ break;
+
+ if (reg == -1)
+ abort ();
+
+ emit_swap_insn (insn, old,
+ FP_mode_reg[old->reg[reg]][(int) DFmode]);
+ }
+
+ /* See if any regs remain incorrect. If so, bring an
+ incorrect reg to the top of stack, and let the while loop
+ above fix it. */
+
+ for (reg = new->top; reg >= 0; reg--)
+ if (new->reg[reg] != old->reg[reg])
+ {
+ emit_swap_insn (insn, old,
+ FP_mode_reg[old->reg[reg]][(int) DFmode]);
+ break;
+ }
+ } while (reg >= 0);
+
+ /* At this point there must be no differences. */
+
+ for (reg = old->top; reg >= 0; reg--)
+ if (old->reg[reg] != new->reg[reg])
+ abort ();
+ }
+}
+
+/* Check PAT, which points to RTL in INSN, for a LABEL_REF. If it is
+ found, ensure that a jump from INSN to the code_label to which the
+ label_ref points ends up with the same stack as that at the
+ code_label. Do this by inserting insns just before the code_label to
+ pop and rotate the stack until it is in the correct order. REGSTACK
+ is the order of the register stack in INSN.
+
+ Any code that is emitted here must not be later processed as part
+ of any block, as it will already contain hard register numbers. */
+
+static void
+goto_block_pat (insn, regstack, pat)
+ rtx insn;
+ stack regstack;
+ rtx pat;
+{
+ rtx label;
+ rtx new_jump, new_label, new_barrier;
+ rtx *ref;
+ stack label_stack;
+ struct stack_def temp_stack;
+ int reg;
+
+ if (GET_CODE (pat) != LABEL_REF)
+ {
+ int i, j;
+ char *fmt = GET_RTX_FORMAT (GET_CODE (pat));
+
+ for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ goto_block_pat (insn, regstack, XEXP (pat, i));
+ if (fmt[i] == 'E')
+ for (j = 0; j < XVECLEN (pat, i); j++)
+ goto_block_pat (insn, regstack, XVECEXP (pat, i, j));
+ }
+ return;
+ }
+
+ label = XEXP (pat, 0);
+ if (GET_CODE (label) != CODE_LABEL)
+ abort ();
+
+ /* First, see if in fact anything needs to be done to the stack at all. */
+ if (INSN_UID (label) <= 0)
+ return;
+
+ label_stack = &block_stack_in[BLOCK_NUM (label)];
+
+ if (label_stack->top == -2)
+ {
+ /* If the target block hasn't had a stack order selected, then
+ we need merely ensure that no pops are needed. */
+
+ for (reg = regstack->top; reg >= 0; reg--)
+ if (! TEST_HARD_REG_BIT (label_stack->reg_set, regstack->reg[reg]))
+ break;
+
+ if (reg == -1)
+ {
+ /* change_stack will not emit any code in this case. */
+
+ change_stack (label, regstack, label_stack, emit_insn_after);
+ return;
+ }
+ }
+ else if (label_stack->top == regstack->top)
+ {
+ for (reg = label_stack->top; reg >= 0; reg--)
+ if (label_stack->reg[reg] != regstack->reg[reg])
+ break;
+
+ if (reg == -1)
+ return;
+ }
+
+ /* At least one insn will need to be inserted before label. Insert
+ a jump around the code we are about to emit. Emit a label for the new
+ code, and point the original insn at this new label. We can't use
+ redirect_jump here, because we're using fld[4] of the code labels as
+ LABEL_REF chains, no NUSES counters. */
+
+ new_jump = emit_jump_insn_before (gen_jump (label), label);
+ record_label_references (new_jump, PATTERN (new_jump));
+ JUMP_LABEL (new_jump) = label;
+
+ new_barrier = emit_barrier_after (new_jump);
+
+ new_label = gen_label_rtx ();
+ emit_label_after (new_label, new_barrier);
+ LABEL_REFS (new_label) = new_label;
+
+ /* The old label_ref will no longer point to the code_label if now uses,
+ so strip the label_ref from the code_label's chain of references. */
+
+ for (ref = &LABEL_REFS (label); *ref != label; ref = &LABEL_NEXTREF (*ref))
+ if (*ref == pat)
+ break;
+
+ if (*ref == label)
+ abort ();
+
+ *ref = LABEL_NEXTREF (*ref);
+
+ XEXP (pat, 0) = new_label;
+ record_label_references (insn, PATTERN (insn));
+
+ if (JUMP_LABEL (insn) == label)
+ JUMP_LABEL (insn) = new_label;
+
+ /* Now emit the needed code. */
+
+ temp_stack = *regstack;
+
+ change_stack (new_label, &temp_stack, label_stack, emit_insn_after);
+}
+
+/* Traverse all basic blocks in a function, converting the register
+ references in each insn from the "flat" register file that gcc uses, to
+ the stack-like registers the 387 uses. */
+
+static void
+convert_regs ()
+{
+ register int block, reg;
+ register rtx insn, next;
+ struct stack_def regstack;
+
+ for (block = 0; block < blocks; block++)
+ {
+ if (block_stack_in[block].top == -2)
+ {
+ /* This block has not been previously encountered. Choose a
+ default mapping for any stack regs live on entry */
+
+ block_stack_in[block].top = -1;
+
+ for (reg = LAST_STACK_REG; reg >= FIRST_STACK_REG; reg--)
+ if (TEST_HARD_REG_BIT (block_stack_in[block].reg_set, reg))
+ block_stack_in[block].reg[++block_stack_in[block].top] = reg;
+ }
+
+ /* Process all insns in this block. Keep track of `next' here,
+ so that we don't process any insns emitted while making
+ substitutions in INSN. */
+
+ next = block_begin[block];
+ regstack = block_stack_in[block];
+ do
+ {
+ insn = next;
+ next = NEXT_INSN (insn);
+
+ /* Don't bother processing unless there is a stack reg
+ mentioned.
+
+ ??? For now, process CALL_INSNs too to make sure that the
+ stack regs are dead after a call. Remove this eventually. */
+
+ if (GET_MODE (insn) == QImode || GET_CODE (insn) == CALL_INSN)
+ subst_stack_regs (insn, &regstack);
+
+ } while (insn != block_end[block]);
+
+ /* Something failed if the stack life doesn't match. */
+
+ GO_IF_HARD_REG_EQUAL (regstack.reg_set, block_out_reg_set[block], win);
+
+ abort ();
+
+ win:
+
+ /* Adjust the stack of this block on exit to match the stack of
+ the target block, or copy stack information into stack of
+ jump target if the target block's stack order hasn't been set
+ yet. */
+
+ if (GET_CODE (insn) == JUMP_INSN)
+ goto_block_pat (insn, &regstack, PATTERN (insn));
+
+ /* Likewise handle the case where we fall into the next block. */
+
+ if ((block < blocks - 1) && block_drops_in[block+1])
+ change_stack (insn, &regstack, &block_stack_in[block+1],
+ emit_insn_after);
+ }
+
+ /* If the last basic block is the end of a loop, and that loop has
+ regs live at its start, then the last basic block will have regs live
+ at its end that need to be popped before the function returns. */
+
+ for (reg = regstack.top; reg >= 0; reg--)
+ if (! current_function_returns_real
+ || regstack.reg[reg] != FIRST_STACK_REG)
+ insn = emit_pop_insn (insn, &regstack,
+ FP_mode_reg[regstack.reg[reg]][(int) DFmode],
+ emit_insn_after);
+}
+
+/* Check expression PAT, which is in INSN, for label references. if
+ one is found, print the block number of destination to FILE. */
+
+static void
+print_blocks (file, insn, pat)
+ FILE *file;
+ rtx insn, pat;
+{
+ register RTX_CODE code = GET_CODE (pat);
+ register int i;
+ register char *fmt;
+
+ if (code == LABEL_REF)
+ {
+ register rtx label = XEXP (pat, 0);
+
+ if (GET_CODE (label) != CODE_LABEL)
+ abort ();
+
+ fprintf (file, " %d", BLOCK_NUM (label));
+
+ return;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ print_blocks (file, insn, XEXP (pat, i));
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (pat, i); j++)
+ print_blocks (file, insn, XVECEXP (pat, i, j));
+ }
+ }
+}
+
+/* Write information about stack registers and stack blocks into FILE.
+ This is part of making a debugging dump. */
+static void
+dump_stack_info (file)
+ FILE *file;
+{
+ register int block;
+
+ fprintf (file, "\n%d stack blocks.\n", blocks);
+ for (block = 0; block < blocks; block++)
+ {
+ register rtx head, jump, end;
+ register int regno;
+
+ fprintf (file, "\nStack block %d: first insn %d, last %d.\n",
+ block, INSN_UID (block_begin[block]),
+ INSN_UID (block_end[block]));
+
+ head = block_begin[block];
+
+ fprintf (file, "Reached from blocks: ");
+ if (GET_CODE (head) == CODE_LABEL)
+ for (jump = LABEL_REFS (head);
+ jump != head;
+ jump = LABEL_NEXTREF (jump))
+ {
+ register int from_block = BLOCK_NUM (CONTAINING_INSN (jump));
+ fprintf (file, " %d", from_block);
+ }
+ if (block_drops_in[block])
+ fprintf (file, " previous");
+
+ fprintf (file, "\nlive stack registers on block entry: ");
+ for (regno = FIRST_STACK_REG; regno <= LAST_STACK_REG ; regno++)
+ {
+ if (TEST_HARD_REG_BIT (block_stack_in[block].reg_set, regno))
+ fprintf (file, "%d ", regno);
+ }
+
+ fprintf (file, "\nlive stack registers on block exit: ");
+ for (regno = FIRST_STACK_REG; regno <= LAST_STACK_REG ; regno++)
+ {
+ if (TEST_HARD_REG_BIT (block_out_reg_set[block], regno))
+ fprintf (file, "%d ", regno);
+ }
+
+ end = block_end[block];
+
+ fprintf (file, "\nJumps to blocks: ");
+ if (GET_CODE (end) == JUMP_INSN)
+ print_blocks (file, end, PATTERN (end));
+
+ if (block + 1 < blocks && block_drops_in[block+1])
+ fprintf (file, " next");
+ else if (block + 1 == blocks
+ || (GET_CODE (end) == JUMP_INSN
+ && GET_CODE (PATTERN (end)) == RETURN))
+ fprintf (file, " return");
+
+ fprintf (file, "\n");
+ }
+}
+#endif /* STACK_REGS */
diff --git a/gnu/usr.bin/cc/cc_int/regclass.c b/gnu/usr.bin/cc/cc_int/regclass.c
new file mode 100644
index 0000000..d4636d5
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/regclass.c
@@ -0,0 +1,1856 @@
+/* Compute register class preferences for pseudo-registers.
+ Copyright (C) 1987, 88, 91, 92, 93, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file contains two passes of the compiler: reg_scan and reg_class.
+ It also defines some tables of information about the hardware registers
+ and a function init_reg_sets to initialize the tables. */
+
+#include "config.h"
+#include "rtl.h"
+#include "hard-reg-set.h"
+#include "flags.h"
+#include "basic-block.h"
+#include "regs.h"
+#include "insn-config.h"
+#include "recog.h"
+#include "reload.h"
+#include "real.h"
+#include "bytecode.h"
+
+#ifndef REGISTER_MOVE_COST
+#define REGISTER_MOVE_COST(x, y) 2
+#endif
+
+#ifndef MEMORY_MOVE_COST
+#define MEMORY_MOVE_COST(x) 4
+#endif
+
+/* If we have auto-increment or auto-decrement and we can have secondary
+ reloads, we are not allowed to use classes requiring secondary
+ reloads for psuedos auto-incremented since reload can't handle it. */
+
+#ifdef AUTO_INC_DEC
+#if defined(SECONDARY_INPUT_RELOAD_CLASS) || defined(SECONDARY_OUTPUT_RELOAD_CLASS)
+#define FORBIDDEN_INC_DEC_CLASSES
+#endif
+#endif
+
+/* Register tables used by many passes. */
+
+/* Indexed by hard register number, contains 1 for registers
+ that are fixed use (stack pointer, pc, frame pointer, etc.).
+ These are the registers that cannot be used to allocate
+ a pseudo reg whose life does not cross calls. */
+
+char fixed_regs[FIRST_PSEUDO_REGISTER];
+
+/* Same info as a HARD_REG_SET. */
+
+HARD_REG_SET fixed_reg_set;
+
+/* Data for initializing the above. */
+
+static char initial_fixed_regs[] = FIXED_REGISTERS;
+
+/* Indexed by hard register number, contains 1 for registers
+ that are fixed use or are clobbered by function calls.
+ These are the registers that cannot be used to allocate
+ a pseudo reg whose life crosses calls. */
+
+char call_used_regs[FIRST_PSEUDO_REGISTER];
+
+/* Same info as a HARD_REG_SET. */
+
+HARD_REG_SET call_used_reg_set;
+
+/* Data for initializing the above. */
+
+static char initial_call_used_regs[] = CALL_USED_REGISTERS;
+
+/* Indexed by hard register number, contains 1 for registers that are
+ fixed use -- i.e. in fixed_regs -- or a function value return register
+ or STRUCT_VALUE_REGNUM or STATIC_CHAIN_REGNUM. These are the
+ registers that cannot hold quantities across calls even if we are
+ willing to save and restore them. */
+
+char call_fixed_regs[FIRST_PSEUDO_REGISTER];
+
+/* The same info as a HARD_REG_SET. */
+
+HARD_REG_SET call_fixed_reg_set;
+
+/* Number of non-fixed registers. */
+
+int n_non_fixed_regs;
+
+/* Indexed by hard register number, contains 1 for registers
+ that are being used for global register decls.
+ These must be exempt from ordinary flow analysis
+ and are also considered fixed. */
+
+char global_regs[FIRST_PSEUDO_REGISTER];
+
+/* Table of register numbers in the order in which to try to use them. */
+#ifdef REG_ALLOC_ORDER
+int reg_alloc_order[FIRST_PSEUDO_REGISTER] = REG_ALLOC_ORDER;
+#endif
+
+/* For each reg class, a HARD_REG_SET saying which registers are in it. */
+
+HARD_REG_SET reg_class_contents[N_REG_CLASSES];
+
+/* The same information, but as an array of unsigned ints. We copy from
+ these unsigned ints to the table above. We do this so the tm.h files
+ do not have to be aware of the wordsize for machines with <= 64 regs. */
+
+#define N_REG_INTS \
+ ((FIRST_PSEUDO_REGISTER + (HOST_BITS_PER_INT - 1)) / HOST_BITS_PER_INT)
+
+static unsigned int_reg_class_contents[N_REG_CLASSES][N_REG_INTS]
+ = REG_CLASS_CONTENTS;
+
+/* For each reg class, number of regs it contains. */
+
+int reg_class_size[N_REG_CLASSES];
+
+/* For each reg class, table listing all the containing classes. */
+
+enum reg_class reg_class_superclasses[N_REG_CLASSES][N_REG_CLASSES];
+
+/* For each reg class, table listing all the classes contained in it. */
+
+enum reg_class reg_class_subclasses[N_REG_CLASSES][N_REG_CLASSES];
+
+/* For each pair of reg classes,
+ a largest reg class contained in their union. */
+
+enum reg_class reg_class_subunion[N_REG_CLASSES][N_REG_CLASSES];
+
+/* For each pair of reg classes,
+ the smallest reg class containing their union. */
+
+enum reg_class reg_class_superunion[N_REG_CLASSES][N_REG_CLASSES];
+
+/* Array containing all of the register names */
+
+char *reg_names[] = REGISTER_NAMES;
+
+/* For each hard register, the widest mode object that it can contain.
+ This will be a MODE_INT mode if the register can hold integers. Otherwise
+ it will be a MODE_FLOAT or a MODE_CC mode, whichever is valid for the
+ register. */
+
+enum machine_mode reg_raw_mode[FIRST_PSEUDO_REGISTER];
+
+/* Indexed by n, gives number of times (REG n) is set or clobbered.
+ This information remains valid for the rest of the compilation
+ of the current function; it is used to control register allocation.
+
+ This information applies to both hard registers and pseudo registers,
+ unlike much of the information above. */
+
+short *reg_n_sets;
+
+/* Maximum cost of moving from a register in one class to a register in
+ another class. Based on REGISTER_MOVE_COST. */
+
+static int move_cost[N_REG_CLASSES][N_REG_CLASSES];
+
+/* Similar, but here we don't have to move if the first index is a subset
+ of the second so in that case the cost is zero. */
+
+static int may_move_cost[N_REG_CLASSES][N_REG_CLASSES];
+
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+
+/* These are the classes that regs which are auto-incremented or decremented
+ cannot be put in. */
+
+static int forbidden_inc_dec_class[N_REG_CLASSES];
+
+/* Indexed by n, is non-zero if (REG n) is used in an auto-inc or auto-dec
+ context. */
+
+static char *in_inc_dec;
+
+#endif /* FORBIDDEN_INC_DEC_CLASSES */
+
+/* Function called only once to initialize the above data on reg usage.
+ Once this is done, various switches may override. */
+
+void
+init_reg_sets ()
+{
+ register int i, j;
+
+ /* First copy the register information from the initial int form into
+ the regsets. */
+
+ for (i = 0; i < N_REG_CLASSES; i++)
+ {
+ CLEAR_HARD_REG_SET (reg_class_contents[i]);
+
+ for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
+ if (int_reg_class_contents[i][j / HOST_BITS_PER_INT]
+ & ((unsigned) 1 << (j % HOST_BITS_PER_INT)))
+ SET_HARD_REG_BIT (reg_class_contents[i], j);
+ }
+
+ bcopy (initial_fixed_regs, fixed_regs, sizeof fixed_regs);
+ bcopy (initial_call_used_regs, call_used_regs, sizeof call_used_regs);
+ bzero (global_regs, sizeof global_regs);
+
+ /* Compute number of hard regs in each class. */
+
+ bzero ((char *) reg_class_size, sizeof reg_class_size);
+ for (i = 0; i < N_REG_CLASSES; i++)
+ for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
+ if (TEST_HARD_REG_BIT (reg_class_contents[i], j))
+ reg_class_size[i]++;
+
+ /* Initialize the table of subunions.
+ reg_class_subunion[I][J] gets the largest-numbered reg-class
+ that is contained in the union of classes I and J. */
+
+ for (i = 0; i < N_REG_CLASSES; i++)
+ {
+ for (j = 0; j < N_REG_CLASSES; j++)
+ {
+#ifdef HARD_REG_SET
+ register /* Declare it register if it's a scalar. */
+#endif
+ HARD_REG_SET c;
+ register int k;
+
+ COPY_HARD_REG_SET (c, reg_class_contents[i]);
+ IOR_HARD_REG_SET (c, reg_class_contents[j]);
+ for (k = 0; k < N_REG_CLASSES; k++)
+ {
+ GO_IF_HARD_REG_SUBSET (reg_class_contents[k], c,
+ subclass1);
+ continue;
+
+ subclass1:
+ /* keep the largest subclass */ /* SPEE 900308 */
+ GO_IF_HARD_REG_SUBSET (reg_class_contents[k],
+ reg_class_contents[(int) reg_class_subunion[i][j]],
+ subclass2);
+ reg_class_subunion[i][j] = (enum reg_class) k;
+ subclass2:
+ ;
+ }
+ }
+ }
+
+ /* Initialize the table of superunions.
+ reg_class_superunion[I][J] gets the smallest-numbered reg-class
+ containing the union of classes I and J. */
+
+ for (i = 0; i < N_REG_CLASSES; i++)
+ {
+ for (j = 0; j < N_REG_CLASSES; j++)
+ {
+#ifdef HARD_REG_SET
+ register /* Declare it register if it's a scalar. */
+#endif
+ HARD_REG_SET c;
+ register int k;
+
+ COPY_HARD_REG_SET (c, reg_class_contents[i]);
+ IOR_HARD_REG_SET (c, reg_class_contents[j]);
+ for (k = 0; k < N_REG_CLASSES; k++)
+ GO_IF_HARD_REG_SUBSET (c, reg_class_contents[k], superclass);
+
+ superclass:
+ reg_class_superunion[i][j] = (enum reg_class) k;
+ }
+ }
+
+ /* Initialize the tables of subclasses and superclasses of each reg class.
+ First clear the whole table, then add the elements as they are found. */
+
+ for (i = 0; i < N_REG_CLASSES; i++)
+ {
+ for (j = 0; j < N_REG_CLASSES; j++)
+ {
+ reg_class_superclasses[i][j] = LIM_REG_CLASSES;
+ reg_class_subclasses[i][j] = LIM_REG_CLASSES;
+ }
+ }
+
+ for (i = 0; i < N_REG_CLASSES; i++)
+ {
+ if (i == (int) NO_REGS)
+ continue;
+
+ for (j = i + 1; j < N_REG_CLASSES; j++)
+ {
+ enum reg_class *p;
+
+ GO_IF_HARD_REG_SUBSET (reg_class_contents[i], reg_class_contents[j],
+ subclass);
+ continue;
+ subclass:
+ /* Reg class I is a subclass of J.
+ Add J to the table of superclasses of I. */
+ p = &reg_class_superclasses[i][0];
+ while (*p != LIM_REG_CLASSES) p++;
+ *p = (enum reg_class) j;
+ /* Add I to the table of superclasses of J. */
+ p = &reg_class_subclasses[j][0];
+ while (*p != LIM_REG_CLASSES) p++;
+ *p = (enum reg_class) i;
+ }
+ }
+
+ /* Initialize the move cost table. Find every subset of each class
+ and take the maximum cost of moving any subset to any other. */
+
+ for (i = 0; i < N_REG_CLASSES; i++)
+ for (j = 0; j < N_REG_CLASSES; j++)
+ {
+ int cost = i == j ? 2 : REGISTER_MOVE_COST (i, j);
+ enum reg_class *p1, *p2;
+
+ for (p2 = &reg_class_subclasses[j][0]; *p2 != LIM_REG_CLASSES; p2++)
+ if (*p2 != i)
+ cost = MAX (cost, REGISTER_MOVE_COST (i, *p2));
+
+ for (p1 = &reg_class_subclasses[i][0]; *p1 != LIM_REG_CLASSES; p1++)
+ {
+ if (*p1 != j)
+ cost = MAX (cost, REGISTER_MOVE_COST (*p1, j));
+
+ for (p2 = &reg_class_subclasses[j][0];
+ *p2 != LIM_REG_CLASSES; p2++)
+ if (*p1 != *p2)
+ cost = MAX (cost, REGISTER_MOVE_COST (*p1, *p2));
+ }
+
+ move_cost[i][j] = cost;
+
+ if (reg_class_subset_p (i, j))
+ cost = 0;
+
+ may_move_cost[i][j] = cost;
+ }
+}
+
+/* After switches have been processed, which perhaps alter
+ `fixed_regs' and `call_used_regs', convert them to HARD_REG_SETs. */
+
+static void
+init_reg_sets_1 ()
+{
+ register int i;
+
+ /* This macro allows the fixed or call-used registers
+ to depend on target flags. */
+
+#ifdef CONDITIONAL_REGISTER_USAGE
+ CONDITIONAL_REGISTER_USAGE;
+#endif
+
+ /* Initialize "constant" tables. */
+
+ CLEAR_HARD_REG_SET (fixed_reg_set);
+ CLEAR_HARD_REG_SET (call_used_reg_set);
+ CLEAR_HARD_REG_SET (call_fixed_reg_set);
+
+ bcopy (fixed_regs, call_fixed_regs, sizeof call_fixed_regs);
+
+ n_non_fixed_regs = 0;
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ if (fixed_regs[i])
+ SET_HARD_REG_BIT (fixed_reg_set, i);
+ else
+ n_non_fixed_regs++;
+
+ if (call_used_regs[i])
+ SET_HARD_REG_BIT (call_used_reg_set, i);
+ if (call_fixed_regs[i])
+ SET_HARD_REG_BIT (call_fixed_reg_set, i);
+ }
+}
+
+/* Compute the table of register modes.
+ These values are used to record death information for individual registers
+ (as opposed to a multi-register mode). */
+
+static void
+init_reg_modes ()
+{
+ register int i;
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ reg_raw_mode[i] = choose_hard_reg_mode (i, 1);
+
+ /* If we couldn't find a valid mode, fall back to `word_mode'.
+ ??? We assume `word_mode' has already been initialized.
+ ??? One situation in which we need to do this is on the mips where
+ HARD_REGNO_NREGS (fpreg, [SD]Fmode) returns 2. Ideally we'd like
+ to use DF mode for the even registers and VOIDmode for the odd
+ (for the cpu models where the odd ones are inaccessable). */
+ if (reg_raw_mode[i] == VOIDmode)
+ reg_raw_mode[i] = word_mode;
+ }
+}
+
+/* Finish initializing the register sets and
+ initialize the register modes. */
+
+void
+init_regs ()
+{
+ /* This finishes what was started by init_reg_sets, but couldn't be done
+ until after register usage was specified. */
+ if (!output_bytecode)
+ init_reg_sets_1 ();
+
+ init_reg_modes ();
+}
+
+/* Return a machine mode that is legitimate for hard reg REGNO and large
+ enough to save nregs. If we can't find one, return VOIDmode. */
+
+enum machine_mode
+choose_hard_reg_mode (regno, nregs)
+ int regno;
+ int nregs;
+{
+ enum machine_mode found_mode = VOIDmode, mode;
+
+ /* We first look for the largest integer mode that can be validly
+ held in REGNO. If none, we look for the largest floating-point mode.
+ If we still didn't find a valid mode, try CCmode. */
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+ mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ if (HARD_REGNO_NREGS (regno, mode) == nregs
+ && HARD_REGNO_MODE_OK (regno, mode))
+ found_mode = mode;
+
+ if (found_mode != VOIDmode)
+ return found_mode;
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT);
+ mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ if (HARD_REGNO_NREGS (regno, mode) == nregs
+ && HARD_REGNO_MODE_OK (regno, mode))
+ found_mode = mode;
+
+ if (found_mode != VOIDmode)
+ return found_mode;
+
+ if (HARD_REGNO_NREGS (regno, CCmode) == nregs
+ && HARD_REGNO_MODE_OK (regno, CCmode))
+ return CCmode;
+
+ /* We can't find a mode valid for this register. */
+ return VOIDmode;
+}
+
+/* Specify the usage characteristics of the register named NAME.
+ It should be a fixed register if FIXED and a
+ call-used register if CALL_USED. */
+
+void
+fix_register (name, fixed, call_used)
+ char *name;
+ int fixed, call_used;
+{
+ int i;
+
+ if (output_bytecode)
+ {
+ warning ("request to mark `%s' as %s ignored by bytecode compiler",
+ name, call_used ? "call-used" : "fixed");
+ return;
+ }
+
+ /* Decode the name and update the primary form of
+ the register info. */
+
+ if ((i = decode_reg_name (name)) >= 0)
+ {
+ fixed_regs[i] = fixed;
+ call_used_regs[i] = call_used;
+ }
+ else
+ {
+ warning ("unknown register name: %s", name);
+ }
+}
+
+/* Mark register number I as global. */
+
+void
+globalize_reg (i)
+ int i;
+{
+ if (global_regs[i])
+ {
+ warning ("register used for two global register variables");
+ return;
+ }
+
+ if (call_used_regs[i] && ! fixed_regs[i])
+ warning ("call-clobbered register used for global register variable");
+
+ global_regs[i] = 1;
+
+ /* If already fixed, nothing else to do. */
+ if (fixed_regs[i])
+ return;
+
+ fixed_regs[i] = call_used_regs[i] = call_fixed_regs[i] = 1;
+ n_non_fixed_regs--;
+
+ SET_HARD_REG_BIT (fixed_reg_set, i);
+ SET_HARD_REG_BIT (call_used_reg_set, i);
+ SET_HARD_REG_BIT (call_fixed_reg_set, i);
+}
+
+/* Now the data and code for the `regclass' pass, which happens
+ just before local-alloc. */
+
+/* The `costs' struct records the cost of using a hard register of each class
+ and of using memory for each pseudo. We use this data to set up
+ register class preferences. */
+
+struct costs
+{
+ int cost[N_REG_CLASSES];
+ int mem_cost;
+};
+
+/* Record the cost of each class for each pseudo. */
+
+static struct costs *costs;
+
+/* Record the same data by operand number, accumulated for each alternative
+ in an insn. The contribution to a pseudo is that of the minimum-cost
+ alternative. */
+
+static struct costs op_costs[MAX_RECOG_OPERANDS];
+
+/* (enum reg_class) prefclass[R] is the preferred class for pseudo number R.
+ This is available after `regclass' is run. */
+
+static char *prefclass;
+
+/* altclass[R] is a register class that we should use for allocating
+ pseudo number R if no register in the preferred class is available.
+ If no register in this class is available, memory is preferred.
+
+ It might appear to be more general to have a bitmask of classes here,
+ but since it is recommended that there be a class corresponding to the
+ union of most major pair of classes, that generality is not required.
+
+ This is available after `regclass' is run. */
+
+static char *altclass;
+
+/* Record the depth of loops that we are in. */
+
+static int loop_depth;
+
+/* Account for the fact that insns within a loop are executed very commonly,
+ but don't keep doing this as loops go too deep. */
+
+static int loop_cost;
+
+static void record_reg_classes PROTO((int, int, rtx *, enum machine_mode *,
+ char **, rtx));
+static int copy_cost PROTO((rtx, enum machine_mode,
+ enum reg_class, int));
+static void record_address_regs PROTO((rtx, enum reg_class, int));
+static auto_inc_dec_reg_p PROTO((rtx, enum machine_mode));
+static void reg_scan_mark_refs PROTO((rtx, rtx, int));
+
+/* Return the reg_class in which pseudo reg number REGNO is best allocated.
+ This function is sometimes called before the info has been computed.
+ When that happens, just return GENERAL_REGS, which is innocuous. */
+
+enum reg_class
+reg_preferred_class (regno)
+ int regno;
+{
+ if (prefclass == 0)
+ return GENERAL_REGS;
+ return (enum reg_class) prefclass[regno];
+}
+
+enum reg_class
+reg_alternate_class (regno)
+{
+ if (prefclass == 0)
+ return ALL_REGS;
+
+ return (enum reg_class) altclass[regno];
+}
+
+/* This prevents dump_flow_info from losing if called
+ before regclass is run. */
+
+void
+regclass_init ()
+{
+ prefclass = 0;
+}
+
+/* This is a pass of the compiler that scans all instructions
+ and calculates the preferred class for each pseudo-register.
+ This information can be accessed later by calling `reg_preferred_class'.
+ This pass comes just before local register allocation. */
+
+void
+regclass (f, nregs)
+ rtx f;
+ int nregs;
+{
+#ifdef REGISTER_CONSTRAINTS
+ register rtx insn;
+ register int i, j;
+ struct costs init_cost;
+ rtx set;
+ int pass;
+
+ init_recog ();
+
+ costs = (struct costs *) alloca (nregs * sizeof (struct costs));
+
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+
+ in_inc_dec = (char *) alloca (nregs);
+
+ /* Initialize information about which register classes can be used for
+ pseudos that are auto-incremented or auto-decremented. It would
+ seem better to put this in init_reg_sets, but we need to be able
+ to allocate rtx, which we can't do that early. */
+
+ for (i = 0; i < N_REG_CLASSES; i++)
+ {
+ rtx r = gen_rtx (REG, VOIDmode, 0);
+ enum machine_mode m;
+
+ for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
+ if (TEST_HARD_REG_BIT (reg_class_contents[i], j))
+ {
+ REGNO (r) = j;
+
+ for (m = VOIDmode; (int) m < (int) MAX_MACHINE_MODE;
+ m = (enum machine_mode) ((int) m + 1))
+ if (HARD_REGNO_MODE_OK (j, m))
+ {
+ PUT_MODE (r, m);
+
+ /* If a register is not directly suitable for an
+ auto-increment or decrement addressing mode and
+ requires secondary reloads, disallow its class from
+ being used in such addresses. */
+
+ if ((0
+#ifdef SECONDARY_INPUT_RELOAD_CLASS
+ || (SECONDARY_INPUT_RELOAD_CLASS (BASE_REG_CLASS, m, r)
+ != NO_REGS)
+#endif
+#ifdef SECONDARY_OUTPUT_RELOAD_CLASS
+ || (SECONDARY_OUTPUT_RELOAD_CLASS (BASE_REG_CLASS, m, r)
+ != NO_REGS)
+#endif
+ )
+ && ! auto_inc_dec_reg_p (r, m))
+ forbidden_inc_dec_class[i] = 1;
+ }
+ }
+ }
+#endif /* FORBIDDEN_INC_DEC_CLASSES */
+
+ init_cost.mem_cost = 10000;
+ for (i = 0; i < N_REG_CLASSES; i++)
+ init_cost.cost[i] = 10000;
+
+ /* Normally we scan the insns once and determine the best class to use for
+ each register. However, if -fexpensive_optimizations are on, we do so
+ twice, the second time using the tentative best classes to guide the
+ selection. */
+
+ for (pass = 0; pass <= flag_expensive_optimizations; pass++)
+ {
+ /* Zero out our accumulation of the cost of each class for each reg. */
+
+ bzero ((char *) costs, nregs * sizeof (struct costs));
+
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+ bzero (in_inc_dec, nregs);
+#endif
+
+ loop_depth = 0, loop_cost = 1;
+
+ /* Scan the instructions and record each time it would
+ save code to put a certain register in a certain class. */
+
+ for (insn = f; insn; insn = NEXT_INSN (insn))
+ {
+ char *constraints[MAX_RECOG_OPERANDS];
+ enum machine_mode modes[MAX_RECOG_OPERANDS];
+ int nalternatives;
+ int noperands;
+
+ /* Show that an insn inside a loop is likely to be executed three
+ times more than insns outside a loop. This is much more aggressive
+ than the assumptions made elsewhere and is being tried as an
+ experiment. */
+
+ if (GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
+ loop_depth++, loop_cost = 1 << (2 * MIN (loop_depth, 5));
+ else if (GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
+ loop_depth--, loop_cost = 1 << (2 * MIN (loop_depth, 5));
+
+ else if ((GET_CODE (insn) == INSN
+ && GET_CODE (PATTERN (insn)) != USE
+ && GET_CODE (PATTERN (insn)) != CLOBBER
+ && GET_CODE (PATTERN (insn)) != ASM_INPUT)
+ || (GET_CODE (insn) == JUMP_INSN
+ && GET_CODE (PATTERN (insn)) != ADDR_VEC
+ && GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC)
+ || GET_CODE (insn) == CALL_INSN)
+ {
+ if (GET_CODE (insn) == INSN
+ && (noperands = asm_noperands (PATTERN (insn))) >= 0)
+ {
+ decode_asm_operands (PATTERN (insn), recog_operand, NULL_PTR,
+ constraints, modes);
+ nalternatives = (noperands == 0 ? 0
+ : n_occurrences (',', constraints[0]) + 1);
+ }
+ else
+ {
+ int insn_code_number = recog_memoized (insn);
+ rtx note;
+
+ set = single_set (insn);
+ insn_extract (insn);
+
+ nalternatives = insn_n_alternatives[insn_code_number];
+ noperands = insn_n_operands[insn_code_number];
+
+ /* If this insn loads a parameter from its stack slot, then
+ it represents a savings, rather than a cost, if the
+ parameter is stored in memory. Record this fact. */
+
+ if (set != 0 && GET_CODE (SET_DEST (set)) == REG
+ && GET_CODE (SET_SRC (set)) == MEM
+ && (note = find_reg_note (insn, REG_EQUIV,
+ NULL_RTX)) != 0
+ && GET_CODE (XEXP (note, 0)) == MEM)
+ {
+ costs[REGNO (SET_DEST (set))].mem_cost
+ -= (MEMORY_MOVE_COST (GET_MODE (SET_DEST (set)))
+ * loop_cost);
+ record_address_regs (XEXP (SET_SRC (set), 0),
+ BASE_REG_CLASS, loop_cost * 2);
+ continue;
+ }
+
+ /* Improve handling of two-address insns such as
+ (set X (ashift CONST Y)) where CONST must be made to
+ match X. Change it into two insns: (set X CONST)
+ (set X (ashift X Y)). If we left this for reloading, it
+ would probably get three insns because X and Y might go
+ in the same place. This prevents X and Y from receiving
+ the same hard reg.
+
+ We can only do this if the modes of operands 0 and 1
+ (which might not be the same) are tieable and we only need
+ do this during our first pass. */
+
+ if (pass == 0 && optimize
+ && noperands >= 3
+ && insn_operand_constraint[insn_code_number][1][0] == '0'
+ && insn_operand_constraint[insn_code_number][1][1] == 0
+ && CONSTANT_P (recog_operand[1])
+ && ! rtx_equal_p (recog_operand[0], recog_operand[1])
+ && ! rtx_equal_p (recog_operand[0], recog_operand[2])
+ && GET_CODE (recog_operand[0]) == REG
+ && MODES_TIEABLE_P (GET_MODE (recog_operand[0]),
+ insn_operand_mode[insn_code_number][1]))
+ {
+ rtx previnsn = prev_real_insn (insn);
+ rtx dest
+ = gen_lowpart (insn_operand_mode[insn_code_number][1],
+ recog_operand[0]);
+ rtx newinsn
+ = emit_insn_before (gen_move_insn (dest,
+ recog_operand[1]),
+ insn);
+
+ /* If this insn was the start of a basic block,
+ include the new insn in that block.
+ We need not check for code_label here;
+ while a basic block can start with a code_label,
+ INSN could not be at the beginning of that block. */
+ if (previnsn == 0 || GET_CODE (previnsn) == JUMP_INSN)
+ {
+ int b;
+ for (b = 0; b < n_basic_blocks; b++)
+ if (insn == basic_block_head[b])
+ basic_block_head[b] = newinsn;
+ }
+
+ /* This makes one more setting of new insns's dest. */
+ reg_n_sets[REGNO (recog_operand[0])]++;
+
+ *recog_operand_loc[1] = recog_operand[0];
+ for (i = insn_n_dups[insn_code_number] - 1; i >= 0; i--)
+ if (recog_dup_num[i] == 1)
+ *recog_dup_loc[i] = recog_operand[0];
+
+ insn = PREV_INSN (newinsn);
+ continue;
+ }
+
+ for (i = 0; i < noperands; i++)
+ {
+ constraints[i]
+ = insn_operand_constraint[insn_code_number][i];
+ modes[i] = insn_operand_mode[insn_code_number][i];
+ }
+ }
+
+ /* If we get here, we are set up to record the costs of all the
+ operands for this insn. Start by initializing the costs.
+ Then handle any address registers. Finally record the desired
+ classes for any pseudos, doing it twice if some pair of
+ operands are commutative. */
+
+ for (i = 0; i < noperands; i++)
+ {
+ op_costs[i] = init_cost;
+
+ if (GET_CODE (recog_operand[i]) == SUBREG)
+ recog_operand[i] = SUBREG_REG (recog_operand[i]);
+
+ if (GET_CODE (recog_operand[i]) == MEM)
+ record_address_regs (XEXP (recog_operand[i], 0),
+ BASE_REG_CLASS, loop_cost * 2);
+ else if (constraints[i][0] == 'p')
+ record_address_regs (recog_operand[i],
+ BASE_REG_CLASS, loop_cost * 2);
+ }
+
+ /* Check for commutative in a separate loop so everything will
+ have been initialized. We must do this even if one operand
+ is a constant--see addsi3 in m68k.md. */
+
+ for (i = 0; i < noperands - 1; i++)
+ if (constraints[i][0] == '%')
+ {
+ char *xconstraints[MAX_RECOG_OPERANDS];
+ int j;
+
+ /* Handle commutative operands by swapping the constraints.
+ We assume the modes are the same. */
+
+ for (j = 0; j < noperands; j++)
+ xconstraints[j] = constraints[j];
+
+ xconstraints[i] = constraints[i+1];
+ xconstraints[i+1] = constraints[i];
+ record_reg_classes (nalternatives, noperands,
+ recog_operand, modes, xconstraints,
+ insn);
+ }
+
+ record_reg_classes (nalternatives, noperands, recog_operand,
+ modes, constraints, insn);
+
+ /* Now add the cost for each operand to the total costs for
+ its register. */
+
+ for (i = 0; i < noperands; i++)
+ if (GET_CODE (recog_operand[i]) == REG
+ && REGNO (recog_operand[i]) >= FIRST_PSEUDO_REGISTER)
+ {
+ int regno = REGNO (recog_operand[i]);
+ struct costs *p = &costs[regno], *q = &op_costs[i];
+
+ p->mem_cost += q->mem_cost * loop_cost;
+ for (j = 0; j < N_REG_CLASSES; j++)
+ p->cost[j] += q->cost[j] * loop_cost;
+ }
+ }
+ }
+
+ /* Now for each register look at how desirable each class is
+ and find which class is preferred. Store that in
+ `prefclass[REGNO]'. Record in `altclass[REGNO]' the largest register
+ class any of whose registers is better than memory. */
+
+ if (pass == 0)
+ {
+ prefclass = (char *) oballoc (nregs);
+ altclass = (char *) oballoc (nregs);
+ }
+
+ for (i = FIRST_PSEUDO_REGISTER; i < nregs; i++)
+ {
+ register int best_cost = (1 << (HOST_BITS_PER_INT - 2)) - 1;
+ enum reg_class best = ALL_REGS, alt = NO_REGS;
+ /* This is an enum reg_class, but we call it an int
+ to save lots of casts. */
+ register int class;
+ register struct costs *p = &costs[i];
+
+ for (class = (int) ALL_REGS - 1; class > 0; class--)
+ {
+ /* Ignore classes that are too small for this operand or
+ invalid for a operand that was auto-incremented. */
+ if (CLASS_MAX_NREGS (class, PSEUDO_REGNO_MODE (i))
+ > reg_class_size[class]
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+ || (in_inc_dec[i] && forbidden_inc_dec_class[class])
+#endif
+ )
+ ;
+ else if (p->cost[class] < best_cost)
+ {
+ best_cost = p->cost[class];
+ best = (enum reg_class) class;
+ }
+ else if (p->cost[class] == best_cost)
+ best = reg_class_subunion[(int)best][class];
+ }
+
+ /* Record the alternate register class; i.e., a class for which
+ every register in it is better than using memory. If adding a
+ class would make a smaller class (i.e., no union of just those
+ classes exists), skip that class. The major unions of classes
+ should be provided as a register class. Don't do this if we
+ will be doing it again later. */
+
+ if (pass == 1 || ! flag_expensive_optimizations)
+ for (class = 0; class < N_REG_CLASSES; class++)
+ if (p->cost[class] < p->mem_cost
+ && (reg_class_size[(int) reg_class_subunion[(int) alt][class]]
+ > reg_class_size[(int) alt])
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+ && ! (in_inc_dec[i] && forbidden_inc_dec_class[class])
+#endif
+ )
+ alt = reg_class_subunion[(int) alt][class];
+
+ /* If we don't add any classes, nothing to try. */
+ if (alt == best)
+ alt = (int) NO_REGS;
+
+ /* We cast to (int) because (char) hits bugs in some compilers. */
+ prefclass[i] = (int) best;
+ altclass[i] = (int) alt;
+ }
+ }
+#endif /* REGISTER_CONSTRAINTS */
+}
+
+#ifdef REGISTER_CONSTRAINTS
+
+/* Record the cost of using memory or registers of various classes for
+ the operands in INSN.
+
+ N_ALTS is the number of alternatives.
+
+ N_OPS is the number of operands.
+
+ OPS is an array of the operands.
+
+ MODES are the modes of the operands, in case any are VOIDmode.
+
+ CONSTRAINTS are the constraints to use for the operands. This array
+ is modified by this procedure.
+
+ This procedure works alternative by alternative. For each alternative
+ we assume that we will be able to allocate all pseudos to their ideal
+ register class and calculate the cost of using that alternative. Then
+ we compute for each operand that is a pseudo-register, the cost of
+ having the pseudo allocated to each register class and using it in that
+ alternative. To this cost is added the cost of the alternative.
+
+ The cost of each class for this insn is its lowest cost among all the
+ alternatives. */
+
+static void
+record_reg_classes (n_alts, n_ops, ops, modes, constraints, insn)
+ int n_alts;
+ int n_ops;
+ rtx *ops;
+ enum machine_mode *modes;
+ char **constraints;
+ rtx insn;
+{
+ int alt;
+ enum op_type {OP_READ, OP_WRITE, OP_READ_WRITE} op_types[MAX_RECOG_OPERANDS];
+ int i, j;
+
+ /* By default, each operand is an input operand. */
+
+ for (i = 0; i < n_ops; i++)
+ op_types[i] = OP_READ;
+
+ /* Process each alternative, each time minimizing an operand's cost with
+ the cost for each operand in that alternative. */
+
+ for (alt = 0; alt < n_alts; alt++)
+ {
+ struct costs this_op_costs[MAX_RECOG_OPERANDS];
+ int alt_fail = 0;
+ int alt_cost = 0;
+ enum reg_class classes[MAX_RECOG_OPERANDS];
+ int class;
+
+ for (i = 0; i < n_ops; i++)
+ {
+ char *p = constraints[i];
+ rtx op = ops[i];
+ enum machine_mode mode = modes[i];
+ int allows_mem = 0;
+ int win = 0;
+ char c;
+
+ /* If this operand has no constraints at all, we can conclude
+ nothing about it since anything is valid. */
+
+ if (*p == 0)
+ {
+ if (GET_CODE (op) == REG && REGNO (op) >= FIRST_PSEUDO_REGISTER)
+ bzero ((char *) &this_op_costs[i], sizeof this_op_costs[i]);
+
+ continue;
+ }
+
+ if (*p == '%')
+ p++;
+
+ /* If this alternative is only relevant when this operand
+ matches a previous operand, we do different things depending
+ on whether this operand is a pseudo-reg or not. */
+
+ if (p[0] >= '0' && p[0] <= '0' + i && (p[1] == ',' || p[1] == 0))
+ {
+ j = p[0] - '0';
+ classes[i] = classes[j];
+
+ if (GET_CODE (op) != REG || REGNO (op) < FIRST_PSEUDO_REGISTER)
+ {
+ /* If this matches the other operand, we have no added
+ cost and we win. */
+ if (rtx_equal_p (ops[j], op))
+ win = 1;
+
+ /* If we can put the other operand into a register, add to
+ the cost of this alternative the cost to copy this
+ operand to the register used for the other operand. */
+
+ else if (classes[j] != NO_REGS)
+ alt_cost += copy_cost (op, mode, classes[j], 1), win = 1;
+ }
+ else if (GET_CODE (ops[j]) != REG
+ || REGNO (ops[j]) < FIRST_PSEUDO_REGISTER)
+ {
+ /* This op is a pseudo but the one it matches is not. */
+
+ /* If we can't put the other operand into a register, this
+ alternative can't be used. */
+
+ if (classes[j] == NO_REGS)
+ alt_fail = 1;
+
+ /* Otherwise, add to the cost of this alternative the cost
+ to copy the other operand to the register used for this
+ operand. */
+
+ else
+ alt_cost += copy_cost (ops[j], mode, classes[j], 1);
+ }
+ else
+ {
+ /* The costs of this operand are the same as that of the
+ other operand. However, if we cannot tie them, this
+ alternative needs to do a copy, which is one
+ instruction. */
+
+ this_op_costs[i] = this_op_costs[j];
+ if (REGNO (ops[i]) != REGNO (ops[j])
+ && ! find_reg_note (insn, REG_DEAD, op))
+ alt_cost += 2;
+
+ /* This is in place of ordinary cost computation
+ for this operand, so skip to the end of the
+ alternative (should be just one character). */
+ while (*p && *p++ != ',')
+ ;
+
+ constraints[i] = p;
+ continue;
+ }
+ }
+
+ /* Scan all the constraint letters. See if the operand matches
+ any of the constraints. Collect the valid register classes
+ and see if this operand accepts memory. */
+
+ classes[i] = NO_REGS;
+ while (*p && (c = *p++) != ',')
+ switch (c)
+ {
+ case '=':
+ op_types[i] = OP_WRITE;
+ break;
+
+ case '+':
+ op_types[i] = OP_READ_WRITE;
+ break;
+
+ case '*':
+ /* Ignore the next letter for this pass. */
+ p++;
+ break;
+
+ case '%':
+ case '?': case '!': case '#':
+ case '&':
+ case '0': case '1': case '2': case '3': case '4':
+ case 'p':
+ break;
+
+ case 'm': case 'o': case 'V':
+ /* It doesn't seem worth distinguishing between offsettable
+ and non-offsettable addresses here. */
+ allows_mem = 1;
+ if (GET_CODE (op) == MEM)
+ win = 1;
+ break;
+
+ case '<':
+ if (GET_CODE (op) == MEM
+ && (GET_CODE (XEXP (op, 0)) == PRE_DEC
+ || GET_CODE (XEXP (op, 0)) == POST_DEC))
+ win = 1;
+ break;
+
+ case '>':
+ if (GET_CODE (op) == MEM
+ && (GET_CODE (XEXP (op, 0)) == PRE_INC
+ || GET_CODE (XEXP (op, 0)) == POST_INC))
+ win = 1;
+ break;
+
+ case 'E':
+ /* Match any floating double constant, but only if
+ we can examine the bits of it reliably. */
+ if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
+ || HOST_BITS_PER_WIDE_INT != BITS_PER_WORD)
+ && GET_MODE (op) != VOIDmode && ! flag_pretend_float)
+ break;
+ if (GET_CODE (op) == CONST_DOUBLE)
+ win = 1;
+ break;
+
+ case 'F':
+ if (GET_CODE (op) == CONST_DOUBLE)
+ win = 1;
+ break;
+
+ case 'G':
+ case 'H':
+ if (GET_CODE (op) == CONST_DOUBLE
+ && CONST_DOUBLE_OK_FOR_LETTER_P (op, c))
+ win = 1;
+ break;
+
+ case 's':
+ if (GET_CODE (op) == CONST_INT
+ || (GET_CODE (op) == CONST_DOUBLE
+ && GET_MODE (op) == VOIDmode))
+ break;
+ case 'i':
+ if (CONSTANT_P (op)
+#ifdef LEGITIMATE_PIC_OPERAND_P
+ && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op))
+#endif
+ )
+ win = 1;
+ break;
+
+ case 'n':
+ if (GET_CODE (op) == CONST_INT
+ || (GET_CODE (op) == CONST_DOUBLE
+ && GET_MODE (op) == VOIDmode))
+ win = 1;
+ break;
+
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ if (GET_CODE (op) == CONST_INT
+ && CONST_OK_FOR_LETTER_P (INTVAL (op), c))
+ win = 1;
+ break;
+
+ case 'X':
+ win = 1;
+ break;
+
+#ifdef EXTRA_CONSTRAINT
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ if (EXTRA_CONSTRAINT (op, c))
+ win = 1;
+ break;
+#endif
+
+ case 'g':
+ if (GET_CODE (op) == MEM
+ || (CONSTANT_P (op)
+#ifdef LEGITIMATE_PIC_OPERAND_P
+ && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op))
+#endif
+ ))
+ win = 1;
+ allows_mem = 1;
+ case 'r':
+ classes[i]
+ = reg_class_subunion[(int) classes[i]][(int) GENERAL_REGS];
+ break;
+
+ default:
+ classes[i]
+ = reg_class_subunion[(int) classes[i]]
+ [(int) REG_CLASS_FROM_LETTER (c)];
+ }
+
+ constraints[i] = p;
+
+ /* How we account for this operand now depends on whether it is a
+ pseudo register or not. If it is, we first check if any
+ register classes are valid. If not, we ignore this alternative,
+ since we want to assume that all pseudos get allocated for
+ register preferencing. If some register class is valid, compute
+ the costs of moving the pseudo into that class. */
+
+ if (GET_CODE (op) == REG && REGNO (op) >= FIRST_PSEUDO_REGISTER)
+ {
+ if (classes[i] == NO_REGS)
+ alt_fail = 1;
+ else
+ {
+ struct costs *pp = &this_op_costs[i];
+
+ for (class = 0; class < N_REG_CLASSES; class++)
+ pp->cost[class] = may_move_cost[class][(int) classes[i]];
+
+ /* If the alternative actually allows memory, make things
+ a bit cheaper since we won't need an extra insn to
+ load it. */
+
+ pp->mem_cost = MEMORY_MOVE_COST (mode) - allows_mem;
+
+ /* If we have assigned a class to this register in our
+ first pass, add a cost to this alternative corresponding
+ to what we would add if this register were not in the
+ appropriate class. */
+
+ if (prefclass)
+ alt_cost
+ += may_move_cost[prefclass[REGNO (op)]][(int) classes[i]];
+ }
+ }
+
+ /* Otherwise, if this alternative wins, either because we
+ have already determined that or if we have a hard register of
+ the proper class, there is no cost for this alternative. */
+
+ else if (win
+ || (GET_CODE (op) == REG
+ && reg_fits_class_p (op, classes[i], 0, GET_MODE (op))))
+ ;
+
+ /* If registers are valid, the cost of this alternative includes
+ copying the object to and/or from a register. */
+
+ else if (classes[i] != NO_REGS)
+ {
+ if (op_types[i] != OP_WRITE)
+ alt_cost += copy_cost (op, mode, classes[i], 1);
+
+ if (op_types[i] != OP_READ)
+ alt_cost += copy_cost (op, mode, classes[i], 0);
+ }
+
+ /* The only other way this alternative can be used is if this is a
+ constant that could be placed into memory. */
+
+ else if (CONSTANT_P (op) && allows_mem)
+ alt_cost += MEMORY_MOVE_COST (mode);
+ else
+ alt_fail = 1;
+ }
+
+ if (alt_fail)
+ continue;
+
+ /* Finally, update the costs with the information we've calculated
+ about this alternative. */
+
+ for (i = 0; i < n_ops; i++)
+ if (GET_CODE (ops[i]) == REG
+ && REGNO (ops[i]) >= FIRST_PSEUDO_REGISTER)
+ {
+ struct costs *pp = &op_costs[i], *qq = &this_op_costs[i];
+ int scale = 1 + (op_types[i] == OP_READ_WRITE);
+
+ pp->mem_cost = MIN (pp->mem_cost,
+ (qq->mem_cost + alt_cost) * scale);
+
+ for (class = 0; class < N_REG_CLASSES; class++)
+ pp->cost[class] = MIN (pp->cost[class],
+ (qq->cost[class] + alt_cost) * scale);
+ }
+ }
+}
+
+/* Compute the cost of loading X into (if TO_P is non-zero) or from (if
+ TO_P is zero) a register of class CLASS in mode MODE.
+
+ X must not be a pseudo. */
+
+static int
+copy_cost (x, mode, class, to_p)
+ rtx x;
+ enum machine_mode mode;
+ enum reg_class class;
+ int to_p;
+{
+ enum reg_class secondary_class = NO_REGS;
+
+ /* If X is a SCRATCH, there is actually nothing to move since we are
+ assuming optimal allocation. */
+
+ if (GET_CODE (x) == SCRATCH)
+ return 0;
+
+ /* Get the class we will actually use for a reload. */
+ class = PREFERRED_RELOAD_CLASS (x, class);
+
+#ifdef HAVE_SECONDARY_RELOADS
+ /* If we need a secondary reload (we assume here that we are using
+ the secondary reload as an intermediate, not a scratch register), the
+ cost is that to load the input into the intermediate register, then
+ to copy them. We use a special value of TO_P to avoid recursion. */
+
+#ifdef SECONDARY_INPUT_RELOAD_CLASS
+ if (to_p == 1)
+ secondary_class = SECONDARY_INPUT_RELOAD_CLASS (class, mode, x);
+#endif
+
+#ifdef SECONDARY_OUTPUT_RELOAD_CLASS
+ if (! to_p)
+ secondary_class = SECONDARY_OUTPUT_RELOAD_CLASS (class, mode, x);
+#endif
+
+ if (secondary_class != NO_REGS)
+ return (move_cost[(int) secondary_class][(int) class]
+ + copy_cost (x, mode, secondary_class, 2));
+#endif /* HAVE_SECONDARY_RELOADS */
+
+ /* For memory, use the memory move cost, for (hard) registers, use the
+ cost to move between the register classes, and use 2 for everything
+ else (constants). */
+
+ if (GET_CODE (x) == MEM || class == NO_REGS)
+ return MEMORY_MOVE_COST (mode);
+
+ else if (GET_CODE (x) == REG)
+ return move_cost[(int) REGNO_REG_CLASS (REGNO (x))][(int) class];
+
+ else
+ /* If this is a constant, we may eventually want to call rtx_cost here. */
+ return 2;
+}
+
+/* Record the pseudo registers we must reload into hard registers
+ in a subexpression of a memory address, X.
+
+ CLASS is the class that the register needs to be in and is either
+ BASE_REG_CLASS or INDEX_REG_CLASS.
+
+ SCALE is twice the amount to multiply the cost by (it is twice so we
+ can represent half-cost adjustments). */
+
+static void
+record_address_regs (x, class, scale)
+ rtx x;
+ enum reg_class class;
+ int scale;
+{
+ register enum rtx_code code = GET_CODE (x);
+
+ switch (code)
+ {
+ case CONST_INT:
+ case CONST:
+ case CC0:
+ case PC:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return;
+
+ case PLUS:
+ /* When we have an address that is a sum,
+ we must determine whether registers are "base" or "index" regs.
+ If there is a sum of two registers, we must choose one to be
+ the "base". Luckily, we can use the REGNO_POINTER_FLAG
+ to make a good choice most of the time. We only need to do this
+ on machines that can have two registers in an address and where
+ the base and index register classes are different.
+
+ ??? This code used to set REGNO_POINTER_FLAG in some cases, but
+ that seems bogus since it should only be set when we are sure
+ the register is being used as a pointer. */
+
+ {
+ rtx arg0 = XEXP (x, 0);
+ rtx arg1 = XEXP (x, 1);
+ register enum rtx_code code0 = GET_CODE (arg0);
+ register enum rtx_code code1 = GET_CODE (arg1);
+
+ /* Look inside subregs. */
+ if (code0 == SUBREG)
+ arg0 = SUBREG_REG (arg0), code0 = GET_CODE (arg0);
+ if (code1 == SUBREG)
+ arg1 = SUBREG_REG (arg1), code1 = GET_CODE (arg1);
+
+ /* If this machine only allows one register per address, it must
+ be in the first operand. */
+
+ if (MAX_REGS_PER_ADDRESS == 1)
+ record_address_regs (arg0, class, scale);
+
+ /* If index and base registers are the same on this machine, just
+ record registers in any non-constant operands. We assume here,
+ as well as in the tests below, that all addresses are in
+ canonical form. */
+
+ else if (INDEX_REG_CLASS == BASE_REG_CLASS)
+ {
+ record_address_regs (arg0, class, scale);
+ if (! CONSTANT_P (arg1))
+ record_address_regs (arg1, class, scale);
+ }
+
+ /* If the second operand is a constant integer, it doesn't change
+ what class the first operand must be. */
+
+ else if (code1 == CONST_INT || code1 == CONST_DOUBLE)
+ record_address_regs (arg0, class, scale);
+
+ /* If the second operand is a symbolic constant, the first operand
+ must be an index register. */
+
+ else if (code1 == SYMBOL_REF || code1 == CONST || code1 == LABEL_REF)
+ record_address_regs (arg0, INDEX_REG_CLASS, scale);
+
+ /* If this the sum of two registers where the first is known to be a
+ pointer, it must be a base register with the second an index. */
+
+ else if (code0 == REG && code1 == REG
+ && REGNO_POINTER_FLAG (REGNO (arg0)))
+ {
+ record_address_regs (arg0, BASE_REG_CLASS, scale);
+ record_address_regs (arg1, INDEX_REG_CLASS, scale);
+ }
+
+ /* If this is the sum of two registers and neither is known to
+ be a pointer, count equal chances that each might be a base
+ or index register. This case should be rare. */
+
+ else if (code0 == REG && code1 == REG
+ && ! REGNO_POINTER_FLAG (REGNO (arg0))
+ && ! REGNO_POINTER_FLAG (REGNO (arg1)))
+ {
+ record_address_regs (arg0, BASE_REG_CLASS, scale / 2);
+ record_address_regs (arg0, INDEX_REG_CLASS, scale / 2);
+ record_address_regs (arg1, BASE_REG_CLASS, scale / 2);
+ record_address_regs (arg1, INDEX_REG_CLASS, scale / 2);
+ }
+
+ /* In all other cases, the first operand is an index and the
+ second is the base. */
+
+ else
+ {
+ record_address_regs (arg0, INDEX_REG_CLASS, scale);
+ record_address_regs (arg1, BASE_REG_CLASS, scale);
+ }
+ }
+ break;
+
+ case POST_INC:
+ case PRE_INC:
+ case POST_DEC:
+ case PRE_DEC:
+ /* Double the importance of a pseudo register that is incremented
+ or decremented, since it would take two extra insns
+ if it ends up in the wrong place. If the operand is a pseudo,
+ show it is being used in an INC_DEC context. */
+
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+ if (GET_CODE (XEXP (x, 0)) == REG
+ && REGNO (XEXP (x, 0)) >= FIRST_PSEUDO_REGISTER)
+ in_inc_dec[REGNO (XEXP (x, 0))] = 1;
+#endif
+
+ record_address_regs (XEXP (x, 0), class, 2 * scale);
+ break;
+
+ case REG:
+ {
+ register struct costs *pp = &costs[REGNO (x)];
+ register int i;
+
+ pp->mem_cost += (MEMORY_MOVE_COST (Pmode) * scale) / 2;
+
+ for (i = 0; i < N_REG_CLASSES; i++)
+ pp->cost[i] += (may_move_cost[i][(int) class] * scale) / 2;
+ }
+ break;
+
+ default:
+ {
+ register char *fmt = GET_RTX_FORMAT (code);
+ register int i;
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ if (fmt[i] == 'e')
+ record_address_regs (XEXP (x, i), class, scale);
+ }
+ }
+}
+
+#ifdef FORBIDDEN_INC_DEC_CLASSES
+
+/* Return 1 if REG is valid as an auto-increment memory reference
+ to an object of MODE. */
+
+static
+auto_inc_dec_reg_p (reg, mode)
+ rtx reg;
+ enum machine_mode mode;
+{
+#ifdef HAVE_POST_INCREMENT
+ if (memory_address_p (mode, gen_rtx (POST_INC, Pmode, reg)))
+ return 1;
+#endif
+
+#ifdef HAVE_POST_DECREMENT
+ if (memory_address_p (mode, gen_rtx (POST_DEC, Pmode, reg)))
+ return 1;
+#endif
+
+#ifdef HAVE_PRE_INCREMENT
+ if (memory_address_p (mode, gen_rtx (PRE_INC, Pmode, reg)))
+ return 1;
+#endif
+
+#ifdef HAVE_PRE_DECREMENT
+ if (memory_address_p (mode, gen_rtx (PRE_DEC, Pmode, reg)))
+ return 1;
+#endif
+
+ return 0;
+}
+#endif
+
+#endif /* REGISTER_CONSTRAINTS */
+
+/* This is the `regscan' pass of the compiler, run just before cse
+ and again just before loop.
+
+ It finds the first and last use of each pseudo-register
+ and records them in the vectors regno_first_uid, regno_last_uid
+ and counts the number of sets in the vector reg_n_sets.
+
+ REPEAT is nonzero the second time this is called. */
+
+/* Indexed by pseudo register number, gives uid of first insn using the reg
+ (as of the time reg_scan is called). */
+
+int *regno_first_uid;
+
+/* Indexed by pseudo register number, gives uid of last insn using the reg
+ (as of the time reg_scan is called). */
+
+int *regno_last_uid;
+
+/* Indexed by pseudo register number, gives uid of last insn using the reg
+ or mentioning it in a note (as of the time reg_scan is called). */
+
+int *regno_last_note_uid;
+
+/* Record the number of registers we used when we allocated the above two
+ tables. If we are called again with more than this, we must re-allocate
+ the tables. */
+
+static int highest_regno_in_uid_map;
+
+/* Maximum number of parallel sets and clobbers in any insn in this fn.
+ Always at least 3, since the combiner could put that many togetherm
+ and we want this to remain correct for all the remaining passes. */
+
+int max_parallel;
+
+void
+reg_scan (f, nregs, repeat)
+ rtx f;
+ int nregs;
+ int repeat;
+{
+ register rtx insn;
+
+ if (!repeat || nregs > highest_regno_in_uid_map)
+ {
+ /* Leave some spare space in case more regs are allocated. */
+ highest_regno_in_uid_map = nregs + nregs / 20;
+ regno_first_uid
+ = (int *) oballoc (highest_regno_in_uid_map * sizeof (int));
+ regno_last_uid
+ = (int *) oballoc (highest_regno_in_uid_map * sizeof (int));
+ regno_last_note_uid
+ = (int *) oballoc (highest_regno_in_uid_map * sizeof (int));
+ reg_n_sets
+ = (short *) oballoc (highest_regno_in_uid_map * sizeof (short));
+ }
+
+ bzero ((char *) regno_first_uid, highest_regno_in_uid_map * sizeof (int));
+ bzero ((char *) regno_last_uid, highest_regno_in_uid_map * sizeof (int));
+ bzero ((char *) regno_last_note_uid,
+ highest_regno_in_uid_map * sizeof (int));
+ bzero ((char *) reg_n_sets, highest_regno_in_uid_map * sizeof (short));
+
+ max_parallel = 3;
+
+ for (insn = f; insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == INSN
+ || GET_CODE (insn) == CALL_INSN
+ || GET_CODE (insn) == JUMP_INSN)
+ {
+ if (GET_CODE (PATTERN (insn)) == PARALLEL
+ && XVECLEN (PATTERN (insn), 0) > max_parallel)
+ max_parallel = XVECLEN (PATTERN (insn), 0);
+ reg_scan_mark_refs (PATTERN (insn), insn, 0);
+
+ if (REG_NOTES (insn))
+ reg_scan_mark_refs (REG_NOTES (insn), insn, 1);
+ }
+}
+
+/* X is the expression to scan. INSN is the insn it appears in.
+ NOTE_FLAG is nonzero if X is from INSN's notes rather than its body. */
+
+static void
+reg_scan_mark_refs (x, insn, note_flag)
+ rtx x;
+ rtx insn;
+ int note_flag;
+{
+ register enum rtx_code code = GET_CODE (x);
+ register rtx dest;
+ register rtx note;
+
+ switch (code)
+ {
+ case CONST_INT:
+ case CONST:
+ case CONST_DOUBLE:
+ case CC0:
+ case PC:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ return;
+
+ case REG:
+ {
+ register int regno = REGNO (x);
+
+ regno_last_note_uid[regno] = INSN_UID (insn);
+ if (!note_flag)
+ regno_last_uid[regno] = INSN_UID (insn);
+ if (regno_first_uid[regno] == 0)
+ regno_first_uid[regno] = INSN_UID (insn);
+ }
+ break;
+
+ case EXPR_LIST:
+ if (XEXP (x, 0))
+ reg_scan_mark_refs (XEXP (x, 0), insn, note_flag);
+ if (XEXP (x, 1))
+ reg_scan_mark_refs (XEXP (x, 1), insn, note_flag);
+ break;
+
+ case INSN_LIST:
+ if (XEXP (x, 1))
+ reg_scan_mark_refs (XEXP (x, 1), insn, note_flag);
+ break;
+
+ case SET:
+ /* Count a set of the destination if it is a register. */
+ for (dest = SET_DEST (x);
+ GET_CODE (dest) == SUBREG || GET_CODE (dest) == STRICT_LOW_PART
+ || GET_CODE (dest) == ZERO_EXTEND;
+ dest = XEXP (dest, 0))
+ ;
+
+ if (GET_CODE (dest) == REG)
+ reg_n_sets[REGNO (dest)]++;
+
+ /* If this is setting a pseudo from another pseudo or the sum of a
+ pseudo and a constant integer and the other pseudo is known to be
+ a pointer, set the destination to be a pointer as well.
+
+ Likewise if it is setting the destination from an address or from a
+ value equivalent to an address or to the sum of an address and
+ something else.
+
+ But don't do any of this if the pseudo corresponds to a user
+ variable since it should have already been set as a pointer based
+ on the type. */
+
+ if (GET_CODE (SET_DEST (x)) == REG
+ && REGNO (SET_DEST (x)) >= FIRST_PSEUDO_REGISTER
+ && ! REG_USERVAR_P (SET_DEST (x))
+ && ! REGNO_POINTER_FLAG (REGNO (SET_DEST (x)))
+ && ((GET_CODE (SET_SRC (x)) == REG
+ && REGNO_POINTER_FLAG (REGNO (SET_SRC (x))))
+ || ((GET_CODE (SET_SRC (x)) == PLUS
+ || GET_CODE (SET_SRC (x)) == LO_SUM)
+ && GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT
+ && GET_CODE (XEXP (SET_SRC (x), 0)) == REG
+ && REGNO_POINTER_FLAG (REGNO (XEXP (SET_SRC (x), 0))))
+ || GET_CODE (SET_SRC (x)) == CONST
+ || GET_CODE (SET_SRC (x)) == SYMBOL_REF
+ || GET_CODE (SET_SRC (x)) == LABEL_REF
+ || (GET_CODE (SET_SRC (x)) == HIGH
+ && (GET_CODE (XEXP (SET_SRC (x), 0)) == CONST
+ || GET_CODE (XEXP (SET_SRC (x), 0)) == SYMBOL_REF
+ || GET_CODE (XEXP (SET_SRC (x), 0)) == LABEL_REF))
+ || ((GET_CODE (SET_SRC (x)) == PLUS
+ || GET_CODE (SET_SRC (x)) == LO_SUM)
+ && (GET_CODE (XEXP (SET_SRC (x), 1)) == CONST
+ || GET_CODE (XEXP (SET_SRC (x), 1)) == SYMBOL_REF
+ || GET_CODE (XEXP (SET_SRC (x), 1)) == LABEL_REF))
+ || ((note = find_reg_note (insn, REG_EQUAL, 0)) != 0
+ && (GET_CODE (XEXP (note, 0)) == CONST
+ || GET_CODE (XEXP (note, 0)) == SYMBOL_REF
+ || GET_CODE (XEXP (note, 0)) == LABEL_REF))))
+ REGNO_POINTER_FLAG (REGNO (SET_DEST (x))) = 1;
+
+ /* ... fall through ... */
+
+ default:
+ {
+ register char *fmt = GET_RTX_FORMAT (code);
+ register int i;
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ reg_scan_mark_refs (XEXP (x, i), insn, note_flag);
+ else if (fmt[i] == 'E' && XVEC (x, i) != 0)
+ {
+ register int j;
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ reg_scan_mark_refs (XVECEXP (x, i, j), insn, note_flag);
+ }
+ }
+ }
+ }
+}
+
+/* Return nonzero if C1 is a subset of C2, i.e., if every register in C1
+ is also in C2. */
+
+int
+reg_class_subset_p (c1, c2)
+ register enum reg_class c1;
+ register enum reg_class c2;
+{
+ if (c1 == c2) return 1;
+
+ if (c2 == ALL_REGS)
+ win:
+ return 1;
+ GO_IF_HARD_REG_SUBSET (reg_class_contents[(int)c1],
+ reg_class_contents[(int)c2],
+ win);
+ return 0;
+}
+
+/* Return nonzero if there is a register that is in both C1 and C2. */
+
+int
+reg_classes_intersect_p (c1, c2)
+ register enum reg_class c1;
+ register enum reg_class c2;
+{
+#ifdef HARD_REG_SET
+ register
+#endif
+ HARD_REG_SET c;
+
+ if (c1 == c2) return 1;
+
+ if (c1 == ALL_REGS || c2 == ALL_REGS)
+ return 1;
+
+ COPY_HARD_REG_SET (c, reg_class_contents[(int) c1]);
+ AND_HARD_REG_SET (c, reg_class_contents[(int) c2]);
+
+ GO_IF_HARD_REG_SUBSET (c, reg_class_contents[(int) NO_REGS], lose);
+ return 1;
+
+ lose:
+ return 0;
+}
+
diff --git a/gnu/usr.bin/cc/cc_int/reload.c b/gnu/usr.bin/cc/cc_int/reload.c
new file mode 100644
index 0000000..b9a1c27
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/reload.c
@@ -0,0 +1,5650 @@
+/* Search an insn for pseudo regs that must be in hard regs and are not.
+ Copyright (C) 1987, 88, 89, 92, 93, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file contains subroutines used only from the file reload1.c.
+ It knows how to scan one insn for operands and values
+ that need to be copied into registers to make valid code.
+ It also finds other operands and values which are valid
+ but for which equivalent values in registers exist and
+ ought to be used instead.
+
+ Before processing the first insn of the function, call `init_reload'.
+
+ To scan an insn, call `find_reloads'. This does two things:
+ 1. sets up tables describing which values must be reloaded
+ for this insn, and what kind of hard regs they must be reloaded into;
+ 2. optionally record the locations where those values appear in
+ the data, so they can be replaced properly later.
+ This is done only if the second arg to `find_reloads' is nonzero.
+
+ The third arg to `find_reloads' specifies the number of levels
+ of indirect addressing supported by the machine. If it is zero,
+ indirect addressing is not valid. If it is one, (MEM (REG n))
+ is valid even if (REG n) did not get a hard register; if it is two,
+ (MEM (MEM (REG n))) is also valid even if (REG n) did not get a
+ hard register, and similarly for higher values.
+
+ Then you must choose the hard regs to reload those pseudo regs into,
+ and generate appropriate load insns before this insn and perhaps
+ also store insns after this insn. Set up the array `reload_reg_rtx'
+ to contain the REG rtx's for the registers you used. In some
+ cases `find_reloads' will return a nonzero value in `reload_reg_rtx'
+ for certain reloads. Then that tells you which register to use,
+ so you do not need to allocate one. But you still do need to add extra
+ instructions to copy the value into and out of that register.
+
+ Finally you must call `subst_reloads' to substitute the reload reg rtx's
+ into the locations already recorded.
+
+NOTE SIDE EFFECTS:
+
+ find_reloads can alter the operands of the instruction it is called on.
+
+ 1. Two operands of any sort may be interchanged, if they are in a
+ commutative instruction.
+ This happens only if find_reloads thinks the instruction will compile
+ better that way.
+
+ 2. Pseudo-registers that are equivalent to constants are replaced
+ with those constants if they are not in hard registers.
+
+1 happens every time find_reloads is called.
+2 happens only when REPLACE is 1, which is only when
+actually doing the reloads, not when just counting them.
+
+
+Using a reload register for several reloads in one insn:
+
+When an insn has reloads, it is considered as having three parts:
+the input reloads, the insn itself after reloading, and the output reloads.
+Reloads of values used in memory addresses are often needed for only one part.
+
+When this is so, reload_when_needed records which part needs the reload.
+Two reloads for different parts of the insn can share the same reload
+register.
+
+When a reload is used for addresses in multiple parts, or when it is
+an ordinary operand, it is classified as RELOAD_OTHER, and cannot share
+a register with any other reload. */
+
+#define REG_OK_STRICT
+
+#include "config.h"
+#include "rtl.h"
+#include "insn-config.h"
+#include "insn-codes.h"
+#include "recog.h"
+#include "reload.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "flags.h"
+#include "real.h"
+
+#ifndef REGISTER_MOVE_COST
+#define REGISTER_MOVE_COST(x, y) 2
+#endif
+
+/* The variables set up by `find_reloads' are:
+
+ n_reloads number of distinct reloads needed; max reload # + 1
+ tables indexed by reload number
+ reload_in rtx for value to reload from
+ reload_out rtx for where to store reload-reg afterward if nec
+ (often the same as reload_in)
+ reload_reg_class enum reg_class, saying what regs to reload into
+ reload_inmode enum machine_mode; mode this operand should have
+ when reloaded, on input.
+ reload_outmode enum machine_mode; mode this operand should have
+ when reloaded, on output.
+ reload_optional char, nonzero for an optional reload.
+ Optional reloads are ignored unless the
+ value is already sitting in a register.
+ reload_inc int, positive amount to increment or decrement by if
+ reload_in is a PRE_DEC, PRE_INC, POST_DEC, POST_INC.
+ Ignored otherwise (don't assume it is zero).
+ reload_in_reg rtx. A reg for which reload_in is the equivalent.
+ If reload_in is a symbol_ref which came from
+ reg_equiv_constant, then this is the pseudo
+ which has that symbol_ref as equivalent.
+ reload_reg_rtx rtx. This is the register to reload into.
+ If it is zero when `find_reloads' returns,
+ you must find a suitable register in the class
+ specified by reload_reg_class, and store here
+ an rtx for that register with mode from
+ reload_inmode or reload_outmode.
+ reload_nocombine char, nonzero if this reload shouldn't be
+ combined with another reload.
+ reload_opnum int, operand number being reloaded. This is
+ used to group related reloads and need not always
+ be equal to the actual operand number in the insn,
+ though it current will be; for in-out operands, it
+ is one of the two operand numbers.
+ reload_when_needed enum, classifies reload as needed either for
+ addressing an input reload, addressing an output,
+ for addressing a non-reloaded mem ref,
+ or for unspecified purposes (i.e., more than one
+ of the above).
+ reload_secondary_p int, 1 if this is a secondary register for one
+ or more reloads.
+ reload_secondary_in_reload
+ reload_secondary_out_reload
+ int, gives the reload number of a secondary
+ reload, when needed; otherwise -1
+ reload_secondary_in_icode
+ reload_secondary_out_icode
+ enum insn_code, if a secondary reload is required,
+ gives the INSN_CODE that uses the secondary
+ reload as a scratch register, or CODE_FOR_nothing
+ if the secondary reload register is to be an
+ intermediate register. */
+int n_reloads;
+
+rtx reload_in[MAX_RELOADS];
+rtx reload_out[MAX_RELOADS];
+enum reg_class reload_reg_class[MAX_RELOADS];
+enum machine_mode reload_inmode[MAX_RELOADS];
+enum machine_mode reload_outmode[MAX_RELOADS];
+rtx reload_reg_rtx[MAX_RELOADS];
+char reload_optional[MAX_RELOADS];
+int reload_inc[MAX_RELOADS];
+rtx reload_in_reg[MAX_RELOADS];
+char reload_nocombine[MAX_RELOADS];
+int reload_opnum[MAX_RELOADS];
+enum reload_type reload_when_needed[MAX_RELOADS];
+int reload_secondary_p[MAX_RELOADS];
+int reload_secondary_in_reload[MAX_RELOADS];
+int reload_secondary_out_reload[MAX_RELOADS];
+enum insn_code reload_secondary_in_icode[MAX_RELOADS];
+enum insn_code reload_secondary_out_icode[MAX_RELOADS];
+
+/* All the "earlyclobber" operands of the current insn
+ are recorded here. */
+int n_earlyclobbers;
+rtx reload_earlyclobbers[MAX_RECOG_OPERANDS];
+
+int reload_n_operands;
+
+/* Replacing reloads.
+
+ If `replace_reloads' is nonzero, then as each reload is recorded
+ an entry is made for it in the table `replacements'.
+ Then later `subst_reloads' can look through that table and
+ perform all the replacements needed. */
+
+/* Nonzero means record the places to replace. */
+static int replace_reloads;
+
+/* Each replacement is recorded with a structure like this. */
+struct replacement
+{
+ rtx *where; /* Location to store in */
+ rtx *subreg_loc; /* Location of SUBREG if WHERE is inside
+ a SUBREG; 0 otherwise. */
+ int what; /* which reload this is for */
+ enum machine_mode mode; /* mode it must have */
+};
+
+static struct replacement replacements[MAX_RECOG_OPERANDS * ((MAX_REGS_PER_ADDRESS * 2) + 1)];
+
+/* Number of replacements currently recorded. */
+static int n_replacements;
+
+/* Used to track what is modified by an operand. */
+struct decomposition
+{
+ int reg_flag; /* Nonzero if referencing a register. */
+ int safe; /* Nonzero if this can't conflict with anything. */
+ rtx base; /* Base adddress for MEM. */
+ HOST_WIDE_INT start; /* Starting offset or register number. */
+ HOST_WIDE_INT end; /* Endinf offset or register number. */
+};
+
+/* MEM-rtx's created for pseudo-regs in stack slots not directly addressable;
+ (see reg_equiv_address). */
+static rtx memlocs[MAX_RECOG_OPERANDS * ((MAX_REGS_PER_ADDRESS * 2) + 1)];
+static int n_memlocs;
+
+#ifdef SECONDARY_MEMORY_NEEDED
+
+/* Save MEMs needed to copy from one class of registers to another. One MEM
+ is used per mode, but normally only one or two modes are ever used.
+
+ We keep two versions, before and after register elimination. The one
+ after register elimination is record separately for each operand. This
+ is done in case the address is not valid to be sure that we separately
+ reload each. */
+
+static rtx secondary_memlocs[NUM_MACHINE_MODES];
+static rtx secondary_memlocs_elim[NUM_MACHINE_MODES][MAX_RECOG_OPERANDS];
+#endif
+
+/* The instruction we are doing reloads for;
+ so we can test whether a register dies in it. */
+static rtx this_insn;
+
+/* Nonzero if this instruction is a user-specified asm with operands. */
+static int this_insn_is_asm;
+
+/* If hard_regs_live_known is nonzero,
+ we can tell which hard regs are currently live,
+ at least enough to succeed in choosing dummy reloads. */
+static int hard_regs_live_known;
+
+/* Indexed by hard reg number,
+ element is nonegative if hard reg has been spilled.
+ This vector is passed to `find_reloads' as an argument
+ and is not changed here. */
+static short *static_reload_reg_p;
+
+/* Set to 1 in subst_reg_equivs if it changes anything. */
+static int subst_reg_equivs_changed;
+
+/* On return from push_reload, holds the reload-number for the OUT
+ operand, which can be different for that from the input operand. */
+static int output_reloadnum;
+
+ /* Compare two RTX's. */
+#define MATCHES(x, y) \
+ (x == y || (x != 0 && (GET_CODE (x) == REG \
+ ? GET_CODE (y) == REG && REGNO (x) == REGNO (y) \
+ : rtx_equal_p (x, y) && ! side_effects_p (x))))
+
+ /* Indicates if two reloads purposes are for similar enough things that we
+ can merge their reloads. */
+#define MERGABLE_RELOADS(when1, when2, op1, op2) \
+ ((when1) == RELOAD_OTHER || (when2) == RELOAD_OTHER \
+ || ((when1) == (when2) && (op1) == (op2)) \
+ || ((when1) == RELOAD_FOR_INPUT && (when2) == RELOAD_FOR_INPUT) \
+ || ((when1) == RELOAD_FOR_OPERAND_ADDRESS \
+ && (when2) == RELOAD_FOR_OPERAND_ADDRESS) \
+ || ((when1) == RELOAD_FOR_OTHER_ADDRESS \
+ && (when2) == RELOAD_FOR_OTHER_ADDRESS))
+
+ /* Nonzero if these two reload purposes produce RELOAD_OTHER when merged. */
+#define MERGE_TO_OTHER(when1, when2, op1, op2) \
+ ((when1) != (when2) \
+ || ! ((op1) == (op2) \
+ || (when1) == RELOAD_FOR_INPUT \
+ || (when1) == RELOAD_FOR_OPERAND_ADDRESS \
+ || (when1) == RELOAD_FOR_OTHER_ADDRESS))
+
+static int push_secondary_reload PROTO((int, rtx, int, int, enum reg_class,
+ enum machine_mode, enum reload_type,
+ enum insn_code *));
+static int push_reload PROTO((rtx, rtx, rtx *, rtx *, enum reg_class,
+ enum machine_mode, enum machine_mode,
+ int, int, int, enum reload_type));
+static void push_replacement PROTO((rtx *, int, enum machine_mode));
+static void combine_reloads PROTO((void));
+static rtx find_dummy_reload PROTO((rtx, rtx, rtx *, rtx *,
+ enum machine_mode, enum machine_mode,
+ enum reg_class, int));
+static int earlyclobber_operand_p PROTO((rtx));
+static int hard_reg_set_here_p PROTO((int, int, rtx));
+static struct decomposition decompose PROTO((rtx));
+static int immune_p PROTO((rtx, rtx, struct decomposition));
+static int alternative_allows_memconst PROTO((char *, int));
+static rtx find_reloads_toplev PROTO((rtx, int, enum reload_type, int, int));
+static rtx make_memloc PROTO((rtx, int));
+static int find_reloads_address PROTO((enum machine_mode, rtx *, rtx, rtx *,
+ int, enum reload_type, int));
+static rtx subst_reg_equivs PROTO((rtx));
+static rtx subst_indexed_address PROTO((rtx));
+static int find_reloads_address_1 PROTO((rtx, int, rtx *, int,
+ enum reload_type,int));
+static void find_reloads_address_part PROTO((rtx, rtx *, enum reg_class,
+ enum machine_mode, int,
+ enum reload_type, int));
+static int find_inc_amount PROTO((rtx, rtx));
+
+#ifdef HAVE_SECONDARY_RELOADS
+
+/* Determine if any secondary reloads are needed for loading (if IN_P is
+ non-zero) or storing (if IN_P is zero) X to or from a reload register of
+ register class RELOAD_CLASS in mode RELOAD_MODE. If secondary reloads
+ are needed, push them.
+
+ Return the reload number of the secondary reload we made, or -1 if
+ we didn't need one. *PICODE is set to the insn_code to use if we do
+ need a secondary reload. */
+
+static int
+push_secondary_reload (in_p, x, opnum, optional, reload_class, reload_mode,
+ type, picode)
+ int in_p;
+ rtx x;
+ int opnum;
+ int optional;
+ enum reg_class reload_class;
+ enum machine_mode reload_mode;
+ enum reload_type type;
+ enum insn_code *picode;
+{
+ enum reg_class class = NO_REGS;
+ enum machine_mode mode = reload_mode;
+ enum insn_code icode = CODE_FOR_nothing;
+ enum reg_class t_class = NO_REGS;
+ enum machine_mode t_mode = VOIDmode;
+ enum insn_code t_icode = CODE_FOR_nothing;
+ enum reload_type secondary_type;
+ int i;
+ int s_reload, t_reload = -1;
+
+ if (type == RELOAD_FOR_INPUT_ADDRESS || type == RELOAD_FOR_OUTPUT_ADDRESS)
+ secondary_type = type;
+ else
+ secondary_type = in_p ? RELOAD_FOR_INPUT_ADDRESS : RELOAD_FOR_OUTPUT_ADDRESS;
+
+ *picode = CODE_FOR_nothing;
+
+ /* If X is a pseudo-register that has an equivalent MEM (actually, if it
+ is still a pseudo-register by now, it *must* have an equivalent MEM
+ but we don't want to assume that), use that equivalent when seeing if
+ a secondary reload is needed since whether or not a reload is needed
+ might be sensitive to the form of the MEM. */
+
+ if (GET_CODE (x) == REG && REGNO (x) >= FIRST_PSEUDO_REGISTER
+ && reg_equiv_mem[REGNO (x)] != 0)
+ x = reg_equiv_mem[REGNO (x)];
+
+#ifdef SECONDARY_INPUT_RELOAD_CLASS
+ if (in_p)
+ class = SECONDARY_INPUT_RELOAD_CLASS (reload_class, reload_mode, x);
+#endif
+
+#ifdef SECONDARY_OUTPUT_RELOAD_CLASS
+ if (! in_p)
+ class = SECONDARY_OUTPUT_RELOAD_CLASS (reload_class, reload_mode, x);
+#endif
+
+ /* If we don't need any secondary registers, done. */
+ if (class == NO_REGS)
+ return -1;
+
+ /* Get a possible insn to use. If the predicate doesn't accept X, don't
+ use the insn. */
+
+ icode = (in_p ? reload_in_optab[(int) reload_mode]
+ : reload_out_optab[(int) reload_mode]);
+
+ if (icode != CODE_FOR_nothing
+ && insn_operand_predicate[(int) icode][in_p]
+ && (! (insn_operand_predicate[(int) icode][in_p]) (x, reload_mode)))
+ icode = CODE_FOR_nothing;
+
+ /* If we will be using an insn, see if it can directly handle the reload
+ register we will be using. If it can, the secondary reload is for a
+ scratch register. If it can't, we will use the secondary reload for
+ an intermediate register and require a tertiary reload for the scratch
+ register. */
+
+ if (icode != CODE_FOR_nothing)
+ {
+ /* If IN_P is non-zero, the reload register will be the output in
+ operand 0. If IN_P is zero, the reload register will be the input
+ in operand 1. Outputs should have an initial "=", which we must
+ skip. */
+
+ char insn_letter = insn_operand_constraint[(int) icode][!in_p][in_p];
+ enum reg_class insn_class
+ = (insn_letter == 'r' ? GENERAL_REGS
+ : REG_CLASS_FROM_LETTER (insn_letter));
+
+ if (insn_class == NO_REGS
+ || (in_p && insn_operand_constraint[(int) icode][!in_p][0] != '=')
+ /* The scratch register's constraint must start with "=&". */
+ || insn_operand_constraint[(int) icode][2][0] != '='
+ || insn_operand_constraint[(int) icode][2][1] != '&')
+ abort ();
+
+ if (reg_class_subset_p (reload_class, insn_class))
+ mode = insn_operand_mode[(int) icode][2];
+ else
+ {
+ char t_letter = insn_operand_constraint[(int) icode][2][2];
+ class = insn_class;
+ t_mode = insn_operand_mode[(int) icode][2];
+ t_class = (t_letter == 'r' ? GENERAL_REGS
+ : REG_CLASS_FROM_LETTER (t_letter));
+ t_icode = icode;
+ icode = CODE_FOR_nothing;
+ }
+ }
+
+ /* This case isn't valid, so fail. Reload is allowed to use the same
+ register for RELOAD_FOR_INPUT_ADDRESS and RELOAD_FOR_INPUT reloads, but
+ in the case of a secondary register, we actually need two different
+ registers for correct code. We fail here to prevent the possibility of
+ silently generating incorrect code later.
+
+ The convention is that secondary input reloads are valid only if the
+ secondary_class is different from class. If you have such a case, you
+ can not use secondary reloads, you must work around the problem some
+ other way.
+
+ Allow this when MODE is not reload_mode and assume that the generated
+ code handles this case (it does on the Alpha, which is the only place
+ this currently happens). */
+
+ if (in_p && class == reload_class && mode == reload_mode)
+ abort ();
+
+ /* If we need a tertiary reload, see if we have one we can reuse or else
+ make a new one. */
+
+ if (t_class != NO_REGS)
+ {
+ for (t_reload = 0; t_reload < n_reloads; t_reload++)
+ if (reload_secondary_p[t_reload]
+ && (reg_class_subset_p (t_class, reload_reg_class[t_reload])
+ || reg_class_subset_p (reload_reg_class[t_reload], t_class))
+ && ((in_p && reload_inmode[t_reload] == t_mode)
+ || (! in_p && reload_outmode[t_reload] == t_mode))
+ && ((in_p && (reload_secondary_in_icode[t_reload]
+ == CODE_FOR_nothing))
+ || (! in_p &&(reload_secondary_out_icode[t_reload]
+ == CODE_FOR_nothing)))
+ && (reg_class_size[(int) t_class] == 1
+#ifdef SMALL_REGISTER_CLASSES
+ || 1
+#endif
+ )
+ && MERGABLE_RELOADS (secondary_type,
+ reload_when_needed[t_reload],
+ opnum, reload_opnum[t_reload]))
+ {
+ if (in_p)
+ reload_inmode[t_reload] = t_mode;
+ if (! in_p)
+ reload_outmode[t_reload] = t_mode;
+
+ if (reg_class_subset_p (t_class, reload_reg_class[t_reload]))
+ reload_reg_class[t_reload] = t_class;
+
+ reload_opnum[t_reload] = MIN (reload_opnum[t_reload], opnum);
+ reload_optional[t_reload] &= optional;
+ reload_secondary_p[t_reload] = 1;
+ if (MERGE_TO_OTHER (secondary_type, reload_when_needed[t_reload],
+ opnum, reload_opnum[t_reload]))
+ reload_when_needed[t_reload] = RELOAD_OTHER;
+ }
+
+ if (t_reload == n_reloads)
+ {
+ /* We need to make a new tertiary reload for this register class. */
+ reload_in[t_reload] = reload_out[t_reload] = 0;
+ reload_reg_class[t_reload] = t_class;
+ reload_inmode[t_reload] = in_p ? t_mode : VOIDmode;
+ reload_outmode[t_reload] = ! in_p ? t_mode : VOIDmode;
+ reload_reg_rtx[t_reload] = 0;
+ reload_optional[t_reload] = optional;
+ reload_inc[t_reload] = 0;
+ /* Maybe we could combine these, but it seems too tricky. */
+ reload_nocombine[t_reload] = 1;
+ reload_in_reg[t_reload] = 0;
+ reload_opnum[t_reload] = opnum;
+ reload_when_needed[t_reload] = secondary_type;
+ reload_secondary_in_reload[t_reload] = -1;
+ reload_secondary_out_reload[t_reload] = -1;
+ reload_secondary_in_icode[t_reload] = CODE_FOR_nothing;
+ reload_secondary_out_icode[t_reload] = CODE_FOR_nothing;
+ reload_secondary_p[t_reload] = 1;
+
+ n_reloads++;
+ }
+ }
+
+ /* See if we can reuse an existing secondary reload. */
+ for (s_reload = 0; s_reload < n_reloads; s_reload++)
+ if (reload_secondary_p[s_reload]
+ && (reg_class_subset_p (class, reload_reg_class[s_reload])
+ || reg_class_subset_p (reload_reg_class[s_reload], class))
+ && ((in_p && reload_inmode[s_reload] == mode)
+ || (! in_p && reload_outmode[s_reload] == mode))
+ && ((in_p && reload_secondary_in_reload[s_reload] == t_reload)
+ || (! in_p && reload_secondary_out_reload[s_reload] == t_reload))
+ && ((in_p && reload_secondary_in_icode[s_reload] == t_icode)
+ || (! in_p && reload_secondary_out_icode[s_reload] == t_icode))
+ && (reg_class_size[(int) class] == 1
+#ifdef SMALL_REGISTER_CLASSES
+ || 1
+#endif
+ )
+ && MERGABLE_RELOADS (secondary_type, reload_when_needed[s_reload],
+ opnum, reload_opnum[s_reload]))
+ {
+ if (in_p)
+ reload_inmode[s_reload] = mode;
+ if (! in_p)
+ reload_outmode[s_reload] = mode;
+
+ if (reg_class_subset_p (class, reload_reg_class[s_reload]))
+ reload_reg_class[s_reload] = class;
+
+ reload_opnum[s_reload] = MIN (reload_opnum[s_reload], opnum);
+ reload_optional[s_reload] &= optional;
+ reload_secondary_p[s_reload] = 1;
+ if (MERGE_TO_OTHER (secondary_type, reload_when_needed[s_reload],
+ opnum, reload_opnum[s_reload]))
+ reload_when_needed[s_reload] = RELOAD_OTHER;
+ }
+
+ if (s_reload == n_reloads)
+ {
+ /* We need to make a new secondary reload for this register class. */
+ reload_in[s_reload] = reload_out[s_reload] = 0;
+ reload_reg_class[s_reload] = class;
+
+ reload_inmode[s_reload] = in_p ? mode : VOIDmode;
+ reload_outmode[s_reload] = ! in_p ? mode : VOIDmode;
+ reload_reg_rtx[s_reload] = 0;
+ reload_optional[s_reload] = optional;
+ reload_inc[s_reload] = 0;
+ /* Maybe we could combine these, but it seems too tricky. */
+ reload_nocombine[s_reload] = 1;
+ reload_in_reg[s_reload] = 0;
+ reload_opnum[s_reload] = opnum;
+ reload_when_needed[s_reload] = secondary_type;
+ reload_secondary_in_reload[s_reload] = in_p ? t_reload : -1;
+ reload_secondary_out_reload[s_reload] = ! in_p ? t_reload : -1;
+ reload_secondary_in_icode[s_reload] = in_p ? t_icode : CODE_FOR_nothing;
+ reload_secondary_out_icode[s_reload]
+ = ! in_p ? t_icode : CODE_FOR_nothing;
+ reload_secondary_p[s_reload] = 1;
+
+ n_reloads++;
+
+#ifdef SECONDARY_MEMORY_NEEDED
+ /* If we need a memory location to copy between the two reload regs,
+ set it up now. */
+
+ if (in_p && icode == CODE_FOR_nothing
+ && SECONDARY_MEMORY_NEEDED (class, reload_class, reload_mode))
+ get_secondary_mem (x, reload_mode, opnum, type);
+
+ if (! in_p && icode == CODE_FOR_nothing
+ && SECONDARY_MEMORY_NEEDED (reload_class, class, reload_mode))
+ get_secondary_mem (x, reload_mode, opnum, type);
+#endif
+ }
+
+ *picode = icode;
+ return s_reload;
+}
+#endif /* HAVE_SECONDARY_RELOADS */
+
+#ifdef SECONDARY_MEMORY_NEEDED
+
+/* Return a memory location that will be used to copy X in mode MODE.
+ If we haven't already made a location for this mode in this insn,
+ call find_reloads_address on the location being returned. */
+
+rtx
+get_secondary_mem (x, mode, opnum, type)
+ rtx x;
+ enum machine_mode mode;
+ int opnum;
+ enum reload_type type;
+{
+ rtx loc;
+ int mem_valid;
+
+ /* By default, if MODE is narrower than a word, widen it to a word.
+ This is required because most machines that require these memory
+ locations do not support short load and stores from all registers
+ (e.g., FP registers). */
+
+#ifdef SECONDARY_MEMORY_NEEDED_MODE
+ mode = SECONDARY_MEMORY_NEEDED_MODE (mode);
+#else
+ if (GET_MODE_BITSIZE (mode) < BITS_PER_WORD)
+ mode = mode_for_size (BITS_PER_WORD, GET_MODE_CLASS (mode), 0);
+#endif
+
+ /* If we already have made a MEM for this operand in MODE, return it. */
+ if (secondary_memlocs_elim[(int) mode][opnum] != 0)
+ return secondary_memlocs_elim[(int) mode][opnum];
+
+ /* If this is the first time we've tried to get a MEM for this mode,
+ allocate a new one. `something_changed' in reload will get set
+ by noticing that the frame size has changed. */
+
+ if (secondary_memlocs[(int) mode] == 0)
+ {
+#ifdef SECONDARY_MEMORY_NEEDED_RTX
+ secondary_memlocs[(int) mode] = SECONDARY_MEMORY_NEEDED_RTX (mode);
+#else
+ secondary_memlocs[(int) mode]
+ = assign_stack_local (mode, GET_MODE_SIZE (mode), 0);
+#endif
+ }
+
+ /* Get a version of the address doing any eliminations needed. If that
+ didn't give us a new MEM, make a new one if it isn't valid. */
+
+ loc = eliminate_regs (secondary_memlocs[(int) mode], VOIDmode, NULL_RTX);
+ mem_valid = strict_memory_address_p (mode, XEXP (loc, 0));
+
+ if (! mem_valid && loc == secondary_memlocs[(int) mode])
+ loc = copy_rtx (loc);
+
+ /* The only time the call below will do anything is if the stack
+ offset is too large. In that case IND_LEVELS doesn't matter, so we
+ can just pass a zero. Adjust the type to be the address of the
+ corresponding object. If the address was valid, save the eliminated
+ address. If it wasn't valid, we need to make a reload each time, so
+ don't save it. */
+
+ if (! mem_valid)
+ {
+ type = (type == RELOAD_FOR_INPUT ? RELOAD_FOR_INPUT_ADDRESS
+ : type == RELOAD_FOR_OUTPUT ? RELOAD_FOR_OUTPUT_ADDRESS
+ : RELOAD_OTHER);
+
+ find_reloads_address (mode, NULL_PTR, XEXP (loc, 0), &XEXP (loc, 0),
+ opnum, type, 0);
+ }
+
+ secondary_memlocs_elim[(int) mode][opnum] = loc;
+ return loc;
+}
+
+/* Clear any secondary memory locations we've made. */
+
+void
+clear_secondary_mem ()
+{
+ bzero ((char *) secondary_memlocs, sizeof secondary_memlocs);
+}
+#endif /* SECONDARY_MEMORY_NEEDED */
+
+/* Record one reload that needs to be performed.
+ IN is an rtx saying where the data are to be found before this instruction.
+ OUT says where they must be stored after the instruction.
+ (IN is zero for data not read, and OUT is zero for data not written.)
+ INLOC and OUTLOC point to the places in the instructions where
+ IN and OUT were found.
+ If IN and OUT are both non-zero, it means the same register must be used
+ to reload both IN and OUT.
+
+ CLASS is a register class required for the reloaded data.
+ INMODE is the machine mode that the instruction requires
+ for the reg that replaces IN and OUTMODE is likewise for OUT.
+
+ If IN is zero, then OUT's location and mode should be passed as
+ INLOC and INMODE.
+
+ STRICT_LOW is the 1 if there is a containing STRICT_LOW_PART rtx.
+
+ OPTIONAL nonzero means this reload does not need to be performed:
+ it can be discarded if that is more convenient.
+
+ OPNUM and TYPE say what the purpose of this reload is.
+
+ The return value is the reload-number for this reload.
+
+ If both IN and OUT are nonzero, in some rare cases we might
+ want to make two separate reloads. (Actually we never do this now.)
+ Therefore, the reload-number for OUT is stored in
+ output_reloadnum when we return; the return value applies to IN.
+ Usually (presently always), when IN and OUT are nonzero,
+ the two reload-numbers are equal, but the caller should be careful to
+ distinguish them. */
+
+static int
+push_reload (in, out, inloc, outloc, class,
+ inmode, outmode, strict_low, optional, opnum, type)
+ register rtx in, out;
+ rtx *inloc, *outloc;
+ enum reg_class class;
+ enum machine_mode inmode, outmode;
+ int strict_low;
+ int optional;
+ int opnum;
+ enum reload_type type;
+{
+ register int i;
+ int dont_share = 0;
+ rtx *in_subreg_loc = 0, *out_subreg_loc = 0;
+ int secondary_in_reload = -1, secondary_out_reload = -1;
+ enum insn_code secondary_in_icode, secondary_out_icode;
+
+ /* INMODE and/or OUTMODE could be VOIDmode if no mode
+ has been specified for the operand. In that case,
+ use the operand's mode as the mode to reload. */
+ if (inmode == VOIDmode && in != 0)
+ inmode = GET_MODE (in);
+ if (outmode == VOIDmode && out != 0)
+ outmode = GET_MODE (out);
+
+ /* If IN is a pseudo register everywhere-equivalent to a constant, and
+ it is not in a hard register, reload straight from the constant,
+ since we want to get rid of such pseudo registers.
+ Often this is done earlier, but not always in find_reloads_address. */
+ if (in != 0 && GET_CODE (in) == REG)
+ {
+ register int regno = REGNO (in);
+
+ if (regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] < 0
+ && reg_equiv_constant[regno] != 0)
+ in = reg_equiv_constant[regno];
+ }
+
+ /* Likewise for OUT. Of course, OUT will never be equivalent to
+ an actual constant, but it might be equivalent to a memory location
+ (in the case of a parameter). */
+ if (out != 0 && GET_CODE (out) == REG)
+ {
+ register int regno = REGNO (out);
+
+ if (regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] < 0
+ && reg_equiv_constant[regno] != 0)
+ out = reg_equiv_constant[regno];
+ }
+
+ /* If we have a read-write operand with an address side-effect,
+ change either IN or OUT so the side-effect happens only once. */
+ if (in != 0 && out != 0 && GET_CODE (in) == MEM && rtx_equal_p (in, out))
+ {
+ if (GET_CODE (XEXP (in, 0)) == POST_INC
+ || GET_CODE (XEXP (in, 0)) == POST_DEC)
+ in = gen_rtx (MEM, GET_MODE (in), XEXP (XEXP (in, 0), 0));
+ if (GET_CODE (XEXP (in, 0)) == PRE_INC
+ || GET_CODE (XEXP (in, 0)) == PRE_DEC)
+ out = gen_rtx (MEM, GET_MODE (out), XEXP (XEXP (out, 0), 0));
+ }
+
+ /* If we are reloading a (SUBREG constant ...), really reload just the
+ inside expression in its own mode. Similarly for (SUBREG (PLUS ...)).
+ If we have (SUBREG:M1 (MEM:M2 ...) ...) (or an inner REG that is still
+ a pseudo and hence will become a MEM) with M1 wider than M2 and the
+ register is a pseudo, also reload the inside expression.
+ For machines that extend byte loads, do this for any SUBREG of a pseudo
+ where both M1 and M2 are a word or smaller unless they are the same
+ size.
+ Similar issue for (SUBREG:M1 (REG:M2 ...) ...) for a hard register R where
+ either M1 is not valid for R or M2 is wider than a word but we only
+ need one word to store an M2-sized quantity in R.
+ (However, if OUT is nonzero, we need to reload the reg *and*
+ the subreg, so do nothing here, and let following statement handle it.)
+
+ Note that the case of (SUBREG (CONST_INT...)...) is handled elsewhere;
+ we can't handle it here because CONST_INT does not indicate a mode.
+
+ Similarly, we must reload the inside expression if we have a
+ STRICT_LOW_PART (presumably, in == out in the cas).
+
+ Also reload the inner expression if it does not require a secondary
+ reload but the SUBREG does. */
+
+ if (in != 0 && GET_CODE (in) == SUBREG
+ && (CONSTANT_P (SUBREG_REG (in))
+ || GET_CODE (SUBREG_REG (in)) == PLUS
+ || strict_low
+ || (((GET_CODE (SUBREG_REG (in)) == REG
+ && REGNO (SUBREG_REG (in)) >= FIRST_PSEUDO_REGISTER)
+ || GET_CODE (SUBREG_REG (in)) == MEM)
+ && ((GET_MODE_SIZE (inmode)
+ > GET_MODE_SIZE (GET_MODE (SUBREG_REG (in))))
+#ifdef LOAD_EXTEND_OP
+ || (GET_MODE_SIZE (inmode) <= UNITS_PER_WORD
+ && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (in)))
+ <= UNITS_PER_WORD)
+ && (GET_MODE_SIZE (inmode)
+ != GET_MODE_SIZE (GET_MODE (SUBREG_REG (in)))))
+#endif
+ ))
+ || (GET_CODE (SUBREG_REG (in)) == REG
+ && REGNO (SUBREG_REG (in)) < FIRST_PSEUDO_REGISTER
+ /* The case where out is nonzero
+ is handled differently in the following statement. */
+ && (out == 0 || SUBREG_WORD (in) == 0)
+ && ((GET_MODE_SIZE (inmode) <= UNITS_PER_WORD
+ && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (in)))
+ > UNITS_PER_WORD)
+ && ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (in)))
+ / UNITS_PER_WORD)
+ != HARD_REGNO_NREGS (REGNO (SUBREG_REG (in)),
+ GET_MODE (SUBREG_REG (in)))))
+ || ! HARD_REGNO_MODE_OK ((REGNO (SUBREG_REG (in))
+ + SUBREG_WORD (in)),
+ inmode)))
+#ifdef SECONDARY_INPUT_RELOAD_CLASS
+ || (SECONDARY_INPUT_RELOAD_CLASS (class, inmode, in) != NO_REGS
+ && (SECONDARY_INPUT_RELOAD_CLASS (class,
+ GET_MODE (SUBREG_REG (in)),
+ SUBREG_REG (in))
+ == NO_REGS))
+#endif
+ ))
+ {
+ in_subreg_loc = inloc;
+ inloc = &SUBREG_REG (in);
+ in = *inloc;
+#ifndef LOAD_EXTEND_OP
+ if (GET_CODE (in) == MEM)
+ /* This is supposed to happen only for paradoxical subregs made by
+ combine.c. (SUBREG (MEM)) isn't supposed to occur other ways. */
+ if (GET_MODE_SIZE (GET_MODE (in)) > GET_MODE_SIZE (inmode))
+ abort ();
+#endif
+ inmode = GET_MODE (in);
+ }
+
+ /* Similar issue for (SUBREG:M1 (REG:M2 ...) ...) for a hard register R where
+ either M1 is not valid for R or M2 is wider than a word but we only
+ need one word to store an M2-sized quantity in R.
+
+ However, we must reload the inner reg *as well as* the subreg in
+ that case. */
+
+ if (in != 0 && GET_CODE (in) == SUBREG
+ && GET_CODE (SUBREG_REG (in)) == REG
+ && REGNO (SUBREG_REG (in)) < FIRST_PSEUDO_REGISTER
+ && (! HARD_REGNO_MODE_OK (REGNO (SUBREG_REG (in)), inmode)
+ || (GET_MODE_SIZE (inmode) <= UNITS_PER_WORD
+ && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (in)))
+ > UNITS_PER_WORD)
+ && ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (in)))
+ / UNITS_PER_WORD)
+ != HARD_REGNO_NREGS (REGNO (SUBREG_REG (in)),
+ GET_MODE (SUBREG_REG (in)))))))
+ {
+ push_reload (SUBREG_REG (in), NULL_RTX, &SUBREG_REG (in), NULL_PTR,
+ GENERAL_REGS, VOIDmode, VOIDmode, 0, 0, opnum, type);
+ }
+
+
+ /* Similarly for paradoxical and problematical SUBREGs on the output.
+ Note that there is no reason we need worry about the previous value
+ of SUBREG_REG (out); even if wider than out,
+ storing in a subreg is entitled to clobber it all
+ (except in the case of STRICT_LOW_PART,
+ and in that case the constraint should label it input-output.) */
+ if (out != 0 && GET_CODE (out) == SUBREG
+ && (CONSTANT_P (SUBREG_REG (out))
+ || strict_low
+ || (((GET_CODE (SUBREG_REG (out)) == REG
+ && REGNO (SUBREG_REG (out)) >= FIRST_PSEUDO_REGISTER)
+ || GET_CODE (SUBREG_REG (out)) == MEM)
+ && ((GET_MODE_SIZE (outmode)
+ > GET_MODE_SIZE (GET_MODE (SUBREG_REG (out))))
+#ifdef LOAD_EXTEND_OP
+ || (GET_MODE_SIZE (outmode) <= UNITS_PER_WORD
+ && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (out)))
+ <= UNITS_PER_WORD)
+ && (GET_MODE_SIZE (outmode)
+ != GET_MODE_SIZE (GET_MODE (SUBREG_REG (out)))))
+#endif
+ ))
+ || (GET_CODE (SUBREG_REG (out)) == REG
+ && REGNO (SUBREG_REG (out)) < FIRST_PSEUDO_REGISTER
+ && ((GET_MODE_SIZE (outmode) <= UNITS_PER_WORD
+ && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (out)))
+ > UNITS_PER_WORD)
+ && ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (out)))
+ / UNITS_PER_WORD)
+ != HARD_REGNO_NREGS (REGNO (SUBREG_REG (out)),
+ GET_MODE (SUBREG_REG (out)))))
+ || ! HARD_REGNO_MODE_OK ((REGNO (SUBREG_REG (out))
+ + SUBREG_WORD (out)),
+ outmode)))
+#ifdef SECONDARY_OUTPUT_RELOAD_CLASS
+ || (SECONDARY_OUTPUT_RELOAD_CLASS (class, outmode, out) != NO_REGS
+ && (SECONDARY_OUTPUT_RELOAD_CLASS (class,
+ GET_MODE (SUBREG_REG (out)),
+ SUBREG_REG (out))
+ == NO_REGS))
+#endif
+ ))
+ {
+ out_subreg_loc = outloc;
+ outloc = &SUBREG_REG (out);
+ out = *outloc;
+#ifndef LOAD_EXTEND_OP
+ if (GET_CODE (out) == MEM
+ && GET_MODE_SIZE (GET_MODE (out)) > GET_MODE_SIZE (outmode))
+ abort ();
+#endif
+ outmode = GET_MODE (out);
+ }
+
+ /* If IN appears in OUT, we can't share any input-only reload for IN. */
+ if (in != 0 && out != 0 && GET_CODE (out) == MEM
+ && (GET_CODE (in) == REG || GET_CODE (in) == MEM)
+ && reg_overlap_mentioned_for_reload_p (in, XEXP (out, 0)))
+ dont_share = 1;
+
+ /* If IN is a SUBREG of a hard register, make a new REG. This
+ simplifies some of the cases below. */
+
+ if (in != 0 && GET_CODE (in) == SUBREG && GET_CODE (SUBREG_REG (in)) == REG
+ && REGNO (SUBREG_REG (in)) < FIRST_PSEUDO_REGISTER)
+ in = gen_rtx (REG, GET_MODE (in),
+ REGNO (SUBREG_REG (in)) + SUBREG_WORD (in));
+
+ /* Similarly for OUT. */
+ if (out != 0 && GET_CODE (out) == SUBREG
+ && GET_CODE (SUBREG_REG (out)) == REG
+ && REGNO (SUBREG_REG (out)) < FIRST_PSEUDO_REGISTER)
+ out = gen_rtx (REG, GET_MODE (out),
+ REGNO (SUBREG_REG (out)) + SUBREG_WORD (out));
+
+ /* Narrow down the class of register wanted if that is
+ desirable on this machine for efficiency. */
+ if (in != 0)
+ class = PREFERRED_RELOAD_CLASS (in, class);
+
+ /* Output reloads may need analogous treatment, different in detail. */
+#ifdef PREFERRED_OUTPUT_RELOAD_CLASS
+ if (out != 0)
+ class = PREFERRED_OUTPUT_RELOAD_CLASS (out, class);
+#endif
+
+ /* Make sure we use a class that can handle the actual pseudo
+ inside any subreg. For example, on the 386, QImode regs
+ can appear within SImode subregs. Although GENERAL_REGS
+ can handle SImode, QImode needs a smaller class. */
+#ifdef LIMIT_RELOAD_CLASS
+ if (in_subreg_loc)
+ class = LIMIT_RELOAD_CLASS (inmode, class);
+ else if (in != 0 && GET_CODE (in) == SUBREG)
+ class = LIMIT_RELOAD_CLASS (GET_MODE (SUBREG_REG (in)), class);
+
+ if (out_subreg_loc)
+ class = LIMIT_RELOAD_CLASS (outmode, class);
+ if (out != 0 && GET_CODE (out) == SUBREG)
+ class = LIMIT_RELOAD_CLASS (GET_MODE (SUBREG_REG (out)), class);
+#endif
+
+ /* Verify that this class is at least possible for the mode that
+ is specified. */
+ if (this_insn_is_asm)
+ {
+ enum machine_mode mode;
+ if (GET_MODE_SIZE (inmode) > GET_MODE_SIZE (outmode))
+ mode = inmode;
+ else
+ mode = outmode;
+ if (mode == VOIDmode)
+ {
+ error_for_asm (this_insn, "cannot reload integer constant operand in `asm'");
+ mode = word_mode;
+ if (in != 0)
+ inmode = word_mode;
+ if (out != 0)
+ outmode = word_mode;
+ }
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (HARD_REGNO_MODE_OK (i, mode)
+ && TEST_HARD_REG_BIT (reg_class_contents[(int) class], i))
+ {
+ int nregs = HARD_REGNO_NREGS (i, mode);
+
+ int j;
+ for (j = 1; j < nregs; j++)
+ if (! TEST_HARD_REG_BIT (reg_class_contents[(int) class], i + j))
+ break;
+ if (j == nregs)
+ break;
+ }
+ if (i == FIRST_PSEUDO_REGISTER)
+ {
+ error_for_asm (this_insn, "impossible register constraint in `asm'");
+ class = ALL_REGS;
+ }
+ }
+
+ if (class == NO_REGS)
+ abort ();
+
+ /* We can use an existing reload if the class is right
+ and at least one of IN and OUT is a match
+ and the other is at worst neutral.
+ (A zero compared against anything is neutral.)
+
+ If SMALL_REGISTER_CLASSES, don't use existing reloads unless they are
+ for the same thing since that can cause us to need more reload registers
+ than we otherwise would. */
+
+ for (i = 0; i < n_reloads; i++)
+ if ((reg_class_subset_p (class, reload_reg_class[i])
+ || reg_class_subset_p (reload_reg_class[i], class))
+ /* If the existing reload has a register, it must fit our class. */
+ && (reload_reg_rtx[i] == 0
+ || TEST_HARD_REG_BIT (reg_class_contents[(int) class],
+ true_regnum (reload_reg_rtx[i])))
+ && ((in != 0 && MATCHES (reload_in[i], in) && ! dont_share
+ && (out == 0 || reload_out[i] == 0 || MATCHES (reload_out[i], out)))
+ ||
+ (out != 0 && MATCHES (reload_out[i], out)
+ && (in == 0 || reload_in[i] == 0 || MATCHES (reload_in[i], in))))
+ && (reg_class_size[(int) class] == 1
+#ifdef SMALL_REGISTER_CLASSES
+ || 1
+#endif
+ )
+ && MERGABLE_RELOADS (type, reload_when_needed[i],
+ opnum, reload_opnum[i]))
+ break;
+
+ /* Reloading a plain reg for input can match a reload to postincrement
+ that reg, since the postincrement's value is the right value.
+ Likewise, it can match a preincrement reload, since we regard
+ the preincrementation as happening before any ref in this insn
+ to that register. */
+ if (i == n_reloads)
+ for (i = 0; i < n_reloads; i++)
+ if ((reg_class_subset_p (class, reload_reg_class[i])
+ || reg_class_subset_p (reload_reg_class[i], class))
+ /* If the existing reload has a register, it must fit our class. */
+ && (reload_reg_rtx[i] == 0
+ || TEST_HARD_REG_BIT (reg_class_contents[(int) class],
+ true_regnum (reload_reg_rtx[i])))
+ && out == 0 && reload_out[i] == 0 && reload_in[i] != 0
+ && ((GET_CODE (in) == REG
+ && (GET_CODE (reload_in[i]) == POST_INC
+ || GET_CODE (reload_in[i]) == POST_DEC
+ || GET_CODE (reload_in[i]) == PRE_INC
+ || GET_CODE (reload_in[i]) == PRE_DEC)
+ && MATCHES (XEXP (reload_in[i], 0), in))
+ ||
+ (GET_CODE (reload_in[i]) == REG
+ && (GET_CODE (in) == POST_INC
+ || GET_CODE (in) == POST_DEC
+ || GET_CODE (in) == PRE_INC
+ || GET_CODE (in) == PRE_DEC)
+ && MATCHES (XEXP (in, 0), reload_in[i])))
+ && (reg_class_size[(int) class] == 1
+#ifdef SMALL_REGISTER_CLASSES
+ || 1
+#endif
+ )
+ && MERGABLE_RELOADS (type, reload_when_needed[i],
+ opnum, reload_opnum[i]))
+ {
+ /* Make sure reload_in ultimately has the increment,
+ not the plain register. */
+ if (GET_CODE (in) == REG)
+ in = reload_in[i];
+ break;
+ }
+
+ if (i == n_reloads)
+ {
+ /* See if we need a secondary reload register to move between CLASS
+ and IN or CLASS and OUT. Get the icode and push any required reloads
+ needed for each of them if so. */
+
+#ifdef SECONDARY_INPUT_RELOAD_CLASS
+ if (in != 0)
+ secondary_in_reload
+ = push_secondary_reload (1, in, opnum, optional, class, inmode, type,
+ &secondary_in_icode);
+#endif
+
+#ifdef SECONDARY_OUTPUT_RELOAD_CLASS
+ if (out != 0 && GET_CODE (out) != SCRATCH)
+ secondary_out_reload
+ = push_secondary_reload (0, out, opnum, optional, class, outmode,
+ type, &secondary_out_icode);
+#endif
+
+ /* We found no existing reload suitable for re-use.
+ So add an additional reload. */
+
+ i = n_reloads;
+ reload_in[i] = in;
+ reload_out[i] = out;
+ reload_reg_class[i] = class;
+ reload_inmode[i] = inmode;
+ reload_outmode[i] = outmode;
+ reload_reg_rtx[i] = 0;
+ reload_optional[i] = optional;
+ reload_inc[i] = 0;
+ reload_nocombine[i] = 0;
+ reload_in_reg[i] = inloc ? *inloc : 0;
+ reload_opnum[i] = opnum;
+ reload_when_needed[i] = type;
+ reload_secondary_in_reload[i] = secondary_in_reload;
+ reload_secondary_out_reload[i] = secondary_out_reload;
+ reload_secondary_in_icode[i] = secondary_in_icode;
+ reload_secondary_out_icode[i] = secondary_out_icode;
+ reload_secondary_p[i] = 0;
+
+ n_reloads++;
+
+#ifdef SECONDARY_MEMORY_NEEDED
+ /* If a memory location is needed for the copy, make one. */
+ if (in != 0 && GET_CODE (in) == REG
+ && REGNO (in) < FIRST_PSEUDO_REGISTER
+ && SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (REGNO (in)),
+ class, inmode))
+ get_secondary_mem (in, inmode, opnum, type);
+
+ if (out != 0 && GET_CODE (out) == REG
+ && REGNO (out) < FIRST_PSEUDO_REGISTER
+ && SECONDARY_MEMORY_NEEDED (class, REGNO_REG_CLASS (REGNO (out)),
+ outmode))
+ get_secondary_mem (out, outmode, opnum, type);
+#endif
+ }
+ else
+ {
+ /* We are reusing an existing reload,
+ but we may have additional information for it.
+ For example, we may now have both IN and OUT
+ while the old one may have just one of them. */
+
+ if (inmode != VOIDmode)
+ reload_inmode[i] = inmode;
+ if (outmode != VOIDmode)
+ reload_outmode[i] = outmode;
+ if (in != 0)
+ reload_in[i] = in;
+ if (out != 0)
+ reload_out[i] = out;
+ if (reg_class_subset_p (class, reload_reg_class[i]))
+ reload_reg_class[i] = class;
+ reload_optional[i] &= optional;
+ if (MERGE_TO_OTHER (type, reload_when_needed[i],
+ opnum, reload_opnum[i]))
+ reload_when_needed[i] = RELOAD_OTHER;
+ reload_opnum[i] = MIN (reload_opnum[i], opnum);
+ }
+
+ /* If the ostensible rtx being reload differs from the rtx found
+ in the location to substitute, this reload is not safe to combine
+ because we cannot reliably tell whether it appears in the insn. */
+
+ if (in != 0 && in != *inloc)
+ reload_nocombine[i] = 1;
+
+#if 0
+ /* This was replaced by changes in find_reloads_address_1 and the new
+ function inc_for_reload, which go with a new meaning of reload_inc. */
+
+ /* If this is an IN/OUT reload in an insn that sets the CC,
+ it must be for an autoincrement. It doesn't work to store
+ the incremented value after the insn because that would clobber the CC.
+ So we must do the increment of the value reloaded from,
+ increment it, store it back, then decrement again. */
+ if (out != 0 && sets_cc0_p (PATTERN (this_insn)))
+ {
+ out = 0;
+ reload_out[i] = 0;
+ reload_inc[i] = find_inc_amount (PATTERN (this_insn), in);
+ /* If we did not find a nonzero amount-to-increment-by,
+ that contradicts the belief that IN is being incremented
+ in an address in this insn. */
+ if (reload_inc[i] == 0)
+ abort ();
+ }
+#endif
+
+ /* If we will replace IN and OUT with the reload-reg,
+ record where they are located so that substitution need
+ not do a tree walk. */
+
+ if (replace_reloads)
+ {
+ if (inloc != 0)
+ {
+ register struct replacement *r = &replacements[n_replacements++];
+ r->what = i;
+ r->subreg_loc = in_subreg_loc;
+ r->where = inloc;
+ r->mode = inmode;
+ }
+ if (outloc != 0 && outloc != inloc)
+ {
+ register struct replacement *r = &replacements[n_replacements++];
+ r->what = i;
+ r->where = outloc;
+ r->subreg_loc = out_subreg_loc;
+ r->mode = outmode;
+ }
+ }
+
+ /* If this reload is just being introduced and it has both
+ an incoming quantity and an outgoing quantity that are
+ supposed to be made to match, see if either one of the two
+ can serve as the place to reload into.
+
+ If one of them is acceptable, set reload_reg_rtx[i]
+ to that one. */
+
+ if (in != 0 && out != 0 && in != out && reload_reg_rtx[i] == 0)
+ {
+ reload_reg_rtx[i] = find_dummy_reload (in, out, inloc, outloc,
+ inmode, outmode,
+ reload_reg_class[i], i);
+
+ /* If the outgoing register already contains the same value
+ as the incoming one, we can dispense with loading it.
+ The easiest way to tell the caller that is to give a phony
+ value for the incoming operand (same as outgoing one). */
+ if (reload_reg_rtx[i] == out
+ && (GET_CODE (in) == REG || CONSTANT_P (in))
+ && 0 != find_equiv_reg (in, this_insn, 0, REGNO (out),
+ static_reload_reg_p, i, inmode))
+ reload_in[i] = out;
+ }
+
+ /* If this is an input reload and the operand contains a register that
+ dies in this insn and is used nowhere else, see if it is the right class
+ to be used for this reload. Use it if so. (This occurs most commonly
+ in the case of paradoxical SUBREGs and in-out reloads). We cannot do
+ this if it is also an output reload that mentions the register unless
+ the output is a SUBREG that clobbers an entire register.
+
+ Note that the operand might be one of the spill regs, if it is a
+ pseudo reg and we are in a block where spilling has not taken place.
+ But if there is no spilling in this block, that is OK.
+ An explicitly used hard reg cannot be a spill reg. */
+
+ if (reload_reg_rtx[i] == 0 && in != 0)
+ {
+ rtx note;
+ int regno;
+
+ for (note = REG_NOTES (this_insn); note; note = XEXP (note, 1))
+ if (REG_NOTE_KIND (note) == REG_DEAD
+ && GET_CODE (XEXP (note, 0)) == REG
+ && (regno = REGNO (XEXP (note, 0))) < FIRST_PSEUDO_REGISTER
+ && reg_mentioned_p (XEXP (note, 0), in)
+ && ! refers_to_regno_for_reload_p (regno,
+ (regno
+ + HARD_REGNO_NREGS (regno,
+ inmode)),
+ PATTERN (this_insn), inloc)
+ /* If this is also an output reload, IN cannot be used as
+ the reload register if it is set in this insn unless IN
+ is also OUT. */
+ && (out == 0 || in == out
+ || ! hard_reg_set_here_p (regno,
+ (regno
+ + HARD_REGNO_NREGS (regno,
+ inmode)),
+ PATTERN (this_insn)))
+ /* ??? Why is this code so different from the previous?
+ Is there any simple coherent way to describe the two together?
+ What's going on here. */
+ && (in != out
+ || (GET_CODE (in) == SUBREG
+ && (((GET_MODE_SIZE (GET_MODE (in)) + (UNITS_PER_WORD - 1))
+ / UNITS_PER_WORD)
+ == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (in)))
+ + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD))))
+ /* Make sure the operand fits in the reg that dies. */
+ && GET_MODE_SIZE (inmode) <= GET_MODE_SIZE (GET_MODE (XEXP (note, 0)))
+ && HARD_REGNO_MODE_OK (regno, inmode)
+ && GET_MODE_SIZE (outmode) <= GET_MODE_SIZE (GET_MODE (XEXP (note, 0)))
+ && HARD_REGNO_MODE_OK (regno, outmode)
+ && TEST_HARD_REG_BIT (reg_class_contents[(int) class], regno)
+ && !fixed_regs[regno])
+ {
+ reload_reg_rtx[i] = gen_rtx (REG, inmode, regno);
+ break;
+ }
+ }
+
+ if (out)
+ output_reloadnum = i;
+
+ return i;
+}
+
+/* Record an additional place we must replace a value
+ for which we have already recorded a reload.
+ RELOADNUM is the value returned by push_reload
+ when the reload was recorded.
+ This is used in insn patterns that use match_dup. */
+
+static void
+push_replacement (loc, reloadnum, mode)
+ rtx *loc;
+ int reloadnum;
+ enum machine_mode mode;
+{
+ if (replace_reloads)
+ {
+ register struct replacement *r = &replacements[n_replacements++];
+ r->what = reloadnum;
+ r->where = loc;
+ r->subreg_loc = 0;
+ r->mode = mode;
+ }
+}
+
+/* Transfer all replacements that used to be in reload FROM to be in
+ reload TO. */
+
+void
+transfer_replacements (to, from)
+ int to, from;
+{
+ int i;
+
+ for (i = 0; i < n_replacements; i++)
+ if (replacements[i].what == from)
+ replacements[i].what = to;
+}
+
+/* If there is only one output reload, and it is not for an earlyclobber
+ operand, try to combine it with a (logically unrelated) input reload
+ to reduce the number of reload registers needed.
+
+ This is safe if the input reload does not appear in
+ the value being output-reloaded, because this implies
+ it is not needed any more once the original insn completes.
+
+ If that doesn't work, see we can use any of the registers that
+ die in this insn as a reload register. We can if it is of the right
+ class and does not appear in the value being output-reloaded. */
+
+static void
+combine_reloads ()
+{
+ int i;
+ int output_reload = -1;
+ rtx note;
+
+ /* Find the output reload; return unless there is exactly one
+ and that one is mandatory. */
+
+ for (i = 0; i < n_reloads; i++)
+ if (reload_out[i] != 0)
+ {
+ if (output_reload >= 0)
+ return;
+ output_reload = i;
+ }
+
+ if (output_reload < 0 || reload_optional[output_reload])
+ return;
+
+ /* An input-output reload isn't combinable. */
+
+ if (reload_in[output_reload] != 0)
+ return;
+
+ /* If this reload is for an earlyclobber operand, we can't do anything. */
+ if (earlyclobber_operand_p (reload_out[output_reload]))
+ return;
+
+ /* Check each input reload; can we combine it? */
+
+ for (i = 0; i < n_reloads; i++)
+ if (reload_in[i] && ! reload_optional[i] && ! reload_nocombine[i]
+ /* Life span of this reload must not extend past main insn. */
+ && reload_when_needed[i] != RELOAD_FOR_OUTPUT_ADDRESS
+ && reload_when_needed[i] != RELOAD_OTHER
+ && (CLASS_MAX_NREGS (reload_reg_class[i], reload_inmode[i])
+ == CLASS_MAX_NREGS (reload_reg_class[output_reload],
+ reload_outmode[output_reload]))
+ && reload_inc[i] == 0
+ && reload_reg_rtx[i] == 0
+#ifdef SECONDARY_MEMORY_NEEDED
+ /* Don't combine two reloads with different secondary
+ memory locations. */
+ && (secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[i]] == 0
+ || secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[output_reload]] == 0
+ || rtx_equal_p (secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[i]],
+ secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[output_reload]]))
+#endif
+#ifdef SMALL_REGISTER_CLASSES
+ && reload_reg_class[i] == reload_reg_class[output_reload]
+#else
+ && (reg_class_subset_p (reload_reg_class[i],
+ reload_reg_class[output_reload])
+ || reg_class_subset_p (reload_reg_class[output_reload],
+ reload_reg_class[i]))
+#endif
+ && (MATCHES (reload_in[i], reload_out[output_reload])
+ /* Args reversed because the first arg seems to be
+ the one that we imagine being modified
+ while the second is the one that might be affected. */
+ || (! reg_overlap_mentioned_for_reload_p (reload_out[output_reload],
+ reload_in[i])
+ /* However, if the input is a register that appears inside
+ the output, then we also can't share.
+ Imagine (set (mem (reg 69)) (plus (reg 69) ...)).
+ If the same reload reg is used for both reg 69 and the
+ result to be stored in memory, then that result
+ will clobber the address of the memory ref. */
+ && ! (GET_CODE (reload_in[i]) == REG
+ && reg_overlap_mentioned_for_reload_p (reload_in[i],
+ reload_out[output_reload]))))
+ && (reg_class_size[(int) reload_reg_class[i]]
+#ifdef SMALL_REGISTER_CLASSES
+ || 1
+#endif
+ )
+ /* We will allow making things slightly worse by combining an
+ input and an output, but no worse than that. */
+ && (reload_when_needed[i] == RELOAD_FOR_INPUT
+ || reload_when_needed[i] == RELOAD_FOR_OUTPUT))
+ {
+ int j;
+
+ /* We have found a reload to combine with! */
+ reload_out[i] = reload_out[output_reload];
+ reload_outmode[i] = reload_outmode[output_reload];
+ /* Mark the old output reload as inoperative. */
+ reload_out[output_reload] = 0;
+ /* The combined reload is needed for the entire insn. */
+ reload_when_needed[i] = RELOAD_OTHER;
+ /* If the output reload had a secondary reload, copy it. */
+ if (reload_secondary_out_reload[output_reload] != -1)
+ {
+ reload_secondary_out_reload[i]
+ = reload_secondary_out_reload[output_reload];
+ reload_secondary_out_icode[i]
+ = reload_secondary_out_icode[output_reload];
+ }
+
+#ifdef SECONDARY_MEMORY_NEEDED
+ /* Copy any secondary MEM. */
+ if (secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[output_reload]] != 0)
+ secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[i]]
+ = secondary_memlocs_elim[(int) reload_outmode[output_reload]][reload_opnum[output_reload]];
+#endif
+ /* If required, minimize the register class. */
+ if (reg_class_subset_p (reload_reg_class[output_reload],
+ reload_reg_class[i]))
+ reload_reg_class[i] = reload_reg_class[output_reload];
+
+ /* Transfer all replacements from the old reload to the combined. */
+ for (j = 0; j < n_replacements; j++)
+ if (replacements[j].what == output_reload)
+ replacements[j].what = i;
+
+ return;
+ }
+
+ /* If this insn has only one operand that is modified or written (assumed
+ to be the first), it must be the one corresponding to this reload. It
+ is safe to use anything that dies in this insn for that output provided
+ that it does not occur in the output (we already know it isn't an
+ earlyclobber. If this is an asm insn, give up. */
+
+ if (INSN_CODE (this_insn) == -1)
+ return;
+
+ for (i = 1; i < insn_n_operands[INSN_CODE (this_insn)]; i++)
+ if (insn_operand_constraint[INSN_CODE (this_insn)][i][0] == '='
+ || insn_operand_constraint[INSN_CODE (this_insn)][i][0] == '+')
+ return;
+
+ /* See if some hard register that dies in this insn and is not used in
+ the output is the right class. Only works if the register we pick
+ up can fully hold our output reload. */
+ for (note = REG_NOTES (this_insn); note; note = XEXP (note, 1))
+ if (REG_NOTE_KIND (note) == REG_DEAD
+ && GET_CODE (XEXP (note, 0)) == REG
+ && ! reg_overlap_mentioned_for_reload_p (XEXP (note, 0),
+ reload_out[output_reload])
+ && REGNO (XEXP (note, 0)) < FIRST_PSEUDO_REGISTER
+ && HARD_REGNO_MODE_OK (REGNO (XEXP (note, 0)), reload_outmode[output_reload])
+ && TEST_HARD_REG_BIT (reg_class_contents[(int) reload_reg_class[output_reload]],
+ REGNO (XEXP (note, 0)))
+ && (HARD_REGNO_NREGS (REGNO (XEXP (note, 0)), reload_outmode[output_reload])
+ <= HARD_REGNO_NREGS (REGNO (XEXP (note, 0)), GET_MODE (XEXP (note, 0))))
+ && ! fixed_regs[REGNO (XEXP (note, 0))])
+ {
+ reload_reg_rtx[output_reload] = gen_rtx (REG,
+ reload_outmode[output_reload],
+ REGNO (XEXP (note, 0)));
+ return;
+ }
+}
+
+/* Try to find a reload register for an in-out reload (expressions IN and OUT).
+ See if one of IN and OUT is a register that may be used;
+ this is desirable since a spill-register won't be needed.
+ If so, return the register rtx that proves acceptable.
+
+ INLOC and OUTLOC are locations where IN and OUT appear in the insn.
+ CLASS is the register class required for the reload.
+
+ If FOR_REAL is >= 0, it is the number of the reload,
+ and in some cases when it can be discovered that OUT doesn't need
+ to be computed, clear out reload_out[FOR_REAL].
+
+ If FOR_REAL is -1, this should not be done, because this call
+ is just to see if a register can be found, not to find and install it. */
+
+static rtx
+find_dummy_reload (real_in, real_out, inloc, outloc,
+ inmode, outmode, class, for_real)
+ rtx real_in, real_out;
+ rtx *inloc, *outloc;
+ enum machine_mode inmode, outmode;
+ enum reg_class class;
+ int for_real;
+{
+ rtx in = real_in;
+ rtx out = real_out;
+ int in_offset = 0;
+ int out_offset = 0;
+ rtx value = 0;
+
+ /* If operands exceed a word, we can't use either of them
+ unless they have the same size. */
+ if (GET_MODE_SIZE (outmode) != GET_MODE_SIZE (inmode)
+ && (GET_MODE_SIZE (outmode) > UNITS_PER_WORD
+ || GET_MODE_SIZE (inmode) > UNITS_PER_WORD))
+ return 0;
+
+ /* Find the inside of any subregs. */
+ while (GET_CODE (out) == SUBREG)
+ {
+ out_offset = SUBREG_WORD (out);
+ out = SUBREG_REG (out);
+ }
+ while (GET_CODE (in) == SUBREG)
+ {
+ in_offset = SUBREG_WORD (in);
+ in = SUBREG_REG (in);
+ }
+
+ /* Narrow down the reg class, the same way push_reload will;
+ otherwise we might find a dummy now, but push_reload won't. */
+ class = PREFERRED_RELOAD_CLASS (in, class);
+
+ /* See if OUT will do. */
+ if (GET_CODE (out) == REG
+ && REGNO (out) < FIRST_PSEUDO_REGISTER)
+ {
+ register int regno = REGNO (out) + out_offset;
+ int nwords = HARD_REGNO_NREGS (regno, outmode);
+ rtx saved_rtx;
+
+ /* When we consider whether the insn uses OUT,
+ ignore references within IN. They don't prevent us
+ from copying IN into OUT, because those refs would
+ move into the insn that reloads IN.
+
+ However, we only ignore IN in its role as this reload.
+ If the insn uses IN elsewhere and it contains OUT,
+ that counts. We can't be sure it's the "same" operand
+ so it might not go through this reload. */
+ saved_rtx = *inloc;
+ *inloc = const0_rtx;
+
+ if (regno < FIRST_PSEUDO_REGISTER
+ /* A fixed reg that can overlap other regs better not be used
+ for reloading in any way. */
+#ifdef OVERLAPPING_REGNO_P
+ && ! (fixed_regs[regno] && OVERLAPPING_REGNO_P (regno))
+#endif
+ && ! refers_to_regno_for_reload_p (regno, regno + nwords,
+ PATTERN (this_insn), outloc))
+ {
+ int i;
+ for (i = 0; i < nwords; i++)
+ if (! TEST_HARD_REG_BIT (reg_class_contents[(int) class],
+ regno + i))
+ break;
+
+ if (i == nwords)
+ {
+ if (GET_CODE (real_out) == REG)
+ value = real_out;
+ else
+ value = gen_rtx (REG, outmode, regno);
+ }
+ }
+
+ *inloc = saved_rtx;
+ }
+
+ /* Consider using IN if OUT was not acceptable
+ or if OUT dies in this insn (like the quotient in a divmod insn).
+ We can't use IN unless it is dies in this insn,
+ which means we must know accurately which hard regs are live.
+ Also, the result can't go in IN if IN is used within OUT. */
+ if (hard_regs_live_known
+ && GET_CODE (in) == REG
+ && REGNO (in) < FIRST_PSEUDO_REGISTER
+ && (value == 0
+ || find_reg_note (this_insn, REG_UNUSED, real_out))
+ && find_reg_note (this_insn, REG_DEAD, real_in)
+ && !fixed_regs[REGNO (in)]
+ && HARD_REGNO_MODE_OK (REGNO (in),
+ /* The only case where out and real_out might
+ have different modes is where real_out
+ is a subreg, and in that case, out
+ has a real mode. */
+ (GET_MODE (out) != VOIDmode
+ ? GET_MODE (out) : outmode)))
+ {
+ register int regno = REGNO (in) + in_offset;
+ int nwords = HARD_REGNO_NREGS (regno, inmode);
+
+ if (! refers_to_regno_for_reload_p (regno, regno + nwords, out, NULL_PTR)
+ && ! hard_reg_set_here_p (regno, regno + nwords,
+ PATTERN (this_insn)))
+ {
+ int i;
+ for (i = 0; i < nwords; i++)
+ if (! TEST_HARD_REG_BIT (reg_class_contents[(int) class],
+ regno + i))
+ break;
+
+ if (i == nwords)
+ {
+ /* If we were going to use OUT as the reload reg
+ and changed our mind, it means OUT is a dummy that
+ dies here. So don't bother copying value to it. */
+ if (for_real >= 0 && value == real_out)
+ reload_out[for_real] = 0;
+ if (GET_CODE (real_in) == REG)
+ value = real_in;
+ else
+ value = gen_rtx (REG, inmode, regno);
+ }
+ }
+ }
+
+ return value;
+}
+
+/* This page contains subroutines used mainly for determining
+ whether the IN or an OUT of a reload can serve as the
+ reload register. */
+
+/* Return 1 if X is an operand of an insn that is being earlyclobbered. */
+
+static int
+earlyclobber_operand_p (x)
+ rtx x;
+{
+ int i;
+
+ for (i = 0; i < n_earlyclobbers; i++)
+ if (reload_earlyclobbers[i] == x)
+ return 1;
+
+ return 0;
+}
+
+/* Return 1 if expression X alters a hard reg in the range
+ from BEG_REGNO (inclusive) to END_REGNO (exclusive),
+ either explicitly or in the guise of a pseudo-reg allocated to REGNO.
+ X should be the body of an instruction. */
+
+static int
+hard_reg_set_here_p (beg_regno, end_regno, x)
+ register int beg_regno, end_regno;
+ rtx x;
+{
+ if (GET_CODE (x) == SET || GET_CODE (x) == CLOBBER)
+ {
+ register rtx op0 = SET_DEST (x);
+ while (GET_CODE (op0) == SUBREG)
+ op0 = SUBREG_REG (op0);
+ if (GET_CODE (op0) == REG)
+ {
+ register int r = REGNO (op0);
+ /* See if this reg overlaps range under consideration. */
+ if (r < end_regno
+ && r + HARD_REGNO_NREGS (r, GET_MODE (op0)) > beg_regno)
+ return 1;
+ }
+ }
+ else if (GET_CODE (x) == PARALLEL)
+ {
+ register int i = XVECLEN (x, 0) - 1;
+ for (; i >= 0; i--)
+ if (hard_reg_set_here_p (beg_regno, end_regno, XVECEXP (x, 0, i)))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Return 1 if ADDR is a valid memory address for mode MODE,
+ and check that each pseudo reg has the proper kind of
+ hard reg. */
+
+int
+strict_memory_address_p (mode, addr)
+ enum machine_mode mode;
+ register rtx addr;
+{
+ GO_IF_LEGITIMATE_ADDRESS (mode, addr, win);
+ return 0;
+
+ win:
+ return 1;
+}
+
+/* Like rtx_equal_p except that it allows a REG and a SUBREG to match
+ if they are the same hard reg, and has special hacks for
+ autoincrement and autodecrement.
+ This is specifically intended for find_reloads to use
+ in determining whether two operands match.
+ X is the operand whose number is the lower of the two.
+
+ The value is 2 if Y contains a pre-increment that matches
+ a non-incrementing address in X. */
+
+/* ??? To be completely correct, we should arrange to pass
+ for X the output operand and for Y the input operand.
+ For now, we assume that the output operand has the lower number
+ because that is natural in (SET output (... input ...)). */
+
+int
+operands_match_p (x, y)
+ register rtx x, y;
+{
+ register int i;
+ register RTX_CODE code = GET_CODE (x);
+ register char *fmt;
+ int success_2;
+
+ if (x == y)
+ return 1;
+ if ((code == REG || (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG))
+ && (GET_CODE (y) == REG || (GET_CODE (y) == SUBREG
+ && GET_CODE (SUBREG_REG (y)) == REG)))
+ {
+ register int j;
+
+ if (code == SUBREG)
+ {
+ i = REGNO (SUBREG_REG (x));
+ if (i >= FIRST_PSEUDO_REGISTER)
+ goto slow;
+ i += SUBREG_WORD (x);
+ }
+ else
+ i = REGNO (x);
+
+ if (GET_CODE (y) == SUBREG)
+ {
+ j = REGNO (SUBREG_REG (y));
+ if (j >= FIRST_PSEUDO_REGISTER)
+ goto slow;
+ j += SUBREG_WORD (y);
+ }
+ else
+ j = REGNO (y);
+
+ /* On a WORDS_BIG_ENDIAN machine, point to the last register of a
+ multiple hard register group, so that for example (reg:DI 0) and
+ (reg:SI 1) will be considered the same register. */
+ if (WORDS_BIG_ENDIAN && GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD
+ && i < FIRST_PSEUDO_REGISTER)
+ i += (GET_MODE_SIZE (GET_MODE (x)) / UNITS_PER_WORD) - 1;
+ if (WORDS_BIG_ENDIAN && GET_MODE_SIZE (GET_MODE (y)) > UNITS_PER_WORD
+ && j < FIRST_PSEUDO_REGISTER)
+ j += (GET_MODE_SIZE (GET_MODE (y)) / UNITS_PER_WORD) - 1;
+
+ return i == j;
+ }
+ /* If two operands must match, because they are really a single
+ operand of an assembler insn, then two postincrements are invalid
+ because the assembler insn would increment only once.
+ On the other hand, an postincrement matches ordinary indexing
+ if the postincrement is the output operand. */
+ if (code == POST_DEC || code == POST_INC)
+ return operands_match_p (XEXP (x, 0), y);
+ /* Two preincrements are invalid
+ because the assembler insn would increment only once.
+ On the other hand, an preincrement matches ordinary indexing
+ if the preincrement is the input operand.
+ In this case, return 2, since some callers need to do special
+ things when this happens. */
+ if (GET_CODE (y) == PRE_DEC || GET_CODE (y) == PRE_INC)
+ return operands_match_p (x, XEXP (y, 0)) ? 2 : 0;
+
+ slow:
+
+ /* Now we have disposed of all the cases
+ in which different rtx codes can match. */
+ if (code != GET_CODE (y))
+ return 0;
+ if (code == LABEL_REF)
+ return XEXP (x, 0) == XEXP (y, 0);
+ if (code == SYMBOL_REF)
+ return XSTR (x, 0) == XSTR (y, 0);
+
+ /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. */
+
+ if (GET_MODE (x) != GET_MODE (y))
+ return 0;
+
+ /* Compare the elements. If any pair of corresponding elements
+ fail to match, return 0 for the whole things. */
+
+ success_2 = 0;
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ int val;
+ switch (fmt[i])
+ {
+ case 'w':
+ if (XWINT (x, i) != XWINT (y, i))
+ return 0;
+ break;
+
+ case 'i':
+ if (XINT (x, i) != XINT (y, i))
+ return 0;
+ break;
+
+ case 'e':
+ val = operands_match_p (XEXP (x, i), XEXP (y, i));
+ if (val == 0)
+ return 0;
+ /* If any subexpression returns 2,
+ we should return 2 if we are successful. */
+ if (val == 2)
+ success_2 = 1;
+ break;
+
+ case '0':
+ break;
+
+ /* It is believed that rtx's at this level will never
+ contain anything but integers and other rtx's,
+ except for within LABEL_REFs and SYMBOL_REFs. */
+ default:
+ abort ();
+ }
+ }
+ return 1 + success_2;
+}
+
+/* Return the number of times character C occurs in string S. */
+
+int
+n_occurrences (c, s)
+ char c;
+ char *s;
+{
+ int n = 0;
+ while (*s)
+ n += (*s++ == c);
+ return n;
+}
+
+/* Describe the range of registers or memory referenced by X.
+ If X is a register, set REG_FLAG and put the first register
+ number into START and the last plus one into END.
+ If X is a memory reference, put a base address into BASE
+ and a range of integer offsets into START and END.
+ If X is pushing on the stack, we can assume it causes no trouble,
+ so we set the SAFE field. */
+
+static struct decomposition
+decompose (x)
+ rtx x;
+{
+ struct decomposition val;
+ int all_const = 0;
+
+ val.reg_flag = 0;
+ val.safe = 0;
+ if (GET_CODE (x) == MEM)
+ {
+ rtx base, offset = 0;
+ rtx addr = XEXP (x, 0);
+
+ if (GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == PRE_INC
+ || GET_CODE (addr) == POST_DEC || GET_CODE (addr) == POST_INC)
+ {
+ val.base = XEXP (addr, 0);
+ val.start = - GET_MODE_SIZE (GET_MODE (x));
+ val.end = GET_MODE_SIZE (GET_MODE (x));
+ val.safe = REGNO (val.base) == STACK_POINTER_REGNUM;
+ return val;
+ }
+
+ if (GET_CODE (addr) == CONST)
+ {
+ addr = XEXP (addr, 0);
+ all_const = 1;
+ }
+ if (GET_CODE (addr) == PLUS)
+ {
+ if (CONSTANT_P (XEXP (addr, 0)))
+ {
+ base = XEXP (addr, 1);
+ offset = XEXP (addr, 0);
+ }
+ else if (CONSTANT_P (XEXP (addr, 1)))
+ {
+ base = XEXP (addr, 0);
+ offset = XEXP (addr, 1);
+ }
+ }
+
+ if (offset == 0)
+ {
+ base = addr;
+ offset = const0_rtx;
+ }
+ if (GET_CODE (offset) == CONST)
+ offset = XEXP (offset, 0);
+ if (GET_CODE (offset) == PLUS)
+ {
+ if (GET_CODE (XEXP (offset, 0)) == CONST_INT)
+ {
+ base = gen_rtx (PLUS, GET_MODE (base), base, XEXP (offset, 1));
+ offset = XEXP (offset, 0);
+ }
+ else if (GET_CODE (XEXP (offset, 1)) == CONST_INT)
+ {
+ base = gen_rtx (PLUS, GET_MODE (base), base, XEXP (offset, 0));
+ offset = XEXP (offset, 1);
+ }
+ else
+ {
+ base = gen_rtx (PLUS, GET_MODE (base), base, offset);
+ offset = const0_rtx;
+ }
+ }
+ else if (GET_CODE (offset) != CONST_INT)
+ {
+ base = gen_rtx (PLUS, GET_MODE (base), base, offset);
+ offset = const0_rtx;
+ }
+
+ if (all_const && GET_CODE (base) == PLUS)
+ base = gen_rtx (CONST, GET_MODE (base), base);
+
+ if (GET_CODE (offset) != CONST_INT)
+ abort ();
+
+ val.start = INTVAL (offset);
+ val.end = val.start + GET_MODE_SIZE (GET_MODE (x));
+ val.base = base;
+ return val;
+ }
+ else if (GET_CODE (x) == REG)
+ {
+ val.reg_flag = 1;
+ val.start = true_regnum (x);
+ if (val.start < 0)
+ {
+ /* A pseudo with no hard reg. */
+ val.start = REGNO (x);
+ val.end = val.start + 1;
+ }
+ else
+ /* A hard reg. */
+ val.end = val.start + HARD_REGNO_NREGS (val.start, GET_MODE (x));
+ }
+ else if (GET_CODE (x) == SUBREG)
+ {
+ if (GET_CODE (SUBREG_REG (x)) != REG)
+ /* This could be more precise, but it's good enough. */
+ return decompose (SUBREG_REG (x));
+ val.reg_flag = 1;
+ val.start = true_regnum (x);
+ if (val.start < 0)
+ return decompose (SUBREG_REG (x));
+ else
+ /* A hard reg. */
+ val.end = val.start + HARD_REGNO_NREGS (val.start, GET_MODE (x));
+ }
+ else if (CONSTANT_P (x)
+ /* This hasn't been assigned yet, so it can't conflict yet. */
+ || GET_CODE (x) == SCRATCH)
+ val.safe = 1;
+ else
+ abort ();
+ return val;
+}
+
+/* Return 1 if altering Y will not modify the value of X.
+ Y is also described by YDATA, which should be decompose (Y). */
+
+static int
+immune_p (x, y, ydata)
+ rtx x, y;
+ struct decomposition ydata;
+{
+ struct decomposition xdata;
+
+ if (ydata.reg_flag)
+ return !refers_to_regno_for_reload_p (ydata.start, ydata.end, x, NULL_PTR);
+ if (ydata.safe)
+ return 1;
+
+ if (GET_CODE (y) != MEM)
+ abort ();
+ /* If Y is memory and X is not, Y can't affect X. */
+ if (GET_CODE (x) != MEM)
+ return 1;
+
+ xdata = decompose (x);
+
+ if (! rtx_equal_p (xdata.base, ydata.base))
+ {
+ /* If bases are distinct symbolic constants, there is no overlap. */
+ if (CONSTANT_P (xdata.base) && CONSTANT_P (ydata.base))
+ return 1;
+ /* Constants and stack slots never overlap. */
+ if (CONSTANT_P (xdata.base)
+ && (ydata.base == frame_pointer_rtx
+ || ydata.base == hard_frame_pointer_rtx
+ || ydata.base == stack_pointer_rtx))
+ return 1;
+ if (CONSTANT_P (ydata.base)
+ && (xdata.base == frame_pointer_rtx
+ || xdata.base == hard_frame_pointer_rtx
+ || xdata.base == stack_pointer_rtx))
+ return 1;
+ /* If either base is variable, we don't know anything. */
+ return 0;
+ }
+
+
+ return (xdata.start >= ydata.end || ydata.start >= xdata.end);
+}
+
+/* Similar, but calls decompose. */
+
+int
+safe_from_earlyclobber (op, clobber)
+ rtx op, clobber;
+{
+ struct decomposition early_data;
+
+ early_data = decompose (clobber);
+ return immune_p (op, clobber, early_data);
+}
+
+/* Main entry point of this file: search the body of INSN
+ for values that need reloading and record them with push_reload.
+ REPLACE nonzero means record also where the values occur
+ so that subst_reloads can be used.
+
+ IND_LEVELS says how many levels of indirection are supported by this
+ machine; a value of zero means that a memory reference is not a valid
+ memory address.
+
+ LIVE_KNOWN says we have valid information about which hard
+ regs are live at each point in the program; this is true when
+ we are called from global_alloc but false when stupid register
+ allocation has been done.
+
+ RELOAD_REG_P if nonzero is a vector indexed by hard reg number
+ which is nonnegative if the reg has been commandeered for reloading into.
+ It is copied into STATIC_RELOAD_REG_P and referenced from there
+ by various subroutines. */
+
+void
+find_reloads (insn, replace, ind_levels, live_known, reload_reg_p)
+ rtx insn;
+ int replace, ind_levels;
+ int live_known;
+ short *reload_reg_p;
+{
+#ifdef REGISTER_CONSTRAINTS
+
+ register int insn_code_number;
+ register int i, j;
+ int noperands;
+ /* These are the constraints for the insn. We don't change them. */
+ char *constraints1[MAX_RECOG_OPERANDS];
+ /* These start out as the constraints for the insn
+ and they are chewed up as we consider alternatives. */
+ char *constraints[MAX_RECOG_OPERANDS];
+ /* These are the preferred classes for an operand, or NO_REGS if it isn't
+ a register. */
+ enum reg_class preferred_class[MAX_RECOG_OPERANDS];
+ char pref_or_nothing[MAX_RECOG_OPERANDS];
+ /* Nonzero for a MEM operand whose entire address needs a reload. */
+ int address_reloaded[MAX_RECOG_OPERANDS];
+ /* Value of enum reload_type to use for operand. */
+ enum reload_type operand_type[MAX_RECOG_OPERANDS];
+ /* Value of enum reload_type to use within address of operand. */
+ enum reload_type address_type[MAX_RECOG_OPERANDS];
+ /* Save the usage of each operand. */
+ enum reload_usage { RELOAD_READ, RELOAD_READ_WRITE, RELOAD_WRITE } modified[MAX_RECOG_OPERANDS];
+ int no_input_reloads = 0, no_output_reloads = 0;
+ int n_alternatives;
+ int this_alternative[MAX_RECOG_OPERANDS];
+ char this_alternative_win[MAX_RECOG_OPERANDS];
+ char this_alternative_offmemok[MAX_RECOG_OPERANDS];
+ char this_alternative_earlyclobber[MAX_RECOG_OPERANDS];
+ int this_alternative_matches[MAX_RECOG_OPERANDS];
+ int swapped;
+ int goal_alternative[MAX_RECOG_OPERANDS];
+ int this_alternative_number;
+ int goal_alternative_number;
+ int operand_reloadnum[MAX_RECOG_OPERANDS];
+ int goal_alternative_matches[MAX_RECOG_OPERANDS];
+ int goal_alternative_matched[MAX_RECOG_OPERANDS];
+ char goal_alternative_win[MAX_RECOG_OPERANDS];
+ char goal_alternative_offmemok[MAX_RECOG_OPERANDS];
+ char goal_alternative_earlyclobber[MAX_RECOG_OPERANDS];
+ int goal_alternative_swapped;
+ int best;
+ int commutative;
+ char operands_match[MAX_RECOG_OPERANDS][MAX_RECOG_OPERANDS];
+ rtx substed_operand[MAX_RECOG_OPERANDS];
+ rtx body = PATTERN (insn);
+ rtx set = single_set (insn);
+ int goal_earlyclobber, this_earlyclobber;
+ enum machine_mode operand_mode[MAX_RECOG_OPERANDS];
+
+ this_insn = insn;
+ this_insn_is_asm = 0; /* Tentative. */
+ n_reloads = 0;
+ n_replacements = 0;
+ n_memlocs = 0;
+ n_earlyclobbers = 0;
+ replace_reloads = replace;
+ hard_regs_live_known = live_known;
+ static_reload_reg_p = reload_reg_p;
+
+ /* JUMP_INSNs and CALL_INSNs are not allowed to have any output reloads;
+ neither are insns that SET cc0. Insns that use CC0 are not allowed
+ to have any input reloads. */
+ if (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CALL_INSN)
+ no_output_reloads = 1;
+
+#ifdef HAVE_cc0
+ if (reg_referenced_p (cc0_rtx, PATTERN (insn)))
+ no_input_reloads = 1;
+ if (reg_set_p (cc0_rtx, PATTERN (insn)))
+ no_output_reloads = 1;
+#endif
+
+#ifdef SECONDARY_MEMORY_NEEDED
+ /* The eliminated forms of any secondary memory locations are per-insn, so
+ clear them out here. */
+
+ bzero ((char *) secondary_memlocs_elim, sizeof secondary_memlocs_elim);
+#endif
+
+ /* Find what kind of insn this is. NOPERANDS gets number of operands.
+ Make OPERANDS point to a vector of operand values.
+ Make OPERAND_LOCS point to a vector of pointers to
+ where the operands were found.
+ Fill CONSTRAINTS and CONSTRAINTS1 with pointers to the
+ constraint-strings for this insn.
+ Return if the insn needs no reload processing. */
+
+ switch (GET_CODE (body))
+ {
+ case USE:
+ case CLOBBER:
+ case ASM_INPUT:
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ return;
+
+ case SET:
+ /* Dispose quickly of (set (reg..) (reg..)) if both have hard regs and it
+ is cheap to move between them. If it is not, there may not be an insn
+ to do the copy, so we may need a reload. */
+ if (GET_CODE (SET_DEST (body)) == REG
+ && REGNO (SET_DEST (body)) < FIRST_PSEUDO_REGISTER
+ && GET_CODE (SET_SRC (body)) == REG
+ && REGNO (SET_SRC (body)) < FIRST_PSEUDO_REGISTER
+ && REGISTER_MOVE_COST (REGNO_REG_CLASS (REGNO (SET_SRC (body))),
+ REGNO_REG_CLASS (REGNO (SET_DEST (body)))) == 2)
+ return;
+ case PARALLEL:
+ case ASM_OPERANDS:
+ reload_n_operands = noperands = asm_noperands (body);
+ if (noperands >= 0)
+ {
+ /* This insn is an `asm' with operands. */
+
+ insn_code_number = -1;
+ this_insn_is_asm = 1;
+
+ /* expand_asm_operands makes sure there aren't too many operands. */
+ if (noperands > MAX_RECOG_OPERANDS)
+ abort ();
+
+ /* Now get the operand values and constraints out of the insn. */
+
+ decode_asm_operands (body, recog_operand, recog_operand_loc,
+ constraints, operand_mode);
+ if (noperands > 0)
+ {
+ bcopy ((char *) constraints, (char *) constraints1,
+ noperands * sizeof (char *));
+ n_alternatives = n_occurrences (',', constraints[0]) + 1;
+ for (i = 1; i < noperands; i++)
+ if (n_alternatives != n_occurrences (',', constraints[i]) + 1)
+ {
+ error_for_asm (insn, "operand constraints differ in number of alternatives");
+ /* Avoid further trouble with this insn. */
+ PATTERN (insn) = gen_rtx (USE, VOIDmode, const0_rtx);
+ n_reloads = 0;
+ return;
+ }
+ }
+ break;
+ }
+
+ default:
+ /* Ordinary insn: recognize it, get the operands via insn_extract
+ and get the constraints. */
+
+ insn_code_number = recog_memoized (insn);
+ if (insn_code_number < 0)
+ fatal_insn_not_found (insn);
+
+ reload_n_operands = noperands = insn_n_operands[insn_code_number];
+ n_alternatives = insn_n_alternatives[insn_code_number];
+ /* Just return "no reloads" if insn has no operands with constraints. */
+ if (n_alternatives == 0)
+ return;
+ insn_extract (insn);
+ for (i = 0; i < noperands; i++)
+ {
+ constraints[i] = constraints1[i]
+ = insn_operand_constraint[insn_code_number][i];
+ operand_mode[i] = insn_operand_mode[insn_code_number][i];
+ }
+ }
+
+ if (noperands == 0)
+ return;
+
+ commutative = -1;
+
+ /* If we will need to know, later, whether some pair of operands
+ are the same, we must compare them now and save the result.
+ Reloading the base and index registers will clobber them
+ and afterward they will fail to match. */
+
+ for (i = 0; i < noperands; i++)
+ {
+ register char *p;
+ register int c;
+
+ substed_operand[i] = recog_operand[i];
+ p = constraints[i];
+
+ modified[i] = RELOAD_READ;
+
+ /* Scan this operand's constraint to see if it is an output operand,
+ an in-out operand, is commutative, or should match another. */
+
+ while (c = *p++)
+ {
+ if (c == '=')
+ modified[i] = RELOAD_WRITE;
+ else if (c == '+')
+ modified[i] = RELOAD_READ_WRITE;
+ else if (c == '%')
+ {
+ /* The last operand should not be marked commutative. */
+ if (i == noperands - 1)
+ {
+ if (this_insn_is_asm)
+ warning_for_asm (this_insn,
+ "`%%' constraint used with last operand");
+ else
+ abort ();
+ }
+ else
+ commutative = i;
+ }
+ else if (c >= '0' && c <= '9')
+ {
+ c -= '0';
+ operands_match[c][i]
+ = operands_match_p (recog_operand[c], recog_operand[i]);
+
+ /* An operand may not match itself. */
+ if (c == i)
+ {
+ if (this_insn_is_asm)
+ warning_for_asm (this_insn,
+ "operand %d has constraint %d", i, c);
+ else
+ abort ();
+ }
+
+ /* If C can be commuted with C+1, and C might need to match I,
+ then C+1 might also need to match I. */
+ if (commutative >= 0)
+ {
+ if (c == commutative || c == commutative + 1)
+ {
+ int other = c + (c == commutative ? 1 : -1);
+ operands_match[other][i]
+ = operands_match_p (recog_operand[other], recog_operand[i]);
+ }
+ if (i == commutative || i == commutative + 1)
+ {
+ int other = i + (i == commutative ? 1 : -1);
+ operands_match[c][other]
+ = operands_match_p (recog_operand[c], recog_operand[other]);
+ }
+ /* Note that C is supposed to be less than I.
+ No need to consider altering both C and I because in
+ that case we would alter one into the other. */
+ }
+ }
+ }
+ }
+
+ /* Examine each operand that is a memory reference or memory address
+ and reload parts of the addresses into index registers.
+ Also here any references to pseudo regs that didn't get hard regs
+ but are equivalent to constants get replaced in the insn itself
+ with those constants. Nobody will ever see them again.
+
+ Finally, set up the preferred classes of each operand. */
+
+ for (i = 0; i < noperands; i++)
+ {
+ register RTX_CODE code = GET_CODE (recog_operand[i]);
+
+ address_reloaded[i] = 0;
+ operand_type[i] = (modified[i] == RELOAD_READ ? RELOAD_FOR_INPUT
+ : modified[i] == RELOAD_WRITE ? RELOAD_FOR_OUTPUT
+ : RELOAD_OTHER);
+ address_type[i]
+ = (modified[i] == RELOAD_READ ? RELOAD_FOR_INPUT_ADDRESS
+ : modified[i] == RELOAD_WRITE ? RELOAD_FOR_OUTPUT_ADDRESS
+ : RELOAD_OTHER);
+
+ if (*constraints[i] == 0)
+ /* Ignore things like match_operator operands. */
+ ;
+ else if (constraints[i][0] == 'p')
+ {
+ find_reloads_address (VOIDmode, NULL_PTR,
+ recog_operand[i], recog_operand_loc[i],
+ i, operand_type[i], ind_levels);
+ substed_operand[i] = recog_operand[i] = *recog_operand_loc[i];
+ }
+ else if (code == MEM)
+ {
+ if (find_reloads_address (GET_MODE (recog_operand[i]),
+ recog_operand_loc[i],
+ XEXP (recog_operand[i], 0),
+ &XEXP (recog_operand[i], 0),
+ i, address_type[i], ind_levels))
+ address_reloaded[i] = 1;
+ substed_operand[i] = recog_operand[i] = *recog_operand_loc[i];
+ }
+ else if (code == SUBREG)
+ substed_operand[i] = recog_operand[i] = *recog_operand_loc[i]
+ = find_reloads_toplev (recog_operand[i], i, address_type[i],
+ ind_levels,
+ set != 0
+ && &SET_DEST (set) == recog_operand_loc[i]);
+ else if (code == PLUS)
+ /* We can get a PLUS as an "operand" as a result of
+ register elimination. See eliminate_regs and gen_input_reload. */
+ substed_operand[i] = recog_operand[i] = *recog_operand_loc[i]
+ = find_reloads_toplev (recog_operand[i], i, address_type[i],
+ ind_levels, 0);
+ else if (code == REG)
+ {
+ /* This is equivalent to calling find_reloads_toplev.
+ The code is duplicated for speed.
+ When we find a pseudo always equivalent to a constant,
+ we replace it by the constant. We must be sure, however,
+ that we don't try to replace it in the insn in which it
+ is being set. */
+ register int regno = REGNO (recog_operand[i]);
+ if (reg_equiv_constant[regno] != 0
+ && (set == 0 || &SET_DEST (set) != recog_operand_loc[i]))
+ substed_operand[i] = recog_operand[i]
+ = reg_equiv_constant[regno];
+#if 0 /* This might screw code in reload1.c to delete prior output-reload
+ that feeds this insn. */
+ if (reg_equiv_mem[regno] != 0)
+ substed_operand[i] = recog_operand[i]
+ = reg_equiv_mem[regno];
+#endif
+ if (reg_equiv_address[regno] != 0)
+ {
+ /* If reg_equiv_address is not a constant address, copy it,
+ since it may be shared. */
+ rtx address = reg_equiv_address[regno];
+
+ if (rtx_varies_p (address))
+ address = copy_rtx (address);
+
+ /* If this is an output operand, we must output a CLOBBER
+ after INSN so find_equiv_reg knows REGNO is being written.
+ Mark this insn specially, do we can put our output reloads
+ after it. */
+
+ if (modified[i] != RELOAD_READ)
+ PUT_MODE (emit_insn_after (gen_rtx (CLOBBER, VOIDmode,
+ recog_operand[i]),
+ insn),
+ DImode);
+
+ *recog_operand_loc[i] = recog_operand[i]
+ = gen_rtx (MEM, GET_MODE (recog_operand[i]), address);
+ RTX_UNCHANGING_P (recog_operand[i])
+ = RTX_UNCHANGING_P (regno_reg_rtx[regno]);
+ find_reloads_address (GET_MODE (recog_operand[i]),
+ recog_operand_loc[i],
+ XEXP (recog_operand[i], 0),
+ &XEXP (recog_operand[i], 0),
+ i, address_type[i], ind_levels);
+ substed_operand[i] = recog_operand[i] = *recog_operand_loc[i];
+ }
+ }
+ /* If the operand is still a register (we didn't replace it with an
+ equivalent), get the preferred class to reload it into. */
+ code = GET_CODE (recog_operand[i]);
+ preferred_class[i]
+ = ((code == REG && REGNO (recog_operand[i]) >= FIRST_PSEUDO_REGISTER)
+ ? reg_preferred_class (REGNO (recog_operand[i])) : NO_REGS);
+ pref_or_nothing[i]
+ = (code == REG && REGNO (recog_operand[i]) >= FIRST_PSEUDO_REGISTER
+ && reg_alternate_class (REGNO (recog_operand[i])) == NO_REGS);
+ }
+
+ /* If this is simply a copy from operand 1 to operand 0, merge the
+ preferred classes for the operands. */
+ if (set != 0 && noperands >= 2 && recog_operand[0] == SET_DEST (set)
+ && recog_operand[1] == SET_SRC (set))
+ {
+ preferred_class[0] = preferred_class[1]
+ = reg_class_subunion[(int) preferred_class[0]][(int) preferred_class[1]];
+ pref_or_nothing[0] |= pref_or_nothing[1];
+ pref_or_nothing[1] |= pref_or_nothing[0];
+ }
+
+ /* Now see what we need for pseudo-regs that didn't get hard regs
+ or got the wrong kind of hard reg. For this, we must consider
+ all the operands together against the register constraints. */
+
+ best = MAX_RECOG_OPERANDS + 300;
+
+ swapped = 0;
+ goal_alternative_swapped = 0;
+ try_swapped:
+
+ /* The constraints are made of several alternatives.
+ Each operand's constraint looks like foo,bar,... with commas
+ separating the alternatives. The first alternatives for all
+ operands go together, the second alternatives go together, etc.
+
+ First loop over alternatives. */
+
+ for (this_alternative_number = 0;
+ this_alternative_number < n_alternatives;
+ this_alternative_number++)
+ {
+ /* Loop over operands for one constraint alternative. */
+ /* LOSERS counts those that don't fit this alternative
+ and would require loading. */
+ int losers = 0;
+ /* BAD is set to 1 if it some operand can't fit this alternative
+ even after reloading. */
+ int bad = 0;
+ /* REJECT is a count of how undesirable this alternative says it is
+ if any reloading is required. If the alternative matches exactly
+ then REJECT is ignored, but otherwise it gets this much
+ counted against it in addition to the reloading needed. Each
+ ? counts three times here since we want the disparaging caused by
+ a bad register class to only count 1/3 as much. */
+ int reject = 0;
+
+ this_earlyclobber = 0;
+
+ for (i = 0; i < noperands; i++)
+ {
+ register char *p = constraints[i];
+ register int win = 0;
+ /* 0 => this operand can be reloaded somehow for this alternative */
+ int badop = 1;
+ /* 0 => this operand can be reloaded if the alternative allows regs. */
+ int winreg = 0;
+ int c;
+ register rtx operand = recog_operand[i];
+ int offset = 0;
+ /* Nonzero means this is a MEM that must be reloaded into a reg
+ regardless of what the constraint says. */
+ int force_reload = 0;
+ int offmemok = 0;
+ /* Nonzero if a constant forced into memory would be OK for this
+ operand. */
+ int constmemok = 0;
+ int earlyclobber = 0;
+
+ /* If the operand is a SUBREG, extract
+ the REG or MEM (or maybe even a constant) within.
+ (Constants can occur as a result of reg_equiv_constant.) */
+
+ while (GET_CODE (operand) == SUBREG)
+ {
+ offset += SUBREG_WORD (operand);
+ operand = SUBREG_REG (operand);
+ /* Force reload if this is a constant or PLUS or if there may may
+ be a problem accessing OPERAND in the outer mode. */
+ if (CONSTANT_P (operand)
+ || GET_CODE (operand) == PLUS
+ /* We must force a reload of paradoxical SUBREGs
+ of a MEM because the alignment of the inner value
+ may not be enough to do the outer reference.
+
+ On machines that extend byte operations and we have a
+ SUBREG where both the inner and outer modes are different
+ size but no wider than a word, combine.c has made
+ assumptions about the behavior of the machine in such
+ register access. If the data is, in fact, in memory we
+ must always load using the size assumed to be in the
+ register and let the insn do the different-sized
+ accesses. */
+ || ((GET_CODE (operand) == MEM
+ || (GET_CODE (operand)== REG
+ && REGNO (operand) >= FIRST_PSEUDO_REGISTER))
+ && (((GET_MODE_BITSIZE (GET_MODE (operand))
+ < BIGGEST_ALIGNMENT)
+ && (GET_MODE_SIZE (operand_mode[i])
+ > GET_MODE_SIZE (GET_MODE (operand))))
+#ifdef LOAD_EXTEND_OP
+ || (GET_MODE_SIZE (operand_mode[i]) <= UNITS_PER_WORD
+ && (GET_MODE_SIZE (GET_MODE (operand))
+ <= UNITS_PER_WORD)
+ && (GET_MODE_SIZE (operand_mode[i])
+ != GET_MODE_SIZE (GET_MODE (operand))))
+#endif
+ ))
+ /* Subreg of a hard reg which can't handle the subreg's mode
+ or which would handle that mode in the wrong number of
+ registers for subregging to work. */
+ || (GET_CODE (operand) == REG
+ && REGNO (operand) < FIRST_PSEUDO_REGISTER
+ && ((GET_MODE_SIZE (operand_mode[i]) <= UNITS_PER_WORD
+ && (GET_MODE_SIZE (GET_MODE (operand))
+ > UNITS_PER_WORD)
+ && ((GET_MODE_SIZE (GET_MODE (operand))
+ / UNITS_PER_WORD)
+ != HARD_REGNO_NREGS (REGNO (operand),
+ GET_MODE (operand))))
+ || ! HARD_REGNO_MODE_OK (REGNO (operand) + offset,
+ operand_mode[i]))))
+ force_reload = 1;
+ }
+
+ this_alternative[i] = (int) NO_REGS;
+ this_alternative_win[i] = 0;
+ this_alternative_offmemok[i] = 0;
+ this_alternative_earlyclobber[i] = 0;
+ this_alternative_matches[i] = -1;
+
+ /* An empty constraint or empty alternative
+ allows anything which matched the pattern. */
+ if (*p == 0 || *p == ',')
+ win = 1, badop = 0;
+
+ /* Scan this alternative's specs for this operand;
+ set WIN if the operand fits any letter in this alternative.
+ Otherwise, clear BADOP if this operand could
+ fit some letter after reloads,
+ or set WINREG if this operand could fit after reloads
+ provided the constraint allows some registers. */
+
+ while (*p && (c = *p++) != ',')
+ switch (c)
+ {
+ case '=':
+ case '+':
+ case '*':
+ break;
+
+ case '%':
+ /* The last operand should not be marked commutative. */
+ if (i != noperands - 1)
+ commutative = i;
+ break;
+
+ case '?':
+ reject += 3;
+ break;
+
+ case '!':
+ reject = 300;
+ break;
+
+ case '#':
+ /* Ignore rest of this alternative as far as
+ reloading is concerned. */
+ while (*p && *p != ',') p++;
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ c -= '0';
+ this_alternative_matches[i] = c;
+ /* We are supposed to match a previous operand.
+ If we do, we win if that one did.
+ If we do not, count both of the operands as losers.
+ (This is too conservative, since most of the time
+ only a single reload insn will be needed to make
+ the two operands win. As a result, this alternative
+ may be rejected when it is actually desirable.) */
+ if ((swapped && (c != commutative || i != commutative + 1))
+ /* If we are matching as if two operands were swapped,
+ also pretend that operands_match had been computed
+ with swapped.
+ But if I is the second of those and C is the first,
+ don't exchange them, because operands_match is valid
+ only on one side of its diagonal. */
+ ? (operands_match
+ [(c == commutative || c == commutative + 1)
+ ? 2*commutative + 1 - c : c]
+ [(i == commutative || i == commutative + 1)
+ ? 2*commutative + 1 - i : i])
+ : operands_match[c][i])
+ win = this_alternative_win[c];
+ else
+ {
+ /* Operands don't match. */
+ rtx value;
+ /* Retroactively mark the operand we had to match
+ as a loser, if it wasn't already. */
+ if (this_alternative_win[c])
+ losers++;
+ this_alternative_win[c] = 0;
+ if (this_alternative[c] == (int) NO_REGS)
+ bad = 1;
+ /* But count the pair only once in the total badness of
+ this alternative, if the pair can be a dummy reload. */
+ value
+ = find_dummy_reload (recog_operand[i], recog_operand[c],
+ recog_operand_loc[i], recog_operand_loc[c],
+ operand_mode[i], operand_mode[c],
+ this_alternative[c], -1);
+
+ if (value != 0)
+ losers--;
+ }
+ /* This can be fixed with reloads if the operand
+ we are supposed to match can be fixed with reloads. */
+ badop = 0;
+ this_alternative[i] = this_alternative[c];
+
+ /* If we have to reload this operand and some previous
+ operand also had to match the same thing as this
+ operand, we don't know how to do that. So reject this
+ alternative. */
+ if (! win || force_reload)
+ for (j = 0; j < i; j++)
+ if (this_alternative_matches[j]
+ == this_alternative_matches[i])
+ badop = 1;
+
+ break;
+
+ case 'p':
+ /* All necessary reloads for an address_operand
+ were handled in find_reloads_address. */
+ this_alternative[i] = (int) ALL_REGS;
+ win = 1;
+ break;
+
+ case 'm':
+ if (force_reload)
+ break;
+ if (GET_CODE (operand) == MEM
+ || (GET_CODE (operand) == REG
+ && REGNO (operand) >= FIRST_PSEUDO_REGISTER
+ && reg_renumber[REGNO (operand)] < 0))
+ win = 1;
+ if (CONSTANT_P (operand))
+ badop = 0;
+ constmemok = 1;
+ break;
+
+ case '<':
+ if (GET_CODE (operand) == MEM
+ && ! address_reloaded[i]
+ && (GET_CODE (XEXP (operand, 0)) == PRE_DEC
+ || GET_CODE (XEXP (operand, 0)) == POST_DEC))
+ win = 1;
+ break;
+
+ case '>':
+ if (GET_CODE (operand) == MEM
+ && ! address_reloaded[i]
+ && (GET_CODE (XEXP (operand, 0)) == PRE_INC
+ || GET_CODE (XEXP (operand, 0)) == POST_INC))
+ win = 1;
+ break;
+
+ /* Memory operand whose address is not offsettable. */
+ case 'V':
+ if (force_reload)
+ break;
+ if (GET_CODE (operand) == MEM
+ && ! (ind_levels ? offsettable_memref_p (operand)
+ : offsettable_nonstrict_memref_p (operand))
+ /* Certain mem addresses will become offsettable
+ after they themselves are reloaded. This is important;
+ we don't want our own handling of unoffsettables
+ to override the handling of reg_equiv_address. */
+ && !(GET_CODE (XEXP (operand, 0)) == REG
+ && (ind_levels == 0
+ || reg_equiv_address[REGNO (XEXP (operand, 0))] != 0)))
+ win = 1;
+ break;
+
+ /* Memory operand whose address is offsettable. */
+ case 'o':
+ if (force_reload)
+ break;
+ if ((GET_CODE (operand) == MEM
+ /* If IND_LEVELS, find_reloads_address won't reload a
+ pseudo that didn't get a hard reg, so we have to
+ reject that case. */
+ && (ind_levels ? offsettable_memref_p (operand)
+ : offsettable_nonstrict_memref_p (operand)))
+ /* Certain mem addresses will become offsettable
+ after they themselves are reloaded. This is important;
+ we don't want our own handling of unoffsettables
+ to override the handling of reg_equiv_address. */
+ || (GET_CODE (operand) == MEM
+ && GET_CODE (XEXP (operand, 0)) == REG
+ && (ind_levels == 0
+ || reg_equiv_address[REGNO (XEXP (operand, 0))] != 0))
+ || (GET_CODE (operand) == REG
+ && REGNO (operand) >= FIRST_PSEUDO_REGISTER
+ && reg_renumber[REGNO (operand)] < 0
+ /* If reg_equiv_address is nonzero, we will be
+ loading it into a register; hence it will be
+ offsettable, but we cannot say that reg_equiv_mem
+ is offsettable without checking. */
+ && ((reg_equiv_mem[REGNO (operand)] != 0
+ && offsettable_memref_p (reg_equiv_mem[REGNO (operand)]))
+ || (reg_equiv_address[REGNO (operand)] != 0))))
+ win = 1;
+ if (CONSTANT_P (operand) || GET_CODE (operand) == MEM)
+ badop = 0;
+ constmemok = 1;
+ offmemok = 1;
+ break;
+
+ case '&':
+ /* Output operand that is stored before the need for the
+ input operands (and their index registers) is over. */
+ earlyclobber = 1, this_earlyclobber = 1;
+ break;
+
+ case 'E':
+ /* Match any floating double constant, but only if
+ we can examine the bits of it reliably. */
+ if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
+ || HOST_BITS_PER_WIDE_INT != BITS_PER_WORD)
+ && GET_MODE (operand) != VOIDmode && ! flag_pretend_float)
+ break;
+ if (GET_CODE (operand) == CONST_DOUBLE)
+ win = 1;
+ break;
+
+ case 'F':
+ if (GET_CODE (operand) == CONST_DOUBLE)
+ win = 1;
+ break;
+
+ case 'G':
+ case 'H':
+ if (GET_CODE (operand) == CONST_DOUBLE
+ && CONST_DOUBLE_OK_FOR_LETTER_P (operand, c))
+ win = 1;
+ break;
+
+ case 's':
+ if (GET_CODE (operand) == CONST_INT
+ || (GET_CODE (operand) == CONST_DOUBLE
+ && GET_MODE (operand) == VOIDmode))
+ break;
+ case 'i':
+ if (CONSTANT_P (operand)
+#ifdef LEGITIMATE_PIC_OPERAND_P
+ && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (operand))
+#endif
+ )
+ win = 1;
+ break;
+
+ case 'n':
+ if (GET_CODE (operand) == CONST_INT
+ || (GET_CODE (operand) == CONST_DOUBLE
+ && GET_MODE (operand) == VOIDmode))
+ win = 1;
+ break;
+
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ if (GET_CODE (operand) == CONST_INT
+ && CONST_OK_FOR_LETTER_P (INTVAL (operand), c))
+ win = 1;
+ break;
+
+ case 'X':
+ win = 1;
+ break;
+
+ case 'g':
+ if (! force_reload
+ /* A PLUS is never a valid operand, but reload can make
+ it from a register when eliminating registers. */
+ && GET_CODE (operand) != PLUS
+ /* A SCRATCH is not a valid operand. */
+ && GET_CODE (operand) != SCRATCH
+#ifdef LEGITIMATE_PIC_OPERAND_P
+ && (! CONSTANT_P (operand)
+ || ! flag_pic
+ || LEGITIMATE_PIC_OPERAND_P (operand))
+#endif
+ && (GENERAL_REGS == ALL_REGS
+ || GET_CODE (operand) != REG
+ || (REGNO (operand) >= FIRST_PSEUDO_REGISTER
+ && reg_renumber[REGNO (operand)] < 0)))
+ win = 1;
+ /* Drop through into 'r' case */
+
+ case 'r':
+ this_alternative[i]
+ = (int) reg_class_subunion[this_alternative[i]][(int) GENERAL_REGS];
+ goto reg;
+
+#ifdef EXTRA_CONSTRAINT
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ if (EXTRA_CONSTRAINT (operand, c))
+ win = 1;
+ break;
+#endif
+
+ default:
+ this_alternative[i]
+ = (int) reg_class_subunion[this_alternative[i]][(int) REG_CLASS_FROM_LETTER (c)];
+
+ reg:
+ if (GET_MODE (operand) == BLKmode)
+ break;
+ winreg = 1;
+ if (GET_CODE (operand) == REG
+ && reg_fits_class_p (operand, this_alternative[i],
+ offset, GET_MODE (recog_operand[i])))
+ win = 1;
+ break;
+ }
+
+ constraints[i] = p;
+
+ /* If this operand could be handled with a reg,
+ and some reg is allowed, then this operand can be handled. */
+ if (winreg && this_alternative[i] != (int) NO_REGS)
+ badop = 0;
+
+ /* Record which operands fit this alternative. */
+ this_alternative_earlyclobber[i] = earlyclobber;
+ if (win && ! force_reload)
+ this_alternative_win[i] = 1;
+ else
+ {
+ int const_to_mem = 0;
+
+ this_alternative_offmemok[i] = offmemok;
+ losers++;
+ if (badop)
+ bad = 1;
+ /* Alternative loses if it has no regs for a reg operand. */
+ if (GET_CODE (operand) == REG
+ && this_alternative[i] == (int) NO_REGS
+ && this_alternative_matches[i] < 0)
+ bad = 1;
+
+ /* Alternative loses if it requires a type of reload not
+ permitted for this insn. We can always reload SCRATCH
+ and objects with a REG_UNUSED note. */
+ if (GET_CODE (operand) != SCRATCH
+ && modified[i] != RELOAD_READ && no_output_reloads
+ && ! find_reg_note (insn, REG_UNUSED, operand))
+ bad = 1;
+ else if (modified[i] != RELOAD_WRITE && no_input_reloads)
+ bad = 1;
+
+ /* If this is a constant that is reloaded into the desired
+ class by copying it to memory first, count that as another
+ reload. This is consistent with other code and is
+ required to avoid chosing another alternative when
+ the constant is moved into memory by this function on
+ an early reload pass. Note that the test here is
+ precisely the same as in the code below that calls
+ force_const_mem. */
+ if (CONSTANT_P (operand)
+ /* force_const_mem does not accept HIGH. */
+ && GET_CODE (operand) != HIGH
+ && (PREFERRED_RELOAD_CLASS (operand,
+ (enum reg_class) this_alternative[i])
+ == NO_REGS)
+ && operand_mode[i] != VOIDmode)
+ {
+ const_to_mem = 1;
+ if (this_alternative[i] != (int) NO_REGS)
+ losers++;
+ }
+
+ /* If we can't reload this value at all, reject this
+ alternative. Note that we could also lose due to
+ LIMIT_RELOAD_RELOAD_CLASS, but we don't check that
+ here. */
+
+ if (! CONSTANT_P (operand)
+ && (enum reg_class) this_alternative[i] != NO_REGS
+ && (PREFERRED_RELOAD_CLASS (operand,
+ (enum reg_class) this_alternative[i])
+ == NO_REGS))
+ bad = 1;
+
+ /* We prefer to reload pseudos over reloading other things,
+ since such reloads may be able to be eliminated later.
+ If we are reloading a SCRATCH, we won't be generating any
+ insns, just using a register, so it is also preferred.
+ So bump REJECT in other cases. Don't do this in the
+ case where we are forcing a constant into memory and
+ it will then win since we don't want to have a different
+ alternative match then. */
+ if (! (GET_CODE (operand) == REG
+ && REGNO (operand) >= FIRST_PSEUDO_REGISTER)
+ && GET_CODE (operand) != SCRATCH
+ && ! (const_to_mem && constmemok))
+ reject++;
+ }
+
+ /* If this operand is a pseudo register that didn't get a hard
+ reg and this alternative accepts some register, see if the
+ class that we want is a subset of the preferred class for this
+ register. If not, but it intersects that class, use the
+ preferred class instead. If it does not intersect the preferred
+ class, show that usage of this alternative should be discouraged;
+ it will be discouraged more still if the register is `preferred
+ or nothing'. We do this because it increases the chance of
+ reusing our spill register in a later insn and avoiding a pair
+ of memory stores and loads.
+
+ Don't bother with this if this alternative will accept this
+ operand.
+
+ Don't do this for a multiword operand, since it is only a
+ small win and has the risk of requiring more spill registers,
+ which could cause a large loss.
+
+ Don't do this if the preferred class has only one register
+ because we might otherwise exhaust the class. */
+
+
+ if (! win && this_alternative[i] != (int) NO_REGS
+ && GET_MODE_SIZE (operand_mode[i]) <= UNITS_PER_WORD
+ && reg_class_size[(int) preferred_class[i]] > 1)
+ {
+ if (! reg_class_subset_p (this_alternative[i],
+ preferred_class[i]))
+ {
+ /* Since we don't have a way of forming the intersection,
+ we just do something special if the preferred class
+ is a subset of the class we have; that's the most
+ common case anyway. */
+ if (reg_class_subset_p (preferred_class[i],
+ this_alternative[i]))
+ this_alternative[i] = (int) preferred_class[i];
+ else
+ reject += (1 + pref_or_nothing[i]);
+ }
+ }
+ }
+
+ /* Now see if any output operands that are marked "earlyclobber"
+ in this alternative conflict with any input operands
+ or any memory addresses. */
+
+ for (i = 0; i < noperands; i++)
+ if (this_alternative_earlyclobber[i]
+ && this_alternative_win[i])
+ {
+ struct decomposition early_data;
+
+ early_data = decompose (recog_operand[i]);
+
+ if (modified[i] == RELOAD_READ)
+ {
+ if (this_insn_is_asm)
+ warning_for_asm (this_insn,
+ "`&' constraint used with input operand");
+ else
+ abort ();
+ continue;
+ }
+
+ if (this_alternative[i] == NO_REGS)
+ {
+ this_alternative_earlyclobber[i] = 0;
+ if (this_insn_is_asm)
+ error_for_asm (this_insn,
+ "`&' constraint used with no register class");
+ else
+ abort ();
+ }
+
+ for (j = 0; j < noperands; j++)
+ /* Is this an input operand or a memory ref? */
+ if ((GET_CODE (recog_operand[j]) == MEM
+ || modified[j] != RELOAD_WRITE)
+ && j != i
+ /* Ignore things like match_operator operands. */
+ && *constraints1[j] != 0
+ /* Don't count an input operand that is constrained to match
+ the early clobber operand. */
+ && ! (this_alternative_matches[j] == i
+ && rtx_equal_p (recog_operand[i], recog_operand[j]))
+ /* Is it altered by storing the earlyclobber operand? */
+ && !immune_p (recog_operand[j], recog_operand[i], early_data))
+ {
+ /* If the output is in a single-reg class,
+ it's costly to reload it, so reload the input instead. */
+ if (reg_class_size[this_alternative[i]] == 1
+ && (GET_CODE (recog_operand[j]) == REG
+ || GET_CODE (recog_operand[j]) == SUBREG))
+ {
+ losers++;
+ this_alternative_win[j] = 0;
+ }
+ else
+ break;
+ }
+ /* If an earlyclobber operand conflicts with something,
+ it must be reloaded, so request this and count the cost. */
+ if (j != noperands)
+ {
+ losers++;
+ this_alternative_win[i] = 0;
+ for (j = 0; j < noperands; j++)
+ if (this_alternative_matches[j] == i
+ && this_alternative_win[j])
+ {
+ this_alternative_win[j] = 0;
+ losers++;
+ }
+ }
+ }
+
+ /* If one alternative accepts all the operands, no reload required,
+ choose that alternative; don't consider the remaining ones. */
+ if (losers == 0)
+ {
+ /* Unswap these so that they are never swapped at `finish'. */
+ if (commutative >= 0)
+ {
+ recog_operand[commutative] = substed_operand[commutative];
+ recog_operand[commutative + 1]
+ = substed_operand[commutative + 1];
+ }
+ for (i = 0; i < noperands; i++)
+ {
+ goal_alternative_win[i] = 1;
+ goal_alternative[i] = this_alternative[i];
+ goal_alternative_offmemok[i] = this_alternative_offmemok[i];
+ goal_alternative_matches[i] = this_alternative_matches[i];
+ goal_alternative_earlyclobber[i]
+ = this_alternative_earlyclobber[i];
+ }
+ goal_alternative_number = this_alternative_number;
+ goal_alternative_swapped = swapped;
+ goal_earlyclobber = this_earlyclobber;
+ goto finish;
+ }
+
+ /* REJECT, set by the ! and ? constraint characters and when a register
+ would be reloaded into a non-preferred class, discourages the use of
+ this alternative for a reload goal. REJECT is incremented by three
+ for each ? and one for each non-preferred class. */
+ losers = losers * 3 + reject;
+
+ /* If this alternative can be made to work by reloading,
+ and it needs less reloading than the others checked so far,
+ record it as the chosen goal for reloading. */
+ if (! bad && best > losers)
+ {
+ for (i = 0; i < noperands; i++)
+ {
+ goal_alternative[i] = this_alternative[i];
+ goal_alternative_win[i] = this_alternative_win[i];
+ goal_alternative_offmemok[i] = this_alternative_offmemok[i];
+ goal_alternative_matches[i] = this_alternative_matches[i];
+ goal_alternative_earlyclobber[i]
+ = this_alternative_earlyclobber[i];
+ }
+ goal_alternative_swapped = swapped;
+ best = losers;
+ goal_alternative_number = this_alternative_number;
+ goal_earlyclobber = this_earlyclobber;
+ }
+ }
+
+ /* If insn is commutative (it's safe to exchange a certain pair of operands)
+ then we need to try each alternative twice,
+ the second time matching those two operands
+ as if we had exchanged them.
+ To do this, really exchange them in operands.
+
+ If we have just tried the alternatives the second time,
+ return operands to normal and drop through. */
+
+ if (commutative >= 0)
+ {
+ swapped = !swapped;
+ if (swapped)
+ {
+ register enum reg_class tclass;
+ register int t;
+
+ recog_operand[commutative] = substed_operand[commutative + 1];
+ recog_operand[commutative + 1] = substed_operand[commutative];
+
+ tclass = preferred_class[commutative];
+ preferred_class[commutative] = preferred_class[commutative + 1];
+ preferred_class[commutative + 1] = tclass;
+
+ t = pref_or_nothing[commutative];
+ pref_or_nothing[commutative] = pref_or_nothing[commutative + 1];
+ pref_or_nothing[commutative + 1] = t;
+
+ bcopy ((char *) constraints1, (char *) constraints,
+ noperands * sizeof (char *));
+ goto try_swapped;
+ }
+ else
+ {
+ recog_operand[commutative] = substed_operand[commutative];
+ recog_operand[commutative + 1] = substed_operand[commutative + 1];
+ }
+ }
+
+ /* The operands don't meet the constraints.
+ goal_alternative describes the alternative
+ that we could reach by reloading the fewest operands.
+ Reload so as to fit it. */
+
+ if (best == MAX_RECOG_OPERANDS + 300)
+ {
+ /* No alternative works with reloads?? */
+ if (insn_code_number >= 0)
+ abort ();
+ error_for_asm (insn, "inconsistent operand constraints in an `asm'");
+ /* Avoid further trouble with this insn. */
+ PATTERN (insn) = gen_rtx (USE, VOIDmode, const0_rtx);
+ n_reloads = 0;
+ return;
+ }
+
+ /* Jump to `finish' from above if all operands are valid already.
+ In that case, goal_alternative_win is all 1. */
+ finish:
+
+ /* Right now, for any pair of operands I and J that are required to match,
+ with I < J,
+ goal_alternative_matches[J] is I.
+ Set up goal_alternative_matched as the inverse function:
+ goal_alternative_matched[I] = J. */
+
+ for (i = 0; i < noperands; i++)
+ goal_alternative_matched[i] = -1;
+
+ for (i = 0; i < noperands; i++)
+ if (! goal_alternative_win[i]
+ && goal_alternative_matches[i] >= 0)
+ goal_alternative_matched[goal_alternative_matches[i]] = i;
+
+ /* If the best alternative is with operands 1 and 2 swapped,
+ consider them swapped before reporting the reloads. Update the
+ operand numbers of any reloads already pushed. */
+
+ if (goal_alternative_swapped)
+ {
+ register rtx tem;
+
+ tem = substed_operand[commutative];
+ substed_operand[commutative] = substed_operand[commutative + 1];
+ substed_operand[commutative + 1] = tem;
+ tem = recog_operand[commutative];
+ recog_operand[commutative] = recog_operand[commutative + 1];
+ recog_operand[commutative + 1] = tem;
+
+ for (i = 0; i < n_reloads; i++)
+ {
+ if (reload_opnum[i] == commutative)
+ reload_opnum[i] = commutative + 1;
+ else if (reload_opnum[i] == commutative + 1)
+ reload_opnum[i] = commutative;
+ }
+ }
+
+ /* Perform whatever substitutions on the operands we are supposed
+ to make due to commutativity or replacement of registers
+ with equivalent constants or memory slots. */
+
+ for (i = 0; i < noperands; i++)
+ {
+ *recog_operand_loc[i] = substed_operand[i];
+ /* While we are looping on operands, initialize this. */
+ operand_reloadnum[i] = -1;
+
+ /* If this is an earlyclobber operand, we need to widen the scope.
+ The reload must remain valid from the start of the insn being
+ reloaded until after the operand is stored into its destination.
+ We approximate this with RELOAD_OTHER even though we know that we
+ do not conflict with RELOAD_FOR_INPUT_ADDRESS reloads.
+
+ One special case that is worth checking is when we have an
+ output that is earlyclobber but isn't used past the insn (typically
+ a SCRATCH). In this case, we only need have the reload live
+ through the insn itself, but not for any of our input or output
+ reloads.
+
+ In any case, anything needed to address this operand can remain
+ however they were previously categorized. */
+
+ if (goal_alternative_earlyclobber[i])
+ operand_type[i]
+ = (find_reg_note (insn, REG_UNUSED, recog_operand[i])
+ ? RELOAD_FOR_INSN : RELOAD_OTHER);
+ }
+
+ /* Any constants that aren't allowed and can't be reloaded
+ into registers are here changed into memory references. */
+ for (i = 0; i < noperands; i++)
+ if (! goal_alternative_win[i]
+ && CONSTANT_P (recog_operand[i])
+ /* force_const_mem does not accept HIGH. */
+ && GET_CODE (recog_operand[i]) != HIGH
+ && (PREFERRED_RELOAD_CLASS (recog_operand[i],
+ (enum reg_class) goal_alternative[i])
+ == NO_REGS)
+ && operand_mode[i] != VOIDmode)
+ {
+ *recog_operand_loc[i] = recog_operand[i]
+ = find_reloads_toplev (force_const_mem (operand_mode[i],
+ recog_operand[i]),
+ i, address_type[i], ind_levels, 0);
+ if (alternative_allows_memconst (constraints1[i],
+ goal_alternative_number))
+ goal_alternative_win[i] = 1;
+ }
+
+ /* Record the values of the earlyclobber operands for the caller. */
+ if (goal_earlyclobber)
+ for (i = 0; i < noperands; i++)
+ if (goal_alternative_earlyclobber[i])
+ reload_earlyclobbers[n_earlyclobbers++] = recog_operand[i];
+
+ /* Now record reloads for all the operands that need them. */
+ for (i = 0; i < noperands; i++)
+ if (! goal_alternative_win[i])
+ {
+ /* Operands that match previous ones have already been handled. */
+ if (goal_alternative_matches[i] >= 0)
+ ;
+ /* Handle an operand with a nonoffsettable address
+ appearing where an offsettable address will do
+ by reloading the address into a base register.
+
+ ??? We can also do this when the operand is a register and
+ reg_equiv_mem is not offsettable, but this is a bit tricky,
+ so we don't bother with it. It may not be worth doing. */
+ else if (goal_alternative_matched[i] == -1
+ && goal_alternative_offmemok[i]
+ && GET_CODE (recog_operand[i]) == MEM)
+ {
+ operand_reloadnum[i]
+ = push_reload (XEXP (recog_operand[i], 0), NULL_RTX,
+ &XEXP (recog_operand[i], 0), NULL_PTR,
+ BASE_REG_CLASS, GET_MODE (XEXP (recog_operand[i], 0)),
+ VOIDmode, 0, 0, i, RELOAD_FOR_INPUT);
+ reload_inc[operand_reloadnum[i]]
+ = GET_MODE_SIZE (GET_MODE (recog_operand[i]));
+
+ /* If this operand is an output, we will have made any
+ reloads for its address as RELOAD_FOR_OUTPUT_ADDRESS, but
+ now we are treating part of the operand as an input, so
+ we must change these to RELOAD_FOR_INPUT_ADDRESS. */
+
+ if (modified[i] == RELOAD_WRITE)
+ for (j = 0; j < n_reloads; j++)
+ if (reload_opnum[j] == i
+ && reload_when_needed[j] == RELOAD_FOR_OUTPUT_ADDRESS)
+ reload_when_needed[j] = RELOAD_FOR_INPUT_ADDRESS;
+ }
+ else if (goal_alternative_matched[i] == -1)
+ operand_reloadnum[i] =
+ push_reload (modified[i] != RELOAD_WRITE ? recog_operand[i] : 0,
+ modified[i] != RELOAD_READ ? recog_operand[i] : 0,
+ (modified[i] != RELOAD_WRITE ?
+ recog_operand_loc[i] : 0),
+ modified[i] != RELOAD_READ ? recog_operand_loc[i] : 0,
+ (enum reg_class) goal_alternative[i],
+ (modified[i] == RELOAD_WRITE
+ ? VOIDmode : operand_mode[i]),
+ (modified[i] == RELOAD_READ
+ ? VOIDmode : operand_mode[i]),
+ (insn_code_number < 0 ? 0
+ : insn_operand_strict_low[insn_code_number][i]),
+ 0, i, operand_type[i]);
+ /* In a matching pair of operands, one must be input only
+ and the other must be output only.
+ Pass the input operand as IN and the other as OUT. */
+ else if (modified[i] == RELOAD_READ
+ && modified[goal_alternative_matched[i]] == RELOAD_WRITE)
+ {
+ operand_reloadnum[i]
+ = push_reload (recog_operand[i],
+ recog_operand[goal_alternative_matched[i]],
+ recog_operand_loc[i],
+ recog_operand_loc[goal_alternative_matched[i]],
+ (enum reg_class) goal_alternative[i],
+ operand_mode[i],
+ operand_mode[goal_alternative_matched[i]],
+ 0, 0, i, RELOAD_OTHER);
+ operand_reloadnum[goal_alternative_matched[i]] = output_reloadnum;
+ }
+ else if (modified[i] == RELOAD_WRITE
+ && modified[goal_alternative_matched[i]] == RELOAD_READ)
+ {
+ operand_reloadnum[goal_alternative_matched[i]]
+ = push_reload (recog_operand[goal_alternative_matched[i]],
+ recog_operand[i],
+ recog_operand_loc[goal_alternative_matched[i]],
+ recog_operand_loc[i],
+ (enum reg_class) goal_alternative[i],
+ operand_mode[goal_alternative_matched[i]],
+ operand_mode[i],
+ 0, 0, i, RELOAD_OTHER);
+ operand_reloadnum[i] = output_reloadnum;
+ }
+ else if (insn_code_number >= 0)
+ abort ();
+ else
+ {
+ error_for_asm (insn, "inconsistent operand constraints in an `asm'");
+ /* Avoid further trouble with this insn. */
+ PATTERN (insn) = gen_rtx (USE, VOIDmode, const0_rtx);
+ n_reloads = 0;
+ return;
+ }
+ }
+ else if (goal_alternative_matched[i] < 0
+ && goal_alternative_matches[i] < 0
+ && optimize)
+ {
+ /* For each non-matching operand that's a MEM or a pseudo-register
+ that didn't get a hard register, make an optional reload.
+ This may get done even if the insn needs no reloads otherwise. */
+
+ rtx operand = recog_operand[i];
+
+ while (GET_CODE (operand) == SUBREG)
+ operand = XEXP (operand, 0);
+ if ((GET_CODE (operand) == MEM
+ || (GET_CODE (operand) == REG
+ && REGNO (operand) >= FIRST_PSEUDO_REGISTER))
+ && (enum reg_class) goal_alternative[i] != NO_REGS
+ && ! no_input_reloads
+ /* Optional output reloads don't do anything and we mustn't
+ make in-out reloads on insns that are not permitted output
+ reloads. */
+ && (modified[i] == RELOAD_READ
+ || (modified[i] == RELOAD_READ_WRITE && ! no_output_reloads)))
+ operand_reloadnum[i]
+ = push_reload (modified[i] != RELOAD_WRITE ? recog_operand[i] : 0,
+ modified[i] != RELOAD_READ ? recog_operand[i] : 0,
+ (modified[i] != RELOAD_WRITE
+ ? recog_operand_loc[i] : 0),
+ (modified[i] != RELOAD_READ
+ ? recog_operand_loc[i] : 0),
+ (enum reg_class) goal_alternative[i],
+ (modified[i] == RELOAD_WRITE
+ ? VOIDmode : operand_mode[i]),
+ (modified[i] == RELOAD_READ
+ ? VOIDmode : operand_mode[i]),
+ (insn_code_number < 0 ? 0
+ : insn_operand_strict_low[insn_code_number][i]),
+ 1, i, operand_type[i]);
+ }
+ else if (goal_alternative_matches[i] >= 0
+ && goal_alternative_win[goal_alternative_matches[i]]
+ && modified[i] == RELOAD_READ
+ && modified[goal_alternative_matches[i]] == RELOAD_WRITE
+ && ! no_input_reloads && ! no_output_reloads
+ && optimize)
+ {
+ /* Similarly, make an optional reload for a pair of matching
+ objects that are in MEM or a pseudo that didn't get a hard reg. */
+
+ rtx operand = recog_operand[i];
+
+ while (GET_CODE (operand) == SUBREG)
+ operand = XEXP (operand, 0);
+ if ((GET_CODE (operand) == MEM
+ || (GET_CODE (operand) == REG
+ && REGNO (operand) >= FIRST_PSEUDO_REGISTER))
+ && ((enum reg_class) goal_alternative[goal_alternative_matches[i]]
+ != NO_REGS))
+ operand_reloadnum[i] = operand_reloadnum[goal_alternative_matches[i]]
+ = push_reload (recog_operand[goal_alternative_matches[i]],
+ recog_operand[i],
+ recog_operand_loc[goal_alternative_matches[i]],
+ recog_operand_loc[i],
+ (enum reg_class) goal_alternative[goal_alternative_matches[i]],
+ operand_mode[goal_alternative_matches[i]],
+ operand_mode[i],
+ 0, 1, goal_alternative_matches[i], RELOAD_OTHER);
+ }
+
+ /* If this insn pattern contains any MATCH_DUP's, make sure that
+ they will be substituted if the operands they match are substituted.
+ Also do now any substitutions we already did on the operands.
+
+ Don't do this if we aren't making replacements because we might be
+ propagating things allocated by frame pointer elimination into places
+ it doesn't expect. */
+
+ if (insn_code_number >= 0 && replace)
+ for (i = insn_n_dups[insn_code_number] - 1; i >= 0; i--)
+ {
+ int opno = recog_dup_num[i];
+ *recog_dup_loc[i] = *recog_operand_loc[opno];
+ if (operand_reloadnum[opno] >= 0)
+ push_replacement (recog_dup_loc[i], operand_reloadnum[opno],
+ insn_operand_mode[insn_code_number][opno]);
+ }
+
+#if 0
+ /* This loses because reloading of prior insns can invalidate the equivalence
+ (or at least find_equiv_reg isn't smart enough to find it any more),
+ causing this insn to need more reload regs than it needed before.
+ It may be too late to make the reload regs available.
+ Now this optimization is done safely in choose_reload_regs. */
+
+ /* For each reload of a reg into some other class of reg,
+ search for an existing equivalent reg (same value now) in the right class.
+ We can use it as long as we don't need to change its contents. */
+ for (i = 0; i < n_reloads; i++)
+ if (reload_reg_rtx[i] == 0
+ && reload_in[i] != 0
+ && GET_CODE (reload_in[i]) == REG
+ && reload_out[i] == 0)
+ {
+ reload_reg_rtx[i]
+ = find_equiv_reg (reload_in[i], insn, reload_reg_class[i], -1,
+ static_reload_reg_p, 0, reload_inmode[i]);
+ /* Prevent generation of insn to load the value
+ because the one we found already has the value. */
+ if (reload_reg_rtx[i])
+ reload_in[i] = reload_reg_rtx[i];
+ }
+#endif
+
+ /* Perhaps an output reload can be combined with another
+ to reduce needs by one. */
+ if (!goal_earlyclobber)
+ combine_reloads ();
+
+ /* If we have a pair of reloads for parts of an address, they are reloading
+ the same object, the operands themselves were not reloaded, and they
+ are for two operands that are supposed to match, merge the reloads and
+ change the type of the surviving reload to RELOAD_FOR_OPERAND_ADDRESS. */
+
+ for (i = 0; i < n_reloads; i++)
+ {
+ int k;
+
+ for (j = i + 1; j < n_reloads; j++)
+ if ((reload_when_needed[i] == RELOAD_FOR_INPUT_ADDRESS
+ || reload_when_needed[i] == RELOAD_FOR_OUTPUT_ADDRESS)
+ && (reload_when_needed[j] == RELOAD_FOR_INPUT_ADDRESS
+ || reload_when_needed[j] == RELOAD_FOR_OUTPUT_ADDRESS)
+ && rtx_equal_p (reload_in[i], reload_in[j])
+ && (operand_reloadnum[reload_opnum[i]] < 0
+ || reload_optional[operand_reloadnum[reload_opnum[i]]])
+ && (operand_reloadnum[reload_opnum[j]] < 0
+ || reload_optional[operand_reloadnum[reload_opnum[j]]])
+ && (goal_alternative_matches[reload_opnum[i]] == reload_opnum[j]
+ || (goal_alternative_matches[reload_opnum[j]]
+ == reload_opnum[i])))
+ {
+ for (k = 0; k < n_replacements; k++)
+ if (replacements[k].what == j)
+ replacements[k].what = i;
+
+ reload_when_needed[i] = RELOAD_FOR_OPERAND_ADDRESS;
+ reload_in[j] = 0;
+ }
+ }
+
+ /* Scan all the reloads and update their type.
+ If a reload is for the address of an operand and we didn't reload
+ that operand, change the type. Similarly, change the operand number
+ of a reload when two operands match. If a reload is optional, treat it
+ as though the operand isn't reloaded.
+
+ ??? This latter case is somewhat odd because if we do the optional
+ reload, it means the object is hanging around. Thus we need only
+ do the address reload if the optional reload was NOT done.
+
+ Change secondary reloads to be the address type of their operand, not
+ the normal type.
+
+ If an operand's reload is now RELOAD_OTHER, change any
+ RELOAD_FOR_INPUT_ADDRESS reloads of that operand to
+ RELOAD_FOR_OTHER_ADDRESS. */
+
+ for (i = 0; i < n_reloads; i++)
+ {
+ if (reload_secondary_p[i]
+ && reload_when_needed[i] == operand_type[reload_opnum[i]])
+ reload_when_needed[i] = address_type[reload_opnum[i]];
+
+ if ((reload_when_needed[i] == RELOAD_FOR_INPUT_ADDRESS
+ || reload_when_needed[i] == RELOAD_FOR_OUTPUT_ADDRESS)
+ && (operand_reloadnum[reload_opnum[i]] < 0
+ || reload_optional[operand_reloadnum[reload_opnum[i]]]))
+ {
+ /* If we have a secondary reload to go along with this reload,
+ change its type to RELOAD_FOR_OPADDR_ADDR. */
+
+ if (reload_when_needed[i] == RELOAD_FOR_INPUT_ADDRESS
+ && reload_secondary_in_reload[i] != -1)
+ {
+ int secondary_in_reload = reload_secondary_in_reload[i];
+
+ reload_when_needed[secondary_in_reload] =
+ RELOAD_FOR_OPADDR_ADDR;
+
+ /* If there's a tertiary reload we have to change it also. */
+ if (secondary_in_reload > 0
+ && reload_secondary_in_reload[secondary_in_reload] != -1)
+ reload_when_needed[reload_secondary_in_reload[secondary_in_reload]]
+ = RELOAD_FOR_OPADDR_ADDR;
+ }
+
+ if (reload_when_needed[i] == RELOAD_FOR_OUTPUT_ADDRESS
+ && reload_secondary_out_reload[i] != -1)
+ {
+ int secondary_out_reload = reload_secondary_out_reload[i];
+
+ reload_when_needed[secondary_out_reload] =
+ RELOAD_FOR_OPADDR_ADDR;
+
+ /* If there's a tertiary reload we have to change it also. */
+ if (secondary_out_reload
+ && reload_secondary_out_reload[secondary_out_reload] != -1)
+ reload_when_needed[reload_secondary_out_reload[secondary_out_reload]]
+ = RELOAD_FOR_OPADDR_ADDR;
+ }
+ reload_when_needed[i] = RELOAD_FOR_OPERAND_ADDRESS;
+ }
+
+ if (reload_when_needed[i] == RELOAD_FOR_INPUT_ADDRESS
+ && operand_reloadnum[reload_opnum[i]] >= 0
+ && (reload_when_needed[operand_reloadnum[reload_opnum[i]]]
+ == RELOAD_OTHER))
+ reload_when_needed[i] = RELOAD_FOR_OTHER_ADDRESS;
+
+ if (goal_alternative_matches[reload_opnum[i]] >= 0)
+ reload_opnum[i] = goal_alternative_matches[reload_opnum[i]];
+ }
+
+ /* See if we have any reloads that are now allowed to be merged
+ because we've changed when the reload is needed to
+ RELOAD_FOR_OPERAND_ADDRESS or RELOAD_FOR_OTHER_ADDRESS. Only
+ check for the most common cases. */
+
+ for (i = 0; i < n_reloads; i++)
+ if (reload_in[i] != 0 && reload_out[i] == 0
+ && (reload_when_needed[i] == RELOAD_FOR_OPERAND_ADDRESS
+ || reload_when_needed[i] == RELOAD_FOR_OTHER_ADDRESS))
+ for (j = 0; j < n_reloads; j++)
+ if (i != j && reload_in[j] != 0 && reload_out[j] == 0
+ && reload_when_needed[j] == reload_when_needed[i]
+ && MATCHES (reload_in[i], reload_in[j])
+ && reload_reg_class[i] == reload_reg_class[j]
+ && !reload_nocombine[i] && !reload_nocombine[j]
+ && reload_reg_rtx[i] == reload_reg_rtx[j])
+ {
+ reload_opnum[i] = MIN (reload_opnum[i], reload_opnum[j]);
+ transfer_replacements (i, j);
+ reload_in[j] = 0;
+ }
+
+#else /* no REGISTER_CONSTRAINTS */
+ int noperands;
+ int insn_code_number;
+ int goal_earlyclobber = 0; /* Always 0, to make combine_reloads happen. */
+ register int i;
+ rtx body = PATTERN (insn);
+
+ n_reloads = 0;
+ n_replacements = 0;
+ n_earlyclobbers = 0;
+ replace_reloads = replace;
+ this_insn = insn;
+
+ /* Find what kind of insn this is. NOPERANDS gets number of operands.
+ Store the operand values in RECOG_OPERAND and the locations
+ of the words in the insn that point to them in RECOG_OPERAND_LOC.
+ Return if the insn needs no reload processing. */
+
+ switch (GET_CODE (body))
+ {
+ case USE:
+ case CLOBBER:
+ case ASM_INPUT:
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ return;
+
+ case PARALLEL:
+ case SET:
+ noperands = asm_noperands (body);
+ if (noperands >= 0)
+ {
+ /* This insn is an `asm' with operands.
+ First, find out how many operands, and allocate space. */
+
+ insn_code_number = -1;
+ /* ??? This is a bug! ???
+ Give up and delete this insn if it has too many operands. */
+ if (noperands > MAX_RECOG_OPERANDS)
+ abort ();
+
+ /* Now get the operand values out of the insn. */
+
+ decode_asm_operands (body, recog_operand, recog_operand_loc,
+ NULL_PTR, NULL_PTR);
+ break;
+ }
+
+ default:
+ /* Ordinary insn: recognize it, allocate space for operands and
+ constraints, and get them out via insn_extract. */
+
+ insn_code_number = recog_memoized (insn);
+ noperands = insn_n_operands[insn_code_number];
+ insn_extract (insn);
+ }
+
+ if (noperands == 0)
+ return;
+
+ for (i = 0; i < noperands; i++)
+ {
+ register RTX_CODE code = GET_CODE (recog_operand[i]);
+ int is_set_dest = GET_CODE (body) == SET && (i == 0);
+
+ if (insn_code_number >= 0)
+ if (insn_operand_address_p[insn_code_number][i])
+ find_reloads_address (VOIDmode, NULL_PTR,
+ recog_operand[i], recog_operand_loc[i],
+ i, RELOAD_FOR_INPUT, ind_levels);
+
+ /* In these cases, we can't tell if the operand is an input
+ or an output, so be conservative. In practice it won't be
+ problem. */
+
+ if (code == MEM)
+ find_reloads_address (GET_MODE (recog_operand[i]),
+ recog_operand_loc[i],
+ XEXP (recog_operand[i], 0),
+ &XEXP (recog_operand[i], 0),
+ i, RELOAD_OTHER, ind_levels);
+ if (code == SUBREG)
+ recog_operand[i] = *recog_operand_loc[i]
+ = find_reloads_toplev (recog_operand[i], i, RELOAD_OTHER,
+ ind_levels, is_set_dest);
+ if (code == REG)
+ {
+ register int regno = REGNO (recog_operand[i]);
+ if (reg_equiv_constant[regno] != 0 && !is_set_dest)
+ recog_operand[i] = *recog_operand_loc[i]
+ = reg_equiv_constant[regno];
+#if 0 /* This might screw code in reload1.c to delete prior output-reload
+ that feeds this insn. */
+ if (reg_equiv_mem[regno] != 0)
+ recog_operand[i] = *recog_operand_loc[i]
+ = reg_equiv_mem[regno];
+#endif
+ }
+ }
+
+ /* Perhaps an output reload can be combined with another
+ to reduce needs by one. */
+ if (!goal_earlyclobber)
+ combine_reloads ();
+#endif /* no REGISTER_CONSTRAINTS */
+}
+
+/* Return 1 if alternative number ALTNUM in constraint-string CONSTRAINT
+ accepts a memory operand with constant address. */
+
+static int
+alternative_allows_memconst (constraint, altnum)
+ char *constraint;
+ int altnum;
+{
+ register int c;
+ /* Skip alternatives before the one requested. */
+ while (altnum > 0)
+ {
+ while (*constraint++ != ',');
+ altnum--;
+ }
+ /* Scan the requested alternative for 'm' or 'o'.
+ If one of them is present, this alternative accepts memory constants. */
+ while ((c = *constraint++) && c != ',' && c != '#')
+ if (c == 'm' || c == 'o')
+ return 1;
+ return 0;
+}
+
+/* Scan X for memory references and scan the addresses for reloading.
+ Also checks for references to "constant" regs that we want to eliminate
+ and replaces them with the values they stand for.
+ We may alter X destructively if it contains a reference to such.
+ If X is just a constant reg, we return the equivalent value
+ instead of X.
+
+ IND_LEVELS says how many levels of indirect addressing this machine
+ supports.
+
+ OPNUM and TYPE identify the purpose of the reload.
+
+ IS_SET_DEST is true if X is the destination of a SET, which is not
+ appropriate to be replaced by a constant. */
+
+static rtx
+find_reloads_toplev (x, opnum, type, ind_levels, is_set_dest)
+ rtx x;
+ int opnum;
+ enum reload_type type;
+ int ind_levels;
+ int is_set_dest;
+{
+ register RTX_CODE code = GET_CODE (x);
+
+ register char *fmt = GET_RTX_FORMAT (code);
+ register int i;
+
+ if (code == REG)
+ {
+ /* This code is duplicated for speed in find_reloads. */
+ register int regno = REGNO (x);
+ if (reg_equiv_constant[regno] != 0 && !is_set_dest)
+ x = reg_equiv_constant[regno];
+#if 0
+/* This creates (subreg (mem...)) which would cause an unnecessary
+ reload of the mem. */
+ else if (reg_equiv_mem[regno] != 0)
+ x = reg_equiv_mem[regno];
+#endif
+ else if (reg_equiv_address[regno] != 0)
+ {
+ /* If reg_equiv_address varies, it may be shared, so copy it. */
+ rtx addr = reg_equiv_address[regno];
+
+ if (rtx_varies_p (addr))
+ addr = copy_rtx (addr);
+
+ x = gen_rtx (MEM, GET_MODE (x), addr);
+ RTX_UNCHANGING_P (x) = RTX_UNCHANGING_P (regno_reg_rtx[regno]);
+ find_reloads_address (GET_MODE (x), NULL_PTR,
+ XEXP (x, 0),
+ &XEXP (x, 0), opnum, type, ind_levels);
+ }
+ return x;
+ }
+ if (code == MEM)
+ {
+ rtx tem = x;
+ find_reloads_address (GET_MODE (x), &tem, XEXP (x, 0), &XEXP (x, 0),
+ opnum, type, ind_levels);
+ return tem;
+ }
+
+ if (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG)
+ {
+ /* Check for SUBREG containing a REG that's equivalent to a constant.
+ If the constant has a known value, truncate it right now.
+ Similarly if we are extracting a single-word of a multi-word
+ constant. If the constant is symbolic, allow it to be substituted
+ normally. push_reload will strip the subreg later. If the
+ constant is VOIDmode, abort because we will lose the mode of
+ the register (this should never happen because one of the cases
+ above should handle it). */
+
+ register int regno = REGNO (SUBREG_REG (x));
+ rtx tem;
+
+ if (subreg_lowpart_p (x)
+ && regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] < 0
+ && reg_equiv_constant[regno] != 0
+ && (tem = gen_lowpart_common (GET_MODE (x),
+ reg_equiv_constant[regno])) != 0)
+ return tem;
+
+ if (GET_MODE_BITSIZE (GET_MODE (x)) == BITS_PER_WORD
+ && regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] < 0
+ && reg_equiv_constant[regno] != 0
+ && (tem = operand_subword (reg_equiv_constant[regno],
+ SUBREG_WORD (x), 0,
+ GET_MODE (SUBREG_REG (x)))) != 0)
+ return tem;
+
+ if (regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] < 0
+ && reg_equiv_constant[regno] != 0
+ && GET_MODE (reg_equiv_constant[regno]) == VOIDmode)
+ abort ();
+
+ /* If the subreg contains a reg that will be converted to a mem,
+ convert the subreg to a narrower memref now.
+ Otherwise, we would get (subreg (mem ...) ...),
+ which would force reload of the mem.
+
+ We also need to do this if there is an equivalent MEM that is
+ not offsettable. In that case, alter_subreg would produce an
+ invalid address on big-endian machines.
+
+ For machines that extend byte loads, we must not reload using
+ a wider mode if we have a paradoxical SUBREG. find_reloads will
+ force a reload in that case. So we should not do anything here. */
+
+ else if (regno >= FIRST_PSEUDO_REGISTER
+#ifdef LOAD_EXTEND_OP
+ && (GET_MODE_SIZE (GET_MODE (x))
+ <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+#endif
+ && (reg_equiv_address[regno] != 0
+ || (reg_equiv_mem[regno] != 0
+ && (! strict_memory_address_p (GET_MODE (x),
+ XEXP (reg_equiv_mem[regno], 0))
+ || ! offsettable_memref_p (reg_equiv_mem[regno])))))
+ {
+ int offset = SUBREG_WORD (x) * UNITS_PER_WORD;
+ rtx addr = (reg_equiv_address[regno] ? reg_equiv_address[regno]
+ : XEXP (reg_equiv_mem[regno], 0));
+#if BYTES_BIG_ENDIAN
+ int size;
+ size = GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)));
+ offset += MIN (size, UNITS_PER_WORD);
+ size = GET_MODE_SIZE (GET_MODE (x));
+ offset -= MIN (size, UNITS_PER_WORD);
+#endif
+ addr = plus_constant (addr, offset);
+ x = gen_rtx (MEM, GET_MODE (x), addr);
+ RTX_UNCHANGING_P (x) = RTX_UNCHANGING_P (regno_reg_rtx[regno]);
+ find_reloads_address (GET_MODE (x), NULL_PTR,
+ XEXP (x, 0),
+ &XEXP (x, 0), opnum, type, ind_levels);
+ }
+
+ }
+
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ XEXP (x, i) = find_reloads_toplev (XEXP (x, i), opnum, type,
+ ind_levels, is_set_dest);
+ }
+ return x;
+}
+
+/* Return a mem ref for the memory equivalent of reg REGNO.
+ This mem ref is not shared with anything. */
+
+static rtx
+make_memloc (ad, regno)
+ rtx ad;
+ int regno;
+{
+ register int i;
+ rtx tem = reg_equiv_address[regno];
+
+#if 0 /* We cannot safely reuse a memloc made here;
+ if the pseudo appears twice, and its mem needs a reload,
+ it gets two separate reloads assigned, but it only
+ gets substituted with the second of them;
+ then it can get used before that reload reg gets loaded up. */
+ for (i = 0; i < n_memlocs; i++)
+ if (rtx_equal_p (tem, XEXP (memlocs[i], 0)))
+ return memlocs[i];
+#endif
+
+ /* If TEM might contain a pseudo, we must copy it to avoid
+ modifying it when we do the substitution for the reload. */
+ if (rtx_varies_p (tem))
+ tem = copy_rtx (tem);
+
+ tem = gen_rtx (MEM, GET_MODE (ad), tem);
+ RTX_UNCHANGING_P (tem) = RTX_UNCHANGING_P (regno_reg_rtx[regno]);
+ memlocs[n_memlocs++] = tem;
+ return tem;
+}
+
+/* Record all reloads needed for handling memory address AD
+ which appears in *LOC in a memory reference to mode MODE
+ which itself is found in location *MEMREFLOC.
+ Note that we take shortcuts assuming that no multi-reg machine mode
+ occurs as part of an address.
+
+ OPNUM and TYPE specify the purpose of this reload.
+
+ IND_LEVELS says how many levels of indirect addressing this machine
+ supports.
+
+ Value is nonzero if this address is reloaded or replaced as a whole.
+ This is interesting to the caller if the address is an autoincrement.
+
+ Note that there is no verification that the address will be valid after
+ this routine does its work. Instead, we rely on the fact that the address
+ was valid when reload started. So we need only undo things that reload
+ could have broken. These are wrong register types, pseudos not allocated
+ to a hard register, and frame pointer elimination. */
+
+static int
+find_reloads_address (mode, memrefloc, ad, loc, opnum, type, ind_levels)
+ enum machine_mode mode;
+ rtx *memrefloc;
+ rtx ad;
+ rtx *loc;
+ int opnum;
+ enum reload_type type;
+ int ind_levels;
+{
+ register int regno;
+ rtx tem;
+
+ /* If the address is a register, see if it is a legitimate address and
+ reload if not. We first handle the cases where we need not reload
+ or where we must reload in a non-standard way. */
+
+ if (GET_CODE (ad) == REG)
+ {
+ regno = REGNO (ad);
+
+ if (reg_equiv_constant[regno] != 0
+ && strict_memory_address_p (mode, reg_equiv_constant[regno]))
+ {
+ *loc = ad = reg_equiv_constant[regno];
+ return 1;
+ }
+
+ else if (reg_equiv_address[regno] != 0)
+ {
+ tem = make_memloc (ad, regno);
+ find_reloads_address (GET_MODE (tem), NULL_PTR, XEXP (tem, 0),
+ &XEXP (tem, 0), opnum, type, ind_levels);
+ push_reload (tem, NULL_RTX, loc, NULL_PTR, BASE_REG_CLASS,
+ GET_MODE (ad), VOIDmode, 0, 0,
+ opnum, type);
+ return 1;
+ }
+
+ /* We can avoid a reload if the register's equivalent memory expression
+ is valid as an indirect memory address.
+ But not all addresses are valid in a mem used as an indirect address:
+ only reg or reg+constant. */
+
+ else if (reg_equiv_mem[regno] != 0 && ind_levels > 0
+ && strict_memory_address_p (mode, reg_equiv_mem[regno])
+ && (GET_CODE (XEXP (reg_equiv_mem[regno], 0)) == REG
+ || (GET_CODE (XEXP (reg_equiv_mem[regno], 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (reg_equiv_mem[regno], 0), 0)) == REG
+ && CONSTANT_P (XEXP (XEXP (reg_equiv_mem[regno], 0), 0)))))
+ return 0;
+
+ /* The only remaining case where we can avoid a reload is if this is a
+ hard register that is valid as a base register and which is not the
+ subject of a CLOBBER in this insn. */
+
+ else if (regno < FIRST_PSEUDO_REGISTER && REGNO_OK_FOR_BASE_P (regno)
+ && ! regno_clobbered_p (regno, this_insn))
+ return 0;
+
+ /* If we do not have one of the cases above, we must do the reload. */
+ push_reload (ad, NULL_RTX, loc, NULL_PTR, BASE_REG_CLASS,
+ GET_MODE (ad), VOIDmode, 0, 0, opnum, type);
+ return 1;
+ }
+
+ if (strict_memory_address_p (mode, ad))
+ {
+ /* The address appears valid, so reloads are not needed.
+ But the address may contain an eliminable register.
+ This can happen because a machine with indirect addressing
+ may consider a pseudo register by itself a valid address even when
+ it has failed to get a hard reg.
+ So do a tree-walk to find and eliminate all such regs. */
+
+ /* But first quickly dispose of a common case. */
+ if (GET_CODE (ad) == PLUS
+ && GET_CODE (XEXP (ad, 1)) == CONST_INT
+ && GET_CODE (XEXP (ad, 0)) == REG
+ && reg_equiv_constant[REGNO (XEXP (ad, 0))] == 0)
+ return 0;
+
+ subst_reg_equivs_changed = 0;
+ *loc = subst_reg_equivs (ad);
+
+ if (! subst_reg_equivs_changed)
+ return 0;
+
+ /* Check result for validity after substitution. */
+ if (strict_memory_address_p (mode, ad))
+ return 0;
+ }
+
+ /* The address is not valid. We have to figure out why. One possibility
+ is that it is itself a MEM. This can happen when the frame pointer is
+ being eliminated, a pseudo is not allocated to a hard register, and the
+ offset between the frame and stack pointers is not its initial value.
+ In that case the pseudo will have been replaced by a MEM referring to
+ the stack pointer. */
+ if (GET_CODE (ad) == MEM)
+ {
+ /* First ensure that the address in this MEM is valid. Then, unless
+ indirect addresses are valid, reload the MEM into a register. */
+ tem = ad;
+ find_reloads_address (GET_MODE (ad), &tem, XEXP (ad, 0), &XEXP (ad, 0),
+ opnum, type, ind_levels == 0 ? 0 : ind_levels - 1);
+
+ /* If tem was changed, then we must create a new memory reference to
+ hold it and store it back into memrefloc. */
+ if (tem != ad && memrefloc)
+ {
+ *memrefloc = copy_rtx (*memrefloc);
+ copy_replacements (tem, XEXP (*memrefloc, 0));
+ loc = &XEXP (*memrefloc, 0);
+ }
+
+ /* Check similar cases as for indirect addresses as above except
+ that we can allow pseudos and a MEM since they should have been
+ taken care of above. */
+
+ if (ind_levels == 0
+ || (GET_CODE (XEXP (tem, 0)) == SYMBOL_REF && ! indirect_symref_ok)
+ || GET_CODE (XEXP (tem, 0)) == MEM
+ || ! (GET_CODE (XEXP (tem, 0)) == REG
+ || (GET_CODE (XEXP (tem, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (tem, 0), 0)) == REG
+ && GET_CODE (XEXP (XEXP (tem, 0), 1)) == CONST_INT)))
+ {
+ /* Must use TEM here, not AD, since it is the one that will
+ have any subexpressions reloaded, if needed. */
+ push_reload (tem, NULL_RTX, loc, NULL_PTR,
+ BASE_REG_CLASS, GET_MODE (tem), VOIDmode, 0,
+ 0, opnum, type);
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+ /* If we have address of a stack slot but it's not valid
+ (displacement is too large), compute the sum in a register. */
+ else if (GET_CODE (ad) == PLUS
+ && (XEXP (ad, 0) == frame_pointer_rtx
+#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ || XEXP (ad, 0) == hard_frame_pointer_rtx
+#endif
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ || XEXP (ad, 0) == arg_pointer_rtx
+#endif
+ || XEXP (ad, 0) == stack_pointer_rtx)
+ && GET_CODE (XEXP (ad, 1)) == CONST_INT)
+ {
+ /* Unshare the MEM rtx so we can safely alter it. */
+ if (memrefloc)
+ {
+ *memrefloc = copy_rtx (*memrefloc);
+ loc = &XEXP (*memrefloc, 0);
+ }
+ if (double_reg_address_ok)
+ {
+ /* Unshare the sum as well. */
+ *loc = ad = copy_rtx (ad);
+ /* Reload the displacement into an index reg.
+ We assume the frame pointer or arg pointer is a base reg. */
+ find_reloads_address_part (XEXP (ad, 1), &XEXP (ad, 1),
+ INDEX_REG_CLASS, GET_MODE (ad), opnum,
+ type, ind_levels);
+ }
+ else
+ {
+ /* If the sum of two regs is not necessarily valid,
+ reload the sum into a base reg.
+ That will at least work. */
+ find_reloads_address_part (ad, loc, BASE_REG_CLASS, Pmode,
+ opnum, type, ind_levels);
+ }
+ return 1;
+ }
+
+ /* If we have an indexed stack slot, there are three possible reasons why
+ it might be invalid: The index might need to be reloaded, the address
+ might have been made by frame pointer elimination and hence have a
+ constant out of range, or both reasons might apply.
+
+ We can easily check for an index needing reload, but even if that is the
+ case, we might also have an invalid constant. To avoid making the
+ conservative assumption and requiring two reloads, we see if this address
+ is valid when not interpreted strictly. If it is, the only problem is
+ that the index needs a reload and find_reloads_address_1 will take care
+ of it.
+
+ There is still a case when we might generate an extra reload,
+ however. In certain cases eliminate_regs will return a MEM for a REG
+ (see the code there for details). In those cases, memory_address_p
+ applied to our address will return 0 so we will think that our offset
+ must be too large. But it might indeed be valid and the only problem
+ is that a MEM is present where a REG should be. This case should be
+ very rare and there doesn't seem to be any way to avoid it.
+
+ If we decide to do something here, it must be that
+ `double_reg_address_ok' is true and that this address rtl was made by
+ eliminate_regs. We generate a reload of the fp/sp/ap + constant and
+ rework the sum so that the reload register will be added to the index.
+ This is safe because we know the address isn't shared.
+
+ We check for fp/ap/sp as both the first and second operand of the
+ innermost PLUS. */
+
+ else if (GET_CODE (ad) == PLUS && GET_CODE (XEXP (ad, 1)) == CONST_INT
+ && GET_CODE (XEXP (ad, 0)) == PLUS
+ && (XEXP (XEXP (ad, 0), 0) == frame_pointer_rtx
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+ || XEXP (XEXP (ad, 0), 0) == hard_frame_pointer_rtx
+#endif
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ || XEXP (XEXP (ad, 0), 0) == arg_pointer_rtx
+#endif
+ || XEXP (XEXP (ad, 0), 0) == stack_pointer_rtx)
+ && ! memory_address_p (mode, ad))
+ {
+ *loc = ad = gen_rtx (PLUS, GET_MODE (ad),
+ plus_constant (XEXP (XEXP (ad, 0), 0),
+ INTVAL (XEXP (ad, 1))),
+ XEXP (XEXP (ad, 0), 1));
+ find_reloads_address_part (XEXP (ad, 0), &XEXP (ad, 0), BASE_REG_CLASS,
+ GET_MODE (ad), opnum, type, ind_levels);
+ find_reloads_address_1 (XEXP (ad, 1), 1, &XEXP (ad, 1), opnum, type, 0);
+
+ return 1;
+ }
+
+ else if (GET_CODE (ad) == PLUS && GET_CODE (XEXP (ad, 1)) == CONST_INT
+ && GET_CODE (XEXP (ad, 0)) == PLUS
+ && (XEXP (XEXP (ad, 0), 1) == frame_pointer_rtx
+#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ || XEXP (XEXP (ad, 0), 1) == hard_frame_pointer_rtx
+#endif
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ || XEXP (XEXP (ad, 0), 1) == arg_pointer_rtx
+#endif
+ || XEXP (XEXP (ad, 0), 1) == stack_pointer_rtx)
+ && ! memory_address_p (mode, ad))
+ {
+ *loc = ad = gen_rtx (PLUS, GET_MODE (ad),
+ plus_constant (XEXP (XEXP (ad, 0), 1),
+ INTVAL (XEXP (ad, 1))),
+ XEXP (XEXP (ad, 0), 0));
+ find_reloads_address_part (XEXP (ad, 0), &XEXP (ad, 0), BASE_REG_CLASS,
+ GET_MODE (ad), opnum, type, ind_levels);
+ find_reloads_address_1 (XEXP (ad, 1), 1, &XEXP (ad, 1), opnum, type, 0);
+
+ return 1;
+ }
+
+ /* See if address becomes valid when an eliminable register
+ in a sum is replaced. */
+
+ tem = ad;
+ if (GET_CODE (ad) == PLUS)
+ tem = subst_indexed_address (ad);
+ if (tem != ad && strict_memory_address_p (mode, tem))
+ {
+ /* Ok, we win that way. Replace any additional eliminable
+ registers. */
+
+ subst_reg_equivs_changed = 0;
+ tem = subst_reg_equivs (tem);
+
+ /* Make sure that didn't make the address invalid again. */
+
+ if (! subst_reg_equivs_changed || strict_memory_address_p (mode, tem))
+ {
+ *loc = tem;
+ return 0;
+ }
+ }
+
+ /* If constants aren't valid addresses, reload the constant address
+ into a register. */
+ if (CONSTANT_P (ad) && ! strict_memory_address_p (mode, ad))
+ {
+ /* If AD is in address in the constant pool, the MEM rtx may be shared.
+ Unshare it so we can safely alter it. */
+ if (memrefloc && GET_CODE (ad) == SYMBOL_REF
+ && CONSTANT_POOL_ADDRESS_P (ad))
+ {
+ *memrefloc = copy_rtx (*memrefloc);
+ loc = &XEXP (*memrefloc, 0);
+ }
+
+ find_reloads_address_part (ad, loc, BASE_REG_CLASS, Pmode, opnum, type,
+ ind_levels);
+ return 1;
+ }
+
+ return find_reloads_address_1 (ad, 0, loc, opnum, type, ind_levels);
+}
+
+/* Find all pseudo regs appearing in AD
+ that are eliminable in favor of equivalent values
+ and do not have hard regs; replace them by their equivalents. */
+
+static rtx
+subst_reg_equivs (ad)
+ rtx ad;
+{
+ register RTX_CODE code = GET_CODE (ad);
+ register int i;
+ register char *fmt;
+
+ switch (code)
+ {
+ case HIGH:
+ case CONST_INT:
+ case CONST:
+ case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case PC:
+ case CC0:
+ return ad;
+
+ case REG:
+ {
+ register int regno = REGNO (ad);
+
+ if (reg_equiv_constant[regno] != 0)
+ {
+ subst_reg_equivs_changed = 1;
+ return reg_equiv_constant[regno];
+ }
+ }
+ return ad;
+
+ case PLUS:
+ /* Quickly dispose of a common case. */
+ if (XEXP (ad, 0) == frame_pointer_rtx
+ && GET_CODE (XEXP (ad, 1)) == CONST_INT)
+ return ad;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ if (fmt[i] == 'e')
+ XEXP (ad, i) = subst_reg_equivs (XEXP (ad, i));
+ return ad;
+}
+
+/* Compute the sum of X and Y, making canonicalizations assumed in an
+ address, namely: sum constant integers, surround the sum of two
+ constants with a CONST, put the constant as the second operand, and
+ group the constant on the outermost sum.
+
+ This routine assumes both inputs are already in canonical form. */
+
+rtx
+form_sum (x, y)
+ rtx x, y;
+{
+ rtx tem;
+ enum machine_mode mode = GET_MODE (x);
+
+ if (mode == VOIDmode)
+ mode = GET_MODE (y);
+
+ if (mode == VOIDmode)
+ mode = Pmode;
+
+ if (GET_CODE (x) == CONST_INT)
+ return plus_constant (y, INTVAL (x));
+ else if (GET_CODE (y) == CONST_INT)
+ return plus_constant (x, INTVAL (y));
+ else if (CONSTANT_P (x))
+ tem = x, x = y, y = tem;
+
+ if (GET_CODE (x) == PLUS && CONSTANT_P (XEXP (x, 1)))
+ return form_sum (XEXP (x, 0), form_sum (XEXP (x, 1), y));
+
+ /* Note that if the operands of Y are specified in the opposite
+ order in the recursive calls below, infinite recursion will occur. */
+ if (GET_CODE (y) == PLUS && CONSTANT_P (XEXP (y, 1)))
+ return form_sum (form_sum (x, XEXP (y, 0)), XEXP (y, 1));
+
+ /* If both constant, encapsulate sum. Otherwise, just form sum. A
+ constant will have been placed second. */
+ if (CONSTANT_P (x) && CONSTANT_P (y))
+ {
+ if (GET_CODE (x) == CONST)
+ x = XEXP (x, 0);
+ if (GET_CODE (y) == CONST)
+ y = XEXP (y, 0);
+
+ return gen_rtx (CONST, VOIDmode, gen_rtx (PLUS, mode, x, y));
+ }
+
+ return gen_rtx (PLUS, mode, x, y);
+}
+
+/* If ADDR is a sum containing a pseudo register that should be
+ replaced with a constant (from reg_equiv_constant),
+ return the result of doing so, and also apply the associative
+ law so that the result is more likely to be a valid address.
+ (But it is not guaranteed to be one.)
+
+ Note that at most one register is replaced, even if more are
+ replaceable. Also, we try to put the result into a canonical form
+ so it is more likely to be a valid address.
+
+ In all other cases, return ADDR. */
+
+static rtx
+subst_indexed_address (addr)
+ rtx addr;
+{
+ rtx op0 = 0, op1 = 0, op2 = 0;
+ rtx tem;
+ int regno;
+
+ if (GET_CODE (addr) == PLUS)
+ {
+ /* Try to find a register to replace. */
+ op0 = XEXP (addr, 0), op1 = XEXP (addr, 1), op2 = 0;
+ if (GET_CODE (op0) == REG
+ && (regno = REGNO (op0)) >= FIRST_PSEUDO_REGISTER
+ && reg_renumber[regno] < 0
+ && reg_equiv_constant[regno] != 0)
+ op0 = reg_equiv_constant[regno];
+ else if (GET_CODE (op1) == REG
+ && (regno = REGNO (op1)) >= FIRST_PSEUDO_REGISTER
+ && reg_renumber[regno] < 0
+ && reg_equiv_constant[regno] != 0)
+ op1 = reg_equiv_constant[regno];
+ else if (GET_CODE (op0) == PLUS
+ && (tem = subst_indexed_address (op0)) != op0)
+ op0 = tem;
+ else if (GET_CODE (op1) == PLUS
+ && (tem = subst_indexed_address (op1)) != op1)
+ op1 = tem;
+ else
+ return addr;
+
+ /* Pick out up to three things to add. */
+ if (GET_CODE (op1) == PLUS)
+ op2 = XEXP (op1, 1), op1 = XEXP (op1, 0);
+ else if (GET_CODE (op0) == PLUS)
+ op2 = op1, op1 = XEXP (op0, 1), op0 = XEXP (op0, 0);
+
+ /* Compute the sum. */
+ if (op2 != 0)
+ op1 = form_sum (op1, op2);
+ if (op1 != 0)
+ op0 = form_sum (op0, op1);
+
+ return op0;
+ }
+ return addr;
+}
+
+/* Record the pseudo registers we must reload into hard registers
+ in a subexpression of a would-be memory address, X.
+ (This function is not called if the address we find is strictly valid.)
+ CONTEXT = 1 means we are considering regs as index regs,
+ = 0 means we are considering them as base regs.
+
+ OPNUM and TYPE specify the purpose of any reloads made.
+
+ IND_LEVELS says how many levels of indirect addressing are
+ supported at this point in the address.
+
+ We return nonzero if X, as a whole, is reloaded or replaced. */
+
+/* Note that we take shortcuts assuming that no multi-reg machine mode
+ occurs as part of an address.
+ Also, this is not fully machine-customizable; it works for machines
+ such as vaxes and 68000's and 32000's, but other possible machines
+ could have addressing modes that this does not handle right. */
+
+static int
+find_reloads_address_1 (x, context, loc, opnum, type, ind_levels)
+ rtx x;
+ int context;
+ rtx *loc;
+ int opnum;
+ enum reload_type type;
+ int ind_levels;
+{
+ register RTX_CODE code = GET_CODE (x);
+
+ switch (code)
+ {
+ case PLUS:
+ {
+ register rtx orig_op0 = XEXP (x, 0);
+ register rtx orig_op1 = XEXP (x, 1);
+ register RTX_CODE code0 = GET_CODE (orig_op0);
+ register RTX_CODE code1 = GET_CODE (orig_op1);
+ register rtx op0 = orig_op0;
+ register rtx op1 = orig_op1;
+
+ if (GET_CODE (op0) == SUBREG)
+ {
+ op0 = SUBREG_REG (op0);
+ code0 = GET_CODE (op0);
+ }
+
+ if (GET_CODE (op1) == SUBREG)
+ {
+ op1 = SUBREG_REG (op1);
+ code1 = GET_CODE (op1);
+ }
+
+ if (code0 == MULT || code0 == SIGN_EXTEND || code1 == MEM)
+ {
+ find_reloads_address_1 (orig_op0, 1, &XEXP (x, 0), opnum, type,
+ ind_levels);
+ find_reloads_address_1 (orig_op1, 0, &XEXP (x, 1), opnum, type,
+ ind_levels);
+ }
+
+ else if (code1 == MULT || code1 == SIGN_EXTEND || code0 == MEM)
+ {
+ find_reloads_address_1 (orig_op0, 0, &XEXP (x, 0), opnum, type,
+ ind_levels);
+ find_reloads_address_1 (orig_op1, 1, &XEXP (x, 1), opnum, type,
+ ind_levels);
+ }
+
+ else if (code0 == CONST_INT || code0 == CONST
+ || code0 == SYMBOL_REF || code0 == LABEL_REF)
+ find_reloads_address_1 (orig_op1, 0, &XEXP (x, 1), opnum, type,
+ ind_levels);
+
+ else if (code1 == CONST_INT || code1 == CONST
+ || code1 == SYMBOL_REF || code1 == LABEL_REF)
+ find_reloads_address_1 (orig_op0, 0, &XEXP (x, 0), opnum, type,
+ ind_levels);
+
+ else if (code0 == REG && code1 == REG)
+ {
+ if (REG_OK_FOR_INDEX_P (op0)
+ && REG_OK_FOR_BASE_P (op1))
+ return 0;
+ else if (REG_OK_FOR_INDEX_P (op1)
+ && REG_OK_FOR_BASE_P (op0))
+ return 0;
+ else if (REG_OK_FOR_BASE_P (op1))
+ find_reloads_address_1 (orig_op0, 1, &XEXP (x, 0), opnum, type,
+ ind_levels);
+ else if (REG_OK_FOR_BASE_P (op0))
+ find_reloads_address_1 (orig_op1, 1, &XEXP (x, 1), opnum, type,
+ ind_levels);
+ else if (REG_OK_FOR_INDEX_P (op1))
+ find_reloads_address_1 (orig_op0, 0, &XEXP (x, 0), opnum, type,
+ ind_levels);
+ else if (REG_OK_FOR_INDEX_P (op0))
+ find_reloads_address_1 (orig_op1, 0, &XEXP (x, 1), opnum, type,
+ ind_levels);
+ else
+ {
+ find_reloads_address_1 (orig_op0, 1, &XEXP (x, 0), opnum, type,
+ ind_levels);
+ find_reloads_address_1 (orig_op1, 0, &XEXP (x, 1), opnum, type,
+ ind_levels);
+ }
+ }
+
+ else if (code0 == REG)
+ {
+ find_reloads_address_1 (orig_op0, 1, &XEXP (x, 0), opnum, type,
+ ind_levels);
+ find_reloads_address_1 (orig_op1, 0, &XEXP (x, 1), opnum, type,
+ ind_levels);
+ }
+
+ else if (code1 == REG)
+ {
+ find_reloads_address_1 (orig_op1, 1, &XEXP (x, 1), opnum, type,
+ ind_levels);
+ find_reloads_address_1 (orig_op0, 0, &XEXP (x, 0), opnum, type,
+ ind_levels);
+ }
+ }
+
+ return 0;
+
+ case POST_INC:
+ case POST_DEC:
+ case PRE_INC:
+ case PRE_DEC:
+ if (GET_CODE (XEXP (x, 0)) == REG)
+ {
+ register int regno = REGNO (XEXP (x, 0));
+ int value = 0;
+ rtx x_orig = x;
+
+ /* A register that is incremented cannot be constant! */
+ if (regno >= FIRST_PSEUDO_REGISTER
+ && reg_equiv_constant[regno] != 0)
+ abort ();
+
+ /* Handle a register that is equivalent to a memory location
+ which cannot be addressed directly. */
+ if (reg_equiv_address[regno] != 0)
+ {
+ rtx tem = make_memloc (XEXP (x, 0), regno);
+ /* First reload the memory location's address. */
+ find_reloads_address (GET_MODE (tem), 0, XEXP (tem, 0),
+ &XEXP (tem, 0), opnum, type, ind_levels);
+ /* Put this inside a new increment-expression. */
+ x = gen_rtx (GET_CODE (x), GET_MODE (x), tem);
+ /* Proceed to reload that, as if it contained a register. */
+ }
+
+ /* If we have a hard register that is ok as an index,
+ don't make a reload. If an autoincrement of a nice register
+ isn't "valid", it must be that no autoincrement is "valid".
+ If that is true and something made an autoincrement anyway,
+ this must be a special context where one is allowed.
+ (For example, a "push" instruction.)
+ We can't improve this address, so leave it alone. */
+
+ /* Otherwise, reload the autoincrement into a suitable hard reg
+ and record how much to increment by. */
+
+ if (reg_renumber[regno] >= 0)
+ regno = reg_renumber[regno];
+ if ((regno >= FIRST_PSEUDO_REGISTER
+ || !(context ? REGNO_OK_FOR_INDEX_P (regno)
+ : REGNO_OK_FOR_BASE_P (regno))))
+ {
+ register rtx link;
+
+ int reloadnum
+ = push_reload (x, NULL_RTX, loc, NULL_PTR,
+ context ? INDEX_REG_CLASS : BASE_REG_CLASS,
+ GET_MODE (x), GET_MODE (x), VOIDmode, 0,
+ opnum, type);
+ reload_inc[reloadnum]
+ = find_inc_amount (PATTERN (this_insn), XEXP (x_orig, 0));
+
+ value = 1;
+
+#ifdef AUTO_INC_DEC
+ /* Update the REG_INC notes. */
+
+ for (link = REG_NOTES (this_insn);
+ link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_INC
+ && REGNO (XEXP (link, 0)) == REGNO (XEXP (x_orig, 0)))
+ push_replacement (&XEXP (link, 0), reloadnum, VOIDmode);
+#endif
+ }
+ return value;
+ }
+
+ else if (GET_CODE (XEXP (x, 0)) == MEM)
+ {
+ /* This is probably the result of a substitution, by eliminate_regs,
+ of an equivalent address for a pseudo that was not allocated to a
+ hard register. Verify that the specified address is valid and
+ reload it into a register. */
+ rtx tem = XEXP (x, 0);
+ register rtx link;
+ int reloadnum;
+
+ /* Since we know we are going to reload this item, don't decrement
+ for the indirection level.
+
+ Note that this is actually conservative: it would be slightly
+ more efficient to use the value of SPILL_INDIRECT_LEVELS from
+ reload1.c here. */
+ find_reloads_address (GET_MODE (x), &XEXP (x, 0),
+ XEXP (XEXP (x, 0), 0), &XEXP (XEXP (x, 0), 0),
+ opnum, type, ind_levels);
+
+ reloadnum = push_reload (x, NULL_RTX, loc, NULL_PTR,
+ context ? INDEX_REG_CLASS : BASE_REG_CLASS,
+ GET_MODE (x), VOIDmode, 0, 0, opnum, type);
+ reload_inc[reloadnum]
+ = find_inc_amount (PATTERN (this_insn), XEXP (x, 0));
+
+ link = FIND_REG_INC_NOTE (this_insn, tem);
+ if (link != 0)
+ push_replacement (&XEXP (link, 0), reloadnum, VOIDmode);
+
+ return 1;
+ }
+ return 0;
+
+ case MEM:
+ /* This is probably the result of a substitution, by eliminate_regs, of
+ an equivalent address for a pseudo that was not allocated to a hard
+ register. Verify that the specified address is valid and reload it
+ into a register.
+
+ Since we know we are going to reload this item, don't decrement for
+ the indirection level.
+
+ Note that this is actually conservative: it would be slightly more
+ efficient to use the value of SPILL_INDIRECT_LEVELS from
+ reload1.c here. */
+
+ find_reloads_address (GET_MODE (x), loc, XEXP (x, 0), &XEXP (x, 0),
+ opnum, type, ind_levels);
+ push_reload (*loc, NULL_RTX, loc, NULL_PTR,
+ context ? INDEX_REG_CLASS : BASE_REG_CLASS,
+ GET_MODE (x), VOIDmode, 0, 0, opnum, type);
+ return 1;
+
+ case REG:
+ {
+ register int regno = REGNO (x);
+
+ if (reg_equiv_constant[regno] != 0)
+ {
+ find_reloads_address_part (reg_equiv_constant[regno], loc,
+ (context ? INDEX_REG_CLASS
+ : BASE_REG_CLASS),
+ GET_MODE (x), opnum, type, ind_levels);
+ return 1;
+ }
+
+#if 0 /* This might screw code in reload1.c to delete prior output-reload
+ that feeds this insn. */
+ if (reg_equiv_mem[regno] != 0)
+ {
+ push_reload (reg_equiv_mem[regno], NULL_RTX, loc, NULL_PTR,
+ context ? INDEX_REG_CLASS : BASE_REG_CLASS,
+ GET_MODE (x), VOIDmode, 0, 0, opnum, type);
+ return 1;
+ }
+#endif
+
+ if (reg_equiv_address[regno] != 0)
+ {
+ x = make_memloc (x, regno);
+ find_reloads_address (GET_MODE (x), 0, XEXP (x, 0), &XEXP (x, 0),
+ opnum, type, ind_levels);
+ }
+
+ if (reg_renumber[regno] >= 0)
+ regno = reg_renumber[regno];
+
+ if ((regno >= FIRST_PSEUDO_REGISTER
+ || !(context ? REGNO_OK_FOR_INDEX_P (regno)
+ : REGNO_OK_FOR_BASE_P (regno))))
+ {
+ push_reload (x, NULL_RTX, loc, NULL_PTR,
+ context ? INDEX_REG_CLASS : BASE_REG_CLASS,
+ GET_MODE (x), VOIDmode, 0, 0, opnum, type);
+ return 1;
+ }
+
+ /* If a register appearing in an address is the subject of a CLOBBER
+ in this insn, reload it into some other register to be safe.
+ The CLOBBER is supposed to make the register unavailable
+ from before this insn to after it. */
+ if (regno_clobbered_p (regno, this_insn))
+ {
+ push_reload (x, NULL_RTX, loc, NULL_PTR,
+ context ? INDEX_REG_CLASS : BASE_REG_CLASS,
+ GET_MODE (x), VOIDmode, 0, 0, opnum, type);
+ return 1;
+ }
+ }
+ return 0;
+
+ case SUBREG:
+ /* If this is a SUBREG of a hard register and the resulting register is
+ of the wrong class, reload the whole SUBREG. This avoids needless
+ copies if SUBREG_REG is multi-word. */
+ if (GET_CODE (SUBREG_REG (x)) == REG
+ && REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER)
+ {
+ int regno = REGNO (SUBREG_REG (x)) + SUBREG_WORD (x);
+
+ if (! (context ? REGNO_OK_FOR_INDEX_P (regno)
+ : REGNO_OK_FOR_BASE_P (regno)))
+ {
+ push_reload (x, NULL_RTX, loc, NULL_PTR,
+ context ? INDEX_REG_CLASS : BASE_REG_CLASS,
+ GET_MODE (x), VOIDmode, 0, 0, opnum, type);
+ return 1;
+ }
+ }
+ break;
+ }
+
+ {
+ register char *fmt = GET_RTX_FORMAT (code);
+ register int i;
+
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ find_reloads_address_1 (XEXP (x, i), context, &XEXP (x, i),
+ opnum, type, ind_levels);
+ }
+ }
+
+ return 0;
+}
+
+/* X, which is found at *LOC, is a part of an address that needs to be
+ reloaded into a register of class CLASS. If X is a constant, or if
+ X is a PLUS that contains a constant, check that the constant is a
+ legitimate operand and that we are supposed to be able to load
+ it into the register.
+
+ If not, force the constant into memory and reload the MEM instead.
+
+ MODE is the mode to use, in case X is an integer constant.
+
+ OPNUM and TYPE describe the purpose of any reloads made.
+
+ IND_LEVELS says how many levels of indirect addressing this machine
+ supports. */
+
+static void
+find_reloads_address_part (x, loc, class, mode, opnum, type, ind_levels)
+ rtx x;
+ rtx *loc;
+ enum reg_class class;
+ enum machine_mode mode;
+ int opnum;
+ enum reload_type type;
+ int ind_levels;
+{
+ if (CONSTANT_P (x)
+ && (! LEGITIMATE_CONSTANT_P (x)
+ || PREFERRED_RELOAD_CLASS (x, class) == NO_REGS))
+ {
+ rtx tem = x = force_const_mem (mode, x);
+ find_reloads_address (mode, &tem, XEXP (tem, 0), &XEXP (tem, 0),
+ opnum, type, ind_levels);
+ }
+
+ else if (GET_CODE (x) == PLUS
+ && CONSTANT_P (XEXP (x, 1))
+ && (! LEGITIMATE_CONSTANT_P (XEXP (x, 1))
+ || PREFERRED_RELOAD_CLASS (XEXP (x, 1), class) == NO_REGS))
+ {
+ rtx tem = force_const_mem (GET_MODE (x), XEXP (x, 1));
+
+ x = gen_rtx (PLUS, GET_MODE (x), XEXP (x, 0), tem);
+ find_reloads_address (mode, &tem, XEXP (tem, 0), &XEXP (tem, 0),
+ opnum, type, ind_levels);
+ }
+
+ push_reload (x, NULL_RTX, loc, NULL_PTR, class,
+ mode, VOIDmode, 0, 0, opnum, type);
+}
+
+/* Substitute into the current INSN the registers into which we have reloaded
+ the things that need reloading. The array `replacements'
+ says contains the locations of all pointers that must be changed
+ and says what to replace them with.
+
+ Return the rtx that X translates into; usually X, but modified. */
+
+void
+subst_reloads ()
+{
+ register int i;
+
+ for (i = 0; i < n_replacements; i++)
+ {
+ register struct replacement *r = &replacements[i];
+ register rtx reloadreg = reload_reg_rtx[r->what];
+ if (reloadreg)
+ {
+ /* Encapsulate RELOADREG so its machine mode matches what
+ used to be there. Note that gen_lowpart_common will
+ do the wrong thing if RELOADREG is multi-word. RELOADREG
+ will always be a REG here. */
+ if (GET_MODE (reloadreg) != r->mode && r->mode != VOIDmode)
+ reloadreg = gen_rtx (REG, r->mode, REGNO (reloadreg));
+
+ /* If we are putting this into a SUBREG and RELOADREG is a
+ SUBREG, we would be making nested SUBREGs, so we have to fix
+ this up. Note that r->where == &SUBREG_REG (*r->subreg_loc). */
+
+ if (r->subreg_loc != 0 && GET_CODE (reloadreg) == SUBREG)
+ {
+ if (GET_MODE (*r->subreg_loc)
+ == GET_MODE (SUBREG_REG (reloadreg)))
+ *r->subreg_loc = SUBREG_REG (reloadreg);
+ else
+ {
+ *r->where = SUBREG_REG (reloadreg);
+ SUBREG_WORD (*r->subreg_loc) += SUBREG_WORD (reloadreg);
+ }
+ }
+ else
+ *r->where = reloadreg;
+ }
+ /* If reload got no reg and isn't optional, something's wrong. */
+ else if (! reload_optional[r->what])
+ abort ();
+ }
+}
+
+/* Make a copy of any replacements being done into X and move those copies
+ to locations in Y, a copy of X. We only look at the highest level of
+ the RTL. */
+
+void
+copy_replacements (x, y)
+ rtx x;
+ rtx y;
+{
+ int i, j;
+ enum rtx_code code = GET_CODE (x);
+ char *fmt = GET_RTX_FORMAT (code);
+ struct replacement *r;
+
+ /* We can't support X being a SUBREG because we might then need to know its
+ location if something inside it was replaced. */
+ if (code == SUBREG)
+ abort ();
+
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ if (fmt[i] == 'e')
+ for (j = 0; j < n_replacements; j++)
+ {
+ if (replacements[j].subreg_loc == &XEXP (x, i))
+ {
+ r = &replacements[n_replacements++];
+ r->where = replacements[j].where;
+ r->subreg_loc = &XEXP (y, i);
+ r->what = replacements[j].what;
+ r->mode = replacements[j].mode;
+ }
+ else if (replacements[j].where == &XEXP (x, i))
+ {
+ r = &replacements[n_replacements++];
+ r->where = &XEXP (y, i);
+ r->subreg_loc = 0;
+ r->what = replacements[j].what;
+ r->mode = replacements[j].mode;
+ }
+ }
+}
+
+/* If LOC was scheduled to be replaced by something, return the replacement.
+ Otherwise, return *LOC. */
+
+rtx
+find_replacement (loc)
+ rtx *loc;
+{
+ struct replacement *r;
+
+ for (r = &replacements[0]; r < &replacements[n_replacements]; r++)
+ {
+ rtx reloadreg = reload_reg_rtx[r->what];
+
+ if (reloadreg && r->where == loc)
+ {
+ if (r->mode != VOIDmode && GET_MODE (reloadreg) != r->mode)
+ reloadreg = gen_rtx (REG, r->mode, REGNO (reloadreg));
+
+ return reloadreg;
+ }
+ else if (reloadreg && r->subreg_loc == loc)
+ {
+ /* RELOADREG must be either a REG or a SUBREG.
+
+ ??? Is it actually still ever a SUBREG? If so, why? */
+
+ if (GET_CODE (reloadreg) == REG)
+ return gen_rtx (REG, GET_MODE (*loc),
+ REGNO (reloadreg) + SUBREG_WORD (*loc));
+ else if (GET_MODE (reloadreg) == GET_MODE (*loc))
+ return reloadreg;
+ else
+ return gen_rtx (SUBREG, GET_MODE (*loc), SUBREG_REG (reloadreg),
+ SUBREG_WORD (reloadreg) + SUBREG_WORD (*loc));
+ }
+ }
+
+ return *loc;
+}
+
+/* Return nonzero if register in range [REGNO, ENDREGNO)
+ appears either explicitly or implicitly in X
+ other than being stored into (except for earlyclobber operands).
+
+ References contained within the substructure at LOC do not count.
+ LOC may be zero, meaning don't ignore anything.
+
+ This is similar to refers_to_regno_p in rtlanal.c except that we
+ look at equivalences for pseudos that didn't get hard registers. */
+
+int
+refers_to_regno_for_reload_p (regno, endregno, x, loc)
+ int regno, endregno;
+ rtx x;
+ rtx *loc;
+{
+ register int i;
+ register RTX_CODE code;
+ register char *fmt;
+
+ if (x == 0)
+ return 0;
+
+ repeat:
+ code = GET_CODE (x);
+
+ switch (code)
+ {
+ case REG:
+ i = REGNO (x);
+
+ /* If this is a pseudo, a hard register must not have been allocated.
+ X must therefore either be a constant or be in memory. */
+ if (i >= FIRST_PSEUDO_REGISTER)
+ {
+ if (reg_equiv_memory_loc[i])
+ return refers_to_regno_for_reload_p (regno, endregno,
+ reg_equiv_memory_loc[i],
+ NULL_PTR);
+
+ if (reg_equiv_constant[i])
+ return 0;
+
+ abort ();
+ }
+
+ return (endregno > i
+ && regno < i + (i < FIRST_PSEUDO_REGISTER
+ ? HARD_REGNO_NREGS (i, GET_MODE (x))
+ : 1));
+
+ case SUBREG:
+ /* If this is a SUBREG of a hard reg, we can see exactly which
+ registers are being modified. Otherwise, handle normally. */
+ if (GET_CODE (SUBREG_REG (x)) == REG
+ && REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER)
+ {
+ int inner_regno = REGNO (SUBREG_REG (x)) + SUBREG_WORD (x);
+ int inner_endregno
+ = inner_regno + (inner_regno < FIRST_PSEUDO_REGISTER
+ ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1);
+
+ return endregno > inner_regno && regno < inner_endregno;
+ }
+ break;
+
+ case CLOBBER:
+ case SET:
+ if (&SET_DEST (x) != loc
+ /* Note setting a SUBREG counts as referring to the REG it is in for
+ a pseudo but not for hard registers since we can
+ treat each word individually. */
+ && ((GET_CODE (SET_DEST (x)) == SUBREG
+ && loc != &SUBREG_REG (SET_DEST (x))
+ && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG
+ && REGNO (SUBREG_REG (SET_DEST (x))) >= FIRST_PSEUDO_REGISTER
+ && refers_to_regno_for_reload_p (regno, endregno,
+ SUBREG_REG (SET_DEST (x)),
+ loc))
+ /* If the ouput is an earlyclobber operand, this is
+ a conflict. */
+ || ((GET_CODE (SET_DEST (x)) != REG
+ || earlyclobber_operand_p (SET_DEST (x)))
+ && refers_to_regno_for_reload_p (regno, endregno,
+ SET_DEST (x), loc))))
+ return 1;
+
+ if (code == CLOBBER || loc == &SET_SRC (x))
+ return 0;
+ x = SET_SRC (x);
+ goto repeat;
+ }
+
+ /* X does not match, so try its subexpressions. */
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e' && loc != &XEXP (x, i))
+ {
+ if (i == 0)
+ {
+ x = XEXP (x, 0);
+ goto repeat;
+ }
+ else
+ if (refers_to_regno_for_reload_p (regno, endregno,
+ XEXP (x, i), loc))
+ return 1;
+ }
+ else if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = XVECLEN (x, i) - 1; j >=0; j--)
+ if (loc != &XVECEXP (x, i, j)
+ && refers_to_regno_for_reload_p (regno, endregno,
+ XVECEXP (x, i, j), loc))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Nonzero if modifying X will affect IN. If X is a register or a SUBREG,
+ we check if any register number in X conflicts with the relevant register
+ numbers. If X is a constant, return 0. If X is a MEM, return 1 iff IN
+ contains a MEM (we don't bother checking for memory addresses that can't
+ conflict because we expect this to be a rare case.
+
+ This function is similar to reg_overlap_mention_p in rtlanal.c except
+ that we look at equivalences for pseudos that didn't get hard registers. */
+
+int
+reg_overlap_mentioned_for_reload_p (x, in)
+ rtx x, in;
+{
+ int regno, endregno;
+
+ if (GET_CODE (x) == SUBREG)
+ {
+ regno = REGNO (SUBREG_REG (x));
+ if (regno < FIRST_PSEUDO_REGISTER)
+ regno += SUBREG_WORD (x);
+ }
+ else if (GET_CODE (x) == REG)
+ {
+ regno = REGNO (x);
+
+ /* If this is a pseudo, it must not have been assigned a hard register.
+ Therefore, it must either be in memory or be a constant. */
+
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ {
+ if (reg_equiv_memory_loc[regno])
+ return refers_to_mem_for_reload_p (in);
+ else if (reg_equiv_constant[regno])
+ return 0;
+ abort ();
+ }
+ }
+ else if (CONSTANT_P (x))
+ return 0;
+ else if (GET_CODE (x) == MEM)
+ return refers_to_mem_for_reload_p (in);
+ else if (GET_CODE (x) == SCRATCH || GET_CODE (x) == PC
+ || GET_CODE (x) == CC0)
+ return reg_mentioned_p (x, in);
+ else
+ abort ();
+
+ endregno = regno + (regno < FIRST_PSEUDO_REGISTER
+ ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1);
+
+ return refers_to_regno_for_reload_p (regno, endregno, in, NULL_PTR);
+}
+
+/* Return nonzero if anything in X contains a MEM. Look also for pseudo
+ registers. */
+
+int
+refers_to_mem_for_reload_p (x)
+ rtx x;
+{
+ char *fmt;
+ int i;
+
+ if (GET_CODE (x) == MEM)
+ return 1;
+
+ if (GET_CODE (x) == REG)
+ return (REGNO (x) >= FIRST_PSEUDO_REGISTER
+ && reg_equiv_memory_loc[REGNO (x)]);
+
+ fmt = GET_RTX_FORMAT (GET_CODE (x));
+ for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+ if (fmt[i] == 'e'
+ && (GET_CODE (XEXP (x, i)) == MEM
+ || refers_to_mem_for_reload_p (XEXP (x, i))))
+ return 1;
+
+ return 0;
+}
+
+/* Check the insns before INSN to see if there is a suitable register
+ containing the same value as GOAL.
+ If OTHER is -1, look for a register in class CLASS.
+ Otherwise, just see if register number OTHER shares GOAL's value.
+
+ Return an rtx for the register found, or zero if none is found.
+
+ If RELOAD_REG_P is (short *)1,
+ we reject any hard reg that appears in reload_reg_rtx
+ because such a hard reg is also needed coming into this insn.
+
+ If RELOAD_REG_P is any other nonzero value,
+ it is a vector indexed by hard reg number
+ and we reject any hard reg whose element in the vector is nonnegative
+ as well as any that appears in reload_reg_rtx.
+
+ If GOAL is zero, then GOALREG is a register number; we look
+ for an equivalent for that register.
+
+ MODE is the machine mode of the value we want an equivalence for.
+ If GOAL is nonzero and not VOIDmode, then it must have mode MODE.
+
+ This function is used by jump.c as well as in the reload pass.
+
+ If GOAL is the sum of the stack pointer and a constant, we treat it
+ as if it were a constant except that sp is required to be unchanging. */
+
+rtx
+find_equiv_reg (goal, insn, class, other, reload_reg_p, goalreg, mode)
+ register rtx goal;
+ rtx insn;
+ enum reg_class class;
+ register int other;
+ short *reload_reg_p;
+ int goalreg;
+ enum machine_mode mode;
+{
+ register rtx p = insn;
+ rtx goaltry, valtry, value, where;
+ register rtx pat;
+ register int regno = -1;
+ int valueno;
+ int goal_mem = 0;
+ int goal_const = 0;
+ int goal_mem_addr_varies = 0;
+ int need_stable_sp = 0;
+ int nregs;
+ int valuenregs;
+
+ if (goal == 0)
+ regno = goalreg;
+ else if (GET_CODE (goal) == REG)
+ regno = REGNO (goal);
+ else if (GET_CODE (goal) == MEM)
+ {
+ enum rtx_code code = GET_CODE (XEXP (goal, 0));
+ if (MEM_VOLATILE_P (goal))
+ return 0;
+ if (flag_float_store && GET_MODE_CLASS (GET_MODE (goal)) == MODE_FLOAT)
+ return 0;
+ /* An address with side effects must be reexecuted. */
+ switch (code)
+ {
+ case POST_INC:
+ case PRE_INC:
+ case POST_DEC:
+ case PRE_DEC:
+ return 0;
+ }
+ goal_mem = 1;
+ }
+ else if (CONSTANT_P (goal))
+ goal_const = 1;
+ else if (GET_CODE (goal) == PLUS
+ && XEXP (goal, 0) == stack_pointer_rtx
+ && CONSTANT_P (XEXP (goal, 1)))
+ goal_const = need_stable_sp = 1;
+ else
+ return 0;
+
+ /* On some machines, certain regs must always be rejected
+ because they don't behave the way ordinary registers do. */
+
+#ifdef OVERLAPPING_REGNO_P
+ if (regno >= 0 && regno < FIRST_PSEUDO_REGISTER
+ && OVERLAPPING_REGNO_P (regno))
+ return 0;
+#endif
+
+ /* Scan insns back from INSN, looking for one that copies
+ a value into or out of GOAL.
+ Stop and give up if we reach a label. */
+
+ while (1)
+ {
+ p = PREV_INSN (p);
+ if (p == 0 || GET_CODE (p) == CODE_LABEL)
+ return 0;
+ if (GET_CODE (p) == INSN
+ /* If we don't want spill regs ... */
+ && (! (reload_reg_p != 0
+ && reload_reg_p != (short *) (HOST_WIDE_INT) 1)
+ /* ... then ignore insns introduced by reload; they aren't useful
+ and can cause results in reload_as_needed to be different
+ from what they were when calculating the need for spills.
+ If we notice an input-reload insn here, we will reject it below,
+ but it might hide a usable equivalent. That makes bad code.
+ It may even abort: perhaps no reg was spilled for this insn
+ because it was assumed we would find that equivalent. */
+ || INSN_UID (p) < reload_first_uid))
+ {
+ rtx tem;
+ pat = single_set (p);
+ /* First check for something that sets some reg equal to GOAL. */
+ if (pat != 0
+ && ((regno >= 0
+ && true_regnum (SET_SRC (pat)) == regno
+ && (valueno = true_regnum (valtry = SET_DEST (pat))) >= 0)
+ ||
+ (regno >= 0
+ && true_regnum (SET_DEST (pat)) == regno
+ && (valueno = true_regnum (valtry = SET_SRC (pat))) >= 0)
+ ||
+ (goal_const && rtx_equal_p (SET_SRC (pat), goal)
+ && (valueno = true_regnum (valtry = SET_DEST (pat))) >= 0)
+ || (goal_mem
+ && (valueno = true_regnum (valtry = SET_DEST (pat))) >= 0
+ && rtx_renumbered_equal_p (goal, SET_SRC (pat)))
+ || (goal_mem
+ && (valueno = true_regnum (valtry = SET_SRC (pat))) >= 0
+ && rtx_renumbered_equal_p (goal, SET_DEST (pat)))
+ /* If we are looking for a constant,
+ and something equivalent to that constant was copied
+ into a reg, we can use that reg. */
+ || (goal_const && (tem = find_reg_note (p, REG_EQUIV,
+ NULL_RTX))
+ && rtx_equal_p (XEXP (tem, 0), goal)
+ && (valueno = true_regnum (valtry = SET_DEST (pat))) >= 0)
+ || (goal_const && (tem = find_reg_note (p, REG_EQUIV,
+ NULL_RTX))
+ && GET_CODE (SET_DEST (pat)) == REG
+ && GET_CODE (XEXP (tem, 0)) == CONST_DOUBLE
+ && GET_MODE_CLASS (GET_MODE (XEXP (tem, 0))) == MODE_FLOAT
+ && GET_CODE (goal) == CONST_INT
+ && 0 != (goaltry = operand_subword (XEXP (tem, 0), 0, 0,
+ VOIDmode))
+ && rtx_equal_p (goal, goaltry)
+ && (valtry = operand_subword (SET_DEST (pat), 0, 0,
+ VOIDmode))
+ && (valueno = true_regnum (valtry)) >= 0)
+ || (goal_const && (tem = find_reg_note (p, REG_EQUIV,
+ NULL_RTX))
+ && GET_CODE (SET_DEST (pat)) == REG
+ && GET_CODE (XEXP (tem, 0)) == CONST_DOUBLE
+ && GET_MODE_CLASS (GET_MODE (XEXP (tem, 0))) == MODE_FLOAT
+ && GET_CODE (goal) == CONST_INT
+ && 0 != (goaltry = operand_subword (XEXP (tem, 0), 1, 0,
+ VOIDmode))
+ && rtx_equal_p (goal, goaltry)
+ && (valtry
+ = operand_subword (SET_DEST (pat), 1, 0, VOIDmode))
+ && (valueno = true_regnum (valtry)) >= 0)))
+ if (other >= 0
+ ? valueno == other
+ : ((unsigned) valueno < FIRST_PSEUDO_REGISTER
+ && TEST_HARD_REG_BIT (reg_class_contents[(int) class],
+ valueno)))
+ {
+ value = valtry;
+ where = p;
+ break;
+ }
+ }
+ }
+
+ /* We found a previous insn copying GOAL into a suitable other reg VALUE
+ (or copying VALUE into GOAL, if GOAL is also a register).
+ Now verify that VALUE is really valid. */
+
+ /* VALUENO is the register number of VALUE; a hard register. */
+
+ /* Don't try to re-use something that is killed in this insn. We want
+ to be able to trust REG_UNUSED notes. */
+ if (find_reg_note (where, REG_UNUSED, value))
+ return 0;
+
+ /* If we propose to get the value from the stack pointer or if GOAL is
+ a MEM based on the stack pointer, we need a stable SP. */
+ if (valueno == STACK_POINTER_REGNUM
+ || (goal_mem && reg_overlap_mentioned_for_reload_p (stack_pointer_rtx,
+ goal)))
+ need_stable_sp = 1;
+
+ /* Reject VALUE if the copy-insn moved the wrong sort of datum. */
+ if (GET_MODE (value) != mode)
+ return 0;
+
+ /* Reject VALUE if it was loaded from GOAL
+ and is also a register that appears in the address of GOAL. */
+
+ if (goal_mem && value == SET_DEST (PATTERN (where))
+ && refers_to_regno_for_reload_p (valueno,
+ (valueno
+ + HARD_REGNO_NREGS (valueno, mode)),
+ goal, NULL_PTR))
+ return 0;
+
+ /* Reject registers that overlap GOAL. */
+
+ if (!goal_mem && !goal_const
+ && regno + HARD_REGNO_NREGS (regno, mode) > valueno
+ && regno < valueno + HARD_REGNO_NREGS (valueno, mode))
+ return 0;
+
+ /* Reject VALUE if it is one of the regs reserved for reloads.
+ Reload1 knows how to reuse them anyway, and it would get
+ confused if we allocated one without its knowledge.
+ (Now that insns introduced by reload are ignored above,
+ this case shouldn't happen, but I'm not positive.) */
+
+ if (reload_reg_p != 0 && reload_reg_p != (short *) (HOST_WIDE_INT) 1
+ && reload_reg_p[valueno] >= 0)
+ return 0;
+
+ /* On some machines, certain regs must always be rejected
+ because they don't behave the way ordinary registers do. */
+
+#ifdef OVERLAPPING_REGNO_P
+ if (OVERLAPPING_REGNO_P (valueno))
+ return 0;
+#endif
+
+ nregs = HARD_REGNO_NREGS (regno, mode);
+ valuenregs = HARD_REGNO_NREGS (valueno, mode);
+
+ /* Reject VALUE if it is a register being used for an input reload
+ even if it is not one of those reserved. */
+
+ if (reload_reg_p != 0)
+ {
+ int i;
+ for (i = 0; i < n_reloads; i++)
+ if (reload_reg_rtx[i] != 0 && reload_in[i])
+ {
+ int regno1 = REGNO (reload_reg_rtx[i]);
+ int nregs1 = HARD_REGNO_NREGS (regno1,
+ GET_MODE (reload_reg_rtx[i]));
+ if (regno1 < valueno + valuenregs
+ && regno1 + nregs1 > valueno)
+ return 0;
+ }
+ }
+
+ if (goal_mem)
+ /* We must treat frame pointer as varying here,
+ since it can vary--in a nonlocal goto as generated by expand_goto. */
+ goal_mem_addr_varies = !CONSTANT_ADDRESS_P (XEXP (goal, 0));
+
+ /* Now verify that the values of GOAL and VALUE remain unaltered
+ until INSN is reached. */
+
+ p = insn;
+ while (1)
+ {
+ p = PREV_INSN (p);
+ if (p == where)
+ return value;
+
+ /* Don't trust the conversion past a function call
+ if either of the two is in a call-clobbered register, or memory. */
+ if (GET_CODE (p) == CALL_INSN
+ && ((regno >= 0 && regno < FIRST_PSEUDO_REGISTER
+ && call_used_regs[regno])
+ ||
+ (valueno >= 0 && valueno < FIRST_PSEUDO_REGISTER
+ && call_used_regs[valueno])
+ ||
+ goal_mem
+ || need_stable_sp))
+ return 0;
+
+#ifdef INSN_CLOBBERS_REGNO_P
+ if ((valueno >= 0 && valueno < FIRST_PSEUDO_REGISTER
+ && INSN_CLOBBERS_REGNO_P (p, valueno))
+ || (regno >= 0 && regno < FIRST_PSEUDO_REGISTER
+ && INSN_CLOBBERS_REGNO_P (p, regno)))
+ return 0;
+#endif
+
+ if (GET_RTX_CLASS (GET_CODE (p)) == 'i')
+ {
+ /* If this insn P stores in either GOAL or VALUE, return 0.
+ If GOAL is a memory ref and this insn writes memory, return 0.
+ If GOAL is a memory ref and its address is not constant,
+ and this insn P changes a register used in GOAL, return 0. */
+
+ pat = PATTERN (p);
+ if (GET_CODE (pat) == SET || GET_CODE (pat) == CLOBBER)
+ {
+ register rtx dest = SET_DEST (pat);
+ while (GET_CODE (dest) == SUBREG
+ || GET_CODE (dest) == ZERO_EXTRACT
+ || GET_CODE (dest) == SIGN_EXTRACT
+ || GET_CODE (dest) == STRICT_LOW_PART)
+ dest = XEXP (dest, 0);
+ if (GET_CODE (dest) == REG)
+ {
+ register int xregno = REGNO (dest);
+ int xnregs;
+ if (REGNO (dest) < FIRST_PSEUDO_REGISTER)
+ xnregs = HARD_REGNO_NREGS (xregno, GET_MODE (dest));
+ else
+ xnregs = 1;
+ if (xregno < regno + nregs && xregno + xnregs > regno)
+ return 0;
+ if (xregno < valueno + valuenregs
+ && xregno + xnregs > valueno)
+ return 0;
+ if (goal_mem_addr_varies
+ && reg_overlap_mentioned_for_reload_p (dest, goal))
+ return 0;
+ }
+ else if (goal_mem && GET_CODE (dest) == MEM
+ && ! push_operand (dest, GET_MODE (dest)))
+ return 0;
+ else if (need_stable_sp && push_operand (dest, GET_MODE (dest)))
+ return 0;
+ }
+ else if (GET_CODE (pat) == PARALLEL)
+ {
+ register int i;
+ for (i = XVECLEN (pat, 0) - 1; i >= 0; i--)
+ {
+ register rtx v1 = XVECEXP (pat, 0, i);
+ if (GET_CODE (v1) == SET || GET_CODE (v1) == CLOBBER)
+ {
+ register rtx dest = SET_DEST (v1);
+ while (GET_CODE (dest) == SUBREG
+ || GET_CODE (dest) == ZERO_EXTRACT
+ || GET_CODE (dest) == SIGN_EXTRACT
+ || GET_CODE (dest) == STRICT_LOW_PART)
+ dest = XEXP (dest, 0);
+ if (GET_CODE (dest) == REG)
+ {
+ register int xregno = REGNO (dest);
+ int xnregs;
+ if (REGNO (dest) < FIRST_PSEUDO_REGISTER)
+ xnregs = HARD_REGNO_NREGS (xregno, GET_MODE (dest));
+ else
+ xnregs = 1;
+ if (xregno < regno + nregs
+ && xregno + xnregs > regno)
+ return 0;
+ if (xregno < valueno + valuenregs
+ && xregno + xnregs > valueno)
+ return 0;
+ if (goal_mem_addr_varies
+ && reg_overlap_mentioned_for_reload_p (dest,
+ goal))
+ return 0;
+ }
+ else if (goal_mem && GET_CODE (dest) == MEM
+ && ! push_operand (dest, GET_MODE (dest)))
+ return 0;
+ else if (need_stable_sp
+ && push_operand (dest, GET_MODE (dest)))
+ return 0;
+ }
+ }
+ }
+
+#ifdef AUTO_INC_DEC
+ /* If this insn auto-increments or auto-decrements
+ either regno or valueno, return 0 now.
+ If GOAL is a memory ref and its address is not constant,
+ and this insn P increments a register used in GOAL, return 0. */
+ {
+ register rtx link;
+
+ for (link = REG_NOTES (p); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_INC
+ && GET_CODE (XEXP (link, 0)) == REG)
+ {
+ register int incno = REGNO (XEXP (link, 0));
+ if (incno < regno + nregs && incno >= regno)
+ return 0;
+ if (incno < valueno + valuenregs && incno >= valueno)
+ return 0;
+ if (goal_mem_addr_varies
+ && reg_overlap_mentioned_for_reload_p (XEXP (link, 0),
+ goal))
+ return 0;
+ }
+ }
+#endif
+ }
+ }
+}
+
+/* Find a place where INCED appears in an increment or decrement operator
+ within X, and return the amount INCED is incremented or decremented by.
+ The value is always positive. */
+
+static int
+find_inc_amount (x, inced)
+ rtx x, inced;
+{
+ register enum rtx_code code = GET_CODE (x);
+ register char *fmt;
+ register int i;
+
+ if (code == MEM)
+ {
+ register rtx addr = XEXP (x, 0);
+ if ((GET_CODE (addr) == PRE_DEC
+ || GET_CODE (addr) == POST_DEC
+ || GET_CODE (addr) == PRE_INC
+ || GET_CODE (addr) == POST_INC)
+ && XEXP (addr, 0) == inced)
+ return GET_MODE_SIZE (GET_MODE (x));
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ {
+ register int tem = find_inc_amount (XEXP (x, i), inced);
+ if (tem != 0)
+ return tem;
+ }
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ {
+ register int tem = find_inc_amount (XVECEXP (x, i, j), inced);
+ if (tem != 0)
+ return tem;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Return 1 if register REGNO is the subject of a clobber in insn INSN. */
+
+int
+regno_clobbered_p (regno, insn)
+ int regno;
+ rtx insn;
+{
+ if (GET_CODE (PATTERN (insn)) == CLOBBER
+ && GET_CODE (XEXP (PATTERN (insn), 0)) == REG)
+ return REGNO (XEXP (PATTERN (insn), 0)) == regno;
+
+ if (GET_CODE (PATTERN (insn)) == PARALLEL)
+ {
+ int i = XVECLEN (PATTERN (insn), 0) - 1;
+
+ for (; i >= 0; i--)
+ {
+ rtx elt = XVECEXP (PATTERN (insn), 0, i);
+ if (GET_CODE (elt) == CLOBBER && GET_CODE (XEXP (elt, 0)) == REG
+ && REGNO (XEXP (elt, 0)) == regno)
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/gnu/usr.bin/cc/cc_int/reload1.c b/gnu/usr.bin/cc/cc_int/reload1.c
new file mode 100644
index 0000000..e46d764
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/reload1.c
@@ -0,0 +1,7122 @@
+/* Reload pseudo regs into hard regs for insns that require hard regs.
+ Copyright (C) 1987, 88, 89, 92, 93, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include <stdio.h>
+#include "config.h"
+#include "rtl.h"
+#include "obstack.h"
+#include "insn-config.h"
+#include "insn-flags.h"
+#include "insn-codes.h"
+#include "flags.h"
+#include "expr.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "reload.h"
+#include "recog.h"
+#include "basic-block.h"
+#include "output.h"
+
+/* This file contains the reload pass of the compiler, which is
+ run after register allocation has been done. It checks that
+ each insn is valid (operands required to be in registers really
+ are in registers of the proper class) and fixes up invalid ones
+ by copying values temporarily into registers for the insns
+ that need them.
+
+ The results of register allocation are described by the vector
+ reg_renumber; the insns still contain pseudo regs, but reg_renumber
+ can be used to find which hard reg, if any, a pseudo reg is in.
+
+ The technique we always use is to free up a few hard regs that are
+ called ``reload regs'', and for each place where a pseudo reg
+ must be in a hard reg, copy it temporarily into one of the reload regs.
+
+ All the pseudos that were formerly allocated to the hard regs that
+ are now in use as reload regs must be ``spilled''. This means
+ that they go to other hard regs, or to stack slots if no other
+ available hard regs can be found. Spilling can invalidate more
+ insns, requiring additional need for reloads, so we must keep checking
+ until the process stabilizes.
+
+ For machines with different classes of registers, we must keep track
+ of the register class needed for each reload, and make sure that
+ we allocate enough reload registers of each class.
+
+ The file reload.c contains the code that checks one insn for
+ validity and reports the reloads that it needs. This file
+ is in charge of scanning the entire rtl code, accumulating the
+ reload needs, spilling, assigning reload registers to use for
+ fixing up each insn, and generating the new insns to copy values
+ into the reload registers. */
+
+
+#ifndef REGISTER_MOVE_COST
+#define REGISTER_MOVE_COST(x, y) 2
+#endif
+
+#ifndef MEMORY_MOVE_COST
+#define MEMORY_MOVE_COST(x) 4
+#endif
+
+/* During reload_as_needed, element N contains a REG rtx for the hard reg
+ into which reg N has been reloaded (perhaps for a previous insn). */
+static rtx *reg_last_reload_reg;
+
+/* Elt N nonzero if reg_last_reload_reg[N] has been set in this insn
+ for an output reload that stores into reg N. */
+static char *reg_has_output_reload;
+
+/* Indicates which hard regs are reload-registers for an output reload
+ in the current insn. */
+static HARD_REG_SET reg_is_output_reload;
+
+/* Element N is the constant value to which pseudo reg N is equivalent,
+ or zero if pseudo reg N is not equivalent to a constant.
+ find_reloads looks at this in order to replace pseudo reg N
+ with the constant it stands for. */
+rtx *reg_equiv_constant;
+
+/* Element N is a memory location to which pseudo reg N is equivalent,
+ prior to any register elimination (such as frame pointer to stack
+ pointer). Depending on whether or not it is a valid address, this value
+ is transferred to either reg_equiv_address or reg_equiv_mem. */
+rtx *reg_equiv_memory_loc;
+
+/* Element N is the address of stack slot to which pseudo reg N is equivalent.
+ This is used when the address is not valid as a memory address
+ (because its displacement is too big for the machine.) */
+rtx *reg_equiv_address;
+
+/* Element N is the memory slot to which pseudo reg N is equivalent,
+ or zero if pseudo reg N is not equivalent to a memory slot. */
+rtx *reg_equiv_mem;
+
+/* Widest width in which each pseudo reg is referred to (via subreg). */
+static int *reg_max_ref_width;
+
+/* Element N is the insn that initialized reg N from its equivalent
+ constant or memory slot. */
+static rtx *reg_equiv_init;
+
+/* During reload_as_needed, element N contains the last pseudo regno
+ reloaded into the Nth reload register. This vector is in parallel
+ with spill_regs. If that pseudo reg occupied more than one register,
+ reg_reloaded_contents points to that pseudo for each spill register in
+ use; all of these must remain set for an inheritance to occur. */
+static int reg_reloaded_contents[FIRST_PSEUDO_REGISTER];
+
+/* During reload_as_needed, element N contains the insn for which
+ the Nth reload register was last used. This vector is in parallel
+ with spill_regs, and its contents are significant only when
+ reg_reloaded_contents is significant. */
+static rtx reg_reloaded_insn[FIRST_PSEUDO_REGISTER];
+
+/* Number of spill-regs so far; number of valid elements of spill_regs. */
+static int n_spills;
+
+/* In parallel with spill_regs, contains REG rtx's for those regs.
+ Holds the last rtx used for any given reg, or 0 if it has never
+ been used for spilling yet. This rtx is reused, provided it has
+ the proper mode. */
+static rtx spill_reg_rtx[FIRST_PSEUDO_REGISTER];
+
+/* In parallel with spill_regs, contains nonzero for a spill reg
+ that was stored after the last time it was used.
+ The precise value is the insn generated to do the store. */
+static rtx spill_reg_store[FIRST_PSEUDO_REGISTER];
+
+/* This table is the inverse mapping of spill_regs:
+ indexed by hard reg number,
+ it contains the position of that reg in spill_regs,
+ or -1 for something that is not in spill_regs. */
+static short spill_reg_order[FIRST_PSEUDO_REGISTER];
+
+/* This reg set indicates registers that may not be used for retrying global
+ allocation. The registers that may not be used include all spill registers
+ and the frame pointer (if we are using one). */
+HARD_REG_SET forbidden_regs;
+
+/* This reg set indicates registers that are not good for spill registers.
+ They will not be used to complete groups of spill registers. This includes
+ all fixed registers, registers that may be eliminated, and, if
+ SMALL_REGISTER_CLASSES is not defined, registers explicitly used in the rtl.
+
+ (spill_reg_order prevents these registers from being used to start a
+ group.) */
+static HARD_REG_SET bad_spill_regs;
+
+/* Describes order of use of registers for reloading
+ of spilled pseudo-registers. `spills' is the number of
+ elements that are actually valid; new ones are added at the end. */
+static short spill_regs[FIRST_PSEUDO_REGISTER];
+
+/* Describes order of preference for putting regs into spill_regs.
+ Contains the numbers of all the hard regs, in order most preferred first.
+ This order is different for each function.
+ It is set up by order_regs_for_reload.
+ Empty elements at the end contain -1. */
+static short potential_reload_regs[FIRST_PSEUDO_REGISTER];
+
+/* 1 for a hard register that appears explicitly in the rtl
+ (for example, function value registers, special registers
+ used by insns, structure value pointer registers). */
+static char regs_explicitly_used[FIRST_PSEUDO_REGISTER];
+
+/* Indicates if a register was counted against the need for
+ groups. 0 means it can count against max_nongroup instead. */
+static HARD_REG_SET counted_for_groups;
+
+/* Indicates if a register was counted against the need for
+ non-groups. 0 means it can become part of a new group.
+ During choose_reload_regs, 1 here means don't use this reg
+ as part of a group, even if it seems to be otherwise ok. */
+static HARD_REG_SET counted_for_nongroups;
+
+/* Indexed by pseudo reg number N,
+ says may not delete stores into the real (memory) home of pseudo N.
+ This is set if we already substituted a memory equivalent in some uses,
+ which happens when we have to eliminate the fp from it. */
+static char *cannot_omit_stores;
+
+/* Nonzero if indirect addressing is supported on the machine; this means
+ that spilling (REG n) does not require reloading it into a register in
+ order to do (MEM (REG n)) or (MEM (PLUS (REG n) (CONST_INT c))). The
+ value indicates the level of indirect addressing supported, e.g., two
+ means that (MEM (MEM (REG n))) is also valid if (REG n) does not get
+ a hard register. */
+
+static char spill_indirect_levels;
+
+/* Nonzero if indirect addressing is supported when the innermost MEM is
+ of the form (MEM (SYMBOL_REF sym)). It is assumed that the level to
+ which these are valid is the same as spill_indirect_levels, above. */
+
+char indirect_symref_ok;
+
+/* Nonzero if an address (plus (reg frame_pointer) (reg ...)) is valid. */
+
+char double_reg_address_ok;
+
+/* Record the stack slot for each spilled hard register. */
+
+static rtx spill_stack_slot[FIRST_PSEUDO_REGISTER];
+
+/* Width allocated so far for that stack slot. */
+
+static int spill_stack_slot_width[FIRST_PSEUDO_REGISTER];
+
+/* Indexed by register class and basic block number, nonzero if there is
+ any need for a spill register of that class in that basic block.
+ The pointer is 0 if we did stupid allocation and don't know
+ the structure of basic blocks. */
+
+char *basic_block_needs[N_REG_CLASSES];
+
+/* First uid used by insns created by reload in this function.
+ Used in find_equiv_reg. */
+int reload_first_uid;
+
+/* Flag set by local-alloc or global-alloc if anything is live in
+ a call-clobbered reg across calls. */
+
+int caller_save_needed;
+
+/* Set to 1 while reload_as_needed is operating.
+ Required by some machines to handle any generated moves differently. */
+
+int reload_in_progress = 0;
+
+/* These arrays record the insn_code of insns that may be needed to
+ perform input and output reloads of special objects. They provide a
+ place to pass a scratch register. */
+
+enum insn_code reload_in_optab[NUM_MACHINE_MODES];
+enum insn_code reload_out_optab[NUM_MACHINE_MODES];
+
+/* This obstack is used for allocation of rtl during register elimination.
+ The allocated storage can be freed once find_reloads has processed the
+ insn. */
+
+struct obstack reload_obstack;
+char *reload_firstobj;
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+/* List of labels that must never be deleted. */
+extern rtx forced_labels;
+
+/* This structure is used to record information about register eliminations.
+ Each array entry describes one possible way of eliminating a register
+ in favor of another. If there is more than one way of eliminating a
+ particular register, the most preferred should be specified first. */
+
+static struct elim_table
+{
+ int from; /* Register number to be eliminated. */
+ int to; /* Register number used as replacement. */
+ int initial_offset; /* Initial difference between values. */
+ int can_eliminate; /* Non-zero if this elimination can be done. */
+ int can_eliminate_previous; /* Value of CAN_ELIMINATE in previous scan over
+ insns made by reload. */
+ int offset; /* Current offset between the two regs. */
+ int max_offset; /* Maximum offset between the two regs. */
+ int previous_offset; /* Offset at end of previous insn. */
+ int ref_outside_mem; /* "to" has been referenced outside a MEM. */
+ rtx from_rtx; /* REG rtx for the register to be eliminated.
+ We cannot simply compare the number since
+ we might then spuriously replace a hard
+ register corresponding to a pseudo
+ assigned to the reg to be eliminated. */
+ rtx to_rtx; /* REG rtx for the replacement. */
+} reg_eliminate[] =
+
+/* If a set of eliminable registers was specified, define the table from it.
+ Otherwise, default to the normal case of the frame pointer being
+ replaced by the stack pointer. */
+
+#ifdef ELIMINABLE_REGS
+ ELIMINABLE_REGS;
+#else
+ {{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}};
+#endif
+
+#define NUM_ELIMINABLE_REGS (sizeof reg_eliminate / sizeof reg_eliminate[0])
+
+/* Record the number of pending eliminations that have an offset not equal
+ to their initial offset. If non-zero, we use a new copy of each
+ replacement result in any insns encountered. */
+static int num_not_at_initial_offset;
+
+/* Count the number of registers that we may be able to eliminate. */
+static int num_eliminable;
+
+/* For each label, we record the offset of each elimination. If we reach
+ a label by more than one path and an offset differs, we cannot do the
+ elimination. This information is indexed by the number of the label.
+ The first table is an array of flags that records whether we have yet
+ encountered a label and the second table is an array of arrays, one
+ entry in the latter array for each elimination. */
+
+static char *offsets_known_at;
+static int (*offsets_at)[NUM_ELIMINABLE_REGS];
+
+/* Number of labels in the current function. */
+
+static int num_labels;
+
+struct hard_reg_n_uses { int regno; int uses; };
+
+static int possible_group_p PROTO((int, int *));
+static void count_possible_groups PROTO((int *, enum machine_mode *,
+ int *));
+static int modes_equiv_for_class_p PROTO((enum machine_mode,
+ enum machine_mode,
+ enum reg_class));
+static void spill_failure PROTO((rtx));
+static int new_spill_reg PROTO((int, int, int *, int *, int,
+ FILE *));
+static void delete_dead_insn PROTO((rtx));
+static void alter_reg PROTO((int, int));
+static void mark_scratch_live PROTO((rtx));
+static void set_label_offsets PROTO((rtx, rtx, int));
+static int eliminate_regs_in_insn PROTO((rtx, int));
+static void mark_not_eliminable PROTO((rtx, rtx));
+static int spill_hard_reg PROTO((int, int, FILE *, int));
+static void scan_paradoxical_subregs PROTO((rtx));
+static int hard_reg_use_compare PROTO((struct hard_reg_n_uses *,
+ struct hard_reg_n_uses *));
+static void order_regs_for_reload PROTO((void));
+static int compare_spill_regs PROTO((short *, short *));
+static void reload_as_needed PROTO((rtx, int));
+static void forget_old_reloads_1 PROTO((rtx, rtx));
+static int reload_reg_class_lower PROTO((short *, short *));
+static void mark_reload_reg_in_use PROTO((int, int, enum reload_type,
+ enum machine_mode));
+static void clear_reload_reg_in_use PROTO((int, int, enum reload_type,
+ enum machine_mode));
+static int reload_reg_free_p PROTO((int, int, enum reload_type));
+static int reload_reg_free_before_p PROTO((int, int, enum reload_type));
+static int reload_reg_reaches_end_p PROTO((int, int, enum reload_type));
+static int reloads_conflict PROTO((int, int));
+static int allocate_reload_reg PROTO((int, rtx, int, int));
+static void choose_reload_regs PROTO((rtx, rtx));
+static void merge_assigned_reloads PROTO((rtx));
+static void emit_reload_insns PROTO((rtx));
+static void delete_output_reload PROTO((rtx, int, rtx));
+static void inc_for_reload PROTO((rtx, rtx, int));
+static int constraint_accepts_reg_p PROTO((char *, rtx));
+static int count_occurrences PROTO((rtx, rtx));
+
+/* Initialize the reload pass once per compilation. */
+
+void
+init_reload ()
+{
+ register int i;
+
+ /* Often (MEM (REG n)) is still valid even if (REG n) is put on the stack.
+ Set spill_indirect_levels to the number of levels such addressing is
+ permitted, zero if it is not permitted at all. */
+
+ register rtx tem
+ = gen_rtx (MEM, Pmode,
+ gen_rtx (PLUS, Pmode,
+ gen_rtx (REG, Pmode, LAST_VIRTUAL_REGISTER + 1),
+ GEN_INT (4)));
+ spill_indirect_levels = 0;
+
+ while (memory_address_p (QImode, tem))
+ {
+ spill_indirect_levels++;
+ tem = gen_rtx (MEM, Pmode, tem);
+ }
+
+ /* See if indirect addressing is valid for (MEM (SYMBOL_REF ...)). */
+
+ tem = gen_rtx (MEM, Pmode, gen_rtx (SYMBOL_REF, Pmode, "foo"));
+ indirect_symref_ok = memory_address_p (QImode, tem);
+
+ /* See if reg+reg is a valid (and offsettable) address. */
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ tem = gen_rtx (PLUS, Pmode,
+ gen_rtx (REG, Pmode, HARD_FRAME_POINTER_REGNUM),
+ gen_rtx (REG, Pmode, i));
+ /* This way, we make sure that reg+reg is an offsettable address. */
+ tem = plus_constant (tem, 4);
+
+ if (memory_address_p (QImode, tem))
+ {
+ double_reg_address_ok = 1;
+ break;
+ }
+ }
+
+ /* Initialize obstack for our rtl allocation. */
+ gcc_obstack_init (&reload_obstack);
+ reload_firstobj = (char *) obstack_alloc (&reload_obstack, 0);
+}
+
+/* Main entry point for the reload pass.
+
+ FIRST is the first insn of the function being compiled.
+
+ GLOBAL nonzero means we were called from global_alloc
+ and should attempt to reallocate any pseudoregs that we
+ displace from hard regs we will use for reloads.
+ If GLOBAL is zero, we do not have enough information to do that,
+ so any pseudo reg that is spilled must go to the stack.
+
+ DUMPFILE is the global-reg debugging dump file stream, or 0.
+ If it is nonzero, messages are written to it to describe
+ which registers are seized as reload regs, which pseudo regs
+ are spilled from them, and where the pseudo regs are reallocated to.
+
+ Return value is nonzero if reload failed
+ and we must not do any more for this function. */
+
+int
+reload (first, global, dumpfile)
+ rtx first;
+ int global;
+ FILE *dumpfile;
+{
+ register int class;
+ register int i, j, k;
+ register rtx insn;
+ register struct elim_table *ep;
+
+ int something_changed;
+ int something_needs_reloads;
+ int something_needs_elimination;
+ int new_basic_block_needs;
+ enum reg_class caller_save_spill_class = NO_REGS;
+ int caller_save_group_size = 1;
+
+ /* Nonzero means we couldn't get enough spill regs. */
+ int failure = 0;
+
+ /* The basic block number currently being processed for INSN. */
+ int this_block;
+
+ /* Make sure even insns with volatile mem refs are recognizable. */
+ init_recog ();
+
+ /* Enable find_equiv_reg to distinguish insns made by reload. */
+ reload_first_uid = get_max_uid ();
+
+ for (i = 0; i < N_REG_CLASSES; i++)
+ basic_block_needs[i] = 0;
+
+#ifdef SECONDARY_MEMORY_NEEDED
+ /* Initialize the secondary memory table. */
+ clear_secondary_mem ();
+#endif
+
+ /* Remember which hard regs appear explicitly
+ before we merge into `regs_ever_live' the ones in which
+ pseudo regs have been allocated. */
+ bcopy (regs_ever_live, regs_explicitly_used, sizeof regs_ever_live);
+
+ /* We don't have a stack slot for any spill reg yet. */
+ bzero ((char *) spill_stack_slot, sizeof spill_stack_slot);
+ bzero ((char *) spill_stack_slot_width, sizeof spill_stack_slot_width);
+
+ /* Initialize the save area information for caller-save, in case some
+ are needed. */
+ init_save_areas ();
+
+ /* Compute which hard registers are now in use
+ as homes for pseudo registers.
+ This is done here rather than (eg) in global_alloc
+ because this point is reached even if not optimizing. */
+
+ for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+ mark_home_live (i);
+
+ for (i = 0; i < scratch_list_length; i++)
+ if (scratch_list[i])
+ mark_scratch_live (scratch_list[i]);
+
+ /* Make sure that the last insn in the chain
+ is not something that needs reloading. */
+ emit_note (NULL_PTR, NOTE_INSN_DELETED);
+
+ /* Find all the pseudo registers that didn't get hard regs
+ but do have known equivalent constants or memory slots.
+ These include parameters (known equivalent to parameter slots)
+ and cse'd or loop-moved constant memory addresses.
+
+ Record constant equivalents in reg_equiv_constant
+ so they will be substituted by find_reloads.
+ Record memory equivalents in reg_mem_equiv so they can
+ be substituted eventually by altering the REG-rtx's. */
+
+ reg_equiv_constant = (rtx *) alloca (max_regno * sizeof (rtx));
+ bzero ((char *) reg_equiv_constant, max_regno * sizeof (rtx));
+ reg_equiv_memory_loc = (rtx *) alloca (max_regno * sizeof (rtx));
+ bzero ((char *) reg_equiv_memory_loc, max_regno * sizeof (rtx));
+ reg_equiv_mem = (rtx *) alloca (max_regno * sizeof (rtx));
+ bzero ((char *) reg_equiv_mem, max_regno * sizeof (rtx));
+ reg_equiv_init = (rtx *) alloca (max_regno * sizeof (rtx));
+ bzero ((char *) reg_equiv_init, max_regno * sizeof (rtx));
+ reg_equiv_address = (rtx *) alloca (max_regno * sizeof (rtx));
+ bzero ((char *) reg_equiv_address, max_regno * sizeof (rtx));
+ reg_max_ref_width = (int *) alloca (max_regno * sizeof (int));
+ bzero ((char *) reg_max_ref_width, max_regno * sizeof (int));
+ cannot_omit_stores = (char *) alloca (max_regno);
+ bzero (cannot_omit_stores, max_regno);
+
+#ifdef SMALL_REGISTER_CLASSES
+ CLEAR_HARD_REG_SET (forbidden_regs);
+#endif
+
+ /* Look for REG_EQUIV notes; record what each pseudo is equivalent to.
+ Also find all paradoxical subregs and find largest such for each pseudo.
+ On machines with small register classes, record hard registers that
+ are used for user variables. These can never be used for spills. */
+
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ rtx set = single_set (insn);
+
+ if (set != 0 && GET_CODE (SET_DEST (set)) == REG)
+ {
+ rtx note = find_reg_note (insn, REG_EQUIV, NULL_RTX);
+ if (note
+#ifdef LEGITIMATE_PIC_OPERAND_P
+ && (! CONSTANT_P (XEXP (note, 0)) || ! flag_pic
+ || LEGITIMATE_PIC_OPERAND_P (XEXP (note, 0)))
+#endif
+ )
+ {
+ rtx x = XEXP (note, 0);
+ i = REGNO (SET_DEST (set));
+ if (i > LAST_VIRTUAL_REGISTER)
+ {
+ if (GET_CODE (x) == MEM)
+ reg_equiv_memory_loc[i] = x;
+ else if (CONSTANT_P (x))
+ {
+ if (LEGITIMATE_CONSTANT_P (x))
+ reg_equiv_constant[i] = x;
+ else
+ reg_equiv_memory_loc[i]
+ = force_const_mem (GET_MODE (SET_DEST (set)), x);
+ }
+ else
+ continue;
+
+ /* If this register is being made equivalent to a MEM
+ and the MEM is not SET_SRC, the equivalencing insn
+ is one with the MEM as a SET_DEST and it occurs later.
+ So don't mark this insn now. */
+ if (GET_CODE (x) != MEM
+ || rtx_equal_p (SET_SRC (set), x))
+ reg_equiv_init[i] = insn;
+ }
+ }
+ }
+
+ /* If this insn is setting a MEM from a register equivalent to it,
+ this is the equivalencing insn. */
+ else if (set && GET_CODE (SET_DEST (set)) == MEM
+ && GET_CODE (SET_SRC (set)) == REG
+ && reg_equiv_memory_loc[REGNO (SET_SRC (set))]
+ && rtx_equal_p (SET_DEST (set),
+ reg_equiv_memory_loc[REGNO (SET_SRC (set))]))
+ reg_equiv_init[REGNO (SET_SRC (set))] = insn;
+
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ scan_paradoxical_subregs (PATTERN (insn));
+ }
+
+ /* Does this function require a frame pointer? */
+
+ frame_pointer_needed = (! flag_omit_frame_pointer
+#ifdef EXIT_IGNORE_STACK
+ /* ?? If EXIT_IGNORE_STACK is set, we will not save
+ and restore sp for alloca. So we can't eliminate
+ the frame pointer in that case. At some point,
+ we should improve this by emitting the
+ sp-adjusting insns for this case. */
+ || (current_function_calls_alloca
+ && EXIT_IGNORE_STACK)
+#endif
+ || FRAME_POINTER_REQUIRED);
+
+ num_eliminable = 0;
+
+ /* Initialize the table of registers to eliminate. The way we do this
+ depends on how the eliminable registers were defined. */
+#ifdef ELIMINABLE_REGS
+ for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+ {
+ ep->can_eliminate = ep->can_eliminate_previous
+ = (CAN_ELIMINATE (ep->from, ep->to)
+ && ! (ep->to == STACK_POINTER_REGNUM && frame_pointer_needed));
+ }
+#else
+ reg_eliminate[0].can_eliminate = reg_eliminate[0].can_eliminate_previous
+ = ! frame_pointer_needed;
+#endif
+
+ /* Count the number of eliminable registers and build the FROM and TO
+ REG rtx's. Note that code in gen_rtx will cause, e.g.,
+ gen_rtx (REG, Pmode, STACK_POINTER_REGNUM) to equal stack_pointer_rtx.
+ We depend on this. */
+ for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+ {
+ num_eliminable += ep->can_eliminate;
+ ep->from_rtx = gen_rtx (REG, Pmode, ep->from);
+ ep->to_rtx = gen_rtx (REG, Pmode, ep->to);
+ }
+
+ num_labels = max_label_num () - get_first_label_num ();
+
+ /* Allocate the tables used to store offset information at labels. */
+ offsets_known_at = (char *) alloca (num_labels);
+ offsets_at
+ = (int (*)[NUM_ELIMINABLE_REGS])
+ alloca (num_labels * NUM_ELIMINABLE_REGS * sizeof (int));
+
+ offsets_known_at -= get_first_label_num ();
+ offsets_at -= get_first_label_num ();
+
+ /* Alter each pseudo-reg rtx to contain its hard reg number.
+ Assign stack slots to the pseudos that lack hard regs or equivalents.
+ Do not touch virtual registers. */
+
+ for (i = LAST_VIRTUAL_REGISTER + 1; i < max_regno; i++)
+ alter_reg (i, -1);
+
+ /* Round size of stack frame to BIGGEST_ALIGNMENT. This must be done here
+ because the stack size may be a part of the offset computation for
+ register elimination. */
+ assign_stack_local (BLKmode, 0, 0);
+
+ /* If we have some registers we think can be eliminated, scan all insns to
+ see if there is an insn that sets one of these registers to something
+ other than itself plus a constant. If so, the register cannot be
+ eliminated. Doing this scan here eliminates an extra pass through the
+ main reload loop in the most common case where register elimination
+ cannot be done. */
+ for (insn = first; insn && num_eliminable; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN
+ || GET_CODE (insn) == CALL_INSN)
+ note_stores (PATTERN (insn), mark_not_eliminable);
+
+#ifndef REGISTER_CONSTRAINTS
+ /* If all the pseudo regs have hard regs,
+ except for those that are never referenced,
+ we know that no reloads are needed. */
+ /* But that is not true if there are register constraints, since
+ in that case some pseudos might be in the wrong kind of hard reg. */
+
+ for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+ if (reg_renumber[i] == -1 && reg_n_refs[i] != 0)
+ break;
+
+ if (i == max_regno && num_eliminable == 0 && ! caller_save_needed)
+ return;
+#endif
+
+ /* Compute the order of preference for hard registers to spill.
+ Store them by decreasing preference in potential_reload_regs. */
+
+ order_regs_for_reload ();
+
+ /* So far, no hard regs have been spilled. */
+ n_spills = 0;
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ spill_reg_order[i] = -1;
+
+ /* On most machines, we can't use any register explicitly used in the
+ rtl as a spill register. But on some, we have to. Those will have
+ taken care to keep the life of hard regs as short as possible. */
+
+#ifndef SMALL_REGISTER_CLASSES
+ COPY_HARD_REG_SET (forbidden_regs, bad_spill_regs);
+#endif
+
+ /* Spill any hard regs that we know we can't eliminate. */
+ for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+ if (! ep->can_eliminate)
+ spill_hard_reg (ep->from, global, dumpfile, 1);
+
+#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ if (frame_pointer_needed)
+ spill_hard_reg (HARD_FRAME_POINTER_REGNUM, global, dumpfile, 1);
+#endif
+
+ if (global)
+ for (i = 0; i < N_REG_CLASSES; i++)
+ {
+ basic_block_needs[i] = (char *) alloca (n_basic_blocks);
+ bzero (basic_block_needs[i], n_basic_blocks);
+ }
+
+ /* From now on, we need to emit any moves without making new pseudos. */
+ reload_in_progress = 1;
+
+ /* This loop scans the entire function each go-round
+ and repeats until one repetition spills no additional hard regs. */
+
+ /* This flag is set when a pseudo reg is spilled,
+ to require another pass. Note that getting an additional reload
+ reg does not necessarily imply any pseudo reg was spilled;
+ sometimes we find a reload reg that no pseudo reg was allocated in. */
+ something_changed = 1;
+ /* This flag is set if there are any insns that require reloading. */
+ something_needs_reloads = 0;
+ /* This flag is set if there are any insns that require register
+ eliminations. */
+ something_needs_elimination = 0;
+ while (something_changed)
+ {
+ rtx after_call = 0;
+
+ /* For each class, number of reload regs needed in that class.
+ This is the maximum over all insns of the needs in that class
+ of the individual insn. */
+ int max_needs[N_REG_CLASSES];
+ /* For each class, size of group of consecutive regs
+ that is needed for the reloads of this class. */
+ int group_size[N_REG_CLASSES];
+ /* For each class, max number of consecutive groups needed.
+ (Each group contains group_size[CLASS] consecutive registers.) */
+ int max_groups[N_REG_CLASSES];
+ /* For each class, max number needed of regs that don't belong
+ to any of the groups. */
+ int max_nongroups[N_REG_CLASSES];
+ /* For each class, the machine mode which requires consecutive
+ groups of regs of that class.
+ If two different modes ever require groups of one class,
+ they must be the same size and equally restrictive for that class,
+ otherwise we can't handle the complexity. */
+ enum machine_mode group_mode[N_REG_CLASSES];
+ /* Record the insn where each maximum need is first found. */
+ rtx max_needs_insn[N_REG_CLASSES];
+ rtx max_groups_insn[N_REG_CLASSES];
+ rtx max_nongroups_insn[N_REG_CLASSES];
+ rtx x;
+ int starting_frame_size = get_frame_size ();
+ int previous_frame_pointer_needed = frame_pointer_needed;
+ static char *reg_class_names[] = REG_CLASS_NAMES;
+
+ something_changed = 0;
+ bzero ((char *) max_needs, sizeof max_needs);
+ bzero ((char *) max_groups, sizeof max_groups);
+ bzero ((char *) max_nongroups, sizeof max_nongroups);
+ bzero ((char *) max_needs_insn, sizeof max_needs_insn);
+ bzero ((char *) max_groups_insn, sizeof max_groups_insn);
+ bzero ((char *) max_nongroups_insn, sizeof max_nongroups_insn);
+ bzero ((char *) group_size, sizeof group_size);
+ for (i = 0; i < N_REG_CLASSES; i++)
+ group_mode[i] = VOIDmode;
+
+ /* Keep track of which basic blocks are needing the reloads. */
+ this_block = 0;
+
+ /* Remember whether any element of basic_block_needs
+ changes from 0 to 1 in this pass. */
+ new_basic_block_needs = 0;
+
+ /* Reset all offsets on eliminable registers to their initial values. */
+#ifdef ELIMINABLE_REGS
+ for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+ {
+ INITIAL_ELIMINATION_OFFSET (ep->from, ep->to, ep->initial_offset);
+ ep->previous_offset = ep->offset
+ = ep->max_offset = ep->initial_offset;
+ }
+#else
+#ifdef INITIAL_FRAME_POINTER_OFFSET
+ INITIAL_FRAME_POINTER_OFFSET (reg_eliminate[0].initial_offset);
+#else
+ if (!FRAME_POINTER_REQUIRED)
+ abort ();
+ reg_eliminate[0].initial_offset = 0;
+#endif
+ reg_eliminate[0].previous_offset = reg_eliminate[0].max_offset
+ = reg_eliminate[0].offset = reg_eliminate[0].initial_offset;
+#endif
+
+ num_not_at_initial_offset = 0;
+
+ bzero ((char *) &offsets_known_at[get_first_label_num ()], num_labels);
+
+ /* Set a known offset for each forced label to be at the initial offset
+ of each elimination. We do this because we assume that all
+ computed jumps occur from a location where each elimination is
+ at its initial offset. */
+
+ for (x = forced_labels; x; x = XEXP (x, 1))
+ if (XEXP (x, 0))
+ set_label_offsets (XEXP (x, 0), NULL_RTX, 1);
+
+ /* For each pseudo register that has an equivalent location defined,
+ try to eliminate any eliminable registers (such as the frame pointer)
+ assuming initial offsets for the replacement register, which
+ is the normal case.
+
+ If the resulting location is directly addressable, substitute
+ the MEM we just got directly for the old REG.
+
+ If it is not addressable but is a constant or the sum of a hard reg
+ and constant, it is probably not addressable because the constant is
+ out of range, in that case record the address; we will generate
+ hairy code to compute the address in a register each time it is
+ needed. Similarly if it is a hard register, but one that is not
+ valid as an address register.
+
+ If the location is not addressable, but does not have one of the
+ above forms, assign a stack slot. We have to do this to avoid the
+ potential of producing lots of reloads if, e.g., a location involves
+ a pseudo that didn't get a hard register and has an equivalent memory
+ location that also involves a pseudo that didn't get a hard register.
+
+ Perhaps at some point we will improve reload_when_needed handling
+ so this problem goes away. But that's very hairy. */
+
+ for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+ if (reg_renumber[i] < 0 && reg_equiv_memory_loc[i])
+ {
+ rtx x = eliminate_regs (reg_equiv_memory_loc[i], 0, NULL_RTX);
+
+ if (strict_memory_address_p (GET_MODE (regno_reg_rtx[i]),
+ XEXP (x, 0)))
+ reg_equiv_mem[i] = x, reg_equiv_address[i] = 0;
+ else if (CONSTANT_P (XEXP (x, 0))
+ || (GET_CODE (XEXP (x, 0)) == REG
+ && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER)
+ || (GET_CODE (XEXP (x, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
+ && (REGNO (XEXP (XEXP (x, 0), 0))
+ < FIRST_PSEUDO_REGISTER)
+ && CONSTANT_P (XEXP (XEXP (x, 0), 1))))
+ reg_equiv_address[i] = XEXP (x, 0), reg_equiv_mem[i] = 0;
+ else
+ {
+ /* Make a new stack slot. Then indicate that something
+ changed so we go back and recompute offsets for
+ eliminable registers because the allocation of memory
+ below might change some offset. reg_equiv_{mem,address}
+ will be set up for this pseudo on the next pass around
+ the loop. */
+ reg_equiv_memory_loc[i] = 0;
+ reg_equiv_init[i] = 0;
+ alter_reg (i, -1);
+ something_changed = 1;
+ }
+ }
+
+ /* If we allocated another pseudo to the stack, redo elimination
+ bookkeeping. */
+ if (something_changed)
+ continue;
+
+ /* If caller-saves needs a group, initialize the group to include
+ the size and mode required for caller-saves. */
+
+ if (caller_save_group_size > 1)
+ {
+ group_mode[(int) caller_save_spill_class] = Pmode;
+ group_size[(int) caller_save_spill_class] = caller_save_group_size;
+ }
+
+ /* Compute the most additional registers needed by any instruction.
+ Collect information separately for each class of regs. */
+
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ if (global && this_block + 1 < n_basic_blocks
+ && insn == basic_block_head[this_block+1])
+ ++this_block;
+
+ /* If this is a label, a JUMP_INSN, or has REG_NOTES (which
+ might include REG_LABEL), we need to see what effects this
+ has on the known offsets at labels. */
+
+ if (GET_CODE (insn) == CODE_LABEL || GET_CODE (insn) == JUMP_INSN
+ || (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && REG_NOTES (insn) != 0))
+ set_label_offsets (insn, insn, 0);
+
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ /* Nonzero means don't use a reload reg that overlaps
+ the place where a function value can be returned. */
+ rtx avoid_return_reg = 0;
+
+ rtx old_body = PATTERN (insn);
+ int old_code = INSN_CODE (insn);
+ rtx old_notes = REG_NOTES (insn);
+ int did_elimination = 0;
+
+ /* To compute the number of reload registers of each class
+ needed for an insn, we must similate what choose_reload_regs
+ can do. We do this by splitting an insn into an "input" and
+ an "output" part. RELOAD_OTHER reloads are used in both.
+ The input part uses those reloads, RELOAD_FOR_INPUT reloads,
+ which must be live over the entire input section of reloads,
+ and the maximum of all the RELOAD_FOR_INPUT_ADDRESS and
+ RELOAD_FOR_OPERAND_ADDRESS reloads, which conflict with the
+ inputs.
+
+ The registers needed for output are RELOAD_OTHER and
+ RELOAD_FOR_OUTPUT, which are live for the entire output
+ portion, and the maximum of all the RELOAD_FOR_OUTPUT_ADDRESS
+ reloads for each operand.
+
+ The total number of registers needed is the maximum of the
+ inputs and outputs. */
+
+ struct needs
+ {
+ /* [0] is normal, [1] is nongroup. */
+ int regs[2][N_REG_CLASSES];
+ int groups[N_REG_CLASSES];
+ };
+
+ /* Each `struct needs' corresponds to one RELOAD_... type. */
+ struct {
+ struct needs other;
+ struct needs input;
+ struct needs output;
+ struct needs insn;
+ struct needs other_addr;
+ struct needs op_addr;
+ struct needs op_addr_reload;
+ struct needs in_addr[MAX_RECOG_OPERANDS];
+ struct needs out_addr[MAX_RECOG_OPERANDS];
+ } insn_needs;
+
+ /* If needed, eliminate any eliminable registers. */
+ if (num_eliminable)
+ did_elimination = eliminate_regs_in_insn (insn, 0);
+
+#ifdef SMALL_REGISTER_CLASSES
+ /* Set avoid_return_reg if this is an insn
+ that might use the value of a function call. */
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ if (GET_CODE (PATTERN (insn)) == SET)
+ after_call = SET_DEST (PATTERN (insn));
+ else if (GET_CODE (PATTERN (insn)) == PARALLEL
+ && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET)
+ after_call = SET_DEST (XVECEXP (PATTERN (insn), 0, 0));
+ else
+ after_call = 0;
+ }
+ else if (after_call != 0
+ && !(GET_CODE (PATTERN (insn)) == SET
+ && SET_DEST (PATTERN (insn)) == stack_pointer_rtx))
+ {
+ if (reg_referenced_p (after_call, PATTERN (insn)))
+ avoid_return_reg = after_call;
+ after_call = 0;
+ }
+#endif /* SMALL_REGISTER_CLASSES */
+
+ /* Analyze the instruction. */
+ find_reloads (insn, 0, spill_indirect_levels, global,
+ spill_reg_order);
+
+ /* Remember for later shortcuts which insns had any reloads or
+ register eliminations.
+
+ One might think that it would be worthwhile to mark insns
+ that need register replacements but not reloads, but this is
+ not safe because find_reloads may do some manipulation of
+ the insn (such as swapping commutative operands), which would
+ be lost when we restore the old pattern after register
+ replacement. So the actions of find_reloads must be redone in
+ subsequent passes or in reload_as_needed.
+
+ However, it is safe to mark insns that need reloads
+ but not register replacement. */
+
+ PUT_MODE (insn, (did_elimination ? QImode
+ : n_reloads ? HImode
+ : GET_MODE (insn) == DImode ? DImode
+ : VOIDmode));
+
+ /* Discard any register replacements done. */
+ if (did_elimination)
+ {
+ obstack_free (&reload_obstack, reload_firstobj);
+ PATTERN (insn) = old_body;
+ INSN_CODE (insn) = old_code;
+ REG_NOTES (insn) = old_notes;
+ something_needs_elimination = 1;
+ }
+
+ /* If this insn has no reloads, we need not do anything except
+ in the case of a CALL_INSN when we have caller-saves and
+ caller-save needs reloads. */
+
+ if (n_reloads == 0
+ && ! (GET_CODE (insn) == CALL_INSN
+ && caller_save_spill_class != NO_REGS))
+ continue;
+
+ something_needs_reloads = 1;
+ bzero ((char *) &insn_needs, sizeof insn_needs);
+
+ /* Count each reload once in every class
+ containing the reload's own class. */
+
+ for (i = 0; i < n_reloads; i++)
+ {
+ register enum reg_class *p;
+ enum reg_class class = reload_reg_class[i];
+ int size;
+ enum machine_mode mode;
+ int nongroup_need;
+ struct needs *this_needs;
+
+ /* Don't count the dummy reloads, for which one of the
+ regs mentioned in the insn can be used for reloading.
+ Don't count optional reloads.
+ Don't count reloads that got combined with others. */
+ if (reload_reg_rtx[i] != 0
+ || reload_optional[i] != 0
+ || (reload_out[i] == 0 && reload_in[i] == 0
+ && ! reload_secondary_p[i]))
+ continue;
+
+ /* Show that a reload register of this class is needed
+ in this basic block. We do not use insn_needs and
+ insn_groups because they are overly conservative for
+ this purpose. */
+ if (global && ! basic_block_needs[(int) class][this_block])
+ {
+ basic_block_needs[(int) class][this_block] = 1;
+ new_basic_block_needs = 1;
+ }
+
+
+ mode = reload_inmode[i];
+ if (GET_MODE_SIZE (reload_outmode[i]) > GET_MODE_SIZE (mode))
+ mode = reload_outmode[i];
+ size = CLASS_MAX_NREGS (class, mode);
+
+ /* If this class doesn't want a group, determine if we have
+ a nongroup need or a regular need. We have a nongroup
+ need if this reload conflicts with a group reload whose
+ class intersects with this reload's class. */
+
+ nongroup_need = 0;
+ if (size == 1)
+ for (j = 0; j < n_reloads; j++)
+ if ((CLASS_MAX_NREGS (reload_reg_class[j],
+ (GET_MODE_SIZE (reload_outmode[j])
+ > GET_MODE_SIZE (reload_inmode[j]))
+ ? reload_outmode[j]
+ : reload_inmode[j])
+ > 1)
+ && (!reload_optional[j])
+ && (reload_in[j] != 0 || reload_out[j] != 0
+ || reload_secondary_p[j])
+ && reloads_conflict (i, j)
+ && reg_classes_intersect_p (class,
+ reload_reg_class[j]))
+ {
+ nongroup_need = 1;
+ break;
+ }
+
+ /* Decide which time-of-use to count this reload for. */
+ switch (reload_when_needed[i])
+ {
+ case RELOAD_OTHER:
+ this_needs = &insn_needs.other;
+ break;
+ case RELOAD_FOR_INPUT:
+ this_needs = &insn_needs.input;
+ break;
+ case RELOAD_FOR_OUTPUT:
+ this_needs = &insn_needs.output;
+ break;
+ case RELOAD_FOR_INSN:
+ this_needs = &insn_needs.insn;
+ break;
+ case RELOAD_FOR_OTHER_ADDRESS:
+ this_needs = &insn_needs.other_addr;
+ break;
+ case RELOAD_FOR_INPUT_ADDRESS:
+ this_needs = &insn_needs.in_addr[reload_opnum[i]];
+ break;
+ case RELOAD_FOR_OUTPUT_ADDRESS:
+ this_needs = &insn_needs.out_addr[reload_opnum[i]];
+ break;
+ case RELOAD_FOR_OPERAND_ADDRESS:
+ this_needs = &insn_needs.op_addr;
+ break;
+ case RELOAD_FOR_OPADDR_ADDR:
+ this_needs = &insn_needs.op_addr_reload;
+ break;
+ }
+
+ if (size > 1)
+ {
+ enum machine_mode other_mode, allocate_mode;
+
+ /* Count number of groups needed separately from
+ number of individual regs needed. */
+ this_needs->groups[(int) class]++;
+ p = reg_class_superclasses[(int) class];
+ while (*p != LIM_REG_CLASSES)
+ this_needs->groups[(int) *p++]++;
+
+ /* Record size and mode of a group of this class. */
+ /* If more than one size group is needed,
+ make all groups the largest needed size. */
+ if (group_size[(int) class] < size)
+ {
+ other_mode = group_mode[(int) class];
+ allocate_mode = mode;
+
+ group_size[(int) class] = size;
+ group_mode[(int) class] = mode;
+ }
+ else
+ {
+ other_mode = mode;
+ allocate_mode = group_mode[(int) class];
+ }
+
+ /* Crash if two dissimilar machine modes both need
+ groups of consecutive regs of the same class. */
+
+ if (other_mode != VOIDmode && other_mode != allocate_mode
+ && ! modes_equiv_for_class_p (allocate_mode,
+ other_mode, class))
+ abort ();
+ }
+ else if (size == 1)
+ {
+ this_needs->regs[nongroup_need][(int) class] += 1;
+ p = reg_class_superclasses[(int) class];
+ while (*p != LIM_REG_CLASSES)
+ this_needs->regs[nongroup_need][(int) *p++] += 1;
+ }
+ else
+ abort ();
+ }
+
+ /* All reloads have been counted for this insn;
+ now merge the various times of use.
+ This sets insn_needs, etc., to the maximum total number
+ of registers needed at any point in this insn. */
+
+ for (i = 0; i < N_REG_CLASSES; i++)
+ {
+ int in_max, out_max;
+
+ /* Compute normal and nongroup needs. */
+ for (j = 0; j <= 1; j++)
+ {
+ for (in_max = 0, out_max = 0, k = 0;
+ k < reload_n_operands; k++)
+ {
+ in_max
+ = MAX (in_max, insn_needs.in_addr[k].regs[j][i]);
+ out_max
+ = MAX (out_max, insn_needs.out_addr[k].regs[j][i]);
+ }
+
+ /* RELOAD_FOR_INSN reloads conflict with inputs, outputs,
+ and operand addresses but not things used to reload
+ them. Similarly, RELOAD_FOR_OPERAND_ADDRESS reloads
+ don't conflict with things needed to reload inputs or
+ outputs. */
+
+ in_max = MAX (MAX (insn_needs.op_addr.regs[j][i],
+ insn_needs.op_addr_reload.regs[j][i]),
+ in_max);
+
+ out_max = MAX (out_max, insn_needs.insn.regs[j][i]);
+
+ insn_needs.input.regs[j][i]
+ = MAX (insn_needs.input.regs[j][i]
+ + insn_needs.op_addr.regs[j][i]
+ + insn_needs.insn.regs[j][i],
+ in_max + insn_needs.input.regs[j][i]);
+
+ insn_needs.output.regs[j][i] += out_max;
+ insn_needs.other.regs[j][i]
+ += MAX (MAX (insn_needs.input.regs[j][i],
+ insn_needs.output.regs[j][i]),
+ insn_needs.other_addr.regs[j][i]);
+
+ }
+
+ /* Now compute group needs. */
+ for (in_max = 0, out_max = 0, j = 0;
+ j < reload_n_operands; j++)
+ {
+ in_max = MAX (in_max, insn_needs.in_addr[j].groups[i]);
+ out_max
+ = MAX (out_max, insn_needs.out_addr[j].groups[i]);
+ }
+
+ in_max = MAX (MAX (insn_needs.op_addr.groups[i],
+ insn_needs.op_addr_reload.groups[i]),
+ in_max);
+ out_max = MAX (out_max, insn_needs.insn.groups[i]);
+
+ insn_needs.input.groups[i]
+ = MAX (insn_needs.input.groups[i]
+ + insn_needs.op_addr.groups[i]
+ + insn_needs.insn.groups[i],
+ in_max + insn_needs.input.groups[i]);
+
+ insn_needs.output.groups[i] += out_max;
+ insn_needs.other.groups[i]
+ += MAX (MAX (insn_needs.input.groups[i],
+ insn_needs.output.groups[i]),
+ insn_needs.other_addr.groups[i]);
+ }
+
+ /* If this is a CALL_INSN and caller-saves will need
+ a spill register, act as if the spill register is
+ needed for this insn. However, the spill register
+ can be used by any reload of this insn, so we only
+ need do something if no need for that class has
+ been recorded.
+
+ The assumption that every CALL_INSN will trigger a
+ caller-save is highly conservative, however, the number
+ of cases where caller-saves will need a spill register but
+ a block containing a CALL_INSN won't need a spill register
+ of that class should be quite rare.
+
+ If a group is needed, the size and mode of the group will
+ have been set up at the beginning of this loop. */
+
+ if (GET_CODE (insn) == CALL_INSN
+ && caller_save_spill_class != NO_REGS)
+ {
+ /* See if this register would conflict with any reload
+ that needs a group. */
+ int nongroup_need = 0;
+ int *caller_save_needs;
+
+ for (j = 0; j < n_reloads; j++)
+ if ((CLASS_MAX_NREGS (reload_reg_class[j],
+ (GET_MODE_SIZE (reload_outmode[j])
+ > GET_MODE_SIZE (reload_inmode[j]))
+ ? reload_outmode[j]
+ : reload_inmode[j])
+ > 1)
+ && reg_classes_intersect_p (caller_save_spill_class,
+ reload_reg_class[j]))
+ {
+ nongroup_need = 1;
+ break;
+ }
+
+ caller_save_needs
+ = (caller_save_group_size > 1
+ ? insn_needs.other.groups
+ : insn_needs.other.regs[nongroup_need]);
+
+ if (caller_save_needs[(int) caller_save_spill_class] == 0)
+ {
+ register enum reg_class *p
+ = reg_class_superclasses[(int) caller_save_spill_class];
+
+ caller_save_needs[(int) caller_save_spill_class]++;
+
+ while (*p != LIM_REG_CLASSES)
+ caller_save_needs[(int) *p++] += 1;
+ }
+
+ /* Show that this basic block will need a register of
+ this class. */
+
+ if (global
+ && ! (basic_block_needs[(int) caller_save_spill_class]
+ [this_block]))
+ {
+ basic_block_needs[(int) caller_save_spill_class]
+ [this_block] = 1;
+ new_basic_block_needs = 1;
+ }
+ }
+
+#ifdef SMALL_REGISTER_CLASSES
+ /* If this insn stores the value of a function call,
+ and that value is in a register that has been spilled,
+ and if the insn needs a reload in a class
+ that might use that register as the reload register,
+ then add add an extra need in that class.
+ This makes sure we have a register available that does
+ not overlap the return value. */
+
+ if (avoid_return_reg)
+ {
+ int regno = REGNO (avoid_return_reg);
+ int nregs
+ = HARD_REGNO_NREGS (regno, GET_MODE (avoid_return_reg));
+ int r;
+ int basic_needs[N_REG_CLASSES], basic_groups[N_REG_CLASSES];
+
+ /* First compute the "basic needs", which counts a
+ need only in the smallest class in which it
+ is required. */
+
+ bcopy (insn_needs.other.regs[0], basic_needs,
+ sizeof basic_needs);
+ bcopy (insn_needs.other.groups, basic_groups,
+ sizeof basic_groups);
+
+ for (i = 0; i < N_REG_CLASSES; i++)
+ {
+ enum reg_class *p;
+
+ if (basic_needs[i] >= 0)
+ for (p = reg_class_superclasses[i];
+ *p != LIM_REG_CLASSES; p++)
+ basic_needs[(int) *p] -= basic_needs[i];
+
+ if (basic_groups[i] >= 0)
+ for (p = reg_class_superclasses[i];
+ *p != LIM_REG_CLASSES; p++)
+ basic_groups[(int) *p] -= basic_groups[i];
+ }
+
+ /* Now count extra regs if there might be a conflict with
+ the return value register.
+
+ ??? This is not quite correct because we don't properly
+ handle the case of groups, but if we end up doing
+ something wrong, it either will end up not mattering or
+ we will abort elsewhere. */
+
+ for (r = regno; r < regno + nregs; r++)
+ if (spill_reg_order[r] >= 0)
+ for (i = 0; i < N_REG_CLASSES; i++)
+ if (TEST_HARD_REG_BIT (reg_class_contents[i], r))
+ {
+ if (basic_needs[i] > 0 || basic_groups[i] > 0)
+ {
+ enum reg_class *p;
+
+ insn_needs.other.regs[0][i]++;
+ p = reg_class_superclasses[i];
+ while (*p != LIM_REG_CLASSES)
+ insn_needs.other.regs[0][(int) *p++]++;
+ }
+ }
+ }
+#endif /* SMALL_REGISTER_CLASSES */
+
+ /* For each class, collect maximum need of any insn. */
+
+ for (i = 0; i < N_REG_CLASSES; i++)
+ {
+ if (max_needs[i] < insn_needs.other.regs[0][i])
+ {
+ max_needs[i] = insn_needs.other.regs[0][i];
+ max_needs_insn[i] = insn;
+ }
+ if (max_groups[i] < insn_needs.other.groups[i])
+ {
+ max_groups[i] = insn_needs.other.groups[i];
+ max_groups_insn[i] = insn;
+ }
+ if (max_nongroups[i] < insn_needs.other.regs[1][i])
+ {
+ max_nongroups[i] = insn_needs.other.regs[1][i];
+ max_nongroups_insn[i] = insn;
+ }
+ }
+ }
+ /* Note that there is a continue statement above. */
+ }
+
+ /* If we allocated any new memory locations, make another pass
+ since it might have changed elimination offsets. */
+ if (starting_frame_size != get_frame_size ())
+ something_changed = 1;
+
+ if (dumpfile)
+ for (i = 0; i < N_REG_CLASSES; i++)
+ {
+ if (max_needs[i] > 0)
+ fprintf (dumpfile,
+ ";; Need %d reg%s of class %s (for insn %d).\n",
+ max_needs[i], max_needs[i] == 1 ? "" : "s",
+ reg_class_names[i], INSN_UID (max_needs_insn[i]));
+ if (max_nongroups[i] > 0)
+ fprintf (dumpfile,
+ ";; Need %d nongroup reg%s of class %s (for insn %d).\n",
+ max_nongroups[i], max_nongroups[i] == 1 ? "" : "s",
+ reg_class_names[i], INSN_UID (max_nongroups_insn[i]));
+ if (max_groups[i] > 0)
+ fprintf (dumpfile,
+ ";; Need %d group%s (%smode) of class %s (for insn %d).\n",
+ max_groups[i], max_groups[i] == 1 ? "" : "s",
+ mode_name[(int) group_mode[i]],
+ reg_class_names[i], INSN_UID (max_groups_insn[i]));
+ }
+
+ /* If we have caller-saves, set up the save areas and see if caller-save
+ will need a spill register. */
+
+ if (caller_save_needed
+ && ! setup_save_areas (&something_changed)
+ && caller_save_spill_class == NO_REGS)
+ {
+ /* The class we will need depends on whether the machine
+ supports the sum of two registers for an address; see
+ find_address_reloads for details. */
+
+ caller_save_spill_class
+ = double_reg_address_ok ? INDEX_REG_CLASS : BASE_REG_CLASS;
+ caller_save_group_size
+ = CLASS_MAX_NREGS (caller_save_spill_class, Pmode);
+ something_changed = 1;
+ }
+
+ /* See if anything that happened changes which eliminations are valid.
+ For example, on the Sparc, whether or not the frame pointer can
+ be eliminated can depend on what registers have been used. We need
+ not check some conditions again (such as flag_omit_frame_pointer)
+ since they can't have changed. */
+
+ for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+ if ((ep->from == HARD_FRAME_POINTER_REGNUM && FRAME_POINTER_REQUIRED)
+#ifdef ELIMINABLE_REGS
+ || ! CAN_ELIMINATE (ep->from, ep->to)
+#endif
+ )
+ ep->can_eliminate = 0;
+
+ /* Look for the case where we have discovered that we can't replace
+ register A with register B and that means that we will now be
+ trying to replace register A with register C. This means we can
+ no longer replace register C with register B and we need to disable
+ such an elimination, if it exists. This occurs often with A == ap,
+ B == sp, and C == fp. */
+
+ for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+ {
+ struct elim_table *op;
+ register int new_to = -1;
+
+ if (! ep->can_eliminate && ep->can_eliminate_previous)
+ {
+ /* Find the current elimination for ep->from, if there is a
+ new one. */
+ for (op = reg_eliminate;
+ op < &reg_eliminate[NUM_ELIMINABLE_REGS]; op++)
+ if (op->from == ep->from && op->can_eliminate)
+ {
+ new_to = op->to;
+ break;
+ }
+
+ /* See if there is an elimination of NEW_TO -> EP->TO. If so,
+ disable it. */
+ for (op = reg_eliminate;
+ op < &reg_eliminate[NUM_ELIMINABLE_REGS]; op++)
+ if (op->from == new_to && op->to == ep->to)
+ op->can_eliminate = 0;
+ }
+ }
+
+ /* See if any registers that we thought we could eliminate the previous
+ time are no longer eliminable. If so, something has changed and we
+ must spill the register. Also, recompute the number of eliminable
+ registers and see if the frame pointer is needed; it is if there is
+ no elimination of the frame pointer that we can perform. */
+
+ frame_pointer_needed = 1;
+ for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+ {
+ if (ep->can_eliminate && ep->from == FRAME_POINTER_REGNUM
+ && ep->to != HARD_FRAME_POINTER_REGNUM)
+ frame_pointer_needed = 0;
+
+ if (! ep->can_eliminate && ep->can_eliminate_previous)
+ {
+ ep->can_eliminate_previous = 0;
+ spill_hard_reg (ep->from, global, dumpfile, 1);
+ something_changed = 1;
+ num_eliminable--;
+ }
+ }
+
+#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ /* If we didn't need a frame pointer last time, but we do now, spill
+ the hard frame pointer. */
+ if (frame_pointer_needed && ! previous_frame_pointer_needed)
+ {
+ spill_hard_reg (HARD_FRAME_POINTER_REGNUM, global, dumpfile, 1);
+ something_changed = 1;
+ }
+#endif
+
+ /* If all needs are met, we win. */
+
+ for (i = 0; i < N_REG_CLASSES; i++)
+ if (max_needs[i] > 0 || max_groups[i] > 0 || max_nongroups[i] > 0)
+ break;
+ if (i == N_REG_CLASSES && !new_basic_block_needs && ! something_changed)
+ break;
+
+ /* Not all needs are met; must spill some hard regs. */
+
+ /* Put all registers spilled so far back in potential_reload_regs, but
+ put them at the front, since we've already spilled most of the
+ psuedos in them (we might have left some pseudos unspilled if they
+ were in a block that didn't need any spill registers of a conflicting
+ class. We used to try to mark off the need for those registers,
+ but doing so properly is very complex and reallocating them is the
+ simpler approach. First, "pack" potential_reload_regs by pushing
+ any nonnegative entries towards the end. That will leave room
+ for the registers we already spilled.
+
+ Also, undo the marking of the spill registers from the last time
+ around in FORBIDDEN_REGS since we will be probably be allocating
+ them again below.
+
+ ??? It is theoretically possible that we might end up not using one
+ of our previously-spilled registers in this allocation, even though
+ they are at the head of the list. It's not clear what to do about
+ this, but it was no better before, when we marked off the needs met
+ by the previously-spilled registers. With the current code, globals
+ can be allocated into these registers, but locals cannot. */
+
+ if (n_spills)
+ {
+ for (i = j = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
+ if (potential_reload_regs[i] != -1)
+ potential_reload_regs[j--] = potential_reload_regs[i];
+
+ for (i = 0; i < n_spills; i++)
+ {
+ potential_reload_regs[i] = spill_regs[i];
+ spill_reg_order[spill_regs[i]] = -1;
+ CLEAR_HARD_REG_BIT (forbidden_regs, spill_regs[i]);
+ }
+
+ n_spills = 0;
+ }
+
+ /* Now find more reload regs to satisfy the remaining need
+ Do it by ascending class number, since otherwise a reg
+ might be spilled for a big class and might fail to count
+ for a smaller class even though it belongs to that class.
+
+ Count spilled regs in `spills', and add entries to
+ `spill_regs' and `spill_reg_order'.
+
+ ??? Note there is a problem here.
+ When there is a need for a group in a high-numbered class,
+ and also need for non-group regs that come from a lower class,
+ the non-group regs are chosen first. If there aren't many regs,
+ they might leave no room for a group.
+
+ This was happening on the 386. To fix it, we added the code
+ that calls possible_group_p, so that the lower class won't
+ break up the last possible group.
+
+ Really fixing the problem would require changes above
+ in counting the regs already spilled, and in choose_reload_regs.
+ It might be hard to avoid introducing bugs there. */
+
+ CLEAR_HARD_REG_SET (counted_for_groups);
+ CLEAR_HARD_REG_SET (counted_for_nongroups);
+
+ for (class = 0; class < N_REG_CLASSES; class++)
+ {
+ /* First get the groups of registers.
+ If we got single registers first, we might fragment
+ possible groups. */
+ while (max_groups[class] > 0)
+ {
+ /* If any single spilled regs happen to form groups,
+ count them now. Maybe we don't really need
+ to spill another group. */
+ count_possible_groups (group_size, group_mode, max_groups);
+
+ if (max_groups[class] <= 0)
+ break;
+
+ /* Groups of size 2 (the only groups used on most machines)
+ are treated specially. */
+ if (group_size[class] == 2)
+ {
+ /* First, look for a register that will complete a group. */
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ int other;
+
+ j = potential_reload_regs[i];
+ if (j >= 0 && ! TEST_HARD_REG_BIT (bad_spill_regs, j)
+ &&
+ ((j > 0 && (other = j - 1, spill_reg_order[other] >= 0)
+ && TEST_HARD_REG_BIT (reg_class_contents[class], j)
+ && TEST_HARD_REG_BIT (reg_class_contents[class], other)
+ && HARD_REGNO_MODE_OK (other, group_mode[class])
+ && ! TEST_HARD_REG_BIT (counted_for_nongroups,
+ other)
+ /* We don't want one part of another group.
+ We could get "two groups" that overlap! */
+ && ! TEST_HARD_REG_BIT (counted_for_groups, other))
+ ||
+ (j < FIRST_PSEUDO_REGISTER - 1
+ && (other = j + 1, spill_reg_order[other] >= 0)
+ && TEST_HARD_REG_BIT (reg_class_contents[class], j)
+ && TEST_HARD_REG_BIT (reg_class_contents[class], other)
+ && HARD_REGNO_MODE_OK (j, group_mode[class])
+ && ! TEST_HARD_REG_BIT (counted_for_nongroups,
+ other)
+ && ! TEST_HARD_REG_BIT (counted_for_groups,
+ other))))
+ {
+ register enum reg_class *p;
+
+ /* We have found one that will complete a group,
+ so count off one group as provided. */
+ max_groups[class]--;
+ p = reg_class_superclasses[class];
+ while (*p != LIM_REG_CLASSES)
+ max_groups[(int) *p++]--;
+
+ /* Indicate both these regs are part of a group. */
+ SET_HARD_REG_BIT (counted_for_groups, j);
+ SET_HARD_REG_BIT (counted_for_groups, other);
+ break;
+ }
+ }
+ /* We can't complete a group, so start one. */
+#ifdef SMALL_REGISTER_CLASSES
+ /* Look for a pair neither of which is explicitly used. */
+ if (i == FIRST_PSEUDO_REGISTER)
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ int k;
+ j = potential_reload_regs[i];
+ /* Verify that J+1 is a potential reload reg. */
+ for (k = 0; k < FIRST_PSEUDO_REGISTER; k++)
+ if (potential_reload_regs[k] == j + 1)
+ break;
+ if (j >= 0 && j + 1 < FIRST_PSEUDO_REGISTER
+ && k < FIRST_PSEUDO_REGISTER
+ && spill_reg_order[j] < 0 && spill_reg_order[j + 1] < 0
+ && TEST_HARD_REG_BIT (reg_class_contents[class], j)
+ && TEST_HARD_REG_BIT (reg_class_contents[class], j + 1)
+ && HARD_REGNO_MODE_OK (j, group_mode[class])
+ && ! TEST_HARD_REG_BIT (counted_for_nongroups,
+ j + 1)
+ && ! TEST_HARD_REG_BIT (bad_spill_regs, j + 1)
+ /* Reject J at this stage
+ if J+1 was explicitly used. */
+ && ! regs_explicitly_used[j + 1])
+ break;
+ }
+#endif
+ /* Now try any group at all
+ whose registers are not in bad_spill_regs. */
+ if (i == FIRST_PSEUDO_REGISTER)
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ int k;
+ j = potential_reload_regs[i];
+ /* Verify that J+1 is a potential reload reg. */
+ for (k = 0; k < FIRST_PSEUDO_REGISTER; k++)
+ if (potential_reload_regs[k] == j + 1)
+ break;
+ if (j >= 0 && j + 1 < FIRST_PSEUDO_REGISTER
+ && k < FIRST_PSEUDO_REGISTER
+ && spill_reg_order[j] < 0 && spill_reg_order[j + 1] < 0
+ && TEST_HARD_REG_BIT (reg_class_contents[class], j)
+ && TEST_HARD_REG_BIT (reg_class_contents[class], j + 1)
+ && HARD_REGNO_MODE_OK (j, group_mode[class])
+ && ! TEST_HARD_REG_BIT (counted_for_nongroups,
+ j + 1)
+ && ! TEST_HARD_REG_BIT (bad_spill_regs, j + 1))
+ break;
+ }
+
+ /* I should be the index in potential_reload_regs
+ of the new reload reg we have found. */
+
+ if (i >= FIRST_PSEUDO_REGISTER)
+ {
+ /* There are no groups left to spill. */
+ spill_failure (max_groups_insn[class]);
+ failure = 1;
+ goto failed;
+ }
+ else
+ something_changed
+ |= new_spill_reg (i, class, max_needs, NULL_PTR,
+ global, dumpfile);
+ }
+ else
+ {
+ /* For groups of more than 2 registers,
+ look for a sufficient sequence of unspilled registers,
+ and spill them all at once. */
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ int k;
+
+ j = potential_reload_regs[i];
+ if (j >= 0
+ && j + group_size[class] <= FIRST_PSEUDO_REGISTER
+ && HARD_REGNO_MODE_OK (j, group_mode[class]))
+ {
+ /* Check each reg in the sequence. */
+ for (k = 0; k < group_size[class]; k++)
+ if (! (spill_reg_order[j + k] < 0
+ && ! TEST_HARD_REG_BIT (bad_spill_regs, j + k)
+ && TEST_HARD_REG_BIT (reg_class_contents[class], j + k)))
+ break;
+ /* We got a full sequence, so spill them all. */
+ if (k == group_size[class])
+ {
+ register enum reg_class *p;
+ for (k = 0; k < group_size[class]; k++)
+ {
+ int idx;
+ SET_HARD_REG_BIT (counted_for_groups, j + k);
+ for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx++)
+ if (potential_reload_regs[idx] == j + k)
+ break;
+ something_changed
+ |= new_spill_reg (idx, class,
+ max_needs, NULL_PTR,
+ global, dumpfile);
+ }
+
+ /* We have found one that will complete a group,
+ so count off one group as provided. */
+ max_groups[class]--;
+ p = reg_class_superclasses[class];
+ while (*p != LIM_REG_CLASSES)
+ max_groups[(int) *p++]--;
+
+ break;
+ }
+ }
+ }
+ /* We couldn't find any registers for this reload.
+ Avoid going into an infinite loop. */
+ if (i >= FIRST_PSEUDO_REGISTER)
+ {
+ /* There are no groups left. */
+ spill_failure (max_groups_insn[class]);
+ failure = 1;
+ goto failed;
+ }
+ }
+ }
+
+ /* Now similarly satisfy all need for single registers. */
+
+ while (max_needs[class] > 0 || max_nongroups[class] > 0)
+ {
+#ifdef SMALL_REGISTER_CLASSES
+ /* This should be right for all machines, but only the 386
+ is known to need it, so this conditional plays safe.
+ ??? For 2.5, try making this unconditional. */
+ /* If we spilled enough regs, but they weren't counted
+ against the non-group need, see if we can count them now.
+ If so, we can avoid some actual spilling. */
+ if (max_needs[class] <= 0 && max_nongroups[class] > 0)
+ for (i = 0; i < n_spills; i++)
+ if (TEST_HARD_REG_BIT (reg_class_contents[class],
+ spill_regs[i])
+ && !TEST_HARD_REG_BIT (counted_for_groups,
+ spill_regs[i])
+ && !TEST_HARD_REG_BIT (counted_for_nongroups,
+ spill_regs[i])
+ && max_nongroups[class] > 0)
+ {
+ register enum reg_class *p;
+
+ SET_HARD_REG_BIT (counted_for_nongroups, spill_regs[i]);
+ max_nongroups[class]--;
+ p = reg_class_superclasses[class];
+ while (*p != LIM_REG_CLASSES)
+ max_nongroups[(int) *p++]--;
+ }
+ if (max_needs[class] <= 0 && max_nongroups[class] <= 0)
+ break;
+#endif
+
+ /* Consider the potential reload regs that aren't
+ yet in use as reload regs, in order of preference.
+ Find the most preferred one that's in this class. */
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (potential_reload_regs[i] >= 0
+ && TEST_HARD_REG_BIT (reg_class_contents[class],
+ potential_reload_regs[i])
+ /* If this reg will not be available for groups,
+ pick one that does not foreclose possible groups.
+ This is a kludge, and not very general,
+ but it should be sufficient to make the 386 work,
+ and the problem should not occur on machines with
+ more registers. */
+ && (max_nongroups[class] == 0
+ || possible_group_p (potential_reload_regs[i], max_groups)))
+ break;
+
+ /* If we couldn't get a register, try to get one even if we
+ might foreclose possible groups. This may cause problems
+ later, but that's better than aborting now, since it is
+ possible that we will, in fact, be able to form the needed
+ group even with this allocation. */
+
+ if (i >= FIRST_PSEUDO_REGISTER
+ && (asm_noperands (max_needs[class] > 0
+ ? max_needs_insn[class]
+ : max_nongroups_insn[class])
+ < 0))
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (potential_reload_regs[i] >= 0
+ && TEST_HARD_REG_BIT (reg_class_contents[class],
+ potential_reload_regs[i]))
+ break;
+
+ /* I should be the index in potential_reload_regs
+ of the new reload reg we have found. */
+
+ if (i >= FIRST_PSEUDO_REGISTER)
+ {
+ /* There are no possible registers left to spill. */
+ spill_failure (max_needs[class] > 0 ? max_needs_insn[class]
+ : max_nongroups_insn[class]);
+ failure = 1;
+ goto failed;
+ }
+ else
+ something_changed
+ |= new_spill_reg (i, class, max_needs, max_nongroups,
+ global, dumpfile);
+ }
+ }
+ }
+
+ /* If global-alloc was run, notify it of any register eliminations we have
+ done. */
+ if (global)
+ for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+ if (ep->can_eliminate)
+ mark_elimination (ep->from, ep->to);
+
+ /* Insert code to save and restore call-clobbered hard regs
+ around calls. Tell if what mode to use so that we will process
+ those insns in reload_as_needed if we have to. */
+
+ if (caller_save_needed)
+ save_call_clobbered_regs (num_eliminable ? QImode
+ : caller_save_spill_class != NO_REGS ? HImode
+ : VOIDmode);
+
+ /* If a pseudo has no hard reg, delete the insns that made the equivalence.
+ If that insn didn't set the register (i.e., it copied the register to
+ memory), just delete that insn instead of the equivalencing insn plus
+ anything now dead. If we call delete_dead_insn on that insn, we may
+ delete the insn that actually sets the register if the register die
+ there and that is incorrect. */
+
+ for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+ if (reg_renumber[i] < 0 && reg_equiv_init[i] != 0
+ && GET_CODE (reg_equiv_init[i]) != NOTE)
+ {
+ if (reg_set_p (regno_reg_rtx[i], PATTERN (reg_equiv_init[i])))
+ delete_dead_insn (reg_equiv_init[i]);
+ else
+ {
+ PUT_CODE (reg_equiv_init[i], NOTE);
+ NOTE_SOURCE_FILE (reg_equiv_init[i]) = 0;
+ NOTE_LINE_NUMBER (reg_equiv_init[i]) = NOTE_INSN_DELETED;
+ }
+ }
+
+ /* Use the reload registers where necessary
+ by generating move instructions to move the must-be-register
+ values into or out of the reload registers. */
+
+ if (something_needs_reloads || something_needs_elimination
+ || (caller_save_needed && num_eliminable)
+ || caller_save_spill_class != NO_REGS)
+ reload_as_needed (first, global);
+
+ /* If we were able to eliminate the frame pointer, show that it is no
+ longer live at the start of any basic block. If it ls live by
+ virtue of being in a pseudo, that pseudo will be marked live
+ and hence the frame pointer will be known to be live via that
+ pseudo. */
+
+ if (! frame_pointer_needed)
+ for (i = 0; i < n_basic_blocks; i++)
+ basic_block_live_at_start[i][HARD_FRAME_POINTER_REGNUM / REGSET_ELT_BITS]
+ &= ~ ((REGSET_ELT_TYPE) 1 << (HARD_FRAME_POINTER_REGNUM
+ % REGSET_ELT_BITS));
+
+ /* Come here (with failure set nonzero) if we can't get enough spill regs
+ and we decide not to abort about it. */
+ failed:
+
+ reload_in_progress = 0;
+
+ /* Now eliminate all pseudo regs by modifying them into
+ their equivalent memory references.
+ The REG-rtx's for the pseudos are modified in place,
+ so all insns that used to refer to them now refer to memory.
+
+ For a reg that has a reg_equiv_address, all those insns
+ were changed by reloading so that no insns refer to it any longer;
+ but the DECL_RTL of a variable decl may refer to it,
+ and if so this causes the debugging info to mention the variable. */
+
+ for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+ {
+ rtx addr = 0;
+ int in_struct = 0;
+ if (reg_equiv_mem[i])
+ {
+ addr = XEXP (reg_equiv_mem[i], 0);
+ in_struct = MEM_IN_STRUCT_P (reg_equiv_mem[i]);
+ }
+ if (reg_equiv_address[i])
+ addr = reg_equiv_address[i];
+ if (addr)
+ {
+ if (reg_renumber[i] < 0)
+ {
+ rtx reg = regno_reg_rtx[i];
+ XEXP (reg, 0) = addr;
+ REG_USERVAR_P (reg) = 0;
+ MEM_IN_STRUCT_P (reg) = in_struct;
+ PUT_CODE (reg, MEM);
+ }
+ else if (reg_equiv_mem[i])
+ XEXP (reg_equiv_mem[i], 0) = addr;
+ }
+ }
+
+#ifdef PRESERVE_DEATH_INFO_REGNO_P
+ /* Make a pass over all the insns and remove death notes for things that
+ are no longer registers or no longer die in the insn (e.g., an input
+ and output pseudo being tied). */
+
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ rtx note, next;
+
+ for (note = REG_NOTES (insn); note; note = next)
+ {
+ next = XEXP (note, 1);
+ if (REG_NOTE_KIND (note) == REG_DEAD
+ && (GET_CODE (XEXP (note, 0)) != REG
+ || reg_set_p (XEXP (note, 0), PATTERN (insn))))
+ remove_note (insn, note);
+ }
+ }
+#endif
+
+ /* Indicate that we no longer have known memory locations or constants. */
+ reg_equiv_constant = 0;
+ reg_equiv_memory_loc = 0;
+
+ if (scratch_list)
+ free (scratch_list);
+ scratch_list = 0;
+ if (scratch_block)
+ free (scratch_block);
+ scratch_block = 0;
+
+ return failure;
+}
+
+/* Nonzero if, after spilling reg REGNO for non-groups,
+ it will still be possible to find a group if we still need one. */
+
+static int
+possible_group_p (regno, max_groups)
+ int regno;
+ int *max_groups;
+{
+ int i;
+ int class = (int) NO_REGS;
+
+ for (i = 0; i < (int) N_REG_CLASSES; i++)
+ if (max_groups[i] > 0)
+ {
+ class = i;
+ break;
+ }
+
+ if (class == (int) NO_REGS)
+ return 1;
+
+ /* Consider each pair of consecutive registers. */
+ for (i = 0; i < FIRST_PSEUDO_REGISTER - 1; i++)
+ {
+ /* Ignore pairs that include reg REGNO. */
+ if (i == regno || i + 1 == regno)
+ continue;
+
+ /* Ignore pairs that are outside the class that needs the group.
+ ??? Here we fail to handle the case where two different classes
+ independently need groups. But this never happens with our
+ current machine descriptions. */
+ if (! (TEST_HARD_REG_BIT (reg_class_contents[class], i)
+ && TEST_HARD_REG_BIT (reg_class_contents[class], i + 1)))
+ continue;
+
+ /* A pair of consecutive regs we can still spill does the trick. */
+ if (spill_reg_order[i] < 0 && spill_reg_order[i + 1] < 0
+ && ! TEST_HARD_REG_BIT (bad_spill_regs, i)
+ && ! TEST_HARD_REG_BIT (bad_spill_regs, i + 1))
+ return 1;
+
+ /* A pair of one already spilled and one we can spill does it
+ provided the one already spilled is not otherwise reserved. */
+ if (spill_reg_order[i] < 0
+ && ! TEST_HARD_REG_BIT (bad_spill_regs, i)
+ && spill_reg_order[i + 1] >= 0
+ && ! TEST_HARD_REG_BIT (counted_for_groups, i + 1)
+ && ! TEST_HARD_REG_BIT (counted_for_nongroups, i + 1))
+ return 1;
+ if (spill_reg_order[i + 1] < 0
+ && ! TEST_HARD_REG_BIT (bad_spill_regs, i + 1)
+ && spill_reg_order[i] >= 0
+ && ! TEST_HARD_REG_BIT (counted_for_groups, i)
+ && ! TEST_HARD_REG_BIT (counted_for_nongroups, i))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Count any groups that can be formed from the registers recently spilled.
+ This is done class by class, in order of ascending class number. */
+
+static void
+count_possible_groups (group_size, group_mode, max_groups)
+ int *group_size;
+ enum machine_mode *group_mode;
+ int *max_groups;
+{
+ int i;
+ /* Now find all consecutive groups of spilled registers
+ and mark each group off against the need for such groups.
+ But don't count them against ordinary need, yet. */
+
+ for (i = 0; i < N_REG_CLASSES; i++)
+ if (group_size[i] > 1)
+ {
+ HARD_REG_SET new;
+ int j;
+
+ CLEAR_HARD_REG_SET (new);
+
+ /* Make a mask of all the regs that are spill regs in class I. */
+ for (j = 0; j < n_spills; j++)
+ if (TEST_HARD_REG_BIT (reg_class_contents[i], spill_regs[j])
+ && ! TEST_HARD_REG_BIT (counted_for_groups, spill_regs[j])
+ && ! TEST_HARD_REG_BIT (counted_for_nongroups,
+ spill_regs[j]))
+ SET_HARD_REG_BIT (new, spill_regs[j]);
+
+ /* Find each consecutive group of them. */
+ for (j = 0; j < FIRST_PSEUDO_REGISTER && max_groups[i] > 0; j++)
+ if (TEST_HARD_REG_BIT (new, j)
+ && j + group_size[i] <= FIRST_PSEUDO_REGISTER
+ /* Next line in case group-mode for this class
+ demands an even-odd pair. */
+ && HARD_REGNO_MODE_OK (j, group_mode[i]))
+ {
+ int k;
+ for (k = 1; k < group_size[i]; k++)
+ if (! TEST_HARD_REG_BIT (new, j + k))
+ break;
+ if (k == group_size[i])
+ {
+ /* We found a group. Mark it off against this class's
+ need for groups, and against each superclass too. */
+ register enum reg_class *p;
+ max_groups[i]--;
+ p = reg_class_superclasses[i];
+ while (*p != LIM_REG_CLASSES)
+ max_groups[(int) *p++]--;
+ /* Don't count these registers again. */
+ for (k = 0; k < group_size[i]; k++)
+ SET_HARD_REG_BIT (counted_for_groups, j + k);
+ }
+ /* Skip to the last reg in this group. When j is incremented
+ above, it will then point to the first reg of the next
+ possible group. */
+ j += k - 1;
+ }
+ }
+
+}
+
+/* ALLOCATE_MODE is a register mode that needs to be reloaded. OTHER_MODE is
+ another mode that needs to be reloaded for the same register class CLASS.
+ If any reg in CLASS allows ALLOCATE_MODE but not OTHER_MODE, fail.
+ ALLOCATE_MODE will never be smaller than OTHER_MODE.
+
+ This code used to also fail if any reg in CLASS allows OTHER_MODE but not
+ ALLOCATE_MODE. This test is unnecessary, because we will never try to put
+ something of mode ALLOCATE_MODE into an OTHER_MODE register. Testing this
+ causes unnecessary failures on machines requiring alignment of register
+ groups when the two modes are different sizes, because the larger mode has
+ more strict alignment rules than the smaller mode. */
+
+static int
+modes_equiv_for_class_p (allocate_mode, other_mode, class)
+ enum machine_mode allocate_mode, other_mode;
+ enum reg_class class;
+{
+ register int regno;
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ {
+ if (TEST_HARD_REG_BIT (reg_class_contents[(int) class], regno)
+ && HARD_REGNO_MODE_OK (regno, allocate_mode)
+ && ! HARD_REGNO_MODE_OK (regno, other_mode))
+ return 0;
+ }
+ return 1;
+}
+
+/* Handle the failure to find a register to spill.
+ INSN should be one of the insns which needed this particular spill reg. */
+
+static void
+spill_failure (insn)
+ rtx insn;
+{
+ if (asm_noperands (PATTERN (insn)) >= 0)
+ error_for_asm (insn, "`asm' needs too many reloads");
+ else
+ abort ();
+}
+
+/* Add a new register to the tables of available spill-registers
+ (as well as spilling all pseudos allocated to the register).
+ I is the index of this register in potential_reload_regs.
+ CLASS is the regclass whose need is being satisfied.
+ MAX_NEEDS and MAX_NONGROUPS are the vectors of needs,
+ so that this register can count off against them.
+ MAX_NONGROUPS is 0 if this register is part of a group.
+ GLOBAL and DUMPFILE are the same as the args that `reload' got. */
+
+static int
+new_spill_reg (i, class, max_needs, max_nongroups, global, dumpfile)
+ int i;
+ int class;
+ int *max_needs;
+ int *max_nongroups;
+ int global;
+ FILE *dumpfile;
+{
+ register enum reg_class *p;
+ int val;
+ int regno = potential_reload_regs[i];
+
+ if (i >= FIRST_PSEUDO_REGISTER)
+ abort (); /* Caller failed to find any register. */
+
+ if (fixed_regs[regno] || TEST_HARD_REG_BIT (forbidden_regs, regno))
+ fatal ("fixed or forbidden register was spilled.\n\
+This may be due to a compiler bug or to impossible asm\n\
+statements or clauses.");
+
+ /* Make reg REGNO an additional reload reg. */
+
+ potential_reload_regs[i] = -1;
+ spill_regs[n_spills] = regno;
+ spill_reg_order[regno] = n_spills;
+ if (dumpfile)
+ fprintf (dumpfile, "Spilling reg %d.\n", spill_regs[n_spills]);
+
+ /* Clear off the needs we just satisfied. */
+
+ max_needs[class]--;
+ p = reg_class_superclasses[class];
+ while (*p != LIM_REG_CLASSES)
+ max_needs[(int) *p++]--;
+
+ if (max_nongroups && max_nongroups[class] > 0)
+ {
+ SET_HARD_REG_BIT (counted_for_nongroups, regno);
+ max_nongroups[class]--;
+ p = reg_class_superclasses[class];
+ while (*p != LIM_REG_CLASSES)
+ max_nongroups[(int) *p++]--;
+ }
+
+ /* Spill every pseudo reg that was allocated to this reg
+ or to something that overlaps this reg. */
+
+ val = spill_hard_reg (spill_regs[n_spills], global, dumpfile, 0);
+
+ /* If there are some registers still to eliminate and this register
+ wasn't ever used before, additional stack space may have to be
+ allocated to store this register. Thus, we may have changed the offset
+ between the stack and frame pointers, so mark that something has changed.
+ (If new pseudos were spilled, thus requiring more space, VAL would have
+ been set non-zero by the call to spill_hard_reg above since additional
+ reloads may be needed in that case.
+
+ One might think that we need only set VAL to 1 if this is a call-used
+ register. However, the set of registers that must be saved by the
+ prologue is not identical to the call-used set. For example, the
+ register used by the call insn for the return PC is a call-used register,
+ but must be saved by the prologue. */
+ if (num_eliminable && ! regs_ever_live[spill_regs[n_spills]])
+ val = 1;
+
+ regs_ever_live[spill_regs[n_spills]] = 1;
+ n_spills++;
+
+ return val;
+}
+
+/* Delete an unneeded INSN and any previous insns who sole purpose is loading
+ data that is dead in INSN. */
+
+static void
+delete_dead_insn (insn)
+ rtx insn;
+{
+ rtx prev = prev_real_insn (insn);
+ rtx prev_dest;
+
+ /* If the previous insn sets a register that dies in our insn, delete it
+ too. */
+ if (prev && GET_CODE (PATTERN (prev)) == SET
+ && (prev_dest = SET_DEST (PATTERN (prev)), GET_CODE (prev_dest) == REG)
+ && reg_mentioned_p (prev_dest, PATTERN (insn))
+ && find_regno_note (insn, REG_DEAD, REGNO (prev_dest)))
+ delete_dead_insn (prev);
+
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+}
+
+/* Modify the home of pseudo-reg I.
+ The new home is present in reg_renumber[I].
+
+ FROM_REG may be the hard reg that the pseudo-reg is being spilled from;
+ or it may be -1, meaning there is none or it is not relevant.
+ This is used so that all pseudos spilled from a given hard reg
+ can share one stack slot. */
+
+static void
+alter_reg (i, from_reg)
+ register int i;
+ int from_reg;
+{
+ /* When outputting an inline function, this can happen
+ for a reg that isn't actually used. */
+ if (regno_reg_rtx[i] == 0)
+ return;
+
+ /* If the reg got changed to a MEM at rtl-generation time,
+ ignore it. */
+ if (GET_CODE (regno_reg_rtx[i]) != REG)
+ return;
+
+ /* Modify the reg-rtx to contain the new hard reg
+ number or else to contain its pseudo reg number. */
+ REGNO (regno_reg_rtx[i])
+ = reg_renumber[i] >= 0 ? reg_renumber[i] : i;
+
+ /* If we have a pseudo that is needed but has no hard reg or equivalent,
+ allocate a stack slot for it. */
+
+ if (reg_renumber[i] < 0
+ && reg_n_refs[i] > 0
+ && reg_equiv_constant[i] == 0
+ && reg_equiv_memory_loc[i] == 0)
+ {
+ register rtx x;
+ int inherent_size = PSEUDO_REGNO_BYTES (i);
+ int total_size = MAX (inherent_size, reg_max_ref_width[i]);
+ int adjust = 0;
+
+ /* Each pseudo reg has an inherent size which comes from its own mode,
+ and a total size which provides room for paradoxical subregs
+ which refer to the pseudo reg in wider modes.
+
+ We can use a slot already allocated if it provides both
+ enough inherent space and enough total space.
+ Otherwise, we allocate a new slot, making sure that it has no less
+ inherent space, and no less total space, then the previous slot. */
+ if (from_reg == -1)
+ {
+ /* No known place to spill from => no slot to reuse. */
+ x = assign_stack_local (GET_MODE (regno_reg_rtx[i]), total_size, -1);
+#if BYTES_BIG_ENDIAN
+ /* Cancel the big-endian correction done in assign_stack_local.
+ Get the address of the beginning of the slot.
+ This is so we can do a big-endian correction unconditionally
+ below. */
+ adjust = inherent_size - total_size;
+#endif
+ }
+ /* Reuse a stack slot if possible. */
+ else if (spill_stack_slot[from_reg] != 0
+ && spill_stack_slot_width[from_reg] >= total_size
+ && (GET_MODE_SIZE (GET_MODE (spill_stack_slot[from_reg]))
+ >= inherent_size))
+ x = spill_stack_slot[from_reg];
+ /* Allocate a bigger slot. */
+ else
+ {
+ /* Compute maximum size needed, both for inherent size
+ and for total size. */
+ enum machine_mode mode = GET_MODE (regno_reg_rtx[i]);
+ if (spill_stack_slot[from_reg])
+ {
+ if (GET_MODE_SIZE (GET_MODE (spill_stack_slot[from_reg]))
+ > inherent_size)
+ mode = GET_MODE (spill_stack_slot[from_reg]);
+ if (spill_stack_slot_width[from_reg] > total_size)
+ total_size = spill_stack_slot_width[from_reg];
+ }
+ /* Make a slot with that size. */
+ x = assign_stack_local (mode, total_size, -1);
+#if BYTES_BIG_ENDIAN
+ /* Cancel the big-endian correction done in assign_stack_local.
+ Get the address of the beginning of the slot.
+ This is so we can do a big-endian correction unconditionally
+ below. */
+ adjust = GET_MODE_SIZE (mode) - total_size;
+#endif
+ spill_stack_slot[from_reg] = x;
+ spill_stack_slot_width[from_reg] = total_size;
+ }
+
+#if BYTES_BIG_ENDIAN
+ /* On a big endian machine, the "address" of the slot
+ is the address of the low part that fits its inherent mode. */
+ if (inherent_size < total_size)
+ adjust += (total_size - inherent_size);
+#endif /* BYTES_BIG_ENDIAN */
+
+ /* If we have any adjustment to make, or if the stack slot is the
+ wrong mode, make a new stack slot. */
+ if (adjust != 0 || GET_MODE (x) != GET_MODE (regno_reg_rtx[i]))
+ {
+ x = gen_rtx (MEM, GET_MODE (regno_reg_rtx[i]),
+ plus_constant (XEXP (x, 0), adjust));
+ RTX_UNCHANGING_P (x) = RTX_UNCHANGING_P (regno_reg_rtx[i]);
+ }
+
+ /* Save the stack slot for later. */
+ reg_equiv_memory_loc[i] = x;
+ }
+}
+
+/* Mark the slots in regs_ever_live for the hard regs
+ used by pseudo-reg number REGNO. */
+
+void
+mark_home_live (regno)
+ int regno;
+{
+ register int i, lim;
+ i = reg_renumber[regno];
+ if (i < 0)
+ return;
+ lim = i + HARD_REGNO_NREGS (i, PSEUDO_REGNO_MODE (regno));
+ while (i < lim)
+ regs_ever_live[i++] = 1;
+}
+
+/* Mark the registers used in SCRATCH as being live. */
+
+static void
+mark_scratch_live (scratch)
+ rtx scratch;
+{
+ register int i;
+ int regno = REGNO (scratch);
+ int lim = regno + HARD_REGNO_NREGS (regno, GET_MODE (scratch));
+
+ for (i = regno; i < lim; i++)
+ regs_ever_live[i] = 1;
+}
+
+/* This function handles the tracking of elimination offsets around branches.
+
+ X is a piece of RTL being scanned.
+
+ INSN is the insn that it came from, if any.
+
+ INITIAL_P is non-zero if we are to set the offset to be the initial
+ offset and zero if we are setting the offset of the label to be the
+ current offset. */
+
+static void
+set_label_offsets (x, insn, initial_p)
+ rtx x;
+ rtx insn;
+ int initial_p;
+{
+ enum rtx_code code = GET_CODE (x);
+ rtx tem;
+ int i;
+ struct elim_table *p;
+
+ switch (code)
+ {
+ case LABEL_REF:
+ if (LABEL_REF_NONLOCAL_P (x))
+ return;
+
+ x = XEXP (x, 0);
+
+ /* ... fall through ... */
+
+ case CODE_LABEL:
+ /* If we know nothing about this label, set the desired offsets. Note
+ that this sets the offset at a label to be the offset before a label
+ if we don't know anything about the label. This is not correct for
+ the label after a BARRIER, but is the best guess we can make. If
+ we guessed wrong, we will suppress an elimination that might have
+ been possible had we been able to guess correctly. */
+
+ if (! offsets_known_at[CODE_LABEL_NUMBER (x)])
+ {
+ for (i = 0; i < NUM_ELIMINABLE_REGS; i++)
+ offsets_at[CODE_LABEL_NUMBER (x)][i]
+ = (initial_p ? reg_eliminate[i].initial_offset
+ : reg_eliminate[i].offset);
+ offsets_known_at[CODE_LABEL_NUMBER (x)] = 1;
+ }
+
+ /* Otherwise, if this is the definition of a label and it is
+ preceded by a BARRIER, set our offsets to the known offset of
+ that label. */
+
+ else if (x == insn
+ && (tem = prev_nonnote_insn (insn)) != 0
+ && GET_CODE (tem) == BARRIER)
+ {
+ num_not_at_initial_offset = 0;
+ for (i = 0; i < NUM_ELIMINABLE_REGS; i++)
+ {
+ reg_eliminate[i].offset = reg_eliminate[i].previous_offset
+ = offsets_at[CODE_LABEL_NUMBER (x)][i];
+ if (reg_eliminate[i].can_eliminate
+ && (reg_eliminate[i].offset
+ != reg_eliminate[i].initial_offset))
+ num_not_at_initial_offset++;
+ }
+ }
+
+ else
+ /* If neither of the above cases is true, compare each offset
+ with those previously recorded and suppress any eliminations
+ where the offsets disagree. */
+
+ for (i = 0; i < NUM_ELIMINABLE_REGS; i++)
+ if (offsets_at[CODE_LABEL_NUMBER (x)][i]
+ != (initial_p ? reg_eliminate[i].initial_offset
+ : reg_eliminate[i].offset))
+ reg_eliminate[i].can_eliminate = 0;
+
+ return;
+
+ case JUMP_INSN:
+ set_label_offsets (PATTERN (insn), insn, initial_p);
+
+ /* ... fall through ... */
+
+ case INSN:
+ case CALL_INSN:
+ /* Any labels mentioned in REG_LABEL notes can be branched to indirectly
+ and hence must have all eliminations at their initial offsets. */
+ for (tem = REG_NOTES (x); tem; tem = XEXP (tem, 1))
+ if (REG_NOTE_KIND (tem) == REG_LABEL)
+ set_label_offsets (XEXP (tem, 0), insn, 1);
+ return;
+
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ /* Each of the labels in the address vector must be at their initial
+ offsets. We want the first first for ADDR_VEC and the second
+ field for ADDR_DIFF_VEC. */
+
+ for (i = 0; i < XVECLEN (x, code == ADDR_DIFF_VEC); i++)
+ set_label_offsets (XVECEXP (x, code == ADDR_DIFF_VEC, i),
+ insn, initial_p);
+ return;
+
+ case SET:
+ /* We only care about setting PC. If the source is not RETURN,
+ IF_THEN_ELSE, or a label, disable any eliminations not at
+ their initial offsets. Similarly if any arm of the IF_THEN_ELSE
+ isn't one of those possibilities. For branches to a label,
+ call ourselves recursively.
+
+ Note that this can disable elimination unnecessarily when we have
+ a non-local goto since it will look like a non-constant jump to
+ someplace in the current function. This isn't a significant
+ problem since such jumps will normally be when all elimination
+ pairs are back to their initial offsets. */
+
+ if (SET_DEST (x) != pc_rtx)
+ return;
+
+ switch (GET_CODE (SET_SRC (x)))
+ {
+ case PC:
+ case RETURN:
+ return;
+
+ case LABEL_REF:
+ set_label_offsets (XEXP (SET_SRC (x), 0), insn, initial_p);
+ return;
+
+ case IF_THEN_ELSE:
+ tem = XEXP (SET_SRC (x), 1);
+ if (GET_CODE (tem) == LABEL_REF)
+ set_label_offsets (XEXP (tem, 0), insn, initial_p);
+ else if (GET_CODE (tem) != PC && GET_CODE (tem) != RETURN)
+ break;
+
+ tem = XEXP (SET_SRC (x), 2);
+ if (GET_CODE (tem) == LABEL_REF)
+ set_label_offsets (XEXP (tem, 0), insn, initial_p);
+ else if (GET_CODE (tem) != PC && GET_CODE (tem) != RETURN)
+ break;
+ return;
+ }
+
+ /* If we reach here, all eliminations must be at their initial
+ offset because we are doing a jump to a variable address. */
+ for (p = reg_eliminate; p < &reg_eliminate[NUM_ELIMINABLE_REGS]; p++)
+ if (p->offset != p->initial_offset)
+ p->can_eliminate = 0;
+ }
+}
+
+/* Used for communication between the next two function to properly share
+ the vector for an ASM_OPERANDS. */
+
+static struct rtvec_def *old_asm_operands_vec, *new_asm_operands_vec;
+
+/* Scan X and replace any eliminable registers (such as fp) with a
+ replacement (such as sp), plus an offset.
+
+ MEM_MODE is the mode of an enclosing MEM. We need this to know how
+ much to adjust a register for, e.g., PRE_DEC. Also, if we are inside a
+ MEM, we are allowed to replace a sum of a register and the constant zero
+ with the register, which we cannot do outside a MEM. In addition, we need
+ to record the fact that a register is referenced outside a MEM.
+
+ If INSN is an insn, it is the insn containing X. If we replace a REG
+ in a SET_DEST with an equivalent MEM and INSN is non-zero, write a
+ CLOBBER of the pseudo after INSN so find_equiv_regs will know that
+ that the REG is being modified.
+
+ Alternatively, INSN may be a note (an EXPR_LIST or INSN_LIST).
+ That's used when we eliminate in expressions stored in notes.
+ This means, do not set ref_outside_mem even if the reference
+ is outside of MEMs.
+
+ If we see a modification to a register we know about, take the
+ appropriate action (see case SET, below).
+
+ REG_EQUIV_MEM and REG_EQUIV_ADDRESS contain address that have had
+ replacements done assuming all offsets are at their initial values. If
+ they are not, or if REG_EQUIV_ADDRESS is nonzero for a pseudo we
+ encounter, return the actual location so that find_reloads will do
+ the proper thing. */
+
+rtx
+eliminate_regs (x, mem_mode, insn)
+ rtx x;
+ enum machine_mode mem_mode;
+ rtx insn;
+{
+ enum rtx_code code = GET_CODE (x);
+ struct elim_table *ep;
+ int regno;
+ rtx new;
+ int i, j;
+ char *fmt;
+ int copied = 0;
+
+ switch (code)
+ {
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case CONST:
+ case SYMBOL_REF:
+ case CODE_LABEL:
+ case PC:
+ case CC0:
+ case ASM_INPUT:
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ case RETURN:
+ return x;
+
+ case REG:
+ regno = REGNO (x);
+
+ /* First handle the case where we encounter a bare register that
+ is eliminable. Replace it with a PLUS. */
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS];
+ ep++)
+ if (ep->from_rtx == x && ep->can_eliminate)
+ {
+ if (! mem_mode
+ /* Refs inside notes don't count for this purpose. */
+ && ! (insn != 0 && (GET_CODE (insn) == EXPR_LIST
+ || GET_CODE (insn) == INSN_LIST)))
+ ep->ref_outside_mem = 1;
+ return plus_constant (ep->to_rtx, ep->previous_offset);
+ }
+
+ }
+ else if (reg_equiv_memory_loc && reg_equiv_memory_loc[regno]
+ && (reg_equiv_address[regno] || num_not_at_initial_offset))
+ {
+ /* In this case, find_reloads would attempt to either use an
+ incorrect address (if something is not at its initial offset)
+ or substitute an replaced address into an insn (which loses
+ if the offset is changed by some later action). So we simply
+ return the replaced stack slot (assuming it is changed by
+ elimination) and ignore the fact that this is actually a
+ reference to the pseudo. Ensure we make a copy of the
+ address in case it is shared. */
+ new = eliminate_regs (reg_equiv_memory_loc[regno],
+ mem_mode, insn);
+ if (new != reg_equiv_memory_loc[regno])
+ {
+ cannot_omit_stores[regno] = 1;
+ return copy_rtx (new);
+ }
+ }
+ return x;
+
+ case PLUS:
+ /* If this is the sum of an eliminable register and a constant, rework
+ the sum. */
+ if (GET_CODE (XEXP (x, 0)) == REG
+ && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER
+ && CONSTANT_P (XEXP (x, 1)))
+ {
+ for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS];
+ ep++)
+ if (ep->from_rtx == XEXP (x, 0) && ep->can_eliminate)
+ {
+ if (! mem_mode
+ /* Refs inside notes don't count for this purpose. */
+ && ! (insn != 0 && (GET_CODE (insn) == EXPR_LIST
+ || GET_CODE (insn) == INSN_LIST)))
+ ep->ref_outside_mem = 1;
+
+ /* The only time we want to replace a PLUS with a REG (this
+ occurs when the constant operand of the PLUS is the negative
+ of the offset) is when we are inside a MEM. We won't want
+ to do so at other times because that would change the
+ structure of the insn in a way that reload can't handle.
+ We special-case the commonest situation in
+ eliminate_regs_in_insn, so just replace a PLUS with a
+ PLUS here, unless inside a MEM. */
+ if (mem_mode != 0 && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) == - ep->previous_offset)
+ return ep->to_rtx;
+ else
+ return gen_rtx (PLUS, Pmode, ep->to_rtx,
+ plus_constant (XEXP (x, 1),
+ ep->previous_offset));
+ }
+
+ /* If the register is not eliminable, we are done since the other
+ operand is a constant. */
+ return x;
+ }
+
+ /* If this is part of an address, we want to bring any constant to the
+ outermost PLUS. We will do this by doing register replacement in
+ our operands and seeing if a constant shows up in one of them.
+
+ We assume here this is part of an address (or a "load address" insn)
+ since an eliminable register is not likely to appear in any other
+ context.
+
+ If we have (plus (eliminable) (reg)), we want to produce
+ (plus (plus (replacement) (reg) (const))). If this was part of a
+ normal add insn, (plus (replacement) (reg)) will be pushed as a
+ reload. This is the desired action. */
+
+ {
+ rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, insn);
+ rtx new1 = eliminate_regs (XEXP (x, 1), mem_mode, insn);
+
+ if (new0 != XEXP (x, 0) || new1 != XEXP (x, 1))
+ {
+ /* If one side is a PLUS and the other side is a pseudo that
+ didn't get a hard register but has a reg_equiv_constant,
+ we must replace the constant here since it may no longer
+ be in the position of any operand. */
+ if (GET_CODE (new0) == PLUS && GET_CODE (new1) == REG
+ && REGNO (new1) >= FIRST_PSEUDO_REGISTER
+ && reg_renumber[REGNO (new1)] < 0
+ && reg_equiv_constant != 0
+ && reg_equiv_constant[REGNO (new1)] != 0)
+ new1 = reg_equiv_constant[REGNO (new1)];
+ else if (GET_CODE (new1) == PLUS && GET_CODE (new0) == REG
+ && REGNO (new0) >= FIRST_PSEUDO_REGISTER
+ && reg_renumber[REGNO (new0)] < 0
+ && reg_equiv_constant[REGNO (new0)] != 0)
+ new0 = reg_equiv_constant[REGNO (new0)];
+
+ new = form_sum (new0, new1);
+
+ /* As above, if we are not inside a MEM we do not want to
+ turn a PLUS into something else. We might try to do so here
+ for an addition of 0 if we aren't optimizing. */
+ if (! mem_mode && GET_CODE (new) != PLUS)
+ return gen_rtx (PLUS, GET_MODE (x), new, const0_rtx);
+ else
+ return new;
+ }
+ }
+ return x;
+
+ case MULT:
+ /* If this is the product of an eliminable register and a
+ constant, apply the distribute law and move the constant out
+ so that we have (plus (mult ..) ..). This is needed in order
+ to keep load-address insns valid. This case is pathalogical.
+ We ignore the possibility of overflow here. */
+ if (GET_CODE (XEXP (x, 0)) == REG
+ && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER
+ && GET_CODE (XEXP (x, 1)) == CONST_INT)
+ for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS];
+ ep++)
+ if (ep->from_rtx == XEXP (x, 0) && ep->can_eliminate)
+ {
+ if (! mem_mode
+ /* Refs inside notes don't count for this purpose. */
+ && ! (insn != 0 && (GET_CODE (insn) == EXPR_LIST
+ || GET_CODE (insn) == INSN_LIST)))
+ ep->ref_outside_mem = 1;
+
+ return
+ plus_constant (gen_rtx (MULT, Pmode, ep->to_rtx, XEXP (x, 1)),
+ ep->previous_offset * INTVAL (XEXP (x, 1)));
+ }
+
+ /* ... fall through ... */
+
+ case CALL:
+ case COMPARE:
+ case MINUS:
+ case DIV: case UDIV:
+ case MOD: case UMOD:
+ case AND: case IOR: case XOR:
+ case ROTATERT: case ROTATE:
+ case ASHIFTRT: case LSHIFTRT: case ASHIFT:
+ case NE: case EQ:
+ case GE: case GT: case GEU: case GTU:
+ case LE: case LT: case LEU: case LTU:
+ {
+ rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, insn);
+ rtx new1
+ = XEXP (x, 1) ? eliminate_regs (XEXP (x, 1), mem_mode, insn) : 0;
+
+ if (new0 != XEXP (x, 0) || new1 != XEXP (x, 1))
+ return gen_rtx (code, GET_MODE (x), new0, new1);
+ }
+ return x;
+
+ case EXPR_LIST:
+ /* If we have something in XEXP (x, 0), the usual case, eliminate it. */
+ if (XEXP (x, 0))
+ {
+ new = eliminate_regs (XEXP (x, 0), mem_mode, insn);
+ if (new != XEXP (x, 0))
+ x = gen_rtx (EXPR_LIST, REG_NOTE_KIND (x), new, XEXP (x, 1));
+ }
+
+ /* ... fall through ... */
+
+ case INSN_LIST:
+ /* Now do eliminations in the rest of the chain. If this was
+ an EXPR_LIST, this might result in allocating more memory than is
+ strictly needed, but it simplifies the code. */
+ if (XEXP (x, 1))
+ {
+ new = eliminate_regs (XEXP (x, 1), mem_mode, insn);
+ if (new != XEXP (x, 1))
+ return gen_rtx (GET_CODE (x), GET_MODE (x), XEXP (x, 0), new);
+ }
+ return x;
+
+ case PRE_INC:
+ case POST_INC:
+ case PRE_DEC:
+ case POST_DEC:
+ for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+ if (ep->to_rtx == XEXP (x, 0))
+ {
+ int size = GET_MODE_SIZE (mem_mode);
+
+ /* If more bytes than MEM_MODE are pushed, account for them. */
+#ifdef PUSH_ROUNDING
+ if (ep->to_rtx == stack_pointer_rtx)
+ size = PUSH_ROUNDING (size);
+#endif
+ if (code == PRE_DEC || code == POST_DEC)
+ ep->offset += size;
+ else
+ ep->offset -= size;
+ }
+
+ /* Fall through to generic unary operation case. */
+ case USE:
+ case STRICT_LOW_PART:
+ case NEG: case NOT:
+ case SIGN_EXTEND: case ZERO_EXTEND:
+ case TRUNCATE: case FLOAT_EXTEND: case FLOAT_TRUNCATE:
+ case FLOAT: case FIX:
+ case UNSIGNED_FIX: case UNSIGNED_FLOAT:
+ case ABS:
+ case SQRT:
+ case FFS:
+ new = eliminate_regs (XEXP (x, 0), mem_mode, insn);
+ if (new != XEXP (x, 0))
+ return gen_rtx (code, GET_MODE (x), new);
+ return x;
+
+ case SUBREG:
+ /* Similar to above processing, but preserve SUBREG_WORD.
+ Convert (subreg (mem)) to (mem) if not paradoxical.
+ Also, if we have a non-paradoxical (subreg (pseudo)) and the
+ pseudo didn't get a hard reg, we must replace this with the
+ eliminated version of the memory location because push_reloads
+ may do the replacement in certain circumstances. */
+ if (GET_CODE (SUBREG_REG (x)) == REG
+ && (GET_MODE_SIZE (GET_MODE (x))
+ <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+ && reg_equiv_memory_loc != 0
+ && reg_equiv_memory_loc[REGNO (SUBREG_REG (x))] != 0)
+ {
+ new = eliminate_regs (reg_equiv_memory_loc[REGNO (SUBREG_REG (x))],
+ mem_mode, insn);
+
+ /* If we didn't change anything, we must retain the pseudo. */
+ if (new == reg_equiv_memory_loc[REGNO (SUBREG_REG (x))])
+ new = XEXP (x, 0);
+ else
+ /* Otherwise, ensure NEW isn't shared in case we have to reload
+ it. */
+ new = copy_rtx (new);
+ }
+ else
+ new = eliminate_regs (SUBREG_REG (x), mem_mode, insn);
+
+ if (new != XEXP (x, 0))
+ {
+ if (GET_CODE (new) == MEM
+ && (GET_MODE_SIZE (GET_MODE (x))
+ <= GET_MODE_SIZE (GET_MODE (new)))
+#ifdef LOAD_EXTEND_OP
+ /* On these machines we will be reloading what is
+ inside the SUBREG if it originally was a pseudo and
+ the inner and outer modes are both a word or
+ smaller. So leave the SUBREG then. */
+ && ! (GET_CODE (SUBREG_REG (x)) == REG
+ && GET_MODE_SIZE (GET_MODE (x)) <= UNITS_PER_WORD
+ && GET_MODE_SIZE (GET_MODE (new)) <= UNITS_PER_WORD)
+#endif
+ )
+ {
+ int offset = SUBREG_WORD (x) * UNITS_PER_WORD;
+ enum machine_mode mode = GET_MODE (x);
+
+#if BYTES_BIG_ENDIAN
+ offset += (MIN (UNITS_PER_WORD,
+ GET_MODE_SIZE (GET_MODE (new)))
+ - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)));
+#endif
+
+ PUT_MODE (new, mode);
+ XEXP (new, 0) = plus_constant (XEXP (new, 0), offset);
+ return new;
+ }
+ else
+ return gen_rtx (SUBREG, GET_MODE (x), new, SUBREG_WORD (x));
+ }
+
+ return x;
+
+ case CLOBBER:
+ /* If clobbering a register that is the replacement register for an
+ elimination we still think can be performed, note that it cannot
+ be performed. Otherwise, we need not be concerned about it. */
+ for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+ if (ep->to_rtx == XEXP (x, 0))
+ ep->can_eliminate = 0;
+
+ new = eliminate_regs (XEXP (x, 0), mem_mode, insn);
+ if (new != XEXP (x, 0))
+ return gen_rtx (code, GET_MODE (x), new);
+ return x;
+
+ case ASM_OPERANDS:
+ {
+ rtx *temp_vec;
+ /* Properly handle sharing input and constraint vectors. */
+ if (ASM_OPERANDS_INPUT_VEC (x) != old_asm_operands_vec)
+ {
+ /* When we come to a new vector not seen before,
+ scan all its elements; keep the old vector if none
+ of them changes; otherwise, make a copy. */
+ old_asm_operands_vec = ASM_OPERANDS_INPUT_VEC (x);
+ temp_vec = (rtx *) alloca (XVECLEN (x, 3) * sizeof (rtx));
+ for (i = 0; i < ASM_OPERANDS_INPUT_LENGTH (x); i++)
+ temp_vec[i] = eliminate_regs (ASM_OPERANDS_INPUT (x, i),
+ mem_mode, insn);
+
+ for (i = 0; i < ASM_OPERANDS_INPUT_LENGTH (x); i++)
+ if (temp_vec[i] != ASM_OPERANDS_INPUT (x, i))
+ break;
+
+ if (i == ASM_OPERANDS_INPUT_LENGTH (x))
+ new_asm_operands_vec = old_asm_operands_vec;
+ else
+ new_asm_operands_vec
+ = gen_rtvec_v (ASM_OPERANDS_INPUT_LENGTH (x), temp_vec);
+ }
+
+ /* If we had to copy the vector, copy the entire ASM_OPERANDS. */
+ if (new_asm_operands_vec == old_asm_operands_vec)
+ return x;
+
+ new = gen_rtx (ASM_OPERANDS, VOIDmode, ASM_OPERANDS_TEMPLATE (x),
+ ASM_OPERANDS_OUTPUT_CONSTRAINT (x),
+ ASM_OPERANDS_OUTPUT_IDX (x), new_asm_operands_vec,
+ ASM_OPERANDS_INPUT_CONSTRAINT_VEC (x),
+ ASM_OPERANDS_SOURCE_FILE (x),
+ ASM_OPERANDS_SOURCE_LINE (x));
+ new->volatil = x->volatil;
+ return new;
+ }
+
+ case SET:
+ /* Check for setting a register that we know about. */
+ if (GET_CODE (SET_DEST (x)) == REG)
+ {
+ /* See if this is setting the replacement register for an
+ elimination.
+
+ If DEST is the hard frame pointer, we do nothing because we
+ assume that all assignments to the frame pointer are for
+ non-local gotos and are being done at a time when they are valid
+ and do not disturb anything else. Some machines want to
+ eliminate a fake argument pointer (or even a fake frame pointer)
+ with either the real frame or the stack pointer. Assignments to
+ the hard frame pointer must not prevent this elimination. */
+
+ for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS];
+ ep++)
+ if (ep->to_rtx == SET_DEST (x)
+ && SET_DEST (x) != hard_frame_pointer_rtx)
+ {
+ /* If it is being incremented, adjust the offset. Otherwise,
+ this elimination can't be done. */
+ rtx src = SET_SRC (x);
+
+ if (GET_CODE (src) == PLUS
+ && XEXP (src, 0) == SET_DEST (x)
+ && GET_CODE (XEXP (src, 1)) == CONST_INT)
+ ep->offset -= INTVAL (XEXP (src, 1));
+ else
+ ep->can_eliminate = 0;
+ }
+
+ /* Now check to see we are assigning to a register that can be
+ eliminated. If so, it must be as part of a PARALLEL, since we
+ will not have been called if this is a single SET. So indicate
+ that we can no longer eliminate this reg. */
+ for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS];
+ ep++)
+ if (ep->from_rtx == SET_DEST (x) && ep->can_eliminate)
+ ep->can_eliminate = 0;
+ }
+
+ /* Now avoid the loop below in this common case. */
+ {
+ rtx new0 = eliminate_regs (SET_DEST (x), 0, insn);
+ rtx new1 = eliminate_regs (SET_SRC (x), 0, insn);
+
+ /* If SET_DEST changed from a REG to a MEM and INSN is an insn,
+ write a CLOBBER insn. */
+ if (GET_CODE (SET_DEST (x)) == REG && GET_CODE (new0) == MEM
+ && insn != 0 && GET_CODE (insn) != EXPR_LIST
+ && GET_CODE (insn) != INSN_LIST)
+ emit_insn_after (gen_rtx (CLOBBER, VOIDmode, SET_DEST (x)), insn);
+
+ if (new0 != SET_DEST (x) || new1 != SET_SRC (x))
+ return gen_rtx (SET, VOIDmode, new0, new1);
+ }
+
+ return x;
+
+ case MEM:
+ /* Our only special processing is to pass the mode of the MEM to our
+ recursive call and copy the flags. While we are here, handle this
+ case more efficiently. */
+ new = eliminate_regs (XEXP (x, 0), GET_MODE (x), insn);
+ if (new != XEXP (x, 0))
+ {
+ new = gen_rtx (MEM, GET_MODE (x), new);
+ new->volatil = x->volatil;
+ new->unchanging = x->unchanging;
+ new->in_struct = x->in_struct;
+ return new;
+ }
+ else
+ return x;
+ }
+
+ /* Process each of our operands recursively. If any have changed, make a
+ copy of the rtx. */
+ fmt = GET_RTX_FORMAT (code);
+ for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
+ {
+ if (*fmt == 'e')
+ {
+ new = eliminate_regs (XEXP (x, i), mem_mode, insn);
+ if (new != XEXP (x, i) && ! copied)
+ {
+ rtx new_x = rtx_alloc (code);
+ bcopy ((char *) x, (char *) new_x,
+ (sizeof (*new_x) - sizeof (new_x->fld)
+ + sizeof (new_x->fld[0]) * GET_RTX_LENGTH (code)));
+ x = new_x;
+ copied = 1;
+ }
+ XEXP (x, i) = new;
+ }
+ else if (*fmt == 'E')
+ {
+ int copied_vec = 0;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ {
+ new = eliminate_regs (XVECEXP (x, i, j), mem_mode, insn);
+ if (new != XVECEXP (x, i, j) && ! copied_vec)
+ {
+ rtvec new_v = gen_rtvec_v (XVECLEN (x, i),
+ &XVECEXP (x, i, 0));
+ if (! copied)
+ {
+ rtx new_x = rtx_alloc (code);
+ bcopy ((char *) x, (char *) new_x,
+ (sizeof (*new_x) - sizeof (new_x->fld)
+ + (sizeof (new_x->fld[0])
+ * GET_RTX_LENGTH (code))));
+ x = new_x;
+ copied = 1;
+ }
+ XVEC (x, i) = new_v;
+ copied_vec = 1;
+ }
+ XVECEXP (x, i, j) = new;
+ }
+ }
+ }
+
+ return x;
+}
+
+/* Scan INSN and eliminate all eliminable registers in it.
+
+ If REPLACE is nonzero, do the replacement destructively. Also
+ delete the insn as dead it if it is setting an eliminable register.
+
+ If REPLACE is zero, do all our allocations in reload_obstack.
+
+ If no eliminations were done and this insn doesn't require any elimination
+ processing (these are not identical conditions: it might be updating sp,
+ but not referencing fp; this needs to be seen during reload_as_needed so
+ that the offset between fp and sp can be taken into consideration), zero
+ is returned. Otherwise, 1 is returned. */
+
+static int
+eliminate_regs_in_insn (insn, replace)
+ rtx insn;
+ int replace;
+{
+ rtx old_body = PATTERN (insn);
+ rtx old_set = single_set (insn);
+ rtx new_body;
+ int val = 0;
+ struct elim_table *ep;
+
+ if (! replace)
+ push_obstacks (&reload_obstack, &reload_obstack);
+
+ if (old_set != 0 && GET_CODE (SET_DEST (old_set)) == REG
+ && REGNO (SET_DEST (old_set)) < FIRST_PSEUDO_REGISTER)
+ {
+ /* Check for setting an eliminable register. */
+ for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+ if (ep->from_rtx == SET_DEST (old_set) && ep->can_eliminate)
+ {
+#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ /* If this is setting the frame pointer register to the
+ hardware frame pointer register and this is an elimination
+ that will be done (tested above), this insn is really
+ adjusting the frame pointer downward to compensate for
+ the adjustment done before a nonlocal goto. */
+ if (ep->from == FRAME_POINTER_REGNUM
+ && ep->to == HARD_FRAME_POINTER_REGNUM)
+ {
+ rtx src = SET_SRC (old_set);
+ int offset, ok = 0;
+
+ if (src == ep->to_rtx)
+ offset = 0, ok = 1;
+ else if (GET_CODE (src) == PLUS
+ && GET_CODE (XEXP (src, 0)) == CONST_INT)
+ offset = INTVAL (XEXP (src, 0)), ok = 1;
+
+ if (ok)
+ {
+ if (replace)
+ {
+ rtx src
+ = plus_constant (ep->to_rtx, offset - ep->offset);
+
+ /* First see if this insn remains valid when we
+ make the change. If not, keep the INSN_CODE
+ the same and let reload fit it up. */
+ validate_change (insn, &SET_SRC (old_set), src, 1);
+ validate_change (insn, &SET_DEST (old_set),
+ ep->to_rtx, 1);
+ if (! apply_change_group ())
+ {
+ SET_SRC (old_set) = src;
+ SET_DEST (old_set) = ep->to_rtx;
+ }
+ }
+
+ val = 1;
+ goto done;
+ }
+ }
+#endif
+
+ /* In this case this insn isn't serving a useful purpose. We
+ will delete it in reload_as_needed once we know that this
+ elimination is, in fact, being done.
+
+ If REPLACE isn't set, we can't delete this insn, but neededn't
+ process it since it won't be used unless something changes. */
+ if (replace)
+ delete_dead_insn (insn);
+ val = 1;
+ goto done;
+ }
+
+ /* Check for (set (reg) (plus (reg from) (offset))) where the offset
+ in the insn is the negative of the offset in FROM. Substitute
+ (set (reg) (reg to)) for the insn and change its code.
+
+ We have to do this here, rather than in eliminate_regs, do that we can
+ change the insn code. */
+
+ if (GET_CODE (SET_SRC (old_set)) == PLUS
+ && GET_CODE (XEXP (SET_SRC (old_set), 0)) == REG
+ && GET_CODE (XEXP (SET_SRC (old_set), 1)) == CONST_INT)
+ for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS];
+ ep++)
+ if (ep->from_rtx == XEXP (SET_SRC (old_set), 0)
+ && ep->can_eliminate)
+ {
+ /* We must stop at the first elimination that will be used.
+ If this one would replace the PLUS with a REG, do it
+ now. Otherwise, quit the loop and let eliminate_regs
+ do its normal replacement. */
+ if (ep->offset == - INTVAL (XEXP (SET_SRC (old_set), 1)))
+ {
+ /* We assume here that we don't need a PARALLEL of
+ any CLOBBERs for this assignment. There's not
+ much we can do if we do need it. */
+ PATTERN (insn) = gen_rtx (SET, VOIDmode,
+ SET_DEST (old_set), ep->to_rtx);
+ INSN_CODE (insn) = -1;
+ val = 1;
+ goto done;
+ }
+
+ break;
+ }
+ }
+
+ old_asm_operands_vec = 0;
+
+ /* Replace the body of this insn with a substituted form. If we changed
+ something, return non-zero.
+
+ If we are replacing a body that was a (set X (plus Y Z)), try to
+ re-recognize the insn. We do this in case we had a simple addition
+ but now can do this as a load-address. This saves an insn in this
+ common case. */
+
+ new_body = eliminate_regs (old_body, 0, replace ? insn : NULL_RTX);
+ if (new_body != old_body)
+ {
+ /* If we aren't replacing things permanently and we changed something,
+ make another copy to ensure that all the RTL is new. Otherwise
+ things can go wrong if find_reload swaps commutative operands
+ and one is inside RTL that has been copied while the other is not. */
+
+ /* Don't copy an asm_operands because (1) there's no need and (2)
+ copy_rtx can't do it properly when there are multiple outputs. */
+ if (! replace && asm_noperands (old_body) < 0)
+ new_body = copy_rtx (new_body);
+
+ /* If we had a move insn but now we don't, rerecognize it. This will
+ cause spurious re-recognition if the old move had a PARALLEL since
+ the new one still will, but we can't call single_set without
+ having put NEW_BODY into the insn and the re-recognition won't
+ hurt in this rare case. */
+ if (old_set != 0
+ && ((GET_CODE (SET_SRC (old_set)) == REG
+ && (GET_CODE (new_body) != SET
+ || GET_CODE (SET_SRC (new_body)) != REG))
+ /* If this was a load from or store to memory, compare
+ the MEM in recog_operand to the one in the insn. If they
+ are not equal, then rerecognize the insn. */
+ || (old_set != 0
+ && ((GET_CODE (SET_SRC (old_set)) == MEM
+ && SET_SRC (old_set) != recog_operand[1])
+ || (GET_CODE (SET_DEST (old_set)) == MEM
+ && SET_DEST (old_set) != recog_operand[0])))
+ /* If this was an add insn before, rerecognize. */
+ || GET_CODE (SET_SRC (old_set)) == PLUS))
+ {
+ if (! validate_change (insn, &PATTERN (insn), new_body, 0))
+ /* If recognition fails, store the new body anyway.
+ It's normal to have recognition failures here
+ due to bizarre memory addresses; reloading will fix them. */
+ PATTERN (insn) = new_body;
+ }
+ else
+ PATTERN (insn) = new_body;
+
+ val = 1;
+ }
+
+ /* Loop through all elimination pairs. See if any have changed and
+ recalculate the number not at initial offset.
+
+ Compute the maximum offset (minimum offset if the stack does not
+ grow downward) for each elimination pair.
+
+ We also detect a cases where register elimination cannot be done,
+ namely, if a register would be both changed and referenced outside a MEM
+ in the resulting insn since such an insn is often undefined and, even if
+ not, we cannot know what meaning will be given to it. Note that it is
+ valid to have a register used in an address in an insn that changes it
+ (presumably with a pre- or post-increment or decrement).
+
+ If anything changes, return nonzero. */
+
+ num_not_at_initial_offset = 0;
+ for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+ {
+ if (ep->previous_offset != ep->offset && ep->ref_outside_mem)
+ ep->can_eliminate = 0;
+
+ ep->ref_outside_mem = 0;
+
+ if (ep->previous_offset != ep->offset)
+ val = 1;
+
+ ep->previous_offset = ep->offset;
+ if (ep->can_eliminate && ep->offset != ep->initial_offset)
+ num_not_at_initial_offset++;
+
+#ifdef STACK_GROWS_DOWNWARD
+ ep->max_offset = MAX (ep->max_offset, ep->offset);
+#else
+ ep->max_offset = MIN (ep->max_offset, ep->offset);
+#endif
+ }
+
+ done:
+ /* If we changed something, perform elmination in REG_NOTES. This is
+ needed even when REPLACE is zero because a REG_DEAD note might refer
+ to a register that we eliminate and could cause a different number
+ of spill registers to be needed in the final reload pass than in
+ the pre-passes. */
+ if (val && REG_NOTES (insn) != 0)
+ REG_NOTES (insn) = eliminate_regs (REG_NOTES (insn), 0, REG_NOTES (insn));
+
+ if (! replace)
+ pop_obstacks ();
+
+ return val;
+}
+
+/* Given X, a SET or CLOBBER of DEST, if DEST is the target of a register
+ replacement we currently believe is valid, mark it as not eliminable if X
+ modifies DEST in any way other than by adding a constant integer to it.
+
+ If DEST is the frame pointer, we do nothing because we assume that
+ all assignments to the hard frame pointer are nonlocal gotos and are being
+ done at a time when they are valid and do not disturb anything else.
+ Some machines want to eliminate a fake argument pointer with either the
+ frame or stack pointer. Assignments to the hard frame pointer must not
+ prevent this elimination.
+
+ Called via note_stores from reload before starting its passes to scan
+ the insns of the function. */
+
+static void
+mark_not_eliminable (dest, x)
+ rtx dest;
+ rtx x;
+{
+ register int i;
+
+ /* A SUBREG of a hard register here is just changing its mode. We should
+ not see a SUBREG of an eliminable hard register, but check just in
+ case. */
+ if (GET_CODE (dest) == SUBREG)
+ dest = SUBREG_REG (dest);
+
+ if (dest == hard_frame_pointer_rtx)
+ return;
+
+ for (i = 0; i < NUM_ELIMINABLE_REGS; i++)
+ if (reg_eliminate[i].can_eliminate && dest == reg_eliminate[i].to_rtx
+ && (GET_CODE (x) != SET
+ || GET_CODE (SET_SRC (x)) != PLUS
+ || XEXP (SET_SRC (x), 0) != dest
+ || GET_CODE (XEXP (SET_SRC (x), 1)) != CONST_INT))
+ {
+ reg_eliminate[i].can_eliminate_previous
+ = reg_eliminate[i].can_eliminate = 0;
+ num_eliminable--;
+ }
+}
+
+/* Kick all pseudos out of hard register REGNO.
+ If GLOBAL is nonzero, try to find someplace else to put them.
+ If DUMPFILE is nonzero, log actions taken on that file.
+
+ If CANT_ELIMINATE is nonzero, it means that we are doing this spill
+ because we found we can't eliminate some register. In the case, no pseudos
+ are allowed to be in the register, even if they are only in a block that
+ doesn't require spill registers, unlike the case when we are spilling this
+ hard reg to produce another spill register.
+
+ Return nonzero if any pseudos needed to be kicked out. */
+
+static int
+spill_hard_reg (regno, global, dumpfile, cant_eliminate)
+ register int regno;
+ int global;
+ FILE *dumpfile;
+ int cant_eliminate;
+{
+ enum reg_class class = REGNO_REG_CLASS (regno);
+ int something_changed = 0;
+ register int i;
+
+ SET_HARD_REG_BIT (forbidden_regs, regno);
+
+ if (cant_eliminate)
+ regs_ever_live[regno] = 1;
+
+ /* Spill every pseudo reg that was allocated to this reg
+ or to something that overlaps this reg. */
+
+ for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+ if (reg_renumber[i] >= 0
+ && reg_renumber[i] <= regno
+ && (reg_renumber[i]
+ + HARD_REGNO_NREGS (reg_renumber[i],
+ PSEUDO_REGNO_MODE (i))
+ > regno))
+ {
+ /* If this register belongs solely to a basic block which needed no
+ spilling of any class that this register is contained in,
+ leave it be, unless we are spilling this register because
+ it was a hard register that can't be eliminated. */
+
+ if (! cant_eliminate
+ && basic_block_needs[0]
+ && reg_basic_block[i] >= 0
+ && basic_block_needs[(int) class][reg_basic_block[i]] == 0)
+ {
+ enum reg_class *p;
+
+ for (p = reg_class_superclasses[(int) class];
+ *p != LIM_REG_CLASSES; p++)
+ if (basic_block_needs[(int) *p][reg_basic_block[i]] > 0)
+ break;
+
+ if (*p == LIM_REG_CLASSES)
+ continue;
+ }
+
+ /* Mark it as no longer having a hard register home. */
+ reg_renumber[i] = -1;
+ /* We will need to scan everything again. */
+ something_changed = 1;
+ if (global)
+ retry_global_alloc (i, forbidden_regs);
+
+ alter_reg (i, regno);
+ if (dumpfile)
+ {
+ if (reg_renumber[i] == -1)
+ fprintf (dumpfile, " Register %d now on stack.\n\n", i);
+ else
+ fprintf (dumpfile, " Register %d now in %d.\n\n",
+ i, reg_renumber[i]);
+ }
+ }
+ for (i = 0; i < scratch_list_length; i++)
+ {
+ if (scratch_list[i] && REGNO (scratch_list[i]) == regno)
+ {
+ if (! cant_eliminate && basic_block_needs[0]
+ && ! basic_block_needs[(int) class][scratch_block[i]])
+ {
+ enum reg_class *p;
+
+ for (p = reg_class_superclasses[(int) class];
+ *p != LIM_REG_CLASSES; p++)
+ if (basic_block_needs[(int) *p][scratch_block[i]] > 0)
+ break;
+
+ if (*p == LIM_REG_CLASSES)
+ continue;
+ }
+ PUT_CODE (scratch_list[i], SCRATCH);
+ scratch_list[i] = 0;
+ something_changed = 1;
+ continue;
+ }
+ }
+
+ return something_changed;
+}
+
+/* Find all paradoxical subregs within X and update reg_max_ref_width.
+ Also mark any hard registers used to store user variables as
+ forbidden from being used for spill registers. */
+
+static void
+scan_paradoxical_subregs (x)
+ register rtx x;
+{
+ register int i;
+ register char *fmt;
+ register enum rtx_code code = GET_CODE (x);
+
+ switch (code)
+ {
+ case REG:
+#ifdef SMALL_REGISTER_CLASSES
+ if (REGNO (x) < FIRST_PSEUDO_REGISTER && REG_USERVAR_P (x))
+ SET_HARD_REG_BIT (forbidden_regs, REGNO (x));
+#endif
+ return;
+
+ case CONST_INT:
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case CONST_DOUBLE:
+ case CC0:
+ case PC:
+ case USE:
+ case CLOBBER:
+ return;
+
+ case SUBREG:
+ if (GET_CODE (SUBREG_REG (x)) == REG
+ && GET_MODE_SIZE (GET_MODE (x)) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+ reg_max_ref_width[REGNO (SUBREG_REG (x))]
+ = GET_MODE_SIZE (GET_MODE (x));
+ return;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ scan_paradoxical_subregs (XEXP (x, i));
+ else if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = XVECLEN (x, i) - 1; j >=0; j--)
+ scan_paradoxical_subregs (XVECEXP (x, i, j));
+ }
+ }
+}
+
+static int
+hard_reg_use_compare (p1, p2)
+ struct hard_reg_n_uses *p1, *p2;
+{
+ int tem = p1->uses - p2->uses;
+ if (tem != 0) return tem;
+ /* If regs are equally good, sort by regno,
+ so that the results of qsort leave nothing to chance. */
+ return p1->regno - p2->regno;
+}
+
+/* Choose the order to consider regs for use as reload registers
+ based on how much trouble would be caused by spilling one.
+ Store them in order of decreasing preference in potential_reload_regs. */
+
+static void
+order_regs_for_reload ()
+{
+ register int i;
+ register int o = 0;
+ int large = 0;
+
+ struct hard_reg_n_uses hard_reg_n_uses[FIRST_PSEUDO_REGISTER];
+
+ CLEAR_HARD_REG_SET (bad_spill_regs);
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ potential_reload_regs[i] = -1;
+
+ /* Count number of uses of each hard reg by pseudo regs allocated to it
+ and then order them by decreasing use. */
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ hard_reg_n_uses[i].uses = 0;
+ hard_reg_n_uses[i].regno = i;
+ }
+
+ for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+ {
+ int regno = reg_renumber[i];
+ if (regno >= 0)
+ {
+ int lim = regno + HARD_REGNO_NREGS (regno, PSEUDO_REGNO_MODE (i));
+ while (regno < lim)
+ hard_reg_n_uses[regno++].uses += reg_n_refs[i];
+ }
+ large += reg_n_refs[i];
+ }
+
+ /* Now fixed registers (which cannot safely be used for reloading)
+ get a very high use count so they will be considered least desirable.
+ Registers used explicitly in the rtl code are almost as bad. */
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ if (fixed_regs[i])
+ {
+ hard_reg_n_uses[i].uses += 2 * large + 2;
+ SET_HARD_REG_BIT (bad_spill_regs, i);
+ }
+ else if (regs_explicitly_used[i])
+ {
+ hard_reg_n_uses[i].uses += large + 1;
+#ifndef SMALL_REGISTER_CLASSES
+ /* ??? We are doing this here because of the potential that
+ bad code may be generated if a register explicitly used in
+ an insn was used as a spill register for that insn. But
+ not using these are spill registers may lose on some machine.
+ We'll have to see how this works out. */
+ SET_HARD_REG_BIT (bad_spill_regs, i);
+#endif
+ }
+ }
+ hard_reg_n_uses[HARD_FRAME_POINTER_REGNUM].uses += 2 * large + 2;
+ SET_HARD_REG_BIT (bad_spill_regs, HARD_FRAME_POINTER_REGNUM);
+
+#ifdef ELIMINABLE_REGS
+ /* If registers other than the frame pointer are eliminable, mark them as
+ poor choices. */
+ for (i = 0; i < NUM_ELIMINABLE_REGS; i++)
+ {
+ hard_reg_n_uses[reg_eliminate[i].from].uses += 2 * large + 2;
+ SET_HARD_REG_BIT (bad_spill_regs, reg_eliminate[i].from);
+ }
+#endif
+
+ /* Prefer registers not so far used, for use in temporary loading.
+ Among them, if REG_ALLOC_ORDER is defined, use that order.
+ Otherwise, prefer registers not preserved by calls. */
+
+#ifdef REG_ALLOC_ORDER
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ int regno = reg_alloc_order[i];
+
+ if (hard_reg_n_uses[regno].uses == 0)
+ potential_reload_regs[o++] = regno;
+ }
+#else
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ if (hard_reg_n_uses[i].uses == 0 && call_used_regs[i])
+ potential_reload_regs[o++] = i;
+ }
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ if (hard_reg_n_uses[i].uses == 0 && ! call_used_regs[i])
+ potential_reload_regs[o++] = i;
+ }
+#endif
+
+ qsort (hard_reg_n_uses, FIRST_PSEUDO_REGISTER,
+ sizeof hard_reg_n_uses[0], hard_reg_use_compare);
+
+ /* Now add the regs that are already used,
+ preferring those used less often. The fixed and otherwise forbidden
+ registers will be at the end of this list. */
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (hard_reg_n_uses[i].uses != 0)
+ potential_reload_regs[o++] = hard_reg_n_uses[i].regno;
+}
+
+/* Used in reload_as_needed to sort the spilled regs. */
+static int
+compare_spill_regs (r1, r2)
+ short *r1, *r2;
+{
+ return *r1 < *r2 ? -1: 1;
+}
+
+/* Reload pseudo-registers into hard regs around each insn as needed.
+ Additional register load insns are output before the insn that needs it
+ and perhaps store insns after insns that modify the reloaded pseudo reg.
+
+ reg_last_reload_reg and reg_reloaded_contents keep track of
+ which registers are already available in reload registers.
+ We update these for the reloads that we perform,
+ as the insns are scanned. */
+
+static void
+reload_as_needed (first, live_known)
+ rtx first;
+ int live_known;
+{
+ register rtx insn;
+ register int i;
+ int this_block = 0;
+ rtx x;
+ rtx after_call = 0;
+
+ bzero ((char *) spill_reg_rtx, sizeof spill_reg_rtx);
+ bzero ((char *) spill_reg_store, sizeof spill_reg_store);
+ reg_last_reload_reg = (rtx *) alloca (max_regno * sizeof (rtx));
+ bzero ((char *) reg_last_reload_reg, max_regno * sizeof (rtx));
+ reg_has_output_reload = (char *) alloca (max_regno);
+ for (i = 0; i < n_spills; i++)
+ {
+ reg_reloaded_contents[i] = -1;
+ reg_reloaded_insn[i] = 0;
+ }
+
+ /* Reset all offsets on eliminable registers to their initial values. */
+#ifdef ELIMINABLE_REGS
+ for (i = 0; i < NUM_ELIMINABLE_REGS; i++)
+ {
+ INITIAL_ELIMINATION_OFFSET (reg_eliminate[i].from, reg_eliminate[i].to,
+ reg_eliminate[i].initial_offset);
+ reg_eliminate[i].previous_offset
+ = reg_eliminate[i].offset = reg_eliminate[i].initial_offset;
+ }
+#else
+ INITIAL_FRAME_POINTER_OFFSET (reg_eliminate[0].initial_offset);
+ reg_eliminate[0].previous_offset
+ = reg_eliminate[0].offset = reg_eliminate[0].initial_offset;
+#endif
+
+ num_not_at_initial_offset = 0;
+
+ /* Order the spilled regs, so that allocate_reload_regs can guarantee to
+ pack registers with group needs. */
+ if (n_spills > 1)
+ {
+ qsort (spill_regs, n_spills, sizeof (short), compare_spill_regs);
+ for (i = 0; i < n_spills; i++)
+ spill_reg_order[spill_regs[i]] = i;
+ }
+
+ for (insn = first; insn;)
+ {
+ register rtx next = NEXT_INSN (insn);
+
+ /* Notice when we move to a new basic block. */
+ if (live_known && this_block + 1 < n_basic_blocks
+ && insn == basic_block_head[this_block+1])
+ ++this_block;
+
+ /* If we pass a label, copy the offsets from the label information
+ into the current offsets of each elimination. */
+ if (GET_CODE (insn) == CODE_LABEL)
+ {
+ num_not_at_initial_offset = 0;
+ for (i = 0; i < NUM_ELIMINABLE_REGS; i++)
+ {
+ reg_eliminate[i].offset = reg_eliminate[i].previous_offset
+ = offsets_at[CODE_LABEL_NUMBER (insn)][i];
+ if (reg_eliminate[i].can_eliminate
+ && (reg_eliminate[i].offset
+ != reg_eliminate[i].initial_offset))
+ num_not_at_initial_offset++;
+ }
+ }
+
+ else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ rtx avoid_return_reg = 0;
+
+#ifdef SMALL_REGISTER_CLASSES
+ /* Set avoid_return_reg if this is an insn
+ that might use the value of a function call. */
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ if (GET_CODE (PATTERN (insn)) == SET)
+ after_call = SET_DEST (PATTERN (insn));
+ else if (GET_CODE (PATTERN (insn)) == PARALLEL
+ && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET)
+ after_call = SET_DEST (XVECEXP (PATTERN (insn), 0, 0));
+ else
+ after_call = 0;
+ }
+ else if (after_call != 0
+ && !(GET_CODE (PATTERN (insn)) == SET
+ && SET_DEST (PATTERN (insn)) == stack_pointer_rtx))
+ {
+ if (reg_referenced_p (after_call, PATTERN (insn)))
+ avoid_return_reg = after_call;
+ after_call = 0;
+ }
+#endif /* SMALL_REGISTER_CLASSES */
+
+ /* If this is a USE and CLOBBER of a MEM, ensure that any
+ references to eliminable registers have been removed. */
+
+ if ((GET_CODE (PATTERN (insn)) == USE
+ || GET_CODE (PATTERN (insn)) == CLOBBER)
+ && GET_CODE (XEXP (PATTERN (insn), 0)) == MEM)
+ XEXP (XEXP (PATTERN (insn), 0), 0)
+ = eliminate_regs (XEXP (XEXP (PATTERN (insn), 0), 0),
+ GET_MODE (XEXP (PATTERN (insn), 0)), NULL_RTX);
+
+ /* If we need to do register elimination processing, do so.
+ This might delete the insn, in which case we are done. */
+ if (num_eliminable && GET_MODE (insn) == QImode)
+ {
+ eliminate_regs_in_insn (insn, 1);
+ if (GET_CODE (insn) == NOTE)
+ {
+ insn = next;
+ continue;
+ }
+ }
+
+ if (GET_MODE (insn) == VOIDmode)
+ n_reloads = 0;
+ /* First find the pseudo regs that must be reloaded for this insn.
+ This info is returned in the tables reload_... (see reload.h).
+ Also modify the body of INSN by substituting RELOAD
+ rtx's for those pseudo regs. */
+ else
+ {
+ bzero (reg_has_output_reload, max_regno);
+ CLEAR_HARD_REG_SET (reg_is_output_reload);
+
+ find_reloads (insn, 1, spill_indirect_levels, live_known,
+ spill_reg_order);
+ }
+
+ if (n_reloads > 0)
+ {
+ rtx prev = PREV_INSN (insn), next = NEXT_INSN (insn);
+ rtx p;
+ int class;
+
+ /* If this block has not had spilling done for a
+ particular clas and we have any non-optionals that need a
+ spill reg in that class, abort. */
+
+ for (class = 0; class < N_REG_CLASSES; class++)
+ if (basic_block_needs[class] != 0
+ && basic_block_needs[class][this_block] == 0)
+ for (i = 0; i < n_reloads; i++)
+ if (class == (int) reload_reg_class[i]
+ && reload_reg_rtx[i] == 0
+ && ! reload_optional[i]
+ && (reload_in[i] != 0 || reload_out[i] != 0
+ || reload_secondary_p[i] != 0))
+ abort ();
+
+ /* Now compute which reload regs to reload them into. Perhaps
+ reusing reload regs from previous insns, or else output
+ load insns to reload them. Maybe output store insns too.
+ Record the choices of reload reg in reload_reg_rtx. */
+ choose_reload_regs (insn, avoid_return_reg);
+
+#ifdef SMALL_REGISTER_CLASSES
+ /* Merge any reloads that we didn't combine for fear of
+ increasing the number of spill registers needed but now
+ discover can be safely merged. */
+ merge_assigned_reloads (insn);
+#endif
+
+ /* Generate the insns to reload operands into or out of
+ their reload regs. */
+ emit_reload_insns (insn);
+
+ /* Substitute the chosen reload regs from reload_reg_rtx
+ into the insn's body (or perhaps into the bodies of other
+ load and store insn that we just made for reloading
+ and that we moved the structure into). */
+ subst_reloads ();
+
+ /* If this was an ASM, make sure that all the reload insns
+ we have generated are valid. If not, give an error
+ and delete them. */
+
+ if (asm_noperands (PATTERN (insn)) >= 0)
+ for (p = NEXT_INSN (prev); p != next; p = NEXT_INSN (p))
+ if (p != insn && GET_RTX_CLASS (GET_CODE (p)) == 'i'
+ && (recog_memoized (p) < 0
+ || (insn_extract (p),
+ ! constrain_operands (INSN_CODE (p), 1))))
+ {
+ error_for_asm (insn,
+ "`asm' operand requires impossible reload");
+ PUT_CODE (p, NOTE);
+ NOTE_SOURCE_FILE (p) = 0;
+ NOTE_LINE_NUMBER (p) = NOTE_INSN_DELETED;
+ }
+ }
+ /* Any previously reloaded spilled pseudo reg, stored in this insn,
+ is no longer validly lying around to save a future reload.
+ Note that this does not detect pseudos that were reloaded
+ for this insn in order to be stored in
+ (obeying register constraints). That is correct; such reload
+ registers ARE still valid. */
+ note_stores (PATTERN (insn), forget_old_reloads_1);
+
+ /* There may have been CLOBBER insns placed after INSN. So scan
+ between INSN and NEXT and use them to forget old reloads. */
+ for (x = NEXT_INSN (insn); x != next; x = NEXT_INSN (x))
+ if (GET_CODE (x) == INSN && GET_CODE (PATTERN (x)) == CLOBBER)
+ note_stores (PATTERN (x), forget_old_reloads_1);
+
+#ifdef AUTO_INC_DEC
+ /* Likewise for regs altered by auto-increment in this insn.
+ But note that the reg-notes are not changed by reloading:
+ they still contain the pseudo-regs, not the spill regs. */
+ for (x = REG_NOTES (insn); x; x = XEXP (x, 1))
+ if (REG_NOTE_KIND (x) == REG_INC)
+ {
+ /* See if this pseudo reg was reloaded in this insn.
+ If so, its last-reload info is still valid
+ because it is based on this insn's reload. */
+ for (i = 0; i < n_reloads; i++)
+ if (reload_out[i] == XEXP (x, 0))
+ break;
+
+ if (i == n_reloads)
+ forget_old_reloads_1 (XEXP (x, 0), NULL_RTX);
+ }
+#endif
+ }
+ /* A reload reg's contents are unknown after a label. */
+ if (GET_CODE (insn) == CODE_LABEL)
+ for (i = 0; i < n_spills; i++)
+ {
+ reg_reloaded_contents[i] = -1;
+ reg_reloaded_insn[i] = 0;
+ }
+
+ /* Don't assume a reload reg is still good after a call insn
+ if it is a call-used reg. */
+ else if (GET_CODE (insn) == CALL_INSN)
+ for (i = 0; i < n_spills; i++)
+ if (call_used_regs[spill_regs[i]])
+ {
+ reg_reloaded_contents[i] = -1;
+ reg_reloaded_insn[i] = 0;
+ }
+
+ /* In case registers overlap, allow certain insns to invalidate
+ particular hard registers. */
+
+#ifdef INSN_CLOBBERS_REGNO_P
+ for (i = 0 ; i < n_spills ; i++)
+ if (INSN_CLOBBERS_REGNO_P (insn, spill_regs[i]))
+ {
+ reg_reloaded_contents[i] = -1;
+ reg_reloaded_insn[i] = 0;
+ }
+#endif
+
+ insn = next;
+
+#ifdef USE_C_ALLOCA
+ alloca (0);
+#endif
+ }
+}
+
+/* Discard all record of any value reloaded from X,
+ or reloaded in X from someplace else;
+ unless X is an output reload reg of the current insn.
+
+ X may be a hard reg (the reload reg)
+ or it may be a pseudo reg that was reloaded from. */
+
+static void
+forget_old_reloads_1 (x, ignored)
+ rtx x;
+ rtx ignored;
+{
+ register int regno;
+ int nr;
+ int offset = 0;
+
+ /* note_stores does give us subregs of hard regs. */
+ while (GET_CODE (x) == SUBREG)
+ {
+ offset += SUBREG_WORD (x);
+ x = SUBREG_REG (x);
+ }
+
+ if (GET_CODE (x) != REG)
+ return;
+
+ regno = REGNO (x) + offset;
+
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ nr = 1;
+ else
+ {
+ int i;
+ nr = HARD_REGNO_NREGS (regno, GET_MODE (x));
+ /* Storing into a spilled-reg invalidates its contents.
+ This can happen if a block-local pseudo is allocated to that reg
+ and it wasn't spilled because this block's total need is 0.
+ Then some insn might have an optional reload and use this reg. */
+ for (i = 0; i < nr; i++)
+ if (spill_reg_order[regno + i] >= 0
+ /* But don't do this if the reg actually serves as an output
+ reload reg in the current instruction. */
+ && (n_reloads == 0
+ || ! TEST_HARD_REG_BIT (reg_is_output_reload, regno + i)))
+ {
+ reg_reloaded_contents[spill_reg_order[regno + i]] = -1;
+ reg_reloaded_insn[spill_reg_order[regno + i]] = 0;
+ }
+ }
+
+ /* Since value of X has changed,
+ forget any value previously copied from it. */
+
+ while (nr-- > 0)
+ /* But don't forget a copy if this is the output reload
+ that establishes the copy's validity. */
+ if (n_reloads == 0 || reg_has_output_reload[regno + nr] == 0)
+ reg_last_reload_reg[regno + nr] = 0;
+}
+
+/* For each reload, the mode of the reload register. */
+static enum machine_mode reload_mode[MAX_RELOADS];
+
+/* For each reload, the largest number of registers it will require. */
+static int reload_nregs[MAX_RELOADS];
+
+/* Comparison function for qsort to decide which of two reloads
+ should be handled first. *P1 and *P2 are the reload numbers. */
+
+static int
+reload_reg_class_lower (p1, p2)
+ short *p1, *p2;
+{
+ register int r1 = *p1, r2 = *p2;
+ register int t;
+
+ /* Consider required reloads before optional ones. */
+ t = reload_optional[r1] - reload_optional[r2];
+ if (t != 0)
+ return t;
+
+ /* Count all solitary classes before non-solitary ones. */
+ t = ((reg_class_size[(int) reload_reg_class[r2]] == 1)
+ - (reg_class_size[(int) reload_reg_class[r1]] == 1));
+ if (t != 0)
+ return t;
+
+ /* Aside from solitaires, consider all multi-reg groups first. */
+ t = reload_nregs[r2] - reload_nregs[r1];
+ if (t != 0)
+ return t;
+
+ /* Consider reloads in order of increasing reg-class number. */
+ t = (int) reload_reg_class[r1] - (int) reload_reg_class[r2];
+ if (t != 0)
+ return t;
+
+ /* If reloads are equally urgent, sort by reload number,
+ so that the results of qsort leave nothing to chance. */
+ return r1 - r2;
+}
+
+/* The following HARD_REG_SETs indicate when each hard register is
+ used for a reload of various parts of the current insn. */
+
+/* If reg is in use as a reload reg for a RELOAD_OTHER reload. */
+static HARD_REG_SET reload_reg_used;
+/* If reg is in use for a RELOAD_FOR_INPUT_ADDRESS reload for operand I. */
+static HARD_REG_SET reload_reg_used_in_input_addr[MAX_RECOG_OPERANDS];
+/* If reg is in use for a RELOAD_FOR_OUTPUT_ADDRESS reload for operand I. */
+static HARD_REG_SET reload_reg_used_in_output_addr[MAX_RECOG_OPERANDS];
+/* If reg is in use for a RELOAD_FOR_INPUT reload for operand I. */
+static HARD_REG_SET reload_reg_used_in_input[MAX_RECOG_OPERANDS];
+/* If reg is in use for a RELOAD_FOR_OUTPUT reload for operand I. */
+static HARD_REG_SET reload_reg_used_in_output[MAX_RECOG_OPERANDS];
+/* If reg is in use for a RELOAD_FOR_OPERAND_ADDRESS reload. */
+static HARD_REG_SET reload_reg_used_in_op_addr;
+/* If reg is in use for a RELOAD_FOR_OPADDR_ADDR reload. */
+static HARD_REG_SET reload_reg_used_in_op_addr_reload;
+/* If reg is in use for a RELOAD_FOR_INSN reload. */
+static HARD_REG_SET reload_reg_used_in_insn;
+/* If reg is in use for a RELOAD_FOR_OTHER_ADDRESS reload. */
+static HARD_REG_SET reload_reg_used_in_other_addr;
+
+/* If reg is in use as a reload reg for any sort of reload. */
+static HARD_REG_SET reload_reg_used_at_all;
+
+/* If reg is use as an inherited reload. We just mark the first register
+ in the group. */
+static HARD_REG_SET reload_reg_used_for_inherit;
+
+/* Mark reg REGNO as in use for a reload of the sort spec'd by OPNUM and
+ TYPE. MODE is used to indicate how many consecutive regs are
+ actually used. */
+
+static void
+mark_reload_reg_in_use (regno, opnum, type, mode)
+ int regno;
+ int opnum;
+ enum reload_type type;
+ enum machine_mode mode;
+{
+ int nregs = HARD_REGNO_NREGS (regno, mode);
+ int i;
+
+ for (i = regno; i < nregs + regno; i++)
+ {
+ switch (type)
+ {
+ case RELOAD_OTHER:
+ SET_HARD_REG_BIT (reload_reg_used, i);
+ break;
+
+ case RELOAD_FOR_INPUT_ADDRESS:
+ SET_HARD_REG_BIT (reload_reg_used_in_input_addr[opnum], i);
+ break;
+
+ case RELOAD_FOR_OUTPUT_ADDRESS:
+ SET_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], i);
+ break;
+
+ case RELOAD_FOR_OPERAND_ADDRESS:
+ SET_HARD_REG_BIT (reload_reg_used_in_op_addr, i);
+ break;
+
+ case RELOAD_FOR_OPADDR_ADDR:
+ SET_HARD_REG_BIT (reload_reg_used_in_op_addr_reload, i);
+ break;
+
+ case RELOAD_FOR_OTHER_ADDRESS:
+ SET_HARD_REG_BIT (reload_reg_used_in_other_addr, i);
+ break;
+
+ case RELOAD_FOR_INPUT:
+ SET_HARD_REG_BIT (reload_reg_used_in_input[opnum], i);
+ break;
+
+ case RELOAD_FOR_OUTPUT:
+ SET_HARD_REG_BIT (reload_reg_used_in_output[opnum], i);
+ break;
+
+ case RELOAD_FOR_INSN:
+ SET_HARD_REG_BIT (reload_reg_used_in_insn, i);
+ break;
+ }
+
+ SET_HARD_REG_BIT (reload_reg_used_at_all, i);
+ }
+}
+
+/* Similarly, but show REGNO is no longer in use for a reload. */
+
+static void
+clear_reload_reg_in_use (regno, opnum, type, mode)
+ int regno;
+ int opnum;
+ enum reload_type type;
+ enum machine_mode mode;
+{
+ int nregs = HARD_REGNO_NREGS (regno, mode);
+ int i;
+
+ for (i = regno; i < nregs + regno; i++)
+ {
+ switch (type)
+ {
+ case RELOAD_OTHER:
+ CLEAR_HARD_REG_BIT (reload_reg_used, i);
+ break;
+
+ case RELOAD_FOR_INPUT_ADDRESS:
+ CLEAR_HARD_REG_BIT (reload_reg_used_in_input_addr[opnum], i);
+ break;
+
+ case RELOAD_FOR_OUTPUT_ADDRESS:
+ CLEAR_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], i);
+ break;
+
+ case RELOAD_FOR_OPERAND_ADDRESS:
+ CLEAR_HARD_REG_BIT (reload_reg_used_in_op_addr, i);
+ break;
+
+ case RELOAD_FOR_OPADDR_ADDR:
+ CLEAR_HARD_REG_BIT (reload_reg_used_in_op_addr_reload, i);
+ break;
+
+ case RELOAD_FOR_OTHER_ADDRESS:
+ CLEAR_HARD_REG_BIT (reload_reg_used_in_other_addr, i);
+ break;
+
+ case RELOAD_FOR_INPUT:
+ CLEAR_HARD_REG_BIT (reload_reg_used_in_input[opnum], i);
+ break;
+
+ case RELOAD_FOR_OUTPUT:
+ CLEAR_HARD_REG_BIT (reload_reg_used_in_output[opnum], i);
+ break;
+
+ case RELOAD_FOR_INSN:
+ CLEAR_HARD_REG_BIT (reload_reg_used_in_insn, i);
+ break;
+ }
+ }
+}
+
+/* 1 if reg REGNO is free as a reload reg for a reload of the sort
+ specified by OPNUM and TYPE. */
+
+static int
+reload_reg_free_p (regno, opnum, type)
+ int regno;
+ int opnum;
+ enum reload_type type;
+{
+ int i;
+
+ /* In use for a RELOAD_OTHER means it's not available for anything except
+ RELOAD_FOR_OTHER_ADDRESS. Recall that RELOAD_FOR_OTHER_ADDRESS is known
+ to be used only for inputs. */
+
+ if (type != RELOAD_FOR_OTHER_ADDRESS
+ && TEST_HARD_REG_BIT (reload_reg_used, regno))
+ return 0;
+
+ switch (type)
+ {
+ case RELOAD_OTHER:
+ /* In use for anything except RELOAD_FOR_OTHER_ADDRESS means
+ we can't use it for RELOAD_OTHER. */
+ if (TEST_HARD_REG_BIT (reload_reg_used, regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno))
+ return 0;
+
+ for (i = 0; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno))
+ return 0;
+
+ return 1;
+
+ case RELOAD_FOR_INPUT:
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno))
+ return 0;
+
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_op_addr_reload, regno))
+ return 0;
+
+ /* If it is used for some other input, can't use it. */
+ for (i = 0; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno))
+ return 0;
+
+ /* If it is used in a later operand's address, can't use it. */
+ for (i = opnum + 1; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno))
+ return 0;
+
+ return 1;
+
+ case RELOAD_FOR_INPUT_ADDRESS:
+ /* Can't use a register if it is used for an input address for this
+ operand or used as an input in an earlier one. */
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[opnum], regno))
+ return 0;
+
+ for (i = 0; i < opnum; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno))
+ return 0;
+
+ return 1;
+
+ case RELOAD_FOR_OUTPUT_ADDRESS:
+ /* Can't use a register if it is used for an output address for this
+ operand or used as an output in this or a later operand. */
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], regno))
+ return 0;
+
+ for (i = opnum; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno))
+ return 0;
+
+ return 1;
+
+ case RELOAD_FOR_OPERAND_ADDRESS:
+ for (i = 0; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno))
+ return 0;
+
+ return (! TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno)
+ && ! TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno));
+
+ case RELOAD_FOR_OPADDR_ADDR:
+ for (i = 0; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno))
+ return 0;
+
+ return (!TEST_HARD_REG_BIT (reload_reg_used_in_op_addr_reload, regno));
+
+ case RELOAD_FOR_OUTPUT:
+ /* This cannot share a register with RELOAD_FOR_INSN reloads, other
+ outputs, or an operand address for this or an earlier output. */
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno))
+ return 0;
+
+ for (i = 0; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno))
+ return 0;
+
+ for (i = 0; i <= opnum; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno))
+ return 0;
+
+ return 1;
+
+ case RELOAD_FOR_INSN:
+ for (i = 0; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno))
+ return 0;
+
+ return (! TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno)
+ && ! TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno));
+
+ case RELOAD_FOR_OTHER_ADDRESS:
+ return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno);
+ }
+ abort ();
+}
+
+/* Return 1 if the value in reload reg REGNO, as used by a reload
+ needed for the part of the insn specified by OPNUM and TYPE,
+ is not in use for a reload in any prior part of the insn.
+
+ We can assume that the reload reg was already tested for availability
+ at the time it is needed, and we should not check this again,
+ in case the reg has already been marked in use. */
+
+static int
+reload_reg_free_before_p (regno, opnum, type)
+ int regno;
+ int opnum;
+ enum reload_type type;
+{
+ int i;
+
+ switch (type)
+ {
+ case RELOAD_FOR_OTHER_ADDRESS:
+ /* These always come first. */
+ return 1;
+
+ case RELOAD_OTHER:
+ return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno);
+
+ /* If this use is for part of the insn,
+ check the reg is not in use for any prior part. It is tempting
+ to try to do this by falling through from objecs that occur
+ later in the insn to ones that occur earlier, but that will not
+ correctly take into account the fact that here we MUST ignore
+ things that would prevent the register from being allocated in
+ the first place, since we know that it was allocated. */
+
+ case RELOAD_FOR_OUTPUT_ADDRESS:
+ /* Earlier reloads are for earlier outputs or their addresses,
+ any RELOAD_FOR_INSN reloads, any inputs or their addresses, or any
+ RELOAD_FOR_OTHER_ADDRESS reloads (we know it can't conflict with
+ RELOAD_OTHER).. */
+ for (i = 0; i < opnum; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno))
+ return 0;
+
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno))
+ return 0;
+
+ for (i = 0; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno))
+ return 0;
+
+ return (! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno)
+ && ! TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno)
+ && ! TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno));
+
+ case RELOAD_FOR_OUTPUT:
+ /* This can't be used in the output address for this operand and
+ anything that can't be used for it, except that we've already
+ tested for RELOAD_FOR_INSN objects. */
+
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], regno))
+ return 0;
+
+ for (i = 0; i < opnum; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno))
+ return 0;
+
+ for (i = 0; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno))
+ return 0;
+
+ return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno);
+
+ case RELOAD_FOR_OPERAND_ADDRESS:
+ case RELOAD_FOR_OPADDR_ADDR:
+ case RELOAD_FOR_INSN:
+ /* These can't conflict with inputs, or each other, so all we have to
+ test is input addresses and the addresses of OTHER items. */
+
+ for (i = 0; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno))
+ return 0;
+
+ return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno);
+
+ case RELOAD_FOR_INPUT:
+ /* The only things earlier are the address for this and
+ earlier inputs, other inputs (which we know we don't conflict
+ with), and addresses of RELOAD_OTHER objects. */
+
+ for (i = 0; i <= opnum; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno))
+ return 0;
+
+ return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno);
+
+ case RELOAD_FOR_INPUT_ADDRESS:
+ /* Similarly, all we have to check is for use in earlier inputs'
+ addresses. */
+ for (i = 0; i < opnum; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno))
+ return 0;
+
+ return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno);
+ }
+ abort ();
+}
+
+/* Return 1 if the value in reload reg REGNO, as used by a reload
+ needed for the part of the insn specified by OPNUM and TYPE,
+ is still available in REGNO at the end of the insn.
+
+ We can assume that the reload reg was already tested for availability
+ at the time it is needed, and we should not check this again,
+ in case the reg has already been marked in use. */
+
+static int
+reload_reg_reaches_end_p (regno, opnum, type)
+ int regno;
+ int opnum;
+ enum reload_type type;
+{
+ int i;
+
+ switch (type)
+ {
+ case RELOAD_OTHER:
+ /* Since a RELOAD_OTHER reload claims the reg for the entire insn,
+ its value must reach the end. */
+ return 1;
+
+ /* If this use is for part of the insn,
+ its value reaches if no subsequent part uses the same register.
+ Just like the above function, don't try to do this with lots
+ of fallthroughs. */
+
+ case RELOAD_FOR_OTHER_ADDRESS:
+ /* Here we check for everything else, since these don't conflict
+ with anything else and everything comes later. */
+
+ for (i = 0; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno))
+ return 0;
+
+ return (! TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno)
+ && ! TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno)
+ && ! TEST_HARD_REG_BIT (reload_reg_used, regno));
+
+ case RELOAD_FOR_INPUT_ADDRESS:
+ /* Similar, except that we check only for this and subsequent inputs
+ and the address of only subsequent inputs and we do not need
+ to check for RELOAD_OTHER objects since they are known not to
+ conflict. */
+
+ for (i = opnum; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno))
+ return 0;
+
+ for (i = opnum + 1; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno))
+ return 0;
+
+ for (i = 0; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno))
+ return 0;
+
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_op_addr_reload, regno))
+ return 0;
+
+ return (! TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno)
+ && ! TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno));
+
+ case RELOAD_FOR_INPUT:
+ /* Similar to input address, except we start at the next operand for
+ both input and input address and we do not check for
+ RELOAD_FOR_OPERAND_ADDRESS and RELOAD_FOR_INSN since these
+ would conflict. */
+
+ for (i = opnum + 1; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno))
+ return 0;
+
+ /* ... fall through ... */
+
+ case RELOAD_FOR_OPERAND_ADDRESS:
+ /* Check outputs and their addresses. */
+
+ for (i = 0; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno))
+ return 0;
+
+ return 1;
+
+ case RELOAD_FOR_OPADDR_ADDR:
+ for (i = 0; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno)
+ || TEST_HARD_REG_BIT (reload_reg_used_in_output[i], regno))
+ return 0;
+
+ return (! TEST_HARD_REG_BIT (reload_reg_used_in_op_addr, regno)
+ && !TEST_HARD_REG_BIT (reload_reg_used_in_insn, regno));
+
+ case RELOAD_FOR_INSN:
+ /* These conflict with other outputs with RELOAD_OTHER. So
+ we need only check for output addresses. */
+
+ opnum = -1;
+
+ /* ... fall through ... */
+
+ case RELOAD_FOR_OUTPUT:
+ case RELOAD_FOR_OUTPUT_ADDRESS:
+ /* We already know these can't conflict with a later output. So the
+ only thing to check are later output addresses. */
+ for (i = opnum + 1; i < reload_n_operands; i++)
+ if (TEST_HARD_REG_BIT (reload_reg_used_in_output_addr[i], regno))
+ return 0;
+
+ return 1;
+ }
+
+ abort ();
+}
+
+/* Return 1 if the reloads denoted by R1 and R2 cannot share a register.
+ Return 0 otherwise.
+
+ This function uses the same algorithm as reload_reg_free_p above. */
+
+static int
+reloads_conflict (r1, r2)
+ int r1, r2;
+{
+ enum reload_type r1_type = reload_when_needed[r1];
+ enum reload_type r2_type = reload_when_needed[r2];
+ int r1_opnum = reload_opnum[r1];
+ int r2_opnum = reload_opnum[r2];
+
+ /* RELOAD_OTHER conflicts with everything except RELOAD_FOR_OTHER_ADDRESS. */
+
+ if (r2_type == RELOAD_OTHER && r1_type != RELOAD_FOR_OTHER_ADDRESS)
+ return 1;
+
+ /* Otherwise, check conflicts differently for each type. */
+
+ switch (r1_type)
+ {
+ case RELOAD_FOR_INPUT:
+ return (r2_type == RELOAD_FOR_INSN
+ || r2_type == RELOAD_FOR_OPERAND_ADDRESS
+ || r2_type == RELOAD_FOR_OPADDR_ADDR
+ || r2_type == RELOAD_FOR_INPUT
+ || (r2_type == RELOAD_FOR_INPUT_ADDRESS && r2_opnum > r1_opnum));
+
+ case RELOAD_FOR_INPUT_ADDRESS:
+ return ((r2_type == RELOAD_FOR_INPUT_ADDRESS && r1_opnum == r2_opnum)
+ || (r2_type == RELOAD_FOR_INPUT && r2_opnum < r1_opnum));
+
+ case RELOAD_FOR_OUTPUT_ADDRESS:
+ return ((r2_type == RELOAD_FOR_OUTPUT_ADDRESS && r2_opnum == r1_opnum)
+ || (r2_type == RELOAD_FOR_OUTPUT && r2_opnum >= r1_opnum));
+
+ case RELOAD_FOR_OPERAND_ADDRESS:
+ return (r2_type == RELOAD_FOR_INPUT || r2_type == RELOAD_FOR_INSN
+ || r2_type == RELOAD_FOR_OPERAND_ADDRESS);
+
+ case RELOAD_FOR_OPADDR_ADDR:
+ return (r2_type == RELOAD_FOR_INPUT
+ || r2_type == RELOAD_FOR_OPADDR_ADDR);
+
+ case RELOAD_FOR_OUTPUT:
+ return (r2_type == RELOAD_FOR_INSN || r2_type == RELOAD_FOR_OUTPUT
+ || (r2_type == RELOAD_FOR_OUTPUT_ADDRESS
+ && r2_opnum >= r1_opnum));
+
+ case RELOAD_FOR_INSN:
+ return (r2_type == RELOAD_FOR_INPUT || r2_type == RELOAD_FOR_OUTPUT
+ || r2_type == RELOAD_FOR_INSN
+ || r2_type == RELOAD_FOR_OPERAND_ADDRESS);
+
+ case RELOAD_FOR_OTHER_ADDRESS:
+ return r2_type == RELOAD_FOR_OTHER_ADDRESS;
+
+ case RELOAD_OTHER:
+ return r2_type != RELOAD_FOR_OTHER_ADDRESS;
+
+ default:
+ abort ();
+ }
+}
+
+/* Vector of reload-numbers showing the order in which the reloads should
+ be processed. */
+short reload_order[MAX_RELOADS];
+
+/* Indexed by reload number, 1 if incoming value
+ inherited from previous insns. */
+char reload_inherited[MAX_RELOADS];
+
+/* For an inherited reload, this is the insn the reload was inherited from,
+ if we know it. Otherwise, this is 0. */
+rtx reload_inheritance_insn[MAX_RELOADS];
+
+/* If non-zero, this is a place to get the value of the reload,
+ rather than using reload_in. */
+rtx reload_override_in[MAX_RELOADS];
+
+/* For each reload, the index in spill_regs of the spill register used,
+ or -1 if we did not need one of the spill registers for this reload. */
+int reload_spill_index[MAX_RELOADS];
+
+/* Index of last register assigned as a spill register. We allocate in
+ a round-robin fashio. */
+
+static int last_spill_reg = 0;
+
+/* Find a spill register to use as a reload register for reload R.
+ LAST_RELOAD is non-zero if this is the last reload for the insn being
+ processed.
+
+ Set reload_reg_rtx[R] to the register allocated.
+
+ If NOERROR is nonzero, we return 1 if successful,
+ or 0 if we couldn't find a spill reg and we didn't change anything. */
+
+static int
+allocate_reload_reg (r, insn, last_reload, noerror)
+ int r;
+ rtx insn;
+ int last_reload;
+ int noerror;
+{
+ int i;
+ int pass;
+ int count;
+ rtx new;
+ int regno;
+
+ /* If we put this reload ahead, thinking it is a group,
+ then insist on finding a group. Otherwise we can grab a
+ reg that some other reload needs.
+ (That can happen when we have a 68000 DATA_OR_FP_REG
+ which is a group of data regs or one fp reg.)
+ We need not be so restrictive if there are no more reloads
+ for this insn.
+
+ ??? Really it would be nicer to have smarter handling
+ for that kind of reg class, where a problem like this is normal.
+ Perhaps those classes should be avoided for reloading
+ by use of more alternatives. */
+
+ int force_group = reload_nregs[r] > 1 && ! last_reload;
+
+ /* If we want a single register and haven't yet found one,
+ take any reg in the right class and not in use.
+ If we want a consecutive group, here is where we look for it.
+
+ We use two passes so we can first look for reload regs to
+ reuse, which are already in use for other reloads in this insn,
+ and only then use additional registers.
+ I think that maximizing reuse is needed to make sure we don't
+ run out of reload regs. Suppose we have three reloads, and
+ reloads A and B can share regs. These need two regs.
+ Suppose A and B are given different regs.
+ That leaves none for C. */
+ for (pass = 0; pass < 2; pass++)
+ {
+ /* I is the index in spill_regs.
+ We advance it round-robin between insns to use all spill regs
+ equally, so that inherited reloads have a chance
+ of leapfrogging each other. Don't do this, however, when we have
+ group needs and failure would be fatal; if we only have a relatively
+ small number of spill registers, and more than one of them has
+ group needs, then by starting in the middle, we may end up
+ allocating the first one in such a way that we are not left with
+ sufficient groups to handle the rest. */
+
+ if (noerror || ! force_group)
+ i = last_spill_reg;
+ else
+ i = -1;
+
+ for (count = 0; count < n_spills; count++)
+ {
+ int class = (int) reload_reg_class[r];
+
+ i = (i + 1) % n_spills;
+
+ if (reload_reg_free_p (spill_regs[i], reload_opnum[r],
+ reload_when_needed[r])
+ && TEST_HARD_REG_BIT (reg_class_contents[class], spill_regs[i])
+ && HARD_REGNO_MODE_OK (spill_regs[i], reload_mode[r])
+ /* Look first for regs to share, then for unshared. But
+ don't share regs used for inherited reloads; they are
+ the ones we want to preserve. */
+ && (pass
+ || (TEST_HARD_REG_BIT (reload_reg_used_at_all,
+ spill_regs[i])
+ && ! TEST_HARD_REG_BIT (reload_reg_used_for_inherit,
+ spill_regs[i]))))
+ {
+ int nr = HARD_REGNO_NREGS (spill_regs[i], reload_mode[r]);
+ /* Avoid the problem where spilling a GENERAL_OR_FP_REG
+ (on 68000) got us two FP regs. If NR is 1,
+ we would reject both of them. */
+ if (force_group)
+ nr = CLASS_MAX_NREGS (reload_reg_class[r], reload_mode[r]);
+ /* If we need only one reg, we have already won. */
+ if (nr == 1)
+ {
+ /* But reject a single reg if we demand a group. */
+ if (force_group)
+ continue;
+ break;
+ }
+ /* Otherwise check that as many consecutive regs as we need
+ are available here.
+ Also, don't use for a group registers that are
+ needed for nongroups. */
+ if (! TEST_HARD_REG_BIT (counted_for_nongroups, spill_regs[i]))
+ while (nr > 1)
+ {
+ regno = spill_regs[i] + nr - 1;
+ if (!(TEST_HARD_REG_BIT (reg_class_contents[class], regno)
+ && spill_reg_order[regno] >= 0
+ && reload_reg_free_p (regno, reload_opnum[r],
+ reload_when_needed[r])
+ && ! TEST_HARD_REG_BIT (counted_for_nongroups,
+ regno)))
+ break;
+ nr--;
+ }
+ if (nr == 1)
+ break;
+ }
+ }
+
+ /* If we found something on pass 1, omit pass 2. */
+ if (count < n_spills)
+ break;
+ }
+
+ /* We should have found a spill register by now. */
+ if (count == n_spills)
+ {
+ if (noerror)
+ return 0;
+ goto failure;
+ }
+
+ /* I is the index in SPILL_REG_RTX of the reload register we are to
+ allocate. Get an rtx for it and find its register number. */
+
+ new = spill_reg_rtx[i];
+
+ if (new == 0 || GET_MODE (new) != reload_mode[r])
+ spill_reg_rtx[i] = new
+ = gen_rtx (REG, reload_mode[r], spill_regs[i]);
+
+ regno = true_regnum (new);
+
+ /* Detect when the reload reg can't hold the reload mode.
+ This used to be one `if', but Sequent compiler can't handle that. */
+ if (HARD_REGNO_MODE_OK (regno, reload_mode[r]))
+ {
+ enum machine_mode test_mode = VOIDmode;
+ if (reload_in[r])
+ test_mode = GET_MODE (reload_in[r]);
+ /* If reload_in[r] has VOIDmode, it means we will load it
+ in whatever mode the reload reg has: to wit, reload_mode[r].
+ We have already tested that for validity. */
+ /* Aside from that, we need to test that the expressions
+ to reload from or into have modes which are valid for this
+ reload register. Otherwise the reload insns would be invalid. */
+ if (! (reload_in[r] != 0 && test_mode != VOIDmode
+ && ! HARD_REGNO_MODE_OK (regno, test_mode)))
+ if (! (reload_out[r] != 0
+ && ! HARD_REGNO_MODE_OK (regno, GET_MODE (reload_out[r]))))
+ {
+ /* The reg is OK. */
+ last_spill_reg = i;
+
+ /* Mark as in use for this insn the reload regs we use
+ for this. */
+ mark_reload_reg_in_use (spill_regs[i], reload_opnum[r],
+ reload_when_needed[r], reload_mode[r]);
+
+ reload_reg_rtx[r] = new;
+ reload_spill_index[r] = i;
+ return 1;
+ }
+ }
+
+ /* The reg is not OK. */
+ if (noerror)
+ return 0;
+
+ failure:
+ if (asm_noperands (PATTERN (insn)) < 0)
+ /* It's the compiler's fault. */
+ abort ();
+
+ /* It's the user's fault; the operand's mode and constraint
+ don't match. Disable this reload so we don't crash in final. */
+ error_for_asm (insn,
+ "`asm' operand constraint incompatible with operand size");
+ reload_in[r] = 0;
+ reload_out[r] = 0;
+ reload_reg_rtx[r] = 0;
+ reload_optional[r] = 1;
+ reload_secondary_p[r] = 1;
+
+ return 1;
+}
+
+/* Assign hard reg targets for the pseudo-registers we must reload
+ into hard regs for this insn.
+ Also output the instructions to copy them in and out of the hard regs.
+
+ For machines with register classes, we are responsible for
+ finding a reload reg in the proper class. */
+
+static void
+choose_reload_regs (insn, avoid_return_reg)
+ rtx insn;
+ rtx avoid_return_reg;
+{
+ register int i, j;
+ int max_group_size = 1;
+ enum reg_class group_class = NO_REGS;
+ int inheritance;
+
+ rtx save_reload_reg_rtx[MAX_RELOADS];
+ char save_reload_inherited[MAX_RELOADS];
+ rtx save_reload_inheritance_insn[MAX_RELOADS];
+ rtx save_reload_override_in[MAX_RELOADS];
+ int save_reload_spill_index[MAX_RELOADS];
+ HARD_REG_SET save_reload_reg_used;
+ HARD_REG_SET save_reload_reg_used_in_input_addr[MAX_RECOG_OPERANDS];
+ HARD_REG_SET save_reload_reg_used_in_output_addr[MAX_RECOG_OPERANDS];
+ HARD_REG_SET save_reload_reg_used_in_input[MAX_RECOG_OPERANDS];
+ HARD_REG_SET save_reload_reg_used_in_output[MAX_RECOG_OPERANDS];
+ HARD_REG_SET save_reload_reg_used_in_op_addr;
+ HARD_REG_SET save_reload_reg_used_in_op_addr_reload;
+ HARD_REG_SET save_reload_reg_used_in_insn;
+ HARD_REG_SET save_reload_reg_used_in_other_addr;
+ HARD_REG_SET save_reload_reg_used_at_all;
+
+ bzero (reload_inherited, MAX_RELOADS);
+ bzero ((char *) reload_inheritance_insn, MAX_RELOADS * sizeof (rtx));
+ bzero ((char *) reload_override_in, MAX_RELOADS * sizeof (rtx));
+
+ CLEAR_HARD_REG_SET (reload_reg_used);
+ CLEAR_HARD_REG_SET (reload_reg_used_at_all);
+ CLEAR_HARD_REG_SET (reload_reg_used_in_op_addr);
+ CLEAR_HARD_REG_SET (reload_reg_used_in_op_addr_reload);
+ CLEAR_HARD_REG_SET (reload_reg_used_in_insn);
+ CLEAR_HARD_REG_SET (reload_reg_used_in_other_addr);
+
+ for (i = 0; i < reload_n_operands; i++)
+ {
+ CLEAR_HARD_REG_SET (reload_reg_used_in_output[i]);
+ CLEAR_HARD_REG_SET (reload_reg_used_in_input[i]);
+ CLEAR_HARD_REG_SET (reload_reg_used_in_input_addr[i]);
+ CLEAR_HARD_REG_SET (reload_reg_used_in_output_addr[i]);
+ }
+
+#ifdef SMALL_REGISTER_CLASSES
+ /* Don't bother with avoiding the return reg
+ if we have no mandatory reload that could use it. */
+ if (avoid_return_reg)
+ {
+ int do_avoid = 0;
+ int regno = REGNO (avoid_return_reg);
+ int nregs
+ = HARD_REGNO_NREGS (regno, GET_MODE (avoid_return_reg));
+ int r;
+
+ for (r = regno; r < regno + nregs; r++)
+ if (spill_reg_order[r] >= 0)
+ for (j = 0; j < n_reloads; j++)
+ if (!reload_optional[j] && reload_reg_rtx[j] == 0
+ && (reload_in[j] != 0 || reload_out[j] != 0
+ || reload_secondary_p[j])
+ &&
+ TEST_HARD_REG_BIT (reg_class_contents[(int) reload_reg_class[j]], r))
+ do_avoid = 1;
+ if (!do_avoid)
+ avoid_return_reg = 0;
+ }
+#endif /* SMALL_REGISTER_CLASSES */
+
+#if 0 /* Not needed, now that we can always retry without inheritance. */
+ /* See if we have more mandatory reloads than spill regs.
+ If so, then we cannot risk optimizations that could prevent
+ reloads from sharing one spill register.
+
+ Since we will try finding a better register than reload_reg_rtx
+ unless it is equal to reload_in or reload_out, count such reloads. */
+
+ {
+ int tem = 0;
+#ifdef SMALL_REGISTER_CLASSES
+ int tem = (avoid_return_reg != 0);
+#endif
+ for (j = 0; j < n_reloads; j++)
+ if (! reload_optional[j]
+ && (reload_in[j] != 0 || reload_out[j] != 0 || reload_secondary_p[j])
+ && (reload_reg_rtx[j] == 0
+ || (! rtx_equal_p (reload_reg_rtx[j], reload_in[j])
+ && ! rtx_equal_p (reload_reg_rtx[j], reload_out[j]))))
+ tem++;
+ if (tem > n_spills)
+ must_reuse = 1;
+ }
+#endif
+
+#ifdef SMALL_REGISTER_CLASSES
+ /* Don't use the subroutine call return reg for a reload
+ if we are supposed to avoid it. */
+ if (avoid_return_reg)
+ {
+ int regno = REGNO (avoid_return_reg);
+ int nregs
+ = HARD_REGNO_NREGS (regno, GET_MODE (avoid_return_reg));
+ int r;
+
+ for (r = regno; r < regno + nregs; r++)
+ if (spill_reg_order[r] >= 0)
+ SET_HARD_REG_BIT (reload_reg_used, r);
+ }
+#endif /* SMALL_REGISTER_CLASSES */
+
+ /* In order to be certain of getting the registers we need,
+ we must sort the reloads into order of increasing register class.
+ Then our grabbing of reload registers will parallel the process
+ that provided the reload registers.
+
+ Also note whether any of the reloads wants a consecutive group of regs.
+ If so, record the maximum size of the group desired and what
+ register class contains all the groups needed by this insn. */
+
+ for (j = 0; j < n_reloads; j++)
+ {
+ reload_order[j] = j;
+ reload_spill_index[j] = -1;
+
+ reload_mode[j]
+ = (reload_inmode[j] == VOIDmode
+ || (GET_MODE_SIZE (reload_outmode[j])
+ > GET_MODE_SIZE (reload_inmode[j])))
+ ? reload_outmode[j] : reload_inmode[j];
+
+ reload_nregs[j] = CLASS_MAX_NREGS (reload_reg_class[j], reload_mode[j]);
+
+ if (reload_nregs[j] > 1)
+ {
+ max_group_size = MAX (reload_nregs[j], max_group_size);
+ group_class = reg_class_superunion[(int)reload_reg_class[j]][(int)group_class];
+ }
+
+ /* If we have already decided to use a certain register,
+ don't use it in another way. */
+ if (reload_reg_rtx[j])
+ mark_reload_reg_in_use (REGNO (reload_reg_rtx[j]), reload_opnum[j],
+ reload_when_needed[j], reload_mode[j]);
+ }
+
+ if (n_reloads > 1)
+ qsort (reload_order, n_reloads, sizeof (short), reload_reg_class_lower);
+
+ bcopy ((char *) reload_reg_rtx, (char *) save_reload_reg_rtx,
+ sizeof reload_reg_rtx);
+ bcopy (reload_inherited, save_reload_inherited, sizeof reload_inherited);
+ bcopy ((char *) reload_inheritance_insn,
+ (char *) save_reload_inheritance_insn,
+ sizeof reload_inheritance_insn);
+ bcopy ((char *) reload_override_in, (char *) save_reload_override_in,
+ sizeof reload_override_in);
+ bcopy ((char *) reload_spill_index, (char *) save_reload_spill_index,
+ sizeof reload_spill_index);
+ COPY_HARD_REG_SET (save_reload_reg_used, reload_reg_used);
+ COPY_HARD_REG_SET (save_reload_reg_used_at_all, reload_reg_used_at_all);
+ COPY_HARD_REG_SET (save_reload_reg_used_in_op_addr,
+ reload_reg_used_in_op_addr);
+
+ COPY_HARD_REG_SET (save_reload_reg_used_in_op_addr_reload,
+ reload_reg_used_in_op_addr_reload);
+
+ COPY_HARD_REG_SET (save_reload_reg_used_in_insn,
+ reload_reg_used_in_insn);
+ COPY_HARD_REG_SET (save_reload_reg_used_in_other_addr,
+ reload_reg_used_in_other_addr);
+
+ for (i = 0; i < reload_n_operands; i++)
+ {
+ COPY_HARD_REG_SET (save_reload_reg_used_in_output[i],
+ reload_reg_used_in_output[i]);
+ COPY_HARD_REG_SET (save_reload_reg_used_in_input[i],
+ reload_reg_used_in_input[i]);
+ COPY_HARD_REG_SET (save_reload_reg_used_in_input_addr[i],
+ reload_reg_used_in_input_addr[i]);
+ COPY_HARD_REG_SET (save_reload_reg_used_in_output_addr[i],
+ reload_reg_used_in_output_addr[i]);
+ }
+
+ /* If -O, try first with inheritance, then turning it off.
+ If not -O, don't do inheritance.
+ Using inheritance when not optimizing leads to paradoxes
+ with fp on the 68k: fp numbers (not NaNs) fail to be equal to themselves
+ because one side of the comparison might be inherited. */
+
+ for (inheritance = optimize > 0; inheritance >= 0; inheritance--)
+ {
+ /* Process the reloads in order of preference just found.
+ Beyond this point, subregs can be found in reload_reg_rtx.
+
+ This used to look for an existing reloaded home for all
+ of the reloads, and only then perform any new reloads.
+ But that could lose if the reloads were done out of reg-class order
+ because a later reload with a looser constraint might have an old
+ home in a register needed by an earlier reload with a tighter constraint.
+
+ To solve this, we make two passes over the reloads, in the order
+ described above. In the first pass we try to inherit a reload
+ from a previous insn. If there is a later reload that needs a
+ class that is a proper subset of the class being processed, we must
+ also allocate a spill register during the first pass.
+
+ Then make a second pass over the reloads to allocate any reloads
+ that haven't been given registers yet. */
+
+ CLEAR_HARD_REG_SET (reload_reg_used_for_inherit);
+
+ for (j = 0; j < n_reloads; j++)
+ {
+ register int r = reload_order[j];
+
+ /* Ignore reloads that got marked inoperative. */
+ if (reload_out[r] == 0 && reload_in[r] == 0 && ! reload_secondary_p[r])
+ continue;
+
+ /* If find_reloads chose a to use reload_in or reload_out as a reload
+ register, we don't need to chose one. Otherwise, try even if it found
+ one since we might save an insn if we find the value lying around. */
+ if (reload_in[r] != 0 && reload_reg_rtx[r] != 0
+ && (rtx_equal_p (reload_in[r], reload_reg_rtx[r])
+ || rtx_equal_p (reload_out[r], reload_reg_rtx[r])))
+ continue;
+
+#if 0 /* No longer needed for correct operation.
+ It might give better code, or might not; worth an experiment? */
+ /* If this is an optional reload, we can't inherit from earlier insns
+ until we are sure that any non-optional reloads have been allocated.
+ The following code takes advantage of the fact that optional reloads
+ are at the end of reload_order. */
+ if (reload_optional[r] != 0)
+ for (i = 0; i < j; i++)
+ if ((reload_out[reload_order[i]] != 0
+ || reload_in[reload_order[i]] != 0
+ || reload_secondary_p[reload_order[i]])
+ && ! reload_optional[reload_order[i]]
+ && reload_reg_rtx[reload_order[i]] == 0)
+ allocate_reload_reg (reload_order[i], insn, 0, inheritance);
+#endif
+
+ /* First see if this pseudo is already available as reloaded
+ for a previous insn. We cannot try to inherit for reloads
+ that are smaller than the maximum number of registers needed
+ for groups unless the register we would allocate cannot be used
+ for the groups.
+
+ We could check here to see if this is a secondary reload for
+ an object that is already in a register of the desired class.
+ This would avoid the need for the secondary reload register.
+ But this is complex because we can't easily determine what
+ objects might want to be loaded via this reload. So let a register
+ be allocated here. In `emit_reload_insns' we suppress one of the
+ loads in the case described above. */
+
+ if (inheritance)
+ {
+ register int regno = -1;
+ enum machine_mode mode;
+
+ if (reload_in[r] == 0)
+ ;
+ else if (GET_CODE (reload_in[r]) == REG)
+ {
+ regno = REGNO (reload_in[r]);
+ mode = GET_MODE (reload_in[r]);
+ }
+ else if (GET_CODE (reload_in_reg[r]) == REG)
+ {
+ regno = REGNO (reload_in_reg[r]);
+ mode = GET_MODE (reload_in_reg[r]);
+ }
+#if 0
+ /* This won't work, since REGNO can be a pseudo reg number.
+ Also, it takes much more hair to keep track of all the things
+ that can invalidate an inherited reload of part of a pseudoreg. */
+ else if (GET_CODE (reload_in[r]) == SUBREG
+ && GET_CODE (SUBREG_REG (reload_in[r])) == REG)
+ regno = REGNO (SUBREG_REG (reload_in[r])) + SUBREG_WORD (reload_in[r]);
+#endif
+
+ if (regno >= 0 && reg_last_reload_reg[regno] != 0)
+ {
+ i = spill_reg_order[REGNO (reg_last_reload_reg[regno])];
+
+ if (reg_reloaded_contents[i] == regno
+ && (GET_MODE_SIZE (GET_MODE (reg_last_reload_reg[regno]))
+ >= GET_MODE_SIZE (mode))
+ && HARD_REGNO_MODE_OK (spill_regs[i], reload_mode[r])
+ && TEST_HARD_REG_BIT (reg_class_contents[(int) reload_reg_class[r]],
+ spill_regs[i])
+ && (reload_nregs[r] == max_group_size
+ || ! TEST_HARD_REG_BIT (reg_class_contents[(int) group_class],
+ spill_regs[i]))
+ && reload_reg_free_p (spill_regs[i], reload_opnum[r],
+ reload_when_needed[r])
+ && reload_reg_free_before_p (spill_regs[i],
+ reload_opnum[r],
+ reload_when_needed[r]))
+ {
+ /* If a group is needed, verify that all the subsequent
+ registers still have their values intact. */
+ int nr
+ = HARD_REGNO_NREGS (spill_regs[i], reload_mode[r]);
+ int k;
+
+ for (k = 1; k < nr; k++)
+ if (reg_reloaded_contents[spill_reg_order[spill_regs[i] + k]]
+ != regno)
+ break;
+
+ if (k == nr)
+ {
+ int i1;
+
+ /* We found a register that contains the
+ value we need. If this register is the
+ same as an `earlyclobber' operand of the
+ current insn, just mark it as a place to
+ reload from since we can't use it as the
+ reload register itself. */
+
+ for (i1 = 0; i1 < n_earlyclobbers; i1++)
+ if (reg_overlap_mentioned_for_reload_p
+ (reg_last_reload_reg[regno],
+ reload_earlyclobbers[i1]))
+ break;
+
+ if (i1 != n_earlyclobbers
+ /* Don't really use the inherited spill reg
+ if we need it wider than we've got it. */
+ || (GET_MODE_SIZE (reload_mode[r])
+ > GET_MODE_SIZE (mode)))
+ reload_override_in[r] = reg_last_reload_reg[regno];
+ else
+ {
+ int k;
+ /* We can use this as a reload reg. */
+ /* Mark the register as in use for this part of
+ the insn. */
+ mark_reload_reg_in_use (spill_regs[i],
+ reload_opnum[r],
+ reload_when_needed[r],
+ reload_mode[r]);
+ reload_reg_rtx[r] = reg_last_reload_reg[regno];
+ reload_inherited[r] = 1;
+ reload_inheritance_insn[r]
+ = reg_reloaded_insn[i];
+ reload_spill_index[r] = i;
+ for (k = 0; k < nr; k++)
+ SET_HARD_REG_BIT (reload_reg_used_for_inherit,
+ spill_regs[i + k]);
+ }
+ }
+ }
+ }
+ }
+
+ /* Here's another way to see if the value is already lying around. */
+ if (inheritance
+ && reload_in[r] != 0
+ && ! reload_inherited[r]
+ && reload_out[r] == 0
+ && (CONSTANT_P (reload_in[r])
+ || GET_CODE (reload_in[r]) == PLUS
+ || GET_CODE (reload_in[r]) == REG
+ || GET_CODE (reload_in[r]) == MEM)
+ && (reload_nregs[r] == max_group_size
+ || ! reg_classes_intersect_p (reload_reg_class[r], group_class)))
+ {
+ register rtx equiv
+ = find_equiv_reg (reload_in[r], insn, reload_reg_class[r],
+ -1, NULL_PTR, 0, reload_mode[r]);
+ int regno;
+
+ if (equiv != 0)
+ {
+ if (GET_CODE (equiv) == REG)
+ regno = REGNO (equiv);
+ else if (GET_CODE (equiv) == SUBREG)
+ {
+ /* This must be a SUBREG of a hard register.
+ Make a new REG since this might be used in an
+ address and not all machines support SUBREGs
+ there. */
+ regno = REGNO (SUBREG_REG (equiv)) + SUBREG_WORD (equiv);
+ equiv = gen_rtx (REG, reload_mode[r], regno);
+ }
+ else
+ abort ();
+ }
+
+ /* If we found a spill reg, reject it unless it is free
+ and of the desired class. */
+ if (equiv != 0
+ && ((spill_reg_order[regno] >= 0
+ && ! reload_reg_free_before_p (regno, reload_opnum[r],
+ reload_when_needed[r]))
+ || ! TEST_HARD_REG_BIT (reg_class_contents[(int) reload_reg_class[r]],
+ regno)))
+ equiv = 0;
+
+ if (equiv != 0 && TEST_HARD_REG_BIT (reload_reg_used_at_all, regno))
+ equiv = 0;
+
+ if (equiv != 0 && ! HARD_REGNO_MODE_OK (regno, reload_mode[r]))
+ equiv = 0;
+
+ /* We found a register that contains the value we need.
+ If this register is the same as an `earlyclobber' operand
+ of the current insn, just mark it as a place to reload from
+ since we can't use it as the reload register itself. */
+
+ if (equiv != 0)
+ for (i = 0; i < n_earlyclobbers; i++)
+ if (reg_overlap_mentioned_for_reload_p (equiv,
+ reload_earlyclobbers[i]))
+ {
+ reload_override_in[r] = equiv;
+ equiv = 0;
+ break;
+ }
+
+ /* JRV: If the equiv register we have found is explicitly
+ clobbered in the current insn, mark but don't use, as above. */
+
+ if (equiv != 0 && regno_clobbered_p (regno, insn))
+ {
+ reload_override_in[r] = equiv;
+ equiv = 0;
+ }
+
+ /* If we found an equivalent reg, say no code need be generated
+ to load it, and use it as our reload reg. */
+ if (equiv != 0 && regno != HARD_FRAME_POINTER_REGNUM)
+ {
+ reload_reg_rtx[r] = equiv;
+ reload_inherited[r] = 1;
+ /* If it is a spill reg,
+ mark the spill reg as in use for this insn. */
+ i = spill_reg_order[regno];
+ if (i >= 0)
+ {
+ int nr = HARD_REGNO_NREGS (regno, reload_mode[r]);
+ int k;
+ mark_reload_reg_in_use (regno, reload_opnum[r],
+ reload_when_needed[r],
+ reload_mode[r]);
+ for (k = 0; k < nr; k++)
+ SET_HARD_REG_BIT (reload_reg_used_for_inherit, regno + k);
+ }
+ }
+ }
+
+ /* If we found a register to use already, or if this is an optional
+ reload, we are done. */
+ if (reload_reg_rtx[r] != 0 || reload_optional[r] != 0)
+ continue;
+
+#if 0 /* No longer needed for correct operation. Might or might not
+ give better code on the average. Want to experiment? */
+
+ /* See if there is a later reload that has a class different from our
+ class that intersects our class or that requires less register
+ than our reload. If so, we must allocate a register to this
+ reload now, since that reload might inherit a previous reload
+ and take the only available register in our class. Don't do this
+ for optional reloads since they will force all previous reloads
+ to be allocated. Also don't do this for reloads that have been
+ turned off. */
+
+ for (i = j + 1; i < n_reloads; i++)
+ {
+ int s = reload_order[i];
+
+ if ((reload_in[s] == 0 && reload_out[s] == 0
+ && ! reload_secondary_p[s])
+ || reload_optional[s])
+ continue;
+
+ if ((reload_reg_class[s] != reload_reg_class[r]
+ && reg_classes_intersect_p (reload_reg_class[r],
+ reload_reg_class[s]))
+ || reload_nregs[s] < reload_nregs[r])
+ break;
+ }
+
+ if (i == n_reloads)
+ continue;
+
+ allocate_reload_reg (r, insn, j == n_reloads - 1, inheritance);
+#endif
+ }
+
+ /* Now allocate reload registers for anything non-optional that
+ didn't get one yet. */
+ for (j = 0; j < n_reloads; j++)
+ {
+ register int r = reload_order[j];
+
+ /* Ignore reloads that got marked inoperative. */
+ if (reload_out[r] == 0 && reload_in[r] == 0 && ! reload_secondary_p[r])
+ continue;
+
+ /* Skip reloads that already have a register allocated or are
+ optional. */
+ if (reload_reg_rtx[r] != 0 || reload_optional[r])
+ continue;
+
+ if (! allocate_reload_reg (r, insn, j == n_reloads - 1, inheritance))
+ break;
+ }
+
+ /* If that loop got all the way, we have won. */
+ if (j == n_reloads)
+ break;
+
+ fail:
+ /* Loop around and try without any inheritance. */
+ /* First undo everything done by the failed attempt
+ to allocate with inheritance. */
+ bcopy ((char *) save_reload_reg_rtx, (char *) reload_reg_rtx,
+ sizeof reload_reg_rtx);
+ bcopy ((char *) save_reload_inherited, (char *) reload_inherited,
+ sizeof reload_inherited);
+ bcopy ((char *) save_reload_inheritance_insn,
+ (char *) reload_inheritance_insn,
+ sizeof reload_inheritance_insn);
+ bcopy ((char *) save_reload_override_in, (char *) reload_override_in,
+ sizeof reload_override_in);
+ bcopy ((char *) save_reload_spill_index, (char *) reload_spill_index,
+ sizeof reload_spill_index);
+ COPY_HARD_REG_SET (reload_reg_used, save_reload_reg_used);
+ COPY_HARD_REG_SET (reload_reg_used_at_all, save_reload_reg_used_at_all);
+ COPY_HARD_REG_SET (reload_reg_used_in_op_addr,
+ save_reload_reg_used_in_op_addr);
+ COPY_HARD_REG_SET (reload_reg_used_in_op_addr_reload,
+ save_reload_reg_used_in_op_addr_reload);
+ COPY_HARD_REG_SET (reload_reg_used_in_insn,
+ save_reload_reg_used_in_insn);
+ COPY_HARD_REG_SET (reload_reg_used_in_other_addr,
+ save_reload_reg_used_in_other_addr);
+
+ for (i = 0; i < reload_n_operands; i++)
+ {
+ COPY_HARD_REG_SET (reload_reg_used_in_input[i],
+ save_reload_reg_used_in_input[i]);
+ COPY_HARD_REG_SET (reload_reg_used_in_output[i],
+ save_reload_reg_used_in_output[i]);
+ COPY_HARD_REG_SET (reload_reg_used_in_input_addr[i],
+ save_reload_reg_used_in_input_addr[i]);
+ COPY_HARD_REG_SET (reload_reg_used_in_output_addr[i],
+ save_reload_reg_used_in_output_addr[i]);
+ }
+ }
+
+ /* If we thought we could inherit a reload, because it seemed that
+ nothing else wanted the same reload register earlier in the insn,
+ verify that assumption, now that all reloads have been assigned. */
+
+ for (j = 0; j < n_reloads; j++)
+ {
+ register int r = reload_order[j];
+
+ if (reload_inherited[r] && reload_reg_rtx[r] != 0
+ && ! reload_reg_free_before_p (true_regnum (reload_reg_rtx[r]),
+ reload_opnum[r],
+ reload_when_needed[r]))
+ reload_inherited[r] = 0;
+
+ /* If we found a better place to reload from,
+ validate it in the same fashion, if it is a reload reg. */
+ if (reload_override_in[r]
+ && (GET_CODE (reload_override_in[r]) == REG
+ || GET_CODE (reload_override_in[r]) == SUBREG))
+ {
+ int regno = true_regnum (reload_override_in[r]);
+ if (spill_reg_order[regno] >= 0
+ && ! reload_reg_free_before_p (regno, reload_opnum[r],
+ reload_when_needed[r]))
+ reload_override_in[r] = 0;
+ }
+ }
+
+ /* Now that reload_override_in is known valid,
+ actually override reload_in. */
+ for (j = 0; j < n_reloads; j++)
+ if (reload_override_in[j])
+ reload_in[j] = reload_override_in[j];
+
+ /* If this reload won't be done because it has been cancelled or is
+ optional and not inherited, clear reload_reg_rtx so other
+ routines (such as subst_reloads) don't get confused. */
+ for (j = 0; j < n_reloads; j++)
+ if (reload_reg_rtx[j] != 0
+ && ((reload_optional[j] && ! reload_inherited[j])
+ || (reload_in[j] == 0 && reload_out[j] == 0
+ && ! reload_secondary_p[j])))
+ {
+ int regno = true_regnum (reload_reg_rtx[j]);
+
+ if (spill_reg_order[regno] >= 0)
+ clear_reload_reg_in_use (regno, reload_opnum[j],
+ reload_when_needed[j], reload_mode[j]);
+ reload_reg_rtx[j] = 0;
+ }
+
+ /* Record which pseudos and which spill regs have output reloads. */
+ for (j = 0; j < n_reloads; j++)
+ {
+ register int r = reload_order[j];
+
+ i = reload_spill_index[r];
+
+ /* I is nonneg if this reload used one of the spill regs.
+ If reload_reg_rtx[r] is 0, this is an optional reload
+ that we opted to ignore. */
+ if (reload_out[r] != 0 && GET_CODE (reload_out[r]) == REG
+ && reload_reg_rtx[r] != 0)
+ {
+ register int nregno = REGNO (reload_out[r]);
+ int nr = 1;
+
+ if (nregno < FIRST_PSEUDO_REGISTER)
+ nr = HARD_REGNO_NREGS (nregno, reload_mode[r]);
+
+ while (--nr >= 0)
+ reg_has_output_reload[nregno + nr] = 1;
+
+ if (i >= 0)
+ {
+ nr = HARD_REGNO_NREGS (spill_regs[i], reload_mode[r]);
+ while (--nr >= 0)
+ SET_HARD_REG_BIT (reg_is_output_reload, spill_regs[i] + nr);
+ }
+
+ if (reload_when_needed[r] != RELOAD_OTHER
+ && reload_when_needed[r] != RELOAD_FOR_OUTPUT
+ && reload_when_needed[r] != RELOAD_FOR_INSN)
+ abort ();
+ }
+ }
+}
+
+/* If SMALL_REGISTER_CLASSES are defined, we may not have merged two
+ reloads of the same item for fear that we might not have enough reload
+ registers. However, normally they will get the same reload register
+ and hence actually need not be loaded twice.
+
+ Here we check for the most common case of this phenomenon: when we have
+ a number of reloads for the same object, each of which were allocated
+ the same reload_reg_rtx, that reload_reg_rtx is not used for any other
+ reload, and is not modified in the insn itself. If we find such,
+ merge all the reloads and set the resulting reload to RELOAD_OTHER.
+ This will not increase the number of spill registers needed and will
+ prevent redundant code. */
+
+#ifdef SMALL_REGISTER_CLASSES
+
+static void
+merge_assigned_reloads (insn)
+ rtx insn;
+{
+ int i, j;
+
+ /* Scan all the reloads looking for ones that only load values and
+ are not already RELOAD_OTHER and ones whose reload_reg_rtx are
+ assigned and not modified by INSN. */
+
+ for (i = 0; i < n_reloads; i++)
+ {
+ if (reload_in[i] == 0 || reload_when_needed[i] == RELOAD_OTHER
+ || reload_out[i] != 0 || reload_reg_rtx[i] == 0
+ || reg_set_p (reload_reg_rtx[i], insn))
+ continue;
+
+ /* Look at all other reloads. Ensure that the only use of this
+ reload_reg_rtx is in a reload that just loads the same value
+ as we do. Note that any secondary reloads must be of the identical
+ class since the values, modes, and result registers are the
+ same, so we need not do anything with any secondary reloads. */
+
+ for (j = 0; j < n_reloads; j++)
+ {
+ if (i == j || reload_reg_rtx[j] == 0
+ || ! reg_overlap_mentioned_p (reload_reg_rtx[j],
+ reload_reg_rtx[i]))
+ continue;
+
+ /* If the reload regs aren't exactly the same (e.g, different modes)
+ or if the values are different, we can't merge anything with this
+ reload register. */
+
+ if (! rtx_equal_p (reload_reg_rtx[i], reload_reg_rtx[j])
+ || reload_out[j] != 0 || reload_in[j] == 0
+ || ! rtx_equal_p (reload_in[i], reload_in[j]))
+ break;
+ }
+
+ /* If all is OK, merge the reloads. Only set this to RELOAD_OTHER if
+ we, in fact, found any matching reloads. */
+
+ if (j == n_reloads)
+ {
+ for (j = 0; j < n_reloads; j++)
+ if (i != j && reload_reg_rtx[j] != 0
+ && rtx_equal_p (reload_reg_rtx[i], reload_reg_rtx[j]))
+ {
+ reload_when_needed[i] = RELOAD_OTHER;
+ reload_in[j] = 0;
+ transfer_replacements (i, j);
+ }
+
+ /* If this is now RELOAD_OTHER, look for any reloads that load
+ parts of this operand and set them to RELOAD_FOR_OTHER_ADDRESS
+ if they were for inputs, RELOAD_OTHER for outputs. Note that
+ this test is equivalent to looking for reloads for this operand
+ number. */
+
+ if (reload_when_needed[i] == RELOAD_OTHER)
+ for (j = 0; j < n_reloads; j++)
+ if (reload_in[j] != 0
+ && reload_when_needed[i] != RELOAD_OTHER
+ && reg_overlap_mentioned_for_reload_p (reload_in[j],
+ reload_in[i]))
+ reload_when_needed[j]
+ = reload_when_needed[i] == RELOAD_FOR_INPUT_ADDRESS
+ ? RELOAD_FOR_OTHER_ADDRESS : RELOAD_OTHER;
+ }
+ }
+}
+#endif /* SMALL_RELOAD_CLASSES */
+
+/* Output insns to reload values in and out of the chosen reload regs. */
+
+static void
+emit_reload_insns (insn)
+ rtx insn;
+{
+ register int j;
+ rtx input_reload_insns[MAX_RECOG_OPERANDS];
+ rtx other_input_address_reload_insns = 0;
+ rtx other_input_reload_insns = 0;
+ rtx input_address_reload_insns[MAX_RECOG_OPERANDS];
+ rtx output_reload_insns[MAX_RECOG_OPERANDS];
+ rtx output_address_reload_insns[MAX_RECOG_OPERANDS];
+ rtx operand_reload_insns = 0;
+ rtx other_operand_reload_insns = 0;
+ rtx following_insn = NEXT_INSN (insn);
+ rtx before_insn = insn;
+ int special;
+ /* Values to be put in spill_reg_store are put here first. */
+ rtx new_spill_reg_store[FIRST_PSEUDO_REGISTER];
+
+ for (j = 0; j < reload_n_operands; j++)
+ input_reload_insns[j] = input_address_reload_insns[j]
+ = output_reload_insns[j] = output_address_reload_insns[j] = 0;
+
+ /* Now output the instructions to copy the data into and out of the
+ reload registers. Do these in the order that the reloads were reported,
+ since reloads of base and index registers precede reloads of operands
+ and the operands may need the base and index registers reloaded. */
+
+ for (j = 0; j < n_reloads; j++)
+ {
+ register rtx old;
+ rtx oldequiv_reg = 0;
+ rtx store_insn = 0;
+
+ old = reload_in[j];
+ if (old != 0 && ! reload_inherited[j]
+ && ! rtx_equal_p (reload_reg_rtx[j], old)
+ && reload_reg_rtx[j] != 0)
+ {
+ register rtx reloadreg = reload_reg_rtx[j];
+ rtx oldequiv = 0;
+ enum machine_mode mode;
+ rtx *where;
+
+ /* Determine the mode to reload in.
+ This is very tricky because we have three to choose from.
+ There is the mode the insn operand wants (reload_inmode[J]).
+ There is the mode of the reload register RELOADREG.
+ There is the intrinsic mode of the operand, which we could find
+ by stripping some SUBREGs.
+ It turns out that RELOADREG's mode is irrelevant:
+ we can change that arbitrarily.
+
+ Consider (SUBREG:SI foo:QI) as an operand that must be SImode;
+ then the reload reg may not support QImode moves, so use SImode.
+ If foo is in memory due to spilling a pseudo reg, this is safe,
+ because the QImode value is in the least significant part of a
+ slot big enough for a SImode. If foo is some other sort of
+ memory reference, then it is impossible to reload this case,
+ so previous passes had better make sure this never happens.
+
+ Then consider a one-word union which has SImode and one of its
+ members is a float, being fetched as (SUBREG:SF union:SI).
+ We must fetch that as SFmode because we could be loading into
+ a float-only register. In this case OLD's mode is correct.
+
+ Consider an immediate integer: it has VOIDmode. Here we need
+ to get a mode from something else.
+
+ In some cases, there is a fourth mode, the operand's
+ containing mode. If the insn specifies a containing mode for
+ this operand, it overrides all others.
+
+ I am not sure whether the algorithm here is always right,
+ but it does the right things in those cases. */
+
+ mode = GET_MODE (old);
+ if (mode == VOIDmode)
+ mode = reload_inmode[j];
+
+#ifdef SECONDARY_INPUT_RELOAD_CLASS
+ /* If we need a secondary register for this operation, see if
+ the value is already in a register in that class. Don't
+ do this if the secondary register will be used as a scratch
+ register. */
+
+ if (reload_secondary_in_reload[j] >= 0
+ && reload_secondary_in_icode[j] == CODE_FOR_nothing
+ && optimize)
+ oldequiv
+ = find_equiv_reg (old, insn,
+ reload_reg_class[reload_secondary_in_reload[j]],
+ -1, NULL_PTR, 0, mode);
+#endif
+
+ /* If reloading from memory, see if there is a register
+ that already holds the same value. If so, reload from there.
+ We can pass 0 as the reload_reg_p argument because
+ any other reload has either already been emitted,
+ in which case find_equiv_reg will see the reload-insn,
+ or has yet to be emitted, in which case it doesn't matter
+ because we will use this equiv reg right away. */
+
+ if (oldequiv == 0 && optimize
+ && (GET_CODE (old) == MEM
+ || (GET_CODE (old) == REG
+ && REGNO (old) >= FIRST_PSEUDO_REGISTER
+ && reg_renumber[REGNO (old)] < 0)))
+ oldequiv = find_equiv_reg (old, insn, ALL_REGS,
+ -1, NULL_PTR, 0, mode);
+
+ if (oldequiv)
+ {
+ int regno = true_regnum (oldequiv);
+
+ /* If OLDEQUIV is a spill register, don't use it for this
+ if any other reload needs it at an earlier stage of this insn
+ or at this stage. */
+ if (spill_reg_order[regno] >= 0
+ && (! reload_reg_free_p (regno, reload_opnum[j],
+ reload_when_needed[j])
+ || ! reload_reg_free_before_p (regno, reload_opnum[j],
+ reload_when_needed[j])))
+ oldequiv = 0;
+
+ /* If OLDEQUIV is not a spill register,
+ don't use it if any other reload wants it. */
+ if (spill_reg_order[regno] < 0)
+ {
+ int k;
+ for (k = 0; k < n_reloads; k++)
+ if (reload_reg_rtx[k] != 0 && k != j
+ && reg_overlap_mentioned_for_reload_p (reload_reg_rtx[k],
+ oldequiv))
+ {
+ oldequiv = 0;
+ break;
+ }
+ }
+
+ /* If it is no cheaper to copy from OLDEQUIV into the
+ reload register than it would be to move from memory,
+ don't use it. Likewise, if we need a secondary register
+ or memory. */
+
+ if (oldequiv != 0
+ && ((REGNO_REG_CLASS (regno) != reload_reg_class[j]
+ && (REGISTER_MOVE_COST (REGNO_REG_CLASS (regno),
+ reload_reg_class[j])
+ >= MEMORY_MOVE_COST (mode)))
+#ifdef SECONDARY_INPUT_RELOAD_CLASS
+ || (SECONDARY_INPUT_RELOAD_CLASS (reload_reg_class[j],
+ mode, oldequiv)
+ != NO_REGS)
+#endif
+#ifdef SECONDARY_MEMORY_NEEDED
+ || SECONDARY_MEMORY_NEEDED (reload_reg_class[j],
+ REGNO_REG_CLASS (regno),
+ mode)
+#endif
+ ))
+ oldequiv = 0;
+ }
+
+ if (oldequiv == 0)
+ oldequiv = old;
+ else if (GET_CODE (oldequiv) == REG)
+ oldequiv_reg = oldequiv;
+ else if (GET_CODE (oldequiv) == SUBREG)
+ oldequiv_reg = SUBREG_REG (oldequiv);
+
+ /* If we are reloading from a register that was recently stored in
+ with an output-reload, see if we can prove there was
+ actually no need to store the old value in it. */
+
+ if (optimize && GET_CODE (oldequiv) == REG
+ && REGNO (oldequiv) < FIRST_PSEUDO_REGISTER
+ && spill_reg_order[REGNO (oldequiv)] >= 0
+ && spill_reg_store[spill_reg_order[REGNO (oldequiv)]] != 0
+ && find_reg_note (insn, REG_DEAD, reload_in[j])
+ /* This is unsafe if operand occurs more than once in current
+ insn. Perhaps some occurrences weren't reloaded. */
+ && count_occurrences (PATTERN (insn), reload_in[j]) == 1)
+ delete_output_reload
+ (insn, j, spill_reg_store[spill_reg_order[REGNO (oldequiv)]]);
+
+ /* Encapsulate both RELOADREG and OLDEQUIV into that mode,
+ then load RELOADREG from OLDEQUIV. Note that we cannot use
+ gen_lowpart_common since it can do the wrong thing when
+ RELOADREG has a multi-word mode. Note that RELOADREG
+ must always be a REG here. */
+
+ if (GET_MODE (reloadreg) != mode)
+ reloadreg = gen_rtx (REG, mode, REGNO (reloadreg));
+ while (GET_CODE (oldequiv) == SUBREG && GET_MODE (oldequiv) != mode)
+ oldequiv = SUBREG_REG (oldequiv);
+ if (GET_MODE (oldequiv) != VOIDmode
+ && mode != GET_MODE (oldequiv))
+ oldequiv = gen_rtx (SUBREG, mode, oldequiv, 0);
+
+ /* Switch to the right place to emit the reload insns. */
+ switch (reload_when_needed[j])
+ {
+ case RELOAD_OTHER:
+ where = &other_input_reload_insns;
+ break;
+ case RELOAD_FOR_INPUT:
+ where = &input_reload_insns[reload_opnum[j]];
+ break;
+ case RELOAD_FOR_INPUT_ADDRESS:
+ where = &input_address_reload_insns[reload_opnum[j]];
+ break;
+ case RELOAD_FOR_OUTPUT_ADDRESS:
+ where = &output_address_reload_insns[reload_opnum[j]];
+ break;
+ case RELOAD_FOR_OPERAND_ADDRESS:
+ where = &operand_reload_insns;
+ break;
+ case RELOAD_FOR_OPADDR_ADDR:
+ where = &other_operand_reload_insns;
+ break;
+ case RELOAD_FOR_OTHER_ADDRESS:
+ where = &other_input_address_reload_insns;
+ break;
+ default:
+ abort ();
+ }
+
+ push_to_sequence (*where);
+ special = 0;
+
+ /* Auto-increment addresses must be reloaded in a special way. */
+ if (GET_CODE (oldequiv) == POST_INC
+ || GET_CODE (oldequiv) == POST_DEC
+ || GET_CODE (oldequiv) == PRE_INC
+ || GET_CODE (oldequiv) == PRE_DEC)
+ {
+ /* We are not going to bother supporting the case where a
+ incremented register can't be copied directly from
+ OLDEQUIV since this seems highly unlikely. */
+ if (reload_secondary_in_reload[j] >= 0)
+ abort ();
+ /* Prevent normal processing of this reload. */
+ special = 1;
+ /* Output a special code sequence for this case. */
+ inc_for_reload (reloadreg, oldequiv, reload_inc[j]);
+ }
+
+ /* If we are reloading a pseudo-register that was set by the previous
+ insn, see if we can get rid of that pseudo-register entirely
+ by redirecting the previous insn into our reload register. */
+
+ else if (optimize && GET_CODE (old) == REG
+ && REGNO (old) >= FIRST_PSEUDO_REGISTER
+ && dead_or_set_p (insn, old)
+ /* This is unsafe if some other reload
+ uses the same reg first. */
+ && reload_reg_free_before_p (REGNO (reloadreg),
+ reload_opnum[j],
+ reload_when_needed[j]))
+ {
+ rtx temp = PREV_INSN (insn);
+ while (temp && GET_CODE (temp) == NOTE)
+ temp = PREV_INSN (temp);
+ if (temp
+ && GET_CODE (temp) == INSN
+ && GET_CODE (PATTERN (temp)) == SET
+ && SET_DEST (PATTERN (temp)) == old
+ /* Make sure we can access insn_operand_constraint. */
+ && asm_noperands (PATTERN (temp)) < 0
+ /* This is unsafe if prev insn rejects our reload reg. */
+ && constraint_accepts_reg_p (insn_operand_constraint[recog_memoized (temp)][0],
+ reloadreg)
+ /* This is unsafe if operand occurs more than once in current
+ insn. Perhaps some occurrences aren't reloaded. */
+ && count_occurrences (PATTERN (insn), old) == 1
+ /* Don't risk splitting a matching pair of operands. */
+ && ! reg_mentioned_p (old, SET_SRC (PATTERN (temp))))
+ {
+ /* Store into the reload register instead of the pseudo. */
+ SET_DEST (PATTERN (temp)) = reloadreg;
+ /* If these are the only uses of the pseudo reg,
+ pretend for GDB it lives in the reload reg we used. */
+ if (reg_n_deaths[REGNO (old)] == 1
+ && reg_n_sets[REGNO (old)] == 1)
+ {
+ reg_renumber[REGNO (old)] = REGNO (reload_reg_rtx[j]);
+ alter_reg (REGNO (old), -1);
+ }
+ special = 1;
+ }
+ }
+
+ /* We can't do that, so output an insn to load RELOADREG. */
+
+ if (! special)
+ {
+#ifdef SECONDARY_INPUT_RELOAD_CLASS
+ rtx second_reload_reg = 0;
+ enum insn_code icode;
+
+ /* If we have a secondary reload, pick up the secondary register
+ and icode, if any. If OLDEQUIV and OLD are different or
+ if this is an in-out reload, recompute whether or not we
+ still need a secondary register and what the icode should
+ be. If we still need a secondary register and the class or
+ icode is different, go back to reloading from OLD if using
+ OLDEQUIV means that we got the wrong type of register. We
+ cannot have different class or icode due to an in-out reload
+ because we don't make such reloads when both the input and
+ output need secondary reload registers. */
+
+ if (reload_secondary_in_reload[j] >= 0)
+ {
+ int secondary_reload = reload_secondary_in_reload[j];
+ rtx real_oldequiv = oldequiv;
+ rtx real_old = old;
+
+ /* If OLDEQUIV is a pseudo with a MEM, get the real MEM
+ and similarly for OLD.
+ See comments in get_secondary_reload in reload.c. */
+ if (GET_CODE (oldequiv) == REG
+ && REGNO (oldequiv) >= FIRST_PSEUDO_REGISTER
+ && reg_equiv_mem[REGNO (oldequiv)] != 0)
+ real_oldequiv = reg_equiv_mem[REGNO (oldequiv)];
+
+ if (GET_CODE (old) == REG
+ && REGNO (old) >= FIRST_PSEUDO_REGISTER
+ && reg_equiv_mem[REGNO (old)] != 0)
+ real_old = reg_equiv_mem[REGNO (old)];
+
+ second_reload_reg = reload_reg_rtx[secondary_reload];
+ icode = reload_secondary_in_icode[j];
+
+ if ((old != oldequiv && ! rtx_equal_p (old, oldequiv))
+ || (reload_in[j] != 0 && reload_out[j] != 0))
+ {
+ enum reg_class new_class
+ = SECONDARY_INPUT_RELOAD_CLASS (reload_reg_class[j],
+ mode, real_oldequiv);
+
+ if (new_class == NO_REGS)
+ second_reload_reg = 0;
+ else
+ {
+ enum insn_code new_icode;
+ enum machine_mode new_mode;
+
+ if (! TEST_HARD_REG_BIT (reg_class_contents[(int) new_class],
+ REGNO (second_reload_reg)))
+ oldequiv = old, real_oldequiv = real_old;
+ else
+ {
+ new_icode = reload_in_optab[(int) mode];
+ if (new_icode != CODE_FOR_nothing
+ && ((insn_operand_predicate[(int) new_icode][0]
+ && ! ((*insn_operand_predicate[(int) new_icode][0])
+ (reloadreg, mode)))
+ || (insn_operand_predicate[(int) new_icode][1]
+ && ! ((*insn_operand_predicate[(int) new_icode][1])
+ (real_oldequiv, mode)))))
+ new_icode = CODE_FOR_nothing;
+
+ if (new_icode == CODE_FOR_nothing)
+ new_mode = mode;
+ else
+ new_mode = insn_operand_mode[(int) new_icode][2];
+
+ if (GET_MODE (second_reload_reg) != new_mode)
+ {
+ if (!HARD_REGNO_MODE_OK (REGNO (second_reload_reg),
+ new_mode))
+ oldequiv = old, real_oldequiv = real_old;
+ else
+ second_reload_reg
+ = gen_rtx (REG, new_mode,
+ REGNO (second_reload_reg));
+ }
+ }
+ }
+ }
+
+ /* If we still need a secondary reload register, check
+ to see if it is being used as a scratch or intermediate
+ register and generate code appropriately. If we need
+ a scratch register, use REAL_OLDEQUIV since the form of
+ the insn may depend on the actual address if it is
+ a MEM. */
+
+ if (second_reload_reg)
+ {
+ if (icode != CODE_FOR_nothing)
+ {
+ emit_insn (GEN_FCN (icode) (reloadreg, real_oldequiv,
+ second_reload_reg));
+ special = 1;
+ }
+ else
+ {
+ /* See if we need a scratch register to load the
+ intermediate register (a tertiary reload). */
+ enum insn_code tertiary_icode
+ = reload_secondary_in_icode[secondary_reload];
+
+ if (tertiary_icode != CODE_FOR_nothing)
+ {
+ rtx third_reload_reg
+ = reload_reg_rtx[reload_secondary_in_reload[secondary_reload]];
+
+ emit_insn ((GEN_FCN (tertiary_icode)
+ (second_reload_reg, real_oldequiv,
+ third_reload_reg)));
+ }
+ else
+ gen_input_reload (second_reload_reg, oldequiv,
+ reload_opnum[j],
+ reload_when_needed[j]);
+
+ oldequiv = second_reload_reg;
+ }
+ }
+ }
+#endif
+
+ if (! special && ! rtx_equal_p (reloadreg, oldequiv))
+ gen_input_reload (reloadreg, oldequiv, reload_opnum[j],
+ reload_when_needed[j]);
+
+#if defined(SECONDARY_INPUT_RELOAD_CLASS) && defined(PRESERVE_DEATH_INFO_REGNO_P)
+ /* We may have to make a REG_DEAD note for the secondary reload
+ register in the insns we just made. Find the last insn that
+ mentioned the register. */
+ if (! special && second_reload_reg
+ && PRESERVE_DEATH_INFO_REGNO_P (REGNO (second_reload_reg)))
+ {
+ rtx prev;
+
+ for (prev = get_last_insn (); prev;
+ prev = PREV_INSN (prev))
+ if (GET_RTX_CLASS (GET_CODE (prev) == 'i')
+ && reg_overlap_mentioned_for_reload_p (second_reload_reg,
+ PATTERN (prev)))
+ {
+ REG_NOTES (prev) = gen_rtx (EXPR_LIST, REG_DEAD,
+ second_reload_reg,
+ REG_NOTES (prev));
+ break;
+ }
+ }
+#endif
+ }
+
+ /* End this sequence. */
+ *where = get_insns ();
+ end_sequence ();
+ }
+
+ /* Add a note saying the input reload reg
+ dies in this insn, if anyone cares. */
+#ifdef PRESERVE_DEATH_INFO_REGNO_P
+ if (old != 0
+ && reload_reg_rtx[j] != old
+ && reload_reg_rtx[j] != 0
+ && reload_out[j] == 0
+ && ! reload_inherited[j]
+ && PRESERVE_DEATH_INFO_REGNO_P (REGNO (reload_reg_rtx[j])))
+ {
+ register rtx reloadreg = reload_reg_rtx[j];
+
+#if 0
+ /* We can't abort here because we need to support this for sched.c.
+ It's not terrible to miss a REG_DEAD note, but we should try
+ to figure out how to do this correctly. */
+ /* The code below is incorrect for address-only reloads. */
+ if (reload_when_needed[j] != RELOAD_OTHER
+ && reload_when_needed[j] != RELOAD_FOR_INPUT)
+ abort ();
+#endif
+
+ /* Add a death note to this insn, for an input reload. */
+
+ if ((reload_when_needed[j] == RELOAD_OTHER
+ || reload_when_needed[j] == RELOAD_FOR_INPUT)
+ && ! dead_or_set_p (insn, reloadreg))
+ REG_NOTES (insn)
+ = gen_rtx (EXPR_LIST, REG_DEAD,
+ reloadreg, REG_NOTES (insn));
+ }
+
+ /* When we inherit a reload, the last marked death of the reload reg
+ may no longer really be a death. */
+ if (reload_reg_rtx[j] != 0
+ && PRESERVE_DEATH_INFO_REGNO_P (REGNO (reload_reg_rtx[j]))
+ && reload_inherited[j])
+ {
+ /* Handle inheriting an output reload.
+ Remove the death note from the output reload insn. */
+ if (reload_spill_index[j] >= 0
+ && GET_CODE (reload_in[j]) == REG
+ && spill_reg_store[reload_spill_index[j]] != 0
+ && find_regno_note (spill_reg_store[reload_spill_index[j]],
+ REG_DEAD, REGNO (reload_reg_rtx[j])))
+ remove_death (REGNO (reload_reg_rtx[j]),
+ spill_reg_store[reload_spill_index[j]]);
+ /* Likewise for input reloads that were inherited. */
+ else if (reload_spill_index[j] >= 0
+ && GET_CODE (reload_in[j]) == REG
+ && spill_reg_store[reload_spill_index[j]] == 0
+ && reload_inheritance_insn[j] != 0
+ && find_regno_note (reload_inheritance_insn[j], REG_DEAD,
+ REGNO (reload_reg_rtx[j])))
+ remove_death (REGNO (reload_reg_rtx[j]),
+ reload_inheritance_insn[j]);
+ else
+ {
+ rtx prev;
+
+ /* We got this register from find_equiv_reg.
+ Search back for its last death note and get rid of it.
+ But don't search back too far.
+ Don't go past a place where this reg is set,
+ since a death note before that remains valid. */
+ for (prev = PREV_INSN (insn);
+ prev && GET_CODE (prev) != CODE_LABEL;
+ prev = PREV_INSN (prev))
+ if (GET_RTX_CLASS (GET_CODE (prev)) == 'i'
+ && dead_or_set_p (prev, reload_reg_rtx[j]))
+ {
+ if (find_regno_note (prev, REG_DEAD,
+ REGNO (reload_reg_rtx[j])))
+ remove_death (REGNO (reload_reg_rtx[j]), prev);
+ break;
+ }
+ }
+ }
+
+ /* We might have used find_equiv_reg above to choose an alternate
+ place from which to reload. If so, and it died, we need to remove
+ that death and move it to one of the insns we just made. */
+
+ if (oldequiv_reg != 0
+ && PRESERVE_DEATH_INFO_REGNO_P (true_regnum (oldequiv_reg)))
+ {
+ rtx prev, prev1;
+
+ for (prev = PREV_INSN (insn); prev && GET_CODE (prev) != CODE_LABEL;
+ prev = PREV_INSN (prev))
+ if (GET_RTX_CLASS (GET_CODE (prev)) == 'i'
+ && dead_or_set_p (prev, oldequiv_reg))
+ {
+ if (find_regno_note (prev, REG_DEAD, REGNO (oldequiv_reg)))
+ {
+ for (prev1 = this_reload_insn;
+ prev1; prev1 = PREV_INSN (prev1))
+ if (GET_RTX_CLASS (GET_CODE (prev1) == 'i')
+ && reg_overlap_mentioned_for_reload_p (oldequiv_reg,
+ PATTERN (prev1)))
+ {
+ REG_NOTES (prev1) = gen_rtx (EXPR_LIST, REG_DEAD,
+ oldequiv_reg,
+ REG_NOTES (prev1));
+ break;
+ }
+ remove_death (REGNO (oldequiv_reg), prev);
+ }
+ break;
+ }
+ }
+#endif
+
+ /* If we are reloading a register that was recently stored in with an
+ output-reload, see if we can prove there was
+ actually no need to store the old value in it. */
+
+ if (optimize && reload_inherited[j] && reload_spill_index[j] >= 0
+ && reload_in[j] != 0
+ && GET_CODE (reload_in[j]) == REG
+#if 0
+ /* There doesn't seem to be any reason to restrict this to pseudos
+ and doing so loses in the case where we are copying from a
+ register of the wrong class. */
+ && REGNO (reload_in[j]) >= FIRST_PSEUDO_REGISTER
+#endif
+ && spill_reg_store[reload_spill_index[j]] != 0
+ /* This is unsafe if some other reload uses the same reg first. */
+ && reload_reg_free_before_p (spill_regs[reload_spill_index[j]],
+ reload_opnum[j], reload_when_needed[j])
+ && dead_or_set_p (insn, reload_in[j])
+ /* This is unsafe if operand occurs more than once in current
+ insn. Perhaps some occurrences weren't reloaded. */
+ && count_occurrences (PATTERN (insn), reload_in[j]) == 1)
+ delete_output_reload (insn, j,
+ spill_reg_store[reload_spill_index[j]]);
+
+ /* Input-reloading is done. Now do output-reloading,
+ storing the value from the reload-register after the main insn
+ if reload_out[j] is nonzero.
+
+ ??? At some point we need to support handling output reloads of
+ JUMP_INSNs or insns that set cc0. */
+ old = reload_out[j];
+ if (old != 0
+ && reload_reg_rtx[j] != old
+ && reload_reg_rtx[j] != 0)
+ {
+ register rtx reloadreg = reload_reg_rtx[j];
+ register rtx second_reloadreg = 0;
+ rtx note, p;
+ enum machine_mode mode;
+ int special = 0;
+
+ /* An output operand that dies right away does need a reload,
+ but need not be copied from it. Show the new location in the
+ REG_UNUSED note. */
+ if ((GET_CODE (old) == REG || GET_CODE (old) == SCRATCH)
+ && (note = find_reg_note (insn, REG_UNUSED, old)) != 0)
+ {
+ XEXP (note, 0) = reload_reg_rtx[j];
+ continue;
+ }
+ else if (GET_CODE (old) == SCRATCH)
+ /* If we aren't optimizing, there won't be a REG_UNUSED note,
+ but we don't want to make an output reload. */
+ continue;
+
+#if 0
+ /* Strip off of OLD any size-increasing SUBREGs such as
+ (SUBREG:SI foo:QI 0). */
+
+ while (GET_CODE (old) == SUBREG && SUBREG_WORD (old) == 0
+ && (GET_MODE_SIZE (GET_MODE (old))
+ > GET_MODE_SIZE (GET_MODE (SUBREG_REG (old)))))
+ old = SUBREG_REG (old);
+#endif
+
+ /* If is a JUMP_INSN, we can't support output reloads yet. */
+ if (GET_CODE (insn) == JUMP_INSN)
+ abort ();
+
+ push_to_sequence (output_reload_insns[reload_opnum[j]]);
+
+ /* Determine the mode to reload in.
+ See comments above (for input reloading). */
+
+ mode = GET_MODE (old);
+ if (mode == VOIDmode)
+ {
+ /* VOIDmode should never happen for an output. */
+ if (asm_noperands (PATTERN (insn)) < 0)
+ /* It's the compiler's fault. */
+ abort ();
+ error_for_asm (insn, "output operand is constant in `asm'");
+ /* Prevent crash--use something we know is valid. */
+ mode = word_mode;
+ old = gen_rtx (REG, mode, REGNO (reloadreg));
+ }
+
+ if (GET_MODE (reloadreg) != mode)
+ reloadreg = gen_rtx (REG, mode, REGNO (reloadreg));
+
+#ifdef SECONDARY_OUTPUT_RELOAD_CLASS
+
+ /* If we need two reload regs, set RELOADREG to the intermediate
+ one, since it will be stored into OUT. We might need a secondary
+ register only for an input reload, so check again here. */
+
+ if (reload_secondary_out_reload[j] >= 0)
+ {
+ rtx real_old = old;
+
+ if (GET_CODE (old) == REG && REGNO (old) >= FIRST_PSEUDO_REGISTER
+ && reg_equiv_mem[REGNO (old)] != 0)
+ real_old = reg_equiv_mem[REGNO (old)];
+
+ if((SECONDARY_OUTPUT_RELOAD_CLASS (reload_reg_class[j],
+ mode, real_old)
+ != NO_REGS))
+ {
+ second_reloadreg = reloadreg;
+ reloadreg = reload_reg_rtx[reload_secondary_out_reload[j]];
+
+ /* See if RELOADREG is to be used as a scratch register
+ or as an intermediate register. */
+ if (reload_secondary_out_icode[j] != CODE_FOR_nothing)
+ {
+ emit_insn ((GEN_FCN (reload_secondary_out_icode[j])
+ (real_old, second_reloadreg, reloadreg)));
+ special = 1;
+ }
+ else
+ {
+ /* See if we need both a scratch and intermediate reload
+ register. */
+ int secondary_reload = reload_secondary_out_reload[j];
+ enum insn_code tertiary_icode
+ = reload_secondary_out_icode[secondary_reload];
+ rtx pat;
+
+ if (GET_MODE (reloadreg) != mode)
+ reloadreg = gen_rtx (REG, mode, REGNO (reloadreg));
+
+ if (tertiary_icode != CODE_FOR_nothing)
+ {
+ rtx third_reloadreg
+ = reload_reg_rtx[reload_secondary_out_reload[secondary_reload]];
+ pat = (GEN_FCN (tertiary_icode)
+ (reloadreg, second_reloadreg, third_reloadreg));
+ }
+#ifdef SECONDARY_MEMORY_NEEDED
+ /* If we need a memory location to do the move, do it that way. */
+ else if (GET_CODE (reloadreg) == REG
+ && REGNO (reloadreg) < FIRST_PSEUDO_REGISTER
+ && SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (REGNO (reloadreg)),
+ REGNO_REG_CLASS (REGNO (second_reloadreg)),
+ GET_MODE (second_reloadreg)))
+ {
+ /* Get the memory to use and rewrite both registers
+ to its mode. */
+ rtx loc
+ = get_secondary_mem (reloadreg,
+ GET_MODE (second_reloadreg),
+ reload_opnum[j],
+ reload_when_needed[j]);
+ rtx tmp_reloadreg;
+
+ if (GET_MODE (loc) != GET_MODE (second_reloadreg))
+ second_reloadreg = gen_rtx (REG, GET_MODE (loc),
+ REGNO (second_reloadreg));
+
+ if (GET_MODE (loc) != GET_MODE (reloadreg))
+ tmp_reloadreg = gen_rtx (REG, GET_MODE (loc),
+ REGNO (reloadreg));
+ else
+ tmp_reloadreg = reloadreg;
+
+ emit_move_insn (loc, second_reloadreg);
+ pat = gen_move_insn (tmp_reloadreg, loc);
+ }
+#endif
+ else
+ pat = gen_move_insn (reloadreg, second_reloadreg);
+
+ emit_insn (pat);
+ }
+ }
+ }
+#endif
+
+ /* Output the last reload insn. */
+ if (! special)
+ {
+#ifdef SECONDARY_MEMORY_NEEDED
+ /* If we need a memory location to do the move, do it that way. */
+ if (GET_CODE (old) == REG && REGNO (old) < FIRST_PSEUDO_REGISTER
+ && SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (REGNO (old)),
+ REGNO_REG_CLASS (REGNO (reloadreg)),
+ GET_MODE (reloadreg)))
+ {
+ /* Get the memory to use and rewrite both registers to
+ its mode. */
+ rtx loc = get_secondary_mem (old, GET_MODE (reloadreg),
+ reload_opnum[j],
+ reload_when_needed[j]);
+
+ if (GET_MODE (loc) != GET_MODE (reloadreg))
+ reloadreg = gen_rtx (REG, GET_MODE (loc),
+ REGNO (reloadreg));
+
+ if (GET_MODE (loc) != GET_MODE (old))
+ old = gen_rtx (REG, GET_MODE (loc), REGNO (old));
+
+ emit_insn (gen_move_insn (loc, reloadreg));
+ emit_insn (gen_move_insn (old, loc));
+ }
+ else
+#endif
+ emit_insn (gen_move_insn (old, reloadreg));
+ }
+
+#ifdef PRESERVE_DEATH_INFO_REGNO_P
+ /* If final will look at death notes for this reg,
+ put one on the last output-reload insn to use it. Similarly
+ for any secondary register. */
+ if (PRESERVE_DEATH_INFO_REGNO_P (REGNO (reloadreg)))
+ for (p = get_last_insn (); p; p = PREV_INSN (p))
+ if (GET_RTX_CLASS (GET_CODE (p)) == 'i'
+ && reg_overlap_mentioned_for_reload_p (reloadreg,
+ PATTERN (p)))
+ REG_NOTES (p) = gen_rtx (EXPR_LIST, REG_DEAD,
+ reloadreg, REG_NOTES (p));
+
+#ifdef SECONDARY_OUTPUT_RELOAD_CLASS
+ if (! special
+ && PRESERVE_DEATH_INFO_REGNO_P (REGNO (second_reloadreg)))
+ for (p = get_last_insn (); p; p = PREV_INSN (p))
+ if (GET_RTX_CLASS (GET_CODE (p)) == 'i'
+ && reg_overlap_mentioned_for_reload_p (second_reloadreg,
+ PATTERN (p)))
+ REG_NOTES (p) = gen_rtx (EXPR_LIST, REG_DEAD,
+ second_reloadreg, REG_NOTES (p));
+#endif
+#endif
+ /* Look at all insns we emitted, just to be safe. */
+ for (p = get_insns (); p; p = NEXT_INSN (p))
+ if (GET_RTX_CLASS (GET_CODE (p)) == 'i')
+ {
+ /* If this output reload doesn't come from a spill reg,
+ clear any memory of reloaded copies of the pseudo reg.
+ If this output reload comes from a spill reg,
+ reg_has_output_reload will make this do nothing. */
+ note_stores (PATTERN (p), forget_old_reloads_1);
+
+ if (reg_mentioned_p (reload_reg_rtx[j], PATTERN (p)))
+ store_insn = p;
+ }
+
+ output_reload_insns[reload_opnum[j]] = get_insns ();
+ end_sequence ();
+
+ }
+
+ if (reload_spill_index[j] >= 0)
+ new_spill_reg_store[reload_spill_index[j]] = store_insn;
+ }
+
+ /* Now write all the insns we made for reloads in the order expected by
+ the allocation functions. Prior to the insn being reloaded, we write
+ the following reloads:
+
+ RELOAD_FOR_OTHER_ADDRESS reloads for input addresses.
+
+ RELOAD_OTHER reloads.
+
+ For each operand, any RELOAD_FOR_INPUT_ADDRESS reloads followed by
+ the RELOAD_FOR_INPUT reload for the operand.
+
+ RELOAD_FOR_OPADDR_ADDRS reloads.
+
+ RELOAD_FOR_OPERAND_ADDRESS reloads.
+
+ After the insn being reloaded, we write the following:
+
+ For each operand, any RELOAD_FOR_OUTPUT_ADDRESS reload followed by
+ the RELOAD_FOR_OUTPUT reload for that operand. */
+
+ emit_insns_before (other_input_address_reload_insns, before_insn);
+ emit_insns_before (other_input_reload_insns, before_insn);
+
+ for (j = 0; j < reload_n_operands; j++)
+ {
+ emit_insns_before (input_address_reload_insns[j], before_insn);
+ emit_insns_before (input_reload_insns[j], before_insn);
+ }
+
+ emit_insns_before (other_operand_reload_insns, before_insn);
+ emit_insns_before (operand_reload_insns, before_insn);
+
+ for (j = 0; j < reload_n_operands; j++)
+ {
+ emit_insns_before (output_address_reload_insns[j], following_insn);
+ emit_insns_before (output_reload_insns[j], following_insn);
+ }
+
+ /* Move death notes from INSN
+ to output-operand-address and output reload insns. */
+#ifdef PRESERVE_DEATH_INFO_REGNO_P
+ {
+ rtx insn1;
+ /* Loop over those insns, last ones first. */
+ for (insn1 = PREV_INSN (following_insn); insn1 != insn;
+ insn1 = PREV_INSN (insn1))
+ if (GET_CODE (insn1) == INSN && GET_CODE (PATTERN (insn1)) == SET)
+ {
+ rtx source = SET_SRC (PATTERN (insn1));
+ rtx dest = SET_DEST (PATTERN (insn1));
+
+ /* The note we will examine next. */
+ rtx reg_notes = REG_NOTES (insn);
+ /* The place that pointed to this note. */
+ rtx *prev_reg_note = &REG_NOTES (insn);
+
+ /* If the note is for something used in the source of this
+ reload insn, or in the output address, move the note. */
+ while (reg_notes)
+ {
+ rtx next_reg_notes = XEXP (reg_notes, 1);
+ if (REG_NOTE_KIND (reg_notes) == REG_DEAD
+ && GET_CODE (XEXP (reg_notes, 0)) == REG
+ && ((GET_CODE (dest) != REG
+ && reg_overlap_mentioned_for_reload_p (XEXP (reg_notes, 0),
+ dest))
+ || reg_overlap_mentioned_for_reload_p (XEXP (reg_notes, 0),
+ source)))
+ {
+ *prev_reg_note = next_reg_notes;
+ XEXP (reg_notes, 1) = REG_NOTES (insn1);
+ REG_NOTES (insn1) = reg_notes;
+ }
+ else
+ prev_reg_note = &XEXP (reg_notes, 1);
+
+ reg_notes = next_reg_notes;
+ }
+ }
+ }
+#endif
+
+ /* For all the spill regs newly reloaded in this instruction,
+ record what they were reloaded from, so subsequent instructions
+ can inherit the reloads.
+
+ Update spill_reg_store for the reloads of this insn.
+ Copy the elements that were updated in the loop above. */
+
+ for (j = 0; j < n_reloads; j++)
+ {
+ register int r = reload_order[j];
+ register int i = reload_spill_index[r];
+
+ /* I is nonneg if this reload used one of the spill regs.
+ If reload_reg_rtx[r] is 0, this is an optional reload
+ that we opted to ignore.
+
+ Also ignore reloads that don't reach the end of the insn,
+ since we will eventually see the one that does. */
+
+ if (i >= 0 && reload_reg_rtx[r] != 0
+ && reload_reg_reaches_end_p (spill_regs[i], reload_opnum[r],
+ reload_when_needed[r]))
+ {
+ /* First, clear out memory of what used to be in this spill reg.
+ If consecutive registers are used, clear them all. */
+ int nr
+ = HARD_REGNO_NREGS (spill_regs[i], GET_MODE (reload_reg_rtx[r]));
+ int k;
+
+ for (k = 0; k < nr; k++)
+ {
+ reg_reloaded_contents[spill_reg_order[spill_regs[i] + k]] = -1;
+ reg_reloaded_insn[spill_reg_order[spill_regs[i] + k]] = 0;
+ }
+
+ /* Maybe the spill reg contains a copy of reload_out. */
+ if (reload_out[r] != 0 && GET_CODE (reload_out[r]) == REG)
+ {
+ register int nregno = REGNO (reload_out[r]);
+ int nnr = (nregno >= FIRST_PSEUDO_REGISTER ? 1
+ : HARD_REGNO_NREGS (nregno,
+ GET_MODE (reload_reg_rtx[r])));
+
+ spill_reg_store[i] = new_spill_reg_store[i];
+ reg_last_reload_reg[nregno] = reload_reg_rtx[r];
+
+ /* If NREGNO is a hard register, it may occupy more than
+ one register. If it does, say what is in the
+ rest of the registers assuming that both registers
+ agree on how many words the object takes. If not,
+ invalidate the subsequent registers. */
+
+ if (nregno < FIRST_PSEUDO_REGISTER)
+ for (k = 1; k < nnr; k++)
+ reg_last_reload_reg[nregno + k]
+ = (nr == nnr ? gen_rtx (REG,
+ reg_raw_mode[REGNO (reload_reg_rtx[r]) + k],
+ REGNO (reload_reg_rtx[r]) + k)
+ : 0);
+
+ /* Now do the inverse operation. */
+ for (k = 0; k < nr; k++)
+ {
+ reg_reloaded_contents[spill_reg_order[spill_regs[i] + k]]
+ = (nregno >= FIRST_PSEUDO_REGISTER || nr != nnr ? nregno
+ : nregno + k);
+ reg_reloaded_insn[spill_reg_order[spill_regs[i] + k]] = insn;
+ }
+ }
+
+ /* Maybe the spill reg contains a copy of reload_in. Only do
+ something if there will not be an output reload for
+ the register being reloaded. */
+ else if (reload_out[r] == 0
+ && reload_in[r] != 0
+ && ((GET_CODE (reload_in[r]) == REG
+ && ! reg_has_output_reload[REGNO (reload_in[r])]
+ || (GET_CODE (reload_in_reg[r]) == REG
+ && ! reg_has_output_reload[REGNO (reload_in_reg[r])]))))
+ {
+ register int nregno;
+ int nnr;
+
+ if (GET_CODE (reload_in[r]) == REG)
+ nregno = REGNO (reload_in[r]);
+ else
+ nregno = REGNO (reload_in_reg[r]);
+
+ nnr = (nregno >= FIRST_PSEUDO_REGISTER ? 1
+ : HARD_REGNO_NREGS (nregno,
+ GET_MODE (reload_reg_rtx[r])));
+
+ reg_last_reload_reg[nregno] = reload_reg_rtx[r];
+
+ if (nregno < FIRST_PSEUDO_REGISTER)
+ for (k = 1; k < nnr; k++)
+ reg_last_reload_reg[nregno + k]
+ = (nr == nnr ? gen_rtx (REG,
+ reg_raw_mode[REGNO (reload_reg_rtx[r]) + k],
+ REGNO (reload_reg_rtx[r]) + k)
+ : 0);
+
+ /* Unless we inherited this reload, show we haven't
+ recently done a store. */
+ if (! reload_inherited[r])
+ spill_reg_store[i] = 0;
+
+ for (k = 0; k < nr; k++)
+ {
+ reg_reloaded_contents[spill_reg_order[spill_regs[i] + k]]
+ = (nregno >= FIRST_PSEUDO_REGISTER || nr != nnr ? nregno
+ : nregno + k);
+ reg_reloaded_insn[spill_reg_order[spill_regs[i] + k]]
+ = insn;
+ }
+ }
+ }
+
+ /* The following if-statement was #if 0'd in 1.34 (or before...).
+ It's reenabled in 1.35 because supposedly nothing else
+ deals with this problem. */
+
+ /* If a register gets output-reloaded from a non-spill register,
+ that invalidates any previous reloaded copy of it.
+ But forget_old_reloads_1 won't get to see it, because
+ it thinks only about the original insn. So invalidate it here. */
+ if (i < 0 && reload_out[r] != 0 && GET_CODE (reload_out[r]) == REG)
+ {
+ register int nregno = REGNO (reload_out[r]);
+ int num_regs = HARD_REGNO_NREGS (nregno, GET_MODE (reload_out[r]));
+
+ while (num_regs-- > 0)
+ reg_last_reload_reg[nregno + num_regs] = 0;
+ }
+ }
+}
+
+/* Emit code to perform an input reload of IN to RELOADREG. IN is from
+ operand OPNUM with reload type TYPE.
+
+ Returns first insn emitted. */
+
+rtx
+gen_input_reload (reloadreg, in, opnum, type)
+ rtx reloadreg;
+ rtx in;
+ int opnum;
+ enum reload_type type;
+{
+ rtx last = get_last_insn ();
+
+ /* How to do this reload can get quite tricky. Normally, we are being
+ asked to reload a simple operand, such as a MEM, a constant, or a pseudo
+ register that didn't get a hard register. In that case we can just
+ call emit_move_insn.
+
+ We can also be asked to reload a PLUS that adds a register or a MEM to
+ another register, constant or MEM. This can occur during frame pointer
+ elimination and while reloading addresses. This case is handled by
+ trying to emit a single insn to perform the add. If it is not valid,
+ we use a two insn sequence.
+
+ Finally, we could be called to handle an 'o' constraint by putting
+ an address into a register. In that case, we first try to do this
+ with a named pattern of "reload_load_address". If no such pattern
+ exists, we just emit a SET insn and hope for the best (it will normally
+ be valid on machines that use 'o').
+
+ This entire process is made complex because reload will never
+ process the insns we generate here and so we must ensure that
+ they will fit their constraints and also by the fact that parts of
+ IN might be being reloaded separately and replaced with spill registers.
+ Because of this, we are, in some sense, just guessing the right approach
+ here. The one listed above seems to work.
+
+ ??? At some point, this whole thing needs to be rethought. */
+
+ if (GET_CODE (in) == PLUS
+ && (GET_CODE (XEXP (in, 0)) == REG
+ || GET_CODE (XEXP (in, 0)) == MEM)
+ && (GET_CODE (XEXP (in, 1)) == REG
+ || CONSTANT_P (XEXP (in, 1))
+ || GET_CODE (XEXP (in, 1)) == MEM))
+ {
+ /* We need to compute the sum of a register or a MEM and another
+ register, constant, or MEM, and put it into the reload
+ register. The best possible way of doing this is if the machine
+ has a three-operand ADD insn that accepts the required operands.
+
+ The simplest approach is to try to generate such an insn and see if it
+ is recognized and matches its constraints. If so, it can be used.
+
+ It might be better not to actually emit the insn unless it is valid,
+ but we need to pass the insn as an operand to `recog' and
+ `insn_extract' and it is simpler to emit and then delete the insn if
+ not valid than to dummy things up. */
+
+ rtx op0, op1, tem, insn;
+ int code;
+
+ op0 = find_replacement (&XEXP (in, 0));
+ op1 = find_replacement (&XEXP (in, 1));
+
+ /* Since constraint checking is strict, commutativity won't be
+ checked, so we need to do that here to avoid spurious failure
+ if the add instruction is two-address and the second operand
+ of the add is the same as the reload reg, which is frequently
+ the case. If the insn would be A = B + A, rearrange it so
+ it will be A = A + B as constrain_operands expects. */
+
+ if (GET_CODE (XEXP (in, 1)) == REG
+ && REGNO (reloadreg) == REGNO (XEXP (in, 1)))
+ tem = op0, op0 = op1, op1 = tem;
+
+ if (op0 != XEXP (in, 0) || op1 != XEXP (in, 1))
+ in = gen_rtx (PLUS, GET_MODE (in), op0, op1);
+
+ insn = emit_insn (gen_rtx (SET, VOIDmode, reloadreg, in));
+ code = recog_memoized (insn);
+
+ if (code >= 0)
+ {
+ insn_extract (insn);
+ /* We want constrain operands to treat this insn strictly in
+ its validity determination, i.e., the way it would after reload
+ has completed. */
+ if (constrain_operands (code, 1))
+ return insn;
+ }
+
+ delete_insns_since (last);
+
+ /* If that failed, we must use a conservative two-insn sequence.
+ use move to copy constant, MEM, or pseudo register to the reload
+ register since "move" will be able to handle an arbitrary operand,
+ unlike add which can't, in general. Then add the registers.
+
+ If there is another way to do this for a specific machine, a
+ DEFINE_PEEPHOLE should be specified that recognizes the sequence
+ we emit below. */
+
+ if (CONSTANT_P (op1) || GET_CODE (op1) == MEM
+ || (GET_CODE (op1) == REG
+ && REGNO (op1) >= FIRST_PSEUDO_REGISTER))
+ tem = op0, op0 = op1, op1 = tem;
+
+ emit_insn (gen_move_insn (reloadreg, op0));
+
+ /* If OP0 and OP1 are the same, we can use RELOADREG for OP1.
+ This fixes a problem on the 32K where the stack pointer cannot
+ be used as an operand of an add insn. */
+
+ if (rtx_equal_p (op0, op1))
+ op1 = reloadreg;
+
+ insn = emit_insn (gen_add2_insn (reloadreg, op1));
+
+ /* If that failed, copy the address register to the reload register.
+ Then add the constant to the reload register. */
+
+ code = recog_memoized (insn);
+
+ if (code >= 0)
+ {
+ insn_extract (insn);
+ /* We want constrain operands to treat this insn strictly in
+ its validity determination, i.e., the way it would after reload
+ has completed. */
+ if (constrain_operands (code, 1))
+ return insn;
+ }
+
+ delete_insns_since (last);
+
+ emit_insn (gen_move_insn (reloadreg, op1));
+ emit_insn (gen_add2_insn (reloadreg, op0));
+ }
+
+#ifdef SECONDARY_MEMORY_NEEDED
+ /* If we need a memory location to do the move, do it that way. */
+ else if (GET_CODE (in) == REG && REGNO (in) < FIRST_PSEUDO_REGISTER
+ && SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (REGNO (in)),
+ REGNO_REG_CLASS (REGNO (reloadreg)),
+ GET_MODE (reloadreg)))
+ {
+ /* Get the memory to use and rewrite both registers to its mode. */
+ rtx loc = get_secondary_mem (in, GET_MODE (reloadreg), opnum, type);
+
+ if (GET_MODE (loc) != GET_MODE (reloadreg))
+ reloadreg = gen_rtx (REG, GET_MODE (loc), REGNO (reloadreg));
+
+ if (GET_MODE (loc) != GET_MODE (in))
+ in = gen_rtx (REG, GET_MODE (loc), REGNO (in));
+
+ emit_insn (gen_move_insn (loc, in));
+ emit_insn (gen_move_insn (reloadreg, loc));
+ }
+#endif
+
+ /* If IN is a simple operand, use gen_move_insn. */
+ else if (GET_RTX_CLASS (GET_CODE (in)) == 'o' || GET_CODE (in) == SUBREG)
+ emit_insn (gen_move_insn (reloadreg, in));
+
+#ifdef HAVE_reload_load_address
+ else if (HAVE_reload_load_address)
+ emit_insn (gen_reload_load_address (reloadreg, in));
+#endif
+
+ /* Otherwise, just write (set REGLOADREG IN) and hope for the best. */
+ else
+ emit_insn (gen_rtx (SET, VOIDmode, reloadreg, in));
+
+ /* Return the first insn emitted.
+ We can not just return get_last_insn, because there may have
+ been multiple instructions emitted. Also note that gen_move_insn may
+ emit more than one insn itself, so we can not assume that there is one
+ insn emitted per emit_insn_before call. */
+
+ return last ? NEXT_INSN (last) : get_insns ();
+}
+
+/* Delete a previously made output-reload
+ whose result we now believe is not needed.
+ First we double-check.
+
+ INSN is the insn now being processed.
+ OUTPUT_RELOAD_INSN is the insn of the output reload.
+ J is the reload-number for this insn. */
+
+static void
+delete_output_reload (insn, j, output_reload_insn)
+ rtx insn;
+ int j;
+ rtx output_reload_insn;
+{
+ register rtx i1;
+
+ /* Get the raw pseudo-register referred to. */
+
+ rtx reg = reload_in[j];
+ while (GET_CODE (reg) == SUBREG)
+ reg = SUBREG_REG (reg);
+
+ /* If the pseudo-reg we are reloading is no longer referenced
+ anywhere between the store into it and here,
+ and no jumps or labels intervene, then the value can get
+ here through the reload reg alone.
+ Otherwise, give up--return. */
+ for (i1 = NEXT_INSN (output_reload_insn);
+ i1 != insn; i1 = NEXT_INSN (i1))
+ {
+ if (GET_CODE (i1) == CODE_LABEL || GET_CODE (i1) == JUMP_INSN)
+ return;
+ if ((GET_CODE (i1) == INSN || GET_CODE (i1) == CALL_INSN)
+ && reg_mentioned_p (reg, PATTERN (i1)))
+ return;
+ }
+
+ if (cannot_omit_stores[REGNO (reg)])
+ return;
+
+ /* If this insn will store in the pseudo again,
+ the previous store can be removed. */
+ if (reload_out[j] == reload_in[j])
+ delete_insn (output_reload_insn);
+
+ /* See if the pseudo reg has been completely replaced
+ with reload regs. If so, delete the store insn
+ and forget we had a stack slot for the pseudo. */
+ else if (reg_n_deaths[REGNO (reg)] == 1
+ && reg_basic_block[REGNO (reg)] >= 0
+ && find_regno_note (insn, REG_DEAD, REGNO (reg)))
+ {
+ rtx i2;
+
+ /* We know that it was used only between here
+ and the beginning of the current basic block.
+ (We also know that the last use before INSN was
+ the output reload we are thinking of deleting, but never mind that.)
+ Search that range; see if any ref remains. */
+ for (i2 = PREV_INSN (insn); i2; i2 = PREV_INSN (i2))
+ {
+ rtx set = single_set (i2);
+
+ /* Uses which just store in the pseudo don't count,
+ since if they are the only uses, they are dead. */
+ if (set != 0 && SET_DEST (set) == reg)
+ continue;
+ if (GET_CODE (i2) == CODE_LABEL
+ || GET_CODE (i2) == JUMP_INSN)
+ break;
+ if ((GET_CODE (i2) == INSN || GET_CODE (i2) == CALL_INSN)
+ && reg_mentioned_p (reg, PATTERN (i2)))
+ /* Some other ref remains;
+ we can't do anything. */
+ return;
+ }
+
+ /* Delete the now-dead stores into this pseudo. */
+ for (i2 = PREV_INSN (insn); i2; i2 = PREV_INSN (i2))
+ {
+ rtx set = single_set (i2);
+
+ if (set != 0 && SET_DEST (set) == reg)
+ delete_insn (i2);
+ if (GET_CODE (i2) == CODE_LABEL
+ || GET_CODE (i2) == JUMP_INSN)
+ break;
+ }
+
+ /* For the debugging info,
+ say the pseudo lives in this reload reg. */
+ reg_renumber[REGNO (reg)] = REGNO (reload_reg_rtx[j]);
+ alter_reg (REGNO (reg), -1);
+ }
+}
+
+/* Output reload-insns to reload VALUE into RELOADREG.
+ VALUE is an autoincrement or autodecrement RTX whose operand
+ is a register or memory location;
+ so reloading involves incrementing that location.
+
+ INC_AMOUNT is the number to increment or decrement by (always positive).
+ This cannot be deduced from VALUE. */
+
+static void
+inc_for_reload (reloadreg, value, inc_amount)
+ rtx reloadreg;
+ rtx value;
+ int inc_amount;
+{
+ /* REG or MEM to be copied and incremented. */
+ rtx incloc = XEXP (value, 0);
+ /* Nonzero if increment after copying. */
+ int post = (GET_CODE (value) == POST_DEC || GET_CODE (value) == POST_INC);
+ rtx last;
+ rtx inc;
+ rtx add_insn;
+ int code;
+
+ /* No hard register is equivalent to this register after
+ inc/dec operation. If REG_LAST_RELOAD_REG were non-zero,
+ we could inc/dec that register as well (maybe even using it for
+ the source), but I'm not sure it's worth worrying about. */
+ if (GET_CODE (incloc) == REG)
+ reg_last_reload_reg[REGNO (incloc)] = 0;
+
+ if (GET_CODE (value) == PRE_DEC || GET_CODE (value) == POST_DEC)
+ inc_amount = - inc_amount;
+
+ inc = GEN_INT (inc_amount);
+
+ /* If this is post-increment, first copy the location to the reload reg. */
+ if (post)
+ emit_insn (gen_move_insn (reloadreg, incloc));
+
+ /* See if we can directly increment INCLOC. Use a method similar to that
+ in gen_input_reload. */
+
+ last = get_last_insn ();
+ add_insn = emit_insn (gen_rtx (SET, VOIDmode, incloc,
+ gen_rtx (PLUS, GET_MODE (incloc),
+ incloc, inc)));
+
+ code = recog_memoized (add_insn);
+ if (code >= 0)
+ {
+ insn_extract (add_insn);
+ if (constrain_operands (code, 1))
+ {
+ /* If this is a pre-increment and we have incremented the value
+ where it lives, copy the incremented value to RELOADREG to
+ be used as an address. */
+
+ if (! post)
+ emit_insn (gen_move_insn (reloadreg, incloc));
+
+ return;
+ }
+ }
+
+ delete_insns_since (last);
+
+ /* If couldn't do the increment directly, must increment in RELOADREG.
+ The way we do this depends on whether this is pre- or post-increment.
+ For pre-increment, copy INCLOC to the reload register, increment it
+ there, then save back. */
+
+ if (! post)
+ {
+ emit_insn (gen_move_insn (reloadreg, incloc));
+ emit_insn (gen_add2_insn (reloadreg, inc));
+ emit_insn (gen_move_insn (incloc, reloadreg));
+ }
+ else
+ {
+ /* Postincrement.
+ Because this might be a jump insn or a compare, and because RELOADREG
+ may not be available after the insn in an input reload, we must do
+ the incrementation before the insn being reloaded for.
+
+ We have already copied INCLOC to RELOADREG. Increment the copy in
+ RELOADREG, save that back, then decrement RELOADREG so it has
+ the original value. */
+
+ emit_insn (gen_add2_insn (reloadreg, inc));
+ emit_insn (gen_move_insn (incloc, reloadreg));
+ emit_insn (gen_add2_insn (reloadreg, GEN_INT (-inc_amount)));
+ }
+
+ return;
+}
+
+/* Return 1 if we are certain that the constraint-string STRING allows
+ the hard register REG. Return 0 if we can't be sure of this. */
+
+static int
+constraint_accepts_reg_p (string, reg)
+ char *string;
+ rtx reg;
+{
+ int value = 0;
+ int regno = true_regnum (reg);
+ int c;
+
+ /* Initialize for first alternative. */
+ value = 0;
+ /* Check that each alternative contains `g' or `r'. */
+ while (1)
+ switch (c = *string++)
+ {
+ case 0:
+ /* If an alternative lacks `g' or `r', we lose. */
+ return value;
+ case ',':
+ /* If an alternative lacks `g' or `r', we lose. */
+ if (value == 0)
+ return 0;
+ /* Initialize for next alternative. */
+ value = 0;
+ break;
+ case 'g':
+ case 'r':
+ /* Any general reg wins for this alternative. */
+ if (TEST_HARD_REG_BIT (reg_class_contents[(int) GENERAL_REGS], regno))
+ value = 1;
+ break;
+ default:
+ /* Any reg in specified class wins for this alternative. */
+ {
+ enum reg_class class = REG_CLASS_FROM_LETTER (c);
+
+ if (TEST_HARD_REG_BIT (reg_class_contents[(int) class], regno))
+ value = 1;
+ }
+ }
+}
+
+/* Return the number of places FIND appears within X, but don't count
+ an occurrence if some SET_DEST is FIND. */
+
+static int
+count_occurrences (x, find)
+ register rtx x, find;
+{
+ register int i, j;
+ register enum rtx_code code;
+ register char *format_ptr;
+ int count;
+
+ if (x == find)
+ return 1;
+ if (x == 0)
+ return 0;
+
+ code = GET_CODE (x);
+
+ switch (code)
+ {
+ case REG:
+ case QUEUED:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case CODE_LABEL:
+ case PC:
+ case CC0:
+ return 0;
+
+ case SET:
+ if (SET_DEST (x) == find)
+ return count_occurrences (SET_SRC (x), find);
+ break;
+ }
+
+ format_ptr = GET_RTX_FORMAT (code);
+ count = 0;
+
+ for (i = 0; i < GET_RTX_LENGTH (code); i++)
+ {
+ switch (*format_ptr++)
+ {
+ case 'e':
+ count += count_occurrences (XEXP (x, i), find);
+ break;
+
+ case 'E':
+ if (XVEC (x, i) != NULL)
+ {
+ for (j = 0; j < XVECLEN (x, i); j++)
+ count += count_occurrences (XVECEXP (x, i, j), find);
+ }
+ break;
+ }
+ }
+ return count;
+}
diff --git a/gnu/usr.bin/cc/cc_int/reorg.c b/gnu/usr.bin/cc/cc_int/reorg.c
new file mode 100644
index 0000000..4082ad8
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/reorg.c
@@ -0,0 +1,4281 @@
+/* Perform instruction reorganizations for delay slot filling.
+ Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
+ Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu).
+ Hacked by Michael Tiemann (tiemann@cygnus.com).
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Instruction reorganization pass.
+
+ This pass runs after register allocation and final jump
+ optimization. It should be the last pass to run before peephole.
+ It serves primarily to fill delay slots of insns, typically branch
+ and call insns. Other insns typically involve more complicated
+ interactions of data dependencies and resource constraints, and
+ are better handled by scheduling before register allocation (by the
+ function `schedule_insns').
+
+ The Branch Penalty is the number of extra cycles that are needed to
+ execute a branch insn. On an ideal machine, branches take a single
+ cycle, and the Branch Penalty is 0. Several RISC machines approach
+ branch delays differently:
+
+ The MIPS and AMD 29000 have a single branch delay slot. Most insns
+ (except other branches) can be used to fill this slot. When the
+ slot is filled, two insns execute in two cycles, reducing the
+ branch penalty to zero.
+
+ The Motorola 88000 conditionally exposes its branch delay slot,
+ so code is shorter when it is turned off, but will run faster
+ when useful insns are scheduled there.
+
+ The IBM ROMP has two forms of branch and call insns, both with and
+ without a delay slot. Much like the 88k, insns not using the delay
+ slot can be shorted (2 bytes vs. 4 bytes), but will run slowed.
+
+ The SPARC always has a branch delay slot, but its effects can be
+ annulled when the branch is not taken. This means that failing to
+ find other sources of insns, we can hoist an insn from the branch
+ target that would only be safe to execute knowing that the branch
+ is taken.
+
+ The HP-PA always has a branch delay slot. For unconditional branches
+ its effects can be annulled when the branch is taken. The effects
+ of the delay slot in a conditional branch can be nullified for forward
+ taken branches, or for untaken backward branches. This means
+ we can hoist insns from the fall-through path for forward branches or
+ steal insns from the target of backward branches.
+
+ Three techniques for filling delay slots have been implemented so far:
+
+ (1) `fill_simple_delay_slots' is the simplest, most efficient way
+ to fill delay slots. This pass first looks for insns which come
+ from before the branch and which are safe to execute after the
+ branch. Then it searches after the insn requiring delay slots or,
+ in the case of a branch, for insns that are after the point at
+ which the branch merges into the fallthrough code, if such a point
+ exists. When such insns are found, the branch penalty decreases
+ and no code expansion takes place.
+
+ (2) `fill_eager_delay_slots' is more complicated: it is used for
+ scheduling conditional jumps, or for scheduling jumps which cannot
+ be filled using (1). A machine need not have annulled jumps to use
+ this strategy, but it helps (by keeping more options open).
+ `fill_eager_delay_slots' tries to guess the direction the branch
+ will go; if it guesses right 100% of the time, it can reduce the
+ branch penalty as much as `fill_simple_delay_slots' does. If it
+ guesses wrong 100% of the time, it might as well schedule nops (or
+ on the m88k, unexpose the branch slot). When
+ `fill_eager_delay_slots' takes insns from the fall-through path of
+ the jump, usually there is no code expansion; when it takes insns
+ from the branch target, there is code expansion if it is not the
+ only way to reach that target.
+
+ (3) `relax_delay_slots' uses a set of rules to simplify code that
+ has been reorganized by (1) and (2). It finds cases where
+ conditional test can be eliminated, jumps can be threaded, extra
+ insns can be eliminated, etc. It is the job of (1) and (2) to do a
+ good job of scheduling locally; `relax_delay_slots' takes care of
+ making the various individual schedules work well together. It is
+ especially tuned to handle the control flow interactions of branch
+ insns. It does nothing for insns with delay slots that do not
+ branch.
+
+ On machines that use CC0, we are very conservative. We will not make
+ a copy of an insn involving CC0 since we want to maintain a 1-1
+ correspondence between the insn that sets and uses CC0. The insns are
+ allowed to be separated by placing an insn that sets CC0 (but not an insn
+ that uses CC0; we could do this, but it doesn't seem worthwhile) in a
+ delay slot. In that case, we point each insn at the other with REG_CC_USER
+ and REG_CC_SETTER notes. Note that these restrictions affect very few
+ machines because most RISC machines with delay slots will not use CC0
+ (the RT is the only known exception at this point).
+
+ Not yet implemented:
+
+ The Acorn Risc Machine can conditionally execute most insns, so
+ it is profitable to move single insns into a position to execute
+ based on the condition code of the previous insn.
+
+ The HP-PA can conditionally nullify insns, providing a similar
+ effect to the ARM, differing mostly in which insn is "in charge". */
+
+#include <stdio.h>
+#include "config.h"
+#include "rtl.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "hard-reg-set.h"
+#include "basic-block.h"
+#include "regs.h"
+#include "insn-flags.h"
+#include "recog.h"
+#include "flags.h"
+#include "output.h"
+#include "obstack.h"
+#include "insn-attr.h"
+
+#ifdef DELAY_SLOTS
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+#ifndef ANNUL_IFTRUE_SLOTS
+#define eligible_for_annul_true(INSN, SLOTS, TRIAL, FLAGS) 0
+#endif
+#ifndef ANNUL_IFFALSE_SLOTS
+#define eligible_for_annul_false(INSN, SLOTS, TRIAL, FLAGS) 0
+#endif
+
+/* Insns which have delay slots that have not yet been filled. */
+
+static struct obstack unfilled_slots_obstack;
+static rtx *unfilled_firstobj;
+
+/* Define macros to refer to the first and last slot containing unfilled
+ insns. These are used because the list may move and its address
+ should be recomputed at each use. */
+
+#define unfilled_slots_base \
+ ((rtx *) obstack_base (&unfilled_slots_obstack))
+
+#define unfilled_slots_next \
+ ((rtx *) obstack_next_free (&unfilled_slots_obstack))
+
+/* This structure is used to indicate which hardware resources are set or
+ needed by insns so far. */
+
+struct resources
+{
+ char memory; /* Insn sets or needs a memory location. */
+ char volatil; /* Insn sets or needs a volatile memory loc. */
+ char cc; /* Insn sets or needs the condition codes. */
+ HARD_REG_SET regs; /* Which registers are set or needed. */
+};
+
+/* Macro to clear all resources. */
+#define CLEAR_RESOURCE(RES) \
+ do { (RES)->memory = (RES)->volatil = (RES)->cc = 0; \
+ CLEAR_HARD_REG_SET ((RES)->regs); } while (0)
+
+/* Indicates what resources are required at the beginning of the epilogue. */
+static struct resources start_of_epilogue_needs;
+
+/* Indicates what resources are required at function end. */
+static struct resources end_of_function_needs;
+
+/* Points to the label before the end of the function. */
+static rtx end_of_function_label;
+
+/* This structure is used to record liveness information at the targets or
+ fallthrough insns of branches. We will most likely need the information
+ at targets again, so save them in a hash table rather than recomputing them
+ each time. */
+
+struct target_info
+{
+ int uid; /* INSN_UID of target. */
+ struct target_info *next; /* Next info for same hash bucket. */
+ HARD_REG_SET live_regs; /* Registers live at target. */
+ int block; /* Basic block number containing target. */
+ int bb_tick; /* Generation count of basic block info. */
+};
+
+#define TARGET_HASH_PRIME 257
+
+/* Define the hash table itself. */
+static struct target_info **target_hash_table;
+
+/* For each basic block, we maintain a generation number of its basic
+ block info, which is updated each time we move an insn from the
+ target of a jump. This is the generation number indexed by block
+ number. */
+
+static int *bb_ticks;
+
+/* Mapping between INSN_UID's and position in the code since INSN_UID's do
+ not always monotonically increase. */
+static int *uid_to_ruid;
+
+/* Highest valid index in `uid_to_ruid'. */
+static int max_uid;
+
+static void mark_referenced_resources PROTO((rtx, struct resources *, int));
+static void mark_set_resources PROTO((rtx, struct resources *, int, int));
+static int stop_search_p PROTO((rtx, int));
+static int resource_conflicts_p PROTO((struct resources *,
+ struct resources *));
+static int insn_references_resource_p PROTO((rtx, struct resources *, int));
+static int insn_sets_resources_p PROTO((rtx, struct resources *, int));
+static rtx find_end_label PROTO((void));
+static rtx emit_delay_sequence PROTO((rtx, rtx, int, int));
+static rtx add_to_delay_list PROTO((rtx, rtx));
+static void delete_from_delay_slot PROTO((rtx));
+static void delete_scheduled_jump PROTO((rtx));
+static void note_delay_statistics PROTO((int, int));
+static rtx optimize_skip PROTO((rtx));
+static int get_jump_flags PROTO((rtx, rtx));
+static int rare_destination PROTO((rtx));
+static int mostly_true_jump PROTO((rtx, rtx));
+static rtx get_branch_condition PROTO((rtx, rtx));
+static int condition_dominates_p PROTO((rtx, rtx));
+static rtx steal_delay_list_from_target PROTO((rtx, rtx, rtx, rtx,
+ struct resources *,
+ struct resources *,
+ struct resources *,
+ int, int *, int *, rtx *));
+static rtx steal_delay_list_from_fallthrough PROTO((rtx, rtx, rtx, rtx,
+ struct resources *,
+ struct resources *,
+ struct resources *,
+ int, int *, int *));
+static void try_merge_delay_insns PROTO((rtx, rtx));
+static int redundant_insn_p PROTO((rtx, rtx, rtx));
+static int own_thread_p PROTO((rtx, rtx, int));
+static int find_basic_block PROTO((rtx));
+static void update_block PROTO((rtx, rtx));
+static int reorg_redirect_jump PROTO((rtx, rtx));
+static void update_reg_dead_notes PROTO((rtx, rtx));
+static void update_live_status PROTO((rtx, rtx));
+static rtx next_insn_no_annul PROTO((rtx));
+static void mark_target_live_regs PROTO((rtx, struct resources *));
+static void fill_simple_delay_slots PROTO((rtx, int));
+static rtx fill_slots_from_thread PROTO((rtx, rtx, rtx, rtx, int, int,
+ int, int, int, int *));
+static void fill_eager_delay_slots PROTO((rtx));
+static void relax_delay_slots PROTO((rtx));
+static void make_return_insns PROTO((rtx));
+static int redirect_with_delay_slots_safe_p PROTO ((rtx, rtx, rtx));
+static int redirect_with_delay_list_safe_p PROTO ((rtx, rtx, rtx));
+
+/* Given X, some rtl, and RES, a pointer to a `struct resource', mark
+ which resources are references by the insn. If INCLUDE_CALLED_ROUTINE
+ is TRUE, resources used by the called routine will be included for
+ CALL_INSNs. */
+
+static void
+mark_referenced_resources (x, res, include_delayed_effects)
+ register rtx x;
+ register struct resources *res;
+ register int include_delayed_effects;
+{
+ register enum rtx_code code = GET_CODE (x);
+ register int i, j;
+ register char *format_ptr;
+
+ /* Handle leaf items for which we set resource flags. Also, special-case
+ CALL, SET and CLOBBER operators. */
+ switch (code)
+ {
+ case CONST:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case PC:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return;
+
+ case SUBREG:
+ if (GET_CODE (SUBREG_REG (x)) != REG)
+ mark_referenced_resources (SUBREG_REG (x), res, 0);
+ else
+ {
+ int regno = REGNO (SUBREG_REG (x)) + SUBREG_WORD (x);
+ int last_regno = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
+ for (i = regno; i < last_regno; i++)
+ SET_HARD_REG_BIT (res->regs, i);
+ }
+ return;
+
+ case REG:
+ for (i = 0; i < HARD_REGNO_NREGS (REGNO (x), GET_MODE (x)); i++)
+ SET_HARD_REG_BIT (res->regs, REGNO (x) + i);
+ return;
+
+ case MEM:
+ /* If this memory shouldn't change, it really isn't referencing
+ memory. */
+ if (! RTX_UNCHANGING_P (x))
+ res->memory = 1;
+ res->volatil = MEM_VOLATILE_P (x);
+
+ /* Mark registers used to access memory. */
+ mark_referenced_resources (XEXP (x, 0), res, 0);
+ return;
+
+ case CC0:
+ res->cc = 1;
+ return;
+
+ case UNSPEC_VOLATILE:
+ case ASM_INPUT:
+ /* Traditional asm's are always volatile. */
+ res->volatil = 1;
+ return;
+
+ case ASM_OPERANDS:
+ res->volatil = MEM_VOLATILE_P (x);
+
+ /* For all ASM_OPERANDS, we must traverse the vector of input operands.
+ We can not just fall through here since then we would be confused
+ by the ASM_INPUT rtx inside ASM_OPERANDS, which do not indicate
+ traditional asms unlike their normal usage. */
+
+ for (i = 0; i < ASM_OPERANDS_INPUT_LENGTH (x); i++)
+ mark_referenced_resources (ASM_OPERANDS_INPUT (x, i), res, 0);
+ return;
+
+ case CALL:
+ /* The first operand will be a (MEM (xxx)) but doesn't really reference
+ memory. The second operand may be referenced, though. */
+ mark_referenced_resources (XEXP (XEXP (x, 0), 0), res, 0);
+ mark_referenced_resources (XEXP (x, 1), res, 0);
+ return;
+
+ case SET:
+ /* Usually, the first operand of SET is set, not referenced. But
+ registers used to access memory are referenced. SET_DEST is
+ also referenced if it is a ZERO_EXTRACT or SIGN_EXTRACT. */
+
+ mark_referenced_resources (SET_SRC (x), res, 0);
+
+ x = SET_DEST (x);
+ if (GET_CODE (x) == SIGN_EXTRACT || GET_CODE (x) == ZERO_EXTRACT)
+ mark_referenced_resources (x, res, 0);
+ else if (GET_CODE (x) == SUBREG)
+ x = SUBREG_REG (x);
+ if (GET_CODE (x) == MEM)
+ mark_referenced_resources (XEXP (x, 0), res, 0);
+ return;
+
+ case CLOBBER:
+ return;
+
+ case CALL_INSN:
+ if (include_delayed_effects)
+ {
+ /* A CALL references memory, the frame pointer if it exists, the
+ stack pointer, any global registers and any registers given in
+ USE insns immediately in front of the CALL.
+
+ However, we may have moved some of the parameter loading insns
+ into the delay slot of this CALL. If so, the USE's for them
+ don't count and should be skipped. */
+ rtx insn = PREV_INSN (x);
+ rtx sequence = 0;
+ int seq_size = 0;
+ int i;
+
+ /* If we are part of a delay slot sequence, point at the SEQUENCE. */
+ if (NEXT_INSN (insn) != x)
+ {
+ sequence = PATTERN (NEXT_INSN (insn));
+ seq_size = XVECLEN (sequence, 0);
+ if (GET_CODE (sequence) != SEQUENCE)
+ abort ();
+ }
+
+ res->memory = 1;
+ SET_HARD_REG_BIT (res->regs, STACK_POINTER_REGNUM);
+ if (frame_pointer_needed)
+ {
+ SET_HARD_REG_BIT (res->regs, FRAME_POINTER_REGNUM);
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+ SET_HARD_REG_BIT (res->regs, HARD_FRAME_POINTER_REGNUM);
+#endif
+ }
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (global_regs[i])
+ SET_HARD_REG_BIT (res->regs, i);
+
+ {
+ rtx link;
+
+ for (link = CALL_INSN_FUNCTION_USAGE (x);
+ link;
+ link = XEXP (link, 1))
+ if (GET_CODE (XEXP (link, 0)) == USE)
+ {
+ for (i = 1; i < seq_size; i++)
+ {
+ rtx slot_pat = PATTERN (XVECEXP (sequence, 0, i));
+ if (GET_CODE (slot_pat) == SET
+ && rtx_equal_p (SET_DEST (slot_pat),
+ SET_DEST (XEXP (link, 0))))
+ break;
+ }
+ if (i >= seq_size)
+ mark_referenced_resources (SET_DEST (XEXP (link, 0)),
+ res, 0);
+ }
+ }
+ }
+
+ /* ... fall through to other INSN processing ... */
+
+ case INSN:
+ case JUMP_INSN:
+
+#ifdef INSN_REFERENCES_ARE_DELAYED
+ if (! include_delayed_effects
+ && INSN_REFERENCES_ARE_DELAYED (x))
+ return;
+#endif
+
+ /* No special processing, just speed up. */
+ mark_referenced_resources (PATTERN (x), res, include_delayed_effects);
+ return;
+ }
+
+ /* Process each sub-expression and flag what it needs. */
+ format_ptr = GET_RTX_FORMAT (code);
+ for (i = 0; i < GET_RTX_LENGTH (code); i++)
+ switch (*format_ptr++)
+ {
+ case 'e':
+ mark_referenced_resources (XEXP (x, i), res, include_delayed_effects);
+ break;
+
+ case 'E':
+ for (j = 0; j < XVECLEN (x, i); j++)
+ mark_referenced_resources (XVECEXP (x, i, j), res,
+ include_delayed_effects);
+ break;
+ }
+}
+
+/* Given X, a part of an insn, and a pointer to a `struct resource', RES,
+ indicate which resources are modified by the insn. If INCLUDE_CALLED_ROUTINE
+ is nonzero, also mark resources potentially set by the called routine.
+
+ If IN_DEST is nonzero, it means we are inside a SET. Otherwise,
+ objects are being referenced instead of set.
+
+ We never mark the insn as modifying the condition code unless it explicitly
+ SETs CC0 even though this is not totally correct. The reason for this is
+ that we require a SET of CC0 to immediately precede the reference to CC0.
+ So if some other insn sets CC0 as a side-effect, we know it cannot affect
+ our computation and thus may be placed in a delay slot. */
+
+static void
+mark_set_resources (x, res, in_dest, include_delayed_effects)
+ register rtx x;
+ register struct resources *res;
+ int in_dest;
+ int include_delayed_effects;
+{
+ register enum rtx_code code;
+ register int i, j;
+ register char *format_ptr;
+
+ restart:
+
+ code = GET_CODE (x);
+
+ switch (code)
+ {
+ case NOTE:
+ case BARRIER:
+ case CODE_LABEL:
+ case USE:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ case CONST:
+ case PC:
+ /* These don't set any resources. */
+ return;
+
+ case CC0:
+ if (in_dest)
+ res->cc = 1;
+ return;
+
+ case CALL_INSN:
+ /* Called routine modifies the condition code, memory, any registers
+ that aren't saved across calls, global registers and anything
+ explicitly CLOBBERed immediately after the CALL_INSN. */
+
+ if (include_delayed_effects)
+ {
+ rtx next = NEXT_INSN (x);
+ rtx prev = PREV_INSN (x);
+ rtx link;
+
+ res->cc = res->memory = 1;
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (call_used_regs[i] || global_regs[i])
+ SET_HARD_REG_BIT (res->regs, i);
+
+ /* If X is part of a delay slot sequence, then NEXT should be
+ the first insn after the sequence. */
+ if (NEXT_INSN (prev) != x)
+ next = NEXT_INSN (NEXT_INSN (prev));
+
+ for (link = CALL_INSN_FUNCTION_USAGE (x);
+ link; link = XEXP (link, 1))
+ if (GET_CODE (XEXP (link, 0)) == CLOBBER)
+ mark_set_resources (SET_DEST (XEXP (link, 0)), res, 1, 0);
+
+ /* Check for a NOTE_INSN_SETJMP. If it exists, then we must
+ assume that this call can clobber any register. */
+ if (next && GET_CODE (next) == NOTE
+ && NOTE_LINE_NUMBER (next) == NOTE_INSN_SETJMP)
+ SET_HARD_REG_SET (res->regs);
+ }
+
+ /* ... and also what it's RTL says it modifies, if anything. */
+
+ case JUMP_INSN:
+ case INSN:
+
+ /* An insn consisting of just a CLOBBER (or USE) is just for flow
+ and doesn't actually do anything, so we ignore it. */
+
+#ifdef INSN_SETS_ARE_DELAYED
+ if (! include_delayed_effects
+ && INSN_SETS_ARE_DELAYED (x))
+ return;
+#endif
+
+ x = PATTERN (x);
+ if (GET_CODE (x) != USE && GET_CODE (x) != CLOBBER)
+ goto restart;
+ return;
+
+ case SET:
+ /* If the source of a SET is a CALL, this is actually done by
+ the called routine. So only include it if we are to include the
+ effects of the calling routine. */
+
+ mark_set_resources (SET_DEST (x), res,
+ (include_delayed_effects
+ || GET_CODE (SET_SRC (x)) != CALL),
+ 0);
+
+ mark_set_resources (SET_SRC (x), res, 0, 0);
+ return;
+
+ case CLOBBER:
+ mark_set_resources (XEXP (x, 0), res, 1, 0);
+ return;
+
+ case SEQUENCE:
+ for (i = 0; i < XVECLEN (x, 0); i++)
+ if (! (INSN_ANNULLED_BRANCH_P (XVECEXP (x, 0, 0))
+ && INSN_FROM_TARGET_P (XVECEXP (x, 0, i))))
+ mark_set_resources (XVECEXP (x, 0, i), res, 0,
+ include_delayed_effects);
+ return;
+
+ case POST_INC:
+ case PRE_INC:
+ case POST_DEC:
+ case PRE_DEC:
+ mark_set_resources (XEXP (x, 0), res, 1, 0);
+ return;
+
+ case ZERO_EXTRACT:
+ mark_set_resources (XEXP (x, 0), res, in_dest, 0);
+ mark_set_resources (XEXP (x, 1), res, 0, 0);
+ mark_set_resources (XEXP (x, 2), res, 0, 0);
+ return;
+
+ case MEM:
+ if (in_dest)
+ {
+ res->memory = 1;
+ res->volatil = MEM_VOLATILE_P (x);
+ }
+
+ mark_set_resources (XEXP (x, 0), res, 0, 0);
+ return;
+
+ case REG:
+ if (in_dest)
+ for (i = 0; i < HARD_REGNO_NREGS (REGNO (x), GET_MODE (x)); i++)
+ SET_HARD_REG_BIT (res->regs, REGNO (x) + i);
+ return;
+ }
+
+ /* Process each sub-expression and flag what it needs. */
+ format_ptr = GET_RTX_FORMAT (code);
+ for (i = 0; i < GET_RTX_LENGTH (code); i++)
+ switch (*format_ptr++)
+ {
+ case 'e':
+ mark_set_resources (XEXP (x, i), res, in_dest, include_delayed_effects);
+ break;
+
+ case 'E':
+ for (j = 0; j < XVECLEN (x, i); j++)
+ mark_set_resources (XVECEXP (x, i, j), res, in_dest,
+ include_delayed_effects);
+ break;
+ }
+}
+
+/* Return TRUE if this insn should stop the search for insn to fill delay
+ slots. LABELS_P indicates that labels should terminate the search.
+ In all cases, jumps terminate the search. */
+
+static int
+stop_search_p (insn, labels_p)
+ rtx insn;
+ int labels_p;
+{
+ if (insn == 0)
+ return 1;
+
+ switch (GET_CODE (insn))
+ {
+ case NOTE:
+ case CALL_INSN:
+ return 0;
+
+ case CODE_LABEL:
+ return labels_p;
+
+ case JUMP_INSN:
+ case BARRIER:
+ return 1;
+
+ case INSN:
+ /* OK unless it contains a delay slot or is an `asm' insn of some type.
+ We don't know anything about these. */
+ return (GET_CODE (PATTERN (insn)) == SEQUENCE
+ || GET_CODE (PATTERN (insn)) == ASM_INPUT
+ || asm_noperands (PATTERN (insn)) >= 0);
+
+ default:
+ abort ();
+ }
+}
+
+/* Return TRUE if any resources are marked in both RES1 and RES2 or if either
+ resource set contains a volatile memory reference. Otherwise, return FALSE. */
+
+static int
+resource_conflicts_p (res1, res2)
+ struct resources *res1, *res2;
+{
+ if ((res1->cc && res2->cc) || (res1->memory && res2->memory)
+ || res1->volatil || res2->volatil)
+ return 1;
+
+#ifdef HARD_REG_SET
+ return (res1->regs & res2->regs) != HARD_CONST (0);
+#else
+ {
+ int i;
+
+ for (i = 0; i < HARD_REG_SET_LONGS; i++)
+ if ((res1->regs[i] & res2->regs[i]) != 0)
+ return 1;
+ return 0;
+ }
+#endif
+}
+
+/* Return TRUE if any resource marked in RES, a `struct resources', is
+ referenced by INSN. If INCLUDE_CALLED_ROUTINE is set, return if the called
+ routine is using those resources.
+
+ We compute this by computing all the resources referenced by INSN and
+ seeing if this conflicts with RES. It might be faster to directly check
+ ourselves, and this is the way it used to work, but it means duplicating
+ a large block of complex code. */
+
+static int
+insn_references_resource_p (insn, res, include_delayed_effects)
+ register rtx insn;
+ register struct resources *res;
+ int include_delayed_effects;
+{
+ struct resources insn_res;
+
+ CLEAR_RESOURCE (&insn_res);
+ mark_referenced_resources (insn, &insn_res, include_delayed_effects);
+ return resource_conflicts_p (&insn_res, res);
+}
+
+/* Return TRUE if INSN modifies resources that are marked in RES.
+ INCLUDE_CALLED_ROUTINE is set if the actions of that routine should be
+ included. CC0 is only modified if it is explicitly set; see comments
+ in front of mark_set_resources for details. */
+
+static int
+insn_sets_resource_p (insn, res, include_delayed_effects)
+ register rtx insn;
+ register struct resources *res;
+ int include_delayed_effects;
+{
+ struct resources insn_sets;
+
+ CLEAR_RESOURCE (&insn_sets);
+ mark_set_resources (insn, &insn_sets, 0, include_delayed_effects);
+ return resource_conflicts_p (&insn_sets, res);
+}
+
+/* Find a label at the end of the function or before a RETURN. If there is
+ none, make one. */
+
+static rtx
+find_end_label ()
+{
+ rtx insn;
+
+ /* If we found one previously, return it. */
+ if (end_of_function_label)
+ return end_of_function_label;
+
+ /* Otherwise, see if there is a label at the end of the function. If there
+ is, it must be that RETURN insns aren't needed, so that is our return
+ label and we don't have to do anything else. */
+
+ insn = get_last_insn ();
+ while (GET_CODE (insn) == NOTE
+ || (GET_CODE (insn) == INSN
+ && (GET_CODE (PATTERN (insn)) == USE
+ || GET_CODE (PATTERN (insn)) == CLOBBER)))
+ insn = PREV_INSN (insn);
+
+ /* When a target threads its epilogue we might already have a
+ suitable return insn. If so put a label before it for the
+ end_of_function_label. */
+ if (GET_CODE (insn) == BARRIER
+ && GET_CODE (PREV_INSN (insn)) == JUMP_INSN
+ && GET_CODE (PATTERN (PREV_INSN (insn))) == RETURN)
+ {
+ rtx temp = PREV_INSN (PREV_INSN (insn));
+ end_of_function_label = gen_label_rtx ();
+ LABEL_NUSES (end_of_function_label) = 0;
+
+ /* Put the label before an USE insns that may proceed the RETURN insn. */
+ while (GET_CODE (temp) == USE)
+ temp = PREV_INSN (temp);
+
+ emit_label_after (end_of_function_label, temp);
+ }
+
+ else if (GET_CODE (insn) == CODE_LABEL)
+ end_of_function_label = insn;
+ else
+ {
+ /* Otherwise, make a new label and emit a RETURN and BARRIER,
+ if needed. */
+ end_of_function_label = gen_label_rtx ();
+ LABEL_NUSES (end_of_function_label) = 0;
+ emit_label (end_of_function_label);
+#ifdef HAVE_return
+ if (HAVE_return)
+ {
+ /* The return we make may have delay slots too. */
+ rtx insn = gen_return ();
+ insn = emit_jump_insn (insn);
+ emit_barrier ();
+ if (num_delay_slots (insn) > 0)
+ obstack_ptr_grow (&unfilled_slots_obstack, insn);
+ }
+#endif
+ }
+
+ /* Show one additional use for this label so it won't go away until
+ we are done. */
+ ++LABEL_NUSES (end_of_function_label);
+
+ return end_of_function_label;
+}
+
+/* Put INSN and LIST together in a SEQUENCE rtx of LENGTH, and replace
+ the pattern of INSN with the SEQUENCE.
+
+ Chain the insns so that NEXT_INSN of each insn in the sequence points to
+ the next and NEXT_INSN of the last insn in the sequence points to
+ the first insn after the sequence. Similarly for PREV_INSN. This makes
+ it easier to scan all insns.
+
+ Returns the SEQUENCE that replaces INSN. */
+
+static rtx
+emit_delay_sequence (insn, list, length, avail)
+ rtx insn;
+ rtx list;
+ int length;
+ int avail;
+{
+ register int i = 1;
+ register rtx li;
+ int had_barrier = 0;
+
+ /* Allocate the the rtvec to hold the insns and the SEQUENCE. */
+ rtvec seqv = rtvec_alloc (length + 1);
+ rtx seq = gen_rtx (SEQUENCE, VOIDmode, seqv);
+ rtx seq_insn = make_insn_raw (seq);
+ rtx first = get_insns ();
+ rtx last = get_last_insn ();
+
+ /* Make a copy of the insn having delay slots. */
+ rtx delay_insn = copy_rtx (insn);
+
+ /* If INSN is followed by a BARRIER, delete the BARRIER since it will only
+ confuse further processing. Update LAST in case it was the last insn.
+ We will put the BARRIER back in later. */
+ if (NEXT_INSN (insn) && GET_CODE (NEXT_INSN (insn)) == BARRIER)
+ {
+ delete_insn (NEXT_INSN (insn));
+ last = get_last_insn ();
+ had_barrier = 1;
+ }
+
+ /* Splice our SEQUENCE into the insn stream where INSN used to be. */
+ NEXT_INSN (seq_insn) = NEXT_INSN (insn);
+ PREV_INSN (seq_insn) = PREV_INSN (insn);
+
+ if (insn == last)
+ set_new_first_and_last_insn (first, seq_insn);
+ else
+ PREV_INSN (NEXT_INSN (seq_insn)) = seq_insn;
+
+ if (insn == first)
+ set_new_first_and_last_insn (seq_insn, last);
+ else
+ NEXT_INSN (PREV_INSN (seq_insn)) = seq_insn;
+
+ /* Build our SEQUENCE and rebuild the insn chain. */
+ XVECEXP (seq, 0, 0) = delay_insn;
+ INSN_DELETED_P (delay_insn) = 0;
+ PREV_INSN (delay_insn) = PREV_INSN (seq_insn);
+
+ for (li = list; li; li = XEXP (li, 1), i++)
+ {
+ rtx tem = XEXP (li, 0);
+ rtx note;
+
+ /* Show that this copy of the insn isn't deleted. */
+ INSN_DELETED_P (tem) = 0;
+
+ XVECEXP (seq, 0, i) = tem;
+ PREV_INSN (tem) = XVECEXP (seq, 0, i - 1);
+ NEXT_INSN (XVECEXP (seq, 0, i - 1)) = tem;
+
+ /* Remove any REG_DEAD notes because we can't rely on them now
+ that the insn has been moved. */
+ for (note = REG_NOTES (tem); note; note = XEXP (note, 1))
+ if (REG_NOTE_KIND (note) == REG_DEAD)
+ XEXP (note, 0) = const0_rtx;
+ }
+
+ NEXT_INSN (XVECEXP (seq, 0, length)) = NEXT_INSN (seq_insn);
+
+ /* If the previous insn is a SEQUENCE, update the NEXT_INSN pointer on the
+ last insn in that SEQUENCE to point to us. Similarly for the first
+ insn in the following insn if it is a SEQUENCE. */
+
+ if (PREV_INSN (seq_insn) && GET_CODE (PREV_INSN (seq_insn)) == INSN
+ && GET_CODE (PATTERN (PREV_INSN (seq_insn))) == SEQUENCE)
+ NEXT_INSN (XVECEXP (PATTERN (PREV_INSN (seq_insn)), 0,
+ XVECLEN (PATTERN (PREV_INSN (seq_insn)), 0) - 1))
+ = seq_insn;
+
+ if (NEXT_INSN (seq_insn) && GET_CODE (NEXT_INSN (seq_insn)) == INSN
+ && GET_CODE (PATTERN (NEXT_INSN (seq_insn))) == SEQUENCE)
+ PREV_INSN (XVECEXP (PATTERN (NEXT_INSN (seq_insn)), 0, 0)) = seq_insn;
+
+ /* If there used to be a BARRIER, put it back. */
+ if (had_barrier)
+ emit_barrier_after (seq_insn);
+
+ if (i != length + 1)
+ abort ();
+
+ return seq_insn;
+}
+
+/* Add INSN to DELAY_LIST and return the head of the new list. The list must
+ be in the order in which the insns are to be executed. */
+
+static rtx
+add_to_delay_list (insn, delay_list)
+ rtx insn;
+ rtx delay_list;
+{
+ /* If we have an empty list, just make a new list element. If
+ INSN has it's block number recorded, clear it since we may
+ be moving the insn to a new block. */
+
+ if (delay_list == 0)
+ {
+ struct target_info *tinfo;
+
+ for (tinfo = target_hash_table[INSN_UID (insn) % TARGET_HASH_PRIME];
+ tinfo; tinfo = tinfo->next)
+ if (tinfo->uid == INSN_UID (insn))
+ break;
+
+ if (tinfo)
+ tinfo->block = -1;
+
+ return gen_rtx (INSN_LIST, VOIDmode, insn, NULL_RTX);
+ }
+
+ /* Otherwise this must be an INSN_LIST. Add INSN to the end of the
+ list. */
+ XEXP (delay_list, 1) = add_to_delay_list (insn, XEXP (delay_list, 1));
+
+ return delay_list;
+}
+
+/* Delete INSN from the the delay slot of the insn that it is in. This may
+ produce an insn without anything in its delay slots. */
+
+static void
+delete_from_delay_slot (insn)
+ rtx insn;
+{
+ rtx trial, seq_insn, seq, prev;
+ rtx delay_list = 0;
+ int i;
+
+ /* We first must find the insn containing the SEQUENCE with INSN in its
+ delay slot. Do this by finding an insn, TRIAL, where
+ PREV_INSN (NEXT_INSN (TRIAL)) != TRIAL. */
+
+ for (trial = insn;
+ PREV_INSN (NEXT_INSN (trial)) == trial;
+ trial = NEXT_INSN (trial))
+ ;
+
+ seq_insn = PREV_INSN (NEXT_INSN (trial));
+ seq = PATTERN (seq_insn);
+
+ /* Create a delay list consisting of all the insns other than the one
+ we are deleting (unless we were the only one). */
+ if (XVECLEN (seq, 0) > 2)
+ for (i = 1; i < XVECLEN (seq, 0); i++)
+ if (XVECEXP (seq, 0, i) != insn)
+ delay_list = add_to_delay_list (XVECEXP (seq, 0, i), delay_list);
+
+ /* Delete the old SEQUENCE, re-emit the insn that used to have the delay
+ list, and rebuild the delay list if non-empty. */
+ prev = PREV_INSN (seq_insn);
+ trial = XVECEXP (seq, 0, 0);
+ delete_insn (seq_insn);
+ add_insn_after (trial, prev);
+
+ if (GET_CODE (trial) == JUMP_INSN
+ && (simplejump_p (trial) || GET_CODE (PATTERN (trial)) == RETURN))
+ emit_barrier_after (trial);
+
+ /* If there are any delay insns, remit them. Otherwise clear the
+ annul flag. */
+ if (delay_list)
+ trial = emit_delay_sequence (trial, delay_list, XVECLEN (seq, 0) - 2, 0);
+ else
+ INSN_ANNULLED_BRANCH_P (trial) = 0;
+
+ INSN_FROM_TARGET_P (insn) = 0;
+
+ /* Show we need to fill this insn again. */
+ obstack_ptr_grow (&unfilled_slots_obstack, trial);
+}
+
+/* Delete INSN, a JUMP_INSN. If it is a conditional jump, we must track down
+ the insn that sets CC0 for it and delete it too. */
+
+static void
+delete_scheduled_jump (insn)
+ rtx insn;
+{
+ /* Delete the insn that sets cc0 for us. On machines without cc0, we could
+ delete the insn that sets the condition code, but it is hard to find it.
+ Since this case is rare anyway, don't bother trying; there would likely
+ be other insns that became dead anyway, which we wouldn't know to
+ delete. */
+
+#ifdef HAVE_cc0
+ if (reg_mentioned_p (cc0_rtx, insn))
+ {
+ rtx note = find_reg_note (insn, REG_CC_SETTER, NULL_RTX);
+
+ /* If a reg-note was found, it points to an insn to set CC0. This
+ insn is in the delay list of some other insn. So delete it from
+ the delay list it was in. */
+ if (note)
+ {
+ if (! FIND_REG_INC_NOTE (XEXP (note, 0), NULL_RTX)
+ && sets_cc0_p (PATTERN (XEXP (note, 0))) == 1)
+ delete_from_delay_slot (XEXP (note, 0));
+ }
+ else
+ {
+ /* The insn setting CC0 is our previous insn, but it may be in
+ a delay slot. It will be the last insn in the delay slot, if
+ it is. */
+ rtx trial = previous_insn (insn);
+ if (GET_CODE (trial) == NOTE)
+ trial = prev_nonnote_insn (trial);
+ if (sets_cc0_p (PATTERN (trial)) != 1
+ || FIND_REG_INC_NOTE (trial, 0))
+ return;
+ if (PREV_INSN (NEXT_INSN (trial)) == trial)
+ delete_insn (trial);
+ else
+ delete_from_delay_slot (trial);
+ }
+ }
+#endif
+
+ delete_insn (insn);
+}
+
+/* Counters for delay-slot filling. */
+
+#define NUM_REORG_FUNCTIONS 2
+#define MAX_DELAY_HISTOGRAM 3
+#define MAX_REORG_PASSES 2
+
+static int num_insns_needing_delays[NUM_REORG_FUNCTIONS][MAX_REORG_PASSES];
+
+static int num_filled_delays[NUM_REORG_FUNCTIONS][MAX_DELAY_HISTOGRAM+1][MAX_REORG_PASSES];
+
+static int reorg_pass_number;
+
+static void
+note_delay_statistics (slots_filled, index)
+ int slots_filled, index;
+{
+ num_insns_needing_delays[index][reorg_pass_number]++;
+ if (slots_filled > MAX_DELAY_HISTOGRAM)
+ slots_filled = MAX_DELAY_HISTOGRAM;
+ num_filled_delays[index][slots_filled][reorg_pass_number]++;
+}
+
+#if defined(ANNUL_IFFALSE_SLOTS) || defined(ANNUL_IFTRUE_SLOTS)
+
+/* Optimize the following cases:
+
+ 1. When a conditional branch skips over only one instruction,
+ use an annulling branch and put that insn in the delay slot.
+ Use either a branch that annuls when the condition if true or
+ invert the test with a branch that annuls when the condition is
+ false. This saves insns, since otherwise we must copy an insn
+ from the L1 target.
+
+ (orig) (skip) (otherwise)
+ Bcc.n L1 Bcc',a L1 Bcc,a L1'
+ insn insn insn2
+ L1: L1: L1:
+ insn2 insn2 insn2
+ insn3 insn3 L1':
+ insn3
+
+ 2. When a conditional branch skips over only one instruction,
+ and after that, it unconditionally branches somewhere else,
+ perform the similar optimization. This saves executing the
+ second branch in the case where the inverted condition is true.
+
+ Bcc.n L1 Bcc',a L2
+ insn insn
+ L1: L1:
+ Bra L2 Bra L2
+
+ INSN is a JUMP_INSN.
+
+ This should be expanded to skip over N insns, where N is the number
+ of delay slots required. */
+
+static rtx
+optimize_skip (insn)
+ register rtx insn;
+{
+ register rtx trial = next_nonnote_insn (insn);
+ rtx next_trial = next_active_insn (trial);
+ rtx delay_list = 0;
+ rtx target_label;
+ int flags;
+
+ flags = get_jump_flags (insn, JUMP_LABEL (insn));
+
+ if (trial == 0
+ || GET_CODE (trial) != INSN
+ || GET_CODE (PATTERN (trial)) == SEQUENCE
+ || recog_memoized (trial) < 0
+ || (! eligible_for_annul_false (insn, 0, trial, flags)
+ && ! eligible_for_annul_true (insn, 0, trial, flags)))
+ return 0;
+
+ /* There are two cases where we are just executing one insn (we assume
+ here that a branch requires only one insn; this should be generalized
+ at some point): Where the branch goes around a single insn or where
+ we have one insn followed by a branch to the same label we branch to.
+ In both of these cases, inverting the jump and annulling the delay
+ slot give the same effect in fewer insns. */
+ if ((next_trial == next_active_insn (JUMP_LABEL (insn)))
+ || (next_trial != 0
+ && GET_CODE (next_trial) == JUMP_INSN
+ && JUMP_LABEL (insn) == JUMP_LABEL (next_trial)
+ && (simplejump_p (next_trial)
+ || GET_CODE (PATTERN (next_trial)) == RETURN)))
+ {
+ if (eligible_for_annul_false (insn, 0, trial, flags))
+ {
+ if (invert_jump (insn, JUMP_LABEL (insn)))
+ INSN_FROM_TARGET_P (trial) = 1;
+ else if (! eligible_for_annul_true (insn, 0, trial, flags))
+ return 0;
+ }
+
+ delay_list = add_to_delay_list (trial, NULL_RTX);
+ next_trial = next_active_insn (trial);
+ update_block (trial, trial);
+ delete_insn (trial);
+
+ /* Also, if we are targeting an unconditional
+ branch, thread our jump to the target of that branch. Don't
+ change this into a RETURN here, because it may not accept what
+ we have in the delay slot. We'll fix this up later. */
+ if (next_trial && GET_CODE (next_trial) == JUMP_INSN
+ && (simplejump_p (next_trial)
+ || GET_CODE (PATTERN (next_trial)) == RETURN))
+ {
+ target_label = JUMP_LABEL (next_trial);
+ if (target_label == 0)
+ target_label = find_end_label ();
+
+ /* Recompute the flags based on TARGET_LABEL since threading
+ the jump to TARGET_LABEL may change the direction of the
+ jump (which may change the circumstances in which the
+ delay slot is nullified). */
+ flags = get_jump_flags (insn, target_label);
+ if (eligible_for_annul_true (insn, 0, trial, flags))
+ reorg_redirect_jump (insn, target_label);
+ }
+
+ INSN_ANNULLED_BRANCH_P (insn) = 1;
+ }
+
+ return delay_list;
+}
+#endif
+
+
+/* Encode and return branch direction and prediction information for
+ INSN assuming it will jump to LABEL.
+
+ Non conditional branches return no direction information and
+ are predicted as very likely taken. */
+static int
+get_jump_flags (insn, label)
+ rtx insn, label;
+{
+ int flags;
+
+ /* get_jump_flags can be passed any insn with delay slots, these may
+ be INSNs, CALL_INSNs, or JUMP_INSNs. Only JUMP_INSNs have branch
+ direction information, and only if they are conditional jumps.
+
+ If LABEL is zero, then there is no way to determine the branch
+ direction. */
+ if (GET_CODE (insn) == JUMP_INSN
+ && (condjump_p (insn) || condjump_in_parallel_p (insn))
+ && INSN_UID (insn) <= max_uid
+ && label != 0
+ && INSN_UID (label) <= max_uid)
+ flags
+ = (uid_to_ruid[INSN_UID (label)] > uid_to_ruid[INSN_UID (insn)])
+ ? ATTR_FLAG_forward : ATTR_FLAG_backward;
+ /* No valid direction information. */
+ else
+ flags = 0;
+
+ /* If insn is a conditional branch call mostly_true_jump to get
+ determine the branch prediction.
+
+ Non conditional branches are predicted as very likely taken. */
+ if (GET_CODE (insn) == JUMP_INSN
+ && (condjump_p (insn) || condjump_in_parallel_p (insn)))
+ {
+ int prediction;
+
+ prediction = mostly_true_jump (insn, get_branch_condition (insn, label));
+ switch (prediction)
+ {
+ case 2:
+ flags |= (ATTR_FLAG_very_likely | ATTR_FLAG_likely);
+ break;
+ case 1:
+ flags |= ATTR_FLAG_likely;
+ break;
+ case 0:
+ flags |= ATTR_FLAG_unlikely;
+ break;
+ case -1:
+ flags |= (ATTR_FLAG_very_unlikely | ATTR_FLAG_unlikely);
+ break;
+
+ default:
+ abort();
+ }
+ }
+ else
+ flags |= (ATTR_FLAG_very_likely | ATTR_FLAG_likely);
+
+ return flags;
+}
+
+/* Return 1 if INSN is a destination that will be branched to rarely (the
+ return point of a function); return 2 if DEST will be branched to very
+ rarely (a call to a function that doesn't return). Otherwise,
+ return 0. */
+
+static int
+rare_destination (insn)
+ rtx insn;
+{
+ int jump_count = 0;
+ rtx next;
+
+ for (; insn; insn = next)
+ {
+ if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE)
+ insn = XVECEXP (PATTERN (insn), 0, 0);
+
+ next = NEXT_INSN (insn);
+
+ switch (GET_CODE (insn))
+ {
+ case CODE_LABEL:
+ return 0;
+ case BARRIER:
+ /* A BARRIER can either be after a JUMP_INSN or a CALL_INSN. We
+ don't scan past JUMP_INSNs, so any barrier we find here must
+ have been after a CALL_INSN and hence mean the call doesn't
+ return. */
+ return 2;
+ case JUMP_INSN:
+ if (GET_CODE (PATTERN (insn)) == RETURN)
+ return 1;
+ else if (simplejump_p (insn)
+ && jump_count++ < 10)
+ next = JUMP_LABEL (insn);
+ else
+ return 0;
+ }
+ }
+
+ /* If we got here it means we hit the end of the function. So this
+ is an unlikely destination. */
+
+ return 1;
+}
+
+/* Return truth value of the statement that this branch
+ is mostly taken. If we think that the branch is extremely likely
+ to be taken, we return 2. If the branch is slightly more likely to be
+ taken, return 1. If the branch is slightly less likely to be taken,
+ return 0 and if the branch is highly unlikely to be taken, return -1.
+
+ CONDITION, if non-zero, is the condition that JUMP_INSN is testing. */
+
+static int
+mostly_true_jump (jump_insn, condition)
+ rtx jump_insn, condition;
+{
+ rtx target_label = JUMP_LABEL (jump_insn);
+ rtx insn;
+ int rare_dest = rare_destination (target_label);
+ int rare_fallthrough = rare_destination (NEXT_INSN (jump_insn));
+
+ /* If this is a branch outside a loop, it is highly unlikely. */
+ if (GET_CODE (PATTERN (jump_insn)) == SET
+ && GET_CODE (SET_SRC (PATTERN (jump_insn))) == IF_THEN_ELSE
+ && ((GET_CODE (XEXP (SET_SRC (PATTERN (jump_insn)), 1)) == LABEL_REF
+ && LABEL_OUTSIDE_LOOP_P (XEXP (SET_SRC (PATTERN (jump_insn)), 1)))
+ || (GET_CODE (XEXP (SET_SRC (PATTERN (jump_insn)), 2)) == LABEL_REF
+ && LABEL_OUTSIDE_LOOP_P (XEXP (SET_SRC (PATTERN (jump_insn)), 2)))))
+ return -1;
+
+ if (target_label)
+ {
+ /* If this is the test of a loop, it is very likely true. We scan
+ backwards from the target label. If we find a NOTE_INSN_LOOP_BEG
+ before the next real insn, we assume the branch is to the top of
+ the loop. */
+ for (insn = PREV_INSN (target_label);
+ insn && GET_CODE (insn) == NOTE;
+ insn = PREV_INSN (insn))
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
+ return 2;
+
+ /* If this is a jump to the test of a loop, it is likely true. We scan
+ forwards from the target label. If we find a NOTE_INSN_LOOP_VTOP
+ before the next real insn, we assume the branch is to the loop branch
+ test. */
+ for (insn = NEXT_INSN (target_label);
+ insn && GET_CODE (insn) == NOTE;
+ insn = PREV_INSN (insn))
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_VTOP)
+ return 1;
+ }
+
+ /* Look at the relative rarities of the fallthough and destination. If
+ they differ, we can predict the branch that way. */
+
+ switch (rare_fallthrough - rare_dest)
+ {
+ case -2:
+ return -1;
+ case -1:
+ return 0;
+ case 0:
+ break;
+ case 1:
+ return 1;
+ case 2:
+ return 2;
+ }
+
+ /* If we couldn't figure out what this jump was, assume it won't be
+ taken. This should be rare. */
+ if (condition == 0)
+ return 0;
+
+ /* EQ tests are usually false and NE tests are usually true. Also,
+ most quantities are positive, so we can make the appropriate guesses
+ about signed comparisons against zero. */
+ switch (GET_CODE (condition))
+ {
+ case CONST_INT:
+ /* Unconditional branch. */
+ return 1;
+ case EQ:
+ return 0;
+ case NE:
+ return 1;
+ case LE:
+ case LT:
+ if (XEXP (condition, 1) == const0_rtx)
+ return 0;
+ break;
+ case GE:
+ case GT:
+ if (XEXP (condition, 1) == const0_rtx)
+ return 1;
+ break;
+ }
+
+ /* Predict backward branches usually take, forward branches usually not. If
+ we don't know whether this is forward or backward, assume the branch
+ will be taken, since most are. */
+ return (target_label == 0 || INSN_UID (jump_insn) > max_uid
+ || INSN_UID (target_label) > max_uid
+ || (uid_to_ruid[INSN_UID (jump_insn)]
+ > uid_to_ruid[INSN_UID (target_label)]));;
+}
+
+/* Return the condition under which INSN will branch to TARGET. If TARGET
+ is zero, return the condition under which INSN will return. If INSN is
+ an unconditional branch, return const_true_rtx. If INSN isn't a simple
+ type of jump, or it doesn't go to TARGET, return 0. */
+
+static rtx
+get_branch_condition (insn, target)
+ rtx insn;
+ rtx target;
+{
+ rtx pat = PATTERN (insn);
+ rtx src;
+
+ if (condjump_in_parallel_p (insn))
+ pat = XVECEXP (pat, 0, 0);
+
+ if (GET_CODE (pat) == RETURN)
+ return target == 0 ? const_true_rtx : 0;
+
+ else if (GET_CODE (pat) != SET || SET_DEST (pat) != pc_rtx)
+ return 0;
+
+ src = SET_SRC (pat);
+ if (GET_CODE (src) == LABEL_REF && XEXP (src, 0) == target)
+ return const_true_rtx;
+
+ else if (GET_CODE (src) == IF_THEN_ELSE
+ && ((target == 0 && GET_CODE (XEXP (src, 1)) == RETURN)
+ || (GET_CODE (XEXP (src, 1)) == LABEL_REF
+ && XEXP (XEXP (src, 1), 0) == target))
+ && XEXP (src, 2) == pc_rtx)
+ return XEXP (src, 0);
+
+ else if (GET_CODE (src) == IF_THEN_ELSE
+ && ((target == 0 && GET_CODE (XEXP (src, 2)) == RETURN)
+ || (GET_CODE (XEXP (src, 2)) == LABEL_REF
+ && XEXP (XEXP (src, 2), 0) == target))
+ && XEXP (src, 1) == pc_rtx)
+ return gen_rtx (reverse_condition (GET_CODE (XEXP (src, 0))),
+ GET_MODE (XEXP (src, 0)),
+ XEXP (XEXP (src, 0), 0), XEXP (XEXP (src, 0), 1));
+
+ return 0;
+}
+
+/* Return non-zero if CONDITION is more strict than the condition of
+ INSN, i.e., if INSN will always branch if CONDITION is true. */
+
+static int
+condition_dominates_p (condition, insn)
+ rtx condition;
+ rtx insn;
+{
+ rtx other_condition = get_branch_condition (insn, JUMP_LABEL (insn));
+ enum rtx_code code = GET_CODE (condition);
+ enum rtx_code other_code;
+
+ if (rtx_equal_p (condition, other_condition)
+ || other_condition == const_true_rtx)
+ return 1;
+
+ else if (condition == const_true_rtx || other_condition == 0)
+ return 0;
+
+ other_code = GET_CODE (other_condition);
+ if (GET_RTX_LENGTH (code) != 2 || GET_RTX_LENGTH (other_code) != 2
+ || ! rtx_equal_p (XEXP (condition, 0), XEXP (other_condition, 0))
+ || ! rtx_equal_p (XEXP (condition, 1), XEXP (other_condition, 1)))
+ return 0;
+
+ return comparison_dominates_p (code, other_code);
+}
+
+/* Return non-zero if redirecting JUMP to NEWLABEL does not invalidate
+ any insns already in the delay slot of JUMP. */
+
+static int
+redirect_with_delay_slots_safe_p (jump, newlabel, seq)
+ rtx jump, newlabel, seq;
+{
+ int flags, slots, i;
+ rtx pat = PATTERN (seq);
+
+ /* Make sure all the delay slots of this jump would still
+ be valid after threading the jump. If they are still
+ valid, then return non-zero. */
+
+ flags = get_jump_flags (jump, newlabel);
+ for (i = 1; i < XVECLEN (pat, 0); i++)
+ if (! (
+#ifdef ANNUL_IFFALSE_SLOTS
+ (INSN_ANNULLED_BRANCH_P (jump)
+ && INSN_FROM_TARGET_P (XVECEXP (pat, 0, i)))
+ ? eligible_for_annul_false (jump, i - 1,
+ XVECEXP (pat, 0, i), flags) :
+#endif
+#ifdef ANNUL_IFTRUE_SLOTS
+ (INSN_ANNULLED_BRANCH_P (jump)
+ && ! INSN_FROM_TARGET_P (XVECEXP (pat, 0, i)))
+ ? eligible_for_annul_true (jump, i - 1,
+ XVECEXP (pat, 0, i), flags) :
+#endif
+ eligible_for_delay (jump, i -1, XVECEXP (pat, 0, i), flags)))
+ break;
+
+ return (i == XVECLEN (pat, 0));
+}
+
+/* Return non-zero if redirecting JUMP to NEWLABEL does not invalidate
+ any insns we wish to place in the delay slot of JUMP. */
+
+static int
+redirect_with_delay_list_safe_p (jump, newlabel, delay_list)
+ rtx jump, newlabel, delay_list;
+{
+ int flags, i;
+ rtx li;
+
+ /* Make sure all the insns in DELAY_LIST would still be
+ valid after threading the jump. If they are still
+ valid, then return non-zero. */
+
+ flags = get_jump_flags (jump, newlabel);
+ for (li = delay_list, i = 0; li; li = XEXP (li, 1), i++)
+ if (! (
+#ifdef ANNUL_IFFALSE_SLOTS
+ (INSN_ANNULLED_BRANCH_P (jump)
+ && INSN_FROM_TARGET_P (XEXP (li, 0)))
+ ? eligible_for_annul_false (jump, i, XEXP (li, 0), flags) :
+#endif
+#ifdef ANNUL_IFTRUE_SLOTS
+ (INSN_ANNULLED_BRANCH_P (jump)
+ && ! INSN_FROM_TARGET_P (XEXP (li, 0)))
+ ? eligible_for_annul_true (jump, i, XEXP (li, 0), flags) :
+#endif
+ eligible_for_delay (jump, i, XEXP (li, 0), flags)))
+ break;
+
+ return (li == NULL);
+}
+
+
+/* INSN branches to an insn whose pattern SEQ is a SEQUENCE. Given that
+ the condition tested by INSN is CONDITION and the resources shown in
+ OTHER_NEEDED are needed after INSN, see whether INSN can take all the insns
+ from SEQ's delay list, in addition to whatever insns it may execute
+ (in DELAY_LIST). SETS and NEEDED are denote resources already set and
+ needed while searching for delay slot insns. Return the concatenated
+ delay list if possible, otherwise, return 0.
+
+ SLOTS_TO_FILL is the total number of slots required by INSN, and
+ PSLOTS_FILLED points to the number filled so far (also the number of
+ insns in DELAY_LIST). It is updated with the number that have been
+ filled from the SEQUENCE, if any.
+
+ PANNUL_P points to a non-zero value if we already know that we need
+ to annul INSN. If this routine determines that annulling is needed,
+ it may set that value non-zero.
+
+ PNEW_THREAD points to a location that is to receive the place at which
+ execution should continue. */
+
+static rtx
+steal_delay_list_from_target (insn, condition, seq, delay_list,
+ sets, needed, other_needed,
+ slots_to_fill, pslots_filled, pannul_p,
+ pnew_thread)
+ rtx insn, condition;
+ rtx seq;
+ rtx delay_list;
+ struct resources *sets, *needed, *other_needed;
+ int slots_to_fill;
+ int *pslots_filled;
+ int *pannul_p;
+ rtx *pnew_thread;
+{
+ rtx temp;
+ int slots_remaining = slots_to_fill - *pslots_filled;
+ int total_slots_filled = *pslots_filled;
+ rtx new_delay_list = 0;
+ int must_annul = *pannul_p;
+ int i;
+
+ /* We can't do anything if there are more delay slots in SEQ than we
+ can handle, or if we don't know that it will be a taken branch.
+
+ We know that it will be a taken branch if it is either an unconditional
+ branch or a conditional branch with a stricter branch condition. */
+
+ if (XVECLEN (seq, 0) - 1 > slots_remaining
+ || ! condition_dominates_p (condition, XVECEXP (seq, 0, 0)))
+ return delay_list;
+
+ for (i = 1; i < XVECLEN (seq, 0); i++)
+ {
+ rtx trial = XVECEXP (seq, 0, i);
+ int flags;
+
+ if (insn_references_resource_p (trial, sets, 0)
+ || insn_sets_resource_p (trial, needed, 0)
+ || insn_sets_resource_p (trial, sets, 0)
+#ifdef HAVE_cc0
+ /* If TRIAL sets CC0, we can't copy it, so we can't steal this
+ delay list. */
+ || find_reg_note (trial, REG_CC_USER, NULL_RTX)
+#endif
+ /* If TRIAL is from the fallthrough code of an annulled branch insn
+ in SEQ, we cannot use it. */
+ || (INSN_ANNULLED_BRANCH_P (XVECEXP (seq, 0, 0))
+ && ! INSN_FROM_TARGET_P (trial)))
+ return delay_list;
+
+ /* If this insn was already done (usually in a previous delay slot),
+ pretend we put it in our delay slot. */
+ if (redundant_insn_p (trial, insn, new_delay_list))
+ continue;
+
+ /* We will end up re-vectoring this branch, so compute flags
+ based on jumping to the new label. */
+ flags = get_jump_flags (insn, JUMP_LABEL (XVECEXP (seq, 0, 0)));
+
+ if (! must_annul
+ && ((condition == const_true_rtx
+ || (! insn_sets_resource_p (trial, other_needed, 0)
+ && ! may_trap_p (PATTERN (trial)))))
+ ? eligible_for_delay (insn, total_slots_filled, trial, flags)
+ : (must_annul = 1,
+ eligible_for_annul_false (insn, total_slots_filled, trial, flags)))
+ {
+ temp = copy_rtx (trial);
+ INSN_FROM_TARGET_P (temp) = 1;
+ new_delay_list = add_to_delay_list (temp, new_delay_list);
+ total_slots_filled++;
+
+ if (--slots_remaining == 0)
+ break;
+ }
+ else
+ return delay_list;
+ }
+
+ /* Show the place to which we will be branching. */
+ *pnew_thread = next_active_insn (JUMP_LABEL (XVECEXP (seq, 0, 0)));
+
+ /* Add any new insns to the delay list and update the count of the
+ number of slots filled. */
+ *pslots_filled = total_slots_filled;
+ *pannul_p = must_annul;
+
+ if (delay_list == 0)
+ return new_delay_list;
+
+ for (temp = new_delay_list; temp; temp = XEXP (temp, 1))
+ delay_list = add_to_delay_list (XEXP (temp, 0), delay_list);
+
+ return delay_list;
+}
+
+/* Similar to steal_delay_list_from_target except that SEQ is on the
+ fallthrough path of INSN. Here we only do something if the delay insn
+ of SEQ is an unconditional branch. In that case we steal its delay slot
+ for INSN since unconditional branches are much easier to fill. */
+
+static rtx
+steal_delay_list_from_fallthrough (insn, condition, seq,
+ delay_list, sets, needed, other_needed,
+ slots_to_fill, pslots_filled, pannul_p)
+ rtx insn, condition;
+ rtx seq;
+ rtx delay_list;
+ struct resources *sets, *needed, *other_needed;
+ int slots_to_fill;
+ int *pslots_filled;
+ int *pannul_p;
+{
+ int i;
+ int flags;
+
+ flags = get_jump_flags (insn, JUMP_LABEL (insn));
+
+ /* We can't do anything if SEQ's delay insn isn't an
+ unconditional branch. */
+
+ if (! simplejump_p (XVECEXP (seq, 0, 0))
+ && GET_CODE (PATTERN (XVECEXP (seq, 0, 0))) != RETURN)
+ return delay_list;
+
+ for (i = 1; i < XVECLEN (seq, 0); i++)
+ {
+ rtx trial = XVECEXP (seq, 0, i);
+
+ /* If TRIAL sets CC0, stealing it will move it too far from the use
+ of CC0. */
+ if (insn_references_resource_p (trial, sets, 0)
+ || insn_sets_resource_p (trial, needed, 0)
+ || insn_sets_resource_p (trial, sets, 0)
+#ifdef HAVE_cc0
+ || sets_cc0_p (PATTERN (trial))
+#endif
+ )
+
+ break;
+
+ /* If this insn was already done, we don't need it. */
+ if (redundant_insn_p (trial, insn, delay_list))
+ {
+ delete_from_delay_slot (trial);
+ continue;
+ }
+
+ if (! *pannul_p
+ && ((condition == const_true_rtx
+ || (! insn_sets_resource_p (trial, other_needed, 0)
+ && ! may_trap_p (PATTERN (trial)))))
+ ? eligible_for_delay (insn, *pslots_filled, trial, flags)
+ : (*pannul_p = 1,
+ eligible_for_annul_true (insn, *pslots_filled, trial, flags)))
+ {
+ delete_from_delay_slot (trial);
+ delay_list = add_to_delay_list (trial, delay_list);
+
+ if (++(*pslots_filled) == slots_to_fill)
+ break;
+ }
+ else
+ break;
+ }
+
+ return delay_list;
+}
+
+/* Try merging insns starting at THREAD which match exactly the insns in
+ INSN's delay list.
+
+ If all insns were matched and the insn was previously annulling, the
+ annul bit will be cleared.
+
+ For each insn that is merged, if the branch is or will be non-annulling,
+ we delete the merged insn. */
+
+static void
+try_merge_delay_insns (insn, thread)
+ rtx insn, thread;
+{
+ rtx trial, next_trial;
+ rtx delay_insn = XVECEXP (PATTERN (insn), 0, 0);
+ int annul_p = INSN_ANNULLED_BRANCH_P (delay_insn);
+ int slot_number = 1;
+ int num_slots = XVECLEN (PATTERN (insn), 0);
+ rtx next_to_match = XVECEXP (PATTERN (insn), 0, slot_number);
+ struct resources set, needed;
+ rtx merged_insns = 0;
+ int i;
+ int flags;
+
+ flags = get_jump_flags (delay_insn, JUMP_LABEL (delay_insn));
+
+ CLEAR_RESOURCE (&needed);
+ CLEAR_RESOURCE (&set);
+
+ /* If this is not an annulling branch, take into account anything needed in
+ NEXT_TO_MATCH. This prevents two increments from being incorrectly
+ folded into one. If we are annulling, this would be the correct
+ thing to do. (The alternative, looking at things set in NEXT_TO_MATCH
+ will essentially disable this optimization. This method is somewhat of
+ a kludge, but I don't see a better way.) */
+ if (! annul_p)
+ mark_referenced_resources (next_to_match, &needed, 1);
+
+ for (trial = thread; !stop_search_p (trial, 1); trial = next_trial)
+ {
+ rtx pat = PATTERN (trial);
+
+ next_trial = next_nonnote_insn (trial);
+
+ /* TRIAL must be a CALL_INSN or INSN. Skip USE and CLOBBER. */
+ if (GET_CODE (trial) == INSN
+ && (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER))
+ continue;
+
+ if (GET_CODE (next_to_match) == GET_CODE (trial)
+#ifdef HAVE_cc0
+ /* We can't share an insn that sets cc0. */
+ && ! sets_cc0_p (pat)
+#endif
+ && ! insn_references_resource_p (trial, &set, 1)
+ && ! insn_sets_resource_p (trial, &set, 1)
+ && ! insn_sets_resource_p (trial, &needed, 1)
+ && (trial = try_split (pat, trial, 0)) != 0
+ /* Update next_trial, in case try_split succeeded. */
+ && (next_trial = next_nonnote_insn (trial))
+ && rtx_equal_p (PATTERN (next_to_match), PATTERN (trial))
+ /* Have to test this condition if annul condition is different
+ from (and less restrictive than) non-annulling one. */
+ && eligible_for_delay (delay_insn, slot_number - 1, trial, flags))
+ {
+
+ if (! annul_p)
+ {
+ update_block (trial, thread);
+ delete_insn (trial);
+ INSN_FROM_TARGET_P (next_to_match) = 0;
+ }
+ else
+ merged_insns = gen_rtx (INSN_LIST, VOIDmode, trial, merged_insns);
+
+ if (++slot_number == num_slots)
+ break;
+
+ next_to_match = XVECEXP (PATTERN (insn), 0, slot_number);
+ if (! annul_p)
+ mark_referenced_resources (next_to_match, &needed, 1);
+ }
+
+ mark_set_resources (trial, &set, 0, 1);
+ mark_referenced_resources (trial, &needed, 1);
+ }
+
+ /* See if we stopped on a filled insn. If we did, try to see if its
+ delay slots match. */
+ if (slot_number != num_slots
+ && trial && GET_CODE (trial) == INSN
+ && GET_CODE (PATTERN (trial)) == SEQUENCE
+ && ! INSN_ANNULLED_BRANCH_P (XVECEXP (PATTERN (trial), 0, 0)))
+ {
+ rtx pat = PATTERN (trial);
+ rtx filled_insn = XVECEXP (pat, 0, 0);
+
+ /* Account for resources set/needed by the filled insn. */
+ mark_set_resources (filled_insn, &set, 0, 1);
+ mark_referenced_resources (filled_insn, &needed, 1);
+
+ for (i = 1; i < XVECLEN (pat, 0); i++)
+ {
+ rtx dtrial = XVECEXP (pat, 0, i);
+
+ if (! insn_references_resource_p (dtrial, &set, 1)
+ && ! insn_sets_resource_p (dtrial, &set, 1)
+ && ! insn_sets_resource_p (dtrial, &needed, 1)
+#ifdef HAVE_cc0
+ && ! sets_cc0_p (PATTERN (dtrial))
+#endif
+ && rtx_equal_p (PATTERN (next_to_match), PATTERN (dtrial))
+ && eligible_for_delay (delay_insn, slot_number - 1, dtrial, flags))
+ {
+ if (! annul_p)
+ {
+ update_block (dtrial, thread);
+ delete_from_delay_slot (dtrial);
+ INSN_FROM_TARGET_P (next_to_match) = 0;
+ }
+ else
+ merged_insns = gen_rtx (INSN_LIST, SImode, dtrial,
+ merged_insns);
+
+ if (++slot_number == num_slots)
+ break;
+
+ next_to_match = XVECEXP (PATTERN (insn), 0, slot_number);
+ }
+ }
+ }
+
+ /* If all insns in the delay slot have been matched and we were previously
+ annulling the branch, we need not any more. In that case delete all the
+ merged insns. Also clear the INSN_FROM_TARGET_P bit of each insn the
+ the delay list so that we know that it isn't only being used at the
+ target. */
+ if (slot_number == num_slots && annul_p)
+ {
+ for (; merged_insns; merged_insns = XEXP (merged_insns, 1))
+ {
+ if (GET_MODE (merged_insns) == SImode)
+ {
+ update_block (XEXP (merged_insns, 0), thread);
+ delete_from_delay_slot (XEXP (merged_insns, 0));
+ }
+ else
+ {
+ update_block (XEXP (merged_insns, 0), thread);
+ delete_insn (XEXP (merged_insns, 0));
+ }
+ }
+
+ INSN_ANNULLED_BRANCH_P (delay_insn) = 0;
+
+ for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
+ INSN_FROM_TARGET_P (XVECEXP (PATTERN (insn), 0, i)) = 0;
+ }
+}
+
+/* See if INSN is redundant with an insn in front of TARGET. Often this
+ is called when INSN is a candidate for a delay slot of TARGET.
+ DELAY_LIST are insns that will be placed in delay slots of TARGET in front
+ of INSN. Often INSN will be redundant with an insn in a delay slot of
+ some previous insn. This happens when we have a series of branches to the
+ same label; in that case the first insn at the target might want to go
+ into each of the delay slots.
+
+ If we are not careful, this routine can take up a significant fraction
+ of the total compilation time (4%), but only wins rarely. Hence we
+ speed this routine up by making two passes. The first pass goes back
+ until it hits a label and sees if it find an insn with an identical
+ pattern. Only in this (relatively rare) event does it check for
+ data conflicts.
+
+ We do not split insns we encounter. This could cause us not to find a
+ redundant insn, but the cost of splitting seems greater than the possible
+ gain in rare cases. */
+
+static int
+redundant_insn_p (insn, target, delay_list)
+ rtx insn;
+ rtx target;
+ rtx delay_list;
+{
+ rtx target_main = target;
+ rtx ipat = PATTERN (insn);
+ rtx trial, pat;
+ struct resources needed, set;
+ int i;
+
+ /* Scan backwards looking for a match. */
+ for (trial = PREV_INSN (target); trial; trial = PREV_INSN (trial))
+ {
+ if (GET_CODE (trial) == CODE_LABEL)
+ return 0;
+
+ if (GET_RTX_CLASS (GET_CODE (trial)) != 'i')
+ continue;
+
+ pat = PATTERN (trial);
+ if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER)
+ continue;
+
+ if (GET_CODE (pat) == SEQUENCE)
+ {
+ /* Stop for a CALL and its delay slots because it is difficult to
+ track its resource needs correctly. */
+ if (GET_CODE (XVECEXP (pat, 0, 0)) == CALL_INSN)
+ return 0;
+
+ /* Stop for an INSN or JUMP_INSN with delayed effects and its delay
+ slots because it is difficult to track its resource needs
+ correctly. */
+
+#ifdef INSN_SETS_ARE_DELAYED
+ if (INSN_SETS_ARE_DELAYED (XVECEXP (pat, 0, 0)))
+ return 0;
+#endif
+
+#ifdef INSN_REFERENCES_ARE_DELAYED
+ if (INSN_REFERENCES_ARE_DELAYED (XVECEXP (pat, 0, 0)))
+ return 0;
+#endif
+
+ /* See if any of the insns in the delay slot match, updating
+ resource requirements as we go. */
+ for (i = XVECLEN (pat, 0) - 1; i > 0; i--)
+ if (GET_CODE (XVECEXP (pat, 0, i)) == GET_CODE (insn)
+ && rtx_equal_p (PATTERN (XVECEXP (pat, 0, i)), ipat))
+ break;
+
+ /* If found a match, exit this loop early. */
+ if (i > 0)
+ break;
+ }
+
+ else if (GET_CODE (trial) == GET_CODE (insn) && rtx_equal_p (pat, ipat))
+ break;
+ }
+
+ /* If we didn't find an insn that matches, return 0. */
+ if (trial == 0)
+ return 0;
+
+ /* See what resources this insn sets and needs. If they overlap, or
+ if this insn references CC0, it can't be redundant. */
+
+ CLEAR_RESOURCE (&needed);
+ CLEAR_RESOURCE (&set);
+ mark_set_resources (insn, &set, 0, 1);
+ mark_referenced_resources (insn, &needed, 1);
+
+ /* If TARGET is a SEQUENCE, get the main insn. */
+ if (GET_CODE (target) == INSN && GET_CODE (PATTERN (target)) == SEQUENCE)
+ target_main = XVECEXP (PATTERN (target), 0, 0);
+
+ if (resource_conflicts_p (&needed, &set)
+#ifdef HAVE_cc0
+ || reg_mentioned_p (cc0_rtx, ipat)
+#endif
+ /* The insn requiring the delay may not set anything needed or set by
+ INSN. */
+ || insn_sets_resource_p (target_main, &needed, 1)
+ || insn_sets_resource_p (target_main, &set, 1))
+ return 0;
+
+ /* Insns we pass may not set either NEEDED or SET, so merge them for
+ simpler tests. */
+ needed.memory |= set.memory;
+ IOR_HARD_REG_SET (needed.regs, set.regs);
+
+ /* This insn isn't redundant if it conflicts with an insn that either is
+ or will be in a delay slot of TARGET. */
+
+ while (delay_list)
+ {
+ if (insn_sets_resource_p (XEXP (delay_list, 0), &needed, 1))
+ return 0;
+ delay_list = XEXP (delay_list, 1);
+ }
+
+ if (GET_CODE (target) == INSN && GET_CODE (PATTERN (target)) == SEQUENCE)
+ for (i = 1; i < XVECLEN (PATTERN (target), 0); i++)
+ if (insn_sets_resource_p (XVECEXP (PATTERN (target), 0, i), &needed, 1))
+ return 0;
+
+ /* Scan backwards until we reach a label or an insn that uses something
+ INSN sets or sets something insn uses or sets. */
+
+ for (trial = PREV_INSN (target);
+ trial && GET_CODE (trial) != CODE_LABEL;
+ trial = PREV_INSN (trial))
+ {
+ if (GET_CODE (trial) != INSN && GET_CODE (trial) != CALL_INSN
+ && GET_CODE (trial) != JUMP_INSN)
+ continue;
+
+ pat = PATTERN (trial);
+ if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER)
+ continue;
+
+ if (GET_CODE (pat) == SEQUENCE)
+ {
+ /* If this is a CALL_INSN and its delay slots, it is hard to track
+ the resource needs properly, so give up. */
+ if (GET_CODE (XVECEXP (pat, 0, 0)) == CALL_INSN)
+ return 0;
+
+ /* If this this is an INSN or JUMP_INSN with delayed effects, it
+ is hard to track the resource needs properly, so give up. */
+
+#ifdef INSN_SETS_ARE_DELAYED
+ if (INSN_SETS_ARE_DELAYED (XVECEXP (pat, 0, 0)))
+ return 0;
+#endif
+
+#ifdef INSN_REFERENCES_ARE_DELAYED
+ if (INSN_REFERENCES_ARE_DELAYED (XVECEXP (pat, 0, 0)))
+ return 0;
+#endif
+
+ /* See if any of the insns in the delay slot match, updating
+ resource requirements as we go. */
+ for (i = XVECLEN (pat, 0) - 1; i > 0; i--)
+ {
+ rtx candidate = XVECEXP (pat, 0, i);
+
+ /* If an insn will be annulled if the branch is false, it isn't
+ considered as a possible duplicate insn. */
+ if (rtx_equal_p (PATTERN (candidate), ipat)
+ && ! (INSN_ANNULLED_BRANCH_P (XVECEXP (pat, 0, 0))
+ && INSN_FROM_TARGET_P (candidate)))
+ {
+ /* Show that this insn will be used in the sequel. */
+ INSN_FROM_TARGET_P (candidate) = 0;
+ return 1;
+ }
+
+ /* Unless this is an annulled insn from the target of a branch,
+ we must stop if it sets anything needed or set by INSN. */
+ if ((! INSN_ANNULLED_BRANCH_P (XVECEXP (pat, 0, 0))
+ || ! INSN_FROM_TARGET_P (candidate))
+ && insn_sets_resource_p (candidate, &needed, 1))
+ return 0;
+ }
+
+
+ /* If the insn requiring the delay slot conflicts with INSN, we
+ must stop. */
+ if (insn_sets_resource_p (XVECEXP (pat, 0, 0), &needed, 1))
+ return 0;
+ }
+ else
+ {
+ /* See if TRIAL is the same as INSN. */
+ pat = PATTERN (trial);
+ if (rtx_equal_p (pat, ipat))
+ return 1;
+
+ /* Can't go any further if TRIAL conflicts with INSN. */
+ if (insn_sets_resource_p (trial, &needed, 1))
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/* Return 1 if THREAD can only be executed in one way. If LABEL is non-zero,
+ it is the target of the branch insn being scanned. If ALLOW_FALLTHROUGH
+ is non-zero, we are allowed to fall into this thread; otherwise, we are
+ not.
+
+ If LABEL is used more than one or we pass a label other than LABEL before
+ finding an active insn, we do not own this thread. */
+
+static int
+own_thread_p (thread, label, allow_fallthrough)
+ rtx thread;
+ rtx label;
+ int allow_fallthrough;
+{
+ rtx active_insn;
+ rtx insn;
+
+ /* We don't own the function end. */
+ if (thread == 0)
+ return 0;
+
+ /* Get the first active insn, or THREAD, if it is an active insn. */
+ active_insn = next_active_insn (PREV_INSN (thread));
+
+ for (insn = thread; insn != active_insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == CODE_LABEL
+ && (insn != label || LABEL_NUSES (insn) != 1))
+ return 0;
+
+ if (allow_fallthrough)
+ return 1;
+
+ /* Ensure that we reach a BARRIER before any insn or label. */
+ for (insn = prev_nonnote_insn (thread);
+ insn == 0 || GET_CODE (insn) != BARRIER;
+ insn = prev_nonnote_insn (insn))
+ if (insn == 0
+ || GET_CODE (insn) == CODE_LABEL
+ || (GET_CODE (insn) == INSN
+ && GET_CODE (PATTERN (insn)) != USE
+ && GET_CODE (PATTERN (insn)) != CLOBBER))
+ return 0;
+
+ return 1;
+}
+
+/* Find the number of the basic block that starts closest to INSN. Return -1
+ if we couldn't find such a basic block. */
+
+static int
+find_basic_block (insn)
+ rtx insn;
+{
+ int i;
+
+ /* Scan backwards to the previous BARRIER. Then see if we can find a
+ label that starts a basic block. Return the basic block number. */
+
+ for (insn = prev_nonnote_insn (insn);
+ insn && GET_CODE (insn) != BARRIER;
+ insn = prev_nonnote_insn (insn))
+ ;
+
+ /* The start of the function is basic block zero. */
+ if (insn == 0)
+ return 0;
+
+ /* See if any of the upcoming CODE_LABELs start a basic block. If we reach
+ anything other than a CODE_LABEL or note, we can't find this code. */
+ for (insn = next_nonnote_insn (insn);
+ insn && GET_CODE (insn) == CODE_LABEL;
+ insn = next_nonnote_insn (insn))
+ {
+ for (i = 0; i < n_basic_blocks; i++)
+ if (insn == basic_block_head[i])
+ return i;
+ }
+
+ return -1;
+}
+
+/* Called when INSN is being moved from a location near the target of a jump.
+ We leave a marker of the form (use (INSN)) immediately in front
+ of WHERE for mark_target_live_regs. These markers will be deleted when
+ reorg finishes.
+
+ We used to try to update the live status of registers if WHERE is at
+ the start of a basic block, but that can't work since we may remove a
+ BARRIER in relax_delay_slots. */
+
+static void
+update_block (insn, where)
+ rtx insn;
+ rtx where;
+{
+ int b;
+
+ /* Ignore if this was in a delay slot and it came from the target of
+ a branch. */
+ if (INSN_FROM_TARGET_P (insn))
+ return;
+
+ emit_insn_before (gen_rtx (USE, VOIDmode, insn), where);
+
+ /* INSN might be making a value live in a block where it didn't use to
+ be. So recompute liveness information for this block. */
+
+ b = find_basic_block (insn);
+ if (b != -1)
+ bb_ticks[b]++;
+}
+
+/* Similar to REDIRECT_JUMP except that we update the BB_TICKS entry for
+ the basic block containing the jump. */
+
+static int
+reorg_redirect_jump (jump, nlabel)
+ rtx jump;
+ rtx nlabel;
+{
+ int b = find_basic_block (jump);
+
+ if (b != -1)
+ bb_ticks[b]++;
+
+ return redirect_jump (jump, nlabel);
+}
+
+/* Called when INSN is being moved forward into a delay slot of DELAYED_INSN.
+ We check every instruction between INSN and DELAYED_INSN for REG_DEAD notes
+ that reference values used in INSN. If we find one, then we move the
+ REG_DEAD note to INSN.
+
+ This is needed to handle the case where an later insn (after INSN) has a
+ REG_DEAD note for a register used by INSN, and this later insn subsequently
+ gets moved before a CODE_LABEL because it is a redundant insn. In this
+ case, mark_target_live_regs may be confused into thinking the register
+ is dead because it sees a REG_DEAD note immediately before a CODE_LABEL. */
+
+static void
+update_reg_dead_notes (insn, delayed_insn)
+ rtx insn, delayed_insn;
+{
+ rtx p, link, next;
+
+ for (p = next_nonnote_insn (insn); p != delayed_insn;
+ p = next_nonnote_insn (p))
+ for (link = REG_NOTES (p); link; link = next)
+ {
+ next = XEXP (link, 1);
+
+ if (REG_NOTE_KIND (link) != REG_DEAD
+ || GET_CODE (XEXP (link, 0)) != REG)
+ continue;
+
+ if (reg_referenced_p (XEXP (link, 0), PATTERN (insn)))
+ {
+ /* Move the REG_DEAD note from P to INSN. */
+ remove_note (p, link);
+ XEXP (link, 1) = REG_NOTES (insn);
+ REG_NOTES (insn) = link;
+ }
+ }
+}
+
+/* Marks registers possibly live at the current place being scanned by
+ mark_target_live_regs. Used only by next two function. */
+
+static HARD_REG_SET current_live_regs;
+
+/* Marks registers for which we have seen a REG_DEAD note but no assignment.
+ Also only used by the next two functions. */
+
+static HARD_REG_SET pending_dead_regs;
+
+/* Utility function called from mark_target_live_regs via note_stores.
+ It deadens any CLOBBERed registers and livens any SET registers. */
+
+static void
+update_live_status (dest, x)
+ rtx dest;
+ rtx x;
+{
+ int first_regno, last_regno;
+ int i;
+
+ if (GET_CODE (dest) != REG
+ && (GET_CODE (dest) != SUBREG || GET_CODE (SUBREG_REG (dest)) != REG))
+ return;
+
+ if (GET_CODE (dest) == SUBREG)
+ first_regno = REGNO (SUBREG_REG (dest)) + SUBREG_WORD (dest);
+ else
+ first_regno = REGNO (dest);
+
+ last_regno = first_regno + HARD_REGNO_NREGS (first_regno, GET_MODE (dest));
+
+ if (GET_CODE (x) == CLOBBER)
+ for (i = first_regno; i < last_regno; i++)
+ CLEAR_HARD_REG_BIT (current_live_regs, i);
+ else
+ for (i = first_regno; i < last_regno; i++)
+ {
+ SET_HARD_REG_BIT (current_live_regs, i);
+ CLEAR_HARD_REG_BIT (pending_dead_regs, i);
+ }
+}
+
+/* Similar to next_insn, but ignores insns in the delay slots of
+ an annulled branch. */
+
+static rtx
+next_insn_no_annul (insn)
+ rtx insn;
+{
+ if (insn)
+ {
+ /* If INSN is an annulled branch, skip any insns from the target
+ of the branch. */
+ if (INSN_ANNULLED_BRANCH_P (insn)
+ && NEXT_INSN (PREV_INSN (insn)) != insn)
+ while (INSN_FROM_TARGET_P (NEXT_INSN (insn)))
+ insn = NEXT_INSN (insn);
+
+ insn = NEXT_INSN (insn);
+ if (insn && GET_CODE (insn) == INSN
+ && GET_CODE (PATTERN (insn)) == SEQUENCE)
+ insn = XVECEXP (PATTERN (insn), 0, 0);
+ }
+
+ return insn;
+}
+
+/* Set the resources that are live at TARGET.
+
+ If TARGET is zero, we refer to the end of the current function and can
+ return our precomputed value.
+
+ Otherwise, we try to find out what is live by consulting the basic block
+ information. This is tricky, because we must consider the actions of
+ reload and jump optimization, which occur after the basic block information
+ has been computed.
+
+ Accordingly, we proceed as follows::
+
+ We find the previous BARRIER and look at all immediately following labels
+ (with no intervening active insns) to see if any of them start a basic
+ block. If we hit the start of the function first, we use block 0.
+
+ Once we have found a basic block and a corresponding first insns, we can
+ accurately compute the live status from basic_block_live_regs and
+ reg_renumber. (By starting at a label following a BARRIER, we are immune
+ to actions taken by reload and jump.) Then we scan all insns between
+ that point and our target. For each CLOBBER (or for call-clobbered regs
+ when we pass a CALL_INSN), mark the appropriate registers are dead. For
+ a SET, mark them as live.
+
+ We have to be careful when using REG_DEAD notes because they are not
+ updated by such things as find_equiv_reg. So keep track of registers
+ marked as dead that haven't been assigned to, and mark them dead at the
+ next CODE_LABEL since reload and jump won't propagate values across labels.
+
+ If we cannot find the start of a basic block (should be a very rare
+ case, if it can happen at all), mark everything as potentially live.
+
+ Next, scan forward from TARGET looking for things set or clobbered
+ before they are used. These are not live.
+
+ Because we can be called many times on the same target, save our results
+ in a hash table indexed by INSN_UID. */
+
+static void
+mark_target_live_regs (target, res)
+ rtx target;
+ struct resources *res;
+{
+ int b = -1;
+ int i;
+ struct target_info *tinfo;
+ rtx insn, next;
+ rtx jump_insn = 0;
+ rtx jump_target;
+ HARD_REG_SET scratch;
+ struct resources set, needed;
+ int jump_count = 0;
+
+ /* Handle end of function. */
+ if (target == 0)
+ {
+ *res = end_of_function_needs;
+ return;
+ }
+
+ /* We have to assume memory is needed, but the CC isn't. */
+ res->memory = 1;
+ res->volatil = 0;
+ res->cc = 0;
+
+ /* See if we have computed this value already. */
+ for (tinfo = target_hash_table[INSN_UID (target) % TARGET_HASH_PRIME];
+ tinfo; tinfo = tinfo->next)
+ if (tinfo->uid == INSN_UID (target))
+ break;
+
+ /* Start by getting the basic block number. If we have saved information,
+ we can get it from there unless the insn at the start of the basic block
+ has been deleted. */
+ if (tinfo && tinfo->block != -1
+ && ! INSN_DELETED_P (basic_block_head[tinfo->block]))
+ b = tinfo->block;
+
+ if (b == -1)
+ b = find_basic_block (target);
+
+ if (tinfo)
+ {
+ /* If the information is up-to-date, use it. Otherwise, we will
+ update it below. */
+ if (b == tinfo->block && b != -1 && tinfo->bb_tick == bb_ticks[b])
+ {
+ COPY_HARD_REG_SET (res->regs, tinfo->live_regs);
+ return;
+ }
+ }
+ else
+ {
+ /* Allocate a place to put our results and chain it into the
+ hash table. */
+ tinfo = (struct target_info *) oballoc (sizeof (struct target_info));
+ tinfo->uid = INSN_UID (target);
+ tinfo->block = b;
+ tinfo->next = target_hash_table[INSN_UID (target) % TARGET_HASH_PRIME];
+ target_hash_table[INSN_UID (target) % TARGET_HASH_PRIME] = tinfo;
+ }
+
+ CLEAR_HARD_REG_SET (pending_dead_regs);
+
+ /* If we found a basic block, get the live registers from it and update
+ them with anything set or killed between its start and the insn before
+ TARGET. Otherwise, we must assume everything is live. */
+ if (b != -1)
+ {
+ regset regs_live = basic_block_live_at_start[b];
+ int offset, j;
+ REGSET_ELT_TYPE bit;
+ int regno;
+ rtx start_insn, stop_insn;
+
+ /* Compute hard regs live at start of block -- this is the real hard regs
+ marked live, plus live pseudo regs that have been renumbered to
+ hard regs. */
+
+#ifdef HARD_REG_SET
+ current_live_regs = *regs_live;
+#else
+ COPY_HARD_REG_SET (current_live_regs, regs_live);
+#endif
+
+ for (offset = 0, i = 0; offset < regset_size; offset++)
+ {
+ if (regs_live[offset] == 0)
+ i += REGSET_ELT_BITS;
+ else
+ for (bit = 1; bit && i < max_regno; bit <<= 1, i++)
+ if ((regs_live[offset] & bit)
+ && (regno = reg_renumber[i]) >= 0)
+ for (j = regno;
+ j < regno + HARD_REGNO_NREGS (regno,
+ PSEUDO_REGNO_MODE (i));
+ j++)
+ SET_HARD_REG_BIT (current_live_regs, j);
+ }
+
+ /* Get starting and ending insn, handling the case where each might
+ be a SEQUENCE. */
+ start_insn = (b == 0 ? get_insns () : basic_block_head[b]);
+ stop_insn = target;
+
+ if (GET_CODE (start_insn) == INSN
+ && GET_CODE (PATTERN (start_insn)) == SEQUENCE)
+ start_insn = XVECEXP (PATTERN (start_insn), 0, 0);
+
+ if (GET_CODE (stop_insn) == INSN
+ && GET_CODE (PATTERN (stop_insn)) == SEQUENCE)
+ stop_insn = next_insn (PREV_INSN (stop_insn));
+
+ for (insn = start_insn; insn != stop_insn;
+ insn = next_insn_no_annul (insn))
+ {
+ rtx link;
+ rtx real_insn = insn;
+
+ /* If this insn is from the target of a branch, it isn't going to
+ be used in the sequel. If it is used in both cases, this
+ test will not be true. */
+ if (INSN_FROM_TARGET_P (insn))
+ continue;
+
+ /* If this insn is a USE made by update_block, we care about the
+ underlying insn. */
+ if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == USE
+ && GET_RTX_CLASS (GET_CODE (XEXP (PATTERN (insn), 0))) == 'i')
+ real_insn = XEXP (PATTERN (insn), 0);
+
+ if (GET_CODE (real_insn) == CALL_INSN)
+ {
+ /* CALL clobbers all call-used regs that aren't fixed except
+ sp, ap, and fp. Do this before setting the result of the
+ call live. */
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (call_used_regs[i]
+ && i != STACK_POINTER_REGNUM && i != FRAME_POINTER_REGNUM
+ && i != ARG_POINTER_REGNUM
+#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ && i != HARD_FRAME_POINTER_REGNUM
+#endif
+#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ && ! (i == ARG_POINTER_REGNUM && fixed_regs[i])
+#endif
+#ifdef PIC_OFFSET_TABLE_REGNUM
+ && ! (i == PIC_OFFSET_TABLE_REGNUM && flag_pic)
+#endif
+ )
+ CLEAR_HARD_REG_BIT (current_live_regs, i);
+
+ /* A CALL_INSN sets any global register live, since it may
+ have been modified by the call. */
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (global_regs[i])
+ SET_HARD_REG_BIT (current_live_regs, i);
+ }
+
+ /* Mark anything killed in an insn to be deadened at the next
+ label. Ignore USE insns; the only REG_DEAD notes will be for
+ parameters. But they might be early. A CALL_INSN will usually
+ clobber registers used for parameters. It isn't worth bothering
+ with the unlikely case when it won't. */
+ if ((GET_CODE (real_insn) == INSN
+ && GET_CODE (PATTERN (real_insn)) != USE
+ && GET_CODE (PATTERN (real_insn)) != CLOBBER)
+ || GET_CODE (real_insn) == JUMP_INSN
+ || GET_CODE (real_insn) == CALL_INSN)
+ {
+ for (link = REG_NOTES (real_insn); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_DEAD
+ && GET_CODE (XEXP (link, 0)) == REG
+ && REGNO (XEXP (link, 0)) < FIRST_PSEUDO_REGISTER)
+ {
+ int first_regno = REGNO (XEXP (link, 0));
+ int last_regno
+ = (first_regno
+ + HARD_REGNO_NREGS (first_regno,
+ GET_MODE (XEXP (link, 0))));
+
+ for (i = first_regno; i < last_regno; i++)
+ SET_HARD_REG_BIT (pending_dead_regs, i);
+ }
+
+ note_stores (PATTERN (real_insn), update_live_status);
+
+ /* If any registers were unused after this insn, kill them.
+ These notes will always be accurate. */
+ for (link = REG_NOTES (real_insn); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_UNUSED
+ && GET_CODE (XEXP (link, 0)) == REG
+ && REGNO (XEXP (link, 0)) < FIRST_PSEUDO_REGISTER)
+ {
+ int first_regno = REGNO (XEXP (link, 0));
+ int last_regno
+ = (first_regno
+ + HARD_REGNO_NREGS (first_regno,
+ GET_MODE (XEXP (link, 0))));
+
+ for (i = first_regno; i < last_regno; i++)
+ CLEAR_HARD_REG_BIT (current_live_regs, i);
+ }
+ }
+
+ else if (GET_CODE (real_insn) == CODE_LABEL)
+ {
+ /* A label clobbers the pending dead registers since neither
+ reload nor jump will propagate a value across a label. */
+ AND_COMPL_HARD_REG_SET (current_live_regs, pending_dead_regs);
+ CLEAR_HARD_REG_SET (pending_dead_regs);
+ }
+
+ /* The beginning of the epilogue corresponds to the end of the
+ RTL chain when there are no epilogue insns. Certain resources
+ are implicitly required at that point. */
+ else if (GET_CODE (real_insn) == NOTE
+ && NOTE_LINE_NUMBER (real_insn) == NOTE_INSN_EPILOGUE_BEG)
+ IOR_HARD_REG_SET (current_live_regs, start_of_epilogue_needs.regs);
+ }
+
+ COPY_HARD_REG_SET (res->regs, current_live_regs);
+ tinfo->block = b;
+ tinfo->bb_tick = bb_ticks[b];
+ }
+ else
+ /* We didn't find the start of a basic block. Assume everything
+ in use. This should happen only extremely rarely. */
+ SET_HARD_REG_SET (res->regs);
+
+ /* Now step forward from TARGET looking for registers that are set before
+ they are used. These are dead. If we pass a label, any pending dead
+ registers that weren't yet used can be made dead. Stop when we pass a
+ conditional JUMP_INSN; follow the first few unconditional branches. */
+
+ CLEAR_RESOURCE (&set);
+ CLEAR_RESOURCE (&needed);
+
+ for (insn = target; insn; insn = next)
+ {
+ rtx this_jump_insn = insn;
+
+ next = NEXT_INSN (insn);
+ switch (GET_CODE (insn))
+ {
+ case CODE_LABEL:
+ AND_COMPL_HARD_REG_SET (pending_dead_regs, needed.regs);
+ AND_COMPL_HARD_REG_SET (res->regs, pending_dead_regs);
+ CLEAR_HARD_REG_SET (pending_dead_regs);
+ continue;
+
+ case BARRIER:
+ case NOTE:
+ continue;
+
+ case INSN:
+ if (GET_CODE (PATTERN (insn)) == USE)
+ {
+ /* If INSN is a USE made by update_block, we care about the
+ underlying insn. Any registers set by the underlying insn
+ are live since the insn is being done somewhere else. */
+ if (GET_RTX_CLASS (GET_CODE (XEXP (PATTERN (insn), 0))) == 'i')
+ mark_set_resources (XEXP (PATTERN (insn), 0), res, 0, 1);
+
+ /* All other USE insns are to be ignored. */
+ continue;
+ }
+ else if (GET_CODE (PATTERN (insn)) == CLOBBER)
+ continue;
+ else if (GET_CODE (PATTERN (insn)) == SEQUENCE)
+ {
+ /* An unconditional jump can be used to fill the delay slot
+ of a call, so search for a JUMP_INSN in any position. */
+ for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
+ {
+ this_jump_insn = XVECEXP (PATTERN (insn), 0, i);
+ if (GET_CODE (this_jump_insn) == JUMP_INSN)
+ break;
+ }
+ }
+ }
+
+ if (GET_CODE (this_jump_insn) == JUMP_INSN)
+ {
+ if (jump_count++ < 10
+ && (simplejump_p (this_jump_insn)
+ || GET_CODE (PATTERN (this_jump_insn)) == RETURN))
+ {
+ next = next_active_insn (JUMP_LABEL (this_jump_insn));
+ if (jump_insn == 0)
+ {
+ jump_insn = insn;
+ jump_target = JUMP_LABEL (this_jump_insn);
+ }
+ }
+ else
+ break;
+ }
+
+ mark_referenced_resources (insn, &needed, 1);
+ mark_set_resources (insn, &set, 0, 1);
+
+ COPY_HARD_REG_SET (scratch, set.regs);
+ AND_COMPL_HARD_REG_SET (scratch, needed.regs);
+ AND_COMPL_HARD_REG_SET (res->regs, scratch);
+ }
+
+ /* If we hit an unconditional branch, we have another way of finding out
+ what is live: we can see what is live at the branch target and include
+ anything used but not set before the branch. The only things that are
+ live are those that are live using the above test and the test below.
+
+ Don't try this if we expired our jump count above, since that would
+ mean there may be an infinite loop in the function being compiled. */
+
+ if (jump_insn && jump_count < 10)
+ {
+ struct resources new_resources;
+ rtx stop_insn = next_active_insn (jump_insn);
+
+ mark_target_live_regs (next_active_insn (jump_target), &new_resources);
+ CLEAR_RESOURCE (&set);
+ CLEAR_RESOURCE (&needed);
+
+ /* Include JUMP_INSN in the needed registers. */
+ for (insn = target; insn != stop_insn; insn = next_active_insn (insn))
+ {
+ mark_referenced_resources (insn, &needed, 1);
+
+ COPY_HARD_REG_SET (scratch, needed.regs);
+ AND_COMPL_HARD_REG_SET (scratch, set.regs);
+ IOR_HARD_REG_SET (new_resources.regs, scratch);
+
+ mark_set_resources (insn, &set, 0, 1);
+ }
+
+ AND_HARD_REG_SET (res->regs, new_resources.regs);
+ }
+
+ COPY_HARD_REG_SET (tinfo->live_regs, res->regs);
+}
+
+/* Scan a function looking for insns that need a delay slot and find insns to
+ put into the delay slot.
+
+ NON_JUMPS_P is non-zero if we are to only try to fill non-jump insns (such
+ as calls). We do these first since we don't want jump insns (that are
+ easier to fill) to get the only insns that could be used for non-jump insns.
+ When it is zero, only try to fill JUMP_INSNs.
+
+ When slots are filled in this manner, the insns (including the
+ delay_insn) are put together in a SEQUENCE rtx. In this fashion,
+ it is possible to tell whether a delay slot has really been filled
+ or not. `final' knows how to deal with this, by communicating
+ through FINAL_SEQUENCE. */
+
+static void
+fill_simple_delay_slots (first, non_jumps_p)
+ rtx first;
+ int non_jumps_p;
+{
+ register rtx insn, pat, trial, next_trial;
+ register int i, j;
+ int num_unfilled_slots = unfilled_slots_next - unfilled_slots_base;
+ struct resources needed, set;
+ register int slots_to_fill, slots_filled;
+ rtx delay_list;
+
+ for (i = 0; i < num_unfilled_slots; i++)
+ {
+ int flags;
+ /* Get the next insn to fill. If it has already had any slots assigned,
+ we can't do anything with it. Maybe we'll improve this later. */
+
+ insn = unfilled_slots_base[i];
+ if (insn == 0
+ || INSN_DELETED_P (insn)
+ || (GET_CODE (insn) == INSN
+ && GET_CODE (PATTERN (insn)) == SEQUENCE)
+ || (GET_CODE (insn) == JUMP_INSN && non_jumps_p)
+ || (GET_CODE (insn) != JUMP_INSN && ! non_jumps_p))
+ continue;
+
+ if (GET_CODE (insn) == JUMP_INSN)
+ flags = get_jump_flags (insn, JUMP_LABEL (insn));
+ else
+ flags = get_jump_flags (insn, NULL_RTX);
+ slots_to_fill = num_delay_slots (insn);
+ if (slots_to_fill == 0)
+ abort ();
+
+ /* This insn needs, or can use, some delay slots. SLOTS_TO_FILL
+ says how many. After initialization, first try optimizing
+
+ call _foo call _foo
+ nop add %o7,.-L1,%o7
+ b,a L1
+ nop
+
+ If this case applies, the delay slot of the call is filled with
+ the unconditional jump. This is done first to avoid having the
+ delay slot of the call filled in the backward scan. Also, since
+ the unconditional jump is likely to also have a delay slot, that
+ insn must exist when it is subsequently scanned.
+
+ This is tried on each insn with delay slots as some machines
+ have insns which perform calls, but are not represented as
+ CALL_INSNs. */
+
+ slots_filled = 0;
+ delay_list = 0;
+
+ if ((trial = next_active_insn (insn))
+ && GET_CODE (trial) == JUMP_INSN
+ && simplejump_p (trial)
+ && eligible_for_delay (insn, slots_filled, trial, flags)
+ && no_labels_between_p (insn, trial))
+ {
+ slots_filled++;
+ delay_list = add_to_delay_list (trial, delay_list);
+ /* Remove the unconditional jump from consideration for delay slot
+ filling and unthread it. */
+ if (unfilled_slots_base[i + 1] == trial)
+ unfilled_slots_base[i + 1] = 0;
+ {
+ rtx next = NEXT_INSN (trial);
+ rtx prev = PREV_INSN (trial);
+ if (prev)
+ NEXT_INSN (prev) = next;
+ if (next)
+ PREV_INSN (next) = prev;
+ }
+ }
+
+ /* Now, scan backwards from the insn to search for a potential
+ delay-slot candidate. Stop searching when a label or jump is hit.
+
+ For each candidate, if it is to go into the delay slot (moved
+ forward in execution sequence), it must not need or set any resources
+ that were set by later insns and must not set any resources that
+ are needed for those insns.
+
+ The delay slot insn itself sets resources unless it is a call
+ (in which case the called routine, not the insn itself, is doing
+ the setting). */
+
+ if (slots_filled < slots_to_fill)
+ {
+ CLEAR_RESOURCE (&needed);
+ CLEAR_RESOURCE (&set);
+ mark_set_resources (insn, &set, 0, 0);
+ mark_referenced_resources (insn, &needed, 0);
+
+ for (trial = prev_nonnote_insn (insn); ! stop_search_p (trial, 1);
+ trial = next_trial)
+ {
+ next_trial = prev_nonnote_insn (trial);
+
+ /* This must be an INSN or CALL_INSN. */
+ pat = PATTERN (trial);
+
+ /* USE and CLOBBER at this level was just for flow; ignore it. */
+ if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER)
+ continue;
+
+ /* Check for resource conflict first, to avoid unnecessary
+ splitting. */
+ if (! insn_references_resource_p (trial, &set, 1)
+ && ! insn_sets_resource_p (trial, &set, 1)
+ && ! insn_sets_resource_p (trial, &needed, 1)
+#ifdef HAVE_cc0
+ /* Can't separate set of cc0 from its use. */
+ && ! (reg_mentioned_p (cc0_rtx, pat)
+ && ! sets_cc0_p (cc0_rtx, pat))
+#endif
+ )
+ {
+ trial = try_split (pat, trial, 1);
+ next_trial = prev_nonnote_insn (trial);
+ if (eligible_for_delay (insn, slots_filled, trial, flags))
+ {
+ /* In this case, we are searching backward, so if we
+ find insns to put on the delay list, we want
+ to put them at the head, rather than the
+ tail, of the list. */
+
+ update_reg_dead_notes (trial, insn);
+ delay_list = gen_rtx (INSN_LIST, VOIDmode,
+ trial, delay_list);
+ update_block (trial, trial);
+ delete_insn (trial);
+ if (slots_to_fill == ++slots_filled)
+ break;
+ continue;
+ }
+ }
+
+ mark_set_resources (trial, &set, 0, 1);
+ mark_referenced_resources (trial, &needed, 1);
+ }
+ }
+
+ /* If all needed slots haven't been filled, we come here. */
+
+ /* Try to optimize case of jumping around a single insn. */
+#if defined(ANNUL_IFFALSE_SLOTS) || defined(ANNUL_IFTRUE_SLOTS)
+ if (slots_filled != slots_to_fill
+ && delay_list == 0
+ && GET_CODE (insn) == JUMP_INSN
+ && (condjump_p (insn) || condjump_in_parallel_p (insn)))
+ {
+ delay_list = optimize_skip (insn);
+ if (delay_list)
+ slots_filled += 1;
+ }
+#endif
+
+ /* Try to get insns from beyond the insn needing the delay slot.
+ These insns can neither set or reference resources set in insns being
+ skipped, cannot set resources in the insn being skipped, and, if this
+ is a CALL_INSN (or a CALL_INSN is passed), cannot trap (because the
+ call might not return).
+
+ If this is a conditional jump, see if it merges back to us early
+ enough for us to pick up insns from the merge point. Don't do
+ this if there is another branch to our label unless we pass all of
+ them.
+
+ Another similar merge is if we jump to the same place that a
+ later unconditional jump branches to. In that case, we don't
+ care about the number of uses of our label. */
+
+ if (slots_filled != slots_to_fill
+ && (GET_CODE (insn) != JUMP_INSN
+ || ((condjump_p (insn) || condjump_in_parallel_p (insn))
+ && ! simplejump_p (insn)
+ && JUMP_LABEL (insn) != 0)))
+ {
+ rtx target = 0;
+ int maybe_never = 0;
+ int passed_label = 0;
+ int target_uses;
+ struct resources needed_at_jump;
+
+ CLEAR_RESOURCE (&needed);
+ CLEAR_RESOURCE (&set);
+
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ mark_set_resources (insn, &set, 0, 1);
+ mark_referenced_resources (insn, &needed, 1);
+ maybe_never = 1;
+ }
+ else
+ {
+ mark_set_resources (insn, &set, 0, 1);
+ mark_referenced_resources (insn, &needed, 1);
+ if (GET_CODE (insn) == JUMP_INSN)
+ {
+ /* Get our target and show how many more uses we want to
+ see before we hit the label. */
+ target = JUMP_LABEL (insn);
+ target_uses = LABEL_NUSES (target) - 1;
+ }
+
+ }
+
+ for (trial = next_nonnote_insn (insn); trial; trial = next_trial)
+ {
+ rtx pat, trial_delay;
+
+ next_trial = next_nonnote_insn (trial);
+
+ if (GET_CODE (trial) == CODE_LABEL)
+ {
+ passed_label = 1;
+
+ /* If this is our target, see if we have seen all its uses.
+ If so, indicate we have passed our target and ignore it.
+ All other labels cause us to stop our search. */
+ if (trial == target && target_uses == 0)
+ {
+ target = 0;
+ continue;
+ }
+ else
+ break;
+ }
+ else if (GET_CODE (trial) == BARRIER)
+ break;
+
+ /* We must have an INSN, JUMP_INSN, or CALL_INSN. */
+ pat = PATTERN (trial);
+
+ /* Stand-alone USE and CLOBBER are just for flow. */
+ if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER)
+ continue;
+
+ /* If this already has filled delay slots, get the insn needing
+ the delay slots. */
+ if (GET_CODE (pat) == SEQUENCE)
+ trial_delay = XVECEXP (pat, 0, 0);
+ else
+ trial_delay = trial;
+
+ /* If this is a jump insn to our target, indicate that we have
+ seen another jump to it. If we aren't handling a conditional
+ jump, stop our search. Otherwise, compute the needs at its
+ target and add them to NEEDED. */
+ if (GET_CODE (trial_delay) == JUMP_INSN)
+ {
+ if (target == 0)
+ break;
+ else if (JUMP_LABEL (trial_delay) == target)
+ target_uses--;
+ else
+ {
+ mark_target_live_regs
+ (next_active_insn (JUMP_LABEL (trial_delay)),
+ &needed_at_jump);
+ needed.memory |= needed_at_jump.memory;
+ IOR_HARD_REG_SET (needed.regs, needed_at_jump.regs);
+ }
+ }
+
+ /* See if we have a resource problem before we try to
+ split. */
+ if (target == 0
+ && GET_CODE (pat) != SEQUENCE
+ && ! insn_references_resource_p (trial, &set, 1)
+ && ! insn_sets_resource_p (trial, &set, 1)
+ && ! insn_sets_resource_p (trial, &needed, 1)
+#ifdef HAVE_cc0
+ && ! (reg_mentioned_p (cc0_rtx, pat) && ! sets_cc0_p (pat))
+#endif
+ && ! (maybe_never && may_trap_p (pat))
+ && (trial = try_split (pat, trial, 0))
+ && eligible_for_delay (insn, slots_filled, trial, flags))
+ {
+ next_trial = next_nonnote_insn (trial);
+ delay_list = add_to_delay_list (trial, delay_list);
+
+#ifdef HAVE_cc0
+ if (reg_mentioned_p (cc0_rtx, pat))
+ link_cc0_insns (trial);
+#endif
+
+ if (passed_label)
+ update_block (trial, trial);
+ delete_insn (trial);
+ if (slots_to_fill == ++slots_filled)
+ break;
+ continue;
+ }
+
+ mark_set_resources (trial, &set, 0, 1);
+ mark_referenced_resources (trial, &needed, 1);
+
+ /* Ensure we don't put insns between the setting of cc and the
+ comparison by moving a setting of cc into an earlier delay
+ slot since these insns could clobber the condition code. */
+ set.cc = 1;
+
+ /* If this is a call or jump, we might not get here. */
+ if (GET_CODE (trial) == CALL_INSN
+ || GET_CODE (trial) == JUMP_INSN)
+ maybe_never = 1;
+ }
+
+ /* If there are slots left to fill and our search was stopped by an
+ unconditional branch, try the insn at the branch target. We can
+ redirect the branch if it works. */
+ if (slots_to_fill != slots_filled
+ && trial
+ && GET_CODE (trial) == JUMP_INSN
+ && simplejump_p (trial)
+ && (target == 0 || JUMP_LABEL (trial) == target)
+ && (next_trial = next_active_insn (JUMP_LABEL (trial))) != 0
+ && ! (GET_CODE (next_trial) == INSN
+ && GET_CODE (PATTERN (next_trial)) == SEQUENCE)
+ && ! insn_references_resource_p (next_trial, &set, 1)
+ && ! insn_sets_resource_p (next_trial, &set, 1)
+ && ! insn_sets_resource_p (next_trial, &needed, 1)
+#ifdef HAVE_cc0
+ && ! reg_mentioned_p (cc0_rtx, PATTERN (next_trial))
+#endif
+ && ! (maybe_never && may_trap_p (PATTERN (next_trial)))
+ && (next_trial = try_split (PATTERN (next_trial), next_trial, 0))
+ && eligible_for_delay (insn, slots_filled, next_trial, flags))
+ {
+ rtx new_label = next_active_insn (next_trial);
+
+ if (new_label != 0)
+ new_label = get_label_before (new_label);
+ else
+ new_label = find_end_label ();
+
+ delay_list
+ = add_to_delay_list (copy_rtx (next_trial), delay_list);
+ slots_filled++;
+ reorg_redirect_jump (trial, new_label);
+
+ /* If we merged because we both jumped to the same place,
+ redirect the original insn also. */
+ if (target)
+ reorg_redirect_jump (insn, new_label);
+ }
+ }
+
+ if (delay_list)
+ unfilled_slots_base[i]
+ = emit_delay_sequence (insn, delay_list,
+ slots_filled, slots_to_fill);
+
+ if (slots_to_fill == slots_filled)
+ unfilled_slots_base[i] = 0;
+
+ note_delay_statistics (slots_filled, 0);
+ }
+
+#ifdef DELAY_SLOTS_FOR_EPILOGUE
+ /* See if the epilogue needs any delay slots. Try to fill them if so.
+ The only thing we can do is scan backwards from the end of the
+ function. If we did this in a previous pass, it is incorrect to do it
+ again. */
+ if (current_function_epilogue_delay_list)
+ return;
+
+ slots_to_fill = DELAY_SLOTS_FOR_EPILOGUE;
+ if (slots_to_fill == 0)
+ return;
+
+ slots_filled = 0;
+ needed = end_of_function_needs;
+ CLEAR_RESOURCE (&set);
+
+ for (trial = get_last_insn (); ! stop_search_p (trial, 1);
+ trial = PREV_INSN (trial))
+ {
+ if (GET_CODE (trial) == NOTE)
+ continue;
+ pat = PATTERN (trial);
+ if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER)
+ continue;
+
+ if (! insn_references_resource_p (trial, &set, 1)
+ && ! insn_sets_resource_p (trial, &needed, 1)
+#ifdef HAVE_cc0
+ /* Don't want to mess with cc0 here. */
+ && ! reg_mentioned_p (cc0_rtx, pat)
+#endif
+ )
+ {
+ trial = try_split (pat, trial, 1);
+ if (ELIGIBLE_FOR_EPILOGUE_DELAY (trial, slots_filled))
+ {
+ /* Here as well we are searching backward, so put the
+ insns we find on the head of the list. */
+
+ current_function_epilogue_delay_list
+ = gen_rtx (INSN_LIST, VOIDmode, trial,
+ current_function_epilogue_delay_list);
+ mark_referenced_resources (trial, &end_of_function_needs, 1);
+ update_block (trial, trial);
+ delete_insn (trial);
+
+ /* Clear deleted bit so final.c will output the insn. */
+ INSN_DELETED_P (trial) = 0;
+
+ if (slots_to_fill == ++slots_filled)
+ break;
+ continue;
+ }
+ }
+
+ mark_set_resources (trial, &set, 0, 1);
+ mark_referenced_resources (trial, &needed, 1);
+ }
+
+ note_delay_statistics (slots_filled, 0);
+#endif
+}
+
+/* Try to find insns to place in delay slots.
+
+ INSN is the jump needing SLOTS_TO_FILL delay slots. It tests CONDITION
+ or is an unconditional branch if CONDITION is const_true_rtx.
+ *PSLOTS_FILLED is updated with the number of slots that we have filled.
+
+ THREAD is a flow-of-control, either the insns to be executed if the
+ branch is true or if the branch is false, THREAD_IF_TRUE says which.
+
+ OPPOSITE_THREAD is the thread in the opposite direction. It is used
+ to see if any potential delay slot insns set things needed there.
+
+ LIKELY is non-zero if it is extremely likely that the branch will be
+ taken and THREAD_IF_TRUE is set. This is used for the branch at the
+ end of a loop back up to the top.
+
+ OWN_THREAD and OWN_OPPOSITE_THREAD are true if we are the only user of the
+ thread. I.e., it is the fallthrough code of our jump or the target of the
+ jump when we are the only jump going there.
+
+ If OWN_THREAD is false, it must be the "true" thread of a jump. In that
+ case, we can only take insns from the head of the thread for our delay
+ slot. We then adjust the jump to point after the insns we have taken. */
+
+static rtx
+fill_slots_from_thread (insn, condition, thread, opposite_thread, likely,
+ thread_if_true, own_thread, own_opposite_thread,
+ slots_to_fill, pslots_filled)
+ rtx insn;
+ rtx condition;
+ rtx thread, opposite_thread;
+ int likely;
+ int thread_if_true;
+ int own_thread, own_opposite_thread;
+ int slots_to_fill, *pslots_filled;
+{
+ rtx new_thread;
+ rtx delay_list = 0;
+ struct resources opposite_needed, set, needed;
+ rtx trial;
+ int lose = 0;
+ int must_annul = 0;
+ int flags;
+
+ /* Validate our arguments. */
+ if ((condition == const_true_rtx && ! thread_if_true)
+ || (! own_thread && ! thread_if_true))
+ abort ();
+
+ flags = get_jump_flags (insn, JUMP_LABEL (insn));
+
+ /* If our thread is the end of subroutine, we can't get any delay
+ insns from that. */
+ if (thread == 0)
+ return 0;
+
+ /* If this is an unconditional branch, nothing is needed at the
+ opposite thread. Otherwise, compute what is needed there. */
+ if (condition == const_true_rtx)
+ CLEAR_RESOURCE (&opposite_needed);
+ else
+ mark_target_live_regs (opposite_thread, &opposite_needed);
+
+ /* If the insn at THREAD can be split, do it here to avoid having to
+ update THREAD and NEW_THREAD if it is done in the loop below. Also
+ initialize NEW_THREAD. */
+
+ new_thread = thread = try_split (PATTERN (thread), thread, 0);
+
+ /* Scan insns at THREAD. We are looking for an insn that can be removed
+ from THREAD (it neither sets nor references resources that were set
+ ahead of it and it doesn't set anything needs by the insns ahead of
+ it) and that either can be placed in an annulling insn or aren't
+ needed at OPPOSITE_THREAD. */
+
+ CLEAR_RESOURCE (&needed);
+ CLEAR_RESOURCE (&set);
+
+ /* If we do not own this thread, we must stop as soon as we find
+ something that we can't put in a delay slot, since all we can do
+ is branch into THREAD at a later point. Therefore, labels stop
+ the search if this is not the `true' thread. */
+
+ for (trial = thread;
+ ! stop_search_p (trial, ! thread_if_true) && (! lose || own_thread);
+ trial = next_nonnote_insn (trial))
+ {
+ rtx pat, old_trial;
+
+ /* If we have passed a label, we no longer own this thread. */
+ if (GET_CODE (trial) == CODE_LABEL)
+ {
+ own_thread = 0;
+ continue;
+ }
+
+ pat = PATTERN (trial);
+ if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER)
+ continue;
+
+ /* If TRIAL conflicts with the insns ahead of it, we lose. Also,
+ don't separate or copy insns that set and use CC0. */
+ if (! insn_references_resource_p (trial, &set, 1)
+ && ! insn_sets_resource_p (trial, &set, 1)
+ && ! insn_sets_resource_p (trial, &needed, 1)
+#ifdef HAVE_cc0
+ && ! (reg_mentioned_p (cc0_rtx, pat)
+ && (! own_thread || ! sets_cc0_p (pat)))
+#endif
+ )
+ {
+ /* If TRIAL is redundant with some insn before INSN, we don't
+ actually need to add it to the delay list; we can merely pretend
+ we did. */
+ if (redundant_insn_p (trial, insn, delay_list))
+ {
+ if (own_thread)
+ {
+ update_block (trial, thread);
+ delete_insn (trial);
+ }
+ else
+ new_thread = next_active_insn (trial);
+
+ continue;
+ }
+
+ /* There are two ways we can win: If TRIAL doesn't set anything
+ needed at the opposite thread and can't trap, or if it can
+ go into an annulled delay slot. */
+ if (condition == const_true_rtx
+ || (! insn_sets_resource_p (trial, &opposite_needed, 1)
+ && ! may_trap_p (pat)))
+ {
+ old_trial = trial;
+ trial = try_split (pat, trial, 0);
+ if (new_thread == old_trial)
+ new_thread = trial;
+ pat = PATTERN (trial);
+ if (eligible_for_delay (insn, *pslots_filled, trial, flags))
+ goto winner;
+ }
+ else if (0
+#ifdef ANNUL_IFTRUE_SLOTS
+ || ! thread_if_true
+#endif
+#ifdef ANNUL_IFFALSE_SLOTS
+ || thread_if_true
+#endif
+ )
+ {
+ old_trial = trial;
+ trial = try_split (pat, trial, 0);
+ if (new_thread == old_trial)
+ new_thread = trial;
+ pat = PATTERN (trial);
+ if ((thread_if_true
+ ? eligible_for_annul_false (insn, *pslots_filled, trial, flags)
+ : eligible_for_annul_true (insn, *pslots_filled, trial, flags)))
+ {
+ rtx temp;
+
+ must_annul = 1;
+ winner:
+
+#ifdef HAVE_cc0
+ if (reg_mentioned_p (cc0_rtx, pat))
+ link_cc0_insns (trial);
+#endif
+
+ /* If we own this thread, delete the insn. If this is the
+ destination of a branch, show that a basic block status
+ may have been updated. In any case, mark the new
+ starting point of this thread. */
+ if (own_thread)
+ {
+ update_block (trial, thread);
+ delete_insn (trial);
+ }
+ else
+ new_thread = next_active_insn (trial);
+
+ temp = own_thread ? trial : copy_rtx (trial);
+ if (thread_if_true)
+ INSN_FROM_TARGET_P (temp) = 1;
+
+ delay_list = add_to_delay_list (temp, delay_list);
+
+ if (slots_to_fill == ++(*pslots_filled))
+ {
+ /* Even though we have filled all the slots, we
+ may be branching to a location that has a
+ redundant insn. Skip any if so. */
+ while (new_thread && ! own_thread
+ && ! insn_sets_resource_p (new_thread, &set, 1)
+ && ! insn_sets_resource_p (new_thread, &needed, 1)
+ && ! insn_references_resource_p (new_thread,
+ &set, 1)
+ && redundant_insn_p (new_thread, insn,
+ delay_list))
+ new_thread = next_active_insn (new_thread);
+ break;
+ }
+
+ continue;
+ }
+ }
+ }
+
+ /* This insn can't go into a delay slot. */
+ lose = 1;
+ mark_set_resources (trial, &set, 0, 1);
+ mark_referenced_resources (trial, &needed, 1);
+
+ /* Ensure we don't put insns between the setting of cc and the comparison
+ by moving a setting of cc into an earlier delay slot since these insns
+ could clobber the condition code. */
+ set.cc = 1;
+
+ /* If this insn is a register-register copy and the next insn has
+ a use of our destination, change it to use our source. That way,
+ it will become a candidate for our delay slot the next time
+ through this loop. This case occurs commonly in loops that
+ scan a list.
+
+ We could check for more complex cases than those tested below,
+ but it doesn't seem worth it. It might also be a good idea to try
+ to swap the two insns. That might do better.
+
+ We can't do this if the next insn modifies our destination, because
+ that would make the replacement into the insn invalid. We also can't
+ do this if it modifies our source, because it might be an earlyclobber
+ operand. This latter test also prevents updating the contents of
+ a PRE_INC. */
+
+ if (GET_CODE (trial) == INSN && GET_CODE (pat) == SET
+ && GET_CODE (SET_SRC (pat)) == REG
+ && GET_CODE (SET_DEST (pat)) == REG)
+ {
+ rtx next = next_nonnote_insn (trial);
+
+ if (next && GET_CODE (next) == INSN
+ && GET_CODE (PATTERN (next)) != USE
+ && ! reg_set_p (SET_DEST (pat), next)
+ && ! reg_set_p (SET_SRC (pat), next)
+ && reg_referenced_p (SET_DEST (pat), PATTERN (next)))
+ validate_replace_rtx (SET_DEST (pat), SET_SRC (pat), next);
+ }
+ }
+
+ /* If we stopped on a branch insn that has delay slots, see if we can
+ steal some of the insns in those slots. */
+ if (trial && GET_CODE (trial) == INSN
+ && GET_CODE (PATTERN (trial)) == SEQUENCE
+ && GET_CODE (XVECEXP (PATTERN (trial), 0, 0)) == JUMP_INSN)
+ {
+ /* If this is the `true' thread, we will want to follow the jump,
+ so we can only do this if we have taken everything up to here. */
+ if (thread_if_true && trial == new_thread)
+ delay_list
+ = steal_delay_list_from_target (insn, condition, PATTERN (trial),
+ delay_list, &set, &needed,
+ &opposite_needed, slots_to_fill,
+ pslots_filled, &must_annul,
+ &new_thread);
+ else if (! thread_if_true)
+ delay_list
+ = steal_delay_list_from_fallthrough (insn, condition,
+ PATTERN (trial),
+ delay_list, &set, &needed,
+ &opposite_needed, slots_to_fill,
+ pslots_filled, &must_annul);
+ }
+
+ /* If we haven't found anything for this delay slot and it is very
+ likely that the branch will be taken, see if the insn at our target
+ increments or decrements a register with an increment that does not
+ depend on the destination register. If so, try to place the opposite
+ arithmetic insn after the jump insn and put the arithmetic insn in the
+ delay slot. If we can't do this, return. */
+ if (delay_list == 0 && likely && new_thread && GET_CODE (new_thread) == INSN)
+ {
+ rtx pat = PATTERN (new_thread);
+ rtx dest;
+ rtx src;
+
+ trial = new_thread;
+ pat = PATTERN (trial);
+
+ if (GET_CODE (trial) != INSN || GET_CODE (pat) != SET
+ || ! eligible_for_delay (insn, 0, trial, flags))
+ return 0;
+
+ dest = SET_DEST (pat), src = SET_SRC (pat);
+ if ((GET_CODE (src) == PLUS || GET_CODE (src) == MINUS)
+ && rtx_equal_p (XEXP (src, 0), dest)
+ && ! reg_overlap_mentioned_p (dest, XEXP (src, 1)))
+ {
+ rtx other = XEXP (src, 1);
+ rtx new_arith;
+ rtx ninsn;
+
+ /* If this is a constant adjustment, use the same code with
+ the negated constant. Otherwise, reverse the sense of the
+ arithmetic. */
+ if (GET_CODE (other) == CONST_INT)
+ new_arith = gen_rtx (GET_CODE (src), GET_MODE (src), dest,
+ negate_rtx (GET_MODE (src), other));
+ else
+ new_arith = gen_rtx (GET_CODE (src) == PLUS ? MINUS : PLUS,
+ GET_MODE (src), dest, other);
+
+ ninsn = emit_insn_after (gen_rtx (SET, VOIDmode, dest, new_arith),
+ insn);
+
+ if (recog_memoized (ninsn) < 0
+ || (insn_extract (ninsn),
+ ! constrain_operands (INSN_CODE (ninsn), 1)))
+ {
+ delete_insn (ninsn);
+ return 0;
+ }
+
+ if (own_thread)
+ {
+ update_block (trial, thread);
+ delete_insn (trial);
+ }
+ else
+ new_thread = next_active_insn (trial);
+
+ ninsn = own_thread ? trial : copy_rtx (trial);
+ if (thread_if_true)
+ INSN_FROM_TARGET_P (ninsn) = 1;
+
+ delay_list = add_to_delay_list (ninsn, NULL_RTX);
+ (*pslots_filled)++;
+ }
+ }
+
+ if (delay_list && must_annul)
+ INSN_ANNULLED_BRANCH_P (insn) = 1;
+
+ /* If we are to branch into the middle of this thread, find an appropriate
+ label or make a new one if none, and redirect INSN to it. If we hit the
+ end of the function, use the end-of-function label. */
+ if (new_thread != thread)
+ {
+ rtx label;
+
+ if (! thread_if_true)
+ abort ();
+
+ if (new_thread && GET_CODE (new_thread) == JUMP_INSN
+ && (simplejump_p (new_thread)
+ || GET_CODE (PATTERN (new_thread)) == RETURN)
+ && redirect_with_delay_list_safe_p (insn,
+ JUMP_LABEL (new_thread),
+ delay_list))
+ new_thread = follow_jumps (JUMP_LABEL (new_thread));
+
+ if (new_thread == 0)
+ label = find_end_label ();
+ else if (GET_CODE (new_thread) == CODE_LABEL)
+ label = new_thread;
+ else
+ label = get_label_before (new_thread);
+
+ reorg_redirect_jump (insn, label);
+ }
+
+ return delay_list;
+}
+
+/* Make another attempt to find insns to place in delay slots.
+
+ We previously looked for insns located in front of the delay insn
+ and, for non-jump delay insns, located behind the delay insn.
+
+ Here only try to schedule jump insns and try to move insns from either
+ the target or the following insns into the delay slot. If annulling is
+ supported, we will be likely to do this. Otherwise, we can do this only
+ if safe. */
+
+static void
+fill_eager_delay_slots (first)
+ rtx first;
+{
+ register rtx insn;
+ register int i;
+ int num_unfilled_slots = unfilled_slots_next - unfilled_slots_base;
+
+ for (i = 0; i < num_unfilled_slots; i++)
+ {
+ rtx condition;
+ rtx target_label, insn_at_target, fallthrough_insn;
+ rtx delay_list = 0;
+ int own_target;
+ int own_fallthrough;
+ int prediction, slots_to_fill, slots_filled;
+
+ insn = unfilled_slots_base[i];
+ if (insn == 0
+ || INSN_DELETED_P (insn)
+ || GET_CODE (insn) != JUMP_INSN
+ || ! (condjump_p (insn) || condjump_in_parallel_p (insn)))
+ continue;
+
+ slots_to_fill = num_delay_slots (insn);
+ if (slots_to_fill == 0)
+ abort ();
+
+ slots_filled = 0;
+ target_label = JUMP_LABEL (insn);
+ condition = get_branch_condition (insn, target_label);
+
+ if (condition == 0)
+ continue;
+
+ /* Get the next active fallthough and target insns and see if we own
+ them. Then see whether the branch is likely true. We don't need
+ to do a lot of this for unconditional branches. */
+
+ insn_at_target = next_active_insn (target_label);
+ own_target = own_thread_p (target_label, target_label, 0);
+
+ if (condition == const_true_rtx)
+ {
+ own_fallthrough = 0;
+ fallthrough_insn = 0;
+ prediction = 2;
+ }
+ else
+ {
+ fallthrough_insn = next_active_insn (insn);
+ own_fallthrough = own_thread_p (NEXT_INSN (insn), NULL_RTX, 1);
+ prediction = mostly_true_jump (insn, condition);
+ }
+
+ /* If this insn is expected to branch, first try to get insns from our
+ target, then our fallthrough insns. If it is not, expected to branch,
+ try the other order. */
+
+ if (prediction > 0)
+ {
+ delay_list
+ = fill_slots_from_thread (insn, condition, insn_at_target,
+ fallthrough_insn, prediction == 2, 1,
+ own_target, own_fallthrough,
+ slots_to_fill, &slots_filled);
+
+ if (delay_list == 0 && own_fallthrough)
+ {
+ /* Even though we didn't find anything for delay slots,
+ we might have found a redundant insn which we deleted
+ from the thread that was filled. So we have to recompute
+ the next insn at the target. */
+ target_label = JUMP_LABEL (insn);
+ insn_at_target = next_active_insn (target_label);
+
+ delay_list
+ = fill_slots_from_thread (insn, condition, fallthrough_insn,
+ insn_at_target, 0, 0,
+ own_fallthrough, own_target,
+ slots_to_fill, &slots_filled);
+ }
+ }
+ else
+ {
+ if (own_fallthrough)
+ delay_list
+ = fill_slots_from_thread (insn, condition, fallthrough_insn,
+ insn_at_target, 0, 0,
+ own_fallthrough, own_target,
+ slots_to_fill, &slots_filled);
+
+ if (delay_list == 0)
+ delay_list
+ = fill_slots_from_thread (insn, condition, insn_at_target,
+ next_active_insn (insn), 0, 1,
+ own_target, own_fallthrough,
+ slots_to_fill, &slots_filled);
+ }
+
+ if (delay_list)
+ unfilled_slots_base[i]
+ = emit_delay_sequence (insn, delay_list,
+ slots_filled, slots_to_fill);
+
+ if (slots_to_fill == slots_filled)
+ unfilled_slots_base[i] = 0;
+
+ note_delay_statistics (slots_filled, 1);
+ }
+}
+
+/* Once we have tried two ways to fill a delay slot, make a pass over the
+ code to try to improve the results and to do such things as more jump
+ threading. */
+
+static void
+relax_delay_slots (first)
+ rtx first;
+{
+ register rtx insn, next, pat;
+ register rtx trial, delay_insn, target_label;
+
+ /* Look at every JUMP_INSN and see if we can improve it. */
+ for (insn = first; insn; insn = next)
+ {
+ rtx other;
+
+ next = next_active_insn (insn);
+
+ /* If this is a jump insn, see if it now jumps to a jump, jumps to
+ the next insn, or jumps to a label that is not the last of a
+ group of consecutive labels. */
+ if (GET_CODE (insn) == JUMP_INSN
+ && (condjump_p (insn) || condjump_in_parallel_p (insn))
+ && (target_label = JUMP_LABEL (insn)) != 0)
+ {
+ target_label = follow_jumps (target_label);
+ target_label = prev_label (next_active_insn (target_label));
+
+ if (target_label == 0)
+ target_label = find_end_label ();
+
+ if (next_active_insn (target_label) == next
+ && ! condjump_in_parallel_p (insn))
+ {
+ delete_jump (insn);
+ continue;
+ }
+
+ if (target_label != JUMP_LABEL (insn))
+ reorg_redirect_jump (insn, target_label);
+
+ /* See if this jump branches around a unconditional jump.
+ If so, invert this jump and point it to the target of the
+ second jump. */
+ if (next && GET_CODE (next) == JUMP_INSN
+ && (simplejump_p (next) || GET_CODE (PATTERN (next)) == RETURN)
+ && next_active_insn (target_label) == next_active_insn (next)
+ && no_labels_between_p (insn, next))
+ {
+ rtx label = JUMP_LABEL (next);
+
+ /* Be careful how we do this to avoid deleting code or
+ labels that are momentarily dead. See similar optimization
+ in jump.c.
+
+ We also need to ensure we properly handle the case when
+ invert_jump fails. */
+
+ ++LABEL_NUSES (target_label);
+ if (label)
+ ++LABEL_NUSES (label);
+
+ if (invert_jump (insn, label))
+ {
+ delete_insn (next);
+ next = insn;
+ }
+
+ if (label)
+ --LABEL_NUSES (label);
+
+ if (--LABEL_NUSES (target_label) == 0)
+ delete_insn (target_label);
+
+ continue;
+ }
+ }
+
+ /* If this is an unconditional jump and the previous insn is a
+ conditional jump, try reversing the condition of the previous
+ insn and swapping our targets. The next pass might be able to
+ fill the slots.
+
+ Don't do this if we expect the conditional branch to be true, because
+ we would then be making the more common case longer. */
+
+ if (GET_CODE (insn) == JUMP_INSN
+ && (simplejump_p (insn) || GET_CODE (PATTERN (insn)) == RETURN)
+ && (other = prev_active_insn (insn)) != 0
+ && (condjump_p (other) || condjump_in_parallel_p (other))
+ && no_labels_between_p (other, insn)
+ && 0 < mostly_true_jump (other,
+ get_branch_condition (other,
+ JUMP_LABEL (other))))
+ {
+ rtx other_target = JUMP_LABEL (other);
+ target_label = JUMP_LABEL (insn);
+
+ /* Increment the count of OTHER_TARGET, so it doesn't get deleted
+ as we move the label. */
+ if (other_target)
+ ++LABEL_NUSES (other_target);
+
+ if (invert_jump (other, target_label))
+ reorg_redirect_jump (insn, other_target);
+
+ if (other_target)
+ --LABEL_NUSES (other_target);
+ }
+
+ /* Now look only at cases where we have filled a delay slot. */
+ if (GET_CODE (insn) != INSN
+ || GET_CODE (PATTERN (insn)) != SEQUENCE)
+ continue;
+
+ pat = PATTERN (insn);
+ delay_insn = XVECEXP (pat, 0, 0);
+
+ /* See if the first insn in the delay slot is redundant with some
+ previous insn. Remove it from the delay slot if so; then set up
+ to reprocess this insn. */
+ if (redundant_insn_p (XVECEXP (pat, 0, 1), delay_insn, 0))
+ {
+ delete_from_delay_slot (XVECEXP (pat, 0, 1));
+ next = prev_active_insn (next);
+ continue;
+ }
+
+ /* Now look only at the cases where we have a filled JUMP_INSN. */
+ if (GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) != JUMP_INSN
+ || ! (condjump_p (XVECEXP (PATTERN (insn), 0, 0))
+ || condjump_in_parallel_p (XVECEXP (PATTERN (insn), 0, 0))))
+ continue;
+
+ target_label = JUMP_LABEL (delay_insn);
+
+ if (target_label)
+ {
+ /* If this jump goes to another unconditional jump, thread it, but
+ don't convert a jump into a RETURN here. */
+ trial = follow_jumps (target_label);
+ trial = prev_label (next_active_insn (trial));
+ if (trial == 0 && target_label != 0)
+ trial = find_end_label ();
+
+ if (trial != target_label
+ && redirect_with_delay_slots_safe_p (delay_insn, trial, insn))
+ {
+ reorg_redirect_jump (delay_insn, trial);
+ target_label = trial;
+ }
+
+ /* If the first insn at TARGET_LABEL is redundant with a previous
+ insn, redirect the jump to the following insn process again. */
+ trial = next_active_insn (target_label);
+ if (trial && GET_CODE (PATTERN (trial)) != SEQUENCE
+ && redundant_insn_p (trial, insn, 0))
+ {
+ trial = next_active_insn (trial);
+ if (trial == 0)
+ target_label = find_end_label ();
+ else
+ target_label = get_label_before (trial);
+ reorg_redirect_jump (delay_insn, target_label);
+ next = insn;
+ continue;
+ }
+
+ /* Similarly, if it is an unconditional jump with one insn in its
+ delay list and that insn is redundant, thread the jump. */
+ if (trial && GET_CODE (PATTERN (trial)) == SEQUENCE
+ && XVECLEN (PATTERN (trial), 0) == 2
+ && GET_CODE (XVECEXP (PATTERN (trial), 0, 0)) == JUMP_INSN
+ && (simplejump_p (XVECEXP (PATTERN (trial), 0, 0))
+ || GET_CODE (PATTERN (XVECEXP (PATTERN (trial), 0, 0))) == RETURN)
+ && redundant_insn_p (XVECEXP (PATTERN (trial), 0, 1), insn, 0))
+ {
+ target_label = JUMP_LABEL (XVECEXP (PATTERN (trial), 0, 0));
+ if (target_label == 0)
+ target_label = find_end_label ();
+
+ if (redirect_with_delay_slots_safe_p (delay_insn, target_label,
+ insn))
+ {
+ reorg_redirect_jump (delay_insn, target_label);
+ next = insn;
+ continue;
+ }
+ }
+ }
+
+ if (! INSN_ANNULLED_BRANCH_P (delay_insn)
+ && prev_active_insn (target_label) == insn
+ && ! condjump_in_parallel_p (delay_insn)
+#ifdef HAVE_cc0
+ /* If the last insn in the delay slot sets CC0 for some insn,
+ various code assumes that it is in a delay slot. We could
+ put it back where it belonged and delete the register notes,
+ but it doesn't seem worthwhile in this uncommon case. */
+ && ! find_reg_note (XVECEXP (pat, 0, XVECLEN (pat, 0) - 1),
+ REG_CC_USER, NULL_RTX)
+#endif
+ )
+ {
+ int i;
+
+ /* All this insn does is execute its delay list and jump to the
+ following insn. So delete the jump and just execute the delay
+ list insns.
+
+ We do this by deleting the INSN containing the SEQUENCE, then
+ re-emitting the insns separately, and then deleting the jump.
+ This allows the count of the jump target to be properly
+ decremented. */
+
+ /* Clear the from target bit, since these insns are no longer
+ in delay slots. */
+ for (i = 0; i < XVECLEN (pat, 0); i++)
+ INSN_FROM_TARGET_P (XVECEXP (pat, 0, i)) = 0;
+
+ trial = PREV_INSN (insn);
+ delete_insn (insn);
+ emit_insn_after (pat, trial);
+ delete_scheduled_jump (delay_insn);
+ continue;
+ }
+
+ /* See if this is an unconditional jump around a single insn which is
+ identical to the one in its delay slot. In this case, we can just
+ delete the branch and the insn in its delay slot. */
+ if (next && GET_CODE (next) == INSN
+ && prev_label (next_active_insn (next)) == target_label
+ && simplejump_p (insn)
+ && XVECLEN (pat, 0) == 2
+ && rtx_equal_p (PATTERN (next), PATTERN (XVECEXP (pat, 0, 1))))
+ {
+ delete_insn (insn);
+ continue;
+ }
+
+ /* See if this jump (with its delay slots) branches around another
+ jump (without delay slots). If so, invert this jump and point
+ it to the target of the second jump. We cannot do this for
+ annulled jumps, though. Again, don't convert a jump to a RETURN
+ here. */
+ if (! INSN_ANNULLED_BRANCH_P (delay_insn)
+ && next && GET_CODE (next) == JUMP_INSN
+ && (simplejump_p (next) || GET_CODE (PATTERN (next)) == RETURN)
+ && next_active_insn (target_label) == next_active_insn (next)
+ && no_labels_between_p (insn, next))
+ {
+ rtx label = JUMP_LABEL (next);
+ rtx old_label = JUMP_LABEL (delay_insn);
+
+ if (label == 0)
+ label = find_end_label ();
+
+ if (redirect_with_delay_slots_safe_p (delay_insn, label, insn))
+ {
+ /* Be careful how we do this to avoid deleting code or labels
+ that are momentarily dead. See similar optimization in
+ jump.c */
+ if (old_label)
+ ++LABEL_NUSES (old_label);
+
+ if (invert_jump (delay_insn, label))
+ {
+ delete_insn (next);
+ next = insn;
+ }
+
+ if (old_label && --LABEL_NUSES (old_label) == 0)
+ delete_insn (old_label);
+ continue;
+ }
+ }
+
+ /* If we own the thread opposite the way this insn branches, see if we
+ can merge its delay slots with following insns. */
+ if (INSN_FROM_TARGET_P (XVECEXP (pat, 0, 1))
+ && own_thread_p (NEXT_INSN (insn), 0, 1))
+ try_merge_delay_insns (insn, next);
+ else if (! INSN_FROM_TARGET_P (XVECEXP (pat, 0, 1))
+ && own_thread_p (target_label, target_label, 0))
+ try_merge_delay_insns (insn, next_active_insn (target_label));
+
+ /* If we get here, we haven't deleted INSN. But we may have deleted
+ NEXT, so recompute it. */
+ next = next_active_insn (insn);
+ }
+}
+
+#ifdef HAVE_return
+
+/* Look for filled jumps to the end of function label. We can try to convert
+ them into RETURN insns if the insns in the delay slot are valid for the
+ RETURN as well. */
+
+static void
+make_return_insns (first)
+ rtx first;
+{
+ rtx insn, jump_insn, pat;
+ rtx real_return_label = end_of_function_label;
+ int slots, i;
+
+ /* See if there is a RETURN insn in the function other than the one we
+ made for END_OF_FUNCTION_LABEL. If so, set up anything we can't change
+ into a RETURN to jump to it. */
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == JUMP_INSN && GET_CODE (PATTERN (insn)) == RETURN)
+ {
+ real_return_label = get_label_before (insn);
+ break;
+ }
+
+ /* Show an extra usage of REAL_RETURN_LABEL so it won't go away if it
+ was equal to END_OF_FUNCTION_LABEL. */
+ LABEL_NUSES (real_return_label)++;
+
+ /* Clear the list of insns to fill so we can use it. */
+ obstack_free (&unfilled_slots_obstack, unfilled_firstobj);
+
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ int flags;
+
+ /* Only look at filled JUMP_INSNs that go to the end of function
+ label. */
+ if (GET_CODE (insn) != INSN
+ || GET_CODE (PATTERN (insn)) != SEQUENCE
+ || GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) != JUMP_INSN
+ || JUMP_LABEL (XVECEXP (PATTERN (insn), 0, 0)) != end_of_function_label)
+ continue;
+
+ pat = PATTERN (insn);
+ jump_insn = XVECEXP (pat, 0, 0);
+
+ /* If we can't make the jump into a RETURN, try to redirect it to the best
+ RETURN and go on to the next insn. */
+ if (! reorg_redirect_jump (jump_insn, NULL_RTX))
+ {
+ /* Make sure redirecting the jump will not invalidate the delay
+ slot insns. */
+ if (redirect_with_delay_slots_safe_p (jump_insn,
+ real_return_label,
+ insn))
+ reorg_redirect_jump (jump_insn, real_return_label);
+ continue;
+ }
+
+ /* See if this RETURN can accept the insns current in its delay slot.
+ It can if it has more or an equal number of slots and the contents
+ of each is valid. */
+
+ flags = get_jump_flags (jump_insn, JUMP_LABEL (jump_insn));
+ slots = num_delay_slots (jump_insn);
+ if (slots >= XVECLEN (pat, 0) - 1)
+ {
+ for (i = 1; i < XVECLEN (pat, 0); i++)
+ if (! (
+#ifdef ANNUL_IFFALSE_SLOTS
+ (INSN_ANNULLED_BRANCH_P (jump_insn)
+ && INSN_FROM_TARGET_P (XVECEXP (pat, 0, i)))
+ ? eligible_for_annul_false (jump_insn, i - 1,
+ XVECEXP (pat, 0, i), flags) :
+#endif
+#ifdef ANNUL_IFTRUE_SLOTS
+ (INSN_ANNULLED_BRANCH_P (jump_insn)
+ && ! INSN_FROM_TARGET_P (XVECEXP (pat, 0, i)))
+ ? eligible_for_annul_true (jump_insn, i - 1,
+ XVECEXP (pat, 0, i), flags) :
+#endif
+ eligible_for_delay (jump_insn, i -1, XVECEXP (pat, 0, i), flags)))
+ break;
+ }
+ else
+ i = 0;
+
+ if (i == XVECLEN (pat, 0))
+ continue;
+
+ /* We have to do something with this insn. If it is an unconditional
+ RETURN, delete the SEQUENCE and output the individual insns,
+ followed by the RETURN. Then set things up so we try to find
+ insns for its delay slots, if it needs some. */
+ if (GET_CODE (PATTERN (jump_insn)) == RETURN)
+ {
+ rtx prev = PREV_INSN (insn);
+
+ delete_insn (insn);
+ for (i = 1; i < XVECLEN (pat, 0); i++)
+ prev = emit_insn_after (PATTERN (XVECEXP (pat, 0, i)), prev);
+
+ insn = emit_jump_insn_after (PATTERN (jump_insn), prev);
+ emit_barrier_after (insn);
+
+ if (slots)
+ obstack_ptr_grow (&unfilled_slots_obstack, insn);
+ }
+ else
+ /* It is probably more efficient to keep this with its current
+ delay slot as a branch to a RETURN. */
+ reorg_redirect_jump (jump_insn, real_return_label);
+ }
+
+ /* Now delete REAL_RETURN_LABEL if we never used it. Then try to fill any
+ new delay slots we have created. */
+ if (--LABEL_NUSES (real_return_label) == 0)
+ delete_insn (real_return_label);
+
+ fill_simple_delay_slots (first, 1);
+ fill_simple_delay_slots (first, 0);
+}
+#endif
+
+/* Try to find insns to place in delay slots. */
+
+void
+dbr_schedule (first, file)
+ rtx first;
+ FILE *file;
+{
+ rtx insn, next, epilogue_insn = 0;
+ int i;
+#if 0
+ int old_flag_no_peephole = flag_no_peephole;
+
+ /* Execute `final' once in prescan mode to delete any insns that won't be
+ used. Don't let final try to do any peephole optimization--it will
+ ruin dataflow information for this pass. */
+
+ flag_no_peephole = 1;
+ final (first, 0, NO_DEBUG, 1, 1);
+ flag_no_peephole = old_flag_no_peephole;
+#endif
+
+ /* If the current function has no insns other than the prologue and
+ epilogue, then do not try to fill any delay slots. */
+ if (n_basic_blocks == 0)
+ return;
+
+ /* Find the highest INSN_UID and allocate and initialize our map from
+ INSN_UID's to position in code. */
+ for (max_uid = 0, insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ if (INSN_UID (insn) > max_uid)
+ max_uid = INSN_UID (insn);
+ if (GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EPILOGUE_BEG)
+ epilogue_insn = insn;
+ }
+
+ uid_to_ruid = (int *) alloca ((max_uid + 1) * sizeof (int *));
+ for (i = 0, insn = first; insn; i++, insn = NEXT_INSN (insn))
+ uid_to_ruid[INSN_UID (insn)] = i;
+
+ /* Initialize the list of insns that need filling. */
+ if (unfilled_firstobj == 0)
+ {
+ gcc_obstack_init (&unfilled_slots_obstack);
+ unfilled_firstobj = (rtx *) obstack_alloc (&unfilled_slots_obstack, 0);
+ }
+
+ for (insn = next_active_insn (first); insn; insn = next_active_insn (insn))
+ {
+ rtx target;
+
+ INSN_ANNULLED_BRANCH_P (insn) = 0;
+ INSN_FROM_TARGET_P (insn) = 0;
+
+ /* Skip vector tables. We can't get attributes for them. */
+ if (GET_CODE (insn) == JUMP_INSN
+ && (GET_CODE (PATTERN (insn)) == ADDR_VEC
+ || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC))
+ continue;
+
+ if (num_delay_slots (insn) > 0)
+ obstack_ptr_grow (&unfilled_slots_obstack, insn);
+
+ /* Ensure all jumps go to the last of a set of consecutive labels. */
+ if (GET_CODE (insn) == JUMP_INSN
+ && (condjump_p (insn) || condjump_in_parallel_p (insn))
+ && JUMP_LABEL (insn) != 0
+ && ((target = prev_label (next_active_insn (JUMP_LABEL (insn))))
+ != JUMP_LABEL (insn)))
+ redirect_jump (insn, target);
+ }
+
+ /* Indicate what resources are required to be valid at the end of the current
+ function. The condition code never is and memory always is. If the
+ frame pointer is needed, it is and so is the stack pointer unless
+ EXIT_IGNORE_STACK is non-zero. If the frame pointer is not needed, the
+ stack pointer is. Registers used to return the function value are
+ needed. Registers holding global variables are needed. */
+
+ end_of_function_needs.cc = 0;
+ end_of_function_needs.memory = 1;
+ CLEAR_HARD_REG_SET (end_of_function_needs.regs);
+
+ if (frame_pointer_needed)
+ {
+ SET_HARD_REG_BIT (end_of_function_needs.regs, FRAME_POINTER_REGNUM);
+#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ SET_HARD_REG_BIT (end_of_function_needs.regs, HARD_FRAME_POINTER_REGNUM);
+#endif
+#ifdef EXIT_IGNORE_STACK
+ if (! EXIT_IGNORE_STACK)
+#endif
+ SET_HARD_REG_BIT (end_of_function_needs.regs, STACK_POINTER_REGNUM);
+ }
+ else
+ SET_HARD_REG_BIT (end_of_function_needs.regs, STACK_POINTER_REGNUM);
+
+ if (current_function_return_rtx != 0
+ && GET_CODE (current_function_return_rtx) == REG)
+ mark_referenced_resources (current_function_return_rtx,
+ &end_of_function_needs, 1);
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (global_regs[i])
+ SET_HARD_REG_BIT (end_of_function_needs.regs, i);
+
+ /* The registers required to be live at the end of the function are
+ represented in the flow information as being dead just prior to
+ reaching the end of the function. For example, the return of a value
+ might be represented by a USE of the return register immediately
+ followed by an unconditional jump to the return label where the
+ return label is the end of the RTL chain. The end of the RTL chain
+ is then taken to mean that the return register is live.
+
+ This sequence is no longer maintained when epilogue instructions are
+ added to the RTL chain. To reconstruct the original meaning, the
+ start of the epilogue (NOTE_INSN_EPILOGUE_BEG) is regarded as the
+ point where these registers become live (start_of_epilogue_needs).
+ If epilogue instructions are present, the registers set by those
+ instructions won't have been processed by flow. Thus, those
+ registers are additionally required at the end of the RTL chain
+ (end_of_function_needs). */
+
+ start_of_epilogue_needs = end_of_function_needs;
+
+ while (epilogue_insn = next_nonnote_insn (epilogue_insn))
+ mark_set_resources (epilogue_insn, &end_of_function_needs, 0, 1);
+
+ /* Show we haven't computed an end-of-function label yet. */
+ end_of_function_label = 0;
+
+ /* Allocate and initialize the tables used by mark_target_live_regs. */
+ target_hash_table
+ = (struct target_info **) alloca ((TARGET_HASH_PRIME
+ * sizeof (struct target_info *)));
+ bzero (target_hash_table, TARGET_HASH_PRIME * sizeof (struct target_info *));
+
+ bb_ticks = (int *) alloca (n_basic_blocks * sizeof (int));
+ bzero (bb_ticks, n_basic_blocks * sizeof (int));
+
+ /* Initialize the statistics for this function. */
+ bzero (num_insns_needing_delays, sizeof num_insns_needing_delays);
+ bzero (num_filled_delays, sizeof num_filled_delays);
+
+ /* Now do the delay slot filling. Try everything twice in case earlier
+ changes make more slots fillable. */
+
+ for (reorg_pass_number = 0;
+ reorg_pass_number < MAX_REORG_PASSES;
+ reorg_pass_number++)
+ {
+ fill_simple_delay_slots (first, 1);
+ fill_simple_delay_slots (first, 0);
+ fill_eager_delay_slots (first);
+ relax_delay_slots (first);
+ }
+
+ /* Delete any USE insns made by update_block; subsequent passes don't need
+ them or know how to deal with them. */
+ for (insn = first; insn; insn = next)
+ {
+ next = NEXT_INSN (insn);
+
+ if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == USE
+ && GET_RTX_CLASS (GET_CODE (XEXP (PATTERN (insn), 0))) == 'i')
+ next = delete_insn (insn);
+ }
+
+ /* If we made an end of function label, indicate that it is now
+ safe to delete it by undoing our prior adjustment to LABEL_NUSES.
+ If it is now unused, delete it. */
+ if (end_of_function_label && --LABEL_NUSES (end_of_function_label) == 0)
+ delete_insn (end_of_function_label);
+
+#ifdef HAVE_return
+ if (HAVE_return && end_of_function_label != 0)
+ make_return_insns (first);
+#endif
+
+ obstack_free (&unfilled_slots_obstack, unfilled_firstobj);
+
+ /* It is not clear why the line below is needed, but it does seem to be. */
+ unfilled_firstobj = (rtx *) obstack_alloc (&unfilled_slots_obstack, 0);
+
+ /* Reposition the prologue and epilogue notes in case we moved the
+ prologue/epilogue insns. */
+ reposition_prologue_and_epilogue_notes (first);
+
+ if (file)
+ {
+ register int i, j, need_comma;
+
+ for (reorg_pass_number = 0;
+ reorg_pass_number < MAX_REORG_PASSES;
+ reorg_pass_number++)
+ {
+ fprintf (file, ";; Reorg pass #%d:\n", reorg_pass_number + 1);
+ for (i = 0; i < NUM_REORG_FUNCTIONS; i++)
+ {
+ need_comma = 0;
+ fprintf (file, ";; Reorg function #%d\n", i);
+
+ fprintf (file, ";; %d insns needing delay slots\n;; ",
+ num_insns_needing_delays[i][reorg_pass_number]);
+
+ for (j = 0; j < MAX_DELAY_HISTOGRAM; j++)
+ if (num_filled_delays[i][j][reorg_pass_number])
+ {
+ if (need_comma)
+ fprintf (file, ", ");
+ need_comma = 1;
+ fprintf (file, "%d got %d delays",
+ num_filled_delays[i][j][reorg_pass_number], j);
+ }
+ fprintf (file, "\n");
+ }
+ }
+ }
+}
+#endif /* DELAY_SLOTS */
diff --git a/gnu/usr.bin/cc/cc_int/rtl.c b/gnu/usr.bin/cc/cc_int/rtl.c
new file mode 100644
index 0000000..6f29f7f
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/rtl.c
@@ -0,0 +1,850 @@
+/* Allocate and read RTL for GNU C Compiler.
+ Copyright (C) 1987, 1988, 1991, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#include <ctype.h>
+#include <stdio.h>
+#include "rtl.h"
+#include "real.h"
+
+#include "obstack.h"
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+/* Obstack used for allocating RTL objects.
+ Between functions, this is the permanent_obstack.
+ While parsing and expanding a function, this is maybepermanent_obstack
+ so we can save it if it is an inline function.
+ During optimization and output, this is function_obstack. */
+
+extern struct obstack *rtl_obstack;
+
+#if HOST_BITS_PER_WIDE_INT != HOST_BITS_PER_INT
+extern long atol();
+#endif
+
+/* Indexed by rtx code, gives number of operands for an rtx with that code.
+ Does NOT include rtx header data (code and links).
+ This array is initialized in init_rtl. */
+
+int rtx_length[NUM_RTX_CODE + 1];
+
+/* Indexed by rtx code, gives the name of that kind of rtx, as a C string. */
+
+#define DEF_RTL_EXPR(ENUM, NAME, FORMAT, CLASS) NAME ,
+
+char *rtx_name[] = {
+#include "rtl.def" /* rtl expressions are documented here */
+};
+
+#undef DEF_RTL_EXPR
+
+/* Indexed by machine mode, gives the name of that machine mode.
+ This name does not include the letters "mode". */
+
+#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) NAME,
+
+char *mode_name[(int) MAX_MACHINE_MODE] = {
+#include "machmode.def"
+
+#ifdef EXTRA_CC_MODES
+ EXTRA_CC_NAMES
+#endif
+
+};
+
+#undef DEF_MACHMODE
+
+/* Indexed by machine mode, gives the length of the mode, in bytes.
+ GET_MODE_CLASS uses this. */
+
+#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) CLASS,
+
+enum mode_class mode_class[(int) MAX_MACHINE_MODE] = {
+#include "machmode.def"
+};
+
+#undef DEF_MACHMODE
+
+/* Indexed by machine mode, gives the length of the mode, in bytes.
+ GET_MODE_SIZE uses this. */
+
+#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) SIZE,
+
+int mode_size[(int) MAX_MACHINE_MODE] = {
+#include "machmode.def"
+};
+
+#undef DEF_MACHMODE
+
+/* Indexed by machine mode, gives the length of the mode's subunit.
+ GET_MODE_UNIT_SIZE uses this. */
+
+#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) UNIT,
+
+int mode_unit_size[(int) MAX_MACHINE_MODE] = {
+#include "machmode.def" /* machine modes are documented here */
+};
+
+#undef DEF_MACHMODE
+
+/* Indexed by machine mode, gives next wider natural mode
+ (QI -> HI -> SI -> DI, etc.) Widening multiply instructions
+ use this. */
+
+#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER) \
+ (enum machine_mode) WIDER,
+
+enum machine_mode mode_wider_mode[(int) MAX_MACHINE_MODE] = {
+#include "machmode.def" /* machine modes are documented here */
+};
+
+#undef DEF_MACHMODE
+
+/* Indexed by mode class, gives the narrowest mode for each class. */
+
+enum machine_mode class_narrowest_mode[(int) MAX_MODE_CLASS];
+
+/* Indexed by rtx code, gives a sequence of operand-types for
+ rtx's of that code. The sequence is a C string in which
+ each character describes one operand. */
+
+char *rtx_format[] = {
+ /* "*" undefined.
+ can cause a warning message
+ "0" field is unused (or used in a phase-dependent manner)
+ prints nothing
+ "i" an integer
+ prints the integer
+ "n" like "i", but prints entries from `note_insn_name'
+ "w" an integer of width HOST_BITS_PER_WIDE_INT
+ prints the integer
+ "s" a pointer to a string
+ prints the string
+ "S" like "s", but optional:
+ the containing rtx may end before this operand
+ "e" a pointer to an rtl expression
+ prints the expression
+ "E" a pointer to a vector that points to a number of rtl expressions
+ prints a list of the rtl expressions
+ "V" like "E", but optional:
+ the containing rtx may end before this operand
+ "u" a pointer to another insn
+ prints the uid of the insn. */
+
+#define DEF_RTL_EXPR(ENUM, NAME, FORMAT, CLASS) FORMAT ,
+#include "rtl.def" /* rtl expressions are defined here */
+#undef DEF_RTL_EXPR
+};
+
+/* Indexed by rtx code, gives a character representing the "class" of
+ that rtx code. See rtl.def for documentation on the defined classes. */
+
+char rtx_class[] = {
+#define DEF_RTL_EXPR(ENUM, NAME, FORMAT, CLASS) CLASS,
+#include "rtl.def" /* rtl expressions are defined here */
+#undef DEF_RTL_EXPR
+};
+
+/* Names for kinds of NOTEs and REG_NOTEs. */
+
+char *note_insn_name[] = { 0 , "NOTE_INSN_DELETED",
+ "NOTE_INSN_BLOCK_BEG", "NOTE_INSN_BLOCK_END",
+ "NOTE_INSN_LOOP_BEG", "NOTE_INSN_LOOP_END",
+ "NOTE_INSN_FUNCTION_END", "NOTE_INSN_SETJMP",
+ "NOTE_INSN_LOOP_CONT", "NOTE_INSN_LOOP_VTOP",
+ "NOTE_INSN_PROLOGUE_END", "NOTE_INSN_EPILOGUE_BEG",
+ "NOTE_INSN_DELETED_LABEL", "NOTE_INSN_FUNCTION_BEG"};
+
+char *reg_note_name[] = { "", "REG_DEAD", "REG_INC", "REG_EQUIV", "REG_WAS_0",
+ "REG_EQUAL", "REG_RETVAL", "REG_LIBCALL",
+ "REG_NONNEG", "REG_NO_CONFLICT", "REG_UNUSED",
+ "REG_CC_SETTER", "REG_CC_USER", "REG_LABEL",
+ "REG_DEP_ANTI", "REG_DEP_OUTPUT" };
+
+/* Allocate an rtx vector of N elements.
+ Store the length, and initialize all elements to zero. */
+
+rtvec
+rtvec_alloc (n)
+ int n;
+{
+ rtvec rt;
+ int i;
+
+ rt = (rtvec) obstack_alloc (rtl_obstack,
+ sizeof (struct rtvec_def)
+ + (( n - 1) * sizeof (rtunion)));
+
+ /* clear out the vector */
+ PUT_NUM_ELEM(rt, n);
+ for (i=0; i < n; i++)
+ rt->elem[i].rtvec = NULL; /* @@ not portable due to rtunion */
+
+ return rt;
+}
+
+/* Allocate an rtx of code CODE. The CODE is stored in the rtx;
+ all the rest is initialized to zero. */
+
+rtx
+rtx_alloc (code)
+ RTX_CODE code;
+{
+ rtx rt;
+ register struct obstack *ob = rtl_obstack;
+ register int nelts = GET_RTX_LENGTH (code);
+ register int length = sizeof (struct rtx_def)
+ + (nelts - 1) * sizeof (rtunion);
+
+ /* This function is called more than any other in GCC,
+ so we manipulate the obstack directly.
+
+ Even though rtx objects are word aligned, we may be sharing an obstack
+ with tree nodes, which may have to be double-word aligned. So align
+ our length to the alignment mask in the obstack. */
+
+ length = (length + ob->alignment_mask) & ~ ob->alignment_mask;
+
+ if (ob->chunk_limit - ob->next_free < length)
+ _obstack_newchunk (ob, length);
+ rt = (rtx)ob->object_base;
+ ob->next_free += length;
+ ob->object_base = ob->next_free;
+
+ /* We want to clear everything up to the FLD array. Normally, this is
+ one int, but we don't want to assume that and it isn't very portable
+ anyway; this is. */
+
+ length = (sizeof (struct rtx_def) - sizeof (rtunion) - 1) / sizeof (int);
+ for (; length >= 0; length--)
+ ((int *) rt)[length] = 0;
+
+ PUT_CODE (rt, code);
+
+ return rt;
+}
+
+/* Free the rtx X and all RTL allocated since X. */
+
+void
+rtx_free (x)
+ rtx x;
+{
+ obstack_free (rtl_obstack, x);
+}
+
+/* Create a new copy of an rtx.
+ Recursively copies the operands of the rtx,
+ except for those few rtx codes that are sharable. */
+
+rtx
+copy_rtx (orig)
+ register rtx orig;
+{
+ register rtx copy;
+ register int i, j;
+ register RTX_CODE code;
+ register char *format_ptr;
+
+ code = GET_CODE (orig);
+
+ switch (code)
+ {
+ case REG:
+ case QUEUED:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case CODE_LABEL:
+ case PC:
+ case CC0:
+ case SCRATCH:
+ /* SCRATCH must be shared because they represent distinct values. */
+ return orig;
+
+ case CONST:
+ /* CONST can be shared if it contains a SYMBOL_REF. If it contains
+ a LABEL_REF, it isn't sharable. */
+ if (GET_CODE (XEXP (orig, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (orig, 0), 0)) == SYMBOL_REF
+ && GET_CODE (XEXP (XEXP (orig, 0), 1)) == CONST_INT)
+ return orig;
+ break;
+
+ /* A MEM with a constant address is not sharable. The problem is that
+ the constant address may need to be reloaded. If the mem is shared,
+ then reloading one copy of this mem will cause all copies to appear
+ to have been reloaded. */
+ }
+
+ copy = rtx_alloc (code);
+ PUT_MODE (copy, GET_MODE (orig));
+ copy->in_struct = orig->in_struct;
+ copy->volatil = orig->volatil;
+ copy->unchanging = orig->unchanging;
+ copy->integrated = orig->integrated;
+
+ format_ptr = GET_RTX_FORMAT (GET_CODE (copy));
+
+ for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++)
+ {
+ switch (*format_ptr++)
+ {
+ case 'e':
+ XEXP (copy, i) = XEXP (orig, i);
+ if (XEXP (orig, i) != NULL)
+ XEXP (copy, i) = copy_rtx (XEXP (orig, i));
+ break;
+
+ case '0':
+ case 'u':
+ XEXP (copy, i) = XEXP (orig, i);
+ break;
+
+ case 'E':
+ case 'V':
+ XVEC (copy, i) = XVEC (orig, i);
+ if (XVEC (orig, i) != NULL)
+ {
+ XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i));
+ for (j = 0; j < XVECLEN (copy, i); j++)
+ XVECEXP (copy, i, j) = copy_rtx (XVECEXP (orig, i, j));
+ }
+ break;
+
+ case 'w':
+ XWINT (copy, i) = XWINT (orig, i);
+ break;
+
+ case 'i':
+ XINT (copy, i) = XINT (orig, i);
+ break;
+
+ case 's':
+ case 'S':
+ XSTR (copy, i) = XSTR (orig, i);
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ return copy;
+}
+
+/* Similar to `copy_rtx' except that if MAY_SHARE is present, it is
+ placed in the result directly, rather than being copied. */
+
+rtx
+copy_most_rtx (orig, may_share)
+ register rtx orig;
+ register rtx may_share;
+{
+ register rtx copy;
+ register int i, j;
+ register RTX_CODE code;
+ register char *format_ptr;
+
+ if (orig == may_share)
+ return orig;
+
+ code = GET_CODE (orig);
+
+ switch (code)
+ {
+ case REG:
+ case QUEUED:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case CODE_LABEL:
+ case PC:
+ case CC0:
+ return orig;
+ }
+
+ copy = rtx_alloc (code);
+ PUT_MODE (copy, GET_MODE (orig));
+ copy->in_struct = orig->in_struct;
+ copy->volatil = orig->volatil;
+ copy->unchanging = orig->unchanging;
+ copy->integrated = orig->integrated;
+
+ format_ptr = GET_RTX_FORMAT (GET_CODE (copy));
+
+ for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++)
+ {
+ switch (*format_ptr++)
+ {
+ case 'e':
+ XEXP (copy, i) = XEXP (orig, i);
+ if (XEXP (orig, i) != NULL && XEXP (orig, i) != may_share)
+ XEXP (copy, i) = copy_most_rtx (XEXP (orig, i), may_share);
+ break;
+
+ case '0':
+ case 'u':
+ XEXP (copy, i) = XEXP (orig, i);
+ break;
+
+ case 'E':
+ case 'V':
+ XVEC (copy, i) = XVEC (orig, i);
+ if (XVEC (orig, i) != NULL)
+ {
+ XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i));
+ for (j = 0; j < XVECLEN (copy, i); j++)
+ XVECEXP (copy, i, j)
+ = copy_most_rtx (XVECEXP (orig, i, j), may_share);
+ }
+ break;
+
+ case 'w':
+ XWINT (copy, i) = XWINT (orig, i);
+ break;
+
+ case 'n':
+ case 'i':
+ XINT (copy, i) = XINT (orig, i);
+ break;
+
+ case 's':
+ case 'S':
+ XSTR (copy, i) = XSTR (orig, i);
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ return copy;
+}
+
+/* Subroutines of read_rtx. */
+
+/* Dump code after printing a message. Used when read_rtx finds
+ invalid data. */
+
+static void
+dump_and_abort (expected_c, actual_c, infile)
+ int expected_c, actual_c;
+ FILE *infile;
+{
+ int c, i;
+
+ if (expected_c >= 0)
+ fprintf (stderr,
+ "Expected character %c. Found character %c.",
+ expected_c, actual_c);
+ fprintf (stderr, " At file position: %ld\n", ftell (infile));
+ fprintf (stderr, "Following characters are:\n\t");
+ for (i = 0; i < 200; i++)
+ {
+ c = getc (infile);
+ if (EOF == c) break;
+ putc (c, stderr);
+ }
+ fprintf (stderr, "Aborting.\n");
+ abort ();
+}
+
+/* Read chars from INFILE until a non-whitespace char
+ and return that. Comments, both Lisp style and C style,
+ are treated as whitespace.
+ Tools such as genflags use this function. */
+
+int
+read_skip_spaces (infile)
+ FILE *infile;
+{
+ register int c;
+ while (c = getc (infile))
+ {
+ if (c == ' ' || c == '\n' || c == '\t' || c == '\f')
+ ;
+ else if (c == ';')
+ {
+ while ((c = getc (infile)) && c != '\n') ;
+ }
+ else if (c == '/')
+ {
+ register int prevc;
+ c = getc (infile);
+ if (c != '*')
+ dump_and_abort ('*', c, infile);
+
+ prevc = 0;
+ while (c = getc (infile))
+ {
+ if (prevc == '*' && c == '/')
+ break;
+ prevc = c;
+ }
+ }
+ else break;
+ }
+ return c;
+}
+
+/* Read an rtx code name into the buffer STR[].
+ It is terminated by any of the punctuation chars of rtx printed syntax. */
+
+static void
+read_name (str, infile)
+ char *str;
+ FILE *infile;
+{
+ register char *p;
+ register int c;
+
+ c = read_skip_spaces(infile);
+
+ p = str;
+ while (1)
+ {
+ if (c == ' ' || c == '\n' || c == '\t' || c == '\f')
+ break;
+ if (c == ':' || c == ')' || c == ']' || c == '"' || c == '/'
+ || c == '(' || c == '[')
+ {
+ ungetc (c, infile);
+ break;
+ }
+ *p++ = c;
+ c = getc (infile);
+ }
+ if (p == str)
+ {
+ fprintf (stderr, "missing name or number");
+ dump_and_abort (-1, -1, infile);
+ }
+
+ *p = 0;
+}
+
+/* Read an rtx in printed representation from INFILE
+ and return an actual rtx in core constructed accordingly.
+ read_rtx is not used in the compiler proper, but rather in
+ the utilities gen*.c that construct C code from machine descriptions. */
+
+rtx
+read_rtx (infile)
+ FILE *infile;
+{
+ register int i, j, list_counter;
+ RTX_CODE tmp_code;
+ register char *format_ptr;
+ /* tmp_char is a buffer used for reading decimal integers
+ and names of rtx types and machine modes.
+ Therefore, 256 must be enough. */
+ char tmp_char[256];
+ rtx return_rtx;
+ register int c;
+ int tmp_int;
+ HOST_WIDE_INT tmp_wide;
+
+ /* Linked list structure for making RTXs: */
+ struct rtx_list
+ {
+ struct rtx_list *next;
+ rtx value; /* Value of this node... */
+ };
+
+ c = read_skip_spaces (infile); /* Should be open paren. */
+ if (c != '(')
+ dump_and_abort ('(', c, infile);
+
+ read_name (tmp_char, infile);
+
+ tmp_code = UNKNOWN;
+
+ for (i=0; i < NUM_RTX_CODE; i++) /* @@ might speed this search up */
+ {
+ if (!(strcmp (tmp_char, GET_RTX_NAME (i))))
+ {
+ tmp_code = (RTX_CODE) i; /* get value for name */
+ break;
+ }
+ }
+ if (tmp_code == UNKNOWN)
+ {
+ fprintf (stderr,
+ "Unknown rtx read in rtl.read_rtx(). Code name was %s .",
+ tmp_char);
+ }
+ /* (NIL) stands for an expression that isn't there. */
+ if (tmp_code == NIL)
+ {
+ /* Discard the closeparen. */
+ while ((c = getc (infile)) && c != ')');
+ return 0;
+ }
+
+ return_rtx = rtx_alloc (tmp_code); /* if we end up with an insn expression
+ then we free this space below. */
+ format_ptr = GET_RTX_FORMAT (GET_CODE (return_rtx));
+
+ /* If what follows is `: mode ', read it and
+ store the mode in the rtx. */
+
+ i = read_skip_spaces (infile);
+ if (i == ':')
+ {
+ register int k;
+ read_name (tmp_char, infile);
+ for (k = 0; k < NUM_MACHINE_MODES; k++)
+ if (!strcmp (GET_MODE_NAME (k), tmp_char))
+ break;
+
+ PUT_MODE (return_rtx, (enum machine_mode) k );
+ }
+ else
+ ungetc (i, infile);
+
+ for (i = 0; i < GET_RTX_LENGTH (GET_CODE (return_rtx)); i++)
+ switch (*format_ptr++)
+ {
+ /* 0 means a field for internal use only.
+ Don't expect it to be present in the input. */
+ case '0':
+ break;
+
+ case 'e':
+ case 'u':
+ XEXP (return_rtx, i) = read_rtx (infile);
+ break;
+
+ case 'V':
+ /* 'V' is an optional vector: if a closeparen follows,
+ just store NULL for this element. */
+ c = read_skip_spaces (infile);
+ ungetc (c, infile);
+ if (c == ')')
+ {
+ XVEC (return_rtx, i) = 0;
+ break;
+ }
+ /* Now process the vector. */
+
+ case 'E':
+ {
+ register struct rtx_list *next_rtx, *rtx_list_link;
+ struct rtx_list *list_rtx;
+
+ c = read_skip_spaces (infile);
+ if (c != '[')
+ dump_and_abort ('[', c, infile);
+
+ /* add expressions to a list, while keeping a count */
+ next_rtx = NULL;
+ list_counter = 0;
+ while ((c = read_skip_spaces (infile)) && c != ']')
+ {
+ ungetc (c, infile);
+ list_counter++;
+ rtx_list_link = (struct rtx_list *)
+ alloca (sizeof (struct rtx_list));
+ rtx_list_link->value = read_rtx (infile);
+ if (next_rtx == 0)
+ list_rtx = rtx_list_link;
+ else
+ next_rtx->next = rtx_list_link;
+ next_rtx = rtx_list_link;
+ rtx_list_link->next = 0;
+ }
+ /* get vector length and allocate it */
+ XVEC (return_rtx, i) = (list_counter
+ ? rtvec_alloc (list_counter) : NULL_RTVEC);
+ if (list_counter > 0)
+ {
+ next_rtx = list_rtx;
+ for (j = 0; j < list_counter; j++,
+ next_rtx = next_rtx->next)
+ XVECEXP (return_rtx, i, j) = next_rtx->value;
+ }
+ /* close bracket gotten */
+ }
+ break;
+
+ case 'S':
+ /* 'S' is an optional string: if a closeparen follows,
+ just store NULL for this element. */
+ c = read_skip_spaces (infile);
+ ungetc (c, infile);
+ if (c == ')')
+ {
+ XSTR (return_rtx, i) = 0;
+ break;
+ }
+
+ case 's':
+ {
+ int saw_paren = 0;
+ register char *stringbuf;
+
+ c = read_skip_spaces (infile);
+ if (c == '(')
+ {
+ saw_paren = 1;
+ c = read_skip_spaces (infile);
+ }
+ if (c != '"')
+ dump_and_abort ('"', c, infile);
+
+ while (1)
+ {
+ c = getc (infile); /* Read the string */
+ if (c == '\\')
+ {
+ c = getc (infile); /* Read the string */
+ /* \; makes stuff for a C string constant containing
+ newline and tab. */
+ if (c == ';')
+ {
+ obstack_grow (rtl_obstack, "\\n\\t", 4);
+ continue;
+ }
+ }
+ else if (c == '"')
+ break;
+
+ obstack_1grow (rtl_obstack, c);
+ }
+
+ obstack_1grow (rtl_obstack, 0);
+ stringbuf = (char *) obstack_finish (rtl_obstack);
+
+ if (saw_paren)
+ {
+ c = read_skip_spaces (infile);
+ if (c != ')')
+ dump_and_abort (')', c, infile);
+ }
+ XSTR (return_rtx, i) = stringbuf;
+ }
+ break;
+
+ case 'w':
+ read_name (tmp_char, infile);
+#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT
+ tmp_wide = atoi (tmp_char);
+#else
+ tmp_wide = atol (tmp_char);
+#endif
+ XWINT (return_rtx, i) = tmp_wide;
+ break;
+
+ case 'i':
+ case 'n':
+ read_name (tmp_char, infile);
+ tmp_int = atoi (tmp_char);
+ XINT (return_rtx, i) = tmp_int;
+ break;
+
+ default:
+ fprintf (stderr,
+ "switch format wrong in rtl.read_rtx(). format was: %c.\n",
+ format_ptr[-1]);
+ fprintf (stderr, "\tfile position: %ld\n", ftell (infile));
+ abort ();
+ }
+
+ c = read_skip_spaces (infile);
+ if (c != ')')
+ dump_and_abort (')', c, infile);
+
+ return return_rtx;
+}
+
+/* This is called once per compilation, before any rtx's are constructed.
+ It initializes the vector `rtx_length', the extra CC modes, if any,
+ and computes certain commonly-used modes. */
+
+void
+init_rtl ()
+{
+ int min_class_size[(int) MAX_MODE_CLASS];
+ enum machine_mode mode;
+ int i;
+
+ for (i = 0; i < NUM_RTX_CODE; i++)
+ rtx_length[i] = strlen (rtx_format[i]);
+
+ /* Make CONST_DOUBLE bigger, if real values are bigger than
+ it normally expects to have room for.
+ Note that REAL_VALUE_TYPE is not defined by default,
+ since tree.h is not included. But the default dfn as `double'
+ would do no harm. */
+#ifdef REAL_VALUE_TYPE
+ i = sizeof (REAL_VALUE_TYPE) / sizeof (rtunion) + 2;
+ if (rtx_length[(int) CONST_DOUBLE] < i)
+ {
+ char *s = (char *) xmalloc (i + 1);
+ rtx_length[(int) CONST_DOUBLE] = i;
+ rtx_format[(int) CONST_DOUBLE] = s;
+ *s++ = 'e';
+ *s++ = '0';
+ /* Set the GET_RTX_FORMAT of CONST_DOUBLE to a string
+ of as many `w's as we now have elements. Subtract two from
+ the size to account for the 'e' and the '0'. */
+ for (i = 2; i < rtx_length[(int) CONST_DOUBLE]; i++)
+ *s++ = 'w';
+ *s++ = 0;
+ }
+#endif
+
+#ifdef EXTRA_CC_MODES
+ for (i = (int) CCmode + 1; i < (int) MAX_MACHINE_MODE; i++)
+ {
+ mode_class[i] = MODE_CC;
+ mode_size[i] = mode_size[(int) CCmode];
+ mode_unit_size[i] = mode_unit_size[(int) CCmode];
+ mode_wider_mode[i - 1] = (enum machine_mode) i;
+ mode_wider_mode[i] = VOIDmode;
+ }
+#endif
+
+ /* Find the narrowest mode for each class. */
+
+ for (i = 0; i < (int) MAX_MODE_CLASS; i++)
+ min_class_size[i] = 1000;
+
+ for (mode = VOIDmode; (int) mode < (int) MAX_MACHINE_MODE;
+ mode = (enum machine_mode) ((int) mode + 1))
+ {
+ if (GET_MODE_SIZE (mode) < min_class_size[(int) GET_MODE_CLASS (mode)])
+ {
+ class_narrowest_mode[(int) GET_MODE_CLASS (mode)] = mode;
+ min_class_size[(int) GET_MODE_CLASS (mode)] = GET_MODE_SIZE (mode);
+ }
+ }
+}
+
+#ifdef memset
+gcc_memset (dest, value, len)
+ char *dest;
+ int value;
+ int len;
+{
+ while (len-- > 0)
+ *dest++ = value;
+}
+#endif /* memset */
diff --git a/gnu/usr.bin/cc/cc_int/rtlanal.c b/gnu/usr.bin/cc/cc_int/rtlanal.c
new file mode 100644
index 0000000..188fb93
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/rtlanal.c
@@ -0,0 +1,1835 @@
+/* Analyze RTL for C-Compiler
+ Copyright (C) 1987, 88, 91, 92, 93, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#include "rtl.h"
+
+void note_stores ();
+int reg_set_p ();
+
+/* Bit flags that specify the machine subtype we are compiling for.
+ Bits are tested using macros TARGET_... defined in the tm.h file
+ and set by `-m...' switches. Must be defined in rtlanal.c. */
+
+int target_flags;
+
+/* Return 1 if the value of X is unstable
+ (would be different at a different point in the program).
+ The frame pointer, arg pointer, etc. are considered stable
+ (within one function) and so is anything marked `unchanging'. */
+
+int
+rtx_unstable_p (x)
+ rtx x;
+{
+ register RTX_CODE code = GET_CODE (x);
+ register int i;
+ register char *fmt;
+
+ if (code == MEM)
+ return ! RTX_UNCHANGING_P (x);
+
+ if (code == QUEUED)
+ return 1;
+
+ if (code == CONST || code == CONST_INT)
+ return 0;
+
+ if (code == REG)
+ return ! (REGNO (x) == FRAME_POINTER_REGNUM
+ || REGNO (x) == HARD_FRAME_POINTER_REGNUM
+ || REGNO (x) == ARG_POINTER_REGNUM
+ || RTX_UNCHANGING_P (x));
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ if (fmt[i] == 'e')
+ if (rtx_unstable_p (XEXP (x, i)))
+ return 1;
+ return 0;
+}
+
+/* Return 1 if X has a value that can vary even between two
+ executions of the program. 0 means X can be compared reliably
+ against certain constants or near-constants.
+ The frame pointer and the arg pointer are considered constant. */
+
+int
+rtx_varies_p (x)
+ rtx x;
+{
+ register RTX_CODE code = GET_CODE (x);
+ register int i;
+ register char *fmt;
+
+ switch (code)
+ {
+ case MEM:
+ case QUEUED:
+ return 1;
+
+ case CONST:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return 0;
+
+ case REG:
+ /* Note that we have to test for the actual rtx used for the frame
+ and arg pointers and not just the register number in case we have
+ eliminated the frame and/or arg pointer and are using it
+ for pseudos. */
+ return ! (x == frame_pointer_rtx || x == hard_frame_pointer_rtx
+ || x == arg_pointer_rtx);
+
+ case LO_SUM:
+ /* The operand 0 of a LO_SUM is considered constant
+ (in fact is it related specifically to operand 1). */
+ return rtx_varies_p (XEXP (x, 1));
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ if (fmt[i] == 'e')
+ if (rtx_varies_p (XEXP (x, i)))
+ return 1;
+ return 0;
+}
+
+/* Return 0 if the use of X as an address in a MEM can cause a trap. */
+
+int
+rtx_addr_can_trap_p (x)
+ register rtx x;
+{
+ register enum rtx_code code = GET_CODE (x);
+
+ switch (code)
+ {
+ case SYMBOL_REF:
+ case LABEL_REF:
+ /* SYMBOL_REF is problematic due to the possible presence of
+ a #pragma weak, but to say that loads from symbols can trap is
+ *very* costly. It's not at all clear what's best here. For
+ now, we ignore the impact of #pragma weak. */
+ return 0;
+
+ case REG:
+ /* As in rtx_varies_p, we have to use the actual rtx, not reg number. */
+ return ! (x == frame_pointer_rtx || x == hard_frame_pointer_rtx
+ || x == stack_pointer_rtx || x == arg_pointer_rtx);
+
+ case CONST:
+ return rtx_addr_can_trap_p (XEXP (x, 0));
+
+ case PLUS:
+ /* An address is assumed not to trap if it is an address that can't
+ trap plus a constant integer. */
+ return (rtx_addr_can_trap_p (XEXP (x, 0))
+ || GET_CODE (XEXP (x, 1)) != CONST_INT);
+
+ case LO_SUM:
+ return rtx_addr_can_trap_p (XEXP (x, 1));
+ }
+
+ /* If it isn't one of the case above, it can cause a trap. */
+ return 1;
+}
+
+/* Return 1 if X refers to a memory location whose address
+ cannot be compared reliably with constant addresses,
+ or if X refers to a BLKmode memory object. */
+
+int
+rtx_addr_varies_p (x)
+ rtx x;
+{
+ register enum rtx_code code;
+ register int i;
+ register char *fmt;
+
+ if (x == 0)
+ return 0;
+
+ code = GET_CODE (x);
+ if (code == MEM)
+ return GET_MODE (x) == BLKmode || rtx_varies_p (XEXP (x, 0));
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ if (fmt[i] == 'e')
+ if (rtx_addr_varies_p (XEXP (x, i)))
+ return 1;
+ return 0;
+}
+
+/* Return the value of the integer term in X, if one is apparent;
+ otherwise return 0.
+ Only obvious integer terms are detected.
+ This is used in cse.c with the `related_value' field.*/
+
+HOST_WIDE_INT
+get_integer_term (x)
+ rtx x;
+{
+ if (GET_CODE (x) == CONST)
+ x = XEXP (x, 0);
+
+ if (GET_CODE (x) == MINUS
+ && GET_CODE (XEXP (x, 1)) == CONST_INT)
+ return - INTVAL (XEXP (x, 1));
+ if (GET_CODE (x) == PLUS
+ && GET_CODE (XEXP (x, 1)) == CONST_INT)
+ return INTVAL (XEXP (x, 1));
+ return 0;
+}
+
+/* If X is a constant, return the value sans apparent integer term;
+ otherwise return 0.
+ Only obvious integer terms are detected. */
+
+rtx
+get_related_value (x)
+ rtx x;
+{
+ if (GET_CODE (x) != CONST)
+ return 0;
+ x = XEXP (x, 0);
+ if (GET_CODE (x) == PLUS
+ && GET_CODE (XEXP (x, 1)) == CONST_INT)
+ return XEXP (x, 0);
+ else if (GET_CODE (x) == MINUS
+ && GET_CODE (XEXP (x, 1)) == CONST_INT)
+ return XEXP (x, 0);
+ return 0;
+}
+
+/* Nonzero if register REG appears somewhere within IN.
+ Also works if REG is not a register; in this case it checks
+ for a subexpression of IN that is Lisp "equal" to REG. */
+
+int
+reg_mentioned_p (reg, in)
+ register rtx reg, in;
+{
+ register char *fmt;
+ register int i;
+ register enum rtx_code code;
+
+ if (in == 0)
+ return 0;
+
+ if (reg == in)
+ return 1;
+
+ if (GET_CODE (in) == LABEL_REF)
+ return reg == XEXP (in, 0);
+
+ code = GET_CODE (in);
+
+ switch (code)
+ {
+ /* Compare registers by number. */
+ case REG:
+ return GET_CODE (reg) == REG && REGNO (in) == REGNO (reg);
+
+ /* These codes have no constituent expressions
+ and are unique. */
+ case SCRATCH:
+ case CC0:
+ case PC:
+ return 0;
+
+ case CONST_INT:
+ return GET_CODE (reg) == CONST_INT && INTVAL (in) == INTVAL (reg);
+
+ case CONST_DOUBLE:
+ /* These are kept unique for a given value. */
+ return 0;
+ }
+
+ if (GET_CODE (reg) == code && rtx_equal_p (reg, in))
+ return 1;
+
+ fmt = GET_RTX_FORMAT (code);
+
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = XVECLEN (in, i) - 1; j >= 0; j--)
+ if (reg_mentioned_p (reg, XVECEXP (in, i, j)))
+ return 1;
+ }
+ else if (fmt[i] == 'e'
+ && reg_mentioned_p (reg, XEXP (in, i)))
+ return 1;
+ }
+ return 0;
+}
+
+/* Return 1 if in between BEG and END, exclusive of BEG and END, there is
+ no CODE_LABEL insn. */
+
+int
+no_labels_between_p (beg, end)
+ rtx beg, end;
+{
+ register rtx p;
+ for (p = NEXT_INSN (beg); p != end; p = NEXT_INSN (p))
+ if (GET_CODE (p) == CODE_LABEL)
+ return 0;
+ return 1;
+}
+
+/* Nonzero if register REG is used in an insn between
+ FROM_INSN and TO_INSN (exclusive of those two). */
+
+int
+reg_used_between_p (reg, from_insn, to_insn)
+ rtx reg, from_insn, to_insn;
+{
+ register rtx insn;
+
+ if (from_insn == to_insn)
+ return 0;
+
+ for (insn = NEXT_INSN (from_insn); insn != to_insn; insn = NEXT_INSN (insn))
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && (reg_overlap_mentioned_p (reg, PATTERN (insn))
+ || (GET_CODE (insn) == CALL_INSN
+ && (find_reg_fusage (insn, USE, reg)
+ || find_reg_fusage (insn, CLOBBER, reg)))))
+ return 1;
+ return 0;
+}
+
+/* Nonzero if the old value of X, a register, is referenced in BODY. If X
+ is entirely replaced by a new value and the only use is as a SET_DEST,
+ we do not consider it a reference. */
+
+int
+reg_referenced_p (x, body)
+ rtx x;
+ rtx body;
+{
+ int i;
+
+ switch (GET_CODE (body))
+ {
+ case SET:
+ if (reg_overlap_mentioned_p (x, SET_SRC (body)))
+ return 1;
+
+ /* If the destination is anything other than CC0, PC, a REG or a SUBREG
+ of a REG that occupies all of the REG, the insn references X if
+ it is mentioned in the destination. */
+ if (GET_CODE (SET_DEST (body)) != CC0
+ && GET_CODE (SET_DEST (body)) != PC
+ && GET_CODE (SET_DEST (body)) != REG
+ && ! (GET_CODE (SET_DEST (body)) == SUBREG
+ && GET_CODE (SUBREG_REG (SET_DEST (body))) == REG
+ && (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (body))))
+ + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)
+ == ((GET_MODE_SIZE (GET_MODE (SET_DEST (body)))
+ + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)))
+ && reg_overlap_mentioned_p (x, SET_DEST (body)))
+ return 1;
+ break;
+
+ case ASM_OPERANDS:
+ for (i = ASM_OPERANDS_INPUT_LENGTH (body) - 1; i >= 0; i--)
+ if (reg_overlap_mentioned_p (x, ASM_OPERANDS_INPUT (body, i)))
+ return 1;
+ break;
+
+ case CALL:
+ case USE:
+ return reg_overlap_mentioned_p (x, body);
+
+ case TRAP_IF:
+ return reg_overlap_mentioned_p (x, TRAP_CONDITION (body));
+
+ case UNSPEC:
+ case UNSPEC_VOLATILE:
+ case PARALLEL:
+ for (i = XVECLEN (body, 0) - 1; i >= 0; i--)
+ if (reg_referenced_p (x, XVECEXP (body, 0, i)))
+ return 1;
+ break;
+ }
+
+ return 0;
+}
+
+/* Nonzero if register REG is referenced in an insn between
+ FROM_INSN and TO_INSN (exclusive of those two). Sets of REG do
+ not count. */
+
+int
+reg_referenced_between_p (reg, from_insn, to_insn)
+ rtx reg, from_insn, to_insn;
+{
+ register rtx insn;
+
+ if (from_insn == to_insn)
+ return 0;
+
+ for (insn = NEXT_INSN (from_insn); insn != to_insn; insn = NEXT_INSN (insn))
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && (reg_referenced_p (reg, PATTERN (insn))
+ || (GET_CODE (insn) == CALL_INSN
+ && find_reg_fusage (insn, USE, reg))))
+ return 1;
+ return 0;
+}
+
+/* Nonzero if register REG is set or clobbered in an insn between
+ FROM_INSN and TO_INSN (exclusive of those two). */
+
+int
+reg_set_between_p (reg, from_insn, to_insn)
+ rtx reg, from_insn, to_insn;
+{
+ register rtx insn;
+
+ if (from_insn == to_insn)
+ return 0;
+
+ for (insn = NEXT_INSN (from_insn); insn != to_insn; insn = NEXT_INSN (insn))
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && reg_set_p (reg, insn))
+ return 1;
+ return 0;
+}
+
+/* Internals of reg_set_between_p. */
+
+static rtx reg_set_reg;
+static int reg_set_flag;
+
+void
+reg_set_p_1 (x)
+ rtx x;
+{
+ /* We don't want to return 1 if X is a MEM that contains a register
+ within REG_SET_REG. */
+
+ if ((GET_CODE (x) != MEM)
+ && reg_overlap_mentioned_p (reg_set_reg, x))
+ reg_set_flag = 1;
+}
+
+int
+reg_set_p (reg, insn)
+ rtx reg, insn;
+{
+ rtx body = insn;
+
+ /* We can be passed an insn or part of one. If we are passed an insn,
+ check if a side-effect of the insn clobbers REG. */
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ if (FIND_REG_INC_NOTE (insn, reg)
+ || (GET_CODE (insn) == CALL_INSN
+ /* We'd like to test call_used_regs here, but rtlanal.c can't
+ reference that variable due to its use in genattrtab. So
+ we'll just be more conservative.
+
+ ??? Unless we could ensure that the CALL_INSN_FUNCTION_USAGE
+ information holds all clobbered registers. */
+ && ((GET_CODE (reg) == REG
+ && REGNO (reg) < FIRST_PSEUDO_REGISTER)
+ || GET_CODE (reg) == MEM
+ || find_reg_fusage (insn, CLOBBER, reg))))
+ return 1;
+
+ body = PATTERN (insn);
+ }
+
+ reg_set_reg = reg;
+ reg_set_flag = 0;
+ note_stores (body, reg_set_p_1);
+ return reg_set_flag;
+}
+
+/* Similar to reg_set_between_p, but check all registers in X. Return 0
+ only if none of them are modified between START and END. Return 1 if
+ X contains a MEM; this routine does not perform any memory aliasing. */
+
+int
+modified_between_p (x, start, end)
+ rtx x;
+ rtx start, end;
+{
+ enum rtx_code code = GET_CODE (x);
+ char *fmt;
+ int i, j;
+
+ switch (code)
+ {
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return 0;
+
+ case PC:
+ case CC0:
+ return 1;
+
+ case MEM:
+ /* If the memory is not constant, assume it is modified. If it is
+ constant, we still have to check the address. */
+ if (! RTX_UNCHANGING_P (x))
+ return 1;
+ break;
+
+ case REG:
+ return reg_set_between_p (x, start, end);
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e' && modified_between_p (XEXP (x, i), start, end))
+ return 1;
+
+ if (fmt[i] == 'E')
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (modified_between_p (XVECEXP (x, i, j), start, end))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Similar to reg_set_p, but check all registers in X. Return 0 only if none
+ of them are modified in INSN. Return 1 if X contains a MEM; this routine
+ does not perform any memory aliasing. */
+
+int
+modified_in_p (x, insn)
+ rtx x;
+ rtx insn;
+{
+ enum rtx_code code = GET_CODE (x);
+ char *fmt;
+ int i, j;
+
+ switch (code)
+ {
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return 0;
+
+ case PC:
+ case CC0:
+ return 1;
+
+ case MEM:
+ /* If the memory is not constant, assume it is modified. If it is
+ constant, we still have to check the address. */
+ if (! RTX_UNCHANGING_P (x))
+ return 1;
+ break;
+
+ case REG:
+ return reg_set_p (x, insn);
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e' && modified_in_p (XEXP (x, i), insn))
+ return 1;
+
+ if (fmt[i] == 'E')
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (modified_in_p (XVECEXP (x, i, j), insn))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Given an INSN, return a SET expression if this insn has only a single SET.
+ It may also have CLOBBERs, USEs, or SET whose output
+ will not be used, which we ignore. */
+
+rtx
+single_set (insn)
+ rtx insn;
+{
+ rtx set;
+ int i;
+
+ if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+ return 0;
+
+ if (GET_CODE (PATTERN (insn)) == SET)
+ return PATTERN (insn);
+
+ else if (GET_CODE (PATTERN (insn)) == PARALLEL)
+ {
+ for (i = 0, set = 0; i < XVECLEN (PATTERN (insn), 0); i++)
+ if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET
+ && (! find_reg_note (insn, REG_UNUSED,
+ SET_DEST (XVECEXP (PATTERN (insn), 0, i)))
+ || side_effects_p (XVECEXP (PATTERN (insn), 0, i))))
+ {
+ if (set)
+ return 0;
+ else
+ set = XVECEXP (PATTERN (insn), 0, i);
+ }
+ return set;
+ }
+
+ return 0;
+}
+
+/* Return the last thing that X was assigned from before *PINSN. Verify that
+ the object is not modified up to VALID_TO. If it was, if we hit
+ a partial assignment to X, or hit a CODE_LABEL first, return X. If we
+ found an assignment, update *PINSN to point to it. */
+
+rtx
+find_last_value (x, pinsn, valid_to)
+ rtx x;
+ rtx *pinsn;
+ rtx valid_to;
+{
+ rtx p;
+
+ for (p = PREV_INSN (*pinsn); p && GET_CODE (p) != CODE_LABEL;
+ p = PREV_INSN (p))
+ if (GET_RTX_CLASS (GET_CODE (p)) == 'i')
+ {
+ rtx set = single_set (p);
+ rtx note = find_reg_note (p, REG_EQUAL, NULL_RTX);
+
+ if (set && rtx_equal_p (x, SET_DEST (set)))
+ {
+ rtx src = SET_SRC (set);
+
+ if (note && GET_CODE (XEXP (note, 0)) != EXPR_LIST)
+ src = XEXP (note, 0);
+
+ if (! modified_between_p (src, PREV_INSN (p), valid_to)
+ /* Reject hard registers because we don't usually want
+ to use them; we'd rather use a pseudo. */
+ && ! (GET_CODE (src) == REG
+ && REGNO (src) < FIRST_PSEUDO_REGISTER))
+ {
+ *pinsn = p;
+ return src;
+ }
+ }
+
+ /* If set in non-simple way, we don't have a value. */
+ if (reg_set_p (x, p))
+ break;
+ }
+
+ return x;
+}
+
+/* Return nonzero if register in range [REGNO, ENDREGNO)
+ appears either explicitly or implicitly in X
+ other than being stored into.
+
+ References contained within the substructure at LOC do not count.
+ LOC may be zero, meaning don't ignore anything. */
+
+int
+refers_to_regno_p (regno, endregno, x, loc)
+ int regno, endregno;
+ rtx x;
+ rtx *loc;
+{
+ register int i;
+ register RTX_CODE code;
+ register char *fmt;
+
+ repeat:
+ /* The contents of a REG_NONNEG note is always zero, so we must come here
+ upon repeat in case the last REG_NOTE is a REG_NONNEG note. */
+ if (x == 0)
+ return 0;
+
+ code = GET_CODE (x);
+
+ switch (code)
+ {
+ case REG:
+ i = REGNO (x);
+
+ /* If we modifying the stack, frame, or argument pointer, it will
+ clobber a virtual register. In fact, we could be more precise,
+ but it isn't worth it. */
+ if ((i == STACK_POINTER_REGNUM
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ || i == ARG_POINTER_REGNUM
+#endif
+ || i == FRAME_POINTER_REGNUM)
+ && regno >= FIRST_VIRTUAL_REGISTER && regno <= LAST_VIRTUAL_REGISTER)
+ return 1;
+
+ return (endregno > i
+ && regno < i + (i < FIRST_PSEUDO_REGISTER
+ ? HARD_REGNO_NREGS (i, GET_MODE (x))
+ : 1));
+
+ case SUBREG:
+ /* If this is a SUBREG of a hard reg, we can see exactly which
+ registers are being modified. Otherwise, handle normally. */
+ if (GET_CODE (SUBREG_REG (x)) == REG
+ && REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER)
+ {
+ int inner_regno = REGNO (SUBREG_REG (x)) + SUBREG_WORD (x);
+ int inner_endregno
+ = inner_regno + (inner_regno < FIRST_PSEUDO_REGISTER
+ ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1);
+
+ return endregno > inner_regno && regno < inner_endregno;
+ }
+ break;
+
+ case CLOBBER:
+ case SET:
+ if (&SET_DEST (x) != loc
+ /* Note setting a SUBREG counts as referring to the REG it is in for
+ a pseudo but not for hard registers since we can
+ treat each word individually. */
+ && ((GET_CODE (SET_DEST (x)) == SUBREG
+ && loc != &SUBREG_REG (SET_DEST (x))
+ && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG
+ && REGNO (SUBREG_REG (SET_DEST (x))) >= FIRST_PSEUDO_REGISTER
+ && refers_to_regno_p (regno, endregno,
+ SUBREG_REG (SET_DEST (x)), loc))
+ || (GET_CODE (SET_DEST (x)) != REG
+ && refers_to_regno_p (regno, endregno, SET_DEST (x), loc))))
+ return 1;
+
+ if (code == CLOBBER || loc == &SET_SRC (x))
+ return 0;
+ x = SET_SRC (x);
+ goto repeat;
+ }
+
+ /* X does not match, so try its subexpressions. */
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e' && loc != &XEXP (x, i))
+ {
+ if (i == 0)
+ {
+ x = XEXP (x, 0);
+ goto repeat;
+ }
+ else
+ if (refers_to_regno_p (regno, endregno, XEXP (x, i), loc))
+ return 1;
+ }
+ else if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = XVECLEN (x, i) - 1; j >=0; j--)
+ if (loc != &XVECEXP (x, i, j)
+ && refers_to_regno_p (regno, endregno, XVECEXP (x, i, j), loc))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Nonzero if modifying X will affect IN. If X is a register or a SUBREG,
+ we check if any register number in X conflicts with the relevant register
+ numbers. If X is a constant, return 0. If X is a MEM, return 1 iff IN
+ contains a MEM (we don't bother checking for memory addresses that can't
+ conflict because we expect this to be a rare case. */
+
+int
+reg_overlap_mentioned_p (x, in)
+ rtx x, in;
+{
+ int regno, endregno;
+
+ if (GET_CODE (x) == SUBREG)
+ {
+ regno = REGNO (SUBREG_REG (x));
+ if (regno < FIRST_PSEUDO_REGISTER)
+ regno += SUBREG_WORD (x);
+ }
+ else if (GET_CODE (x) == REG)
+ regno = REGNO (x);
+ else if (CONSTANT_P (x))
+ return 0;
+ else if (GET_CODE (x) == MEM)
+ {
+ char *fmt;
+ int i;
+
+ if (GET_CODE (in) == MEM)
+ return 1;
+
+ fmt = GET_RTX_FORMAT (GET_CODE (in));
+
+ for (i = GET_RTX_LENGTH (GET_CODE (in)) - 1; i >= 0; i--)
+ if (fmt[i] == 'e' && reg_overlap_mentioned_p (x, XEXP (in, i)))
+ return 1;
+
+ return 0;
+ }
+ else if (GET_CODE (x) == SCRATCH || GET_CODE (x) == PC
+ || GET_CODE (x) == CC0)
+ return reg_mentioned_p (x, in);
+ else
+ abort ();
+
+ endregno = regno + (regno < FIRST_PSEUDO_REGISTER
+ ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1);
+
+ return refers_to_regno_p (regno, endregno, in, NULL_PTR);
+}
+
+/* Used for communications between the next few functions. */
+
+static int reg_set_last_unknown;
+static rtx reg_set_last_value;
+static int reg_set_last_first_regno, reg_set_last_last_regno;
+
+/* Called via note_stores from reg_set_last. */
+
+static void
+reg_set_last_1 (x, pat)
+ rtx x;
+ rtx pat;
+{
+ int first, last;
+
+ /* If X is not a register, or is not one in the range we care
+ about, ignore. */
+ if (GET_CODE (x) != REG)
+ return;
+
+ first = REGNO (x);
+ last = first + (first < FIRST_PSEUDO_REGISTER
+ ? HARD_REGNO_NREGS (first, GET_MODE (x)) : 1);
+
+ if (first >= reg_set_last_last_regno
+ || last <= reg_set_last_first_regno)
+ return;
+
+ /* If this is a CLOBBER or is some complex LHS, or doesn't modify
+ exactly the registers we care about, show we don't know the value. */
+ if (GET_CODE (pat) == CLOBBER || SET_DEST (pat) != x
+ || first != reg_set_last_first_regno
+ || last != reg_set_last_last_regno)
+ reg_set_last_unknown = 1;
+ else
+ reg_set_last_value = SET_SRC (pat);
+}
+
+/* Return the last value to which REG was set prior to INSN. If we can't
+ find it easily, return 0.
+
+ We only return a REG, SUBREG, or constant because it is too hard to
+ check if a MEM remains unchanged. */
+
+rtx
+reg_set_last (x, insn)
+ rtx x;
+ rtx insn;
+{
+ rtx orig_insn = insn;
+
+ reg_set_last_first_regno = REGNO (x);
+
+ reg_set_last_last_regno
+ = reg_set_last_first_regno
+ + (reg_set_last_first_regno < FIRST_PSEUDO_REGISTER
+ ? HARD_REGNO_NREGS (reg_set_last_first_regno, GET_MODE (x)) : 1);
+
+ reg_set_last_unknown = 0;
+ reg_set_last_value = 0;
+
+ /* Scan backwards until reg_set_last_1 changed one of the above flags.
+ Stop when we reach a label or X is a hard reg and we reach a
+ CALL_INSN (if reg_set_last_last_regno is a hard reg).
+
+ If we find a set of X, ensure that its SET_SRC remains unchanged. */
+
+ /* We compare with <= here, because reg_set_last_last_regno
+ is actually the number of the first reg *not* in X. */
+ for (;
+ insn && GET_CODE (insn) != CODE_LABEL
+ && ! (GET_CODE (insn) == CALL_INSN
+ && reg_set_last_last_regno <= FIRST_PSEUDO_REGISTER);
+ insn = PREV_INSN (insn))
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ note_stores (PATTERN (insn), reg_set_last_1);
+ if (reg_set_last_unknown)
+ return 0;
+ else if (reg_set_last_value)
+ {
+ if (CONSTANT_P (reg_set_last_value)
+ || ((GET_CODE (reg_set_last_value) == REG
+ || GET_CODE (reg_set_last_value) == SUBREG)
+ && ! reg_set_between_p (reg_set_last_value,
+ NEXT_INSN (insn), orig_insn)))
+ return reg_set_last_value;
+ else
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/* This is 1 until after reload pass. */
+int rtx_equal_function_value_matters;
+
+/* Return 1 if X and Y are identical-looking rtx's.
+ This is the Lisp function EQUAL for rtx arguments. */
+
+int
+rtx_equal_p (x, y)
+ rtx x, y;
+{
+ register int i;
+ register int j;
+ register enum rtx_code code;
+ register char *fmt;
+
+ if (x == y)
+ return 1;
+ if (x == 0 || y == 0)
+ return 0;
+
+ code = GET_CODE (x);
+ /* Rtx's of different codes cannot be equal. */
+ if (code != GET_CODE (y))
+ return 0;
+
+ /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent.
+ (REG:SI x) and (REG:HI x) are NOT equivalent. */
+
+ if (GET_MODE (x) != GET_MODE (y))
+ return 0;
+
+ /* REG, LABEL_REF, and SYMBOL_REF can be compared nonrecursively. */
+
+ if (code == REG)
+ /* Until rtl generation is complete, don't consider a reference to the
+ return register of the current function the same as the return from a
+ called function. This eases the job of function integration. Once the
+ distinction is no longer needed, they can be considered equivalent. */
+ return (REGNO (x) == REGNO (y)
+ && (! rtx_equal_function_value_matters
+ || REG_FUNCTION_VALUE_P (x) == REG_FUNCTION_VALUE_P (y)));
+ else if (code == LABEL_REF)
+ return XEXP (x, 0) == XEXP (y, 0);
+ else if (code == SYMBOL_REF)
+ return XSTR (x, 0) == XSTR (y, 0);
+ else if (code == SCRATCH || code == CONST_DOUBLE)
+ return 0;
+
+ /* Compare the elements. If any pair of corresponding elements
+ fail to match, return 0 for the whole things. */
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ switch (fmt[i])
+ {
+ case 'w':
+ if (XWINT (x, i) != XWINT (y, i))
+ return 0;
+ break;
+
+ case 'n':
+ case 'i':
+ if (XINT (x, i) != XINT (y, i))
+ return 0;
+ break;
+
+ case 'V':
+ case 'E':
+ /* Two vectors must have the same length. */
+ if (XVECLEN (x, i) != XVECLEN (y, i))
+ return 0;
+
+ /* And the corresponding elements must match. */
+ for (j = 0; j < XVECLEN (x, i); j++)
+ if (rtx_equal_p (XVECEXP (x, i, j), XVECEXP (y, i, j)) == 0)
+ return 0;
+ break;
+
+ case 'e':
+ if (rtx_equal_p (XEXP (x, i), XEXP (y, i)) == 0)
+ return 0;
+ break;
+
+ case 'S':
+ case 's':
+ if (strcmp (XSTR (x, i), XSTR (y, i)))
+ return 0;
+ break;
+
+ case 'u':
+ /* These are just backpointers, so they don't matter. */
+ break;
+
+ case '0':
+ break;
+
+ /* It is believed that rtx's at this level will never
+ contain anything but integers and other rtx's,
+ except for within LABEL_REFs and SYMBOL_REFs. */
+ default:
+ abort ();
+ }
+ }
+ return 1;
+}
+
+/* Call FUN on each register or MEM that is stored into or clobbered by X.
+ (X would be the pattern of an insn).
+ FUN receives two arguments:
+ the REG, MEM, CC0 or PC being stored in or clobbered,
+ the SET or CLOBBER rtx that does the store.
+
+ If the item being stored in or clobbered is a SUBREG of a hard register,
+ the SUBREG will be passed. */
+
+void
+note_stores (x, fun)
+ register rtx x;
+ void (*fun) ();
+{
+ if ((GET_CODE (x) == SET || GET_CODE (x) == CLOBBER))
+ {
+ register rtx dest = SET_DEST (x);
+ while ((GET_CODE (dest) == SUBREG
+ && (GET_CODE (SUBREG_REG (dest)) != REG
+ || REGNO (SUBREG_REG (dest)) >= FIRST_PSEUDO_REGISTER))
+ || GET_CODE (dest) == ZERO_EXTRACT
+ || GET_CODE (dest) == SIGN_EXTRACT
+ || GET_CODE (dest) == STRICT_LOW_PART)
+ dest = XEXP (dest, 0);
+ (*fun) (dest, x);
+ }
+ else if (GET_CODE (x) == PARALLEL)
+ {
+ register int i;
+ for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
+ {
+ register rtx y = XVECEXP (x, 0, i);
+ if (GET_CODE (y) == SET || GET_CODE (y) == CLOBBER)
+ {
+ register rtx dest = SET_DEST (y);
+ while ((GET_CODE (dest) == SUBREG
+ && (GET_CODE (SUBREG_REG (dest)) != REG
+ || (REGNO (SUBREG_REG (dest))
+ >= FIRST_PSEUDO_REGISTER)))
+ || GET_CODE (dest) == ZERO_EXTRACT
+ || GET_CODE (dest) == SIGN_EXTRACT
+ || GET_CODE (dest) == STRICT_LOW_PART)
+ dest = XEXP (dest, 0);
+ (*fun) (dest, y);
+ }
+ }
+ }
+}
+
+/* Return nonzero if X's old contents don't survive after INSN.
+ This will be true if X is (cc0) or if X is a register and
+ X dies in INSN or because INSN entirely sets X.
+
+ "Entirely set" means set directly and not through a SUBREG,
+ ZERO_EXTRACT or SIGN_EXTRACT, so no trace of the old contents remains.
+ Likewise, REG_INC does not count.
+
+ REG may be a hard or pseudo reg. Renumbering is not taken into account,
+ but for this use that makes no difference, since regs don't overlap
+ during their lifetimes. Therefore, this function may be used
+ at any time after deaths have been computed (in flow.c).
+
+ If REG is a hard reg that occupies multiple machine registers, this
+ function will only return 1 if each of those registers will be replaced
+ by INSN. */
+
+int
+dead_or_set_p (insn, x)
+ rtx insn;
+ rtx x;
+{
+ register int regno, last_regno;
+ register int i;
+
+ /* Can't use cc0_rtx below since this file is used by genattrtab.c. */
+ if (GET_CODE (x) == CC0)
+ return 1;
+
+ if (GET_CODE (x) != REG)
+ abort ();
+
+ regno = REGNO (x);
+ last_regno = (regno >= FIRST_PSEUDO_REGISTER ? regno
+ : regno + HARD_REGNO_NREGS (regno, GET_MODE (x)) - 1);
+
+ for (i = regno; i <= last_regno; i++)
+ if (! dead_or_set_regno_p (insn, i))
+ return 0;
+
+ return 1;
+}
+
+/* Utility function for dead_or_set_p to check an individual register. Also
+ called from flow.c. */
+
+int
+dead_or_set_regno_p (insn, test_regno)
+ rtx insn;
+ int test_regno;
+{
+ int regno, endregno;
+ rtx link;
+
+ /* See if there is a death note for something that includes TEST_REGNO. */
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+ {
+ if (REG_NOTE_KIND (link) != REG_DEAD || GET_CODE (XEXP (link, 0)) != REG)
+ continue;
+
+ regno = REGNO (XEXP (link, 0));
+ endregno = (regno >= FIRST_PSEUDO_REGISTER ? regno + 1
+ : regno + HARD_REGNO_NREGS (regno,
+ GET_MODE (XEXP (link, 0))));
+
+ if (test_regno >= regno && test_regno < endregno)
+ return 1;
+ }
+
+ if (GET_CODE (insn) == CALL_INSN
+ && find_regno_fusage (insn, CLOBBER, test_regno))
+ return 1;
+
+ if (GET_CODE (PATTERN (insn)) == SET)
+ {
+ rtx dest = SET_DEST (PATTERN (insn));
+
+ /* A value is totally replaced if it is the destination or the
+ destination is a SUBREG of REGNO that does not change the number of
+ words in it. */
+ if (GET_CODE (dest) == SUBREG
+ && (((GET_MODE_SIZE (GET_MODE (dest))
+ + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
+ == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest)))
+ + UNITS_PER_WORD - 1) / UNITS_PER_WORD)))
+ dest = SUBREG_REG (dest);
+
+ if (GET_CODE (dest) != REG)
+ return 0;
+
+ regno = REGNO (dest);
+ endregno = (regno >= FIRST_PSEUDO_REGISTER ? regno + 1
+ : regno + HARD_REGNO_NREGS (regno, GET_MODE (dest)));
+
+ return (test_regno >= regno && test_regno < endregno);
+ }
+ else if (GET_CODE (PATTERN (insn)) == PARALLEL)
+ {
+ register int i;
+
+ for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
+ {
+ rtx body = XVECEXP (PATTERN (insn), 0, i);
+
+ if (GET_CODE (body) == SET || GET_CODE (body) == CLOBBER)
+ {
+ rtx dest = SET_DEST (body);
+
+ if (GET_CODE (dest) == SUBREG
+ && (((GET_MODE_SIZE (GET_MODE (dest))
+ + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
+ == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest)))
+ + UNITS_PER_WORD - 1) / UNITS_PER_WORD)))
+ dest = SUBREG_REG (dest);
+
+ if (GET_CODE (dest) != REG)
+ continue;
+
+ regno = REGNO (dest);
+ endregno = (regno >= FIRST_PSEUDO_REGISTER ? regno + 1
+ : regno + HARD_REGNO_NREGS (regno, GET_MODE (dest)));
+
+ if (test_regno >= regno && test_regno < endregno)
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Return the reg-note of kind KIND in insn INSN, if there is one.
+ If DATUM is nonzero, look for one whose datum is DATUM. */
+
+rtx
+find_reg_note (insn, kind, datum)
+ rtx insn;
+ enum reg_note kind;
+ rtx datum;
+{
+ register rtx link;
+
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == kind
+ && (datum == 0 || datum == XEXP (link, 0)))
+ return link;
+ return 0;
+}
+
+/* Return the reg-note of kind KIND in insn INSN which applies to register
+ number REGNO, if any. Return 0 if there is no such reg-note. Note that
+ the REGNO of this NOTE need not be REGNO if REGNO is a hard register;
+ it might be the case that the note overlaps REGNO. */
+
+rtx
+find_regno_note (insn, kind, regno)
+ rtx insn;
+ enum reg_note kind;
+ int regno;
+{
+ register rtx link;
+
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == kind
+ /* Verify that it is a register, so that scratch and MEM won't cause a
+ problem here. */
+ && GET_CODE (XEXP (link, 0)) == REG
+ && REGNO (XEXP (link, 0)) <= regno
+ && ((REGNO (XEXP (link, 0))
+ + (REGNO (XEXP (link, 0)) >= FIRST_PSEUDO_REGISTER ? 1
+ : HARD_REGNO_NREGS (REGNO (XEXP (link, 0)),
+ GET_MODE (XEXP (link, 0)))))
+ > regno))
+ return link;
+ return 0;
+}
+
+/* Return true if DATUM, or any overlap of DATUM, of kind CODE is found
+ in the CALL_INSN_FUNCTION_USAGE information of INSN. */
+
+int
+find_reg_fusage (insn, code, datum)
+ rtx insn;
+ enum rtx_code code;
+ rtx datum;
+{
+ /* If it's not a CALL_INSN, it can't possibly have a
+ CALL_INSN_FUNCTION_USAGE field, so don't bother checking. */
+ if (GET_CODE (insn) != CALL_INSN)
+ return 0;
+
+ if (! datum)
+ abort();
+
+ if (GET_CODE (datum) != REG)
+ {
+ register rtx link;
+
+ for (link = CALL_INSN_FUNCTION_USAGE (insn);
+ link;
+ link = XEXP (link, 1))
+ if (GET_CODE (XEXP (link, 0)) == code
+ && rtx_equal_p (datum, SET_DEST (XEXP (link, 0))))
+ return 1;
+ }
+ else
+ {
+ register int regno = REGNO (datum);
+
+ /* CALL_INSN_FUNCTION_USAGE information cannot contain references
+ to pseudo registers, so don't bother checking. */
+
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ int end_regno = regno + HARD_REGNO_NREGS (regno, GET_MODE (datum));
+ int i;
+
+ for (i = regno; i < end_regno; i++)
+ if (find_regno_fusage (insn, code, i))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Return true if REGNO, or any overlap of REGNO, of kind CODE is found
+ in the CALL_INSN_FUNCTION_USAGE information of INSN. */
+
+int
+find_regno_fusage (insn, code, regno)
+ rtx insn;
+ enum rtx_code code;
+ int regno;
+{
+ register rtx link;
+
+ /* CALL_INSN_FUNCTION_USAGE information cannot contain references
+ to pseudo registers, so don't bother checking. */
+
+ if (regno >= FIRST_PSEUDO_REGISTER
+ || GET_CODE (insn) != CALL_INSN )
+ return 0;
+
+ for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
+ {
+ register int regnote;
+ register rtx op;
+
+ if (GET_CODE (op = XEXP (link, 0)) == code
+ && GET_CODE (SET_DEST (op)) == REG
+ && (regnote = REGNO (SET_DEST (op))) <= regno
+ && regnote
+ + HARD_REGNO_NREGS (regnote, GET_MODE (SET_DEST (op)))
+ > regno)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Remove register note NOTE from the REG_NOTES of INSN. */
+
+void
+remove_note (insn, note)
+ register rtx note;
+ register rtx insn;
+{
+ register rtx link;
+
+ if (REG_NOTES (insn) == note)
+ {
+ REG_NOTES (insn) = XEXP (note, 1);
+ return;
+ }
+
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+ if (XEXP (link, 1) == note)
+ {
+ XEXP (link, 1) = XEXP (note, 1);
+ return;
+ }
+
+ abort ();
+}
+
+/* Nonzero if X contains any volatile instructions. These are instructions
+ which may cause unpredictable machine state instructions, and thus no
+ instructions should be moved or combined across them. This includes
+ only volatile asms and UNSPEC_VOLATILE instructions. */
+
+int
+volatile_insn_p (x)
+ rtx x;
+{
+ register RTX_CODE code;
+
+ code = GET_CODE (x);
+ switch (code)
+ {
+ case LABEL_REF:
+ case SYMBOL_REF:
+ case CONST_INT:
+ case CONST:
+ case CONST_DOUBLE:
+ case CC0:
+ case PC:
+ case REG:
+ case SCRATCH:
+ case CLOBBER:
+ case ASM_INPUT:
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ case CALL:
+ case MEM:
+ return 0;
+
+ case UNSPEC_VOLATILE:
+ /* case TRAP_IF: This isn't clear yet. */
+ return 1;
+
+ case ASM_OPERANDS:
+ if (MEM_VOLATILE_P (x))
+ return 1;
+ }
+
+ /* Recursively scan the operands of this expression. */
+
+ {
+ register char *fmt = GET_RTX_FORMAT (code);
+ register int i;
+
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ {
+ if (volatile_insn_p (XEXP (x, i)))
+ return 1;
+ }
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ if (volatile_insn_p (XVECEXP (x, i, j)))
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Nonzero if X contains any volatile memory references
+ UNSPEC_VOLATILE operations or volatile ASM_OPERANDS expressions. */
+
+int
+volatile_refs_p (x)
+ rtx x;
+{
+ register RTX_CODE code;
+
+ code = GET_CODE (x);
+ switch (code)
+ {
+ case LABEL_REF:
+ case SYMBOL_REF:
+ case CONST_INT:
+ case CONST:
+ case CONST_DOUBLE:
+ case CC0:
+ case PC:
+ case REG:
+ case SCRATCH:
+ case CLOBBER:
+ case ASM_INPUT:
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ return 0;
+
+ case CALL:
+ case UNSPEC_VOLATILE:
+ /* case TRAP_IF: This isn't clear yet. */
+ return 1;
+
+ case MEM:
+ case ASM_OPERANDS:
+ if (MEM_VOLATILE_P (x))
+ return 1;
+ }
+
+ /* Recursively scan the operands of this expression. */
+
+ {
+ register char *fmt = GET_RTX_FORMAT (code);
+ register int i;
+
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ {
+ if (volatile_refs_p (XEXP (x, i)))
+ return 1;
+ }
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ if (volatile_refs_p (XVECEXP (x, i, j)))
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Similar to above, except that it also rejects register pre- and post-
+ incrementing. */
+
+int
+side_effects_p (x)
+ rtx x;
+{
+ register RTX_CODE code;
+
+ code = GET_CODE (x);
+ switch (code)
+ {
+ case LABEL_REF:
+ case SYMBOL_REF:
+ case CONST_INT:
+ case CONST:
+ case CONST_DOUBLE:
+ case CC0:
+ case PC:
+ case REG:
+ case SCRATCH:
+ case ASM_INPUT:
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ return 0;
+
+ case CLOBBER:
+ /* Reject CLOBBER with a non-VOID mode. These are made by combine.c
+ when some combination can't be done. If we see one, don't think
+ that we can simplify the expression. */
+ return (GET_MODE (x) != VOIDmode);
+
+ case PRE_INC:
+ case PRE_DEC:
+ case POST_INC:
+ case POST_DEC:
+ case CALL:
+ case UNSPEC_VOLATILE:
+ /* case TRAP_IF: This isn't clear yet. */
+ return 1;
+
+ case MEM:
+ case ASM_OPERANDS:
+ if (MEM_VOLATILE_P (x))
+ return 1;
+ }
+
+ /* Recursively scan the operands of this expression. */
+
+ {
+ register char *fmt = GET_RTX_FORMAT (code);
+ register int i;
+
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ {
+ if (side_effects_p (XEXP (x, i)))
+ return 1;
+ }
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ if (side_effects_p (XVECEXP (x, i, j)))
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Return nonzero if evaluating rtx X might cause a trap. */
+
+int
+may_trap_p (x)
+ rtx x;
+{
+ int i;
+ enum rtx_code code;
+ char *fmt;
+
+ if (x == 0)
+ return 0;
+ code = GET_CODE (x);
+ switch (code)
+ {
+ /* Handle these cases quickly. */
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case CONST:
+ case PC:
+ case CC0:
+ case REG:
+ case SCRATCH:
+ return 0;
+
+ /* Conditional trap can trap! */
+ case UNSPEC_VOLATILE:
+ case TRAP_IF:
+ return 1;
+
+ /* Memory ref can trap unless it's a static var or a stack slot. */
+ case MEM:
+ return rtx_addr_can_trap_p (XEXP (x, 0));
+
+ /* Division by a non-constant might trap. */
+ case DIV:
+ case MOD:
+ case UDIV:
+ case UMOD:
+ if (! CONSTANT_P (XEXP (x, 1)))
+ return 1;
+ /* This was const0_rtx, but by not using that,
+ we can link this file into other programs. */
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) == 0)
+ return 1;
+ default:
+ /* Any floating arithmetic may trap. */
+ if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+ return 1;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ {
+ if (may_trap_p (XEXP (x, i)))
+ return 1;
+ }
+ else if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ if (may_trap_p (XVECEXP (x, i, j)))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Return nonzero if X contains a comparison that is not either EQ or NE,
+ i.e., an inequality. */
+
+int
+inequality_comparisons_p (x)
+ rtx x;
+{
+ register char *fmt;
+ register int len, i;
+ register enum rtx_code code = GET_CODE (x);
+
+ switch (code)
+ {
+ case REG:
+ case SCRATCH:
+ case PC:
+ case CC0:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ return 0;
+
+ case LT:
+ case LTU:
+ case GT:
+ case GTU:
+ case LE:
+ case LEU:
+ case GE:
+ case GEU:
+ return 1;
+ }
+
+ len = GET_RTX_LENGTH (code);
+ fmt = GET_RTX_FORMAT (code);
+
+ for (i = 0; i < len; i++)
+ {
+ if (fmt[i] == 'e')
+ {
+ if (inequality_comparisons_p (XEXP (x, i)))
+ return 1;
+ }
+ else if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (inequality_comparisons_p (XVECEXP (x, i, j)))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Replace any occurrence of FROM in X with TO.
+
+ Note that copying is not done so X must not be shared unless all copies
+ are to be modified. */
+
+rtx
+replace_rtx (x, from, to)
+ rtx x, from, to;
+{
+ register int i, j;
+ register char *fmt;
+
+ if (x == from)
+ return to;
+
+ /* Allow this function to make replacements in EXPR_LISTs. */
+ if (x == 0)
+ return 0;
+
+ fmt = GET_RTX_FORMAT (GET_CODE (x));
+ for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ XEXP (x, i) = replace_rtx (XEXP (x, i), from, to);
+ else if (fmt[i] == 'E')
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ XVECEXP (x, i, j) = replace_rtx (XVECEXP (x, i, j), from, to);
+ }
+
+ return x;
+}
+
+/* Throughout the rtx X, replace many registers according to REG_MAP.
+ Return the replacement for X (which may be X with altered contents).
+ REG_MAP[R] is the replacement for register R, or 0 for don't replace.
+ NREGS is the length of REG_MAP; regs >= NREGS are not mapped.
+
+ We only support REG_MAP entries of REG or SUBREG. Also, hard registers
+ should not be mapped to pseudos or vice versa since validate_change
+ is not called.
+
+ If REPLACE_DEST is 1, replacements are also done in destinations;
+ otherwise, only sources are replaced. */
+
+rtx
+replace_regs (x, reg_map, nregs, replace_dest)
+ rtx x;
+ rtx *reg_map;
+ int nregs;
+ int replace_dest;
+{
+ register enum rtx_code code;
+ register int i;
+ register char *fmt;
+
+ if (x == 0)
+ return x;
+
+ code = GET_CODE (x);
+ switch (code)
+ {
+ case SCRATCH:
+ case PC:
+ case CC0:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return x;
+
+ case REG:
+ /* Verify that the register has an entry before trying to access it. */
+ if (REGNO (x) < nregs && reg_map[REGNO (x)] != 0)
+ {
+ /* SUBREGs can't be shared. Always return a copy to ensure that if
+ this replacement occurs more than once then each instance will
+ get distinct rtx. */
+ if (GET_CODE (reg_map[REGNO (x)]) == SUBREG)
+ return copy_rtx (reg_map[REGNO (x)]);
+ return reg_map[REGNO (x)];
+ }
+ return x;
+
+ case SUBREG:
+ /* Prevent making nested SUBREGs. */
+ if (GET_CODE (SUBREG_REG (x)) == REG && REGNO (SUBREG_REG (x)) < nregs
+ && reg_map[REGNO (SUBREG_REG (x))] != 0
+ && GET_CODE (reg_map[REGNO (SUBREG_REG (x))]) == SUBREG)
+ {
+ rtx map_val = reg_map[REGNO (SUBREG_REG (x))];
+ rtx map_inner = SUBREG_REG (map_val);
+
+ if (GET_MODE (x) == GET_MODE (map_inner))
+ return map_inner;
+ else
+ {
+ /* We cannot call gen_rtx here since we may be linked with
+ genattrtab.c. */
+ /* Let's try clobbering the incoming SUBREG and see
+ if this is really safe. */
+ SUBREG_REG (x) = map_inner;
+ SUBREG_WORD (x) += SUBREG_WORD (map_val);
+ return x;
+#if 0
+ rtx new = rtx_alloc (SUBREG);
+ PUT_MODE (new, GET_MODE (x));
+ SUBREG_REG (new) = map_inner;
+ SUBREG_WORD (new) = SUBREG_WORD (x) + SUBREG_WORD (map_val);
+#endif
+ }
+ }
+ break;
+
+ case SET:
+ if (replace_dest)
+ SET_DEST (x) = replace_regs (SET_DEST (x), reg_map, nregs, 0);
+
+ else if (GET_CODE (SET_DEST (x)) == MEM
+ || GET_CODE (SET_DEST (x)) == STRICT_LOW_PART)
+ /* Even if we are not to replace destinations, replace register if it
+ is CONTAINED in destination (destination is memory or
+ STRICT_LOW_PART). */
+ XEXP (SET_DEST (x), 0) = replace_regs (XEXP (SET_DEST (x), 0),
+ reg_map, nregs, 0);
+ else if (GET_CODE (SET_DEST (x)) == ZERO_EXTRACT)
+ /* Similarly, for ZERO_EXTRACT we replace all operands. */
+ break;
+
+ SET_SRC (x) = replace_regs (SET_SRC (x), reg_map, nregs, 0);
+ return x;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ XEXP (x, i) = replace_regs (XEXP (x, i), reg_map, nregs, replace_dest);
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ XVECEXP (x, i, j) = replace_regs (XVECEXP (x, i, j), reg_map,
+ nregs, replace_dest);
+ }
+ }
+ return x;
+}
diff --git a/gnu/usr.bin/cc/cc_int/sched.c b/gnu/usr.bin/cc/cc_int/sched.c
new file mode 100644
index 0000000..9870967
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/sched.c
@@ -0,0 +1,4884 @@
+/* Instruction scheduling pass.
+ Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
+ Contributed by Michael Tiemann (tiemann@cygnus.com)
+ Enhanced by, and currently maintained by, Jim Wilson (wilson@cygnus.com)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Instruction scheduling pass.
+
+ This pass implements list scheduling within basic blocks. It is
+ run after flow analysis, but before register allocation. The
+ scheduler works as follows:
+
+ We compute insn priorities based on data dependencies. Flow
+ analysis only creates a fraction of the data-dependencies we must
+ observe: namely, only those dependencies which the combiner can be
+ expected to use. For this pass, we must therefore create the
+ remaining dependencies we need to observe: register dependencies,
+ memory dependencies, dependencies to keep function calls in order,
+ and the dependence between a conditional branch and the setting of
+ condition codes are all dealt with here.
+
+ The scheduler first traverses the data flow graph, starting with
+ the last instruction, and proceeding to the first, assigning
+ values to insn_priority as it goes. This sorts the instructions
+ topologically by data dependence.
+
+ Once priorities have been established, we order the insns using
+ list scheduling. This works as follows: starting with a list of
+ all the ready insns, and sorted according to priority number, we
+ schedule the insn from the end of the list by placing its
+ predecessors in the list according to their priority order. We
+ consider this insn scheduled by setting the pointer to the "end" of
+ the list to point to the previous insn. When an insn has no
+ predecessors, we either queue it until sufficient time has elapsed
+ or add it to the ready list. As the instructions are scheduled or
+ when stalls are introduced, the queue advances and dumps insns into
+ the ready list. When all insns down to the lowest priority have
+ been scheduled, the critical path of the basic block has been made
+ as short as possible. The remaining insns are then scheduled in
+ remaining slots.
+
+ Function unit conflicts are resolved during reverse list scheduling
+ by tracking the time when each insn is committed to the schedule
+ and from that, the time the function units it uses must be free.
+ As insns on the ready list are considered for scheduling, those
+ that would result in a blockage of the already committed insns are
+ queued until no blockage will result. Among the remaining insns on
+ the ready list to be considered, the first one with the largest
+ potential for causing a subsequent blockage is chosen.
+
+ The following list shows the order in which we want to break ties
+ among insns in the ready list:
+
+ 1. choose insn with lowest conflict cost, ties broken by
+ 2. choose insn with the longest path to end of bb, ties broken by
+ 3. choose insn that kills the most registers, ties broken by
+ 4. choose insn that conflicts with the most ready insns, or finally
+ 5. choose insn with lowest UID.
+
+ Memory references complicate matters. Only if we can be certain
+ that memory references are not part of the data dependency graph
+ (via true, anti, or output dependence), can we move operations past
+ memory references. To first approximation, reads can be done
+ independently, while writes introduce dependencies. Better
+ approximations will yield fewer dependencies.
+
+ Dependencies set up by memory references are treated in exactly the
+ same way as other dependencies, by using LOG_LINKS.
+
+ Having optimized the critical path, we may have also unduly
+ extended the lifetimes of some registers. If an operation requires
+ that constants be loaded into registers, it is certainly desirable
+ to load those constants as early as necessary, but no earlier.
+ I.e., it will not do to load up a bunch of registers at the
+ beginning of a basic block only to use them at the end, if they
+ could be loaded later, since this may result in excessive register
+ utilization.
+
+ Note that since branches are never in basic blocks, but only end
+ basic blocks, this pass will not do any branch scheduling. But
+ that is ok, since we can use GNU's delayed branch scheduling
+ pass to take care of this case.
+
+ Also note that no further optimizations based on algebraic identities
+ are performed, so this pass would be a good one to perform instruction
+ splitting, such as breaking up a multiply instruction into shifts
+ and adds where that is profitable.
+
+ Given the memory aliasing analysis that this pass should perform,
+ it should be possible to remove redundant stores to memory, and to
+ load values from registers instead of hitting memory.
+
+ This pass must update information that subsequent passes expect to be
+ correct. Namely: reg_n_refs, reg_n_sets, reg_n_deaths,
+ reg_n_calls_crossed, and reg_live_length. Also, basic_block_head,
+ basic_block_end.
+
+ The information in the line number notes is carefully retained by this
+ pass. All other NOTE insns are grouped in their same relative order at
+ the beginning of basic blocks that have been scheduled. */
+
+#include <stdio.h>
+#include "config.h"
+#include "rtl.h"
+#include "basic-block.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "flags.h"
+#include "insn-config.h"
+#include "insn-attr.h"
+
+#ifdef INSN_SCHEDULING
+/* Arrays set up by scheduling for the same respective purposes as
+ similar-named arrays set up by flow analysis. We work with these
+ arrays during the scheduling pass so we can compare values against
+ unscheduled code.
+
+ Values of these arrays are copied at the end of this pass into the
+ arrays set up by flow analysis. */
+static short *sched_reg_n_deaths;
+static int *sched_reg_n_calls_crossed;
+static int *sched_reg_live_length;
+
+/* Element N is the next insn that sets (hard or pseudo) register
+ N within the current basic block; or zero, if there is no
+ such insn. Needed for new registers which may be introduced
+ by splitting insns. */
+static rtx *reg_last_uses;
+static rtx *reg_last_sets;
+static regset reg_pending_sets;
+static int reg_pending_sets_all;
+
+/* Vector indexed by INSN_UID giving the original ordering of the insns. */
+static int *insn_luid;
+#define INSN_LUID(INSN) (insn_luid[INSN_UID (INSN)])
+
+/* Vector indexed by INSN_UID giving each instruction a priority. */
+static int *insn_priority;
+#define INSN_PRIORITY(INSN) (insn_priority[INSN_UID (INSN)])
+
+static short *insn_costs;
+#define INSN_COST(INSN) insn_costs[INSN_UID (INSN)]
+
+/* Vector indexed by INSN_UID giving an encoding of the function units
+ used. */
+static short *insn_units;
+#define INSN_UNIT(INSN) insn_units[INSN_UID (INSN)]
+
+/* Vector indexed by INSN_UID giving an encoding of the blockage range
+ function. The unit and the range are encoded. */
+static unsigned int *insn_blockage;
+#define INSN_BLOCKAGE(INSN) insn_blockage[INSN_UID (INSN)]
+#define UNIT_BITS 5
+#define BLOCKAGE_MASK ((1 << BLOCKAGE_BITS) - 1)
+#define ENCODE_BLOCKAGE(U,R) \
+ ((((U) << UNIT_BITS) << BLOCKAGE_BITS \
+ | MIN_BLOCKAGE_COST (R)) << BLOCKAGE_BITS \
+ | MAX_BLOCKAGE_COST (R))
+#define UNIT_BLOCKED(B) ((B) >> (2 * BLOCKAGE_BITS))
+#define BLOCKAGE_RANGE(B) \
+ (((((B) >> BLOCKAGE_BITS) & BLOCKAGE_MASK) << (HOST_BITS_PER_INT / 2)) \
+ | (B) & BLOCKAGE_MASK)
+
+/* Encodings of the `<name>_unit_blockage_range' function. */
+#define MIN_BLOCKAGE_COST(R) ((R) >> (HOST_BITS_PER_INT / 2))
+#define MAX_BLOCKAGE_COST(R) ((R) & ((1 << (HOST_BITS_PER_INT / 2)) - 1))
+
+#define DONE_PRIORITY -1
+#define MAX_PRIORITY 0x7fffffff
+#define TAIL_PRIORITY 0x7ffffffe
+#define LAUNCH_PRIORITY 0x7f000001
+#define DONE_PRIORITY_P(INSN) (INSN_PRIORITY (INSN) < 0)
+#define LOW_PRIORITY_P(INSN) ((INSN_PRIORITY (INSN) & 0x7f000000) == 0)
+
+/* Vector indexed by INSN_UID giving number of insns referring to this insn. */
+static int *insn_ref_count;
+#define INSN_REF_COUNT(INSN) (insn_ref_count[INSN_UID (INSN)])
+
+/* Vector indexed by INSN_UID giving line-number note in effect for each
+ insn. For line-number notes, this indicates whether the note may be
+ reused. */
+static rtx *line_note;
+#define LINE_NOTE(INSN) (line_note[INSN_UID (INSN)])
+
+/* Vector indexed by basic block number giving the starting line-number
+ for each basic block. */
+static rtx *line_note_head;
+
+/* List of important notes we must keep around. This is a pointer to the
+ last element in the list. */
+static rtx note_list;
+
+/* Regsets telling whether a given register is live or dead before the last
+ scheduled insn. Must scan the instructions once before scheduling to
+ determine what registers are live or dead at the end of the block. */
+static regset bb_dead_regs;
+static regset bb_live_regs;
+
+/* Regset telling whether a given register is live after the insn currently
+ being scheduled. Before processing an insn, this is equal to bb_live_regs
+ above. This is used so that we can find registers that are newly born/dead
+ after processing an insn. */
+static regset old_live_regs;
+
+/* The chain of REG_DEAD notes. REG_DEAD notes are removed from all insns
+ during the initial scan and reused later. If there are not exactly as
+ many REG_DEAD notes in the post scheduled code as there were in the
+ prescheduled code then we trigger an abort because this indicates a bug. */
+static rtx dead_notes;
+
+/* Queues, etc. */
+
+/* An instruction is ready to be scheduled when all insns following it
+ have already been scheduled. It is important to ensure that all
+ insns which use its result will not be executed until its result
+ has been computed. An insn is maintained in one of four structures:
+
+ (P) the "Pending" set of insns which cannot be scheduled until
+ their dependencies have been satisfied.
+ (Q) the "Queued" set of insns that can be scheduled when sufficient
+ time has passed.
+ (R) the "Ready" list of unscheduled, uncommitted insns.
+ (S) the "Scheduled" list of insns.
+
+ Initially, all insns are either "Pending" or "Ready" depending on
+ whether their dependencies are satisfied.
+
+ Insns move from the "Ready" list to the "Scheduled" list as they
+ are committed to the schedule. As this occurs, the insns in the
+ "Pending" list have their dependencies satisfied and move to either
+ the "Ready" list or the "Queued" set depending on whether
+ sufficient time has passed to make them ready. As time passes,
+ insns move from the "Queued" set to the "Ready" list. Insns may
+ move from the "Ready" list to the "Queued" set if they are blocked
+ due to a function unit conflict.
+
+ The "Pending" list (P) are the insns in the LOG_LINKS of the unscheduled
+ insns, i.e., those that are ready, queued, and pending.
+ The "Queued" set (Q) is implemented by the variable `insn_queue'.
+ The "Ready" list (R) is implemented by the variables `ready' and
+ `n_ready'.
+ The "Scheduled" list (S) is the new insn chain built by this pass.
+
+ The transition (R->S) is implemented in the scheduling loop in
+ `schedule_block' when the best insn to schedule is chosen.
+ The transition (R->Q) is implemented in `schedule_select' when an
+ insn is found to to have a function unit conflict with the already
+ committed insns.
+ The transitions (P->R and P->Q) are implemented in `schedule_insn' as
+ insns move from the ready list to the scheduled list.
+ The transition (Q->R) is implemented at the top of the scheduling
+ loop in `schedule_block' as time passes or stalls are introduced. */
+
+/* Implement a circular buffer to delay instructions until sufficient
+ time has passed. INSN_QUEUE_SIZE is a power of two larger than
+ MAX_BLOCKAGE and MAX_READY_COST computed by genattr.c. This is the
+ longest time an isnsn may be queued. */
+static rtx insn_queue[INSN_QUEUE_SIZE];
+static int q_ptr = 0;
+static int q_size = 0;
+#define NEXT_Q(X) (((X)+1) & (INSN_QUEUE_SIZE-1))
+#define NEXT_Q_AFTER(X,C) (((X)+C) & (INSN_QUEUE_SIZE-1))
+
+/* Vector indexed by INSN_UID giving the minimum clock tick at which
+ the insn becomes ready. This is used to note timing constraints for
+ insns in the pending list. */
+static int *insn_tick;
+#define INSN_TICK(INSN) (insn_tick[INSN_UID (INSN)])
+
+/* Data structure for keeping track of register information
+ during that register's life. */
+
+struct sometimes
+{
+ short offset; short bit;
+ short live_length; short calls_crossed;
+};
+
+/* Forward declarations. */
+static rtx canon_rtx PROTO((rtx));
+static int rtx_equal_for_memref_p PROTO((rtx, rtx));
+static rtx find_symbolic_term PROTO((rtx));
+static int memrefs_conflict_p PROTO((int, rtx, int, rtx,
+ HOST_WIDE_INT));
+static void add_dependence PROTO((rtx, rtx, enum reg_note));
+static void remove_dependence PROTO((rtx, rtx));
+static rtx find_insn_list PROTO((rtx, rtx));
+static int insn_unit PROTO((rtx));
+static unsigned int blockage_range PROTO((int, rtx));
+static void clear_units PROTO((void));
+static void prepare_unit PROTO((int));
+static int actual_hazard_this_instance PROTO((int, int, rtx, int, int));
+static void schedule_unit PROTO((int, rtx, int));
+static int actual_hazard PROTO((int, rtx, int, int));
+static int potential_hazard PROTO((int, rtx, int));
+static int insn_cost PROTO((rtx, rtx, rtx));
+static int priority PROTO((rtx));
+static void free_pending_lists PROTO((void));
+static void add_insn_mem_dependence PROTO((rtx *, rtx *, rtx, rtx));
+static void flush_pending_lists PROTO((rtx));
+static void sched_analyze_1 PROTO((rtx, rtx));
+static void sched_analyze_2 PROTO((rtx, rtx));
+static void sched_analyze_insn PROTO((rtx, rtx));
+static int sched_analyze PROTO((rtx, rtx));
+static void sched_note_set PROTO((int, rtx, int));
+static int rank_for_schedule PROTO((rtx *, rtx *));
+static void swap_sort PROTO((rtx *, int));
+static void queue_insn PROTO((rtx, int));
+static int birthing_insn PROTO((rtx));
+static void adjust_priority PROTO((rtx));
+static int schedule_insn PROTO((rtx, rtx *, int, int));
+static int schedule_select PROTO((rtx *, int, int, FILE *));
+static void create_reg_dead_note PROTO((rtx, rtx));
+static void attach_deaths PROTO((rtx, rtx, int));
+static void attach_deaths_insn PROTO((rtx));
+static rtx unlink_notes PROTO((rtx, rtx));
+static int new_sometimes_live PROTO((struct sometimes *, int, int,
+ int));
+static void finish_sometimes_live PROTO((struct sometimes *, int));
+static void schedule_block PROTO((int, FILE *));
+static rtx regno_use_in PROTO((int, rtx));
+static void split_hard_reg_notes PROTO((rtx, rtx, rtx, rtx));
+static void new_insn_dead_notes PROTO((rtx, rtx, rtx, rtx));
+static void update_n_sets PROTO((rtx, int));
+static void update_flow_info PROTO((rtx, rtx, rtx, rtx));
+
+/* Main entry point of this file. */
+void schedule_insns PROTO((FILE *));
+
+#endif /* INSN_SCHEDULING */
+
+#define SIZE_FOR_MODE(X) (GET_MODE_SIZE (GET_MODE (X)))
+
+/* Vector indexed by N giving the initial (unchanging) value known
+ for pseudo-register N. */
+static rtx *reg_known_value;
+
+/* Vector recording for each reg_known_value whether it is due to a
+ REG_EQUIV note. Future passes (viz., reload) may replace the
+ pseudo with the equivalent expression and so we account for the
+ dependences that would be introduced if that happens. */
+/* ??? This is a problem only on the Convex. The REG_EQUIV notes created in
+ assign_parms mention the arg pointer, and there are explicit insns in the
+ RTL that modify the arg pointer. Thus we must ensure that such insns don't
+ get scheduled across each other because that would invalidate the REG_EQUIV
+ notes. One could argue that the REG_EQUIV notes are wrong, but solving
+ the problem in the scheduler will likely give better code, so we do it
+ here. */
+static char *reg_known_equiv_p;
+
+/* Indicates number of valid entries in reg_known_value. */
+static int reg_known_value_size;
+
+static rtx
+canon_rtx (x)
+ rtx x;
+{
+ if (GET_CODE (x) == REG && REGNO (x) >= FIRST_PSEUDO_REGISTER
+ && REGNO (x) <= reg_known_value_size)
+ return reg_known_value[REGNO (x)];
+ else if (GET_CODE (x) == PLUS)
+ {
+ rtx x0 = canon_rtx (XEXP (x, 0));
+ rtx x1 = canon_rtx (XEXP (x, 1));
+
+ if (x0 != XEXP (x, 0) || x1 != XEXP (x, 1))
+ {
+ /* We can tolerate LO_SUMs being offset here; these
+ rtl are used for nothing other than comparisons. */
+ if (GET_CODE (x0) == CONST_INT)
+ return plus_constant_for_output (x1, INTVAL (x0));
+ else if (GET_CODE (x1) == CONST_INT)
+ return plus_constant_for_output (x0, INTVAL (x1));
+ return gen_rtx (PLUS, GET_MODE (x), x0, x1);
+ }
+ }
+ return x;
+}
+
+/* Set up all info needed to perform alias analysis on memory references. */
+
+void
+init_alias_analysis ()
+{
+ int maxreg = max_reg_num ();
+ rtx insn;
+ rtx note;
+ rtx set;
+
+ reg_known_value_size = maxreg;
+
+ reg_known_value
+ = (rtx *) oballoc ((maxreg-FIRST_PSEUDO_REGISTER) * sizeof (rtx))
+ - FIRST_PSEUDO_REGISTER;
+ bzero ((char *) (reg_known_value + FIRST_PSEUDO_REGISTER),
+ (maxreg-FIRST_PSEUDO_REGISTER) * sizeof (rtx));
+
+ reg_known_equiv_p
+ = (char *) oballoc ((maxreg -FIRST_PSEUDO_REGISTER) * sizeof (char))
+ - FIRST_PSEUDO_REGISTER;
+ bzero (reg_known_equiv_p + FIRST_PSEUDO_REGISTER,
+ (maxreg - FIRST_PSEUDO_REGISTER) * sizeof (char));
+
+ /* Fill in the entries with known constant values. */
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ if ((set = single_set (insn)) != 0
+ && GET_CODE (SET_DEST (set)) == REG
+ && REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER
+ && (((note = find_reg_note (insn, REG_EQUAL, 0)) != 0
+ && reg_n_sets[REGNO (SET_DEST (set))] == 1)
+ || (note = find_reg_note (insn, REG_EQUIV, NULL_RTX)) != 0)
+ && GET_CODE (XEXP (note, 0)) != EXPR_LIST)
+ {
+ int regno = REGNO (SET_DEST (set));
+ reg_known_value[regno] = XEXP (note, 0);
+ reg_known_equiv_p[regno] = REG_NOTE_KIND (note) == REG_EQUIV;
+ }
+
+ /* Fill in the remaining entries. */
+ while (--maxreg >= FIRST_PSEUDO_REGISTER)
+ if (reg_known_value[maxreg] == 0)
+ reg_known_value[maxreg] = regno_reg_rtx[maxreg];
+}
+
+/* Return 1 if X and Y are identical-looking rtx's.
+
+ We use the data in reg_known_value above to see if two registers with
+ different numbers are, in fact, equivalent. */
+
+static int
+rtx_equal_for_memref_p (x, y)
+ rtx x, y;
+{
+ register int i;
+ register int j;
+ register enum rtx_code code;
+ register char *fmt;
+
+ if (x == 0 && y == 0)
+ return 1;
+ if (x == 0 || y == 0)
+ return 0;
+ x = canon_rtx (x);
+ y = canon_rtx (y);
+
+ if (x == y)
+ return 1;
+
+ code = GET_CODE (x);
+ /* Rtx's of different codes cannot be equal. */
+ if (code != GET_CODE (y))
+ return 0;
+
+ /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent.
+ (REG:SI x) and (REG:HI x) are NOT equivalent. */
+
+ if (GET_MODE (x) != GET_MODE (y))
+ return 0;
+
+ /* REG, LABEL_REF, and SYMBOL_REF can be compared nonrecursively. */
+
+ if (code == REG)
+ return REGNO (x) == REGNO (y);
+ if (code == LABEL_REF)
+ return XEXP (x, 0) == XEXP (y, 0);
+ if (code == SYMBOL_REF)
+ return XSTR (x, 0) == XSTR (y, 0);
+
+ /* For commutative operations, the RTX match if the operand match in any
+ order. Also handle the simple binary and unary cases without a loop. */
+ if (code == EQ || code == NE || GET_RTX_CLASS (code) == 'c')
+ return ((rtx_equal_for_memref_p (XEXP (x, 0), XEXP (y, 0))
+ && rtx_equal_for_memref_p (XEXP (x, 1), XEXP (y, 1)))
+ || (rtx_equal_for_memref_p (XEXP (x, 0), XEXP (y, 1))
+ && rtx_equal_for_memref_p (XEXP (x, 1), XEXP (y, 0))));
+ else if (GET_RTX_CLASS (code) == '<' || GET_RTX_CLASS (code) == '2')
+ return (rtx_equal_for_memref_p (XEXP (x, 0), XEXP (y, 0))
+ && rtx_equal_for_memref_p (XEXP (x, 1), XEXP (y, 1)));
+ else if (GET_RTX_CLASS (code) == '1')
+ return rtx_equal_for_memref_p (XEXP (x, 0), XEXP (y, 0));
+
+ /* Compare the elements. If any pair of corresponding elements
+ fail to match, return 0 for the whole things. */
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ switch (fmt[i])
+ {
+ case 'w':
+ if (XWINT (x, i) != XWINT (y, i))
+ return 0;
+ break;
+
+ case 'n':
+ case 'i':
+ if (XINT (x, i) != XINT (y, i))
+ return 0;
+ break;
+
+ case 'V':
+ case 'E':
+ /* Two vectors must have the same length. */
+ if (XVECLEN (x, i) != XVECLEN (y, i))
+ return 0;
+
+ /* And the corresponding elements must match. */
+ for (j = 0; j < XVECLEN (x, i); j++)
+ if (rtx_equal_for_memref_p (XVECEXP (x, i, j), XVECEXP (y, i, j)) == 0)
+ return 0;
+ break;
+
+ case 'e':
+ if (rtx_equal_for_memref_p (XEXP (x, i), XEXP (y, i)) == 0)
+ return 0;
+ break;
+
+ case 'S':
+ case 's':
+ if (strcmp (XSTR (x, i), XSTR (y, i)))
+ return 0;
+ break;
+
+ case 'u':
+ /* These are just backpointers, so they don't matter. */
+ break;
+
+ case '0':
+ break;
+
+ /* It is believed that rtx's at this level will never
+ contain anything but integers and other rtx's,
+ except for within LABEL_REFs and SYMBOL_REFs. */
+ default:
+ abort ();
+ }
+ }
+ return 1;
+}
+
+/* Given an rtx X, find a SYMBOL_REF or LABEL_REF within
+ X and return it, or return 0 if none found. */
+
+static rtx
+find_symbolic_term (x)
+ rtx x;
+{
+ register int i;
+ register enum rtx_code code;
+ register char *fmt;
+
+ code = GET_CODE (x);
+ if (code == SYMBOL_REF || code == LABEL_REF)
+ return x;
+ if (GET_RTX_CLASS (code) == 'o')
+ return 0;
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ rtx t;
+
+ if (fmt[i] == 'e')
+ {
+ t = find_symbolic_term (XEXP (x, i));
+ if (t != 0)
+ return t;
+ }
+ else if (fmt[i] == 'E')
+ break;
+ }
+ return 0;
+}
+
+/* Return nonzero if X and Y (memory addresses) could reference the
+ same location in memory. C is an offset accumulator. When
+ C is nonzero, we are testing aliases between X and Y + C.
+ XSIZE is the size in bytes of the X reference,
+ similarly YSIZE is the size in bytes for Y.
+
+ If XSIZE or YSIZE is zero, we do not know the amount of memory being
+ referenced (the reference was BLKmode), so make the most pessimistic
+ assumptions.
+
+ We recognize the following cases of non-conflicting memory:
+
+ (1) addresses involving the frame pointer cannot conflict
+ with addresses involving static variables.
+ (2) static variables with different addresses cannot conflict.
+
+ Nice to notice that varying addresses cannot conflict with fp if no
+ local variables had their addresses taken, but that's too hard now. */
+
+/* ??? In Fortran, references to a array parameter can never conflict with
+ another array parameter. */
+
+static int
+memrefs_conflict_p (xsize, x, ysize, y, c)
+ rtx x, y;
+ int xsize, ysize;
+ HOST_WIDE_INT c;
+{
+ if (GET_CODE (x) == HIGH)
+ x = XEXP (x, 0);
+ else if (GET_CODE (x) == LO_SUM)
+ x = XEXP (x, 1);
+ else
+ x = canon_rtx (x);
+ if (GET_CODE (y) == HIGH)
+ y = XEXP (y, 0);
+ else if (GET_CODE (y) == LO_SUM)
+ y = XEXP (y, 1);
+ else
+ y = canon_rtx (y);
+
+ if (rtx_equal_for_memref_p (x, y))
+ return (xsize == 0 || ysize == 0 ||
+ (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0));
+
+ if (y == frame_pointer_rtx || y == hard_frame_pointer_rtx
+ || y == stack_pointer_rtx)
+ {
+ rtx t = y;
+ int tsize = ysize;
+ y = x; ysize = xsize;
+ x = t; xsize = tsize;
+ }
+
+ if (x == frame_pointer_rtx || x == hard_frame_pointer_rtx
+ || x == stack_pointer_rtx)
+ {
+ rtx y1;
+
+ if (CONSTANT_P (y))
+ return 0;
+
+ if (GET_CODE (y) == PLUS
+ && canon_rtx (XEXP (y, 0)) == x
+ && (y1 = canon_rtx (XEXP (y, 1)))
+ && GET_CODE (y1) == CONST_INT)
+ {
+ c += INTVAL (y1);
+ return (xsize == 0 || ysize == 0
+ || (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0));
+ }
+
+ if (GET_CODE (y) == PLUS
+ && (y1 = canon_rtx (XEXP (y, 0)))
+ && CONSTANT_P (y1))
+ return 0;
+
+ return 1;
+ }
+
+ if (GET_CODE (x) == PLUS)
+ {
+ /* The fact that X is canonicalized means that this
+ PLUS rtx is canonicalized. */
+ rtx x0 = XEXP (x, 0);
+ rtx x1 = XEXP (x, 1);
+
+ if (GET_CODE (y) == PLUS)
+ {
+ /* The fact that Y is canonicalized means that this
+ PLUS rtx is canonicalized. */
+ rtx y0 = XEXP (y, 0);
+ rtx y1 = XEXP (y, 1);
+
+ if (rtx_equal_for_memref_p (x1, y1))
+ return memrefs_conflict_p (xsize, x0, ysize, y0, c);
+ if (rtx_equal_for_memref_p (x0, y0))
+ return memrefs_conflict_p (xsize, x1, ysize, y1, c);
+ if (GET_CODE (x1) == CONST_INT)
+ if (GET_CODE (y1) == CONST_INT)
+ return memrefs_conflict_p (xsize, x0, ysize, y0,
+ c - INTVAL (x1) + INTVAL (y1));
+ else
+ return memrefs_conflict_p (xsize, x0, ysize, y, c - INTVAL (x1));
+ else if (GET_CODE (y1) == CONST_INT)
+ return memrefs_conflict_p (xsize, x, ysize, y0, c + INTVAL (y1));
+
+ /* Handle case where we cannot understand iteration operators,
+ but we notice that the base addresses are distinct objects. */
+ x = find_symbolic_term (x);
+ if (x == 0)
+ return 1;
+ y = find_symbolic_term (y);
+ if (y == 0)
+ return 1;
+ return rtx_equal_for_memref_p (x, y);
+ }
+ else if (GET_CODE (x1) == CONST_INT)
+ return memrefs_conflict_p (xsize, x0, ysize, y, c - INTVAL (x1));
+ }
+ else if (GET_CODE (y) == PLUS)
+ {
+ /* The fact that Y is canonicalized means that this
+ PLUS rtx is canonicalized. */
+ rtx y0 = XEXP (y, 0);
+ rtx y1 = XEXP (y, 1);
+
+ if (GET_CODE (y1) == CONST_INT)
+ return memrefs_conflict_p (xsize, x, ysize, y0, c + INTVAL (y1));
+ else
+ return 1;
+ }
+
+ if (GET_CODE (x) == GET_CODE (y))
+ switch (GET_CODE (x))
+ {
+ case MULT:
+ {
+ /* Handle cases where we expect the second operands to be the
+ same, and check only whether the first operand would conflict
+ or not. */
+ rtx x0, y0;
+ rtx x1 = canon_rtx (XEXP (x, 1));
+ rtx y1 = canon_rtx (XEXP (y, 1));
+ if (! rtx_equal_for_memref_p (x1, y1))
+ return 1;
+ x0 = canon_rtx (XEXP (x, 0));
+ y0 = canon_rtx (XEXP (y, 0));
+ if (rtx_equal_for_memref_p (x0, y0))
+ return (xsize == 0 || ysize == 0
+ || (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0));
+
+ /* Can't properly adjust our sizes. */
+ if (GET_CODE (x1) != CONST_INT)
+ return 1;
+ xsize /= INTVAL (x1);
+ ysize /= INTVAL (x1);
+ c /= INTVAL (x1);
+ return memrefs_conflict_p (xsize, x0, ysize, y0, c);
+ }
+ }
+
+ if (CONSTANT_P (x))
+ {
+ if (GET_CODE (x) == CONST_INT && GET_CODE (y) == CONST_INT)
+ {
+ c += (INTVAL (y) - INTVAL (x));
+ return (xsize == 0 || ysize == 0
+ || (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0));
+ }
+
+ if (GET_CODE (x) == CONST)
+ {
+ if (GET_CODE (y) == CONST)
+ return memrefs_conflict_p (xsize, canon_rtx (XEXP (x, 0)),
+ ysize, canon_rtx (XEXP (y, 0)), c);
+ else
+ return memrefs_conflict_p (xsize, canon_rtx (XEXP (x, 0)),
+ ysize, y, c);
+ }
+ if (GET_CODE (y) == CONST)
+ return memrefs_conflict_p (xsize, x, ysize,
+ canon_rtx (XEXP (y, 0)), c);
+
+ if (CONSTANT_P (y))
+ return (rtx_equal_for_memref_p (x, y)
+ && (xsize == 0 || ysize == 0
+ || (c >= 0 && xsize > c) || (c < 0 && ysize+c > 0)));
+
+ return 1;
+ }
+ return 1;
+}
+
+/* Functions to compute memory dependencies.
+
+ Since we process the insns in execution order, we can build tables
+ to keep track of what registers are fixed (and not aliased), what registers
+ are varying in known ways, and what registers are varying in unknown
+ ways.
+
+ If both memory references are volatile, then there must always be a
+ dependence between the two references, since their order can not be
+ changed. A volatile and non-volatile reference can be interchanged
+ though.
+
+ A MEM_IN_STRUCT reference at a non-QImode varying address can never
+ conflict with a non-MEM_IN_STRUCT reference at a fixed address. We must
+ allow QImode aliasing because the ANSI C standard allows character
+ pointers to alias anything. We are assuming that characters are
+ always QImode here. */
+
+/* Read dependence: X is read after read in MEM takes place. There can
+ only be a dependence here if both reads are volatile. */
+
+int
+read_dependence (mem, x)
+ rtx mem;
+ rtx x;
+{
+ return MEM_VOLATILE_P (x) && MEM_VOLATILE_P (mem);
+}
+
+/* True dependence: X is read after store in MEM takes place. */
+
+int
+true_dependence (mem, x)
+ rtx mem;
+ rtx x;
+{
+ /* If X is an unchanging read, then it can't possibly conflict with any
+ non-unchanging store. It may conflict with an unchanging write though,
+ because there may be a single store to this address to initialize it.
+ Just fall through to the code below to resolve the case where we have
+ both an unchanging read and an unchanging write. This won't handle all
+ cases optimally, but the possible performance loss should be
+ negligible. */
+ if (RTX_UNCHANGING_P (x) && ! RTX_UNCHANGING_P (mem))
+ return 0;
+
+ return ((MEM_VOLATILE_P (x) && MEM_VOLATILE_P (mem))
+ || (memrefs_conflict_p (SIZE_FOR_MODE (mem), XEXP (mem, 0),
+ SIZE_FOR_MODE (x), XEXP (x, 0), 0)
+ && ! (MEM_IN_STRUCT_P (mem) && rtx_addr_varies_p (mem)
+ && GET_MODE (mem) != QImode
+ && ! MEM_IN_STRUCT_P (x) && ! rtx_addr_varies_p (x))
+ && ! (MEM_IN_STRUCT_P (x) && rtx_addr_varies_p (x)
+ && GET_MODE (x) != QImode
+ && ! MEM_IN_STRUCT_P (mem) && ! rtx_addr_varies_p (mem))));
+}
+
+/* Anti dependence: X is written after read in MEM takes place. */
+
+int
+anti_dependence (mem, x)
+ rtx mem;
+ rtx x;
+{
+ /* If MEM is an unchanging read, then it can't possibly conflict with
+ the store to X, because there is at most one store to MEM, and it must
+ have occured somewhere before MEM. */
+ if (RTX_UNCHANGING_P (mem))
+ return 0;
+
+ return ((MEM_VOLATILE_P (x) && MEM_VOLATILE_P (mem))
+ || (memrefs_conflict_p (SIZE_FOR_MODE (mem), XEXP (mem, 0),
+ SIZE_FOR_MODE (x), XEXP (x, 0), 0)
+ && ! (MEM_IN_STRUCT_P (mem) && rtx_addr_varies_p (mem)
+ && GET_MODE (mem) != QImode
+ && ! MEM_IN_STRUCT_P (x) && ! rtx_addr_varies_p (x))
+ && ! (MEM_IN_STRUCT_P (x) && rtx_addr_varies_p (x)
+ && GET_MODE (x) != QImode
+ && ! MEM_IN_STRUCT_P (mem) && ! rtx_addr_varies_p (mem))));
+}
+
+/* Output dependence: X is written after store in MEM takes place. */
+
+int
+output_dependence (mem, x)
+ rtx mem;
+ rtx x;
+{
+ return ((MEM_VOLATILE_P (x) && MEM_VOLATILE_P (mem))
+ || (memrefs_conflict_p (SIZE_FOR_MODE (mem), XEXP (mem, 0),
+ SIZE_FOR_MODE (x), XEXP (x, 0), 0)
+ && ! (MEM_IN_STRUCT_P (mem) && rtx_addr_varies_p (mem)
+ && GET_MODE (mem) != QImode
+ && ! MEM_IN_STRUCT_P (x) && ! rtx_addr_varies_p (x))
+ && ! (MEM_IN_STRUCT_P (x) && rtx_addr_varies_p (x)
+ && GET_MODE (x) != QImode
+ && ! MEM_IN_STRUCT_P (mem) && ! rtx_addr_varies_p (mem))));
+}
+
+/* Helper functions for instruction scheduling. */
+
+/* Add ELEM wrapped in an INSN_LIST with reg note kind DEP_TYPE to the
+ LOG_LINKS of INSN, if not already there. DEP_TYPE indicates the type
+ of dependence that this link represents. */
+
+static void
+add_dependence (insn, elem, dep_type)
+ rtx insn;
+ rtx elem;
+ enum reg_note dep_type;
+{
+ rtx link, next;
+
+ /* Don't depend an insn on itself. */
+ if (insn == elem)
+ return;
+
+ /* If elem is part of a sequence that must be scheduled together, then
+ make the dependence point to the last insn of the sequence.
+ When HAVE_cc0, it is possible for NOTEs to exist between users and
+ setters of the condition codes, so we must skip past notes here.
+ Otherwise, NOTEs are impossible here. */
+
+ next = NEXT_INSN (elem);
+
+#ifdef HAVE_cc0
+ while (next && GET_CODE (next) == NOTE)
+ next = NEXT_INSN (next);
+#endif
+
+ if (next && SCHED_GROUP_P (next))
+ {
+ /* Notes will never intervene here though, so don't bother checking
+ for them. */
+ /* We must reject CODE_LABELs, so that we don't get confused by one
+ that has LABEL_PRESERVE_P set, which is represented by the same
+ bit in the rtl as SCHED_GROUP_P. A CODE_LABEL can never be
+ SCHED_GROUP_P. */
+ while (NEXT_INSN (next) && SCHED_GROUP_P (NEXT_INSN (next))
+ && GET_CODE (NEXT_INSN (next)) != CODE_LABEL)
+ next = NEXT_INSN (next);
+
+ /* Again, don't depend an insn on itself. */
+ if (insn == next)
+ return;
+
+ /* Make the dependence to NEXT, the last insn of the group, instead
+ of the original ELEM. */
+ elem = next;
+ }
+
+ /* Check that we don't already have this dependence. */
+ for (link = LOG_LINKS (insn); link; link = XEXP (link, 1))
+ if (XEXP (link, 0) == elem)
+ {
+ /* If this is a more restrictive type of dependence than the existing
+ one, then change the existing dependence to this type. */
+ if ((int) dep_type < (int) REG_NOTE_KIND (link))
+ PUT_REG_NOTE_KIND (link, dep_type);
+ return;
+ }
+ /* Might want to check one level of transitivity to save conses. */
+
+ link = rtx_alloc (INSN_LIST);
+ /* Insn dependency, not data dependency. */
+ PUT_REG_NOTE_KIND (link, dep_type);
+ XEXP (link, 0) = elem;
+ XEXP (link, 1) = LOG_LINKS (insn);
+ LOG_LINKS (insn) = link;
+}
+
+/* Remove ELEM wrapped in an INSN_LIST from the LOG_LINKS
+ of INSN. Abort if not found. */
+
+static void
+remove_dependence (insn, elem)
+ rtx insn;
+ rtx elem;
+{
+ rtx prev, link;
+ int found = 0;
+
+ for (prev = 0, link = LOG_LINKS (insn); link;
+ prev = link, link = XEXP (link, 1))
+ {
+ if (XEXP (link, 0) == elem)
+ {
+ if (prev)
+ XEXP (prev, 1) = XEXP (link, 1);
+ else
+ LOG_LINKS (insn) = XEXP (link, 1);
+ found = 1;
+ }
+ }
+
+ if (! found)
+ abort ();
+ return;
+}
+
+#ifndef INSN_SCHEDULING
+void
+schedule_insns (dump_file)
+ FILE *dump_file;
+{
+}
+#else
+#ifndef __GNUC__
+#define __inline
+#endif
+
+/* Computation of memory dependencies. */
+
+/* The *_insns and *_mems are paired lists. Each pending memory operation
+ will have a pointer to the MEM rtx on one list and a pointer to the
+ containing insn on the other list in the same place in the list. */
+
+/* We can't use add_dependence like the old code did, because a single insn
+ may have multiple memory accesses, and hence needs to be on the list
+ once for each memory access. Add_dependence won't let you add an insn
+ to a list more than once. */
+
+/* An INSN_LIST containing all insns with pending read operations. */
+static rtx pending_read_insns;
+
+/* An EXPR_LIST containing all MEM rtx's which are pending reads. */
+static rtx pending_read_mems;
+
+/* An INSN_LIST containing all insns with pending write operations. */
+static rtx pending_write_insns;
+
+/* An EXPR_LIST containing all MEM rtx's which are pending writes. */
+static rtx pending_write_mems;
+
+/* Indicates the combined length of the two pending lists. We must prevent
+ these lists from ever growing too large since the number of dependencies
+ produced is at least O(N*N), and execution time is at least O(4*N*N), as
+ a function of the length of these pending lists. */
+
+static int pending_lists_length;
+
+/* An INSN_LIST containing all INSN_LISTs allocated but currently unused. */
+
+static rtx unused_insn_list;
+
+/* An EXPR_LIST containing all EXPR_LISTs allocated but currently unused. */
+
+static rtx unused_expr_list;
+
+/* The last insn upon which all memory references must depend.
+ This is an insn which flushed the pending lists, creating a dependency
+ between it and all previously pending memory references. This creates
+ a barrier (or a checkpoint) which no memory reference is allowed to cross.
+
+ This includes all non constant CALL_INSNs. When we do interprocedural
+ alias analysis, this restriction can be relaxed.
+ This may also be an INSN that writes memory if the pending lists grow
+ too large. */
+
+static rtx last_pending_memory_flush;
+
+/* The last function call we have seen. All hard regs, and, of course,
+ the last function call, must depend on this. */
+
+static rtx last_function_call;
+
+/* The LOG_LINKS field of this is a list of insns which use a pseudo register
+ that does not already cross a call. We create dependencies between each
+ of those insn and the next call insn, to ensure that they won't cross a call
+ after scheduling is done. */
+
+static rtx sched_before_next_call;
+
+/* Pointer to the last instruction scheduled. Used by rank_for_schedule,
+ so that insns independent of the last scheduled insn will be preferred
+ over dependent instructions. */
+
+static rtx last_scheduled_insn;
+
+/* Process an insn's memory dependencies. There are four kinds of
+ dependencies:
+
+ (0) read dependence: read follows read
+ (1) true dependence: read follows write
+ (2) anti dependence: write follows read
+ (3) output dependence: write follows write
+
+ We are careful to build only dependencies which actually exist, and
+ use transitivity to avoid building too many links. */
+
+/* Return the INSN_LIST containing INSN in LIST, or NULL
+ if LIST does not contain INSN. */
+
+__inline static rtx
+find_insn_list (insn, list)
+ rtx insn;
+ rtx list;
+{
+ while (list)
+ {
+ if (XEXP (list, 0) == insn)
+ return list;
+ list = XEXP (list, 1);
+ }
+ return 0;
+}
+
+/* Compute the function units used by INSN. This caches the value
+ returned by function_units_used. A function unit is encoded as the
+ unit number if the value is non-negative and the compliment of a
+ mask if the value is negative. A function unit index is the
+ non-negative encoding. */
+
+__inline static int
+insn_unit (insn)
+ rtx insn;
+{
+ register int unit = INSN_UNIT (insn);
+
+ if (unit == 0)
+ {
+ recog_memoized (insn);
+
+ /* A USE insn, or something else we don't need to understand.
+ We can't pass these directly to function_units_used because it will
+ trigger a fatal error for unrecognizable insns. */
+ if (INSN_CODE (insn) < 0)
+ unit = -1;
+ else
+ {
+ unit = function_units_used (insn);
+ /* Increment non-negative values so we can cache zero. */
+ if (unit >= 0) unit++;
+ }
+ /* We only cache 16 bits of the result, so if the value is out of
+ range, don't cache it. */
+ if (FUNCTION_UNITS_SIZE < HOST_BITS_PER_SHORT
+ || unit >= 0
+ || (~unit & ((1 << (HOST_BITS_PER_SHORT - 1)) - 1)) == 0)
+ INSN_UNIT (insn) = unit;
+ }
+ return (unit > 0 ? unit - 1 : unit);
+}
+
+/* Compute the blockage range for executing INSN on UNIT. This caches
+ the value returned by the blockage_range_function for the unit.
+ These values are encoded in an int where the upper half gives the
+ minimum value and the lower half gives the maximum value. */
+
+__inline static unsigned int
+blockage_range (unit, insn)
+ int unit;
+ rtx insn;
+{
+ unsigned int blockage = INSN_BLOCKAGE (insn);
+ unsigned int range;
+
+ if (UNIT_BLOCKED (blockage) != unit + 1)
+ {
+ range = function_units[unit].blockage_range_function (insn);
+ /* We only cache the blockage range for one unit and then only if
+ the values fit. */
+ if (HOST_BITS_PER_INT >= UNIT_BITS + 2 * BLOCKAGE_BITS)
+ INSN_BLOCKAGE (insn) = ENCODE_BLOCKAGE (unit + 1, range);
+ }
+ else
+ range = BLOCKAGE_RANGE (blockage);
+
+ return range;
+}
+
+/* A vector indexed by function unit instance giving the last insn to use
+ the unit. The value of the function unit instance index for unit U
+ instance I is (U + I * FUNCTION_UNITS_SIZE). */
+static rtx unit_last_insn[FUNCTION_UNITS_SIZE * MAX_MULTIPLICITY];
+
+/* A vector indexed by function unit instance giving the minimum time when
+ the unit will unblock based on the maximum blockage cost. */
+static int unit_tick[FUNCTION_UNITS_SIZE * MAX_MULTIPLICITY];
+
+/* A vector indexed by function unit number giving the number of insns
+ that remain to use the unit. */
+static int unit_n_insns[FUNCTION_UNITS_SIZE];
+
+/* Reset the function unit state to the null state. */
+
+static void
+clear_units ()
+{
+ bzero ((char *) unit_last_insn, sizeof (unit_last_insn));
+ bzero ((char *) unit_tick, sizeof (unit_tick));
+ bzero ((char *) unit_n_insns, sizeof (unit_n_insns));
+}
+
+/* Record an insn as one that will use the units encoded by UNIT. */
+
+__inline static void
+prepare_unit (unit)
+ int unit;
+{
+ int i;
+
+ if (unit >= 0)
+ unit_n_insns[unit]++;
+ else
+ for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
+ if ((unit & 1) != 0)
+ prepare_unit (i);
+}
+
+/* Return the actual hazard cost of executing INSN on the unit UNIT,
+ instance INSTANCE at time CLOCK if the previous actual hazard cost
+ was COST. */
+
+__inline static int
+actual_hazard_this_instance (unit, instance, insn, clock, cost)
+ int unit, instance, clock, cost;
+ rtx insn;
+{
+ int tick = unit_tick[instance];
+
+ if (tick - clock > cost)
+ {
+ /* The scheduler is operating in reverse, so INSN is the executing
+ insn and the unit's last insn is the candidate insn. We want a
+ more exact measure of the blockage if we execute INSN at CLOCK
+ given when we committed the execution of the unit's last insn.
+
+ The blockage value is given by either the unit's max blockage
+ constant, blockage range function, or blockage function. Use
+ the most exact form for the given unit. */
+
+ if (function_units[unit].blockage_range_function)
+ {
+ if (function_units[unit].blockage_function)
+ tick += (function_units[unit].blockage_function
+ (insn, unit_last_insn[instance])
+ - function_units[unit].max_blockage);
+ else
+ tick += ((int) MAX_BLOCKAGE_COST (blockage_range (unit, insn))
+ - function_units[unit].max_blockage);
+ }
+ if (tick - clock > cost)
+ cost = tick - clock;
+ }
+ return cost;
+}
+
+/* Record INSN as having begun execution on the units encoded by UNIT at
+ time CLOCK. */
+
+__inline static void
+schedule_unit (unit, insn, clock)
+ int unit, clock;
+ rtx insn;
+{
+ int i;
+
+ if (unit >= 0)
+ {
+ int instance = unit;
+#if MAX_MULTIPLICITY > 1
+ /* Find the first free instance of the function unit and use that
+ one. We assume that one is free. */
+ for (i = function_units[unit].multiplicity - 1; i > 0; i--)
+ {
+ if (! actual_hazard_this_instance (unit, instance, insn, clock, 0))
+ break;
+ instance += FUNCTION_UNITS_SIZE;
+ }
+#endif
+ unit_last_insn[instance] = insn;
+ unit_tick[instance] = (clock + function_units[unit].max_blockage);
+ }
+ else
+ for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
+ if ((unit & 1) != 0)
+ schedule_unit (i, insn, clock);
+}
+
+/* Return the actual hazard cost of executing INSN on the units encoded by
+ UNIT at time CLOCK if the previous actual hazard cost was COST. */
+
+__inline static int
+actual_hazard (unit, insn, clock, cost)
+ int unit, clock, cost;
+ rtx insn;
+{
+ int i;
+
+ if (unit >= 0)
+ {
+ /* Find the instance of the function unit with the minimum hazard. */
+ int instance = unit;
+ int best_cost = actual_hazard_this_instance (unit, instance, insn,
+ clock, cost);
+ int this_cost;
+
+#if MAX_MULTIPLICITY > 1
+ if (best_cost > cost)
+ {
+ for (i = function_units[unit].multiplicity - 1; i > 0; i--)
+ {
+ instance += FUNCTION_UNITS_SIZE;
+ this_cost = actual_hazard_this_instance (unit, instance, insn,
+ clock, cost);
+ if (this_cost < best_cost)
+ {
+ best_cost = this_cost;
+ if (this_cost <= cost)
+ break;
+ }
+ }
+ }
+#endif
+ cost = MAX (cost, best_cost);
+ }
+ else
+ for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
+ if ((unit & 1) != 0)
+ cost = actual_hazard (i, insn, clock, cost);
+
+ return cost;
+}
+
+/* Return the potential hazard cost of executing an instruction on the
+ units encoded by UNIT if the previous potential hazard cost was COST.
+ An insn with a large blockage time is chosen in preference to one
+ with a smaller time; an insn that uses a unit that is more likely
+ to be used is chosen in preference to one with a unit that is less
+ used. We are trying to minimize a subsequent actual hazard. */
+
+__inline static int
+potential_hazard (unit, insn, cost)
+ int unit, cost;
+ rtx insn;
+{
+ int i, ncost;
+ unsigned int minb, maxb;
+
+ if (unit >= 0)
+ {
+ minb = maxb = function_units[unit].max_blockage;
+ if (maxb > 1)
+ {
+ if (function_units[unit].blockage_range_function)
+ {
+ maxb = minb = blockage_range (unit, insn);
+ maxb = MAX_BLOCKAGE_COST (maxb);
+ minb = MIN_BLOCKAGE_COST (minb);
+ }
+
+ if (maxb > 1)
+ {
+ /* Make the number of instructions left dominate. Make the
+ minimum delay dominate the maximum delay. If all these
+ are the same, use the unit number to add an arbitrary
+ ordering. Other terms can be added. */
+ ncost = minb * 0x40 + maxb;
+ ncost *= (unit_n_insns[unit] - 1) * 0x1000 + unit;
+ if (ncost > cost)
+ cost = ncost;
+ }
+ }
+ }
+ else
+ for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
+ if ((unit & 1) != 0)
+ cost = potential_hazard (i, insn, cost);
+
+ return cost;
+}
+
+/* Compute cost of executing INSN given the dependence LINK on the insn USED.
+ This is the number of virtual cycles taken between instruction issue and
+ instruction results. */
+
+__inline static int
+insn_cost (insn, link, used)
+ rtx insn, link, used;
+{
+ register int cost = INSN_COST (insn);
+
+ if (cost == 0)
+ {
+ recog_memoized (insn);
+
+ /* A USE insn, or something else we don't need to understand.
+ We can't pass these directly to result_ready_cost because it will
+ trigger a fatal error for unrecognizable insns. */
+ if (INSN_CODE (insn) < 0)
+ {
+ INSN_COST (insn) = 1;
+ return 1;
+ }
+ else
+ {
+ cost = result_ready_cost (insn);
+
+ if (cost < 1)
+ cost = 1;
+
+ INSN_COST (insn) = cost;
+ }
+ }
+
+ /* A USE insn should never require the value used to be computed. This
+ allows the computation of a function's result and parameter values to
+ overlap the return and call. */
+ recog_memoized (used);
+ if (INSN_CODE (used) < 0)
+ LINK_COST_FREE (link) = 1;
+
+ /* If some dependencies vary the cost, compute the adjustment. Most
+ commonly, the adjustment is complete: either the cost is ignored
+ (in the case of an output- or anti-dependence), or the cost is
+ unchanged. These values are cached in the link as LINK_COST_FREE
+ and LINK_COST_ZERO. */
+
+ if (LINK_COST_FREE (link))
+ cost = 1;
+#ifdef ADJUST_COST
+ else if (! LINK_COST_ZERO (link))
+ {
+ int ncost = cost;
+
+ ADJUST_COST (used, link, insn, ncost);
+ if (ncost <= 1)
+ LINK_COST_FREE (link) = ncost = 1;
+ if (cost == ncost)
+ LINK_COST_ZERO (link) = 1;
+ cost = ncost;
+ }
+#endif
+ return cost;
+}
+
+/* Compute the priority number for INSN. */
+
+static int
+priority (insn)
+ rtx insn;
+{
+ if (insn && GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ int prev_priority;
+ int max_priority;
+ int this_priority = INSN_PRIORITY (insn);
+ rtx prev;
+
+ if (this_priority > 0)
+ return this_priority;
+
+ max_priority = 1;
+
+ /* Nonzero if these insns must be scheduled together. */
+ if (SCHED_GROUP_P (insn))
+ {
+ prev = insn;
+ while (SCHED_GROUP_P (prev))
+ {
+ prev = PREV_INSN (prev);
+ INSN_REF_COUNT (prev) += 1;
+ }
+ }
+
+ for (prev = LOG_LINKS (insn); prev; prev = XEXP (prev, 1))
+ {
+ rtx x = XEXP (prev, 0);
+
+ /* A dependence pointing to a note or deleted insn is always
+ obsolete, because sched_analyze_insn will have created any
+ necessary new dependences which replace it. Notes and deleted
+ insns can be created when instructions are deleted by insn
+ splitting, or by register allocation. */
+ if (GET_CODE (x) == NOTE || INSN_DELETED_P (x))
+ {
+ remove_dependence (insn, x);
+ continue;
+ }
+
+ /* Clear the link cost adjustment bits. */
+ LINK_COST_FREE (prev) = 0;
+#ifdef ADJUST_COST
+ LINK_COST_ZERO (prev) = 0;
+#endif
+
+ /* This priority calculation was chosen because it results in the
+ least instruction movement, and does not hurt the performance
+ of the resulting code compared to the old algorithm.
+ This makes the sched algorithm more stable, which results
+ in better code, because there is less register pressure,
+ cross jumping is more likely to work, and debugging is easier.
+
+ When all instructions have a latency of 1, there is no need to
+ move any instructions. Subtracting one here ensures that in such
+ cases all instructions will end up with a priority of one, and
+ hence no scheduling will be done.
+
+ The original code did not subtract the one, and added the
+ insn_cost of the current instruction to its priority (e.g.
+ move the insn_cost call down to the end). */
+
+ prev_priority = priority (x) + insn_cost (x, prev, insn) - 1;
+
+ if (prev_priority > max_priority)
+ max_priority = prev_priority;
+ INSN_REF_COUNT (x) += 1;
+ }
+
+ prepare_unit (insn_unit (insn));
+ INSN_PRIORITY (insn) = max_priority;
+ return INSN_PRIORITY (insn);
+ }
+ return 0;
+}
+
+/* Remove all INSN_LISTs and EXPR_LISTs from the pending lists and add
+ them to the unused_*_list variables, so that they can be reused. */
+
+static void
+free_pending_lists ()
+{
+ register rtx link, prev_link;
+
+ if (pending_read_insns)
+ {
+ prev_link = pending_read_insns;
+ link = XEXP (prev_link, 1);
+
+ while (link)
+ {
+ prev_link = link;
+ link = XEXP (link, 1);
+ }
+
+ XEXP (prev_link, 1) = unused_insn_list;
+ unused_insn_list = pending_read_insns;
+ pending_read_insns = 0;
+ }
+
+ if (pending_write_insns)
+ {
+ prev_link = pending_write_insns;
+ link = XEXP (prev_link, 1);
+
+ while (link)
+ {
+ prev_link = link;
+ link = XEXP (link, 1);
+ }
+
+ XEXP (prev_link, 1) = unused_insn_list;
+ unused_insn_list = pending_write_insns;
+ pending_write_insns = 0;
+ }
+
+ if (pending_read_mems)
+ {
+ prev_link = pending_read_mems;
+ link = XEXP (prev_link, 1);
+
+ while (link)
+ {
+ prev_link = link;
+ link = XEXP (link, 1);
+ }
+
+ XEXP (prev_link, 1) = unused_expr_list;
+ unused_expr_list = pending_read_mems;
+ pending_read_mems = 0;
+ }
+
+ if (pending_write_mems)
+ {
+ prev_link = pending_write_mems;
+ link = XEXP (prev_link, 1);
+
+ while (link)
+ {
+ prev_link = link;
+ link = XEXP (link, 1);
+ }
+
+ XEXP (prev_link, 1) = unused_expr_list;
+ unused_expr_list = pending_write_mems;
+ pending_write_mems = 0;
+ }
+}
+
+/* Add an INSN and MEM reference pair to a pending INSN_LIST and MEM_LIST.
+ The MEM is a memory reference contained within INSN, which we are saving
+ so that we can do memory aliasing on it. */
+
+static void
+add_insn_mem_dependence (insn_list, mem_list, insn, mem)
+ rtx *insn_list, *mem_list, insn, mem;
+{
+ register rtx link;
+
+ if (unused_insn_list)
+ {
+ link = unused_insn_list;
+ unused_insn_list = XEXP (link, 1);
+ }
+ else
+ link = rtx_alloc (INSN_LIST);
+ XEXP (link, 0) = insn;
+ XEXP (link, 1) = *insn_list;
+ *insn_list = link;
+
+ if (unused_expr_list)
+ {
+ link = unused_expr_list;
+ unused_expr_list = XEXP (link, 1);
+ }
+ else
+ link = rtx_alloc (EXPR_LIST);
+ XEXP (link, 0) = mem;
+ XEXP (link, 1) = *mem_list;
+ *mem_list = link;
+
+ pending_lists_length++;
+}
+
+/* Make a dependency between every memory reference on the pending lists
+ and INSN, thus flushing the pending lists. */
+
+static void
+flush_pending_lists (insn)
+ rtx insn;
+{
+ rtx link;
+
+ while (pending_read_insns)
+ {
+ add_dependence (insn, XEXP (pending_read_insns, 0), REG_DEP_ANTI);
+
+ link = pending_read_insns;
+ pending_read_insns = XEXP (pending_read_insns, 1);
+ XEXP (link, 1) = unused_insn_list;
+ unused_insn_list = link;
+
+ link = pending_read_mems;
+ pending_read_mems = XEXP (pending_read_mems, 1);
+ XEXP (link, 1) = unused_expr_list;
+ unused_expr_list = link;
+ }
+ while (pending_write_insns)
+ {
+ add_dependence (insn, XEXP (pending_write_insns, 0), REG_DEP_ANTI);
+
+ link = pending_write_insns;
+ pending_write_insns = XEXP (pending_write_insns, 1);
+ XEXP (link, 1) = unused_insn_list;
+ unused_insn_list = link;
+
+ link = pending_write_mems;
+ pending_write_mems = XEXP (pending_write_mems, 1);
+ XEXP (link, 1) = unused_expr_list;
+ unused_expr_list = link;
+ }
+ pending_lists_length = 0;
+
+ if (last_pending_memory_flush)
+ add_dependence (insn, last_pending_memory_flush, REG_DEP_ANTI);
+
+ last_pending_memory_flush = insn;
+}
+
+/* Analyze a single SET or CLOBBER rtx, X, creating all dependencies generated
+ by the write to the destination of X, and reads of everything mentioned. */
+
+static void
+sched_analyze_1 (x, insn)
+ rtx x;
+ rtx insn;
+{
+ register int regno;
+ register rtx dest = SET_DEST (x);
+
+ if (dest == 0)
+ return;
+
+ while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SUBREG
+ || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT)
+ {
+ if (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT)
+ {
+ /* The second and third arguments are values read by this insn. */
+ sched_analyze_2 (XEXP (dest, 1), insn);
+ sched_analyze_2 (XEXP (dest, 2), insn);
+ }
+ dest = SUBREG_REG (dest);
+ }
+
+ if (GET_CODE (dest) == REG)
+ {
+ register int i;
+
+ regno = REGNO (dest);
+
+ /* A hard reg in a wide mode may really be multiple registers.
+ If so, mark all of them just like the first. */
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ i = HARD_REGNO_NREGS (regno, GET_MODE (dest));
+ while (--i >= 0)
+ {
+ rtx u;
+
+ for (u = reg_last_uses[regno+i]; u; u = XEXP (u, 1))
+ add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
+ reg_last_uses[regno + i] = 0;
+ if (reg_last_sets[regno + i])
+ add_dependence (insn, reg_last_sets[regno + i],
+ REG_DEP_OUTPUT);
+ reg_pending_sets[(regno + i) / REGSET_ELT_BITS]
+ |= (REGSET_ELT_TYPE) 1 << ((regno + i) % REGSET_ELT_BITS);
+ if ((call_used_regs[i] || global_regs[i])
+ && last_function_call)
+ /* Function calls clobber all call_used regs. */
+ add_dependence (insn, last_function_call, REG_DEP_ANTI);
+ }
+ }
+ else
+ {
+ rtx u;
+
+ for (u = reg_last_uses[regno]; u; u = XEXP (u, 1))
+ add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
+ reg_last_uses[regno] = 0;
+ if (reg_last_sets[regno])
+ add_dependence (insn, reg_last_sets[regno], REG_DEP_OUTPUT);
+ reg_pending_sets[regno / REGSET_ELT_BITS]
+ |= (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS);
+
+ /* Pseudos that are REG_EQUIV to something may be replaced
+ by that during reloading. We need only add dependencies for
+ the address in the REG_EQUIV note. */
+ if (! reload_completed
+ && reg_known_equiv_p[regno]
+ && GET_CODE (reg_known_value[regno]) == MEM)
+ sched_analyze_2 (XEXP (reg_known_value[regno], 0), insn);
+
+ /* Don't let it cross a call after scheduling if it doesn't
+ already cross one. */
+ if (reg_n_calls_crossed[regno] == 0 && last_function_call)
+ add_dependence (insn, last_function_call, REG_DEP_ANTI);
+ }
+ }
+ else if (GET_CODE (dest) == MEM)
+ {
+ /* Writing memory. */
+
+ if (pending_lists_length > 32)
+ {
+ /* Flush all pending reads and writes to prevent the pending lists
+ from getting any larger. Insn scheduling runs too slowly when
+ these lists get long. The number 32 was chosen because it
+ seems like a reasonable number. When compiling GCC with itself,
+ this flush occurs 8 times for sparc, and 10 times for m88k using
+ the number 32. */
+ flush_pending_lists (insn);
+ }
+ else
+ {
+ rtx pending, pending_mem;
+
+ pending = pending_read_insns;
+ pending_mem = pending_read_mems;
+ while (pending)
+ {
+ /* If a dependency already exists, don't create a new one. */
+ if (! find_insn_list (XEXP (pending, 0), LOG_LINKS (insn)))
+ if (anti_dependence (XEXP (pending_mem, 0), dest))
+ add_dependence (insn, XEXP (pending, 0), REG_DEP_ANTI);
+
+ pending = XEXP (pending, 1);
+ pending_mem = XEXP (pending_mem, 1);
+ }
+
+ pending = pending_write_insns;
+ pending_mem = pending_write_mems;
+ while (pending)
+ {
+ /* If a dependency already exists, don't create a new one. */
+ if (! find_insn_list (XEXP (pending, 0), LOG_LINKS (insn)))
+ if (output_dependence (XEXP (pending_mem, 0), dest))
+ add_dependence (insn, XEXP (pending, 0), REG_DEP_OUTPUT);
+
+ pending = XEXP (pending, 1);
+ pending_mem = XEXP (pending_mem, 1);
+ }
+
+ if (last_pending_memory_flush)
+ add_dependence (insn, last_pending_memory_flush, REG_DEP_ANTI);
+
+ add_insn_mem_dependence (&pending_write_insns, &pending_write_mems,
+ insn, dest);
+ }
+ sched_analyze_2 (XEXP (dest, 0), insn);
+ }
+
+ /* Analyze reads. */
+ if (GET_CODE (x) == SET)
+ sched_analyze_2 (SET_SRC (x), insn);
+}
+
+/* Analyze the uses of memory and registers in rtx X in INSN. */
+
+static void
+sched_analyze_2 (x, insn)
+ rtx x;
+ rtx insn;
+{
+ register int i;
+ register int j;
+ register enum rtx_code code;
+ register char *fmt;
+
+ if (x == 0)
+ return;
+
+ code = GET_CODE (x);
+
+ switch (code)
+ {
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case SYMBOL_REF:
+ case CONST:
+ case LABEL_REF:
+ /* Ignore constants. Note that we must handle CONST_DOUBLE here
+ because it may have a cc0_rtx in its CONST_DOUBLE_CHAIN field, but
+ this does not mean that this insn is using cc0. */
+ return;
+
+#ifdef HAVE_cc0
+ case CC0:
+ {
+ rtx link, prev;
+
+ /* There may be a note before this insn now, but all notes will
+ be removed before we actually try to schedule the insns, so
+ it won't cause a problem later. We must avoid it here though. */
+
+ /* User of CC0 depends on immediately preceding insn. */
+ SCHED_GROUP_P (insn) = 1;
+
+ /* Make a copy of all dependencies on the immediately previous insn,
+ and add to this insn. This is so that all the dependencies will
+ apply to the group. Remove an explicit dependence on this insn
+ as SCHED_GROUP_P now represents it. */
+
+ prev = PREV_INSN (insn);
+ while (GET_CODE (prev) == NOTE)
+ prev = PREV_INSN (prev);
+
+ if (find_insn_list (prev, LOG_LINKS (insn)))
+ remove_dependence (insn, prev);
+
+ for (link = LOG_LINKS (prev); link; link = XEXP (link, 1))
+ add_dependence (insn, XEXP (link, 0), REG_NOTE_KIND (link));
+
+ return;
+ }
+#endif
+
+ case REG:
+ {
+ int regno = REGNO (x);
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ int i;
+
+ i = HARD_REGNO_NREGS (regno, GET_MODE (x));
+ while (--i >= 0)
+ {
+ reg_last_uses[regno + i]
+ = gen_rtx (INSN_LIST, VOIDmode,
+ insn, reg_last_uses[regno + i]);
+ if (reg_last_sets[regno + i])
+ add_dependence (insn, reg_last_sets[regno + i], 0);
+ if ((call_used_regs[regno + i] || global_regs[regno + i])
+ && last_function_call)
+ /* Function calls clobber all call_used regs. */
+ add_dependence (insn, last_function_call, REG_DEP_ANTI);
+ }
+ }
+ else
+ {
+ reg_last_uses[regno]
+ = gen_rtx (INSN_LIST, VOIDmode, insn, reg_last_uses[regno]);
+ if (reg_last_sets[regno])
+ add_dependence (insn, reg_last_sets[regno], 0);
+
+ /* Pseudos that are REG_EQUIV to something may be replaced
+ by that during reloading. We need only add dependencies for
+ the address in the REG_EQUIV note. */
+ if (! reload_completed
+ && reg_known_equiv_p[regno]
+ && GET_CODE (reg_known_value[regno]) == MEM)
+ sched_analyze_2 (XEXP (reg_known_value[regno], 0), insn);
+
+ /* If the register does not already cross any calls, then add this
+ insn to the sched_before_next_call list so that it will still
+ not cross calls after scheduling. */
+ if (reg_n_calls_crossed[regno] == 0)
+ add_dependence (sched_before_next_call, insn, REG_DEP_ANTI);
+ }
+ return;
+ }
+
+ case MEM:
+ {
+ /* Reading memory. */
+
+ rtx pending, pending_mem;
+
+ pending = pending_read_insns;
+ pending_mem = pending_read_mems;
+ while (pending)
+ {
+ /* If a dependency already exists, don't create a new one. */
+ if (! find_insn_list (XEXP (pending, 0), LOG_LINKS (insn)))
+ if (read_dependence (XEXP (pending_mem, 0), x))
+ add_dependence (insn, XEXP (pending, 0), REG_DEP_ANTI);
+
+ pending = XEXP (pending, 1);
+ pending_mem = XEXP (pending_mem, 1);
+ }
+
+ pending = pending_write_insns;
+ pending_mem = pending_write_mems;
+ while (pending)
+ {
+ /* If a dependency already exists, don't create a new one. */
+ if (! find_insn_list (XEXP (pending, 0), LOG_LINKS (insn)))
+ if (true_dependence (XEXP (pending_mem, 0), x))
+ add_dependence (insn, XEXP (pending, 0), 0);
+
+ pending = XEXP (pending, 1);
+ pending_mem = XEXP (pending_mem, 1);
+ }
+ if (last_pending_memory_flush)
+ add_dependence (insn, last_pending_memory_flush, REG_DEP_ANTI);
+
+ /* Always add these dependencies to pending_reads, since
+ this insn may be followed by a write. */
+ add_insn_mem_dependence (&pending_read_insns, &pending_read_mems,
+ insn, x);
+
+ /* Take advantage of tail recursion here. */
+ sched_analyze_2 (XEXP (x, 0), insn);
+ return;
+ }
+
+ case ASM_OPERANDS:
+ case ASM_INPUT:
+ case UNSPEC_VOLATILE:
+ case TRAP_IF:
+ {
+ rtx u;
+
+ /* Traditional and volatile asm instructions must be considered to use
+ and clobber all hard registers, all pseudo-registers and all of
+ memory. So must TRAP_IF and UNSPEC_VOLATILE operations.
+
+ Consider for instance a volatile asm that changes the fpu rounding
+ mode. An insn should not be moved across this even if it only uses
+ pseudo-regs because it might give an incorrectly rounded result. */
+ if (code != ASM_OPERANDS || MEM_VOLATILE_P (x))
+ {
+ int max_reg = max_reg_num ();
+ for (i = 0; i < max_reg; i++)
+ {
+ for (u = reg_last_uses[i]; u; u = XEXP (u, 1))
+ add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
+ reg_last_uses[i] = 0;
+ if (reg_last_sets[i])
+ add_dependence (insn, reg_last_sets[i], 0);
+ }
+ reg_pending_sets_all = 1;
+
+ flush_pending_lists (insn);
+ }
+
+ /* For all ASM_OPERANDS, we must traverse the vector of input operands.
+ We can not just fall through here since then we would be confused
+ by the ASM_INPUT rtx inside ASM_OPERANDS, which do not indicate
+ traditional asms unlike their normal usage. */
+
+ if (code == ASM_OPERANDS)
+ {
+ for (j = 0; j < ASM_OPERANDS_INPUT_LENGTH (x); j++)
+ sched_analyze_2 (ASM_OPERANDS_INPUT (x, j), insn);
+ return;
+ }
+ break;
+ }
+
+ case PRE_DEC:
+ case POST_DEC:
+ case PRE_INC:
+ case POST_INC:
+ /* These both read and modify the result. We must handle them as writes
+ to get proper dependencies for following instructions. We must handle
+ them as reads to get proper dependencies from this to previous
+ instructions. Thus we need to pass them to both sched_analyze_1
+ and sched_analyze_2. We must call sched_analyze_2 first in order
+ to get the proper antecedent for the read. */
+ sched_analyze_2 (XEXP (x, 0), insn);
+ sched_analyze_1 (x, insn);
+ return;
+ }
+
+ /* Other cases: walk the insn. */
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ sched_analyze_2 (XEXP (x, i), insn);
+ else if (fmt[i] == 'E')
+ for (j = 0; j < XVECLEN (x, i); j++)
+ sched_analyze_2 (XVECEXP (x, i, j), insn);
+ }
+}
+
+/* Analyze an INSN with pattern X to find all dependencies. */
+
+static void
+sched_analyze_insn (x, insn)
+ rtx x, insn;
+{
+ register RTX_CODE code = GET_CODE (x);
+ rtx link;
+ int maxreg = max_reg_num ();
+ int i;
+
+ if (code == SET || code == CLOBBER)
+ sched_analyze_1 (x, insn);
+ else if (code == PARALLEL)
+ {
+ register int i;
+ for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
+ {
+ code = GET_CODE (XVECEXP (x, 0, i));
+ if (code == SET || code == CLOBBER)
+ sched_analyze_1 (XVECEXP (x, 0, i), insn);
+ else
+ sched_analyze_2 (XVECEXP (x, 0, i), insn);
+ }
+ }
+ else
+ sched_analyze_2 (x, insn);
+
+ /* Mark registers CLOBBERED or used by called function. */
+ if (GET_CODE (insn) == CALL_INSN)
+ for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
+ {
+ if (GET_CODE (XEXP (link, 0)) == CLOBBER)
+ sched_analyze_1 (XEXP (link, 0), insn);
+ else
+ sched_analyze_2 (XEXP (link, 0), insn);
+ }
+
+ /* After reload, it is possible for an instruction to have a REG_DEAD note
+ for a register that actually dies a few instructions earlier. For
+ example, this can happen with SECONDARY_MEMORY_NEEDED reloads.
+ In this case, we must consider the insn to use the register mentioned
+ in the REG_DEAD note. Otherwise, we may accidentally move this insn
+ after another insn that sets the register, thus getting obviously invalid
+ rtl. This confuses reorg which believes that REG_DEAD notes are still
+ meaningful.
+
+ ??? We would get better code if we fixed reload to put the REG_DEAD
+ notes in the right places, but that may not be worth the effort. */
+
+ if (reload_completed)
+ {
+ rtx note;
+
+ for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
+ if (REG_NOTE_KIND (note) == REG_DEAD)
+ sched_analyze_2 (XEXP (note, 0), insn);
+ }
+
+ for (i = 0; i < regset_size; i++)
+ {
+ REGSET_ELT_TYPE sets = reg_pending_sets[i];
+ if (sets)
+ {
+ register int bit;
+ for (bit = 0; bit < REGSET_ELT_BITS; bit++)
+ if (sets & ((REGSET_ELT_TYPE) 1 << bit))
+ reg_last_sets[i * REGSET_ELT_BITS + bit] = insn;
+ reg_pending_sets[i] = 0;
+ }
+ }
+ if (reg_pending_sets_all)
+ {
+ for (i = 0; i < maxreg; i++)
+ reg_last_sets[i] = insn;
+ reg_pending_sets_all = 0;
+ }
+
+ /* Handle function calls and function returns created by the epilogue
+ threading code. */
+ if (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
+ {
+ rtx dep_insn;
+ rtx prev_dep_insn;
+
+ /* When scheduling instructions, we make sure calls don't lose their
+ accompanying USE insns by depending them one on another in order.
+
+ Also, we must do the same thing for returns created by the epilogue
+ threading code. Note this code works only in this special case,
+ because other passes make no guarantee that they will never emit
+ an instruction between a USE and a RETURN. There is such a guarantee
+ for USE instructions immediately before a call. */
+
+ prev_dep_insn = insn;
+ dep_insn = PREV_INSN (insn);
+ while (GET_CODE (dep_insn) == INSN
+ && GET_CODE (PATTERN (dep_insn)) == USE)
+ {
+ SCHED_GROUP_P (prev_dep_insn) = 1;
+
+ /* Make a copy of all dependencies on dep_insn, and add to insn.
+ This is so that all of the dependencies will apply to the
+ group. */
+
+ for (link = LOG_LINKS (dep_insn); link; link = XEXP (link, 1))
+ add_dependence (insn, XEXP (link, 0), REG_NOTE_KIND (link));
+
+ prev_dep_insn = dep_insn;
+ dep_insn = PREV_INSN (dep_insn);
+ }
+ }
+}
+
+/* Analyze every insn between HEAD and TAIL inclusive, creating LOG_LINKS
+ for every dependency. */
+
+static int
+sched_analyze (head, tail)
+ rtx head, tail;
+{
+ register rtx insn;
+ register int n_insns = 0;
+ register rtx u;
+ register int luid = 0;
+
+ for (insn = head; ; insn = NEXT_INSN (insn))
+ {
+ INSN_LUID (insn) = luid++;
+
+ if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN)
+ {
+ sched_analyze_insn (PATTERN (insn), insn);
+ n_insns += 1;
+ }
+ else if (GET_CODE (insn) == CALL_INSN)
+ {
+ rtx x;
+ register int i;
+
+ /* Any instruction using a hard register which may get clobbered
+ by a call needs to be marked as dependent on this call.
+ This prevents a use of a hard return reg from being moved
+ past a void call (i.e. it does not explicitly set the hard
+ return reg). */
+
+ /* If this call is followed by a NOTE_INSN_SETJMP, then assume that
+ all registers, not just hard registers, may be clobbered by this
+ call. */
+
+ /* Insn, being a CALL_INSN, magically depends on
+ `last_function_call' already. */
+
+ if (NEXT_INSN (insn) && GET_CODE (NEXT_INSN (insn)) == NOTE
+ && NOTE_LINE_NUMBER (NEXT_INSN (insn)) == NOTE_INSN_SETJMP)
+ {
+ int max_reg = max_reg_num ();
+ for (i = 0; i < max_reg; i++)
+ {
+ for (u = reg_last_uses[i]; u; u = XEXP (u, 1))
+ add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
+ reg_last_uses[i] = 0;
+ if (reg_last_sets[i])
+ add_dependence (insn, reg_last_sets[i], 0);
+ }
+ reg_pending_sets_all = 1;
+
+ /* Add a fake REG_NOTE which we will later convert
+ back into a NOTE_INSN_SETJMP note. */
+ REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_DEAD, constm1_rtx,
+ REG_NOTES (insn));
+ }
+ else
+ {
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (call_used_regs[i] || global_regs[i])
+ {
+ for (u = reg_last_uses[i]; u; u = XEXP (u, 1))
+ add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
+ reg_last_uses[i] = 0;
+ if (reg_last_sets[i])
+ add_dependence (insn, reg_last_sets[i], REG_DEP_ANTI);
+ reg_pending_sets[i / REGSET_ELT_BITS]
+ |= (REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS);
+ }
+ }
+
+ /* For each insn which shouldn't cross a call, add a dependence
+ between that insn and this call insn. */
+ x = LOG_LINKS (sched_before_next_call);
+ while (x)
+ {
+ add_dependence (insn, XEXP (x, 0), REG_DEP_ANTI);
+ x = XEXP (x, 1);
+ }
+ LOG_LINKS (sched_before_next_call) = 0;
+
+ sched_analyze_insn (PATTERN (insn), insn);
+
+ /* We don't need to flush memory for a function call which does
+ not involve memory. */
+ if (! CONST_CALL_P (insn))
+ {
+ /* In the absence of interprocedural alias analysis,
+ we must flush all pending reads and writes, and
+ start new dependencies starting from here. */
+ flush_pending_lists (insn);
+ }
+
+ /* Depend this function call (actually, the user of this
+ function call) on all hard register clobberage. */
+ last_function_call = insn;
+ n_insns += 1;
+ }
+
+ if (insn == tail)
+ return n_insns;
+ }
+}
+
+/* Called when we see a set of a register. If death is true, then we are
+ scanning backwards. Mark that register as unborn. If nobody says
+ otherwise, that is how things will remain. If death is false, then we
+ are scanning forwards. Mark that register as being born. */
+
+static void
+sched_note_set (b, x, death)
+ int b;
+ rtx x;
+ int death;
+{
+ register int regno;
+ register rtx reg = SET_DEST (x);
+ int subreg_p = 0;
+
+ if (reg == 0)
+ return;
+
+ while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == STRICT_LOW_PART
+ || GET_CODE (reg) == SIGN_EXTRACT || GET_CODE (reg) == ZERO_EXTRACT)
+ {
+ /* Must treat modification of just one hardware register of a multi-reg
+ value or just a byte field of a register exactly the same way that
+ mark_set_1 in flow.c does, i.e. anything except a paradoxical subreg
+ does not kill the entire register. */
+ if (GET_CODE (reg) != SUBREG
+ || REG_SIZE (SUBREG_REG (reg)) > REG_SIZE (reg))
+ subreg_p = 1;
+
+ reg = SUBREG_REG (reg);
+ }
+
+ if (GET_CODE (reg) != REG)
+ return;
+
+ /* Global registers are always live, so the code below does not apply
+ to them. */
+
+ regno = REGNO (reg);
+ if (regno >= FIRST_PSEUDO_REGISTER || ! global_regs[regno])
+ {
+ register int offset = regno / REGSET_ELT_BITS;
+ register REGSET_ELT_TYPE bit
+ = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS);
+
+ if (death)
+ {
+ /* If we only set part of the register, then this set does not
+ kill it. */
+ if (subreg_p)
+ return;
+
+ /* Try killing this register. */
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ int j = HARD_REGNO_NREGS (regno, GET_MODE (reg));
+ while (--j >= 0)
+ {
+ offset = (regno + j) / REGSET_ELT_BITS;
+ bit = (REGSET_ELT_TYPE) 1 << ((regno + j) % REGSET_ELT_BITS);
+
+ bb_live_regs[offset] &= ~bit;
+ bb_dead_regs[offset] |= bit;
+ }
+ }
+ else
+ {
+ bb_live_regs[offset] &= ~bit;
+ bb_dead_regs[offset] |= bit;
+ }
+ }
+ else
+ {
+ /* Make the register live again. */
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ int j = HARD_REGNO_NREGS (regno, GET_MODE (reg));
+ while (--j >= 0)
+ {
+ offset = (regno + j) / REGSET_ELT_BITS;
+ bit = (REGSET_ELT_TYPE) 1 << ((regno + j) % REGSET_ELT_BITS);
+
+ bb_live_regs[offset] |= bit;
+ bb_dead_regs[offset] &= ~bit;
+ }
+ }
+ else
+ {
+ bb_live_regs[offset] |= bit;
+ bb_dead_regs[offset] &= ~bit;
+ }
+ }
+ }
+}
+
+/* Macros and functions for keeping the priority queue sorted, and
+ dealing with queueing and unqueueing of instructions. */
+
+#define SCHED_SORT(READY, NEW_READY, OLD_READY) \
+ do { if ((NEW_READY) - (OLD_READY) == 1) \
+ swap_sort (READY, NEW_READY); \
+ else if ((NEW_READY) - (OLD_READY) > 1) \
+ qsort (READY, NEW_READY, sizeof (rtx), rank_for_schedule); } \
+ while (0)
+
+/* Returns a positive value if y is preferred; returns a negative value if
+ x is preferred. Should never return 0, since that will make the sort
+ unstable. */
+
+static int
+rank_for_schedule (x, y)
+ rtx *x, *y;
+{
+ rtx tmp = *y;
+ rtx tmp2 = *x;
+ rtx link;
+ int tmp_class, tmp2_class;
+ int value;
+
+ /* Choose the instruction with the highest priority, if different. */
+ if (value = INSN_PRIORITY (tmp) - INSN_PRIORITY (tmp2))
+ return value;
+
+ if (last_scheduled_insn)
+ {
+ /* Classify the instructions into three classes:
+ 1) Data dependent on last schedule insn.
+ 2) Anti/Output dependent on last scheduled insn.
+ 3) Independent of last scheduled insn, or has latency of one.
+ Choose the insn from the highest numbered class if different. */
+ link = find_insn_list (tmp, LOG_LINKS (last_scheduled_insn));
+ if (link == 0 || insn_cost (tmp, link, last_scheduled_insn) == 1)
+ tmp_class = 3;
+ else if (REG_NOTE_KIND (link) == 0) /* Data dependence. */
+ tmp_class = 1;
+ else
+ tmp_class = 2;
+
+ link = find_insn_list (tmp2, LOG_LINKS (last_scheduled_insn));
+ if (link == 0 || insn_cost (tmp2, link, last_scheduled_insn) == 1)
+ tmp2_class = 3;
+ else if (REG_NOTE_KIND (link) == 0) /* Data dependence. */
+ tmp2_class = 1;
+ else
+ tmp2_class = 2;
+
+ if (value = tmp_class - tmp2_class)
+ return value;
+ }
+
+ /* If insns are equally good, sort by INSN_LUID (original insn order),
+ so that we make the sort stable. This minimizes instruction movement,
+ thus minimizing sched's effect on debugging and cross-jumping. */
+ return INSN_LUID (tmp) - INSN_LUID (tmp2);
+}
+
+/* Resort the array A in which only element at index N may be out of order. */
+
+__inline static void
+swap_sort (a, n)
+ rtx *a;
+ int n;
+{
+ rtx insn = a[n-1];
+ int i = n-2;
+
+ while (i >= 0 && rank_for_schedule (a+i, &insn) >= 0)
+ {
+ a[i+1] = a[i];
+ i -= 1;
+ }
+ a[i+1] = insn;
+}
+
+static int max_priority;
+
+/* Add INSN to the insn queue so that it fires at least N_CYCLES
+ before the currently executing insn. */
+
+__inline static void
+queue_insn (insn, n_cycles)
+ rtx insn;
+ int n_cycles;
+{
+ int next_q = NEXT_Q_AFTER (q_ptr, n_cycles);
+ NEXT_INSN (insn) = insn_queue[next_q];
+ insn_queue[next_q] = insn;
+ q_size += 1;
+}
+
+/* Return nonzero if PAT is the pattern of an insn which makes a
+ register live. */
+
+__inline static int
+birthing_insn_p (pat)
+ rtx pat;
+{
+ int j;
+
+ if (reload_completed == 1)
+ return 0;
+
+ if (GET_CODE (pat) == SET
+ && GET_CODE (SET_DEST (pat)) == REG)
+ {
+ rtx dest = SET_DEST (pat);
+ int i = REGNO (dest);
+ int offset = i / REGSET_ELT_BITS;
+ REGSET_ELT_TYPE bit = (REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS);
+
+ /* It would be more accurate to use refers_to_regno_p or
+ reg_mentioned_p to determine when the dest is not live before this
+ insn. */
+
+ if (bb_live_regs[offset] & bit)
+ return (reg_n_sets[i] == 1);
+
+ return 0;
+ }
+ if (GET_CODE (pat) == PARALLEL)
+ {
+ for (j = 0; j < XVECLEN (pat, 0); j++)
+ if (birthing_insn_p (XVECEXP (pat, 0, j)))
+ return 1;
+ }
+ return 0;
+}
+
+/* PREV is an insn that is ready to execute. Adjust its priority if that
+ will help shorten register lifetimes. */
+
+__inline static void
+adjust_priority (prev)
+ rtx prev;
+{
+ /* Trying to shorten register lives after reload has completed
+ is useless and wrong. It gives inaccurate schedules. */
+ if (reload_completed == 0)
+ {
+ rtx note;
+ int n_deaths = 0;
+
+ /* ??? This code has no effect, because REG_DEAD notes are removed
+ before we ever get here. */
+ for (note = REG_NOTES (prev); note; note = XEXP (note, 1))
+ if (REG_NOTE_KIND (note) == REG_DEAD)
+ n_deaths += 1;
+
+ /* Defer scheduling insns which kill registers, since that
+ shortens register lives. Prefer scheduling insns which
+ make registers live for the same reason. */
+ switch (n_deaths)
+ {
+ default:
+ INSN_PRIORITY (prev) >>= 3;
+ break;
+ case 3:
+ INSN_PRIORITY (prev) >>= 2;
+ break;
+ case 2:
+ case 1:
+ INSN_PRIORITY (prev) >>= 1;
+ break;
+ case 0:
+ if (birthing_insn_p (PATTERN (prev)))
+ {
+ int max = max_priority;
+
+ if (max > INSN_PRIORITY (prev))
+ INSN_PRIORITY (prev) = max;
+ }
+ break;
+ }
+ }
+}
+
+/* INSN is the "currently executing insn". Launch each insn which was
+ waiting on INSN (in the backwards dataflow sense). READY is a
+ vector of insns which are ready to fire. N_READY is the number of
+ elements in READY. CLOCK is the current virtual cycle. */
+
+static int
+schedule_insn (insn, ready, n_ready, clock)
+ rtx insn;
+ rtx *ready;
+ int n_ready;
+ int clock;
+{
+ rtx link;
+ int new_ready = n_ready;
+
+ if (MAX_BLOCKAGE > 1)
+ schedule_unit (insn_unit (insn), insn, clock);
+
+ if (LOG_LINKS (insn) == 0)
+ return n_ready;
+
+ /* This is used by the function adjust_priority above. */
+ if (n_ready > 0)
+ max_priority = MAX (INSN_PRIORITY (ready[0]), INSN_PRIORITY (insn));
+ else
+ max_priority = INSN_PRIORITY (insn);
+
+ for (link = LOG_LINKS (insn); link != 0; link = XEXP (link, 1))
+ {
+ rtx prev = XEXP (link, 0);
+ int cost = insn_cost (prev, link, insn);
+
+ if ((INSN_REF_COUNT (prev) -= 1) != 0)
+ {
+ /* We satisfied one requirement to fire PREV. Record the earliest
+ time when PREV can fire. No need to do this if the cost is 1,
+ because PREV can fire no sooner than the next cycle. */
+ if (cost > 1)
+ INSN_TICK (prev) = MAX (INSN_TICK (prev), clock + cost);
+ }
+ else
+ {
+ /* We satisfied the last requirement to fire PREV. Ensure that all
+ timing requirements are satisfied. */
+ if (INSN_TICK (prev) - clock > cost)
+ cost = INSN_TICK (prev) - clock;
+
+ /* Adjust the priority of PREV and either put it on the ready
+ list or queue it. */
+ adjust_priority (prev);
+ if (cost <= 1)
+ ready[new_ready++] = prev;
+ else
+ queue_insn (prev, cost);
+ }
+ }
+
+ return new_ready;
+}
+
+/* Given N_READY insns in the ready list READY at time CLOCK, queue
+ those that are blocked due to function unit hazards and rearrange
+ the remaining ones to minimize subsequent function unit hazards. */
+
+static int
+schedule_select (ready, n_ready, clock, file)
+ rtx *ready;
+ int n_ready, clock;
+ FILE *file;
+{
+ int pri = INSN_PRIORITY (ready[0]);
+ int i, j, k, q, cost, best_cost, best_insn = 0, new_ready = n_ready;
+ rtx insn;
+
+ /* Work down the ready list in groups of instructions with the same
+ priority value. Queue insns in the group that are blocked and
+ select among those that remain for the one with the largest
+ potential hazard. */
+ for (i = 0; i < n_ready; i = j)
+ {
+ int opri = pri;
+ for (j = i + 1; j < n_ready; j++)
+ if ((pri = INSN_PRIORITY (ready[j])) != opri)
+ break;
+
+ /* Queue insns in the group that are blocked. */
+ for (k = i, q = 0; k < j; k++)
+ {
+ insn = ready[k];
+ if ((cost = actual_hazard (insn_unit (insn), insn, clock, 0)) != 0)
+ {
+ q++;
+ ready[k] = 0;
+ queue_insn (insn, cost);
+ if (file)
+ fprintf (file, "\n;; blocking insn %d for %d cycles",
+ INSN_UID (insn), cost);
+ }
+ }
+ new_ready -= q;
+
+ /* Check the next group if all insns were queued. */
+ if (j - i - q == 0)
+ continue;
+
+ /* If more than one remains, select the first one with the largest
+ potential hazard. */
+ else if (j - i - q > 1)
+ {
+ best_cost = -1;
+ for (k = i; k < j; k++)
+ {
+ if ((insn = ready[k]) == 0)
+ continue;
+ if ((cost = potential_hazard (insn_unit (insn), insn, 0))
+ > best_cost)
+ {
+ best_cost = cost;
+ best_insn = k;
+ }
+ }
+ }
+ /* We have found a suitable insn to schedule. */
+ break;
+ }
+
+ /* Move the best insn to be front of the ready list. */
+ if (best_insn != 0)
+ {
+ if (file)
+ {
+ fprintf (file, ", now");
+ for (i = 0; i < n_ready; i++)
+ if (ready[i])
+ fprintf (file, " %d", INSN_UID (ready[i]));
+ fprintf (file, "\n;; insn %d has a greater potential hazard",
+ INSN_UID (ready[best_insn]));
+ }
+ for (i = best_insn; i > 0; i--)
+ {
+ insn = ready[i-1];
+ ready[i-1] = ready[i];
+ ready[i] = insn;
+ }
+ }
+
+ /* Compact the ready list. */
+ if (new_ready < n_ready)
+ for (i = j = 0; i < n_ready; i++)
+ if (ready[i])
+ ready[j++] = ready[i];
+
+ return new_ready;
+}
+
+/* Add a REG_DEAD note for REG to INSN, reusing a REG_DEAD note from the
+ dead_notes list. */
+
+static void
+create_reg_dead_note (reg, insn)
+ rtx reg, insn;
+{
+ rtx link;
+
+ /* The number of registers killed after scheduling must be the same as the
+ number of registers killed before scheduling. The number of REG_DEAD
+ notes may not be conserved, i.e. two SImode hard register REG_DEAD notes
+ might become one DImode hard register REG_DEAD note, but the number of
+ registers killed will be conserved.
+
+ We carefully remove REG_DEAD notes from the dead_notes list, so that
+ there will be none left at the end. If we run out early, then there
+ is a bug somewhere in flow, combine and/or sched. */
+
+ if (dead_notes == 0)
+ {
+#if 1
+ abort ();
+#else
+ link = rtx_alloc (EXPR_LIST);
+ PUT_REG_NOTE_KIND (link, REG_DEAD);
+#endif
+ }
+ else
+ {
+ /* Number of regs killed by REG. */
+ int regs_killed = (REGNO (reg) >= FIRST_PSEUDO_REGISTER ? 1
+ : HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg)));
+ /* Number of regs killed by REG_DEAD notes taken off the list. */
+ int reg_note_regs;
+
+ link = dead_notes;
+ reg_note_regs = (REGNO (XEXP (link, 0)) >= FIRST_PSEUDO_REGISTER ? 1
+ : HARD_REGNO_NREGS (REGNO (XEXP (link, 0)),
+ GET_MODE (XEXP (link, 0))));
+ while (reg_note_regs < regs_killed)
+ {
+ link = XEXP (link, 1);
+ reg_note_regs += (REGNO (XEXP (link, 0)) >= FIRST_PSEUDO_REGISTER ? 1
+ : HARD_REGNO_NREGS (REGNO (XEXP (link, 0)),
+ GET_MODE (XEXP (link, 0))));
+ }
+ dead_notes = XEXP (link, 1);
+
+ /* If we took too many regs kills off, put the extra ones back. */
+ while (reg_note_regs > regs_killed)
+ {
+ rtx temp_reg, temp_link;
+
+ temp_reg = gen_rtx (REG, word_mode, 0);
+ temp_link = rtx_alloc (EXPR_LIST);
+ PUT_REG_NOTE_KIND (temp_link, REG_DEAD);
+ XEXP (temp_link, 0) = temp_reg;
+ XEXP (temp_link, 1) = dead_notes;
+ dead_notes = temp_link;
+ reg_note_regs--;
+ }
+ }
+
+ XEXP (link, 0) = reg;
+ XEXP (link, 1) = REG_NOTES (insn);
+ REG_NOTES (insn) = link;
+}
+
+/* Subroutine on attach_deaths_insn--handles the recursive search
+ through INSN. If SET_P is true, then x is being modified by the insn. */
+
+static void
+attach_deaths (x, insn, set_p)
+ rtx x;
+ rtx insn;
+ int set_p;
+{
+ register int i;
+ register int j;
+ register enum rtx_code code;
+ register char *fmt;
+
+ if (x == 0)
+ return;
+
+ code = GET_CODE (x);
+
+ switch (code)
+ {
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ case CONST:
+ case CODE_LABEL:
+ case PC:
+ case CC0:
+ /* Get rid of the easy cases first. */
+ return;
+
+ case REG:
+ {
+ /* If the register dies in this insn, queue that note, and mark
+ this register as needing to die. */
+ /* This code is very similar to mark_used_1 (if set_p is false)
+ and mark_set_1 (if set_p is true) in flow.c. */
+
+ register int regno = REGNO (x);
+ register int offset = regno / REGSET_ELT_BITS;
+ register REGSET_ELT_TYPE bit
+ = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS);
+ REGSET_ELT_TYPE all_needed = (old_live_regs[offset] & bit);
+ REGSET_ELT_TYPE some_needed = (old_live_regs[offset] & bit);
+
+ if (set_p)
+ return;
+
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ int n;
+
+ n = HARD_REGNO_NREGS (regno, GET_MODE (x));
+ while (--n > 0)
+ {
+ some_needed |= (old_live_regs[(regno + n) / REGSET_ELT_BITS]
+ & ((REGSET_ELT_TYPE) 1
+ << ((regno + n) % REGSET_ELT_BITS)));
+ all_needed &= (old_live_regs[(regno + n) / REGSET_ELT_BITS]
+ & ((REGSET_ELT_TYPE) 1
+ << ((regno + n) % REGSET_ELT_BITS)));
+ }
+ }
+
+ /* If it wasn't live before we started, then add a REG_DEAD note.
+ We must check the previous lifetime info not the current info,
+ because we may have to execute this code several times, e.g.
+ once for a clobber (which doesn't add a note) and later
+ for a use (which does add a note).
+
+ Always make the register live. We must do this even if it was
+ live before, because this may be an insn which sets and uses
+ the same register, in which case the register has already been
+ killed, so we must make it live again.
+
+ Global registers are always live, and should never have a REG_DEAD
+ note added for them, so none of the code below applies to them. */
+
+ if (regno >= FIRST_PSEUDO_REGISTER || ! global_regs[regno])
+ {
+ /* Never add REG_DEAD notes for the FRAME_POINTER_REGNUM or the
+ STACK_POINTER_REGNUM, since these are always considered to be
+ live. Similarly for ARG_POINTER_REGNUM if it is fixed. */
+ if (regno != FRAME_POINTER_REGNUM
+#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ && ! (regno == HARD_FRAME_POINTER_REGNUM)
+#endif
+#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ && ! (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
+#endif
+ && regno != STACK_POINTER_REGNUM)
+ {
+ if (! all_needed && ! dead_or_set_p (insn, x))
+ {
+ /* If none of the words in X is needed, make a REG_DEAD
+ note. Otherwise, we must make partial REG_DEAD
+ notes. */
+ if (! some_needed)
+ create_reg_dead_note (x, insn);
+ else
+ {
+ int i;
+
+ /* Don't make a REG_DEAD note for a part of a
+ register that is set in the insn. */
+ for (i = HARD_REGNO_NREGS (regno, GET_MODE (x)) - 1;
+ i >= 0; i--)
+ if ((old_live_regs[(regno + i) / REGSET_ELT_BITS]
+ & ((REGSET_ELT_TYPE) 1
+ << ((regno +i) % REGSET_ELT_BITS))) == 0
+ && ! dead_or_set_regno_p (insn, regno + i))
+ create_reg_dead_note (gen_rtx (REG,
+ reg_raw_mode[regno + i],
+ regno + i),
+ insn);
+ }
+ }
+ }
+
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ int j = HARD_REGNO_NREGS (regno, GET_MODE (x));
+ while (--j >= 0)
+ {
+ offset = (regno + j) / REGSET_ELT_BITS;
+ bit
+ = (REGSET_ELT_TYPE) 1 << ((regno + j) % REGSET_ELT_BITS);
+
+ bb_dead_regs[offset] &= ~bit;
+ bb_live_regs[offset] |= bit;
+ }
+ }
+ else
+ {
+ bb_dead_regs[offset] &= ~bit;
+ bb_live_regs[offset] |= bit;
+ }
+ }
+ return;
+ }
+
+ case MEM:
+ /* Handle tail-recursive case. */
+ attach_deaths (XEXP (x, 0), insn, 0);
+ return;
+
+ case SUBREG:
+ case STRICT_LOW_PART:
+ /* These two cases preserve the value of SET_P, so handle them
+ separately. */
+ attach_deaths (XEXP (x, 0), insn, set_p);
+ return;
+
+ case ZERO_EXTRACT:
+ case SIGN_EXTRACT:
+ /* This case preserves the value of SET_P for the first operand, but
+ clears it for the other two. */
+ attach_deaths (XEXP (x, 0), insn, set_p);
+ attach_deaths (XEXP (x, 1), insn, 0);
+ attach_deaths (XEXP (x, 2), insn, 0);
+ return;
+
+ default:
+ /* Other cases: walk the insn. */
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ attach_deaths (XEXP (x, i), insn, 0);
+ else if (fmt[i] == 'E')
+ for (j = 0; j < XVECLEN (x, i); j++)
+ attach_deaths (XVECEXP (x, i, j), insn, 0);
+ }
+ }
+}
+
+/* After INSN has executed, add register death notes for each register
+ that is dead after INSN. */
+
+static void
+attach_deaths_insn (insn)
+ rtx insn;
+{
+ rtx x = PATTERN (insn);
+ register RTX_CODE code = GET_CODE (x);
+ rtx link;
+
+ if (code == SET)
+ {
+ attach_deaths (SET_SRC (x), insn, 0);
+
+ /* A register might die here even if it is the destination, e.g.
+ it is the target of a volatile read and is otherwise unused.
+ Hence we must always call attach_deaths for the SET_DEST. */
+ attach_deaths (SET_DEST (x), insn, 1);
+ }
+ else if (code == PARALLEL)
+ {
+ register int i;
+ for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
+ {
+ code = GET_CODE (XVECEXP (x, 0, i));
+ if (code == SET)
+ {
+ attach_deaths (SET_SRC (XVECEXP (x, 0, i)), insn, 0);
+
+ attach_deaths (SET_DEST (XVECEXP (x, 0, i)), insn, 1);
+ }
+ /* Flow does not add REG_DEAD notes to registers that die in
+ clobbers, so we can't either. */
+ else if (code != CLOBBER)
+ attach_deaths (XVECEXP (x, 0, i), insn, 0);
+ }
+ }
+ /* If this is a CLOBBER, only add REG_DEAD notes to registers inside a
+ MEM being clobbered, just like flow. */
+ else if (code == CLOBBER && GET_CODE (XEXP (x, 0)) == MEM)
+ attach_deaths (XEXP (XEXP (x, 0), 0), insn, 0);
+ /* Otherwise don't add a death note to things being clobbered. */
+ else if (code != CLOBBER)
+ attach_deaths (x, insn, 0);
+
+ /* Make death notes for things used in the called function. */
+ if (GET_CODE (insn) == CALL_INSN)
+ for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
+ attach_deaths (XEXP (XEXP (link, 0), 0), insn,
+ GET_CODE (XEXP (link, 0)) == CLOBBER);
+}
+
+/* Delete notes beginning with INSN and maybe put them in the chain
+ of notes ended by NOTE_LIST.
+ Returns the insn following the notes. */
+
+static rtx
+unlink_notes (insn, tail)
+ rtx insn, tail;
+{
+ rtx prev = PREV_INSN (insn);
+
+ while (insn != tail && GET_CODE (insn) == NOTE)
+ {
+ rtx next = NEXT_INSN (insn);
+ /* Delete the note from its current position. */
+ if (prev)
+ NEXT_INSN (prev) = next;
+ if (next)
+ PREV_INSN (next) = prev;
+
+ if (write_symbols != NO_DEBUG && NOTE_LINE_NUMBER (insn) > 0)
+ /* Record line-number notes so they can be reused. */
+ LINE_NOTE (insn) = insn;
+
+ /* Don't save away NOTE_INSN_SETJMPs, because they must remain
+ immediately after the call they follow. We use a fake
+ (REG_DEAD (const_int -1)) note to remember them. */
+ else if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_SETJMP)
+ {
+ /* Insert the note at the end of the notes list. */
+ PREV_INSN (insn) = note_list;
+ if (note_list)
+ NEXT_INSN (note_list) = insn;
+ note_list = insn;
+ }
+
+ insn = next;
+ }
+ return insn;
+}
+
+/* Constructor for `sometimes' data structure. */
+
+static int
+new_sometimes_live (regs_sometimes_live, offset, bit, sometimes_max)
+ struct sometimes *regs_sometimes_live;
+ int offset, bit;
+ int sometimes_max;
+{
+ register struct sometimes *p;
+ register int regno = offset * REGSET_ELT_BITS + bit;
+
+ /* There should never be a register greater than max_regno here. If there
+ is, it means that a define_split has created a new pseudo reg. This
+ is not allowed, since there will not be flow info available for any
+ new register, so catch the error here. */
+ if (regno >= max_regno)
+ abort ();
+
+ p = &regs_sometimes_live[sometimes_max];
+ p->offset = offset;
+ p->bit = bit;
+ p->live_length = 0;
+ p->calls_crossed = 0;
+ sometimes_max++;
+ return sometimes_max;
+}
+
+/* Count lengths of all regs we are currently tracking,
+ and find new registers no longer live. */
+
+static void
+finish_sometimes_live (regs_sometimes_live, sometimes_max)
+ struct sometimes *regs_sometimes_live;
+ int sometimes_max;
+{
+ int i;
+
+ for (i = 0; i < sometimes_max; i++)
+ {
+ register struct sometimes *p = &regs_sometimes_live[i];
+ int regno;
+
+ regno = p->offset * REGSET_ELT_BITS + p->bit;
+
+ sched_reg_live_length[regno] += p->live_length;
+ sched_reg_n_calls_crossed[regno] += p->calls_crossed;
+ }
+}
+
+/* Use modified list scheduling to rearrange insns in basic block
+ B. FILE, if nonzero, is where we dump interesting output about
+ this pass. */
+
+static void
+schedule_block (b, file)
+ int b;
+ FILE *file;
+{
+ rtx insn, last;
+ rtx *ready, link;
+ int i, j, n_ready = 0, new_ready, n_insns = 0;
+ int sched_n_insns = 0;
+ int clock;
+#define NEED_NOTHING 0
+#define NEED_HEAD 1
+#define NEED_TAIL 2
+ int new_needs;
+
+ /* HEAD and TAIL delimit the region being scheduled. */
+ rtx head = basic_block_head[b];
+ rtx tail = basic_block_end[b];
+ /* PREV_HEAD and NEXT_TAIL are the boundaries of the insns
+ being scheduled. When the insns have been ordered,
+ these insns delimit where the new insns are to be
+ spliced back into the insn chain. */
+ rtx next_tail;
+ rtx prev_head;
+
+ /* Keep life information accurate. */
+ register struct sometimes *regs_sometimes_live;
+ int sometimes_max;
+
+ if (file)
+ fprintf (file, ";;\t -- basic block number %d from %d to %d --\n",
+ b, INSN_UID (basic_block_head[b]), INSN_UID (basic_block_end[b]));
+
+ i = max_reg_num ();
+ reg_last_uses = (rtx *) alloca (i * sizeof (rtx));
+ bzero ((char *) reg_last_uses, i * sizeof (rtx));
+ reg_last_sets = (rtx *) alloca (i * sizeof (rtx));
+ bzero ((char *) reg_last_sets, i * sizeof (rtx));
+ reg_pending_sets = (regset) alloca (regset_bytes);
+ bzero ((char *) reg_pending_sets, regset_bytes);
+ reg_pending_sets_all = 0;
+ clear_units ();
+
+ /* Remove certain insns at the beginning from scheduling,
+ by advancing HEAD. */
+
+ /* At the start of a function, before reload has run, don't delay getting
+ parameters from hard registers into pseudo registers. */
+ if (reload_completed == 0 && b == 0)
+ {
+ while (head != tail
+ && GET_CODE (head) == NOTE
+ && NOTE_LINE_NUMBER (head) != NOTE_INSN_FUNCTION_BEG)
+ head = NEXT_INSN (head);
+ while (head != tail
+ && GET_CODE (head) == INSN
+ && GET_CODE (PATTERN (head)) == SET)
+ {
+ rtx src = SET_SRC (PATTERN (head));
+ while (GET_CODE (src) == SUBREG
+ || GET_CODE (src) == SIGN_EXTEND
+ || GET_CODE (src) == ZERO_EXTEND
+ || GET_CODE (src) == SIGN_EXTRACT
+ || GET_CODE (src) == ZERO_EXTRACT)
+ src = XEXP (src, 0);
+ if (GET_CODE (src) != REG
+ || REGNO (src) >= FIRST_PSEUDO_REGISTER)
+ break;
+ /* Keep this insn from ever being scheduled. */
+ INSN_REF_COUNT (head) = 1;
+ head = NEXT_INSN (head);
+ }
+ }
+
+ /* Don't include any notes or labels at the beginning of the
+ basic block, or notes at the ends of basic blocks. */
+ while (head != tail)
+ {
+ if (GET_CODE (head) == NOTE)
+ head = NEXT_INSN (head);
+ else if (GET_CODE (tail) == NOTE)
+ tail = PREV_INSN (tail);
+ else if (GET_CODE (head) == CODE_LABEL)
+ head = NEXT_INSN (head);
+ else break;
+ }
+ /* If the only insn left is a NOTE or a CODE_LABEL, then there is no need
+ to schedule this block. */
+ if (head == tail
+ && (GET_CODE (head) == NOTE || GET_CODE (head) == CODE_LABEL))
+ return;
+
+#if 0
+ /* This short-cut doesn't work. It does not count call insns crossed by
+ registers in reg_sometimes_live. It does not mark these registers as
+ dead if they die in this block. It does not mark these registers live
+ (or create new reg_sometimes_live entries if necessary) if they are born
+ in this block.
+
+ The easy solution is to just always schedule a block. This block only
+ has one insn, so this won't slow down this pass by much. */
+
+ if (head == tail)
+ return;
+#endif
+
+ /* Now HEAD through TAIL are the insns actually to be rearranged;
+ Let PREV_HEAD and NEXT_TAIL enclose them. */
+ prev_head = PREV_INSN (head);
+ next_tail = NEXT_INSN (tail);
+
+ /* Initialize basic block data structures. */
+ dead_notes = 0;
+ pending_read_insns = 0;
+ pending_read_mems = 0;
+ pending_write_insns = 0;
+ pending_write_mems = 0;
+ pending_lists_length = 0;
+ last_pending_memory_flush = 0;
+ last_function_call = 0;
+ last_scheduled_insn = 0;
+
+ LOG_LINKS (sched_before_next_call) = 0;
+
+ n_insns += sched_analyze (head, tail);
+ if (n_insns == 0)
+ {
+ free_pending_lists ();
+ return;
+ }
+
+ /* Allocate vector to hold insns to be rearranged (except those
+ insns which are controlled by an insn with SCHED_GROUP_P set).
+ All these insns are included between ORIG_HEAD and ORIG_TAIL,
+ as those variables ultimately are set up. */
+ ready = (rtx *) alloca ((n_insns+1) * sizeof (rtx));
+
+ /* TAIL is now the last of the insns to be rearranged.
+ Put those insns into the READY vector. */
+ insn = tail;
+
+ /* For all branches, calls, uses, and cc0 setters, force them to remain
+ in order at the end of the block by adding dependencies and giving
+ the last a high priority. There may be notes present, and prev_head
+ may also be a note.
+
+ Branches must obviously remain at the end. Calls should remain at the
+ end since moving them results in worse register allocation. Uses remain
+ at the end to ensure proper register allocation. cc0 setters remaim
+ at the end because they can't be moved away from their cc0 user. */
+ last = 0;
+ while (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN
+ || (GET_CODE (insn) == INSN
+ && (GET_CODE (PATTERN (insn)) == USE
+#ifdef HAVE_cc0
+ || sets_cc0_p (PATTERN (insn))
+#endif
+ ))
+ || GET_CODE (insn) == NOTE)
+ {
+ if (GET_CODE (insn) != NOTE)
+ {
+ priority (insn);
+ if (last == 0)
+ {
+ ready[n_ready++] = insn;
+ INSN_PRIORITY (insn) = TAIL_PRIORITY - i;
+ INSN_REF_COUNT (insn) = 0;
+ }
+ else if (! find_insn_list (insn, LOG_LINKS (last)))
+ {
+ add_dependence (last, insn, REG_DEP_ANTI);
+ INSN_REF_COUNT (insn)++;
+ }
+ last = insn;
+
+ /* Skip over insns that are part of a group. */
+ while (SCHED_GROUP_P (insn))
+ {
+ insn = prev_nonnote_insn (insn);
+ priority (insn);
+ }
+ }
+
+ insn = PREV_INSN (insn);
+ /* Don't overrun the bounds of the basic block. */
+ if (insn == prev_head)
+ break;
+ }
+
+ /* Assign priorities to instructions. Also check whether they
+ are in priority order already. If so then I will be nonnegative.
+ We use this shortcut only before reloading. */
+#if 0
+ i = reload_completed ? DONE_PRIORITY : MAX_PRIORITY;
+#endif
+
+ for (; insn != prev_head; insn = PREV_INSN (insn))
+ {
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ priority (insn);
+ if (INSN_REF_COUNT (insn) == 0)
+ {
+ if (last == 0)
+ ready[n_ready++] = insn;
+ else
+ {
+ /* Make this dependent on the last of the instructions
+ that must remain in order at the end of the block. */
+ add_dependence (last, insn, REG_DEP_ANTI);
+ INSN_REF_COUNT (insn) = 1;
+ }
+ }
+ if (SCHED_GROUP_P (insn))
+ {
+ while (SCHED_GROUP_P (insn))
+ {
+ insn = PREV_INSN (insn);
+ while (GET_CODE (insn) == NOTE)
+ insn = PREV_INSN (insn);
+ priority (insn);
+ }
+ continue;
+ }
+#if 0
+ if (i < 0)
+ continue;
+ if (INSN_PRIORITY (insn) < i)
+ i = INSN_PRIORITY (insn);
+ else if (INSN_PRIORITY (insn) > i)
+ i = DONE_PRIORITY;
+#endif
+ }
+ }
+
+#if 0
+ /* This short-cut doesn't work. It does not count call insns crossed by
+ registers in reg_sometimes_live. It does not mark these registers as
+ dead if they die in this block. It does not mark these registers live
+ (or create new reg_sometimes_live entries if necessary) if they are born
+ in this block.
+
+ The easy solution is to just always schedule a block. These blocks tend
+ to be very short, so this doesn't slow down this pass by much. */
+
+ /* If existing order is good, don't bother to reorder. */
+ if (i != DONE_PRIORITY)
+ {
+ if (file)
+ fprintf (file, ";; already scheduled\n");
+
+ if (reload_completed == 0)
+ {
+ for (i = 0; i < sometimes_max; i++)
+ regs_sometimes_live[i].live_length += n_insns;
+
+ finish_sometimes_live (regs_sometimes_live, sometimes_max);
+ }
+ free_pending_lists ();
+ return;
+ }
+#endif
+
+ /* Scan all the insns to be scheduled, removing NOTE insns
+ and register death notes.
+ Line number NOTE insns end up in NOTE_LIST.
+ Register death notes end up in DEAD_NOTES.
+
+ Recreate the register life information for the end of this basic
+ block. */
+
+ if (reload_completed == 0)
+ {
+ bcopy ((char *) basic_block_live_at_start[b], (char *) bb_live_regs,
+ regset_bytes);
+ bzero ((char *) bb_dead_regs, regset_bytes);
+
+ if (b == 0)
+ {
+ /* This is the first block in the function. There may be insns
+ before head that we can't schedule. We still need to examine
+ them though for accurate register lifetime analysis. */
+
+ /* We don't want to remove any REG_DEAD notes as the code below
+ does. */
+
+ for (insn = basic_block_head[b]; insn != head;
+ insn = NEXT_INSN (insn))
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ /* See if the register gets born here. */
+ /* We must check for registers being born before we check for
+ registers dying. It is possible for a register to be born
+ and die in the same insn, e.g. reading from a volatile
+ memory location into an otherwise unused register. Such
+ a register must be marked as dead after this insn. */
+ if (GET_CODE (PATTERN (insn)) == SET
+ || GET_CODE (PATTERN (insn)) == CLOBBER)
+ sched_note_set (b, PATTERN (insn), 0);
+ else if (GET_CODE (PATTERN (insn)) == PARALLEL)
+ {
+ int j;
+ for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
+ if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET
+ || GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER)
+ sched_note_set (b, XVECEXP (PATTERN (insn), 0, j), 0);
+
+ /* ??? This code is obsolete and should be deleted. It
+ is harmless though, so we will leave it in for now. */
+ for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
+ if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == USE)
+ sched_note_set (b, XVECEXP (PATTERN (insn), 0, j), 0);
+ }
+
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+ {
+ if ((REG_NOTE_KIND (link) == REG_DEAD
+ || REG_NOTE_KIND (link) == REG_UNUSED)
+ /* Verify that the REG_NOTE has a legal value. */
+ && GET_CODE (XEXP (link, 0)) == REG)
+ {
+ register int regno = REGNO (XEXP (link, 0));
+ register int offset = regno / REGSET_ELT_BITS;
+ register REGSET_ELT_TYPE bit
+ = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS);
+
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ int j = HARD_REGNO_NREGS (regno,
+ GET_MODE (XEXP (link, 0)));
+ while (--j >= 0)
+ {
+ offset = (regno + j) / REGSET_ELT_BITS;
+ bit = ((REGSET_ELT_TYPE) 1
+ << ((regno + j) % REGSET_ELT_BITS));
+
+ bb_live_regs[offset] &= ~bit;
+ bb_dead_regs[offset] |= bit;
+ }
+ }
+ else
+ {
+ bb_live_regs[offset] &= ~bit;
+ bb_dead_regs[offset] |= bit;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* If debugging information is being produced, keep track of the line
+ number notes for each insn. */
+ if (write_symbols != NO_DEBUG)
+ {
+ /* We must use the true line number for the first insn in the block
+ that was computed and saved at the start of this pass. We can't
+ use the current line number, because scheduling of the previous
+ block may have changed the current line number. */
+ rtx line = line_note_head[b];
+
+ for (insn = basic_block_head[b];
+ insn != next_tail;
+ insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
+ line = insn;
+ else
+ LINE_NOTE (insn) = line;
+ }
+
+ for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
+ {
+ rtx prev, next, link;
+
+ /* Farm out notes. This is needed to keep the debugger from
+ getting completely deranged. */
+ if (GET_CODE (insn) == NOTE)
+ {
+ prev = insn;
+ insn = unlink_notes (insn, next_tail);
+ if (prev == tail)
+ abort ();
+ if (prev == head)
+ abort ();
+ if (insn == next_tail)
+ abort ();
+ }
+
+ if (reload_completed == 0
+ && GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ /* See if the register gets born here. */
+ /* We must check for registers being born before we check for
+ registers dying. It is possible for a register to be born and
+ die in the same insn, e.g. reading from a volatile memory
+ location into an otherwise unused register. Such a register
+ must be marked as dead after this insn. */
+ if (GET_CODE (PATTERN (insn)) == SET
+ || GET_CODE (PATTERN (insn)) == CLOBBER)
+ sched_note_set (b, PATTERN (insn), 0);
+ else if (GET_CODE (PATTERN (insn)) == PARALLEL)
+ {
+ int j;
+ for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
+ if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET
+ || GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER)
+ sched_note_set (b, XVECEXP (PATTERN (insn), 0, j), 0);
+
+ /* ??? This code is obsolete and should be deleted. It
+ is harmless though, so we will leave it in for now. */
+ for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
+ if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == USE)
+ sched_note_set (b, XVECEXP (PATTERN (insn), 0, j), 0);
+ }
+
+ /* Need to know what registers this insn kills. */
+ for (prev = 0, link = REG_NOTES (insn); link; link = next)
+ {
+ next = XEXP (link, 1);
+ if ((REG_NOTE_KIND (link) == REG_DEAD
+ || REG_NOTE_KIND (link) == REG_UNUSED)
+ /* Verify that the REG_NOTE has a legal value. */
+ && GET_CODE (XEXP (link, 0)) == REG)
+ {
+ register int regno = REGNO (XEXP (link, 0));
+ register int offset = regno / REGSET_ELT_BITS;
+ register REGSET_ELT_TYPE bit
+ = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS);
+
+ /* Only unlink REG_DEAD notes; leave REG_UNUSED notes
+ alone. */
+ if (REG_NOTE_KIND (link) == REG_DEAD)
+ {
+ if (prev)
+ XEXP (prev, 1) = next;
+ else
+ REG_NOTES (insn) = next;
+ XEXP (link, 1) = dead_notes;
+ dead_notes = link;
+ }
+ else
+ prev = link;
+
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ int j = HARD_REGNO_NREGS (regno,
+ GET_MODE (XEXP (link, 0)));
+ while (--j >= 0)
+ {
+ offset = (regno + j) / REGSET_ELT_BITS;
+ bit = ((REGSET_ELT_TYPE) 1
+ << ((regno + j) % REGSET_ELT_BITS));
+
+ bb_live_regs[offset] &= ~bit;
+ bb_dead_regs[offset] |= bit;
+ }
+ }
+ else
+ {
+ bb_live_regs[offset] &= ~bit;
+ bb_dead_regs[offset] |= bit;
+ }
+ }
+ else
+ prev = link;
+ }
+ }
+ }
+
+ if (reload_completed == 0)
+ {
+ /* Keep track of register lives. */
+ old_live_regs = (regset) alloca (regset_bytes);
+ regs_sometimes_live
+ = (struct sometimes *) alloca (max_regno * sizeof (struct sometimes));
+ sometimes_max = 0;
+
+ /* Start with registers live at end. */
+ for (j = 0; j < regset_size; j++)
+ {
+ REGSET_ELT_TYPE live = bb_live_regs[j];
+ old_live_regs[j] = live;
+ if (live)
+ {
+ register int bit;
+ for (bit = 0; bit < REGSET_ELT_BITS; bit++)
+ if (live & ((REGSET_ELT_TYPE) 1 << bit))
+ sometimes_max = new_sometimes_live (regs_sometimes_live, j,
+ bit, sometimes_max);
+ }
+ }
+ }
+
+ SCHED_SORT (ready, n_ready, 1);
+
+ if (file)
+ {
+ fprintf (file, ";; ready list initially:\n;; ");
+ for (i = 0; i < n_ready; i++)
+ fprintf (file, "%d ", INSN_UID (ready[i]));
+ fprintf (file, "\n\n");
+
+ for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
+ if (INSN_PRIORITY (insn) > 0)
+ fprintf (file, ";; insn[%4d]: priority = %4d, ref_count = %4d\n",
+ INSN_UID (insn), INSN_PRIORITY (insn),
+ INSN_REF_COUNT (insn));
+ }
+
+ /* Now HEAD and TAIL are going to become disconnected
+ entirely from the insn chain. */
+ tail = 0;
+
+ /* Q_SIZE will always be zero here. */
+ q_ptr = 0; clock = 0;
+ bzero ((char *) insn_queue, sizeof (insn_queue));
+
+ /* Now, perform list scheduling. */
+
+ /* Where we start inserting insns is after TAIL. */
+ last = next_tail;
+
+ new_needs = (NEXT_INSN (prev_head) == basic_block_head[b]
+ ? NEED_HEAD : NEED_NOTHING);
+ if (PREV_INSN (next_tail) == basic_block_end[b])
+ new_needs |= NEED_TAIL;
+
+ new_ready = n_ready;
+ while (sched_n_insns < n_insns)
+ {
+ q_ptr = NEXT_Q (q_ptr); clock++;
+
+ /* Add all pending insns that can be scheduled without stalls to the
+ ready list. */
+ for (insn = insn_queue[q_ptr]; insn; insn = NEXT_INSN (insn))
+ {
+ if (file)
+ fprintf (file, ";; launching %d before %d with no stalls at T-%d\n",
+ INSN_UID (insn), INSN_UID (last), clock);
+ ready[new_ready++] = insn;
+ q_size -= 1;
+ }
+ insn_queue[q_ptr] = 0;
+
+ /* If there are no ready insns, stall until one is ready and add all
+ of the pending insns at that point to the ready list. */
+ if (new_ready == 0)
+ {
+ register int stalls;
+
+ for (stalls = 1; stalls < INSN_QUEUE_SIZE; stalls++)
+ if (insn = insn_queue[NEXT_Q_AFTER (q_ptr, stalls)])
+ {
+ for (; insn; insn = NEXT_INSN (insn))
+ {
+ if (file)
+ fprintf (file, ";; launching %d before %d with %d stalls at T-%d\n",
+ INSN_UID (insn), INSN_UID (last), stalls, clock);
+ ready[new_ready++] = insn;
+ q_size -= 1;
+ }
+ insn_queue[NEXT_Q_AFTER (q_ptr, stalls)] = 0;
+ break;
+ }
+
+ q_ptr = NEXT_Q_AFTER (q_ptr, stalls); clock += stalls;
+ }
+
+ /* There should be some instructions waiting to fire. */
+ if (new_ready == 0)
+ abort ();
+
+ if (file)
+ {
+ fprintf (file, ";; ready list at T-%d:", clock);
+ for (i = 0; i < new_ready; i++)
+ fprintf (file, " %d (%x)",
+ INSN_UID (ready[i]), INSN_PRIORITY (ready[i]));
+ }
+
+ /* Sort the ready list and choose the best insn to schedule. Select
+ which insn should issue in this cycle and queue those that are
+ blocked by function unit hazards.
+
+ N_READY holds the number of items that were scheduled the last time,
+ minus the one instruction scheduled on the last loop iteration; it
+ is not modified for any other reason in this loop. */
+
+ SCHED_SORT (ready, new_ready, n_ready);
+ if (MAX_BLOCKAGE > 1)
+ {
+ new_ready = schedule_select (ready, new_ready, clock, file);
+ if (new_ready == 0)
+ {
+ if (file)
+ fprintf (file, "\n");
+ /* We must set n_ready here, to ensure that sorting always
+ occurs when we come back to the SCHED_SORT line above. */
+ n_ready = 0;
+ continue;
+ }
+ }
+ n_ready = new_ready;
+ last_scheduled_insn = insn = ready[0];
+
+ /* The first insn scheduled becomes the new tail. */
+ if (tail == 0)
+ tail = insn;
+
+ if (file)
+ {
+ fprintf (file, ", now");
+ for (i = 0; i < n_ready; i++)
+ fprintf (file, " %d", INSN_UID (ready[i]));
+ fprintf (file, "\n");
+ }
+
+ if (DONE_PRIORITY_P (insn))
+ abort ();
+
+ if (reload_completed == 0)
+ {
+ /* Process this insn, and each insn linked to this one which must
+ be immediately output after this insn. */
+ do
+ {
+ /* First we kill registers set by this insn, and then we
+ make registers used by this insn live. This is the opposite
+ order used above because we are traversing the instructions
+ backwards. */
+
+ /* Strictly speaking, we should scan REG_UNUSED notes and make
+ every register mentioned there live, however, we will just
+ kill them again immediately below, so there doesn't seem to
+ be any reason why we bother to do this. */
+
+ /* See if this is the last notice we must take of a register. */
+ if (GET_CODE (PATTERN (insn)) == SET
+ || GET_CODE (PATTERN (insn)) == CLOBBER)
+ sched_note_set (b, PATTERN (insn), 1);
+ else if (GET_CODE (PATTERN (insn)) == PARALLEL)
+ {
+ int j;
+ for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
+ if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET
+ || GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER)
+ sched_note_set (b, XVECEXP (PATTERN (insn), 0, j), 1);
+ }
+
+ /* This code keeps life analysis information up to date. */
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ register struct sometimes *p;
+
+ /* A call kills all call used and global registers, except
+ for those mentioned in the call pattern which will be
+ made live again later. */
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (call_used_regs[i] || global_regs[i])
+ {
+ register int offset = i / REGSET_ELT_BITS;
+ register REGSET_ELT_TYPE bit
+ = (REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS);
+
+ bb_live_regs[offset] &= ~bit;
+ bb_dead_regs[offset] |= bit;
+ }
+
+ /* Regs live at the time of a call instruction must not
+ go in a register clobbered by calls. Record this for
+ all regs now live. Note that insns which are born or
+ die in a call do not cross a call, so this must be done
+ after the killings (above) and before the births
+ (below). */
+ p = regs_sometimes_live;
+ for (i = 0; i < sometimes_max; i++, p++)
+ if (bb_live_regs[p->offset]
+ & ((REGSET_ELT_TYPE) 1 << p->bit))
+ p->calls_crossed += 1;
+ }
+
+ /* Make every register used live, and add REG_DEAD notes for
+ registers which were not live before we started. */
+ attach_deaths_insn (insn);
+
+ /* Find registers now made live by that instruction. */
+ for (i = 0; i < regset_size; i++)
+ {
+ REGSET_ELT_TYPE diff = bb_live_regs[i] & ~old_live_regs[i];
+ if (diff)
+ {
+ register int bit;
+ old_live_regs[i] |= diff;
+ for (bit = 0; bit < REGSET_ELT_BITS; bit++)
+ if (diff & ((REGSET_ELT_TYPE) 1 << bit))
+ sometimes_max
+ = new_sometimes_live (regs_sometimes_live, i, bit,
+ sometimes_max);
+ }
+ }
+
+ /* Count lengths of all regs we are worrying about now,
+ and handle registers no longer live. */
+
+ for (i = 0; i < sometimes_max; i++)
+ {
+ register struct sometimes *p = &regs_sometimes_live[i];
+ int regno = p->offset*REGSET_ELT_BITS + p->bit;
+
+ p->live_length += 1;
+
+ if ((bb_live_regs[p->offset]
+ & ((REGSET_ELT_TYPE) 1 << p->bit)) == 0)
+ {
+ /* This is the end of one of this register's lifetime
+ segments. Save the lifetime info collected so far,
+ and clear its bit in the old_live_regs entry. */
+ sched_reg_live_length[regno] += p->live_length;
+ sched_reg_n_calls_crossed[regno] += p->calls_crossed;
+ old_live_regs[p->offset]
+ &= ~((REGSET_ELT_TYPE) 1 << p->bit);
+
+ /* Delete the reg_sometimes_live entry for this reg by
+ copying the last entry over top of it. */
+ *p = regs_sometimes_live[--sometimes_max];
+ /* ...and decrement i so that this newly copied entry
+ will be processed. */
+ i--;
+ }
+ }
+
+ link = insn;
+ insn = PREV_INSN (insn);
+ }
+ while (SCHED_GROUP_P (link));
+
+ /* Set INSN back to the insn we are scheduling now. */
+ insn = ready[0];
+ }
+
+ /* Schedule INSN. Remove it from the ready list. */
+ ready += 1;
+ n_ready -= 1;
+
+ sched_n_insns += 1;
+ NEXT_INSN (insn) = last;
+ PREV_INSN (last) = insn;
+ last = insn;
+
+ /* Check to see if we need to re-emit a NOTE_INSN_SETJMP here. */
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ rtx note = find_reg_note (insn, REG_DEAD, constm1_rtx);
+
+ if (note)
+ {
+ emit_note_after (NOTE_INSN_SETJMP, insn);
+ remove_note (insn, note);
+ }
+ }
+
+ /* Everything that precedes INSN now either becomes "ready", if
+ it can execute immediately before INSN, or "pending", if
+ there must be a delay. Give INSN high enough priority that
+ at least one (maybe more) reg-killing insns can be launched
+ ahead of all others. Mark INSN as scheduled by changing its
+ priority to -1. */
+ INSN_PRIORITY (insn) = LAUNCH_PRIORITY;
+ new_ready = schedule_insn (insn, ready, n_ready, clock);
+ INSN_PRIORITY (insn) = DONE_PRIORITY;
+
+ /* Schedule all prior insns that must not be moved. */
+ if (SCHED_GROUP_P (insn))
+ {
+ /* Disable these insns from being launched. */
+ link = insn;
+ while (SCHED_GROUP_P (link))
+ {
+ /* Disable these insns from being launched by anybody. */
+ link = PREV_INSN (link);
+ INSN_REF_COUNT (link) = 0;
+ }
+
+ /* None of these insns can move forward into delay slots. */
+ while (SCHED_GROUP_P (insn))
+ {
+ insn = PREV_INSN (insn);
+ new_ready = schedule_insn (insn, ready, new_ready, clock);
+ INSN_PRIORITY (insn) = DONE_PRIORITY;
+
+ sched_n_insns += 1;
+ NEXT_INSN (insn) = last;
+ PREV_INSN (last) = insn;
+ last = insn;
+ }
+ }
+ }
+ if (q_size != 0)
+ abort ();
+
+ if (reload_completed == 0)
+ finish_sometimes_live (regs_sometimes_live, sometimes_max);
+
+ /* HEAD is now the first insn in the chain of insns that
+ been scheduled by the loop above.
+ TAIL is the last of those insns. */
+ head = insn;
+
+ /* NOTE_LIST is the end of a chain of notes previously found
+ among the insns. Insert them at the beginning of the insns. */
+ if (note_list != 0)
+ {
+ rtx note_head = note_list;
+ while (PREV_INSN (note_head))
+ note_head = PREV_INSN (note_head);
+
+ PREV_INSN (head) = note_list;
+ NEXT_INSN (note_list) = head;
+ head = note_head;
+ }
+
+ /* There should be no REG_DEAD notes leftover at the end.
+ In practice, this can occur as the result of bugs in flow, combine.c,
+ and/or sched.c. The values of the REG_DEAD notes remaining are
+ meaningless, because dead_notes is just used as a free list. */
+#if 1
+ if (dead_notes != 0)
+ abort ();
+#endif
+
+ if (new_needs & NEED_HEAD)
+ basic_block_head[b] = head;
+ PREV_INSN (head) = prev_head;
+ NEXT_INSN (prev_head) = head;
+
+ if (new_needs & NEED_TAIL)
+ basic_block_end[b] = tail;
+ NEXT_INSN (tail) = next_tail;
+ PREV_INSN (next_tail) = tail;
+
+ /* Restore the line-number notes of each insn. */
+ if (write_symbols != NO_DEBUG)
+ {
+ rtx line, note, prev, new;
+ int notes = 0;
+
+ head = basic_block_head[b];
+ next_tail = NEXT_INSN (basic_block_end[b]);
+
+ /* Determine the current line-number. We want to know the current
+ line number of the first insn of the block here, in case it is
+ different from the true line number that was saved earlier. If
+ different, then we need a line number note before the first insn
+ of this block. If it happens to be the same, then we don't want to
+ emit another line number note here. */
+ for (line = head; line; line = PREV_INSN (line))
+ if (GET_CODE (line) == NOTE && NOTE_LINE_NUMBER (line) > 0)
+ break;
+
+ /* Walk the insns keeping track of the current line-number and inserting
+ the line-number notes as needed. */
+ for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
+ line = insn;
+ /* This used to emit line number notes before every non-deleted note.
+ However, this confuses a debugger, because line notes not separated
+ by real instructions all end up at the same address. I can find no
+ use for line number notes before other notes, so none are emitted. */
+ else if (GET_CODE (insn) != NOTE
+ && (note = LINE_NOTE (insn)) != 0
+ && note != line
+ && (line == 0
+ || NOTE_LINE_NUMBER (note) != NOTE_LINE_NUMBER (line)
+ || NOTE_SOURCE_FILE (note) != NOTE_SOURCE_FILE (line)))
+ {
+ line = note;
+ prev = PREV_INSN (insn);
+ if (LINE_NOTE (note))
+ {
+ /* Re-use the original line-number note. */
+ LINE_NOTE (note) = 0;
+ PREV_INSN (note) = prev;
+ NEXT_INSN (prev) = note;
+ PREV_INSN (insn) = note;
+ NEXT_INSN (note) = insn;
+ }
+ else
+ {
+ notes++;
+ new = emit_note_after (NOTE_LINE_NUMBER (note), prev);
+ NOTE_SOURCE_FILE (new) = NOTE_SOURCE_FILE (note);
+ }
+ }
+ if (file && notes)
+ fprintf (file, ";; added %d line-number notes\n", notes);
+ }
+
+ if (file)
+ {
+ fprintf (file, ";; total time = %d\n;; new basic block head = %d\n;; new basic block end = %d\n\n",
+ clock, INSN_UID (basic_block_head[b]), INSN_UID (basic_block_end[b]));
+ }
+
+ /* Yow! We're done! */
+ free_pending_lists ();
+
+ return;
+}
+
+/* Subroutine of split_hard_reg_notes. Searches X for any reference to
+ REGNO, returning the rtx of the reference found if any. Otherwise,
+ returns 0. */
+
+static rtx
+regno_use_in (regno, x)
+ int regno;
+ rtx x;
+{
+ register char *fmt;
+ int i, j;
+ rtx tem;
+
+ if (GET_CODE (x) == REG && REGNO (x) == regno)
+ return x;
+
+ fmt = GET_RTX_FORMAT (GET_CODE (x));
+ for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ {
+ if (tem = regno_use_in (regno, XEXP (x, i)))
+ return tem;
+ }
+ else if (fmt[i] == 'E')
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (tem = regno_use_in (regno , XVECEXP (x, i, j)))
+ return tem;
+ }
+
+ return 0;
+}
+
+/* Subroutine of update_flow_info. Determines whether any new REG_NOTEs are
+ needed for the hard register mentioned in the note. This can happen
+ if the reference to the hard register in the original insn was split into
+ several smaller hard register references in the split insns. */
+
+static void
+split_hard_reg_notes (note, first, last, orig_insn)
+ rtx note, first, last, orig_insn;
+{
+ rtx reg, temp, link;
+ int n_regs, i, new_reg;
+ rtx insn;
+
+ /* Assume that this is a REG_DEAD note. */
+ if (REG_NOTE_KIND (note) != REG_DEAD)
+ abort ();
+
+ reg = XEXP (note, 0);
+
+ n_regs = HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg));
+
+ for (i = 0; i < n_regs; i++)
+ {
+ new_reg = REGNO (reg) + i;
+
+ /* Check for references to new_reg in the split insns. */
+ for (insn = last; ; insn = PREV_INSN (insn))
+ {
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && (temp = regno_use_in (new_reg, PATTERN (insn))))
+ {
+ /* Create a new reg dead note here. */
+ link = rtx_alloc (EXPR_LIST);
+ PUT_REG_NOTE_KIND (link, REG_DEAD);
+ XEXP (link, 0) = temp;
+ XEXP (link, 1) = REG_NOTES (insn);
+ REG_NOTES (insn) = link;
+
+ /* If killed multiple registers here, then add in the excess. */
+ i += HARD_REGNO_NREGS (REGNO (temp), GET_MODE (temp)) - 1;
+
+ break;
+ }
+ /* It isn't mentioned anywhere, so no new reg note is needed for
+ this register. */
+ if (insn == first)
+ break;
+ }
+ }
+}
+
+/* Subroutine of update_flow_info. Determines whether a SET or CLOBBER in an
+ insn created by splitting needs a REG_DEAD or REG_UNUSED note added. */
+
+static void
+new_insn_dead_notes (pat, insn, last, orig_insn)
+ rtx pat, insn, last, orig_insn;
+{
+ rtx dest, tem, set;
+
+ /* PAT is either a CLOBBER or a SET here. */
+ dest = XEXP (pat, 0);
+
+ while (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SUBREG
+ || GET_CODE (dest) == STRICT_LOW_PART
+ || GET_CODE (dest) == SIGN_EXTRACT)
+ dest = XEXP (dest, 0);
+
+ if (GET_CODE (dest) == REG)
+ {
+ for (tem = last; tem != insn; tem = PREV_INSN (tem))
+ {
+ if (GET_RTX_CLASS (GET_CODE (tem)) == 'i'
+ && reg_overlap_mentioned_p (dest, PATTERN (tem))
+ && (set = single_set (tem)))
+ {
+ rtx tem_dest = SET_DEST (set);
+
+ while (GET_CODE (tem_dest) == ZERO_EXTRACT
+ || GET_CODE (tem_dest) == SUBREG
+ || GET_CODE (tem_dest) == STRICT_LOW_PART
+ || GET_CODE (tem_dest) == SIGN_EXTRACT)
+ tem_dest = XEXP (tem_dest, 0);
+
+ if (! rtx_equal_p (tem_dest, dest))
+ {
+ /* Use the same scheme as combine.c, don't put both REG_DEAD
+ and REG_UNUSED notes on the same insn. */
+ if (! find_regno_note (tem, REG_UNUSED, REGNO (dest))
+ && ! find_regno_note (tem, REG_DEAD, REGNO (dest)))
+ {
+ rtx note = rtx_alloc (EXPR_LIST);
+ PUT_REG_NOTE_KIND (note, REG_DEAD);
+ XEXP (note, 0) = dest;
+ XEXP (note, 1) = REG_NOTES (tem);
+ REG_NOTES (tem) = note;
+ }
+ /* The reg only dies in one insn, the last one that uses
+ it. */
+ break;
+ }
+ else if (reg_overlap_mentioned_p (dest, SET_SRC (set)))
+ /* We found an instruction that both uses the register,
+ and sets it, so no new REG_NOTE is needed for this set. */
+ break;
+ }
+ }
+ /* If this is a set, it must die somewhere, unless it is the dest of
+ the original insn, and hence is live after the original insn. Abort
+ if it isn't supposed to be live after the original insn.
+
+ If this is a clobber, then just add a REG_UNUSED note. */
+ if (tem == insn)
+ {
+ int live_after_orig_insn = 0;
+ rtx pattern = PATTERN (orig_insn);
+ int i;
+
+ if (GET_CODE (pat) == CLOBBER)
+ {
+ rtx note = rtx_alloc (EXPR_LIST);
+ PUT_REG_NOTE_KIND (note, REG_UNUSED);
+ XEXP (note, 0) = dest;
+ XEXP (note, 1) = REG_NOTES (insn);
+ REG_NOTES (insn) = note;
+ return;
+ }
+
+ /* The original insn could have multiple sets, so search the
+ insn for all sets. */
+ if (GET_CODE (pattern) == SET)
+ {
+ if (reg_overlap_mentioned_p (dest, SET_DEST (pattern)))
+ live_after_orig_insn = 1;
+ }
+ else if (GET_CODE (pattern) == PARALLEL)
+ {
+ for (i = 0; i < XVECLEN (pattern, 0); i++)
+ if (GET_CODE (XVECEXP (pattern, 0, i)) == SET
+ && reg_overlap_mentioned_p (dest,
+ SET_DEST (XVECEXP (pattern,
+ 0, i))))
+ live_after_orig_insn = 1;
+ }
+
+ if (! live_after_orig_insn)
+ abort ();
+ }
+ }
+}
+
+/* Subroutine of update_flow_info. Update the value of reg_n_sets for all
+ registers modified by X. INC is -1 if the containing insn is being deleted,
+ and is 1 if the containing insn is a newly generated insn. */
+
+static void
+update_n_sets (x, inc)
+ rtx x;
+ int inc;
+{
+ rtx dest = SET_DEST (x);
+
+ while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SUBREG
+ || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT)
+ dest = SUBREG_REG (dest);
+
+ if (GET_CODE (dest) == REG)
+ {
+ int regno = REGNO (dest);
+
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ register int i;
+ int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (dest));
+
+ for (i = regno; i < endregno; i++)
+ reg_n_sets[i] += inc;
+ }
+ else
+ reg_n_sets[regno] += inc;
+ }
+}
+
+/* Updates all flow-analysis related quantities (including REG_NOTES) for
+ the insns from FIRST to LAST inclusive that were created by splitting
+ ORIG_INSN. NOTES are the original REG_NOTES. */
+
+static void
+update_flow_info (notes, first, last, orig_insn)
+ rtx notes;
+ rtx first, last;
+ rtx orig_insn;
+{
+ rtx insn, note;
+ rtx next;
+ rtx orig_dest, temp;
+ rtx set;
+
+ /* Get and save the destination set by the original insn. */
+
+ orig_dest = single_set (orig_insn);
+ if (orig_dest)
+ orig_dest = SET_DEST (orig_dest);
+
+ /* Move REG_NOTES from the original insn to where they now belong. */
+
+ for (note = notes; note; note = next)
+ {
+ next = XEXP (note, 1);
+ switch (REG_NOTE_KIND (note))
+ {
+ case REG_DEAD:
+ case REG_UNUSED:
+ /* Move these notes from the original insn to the last new insn where
+ the register is now set. */
+
+ for (insn = last; ; insn = PREV_INSN (insn))
+ {
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && reg_mentioned_p (XEXP (note, 0), PATTERN (insn)))
+ {
+ /* If this note refers to a multiple word hard register, it
+ may have been split into several smaller hard register
+ references, so handle it specially. */
+ temp = XEXP (note, 0);
+ if (REG_NOTE_KIND (note) == REG_DEAD
+ && GET_CODE (temp) == REG
+ && REGNO (temp) < FIRST_PSEUDO_REGISTER
+ && HARD_REGNO_NREGS (REGNO (temp), GET_MODE (temp)) > 1)
+ split_hard_reg_notes (note, first, last, orig_insn);
+ else
+ {
+ XEXP (note, 1) = REG_NOTES (insn);
+ REG_NOTES (insn) = note;
+ }
+
+ /* Sometimes need to convert REG_UNUSED notes to REG_DEAD
+ notes. */
+ /* ??? This won't handle multiple word registers correctly,
+ but should be good enough for now. */
+ if (REG_NOTE_KIND (note) == REG_UNUSED
+ && ! dead_or_set_p (insn, XEXP (note, 0)))
+ PUT_REG_NOTE_KIND (note, REG_DEAD);
+
+ /* The reg only dies in one insn, the last one that uses
+ it. */
+ break;
+ }
+ /* It must die somewhere, fail it we couldn't find where it died.
+
+ If this is a REG_UNUSED note, then it must be a temporary
+ register that was not needed by this instantiation of the
+ pattern, so we can safely ignore it. */
+ if (insn == first)
+ {
+ if (REG_NOTE_KIND (note) != REG_UNUSED)
+ abort ();
+
+ break;
+ }
+ }
+ break;
+
+ case REG_WAS_0:
+ /* This note applies to the dest of the original insn. Find the
+ first new insn that now has the same dest, and move the note
+ there. */
+
+ if (! orig_dest)
+ abort ();
+
+ for (insn = first; ; insn = NEXT_INSN (insn))
+ {
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && (temp = single_set (insn))
+ && rtx_equal_p (SET_DEST (temp), orig_dest))
+ {
+ XEXP (note, 1) = REG_NOTES (insn);
+ REG_NOTES (insn) = note;
+ /* The reg is only zero before one insn, the first that
+ uses it. */
+ break;
+ }
+ /* It must be set somewhere, fail if we couldn't find where it
+ was set. */
+ if (insn == last)
+ abort ();
+ }
+ break;
+
+ case REG_EQUAL:
+ case REG_EQUIV:
+ /* A REG_EQUIV or REG_EQUAL note on an insn with more than one
+ set is meaningless. Just drop the note. */
+ if (! orig_dest)
+ break;
+
+ case REG_NO_CONFLICT:
+ /* These notes apply to the dest of the original insn. Find the last
+ new insn that now has the same dest, and move the note there. */
+
+ if (! orig_dest)
+ abort ();
+
+ for (insn = last; ; insn = PREV_INSN (insn))
+ {
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && (temp = single_set (insn))
+ && rtx_equal_p (SET_DEST (temp), orig_dest))
+ {
+ XEXP (note, 1) = REG_NOTES (insn);
+ REG_NOTES (insn) = note;
+ /* Only put this note on one of the new insns. */
+ break;
+ }
+
+ /* The original dest must still be set someplace. Abort if we
+ couldn't find it. */
+ if (insn == first)
+ abort ();
+ }
+ break;
+
+ case REG_LIBCALL:
+ /* Move a REG_LIBCALL note to the first insn created, and update
+ the corresponding REG_RETVAL note. */
+ XEXP (note, 1) = REG_NOTES (first);
+ REG_NOTES (first) = note;
+
+ insn = XEXP (note, 0);
+ note = find_reg_note (insn, REG_RETVAL, NULL_RTX);
+ if (note)
+ XEXP (note, 0) = first;
+ break;
+
+ case REG_RETVAL:
+ /* Move a REG_RETVAL note to the last insn created, and update
+ the corresponding REG_LIBCALL note. */
+ XEXP (note, 1) = REG_NOTES (last);
+ REG_NOTES (last) = note;
+
+ insn = XEXP (note, 0);
+ note = find_reg_note (insn, REG_LIBCALL, NULL_RTX);
+ if (note)
+ XEXP (note, 0) = last;
+ break;
+
+ case REG_NONNEG:
+ /* This should be moved to whichever instruction is a JUMP_INSN. */
+
+ for (insn = last; ; insn = PREV_INSN (insn))
+ {
+ if (GET_CODE (insn) == JUMP_INSN)
+ {
+ XEXP (note, 1) = REG_NOTES (insn);
+ REG_NOTES (insn) = note;
+ /* Only put this note on one of the new insns. */
+ break;
+ }
+ /* Fail if we couldn't find a JUMP_INSN. */
+ if (insn == first)
+ abort ();
+ }
+ break;
+
+ case REG_INC:
+ /* This should be moved to whichever instruction now has the
+ increment operation. */
+ abort ();
+
+ case REG_LABEL:
+ /* Should be moved to the new insn(s) which use the label. */
+ for (insn = first; insn != NEXT_INSN (last); insn = NEXT_INSN (insn))
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && reg_mentioned_p (XEXP (note, 0), PATTERN (insn)))
+ REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_LABEL,
+ XEXP (note, 0), REG_NOTES (insn));
+ break;
+
+ case REG_CC_SETTER:
+ case REG_CC_USER:
+ /* These two notes will never appear until after reorg, so we don't
+ have to handle them here. */
+ default:
+ abort ();
+ }
+ }
+
+ /* Each new insn created, except the last, has a new set. If the destination
+ is a register, then this reg is now live across several insns, whereas
+ previously the dest reg was born and died within the same insn. To
+ reflect this, we now need a REG_DEAD note on the insn where this
+ dest reg dies.
+
+ Similarly, the new insns may have clobbers that need REG_UNUSED notes. */
+
+ for (insn = first; insn != last; insn = NEXT_INSN (insn))
+ {
+ rtx pat;
+ int i;
+
+ pat = PATTERN (insn);
+ if (GET_CODE (pat) == SET || GET_CODE (pat) == CLOBBER)
+ new_insn_dead_notes (pat, insn, last, orig_insn);
+ else if (GET_CODE (pat) == PARALLEL)
+ {
+ for (i = 0; i < XVECLEN (pat, 0); i++)
+ if (GET_CODE (XVECEXP (pat, 0, i)) == SET
+ || GET_CODE (XVECEXP (pat, 0, i)) == CLOBBER)
+ new_insn_dead_notes (XVECEXP (pat, 0, i), insn, last, orig_insn);
+ }
+ }
+
+ /* If any insn, except the last, uses the register set by the last insn,
+ then we need a new REG_DEAD note on that insn. In this case, there
+ would not have been a REG_DEAD note for this register in the original
+ insn because it was used and set within one insn.
+
+ There is no new REG_DEAD note needed if the last insn uses the register
+ that it is setting. */
+
+ set = single_set (last);
+ if (set)
+ {
+ rtx dest = SET_DEST (set);
+
+ while (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SUBREG
+ || GET_CODE (dest) == STRICT_LOW_PART
+ || GET_CODE (dest) == SIGN_EXTRACT)
+ dest = XEXP (dest, 0);
+
+ if (GET_CODE (dest) == REG
+ && ! reg_overlap_mentioned_p (dest, SET_SRC (set)))
+ {
+ for (insn = PREV_INSN (last); ; insn = PREV_INSN (insn))
+ {
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && reg_mentioned_p (dest, PATTERN (insn))
+ && (set = single_set (insn)))
+ {
+ rtx insn_dest = SET_DEST (set);
+
+ while (GET_CODE (insn_dest) == ZERO_EXTRACT
+ || GET_CODE (insn_dest) == SUBREG
+ || GET_CODE (insn_dest) == STRICT_LOW_PART
+ || GET_CODE (insn_dest) == SIGN_EXTRACT)
+ insn_dest = XEXP (insn_dest, 0);
+
+ if (insn_dest != dest)
+ {
+ note = rtx_alloc (EXPR_LIST);
+ PUT_REG_NOTE_KIND (note, REG_DEAD);
+ XEXP (note, 0) = dest;
+ XEXP (note, 1) = REG_NOTES (insn);
+ REG_NOTES (insn) = note;
+ /* The reg only dies in one insn, the last one
+ that uses it. */
+ break;
+ }
+ }
+ if (insn == first)
+ break;
+ }
+ }
+ }
+
+ /* If the original dest is modifying a multiple register target, and the
+ original instruction was split such that the original dest is now set
+ by two or more SUBREG sets, then the split insns no longer kill the
+ destination of the original insn.
+
+ In this case, if there exists an instruction in the same basic block,
+ before the split insn, which uses the original dest, and this use is
+ killed by the original insn, then we must remove the REG_DEAD note on
+ this insn, because it is now superfluous.
+
+ This does not apply when a hard register gets split, because the code
+ knows how to handle overlapping hard registers properly. */
+ if (orig_dest && GET_CODE (orig_dest) == REG)
+ {
+ int found_orig_dest = 0;
+ int found_split_dest = 0;
+
+ for (insn = first; ; insn = NEXT_INSN (insn))
+ {
+ set = single_set (insn);
+ if (set)
+ {
+ if (GET_CODE (SET_DEST (set)) == REG
+ && REGNO (SET_DEST (set)) == REGNO (orig_dest))
+ {
+ found_orig_dest = 1;
+ break;
+ }
+ else if (GET_CODE (SET_DEST (set)) == SUBREG
+ && SUBREG_REG (SET_DEST (set)) == orig_dest)
+ {
+ found_split_dest = 1;
+ break;
+ }
+ }
+
+ if (insn == last)
+ break;
+ }
+
+ if (found_split_dest)
+ {
+ /* Search backwards from FIRST, looking for the first insn that uses
+ the original dest. Stop if we pass a CODE_LABEL or a JUMP_INSN.
+ If we find an insn, and it has a REG_DEAD note, then delete the
+ note. */
+
+ for (insn = first; insn; insn = PREV_INSN (insn))
+ {
+ if (GET_CODE (insn) == CODE_LABEL
+ || GET_CODE (insn) == JUMP_INSN)
+ break;
+ else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && reg_mentioned_p (orig_dest, insn))
+ {
+ note = find_regno_note (insn, REG_DEAD, REGNO (orig_dest));
+ if (note)
+ remove_note (insn, note);
+ }
+ }
+ }
+ else if (! found_orig_dest)
+ {
+ /* This should never happen. */
+ abort ();
+ }
+ }
+
+ /* Update reg_n_sets. This is necessary to prevent local alloc from
+ converting REG_EQUAL notes to REG_EQUIV when splitting has modified
+ a reg from set once to set multiple times. */
+
+ {
+ rtx x = PATTERN (orig_insn);
+ RTX_CODE code = GET_CODE (x);
+
+ if (code == SET || code == CLOBBER)
+ update_n_sets (x, -1);
+ else if (code == PARALLEL)
+ {
+ int i;
+ for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
+ {
+ code = GET_CODE (XVECEXP (x, 0, i));
+ if (code == SET || code == CLOBBER)
+ update_n_sets (XVECEXP (x, 0, i), -1);
+ }
+ }
+
+ for (insn = first; ; insn = NEXT_INSN (insn))
+ {
+ x = PATTERN (insn);
+ code = GET_CODE (x);
+
+ if (code == SET || code == CLOBBER)
+ update_n_sets (x, 1);
+ else if (code == PARALLEL)
+ {
+ int i;
+ for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
+ {
+ code = GET_CODE (XVECEXP (x, 0, i));
+ if (code == SET || code == CLOBBER)
+ update_n_sets (XVECEXP (x, 0, i), 1);
+ }
+ }
+
+ if (insn == last)
+ break;
+ }
+ }
+}
+
+/* The one entry point in this file. DUMP_FILE is the dump file for
+ this pass. */
+
+void
+schedule_insns (dump_file)
+ FILE *dump_file;
+{
+ int max_uid = MAX_INSNS_PER_SPLIT * (get_max_uid () + 1);
+ int b;
+ rtx insn;
+
+ /* Taking care of this degenerate case makes the rest of
+ this code simpler. */
+ if (n_basic_blocks == 0)
+ return;
+
+ /* Create an insn here so that we can hang dependencies off of it later. */
+ sched_before_next_call
+ = gen_rtx (INSN, VOIDmode, 0, NULL_RTX, NULL_RTX,
+ NULL_RTX, 0, NULL_RTX, 0);
+
+ /* Initialize the unused_*_lists. We can't use the ones left over from
+ the previous function, because gcc has freed that memory. We can use
+ the ones left over from the first sched pass in the second pass however,
+ so only clear them on the first sched pass. The first pass is before
+ reload if flag_schedule_insns is set, otherwise it is afterwards. */
+
+ if (reload_completed == 0 || ! flag_schedule_insns)
+ {
+ unused_insn_list = 0;
+ unused_expr_list = 0;
+ }
+
+ /* We create no insns here, only reorder them, so we
+ remember how far we can cut back the stack on exit. */
+
+ /* Allocate data for this pass. See comments, above,
+ for what these vectors do. */
+ insn_luid = (int *) alloca (max_uid * sizeof (int));
+ insn_priority = (int *) alloca (max_uid * sizeof (int));
+ insn_tick = (int *) alloca (max_uid * sizeof (int));
+ insn_costs = (short *) alloca (max_uid * sizeof (short));
+ insn_units = (short *) alloca (max_uid * sizeof (short));
+ insn_blockage = (unsigned int *) alloca (max_uid * sizeof (unsigned int));
+ insn_ref_count = (int *) alloca (max_uid * sizeof (int));
+
+ if (reload_completed == 0)
+ {
+ sched_reg_n_deaths = (short *) alloca (max_regno * sizeof (short));
+ sched_reg_n_calls_crossed = (int *) alloca (max_regno * sizeof (int));
+ sched_reg_live_length = (int *) alloca (max_regno * sizeof (int));
+ bb_dead_regs = (regset) alloca (regset_bytes);
+ bb_live_regs = (regset) alloca (regset_bytes);
+ bzero ((char *) sched_reg_n_calls_crossed, max_regno * sizeof (int));
+ bzero ((char *) sched_reg_live_length, max_regno * sizeof (int));
+ bcopy ((char *) reg_n_deaths, (char *) sched_reg_n_deaths,
+ max_regno * sizeof (short));
+ init_alias_analysis ();
+ }
+ else
+ {
+ sched_reg_n_deaths = 0;
+ sched_reg_n_calls_crossed = 0;
+ sched_reg_live_length = 0;
+ bb_dead_regs = 0;
+ bb_live_regs = 0;
+ if (! flag_schedule_insns)
+ init_alias_analysis ();
+ }
+
+ if (write_symbols != NO_DEBUG)
+ {
+ rtx line;
+
+ line_note = (rtx *) alloca (max_uid * sizeof (rtx));
+ bzero ((char *) line_note, max_uid * sizeof (rtx));
+ line_note_head = (rtx *) alloca (n_basic_blocks * sizeof (rtx));
+ bzero ((char *) line_note_head, n_basic_blocks * sizeof (rtx));
+
+ /* Determine the line-number at the start of each basic block.
+ This must be computed and saved now, because after a basic block's
+ predecessor has been scheduled, it is impossible to accurately
+ determine the correct line number for the first insn of the block. */
+
+ for (b = 0; b < n_basic_blocks; b++)
+ for (line = basic_block_head[b]; line; line = PREV_INSN (line))
+ if (GET_CODE (line) == NOTE && NOTE_LINE_NUMBER (line) > 0)
+ {
+ line_note_head[b] = line;
+ break;
+ }
+ }
+
+ bzero ((char *) insn_luid, max_uid * sizeof (int));
+ bzero ((char *) insn_priority, max_uid * sizeof (int));
+ bzero ((char *) insn_tick, max_uid * sizeof (int));
+ bzero ((char *) insn_costs, max_uid * sizeof (short));
+ bzero ((char *) insn_units, max_uid * sizeof (short));
+ bzero ((char *) insn_blockage, max_uid * sizeof (unsigned int));
+ bzero ((char *) insn_ref_count, max_uid * sizeof (int));
+
+ /* Schedule each basic block, block by block. */
+
+ /* ??? Add a NOTE after the last insn of the last basic block. It is not
+ known why this is done. */
+
+ insn = basic_block_end[n_basic_blocks-1];
+ if (NEXT_INSN (insn) == 0
+ || (GET_CODE (insn) != NOTE
+ && GET_CODE (insn) != CODE_LABEL
+ /* Don't emit a NOTE if it would end up between an unconditional
+ jump and a BARRIER. */
+ && ! (GET_CODE (insn) == JUMP_INSN
+ && GET_CODE (NEXT_INSN (insn)) == BARRIER)))
+ emit_note_after (NOTE_INSN_DELETED, basic_block_end[n_basic_blocks-1]);
+
+ for (b = 0; b < n_basic_blocks; b++)
+ {
+ rtx insn, next;
+
+ note_list = 0;
+
+ for (insn = basic_block_head[b]; ; insn = next)
+ {
+ rtx prev;
+ rtx set;
+
+ /* Can't use `next_real_insn' because that
+ might go across CODE_LABELS and short-out basic blocks. */
+ next = NEXT_INSN (insn);
+ if (GET_CODE (insn) != INSN)
+ {
+ if (insn == basic_block_end[b])
+ break;
+
+ continue;
+ }
+
+ /* Don't split no-op move insns. These should silently disappear
+ later in final. Splitting such insns would break the code
+ that handles REG_NO_CONFLICT blocks. */
+ set = single_set (insn);
+ if (set && rtx_equal_p (SET_SRC (set), SET_DEST (set)))
+ {
+ if (insn == basic_block_end[b])
+ break;
+
+ /* Nops get in the way while scheduling, so delete them now if
+ register allocation has already been done. It is too risky
+ to try to do this before register allocation, and there are
+ unlikely to be very many nops then anyways. */
+ if (reload_completed)
+ {
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ }
+
+ continue;
+ }
+
+ /* Split insns here to get max fine-grain parallelism. */
+ prev = PREV_INSN (insn);
+ if (reload_completed == 0)
+ {
+ rtx last, first = PREV_INSN (insn);
+ rtx notes = REG_NOTES (insn);
+
+ last = try_split (PATTERN (insn), insn, 1);
+ if (last != insn)
+ {
+ /* try_split returns the NOTE that INSN became. */
+ first = NEXT_INSN (first);
+ update_flow_info (notes, first, last, insn);
+
+ PUT_CODE (insn, NOTE);
+ NOTE_SOURCE_FILE (insn) = 0;
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ if (insn == basic_block_head[b])
+ basic_block_head[b] = first;
+ if (insn == basic_block_end[b])
+ {
+ basic_block_end[b] = last;
+ break;
+ }
+ }
+ }
+
+ if (insn == basic_block_end[b])
+ break;
+ }
+
+ schedule_block (b, dump_file);
+
+#ifdef USE_C_ALLOCA
+ alloca (0);
+#endif
+ }
+
+ /* Reposition the prologue and epilogue notes in case we moved the
+ prologue/epilogue insns. */
+ if (reload_completed)
+ reposition_prologue_and_epilogue_notes (get_insns ());
+
+ if (write_symbols != NO_DEBUG)
+ {
+ rtx line = 0;
+ rtx insn = get_insns ();
+ int active_insn = 0;
+ int notes = 0;
+
+ /* Walk the insns deleting redundant line-number notes. Many of these
+ are already present. The remainder tend to occur at basic
+ block boundaries. */
+ for (insn = get_last_insn (); insn; insn = PREV_INSN (insn))
+ if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
+ {
+ /* If there are no active insns following, INSN is redundant. */
+ if (active_insn == 0)
+ {
+ notes++;
+ NOTE_SOURCE_FILE (insn) = 0;
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ }
+ /* If the line number is unchanged, LINE is redundant. */
+ else if (line
+ && NOTE_LINE_NUMBER (line) == NOTE_LINE_NUMBER (insn)
+ && NOTE_SOURCE_FILE (line) == NOTE_SOURCE_FILE (insn))
+ {
+ notes++;
+ NOTE_SOURCE_FILE (line) = 0;
+ NOTE_LINE_NUMBER (line) = NOTE_INSN_DELETED;
+ line = insn;
+ }
+ else
+ line = insn;
+ active_insn = 0;
+ }
+ else if (! ((GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED)
+ || (GET_CODE (insn) == INSN
+ && (GET_CODE (PATTERN (insn)) == USE
+ || GET_CODE (PATTERN (insn)) == CLOBBER))))
+ active_insn++;
+
+ if (dump_file && notes)
+ fprintf (dump_file, ";; deleted %d line-number notes\n", notes);
+ }
+
+ if (reload_completed == 0)
+ {
+ int regno;
+ for (regno = 0; regno < max_regno; regno++)
+ if (sched_reg_live_length[regno])
+ {
+ if (dump_file)
+ {
+ if (reg_live_length[regno] > sched_reg_live_length[regno])
+ fprintf (dump_file,
+ ";; register %d life shortened from %d to %d\n",
+ regno, reg_live_length[regno],
+ sched_reg_live_length[regno]);
+ /* Negative values are special; don't overwrite the current
+ reg_live_length value if it is negative. */
+ else if (reg_live_length[regno] < sched_reg_live_length[regno]
+ && reg_live_length[regno] >= 0)
+ fprintf (dump_file,
+ ";; register %d life extended from %d to %d\n",
+ regno, reg_live_length[regno],
+ sched_reg_live_length[regno]);
+
+ if (! reg_n_calls_crossed[regno]
+ && sched_reg_n_calls_crossed[regno])
+ fprintf (dump_file,
+ ";; register %d now crosses calls\n", regno);
+ else if (reg_n_calls_crossed[regno]
+ && ! sched_reg_n_calls_crossed[regno]
+ && reg_basic_block[regno] != REG_BLOCK_GLOBAL)
+ fprintf (dump_file,
+ ";; register %d no longer crosses calls\n", regno);
+
+ }
+ /* Negative values are special; don't overwrite the current
+ reg_live_length value if it is negative. */
+ if (reg_live_length[regno] >= 0)
+ reg_live_length[regno] = sched_reg_live_length[regno];
+
+ /* We can't change the value of reg_n_calls_crossed to zero for
+ pseudos which are live in more than one block.
+
+ This is because combine might have made an optimization which
+ invalidated basic_block_live_at_start and reg_n_calls_crossed,
+ but it does not update them. If we update reg_n_calls_crossed
+ here, the two variables are now inconsistent, and this might
+ confuse the caller-save code into saving a register that doesn't
+ need to be saved. This is only a problem when we zero calls
+ crossed for a pseudo live in multiple basic blocks.
+
+ Alternatively, we could try to correctly update basic block live
+ at start here in sched, but that seems complicated. */
+ if (sched_reg_n_calls_crossed[regno]
+ || reg_basic_block[regno] != REG_BLOCK_GLOBAL)
+ reg_n_calls_crossed[regno] = sched_reg_n_calls_crossed[regno];
+ }
+ }
+}
+#endif /* INSN_SCHEDULING */
diff --git a/gnu/usr.bin/cc/cc_int/sdbout.c b/gnu/usr.bin/cc/cc_int/sdbout.c
new file mode 100644
index 0000000..6a03108
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/sdbout.c
@@ -0,0 +1,1530 @@
+/* Output sdb-format symbol table information from GNU compiler.
+ Copyright (C) 1988, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* mike@tredysvr.Tredydev.Unisys.COM says:
+I modified the struct.c example and have a nm of a .o resulting from the
+AT&T C compiler. From the example below I would conclude the following:
+
+1. All .defs from structures are emitted as scanned. The example below
+ clearly shows the symbol table entries for BoxRec2 are after the first
+ function.
+
+2. All functions and their locals (including statics) are emitted as scanned.
+
+3. All nested unnamed union and structure .defs must be emitted before
+ the structure in which they are nested. The AT&T assembler is a
+ one pass beast as far as symbolics are concerned.
+
+4. All structure .defs are emitted before the typedefs that refer to them.
+
+5. All top level static and external variable definitions are moved to the
+ end of file with all top level statics occurring first before externs.
+
+6. All undefined references are at the end of the file.
+*/
+
+#include "config.h"
+
+#ifdef SDB_DEBUGGING_INFO
+
+#include "tree.h"
+#include "rtl.h"
+#include <stdio.h>
+#include "regs.h"
+#include "flags.h"
+#include "insn-config.h"
+#include "reload.h"
+
+/* Mips systems use the SDB functions to dump out symbols, but
+ do not supply usable syms.h include files. */
+#if defined(USG) && !defined(MIPS) && !defined (hpux)
+#include <syms.h>
+/* Use T_INT if we don't have T_VOID. */
+#ifndef T_VOID
+#define T_VOID T_INT
+#endif
+#else /* not USG, or MIPS */
+#include "gsyms.h"
+#endif /* not USG, or MIPS */
+
+/* #include <storclass.h> used to be this instead of syms.h. */
+
+/* 1 if PARM is passed to this function in memory. */
+
+#define PARM_PASSED_IN_MEMORY(PARM) \
+ (GET_CODE (DECL_INCOMING_RTL (PARM)) == MEM)
+
+/* A C expression for the integer offset value of an automatic variable
+ (C_AUTO) having address X (an RTX). */
+#ifndef DEBUGGER_AUTO_OFFSET
+#define DEBUGGER_AUTO_OFFSET(X) \
+ (GET_CODE (X) == PLUS ? INTVAL (XEXP (X, 1)) : 0)
+#endif
+
+/* A C expression for the integer offset value of an argument (C_ARG)
+ having address X (an RTX). The nominal offset is OFFSET. */
+#ifndef DEBUGGER_ARG_OFFSET
+#define DEBUGGER_ARG_OFFSET(OFFSET, X) (OFFSET)
+#endif
+
+/* Line number of beginning of current function, minus one.
+ Negative means not in a function or not using sdb. */
+
+int sdb_begin_function_line = -1;
+
+/* Counter to generate unique "names" for nameless struct members. */
+
+static int unnamed_struct_number = 0;
+
+extern FILE *asm_out_file;
+
+extern tree current_function_decl;
+
+void sdbout_init ();
+void sdbout_symbol ();
+void sdbout_types();
+
+static void sdbout_typedefs ();
+static void sdbout_syms ();
+static void sdbout_one_type ();
+static void sdbout_queue_anonymous_type ();
+static void sdbout_dequeue_anonymous_types ();
+static int plain_type_1 ();
+
+/* Define the default sizes for various types. */
+
+#ifndef CHAR_TYPE_SIZE
+#define CHAR_TYPE_SIZE BITS_PER_UNIT
+#endif
+
+#ifndef SHORT_TYPE_SIZE
+#define SHORT_TYPE_SIZE (BITS_PER_UNIT * MIN ((UNITS_PER_WORD + 1) / 2, 2))
+#endif
+
+#ifndef INT_TYPE_SIZE
+#define INT_TYPE_SIZE BITS_PER_WORD
+#endif
+
+#ifndef LONG_TYPE_SIZE
+#define LONG_TYPE_SIZE BITS_PER_WORD
+#endif
+
+#ifndef LONG_LONG_TYPE_SIZE
+#define LONG_LONG_TYPE_SIZE (BITS_PER_WORD * 2)
+#endif
+
+#ifndef FLOAT_TYPE_SIZE
+#define FLOAT_TYPE_SIZE BITS_PER_WORD
+#endif
+
+#ifndef DOUBLE_TYPE_SIZE
+#define DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2)
+#endif
+
+#ifndef LONG_DOUBLE_TYPE_SIZE
+#define LONG_DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2)
+#endif
+
+/* Random macros describing parts of SDB data. */
+
+/* Put something here if lines get too long */
+#define CONTIN
+
+/* Default value of delimiter is ";". */
+#ifndef SDB_DELIM
+#define SDB_DELIM ";"
+#endif
+
+/* Maximum number of dimensions the assembler will allow. */
+#ifndef SDB_MAX_DIM
+#define SDB_MAX_DIM 4
+#endif
+
+#ifndef PUT_SDB_SCL
+#define PUT_SDB_SCL(a) fprintf(asm_out_file, "\t.scl\t%d%s", (a), SDB_DELIM)
+#endif
+
+#ifndef PUT_SDB_INT_VAL
+#define PUT_SDB_INT_VAL(a) fprintf (asm_out_file, "\t.val\t%d%s", (a), SDB_DELIM)
+#endif
+
+#ifndef PUT_SDB_VAL
+#define PUT_SDB_VAL(a) \
+( fputs ("\t.val\t", asm_out_file), \
+ output_addr_const (asm_out_file, (a)), \
+ fprintf (asm_out_file, SDB_DELIM))
+#endif
+
+#ifndef PUT_SDB_DEF
+#define PUT_SDB_DEF(a) \
+do { fprintf (asm_out_file, "\t.def\t"); \
+ ASM_OUTPUT_LABELREF (asm_out_file, a); \
+ fprintf (asm_out_file, SDB_DELIM); } while (0)
+#endif
+
+#ifndef PUT_SDB_PLAIN_DEF
+#define PUT_SDB_PLAIN_DEF(a) fprintf(asm_out_file,"\t.def\t.%s%s",a, SDB_DELIM)
+#endif
+
+#ifndef PUT_SDB_ENDEF
+#define PUT_SDB_ENDEF fputs("\t.endef\n", asm_out_file)
+#endif
+
+#ifndef PUT_SDB_TYPE
+#define PUT_SDB_TYPE(a) fprintf(asm_out_file, "\t.type\t0%o%s", a, SDB_DELIM)
+#endif
+
+#ifndef PUT_SDB_SIZE
+#define PUT_SDB_SIZE(a) fprintf(asm_out_file, "\t.size\t%d%s", a, SDB_DELIM)
+#endif
+
+#ifndef PUT_SDB_START_DIM
+#define PUT_SDB_START_DIM fprintf(asm_out_file, "\t.dim\t")
+#endif
+
+#ifndef PUT_SDB_NEXT_DIM
+#define PUT_SDB_NEXT_DIM(a) fprintf(asm_out_file, "%d,", a)
+#endif
+
+#ifndef PUT_SDB_LAST_DIM
+#define PUT_SDB_LAST_DIM(a) fprintf(asm_out_file, "%d%s", a, SDB_DELIM)
+#endif
+
+#ifndef PUT_SDB_TAG
+#define PUT_SDB_TAG(a) \
+do { fprintf (asm_out_file, "\t.tag\t"); \
+ ASM_OUTPUT_LABELREF (asm_out_file, a); \
+ fprintf (asm_out_file, SDB_DELIM); } while (0)
+#endif
+
+#ifndef PUT_SDB_BLOCK_START
+#define PUT_SDB_BLOCK_START(LINE) \
+ fprintf (asm_out_file, \
+ "\t.def\t.bb%s\t.val\t.%s\t.scl\t100%s\t.line\t%d%s\t.endef\n", \
+ SDB_DELIM, SDB_DELIM, SDB_DELIM, (LINE), SDB_DELIM)
+#endif
+
+#ifndef PUT_SDB_BLOCK_END
+#define PUT_SDB_BLOCK_END(LINE) \
+ fprintf (asm_out_file, \
+ "\t.def\t.eb%s\t.val\t.%s\t.scl\t100%s\t.line\t%d%s\t.endef\n", \
+ SDB_DELIM, SDB_DELIM, SDB_DELIM, (LINE), SDB_DELIM)
+#endif
+
+#ifndef PUT_SDB_FUNCTION_START
+#define PUT_SDB_FUNCTION_START(LINE) \
+ fprintf (asm_out_file, \
+ "\t.def\t.bf%s\t.val\t.%s\t.scl\t101%s\t.line\t%d%s\t.endef\n", \
+ SDB_DELIM, SDB_DELIM, SDB_DELIM, (LINE), SDB_DELIM)
+#endif
+
+#ifndef PUT_SDB_FUNCTION_END
+#define PUT_SDB_FUNCTION_END(LINE) \
+ fprintf (asm_out_file, \
+ "\t.def\t.ef%s\t.val\t.%s\t.scl\t101%s\t.line\t%d%s\t.endef\n", \
+ SDB_DELIM, SDB_DELIM, SDB_DELIM, (LINE), SDB_DELIM)
+#endif
+
+#ifndef PUT_SDB_EPILOGUE_END
+#define PUT_SDB_EPILOGUE_END(NAME) \
+do { fprintf (asm_out_file, "\t.def\t"); \
+ ASM_OUTPUT_LABELREF (asm_out_file, NAME); \
+ fprintf (asm_out_file, \
+ "%s\t.val\t.%s\t.scl\t-1%s\t.endef\n", \
+ SDB_DELIM, SDB_DELIM, SDB_DELIM); } while (0)
+#endif
+
+#ifndef SDB_GENERATE_FAKE
+#define SDB_GENERATE_FAKE(BUFFER, NUMBER) \
+ sprintf ((BUFFER), ".%dfake", (NUMBER));
+#endif
+
+/* Return the sdb tag identifier string for TYPE
+ if TYPE has already been defined; otherwise return a null pointer. */
+
+#define KNOWN_TYPE_TAG(type) TYPE_SYMTAB_POINTER (type)
+
+/* Set the sdb tag identifier string for TYPE to NAME. */
+
+#define SET_KNOWN_TYPE_TAG(TYPE, NAME) \
+ TYPE_SYMTAB_POINTER (TYPE) = (NAME)
+
+/* Return the name (a string) of the struct, union or enum tag
+ described by the TREE_LIST node LINK. This is 0 for an anonymous one. */
+
+#define TAG_NAME(link) \
+ (((link) && TREE_PURPOSE ((link)) \
+ && IDENTIFIER_POINTER (TREE_PURPOSE ((link)))) \
+ ? IDENTIFIER_POINTER (TREE_PURPOSE ((link))) : (char *) 0)
+
+/* Ensure we don't output a negative line number. */
+#define MAKE_LINE_SAFE(line) \
+ if (line <= sdb_begin_function_line) line = sdb_begin_function_line + 1
+
+/* Set up for SDB output at the start of compilation. */
+
+void
+sdbout_init (asm_file, input_file_name, syms)
+ FILE *asm_file;
+ char *input_file_name;
+ tree syms;
+{
+#ifdef RMS_QUICK_HACK_1
+ tree t;
+ for (t = syms; t; t = TREE_CHAIN (t))
+ if (DECL_NAME (t) && IDENTIFIER_POINTER (DECL_NAME (t)) != 0
+ && !strcmp (IDENTIFIER_POINTER (DECL_NAME (t)), "__vtbl_ptr_type"))
+ sdbout_symbol (t, 0);
+#endif
+
+#if 0 /* Nothing need be output for the predefined types. */
+ /* Get all permanent types that have typedef names,
+ and output them all, except for those already output. */
+
+ sdbout_typedefs (syms);
+#endif
+}
+
+#if 0
+
+/* return the tag identifier for type
+ */
+
+char *
+tag_of_ru_type (type,link)
+ tree type,link;
+{
+ if (TYPE_SYMTAB_ADDRESS (type))
+ return TYPE_SYMTAB_ADDRESS (type);
+ if (link && TREE_PURPOSE (link)
+ && IDENTIFIER_POINTER (TREE_PURPOSE (link)))
+ TYPE_SYMTAB_ADDRESS (type) = IDENTIFIER_POINTER (TREE_PURPOSE (link));
+ else
+ return (char *) TYPE_SYMTAB_ADDRESS (type);
+}
+#endif
+
+/* Return a unique string to name an anonymous type. */
+
+static char *
+gen_fake_label ()
+{
+ char label[10];
+ char *labelstr;
+ SDB_GENERATE_FAKE (label, unnamed_struct_number);
+ unnamed_struct_number++;
+ labelstr = (char *) permalloc (strlen (label) + 1);
+ strcpy (labelstr, label);
+ return labelstr;
+}
+
+/* Return the number which describes TYPE for SDB.
+ For pointers, etc., this function is recursive.
+ Each record, union or enumeral type must already have had a
+ tag number output. */
+
+/* The number is given by d6d5d4d3d2d1bbbb
+ where bbbb is 4 bit basic type, and di indicate one of notype,ptr,fn,array.
+ Thus, char *foo () has bbbb=T_CHAR
+ d1=D_FCN
+ d2=D_PTR
+ N_BTMASK= 017 1111 basic type field.
+ N_TSHIFT= 2 derived type shift
+ N_BTSHFT= 4 Basic type shift */
+
+/* Produce the number that describes a pointer, function or array type.
+ PREV is the number describing the target, value or element type.
+ DT_type describes how to transform that type. */
+#define PUSH_DERIVED_LEVEL(DT_type,PREV) \
+ ((((PREV) & ~(int)N_BTMASK) << (int)N_TSHIFT) \
+ | ((int)DT_type << (int)N_BTSHFT) \
+ | ((PREV) & (int)N_BTMASK))
+
+/* Number of elements used in sdb_dims. */
+static int sdb_n_dims = 0;
+
+/* Table of array dimensions of current type. */
+static int sdb_dims[SDB_MAX_DIM];
+
+/* Size of outermost array currently being processed. */
+static int sdb_type_size = -1;
+
+static int
+plain_type (type)
+ tree type;
+{
+ int val = plain_type_1 (type);
+
+ /* If we have already saved up some array dimensions, print them now. */
+ if (sdb_n_dims > 0)
+ {
+ int i;
+ PUT_SDB_START_DIM;
+ for (i = sdb_n_dims - 1; i > 0; i--)
+ PUT_SDB_NEXT_DIM (sdb_dims[i]);
+ PUT_SDB_LAST_DIM (sdb_dims[0]);
+ sdb_n_dims = 0;
+
+ sdb_type_size = int_size_in_bytes (type);
+ /* Don't kill sdb if type is not laid out or has variable size. */
+ if (sdb_type_size < 0)
+ sdb_type_size = 0;
+ }
+ /* If we have computed the size of an array containing this type,
+ print it now. */
+ if (sdb_type_size >= 0)
+ {
+ PUT_SDB_SIZE (sdb_type_size);
+ sdb_type_size = -1;
+ }
+ return val;
+}
+
+static int
+template_name_p (name)
+ tree name;
+{
+ register char *ptr = IDENTIFIER_POINTER (name);
+ while (*ptr && *ptr != '<')
+ ptr++;
+
+ return *ptr != '\0';
+}
+
+static void
+sdbout_record_type_name (type)
+ tree type;
+{
+ char *name = 0;
+ int no_name;
+
+ if (KNOWN_TYPE_TAG (type))
+ return;
+
+ if (TYPE_NAME (type) != 0)
+ {
+ tree t = 0;
+ /* Find the IDENTIFIER_NODE for the type name. */
+ if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
+ {
+ t = TYPE_NAME (type);
+ }
+#if 1 /* As a temporary hack, use typedef names for C++ only. */
+ else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
+ && TYPE_LANG_SPECIFIC (type))
+ {
+ t = DECL_NAME (TYPE_NAME (type));
+ /* The DECL_NAME for templates includes "<>", which breaks
+ most assemblers. Use its assembler name instead, which
+ has been mangled into being safe. */
+ if (t && template_name_p (t))
+ t = DECL_ASSEMBLER_NAME (TYPE_NAME (type));
+ }
+#endif
+
+ /* Now get the name as a string, or invent one. */
+ if (t != NULL_TREE)
+ name = IDENTIFIER_POINTER (t);
+ }
+
+ no_name = (name == 0 || *name == 0);
+ if (no_name)
+ name = gen_fake_label ();
+
+ SET_KNOWN_TYPE_TAG (type, name);
+#ifdef SDB_ALLOW_FORWARD_REFERENCES
+ if (no_name)
+ sdbout_queue_anonymous_type (type);
+#endif
+}
+
+static int
+plain_type_1 (type)
+ tree type;
+{
+ if (type == 0)
+ type = void_type_node;
+ if (type == error_mark_node)
+ type = integer_type_node;
+ type = TYPE_MAIN_VARIANT (type);
+
+ switch (TREE_CODE (type))
+ {
+ case VOID_TYPE:
+ return T_VOID;
+ case INTEGER_TYPE:
+ {
+ int size = int_size_in_bytes (type) * BITS_PER_UNIT;
+
+ /* Carefully distinguish all the standard types of C,
+ without messing up if the language is not C.
+ Note that we check only for the names that contain spaces;
+ other names might occur by coincidence in other languages. */
+ if (TYPE_NAME (type) != 0
+ && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
+ && DECL_NAME (TYPE_NAME (type)) != 0
+ && TREE_CODE (DECL_NAME (TYPE_NAME (type))) == IDENTIFIER_NODE)
+ {
+ char *name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)));
+
+ if (!strcmp (name, "unsigned char"))
+ return T_UCHAR;
+ if (!strcmp (name, "signed char"))
+ return T_CHAR;
+ if (!strcmp (name, "unsigned int"))
+ return T_UINT;
+ if (!strcmp (name, "short int"))
+ return T_SHORT;
+ if (!strcmp (name, "short unsigned int"))
+ return T_USHORT;
+ if (!strcmp (name, "long int"))
+ return T_LONG;
+ if (!strcmp (name, "long unsigned int"))
+ return T_ULONG;
+ }
+
+ if (size == CHAR_TYPE_SIZE)
+ return (TREE_UNSIGNED (type) ? T_UCHAR : T_CHAR);
+ if (size == SHORT_TYPE_SIZE)
+ return (TREE_UNSIGNED (type) ? T_USHORT : T_SHORT);
+ if (size == INT_TYPE_SIZE)
+ return (TREE_UNSIGNED (type) ? T_UINT : T_INT);
+ if (size == LONG_TYPE_SIZE)
+ return (TREE_UNSIGNED (type) ? T_ULONG : T_LONG);
+ return 0;
+ }
+
+ case REAL_TYPE:
+ {
+ int size = int_size_in_bytes (type) * BITS_PER_UNIT;
+ if (size == FLOAT_TYPE_SIZE)
+ return T_FLOAT;
+ if (size == DOUBLE_TYPE_SIZE)
+ return T_DOUBLE;
+ return 0;
+ }
+
+ case ARRAY_TYPE:
+ {
+ int m;
+ m = plain_type_1 (TREE_TYPE (type));
+ if (sdb_n_dims < SDB_MAX_DIM)
+ sdb_dims[sdb_n_dims++]
+ = (TYPE_DOMAIN (type)
+ ? TREE_INT_CST_LOW (TYPE_MAX_VALUE (TYPE_DOMAIN (type))) + 1
+ : 0);
+ return PUSH_DERIVED_LEVEL (DT_ARY, m);
+ }
+
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ case QUAL_UNION_TYPE:
+ case ENUMERAL_TYPE:
+ {
+ char *tag;
+#ifdef SDB_ALLOW_FORWARD_REFERENCES
+ sdbout_record_type_name (type);
+#endif
+#ifndef SDB_ALLOW_UNKNOWN_REFERENCES
+ if ((TREE_ASM_WRITTEN (type) && KNOWN_TYPE_TAG (type) != 0)
+#ifdef SDB_ALLOW_FORWARD_REFERENCES
+ || TYPE_MODE (type) != VOIDmode
+#endif
+ )
+#endif
+ {
+ /* Output the referenced structure tag name
+ only if the .def has already been finished.
+ At least on 386, the Unix assembler
+ cannot handle forward references to tags. */
+ /* But the 88100, it requires them, sigh... */
+ /* And the MIPS requires unknown refs as well... */
+ tag = KNOWN_TYPE_TAG (type);
+ PUT_SDB_TAG (tag);
+ /* These 3 lines used to follow the close brace.
+ However, a size of 0 without a tag implies a tag of 0,
+ so if we don't know a tag, we can't mention the size. */
+ sdb_type_size = int_size_in_bytes (type);
+ if (sdb_type_size < 0)
+ sdb_type_size = 0;
+ }
+ return ((TREE_CODE (type) == RECORD_TYPE) ? T_STRUCT
+ : (TREE_CODE (type) == UNION_TYPE) ? T_UNION
+ : (TREE_CODE (type) == QUAL_UNION_TYPE) ? T_UNION
+ : T_ENUM);
+ }
+ case POINTER_TYPE:
+ case REFERENCE_TYPE:
+ {
+ int m = plain_type_1 (TREE_TYPE (type));
+ return PUSH_DERIVED_LEVEL (DT_PTR, m);
+ }
+ case FUNCTION_TYPE:
+ case METHOD_TYPE:
+ {
+ int m = plain_type_1 (TREE_TYPE (type));
+ return PUSH_DERIVED_LEVEL (DT_FCN, m);
+ }
+ default:
+ return 0;
+ }
+}
+
+/* Output the symbols defined in block number DO_BLOCK.
+ Set NEXT_BLOCK_NUMBER to 0 before calling.
+
+ This function works by walking the tree structure of blocks,
+ counting blocks until it finds the desired block. */
+
+static int do_block = 0;
+
+static int next_block_number;
+
+static void
+sdbout_block (block)
+ register tree block;
+{
+ while (block)
+ {
+ /* Ignore blocks never expanded or otherwise marked as real. */
+ if (TREE_USED (block))
+ {
+ /* When we reach the specified block, output its symbols. */
+ if (next_block_number == do_block)
+ {
+ sdbout_syms (BLOCK_VARS (block));
+ }
+
+ /* If we are past the specified block, stop the scan. */
+ if (next_block_number > do_block)
+ return;
+
+ next_block_number++;
+
+ /* Scan the blocks within this block. */
+ sdbout_block (BLOCK_SUBBLOCKS (block));
+ }
+
+ block = BLOCK_CHAIN (block);
+ }
+}
+
+/* Call sdbout_symbol on each decl in the chain SYMS. */
+
+static void
+sdbout_syms (syms)
+ tree syms;
+{
+ while (syms)
+ {
+ if (TREE_CODE (syms) != LABEL_DECL)
+ sdbout_symbol (syms, 1);
+ syms = TREE_CHAIN (syms);
+ }
+}
+
+/* Output SDB information for a symbol described by DECL.
+ LOCAL is nonzero if the symbol is not file-scope. */
+
+void
+sdbout_symbol (decl, local)
+ tree decl;
+ int local;
+{
+ tree type = TREE_TYPE (decl);
+ tree context = NULL_TREE;
+ rtx value;
+ int regno = -1;
+ char *name;
+
+ sdbout_one_type (type);
+
+#if 0 /* This loses when functions are marked to be ignored,
+ which happens in the C++ front end. */
+ if (DECL_IGNORED_P (decl))
+ return;
+#endif
+
+ switch (TREE_CODE (decl))
+ {
+ case CONST_DECL:
+ /* Enum values are defined by defining the enum type. */
+ return;
+
+ case FUNCTION_DECL:
+ /* Don't mention a nested function under its parent. */
+ context = decl_function_context (decl);
+ if (context == current_function_decl)
+ return;
+ if (DECL_EXTERNAL (decl))
+ return;
+ if (GET_CODE (DECL_RTL (decl)) != MEM
+ || GET_CODE (XEXP (DECL_RTL (decl), 0)) != SYMBOL_REF)
+ return;
+ PUT_SDB_DEF (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)));
+ PUT_SDB_VAL (XEXP (DECL_RTL (decl), 0));
+ PUT_SDB_SCL (TREE_PUBLIC (decl) ? C_EXT : C_STAT);
+ break;
+
+ case TYPE_DECL:
+ /* Done with tagged types. */
+ if (DECL_NAME (decl) == 0)
+ return;
+ if (DECL_IGNORED_P (decl))
+ return;
+
+ /* Output typedef name. */
+ if (template_name_p (DECL_NAME (decl)))
+ PUT_SDB_DEF (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)));
+ else
+ PUT_SDB_DEF (IDENTIFIER_POINTER (DECL_NAME (decl)));
+ PUT_SDB_SCL (C_TPDEF);
+ break;
+
+ case PARM_DECL:
+ /* Parm decls go in their own separate chains
+ and are output by sdbout_reg_parms and sdbout_parms. */
+ abort ();
+
+ case VAR_DECL:
+ /* Don't mention a variable that is external.
+ Let the file that defines it describe it. */
+ if (DECL_EXTERNAL (decl))
+ return;
+
+ /* Ignore __FUNCTION__, etc. */
+ if (DECL_IGNORED_P (decl))
+ return;
+
+ /* If there was an error in the declaration, don't dump core
+ if there is no RTL associated with the variable doesn't
+ exist. */
+ if (DECL_RTL (decl) == 0)
+ return;
+
+ DECL_RTL (decl) = eliminate_regs (DECL_RTL (decl), 0, NULL_RTX);
+#ifdef LEAF_REG_REMAP
+ if (leaf_function)
+ leaf_renumber_regs_insn (DECL_RTL (decl));
+#endif
+ value = DECL_RTL (decl);
+
+ /* Don't mention a variable at all
+ if it was completely optimized into nothingness.
+
+ If DECL was from an inline function, then its rtl
+ is not identically the rtl that was used in this
+ particular compilation. */
+ if (GET_CODE (value) == REG)
+ {
+ regno = REGNO (DECL_RTL (decl));
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ return;
+ }
+ else if (GET_CODE (value) == SUBREG)
+ {
+ int offset = 0;
+ while (GET_CODE (value) == SUBREG)
+ {
+ offset += SUBREG_WORD (value);
+ value = SUBREG_REG (value);
+ }
+ if (GET_CODE (value) == REG)
+ {
+ regno = REGNO (value);
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ return;
+ regno += offset;
+ }
+ alter_subreg (DECL_RTL (decl));
+ value = DECL_RTL (decl);
+ }
+ /* Don't output anything if an auto variable
+ gets RTL that is static.
+ GAS version 2.2 can't handle such output. */
+ else if (GET_CODE (value) == MEM && CONSTANT_P (XEXP (value, 0))
+ && ! TREE_STATIC (decl))
+ return;
+
+ /* Emit any structure, union, or enum type that has not been output.
+ This occurs for tag-less structs (et al) used to declare variables
+ within functions. */
+ if (TREE_CODE (type) == ENUMERAL_TYPE
+ || TREE_CODE (type) == RECORD_TYPE
+ || TREE_CODE (type) == UNION_TYPE
+ || TREE_CODE (type) == QUAL_UNION_TYPE)
+ {
+ if (TYPE_SIZE (type) != 0 /* not a forward reference */
+ && KNOWN_TYPE_TAG (type) == 0) /* not yet declared */
+ sdbout_one_type (type);
+ }
+
+ /* Defer SDB information for top-level initialized variables! */
+ if (! local
+ && GET_CODE (value) == MEM
+ && DECL_INITIAL (decl))
+ return;
+
+ /* C++ in 2.3 makes nameless symbols. That will be fixed later.
+ For now, avoid crashing. */
+ if (DECL_NAME (decl) == NULL_TREE)
+ return;
+
+ /* Record the name for, starting a symtab entry. */
+ if (DECL_LANG_SPECIFIC (decl))
+ name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+ else
+ name = IDENTIFIER_POINTER (DECL_NAME (decl));
+
+ if (GET_CODE (value) == MEM
+ && GET_CODE (XEXP (value, 0)) == SYMBOL_REF)
+ {
+ PUT_SDB_DEF (name);
+ if (TREE_PUBLIC (decl))
+ {
+ PUT_SDB_VAL (XEXP (value, 0));
+ PUT_SDB_SCL (C_EXT);
+ }
+ else
+ {
+ PUT_SDB_VAL (XEXP (value, 0));
+ PUT_SDB_SCL (C_STAT);
+ }
+ }
+ else if (regno >= 0)
+ {
+ PUT_SDB_DEF (name);
+ PUT_SDB_INT_VAL (DBX_REGISTER_NUMBER (regno));
+ PUT_SDB_SCL (C_REG);
+ }
+ else if (GET_CODE (value) == MEM
+ && (GET_CODE (XEXP (value, 0)) == MEM
+ || (GET_CODE (XEXP (value, 0)) == REG
+ && REGNO (XEXP (value, 0)) != HARD_FRAME_POINTER_REGNUM
+ && REGNO (XEXP (value, 0)) != STACK_POINTER_REGNUM)))
+ /* If the value is indirect by memory or by a register
+ that isn't the frame pointer
+ then it means the object is variable-sized and address through
+ that register or stack slot. COFF has no way to represent this
+ so all we can do is output the variable as a pointer. */
+ {
+ PUT_SDB_DEF (name);
+ if (GET_CODE (XEXP (value, 0)) == REG)
+ {
+ PUT_SDB_INT_VAL (DBX_REGISTER_NUMBER (REGNO (XEXP (value, 0))));
+ PUT_SDB_SCL (C_REG);
+ }
+ else
+ {
+ /* DECL_RTL looks like (MEM (MEM (PLUS (REG...)
+ (CONST_INT...)))).
+ We want the value of that CONST_INT. */
+ /* Encore compiler hates a newline in a macro arg, it seems. */
+ PUT_SDB_INT_VAL (DEBUGGER_AUTO_OFFSET
+ (XEXP (XEXP (value, 0), 0)));
+ PUT_SDB_SCL (C_AUTO);
+ }
+
+ type = build_pointer_type (TREE_TYPE (decl));
+ }
+ else if (GET_CODE (value) == MEM
+ && ((GET_CODE (XEXP (value, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (value, 0), 0)) == REG
+ && GET_CODE (XEXP (XEXP (value, 0), 1)) == CONST_INT)
+ /* This is for variables which are at offset zero from
+ the frame pointer. This happens on the Alpha.
+ Non-frame pointer registers are excluded above. */
+ || (GET_CODE (XEXP (value, 0)) == REG)))
+ {
+ /* DECL_RTL looks like (MEM (PLUS (REG...) (CONST_INT...)))
+ or (MEM (REG...)). We want the value of that CONST_INT
+ or zero. */
+ PUT_SDB_DEF (name);
+ PUT_SDB_INT_VAL (DEBUGGER_AUTO_OFFSET (XEXP (value, 0)));
+ PUT_SDB_SCL (C_AUTO);
+ }
+ else if (GET_CODE (value) == MEM && GET_CODE (XEXP (value, 0)) == CONST)
+ {
+ /* Handle an obscure case which can arise when optimizing and
+ when there are few available registers. (This is *always*
+ the case for i386/i486 targets). The DECL_RTL looks like
+ (MEM (CONST ...)) even though this variable is a local `auto'
+ or a local `register' variable. In effect, what has happened
+ is that the reload pass has seen that all assignments and
+ references for one such a local variable can be replaced by
+ equivalent assignments and references to some static storage
+ variable, thereby avoiding the need for a register. In such
+ cases we're forced to lie to debuggers and tell them that
+ this variable was itself `static'. */
+ PUT_SDB_DEF (name);
+ PUT_SDB_VAL (XEXP (XEXP (value, 0), 0));
+ PUT_SDB_SCL (C_STAT);
+ }
+ else
+ {
+ /* It is something we don't know how to represent for SDB. */
+ return;
+ }
+ break;
+ }
+ PUT_SDB_TYPE (plain_type (type));
+ PUT_SDB_ENDEF;
+}
+
+/* Output SDB information for a top-level initialized variable
+ that has been delayed. */
+
+void
+sdbout_toplevel_data (decl)
+ tree decl;
+{
+ tree type = TREE_TYPE (decl);
+
+ if (DECL_IGNORED_P (decl))
+ return;
+
+ if (! (TREE_CODE (decl) == VAR_DECL
+ && GET_CODE (DECL_RTL (decl)) == MEM
+ && DECL_INITIAL (decl)))
+ abort ();
+
+ PUT_SDB_DEF (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)));
+ PUT_SDB_VAL (XEXP (DECL_RTL (decl), 0));
+ if (TREE_PUBLIC (decl))
+ {
+ PUT_SDB_SCL (C_EXT);
+ }
+ else
+ {
+ PUT_SDB_SCL (C_STAT);
+ }
+ PUT_SDB_TYPE (plain_type (type));
+ PUT_SDB_ENDEF;
+}
+
+#ifdef SDB_ALLOW_FORWARD_REFERENCES
+
+/* Machinery to record and output anonymous types. */
+
+static tree anonymous_types;
+
+static void
+sdbout_queue_anonymous_type (type)
+ tree type;
+{
+ anonymous_types = saveable_tree_cons (NULL_TREE, type, anonymous_types);
+}
+
+static void
+sdbout_dequeue_anonymous_types ()
+{
+ register tree types, link;
+
+ while (anonymous_types)
+ {
+ types = nreverse (anonymous_types);
+ anonymous_types = NULL_TREE;
+
+ for (link = types; link; link = TREE_CHAIN (link))
+ {
+ register tree type = TREE_VALUE (link);
+
+ if (type && ! TREE_ASM_WRITTEN (type))
+ sdbout_one_type (type);
+ }
+ }
+}
+
+#endif
+
+/* Given a chain of ..._TYPE nodes, all of which have names,
+ output definitions of those names, as typedefs. */
+
+void
+sdbout_types (types)
+ register tree types;
+{
+ register tree link;
+
+ for (link = types; link; link = TREE_CHAIN (link))
+ sdbout_one_type (link);
+
+#ifdef SDB_ALLOW_FORWARD_REFERENCES
+ sdbout_dequeue_anonymous_types ();
+#endif
+}
+
+static void
+sdbout_type (type)
+ tree type;
+{
+ if (type == error_mark_node)
+ type = integer_type_node;
+ PUT_SDB_TYPE (plain_type (type));
+}
+
+/* Output types of the fields of type TYPE, if they are structs.
+
+ Formerly did not chase through pointer types, since that could be circular.
+ They must come before TYPE, since forward refs are not allowed.
+ Now james@bigtex.cactus.org says to try them. */
+
+static void
+sdbout_field_types (type)
+ tree type;
+{
+ tree tail;
+ for (tail = TYPE_FIELDS (type); tail; tail = TREE_CHAIN (tail))
+ if (TREE_CODE (TREE_TYPE (tail)) == POINTER_TYPE)
+ sdbout_one_type (TREE_TYPE (TREE_TYPE (tail)));
+ else
+ sdbout_one_type (TREE_TYPE (tail));
+}
+
+/* Use this to put out the top level defined record and union types
+ for later reference. If this is a struct with a name, then put that
+ name out. Other unnamed structs will have .xxfake labels generated so
+ that they may be referred to later.
+ The label will be stored in the KNOWN_TYPE_TAG slot of a type.
+ It may NOT be called recursively. */
+
+static void
+sdbout_one_type (type)
+ tree type;
+{
+ text_section ();
+
+ switch (TREE_CODE (type))
+ {
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ case QUAL_UNION_TYPE:
+ case ENUMERAL_TYPE:
+ type = TYPE_MAIN_VARIANT (type);
+ /* Don't output a type twice. */
+ if (TREE_ASM_WRITTEN (type))
+ /* James said test TREE_ASM_BEING_WRITTEN here. */
+ return;
+
+ /* Output nothing if type is not yet defined. */
+ if (TYPE_SIZE (type) == 0)
+ return;
+
+ TREE_ASM_WRITTEN (type) = 1;
+#if 1
+ /* This is reputed to cause trouble with the following case,
+ but perhaps checking TYPE_SIZE above will fix it. */
+
+ /* Here is a test case:
+
+ struct foo {
+ struct badstr *bbb;
+ } forwardref;
+
+ typedef struct intermediate {
+ int aaaa;
+ } intermediate_ref;
+
+ typedef struct badstr {
+ int ccccc;
+ } badtype; */
+
+#if 0
+ TREE_ASM_BEING_WRITTEN (type) = 1;
+#endif
+ /* This change, which ought to make better output,
+ used to make the COFF assembler unhappy.
+ Changes involving KNOWN_TYPE_TAG may fix the problem. */
+ /* Before really doing anything, output types we want to refer to. */
+ /* Note that in version 1 the following two lines
+ are not used if forward references are in use. */
+ if (TREE_CODE (type) != ENUMERAL_TYPE)
+ sdbout_field_types (type);
+#if 0
+ TREE_ASM_WRITTEN (type) = 1;
+#endif
+#endif
+
+ /* Output a structure type. */
+ {
+ int size = int_size_in_bytes (type);
+ int member_scl;
+ tree tem;
+ int i, n_baseclasses = 0;
+
+ /* Record the type tag, but not in its permanent place just yet. */
+ sdbout_record_type_name (type);
+
+ PUT_SDB_DEF (KNOWN_TYPE_TAG (type));
+
+ switch (TREE_CODE (type))
+ {
+ case UNION_TYPE:
+ case QUAL_UNION_TYPE:
+ PUT_SDB_SCL (C_UNTAG);
+ PUT_SDB_TYPE (T_UNION);
+ member_scl = C_MOU;
+ break;
+
+ case RECORD_TYPE:
+ PUT_SDB_SCL (C_STRTAG);
+ PUT_SDB_TYPE (T_STRUCT);
+ member_scl = C_MOS;
+ break;
+
+ case ENUMERAL_TYPE:
+ PUT_SDB_SCL (C_ENTAG);
+ PUT_SDB_TYPE (T_ENUM);
+ member_scl = C_MOE;
+ break;
+ }
+
+ PUT_SDB_SIZE (size);
+ PUT_SDB_ENDEF;
+
+ /* Print out the base class information with fields
+ named after the types they hold. */
+ if (TYPE_BINFO (type)
+ && TYPE_BINFO_BASETYPES (type))
+ n_baseclasses = TREE_VEC_LENGTH (TYPE_BINFO_BASETYPES (type));
+ for (i = 0; i < n_baseclasses; i++)
+ {
+ tree child = TREE_VEC_ELT (BINFO_BASETYPES (TYPE_BINFO (type)), i);
+ tree child_type = BINFO_TYPE (child);
+ tree child_type_name;
+ if (TYPE_NAME (child_type) == 0)
+ continue;
+ if (TREE_CODE (TYPE_NAME (child_type)) == IDENTIFIER_NODE)
+ child_type_name = TYPE_NAME (child_type);
+ else if (TREE_CODE (TYPE_NAME (child_type)) == TYPE_DECL)
+ {
+ child_type_name = DECL_NAME (TYPE_NAME (child_type));
+ if (child_type_name && template_name_p (child_type_name))
+ child_type_name
+ = DECL_ASSEMBLER_NAME (TYPE_NAME (child_type));
+ }
+ else
+ continue;
+
+ CONTIN;
+ PUT_SDB_DEF (IDENTIFIER_POINTER (child_type_name));
+ PUT_SDB_INT_VAL (TREE_INT_CST_LOW (BINFO_OFFSET (child)));
+ PUT_SDB_SCL (member_scl);
+ sdbout_type (BINFO_TYPE (child));
+ PUT_SDB_ENDEF;
+ }
+
+ /* output the individual fields */
+
+ if (TREE_CODE (type) == ENUMERAL_TYPE)
+ for (tem = TYPE_FIELDS (type); tem; tem = TREE_CHAIN (tem))
+ {
+ PUT_SDB_DEF (IDENTIFIER_POINTER (TREE_PURPOSE (tem)));
+ PUT_SDB_INT_VAL (TREE_INT_CST_LOW (TREE_VALUE (tem)));
+ PUT_SDB_SCL (C_MOE);
+ PUT_SDB_TYPE (T_MOE);
+ PUT_SDB_ENDEF;
+ }
+
+ else /* record or union type */
+ for (tem = TYPE_FIELDS (type); tem; tem = TREE_CHAIN (tem))
+ /* Output the name, type, position (in bits), size (in bits)
+ of each field. */
+
+ /* Omit here the nameless fields that are used to skip bits.
+ Also omit fields with variable size or position.
+ Also omit non FIELD_DECL nodes that GNU C++ may put here. */
+ if (TREE_CODE (tem) == FIELD_DECL
+ && DECL_NAME (tem) != 0
+ && TREE_CODE (DECL_SIZE (tem)) == INTEGER_CST
+ && TREE_CODE (DECL_FIELD_BITPOS (tem)) == INTEGER_CST)
+ {
+ char *name;
+
+ CONTIN;
+ if (DECL_LANG_SPECIFIC (tem))
+ name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (tem));
+ else
+ name = IDENTIFIER_POINTER (DECL_NAME (tem));
+ PUT_SDB_DEF (name);
+ if (DECL_BIT_FIELD_TYPE (tem))
+ {
+ PUT_SDB_INT_VAL (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (tem)));
+ PUT_SDB_SCL (C_FIELD);
+ sdbout_type (DECL_BIT_FIELD_TYPE (tem));
+ PUT_SDB_SIZE (TREE_INT_CST_LOW (DECL_SIZE (tem)));
+ }
+ else
+ {
+ PUT_SDB_INT_VAL (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (tem))
+ / BITS_PER_UNIT);
+ PUT_SDB_SCL (member_scl);
+ sdbout_type (TREE_TYPE (tem));
+ }
+ PUT_SDB_ENDEF;
+ }
+ /* output end of a structure,union, or enumeral definition */
+
+ PUT_SDB_PLAIN_DEF ("eos");
+ PUT_SDB_INT_VAL (size);
+ PUT_SDB_SCL (C_EOS);
+ PUT_SDB_TAG (KNOWN_TYPE_TAG (type));
+ PUT_SDB_SIZE (size);
+ PUT_SDB_ENDEF;
+ break;
+ }
+ }
+}
+
+/* The following two functions output definitions of function parameters.
+ Each parameter gets a definition locating it in the parameter list.
+ Each parameter that is a register variable gets a second definition
+ locating it in the register.
+
+ Printing or argument lists in gdb uses the definitions that
+ locate in the parameter list. But reference to the variable in
+ expressions uses preferentially the definition as a register. */
+
+/* Output definitions, referring to storage in the parmlist,
+ of all the parms in PARMS, which is a chain of PARM_DECL nodes. */
+
+static void
+sdbout_parms (parms)
+ tree parms;
+{
+ for (; parms; parms = TREE_CHAIN (parms))
+ if (DECL_NAME (parms))
+ {
+ int current_sym_value = 0;
+ char *name = IDENTIFIER_POINTER (DECL_NAME (parms));
+
+ if (name == 0 || *name == 0)
+ name = gen_fake_label ();
+
+ /* Perform any necessary register eliminations on the parameter's rtl,
+ so that the debugging output will be accurate. */
+ DECL_INCOMING_RTL (parms) =
+ eliminate_regs (DECL_INCOMING_RTL (parms), 0, NULL_RTX);
+ DECL_RTL (parms) = eliminate_regs (DECL_RTL (parms), 0, NULL_RTX);
+
+ if (PARM_PASSED_IN_MEMORY (parms))
+ {
+ rtx addr = XEXP (DECL_INCOMING_RTL (parms), 0);
+ tree type;
+
+ /* ??? Here we assume that the parm address is indexed
+ off the frame pointer or arg pointer.
+ If that is not true, we produce meaningless results,
+ but do not crash. */
+ if (GET_CODE (addr) == PLUS
+ && GET_CODE (XEXP (addr, 1)) == CONST_INT)
+ current_sym_value = INTVAL (XEXP (addr, 1));
+ else
+ current_sym_value = 0;
+
+ if (GET_CODE (DECL_RTL (parms)) == REG
+ && REGNO (DECL_RTL (parms)) >= 0
+ && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER)
+ type = DECL_ARG_TYPE (parms);
+ else
+ {
+ int original_sym_value = current_sym_value;
+
+ /* This is the case where the parm is passed as an int or
+ double and it is converted to a char, short or float
+ and stored back in the parmlist. In this case, describe
+ the parm with the variable's declared type, and adjust
+ the address if the least significant bytes (which we are
+ using) are not the first ones. */
+#if BYTES_BIG_ENDIAN
+ if (TREE_TYPE (parms) != DECL_ARG_TYPE (parms))
+ current_sym_value +=
+ (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms)))
+ - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms))));
+#endif
+ if (GET_CODE (DECL_RTL (parms)) == MEM
+ && GET_CODE (XEXP (DECL_RTL (parms), 0)) == PLUS
+ && (GET_CODE (XEXP (XEXP (DECL_RTL (parms), 0), 1))
+ == CONST_INT)
+ && (INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1))
+ == current_sym_value))
+ type = TREE_TYPE (parms);
+ else
+ {
+ current_sym_value = original_sym_value;
+ type = DECL_ARG_TYPE (parms);
+ }
+ }
+
+ PUT_SDB_DEF (name);
+ PUT_SDB_INT_VAL (DEBUGGER_ARG_OFFSET (current_sym_value, addr));
+ PUT_SDB_SCL (C_ARG);
+ PUT_SDB_TYPE (plain_type (type));
+ PUT_SDB_ENDEF;
+ }
+ else if (GET_CODE (DECL_RTL (parms)) == REG)
+ {
+ rtx best_rtl;
+ /* Parm passed in registers and lives in registers or nowhere. */
+
+ /* If parm lives in a register, use that register;
+ pretend the parm was passed there. It would be more consistent
+ to describe the register where the parm was passed,
+ but in practice that register usually holds something else. */
+ if (REGNO (DECL_RTL (parms)) >= 0
+ && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER)
+ best_rtl = DECL_RTL (parms);
+ /* If the parm lives nowhere,
+ use the register where it was passed. */
+ else
+ best_rtl = DECL_INCOMING_RTL (parms);
+
+ PUT_SDB_DEF (name);
+ PUT_SDB_INT_VAL (DBX_REGISTER_NUMBER (REGNO (best_rtl)));
+ PUT_SDB_SCL (C_REGPARM);
+ PUT_SDB_TYPE (plain_type (TREE_TYPE (parms), 0));
+ PUT_SDB_ENDEF;
+ }
+ else if (GET_CODE (DECL_RTL (parms)) == MEM
+ && XEXP (DECL_RTL (parms), 0) != const0_rtx)
+ {
+ /* Parm was passed in registers but lives on the stack. */
+
+ /* DECL_RTL looks like (MEM (PLUS (REG...) (CONST_INT...))),
+ in which case we want the value of that CONST_INT,
+ or (MEM (REG ...)) or (MEM (MEM ...)),
+ in which case we use a value of zero. */
+ if (GET_CODE (XEXP (DECL_RTL (parms), 0)) == REG
+ || GET_CODE (XEXP (DECL_RTL (parms), 0)) == MEM)
+ current_sym_value = 0;
+ else
+ current_sym_value = INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1));
+
+ /* Again, this assumes the offset is based on the arg pointer. */
+ PUT_SDB_DEF (name);
+ PUT_SDB_INT_VAL (DEBUGGER_ARG_OFFSET (current_sym_value,
+ XEXP (DECL_RTL (parms), 0)));
+ PUT_SDB_SCL (C_ARG);
+ PUT_SDB_TYPE (plain_type (TREE_TYPE (parms), 0));
+ PUT_SDB_ENDEF;
+ }
+ }
+}
+
+/* Output definitions for the places where parms live during the function,
+ when different from where they were passed, when the parms were passed
+ in memory.
+
+ It is not useful to do this for parms passed in registers
+ that live during the function in different registers, because it is
+ impossible to look in the passed register for the passed value,
+ so we use the within-the-function register to begin with.
+
+ PARMS is a chain of PARM_DECL nodes. */
+
+static void
+sdbout_reg_parms (parms)
+ tree parms;
+{
+ for (; parms; parms = TREE_CHAIN (parms))
+ if (DECL_NAME (parms))
+ {
+ char *name = IDENTIFIER_POINTER (DECL_NAME (parms));
+
+ /* Report parms that live in registers during the function
+ but were passed in memory. */
+ if (GET_CODE (DECL_RTL (parms)) == REG
+ && REGNO (DECL_RTL (parms)) >= 0
+ && REGNO (DECL_RTL (parms)) < FIRST_PSEUDO_REGISTER
+ && PARM_PASSED_IN_MEMORY (parms))
+ {
+ if (name == 0 || *name == 0)
+ name = gen_fake_label ();
+ PUT_SDB_DEF (name);
+ PUT_SDB_INT_VAL (DBX_REGISTER_NUMBER (REGNO (DECL_RTL (parms))));
+ PUT_SDB_SCL (C_REG);
+ PUT_SDB_TYPE (plain_type (TREE_TYPE (parms), 0));
+ PUT_SDB_ENDEF;
+ }
+ /* Report parms that live in memory but not where they were passed. */
+ else if (GET_CODE (DECL_RTL (parms)) == MEM
+ && GET_CODE (XEXP (DECL_RTL (parms), 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (DECL_RTL (parms), 0), 1)) == CONST_INT
+ && PARM_PASSED_IN_MEMORY (parms)
+ && ! rtx_equal_p (DECL_RTL (parms), DECL_INCOMING_RTL (parms)))
+ {
+#if 0 /* ??? It is not clear yet what should replace this. */
+ int offset = DECL_OFFSET (parms) / BITS_PER_UNIT;
+ /* A parm declared char is really passed as an int,
+ so it occupies the least significant bytes.
+ On a big-endian machine those are not the low-numbered ones. */
+#if BYTES_BIG_ENDIAN
+ if (offset != -1 && TREE_TYPE (parms) != DECL_ARG_TYPE (parms))
+ offset += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parms)))
+ - GET_MODE_SIZE (GET_MODE (DECL_RTL (parms))));
+#endif
+ if (INTVAL (XEXP (XEXP (DECL_RTL (parms), 0), 1)) != offset) {...}
+#endif
+ {
+ if (name == 0 || *name == 0)
+ name = gen_fake_label ();
+ PUT_SDB_DEF (name);
+ PUT_SDB_INT_VAL (DEBUGGER_AUTO_OFFSET
+ (XEXP (DECL_RTL (parms), 0)));
+ PUT_SDB_SCL (C_AUTO);
+ PUT_SDB_TYPE (plain_type (TREE_TYPE (parms)));
+ PUT_SDB_ENDEF;
+ }
+ }
+ }
+}
+
+/* Describe the beginning of an internal block within a function.
+ Also output descriptions of variables defined in this block.
+
+ N is the number of the block, by order of beginning, counting from 1,
+ and not counting the outermost (function top-level) block.
+ The blocks match the BLOCKs in DECL_INITIAL (current_function_decl),
+ if the count starts at 0 for the outermost one. */
+
+void
+sdbout_begin_block (file, line, n)
+ FILE *file;
+ int line;
+ int n;
+{
+ tree decl = current_function_decl;
+ MAKE_LINE_SAFE (line);
+
+ /* The SCO compiler does not emit a separate block for the function level
+ scope, so we avoid it here also. However, mips ECOFF compilers do emit
+ a separate block, so we retain it when MIPS_DEBUGGING_INFO is defined. */
+#ifndef MIPS_DEBUGGING_INFO
+ if (n != 1)
+#endif
+ PUT_SDB_BLOCK_START (line - sdb_begin_function_line);
+
+ if (n == 1)
+ {
+ /* Include the outermost BLOCK's variables in block 1. */
+ next_block_number = 0;
+ do_block = 0;
+ sdbout_block (DECL_INITIAL (decl));
+ }
+ /* If -g1, suppress all the internal symbols of functions
+ except for arguments. */
+ if (debug_info_level != DINFO_LEVEL_TERSE)
+ {
+ next_block_number = 0;
+ do_block = n;
+ sdbout_block (DECL_INITIAL (decl));
+ }
+
+#ifdef SDB_ALLOW_FORWARD_REFERENCES
+ sdbout_dequeue_anonymous_types ();
+#endif
+}
+
+/* Describe the end line-number of an internal block within a function. */
+
+void
+sdbout_end_block (file, line, n)
+ FILE *file;
+ int line;
+ int n;
+{
+ MAKE_LINE_SAFE (line);
+
+ /* The SCO compiler does not emit a separate block for the function level
+ scope, so we avoid it here also. However, mips ECOFF compilers do emit
+ a separate block, so we retain it when MIPS_DEBUGGING_INFO is defined. */
+#ifndef MIPS_DEBUGGING_INFO
+ if (n != 1)
+#endif
+ PUT_SDB_BLOCK_END (line - sdb_begin_function_line);
+}
+
+/* Output sdb info for the current function name.
+ Called from assemble_start_function. */
+
+void
+sdbout_mark_begin_function ()
+{
+ sdbout_symbol (current_function_decl, 0);
+}
+
+/* Called at beginning of function body (after prologue).
+ Record the function's starting line number, so we can output
+ relative line numbers for the other lines.
+ Describe beginning of outermost block.
+ Also describe the parameter list. */
+
+void
+sdbout_begin_function (line)
+ int line;
+{
+ sdb_begin_function_line = line - 1;
+ PUT_SDB_FUNCTION_START (line);
+ sdbout_parms (DECL_ARGUMENTS (current_function_decl));
+ sdbout_reg_parms (DECL_ARGUMENTS (current_function_decl));
+}
+
+/* Called at end of function (before epilogue).
+ Describe end of outermost block. */
+
+void
+sdbout_end_function (line)
+ int line;
+{
+#ifdef SDB_ALLOW_FORWARD_REFERENCES
+ sdbout_dequeue_anonymous_types ();
+#endif
+
+ MAKE_LINE_SAFE (line);
+ PUT_SDB_FUNCTION_END (line - sdb_begin_function_line);
+
+ /* Indicate we are between functions, for line-number output. */
+ sdb_begin_function_line = -1;
+}
+
+/* Output sdb info for the absolute end of a function.
+ Called after the epilogue is output. */
+
+void
+sdbout_end_epilogue ()
+{
+ char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl));
+ PUT_SDB_EPILOGUE_END (name);
+}
+
+/* Output sdb info for the given label. Called only if LABEL_NAME (insn)
+ is present. */
+
+void
+sdbout_label (insn)
+ register rtx insn;
+{
+ PUT_SDB_DEF (LABEL_NAME (insn));
+ PUT_SDB_VAL (insn);
+ PUT_SDB_SCL (C_LABEL);
+ PUT_SDB_TYPE (T_NULL);
+ PUT_SDB_ENDEF;
+}
+
+#endif /* SDB_DEBUGGING_INFO */
diff --git a/gnu/usr.bin/cc/cc_int/stmt.c b/gnu/usr.bin/cc/cc_int/stmt.c
new file mode 100644
index 0000000..4069829
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/stmt.c
@@ -0,0 +1,5431 @@
+/* Expands front end tree to back end RTL for GNU C-Compiler
+ Copyright (C) 1987, 88, 89, 92, 93, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file handles the generation of rtl code from tree structure
+ above the level of expressions, using subroutines in exp*.c and emit-rtl.c.
+ It also creates the rtl expressions for parameters and auto variables
+ and has full responsibility for allocating stack slots.
+
+ The functions whose names start with `expand_' are called by the
+ parser to generate RTL instructions for various kinds of constructs.
+
+ Some control and binding constructs require calling several such
+ functions at different times. For example, a simple if-then
+ is expanded by calling `expand_start_cond' (with the condition-expression
+ as argument) before parsing the then-clause and calling `expand_end_cond'
+ after parsing the then-clause. */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "rtl.h"
+#include "tree.h"
+#include "flags.h"
+#include "function.h"
+#include "insn-flags.h"
+#include "insn-config.h"
+#include "insn-codes.h"
+#include "expr.h"
+#include "hard-reg-set.h"
+#include "obstack.h"
+#include "loop.h"
+#include "recog.h"
+#include "machmode.h"
+
+#include "bytecode.h"
+#include "bc-typecd.h"
+#include "bc-opcode.h"
+#include "bc-optab.h"
+#include "bc-emit.h"
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+struct obstack stmt_obstack;
+
+/* Filename and line number of last line-number note,
+ whether we actually emitted it or not. */
+char *emit_filename;
+int emit_lineno;
+
+/* Nonzero if within a ({...}) grouping, in which case we must
+ always compute a value for each expr-stmt in case it is the last one. */
+
+int expr_stmts_for_value;
+
+/* Each time we expand an expression-statement,
+ record the expr's type and its RTL value here. */
+
+static tree last_expr_type;
+static rtx last_expr_value;
+
+/* Each time we expand the end of a binding contour (in `expand_end_bindings')
+ and we emit a new NOTE_INSN_BLOCK_END note, we save a pointer to it here.
+ This is used by the `remember_end_note' function to record the endpoint
+ of each generated block in its associated BLOCK node. */
+
+static rtx last_block_end_note;
+
+/* Number of binding contours started so far in this function. */
+
+int block_start_count;
+
+/* Nonzero if function being compiled needs to
+ return the address of where it has put a structure value. */
+
+extern int current_function_returns_pcc_struct;
+
+/* Label that will go on parm cleanup code, if any.
+ Jumping to this label runs cleanup code for parameters, if
+ such code must be run. Following this code is the logical return label. */
+
+extern rtx cleanup_label;
+
+/* Label that will go on function epilogue.
+ Jumping to this label serves as a "return" instruction
+ on machines which require execution of the epilogue on all returns. */
+
+extern rtx return_label;
+
+/* List (chain of EXPR_LISTs) of pseudo-regs of SAVE_EXPRs.
+ So we can mark them all live at the end of the function, if nonopt. */
+extern rtx save_expr_regs;
+
+/* Offset to end of allocated area of stack frame.
+ If stack grows down, this is the address of the last stack slot allocated.
+ If stack grows up, this is the address for the next slot. */
+extern int frame_offset;
+
+/* Label to jump back to for tail recursion, or 0 if we have
+ not yet needed one for this function. */
+extern rtx tail_recursion_label;
+
+/* Place after which to insert the tail_recursion_label if we need one. */
+extern rtx tail_recursion_reentry;
+
+/* Location at which to save the argument pointer if it will need to be
+ referenced. There are two cases where this is done: if nonlocal gotos
+ exist, or if vars whose is an offset from the argument pointer will be
+ needed by inner routines. */
+
+extern rtx arg_pointer_save_area;
+
+/* Chain of all RTL_EXPRs that have insns in them. */
+extern tree rtl_expr_chain;
+
+#if 0 /* Turned off because 0 seems to work just as well. */
+/* Cleanup lists are required for binding levels regardless of whether
+ that binding level has cleanups or not. This node serves as the
+ cleanup list whenever an empty list is required. */
+static tree empty_cleanup_list;
+#endif
+
+extern void (*interim_eh_hook) PROTO((tree));
+
+/* Functions and data structures for expanding case statements. */
+
+/* Case label structure, used to hold info on labels within case
+ statements. We handle "range" labels; for a single-value label
+ as in C, the high and low limits are the same.
+
+ A chain of case nodes is initially maintained via the RIGHT fields
+ in the nodes. Nodes with higher case values are later in the list.
+
+ Switch statements can be output in one of two forms. A branch table
+ is used if there are more than a few labels and the labels are dense
+ within the range between the smallest and largest case value. If a
+ branch table is used, no further manipulations are done with the case
+ node chain.
+
+ The alternative to the use of a branch table is to generate a series
+ of compare and jump insns. When that is done, we use the LEFT, RIGHT,
+ and PARENT fields to hold a binary tree. Initially the tree is
+ totally unbalanced, with everything on the right. We balance the tree
+ with nodes on the left having lower case values than the parent
+ and nodes on the right having higher values. We then output the tree
+ in order. */
+
+struct case_node
+{
+ struct case_node *left; /* Left son in binary tree */
+ struct case_node *right; /* Right son in binary tree; also node chain */
+ struct case_node *parent; /* Parent of node in binary tree */
+ tree low; /* Lowest index value for this label */
+ tree high; /* Highest index value for this label */
+ tree code_label; /* Label to jump to when node matches */
+};
+
+typedef struct case_node case_node;
+typedef struct case_node *case_node_ptr;
+
+/* These are used by estimate_case_costs and balance_case_nodes. */
+
+/* This must be a signed type, and non-ANSI compilers lack signed char. */
+static short *cost_table;
+static int use_cost_table;
+
+/* Stack of control and binding constructs we are currently inside.
+
+ These constructs begin when you call `expand_start_WHATEVER'
+ and end when you call `expand_end_WHATEVER'. This stack records
+ info about how the construct began that tells the end-function
+ what to do. It also may provide information about the construct
+ to alter the behavior of other constructs within the body.
+ For example, they may affect the behavior of C `break' and `continue'.
+
+ Each construct gets one `struct nesting' object.
+ All of these objects are chained through the `all' field.
+ `nesting_stack' points to the first object (innermost construct).
+ The position of an entry on `nesting_stack' is in its `depth' field.
+
+ Each type of construct has its own individual stack.
+ For example, loops have `loop_stack'. Each object points to the
+ next object of the same type through the `next' field.
+
+ Some constructs are visible to `break' exit-statements and others
+ are not. Which constructs are visible depends on the language.
+ Therefore, the data structure allows each construct to be visible
+ or not, according to the args given when the construct is started.
+ The construct is visible if the `exit_label' field is non-null.
+ In that case, the value should be a CODE_LABEL rtx. */
+
+struct nesting
+{
+ struct nesting *all;
+ struct nesting *next;
+ int depth;
+ rtx exit_label;
+ union
+ {
+ /* For conds (if-then and if-then-else statements). */
+ struct
+ {
+ /* Label for the end of the if construct.
+ There is none if EXITFLAG was not set
+ and no `else' has been seen yet. */
+ rtx endif_label;
+ /* Label for the end of this alternative.
+ This may be the end of the if or the next else/elseif. */
+ rtx next_label;
+ } cond;
+ /* For loops. */
+ struct
+ {
+ /* Label at the top of the loop; place to loop back to. */
+ rtx start_label;
+ /* Label at the end of the whole construct. */
+ rtx end_label;
+ /* Label before a jump that branches to the end of the whole
+ construct. This is where destructors go if any. */
+ rtx alt_end_label;
+ /* Label for `continue' statement to jump to;
+ this is in front of the stepper of the loop. */
+ rtx continue_label;
+ } loop;
+ /* For variable binding contours. */
+ struct
+ {
+ /* Sequence number of this binding contour within the function,
+ in order of entry. */
+ int block_start_count;
+ /* Nonzero => value to restore stack to on exit. Complemented by
+ bc_stack_level (see below) when generating bytecodes. */
+ rtx stack_level;
+ /* The NOTE that starts this contour.
+ Used by expand_goto to check whether the destination
+ is within each contour or not. */
+ rtx first_insn;
+ /* Innermost containing binding contour that has a stack level. */
+ struct nesting *innermost_stack_block;
+ /* List of cleanups to be run on exit from this contour.
+ This is a list of expressions to be evaluated.
+ The TREE_PURPOSE of each link is the ..._DECL node
+ which the cleanup pertains to. */
+ tree cleanups;
+ /* List of cleanup-lists of blocks containing this block,
+ as they were at the locus where this block appears.
+ There is an element for each containing block,
+ ordered innermost containing block first.
+ The tail of this list can be 0 (was empty_cleanup_list),
+ if all remaining elements would be empty lists.
+ The element's TREE_VALUE is the cleanup-list of that block,
+ which may be null. */
+ tree outer_cleanups;
+ /* Chain of labels defined inside this binding contour.
+ For contours that have stack levels or cleanups. */
+ struct label_chain *label_chain;
+ /* Number of function calls seen, as of start of this block. */
+ int function_call_count;
+ /* Bytecode specific: stack level to restore stack to on exit. */
+ int bc_stack_level;
+ } block;
+ /* For switch (C) or case (Pascal) statements,
+ and also for dummies (see `expand_start_case_dummy'). */
+ struct
+ {
+ /* The insn after which the case dispatch should finally
+ be emitted. Zero for a dummy. */
+ rtx start;
+ /* For bytecodes, the case table is in-lined right in the code.
+ A label is needed for skipping over this block. It is only
+ used when generating bytecodes. */
+ rtx skip_label;
+ /* A list of case labels, kept in ascending order by value
+ as the list is built.
+ During expand_end_case, this list may be rearranged into a
+ nearly balanced binary tree. */
+ struct case_node *case_list;
+ /* Label to jump to if no case matches. */
+ tree default_label;
+ /* The expression to be dispatched on. */
+ tree index_expr;
+ /* Type that INDEX_EXPR should be converted to. */
+ tree nominal_type;
+ /* Number of range exprs in case statement. */
+ int num_ranges;
+ /* Name of this kind of statement, for warnings. */
+ char *printname;
+ /* Nonzero if a case label has been seen in this case stmt. */
+ char seenlabel;
+ } case_stmt;
+ } data;
+};
+
+/* Chain of all pending binding contours. */
+struct nesting *block_stack;
+
+/* If any new stacks are added here, add them to POPSTACKS too. */
+
+/* Chain of all pending binding contours that restore stack levels
+ or have cleanups. */
+struct nesting *stack_block_stack;
+
+/* Chain of all pending conditional statements. */
+struct nesting *cond_stack;
+
+/* Chain of all pending loops. */
+struct nesting *loop_stack;
+
+/* Chain of all pending case or switch statements. */
+struct nesting *case_stack;
+
+/* Separate chain including all of the above,
+ chained through the `all' field. */
+struct nesting *nesting_stack;
+
+/* Number of entries on nesting_stack now. */
+int nesting_depth;
+
+/* Allocate and return a new `struct nesting'. */
+
+#define ALLOC_NESTING() \
+ (struct nesting *) obstack_alloc (&stmt_obstack, sizeof (struct nesting))
+
+/* Pop the nesting stack element by element until we pop off
+ the element which is at the top of STACK.
+ Update all the other stacks, popping off elements from them
+ as we pop them from nesting_stack. */
+
+#define POPSTACK(STACK) \
+do { struct nesting *target = STACK; \
+ struct nesting *this; \
+ do { this = nesting_stack; \
+ if (loop_stack == this) \
+ loop_stack = loop_stack->next; \
+ if (cond_stack == this) \
+ cond_stack = cond_stack->next; \
+ if (block_stack == this) \
+ block_stack = block_stack->next; \
+ if (stack_block_stack == this) \
+ stack_block_stack = stack_block_stack->next; \
+ if (case_stack == this) \
+ case_stack = case_stack->next; \
+ nesting_depth = nesting_stack->depth - 1; \
+ nesting_stack = this->all; \
+ obstack_free (&stmt_obstack, this); } \
+ while (this != target); } while (0)
+
+/* In some cases it is impossible to generate code for a forward goto
+ until the label definition is seen. This happens when it may be necessary
+ for the goto to reset the stack pointer: we don't yet know how to do that.
+ So expand_goto puts an entry on this fixup list.
+ Each time a binding contour that resets the stack is exited,
+ we check each fixup.
+ If the target label has now been defined, we can insert the proper code. */
+
+struct goto_fixup
+{
+ /* Points to following fixup. */
+ struct goto_fixup *next;
+ /* Points to the insn before the jump insn.
+ If more code must be inserted, it goes after this insn. */
+ rtx before_jump;
+ /* The LABEL_DECL that this jump is jumping to, or 0
+ for break, continue or return. */
+ tree target;
+ /* The BLOCK for the place where this goto was found. */
+ tree context;
+ /* The CODE_LABEL rtx that this is jumping to. */
+ rtx target_rtl;
+ /* Number of binding contours started in current function
+ before the label reference. */
+ int block_start_count;
+ /* The outermost stack level that should be restored for this jump.
+ Each time a binding contour that resets the stack is exited,
+ if the target label is *not* yet defined, this slot is updated. */
+ rtx stack_level;
+ /* List of lists of cleanup expressions to be run by this goto.
+ There is one element for each block that this goto is within.
+ The tail of this list can be 0 (was empty_cleanup_list),
+ if all remaining elements would be empty.
+ The TREE_VALUE contains the cleanup list of that block as of the
+ time this goto was seen.
+ The TREE_ADDRESSABLE flag is 1 for a block that has been exited. */
+ tree cleanup_list_list;
+
+ /* Bytecode specific members follow */
+
+ /* The label that this jump is jumping to, or 0 for break, continue
+ or return. */
+ struct bc_label *bc_target;
+
+ /* The label we use for the fixup patch */
+ struct bc_label *label;
+
+ /* True (non-0) if fixup has been handled */
+ int bc_handled:1;
+
+ /* Like stack_level above, except refers to the interpreter stack */
+ int bc_stack_level;
+};
+
+static struct goto_fixup *goto_fixup_chain;
+
+/* Within any binding contour that must restore a stack level,
+ all labels are recorded with a chain of these structures. */
+
+struct label_chain
+{
+ /* Points to following fixup. */
+ struct label_chain *next;
+ tree label;
+};
+static void expand_goto_internal PROTO((tree, rtx, rtx));
+static void bc_expand_goto_internal PROTO((enum bytecode_opcode,
+ struct bc_label *, tree));
+static int expand_fixup PROTO((tree, rtx, rtx));
+static void bc_expand_fixup PROTO((enum bytecode_opcode,
+ struct bc_label *, int));
+static void fixup_gotos PROTO((struct nesting *, rtx, tree,
+ rtx, int));
+static void bc_fixup_gotos PROTO((struct nesting *, int, tree,
+ rtx, int));
+static int warn_if_unused_value PROTO((tree));
+static void bc_expand_start_cond PROTO((tree, int));
+static void bc_expand_end_cond PROTO((void));
+static void bc_expand_start_else PROTO((void));
+static void bc_expand_end_loop PROTO((void));
+static void bc_expand_end_bindings PROTO((tree, int, int));
+static void bc_expand_decl PROTO((tree, tree));
+static void bc_expand_variable_local_init PROTO((tree));
+static void bc_expand_decl_init PROTO((tree));
+static void expand_null_return_1 PROTO((rtx, int));
+static int tail_recursion_args PROTO((tree, tree));
+static void expand_cleanups PROTO((tree, tree));
+static void bc_expand_start_case PROTO((struct nesting *, tree,
+ tree, char *));
+static int bc_pushcase PROTO((tree, tree));
+static void bc_check_for_full_enumeration_handling PROTO((tree));
+static void bc_expand_end_case PROTO((tree));
+static void do_jump_if_equal PROTO((rtx, rtx, rtx, int));
+static int estimate_case_costs PROTO((case_node_ptr));
+static void group_case_nodes PROTO((case_node_ptr));
+static void balance_case_nodes PROTO((case_node_ptr *,
+ case_node_ptr));
+static int node_has_low_bound PROTO((case_node_ptr, tree));
+static int node_has_high_bound PROTO((case_node_ptr, tree));
+static int node_is_bounded PROTO((case_node_ptr, tree));
+static void emit_jump_if_reachable PROTO((rtx));
+static void emit_case_nodes PROTO((rtx, case_node_ptr, rtx, tree));
+
+int bc_expand_exit_loop_if_false ();
+void bc_expand_start_cond ();
+void bc_expand_end_cond ();
+void bc_expand_start_else ();
+void bc_expand_end_bindings ();
+void bc_expand_start_case ();
+void bc_check_for_full_enumeration_handling ();
+void bc_expand_end_case ();
+void bc_expand_decl ();
+
+extern rtx bc_allocate_local ();
+extern rtx bc_allocate_variable_array ();
+
+void
+init_stmt ()
+{
+ gcc_obstack_init (&stmt_obstack);
+#if 0
+ empty_cleanup_list = build_tree_list (NULL_TREE, NULL_TREE);
+#endif
+}
+
+void
+init_stmt_for_function ()
+{
+ /* We are not currently within any block, conditional, loop or case. */
+ block_stack = 0;
+ stack_block_stack = 0;
+ loop_stack = 0;
+ case_stack = 0;
+ cond_stack = 0;
+ nesting_stack = 0;
+ nesting_depth = 0;
+
+ block_start_count = 0;
+
+ /* No gotos have been expanded yet. */
+ goto_fixup_chain = 0;
+
+ /* We are not processing a ({...}) grouping. */
+ expr_stmts_for_value = 0;
+ last_expr_type = 0;
+}
+
+void
+save_stmt_status (p)
+ struct function *p;
+{
+ p->block_stack = block_stack;
+ p->stack_block_stack = stack_block_stack;
+ p->cond_stack = cond_stack;
+ p->loop_stack = loop_stack;
+ p->case_stack = case_stack;
+ p->nesting_stack = nesting_stack;
+ p->nesting_depth = nesting_depth;
+ p->block_start_count = block_start_count;
+ p->last_expr_type = last_expr_type;
+ p->last_expr_value = last_expr_value;
+ p->expr_stmts_for_value = expr_stmts_for_value;
+ p->emit_filename = emit_filename;
+ p->emit_lineno = emit_lineno;
+ p->goto_fixup_chain = goto_fixup_chain;
+}
+
+void
+restore_stmt_status (p)
+ struct function *p;
+{
+ block_stack = p->block_stack;
+ stack_block_stack = p->stack_block_stack;
+ cond_stack = p->cond_stack;
+ loop_stack = p->loop_stack;
+ case_stack = p->case_stack;
+ nesting_stack = p->nesting_stack;
+ nesting_depth = p->nesting_depth;
+ block_start_count = p->block_start_count;
+ last_expr_type = p->last_expr_type;
+ last_expr_value = p->last_expr_value;
+ expr_stmts_for_value = p->expr_stmts_for_value;
+ emit_filename = p->emit_filename;
+ emit_lineno = p->emit_lineno;
+ goto_fixup_chain = p->goto_fixup_chain;
+}
+
+/* Emit a no-op instruction. */
+
+void
+emit_nop ()
+{
+ rtx last_insn;
+
+ if (!output_bytecode)
+ {
+ last_insn = get_last_insn ();
+ if (!optimize
+ && (GET_CODE (last_insn) == CODE_LABEL
+ || prev_real_insn (last_insn) == 0))
+ emit_insn (gen_nop ());
+ }
+}
+
+/* Return the rtx-label that corresponds to a LABEL_DECL,
+ creating it if necessary. */
+
+rtx
+label_rtx (label)
+ tree label;
+{
+ if (TREE_CODE (label) != LABEL_DECL)
+ abort ();
+
+ if (DECL_RTL (label))
+ return DECL_RTL (label);
+
+ return DECL_RTL (label) = gen_label_rtx ();
+}
+
+/* Add an unconditional jump to LABEL as the next sequential instruction. */
+
+void
+emit_jump (label)
+ rtx label;
+{
+ do_pending_stack_adjust ();
+ emit_jump_insn (gen_jump (label));
+ emit_barrier ();
+}
+
+/* Emit code to jump to the address
+ specified by the pointer expression EXP. */
+
+void
+expand_computed_goto (exp)
+ tree exp;
+{
+ if (output_bytecode)
+ {
+ bc_expand_expr (exp);
+ bc_emit_instruction (jumpP);
+ }
+ else
+ {
+ rtx x = expand_expr (exp, NULL_RTX, VOIDmode, 0);
+ emit_queue ();
+ emit_indirect_jump (x);
+ }
+}
+
+/* Handle goto statements and the labels that they can go to. */
+
+/* Specify the location in the RTL code of a label LABEL,
+ which is a LABEL_DECL tree node.
+
+ This is used for the kind of label that the user can jump to with a
+ goto statement, and for alternatives of a switch or case statement.
+ RTL labels generated for loops and conditionals don't go through here;
+ they are generated directly at the RTL level, by other functions below.
+
+ Note that this has nothing to do with defining label *names*.
+ Languages vary in how they do that and what that even means. */
+
+void
+expand_label (label)
+ tree label;
+{
+ struct label_chain *p;
+
+ if (output_bytecode)
+ {
+ if (! DECL_RTL (label))
+ DECL_RTL (label) = bc_gen_rtx ((char *) 0, 0, bc_get_bytecode_label ());
+ if (! bc_emit_bytecode_labeldef (BYTECODE_BC_LABEL (DECL_RTL (label))))
+ error ("multiply defined label");
+ return;
+ }
+
+ do_pending_stack_adjust ();
+ emit_label (label_rtx (label));
+ if (DECL_NAME (label))
+ LABEL_NAME (DECL_RTL (label)) = IDENTIFIER_POINTER (DECL_NAME (label));
+
+ if (stack_block_stack != 0)
+ {
+ p = (struct label_chain *) oballoc (sizeof (struct label_chain));
+ p->next = stack_block_stack->data.block.label_chain;
+ stack_block_stack->data.block.label_chain = p;
+ p->label = label;
+ }
+}
+
+/* Declare that LABEL (a LABEL_DECL) may be used for nonlocal gotos
+ from nested functions. */
+
+void
+declare_nonlocal_label (label)
+ tree label;
+{
+ nonlocal_labels = tree_cons (NULL_TREE, label, nonlocal_labels);
+ LABEL_PRESERVE_P (label_rtx (label)) = 1;
+ if (nonlocal_goto_handler_slot == 0)
+ {
+ nonlocal_goto_handler_slot
+ = assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0);
+ emit_stack_save (SAVE_NONLOCAL,
+ &nonlocal_goto_stack_level,
+ PREV_INSN (tail_recursion_reentry));
+ }
+}
+
+/* Generate RTL code for a `goto' statement with target label LABEL.
+ LABEL should be a LABEL_DECL tree node that was or will later be
+ defined with `expand_label'. */
+
+void
+expand_goto (label)
+ tree label;
+{
+ tree context;
+
+ if (output_bytecode)
+ {
+ expand_goto_internal (label, label_rtx (label), NULL_RTX);
+ return;
+ }
+
+ /* Check for a nonlocal goto to a containing function. */
+ context = decl_function_context (label);
+ if (context != 0 && context != current_function_decl)
+ {
+ struct function *p = find_function_data (context);
+ rtx label_ref = gen_rtx (LABEL_REF, Pmode, label_rtx (label));
+ rtx temp;
+
+ p->has_nonlocal_label = 1;
+ current_function_has_nonlocal_goto = 1;
+ LABEL_REF_NONLOCAL_P (label_ref) = 1;
+
+ /* Copy the rtl for the slots so that they won't be shared in
+ case the virtual stack vars register gets instantiated differently
+ in the parent than in the child. */
+
+#if HAVE_nonlocal_goto
+ if (HAVE_nonlocal_goto)
+ emit_insn (gen_nonlocal_goto (lookup_static_chain (label),
+ copy_rtx (p->nonlocal_goto_handler_slot),
+ copy_rtx (p->nonlocal_goto_stack_level),
+ label_ref));
+ else
+#endif
+ {
+ rtx addr;
+
+ /* Restore frame pointer for containing function.
+ This sets the actual hard register used for the frame pointer
+ to the location of the function's incoming static chain info.
+ The non-local goto handler will then adjust it to contain the
+ proper value and reload the argument pointer, if needed. */
+ emit_move_insn (hard_frame_pointer_rtx, lookup_static_chain (label));
+
+ /* We have now loaded the frame pointer hardware register with
+ the address of that corresponds to the start of the virtual
+ stack vars. So replace virtual_stack_vars_rtx in all
+ addresses we use with stack_pointer_rtx. */
+
+ /* Get addr of containing function's current nonlocal goto handler,
+ which will do any cleanups and then jump to the label. */
+ addr = copy_rtx (p->nonlocal_goto_handler_slot);
+ temp = copy_to_reg (replace_rtx (addr, virtual_stack_vars_rtx,
+ hard_frame_pointer_rtx));
+
+ /* Restore the stack pointer. Note this uses fp just restored. */
+ addr = p->nonlocal_goto_stack_level;
+ if (addr)
+ addr = replace_rtx (copy_rtx (addr),
+ virtual_stack_vars_rtx,
+ hard_frame_pointer_rtx);
+
+ emit_stack_restore (SAVE_NONLOCAL, addr, NULL_RTX);
+
+ /* Put in the static chain register the nonlocal label address. */
+ emit_move_insn (static_chain_rtx, label_ref);
+ /* USE of hard_frame_pointer_rtx added for consistency; not clear if
+ really needed. */
+ emit_insn (gen_rtx (USE, VOIDmode, hard_frame_pointer_rtx));
+ emit_insn (gen_rtx (USE, VOIDmode, stack_pointer_rtx));
+ emit_insn (gen_rtx (USE, VOIDmode, static_chain_rtx));
+ emit_indirect_jump (temp);
+ }
+ }
+ else
+ expand_goto_internal (label, label_rtx (label), NULL_RTX);
+}
+
+/* Generate RTL code for a `goto' statement with target label BODY.
+ LABEL should be a LABEL_REF.
+ LAST_INSN, if non-0, is the rtx we should consider as the last
+ insn emitted (for the purposes of cleaning up a return). */
+
+static void
+expand_goto_internal (body, label, last_insn)
+ tree body;
+ rtx label;
+ rtx last_insn;
+{
+ struct nesting *block;
+ rtx stack_level = 0;
+
+ /* NOTICE! If a bytecode instruction other than `jump' is needed,
+ then the caller has to call bc_expand_goto_internal()
+ directly. This is rather an exceptional case, and there aren't
+ that many places where this is necessary. */
+ if (output_bytecode)
+ {
+ expand_goto_internal (body, label, last_insn);
+ return;
+ }
+
+ if (GET_CODE (label) != CODE_LABEL)
+ abort ();
+
+ /* If label has already been defined, we can tell now
+ whether and how we must alter the stack level. */
+
+ if (PREV_INSN (label) != 0)
+ {
+ /* Find the innermost pending block that contains the label.
+ (Check containment by comparing insn-uids.)
+ Then restore the outermost stack level within that block,
+ and do cleanups of all blocks contained in it. */
+ for (block = block_stack; block; block = block->next)
+ {
+ if (INSN_UID (block->data.block.first_insn) < INSN_UID (label))
+ break;
+ if (block->data.block.stack_level != 0)
+ stack_level = block->data.block.stack_level;
+ /* Execute the cleanups for blocks we are exiting. */
+ if (block->data.block.cleanups != 0)
+ {
+ expand_cleanups (block->data.block.cleanups, NULL_TREE);
+ do_pending_stack_adjust ();
+ }
+ }
+
+ if (stack_level)
+ {
+ /* Ensure stack adjust isn't done by emit_jump, as this would clobber
+ the stack pointer. This one should be deleted as dead by flow. */
+ clear_pending_stack_adjust ();
+ do_pending_stack_adjust ();
+ emit_stack_restore (SAVE_BLOCK, stack_level, NULL_RTX);
+ }
+
+ if (body != 0 && DECL_TOO_LATE (body))
+ error ("jump to `%s' invalidly jumps into binding contour",
+ IDENTIFIER_POINTER (DECL_NAME (body)));
+ }
+ /* Label not yet defined: may need to put this goto
+ on the fixup list. */
+ else if (! expand_fixup (body, label, last_insn))
+ {
+ /* No fixup needed. Record that the label is the target
+ of at least one goto that has no fixup. */
+ if (body != 0)
+ TREE_ADDRESSABLE (body) = 1;
+ }
+
+ emit_jump (label);
+}
+
+/* Generate a jump with OPCODE to the given bytecode LABEL which is
+ found within BODY. */
+
+static void
+bc_expand_goto_internal (opcode, label, body)
+ enum bytecode_opcode opcode;
+ struct bc_label *label;
+ tree body;
+{
+ struct nesting *block;
+ int stack_level = -1;
+
+ /* If the label is defined, adjust the stack as necessary.
+ If it's not defined, we have to push the reference on the
+ fixup list. */
+
+ if (label->defined)
+ {
+
+ /* Find the innermost pending block that contains the label.
+ (Check containment by comparing bytecode uids.) Then restore the
+ outermost stack level within that block. */
+
+ for (block = block_stack; block; block = block->next)
+ {
+ if (BYTECODE_BC_LABEL (block->data.block.first_insn)->uid < label->uid)
+ break;
+ if (block->data.block.bc_stack_level)
+ stack_level = block->data.block.bc_stack_level;
+
+ /* Execute the cleanups for blocks we are exiting. */
+ if (block->data.block.cleanups != 0)
+ {
+ expand_cleanups (block->data.block.cleanups, NULL_TREE);
+ do_pending_stack_adjust ();
+ }
+ }
+
+ /* Restore the stack level. If we need to adjust the stack, we
+ must do so after the jump, since the jump may depend on
+ what's on the stack. Thus, any stack-modifying conditional
+ jumps (these are the only ones that rely on what's on the
+ stack) go into the fixup list. */
+
+ if (stack_level >= 0
+ && stack_depth != stack_level
+ && opcode != jump)
+
+ bc_expand_fixup (opcode, label, stack_level);
+ else
+ {
+ if (stack_level >= 0)
+ bc_adjust_stack (stack_depth - stack_level);
+
+ if (body && DECL_BIT_FIELD (body))
+ error ("jump to `%s' invalidly jumps into binding contour",
+ IDENTIFIER_POINTER (DECL_NAME (body)));
+
+ /* Emit immediate jump */
+ bc_emit_bytecode (opcode);
+ bc_emit_bytecode_labelref (label);
+
+#ifdef DEBUG_PRINT_CODE
+ fputc ('\n', stderr);
+#endif
+ }
+ }
+ else
+ /* Put goto in the fixup list */
+ bc_expand_fixup (opcode, label, stack_level);
+}
+
+/* Generate if necessary a fixup for a goto
+ whose target label in tree structure (if any) is TREE_LABEL
+ and whose target in rtl is RTL_LABEL.
+
+ If LAST_INSN is nonzero, we pretend that the jump appears
+ after insn LAST_INSN instead of at the current point in the insn stream.
+
+ The fixup will be used later to insert insns just before the goto.
+ Those insns will restore the stack level as appropriate for the
+ target label, and will (in the case of C++) also invoke any object
+ destructors which have to be invoked when we exit the scopes which
+ are exited by the goto.
+
+ Value is nonzero if a fixup is made. */
+
+static int
+expand_fixup (tree_label, rtl_label, last_insn)
+ tree tree_label;
+ rtx rtl_label;
+ rtx last_insn;
+{
+ struct nesting *block, *end_block;
+
+ /* See if we can recognize which block the label will be output in.
+ This is possible in some very common cases.
+ If we succeed, set END_BLOCK to that block.
+ Otherwise, set it to 0. */
+
+ if (cond_stack
+ && (rtl_label == cond_stack->data.cond.endif_label
+ || rtl_label == cond_stack->data.cond.next_label))
+ end_block = cond_stack;
+ /* If we are in a loop, recognize certain labels which
+ are likely targets. This reduces the number of fixups
+ we need to create. */
+ else if (loop_stack
+ && (rtl_label == loop_stack->data.loop.start_label
+ || rtl_label == loop_stack->data.loop.end_label
+ || rtl_label == loop_stack->data.loop.continue_label))
+ end_block = loop_stack;
+ else
+ end_block = 0;
+
+ /* Now set END_BLOCK to the binding level to which we will return. */
+
+ if (end_block)
+ {
+ struct nesting *next_block = end_block->all;
+ block = block_stack;
+
+ /* First see if the END_BLOCK is inside the innermost binding level.
+ If so, then no cleanups or stack levels are relevant. */
+ while (next_block && next_block != block)
+ next_block = next_block->all;
+
+ if (next_block)
+ return 0;
+
+ /* Otherwise, set END_BLOCK to the innermost binding level
+ which is outside the relevant control-structure nesting. */
+ next_block = block_stack->next;
+ for (block = block_stack; block != end_block; block = block->all)
+ if (block == next_block)
+ next_block = next_block->next;
+ end_block = next_block;
+ }
+
+ /* Does any containing block have a stack level or cleanups?
+ If not, no fixup is needed, and that is the normal case
+ (the only case, for standard C). */
+ for (block = block_stack; block != end_block; block = block->next)
+ if (block->data.block.stack_level != 0
+ || block->data.block.cleanups != 0)
+ break;
+
+ if (block != end_block)
+ {
+ /* Ok, a fixup is needed. Add a fixup to the list of such. */
+ struct goto_fixup *fixup
+ = (struct goto_fixup *) oballoc (sizeof (struct goto_fixup));
+ /* In case an old stack level is restored, make sure that comes
+ after any pending stack adjust. */
+ /* ?? If the fixup isn't to come at the present position,
+ doing the stack adjust here isn't useful. Doing it with our
+ settings at that location isn't useful either. Let's hope
+ someone does it! */
+ if (last_insn == 0)
+ do_pending_stack_adjust ();
+ fixup->target = tree_label;
+ fixup->target_rtl = rtl_label;
+
+ /* Create a BLOCK node and a corresponding matched set of
+ NOTE_INSN_BEGIN_BLOCK and NOTE_INSN_END_BLOCK notes at
+ this point. The notes will encapsulate any and all fixup
+ code which we might later insert at this point in the insn
+ stream. Also, the BLOCK node will be the parent (i.e. the
+ `SUPERBLOCK') of any other BLOCK nodes which we might create
+ later on when we are expanding the fixup code. */
+
+ {
+ register rtx original_before_jump
+ = last_insn ? last_insn : get_last_insn ();
+
+ start_sequence ();
+ pushlevel (0);
+ fixup->before_jump = emit_note (NULL_PTR, NOTE_INSN_BLOCK_BEG);
+ last_block_end_note = emit_note (NULL_PTR, NOTE_INSN_BLOCK_END);
+ fixup->context = poplevel (1, 0, 0); /* Create the BLOCK node now! */
+ end_sequence ();
+ emit_insns_after (fixup->before_jump, original_before_jump);
+ }
+
+ fixup->block_start_count = block_start_count;
+ fixup->stack_level = 0;
+ fixup->cleanup_list_list
+ = (((block->data.block.outer_cleanups
+#if 0
+ && block->data.block.outer_cleanups != empty_cleanup_list
+#endif
+ )
+ || block->data.block.cleanups)
+ ? tree_cons (NULL_TREE, block->data.block.cleanups,
+ block->data.block.outer_cleanups)
+ : 0);
+ fixup->next = goto_fixup_chain;
+ goto_fixup_chain = fixup;
+ }
+
+ return block != 0;
+}
+
+
+/* Generate bytecode jump with OPCODE to a fixup routine that links to LABEL.
+ Make the fixup restore the stack level to STACK_LEVEL. */
+
+static void
+bc_expand_fixup (opcode, label, stack_level)
+ enum bytecode_opcode opcode;
+ struct bc_label *label;
+ int stack_level;
+{
+ struct goto_fixup *fixup
+ = (struct goto_fixup *) oballoc (sizeof (struct goto_fixup));
+
+ fixup->label = bc_get_bytecode_label ();
+ fixup->bc_target = label;
+ fixup->bc_stack_level = stack_level;
+ fixup->bc_handled = FALSE;
+
+ fixup->next = goto_fixup_chain;
+ goto_fixup_chain = fixup;
+
+ /* Insert a jump to the fixup code */
+ bc_emit_bytecode (opcode);
+ bc_emit_bytecode_labelref (fixup->label);
+
+#ifdef DEBUG_PRINT_CODE
+ fputc ('\n', stderr);
+#endif
+}
+
+/* Expand any needed fixups in the outputmost binding level of the
+ function. FIRST_INSN is the first insn in the function. */
+
+void
+expand_fixups (first_insn)
+ rtx first_insn;
+{
+ fixup_gotos (NULL_PTR, NULL_RTX, NULL_TREE, first_insn, 0);
+}
+
+/* When exiting a binding contour, process all pending gotos requiring fixups.
+ THISBLOCK is the structure that describes the block being exited.
+ STACK_LEVEL is the rtx for the stack level to restore exiting this contour.
+ CLEANUP_LIST is a list of expressions to evaluate on exiting this contour.
+ FIRST_INSN is the insn that began this contour.
+
+ Gotos that jump out of this contour must restore the
+ stack level and do the cleanups before actually jumping.
+
+ DONT_JUMP_IN nonzero means report error there is a jump into this
+ contour from before the beginning of the contour.
+ This is also done if STACK_LEVEL is nonzero. */
+
+static void
+fixup_gotos (thisblock, stack_level, cleanup_list, first_insn, dont_jump_in)
+ struct nesting *thisblock;
+ rtx stack_level;
+ tree cleanup_list;
+ rtx first_insn;
+ int dont_jump_in;
+{
+ register struct goto_fixup *f, *prev;
+
+ if (output_bytecode)
+ {
+ /* ??? The second arg is the bc stack level, which is not the same
+ as STACK_LEVEL. I have no idea what should go here, so I'll
+ just pass 0. */
+ bc_fixup_gotos (thisblock, 0, cleanup_list, first_insn, dont_jump_in);
+ return;
+ }
+
+ /* F is the fixup we are considering; PREV is the previous one. */
+ /* We run this loop in two passes so that cleanups of exited blocks
+ are run first, and blocks that are exited are marked so
+ afterwards. */
+
+ for (prev = 0, f = goto_fixup_chain; f; prev = f, f = f->next)
+ {
+ /* Test for a fixup that is inactive because it is already handled. */
+ if (f->before_jump == 0)
+ {
+ /* Delete inactive fixup from the chain, if that is easy to do. */
+ if (prev != 0)
+ prev->next = f->next;
+ }
+ /* Has this fixup's target label been defined?
+ If so, we can finalize it. */
+ else if (PREV_INSN (f->target_rtl) != 0)
+ {
+ register rtx cleanup_insns;
+
+ /* Get the first non-label after the label
+ this goto jumps to. If that's before this scope begins,
+ we don't have a jump into the scope. */
+ rtx after_label = f->target_rtl;
+ while (after_label != 0 && GET_CODE (after_label) == CODE_LABEL)
+ after_label = NEXT_INSN (after_label);
+
+ /* If this fixup jumped into this contour from before the beginning
+ of this contour, report an error. */
+ /* ??? Bug: this does not detect jumping in through intermediate
+ blocks that have stack levels or cleanups.
+ It detects only a problem with the innermost block
+ around the label. */
+ if (f->target != 0
+ && (dont_jump_in || stack_level || cleanup_list)
+ /* If AFTER_LABEL is 0, it means the jump goes to the end
+ of the rtl, which means it jumps into this scope. */
+ && (after_label == 0
+ || INSN_UID (first_insn) < INSN_UID (after_label))
+ && INSN_UID (first_insn) > INSN_UID (f->before_jump)
+ && ! DECL_REGISTER (f->target))
+ {
+ error_with_decl (f->target,
+ "label `%s' used before containing binding contour");
+ /* Prevent multiple errors for one label. */
+ DECL_REGISTER (f->target) = 1;
+ }
+
+ /* We will expand the cleanups into a sequence of their own and
+ then later on we will attach this new sequence to the insn
+ stream just ahead of the actual jump insn. */
+
+ start_sequence ();
+
+ /* Temporarily restore the lexical context where we will
+ logically be inserting the fixup code. We do this for the
+ sake of getting the debugging information right. */
+
+ pushlevel (0);
+ set_block (f->context);
+
+ /* Expand the cleanups for blocks this jump exits. */
+ if (f->cleanup_list_list)
+ {
+ tree lists;
+ for (lists = f->cleanup_list_list; lists; lists = TREE_CHAIN (lists))
+ /* Marked elements correspond to blocks that have been closed.
+ Do their cleanups. */
+ if (TREE_ADDRESSABLE (lists)
+ && TREE_VALUE (lists) != 0)
+ {
+ expand_cleanups (TREE_VALUE (lists), 0);
+ /* Pop any pushes done in the cleanups,
+ in case function is about to return. */
+ do_pending_stack_adjust ();
+ }
+ }
+
+ /* Restore stack level for the biggest contour that this
+ jump jumps out of. */
+ if (f->stack_level)
+ emit_stack_restore (SAVE_BLOCK, f->stack_level, f->before_jump);
+
+ /* Finish up the sequence containing the insns which implement the
+ necessary cleanups, and then attach that whole sequence to the
+ insn stream just ahead of the actual jump insn. Attaching it
+ at that point insures that any cleanups which are in fact
+ implicit C++ object destructions (which must be executed upon
+ leaving the block) appear (to the debugger) to be taking place
+ in an area of the generated code where the object(s) being
+ destructed are still "in scope". */
+
+ cleanup_insns = get_insns ();
+ poplevel (1, 0, 0);
+
+ end_sequence ();
+ emit_insns_after (cleanup_insns, f->before_jump);
+
+
+ f->before_jump = 0;
+ }
+ }
+
+ /* Mark the cleanups of exited blocks so that they are executed
+ by the code above. */
+ for (prev = 0, f = goto_fixup_chain; f; prev = f, f = f->next)
+ if (f->before_jump != 0
+ && PREV_INSN (f->target_rtl) == 0
+ /* Label has still not appeared. If we are exiting a block with
+ a stack level to restore, that started before the fixup,
+ mark this stack level as needing restoration
+ when the fixup is later finalized.
+ Also mark the cleanup_list_list element for F
+ that corresponds to this block, so that ultimately
+ this block's cleanups will be executed by the code above. */
+ && thisblock != 0
+ /* Note: if THISBLOCK == 0 and we have a label that hasn't appeared,
+ it means the label is undefined. That's erroneous, but possible. */
+ && (thisblock->data.block.block_start_count
+ <= f->block_start_count))
+ {
+ tree lists = f->cleanup_list_list;
+ for (; lists; lists = TREE_CHAIN (lists))
+ /* If the following elt. corresponds to our containing block
+ then the elt. must be for this block. */
+ if (TREE_CHAIN (lists) == thisblock->data.block.outer_cleanups)
+ TREE_ADDRESSABLE (lists) = 1;
+
+ if (stack_level)
+ f->stack_level = stack_level;
+ }
+}
+
+
+/* When exiting a binding contour, process all pending gotos requiring fixups.
+ Note: STACK_DEPTH is not altered.
+
+ The arguments are currently not used in the bytecode compiler, but we may
+ need them one day for languages other than C.
+
+ THISBLOCK is the structure that describes the block being exited.
+ STACK_LEVEL is the rtx for the stack level to restore exiting this contour.
+ CLEANUP_LIST is a list of expressions to evaluate on exiting this contour.
+ FIRST_INSN is the insn that began this contour.
+
+ Gotos that jump out of this contour must restore the
+ stack level and do the cleanups before actually jumping.
+
+ DONT_JUMP_IN nonzero means report error there is a jump into this
+ contour from before the beginning of the contour.
+ This is also done if STACK_LEVEL is nonzero. */
+
+static void
+bc_fixup_gotos (thisblock, stack_level, cleanup_list, first_insn, dont_jump_in)
+ struct nesting *thisblock;
+ int stack_level;
+ tree cleanup_list;
+ rtx first_insn;
+ int dont_jump_in;
+{
+ register struct goto_fixup *f, *prev;
+ int saved_stack_depth;
+
+ /* F is the fixup we are considering; PREV is the previous one. */
+
+ for (prev = 0, f = goto_fixup_chain; f; prev = f, f = f->next)
+ {
+ /* Test for a fixup that is inactive because it is already handled. */
+ if (f->before_jump == 0)
+ {
+ /* Delete inactive fixup from the chain, if that is easy to do. */
+ if (prev)
+ prev->next = f->next;
+ }
+
+ /* Emit code to restore the stack and continue */
+ bc_emit_bytecode_labeldef (f->label);
+
+ /* Save stack_depth across call, since bc_adjust_stack () will alter
+ the perceived stack depth via the instructions generated. */
+
+ if (f->bc_stack_level >= 0)
+ {
+ saved_stack_depth = stack_depth;
+ bc_adjust_stack (stack_depth - f->bc_stack_level);
+ stack_depth = saved_stack_depth;
+ }
+
+ bc_emit_bytecode (jump);
+ bc_emit_bytecode_labelref (f->bc_target);
+
+#ifdef DEBUG_PRINT_CODE
+ fputc ('\n', stderr);
+#endif
+ }
+
+ goto_fixup_chain = NULL;
+}
+
+/* Generate RTL for an asm statement (explicit assembler code).
+ BODY is a STRING_CST node containing the assembler code text,
+ or an ADDR_EXPR containing a STRING_CST. */
+
+void
+expand_asm (body)
+ tree body;
+{
+ if (output_bytecode)
+ {
+ error ("`asm' is illegal when generating bytecode");
+ return;
+ }
+
+ if (TREE_CODE (body) == ADDR_EXPR)
+ body = TREE_OPERAND (body, 0);
+
+ emit_insn (gen_rtx (ASM_INPUT, VOIDmode,
+ TREE_STRING_POINTER (body)));
+ last_expr_type = 0;
+}
+
+/* Generate RTL for an asm statement with arguments.
+ STRING is the instruction template.
+ OUTPUTS is a list of output arguments (lvalues); INPUTS a list of inputs.
+ Each output or input has an expression in the TREE_VALUE and
+ a constraint-string in the TREE_PURPOSE.
+ CLOBBERS is a list of STRING_CST nodes each naming a hard register
+ that is clobbered by this insn.
+
+ Not all kinds of lvalue that may appear in OUTPUTS can be stored directly.
+ Some elements of OUTPUTS may be replaced with trees representing temporary
+ values. The caller should copy those temporary values to the originally
+ specified lvalues.
+
+ VOL nonzero means the insn is volatile; don't optimize it. */
+
+void
+expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
+ tree string, outputs, inputs, clobbers;
+ int vol;
+ char *filename;
+ int line;
+{
+ rtvec argvec, constraints;
+ rtx body;
+ int ninputs = list_length (inputs);
+ int noutputs = list_length (outputs);
+ int nclobbers;
+ tree tail;
+ register int i;
+ /* Vector of RTX's of evaluated output operands. */
+ rtx *output_rtx = (rtx *) alloca (noutputs * sizeof (rtx));
+ /* The insn we have emitted. */
+ rtx insn;
+
+ if (output_bytecode)
+ {
+ error ("`asm' is illegal when generating bytecode");
+ return;
+ }
+
+ /* Count the number of meaningful clobbered registers, ignoring what
+ we would ignore later. */
+ nclobbers = 0;
+ for (tail = clobbers; tail; tail = TREE_CHAIN (tail))
+ {
+ char *regname = TREE_STRING_POINTER (TREE_VALUE (tail));
+ i = decode_reg_name (regname);
+ if (i >= 0 || i == -4)
+ ++nclobbers;
+ }
+
+ last_expr_type = 0;
+
+ for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
+ {
+ tree val = TREE_VALUE (tail);
+ tree val1;
+ int j;
+ int found_equal;
+
+ /* If there's an erroneous arg, emit no insn. */
+ if (TREE_TYPE (val) == error_mark_node)
+ return;
+
+ /* Make sure constraint has `=' and does not have `+'. */
+
+ found_equal = 0;
+ for (j = 0; j < TREE_STRING_LENGTH (TREE_PURPOSE (tail)); j++)
+ {
+ if (TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '+')
+ {
+ error ("output operand constraint contains `+'");
+ return;
+ }
+ if (TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '=')
+ found_equal = 1;
+ }
+ if (! found_equal)
+ {
+ error ("output operand constraint lacks `='");
+ return;
+ }
+
+ /* If an output operand is not a variable or indirect ref,
+ or a part of one,
+ create a SAVE_EXPR which is a pseudo-reg
+ to act as an intermediate temporary.
+ Make the asm insn write into that, then copy it to
+ the real output operand. */
+
+ while (TREE_CODE (val) == COMPONENT_REF
+ || TREE_CODE (val) == ARRAY_REF)
+ val = TREE_OPERAND (val, 0);
+
+ if (TREE_CODE (val) != VAR_DECL
+ && TREE_CODE (val) != PARM_DECL
+ && TREE_CODE (val) != INDIRECT_REF)
+ {
+ TREE_VALUE (tail) = save_expr (TREE_VALUE (tail));
+ /* If it's a constant, print error now so don't crash later. */
+ if (TREE_CODE (TREE_VALUE (tail)) != SAVE_EXPR)
+ {
+ error ("invalid output in `asm'");
+ return;
+ }
+ }
+
+ output_rtx[i] = expand_expr (TREE_VALUE (tail), NULL_RTX, VOIDmode, 0);
+ }
+
+ if (ninputs + noutputs > MAX_RECOG_OPERANDS)
+ {
+ error ("more than %d operands in `asm'", MAX_RECOG_OPERANDS);
+ return;
+ }
+
+ /* Make vectors for the expression-rtx and constraint strings. */
+
+ argvec = rtvec_alloc (ninputs);
+ constraints = rtvec_alloc (ninputs);
+
+ body = gen_rtx (ASM_OPERANDS, VOIDmode,
+ TREE_STRING_POINTER (string), "", 0, argvec, constraints,
+ filename, line);
+ MEM_VOLATILE_P (body) = vol;
+
+ /* Eval the inputs and put them into ARGVEC.
+ Put their constraints into ASM_INPUTs and store in CONSTRAINTS. */
+
+ i = 0;
+ for (tail = inputs; tail; tail = TREE_CHAIN (tail))
+ {
+ int j;
+
+ /* If there's an erroneous arg, emit no insn,
+ because the ASM_INPUT would get VOIDmode
+ and that could cause a crash in reload. */
+ if (TREE_TYPE (TREE_VALUE (tail)) == error_mark_node)
+ return;
+ if (TREE_PURPOSE (tail) == NULL_TREE)
+ {
+ error ("hard register `%s' listed as input operand to `asm'",
+ TREE_STRING_POINTER (TREE_VALUE (tail)) );
+ return;
+ }
+
+ /* Make sure constraint has neither `=' nor `+'. */
+
+ for (j = 0; j < TREE_STRING_LENGTH (TREE_PURPOSE (tail)); j++)
+ if (TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '='
+ || TREE_STRING_POINTER (TREE_PURPOSE (tail))[j] == '+')
+ {
+ error ("input operand constraint contains `%c'",
+ TREE_STRING_POINTER (TREE_PURPOSE (tail))[j]);
+ return;
+ }
+
+ XVECEXP (body, 3, i) /* argvec */
+ = expand_expr (TREE_VALUE (tail), NULL_RTX, VOIDmode, 0);
+ XVECEXP (body, 4, i) /* constraints */
+ = gen_rtx (ASM_INPUT, TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))),
+ TREE_STRING_POINTER (TREE_PURPOSE (tail)));
+ i++;
+ }
+
+ /* Protect all the operands from the queue,
+ now that they have all been evaluated. */
+
+ for (i = 0; i < ninputs; i++)
+ XVECEXP (body, 3, i) = protect_from_queue (XVECEXP (body, 3, i), 0);
+
+ for (i = 0; i < noutputs; i++)
+ output_rtx[i] = protect_from_queue (output_rtx[i], 1);
+
+ /* Now, for each output, construct an rtx
+ (set OUTPUT (asm_operands INSN OUTPUTNUMBER OUTPUTCONSTRAINT
+ ARGVEC CONSTRAINTS))
+ If there is more than one, put them inside a PARALLEL. */
+
+ if (noutputs == 1 && nclobbers == 0)
+ {
+ XSTR (body, 1) = TREE_STRING_POINTER (TREE_PURPOSE (outputs));
+ insn = emit_insn (gen_rtx (SET, VOIDmode, output_rtx[0], body));
+ }
+ else if (noutputs == 0 && nclobbers == 0)
+ {
+ /* No output operands: put in a raw ASM_OPERANDS rtx. */
+ insn = emit_insn (body);
+ }
+ else
+ {
+ rtx obody = body;
+ int num = noutputs;
+ if (num == 0) num = 1;
+ body = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (num + nclobbers));
+
+ /* For each output operand, store a SET. */
+
+ for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
+ {
+ XVECEXP (body, 0, i)
+ = gen_rtx (SET, VOIDmode,
+ output_rtx[i],
+ gen_rtx (ASM_OPERANDS, VOIDmode,
+ TREE_STRING_POINTER (string),
+ TREE_STRING_POINTER (TREE_PURPOSE (tail)),
+ i, argvec, constraints,
+ filename, line));
+ MEM_VOLATILE_P (SET_SRC (XVECEXP (body, 0, i))) = vol;
+ }
+
+ /* If there are no outputs (but there are some clobbers)
+ store the bare ASM_OPERANDS into the PARALLEL. */
+
+ if (i == 0)
+ XVECEXP (body, 0, i++) = obody;
+
+ /* Store (clobber REG) for each clobbered register specified. */
+
+ for (tail = clobbers; tail; tail = TREE_CHAIN (tail))
+ {
+ char *regname = TREE_STRING_POINTER (TREE_VALUE (tail));
+ int j = decode_reg_name (regname);
+
+ if (j < 0)
+ {
+ if (j == -3) /* `cc', which is not a register */
+ continue;
+
+ if (j == -4) /* `memory', don't cache memory across asm */
+ {
+ XVECEXP (body, 0, i++)
+ = gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (MEM, BLKmode,
+ gen_rtx (SCRATCH, VOIDmode, 0)));
+ continue;
+ }
+
+ error ("unknown register name `%s' in `asm'", regname);
+ return;
+ }
+
+ /* Use QImode since that's guaranteed to clobber just one reg. */
+ XVECEXP (body, 0, i++)
+ = gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, QImode, j));
+ }
+
+ insn = emit_insn (body);
+ }
+
+ free_temp_slots ();
+}
+
+/* Generate RTL to evaluate the expression EXP
+ and remember it in case this is the VALUE in a ({... VALUE; }) constr. */
+
+void
+expand_expr_stmt (exp)
+ tree exp;
+{
+ if (output_bytecode)
+ {
+ int org_stack_depth = stack_depth;
+
+ bc_expand_expr (exp);
+
+ /* Restore stack depth */
+ if (stack_depth < org_stack_depth)
+ abort ();
+
+ bc_emit_instruction (drop);
+
+ last_expr_type = TREE_TYPE (exp);
+ return;
+ }
+
+ /* If -W, warn about statements with no side effects,
+ except for an explicit cast to void (e.g. for assert()), and
+ except inside a ({...}) where they may be useful. */
+ if (expr_stmts_for_value == 0 && exp != error_mark_node)
+ {
+ if (! TREE_SIDE_EFFECTS (exp) && (extra_warnings || warn_unused)
+ && !(TREE_CODE (exp) == CONVERT_EXPR
+ && TREE_TYPE (exp) == void_type_node))
+ warning_with_file_and_line (emit_filename, emit_lineno,
+ "statement with no effect");
+ else if (warn_unused)
+ warn_if_unused_value (exp);
+ }
+ last_expr_type = TREE_TYPE (exp);
+ if (! flag_syntax_only)
+ last_expr_value = expand_expr (exp,
+ (expr_stmts_for_value
+ ? NULL_RTX : const0_rtx),
+ VOIDmode, 0);
+
+ /* If all we do is reference a volatile value in memory,
+ copy it to a register to be sure it is actually touched. */
+ if (last_expr_value != 0 && GET_CODE (last_expr_value) == MEM
+ && TREE_THIS_VOLATILE (exp))
+ {
+ if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode)
+ ;
+ else if (TYPE_MODE (TREE_TYPE (exp)) != BLKmode)
+ copy_to_reg (last_expr_value);
+ else
+ {
+ rtx lab = gen_label_rtx ();
+
+ /* Compare the value with itself to reference it. */
+ emit_cmp_insn (last_expr_value, last_expr_value, EQ,
+ expand_expr (TYPE_SIZE (last_expr_type),
+ NULL_RTX, VOIDmode, 0),
+ BLKmode, 0,
+ TYPE_ALIGN (last_expr_type) / BITS_PER_UNIT);
+ emit_jump_insn ((*bcc_gen_fctn[(int) EQ]) (lab));
+ emit_label (lab);
+ }
+ }
+
+ /* If this expression is part of a ({...}) and is in memory, we may have
+ to preserve temporaries. */
+ preserve_temp_slots (last_expr_value);
+
+ /* Free any temporaries used to evaluate this expression. Any temporary
+ used as a result of this expression will already have been preserved
+ above. */
+ free_temp_slots ();
+
+ emit_queue ();
+}
+
+/* Warn if EXP contains any computations whose results are not used.
+ Return 1 if a warning is printed; 0 otherwise. */
+
+static int
+warn_if_unused_value (exp)
+ tree exp;
+{
+ if (TREE_USED (exp))
+ return 0;
+
+ switch (TREE_CODE (exp))
+ {
+ case PREINCREMENT_EXPR:
+ case POSTINCREMENT_EXPR:
+ case PREDECREMENT_EXPR:
+ case POSTDECREMENT_EXPR:
+ case MODIFY_EXPR:
+ case INIT_EXPR:
+ case TARGET_EXPR:
+ case CALL_EXPR:
+ case METHOD_CALL_EXPR:
+ case RTL_EXPR:
+ case WITH_CLEANUP_EXPR:
+ case EXIT_EXPR:
+ /* We don't warn about COND_EXPR because it may be a useful
+ construct if either arm contains a side effect. */
+ case COND_EXPR:
+ return 0;
+
+ case BIND_EXPR:
+ /* For a binding, warn if no side effect within it. */
+ return warn_if_unused_value (TREE_OPERAND (exp, 1));
+
+ case TRUTH_ORIF_EXPR:
+ case TRUTH_ANDIF_EXPR:
+ /* In && or ||, warn if 2nd operand has no side effect. */
+ return warn_if_unused_value (TREE_OPERAND (exp, 1));
+
+ case COMPOUND_EXPR:
+ if (TREE_NO_UNUSED_WARNING (exp))
+ return 0;
+ if (warn_if_unused_value (TREE_OPERAND (exp, 0)))
+ return 1;
+ /* Let people do `(foo (), 0)' without a warning. */
+ if (TREE_CONSTANT (TREE_OPERAND (exp, 1)))
+ return 0;
+ return warn_if_unused_value (TREE_OPERAND (exp, 1));
+
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ case NON_LVALUE_EXPR:
+ /* Don't warn about values cast to void. */
+ if (TREE_TYPE (exp) == void_type_node)
+ return 0;
+ /* Don't warn about conversions not explicit in the user's program. */
+ if (TREE_NO_UNUSED_WARNING (exp))
+ return 0;
+ /* Assignment to a cast usually results in a cast of a modify.
+ Don't complain about that. There can be an arbitrary number of
+ casts before the modify, so we must loop until we find the first
+ non-cast expression and then test to see if that is a modify. */
+ {
+ tree tem = TREE_OPERAND (exp, 0);
+
+ while (TREE_CODE (tem) == CONVERT_EXPR || TREE_CODE (tem) == NOP_EXPR)
+ tem = TREE_OPERAND (tem, 0);
+
+ if (TREE_CODE (tem) == MODIFY_EXPR)
+ return 0;
+ }
+ /* ... fall through ... */
+
+ default:
+ /* Referencing a volatile value is a side effect, so don't warn. */
+ if ((TREE_CODE_CLASS (TREE_CODE (exp)) == 'd'
+ || TREE_CODE_CLASS (TREE_CODE (exp)) == 'r')
+ && TREE_THIS_VOLATILE (exp))
+ return 0;
+ warning_with_file_and_line (emit_filename, emit_lineno,
+ "value computed is not used");
+ return 1;
+ }
+}
+
+/* Clear out the memory of the last expression evaluated. */
+
+void
+clear_last_expr ()
+{
+ last_expr_type = 0;
+}
+
+/* Begin a statement which will return a value.
+ Return the RTL_EXPR for this statement expr.
+ The caller must save that value and pass it to expand_end_stmt_expr. */
+
+tree
+expand_start_stmt_expr ()
+{
+ int momentary;
+ tree t;
+
+ /* When generating bytecode just note down the stack depth */
+ if (output_bytecode)
+ return (build_int_2 (stack_depth, 0));
+
+ /* Make the RTL_EXPR node temporary, not momentary,
+ so that rtl_expr_chain doesn't become garbage. */
+ momentary = suspend_momentary ();
+ t = make_node (RTL_EXPR);
+ resume_momentary (momentary);
+ start_sequence_for_rtl_expr (t);
+ NO_DEFER_POP;
+ expr_stmts_for_value++;
+ return t;
+}
+
+/* Restore the previous state at the end of a statement that returns a value.
+ Returns a tree node representing the statement's value and the
+ insns to compute the value.
+
+ The nodes of that expression have been freed by now, so we cannot use them.
+ But we don't want to do that anyway; the expression has already been
+ evaluated and now we just want to use the value. So generate a RTL_EXPR
+ with the proper type and RTL value.
+
+ If the last substatement was not an expression,
+ return something with type `void'. */
+
+tree
+expand_end_stmt_expr (t)
+ tree t;
+{
+ if (output_bytecode)
+ {
+ int i;
+ tree t;
+
+
+ /* At this point, all expressions have been evaluated in order.
+ However, all expression values have been popped when evaluated,
+ which means we have to recover the last expression value. This is
+ the last value removed by means of a `drop' instruction. Instead
+ of adding code to inhibit dropping the last expression value, it
+ is here recovered by undoing the `drop'. Since `drop' is
+ equivalent to `adjustackSI [1]', it can be undone with `adjstackSI
+ [-1]'. */
+
+ bc_adjust_stack (-1);
+
+ if (!last_expr_type)
+ last_expr_type = void_type_node;
+
+ t = make_node (RTL_EXPR);
+ TREE_TYPE (t) = last_expr_type;
+ RTL_EXPR_RTL (t) = NULL;
+ RTL_EXPR_SEQUENCE (t) = NULL;
+
+ /* Don't consider deleting this expr or containing exprs at tree level. */
+ TREE_THIS_VOLATILE (t) = 1;
+
+ last_expr_type = 0;
+ return t;
+ }
+
+ OK_DEFER_POP;
+
+ if (last_expr_type == 0)
+ {
+ last_expr_type = void_type_node;
+ last_expr_value = const0_rtx;
+ }
+ else if (last_expr_value == 0)
+ /* There are some cases where this can happen, such as when the
+ statement is void type. */
+ last_expr_value = const0_rtx;
+ else if (GET_CODE (last_expr_value) != REG && ! CONSTANT_P (last_expr_value))
+ /* Remove any possible QUEUED. */
+ last_expr_value = protect_from_queue (last_expr_value, 0);
+
+ emit_queue ();
+
+ TREE_TYPE (t) = last_expr_type;
+ RTL_EXPR_RTL (t) = last_expr_value;
+ RTL_EXPR_SEQUENCE (t) = get_insns ();
+
+ rtl_expr_chain = tree_cons (NULL_TREE, t, rtl_expr_chain);
+
+ end_sequence ();
+
+ /* Don't consider deleting this expr or containing exprs at tree level. */
+ TREE_SIDE_EFFECTS (t) = 1;
+ /* Propagate volatility of the actual RTL expr. */
+ TREE_THIS_VOLATILE (t) = volatile_refs_p (last_expr_value);
+
+ last_expr_type = 0;
+ expr_stmts_for_value--;
+
+ return t;
+}
+
+/* Generate RTL for the start of an if-then. COND is the expression
+ whose truth should be tested.
+
+ If EXITFLAG is nonzero, this conditional is visible to
+ `exit_something'. */
+
+void
+expand_start_cond (cond, exitflag)
+ tree cond;
+ int exitflag;
+{
+ struct nesting *thiscond = ALLOC_NESTING ();
+
+ /* Make an entry on cond_stack for the cond we are entering. */
+
+ thiscond->next = cond_stack;
+ thiscond->all = nesting_stack;
+ thiscond->depth = ++nesting_depth;
+ thiscond->data.cond.next_label = gen_label_rtx ();
+ /* Before we encounter an `else', we don't need a separate exit label
+ unless there are supposed to be exit statements
+ to exit this conditional. */
+ thiscond->exit_label = exitflag ? gen_label_rtx () : 0;
+ thiscond->data.cond.endif_label = thiscond->exit_label;
+ cond_stack = thiscond;
+ nesting_stack = thiscond;
+
+ if (output_bytecode)
+ bc_expand_start_cond (cond, exitflag);
+ else
+ do_jump (cond, thiscond->data.cond.next_label, NULL_RTX);
+}
+
+/* Generate RTL between then-clause and the elseif-clause
+ of an if-then-elseif-.... */
+
+void
+expand_start_elseif (cond)
+ tree cond;
+{
+ if (cond_stack->data.cond.endif_label == 0)
+ cond_stack->data.cond.endif_label = gen_label_rtx ();
+ emit_jump (cond_stack->data.cond.endif_label);
+ emit_label (cond_stack->data.cond.next_label);
+ cond_stack->data.cond.next_label = gen_label_rtx ();
+ do_jump (cond, cond_stack->data.cond.next_label, NULL_RTX);
+}
+
+/* Generate RTL between the then-clause and the else-clause
+ of an if-then-else. */
+
+void
+expand_start_else ()
+{
+ if (cond_stack->data.cond.endif_label == 0)
+ cond_stack->data.cond.endif_label = gen_label_rtx ();
+
+ if (output_bytecode)
+ {
+ bc_expand_start_else ();
+ return;
+ }
+
+ emit_jump (cond_stack->data.cond.endif_label);
+ emit_label (cond_stack->data.cond.next_label);
+ cond_stack->data.cond.next_label = 0; /* No more _else or _elseif calls. */
+}
+
+/* Generate RTL for the end of an if-then.
+ Pop the record for it off of cond_stack. */
+
+void
+expand_end_cond ()
+{
+ struct nesting *thiscond = cond_stack;
+
+ if (output_bytecode)
+ bc_expand_end_cond ();
+ else
+ {
+ do_pending_stack_adjust ();
+ if (thiscond->data.cond.next_label)
+ emit_label (thiscond->data.cond.next_label);
+ if (thiscond->data.cond.endif_label)
+ emit_label (thiscond->data.cond.endif_label);
+ }
+
+ POPSTACK (cond_stack);
+ last_expr_type = 0;
+}
+
+
+/* Generate code for the start of an if-then. COND is the expression
+ whose truth is to be tested; if EXITFLAG is nonzero this conditional
+ is to be visible to exit_something. It is assumed that the caller
+ has pushed the previous context on the cond stack. */
+
+static void
+bc_expand_start_cond (cond, exitflag)
+ tree cond;
+ int exitflag;
+{
+ struct nesting *thiscond = cond_stack;
+
+ thiscond->data.case_stmt.nominal_type = cond;
+ if (! exitflag)
+ thiscond->exit_label = gen_label_rtx ();
+ bc_expand_expr (cond);
+ bc_emit_bytecode (xjumpifnot);
+ bc_emit_bytecode_labelref (BYTECODE_BC_LABEL (thiscond->exit_label));
+
+#ifdef DEBUG_PRINT_CODE
+ fputc ('\n', stderr);
+#endif
+}
+
+/* Generate the label for the end of an if with
+ no else- clause. */
+
+static void
+bc_expand_end_cond ()
+{
+ struct nesting *thiscond = cond_stack;
+
+ bc_emit_bytecode_labeldef (BYTECODE_BC_LABEL (thiscond->exit_label));
+}
+
+/* Generate code for the start of the else- clause of
+ an if-then-else. */
+
+static void
+bc_expand_start_else ()
+{
+ struct nesting *thiscond = cond_stack;
+
+ thiscond->data.cond.endif_label = thiscond->exit_label;
+ thiscond->exit_label = gen_label_rtx ();
+ bc_emit_bytecode (jump);
+ bc_emit_bytecode_labelref (BYTECODE_BC_LABEL (thiscond->exit_label));
+
+#ifdef DEBUG_PRINT_CODE
+ fputc ('\n', stderr);
+#endif
+
+ bc_emit_bytecode_labeldef (BYTECODE_BC_LABEL (thiscond->data.cond.endif_label));
+}
+
+/* Generate RTL for the start of a loop. EXIT_FLAG is nonzero if this
+ loop should be exited by `exit_something'. This is a loop for which
+ `expand_continue' will jump to the top of the loop.
+
+ Make an entry on loop_stack to record the labels associated with
+ this loop. */
+
+struct nesting *
+expand_start_loop (exit_flag)
+ int exit_flag;
+{
+ register struct nesting *thisloop = ALLOC_NESTING ();
+
+ /* Make an entry on loop_stack for the loop we are entering. */
+
+ thisloop->next = loop_stack;
+ thisloop->all = nesting_stack;
+ thisloop->depth = ++nesting_depth;
+ thisloop->data.loop.start_label = gen_label_rtx ();
+ thisloop->data.loop.end_label = gen_label_rtx ();
+ thisloop->data.loop.alt_end_label = 0;
+ thisloop->data.loop.continue_label = thisloop->data.loop.start_label;
+ thisloop->exit_label = exit_flag ? thisloop->data.loop.end_label : 0;
+ loop_stack = thisloop;
+ nesting_stack = thisloop;
+
+ if (output_bytecode)
+ {
+ bc_emit_bytecode_labeldef (BYTECODE_BC_LABEL (thisloop->data.loop.start_label));
+ return thisloop;
+ }
+
+ do_pending_stack_adjust ();
+ emit_queue ();
+ emit_note (NULL_PTR, NOTE_INSN_LOOP_BEG);
+ emit_label (thisloop->data.loop.start_label);
+
+ return thisloop;
+}
+
+/* Like expand_start_loop but for a loop where the continuation point
+ (for expand_continue_loop) will be specified explicitly. */
+
+struct nesting *
+expand_start_loop_continue_elsewhere (exit_flag)
+ int exit_flag;
+{
+ struct nesting *thisloop = expand_start_loop (exit_flag);
+ loop_stack->data.loop.continue_label = gen_label_rtx ();
+ return thisloop;
+}
+
+/* Specify the continuation point for a loop started with
+ expand_start_loop_continue_elsewhere.
+ Use this at the point in the code to which a continue statement
+ should jump. */
+
+void
+expand_loop_continue_here ()
+{
+ if (output_bytecode)
+ {
+ bc_emit_bytecode_labeldef (BYTECODE_BC_LABEL (loop_stack->data.loop.continue_label));
+ return;
+ }
+ do_pending_stack_adjust ();
+ emit_note (NULL_PTR, NOTE_INSN_LOOP_CONT);
+ emit_label (loop_stack->data.loop.continue_label);
+}
+
+/* End a loop. */
+
+static void
+bc_expand_end_loop ()
+{
+ struct nesting *thisloop = loop_stack;
+
+ bc_emit_bytecode (jump);
+ bc_emit_bytecode_labelref (BYTECODE_BC_LABEL (thisloop->data.loop.start_label));
+
+#ifdef DEBUG_PRINT_CODE
+ fputc ('\n', stderr);
+#endif
+
+ bc_emit_bytecode_labeldef (BYTECODE_BC_LABEL (thisloop->exit_label));
+ POPSTACK (loop_stack);
+ last_expr_type = 0;
+}
+
+
+/* Finish a loop. Generate a jump back to the top and the loop-exit label.
+ Pop the block off of loop_stack. */
+
+void
+expand_end_loop ()
+{
+ register rtx insn;
+ register rtx start_label;
+ rtx last_test_insn = 0;
+ int num_insns = 0;
+
+ if (output_bytecode)
+ {
+ bc_expand_end_loop ();
+ return;
+ }
+
+ insn = get_last_insn ();
+ start_label = loop_stack->data.loop.start_label;
+
+ /* Mark the continue-point at the top of the loop if none elsewhere. */
+ if (start_label == loop_stack->data.loop.continue_label)
+ emit_note_before (NOTE_INSN_LOOP_CONT, start_label);
+
+ do_pending_stack_adjust ();
+
+ /* If optimizing, perhaps reorder the loop. If the loop
+ starts with a conditional exit, roll that to the end
+ where it will optimize together with the jump back.
+
+ We look for the last conditional branch to the exit that we encounter
+ before hitting 30 insns or a CALL_INSN. If we see an unconditional
+ branch to the exit first, use it.
+
+ We must also stop at NOTE_INSN_BLOCK_BEG and NOTE_INSN_BLOCK_END notes
+ because moving them is not valid. */
+
+ if (optimize
+ &&
+ ! (GET_CODE (insn) == JUMP_INSN
+ && GET_CODE (PATTERN (insn)) == SET
+ && SET_DEST (PATTERN (insn)) == pc_rtx
+ && GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE))
+ {
+ /* Scan insns from the top of the loop looking for a qualified
+ conditional exit. */
+ for (insn = NEXT_INSN (loop_stack->data.loop.start_label); insn;
+ insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == CODE_LABEL)
+ break;
+
+ if (GET_CODE (insn) == NOTE
+ && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG
+ || NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END))
+ break;
+
+ if (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == INSN)
+ num_insns++;
+
+ if (last_test_insn && num_insns > 30)
+ break;
+
+ if (GET_CODE (insn) == JUMP_INSN && GET_CODE (PATTERN (insn)) == SET
+ && SET_DEST (PATTERN (insn)) == pc_rtx
+ && GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE
+ && ((GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 1)) == LABEL_REF
+ && ((XEXP (XEXP (SET_SRC (PATTERN (insn)), 1), 0)
+ == loop_stack->data.loop.end_label)
+ || (XEXP (XEXP (SET_SRC (PATTERN (insn)), 1), 0)
+ == loop_stack->data.loop.alt_end_label)))
+ || (GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 2)) == LABEL_REF
+ && ((XEXP (XEXP (SET_SRC (PATTERN (insn)), 2), 0)
+ == loop_stack->data.loop.end_label)
+ || (XEXP (XEXP (SET_SRC (PATTERN (insn)), 2), 0)
+ == loop_stack->data.loop.alt_end_label)))))
+ last_test_insn = insn;
+
+ if (last_test_insn == 0 && GET_CODE (insn) == JUMP_INSN
+ && GET_CODE (PATTERN (insn)) == SET
+ && SET_DEST (PATTERN (insn)) == pc_rtx
+ && GET_CODE (SET_SRC (PATTERN (insn))) == LABEL_REF
+ && ((XEXP (SET_SRC (PATTERN (insn)), 0)
+ == loop_stack->data.loop.end_label)
+ || (XEXP (SET_SRC (PATTERN (insn)), 0)
+ == loop_stack->data.loop.alt_end_label)))
+ /* Include BARRIER. */
+ last_test_insn = NEXT_INSN (insn);
+ }
+
+ if (last_test_insn != 0 && last_test_insn != get_last_insn ())
+ {
+ /* We found one. Move everything from there up
+ to the end of the loop, and add a jump into the loop
+ to jump to there. */
+ register rtx newstart_label = gen_label_rtx ();
+ register rtx start_move = start_label;
+
+ /* If the start label is preceded by a NOTE_INSN_LOOP_CONT note,
+ then we want to move this note also. */
+ if (GET_CODE (PREV_INSN (start_move)) == NOTE
+ && (NOTE_LINE_NUMBER (PREV_INSN (start_move))
+ == NOTE_INSN_LOOP_CONT))
+ start_move = PREV_INSN (start_move);
+
+ emit_label_after (newstart_label, PREV_INSN (start_move));
+ reorder_insns (start_move, last_test_insn, get_last_insn ());
+ emit_jump_insn_after (gen_jump (start_label),
+ PREV_INSN (newstart_label));
+ emit_barrier_after (PREV_INSN (newstart_label));
+ start_label = newstart_label;
+ }
+ }
+
+ emit_jump (start_label);
+ emit_note (NULL_PTR, NOTE_INSN_LOOP_END);
+ emit_label (loop_stack->data.loop.end_label);
+
+ POPSTACK (loop_stack);
+
+ last_expr_type = 0;
+}
+
+/* Generate a jump to the current loop's continue-point.
+ This is usually the top of the loop, but may be specified
+ explicitly elsewhere. If not currently inside a loop,
+ return 0 and do nothing; caller will print an error message. */
+
+int
+expand_continue_loop (whichloop)
+ struct nesting *whichloop;
+{
+ last_expr_type = 0;
+ if (whichloop == 0)
+ whichloop = loop_stack;
+ if (whichloop == 0)
+ return 0;
+ expand_goto_internal (NULL_TREE, whichloop->data.loop.continue_label,
+ NULL_RTX);
+ return 1;
+}
+
+/* Generate a jump to exit the current loop. If not currently inside a loop,
+ return 0 and do nothing; caller will print an error message. */
+
+int
+expand_exit_loop (whichloop)
+ struct nesting *whichloop;
+{
+ last_expr_type = 0;
+ if (whichloop == 0)
+ whichloop = loop_stack;
+ if (whichloop == 0)
+ return 0;
+ expand_goto_internal (NULL_TREE, whichloop->data.loop.end_label, NULL_RTX);
+ return 1;
+}
+
+/* Generate a conditional jump to exit the current loop if COND
+ evaluates to zero. If not currently inside a loop,
+ return 0 and do nothing; caller will print an error message. */
+
+int
+expand_exit_loop_if_false (whichloop, cond)
+ struct nesting *whichloop;
+ tree cond;
+{
+ last_expr_type = 0;
+ if (whichloop == 0)
+ whichloop = loop_stack;
+ if (whichloop == 0)
+ return 0;
+ if (output_bytecode)
+ {
+ bc_expand_expr (cond);
+ bc_expand_goto_internal (xjumpifnot,
+ BYTECODE_BC_LABEL (whichloop->exit_label),
+ NULL_TREE);
+ }
+ else
+ {
+ /* In order to handle fixups, we actually create a conditional jump
+ around a unconditional branch to exit the loop. If fixups are
+ necessary, they go before the unconditional branch. */
+
+ rtx label = gen_label_rtx ();
+ rtx last_insn;
+
+ do_jump (cond, NULL_RTX, label);
+ last_insn = get_last_insn ();
+ if (GET_CODE (last_insn) == CODE_LABEL)
+ whichloop->data.loop.alt_end_label = last_insn;
+ expand_goto_internal (NULL_TREE, whichloop->data.loop.end_label,
+ NULL_RTX);
+ emit_label (label);
+ }
+
+ return 1;
+}
+
+/* Return non-zero if we should preserve sub-expressions as separate
+ pseudos. We never do so if we aren't optimizing. We always do so
+ if -fexpensive-optimizations.
+
+ Otherwise, we only do so if we are in the "early" part of a loop. I.e.,
+ the loop may still be a small one. */
+
+int
+preserve_subexpressions_p ()
+{
+ rtx insn;
+
+ if (flag_expensive_optimizations)
+ return 1;
+
+ if (optimize == 0 || loop_stack == 0)
+ return 0;
+
+ insn = get_last_insn_anywhere ();
+
+ return (insn
+ && (INSN_UID (insn) - INSN_UID (loop_stack->data.loop.start_label)
+ < n_non_fixed_regs * 3));
+
+}
+
+/* Generate a jump to exit the current loop, conditional, binding contour
+ or case statement. Not all such constructs are visible to this function,
+ only those started with EXIT_FLAG nonzero. Individual languages use
+ the EXIT_FLAG parameter to control which kinds of constructs you can
+ exit this way.
+
+ If not currently inside anything that can be exited,
+ return 0 and do nothing; caller will print an error message. */
+
+int
+expand_exit_something ()
+{
+ struct nesting *n;
+ last_expr_type = 0;
+ for (n = nesting_stack; n; n = n->all)
+ if (n->exit_label != 0)
+ {
+ expand_goto_internal (NULL_TREE, n->exit_label, NULL_RTX);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Generate RTL to return from the current function, with no value.
+ (That is, we do not do anything about returning any value.) */
+
+void
+expand_null_return ()
+{
+ struct nesting *block = block_stack;
+ rtx last_insn = 0;
+
+ if (output_bytecode)
+ {
+ bc_emit_instruction (ret);
+ return;
+ }
+
+ /* Does any pending block have cleanups? */
+
+ while (block && block->data.block.cleanups == 0)
+ block = block->next;
+
+ /* If yes, use a goto to return, since that runs cleanups. */
+
+ expand_null_return_1 (last_insn, block != 0);
+}
+
+/* Generate RTL to return from the current function, with value VAL. */
+
+void
+expand_value_return (val)
+ rtx val;
+{
+ struct nesting *block = block_stack;
+ rtx last_insn = get_last_insn ();
+ rtx return_reg = DECL_RTL (DECL_RESULT (current_function_decl));
+
+ /* Copy the value to the return location
+ unless it's already there. */
+
+ if (return_reg != val)
+ {
+#ifdef PROMOTE_FUNCTION_RETURN
+ tree type = TREE_TYPE (DECL_RESULT (current_function_decl));
+ int unsignedp = TREE_UNSIGNED (type);
+ enum machine_mode mode
+ = promote_mode (type, DECL_MODE (DECL_RESULT (current_function_decl)),
+ &unsignedp, 1);
+
+ if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
+ convert_move (return_reg, val, unsignedp);
+ else
+#endif
+ emit_move_insn (return_reg, val);
+ }
+ if (GET_CODE (return_reg) == REG
+ && REGNO (return_reg) < FIRST_PSEUDO_REGISTER)
+ emit_insn (gen_rtx (USE, VOIDmode, return_reg));
+
+ /* Does any pending block have cleanups? */
+
+ while (block && block->data.block.cleanups == 0)
+ block = block->next;
+
+ /* If yes, use a goto to return, since that runs cleanups.
+ Use LAST_INSN to put cleanups *before* the move insn emitted above. */
+
+ expand_null_return_1 (last_insn, block != 0);
+}
+
+/* Output a return with no value. If LAST_INSN is nonzero,
+ pretend that the return takes place after LAST_INSN.
+ If USE_GOTO is nonzero then don't use a return instruction;
+ go to the return label instead. This causes any cleanups
+ of pending blocks to be executed normally. */
+
+static void
+expand_null_return_1 (last_insn, use_goto)
+ rtx last_insn;
+ int use_goto;
+{
+ rtx end_label = cleanup_label ? cleanup_label : return_label;
+
+ clear_pending_stack_adjust ();
+ do_pending_stack_adjust ();
+ last_expr_type = 0;
+
+ /* PCC-struct return always uses an epilogue. */
+ if (current_function_returns_pcc_struct || use_goto)
+ {
+ if (end_label == 0)
+ end_label = return_label = gen_label_rtx ();
+ expand_goto_internal (NULL_TREE, end_label, last_insn);
+ return;
+ }
+
+ /* Otherwise output a simple return-insn if one is available,
+ unless it won't do the job. */
+#ifdef HAVE_return
+ if (HAVE_return && use_goto == 0 && cleanup_label == 0)
+ {
+ emit_jump_insn (gen_return ());
+ emit_barrier ();
+ return;
+ }
+#endif
+
+ /* Otherwise jump to the epilogue. */
+ expand_goto_internal (NULL_TREE, end_label, last_insn);
+}
+
+/* Generate RTL to evaluate the expression RETVAL and return it
+ from the current function. */
+
+void
+expand_return (retval)
+ tree retval;
+{
+ /* If there are any cleanups to be performed, then they will
+ be inserted following LAST_INSN. It is desirable
+ that the last_insn, for such purposes, should be the
+ last insn before computing the return value. Otherwise, cleanups
+ which call functions can clobber the return value. */
+ /* ??? rms: I think that is erroneous, because in C++ it would
+ run destructors on variables that might be used in the subsequent
+ computation of the return value. */
+ rtx last_insn = 0;
+ register rtx val = 0;
+ register rtx op0;
+ tree retval_rhs;
+ int cleanups;
+ struct nesting *block;
+
+ /* Bytecode returns are quite simple, just leave the result on the
+ arithmetic stack. */
+ if (output_bytecode)
+ {
+ bc_expand_expr (retval);
+ bc_emit_instruction (ret);
+ return;
+ }
+
+ /* If function wants no value, give it none. */
+ if (TREE_CODE (TREE_TYPE (TREE_TYPE (current_function_decl))) == VOID_TYPE)
+ {
+ expand_expr (retval, NULL_RTX, VOIDmode, 0);
+ emit_queue ();
+ expand_null_return ();
+ return;
+ }
+
+ /* Are any cleanups needed? E.g. C++ destructors to be run? */
+ cleanups = any_pending_cleanups (1);
+
+ if (TREE_CODE (retval) == RESULT_DECL)
+ retval_rhs = retval;
+ else if ((TREE_CODE (retval) == MODIFY_EXPR || TREE_CODE (retval) == INIT_EXPR)
+ && TREE_CODE (TREE_OPERAND (retval, 0)) == RESULT_DECL)
+ retval_rhs = TREE_OPERAND (retval, 1);
+ else if (TREE_TYPE (retval) == void_type_node)
+ /* Recognize tail-recursive call to void function. */
+ retval_rhs = retval;
+ else
+ retval_rhs = NULL_TREE;
+
+ /* Only use `last_insn' if there are cleanups which must be run. */
+ if (cleanups || cleanup_label != 0)
+ last_insn = get_last_insn ();
+
+ /* Distribute return down conditional expr if either of the sides
+ may involve tail recursion (see test below). This enhances the number
+ of tail recursions we see. Don't do this always since it can produce
+ sub-optimal code in some cases and we distribute assignments into
+ conditional expressions when it would help. */
+
+ if (optimize && retval_rhs != 0
+ && frame_offset == 0
+ && TREE_CODE (retval_rhs) == COND_EXPR
+ && (TREE_CODE (TREE_OPERAND (retval_rhs, 1)) == CALL_EXPR
+ || TREE_CODE (TREE_OPERAND (retval_rhs, 2)) == CALL_EXPR))
+ {
+ rtx label = gen_label_rtx ();
+ tree expr;
+
+ do_jump (TREE_OPERAND (retval_rhs, 0), label, NULL_RTX);
+ expr = build (MODIFY_EXPR, TREE_TYPE (current_function_decl),
+ DECL_RESULT (current_function_decl),
+ TREE_OPERAND (retval_rhs, 1));
+ TREE_SIDE_EFFECTS (expr) = 1;
+ expand_return (expr);
+ emit_label (label);
+
+ expr = build (MODIFY_EXPR, TREE_TYPE (current_function_decl),
+ DECL_RESULT (current_function_decl),
+ TREE_OPERAND (retval_rhs, 2));
+ TREE_SIDE_EFFECTS (expr) = 1;
+ expand_return (expr);
+ return;
+ }
+
+ /* For tail-recursive call to current function,
+ just jump back to the beginning.
+ It's unsafe if any auto variable in this function
+ has its address taken; for simplicity,
+ require stack frame to be empty. */
+ if (optimize && retval_rhs != 0
+ && frame_offset == 0
+ && TREE_CODE (retval_rhs) == CALL_EXPR
+ && TREE_CODE (TREE_OPERAND (retval_rhs, 0)) == ADDR_EXPR
+ && TREE_OPERAND (TREE_OPERAND (retval_rhs, 0), 0) == current_function_decl
+ /* Finish checking validity, and if valid emit code
+ to set the argument variables for the new call. */
+ && tail_recursion_args (TREE_OPERAND (retval_rhs, 1),
+ DECL_ARGUMENTS (current_function_decl)))
+ {
+ if (tail_recursion_label == 0)
+ {
+ tail_recursion_label = gen_label_rtx ();
+ emit_label_after (tail_recursion_label,
+ tail_recursion_reentry);
+ }
+ emit_queue ();
+ expand_goto_internal (NULL_TREE, tail_recursion_label, last_insn);
+ emit_barrier ();
+ return;
+ }
+#ifdef HAVE_return
+ /* This optimization is safe if there are local cleanups
+ because expand_null_return takes care of them.
+ ??? I think it should also be safe when there is a cleanup label,
+ because expand_null_return takes care of them, too.
+ Any reason why not? */
+ if (HAVE_return && cleanup_label == 0
+ && ! current_function_returns_pcc_struct
+ && BRANCH_COST <= 1)
+ {
+ /* If this is return x == y; then generate
+ if (x == y) return 1; else return 0;
+ if we can do it with explicit return insns and
+ branches are cheap. */
+ if (retval_rhs)
+ switch (TREE_CODE (retval_rhs))
+ {
+ case EQ_EXPR:
+ case NE_EXPR:
+ case GT_EXPR:
+ case GE_EXPR:
+ case LT_EXPR:
+ case LE_EXPR:
+ case TRUTH_ANDIF_EXPR:
+ case TRUTH_ORIF_EXPR:
+ case TRUTH_AND_EXPR:
+ case TRUTH_OR_EXPR:
+ case TRUTH_NOT_EXPR:
+ case TRUTH_XOR_EXPR:
+ op0 = gen_label_rtx ();
+ jumpifnot (retval_rhs, op0);
+ expand_value_return (const1_rtx);
+ emit_label (op0);
+ expand_value_return (const0_rtx);
+ return;
+ }
+ }
+#endif /* HAVE_return */
+
+ if (cleanups
+ && retval_rhs != 0
+ && TREE_TYPE (retval_rhs) != void_type_node
+ && GET_CODE (DECL_RTL (DECL_RESULT (current_function_decl))) == REG)
+ {
+ /* Calculate the return value into a pseudo reg. */
+ val = expand_expr (retval_rhs, NULL_RTX, VOIDmode, 0);
+ emit_queue ();
+ /* All temporaries have now been used. */
+ free_temp_slots ();
+ /* Return the calculated value, doing cleanups first. */
+ expand_value_return (val);
+ }
+ else
+ {
+ /* No cleanups or no hard reg used;
+ calculate value into hard return reg. */
+ expand_expr (retval, const0_rtx, VOIDmode, 0);
+ emit_queue ();
+ free_temp_slots ();
+ expand_value_return (DECL_RTL (DECL_RESULT (current_function_decl)));
+ }
+}
+
+/* Return 1 if the end of the generated RTX is not a barrier.
+ This means code already compiled can drop through. */
+
+int
+drop_through_at_end_p ()
+{
+ rtx insn = get_last_insn ();
+ while (insn && GET_CODE (insn) == NOTE)
+ insn = PREV_INSN (insn);
+ return insn && GET_CODE (insn) != BARRIER;
+}
+
+/* Emit code to alter this function's formal parms for a tail-recursive call.
+ ACTUALS is a list of actual parameter expressions (chain of TREE_LISTs).
+ FORMALS is the chain of decls of formals.
+ Return 1 if this can be done;
+ otherwise return 0 and do not emit any code. */
+
+static int
+tail_recursion_args (actuals, formals)
+ tree actuals, formals;
+{
+ register tree a = actuals, f = formals;
+ register int i;
+ register rtx *argvec;
+
+ /* Check that number and types of actuals are compatible
+ with the formals. This is not always true in valid C code.
+ Also check that no formal needs to be addressable
+ and that all formals are scalars. */
+
+ /* Also count the args. */
+
+ for (a = actuals, f = formals, i = 0; a && f; a = TREE_CHAIN (a), f = TREE_CHAIN (f), i++)
+ {
+ if (TREE_TYPE (TREE_VALUE (a)) != TREE_TYPE (f))
+ return 0;
+ if (GET_CODE (DECL_RTL (f)) != REG || DECL_MODE (f) == BLKmode)
+ return 0;
+ }
+ if (a != 0 || f != 0)
+ return 0;
+
+ /* Compute all the actuals. */
+
+ argvec = (rtx *) alloca (i * sizeof (rtx));
+
+ for (a = actuals, i = 0; a; a = TREE_CHAIN (a), i++)
+ argvec[i] = expand_expr (TREE_VALUE (a), NULL_RTX, VOIDmode, 0);
+
+ /* Find which actual values refer to current values of previous formals.
+ Copy each of them now, before any formal is changed. */
+
+ for (a = actuals, i = 0; a; a = TREE_CHAIN (a), i++)
+ {
+ int copy = 0;
+ register int j;
+ for (f = formals, j = 0; j < i; f = TREE_CHAIN (f), j++)
+ if (reg_mentioned_p (DECL_RTL (f), argvec[i]))
+ { copy = 1; break; }
+ if (copy)
+ argvec[i] = copy_to_reg (argvec[i]);
+ }
+
+ /* Store the values of the actuals into the formals. */
+
+ for (f = formals, a = actuals, i = 0; f;
+ f = TREE_CHAIN (f), a = TREE_CHAIN (a), i++)
+ {
+ if (GET_MODE (DECL_RTL (f)) == GET_MODE (argvec[i]))
+ emit_move_insn (DECL_RTL (f), argvec[i]);
+ else
+ convert_move (DECL_RTL (f), argvec[i],
+ TREE_UNSIGNED (TREE_TYPE (TREE_VALUE (a))));
+ }
+
+ free_temp_slots ();
+ return 1;
+}
+
+/* Generate the RTL code for entering a binding contour.
+ The variables are declared one by one, by calls to `expand_decl'.
+
+ EXIT_FLAG is nonzero if this construct should be visible to
+ `exit_something'. */
+
+void
+expand_start_bindings (exit_flag)
+ int exit_flag;
+{
+ struct nesting *thisblock = ALLOC_NESTING ();
+ rtx note = output_bytecode ? 0 : emit_note (NULL_PTR, NOTE_INSN_BLOCK_BEG);
+
+ /* Make an entry on block_stack for the block we are entering. */
+
+ thisblock->next = block_stack;
+ thisblock->all = nesting_stack;
+ thisblock->depth = ++nesting_depth;
+ thisblock->data.block.stack_level = 0;
+ thisblock->data.block.cleanups = 0;
+ thisblock->data.block.function_call_count = 0;
+#if 0
+ if (block_stack)
+ {
+ if (block_stack->data.block.cleanups == NULL_TREE
+ && (block_stack->data.block.outer_cleanups == NULL_TREE
+ || block_stack->data.block.outer_cleanups == empty_cleanup_list))
+ thisblock->data.block.outer_cleanups = empty_cleanup_list;
+ else
+ thisblock->data.block.outer_cleanups
+ = tree_cons (NULL_TREE, block_stack->data.block.cleanups,
+ block_stack->data.block.outer_cleanups);
+ }
+ else
+ thisblock->data.block.outer_cleanups = 0;
+#endif
+#if 1
+ if (block_stack
+ && !(block_stack->data.block.cleanups == NULL_TREE
+ && block_stack->data.block.outer_cleanups == NULL_TREE))
+ thisblock->data.block.outer_cleanups
+ = tree_cons (NULL_TREE, block_stack->data.block.cleanups,
+ block_stack->data.block.outer_cleanups);
+ else
+ thisblock->data.block.outer_cleanups = 0;
+#endif
+ thisblock->data.block.label_chain = 0;
+ thisblock->data.block.innermost_stack_block = stack_block_stack;
+ thisblock->data.block.first_insn = note;
+ thisblock->data.block.block_start_count = ++block_start_count;
+ thisblock->exit_label = exit_flag ? gen_label_rtx () : 0;
+ block_stack = thisblock;
+ nesting_stack = thisblock;
+
+ if (!output_bytecode)
+ {
+ /* Make a new level for allocating stack slots. */
+ push_temp_slots ();
+ }
+}
+
+/* Given a pointer to a BLOCK node, save a pointer to the most recently
+ generated NOTE_INSN_BLOCK_END in the BLOCK_END_NOTE field of the given
+ BLOCK node. */
+
+void
+remember_end_note (block)
+ register tree block;
+{
+ BLOCK_END_NOTE (block) = last_block_end_note;
+ last_block_end_note = NULL_RTX;
+}
+
+/* Generate RTL code to terminate a binding contour.
+ VARS is the chain of VAR_DECL nodes
+ for the variables bound in this contour.
+ MARK_ENDS is nonzero if we should put a note at the beginning
+ and end of this binding contour.
+
+ DONT_JUMP_IN is nonzero if it is not valid to jump into this contour.
+ (That is true automatically if the contour has a saved stack level.) */
+
+void
+expand_end_bindings (vars, mark_ends, dont_jump_in)
+ tree vars;
+ int mark_ends;
+ int dont_jump_in;
+{
+ register struct nesting *thisblock = block_stack;
+ register tree decl;
+
+ if (output_bytecode)
+ {
+ bc_expand_end_bindings (vars, mark_ends, dont_jump_in);
+ return;
+ }
+
+ if (warn_unused)
+ for (decl = vars; decl; decl = TREE_CHAIN (decl))
+ if (! TREE_USED (decl) && TREE_CODE (decl) == VAR_DECL
+ && ! DECL_IN_SYSTEM_HEADER (decl))
+ warning_with_decl (decl, "unused variable `%s'");
+
+ if (thisblock->exit_label)
+ {
+ do_pending_stack_adjust ();
+ emit_label (thisblock->exit_label);
+ }
+
+ /* If necessary, make a handler for nonlocal gotos taking
+ place in the function calls in this block. */
+ if (function_call_count != thisblock->data.block.function_call_count
+ && nonlocal_labels
+ /* Make handler for outermost block
+ if there were any nonlocal gotos to this function. */
+ && (thisblock->next == 0 ? current_function_has_nonlocal_label
+ /* Make handler for inner block if it has something
+ special to do when you jump out of it. */
+ : (thisblock->data.block.cleanups != 0
+ || thisblock->data.block.stack_level != 0)))
+ {
+ tree link;
+ rtx afterward = gen_label_rtx ();
+ rtx handler_label = gen_label_rtx ();
+ rtx save_receiver = gen_reg_rtx (Pmode);
+ rtx insns;
+
+ /* Don't let jump_optimize delete the handler. */
+ LABEL_PRESERVE_P (handler_label) = 1;
+
+ /* Record the handler address in the stack slot for that purpose,
+ during this block, saving and restoring the outer value. */
+ if (thisblock->next != 0)
+ {
+ emit_move_insn (nonlocal_goto_handler_slot, save_receiver);
+
+ start_sequence ();
+ emit_move_insn (save_receiver, nonlocal_goto_handler_slot);
+ insns = get_insns ();
+ end_sequence ();
+ emit_insns_before (insns, thisblock->data.block.first_insn);
+ }
+
+ start_sequence ();
+ emit_move_insn (nonlocal_goto_handler_slot,
+ gen_rtx (LABEL_REF, Pmode, handler_label));
+ insns = get_insns ();
+ end_sequence ();
+ emit_insns_before (insns, thisblock->data.block.first_insn);
+
+ /* Jump around the handler; it runs only when specially invoked. */
+ emit_jump (afterward);
+ emit_label (handler_label);
+
+#ifdef HAVE_nonlocal_goto
+ if (! HAVE_nonlocal_goto)
+#endif
+ /* First adjust our frame pointer to its actual value. It was
+ previously set to the start of the virtual area corresponding to
+ the stacked variables when we branched here and now needs to be
+ adjusted to the actual hardware fp value.
+
+ Assignments are to virtual registers are converted by
+ instantiate_virtual_regs into the corresponding assignment
+ to the underlying register (fp in this case) that makes
+ the original assignment true.
+ So the following insn will actually be
+ decrementing fp by STARTING_FRAME_OFFSET. */
+ emit_move_insn (virtual_stack_vars_rtx, hard_frame_pointer_rtx);
+
+#if ARG_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+ if (fixed_regs[ARG_POINTER_REGNUM])
+ {
+#ifdef ELIMINABLE_REGS
+ /* If the argument pointer can be eliminated in favor of the
+ frame pointer, we don't need to restore it. We assume here
+ that if such an elimination is present, it can always be used.
+ This is the case on all known machines; if we don't make this
+ assumption, we do unnecessary saving on many machines. */
+ static struct elims {int from, to;} elim_regs[] = ELIMINABLE_REGS;
+ int i;
+
+ for (i = 0; i < sizeof elim_regs / sizeof elim_regs[0]; i++)
+ if (elim_regs[i].from == ARG_POINTER_REGNUM
+ && elim_regs[i].to == HARD_FRAME_POINTER_REGNUM)
+ break;
+
+ if (i == sizeof elim_regs / sizeof elim_regs [0])
+#endif
+ {
+ /* Now restore our arg pointer from the address at which it
+ was saved in our stack frame.
+ If there hasn't be space allocated for it yet, make
+ some now. */
+ if (arg_pointer_save_area == 0)
+ arg_pointer_save_area
+ = assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0);
+ emit_move_insn (virtual_incoming_args_rtx,
+ /* We need a pseudo here, or else
+ instantiate_virtual_regs_1 complains. */
+ copy_to_reg (arg_pointer_save_area));
+ }
+ }
+#endif
+
+ /* The handler expects the desired label address in the static chain
+ register. It tests the address and does an appropriate jump
+ to whatever label is desired. */
+ for (link = nonlocal_labels; link; link = TREE_CHAIN (link))
+ /* Skip any labels we shouldn't be able to jump to from here. */
+ if (! DECL_TOO_LATE (TREE_VALUE (link)))
+ {
+ rtx not_this = gen_label_rtx ();
+ rtx this = gen_label_rtx ();
+ do_jump_if_equal (static_chain_rtx,
+ gen_rtx (LABEL_REF, Pmode, DECL_RTL (TREE_VALUE (link))),
+ this, 0);
+ emit_jump (not_this);
+ emit_label (this);
+ expand_goto (TREE_VALUE (link));
+ emit_label (not_this);
+ }
+ /* If label is not recognized, abort. */
+ emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "abort"), 0,
+ VOIDmode, 0);
+ emit_label (afterward);
+ }
+
+ /* Don't allow jumping into a block that has cleanups or a stack level. */
+ if (dont_jump_in
+ || thisblock->data.block.stack_level != 0
+ || thisblock->data.block.cleanups != 0)
+ {
+ struct label_chain *chain;
+
+ /* Any labels in this block are no longer valid to go to.
+ Mark them to cause an error message. */
+ for (chain = thisblock->data.block.label_chain; chain; chain = chain->next)
+ {
+ DECL_TOO_LATE (chain->label) = 1;
+ /* If any goto without a fixup came to this label,
+ that must be an error, because gotos without fixups
+ come from outside all saved stack-levels and all cleanups. */
+ if (TREE_ADDRESSABLE (chain->label))
+ error_with_decl (chain->label,
+ "label `%s' used before containing binding contour");
+ }
+ }
+
+ /* Restore stack level in effect before the block
+ (only if variable-size objects allocated). */
+ /* Perform any cleanups associated with the block. */
+
+ if (thisblock->data.block.stack_level != 0
+ || thisblock->data.block.cleanups != 0)
+ {
+ /* Only clean up here if this point can actually be reached. */
+ if (GET_CODE (get_last_insn ()) != BARRIER)
+ {
+ /* Don't let cleanups affect ({...}) constructs. */
+ int old_expr_stmts_for_value = expr_stmts_for_value;
+ rtx old_last_expr_value = last_expr_value;
+ tree old_last_expr_type = last_expr_type;
+ expr_stmts_for_value = 0;
+
+ /* Do the cleanups. */
+ expand_cleanups (thisblock->data.block.cleanups, NULL_TREE);
+ do_pending_stack_adjust ();
+
+ expr_stmts_for_value = old_expr_stmts_for_value;
+ last_expr_value = old_last_expr_value;
+ last_expr_type = old_last_expr_type;
+
+ /* Restore the stack level. */
+
+ if (thisblock->data.block.stack_level != 0)
+ {
+ emit_stack_restore (thisblock->next ? SAVE_BLOCK : SAVE_FUNCTION,
+ thisblock->data.block.stack_level, NULL_RTX);
+ if (nonlocal_goto_handler_slot != 0)
+ emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level,
+ NULL_RTX);
+ }
+ }
+
+ /* Any gotos out of this block must also do these things.
+ Also report any gotos with fixups that came to labels in this
+ level. */
+ fixup_gotos (thisblock,
+ thisblock->data.block.stack_level,
+ thisblock->data.block.cleanups,
+ thisblock->data.block.first_insn,
+ dont_jump_in);
+ }
+
+ /* Mark the beginning and end of the scope if requested.
+ We do this now, after running cleanups on the variables
+ just going out of scope, so they are in scope for their cleanups. */
+
+ if (mark_ends)
+ last_block_end_note = emit_note (NULL_PTR, NOTE_INSN_BLOCK_END);
+ else
+ /* Get rid of the beginning-mark if we don't make an end-mark. */
+ NOTE_LINE_NUMBER (thisblock->data.block.first_insn) = NOTE_INSN_DELETED;
+
+ /* If doing stupid register allocation, make sure lives of all
+ register variables declared here extend thru end of scope. */
+
+ if (obey_regdecls)
+ for (decl = vars; decl; decl = TREE_CHAIN (decl))
+ {
+ rtx rtl = DECL_RTL (decl);
+ if (TREE_CODE (decl) == VAR_DECL && rtl != 0)
+ use_variable (rtl);
+ }
+
+ /* Restore block_stack level for containing block. */
+
+ stack_block_stack = thisblock->data.block.innermost_stack_block;
+ POPSTACK (block_stack);
+
+ /* Pop the stack slot nesting and free any slots at this level. */
+ pop_temp_slots ();
+}
+
+
+/* End a binding contour.
+ VARS is the chain of VAR_DECL nodes for the variables bound
+ in this contour. MARK_ENDS is nonzer if we should put a note
+ at the beginning and end of this binding contour.
+ DONT_JUMP_IN is nonzero if it is not valid to jump into this
+ contour. */
+
+static void
+bc_expand_end_bindings (vars, mark_ends, dont_jump_in)
+ tree vars;
+ int mark_ends;
+ int dont_jump_in;
+{
+ struct nesting *thisbind = nesting_stack;
+ tree decl;
+
+ if (warn_unused)
+ for (decl = vars; decl; decl = TREE_CHAIN (decl))
+ if (! TREE_USED (TREE_VALUE (decl)) && TREE_CODE (TREE_VALUE (decl)) == VAR_DECL)
+ warning_with_decl (decl, "unused variable `%s'");
+
+ if (thisbind->exit_label)
+ bc_emit_bytecode_labeldef (BYTECODE_BC_LABEL (thisbind->exit_label));
+
+ /* Pop block/bindings off stack */
+ POPSTACK (block_stack);
+}
+
+/* Generate RTL for the automatic variable declaration DECL.
+ (Other kinds of declarations are simply ignored if seen here.)
+ CLEANUP is an expression to be executed at exit from this binding contour;
+ for example, in C++, it might call the destructor for this variable.
+
+ If CLEANUP contains any SAVE_EXPRs, then you must preevaluate them
+ either before or after calling `expand_decl' but before compiling
+ any subsequent expressions. This is because CLEANUP may be expanded
+ more than once, on different branches of execution.
+ For the same reason, CLEANUP may not contain a CALL_EXPR
+ except as its topmost node--else `preexpand_calls' would get confused.
+
+ If CLEANUP is nonzero and DECL is zero, we record a cleanup
+ that is not associated with any particular variable.
+
+ There is no special support here for C++ constructors.
+ They should be handled by the proper code in DECL_INITIAL. */
+
+void
+expand_decl (decl)
+ register tree decl;
+{
+ struct nesting *thisblock = block_stack;
+ tree type;
+
+ if (output_bytecode)
+ {
+ bc_expand_decl (decl, 0);
+ return;
+ }
+
+ type = TREE_TYPE (decl);
+
+ /* Only automatic variables need any expansion done.
+ Static and external variables, and external functions,
+ will be handled by `assemble_variable' (called from finish_decl).
+ TYPE_DECL and CONST_DECL require nothing.
+ PARM_DECLs are handled in `assign_parms'. */
+
+ if (TREE_CODE (decl) != VAR_DECL)
+ return;
+ if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
+ return;
+
+ /* Create the RTL representation for the variable. */
+
+ if (type == error_mark_node)
+ DECL_RTL (decl) = gen_rtx (MEM, BLKmode, const0_rtx);
+ else if (DECL_SIZE (decl) == 0)
+ /* Variable with incomplete type. */
+ {
+ if (DECL_INITIAL (decl) == 0)
+ /* Error message was already done; now avoid a crash. */
+ DECL_RTL (decl) = assign_stack_temp (DECL_MODE (decl), 0, 1);
+ else
+ /* An initializer is going to decide the size of this array.
+ Until we know the size, represent its address with a reg. */
+ DECL_RTL (decl) = gen_rtx (MEM, BLKmode, gen_reg_rtx (Pmode));
+ }
+ else if (DECL_MODE (decl) != BLKmode
+ /* If -ffloat-store, don't put explicit float vars
+ into regs. */
+ && !(flag_float_store
+ && TREE_CODE (type) == REAL_TYPE)
+ && ! TREE_THIS_VOLATILE (decl)
+ && ! TREE_ADDRESSABLE (decl)
+ && (DECL_REGISTER (decl) || ! obey_regdecls))
+ {
+ /* Automatic variable that can go in a register. */
+ int unsignedp = TREE_UNSIGNED (type);
+ enum machine_mode reg_mode
+ = promote_mode (type, DECL_MODE (decl), &unsignedp, 0);
+
+ if (TREE_CODE (type) == COMPLEX_TYPE)
+ {
+ rtx realpart, imagpart;
+ enum machine_mode partmode = TYPE_MODE (TREE_TYPE (type));
+
+ /* For a complex type variable, make a CONCAT of two pseudos
+ so that the real and imaginary parts
+ can be allocated separately. */
+ realpart = gen_reg_rtx (partmode);
+ REG_USERVAR_P (realpart) = 1;
+ imagpart = gen_reg_rtx (partmode);
+ REG_USERVAR_P (imagpart) = 1;
+ DECL_RTL (decl) = gen_rtx (CONCAT, reg_mode, realpart, imagpart);
+ }
+ else
+ {
+ DECL_RTL (decl) = gen_reg_rtx (reg_mode);
+ if (TREE_CODE (type) == POINTER_TYPE)
+ mark_reg_pointer (DECL_RTL (decl));
+ REG_USERVAR_P (DECL_RTL (decl)) = 1;
+ }
+ }
+ else if (TREE_CODE (DECL_SIZE (decl)) == INTEGER_CST)
+ {
+ /* Variable of fixed size that goes on the stack. */
+ rtx oldaddr = 0;
+ rtx addr;
+
+ /* If we previously made RTL for this decl, it must be an array
+ whose size was determined by the initializer.
+ The old address was a register; set that register now
+ to the proper address. */
+ if (DECL_RTL (decl) != 0)
+ {
+ if (GET_CODE (DECL_RTL (decl)) != MEM
+ || GET_CODE (XEXP (DECL_RTL (decl), 0)) != REG)
+ abort ();
+ oldaddr = XEXP (DECL_RTL (decl), 0);
+ }
+
+ DECL_RTL (decl)
+ = assign_stack_temp (DECL_MODE (decl),
+ ((TREE_INT_CST_LOW (DECL_SIZE (decl))
+ + BITS_PER_UNIT - 1)
+ / BITS_PER_UNIT),
+ 1);
+
+ /* Set alignment we actually gave this decl. */
+ DECL_ALIGN (decl) = (DECL_MODE (decl) == BLKmode ? BIGGEST_ALIGNMENT
+ : GET_MODE_BITSIZE (DECL_MODE (decl)));
+
+ if (oldaddr)
+ {
+ addr = force_operand (XEXP (DECL_RTL (decl), 0), oldaddr);
+ if (addr != oldaddr)
+ emit_move_insn (oldaddr, addr);
+ }
+
+ /* If this is a memory ref that contains aggregate components,
+ mark it as such for cse and loop optimize. */
+ MEM_IN_STRUCT_P (DECL_RTL (decl)) = AGGREGATE_TYPE_P (TREE_TYPE (decl));
+#if 0
+ /* If this is in memory because of -ffloat-store,
+ set the volatile bit, to prevent optimizations from
+ undoing the effects. */
+ if (flag_float_store && TREE_CODE (type) == REAL_TYPE)
+ MEM_VOLATILE_P (DECL_RTL (decl)) = 1;
+#endif
+ }
+ else
+ /* Dynamic-size object: must push space on the stack. */
+ {
+ rtx address, size;
+
+ /* Record the stack pointer on entry to block, if have
+ not already done so. */
+ if (thisblock->data.block.stack_level == 0)
+ {
+ do_pending_stack_adjust ();
+ emit_stack_save (thisblock->next ? SAVE_BLOCK : SAVE_FUNCTION,
+ &thisblock->data.block.stack_level,
+ thisblock->data.block.first_insn);
+ stack_block_stack = thisblock;
+ }
+
+ /* Compute the variable's size, in bytes. */
+ size = expand_expr (size_binop (CEIL_DIV_EXPR,
+ DECL_SIZE (decl),
+ size_int (BITS_PER_UNIT)),
+ NULL_RTX, VOIDmode, 0);
+ free_temp_slots ();
+
+ /* Allocate space on the stack for the variable. */
+ address = allocate_dynamic_stack_space (size, NULL_RTX,
+ DECL_ALIGN (decl));
+
+ /* Reference the variable indirect through that rtx. */
+ DECL_RTL (decl) = gen_rtx (MEM, DECL_MODE (decl), address);
+
+ /* If this is a memory ref that contains aggregate components,
+ mark it as such for cse and loop optimize. */
+ MEM_IN_STRUCT_P (DECL_RTL (decl)) = AGGREGATE_TYPE_P (TREE_TYPE (decl));
+
+ /* Indicate the alignment we actually gave this variable. */
+#ifdef STACK_BOUNDARY
+ DECL_ALIGN (decl) = STACK_BOUNDARY;
+#else
+ DECL_ALIGN (decl) = BIGGEST_ALIGNMENT;
+#endif
+ }
+
+ if (TREE_THIS_VOLATILE (decl))
+ MEM_VOLATILE_P (DECL_RTL (decl)) = 1;
+#if 0 /* A variable is not necessarily unchanging
+ just because it is const. RTX_UNCHANGING_P
+ means no change in the function,
+ not merely no change in the variable's scope.
+ It is correct to set RTX_UNCHANGING_P if the variable's scope
+ is the whole function. There's no convenient way to test that. */
+ if (TREE_READONLY (decl))
+ RTX_UNCHANGING_P (DECL_RTL (decl)) = 1;
+#endif
+
+ /* If doing stupid register allocation, make sure life of any
+ register variable starts here, at the start of its scope. */
+
+ if (obey_regdecls)
+ use_variable (DECL_RTL (decl));
+}
+
+
+/* Generate code for the automatic variable declaration DECL. For
+ most variables this just means we give it a stack offset. The
+ compiler sometimes emits cleanups without variables and we will
+ have to deal with those too. */
+
+static void
+bc_expand_decl (decl, cleanup)
+ tree decl;
+ tree cleanup;
+{
+ tree type;
+
+ if (!decl)
+ {
+ /* A cleanup with no variable. */
+ if (!cleanup)
+ abort ();
+
+ return;
+ }
+
+ /* Only auto variables need any work. */
+ if (TREE_CODE (decl) != VAR_DECL || TREE_STATIC (decl) || DECL_EXTERNAL (decl))
+ return;
+
+ type = TREE_TYPE (decl);
+
+ if (type == error_mark_node)
+ DECL_RTL (decl) = bc_gen_rtx ((char *) 0, 0, (struct bc_label *) 0);
+
+ else if (DECL_SIZE (decl) == 0)
+
+ /* Variable with incomplete type. The stack offset herein will be
+ fixed later in expand_decl_init (). */
+ DECL_RTL (decl) = bc_gen_rtx ((char *) 0, 0, (struct bc_label *) 0);
+
+ else if (TREE_CONSTANT (DECL_SIZE (decl)))
+ {
+ DECL_RTL (decl) = bc_allocate_local (TREE_INT_CST_LOW (DECL_SIZE (decl)) / BITS_PER_UNIT,
+ DECL_ALIGN (decl));
+ }
+ else
+ DECL_RTL (decl) = bc_allocate_variable_array (DECL_SIZE (decl));
+}
+
+/* Emit code to perform the initialization of a declaration DECL. */
+
+void
+expand_decl_init (decl)
+ tree decl;
+{
+ int was_used = TREE_USED (decl);
+
+ if (output_bytecode)
+ {
+ bc_expand_decl_init (decl);
+ return;
+ }
+
+ /* If this is a CONST_DECL, we don't have to generate any code, but
+ if DECL_INITIAL is a constant, call expand_expr to force TREE_CST_RTL
+ to be set while in the obstack containing the constant. If we don't
+ do this, we can lose if we have functions nested three deep and the middle
+ function makes a CONST_DECL whose DECL_INITIAL is a STRING_CST while
+ the innermost function is the first to expand that STRING_CST. */
+ if (TREE_CODE (decl) == CONST_DECL)
+ {
+ if (DECL_INITIAL (decl) && TREE_CONSTANT (DECL_INITIAL (decl)))
+ expand_expr (DECL_INITIAL (decl), NULL_RTX, VOIDmode,
+ EXPAND_INITIALIZER);
+ return;
+ }
+
+ if (TREE_STATIC (decl))
+ return;
+
+ /* Compute and store the initial value now. */
+
+ if (DECL_INITIAL (decl) == error_mark_node)
+ {
+ enum tree_code code = TREE_CODE (TREE_TYPE (decl));
+ if (code == INTEGER_TYPE || code == REAL_TYPE || code == ENUMERAL_TYPE
+ || code == POINTER_TYPE)
+ expand_assignment (decl, convert (TREE_TYPE (decl), integer_zero_node),
+ 0, 0);
+ emit_queue ();
+ }
+ else if (DECL_INITIAL (decl) && TREE_CODE (DECL_INITIAL (decl)) != TREE_LIST)
+ {
+ emit_line_note (DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl));
+ expand_assignment (decl, DECL_INITIAL (decl), 0, 0);
+ emit_queue ();
+ }
+
+ /* Don't let the initialization count as "using" the variable. */
+ TREE_USED (decl) = was_used;
+
+ /* Free any temporaries we made while initializing the decl. */
+ free_temp_slots ();
+}
+
+/* Expand initialization for variable-sized types. Allocate array
+ using newlocalSI and set local variable, which is a pointer to the
+ storage. */
+
+static void
+bc_expand_variable_local_init (decl)
+ tree decl;
+{
+ /* Evaluate size expression and coerce to SI */
+ bc_expand_expr (DECL_SIZE (decl));
+
+ /* Type sizes are always (?) of TREE_CODE INTEGER_CST, so
+ no coercion is necessary (?) */
+
+/* emit_typecode_conversion (preferred_typecode (TYPE_MODE (DECL_SIZE (decl)),
+ TREE_UNSIGNED (DECL_SIZE (decl))), SIcode); */
+
+ /* Emit code to allocate array */
+ bc_emit_instruction (newlocalSI);
+
+ /* Store array pointer in local variable. This is the only instance
+ where we actually want the address of the pointer to the
+ variable-size block, rather than the pointer itself. We avoid
+ using expand_address() since that would cause the pointer to be
+ pushed rather than its address. Hence the hard-coded reference;
+ notice also that the variable is always local (no global
+ variable-size type variables). */
+
+ bc_load_localaddr (DECL_RTL (decl));
+ bc_emit_instruction (storeP);
+}
+
+
+/* Emit code to initialize a declaration. */
+
+static void
+bc_expand_decl_init (decl)
+ tree decl;
+{
+ int org_stack_depth;
+
+ /* Statical initializers are handled elsewhere */
+
+ if (TREE_STATIC (decl))
+ return;
+
+ /* Memory original stack depth */
+ org_stack_depth = stack_depth;
+
+ /* If the type is variable-size, we first create its space (we ASSUME
+ it CAN'T be static). We do this regardless of whether there's an
+ initializer assignment or not. */
+
+ if (TREE_CODE (DECL_SIZE (decl)) != INTEGER_CST)
+ bc_expand_variable_local_init (decl);
+
+ /* Expand initializer assignment */
+ if (DECL_INITIAL (decl) == error_mark_node)
+ {
+ enum tree_code code = TREE_CODE (TREE_TYPE (decl));
+
+ if (code == INTEGER_TYPE || code == REAL_TYPE || code == ENUMERAL_TYPE
+ || code == POINTER_TYPE)
+
+ expand_assignment (TREE_TYPE (decl), decl, 0, 0);
+ }
+ else if (DECL_INITIAL (decl))
+ expand_assignment (TREE_TYPE (decl), decl, 0, 0);
+
+ /* Restore stack depth */
+ if (org_stack_depth > stack_depth)
+ abort ();
+
+ bc_adjust_stack (stack_depth - org_stack_depth);
+}
+
+
+/* CLEANUP is an expression to be executed at exit from this binding contour;
+ for example, in C++, it might call the destructor for this variable.
+
+ If CLEANUP contains any SAVE_EXPRs, then you must preevaluate them
+ either before or after calling `expand_decl' but before compiling
+ any subsequent expressions. This is because CLEANUP may be expanded
+ more than once, on different branches of execution.
+ For the same reason, CLEANUP may not contain a CALL_EXPR
+ except as its topmost node--else `preexpand_calls' would get confused.
+
+ If CLEANUP is nonzero and DECL is zero, we record a cleanup
+ that is not associated with any particular variable. */
+
+int
+expand_decl_cleanup (decl, cleanup)
+ tree decl, cleanup;
+{
+ struct nesting *thisblock = block_stack;
+
+ /* Error if we are not in any block. */
+ if (thisblock == 0)
+ return 0;
+
+ /* Record the cleanup if there is one. */
+
+ if (cleanup != 0)
+ {
+ thisblock->data.block.cleanups
+ = temp_tree_cons (decl, cleanup, thisblock->data.block.cleanups);
+ /* If this block has a cleanup, it belongs in stack_block_stack. */
+ stack_block_stack = thisblock;
+ (*interim_eh_hook) (NULL_TREE);
+ }
+ return 1;
+}
+
+/* DECL is an anonymous union. CLEANUP is a cleanup for DECL.
+ DECL_ELTS is the list of elements that belong to DECL's type.
+ In each, the TREE_VALUE is a VAR_DECL, and the TREE_PURPOSE a cleanup. */
+
+void
+expand_anon_union_decl (decl, cleanup, decl_elts)
+ tree decl, cleanup, decl_elts;
+{
+ struct nesting *thisblock = block_stack;
+ rtx x;
+
+ expand_decl (decl, cleanup);
+ x = DECL_RTL (decl);
+
+ while (decl_elts)
+ {
+ tree decl_elt = TREE_VALUE (decl_elts);
+ tree cleanup_elt = TREE_PURPOSE (decl_elts);
+ enum machine_mode mode = TYPE_MODE (TREE_TYPE (decl_elt));
+
+ /* (SUBREG (MEM ...)) at RTL generation time is invalid, so we
+ instead create a new MEM rtx with the proper mode. */
+ if (GET_CODE (x) == MEM)
+ {
+ if (mode == GET_MODE (x))
+ DECL_RTL (decl_elt) = x;
+ else
+ {
+ DECL_RTL (decl_elt) = gen_rtx (MEM, mode, copy_rtx (XEXP (x, 0)));
+ MEM_IN_STRUCT_P (DECL_RTL (decl_elt)) = MEM_IN_STRUCT_P (x);
+ RTX_UNCHANGING_P (DECL_RTL (decl_elt)) = RTX_UNCHANGING_P (x);
+ }
+ }
+ else if (GET_CODE (x) == REG)
+ {
+ if (mode == GET_MODE (x))
+ DECL_RTL (decl_elt) = x;
+ else
+ DECL_RTL (decl_elt) = gen_rtx (SUBREG, mode, x, 0);
+ }
+ else
+ abort ();
+
+ /* Record the cleanup if there is one. */
+
+ if (cleanup != 0)
+ thisblock->data.block.cleanups
+ = temp_tree_cons (decl_elt, cleanup_elt,
+ thisblock->data.block.cleanups);
+
+ decl_elts = TREE_CHAIN (decl_elts);
+ }
+}
+
+/* Expand a list of cleanups LIST.
+ Elements may be expressions or may be nested lists.
+
+ If DONT_DO is nonnull, then any list-element
+ whose TREE_PURPOSE matches DONT_DO is omitted.
+ This is sometimes used to avoid a cleanup associated with
+ a value that is being returned out of the scope. */
+
+static void
+expand_cleanups (list, dont_do)
+ tree list;
+ tree dont_do;
+{
+ tree tail;
+ for (tail = list; tail; tail = TREE_CHAIN (tail))
+ if (dont_do == 0 || TREE_PURPOSE (tail) != dont_do)
+ {
+ if (TREE_CODE (TREE_VALUE (tail)) == TREE_LIST)
+ expand_cleanups (TREE_VALUE (tail), dont_do);
+ else
+ {
+ (*interim_eh_hook) (TREE_VALUE (tail));
+
+ /* Cleanups may be run multiple times. For example,
+ when exiting a binding contour, we expand the
+ cleanups associated with that contour. When a goto
+ within that binding contour has a target outside that
+ contour, it will expand all cleanups from its scope to
+ the target. Though the cleanups are expanded multiple
+ times, the control paths are non-overlapping so the
+ cleanups will not be executed twice. */
+ expand_expr (TREE_VALUE (tail), const0_rtx, VOIDmode, 0);
+ free_temp_slots ();
+ }
+ }
+}
+
+/* Move all cleanups from the current block_stack
+ to the containing block_stack, where they are assumed to
+ have been created. If anything can cause a temporary to
+ be created, but not expanded for more than one level of
+ block_stacks, then this code will have to change. */
+
+void
+move_cleanups_up ()
+{
+ struct nesting *block = block_stack;
+ struct nesting *outer = block->next;
+
+ outer->data.block.cleanups
+ = chainon (block->data.block.cleanups,
+ outer->data.block.cleanups);
+ block->data.block.cleanups = 0;
+}
+
+tree
+last_cleanup_this_contour ()
+{
+ if (block_stack == 0)
+ return 0;
+
+ return block_stack->data.block.cleanups;
+}
+
+/* Return 1 if there are any pending cleanups at this point.
+ If THIS_CONTOUR is nonzero, check the current contour as well.
+ Otherwise, look only at the contours that enclose this one. */
+
+int
+any_pending_cleanups (this_contour)
+ int this_contour;
+{
+ struct nesting *block;
+
+ if (block_stack == 0)
+ return 0;
+
+ if (this_contour && block_stack->data.block.cleanups != NULL)
+ return 1;
+ if (block_stack->data.block.cleanups == 0
+ && (block_stack->data.block.outer_cleanups == 0
+#if 0
+ || block_stack->data.block.outer_cleanups == empty_cleanup_list
+#endif
+ ))
+ return 0;
+
+ for (block = block_stack->next; block; block = block->next)
+ if (block->data.block.cleanups != 0)
+ return 1;
+
+ return 0;
+}
+
+/* Enter a case (Pascal) or switch (C) statement.
+ Push a block onto case_stack and nesting_stack
+ to accumulate the case-labels that are seen
+ and to record the labels generated for the statement.
+
+ EXIT_FLAG is nonzero if `exit_something' should exit this case stmt.
+ Otherwise, this construct is transparent for `exit_something'.
+
+ EXPR is the index-expression to be dispatched on.
+ TYPE is its nominal type. We could simply convert EXPR to this type,
+ but instead we take short cuts. */
+
+void
+expand_start_case (exit_flag, expr, type, printname)
+ int exit_flag;
+ tree expr;
+ tree type;
+ char *printname;
+{
+ register struct nesting *thiscase = ALLOC_NESTING ();
+
+ /* Make an entry on case_stack for the case we are entering. */
+
+ thiscase->next = case_stack;
+ thiscase->all = nesting_stack;
+ thiscase->depth = ++nesting_depth;
+ thiscase->exit_label = exit_flag ? gen_label_rtx () : 0;
+ thiscase->data.case_stmt.case_list = 0;
+ thiscase->data.case_stmt.index_expr = expr;
+ thiscase->data.case_stmt.nominal_type = type;
+ thiscase->data.case_stmt.default_label = 0;
+ thiscase->data.case_stmt.num_ranges = 0;
+ thiscase->data.case_stmt.printname = printname;
+ thiscase->data.case_stmt.seenlabel = 0;
+ case_stack = thiscase;
+ nesting_stack = thiscase;
+
+ if (output_bytecode)
+ {
+ bc_expand_start_case (thiscase, expr, type, printname);
+ return;
+ }
+
+ do_pending_stack_adjust ();
+
+ /* Make sure case_stmt.start points to something that won't
+ need any transformation before expand_end_case. */
+ if (GET_CODE (get_last_insn ()) != NOTE)
+ emit_note (NULL_PTR, NOTE_INSN_DELETED);
+
+ thiscase->data.case_stmt.start = get_last_insn ();
+}
+
+
+/* Enter a case statement. It is assumed that the caller has pushed
+ the current context onto the case stack. */
+
+static void
+bc_expand_start_case (thiscase, expr, type, printname)
+ struct nesting *thiscase;
+ tree expr;
+ tree type;
+ char *printname;
+{
+ bc_expand_expr (expr);
+ bc_expand_conversion (TREE_TYPE (expr), type);
+
+ /* For cases, the skip is a place we jump to that's emitted after
+ the size of the jump table is known. */
+
+ thiscase->data.case_stmt.skip_label = gen_label_rtx ();
+ bc_emit_bytecode (jump);
+ bc_emit_bytecode_labelref (BYTECODE_BC_LABEL (thiscase->data.case_stmt.skip_label));
+
+#ifdef DEBUG_PRINT_CODE
+ fputc ('\n', stderr);
+#endif
+}
+
+
+/* Start a "dummy case statement" within which case labels are invalid
+ and are not connected to any larger real case statement.
+ This can be used if you don't want to let a case statement jump
+ into the middle of certain kinds of constructs. */
+
+void
+expand_start_case_dummy ()
+{
+ register struct nesting *thiscase = ALLOC_NESTING ();
+
+ /* Make an entry on case_stack for the dummy. */
+
+ thiscase->next = case_stack;
+ thiscase->all = nesting_stack;
+ thiscase->depth = ++nesting_depth;
+ thiscase->exit_label = 0;
+ thiscase->data.case_stmt.case_list = 0;
+ thiscase->data.case_stmt.start = 0;
+ thiscase->data.case_stmt.nominal_type = 0;
+ thiscase->data.case_stmt.default_label = 0;
+ thiscase->data.case_stmt.num_ranges = 0;
+ case_stack = thiscase;
+ nesting_stack = thiscase;
+}
+
+/* End a dummy case statement. */
+
+void
+expand_end_case_dummy ()
+{
+ POPSTACK (case_stack);
+}
+
+/* Return the data type of the index-expression
+ of the innermost case statement, or null if none. */
+
+tree
+case_index_expr_type ()
+{
+ if (case_stack)
+ return TREE_TYPE (case_stack->data.case_stmt.index_expr);
+ return 0;
+}
+
+/* Accumulate one case or default label inside a case or switch statement.
+ VALUE is the value of the case (a null pointer, for a default label).
+ The function CONVERTER, when applied to arguments T and V,
+ converts the value V to the type T.
+
+ If not currently inside a case or switch statement, return 1 and do
+ nothing. The caller will print a language-specific error message.
+ If VALUE is a duplicate or overlaps, return 2 and do nothing
+ except store the (first) duplicate node in *DUPLICATE.
+ If VALUE is out of range, return 3 and do nothing.
+ If we are jumping into the scope of a cleaup or var-sized array, return 5.
+ Return 0 on success.
+
+ Extended to handle range statements. */
+
+int
+pushcase (value, converter, label, duplicate)
+ register tree value;
+ tree (*converter) PROTO((tree, tree));
+ register tree label;
+ tree *duplicate;
+{
+ register struct case_node **l;
+ register struct case_node *n;
+ tree index_type;
+ tree nominal_type;
+
+ if (output_bytecode)
+ return bc_pushcase (value, label);
+
+ /* Fail if not inside a real case statement. */
+ if (! (case_stack && case_stack->data.case_stmt.start))
+ return 1;
+
+ if (stack_block_stack
+ && stack_block_stack->depth > case_stack->depth)
+ return 5;
+
+ index_type = TREE_TYPE (case_stack->data.case_stmt.index_expr);
+ nominal_type = case_stack->data.case_stmt.nominal_type;
+
+ /* If the index is erroneous, avoid more problems: pretend to succeed. */
+ if (index_type == error_mark_node)
+ return 0;
+
+ /* Convert VALUE to the type in which the comparisons are nominally done. */
+ if (value != 0)
+ value = (*converter) (nominal_type, value);
+
+ /* If this is the first label, warn if any insns have been emitted. */
+ if (case_stack->data.case_stmt.seenlabel == 0)
+ {
+ rtx insn;
+ for (insn = case_stack->data.case_stmt.start;
+ insn;
+ insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == CODE_LABEL)
+ break;
+ if (GET_CODE (insn) != NOTE
+ && (GET_CODE (insn) != INSN || GET_CODE (PATTERN (insn)) != USE))
+ {
+ warning ("unreachable code at beginning of %s",
+ case_stack->data.case_stmt.printname);
+ break;
+ }
+ }
+ }
+ case_stack->data.case_stmt.seenlabel = 1;
+
+ /* Fail if this value is out of range for the actual type of the index
+ (which may be narrower than NOMINAL_TYPE). */
+ if (value != 0 && ! int_fits_type_p (value, index_type))
+ return 3;
+
+ /* Fail if this is a duplicate or overlaps another entry. */
+ if (value == 0)
+ {
+ if (case_stack->data.case_stmt.default_label != 0)
+ {
+ *duplicate = case_stack->data.case_stmt.default_label;
+ return 2;
+ }
+ case_stack->data.case_stmt.default_label = label;
+ }
+ else
+ {
+ /* Find the elt in the chain before which to insert the new value,
+ to keep the chain sorted in increasing order.
+ But report an error if this element is a duplicate. */
+ for (l = &case_stack->data.case_stmt.case_list;
+ /* Keep going past elements distinctly less than VALUE. */
+ *l != 0 && tree_int_cst_lt ((*l)->high, value);
+ l = &(*l)->right)
+ ;
+ if (*l)
+ {
+ /* Element we will insert before must be distinctly greater;
+ overlap means error. */
+ if (! tree_int_cst_lt (value, (*l)->low))
+ {
+ *duplicate = (*l)->code_label;
+ return 2;
+ }
+ }
+
+ /* Add this label to the chain, and succeed.
+ Copy VALUE so it is on temporary rather than momentary
+ obstack and will thus survive till the end of the case statement. */
+ n = (struct case_node *) oballoc (sizeof (struct case_node));
+ n->left = 0;
+ n->right = *l;
+ n->high = n->low = copy_node (value);
+ n->code_label = label;
+ *l = n;
+ }
+
+ expand_label (label);
+ return 0;
+}
+
+/* Like pushcase but this case applies to all values
+ between VALUE1 and VALUE2 (inclusive).
+ The return value is the same as that of pushcase
+ but there is one additional error code:
+ 4 means the specified range was empty. */
+
+int
+pushcase_range (value1, value2, converter, label, duplicate)
+ register tree value1, value2;
+ tree (*converter) PROTO((tree, tree));
+ register tree label;
+ tree *duplicate;
+{
+ register struct case_node **l;
+ register struct case_node *n;
+ tree index_type;
+ tree nominal_type;
+
+ /* Fail if not inside a real case statement. */
+ if (! (case_stack && case_stack->data.case_stmt.start))
+ return 1;
+
+ if (stack_block_stack
+ && stack_block_stack->depth > case_stack->depth)
+ return 5;
+
+ index_type = TREE_TYPE (case_stack->data.case_stmt.index_expr);
+ nominal_type = case_stack->data.case_stmt.nominal_type;
+
+ /* If the index is erroneous, avoid more problems: pretend to succeed. */
+ if (index_type == error_mark_node)
+ return 0;
+
+ /* If this is the first label, warn if any insns have been emitted. */
+ if (case_stack->data.case_stmt.seenlabel == 0)
+ {
+ rtx insn;
+ for (insn = case_stack->data.case_stmt.start;
+ insn;
+ insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == CODE_LABEL)
+ break;
+ if (GET_CODE (insn) != NOTE
+ && (GET_CODE (insn) != INSN || GET_CODE (PATTERN (insn)) != USE))
+ {
+ warning ("unreachable code at beginning of %s",
+ case_stack->data.case_stmt.printname);
+ break;
+ }
+ }
+ }
+ case_stack->data.case_stmt.seenlabel = 1;
+
+ /* Convert VALUEs to type in which the comparisons are nominally done. */
+ if (value1 == 0) /* Negative infinity. */
+ value1 = TYPE_MIN_VALUE(index_type);
+ value1 = (*converter) (nominal_type, value1);
+
+ if (value2 == 0) /* Positive infinity. */
+ value2 = TYPE_MAX_VALUE(index_type);
+ value2 = (*converter) (nominal_type, value2);
+
+ /* Fail if these values are out of range. */
+ if (! int_fits_type_p (value1, index_type))
+ return 3;
+
+ if (! int_fits_type_p (value2, index_type))
+ return 3;
+
+ /* Fail if the range is empty. */
+ if (tree_int_cst_lt (value2, value1))
+ return 4;
+
+ /* If the bounds are equal, turn this into the one-value case. */
+ if (tree_int_cst_equal (value1, value2))
+ return pushcase (value1, converter, label, duplicate);
+
+ /* Find the elt in the chain before which to insert the new value,
+ to keep the chain sorted in increasing order.
+ But report an error if this element is a duplicate. */
+ for (l = &case_stack->data.case_stmt.case_list;
+ /* Keep going past elements distinctly less than this range. */
+ *l != 0 && tree_int_cst_lt ((*l)->high, value1);
+ l = &(*l)->right)
+ ;
+ if (*l)
+ {
+ /* Element we will insert before must be distinctly greater;
+ overlap means error. */
+ if (! tree_int_cst_lt (value2, (*l)->low))
+ {
+ *duplicate = (*l)->code_label;
+ return 2;
+ }
+ }
+
+ /* Add this label to the chain, and succeed.
+ Copy VALUE1, VALUE2 so they are on temporary rather than momentary
+ obstack and will thus survive till the end of the case statement. */
+
+ n = (struct case_node *) oballoc (sizeof (struct case_node));
+ n->left = 0;
+ n->right = *l;
+ n->low = copy_node (value1);
+ n->high = copy_node (value2);
+ n->code_label = label;
+ *l = n;
+
+ expand_label (label);
+
+ case_stack->data.case_stmt.num_ranges++;
+
+ return 0;
+}
+
+
+/* Accumulate one case or default label; VALUE is the value of the
+ case, or nil for a default label. If not currently inside a case,
+ return 1 and do nothing. If VALUE is a duplicate or overlaps, return
+ 2 and do nothing. If VALUE is out of range, return 3 and do nothing.
+ Return 0 on success. This function is a leftover from the earlier
+ bytecode compiler, which was based on gcc 1.37. It should be
+ merged into pushcase. */
+
+static int
+bc_pushcase (value, label)
+ tree value;
+ tree label;
+{
+ struct nesting *thiscase = case_stack;
+ struct case_node *case_label, *new_label;
+
+ if (! thiscase)
+ return 1;
+
+ /* Fail if duplicate, overlap, or out of type range. */
+ if (value)
+ {
+ value = convert (thiscase->data.case_stmt.nominal_type, value);
+ if (! int_fits_type_p (value, thiscase->data.case_stmt.nominal_type))
+ return 3;
+
+ for (case_label = thiscase->data.case_stmt.case_list;
+ case_label->left; case_label = case_label->left)
+ if (! tree_int_cst_lt (case_label->left->high, value))
+ break;
+
+ if (case_label != thiscase->data.case_stmt.case_list
+ && ! tree_int_cst_lt (case_label->high, value)
+ || case_label->left && ! tree_int_cst_lt (value, case_label->left->low))
+ return 2;
+
+ new_label = (struct case_node *) oballoc (sizeof (struct case_node));
+ new_label->low = new_label->high = copy_node (value);
+ new_label->code_label = label;
+ new_label->left = case_label->left;
+
+ case_label->left = new_label;
+ thiscase->data.case_stmt.num_ranges++;
+ }
+ else
+ {
+ if (thiscase->data.case_stmt.default_label)
+ return 2;
+ thiscase->data.case_stmt.default_label = label;
+ }
+
+ expand_label (label);
+ return 0;
+}
+
+/* Called when the index of a switch statement is an enumerated type
+ and there is no default label.
+
+ Checks that all enumeration literals are covered by the case
+ expressions of a switch. Also, warn if there are any extra
+ switch cases that are *not* elements of the enumerated type.
+
+ If all enumeration literals were covered by the case expressions,
+ turn one of the expressions into the default expression since it should
+ not be possible to fall through such a switch. */
+
+void
+check_for_full_enumeration_handling (type)
+ tree type;
+{
+ register struct case_node *n;
+ register struct case_node **l;
+ register tree chain;
+ int all_values = 1;
+
+ if (output_bytecode)
+ {
+ bc_check_for_full_enumeration_handling (type);
+ return;
+ }
+
+ /* The time complexity of this loop is currently O(N * M), with
+ N being the number of members in the enumerated type, and
+ M being the number of case expressions in the switch. */
+
+ for (chain = TYPE_VALUES (type);
+ chain;
+ chain = TREE_CHAIN (chain))
+ {
+ /* Find a match between enumeral and case expression, if possible.
+ Quit looking when we've gone too far (since case expressions
+ are kept sorted in ascending order). Warn about enumerators not
+ handled in the switch statement case expression list. */
+
+ for (n = case_stack->data.case_stmt.case_list;
+ n && tree_int_cst_lt (n->high, TREE_VALUE (chain));
+ n = n->right)
+ ;
+
+ if (!n || tree_int_cst_lt (TREE_VALUE (chain), n->low))
+ {
+ if (warn_switch)
+ warning ("enumeration value `%s' not handled in switch",
+ IDENTIFIER_POINTER (TREE_PURPOSE (chain)));
+ all_values = 0;
+ }
+ }
+
+ /* Now we go the other way around; we warn if there are case
+ expressions that don't correspond to enumerators. This can
+ occur since C and C++ don't enforce type-checking of
+ assignments to enumeration variables. */
+
+ if (warn_switch)
+ for (n = case_stack->data.case_stmt.case_list; n; n = n->right)
+ {
+ for (chain = TYPE_VALUES (type);
+ chain && !tree_int_cst_equal (n->low, TREE_VALUE (chain));
+ chain = TREE_CHAIN (chain))
+ ;
+
+ if (!chain)
+ {
+ if (TYPE_NAME (type) == 0)
+ warning ("case value `%d' not in enumerated type",
+ TREE_INT_CST_LOW (n->low));
+ else
+ warning ("case value `%d' not in enumerated type `%s'",
+ TREE_INT_CST_LOW (n->low),
+ IDENTIFIER_POINTER ((TREE_CODE (TYPE_NAME (type))
+ == IDENTIFIER_NODE)
+ ? TYPE_NAME (type)
+ : DECL_NAME (TYPE_NAME (type))));
+ }
+ if (!tree_int_cst_equal (n->low, n->high))
+ {
+ for (chain = TYPE_VALUES (type);
+ chain && !tree_int_cst_equal (n->high, TREE_VALUE (chain));
+ chain = TREE_CHAIN (chain))
+ ;
+
+ if (!chain)
+ {
+ if (TYPE_NAME (type) == 0)
+ warning ("case value `%d' not in enumerated type",
+ TREE_INT_CST_LOW (n->high));
+ else
+ warning ("case value `%d' not in enumerated type `%s'",
+ TREE_INT_CST_LOW (n->high),
+ IDENTIFIER_POINTER ((TREE_CODE (TYPE_NAME (type))
+ == IDENTIFIER_NODE)
+ ? TYPE_NAME (type)
+ : DECL_NAME (TYPE_NAME (type))));
+ }
+ }
+ }
+
+#if 0
+ /* ??? This optimization is disabled because it causes valid programs to
+ fail. ANSI C does not guarantee that an expression with enum type
+ will have a value that is the same as one of the enumation literals. */
+
+ /* If all values were found as case labels, make one of them the default
+ label. Thus, this switch will never fall through. We arbitrarily pick
+ the last one to make the default since this is likely the most
+ efficient choice. */
+
+ if (all_values)
+ {
+ for (l = &case_stack->data.case_stmt.case_list;
+ (*l)->right != 0;
+ l = &(*l)->right)
+ ;
+
+ case_stack->data.case_stmt.default_label = (*l)->code_label;
+ *l = 0;
+ }
+#endif /* 0 */
+}
+
+
+/* Check that all enumeration literals are covered by the case
+ expressions of a switch. Also warn if there are any cases
+ that are not elements of the enumerated type. */
+
+static void
+bc_check_for_full_enumeration_handling (type)
+ tree type;
+{
+ struct nesting *thiscase = case_stack;
+ struct case_node *c;
+ tree e;
+
+ /* Check for enums not handled. */
+ for (e = TYPE_VALUES (type); e; e = TREE_CHAIN (e))
+ {
+ for (c = thiscase->data.case_stmt.case_list->left;
+ c && tree_int_cst_lt (c->high, TREE_VALUE (e));
+ c = c->left)
+ ;
+ if (! (c && tree_int_cst_equal (c->low, TREE_VALUE (e))))
+ warning ("enumerated value `%s' not handled in switch",
+ IDENTIFIER_POINTER (TREE_PURPOSE (e)));
+ }
+
+ /* Check for cases not in the enumeration. */
+ for (c = thiscase->data.case_stmt.case_list->left; c; c = c->left)
+ {
+ for (e = TYPE_VALUES (type);
+ e && !tree_int_cst_equal (c->low, TREE_VALUE (e));
+ e = TREE_CHAIN (e))
+ ;
+ if (! e)
+ warning ("case value `%d' not in enumerated type `%s'",
+ TREE_INT_CST_LOW (c->low),
+ IDENTIFIER_POINTER (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE
+ ? TYPE_NAME (type)
+ : DECL_NAME (TYPE_NAME (type))));
+ }
+}
+
+/* Terminate a case (Pascal) or switch (C) statement
+ in which ORIG_INDEX is the expression to be tested.
+ Generate the code to test it and jump to the right place. */
+
+void
+expand_end_case (orig_index)
+ tree orig_index;
+{
+ tree minval, maxval, range, orig_minval;
+ rtx default_label = 0;
+ register struct case_node *n;
+ int count;
+ rtx index;
+ rtx table_label;
+ int ncases;
+ rtx *labelvec;
+ register int i;
+ rtx before_case;
+ register struct nesting *thiscase = case_stack;
+ tree index_expr, index_type;
+ int unsignedp;
+
+ if (output_bytecode)
+ {
+ bc_expand_end_case (orig_index);
+ return;
+ }
+
+ table_label = gen_label_rtx ();
+ index_expr = thiscase->data.case_stmt.index_expr;
+ index_type = TREE_TYPE (index_expr);
+ unsignedp = TREE_UNSIGNED (index_type);
+
+ do_pending_stack_adjust ();
+
+ /* An ERROR_MARK occurs for various reasons including invalid data type. */
+ if (index_type != error_mark_node)
+ {
+ /* If switch expression was an enumerated type, check that all
+ enumeration literals are covered by the cases.
+ No sense trying this if there's a default case, however. */
+
+ if (!thiscase->data.case_stmt.default_label
+ && TREE_CODE (TREE_TYPE (orig_index)) == ENUMERAL_TYPE
+ && TREE_CODE (index_expr) != INTEGER_CST)
+ check_for_full_enumeration_handling (TREE_TYPE (orig_index));
+
+ /* If this is the first label, warn if any insns have been emitted. */
+ if (thiscase->data.case_stmt.seenlabel == 0)
+ {
+ rtx insn;
+ for (insn = get_last_insn ();
+ insn != case_stack->data.case_stmt.start;
+ insn = PREV_INSN (insn))
+ if (GET_CODE (insn) != NOTE
+ && (GET_CODE (insn) != INSN || GET_CODE (PATTERN (insn))!= USE))
+ {
+ warning ("unreachable code at beginning of %s",
+ case_stack->data.case_stmt.printname);
+ break;
+ }
+ }
+
+ /* If we don't have a default-label, create one here,
+ after the body of the switch. */
+ if (thiscase->data.case_stmt.default_label == 0)
+ {
+ thiscase->data.case_stmt.default_label
+ = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+ expand_label (thiscase->data.case_stmt.default_label);
+ }
+ default_label = label_rtx (thiscase->data.case_stmt.default_label);
+
+ before_case = get_last_insn ();
+
+ /* Simplify the case-list before we count it. */
+ group_case_nodes (thiscase->data.case_stmt.case_list);
+
+ /* Get upper and lower bounds of case values.
+ Also convert all the case values to the index expr's data type. */
+
+ count = 0;
+ for (n = thiscase->data.case_stmt.case_list; n; n = n->right)
+ {
+ /* Check low and high label values are integers. */
+ if (TREE_CODE (n->low) != INTEGER_CST)
+ abort ();
+ if (TREE_CODE (n->high) != INTEGER_CST)
+ abort ();
+
+ n->low = convert (index_type, n->low);
+ n->high = convert (index_type, n->high);
+
+ /* Count the elements and track the largest and smallest
+ of them (treating them as signed even if they are not). */
+ if (count++ == 0)
+ {
+ minval = n->low;
+ maxval = n->high;
+ }
+ else
+ {
+ if (INT_CST_LT (n->low, minval))
+ minval = n->low;
+ if (INT_CST_LT (maxval, n->high))
+ maxval = n->high;
+ }
+ /* A range counts double, since it requires two compares. */
+ if (! tree_int_cst_equal (n->low, n->high))
+ count++;
+ }
+
+ orig_minval = minval;
+
+ /* Compute span of values. */
+ if (count != 0)
+ range = fold (build (MINUS_EXPR, index_type, maxval, minval));
+
+ if (count == 0)
+ {
+ expand_expr (index_expr, const0_rtx, VOIDmode, 0);
+ emit_queue ();
+ emit_jump (default_label);
+ }
+
+ /* If range of values is much bigger than number of values,
+ make a sequence of conditional branches instead of a dispatch.
+ If the switch-index is a constant, do it this way
+ because we can optimize it. */
+
+#ifndef CASE_VALUES_THRESHOLD
+#ifdef HAVE_casesi
+#define CASE_VALUES_THRESHOLD (HAVE_casesi ? 4 : 5)
+#else
+ /* If machine does not have a case insn that compares the
+ bounds, this means extra overhead for dispatch tables
+ which raises the threshold for using them. */
+#define CASE_VALUES_THRESHOLD 5
+#endif /* HAVE_casesi */
+#endif /* CASE_VALUES_THRESHOLD */
+
+ else if (TREE_INT_CST_HIGH (range) != 0
+ || count < CASE_VALUES_THRESHOLD
+ || ((unsigned HOST_WIDE_INT) (TREE_INT_CST_LOW (range))
+ > 10 * count)
+ || TREE_CODE (index_expr) == INTEGER_CST
+ /* These will reduce to a constant. */
+ || (TREE_CODE (index_expr) == CALL_EXPR
+ && TREE_CODE (TREE_OPERAND (index_expr, 0)) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (TREE_OPERAND (index_expr, 0), 0)) == FUNCTION_DECL
+ && DECL_FUNCTION_CODE (TREE_OPERAND (TREE_OPERAND (index_expr, 0), 0)) == BUILT_IN_CLASSIFY_TYPE)
+ || (TREE_CODE (index_expr) == COMPOUND_EXPR
+ && TREE_CODE (TREE_OPERAND (index_expr, 1)) == INTEGER_CST))
+ {
+ index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0);
+
+ /* If the index is a short or char that we do not have
+ an insn to handle comparisons directly, convert it to
+ a full integer now, rather than letting each comparison
+ generate the conversion. */
+
+ if (GET_MODE_CLASS (GET_MODE (index)) == MODE_INT
+ && (cmp_optab->handlers[(int) GET_MODE(index)].insn_code
+ == CODE_FOR_nothing))
+ {
+ enum machine_mode wider_mode;
+ for (wider_mode = GET_MODE (index); wider_mode != VOIDmode;
+ wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+ if (cmp_optab->handlers[(int) wider_mode].insn_code
+ != CODE_FOR_nothing)
+ {
+ index = convert_to_mode (wider_mode, index, unsignedp);
+ break;
+ }
+ }
+
+ emit_queue ();
+ do_pending_stack_adjust ();
+
+ index = protect_from_queue (index, 0);
+ if (GET_CODE (index) == MEM)
+ index = copy_to_reg (index);
+ if (GET_CODE (index) == CONST_INT
+ || TREE_CODE (index_expr) == INTEGER_CST)
+ {
+ /* Make a tree node with the proper constant value
+ if we don't already have one. */
+ if (TREE_CODE (index_expr) != INTEGER_CST)
+ {
+ index_expr
+ = build_int_2 (INTVAL (index),
+ unsignedp || INTVAL (index) >= 0 ? 0 : -1);
+ index_expr = convert (index_type, index_expr);
+ }
+
+ /* For constant index expressions we need only
+ issue a unconditional branch to the appropriate
+ target code. The job of removing any unreachable
+ code is left to the optimisation phase if the
+ "-O" option is specified. */
+ for (n = thiscase->data.case_stmt.case_list; n; n = n->right)
+ if (! tree_int_cst_lt (index_expr, n->low)
+ && ! tree_int_cst_lt (n->high, index_expr))
+ break;
+
+ if (n)
+ emit_jump (label_rtx (n->code_label));
+ else
+ emit_jump (default_label);
+ }
+ else
+ {
+ /* If the index expression is not constant we generate
+ a binary decision tree to select the appropriate
+ target code. This is done as follows:
+
+ The list of cases is rearranged into a binary tree,
+ nearly optimal assuming equal probability for each case.
+
+ The tree is transformed into RTL, eliminating
+ redundant test conditions at the same time.
+
+ If program flow could reach the end of the
+ decision tree an unconditional jump to the
+ default code is emitted. */
+
+ use_cost_table
+ = (TREE_CODE (TREE_TYPE (orig_index)) != ENUMERAL_TYPE
+ && estimate_case_costs (thiscase->data.case_stmt.case_list));
+ balance_case_nodes (&thiscase->data.case_stmt.case_list,
+ NULL_PTR);
+ emit_case_nodes (index, thiscase->data.case_stmt.case_list,
+ default_label, index_type);
+ emit_jump_if_reachable (default_label);
+ }
+ }
+ else
+ {
+ int win = 0;
+#ifdef HAVE_casesi
+ if (HAVE_casesi)
+ {
+ enum machine_mode index_mode = SImode;
+ int index_bits = GET_MODE_BITSIZE (index_mode);
+ rtx op1, op2;
+ enum machine_mode op_mode;
+
+ /* Convert the index to SImode. */
+ if (GET_MODE_BITSIZE (TYPE_MODE (index_type))
+ > GET_MODE_BITSIZE (index_mode))
+ {
+ enum machine_mode omode = TYPE_MODE (index_type);
+ rtx rangertx = expand_expr (range, NULL_RTX, VOIDmode, 0);
+
+ /* We must handle the endpoints in the original mode. */
+ index_expr = build (MINUS_EXPR, index_type,
+ index_expr, minval);
+ minval = integer_zero_node;
+ index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0);
+ emit_cmp_insn (rangertx, index, LTU, NULL_RTX, omode, 1, 0);
+ emit_jump_insn (gen_bltu (default_label));
+ /* Now we can safely truncate. */
+ index = convert_to_mode (index_mode, index, 0);
+ }
+ else
+ {
+ if (TYPE_MODE (index_type) != index_mode)
+ {
+ index_expr = convert (type_for_size (index_bits, 0),
+ index_expr);
+ index_type = TREE_TYPE (index_expr);
+ }
+
+ index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0);
+ }
+ emit_queue ();
+ index = protect_from_queue (index, 0);
+ do_pending_stack_adjust ();
+
+ op_mode = insn_operand_mode[(int)CODE_FOR_casesi][0];
+ if (! (*insn_operand_predicate[(int)CODE_FOR_casesi][0])
+ (index, op_mode))
+ index = copy_to_mode_reg (op_mode, index);
+
+ op1 = expand_expr (minval, NULL_RTX, VOIDmode, 0);
+
+ op_mode = insn_operand_mode[(int)CODE_FOR_casesi][1];
+ if (! (*insn_operand_predicate[(int)CODE_FOR_casesi][1])
+ (op1, op_mode))
+ op1 = copy_to_mode_reg (op_mode, op1);
+
+ op2 = expand_expr (range, NULL_RTX, VOIDmode, 0);
+
+ op_mode = insn_operand_mode[(int)CODE_FOR_casesi][2];
+ if (! (*insn_operand_predicate[(int)CODE_FOR_casesi][2])
+ (op2, op_mode))
+ op2 = copy_to_mode_reg (op_mode, op2);
+
+ emit_jump_insn (gen_casesi (index, op1, op2,
+ table_label, default_label));
+ win = 1;
+ }
+#endif
+#ifdef HAVE_tablejump
+ if (! win && HAVE_tablejump)
+ {
+ index_expr = convert (thiscase->data.case_stmt.nominal_type,
+ fold (build (MINUS_EXPR, index_type,
+ index_expr, minval)));
+ index_type = TREE_TYPE (index_expr);
+ index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0);
+ emit_queue ();
+ index = protect_from_queue (index, 0);
+ do_pending_stack_adjust ();
+
+ do_tablejump (index, TYPE_MODE (index_type),
+ expand_expr (range, NULL_RTX, VOIDmode, 0),
+ table_label, default_label);
+ win = 1;
+ }
+#endif
+ if (! win)
+ abort ();
+
+ /* Get table of labels to jump to, in order of case index. */
+
+ ncases = TREE_INT_CST_LOW (range) + 1;
+ labelvec = (rtx *) alloca (ncases * sizeof (rtx));
+ bzero ((char *) labelvec, ncases * sizeof (rtx));
+
+ for (n = thiscase->data.case_stmt.case_list; n; n = n->right)
+ {
+ register HOST_WIDE_INT i
+ = TREE_INT_CST_LOW (n->low) - TREE_INT_CST_LOW (orig_minval);
+
+ while (1)
+ {
+ labelvec[i]
+ = gen_rtx (LABEL_REF, Pmode, label_rtx (n->code_label));
+ if (i + TREE_INT_CST_LOW (orig_minval)
+ == TREE_INT_CST_LOW (n->high))
+ break;
+ i++;
+ }
+ }
+
+ /* Fill in the gaps with the default. */
+ for (i = 0; i < ncases; i++)
+ if (labelvec[i] == 0)
+ labelvec[i] = gen_rtx (LABEL_REF, Pmode, default_label);
+
+ /* Output the table */
+ emit_label (table_label);
+
+ /* This would be a lot nicer if CASE_VECTOR_PC_RELATIVE
+ were an expression, instead of an #ifdef/#ifndef. */
+ if (
+#ifdef CASE_VECTOR_PC_RELATIVE
+ 1 ||
+#endif
+ flag_pic)
+ emit_jump_insn (gen_rtx (ADDR_DIFF_VEC, CASE_VECTOR_MODE,
+ gen_rtx (LABEL_REF, Pmode, table_label),
+ gen_rtvec_v (ncases, labelvec)));
+ else
+ emit_jump_insn (gen_rtx (ADDR_VEC, CASE_VECTOR_MODE,
+ gen_rtvec_v (ncases, labelvec)));
+
+ /* If the case insn drops through the table,
+ after the table we must jump to the default-label.
+ Otherwise record no drop-through after the table. */
+#ifdef CASE_DROPS_THROUGH
+ emit_jump (default_label);
+#else
+ emit_barrier ();
+#endif
+ }
+
+ before_case = squeeze_notes (NEXT_INSN (before_case), get_last_insn ());
+ reorder_insns (before_case, get_last_insn (),
+ thiscase->data.case_stmt.start);
+ }
+
+ if (thiscase->exit_label)
+ emit_label (thiscase->exit_label);
+
+ POPSTACK (case_stack);
+
+ free_temp_slots ();
+}
+
+
+/* Terminate a case statement. EXPR is the original index
+ expression. */
+
+static void
+bc_expand_end_case (expr)
+ tree expr;
+{
+ struct nesting *thiscase = case_stack;
+ enum bytecode_opcode opcode;
+ struct bc_label *jump_label;
+ struct case_node *c;
+
+ bc_emit_bytecode (jump);
+ bc_emit_bytecode_labelref (BYTECODE_BC_LABEL (thiscase->exit_label));
+
+#ifdef DEBUG_PRINT_CODE
+ fputc ('\n', stderr);
+#endif
+
+ /* Now that the size of the jump table is known, emit the actual
+ indexed jump instruction. */
+ bc_emit_bytecode_labeldef (BYTECODE_BC_LABEL (thiscase->data.case_stmt.skip_label));
+
+ opcode = TYPE_MODE (thiscase->data.case_stmt.nominal_type) == SImode
+ ? TREE_UNSIGNED (thiscase->data.case_stmt.nominal_type) ? caseSU : caseSI
+ : TREE_UNSIGNED (thiscase->data.case_stmt.nominal_type) ? caseDU : caseDI;
+
+ bc_emit_bytecode (opcode);
+
+ /* Now emit the case instructions literal arguments, in order.
+ In addition to the value on the stack, it uses:
+ 1. The address of the jump table.
+ 2. The size of the jump table.
+ 3. The default label. */
+
+ jump_label = bc_get_bytecode_label ();
+ bc_emit_bytecode_labelref (jump_label);
+ bc_emit_bytecode_const ((char *) &thiscase->data.case_stmt.num_ranges,
+ sizeof thiscase->data.case_stmt.num_ranges);
+
+ if (thiscase->data.case_stmt.default_label)
+ bc_emit_bytecode_labelref (BYTECODE_BC_LABEL (DECL_RTL (thiscase->data.case_stmt.default_label)));
+ else
+ bc_emit_bytecode_labelref (BYTECODE_BC_LABEL (thiscase->exit_label));
+
+ /* Output the jump table. */
+
+ bc_align_bytecode (3 /* PTR_ALIGN */);
+ bc_emit_bytecode_labeldef (jump_label);
+
+ if (TYPE_MODE (thiscase->data.case_stmt.nominal_type) == SImode)
+ for (c = thiscase->data.case_stmt.case_list->left; c; c = c->left)
+ {
+ opcode = TREE_INT_CST_LOW (c->low);
+ bc_emit_bytecode_const ((char *) &opcode, sizeof opcode);
+
+ opcode = TREE_INT_CST_LOW (c->high);
+ bc_emit_bytecode_const ((char *) &opcode, sizeof opcode);
+
+ bc_emit_bytecode_labelref (BYTECODE_BC_LABEL (DECL_RTL (c->code_label)));
+ }
+ else
+ if (TYPE_MODE (thiscase->data.case_stmt.nominal_type) == DImode)
+ for (c = thiscase->data.case_stmt.case_list->left; c; c = c->left)
+ {
+ bc_emit_bytecode_DI_const (c->low);
+ bc_emit_bytecode_DI_const (c->high);
+
+ bc_emit_bytecode_labelref (BYTECODE_BC_LABEL (DECL_RTL (c->code_label)));
+ }
+ else
+ /* Bad mode */
+ abort ();
+
+
+ bc_emit_bytecode_labeldef (BYTECODE_BC_LABEL (thiscase->exit_label));
+
+ /* Possibly issue enumeration warnings. */
+
+ if (!thiscase->data.case_stmt.default_label
+ && TREE_CODE (TREE_TYPE (expr)) == ENUMERAL_TYPE
+ && TREE_CODE (expr) != INTEGER_CST
+ && warn_switch)
+ check_for_full_enumeration_handling (TREE_TYPE (expr));
+
+
+#ifdef DEBUG_PRINT_CODE
+ fputc ('\n', stderr);
+#endif
+
+ POPSTACK (case_stack);
+}
+
+
+/* Return unique bytecode ID. */
+
+int
+bc_new_uid ()
+{
+ static int bc_uid = 0;
+
+ return (++bc_uid);
+}
+
+/* Generate code to jump to LABEL if OP1 and OP2 are equal. */
+
+static void
+do_jump_if_equal (op1, op2, label, unsignedp)
+ rtx op1, op2, label;
+ int unsignedp;
+{
+ if (GET_CODE (op1) == CONST_INT
+ && GET_CODE (op2) == CONST_INT)
+ {
+ if (INTVAL (op1) == INTVAL (op2))
+ emit_jump (label);
+ }
+ else
+ {
+ enum machine_mode mode = GET_MODE (op1);
+ if (mode == VOIDmode)
+ mode = GET_MODE (op2);
+ emit_cmp_insn (op1, op2, EQ, NULL_RTX, mode, unsignedp, 0);
+ emit_jump_insn (gen_beq (label));
+ }
+}
+
+/* Not all case values are encountered equally. This function
+ uses a heuristic to weight case labels, in cases where that
+ looks like a reasonable thing to do.
+
+ Right now, all we try to guess is text, and we establish the
+ following weights:
+
+ chars above space: 16
+ digits: 16
+ default: 12
+ space, punct: 8
+ tab: 4
+ newline: 2
+ other "\" chars: 1
+ remaining chars: 0
+
+ If we find any cases in the switch that are not either -1 or in the range
+ of valid ASCII characters, or are control characters other than those
+ commonly used with "\", don't treat this switch scanning text.
+
+ Return 1 if these nodes are suitable for cost estimation, otherwise
+ return 0. */
+
+static int
+estimate_case_costs (node)
+ case_node_ptr node;
+{
+ tree min_ascii = build_int_2 (-1, -1);
+ tree max_ascii = convert (TREE_TYPE (node->high), build_int_2 (127, 0));
+ case_node_ptr n;
+ int i;
+
+ /* If we haven't already made the cost table, make it now. Note that the
+ lower bound of the table is -1, not zero. */
+
+ if (cost_table == NULL)
+ {
+ cost_table = ((short *) xmalloc (129 * sizeof (short))) + 1;
+ bzero ((char *) (cost_table - 1), 129 * sizeof (short));
+
+ for (i = 0; i < 128; i++)
+ {
+ if (isalnum (i))
+ cost_table[i] = 16;
+ else if (ispunct (i))
+ cost_table[i] = 8;
+ else if (iscntrl (i))
+ cost_table[i] = -1;
+ }
+
+ cost_table[' '] = 8;
+ cost_table['\t'] = 4;
+ cost_table['\0'] = 4;
+ cost_table['\n'] = 2;
+ cost_table['\f'] = 1;
+ cost_table['\v'] = 1;
+ cost_table['\b'] = 1;
+ }
+
+ /* See if all the case expressions look like text. It is text if the
+ constant is >= -1 and the highest constant is <= 127. Do all comparisons
+ as signed arithmetic since we don't want to ever access cost_table with a
+ value less than -1. Also check that none of the constants in a range
+ are strange control characters. */
+
+ for (n = node; n; n = n->right)
+ {
+ if ((INT_CST_LT (n->low, min_ascii)) || INT_CST_LT (max_ascii, n->high))
+ return 0;
+
+ for (i = TREE_INT_CST_LOW (n->low); i <= TREE_INT_CST_LOW (n->high); i++)
+ if (cost_table[i] < 0)
+ return 0;
+ }
+
+ /* All interesting values are within the range of interesting
+ ASCII characters. */
+ return 1;
+}
+
+/* Scan an ordered list of case nodes
+ combining those with consecutive values or ranges.
+
+ Eg. three separate entries 1: 2: 3: become one entry 1..3: */
+
+static void
+group_case_nodes (head)
+ case_node_ptr head;
+{
+ case_node_ptr node = head;
+
+ while (node)
+ {
+ rtx lb = next_real_insn (label_rtx (node->code_label));
+ case_node_ptr np = node;
+
+ /* Try to group the successors of NODE with NODE. */
+ while (((np = np->right) != 0)
+ /* Do they jump to the same place? */
+ && next_real_insn (label_rtx (np->code_label)) == lb
+ /* Are their ranges consecutive? */
+ && tree_int_cst_equal (np->low,
+ fold (build (PLUS_EXPR,
+ TREE_TYPE (node->high),
+ node->high,
+ integer_one_node)))
+ /* An overflow is not consecutive. */
+ && tree_int_cst_lt (node->high,
+ fold (build (PLUS_EXPR,
+ TREE_TYPE (node->high),
+ node->high,
+ integer_one_node))))
+ {
+ node->high = np->high;
+ }
+ /* NP is the first node after NODE which can't be grouped with it.
+ Delete the nodes in between, and move on to that node. */
+ node->right = np;
+ node = np;
+ }
+}
+
+/* Take an ordered list of case nodes
+ and transform them into a near optimal binary tree,
+ on the assumption that any target code selection value is as
+ likely as any other.
+
+ The transformation is performed by splitting the ordered
+ list into two equal sections plus a pivot. The parts are
+ then attached to the pivot as left and right branches. Each
+ branch is is then transformed recursively. */
+
+static void
+balance_case_nodes (head, parent)
+ case_node_ptr *head;
+ case_node_ptr parent;
+{
+ register case_node_ptr np;
+
+ np = *head;
+ if (np)
+ {
+ int cost = 0;
+ int i = 0;
+ int ranges = 0;
+ register case_node_ptr *npp;
+ case_node_ptr left;
+
+ /* Count the number of entries on branch. Also count the ranges. */
+
+ while (np)
+ {
+ if (!tree_int_cst_equal (np->low, np->high))
+ {
+ ranges++;
+ if (use_cost_table)
+ cost += cost_table[TREE_INT_CST_LOW (np->high)];
+ }
+
+ if (use_cost_table)
+ cost += cost_table[TREE_INT_CST_LOW (np->low)];
+
+ i++;
+ np = np->right;
+ }
+
+ if (i > 2)
+ {
+ /* Split this list if it is long enough for that to help. */
+ npp = head;
+ left = *npp;
+ if (use_cost_table)
+ {
+ /* Find the place in the list that bisects the list's total cost,
+ Here I gets half the total cost. */
+ int n_moved = 0;
+ i = (cost + 1) / 2;
+ while (1)
+ {
+ /* Skip nodes while their cost does not reach that amount. */
+ if (!tree_int_cst_equal ((*npp)->low, (*npp)->high))
+ i -= cost_table[TREE_INT_CST_LOW ((*npp)->high)];
+ i -= cost_table[TREE_INT_CST_LOW ((*npp)->low)];
+ if (i <= 0)
+ break;
+ npp = &(*npp)->right;
+ n_moved += 1;
+ }
+ if (n_moved == 0)
+ {
+ /* Leave this branch lopsided, but optimize left-hand
+ side and fill in `parent' fields for right-hand side. */
+ np = *head;
+ np->parent = parent;
+ balance_case_nodes (&np->left, np);
+ for (; np->right; np = np->right)
+ np->right->parent = np;
+ return;
+ }
+ }
+ /* If there are just three nodes, split at the middle one. */
+ else if (i == 3)
+ npp = &(*npp)->right;
+ else
+ {
+ /* Find the place in the list that bisects the list's total cost,
+ where ranges count as 2.
+ Here I gets half the total cost. */
+ i = (i + ranges + 1) / 2;
+ while (1)
+ {
+ /* Skip nodes while their cost does not reach that amount. */
+ if (!tree_int_cst_equal ((*npp)->low, (*npp)->high))
+ i--;
+ i--;
+ if (i <= 0)
+ break;
+ npp = &(*npp)->right;
+ }
+ }
+ *head = np = *npp;
+ *npp = 0;
+ np->parent = parent;
+ np->left = left;
+
+ /* Optimize each of the two split parts. */
+ balance_case_nodes (&np->left, np);
+ balance_case_nodes (&np->right, np);
+ }
+ else
+ {
+ /* Else leave this branch as one level,
+ but fill in `parent' fields. */
+ np = *head;
+ np->parent = parent;
+ for (; np->right; np = np->right)
+ np->right->parent = np;
+ }
+ }
+}
+
+/* Search the parent sections of the case node tree
+ to see if a test for the lower bound of NODE would be redundant.
+ INDEX_TYPE is the type of the index expression.
+
+ The instructions to generate the case decision tree are
+ output in the same order as nodes are processed so it is
+ known that if a parent node checks the range of the current
+ node minus one that the current node is bounded at its lower
+ span. Thus the test would be redundant. */
+
+static int
+node_has_low_bound (node, index_type)
+ case_node_ptr node;
+ tree index_type;
+{
+ tree low_minus_one;
+ case_node_ptr pnode;
+
+ /* If the lower bound of this node is the lowest value in the index type,
+ we need not test it. */
+
+ if (tree_int_cst_equal (node->low, TYPE_MIN_VALUE (index_type)))
+ return 1;
+
+ /* If this node has a left branch, the value at the left must be less
+ than that at this node, so it cannot be bounded at the bottom and
+ we need not bother testing any further. */
+
+ if (node->left)
+ return 0;
+
+ low_minus_one = fold (build (MINUS_EXPR, TREE_TYPE (node->low),
+ node->low, integer_one_node));
+
+ /* If the subtraction above overflowed, we can't verify anything.
+ Otherwise, look for a parent that tests our value - 1. */
+
+ if (! tree_int_cst_lt (low_minus_one, node->low))
+ return 0;
+
+ for (pnode = node->parent; pnode; pnode = pnode->parent)
+ if (tree_int_cst_equal (low_minus_one, pnode->high))
+ return 1;
+
+ return 0;
+}
+
+/* Search the parent sections of the case node tree
+ to see if a test for the upper bound of NODE would be redundant.
+ INDEX_TYPE is the type of the index expression.
+
+ The instructions to generate the case decision tree are
+ output in the same order as nodes are processed so it is
+ known that if a parent node checks the range of the current
+ node plus one that the current node is bounded at its upper
+ span. Thus the test would be redundant. */
+
+static int
+node_has_high_bound (node, index_type)
+ case_node_ptr node;
+ tree index_type;
+{
+ tree high_plus_one;
+ case_node_ptr pnode;
+
+ /* If the upper bound of this node is the highest value in the type
+ of the index expression, we need not test against it. */
+
+ if (tree_int_cst_equal (node->high, TYPE_MAX_VALUE (index_type)))
+ return 1;
+
+ /* If this node has a right branch, the value at the right must be greater
+ than that at this node, so it cannot be bounded at the top and
+ we need not bother testing any further. */
+
+ if (node->right)
+ return 0;
+
+ high_plus_one = fold (build (PLUS_EXPR, TREE_TYPE (node->high),
+ node->high, integer_one_node));
+
+ /* If the addition above overflowed, we can't verify anything.
+ Otherwise, look for a parent that tests our value + 1. */
+
+ if (! tree_int_cst_lt (node->high, high_plus_one))
+ return 0;
+
+ for (pnode = node->parent; pnode; pnode = pnode->parent)
+ if (tree_int_cst_equal (high_plus_one, pnode->low))
+ return 1;
+
+ return 0;
+}
+
+/* Search the parent sections of the
+ case node tree to see if both tests for the upper and lower
+ bounds of NODE would be redundant. */
+
+static int
+node_is_bounded (node, index_type)
+ case_node_ptr node;
+ tree index_type;
+{
+ return (node_has_low_bound (node, index_type)
+ && node_has_high_bound (node, index_type));
+}
+
+/* Emit an unconditional jump to LABEL unless it would be dead code. */
+
+static void
+emit_jump_if_reachable (label)
+ rtx label;
+{
+ if (GET_CODE (get_last_insn ()) != BARRIER)
+ emit_jump (label);
+}
+
+/* Emit step-by-step code to select a case for the value of INDEX.
+ The thus generated decision tree follows the form of the
+ case-node binary tree NODE, whose nodes represent test conditions.
+ INDEX_TYPE is the type of the index of the switch.
+
+ Care is taken to prune redundant tests from the decision tree
+ by detecting any boundary conditions already checked by
+ emitted rtx. (See node_has_high_bound, node_has_low_bound
+ and node_is_bounded, above.)
+
+ Where the test conditions can be shown to be redundant we emit
+ an unconditional jump to the target code. As a further
+ optimization, the subordinates of a tree node are examined to
+ check for bounded nodes. In this case conditional and/or
+ unconditional jumps as a result of the boundary check for the
+ current node are arranged to target the subordinates associated
+ code for out of bound conditions on the current node node.
+
+ We can assume that when control reaches the code generated here,
+ the index value has already been compared with the parents
+ of this node, and determined to be on the same side of each parent
+ as this node is. Thus, if this node tests for the value 51,
+ and a parent tested for 52, we don't need to consider
+ the possibility of a value greater than 51. If another parent
+ tests for the value 50, then this node need not test anything. */
+
+static void
+emit_case_nodes (index, node, default_label, index_type)
+ rtx index;
+ case_node_ptr node;
+ rtx default_label;
+ tree index_type;
+{
+ /* If INDEX has an unsigned type, we must make unsigned branches. */
+ int unsignedp = TREE_UNSIGNED (index_type);
+ typedef rtx rtx_function ();
+ rtx_function *gen_bgt_pat = unsignedp ? gen_bgtu : gen_bgt;
+ rtx_function *gen_bge_pat = unsignedp ? gen_bgeu : gen_bge;
+ rtx_function *gen_blt_pat = unsignedp ? gen_bltu : gen_blt;
+ rtx_function *gen_ble_pat = unsignedp ? gen_bleu : gen_ble;
+ enum machine_mode mode = GET_MODE (index);
+
+ /* See if our parents have already tested everything for us.
+ If they have, emit an unconditional jump for this node. */
+ if (node_is_bounded (node, index_type))
+ emit_jump (label_rtx (node->code_label));
+
+ else if (tree_int_cst_equal (node->low, node->high))
+ {
+ /* Node is single valued. First see if the index expression matches
+ this node and then check our children, if any. */
+
+ do_jump_if_equal (index, expand_expr (node->low, NULL_RTX, VOIDmode, 0),
+ label_rtx (node->code_label), unsignedp);
+
+ if (node->right != 0 && node->left != 0)
+ {
+ /* This node has children on both sides.
+ Dispatch to one side or the other
+ by comparing the index value with this node's value.
+ If one subtree is bounded, check that one first,
+ so we can avoid real branches in the tree. */
+
+ if (node_is_bounded (node->right, index_type))
+ {
+ emit_cmp_insn (index, expand_expr (node->high, NULL_RTX,
+ VOIDmode, 0),
+ GT, NULL_RTX, mode, unsignedp, 0);
+
+ emit_jump_insn ((*gen_bgt_pat) (label_rtx (node->right->code_label)));
+ emit_case_nodes (index, node->left, default_label, index_type);
+ }
+
+ else if (node_is_bounded (node->left, index_type))
+ {
+ emit_cmp_insn (index, expand_expr (node->high, NULL_RTX,
+ VOIDmode, 0),
+ LT, NULL_RTX, mode, unsignedp, 0);
+ emit_jump_insn ((*gen_blt_pat) (label_rtx (node->left->code_label)));
+ emit_case_nodes (index, node->right, default_label, index_type);
+ }
+
+ else
+ {
+ /* Neither node is bounded. First distinguish the two sides;
+ then emit the code for one side at a time. */
+
+ tree test_label
+ = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+
+ /* See if the value is on the right. */
+ emit_cmp_insn (index, expand_expr (node->high, NULL_RTX,
+ VOIDmode, 0),
+ GT, NULL_RTX, mode, unsignedp, 0);
+ emit_jump_insn ((*gen_bgt_pat) (label_rtx (test_label)));
+
+ /* Value must be on the left.
+ Handle the left-hand subtree. */
+ emit_case_nodes (index, node->left, default_label, index_type);
+ /* If left-hand subtree does nothing,
+ go to default. */
+ emit_jump_if_reachable (default_label);
+
+ /* Code branches here for the right-hand subtree. */
+ expand_label (test_label);
+ emit_case_nodes (index, node->right, default_label, index_type);
+ }
+ }
+
+ else if (node->right != 0 && node->left == 0)
+ {
+ /* Here we have a right child but no left so we issue conditional
+ branch to default and process the right child.
+
+ Omit the conditional branch to default if we it avoid only one
+ right child; it costs too much space to save so little time. */
+
+ if (node->right->right || node->right->left
+ || !tree_int_cst_equal (node->right->low, node->right->high))
+ {
+ if (!node_has_low_bound (node, index_type))
+ {
+ emit_cmp_insn (index, expand_expr (node->high, NULL_RTX,
+ VOIDmode, 0),
+ LT, NULL_RTX, mode, unsignedp, 0);
+ emit_jump_insn ((*gen_blt_pat) (default_label));
+ }
+
+ emit_case_nodes (index, node->right, default_label, index_type);
+ }
+ else
+ /* We cannot process node->right normally
+ since we haven't ruled out the numbers less than
+ this node's value. So handle node->right explicitly. */
+ do_jump_if_equal (index,
+ expand_expr (node->right->low, NULL_RTX,
+ VOIDmode, 0),
+ label_rtx (node->right->code_label), unsignedp);
+ }
+
+ else if (node->right == 0 && node->left != 0)
+ {
+ /* Just one subtree, on the left. */
+
+#if 0 /* The following code and comment were formerly part
+ of the condition here, but they didn't work
+ and I don't understand what the idea was. -- rms. */
+ /* If our "most probable entry" is less probable
+ than the default label, emit a jump to
+ the default label using condition codes
+ already lying around. With no right branch,
+ a branch-greater-than will get us to the default
+ label correctly. */
+ if (use_cost_table
+ && cost_table[TREE_INT_CST_LOW (node->high)] < 12)
+ ;
+#endif /* 0 */
+ if (node->left->left || node->left->right
+ || !tree_int_cst_equal (node->left->low, node->left->high))
+ {
+ if (!node_has_high_bound (node, index_type))
+ {
+ emit_cmp_insn (index, expand_expr (node->high, NULL_RTX,
+ VOIDmode, 0),
+ GT, NULL_RTX, mode, unsignedp, 0);
+ emit_jump_insn ((*gen_bgt_pat) (default_label));
+ }
+
+ emit_case_nodes (index, node->left, default_label, index_type);
+ }
+ else
+ /* We cannot process node->left normally
+ since we haven't ruled out the numbers less than
+ this node's value. So handle node->left explicitly. */
+ do_jump_if_equal (index,
+ expand_expr (node->left->low, NULL_RTX,
+ VOIDmode, 0),
+ label_rtx (node->left->code_label), unsignedp);
+ }
+ }
+ else
+ {
+ /* Node is a range. These cases are very similar to those for a single
+ value, except that we do not start by testing whether this node
+ is the one to branch to. */
+
+ if (node->right != 0 && node->left != 0)
+ {
+ /* Node has subtrees on both sides.
+ If the right-hand subtree is bounded,
+ test for it first, since we can go straight there.
+ Otherwise, we need to make a branch in the control structure,
+ then handle the two subtrees. */
+ tree test_label = 0;
+
+ emit_cmp_insn (index, expand_expr (node->high, NULL_RTX,
+ VOIDmode, 0),
+ GT, NULL_RTX, mode, unsignedp, 0);
+
+ if (node_is_bounded (node->right, index_type))
+ /* Right hand node is fully bounded so we can eliminate any
+ testing and branch directly to the target code. */
+ emit_jump_insn ((*gen_bgt_pat) (label_rtx (node->right->code_label)));
+ else
+ {
+ /* Right hand node requires testing.
+ Branch to a label where we will handle it later. */
+
+ test_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+ emit_jump_insn ((*gen_bgt_pat) (label_rtx (test_label)));
+ }
+
+ /* Value belongs to this node or to the left-hand subtree. */
+
+ emit_cmp_insn (index, expand_expr (node->low, NULL_RTX, VOIDmode, 0),
+ GE, NULL_RTX, mode, unsignedp, 0);
+ emit_jump_insn ((*gen_bge_pat) (label_rtx (node->code_label)));
+
+ /* Handle the left-hand subtree. */
+ emit_case_nodes (index, node->left, default_label, index_type);
+
+ /* If right node had to be handled later, do that now. */
+
+ if (test_label)
+ {
+ /* If the left-hand subtree fell through,
+ don't let it fall into the right-hand subtree. */
+ emit_jump_if_reachable (default_label);
+
+ expand_label (test_label);
+ emit_case_nodes (index, node->right, default_label, index_type);
+ }
+ }
+
+ else if (node->right != 0 && node->left == 0)
+ {
+ /* Deal with values to the left of this node,
+ if they are possible. */
+ if (!node_has_low_bound (node, index_type))
+ {
+ emit_cmp_insn (index, expand_expr (node->low, NULL_RTX,
+ VOIDmode, 0),
+ LT, NULL_RTX, mode, unsignedp, 0);
+ emit_jump_insn ((*gen_blt_pat) (default_label));
+ }
+
+ /* Value belongs to this node or to the right-hand subtree. */
+
+ emit_cmp_insn (index, expand_expr (node->high, NULL_RTX,
+ VOIDmode, 0),
+ LE, NULL_RTX, mode, unsignedp, 0);
+ emit_jump_insn ((*gen_ble_pat) (label_rtx (node->code_label)));
+
+ emit_case_nodes (index, node->right, default_label, index_type);
+ }
+
+ else if (node->right == 0 && node->left != 0)
+ {
+ /* Deal with values to the right of this node,
+ if they are possible. */
+ if (!node_has_high_bound (node, index_type))
+ {
+ emit_cmp_insn (index, expand_expr (node->high, NULL_RTX,
+ VOIDmode, 0),
+ GT, NULL_RTX, mode, unsignedp, 0);
+ emit_jump_insn ((*gen_bgt_pat) (default_label));
+ }
+
+ /* Value belongs to this node or to the left-hand subtree. */
+
+ emit_cmp_insn (index, expand_expr (node->low, NULL_RTX, VOIDmode, 0),
+ GE, NULL_RTX, mode, unsignedp, 0);
+ emit_jump_insn ((*gen_bge_pat) (label_rtx (node->code_label)));
+
+ emit_case_nodes (index, node->left, default_label, index_type);
+ }
+
+ else
+ {
+ /* Node has no children so we check low and high bounds to remove
+ redundant tests. Only one of the bounds can exist,
+ since otherwise this node is bounded--a case tested already. */
+
+ if (!node_has_high_bound (node, index_type))
+ {
+ emit_cmp_insn (index, expand_expr (node->high, NULL_RTX,
+ VOIDmode, 0),
+ GT, NULL_RTX, mode, unsignedp, 0);
+ emit_jump_insn ((*gen_bgt_pat) (default_label));
+ }
+
+ if (!node_has_low_bound (node, index_type))
+ {
+ emit_cmp_insn (index, expand_expr (node->low, NULL_RTX,
+ VOIDmode, 0),
+ LT, NULL_RTX, mode, unsignedp, 0);
+ emit_jump_insn ((*gen_blt_pat) (default_label));
+ }
+
+ emit_jump (label_rtx (node->code_label));
+ }
+ }
+}
+
+/* These routines are used by the loop unrolling code. They copy BLOCK trees
+ so that the debugging info will be correct for the unrolled loop. */
+
+/* Indexed by block number, contains a pointer to the N'th block node. */
+
+static tree *block_vector;
+
+void
+find_loop_tree_blocks ()
+{
+ tree block = DECL_INITIAL (current_function_decl);
+
+ /* There first block is for the function body, and does not have
+ corresponding block notes. Don't include it in the block vector. */
+ block = BLOCK_SUBBLOCKS (block);
+
+ block_vector = identify_blocks (block, get_insns ());
+}
+
+void
+unroll_block_trees ()
+{
+ tree block = DECL_INITIAL (current_function_decl);
+
+ reorder_blocks (block_vector, block, get_insns ());
+}
+
diff --git a/gnu/usr.bin/cc/cc_int/stor-layout.c b/gnu/usr.bin/cc/cc_int/stor-layout.c
new file mode 100644
index 0000000..d2c6f28
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/stor-layout.c
@@ -0,0 +1,1176 @@
+/* C-compiler utilities for types and variables storage layout
+ Copyright (C) 1987, 1988, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#include <stdio.h>
+
+#include "tree.h"
+#include "function.h"
+
+#define CEIL(x,y) (((x) + (y) - 1) / (y))
+
+/* Data type for the expressions representing sizes of data types.
+ It is the first integer type laid out.
+ In C, this is int. */
+
+tree sizetype;
+
+/* An integer constant with value 0 whose type is sizetype. */
+
+tree size_zero_node;
+
+/* An integer constant with value 1 whose type is sizetype. */
+
+tree size_one_node;
+
+/* If nonzero, this is an upper limit on alignment of structure fields.
+ The value is measured in bits. */
+int maximum_field_alignment;
+
+#define GET_MODE_ALIGNMENT(MODE) \
+ MIN (BIGGEST_ALIGNMENT, \
+ MAX (1, (GET_MODE_UNIT_SIZE (MODE) * BITS_PER_UNIT)))
+
+static enum machine_mode smallest_mode_for_size PROTO((unsigned int,
+ enum mode_class));
+static tree layout_record PROTO((tree));
+static void layout_union PROTO((tree));
+
+/* SAVE_EXPRs for sizes of types and decls, waiting to be expanded. */
+
+static tree pending_sizes;
+
+/* Nonzero means cannot safely call expand_expr now,
+ so put variable sizes onto `pending_sizes' instead. */
+
+int immediate_size_expand;
+
+tree
+get_pending_sizes ()
+{
+ tree chain = pending_sizes;
+ tree t;
+
+ /* Put each SAVE_EXPR into the current function. */
+ for (t = chain; t; t = TREE_CHAIN (t))
+ SAVE_EXPR_CONTEXT (TREE_VALUE (t)) = current_function_decl;
+ pending_sizes = 0;
+ return chain;
+}
+
+/* Given a size SIZE that isn't constant, return a SAVE_EXPR
+ to serve as the actual size-expression for a type or decl. */
+
+tree
+variable_size (size)
+ tree size;
+{
+ /* If the language-processor is to take responsibility for variable-sized
+ items (e.g., languages which have elaboration procedures like Ada),
+ just return SIZE unchanged. */
+ if (global_bindings_p () < 0)
+ return size;
+
+ size = save_expr (size);
+
+ if (global_bindings_p ())
+ {
+ if (TREE_CONSTANT (size))
+ error ("type size can't be explicitly evaluated");
+ else
+ error ("variable-size type declared outside of any function");
+
+ return size_int (1);
+ }
+
+ if (immediate_size_expand)
+ /* NULL_RTX is not defined; neither is the rtx type.
+ Also, we would like to pass const0_rtx here, but don't have it. */
+ expand_expr (size, expand_expr (integer_zero_node, NULL_PTR, VOIDmode, 0),
+ VOIDmode, 0);
+ else
+ pending_sizes = tree_cons (NULL_TREE, size, pending_sizes);
+
+ return size;
+}
+
+#ifndef MAX_FIXED_MODE_SIZE
+#define MAX_FIXED_MODE_SIZE GET_MODE_BITSIZE (DImode)
+#endif
+
+/* Return the machine mode to use for a nonscalar of SIZE bits.
+ The mode must be in class CLASS, and have exactly that many bits.
+ If LIMIT is nonzero, modes of wider than MAX_FIXED_MODE_SIZE will not
+ be used. */
+
+enum machine_mode
+mode_for_size (size, class, limit)
+ unsigned int size;
+ enum mode_class class;
+ int limit;
+{
+ register enum machine_mode mode;
+
+ if (limit && size > MAX_FIXED_MODE_SIZE)
+ return BLKmode;
+
+ /* Get the first mode which has this size, in the specified class. */
+ for (mode = GET_CLASS_NARROWEST_MODE (class); mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ if (GET_MODE_BITSIZE (mode) == size)
+ return mode;
+
+ return BLKmode;
+}
+
+/* Similar, but never return BLKmode; return the narrowest mode that
+ contains at least the requested number of bits. */
+
+static enum machine_mode
+smallest_mode_for_size (size, class)
+ unsigned int size;
+ enum mode_class class;
+{
+ register enum machine_mode mode;
+
+ /* Get the first mode which has at least this size, in the
+ specified class. */
+ for (mode = GET_CLASS_NARROWEST_MODE (class); mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ if (GET_MODE_BITSIZE (mode) >= size)
+ return mode;
+
+ abort ();
+}
+
+/* Return the value of VALUE, rounded up to a multiple of DIVISOR. */
+
+tree
+round_up (value, divisor)
+ tree value;
+ int divisor;
+{
+ return size_binop (MULT_EXPR,
+ size_binop (CEIL_DIV_EXPR, value, size_int (divisor)),
+ size_int (divisor));
+}
+
+/* Set the size, mode and alignment of a ..._DECL node.
+ TYPE_DECL does need this for C++.
+ Note that LABEL_DECL and CONST_DECL nodes do not need this,
+ and FUNCTION_DECL nodes have them set up in a special (and simple) way.
+ Don't call layout_decl for them.
+
+ KNOWN_ALIGN is the amount of alignment we can assume this
+ decl has with no special effort. It is relevant only for FIELD_DECLs
+ and depends on the previous fields.
+ All that matters about KNOWN_ALIGN is which powers of 2 divide it.
+ If KNOWN_ALIGN is 0, it means, "as much alignment as you like":
+ the record will be aligned to suit. */
+
+void
+layout_decl (decl, known_align)
+ tree decl;
+ unsigned known_align;
+{
+ register tree type = TREE_TYPE (decl);
+ register enum tree_code code = TREE_CODE (decl);
+ int spec_size = DECL_FIELD_SIZE (decl);
+
+ if (code == CONST_DECL)
+ return;
+
+ if (code != VAR_DECL && code != PARM_DECL && code != RESULT_DECL
+ && code != FIELD_DECL && code != TYPE_DECL)
+ abort ();
+
+ if (type == error_mark_node)
+ {
+ type = void_type_node;
+ spec_size = 0;
+ }
+
+ /* Usually the size and mode come from the data type without change. */
+
+ DECL_MODE (decl) = TYPE_MODE (type);
+ TREE_UNSIGNED (decl) = TREE_UNSIGNED (type);
+ if (DECL_SIZE (decl) == 0)
+ DECL_SIZE (decl) = TYPE_SIZE (type);
+
+ if (code == FIELD_DECL && DECL_BIT_FIELD (decl))
+ {
+ /* This is a bit-field. We don't know how to handle
+ them except for integral types, and front ends should
+ never generate them otherwise. */
+
+ if (! INTEGRAL_TYPE_P (type))
+ abort ();
+
+ if (spec_size == 0 && DECL_NAME (decl) != 0)
+ abort ();
+
+ /* Size is specified number of bits. */
+ DECL_SIZE (decl) = size_int (spec_size);
+ }
+ /* Force alignment required for the data type.
+ But if the decl itself wants greater alignment, don't override that.
+ Likewise, if the decl is packed, don't override it. */
+ else if (DECL_ALIGN (decl) == 0
+ || (! DECL_PACKED (decl) && TYPE_ALIGN (type) > DECL_ALIGN (decl)))
+ DECL_ALIGN (decl) = TYPE_ALIGN (type);
+
+ /* See if we can use an ordinary integer mode for a bit-field. */
+ /* Conditions are: a fixed size that is correct for another mode
+ and occupying a complete byte or bytes on proper boundary. */
+ if (code == FIELD_DECL)
+ {
+ DECL_BIT_FIELD_TYPE (decl) = DECL_BIT_FIELD (decl) ? type : 0;
+ if (maximum_field_alignment != 0)
+ DECL_ALIGN (decl) = MIN (DECL_ALIGN (decl), maximum_field_alignment);
+ }
+
+ if (DECL_BIT_FIELD (decl)
+ && TYPE_SIZE (type) != 0
+ && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST)
+ {
+ register enum machine_mode xmode
+ = mode_for_size (TREE_INT_CST_LOW (DECL_SIZE (decl)), MODE_INT, 1);
+
+ if (xmode != BLKmode
+ && known_align % GET_MODE_ALIGNMENT (xmode) == 0)
+ {
+ DECL_ALIGN (decl) = MAX (GET_MODE_ALIGNMENT (xmode),
+ DECL_ALIGN (decl));
+ DECL_MODE (decl) = xmode;
+ DECL_SIZE (decl) = size_int (GET_MODE_BITSIZE (xmode));
+ /* This no longer needs to be accessed as a bit field. */
+ DECL_BIT_FIELD (decl) = 0;
+ }
+ }
+
+ /* Evaluate nonconstant size only once, either now or as soon as safe. */
+ if (DECL_SIZE (decl) != 0 && TREE_CODE (DECL_SIZE (decl)) != INTEGER_CST)
+ DECL_SIZE (decl) = variable_size (DECL_SIZE (decl));
+}
+
+/* Lay out a RECORD_TYPE type (a C struct).
+ This means laying out the fields, determining their positions,
+ and computing the overall size and required alignment of the record.
+ Note that if you set the TYPE_ALIGN before calling this
+ then the struct is aligned to at least that boundary.
+
+ If the type has basetypes, you must call layout_basetypes
+ before calling this function.
+
+ The return value is a list of static members of the record.
+ They still need to be laid out. */
+
+static tree
+layout_record (rec)
+ tree rec;
+{
+ register tree field;
+#ifdef STRUCTURE_SIZE_BOUNDARY
+ unsigned record_align = MAX (STRUCTURE_SIZE_BOUNDARY, TYPE_ALIGN (rec));
+#else
+ unsigned record_align = MAX (BITS_PER_UNIT, TYPE_ALIGN (rec));
+#endif
+ /* These must be laid out *after* the record is. */
+ tree pending_statics = NULL_TREE;
+ /* Record size so far is CONST_SIZE + VAR_SIZE bits,
+ where CONST_SIZE is an integer
+ and VAR_SIZE is a tree expression.
+ If VAR_SIZE is null, the size is just CONST_SIZE.
+ Naturally we try to avoid using VAR_SIZE. */
+ register int const_size = 0;
+ register tree var_size = 0;
+ /* Once we start using VAR_SIZE, this is the maximum alignment
+ that we know VAR_SIZE has. */
+ register int var_align = BITS_PER_UNIT;
+
+
+ for (field = TYPE_FIELDS (rec); field; field = TREE_CHAIN (field))
+ {
+ register int known_align = var_size ? var_align : const_size;
+ register int desired_align;
+
+ /* If FIELD is static, then treat it like a separate variable,
+ not really like a structure field.
+ If it is a FUNCTION_DECL, it's a method.
+ In both cases, all we do is lay out the decl,
+ and we do it *after* the record is laid out. */
+
+ if (TREE_STATIC (field))
+ {
+ pending_statics = tree_cons (NULL_TREE, field, pending_statics);
+ continue;
+ }
+ /* Enumerators and enum types which are local to this class need not
+ be laid out. Likewise for initialized constant fields. */
+ if (TREE_CODE (field) != FIELD_DECL)
+ continue;
+
+ /* Lay out the field so we know what alignment it needs.
+ For a packed field, use the alignment as specified,
+ disregarding what the type would want. */
+ if (DECL_PACKED (field))
+ desired_align = DECL_ALIGN (field);
+ layout_decl (field, known_align);
+ if (! DECL_PACKED (field))
+ desired_align = DECL_ALIGN (field);
+ /* Some targets (i.e. VMS) limit struct field alignment
+ to a lower boundary than alignment of variables. */
+#ifdef BIGGEST_FIELD_ALIGNMENT
+ desired_align = MIN (desired_align, BIGGEST_FIELD_ALIGNMENT);
+#endif
+
+ /* Record must have at least as much alignment as any field.
+ Otherwise, the alignment of the field within the record
+ is meaningless. */
+
+#ifndef PCC_BITFIELD_TYPE_MATTERS
+ record_align = MAX (record_align, desired_align);
+#else
+ if (PCC_BITFIELD_TYPE_MATTERS && TREE_TYPE (field) != error_mark_node
+ && DECL_BIT_FIELD_TYPE (field)
+ && ! integer_zerop (TYPE_SIZE (TREE_TYPE (field))))
+ {
+ /* For these machines, a zero-length field does not
+ affect the alignment of the structure as a whole.
+ It does, however, affect the alignment of the next field
+ within the structure. */
+ if (! integer_zerop (DECL_SIZE (field)))
+ record_align = MAX (record_align, desired_align);
+ else if (! DECL_PACKED (field))
+ desired_align = TYPE_ALIGN (TREE_TYPE (field));
+ /* A named bit field of declared type `int'
+ forces the entire structure to have `int' alignment. */
+ if (DECL_NAME (field) != 0)
+ {
+ int type_align = TYPE_ALIGN (TREE_TYPE (field));
+ if (maximum_field_alignment != 0)
+ type_align = MIN (type_align, maximum_field_alignment);
+
+ record_align = MAX (record_align, type_align);
+ }
+ }
+ else
+ record_align = MAX (record_align, desired_align);
+#endif
+
+ /* Does this field automatically have alignment it needs
+ by virtue of the fields that precede it and the record's
+ own alignment? */
+
+ if (const_size % desired_align != 0
+ || (var_align % desired_align != 0
+ && var_size != 0))
+ {
+ /* No, we need to skip space before this field.
+ Bump the cumulative size to multiple of field alignment. */
+
+ if (var_size == 0
+ || var_align % desired_align == 0)
+ const_size
+ = CEIL (const_size, desired_align) * desired_align;
+ else
+ {
+ if (const_size > 0)
+ var_size = size_binop (PLUS_EXPR, var_size,
+ size_int (const_size));
+ const_size = 0;
+ var_size = round_up (var_size, desired_align);
+ var_align = MIN (var_align, desired_align);
+ }
+ }
+
+#ifdef PCC_BITFIELD_TYPE_MATTERS
+ if (PCC_BITFIELD_TYPE_MATTERS
+ && TREE_CODE (field) == FIELD_DECL
+ && TREE_TYPE (field) != error_mark_node
+ && DECL_BIT_FIELD_TYPE (field)
+ && !DECL_PACKED (field)
+ /* If #pragma pack is in effect, turn off this feature. */
+ && maximum_field_alignment == 0
+ && !integer_zerop (DECL_SIZE (field)))
+ {
+ int type_align = TYPE_ALIGN (TREE_TYPE (field));
+ register tree dsize = DECL_SIZE (field);
+ int field_size = TREE_INT_CST_LOW (dsize);
+
+ /* A bit field may not span the unit of alignment of its type.
+ Advance to next boundary if necessary. */
+ /* ??? There is some uncertainty here as to what
+ should be done if type_align is less than the width of the type.
+ That can happen because the width exceeds BIGGEST_ALIGNMENT
+ or because it exceeds maximum_field_alignment. */
+ if (const_size / type_align
+ != (const_size + field_size - 1) / type_align)
+ const_size = CEIL (const_size, type_align) * type_align;
+ }
+#endif
+
+/* No existing machine description uses this parameter.
+ So I have made it in this aspect identical to PCC_BITFIELD_TYPE_MATTERS. */
+#ifdef BITFIELD_NBYTES_LIMITED
+ if (BITFIELD_NBYTES_LIMITED
+ && TREE_CODE (field) == FIELD_DECL
+ && TREE_TYPE (field) != error_mark_node
+ && DECL_BIT_FIELD_TYPE (field)
+ && !DECL_PACKED (field)
+ && !integer_zerop (DECL_SIZE (field)))
+ {
+ int type_align = TYPE_ALIGN (TREE_TYPE (field));
+ register tree dsize = DECL_SIZE (field);
+ int field_size = TREE_INT_CST_LOW (dsize);
+
+ if (maximum_field_alignment != 0)
+ type_align = MIN (type_align, maximum_field_alignment);
+
+ /* A bit field may not span the unit of alignment of its type.
+ Advance to next boundary if necessary. */
+ if (const_size / type_align
+ != (const_size + field_size - 1) / type_align)
+ const_size = CEIL (const_size, type_align) * type_align;
+ }
+#endif
+
+ /* Size so far becomes the position of this field. */
+
+ if (var_size && const_size)
+ DECL_FIELD_BITPOS (field)
+ = size_binop (PLUS_EXPR, var_size, size_int (const_size));
+ else if (var_size)
+ DECL_FIELD_BITPOS (field) = var_size;
+ else
+ {
+ DECL_FIELD_BITPOS (field) = size_int (const_size);
+
+ /* If this field ended up more aligned than we thought it
+ would be (we approximate this by seeing if its position
+ changed), lay out the field again; perhaps we can use an
+ integral mode for it now. */
+ if (known_align != const_size)
+ layout_decl (field, const_size);
+ }
+
+ /* Now add size of this field to the size of the record. */
+
+ {
+ register tree dsize = DECL_SIZE (field);
+
+ /* This can happen when we have an invalid nested struct definition,
+ such as struct j { struct j { int i; } }. The error message is
+ printed in finish_struct. */
+ if (dsize == 0)
+ /* Do nothing. */;
+ else if (TREE_CODE (dsize) == INTEGER_CST
+ && TREE_INT_CST_HIGH (dsize) == 0
+ && TREE_INT_CST_LOW (dsize) + const_size > const_size)
+ /* Use const_size if there's no overflow. */
+ const_size += TREE_INT_CST_LOW (dsize);
+ else
+ {
+ if (var_size == 0)
+ var_size = dsize;
+ else
+ var_size = size_binop (PLUS_EXPR, var_size, dsize);
+ }
+ }
+ }
+
+ /* Work out the total size and alignment of the record
+ as one expression and store in the record type.
+ Round it up to a multiple of the record's alignment. */
+
+ if (var_size == 0)
+ {
+ TYPE_SIZE (rec) = size_int (const_size);
+ }
+ else
+ {
+ if (const_size)
+ var_size
+ = size_binop (PLUS_EXPR, var_size, size_int (const_size));
+ TYPE_SIZE (rec) = var_size;
+ }
+
+ /* Determine the desired alignment. */
+#ifdef ROUND_TYPE_ALIGN
+ TYPE_ALIGN (rec) = ROUND_TYPE_ALIGN (rec, TYPE_ALIGN (rec), record_align);
+#else
+ TYPE_ALIGN (rec) = MAX (TYPE_ALIGN (rec), record_align);
+#endif
+
+#ifdef ROUND_TYPE_SIZE
+ TYPE_SIZE (rec) = ROUND_TYPE_SIZE (rec, TYPE_SIZE (rec), TYPE_ALIGN (rec));
+#else
+ /* Round the size up to be a multiple of the required alignment */
+ TYPE_SIZE (rec) = round_up (TYPE_SIZE (rec), TYPE_ALIGN (rec));
+#endif
+
+ return pending_statics;
+}
+
+/* Lay out a UNION_TYPE or QUAL_UNION_TYPE type.
+ Lay out all the fields, set their positions to zero,
+ and compute the size and alignment of the union (maximum of any field).
+ Note that if you set the TYPE_ALIGN before calling this
+ then the union align is aligned to at least that boundary. */
+
+static void
+layout_union (rec)
+ tree rec;
+{
+ register tree field;
+#ifdef STRUCTURE_SIZE_BOUNDARY
+ unsigned union_align = STRUCTURE_SIZE_BOUNDARY;
+#else
+ unsigned union_align = BITS_PER_UNIT;
+#endif
+
+ /* The size of the union, based on the fields scanned so far,
+ is max (CONST_SIZE, VAR_SIZE).
+ VAR_SIZE may be null; then CONST_SIZE by itself is the size. */
+ register int const_size = 0;
+ register tree var_size = 0;
+
+ /* If this is a QUAL_UNION_TYPE, we want to process the fields in
+ the reverse order in building the COND_EXPR that denotes its
+ size. We reverse them again later. */
+ if (TREE_CODE (rec) == QUAL_UNION_TYPE)
+ TYPE_FIELDS (rec) = nreverse (TYPE_FIELDS (rec));
+
+ for (field = TYPE_FIELDS (rec); field; field = TREE_CHAIN (field))
+ {
+ /* Enums which are local to this class need not be laid out. */
+ if (TREE_CODE (field) == CONST_DECL || TREE_CODE (field) == TYPE_DECL)
+ continue;
+
+ layout_decl (field, 0);
+ DECL_FIELD_BITPOS (field) = size_int (0);
+
+ /* Union must be at least as aligned as any field requires. */
+
+ union_align = MAX (union_align, DECL_ALIGN (field));
+
+#ifdef PCC_BITFIELD_TYPE_MATTERS
+ /* On the m88000, a bit field of declare type `int'
+ forces the entire union to have `int' alignment. */
+ if (PCC_BITFIELD_TYPE_MATTERS && DECL_BIT_FIELD_TYPE (field))
+ union_align = MAX (union_align, TYPE_ALIGN (TREE_TYPE (field)));
+#endif
+
+ if (TREE_CODE (rec) == UNION_TYPE)
+ {
+ /* Set union_size to max (decl_size, union_size).
+ There are more and less general ways to do this.
+ Use only CONST_SIZE unless forced to use VAR_SIZE. */
+
+ if (TREE_CODE (DECL_SIZE (field)) == INTEGER_CST)
+ const_size
+ = MAX (const_size, TREE_INT_CST_LOW (DECL_SIZE (field)));
+ else if (var_size == 0)
+ var_size = DECL_SIZE (field);
+ else
+ var_size = size_binop (MAX_EXPR, var_size, DECL_SIZE (field));
+ }
+ else if (TREE_CODE (rec) == QUAL_UNION_TYPE)
+ var_size = fold (build (COND_EXPR, sizetype, DECL_QUALIFIER (field),
+ DECL_SIZE (field),
+ var_size ? var_size : integer_zero_node));
+ }
+
+ if (TREE_CODE (rec) == QUAL_UNION_TYPE)
+ TYPE_FIELDS (rec) = nreverse (TYPE_FIELDS (rec));
+
+ /* Determine the ultimate size of the union (in bytes). */
+ if (NULL == var_size)
+ TYPE_SIZE (rec) = size_int (CEIL (const_size, BITS_PER_UNIT)
+ * BITS_PER_UNIT);
+ else if (const_size == 0)
+ TYPE_SIZE (rec) = var_size;
+ else
+ TYPE_SIZE (rec) = size_binop (MAX_EXPR, var_size,
+ round_up (size_int (const_size),
+ BITS_PER_UNIT));
+
+ /* Determine the desired alignment. */
+#ifdef ROUND_TYPE_ALIGN
+ TYPE_ALIGN (rec) = ROUND_TYPE_ALIGN (rec, TYPE_ALIGN (rec), union_align);
+#else
+ TYPE_ALIGN (rec) = MAX (TYPE_ALIGN (rec), union_align);
+#endif
+
+#ifdef ROUND_TYPE_SIZE
+ TYPE_SIZE (rec) = ROUND_TYPE_SIZE (rec, TYPE_SIZE (rec), TYPE_ALIGN (rec));
+#else
+ /* Round the size up to be a multiple of the required alignment */
+ TYPE_SIZE (rec) = round_up (TYPE_SIZE (rec), TYPE_ALIGN (rec));
+#endif
+}
+
+/* Calculate the mode, size, and alignment for TYPE.
+ For an array type, calculate the element separation as well.
+ Record TYPE on the chain of permanent or temporary types
+ so that dbxout will find out about it.
+
+ TYPE_SIZE of a type is nonzero if the type has been laid out already.
+ layout_type does nothing on such a type.
+
+ If the type is incomplete, its TYPE_SIZE remains zero. */
+
+void
+layout_type (type)
+ tree type;
+{
+ int old;
+ tree pending_statics;
+
+ if (type == 0)
+ abort ();
+
+ /* Do nothing if type has been laid out before. */
+ if (TYPE_SIZE (type))
+ return;
+
+ /* Make sure all nodes we allocate are not momentary;
+ they must last past the current statement. */
+ old = suspend_momentary ();
+
+ /* Put all our nodes into the same obstack as the type. Also,
+ make expressions saveable (this is a no-op for permanent types). */
+
+ push_obstacks (TYPE_OBSTACK (type), TYPE_OBSTACK (type));
+ saveable_allocation ();
+
+ switch (TREE_CODE (type))
+ {
+ case LANG_TYPE:
+ /* This kind of type is the responsibility
+ of the languge-specific code. */
+ abort ();
+
+ case INTEGER_TYPE:
+ case ENUMERAL_TYPE:
+ if (TREE_CODE (TYPE_MIN_VALUE (type)) == INTEGER_CST
+ && tree_int_cst_sgn (TYPE_MIN_VALUE (type)) >= 0)
+ TREE_UNSIGNED (type) = 1;
+
+ TYPE_MODE (type) = smallest_mode_for_size (TYPE_PRECISION (type),
+ MODE_INT);
+ TYPE_SIZE (type) = size_int (GET_MODE_BITSIZE (TYPE_MODE (type)));
+ break;
+
+ case REAL_TYPE:
+ TYPE_MODE (type) = mode_for_size (TYPE_PRECISION (type), MODE_FLOAT, 0);
+ TYPE_SIZE (type) = size_int (GET_MODE_BITSIZE (TYPE_MODE (type)));
+ break;
+
+ case COMPLEX_TYPE:
+ TREE_UNSIGNED (type) = TREE_UNSIGNED (TREE_TYPE (type));
+ TYPE_MODE (type)
+ = mode_for_size (2 * TYPE_PRECISION (TREE_TYPE (type)),
+ (TREE_CODE (TREE_TYPE (type)) == INTEGER_TYPE
+ ? MODE_COMPLEX_INT : MODE_COMPLEX_FLOAT),
+ 0);
+ TYPE_SIZE (type) = size_int (GET_MODE_BITSIZE (TYPE_MODE (type)));
+ break;
+
+ case VOID_TYPE:
+ TYPE_SIZE (type) = size_zero_node;
+ TYPE_ALIGN (type) = 1;
+ TYPE_MODE (type) = VOIDmode;
+ break;
+
+ case OFFSET_TYPE:
+ TYPE_SIZE (type) = size_int (POINTER_SIZE);
+ TYPE_MODE (type) = mode_for_size (POINTER_SIZE,
+ GET_MODE_CLASS (Pmode), 0);
+ break;
+
+ case FUNCTION_TYPE:
+ case METHOD_TYPE:
+ TYPE_MODE (type) = mode_for_size (2 * POINTER_SIZE, MODE_INT, 0);
+ TYPE_SIZE (type) = size_int (2 * POINTER_SIZE);
+ break;
+
+ case POINTER_TYPE:
+ case REFERENCE_TYPE:
+ TYPE_MODE (type) = mode_for_size (POINTER_SIZE,
+ GET_MODE_CLASS (Pmode), 0);
+ TYPE_SIZE (type) = size_int (POINTER_SIZE);
+ TREE_UNSIGNED (type) = 1;
+ TYPE_PRECISION (type) = POINTER_SIZE;
+ break;
+
+ case ARRAY_TYPE:
+ {
+ register tree index = TYPE_DOMAIN (type);
+ register tree element = TREE_TYPE (type);
+
+ build_pointer_type (element);
+
+ /* We need to know both bounds in order to compute the size. */
+ if (index && TYPE_MAX_VALUE (index) && TYPE_MIN_VALUE (index)
+ && TYPE_SIZE (element))
+ {
+ tree length
+ = size_binop (PLUS_EXPR, size_one_node,
+ size_binop (MINUS_EXPR, TYPE_MAX_VALUE (index),
+ TYPE_MIN_VALUE (index)));
+
+ TYPE_SIZE (type) = size_binop (MULT_EXPR, length,
+ TYPE_SIZE (element));
+ }
+
+ /* Now round the alignment and size,
+ using machine-dependent criteria if any. */
+
+#ifdef ROUND_TYPE_ALIGN
+ TYPE_ALIGN (type)
+ = ROUND_TYPE_ALIGN (type, TYPE_ALIGN (element), BITS_PER_UNIT);
+#else
+ TYPE_ALIGN (type) = MAX (TYPE_ALIGN (element), BITS_PER_UNIT);
+#endif
+
+#ifdef ROUND_TYPE_SIZE
+ if (TYPE_SIZE (type) != 0)
+ TYPE_SIZE (type)
+ = ROUND_TYPE_SIZE (type, TYPE_SIZE (type), TYPE_ALIGN (type));
+#endif
+
+ TYPE_MODE (type) = BLKmode;
+ if (TYPE_SIZE (type) != 0
+ && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
+ /* BLKmode elements force BLKmode aggregate;
+ else extract/store fields may lose. */
+ && (TYPE_MODE (TREE_TYPE (type)) != BLKmode
+ || TYPE_NO_FORCE_BLK (TREE_TYPE (type))))
+ {
+ TYPE_MODE (type)
+ = mode_for_size (TREE_INT_CST_LOW (TYPE_SIZE (type)),
+ MODE_INT, 1);
+
+ if (STRICT_ALIGNMENT && TYPE_ALIGN (type) < BIGGEST_ALIGNMENT
+ && TYPE_ALIGN (type) < TREE_INT_CST_LOW (TYPE_SIZE (type))
+ && TYPE_MODE (type) != BLKmode)
+ {
+ TYPE_NO_FORCE_BLK (type) = 1;
+ TYPE_MODE (type) = BLKmode;
+ }
+ }
+ break;
+ }
+
+ case RECORD_TYPE:
+ pending_statics = layout_record (type);
+ TYPE_MODE (type) = BLKmode;
+ if (TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST)
+ {
+ tree field;
+ /* A record which has any BLKmode members must itself be BLKmode;
+ it can't go in a register.
+ Unless the member is BLKmode only because it isn't aligned. */
+ for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+ {
+ int bitpos;
+
+ if (TREE_CODE (field) != FIELD_DECL)
+ continue;
+
+ if (TYPE_MODE (TREE_TYPE (field)) == BLKmode
+ && ! TYPE_NO_FORCE_BLK (TREE_TYPE (field)))
+ goto record_lose;
+
+ if (TREE_CODE (DECL_FIELD_BITPOS (field)) != INTEGER_CST)
+ goto record_lose;
+
+ bitpos = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field));
+
+ /* Must be BLKmode if any field crosses a word boundary,
+ since extract_bit_field can't handle that in registers. */
+ if (bitpos / BITS_PER_WORD
+ != ((TREE_INT_CST_LOW (DECL_SIZE (field)) + bitpos - 1)
+ / BITS_PER_WORD)
+ /* But there is no problem if the field is entire words. */
+ && TREE_INT_CST_LOW (DECL_SIZE (field)) % BITS_PER_WORD == 0)
+ goto record_lose;
+ }
+
+ TYPE_MODE (type)
+ = mode_for_size (TREE_INT_CST_LOW (TYPE_SIZE (type)),
+ MODE_INT, 1);
+
+ /* If structure's known alignment is less than
+ what the scalar mode would need, and it matters,
+ then stick with BLKmode. */
+ if (STRICT_ALIGNMENT
+ && ! (TYPE_ALIGN (type) >= BIGGEST_ALIGNMENT
+ || (TYPE_ALIGN (type)
+ >= TREE_INT_CST_LOW (TYPE_SIZE (type)))))
+ {
+ if (TYPE_MODE (type) != BLKmode)
+ /* If this is the only reason this type is BLKmode,
+ then don't force containing types to be BLKmode. */
+ TYPE_NO_FORCE_BLK (type) = 1;
+ TYPE_MODE (type) = BLKmode;
+ }
+
+ record_lose: ;
+ }
+
+ /* Lay out any static members. This is done now
+ because their type may use the record's type. */
+ while (pending_statics)
+ {
+ layout_decl (TREE_VALUE (pending_statics), 0);
+ pending_statics = TREE_CHAIN (pending_statics);
+ }
+ break;
+
+ case UNION_TYPE:
+ case QUAL_UNION_TYPE:
+ layout_union (type);
+ TYPE_MODE (type) = BLKmode;
+ if (TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
+ /* If structure's known alignment is less than
+ what the scalar mode would need, and it matters,
+ then stick with BLKmode. */
+ && (! STRICT_ALIGNMENT
+ || TYPE_ALIGN (type) >= BIGGEST_ALIGNMENT
+ || TYPE_ALIGN (type) >= TREE_INT_CST_LOW (TYPE_SIZE (type))))
+ {
+ tree field;
+ /* A union which has any BLKmode members must itself be BLKmode;
+ it can't go in a register.
+ Unless the member is BLKmode only because it isn't aligned. */
+ for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+ {
+ if (TREE_CODE (field) != FIELD_DECL)
+ continue;
+
+ if (TYPE_MODE (TREE_TYPE (field)) == BLKmode
+ && ! TYPE_NO_FORCE_BLK (TREE_TYPE (field)))
+ goto union_lose;
+ }
+
+ TYPE_MODE (type)
+ = mode_for_size (TREE_INT_CST_LOW (TYPE_SIZE (type)),
+ MODE_INT, 1);
+
+ union_lose: ;
+ }
+ break;
+
+ /* Pascal and Chill types */
+ case BOOLEAN_TYPE: /* store one byte/boolean for now. */
+ TYPE_MODE (type) = QImode;
+ TYPE_SIZE (type) = size_int (GET_MODE_BITSIZE (TYPE_MODE (type)));
+ TYPE_PRECISION (type) = 1;
+ TYPE_ALIGN (type) = GET_MODE_ALIGNMENT (TYPE_MODE (type));
+ if (TREE_CODE (TYPE_MIN_VALUE (type)) == INTEGER_CST
+ && tree_int_cst_sgn (TYPE_MIN_VALUE (type)) >= 0)
+ TREE_UNSIGNED (type) = 1;
+ break;
+
+ case CHAR_TYPE:
+ TYPE_MODE (type) = QImode;
+ TYPE_SIZE (type) = size_int (GET_MODE_BITSIZE (TYPE_MODE (type)));
+ TYPE_PRECISION (type) = GET_MODE_BITSIZE (TYPE_MODE (type));
+ TYPE_ALIGN (type) = GET_MODE_ALIGNMENT (TYPE_MODE (type));
+ break;
+
+ case FILE_TYPE:
+ /* The size may vary in different languages, so the language front end
+ should fill in the size. */
+ TYPE_ALIGN (type) = BIGGEST_ALIGNMENT;
+ TYPE_MODE (type) = BLKmode;
+ break;
+
+ default:
+ abort ();
+ } /* end switch */
+
+ /* Normally, use the alignment corresponding to the mode chosen.
+ However, where strict alignment is not required, avoid
+ over-aligning structures, since most compilers do not do this
+ alignment. */
+
+ if (TYPE_MODE (type) != BLKmode && TYPE_MODE (type) != VOIDmode
+ && (STRICT_ALIGNMENT
+ || (TREE_CODE (type) != RECORD_TYPE && TREE_CODE (type) != UNION_TYPE
+ && TREE_CODE (type) != QUAL_UNION_TYPE
+ && TREE_CODE (type) != ARRAY_TYPE)))
+ TYPE_ALIGN (type) = GET_MODE_ALIGNMENT (TYPE_MODE (type));
+
+ /* Evaluate nonconstant size only once, either now or as soon as safe. */
+ if (TYPE_SIZE (type) != 0 && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+ TYPE_SIZE (type) = variable_size (TYPE_SIZE (type));
+
+ /* Also layout any other variants of the type. */
+ if (TYPE_NEXT_VARIANT (type)
+ || type != TYPE_MAIN_VARIANT (type))
+ {
+ tree variant;
+ /* Record layout info of this variant. */
+ tree size = TYPE_SIZE (type);
+ int align = TYPE_ALIGN (type);
+ enum machine_mode mode = TYPE_MODE (type);
+
+ /* Copy it into all variants. */
+ for (variant = TYPE_MAIN_VARIANT (type);
+ variant;
+ variant = TYPE_NEXT_VARIANT (variant))
+ {
+ TYPE_SIZE (variant) = size;
+ TYPE_ALIGN (variant) = align;
+ TYPE_MODE (variant) = mode;
+ }
+ }
+
+ pop_obstacks ();
+ resume_momentary (old);
+}
+
+/* Create and return a type for signed integers of PRECISION bits. */
+
+tree
+make_signed_type (precision)
+ int precision;
+{
+ register tree type = make_node (INTEGER_TYPE);
+
+ TYPE_PRECISION (type) = precision;
+
+ /* Create the extreme values based on the number of bits. */
+
+ TYPE_MIN_VALUE (type)
+ = build_int_2 ((precision - HOST_BITS_PER_WIDE_INT > 0
+ ? 0 : (HOST_WIDE_INT) (-1) << (precision - 1)),
+ (((HOST_WIDE_INT) (-1)
+ << (precision - HOST_BITS_PER_WIDE_INT - 1 > 0
+ ? precision - HOST_BITS_PER_WIDE_INT - 1
+ : 0))));
+ TYPE_MAX_VALUE (type)
+ = build_int_2 ((precision - HOST_BITS_PER_WIDE_INT > 0
+ ? -1 : ((HOST_WIDE_INT) 1 << (precision - 1)) - 1),
+ (precision - HOST_BITS_PER_WIDE_INT - 1 > 0
+ ? (((HOST_WIDE_INT) 1
+ << (precision - HOST_BITS_PER_WIDE_INT - 1))) - 1
+ : 0));
+
+ /* Give this type's extreme values this type as their type. */
+
+ TREE_TYPE (TYPE_MIN_VALUE (type)) = type;
+ TREE_TYPE (TYPE_MAX_VALUE (type)) = type;
+
+ /* The first type made with this or `make_unsigned_type'
+ is the type for size values. */
+
+ if (sizetype == 0)
+ {
+ sizetype = type;
+ }
+
+ /* Lay out the type: set its alignment, size, etc. */
+
+ layout_type (type);
+
+ return type;
+}
+
+/* Create and return a type for unsigned integers of PRECISION bits. */
+
+tree
+make_unsigned_type (precision)
+ int precision;
+{
+ register tree type = make_node (INTEGER_TYPE);
+
+ TYPE_PRECISION (type) = precision;
+
+ /* The first type made with this or `make_signed_type'
+ is the type for size values. */
+
+ if (sizetype == 0)
+ {
+ sizetype = type;
+ }
+
+ fixup_unsigned_type (type);
+ return type;
+}
+
+/* Set the extreme values of TYPE based on its precision in bits,
+ then lay it out. Used when make_signed_type won't do
+ because the tree code is not INTEGER_TYPE.
+ E.g. for Pascal, when the -fsigned-char option is given. */
+
+void
+fixup_signed_type (type)
+ tree type;
+{
+ register int precision = TYPE_PRECISION (type);
+
+ TYPE_MIN_VALUE (type)
+ = build_int_2 ((precision - HOST_BITS_PER_WIDE_INT > 0
+ ? 0 : (HOST_WIDE_INT) (-1) << (precision - 1)),
+ (((HOST_WIDE_INT) (-1)
+ << (precision - HOST_BITS_PER_WIDE_INT - 1 > 0
+ ? precision - HOST_BITS_PER_WIDE_INT - 1
+ : 0))));
+ TYPE_MAX_VALUE (type)
+ = build_int_2 ((precision - HOST_BITS_PER_WIDE_INT > 0
+ ? -1 : ((HOST_WIDE_INT) 1 << (precision - 1)) - 1),
+ (precision - HOST_BITS_PER_WIDE_INT - 1 > 0
+ ? (((HOST_WIDE_INT) 1
+ << (precision - HOST_BITS_PER_WIDE_INT - 1))) - 1
+ : 0));
+
+ TREE_TYPE (TYPE_MIN_VALUE (type)) = type;
+ TREE_TYPE (TYPE_MAX_VALUE (type)) = type;
+
+ /* Lay out the type: set its alignment, size, etc. */
+
+ layout_type (type);
+}
+
+/* Set the extreme values of TYPE based on its precision in bits,
+ then lay it out. This is used both in `make_unsigned_type'
+ and for enumeral types. */
+
+void
+fixup_unsigned_type (type)
+ tree type;
+{
+ register int precision = TYPE_PRECISION (type);
+
+ TYPE_MIN_VALUE (type) = build_int_2 (0, 0);
+ TYPE_MAX_VALUE (type)
+ = build_int_2 (precision - HOST_BITS_PER_WIDE_INT >= 0
+ ? -1 : ((HOST_WIDE_INT) 1 << precision) - 1,
+ precision - HOST_BITS_PER_WIDE_INT > 0
+ ? ((unsigned HOST_WIDE_INT) ~0
+ >> (HOST_BITS_PER_WIDE_INT
+ - (precision - HOST_BITS_PER_WIDE_INT)))
+ : 0);
+ TREE_TYPE (TYPE_MIN_VALUE (type)) = type;
+ TREE_TYPE (TYPE_MAX_VALUE (type)) = type;
+
+ /* Lay out the type: set its alignment, size, etc. */
+
+ layout_type (type);
+}
+
+/* Find the best machine mode to use when referencing a bit field of length
+ BITSIZE bits starting at BITPOS.
+
+ The underlying object is known to be aligned to a boundary of ALIGN bits.
+ If LARGEST_MODE is not VOIDmode, it means that we should not use a mode
+ larger than LARGEST_MODE (usually SImode).
+
+ If no mode meets all these conditions, we return VOIDmode. Otherwise, if
+ VOLATILEP is true or SLOW_BYTE_ACCESS is false, we return the smallest
+ mode meeting these conditions.
+
+ Otherwise (VOLATILEP is false and SLOW_BYTE_ACCESS is true), we return
+ the largest mode (but a mode no wider than UNITS_PER_WORD) that meets
+ all the conditions. */
+
+enum machine_mode
+get_best_mode (bitsize, bitpos, align, largest_mode, volatilep)
+ int bitsize, bitpos;
+ int align;
+ enum machine_mode largest_mode;
+ int volatilep;
+{
+ enum machine_mode mode;
+ int unit;
+
+ /* Find the narrowest integer mode that contains the bit field. */
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ {
+ unit = GET_MODE_BITSIZE (mode);
+ if (bitpos / unit == (bitpos + bitsize - 1) / unit)
+ break;
+ }
+
+ if (mode == MAX_MACHINE_MODE
+ /* It is tempting to omit the following line
+ if STRICT_ALIGNMENT is true.
+ But that is incorrect, since if the bitfield uses part of 3 bytes
+ and we use a 4-byte mode, we could get a spurious segv
+ if the extra 4th byte is past the end of memory.
+ (Though at least one Unix compiler ignores this problem:
+ that on the Sequent 386 machine. */
+ || MIN (unit, BIGGEST_ALIGNMENT) > align
+ || (largest_mode != VOIDmode && unit > GET_MODE_BITSIZE (largest_mode)))
+ return VOIDmode;
+
+ if (SLOW_BYTE_ACCESS && ! volatilep)
+ {
+ enum machine_mode wide_mode = VOIDmode, tmode;
+
+ for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT); tmode != VOIDmode;
+ tmode = GET_MODE_WIDER_MODE (tmode))
+ {
+ unit = GET_MODE_BITSIZE (tmode);
+ if (bitpos / unit == (bitpos + bitsize - 1) / unit
+ && unit <= BITS_PER_WORD
+ && unit <= MIN (align, BIGGEST_ALIGNMENT)
+ && (largest_mode == VOIDmode
+ || unit <= GET_MODE_BITSIZE (largest_mode)))
+ wide_mode = tmode;
+ }
+
+ if (wide_mode != VOIDmode)
+ return wide_mode;
+ }
+
+ return mode;
+}
+
+/* Save all variables describing the current status into the structure *P.
+ This is used before starting a nested function. */
+
+void
+save_storage_status (p)
+ struct function *p;
+{
+#if 0 /* Need not save, since always 0 and non0 (resp.) within a function. */
+ p->pending_sizes = pending_sizes;
+ p->immediate_size_expand = immediate_size_expand;
+#endif /* 0 */
+}
+
+/* Restore all variables describing the current status from the structure *P.
+ This is used after a nested function. */
+
+void
+restore_storage_status (p)
+ struct function *p;
+{
+#if 0
+ pending_sizes = p->pending_sizes;
+ immediate_size_expand = p->immediate_size_expand;
+#endif /* 0 */
+}
diff --git a/gnu/usr.bin/cc/cc_int/stupid.c b/gnu/usr.bin/cc/cc_int/stupid.c
new file mode 100644
index 0000000..7ceec9f
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/stupid.c
@@ -0,0 +1,518 @@
+/* Dummy data flow analysis for GNU compiler in nonoptimizing mode.
+ Copyright (C) 1987, 1991, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file performs stupid register allocation, which is used
+ when cc1 gets the -noreg switch (which is when cc does not get -O).
+
+ Stupid register allocation goes in place of the the flow_analysis,
+ local_alloc and global_alloc passes. combine_instructions cannot
+ be done with stupid allocation because the data flow info that it needs
+ is not computed here.
+
+ In stupid allocation, the only user-defined variables that can
+ go in registers are those declared "register". They are assumed
+ to have a life span equal to their scope. Other user variables
+ are given stack slots in the rtl-generation pass and are not
+ represented as pseudo regs. A compiler-generated temporary
+ is assumed to live from its first mention to its last mention.
+
+ Since each pseudo-reg's life span is just an interval, it can be
+ represented as a pair of numbers, each of which identifies an insn by
+ its position in the function (number of insns before it). The first
+ thing done for stupid allocation is to compute such a number for each
+ insn. It is called the suid. Then the life-interval of each
+ pseudo reg is computed. Then the pseudo regs are ordered by priority
+ and assigned hard regs in priority order. */
+
+#include <stdio.h>
+#include "config.h"
+#include "rtl.h"
+#include "hard-reg-set.h"
+#include "regs.h"
+#include "flags.h"
+
+/* Vector mapping INSN_UIDs to suids.
+ The suids are like uids but increase monotonically always.
+ We use them to see whether a subroutine call came
+ between a variable's birth and its death. */
+
+static int *uid_suid;
+
+/* Get the suid of an insn. */
+
+#define INSN_SUID(INSN) (uid_suid[INSN_UID (INSN)])
+
+/* Record the suid of the last CALL_INSN
+ so we can tell whether a pseudo reg crosses any calls. */
+
+static int last_call_suid;
+
+/* Element N is suid of insn where life span of pseudo reg N ends.
+ Element is 0 if register N has not been seen yet on backward scan. */
+
+static int *reg_where_dead;
+
+/* Element N is suid of insn where life span of pseudo reg N begins. */
+
+static int *reg_where_born;
+
+/* Numbers of pseudo-regs to be allocated, highest priority first. */
+
+static int *reg_order;
+
+/* Indexed by reg number (hard or pseudo), nonzero if register is live
+ at the current point in the instruction stream. */
+
+static char *regs_live;
+
+/* Indexed by insn's suid, the set of hard regs live after that insn. */
+
+static HARD_REG_SET *after_insn_hard_regs;
+
+/* Record that hard reg REGNO is live after insn INSN. */
+
+#define MARK_LIVE_AFTER(INSN,REGNO) \
+ SET_HARD_REG_BIT (after_insn_hard_regs[INSN_SUID (INSN)], (REGNO))
+
+static int stupid_reg_compare PROTO((int *, int *));
+static int stupid_find_reg PROTO((int, enum reg_class, enum machine_mode,
+ int, int));
+static void stupid_mark_refs PROTO((rtx, rtx));
+
+/* Stupid life analysis is for the case where only variables declared
+ `register' go in registers. For this case, we mark all
+ pseudo-registers that belong to register variables as
+ dying in the last instruction of the function, and all other
+ pseudo registers as dying in the last place they are referenced.
+ Hard registers are marked as dying in the last reference before
+ the end or before each store into them. */
+
+void
+stupid_life_analysis (f, nregs, file)
+ rtx f;
+ int nregs;
+ FILE *file;
+{
+ register int i;
+ register rtx last, insn;
+ int max_uid, max_suid;
+
+ bzero (regs_ever_live, sizeof regs_ever_live);
+
+ regs_live = (char *) alloca (nregs);
+
+ /* First find the last real insn, and count the number of insns,
+ and assign insns their suids. */
+
+ for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
+ if (INSN_UID (insn) > i)
+ i = INSN_UID (insn);
+
+ max_uid = i + 1;
+ uid_suid = (int *) alloca ((i + 1) * sizeof (int));
+
+ /* Compute the mapping from uids to suids.
+ Suids are numbers assigned to insns, like uids,
+ except that suids increase monotonically through the code. */
+
+ last = 0; /* In case of empty function body */
+ for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
+ {
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ last = insn;
+
+ INSN_SUID (insn) = ++i;
+ }
+
+ last_call_suid = i + 1;
+ max_suid = i + 1;
+
+ max_regno = nregs;
+
+ /* Allocate tables to record info about regs. */
+
+ reg_where_dead = (int *) alloca (nregs * sizeof (int));
+ bzero ((char *) reg_where_dead, nregs * sizeof (int));
+
+ reg_where_born = (int *) alloca (nregs * sizeof (int));
+ bzero ((char *) reg_where_born, nregs * sizeof (int));
+
+ reg_order = (int *) alloca (nregs * sizeof (int));
+ bzero ((char *) reg_order, nregs * sizeof (int));
+
+ reg_renumber = (short *) oballoc (nregs * sizeof (short));
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ reg_renumber[i] = i;
+
+ for (i = FIRST_VIRTUAL_REGISTER; i < max_regno; i++)
+ reg_renumber[i] = -1;
+
+ after_insn_hard_regs
+ = (HARD_REG_SET *) alloca (max_suid * sizeof (HARD_REG_SET));
+
+ bzero ((char *) after_insn_hard_regs, max_suid * sizeof (HARD_REG_SET));
+
+ /* Allocate and zero out many data structures
+ that will record the data from lifetime analysis. */
+
+ allocate_for_life_analysis ();
+
+ for (i = 0; i < max_regno; i++)
+ reg_n_deaths[i] = 1;
+
+ bzero (regs_live, nregs);
+
+ /* Find where each pseudo register is born and dies,
+ by scanning all insns from the end to the start
+ and noting all mentions of the registers.
+
+ Also find where each hard register is live
+ and record that info in after_insn_hard_regs.
+ regs_live[I] is 1 if hard reg I is live
+ at the current point in the scan. */
+
+ for (insn = last; insn; insn = PREV_INSN (insn))
+ {
+ register HARD_REG_SET *p = after_insn_hard_regs + INSN_SUID (insn);
+
+ /* Copy the info in regs_live into the element of after_insn_hard_regs
+ for the current position in the rtl code. */
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (regs_live[i])
+ SET_HARD_REG_BIT (*p, i);
+
+ /* Update which hard regs are currently live
+ and also the birth and death suids of pseudo regs
+ based on the pattern of this insn. */
+
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ stupid_mark_refs (PATTERN (insn), insn);
+
+ /* Mark all call-clobbered regs as live after each call insn
+ so that a pseudo whose life span includes this insn
+ will not go in one of them.
+ Then mark those regs as all dead for the continuing scan
+ of the insns before the call. */
+
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ last_call_suid = INSN_SUID (insn);
+ IOR_HARD_REG_SET (after_insn_hard_regs[last_call_suid],
+ call_used_reg_set);
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (call_used_regs[i])
+ regs_live[i] = 0;
+
+ /* It is important that this be done after processing the insn's
+ pattern because we want the function result register to still
+ be live if it's also used to pass arguments. */
+ stupid_mark_refs (CALL_INSN_FUNCTION_USAGE (insn), insn);
+ }
+ }
+
+ /* Now decide the order in which to allocate the pseudo registers. */
+
+ for (i = LAST_VIRTUAL_REGISTER + 1; i < max_regno; i++)
+ reg_order[i] = i;
+
+ qsort (&reg_order[LAST_VIRTUAL_REGISTER + 1],
+ max_regno - LAST_VIRTUAL_REGISTER - 1, sizeof (int),
+ stupid_reg_compare);
+
+ /* Now, in that order, try to find hard registers for those pseudo regs. */
+
+ for (i = LAST_VIRTUAL_REGISTER + 1; i < max_regno; i++)
+ {
+ register int r = reg_order[i];
+
+ /* Some regnos disappear from the rtl. Ignore them to avoid crash. */
+ if (regno_reg_rtx[r] == 0)
+ continue;
+
+ /* Now find the best hard-register class for this pseudo register */
+ if (N_REG_CLASSES > 1)
+ reg_renumber[r] = stupid_find_reg (reg_n_calls_crossed[r],
+ reg_preferred_class (r),
+ PSEUDO_REGNO_MODE (r),
+ reg_where_born[r],
+ reg_where_dead[r]);
+
+ /* If no reg available in that class, try alternate class. */
+ if (reg_renumber[r] == -1 && reg_alternate_class (r) != NO_REGS)
+ reg_renumber[r] = stupid_find_reg (reg_n_calls_crossed[r],
+ reg_alternate_class (r),
+ PSEUDO_REGNO_MODE (r),
+ reg_where_born[r],
+ reg_where_dead[r]);
+ }
+
+ if (file)
+ dump_flow_info (file);
+}
+
+/* Comparison function for qsort.
+ Returns -1 (1) if register *R1P is higher priority than *R2P. */
+
+static int
+stupid_reg_compare (r1p, r2p)
+ int *r1p, *r2p;
+{
+ register int r1 = *r1p, r2 = *r2p;
+ register int len1 = reg_where_dead[r1] - reg_where_born[r1];
+ register int len2 = reg_where_dead[r2] - reg_where_born[r2];
+ int tem;
+
+ tem = len2 - len1;
+ if (tem != 0)
+ return tem;
+
+ tem = reg_n_refs[r1] - reg_n_refs[r2];
+ if (tem != 0)
+ return tem;
+
+ /* If regs are equally good, sort by regno,
+ so that the results of qsort leave nothing to chance. */
+ return r1 - r2;
+}
+
+/* Find a block of SIZE words of hard registers in reg_class CLASS
+ that can hold a value of machine-mode MODE
+ (but actually we test only the first of the block for holding MODE)
+ currently free from after insn whose suid is BIRTH
+ through the insn whose suid is DEATH,
+ and return the number of the first of them.
+ Return -1 if such a block cannot be found.
+
+ If CALL_PRESERVED is nonzero, insist on registers preserved
+ over subroutine calls, and return -1 if cannot find such. */
+
+static int
+stupid_find_reg (call_preserved, class, mode, born_insn, dead_insn)
+ int call_preserved;
+ enum reg_class class;
+ enum machine_mode mode;
+ int born_insn, dead_insn;
+{
+ register int i, ins;
+#ifdef HARD_REG_SET
+ register /* Declare them register if they are scalars. */
+#endif
+ HARD_REG_SET used, this_reg;
+#ifdef ELIMINABLE_REGS
+ static struct {int from, to; } eliminables[] = ELIMINABLE_REGS;
+#endif
+
+ COPY_HARD_REG_SET (used,
+ call_preserved ? call_used_reg_set : fixed_reg_set);
+
+#ifdef ELIMINABLE_REGS
+ for (i = 0; i < sizeof eliminables / sizeof eliminables[0]; i++)
+ SET_HARD_REG_BIT (used, eliminables[i].from);
+#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+ SET_HARD_REG_BIT (used, HARD_FRAME_POINTER_REGNUM);
+#endif
+#else
+ SET_HARD_REG_BIT (used, FRAME_POINTER_REGNUM);
+#endif
+
+ for (ins = born_insn; ins < dead_insn; ins++)
+ IOR_HARD_REG_SET (used, after_insn_hard_regs[ins]);
+
+ IOR_COMPL_HARD_REG_SET (used, reg_class_contents[(int) class]);
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+#ifdef REG_ALLOC_ORDER
+ int regno = reg_alloc_order[i];
+#else
+ int regno = i;
+#endif
+
+ /* If a register has screwy overlap problems,
+ don't use it at all if not optimizing.
+ Actually this is only for the 387 stack register,
+ and it's because subsequent code won't work. */
+#ifdef OVERLAPPING_REGNO_P
+ if (OVERLAPPING_REGNO_P (regno))
+ continue;
+#endif
+
+ if (! TEST_HARD_REG_BIT (used, regno)
+ && HARD_REGNO_MODE_OK (regno, mode))
+ {
+ register int j;
+ register int size1 = HARD_REGNO_NREGS (regno, mode);
+ for (j = 1; j < size1 && ! TEST_HARD_REG_BIT (used, regno + j); j++);
+ if (j == size1)
+ {
+ CLEAR_HARD_REG_SET (this_reg);
+ while (--j >= 0)
+ SET_HARD_REG_BIT (this_reg, regno + j);
+ for (ins = born_insn; ins < dead_insn; ins++)
+ {
+ IOR_HARD_REG_SET (after_insn_hard_regs[ins], this_reg);
+ }
+ return regno;
+ }
+#ifndef REG_ALLOC_ORDER
+ i += j; /* Skip starting points we know will lose */
+#endif
+ }
+ }
+
+ return -1;
+}
+
+/* Walk X, noting all assignments and references to registers
+ and recording what they imply about life spans.
+ INSN is the current insn, supplied so we can find its suid. */
+
+static void
+stupid_mark_refs (x, insn)
+ rtx x, insn;
+{
+ register RTX_CODE code;
+ register char *fmt;
+ register int regno, i;
+
+ if (x == 0)
+ return;
+
+ code = GET_CODE (x);
+
+ if (code == SET || code == CLOBBER)
+ {
+ if (SET_DEST (x) != 0 && GET_CODE (SET_DEST (x)) == REG)
+ {
+ /* Register is being assigned. */
+ regno = REGNO (SET_DEST (x));
+
+ /* For hard regs, update the where-live info. */
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ register int j
+ = HARD_REGNO_NREGS (regno, GET_MODE (SET_DEST (x)));
+
+ while (--j >= 0)
+ {
+ regs_ever_live[regno+j] = 1;
+ regs_live[regno+j] = 0;
+
+ /* The following line is for unused outputs;
+ they do get stored even though never used again. */
+ MARK_LIVE_AFTER (insn, regno);
+
+ /* When a hard reg is clobbered, mark it in use
+ just before this insn, so it is live all through. */
+ if (code == CLOBBER && INSN_SUID (insn) > 0)
+ SET_HARD_REG_BIT (after_insn_hard_regs[INSN_SUID (insn) - 1],
+ regno);
+ }
+ }
+ /* For pseudo regs, record where born, where dead, number of
+ times used, and whether live across a call. */
+ else
+ {
+ /* Update the life-interval bounds of this pseudo reg. */
+
+ /* When a pseudo-reg is CLOBBERed, it is born just before
+ the clobbering insn. When setting, just after. */
+ int where_born = INSN_SUID (insn) - (code == CLOBBER);
+
+ reg_where_born[regno] = where_born;
+
+ /* The reg must live at least one insn even
+ in it is never again used--because it has to go
+ in SOME hard reg. Mark it as dying after the current
+ insn so that it will conflict with any other outputs of
+ this insn. */
+ if (reg_where_dead[regno] < where_born + 2)
+ {
+ reg_where_dead[regno] = where_born + 2;
+ regs_live[regno] = 1;
+ }
+
+ /* Count the refs of this reg. */
+ reg_n_refs[regno]++;
+
+ if (last_call_suid < reg_where_dead[regno])
+ reg_n_calls_crossed[regno] += 1;
+ }
+ }
+
+ /* Record references from the value being set,
+ or from addresses in the place being set if that's not a reg.
+ If setting a SUBREG, we treat the entire reg as *used*. */
+ if (code == SET)
+ {
+ stupid_mark_refs (SET_SRC (x), insn);
+ if (GET_CODE (SET_DEST (x)) != REG)
+ stupid_mark_refs (SET_DEST (x), insn);
+ }
+ return;
+ }
+
+ /* Register value being used, not set. */
+
+ if (code == REG)
+ {
+ regno = REGNO (x);
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ /* Hard reg: mark it live for continuing scan of previous insns. */
+ register int j = HARD_REGNO_NREGS (regno, GET_MODE (x));
+ while (--j >= 0)
+ {
+ regs_ever_live[regno+j] = 1;
+ regs_live[regno+j] = 1;
+ }
+ }
+ else
+ {
+ /* Pseudo reg: record first use, last use and number of uses. */
+
+ reg_where_born[regno] = INSN_SUID (insn);
+ reg_n_refs[regno]++;
+ if (regs_live[regno] == 0)
+ {
+ regs_live[regno] = 1;
+ reg_where_dead[regno] = INSN_SUID (insn);
+ }
+ }
+ return;
+ }
+
+ /* Recursive scan of all other rtx's. */
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ stupid_mark_refs (XEXP (x, i), insn);
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ stupid_mark_refs (XVECEXP (x, i, j), insn);
+ }
+ }
+}
diff --git a/gnu/usr.bin/cc/cc_int/toplev.c b/gnu/usr.bin/cc/cc_int/toplev.c
new file mode 100644
index 0000000..d4b1043
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/toplev.c
@@ -0,0 +1,4061 @@
+/* Top level of GNU C compiler
+ Copyright (C) 1987, 88, 89, 92, 93, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This is the top level of cc1/c++.
+ It parses command args, opens files, invokes the various passes
+ in the proper order, and counts the time used by each.
+ Error messages and low-level interface to malloc also handled here. */
+
+#include "config.h"
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <stdio.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <sys/stat.h>
+
+#ifdef USG
+#undef FLOAT
+#include <sys/param.h>
+/* This is for hpux. It is a real screw. They should change hpux. */
+#undef FLOAT
+#include <sys/times.h>
+#include <time.h> /* Correct for hpux at least. Is it good on other USG? */
+#undef FFS /* Some systems define this in param.h. */
+#else
+#ifndef VMS
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif
+#endif
+
+#include "input.h"
+#include "tree.h"
+/* #include "c-tree.h" */
+#include "rtl.h"
+#include "flags.h"
+#include "insn-attr.h"
+#include "defaults.h"
+
+#ifdef XCOFF_DEBUGGING_INFO
+#include "xcoffout.h"
+#endif
+
+#include "bytecode.h"
+#include "bc-emit.h"
+
+#ifdef VMS
+/* The extra parameters substantially improve the I/O performance. */
+static FILE *
+VMS_fopen (fname, type)
+ char * fname;
+ char * type;
+{
+ if (strcmp (type, "w") == 0)
+ return fopen (fname, type, "mbc=16", "deq=64", "fop=tef", "shr=nil");
+ return fopen (fname, type, "mbc=16");
+}
+#define fopen VMS_fopen
+#endif
+
+#ifndef DEFAULT_GDB_EXTENSIONS
+#define DEFAULT_GDB_EXTENSIONS 1
+#endif
+
+extern int rtx_equal_function_value_matters;
+
+#if ! (defined (VMS) || defined (OS2))
+extern char **environ;
+#endif
+extern char *version_string, *language_string;
+
+/* Carry information from ASM_DECLARE_OBJECT_NAME
+ to ASM_FINISH_DECLARE_OBJECT. */
+
+extern int size_directive_output;
+extern tree last_assemble_variable_decl;
+
+extern void init_lex ();
+extern void init_decl_processing ();
+extern void init_obstacks ();
+extern void init_tree_codes ();
+extern void init_rtl ();
+extern void init_regs ();
+extern void init_optabs ();
+extern void init_stmt ();
+extern void init_reg_sets ();
+extern void dump_flow_info ();
+extern void dump_sched_info ();
+extern void dump_local_alloc ();
+
+void rest_of_decl_compilation ();
+void error_with_file_and_line PVPROTO((char *file, int line, char *s, ...));
+void error_with_decl PVPROTO((tree decl, char *s, ...));
+void error_for_asm PVPROTO((rtx insn, char *s, ...));
+void error PVPROTO((char *s, ...));
+void fatal PVPROTO((char *s, ...));
+void warning_with_file_and_line PVPROTO((char *file, int line, char *s, ...));
+void warning_with_decl PVPROTO((tree decl, char *s, ...));
+void warning_for_asm PVPROTO((rtx insn, char *s, ...));
+void warning PVPROTO((char *s, ...));
+void pedwarn PVPROTO((char *s, ...));
+void pedwarn_with_decl PVPROTO((tree decl, char *s, ...));
+void pedwarn_with_file_and_line PVPROTO((char *file, int line, char *s, ...));
+void sorry PVPROTO((char *s, ...));
+void really_sorry PVPROTO((char *s, ...));
+void fancy_abort ();
+#ifndef abort
+void abort ();
+#endif
+void set_target_switch ();
+static void print_switch_values ();
+static char *decl_name ();
+
+/* Name of program invoked, sans directories. */
+
+char *progname;
+
+/* Copy of arguments to main. */
+int save_argc;
+char **save_argv;
+
+/* Name of current original source file (what was input to cpp).
+ This comes from each #-command in the actual input. */
+
+char *input_filename;
+
+/* Name of top-level original source file (what was input to cpp).
+ This comes from the #-command at the beginning of the actual input.
+ If there isn't any there, then this is the cc1 input file name. */
+
+char *main_input_filename;
+
+/* Stream for reading from the input file. */
+
+FILE *finput;
+
+/* Current line number in real source file. */
+
+int lineno;
+
+/* Stack of currently pending input files. */
+
+struct file_stack *input_file_stack;
+
+/* Incremented on each change to input_file_stack. */
+int input_file_stack_tick;
+
+/* FUNCTION_DECL for function now being parsed or compiled. */
+
+extern tree current_function_decl;
+
+/* Name to use as base of names for dump output files. */
+
+char *dump_base_name;
+
+/* Bit flags that specify the machine subtype we are compiling for.
+ Bits are tested using macros TARGET_... defined in the tm.h file
+ and set by `-m...' switches. Must be defined in rtlanal.c. */
+
+extern int target_flags;
+
+/* Flags saying which kinds of debugging dump have been requested. */
+
+int rtl_dump = 0;
+int rtl_dump_and_exit = 0;
+int jump_opt_dump = 0;
+int cse_dump = 0;
+int loop_dump = 0;
+int cse2_dump = 0;
+int flow_dump = 0;
+int combine_dump = 0;
+int sched_dump = 0;
+int local_reg_dump = 0;
+int global_reg_dump = 0;
+int sched2_dump = 0;
+int jump2_opt_dump = 0;
+int dbr_sched_dump = 0;
+int flag_print_asm_name = 0;
+int stack_reg_dump = 0;
+
+/* Name for output file of assembly code, specified with -o. */
+
+char *asm_file_name;
+
+/* Value of the -G xx switch, and whether it was passed or not. */
+int g_switch_value;
+int g_switch_set;
+
+/* Type(s) of debugging information we are producing (if any).
+ See flags.h for the definitions of the different possible
+ types of debugging information. */
+enum debug_info_type write_symbols = NO_DEBUG;
+
+/* Level of debugging information we are producing. See flags.h
+ for the definitions of the different possible levels. */
+enum debug_info_level debug_info_level = DINFO_LEVEL_NONE;
+
+/* Nonzero means use GNU-only extensions in the generated symbolic
+ debugging information. */
+/* Currently, this only has an effect when write_symbols is set to
+ DBX_DEBUG, XCOFF_DEBUG, or DWARF_DEBUG. */
+int use_gnu_debug_info_extensions = 0;
+
+/* Nonzero means do optimizations. -O.
+ Particular numeric values stand for particular amounts of optimization;
+ thus, -O2 stores 2 here. However, the optimizations beyond the basic
+ ones are not controlled directly by this variable. Instead, they are
+ controlled by individual `flag_...' variables that are defaulted
+ based on this variable. */
+
+int optimize = 0;
+
+/* Number of error messages and warning messages so far. */
+
+int errorcount = 0;
+int warningcount = 0;
+int sorrycount = 0;
+
+/* Flag to output bytecode instead of native assembler */
+int output_bytecode = 0;
+
+/* Pointer to function to compute the name to use to print a declaration. */
+
+char *(*decl_printable_name) ();
+
+/* Pointer to function to compute rtl for a language-specific tree code. */
+
+struct rtx_def *(*lang_expand_expr) ();
+
+/* Pointer to function to finish handling an incomplete decl at the
+ end of compilation. */
+
+void (*incomplete_decl_finalize_hook) () = 0;
+
+/* Pointer to function for interim exception handling implementation.
+ This interface will change, and it is only here until a better interface
+ replaces it. */
+
+void (*interim_eh_hook) PROTO((tree));
+
+/* Nonzero if generating code to do profiling. */
+
+int profile_flag = 0;
+
+/* Nonzero if generating code to do profiling on a line-by-line basis. */
+
+int profile_block_flag;
+
+/* Nonzero for -pedantic switch: warn about anything
+ that standard spec forbids. */
+
+int pedantic = 0;
+
+/* Temporarily suppress certain warnings.
+ This is set while reading code from a system header file. */
+
+int in_system_header = 0;
+
+/* Nonzero means do stupid register allocation.
+ Currently, this is 1 if `optimize' is 0. */
+
+int obey_regdecls = 0;
+
+/* Don't print functions as they are compiled and don't print
+ times taken by the various passes. -quiet. */
+
+int quiet_flag = 0;
+
+/* -f flags. */
+
+/* Nonzero means `char' should be signed. */
+
+int flag_signed_char;
+
+/* Nonzero means give an enum type only as many bytes as it needs. */
+
+int flag_short_enums;
+
+/* Nonzero for -fcaller-saves: allocate values in regs that need to
+ be saved across function calls, if that produces overall better code.
+ Optional now, so people can test it. */
+
+#ifdef DEFAULT_CALLER_SAVES
+int flag_caller_saves = 1;
+#else
+int flag_caller_saves = 0;
+#endif
+
+/* Nonzero if structures and unions should be returned in memory.
+
+ This should only be defined if compatibility with another compiler or
+ with an ABI is needed, because it results in slower code. */
+
+#ifndef DEFAULT_PCC_STRUCT_RETURN
+#define DEFAULT_PCC_STRUCT_RETURN 1
+#endif
+
+/* Nonzero for -fpcc-struct-return: return values the same way PCC does. */
+
+int flag_pcc_struct_return = DEFAULT_PCC_STRUCT_RETURN;
+
+/* Nonzero for -fforce-mem: load memory value into a register
+ before arithmetic on it. This makes better cse but slower compilation. */
+
+int flag_force_mem = 0;
+
+/* Nonzero for -fforce-addr: load memory address into a register before
+ reference to memory. This makes better cse but slower compilation. */
+
+int flag_force_addr = 0;
+
+/* Nonzero for -fdefer-pop: don't pop args after each function call;
+ instead save them up to pop many calls' args with one insns. */
+
+int flag_defer_pop = 0;
+
+/* Nonzero for -ffloat-store: don't allocate floats and doubles
+ in extended-precision registers. */
+
+int flag_float_store = 0;
+
+/* Nonzero for -fcse-follow-jumps:
+ have cse follow jumps to do a more extensive job. */
+
+int flag_cse_follow_jumps;
+
+/* Nonzero for -fcse-skip-blocks:
+ have cse follow a branch around a block. */
+int flag_cse_skip_blocks;
+
+/* Nonzero for -fexpensive-optimizations:
+ perform miscellaneous relatively-expensive optimizations. */
+int flag_expensive_optimizations;
+
+/* Nonzero for -fthread-jumps:
+ have jump optimize output of loop. */
+
+int flag_thread_jumps;
+
+/* Nonzero enables strength-reduction in loop.c. */
+
+int flag_strength_reduce = 0;
+
+/* Nonzero enables loop unrolling in unroll.c. Only loops for which the
+ number of iterations can be calculated at compile-time (UNROLL_COMPLETELY,
+ UNROLL_MODULO) or at run-time (preconditioned to be UNROLL_MODULO) are
+ unrolled. */
+
+int flag_unroll_loops;
+
+/* Nonzero enables loop unrolling in unroll.c. All loops are unrolled.
+ This is generally not a win. */
+
+int flag_unroll_all_loops;
+
+/* Nonzero for -fwritable-strings:
+ store string constants in data segment and don't uniquize them. */
+
+int flag_writable_strings = 0;
+
+/* Nonzero means don't put addresses of constant functions in registers.
+ Used for compiling the Unix kernel, where strange substitutions are
+ done on the assembly output. */
+
+int flag_no_function_cse = 0;
+
+/* Nonzero for -fomit-frame-pointer:
+ don't make a frame pointer in simple functions that don't require one. */
+
+int flag_omit_frame_pointer = 0;
+
+/* Nonzero to inhibit use of define_optimization peephole opts. */
+
+int flag_no_peephole = 0;
+
+/* Nonzero allows GCC to violate some IEEE or ANSI rules regarding math
+ operations in the interest of optimization. For example it allows
+ GCC to assume arguments to sqrt are nonnegative numbers, allowing
+ faster code for sqrt to be generated. */
+
+int flag_fast_math = 0;
+
+/* Nonzero means all references through pointers are volatile. */
+
+int flag_volatile;
+
+/* Nonzero means treat all global and extern variables as global. */
+
+int flag_volatile_global;
+
+/* Nonzero means just do syntax checking; don't output anything. */
+
+int flag_syntax_only = 0;
+
+/* Nonzero means to rerun cse after loop optimization. This increases
+ compilation time about 20% and picks up a few more common expressions. */
+
+static int flag_rerun_cse_after_loop;
+
+/* Nonzero for -finline-functions: ok to inline functions that look like
+ good inline candidates. */
+
+int flag_inline_functions;
+
+/* Nonzero for -fkeep-inline-functions: even if we make a function
+ go inline everywhere, keep its definition around for debugging
+ purposes. */
+
+int flag_keep_inline_functions;
+
+/* Nonzero means that functions declared `inline' will be treated
+ as `static'. Prevents generation of zillions of copies of unused
+ static inline functions; instead, `inlines' are written out
+ only when actually used. Used in conjunction with -g. Also
+ does the right thing with #pragma interface. */
+
+int flag_no_inline;
+
+/* Nonzero means we should be saving declaration info into a .X file. */
+
+int flag_gen_aux_info = 0;
+
+/* Specified name of aux-info file. */
+
+static char *aux_info_file_name;
+
+/* Nonzero means make the text shared if supported. */
+
+int flag_shared_data;
+
+/* Nonzero means schedule into delayed branch slots if supported. */
+
+int flag_delayed_branch;
+
+/* Nonzero means to run cleanups after CALL_EXPRs. */
+
+int flag_short_temps;
+
+/* Nonzero if we are compiling pure (sharable) code.
+ Value is 1 if we are doing reasonable (i.e. simple
+ offset into offset table) pic. Value is 2 if we can
+ only perform register offsets. */
+
+int flag_pic;
+
+/* Nonzero means place uninitialized global data in the bss section. */
+
+int flag_no_common;
+
+/* Nonzero means pretend it is OK to examine bits of target floats,
+ even if that isn't true. The resulting code will have incorrect constants,
+ but the same series of instructions that the native compiler would make. */
+
+int flag_pretend_float;
+
+/* Nonzero means change certain warnings into errors.
+ Usually these are warnings about failure to conform to some standard. */
+
+int flag_pedantic_errors = 0;
+
+/* flag_schedule_insns means schedule insns within basic blocks (before
+ local_alloc).
+ flag_schedule_insns_after_reload means schedule insns after
+ global_alloc. */
+
+int flag_schedule_insns = 0;
+int flag_schedule_insns_after_reload = 0;
+
+/* -finhibit-size-directive inhibits output of .size for ELF.
+ This is used only for compiling crtstuff.c,
+ and it may be extended to other effects
+ needed for crtstuff.c on other systems. */
+int flag_inhibit_size_directive = 0;
+
+/* -fverbose-asm causes extra commentary information to be produced in
+ the generated assembly code (to make it more readable). This option
+ is generally only of use to those who actually need to read the
+ generated assembly code (perhaps while debugging the compiler itself). */
+
+int flag_verbose_asm = 0;
+
+/* -fgnu-linker specifies use of the GNU linker for initializations.
+ (Or, more generally, a linker that handles initializations.)
+ -fno-gnu-linker says that collect2 will be used. */
+#ifdef USE_COLLECT2
+int flag_gnu_linker = 0;
+#else
+int flag_gnu_linker = 1;
+#endif
+
+/* Table of language-independent -f options.
+ STRING is the option name. VARIABLE is the address of the variable.
+ ON_VALUE is the value to store in VARIABLE
+ if `-fSTRING' is seen as an option.
+ (If `-fno-STRING' is seen as an option, the opposite value is stored.) */
+
+struct { char *string; int *variable; int on_value;} f_options[] =
+{
+ {"float-store", &flag_float_store, 1},
+ {"volatile", &flag_volatile, 1},
+ {"volatile-global", &flag_volatile_global, 1},
+ {"defer-pop", &flag_defer_pop, 1},
+ {"omit-frame-pointer", &flag_omit_frame_pointer, 1},
+ {"cse-follow-jumps", &flag_cse_follow_jumps, 1},
+ {"cse-skip-blocks", &flag_cse_skip_blocks, 1},
+ {"expensive-optimizations", &flag_expensive_optimizations, 1},
+ {"thread-jumps", &flag_thread_jumps, 1},
+ {"strength-reduce", &flag_strength_reduce, 1},
+ {"unroll-loops", &flag_unroll_loops, 1},
+ {"unroll-all-loops", &flag_unroll_all_loops, 1},
+ {"writable-strings", &flag_writable_strings, 1},
+ {"peephole", &flag_no_peephole, 0},
+ {"force-mem", &flag_force_mem, 1},
+ {"force-addr", &flag_force_addr, 1},
+ {"function-cse", &flag_no_function_cse, 0},
+ {"inline-functions", &flag_inline_functions, 1},
+ {"keep-inline-functions", &flag_keep_inline_functions, 1},
+ {"inline", &flag_no_inline, 0},
+ {"syntax-only", &flag_syntax_only, 1},
+ {"shared-data", &flag_shared_data, 1},
+ {"caller-saves", &flag_caller_saves, 1},
+ {"pcc-struct-return", &flag_pcc_struct_return, 1},
+ {"reg-struct-return", &flag_pcc_struct_return, 0},
+ {"delayed-branch", &flag_delayed_branch, 1},
+ {"rerun-cse-after-loop", &flag_rerun_cse_after_loop, 1},
+ {"pretend-float", &flag_pretend_float, 1},
+ {"schedule-insns", &flag_schedule_insns, 1},
+ {"schedule-insns2", &flag_schedule_insns_after_reload, 1},
+ {"pic", &flag_pic, 1},
+ {"PIC", &flag_pic, 2},
+ {"fast-math", &flag_fast_math, 1},
+ {"common", &flag_no_common, 0},
+ {"inhibit-size-directive", &flag_inhibit_size_directive, 1},
+ {"verbose-asm", &flag_verbose_asm, 1},
+ {"gnu-linker", &flag_gnu_linker, 1},
+ {"bytecode", &output_bytecode, 1}
+};
+
+/* Table of language-specific options. */
+
+char *lang_options[] =
+{
+ "-ansi",
+ "-fallow-single-precision",
+
+ "-fsigned-bitfields",
+ "-funsigned-bitfields",
+ "-fno-signed-bitfields",
+ "-fno-unsigned-bitfields",
+ "-fsigned-char",
+ "-funsigned-char",
+ "-fno-signed-char",
+ "-fno-unsigned-char",
+
+ "-ftraditional",
+ "-traditional",
+ "-fnotraditional",
+ "-fno-traditional",
+
+ "-fasm",
+ "-fno-asm",
+ "-fbuiltin",
+ "-fno-builtin",
+ "-fcond-mismatch",
+ "-fno-cond-mismatch",
+ "-fdollars-in-identifiers",
+ "-fno-dollars-in-identifiers",
+ "-fident",
+ "-fno-ident",
+ "-fshort-double",
+ "-fno-short-double",
+ "-fshort-enums",
+ "-fno-short-enums",
+
+ "-Wall",
+ "-Wbad-function-cast",
+ "-Wno-bad-function-cast",
+ "-Wcast-qual",
+ "-Wno-cast-qual",
+ "-Wchar-subscripts",
+ "-Wno-char-subscripts",
+ "-Wcomment",
+ "-Wno-comment",
+ "-Wcomments",
+ "-Wno-comments",
+ "-Wconversion",
+ "-Wno-conversion",
+ "-Wformat",
+ "-Wno-format",
+ "-Wimport",
+ "-Wno-import",
+ "-Wimplicit",
+ "-Wno-implicit",
+ "-Wmissing-braces",
+ "-Wno-missing-braces",
+ "-Wmissing-declarations",
+ "-Wno-missing-declarations",
+ "-Wmissing-prototypes",
+ "-Wno-missing-prototypes",
+ "-Wnested-externs",
+ "-Wno-nested-externs",
+ "-Wparentheses",
+ "-Wno-parentheses",
+ "-Wpointer-arith",
+ "-Wno-pointer-arith",
+ "-Wredundant-decls",
+ "-Wno-redundant-decls",
+ "-Wstrict-prototypes",
+ "-Wno-strict-prototypes",
+ "-Wtraditional",
+ "-Wno-traditional",
+ "-Wtrigraphs",
+ "-Wno-trigraphs",
+ "-Wwrite-strings",
+ "-Wno-write-strings",
+
+ /* These are for C++. */
+ "-+e0", /* gcc.c tacks the `-' on the front. */
+ "-+e1",
+ "-+e2",
+ "-fall-virtual",
+ "-fno-all-virtual",
+ "-falt-external-templates",
+ "-fno-alt-external-templates",
+ "-fansi-overloading",
+ "-fno-ansi-overloading",
+ "-fcadillac",
+ "-fno-cadillac",
+ "-fconserve-space",
+ "-fno-conserve-space",
+ "-fdefault-inline",
+ "-fno-default-inline",
+ "-fdossier",
+ "-fno-dossier",
+ "-felide-constructors",
+ "-fno-elide-constructors",
+ "-fenum-int-equiv",
+ "-fno-enum-int-equiv",
+ "-fexternal-templates",
+ "-fno-external-templates",
+ "-fgc",
+ "-fno-gc",
+ "-fhandle-exceptions",
+ "-fno-handle-exceptions",
+ "-fhandle-signatures",
+ "-fno-handle-signatures",
+ "-fhuge-objects",
+ "-fno-huge-objects",
+ "-fimplement-inlines",
+ "-fno-implement-inlines",
+ "-fimplicit-templates",
+ "-fno-implicit-templates",
+ "-flabels-ok",
+ "-fno-labels-ok",
+ "-fmemoize-lookups",
+ "-fno-memoize-lookups",
+ "-fnonnull-objects",
+ "-fno-nonnull-objects",
+ "-fsave-memoized",
+ "-fno-save-memoized",
+ "-fshort-temps",
+ "-fno-short-temps",
+ "-fstats",
+ "-fno-stats",
+ "-fstrict-prototype",
+ "-fno-strict-prototype",
+ "-fthis-is-variable",
+ "-fno-this-is-variable",
+ "-fvtable-thunks",
+ "-fno-vtable-thunks",
+ "-fxref",
+ "-fno-xref",
+
+ "-Wreturn-type",
+ "-Wno-return-type",
+ "-Woverloaded-virtual",
+ "-Wno-overloaded-virtual",
+ "-Wenum-clash",
+ "-Wno-enum-clash",
+ "-Wtemplate-debugging",
+ "-Wno-template-debugging",
+ "-Wctor-dtor-privacy",
+ "-Wno-ctor-dtor-privacy",
+ "-Wnon-virtual-dtor",
+ "-Wno-non-virtual-dtor",
+ "-Wextern-inline",
+ "-Wno-extern-inline",
+
+ /* these are for obj c */
+ "-lang-objc",
+ "-gen-decls",
+ "-fgnu-runtime",
+ "-fno-gnu-runtime",
+ "-fnext-runtime",
+ "-fno-next-runtime",
+ "-Wselector",
+ "-Wno-selector",
+ "-Wprotocol",
+ "-Wno-protocol",
+
+ /* This is for GNAT and is temporary. */
+ "-gnat",
+ 0
+};
+
+/* Options controlling warnings */
+
+/* Don't print warning messages. -w. */
+
+int inhibit_warnings = 0;
+
+/* Print various extra warnings. -W. */
+
+int extra_warnings = 0;
+
+/* Treat warnings as errors. -Werror. */
+
+int warnings_are_errors = 0;
+
+/* Nonzero to warn about unused local variables. */
+
+int warn_unused;
+
+/* Nonzero to warn about variables used before they are initialized. */
+
+int warn_uninitialized;
+
+/* Nonzero means warn about all declarations which shadow others. */
+
+int warn_shadow;
+
+/* Warn if a switch on an enum fails to have a case for every enum value. */
+
+int warn_switch;
+
+/* Nonzero means warn about function definitions that default the return type
+ or that use a null return and have a return-type other than void. */
+
+int warn_return_type;
+
+/* Nonzero means warn about pointer casts that increase the required
+ alignment of the target type (and might therefore lead to a crash
+ due to a misaligned access). */
+
+int warn_cast_align;
+
+/* Nonzero means warn about any identifiers that match in the first N
+ characters. The value N is in `id_clash_len'. */
+
+int warn_id_clash;
+unsigned id_clash_len;
+
+/* Nonzero means warn about any objects definitions whose size is larger
+ than N bytes. Also want about function definitions whose returned
+ values are larger than N bytes. The value N is in `larger_than_size'. */
+
+int warn_larger_than;
+unsigned larger_than_size;
+
+/* Nonzero means warn if inline function is too large. */
+
+int warn_inline;
+
+/* Warn if a function returns an aggregate,
+ since there are often incompatible calling conventions for doing this. */
+
+int warn_aggregate_return;
+
+/* Likewise for -W. */
+
+struct { char *string; int *variable; int on_value;} W_options[] =
+{
+ {"unused", &warn_unused, 1},
+ {"error", &warnings_are_errors, 1},
+ {"shadow", &warn_shadow, 1},
+ {"switch", &warn_switch, 1},
+ {"aggregate-return", &warn_aggregate_return, 1},
+ {"cast-align", &warn_cast_align, 1},
+ {"uninitialized", &warn_uninitialized, 1},
+ {"inline", &warn_inline, 1}
+};
+
+/* Output files for assembler code (real compiler output)
+ and debugging dumps. */
+
+FILE *asm_out_file;
+FILE *aux_info_file;
+FILE *rtl_dump_file;
+FILE *jump_opt_dump_file;
+FILE *cse_dump_file;
+FILE *loop_dump_file;
+FILE *cse2_dump_file;
+FILE *flow_dump_file;
+FILE *combine_dump_file;
+FILE *sched_dump_file;
+FILE *local_reg_dump_file;
+FILE *global_reg_dump_file;
+FILE *sched2_dump_file;
+FILE *jump2_opt_dump_file;
+FILE *dbr_sched_dump_file;
+FILE *stack_reg_dump_file;
+
+/* Time accumulators, to count the total time spent in various passes. */
+
+int parse_time;
+int varconst_time;
+int integration_time;
+int jump_time;
+int cse_time;
+int loop_time;
+int cse2_time;
+int flow_time;
+int combine_time;
+int sched_time;
+int local_alloc_time;
+int global_alloc_time;
+int sched2_time;
+int dbr_sched_time;
+int shorten_branch_time;
+int stack_reg_time;
+int final_time;
+int symout_time;
+int dump_time;
+
+/* Return time used so far, in microseconds. */
+
+int
+get_run_time ()
+{
+#ifdef USG
+ struct tms tms;
+#else
+#ifndef VMS
+ struct rusage rusage;
+#else /* VMS */
+ struct
+ {
+ int proc_user_time;
+ int proc_system_time;
+ int child_user_time;
+ int child_system_time;
+ } vms_times;
+#endif
+#endif
+
+ if (quiet_flag)
+ return 0;
+
+#ifdef USG
+ times (&tms);
+ return (tms.tms_utime + tms.tms_stime) * (1000000 / HZ);
+#else
+#ifndef VMS
+ getrusage (0, &rusage);
+ return (rusage.ru_utime.tv_sec * 1000000 + rusage.ru_utime.tv_usec
+ + rusage.ru_stime.tv_sec * 1000000 + rusage.ru_stime.tv_usec);
+#else /* VMS */
+ times (&vms_times);
+ return (vms_times.proc_user_time + vms_times.proc_system_time) * 10000;
+#endif
+#endif
+}
+
+#define TIMEVAR(VAR, BODY) \
+do { int otime = get_run_time (); BODY; VAR += get_run_time () - otime; } while (0)
+
+void
+print_time (str, total)
+ char *str;
+ int total;
+{
+ fprintf (stderr,
+ "time in %s: %d.%06d\n",
+ str, total / 1000000, total % 1000000);
+}
+
+/* Count an error or warning. Return 1 if the message should be printed. */
+
+int
+count_error (warningp)
+ int warningp;
+{
+ if (warningp && inhibit_warnings)
+ return 0;
+
+ if (warningp && !warnings_are_errors)
+ warningcount++;
+ else
+ {
+ static int warning_message = 0;
+
+ if (warningp && !warning_message)
+ {
+ fprintf (stderr, "%s: warnings being treated as errors\n", progname);
+ warning_message = 1;
+ }
+ errorcount++;
+ }
+
+ return 1;
+}
+
+/* Print a fatal error message. NAME is the text.
+ Also include a system error message based on `errno'. */
+
+void
+pfatal_with_name (name)
+ char *name;
+{
+ fprintf (stderr, "%s: ", progname);
+ perror (name);
+ exit (35);
+}
+
+void
+fatal_io_error (name)
+ char *name;
+{
+ fprintf (stderr, "%s: %s: I/O error\n", progname, name);
+ exit (35);
+}
+
+/* Called to give a better error message when we don't have an insn to match
+ what we are looking for or if the insn's constraints aren't satisfied,
+ rather than just calling abort(). */
+
+void
+fatal_insn_not_found (insn)
+ rtx insn;
+{
+ if (!output_bytecode)
+ {
+ if (INSN_CODE (insn) < 0)
+ error ("internal error--unrecognizable insn:");
+ else
+ error ("internal error--insn does not satisfy its constraints:");
+ debug_rtx (insn);
+ }
+ if (asm_out_file)
+ fflush (asm_out_file);
+ if (aux_info_file)
+ fflush (aux_info_file);
+ if (rtl_dump_file)
+ fflush (rtl_dump_file);
+ if (jump_opt_dump_file)
+ fflush (jump_opt_dump_file);
+ if (cse_dump_file)
+ fflush (cse_dump_file);
+ if (loop_dump_file)
+ fflush (loop_dump_file);
+ if (cse2_dump_file)
+ fflush (cse2_dump_file);
+ if (flow_dump_file)
+ fflush (flow_dump_file);
+ if (combine_dump_file)
+ fflush (combine_dump_file);
+ if (sched_dump_file)
+ fflush (sched_dump_file);
+ if (local_reg_dump_file)
+ fflush (local_reg_dump_file);
+ if (global_reg_dump_file)
+ fflush (global_reg_dump_file);
+ if (sched2_dump_file)
+ fflush (sched2_dump_file);
+ if (jump2_opt_dump_file)
+ fflush (jump2_opt_dump_file);
+ if (dbr_sched_dump_file)
+ fflush (dbr_sched_dump_file);
+ if (stack_reg_dump_file)
+ fflush (stack_reg_dump_file);
+ abort ();
+}
+
+/* This is the default decl_printable_name function. */
+
+static char *
+decl_name (decl, kind)
+ tree decl;
+ char **kind;
+{
+ return IDENTIFIER_POINTER (DECL_NAME (decl));
+}
+
+/* This is the default interim_eh_hook function. */
+
+void
+interim_eh (finalization)
+ tree finalization;
+{
+ /* Don't do anything by default. */
+}
+
+static int need_error_newline;
+
+/* Function of last error message;
+ more generally, function such that if next error message is in it
+ then we don't have to mention the function name. */
+static tree last_error_function = NULL;
+
+/* Used to detect when input_file_stack has changed since last described. */
+static int last_error_tick;
+
+/* Called when the start of a function definition is parsed,
+ this function prints on stderr the name of the function. */
+
+void
+announce_function (decl)
+ tree decl;
+{
+ if (! quiet_flag)
+ {
+ char *junk;
+ if (rtl_dump_and_exit)
+ fprintf (stderr, "%s ", IDENTIFIER_POINTER (DECL_NAME (decl)));
+ else
+ fprintf (stderr, " %s", (*decl_printable_name) (decl, &junk));
+ fflush (stderr);
+ need_error_newline = 1;
+ last_error_function = current_function_decl;
+ }
+}
+
+/* Prints out, if necessary, the name of the current function
+ which caused an error. Called from all error and warning functions. */
+
+void
+report_error_function (file)
+ char *file;
+{
+ struct file_stack *p;
+
+ if (need_error_newline)
+ {
+ fprintf (stderr, "\n");
+ need_error_newline = 0;
+ }
+
+ if (last_error_function != current_function_decl)
+ {
+ char *kind = "function";
+ if (current_function_decl != 0
+ && TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE)
+ kind = "method";
+
+ if (file)
+ fprintf (stderr, "%s: ", file);
+
+ if (current_function_decl == NULL)
+ fprintf (stderr, "At top level:\n");
+ else
+ {
+ char *name = (*decl_printable_name) (current_function_decl, &kind);
+ fprintf (stderr, "In %s `%s':\n", kind, name);
+ }
+
+ last_error_function = current_function_decl;
+ }
+ if (input_file_stack && input_file_stack->next != 0
+ && input_file_stack_tick != last_error_tick)
+ {
+ fprintf (stderr, "In file included");
+ for (p = input_file_stack->next; p; p = p->next)
+ {
+ fprintf (stderr, " from %s:%d", p->name, p->line);
+ if (p->next)
+ fprintf (stderr, ",\n ");
+ }
+ fprintf (stderr, ":\n");
+ last_error_tick = input_file_stack_tick;
+ }
+}
+
+/* Print a message. */
+
+static void
+vmessage (prefix, s, ap)
+ char *prefix;
+ char *s;
+ va_list ap;
+{
+ if (prefix)
+ fprintf (stderr, "%s: ", prefix);
+
+#ifdef HAVE_VPRINTF
+ vfprintf (stderr, s, ap);
+#else
+ {
+ HOST_WIDE_INT v1 = va_arg(ap, HOST_WIDE_INT);
+ HOST_WIDE_INT v2 = va_arg(ap, HOST_WIDE_INT);
+ HOST_WIDE_INT v3 = va_arg(ap, HOST_WIDE_INT);
+ fprintf (stderr, s, v1, v2, v3);
+ }
+#endif
+}
+
+/* Print a message relevant to line LINE of file FILE. */
+
+static void
+v_message_with_file_and_line (file, line, prefix, s, ap)
+ char *file;
+ int line;
+ char *prefix;
+ char *s;
+ va_list ap;
+{
+ if (file)
+ fprintf (stderr, "%s:%d: ", file, line);
+ else
+ fprintf (stderr, "%s: ", progname);
+
+ vmessage (prefix, s, ap);
+ fputc ('\n', stderr);
+}
+
+/* Print a message relevant to the given DECL. */
+
+static void
+v_message_with_decl (decl, prefix, s, ap)
+ tree decl;
+ char *prefix;
+ char *s;
+ va_list ap;
+{
+ char *n, *p, *junk;
+
+ fprintf (stderr, "%s:%d: ",
+ DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl));
+
+ if (prefix)
+ fprintf (stderr, "%s: ", prefix);
+
+ /* Do magic to get around lack of varargs support for insertion
+ of arguments into existing list. We know that the decl is first;
+ we ass_u_me that it will be printed with "%s". */
+
+ for (p = s; *p; ++p)
+ {
+ if (*p == '%')
+ {
+ if (*(p + 1) == '%')
+ ++p;
+ else
+ break;
+ }
+ }
+
+ if (p > s) /* Print the left-hand substring. */
+ {
+ char fmt[sizeof "%.255s"];
+ long width = p - s;
+
+ if (width > 255L) width = 255L; /* arbitrary */
+ sprintf (fmt, "%%.%lds", width);
+ fprintf (stderr, fmt, s);
+ }
+
+ if (*p == '%') /* Print the name. */
+ {
+ char *n = (DECL_NAME (decl)
+ ? (*decl_printable_name) (decl, &junk)
+ : "((anonymous))");
+ fputs (n, stderr);
+ while (*p)
+ {
+ ++p;
+ if (isalpha (*(p - 1) & 0xFF))
+ break;
+ }
+ }
+
+ if (*p) /* Print the rest of the message. */
+ vmessage ((char *)NULL, p, ap);
+
+ fputc ('\n', stderr);
+}
+
+/* Figure file and line of the given INSN. */
+
+static void
+file_and_line_for_asm (insn, pfile, pline)
+ rtx insn;
+ char **pfile;
+ int *pline;
+{
+ rtx body = PATTERN (insn);
+ rtx asmop;
+
+ /* Find the (or one of the) ASM_OPERANDS in the insn. */
+ if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS)
+ asmop = SET_SRC (body);
+ else if (GET_CODE (body) == ASM_OPERANDS)
+ asmop = body;
+ else if (GET_CODE (body) == PARALLEL
+ && GET_CODE (XVECEXP (body, 0, 0)) == SET)
+ asmop = SET_SRC (XVECEXP (body, 0, 0));
+ else if (GET_CODE (body) == PARALLEL
+ && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS)
+ asmop = XVECEXP (body, 0, 0);
+ else
+ asmop = NULL;
+
+ if (asmop)
+ {
+ *pfile = ASM_OPERANDS_SOURCE_FILE (asmop);
+ *pline = ASM_OPERANDS_SOURCE_LINE (asmop);
+ }
+ else
+ {
+ *pfile = input_filename;
+ *pline = lineno;
+ }
+}
+
+/* Report an error at line LINE of file FILE. */
+
+static void
+v_error_with_file_and_line (file, line, s, ap)
+ char *file;
+ int line;
+ char *s;
+ va_list ap;
+{
+ count_error (0);
+ report_error_function (file);
+ v_message_with_file_and_line (file, line, (char *)NULL, s, ap);
+}
+
+void
+error_with_file_and_line VPROTO((char *file, int line, char *s, ...))
+{
+#ifndef __STDC__
+ char *file;
+ int line;
+ char *s;
+#endif
+ va_list ap;
+
+ VA_START (ap, s);
+
+#ifndef __STDC__
+ file = va_arg (ap, char *);
+ line = va_arg (ap, int);
+ s = va_arg (ap, char *);
+#endif
+
+ v_error_with_file_and_line (file, line, s, ap);
+ va_end (ap);
+}
+
+/* Report an error at the declaration DECL.
+ S is a format string which uses %s to substitute the declaration
+ name; subsequent substitutions are a la printf. */
+
+static void
+v_error_with_decl (decl, s, ap)
+ tree decl;
+ char *s;
+ va_list ap;
+{
+ count_error (0);
+ report_error_function (DECL_SOURCE_FILE (decl));
+ v_message_with_decl (decl, (char *)NULL, s, ap);
+}
+
+void
+error_with_decl VPROTO((tree decl, char *s, ...))
+{
+#ifndef __STDC__
+ tree decl;
+ char *s;
+#endif
+ va_list ap;
+
+ VA_START (ap, s);
+
+#ifndef __STDC__
+ decl = va_arg (ap, tree);
+ s = va_arg (ap, char *);
+#endif
+
+ v_error_with_decl (decl, s, ap);
+ va_end (ap);
+}
+
+/* Report an error at the line number of the insn INSN.
+ This is used only when INSN is an `asm' with operands,
+ and each ASM_OPERANDS records its own source file and line. */
+
+static void
+v_error_for_asm (insn, s, ap)
+ rtx insn;
+ char *s;
+ va_list ap;
+{
+ char *file;
+ int line;
+
+ count_error (0);
+ file_and_line_for_asm (insn, &file, &line);
+ report_error_function (file);
+ v_message_with_file_and_line (file, line, (char *)NULL, s, ap);
+}
+
+void
+error_for_asm VPROTO((rtx insn, char *s, ...))
+{
+#ifndef __STDC__
+ rtx insn;
+ char *s;
+#endif
+ va_list ap;
+
+ VA_START (ap, s);
+
+#ifndef __STDC__
+ insn = va_arg (ap, rtx);
+ s = va_arg (ap, char *);
+#endif
+
+ v_error_for_asm (insn, s, ap);
+ va_end (ap);
+}
+
+/* Report an error at the current line number. */
+
+static void
+verror (s, ap)
+ char *s;
+ va_list ap;
+{
+ v_error_with_file_and_line (input_filename, lineno, s, ap);
+}
+
+void
+error VPROTO((char *s, ...))
+{
+#ifndef __STDC__
+ char *s;
+#endif
+ va_list ap;
+
+ VA_START (ap, s);
+
+#ifndef __STDC__
+ s = va_arg (ap, char *);
+#endif
+
+ verror (s, ap);
+ va_end (ap);
+}
+
+/* Report a fatal error at the current line number. */
+
+static void
+vfatal (s, ap)
+ char *s;
+ va_list ap;
+{
+ verror (s, ap);
+ exit (34);
+}
+
+void
+fatal VPROTO((char *s, ...))
+{
+#ifndef __STDC__
+ char *s;
+#endif
+ va_list ap;
+
+ VA_START (ap, s);
+
+#ifndef __STDC__
+ s = va_arg (ap, char *);
+#endif
+
+ vfatal (s, ap);
+ va_end (ap);
+}
+
+/* Report a warning at line LINE of file FILE. */
+
+static void
+v_warning_with_file_and_line (file, line, s, ap)
+ char *file;
+ int line;
+ char *s;
+ va_list ap;
+{
+ if (count_error (1))
+ {
+ report_error_function (file);
+ v_message_with_file_and_line (file, line, "warning", s, ap);
+ }
+}
+
+void
+warning_with_file_and_line VPROTO((char *file, int line, char *s, ...))
+{
+#ifndef __STDC__
+ char *file;
+ int line;
+ char *s;
+#endif
+ va_list ap;
+
+ VA_START (ap, s);
+
+#ifndef __STDC__
+ file = va_arg (ap, char *);
+ line = va_arg (ap, int);
+ s = va_arg (ap, char *);
+#endif
+
+ v_warning_with_file_and_line (file, line, s, ap);
+ va_end (ap);
+}
+
+/* Report a warning at the declaration DECL.
+ S is a format string which uses %s to substitute the declaration
+ name; subsequent substitutions are a la printf. */
+
+static void
+v_warning_with_decl (decl, s, ap)
+ tree decl;
+ char *s;
+ va_list ap;
+{
+ if (count_error (1))
+ {
+ report_error_function (DECL_SOURCE_FILE (decl));
+ v_message_with_decl (decl, "warning", s, ap);
+ }
+}
+
+void
+warning_with_decl VPROTO((tree decl, char *s, ...))
+{
+#ifndef __STDC__
+ tree decl;
+ char *s;
+#endif
+ va_list ap;
+
+ VA_START (ap, s);
+
+#ifndef __STDC__
+ decl = va_arg (ap, tree);
+ s = va_arg (ap, char *);
+#endif
+
+ v_warning_with_decl (decl, s, ap);
+ va_end (ap);
+}
+
+/* Report a warning at the line number of the insn INSN.
+ This is used only when INSN is an `asm' with operands,
+ and each ASM_OPERANDS records its own source file and line. */
+
+static void
+v_warning_for_asm (insn, s, ap)
+ rtx insn;
+ char *s;
+ va_list ap;
+{
+ if (count_error (1))
+ {
+ char *file;
+ int line;
+
+ file_and_line_for_asm (insn, &file, &line);
+ report_error_function (file);
+ v_message_with_file_and_line (file, line, "warning", s, ap);
+ }
+}
+
+void
+warning_for_asm VPROTO((rtx insn, char *s, ...))
+{
+#ifndef __STDC__
+ rtx insn;
+ char *s;
+#endif
+ va_list ap;
+
+ VA_START (ap, s);
+
+#ifndef __STDC__
+ insn = va_arg (ap, rtx);
+ s = va_arg (ap, char *);
+#endif
+
+ v_warning_for_asm (insn, s, ap);
+ va_end (ap);
+}
+
+/* Report a warning at the current line number. */
+
+static void
+vwarning (s, ap)
+ char *s;
+ va_list ap;
+{
+ v_warning_with_file_and_line (input_filename, lineno, s, ap);
+}
+
+void
+warning VPROTO((char *s, ...))
+{
+#ifndef __STDC__
+ char *s;
+#endif
+ va_list ap;
+
+ VA_START (ap, s);
+
+#ifndef __STDC__
+ s = va_arg (ap, char *);
+#endif
+
+ vwarning (s, ap);
+ va_end (ap);
+}
+
+/* These functions issue either warnings or errors depending on
+ -pedantic-errors. */
+
+static void
+vpedwarn (s, ap)
+ char *s;
+ va_list ap;
+{
+ if (flag_pedantic_errors)
+ verror (s, ap);
+ else
+ vwarning (s, ap);
+}
+
+void
+pedwarn VPROTO((char *s, ...))
+{
+#ifndef __STDC__
+ char *s;
+#endif
+ va_list ap;
+
+ VA_START (ap, s);
+
+#ifndef __STDC__
+ s = va_arg (ap, char *);
+#endif
+
+ vpedwarn (s, ap);
+ va_end (ap);
+}
+
+static void
+v_pedwarn_with_decl (decl, s, ap)
+ tree decl;
+ char *s;
+ va_list ap;
+{
+ /* We don't want -pedantic-errors to cause the compilation to fail from
+ "errors" in system header files. Sometimes fixincludes can't fix what's
+ broken (eg: unsigned char bitfields - fixing it may change the alignment
+ which will cause programs to mysteriously fail because the C library
+ or kernel uses the original layout). There's no point in issuing a
+ warning either, it's just unnecessary noise. */
+
+ if (! DECL_IN_SYSTEM_HEADER (decl))
+ {
+ if (flag_pedantic_errors)
+ v_error_with_decl (decl, s, ap);
+ else
+ v_warning_with_decl (decl, s, ap);
+ }
+}
+
+void
+pedwarn_with_decl VPROTO((tree decl, char *s, ...))
+{
+#ifndef __STDC__
+ tree decl;
+ char *s;
+#endif
+ va_list ap;
+
+ VA_START (ap, s);
+
+#ifndef __STDC__
+ decl = va_arg (ap, tree);
+ s = va_arg (ap, char *);
+#endif
+
+ v_pedwarn_with_decl (decl, s, ap);
+ va_end (ap);
+}
+
+static void
+v_pedwarn_with_file_and_line (file, line, s, ap)
+ char *file;
+ int line;
+ char *s;
+ va_list ap;
+{
+ if (flag_pedantic_errors)
+ v_error_with_file_and_line (file, line, s, ap);
+ else
+ v_warning_with_file_and_line (file, line, s, ap);
+}
+
+void
+pedwarn_with_file_and_line VPROTO((char *file, int line, char *s, ...))
+{
+#ifndef __STDC__
+ char *file;
+ int line;
+ char *s;
+#endif
+ va_list ap;
+
+ VA_START (ap, s);
+
+#ifndef __STDC__
+ file = va_arg (ap, char *);
+ line = va_arg (ap, int);
+ s = va_arg (ap, char *);
+#endif
+
+ v_pedwarn_with_file_and_line (file, line, s, ap);
+ va_end (ap);
+}
+
+/* Apologize for not implementing some feature. */
+
+static void
+vsorry (s, ap)
+ char *s;
+ va_list ap;
+{
+ sorrycount++;
+ if (input_filename)
+ fprintf (stderr, "%s:%d: ", input_filename, lineno);
+ else
+ fprintf (stderr, "%s: ", progname);
+ vmessage ("sorry, not implemented", s, ap);
+ fputc ('\n', stderr);
+}
+
+void
+sorry VPROTO((char *s, ...))
+{
+#ifndef __STDC__
+ char *s;
+#endif
+ va_list ap;
+
+ VA_START (ap, s);
+
+#ifndef __STDC__
+ s = va_arg (ap, char *);
+#endif
+
+ vsorry (s, ap);
+ va_end (ap);
+}
+
+/* Apologize for not implementing some feature, then quit. */
+
+static void
+v_really_sorry (s, ap)
+ char *s;
+ va_list ap;
+{
+ sorrycount++;
+ if (input_filename)
+ fprintf (stderr, "%s:%d: ", input_filename, lineno);
+ else
+ fprintf (stderr, "%s: ", progname);
+ vmessage ("sorry, not implemented", s, ap);
+ fatal (" (fatal)\n");
+}
+
+void
+really_sorry VPROTO((char *s, ...))
+{
+#ifndef __STDC__
+ char *s;
+#endif
+ va_list ap;
+
+ VA_START (ap, s);
+
+#ifndef __STDC__
+ s = va_arg (ap, char *);
+#endif
+
+ v_really_sorry (s, ap);
+ va_end (ap);
+}
+
+/* More 'friendly' abort that prints the line and file.
+ config.h can #define abort fancy_abort if you like that sort of thing.
+
+ I don't think this is actually a good idea.
+ Other sorts of crashes will look a certain way.
+ It is a good thing if crashes from calling abort look the same way.
+ -- RMS */
+
+void
+fancy_abort ()
+{
+ fatal ("internal gcc abort");
+}
+
+/* This calls abort and is used to avoid problems when abort if a macro.
+ It is used when we need to pass the address of abort. */
+
+void
+do_abort ()
+{
+ abort ();
+}
+
+/* When `malloc.c' is compiled with `rcheck' defined,
+ it calls this function to report clobberage. */
+
+void
+botch (s)
+{
+ abort ();
+}
+
+/* Same as `malloc' but report error if no memory available. */
+
+char *
+xmalloc (size)
+ unsigned size;
+{
+ register char *value = (char *) malloc (size);
+ if (value == 0)
+ fatal ("virtual memory exhausted");
+ return value;
+}
+
+/* Same as `realloc' but report error if no memory available. */
+
+char *
+xrealloc (ptr, size)
+ char *ptr;
+ int size;
+{
+ char *result = (char *) realloc (ptr, size);
+ if (!result)
+ fatal ("virtual memory exhausted");
+ return result;
+}
+
+/* Return the logarithm of X, base 2, considering X unsigned,
+ if X is a power of 2. Otherwise, returns -1.
+
+ This should be used via the `exact_log2' macro. */
+
+int
+exact_log2_wide (x)
+ register unsigned HOST_WIDE_INT x;
+{
+ register int log = 0;
+ /* Test for 0 or a power of 2. */
+ if (x == 0 || x != (x & -x))
+ return -1;
+ while ((x >>= 1) != 0)
+ log++;
+ return log;
+}
+
+/* Given X, an unsigned number, return the largest int Y such that 2**Y <= X.
+ If X is 0, return -1.
+
+ This should be used via the floor_log2 macro. */
+
+int
+floor_log2_wide (x)
+ register unsigned HOST_WIDE_INT x;
+{
+ register int log = -1;
+ while (x != 0)
+ log++,
+ x >>= 1;
+ return log;
+}
+
+int float_handled;
+jmp_buf float_handler;
+
+/* Specify where to longjmp to when a floating arithmetic error happens.
+ If HANDLER is 0, it means don't handle the errors any more. */
+
+void
+set_float_handler (handler)
+ jmp_buf handler;
+{
+ float_handled = (handler != 0);
+ if (handler)
+ bcopy ((char *) handler, (char *) float_handler, sizeof (float_handler));
+}
+
+/* Specify, in HANDLER, where to longjmp to when a floating arithmetic
+ error happens, pushing the previous specification into OLD_HANDLER.
+ Return an indication of whether there was a previous handler in effect. */
+
+int
+push_float_handler (handler, old_handler)
+ jmp_buf handler, old_handler;
+{
+ int was_handled = float_handled;
+
+ float_handled = 1;
+ if (was_handled)
+ bcopy ((char *) float_handler, (char *) old_handler,
+ sizeof (float_handler));
+
+ bcopy ((char *) handler, (char *) float_handler, sizeof (float_handler));
+ return was_handled;
+}
+
+/* Restore the previous specification of whether and where to longjmp to
+ when a floating arithmetic error happens. */
+
+void
+pop_float_handler (handled, handler)
+ int handled;
+ jmp_buf handler;
+{
+ float_handled = handled;
+ if (handled)
+ bcopy ((char *) handler, (char *) float_handler, sizeof (float_handler));
+}
+
+/* Signals actually come here. */
+
+static void
+float_signal (signo)
+ /* If this is missing, some compilers complain. */
+ int signo;
+{
+ if (float_handled == 0)
+ abort ();
+#if defined (USG) || defined (hpux)
+ signal (SIGFPE, float_signal); /* re-enable the signal catcher */
+#endif
+ float_handled = 0;
+ signal (SIGFPE, float_signal);
+ longjmp (float_handler, 1);
+}
+
+/* Handler for SIGPIPE. */
+
+static void
+pipe_closed (signo)
+ /* If this is missing, some compilers complain. */
+ int signo;
+{
+ fatal ("output pipe has been closed");
+}
+
+/* Strip off a legitimate source ending from the input string NAME of
+ length LEN. */
+
+void
+strip_off_ending (name, len)
+ char *name;
+ int len;
+{
+ if (len > 2 && ! strcmp (".c", name + len - 2))
+ name[len - 2] = 0;
+ else if (len > 2 && ! strcmp (".m", name + len - 2))
+ name[len - 2] = 0;
+ else if (len > 2 && ! strcmp (".i", name + len - 2))
+ name[len - 2] = 0;
+ else if (len > 3 && ! strcmp (".ii", name + len - 3))
+ name[len - 3] = 0;
+ else if (len > 3 && ! strcmp (".co", name + len - 3))
+ name[len - 3] = 0;
+ else if (len > 3 && ! strcmp (".cc", name + len - 3))
+ name[len - 3] = 0;
+ else if (len > 2 && ! strcmp (".C", name + len - 2))
+ name[len - 2] = 0;
+ else if (len > 4 && ! strcmp (".cxx", name + len - 4))
+ name[len - 4] = 0;
+ else if (len > 4 && ! strcmp (".cpp", name + len - 4))
+ name[len - 4] = 0;
+ else if (len > 2 && ! strcmp (".f", name + len - 2))
+ name[len - 2] = 0;
+ /* Ada will use extensions like .ada, .adb, and .ads, so just test
+ for "ad". */
+ else if (len > 4 && ! strncmp (".ad", name + len - 4, 3))
+ name[len - 4] = 0;
+ else if (len > 4 && ! strcmp (".atr", name + len - 4))
+ name[len - 4] = 0;
+}
+
+/* Output a quoted string. */
+void
+output_quoted_string (asm_file, string)
+ FILE *asm_file;
+ char *string;
+{
+ char c;
+
+ putc ('\"', asm_file);
+ while ((c = *string++) != 0)
+ {
+ if (c == '\"' || c == '\\')
+ putc ('\\', asm_file);
+ putc (c, asm_file);
+ }
+ putc ('\"', asm_file);
+}
+
+/* Output a file name in the form wanted by System V. */
+
+void
+output_file_directive (asm_file, input_name)
+ FILE *asm_file;
+ char *input_name;
+{
+ int len = strlen (input_name);
+ char *na = input_name + len;
+
+ /* NA gets INPUT_NAME sans directory names. */
+ while (na > input_name)
+ {
+ if (na[-1] == '/')
+ break;
+ na--;
+ }
+
+#ifdef ASM_OUTPUT_MAIN_SOURCE_FILENAME
+ ASM_OUTPUT_MAIN_SOURCE_FILENAME (asm_file, na);
+#else
+#ifdef ASM_OUTPUT_SOURCE_FILENAME
+ ASM_OUTPUT_SOURCE_FILENAME (asm_file, na);
+#else
+ fprintf (asm_file, "\t.file\t");
+ output_quoted_string (asm_file, na);
+ fputc ('\n', asm_file);
+#endif
+#endif
+}
+
+/* Routine to build language identifier for object file. */
+static void
+output_lang_identify (asm_out_file)
+ FILE *asm_out_file;
+{
+ int len = strlen (lang_identify ()) + sizeof ("__gnu_compiled_") + 1;
+ char *s = (char *) alloca (len);
+ sprintf (s, "__gnu_compiled_%s", lang_identify ());
+ ASM_OUTPUT_LABEL (asm_out_file, s);
+}
+
+/* Routine to open a dump file. */
+static FILE *
+open_dump_file (base_name, suffix)
+ char *base_name;
+ char *suffix;
+{
+ FILE *f;
+ char *dumpname = (char *) alloca (strlen (base_name) + strlen (suffix) + 1);
+
+ strcpy (dumpname, base_name);
+ strcat (dumpname, suffix);
+ f = fopen (dumpname, "w");
+ if (f == 0)
+ pfatal_with_name (dumpname);
+ return f;
+}
+
+/* Compile an entire file of output from cpp, named NAME.
+ Write a file of assembly output and various debugging dumps. */
+
+static void
+compile_file (name)
+ char *name;
+{
+ tree globals;
+ int start_time;
+
+ int name_specified = name != 0;
+
+ if (dump_base_name == 0)
+ dump_base_name = name ? name : "gccdump";
+
+ parse_time = 0;
+ varconst_time = 0;
+ integration_time = 0;
+ jump_time = 0;
+ cse_time = 0;
+ loop_time = 0;
+ cse2_time = 0;
+ flow_time = 0;
+ combine_time = 0;
+ sched_time = 0;
+ local_alloc_time = 0;
+ global_alloc_time = 0;
+ sched2_time = 0;
+ dbr_sched_time = 0;
+ shorten_branch_time = 0;
+ stack_reg_time = 0;
+ final_time = 0;
+ symout_time = 0;
+ dump_time = 0;
+
+ /* Open input file. */
+
+ if (name == 0 || !strcmp (name, "-"))
+ {
+ finput = stdin;
+ name = "stdin";
+ }
+ else
+ finput = fopen (name, "r");
+ if (finput == 0)
+ pfatal_with_name (name);
+
+#ifdef IO_BUFFER_SIZE
+ setvbuf (finput, (char *) xmalloc (IO_BUFFER_SIZE), _IOFBF, IO_BUFFER_SIZE);
+#endif
+
+ /* Initialize data in various passes. */
+
+ init_obstacks ();
+ init_tree_codes ();
+ init_lex ();
+ /* Some of these really don't need to be called when generating bytecode,
+ but the options would have to be parsed first to know that. -bson */
+ init_rtl ();
+ init_emit_once (debug_info_level == DINFO_LEVEL_NORMAL
+ || debug_info_level == DINFO_LEVEL_VERBOSE);
+ init_regs ();
+ init_decl_processing ();
+ init_optabs ();
+ init_stmt ();
+ init_expmed ();
+ init_expr_once ();
+ init_loop ();
+ init_reload ();
+
+ if (flag_caller_saves)
+ init_caller_save ();
+
+ /* If auxiliary info generation is desired, open the output file.
+ This goes in the same directory as the source file--unlike
+ all the other output files. */
+ if (flag_gen_aux_info)
+ {
+ aux_info_file = fopen (aux_info_file_name, "w");
+ if (aux_info_file == 0)
+ pfatal_with_name (aux_info_file_name);
+ }
+
+ /* If rtl dump desired, open the output file. */
+ if (rtl_dump)
+ rtl_dump_file = open_dump_file (dump_base_name, ".rtl");
+
+ /* If jump_opt dump desired, open the output file. */
+ if (jump_opt_dump)
+ jump_opt_dump_file = open_dump_file (dump_base_name, ".jump");
+
+ /* If cse dump desired, open the output file. */
+ if (cse_dump)
+ cse_dump_file = open_dump_file (dump_base_name, ".cse");
+
+ /* If loop dump desired, open the output file. */
+ if (loop_dump)
+ loop_dump_file = open_dump_file (dump_base_name, ".loop");
+
+ /* If cse2 dump desired, open the output file. */
+ if (cse2_dump)
+ cse2_dump_file = open_dump_file (dump_base_name, ".cse2");
+
+ /* If flow dump desired, open the output file. */
+ if (flow_dump)
+ flow_dump_file = open_dump_file (dump_base_name, ".flow");
+
+ /* If combine dump desired, open the output file. */
+ if (combine_dump)
+ combine_dump_file = open_dump_file (dump_base_name, ".combine");
+
+ /* If scheduling dump desired, open the output file. */
+ if (sched_dump)
+ sched_dump_file = open_dump_file (dump_base_name, ".sched");
+
+ /* If local_reg dump desired, open the output file. */
+ if (local_reg_dump)
+ local_reg_dump_file = open_dump_file (dump_base_name, ".lreg");
+
+ /* If global_reg dump desired, open the output file. */
+ if (global_reg_dump)
+ global_reg_dump_file = open_dump_file (dump_base_name, ".greg");
+
+ /* If 2nd scheduling dump desired, open the output file. */
+ if (sched2_dump)
+ sched2_dump_file = open_dump_file (dump_base_name, ".sched2");
+
+ /* If jump2_opt dump desired, open the output file. */
+ if (jump2_opt_dump)
+ jump2_opt_dump_file = open_dump_file (dump_base_name, ".jump2");
+
+ /* If dbr_sched dump desired, open the output file. */
+ if (dbr_sched_dump)
+ dbr_sched_dump_file = open_dump_file (dump_base_name, ".dbr");
+
+#ifdef STACK_REGS
+
+ /* If stack_reg dump desired, open the output file. */
+ if (stack_reg_dump)
+ stack_reg_dump_file = open_dump_file (dump_base_name, ".stack");
+
+#endif
+
+ /* Open assembler code output file. */
+
+ if (! name_specified && asm_file_name == 0)
+ asm_out_file = stdout;
+ else
+ {
+ int len = strlen (dump_base_name);
+ register char *dumpname = (char *) xmalloc (len + 6);
+ strcpy (dumpname, dump_base_name);
+ strip_off_ending (dumpname, len);
+ strcat (dumpname, ".s");
+ if (asm_file_name == 0)
+ {
+ asm_file_name = (char *) xmalloc (strlen (dumpname) + 1);
+ strcpy (asm_file_name, dumpname);
+ }
+ if (!strcmp (asm_file_name, "-"))
+ asm_out_file = stdout;
+ else
+ asm_out_file = fopen (asm_file_name, "w");
+ if (asm_out_file == 0)
+ pfatal_with_name (asm_file_name);
+ }
+
+#ifdef IO_BUFFER_SIZE
+ setvbuf (asm_out_file, (char *) xmalloc (IO_BUFFER_SIZE),
+ _IOFBF, IO_BUFFER_SIZE);
+#endif
+
+ input_filename = name;
+
+ /* Perform language-specific initialization.
+ This may set main_input_filename. */
+ lang_init ();
+
+ /* If the input doesn't start with a #line, use the input name
+ as the official input file name. */
+ if (main_input_filename == 0)
+ main_input_filename = name;
+
+ /* Put an entry on the input file stack for the main input file. */
+ input_file_stack
+ = (struct file_stack *) xmalloc (sizeof (struct file_stack));
+ input_file_stack->next = 0;
+ input_file_stack->name = input_filename;
+
+ if (!output_bytecode)
+ {
+ ASM_FILE_START (asm_out_file);
+ }
+
+ /* Output something to inform GDB that this compilation was by GCC. Also
+ serves to tell GDB file consists of bytecodes. */
+ if (output_bytecode)
+ fprintf (asm_out_file, "bc_gcc2_compiled.:\n");
+ else
+ {
+#ifndef ASM_IDENTIFY_GCC
+ fprintf (asm_out_file, "gcc2_compiled.:\n");
+#else
+ ASM_IDENTIFY_GCC (asm_out_file);
+#endif
+ }
+
+ /* Output something to identify which front-end produced this file. */
+#ifdef ASM_IDENTIFY_LANGUAGE
+ ASM_IDENTIFY_LANGUAGE (asm_out_file);
+#endif
+
+ if (output_bytecode)
+ {
+ if (profile_flag || profile_block_flag)
+ error ("profiling not supported in bytecode compilation");
+ }
+ else
+ {
+ /* ??? Note: There used to be a conditional here
+ to call assemble_zeros without fail if DBX_DEBUGGING_INFO is defined.
+ This was to guarantee separation between gcc_compiled. and
+ the first function, for the sake of dbx on Suns.
+ However, having the extra zero here confused the Emacs
+ code for unexec, and might confuse other programs too.
+ Therefore, I took out that change.
+ In future versions we should find another way to solve
+ that dbx problem. -- rms, 23 May 93. */
+
+ /* Don't let the first function fall at the same address
+ as gcc_compiled., if profiling. */
+ if (profile_flag || profile_block_flag)
+ assemble_zeros (UNITS_PER_WORD);
+ }
+
+ /* If dbx symbol table desired, initialize writing it
+ and output the predefined types. */
+#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO)
+ if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG)
+ TIMEVAR (symout_time, dbxout_init (asm_out_file, main_input_filename,
+ getdecls ()));
+#endif
+#ifdef SDB_DEBUGGING_INFO
+ if (write_symbols == SDB_DEBUG)
+ TIMEVAR (symout_time, sdbout_init (asm_out_file, main_input_filename,
+ getdecls ()));
+#endif
+#ifdef DWARF_DEBUGGING_INFO
+ if (write_symbols == DWARF_DEBUG)
+ TIMEVAR (symout_time, dwarfout_init (asm_out_file, main_input_filename));
+#endif
+
+ /* Initialize yet another pass. */
+
+ if (!output_bytecode)
+ init_final (main_input_filename);
+
+ start_time = get_run_time ();
+
+ /* Call the parser, which parses the entire file
+ (calling rest_of_compilation for each function). */
+
+ if (yyparse () != 0)
+ {
+ if (errorcount == 0)
+ fprintf (stderr, "Errors detected in input file (your bison.simple is out of date)");
+
+ /* In case there were missing closebraces,
+ get us back to the global binding level. */
+ while (! global_bindings_p ())
+ poplevel (0, 0, 0);
+ }
+
+ /* Compilation is now finished except for writing
+ what's left of the symbol table output. */
+
+ parse_time += get_run_time () - start_time;
+
+ parse_time -= integration_time;
+ parse_time -= varconst_time;
+
+ globals = getdecls ();
+
+ /* Really define vars that have had only a tentative definition.
+ Really output inline functions that must actually be callable
+ and have not been output so far. */
+
+ {
+ int len = list_length (globals);
+ tree *vec = (tree *) alloca (sizeof (tree) * len);
+ int i;
+ tree decl;
+ int reconsider = 1;
+
+ /* Process the decls in reverse order--earliest first.
+ Put them into VEC from back to front, then take out from front. */
+
+ for (i = 0, decl = globals; i < len; i++, decl = TREE_CHAIN (decl))
+ vec[len - i - 1] = decl;
+
+ for (i = 0; i < len; i++)
+ {
+ decl = vec[i];
+
+ /* We're not deferring this any longer. */
+ DECL_DEFER_OUTPUT (decl) = 0;
+
+ if (TREE_CODE (decl) == VAR_DECL && DECL_SIZE (decl) == 0
+ && incomplete_decl_finalize_hook != 0)
+ (*incomplete_decl_finalize_hook) (decl);
+ }
+
+ /* Now emit any global variables or functions that we have been putting
+ off. We need to loop in case one of the things emitted here
+ references another one which comes earlier in the list. */
+ while (reconsider)
+ {
+ reconsider = 0;
+ for (i = 0; i < len; i++)
+ {
+ decl = vec[i];
+
+ if (TREE_ASM_WRITTEN (decl) || DECL_EXTERNAL (decl))
+ continue;
+
+ /* Don't write out static consts, unless we still need them.
+
+ We also keep static consts if not optimizing (for debugging).
+ ??? They might be better written into the debug information.
+ This is possible when using DWARF.
+
+ A language processor that wants static constants to be always
+ written out (even if it is not used) is responsible for
+ calling rest_of_decl_compilation itself. E.g. the C front-end
+ calls rest_of_decl_compilation from finish_decl.
+ One motivation for this is that is conventional in some
+ environments to write things like:
+ static const char rcsid[] = "... version string ...";
+ intending to force the string to be in the executable.
+
+ A language processor that would prefer to have unneeded
+ static constants "optimized away" would just defer writing
+ them out until here. E.g. C++ does this, because static
+ constants are often defined in header files.
+
+ ??? A tempting alternative (for both C and C++) would be
+ to force a constant to be written if and only if it is
+ defined in a main file, as opposed to an include file. */
+
+ if (TREE_CODE (decl) == VAR_DECL && TREE_STATIC (decl)
+ && (! TREE_READONLY (decl)
+ || TREE_PUBLIC (decl)
+ || !optimize
+ || TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))))
+ {
+ reconsider = 1;
+ rest_of_decl_compilation (decl, NULL_PTR, 1, 1);
+ }
+
+ if (TREE_CODE (decl) == FUNCTION_DECL
+ && DECL_INITIAL (decl) != 0
+ && DECL_SAVED_INSNS (decl) != 0
+ && (flag_keep_inline_functions
+ || TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))))
+ {
+ reconsider = 1;
+ temporary_allocation ();
+ output_inline_function (decl);
+ permanent_allocation (1);
+ }
+ }
+ }
+
+ for (i = 0; i < len; i++)
+ {
+ decl = vec[i];
+
+ if (TREE_CODE (decl) == VAR_DECL && TREE_STATIC (decl)
+ && ! TREE_ASM_WRITTEN (decl))
+ /* Cancel the RTL for this decl so that, if debugging info
+ output for global variables is still to come,
+ this one will be omitted. */
+ DECL_RTL (decl) = NULL;
+
+ /* Warn about any function
+ declared static but not defined.
+ We don't warn about variables,
+ because many programs have static variables
+ that exist only to get some text into the object file. */
+ if (TREE_CODE (decl) == FUNCTION_DECL
+ && (warn_unused
+ || TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)))
+ && DECL_INITIAL (decl) == 0
+ && DECL_EXTERNAL (decl)
+ && ! TREE_PUBLIC (decl))
+ {
+ pedwarn_with_decl (decl,
+ "`%s' declared `static' but never defined");
+ /* This symbol is effectively an "extern" declaration now. */
+ TREE_PUBLIC (decl) = 1;
+ assemble_external (decl);
+ }
+
+ /* Warn about static fns or vars defined but not used,
+ but not about inline functions or static consts
+ since defining those in header files is normal practice. */
+ if (warn_unused
+ && ((TREE_CODE (decl) == FUNCTION_DECL && ! DECL_INLINE (decl))
+ || (TREE_CODE (decl) == VAR_DECL && ! TREE_READONLY (decl)))
+ && ! DECL_IN_SYSTEM_HEADER (decl)
+ && ! DECL_EXTERNAL (decl)
+ && ! TREE_PUBLIC (decl)
+ && ! TREE_USED (decl)
+ && ! DECL_REGISTER (decl)
+ /* The TREE_USED bit for file-scope decls
+ is kept in the identifier, to handle multiple
+ external decls in different scopes. */
+ && ! TREE_USED (DECL_NAME (decl)))
+ warning_with_decl (decl, "`%s' defined but not used");
+
+#ifdef SDB_DEBUGGING_INFO
+ /* The COFF linker can move initialized global vars to the end.
+ And that can screw up the symbol ordering.
+ By putting the symbols in that order to begin with,
+ we avoid a problem. mcsun!unido!fauern!tumuc!pes@uunet.uu.net. */
+ if (write_symbols == SDB_DEBUG && TREE_CODE (decl) == VAR_DECL
+ && TREE_PUBLIC (decl) && DECL_INITIAL (decl)
+ && ! DECL_EXTERNAL (decl)
+ && DECL_RTL (decl) != 0)
+ TIMEVAR (symout_time, sdbout_symbol (decl, 0));
+
+ /* Output COFF information for non-global
+ file-scope initialized variables. */
+ if (write_symbols == SDB_DEBUG
+ && TREE_CODE (decl) == VAR_DECL
+ && DECL_INITIAL (decl)
+ && ! DECL_EXTERNAL (decl)
+ && DECL_RTL (decl) != 0
+ && GET_CODE (DECL_RTL (decl)) == MEM)
+ TIMEVAR (symout_time, sdbout_toplevel_data (decl));
+#endif /* SDB_DEBUGGING_INFO */
+#ifdef DWARF_DEBUGGING_INFO
+ /* Output DWARF information for file-scope tentative data object
+ declarations, file-scope (extern) function declarations (which
+ had no corresponding body) and file-scope tagged type declarations
+ and definitions which have not yet been forced out. */
+
+ if (write_symbols == DWARF_DEBUG
+ && (TREE_CODE (decl) != FUNCTION_DECL || !DECL_INITIAL (decl)))
+ TIMEVAR (symout_time, dwarfout_file_scope_decl (decl, 1));
+#endif
+ }
+ }
+
+ /* Do dbx symbols */
+#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO)
+ if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG)
+ TIMEVAR (symout_time,
+ {
+ dbxout_finish (asm_out_file, main_input_filename);
+ });
+#endif
+
+#ifdef DWARF_DEBUGGING_INFO
+ if (write_symbols == DWARF_DEBUG)
+ TIMEVAR (symout_time,
+ {
+ dwarfout_finish ();
+ });
+#endif
+
+ /* Output some stuff at end of file if nec. */
+
+ if (!output_bytecode)
+ {
+ end_final (main_input_filename);
+
+#ifdef ASM_FILE_END
+ ASM_FILE_END (asm_out_file);
+#endif
+ }
+
+ /* Language-specific end of compilation actions. */
+
+ lang_finish ();
+
+ if (output_bytecode)
+ bc_write_file (asm_out_file);
+
+ /* Close the dump files. */
+
+ if (flag_gen_aux_info)
+ {
+ fclose (aux_info_file);
+ if (errorcount)
+ unlink (aux_info_file_name);
+ }
+
+ if (rtl_dump)
+ fclose (rtl_dump_file);
+
+ if (jump_opt_dump)
+ fclose (jump_opt_dump_file);
+
+ if (cse_dump)
+ fclose (cse_dump_file);
+
+ if (loop_dump)
+ fclose (loop_dump_file);
+
+ if (cse2_dump)
+ fclose (cse2_dump_file);
+
+ if (flow_dump)
+ fclose (flow_dump_file);
+
+ if (combine_dump)
+ {
+ dump_combine_total_stats (combine_dump_file);
+ fclose (combine_dump_file);
+ }
+
+ if (sched_dump)
+ fclose (sched_dump_file);
+
+ if (local_reg_dump)
+ fclose (local_reg_dump_file);
+
+ if (global_reg_dump)
+ fclose (global_reg_dump_file);
+
+ if (sched2_dump)
+ fclose (sched2_dump_file);
+
+ if (jump2_opt_dump)
+ fclose (jump2_opt_dump_file);
+
+ if (dbr_sched_dump)
+ fclose (dbr_sched_dump_file);
+
+#ifdef STACK_REGS
+ if (stack_reg_dump)
+ fclose (stack_reg_dump_file);
+#endif
+
+ /* Close non-debugging input and output files. Take special care to note
+ whether fclose returns an error, since the pages might still be on the
+ buffer chain while the file is open. */
+
+ fclose (finput);
+ if (ferror (asm_out_file) != 0 || fclose (asm_out_file) != 0)
+ fatal_io_error (asm_file_name);
+
+ /* Print the times. */
+
+ if (! quiet_flag)
+ {
+ fprintf (stderr,"\n");
+ print_time ("parse", parse_time);
+
+ if (!output_bytecode)
+ {
+ print_time ("integration", integration_time);
+ print_time ("jump", jump_time);
+ print_time ("cse", cse_time);
+ print_time ("loop", loop_time);
+ print_time ("cse2", cse2_time);
+ print_time ("flow", flow_time);
+ print_time ("combine", combine_time);
+ print_time ("sched", sched_time);
+ print_time ("local-alloc", local_alloc_time);
+ print_time ("global-alloc", global_alloc_time);
+ print_time ("sched2", sched2_time);
+ print_time ("dbranch", dbr_sched_time);
+ print_time ("shorten-branch", shorten_branch_time);
+ print_time ("stack-reg", stack_reg_time);
+ print_time ("final", final_time);
+ print_time ("varconst", varconst_time);
+ print_time ("symout", symout_time);
+ print_time ("dump", dump_time);
+ }
+ }
+}
+
+/* This is called from various places for FUNCTION_DECL, VAR_DECL,
+ and TYPE_DECL nodes.
+
+ This does nothing for local (non-static) variables.
+ Otherwise, it sets up the RTL and outputs any assembler code
+ (label definition, storage allocation and initialization).
+
+ DECL is the declaration. If ASMSPEC is nonzero, it specifies
+ the assembler symbol name to be used. TOP_LEVEL is nonzero
+ if this declaration is not within a function. */
+
+void
+rest_of_decl_compilation (decl, asmspec, top_level, at_end)
+ tree decl;
+ char *asmspec;
+ int top_level;
+ int at_end;
+{
+ /* Declarations of variables, and of functions defined elsewhere. */
+
+/* The most obvious approach, to put an #ifndef around where
+ this macro is used, doesn't work since it's inside a macro call. */
+#ifndef ASM_FINISH_DECLARE_OBJECT
+#define ASM_FINISH_DECLARE_OBJECT(FILE, DECL, TOP, END)
+#endif
+
+ /* Forward declarations for nested functions are not "external",
+ but we need to treat them as if they were. */
+ if (TREE_STATIC (decl) || DECL_EXTERNAL (decl)
+ || TREE_CODE (decl) == FUNCTION_DECL)
+ TIMEVAR (varconst_time,
+ {
+ make_decl_rtl (decl, asmspec, top_level);
+ /* Initialized extern variable exists to be replaced
+ with its value, or represents something that will be
+ output in another file. */
+ if (! (TREE_CODE (decl) == VAR_DECL
+ && DECL_EXTERNAL (decl) && TREE_READONLY (decl)
+ && DECL_INITIAL (decl) != 0
+ && DECL_INITIAL (decl) != error_mark_node))
+ /* Don't output anything
+ when a tentative file-scope definition is seen.
+ But at end of compilation, do output code for them. */
+ if (! (! at_end && top_level
+ && (DECL_INITIAL (decl) == 0
+ || DECL_INITIAL (decl) == error_mark_node)))
+ assemble_variable (decl, top_level, at_end, 0);
+ if (decl == last_assemble_variable_decl)
+ {
+ ASM_FINISH_DECLARE_OBJECT (asm_out_file, decl,
+ top_level, at_end);
+ }
+ });
+ else if (DECL_REGISTER (decl) && asmspec != 0)
+ {
+ if (decode_reg_name (asmspec) >= 0)
+ {
+ DECL_RTL (decl) = 0;
+ make_decl_rtl (decl, asmspec, top_level);
+ }
+ else
+ error ("invalid register name `%s' for register variable", asmspec);
+ }
+#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO)
+ else if ((write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG)
+ && TREE_CODE (decl) == TYPE_DECL)
+ TIMEVAR (symout_time, dbxout_symbol (decl, 0));
+#endif
+#ifdef SDB_DEBUGGING_INFO
+ else if (write_symbols == SDB_DEBUG && top_level
+ && TREE_CODE (decl) == TYPE_DECL)
+ TIMEVAR (symout_time, sdbout_symbol (decl, 0));
+#endif
+}
+
+/* Called after finishing a record, union or enumeral type. */
+
+void
+rest_of_type_compilation (type, toplev)
+ tree type;
+ int toplev;
+{
+#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO)
+ if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG)
+ TIMEVAR (symout_time, dbxout_symbol (TYPE_STUB_DECL (type), !toplev));
+#endif
+#ifdef SDB_DEBUGGING_INFO
+ if (write_symbols == SDB_DEBUG)
+ TIMEVAR (symout_time, sdbout_symbol (TYPE_STUB_DECL (type), !toplev));
+#endif
+}
+
+/* This is called from finish_function (within yyparse)
+ after each top-level definition is parsed.
+ It is supposed to compile that function or variable
+ and output the assembler code for it.
+ After we return, the tree storage is freed. */
+
+void
+rest_of_compilation (decl)
+ tree decl;
+{
+ register rtx insns;
+ int start_time = get_run_time ();
+ int tem;
+ /* Nonzero if we have saved the original DECL_INITIAL of the function,
+ to be restored after we finish compiling the function
+ (for use when compiling inline calls to this function). */
+ tree saved_block_tree = 0;
+ /* Likewise, for DECL_ARGUMENTS. */
+ tree saved_arguments = 0;
+ int failure = 0;
+
+ if (output_bytecode)
+ return;
+
+ /* If we are reconsidering an inline function
+ at the end of compilation, skip the stuff for making it inline. */
+
+ if (DECL_SAVED_INSNS (decl) == 0)
+ {
+ int specd = DECL_INLINE (decl);
+ char *lose;
+
+ /* If requested, consider whether to make this function inline. */
+ if (specd || flag_inline_functions)
+ TIMEVAR (integration_time,
+ {
+ lose = function_cannot_inline_p (decl);
+ /* If not optimzing, then make sure the DECL_INLINE
+ bit is off. */
+ if (lose || ! optimize)
+ {
+ if (warn_inline && specd)
+ warning_with_decl (decl, lose);
+ DECL_INLINE (decl) = 0;
+ /* Don't really compile an extern inline function.
+ If we can't make it inline, pretend
+ it was only declared. */
+ if (DECL_EXTERNAL (decl))
+ {
+ DECL_INITIAL (decl) = 0;
+ goto exit_rest_of_compilation;
+ }
+ }
+ else
+ DECL_INLINE (decl) = 1;
+ });
+
+ insns = get_insns ();
+
+ /* Dump the rtl code if we are dumping rtl. */
+
+ if (rtl_dump)
+ TIMEVAR (dump_time,
+ {
+ fprintf (rtl_dump_file, "\n;; Function %s\n\n",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ if (DECL_SAVED_INSNS (decl))
+ fprintf (rtl_dump_file, ";; (integrable)\n\n");
+ print_rtl (rtl_dump_file, insns);
+ fflush (rtl_dump_file);
+ });
+
+ /* If function is inline, and we don't yet know whether to
+ compile it by itself, defer decision till end of compilation.
+ finish_compilation will call rest_of_compilation again
+ for those functions that need to be output. Also defer those
+ functions that were marked inline but weren't inlined; they
+ may never be used. */
+
+ if ((specd || DECL_INLINE (decl))
+ && ((! TREE_PUBLIC (decl) && ! TREE_ADDRESSABLE (decl)
+ && ! flag_keep_inline_functions)
+ || DECL_DEFER_OUTPUT (decl)
+ || DECL_EXTERNAL (decl)))
+ {
+#ifdef DWARF_DEBUGGING_INFO
+ /* Generate the DWARF info for the "abstract" instance
+ of a function which we may later generate inlined and/or
+ out-of-line instances of. */
+ if (write_symbols == DWARF_DEBUG)
+ {
+ set_decl_abstract_flags (decl, 1);
+ TIMEVAR (symout_time, dwarfout_file_scope_decl (decl, 0));
+ set_decl_abstract_flags (decl, 0);
+ }
+#endif
+ TIMEVAR (integration_time, save_for_inline_nocopy (decl));
+ goto exit_rest_of_compilation;
+ }
+
+ /* If we have to compile the function now, save its rtl and subdecls
+ so that its compilation will not affect what others get. */
+ if (DECL_INLINE (decl))
+ {
+#ifdef DWARF_DEBUGGING_INFO
+ /* Generate the DWARF info for the "abstract" instance of
+ a function which we will generate an out-of-line instance
+ of almost immediately (and which we may also later generate
+ various inlined instances of). */
+ if (write_symbols == DWARF_DEBUG)
+ {
+ set_decl_abstract_flags (decl, 1);
+ TIMEVAR (symout_time, dwarfout_file_scope_decl (decl, 0));
+ set_decl_abstract_flags (decl, 0);
+ }
+#endif
+ saved_block_tree = DECL_INITIAL (decl);
+ saved_arguments = DECL_ARGUMENTS (decl);
+ TIMEVAR (integration_time, save_for_inline_copying (decl));
+ }
+ }
+
+ if (DECL_DEFER_OUTPUT (decl))
+ goto exit_rest_of_compilation;
+
+ TREE_ASM_WRITTEN (decl) = 1;
+
+ /* Now that integrate will no longer see our rtl, we need not distinguish
+ between the return value of this function and the return value of called
+ functions. */
+ rtx_equal_function_value_matters = 0;
+
+ /* Don't return yet if -Wreturn-type; we need to do jump_optimize. */
+ if ((rtl_dump_and_exit || flag_syntax_only) && !warn_return_type)
+ {
+ goto exit_rest_of_compilation;
+ }
+
+ /* From now on, allocate rtl in current_obstack, not in saveable_obstack.
+ Note that that may have been done above, in save_for_inline_copying.
+ The call to resume_temporary_allocation near the end of this function
+ goes back to the usual state of affairs. */
+
+ rtl_in_current_obstack ();
+
+#ifdef FINALIZE_PIC
+ /* If we are doing position-independent code generation, now
+ is the time to output special prologues and epilogues.
+ We do not want to do this earlier, because it just clutters
+ up inline functions with meaningless insns. */
+ if (flag_pic)
+ FINALIZE_PIC;
+#endif
+
+ insns = get_insns ();
+
+ /* Copy any shared structure that should not be shared. */
+
+ unshare_all_rtl (insns);
+
+ /* Instantiate all virtual registers. */
+
+ instantiate_virtual_regs (current_function_decl, get_insns ());
+
+ /* See if we have allocated stack slots that are not directly addressable.
+ If so, scan all the insns and create explicit address computation
+ for all references to such slots. */
+/* fixup_stack_slots (); */
+
+ /* Do jump optimization the first time, if -opt.
+ Also do it if -W, but in that case it doesn't change the rtl code,
+ it only computes whether control can drop off the end of the function. */
+
+ if (optimize > 0 || extra_warnings || warn_return_type
+ /* If function is `noreturn', we should warn if it tries to return. */
+ || TREE_THIS_VOLATILE (decl))
+ {
+ TIMEVAR (jump_time, reg_scan (insns, max_reg_num (), 0));
+ TIMEVAR (jump_time, jump_optimize (insns, 0, 0, 1));
+ }
+
+ /* Now is when we stop if -fsyntax-only and -Wreturn-type. */
+ if (rtl_dump_and_exit || flag_syntax_only)
+ goto exit_rest_of_compilation;
+
+ /* Dump rtl code after jump, if we are doing that. */
+
+ if (jump_opt_dump)
+ TIMEVAR (dump_time,
+ {
+ fprintf (jump_opt_dump_file, "\n;; Function %s\n\n",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ print_rtl (jump_opt_dump_file, insns);
+ fflush (jump_opt_dump_file);
+ });
+
+ /* Perform common subexpression elimination.
+ Nonzero value from `cse_main' means that jumps were simplified
+ and some code may now be unreachable, so do
+ jump optimization again. */
+
+ if (cse_dump)
+ TIMEVAR (dump_time,
+ {
+ fprintf (cse_dump_file, "\n;; Function %s\n\n",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ });
+
+ if (optimize > 0)
+ {
+ TIMEVAR (cse_time, reg_scan (insns, max_reg_num (), 1));
+
+ if (flag_thread_jumps)
+ /* Hacks by tiemann & kenner. */
+ TIMEVAR (jump_time, thread_jumps (insns, max_reg_num (), 1));
+
+ TIMEVAR (cse_time, tem = cse_main (insns, max_reg_num (),
+ 0, cse_dump_file));
+ TIMEVAR (cse_time, delete_dead_from_cse (insns, max_reg_num ()));
+
+ if (tem)
+ TIMEVAR (jump_time, jump_optimize (insns, 0, 0, 0));
+ }
+
+ /* Dump rtl code after cse, if we are doing that. */
+
+ if (cse_dump)
+ TIMEVAR (dump_time,
+ {
+ print_rtl (cse_dump_file, insns);
+ fflush (cse_dump_file);
+ });
+
+ if (loop_dump)
+ TIMEVAR (dump_time,
+ {
+ fprintf (loop_dump_file, "\n;; Function %s\n\n",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ });
+
+ /* Move constant computations out of loops. */
+
+ if (optimize > 0)
+ {
+ TIMEVAR (loop_time,
+ {
+ loop_optimize (insns, loop_dump_file);
+ });
+ }
+
+ /* Dump rtl code after loop opt, if we are doing that. */
+
+ if (loop_dump)
+ TIMEVAR (dump_time,
+ {
+ print_rtl (loop_dump_file, insns);
+ fflush (loop_dump_file);
+ });
+
+ if (cse2_dump)
+ TIMEVAR (dump_time,
+ {
+ fprintf (cse2_dump_file, "\n;; Function %s\n\n",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ });
+
+ if (optimize > 0 && flag_rerun_cse_after_loop)
+ {
+ /* Running another jump optimization pass before the second
+ cse pass sometimes simplifies the RTL enough to allow
+ the second CSE pass to do a better job. Jump_optimize can change
+ max_reg_num so we must rerun reg_scan afterwards.
+ ??? Rework to not call reg_scan so often. */
+ TIMEVAR (jump_time, reg_scan (insns, max_reg_num (), 0));
+ TIMEVAR (jump_time, jump_optimize (insns, 0, 0, 1));
+
+ TIMEVAR (cse2_time, reg_scan (insns, max_reg_num (), 0));
+ TIMEVAR (cse2_time, tem = cse_main (insns, max_reg_num (),
+ 1, cse2_dump_file));
+ if (tem)
+ TIMEVAR (jump_time, jump_optimize (insns, 0, 0, 0));
+ }
+
+ if (optimize > 0 && flag_thread_jumps)
+ /* This pass of jump threading straightens out code
+ that was kinked by loop optimization. */
+ TIMEVAR (jump_time, thread_jumps (insns, max_reg_num (), 0));
+
+ /* Dump rtl code after cse, if we are doing that. */
+
+ if (cse2_dump)
+ TIMEVAR (dump_time,
+ {
+ print_rtl (cse2_dump_file, insns);
+ fflush (cse2_dump_file);
+ });
+
+ /* We are no longer anticipating cse in this function, at least. */
+
+ cse_not_expected = 1;
+
+ /* Now we choose between stupid (pcc-like) register allocation
+ (if we got the -noreg switch and not -opt)
+ and smart register allocation. */
+
+ if (optimize > 0) /* Stupid allocation probably won't work */
+ obey_regdecls = 0; /* if optimizations being done. */
+
+ regclass_init ();
+
+ /* Print function header into flow dump now
+ because doing the flow analysis makes some of the dump. */
+
+ if (flow_dump)
+ TIMEVAR (dump_time,
+ {
+ fprintf (flow_dump_file, "\n;; Function %s\n\n",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ });
+
+ if (obey_regdecls)
+ {
+ TIMEVAR (flow_time,
+ {
+ regclass (insns, max_reg_num ());
+ stupid_life_analysis (insns, max_reg_num (),
+ flow_dump_file);
+ });
+ }
+ else
+ {
+ /* Do control and data flow analysis,
+ and write some of the results to dump file. */
+
+ TIMEVAR (flow_time, flow_analysis (insns, max_reg_num (),
+ flow_dump_file));
+ if (warn_uninitialized)
+ {
+ uninitialized_vars_warning (DECL_INITIAL (decl));
+ setjmp_args_warning ();
+ }
+ }
+
+ /* Dump rtl after flow analysis. */
+
+ if (flow_dump)
+ TIMEVAR (dump_time,
+ {
+ print_rtl (flow_dump_file, insns);
+ fflush (flow_dump_file);
+ });
+
+ /* If -opt, try combining insns through substitution. */
+
+ if (optimize > 0)
+ TIMEVAR (combine_time, combine_instructions (insns, max_reg_num ()));
+
+ /* Dump rtl code after insn combination. */
+
+ if (combine_dump)
+ TIMEVAR (dump_time,
+ {
+ fprintf (combine_dump_file, "\n;; Function %s\n\n",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ dump_combine_stats (combine_dump_file);
+ print_rtl (combine_dump_file, insns);
+ fflush (combine_dump_file);
+ });
+
+ /* Print function header into sched dump now
+ because doing the sched analysis makes some of the dump. */
+
+ if (sched_dump)
+ TIMEVAR (dump_time,
+ {
+ fprintf (sched_dump_file, "\n;; Function %s\n\n",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ });
+
+ if (optimize > 0 && flag_schedule_insns)
+ {
+ /* Do control and data sched analysis,
+ and write some of the results to dump file. */
+
+ TIMEVAR (sched_time, schedule_insns (sched_dump_file));
+ }
+
+ /* Dump rtl after instruction scheduling. */
+
+ if (sched_dump)
+ TIMEVAR (dump_time,
+ {
+ print_rtl (sched_dump_file, insns);
+ fflush (sched_dump_file);
+ });
+
+ /* Unless we did stupid register allocation,
+ allocate pseudo-regs that are used only within 1 basic block. */
+
+ if (!obey_regdecls)
+ TIMEVAR (local_alloc_time,
+ {
+ regclass (insns, max_reg_num ());
+ local_alloc ();
+ });
+
+ /* Dump rtl code after allocating regs within basic blocks. */
+
+ if (local_reg_dump)
+ TIMEVAR (dump_time,
+ {
+ fprintf (local_reg_dump_file, "\n;; Function %s\n\n",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ dump_flow_info (local_reg_dump_file);
+ dump_local_alloc (local_reg_dump_file);
+ print_rtl (local_reg_dump_file, insns);
+ fflush (local_reg_dump_file);
+ });
+
+ if (global_reg_dump)
+ TIMEVAR (dump_time,
+ fprintf (global_reg_dump_file, "\n;; Function %s\n\n",
+ IDENTIFIER_POINTER (DECL_NAME (decl))));
+
+ /* Unless we did stupid register allocation,
+ allocate remaining pseudo-regs, then do the reload pass
+ fixing up any insns that are invalid. */
+
+ TIMEVAR (global_alloc_time,
+ {
+ if (!obey_regdecls)
+ failure = global_alloc (global_reg_dump_file);
+ else
+ failure = reload (insns, 0, global_reg_dump_file);
+ });
+
+ if (global_reg_dump)
+ TIMEVAR (dump_time,
+ {
+ dump_global_regs (global_reg_dump_file);
+ print_rtl (global_reg_dump_file, insns);
+ fflush (global_reg_dump_file);
+ });
+
+ if (failure)
+ goto exit_rest_of_compilation;
+
+ reload_completed = 1;
+
+ /* On some machines, the prologue and epilogue code, or parts thereof,
+ can be represented as RTL. Doing so lets us schedule insns between
+ it and the rest of the code and also allows delayed branch
+ scheduling to operate in the epilogue. */
+
+ thread_prologue_and_epilogue_insns (insns);
+
+ if (optimize > 0 && flag_schedule_insns_after_reload)
+ {
+ if (sched2_dump)
+ TIMEVAR (dump_time,
+ {
+ fprintf (sched2_dump_file, "\n;; Function %s\n\n",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ });
+
+ /* Do control and data sched analysis again,
+ and write some more of the results to dump file. */
+
+ TIMEVAR (sched2_time, schedule_insns (sched2_dump_file));
+
+ /* Dump rtl after post-reorder instruction scheduling. */
+
+ if (sched2_dump)
+ TIMEVAR (dump_time,
+ {
+ print_rtl (sched2_dump_file, insns);
+ fflush (sched2_dump_file);
+ });
+ }
+
+#ifdef LEAF_REGISTERS
+ leaf_function = 0;
+ if (optimize > 0 && only_leaf_regs_used () && leaf_function_p ())
+ leaf_function = 1;
+#endif
+
+ /* One more attempt to remove jumps to .+1
+ left by dead-store-elimination.
+ Also do cross-jumping this time
+ and delete no-op move insns. */
+
+ if (optimize > 0)
+ {
+ TIMEVAR (jump_time, jump_optimize (insns, 1, 1, 0));
+ }
+
+ /* Dump rtl code after jump, if we are doing that. */
+
+ if (jump2_opt_dump)
+ TIMEVAR (dump_time,
+ {
+ fprintf (jump2_opt_dump_file, "\n;; Function %s\n\n",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ print_rtl (jump2_opt_dump_file, insns);
+ fflush (jump2_opt_dump_file);
+ });
+
+ /* If a machine dependent reorganization is needed, call it. */
+#ifdef MACHINE_DEPENDENT_REORG
+ MACHINE_DEPENDENT_REORG (insns);
+#endif
+
+ /* If a scheduling pass for delayed branches is to be done,
+ call the scheduling code. */
+
+#ifdef DELAY_SLOTS
+ if (optimize > 0 && flag_delayed_branch)
+ {
+ TIMEVAR (dbr_sched_time, dbr_schedule (insns, dbr_sched_dump_file));
+ if (dbr_sched_dump)
+ {
+ TIMEVAR (dump_time,
+ {
+ fprintf (dbr_sched_dump_file, "\n;; Function %s\n\n",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ print_rtl (dbr_sched_dump_file, insns);
+ fflush (dbr_sched_dump_file);
+ });
+ }
+ }
+#endif
+
+ if (optimize > 0)
+ /* Shorten branches. */
+ TIMEVAR (shorten_branch_time,
+ {
+ shorten_branches (get_insns ());
+ });
+
+#ifdef STACK_REGS
+ TIMEVAR (stack_reg_time, reg_to_stack (insns, stack_reg_dump_file));
+ if (stack_reg_dump)
+ {
+ TIMEVAR (dump_time,
+ {
+ fprintf (stack_reg_dump_file, "\n;; Function %s\n\n",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ print_rtl (stack_reg_dump_file, insns);
+ fflush (stack_reg_dump_file);
+ });
+ }
+#endif
+
+ /* Now turn the rtl into assembler code. */
+
+ TIMEVAR (final_time,
+ {
+ rtx x;
+ char *fnname;
+
+ /* Get the function's name, as described by its RTL.
+ This may be different from the DECL_NAME name used
+ in the source file. */
+
+ x = DECL_RTL (decl);
+ if (GET_CODE (x) != MEM)
+ abort ();
+ x = XEXP (x, 0);
+ if (GET_CODE (x) != SYMBOL_REF)
+ abort ();
+ fnname = XSTR (x, 0);
+
+ assemble_start_function (decl, fnname);
+ final_start_function (insns, asm_out_file, optimize);
+ final (insns, asm_out_file, optimize, 0);
+ final_end_function (insns, asm_out_file, optimize);
+ assemble_end_function (decl, fnname);
+ fflush (asm_out_file);
+ });
+
+ /* Write DBX symbols if requested */
+
+ /* Note that for those inline functions where we don't initially
+ know for certain that we will be generating an out-of-line copy,
+ the first invocation of this routine (rest_of_compilation) will
+ skip over this code by doing a `goto exit_rest_of_compilation;'.
+ Later on, finish_compilation will call rest_of_compilation again
+ for those inline functions that need to have out-of-line copies
+ generated. During that call, we *will* be routed past here. */
+
+#ifdef DBX_DEBUGGING_INFO
+ if (write_symbols == DBX_DEBUG)
+ TIMEVAR (symout_time, dbxout_function (decl));
+#endif
+
+#ifdef DWARF_DEBUGGING_INFO
+ if (write_symbols == DWARF_DEBUG)
+ TIMEVAR (symout_time, dwarfout_file_scope_decl (decl, 0));
+#endif
+
+ exit_rest_of_compilation:
+
+ /* In case the function was not output,
+ don't leave any temporary anonymous types
+ queued up for sdb output. */
+#ifdef SDB_DEBUGGING_INFO
+ if (write_symbols == SDB_DEBUG)
+ sdbout_types (NULL_TREE);
+#endif
+
+ /* Put back the tree of subblocks and list of arguments
+ from before we copied them.
+ Code generation and the output of debugging info may have modified
+ the copy, but the original is unchanged. */
+
+ if (saved_block_tree != 0)
+ DECL_INITIAL (decl) = saved_block_tree;
+ if (saved_arguments != 0)
+ DECL_ARGUMENTS (decl) = saved_arguments;
+
+ reload_completed = 0;
+
+ /* Clear out the real_constant_chain before some of the rtx's
+ it runs through become garbage. */
+
+ clear_const_double_mem ();
+
+ /* Cancel the effect of rtl_in_current_obstack. */
+
+ resume_temporary_allocation ();
+
+ /* The parsing time is all the time spent in yyparse
+ *except* what is spent in this function. */
+
+ parse_time -= get_run_time () - start_time;
+}
+
+/* Entry point of cc1/c++. Decode command args, then call compile_file.
+ Exit code is 35 if can't open files, 34 if fatal error,
+ 33 if had nonfatal errors, else success. */
+
+int
+main (argc, argv, envp)
+ int argc;
+ char **argv;
+ char **envp;
+{
+ register int i;
+ char *filename = 0;
+ int flag_print_mem = 0;
+ int version_flag = 0;
+ char *p;
+
+ /* save in case md file wants to emit args as a comment. */
+ save_argc = argc;
+ save_argv = argv;
+
+ p = argv[0] + strlen (argv[0]);
+ while (p != argv[0] && p[-1] != '/') --p;
+ progname = p;
+
+#ifdef RLIMIT_STACK
+ /* Get rid of any avoidable limit on stack size. */
+ {
+ struct rlimit rlim;
+
+ /* Set the stack limit huge so that alloca does not fail. */
+ getrlimit (RLIMIT_STACK, &rlim);
+ rlim.rlim_cur = rlim.rlim_max;
+ setrlimit (RLIMIT_STACK, &rlim);
+ }
+#endif /* RLIMIT_STACK */
+
+ signal (SIGFPE, float_signal);
+
+#ifdef SIGPIPE
+ signal (SIGPIPE, pipe_closed);
+#endif
+
+ decl_printable_name = decl_name;
+ lang_expand_expr = (struct rtx_def *(*)()) do_abort;
+ interim_eh_hook = interim_eh;
+
+ /* Initialize whether `char' is signed. */
+ flag_signed_char = DEFAULT_SIGNED_CHAR;
+#ifdef DEFAULT_SHORT_ENUMS
+ /* Initialize how much space enums occupy, by default. */
+ flag_short_enums = DEFAULT_SHORT_ENUMS;
+#endif
+
+ /* Scan to see what optimization level has been specified. That will
+ determine the default value of many flags. */
+ for (i = 1; i < argc; i++)
+ {
+ if (!strcmp (argv[i], "-O"))
+ {
+ optimize = 1;
+ }
+ else if (argv[i][0] == '-' && argv[i][1] == 'O')
+ {
+ /* Handle -O2, -O3, -O69, ... */
+ char *p = &argv[i][2];
+ int c;
+
+ while (c = *p++)
+ if (! (c >= '0' && c <= '9'))
+ break;
+ if (c == 0)
+ optimize = atoi (&argv[i][2]);
+ }
+ }
+
+ obey_regdecls = (optimize == 0);
+ if (optimize == 0)
+ {
+ flag_no_inline = 1;
+ warn_inline = 0;
+ }
+
+ if (optimize >= 1)
+ {
+ flag_defer_pop = 1;
+ flag_thread_jumps = 1;
+#ifdef DELAY_SLOTS
+ flag_delayed_branch = 1;
+#endif
+#ifdef CAN_DEBUG_WITHOUT_FP
+ flag_omit_frame_pointer = 1;
+#endif
+ }
+
+ if (optimize >= 2)
+ {
+ flag_cse_follow_jumps = 1;
+ flag_cse_skip_blocks = 1;
+ flag_expensive_optimizations = 1;
+ flag_strength_reduce = 1;
+ flag_rerun_cse_after_loop = 1;
+ flag_caller_saves = 1;
+#ifdef INSN_SCHEDULING
+ flag_schedule_insns = 1;
+ flag_schedule_insns_after_reload = 1;
+#endif
+ }
+
+ if (optimize >= 3)
+ {
+ flag_inline_functions = 1;
+ }
+
+#ifdef OPTIMIZATION_OPTIONS
+ /* Allow default optimizations to be specified on a per-machine basis. */
+ OPTIMIZATION_OPTIONS (optimize);
+#endif
+
+ /* Initialize register usage now so switches may override. */
+ init_reg_sets ();
+
+ target_flags = 0;
+ set_target_switch ("");
+
+ for (i = 1; i < argc; i++)
+ {
+ int j;
+ /* If this is a language-specific option,
+ decode it in a language-specific way. */
+ for (j = 0; lang_options[j] != 0; j++)
+ if (!strncmp (argv[i], lang_options[j],
+ strlen (lang_options[j])))
+ break;
+ if (lang_options[j] != 0)
+ /* If the option is valid for *some* language,
+ treat it as valid even if this language doesn't understand it. */
+ lang_decode_option (argv[i]);
+ else if (argv[i][0] == '-' && argv[i][1] != 0)
+ {
+ register char *str = argv[i] + 1;
+ if (str[0] == 'Y')
+ str++;
+
+ if (str[0] == 'm')
+ set_target_switch (&str[1]);
+ else if (!strcmp (str, "dumpbase"))
+ {
+ dump_base_name = argv[++i];
+ }
+ else if (str[0] == 'd')
+ {
+ register char *p = &str[1];
+ while (*p)
+ switch (*p++)
+ {
+ case 'a':
+ combine_dump = 1;
+ dbr_sched_dump = 1;
+ flow_dump = 1;
+ global_reg_dump = 1;
+ jump_opt_dump = 1;
+ jump2_opt_dump = 1;
+ local_reg_dump = 1;
+ loop_dump = 1;
+ rtl_dump = 1;
+ cse_dump = 1, cse2_dump = 1;
+ sched_dump = 1;
+ sched2_dump = 1;
+ stack_reg_dump = 1;
+ break;
+ case 'k':
+ stack_reg_dump = 1;
+ break;
+ case 'c':
+ combine_dump = 1;
+ break;
+ case 'd':
+ dbr_sched_dump = 1;
+ break;
+ case 'f':
+ flow_dump = 1;
+ break;
+ case 'g':
+ global_reg_dump = 1;
+ break;
+ case 'j':
+ jump_opt_dump = 1;
+ break;
+ case 'J':
+ jump2_opt_dump = 1;
+ break;
+ case 'l':
+ local_reg_dump = 1;
+ break;
+ case 'L':
+ loop_dump = 1;
+ break;
+ case 'm':
+ flag_print_mem = 1;
+ break;
+ case 'p':
+ flag_print_asm_name = 1;
+ break;
+ case 'r':
+ rtl_dump = 1;
+ break;
+ case 's':
+ cse_dump = 1;
+ break;
+ case 't':
+ cse2_dump = 1;
+ break;
+ case 'S':
+ sched_dump = 1;
+ break;
+ case 'R':
+ sched2_dump = 1;
+ break;
+ case 'y':
+ set_yydebug (1);
+ break;
+
+ case 'x':
+ rtl_dump_and_exit = 1;
+ break;
+ }
+ }
+ else if (str[0] == 'f')
+ {
+ register char *p = &str[1];
+ int found = 0;
+
+ /* Some kind of -f option.
+ P's value is the option sans `-f'.
+ Search for it in the table of options. */
+
+ for (j = 0;
+ !found && j < sizeof (f_options) / sizeof (f_options[0]);
+ j++)
+ {
+ if (!strcmp (p, f_options[j].string))
+ {
+ *f_options[j].variable = f_options[j].on_value;
+ /* A goto here would be cleaner,
+ but breaks the vax pcc. */
+ found = 1;
+ }
+ if (p[0] == 'n' && p[1] == 'o' && p[2] == '-'
+ && ! strcmp (p+3, f_options[j].string))
+ {
+ *f_options[j].variable = ! f_options[j].on_value;
+ found = 1;
+ }
+ }
+
+ if (found)
+ ;
+ else if (!strncmp (p, "fixed-", 6))
+ fix_register (&p[6], 1, 1);
+ else if (!strncmp (p, "call-used-", 10))
+ fix_register (&p[10], 0, 1);
+ else if (!strncmp (p, "call-saved-", 11))
+ fix_register (&p[11], 0, 0);
+ else
+ error ("Invalid option `%s'", argv[i]);
+ }
+ else if (str[0] == 'O')
+ {
+ register char *p = str+1;
+ while (*p && *p >= '0' && *p <= '9')
+ p++;
+ if (*p == '\0')
+ ;
+ else
+ error ("Invalid option `%s'", argv[i]);
+ }
+ else if (!strcmp (str, "pedantic"))
+ pedantic = 1;
+ else if (!strcmp (str, "pedantic-errors"))
+ flag_pedantic_errors = pedantic = 1;
+ else if (!strcmp (str, "quiet"))
+ quiet_flag = 1;
+ else if (!strcmp (str, "version"))
+ version_flag = 1;
+ else if (!strcmp (str, "w"))
+ inhibit_warnings = 1;
+ else if (!strcmp (str, "W"))
+ {
+ extra_warnings = 1;
+ /* We save the value of warn_uninitialized, since if they put
+ -Wuninitialized on the command line, we need to generate a
+ warning about not using it without also specifying -O. */
+ if (warn_uninitialized != 1)
+ warn_uninitialized = 2;
+ }
+ else if (str[0] == 'W')
+ {
+ register char *p = &str[1];
+ int found = 0;
+
+ /* Some kind of -W option.
+ P's value is the option sans `-W'.
+ Search for it in the table of options. */
+
+ for (j = 0;
+ !found && j < sizeof (W_options) / sizeof (W_options[0]);
+ j++)
+ {
+ if (!strcmp (p, W_options[j].string))
+ {
+ *W_options[j].variable = W_options[j].on_value;
+ /* A goto here would be cleaner,
+ but breaks the vax pcc. */
+ found = 1;
+ }
+ if (p[0] == 'n' && p[1] == 'o' && p[2] == '-'
+ && ! strcmp (p+3, W_options[j].string))
+ {
+ *W_options[j].variable = ! W_options[j].on_value;
+ found = 1;
+ }
+ }
+
+ if (found)
+ ;
+ else if (!strncmp (p, "id-clash-", 9))
+ {
+ char *endp = p + 9;
+
+ while (*endp)
+ {
+ if (*endp >= '0' && *endp <= '9')
+ endp++;
+ else
+ {
+ error ("Invalid option `%s'", argv[i]);
+ goto id_clash_lose;
+ }
+ }
+ warn_id_clash = 1;
+ id_clash_len = atoi (str + 10);
+ id_clash_lose: ;
+ }
+ else if (!strncmp (p, "larger-than-", 12))
+ {
+ char *endp = p + 12;
+
+ while (*endp)
+ {
+ if (*endp >= '0' && *endp <= '9')
+ endp++;
+ else
+ {
+ error ("Invalid option `%s'", argv[i]);
+ goto larger_than_lose;
+ }
+ }
+ warn_larger_than = 1;
+ larger_than_size = atoi (str + 13);
+ larger_than_lose: ;
+ }
+ else
+ error ("Invalid option `%s'", argv[i]);
+ }
+ else if (!strcmp (str, "p"))
+ {
+ if (!output_bytecode)
+ profile_flag = 1;
+ else
+ error ("profiling not supported in bytecode compilation");
+ }
+ else if (!strcmp (str, "a"))
+ {
+#if !defined (BLOCK_PROFILER) || !defined (FUNCTION_BLOCK_PROFILER)
+ warning ("`-a' option (basic block profile) not supported");
+#else
+ profile_block_flag = 1;
+#endif
+ }
+ else if (str[0] == 'g')
+ {
+ char *p = str + 1;
+ char *q;
+ unsigned len;
+ unsigned level;
+
+ while (*p && (*p < '0' || *p > '9'))
+ p++;
+ len = p - str;
+ q = p;
+ while (*q && (*q >= '0' && *q <= '9'))
+ q++;
+ if (*p)
+ level = atoi (p);
+ else
+ level = 2; /* default debugging info level */
+ if (*q || level > 3)
+ {
+ warning ("invalid debug level specification in option: `-%s'",
+ str);
+ warning ("no debugging information will be generated");
+ level = 0;
+ }
+
+ /* If more than one debugging type is supported,
+ you must define PREFERRED_DEBUGGING_TYPE
+ to choose a format in a system-dependent way. */
+ /* This is one long line cause VAXC can't handle a \-newline. */
+#if 1 < (defined (DBX_DEBUGGING_INFO) + defined (SDB_DEBUGGING_INFO) + defined (DWARF_DEBUGGING_INFO) + defined (XCOFF_DEBUGGING_INFO))
+#ifdef PREFERRED_DEBUGGING_TYPE
+ if (!strncmp (str, "ggdb", len))
+ write_symbols = PREFERRED_DEBUGGING_TYPE;
+#else /* no PREFERRED_DEBUGGING_TYPE */
+You Lose! You must define PREFERRED_DEBUGGING_TYPE!
+#endif /* no PREFERRED_DEBUGGING_TYPE */
+#endif /* More than one debugger format enabled. */
+#ifdef DBX_DEBUGGING_INFO
+ if (write_symbols != NO_DEBUG)
+ ;
+ else if (!strncmp (str, "ggdb", len))
+ write_symbols = DBX_DEBUG;
+ else if (!strncmp (str, "gstabs", len))
+ write_symbols = DBX_DEBUG;
+ else if (!strncmp (str, "gstabs+", len))
+ write_symbols = DBX_DEBUG;
+
+ /* Always enable extensions for -ggdb or -gstabs+,
+ always disable for -gstabs.
+ For plain -g, use system-specific default. */
+ if (write_symbols == DBX_DEBUG && !strncmp (str, "ggdb", len)
+ && len >= 2)
+ use_gnu_debug_info_extensions = 1;
+ else if (write_symbols == DBX_DEBUG && !strncmp (str, "gstabs+", len)
+ && len >= 7)
+ use_gnu_debug_info_extensions = 1;
+ else if (write_symbols == DBX_DEBUG
+ && !strncmp (str, "gstabs", len) && len >= 2)
+ use_gnu_debug_info_extensions = 0;
+ else
+ use_gnu_debug_info_extensions = DEFAULT_GDB_EXTENSIONS;
+#endif /* DBX_DEBUGGING_INFO */
+#ifdef DWARF_DEBUGGING_INFO
+ if (write_symbols != NO_DEBUG)
+ ;
+ else if (!strncmp (str, "g", len))
+ write_symbols = DWARF_DEBUG;
+ else if (!strncmp (str, "ggdb", len))
+ write_symbols = DWARF_DEBUG;
+ else if (!strncmp (str, "gdwarf", len))
+ write_symbols = DWARF_DEBUG;
+
+ /* Always enable extensions for -ggdb or -gdwarf+,
+ always disable for -gdwarf.
+ For plain -g, use system-specific default. */
+ if (write_symbols == DWARF_DEBUG && !strncmp (str, "ggdb", len)
+ && len >= 2)
+ use_gnu_debug_info_extensions = 1;
+ else if (write_symbols == DWARF_DEBUG && !strcmp (str, "gdwarf+"))
+ use_gnu_debug_info_extensions = 1;
+ else if (write_symbols == DWARF_DEBUG
+ && !strncmp (str, "gdwarf", len) && len >= 2)
+ use_gnu_debug_info_extensions = 0;
+ else
+ use_gnu_debug_info_extensions = DEFAULT_GDB_EXTENSIONS;
+#endif
+#ifdef SDB_DEBUGGING_INFO
+ if (write_symbols != NO_DEBUG)
+ ;
+ else if (!strncmp (str, "g", len))
+ write_symbols = SDB_DEBUG;
+ else if (!strncmp (str, "gdb", len))
+ write_symbols = SDB_DEBUG;
+ else if (!strncmp (str, "gcoff", len))
+ write_symbols = SDB_DEBUG;
+#endif /* SDB_DEBUGGING_INFO */
+#ifdef XCOFF_DEBUGGING_INFO
+ if (write_symbols != NO_DEBUG)
+ ;
+ else if (!strncmp (str, "g", len))
+ write_symbols = XCOFF_DEBUG;
+ else if (!strncmp (str, "ggdb", len))
+ write_symbols = XCOFF_DEBUG;
+ else if (!strncmp (str, "gxcoff", len))
+ write_symbols = XCOFF_DEBUG;
+
+ /* Always enable extensions for -ggdb or -gxcoff+,
+ always disable for -gxcoff.
+ For plain -g, use system-specific default. */
+ if (write_symbols == XCOFF_DEBUG && !strncmp (str, "ggdb", len)
+ && len >= 2)
+ use_gnu_debug_info_extensions = 1;
+ else if (write_symbols == XCOFF_DEBUG && !strcmp (str, "gxcoff+"))
+ use_gnu_debug_info_extensions = 1;
+ else if (write_symbols == XCOFF_DEBUG
+ && !strncmp (str, "gxcoff", len) && len >= 2)
+ use_gnu_debug_info_extensions = 0;
+ else
+ use_gnu_debug_info_extensions = DEFAULT_GDB_EXTENSIONS;
+#endif
+ if (write_symbols == NO_DEBUG)
+ warning ("`-%s' option not supported on this version of GCC", str);
+ else if (level == 0)
+ write_symbols = NO_DEBUG;
+ else
+ debug_info_level = (enum debug_info_level) level;
+ }
+ else if (!strcmp (str, "o"))
+ {
+ asm_file_name = argv[++i];
+ }
+ else if (str[0] == 'G')
+ {
+ g_switch_set = TRUE;
+ g_switch_value = atoi ((str[1] != '\0') ? str+1 : argv[++i]);
+ }
+ else if (!strncmp (str, "aux-info", 8))
+ {
+ flag_gen_aux_info = 1;
+ aux_info_file_name = (str[8] != '\0' ? str+8 : argv[++i]);
+ }
+ else
+ error ("Invalid option `%s'", argv[i]);
+ }
+ else if (argv[i][0] == '+')
+ error ("Invalid option `%s'", argv[i]);
+ else
+ filename = argv[i];
+ }
+
+ /* Initialize for bytecode output. A good idea to do this as soon as
+ possible after the "-f" options have been parsed. */
+ if (output_bytecode)
+ {
+#ifndef TARGET_SUPPORTS_BYTECODE
+ /* Just die with a fatal error if not supported */
+ fatal ("-fbytecode not supporter for this target");
+#else
+ bc_initialize ();
+#endif
+ }
+
+ if (optimize == 0)
+ {
+ /* Inlining does not work if not optimizing,
+ so force it not to be done. */
+ flag_no_inline = 1;
+ warn_inline = 0;
+
+ /* The c_decode_option and lang_decode_option functions set
+ this to `2' if -Wall is used, so we can avoid giving out
+ lots of errors for people who don't realize what -Wall does. */
+ if (warn_uninitialized == 1)
+ warning ("-Wuninitialized is not supported without -O");
+ }
+
+#if defined(DWARF_DEBUGGING_INFO)
+ if (write_symbols == DWARF_DEBUG
+ && strcmp (language_string, "GNU C++") == 0)
+ {
+ warning ("-g option not supported for C++ on SVR4 systems");
+ write_symbols = NO_DEBUG;
+ }
+#endif /* defined(DWARF_DEBUGGING_INFO) */
+
+#ifdef OVERRIDE_OPTIONS
+ /* Some machines may reject certain combinations of options. */
+ OVERRIDE_OPTIONS;
+#endif
+
+ /* Unrolling all loops implies that standard loop unrolling must also
+ be done. */
+ if (flag_unroll_all_loops)
+ flag_unroll_loops = 1;
+ /* Loop unrolling requires that strength_reduction be on also. Silently
+ turn on strength reduction here if it isn't already on. Also, the loop
+ unrolling code assumes that cse will be run after loop, so that must
+ be turned on also. */
+ if (flag_unroll_loops)
+ {
+ flag_strength_reduce = 1;
+ flag_rerun_cse_after_loop = 1;
+ }
+
+ /* Warn about options that are not supported on this machine. */
+#ifndef INSN_SCHEDULING
+ if (flag_schedule_insns || flag_schedule_insns_after_reload)
+ warning ("instruction scheduling not supported on this target machine");
+#endif
+#ifndef DELAY_SLOTS
+ if (flag_delayed_branch)
+ warning ("this target machine does not have delayed branches");
+#endif
+
+ /* If we are in verbose mode, write out the version and maybe all the
+ option flags in use. */
+ if (version_flag)
+ {
+ fprintf (stderr, "%s version %s", language_string, version_string);
+#ifdef TARGET_VERSION
+ TARGET_VERSION;
+#endif
+#ifdef __GNUC__
+#ifndef __VERSION__
+#define __VERSION__ "[unknown]"
+#endif
+ fprintf (stderr, " compiled by GNU C version %s.\n", __VERSION__);
+#else
+ fprintf (stderr, " compiled by CC.\n");
+#endif
+ if (! quiet_flag)
+ print_switch_values ();
+ }
+
+ compile_file (filename);
+
+#ifndef OS2
+#ifndef VMS
+ if (flag_print_mem)
+ {
+#ifdef __alpha
+ char *sbrk ();
+#endif
+ char *lim = (char *) sbrk (0);
+
+ fprintf (stderr, "Data size %d.\n",
+ lim - (char *) &environ);
+ fflush (stderr);
+
+#ifdef USG
+ system ("ps -l 1>&2");
+#else /* not USG */
+ system ("ps v");
+#endif /* not USG */
+ }
+#endif /* not VMS */
+#endif /* not OS2 */
+
+ if (errorcount)
+ exit (FATAL_EXIT_CODE);
+ if (sorrycount)
+ exit (FATAL_EXIT_CODE);
+ exit (SUCCESS_EXIT_CODE);
+ return 34;
+}
+
+/* Decode -m switches. */
+
+/* Here is a table, controlled by the tm.h file, listing each -m switch
+ and which bits in `target_switches' it should set or clear.
+ If VALUE is positive, it is bits to set.
+ If VALUE is negative, -VALUE is bits to clear.
+ (The sign bit is not used so there is no confusion.) */
+
+struct {char *name; int value;} target_switches []
+ = TARGET_SWITCHES;
+
+/* This table is similar, but allows the switch to have a value. */
+
+#ifdef TARGET_OPTIONS
+struct {char *prefix; char ** variable;} target_options []
+ = TARGET_OPTIONS;
+#endif
+
+/* Decode the switch -mNAME. */
+
+void
+set_target_switch (name)
+ char *name;
+{
+ register int j;
+ int valid = 0;
+
+ for (j = 0; j < sizeof target_switches / sizeof target_switches[0]; j++)
+ if (!strcmp (target_switches[j].name, name))
+ {
+ if (target_switches[j].value < 0)
+ target_flags &= ~-target_switches[j].value;
+ else
+ target_flags |= target_switches[j].value;
+ valid = 1;
+ }
+
+#ifdef TARGET_OPTIONS
+ if (!valid)
+ for (j = 0; j < sizeof target_options / sizeof target_options[0]; j++)
+ {
+ int len = strlen (target_options[j].prefix);
+ if (!strncmp (target_options[j].prefix, name, len))
+ {
+ *target_options[j].variable = name + len;
+ valid = 1;
+ }
+ }
+#endif
+
+ if (!valid)
+ error ("Invalid option `%s'", name);
+}
+
+/* Variable used for communication between the following two routines. */
+
+static int line_position;
+
+/* Print an option value and adjust the position in the line. */
+
+static void
+print_single_switch (type, name)
+ char *type, *name;
+{
+ fprintf (stderr, " %s%s", type, name);
+
+ line_position += strlen (type) + strlen (name) + 1;
+
+ if (line_position > 65)
+ {
+ fprintf (stderr, "\n\t");
+ line_position = 8;
+ }
+}
+
+/* Print default target switches for -version. */
+
+static void
+print_switch_values ()
+{
+ register int j;
+
+ fprintf (stderr, "enabled:");
+ line_position = 8;
+
+ for (j = 0; j < sizeof f_options / sizeof f_options[0]; j++)
+ if (*f_options[j].variable == f_options[j].on_value)
+ print_single_switch ("-f", f_options[j].string);
+
+ for (j = 0; j < sizeof W_options / sizeof W_options[0]; j++)
+ if (*W_options[j].variable == W_options[j].on_value)
+ print_single_switch ("-W", W_options[j].string);
+
+ for (j = 0; j < sizeof target_switches / sizeof target_switches[0]; j++)
+ if (target_switches[j].name[0] != '\0'
+ && target_switches[j].value > 0
+ && ((target_switches[j].value & target_flags)
+ == target_switches[j].value))
+ print_single_switch ("-m", target_switches[j].name);
+
+ fprintf (stderr, "\n");
+}
diff --git a/gnu/usr.bin/cc/cc_int/tree.c b/gnu/usr.bin/cc/cc_int/tree.c
new file mode 100644
index 0000000..e0aa0ae
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/tree.c
@@ -0,0 +1,3996 @@
+/* Language-independent node constructors for parse phase of GNU compiler.
+ Copyright (C) 1987, 1988, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file contains the low level primitives for operating on tree nodes,
+ including allocation, list operations, interning of identifiers,
+ construction of data type nodes and statement nodes,
+ and construction of type conversion nodes. It also contains
+ tables index by tree code that describe how to take apart
+ nodes of that code.
+
+ It is intended to be language-independent, but occasionally
+ calls language-dependent routines defined (for C) in typecheck.c.
+
+ The low-level allocation routines oballoc and permalloc
+ are used also for allocating many other kinds of objects
+ by all passes of the compiler. */
+
+#include <setjmp.h>
+#include "config.h"
+#include "flags.h"
+#include "tree.h"
+#include "function.h"
+#include "obstack.h"
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <stdio.h>
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+/* Tree nodes of permanent duration are allocated in this obstack.
+ They are the identifier nodes, and everything outside of
+ the bodies and parameters of function definitions. */
+
+struct obstack permanent_obstack;
+
+/* The initial RTL, and all ..._TYPE nodes, in a function
+ are allocated in this obstack. Usually they are freed at the
+ end of the function, but if the function is inline they are saved.
+ For top-level functions, this is maybepermanent_obstack.
+ Separate obstacks are made for nested functions. */
+
+struct obstack *function_maybepermanent_obstack;
+
+/* This is the function_maybepermanent_obstack for top-level functions. */
+
+struct obstack maybepermanent_obstack;
+
+/* The contents of the current function definition are allocated
+ in this obstack, and all are freed at the end of the function.
+ For top-level functions, this is temporary_obstack.
+ Separate obstacks are made for nested functions. */
+
+struct obstack *function_obstack;
+
+/* This is used for reading initializers of global variables. */
+
+struct obstack temporary_obstack;
+
+/* The tree nodes of an expression are allocated
+ in this obstack, and all are freed at the end of the expression. */
+
+struct obstack momentary_obstack;
+
+/* The tree nodes of a declarator are allocated
+ in this obstack, and all are freed when the declarator
+ has been parsed. */
+
+static struct obstack temp_decl_obstack;
+
+/* This points at either permanent_obstack
+ or the current function_maybepermanent_obstack. */
+
+struct obstack *saveable_obstack;
+
+/* This is same as saveable_obstack during parse and expansion phase;
+ it points to the current function's obstack during optimization.
+ This is the obstack to be used for creating rtl objects. */
+
+struct obstack *rtl_obstack;
+
+/* This points at either permanent_obstack or the current function_obstack. */
+
+struct obstack *current_obstack;
+
+/* This points at either permanent_obstack or the current function_obstack
+ or momentary_obstack. */
+
+struct obstack *expression_obstack;
+
+/* Stack of obstack selections for push_obstacks and pop_obstacks. */
+
+struct obstack_stack
+{
+ struct obstack_stack *next;
+ struct obstack *current;
+ struct obstack *saveable;
+ struct obstack *expression;
+ struct obstack *rtl;
+};
+
+struct obstack_stack *obstack_stack;
+
+/* Obstack for allocating struct obstack_stack entries. */
+
+static struct obstack obstack_stack_obstack;
+
+/* Addresses of first objects in some obstacks.
+ This is for freeing their entire contents. */
+char *maybepermanent_firstobj;
+char *temporary_firstobj;
+char *momentary_firstobj;
+char *temp_decl_firstobj;
+
+/* This is used to preserve objects (mainly array initializers) that need to
+ live until the end of the current function, but no further. */
+char *momentary_function_firstobj;
+
+/* Nonzero means all ..._TYPE nodes should be allocated permanently. */
+
+int all_types_permanent;
+
+/* Stack of places to restore the momentary obstack back to. */
+
+struct momentary_level
+{
+ /* Pointer back to previous such level. */
+ struct momentary_level *prev;
+ /* First object allocated within this level. */
+ char *base;
+ /* Value of expression_obstack saved at entry to this level. */
+ struct obstack *obstack;
+};
+
+struct momentary_level *momentary_stack;
+
+/* Table indexed by tree code giving a string containing a character
+ classifying the tree code. Possibilities are
+ t, d, s, c, r, <, 1, 2 and e. See tree.def for details. */
+
+#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) TYPE,
+
+char *standard_tree_code_type[] = {
+#include "tree.def"
+};
+#undef DEFTREECODE
+
+/* Table indexed by tree code giving number of expression
+ operands beyond the fixed part of the node structure.
+ Not used for types or decls. */
+
+#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) LENGTH,
+
+int standard_tree_code_length[] = {
+#include "tree.def"
+};
+#undef DEFTREECODE
+
+/* Names of tree components.
+ Used for printing out the tree and error messages. */
+#define DEFTREECODE(SYM, NAME, TYPE, LEN) NAME,
+
+char *standard_tree_code_name[] = {
+#include "tree.def"
+};
+#undef DEFTREECODE
+
+/* Table indexed by tree code giving a string containing a character
+ classifying the tree code. Possibilities are
+ t, d, s, c, r, e, <, 1 and 2. See tree.def for details. */
+
+char **tree_code_type;
+
+/* Table indexed by tree code giving number of expression
+ operands beyond the fixed part of the node structure.
+ Not used for types or decls. */
+
+int *tree_code_length;
+
+/* Table indexed by tree code giving name of tree code, as a string. */
+
+char **tree_code_name;
+
+/* Statistics-gathering stuff. */
+typedef enum
+{
+ d_kind,
+ t_kind,
+ b_kind,
+ s_kind,
+ r_kind,
+ e_kind,
+ c_kind,
+ id_kind,
+ op_id_kind,
+ perm_list_kind,
+ temp_list_kind,
+ vec_kind,
+ x_kind,
+ lang_decl,
+ lang_type,
+ all_kinds
+} tree_node_kind;
+
+int tree_node_counts[(int)all_kinds];
+int tree_node_sizes[(int)all_kinds];
+int id_string_size = 0;
+
+char *tree_node_kind_names[] = {
+ "decls",
+ "types",
+ "blocks",
+ "stmts",
+ "refs",
+ "exprs",
+ "constants",
+ "identifiers",
+ "op_identifiers",
+ "perm_tree_lists",
+ "temp_tree_lists",
+ "vecs",
+ "random kinds",
+ "lang_decl kinds",
+ "lang_type kinds"
+};
+
+/* Hash table for uniquizing IDENTIFIER_NODEs by name. */
+
+#define MAX_HASH_TABLE 1009
+static tree hash_table[MAX_HASH_TABLE]; /* id hash buckets */
+
+/* 0 while creating built-in identifiers. */
+static int do_identifier_warnings;
+
+/* Unique id for next decl created. */
+static int next_decl_uid;
+/* Unique id for next type created. */
+static int next_type_uid = 1;
+
+/* Here is how primitive or already-canonicalized types' hash
+ codes are made. */
+#define TYPE_HASH(TYPE) ((HOST_WIDE_INT) (TYPE) & 0777777)
+
+extern char *mode_name[];
+
+void gcc_obstack_init ();
+static tree stabilize_reference_1 ();
+
+/* Init the principal obstacks. */
+
+void
+init_obstacks ()
+{
+ gcc_obstack_init (&obstack_stack_obstack);
+ gcc_obstack_init (&permanent_obstack);
+
+ gcc_obstack_init (&temporary_obstack);
+ temporary_firstobj = (char *) obstack_alloc (&temporary_obstack, 0);
+ gcc_obstack_init (&momentary_obstack);
+ momentary_firstobj = (char *) obstack_alloc (&momentary_obstack, 0);
+ momentary_function_firstobj = momentary_firstobj;
+ gcc_obstack_init (&maybepermanent_obstack);
+ maybepermanent_firstobj
+ = (char *) obstack_alloc (&maybepermanent_obstack, 0);
+ gcc_obstack_init (&temp_decl_obstack);
+ temp_decl_firstobj = (char *) obstack_alloc (&temp_decl_obstack, 0);
+
+ function_obstack = &temporary_obstack;
+ function_maybepermanent_obstack = &maybepermanent_obstack;
+ current_obstack = &permanent_obstack;
+ expression_obstack = &permanent_obstack;
+ rtl_obstack = saveable_obstack = &permanent_obstack;
+
+ /* Init the hash table of identifiers. */
+ bzero ((char *) hash_table, sizeof hash_table);
+}
+
+void
+gcc_obstack_init (obstack)
+ struct obstack *obstack;
+{
+ /* Let particular systems override the size of a chunk. */
+#ifndef OBSTACK_CHUNK_SIZE
+#define OBSTACK_CHUNK_SIZE 0
+#endif
+ /* Let them override the alloc and free routines too. */
+#ifndef OBSTACK_CHUNK_ALLOC
+#define OBSTACK_CHUNK_ALLOC xmalloc
+#endif
+#ifndef OBSTACK_CHUNK_FREE
+#define OBSTACK_CHUNK_FREE free
+#endif
+ _obstack_begin (obstack, OBSTACK_CHUNK_SIZE, 0,
+ (void *(*) ()) OBSTACK_CHUNK_ALLOC,
+ (void (*) ()) OBSTACK_CHUNK_FREE);
+}
+
+/* Save all variables describing the current status into the structure *P.
+ This is used before starting a nested function. */
+
+void
+save_tree_status (p)
+ struct function *p;
+{
+ p->all_types_permanent = all_types_permanent;
+ p->momentary_stack = momentary_stack;
+ p->maybepermanent_firstobj = maybepermanent_firstobj;
+ p->momentary_firstobj = momentary_firstobj;
+ p->momentary_function_firstobj = momentary_function_firstobj;
+ p->function_obstack = function_obstack;
+ p->function_maybepermanent_obstack = function_maybepermanent_obstack;
+ p->current_obstack = current_obstack;
+ p->expression_obstack = expression_obstack;
+ p->saveable_obstack = saveable_obstack;
+ p->rtl_obstack = rtl_obstack;
+
+ /* Objects that need to be saved in this function can be in the nonsaved
+ obstack of the enclosing function since they can't possibly be needed
+ once it has returned. */
+ function_maybepermanent_obstack = function_obstack;
+
+ function_obstack = (struct obstack *) xmalloc (sizeof (struct obstack));
+ gcc_obstack_init (function_obstack);
+
+ current_obstack = &permanent_obstack;
+ expression_obstack = &permanent_obstack;
+ rtl_obstack = saveable_obstack = &permanent_obstack;
+
+ momentary_firstobj = (char *) obstack_finish (&momentary_obstack);
+ momentary_function_firstobj = momentary_firstobj;
+ maybepermanent_firstobj
+ = (char *) obstack_finish (function_maybepermanent_obstack);
+}
+
+/* Restore all variables describing the current status from the structure *P.
+ This is used after a nested function. */
+
+void
+restore_tree_status (p)
+ struct function *p;
+{
+ all_types_permanent = p->all_types_permanent;
+ momentary_stack = p->momentary_stack;
+
+ obstack_free (&momentary_obstack, momentary_function_firstobj);
+
+ /* Free saveable storage used by the function just compiled and not
+ saved.
+
+ CAUTION: This is in function_obstack of the containing function. So
+ we must be sure that we never allocate from that obstack during
+ the compilation of a nested function if we expect it to survive past the
+ nested function's end. */
+ obstack_free (function_maybepermanent_obstack, maybepermanent_firstobj);
+
+ obstack_free (function_obstack, 0);
+ free (function_obstack);
+
+ momentary_firstobj = p->momentary_firstobj;
+ momentary_function_firstobj = p->momentary_function_firstobj;
+ maybepermanent_firstobj = p->maybepermanent_firstobj;
+ function_obstack = p->function_obstack;
+ function_maybepermanent_obstack = p->function_maybepermanent_obstack;
+ current_obstack = p->current_obstack;
+ expression_obstack = p->expression_obstack;
+ saveable_obstack = p->saveable_obstack;
+ rtl_obstack = p->rtl_obstack;
+}
+
+/* Start allocating on the temporary (per function) obstack.
+ This is done in start_function before parsing the function body,
+ and before each initialization at top level, and to go back
+ to temporary allocation after doing permanent_allocation. */
+
+void
+temporary_allocation ()
+{
+ /* Note that function_obstack at top level points to temporary_obstack.
+ But within a nested function context, it is a separate obstack. */
+ current_obstack = function_obstack;
+ expression_obstack = function_obstack;
+ rtl_obstack = saveable_obstack = function_maybepermanent_obstack;
+ momentary_stack = 0;
+}
+
+/* Start allocating on the permanent obstack but don't
+ free the temporary data. After calling this, call
+ `permanent_allocation' to fully resume permanent allocation status. */
+
+void
+end_temporary_allocation ()
+{
+ current_obstack = &permanent_obstack;
+ expression_obstack = &permanent_obstack;
+ rtl_obstack = saveable_obstack = &permanent_obstack;
+}
+
+/* Resume allocating on the temporary obstack, undoing
+ effects of `end_temporary_allocation'. */
+
+void
+resume_temporary_allocation ()
+{
+ current_obstack = function_obstack;
+ expression_obstack = function_obstack;
+ rtl_obstack = saveable_obstack = function_maybepermanent_obstack;
+}
+
+/* While doing temporary allocation, switch to allocating in such a
+ way as to save all nodes if the function is inlined. Call
+ resume_temporary_allocation to go back to ordinary temporary
+ allocation. */
+
+void
+saveable_allocation ()
+{
+ /* Note that function_obstack at top level points to temporary_obstack.
+ But within a nested function context, it is a separate obstack. */
+ expression_obstack = current_obstack = saveable_obstack;
+}
+
+/* Switch to current obstack CURRENT and maybepermanent obstack SAVEABLE,
+ recording the previously current obstacks on a stack.
+ This does not free any storage in any obstack. */
+
+void
+push_obstacks (current, saveable)
+ struct obstack *current, *saveable;
+{
+ struct obstack_stack *p
+ = (struct obstack_stack *) obstack_alloc (&obstack_stack_obstack,
+ (sizeof (struct obstack_stack)));
+
+ p->current = current_obstack;
+ p->saveable = saveable_obstack;
+ p->expression = expression_obstack;
+ p->rtl = rtl_obstack;
+ p->next = obstack_stack;
+ obstack_stack = p;
+
+ current_obstack = current;
+ expression_obstack = current;
+ rtl_obstack = saveable_obstack = saveable;
+}
+
+/* Save the current set of obstacks, but don't change them. */
+
+void
+push_obstacks_nochange ()
+{
+ struct obstack_stack *p
+ = (struct obstack_stack *) obstack_alloc (&obstack_stack_obstack,
+ (sizeof (struct obstack_stack)));
+
+ p->current = current_obstack;
+ p->saveable = saveable_obstack;
+ p->expression = expression_obstack;
+ p->rtl = rtl_obstack;
+ p->next = obstack_stack;
+ obstack_stack = p;
+}
+
+/* Pop the obstack selection stack. */
+
+void
+pop_obstacks ()
+{
+ struct obstack_stack *p = obstack_stack;
+ obstack_stack = p->next;
+
+ current_obstack = p->current;
+ saveable_obstack = p->saveable;
+ expression_obstack = p->expression;
+ rtl_obstack = p->rtl;
+
+ obstack_free (&obstack_stack_obstack, p);
+}
+
+/* Nonzero if temporary allocation is currently in effect.
+ Zero if currently doing permanent allocation. */
+
+int
+allocation_temporary_p ()
+{
+ return current_obstack != &permanent_obstack;
+}
+
+/* Go back to allocating on the permanent obstack
+ and free everything in the temporary obstack.
+
+ FUNCTION_END is true only if we have just finished compiling a function.
+ In that case, we also free preserved initial values on the momentary
+ obstack. */
+
+void
+permanent_allocation (function_end)
+ int function_end;
+{
+ /* Free up previous temporary obstack data */
+ obstack_free (&temporary_obstack, temporary_firstobj);
+ if (function_end)
+ obstack_free (&momentary_obstack, momentary_function_firstobj);
+ else
+ obstack_free (&momentary_obstack, momentary_firstobj);
+ obstack_free (&maybepermanent_obstack, maybepermanent_firstobj);
+ obstack_free (&temp_decl_obstack, temp_decl_firstobj);
+
+ current_obstack = &permanent_obstack;
+ expression_obstack = &permanent_obstack;
+ rtl_obstack = saveable_obstack = &permanent_obstack;
+}
+
+/* Save permanently everything on the maybepermanent_obstack. */
+
+void
+preserve_data ()
+{
+ maybepermanent_firstobj
+ = (char *) obstack_alloc (function_maybepermanent_obstack, 0);
+}
+
+void
+preserve_initializer ()
+{
+ struct momentary_level *tem;
+ char *old_momentary;
+
+ temporary_firstobj
+ = (char *) obstack_alloc (&temporary_obstack, 0);
+ maybepermanent_firstobj
+ = (char *) obstack_alloc (function_maybepermanent_obstack, 0);
+
+ old_momentary = momentary_firstobj;
+ momentary_firstobj
+ = (char *) obstack_alloc (&momentary_obstack, 0);
+ if (momentary_firstobj != old_momentary)
+ for (tem = momentary_stack; tem; tem = tem->prev)
+ tem->base = momentary_firstobj;
+}
+
+/* Start allocating new rtl in current_obstack.
+ Use resume_temporary_allocation
+ to go back to allocating rtl in saveable_obstack. */
+
+void
+rtl_in_current_obstack ()
+{
+ rtl_obstack = current_obstack;
+}
+
+/* Start allocating rtl from saveable_obstack. Intended to be used after
+ a call to push_obstacks_nochange. */
+
+void
+rtl_in_saveable_obstack ()
+{
+ rtl_obstack = saveable_obstack;
+}
+
+/* Allocate SIZE bytes in the current obstack
+ and return a pointer to them.
+ In practice the current obstack is always the temporary one. */
+
+char *
+oballoc (size)
+ int size;
+{
+ return (char *) obstack_alloc (current_obstack, size);
+}
+
+/* Free the object PTR in the current obstack
+ as well as everything allocated since PTR.
+ In practice the current obstack is always the temporary one. */
+
+void
+obfree (ptr)
+ char *ptr;
+{
+ obstack_free (current_obstack, ptr);
+}
+
+/* Allocate SIZE bytes in the permanent obstack
+ and return a pointer to them. */
+
+char *
+permalloc (size)
+ int size;
+{
+ return (char *) obstack_alloc (&permanent_obstack, size);
+}
+
+/* Allocate NELEM items of SIZE bytes in the permanent obstack
+ and return a pointer to them. The storage is cleared before
+ returning the value. */
+
+char *
+perm_calloc (nelem, size)
+ int nelem;
+ long size;
+{
+ char *rval = (char *) obstack_alloc (&permanent_obstack, nelem * size);
+ bzero (rval, nelem * size);
+ return rval;
+}
+
+/* Allocate SIZE bytes in the saveable obstack
+ and return a pointer to them. */
+
+char *
+savealloc (size)
+ int size;
+{
+ return (char *) obstack_alloc (saveable_obstack, size);
+}
+
+/* Print out which obstack an object is in. */
+
+void
+print_obstack_name (object, file, prefix)
+ char *object;
+ FILE *file;
+ char *prefix;
+{
+ struct obstack *obstack = NULL;
+ char *obstack_name = NULL;
+ struct function *p;
+
+ for (p = outer_function_chain; p; p = p->next)
+ {
+ if (_obstack_allocated_p (p->function_obstack, object))
+ {
+ obstack = p->function_obstack;
+ obstack_name = "containing function obstack";
+ }
+ if (_obstack_allocated_p (p->function_maybepermanent_obstack, object))
+ {
+ obstack = p->function_maybepermanent_obstack;
+ obstack_name = "containing function maybepermanent obstack";
+ }
+ }
+
+ if (_obstack_allocated_p (&obstack_stack_obstack, object))
+ {
+ obstack = &obstack_stack_obstack;
+ obstack_name = "obstack_stack_obstack";
+ }
+ else if (_obstack_allocated_p (function_obstack, object))
+ {
+ obstack = function_obstack;
+ obstack_name = "function obstack";
+ }
+ else if (_obstack_allocated_p (&permanent_obstack, object))
+ {
+ obstack = &permanent_obstack;
+ obstack_name = "permanent_obstack";
+ }
+ else if (_obstack_allocated_p (&momentary_obstack, object))
+ {
+ obstack = &momentary_obstack;
+ obstack_name = "momentary_obstack";
+ }
+ else if (_obstack_allocated_p (function_maybepermanent_obstack, object))
+ {
+ obstack = function_maybepermanent_obstack;
+ obstack_name = "function maybepermanent obstack";
+ }
+ else if (_obstack_allocated_p (&temp_decl_obstack, object))
+ {
+ obstack = &temp_decl_obstack;
+ obstack_name = "temp_decl_obstack";
+ }
+
+ /* Check to see if the object is in the free area of the obstack. */
+ if (obstack != NULL)
+ {
+ if (object >= obstack->next_free
+ && object < obstack->chunk_limit)
+ fprintf (file, "%s in free portion of obstack %s",
+ prefix, obstack_name);
+ else
+ fprintf (file, "%s allocated from %s", prefix, obstack_name);
+ }
+ else
+ fprintf (file, "%s not allocated from any obstack", prefix);
+}
+
+void
+debug_obstack (object)
+ char *object;
+{
+ print_obstack_name (object, stderr, "object");
+ fprintf (stderr, ".\n");
+}
+
+/* Return 1 if OBJ is in the permanent obstack.
+ This is slow, and should be used only for debugging.
+ Use TREE_PERMANENT for other purposes. */
+
+int
+object_permanent_p (obj)
+ tree obj;
+{
+ return _obstack_allocated_p (&permanent_obstack, obj);
+}
+
+/* Start a level of momentary allocation.
+ In C, each compound statement has its own level
+ and that level is freed at the end of each statement.
+ All expression nodes are allocated in the momentary allocation level. */
+
+void
+push_momentary ()
+{
+ struct momentary_level *tem
+ = (struct momentary_level *) obstack_alloc (&momentary_obstack,
+ sizeof (struct momentary_level));
+ tem->prev = momentary_stack;
+ tem->base = (char *) obstack_base (&momentary_obstack);
+ tem->obstack = expression_obstack;
+ momentary_stack = tem;
+ expression_obstack = &momentary_obstack;
+}
+
+/* Free all the storage in the current momentary-allocation level.
+ In C, this happens at the end of each statement. */
+
+void
+clear_momentary ()
+{
+ obstack_free (&momentary_obstack, momentary_stack->base);
+}
+
+/* Discard a level of momentary allocation.
+ In C, this happens at the end of each compound statement.
+ Restore the status of expression node allocation
+ that was in effect before this level was created. */
+
+void
+pop_momentary ()
+{
+ struct momentary_level *tem = momentary_stack;
+ momentary_stack = tem->prev;
+ expression_obstack = tem->obstack;
+ /* We can't free TEM from the momentary_obstack, because there might
+ be objects above it which have been saved. We can free back to the
+ stack of the level we are popping off though. */
+ obstack_free (&momentary_obstack, tem->base);
+}
+
+/* Pop back to the previous level of momentary allocation,
+ but don't free any momentary data just yet. */
+
+void
+pop_momentary_nofree ()
+{
+ struct momentary_level *tem = momentary_stack;
+ momentary_stack = tem->prev;
+ expression_obstack = tem->obstack;
+}
+
+/* Call when starting to parse a declaration:
+ make expressions in the declaration last the length of the function.
+ Returns an argument that should be passed to resume_momentary later. */
+
+int
+suspend_momentary ()
+{
+ register int tem = expression_obstack == &momentary_obstack;
+ expression_obstack = saveable_obstack;
+ return tem;
+}
+
+/* Call when finished parsing a declaration:
+ restore the treatment of node-allocation that was
+ in effect before the suspension.
+ YES should be the value previously returned by suspend_momentary. */
+
+void
+resume_momentary (yes)
+ int yes;
+{
+ if (yes)
+ expression_obstack = &momentary_obstack;
+}
+
+/* Init the tables indexed by tree code.
+ Note that languages can add to these tables to define their own codes. */
+
+void
+init_tree_codes ()
+{
+ tree_code_type = (char **) xmalloc (sizeof (standard_tree_code_type));
+ tree_code_length = (int *) xmalloc (sizeof (standard_tree_code_length));
+ tree_code_name = (char **) xmalloc (sizeof (standard_tree_code_name));
+ bcopy ((char *) standard_tree_code_type, (char *) tree_code_type,
+ sizeof (standard_tree_code_type));
+ bcopy ((char *) standard_tree_code_length, (char *) tree_code_length,
+ sizeof (standard_tree_code_length));
+ bcopy ((char *) standard_tree_code_name, (char *) tree_code_name,
+ sizeof (standard_tree_code_name));
+}
+
+/* Return a newly allocated node of code CODE.
+ Initialize the node's unique id and its TREE_PERMANENT flag.
+ For decl and type nodes, some other fields are initialized.
+ The rest of the node is initialized to zero.
+
+ Achoo! I got a code in the node. */
+
+tree
+make_node (code)
+ enum tree_code code;
+{
+ register tree t;
+ register int type = TREE_CODE_CLASS (code);
+ register int length;
+ register struct obstack *obstack = current_obstack;
+ register int i;
+ register tree_node_kind kind;
+
+ switch (type)
+ {
+ case 'd': /* A decl node */
+#ifdef GATHER_STATISTICS
+ kind = d_kind;
+#endif
+ length = sizeof (struct tree_decl);
+ /* All decls in an inline function need to be saved. */
+ if (obstack != &permanent_obstack)
+ obstack = saveable_obstack;
+
+ /* PARM_DECLs go on the context of the parent. If this is a nested
+ function, then we must allocate the PARM_DECL on the parent's
+ obstack, so that they will live to the end of the parent's
+ closing brace. This is neccesary in case we try to inline the
+ function into its parent.
+
+ PARM_DECLs of top-level functions do not have this problem. However,
+ we allocate them where we put the FUNCTION_DECL for languauges such as
+ Ada that need to consult some flags in the PARM_DECLs of the function
+ when calling it.
+
+ See comment in restore_tree_status for why we can't put this
+ in function_obstack. */
+ if (code == PARM_DECL && obstack != &permanent_obstack)
+ {
+ tree context = 0;
+ if (current_function_decl)
+ context = decl_function_context (current_function_decl);
+
+ if (context)
+ obstack
+ = find_function_data (context)->function_maybepermanent_obstack;
+ }
+ break;
+
+ case 't': /* a type node */
+#ifdef GATHER_STATISTICS
+ kind = t_kind;
+#endif
+ length = sizeof (struct tree_type);
+ /* All data types are put where we can preserve them if nec. */
+ if (obstack != &permanent_obstack)
+ obstack = all_types_permanent ? &permanent_obstack : saveable_obstack;
+ break;
+
+ case 'b': /* a lexical block */
+#ifdef GATHER_STATISTICS
+ kind = b_kind;
+#endif
+ length = sizeof (struct tree_block);
+ /* All BLOCK nodes are put where we can preserve them if nec. */
+ if (obstack != &permanent_obstack)
+ obstack = saveable_obstack;
+ break;
+
+ case 's': /* an expression with side effects */
+#ifdef GATHER_STATISTICS
+ kind = s_kind;
+ goto usual_kind;
+#endif
+ case 'r': /* a reference */
+#ifdef GATHER_STATISTICS
+ kind = r_kind;
+ goto usual_kind;
+#endif
+ case 'e': /* an expression */
+ case '<': /* a comparison expression */
+ case '1': /* a unary arithmetic expression */
+ case '2': /* a binary arithmetic expression */
+#ifdef GATHER_STATISTICS
+ kind = e_kind;
+ usual_kind:
+#endif
+ obstack = expression_obstack;
+ /* All BIND_EXPR nodes are put where we can preserve them if nec. */
+ if (code == BIND_EXPR && obstack != &permanent_obstack)
+ obstack = saveable_obstack;
+ length = sizeof (struct tree_exp)
+ + (tree_code_length[(int) code] - 1) * sizeof (char *);
+ break;
+
+ case 'c': /* a constant */
+#ifdef GATHER_STATISTICS
+ kind = c_kind;
+#endif
+ obstack = expression_obstack;
+
+ /* We can't use tree_code_length for INTEGER_CST, since the number of
+ words is machine-dependent due to varying length of HOST_WIDE_INT,
+ which might be wider than a pointer (e.g., long long). Similarly
+ for REAL_CST, since the number of words is machine-dependent due
+ to varying size and alignment of `double'. */
+
+ if (code == INTEGER_CST)
+ length = sizeof (struct tree_int_cst);
+ else if (code == REAL_CST)
+ length = sizeof (struct tree_real_cst);
+ else
+ length = sizeof (struct tree_common)
+ + tree_code_length[(int) code] * sizeof (char *);
+ break;
+
+ case 'x': /* something random, like an identifier. */
+#ifdef GATHER_STATISTICS
+ if (code == IDENTIFIER_NODE)
+ kind = id_kind;
+ else if (code == OP_IDENTIFIER)
+ kind = op_id_kind;
+ else if (code == TREE_VEC)
+ kind = vec_kind;
+ else
+ kind = x_kind;
+#endif
+ length = sizeof (struct tree_common)
+ + tree_code_length[(int) code] * sizeof (char *);
+ /* Identifier nodes are always permanent since they are
+ unique in a compiler run. */
+ if (code == IDENTIFIER_NODE) obstack = &permanent_obstack;
+ break;
+
+ default:
+ abort ();
+ }
+
+ t = (tree) obstack_alloc (obstack, length);
+
+#ifdef GATHER_STATISTICS
+ tree_node_counts[(int)kind]++;
+ tree_node_sizes[(int)kind] += length;
+#endif
+
+ /* Clear a word at a time. */
+ for (i = (length / sizeof (int)) - 1; i >= 0; i--)
+ ((int *) t)[i] = 0;
+ /* Clear any extra bytes. */
+ for (i = length / sizeof (int) * sizeof (int); i < length; i++)
+ ((char *) t)[i] = 0;
+
+ TREE_SET_CODE (t, code);
+ if (obstack == &permanent_obstack)
+ TREE_PERMANENT (t) = 1;
+
+ switch (type)
+ {
+ case 's':
+ TREE_SIDE_EFFECTS (t) = 1;
+ TREE_TYPE (t) = void_type_node;
+ break;
+
+ case 'd':
+ if (code != FUNCTION_DECL)
+ DECL_ALIGN (t) = 1;
+ DECL_IN_SYSTEM_HEADER (t)
+ = in_system_header && (obstack == &permanent_obstack);
+ DECL_SOURCE_LINE (t) = lineno;
+ DECL_SOURCE_FILE (t) = (input_filename) ? input_filename : "<built-in>";
+ DECL_UID (t) = next_decl_uid++;
+ break;
+
+ case 't':
+ TYPE_UID (t) = next_type_uid++;
+ TYPE_ALIGN (t) = 1;
+ TYPE_MAIN_VARIANT (t) = t;
+ TYPE_OBSTACK (t) = obstack;
+ TYPE_ATTRIBUTES (t) = NULL_TREE;
+#ifdef SET_DEFAULT_TYPE_ATTRIBUTES
+ SET_DEFAULT_TYPE_ATTRIBUTES (t);
+#endif
+ break;
+
+ case 'c':
+ TREE_CONSTANT (t) = 1;
+ break;
+ }
+
+ return t;
+}
+
+/* Return a new node with the same contents as NODE
+ except that its TREE_CHAIN is zero and it has a fresh uid. */
+
+tree
+copy_node (node)
+ tree node;
+{
+ register tree t;
+ register enum tree_code code = TREE_CODE (node);
+ register int length;
+ register int i;
+
+ switch (TREE_CODE_CLASS (code))
+ {
+ case 'd': /* A decl node */
+ length = sizeof (struct tree_decl);
+ break;
+
+ case 't': /* a type node */
+ length = sizeof (struct tree_type);
+ break;
+
+ case 'b': /* a lexical block node */
+ length = sizeof (struct tree_block);
+ break;
+
+ case 'r': /* a reference */
+ case 'e': /* an expression */
+ case 's': /* an expression with side effects */
+ case '<': /* a comparison expression */
+ case '1': /* a unary arithmetic expression */
+ case '2': /* a binary arithmetic expression */
+ length = sizeof (struct tree_exp)
+ + (tree_code_length[(int) code] - 1) * sizeof (char *);
+ break;
+
+ case 'c': /* a constant */
+ /* We can't use tree_code_length for INTEGER_CST, since the number of
+ words is machine-dependent due to varying length of HOST_WIDE_INT,
+ which might be wider than a pointer (e.g., long long). Similarly
+ for REAL_CST, since the number of words is machine-dependent due
+ to varying size and alignment of `double'. */
+ if (code == INTEGER_CST)
+ {
+ length = sizeof (struct tree_int_cst);
+ break;
+ }
+ else if (code == REAL_CST)
+ {
+ length = sizeof (struct tree_real_cst);
+ break;
+ }
+
+ case 'x': /* something random, like an identifier. */
+ length = sizeof (struct tree_common)
+ + tree_code_length[(int) code] * sizeof (char *);
+ if (code == TREE_VEC)
+ length += (TREE_VEC_LENGTH (node) - 1) * sizeof (char *);
+ }
+
+ t = (tree) obstack_alloc (current_obstack, length);
+
+ for (i = (length / sizeof (int)) - 1; i >= 0; i--)
+ ((int *) t)[i] = ((int *) node)[i];
+ /* Clear any extra bytes. */
+ for (i = length / sizeof (int) * sizeof (int); i < length; i++)
+ ((char *) t)[i] = ((char *) node)[i];
+
+ TREE_CHAIN (t) = 0;
+
+ if (TREE_CODE_CLASS (code) == 'd')
+ DECL_UID (t) = next_decl_uid++;
+ else if (TREE_CODE_CLASS (code) == 't')
+ {
+ TYPE_UID (t) = next_type_uid++;
+ TYPE_OBSTACK (t) = current_obstack;
+ }
+
+ TREE_PERMANENT (t) = (current_obstack == &permanent_obstack);
+
+ return t;
+}
+
+/* Return a copy of a chain of nodes, chained through the TREE_CHAIN field.
+ For example, this can copy a list made of TREE_LIST nodes. */
+
+tree
+copy_list (list)
+ tree list;
+{
+ tree head;
+ register tree prev, next;
+
+ if (list == 0)
+ return 0;
+
+ head = prev = copy_node (list);
+ next = TREE_CHAIN (list);
+ while (next)
+ {
+ TREE_CHAIN (prev) = copy_node (next);
+ prev = TREE_CHAIN (prev);
+ next = TREE_CHAIN (next);
+ }
+ return head;
+}
+
+#define HASHBITS 30
+
+/* Return an IDENTIFIER_NODE whose name is TEXT (a null-terminated string).
+ If an identifier with that name has previously been referred to,
+ the same node is returned this time. */
+
+tree
+get_identifier (text)
+ register char *text;
+{
+ register int hi;
+ register int i;
+ register tree idp;
+ register int len, hash_len;
+
+ /* Compute length of text in len. */
+ for (len = 0; text[len]; len++);
+
+ /* Decide how much of that length to hash on */
+ hash_len = len;
+ if (warn_id_clash && len > id_clash_len)
+ hash_len = id_clash_len;
+
+ /* Compute hash code */
+ hi = hash_len * 613 + (unsigned)text[0];
+ for (i = 1; i < hash_len; i += 2)
+ hi = ((hi * 613) + (unsigned)(text[i]));
+
+ hi &= (1 << HASHBITS) - 1;
+ hi %= MAX_HASH_TABLE;
+
+ /* Search table for identifier */
+ for (idp = hash_table[hi]; idp; idp = TREE_CHAIN (idp))
+ if (IDENTIFIER_LENGTH (idp) == len
+ && IDENTIFIER_POINTER (idp)[0] == text[0]
+ && !bcmp (IDENTIFIER_POINTER (idp), text, len))
+ return idp; /* <-- return if found */
+
+ /* Not found; optionally warn about a similar identifier */
+ if (warn_id_clash && do_identifier_warnings && len >= id_clash_len)
+ for (idp = hash_table[hi]; idp; idp = TREE_CHAIN (idp))
+ if (!strncmp (IDENTIFIER_POINTER (idp), text, id_clash_len))
+ {
+ warning ("`%s' and `%s' identical in first %d characters",
+ IDENTIFIER_POINTER (idp), text, id_clash_len);
+ break;
+ }
+
+ if (tree_code_length[(int) IDENTIFIER_NODE] < 0)
+ abort (); /* set_identifier_size hasn't been called. */
+
+ /* Not found, create one, add to chain */
+ idp = make_node (IDENTIFIER_NODE);
+ IDENTIFIER_LENGTH (idp) = len;
+#ifdef GATHER_STATISTICS
+ id_string_size += len;
+#endif
+
+ IDENTIFIER_POINTER (idp) = obstack_copy0 (&permanent_obstack, text, len);
+
+ TREE_CHAIN (idp) = hash_table[hi];
+ hash_table[hi] = idp;
+ return idp; /* <-- return if created */
+}
+
+/* Enable warnings on similar identifiers (if requested).
+ Done after the built-in identifiers are created. */
+
+void
+start_identifier_warnings ()
+{
+ do_identifier_warnings = 1;
+}
+
+/* Record the size of an identifier node for the language in use.
+ SIZE is the total size in bytes.
+ This is called by the language-specific files. This must be
+ called before allocating any identifiers. */
+
+void
+set_identifier_size (size)
+ int size;
+{
+ tree_code_length[(int) IDENTIFIER_NODE]
+ = (size - sizeof (struct tree_common)) / sizeof (tree);
+}
+
+/* Return a newly constructed INTEGER_CST node whose constant value
+ is specified by the two ints LOW and HI.
+ The TREE_TYPE is set to `int'.
+
+ This function should be used via the `build_int_2' macro. */
+
+tree
+build_int_2_wide (low, hi)
+ HOST_WIDE_INT low, hi;
+{
+ register tree t = make_node (INTEGER_CST);
+ TREE_INT_CST_LOW (t) = low;
+ TREE_INT_CST_HIGH (t) = hi;
+ TREE_TYPE (t) = integer_type_node;
+ return t;
+}
+
+/* Return a new REAL_CST node whose type is TYPE and value is D. */
+
+tree
+build_real (type, d)
+ tree type;
+ REAL_VALUE_TYPE d;
+{
+ tree v;
+ int overflow = 0;
+
+ /* Check for valid float value for this type on this target machine;
+ if not, can print error message and store a valid value in D. */
+#ifdef CHECK_FLOAT_VALUE
+ CHECK_FLOAT_VALUE (TYPE_MODE (type), d, overflow);
+#endif
+
+ v = make_node (REAL_CST);
+ TREE_TYPE (v) = type;
+ TREE_REAL_CST (v) = d;
+ TREE_OVERFLOW (v) = TREE_CONSTANT_OVERFLOW (v) = overflow;
+ return v;
+}
+
+/* Return a new REAL_CST node whose type is TYPE
+ and whose value is the integer value of the INTEGER_CST node I. */
+
+#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
+
+REAL_VALUE_TYPE
+real_value_from_int_cst (i)
+ tree i;
+{
+ REAL_VALUE_TYPE d;
+ REAL_VALUE_TYPE e;
+ /* Some 386 compilers mishandle unsigned int to float conversions,
+ so introduce a temporary variable E to avoid those bugs. */
+
+#ifdef REAL_ARITHMETIC
+ if (! TREE_UNSIGNED (TREE_TYPE (i)))
+ REAL_VALUE_FROM_INT (d, TREE_INT_CST_LOW (i), TREE_INT_CST_HIGH (i));
+ else
+ REAL_VALUE_FROM_UNSIGNED_INT (d, TREE_INT_CST_LOW (i), TREE_INT_CST_HIGH (i));
+#else /* not REAL_ARITHMETIC */
+ if (TREE_INT_CST_HIGH (i) < 0 && ! TREE_UNSIGNED (TREE_TYPE (i)))
+ {
+ d = (double) (~ TREE_INT_CST_HIGH (i));
+ e = ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))
+ * (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)));
+ d *= e;
+ e = (double) (unsigned HOST_WIDE_INT) (~ TREE_INT_CST_LOW (i));
+ d += e;
+ d = (- d - 1.0);
+ }
+ else
+ {
+ d = (double) (unsigned HOST_WIDE_INT) TREE_INT_CST_HIGH (i);
+ e = ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))
+ * (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)));
+ d *= e;
+ e = (double) (unsigned HOST_WIDE_INT) TREE_INT_CST_LOW (i);
+ d += e;
+ }
+#endif /* not REAL_ARITHMETIC */
+ return d;
+}
+
+/* This function can't be implemented if we can't do arithmetic
+ on the float representation. */
+
+tree
+build_real_from_int_cst (type, i)
+ tree type;
+ tree i;
+{
+ tree v;
+ int overflow = TREE_OVERFLOW (i);
+ REAL_VALUE_TYPE d;
+ jmp_buf float_error;
+
+ v = make_node (REAL_CST);
+ TREE_TYPE (v) = type;
+
+ if (setjmp (float_error))
+ {
+ d = dconst0;
+ overflow = 1;
+ goto got_it;
+ }
+
+ set_float_handler (float_error);
+
+ d = REAL_VALUE_TRUNCATE (TYPE_MODE (type), real_value_from_int_cst (i));
+
+ /* Check for valid float value for this type on this target machine. */
+
+ got_it:
+ set_float_handler (NULL_PTR);
+
+#ifdef CHECK_FLOAT_VALUE
+ CHECK_FLOAT_VALUE (TYPE_MODE (type), d, overflow);
+#endif
+
+ TREE_REAL_CST (v) = d;
+ TREE_OVERFLOW (v) = TREE_CONSTANT_OVERFLOW (v) = overflow;
+ return v;
+}
+
+#endif /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
+
+/* Return a newly constructed STRING_CST node whose value is
+ the LEN characters at STR.
+ The TREE_TYPE is not initialized. */
+
+tree
+build_string (len, str)
+ int len;
+ char *str;
+{
+ /* Put the string in saveable_obstack since it will be placed in the RTL
+ for an "asm" statement and will also be kept around a while if
+ deferring constant output in varasm.c. */
+
+ register tree s = make_node (STRING_CST);
+ TREE_STRING_LENGTH (s) = len;
+ TREE_STRING_POINTER (s) = obstack_copy0 (saveable_obstack, str, len);
+ return s;
+}
+
+/* Return a newly constructed COMPLEX_CST node whose value is
+ specified by the real and imaginary parts REAL and IMAG.
+ Both REAL and IMAG should be constant nodes.
+ The TREE_TYPE is not initialized. */
+
+tree
+build_complex (real, imag)
+ tree real, imag;
+{
+ register tree t = make_node (COMPLEX_CST);
+
+ TREE_REALPART (t) = real;
+ TREE_IMAGPART (t) = imag;
+ TREE_TYPE (t) = build_complex_type (TREE_TYPE (real));
+ TREE_OVERFLOW (t) = TREE_OVERFLOW (real) | TREE_OVERFLOW (imag);
+ TREE_CONSTANT_OVERFLOW (t)
+ = TREE_CONSTANT_OVERFLOW (real) | TREE_CONSTANT_OVERFLOW (imag);
+ return t;
+}
+
+/* Build a newly constructed TREE_VEC node of length LEN. */
+tree
+make_tree_vec (len)
+ int len;
+{
+ register tree t;
+ register int length = (len-1) * sizeof (tree) + sizeof (struct tree_vec);
+ register struct obstack *obstack = current_obstack;
+ register int i;
+
+#ifdef GATHER_STATISTICS
+ tree_node_counts[(int)vec_kind]++;
+ tree_node_sizes[(int)vec_kind] += length;
+#endif
+
+ t = (tree) obstack_alloc (obstack, length);
+
+ for (i = (length / sizeof (int)) - 1; i >= 0; i--)
+ ((int *) t)[i] = 0;
+
+ TREE_SET_CODE (t, TREE_VEC);
+ TREE_VEC_LENGTH (t) = len;
+ if (obstack == &permanent_obstack)
+ TREE_PERMANENT (t) = 1;
+
+ return t;
+}
+
+/* Return 1 if EXPR is the integer constant zero. */
+
+int
+integer_zerop (expr)
+ tree expr;
+{
+ STRIP_NOPS (expr);
+
+ return (TREE_CODE (expr) == INTEGER_CST
+ && TREE_INT_CST_LOW (expr) == 0
+ && TREE_INT_CST_HIGH (expr) == 0);
+}
+
+/* Return 1 if EXPR is the integer constant one. */
+
+int
+integer_onep (expr)
+ tree expr;
+{
+ STRIP_NOPS (expr);
+
+ return (TREE_CODE (expr) == INTEGER_CST
+ && TREE_INT_CST_LOW (expr) == 1
+ && TREE_INT_CST_HIGH (expr) == 0);
+}
+
+/* Return 1 if EXPR is an integer containing all 1's
+ in as much precision as it contains. */
+
+int
+integer_all_onesp (expr)
+ tree expr;
+{
+ register int prec;
+ register int uns;
+
+ STRIP_NOPS (expr);
+
+ if (TREE_CODE (expr) != INTEGER_CST)
+ return 0;
+
+ uns = TREE_UNSIGNED (TREE_TYPE (expr));
+ if (!uns)
+ return TREE_INT_CST_LOW (expr) == -1 && TREE_INT_CST_HIGH (expr) == -1;
+
+ prec = TYPE_PRECISION (TREE_TYPE (expr));
+ if (prec >= HOST_BITS_PER_WIDE_INT)
+ {
+ int high_value, shift_amount;
+
+ shift_amount = prec - HOST_BITS_PER_WIDE_INT;
+
+ if (shift_amount > HOST_BITS_PER_WIDE_INT)
+ /* Can not handle precisions greater than twice the host int size. */
+ abort ();
+ else if (shift_amount == HOST_BITS_PER_WIDE_INT)
+ /* Shifting by the host word size is undefined according to the ANSI
+ standard, so we must handle this as a special case. */
+ high_value = -1;
+ else
+ high_value = ((HOST_WIDE_INT) 1 << shift_amount) - 1;
+
+ return TREE_INT_CST_LOW (expr) == -1
+ && TREE_INT_CST_HIGH (expr) == high_value;
+ }
+ else
+ return TREE_INT_CST_LOW (expr) == ((HOST_WIDE_INT) 1 << prec) - 1;
+}
+
+/* Return 1 if EXPR is an integer constant that is a power of 2 (i.e., has only
+ one bit on). */
+
+int
+integer_pow2p (expr)
+ tree expr;
+{
+ HOST_WIDE_INT high, low;
+
+ STRIP_NOPS (expr);
+
+ if (TREE_CODE (expr) != INTEGER_CST)
+ return 0;
+
+ high = TREE_INT_CST_HIGH (expr);
+ low = TREE_INT_CST_LOW (expr);
+
+ if (high == 0 && low == 0)
+ return 0;
+
+ return ((high == 0 && (low & (low - 1)) == 0)
+ || (low == 0 && (high & (high - 1)) == 0));
+}
+
+/* Return 1 if EXPR is the real constant zero. */
+
+int
+real_zerop (expr)
+ tree expr;
+{
+ STRIP_NOPS (expr);
+
+ return (TREE_CODE (expr) == REAL_CST
+ && REAL_VALUES_EQUAL (TREE_REAL_CST (expr), dconst0));
+}
+
+/* Return 1 if EXPR is the real constant one. */
+
+int
+real_onep (expr)
+ tree expr;
+{
+ STRIP_NOPS (expr);
+
+ return (TREE_CODE (expr) == REAL_CST
+ && REAL_VALUES_EQUAL (TREE_REAL_CST (expr), dconst1));
+}
+
+/* Return 1 if EXPR is the real constant two. */
+
+int
+real_twop (expr)
+ tree expr;
+{
+ STRIP_NOPS (expr);
+
+ return (TREE_CODE (expr) == REAL_CST
+ && REAL_VALUES_EQUAL (TREE_REAL_CST (expr), dconst2));
+}
+
+/* Nonzero if EXP is a constant or a cast of a constant. */
+
+int
+really_constant_p (exp)
+ tree exp;
+{
+ /* This is not quite the same as STRIP_NOPS. It does more. */
+ while (TREE_CODE (exp) == NOP_EXPR
+ || TREE_CODE (exp) == CONVERT_EXPR
+ || TREE_CODE (exp) == NON_LVALUE_EXPR)
+ exp = TREE_OPERAND (exp, 0);
+ return TREE_CONSTANT (exp);
+}
+
+/* Return first list element whose TREE_VALUE is ELEM.
+ Return 0 if ELEM is not it LIST. */
+
+tree
+value_member (elem, list)
+ tree elem, list;
+{
+ while (list)
+ {
+ if (elem == TREE_VALUE (list))
+ return list;
+ list = TREE_CHAIN (list);
+ }
+ return NULL_TREE;
+}
+
+/* Return first list element whose TREE_PURPOSE is ELEM.
+ Return 0 if ELEM is not it LIST. */
+
+tree
+purpose_member (elem, list)
+ tree elem, list;
+{
+ while (list)
+ {
+ if (elem == TREE_PURPOSE (list))
+ return list;
+ list = TREE_CHAIN (list);
+ }
+ return NULL_TREE;
+}
+
+/* Return first list element whose BINFO_TYPE is ELEM.
+ Return 0 if ELEM is not it LIST. */
+
+tree
+binfo_member (elem, list)
+ tree elem, list;
+{
+ while (list)
+ {
+ if (elem == BINFO_TYPE (list))
+ return list;
+ list = TREE_CHAIN (list);
+ }
+ return NULL_TREE;
+}
+
+/* Return nonzero if ELEM is part of the chain CHAIN. */
+
+int
+chain_member (elem, chain)
+ tree elem, chain;
+{
+ while (chain)
+ {
+ if (elem == chain)
+ return 1;
+ chain = TREE_CHAIN (chain);
+ }
+
+ return 0;
+}
+
+/* Return the length of a chain of nodes chained through TREE_CHAIN.
+ We expect a null pointer to mark the end of the chain.
+ This is the Lisp primitive `length'. */
+
+int
+list_length (t)
+ tree t;
+{
+ register tree tail;
+ register int len = 0;
+
+ for (tail = t; tail; tail = TREE_CHAIN (tail))
+ len++;
+
+ return len;
+}
+
+/* Concatenate two chains of nodes (chained through TREE_CHAIN)
+ by modifying the last node in chain 1 to point to chain 2.
+ This is the Lisp primitive `nconc'. */
+
+tree
+chainon (op1, op2)
+ tree op1, op2;
+{
+
+ if (op1)
+ {
+ register tree t1;
+ register tree t2;
+
+ for (t1 = op1; TREE_CHAIN (t1); t1 = TREE_CHAIN (t1))
+ ;
+ TREE_CHAIN (t1) = op2;
+ for (t2 = op2; t2; t2 = TREE_CHAIN (t2))
+ if (t2 == t1)
+ abort (); /* Circularity created. */
+ return op1;
+ }
+ else return op2;
+}
+
+/* Return the last node in a chain of nodes (chained through TREE_CHAIN). */
+
+tree
+tree_last (chain)
+ register tree chain;
+{
+ register tree next;
+ if (chain)
+ while (next = TREE_CHAIN (chain))
+ chain = next;
+ return chain;
+}
+
+/* Reverse the order of elements in the chain T,
+ and return the new head of the chain (old last element). */
+
+tree
+nreverse (t)
+ tree t;
+{
+ register tree prev = 0, decl, next;
+ for (decl = t; decl; decl = next)
+ {
+ next = TREE_CHAIN (decl);
+ TREE_CHAIN (decl) = prev;
+ prev = decl;
+ }
+ return prev;
+}
+
+/* Given a chain CHAIN of tree nodes,
+ construct and return a list of those nodes. */
+
+tree
+listify (chain)
+ tree chain;
+{
+ tree result = NULL_TREE;
+ tree in_tail = chain;
+ tree out_tail = NULL_TREE;
+
+ while (in_tail)
+ {
+ tree next = tree_cons (NULL_TREE, in_tail, NULL_TREE);
+ if (out_tail)
+ TREE_CHAIN (out_tail) = next;
+ else
+ result = next;
+ out_tail = next;
+ in_tail = TREE_CHAIN (in_tail);
+ }
+
+ return result;
+}
+
+/* Return a newly created TREE_LIST node whose
+ purpose and value fields are PARM and VALUE. */
+
+tree
+build_tree_list (parm, value)
+ tree parm, value;
+{
+ register tree t = make_node (TREE_LIST);
+ TREE_PURPOSE (t) = parm;
+ TREE_VALUE (t) = value;
+ return t;
+}
+
+/* Similar, but build on the temp_decl_obstack. */
+
+tree
+build_decl_list (parm, value)
+ tree parm, value;
+{
+ register tree node;
+ register struct obstack *ambient_obstack = current_obstack;
+ current_obstack = &temp_decl_obstack;
+ node = build_tree_list (parm, value);
+ current_obstack = ambient_obstack;
+ return node;
+}
+
+/* Return a newly created TREE_LIST node whose
+ purpose and value fields are PARM and VALUE
+ and whose TREE_CHAIN is CHAIN. */
+
+tree
+tree_cons (purpose, value, chain)
+ tree purpose, value, chain;
+{
+#if 0
+ register tree node = make_node (TREE_LIST);
+#else
+ register int i;
+ register tree node = (tree) obstack_alloc (current_obstack, sizeof (struct tree_list));
+#ifdef GATHER_STATISTICS
+ tree_node_counts[(int)x_kind]++;
+ tree_node_sizes[(int)x_kind] += sizeof (struct tree_list);
+#endif
+
+ for (i = (sizeof (struct tree_common) / sizeof (int)) - 1; i >= 0; i--)
+ ((int *) node)[i] = 0;
+
+ TREE_SET_CODE (node, TREE_LIST);
+ if (current_obstack == &permanent_obstack)
+ TREE_PERMANENT (node) = 1;
+#endif
+
+ TREE_CHAIN (node) = chain;
+ TREE_PURPOSE (node) = purpose;
+ TREE_VALUE (node) = value;
+ return node;
+}
+
+/* Similar, but build on the temp_decl_obstack. */
+
+tree
+decl_tree_cons (purpose, value, chain)
+ tree purpose, value, chain;
+{
+ register tree node;
+ register struct obstack *ambient_obstack = current_obstack;
+ current_obstack = &temp_decl_obstack;
+ node = tree_cons (purpose, value, chain);
+ current_obstack = ambient_obstack;
+ return node;
+}
+
+/* Same as `tree_cons' but make a permanent object. */
+
+tree
+perm_tree_cons (purpose, value, chain)
+ tree purpose, value, chain;
+{
+ register tree node;
+ register struct obstack *ambient_obstack = current_obstack;
+ current_obstack = &permanent_obstack;
+
+ node = tree_cons (purpose, value, chain);
+ current_obstack = ambient_obstack;
+ return node;
+}
+
+/* Same as `tree_cons', but make this node temporary, regardless. */
+
+tree
+temp_tree_cons (purpose, value, chain)
+ tree purpose, value, chain;
+{
+ register tree node;
+ register struct obstack *ambient_obstack = current_obstack;
+ current_obstack = &temporary_obstack;
+
+ node = tree_cons (purpose, value, chain);
+ current_obstack = ambient_obstack;
+ return node;
+}
+
+/* Same as `tree_cons', but save this node if the function's RTL is saved. */
+
+tree
+saveable_tree_cons (purpose, value, chain)
+ tree purpose, value, chain;
+{
+ register tree node;
+ register struct obstack *ambient_obstack = current_obstack;
+ current_obstack = saveable_obstack;
+
+ node = tree_cons (purpose, value, chain);
+ current_obstack = ambient_obstack;
+ return node;
+}
+
+/* Return the size nominally occupied by an object of type TYPE
+ when it resides in memory. The value is measured in units of bytes,
+ and its data type is that normally used for type sizes
+ (which is the first type created by make_signed_type or
+ make_unsigned_type). */
+
+tree
+size_in_bytes (type)
+ tree type;
+{
+ tree t;
+
+ if (type == error_mark_node)
+ return integer_zero_node;
+ type = TYPE_MAIN_VARIANT (type);
+ if (TYPE_SIZE (type) == 0)
+ {
+ incomplete_type_error (NULL_TREE, type);
+ return integer_zero_node;
+ }
+ t = size_binop (CEIL_DIV_EXPR, TYPE_SIZE (type),
+ size_int (BITS_PER_UNIT));
+ if (TREE_CODE (t) == INTEGER_CST)
+ force_fit_type (t, 0);
+ return t;
+}
+
+/* Return the size of TYPE (in bytes) as an integer,
+ or return -1 if the size can vary. */
+
+int
+int_size_in_bytes (type)
+ tree type;
+{
+ unsigned int size;
+ if (type == error_mark_node)
+ return 0;
+ type = TYPE_MAIN_VARIANT (type);
+ if (TYPE_SIZE (type) == 0)
+ return -1;
+ if (TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+ return -1;
+ if (TREE_INT_CST_HIGH (TYPE_SIZE (type)) != 0)
+ {
+ tree t = size_binop (CEIL_DIV_EXPR, TYPE_SIZE (type),
+ size_int (BITS_PER_UNIT));
+ return TREE_INT_CST_LOW (t);
+ }
+ size = TREE_INT_CST_LOW (TYPE_SIZE (type));
+ return (size + BITS_PER_UNIT - 1) / BITS_PER_UNIT;
+}
+
+/* Return, as a tree node, the number of elements for TYPE (which is an
+ ARRAY_TYPE) minus one. This counts only elements of the top array. */
+
+tree
+array_type_nelts (type)
+ tree type;
+{
+ tree index_type = TYPE_DOMAIN (type);
+
+ return (integer_zerop (TYPE_MIN_VALUE (index_type))
+ ? TYPE_MAX_VALUE (index_type)
+ : fold (build (MINUS_EXPR, TREE_TYPE (TYPE_MAX_VALUE (index_type)),
+ TYPE_MAX_VALUE (index_type),
+ TYPE_MIN_VALUE (index_type))));
+}
+
+/* Return nonzero if arg is static -- a reference to an object in
+ static storage. This is not the same as the C meaning of `static'. */
+
+int
+staticp (arg)
+ tree arg;
+{
+ switch (TREE_CODE (arg))
+ {
+ case FUNCTION_DECL:
+ /* Nested functions aren't static. Since taking their address
+ involves a trampoline. */
+ if (decl_function_context (arg) != 0)
+ return 0;
+ /* ... fall through ... */
+ case VAR_DECL:
+ return TREE_STATIC (arg) || DECL_EXTERNAL (arg);
+
+ case CONSTRUCTOR:
+ return TREE_STATIC (arg);
+
+ case STRING_CST:
+ return 1;
+
+ case COMPONENT_REF:
+ case BIT_FIELD_REF:
+ return staticp (TREE_OPERAND (arg, 0));
+
+ case INDIRECT_REF:
+ return TREE_CONSTANT (TREE_OPERAND (arg, 0));
+
+ case ARRAY_REF:
+ if (TREE_CODE (TYPE_SIZE (TREE_TYPE (arg))) == INTEGER_CST
+ && TREE_CODE (TREE_OPERAND (arg, 1)) == INTEGER_CST)
+ return staticp (TREE_OPERAND (arg, 0));
+ }
+
+ return 0;
+}
+
+/* Wrap a SAVE_EXPR around EXPR, if appropriate.
+ Do this to any expression which may be used in more than one place,
+ but must be evaluated only once.
+
+ Normally, expand_expr would reevaluate the expression each time.
+ Calling save_expr produces something that is evaluated and recorded
+ the first time expand_expr is called on it. Subsequent calls to
+ expand_expr just reuse the recorded value.
+
+ The call to expand_expr that generates code that actually computes
+ the value is the first call *at compile time*. Subsequent calls
+ *at compile time* generate code to use the saved value.
+ This produces correct result provided that *at run time* control
+ always flows through the insns made by the first expand_expr
+ before reaching the other places where the save_expr was evaluated.
+ You, the caller of save_expr, must make sure this is so.
+
+ Constants, and certain read-only nodes, are returned with no
+ SAVE_EXPR because that is safe. Expressions containing placeholders
+ are not touched; see tree.def for an explanation of what these
+ are used for. */
+
+tree
+save_expr (expr)
+ tree expr;
+{
+ register tree t = fold (expr);
+
+ /* We don't care about whether this can be used as an lvalue in this
+ context. */
+ while (TREE_CODE (t) == NON_LVALUE_EXPR)
+ t = TREE_OPERAND (t, 0);
+
+ /* If the tree evaluates to a constant, then we don't want to hide that
+ fact (i.e. this allows further folding, and direct checks for constants).
+ However, a read-only object that has side effects cannot be bypassed.
+ Since it is no problem to reevaluate literals, we just return the
+ literal node. */
+
+ if (TREE_CONSTANT (t) || (TREE_READONLY (t) && ! TREE_SIDE_EFFECTS (t))
+ || TREE_CODE (t) == SAVE_EXPR)
+ return t;
+
+ /* If T contains a PLACEHOLDER_EXPR, we must evaluate it each time, since
+ it means that the size or offset of some field of an object depends on
+ the value within another field.
+
+ Note that it must not be the case that T contains both a PLACEHOLDER_EXPR
+ and some variable since it would then need to be both evaluated once and
+ evaluated more than once. Front-ends must assure this case cannot
+ happen by surrounding any such subexpressions in their own SAVE_EXPR
+ and forcing evaluation at the proper time. */
+ if (contains_placeholder_p (t))
+ return t;
+
+ t = build (SAVE_EXPR, TREE_TYPE (expr), t, current_function_decl, NULL_TREE);
+
+ /* This expression might be placed ahead of a jump to ensure that the
+ value was computed on both sides of the jump. So make sure it isn't
+ eliminated as dead. */
+ TREE_SIDE_EFFECTS (t) = 1;
+ return t;
+}
+
+/* Return 1 if EXP contains a PLACEHOLDER_EXPR; i.e., if it represents a size
+ or offset that depends on a field within a record.
+
+ Note that we only allow such expressions within simple arithmetic
+ or a COND_EXPR. */
+
+int
+contains_placeholder_p (exp)
+ tree exp;
+{
+ register enum tree_code code = TREE_CODE (exp);
+ tree inner;
+
+ /* If we have a WITH_RECORD_EXPR, it "cancels" any PLACEHOLDER_EXPR
+ in it since it is supplying a value for it. */
+ if (code == WITH_RECORD_EXPR)
+ return 0;
+
+ switch (TREE_CODE_CLASS (code))
+ {
+ case 'r':
+ for (inner = TREE_OPERAND (exp, 0);
+ TREE_CODE_CLASS (TREE_CODE (inner)) == 'r';
+ inner = TREE_OPERAND (inner, 0))
+ ;
+ return TREE_CODE (inner) == PLACEHOLDER_EXPR;
+
+ case '1':
+ case '2': case '<':
+ case 'e':
+ switch (tree_code_length[(int) code])
+ {
+ case 1:
+ return contains_placeholder_p (TREE_OPERAND (exp, 0));
+ case 2:
+ return (code != RTL_EXPR
+ && code != CONSTRUCTOR
+ && ! (code == SAVE_EXPR && SAVE_EXPR_RTL (exp) != 0)
+ && code != WITH_RECORD_EXPR
+ && (contains_placeholder_p (TREE_OPERAND (exp, 0))
+ || contains_placeholder_p (TREE_OPERAND (exp, 1))));
+ case 3:
+ return (code == COND_EXPR
+ && (contains_placeholder_p (TREE_OPERAND (exp, 0))
+ || contains_placeholder_p (TREE_OPERAND (exp, 1))
+ || contains_placeholder_p (TREE_OPERAND (exp, 2))));
+ }
+ }
+
+ return 0;
+}
+
+/* Given a tree EXP, a FIELD_DECL F, and a replacement value R,
+ return a tree with all occurrences of references to F in a
+ PLACEHOLDER_EXPR replaced by R. Note that we assume here that EXP
+ contains only arithmetic expressions. */
+
+tree
+substitute_in_expr (exp, f, r)
+ tree exp;
+ tree f;
+ tree r;
+{
+ enum tree_code code = TREE_CODE (exp);
+ tree inner;
+
+ switch (TREE_CODE_CLASS (code))
+ {
+ case 'c':
+ case 'd':
+ return exp;
+
+ case 'x':
+ if (code == PLACEHOLDER_EXPR)
+ return exp;
+ break;
+
+ case '1':
+ case '2':
+ case '<':
+ case 'e':
+ switch (tree_code_length[(int) code])
+ {
+ case 1:
+ return fold (build1 (code, TREE_TYPE (exp),
+ substitute_in_expr (TREE_OPERAND (exp, 0),
+ f, r)));
+
+ case 2:
+ /* An RTL_EXPR cannot contain a PLACEHOLDER_EXPR; a CONSTRUCTOR
+ could, but we don't support it. */
+ if (code == RTL_EXPR)
+ return exp;
+ else if (code == CONSTRUCTOR)
+ abort ();
+
+ return fold (build (code, TREE_TYPE (exp),
+ substitute_in_expr (TREE_OPERAND (exp, 0), f, r),
+ substitute_in_expr (TREE_OPERAND (exp, 1),
+ f, r)));
+
+ case 3:
+ /* It cannot be that anything inside a SAVE_EXPR contains a
+ PLACEHOLDER_EXPR. */
+ if (code == SAVE_EXPR)
+ return exp;
+
+ if (code != COND_EXPR)
+ abort ();
+
+ return fold (build (code, TREE_TYPE (exp),
+ substitute_in_expr (TREE_OPERAND (exp, 0), f, r),
+ substitute_in_expr (TREE_OPERAND (exp, 1), f, r),
+ substitute_in_expr (TREE_OPERAND (exp, 2),
+ f, r)));
+ }
+
+ break;
+
+ case 'r':
+ switch (code)
+ {
+ case COMPONENT_REF:
+ /* If this expression is getting a value from a PLACEHOLDER_EXPR
+ and it is the right field, replace it with R. */
+ for (inner = TREE_OPERAND (exp, 0);
+ TREE_CODE_CLASS (TREE_CODE (inner)) == 'r';
+ inner = TREE_OPERAND (inner, 0))
+ ;
+ if (TREE_CODE (inner) == PLACEHOLDER_EXPR
+ && TREE_OPERAND (exp, 1) == f)
+ return r;
+
+ return fold (build (code, TREE_TYPE (exp),
+ substitute_in_expr (TREE_OPERAND (exp, 0), f, r),
+ TREE_OPERAND (exp, 1)));
+ case BIT_FIELD_REF:
+ return fold (build (code, TREE_TYPE (exp),
+ substitute_in_expr (TREE_OPERAND (exp, 0), f, r),
+ substitute_in_expr (TREE_OPERAND (exp, 1), f, r),
+ substitute_in_expr (TREE_OPERAND (exp, 2), f, r)));
+ case INDIRECT_REF:
+ case BUFFER_REF:
+ return fold (build1 (code, TREE_TYPE (exp),
+ substitute_in_expr (TREE_OPERAND (exp, 0),
+ f, r)));
+ case OFFSET_REF:
+ return fold (build (code, TREE_TYPE (exp),
+ substitute_in_expr (TREE_OPERAND (exp, 0), f, r),
+ substitute_in_expr (TREE_OPERAND (exp, 1), f, r)));
+ }
+ }
+
+ /* If it wasn't one of the cases we handle, give up. */
+
+ abort ();
+}
+
+/* Given a type T, a FIELD_DECL F, and a replacement value R,
+ return a new type with all size expressions that contain F
+ updated by replacing F with R. */
+
+tree
+substitute_in_type (t, f, r)
+ tree t, f, r;
+{
+ switch (TREE_CODE (t))
+ {
+ case POINTER_TYPE:
+ case VOID_TYPE:
+ return t;
+ case INTEGER_TYPE:
+ case ENUMERAL_TYPE:
+ case BOOLEAN_TYPE:
+ case CHAR_TYPE:
+ if ((TREE_CODE (TYPE_MIN_VALUE (t)) != INTEGER_CST
+ && contains_placeholder_p (TYPE_MIN_VALUE (t)))
+ || (TREE_CODE (TYPE_MAX_VALUE (t)) != INTEGER_CST
+ && contains_placeholder_p (TYPE_MAX_VALUE (t))))
+ return build_range_type (t,
+ substitute_in_expr (TYPE_MIN_VALUE (t), f, r),
+ substitute_in_expr (TYPE_MAX_VALUE (t), f, r));
+ return t;
+
+ case REAL_TYPE:
+ if ((TYPE_MIN_VALUE (t) != 0
+ && TREE_CODE (TYPE_MIN_VALUE (t)) != REAL_CST
+ && contains_placeholder_p (TYPE_MIN_VALUE (t)))
+ || (TYPE_MAX_VALUE (t) != 0
+ && TREE_CODE (TYPE_MAX_VALUE (t)) != REAL_CST
+ && contains_placeholder_p (TYPE_MAX_VALUE (t))))
+ {
+ t = build_type_copy (t);
+
+ if (TYPE_MIN_VALUE (t))
+ TYPE_MIN_VALUE (t) = substitute_in_expr (TYPE_MIN_VALUE (t), f, r);
+ if (TYPE_MAX_VALUE (t))
+ TYPE_MAX_VALUE (t) = substitute_in_expr (TYPE_MAX_VALUE (t), f, r);
+ }
+ return t;
+
+ case COMPLEX_TYPE:
+ return build_complex_type (substitute_in_type (TREE_TYPE (t), f, r));
+
+ case OFFSET_TYPE:
+ case METHOD_TYPE:
+ case REFERENCE_TYPE:
+ case FILE_TYPE:
+ case SET_TYPE:
+ case FUNCTION_TYPE:
+ case LANG_TYPE:
+ /* Don't know how to do these yet. */
+ abort ();
+
+ case ARRAY_TYPE:
+ t = build_array_type (substitute_in_type (TREE_TYPE (t), f, r),
+ substitute_in_type (TYPE_DOMAIN (t), f, r));
+ TYPE_SIZE (t) = 0;
+ layout_type (t);
+ return t;
+
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ case QUAL_UNION_TYPE:
+ {
+ tree new = copy_node (t);
+ tree field;
+ tree last_field = 0;
+
+ /* Start out with no fields, make new fields, and chain them
+ in. */
+
+ TYPE_FIELDS (new) = 0;
+ TYPE_SIZE (new) = 0;
+
+ for (field = TYPE_FIELDS (t); field;
+ field = TREE_CHAIN (field))
+ {
+ tree new_field = copy_node (field);
+
+ TREE_TYPE (new_field)
+ = substitute_in_type (TREE_TYPE (new_field), f, r);
+
+ /* If this is an anonymous field and the type of this field is
+ a UNION_TYPE or RECORD_TYPE with no elements, ignore it. If
+ the type just has one element, treat that as the field.
+ But don't do this if we are processing a QUAL_UNION_TYPE. */
+ if (TREE_CODE (t) != QUAL_UNION_TYPE && DECL_NAME (new_field) == 0
+ && (TREE_CODE (TREE_TYPE (new_field)) == UNION_TYPE
+ || TREE_CODE (TREE_TYPE (new_field)) == RECORD_TYPE))
+ {
+ if (TYPE_FIELDS (TREE_TYPE (new_field)) == 0)
+ continue;
+
+ if (TREE_CHAIN (TYPE_FIELDS (TREE_TYPE (new_field))) == 0)
+ new_field = TYPE_FIELDS (TREE_TYPE (new_field));
+ }
+
+ DECL_CONTEXT (new_field) = new;
+ DECL_SIZE (new_field) = 0;
+
+ if (TREE_CODE (t) == QUAL_UNION_TYPE)
+ {
+ /* Do the substitution inside the qualifier and if we find
+ that this field will not be present, omit it. */
+ DECL_QUALIFIER (new_field)
+ = substitute_in_expr (DECL_QUALIFIER (field), f, r);
+ if (integer_zerop (DECL_QUALIFIER (new_field)))
+ continue;
+ }
+
+ if (last_field == 0)
+ TYPE_FIELDS (new) = new_field;
+ else
+ TREE_CHAIN (last_field) = new_field;
+
+ last_field = new_field;
+
+ /* If this is a qualified type and this field will always be
+ present, we are done. */
+ if (TREE_CODE (t) == QUAL_UNION_TYPE
+ && integer_onep (DECL_QUALIFIER (new_field)))
+ break;
+ }
+
+ /* If this used to be a qualified union type, but we now know what
+ field will be present, make this a normal union. */
+ if (TREE_CODE (new) == QUAL_UNION_TYPE
+ && (TYPE_FIELDS (new) == 0
+ || integer_onep (DECL_QUALIFIER (TYPE_FIELDS (new)))))
+ TREE_SET_CODE (new, UNION_TYPE);
+
+ layout_type (new);
+ return new;
+ }
+ }
+}
+
+/* Stabilize a reference so that we can use it any number of times
+ without causing its operands to be evaluated more than once.
+ Returns the stabilized reference. This works by means of save_expr,
+ so see the caveats in the comments about save_expr.
+
+ Also allows conversion expressions whose operands are references.
+ Any other kind of expression is returned unchanged. */
+
+tree
+stabilize_reference (ref)
+ tree ref;
+{
+ register tree result;
+ register enum tree_code code = TREE_CODE (ref);
+
+ switch (code)
+ {
+ case VAR_DECL:
+ case PARM_DECL:
+ case RESULT_DECL:
+ /* No action is needed in this case. */
+ return ref;
+
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ case FLOAT_EXPR:
+ case FIX_TRUNC_EXPR:
+ case FIX_FLOOR_EXPR:
+ case FIX_ROUND_EXPR:
+ case FIX_CEIL_EXPR:
+ result = build_nt (code, stabilize_reference (TREE_OPERAND (ref, 0)));
+ break;
+
+ case INDIRECT_REF:
+ result = build_nt (INDIRECT_REF,
+ stabilize_reference_1 (TREE_OPERAND (ref, 0)));
+ break;
+
+ case COMPONENT_REF:
+ result = build_nt (COMPONENT_REF,
+ stabilize_reference (TREE_OPERAND (ref, 0)),
+ TREE_OPERAND (ref, 1));
+ break;
+
+ case BIT_FIELD_REF:
+ result = build_nt (BIT_FIELD_REF,
+ stabilize_reference (TREE_OPERAND (ref, 0)),
+ stabilize_reference_1 (TREE_OPERAND (ref, 1)),
+ stabilize_reference_1 (TREE_OPERAND (ref, 2)));
+ break;
+
+ case ARRAY_REF:
+ result = build_nt (ARRAY_REF,
+ stabilize_reference (TREE_OPERAND (ref, 0)),
+ stabilize_reference_1 (TREE_OPERAND (ref, 1)));
+ break;
+
+ /* If arg isn't a kind of lvalue we recognize, make no change.
+ Caller should recognize the error for an invalid lvalue. */
+ default:
+ return ref;
+
+ case ERROR_MARK:
+ return error_mark_node;
+ }
+
+ TREE_TYPE (result) = TREE_TYPE (ref);
+ TREE_READONLY (result) = TREE_READONLY (ref);
+ TREE_SIDE_EFFECTS (result) = TREE_SIDE_EFFECTS (ref);
+ TREE_THIS_VOLATILE (result) = TREE_THIS_VOLATILE (ref);
+ TREE_RAISES (result) = TREE_RAISES (ref);
+
+ return result;
+}
+
+/* Subroutine of stabilize_reference; this is called for subtrees of
+ references. Any expression with side-effects must be put in a SAVE_EXPR
+ to ensure that it is only evaluated once.
+
+ We don't put SAVE_EXPR nodes around everything, because assigning very
+ simple expressions to temporaries causes us to miss good opportunities
+ for optimizations. Among other things, the opportunity to fold in the
+ addition of a constant into an addressing mode often gets lost, e.g.
+ "y[i+1] += x;". In general, we take the approach that we should not make
+ an assignment unless we are forced into it - i.e., that any non-side effect
+ operator should be allowed, and that cse should take care of coalescing
+ multiple utterances of the same expression should that prove fruitful. */
+
+static tree
+stabilize_reference_1 (e)
+ tree e;
+{
+ register tree result;
+ register enum tree_code code = TREE_CODE (e);
+
+ /* We cannot ignore const expressions because it might be a reference
+ to a const array but whose index contains side-effects. But we can
+ ignore things that are actual constant or that already have been
+ handled by this function. */
+
+ if (TREE_CONSTANT (e) || code == SAVE_EXPR)
+ return e;
+
+ switch (TREE_CODE_CLASS (code))
+ {
+ case 'x':
+ case 't':
+ case 'd':
+ case 'b':
+ case '<':
+ case 's':
+ case 'e':
+ case 'r':
+ /* If the expression has side-effects, then encase it in a SAVE_EXPR
+ so that it will only be evaluated once. */
+ /* The reference (r) and comparison (<) classes could be handled as
+ below, but it is generally faster to only evaluate them once. */
+ if (TREE_SIDE_EFFECTS (e))
+ return save_expr (e);
+ return e;
+
+ case 'c':
+ /* Constants need no processing. In fact, we should never reach
+ here. */
+ return e;
+
+ case '2':
+ /* Division is slow and tends to be compiled with jumps,
+ especially the division by powers of 2 that is often
+ found inside of an array reference. So do it just once. */
+ if (code == TRUNC_DIV_EXPR || code == TRUNC_MOD_EXPR
+ || code == FLOOR_DIV_EXPR || code == FLOOR_MOD_EXPR
+ || code == CEIL_DIV_EXPR || code == CEIL_MOD_EXPR
+ || code == ROUND_DIV_EXPR || code == ROUND_MOD_EXPR)
+ return save_expr (e);
+ /* Recursively stabilize each operand. */
+ result = build_nt (code, stabilize_reference_1 (TREE_OPERAND (e, 0)),
+ stabilize_reference_1 (TREE_OPERAND (e, 1)));
+ break;
+
+ case '1':
+ /* Recursively stabilize each operand. */
+ result = build_nt (code, stabilize_reference_1 (TREE_OPERAND (e, 0)));
+ break;
+
+ default:
+ abort ();
+ }
+
+ TREE_TYPE (result) = TREE_TYPE (e);
+ TREE_READONLY (result) = TREE_READONLY (e);
+ TREE_SIDE_EFFECTS (result) = TREE_SIDE_EFFECTS (e);
+ TREE_THIS_VOLATILE (result) = TREE_THIS_VOLATILE (e);
+ TREE_RAISES (result) = TREE_RAISES (e);
+
+ return result;
+}
+
+/* Low-level constructors for expressions. */
+
+/* Build an expression of code CODE, data type TYPE,
+ and operands as specified by the arguments ARG1 and following arguments.
+ Expressions and reference nodes can be created this way.
+ Constants, decls, types and misc nodes cannot be. */
+
+tree
+build VPROTO((enum tree_code code, tree tt, ...))
+{
+#ifndef __STDC__
+ enum tree_code code;
+ tree tt;
+#endif
+ va_list p;
+ register tree t;
+ register int length;
+ register int i;
+
+ VA_START (p, tt);
+
+#ifndef __STDC__
+ code = va_arg (p, enum tree_code);
+ tt = va_arg (p, tree);
+#endif
+
+ t = make_node (code);
+ length = tree_code_length[(int) code];
+ TREE_TYPE (t) = tt;
+
+ if (length == 2)
+ {
+ /* This is equivalent to the loop below, but faster. */
+ register tree arg0 = va_arg (p, tree);
+ register tree arg1 = va_arg (p, tree);
+ TREE_OPERAND (t, 0) = arg0;
+ TREE_OPERAND (t, 1) = arg1;
+ if ((arg0 && TREE_SIDE_EFFECTS (arg0))
+ || (arg1 && TREE_SIDE_EFFECTS (arg1)))
+ TREE_SIDE_EFFECTS (t) = 1;
+ TREE_RAISES (t)
+ = (arg0 && TREE_RAISES (arg0)) || (arg1 && TREE_RAISES (arg1));
+ }
+ else if (length == 1)
+ {
+ register tree arg0 = va_arg (p, tree);
+
+ /* Call build1 for this! */
+ if (TREE_CODE_CLASS (code) != 's')
+ abort ();
+ TREE_OPERAND (t, 0) = arg0;
+ if (arg0 && TREE_SIDE_EFFECTS (arg0))
+ TREE_SIDE_EFFECTS (t) = 1;
+ TREE_RAISES (t) = (arg0 && TREE_RAISES (arg0));
+ }
+ else
+ {
+ for (i = 0; i < length; i++)
+ {
+ register tree operand = va_arg (p, tree);
+ TREE_OPERAND (t, i) = operand;
+ if (operand)
+ {
+ if (TREE_SIDE_EFFECTS (operand))
+ TREE_SIDE_EFFECTS (t) = 1;
+ if (TREE_RAISES (operand))
+ TREE_RAISES (t) = 1;
+ }
+ }
+ }
+ va_end (p);
+ return t;
+}
+
+/* Same as above, but only builds for unary operators.
+ Saves lions share of calls to `build'; cuts down use
+ of varargs, which is expensive for RISC machines. */
+tree
+build1 (code, type, node)
+ enum tree_code code;
+ tree type;
+ tree node;
+{
+ register struct obstack *obstack = current_obstack;
+ register int i, length;
+ register tree_node_kind kind;
+ register tree t;
+
+#ifdef GATHER_STATISTICS
+ if (TREE_CODE_CLASS (code) == 'r')
+ kind = r_kind;
+ else
+ kind = e_kind;
+#endif
+
+ obstack = expression_obstack;
+ length = sizeof (struct tree_exp);
+
+ t = (tree) obstack_alloc (obstack, length);
+
+#ifdef GATHER_STATISTICS
+ tree_node_counts[(int)kind]++;
+ tree_node_sizes[(int)kind] += length;
+#endif
+
+ for (i = (length / sizeof (int)) - 1; i >= 0; i--)
+ ((int *) t)[i] = 0;
+
+ TREE_TYPE (t) = type;
+ TREE_SET_CODE (t, code);
+
+ if (obstack == &permanent_obstack)
+ TREE_PERMANENT (t) = 1;
+
+ TREE_OPERAND (t, 0) = node;
+ if (node)
+ {
+ if (TREE_SIDE_EFFECTS (node))
+ TREE_SIDE_EFFECTS (t) = 1;
+ if (TREE_RAISES (node))
+ TREE_RAISES (t) = 1;
+ }
+
+ return t;
+}
+
+/* Similar except don't specify the TREE_TYPE
+ and leave the TREE_SIDE_EFFECTS as 0.
+ It is permissible for arguments to be null,
+ or even garbage if their values do not matter. */
+
+tree
+build_nt VPROTO((enum tree_code code, ...))
+{
+#ifndef __STDC__
+ enum tree_code code;
+#endif
+ va_list p;
+ register tree t;
+ register int length;
+ register int i;
+
+ VA_START (p, code);
+
+#ifndef __STDC__
+ code = va_arg (p, enum tree_code);
+#endif
+
+ t = make_node (code);
+ length = tree_code_length[(int) code];
+
+ for (i = 0; i < length; i++)
+ TREE_OPERAND (t, i) = va_arg (p, tree);
+
+ va_end (p);
+ return t;
+}
+
+/* Similar to `build_nt', except we build
+ on the temp_decl_obstack, regardless. */
+
+tree
+build_parse_node VPROTO((enum tree_code code, ...))
+{
+#ifndef __STDC__
+ enum tree_code code;
+#endif
+ register struct obstack *ambient_obstack = expression_obstack;
+ va_list p;
+ register tree t;
+ register int length;
+ register int i;
+
+ VA_START (p, code);
+
+#ifndef __STDC__
+ code = va_arg (p, enum tree_code);
+#endif
+
+ expression_obstack = &temp_decl_obstack;
+
+ t = make_node (code);
+ length = tree_code_length[(int) code];
+
+ for (i = 0; i < length; i++)
+ TREE_OPERAND (t, i) = va_arg (p, tree);
+
+ va_end (p);
+ expression_obstack = ambient_obstack;
+ return t;
+}
+
+#if 0
+/* Commented out because this wants to be done very
+ differently. See cp-lex.c. */
+tree
+build_op_identifier (op1, op2)
+ tree op1, op2;
+{
+ register tree t = make_node (OP_IDENTIFIER);
+ TREE_PURPOSE (t) = op1;
+ TREE_VALUE (t) = op2;
+ return t;
+}
+#endif
+
+/* Create a DECL_... node of code CODE, name NAME and data type TYPE.
+ We do NOT enter this node in any sort of symbol table.
+
+ layout_decl is used to set up the decl's storage layout.
+ Other slots are initialized to 0 or null pointers. */
+
+tree
+build_decl (code, name, type)
+ enum tree_code code;
+ tree name, type;
+{
+ register tree t;
+
+ t = make_node (code);
+
+/* if (type == error_mark_node)
+ type = integer_type_node; */
+/* That is not done, deliberately, so that having error_mark_node
+ as the type can suppress useless errors in the use of this variable. */
+
+ DECL_NAME (t) = name;
+ DECL_ASSEMBLER_NAME (t) = name;
+ TREE_TYPE (t) = type;
+
+ if (code == VAR_DECL || code == PARM_DECL || code == RESULT_DECL)
+ layout_decl (t, 0);
+ else if (code == FUNCTION_DECL)
+ DECL_MODE (t) = FUNCTION_MODE;
+
+ return t;
+}
+
+/* BLOCK nodes are used to represent the structure of binding contours
+ and declarations, once those contours have been exited and their contents
+ compiled. This information is used for outputting debugging info. */
+
+tree
+build_block (vars, tags, subblocks, supercontext, chain)
+ tree vars, tags, subblocks, supercontext, chain;
+{
+ register tree block = make_node (BLOCK);
+ BLOCK_VARS (block) = vars;
+ BLOCK_TYPE_TAGS (block) = tags;
+ BLOCK_SUBBLOCKS (block) = subblocks;
+ BLOCK_SUPERCONTEXT (block) = supercontext;
+ BLOCK_CHAIN (block) = chain;
+ return block;
+}
+
+/* Return a type like TTYPE except that its TYPE_ATTRIBUTE
+ is ATTRIBUTE.
+
+ Such modified types already made are recorded so that duplicates
+ are not made. */
+
+tree
+build_type_attribute_variant (ttype, attribute)
+ tree ttype, attribute;
+{
+ if ( ! attribute_list_equal (TYPE_ATTRIBUTES (ttype), attribute))
+ {
+ register int hashcode;
+ register struct obstack *ambient_obstack = current_obstack;
+ tree ntype;
+
+ if (ambient_obstack != &permanent_obstack)
+ current_obstack = TYPE_OBSTACK (ttype);
+
+ ntype = copy_node (ttype);
+ current_obstack = ambient_obstack;
+
+ TYPE_POINTER_TO (ntype) = 0;
+ TYPE_REFERENCE_TO (ntype) = 0;
+ TYPE_ATTRIBUTES (ntype) = attribute;
+
+ /* Create a new main variant of TYPE. */
+ TYPE_MAIN_VARIANT (ntype) = ntype;
+ TYPE_NEXT_VARIANT (ntype) = 0;
+ TYPE_READONLY (ntype) = TYPE_VOLATILE (ntype) = 0;
+
+ hashcode = TYPE_HASH (TREE_CODE (ntype))
+ + TYPE_HASH (TREE_TYPE (ntype))
+ + type_hash_list (attribute);
+
+ switch (TREE_CODE (ntype))
+ {
+ case FUNCTION_TYPE:
+ hashcode += TYPE_HASH (TYPE_ARG_TYPES (ntype));
+ break;
+ case ARRAY_TYPE:
+ hashcode += TYPE_HASH (TYPE_DOMAIN (ntype));
+ break;
+ case INTEGER_TYPE:
+ hashcode += TYPE_HASH (TYPE_MAX_VALUE (ntype));
+ break;
+ case REAL_TYPE:
+ hashcode += TYPE_HASH (TYPE_PRECISION (ntype));
+ break;
+ }
+
+ ntype = type_hash_canon (hashcode, ntype);
+ ttype = build_type_variant (ntype, TYPE_READONLY (ttype),
+ TYPE_VOLATILE (ttype));
+ }
+
+ return ttype;
+}
+
+/* Return a type like TYPE except that its TYPE_READONLY is CONSTP
+ and its TYPE_VOLATILE is VOLATILEP.
+
+ Such variant types already made are recorded so that duplicates
+ are not made.
+
+ A variant types should never be used as the type of an expression.
+ Always copy the variant information into the TREE_READONLY
+ and TREE_THIS_VOLATILE of the expression, and then give the expression
+ as its type the "main variant", the variant whose TYPE_READONLY
+ and TYPE_VOLATILE are zero. Use TYPE_MAIN_VARIANT to find the
+ main variant. */
+
+tree
+build_type_variant (type, constp, volatilep)
+ tree type;
+ int constp, volatilep;
+{
+ register tree t;
+
+ /* Treat any nonzero argument as 1. */
+ constp = !!constp;
+ volatilep = !!volatilep;
+
+ /* If not generating auxiliary info, search the chain of variants to see
+ if there is already one there just like the one we need to have. If so,
+ use that existing one.
+
+ We don't do this in the case where we are generating aux info because
+ in that case we want each typedef names to get it's own distinct type
+ node, even if the type of this new typedef is the same as some other
+ (existing) type. */
+
+ if (!flag_gen_aux_info)
+ for (t = TYPE_MAIN_VARIANT(type); t; t = TYPE_NEXT_VARIANT (t))
+ if (constp == TYPE_READONLY (t) && volatilep == TYPE_VOLATILE (t))
+ return t;
+
+ /* We need a new one. */
+
+ t = build_type_copy (type);
+ TYPE_READONLY (t) = constp;
+ TYPE_VOLATILE (t) = volatilep;
+
+ return t;
+}
+
+/* Give TYPE a new main variant: NEW_MAIN.
+ This is the right thing to do only when something else
+ about TYPE is modified in place. */
+
+tree
+change_main_variant (type, new_main)
+ tree type, new_main;
+{
+ tree t;
+ tree omain = TYPE_MAIN_VARIANT (type);
+
+ /* Remove TYPE from the TYPE_NEXT_VARIANT chain of its main variant. */
+ if (TYPE_NEXT_VARIANT (omain) == type)
+ TYPE_NEXT_VARIANT (omain) = TYPE_NEXT_VARIANT (type);
+ else
+ for (t = TYPE_NEXT_VARIANT (omain); t && TYPE_NEXT_VARIANT (t);
+ t = TYPE_NEXT_VARIANT (t))
+ if (TYPE_NEXT_VARIANT (t) == type)
+ {
+ TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (type);
+ break;
+ }
+
+ TYPE_MAIN_VARIANT (type) = new_main;
+ TYPE_NEXT_VARIANT (type) = TYPE_NEXT_VARIANT (new_main);
+ TYPE_NEXT_VARIANT (new_main) = type;
+}
+
+/* Create a new variant of TYPE, equivalent but distinct.
+ This is so the caller can modify it. */
+
+tree
+build_type_copy (type)
+ tree type;
+{
+ register tree t, m = TYPE_MAIN_VARIANT (type);
+ register struct obstack *ambient_obstack = current_obstack;
+
+ current_obstack = TYPE_OBSTACK (type);
+ t = copy_node (type);
+ current_obstack = ambient_obstack;
+
+ TYPE_POINTER_TO (t) = 0;
+ TYPE_REFERENCE_TO (t) = 0;
+
+ /* Add this type to the chain of variants of TYPE. */
+ TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (m);
+ TYPE_NEXT_VARIANT (m) = t;
+
+ return t;
+}
+
+/* Hashing of types so that we don't make duplicates.
+ The entry point is `type_hash_canon'. */
+
+/* Each hash table slot is a bucket containing a chain
+ of these structures. */
+
+struct type_hash
+{
+ struct type_hash *next; /* Next structure in the bucket. */
+ int hashcode; /* Hash code of this type. */
+ tree type; /* The type recorded here. */
+};
+
+/* Now here is the hash table. When recording a type, it is added
+ to the slot whose index is the hash code mod the table size.
+ Note that the hash table is used for several kinds of types
+ (function types, array types and array index range types, for now).
+ While all these live in the same table, they are completely independent,
+ and the hash code is computed differently for each of these. */
+
+#define TYPE_HASH_SIZE 59
+struct type_hash *type_hash_table[TYPE_HASH_SIZE];
+
+/* Compute a hash code for a list of types (chain of TREE_LIST nodes
+ with types in the TREE_VALUE slots), by adding the hash codes
+ of the individual types. */
+
+int
+type_hash_list (list)
+ tree list;
+{
+ register int hashcode;
+ register tree tail;
+ for (hashcode = 0, tail = list; tail; tail = TREE_CHAIN (tail))
+ hashcode += TYPE_HASH (TREE_VALUE (tail));
+ return hashcode;
+}
+
+/* Look in the type hash table for a type isomorphic to TYPE.
+ If one is found, return it. Otherwise return 0. */
+
+tree
+type_hash_lookup (hashcode, type)
+ int hashcode;
+ tree type;
+{
+ register struct type_hash *h;
+ for (h = type_hash_table[hashcode % TYPE_HASH_SIZE]; h; h = h->next)
+ if (h->hashcode == hashcode
+ && TREE_CODE (h->type) == TREE_CODE (type)
+ && TREE_TYPE (h->type) == TREE_TYPE (type)
+ && attribute_list_equal (TYPE_ATTRIBUTES (h->type),
+ TYPE_ATTRIBUTES (type))
+ && (TYPE_MAX_VALUE (h->type) == TYPE_MAX_VALUE (type)
+ || tree_int_cst_equal (TYPE_MAX_VALUE (h->type),
+ TYPE_MAX_VALUE (type)))
+ && (TYPE_MIN_VALUE (h->type) == TYPE_MIN_VALUE (type)
+ || tree_int_cst_equal (TYPE_MIN_VALUE (h->type),
+ TYPE_MIN_VALUE (type)))
+ && (TYPE_DOMAIN (h->type) == TYPE_DOMAIN (type)
+ || (TYPE_DOMAIN (h->type)
+ && TREE_CODE (TYPE_DOMAIN (h->type)) == TREE_LIST
+ && TYPE_DOMAIN (type)
+ && TREE_CODE (TYPE_DOMAIN (type)) == TREE_LIST
+ && type_list_equal (TYPE_DOMAIN (h->type), TYPE_DOMAIN (type)))))
+ return h->type;
+ return 0;
+}
+
+/* Add an entry to the type-hash-table
+ for a type TYPE whose hash code is HASHCODE. */
+
+void
+type_hash_add (hashcode, type)
+ int hashcode;
+ tree type;
+{
+ register struct type_hash *h;
+
+ h = (struct type_hash *) oballoc (sizeof (struct type_hash));
+ h->hashcode = hashcode;
+ h->type = type;
+ h->next = type_hash_table[hashcode % TYPE_HASH_SIZE];
+ type_hash_table[hashcode % TYPE_HASH_SIZE] = h;
+}
+
+/* Given TYPE, and HASHCODE its hash code, return the canonical
+ object for an identical type if one already exists.
+ Otherwise, return TYPE, and record it as the canonical object
+ if it is a permanent object.
+
+ To use this function, first create a type of the sort you want.
+ Then compute its hash code from the fields of the type that
+ make it different from other similar types.
+ Then call this function and use the value.
+ This function frees the type you pass in if it is a duplicate. */
+
+/* Set to 1 to debug without canonicalization. Never set by program. */
+int debug_no_type_hash = 0;
+
+tree
+type_hash_canon (hashcode, type)
+ int hashcode;
+ tree type;
+{
+ tree t1;
+
+ if (debug_no_type_hash)
+ return type;
+
+ t1 = type_hash_lookup (hashcode, type);
+ if (t1 != 0)
+ {
+ obstack_free (TYPE_OBSTACK (type), type);
+#ifdef GATHER_STATISTICS
+ tree_node_counts[(int)t_kind]--;
+ tree_node_sizes[(int)t_kind] -= sizeof (struct tree_type);
+#endif
+ return t1;
+ }
+
+ /* If this is a permanent type, record it for later reuse. */
+ if (TREE_PERMANENT (type))
+ type_hash_add (hashcode, type);
+
+ return type;
+}
+
+/* Given two lists of attributes, return true if list l2 is
+ equivalent to l1. */
+
+int
+attribute_list_equal (l1, l2)
+ tree l1, l2;
+{
+ return attribute_list_contained (l1, l2)
+ && attribute_list_contained (l2, l1);
+}
+
+/* Given two lists of attributes, return true if list l2 is
+ completely contained within l1. */
+
+int
+attribute_list_contained (l1, l2)
+ tree l1, l2;
+{
+ register tree t1, t2;
+
+ /* First check the obvious, maybe the lists are identical. */
+ if (l1 == l2)
+ return 1;
+
+ /* Then check the obvious, maybe the lists are similar. */
+ for (t1 = l1, t2 = l2;
+ t1 && t2
+ && TREE_VALUE (t1) == TREE_VALUE (t2);
+ t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2));
+
+ /* Maybe the lists are equal. */
+ if (t1 == 0 && t2 == 0)
+ return 1;
+
+ for (; t2; t2 = TREE_CHAIN (t2))
+ if (!value_member (l1, t2))
+ return 0;
+ return 1;
+}
+
+/* Given two lists of types
+ (chains of TREE_LIST nodes with types in the TREE_VALUE slots)
+ return 1 if the lists contain the same types in the same order.
+ Also, the TREE_PURPOSEs must match. */
+
+int
+type_list_equal (l1, l2)
+ tree l1, l2;
+{
+ register tree t1, t2;
+ for (t1 = l1, t2 = l2; t1 && t2; t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2))
+ {
+ if (TREE_VALUE (t1) != TREE_VALUE (t2))
+ return 0;
+ if (TREE_PURPOSE (t1) != TREE_PURPOSE (t2))
+ {
+ int cmp = simple_cst_equal (TREE_PURPOSE (t1), TREE_PURPOSE (t2));
+ if (cmp < 0)
+ abort ();
+ if (cmp == 0)
+ return 0;
+ }
+ }
+
+ return t1 == t2;
+}
+
+/* Nonzero if integer constants T1 and T2
+ represent the same constant value. */
+
+int
+tree_int_cst_equal (t1, t2)
+ tree t1, t2;
+{
+ if (t1 == t2)
+ return 1;
+ if (t1 == 0 || t2 == 0)
+ return 0;
+ if (TREE_CODE (t1) == INTEGER_CST
+ && TREE_CODE (t2) == INTEGER_CST
+ && TREE_INT_CST_LOW (t1) == TREE_INT_CST_LOW (t2)
+ && TREE_INT_CST_HIGH (t1) == TREE_INT_CST_HIGH (t2))
+ return 1;
+ return 0;
+}
+
+/* Nonzero if integer constants T1 and T2 represent values that satisfy <.
+ The precise way of comparison depends on their data type. */
+
+int
+tree_int_cst_lt (t1, t2)
+ tree t1, t2;
+{
+ if (t1 == t2)
+ return 0;
+
+ if (!TREE_UNSIGNED (TREE_TYPE (t1)))
+ return INT_CST_LT (t1, t2);
+ return INT_CST_LT_UNSIGNED (t1, t2);
+}
+
+/* Return an indication of the sign of the integer constant T.
+ The return value is -1 if T < 0, 0 if T == 0, and 1 if T > 0.
+ Note that -1 will never be returned it T's type is unsigned. */
+
+int
+tree_int_cst_sgn (t)
+ tree t;
+{
+ if (TREE_INT_CST_LOW (t) == 0 && TREE_INT_CST_HIGH (t) == 0)
+ return 0;
+ else if (TREE_UNSIGNED (TREE_TYPE (t)))
+ return 1;
+ else if (TREE_INT_CST_HIGH (t) < 0)
+ return -1;
+ else
+ return 1;
+}
+
+/* Compare two constructor-element-type constants. */
+int
+simple_cst_list_equal (l1, l2)
+ tree l1, l2;
+{
+ while (l1 != NULL_TREE && l2 != NULL_TREE)
+ {
+ int cmp = simple_cst_equal (TREE_VALUE (l1), TREE_VALUE (l2));
+ if (cmp < 0)
+ abort ();
+ if (cmp == 0)
+ return 0;
+ l1 = TREE_CHAIN (l1);
+ l2 = TREE_CHAIN (l2);
+ }
+ return (l1 == l2);
+}
+
+/* Return truthvalue of whether T1 is the same tree structure as T2.
+ Return 1 if they are the same.
+ Return 0 if they are understandably different.
+ Return -1 if either contains tree structure not understood by
+ this function. */
+
+int
+simple_cst_equal (t1, t2)
+ tree t1, t2;
+{
+ register enum tree_code code1, code2;
+ int cmp;
+
+ if (t1 == t2)
+ return 1;
+ if (t1 == 0 || t2 == 0)
+ return 0;
+
+ code1 = TREE_CODE (t1);
+ code2 = TREE_CODE (t2);
+
+ if (code1 == NOP_EXPR || code1 == CONVERT_EXPR || code1 == NON_LVALUE_EXPR)
+ if (code2 == NOP_EXPR || code2 == CONVERT_EXPR || code2 == NON_LVALUE_EXPR)
+ return simple_cst_equal (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0));
+ else
+ return simple_cst_equal (TREE_OPERAND (t1, 0), t2);
+ else if (code2 == NOP_EXPR || code2 == CONVERT_EXPR
+ || code2 == NON_LVALUE_EXPR)
+ return simple_cst_equal (t1, TREE_OPERAND (t2, 0));
+
+ if (code1 != code2)
+ return 0;
+
+ switch (code1)
+ {
+ case INTEGER_CST:
+ return TREE_INT_CST_LOW (t1) == TREE_INT_CST_LOW (t2)
+ && TREE_INT_CST_HIGH (t1) == TREE_INT_CST_HIGH (t2);
+
+ case REAL_CST:
+ return REAL_VALUES_EQUAL (TREE_REAL_CST (t1), TREE_REAL_CST (t2));
+
+ case STRING_CST:
+ return TREE_STRING_LENGTH (t1) == TREE_STRING_LENGTH (t2)
+ && !bcmp (TREE_STRING_POINTER (t1), TREE_STRING_POINTER (t2),
+ TREE_STRING_LENGTH (t1));
+
+ case CONSTRUCTOR:
+ abort ();
+
+ case SAVE_EXPR:
+ return simple_cst_equal (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0));
+
+ case CALL_EXPR:
+ cmp = simple_cst_equal (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0));
+ if (cmp <= 0)
+ return cmp;
+ return simple_cst_list_equal (TREE_OPERAND (t1, 1), TREE_OPERAND (t2, 1));
+
+ case TARGET_EXPR:
+ /* Special case: if either target is an unallocated VAR_DECL,
+ it means that it's going to be unified with whatever the
+ TARGET_EXPR is really supposed to initialize, so treat it
+ as being equivalent to anything. */
+ if ((TREE_CODE (TREE_OPERAND (t1, 0)) == VAR_DECL
+ && DECL_NAME (TREE_OPERAND (t1, 0)) == NULL_TREE
+ && DECL_RTL (TREE_OPERAND (t1, 0)) == 0)
+ || (TREE_CODE (TREE_OPERAND (t2, 0)) == VAR_DECL
+ && DECL_NAME (TREE_OPERAND (t2, 0)) == NULL_TREE
+ && DECL_RTL (TREE_OPERAND (t2, 0)) == 0))
+ cmp = 1;
+ else
+ cmp = simple_cst_equal (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0));
+ if (cmp <= 0)
+ return cmp;
+ return simple_cst_equal (TREE_OPERAND (t1, 1), TREE_OPERAND (t2, 1));
+
+ case WITH_CLEANUP_EXPR:
+ cmp = simple_cst_equal (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0));
+ if (cmp <= 0)
+ return cmp;
+ return simple_cst_equal (TREE_OPERAND (t1, 2), TREE_OPERAND (t1, 2));
+
+ case COMPONENT_REF:
+ if (TREE_OPERAND (t1, 1) == TREE_OPERAND (t2, 1))
+ return simple_cst_equal (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0));
+ return 0;
+
+ case VAR_DECL:
+ case PARM_DECL:
+ case CONST_DECL:
+ case FUNCTION_DECL:
+ return 0;
+ }
+
+ /* This general rule works for most tree codes.
+ All exceptions should be handled above. */
+
+ switch (TREE_CODE_CLASS (code1))
+ {
+ int i;
+ case '1':
+ case '2':
+ case '<':
+ case 'e':
+ case 'r':
+ case 's':
+ cmp = 1;
+ for (i=0; i<tree_code_length[(int) code1]; ++i)
+ {
+ cmp = simple_cst_equal (TREE_OPERAND (t1, i), TREE_OPERAND (t2, i));
+ if (cmp <= 0)
+ return cmp;
+ }
+ return cmp;
+ }
+
+ return -1;
+}
+
+/* Constructors for pointer, array and function types.
+ (RECORD_TYPE, UNION_TYPE and ENUMERAL_TYPE nodes are
+ constructed by language-dependent code, not here.) */
+
+/* Construct, lay out and return the type of pointers to TO_TYPE.
+ If such a type has already been constructed, reuse it. */
+
+tree
+build_pointer_type (to_type)
+ tree to_type;
+{
+ register tree t = TYPE_POINTER_TO (to_type);
+
+ /* First, if we already have a type for pointers to TO_TYPE, use it. */
+
+ if (t)
+ return t;
+
+ /* We need a new one. Put this in the same obstack as TO_TYPE. */
+ push_obstacks (TYPE_OBSTACK (to_type), TYPE_OBSTACK (to_type));
+ t = make_node (POINTER_TYPE);
+ pop_obstacks ();
+
+ TREE_TYPE (t) = to_type;
+
+ /* Record this type as the pointer to TO_TYPE. */
+ TYPE_POINTER_TO (to_type) = t;
+
+ /* Lay out the type. This function has many callers that are concerned
+ with expression-construction, and this simplifies them all.
+ Also, it guarantees the TYPE_SIZE is in the same obstack as the type. */
+ layout_type (t);
+
+ return t;
+}
+
+/* Create a type of integers to be the TYPE_DOMAIN of an ARRAY_TYPE.
+ MAXVAL should be the maximum value in the domain
+ (one less than the length of the array). */
+
+tree
+build_index_type (maxval)
+ tree maxval;
+{
+ register tree itype = make_node (INTEGER_TYPE);
+ TYPE_PRECISION (itype) = TYPE_PRECISION (sizetype);
+ TYPE_MIN_VALUE (itype) = build_int_2 (0, 0);
+ TREE_TYPE (TYPE_MIN_VALUE (itype)) = sizetype;
+ TYPE_MAX_VALUE (itype) = convert (sizetype, maxval);
+ TYPE_MODE (itype) = TYPE_MODE (sizetype);
+ TYPE_SIZE (itype) = TYPE_SIZE (sizetype);
+ TYPE_ALIGN (itype) = TYPE_ALIGN (sizetype);
+ if (TREE_CODE (maxval) == INTEGER_CST)
+ {
+ int maxint = (int) TREE_INT_CST_LOW (maxval);
+ /* If the domain should be empty, make sure the maxval
+ remains -1 and is not spoiled by truncation. */
+ if (INT_CST_LT (maxval, integer_zero_node))
+ {
+ TYPE_MAX_VALUE (itype) = build_int_2 (-1, -1);
+ TREE_TYPE (TYPE_MAX_VALUE (itype)) = sizetype;
+ }
+ return type_hash_canon (maxint < 0 ? ~maxint : maxint, itype);
+ }
+ else
+ return itype;
+}
+
+/* Create a range of some discrete type TYPE (an INTEGER_TYPE,
+ ENUMERAL_TYPE, BOOLEAN_TYPE, or CHAR_TYPE), with
+ low bound LOWVAL and high bound HIGHVAL.
+ if TYPE==NULL_TREE, sizetype is used. */
+
+tree
+build_range_type (type, lowval, highval)
+ tree type, lowval, highval;
+{
+ register tree itype = make_node (INTEGER_TYPE);
+ TREE_TYPE (itype) = type;
+ if (type == NULL_TREE)
+ type = sizetype;
+ TYPE_PRECISION (itype) = TYPE_PRECISION (type);
+ TYPE_MIN_VALUE (itype) = convert (type, lowval);
+ TYPE_MAX_VALUE (itype) = convert (type, highval);
+ TYPE_MODE (itype) = TYPE_MODE (type);
+ TYPE_SIZE (itype) = TYPE_SIZE (type);
+ TYPE_ALIGN (itype) = TYPE_ALIGN (type);
+ if ((TREE_CODE (lowval) == INTEGER_CST)
+ && (TREE_CODE (highval) == INTEGER_CST))
+ {
+ HOST_WIDE_INT highint = TREE_INT_CST_LOW (highval);
+ HOST_WIDE_INT lowint = TREE_INT_CST_LOW (lowval);
+ int maxint = (int) (highint - lowint);
+ return type_hash_canon (maxint < 0 ? ~maxint : maxint, itype);
+ }
+ else
+ return itype;
+}
+
+/* Just like build_index_type, but takes lowval and highval instead
+ of just highval (maxval). */
+
+tree
+build_index_2_type (lowval,highval)
+ tree lowval, highval;
+{
+ return build_range_type (NULL_TREE, lowval, highval);
+}
+
+/* Return nonzero iff ITYPE1 and ITYPE2 are equal (in the LISP sense).
+ Needed because when index types are not hashed, equal index types
+ built at different times appear distinct, even though structurally,
+ they are not. */
+
+int
+index_type_equal (itype1, itype2)
+ tree itype1, itype2;
+{
+ if (TREE_CODE (itype1) != TREE_CODE (itype2))
+ return 0;
+ if (TREE_CODE (itype1) == INTEGER_TYPE)
+ {
+ if (TYPE_PRECISION (itype1) != TYPE_PRECISION (itype2)
+ || TYPE_MODE (itype1) != TYPE_MODE (itype2)
+ || ! simple_cst_equal (TYPE_SIZE (itype1), TYPE_SIZE (itype2))
+ || TYPE_ALIGN (itype1) != TYPE_ALIGN (itype2))
+ return 0;
+ if (simple_cst_equal (TYPE_MIN_VALUE (itype1), TYPE_MIN_VALUE (itype2))
+ && simple_cst_equal (TYPE_MAX_VALUE (itype1), TYPE_MAX_VALUE (itype2)))
+ return 1;
+ }
+ return 0;
+}
+
+/* Construct, lay out and return the type of arrays of elements with ELT_TYPE
+ and number of elements specified by the range of values of INDEX_TYPE.
+ If such a type has already been constructed, reuse it. */
+
+tree
+build_array_type (elt_type, index_type)
+ tree elt_type, index_type;
+{
+ register tree t;
+ int hashcode;
+
+ if (TREE_CODE (elt_type) == FUNCTION_TYPE)
+ {
+ error ("arrays of functions are not meaningful");
+ elt_type = integer_type_node;
+ }
+
+ /* Make sure TYPE_POINTER_TO (elt_type) is filled in. */
+ build_pointer_type (elt_type);
+
+ /* Allocate the array after the pointer type,
+ in case we free it in type_hash_canon. */
+ t = make_node (ARRAY_TYPE);
+ TREE_TYPE (t) = elt_type;
+ TYPE_DOMAIN (t) = index_type;
+
+ if (index_type == 0)
+ {
+ return t;
+ }
+
+ hashcode = TYPE_HASH (elt_type) + TYPE_HASH (index_type);
+ t = type_hash_canon (hashcode, t);
+
+#if 0 /* This led to crashes, because it could put a temporary node
+ on the TYPE_NEXT_VARIANT chain of a permanent one. */
+ /* The main variant of an array type should always
+ be an array whose element type is the main variant. */
+ if (elt_type != TYPE_MAIN_VARIANT (elt_type))
+ change_main_variant (t, build_array_type (TYPE_MAIN_VARIANT (elt_type),
+ index_type));
+#endif
+
+ if (TYPE_SIZE (t) == 0)
+ layout_type (t);
+ return t;
+}
+
+/* Construct, lay out and return
+ the type of functions returning type VALUE_TYPE
+ given arguments of types ARG_TYPES.
+ ARG_TYPES is a chain of TREE_LIST nodes whose TREE_VALUEs
+ are data type nodes for the arguments of the function.
+ If such a type has already been constructed, reuse it. */
+
+tree
+build_function_type (value_type, arg_types)
+ tree value_type, arg_types;
+{
+ register tree t;
+ int hashcode;
+
+ if (TREE_CODE (value_type) == FUNCTION_TYPE)
+ {
+ error ("function return type cannot be function");
+ value_type = integer_type_node;
+ }
+
+ /* Make a node of the sort we want. */
+ t = make_node (FUNCTION_TYPE);
+ TREE_TYPE (t) = value_type;
+ TYPE_ARG_TYPES (t) = arg_types;
+
+ /* If we already have such a type, use the old one and free this one. */
+ hashcode = TYPE_HASH (value_type) + type_hash_list (arg_types);
+ t = type_hash_canon (hashcode, t);
+
+ if (TYPE_SIZE (t) == 0)
+ layout_type (t);
+ return t;
+}
+
+/* Build the node for the type of references-to-TO_TYPE. */
+
+tree
+build_reference_type (to_type)
+ tree to_type;
+{
+ register tree t = TYPE_REFERENCE_TO (to_type);
+ register struct obstack *ambient_obstack = current_obstack;
+ register struct obstack *ambient_saveable_obstack = saveable_obstack;
+
+ /* First, if we already have a type for pointers to TO_TYPE, use it. */
+
+ if (t)
+ return t;
+
+ /* We need a new one. If TO_TYPE is permanent, make this permanent too. */
+ if (TREE_PERMANENT (to_type))
+ {
+ current_obstack = &permanent_obstack;
+ saveable_obstack = &permanent_obstack;
+ }
+
+ t = make_node (REFERENCE_TYPE);
+ TREE_TYPE (t) = to_type;
+
+ /* Record this type as the pointer to TO_TYPE. */
+ TYPE_REFERENCE_TO (to_type) = t;
+
+ layout_type (t);
+
+ current_obstack = ambient_obstack;
+ saveable_obstack = ambient_saveable_obstack;
+ return t;
+}
+
+/* Construct, lay out and return the type of methods belonging to class
+ BASETYPE and whose arguments and values are described by TYPE.
+ If that type exists already, reuse it.
+ TYPE must be a FUNCTION_TYPE node. */
+
+tree
+build_method_type (basetype, type)
+ tree basetype, type;
+{
+ register tree t;
+ int hashcode;
+
+ /* Make a node of the sort we want. */
+ t = make_node (METHOD_TYPE);
+
+ if (TREE_CODE (type) != FUNCTION_TYPE)
+ abort ();
+
+ TYPE_METHOD_BASETYPE (t) = TYPE_MAIN_VARIANT (basetype);
+ TREE_TYPE (t) = TREE_TYPE (type);
+
+ /* The actual arglist for this function includes a "hidden" argument
+ which is "this". Put it into the list of argument types. */
+
+ TYPE_ARG_TYPES (t)
+ = tree_cons (NULL_TREE,
+ build_pointer_type (basetype), TYPE_ARG_TYPES (type));
+
+ /* If we already have such a type, use the old one and free this one. */
+ hashcode = TYPE_HASH (basetype) + TYPE_HASH (type);
+ t = type_hash_canon (hashcode, t);
+
+ if (TYPE_SIZE (t) == 0)
+ layout_type (t);
+
+ return t;
+}
+
+/* Construct, lay out and return the type of offsets to a value
+ of type TYPE, within an object of type BASETYPE.
+ If a suitable offset type exists already, reuse it. */
+
+tree
+build_offset_type (basetype, type)
+ tree basetype, type;
+{
+ register tree t;
+ int hashcode;
+
+ /* Make a node of the sort we want. */
+ t = make_node (OFFSET_TYPE);
+
+ TYPE_OFFSET_BASETYPE (t) = TYPE_MAIN_VARIANT (basetype);
+ TREE_TYPE (t) = type;
+
+ /* If we already have such a type, use the old one and free this one. */
+ hashcode = TYPE_HASH (basetype) + TYPE_HASH (type);
+ t = type_hash_canon (hashcode, t);
+
+ if (TYPE_SIZE (t) == 0)
+ layout_type (t);
+
+ return t;
+}
+
+/* Create a complex type whose components are COMPONENT_TYPE. */
+
+tree
+build_complex_type (component_type)
+ tree component_type;
+{
+ register tree t;
+ int hashcode;
+
+ /* Make a node of the sort we want. */
+ t = make_node (COMPLEX_TYPE);
+
+ TREE_TYPE (t) = TYPE_MAIN_VARIANT (component_type);
+ TYPE_VOLATILE (t) = TYPE_VOLATILE (component_type);
+ TYPE_READONLY (t) = TYPE_READONLY (component_type);
+
+ /* If we already have such a type, use the old one and free this one. */
+ hashcode = TYPE_HASH (component_type);
+ t = type_hash_canon (hashcode, t);
+
+ if (TYPE_SIZE (t) == 0)
+ layout_type (t);
+
+ return t;
+}
+
+/* Return OP, stripped of any conversions to wider types as much as is safe.
+ Converting the value back to OP's type makes a value equivalent to OP.
+
+ If FOR_TYPE is nonzero, we return a value which, if converted to
+ type FOR_TYPE, would be equivalent to converting OP to type FOR_TYPE.
+
+ If FOR_TYPE is nonzero, unaligned bit-field references may be changed to the
+ narrowest type that can hold the value, even if they don't exactly fit.
+ Otherwise, bit-field references are changed to a narrower type
+ only if they can be fetched directly from memory in that type.
+
+ OP must have integer, real or enumeral type. Pointers are not allowed!
+
+ There are some cases where the obvious value we could return
+ would regenerate to OP if converted to OP's type,
+ but would not extend like OP to wider types.
+ If FOR_TYPE indicates such extension is contemplated, we eschew such values.
+ For example, if OP is (unsigned short)(signed char)-1,
+ we avoid returning (signed char)-1 if FOR_TYPE is int,
+ even though extending that to an unsigned short would regenerate OP,
+ since the result of extending (signed char)-1 to (int)
+ is different from (int) OP. */
+
+tree
+get_unwidened (op, for_type)
+ register tree op;
+ tree for_type;
+{
+ /* Set UNS initially if converting OP to FOR_TYPE is a zero-extension. */
+ /* TYPE_PRECISION is safe in place of type_precision since
+ pointer types are not allowed. */
+ register tree type = TREE_TYPE (op);
+ register unsigned final_prec
+ = TYPE_PRECISION (for_type != 0 ? for_type : type);
+ register int uns
+ = (for_type != 0 && for_type != type
+ && final_prec > TYPE_PRECISION (type)
+ && TREE_UNSIGNED (type));
+ register tree win = op;
+
+ while (TREE_CODE (op) == NOP_EXPR)
+ {
+ register int bitschange
+ = TYPE_PRECISION (TREE_TYPE (op))
+ - TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (op, 0)));
+
+ /* Truncations are many-one so cannot be removed.
+ Unless we are later going to truncate down even farther. */
+ if (bitschange < 0
+ && final_prec > TYPE_PRECISION (TREE_TYPE (op)))
+ break;
+
+ /* See what's inside this conversion. If we decide to strip it,
+ we will set WIN. */
+ op = TREE_OPERAND (op, 0);
+
+ /* If we have not stripped any zero-extensions (uns is 0),
+ we can strip any kind of extension.
+ If we have previously stripped a zero-extension,
+ only zero-extensions can safely be stripped.
+ Any extension can be stripped if the bits it would produce
+ are all going to be discarded later by truncating to FOR_TYPE. */
+
+ if (bitschange > 0)
+ {
+ if (! uns || final_prec <= TYPE_PRECISION (TREE_TYPE (op)))
+ win = op;
+ /* TREE_UNSIGNED says whether this is a zero-extension.
+ Let's avoid computing it if it does not affect WIN
+ and if UNS will not be needed again. */
+ if ((uns || TREE_CODE (op) == NOP_EXPR)
+ && TREE_UNSIGNED (TREE_TYPE (op)))
+ {
+ uns = 1;
+ win = op;
+ }
+ }
+ }
+
+ if (TREE_CODE (op) == COMPONENT_REF
+ /* Since type_for_size always gives an integer type. */
+ && TREE_CODE (type) != REAL_TYPE)
+ {
+ unsigned innerprec = TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (op, 1)));
+ type = type_for_size (innerprec, TREE_UNSIGNED (TREE_OPERAND (op, 1)));
+
+ /* We can get this structure field in the narrowest type it fits in.
+ If FOR_TYPE is 0, do this only for a field that matches the
+ narrower type exactly and is aligned for it
+ The resulting extension to its nominal type (a fullword type)
+ must fit the same conditions as for other extensions. */
+
+ if (innerprec < TYPE_PRECISION (TREE_TYPE (op))
+ && (for_type || ! DECL_BIT_FIELD (TREE_OPERAND (op, 1)))
+ && (! uns || final_prec <= innerprec
+ || TREE_UNSIGNED (TREE_OPERAND (op, 1)))
+ && type != 0)
+ {
+ win = build (COMPONENT_REF, type, TREE_OPERAND (op, 0),
+ TREE_OPERAND (op, 1));
+ TREE_SIDE_EFFECTS (win) = TREE_SIDE_EFFECTS (op);
+ TREE_THIS_VOLATILE (win) = TREE_THIS_VOLATILE (op);
+ TREE_RAISES (win) = TREE_RAISES (op);
+ }
+ }
+ return win;
+}
+
+/* Return OP or a simpler expression for a narrower value
+ which can be sign-extended or zero-extended to give back OP.
+ Store in *UNSIGNEDP_PTR either 1 if the value should be zero-extended
+ or 0 if the value should be sign-extended. */
+
+tree
+get_narrower (op, unsignedp_ptr)
+ register tree op;
+ int *unsignedp_ptr;
+{
+ register int uns = 0;
+ int first = 1;
+ register tree win = op;
+
+ while (TREE_CODE (op) == NOP_EXPR)
+ {
+ register int bitschange
+ = TYPE_PRECISION (TREE_TYPE (op))
+ - TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (op, 0)));
+
+ /* Truncations are many-one so cannot be removed. */
+ if (bitschange < 0)
+ break;
+
+ /* See what's inside this conversion. If we decide to strip it,
+ we will set WIN. */
+ op = TREE_OPERAND (op, 0);
+
+ if (bitschange > 0)
+ {
+ /* An extension: the outermost one can be stripped,
+ but remember whether it is zero or sign extension. */
+ if (first)
+ uns = TREE_UNSIGNED (TREE_TYPE (op));
+ /* Otherwise, if a sign extension has been stripped,
+ only sign extensions can now be stripped;
+ if a zero extension has been stripped, only zero-extensions. */
+ else if (uns != TREE_UNSIGNED (TREE_TYPE (op)))
+ break;
+ first = 0;
+ }
+ else /* bitschange == 0 */
+ {
+ /* A change in nominal type can always be stripped, but we must
+ preserve the unsignedness. */
+ if (first)
+ uns = TREE_UNSIGNED (TREE_TYPE (op));
+ first = 0;
+ }
+
+ win = op;
+ }
+
+ if (TREE_CODE (op) == COMPONENT_REF
+ /* Since type_for_size always gives an integer type. */
+ && TREE_CODE (TREE_TYPE (op)) != REAL_TYPE)
+ {
+ unsigned innerprec = TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (op, 1)));
+ tree type = type_for_size (innerprec, TREE_UNSIGNED (op));
+
+ /* We can get this structure field in a narrower type that fits it,
+ but the resulting extension to its nominal type (a fullword type)
+ must satisfy the same conditions as for other extensions.
+
+ Do this only for fields that are aligned (not bit-fields),
+ because when bit-field insns will be used there is no
+ advantage in doing this. */
+
+ if (innerprec < TYPE_PRECISION (TREE_TYPE (op))
+ && ! DECL_BIT_FIELD (TREE_OPERAND (op, 1))
+ && (first || uns == TREE_UNSIGNED (TREE_OPERAND (op, 1)))
+ && type != 0)
+ {
+ if (first)
+ uns = TREE_UNSIGNED (TREE_OPERAND (op, 1));
+ win = build (COMPONENT_REF, type, TREE_OPERAND (op, 0),
+ TREE_OPERAND (op, 1));
+ TREE_SIDE_EFFECTS (win) = TREE_SIDE_EFFECTS (op);
+ TREE_THIS_VOLATILE (win) = TREE_THIS_VOLATILE (op);
+ TREE_RAISES (win) = TREE_RAISES (op);
+ }
+ }
+ *unsignedp_ptr = uns;
+ return win;
+}
+
+/* Return the precision of a type, for arithmetic purposes.
+ Supports all types on which arithmetic is possible
+ (including pointer types).
+ It's not clear yet what will be right for complex types. */
+
+int
+type_precision (type)
+ register tree type;
+{
+ return ((TREE_CODE (type) == INTEGER_TYPE
+ || TREE_CODE (type) == ENUMERAL_TYPE
+ || TREE_CODE (type) == REAL_TYPE)
+ ? TYPE_PRECISION (type) : POINTER_SIZE);
+}
+
+/* Nonzero if integer constant C has a value that is permissible
+ for type TYPE (an INTEGER_TYPE). */
+
+int
+int_fits_type_p (c, type)
+ tree c, type;
+{
+ if (TREE_UNSIGNED (type))
+ return (! (TREE_CODE (TYPE_MAX_VALUE (type)) == INTEGER_CST
+ && INT_CST_LT_UNSIGNED (TYPE_MAX_VALUE (type), c))
+ && ! (TREE_CODE (TYPE_MIN_VALUE (type)) == INTEGER_CST
+ && INT_CST_LT_UNSIGNED (c, TYPE_MIN_VALUE (type))));
+ else
+ return (! (TREE_CODE (TYPE_MAX_VALUE (type)) == INTEGER_CST
+ && INT_CST_LT (TYPE_MAX_VALUE (type), c))
+ && ! (TREE_CODE (TYPE_MIN_VALUE (type)) == INTEGER_CST
+ && INT_CST_LT (c, TYPE_MIN_VALUE (type))));
+}
+
+/* Return the innermost context enclosing DECL that is
+ a FUNCTION_DECL, or zero if none. */
+
+tree
+decl_function_context (decl)
+ tree decl;
+{
+ tree context;
+
+ if (TREE_CODE (decl) == ERROR_MARK)
+ return 0;
+
+ if (TREE_CODE (decl) == SAVE_EXPR)
+ context = SAVE_EXPR_CONTEXT (decl);
+ else
+ context = DECL_CONTEXT (decl);
+
+ while (context && TREE_CODE (context) != FUNCTION_DECL)
+ {
+ if (TREE_CODE (context) == RECORD_TYPE
+ || TREE_CODE (context) == UNION_TYPE)
+ context = TYPE_CONTEXT (context);
+ else if (TREE_CODE (context) == TYPE_DECL)
+ context = DECL_CONTEXT (context);
+ else if (TREE_CODE (context) == BLOCK)
+ context = BLOCK_SUPERCONTEXT (context);
+ else
+ /* Unhandled CONTEXT !? */
+ abort ();
+ }
+
+ return context;
+}
+
+/* Return the innermost context enclosing DECL that is
+ a RECORD_TYPE, UNION_TYPE or QUAL_UNION_TYPE, or zero if none.
+ TYPE_DECLs and FUNCTION_DECLs are transparent to this function. */
+
+tree
+decl_type_context (decl)
+ tree decl;
+{
+ tree context = DECL_CONTEXT (decl);
+
+ while (context)
+ {
+ if (TREE_CODE (context) == RECORD_TYPE
+ || TREE_CODE (context) == UNION_TYPE
+ || TREE_CODE (context) == QUAL_UNION_TYPE)
+ return context;
+ if (TREE_CODE (context) == TYPE_DECL
+ || TREE_CODE (context) == FUNCTION_DECL)
+ context = DECL_CONTEXT (context);
+ else if (TREE_CODE (context) == BLOCK)
+ context = BLOCK_SUPERCONTEXT (context);
+ else
+ /* Unhandled CONTEXT!? */
+ abort ();
+ }
+ return NULL_TREE;
+}
+
+void
+print_obstack_statistics (str, o)
+ char *str;
+ struct obstack *o;
+{
+ struct _obstack_chunk *chunk = o->chunk;
+ int n_chunks = 0;
+ int n_alloc = 0;
+
+ while (chunk)
+ {
+ n_chunks += 1;
+ n_alloc += chunk->limit - &chunk->contents[0];
+ chunk = chunk->prev;
+ }
+ fprintf (stderr, "obstack %s: %d bytes, %d chunks\n",
+ str, n_alloc, n_chunks);
+}
+void
+dump_tree_statistics ()
+{
+ int i;
+ int total_nodes, total_bytes;
+
+ fprintf (stderr, "\n??? tree nodes created\n\n");
+#ifdef GATHER_STATISTICS
+ fprintf (stderr, "Kind Nodes Bytes\n");
+ fprintf (stderr, "-------------------------------------\n");
+ total_nodes = total_bytes = 0;
+ for (i = 0; i < (int) all_kinds; i++)
+ {
+ fprintf (stderr, "%-20s %6d %9d\n", tree_node_kind_names[i],
+ tree_node_counts[i], tree_node_sizes[i]);
+ total_nodes += tree_node_counts[i];
+ total_bytes += tree_node_sizes[i];
+ }
+ fprintf (stderr, "%-20s %9d\n", "identifier names", id_string_size);
+ fprintf (stderr, "-------------------------------------\n");
+ fprintf (stderr, "%-20s %6d %9d\n", "Total", total_nodes, total_bytes);
+ fprintf (stderr, "-------------------------------------\n");
+#else
+ fprintf (stderr, "(No per-node statistics)\n");
+#endif
+ print_lang_statistics ();
+}
+
+#define FILE_FUNCTION_PREFIX_LEN 9
+
+#ifndef NO_DOLLAR_IN_LABEL
+#define FILE_FUNCTION_FORMAT "_GLOBAL_$D$%s"
+#else /* NO_DOLLAR_IN_LABEL */
+#ifndef NO_DOT_IN_LABEL
+#define FILE_FUNCTION_FORMAT "_GLOBAL_.D.%s"
+#else /* NO_DOT_IN_LABEL */
+#define FILE_FUNCTION_FORMAT "_GLOBAL__D_%s"
+#endif /* NO_DOT_IN_LABEL */
+#endif /* NO_DOLLAR_IN_LABEL */
+
+extern char * first_global_object_name;
+
+/* If KIND=='I', return a suitable global initializer (constructor) name.
+ If KIND=='D', return a suitable global clean-up (destructor) name. */
+
+tree
+get_file_function_name (kind)
+ int kind;
+{
+ char *buf;
+ register char *p;
+
+ if (first_global_object_name)
+ p = first_global_object_name;
+ else if (main_input_filename)
+ p = main_input_filename;
+ else
+ p = input_filename;
+
+ buf = (char *) alloca (sizeof (FILE_FUNCTION_FORMAT) + strlen (p));
+
+ /* Set up the name of the file-level functions we may need. */
+ /* Use a global object (which is already required to be unique over
+ the program) rather than the file name (which imposes extra
+ constraints). -- Raeburn@MIT.EDU, 10 Jan 1990. */
+ sprintf (buf, FILE_FUNCTION_FORMAT, p);
+
+ /* Don't need to pull wierd characters out of global names. */
+ if (p != first_global_object_name)
+ {
+ for (p = buf+11; *p; p++)
+ if (! ((*p >= '0' && *p <= '9')
+#if 0 /* we always want labels, which are valid C++ identifiers (+ `$') */
+#ifndef ASM_IDENTIFY_GCC /* this is required if `.' is invalid -- k. raeburn */
+ || *p == '.'
+#endif
+#endif
+#ifndef NO_DOLLAR_IN_LABEL /* this for `$'; unlikely, but... -- kr */
+ || *p == '$'
+#endif
+#ifndef NO_DOT_IN_LABEL /* this for `.'; unlikely, but... */
+ || *p == '.'
+#endif
+ || (*p >= 'A' && *p <= 'Z')
+ || (*p >= 'a' && *p <= 'z')))
+ *p = '_';
+ }
+
+ buf[FILE_FUNCTION_PREFIX_LEN] = kind;
+
+ return get_identifier (buf);
+}
diff --git a/gnu/usr.bin/cc/cc_int/unroll.c b/gnu/usr.bin/cc/cc_int/unroll.c
new file mode 100644
index 0000000..9b968ac
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/unroll.c
@@ -0,0 +1,3345 @@
+/* Try to unroll loops, and split induction variables.
+ Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
+ Contributed by James E. Wilson, Cygnus Support/UC Berkeley.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Try to unroll a loop, and split induction variables.
+
+ Loops for which the number of iterations can be calculated exactly are
+ handled specially. If the number of iterations times the insn_count is
+ less than MAX_UNROLLED_INSNS, then the loop is unrolled completely.
+ Otherwise, we try to unroll the loop a number of times modulo the number
+ of iterations, so that only one exit test will be needed. It is unrolled
+ a number of times approximately equal to MAX_UNROLLED_INSNS divided by
+ the insn count.
+
+ Otherwise, if the number of iterations can be calculated exactly at
+ run time, and the loop is always entered at the top, then we try to
+ precondition the loop. That is, at run time, calculate how many times
+ the loop will execute, and then execute the loop body a few times so
+ that the remaining iterations will be some multiple of 4 (or 2 if the
+ loop is large). Then fall through to a loop unrolled 4 (or 2) times,
+ with only one exit test needed at the end of the loop.
+
+ Otherwise, if the number of iterations can not be calculated exactly,
+ not even at run time, then we still unroll the loop a number of times
+ approximately equal to MAX_UNROLLED_INSNS divided by the insn count,
+ but there must be an exit test after each copy of the loop body.
+
+ For each induction variable, which is dead outside the loop (replaceable)
+ or for which we can easily calculate the final value, if we can easily
+ calculate its value at each place where it is set as a function of the
+ current loop unroll count and the variable's value at loop entry, then
+ the induction variable is split into `N' different variables, one for
+ each copy of the loop body. One variable is live across the backward
+ branch, and the others are all calculated as a function of this variable.
+ This helps eliminate data dependencies, and leads to further opportunities
+ for cse. */
+
+/* Possible improvements follow: */
+
+/* ??? Add an extra pass somewhere to determine whether unrolling will
+ give any benefit. E.g. after generating all unrolled insns, compute the
+ cost of all insns and compare against cost of insns in rolled loop.
+
+ - On traditional architectures, unrolling a non-constant bound loop
+ is a win if there is a giv whose only use is in memory addresses, the
+ memory addresses can be split, and hence giv increments can be
+ eliminated.
+ - It is also a win if the loop is executed many times, and preconditioning
+ can be performed for the loop.
+ Add code to check for these and similar cases. */
+
+/* ??? Improve control of which loops get unrolled. Could use profiling
+ info to only unroll the most commonly executed loops. Perhaps have
+ a user specifyable option to control the amount of code expansion,
+ or the percent of loops to consider for unrolling. Etc. */
+
+/* ??? Look at the register copies inside the loop to see if they form a
+ simple permutation. If so, iterate the permutation until it gets back to
+ the start state. This is how many times we should unroll the loop, for
+ best results, because then all register copies can be eliminated.
+ For example, the lisp nreverse function should be unrolled 3 times
+ while (this)
+ {
+ next = this->cdr;
+ this->cdr = prev;
+ prev = this;
+ this = next;
+ }
+
+ ??? The number of times to unroll the loop may also be based on data
+ references in the loop. For example, if we have a loop that references
+ x[i-1], x[i], and x[i+1], we should unroll it a multiple of 3 times. */
+
+/* ??? Add some simple linear equation solving capability so that we can
+ determine the number of loop iterations for more complex loops.
+ For example, consider this loop from gdb
+ #define SWAP_TARGET_AND_HOST(buffer,len)
+ {
+ char tmp;
+ char *p = (char *) buffer;
+ char *q = ((char *) buffer) + len - 1;
+ int iterations = (len + 1) >> 1;
+ int i;
+ for (p; p < q; p++, q--;)
+ {
+ tmp = *q;
+ *q = *p;
+ *p = tmp;
+ }
+ }
+ Note that:
+ start value = p = &buffer + current_iteration
+ end value = q = &buffer + len - 1 - current_iteration
+ Given the loop exit test of "p < q", then there must be "q - p" iterations,
+ set equal to zero and solve for number of iterations:
+ q - p = len - 1 - 2*current_iteration = 0
+ current_iteration = (len - 1) / 2
+ Hence, there are (len - 1) / 2 (rounded up to the nearest integer)
+ iterations of this loop. */
+
+/* ??? Currently, no labels are marked as loop invariant when doing loop
+ unrolling. This is because an insn inside the loop, that loads the address
+ of a label inside the loop into a register, could be moved outside the loop
+ by the invariant code motion pass if labels were invariant. If the loop
+ is subsequently unrolled, the code will be wrong because each unrolled
+ body of the loop will use the same address, whereas each actually needs a
+ different address. A case where this happens is when a loop containing
+ a switch statement is unrolled.
+
+ It would be better to let labels be considered invariant. When we
+ unroll loops here, check to see if any insns using a label local to the
+ loop were moved before the loop. If so, then correct the problem, by
+ moving the insn back into the loop, or perhaps replicate the insn before
+ the loop, one copy for each time the loop is unrolled. */
+
+/* The prime factors looked for when trying to unroll a loop by some
+ number which is modulo the total number of iterations. Just checking
+ for these 4 prime factors will find at least one factor for 75% of
+ all numbers theoretically. Practically speaking, this will succeed
+ almost all of the time since loops are generally a multiple of 2
+ and/or 5. */
+
+#define NUM_FACTORS 4
+
+struct _factor { int factor, count; } factors[NUM_FACTORS]
+ = { {2, 0}, {3, 0}, {5, 0}, {7, 0}};
+
+/* Describes the different types of loop unrolling performed. */
+
+enum unroll_types { UNROLL_COMPLETELY, UNROLL_MODULO, UNROLL_NAIVE };
+
+#include "config.h"
+#include "rtl.h"
+#include "insn-config.h"
+#include "integrate.h"
+#include "regs.h"
+#include "flags.h"
+#include "expr.h"
+#include <stdio.h>
+#include "loop.h"
+
+/* This controls which loops are unrolled, and by how much we unroll
+ them. */
+
+#ifndef MAX_UNROLLED_INSNS
+#define MAX_UNROLLED_INSNS 100
+#endif
+
+/* Indexed by register number, if non-zero, then it contains a pointer
+ to a struct induction for a DEST_REG giv which has been combined with
+ one of more address givs. This is needed because whenever such a DEST_REG
+ giv is modified, we must modify the value of all split address givs
+ that were combined with this DEST_REG giv. */
+
+static struct induction **addr_combined_regs;
+
+/* Indexed by register number, if this is a splittable induction variable,
+ then this will hold the current value of the register, which depends on the
+ iteration number. */
+
+static rtx *splittable_regs;
+
+/* Indexed by register number, if this is a splittable induction variable,
+ then this will hold the number of instructions in the loop that modify
+ the induction variable. Used to ensure that only the last insn modifying
+ a split iv will update the original iv of the dest. */
+
+static int *splittable_regs_updates;
+
+/* Values describing the current loop's iteration variable. These are set up
+ by loop_iterations, and used by precondition_loop_p. */
+
+static rtx loop_iteration_var;
+static rtx loop_initial_value;
+static rtx loop_increment;
+static rtx loop_final_value;
+
+/* Forward declarations. */
+
+static void init_reg_map ();
+static int precondition_loop_p ();
+static void copy_loop_body ();
+static void iteration_info ();
+static rtx approx_final_value ();
+static int find_splittable_regs ();
+static int find_splittable_givs ();
+static rtx fold_rtx_mult_add ();
+static rtx remap_split_bivs ();
+
+/* Try to unroll one loop and split induction variables in the loop.
+
+ The loop is described by the arguments LOOP_END, INSN_COUNT, and
+ LOOP_START. END_INSERT_BEFORE indicates where insns should be added
+ which need to be executed when the loop falls through. STRENGTH_REDUCTION_P
+ indicates whether information generated in the strength reduction pass
+ is available.
+
+ This function is intended to be called from within `strength_reduce'
+ in loop.c. */
+
+void
+unroll_loop (loop_end, insn_count, loop_start, end_insert_before,
+ strength_reduce_p)
+ rtx loop_end;
+ int insn_count;
+ rtx loop_start;
+ rtx end_insert_before;
+ int strength_reduce_p;
+{
+ int i, j, temp;
+ int unroll_number = 1;
+ rtx copy_start, copy_end;
+ rtx insn, copy, sequence, pattern, tem;
+ int max_labelno, max_insnno;
+ rtx insert_before;
+ struct inline_remap *map;
+ char *local_label;
+ int maxregnum;
+ int new_maxregnum;
+ rtx exit_label = 0;
+ rtx start_label;
+ struct iv_class *bl;
+ int splitting_not_safe = 0;
+ enum unroll_types unroll_type;
+ int loop_preconditioned = 0;
+ rtx safety_label;
+ /* This points to the last real insn in the loop, which should be either
+ a JUMP_INSN (for conditional jumps) or a BARRIER (for unconditional
+ jumps). */
+ rtx last_loop_insn;
+
+ /* Don't bother unrolling huge loops. Since the minimum factor is
+ two, loops greater than one half of MAX_UNROLLED_INSNS will never
+ be unrolled. */
+ if (insn_count > MAX_UNROLLED_INSNS / 2)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "Unrolling failure: Loop too big.\n");
+ return;
+ }
+
+ /* When emitting debugger info, we can't unroll loops with unequal numbers
+ of block_beg and block_end notes, because that would unbalance the block
+ structure of the function. This can happen as a result of the
+ "if (foo) bar; else break;" optimization in jump.c. */
+
+ if (write_symbols != NO_DEBUG)
+ {
+ int block_begins = 0;
+ int block_ends = 0;
+
+ for (insn = loop_start; insn != loop_end; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == NOTE)
+ {
+ if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG)
+ block_begins++;
+ else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END)
+ block_ends++;
+ }
+ }
+
+ if (block_begins != block_ends)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Unrolling failure: Unbalanced block notes.\n");
+ return;
+ }
+ }
+
+ /* Determine type of unroll to perform. Depends on the number of iterations
+ and the size of the loop. */
+
+ /* If there is no strength reduce info, then set loop_n_iterations to zero.
+ This can happen if strength_reduce can't find any bivs in the loop.
+ A value of zero indicates that the number of iterations could not be
+ calculated. */
+
+ if (! strength_reduce_p)
+ loop_n_iterations = 0;
+
+ if (loop_dump_stream && loop_n_iterations > 0)
+ fprintf (loop_dump_stream,
+ "Loop unrolling: %d iterations.\n", loop_n_iterations);
+
+ /* Find and save a pointer to the last nonnote insn in the loop. */
+
+ last_loop_insn = prev_nonnote_insn (loop_end);
+
+ /* Calculate how many times to unroll the loop. Indicate whether or
+ not the loop is being completely unrolled. */
+
+ if (loop_n_iterations == 1)
+ {
+ /* If number of iterations is exactly 1, then eliminate the compare and
+ branch at the end of the loop since they will never be taken.
+ Then return, since no other action is needed here. */
+
+ /* If the last instruction is not a BARRIER or a JUMP_INSN, then
+ don't do anything. */
+
+ if (GET_CODE (last_loop_insn) == BARRIER)
+ {
+ /* Delete the jump insn. This will delete the barrier also. */
+ delete_insn (PREV_INSN (last_loop_insn));
+ }
+ else if (GET_CODE (last_loop_insn) == JUMP_INSN)
+ {
+#ifdef HAVE_cc0
+ /* The immediately preceding insn is a compare which must be
+ deleted. */
+ delete_insn (last_loop_insn);
+ delete_insn (PREV_INSN (last_loop_insn));
+#else
+ /* The immediately preceding insn may not be the compare, so don't
+ delete it. */
+ delete_insn (last_loop_insn);
+#endif
+ }
+ return;
+ }
+ else if (loop_n_iterations > 0
+ && loop_n_iterations * insn_count < MAX_UNROLLED_INSNS)
+ {
+ unroll_number = loop_n_iterations;
+ unroll_type = UNROLL_COMPLETELY;
+ }
+ else if (loop_n_iterations > 0)
+ {
+ /* Try to factor the number of iterations. Don't bother with the
+ general case, only using 2, 3, 5, and 7 will get 75% of all
+ numbers theoretically, and almost all in practice. */
+
+ for (i = 0; i < NUM_FACTORS; i++)
+ factors[i].count = 0;
+
+ temp = loop_n_iterations;
+ for (i = NUM_FACTORS - 1; i >= 0; i--)
+ while (temp % factors[i].factor == 0)
+ {
+ factors[i].count++;
+ temp = temp / factors[i].factor;
+ }
+
+ /* Start with the larger factors first so that we generally
+ get lots of unrolling. */
+
+ unroll_number = 1;
+ temp = insn_count;
+ for (i = 3; i >= 0; i--)
+ while (factors[i].count--)
+ {
+ if (temp * factors[i].factor < MAX_UNROLLED_INSNS)
+ {
+ unroll_number *= factors[i].factor;
+ temp *= factors[i].factor;
+ }
+ else
+ break;
+ }
+
+ /* If we couldn't find any factors, then unroll as in the normal
+ case. */
+ if (unroll_number == 1)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Loop unrolling: No factors found.\n");
+ }
+ else
+ unroll_type = UNROLL_MODULO;
+ }
+
+
+ /* Default case, calculate number of times to unroll loop based on its
+ size. */
+ if (unroll_number == 1)
+ {
+ if (8 * insn_count < MAX_UNROLLED_INSNS)
+ unroll_number = 8;
+ else if (4 * insn_count < MAX_UNROLLED_INSNS)
+ unroll_number = 4;
+ else
+ unroll_number = 2;
+
+ unroll_type = UNROLL_NAIVE;
+ }
+
+ /* Now we know how many times to unroll the loop. */
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Unrolling loop %d times.\n", unroll_number);
+
+
+ if (unroll_type == UNROLL_COMPLETELY || unroll_type == UNROLL_MODULO)
+ {
+ /* Loops of these types should never start with a jump down to
+ the exit condition test. For now, check for this case just to
+ be sure. UNROLL_NAIVE loops can be of this form, this case is
+ handled below. */
+ insn = loop_start;
+ while (GET_CODE (insn) != CODE_LABEL && GET_CODE (insn) != JUMP_INSN)
+ insn = NEXT_INSN (insn);
+ if (GET_CODE (insn) == JUMP_INSN)
+ abort ();
+ }
+
+ if (unroll_type == UNROLL_COMPLETELY)
+ {
+ /* Completely unrolling the loop: Delete the compare and branch at
+ the end (the last two instructions). This delete must done at the
+ very end of loop unrolling, to avoid problems with calls to
+ back_branch_in_range_p, which is called by find_splittable_regs.
+ All increments of splittable bivs/givs are changed to load constant
+ instructions. */
+
+ copy_start = loop_start;
+
+ /* Set insert_before to the instruction immediately after the JUMP_INSN
+ (or BARRIER), so that any NOTEs between the JUMP_INSN and the end of
+ the loop will be correctly handled by copy_loop_body. */
+ insert_before = NEXT_INSN (last_loop_insn);
+
+ /* Set copy_end to the insn before the jump at the end of the loop. */
+ if (GET_CODE (last_loop_insn) == BARRIER)
+ copy_end = PREV_INSN (PREV_INSN (last_loop_insn));
+ else if (GET_CODE (last_loop_insn) == JUMP_INSN)
+ {
+#ifdef HAVE_cc0
+ /* The instruction immediately before the JUMP_INSN is a compare
+ instruction which we do not want to copy. */
+ copy_end = PREV_INSN (PREV_INSN (last_loop_insn));
+#else
+ /* The instruction immediately before the JUMP_INSN may not be the
+ compare, so we must copy it. */
+ copy_end = PREV_INSN (last_loop_insn);
+#endif
+ }
+ else
+ {
+ /* We currently can't unroll a loop if it doesn't end with a
+ JUMP_INSN. There would need to be a mechanism that recognizes
+ this case, and then inserts a jump after each loop body, which
+ jumps to after the last loop body. */
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Unrolling failure: loop does not end with a JUMP_INSN.\n");
+ return;
+ }
+ }
+ else if (unroll_type == UNROLL_MODULO)
+ {
+ /* Partially unrolling the loop: The compare and branch at the end
+ (the last two instructions) must remain. Don't copy the compare
+ and branch instructions at the end of the loop. Insert the unrolled
+ code immediately before the compare/branch at the end so that the
+ code will fall through to them as before. */
+
+ copy_start = loop_start;
+
+ /* Set insert_before to the jump insn at the end of the loop.
+ Set copy_end to before the jump insn at the end of the loop. */
+ if (GET_CODE (last_loop_insn) == BARRIER)
+ {
+ insert_before = PREV_INSN (last_loop_insn);
+ copy_end = PREV_INSN (insert_before);
+ }
+ else if (GET_CODE (last_loop_insn) == JUMP_INSN)
+ {
+#ifdef HAVE_cc0
+ /* The instruction immediately before the JUMP_INSN is a compare
+ instruction which we do not want to copy or delete. */
+ insert_before = PREV_INSN (last_loop_insn);
+ copy_end = PREV_INSN (insert_before);
+#else
+ /* The instruction immediately before the JUMP_INSN may not be the
+ compare, so we must copy it. */
+ insert_before = last_loop_insn;
+ copy_end = PREV_INSN (last_loop_insn);
+#endif
+ }
+ else
+ {
+ /* We currently can't unroll a loop if it doesn't end with a
+ JUMP_INSN. There would need to be a mechanism that recognizes
+ this case, and then inserts a jump after each loop body, which
+ jumps to after the last loop body. */
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Unrolling failure: loop does not end with a JUMP_INSN.\n");
+ return;
+ }
+ }
+ else
+ {
+ /* Normal case: Must copy the compare and branch instructions at the
+ end of the loop. */
+
+ if (GET_CODE (last_loop_insn) == BARRIER)
+ {
+ /* Loop ends with an unconditional jump and a barrier.
+ Handle this like above, don't copy jump and barrier.
+ This is not strictly necessary, but doing so prevents generating
+ unconditional jumps to an immediately following label.
+
+ This will be corrected below if the target of this jump is
+ not the start_label. */
+
+ insert_before = PREV_INSN (last_loop_insn);
+ copy_end = PREV_INSN (insert_before);
+ }
+ else if (GET_CODE (last_loop_insn) == JUMP_INSN)
+ {
+ /* Set insert_before to immediately after the JUMP_INSN, so that
+ NOTEs at the end of the loop will be correctly handled by
+ copy_loop_body. */
+ insert_before = NEXT_INSN (last_loop_insn);
+ copy_end = last_loop_insn;
+ }
+ else
+ {
+ /* We currently can't unroll a loop if it doesn't end with a
+ JUMP_INSN. There would need to be a mechanism that recognizes
+ this case, and then inserts a jump after each loop body, which
+ jumps to after the last loop body. */
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Unrolling failure: loop does not end with a JUMP_INSN.\n");
+ return;
+ }
+
+ /* If copying exit test branches because they can not be eliminated,
+ then must convert the fall through case of the branch to a jump past
+ the end of the loop. Create a label to emit after the loop and save
+ it for later use. Do not use the label after the loop, if any, since
+ it might be used by insns outside the loop, or there might be insns
+ added before it later by final_[bg]iv_value which must be after
+ the real exit label. */
+ exit_label = gen_label_rtx ();
+
+ insn = loop_start;
+ while (GET_CODE (insn) != CODE_LABEL && GET_CODE (insn) != JUMP_INSN)
+ insn = NEXT_INSN (insn);
+
+ if (GET_CODE (insn) == JUMP_INSN)
+ {
+ /* The loop starts with a jump down to the exit condition test.
+ Start copying the loop after the barrier following this
+ jump insn. */
+ copy_start = NEXT_INSN (insn);
+
+ /* Splitting induction variables doesn't work when the loop is
+ entered via a jump to the bottom, because then we end up doing
+ a comparison against a new register for a split variable, but
+ we did not execute the set insn for the new register because
+ it was skipped over. */
+ splitting_not_safe = 1;
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Splitting not safe, because loop not entered at top.\n");
+ }
+ else
+ copy_start = loop_start;
+ }
+
+ /* This should always be the first label in the loop. */
+ start_label = NEXT_INSN (copy_start);
+ /* There may be a line number note and/or a loop continue note here. */
+ while (GET_CODE (start_label) == NOTE)
+ start_label = NEXT_INSN (start_label);
+ if (GET_CODE (start_label) != CODE_LABEL)
+ {
+ /* This can happen as a result of jump threading. If the first insns in
+ the loop test the same condition as the loop's backward jump, or the
+ opposite condition, then the backward jump will be modified to point
+ to elsewhere, and the loop's start label is deleted.
+
+ This case currently can not be handled by the loop unrolling code. */
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Unrolling failure: unknown insns between BEG note and loop label.\n");
+ return;
+ }
+ if (LABEL_NAME (start_label))
+ {
+ /* The jump optimization pass must have combined the original start label
+ with a named label for a goto. We can't unroll this case because
+ jumps which go to the named label must be handled differently than
+ jumps to the loop start, and it is impossible to differentiate them
+ in this case. */
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Unrolling failure: loop start label is gone\n");
+ return;
+ }
+
+ if (unroll_type == UNROLL_NAIVE
+ && GET_CODE (last_loop_insn) == BARRIER
+ && start_label != JUMP_LABEL (PREV_INSN (last_loop_insn)))
+ {
+ /* In this case, we must copy the jump and barrier, because they will
+ not be converted to jumps to an immediately following label. */
+
+ insert_before = NEXT_INSN (last_loop_insn);
+ copy_end = last_loop_insn;
+ }
+
+ /* Allocate a translation table for the labels and insn numbers.
+ They will be filled in as we copy the insns in the loop. */
+
+ max_labelno = max_label_num ();
+ max_insnno = get_max_uid ();
+
+ map = (struct inline_remap *) alloca (sizeof (struct inline_remap));
+
+ map->integrating = 0;
+
+ /* Allocate the label map. */
+
+ if (max_labelno > 0)
+ {
+ map->label_map = (rtx *) alloca (max_labelno * sizeof (rtx));
+
+ local_label = (char *) alloca (max_labelno);
+ bzero (local_label, max_labelno);
+ }
+ else
+ map->label_map = 0;
+
+ /* Search the loop and mark all local labels, i.e. the ones which have to
+ be distinct labels when copied. For all labels which might be
+ non-local, set their label_map entries to point to themselves.
+ If they happen to be local their label_map entries will be overwritten
+ before the loop body is copied. The label_map entries for local labels
+ will be set to a different value each time the loop body is copied. */
+
+ for (insn = copy_start; insn != loop_end; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == CODE_LABEL)
+ local_label[CODE_LABEL_NUMBER (insn)] = 1;
+ else if (GET_CODE (insn) == JUMP_INSN)
+ {
+ if (JUMP_LABEL (insn))
+ map->label_map[CODE_LABEL_NUMBER (JUMP_LABEL (insn))]
+ = JUMP_LABEL (insn);
+ else if (GET_CODE (PATTERN (insn)) == ADDR_VEC
+ || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC)
+ {
+ rtx pat = PATTERN (insn);
+ int diff_vec_p = GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC;
+ int len = XVECLEN (pat, diff_vec_p);
+ rtx label;
+
+ for (i = 0; i < len; i++)
+ {
+ label = XEXP (XVECEXP (pat, diff_vec_p, i), 0);
+ map->label_map[CODE_LABEL_NUMBER (label)] = label;
+ }
+ }
+ }
+ }
+
+ /* Allocate space for the insn map. */
+
+ map->insn_map = (rtx *) alloca (max_insnno * sizeof (rtx));
+
+ /* Set this to zero, to indicate that we are doing loop unrolling,
+ not function inlining. */
+ map->inline_target = 0;
+
+ /* The register and constant maps depend on the number of registers
+ present, so the final maps can't be created until after
+ find_splittable_regs is called. However, they are needed for
+ preconditioning, so we create temporary maps when preconditioning
+ is performed. */
+
+ /* The preconditioning code may allocate two new pseudo registers. */
+ maxregnum = max_reg_num ();
+
+ /* Allocate and zero out the splittable_regs and addr_combined_regs
+ arrays. These must be zeroed here because they will be used if
+ loop preconditioning is performed, and must be zero for that case.
+
+ It is safe to do this here, since the extra registers created by the
+ preconditioning code and find_splittable_regs will never be used
+ to access the splittable_regs[] and addr_combined_regs[] arrays. */
+
+ splittable_regs = (rtx *) alloca (maxregnum * sizeof (rtx));
+ bzero ((char *) splittable_regs, maxregnum * sizeof (rtx));
+ splittable_regs_updates = (int *) alloca (maxregnum * sizeof (int));
+ bzero ((char *) splittable_regs_updates, maxregnum * sizeof (int));
+ addr_combined_regs
+ = (struct induction **) alloca (maxregnum * sizeof (struct induction *));
+ bzero ((char *) addr_combined_regs, maxregnum * sizeof (struct induction *));
+
+ /* If this loop requires exit tests when unrolled, check to see if we
+ can precondition the loop so as to make the exit tests unnecessary.
+ Just like variable splitting, this is not safe if the loop is entered
+ via a jump to the bottom. Also, can not do this if no strength
+ reduce info, because precondition_loop_p uses this info. */
+
+ /* Must copy the loop body for preconditioning before the following
+ find_splittable_regs call since that will emit insns which need to
+ be after the preconditioned loop copies, but immediately before the
+ unrolled loop copies. */
+
+ /* Also, it is not safe to split induction variables for the preconditioned
+ copies of the loop body. If we split induction variables, then the code
+ assumes that each induction variable can be represented as a function
+ of its initial value and the loop iteration number. This is not true
+ in this case, because the last preconditioned copy of the loop body
+ could be any iteration from the first up to the `unroll_number-1'th,
+ depending on the initial value of the iteration variable. Therefore
+ we can not split induction variables here, because we can not calculate
+ their value. Hence, this code must occur before find_splittable_regs
+ is called. */
+
+ if (unroll_type == UNROLL_NAIVE && ! splitting_not_safe && strength_reduce_p)
+ {
+ rtx initial_value, final_value, increment;
+
+ if (precondition_loop_p (&initial_value, &final_value, &increment,
+ loop_start, loop_end))
+ {
+ register rtx diff, temp;
+ enum machine_mode mode;
+ rtx *labels;
+ int abs_inc, neg_inc;
+
+ map->reg_map = (rtx *) alloca (maxregnum * sizeof (rtx));
+
+ map->const_equiv_map = (rtx *) alloca (maxregnum * sizeof (rtx));
+ map->const_age_map = (unsigned *) alloca (maxregnum
+ * sizeof (unsigned));
+ map->const_equiv_map_size = maxregnum;
+ global_const_equiv_map = map->const_equiv_map;
+ global_const_equiv_map_size = maxregnum;
+
+ init_reg_map (map, maxregnum);
+
+ /* Limit loop unrolling to 4, since this will make 7 copies of
+ the loop body. */
+ if (unroll_number > 4)
+ unroll_number = 4;
+
+ /* Save the absolute value of the increment, and also whether or
+ not it is negative. */
+ neg_inc = 0;
+ abs_inc = INTVAL (increment);
+ if (abs_inc < 0)
+ {
+ abs_inc = - abs_inc;
+ neg_inc = 1;
+ }
+
+ start_sequence ();
+
+ /* Decide what mode to do these calculations in. Choose the larger
+ of final_value's mode and initial_value's mode, or a full-word if
+ both are constants. */
+ mode = GET_MODE (final_value);
+ if (mode == VOIDmode)
+ {
+ mode = GET_MODE (initial_value);
+ if (mode == VOIDmode)
+ mode = word_mode;
+ }
+ else if (mode != GET_MODE (initial_value)
+ && (GET_MODE_SIZE (mode)
+ < GET_MODE_SIZE (GET_MODE (initial_value))))
+ mode = GET_MODE (initial_value);
+
+ /* Calculate the difference between the final and initial values.
+ Final value may be a (plus (reg x) (const_int 1)) rtx.
+ Let the following cse pass simplify this if initial value is
+ a constant.
+
+ We must copy the final and initial values here to avoid
+ improperly shared rtl. */
+
+ diff = expand_binop (mode, sub_optab, copy_rtx (final_value),
+ copy_rtx (initial_value), NULL_RTX, 0,
+ OPTAB_LIB_WIDEN);
+
+ /* Now calculate (diff % (unroll * abs (increment))) by using an
+ and instruction. */
+ diff = expand_binop (GET_MODE (diff), and_optab, diff,
+ GEN_INT (unroll_number * abs_inc - 1),
+ NULL_RTX, 0, OPTAB_LIB_WIDEN);
+
+ /* Now emit a sequence of branches to jump to the proper precond
+ loop entry point. */
+
+ labels = (rtx *) alloca (sizeof (rtx) * unroll_number);
+ for (i = 0; i < unroll_number; i++)
+ labels[i] = gen_label_rtx ();
+
+ /* Assuming the unroll_number is 4, and the increment is 2, then
+ for a negative increment: for a positive increment:
+ diff = 0,1 precond 0 diff = 0,7 precond 0
+ diff = 2,3 precond 3 diff = 1,2 precond 1
+ diff = 4,5 precond 2 diff = 3,4 precond 2
+ diff = 6,7 precond 1 diff = 5,6 precond 3 */
+
+ /* We only need to emit (unroll_number - 1) branches here, the
+ last case just falls through to the following code. */
+
+ /* ??? This would give better code if we emitted a tree of branches
+ instead of the current linear list of branches. */
+
+ for (i = 0; i < unroll_number - 1; i++)
+ {
+ int cmp_const;
+
+ /* For negative increments, must invert the constant compared
+ against, except when comparing against zero. */
+ if (i == 0)
+ cmp_const = 0;
+ else if (neg_inc)
+ cmp_const = unroll_number - i;
+ else
+ cmp_const = i;
+
+ emit_cmp_insn (diff, GEN_INT (abs_inc * cmp_const),
+ EQ, NULL_RTX, mode, 0, 0);
+
+ if (i == 0)
+ emit_jump_insn (gen_beq (labels[i]));
+ else if (neg_inc)
+ emit_jump_insn (gen_bge (labels[i]));
+ else
+ emit_jump_insn (gen_ble (labels[i]));
+ JUMP_LABEL (get_last_insn ()) = labels[i];
+ LABEL_NUSES (labels[i])++;
+ }
+
+ /* If the increment is greater than one, then we need another branch,
+ to handle other cases equivalent to 0. */
+
+ /* ??? This should be merged into the code above somehow to help
+ simplify the code here, and reduce the number of branches emitted.
+ For the negative increment case, the branch here could easily
+ be merged with the `0' case branch above. For the positive
+ increment case, it is not clear how this can be simplified. */
+
+ if (abs_inc != 1)
+ {
+ int cmp_const;
+
+ if (neg_inc)
+ cmp_const = abs_inc - 1;
+ else
+ cmp_const = abs_inc * (unroll_number - 1) + 1;
+
+ emit_cmp_insn (diff, GEN_INT (cmp_const), EQ, NULL_RTX,
+ mode, 0, 0);
+
+ if (neg_inc)
+ emit_jump_insn (gen_ble (labels[0]));
+ else
+ emit_jump_insn (gen_bge (labels[0]));
+ JUMP_LABEL (get_last_insn ()) = labels[0];
+ LABEL_NUSES (labels[0])++;
+ }
+
+ sequence = gen_sequence ();
+ end_sequence ();
+ emit_insn_before (sequence, loop_start);
+
+ /* Only the last copy of the loop body here needs the exit
+ test, so set copy_end to exclude the compare/branch here,
+ and then reset it inside the loop when get to the last
+ copy. */
+
+ if (GET_CODE (last_loop_insn) == BARRIER)
+ copy_end = PREV_INSN (PREV_INSN (last_loop_insn));
+ else if (GET_CODE (last_loop_insn) == JUMP_INSN)
+ {
+#ifdef HAVE_cc0
+ /* The immediately preceding insn is a compare which we do not
+ want to copy. */
+ copy_end = PREV_INSN (PREV_INSN (last_loop_insn));
+#else
+ /* The immediately preceding insn may not be a compare, so we
+ must copy it. */
+ copy_end = PREV_INSN (last_loop_insn);
+#endif
+ }
+ else
+ abort ();
+
+ for (i = 1; i < unroll_number; i++)
+ {
+ emit_label_after (labels[unroll_number - i],
+ PREV_INSN (loop_start));
+
+ bzero ((char *) map->insn_map, max_insnno * sizeof (rtx));
+ bzero ((char *) map->const_equiv_map, maxregnum * sizeof (rtx));
+ bzero ((char *) map->const_age_map,
+ maxregnum * sizeof (unsigned));
+ map->const_age = 0;
+
+ for (j = 0; j < max_labelno; j++)
+ if (local_label[j])
+ map->label_map[j] = gen_label_rtx ();
+
+ /* The last copy needs the compare/branch insns at the end,
+ so reset copy_end here if the loop ends with a conditional
+ branch. */
+
+ if (i == unroll_number - 1)
+ {
+ if (GET_CODE (last_loop_insn) == BARRIER)
+ copy_end = PREV_INSN (PREV_INSN (last_loop_insn));
+ else
+ copy_end = last_loop_insn;
+ }
+
+ /* None of the copies are the `last_iteration', so just
+ pass zero for that parameter. */
+ copy_loop_body (copy_start, copy_end, map, exit_label, 0,
+ unroll_type, start_label, loop_end,
+ loop_start, copy_end);
+ }
+ emit_label_after (labels[0], PREV_INSN (loop_start));
+
+ if (GET_CODE (last_loop_insn) == BARRIER)
+ {
+ insert_before = PREV_INSN (last_loop_insn);
+ copy_end = PREV_INSN (insert_before);
+ }
+ else
+ {
+#ifdef HAVE_cc0
+ /* The immediately preceding insn is a compare which we do not
+ want to copy. */
+ insert_before = PREV_INSN (last_loop_insn);
+ copy_end = PREV_INSN (insert_before);
+#else
+ /* The immediately preceding insn may not be a compare, so we
+ must copy it. */
+ insert_before = last_loop_insn;
+ copy_end = PREV_INSN (last_loop_insn);
+#endif
+ }
+
+ /* Set unroll type to MODULO now. */
+ unroll_type = UNROLL_MODULO;
+ loop_preconditioned = 1;
+ }
+ }
+
+ /* If reach here, and the loop type is UNROLL_NAIVE, then don't unroll
+ the loop unless all loops are being unrolled. */
+ if (unroll_type == UNROLL_NAIVE && ! flag_unroll_all_loops)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "Unrolling failure: Naive unrolling not being done.\n");
+ return;
+ }
+
+ /* At this point, we are guaranteed to unroll the loop. */
+
+ /* For each biv and giv, determine whether it can be safely split into
+ a different variable for each unrolled copy of the loop body.
+ We precalculate and save this info here, since computing it is
+ expensive.
+
+ Do this before deleting any instructions from the loop, so that
+ back_branch_in_range_p will work correctly. */
+
+ if (splitting_not_safe)
+ temp = 0;
+ else
+ temp = find_splittable_regs (unroll_type, loop_start, loop_end,
+ end_insert_before, unroll_number);
+
+ /* find_splittable_regs may have created some new registers, so must
+ reallocate the reg_map with the new larger size, and must realloc
+ the constant maps also. */
+
+ maxregnum = max_reg_num ();
+ map->reg_map = (rtx *) alloca (maxregnum * sizeof (rtx));
+
+ init_reg_map (map, maxregnum);
+
+ /* Space is needed in some of the map for new registers, so new_maxregnum
+ is an (over)estimate of how many registers will exist at the end. */
+ new_maxregnum = maxregnum + (temp * unroll_number * 2);
+
+ /* Must realloc space for the constant maps, because the number of registers
+ may have changed. */
+
+ map->const_equiv_map = (rtx *) alloca (new_maxregnum * sizeof (rtx));
+ map->const_age_map = (unsigned *) alloca (new_maxregnum * sizeof (unsigned));
+
+ map->const_equiv_map_size = new_maxregnum;
+ global_const_equiv_map = map->const_equiv_map;
+ global_const_equiv_map_size = new_maxregnum;
+
+ /* Search the list of bivs and givs to find ones which need to be remapped
+ when split, and set their reg_map entry appropriately. */
+
+ for (bl = loop_iv_list; bl; bl = bl->next)
+ {
+ if (REGNO (bl->biv->src_reg) != bl->regno)
+ map->reg_map[bl->regno] = bl->biv->src_reg;
+#if 0
+ /* Currently, non-reduced/final-value givs are never split. */
+ for (v = bl->giv; v; v = v->next_iv)
+ if (REGNO (v->src_reg) != bl->regno)
+ map->reg_map[REGNO (v->dest_reg)] = v->src_reg;
+#endif
+ }
+
+ /* If the loop is being partially unrolled, and the iteration variables
+ are being split, and are being renamed for the split, then must fix up
+ the compare/jump instruction at the end of the loop to refer to the new
+ registers. This compare isn't copied, so the registers used in it
+ will never be replaced if it isn't done here. */
+
+ if (unroll_type == UNROLL_MODULO)
+ {
+ insn = NEXT_INSN (copy_end);
+ if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN)
+ PATTERN (insn) = remap_split_bivs (PATTERN (insn));
+ }
+
+ /* For unroll_number - 1 times, make a copy of each instruction
+ between copy_start and copy_end, and insert these new instructions
+ before the end of the loop. */
+
+ for (i = 0; i < unroll_number; i++)
+ {
+ bzero ((char *) map->insn_map, max_insnno * sizeof (rtx));
+ bzero ((char *) map->const_equiv_map, new_maxregnum * sizeof (rtx));
+ bzero ((char *) map->const_age_map, new_maxregnum * sizeof (unsigned));
+ map->const_age = 0;
+
+ for (j = 0; j < max_labelno; j++)
+ if (local_label[j])
+ map->label_map[j] = gen_label_rtx ();
+
+ /* If loop starts with a branch to the test, then fix it so that
+ it points to the test of the first unrolled copy of the loop. */
+ if (i == 0 && loop_start != copy_start)
+ {
+ insn = PREV_INSN (copy_start);
+ pattern = PATTERN (insn);
+
+ tem = map->label_map[CODE_LABEL_NUMBER
+ (XEXP (SET_SRC (pattern), 0))];
+ SET_SRC (pattern) = gen_rtx (LABEL_REF, VOIDmode, tem);
+
+ /* Set the jump label so that it can be used by later loop unrolling
+ passes. */
+ JUMP_LABEL (insn) = tem;
+ LABEL_NUSES (tem)++;
+ }
+
+ copy_loop_body (copy_start, copy_end, map, exit_label,
+ i == unroll_number - 1, unroll_type, start_label,
+ loop_end, insert_before, insert_before);
+ }
+
+ /* Before deleting any insns, emit a CODE_LABEL immediately after the last
+ insn to be deleted. This prevents any runaway delete_insn call from
+ more insns that it should, as it always stops at a CODE_LABEL. */
+
+ /* Delete the compare and branch at the end of the loop if completely
+ unrolling the loop. Deleting the backward branch at the end also
+ deletes the code label at the start of the loop. This is done at
+ the very end to avoid problems with back_branch_in_range_p. */
+
+ if (unroll_type == UNROLL_COMPLETELY)
+ safety_label = emit_label_after (gen_label_rtx (), last_loop_insn);
+ else
+ safety_label = emit_label_after (gen_label_rtx (), copy_end);
+
+ /* Delete all of the original loop instructions. Don't delete the
+ LOOP_BEG note, or the first code label in the loop. */
+
+ insn = NEXT_INSN (copy_start);
+ while (insn != safety_label)
+ {
+ if (insn != start_label)
+ insn = delete_insn (insn);
+ else
+ insn = NEXT_INSN (insn);
+ }
+
+ /* Can now delete the 'safety' label emitted to protect us from runaway
+ delete_insn calls. */
+ if (INSN_DELETED_P (safety_label))
+ abort ();
+ delete_insn (safety_label);
+
+ /* If exit_label exists, emit it after the loop. Doing the emit here
+ forces it to have a higher INSN_UID than any insn in the unrolled loop.
+ This is needed so that mostly_true_jump in reorg.c will treat jumps
+ to this loop end label correctly, i.e. predict that they are usually
+ not taken. */
+ if (exit_label)
+ emit_label_after (exit_label, loop_end);
+}
+
+/* Return true if the loop can be safely, and profitably, preconditioned
+ so that the unrolled copies of the loop body don't need exit tests.
+
+ This only works if final_value, initial_value and increment can be
+ determined, and if increment is a constant power of 2.
+ If increment is not a power of 2, then the preconditioning modulo
+ operation would require a real modulo instead of a boolean AND, and this
+ is not considered `profitable'. */
+
+/* ??? If the loop is known to be executed very many times, or the machine
+ has a very cheap divide instruction, then preconditioning is a win even
+ when the increment is not a power of 2. Use RTX_COST to compute
+ whether divide is cheap. */
+
+static int
+precondition_loop_p (initial_value, final_value, increment, loop_start,
+ loop_end)
+ rtx *initial_value, *final_value, *increment;
+ rtx loop_start, loop_end;
+{
+
+ if (loop_n_iterations > 0)
+ {
+ *initial_value = const0_rtx;
+ *increment = const1_rtx;
+ *final_value = GEN_INT (loop_n_iterations);
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Preconditioning: Success, number of iterations known, %d.\n",
+ loop_n_iterations);
+ return 1;
+ }
+
+ if (loop_initial_value == 0)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Preconditioning: Could not find initial value.\n");
+ return 0;
+ }
+ else if (loop_increment == 0)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Preconditioning: Could not find increment value.\n");
+ return 0;
+ }
+ else if (GET_CODE (loop_increment) != CONST_INT)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Preconditioning: Increment not a constant.\n");
+ return 0;
+ }
+ else if ((exact_log2 (INTVAL (loop_increment)) < 0)
+ && (exact_log2 (- INTVAL (loop_increment)) < 0))
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Preconditioning: Increment not a constant power of 2.\n");
+ return 0;
+ }
+
+ /* Unsigned_compare and compare_dir can be ignored here, since they do
+ not matter for preconditioning. */
+
+ if (loop_final_value == 0)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Preconditioning: EQ comparison loop.\n");
+ return 0;
+ }
+
+ /* Must ensure that final_value is invariant, so call invariant_p to
+ check. Before doing so, must check regno against max_reg_before_loop
+ to make sure that the register is in the range covered by invariant_p.
+ If it isn't, then it is most likely a biv/giv which by definition are
+ not invariant. */
+ if ((GET_CODE (loop_final_value) == REG
+ && REGNO (loop_final_value) >= max_reg_before_loop)
+ || (GET_CODE (loop_final_value) == PLUS
+ && REGNO (XEXP (loop_final_value, 0)) >= max_reg_before_loop)
+ || ! invariant_p (loop_final_value))
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Preconditioning: Final value not invariant.\n");
+ return 0;
+ }
+
+ /* Fail for floating point values, since the caller of this function
+ does not have code to deal with them. */
+ if (GET_MODE_CLASS (GET_MODE (loop_final_value)) == MODE_FLOAT
+ || GET_MODE_CLASS (GET_MODE (loop_initial_value)) == MODE_FLOAT)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Preconditioning: Floating point final or initial value.\n");
+ return 0;
+ }
+
+ /* Now set initial_value to be the iteration_var, since that may be a
+ simpler expression, and is guaranteed to be correct if all of the
+ above tests succeed.
+
+ We can not use the initial_value as calculated, because it will be
+ one too small for loops of the form "while (i-- > 0)". We can not
+ emit code before the loop_skip_over insns to fix this problem as this
+ will then give a number one too large for loops of the form
+ "while (--i > 0)".
+
+ Note that all loops that reach here are entered at the top, because
+ this function is not called if the loop starts with a jump. */
+
+ /* Fail if loop_iteration_var is not live before loop_start, since we need
+ to test its value in the preconditioning code. */
+
+ if (uid_luid[regno_first_uid[REGNO (loop_iteration_var)]]
+ > INSN_LUID (loop_start))
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Preconditioning: Iteration var not live before loop start.\n");
+ return 0;
+ }
+
+ *initial_value = loop_iteration_var;
+ *increment = loop_increment;
+ *final_value = loop_final_value;
+
+ /* Success! */
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "Preconditioning: Successful.\n");
+ return 1;
+}
+
+
+/* All pseudo-registers must be mapped to themselves. Two hard registers
+ must be mapped, VIRTUAL_STACK_VARS_REGNUM and VIRTUAL_INCOMING_ARGS_
+ REGNUM, to avoid function-inlining specific conversions of these
+ registers. All other hard regs can not be mapped because they may be
+ used with different
+ modes. */
+
+static void
+init_reg_map (map, maxregnum)
+ struct inline_remap *map;
+ int maxregnum;
+{
+ int i;
+
+ for (i = maxregnum - 1; i > LAST_VIRTUAL_REGISTER; i--)
+ map->reg_map[i] = regno_reg_rtx[i];
+ /* Just clear the rest of the entries. */
+ for (i = LAST_VIRTUAL_REGISTER; i >= 0; i--)
+ map->reg_map[i] = 0;
+
+ map->reg_map[VIRTUAL_STACK_VARS_REGNUM]
+ = regno_reg_rtx[VIRTUAL_STACK_VARS_REGNUM];
+ map->reg_map[VIRTUAL_INCOMING_ARGS_REGNUM]
+ = regno_reg_rtx[VIRTUAL_INCOMING_ARGS_REGNUM];
+}
+
+/* Strength-reduction will often emit code for optimized biv/givs which
+ calculates their value in a temporary register, and then copies the result
+ to the iv. This procedure reconstructs the pattern computing the iv;
+ verifying that all operands are of the proper form.
+
+ The return value is the amount that the giv is incremented by. */
+
+static rtx
+calculate_giv_inc (pattern, src_insn, regno)
+ rtx pattern, src_insn;
+ int regno;
+{
+ rtx increment;
+ rtx increment_total = 0;
+ int tries = 0;
+
+ retry:
+ /* Verify that we have an increment insn here. First check for a plus
+ as the set source. */
+ if (GET_CODE (SET_SRC (pattern)) != PLUS)
+ {
+ /* SR sometimes computes the new giv value in a temp, then copies it
+ to the new_reg. */
+ src_insn = PREV_INSN (src_insn);
+ pattern = PATTERN (src_insn);
+ if (GET_CODE (SET_SRC (pattern)) != PLUS)
+ abort ();
+
+ /* The last insn emitted is not needed, so delete it to avoid confusing
+ the second cse pass. This insn sets the giv unnecessarily. */
+ delete_insn (get_last_insn ());
+ }
+
+ /* Verify that we have a constant as the second operand of the plus. */
+ increment = XEXP (SET_SRC (pattern), 1);
+ if (GET_CODE (increment) != CONST_INT)
+ {
+ /* SR sometimes puts the constant in a register, especially if it is
+ too big to be an add immed operand. */
+ src_insn = PREV_INSN (src_insn);
+ increment = SET_SRC (PATTERN (src_insn));
+
+ /* SR may have used LO_SUM to compute the constant if it is too large
+ for a load immed operand. In this case, the constant is in operand
+ one of the LO_SUM rtx. */
+ if (GET_CODE (increment) == LO_SUM)
+ increment = XEXP (increment, 1);
+
+ if (GET_CODE (increment) != CONST_INT)
+ abort ();
+
+ /* The insn loading the constant into a register is not longer needed,
+ so delete it. */
+ delete_insn (get_last_insn ());
+ }
+
+ if (increment_total)
+ increment_total = GEN_INT (INTVAL (increment_total) + INTVAL (increment));
+ else
+ increment_total = increment;
+
+ /* Check that the source register is the same as the register we expected
+ to see as the source. If not, something is seriously wrong. */
+ if (GET_CODE (XEXP (SET_SRC (pattern), 0)) != REG
+ || REGNO (XEXP (SET_SRC (pattern), 0)) != regno)
+ {
+ /* Some machines (e.g. the romp), may emit two add instructions for
+ certain constants, so lets try looking for another add immediately
+ before this one if we have only seen one add insn so far. */
+
+ if (tries == 0)
+ {
+ tries++;
+
+ src_insn = PREV_INSN (src_insn);
+ pattern = PATTERN (src_insn);
+
+ delete_insn (get_last_insn ());
+
+ goto retry;
+ }
+
+ abort ();
+ }
+
+ return increment_total;
+}
+
+/* Copy REG_NOTES, except for insn references, because not all insn_map
+ entries are valid yet. We do need to copy registers now though, because
+ the reg_map entries can change during copying. */
+
+static rtx
+initial_reg_note_copy (notes, map)
+ rtx notes;
+ struct inline_remap *map;
+{
+ rtx copy;
+
+ if (notes == 0)
+ return 0;
+
+ copy = rtx_alloc (GET_CODE (notes));
+ PUT_MODE (copy, GET_MODE (notes));
+
+ if (GET_CODE (notes) == EXPR_LIST)
+ XEXP (copy, 0) = copy_rtx_and_substitute (XEXP (notes, 0), map);
+ else if (GET_CODE (notes) == INSN_LIST)
+ /* Don't substitute for these yet. */
+ XEXP (copy, 0) = XEXP (notes, 0);
+ else
+ abort ();
+
+ XEXP (copy, 1) = initial_reg_note_copy (XEXP (notes, 1), map);
+
+ return copy;
+}
+
+/* Fixup insn references in copied REG_NOTES. */
+
+static void
+final_reg_note_copy (notes, map)
+ rtx notes;
+ struct inline_remap *map;
+{
+ rtx note;
+
+ for (note = notes; note; note = XEXP (note, 1))
+ if (GET_CODE (note) == INSN_LIST)
+ XEXP (note, 0) = map->insn_map[INSN_UID (XEXP (note, 0))];
+}
+
+/* Copy each instruction in the loop, substituting from map as appropriate.
+ This is very similar to a loop in expand_inline_function. */
+
+static void
+copy_loop_body (copy_start, copy_end, map, exit_label, last_iteration,
+ unroll_type, start_label, loop_end, insert_before,
+ copy_notes_from)
+ rtx copy_start, copy_end;
+ struct inline_remap *map;
+ rtx exit_label;
+ int last_iteration;
+ enum unroll_types unroll_type;
+ rtx start_label, loop_end, insert_before, copy_notes_from;
+{
+ rtx insn, pattern;
+ rtx tem, copy;
+ int dest_reg_was_split, i;
+ rtx cc0_insn = 0;
+ rtx final_label = 0;
+ rtx giv_inc, giv_dest_reg, giv_src_reg;
+
+ /* If this isn't the last iteration, then map any references to the
+ start_label to final_label. Final label will then be emitted immediately
+ after the end of this loop body if it was ever used.
+
+ If this is the last iteration, then map references to the start_label
+ to itself. */
+ if (! last_iteration)
+ {
+ final_label = gen_label_rtx ();
+ map->label_map[CODE_LABEL_NUMBER (start_label)] = final_label;
+ }
+ else
+ map->label_map[CODE_LABEL_NUMBER (start_label)] = start_label;
+
+ start_sequence ();
+
+ insn = copy_start;
+ do
+ {
+ insn = NEXT_INSN (insn);
+
+ map->orig_asm_operands_vector = 0;
+
+ switch (GET_CODE (insn))
+ {
+ case INSN:
+ pattern = PATTERN (insn);
+ copy = 0;
+ giv_inc = 0;
+
+ /* Check to see if this is a giv that has been combined with
+ some split address givs. (Combined in the sense that
+ `combine_givs' in loop.c has put two givs in the same register.)
+ In this case, we must search all givs based on the same biv to
+ find the address givs. Then split the address givs.
+ Do this before splitting the giv, since that may map the
+ SET_DEST to a new register. */
+
+ if (GET_CODE (pattern) == SET
+ && GET_CODE (SET_DEST (pattern)) == REG
+ && addr_combined_regs[REGNO (SET_DEST (pattern))])
+ {
+ struct iv_class *bl;
+ struct induction *v, *tv;
+ int regno = REGNO (SET_DEST (pattern));
+
+ v = addr_combined_regs[REGNO (SET_DEST (pattern))];
+ bl = reg_biv_class[REGNO (v->src_reg)];
+
+ /* Although the giv_inc amount is not needed here, we must call
+ calculate_giv_inc here since it might try to delete the
+ last insn emitted. If we wait until later to call it,
+ we might accidentally delete insns generated immediately
+ below by emit_unrolled_add. */
+
+ giv_inc = calculate_giv_inc (pattern, insn, regno);
+
+ /* Now find all address giv's that were combined with this
+ giv 'v'. */
+ for (tv = bl->giv; tv; tv = tv->next_iv)
+ if (tv->giv_type == DEST_ADDR && tv->same == v)
+ {
+ int this_giv_inc = INTVAL (giv_inc);
+
+ /* Scale this_giv_inc if the multiplicative factors of
+ the two givs are different. */
+ if (tv->mult_val != v->mult_val)
+ this_giv_inc = (this_giv_inc / INTVAL (v->mult_val)
+ * INTVAL (tv->mult_val));
+
+ tv->dest_reg = plus_constant (tv->dest_reg, this_giv_inc);
+ *tv->location = tv->dest_reg;
+
+ if (last_iteration && unroll_type != UNROLL_COMPLETELY)
+ {
+ /* Must emit an insn to increment the split address
+ giv. Add in the const_adjust field in case there
+ was a constant eliminated from the address. */
+ rtx value, dest_reg;
+
+ /* tv->dest_reg will be either a bare register,
+ or else a register plus a constant. */
+ if (GET_CODE (tv->dest_reg) == REG)
+ dest_reg = tv->dest_reg;
+ else
+ dest_reg = XEXP (tv->dest_reg, 0);
+
+ /* Check for shared address givs, and avoid
+ incrementing the shared psuedo reg more than
+ once. */
+ if (! (tv != v && tv->insn == v->insn
+ && tv->new_reg == v->new_reg))
+ {
+ /* tv->dest_reg may actually be a (PLUS (REG)
+ (CONST)) here, so we must call plus_constant
+ to add the const_adjust amount before calling
+ emit_unrolled_add below. */
+ value = plus_constant (tv->dest_reg,
+ tv->const_adjust);
+
+ /* The constant could be too large for an add
+ immediate, so can't directly emit an insn
+ here. */
+ emit_unrolled_add (dest_reg, XEXP (value, 0),
+ XEXP (value, 1));
+ }
+
+ /* Reset the giv to be just the register again, in case
+ it is used after the set we have just emitted.
+ We must subtract the const_adjust factor added in
+ above. */
+ tv->dest_reg = plus_constant (dest_reg,
+ - tv->const_adjust);
+ *tv->location = tv->dest_reg;
+ }
+ }
+ }
+
+ /* If this is a setting of a splittable variable, then determine
+ how to split the variable, create a new set based on this split,
+ and set up the reg_map so that later uses of the variable will
+ use the new split variable. */
+
+ dest_reg_was_split = 0;
+
+ if (GET_CODE (pattern) == SET
+ && GET_CODE (SET_DEST (pattern)) == REG
+ && splittable_regs[REGNO (SET_DEST (pattern))])
+ {
+ int regno = REGNO (SET_DEST (pattern));
+
+ dest_reg_was_split = 1;
+
+ /* Compute the increment value for the giv, if it wasn't
+ already computed above. */
+
+ if (giv_inc == 0)
+ giv_inc = calculate_giv_inc (pattern, insn, regno);
+ giv_dest_reg = SET_DEST (pattern);
+ giv_src_reg = SET_DEST (pattern);
+
+ if (unroll_type == UNROLL_COMPLETELY)
+ {
+ /* Completely unrolling the loop. Set the induction
+ variable to a known constant value. */
+
+ /* The value in splittable_regs may be an invariant
+ value, so we must use plus_constant here. */
+ splittable_regs[regno]
+ = plus_constant (splittable_regs[regno], INTVAL (giv_inc));
+
+ if (GET_CODE (splittable_regs[regno]) == PLUS)
+ {
+ giv_src_reg = XEXP (splittable_regs[regno], 0);
+ giv_inc = XEXP (splittable_regs[regno], 1);
+ }
+ else
+ {
+ /* The splittable_regs value must be a REG or a
+ CONST_INT, so put the entire value in the giv_src_reg
+ variable. */
+ giv_src_reg = splittable_regs[regno];
+ giv_inc = const0_rtx;
+ }
+ }
+ else
+ {
+ /* Partially unrolling loop. Create a new pseudo
+ register for the iteration variable, and set it to
+ be a constant plus the original register. Except
+ on the last iteration, when the result has to
+ go back into the original iteration var register. */
+
+ /* Handle bivs which must be mapped to a new register
+ when split. This happens for bivs which need their
+ final value set before loop entry. The new register
+ for the biv was stored in the biv's first struct
+ induction entry by find_splittable_regs. */
+
+ if (regno < max_reg_before_loop
+ && reg_iv_type[regno] == BASIC_INDUCT)
+ {
+ giv_src_reg = reg_biv_class[regno]->biv->src_reg;
+ giv_dest_reg = giv_src_reg;
+ }
+
+#if 0
+ /* If non-reduced/final-value givs were split, then
+ this would have to remap those givs also. See
+ find_splittable_regs. */
+#endif
+
+ splittable_regs[regno]
+ = GEN_INT (INTVAL (giv_inc)
+ + INTVAL (splittable_regs[regno]));
+ giv_inc = splittable_regs[regno];
+
+ /* Now split the induction variable by changing the dest
+ of this insn to a new register, and setting its
+ reg_map entry to point to this new register.
+
+ If this is the last iteration, and this is the last insn
+ that will update the iv, then reuse the original dest,
+ to ensure that the iv will have the proper value when
+ the loop exits or repeats.
+
+ Using splittable_regs_updates here like this is safe,
+ because it can only be greater than one if all
+ instructions modifying the iv are always executed in
+ order. */
+
+ if (! last_iteration
+ || (splittable_regs_updates[regno]-- != 1))
+ {
+ tem = gen_reg_rtx (GET_MODE (giv_src_reg));
+ giv_dest_reg = tem;
+ map->reg_map[regno] = tem;
+ }
+ else
+ map->reg_map[regno] = giv_src_reg;
+ }
+
+ /* The constant being added could be too large for an add
+ immediate, so can't directly emit an insn here. */
+ emit_unrolled_add (giv_dest_reg, giv_src_reg, giv_inc);
+ copy = get_last_insn ();
+ pattern = PATTERN (copy);
+ }
+ else
+ {
+ pattern = copy_rtx_and_substitute (pattern, map);
+ copy = emit_insn (pattern);
+ }
+ REG_NOTES (copy) = initial_reg_note_copy (REG_NOTES (insn), map);
+
+#ifdef HAVE_cc0
+ /* If this insn is setting CC0, it may need to look at
+ the insn that uses CC0 to see what type of insn it is.
+ In that case, the call to recog via validate_change will
+ fail. So don't substitute constants here. Instead,
+ do it when we emit the following insn.
+
+ For example, see the pyr.md file. That machine has signed and
+ unsigned compares. The compare patterns must check the
+ following branch insn to see which what kind of compare to
+ emit.
+
+ If the previous insn set CC0, substitute constants on it as
+ well. */
+ if (sets_cc0_p (copy) != 0)
+ cc0_insn = copy;
+ else
+ {
+ if (cc0_insn)
+ try_constants (cc0_insn, map);
+ cc0_insn = 0;
+ try_constants (copy, map);
+ }
+#else
+ try_constants (copy, map);
+#endif
+
+ /* Make split induction variable constants `permanent' since we
+ know there are no backward branches across iteration variable
+ settings which would invalidate this. */
+ if (dest_reg_was_split)
+ {
+ int regno = REGNO (SET_DEST (pattern));
+
+ if (regno < map->const_equiv_map_size
+ && map->const_age_map[regno] == map->const_age)
+ map->const_age_map[regno] = -1;
+ }
+ break;
+
+ case JUMP_INSN:
+ pattern = copy_rtx_and_substitute (PATTERN (insn), map);
+ copy = emit_jump_insn (pattern);
+ REG_NOTES (copy) = initial_reg_note_copy (REG_NOTES (insn), map);
+
+ if (JUMP_LABEL (insn) == start_label && insn == copy_end
+ && ! last_iteration)
+ {
+ /* This is a branch to the beginning of the loop; this is the
+ last insn being copied; and this is not the last iteration.
+ In this case, we want to change the original fall through
+ case to be a branch past the end of the loop, and the
+ original jump label case to fall_through. */
+
+ if (! invert_exp (pattern, copy)
+ || ! redirect_exp (&pattern,
+ map->label_map[CODE_LABEL_NUMBER
+ (JUMP_LABEL (insn))],
+ exit_label, copy))
+ abort ();
+ }
+
+#ifdef HAVE_cc0
+ if (cc0_insn)
+ try_constants (cc0_insn, map);
+ cc0_insn = 0;
+#endif
+ try_constants (copy, map);
+
+ /* Set the jump label of COPY correctly to avoid problems with
+ later passes of unroll_loop, if INSN had jump label set. */
+ if (JUMP_LABEL (insn))
+ {
+ rtx label = 0;
+
+ /* Can't use the label_map for every insn, since this may be
+ the backward branch, and hence the label was not mapped. */
+ if (GET_CODE (pattern) == SET)
+ {
+ tem = SET_SRC (pattern);
+ if (GET_CODE (tem) == LABEL_REF)
+ label = XEXP (tem, 0);
+ else if (GET_CODE (tem) == IF_THEN_ELSE)
+ {
+ if (XEXP (tem, 1) != pc_rtx)
+ label = XEXP (XEXP (tem, 1), 0);
+ else
+ label = XEXP (XEXP (tem, 2), 0);
+ }
+ }
+
+ if (label && GET_CODE (label) == CODE_LABEL)
+ JUMP_LABEL (copy) = label;
+ else
+ {
+ /* An unrecognizable jump insn, probably the entry jump
+ for a switch statement. This label must have been mapped,
+ so just use the label_map to get the new jump label. */
+ JUMP_LABEL (copy) = map->label_map[CODE_LABEL_NUMBER
+ (JUMP_LABEL (insn))];
+ }
+
+ /* If this is a non-local jump, then must increase the label
+ use count so that the label will not be deleted when the
+ original jump is deleted. */
+ LABEL_NUSES (JUMP_LABEL (copy))++;
+ }
+ else if (GET_CODE (PATTERN (copy)) == ADDR_VEC
+ || GET_CODE (PATTERN (copy)) == ADDR_DIFF_VEC)
+ {
+ rtx pat = PATTERN (copy);
+ int diff_vec_p = GET_CODE (pat) == ADDR_DIFF_VEC;
+ int len = XVECLEN (pat, diff_vec_p);
+ int i;
+
+ for (i = 0; i < len; i++)
+ LABEL_NUSES (XEXP (XVECEXP (pat, diff_vec_p, i), 0))++;
+ }
+
+ /* If this used to be a conditional jump insn but whose branch
+ direction is now known, we must do something special. */
+ if (condjump_p (insn) && !simplejump_p (insn) && map->last_pc_value)
+ {
+#ifdef HAVE_cc0
+ /* The previous insn set cc0 for us. So delete it. */
+ delete_insn (PREV_INSN (copy));
+#endif
+
+ /* If this is now a no-op, delete it. */
+ if (map->last_pc_value == pc_rtx)
+ {
+ /* Don't let delete_insn delete the label referenced here,
+ because we might possibly need it later for some other
+ instruction in the loop. */
+ if (JUMP_LABEL (copy))
+ LABEL_NUSES (JUMP_LABEL (copy))++;
+ delete_insn (copy);
+ if (JUMP_LABEL (copy))
+ LABEL_NUSES (JUMP_LABEL (copy))--;
+ copy = 0;
+ }
+ else
+ /* Otherwise, this is unconditional jump so we must put a
+ BARRIER after it. We could do some dead code elimination
+ here, but jump.c will do it just as well. */
+ emit_barrier ();
+ }
+ break;
+
+ case CALL_INSN:
+ pattern = copy_rtx_and_substitute (PATTERN (insn), map);
+ copy = emit_call_insn (pattern);
+ REG_NOTES (copy) = initial_reg_note_copy (REG_NOTES (insn), map);
+
+ /* Because the USAGE information potentially contains objects other
+ than hard registers, we need to copy it. */
+ CALL_INSN_FUNCTION_USAGE (copy) =
+ copy_rtx_and_substitute (CALL_INSN_FUNCTION_USAGE (insn), map);
+
+#ifdef HAVE_cc0
+ if (cc0_insn)
+ try_constants (cc0_insn, map);
+ cc0_insn = 0;
+#endif
+ try_constants (copy, map);
+
+ /* Be lazy and assume CALL_INSNs clobber all hard registers. */
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ map->const_equiv_map[i] = 0;
+ break;
+
+ case CODE_LABEL:
+ /* If this is the loop start label, then we don't need to emit a
+ copy of this label since no one will use it. */
+
+ if (insn != start_label)
+ {
+ copy = emit_label (map->label_map[CODE_LABEL_NUMBER (insn)]);
+ map->const_age++;
+ }
+ break;
+
+ case BARRIER:
+ copy = emit_barrier ();
+ break;
+
+ case NOTE:
+ /* VTOP notes are valid only before the loop exit test. If placed
+ anywhere else, loop may generate bad code. */
+
+ if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED
+ && (NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_VTOP
+ || (last_iteration && unroll_type != UNROLL_COMPLETELY)))
+ copy = emit_note (NOTE_SOURCE_FILE (insn),
+ NOTE_LINE_NUMBER (insn));
+ else
+ copy = 0;
+ break;
+
+ default:
+ abort ();
+ break;
+ }
+
+ map->insn_map[INSN_UID (insn)] = copy;
+ }
+ while (insn != copy_end);
+
+ /* Now finish coping the REG_NOTES. */
+ insn = copy_start;
+ do
+ {
+ insn = NEXT_INSN (insn);
+ if ((GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN
+ || GET_CODE (insn) == CALL_INSN)
+ && map->insn_map[INSN_UID (insn)])
+ final_reg_note_copy (REG_NOTES (map->insn_map[INSN_UID (insn)]), map);
+ }
+ while (insn != copy_end);
+
+ /* There may be notes between copy_notes_from and loop_end. Emit a copy of
+ each of these notes here, since there may be some important ones, such as
+ NOTE_INSN_BLOCK_END notes, in this group. We don't do this on the last
+ iteration, because the original notes won't be deleted.
+
+ We can't use insert_before here, because when from preconditioning,
+ insert_before points before the loop. We can't use copy_end, because
+ there may be insns already inserted after it (which we don't want to
+ copy) when not from preconditioning code. */
+
+ if (! last_iteration)
+ {
+ for (insn = copy_notes_from; insn != loop_end; insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == NOTE
+ && NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED)
+ emit_note (NOTE_SOURCE_FILE (insn), NOTE_LINE_NUMBER (insn));
+ }
+ }
+
+ if (final_label && LABEL_NUSES (final_label) > 0)
+ emit_label (final_label);
+
+ tem = gen_sequence ();
+ end_sequence ();
+ emit_insn_before (tem, insert_before);
+}
+
+/* Emit an insn, using the expand_binop to ensure that a valid insn is
+ emitted. This will correctly handle the case where the increment value
+ won't fit in the immediate field of a PLUS insns. */
+
+void
+emit_unrolled_add (dest_reg, src_reg, increment)
+ rtx dest_reg, src_reg, increment;
+{
+ rtx result;
+
+ result = expand_binop (GET_MODE (dest_reg), add_optab, src_reg, increment,
+ dest_reg, 0, OPTAB_LIB_WIDEN);
+
+ if (dest_reg != result)
+ emit_move_insn (dest_reg, result);
+}
+
+/* Searches the insns between INSN and LOOP_END. Returns 1 if there
+ is a backward branch in that range that branches to somewhere between
+ LOOP_START and INSN. Returns 0 otherwise. */
+
+/* ??? This is quadratic algorithm. Could be rewritten to be linear.
+ In practice, this is not a problem, because this function is seldom called,
+ and uses a negligible amount of CPU time on average. */
+
+static int
+back_branch_in_range_p (insn, loop_start, loop_end)
+ rtx insn;
+ rtx loop_start, loop_end;
+{
+ rtx p, q, target_insn;
+
+ /* Stop before we get to the backward branch at the end of the loop. */
+ loop_end = prev_nonnote_insn (loop_end);
+ if (GET_CODE (loop_end) == BARRIER)
+ loop_end = PREV_INSN (loop_end);
+
+ /* Check in case insn has been deleted, search forward for first non
+ deleted insn following it. */
+ while (INSN_DELETED_P (insn))
+ insn = NEXT_INSN (insn);
+
+ /* Check for the case where insn is the last insn in the loop. */
+ if (insn == loop_end)
+ return 0;
+
+ for (p = NEXT_INSN (insn); p != loop_end; p = NEXT_INSN (p))
+ {
+ if (GET_CODE (p) == JUMP_INSN)
+ {
+ target_insn = JUMP_LABEL (p);
+
+ /* Search from loop_start to insn, to see if one of them is
+ the target_insn. We can't use INSN_LUID comparisons here,
+ since insn may not have an LUID entry. */
+ for (q = loop_start; q != insn; q = NEXT_INSN (q))
+ if (q == target_insn)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Try to generate the simplest rtx for the expression
+ (PLUS (MULT mult1 mult2) add1). This is used to calculate the initial
+ value of giv's. */
+
+static rtx
+fold_rtx_mult_add (mult1, mult2, add1, mode)
+ rtx mult1, mult2, add1;
+ enum machine_mode mode;
+{
+ rtx temp, mult_res;
+ rtx result;
+
+ /* The modes must all be the same. This should always be true. For now,
+ check to make sure. */
+ if ((GET_MODE (mult1) != mode && GET_MODE (mult1) != VOIDmode)
+ || (GET_MODE (mult2) != mode && GET_MODE (mult2) != VOIDmode)
+ || (GET_MODE (add1) != mode && GET_MODE (add1) != VOIDmode))
+ abort ();
+
+ /* Ensure that if at least one of mult1/mult2 are constant, then mult2
+ will be a constant. */
+ if (GET_CODE (mult1) == CONST_INT)
+ {
+ temp = mult2;
+ mult2 = mult1;
+ mult1 = temp;
+ }
+
+ mult_res = simplify_binary_operation (MULT, mode, mult1, mult2);
+ if (! mult_res)
+ mult_res = gen_rtx (MULT, mode, mult1, mult2);
+
+ /* Again, put the constant second. */
+ if (GET_CODE (add1) == CONST_INT)
+ {
+ temp = add1;
+ add1 = mult_res;
+ mult_res = temp;
+ }
+
+ result = simplify_binary_operation (PLUS, mode, add1, mult_res);
+ if (! result)
+ result = gen_rtx (PLUS, mode, add1, mult_res);
+
+ return result;
+}
+
+/* Searches the list of induction struct's for the biv BL, to try to calculate
+ the total increment value for one iteration of the loop as a constant.
+
+ Returns the increment value as an rtx, simplified as much as possible,
+ if it can be calculated. Otherwise, returns 0. */
+
+rtx
+biv_total_increment (bl, loop_start, loop_end)
+ struct iv_class *bl;
+ rtx loop_start, loop_end;
+{
+ struct induction *v;
+ rtx result;
+
+ /* For increment, must check every instruction that sets it. Each
+ instruction must be executed only once each time through the loop.
+ To verify this, we check that the the insn is always executed, and that
+ there are no backward branches after the insn that branch to before it.
+ Also, the insn must have a mult_val of one (to make sure it really is
+ an increment). */
+
+ result = const0_rtx;
+ for (v = bl->biv; v; v = v->next_iv)
+ {
+ if (v->always_computable && v->mult_val == const1_rtx
+ && ! back_branch_in_range_p (v->insn, loop_start, loop_end))
+ result = fold_rtx_mult_add (result, const1_rtx, v->add_val, v->mode);
+ else
+ return 0;
+ }
+
+ return result;
+}
+
+/* Determine the initial value of the iteration variable, and the amount
+ that it is incremented each loop. Use the tables constructed by
+ the strength reduction pass to calculate these values.
+
+ Initial_value and/or increment are set to zero if their values could not
+ be calculated. */
+
+static void
+iteration_info (iteration_var, initial_value, increment, loop_start, loop_end)
+ rtx iteration_var, *initial_value, *increment;
+ rtx loop_start, loop_end;
+{
+ struct iv_class *bl;
+ struct induction *v, *b;
+
+ /* Clear the result values, in case no answer can be found. */
+ *initial_value = 0;
+ *increment = 0;
+
+ /* The iteration variable can be either a giv or a biv. Check to see
+ which it is, and compute the variable's initial value, and increment
+ value if possible. */
+
+ /* If this is a new register, can't handle it since we don't have any
+ reg_iv_type entry for it. */
+ if (REGNO (iteration_var) >= max_reg_before_loop)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Loop unrolling: No reg_iv_type entry for iteration var.\n");
+ return;
+ }
+ /* Reject iteration variables larger than the host long size, since they
+ could result in a number of iterations greater than the range of our
+ `unsigned long' variable loop_n_iterations. */
+ else if (GET_MODE_BITSIZE (GET_MODE (iteration_var)) > HOST_BITS_PER_LONG)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Loop unrolling: Iteration var rejected because mode larger than host long.\n");
+ return;
+ }
+ else if (GET_MODE_CLASS (GET_MODE (iteration_var)) != MODE_INT)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Loop unrolling: Iteration var not an integer.\n");
+ return;
+ }
+ else if (reg_iv_type[REGNO (iteration_var)] == BASIC_INDUCT)
+ {
+ /* Grab initial value, only useful if it is a constant. */
+ bl = reg_biv_class[REGNO (iteration_var)];
+ *initial_value = bl->initial_value;
+
+ *increment = biv_total_increment (bl, loop_start, loop_end);
+ }
+ else if (reg_iv_type[REGNO (iteration_var)] == GENERAL_INDUCT)
+ {
+#if 1
+ /* ??? The code below does not work because the incorrect number of
+ iterations is calculated when the biv is incremented after the giv
+ is set (which is the usual case). This can probably be accounted
+ for by biasing the initial_value by subtracting the amount of the
+ increment that occurs between the giv set and the giv test. However,
+ a giv as an iterator is very rare, so it does not seem worthwhile
+ to handle this. */
+ /* ??? An example failure is: i = 6; do {;} while (i++ < 9). */
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Loop unrolling: Giv iterators are not handled.\n");
+ return;
+#else
+ /* Initial value is mult_val times the biv's initial value plus
+ add_val. Only useful if it is a constant. */
+ v = reg_iv_info[REGNO (iteration_var)];
+ bl = reg_biv_class[REGNO (v->src_reg)];
+ *initial_value = fold_rtx_mult_add (v->mult_val, bl->initial_value,
+ v->add_val, v->mode);
+
+ /* Increment value is mult_val times the increment value of the biv. */
+
+ *increment = biv_total_increment (bl, loop_start, loop_end);
+ if (*increment)
+ *increment = fold_rtx_mult_add (v->mult_val, *increment, const0_rtx,
+ v->mode);
+#endif
+ }
+ else
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Loop unrolling: Not basic or general induction var.\n");
+ return;
+ }
+}
+
+/* Calculate the approximate final value of the iteration variable
+ which has an loop exit test with code COMPARISON_CODE and comparison value
+ of COMPARISON_VALUE. Also returns an indication of whether the comparison
+ was signed or unsigned, and the direction of the comparison. This info is
+ needed to calculate the number of loop iterations. */
+
+static rtx
+approx_final_value (comparison_code, comparison_value, unsigned_p, compare_dir)
+ enum rtx_code comparison_code;
+ rtx comparison_value;
+ int *unsigned_p;
+ int *compare_dir;
+{
+ /* Calculate the final value of the induction variable.
+ The exact final value depends on the branch operator, and increment sign.
+ This is only an approximate value. It will be wrong if the iteration
+ variable is not incremented by one each time through the loop, and
+ approx final value - start value % increment != 0. */
+
+ *unsigned_p = 0;
+ switch (comparison_code)
+ {
+ case LEU:
+ *unsigned_p = 1;
+ case LE:
+ *compare_dir = 1;
+ return plus_constant (comparison_value, 1);
+ case GEU:
+ *unsigned_p = 1;
+ case GE:
+ *compare_dir = -1;
+ return plus_constant (comparison_value, -1);
+ case EQ:
+ /* Can not calculate a final value for this case. */
+ *compare_dir = 0;
+ return 0;
+ case LTU:
+ *unsigned_p = 1;
+ case LT:
+ *compare_dir = 1;
+ return comparison_value;
+ break;
+ case GTU:
+ *unsigned_p = 1;
+ case GT:
+ *compare_dir = -1;
+ return comparison_value;
+ case NE:
+ *compare_dir = 0;
+ return comparison_value;
+ default:
+ abort ();
+ }
+}
+
+/* For each biv and giv, determine whether it can be safely split into
+ a different variable for each unrolled copy of the loop body. If it
+ is safe to split, then indicate that by saving some useful info
+ in the splittable_regs array.
+
+ If the loop is being completely unrolled, then splittable_regs will hold
+ the current value of the induction variable while the loop is unrolled.
+ It must be set to the initial value of the induction variable here.
+ Otherwise, splittable_regs will hold the difference between the current
+ value of the induction variable and the value the induction variable had
+ at the top of the loop. It must be set to the value 0 here.
+
+ Returns the total number of instructions that set registers that are
+ splittable. */
+
+/* ?? If the loop is only unrolled twice, then most of the restrictions to
+ constant values are unnecessary, since we can easily calculate increment
+ values in this case even if nothing is constant. The increment value
+ should not involve a multiply however. */
+
+/* ?? Even if the biv/giv increment values aren't constant, it may still
+ be beneficial to split the variable if the loop is only unrolled a few
+ times, since multiplies by small integers (1,2,3,4) are very cheap. */
+
+static int
+find_splittable_regs (unroll_type, loop_start, loop_end, end_insert_before,
+ unroll_number)
+ enum unroll_types unroll_type;
+ rtx loop_start, loop_end;
+ rtx end_insert_before;
+ int unroll_number;
+{
+ struct iv_class *bl;
+ struct induction *v;
+ rtx increment, tem;
+ rtx biv_final_value;
+ int biv_splittable;
+ int result = 0;
+
+ for (bl = loop_iv_list; bl; bl = bl->next)
+ {
+ /* Biv_total_increment must return a constant value,
+ otherwise we can not calculate the split values. */
+
+ increment = biv_total_increment (bl, loop_start, loop_end);
+ if (! increment || GET_CODE (increment) != CONST_INT)
+ continue;
+
+ /* The loop must be unrolled completely, or else have a known number
+ of iterations and only one exit, or else the biv must be dead
+ outside the loop, or else the final value must be known. Otherwise,
+ it is unsafe to split the biv since it may not have the proper
+ value on loop exit. */
+
+ /* loop_number_exit_labels is non-zero if the loop has an exit other than
+ a fall through at the end. */
+
+ biv_splittable = 1;
+ biv_final_value = 0;
+ if (unroll_type != UNROLL_COMPLETELY
+ && (loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]]
+ || unroll_type == UNROLL_NAIVE)
+ && (uid_luid[regno_last_uid[bl->regno]] >= INSN_LUID (loop_end)
+ || ! bl->init_insn
+ || INSN_UID (bl->init_insn) >= max_uid_for_loop
+ || (uid_luid[regno_first_uid[bl->regno]]
+ < INSN_LUID (bl->init_insn))
+ || reg_mentioned_p (bl->biv->dest_reg, SET_SRC (bl->init_set)))
+ && ! (biv_final_value = final_biv_value (bl, loop_start, loop_end)))
+ biv_splittable = 0;
+
+ /* If any of the insns setting the BIV don't do so with a simple
+ PLUS, we don't know how to split it. */
+ for (v = bl->biv; biv_splittable && v; v = v->next_iv)
+ if ((tem = single_set (v->insn)) == 0
+ || GET_CODE (SET_DEST (tem)) != REG
+ || REGNO (SET_DEST (tem)) != bl->regno
+ || GET_CODE (SET_SRC (tem)) != PLUS)
+ biv_splittable = 0;
+
+ /* If final value is non-zero, then must emit an instruction which sets
+ the value of the biv to the proper value. This is done after
+ handling all of the givs, since some of them may need to use the
+ biv's value in their initialization code. */
+
+ /* This biv is splittable. If completely unrolling the loop, save
+ the biv's initial value. Otherwise, save the constant zero. */
+
+ if (biv_splittable == 1)
+ {
+ if (unroll_type == UNROLL_COMPLETELY)
+ {
+ /* If the initial value of the biv is itself (i.e. it is too
+ complicated for strength_reduce to compute), or is a hard
+ register, then we must create a new pseudo reg to hold the
+ initial value of the biv. */
+
+ if (GET_CODE (bl->initial_value) == REG
+ && (REGNO (bl->initial_value) == bl->regno
+ || REGNO (bl->initial_value) < FIRST_PSEUDO_REGISTER))
+ {
+ rtx tem = gen_reg_rtx (bl->biv->mode);
+
+ emit_insn_before (gen_move_insn (tem, bl->biv->src_reg),
+ loop_start);
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "Biv %d initial value remapped to %d.\n",
+ bl->regno, REGNO (tem));
+
+ splittable_regs[bl->regno] = tem;
+ }
+ else
+ splittable_regs[bl->regno] = bl->initial_value;
+ }
+ else
+ splittable_regs[bl->regno] = const0_rtx;
+
+ /* Save the number of instructions that modify the biv, so that
+ we can treat the last one specially. */
+
+ splittable_regs_updates[bl->regno] = bl->biv_count;
+ result += bl->biv_count;
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Biv %d safe to split.\n", bl->regno);
+ }
+
+ /* Check every giv that depends on this biv to see whether it is
+ splittable also. Even if the biv isn't splittable, givs which
+ depend on it may be splittable if the biv is live outside the
+ loop, and the givs aren't. */
+
+ result += find_splittable_givs (bl, unroll_type, loop_start, loop_end,
+ increment, unroll_number);
+
+ /* If final value is non-zero, then must emit an instruction which sets
+ the value of the biv to the proper value. This is done after
+ handling all of the givs, since some of them may need to use the
+ biv's value in their initialization code. */
+ if (biv_final_value)
+ {
+ /* If the loop has multiple exits, emit the insns before the
+ loop to ensure that it will always be executed no matter
+ how the loop exits. Otherwise emit the insn after the loop,
+ since this is slightly more efficient. */
+ if (! loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]])
+ emit_insn_before (gen_move_insn (bl->biv->src_reg,
+ biv_final_value),
+ end_insert_before);
+ else
+ {
+ /* Create a new register to hold the value of the biv, and then
+ set the biv to its final value before the loop start. The biv
+ is set to its final value before loop start to ensure that
+ this insn will always be executed, no matter how the loop
+ exits. */
+ rtx tem = gen_reg_rtx (bl->biv->mode);
+ emit_insn_before (gen_move_insn (tem, bl->biv->src_reg),
+ loop_start);
+ emit_insn_before (gen_move_insn (bl->biv->src_reg,
+ biv_final_value),
+ loop_start);
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "Biv %d mapped to %d for split.\n",
+ REGNO (bl->biv->src_reg), REGNO (tem));
+
+ /* Set up the mapping from the original biv register to the new
+ register. */
+ bl->biv->src_reg = tem;
+ }
+ }
+ }
+ return result;
+}
+
+/* For every giv based on the biv BL, check to determine whether it is
+ splittable. This is a subroutine to find_splittable_regs ().
+
+ Return the number of instructions that set splittable registers. */
+
+static int
+find_splittable_givs (bl, unroll_type, loop_start, loop_end, increment,
+ unroll_number)
+ struct iv_class *bl;
+ enum unroll_types unroll_type;
+ rtx loop_start, loop_end;
+ rtx increment;
+ int unroll_number;
+{
+ struct induction *v;
+ rtx final_value;
+ rtx tem;
+ int result = 0;
+
+ for (v = bl->giv; v; v = v->next_iv)
+ {
+ rtx giv_inc, value;
+
+ /* Only split the giv if it has already been reduced, or if the loop is
+ being completely unrolled. */
+ if (unroll_type != UNROLL_COMPLETELY && v->ignore)
+ continue;
+
+ /* The giv can be split if the insn that sets the giv is executed once
+ and only once on every iteration of the loop. */
+ /* An address giv can always be split. v->insn is just a use not a set,
+ and hence it does not matter whether it is always executed. All that
+ matters is that all the biv increments are always executed, and we
+ won't reach here if they aren't. */
+ if (v->giv_type != DEST_ADDR
+ && (! v->always_computable
+ || back_branch_in_range_p (v->insn, loop_start, loop_end)))
+ continue;
+
+ /* The giv increment value must be a constant. */
+ giv_inc = fold_rtx_mult_add (v->mult_val, increment, const0_rtx,
+ v->mode);
+ if (! giv_inc || GET_CODE (giv_inc) != CONST_INT)
+ continue;
+
+ /* The loop must be unrolled completely, or else have a known number of
+ iterations and only one exit, or else the giv must be dead outside
+ the loop, or else the final value of the giv must be known.
+ Otherwise, it is not safe to split the giv since it may not have the
+ proper value on loop exit. */
+
+ /* The used outside loop test will fail for DEST_ADDR givs. They are
+ never used outside the loop anyways, so it is always safe to split a
+ DEST_ADDR giv. */
+
+ final_value = 0;
+ if (unroll_type != UNROLL_COMPLETELY
+ && (loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]]
+ || unroll_type == UNROLL_NAIVE)
+ && v->giv_type != DEST_ADDR
+ && ((regno_first_uid[REGNO (v->dest_reg)] != INSN_UID (v->insn)
+ /* Check for the case where the pseudo is set by a shift/add
+ sequence, in which case the first insn setting the pseudo
+ is the first insn of the shift/add sequence. */
+ && (! (tem = find_reg_note (v->insn, REG_RETVAL, NULL_RTX))
+ || (regno_first_uid[REGNO (v->dest_reg)]
+ != INSN_UID (XEXP (tem, 0)))))
+ /* Line above always fails if INSN was moved by loop opt. */
+ || (uid_luid[regno_last_uid[REGNO (v->dest_reg)]]
+ >= INSN_LUID (loop_end)))
+ && ! (final_value = v->final_value))
+ continue;
+
+#if 0
+ /* Currently, non-reduced/final-value givs are never split. */
+ /* Should emit insns after the loop if possible, as the biv final value
+ code below does. */
+
+ /* If the final value is non-zero, and the giv has not been reduced,
+ then must emit an instruction to set the final value. */
+ if (final_value && !v->new_reg)
+ {
+ /* Create a new register to hold the value of the giv, and then set
+ the giv to its final value before the loop start. The giv is set
+ to its final value before loop start to ensure that this insn
+ will always be executed, no matter how we exit. */
+ tem = gen_reg_rtx (v->mode);
+ emit_insn_before (gen_move_insn (tem, v->dest_reg), loop_start);
+ emit_insn_before (gen_move_insn (v->dest_reg, final_value),
+ loop_start);
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "Giv %d mapped to %d for split.\n",
+ REGNO (v->dest_reg), REGNO (tem));
+
+ v->src_reg = tem;
+ }
+#endif
+
+ /* This giv is splittable. If completely unrolling the loop, save the
+ giv's initial value. Otherwise, save the constant zero for it. */
+
+ if (unroll_type == UNROLL_COMPLETELY)
+ {
+ /* It is not safe to use bl->initial_value here, because it may not
+ be invariant. It is safe to use the initial value stored in
+ the splittable_regs array if it is set. In rare cases, it won't
+ be set, so then we do exactly the same thing as
+ find_splittable_regs does to get a safe value. */
+ rtx biv_initial_value;
+
+ if (splittable_regs[bl->regno])
+ biv_initial_value = splittable_regs[bl->regno];
+ else if (GET_CODE (bl->initial_value) != REG
+ || (REGNO (bl->initial_value) != bl->regno
+ && REGNO (bl->initial_value) >= FIRST_PSEUDO_REGISTER))
+ biv_initial_value = bl->initial_value;
+ else
+ {
+ rtx tem = gen_reg_rtx (bl->biv->mode);
+
+ emit_insn_before (gen_move_insn (tem, bl->biv->src_reg),
+ loop_start);
+ biv_initial_value = tem;
+ }
+ value = fold_rtx_mult_add (v->mult_val, biv_initial_value,
+ v->add_val, v->mode);
+ }
+ else
+ value = const0_rtx;
+
+ if (v->new_reg)
+ {
+ /* If a giv was combined with another giv, then we can only split
+ this giv if the giv it was combined with was reduced. This
+ is because the value of v->new_reg is meaningless in this
+ case. */
+ if (v->same && ! v->same->new_reg)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "giv combined with unreduced giv not split.\n");
+ continue;
+ }
+ /* If the giv is an address destination, it could be something other
+ than a simple register, these have to be treated differently. */
+ else if (v->giv_type == DEST_REG)
+ {
+ /* If value is not a constant, register, or register plus
+ constant, then compute its value into a register before
+ loop start. This prevents illegal rtx sharing, and should
+ generate better code. We can use bl->initial_value here
+ instead of splittable_regs[bl->regno] because this code
+ is going before the loop start. */
+ if (unroll_type == UNROLL_COMPLETELY
+ && GET_CODE (value) != CONST_INT
+ && GET_CODE (value) != REG
+ && (GET_CODE (value) != PLUS
+ || GET_CODE (XEXP (value, 0)) != REG
+ || GET_CODE (XEXP (value, 1)) != CONST_INT))
+ {
+ rtx tem = gen_reg_rtx (v->mode);
+ emit_iv_add_mult (bl->initial_value, v->mult_val,
+ v->add_val, tem, loop_start);
+ value = tem;
+ }
+
+ splittable_regs[REGNO (v->new_reg)] = value;
+ }
+ else
+ {
+ /* Splitting address givs is useful since it will often allow us
+ to eliminate some increment insns for the base giv as
+ unnecessary. */
+
+ /* If the addr giv is combined with a dest_reg giv, then all
+ references to that dest reg will be remapped, which is NOT
+ what we want for split addr regs. We always create a new
+ register for the split addr giv, just to be safe. */
+
+ /* ??? If there are multiple address givs which have been
+ combined with the same dest_reg giv, then we may only need
+ one new register for them. Pulling out constants below will
+ catch some of the common cases of this. Currently, I leave
+ the work of simplifying multiple address givs to the
+ following cse pass. */
+
+ /* As a special case, if we have multiple identical address givs
+ within a single instruction, then we do use a single psuedo
+ reg for both. This is necessary in case one is a match_dup
+ of the other. */
+
+ v->const_adjust = 0;
+
+ if (v->same && v->same->insn == v->insn
+ && v->new_reg == v->same->new_reg)
+ {
+ v->dest_reg = v->same->dest_reg;
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Sharing address givs with reg %d\n",
+ REGNO (v->dest_reg));
+ }
+ else if (unroll_type != UNROLL_COMPLETELY)
+ {
+ /* If not completely unrolling the loop, then create a new
+ register to hold the split value of the DEST_ADDR giv.
+ Emit insn to initialize its value before loop start. */
+ tem = gen_reg_rtx (v->mode);
+
+ /* If the address giv has a constant in its new_reg value,
+ then this constant can be pulled out and put in value,
+ instead of being part of the initialization code. */
+
+ if (GET_CODE (v->new_reg) == PLUS
+ && GET_CODE (XEXP (v->new_reg, 1)) == CONST_INT)
+ {
+ v->dest_reg
+ = plus_constant (tem, INTVAL (XEXP (v->new_reg,1)));
+
+ /* Only succeed if this will give valid addresses.
+ Try to validate both the first and the last
+ address resulting from loop unrolling, if
+ one fails, then can't do const elim here. */
+ if (memory_address_p (v->mem_mode, v->dest_reg)
+ && memory_address_p (v->mem_mode,
+ plus_constant (v->dest_reg,
+ INTVAL (giv_inc)
+ * (unroll_number - 1))))
+ {
+ /* Save the negative of the eliminated const, so
+ that we can calculate the dest_reg's increment
+ value later. */
+ v->const_adjust = - INTVAL (XEXP (v->new_reg, 1));
+
+ v->new_reg = XEXP (v->new_reg, 0);
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Eliminating constant from giv %d\n",
+ REGNO (tem));
+ }
+ else
+ v->dest_reg = tem;
+ }
+ else
+ v->dest_reg = tem;
+
+ /* If the address hasn't been checked for validity yet, do so
+ now, and fail completely if either the first or the last
+ unrolled copy of the address is not a valid address. */
+ if (v->dest_reg == tem
+ && (! memory_address_p (v->mem_mode, v->dest_reg)
+ || ! memory_address_p (v->mem_mode,
+ plus_constant (v->dest_reg,
+ INTVAL (giv_inc)
+ * (unroll_number -1)))))
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Illegal address for giv at insn %d\n",
+ INSN_UID (v->insn));
+ continue;
+ }
+
+ /* To initialize the new register, just move the value of
+ new_reg into it. This is not guaranteed to give a valid
+ instruction on machines with complex addressing modes.
+ If we can't recognize it, then delete it and emit insns
+ to calculate the value from scratch. */
+ emit_insn_before (gen_rtx (SET, VOIDmode, tem,
+ copy_rtx (v->new_reg)),
+ loop_start);
+ if (recog_memoized (PREV_INSN (loop_start)) < 0)
+ {
+ rtx sequence, ret;
+
+ /* We can't use bl->initial_value to compute the initial
+ value, because the loop may have been preconditioned.
+ We must calculate it from NEW_REG. Try using
+ force_operand instead of emit_iv_add_mult. */
+ delete_insn (PREV_INSN (loop_start));
+
+ start_sequence ();
+ ret = force_operand (v->new_reg, tem);
+ if (ret != tem)
+ emit_move_insn (tem, ret);
+ sequence = gen_sequence ();
+ end_sequence ();
+ emit_insn_before (sequence, loop_start);
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Illegal init insn, rewritten.\n");
+ }
+ }
+ else
+ {
+ v->dest_reg = value;
+
+ /* Check the resulting address for validity, and fail
+ if the resulting address would be illegal. */
+ if (! memory_address_p (v->mem_mode, v->dest_reg)
+ || ! memory_address_p (v->mem_mode,
+ plus_constant (v->dest_reg,
+ INTVAL (giv_inc) *
+ (unroll_number -1))))
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Illegal address for giv at insn %d\n",
+ INSN_UID (v->insn));
+ continue;
+ }
+ }
+
+ /* Store the value of dest_reg into the insn. This sharing
+ will not be a problem as this insn will always be copied
+ later. */
+
+ *v->location = v->dest_reg;
+
+ /* If this address giv is combined with a dest reg giv, then
+ save the base giv's induction pointer so that we will be
+ able to handle this address giv properly. The base giv
+ itself does not have to be splittable. */
+
+ if (v->same && v->same->giv_type == DEST_REG)
+ addr_combined_regs[REGNO (v->same->new_reg)] = v->same;
+
+ if (GET_CODE (v->new_reg) == REG)
+ {
+ /* This giv maybe hasn't been combined with any others.
+ Make sure that it's giv is marked as splittable here. */
+
+ splittable_regs[REGNO (v->new_reg)] = value;
+
+ /* Make it appear to depend upon itself, so that the
+ giv will be properly split in the main loop above. */
+ if (! v->same)
+ {
+ v->same = v;
+ addr_combined_regs[REGNO (v->new_reg)] = v;
+ }
+ }
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream, "DEST_ADDR giv being split.\n");
+ }
+ }
+ else
+ {
+#if 0
+ /* Currently, unreduced giv's can't be split. This is not too much
+ of a problem since unreduced giv's are not live across loop
+ iterations anyways. When unrolling a loop completely though,
+ it makes sense to reduce&split givs when possible, as this will
+ result in simpler instructions, and will not require that a reg
+ be live across loop iterations. */
+
+ splittable_regs[REGNO (v->dest_reg)] = value;
+ fprintf (stderr, "Giv %d at insn %d not reduced\n",
+ REGNO (v->dest_reg), INSN_UID (v->insn));
+#else
+ continue;
+#endif
+ }
+
+ /* Givs are only updated once by definition. Mark it so if this is
+ a splittable register. Don't need to do anything for address givs
+ where this may not be a register. */
+
+ if (GET_CODE (v->new_reg) == REG)
+ splittable_regs_updates[REGNO (v->new_reg)] = 1;
+
+ result++;
+
+ if (loop_dump_stream)
+ {
+ int regnum;
+
+ if (GET_CODE (v->dest_reg) == CONST_INT)
+ regnum = -1;
+ else if (GET_CODE (v->dest_reg) != REG)
+ regnum = REGNO (XEXP (v->dest_reg, 0));
+ else
+ regnum = REGNO (v->dest_reg);
+ fprintf (loop_dump_stream, "Giv %d at insn %d safe to split.\n",
+ regnum, INSN_UID (v->insn));
+ }
+ }
+
+ return result;
+}
+
+/* Try to prove that the register is dead after the loop exits. Trace every
+ loop exit looking for an insn that will always be executed, which sets
+ the register to some value, and appears before the first use of the register
+ is found. If successful, then return 1, otherwise return 0. */
+
+/* ?? Could be made more intelligent in the handling of jumps, so that
+ it can search past if statements and other similar structures. */
+
+static int
+reg_dead_after_loop (reg, loop_start, loop_end)
+ rtx reg, loop_start, loop_end;
+{
+ rtx insn, label;
+ enum rtx_code code;
+ int jump_count = 0;
+
+ /* HACK: Must also search the loop fall through exit, create a label_ref
+ here which points to the loop_end, and append the loop_number_exit_labels
+ list to it. */
+ label = gen_rtx (LABEL_REF, VOIDmode, loop_end);
+ LABEL_NEXTREF (label)
+ = loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]];
+
+ for ( ; label; label = LABEL_NEXTREF (label))
+ {
+ /* Succeed if find an insn which sets the biv or if reach end of
+ function. Fail if find an insn that uses the biv, or if come to
+ a conditional jump. */
+
+ insn = NEXT_INSN (XEXP (label, 0));
+ while (insn)
+ {
+ code = GET_CODE (insn);
+ if (GET_RTX_CLASS (code) == 'i')
+ {
+ rtx set;
+
+ if (reg_referenced_p (reg, PATTERN (insn)))
+ return 0;
+
+ set = single_set (insn);
+ if (set && rtx_equal_p (SET_DEST (set), reg))
+ break;
+ }
+
+ if (code == JUMP_INSN)
+ {
+ if (GET_CODE (PATTERN (insn)) == RETURN)
+ break;
+ else if (! simplejump_p (insn)
+ /* Prevent infinite loop following infinite loops. */
+ || jump_count++ > 20)
+ return 0;
+ else
+ insn = JUMP_LABEL (insn);
+ }
+
+ insn = NEXT_INSN (insn);
+ }
+ }
+
+ /* Success, the register is dead on all loop exits. */
+ return 1;
+}
+
+/* Try to calculate the final value of the biv, the value it will have at
+ the end of the loop. If we can do it, return that value. */
+
+rtx
+final_biv_value (bl, loop_start, loop_end)
+ struct iv_class *bl;
+ rtx loop_start, loop_end;
+{
+ rtx increment, tem;
+
+ /* ??? This only works for MODE_INT biv's. Reject all others for now. */
+
+ if (GET_MODE_CLASS (bl->biv->mode) != MODE_INT)
+ return 0;
+
+ /* The final value for reversed bivs must be calculated differently than
+ for ordinary bivs. In this case, there is already an insn after the
+ loop which sets this biv's final value (if necessary), and there are
+ no other loop exits, so we can return any value. */
+ if (bl->reversed)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Final biv value for %d, reversed biv.\n", bl->regno);
+
+ return const0_rtx;
+ }
+
+ /* Try to calculate the final value as initial value + (number of iterations
+ * increment). For this to work, increment must be invariant, the only
+ exit from the loop must be the fall through at the bottom (otherwise
+ it may not have its final value when the loop exits), and the initial
+ value of the biv must be invariant. */
+
+ if (loop_n_iterations != 0
+ && ! loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]]
+ && invariant_p (bl->initial_value))
+ {
+ increment = biv_total_increment (bl, loop_start, loop_end);
+
+ if (increment && invariant_p (increment))
+ {
+ /* Can calculate the loop exit value, emit insns after loop
+ end to calculate this value into a temporary register in
+ case it is needed later. */
+
+ tem = gen_reg_rtx (bl->biv->mode);
+ /* Make sure loop_end is not the last insn. */
+ if (NEXT_INSN (loop_end) == 0)
+ emit_note_after (NOTE_INSN_DELETED, loop_end);
+ emit_iv_add_mult (increment, GEN_INT (loop_n_iterations),
+ bl->initial_value, tem, NEXT_INSN (loop_end));
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Final biv value for %d, calculated.\n", bl->regno);
+
+ return tem;
+ }
+ }
+
+ /* Check to see if the biv is dead at all loop exits. */
+ if (reg_dead_after_loop (bl->biv->src_reg, loop_start, loop_end))
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Final biv value for %d, biv dead after loop exit.\n",
+ bl->regno);
+
+ return const0_rtx;
+ }
+
+ return 0;
+}
+
+/* Try to calculate the final value of the giv, the value it will have at
+ the end of the loop. If we can do it, return that value. */
+
+rtx
+final_giv_value (v, loop_start, loop_end)
+ struct induction *v;
+ rtx loop_start, loop_end;
+{
+ struct iv_class *bl;
+ rtx insn;
+ rtx increment, tem;
+ rtx insert_before, seq;
+
+ bl = reg_biv_class[REGNO (v->src_reg)];
+
+ /* The final value for givs which depend on reversed bivs must be calculated
+ differently than for ordinary givs. In this case, there is already an
+ insn after the loop which sets this giv's final value (if necessary),
+ and there are no other loop exits, so we can return any value. */
+ if (bl->reversed)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Final giv value for %d, depends on reversed biv\n",
+ REGNO (v->dest_reg));
+ return const0_rtx;
+ }
+
+ /* Try to calculate the final value as a function of the biv it depends
+ upon. The only exit from the loop must be the fall through at the bottom
+ (otherwise it may not have its final value when the loop exits). */
+
+ /* ??? Can calculate the final giv value by subtracting off the
+ extra biv increments times the giv's mult_val. The loop must have
+ only one exit for this to work, but the loop iterations does not need
+ to be known. */
+
+ if (loop_n_iterations != 0
+ && ! loop_number_exit_labels[uid_loop_num[INSN_UID (loop_start)]])
+ {
+ /* ?? It is tempting to use the biv's value here since these insns will
+ be put after the loop, and hence the biv will have its final value
+ then. However, this fails if the biv is subsequently eliminated.
+ Perhaps determine whether biv's are eliminable before trying to
+ determine whether giv's are replaceable so that we can use the
+ biv value here if it is not eliminable. */
+
+ increment = biv_total_increment (bl, loop_start, loop_end);
+
+ if (increment && invariant_p (increment))
+ {
+ /* Can calculate the loop exit value of its biv as
+ (loop_n_iterations * increment) + initial_value */
+
+ /* The loop exit value of the giv is then
+ (final_biv_value - extra increments) * mult_val + add_val.
+ The extra increments are any increments to the biv which
+ occur in the loop after the giv's value is calculated.
+ We must search from the insn that sets the giv to the end
+ of the loop to calculate this value. */
+
+ insert_before = NEXT_INSN (loop_end);
+
+ /* Put the final biv value in tem. */
+ tem = gen_reg_rtx (bl->biv->mode);
+ emit_iv_add_mult (increment, GEN_INT (loop_n_iterations),
+ bl->initial_value, tem, insert_before);
+
+ /* Subtract off extra increments as we find them. */
+ for (insn = NEXT_INSN (v->insn); insn != loop_end;
+ insn = NEXT_INSN (insn))
+ {
+ struct induction *biv;
+
+ for (biv = bl->biv; biv; biv = biv->next_iv)
+ if (biv->insn == insn)
+ {
+ start_sequence ();
+ tem = expand_binop (GET_MODE (tem), sub_optab, tem,
+ biv->add_val, NULL_RTX, 0,
+ OPTAB_LIB_WIDEN);
+ seq = gen_sequence ();
+ end_sequence ();
+ emit_insn_before (seq, insert_before);
+ }
+ }
+
+ /* Now calculate the giv's final value. */
+ emit_iv_add_mult (tem, v->mult_val, v->add_val, tem,
+ insert_before);
+
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Final giv value for %d, calc from biv's value.\n",
+ REGNO (v->dest_reg));
+
+ return tem;
+ }
+ }
+
+ /* Replaceable giv's should never reach here. */
+ if (v->replaceable)
+ abort ();
+
+ /* Check to see if the biv is dead at all loop exits. */
+ if (reg_dead_after_loop (v->dest_reg, loop_start, loop_end))
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Final giv value for %d, giv dead after loop exit.\n",
+ REGNO (v->dest_reg));
+
+ return const0_rtx;
+ }
+
+ return 0;
+}
+
+
+/* Calculate the number of loop iterations. Returns the exact number of loop
+ iterations if it can be calculated, otherwise returns zero. */
+
+unsigned HOST_WIDE_INT
+loop_iterations (loop_start, loop_end)
+ rtx loop_start, loop_end;
+{
+ rtx comparison, comparison_value;
+ rtx iteration_var, initial_value, increment, final_value;
+ enum rtx_code comparison_code;
+ HOST_WIDE_INT i;
+ int increment_dir;
+ int unsigned_compare, compare_dir, final_larger;
+ unsigned long tempu;
+ rtx last_loop_insn;
+
+ /* First find the iteration variable. If the last insn is a conditional
+ branch, and the insn before tests a register value, make that the
+ iteration variable. */
+
+ loop_initial_value = 0;
+ loop_increment = 0;
+ loop_final_value = 0;
+ loop_iteration_var = 0;
+
+ last_loop_insn = prev_nonnote_insn (loop_end);
+
+ comparison = get_condition_for_loop (last_loop_insn);
+ if (comparison == 0)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Loop unrolling: No final conditional branch found.\n");
+ return 0;
+ }
+
+ /* ??? Get_condition may switch position of induction variable and
+ invariant register when it canonicalizes the comparison. */
+
+ comparison_code = GET_CODE (comparison);
+ iteration_var = XEXP (comparison, 0);
+ comparison_value = XEXP (comparison, 1);
+
+ if (GET_CODE (iteration_var) != REG)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Loop unrolling: Comparison not against register.\n");
+ return 0;
+ }
+
+ /* Loop iterations is always called before any new registers are created
+ now, so this should never occur. */
+
+ if (REGNO (iteration_var) >= max_reg_before_loop)
+ abort ();
+
+ iteration_info (iteration_var, &initial_value, &increment,
+ loop_start, loop_end);
+ if (initial_value == 0)
+ /* iteration_info already printed a message. */
+ return 0;
+
+ if (increment == 0)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Loop unrolling: Increment value can't be calculated.\n");
+ return 0;
+ }
+ if (GET_CODE (increment) != CONST_INT)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Loop unrolling: Increment value not constant.\n");
+ return 0;
+ }
+ if (GET_CODE (initial_value) != CONST_INT)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Loop unrolling: Initial value not constant.\n");
+ return 0;
+ }
+
+ /* If the comparison value is an invariant register, then try to find
+ its value from the insns before the start of the loop. */
+
+ if (GET_CODE (comparison_value) == REG && invariant_p (comparison_value))
+ {
+ rtx insn, set;
+
+ for (insn = PREV_INSN (loop_start); insn ; insn = PREV_INSN (insn))
+ {
+ if (GET_CODE (insn) == CODE_LABEL)
+ break;
+
+ else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+ && reg_set_p (comparison_value, insn))
+ {
+ /* We found the last insn before the loop that sets the register.
+ If it sets the entire register, and has a REG_EQUAL note,
+ then use the value of the REG_EQUAL note. */
+ if ((set = single_set (insn))
+ && (SET_DEST (set) == comparison_value))
+ {
+ rtx note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
+
+ /* Only use the REG_EQUAL note if it is a constant.
+ Other things, divide in particular, will cause
+ problems later if we use them. */
+ if (note && GET_CODE (XEXP (note, 0)) != EXPR_LIST
+ && CONSTANT_P (XEXP (note, 0)))
+ comparison_value = XEXP (note, 0);
+ }
+ break;
+ }
+ }
+ }
+
+ final_value = approx_final_value (comparison_code, comparison_value,
+ &unsigned_compare, &compare_dir);
+
+ /* Save the calculated values describing this loop's bounds, in case
+ precondition_loop_p will need them later. These values can not be
+ recalculated inside precondition_loop_p because strength reduction
+ optimizations may obscure the loop's structure. */
+
+ loop_iteration_var = iteration_var;
+ loop_initial_value = initial_value;
+ loop_increment = increment;
+ loop_final_value = final_value;
+
+ if (final_value == 0)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Loop unrolling: EQ comparison loop.\n");
+ return 0;
+ }
+ else if (GET_CODE (final_value) != CONST_INT)
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Loop unrolling: Final value not constant.\n");
+ return 0;
+ }
+
+ /* ?? Final value and initial value do not have to be constants.
+ Only their difference has to be constant. When the iteration variable
+ is an array address, the final value and initial value might both
+ be addresses with the same base but different constant offsets.
+ Final value must be invariant for this to work.
+
+ To do this, need some way to find the values of registers which are
+ invariant. */
+
+ /* Final_larger is 1 if final larger, 0 if they are equal, otherwise -1. */
+ if (unsigned_compare)
+ final_larger
+ = ((unsigned HOST_WIDE_INT) INTVAL (final_value)
+ > (unsigned HOST_WIDE_INT) INTVAL (initial_value))
+ - ((unsigned HOST_WIDE_INT) INTVAL (final_value)
+ < (unsigned HOST_WIDE_INT) INTVAL (initial_value));
+ else
+ final_larger = (INTVAL (final_value) > INTVAL (initial_value))
+ - (INTVAL (final_value) < INTVAL (initial_value));
+
+ if (INTVAL (increment) > 0)
+ increment_dir = 1;
+ else if (INTVAL (increment) == 0)
+ increment_dir = 0;
+ else
+ increment_dir = -1;
+
+ /* There are 27 different cases: compare_dir = -1, 0, 1;
+ final_larger = -1, 0, 1; increment_dir = -1, 0, 1.
+ There are 4 normal cases, 4 reverse cases (where the iteration variable
+ will overflow before the loop exits), 4 infinite loop cases, and 15
+ immediate exit (0 or 1 iteration depending on loop type) cases.
+ Only try to optimize the normal cases. */
+
+ /* (compare_dir/final_larger/increment_dir)
+ Normal cases: (0/-1/-1), (0/1/1), (-1/-1/-1), (1/1/1)
+ Reverse cases: (0/-1/1), (0/1/-1), (-1/-1/1), (1/1/-1)
+ Infinite loops: (0/-1/0), (0/1/0), (-1/-1/0), (1/1/0)
+ Immediate exit: (0/0/X), (-1/0/X), (-1/1/X), (1/0/X), (1/-1/X) */
+
+ /* ?? If the meaning of reverse loops (where the iteration variable
+ will overflow before the loop exits) is undefined, then could
+ eliminate all of these special checks, and just always assume
+ the loops are normal/immediate/infinite. Note that this means
+ the sign of increment_dir does not have to be known. Also,
+ since it does not really hurt if immediate exit loops or infinite loops
+ are optimized, then that case could be ignored also, and hence all
+ loops can be optimized.
+
+ According to ANSI Spec, the reverse loop case result is undefined,
+ because the action on overflow is undefined.
+
+ See also the special test for NE loops below. */
+
+ if (final_larger == increment_dir && final_larger != 0
+ && (final_larger == compare_dir || compare_dir == 0))
+ /* Normal case. */
+ ;
+ else
+ {
+ if (loop_dump_stream)
+ fprintf (loop_dump_stream,
+ "Loop unrolling: Not normal loop.\n");
+ return 0;
+ }
+
+ /* Calculate the number of iterations, final_value is only an approximation,
+ so correct for that. Note that tempu and loop_n_iterations are
+ unsigned, because they can be as large as 2^n - 1. */
+
+ i = INTVAL (increment);
+ if (i > 0)
+ tempu = INTVAL (final_value) - INTVAL (initial_value);
+ else if (i < 0)
+ {
+ tempu = INTVAL (initial_value) - INTVAL (final_value);
+ i = -i;
+ }
+ else
+ abort ();
+
+ /* For NE tests, make sure that the iteration variable won't miss the
+ final value. If tempu mod i is not zero, then the iteration variable
+ will overflow before the loop exits, and we can not calculate the
+ number of iterations. */
+ if (compare_dir == 0 && (tempu % i) != 0)
+ return 0;
+
+ return tempu / i + ((tempu % i) != 0);
+}
+
+/* Replace uses of split bivs with their split psuedo register. This is
+ for original instructions which remain after loop unrolling without
+ copying. */
+
+static rtx
+remap_split_bivs (x)
+ rtx x;
+{
+ register enum rtx_code code;
+ register int i;
+ register char *fmt;
+
+ if (x == 0)
+ return x;
+
+ code = GET_CODE (x);
+ switch (code)
+ {
+ case SCRATCH:
+ case PC:
+ case CC0:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return x;
+
+ case REG:
+#if 0
+ /* If non-reduced/final-value givs were split, then this would also
+ have to remap those givs also. */
+#endif
+ if (REGNO (x) < max_reg_before_loop
+ && reg_iv_type[REGNO (x)] == BASIC_INDUCT)
+ return reg_biv_class[REGNO (x)]->biv->src_reg;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ XEXP (x, i) = remap_split_bivs (XEXP (x, i));
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = 0; j < XVECLEN (x, i); j++)
+ XVECEXP (x, i, j) = remap_split_bivs (XVECEXP (x, i, j));
+ }
+ }
+ return x;
+}
diff --git a/gnu/usr.bin/cc/cc_int/varasm.c b/gnu/usr.bin/cc/cc_int/varasm.c
new file mode 100644
index 0000000..cd49b0c
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/varasm.c
@@ -0,0 +1,3883 @@
+/* Output variables, constants and external declarations, for GNU compiler.
+ Copyright (C) 1987, 88, 89, 92, 93, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file handles generation of all the assembler code
+ *except* the instructions of a function.
+ This includes declarations of variables and their initial values.
+
+ We also output the assembler code for constants stored in memory
+ and are responsible for combining constants with the same value. */
+
+#include <stdio.h>
+#include <setjmp.h>
+/* #include <stab.h> */
+#include "config.h"
+#include "rtl.h"
+#include "tree.h"
+#include "flags.h"
+#include "function.h"
+#include "expr.h"
+#include "hard-reg-set.h"
+#include "regs.h"
+#include "defaults.h"
+#include "real.h"
+#include "bytecode.h"
+
+#include "obstack.h"
+
+#ifdef XCOFF_DEBUGGING_INFO
+#include "xcoffout.h"
+#endif
+
+#include <ctype.h>
+
+#ifndef ASM_STABS_OP
+#define ASM_STABS_OP ".stabs"
+#endif
+
+/* This macro gets just the user-specified name
+ out of the string in a SYMBOL_REF. On most machines,
+ we discard the * if any and that's all. */
+#ifndef STRIP_NAME_ENCODING
+#define STRIP_NAME_ENCODING(VAR,SYMBOL_NAME) \
+ (VAR) = ((SYMBOL_NAME) + ((SYMBOL_NAME)[0] == '*'))
+#endif
+
+/* File in which assembler code is being written. */
+
+extern FILE *asm_out_file;
+
+/* The (assembler) name of the first globally-visible object output. */
+char *first_global_object_name;
+
+extern struct obstack *current_obstack;
+extern struct obstack *saveable_obstack;
+extern struct obstack *rtl_obstack;
+extern struct obstack permanent_obstack;
+#define obstack_chunk_alloc xmalloc
+
+/* Number for making the label on the next
+ constant that is stored in memory. */
+
+int const_labelno;
+
+/* Number for making the label on the next
+ static variable internal to a function. */
+
+int var_labelno;
+
+/* Carry information from ASM_DECLARE_OBJECT_NAME
+ to ASM_FINISH_DECLARE_OBJECT. */
+
+int size_directive_output;
+
+/* The last decl for which assemble_variable was called,
+ if it did ASM_DECLARE_OBJECT_NAME.
+ If the last call to assemble_variable didn't do that,
+ this holds 0. */
+
+tree last_assemble_variable_decl;
+
+/* Nonzero if at least one function definition has been seen. */
+static int function_defined;
+
+extern FILE *asm_out_file;
+
+static char *compare_constant_1 ();
+static void record_constant_1 ();
+static void output_constant_def_contents ();
+static int contains_pointers_p ();
+static void bc_output_ascii ();
+
+void output_constant_pool ();
+void assemble_name ();
+int output_addressed_constants ();
+void output_constant ();
+void output_constructor ();
+void output_byte_asm ();
+void text_section ();
+void readonly_data_section ();
+void data_section ();
+void named_section ();
+static void bc_assemble_integer ();
+
+#ifdef EXTRA_SECTIONS
+static enum in_section {no_section, in_text, in_data, in_named, EXTRA_SECTIONS} in_section
+ = no_section;
+#else
+static enum in_section {no_section, in_text, in_data, in_named} in_section
+ = no_section;
+#endif
+
+/* Return a non-zero value if DECL has a section attribute. */
+#define IN_NAMED_SECTION(DECL) \
+ ((TREE_CODE (DECL) == FUNCTION_DECL || TREE_CODE (DECL) == VAR_DECL) \
+ && DECL_SECTION_NAME (DECL) != NULL_TREE)
+
+/* Text of section name when in_section == in_named. */
+static char *in_named_name;
+
+/* Define functions like text_section for any extra sections. */
+#ifdef EXTRA_SECTION_FUNCTIONS
+EXTRA_SECTION_FUNCTIONS
+#endif
+
+/* Tell assembler to switch to text section. */
+
+void
+text_section ()
+{
+ if (in_section != in_text)
+ {
+ if (output_bytecode)
+ bc_text ();
+ else
+ fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP);
+
+ in_section = in_text;
+ }
+}
+
+/* Tell assembler to switch to data section. */
+
+void
+data_section ()
+{
+ if (in_section != in_data)
+ {
+ if (output_bytecode)
+ bc_data ();
+ else
+ {
+ if (flag_shared_data)
+ {
+#ifdef SHARED_SECTION_ASM_OP
+ fprintf (asm_out_file, "%s\n", SHARED_SECTION_ASM_OP);
+#else
+ fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP);
+#endif
+ }
+ else
+ fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP);
+ }
+
+ in_section = in_data;
+ }
+}
+
+/* Tell assembler to switch to read-only data section. This is normally
+ the text section. */
+
+void
+readonly_data_section ()
+{
+#ifdef READONLY_DATA_SECTION
+ READONLY_DATA_SECTION (); /* Note this can call data_section. */
+#else
+ text_section ();
+#endif
+}
+
+/* Determine if we're in the text section. */
+
+int
+in_text_section ()
+{
+ return in_section == in_text;
+}
+
+/* Tell assembler to change to named section. */
+
+void
+named_section (name)
+ char *name;
+{
+ if (in_section != in_named || strcmp (name, in_named_name))
+ {
+ in_named_name = name;
+ in_section = in_named;
+
+#ifdef ASM_OUTPUT_SECTION_NAME
+ ASM_OUTPUT_SECTION_NAME (asm_out_file, name);
+#else
+ /* Section attributes are not supported if this macro isn't provided -
+ some host formats don't support them at all. The front-end should
+ already have flagged this as an error. */
+ abort ();
+#endif
+ }
+}
+
+/* Create the rtl to represent a function, for a function definition.
+ DECL is a FUNCTION_DECL node which describes which function.
+ The rtl is stored into DECL. */
+
+void
+make_function_rtl (decl)
+ tree decl;
+{
+ char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+
+ if (output_bytecode)
+ {
+ if (DECL_RTL (decl) == 0)
+ DECL_RTL (decl) = bc_gen_rtx (name, 0, (struct bc_label *) 0);
+
+ /* Record that at least one function has been defined. */
+ function_defined = 1;
+ return;
+ }
+
+ /* Rename a nested function to avoid conflicts. */
+ if (decl_function_context (decl) != 0
+ && DECL_INITIAL (decl) != 0
+ && DECL_RTL (decl) == 0)
+ {
+ char *label;
+
+ name = IDENTIFIER_POINTER (DECL_NAME (decl));
+ ASM_FORMAT_PRIVATE_NAME (label, name, var_labelno);
+ name = obstack_copy0 (saveable_obstack, label, strlen (label));
+ var_labelno++;
+ }
+
+ if (DECL_RTL (decl) == 0)
+ {
+ DECL_RTL (decl)
+ = gen_rtx (MEM, DECL_MODE (decl),
+ gen_rtx (SYMBOL_REF, Pmode, name));
+
+ /* Optionally set flags or add text to the name to record information
+ such as that it is a function name. If the name is changed, the macro
+ ASM_OUTPUT_LABELREF will have to know how to strip this information. */
+#ifdef ENCODE_SECTION_INFO
+ ENCODE_SECTION_INFO (decl);
+#endif
+ }
+
+ /* Record at least one function has been defined. */
+ function_defined = 1;
+}
+
+/* Create the DECL_RTL for a declaration for a static or external
+ variable or static or external function.
+ ASMSPEC, if not 0, is the string which the user specified
+ as the assembler symbol name.
+ TOP_LEVEL is nonzero if this is a file-scope variable.
+ This is never called for PARM_DECLs. */
+void
+bc_make_decl_rtl (decl, asmspec, top_level)
+ tree decl;
+ char *asmspec;
+ int top_level;
+{
+ register char *name = TREE_STRING_POINTER (DECL_ASSEMBLER_NAME (decl));
+
+ if (DECL_RTL (decl) == 0)
+ {
+ /* Print an error message for register variables. */
+ if (DECL_REGISTER (decl) && TREE_CODE (decl) == FUNCTION_DECL)
+ error ("function declared `register'");
+ else if (DECL_REGISTER (decl))
+ error ("global register variables not supported in the interpreter");
+
+ /* Handle ordinary static variables and functions. */
+ if (DECL_RTL (decl) == 0)
+ {
+ /* Can't use just the variable's own name for a variable
+ whose scope is less than the whole file.
+ Concatenate a distinguishing number. */
+ if (!top_level && !DECL_EXTERNAL (decl) && asmspec == 0)
+ {
+ char *label;
+
+ ASM_FORMAT_PRIVATE_NAME (label, name, var_labelno);
+ name = obstack_copy0 (saveable_obstack, label, strlen (label));
+ var_labelno++;
+ }
+
+ DECL_RTL (decl) = bc_gen_rtx (name, 0, (struct bc_label *) 0);
+ }
+ }
+}
+
+/* Given NAME, a putative register name, discard any customary prefixes. */
+
+static char *
+strip_reg_name (name)
+ char *name;
+{
+#ifdef REGISTER_PREFIX
+ if (!strncmp (name, REGISTER_PREFIX, strlen (REGISTER_PREFIX)))
+ name += strlen (REGISTER_PREFIX);
+#endif
+ if (name[0] == '%' || name[0] == '#')
+ name++;
+ return name;
+}
+
+/* Decode an `asm' spec for a declaration as a register name.
+ Return the register number, or -1 if nothing specified,
+ or -2 if the ASMSPEC is not `cc' or `memory' and is not recognized,
+ or -3 if ASMSPEC is `cc' and is not recognized,
+ or -4 if ASMSPEC is `memory' and is not recognized.
+ Accept an exact spelling or a decimal number.
+ Prefixes such as % are optional. */
+
+int
+decode_reg_name (asmspec)
+ char *asmspec;
+{
+ if (asmspec != 0)
+ {
+ int i;
+
+ /* Get rid of confusing prefixes. */
+ asmspec = strip_reg_name (asmspec);
+
+ /* Allow a decimal number as a "register name". */
+ for (i = strlen (asmspec) - 1; i >= 0; i--)
+ if (! (asmspec[i] >= '0' && asmspec[i] <= '9'))
+ break;
+ if (asmspec[0] != 0 && i < 0)
+ {
+ i = atoi (asmspec);
+ if (i < FIRST_PSEUDO_REGISTER && i >= 0)
+ return i;
+ else
+ return -2;
+ }
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (reg_names[i][0]
+ && ! strcmp (asmspec, strip_reg_name (reg_names[i])))
+ return i;
+
+#ifdef ADDITIONAL_REGISTER_NAMES
+ {
+ static struct { char *name; int number; } table[]
+ = ADDITIONAL_REGISTER_NAMES;
+
+ for (i = 0; i < sizeof (table) / sizeof (table[0]); i++)
+ if (! strcmp (asmspec, table[i].name))
+ return table[i].number;
+ }
+#endif /* ADDITIONAL_REGISTER_NAMES */
+
+ if (!strcmp (asmspec, "memory"))
+ return -4;
+
+ if (!strcmp (asmspec, "cc"))
+ return -3;
+
+ return -2;
+ }
+
+ return -1;
+}
+
+/* Create the DECL_RTL for a declaration for a static or external variable
+ or static or external function.
+ ASMSPEC, if not 0, is the string which the user specified
+ as the assembler symbol name.
+ TOP_LEVEL is nonzero if this is a file-scope variable.
+
+ This is never called for PARM_DECL nodes. */
+
+void
+make_decl_rtl (decl, asmspec, top_level)
+ tree decl;
+ char *asmspec;
+ int top_level;
+{
+ register char *name = 0;
+ int reg_number;
+
+ if (output_bytecode)
+ {
+ bc_make_decl_rtl (decl, asmspec, top_level);
+ return;
+ }
+
+ reg_number = decode_reg_name (asmspec);
+
+ if (DECL_ASSEMBLER_NAME (decl) != NULL_TREE)
+ name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+
+ if (reg_number == -2)
+ {
+ /* ASMSPEC is given, and not the name of a register. */
+ name = (char *) obstack_alloc (saveable_obstack,
+ strlen (asmspec) + 2);
+ name[0] = '*';
+ strcpy (&name[1], asmspec);
+ }
+
+ /* For a duplicate declaration, we can be called twice on the
+ same DECL node. Don't discard the RTL already made. */
+ if (DECL_RTL (decl) == 0)
+ {
+ DECL_RTL (decl) = 0;
+
+ /* First detect errors in declaring global registers. */
+ if (DECL_REGISTER (decl) && reg_number == -1)
+ error_with_decl (decl,
+ "register name not specified for `%s'");
+ else if (DECL_REGISTER (decl) && reg_number < 0)
+ error_with_decl (decl,
+ "invalid register name for `%s'");
+ else if ((reg_number >= 0 || reg_number == -3) && ! DECL_REGISTER (decl))
+ error_with_decl (decl,
+ "register name given for non-register variable `%s'");
+ else if (DECL_REGISTER (decl) && TREE_CODE (decl) == FUNCTION_DECL)
+ error ("function declared `register'");
+ else if (DECL_REGISTER (decl) && TYPE_MODE (TREE_TYPE (decl)) == BLKmode)
+ error_with_decl (decl, "data type of `%s' isn't suitable for a register");
+ else if (DECL_REGISTER (decl)
+ && ! HARD_REGNO_MODE_OK (reg_number, TYPE_MODE (TREE_TYPE (decl))))
+ error_with_decl (decl, "register number for `%s' isn't suitable for the data type");
+ /* Now handle properly declared static register variables. */
+ else if (DECL_REGISTER (decl))
+ {
+ int nregs;
+#if 0 /* yylex should print the warning for this */
+ if (pedantic)
+ pedwarn ("ANSI C forbids global register variables");
+#endif
+ if (DECL_INITIAL (decl) != 0 && top_level)
+ {
+ DECL_INITIAL (decl) = 0;
+ error ("global register variable has initial value");
+ }
+ if (fixed_regs[reg_number] == 0
+ && function_defined && top_level)
+ error ("global register variable follows a function definition");
+ if (TREE_THIS_VOLATILE (decl))
+ warning ("volatile register variables don't work as you might wish");
+
+ /* If the user specified one of the eliminables registers here,
+ e.g., FRAME_POINTER_REGNUM, we don't want to get this variable
+ confused with that register and be eliminated. Although this
+ usage is somewhat suspect, we nevertheless use the following
+ kludge to avoid setting DECL_RTL to frame_pointer_rtx. */
+
+ DECL_RTL (decl)
+ = gen_rtx (REG, DECL_MODE (decl), FIRST_PSEUDO_REGISTER);
+ REGNO (DECL_RTL (decl)) = reg_number;
+ REG_USERVAR_P (DECL_RTL (decl)) = 1;
+
+ if (top_level)
+ {
+ /* Make this register global, so not usable for anything
+ else. */
+ nregs = HARD_REGNO_NREGS (reg_number, DECL_MODE (decl));
+ while (nregs > 0)
+ globalize_reg (reg_number + --nregs);
+ }
+ }
+ /* Specifying a section attribute on an uninitialized variable does not
+ (and cannot) cause it to be put in the given section. The linker
+ can only put initialized objects in specific sections, everything
+ else goes in bss for the linker to sort out later (otherwise the
+ linker would give a duplicate definition error for each compilation
+ unit that behaved thusly). So warn the user. */
+ else if (TREE_CODE (decl) == VAR_DECL
+ && DECL_SECTION_NAME (decl) != NULL_TREE
+ && DECL_INITIAL (decl) == NULL_TREE)
+ {
+ warning_with_decl (decl,
+ "section attribute ignored for uninitialized variable `%s'");
+ /* Remove the section name so subsequent declarations won't see it.
+ We are ignoring it, remember. */
+ DECL_SECTION_NAME (decl) = NULL_TREE;
+ }
+
+ /* Now handle ordinary static variables and functions (in memory).
+ Also handle vars declared register invalidly. */
+ if (DECL_RTL (decl) == 0)
+ {
+ /* Can't use just the variable's own name for a variable
+ whose scope is less than the whole file.
+ Concatenate a distinguishing number. */
+ if (!top_level && !DECL_EXTERNAL (decl) && asmspec == 0)
+ {
+ char *label;
+
+ ASM_FORMAT_PRIVATE_NAME (label, name, var_labelno);
+ name = obstack_copy0 (saveable_obstack, label, strlen (label));
+ var_labelno++;
+ }
+
+ if (name == 0)
+ abort ();
+
+ DECL_RTL (decl) = gen_rtx (MEM, DECL_MODE (decl),
+ gen_rtx (SYMBOL_REF, Pmode, name));
+
+ /* If this variable is to be treated as volatile, show its
+ tree node has side effects. If it has side effects, either
+ because of this test or from TREE_THIS_VOLATILE also
+ being set, show the MEM is volatile. */
+ if (flag_volatile_global && TREE_CODE (decl) == VAR_DECL
+ && TREE_PUBLIC (decl))
+ TREE_SIDE_EFFECTS (decl) = 1;
+ if (TREE_SIDE_EFFECTS (decl))
+ MEM_VOLATILE_P (DECL_RTL (decl)) = 1;
+
+ if (TREE_READONLY (decl))
+ RTX_UNCHANGING_P (DECL_RTL (decl)) = 1;
+ MEM_IN_STRUCT_P (DECL_RTL (decl))
+ = AGGREGATE_TYPE_P (TREE_TYPE (decl));
+
+ /* Optionally set flags or add text to the name to record information
+ such as that it is a function name.
+ If the name is changed, the macro ASM_OUTPUT_LABELREF
+ will have to know how to strip this information. */
+#ifdef ENCODE_SECTION_INFO
+ ENCODE_SECTION_INFO (decl);
+#endif
+ }
+ }
+ /* If the old RTL had the wrong mode, fix the mode. */
+ else if (GET_MODE (DECL_RTL (decl)) != DECL_MODE (decl))
+ {
+ rtx rtl = DECL_RTL (decl);
+ PUT_MODE (rtl, DECL_MODE (decl));
+ }
+}
+
+/* Make the rtl for variable VAR be volatile.
+ Use this only for static variables. */
+
+void
+make_var_volatile (var)
+ tree var;
+{
+ if (GET_CODE (DECL_RTL (var)) != MEM)
+ abort ();
+
+ MEM_VOLATILE_P (DECL_RTL (var)) = 1;
+}
+
+/* Output alignment directive to align for constant expression EXP. */
+
+void
+assemble_constant_align (exp)
+ tree exp;
+{
+ int align;
+
+ /* Align the location counter as required by EXP's data type. */
+ align = TYPE_ALIGN (TREE_TYPE (exp));
+#ifdef CONSTANT_ALIGNMENT
+ align = CONSTANT_ALIGNMENT (exp, align);
+#endif
+
+ if (align > BITS_PER_UNIT)
+ ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT));
+}
+
+/* Output a string of literal assembler code
+ for an `asm' keyword used between functions. */
+
+void
+assemble_asm (string)
+ tree string;
+{
+ if (output_bytecode)
+ {
+ error ("asm statements not allowed in interpreter");
+ return;
+ }
+
+ app_enable ();
+
+ if (TREE_CODE (string) == ADDR_EXPR)
+ string = TREE_OPERAND (string, 0);
+
+ fprintf (asm_out_file, "\t%s\n", TREE_STRING_POINTER (string));
+}
+
+#if 0 /* This should no longer be needed, because
+ flag_gnu_linker should be 0 on these systems,
+ which should prevent any output
+ if ASM_OUTPUT_CONSTRUCTOR and ASM_OUTPUT_DESTRUCTOR are absent. */
+#if !(defined(DBX_DEBUGGING_INFO) && !defined(FASCIST_ASSEMBLER))
+#ifndef ASM_OUTPUT_CONSTRUCTOR
+#define ASM_OUTPUT_CONSTRUCTOR(file, name)
+#endif
+#ifndef ASM_OUTPUT_DESTRUCTOR
+#define ASM_OUTPUT_DESTRUCTOR(file, name)
+#endif
+#endif
+#endif /* 0 */
+
+/* Record an element in the table of global destructors.
+ How this is done depends on what sort of assembler and linker
+ are in use.
+
+ NAME should be the name of a global function to be called
+ at exit time. This name is output using assemble_name. */
+
+void
+assemble_destructor (name)
+ char *name;
+{
+#ifdef ASM_OUTPUT_DESTRUCTOR
+ ASM_OUTPUT_DESTRUCTOR (asm_out_file, name);
+#else
+ if (flag_gnu_linker)
+ {
+ /* Now tell GNU LD that this is part of the static destructor set. */
+ /* This code works for any machine provided you use GNU as/ld. */
+ fprintf (asm_out_file, "%s \"___DTOR_LIST__\",22,0,0,", ASM_STABS_OP);
+ assemble_name (asm_out_file, name);
+ fputc ('\n', asm_out_file);
+ }
+#endif
+}
+
+/* Likewise for global constructors. */
+
+void
+assemble_constructor (name)
+ char *name;
+{
+#ifdef ASM_OUTPUT_CONSTRUCTOR
+ ASM_OUTPUT_CONSTRUCTOR (asm_out_file, name);
+#else
+ if (flag_gnu_linker)
+ {
+ /* Now tell GNU LD that this is part of the static constructor set. */
+ /* This code works for any machine provided you use GNU as/ld. */
+ fprintf (asm_out_file, "%s \"___CTOR_LIST__\",22,0,0,", ASM_STABS_OP);
+ assemble_name (asm_out_file, name);
+ fputc ('\n', asm_out_file);
+ }
+#endif
+}
+
+/* Likewise for entries we want to record for garbage collection.
+ Garbage collection is still under development. */
+
+void
+assemble_gc_entry (name)
+ char *name;
+{
+#ifdef ASM_OUTPUT_GC_ENTRY
+ ASM_OUTPUT_GC_ENTRY (asm_out_file, name);
+#else
+ if (flag_gnu_linker)
+ {
+ /* Now tell GNU LD that this is part of the static constructor set. */
+ fprintf (asm_out_file, "%s \"___PTR_LIST__\",22,0,0,", ASM_STABS_OP);
+ assemble_name (asm_out_file, name);
+ fputc ('\n', asm_out_file);
+ }
+#endif
+}
+
+/* Output assembler code for the constant pool of a function and associated
+ with defining the name of the function. DECL describes the function.
+ NAME is the function's name. For the constant pool, we use the current
+ constant pool data. */
+
+void
+assemble_start_function (decl, fnname)
+ tree decl;
+ char *fnname;
+{
+ int align;
+
+ /* The following code does not need preprocessing in the assembler. */
+
+ app_disable ();
+
+ output_constant_pool (fnname, decl);
+
+ if (IN_NAMED_SECTION (decl))
+ named_section (TREE_STRING_POINTER (DECL_SECTION_NAME (decl)));
+ else
+ text_section ();
+
+ /* Tell assembler to move to target machine's alignment for functions. */
+ align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT);
+ if (align > 0)
+ {
+ if (output_bytecode)
+ BC_OUTPUT_ALIGN (asm_out_file, align);
+ else
+ ASM_OUTPUT_ALIGN (asm_out_file, align);
+ }
+
+#ifdef ASM_OUTPUT_FUNCTION_PREFIX
+ ASM_OUTPUT_FUNCTION_PREFIX (asm_out_file, fnname);
+#endif
+
+#ifdef SDB_DEBUGGING_INFO
+ /* Output SDB definition of the function. */
+ if (write_symbols == SDB_DEBUG)
+ sdbout_mark_begin_function ();
+#endif
+
+#ifdef DBX_DEBUGGING_INFO
+ /* Output DBX definition of the function. */
+ if (write_symbols == DBX_DEBUG)
+ dbxout_begin_function (decl);
+#endif
+
+ /* Make function name accessible from other files, if appropriate. */
+
+ if (TREE_PUBLIC (decl))
+ {
+ if (!first_global_object_name)
+ STRIP_NAME_ENCODING (first_global_object_name, fnname);
+ if (output_bytecode)
+ BC_GLOBALIZE_LABEL (asm_out_file, fnname);
+ else
+ ASM_GLOBALIZE_LABEL (asm_out_file, fnname);
+ }
+
+ /* Do any machine/system dependent processing of the function name */
+#ifdef ASM_DECLARE_FUNCTION_NAME
+ ASM_DECLARE_FUNCTION_NAME (asm_out_file, fnname, current_function_decl);
+#else
+ /* Standard thing is just output label for the function. */
+ if (output_bytecode)
+ BC_OUTPUT_LABEL (asm_out_file, fnname);
+ else
+ ASM_OUTPUT_LABEL (asm_out_file, fnname);
+#endif /* ASM_DECLARE_FUNCTION_NAME */
+}
+
+/* Output assembler code associated with defining the size of the
+ function. DECL describes the function. NAME is the function's name. */
+
+void
+assemble_end_function (decl, fnname)
+ tree decl;
+ char *fnname;
+{
+#ifdef ASM_DECLARE_FUNCTION_SIZE
+ ASM_DECLARE_FUNCTION_SIZE (asm_out_file, fnname, decl);
+#endif
+}
+
+/* Assemble code to leave SIZE bytes of zeros. */
+
+void
+assemble_zeros (size)
+ int size;
+{
+ if (output_bytecode)
+ {
+ bc_emit_const_skip (size);
+ return;
+ }
+
+#ifdef ASM_NO_SKIP_IN_TEXT
+ /* The `space' pseudo in the text section outputs nop insns rather than 0s,
+ so we must output 0s explicitly in the text section. */
+ if (ASM_NO_SKIP_IN_TEXT && in_text_section ())
+ {
+ int i;
+
+ for (i = 0; i < size - 20; i += 20)
+ {
+#ifdef ASM_BYTE_OP
+ fprintf (asm_out_file,
+ "%s 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n", ASM_BYTE_OP);
+#else
+ fprintf (asm_out_file,
+ "\tbyte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n");
+#endif
+ }
+ if (i < size)
+ {
+#ifdef ASM_BYTE_OP
+ fprintf (asm_out_file, "%s 0", ASM_BYTE_OP);
+#else
+ fprintf (asm_out_file, "\tbyte 0");
+#endif
+ i++;
+ for (; i < size; i++)
+ fprintf (asm_out_file, ",0");
+ fprintf (asm_out_file, "\n");
+ }
+ }
+ else
+#endif
+ if (size > 0)
+ {
+ if (output_bytecode)
+ BC_OUTPUT_SKIP (asm_out_file, size);
+ else
+ ASM_OUTPUT_SKIP (asm_out_file, size);
+ }
+}
+
+/* Assemble an alignment pseudo op for an ALIGN-bit boundary. */
+
+void
+assemble_align (align)
+ int align;
+{
+ if (align > BITS_PER_UNIT)
+ ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT));
+}
+
+/* Assemble a string constant with the specified C string as contents. */
+
+void
+assemble_string (p, size)
+ char *p;
+ int size;
+{
+ register int i;
+ int pos = 0;
+ int maximum = 2000;
+
+ if (output_bytecode)
+ {
+ bc_emit (p, size);
+ return;
+ }
+
+ /* If the string is very long, split it up. */
+
+ while (pos < size)
+ {
+ int thissize = size - pos;
+ if (thissize > maximum)
+ thissize = maximum;
+
+ if (output_bytecode)
+ bc_output_ascii (asm_out_file, p, thissize);
+ else
+ {
+ ASM_OUTPUT_ASCII (asm_out_file, p, thissize);
+ }
+
+ pos += thissize;
+ p += thissize;
+ }
+}
+
+static void
+bc_output_ascii (file, p, size)
+ FILE *file;
+ char *p;
+ int size;
+{
+ BC_OUTPUT_ASCII (file, p, size);
+}
+
+/* Assemble everything that is needed for a variable or function declaration.
+ Not used for automatic variables, and not used for function definitions.
+ Should not be called for variables of incomplete structure type.
+
+ TOP_LEVEL is nonzero if this variable has file scope.
+ AT_END is nonzero if this is the special handling, at end of compilation,
+ to define things that have had only tentative definitions.
+ DONT_OUTPUT_DATA if nonzero means don't actually output the
+ initial value (that will be done by the caller). */
+
+void
+assemble_variable (decl, top_level, at_end, dont_output_data)
+ tree decl;
+ int top_level;
+ int at_end;
+{
+ register char *name;
+ int align;
+ tree size_tree;
+ int reloc = 0;
+ enum in_section saved_in_section;
+
+ last_assemble_variable_decl = 0;
+
+ if (output_bytecode)
+ return;
+
+ if (GET_CODE (DECL_RTL (decl)) == REG)
+ {
+ /* Do output symbol info for global register variables, but do nothing
+ else for them. */
+
+ if (TREE_ASM_WRITTEN (decl))
+ return;
+ TREE_ASM_WRITTEN (decl) = 1;
+
+ if (!output_bytecode)
+ {
+#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO)
+ /* File-scope global variables are output here. */
+ if ((write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG)
+ && top_level)
+ dbxout_symbol (decl, 0);
+#endif
+#ifdef SDB_DEBUGGING_INFO
+ if (write_symbols == SDB_DEBUG && top_level
+ /* Leave initialized global vars for end of compilation;
+ see comment in compile_file. */
+ && (TREE_PUBLIC (decl) == 0 || DECL_INITIAL (decl) == 0))
+ sdbout_symbol (decl, 0);
+#endif
+ }
+
+ /* Don't output any DWARF debugging information for variables here.
+ In the case of local variables, the information for them is output
+ when we do our recursive traversal of the tree representation for
+ the entire containing function. In the case of file-scope variables,
+ we output information for all of them at the very end of compilation
+ while we are doing our final traversal of the chain of file-scope
+ declarations. */
+
+ return;
+ }
+
+ /* Normally no need to say anything here for external references,
+ since assemble_external is called by the langauge-specific code
+ when a declaration is first seen. */
+
+ if (DECL_EXTERNAL (decl))
+ return;
+
+ /* Output no assembler code for a function declaration.
+ Only definitions of functions output anything. */
+
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ return;
+
+ /* If type was incomplete when the variable was declared,
+ see if it is complete now. */
+
+ if (DECL_SIZE (decl) == 0)
+ layout_decl (decl, 0);
+
+ /* Still incomplete => don't allocate it; treat the tentative defn
+ (which is what it must have been) as an `extern' reference. */
+
+ if (!dont_output_data && DECL_SIZE (decl) == 0)
+ {
+ error_with_file_and_line (DECL_SOURCE_FILE (decl),
+ DECL_SOURCE_LINE (decl),
+ "storage size of `%s' isn't known",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ TREE_ASM_WRITTEN (decl) = 1;
+ return;
+ }
+
+ /* The first declaration of a variable that comes through this function
+ decides whether it is global (in C, has external linkage)
+ or local (in C, has internal linkage). So do nothing more
+ if this function has already run. */
+
+ if (TREE_ASM_WRITTEN (decl))
+ return;
+
+ TREE_ASM_WRITTEN (decl) = 1;
+
+ /* If storage size is erroneously variable, just continue.
+ Error message was already made. */
+
+ if (DECL_SIZE (decl))
+ {
+ if (TREE_CODE (DECL_SIZE (decl)) != INTEGER_CST)
+ goto finish;
+
+ app_disable ();
+
+ /* This is better than explicit arithmetic, since it avoids overflow. */
+ size_tree = size_binop (CEIL_DIV_EXPR,
+ DECL_SIZE (decl), size_int (BITS_PER_UNIT));
+
+ if (TREE_INT_CST_HIGH (size_tree) != 0)
+ {
+ error_with_decl (decl, "size of variable `%s' is too large");
+ goto finish;
+ }
+ }
+
+ name = XSTR (XEXP (DECL_RTL (decl), 0), 0);
+
+ /* Handle uninitialized definitions. */
+
+ /* ANSI specifies that a tentative definition which is not merged with
+ a non-tentative definition behaves exactly like a definition with an
+ initializer equal to zero. (Section 3.7.2)
+ -fno-common gives strict ANSI behavior. Usually you don't want it.
+ This matters only for variables with external linkage. */
+ if ((! flag_no_common || ! TREE_PUBLIC (decl))
+ && DECL_COMMON (decl)
+ && ! dont_output_data
+ && (DECL_INITIAL (decl) == 0 || DECL_INITIAL (decl) == error_mark_node))
+ {
+ int size = TREE_INT_CST_LOW (size_tree);
+ int rounded = size;
+
+ if (TREE_INT_CST_HIGH (size_tree) != 0)
+ error_with_decl (decl, "size of variable `%s' is too large");
+ /* Don't allocate zero bytes of common,
+ since that means "undefined external" in the linker. */
+ if (size == 0) rounded = 1;
+ /* Round size up to multiple of BIGGEST_ALIGNMENT bits
+ so that each uninitialized object starts on such a boundary. */
+ rounded += (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1;
+ rounded = (rounded / (BIGGEST_ALIGNMENT / BITS_PER_UNIT)
+ * (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
+
+#ifdef DBX_DEBUGGING_INFO
+ /* File-scope global variables are output here. */
+ if (write_symbols == DBX_DEBUG && top_level)
+ dbxout_symbol (decl, 0);
+#endif
+#ifdef SDB_DEBUGGING_INFO
+ if (write_symbols == SDB_DEBUG && top_level
+ /* Leave initialized global vars for end of compilation;
+ see comment in compile_file. */
+ && (TREE_PUBLIC (decl) == 0 || DECL_INITIAL (decl) == 0))
+ sdbout_symbol (decl, 0);
+#endif
+
+ /* Don't output any DWARF debugging information for variables here.
+ In the case of local variables, the information for them is output
+ when we do our recursive traversal of the tree representation for
+ the entire containing function. In the case of file-scope variables,
+ we output information for all of them at the very end of compilation
+ while we are doing our final traversal of the chain of file-scope
+ declarations. */
+
+#if 0
+ if (flag_shared_data)
+ data_section ();
+#endif
+ if (TREE_PUBLIC (decl))
+ {
+#ifdef ASM_OUTPUT_SHARED_COMMON
+ if (flag_shared_data)
+ ASM_OUTPUT_SHARED_COMMON (asm_out_file, name, size, rounded);
+ else
+#endif
+ if (output_bytecode)
+ {
+ BC_OUTPUT_COMMON (asm_out_file, name, size, rounded);
+ }
+ else
+ {
+#ifdef ASM_OUTPUT_ALIGNED_COMMON
+ ASM_OUTPUT_ALIGNED_COMMON (asm_out_file, name, size,
+ DECL_ALIGN (decl));
+#else
+ ASM_OUTPUT_COMMON (asm_out_file, name, size, rounded);
+#endif
+ }
+ }
+ else
+ {
+#ifdef ASM_OUTPUT_SHARED_LOCAL
+ if (flag_shared_data)
+ ASM_OUTPUT_SHARED_LOCAL (asm_out_file, name, size, rounded);
+ else
+#endif
+ if (output_bytecode)
+ {
+ BC_OUTPUT_LOCAL (asm_out_file, name, size, rounded);
+ }
+ else
+ {
+#ifdef ASM_OUTPUT_ALIGNED_LOCAL
+ ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size,
+ DECL_ALIGN (decl));
+#else
+ ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded);
+#endif
+ }
+ }
+ goto finish;
+ }
+
+ /* Handle initialized definitions. */
+
+ /* First make the assembler name(s) global if appropriate. */
+ if (TREE_PUBLIC (decl) && DECL_NAME (decl))
+ {
+ if (!first_global_object_name)
+ STRIP_NAME_ENCODING(first_global_object_name, name);
+ ASM_GLOBALIZE_LABEL (asm_out_file, name);
+ }
+#if 0
+ for (d = equivalents; d; d = TREE_CHAIN (d))
+ {
+ tree e = TREE_VALUE (d);
+ if (TREE_PUBLIC (e) && DECL_NAME (e))
+ ASM_GLOBALIZE_LABEL (asm_out_file,
+ XSTR (XEXP (DECL_RTL (e), 0), 0));
+ }
+#endif
+
+ /* Output any data that we will need to use the address of. */
+ if (DECL_INITIAL (decl) == error_mark_node)
+ reloc = contains_pointers_p (TREE_TYPE (decl));
+ else if (DECL_INITIAL (decl))
+ reloc = output_addressed_constants (DECL_INITIAL (decl));
+
+ /* Switch to the proper section for this data. */
+ if (IN_NAMED_SECTION (decl))
+ named_section (TREE_STRING_POINTER (DECL_SECTION_NAME (decl)));
+ else
+ {
+ /* C++ can have const variables that get initialized from constructors,
+ and thus can not be in a readonly section. We prevent this by
+ verifying that the initial value is constant for objects put in a
+ readonly section.
+
+ error_mark_node is used by the C front end to indicate that the
+ initializer has not been seen yet. In this case, we assume that
+ the initializer must be constant. */
+#ifdef SELECT_SECTION
+ SELECT_SECTION (decl, reloc);
+#else
+ if (TREE_READONLY (decl)
+ && ! TREE_THIS_VOLATILE (decl)
+ && DECL_INITIAL (decl)
+ && (DECL_INITIAL (decl) == error_mark_node
+ || TREE_CONSTANT (DECL_INITIAL (decl)))
+ && ! (flag_pic && reloc))
+ readonly_data_section ();
+ else
+ data_section ();
+#endif
+ }
+
+ /* dbxout.c needs to know this. */
+ if (in_text_section ())
+ DECL_IN_TEXT_SECTION (decl) = 1;
+
+ /* Record current section so we can restore it if dbxout.c clobbers it. */
+ saved_in_section = in_section;
+
+ /* Output the dbx info now that we have chosen the section. */
+
+#ifdef DBX_DEBUGGING_INFO
+ /* File-scope global variables are output here. */
+ if (write_symbols == DBX_DEBUG && top_level)
+ dbxout_symbol (decl, 0);
+#endif
+#ifdef SDB_DEBUGGING_INFO
+ if (write_symbols == SDB_DEBUG && top_level
+ /* Leave initialized global vars for end of compilation;
+ see comment in compile_file. */
+ && (TREE_PUBLIC (decl) == 0 || DECL_INITIAL (decl) == 0))
+ sdbout_symbol (decl, 0);
+#endif
+
+ /* Don't output any DWARF debugging information for variables here.
+ In the case of local variables, the information for them is output
+ when we do our recursive traversal of the tree representation for
+ the entire containing function. In the case of file-scope variables,
+ we output information for all of them at the very end of compilation
+ while we are doing our final traversal of the chain of file-scope
+ declarations. */
+
+ /* If the debugging output changed sections, reselect the section
+ that's supposed to be selected. */
+ if (in_section != saved_in_section)
+ {
+ /* Switch to the proper section for this data. */
+#ifdef SELECT_SECTION
+ SELECT_SECTION (decl, reloc);
+#else
+ if (TREE_READONLY (decl)
+ && ! TREE_THIS_VOLATILE (decl)
+ && DECL_INITIAL (decl)
+ && (DECL_INITIAL (decl) == error_mark_node
+ || TREE_CONSTANT (DECL_INITIAL (decl)))
+ && ! (flag_pic && reloc))
+ readonly_data_section ();
+ else
+ data_section ();
+#endif
+ }
+
+ /* Compute and output the alignment of this data. */
+
+ align = DECL_ALIGN (decl);
+ /* In the case for initialing an array whose length isn't specified,
+ where we have not yet been able to do the layout,
+ figure out the proper alignment now. */
+ if (dont_output_data && DECL_SIZE (decl) == 0
+ && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
+ align = MAX (align, TYPE_ALIGN (TREE_TYPE (TREE_TYPE (decl))));
+
+ /* Some object file formats have a maximum alignment which they support.
+ In particular, a.out format supports a maximum alignment of 4. */
+#ifndef MAX_OFILE_ALIGNMENT
+#define MAX_OFILE_ALIGNMENT BIGGEST_ALIGNMENT
+#endif
+ if (align > MAX_OFILE_ALIGNMENT)
+ {
+ warning_with_decl (decl,
+ "alignment of `%s' is greater than maximum object file alignment");
+ align = MAX_OFILE_ALIGNMENT;
+ }
+#ifdef DATA_ALIGNMENT
+ /* On some machines, it is good to increase alignment sometimes. */
+ align = DATA_ALIGNMENT (TREE_TYPE (decl), align);
+#endif
+#ifdef CONSTANT_ALIGNMENT
+ if (DECL_INITIAL (decl))
+ align = CONSTANT_ALIGNMENT (DECL_INITIAL (decl), align);
+#endif
+
+ /* Reset the alignment in case we have made it tighter, so we can benefit
+ from it in get_pointer_alignment. */
+ DECL_ALIGN (decl) = align;
+
+ if (align > BITS_PER_UNIT)
+ {
+ if (output_bytecode)
+ BC_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT));
+ else
+ ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT));
+ }
+
+ /* Do any machine/system dependent processing of the object. */
+#ifdef ASM_DECLARE_OBJECT_NAME
+ last_assemble_variable_decl = decl;
+ ASM_DECLARE_OBJECT_NAME (asm_out_file, name, decl);
+#else
+ /* Standard thing is just output label for the object. */
+ if (output_bytecode)
+ BC_OUTPUT_LABEL (asm_out_file, name);
+ else
+ ASM_OUTPUT_LABEL (asm_out_file, name);
+#endif /* ASM_DECLARE_OBJECT_NAME */
+
+ if (!dont_output_data)
+ {
+ if (DECL_INITIAL (decl))
+ /* Output the actual data. */
+ output_constant (DECL_INITIAL (decl),
+ int_size_in_bytes (TREE_TYPE (decl)));
+ else
+ /* Leave space for it. */
+ assemble_zeros (int_size_in_bytes (TREE_TYPE (decl)));
+ }
+
+ finish:
+#ifdef XCOFF_DEBUGGING_INFO
+ /* Unfortunately, the IBM assembler cannot handle stabx before the actual
+ declaration. When something like ".stabx "aa:S-2",aa,133,0" is emitted
+ and `aa' hasn't been output yet, the assembler generates a stab entry with
+ a value of zero, in addition to creating an unnecessary external entry
+ for `aa'. Hence, we must postpone dbxout_symbol to here at the end. */
+
+ /* File-scope global variables are output here. */
+ if (write_symbols == XCOFF_DEBUG && top_level)
+ {
+ saved_in_section = in_section;
+
+ dbxout_symbol (decl, 0);
+
+ if (in_section != saved_in_section)
+ {
+ /* Switch to the proper section for this data. */
+#ifdef SELECT_SECTION
+ SELECT_SECTION (decl, reloc);
+#else
+ if (TREE_READONLY (decl)
+ && ! TREE_THIS_VOLATILE (decl)
+ && DECL_INITIAL (decl)
+ && (DECL_INITIAL (decl) == error_mark_node
+ || TREE_CONSTANT (DECL_INITIAL (decl)))
+ && ! (flag_pic && reloc))
+ readonly_data_section ();
+ else
+ data_section ();
+#endif
+ }
+ }
+#else
+ /* There must be a statement after a label. */
+ ;
+#endif
+}
+
+/* Return 1 if type TYPE contains any pointers. */
+
+static int
+contains_pointers_p (type)
+ tree type;
+{
+ switch (TREE_CODE (type))
+ {
+ case POINTER_TYPE:
+ case REFERENCE_TYPE:
+ /* I'm not sure whether OFFSET_TYPE needs this treatment,
+ so I'll play safe and return 1. */
+ case OFFSET_TYPE:
+ return 1;
+
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ case QUAL_UNION_TYPE:
+ {
+ tree fields;
+ /* For a type that has fields, see if the fields have pointers. */
+ for (fields = TYPE_FIELDS (type); fields; fields = TREE_CHAIN (fields))
+ if (TREE_CODE (fields) == FIELD_DECL
+ && contains_pointers_p (TREE_TYPE (fields)))
+ return 1;
+ return 0;
+ }
+
+ case ARRAY_TYPE:
+ /* An array type contains pointers if its element type does. */
+ return contains_pointers_p (TREE_TYPE (type));
+
+ default:
+ return 0;
+ }
+}
+
+/* Output text storage for constructor CONSTR. Returns rtx of
+ storage. */
+
+rtx
+bc_output_constructor (constr)
+ tree constr;
+{
+ int i;
+
+ /* Must always be a literal; non-literal constructors are handled
+ differently. */
+
+ if (!TREE_CONSTANT (constr))
+ abort ();
+
+ /* Always const */
+ text_section ();
+
+ /* Align */
+ for (i = 0; TYPE_ALIGN (constr) >= BITS_PER_UNIT << (i + 1); i++);
+ if (i > 0)
+ BC_OUTPUT_ALIGN (asm_out_file, i);
+
+ /* Output data */
+ output_constant (constr, int_size_in_bytes (TREE_TYPE (constr)));
+}
+
+
+/* Create storage for constructor CONSTR. */
+
+void
+bc_output_data_constructor (constr)
+ tree constr;
+{
+ int i;
+
+ /* Put in data section */
+ data_section ();
+
+ /* Align */
+ for (i = 0; TYPE_ALIGN (constr) >= BITS_PER_UNIT << (i + 1); i++);
+ if (i > 0)
+ BC_OUTPUT_ALIGN (asm_out_file, i);
+
+ /* The constructor is filled in at runtime. */
+ BC_OUTPUT_SKIP (asm_out_file, int_size_in_bytes (TREE_TYPE (constr)));
+}
+
+
+/* Output something to declare an external symbol to the assembler.
+ (Most assemblers don't need this, so we normally output nothing.)
+ Do nothing if DECL is not external. */
+
+void
+assemble_external (decl)
+ tree decl;
+{
+ if (output_bytecode)
+ return;
+
+#ifdef ASM_OUTPUT_EXTERNAL
+ if (TREE_CODE_CLASS (TREE_CODE (decl)) == 'd'
+ && DECL_EXTERNAL (decl) && TREE_PUBLIC (decl))
+ {
+ rtx rtl = DECL_RTL (decl);
+
+ if (GET_CODE (rtl) == MEM && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF
+ && ! SYMBOL_REF_USED (XEXP (rtl, 0)))
+ {
+ /* Some systems do require some output. */
+ SYMBOL_REF_USED (XEXP (rtl, 0)) = 1;
+ ASM_OUTPUT_EXTERNAL (asm_out_file, decl, XSTR (XEXP (rtl, 0), 0));
+ }
+ }
+#endif
+}
+
+/* Similar, for calling a library function FUN. */
+
+void
+assemble_external_libcall (fun)
+ rtx fun;
+{
+#ifdef ASM_OUTPUT_EXTERNAL_LIBCALL
+ if (!output_bytecode)
+ {
+ /* Declare library function name external when first used, if nec. */
+ if (! SYMBOL_REF_USED (fun))
+ {
+ SYMBOL_REF_USED (fun) = 1;
+ ASM_OUTPUT_EXTERNAL_LIBCALL (asm_out_file, fun);
+ }
+ }
+#endif
+}
+
+/* Declare the label NAME global. */
+
+void
+assemble_global (name)
+ char *name;
+{
+ ASM_GLOBALIZE_LABEL (asm_out_file, name);
+}
+
+/* Assemble a label named NAME. */
+
+void
+assemble_label (name)
+ char *name;
+{
+ if (output_bytecode)
+ BC_OUTPUT_LABEL (asm_out_file, name);
+ else
+ ASM_OUTPUT_LABEL (asm_out_file, name);
+}
+
+/* Output to FILE a reference to the assembler name of a C-level name NAME.
+ If NAME starts with a *, the rest of NAME is output verbatim.
+ Otherwise NAME is transformed in an implementation-defined way
+ (usually by the addition of an underscore).
+ Many macros in the tm file are defined to call this function. */
+
+void
+assemble_name (file, name)
+ FILE *file;
+ char *name;
+{
+ char *real_name;
+
+ STRIP_NAME_ENCODING (real_name, name);
+ TREE_SYMBOL_REFERENCED (get_identifier (real_name)) = 1;
+
+ if (name[0] == '*')
+ {
+ if (output_bytecode)
+ bc_emit_labelref (name);
+ else
+ fputs (&name[1], file);
+ }
+ else
+ {
+ if (output_bytecode)
+ BC_OUTPUT_LABELREF (file, name);
+ else
+ ASM_OUTPUT_LABELREF (file, name);
+ }
+}
+
+/* Allocate SIZE bytes writable static space with a gensym name
+ and return an RTX to refer to its address. */
+
+rtx
+assemble_static_space (size)
+ int size;
+{
+ char name[12];
+ char *namestring;
+ rtx x;
+ /* Round size up to multiple of BIGGEST_ALIGNMENT bits
+ so that each uninitialized object starts on such a boundary. */
+ int rounded = ((size + (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1)
+ / (BIGGEST_ALIGNMENT / BITS_PER_UNIT)
+ * (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
+
+#if 0
+ if (flag_shared_data)
+ data_section ();
+#endif
+
+ ASM_GENERATE_INTERNAL_LABEL (name, "LF", const_labelno);
+ ++const_labelno;
+
+ namestring = (char *) obstack_alloc (saveable_obstack,
+ strlen (name) + 2);
+ strcpy (namestring, name);
+
+ if (output_bytecode)
+ x = bc_gen_rtx (namestring, 0, (struct bc_label *) 0);
+ else
+ x = gen_rtx (SYMBOL_REF, Pmode, namestring);
+
+ if (output_bytecode)
+ {
+ BC_OUTPUT_LOCAL (asm_out_file, name, size, rounded);
+ }
+ else
+ {
+#ifdef ASM_OUTPUT_ALIGNED_LOCAL
+ ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, BIGGEST_ALIGNMENT);
+#else
+ ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded);
+#endif
+ }
+ return x;
+}
+
+/* Assemble the static constant template for function entry trampolines.
+ This is done at most once per compilation.
+ Returns an RTX for the address of the template. */
+
+rtx
+assemble_trampoline_template ()
+{
+ char label[256];
+ char *name;
+ int align;
+
+ /* Shouldn't get here */
+ if (output_bytecode)
+ abort ();
+
+ /* By default, put trampoline templates in read-only data section. */
+
+#ifdef TRAMPOLINE_SECTION
+ TRAMPOLINE_SECTION ();
+#else
+ readonly_data_section ();
+#endif
+
+ /* Write the assembler code to define one. */
+ align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT);
+ if (align > 0)
+ ASM_OUTPUT_ALIGN (asm_out_file, align);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LTRAMP", 0);
+ TRAMPOLINE_TEMPLATE (asm_out_file);
+
+ /* Record the rtl to refer to it. */
+ ASM_GENERATE_INTERNAL_LABEL (label, "LTRAMP", 0);
+ name
+ = (char *) obstack_copy0 (&permanent_obstack, label, strlen (label));
+ return gen_rtx (SYMBOL_REF, Pmode, name);
+}
+
+/* Assemble the integer constant X into an object of SIZE bytes.
+ X must be either a CONST_INT or CONST_DOUBLE.
+
+ Return 1 if we were able to output the constant, otherwise 0. If FORCE is
+ non-zero, abort if we can't output the constant. */
+
+int
+assemble_integer (x, size, force)
+ rtx x;
+ int size;
+ int force;
+{
+ /* First try to use the standard 1, 2, 4, 8, and 16 byte
+ ASM_OUTPUT... macros. */
+
+ switch (size)
+ {
+#ifdef ASM_OUTPUT_CHAR
+ case 1:
+ ASM_OUTPUT_CHAR (asm_out_file, x);
+ return 1;
+#endif
+
+#ifdef ASM_OUTPUT_SHORT
+ case 2:
+ ASM_OUTPUT_SHORT (asm_out_file, x);
+ return 1;
+#endif
+
+#ifdef ASM_OUTPUT_INT
+ case 4:
+ ASM_OUTPUT_INT (asm_out_file, x);
+ return 1;
+#endif
+
+#ifdef ASM_OUTPUT_DOUBLE_INT
+ case 8:
+ ASM_OUTPUT_DOUBLE_INT (asm_out_file, x);
+ return 1;
+#endif
+
+#ifdef ASM_OUTPUT_QUADRUPLE_INT
+ case 16:
+ ASM_OUTPUT_QUADRUPLE_INT (asm_out_file, x);
+ return 1;
+#endif
+ }
+
+ /* If we couldn't do it that way, there are two other possibilities: First,
+ if the machine can output an explicit byte and this is a 1 byte constant,
+ we can use ASM_OUTPUT_BYTE. */
+
+#ifdef ASM_OUTPUT_BYTE
+ if (size == 1 && GET_CODE (x) == CONST_INT)
+ {
+ ASM_OUTPUT_BYTE (asm_out_file, INTVAL (x));
+ return 1;
+ }
+#endif
+
+ /* Finally, if SIZE is larger than a single word, try to output the constant
+ one word at a time. */
+
+ if (size > UNITS_PER_WORD)
+ {
+ int i;
+ enum machine_mode mode
+ = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0);
+ rtx word;
+
+ for (i = 0; i < size / UNITS_PER_WORD; i++)
+ {
+ word = operand_subword (x, i, 0, mode);
+
+ if (word == 0)
+ break;
+
+ if (! assemble_integer (word, UNITS_PER_WORD, 0))
+ break;
+ }
+
+ if (i == size / UNITS_PER_WORD)
+ return 1;
+ /* If we output at least one word and then could not finish,
+ there is no valid way to continue. */
+ if (i > 0)
+ abort ();
+ }
+
+ if (force)
+ abort ();
+
+ return 0;
+}
+
+/* Assemble the floating-point constant D into an object of size MODE. */
+
+void
+assemble_real (d, mode)
+ REAL_VALUE_TYPE d;
+ enum machine_mode mode;
+{
+ jmp_buf output_constant_handler;
+
+ if (setjmp (output_constant_handler))
+ {
+ error ("floating point trap outputting a constant");
+#ifdef REAL_IS_NOT_DOUBLE
+ bzero ((char *) &d, sizeof d);
+ d = dconst0;
+#else
+ d = 0;
+#endif
+ }
+
+ set_float_handler (output_constant_handler);
+
+ switch (mode)
+ {
+#ifdef ASM_OUTPUT_BYTE_FLOAT
+ case QFmode:
+ ASM_OUTPUT_BYTE_FLOAT (asm_out_file, d);
+ break;
+#endif
+#ifdef ASM_OUTPUT_SHORT_FLOAT
+ case HFmode:
+ ASM_OUTPUT_SHORT_FLOAT (asm_out_file, d);
+ break;
+#endif
+#ifdef ASM_OUTPUT_THREE_QUARTER_FLOAT
+ case TQFmode:
+ ASM_OUTPUT_THREE_QUARTER_FLOAT (asm_out_file, d);
+ break;
+#endif
+#ifdef ASM_OUTPUT_FLOAT
+ case SFmode:
+ ASM_OUTPUT_FLOAT (asm_out_file, d);
+ break;
+#endif
+
+#ifdef ASM_OUTPUT_DOUBLE
+ case DFmode:
+ ASM_OUTPUT_DOUBLE (asm_out_file, d);
+ break;
+#endif
+
+#ifdef ASM_OUTPUT_LONG_DOUBLE
+ case XFmode:
+ case TFmode:
+ ASM_OUTPUT_LONG_DOUBLE (asm_out_file, d);
+ break;
+#endif
+
+ default:
+ abort ();
+ }
+
+ set_float_handler (NULL_PTR);
+}
+
+/* Here we combine duplicate floating constants to make
+ CONST_DOUBLE rtx's, and force those out to memory when necessary. */
+
+/* Chain of all CONST_DOUBLE rtx's constructed for the current function.
+ They are chained through the CONST_DOUBLE_CHAIN.
+ A CONST_DOUBLE rtx has CONST_DOUBLE_MEM != cc0_rtx iff it is on this chain.
+ In that case, CONST_DOUBLE_MEM is either a MEM,
+ or const0_rtx if no MEM has been made for this CONST_DOUBLE yet.
+
+ (CONST_DOUBLE_MEM is used only for top-level functions.
+ See force_const_mem for explanation.) */
+
+static rtx const_double_chain;
+
+/* Return a CONST_DOUBLE or CONST_INT for a value specified as a pair of ints.
+ For an integer, I0 is the low-order word and I1 is the high-order word.
+ For a real number, I0 is the word with the low address
+ and I1 is the word with the high address. */
+
+rtx
+immed_double_const (i0, i1, mode)
+ HOST_WIDE_INT i0, i1;
+ enum machine_mode mode;
+{
+ register rtx r;
+ int in_current_obstack;
+
+ if (GET_MODE_CLASS (mode) == MODE_INT
+ || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT)
+ {
+ /* We clear out all bits that don't belong in MODE, unless they and our
+ sign bit are all one. So we get either a reasonable negative value
+ or a reasonable unsigned value for this mode. */
+ int width = GET_MODE_BITSIZE (mode);
+ if (width < HOST_BITS_PER_WIDE_INT
+ && ((i0 & ((HOST_WIDE_INT) (-1) << (width - 1)))
+ != ((HOST_WIDE_INT) (-1) << (width - 1))))
+ i0 &= ((HOST_WIDE_INT) 1 << width) - 1, i1 = 0;
+ else if (width == HOST_BITS_PER_WIDE_INT
+ && ! (i1 == ~0 && i0 < 0))
+ i1 = 0;
+ else if (width > 2 * HOST_BITS_PER_WIDE_INT)
+ /* We cannot represent this value as a constant. */
+ abort ();
+
+ /* If this would be an entire word for the target, but is not for
+ the host, then sign-extend on the host so that the number will look
+ the same way on the host that it would on the target.
+
+ For example, when building a 64 bit alpha hosted 32 bit sparc
+ targeted compiler, then we want the 32 bit unsigned value -1 to be
+ represented as a 64 bit value -1, and not as 0x00000000ffffffff.
+ The later confuses the sparc backend. */
+
+ if (BITS_PER_WORD < HOST_BITS_PER_WIDE_INT && BITS_PER_WORD == width
+ && (i0 & ((HOST_WIDE_INT) 1 << (width - 1))))
+ i0 |= ((HOST_WIDE_INT) (-1) << width);
+
+ /* If MODE fits within HOST_BITS_PER_WIDE_INT, always use a CONST_INT.
+
+ ??? Strictly speaking, this is wrong if we create a CONST_INT
+ for a large unsigned constant with the size of MODE being
+ HOST_BITS_PER_WIDE_INT and later try to interpret that constant in a
+ wider mode. In that case we will mis-interpret it as a negative
+ number.
+
+ Unfortunately, the only alternative is to make a CONST_DOUBLE
+ for any constant in any mode if it is an unsigned constant larger
+ than the maximum signed integer in an int on the host. However,
+ doing this will break everyone that always expects to see a CONST_INT
+ for SImode and smaller.
+
+ We have always been making CONST_INTs in this case, so nothing new
+ is being broken. */
+
+ if (width <= HOST_BITS_PER_WIDE_INT)
+ i1 = (i0 < 0) ? ~0 : 0;
+
+ /* If this integer fits in one word, return a CONST_INT. */
+ if ((i1 == 0 && i0 >= 0)
+ || (i1 == ~0 && i0 < 0))
+ return GEN_INT (i0);
+
+ /* We use VOIDmode for integers. */
+ mode = VOIDmode;
+ }
+
+ /* Search the chain for an existing CONST_DOUBLE with the right value.
+ If one is found, return it. */
+
+ for (r = const_double_chain; r; r = CONST_DOUBLE_CHAIN (r))
+ if (CONST_DOUBLE_LOW (r) == i0 && CONST_DOUBLE_HIGH (r) == i1
+ && GET_MODE (r) == mode)
+ return r;
+
+ /* No; make a new one and add it to the chain.
+
+ We may be called by an optimizer which may be discarding any memory
+ allocated during its processing (such as combine and loop). However,
+ we will be leaving this constant on the chain, so we cannot tolerate
+ freed memory. So switch to saveable_obstack for this allocation
+ and then switch back if we were in current_obstack. */
+
+ push_obstacks_nochange ();
+ rtl_in_saveable_obstack ();
+ r = gen_rtx (CONST_DOUBLE, mode, 0, i0, i1);
+ pop_obstacks ();
+
+ /* Don't touch const_double_chain in nested function; see force_const_mem.
+ Also, don't touch it if not inside any function. */
+ if (outer_function_chain == 0 && current_function_decl != 0)
+ {
+ CONST_DOUBLE_CHAIN (r) = const_double_chain;
+ const_double_chain = r;
+ }
+
+ /* Store const0_rtx in mem-slot since this CONST_DOUBLE is on the chain.
+ Actual use of mem-slot is only through force_const_mem. */
+
+ CONST_DOUBLE_MEM (r) = const0_rtx;
+
+ return r;
+}
+
+/* Return a CONST_DOUBLE for a specified `double' value
+ and machine mode. */
+
+rtx
+immed_real_const_1 (d, mode)
+ REAL_VALUE_TYPE d;
+ enum machine_mode mode;
+{
+ union real_extract u;
+ register rtx r;
+ int in_current_obstack;
+
+ /* Get the desired `double' value as a sequence of ints
+ since that is how they are stored in a CONST_DOUBLE. */
+
+ u.d = d;
+
+ /* Detect special cases. */
+
+ /* Avoid REAL_VALUES_EQUAL here in order to distinguish minus zero. */
+ if (!bcmp ((char *) &dconst0, (char *) &d, sizeof d))
+ return CONST0_RTX (mode);
+ /* Check for NaN first, because some ports (specifically the i386) do not
+ emit correct ieee-fp code by default, and thus will generate a core
+ dump here if we pass a NaN to REAL_VALUES_EQUAL and if REAL_VALUES_EQUAL
+ does a floating point comparison. */
+ else if (! REAL_VALUE_ISNAN (d) && REAL_VALUES_EQUAL (dconst1, d))
+ return CONST1_RTX (mode);
+
+ if (sizeof u == 2 * sizeof (HOST_WIDE_INT))
+ return immed_double_const (u.i[0], u.i[1], mode);
+
+ /* The rest of this function handles the case where
+ a float value requires more than 2 ints of space.
+ It will be deleted as dead code on machines that don't need it. */
+
+ /* Search the chain for an existing CONST_DOUBLE with the right value.
+ If one is found, return it. */
+
+ for (r = const_double_chain; r; r = CONST_DOUBLE_CHAIN (r))
+ if (! bcmp ((char *) &CONST_DOUBLE_LOW (r), (char *) &u, sizeof u)
+ && GET_MODE (r) == mode)
+ return r;
+
+ /* No; make a new one and add it to the chain.
+
+ We may be called by an optimizer which may be discarding any memory
+ allocated during its processing (such as combine and loop). However,
+ we will be leaving this constant on the chain, so we cannot tolerate
+ freed memory. So switch to saveable_obstack for this allocation
+ and then switch back if we were in current_obstack. */
+
+ push_obstacks_nochange ();
+ rtl_in_saveable_obstack ();
+ r = rtx_alloc (CONST_DOUBLE);
+ PUT_MODE (r, mode);
+ bcopy ((char *) &u, (char *) &CONST_DOUBLE_LOW (r), sizeof u);
+ pop_obstacks ();
+
+ /* Don't touch const_double_chain in nested function; see force_const_mem.
+ Also, don't touch it if not inside any function. */
+ if (outer_function_chain == 0 && current_function_decl != 0)
+ {
+ CONST_DOUBLE_CHAIN (r) = const_double_chain;
+ const_double_chain = r;
+ }
+
+ /* Store const0_rtx in CONST_DOUBLE_MEM since this CONST_DOUBLE is on the
+ chain, but has not been allocated memory. Actual use of CONST_DOUBLE_MEM
+ is only through force_const_mem. */
+
+ CONST_DOUBLE_MEM (r) = const0_rtx;
+
+ return r;
+}
+
+/* Return a CONST_DOUBLE rtx for a value specified by EXP,
+ which must be a REAL_CST tree node. */
+
+rtx
+immed_real_const (exp)
+ tree exp;
+{
+ return immed_real_const_1 (TREE_REAL_CST (exp), TYPE_MODE (TREE_TYPE (exp)));
+}
+
+/* At the end of a function, forget the memory-constants
+ previously made for CONST_DOUBLEs. Mark them as not on real_constant_chain.
+ Also clear out real_constant_chain and clear out all the chain-pointers. */
+
+void
+clear_const_double_mem ()
+{
+ register rtx r, next;
+
+ /* Don't touch CONST_DOUBLE_MEM for nested functions.
+ See force_const_mem for explanation. */
+ if (outer_function_chain != 0)
+ return;
+
+ for (r = const_double_chain; r; r = next)
+ {
+ next = CONST_DOUBLE_CHAIN (r);
+ CONST_DOUBLE_CHAIN (r) = 0;
+ CONST_DOUBLE_MEM (r) = cc0_rtx;
+ }
+ const_double_chain = 0;
+}
+
+/* Given an expression EXP with a constant value,
+ reduce it to the sum of an assembler symbol and an integer.
+ Store them both in the structure *VALUE.
+ Abort if EXP does not reduce. */
+
+struct addr_const
+{
+ rtx base;
+ HOST_WIDE_INT offset;
+};
+
+static void
+decode_addr_const (exp, value)
+ tree exp;
+ struct addr_const *value;
+{
+ register tree target = TREE_OPERAND (exp, 0);
+ register int offset = 0;
+ register rtx x;
+
+ while (1)
+ {
+ if (TREE_CODE (target) == COMPONENT_REF
+ && (TREE_CODE (DECL_FIELD_BITPOS (TREE_OPERAND (target, 1)))
+ == INTEGER_CST))
+ {
+ offset += TREE_INT_CST_LOW (DECL_FIELD_BITPOS (TREE_OPERAND (target, 1))) / BITS_PER_UNIT;
+ target = TREE_OPERAND (target, 0);
+ }
+ else if (TREE_CODE (target) == ARRAY_REF)
+ {
+ if (TREE_CODE (TREE_OPERAND (target, 1)) != INTEGER_CST
+ || TREE_CODE (TYPE_SIZE (TREE_TYPE (target))) != INTEGER_CST)
+ abort ();
+ offset += ((TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (target)))
+ * TREE_INT_CST_LOW (TREE_OPERAND (target, 1)))
+ / BITS_PER_UNIT);
+ target = TREE_OPERAND (target, 0);
+ }
+ else
+ break;
+ }
+
+ switch (TREE_CODE (target))
+ {
+ case VAR_DECL:
+ case FUNCTION_DECL:
+ x = DECL_RTL (target);
+ break;
+
+ case LABEL_DECL:
+ if (output_bytecode)
+ /* FIXME: this may not be correct, check it */
+ x = bc_gen_rtx (TREE_STRING_POINTER (target), 0, (struct bc_label *) 0);
+ else
+ x = gen_rtx (MEM, FUNCTION_MODE,
+ gen_rtx (LABEL_REF, VOIDmode,
+ label_rtx (TREE_OPERAND (exp, 0))));
+ break;
+
+ case REAL_CST:
+ case STRING_CST:
+ case COMPLEX_CST:
+ case CONSTRUCTOR:
+ x = TREE_CST_RTL (target);
+ break;
+
+ default:
+ abort ();
+ }
+
+ if (!output_bytecode)
+ {
+ if (GET_CODE (x) != MEM)
+ abort ();
+ x = XEXP (x, 0);
+ }
+
+ value->base = x;
+ value->offset = offset;
+}
+
+/* Uniquize all constants that appear in memory.
+ Each constant in memory thus far output is recorded
+ in `const_hash_table' with a `struct constant_descriptor'
+ that contains a polish representation of the value of
+ the constant.
+
+ We cannot store the trees in the hash table
+ because the trees may be temporary. */
+
+struct constant_descriptor
+{
+ struct constant_descriptor *next;
+ char *label;
+ char contents[1];
+};
+
+#define HASHBITS 30
+#define MAX_HASH_TABLE 1009
+static struct constant_descriptor *const_hash_table[MAX_HASH_TABLE];
+
+/* Compute a hash code for a constant expression. */
+
+int
+const_hash (exp)
+ tree exp;
+{
+ register char *p;
+ register int len, hi, i;
+ register enum tree_code code = TREE_CODE (exp);
+
+ if (code == INTEGER_CST)
+ {
+ p = (char *) &TREE_INT_CST_LOW (exp);
+ len = 2 * sizeof TREE_INT_CST_LOW (exp);
+ }
+ else if (code == REAL_CST)
+ {
+ p = (char *) &TREE_REAL_CST (exp);
+ len = sizeof TREE_REAL_CST (exp);
+ }
+ else if (code == STRING_CST)
+ p = TREE_STRING_POINTER (exp), len = TREE_STRING_LENGTH (exp);
+ else if (code == COMPLEX_CST)
+ return const_hash (TREE_REALPART (exp)) * 5
+ + const_hash (TREE_IMAGPART (exp));
+ else if (code == CONSTRUCTOR)
+ {
+ register tree link;
+
+ /* For record type, include the type in the hashing.
+ We do not do so for array types
+ because (1) the sizes of the elements are sufficient
+ and (2) distinct array types can have the same constructor.
+ Instead, we include the array size because the constructor could
+ be shorter. */
+ if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE)
+ hi = ((HOST_WIDE_INT) TREE_TYPE (exp) & ((1 << HASHBITS) - 1))
+ % MAX_HASH_TABLE;
+ else
+ hi = ((5 + int_size_in_bytes (TREE_TYPE (exp)))
+ & ((1 << HASHBITS) - 1)) % MAX_HASH_TABLE;
+
+ for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link))
+ if (TREE_VALUE (link))
+ hi = (hi * 603 + const_hash (TREE_VALUE (link))) % MAX_HASH_TABLE;
+
+ return hi;
+ }
+ else if (code == ADDR_EXPR)
+ {
+ struct addr_const value;
+ decode_addr_const (exp, &value);
+ if (GET_CODE (value.base) == SYMBOL_REF)
+ {
+ /* Don't hash the address of the SYMBOL_REF;
+ only use the offset and the symbol name. */
+ hi = value.offset;
+ p = XSTR (value.base, 0);
+ for (i = 0; p[i] != 0; i++)
+ hi = ((hi * 613) + (unsigned)(p[i]));
+ }
+ else if (GET_CODE (value.base) == LABEL_REF)
+ hi = value.offset + CODE_LABEL_NUMBER (XEXP (value.base, 0)) * 13;
+
+ hi &= (1 << HASHBITS) - 1;
+ hi %= MAX_HASH_TABLE;
+ return hi;
+ }
+ else if (code == PLUS_EXPR || code == MINUS_EXPR)
+ return const_hash (TREE_OPERAND (exp, 0)) * 9
+ + const_hash (TREE_OPERAND (exp, 1));
+ else if (code == NOP_EXPR || code == CONVERT_EXPR)
+ return const_hash (TREE_OPERAND (exp, 0)) * 7 + 2;
+
+ /* Compute hashing function */
+ hi = len;
+ for (i = 0; i < len; i++)
+ hi = ((hi * 613) + (unsigned)(p[i]));
+
+ hi &= (1 << HASHBITS) - 1;
+ hi %= MAX_HASH_TABLE;
+ return hi;
+}
+
+/* Compare a constant expression EXP with a constant-descriptor DESC.
+ Return 1 if DESC describes a constant with the same value as EXP. */
+
+static int
+compare_constant (exp, desc)
+ tree exp;
+ struct constant_descriptor *desc;
+{
+ return 0 != compare_constant_1 (exp, desc->contents);
+}
+
+/* Compare constant expression EXP with a substring P of a constant descriptor.
+ If they match, return a pointer to the end of the substring matched.
+ If they do not match, return 0.
+
+ Since descriptors are written in polish prefix notation,
+ this function can be used recursively to test one operand of EXP
+ against a subdescriptor, and if it succeeds it returns the
+ address of the subdescriptor for the next operand. */
+
+static char *
+compare_constant_1 (exp, p)
+ tree exp;
+ char *p;
+{
+ register char *strp;
+ register int len;
+ register enum tree_code code = TREE_CODE (exp);
+
+ if (code != (enum tree_code) *p++)
+ return 0;
+
+ if (code == INTEGER_CST)
+ {
+ /* Integer constants are the same only if the same width of type. */
+ if (*p++ != TYPE_PRECISION (TREE_TYPE (exp)))
+ return 0;
+ strp = (char *) &TREE_INT_CST_LOW (exp);
+ len = 2 * sizeof TREE_INT_CST_LOW (exp);
+ }
+ else if (code == REAL_CST)
+ {
+ /* Real constants are the same only if the same width of type. */
+ if (*p++ != TYPE_PRECISION (TREE_TYPE (exp)))
+ return 0;
+ strp = (char *) &TREE_REAL_CST (exp);
+ len = sizeof TREE_REAL_CST (exp);
+ }
+ else if (code == STRING_CST)
+ {
+ if (flag_writable_strings)
+ return 0;
+ strp = TREE_STRING_POINTER (exp);
+ len = TREE_STRING_LENGTH (exp);
+ if (bcmp ((char *) &TREE_STRING_LENGTH (exp), p,
+ sizeof TREE_STRING_LENGTH (exp)))
+ return 0;
+ p += sizeof TREE_STRING_LENGTH (exp);
+ }
+ else if (code == COMPLEX_CST)
+ {
+ p = compare_constant_1 (TREE_REALPART (exp), p);
+ if (p == 0) return 0;
+ p = compare_constant_1 (TREE_IMAGPART (exp), p);
+ return p;
+ }
+ else if (code == CONSTRUCTOR)
+ {
+ register tree link;
+ int length = list_length (CONSTRUCTOR_ELTS (exp));
+ tree type;
+
+ if (bcmp ((char *) &length, p, sizeof length))
+ return 0;
+ p += sizeof length;
+
+ /* For record constructors, insist that the types match.
+ For arrays, just verify both constructors are for arrays. */
+ if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE)
+ type = TREE_TYPE (exp);
+ else
+ type = 0;
+ if (bcmp ((char *) &type, p, sizeof type))
+ return 0;
+ p += sizeof type;
+
+ /* For arrays, insist that the size in bytes match. */
+ if (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE)
+ {
+ int size = int_size_in_bytes (TREE_TYPE (exp));
+ if (bcmp ((char *) &size, p, sizeof size))
+ return 0;
+ p += sizeof size;
+ }
+
+ for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link))
+ {
+ if (TREE_VALUE (link))
+ {
+ if ((p = compare_constant_1 (TREE_VALUE (link), p)) == 0)
+ return 0;
+ }
+ else
+ {
+ tree zero = 0;
+
+ if (bcmp ((char *) &zero, p, sizeof zero))
+ return 0;
+ p += sizeof zero;
+ }
+ }
+
+ return p;
+ }
+ else if (code == ADDR_EXPR)
+ {
+ struct addr_const value;
+ decode_addr_const (exp, &value);
+ strp = (char *) &value.offset;
+ len = sizeof value.offset;
+ /* Compare the offset. */
+ while (--len >= 0)
+ if (*p++ != *strp++)
+ return 0;
+ /* Compare symbol name. */
+ strp = XSTR (value.base, 0);
+ len = strlen (strp) + 1;
+ }
+ else if (code == PLUS_EXPR || code == MINUS_EXPR)
+ {
+ p = compare_constant_1 (TREE_OPERAND (exp, 0), p);
+ if (p == 0) return 0;
+ p = compare_constant_1 (TREE_OPERAND (exp, 1), p);
+ return p;
+ }
+ else if (code == NOP_EXPR || code == CONVERT_EXPR)
+ {
+ p = compare_constant_1 (TREE_OPERAND (exp, 0), p);
+ return p;
+ }
+
+ /* Compare constant contents. */
+ while (--len >= 0)
+ if (*p++ != *strp++)
+ return 0;
+
+ return p;
+}
+
+/* Construct a constant descriptor for the expression EXP.
+ It is up to the caller to enter the descriptor in the hash table. */
+
+static struct constant_descriptor *
+record_constant (exp)
+ tree exp;
+{
+ struct constant_descriptor *next = 0;
+ char *label = 0;
+
+ /* Make a struct constant_descriptor. The first two pointers will
+ be filled in later. Here we just leave space for them. */
+
+ obstack_grow (&permanent_obstack, (char *) &next, sizeof next);
+ obstack_grow (&permanent_obstack, (char *) &label, sizeof label);
+ record_constant_1 (exp);
+ return (struct constant_descriptor *) obstack_finish (&permanent_obstack);
+}
+
+/* Add a description of constant expression EXP
+ to the object growing in `permanent_obstack'.
+ No need to return its address; the caller will get that
+ from the obstack when the object is complete. */
+
+static void
+record_constant_1 (exp)
+ tree exp;
+{
+ register char *strp;
+ register int len;
+ register enum tree_code code = TREE_CODE (exp);
+
+ obstack_1grow (&permanent_obstack, (unsigned int) code);
+
+ if (code == INTEGER_CST)
+ {
+ obstack_1grow (&permanent_obstack, TYPE_PRECISION (TREE_TYPE (exp)));
+ strp = (char *) &TREE_INT_CST_LOW (exp);
+ len = 2 * sizeof TREE_INT_CST_LOW (exp);
+ }
+ else if (code == REAL_CST)
+ {
+ obstack_1grow (&permanent_obstack, TYPE_PRECISION (TREE_TYPE (exp)));
+ strp = (char *) &TREE_REAL_CST (exp);
+ len = sizeof TREE_REAL_CST (exp);
+ }
+ else if (code == STRING_CST)
+ {
+ if (flag_writable_strings)
+ return;
+ strp = TREE_STRING_POINTER (exp);
+ len = TREE_STRING_LENGTH (exp);
+ obstack_grow (&permanent_obstack, (char *) &TREE_STRING_LENGTH (exp),
+ sizeof TREE_STRING_LENGTH (exp));
+ }
+ else if (code == COMPLEX_CST)
+ {
+ record_constant_1 (TREE_REALPART (exp));
+ record_constant_1 (TREE_IMAGPART (exp));
+ return;
+ }
+ else if (code == CONSTRUCTOR)
+ {
+ register tree link;
+ int length = list_length (CONSTRUCTOR_ELTS (exp));
+ tree type;
+
+ obstack_grow (&permanent_obstack, (char *) &length, sizeof length);
+
+ /* For record constructors, insist that the types match.
+ For arrays, just verify both constructors are for arrays. */
+ if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE)
+ type = TREE_TYPE (exp);
+ else
+ type = 0;
+ obstack_grow (&permanent_obstack, (char *) &type, sizeof type);
+
+ /* For arrays, insist that the size in bytes match. */
+ if (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE)
+ {
+ int size = int_size_in_bytes (TREE_TYPE (exp));
+ obstack_grow (&permanent_obstack, (char *) &size, sizeof size);
+ }
+
+ for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link))
+ {
+ if (TREE_VALUE (link))
+ record_constant_1 (TREE_VALUE (link));
+ else
+ {
+ tree zero = 0;
+
+ obstack_grow (&permanent_obstack, (char *) &zero, sizeof zero);
+ }
+ }
+
+ return;
+ }
+ else if (code == ADDR_EXPR)
+ {
+ struct addr_const value;
+ decode_addr_const (exp, &value);
+ /* Record the offset. */
+ obstack_grow (&permanent_obstack,
+ (char *) &value.offset, sizeof value.offset);
+ /* Record the symbol name. */
+ obstack_grow (&permanent_obstack, XSTR (value.base, 0),
+ strlen (XSTR (value.base, 0)) + 1);
+ return;
+ }
+ else if (code == PLUS_EXPR || code == MINUS_EXPR)
+ {
+ record_constant_1 (TREE_OPERAND (exp, 0));
+ record_constant_1 (TREE_OPERAND (exp, 1));
+ return;
+ }
+ else if (code == NOP_EXPR || code == CONVERT_EXPR)
+ {
+ record_constant_1 (TREE_OPERAND (exp, 0));
+ return;
+ }
+
+ /* Record constant contents. */
+ obstack_grow (&permanent_obstack, strp, len);
+}
+
+/* Record a list of constant expressions that were passed to
+ output_constant_def but that could not be output right away. */
+
+struct deferred_constant
+{
+ struct deferred_constant *next;
+ tree exp;
+ int reloc;
+ int labelno;
+};
+
+static struct deferred_constant *deferred_constants;
+
+/* Nonzero means defer output of addressed subconstants
+ (i.e., those for which output_constant_def is called.) */
+static int defer_addressed_constants_flag;
+
+/* Start deferring output of subconstants. */
+
+void
+defer_addressed_constants ()
+{
+ defer_addressed_constants_flag++;
+}
+
+/* Stop deferring output of subconstants,
+ and output now all those that have been deferred. */
+
+void
+output_deferred_addressed_constants ()
+{
+ struct deferred_constant *p, *next;
+
+ defer_addressed_constants_flag--;
+
+ if (defer_addressed_constants_flag > 0)
+ return;
+
+ for (p = deferred_constants; p; p = next)
+ {
+ output_constant_def_contents (p->exp, p->reloc, p->labelno);
+ next = p->next;
+ free (p);
+ }
+
+ deferred_constants = 0;
+}
+
+/* Make a copy of the whole tree structure for a constant.
+ This handles the same types of nodes that compare_constant
+ and record_constant handle. */
+
+static tree
+copy_constant (exp)
+ tree exp;
+{
+ switch (TREE_CODE (exp))
+ {
+ case INTEGER_CST:
+ case REAL_CST:
+ case STRING_CST:
+ case ADDR_EXPR:
+ /* For ADDR_EXPR, we do not want to copy the decl
+ whose address is requested. */
+ return copy_node (exp);
+
+ case COMPLEX_CST:
+ return build_complex (copy_constant (TREE_REALPART (exp)),
+ copy_constant (TREE_IMAGPART (exp)));
+
+ case PLUS_EXPR:
+ case MINUS_EXPR:
+ return build (TREE_CODE (exp), TREE_TYPE (exp),
+ copy_constant (TREE_OPERAND (exp, 0)),
+ copy_constant (TREE_OPERAND (exp, 1)));
+
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ return build1 (TREE_CODE (exp), TREE_TYPE (exp),
+ copy_constant (TREE_OPERAND (exp, 0)));
+
+ case CONSTRUCTOR:
+ {
+ tree copy = copy_node (exp);
+ tree list = copy_list (CONSTRUCTOR_ELTS (exp));
+ tree tail;
+
+ CONSTRUCTOR_ELTS (copy) = list;
+ for (tail = list; tail; tail = TREE_CHAIN (tail))
+ TREE_VALUE (tail) = copy_constant (TREE_VALUE (tail));
+
+ return copy;
+ }
+
+ default:
+ abort ();
+ }
+}
+
+/* Return an rtx representing a reference to constant data in memory
+ for the constant expression EXP.
+
+ If assembler code for such a constant has already been output,
+ return an rtx to refer to it.
+ Otherwise, output such a constant in memory (or defer it for later)
+ and generate an rtx for it.
+
+ The TREE_CST_RTL of EXP is set up to point to that rtx.
+ The const_hash_table records which constants already have label strings. */
+
+rtx
+output_constant_def (exp)
+ tree exp;
+{
+ register int hash;
+ register struct constant_descriptor *desc;
+ char label[256];
+ char *found = 0;
+ int reloc;
+ register rtx def;
+
+ if (TREE_CODE (exp) == INTEGER_CST)
+ abort (); /* No TREE_CST_RTL slot in these. */
+
+ if (TREE_CST_RTL (exp))
+ return TREE_CST_RTL (exp);
+
+ /* Make sure any other constants whose addresses appear in EXP
+ are assigned label numbers. */
+
+ reloc = output_addressed_constants (exp);
+
+ /* Compute hash code of EXP. Search the descriptors for that hash code
+ to see if any of them describes EXP. If yes, the descriptor records
+ the label number already assigned. */
+
+ hash = const_hash (exp) % MAX_HASH_TABLE;
+
+ for (desc = const_hash_table[hash]; desc; desc = desc->next)
+ if (compare_constant (exp, desc))
+ {
+ found = desc->label;
+ break;
+ }
+
+ if (found == 0)
+ {
+ /* No constant equal to EXP is known to have been output.
+ Make a constant descriptor to enter EXP in the hash table.
+ Assign the label number and record it in the descriptor for
+ future calls to this function to find. */
+
+ /* Create a string containing the label name, in LABEL. */
+ ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno);
+
+ desc = record_constant (exp);
+ desc->next = const_hash_table[hash];
+ desc->label
+ = (char *) obstack_copy0 (&permanent_obstack, label, strlen (label));
+ const_hash_table[hash] = desc;
+ }
+ else
+ {
+ /* Create a string containing the label name, in LABEL. */
+ ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno);
+ }
+
+ /* We have a symbol name; construct the SYMBOL_REF and the MEM. */
+
+ push_obstacks_nochange ();
+ if (TREE_PERMANENT (exp))
+ end_temporary_allocation ();
+
+ def = gen_rtx (SYMBOL_REF, Pmode, desc->label);
+
+ TREE_CST_RTL (exp)
+ = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)), def);
+ RTX_UNCHANGING_P (TREE_CST_RTL (exp)) = 1;
+ if (AGGREGATE_TYPE_P (TREE_TYPE (exp)))
+ MEM_IN_STRUCT_P (TREE_CST_RTL (exp)) = 1;
+
+ pop_obstacks ();
+
+ /* Optionally set flags or add text to the name to record information
+ such as that it is a function name. If the name is changed, the macro
+ ASM_OUTPUT_LABELREF will have to know how to strip this information. */
+#ifdef ENCODE_SECTION_INFO
+ ENCODE_SECTION_INFO (exp);
+#endif
+
+ /* If this is the first time we've seen this particular constant,
+ output it (or defer its output for later). */
+ if (found == 0)
+ {
+ if (defer_addressed_constants_flag)
+ {
+ struct deferred_constant *p;
+ p = (struct deferred_constant *) xmalloc (sizeof (struct deferred_constant));
+
+ push_obstacks_nochange ();
+ suspend_momentary ();
+ p->exp = copy_constant (exp);
+ pop_obstacks ();
+ p->reloc = reloc;
+ p->labelno = const_labelno++;
+ p->next = deferred_constants;
+ deferred_constants = p;
+ }
+ else
+ output_constant_def_contents (exp, reloc, const_labelno++);
+ }
+
+ return TREE_CST_RTL (exp);
+}
+
+/* Now output assembler code to define the label for EXP,
+ and follow it with the data of EXP. */
+
+static void
+output_constant_def_contents (exp, reloc, labelno)
+ tree exp;
+ int reloc;
+ int labelno;
+{
+ int align;
+
+ if (IN_NAMED_SECTION (exp))
+ named_section (TREE_STRING_POINTER (DECL_SECTION_NAME (exp)));
+ else
+ {
+ /* First switch to text section, except for writable strings. */
+#ifdef SELECT_SECTION
+ SELECT_SECTION (exp, reloc);
+#else
+ if (((TREE_CODE (exp) == STRING_CST) && flag_writable_strings)
+ || (flag_pic && reloc))
+ data_section ();
+ else
+ readonly_data_section ();
+#endif
+ }
+
+ /* Align the location counter as required by EXP's data type. */
+ align = TYPE_ALIGN (TREE_TYPE (exp));
+#ifdef CONSTANT_ALIGNMENT
+ align = CONSTANT_ALIGNMENT (exp, align);
+#endif
+
+ if (align > BITS_PER_UNIT)
+ {
+ if (!output_bytecode)
+ {
+ ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT));
+ }
+ else
+ {
+ BC_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT));
+ }
+ }
+
+ /* Output the label itself. */
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LC", labelno);
+
+ /* Output the value of EXP. */
+ output_constant (exp,
+ (TREE_CODE (exp) == STRING_CST
+ ? TREE_STRING_LENGTH (exp)
+ : int_size_in_bytes (TREE_TYPE (exp))));
+
+}
+
+/* Similar hash facility for making memory-constants
+ from constant rtl-expressions. It is used on RISC machines
+ where immediate integer arguments and constant addresses are restricted
+ so that such constants must be stored in memory.
+
+ This pool of constants is reinitialized for each function
+ so each function gets its own constants-pool that comes right before it.
+
+ All structures allocated here are discarded when functions are saved for
+ inlining, so they do not need to be allocated permanently. */
+
+#define MAX_RTX_HASH_TABLE 61
+static struct constant_descriptor **const_rtx_hash_table;
+
+/* Structure to represent sufficient information about a constant so that
+ it can be output when the constant pool is output, so that function
+ integration can be done, and to simplify handling on machines that reference
+ constant pool as base+displacement. */
+
+struct pool_constant
+{
+ struct constant_descriptor *desc;
+ struct pool_constant *next;
+ enum machine_mode mode;
+ rtx constant;
+ int labelno;
+ int align;
+ int offset;
+};
+
+/* Pointers to first and last constant in pool. */
+
+static struct pool_constant *first_pool, *last_pool;
+
+/* Current offset in constant pool (does not include any machine-specific
+ header. */
+
+static int pool_offset;
+
+/* Structure used to maintain hash table mapping symbols used to their
+ corresponding constants. */
+
+struct pool_sym
+{
+ char *label;
+ struct pool_constant *pool;
+ struct pool_sym *next;
+};
+
+static struct pool_sym **const_rtx_sym_hash_table;
+
+/* Hash code for a SYMBOL_REF with CONSTANT_POOL_ADDRESS_P true.
+ The argument is XSTR (... , 0) */
+
+#define SYMHASH(LABEL) \
+ ((((HOST_WIDE_INT) (LABEL)) & ((1 << HASHBITS) - 1)) % MAX_RTX_HASH_TABLE)
+
+/* Initialize constant pool hashing for next function. */
+
+void
+init_const_rtx_hash_table ()
+{
+ const_rtx_hash_table
+ = ((struct constant_descriptor **)
+ oballoc (MAX_RTX_HASH_TABLE * sizeof (struct constant_descriptor *)));
+ const_rtx_sym_hash_table
+ = ((struct pool_sym **)
+ oballoc (MAX_RTX_HASH_TABLE * sizeof (struct pool_sym *)));
+ bzero ((char *) const_rtx_hash_table,
+ MAX_RTX_HASH_TABLE * sizeof (struct constant_descriptor *));
+ bzero ((char *) const_rtx_sym_hash_table,
+ MAX_RTX_HASH_TABLE * sizeof (struct pool_sym *));
+
+ first_pool = last_pool = 0;
+ pool_offset = 0;
+}
+
+/* Save and restore it for a nested function. */
+
+void
+save_varasm_status (p)
+ struct function *p;
+{
+ p->const_rtx_hash_table = const_rtx_hash_table;
+ p->const_rtx_sym_hash_table = const_rtx_sym_hash_table;
+ p->first_pool = first_pool;
+ p->last_pool = last_pool;
+ p->pool_offset = pool_offset;
+}
+
+void
+restore_varasm_status (p)
+ struct function *p;
+{
+ const_rtx_hash_table = p->const_rtx_hash_table;
+ const_rtx_sym_hash_table = p->const_rtx_sym_hash_table;
+ first_pool = p->first_pool;
+ last_pool = p->last_pool;
+ pool_offset = p->pool_offset;
+}
+
+enum kind { RTX_DOUBLE, RTX_INT };
+
+struct rtx_const
+{
+#ifdef ONLY_INT_FIELDS
+ unsigned int kind : 16;
+ unsigned int mode : 16;
+#else
+ enum kind kind : 16;
+ enum machine_mode mode : 16;
+#endif
+ union {
+ union real_extract du;
+ struct addr_const addr;
+ } un;
+};
+
+/* Express an rtx for a constant integer (perhaps symbolic)
+ as the sum of a symbol or label plus an explicit integer.
+ They are stored into VALUE. */
+
+static void
+decode_rtx_const (mode, x, value)
+ enum machine_mode mode;
+ rtx x;
+ struct rtx_const *value;
+{
+ /* Clear the whole structure, including any gaps. */
+
+ {
+ int *p = (int *) value;
+ int *end = (int *) (value + 1);
+ while (p < end)
+ *p++ = 0;
+ }
+
+ value->kind = RTX_INT; /* Most usual kind. */
+ value->mode = mode;
+
+ switch (GET_CODE (x))
+ {
+ case CONST_DOUBLE:
+ value->kind = RTX_DOUBLE;
+ if (GET_MODE (x) != VOIDmode)
+ value->mode = GET_MODE (x);
+ bcopy ((char *) &CONST_DOUBLE_LOW (x),
+ (char *) &value->un.du, sizeof value->un.du);
+ break;
+
+ case CONST_INT:
+ value->un.addr.offset = INTVAL (x);
+ break;
+
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case PC:
+ value->un.addr.base = x;
+ break;
+
+ case CONST:
+ x = XEXP (x, 0);
+ if (GET_CODE (x) == PLUS)
+ {
+ value->un.addr.base = XEXP (x, 0);
+ if (GET_CODE (XEXP (x, 1)) != CONST_INT)
+ abort ();
+ value->un.addr.offset = INTVAL (XEXP (x, 1));
+ }
+ else if (GET_CODE (x) == MINUS)
+ {
+ value->un.addr.base = XEXP (x, 0);
+ if (GET_CODE (XEXP (x, 1)) != CONST_INT)
+ abort ();
+ value->un.addr.offset = - INTVAL (XEXP (x, 1));
+ }
+ else
+ abort ();
+ break;
+
+ default:
+ abort ();
+ }
+
+ if (value->kind == RTX_INT && value->un.addr.base != 0)
+ switch (GET_CODE (value->un.addr.base))
+ {
+ case SYMBOL_REF:
+ case LABEL_REF:
+ /* Use the string's address, not the SYMBOL_REF's address,
+ for the sake of addresses of library routines.
+ For a LABEL_REF, compare labels. */
+ value->un.addr.base = XEXP (value->un.addr.base, 0);
+ }
+}
+
+/* Given a MINUS expression, simplify it if both sides
+ include the same symbol. */
+
+rtx
+simplify_subtraction (x)
+ rtx x;
+{
+ struct rtx_const val0, val1;
+
+ decode_rtx_const (GET_MODE (x), XEXP (x, 0), &val0);
+ decode_rtx_const (GET_MODE (x), XEXP (x, 1), &val1);
+
+ if (val0.un.addr.base == val1.un.addr.base)
+ return GEN_INT (val0.un.addr.offset - val1.un.addr.offset);
+ return x;
+}
+
+/* Compute a hash code for a constant RTL expression. */
+
+int
+const_hash_rtx (mode, x)
+ enum machine_mode mode;
+ rtx x;
+{
+ register int hi, i;
+
+ struct rtx_const value;
+ decode_rtx_const (mode, x, &value);
+
+ /* Compute hashing function */
+ hi = 0;
+ for (i = 0; i < sizeof value / sizeof (int); i++)
+ hi += ((int *) &value)[i];
+
+ hi &= (1 << HASHBITS) - 1;
+ hi %= MAX_RTX_HASH_TABLE;
+ return hi;
+}
+
+/* Compare a constant rtl object X with a constant-descriptor DESC.
+ Return 1 if DESC describes a constant with the same value as X. */
+
+static int
+compare_constant_rtx (mode, x, desc)
+ enum machine_mode mode;
+ rtx x;
+ struct constant_descriptor *desc;
+{
+ register int *p = (int *) desc->contents;
+ register int *strp;
+ register int len;
+ struct rtx_const value;
+
+ decode_rtx_const (mode, x, &value);
+ strp = (int *) &value;
+ len = sizeof value / sizeof (int);
+
+ /* Compare constant contents. */
+ while (--len >= 0)
+ if (*p++ != *strp++)
+ return 0;
+
+ return 1;
+}
+
+/* Construct a constant descriptor for the rtl-expression X.
+ It is up to the caller to enter the descriptor in the hash table. */
+
+static struct constant_descriptor *
+record_constant_rtx (mode, x)
+ enum machine_mode mode;
+ rtx x;
+{
+ struct constant_descriptor *ptr;
+ char *label;
+ struct rtx_const value;
+
+ decode_rtx_const (mode, x, &value);
+
+ /* Put these things in the saveable obstack so we can ensure it won't
+ be freed if we are called from combine or some other phase that discards
+ memory allocated from function_obstack (current_obstack). */
+ obstack_grow (saveable_obstack, &ptr, sizeof ptr);
+ obstack_grow (saveable_obstack, &label, sizeof label);
+
+ /* Record constant contents. */
+ obstack_grow (saveable_obstack, &value, sizeof value);
+
+ return (struct constant_descriptor *) obstack_finish (saveable_obstack);
+}
+
+/* Given a constant rtx X, make (or find) a memory constant for its value
+ and return a MEM rtx to refer to it in memory. */
+
+rtx
+force_const_mem (mode, x)
+ enum machine_mode mode;
+ rtx x;
+{
+ register int hash;
+ register struct constant_descriptor *desc;
+ char label[256];
+ char *found = 0;
+ rtx def;
+
+ /* If we want this CONST_DOUBLE in the same mode as it is in memory
+ (this will always be true for floating CONST_DOUBLEs that have been
+ placed in memory, but not for VOIDmode (integer) CONST_DOUBLEs),
+ use the previous copy. Otherwise, make a new one. Note that in
+ the unlikely event that this same CONST_DOUBLE is used in two different
+ modes in an alternating fashion, we will allocate a lot of different
+ memory locations, but this should be extremely rare. */
+
+ /* Don't use CONST_DOUBLE_MEM in a nested function.
+ Nested functions have their own constant pools,
+ so they can't share the same values in CONST_DOUBLE_MEM
+ with the containing function. */
+ if (outer_function_chain == 0)
+ if (GET_CODE (x) == CONST_DOUBLE
+ && GET_CODE (CONST_DOUBLE_MEM (x)) == MEM
+ && GET_MODE (CONST_DOUBLE_MEM (x)) == mode)
+ return CONST_DOUBLE_MEM (x);
+
+ /* Compute hash code of X. Search the descriptors for that hash code
+ to see if any of them describes X. If yes, the descriptor records
+ the label number already assigned. */
+
+ hash = const_hash_rtx (mode, x);
+
+ for (desc = const_rtx_hash_table[hash]; desc; desc = desc->next)
+ if (compare_constant_rtx (mode, x, desc))
+ {
+ found = desc->label;
+ break;
+ }
+
+ if (found == 0)
+ {
+ register struct pool_constant *pool;
+ register struct pool_sym *sym;
+ int align;
+
+ /* No constant equal to X is known to have been output.
+ Make a constant descriptor to enter X in the hash table.
+ Assign the label number and record it in the descriptor for
+ future calls to this function to find. */
+
+ desc = record_constant_rtx (mode, x);
+ desc->next = const_rtx_hash_table[hash];
+ const_rtx_hash_table[hash] = desc;
+
+ /* Align the location counter as required by EXP's data type. */
+ align = (mode == VOIDmode) ? UNITS_PER_WORD : GET_MODE_SIZE (mode);
+ if (align > BIGGEST_ALIGNMENT / BITS_PER_UNIT)
+ align = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
+
+ pool_offset += align - 1;
+ pool_offset &= ~ (align - 1);
+
+ /* If RTL is not being placed into the saveable obstack, make a
+ copy of X that is in the saveable obstack in case we are being
+ called from combine or some other phase that discards memory
+ it allocates. We need only do this if it is a CONST, since
+ no other RTX should be allocated in this situation. */
+ if (rtl_obstack != saveable_obstack
+ && GET_CODE (x) == CONST)
+ {
+ push_obstacks_nochange ();
+ rtl_in_saveable_obstack ();
+
+ x = gen_rtx (CONST, GET_MODE (x),
+ gen_rtx (PLUS, GET_MODE (x),
+ XEXP (XEXP (x, 0), 0), XEXP (XEXP (x, 0), 1)));
+ pop_obstacks ();
+ }
+
+ /* Allocate a pool constant descriptor, fill it in, and chain it in. */
+
+ pool = (struct pool_constant *) savealloc (sizeof (struct pool_constant));
+ pool->desc = desc;
+ pool->constant = x;
+ pool->mode = mode;
+ pool->labelno = const_labelno;
+ pool->align = align;
+ pool->offset = pool_offset;
+ pool->next = 0;
+
+ if (last_pool == 0)
+ first_pool = pool;
+ else
+ last_pool->next = pool;
+
+ last_pool = pool;
+ pool_offset += GET_MODE_SIZE (mode);
+
+ /* Create a string containing the label name, in LABEL. */
+ ASM_GENERATE_INTERNAL_LABEL (label, "LC", const_labelno);
+
+ ++const_labelno;
+
+ desc->label = found
+ = (char *) obstack_copy0 (saveable_obstack, label, strlen (label));
+
+ /* Add label to symbol hash table. */
+ hash = SYMHASH (found);
+ sym = (struct pool_sym *) savealloc (sizeof (struct pool_sym));
+ sym->label = found;
+ sym->pool = pool;
+ sym->next = const_rtx_sym_hash_table[hash];
+ const_rtx_sym_hash_table[hash] = sym;
+ }
+
+ /* We have a symbol name; construct the SYMBOL_REF and the MEM. */
+
+ def = gen_rtx (MEM, mode, gen_rtx (SYMBOL_REF, Pmode, found));
+
+ RTX_UNCHANGING_P (def) = 1;
+ /* Mark the symbol_ref as belonging to this constants pool. */
+ CONSTANT_POOL_ADDRESS_P (XEXP (def, 0)) = 1;
+ current_function_uses_const_pool = 1;
+
+ if (outer_function_chain == 0)
+ if (GET_CODE (x) == CONST_DOUBLE)
+ {
+ if (CONST_DOUBLE_MEM (x) == cc0_rtx)
+ {
+ CONST_DOUBLE_CHAIN (x) = const_double_chain;
+ const_double_chain = x;
+ }
+ CONST_DOUBLE_MEM (x) = def;
+ }
+
+ return def;
+}
+
+/* Given a SYMBOL_REF with CONSTANT_POOL_ADDRESS_P true, return a pointer to
+ the corresponding pool_constant structure. */
+
+static struct pool_constant *
+find_pool_constant (addr)
+ rtx addr;
+{
+ struct pool_sym *sym;
+ char *label = XSTR (addr, 0);
+
+ for (sym = const_rtx_sym_hash_table[SYMHASH (label)]; sym; sym = sym->next)
+ if (sym->label == label)
+ return sym->pool;
+
+ abort ();
+}
+
+/* Given a constant pool SYMBOL_REF, return the corresponding constant. */
+
+rtx
+get_pool_constant (addr)
+ rtx addr;
+{
+ return (find_pool_constant (addr))->constant;
+}
+
+/* Similar, return the mode. */
+
+enum machine_mode
+get_pool_mode (addr)
+ rtx addr;
+{
+ return (find_pool_constant (addr))->mode;
+}
+
+/* Similar, return the offset in the constant pool. */
+
+int
+get_pool_offset (addr)
+ rtx addr;
+{
+ return (find_pool_constant (addr))->offset;
+}
+
+/* Return the size of the constant pool. */
+
+int
+get_pool_size ()
+{
+ return pool_offset;
+}
+
+/* Write all the constants in the constant pool. */
+
+void
+output_constant_pool (fnname, fndecl)
+ char *fnname;
+ tree fndecl;
+{
+ struct pool_constant *pool;
+ rtx x;
+ union real_extract u;
+
+#ifdef ASM_OUTPUT_POOL_PROLOGUE
+ ASM_OUTPUT_POOL_PROLOGUE (asm_out_file, fnname, fndecl, pool_offset);
+#endif
+
+ for (pool = first_pool; pool; pool = pool->next)
+ {
+ x = pool->constant;
+
+ /* See if X is a LABEL_REF (or a CONST referring to a LABEL_REF)
+ whose CODE_LABEL has been deleted. This can occur if a jump table
+ is eliminated by optimization. If so, write a constant of zero
+ instead. Note that this can also happen by turning the
+ CODE_LABEL into a NOTE. */
+ if (((GET_CODE (x) == LABEL_REF
+ && (INSN_DELETED_P (XEXP (x, 0))
+ || GET_CODE (XEXP (x, 0)) == NOTE)))
+ || (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (x, 0), 0)) == LABEL_REF
+ && (INSN_DELETED_P (XEXP (XEXP (XEXP (x, 0), 0), 0))
+ || GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == NOTE)))
+ x = const0_rtx;
+
+ /* First switch to correct section. */
+#ifdef SELECT_RTX_SECTION
+ SELECT_RTX_SECTION (pool->mode, x);
+#else
+ readonly_data_section ();
+#endif
+
+#ifdef ASM_OUTPUT_SPECIAL_POOL_ENTRY
+ ASM_OUTPUT_SPECIAL_POOL_ENTRY (asm_out_file, x, pool->mode,
+ pool->align, pool->labelno, done);
+#endif
+
+ if (pool->align > 1)
+ ASM_OUTPUT_ALIGN (asm_out_file, exact_log2 (pool->align));
+
+ /* Output the label. */
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LC", pool->labelno);
+
+ /* Output the value of the constant itself. */
+ switch (GET_MODE_CLASS (pool->mode))
+ {
+ case MODE_FLOAT:
+ if (GET_CODE (x) != CONST_DOUBLE)
+ abort ();
+
+ bcopy ((char *) &CONST_DOUBLE_LOW (x), (char *) &u, sizeof u);
+ assemble_real (u.d, pool->mode);
+ break;
+
+ case MODE_INT:
+ case MODE_PARTIAL_INT:
+ assemble_integer (x, GET_MODE_SIZE (pool->mode), 1);
+ break;
+
+ default:
+ abort ();
+ }
+
+ done: ;
+ }
+
+ /* Done with this pool. */
+ first_pool = last_pool = 0;
+}
+
+/* Find all the constants whose addresses are referenced inside of EXP,
+ and make sure assembler code with a label has been output for each one.
+ Indicate whether an ADDR_EXPR has been encountered. */
+
+int
+output_addressed_constants (exp)
+ tree exp;
+{
+ int reloc = 0;
+
+ switch (TREE_CODE (exp))
+ {
+ case ADDR_EXPR:
+ {
+ register tree constant = TREE_OPERAND (exp, 0);
+
+ while (TREE_CODE (constant) == COMPONENT_REF)
+ {
+ constant = TREE_OPERAND (constant, 0);
+ }
+
+ if (TREE_CODE_CLASS (TREE_CODE (constant)) == 'c'
+ || TREE_CODE (constant) == CONSTRUCTOR)
+ /* No need to do anything here
+ for addresses of variables or functions. */
+ output_constant_def (constant);
+ }
+ reloc = 1;
+ break;
+
+ case PLUS_EXPR:
+ case MINUS_EXPR:
+ reloc = output_addressed_constants (TREE_OPERAND (exp, 0));
+ reloc |= output_addressed_constants (TREE_OPERAND (exp, 1));
+ break;
+
+ case NOP_EXPR:
+ case CONVERT_EXPR:
+ case NON_LVALUE_EXPR:
+ reloc = output_addressed_constants (TREE_OPERAND (exp, 0));
+ break;
+
+ case CONSTRUCTOR:
+ {
+ register tree link;
+ for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link))
+ if (TREE_VALUE (link) != 0)
+ reloc |= output_addressed_constants (TREE_VALUE (link));
+ }
+ break;
+
+ case ERROR_MARK:
+ break;
+ }
+ return reloc;
+}
+
+
+/* Output assembler for byte constant */
+void
+output_byte_asm (byte)
+ int byte;
+{
+ if (output_bytecode)
+ bc_emit_const ((char *) &byte, sizeof (char));
+#ifdef ASM_OUTPUT_BYTE
+ else
+ {
+ ASM_OUTPUT_BYTE (asm_out_file, byte);
+ }
+#endif
+}
+
+/* Output assembler code for constant EXP to FILE, with no label.
+ This includes the pseudo-op such as ".int" or ".byte", and a newline.
+ Assumes output_addressed_constants has been done on EXP already.
+
+ Generate exactly SIZE bytes of assembler data, padding at the end
+ with zeros if necessary. SIZE must always be specified.
+
+ SIZE is important for structure constructors,
+ since trailing members may have been omitted from the constructor.
+ It is also important for initialization of arrays from string constants
+ since the full length of the string constant might not be wanted.
+ It is also needed for initialization of unions, where the initializer's
+ type is just one member, and that may not be as long as the union.
+
+ There a case in which we would fail to output exactly SIZE bytes:
+ for a structure constructor that wants to produce more than SIZE bytes.
+ But such constructors will never be generated for any possible input. */
+
+void
+output_constant (exp, size)
+ register tree exp;
+ register int size;
+{
+ register enum tree_code code = TREE_CODE (TREE_TYPE (exp));
+ rtx x;
+
+ if (size == 0)
+ return;
+
+ /* Eliminate the NON_LVALUE_EXPR_EXPR that makes a cast not be an lvalue.
+ That way we get the constant (we hope) inside it. Also, strip
+ off any NOP_EXPR that converts between two record or union types. */
+ while ((TREE_CODE (exp) == NOP_EXPR
+ && (TREE_TYPE (exp) == TREE_TYPE (TREE_OPERAND (exp, 0))
+ || TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE
+ || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE
+ || TREE_CODE (TREE_TYPE (exp)) == QUAL_UNION_TYPE))
+ || TREE_CODE (exp) == NON_LVALUE_EXPR)
+ exp = TREE_OPERAND (exp, 0);
+
+ /* Allow a constructor with no elements for any data type.
+ This means to fill the space with zeros. */
+ if (TREE_CODE (exp) == CONSTRUCTOR && CONSTRUCTOR_ELTS (exp) == 0)
+ {
+ if (output_bytecode)
+ bc_emit_const_skip (size);
+ else
+ assemble_zeros (size);
+ return;
+ }
+
+ switch (code)
+ {
+ case CHAR_TYPE:
+ case BOOLEAN_TYPE:
+ case INTEGER_TYPE:
+ case ENUMERAL_TYPE:
+ case POINTER_TYPE:
+ case REFERENCE_TYPE:
+ /* ??? What about (int)((float)(int)&foo + 4) */
+ while (TREE_CODE (exp) == NOP_EXPR || TREE_CODE (exp) == CONVERT_EXPR
+ || TREE_CODE (exp) == NON_LVALUE_EXPR)
+ exp = TREE_OPERAND (exp, 0);
+
+ if (! assemble_integer (expand_expr (exp, NULL_RTX, VOIDmode,
+ EXPAND_INITIALIZER),
+ size, 0))
+ error ("initializer for integer value is too complicated");
+ size = 0;
+ break;
+
+ case REAL_TYPE:
+ if (TREE_CODE (exp) != REAL_CST)
+ error ("initializer for floating value is not a floating constant");
+
+ assemble_real (TREE_REAL_CST (exp),
+ mode_for_size (size * BITS_PER_UNIT, MODE_FLOAT, 0));
+ size = 0;
+ break;
+
+ case COMPLEX_TYPE:
+ output_constant (TREE_REALPART (exp), size / 2);
+ output_constant (TREE_IMAGPART (exp), size / 2);
+ size -= (size / 2) * 2;
+ break;
+
+ case ARRAY_TYPE:
+ if (TREE_CODE (exp) == CONSTRUCTOR)
+ {
+ output_constructor (exp, size);
+ return;
+ }
+ else if (TREE_CODE (exp) == STRING_CST)
+ {
+ int excess = 0;
+
+ if (size > TREE_STRING_LENGTH (exp))
+ {
+ excess = size - TREE_STRING_LENGTH (exp);
+ size = TREE_STRING_LENGTH (exp);
+ }
+
+ assemble_string (TREE_STRING_POINTER (exp), size);
+ size = excess;
+ }
+ else
+ abort ();
+ break;
+
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ if (TREE_CODE (exp) == CONSTRUCTOR)
+ output_constructor (exp, size);
+ else
+ abort ();
+ return;
+ }
+
+ if (size > 0)
+ assemble_zeros (size);
+}
+
+
+/* Bytecode specific code to output assembler for integer. */
+static void
+bc_assemble_integer (exp, size)
+ tree exp;
+ int size;
+{
+ tree const_part;
+ tree addr_part;
+ tree tmp;
+
+ /* FIXME: is this fold() business going to be as good as the
+ expand_expr() using EXPAND_SUM above in the RTL case? I
+ hate RMS.
+ FIXME: Copied as is from BC-GCC1; may need work. Don't hate. -bson */
+
+ exp = fold (exp);
+
+ while (TREE_CODE (exp) == NOP_EXPR || TREE_CODE (exp) == CONVERT_EXPR)
+ exp = TREE_OPERAND (exp, 0);
+ if (TREE_CODE (exp) == INTEGER_CST)
+ {
+ const_part = exp;
+ addr_part = 0;
+ }
+ else if (TREE_CODE (exp) == PLUS_EXPR)
+ {
+ const_part = TREE_OPERAND (exp, 0);
+ while (TREE_CODE (const_part) == NOP_EXPR
+ || TREE_CODE (const_part) == CONVERT_EXPR)
+ const_part = TREE_OPERAND (const_part, 0);
+ addr_part = TREE_OPERAND (exp, 1);
+ while (TREE_CODE (addr_part) == NOP_EXPR
+ || TREE_CODE (addr_part) == CONVERT_EXPR)
+ addr_part = TREE_OPERAND (addr_part, 0);
+ if (TREE_CODE (const_part) != INTEGER_CST)
+ tmp = const_part, const_part = addr_part, addr_part = tmp;
+ if (TREE_CODE (const_part) != INTEGER_CST
+ || TREE_CODE (addr_part) != ADDR_EXPR)
+ abort (); /* FIXME: we really haven't considered
+ all the possible cases here. */
+ }
+ else if (TREE_CODE (exp) == ADDR_EXPR)
+ {
+ const_part = integer_zero_node;
+ addr_part = exp;
+ }
+ else
+ abort (); /* FIXME: ditto previous. */
+
+ if (addr_part == 0)
+ {
+ if (size == 1)
+ {
+ char c = TREE_INT_CST_LOW (const_part);
+ bc_emit (&c, 1);
+ size -= 1;
+ }
+ else if (size == 2)
+ {
+ short s = TREE_INT_CST_LOW (const_part);
+ bc_emit ((char *) &s, 2);
+ size -= 2;
+ }
+ else if (size == 4)
+ {
+ int i = TREE_INT_CST_LOW (const_part);
+ bc_emit ((char *) &i, 4);
+ size -= 4;
+ }
+ else if (size == 8)
+ {
+#if WORDS_BIG_ENDIAN
+ int i = TREE_INT_CST_HIGH (const_part);
+ bc_emit ((char *) &i, 4);
+ i = TREE_INT_CST_LOW (const_part);
+ bc_emit ((char *) &i, 4);
+#else
+ int i = TREE_INT_CST_LOW (const_part);
+ bc_emit ((char *) &i, 4);
+ i = TREE_INT_CST_HIGH (const_part);
+ bc_emit ((char *) &i, 4);
+#endif
+ size -= 8;
+ }
+ }
+ else
+ if (size == 4
+ && TREE_CODE (TREE_OPERAND (addr_part, 0)) == VAR_DECL)
+ bc_emit_labelref (DECL_ASSEMBLER_NAME (TREE_OPERAND (addr_part, 0)),
+ TREE_INT_CST_LOW (const_part));
+ else
+ abort (); /* FIXME: there may be more cases. */
+}
+
+/* Subroutine of output_constant, used for CONSTRUCTORs
+ (aggregate constants).
+ Generate at least SIZE bytes, padding if necessary. */
+
+void
+output_constructor (exp, size)
+ tree exp;
+ int size;
+{
+ register tree link, field = 0;
+ HOST_WIDE_INT min_index = 0;
+ /* Number of bytes output or skipped so far.
+ In other words, current position within the constructor. */
+ int total_bytes = 0;
+ /* Non-zero means BYTE contains part of a byte, to be output. */
+ int byte_buffer_in_use = 0;
+ register int byte;
+
+ if (HOST_BITS_PER_WIDE_INT < BITS_PER_UNIT)
+ abort ();
+
+ if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE)
+ field = TYPE_FIELDS (TREE_TYPE (exp));
+
+ if (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE
+ && TYPE_DOMAIN (TREE_TYPE (exp)) != 0)
+ min_index
+ = TREE_INT_CST_LOW (TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (exp))));
+
+ /* As LINK goes through the elements of the constant,
+ FIELD goes through the structure fields, if the constant is a structure.
+ if the constant is a union, then we override this,
+ by getting the field from the TREE_LIST element.
+ But the constant could also be an array. Then FIELD is zero. */
+ for (link = CONSTRUCTOR_ELTS (exp);
+ link;
+ link = TREE_CHAIN (link),
+ field = field ? TREE_CHAIN (field) : 0)
+ {
+ tree val = TREE_VALUE (link);
+ tree index = 0;
+
+ /* the element in a union constructor specifies the proper field. */
+
+ if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE
+ || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE)
+ {
+ /* if available, use the type given by link */
+ if (TREE_PURPOSE (link) != 0)
+ field = TREE_PURPOSE (link);
+ }
+
+ if (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE)
+ index = TREE_PURPOSE (link);
+
+ /* Eliminate the marker that makes a cast not be an lvalue. */
+ if (val != 0)
+ STRIP_NOPS (val);
+
+ if (field == 0 || !DECL_BIT_FIELD (field))
+ {
+ /* An element that is not a bit-field. */
+
+ register int fieldsize;
+ /* Since this structure is static,
+ we know the positions are constant. */
+ int bitpos = (field ? (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field))
+ / BITS_PER_UNIT)
+ : 0);
+ if (index != 0)
+ bitpos = (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (val)))
+ / BITS_PER_UNIT
+ * (TREE_INT_CST_LOW (index) - min_index));
+
+ /* Output any buffered-up bit-fields preceding this element. */
+ if (byte_buffer_in_use)
+ {
+ ASM_OUTPUT_BYTE (asm_out_file, byte);
+ total_bytes++;
+ byte_buffer_in_use = 0;
+ }
+
+ /* Advance to offset of this element.
+ Note no alignment needed in an array, since that is guaranteed
+ if each element has the proper size. */
+ if ((field != 0 || index != 0) && bitpos != total_bytes)
+ {
+ if (!output_bytecode)
+ assemble_zeros (bitpos - total_bytes);
+ else
+ bc_emit_const_skip (bitpos - total_bytes);
+ total_bytes = bitpos;
+ }
+
+ /* Determine size this element should occupy. */
+ if (field)
+ {
+ if (TREE_CODE (DECL_SIZE (field)) != INTEGER_CST)
+ abort ();
+ if (TREE_INT_CST_LOW (DECL_SIZE (field)) > 100000)
+ {
+ /* This avoids overflow trouble. */
+ tree size_tree = size_binop (CEIL_DIV_EXPR,
+ DECL_SIZE (field),
+ size_int (BITS_PER_UNIT));
+ fieldsize = TREE_INT_CST_LOW (size_tree);
+ }
+ else
+ {
+ fieldsize = TREE_INT_CST_LOW (DECL_SIZE (field));
+ fieldsize = (fieldsize + BITS_PER_UNIT - 1) / BITS_PER_UNIT;
+ }
+ }
+ else
+ fieldsize = int_size_in_bytes (TREE_TYPE (TREE_TYPE (exp)));
+
+ /* Output the element's initial value. */
+ if (val == 0)
+ assemble_zeros (fieldsize);
+ else
+ output_constant (val, fieldsize);
+
+ /* Count its size. */
+ total_bytes += fieldsize;
+ }
+ else if (val != 0 && TREE_CODE (val) != INTEGER_CST)
+ error ("invalid initial value for member `%s'",
+ IDENTIFIER_POINTER (DECL_NAME (field)));
+ else
+ {
+ /* Element that is a bit-field. */
+
+ int next_offset = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field));
+ int end_offset
+ = (next_offset + TREE_INT_CST_LOW (DECL_SIZE (field)));
+
+ if (val == 0)
+ val = integer_zero_node;
+
+ /* If this field does not start in this (or, next) byte,
+ skip some bytes. */
+ if (next_offset / BITS_PER_UNIT != total_bytes)
+ {
+ /* Output remnant of any bit field in previous bytes. */
+ if (byte_buffer_in_use)
+ {
+ ASM_OUTPUT_BYTE (asm_out_file, byte);
+ total_bytes++;
+ byte_buffer_in_use = 0;
+ }
+
+ /* If still not at proper byte, advance to there. */
+ if (next_offset / BITS_PER_UNIT != total_bytes)
+ {
+ assemble_zeros (next_offset / BITS_PER_UNIT - total_bytes);
+ total_bytes = next_offset / BITS_PER_UNIT;
+ }
+ }
+
+ if (! byte_buffer_in_use)
+ byte = 0;
+
+ /* We must split the element into pieces that fall within
+ separate bytes, and combine each byte with previous or
+ following bit-fields. */
+
+ /* next_offset is the offset n fbits from the beginning of
+ the structure to the next bit of this element to be processed.
+ end_offset is the offset of the first bit past the end of
+ this element. */
+ while (next_offset < end_offset)
+ {
+ int this_time;
+ int shift, value;
+ int next_byte = next_offset / BITS_PER_UNIT;
+ int next_bit = next_offset % BITS_PER_UNIT;
+
+ /* Advance from byte to byte
+ within this element when necessary. */
+ while (next_byte != total_bytes)
+ {
+ ASM_OUTPUT_BYTE (asm_out_file, byte);
+ total_bytes++;
+ byte = 0;
+ }
+
+ /* Number of bits we can process at once
+ (all part of the same byte). */
+ this_time = MIN (end_offset - next_offset,
+ BITS_PER_UNIT - next_bit);
+#if BYTES_BIG_ENDIAN
+ /* On big-endian machine, take the most significant bits
+ first (of the bits that are significant)
+ and put them into bytes from the most significant end. */
+ shift = end_offset - next_offset - this_time;
+ /* Don't try to take a bunch of bits that cross
+ the word boundary in the INTEGER_CST. */
+ if (shift < HOST_BITS_PER_WIDE_INT
+ && shift + this_time > HOST_BITS_PER_WIDE_INT)
+ {
+ this_time -= (HOST_BITS_PER_WIDE_INT - shift);
+ shift = HOST_BITS_PER_WIDE_INT;
+ }
+
+ /* Now get the bits from the appropriate constant word. */
+ if (shift < HOST_BITS_PER_WIDE_INT)
+ {
+ value = TREE_INT_CST_LOW (val);
+ }
+ else if (shift < 2 * HOST_BITS_PER_WIDE_INT)
+ {
+ value = TREE_INT_CST_HIGH (val);
+ shift -= HOST_BITS_PER_WIDE_INT;
+ }
+ else
+ abort ();
+ byte |= (((value >> shift)
+ & (((HOST_WIDE_INT) 1 << this_time) - 1))
+ << (BITS_PER_UNIT - this_time - next_bit));
+#else
+ /* On little-endian machines,
+ take first the least significant bits of the value
+ and pack them starting at the least significant
+ bits of the bytes. */
+ shift = (next_offset
+ - TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field)));
+ /* Don't try to take a bunch of bits that cross
+ the word boundary in the INTEGER_CST. */
+ if (shift < HOST_BITS_PER_WIDE_INT
+ && shift + this_time > HOST_BITS_PER_WIDE_INT)
+ {
+ this_time -= (HOST_BITS_PER_WIDE_INT - shift);
+ shift = HOST_BITS_PER_WIDE_INT;
+ }
+
+ /* Now get the bits from the appropriate constant word. */
+ if (shift < HOST_BITS_PER_INT)
+ value = TREE_INT_CST_LOW (val);
+ else if (shift < 2 * HOST_BITS_PER_WIDE_INT)
+ {
+ value = TREE_INT_CST_HIGH (val);
+ shift -= HOST_BITS_PER_WIDE_INT;
+ }
+ else
+ abort ();
+ byte |= ((value >> shift)
+ & (((HOST_WIDE_INT) 1 << this_time) - 1)) << next_bit;
+#endif
+ next_offset += this_time;
+ byte_buffer_in_use = 1;
+ }
+ }
+ }
+ if (byte_buffer_in_use)
+ {
+ ASM_OUTPUT_BYTE (asm_out_file, byte);
+ total_bytes++;
+ }
+ if (total_bytes < size)
+ assemble_zeros (size - total_bytes);
+}
+
+
+#ifdef HANDLE_SYSV_PRAGMA
+
+/* Support #pragma weak by default if WEAK_ASM_OP and ASM_OUTPUT_DEF
+ are defined. */
+#if defined (WEAK_ASM_OP) && defined (ASM_OUTPUT_DEF)
+
+/* See c-pragma.c for an identical definition. */
+enum pragma_state
+{
+ ps_start,
+ ps_done,
+ ps_bad,
+ ps_weak,
+ ps_name,
+ ps_equals,
+ ps_value,
+ ps_pack,
+ ps_left,
+ ps_align,
+ ps_right
+};
+
+/* Output asm to handle ``#pragma weak'' */
+void
+handle_pragma_weak (what, asm_out_file, name, value)
+ enum pragma_state what;
+ FILE *asm_out_file;
+ char *name, *value;
+{
+ if (what == ps_name || what == ps_value)
+ {
+ fprintf (asm_out_file, "\t%s\t", WEAK_ASM_OP);
+
+ if (output_bytecode)
+ BC_OUTPUT_LABELREF (asm_out_file, name);
+ else
+ ASM_OUTPUT_LABELREF (asm_out_file, name);
+
+ fputc ('\n', asm_out_file);
+ if (what == ps_value)
+ ASM_OUTPUT_DEF (asm_out_file, name, value);
+ }
+ else if (! (what == ps_done || what == ps_start))
+ warning ("malformed `#pragma weak'");
+}
+
+#endif /* HANDLE_PRAGMA_WEAK or (WEAK_ASM_OP and SET_ASM_OP) */
+
+#endif /* WEAK_ASM_OP && ASM_OUTPUT_DEF */
diff --git a/gnu/usr.bin/cc/cc_int/version.c b/gnu/usr.bin/cc/cc_int/version.c
new file mode 100644
index 0000000..a77e9ff
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/version.c
@@ -0,0 +1 @@
+char *version_string = "2.6.0";
diff --git a/gnu/usr.bin/cc/cc_int/xcoffout.c b/gnu/usr.bin/cc/cc_int/xcoffout.c
new file mode 100644
index 0000000..42b01f9
--- /dev/null
+++ b/gnu/usr.bin/cc/cc_int/xcoffout.c
@@ -0,0 +1,536 @@
+/* Output xcoff-format symbol table information from GNU compiler.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* Output xcoff-format symbol table data. The main functionality is contained
+ in dbxout.c. This file implements the sdbout-like parts of the xcoff
+ interface. Many functions are very similar to their counterparts in
+ sdbout.c. */
+
+/* Include this first, because it may define MIN and MAX. */
+#include <stdio.h>
+
+#include "config.h"
+#include "tree.h"
+#include "rtl.h"
+#include "flags.h"
+
+#ifdef XCOFF_DEBUGGING_INFO
+
+/* This defines the C_* storage classes. */
+#include <dbxstclass.h>
+
+#include "xcoffout.h"
+
+#if defined (USG) || defined (NO_STAB_H)
+#include "gstab.h"
+#else
+#include <stab.h>
+
+/* This is a GNU extension we need to reference in this file. */
+#ifndef N_CATCH
+#define N_CATCH 0x54
+#endif
+#endif
+
+/* Line number of beginning of current function, minus one.
+ Negative means not in a function or not using xcoff. */
+
+int xcoff_begin_function_line = -1;
+
+/* Name of the current include file. */
+
+char *xcoff_current_include_file;
+
+/* Name of the current function file. This is the file the `.bf' is
+ emitted from. In case a line is emitted from a different file,
+ (by including that file of course), then the line number will be
+ absolute. */
+
+char *xcoff_current_function_file;
+
+/* Names of bss and data sections. These should be unique names for each
+ compilation unit. */
+
+char *xcoff_bss_section_name;
+char *xcoff_private_data_section_name;
+char *xcoff_read_only_section_name;
+
+/* Last source file name mentioned in a NOTE insn. */
+
+char *xcoff_lastfile;
+
+/* Macro definitions used below. */
+
+#define ABS_OR_RELATIVE_LINENO(LINENO) \
+ (xcoff_current_include_file ? (LINENO) : (LINENO) - xcoff_begin_function_line)
+
+/* Output source line numbers via ".line" rather than ".stabd". */
+#define ASM_OUTPUT_SOURCE_LINE(FILE,LINENUM) \
+ do { \
+ if (xcoff_begin_function_line >= 0) \
+ fprintf (FILE, "\t.line\t%d\n", ABS_OR_RELATIVE_LINENO (LINENUM)); \
+ } while (0)
+
+#define ASM_OUTPUT_LFB(FILE,LINENUM) \
+{ \
+ if (xcoff_begin_function_line == -1) \
+ { \
+ xcoff_begin_function_line = (LINENUM) - 1;\
+ fprintf (FILE, "\t.bf\t%d\n", (LINENUM)); \
+ } \
+ xcoff_current_function_file \
+ = (xcoff_current_include_file \
+ ? xcoff_current_include_file : main_input_filename); \
+}
+
+#define ASM_OUTPUT_LFE(FILE,LINENUM) \
+ do { \
+ fprintf (FILE, "\t.ef\t%d\n", (LINENUM)); \
+ xcoff_begin_function_line = -1; \
+ } while (0)
+
+#define ASM_OUTPUT_LBB(FILE,LINENUM,BLOCKNUM) \
+ fprintf (FILE, "\t.bb\t%d\n", ABS_OR_RELATIVE_LINENO (LINENUM))
+
+#define ASM_OUTPUT_LBE(FILE,LINENUM,BLOCKNUM) \
+ fprintf (FILE, "\t.eb\t%d\n", ABS_OR_RELATIVE_LINENO (LINENUM))
+
+/* Support routines for XCOFF debugging info. */
+
+/* Assign NUMBER as the stabx type number for the type described by NAME.
+ Search all decls in the list SYMS to find the type NAME. */
+
+static void
+assign_type_number (syms, name, number)
+ tree syms;
+ char *name;
+ int number;
+{
+ tree decl;
+
+ for (decl = syms; decl; decl = TREE_CHAIN (decl))
+ if (DECL_NAME (decl)
+ && strcmp (IDENTIFIER_POINTER (DECL_NAME (decl)), name) == 0)
+ {
+ TREE_ASM_WRITTEN (decl) = 1;
+ TYPE_SYMTAB_ADDRESS (TREE_TYPE (decl)) = number;
+ }
+}
+
+/* Setup gcc primitive types to use the XCOFF built-in type numbers where
+ possible. */
+
+void
+xcoff_output_standard_types (syms)
+ tree syms;
+{
+ /* Handle built-in C types here. */
+
+ assign_type_number (syms, "int", -1);
+ assign_type_number (syms, "char", -2);
+ assign_type_number (syms, "short int", -3);
+ assign_type_number (syms, "long int", -4);
+ assign_type_number (syms, "unsigned char", -5);
+ assign_type_number (syms, "signed char", -6);
+ assign_type_number (syms, "short unsigned int", -7);
+ assign_type_number (syms, "unsigned int", -8);
+ /* No such type "unsigned". */
+ assign_type_number (syms, "long unsigned int", -10);
+ assign_type_number (syms, "void", -11);
+ assign_type_number (syms, "float", -12);
+ assign_type_number (syms, "double", -13);
+ assign_type_number (syms, "long double", -14);
+ /* Pascal and Fortran types run from -15 to -29. */
+ /* No such type "wchar". */
+
+ /* "long long int", and "long long unsigned int", are not handled here,
+ because there are no predefined types that match them. */
+
+ /* ??? Should also handle built-in C++ and Obj-C types. There perhaps
+ aren't any that C doesn't already have. */
+}
+
+/* Print an error message for unrecognized stab codes. */
+
+#define UNKNOWN_STAB(STR) \
+ do { \
+ fprintf(stderr, "Error, unknown stab %s: : 0x%x\n", STR, stab); \
+ fflush (stderr); \
+ } while (0)
+
+/* Conversion routine from BSD stabs to AIX storage classes. */
+
+int
+stab_to_sclass (stab)
+ int stab;
+{
+ switch (stab)
+ {
+ case N_GSYM:
+ return C_GSYM;
+
+ case N_FNAME:
+ UNKNOWN_STAB ("N_FNAME");
+ abort();
+
+ case N_FUN:
+ return C_FUN;
+
+ case N_STSYM:
+ case N_LCSYM:
+ return C_STSYM;
+
+#ifdef N_MAIN
+ case N_MAIN:
+ UNKNOWN_STAB ("N_MAIN");
+ abort ();
+#endif
+
+ case N_RSYM:
+ return C_RSYM;
+
+ case N_SSYM:
+ UNKNOWN_STAB ("N_SSYM");
+ abort ();
+
+ case N_RPSYM:
+ return C_RPSYM;
+
+ case N_PSYM:
+ return C_PSYM;
+ case N_LSYM:
+ return C_LSYM;
+ case N_DECL:
+ return C_DECL;
+ case N_ENTRY:
+ return C_ENTRY;
+
+ case N_SO:
+ UNKNOWN_STAB ("N_SO");
+ abort ();
+
+ case N_SOL:
+ UNKNOWN_STAB ("N_SOL");
+ abort ();
+
+ case N_SLINE:
+ UNKNOWN_STAB ("N_SLINE");
+ abort ();
+
+#ifdef N_DSLINE
+ case N_DSLINE:
+ UNKNOWN_STAB ("N_DSLINE");
+ abort ();
+#endif
+
+#ifdef N_BSLINE
+ case N_BSLINE:
+ UNKNOWN_STAB ("N_BSLINE");
+ abort ();
+#endif
+#if 0
+ /* This has the same value as N_BSLINE. */
+ case N_BROWS:
+ UNKNOWN_STAB ("N_BROWS");
+ abort ();
+#endif
+
+#ifdef N_BINCL
+ case N_BINCL:
+ UNKNOWN_STAB ("N_BINCL");
+ abort ();
+#endif
+
+#ifdef N_EINCL
+ case N_EINCL:
+ UNKNOWN_STAB ("N_EINCL");
+ abort ();
+#endif
+
+#ifdef N_EXCL
+ case N_EXCL:
+ UNKNOWN_STAB ("N_EXCL");
+ abort ();
+#endif
+
+ case N_LBRAC:
+ UNKNOWN_STAB ("N_LBRAC");
+ abort ();
+
+ case N_RBRAC:
+ UNKNOWN_STAB ("N_RBRAC");
+ abort ();
+
+ case N_BCOMM:
+ return C_BCOMM;
+ case N_ECOMM:
+ return C_ECOMM;
+ case N_ECOML:
+ return C_ECOML;
+
+ case N_LENG:
+ UNKNOWN_STAB ("N_LENG");
+ abort ();
+
+ case N_PC:
+ UNKNOWN_STAB ("N_PC");
+ abort ();
+
+#ifdef N_M2C
+ case N_M2C:
+ UNKNOWN_STAB ("N_M2C");
+ abort ();
+#endif
+
+#ifdef N_SCOPE
+ case N_SCOPE:
+ UNKNOWN_STAB ("N_SCOPE");
+ abort ();
+#endif
+
+ case N_CATCH:
+ UNKNOWN_STAB ("N_CATCH");
+ abort ();
+
+ default:
+ UNKNOWN_STAB ("default");
+ abort ();
+ }
+}
+
+/* In XCOFF, we have to have this .bf before the function prologue.
+ Rely on the value of `dbx_begin_function_line' not to duplicate .bf. */
+
+void
+xcoffout_output_first_source_line (file, last_linenum)
+ FILE *file;
+ int last_linenum;
+{
+ ASM_OUTPUT_LFB (file, last_linenum);
+ dbxout_parms (DECL_ARGUMENTS (current_function_decl));
+ ASM_OUTPUT_SOURCE_LINE (file, last_linenum);
+}
+
+/* Output debugging info to FILE to switch to sourcefile FILENAME.
+ INLINE_P is true if this is from an inlined function. */
+
+void
+xcoffout_source_file (file, filename, inline_p)
+ FILE *file;
+ char *filename;
+ int inline_p;
+{
+ if (filename
+ && (xcoff_lastfile == 0 || strcmp (filename, xcoff_lastfile)
+ || (inline_p && ! xcoff_current_include_file)
+ || (! inline_p && xcoff_current_include_file)))
+ {
+ if (xcoff_current_include_file)
+ {
+ fprintf (file, "\t.ei\t");
+ output_quoted_string (file, xcoff_current_include_file);
+ fprintf (file, "\n");
+ xcoff_current_include_file = NULL;
+ }
+ if (strcmp (main_input_filename, filename) || inline_p)
+ {
+ fprintf (file, "\t.bi\t");
+ output_quoted_string (file, filename);
+ fprintf (file, "\n");
+ xcoff_current_include_file = filename;
+ }
+
+ xcoff_lastfile = filename;
+ }
+}
+
+/* Output a line number symbol entry into output stream FILE,
+ for source file FILENAME and line number NOTE. */
+
+void
+xcoffout_source_line (file, filename, note)
+ FILE *file;
+ char *filename;
+ rtx note;
+{
+ xcoffout_source_file (file, filename, RTX_INTEGRATED_P (note));
+
+ ASM_OUTPUT_SOURCE_LINE (file, NOTE_LINE_NUMBER (note));
+}
+
+/* Output the symbols defined in block number DO_BLOCK.
+ Set NEXT_BLOCK_NUMBER to 0 before calling.
+
+ This function works by walking the tree structure of blocks,
+ counting blocks until it finds the desired block. */
+
+static int do_block = 0;
+
+static int next_block_number;
+
+static void
+xcoffout_block (block, depth, args)
+ register tree block;
+ int depth;
+ tree args;
+{
+ while (block)
+ {
+ /* Ignore blocks never expanded or otherwise marked as real. */
+ if (TREE_USED (block))
+ {
+ /* When we reach the specified block, output its symbols. */
+ if (next_block_number == do_block)
+ {
+ /* Output the syms of the block. */
+ if (debug_info_level != DINFO_LEVEL_TERSE || depth == 0)
+ dbxout_syms (BLOCK_VARS (block));
+ if (args)
+ dbxout_reg_parms (args);
+
+ /* We are now done with the block. Don't go to inner blocks. */
+ return;
+ }
+ /* If we are past the specified block, stop the scan. */
+ else if (next_block_number >= do_block)
+ return;
+
+ next_block_number++;
+
+ /* Output the subblocks. */
+ xcoffout_block (BLOCK_SUBBLOCKS (block), depth + 1, NULL_TREE);
+ }
+ block = BLOCK_CHAIN (block);
+ }
+}
+
+/* Describe the beginning of an internal block within a function.
+ Also output descriptions of variables defined in this block.
+
+ N is the number of the block, by order of beginning, counting from 1,
+ and not counting the outermost (function top-level) block.
+ The blocks match the BLOCKs in DECL_INITIAL (current_function_decl),
+ if the count starts at 0 for the outermost one. */
+
+void
+xcoffout_begin_block (file, line, n)
+ FILE *file;
+ int line;
+ int n;
+{
+ tree decl = current_function_decl;
+
+
+ /* The IBM AIX compiler does not emit a .bb for the function level scope,
+ so we avoid it here also. */
+ if (n != 1)
+ ASM_OUTPUT_LBB (file, line, n);
+
+ do_block = n;
+ next_block_number = 0;
+ xcoffout_block (DECL_INITIAL (decl), 0, DECL_ARGUMENTS (decl));
+}
+
+/* Describe the end line-number of an internal block within a function. */
+
+void
+xcoffout_end_block (file, line, n)
+ FILE *file;
+ int line;
+ int n;
+{
+ if (n != 1)
+ ASM_OUTPUT_LBE (file, line, n);
+}
+
+/* Called at beginning of function (before prologue).
+ Declare function as needed for debugging. */
+
+void
+xcoffout_declare_function (file, decl, name)
+ FILE *file;
+ tree decl;
+ char *name;
+{
+ char *n = name;
+ int i;
+
+ for (i = 0; name[i]; ++i)
+ {
+ if (name[i] == '[')
+ {
+ n = (char *) alloca (i + 1);
+ strncpy (n, name, i);
+ n[i] = '\0';
+ break;
+ }
+ }
+
+ /* Any pending .bi or .ei must occur before the .function psuedo op.
+ Otherwise debuggers will think that the function is in the previous
+ file and/or at the wrong line number. */
+ xcoffout_source_file (file, DECL_SOURCE_FILE (decl), 0);
+ dbxout_symbol (decl, 0);
+ fprintf (file, "\t.function .%s,.%s,16,044,FE..%s-.%s\n", n, n, n, n);
+}
+
+/* Called at beginning of function body (after prologue).
+ Record the function's starting line number, so we can output
+ relative line numbers for the other lines.
+ Record the file name that this function is contained in. */
+
+void
+xcoffout_begin_function (file, last_linenum)
+ FILE *file;
+ int last_linenum;
+{
+ ASM_OUTPUT_LFB (file, last_linenum);
+}
+
+/* Called at end of function (before epilogue).
+ Describe end of outermost block. */
+
+void
+xcoffout_end_function (file, last_linenum)
+ FILE *file;
+ int last_linenum;
+{
+ ASM_OUTPUT_LFE (file, last_linenum);
+}
+
+/* Output xcoff info for the absolute end of a function.
+ Called after the epilogue is output. */
+
+void
+xcoffout_end_epilogue (file)
+ FILE *file;
+{
+ /* We need to pass the correct function size to .function, otherwise,
+ the xas assembler can't figure out the correct size for the function
+ aux entry. So, we emit a label after the last instruction which can
+ be used by the .function pseudo op to calculate the function size. */
+
+ char *fname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
+ if (*fname == '*')
+ ++fname;
+ fprintf (file, "FE..");
+ ASM_OUTPUT_LABEL (file, fname);
+}
+#endif /* XCOFF_DEBUGGING_INFO */
diff --git a/gnu/usr.bin/cc/cccp/Makefile b/gnu/usr.bin/cc/cccp/Makefile
new file mode 100644
index 0000000..b7e09c2
--- /dev/null
+++ b/gnu/usr.bin/cc/cccp/Makefile
@@ -0,0 +1,11 @@
+#
+# $FreeBSD$
+#
+
+PROG = cpp
+SRCS = cccp.c cexp.c
+BINDIR= /usr/libexec
+DPADD+= ${LIBCC_INT}
+LDADD+= -lcc_int
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/cc/cpp/Makefile b/gnu/usr.bin/cc/cpp/Makefile
new file mode 100644
index 0000000..b7e09c2
--- /dev/null
+++ b/gnu/usr.bin/cc/cpp/Makefile
@@ -0,0 +1,11 @@
+#
+# $FreeBSD$
+#
+
+PROG = cpp
+SRCS = cccp.c cexp.c
+BINDIR= /usr/libexec
+DPADD+= ${LIBCC_INT}
+LDADD+= -lcc_int
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/cc/cpp/cccp.c b/gnu/usr.bin/cc/cpp/cccp.c
new file mode 100644
index 0000000..640b36b
--- /dev/null
+++ b/gnu/usr.bin/cc/cpp/cccp.c
@@ -0,0 +1,9804 @@
+/* C Compatible Compiler Preprocessor (CCCP)
+ Copyright (C) 1986, 87, 89, 92, 93, 1994 Free Software Foundation, Inc.
+ Written by Paul Rubin, June 1986
+ Adapted to ANSI C, Richard Stallman, Jan 1987
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ In other words, you are welcome to use, share and improve this program.
+ You are forbidden to forbid anyone else to use, share and improve
+ what you give them. Help stamp out software-hoarding! */
+
+typedef unsigned char U_CHAR;
+
+#ifdef EMACS
+#define NO_SHORTNAMES
+#include "../src/config.h"
+#ifdef open
+#undef open
+#undef read
+#undef write
+#endif /* open */
+#endif /* EMACS */
+
+/* The macro EMACS is defined when cpp is distributed as part of Emacs,
+ for the sake of machines with limited C compilers. */
+#ifndef EMACS
+#include "config.h"
+#endif /* not EMACS */
+
+#ifndef STANDARD_INCLUDE_DIR
+#define STANDARD_INCLUDE_DIR "/usr/include"
+#endif
+
+#ifndef LOCAL_INCLUDE_DIR
+#define LOCAL_INCLUDE_DIR "/usr/local/include"
+#endif
+
+#if 0 /* We can't get ptrdiff_t, so I arranged not to need PTR_INT_TYPE. */
+#ifdef __STDC__
+#define PTR_INT_TYPE ptrdiff_t
+#else
+#define PTR_INT_TYPE long
+#endif
+#endif /* 0 */
+
+#include "pcp.h"
+
+#ifndef STDC_VALUE
+#define STDC_VALUE 1
+#endif
+
+/* By default, colon separates directories in a path. */
+#ifndef PATH_SEPARATOR
+#define PATH_SEPARATOR ':'
+#endif
+
+/* In case config.h defines these. */
+#undef bcopy
+#undef bzero
+#undef bcmp
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <signal.h>
+
+#ifndef VMS
+#ifndef USG
+#include <sys/time.h> /* for __DATE__ and __TIME__ */
+#include <sys/resource.h>
+#else
+#include <time.h>
+#include <fcntl.h>
+#endif /* USG */
+#endif /* not VMS */
+
+/* This defines "errno" properly for VMS, and gives us EACCES. */
+#include <errno.h>
+
+/* VMS-specific definitions */
+#ifdef VMS
+#include <time.h>
+#include <perror.h> /* This defines sys_errlist/sys_nerr properly */
+#include <descrip.h>
+#define O_RDONLY 0 /* Open arg for Read/Only */
+#define O_WRONLY 1 /* Open arg for Write/Only */
+#define read(fd,buf,size) VMS_read (fd,buf,size)
+#define write(fd,buf,size) VMS_write (fd,buf,size)
+#define open(fname,mode,prot) VMS_open (fname,mode,prot)
+#define fopen(fname,mode) VMS_fopen (fname,mode)
+#define freopen(fname,mode,ofile) VMS_freopen (fname,mode,ofile)
+#define strncat(dst,src,cnt) VMS_strncat (dst,src,cnt)
+static char * VMS_strncat ();
+static int VMS_read ();
+static int VMS_write ();
+static int VMS_open ();
+static FILE * VMS_fopen ();
+static FILE * VMS_freopen ();
+static void hack_vms_include_specification ();
+typedef struct { unsigned :16, :16, :16; } vms_ino_t;
+#define ino_t vms_ino_t
+#define INCLUDE_LEN_FUDGE 10 /* leave room for VMS syntax conversion */
+#ifdef __GNUC__
+#define BSTRING /* VMS/GCC supplies the bstring routines */
+#endif /* __GNUC__ */
+#endif /* VMS */
+
+extern char *index ();
+extern char *rindex ();
+
+#ifndef O_RDONLY
+#define O_RDONLY 0
+#endif
+
+#undef MIN
+#undef MAX
+#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
+#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
+
+/* Find the largest host integer type and set its size and type. */
+
+#ifndef HOST_BITS_PER_WIDE_INT
+
+#if HOST_BITS_PER_LONG > HOST_BITS_PER_INT
+#define HOST_BITS_PER_WIDE_INT HOST_BITS_PER_LONG
+#define HOST_WIDE_INT long
+#else
+#define HOST_BITS_PER_WIDE_INT HOST_BITS_PER_INT
+#define HOST_WIDE_INT int
+#endif
+
+#endif
+
+#ifndef S_ISREG
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+
+/* Define a generic NULL if one hasn't already been defined. */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef GENERIC_PTR
+#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
+#define GENERIC_PTR void *
+#else
+#define GENERIC_PTR char *
+#endif
+#endif
+
+#ifndef NULL_PTR
+#define NULL_PTR ((GENERIC_PTR)0)
+#endif
+
+#ifndef INCLUDE_LEN_FUDGE
+#define INCLUDE_LEN_FUDGE 0
+#endif
+
+/* Forward declarations. */
+
+char *xmalloc ();
+void error ();
+void warning ();
+
+/* External declarations. */
+
+extern char *getenv ();
+extern FILE *fdopen ();
+extern char *version_string;
+extern struct tm *localtime ();
+extern int sys_nerr;
+#if defined(bsd4_4) || defined(__NetBSD__)
+extern const char *const sys_errlist[];
+#else
+extern char *sys_errlist[];
+#endif
+extern int parse_escape ();
+
+#ifndef errno
+extern int errno;
+#endif
+
+/* Forward declarations. */
+
+struct directive;
+struct file_buf;
+struct arglist;
+struct argdata;
+
+#if defined(USG) || defined(VMS)
+#ifndef BSTRING
+void bcopy ();
+void bzero ();
+int bcmp ();
+#endif
+#endif
+
+/* These functions are declared to return int instead of void since they
+ are going to be placed in a table and some old compilers have trouble with
+ pointers to functions returning void. */
+
+static int do_define ();
+static int do_line ();
+static int do_include ();
+static int do_undef ();
+static int do_error ();
+static int do_pragma ();
+static int do_ident ();
+static int do_if ();
+static int do_xifdef ();
+static int do_else ();
+static int do_elif ();
+static int do_endif ();
+static int do_sccs ();
+static int do_once ();
+static int do_assert ();
+static int do_unassert ();
+static int do_warning ();
+
+static void add_import ();
+static void append_include_chain ();
+static void deps_output ();
+static void make_undef ();
+static void make_definition ();
+static void make_assertion ();
+static void path_include ();
+static void initialize_builtins ();
+static void initialize_char_syntax ();
+static void dump_arg_n ();
+static void dump_defn_1 ();
+static void delete_macro ();
+static void trigraph_pcp ();
+static void rescan ();
+static void finclude ();
+static void validate_else ();
+static int comp_def_part ();
+static void error_from_errno ();
+static void error_with_line ();
+void pedwarn ();
+void pedwarn_with_line ();
+static void pedwarn_with_file_and_line ();
+static void fatal ();
+void fancy_abort ();
+static void pfatal_with_name ();
+static void perror_with_name ();
+static void pipe_closed ();
+static void print_containing_files ();
+static int lookup_import ();
+static int redundant_include_p ();
+static is_system_include ();
+static struct file_name_map *read_name_map ();
+static char *read_filename_string ();
+static int open_include_file ();
+static int check_preconditions ();
+static void pcfinclude ();
+static void pcstring_used ();
+static void write_output ();
+static int check_macro_name ();
+static int compare_defs ();
+static int compare_token_lists ();
+static int eval_if_expression ();
+static int discard_comments ();
+static int change_newlines ();
+static int line_for_error ();
+static int hashf ();
+static int file_size_and_mode ();
+
+static struct arglist *read_token_list ();
+static void free_token_list ();
+
+static struct hashnode *install ();
+struct hashnode *lookup ();
+
+static struct assertion_hashnode *assertion_install ();
+static struct assertion_hashnode *assertion_lookup ();
+
+static char *xrealloc ();
+static char *xcalloc ();
+static char *savestring ();
+
+static void delete_assertion ();
+static void macroexpand ();
+static void dump_all_macros ();
+static void conditional_skip ();
+static void skip_if_group ();
+static void output_line_command ();
+
+/* Last arg to output_line_command. */
+enum file_change_code {same_file, enter_file, leave_file};
+
+static int grow_outbuf ();
+static int handle_directive ();
+static void memory_full ();
+
+static U_CHAR *macarg1 ();
+static char *macarg ();
+
+static U_CHAR *skip_to_end_of_comment ();
+static U_CHAR *skip_quoted_string ();
+static U_CHAR *skip_paren_group ();
+static char *quote_string ();
+
+static char *check_precompiled ();
+/* static struct macrodef create_definition (); [moved below] */
+static void dump_single_macro ();
+static void output_dots ();
+
+#ifndef FAILURE_EXIT_CODE
+#define FAILURE_EXIT_CODE 33 /* gnu cc command understands this */
+#endif
+
+#ifndef SUCCESS_EXIT_CODE
+#define SUCCESS_EXIT_CODE 0 /* 0 means success on Unix. */
+#endif
+
+/* Name under which this program was invoked. */
+
+static char *progname;
+
+/* Nonzero means use extra default include directories for C++. */
+
+static int cplusplus;
+
+/* Nonzero means handle cplusplus style comments */
+
+static int cplusplus_comments;
+
+/* Nonzero means handle #import, for objective C. */
+
+static int objc;
+
+/* Nonzero means this is an assembly file, and allow
+ unknown directives, which could be comments. */
+
+static int lang_asm;
+
+/* Current maximum length of directory names in the search path
+ for include files. (Altered as we get more of them.) */
+
+static int max_include_len;
+
+/* Nonzero means turn NOTREACHED into #pragma NOTREACHED etc */
+
+static int for_lint = 0;
+
+/* Nonzero means copy comments into the output file. */
+
+static int put_out_comments = 0;
+
+/* Nonzero means don't process the ANSI trigraph sequences. */
+
+static int no_trigraphs = 0;
+
+/* Nonzero means print the names of included files rather than
+ the preprocessed output. 1 means just the #include "...",
+ 2 means #include <...> as well. */
+
+static int print_deps = 0;
+
+/* Nonzero if missing .h files in -M output are assumed to be generated
+ files and not errors. */
+
+static int print_deps_missing_files = 0;
+
+/* Nonzero means print names of header files (-H). */
+
+static int print_include_names = 0;
+
+/* Nonzero means don't output line number information. */
+
+static int no_line_commands;
+
+/* dump_only means inhibit output of the preprocessed text
+ and instead output the definitions of all user-defined
+ macros in a form suitable for use as input to cccp.
+ dump_names means pass #define and the macro name through to output.
+ dump_definitions means pass the whole definition (plus #define) through
+*/
+
+static enum {dump_none, dump_only, dump_names, dump_definitions}
+ dump_macros = dump_none;
+
+/* Nonzero means pass all #define and #undef directives which we actually
+ process through to the output stream. This feature is used primarily
+ to allow cc1 to record the #defines and #undefs for the sake of
+ debuggers which understand about preprocessor macros, but it may
+ also be useful with -E to figure out how symbols are defined, and
+ where they are defined. */
+static int debug_output = 0;
+
+/* Nonzero indicates special processing used by the pcp program. The
+ special effects of this mode are:
+
+ Inhibit all macro expansion, except those inside #if directives.
+
+ Process #define directives normally, and output their contents
+ to the output file.
+
+ Output preconditions to pcp_outfile indicating all the relevant
+ preconditions for use of this file in a later cpp run.
+*/
+static FILE *pcp_outfile;
+
+/* Nonzero means we are inside an IF during a -pcp run. In this mode
+ macro expansion is done, and preconditions are output for all macro
+ uses requiring them. */
+static int pcp_inside_if;
+
+/* Nonzero means never to include precompiled files.
+ This is 1 since there's no way now to make precompiled files,
+ so it's not worth testing for them. */
+static int no_precomp = 1;
+
+/* Nonzero means give all the error messages the ANSI standard requires. */
+
+int pedantic;
+
+/* Nonzero means try to make failure to fit ANSI C an error. */
+
+static int pedantic_errors;
+
+/* Nonzero means don't print warning messages. -w. */
+
+static int inhibit_warnings = 0;
+
+/* Nonzero means warn if slash-star appears in a comment. */
+
+static int warn_comments;
+
+/* Nonzero means warn if a macro argument is (or would be)
+ stringified with -traditional. */
+
+static int warn_stringify;
+
+/* Nonzero means warn if there are any trigraphs. */
+
+static int warn_trigraphs;
+
+/* Nonzero means warn if #import is used. */
+
+static int warn_import = 1;
+
+/* Nonzero means turn warnings into errors. */
+
+static int warnings_are_errors;
+
+/* Nonzero means try to imitate old fashioned non-ANSI preprocessor. */
+
+int traditional;
+
+/* Nonzero causes output not to be done,
+ but directives such as #define that have side effects
+ are still obeyed. */
+
+static int no_output;
+
+/* Nonzero means this file was included with a -imacros or -include
+ command line and should not be recorded as an include file. */
+
+static int no_record_file;
+
+/* Nonzero means that we have finished processing the command line options.
+ This flag is used to decide whether or not to issue certain errors
+ and/or warnings. */
+
+static int done_initializing = 0;
+
+/* Line where a newline was first seen in a string constant. */
+
+static int multiline_string_line = 0;
+
+/* I/O buffer structure.
+ The `fname' field is nonzero for source files and #include files
+ and for the dummy text used for -D and -U.
+ It is zero for rescanning results of macro expansion
+ and for expanding macro arguments. */
+#define INPUT_STACK_MAX 400
+static struct file_buf {
+ char *fname;
+ /* Filename specified with #line command. */
+ char *nominal_fname;
+ /* Record where in the search path this file was found.
+ For #include_next. */
+ struct file_name_list *dir;
+ int lineno;
+ int length;
+ U_CHAR *buf;
+ U_CHAR *bufp;
+ /* Macro that this level is the expansion of.
+ Included so that we can reenable the macro
+ at the end of this level. */
+ struct hashnode *macro;
+ /* Value of if_stack at start of this file.
+ Used to prohibit unmatched #endif (etc) in an include file. */
+ struct if_stack *if_stack;
+ /* Object to be freed at end of input at this level. */
+ U_CHAR *free_ptr;
+ /* True if this is a header file included using <FILENAME>. */
+ char system_header_p;
+} instack[INPUT_STACK_MAX];
+
+static int last_error_tick; /* Incremented each time we print it. */
+static int input_file_stack_tick; /* Incremented when the status changes. */
+
+/* Current nesting level of input sources.
+ `instack[indepth]' is the level currently being read. */
+static int indepth = -1;
+#define CHECK_DEPTH(code) \
+ if (indepth >= (INPUT_STACK_MAX - 1)) \
+ { \
+ error_with_line (line_for_error (instack[indepth].lineno), \
+ "macro or `#include' recursion too deep"); \
+ code; \
+ }
+
+/* Current depth in #include directives that use <...>. */
+static int system_include_depth = 0;
+
+typedef struct file_buf FILE_BUF;
+
+/* The output buffer. Its LENGTH field is the amount of room allocated
+ for the buffer, not the number of chars actually present. To get
+ that, subtract outbuf.buf from outbuf.bufp. */
+
+#define OUTBUF_SIZE 10 /* initial size of output buffer */
+static FILE_BUF outbuf;
+
+/* Grow output buffer OBUF points at
+ so it can hold at least NEEDED more chars. */
+
+#define check_expand(OBUF, NEEDED) \
+ (((OBUF)->length - ((OBUF)->bufp - (OBUF)->buf) <= (NEEDED)) \
+ ? grow_outbuf ((OBUF), (NEEDED)) : 0)
+
+struct file_name_list
+ {
+ struct file_name_list *next;
+ char *fname;
+ /* If the following is nonzero, it is a macro name.
+ Don't include the file again if that macro is defined. */
+ U_CHAR *control_macro;
+ /* If the following is nonzero, it is a C-language system include
+ directory. */
+ int c_system_include_path;
+ /* Mapping of file names for this directory. */
+ struct file_name_map *name_map;
+ /* Non-zero if name_map is valid. */
+ int got_name_map;
+ };
+
+/* #include "file" looks in source file dir, then stack. */
+/* #include <file> just looks in the stack. */
+/* -I directories are added to the end, then the defaults are added. */
+/* The */
+static struct default_include {
+ char *fname; /* The name of the directory. */
+ int cplusplus; /* Only look here if we're compiling C++. */
+ int cxx_aware; /* Includes in this directory don't need to
+ be wrapped in extern "C" when compiling
+ C++. */
+} include_defaults_array[]
+#ifdef INCLUDE_DEFAULTS
+ = INCLUDE_DEFAULTS;
+#else
+ = {
+ /* Pick up GNU C++ specific include files. */
+ { GPLUSPLUS_INCLUDE_DIR, 1, 1 },
+#ifdef CROSS_COMPILE
+ /* This is the dir for fixincludes. Put it just before
+ the files that we fix. */
+ { GCC_INCLUDE_DIR, 0, 0 },
+ /* For cross-compilation, this dir name is generated
+ automatically in Makefile.in. */
+ { CROSS_INCLUDE_DIR, 0, 0 },
+ /* This is another place that the target system's headers might be. */
+ { TOOL_INCLUDE_DIR, 0, 1 },
+#else /* not CROSS_COMPILE */
+ /* This should be /usr/local/include and should come before
+ the fixincludes-fixed header files. */
+ { LOCAL_INCLUDE_DIR, 0, 1 },
+ /* This is here ahead of GCC_INCLUDE_DIR because assert.h goes here.
+ Likewise, behind LOCAL_INCLUDE_DIR, where glibc puts its assert.h. */
+ { TOOL_INCLUDE_DIR, 0, 1 },
+ /* This is the dir for fixincludes. Put it just before
+ the files that we fix. */
+ { GCC_INCLUDE_DIR, 0, 0 },
+ /* Some systems have an extra dir of include files. */
+#ifdef SYSTEM_INCLUDE_DIR
+ { SYSTEM_INCLUDE_DIR, 0, 0 },
+#endif
+ { STANDARD_INCLUDE_DIR, 0, 0 },
+#endif /* not CROSS_COMPILE */
+ { 0, 0, 0 }
+ };
+#endif /* no INCLUDE_DEFAULTS */
+
+/* The code looks at the defaults through this pointer, rather than through
+ the constant structure above. This pointer gets changed if an environment
+ variable specifies other defaults. */
+static struct default_include *include_defaults = include_defaults_array;
+
+static struct file_name_list *include = 0; /* First dir to search */
+ /* First dir to search for <file> */
+/* This is the first element to use for #include <...>.
+ If it is 0, use the entire chain for such includes. */
+static struct file_name_list *first_bracket_include = 0;
+/* This is the first element in the chain that corresponds to
+ a directory of system header files. */
+static struct file_name_list *first_system_include = 0;
+static struct file_name_list *last_include = 0; /* Last in chain */
+
+/* Chain of include directories to put at the end of the other chain. */
+static struct file_name_list *after_include = 0;
+static struct file_name_list *last_after_include = 0; /* Last in chain */
+
+/* Chain to put at the start of the system include files. */
+static struct file_name_list *before_system = 0;
+static struct file_name_list *last_before_system = 0; /* Last in chain */
+
+/* List of included files that contained #pragma once. */
+static struct file_name_list *dont_repeat_files = 0;
+
+/* List of other included files.
+ If ->control_macro if nonzero, the file had a #ifndef
+ around the entire contents, and ->control_macro gives the macro name. */
+static struct file_name_list *all_include_files = 0;
+
+/* Directory prefix that should replace `/usr' in the standard
+ include file directories. */
+static char *include_prefix;
+
+/* Global list of strings read in from precompiled files. This list
+ is kept in the order the strings are read in, with new strings being
+ added at the end through stringlist_tailp. We use this list to output
+ the strings at the end of the run.
+*/
+static STRINGDEF *stringlist;
+static STRINGDEF **stringlist_tailp = &stringlist;
+
+
+/* Structure returned by create_definition */
+typedef struct macrodef MACRODEF;
+struct macrodef
+{
+ struct definition *defn;
+ U_CHAR *symnam;
+ int symlen;
+};
+
+static struct macrodef create_definition ();
+
+
+/* Structure allocated for every #define. For a simple replacement
+ such as
+ #define foo bar ,
+ nargs = -1, the `pattern' list is null, and the expansion is just
+ the replacement text. Nargs = 0 means a functionlike macro with no args,
+ e.g.,
+ #define getchar() getc (stdin) .
+ When there are args, the expansion is the replacement text with the
+ args squashed out, and the reflist is a list describing how to
+ build the output from the input: e.g., "3 chars, then the 1st arg,
+ then 9 chars, then the 3rd arg, then 0 chars, then the 2nd arg".
+ The chars here come from the expansion. Whatever is left of the
+ expansion after the last arg-occurrence is copied after that arg.
+ Note that the reflist can be arbitrarily long---
+ its length depends on the number of times the arguments appear in
+ the replacement text, not how many args there are. Example:
+ #define f(x) x+x+x+x+x+x+x would have replacement text "++++++" and
+ pattern list
+ { (0, 1), (1, 1), (1, 1), ..., (1, 1), NULL }
+ where (x, y) means (nchars, argno). */
+
+typedef struct definition DEFINITION;
+struct definition {
+ int nargs;
+ int length; /* length of expansion string */
+ int predefined; /* True if the macro was builtin or */
+ /* came from the command line */
+ U_CHAR *expansion;
+ int line; /* Line number of definition */
+ char *file; /* File of definition */
+ char rest_args; /* Nonzero if last arg. absorbs the rest */
+ struct reflist {
+ struct reflist *next;
+ char stringify; /* nonzero if this arg was preceded by a
+ # operator. */
+ char raw_before; /* Nonzero if a ## operator before arg. */
+ char raw_after; /* Nonzero if a ## operator after arg. */
+ char rest_args; /* Nonzero if this arg. absorbs the rest */
+ int nchars; /* Number of literal chars to copy before
+ this arg occurrence. */
+ int argno; /* Number of arg to substitute (origin-0) */
+ } *pattern;
+ union {
+ /* Names of macro args, concatenated in reverse order
+ with comma-space between them.
+ The only use of this is that we warn on redefinition
+ if this differs between the old and new definitions. */
+ U_CHAR *argnames;
+ } args;
+};
+
+/* different kinds of things that can appear in the value field
+ of a hash node. Actually, this may be useless now. */
+union hashval {
+ int ival;
+ char *cpval;
+ DEFINITION *defn;
+ KEYDEF *keydef;
+};
+
+/*
+ * special extension string that can be added to the last macro argument to
+ * allow it to absorb the "rest" of the arguments when expanded. Ex:
+ * #define wow(a, b...) process (b, a, b)
+ * { wow (1, 2, 3); } -> { process (2, 3, 1, 2, 3); }
+ * { wow (one, two); } -> { process (two, one, two); }
+ * if this "rest_arg" is used with the concat token '##' and if it is not
+ * supplied then the token attached to with ## will not be outputted. Ex:
+ * #define wow (a, b...) process (b ## , a, ## b)
+ * { wow (1, 2); } -> { process (2, 1, 2); }
+ * { wow (one); } -> { process (one); {
+ */
+static char rest_extension[] = "...";
+#define REST_EXTENSION_LENGTH (sizeof (rest_extension) - 1)
+
+/* The structure of a node in the hash table. The hash table
+ has entries for all tokens defined by #define commands (type T_MACRO),
+ plus some special tokens like __LINE__ (these each have their own
+ type, and the appropriate code is run when that type of node is seen.
+ It does not contain control words like "#define", which are recognized
+ by a separate piece of code. */
+
+/* different flavors of hash nodes --- also used in keyword table */
+enum node_type {
+ T_DEFINE = 1, /* the `#define' keyword */
+ T_INCLUDE, /* the `#include' keyword */
+ T_INCLUDE_NEXT, /* the `#include_next' keyword */
+ T_IMPORT, /* the `#import' keyword */
+ T_IFDEF, /* the `#ifdef' keyword */
+ T_IFNDEF, /* the `#ifndef' keyword */
+ T_IF, /* the `#if' keyword */
+ T_ELSE, /* `#else' */
+ T_PRAGMA, /* `#pragma' */
+ T_ELIF, /* `#elif' */
+ T_UNDEF, /* `#undef' */
+ T_LINE, /* `#line' */
+ T_ERROR, /* `#error' */
+ T_WARNING, /* `#warning' */
+ T_ENDIF, /* `#endif' */
+ T_SCCS, /* `#sccs', used on system V. */
+ T_IDENT, /* `#ident', used on system V. */
+ T_ASSERT, /* `#assert', taken from system V. */
+ T_UNASSERT, /* `#unassert', taken from system V. */
+ T_SPECLINE, /* special symbol `__LINE__' */
+ T_DATE, /* `__DATE__' */
+ T_FILE, /* `__FILE__' */
+ T_BASE_FILE, /* `__BASE_FILE__' */
+ T_INCLUDE_LEVEL, /* `__INCLUDE_LEVEL__' */
+ T_VERSION, /* `__VERSION__' */
+ T_SIZE_TYPE, /* `__SIZE_TYPE__' */
+ T_PTRDIFF_TYPE, /* `__PTRDIFF_TYPE__' */
+ T_WCHAR_TYPE, /* `__WCHAR_TYPE__' */
+ T_USER_LABEL_PREFIX_TYPE, /* `__USER_LABEL_PREFIX__' */
+ T_REGISTER_PREFIX_TYPE, /* `__REGISTER_PREFIX__' */
+ T_TIME, /* `__TIME__' */
+ T_CONST, /* Constant value, used by `__STDC__' */
+ T_MACRO, /* macro defined by `#define' */
+ T_DISABLED, /* macro temporarily turned off for rescan */
+ T_SPEC_DEFINED, /* special `defined' macro for use in #if statements */
+ T_PCSTRING, /* precompiled string (hashval is KEYDEF *) */
+ T_UNUSED /* Used for something not defined. */
+ };
+
+struct hashnode {
+ struct hashnode *next; /* double links for easy deletion */
+ struct hashnode *prev;
+ struct hashnode **bucket_hdr; /* also, a back pointer to this node's hash
+ chain is kept, in case the node is the head
+ of the chain and gets deleted. */
+ enum node_type type; /* type of special token */
+ int length; /* length of token, for quick comparison */
+ U_CHAR *name; /* the actual name */
+ union hashval value; /* pointer to expansion, or whatever */
+};
+
+typedef struct hashnode HASHNODE;
+
+/* Some definitions for the hash table. The hash function MUST be
+ computed as shown in hashf () below. That is because the rescan
+ loop computes the hash value `on the fly' for most tokens,
+ in order to avoid the overhead of a lot of procedure calls to
+ the hashf () function. Hashf () only exists for the sake of
+ politeness, for use when speed isn't so important. */
+
+#define HASHSIZE 1403
+static HASHNODE *hashtab[HASHSIZE];
+#define HASHSTEP(old, c) ((old << 2) + c)
+#define MAKE_POS(v) (v & 0x7fffffff) /* make number positive */
+
+/* Symbols to predefine. */
+
+#ifdef CPP_PREDEFINES
+static char *predefs = CPP_PREDEFINES;
+#else
+static char *predefs = "";
+#endif
+
+/* We let tm.h override the types used here, to handle trivial differences
+ such as the choice of unsigned int or long unsigned int for size_t.
+ When machines start needing nontrivial differences in the size type,
+ it would be best to do something here to figure out automatically
+ from other information what type to use. */
+
+/* The string value for __SIZE_TYPE__. */
+
+#ifndef SIZE_TYPE
+#define SIZE_TYPE "long unsigned int"
+#endif
+
+/* The string value for __PTRDIFF_TYPE__. */
+
+#ifndef PTRDIFF_TYPE
+#define PTRDIFF_TYPE "long int"
+#endif
+
+/* The string value for __WCHAR_TYPE__. */
+
+#ifndef WCHAR_TYPE
+#define WCHAR_TYPE "int"
+#endif
+char * wchar_type = WCHAR_TYPE;
+#undef WCHAR_TYPE
+
+/* The string value for __USER_LABEL_PREFIX__ */
+
+#ifndef USER_LABEL_PREFIX
+#define USER_LABEL_PREFIX ""
+#endif
+
+/* The string value for __REGISTER_PREFIX__ */
+
+#ifndef REGISTER_PREFIX
+#define REGISTER_PREFIX ""
+#endif
+
+/* In the definition of a #assert name, this structure forms
+ a list of the individual values asserted.
+ Each value is itself a list of "tokens".
+ These are strings that are compared by name. */
+
+struct tokenlist_list {
+ struct tokenlist_list *next;
+ struct arglist *tokens;
+};
+
+struct assertion_hashnode {
+ struct assertion_hashnode *next; /* double links for easy deletion */
+ struct assertion_hashnode *prev;
+ /* also, a back pointer to this node's hash
+ chain is kept, in case the node is the head
+ of the chain and gets deleted. */
+ struct assertion_hashnode **bucket_hdr;
+ int length; /* length of token, for quick comparison */
+ U_CHAR *name; /* the actual name */
+ /* List of token-sequences. */
+ struct tokenlist_list *value;
+};
+
+typedef struct assertion_hashnode ASSERTION_HASHNODE;
+
+/* Some definitions for the hash table. The hash function MUST be
+ computed as shown in hashf below. That is because the rescan
+ loop computes the hash value `on the fly' for most tokens,
+ in order to avoid the overhead of a lot of procedure calls to
+ the hashf function. hashf only exists for the sake of
+ politeness, for use when speed isn't so important. */
+
+#define ASSERTION_HASHSIZE 37
+static ASSERTION_HASHNODE *assertion_hashtab[ASSERTION_HASHSIZE];
+
+/* Nonzero means inhibit macroexpansion of what seem to be
+ assertion tests, in rescan. For #if. */
+static int assertions_flag;
+
+/* `struct directive' defines one #-directive, including how to handle it. */
+
+struct directive {
+ int length; /* Length of name */
+ int (*func)(); /* Function to handle directive */
+ char *name; /* Name of directive */
+ enum node_type type; /* Code which describes which directive. */
+ char angle_brackets; /* Nonzero => <...> is special. */
+ char traditional_comments; /* Nonzero: keep comments if -traditional. */
+ char pass_thru; /* Copy preprocessed directive to output file. */
+};
+
+/* Here is the actual list of #-directives, most-often-used first. */
+
+static struct directive directive_table[] = {
+ { 6, do_define, "define", T_DEFINE, 0, 1},
+ { 2, do_if, "if", T_IF},
+ { 5, do_xifdef, "ifdef", T_IFDEF},
+ { 6, do_xifdef, "ifndef", T_IFNDEF},
+ { 5, do_endif, "endif", T_ENDIF},
+ { 4, do_else, "else", T_ELSE},
+ { 4, do_elif, "elif", T_ELIF},
+ { 4, do_line, "line", T_LINE},
+ { 7, do_include, "include", T_INCLUDE, 1},
+ { 12, do_include, "include_next", T_INCLUDE_NEXT, 1},
+ { 6, do_include, "import", T_IMPORT, 1},
+ { 5, do_undef, "undef", T_UNDEF},
+ { 5, do_error, "error", T_ERROR},
+ { 7, do_warning, "warning", T_WARNING},
+#ifdef SCCS_DIRECTIVE
+ { 4, do_sccs, "sccs", T_SCCS},
+#endif
+ { 6, do_pragma, "pragma", T_PRAGMA, 0, 0, 1},
+ { 5, do_ident, "ident", T_IDENT},
+ { 6, do_assert, "assert", T_ASSERT},
+ { 8, do_unassert, "unassert", T_UNASSERT},
+ { -1, 0, "", T_UNUSED},
+};
+
+/* When a directive handler is called,
+ this points to the # that started the directive. */
+U_CHAR *directive_start;
+
+/* table to tell if char can be part of a C identifier. */
+U_CHAR is_idchar[256];
+/* table to tell if char can be first char of a c identifier. */
+U_CHAR is_idstart[256];
+/* table to tell if c is horizontal space. */
+U_CHAR is_hor_space[256];
+/* table to tell if c is horizontal or vertical space. */
+static U_CHAR is_space[256];
+
+#define SKIP_WHITE_SPACE(p) do { while (is_hor_space[*p]) p++; } while (0)
+#define SKIP_ALL_WHITE_SPACE(p) do { while (is_space[*p]) p++; } while (0)
+
+static int errors = 0; /* Error counter for exit code */
+
+/* Name of output file, for error messages. */
+static char *out_fname;
+
+/* Zero means dollar signs are punctuation.
+ -$ stores 0; -traditional may store 1. Default is 1 for VMS, 0 otherwise.
+ This must be 0 for correct processing of this ANSI C program:
+ #define foo(a) #a
+ #define lose(b) foo (b)
+ #define test$
+ lose (test) */
+static int dollars_in_ident;
+#ifndef DOLLARS_IN_IDENTIFIERS
+#define DOLLARS_IN_IDENTIFIERS 1
+#endif
+
+static FILE_BUF expand_to_temp_buffer ();
+
+static DEFINITION *collect_expansion ();
+
+/* Stack of conditionals currently in progress
+ (including both successful and failing conditionals). */
+
+struct if_stack {
+ struct if_stack *next; /* for chaining to the next stack frame */
+ char *fname; /* copied from input when frame is made */
+ int lineno; /* similarly */
+ int if_succeeded; /* true if a leg of this if-group
+ has been passed through rescan */
+ U_CHAR *control_macro; /* For #ifndef at start of file,
+ this is the macro name tested. */
+ enum node_type type; /* type of last directive seen in this group */
+};
+typedef struct if_stack IF_STACK_FRAME;
+static IF_STACK_FRAME *if_stack = NULL;
+
+/* Buffer of -M output. */
+static char *deps_buffer;
+
+/* Number of bytes allocated in above. */
+static int deps_allocated_size;
+
+/* Number of bytes used. */
+static int deps_size;
+
+/* Number of bytes since the last newline. */
+static int deps_column;
+
+/* Nonzero means -I- has been seen,
+ so don't look for #include "foo" the source-file directory. */
+static int ignore_srcdir;
+
+/* Read LEN bytes at PTR from descriptor DESC, for file FILENAME,
+ retrying if necessary. Return a negative value if an error occurs,
+ otherwise return the actual number of bytes read,
+ which must be LEN unless end-of-file was reached. */
+
+static int
+safe_read (desc, ptr, len)
+ int desc;
+ char *ptr;
+ int len;
+{
+ int left = len;
+ while (left > 0) {
+ int nchars = read (desc, ptr, left);
+ if (nchars < 0)
+ {
+#ifdef EINTR
+ if (errno == EINTR)
+ continue;
+#endif
+ return nchars;
+ }
+ if (nchars == 0)
+ break;
+ ptr += nchars;
+ left -= nchars;
+ }
+ return len - left;
+}
+
+/* Write LEN bytes at PTR to descriptor DESC,
+ retrying if necessary, and treating any real error as fatal. */
+
+static void
+safe_write (desc, ptr, len)
+ int desc;
+ char *ptr;
+ int len;
+{
+ while (len > 0) {
+ int written = write (desc, ptr, len);
+ if (written < 0)
+ {
+#ifdef EINTR
+ if (errno == EINTR)
+ continue;
+#endif
+ pfatal_with_name (out_fname);
+ }
+ ptr += written;
+ len -= written;
+ }
+}
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int st_mode;
+ long st_size;
+ char *in_fname;
+ char *p;
+ int f, i;
+ FILE_BUF *fp;
+ char **pend_files = (char **) xmalloc (argc * sizeof (char *));
+ char **pend_defs = (char **) xmalloc (argc * sizeof (char *));
+ char **pend_undefs = (char **) xmalloc (argc * sizeof (char *));
+ char **pend_assertions = (char **) xmalloc (argc * sizeof (char *));
+ char **pend_includes = (char **) xmalloc (argc * sizeof (char *));
+
+ /* Record the option used with each element of pend_assertions.
+ This is preparation for supporting more than one option for making
+ an assertion. */
+ char **pend_assertion_options = (char **) xmalloc (argc * sizeof (char *));
+ int inhibit_predefs = 0;
+ int no_standard_includes = 0;
+ int no_standard_cplusplus_includes = 0;
+ int missing_newline = 0;
+
+ /* Non-0 means don't output the preprocessed program. */
+ int inhibit_output = 0;
+ /* Non-0 means -v, so print the full set of include dirs. */
+ int verbose = 0;
+
+ /* File name which deps are being written to.
+ This is 0 if deps are being written to stdout. */
+ char *deps_file = 0;
+ /* Fopen file mode to open deps_file with. */
+ char *deps_mode = "a";
+ /* Stream on which to print the dependency information. */
+ FILE *deps_stream = 0;
+ /* Target-name to write with the dependency information. */
+ char *deps_target = 0;
+
+#ifdef RLIMIT_STACK
+ /* Get rid of any avoidable limit on stack size. */
+ {
+ struct rlimit rlim;
+
+ /* Set the stack limit huge so that alloca (particularly stringtab
+ * in dbxread.c) does not fail. */
+ getrlimit (RLIMIT_STACK, &rlim);
+ rlim.rlim_cur = rlim.rlim_max;
+ setrlimit (RLIMIT_STACK, &rlim);
+ }
+#endif /* RLIMIT_STACK defined */
+
+#ifdef SIGPIPE
+ signal (SIGPIPE, pipe_closed);
+#endif
+
+ p = argv[0] + strlen (argv[0]);
+ while (p != argv[0] && p[-1] != '/') --p;
+ progname = p;
+
+#ifdef VMS
+ {
+ /* Remove directories from PROGNAME. */
+ char *s;
+
+ progname = savestring (argv[0]);
+
+ if (!(s = rindex (progname, ']')))
+ s = rindex (progname, ':');
+ if (s)
+ strcpy (progname, s+1);
+ if (s = rindex (progname, '.'))
+ *s = '\0';
+ }
+#endif
+
+ in_fname = NULL;
+ out_fname = NULL;
+
+ /* Initialize is_idchar to allow $. */
+ dollars_in_ident = 1;
+ initialize_char_syntax ();
+ dollars_in_ident = DOLLARS_IN_IDENTIFIERS > 0;
+
+ no_line_commands = 0;
+ no_trigraphs = 1;
+ dump_macros = dump_none;
+ no_output = 0;
+ cplusplus = 0;
+ cplusplus_comments = 0;
+
+ bzero ((char *) pend_files, argc * sizeof (char *));
+ bzero ((char *) pend_defs, argc * sizeof (char *));
+ bzero ((char *) pend_undefs, argc * sizeof (char *));
+ bzero ((char *) pend_assertions, argc * sizeof (char *));
+ bzero ((char *) pend_includes, argc * sizeof (char *));
+
+ /* Process switches and find input file name. */
+
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] != '-') {
+ if (out_fname != NULL)
+ fatal ("Usage: %s [switches] input output", argv[0]);
+ else if (in_fname != NULL)
+ out_fname = argv[i];
+ else
+ in_fname = argv[i];
+ } else {
+ switch (argv[i][1]) {
+
+ case 'i':
+ if (!strcmp (argv[i], "-include")) {
+ if (i + 1 == argc)
+ fatal ("Filename missing after `-include' option");
+ else
+ pend_includes[i] = argv[i+1], i++;
+ }
+ if (!strcmp (argv[i], "-imacros")) {
+ if (i + 1 == argc)
+ fatal ("Filename missing after `-imacros' option");
+ else
+ pend_files[i] = argv[i+1], i++;
+ }
+ if (!strcmp (argv[i], "-iprefix")) {
+ if (i + 1 == argc)
+ fatal ("Filename missing after `-iprefix' option");
+ else
+ include_prefix = argv[++i];
+ }
+ if (!strcmp (argv[i], "-isystem")) {
+ struct file_name_list *dirtmp;
+
+ if (i + 1 == argc)
+ fatal ("Filename missing after `-isystem' option");
+
+ dirtmp = (struct file_name_list *)
+ xmalloc (sizeof (struct file_name_list));
+ dirtmp->next = 0;
+ dirtmp->control_macro = 0;
+ dirtmp->c_system_include_path = 1;
+ dirtmp->fname = (char *) xmalloc (strlen (argv[i+1]) + 1);
+ strcpy (dirtmp->fname, argv[++i]);
+ dirtmp->got_name_map = 0;
+
+ if (before_system == 0)
+ before_system = dirtmp;
+ else
+ last_before_system->next = dirtmp;
+ last_before_system = dirtmp; /* Tail follows the last one */
+ }
+ /* Add directory to end of path for includes,
+ with the default prefix at the front of its name. */
+ if (!strcmp (argv[i], "-iwithprefix")) {
+ struct file_name_list *dirtmp;
+ char *prefix;
+
+ if (include_prefix != 0)
+ prefix = include_prefix;
+ else {
+ prefix = savestring (GCC_INCLUDE_DIR);
+ /* Remove the `include' from /usr/local/lib/gcc.../include. */
+ if (!strcmp (prefix + strlen (prefix) - 8, "/include"))
+ prefix[strlen (prefix) - 7] = 0;
+ }
+
+ dirtmp = (struct file_name_list *)
+ xmalloc (sizeof (struct file_name_list));
+ dirtmp->next = 0; /* New one goes on the end */
+ dirtmp->control_macro = 0;
+ dirtmp->c_system_include_path = 0;
+ if (i + 1 == argc)
+ fatal ("Directory name missing after `-iwithprefix' option");
+
+ dirtmp->fname = (char *) xmalloc (strlen (argv[i+1])
+ + strlen (prefix) + 1);
+ strcpy (dirtmp->fname, prefix);
+ strcat (dirtmp->fname, argv[++i]);
+ dirtmp->got_name_map = 0;
+
+ if (after_include == 0)
+ after_include = dirtmp;
+ else
+ last_after_include->next = dirtmp;
+ last_after_include = dirtmp; /* Tail follows the last one */
+ }
+ /* Add directory to main path for includes,
+ with the default prefix at the front of its name. */
+ if (!strcmp (argv[i], "-iwithprefixbefore")) {
+ struct file_name_list *dirtmp;
+ char *prefix;
+
+ if (include_prefix != 0)
+ prefix = include_prefix;
+ else {
+ prefix = savestring (GCC_INCLUDE_DIR);
+ /* Remove the `include' from /usr/local/lib/gcc.../include. */
+ if (!strcmp (prefix + strlen (prefix) - 8, "/include"))
+ prefix[strlen (prefix) - 7] = 0;
+ }
+
+ dirtmp = (struct file_name_list *)
+ xmalloc (sizeof (struct file_name_list));
+ dirtmp->next = 0; /* New one goes on the end */
+ dirtmp->control_macro = 0;
+ dirtmp->c_system_include_path = 0;
+ if (i + 1 == argc)
+ fatal ("Directory name missing after `-iwithprefixbefore' option");
+
+ dirtmp->fname = (char *) xmalloc (strlen (argv[i+1])
+ + strlen (prefix) + 1);
+ strcpy (dirtmp->fname, prefix);
+ strcat (dirtmp->fname, argv[++i]);
+ dirtmp->got_name_map = 0;
+
+ append_include_chain (dirtmp, dirtmp);
+ }
+ /* Add directory to end of path for includes. */
+ if (!strcmp (argv[i], "-idirafter")) {
+ struct file_name_list *dirtmp;
+
+ dirtmp = (struct file_name_list *)
+ xmalloc (sizeof (struct file_name_list));
+ dirtmp->next = 0; /* New one goes on the end */
+ dirtmp->control_macro = 0;
+ dirtmp->c_system_include_path = 0;
+ if (i + 1 == argc)
+ fatal ("Directory name missing after `-idirafter' option");
+ else
+ dirtmp->fname = argv[++i];
+ dirtmp->got_name_map = 0;
+
+ if (after_include == 0)
+ after_include = dirtmp;
+ else
+ last_after_include->next = dirtmp;
+ last_after_include = dirtmp; /* Tail follows the last one */
+ }
+ break;
+
+ case 'o':
+ if (out_fname != NULL)
+ fatal ("Output filename specified twice");
+ if (i + 1 == argc)
+ fatal ("Filename missing after -o option");
+ out_fname = argv[++i];
+ if (!strcmp (out_fname, "-"))
+ out_fname = "";
+ break;
+
+ case 'p':
+ if (!strcmp (argv[i], "-pedantic"))
+ pedantic = 1;
+ else if (!strcmp (argv[i], "-pedantic-errors")) {
+ pedantic = 1;
+ pedantic_errors = 1;
+ } else if (!strcmp (argv[i], "-pcp")) {
+ char *pcp_fname = argv[++i];
+ pcp_outfile =
+ ((pcp_fname[0] != '-' || pcp_fname[1] != '\0')
+ ? fopen (pcp_fname, "w")
+ : fdopen (dup (fileno (stdout)), "w"));
+ if (pcp_outfile == 0)
+ pfatal_with_name (pcp_fname);
+ no_precomp = 1;
+ }
+ break;
+
+ case 't':
+ if (!strcmp (argv[i], "-traditional")) {
+ traditional = 1;
+ if (dollars_in_ident > 0)
+ dollars_in_ident = 1;
+ } else if (!strcmp (argv[i], "-trigraphs")) {
+ no_trigraphs = 0;
+ }
+ break;
+
+ case 'l':
+ if (! strcmp (argv[i], "-lang-c"))
+ cplusplus = 0, cplusplus_comments = 0, objc = 0;
+ if (! strcmp (argv[i], "-lang-c++"))
+ cplusplus = 1, cplusplus_comments = 1, objc = 0;
+ if (! strcmp (argv[i], "-lang-c-c++-comments"))
+ cplusplus = 0, cplusplus_comments = 1, objc = 0;
+ if (! strcmp (argv[i], "-lang-objc"))
+ objc = 1, cplusplus = 0, cplusplus_comments = 1;
+ if (! strcmp (argv[i], "-lang-objc++"))
+ objc = 1, cplusplus = 1, cplusplus_comments = 1;
+ if (! strcmp (argv[i], "-lang-asm"))
+ lang_asm = 1;
+ if (! strcmp (argv[i], "-lint"))
+ for_lint = 1;
+ break;
+
+ case '+':
+ cplusplus = 1, cplusplus_comments = 1;
+ break;
+
+ case 'w':
+ inhibit_warnings = 1;
+ break;
+
+ case 'W':
+ if (!strcmp (argv[i], "-Wtrigraphs"))
+ warn_trigraphs = 1;
+ else if (!strcmp (argv[i], "-Wno-trigraphs"))
+ warn_trigraphs = 0;
+ else if (!strcmp (argv[i], "-Wcomment"))
+ warn_comments = 1;
+ else if (!strcmp (argv[i], "-Wno-comment"))
+ warn_comments = 0;
+ else if (!strcmp (argv[i], "-Wcomments"))
+ warn_comments = 1;
+ else if (!strcmp (argv[i], "-Wno-comments"))
+ warn_comments = 0;
+ else if (!strcmp (argv[i], "-Wtraditional"))
+ warn_stringify = 1;
+ else if (!strcmp (argv[i], "-Wno-traditional"))
+ warn_stringify = 0;
+ else if (!strcmp (argv[i], "-Wimport"))
+ warn_import = 1;
+ else if (!strcmp (argv[i], "-Wno-import"))
+ warn_import = 0;
+ else if (!strcmp (argv[i], "-Werror"))
+ warnings_are_errors = 1;
+ else if (!strcmp (argv[i], "-Wno-error"))
+ warnings_are_errors = 0;
+ else if (!strcmp (argv[i], "-Wall"))
+ {
+ warn_trigraphs = 1;
+ warn_comments = 1;
+ }
+ break;
+
+ case 'M':
+ /* The style of the choices here is a bit mixed.
+ The chosen scheme is a hybrid of keeping all options in one string
+ and specifying each option in a separate argument:
+ -M|-MM|-MD file|-MMD file [-MG]. An alternative is:
+ -M|-MM|-MD file|-MMD file|-MG|-MMG; or more concisely:
+ -M[M][G][D file]. This is awkward to handle in specs, and is not
+ as extensible. */
+ /* ??? -MG must be specified in addition to one of -M or -MM.
+ This can be relaxed in the future without breaking anything.
+ The converse isn't true. */
+
+ /* -MG isn't valid with -MD or -MMD. This is checked for later. */
+ if (!strcmp (argv[i], "-MG"))
+ {
+ print_deps_missing_files = 1;
+ break;
+ }
+ if (!strcmp (argv[i], "-M"))
+ print_deps = 2;
+ else if (!strcmp (argv[i], "-MM"))
+ print_deps = 1;
+ else if (!strcmp (argv[i], "-MD"))
+ print_deps = 2;
+ else if (!strcmp (argv[i], "-MMD"))
+ print_deps = 1;
+ /* For -MD and -MMD options, write deps on file named by next arg. */
+ if (!strcmp (argv[i], "-MD")
+ || !strcmp (argv[i], "-MMD")) {
+ i++;
+ deps_file = argv[i];
+ deps_mode = "w";
+ } else {
+ /* For -M and -MM, write deps on standard output
+ and suppress the usual output. */
+ deps_stream = stdout;
+ inhibit_output = 1;
+ }
+ break;
+
+ case 'd':
+ {
+ char *p = argv[i] + 2;
+ char c;
+ while (c = *p++) {
+ /* Arg to -d specifies what parts of macros to dump */
+ switch (c) {
+ case 'M':
+ dump_macros = dump_only;
+ no_output = 1;
+ break;
+ case 'N':
+ dump_macros = dump_names;
+ break;
+ case 'D':
+ dump_macros = dump_definitions;
+ break;
+ }
+ }
+ }
+ break;
+
+ case 'g':
+ if (argv[i][2] == '3')
+ debug_output = 1;
+ break;
+
+ case 'v':
+ fprintf (stderr, "GNU CPP version %s", version_string);
+#ifdef TARGET_VERSION
+ TARGET_VERSION;
+#endif
+ fprintf (stderr, "\n");
+ verbose = 1;
+ break;
+
+ case 'H':
+ print_include_names = 1;
+ break;
+
+ case 'D':
+ if (argv[i][2] != 0)
+ pend_defs[i] = argv[i] + 2;
+ else if (i + 1 == argc)
+ fatal ("Macro name missing after -D option");
+ else
+ i++, pend_defs[i] = argv[i];
+ break;
+
+ case 'A':
+ {
+ char *p;
+
+ if (argv[i][2] != 0)
+ p = argv[i] + 2;
+ else if (i + 1 == argc)
+ fatal ("Assertion missing after -A option");
+ else
+ p = argv[++i];
+
+ if (!strcmp (p, "-")) {
+ /* -A- eliminates all predefined macros and assertions.
+ Let's include also any that were specified earlier
+ on the command line. That way we can get rid of any
+ that were passed automatically in from GCC. */
+ int j;
+ inhibit_predefs = 1;
+ for (j = 0; j < i; j++)
+ pend_defs[j] = pend_assertions[j] = 0;
+ } else {
+ pend_assertions[i] = p;
+ pend_assertion_options[i] = "-A";
+ }
+ }
+ break;
+
+ case 'U': /* JF #undef something */
+ if (argv[i][2] != 0)
+ pend_undefs[i] = argv[i] + 2;
+ else if (i + 1 == argc)
+ fatal ("Macro name missing after -U option");
+ else
+ pend_undefs[i] = argv[i+1], i++;
+ break;
+
+ case 'C':
+ put_out_comments = 1;
+ break;
+
+ case 'E': /* -E comes from cc -E; ignore it. */
+ break;
+
+ case 'P':
+ no_line_commands = 1;
+ break;
+
+ case '$': /* Don't include $ in identifiers. */
+ dollars_in_ident = 0;
+ break;
+
+ case 'I': /* Add directory to path for includes. */
+ {
+ struct file_name_list *dirtmp;
+
+ if (! ignore_srcdir && !strcmp (argv[i] + 2, "-")) {
+ ignore_srcdir = 1;
+ /* Don't use any preceding -I directories for #include <...>. */
+ first_bracket_include = 0;
+ }
+ else {
+ dirtmp = (struct file_name_list *)
+ xmalloc (sizeof (struct file_name_list));
+ dirtmp->next = 0; /* New one goes on the end */
+ dirtmp->control_macro = 0;
+ dirtmp->c_system_include_path = 0;
+ if (argv[i][2] != 0)
+ dirtmp->fname = argv[i] + 2;
+ else if (i + 1 == argc)
+ fatal ("Directory name missing after -I option");
+ else
+ dirtmp->fname = argv[++i];
+ dirtmp->got_name_map = 0;
+ append_include_chain (dirtmp, dirtmp);
+ }
+ }
+ break;
+
+ case 'n':
+ if (!strcmp (argv[i], "-nostdinc"))
+ /* -nostdinc causes no default include directories.
+ You must specify all include-file directories with -I. */
+ no_standard_includes = 1;
+ else if (!strcmp (argv[i], "-nostdinc++"))
+ /* -nostdinc++ causes no default C++-specific include directories. */
+ no_standard_cplusplus_includes = 1;
+ else if (!strcmp (argv[i], "-noprecomp"))
+ no_precomp = 1;
+ break;
+
+ case 'u':
+ /* Sun compiler passes undocumented switch "-undef".
+ Let's assume it means to inhibit the predefined symbols. */
+ inhibit_predefs = 1;
+ break;
+
+ case '\0': /* JF handle '-' as file name meaning stdin or stdout */
+ if (in_fname == NULL) {
+ in_fname = "";
+ break;
+ } else if (out_fname == NULL) {
+ out_fname = "";
+ break;
+ } /* else fall through into error */
+
+ default:
+ fatal ("Invalid option `%s'", argv[i]);
+ }
+ }
+ }
+
+ /* Add dirs from CPATH after dirs from -I. */
+ /* There seems to be confusion about what CPATH should do,
+ so for the moment it is not documented. */
+ /* Some people say that CPATH should replace the standard include dirs,
+ but that seems pointless: it comes before them, so it overrides them
+ anyway. */
+ p = (char *) getenv ("CPATH");
+ if (p != 0 && ! no_standard_includes)
+ path_include (p);
+
+ /* Now that dollars_in_ident is known, initialize is_idchar. */
+ initialize_char_syntax ();
+
+ /* Initialize output buffer */
+
+ outbuf.buf = (U_CHAR *) xmalloc (OUTBUF_SIZE);
+ outbuf.bufp = outbuf.buf;
+ outbuf.length = OUTBUF_SIZE;
+
+ /* Do partial setup of input buffer for the sake of generating
+ early #line directives (when -g is in effect). */
+
+ fp = &instack[++indepth];
+ if (in_fname == NULL)
+ in_fname = "";
+ fp->nominal_fname = fp->fname = in_fname;
+ fp->lineno = 0;
+
+ /* In C++, wchar_t is a distinct basic type, and we can expect
+ __wchar_t to be defined by cc1plus. */
+ if (cplusplus)
+ wchar_type = "__wchar_t";
+
+ /* Install __LINE__, etc. Must follow initialize_char_syntax
+ and option processing. */
+ initialize_builtins (fp, &outbuf);
+
+ /* Do standard #defines and assertions
+ that identify system and machine type. */
+
+ if (!inhibit_predefs) {
+ char *p = (char *) alloca (strlen (predefs) + 1);
+ strcpy (p, predefs);
+ while (*p) {
+ char *q;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ /* Handle -D options. */
+ if (p[0] == '-' && p[1] == 'D') {
+ q = &p[2];
+ while (*p && *p != ' ' && *p != '\t')
+ p++;
+ if (*p != 0)
+ *p++= 0;
+ if (debug_output)
+ output_line_command (fp, &outbuf, 0, same_file);
+ make_definition (q, &outbuf);
+ while (*p == ' ' || *p == '\t')
+ p++;
+ } else if (p[0] == '-' && p[1] == 'A') {
+ /* Handle -A options (assertions). */
+ char *assertion;
+ char *past_name;
+ char *value;
+ char *past_value;
+ char *termination;
+ int save_char;
+
+ assertion = &p[2];
+ past_name = assertion;
+ /* Locate end of name. */
+ while (*past_name && *past_name != ' '
+ && *past_name != '\t' && *past_name != '(')
+ past_name++;
+ /* Locate `(' at start of value. */
+ value = past_name;
+ while (*value && (*value == ' ' || *value == '\t'))
+ value++;
+ if (*value++ != '(')
+ abort ();
+ while (*value && (*value == ' ' || *value == '\t'))
+ value++;
+ past_value = value;
+ /* Locate end of value. */
+ while (*past_value && *past_value != ' '
+ && *past_value != '\t' && *past_value != ')')
+ past_value++;
+ termination = past_value;
+ while (*termination && (*termination == ' ' || *termination == '\t'))
+ termination++;
+ if (*termination++ != ')')
+ abort ();
+ if (*termination && *termination != ' ' && *termination != '\t')
+ abort ();
+ /* Temporarily null-terminate the value. */
+ save_char = *termination;
+ *termination = '\0';
+ /* Install the assertion. */
+ make_assertion ("-A", assertion);
+ *termination = (char) save_char;
+ p = termination;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ } else {
+ abort ();
+ }
+ }
+ }
+
+ /* Now handle the command line options. */
+
+ /* Do -U's, -D's and -A's in the order they were seen. */
+ for (i = 1; i < argc; i++) {
+ if (pend_undefs[i]) {
+ if (debug_output)
+ output_line_command (fp, &outbuf, 0, same_file);
+ make_undef (pend_undefs[i], &outbuf);
+ }
+ if (pend_defs[i]) {
+ if (debug_output)
+ output_line_command (fp, &outbuf, 0, same_file);
+ make_definition (pend_defs[i], &outbuf);
+ }
+ if (pend_assertions[i])
+ make_assertion (pend_assertion_options[i], pend_assertions[i]);
+ }
+
+ done_initializing = 1;
+
+ { /* read the appropriate environment variable and if it exists
+ replace include_defaults with the listed path. */
+ char *epath = 0;
+ switch ((objc << 1) + cplusplus)
+ {
+ case 0:
+ epath = getenv ("C_INCLUDE_PATH");
+ break;
+ case 1:
+ epath = getenv ("CPLUS_INCLUDE_PATH");
+ break;
+ case 2:
+ epath = getenv ("OBJC_INCLUDE_PATH");
+ break;
+ case 3:
+ epath = getenv ("OBJCPLUS_INCLUDE_PATH");
+ break;
+ }
+ /* If the environment var for this language is set,
+ add to the default list of include directories. */
+ if (epath) {
+ char *nstore = (char *) alloca (strlen (epath) + 2);
+ int num_dirs;
+ char *startp, *endp;
+
+ for (num_dirs = 1, startp = epath; *startp; startp++)
+ if (*startp == PATH_SEPARATOR)
+ num_dirs++;
+ include_defaults
+ = (struct default_include *) xmalloc ((num_dirs
+ * sizeof (struct default_include))
+ + sizeof (include_defaults_array));
+ startp = endp = epath;
+ num_dirs = 0;
+ while (1) {
+ /* Handle cases like c:/usr/lib:d:/gcc/lib */
+ if ((*endp == PATH_SEPARATOR
+#if 0 /* Obsolete, now that we use semicolons as the path separator. */
+#ifdef __MSDOS__
+ && (endp-startp != 1 || !isalpha (*startp))
+#endif
+#endif
+ )
+ || *endp == 0) {
+ strncpy (nstore, startp, endp-startp);
+ if (endp == startp)
+ strcpy (nstore, ".");
+ else
+ nstore[endp-startp] = '\0';
+
+ include_defaults[num_dirs].fname = savestring (nstore);
+ include_defaults[num_dirs].cplusplus = cplusplus;
+ include_defaults[num_dirs].cxx_aware = 1;
+ num_dirs++;
+ if (*endp == '\0')
+ break;
+ endp = startp = endp + 1;
+ } else
+ endp++;
+ }
+ /* Put the usual defaults back in at the end. */
+ bcopy ((char *) include_defaults_array,
+ (char *) &include_defaults[num_dirs],
+ sizeof (include_defaults_array));
+ }
+ }
+
+ append_include_chain (before_system, last_before_system);
+ first_system_include = before_system;
+
+ /* Unless -fnostdinc,
+ tack on the standard include file dirs to the specified list */
+ if (!no_standard_includes) {
+ struct default_include *p = include_defaults;
+ char *specd_prefix = include_prefix;
+ char *default_prefix = savestring (GCC_INCLUDE_DIR);
+ int default_len = 0;
+ /* Remove the `include' from /usr/local/lib/gcc.../include. */
+ if (!strcmp (default_prefix + strlen (default_prefix) - 8, "/include")) {
+ default_len = strlen (default_prefix) - 7;
+ default_prefix[default_len] = 0;
+ }
+ /* Search "translated" versions of GNU directories.
+ These have /usr/local/lib/gcc... replaced by specd_prefix. */
+ if (specd_prefix != 0 && default_len != 0)
+ for (p = include_defaults; p->fname; p++) {
+ /* Some standard dirs are only for C++. */
+ if (!p->cplusplus || (cplusplus && !no_standard_cplusplus_includes)) {
+ /* Does this dir start with the prefix? */
+ if (!strncmp (p->fname, default_prefix, default_len)) {
+ /* Yes; change prefix and add to search list. */
+ struct file_name_list *new
+ = (struct file_name_list *) xmalloc (sizeof (struct file_name_list));
+ int this_len = strlen (specd_prefix) + strlen (p->fname) - default_len;
+ char *str = (char *) xmalloc (this_len + 1);
+ strcpy (str, specd_prefix);
+ strcat (str, p->fname + default_len);
+ new->fname = str;
+ new->control_macro = 0;
+ new->c_system_include_path = !p->cxx_aware;
+ new->got_name_map = 0;
+ append_include_chain (new, new);
+ if (first_system_include == 0)
+ first_system_include = new;
+ }
+ }
+ }
+ /* Search ordinary names for GNU include directories. */
+ for (p = include_defaults; p->fname; p++) {
+ /* Some standard dirs are only for C++. */
+ if (!p->cplusplus || (cplusplus && !no_standard_cplusplus_includes)) {
+ struct file_name_list *new
+ = (struct file_name_list *) xmalloc (sizeof (struct file_name_list));
+ new->control_macro = 0;
+ new->c_system_include_path = !p->cxx_aware;
+ new->fname = p->fname;
+ new->got_name_map = 0;
+ append_include_chain (new, new);
+ if (first_system_include == 0)
+ first_system_include = new;
+ }
+ }
+ }
+
+ /* Tack the after_include chain at the end of the include chain. */
+ append_include_chain (after_include, last_after_include);
+ if (first_system_include == 0)
+ first_system_include = after_include;
+
+ /* With -v, print the list of dirs to search. */
+ if (verbose) {
+ struct file_name_list *p;
+ fprintf (stderr, "#include \"...\" search starts here:\n");
+ for (p = include; p; p = p->next) {
+ if (p == first_bracket_include)
+ fprintf (stderr, "#include <...> search starts here:\n");
+ fprintf (stderr, " %s\n", p->fname);
+ }
+ fprintf (stderr, "End of search list.\n");
+ }
+
+ /* Scan the -imacros files before the main input.
+ Much like #including them, but with no_output set
+ so that only their macro definitions matter. */
+
+ no_output++; no_record_file++;
+ for (i = 1; i < argc; i++)
+ if (pend_files[i]) {
+ int fd = open (pend_files[i], O_RDONLY, 0666);
+ if (fd < 0) {
+ perror_with_name (pend_files[i]);
+ return FAILURE_EXIT_CODE;
+ }
+ finclude (fd, pend_files[i], &outbuf, 0, NULL_PTR);
+ }
+ no_output--; no_record_file--;
+
+ /* Copy the entire contents of the main input file into
+ the stacked input buffer previously allocated for it. */
+
+ /* JF check for stdin */
+ if (in_fname == NULL || *in_fname == 0) {
+ in_fname = "";
+ f = 0;
+ } else if ((f = open (in_fname, O_RDONLY, 0666)) < 0)
+ goto perror;
+
+ /* -MG doesn't select the form of output and must be specified with one of
+ -M or -MM. -MG doesn't make sense with -MD or -MMD since they don't
+ inhibit compilation. */
+ if (print_deps_missing_files && (print_deps == 0 || !inhibit_output))
+ fatal ("-MG must be specified with one of -M or -MM");
+
+ /* Either of two environment variables can specify output of deps.
+ Its value is either "OUTPUT_FILE" or "OUTPUT_FILE DEPS_TARGET",
+ where OUTPUT_FILE is the file to write deps info to
+ and DEPS_TARGET is the target to mention in the deps. */
+
+ if (print_deps == 0
+ && (getenv ("SUNPRO_DEPENDENCIES") != 0
+ || getenv ("DEPENDENCIES_OUTPUT") != 0)) {
+ char *spec = getenv ("DEPENDENCIES_OUTPUT");
+ char *s;
+ char *output_file;
+
+ if (spec == 0) {
+ spec = getenv ("SUNPRO_DEPENDENCIES");
+ print_deps = 2;
+ }
+ else
+ print_deps = 1;
+
+ s = spec;
+ /* Find the space before the DEPS_TARGET, if there is one. */
+ /* This should use index. (mrs) */
+ while (*s != 0 && *s != ' ') s++;
+ if (*s != 0) {
+ deps_target = s + 1;
+ output_file = (char *) xmalloc (s - spec + 1);
+ bcopy (spec, output_file, s - spec);
+ output_file[s - spec] = 0;
+ }
+ else {
+ deps_target = 0;
+ output_file = spec;
+ }
+
+ deps_file = output_file;
+ deps_mode = "a";
+ }
+
+ /* For -M, print the expected object file name
+ as the target of this Make-rule. */
+ if (print_deps) {
+ deps_allocated_size = 200;
+ deps_buffer = (char *) xmalloc (deps_allocated_size);
+ deps_buffer[0] = 0;
+ deps_size = 0;
+ deps_column = 0;
+
+ if (deps_target) {
+ deps_output (deps_target, ':');
+ } else if (*in_fname == 0) {
+ deps_output ("-", ':');
+ } else {
+ char *p, *q;
+ int len;
+
+ /* Discard all directory prefixes from filename. */
+ if ((q = rindex (in_fname, '/')) != NULL)
+ ++q;
+ else
+ q = in_fname;
+
+ /* Copy remainder to mungable area. */
+ p = (char *) alloca (strlen(q) + 8);
+ strcpy (p, q);
+
+ /* Output P, but remove known suffixes. */
+ len = strlen (p);
+ q = p + len;
+ if (len >= 2
+ && p[len - 2] == '.'
+ && index("cCsSm", p[len - 1]))
+ q = p + (len - 2);
+ else if (len >= 3
+ && p[len - 3] == '.'
+ && p[len - 2] == 'c'
+ && p[len - 1] == 'c')
+ q = p + (len - 3);
+ else if (len >= 4
+ && p[len - 4] == '.'
+ && p[len - 3] == 'c'
+ && p[len - 2] == 'x'
+ && p[len - 1] == 'x')
+ q = p + (len - 4);
+ else if (len >= 4
+ && p[len - 4] == '.'
+ && p[len - 3] == 'c'
+ && p[len - 2] == 'p'
+ && p[len - 1] == 'p')
+ q = p + (len - 4);
+
+ /* Supply our own suffix. */
+#ifndef VMS
+ strcpy (q, ".o");
+#else
+ strcpy (q, ".obj");
+#endif
+
+ deps_output (p, ':');
+ deps_output (in_fname, ' ');
+ }
+ }
+
+ file_size_and_mode (f, &st_mode, &st_size);
+ fp->nominal_fname = fp->fname = in_fname;
+ fp->lineno = 1;
+ fp->system_header_p = 0;
+ /* JF all this is mine about reading pipes and ttys */
+ if (! S_ISREG (st_mode)) {
+ /* Read input from a file that is not a normal disk file.
+ We cannot preallocate a buffer with the correct size,
+ so we must read in the file a piece at the time and make it bigger. */
+ int size;
+ int bsize;
+ int cnt;
+
+ bsize = 2000;
+ size = 0;
+ fp->buf = (U_CHAR *) xmalloc (bsize + 2);
+ for (;;) {
+ cnt = safe_read (f, fp->buf + size, bsize - size);
+ if (cnt < 0) goto perror; /* error! */
+ size += cnt;
+ if (size != bsize) break; /* End of file */
+ bsize *= 2;
+ fp->buf = (U_CHAR *) xrealloc (fp->buf, bsize + 2);
+ }
+ fp->length = size;
+ } else {
+ /* Read a file whose size we can determine in advance.
+ For the sake of VMS, st_size is just an upper bound. */
+ fp->buf = (U_CHAR *) xmalloc (st_size + 2);
+ fp->length = safe_read (f, fp->buf, st_size);
+ if (fp->length < 0) goto perror;
+ }
+ fp->bufp = fp->buf;
+ fp->if_stack = if_stack;
+
+ /* Make sure data ends with a newline. And put a null after it. */
+
+ if ((fp->length > 0 && fp->buf[fp->length - 1] != '\n')
+ /* Backslash-newline at end is not good enough. */
+ || (fp->length > 1 && fp->buf[fp->length - 2] == '\\')) {
+ fp->buf[fp->length++] = '\n';
+ missing_newline = 1;
+ }
+ fp->buf[fp->length] = '\0';
+
+ /* Unless inhibited, convert trigraphs in the input. */
+
+ if (!no_trigraphs)
+ trigraph_pcp (fp);
+
+ /* Now that we know the input file is valid, open the output. */
+
+ if (!out_fname || !strcmp (out_fname, ""))
+ out_fname = "stdout";
+ else if (! freopen (out_fname, "w", stdout))
+ pfatal_with_name (out_fname);
+
+ output_line_command (fp, &outbuf, 0, same_file);
+
+ /* Scan the -include files before the main input. */
+
+ no_record_file++;
+ for (i = 1; i < argc; i++)
+ if (pend_includes[i]) {
+ int fd = open (pend_includes[i], O_RDONLY, 0666);
+ if (fd < 0) {
+ perror_with_name (pend_includes[i]);
+ return FAILURE_EXIT_CODE;
+ }
+ finclude (fd, pend_includes[i], &outbuf, 0, NULL_PTR);
+ }
+ no_record_file--;
+
+ /* Scan the input, processing macros and directives. */
+
+ rescan (&outbuf, 0);
+
+ if (missing_newline)
+ fp->lineno--;
+
+ if (pedantic && missing_newline)
+ pedwarn ("file does not end in newline");
+
+ /* Now we have processed the entire input
+ Write whichever kind of output has been requested. */
+
+ if (dump_macros == dump_only)
+ dump_all_macros ();
+ else if (! inhibit_output) {
+ write_output ();
+ }
+
+ if (print_deps) {
+ /* Don't actually write the deps file if compilation has failed. */
+ if (errors == 0) {
+ if (deps_file && ! (deps_stream = fopen (deps_file, deps_mode)))
+ pfatal_with_name (deps_file);
+ fputs (deps_buffer, deps_stream);
+ putc ('\n', deps_stream);
+ if (deps_file) {
+ if (ferror (deps_stream) || fclose (deps_stream) != 0)
+ fatal ("I/O error on output");
+ }
+ }
+ }
+
+ if (pcp_outfile && pcp_outfile != stdout
+ && (ferror (pcp_outfile) || fclose (pcp_outfile) != 0))
+ fatal ("I/O error on `-pcp' output");
+
+ if (ferror (stdout) || fclose (stdout) != 0)
+ fatal ("I/O error on output");
+
+ if (errors)
+ exit (FAILURE_EXIT_CODE);
+ exit (SUCCESS_EXIT_CODE);
+
+ perror:
+ pfatal_with_name (in_fname);
+ return 0;
+}
+
+/* Given a colon-separated list of file names PATH,
+ add all the names to the search path for include files. */
+
+static void
+path_include (path)
+ char *path;
+{
+ char *p;
+
+ p = path;
+
+ if (*p)
+ while (1) {
+ char *q = p;
+ char *name;
+ struct file_name_list *dirtmp;
+
+ /* Find the end of this name. */
+ while (*q != 0 && *q != PATH_SEPARATOR) q++;
+ if (p == q) {
+ /* An empty name in the path stands for the current directory. */
+ name = (char *) xmalloc (2);
+ name[0] = '.';
+ name[1] = 0;
+ } else {
+ /* Otherwise use the directory that is named. */
+ name = (char *) xmalloc (q - p + 1);
+ bcopy (p, name, q - p);
+ name[q - p] = 0;
+ }
+
+ dirtmp = (struct file_name_list *)
+ xmalloc (sizeof (struct file_name_list));
+ dirtmp->next = 0; /* New one goes on the end */
+ dirtmp->control_macro = 0;
+ dirtmp->c_system_include_path = 0;
+ dirtmp->fname = name;
+ dirtmp->got_name_map = 0;
+ append_include_chain (dirtmp, dirtmp);
+
+ /* Advance past this name. */
+ p = q;
+ if (*p == 0)
+ break;
+ /* Skip the colon. */
+ p++;
+ }
+}
+
+/* Pre-C-Preprocessor to translate ANSI trigraph idiocy in BUF
+ before main CCCP processing. Name `pcp' is also in honor of the
+ drugs the trigraph designers must have been on.
+
+ Using an extra pass through the buffer takes a little extra time,
+ but is infinitely less hairy than trying to handle trigraphs inside
+ strings, etc. everywhere, and also makes sure that trigraphs are
+ only translated in the top level of processing. */
+
+static void
+trigraph_pcp (buf)
+ FILE_BUF *buf;
+{
+ register U_CHAR c, *fptr, *bptr, *sptr;
+ int len;
+
+ fptr = bptr = sptr = buf->buf;
+ while ((sptr = (U_CHAR *) index (sptr, '?')) != NULL) {
+ if (*++sptr != '?')
+ continue;
+ switch (*++sptr) {
+ case '=':
+ c = '#';
+ break;
+ case '(':
+ c = '[';
+ break;
+ case '/':
+ c = '\\';
+ break;
+ case ')':
+ c = ']';
+ break;
+ case '\'':
+ c = '^';
+ break;
+ case '<':
+ c = '{';
+ break;
+ case '!':
+ c = '|';
+ break;
+ case '>':
+ c = '}';
+ break;
+ case '-':
+ c = '~';
+ break;
+ case '?':
+ sptr--;
+ continue;
+ default:
+ continue;
+ }
+ len = sptr - fptr - 2;
+
+ /* BSD doc says bcopy () works right for overlapping strings. In ANSI
+ C, this will be memmove (). */
+ if (bptr != fptr && len > 0)
+ bcopy ((char *) fptr, (char *) bptr, len);
+
+ bptr += len;
+ *bptr++ = c;
+ fptr = ++sptr;
+ }
+ len = buf->length - (fptr - buf->buf);
+ if (bptr != fptr && len > 0)
+ bcopy ((char *) fptr, (char *) bptr, len);
+ buf->length -= fptr - bptr;
+ buf->buf[buf->length] = '\0';
+ if (warn_trigraphs && fptr != bptr)
+ warning ("%d trigraph(s) encountered", (fptr - bptr) / 2);
+}
+
+/* Move all backslash-newline pairs out of embarrassing places.
+ Exchange all such pairs following BP
+ with any potentially-embarrassing characters that follow them.
+ Potentially-embarrassing characters are / and *
+ (because a backslash-newline inside a comment delimiter
+ would cause it not to be recognized). */
+
+static void
+newline_fix (bp)
+ U_CHAR *bp;
+{
+ register U_CHAR *p = bp;
+ register int count = 0;
+
+ /* First count the backslash-newline pairs here. */
+
+ while (1) {
+ if (p[0] == '\\') {
+ if (p[1] == '\n')
+ p += 2, count++;
+ else if (p[1] == '\r' && p[2] == '\n')
+ p += 3, count++;
+ else
+ break;
+ } else
+ break;
+ }
+
+ /* What follows the backslash-newlines is not embarrassing. */
+
+ if (count == 0 || (*p != '/' && *p != '*'))
+ return;
+
+ /* Copy all potentially embarrassing characters
+ that follow the backslash-newline pairs
+ down to where the pairs originally started. */
+
+ while (*p == '*' || *p == '/')
+ *bp++ = *p++;
+
+ /* Now write the same number of pairs after the embarrassing chars. */
+ while (count-- > 0) {
+ *bp++ = '\\';
+ *bp++ = '\n';
+ }
+}
+
+/* Like newline_fix but for use within a directive-name.
+ Move any backslash-newlines up past any following symbol constituents. */
+
+static void
+name_newline_fix (bp)
+ U_CHAR *bp;
+{
+ register U_CHAR *p = bp;
+ register int count = 0;
+
+ /* First count the backslash-newline pairs here. */
+ while (1) {
+ if (p[0] == '\\') {
+ if (p[1] == '\n')
+ p += 2, count++;
+ else if (p[1] == '\r' && p[2] == '\n')
+ p += 3, count++;
+ else
+ break;
+ } else
+ break;
+ }
+
+ /* What follows the backslash-newlines is not embarrassing. */
+
+ if (count == 0 || !is_idchar[*p])
+ return;
+
+ /* Copy all potentially embarrassing characters
+ that follow the backslash-newline pairs
+ down to where the pairs originally started. */
+
+ while (is_idchar[*p])
+ *bp++ = *p++;
+
+ /* Now write the same number of pairs after the embarrassing chars. */
+ while (count-- > 0) {
+ *bp++ = '\\';
+ *bp++ = '\n';
+ }
+}
+
+/* Look for lint commands in comments.
+
+ When we come in here, ibp points into a comment. Limit is as one expects.
+ scan within the comment -- it should start, after lwsp, with a lint command.
+ If so that command is returned as a (constant) string.
+
+ Upon return, any arg will be pointed to with argstart and will be
+ arglen long. Note that we don't parse that arg since it will just
+ be printed out again.
+*/
+
+static char *
+get_lintcmd (ibp, limit, argstart, arglen, cmdlen)
+ register U_CHAR *ibp;
+ register U_CHAR *limit;
+ U_CHAR **argstart; /* point to command arg */
+ int *arglen, *cmdlen; /* how long they are */
+{
+ long linsize;
+ register U_CHAR *numptr; /* temp for arg parsing */
+
+ *arglen = 0;
+
+ SKIP_WHITE_SPACE (ibp);
+
+ if (ibp >= limit) return NULL;
+
+ linsize = limit - ibp;
+
+ /* Oh, I wish C had lexical functions... hell, I'll just open-code the set */
+ if ((linsize >= 10) && !strncmp (ibp, "NOTREACHED", 10)) {
+ *cmdlen = 10;
+ return "NOTREACHED";
+ }
+ if ((linsize >= 8) && !strncmp (ibp, "ARGSUSED", 8)) {
+ *cmdlen = 8;
+ return "ARGSUSED";
+ }
+ if ((linsize >= 11) && !strncmp (ibp, "LINTLIBRARY", 11)) {
+ *cmdlen = 11;
+ return "LINTLIBRARY";
+ }
+ if ((linsize >= 7) && !strncmp (ibp, "VARARGS", 7)) {
+ *cmdlen = 7;
+ ibp += 7; linsize -= 7;
+ if ((linsize == 0) || ! isdigit (*ibp)) return "VARARGS";
+
+ /* OK, read a number */
+ for (numptr = *argstart = ibp; (numptr < limit) && isdigit (*numptr);
+ numptr++);
+ *arglen = numptr - *argstart;
+ return "VARARGS";
+ }
+ return NULL;
+}
+
+/*
+ * The main loop of the program.
+ *
+ * Read characters from the input stack, transferring them to the
+ * output buffer OP.
+ *
+ * Macros are expanded and push levels on the input stack.
+ * At the end of such a level it is popped off and we keep reading.
+ * At the end of any other kind of level, we return.
+ * #-directives are handled, except within macros.
+ *
+ * If OUTPUT_MARKS is nonzero, keep Newline markers found in the input
+ * and insert them when appropriate. This is set while scanning macro
+ * arguments before substitution. It is zero when scanning for final output.
+ * There are three types of Newline markers:
+ * * Newline - follows a macro name that was not expanded
+ * because it appeared inside an expansion of the same macro.
+ * This marker prevents future expansion of that identifier.
+ * When the input is rescanned into the final output, these are deleted.
+ * These are also deleted by ## concatenation.
+ * * Newline Space (or Newline and any other whitespace character)
+ * stands for a place that tokens must be separated or whitespace
+ * is otherwise desirable, but where the ANSI standard specifies there
+ * is no whitespace. This marker turns into a Space (or whichever other
+ * whitespace char appears in the marker) in the final output,
+ * but it turns into nothing in an argument that is stringified with #.
+ * Such stringified arguments are the only place where the ANSI standard
+ * specifies with precision that whitespace may not appear.
+ *
+ * During this function, IP->bufp is kept cached in IBP for speed of access.
+ * Likewise, OP->bufp is kept in OBP. Before calling a subroutine
+ * IBP, IP and OBP must be copied back to memory. IP and IBP are
+ * copied back with the RECACHE macro. OBP must be copied back from OP->bufp
+ * explicitly, and before RECACHE, since RECACHE uses OBP.
+ */
+
+static void
+rescan (op, output_marks)
+ FILE_BUF *op;
+ int output_marks;
+{
+ /* Character being scanned in main loop. */
+ register U_CHAR c;
+
+ /* Length of pending accumulated identifier. */
+ register int ident_length = 0;
+
+ /* Hash code of pending accumulated identifier. */
+ register int hash = 0;
+
+ /* Current input level (&instack[indepth]). */
+ FILE_BUF *ip;
+
+ /* Pointer for scanning input. */
+ register U_CHAR *ibp;
+
+ /* Pointer to end of input. End of scan is controlled by LIMIT. */
+ register U_CHAR *limit;
+
+ /* Pointer for storing output. */
+ register U_CHAR *obp;
+
+ /* REDO_CHAR is nonzero if we are processing an identifier
+ after backing up over the terminating character.
+ Sometimes we process an identifier without backing up over
+ the terminating character, if the terminating character
+ is not special. Backing up is done so that the terminating character
+ will be dispatched on again once the identifier is dealt with. */
+ int redo_char = 0;
+
+ /* 1 if within an identifier inside of which a concatenation
+ marker (Newline -) has been seen. */
+ int concatenated = 0;
+
+ /* While scanning a comment or a string constant,
+ this records the line it started on, for error messages. */
+ int start_line;
+
+ /* Record position of last `real' newline. */
+ U_CHAR *beg_of_line;
+
+/* Pop the innermost input stack level, assuming it is a macro expansion. */
+
+#define POPMACRO \
+do { ip->macro->type = T_MACRO; \
+ if (ip->free_ptr) free (ip->free_ptr); \
+ --indepth; } while (0)
+
+/* Reload `rescan's local variables that describe the current
+ level of the input stack. */
+
+#define RECACHE \
+do { ip = &instack[indepth]; \
+ ibp = ip->bufp; \
+ limit = ip->buf + ip->length; \
+ op->bufp = obp; \
+ check_expand (op, limit - ibp); \
+ beg_of_line = 0; \
+ obp = op->bufp; } while (0)
+
+ if (no_output && instack[indepth].fname != 0)
+ skip_if_group (&instack[indepth], 1);
+
+ obp = op->bufp;
+ RECACHE;
+
+ beg_of_line = ibp;
+
+ /* Our caller must always put a null after the end of
+ the input at each input stack level. */
+ if (*limit != 0)
+ abort ();
+
+ while (1) {
+ c = *ibp++;
+ *obp++ = c;
+
+ switch (c) {
+ case '\\':
+ if (ibp >= limit)
+ break;
+ if (*ibp == '\n') {
+ /* Always merge lines ending with backslash-newline,
+ even in middle of identifier. */
+ ++ibp;
+ ++ip->lineno;
+ --obp; /* remove backslash from obuf */
+ break;
+ }
+ /* Otherwise, backslash suppresses specialness of following char,
+ so copy it here to prevent the switch from seeing it.
+ But first get any pending identifier processed. */
+ if (ident_length > 0)
+ goto specialchar;
+ *obp++ = *ibp++;
+ break;
+
+ case '#':
+ if (assertions_flag) {
+ /* Copy #foo (bar lose) without macro expansion. */
+ SKIP_WHITE_SPACE (ibp);
+ while (is_idchar[*ibp])
+ *obp++ = *ibp++;
+ SKIP_WHITE_SPACE (ibp);
+ if (*ibp == '(') {
+ ip->bufp = ibp;
+ skip_paren_group (ip);
+ bcopy ((char *) ibp, (char *) obp, ip->bufp - ibp);
+ obp += ip->bufp - ibp;
+ ibp = ip->bufp;
+ }
+ }
+
+ /* If this is expanding a macro definition, don't recognize
+ preprocessor directives. */
+ if (ip->macro != 0)
+ goto randomchar;
+ /* If this is expand_into_temp_buffer, recognize them
+ only after an actual newline at this level,
+ not at the beginning of the input level. */
+ if (ip->fname == 0 && beg_of_line == ip->buf)
+ goto randomchar;
+ if (ident_length)
+ goto specialchar;
+
+
+ /* # keyword: a # must be first nonblank char on the line */
+ if (beg_of_line == 0)
+ goto randomchar;
+ {
+ U_CHAR *bp;
+
+ /* Scan from start of line, skipping whitespace, comments
+ and backslash-newlines, and see if we reach this #.
+ If not, this # is not special. */
+ bp = beg_of_line;
+ /* If -traditional, require # to be at beginning of line. */
+ if (!traditional)
+ while (1) {
+ if (is_hor_space[*bp])
+ bp++;
+ else if (*bp == '\\' && bp[1] == '\n')
+ bp += 2;
+ else if (*bp == '/' && bp[1] == '*') {
+ bp += 2;
+ while (!(*bp == '*' && bp[1] == '/'))
+ bp++;
+ bp += 2;
+ }
+ /* There is no point in trying to deal with C++ // comments here,
+ because if there is one, then this # must be part of the
+ comment and we would never reach here. */
+ else break;
+ }
+ if (bp + 1 != ibp)
+ goto randomchar;
+ }
+
+ /* This # can start a directive. */
+
+ --obp; /* Don't copy the '#' */
+
+ ip->bufp = ibp;
+ op->bufp = obp;
+ if (! handle_directive (ip, op)) {
+#ifdef USE_C_ALLOCA
+ alloca (0);
+#endif
+ /* Not a known directive: treat it as ordinary text.
+ IP, OP, IBP, etc. have not been changed. */
+ if (no_output && instack[indepth].fname) {
+ /* If not generating expanded output,
+ what we do with ordinary text is skip it.
+ Discard everything until next # directive. */
+ skip_if_group (&instack[indepth], 1);
+ RECACHE;
+ beg_of_line = ibp;
+ break;
+ }
+ ++obp; /* Copy the '#' after all */
+ /* Don't expand an identifier that could be a macro directive.
+ (Section 3.8.3 of the ANSI C standard) */
+ SKIP_WHITE_SPACE (ibp);
+ if (is_idstart[*ibp])
+ {
+ *obp++ = *ibp++;
+ while (is_idchar[*ibp])
+ *obp++ = *ibp++;
+ }
+ goto randomchar;
+ }
+#ifdef USE_C_ALLOCA
+ alloca (0);
+#endif
+ /* A # directive has been successfully processed. */
+ /* If not generating expanded output, ignore everything until
+ next # directive. */
+ if (no_output && instack[indepth].fname)
+ skip_if_group (&instack[indepth], 1);
+ obp = op->bufp;
+ RECACHE;
+ beg_of_line = ibp;
+ break;
+
+ case '\"': /* skip quoted string */
+ case '\'':
+ /* A single quoted string is treated like a double -- some
+ programs (e.g., troff) are perverse this way */
+
+ if (ident_length)
+ goto specialchar;
+
+ start_line = ip->lineno;
+
+ /* Skip ahead to a matching quote. */
+
+ while (1) {
+ if (ibp >= limit) {
+ if (ip->macro != 0) {
+ /* try harder: this string crosses a macro expansion boundary.
+ This can happen naturally if -traditional.
+ Otherwise, only -D can make a macro with an unmatched quote. */
+ POPMACRO;
+ RECACHE;
+ continue;
+ }
+ if (!traditional) {
+ error_with_line (line_for_error (start_line),
+ "unterminated string or character constant");
+ error_with_line (multiline_string_line,
+ "possible real start of unterminated constant");
+ multiline_string_line = 0;
+ }
+ break;
+ }
+ *obp++ = *ibp;
+ switch (*ibp++) {
+ case '\n':
+ ++ip->lineno;
+ ++op->lineno;
+ /* Traditionally, end of line ends a string constant with no error.
+ So exit the loop and record the new line. */
+ if (traditional) {
+ beg_of_line = ibp;
+ goto while2end;
+ }
+ if (c == '\'') {
+ error_with_line (line_for_error (start_line),
+ "unterminated character constant");
+ goto while2end;
+ }
+ if (pedantic && multiline_string_line == 0) {
+ pedwarn_with_line (line_for_error (start_line),
+ "string constant runs past end of line");
+ }
+ if (multiline_string_line == 0)
+ multiline_string_line = ip->lineno - 1;
+ break;
+
+ case '\\':
+ if (ibp >= limit)
+ break;
+ if (*ibp == '\n') {
+ /* Backslash newline is replaced by nothing at all,
+ but keep the line counts correct. */
+ --obp;
+ ++ibp;
+ ++ip->lineno;
+ } else {
+ /* ANSI stupidly requires that in \\ the second \
+ is *not* prevented from combining with a newline. */
+ while (*ibp == '\\' && ibp[1] == '\n') {
+ ibp += 2;
+ ++ip->lineno;
+ }
+ *obp++ = *ibp++;
+ }
+ break;
+
+ case '\"':
+ case '\'':
+ if (ibp[-1] == c)
+ goto while2end;
+ break;
+ }
+ }
+ while2end:
+ break;
+
+ case '/':
+ if (*ibp == '\\' && ibp[1] == '\n')
+ newline_fix (ibp);
+
+ if (*ibp != '*'
+ && !(cplusplus_comments && *ibp == '/'))
+ goto randomchar;
+ if (ip->macro != 0)
+ goto randomchar;
+ if (ident_length)
+ goto specialchar;
+
+ if (*ibp == '/') {
+ /* C++ style comment... */
+ start_line = ip->lineno;
+
+ --ibp; /* Back over the slash */
+ --obp;
+
+ /* Comments are equivalent to spaces. */
+ if (! put_out_comments)
+ *obp++ = ' ';
+ else {
+ /* must fake up a comment here */
+ *obp++ = '/';
+ *obp++ = '/';
+ }
+ {
+ U_CHAR *before_bp = ibp+2;
+
+ while (ibp < limit) {
+ if (ibp[-1] != '\\' && *ibp == '\n') {
+ if (put_out_comments) {
+ bcopy ((char *) before_bp, (char *) obp, ibp - before_bp);
+ obp += ibp - before_bp;
+ }
+ break;
+ } else {
+ if (*ibp == '\n') {
+ ++ip->lineno;
+ /* Copy the newline into the output buffer, in order to
+ avoid the pain of a #line every time a multiline comment
+ is seen. */
+ if (!put_out_comments)
+ *obp++ = '\n';
+ ++op->lineno;
+ }
+ ibp++;
+ }
+ }
+ break;
+ }
+ }
+
+ /* Ordinary C comment. Skip it, optionally copying it to output. */
+
+ start_line = ip->lineno;
+
+ ++ibp; /* Skip the star. */
+
+ /* If this cpp is for lint, we peek inside the comments: */
+ if (for_lint) {
+ U_CHAR *argbp;
+ int cmdlen, arglen;
+ char *lintcmd = get_lintcmd (ibp, limit, &argbp, &arglen, &cmdlen);
+
+ if (lintcmd != NULL) {
+ /* I believe it is always safe to emit this newline: */
+ obp[-1] = '\n';
+ bcopy ("#pragma lint ", (char *) obp, 13);
+ obp += 13;
+ bcopy (lintcmd, (char *) obp, cmdlen);
+ obp += cmdlen;
+
+ if (arglen != 0) {
+ *(obp++) = ' ';
+ bcopy (argbp, (char *) obp, arglen);
+ obp += arglen;
+ }
+
+ /* OK, now bring us back to the state we were in before we entered
+ this branch. We need #line b/c the newline for the pragma
+ could fuck things up. */
+ output_line_command (ip, op, 0, same_file);
+ *(obp++) = ' '; /* just in case, if comments are copied thru */
+ *(obp++) = '/';
+ }
+ }
+
+ /* Comments are equivalent to spaces.
+ Note that we already output the slash; we might not want it.
+ For -traditional, a comment is equivalent to nothing. */
+ if (! put_out_comments) {
+ if (traditional)
+ obp--;
+ else
+ obp[-1] = ' ';
+ }
+ else
+ *obp++ = '*';
+
+ {
+ U_CHAR *before_bp = ibp;
+
+ while (ibp < limit) {
+ switch (*ibp++) {
+ case '/':
+ if (warn_comments && ibp < limit && *ibp == '*')
+ warning ("`/*' within comment");
+ break;
+ case '*':
+ if (*ibp == '\\' && ibp[1] == '\n')
+ newline_fix (ibp);
+ if (ibp >= limit || *ibp == '/')
+ goto comment_end;
+ break;
+ case '\n':
+ ++ip->lineno;
+ /* Copy the newline into the output buffer, in order to
+ avoid the pain of a #line every time a multiline comment
+ is seen. */
+ if (!put_out_comments)
+ *obp++ = '\n';
+ ++op->lineno;
+ }
+ }
+ comment_end:
+
+ if (ibp >= limit)
+ error_with_line (line_for_error (start_line),
+ "unterminated comment");
+ else {
+ ibp++;
+ if (put_out_comments) {
+ bcopy ((char *) before_bp, (char *) obp, ibp - before_bp);
+ obp += ibp - before_bp;
+ }
+ }
+ }
+ break;
+
+ case '$':
+ if (!dollars_in_ident)
+ goto randomchar;
+ goto letter;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ /* If digit is not part of identifier, it starts a number,
+ which means that following letters are not an identifier.
+ "0x5" does not refer to an identifier "x5".
+ So copy all alphanumerics that follow without accumulating
+ as an identifier. Periods also, for sake of "3.e7". */
+
+ if (ident_length == 0) {
+ while (ibp < limit) {
+ while (ibp < limit && ibp[0] == '\\' && ibp[1] == '\n') {
+ ++ip->lineno;
+ ibp += 2;
+ }
+ c = *ibp++;
+ /* ".." terminates a preprocessing number. This is useless for C
+ code but useful for preprocessing other things. */
+ if (!isalnum (c) && (c != '.' || *ibp == '.') && c != '_') {
+ --ibp;
+ break;
+ }
+ *obp++ = c;
+ /* A sign can be part of a preprocessing number
+ if it follows an e. */
+ if (c == 'e' || c == 'E') {
+ while (ibp < limit && ibp[0] == '\\' && ibp[1] == '\n') {
+ ++ip->lineno;
+ ibp += 2;
+ }
+ if (ibp < limit && (*ibp == '+' || *ibp == '-')) {
+ *obp++ = *ibp++;
+ /* But traditional C does not let the token go past the sign. */
+ if (traditional)
+ break;
+ }
+ }
+ }
+ break;
+ }
+ /* fall through */
+
+ case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ letter:
+ ident_length++;
+ /* Compute step of hash function, to avoid a proc call on every token */
+ hash = HASHSTEP (hash, c);
+ break;
+
+ case '\n':
+ if (ip->fname == 0 && *ibp == '-') {
+ /* Newline - inhibits expansion of preceding token.
+ If expanding a macro arg, we keep the newline -.
+ In final output, it is deleted.
+ We recognize Newline - in macro bodies and macro args. */
+ if (! concatenated) {
+ ident_length = 0;
+ hash = 0;
+ }
+ ibp++;
+ if (!output_marks) {
+ obp--;
+ } else {
+ /* If expanding a macro arg, keep the newline -. */
+ *obp++ = '-';
+ }
+ break;
+ }
+
+ /* If reprocessing a macro expansion, newline is a special marker. */
+ else if (ip->macro != 0) {
+ /* Newline White is a "funny space" to separate tokens that are
+ supposed to be separate but without space between.
+ Here White means any whitespace character.
+ Newline - marks a recursive macro use that is not
+ supposed to be expandable. */
+
+ if (is_space[*ibp]) {
+ /* Newline Space does not prevent expansion of preceding token
+ so expand the preceding token and then come back. */
+ if (ident_length > 0)
+ goto specialchar;
+
+ /* If generating final output, newline space makes a space. */
+ if (!output_marks) {
+ obp[-1] = *ibp++;
+ /* And Newline Newline makes a newline, so count it. */
+ if (obp[-1] == '\n')
+ op->lineno++;
+ } else {
+ /* If expanding a macro arg, keep the newline space.
+ If the arg gets stringified, newline space makes nothing. */
+ *obp++ = *ibp++;
+ }
+ } else abort (); /* Newline followed by something random? */
+ break;
+ }
+
+ /* If there is a pending identifier, handle it and come back here. */
+ if (ident_length > 0)
+ goto specialchar;
+
+ beg_of_line = ibp;
+
+ /* Update the line counts and output a #line if necessary. */
+ ++ip->lineno;
+ ++op->lineno;
+ if (ip->lineno != op->lineno) {
+ op->bufp = obp;
+ output_line_command (ip, op, 1, same_file);
+ check_expand (op, ip->length - (ip->bufp - ip->buf));
+ obp = op->bufp;
+ }
+ break;
+
+ /* Come here either after (1) a null character that is part of the input
+ or (2) at the end of the input, because there is a null there. */
+ case 0:
+ if (ibp <= limit)
+ /* Our input really contains a null character. */
+ goto randomchar;
+
+ /* At end of a macro-expansion level, pop it and read next level. */
+ if (ip->macro != 0) {
+ obp--;
+ ibp--;
+ /* If traditional, and we have an identifier that ends here,
+ process it now, so we get the right error for recursion. */
+ if (traditional && ident_length
+ && ! is_idchar[*instack[indepth - 1].bufp]) {
+ redo_char = 1;
+ goto randomchar;
+ }
+ POPMACRO;
+ RECACHE;
+ break;
+ }
+
+ /* If we don't have a pending identifier,
+ return at end of input. */
+ if (ident_length == 0) {
+ obp--;
+ ibp--;
+ op->bufp = obp;
+ ip->bufp = ibp;
+ goto ending;
+ }
+
+ /* If we do have a pending identifier, just consider this null
+ a special character and arrange to dispatch on it again.
+ The second time, IDENT_LENGTH will be zero so we will return. */
+
+ /* Fall through */
+
+specialchar:
+
+ /* Handle the case of a character such as /, ', " or null
+ seen following an identifier. Back over it so that
+ after the identifier is processed the special char
+ will be dispatched on again. */
+
+ ibp--;
+ obp--;
+ redo_char = 1;
+
+ default:
+
+randomchar:
+
+ if (ident_length > 0) {
+ register HASHNODE *hp;
+
+ /* We have just seen an identifier end. If it's a macro, expand it.
+
+ IDENT_LENGTH is the length of the identifier
+ and HASH is its hash code.
+
+ The identifier has already been copied to the output,
+ so if it is a macro we must remove it.
+
+ If REDO_CHAR is 0, the char that terminated the identifier
+ has been skipped in the output and the input.
+ OBP-IDENT_LENGTH-1 points to the identifier.
+ If the identifier is a macro, we must back over the terminator.
+
+ If REDO_CHAR is 1, the terminating char has already been
+ backed over. OBP-IDENT_LENGTH points to the identifier. */
+
+ if (!pcp_outfile || pcp_inside_if) {
+startagain:
+ for (hp = hashtab[MAKE_POS (hash) % HASHSIZE]; hp != NULL;
+ hp = hp->next) {
+
+ if (hp->length == ident_length) {
+ int obufp_before_macroname;
+ int op_lineno_before_macroname;
+ register int i = ident_length;
+ register U_CHAR *p = hp->name;
+ register U_CHAR *q = obp - i;
+ int disabled;
+
+ if (! redo_char)
+ q--;
+
+ do { /* All this to avoid a strncmp () */
+ if (*p++ != *q++)
+ goto hashcollision;
+ } while (--i);
+
+ /* We found a use of a macro name.
+ see if the context shows it is a macro call. */
+
+ /* Back up over terminating character if not already done. */
+ if (! redo_char) {
+ ibp--;
+ obp--;
+ }
+
+ /* Save this as a displacement from the beginning of the output
+ buffer. We can not save this as a position in the output
+ buffer, because it may get realloc'ed by RECACHE. */
+ obufp_before_macroname = (obp - op->buf) - ident_length;
+ op_lineno_before_macroname = op->lineno;
+
+ if (hp->type == T_PCSTRING) {
+ pcstring_used (hp); /* Mark the definition of this key
+ as needed, ensuring that it
+ will be output. */
+ break; /* Exit loop, since the key cannot have a
+ definition any longer. */
+ }
+
+ /* Record whether the macro is disabled. */
+ disabled = hp->type == T_DISABLED;
+
+ /* This looks like a macro ref, but if the macro was disabled,
+ just copy its name and put in a marker if requested. */
+
+ if (disabled) {
+#if 0
+ /* This error check caught useful cases such as
+ #define foo(x,y) bar (x (y,0), y)
+ foo (foo, baz) */
+ if (traditional)
+ error ("recursive use of macro `%s'", hp->name);
+#endif
+
+ if (output_marks) {
+ check_expand (op, limit - ibp + 2);
+ *obp++ = '\n';
+ *obp++ = '-';
+ }
+ break;
+ }
+
+ /* If macro wants an arglist, verify that a '(' follows.
+ first skip all whitespace, copying it to the output
+ after the macro name. Then, if there is no '(',
+ decide this is not a macro call and leave things that way. */
+ if ((hp->type == T_MACRO || hp->type == T_DISABLED)
+ && hp->value.defn->nargs >= 0)
+ {
+ U_CHAR *old_ibp = ibp;
+ U_CHAR *old_obp = obp;
+ int old_iln = ip->lineno;
+ int old_oln = op->lineno;
+
+ while (1) {
+ /* Scan forward over whitespace, copying it to the output. */
+ if (ibp == limit && ip->macro != 0) {
+ POPMACRO;
+ RECACHE;
+ old_ibp = ibp;
+ old_obp = obp;
+ old_iln = ip->lineno;
+ old_oln = op->lineno;
+ }
+ /* A comment: copy it unchanged or discard it. */
+ else if (*ibp == '/' && ibp+1 != limit && ibp[1] == '*') {
+ if (put_out_comments) {
+ *obp++ = '/';
+ *obp++ = '*';
+ } else if (! traditional) {
+ *obp++ = ' ';
+ }
+ ibp += 2;
+ while (ibp + 1 != limit
+ && !(ibp[0] == '*' && ibp[1] == '/')) {
+ /* We need not worry about newline-marks,
+ since they are never found in comments. */
+ if (*ibp == '\n') {
+ /* Newline in a file. Count it. */
+ ++ip->lineno;
+ ++op->lineno;
+ }
+ if (put_out_comments)
+ *obp++ = *ibp++;
+ else
+ ibp++;
+ }
+ ibp += 2;
+ if (put_out_comments) {
+ *obp++ = '*';
+ *obp++ = '/';
+ }
+ }
+ else if (is_space[*ibp]) {
+ *obp++ = *ibp++;
+ if (ibp[-1] == '\n') {
+ if (ip->macro == 0) {
+ /* Newline in a file. Count it. */
+ ++ip->lineno;
+ ++op->lineno;
+ } else if (!output_marks) {
+ /* A newline mark, and we don't want marks
+ in the output. If it is newline-hyphen,
+ discard it entirely. Otherwise, it is
+ newline-whitechar, so keep the whitechar. */
+ obp--;
+ if (*ibp == '-')
+ ibp++;
+ else {
+ if (*ibp == '\n')
+ ++op->lineno;
+ *obp++ = *ibp++;
+ }
+ } else {
+ /* A newline mark; copy both chars to the output. */
+ *obp++ = *ibp++;
+ }
+ }
+ }
+ else break;
+ }
+ if (*ibp != '(') {
+ /* It isn't a macro call.
+ Put back the space that we just skipped. */
+ ibp = old_ibp;
+ obp = old_obp;
+ ip->lineno = old_iln;
+ op->lineno = old_oln;
+ /* Exit the for loop. */
+ break;
+ }
+ }
+
+ /* This is now known to be a macro call.
+ Discard the macro name from the output,
+ along with any following whitespace just copied. */
+ obp = op->buf + obufp_before_macroname;
+ op->lineno = op_lineno_before_macroname;
+
+ /* Prevent accidental token-pasting with a character
+ before the macro call. */
+ if (!traditional && obp != op->buf
+ && (obp[-1] == '-' || obp[1] == '+' || obp[1] == '&'
+ || obp[-1] == '|' || obp[1] == '<' || obp[1] == '>')) {
+ /* If we are expanding a macro arg, make a newline marker
+ to separate the tokens. If we are making real output,
+ a plain space will do. */
+ if (output_marks)
+ *obp++ = '\n';
+ *obp++ = ' ';
+ }
+
+ /* Expand the macro, reading arguments as needed,
+ and push the expansion on the input stack. */
+ ip->bufp = ibp;
+ op->bufp = obp;
+ macroexpand (hp, op);
+
+ /* Reexamine input stack, since macroexpand has pushed
+ a new level on it. */
+ obp = op->bufp;
+ RECACHE;
+ break;
+ }
+hashcollision:
+ ;
+ } /* End hash-table-search loop */
+ }
+ ident_length = hash = 0; /* Stop collecting identifier */
+ redo_char = 0;
+ concatenated = 0;
+ } /* End if (ident_length > 0) */
+ } /* End switch */
+ } /* End per-char loop */
+
+ /* Come here to return -- but first give an error message
+ if there was an unterminated successful conditional. */
+ ending:
+ if (if_stack != ip->if_stack)
+ {
+ char *str = "unknown";
+
+ switch (if_stack->type)
+ {
+ case T_IF:
+ str = "if";
+ break;
+ case T_IFDEF:
+ str = "ifdef";
+ break;
+ case T_IFNDEF:
+ str = "ifndef";
+ break;
+ case T_ELSE:
+ str = "else";
+ break;
+ case T_ELIF:
+ str = "elif";
+ break;
+ }
+
+ error_with_line (line_for_error (if_stack->lineno),
+ "unterminated `#%s' conditional", str);
+ }
+ if_stack = ip->if_stack;
+}
+
+/*
+ * Rescan a string into a temporary buffer and return the result
+ * as a FILE_BUF. Note this function returns a struct, not a pointer.
+ *
+ * OUTPUT_MARKS nonzero means keep Newline markers found in the input
+ * and insert such markers when appropriate. See `rescan' for details.
+ * OUTPUT_MARKS is 1 for macroexpanding a macro argument separately
+ * before substitution; it is 0 for other uses.
+ */
+static FILE_BUF
+expand_to_temp_buffer (buf, limit, output_marks, assertions)
+ U_CHAR *buf, *limit;
+ int output_marks, assertions;
+{
+ register FILE_BUF *ip;
+ FILE_BUF obuf;
+ int length = limit - buf;
+ U_CHAR *buf1;
+ int odepth = indepth;
+ int save_assertions_flag = assertions_flag;
+
+ assertions_flag = assertions;
+
+ if (length < 0)
+ abort ();
+
+ /* Set up the input on the input stack. */
+
+ buf1 = (U_CHAR *) alloca (length + 1);
+ {
+ register U_CHAR *p1 = buf;
+ register U_CHAR *p2 = buf1;
+
+ while (p1 != limit)
+ *p2++ = *p1++;
+ }
+ buf1[length] = 0;
+
+ /* Set up to receive the output. */
+
+ obuf.length = length * 2 + 100; /* Usually enough. Why be stingy? */
+ obuf.bufp = obuf.buf = (U_CHAR *) xmalloc (obuf.length);
+ obuf.fname = 0;
+ obuf.macro = 0;
+ obuf.free_ptr = 0;
+
+ CHECK_DEPTH ({return obuf;});
+
+ ++indepth;
+
+ ip = &instack[indepth];
+ ip->fname = 0;
+ ip->nominal_fname = 0;
+ ip->system_header_p = 0;
+ ip->macro = 0;
+ ip->free_ptr = 0;
+ ip->length = length;
+ ip->buf = ip->bufp = buf1;
+ ip->if_stack = if_stack;
+
+ ip->lineno = obuf.lineno = 1;
+
+ /* Scan the input, create the output. */
+ rescan (&obuf, output_marks);
+
+ /* Pop input stack to original state. */
+ --indepth;
+
+ if (indepth != odepth)
+ abort ();
+
+ /* Record the output. */
+ obuf.length = obuf.bufp - obuf.buf;
+
+ assertions_flag = save_assertions_flag;
+ return obuf;
+}
+
+/*
+ * Process a # directive. Expects IP->bufp to point after the '#', as in
+ * `#define foo bar'. Passes to the command handler
+ * (do_define, do_include, etc.): the addresses of the 1st and
+ * last chars of the command (starting immediately after the #
+ * keyword), plus op and the keyword table pointer. If the command
+ * contains comments it is copied into a temporary buffer sans comments
+ * and the temporary buffer is passed to the command handler instead.
+ * Likewise for backslash-newlines.
+ *
+ * Returns nonzero if this was a known # directive.
+ * Otherwise, returns zero, without advancing the input pointer.
+ */
+
+static int
+handle_directive (ip, op)
+ FILE_BUF *ip, *op;
+{
+ register U_CHAR *bp, *cp;
+ register struct directive *kt;
+ register int ident_length;
+ U_CHAR *resume_p;
+
+ /* Nonzero means we must copy the entire command
+ to get rid of comments or backslash-newlines. */
+ int copy_command = 0;
+
+ U_CHAR *ident, *after_ident;
+
+ bp = ip->bufp;
+
+ /* Record where the directive started. do_xifdef needs this. */
+ directive_start = bp - 1;
+
+ /* Skip whitespace and \-newline. */
+ while (1) {
+ if (is_hor_space[*bp]) {
+ if ((*bp == '\f' || *bp == '\v') && pedantic)
+ pedwarn ("%s in preprocessing directive",
+ *bp == '\f' ? "formfeed" : "vertical tab");
+ bp++;
+ } else if (*bp == '/' && (bp[1] == '*'
+ || (cplusplus_comments && bp[1] == '/'))) {
+ ip->bufp = bp + 2;
+ skip_to_end_of_comment (ip, &ip->lineno, 0);
+ bp = ip->bufp;
+ } else if (*bp == '\\' && bp[1] == '\n') {
+ bp += 2; ip->lineno++;
+ } else break;
+ }
+
+ /* Now find end of directive name.
+ If we encounter a backslash-newline, exchange it with any following
+ symbol-constituents so that we end up with a contiguous name. */
+
+ cp = bp;
+ while (1) {
+ if (is_idchar[*cp])
+ cp++;
+ else {
+ if (*cp == '\\' && cp[1] == '\n')
+ name_newline_fix (cp);
+ if (is_idchar[*cp])
+ cp++;
+ else break;
+ }
+ }
+ ident_length = cp - bp;
+ ident = bp;
+ after_ident = cp;
+
+ /* A line of just `#' becomes blank. */
+
+ if (ident_length == 0 && *after_ident == '\n') {
+ ip->bufp = after_ident;
+ return 1;
+ }
+
+ if (ident_length == 0 || !is_idstart[*ident]) {
+ U_CHAR *p = ident;
+ while (is_idchar[*p]) {
+ if (*p < '0' || *p > '9')
+ break;
+ p++;
+ }
+ /* Handle # followed by a line number. */
+ if (p != ident && !is_idchar[*p]) {
+ static struct directive line_directive_table[] = {
+ { 4, do_line, "line", T_LINE},
+ };
+ if (pedantic)
+ pedwarn ("`#' followed by integer");
+ after_ident = ident;
+ kt = line_directive_table;
+ goto old_linenum;
+ }
+
+ /* Avoid error for `###' and similar cases unless -pedantic. */
+ if (p == ident) {
+ while (*p == '#' || is_hor_space[*p]) p++;
+ if (*p == '\n') {
+ if (pedantic && !lang_asm)
+ warning ("invalid preprocessor directive");
+ return 0;
+ }
+ }
+
+ if (!lang_asm)
+ error ("invalid preprocessor directive name");
+
+ return 0;
+ }
+
+ /*
+ * Decode the keyword and call the appropriate expansion
+ * routine, after moving the input pointer up to the next line.
+ */
+ for (kt = directive_table; kt->length > 0; kt++) {
+ if (kt->length == ident_length && !strncmp (kt->name, ident, ident_length)) {
+ register U_CHAR *buf;
+ register U_CHAR *limit;
+ int unterminated;
+ int junk;
+ int *already_output;
+
+ /* Nonzero means do not delete comments within the directive.
+ #define needs this when -traditional. */
+ int keep_comments;
+
+ old_linenum:
+
+ limit = ip->buf + ip->length;
+ unterminated = 0;
+ already_output = 0;
+ keep_comments = traditional && kt->traditional_comments;
+ /* #import is defined only in Objective C, or when on the NeXT. */
+ if (kt->type == T_IMPORT && !(objc || lookup ("__NeXT__", -1, -1)))
+ break;
+
+ /* Find the end of this command (first newline not backslashed
+ and not in a string or comment).
+ Set COPY_COMMAND if the command must be copied
+ (it contains a backslash-newline or a comment). */
+
+ buf = bp = after_ident;
+ while (bp < limit) {
+ register U_CHAR c = *bp++;
+ switch (c) {
+ case '\\':
+ if (bp < limit) {
+ if (*bp == '\n') {
+ ip->lineno++;
+ copy_command = 1;
+ }
+ bp++;
+ }
+ break;
+
+ case '\'':
+ case '\"':
+ bp = skip_quoted_string (bp - 1, limit, ip->lineno, &ip->lineno, &copy_command, &unterminated);
+ /* Don't bother calling the directive if we already got an error
+ message due to unterminated string. Skip everything and pretend
+ we called the directive. */
+ if (unterminated) {
+ if (traditional) {
+ /* Traditional preprocessing permits unterminated strings. */
+ ip->bufp = bp;
+ goto endloop1;
+ }
+ ip->bufp = bp;
+ return 1;
+ }
+ break;
+
+ /* <...> is special for #include. */
+ case '<':
+ if (!kt->angle_brackets)
+ break;
+ while (*bp && *bp != '>') bp++;
+ break;
+
+ case '/':
+ if (*bp == '\\' && bp[1] == '\n')
+ newline_fix (bp);
+ if (*bp == '*'
+ || (cplusplus_comments && *bp == '/')) {
+ U_CHAR *obp = bp - 1;
+ ip->bufp = bp + 1;
+ skip_to_end_of_comment (ip, &ip->lineno, 0);
+ bp = ip->bufp;
+ /* No need to copy the command because of a comment at the end;
+ just don't include the comment in the directive. */
+ if (bp == limit || *bp == '\n') {
+ bp = obp;
+ goto endloop1;
+ }
+ /* Don't remove the comments if -traditional. */
+ if (! keep_comments)
+ copy_command++;
+ }
+ break;
+
+ case '\f':
+ case '\v':
+ if (pedantic)
+ pedwarn ("%s in preprocessing directive",
+ c == '\f' ? "formfeed" : "vertical tab");
+ break;
+
+ case '\n':
+ --bp; /* Point to the newline */
+ ip->bufp = bp;
+ goto endloop1;
+ }
+ }
+ ip->bufp = bp;
+
+ endloop1:
+ resume_p = ip->bufp;
+ /* BP is the end of the directive.
+ RESUME_P is the next interesting data after the directive.
+ A comment may come between. */
+
+ /* If a directive should be copied through, and -E was given,
+ pass it through before removing comments. */
+ if (!no_output && kt->pass_thru && put_out_comments) {
+ int len;
+
+ /* Output directive name. */
+ check_expand (op, kt->length + 2);
+ /* Make sure # is at the start of a line */
+ if (op->bufp > op->buf && op->bufp[-1] != '\n') {
+ op->lineno++;
+ *op->bufp++ = '\n';
+ }
+ *op->bufp++ = '#';
+ bcopy (kt->name, op->bufp, kt->length);
+ op->bufp += kt->length;
+
+ /* Output arguments. */
+ len = (bp - buf);
+ check_expand (op, len);
+ bcopy (buf, (char *) op->bufp, len);
+ op->bufp += len;
+ /* Take account of any (escaped) newlines just output. */
+ while (--len >= 0)
+ if (buf[len] == '\n')
+ op->lineno++;
+
+ already_output = &junk;
+ } /* Don't we need a newline or #line? */
+
+ if (copy_command) {
+ register U_CHAR *xp = buf;
+ /* Need to copy entire command into temp buffer before dispatching */
+
+ cp = (U_CHAR *) alloca (bp - buf + 5); /* room for cmd plus
+ some slop */
+ buf = cp;
+
+ /* Copy to the new buffer, deleting comments
+ and backslash-newlines (and whitespace surrounding the latter). */
+
+ while (xp < bp) {
+ register U_CHAR c = *xp++;
+ *cp++ = c;
+
+ switch (c) {
+ case '\n':
+ abort (); /* A bare newline should never part of the line. */
+ break;
+
+ /* <...> is special for #include. */
+ case '<':
+ if (!kt->angle_brackets)
+ break;
+ while (xp < bp && c != '>') {
+ c = *xp++;
+ if (c == '\\' && xp < bp && *xp == '\n')
+ xp++;
+ else
+ *cp++ = c;
+ }
+ break;
+
+ case '\\':
+ if (*xp == '\n') {
+ xp++;
+ cp--;
+ if (cp != buf && is_space[cp[-1]]) {
+ while (cp != buf && is_space[cp[-1]]) cp--;
+ cp++;
+ SKIP_WHITE_SPACE (xp);
+ } else if (is_space[*xp]) {
+ *cp++ = *xp++;
+ SKIP_WHITE_SPACE (xp);
+ }
+ } else {
+ *cp++ = *xp++;
+ }
+ break;
+
+ case '\'':
+ case '\"':
+ {
+ register U_CHAR *bp1
+ = skip_quoted_string (xp - 1, bp, ip->lineno,
+ NULL_PTR, NULL_PTR, NULL_PTR);
+ while (xp != bp1)
+ if (*xp == '\\') {
+ if (*++xp != '\n')
+ *cp++ = '\\';
+ else
+ xp++;
+ } else
+ *cp++ = *xp++;
+ }
+ break;
+
+ case '/':
+ if (*xp == '*'
+ || (cplusplus_comments && *xp == '/')) {
+ ip->bufp = xp + 1;
+ /* If we already copied the command through,
+ already_output != 0 prevents outputting comment now. */
+ skip_to_end_of_comment (ip, already_output, 0);
+ if (keep_comments)
+ while (xp != ip->bufp)
+ *cp++ = *xp++;
+ /* Delete or replace the slash. */
+ else if (traditional)
+ cp--;
+ else
+ cp[-1] = ' ';
+ xp = ip->bufp;
+ }
+ }
+ }
+
+ /* Null-terminate the copy. */
+
+ *cp = 0;
+ } else
+ cp = bp;
+
+ ip->bufp = resume_p;
+
+ /* Some directives should be written out for cc1 to process,
+ just as if they were not defined. And sometimes we're copying
+ definitions through. */
+
+ if (!no_output && already_output == 0
+ && (kt->pass_thru
+ || (kt->type == T_DEFINE
+ && (dump_macros == dump_names
+ || dump_macros == dump_definitions)))) {
+ int len;
+
+ /* Output directive name. */
+ check_expand (op, kt->length + 1);
+ *op->bufp++ = '#';
+ bcopy (kt->name, (char *) op->bufp, kt->length);
+ op->bufp += kt->length;
+
+ if (kt->pass_thru || dump_macros == dump_definitions) {
+ /* Output arguments. */
+ len = (cp - buf);
+ check_expand (op, len);
+ bcopy (buf, (char *) op->bufp, len);
+ op->bufp += len;
+ } else if (kt->type == T_DEFINE && dump_macros == dump_names) {
+ U_CHAR *xp = buf;
+ U_CHAR *yp;
+ SKIP_WHITE_SPACE (xp);
+ yp = xp;
+ while (is_idchar[*xp]) xp++;
+ len = (xp - yp);
+ check_expand (op, len + 1);
+ *op->bufp++ = ' ';
+ bcopy (yp, op->bufp, len);
+ op->bufp += len;
+ }
+ } /* Don't we need a newline or #line? */
+
+ /* Call the appropriate command handler. buf now points to
+ either the appropriate place in the input buffer, or to
+ the temp buffer if it was necessary to make one. cp
+ points to the first char after the contents of the (possibly
+ copied) command, in either case. */
+ (*kt->func) (buf, cp, op, kt);
+ check_expand (op, ip->length - (ip->bufp - ip->buf));
+
+ return 1;
+ }
+ }
+
+ /* It is deliberate that we don't warn about undefined directives.
+ That is the responsibility of cc1. */
+ return 0;
+}
+
+static struct tm *
+timestamp ()
+{
+ static struct tm *timebuf;
+ if (!timebuf) {
+ time_t t = time (0);
+ timebuf = localtime (&t);
+ }
+ return timebuf;
+}
+
+static char *monthnames[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+ };
+
+/*
+ * expand things like __FILE__. Place the expansion into the output
+ * buffer *without* rescanning.
+ */
+
+static void
+special_symbol (hp, op)
+ HASHNODE *hp;
+ FILE_BUF *op;
+{
+ char *buf;
+ int i, len;
+ int true_indepth;
+ FILE_BUF *ip = NULL;
+ struct tm *timebuf;
+
+ int paren = 0; /* For special `defined' keyword */
+
+ if (pcp_outfile && pcp_inside_if
+ && hp->type != T_SPEC_DEFINED && hp->type != T_CONST)
+ error ("Predefined macro `%s' used inside `#if' during precompilation",
+ hp->name);
+
+ for (i = indepth; i >= 0; i--)
+ if (instack[i].fname != NULL) {
+ ip = &instack[i];
+ break;
+ }
+ if (ip == NULL) {
+ error ("cccp error: not in any file?!");
+ return; /* the show must go on */
+ }
+
+ switch (hp->type) {
+ case T_FILE:
+ case T_BASE_FILE:
+ {
+ char *string;
+ if (hp->type == T_FILE)
+ string = ip->nominal_fname;
+ else
+ string = instack[0].nominal_fname;
+
+ if (string)
+ {
+ buf = (char *) alloca (3 + 4 * strlen (string));
+ quote_string (buf, string);
+ }
+ else
+ buf = "\"\"";
+
+ break;
+ }
+
+ case T_INCLUDE_LEVEL:
+ true_indepth = 0;
+ for (i = indepth; i >= 0; i--)
+ if (instack[i].fname != NULL)
+ true_indepth++;
+
+ buf = (char *) alloca (8); /* Eight bytes ought to be more than enough */
+ sprintf (buf, "%d", true_indepth - 1);
+ break;
+
+ case T_VERSION:
+ buf = (char *) alloca (3 + strlen (version_string));
+ sprintf (buf, "\"%s\"", version_string);
+ break;
+
+#ifndef NO_BUILTIN_SIZE_TYPE
+ case T_SIZE_TYPE:
+ buf = SIZE_TYPE;
+ break;
+#endif
+
+#ifndef NO_BUILTIN_PTRDIFF_TYPE
+ case T_PTRDIFF_TYPE:
+ buf = PTRDIFF_TYPE;
+ break;
+#endif
+
+ case T_WCHAR_TYPE:
+ buf = wchar_type;
+ break;
+
+ case T_USER_LABEL_PREFIX_TYPE:
+ buf = USER_LABEL_PREFIX;
+ break;
+
+ case T_REGISTER_PREFIX_TYPE:
+ buf = REGISTER_PREFIX;
+ break;
+
+ case T_CONST:
+ buf = (char *) alloca (4 * sizeof (int));
+ sprintf (buf, "%d", hp->value.ival);
+ if (pcp_inside_if && pcp_outfile)
+ /* Output a precondition for this macro use */
+ fprintf (pcp_outfile, "#define %s %d\n", hp->name, hp->value.ival);
+ break;
+
+ case T_SPECLINE:
+ buf = (char *) alloca (10);
+ sprintf (buf, "%d", ip->lineno);
+ break;
+
+ case T_DATE:
+ case T_TIME:
+ buf = (char *) alloca (20);
+ timebuf = timestamp ();
+ if (hp->type == T_DATE)
+ sprintf (buf, "\"%s %2d %4d\"", monthnames[timebuf->tm_mon],
+ timebuf->tm_mday, timebuf->tm_year + 1900);
+ else
+ sprintf (buf, "\"%02d:%02d:%02d\"", timebuf->tm_hour, timebuf->tm_min,
+ timebuf->tm_sec);
+ break;
+
+ case T_SPEC_DEFINED:
+ buf = " 0 "; /* Assume symbol is not defined */
+ ip = &instack[indepth];
+ SKIP_WHITE_SPACE (ip->bufp);
+ if (*ip->bufp == '(') {
+ paren++;
+ ip->bufp++; /* Skip over the paren */
+ SKIP_WHITE_SPACE (ip->bufp);
+ }
+
+ if (!is_idstart[*ip->bufp])
+ goto oops;
+ if (hp = lookup (ip->bufp, -1, -1)) {
+ if (pcp_outfile && pcp_inside_if
+ && hp->value.defn->predefined)
+ /* Output a precondition for this macro use. */
+ fprintf (pcp_outfile, "#define %s\n", hp->name);
+ buf = " 1 ";
+ }
+ else
+ if (pcp_outfile && pcp_inside_if) {
+ /* Output a precondition for this macro use */
+ U_CHAR *cp = ip->bufp;
+ fprintf (pcp_outfile, "#undef ");
+ while (is_idchar[*cp]) /* Ick! */
+ fputc (*cp++, pcp_outfile);
+ putc ('\n', pcp_outfile);
+ }
+ while (is_idchar[*ip->bufp])
+ ++ip->bufp;
+ SKIP_WHITE_SPACE (ip->bufp);
+ if (paren) {
+ if (*ip->bufp != ')')
+ goto oops;
+ ++ip->bufp;
+ }
+ break;
+
+oops:
+
+ error ("`defined' without an identifier");
+ break;
+
+ default:
+ error ("cccp error: invalid special hash type"); /* time for gdb */
+ abort ();
+ }
+ len = strlen (buf);
+ check_expand (op, len);
+ bcopy (buf, (char *) op->bufp, len);
+ op->bufp += len;
+
+ return;
+}
+
+
+/* Routines to handle #directives */
+
+/* Handle #include and #import.
+ This function expects to see "fname" or <fname> on the input. */
+
+static int
+do_include (buf, limit, op, keyword)
+ U_CHAR *buf, *limit;
+ FILE_BUF *op;
+ struct directive *keyword;
+{
+ int importing = (keyword->type == T_IMPORT);
+ int skip_dirs = (keyword->type == T_INCLUDE_NEXT);
+ static int import_warning = 0;
+ char *fname; /* Dynamically allocated fname buffer */
+ char *pcftry;
+ char *pcfname;
+ U_CHAR *fbeg, *fend; /* Beginning and end of fname */
+
+ struct file_name_list *search_start = include; /* Chain of dirs to search */
+ struct file_name_list dsp[1]; /* First in chain, if #include "..." */
+ struct file_name_list *searchptr = 0;
+ int flen;
+
+ int f; /* file number */
+
+ int retried = 0; /* Have already tried macro
+ expanding the include line*/
+ FILE_BUF trybuf; /* It got expanded into here */
+ int angle_brackets = 0; /* 0 for "...", 1 for <...> */
+ int pcf = -1;
+ char *pcfbuf;
+ int pcfbuflimit;
+ int pcfnum;
+ f= -1; /* JF we iz paranoid! */
+
+ if (importing && warn_import && !inhibit_warnings
+ && !instack[indepth].system_header_p && !import_warning) {
+ import_warning = 1;
+ warning ("using `#import' is not recommended");
+ fprintf (stderr, "The fact that a certain header file need not be processed more than once\n");
+ fprintf (stderr, "should be indicated in the header file, not where it is used.\n");
+ fprintf (stderr, "The best way to do this is with a conditional of this form:\n\n");
+ fprintf (stderr, " #ifndef _FOO_H_INCLUDED\n");
+ fprintf (stderr, " #define _FOO_H_INCLUDED\n");
+ fprintf (stderr, " ... <real contents of file> ...\n");
+ fprintf (stderr, " #endif /* Not _FOO_H_INCLUDED */\n\n");
+ fprintf (stderr, "Then users can use `#include' any number of times.\n");
+ fprintf (stderr, "GNU C automatically avoids processing the file more than once\n");
+ fprintf (stderr, "when it is equipped with such a conditional.\n");
+ }
+
+get_filename:
+
+ fbeg = buf;
+ SKIP_WHITE_SPACE (fbeg);
+ /* Discard trailing whitespace so we can easily see
+ if we have parsed all the significant chars we were given. */
+ while (limit != fbeg && is_hor_space[limit[-1]]) limit--;
+
+ switch (*fbeg++) {
+ case '\"':
+ {
+ FILE_BUF *fp;
+ /* Copy the operand text, concatenating the strings. */
+ {
+ U_CHAR *fin = fbeg;
+ fbeg = (U_CHAR *) alloca (limit - fbeg + 1);
+ fend = fbeg;
+ while (fin != limit) {
+ while (fin != limit && *fin != '\"')
+ *fend++ = *fin++;
+ fin++;
+ if (fin == limit)
+ break;
+ /* If not at the end, there had better be another string. */
+ /* Skip just horiz space, and don't go past limit. */
+ while (fin != limit && is_hor_space[*fin]) fin++;
+ if (fin != limit && *fin == '\"')
+ fin++;
+ else
+ goto fail;
+ }
+ }
+ *fend = 0;
+
+ /* We have "filename". Figure out directory this source
+ file is coming from and put it on the front of the list. */
+
+ /* If -I- was specified, don't search current dir, only spec'd ones. */
+ if (ignore_srcdir) break;
+
+ for (fp = &instack[indepth]; fp >= instack; fp--)
+ {
+ int n;
+ char *ep,*nam;
+
+ if ((nam = fp->nominal_fname) != NULL) {
+ /* Found a named file. Figure out dir of the file,
+ and put it in front of the search list. */
+ dsp[0].next = search_start;
+ search_start = dsp;
+#ifndef VMS
+ ep = rindex (nam, '/');
+#else /* VMS */
+ ep = rindex (nam, ']');
+ if (ep == NULL) ep = rindex (nam, '>');
+ if (ep == NULL) ep = rindex (nam, ':');
+ if (ep != NULL) ep++;
+#endif /* VMS */
+ if (ep != NULL) {
+ n = ep - nam;
+ dsp[0].fname = (char *) alloca (n + 1);
+ strncpy (dsp[0].fname, nam, n);
+ dsp[0].fname[n] = '\0';
+ if (n + INCLUDE_LEN_FUDGE > max_include_len)
+ max_include_len = n + INCLUDE_LEN_FUDGE;
+ } else {
+ dsp[0].fname = 0; /* Current directory */
+ }
+ dsp[0].got_name_map = 0;
+ break;
+ }
+ }
+ break;
+ }
+
+ case '<':
+ fend = fbeg;
+ while (fend != limit && *fend != '>') fend++;
+ if (*fend == '>' && fend + 1 == limit) {
+ angle_brackets = 1;
+ /* If -I-, start with the first -I dir after the -I-. */
+ if (first_bracket_include)
+ search_start = first_bracket_include;
+ break;
+ }
+ goto fail;
+
+ default:
+#ifdef VMS
+ /*
+ * Support '#include xyz' like VAX-C to allow for easy use of all the
+ * decwindow include files. It defaults to '#include <xyz.h>' (so the
+ * code from case '<' is repeated here) and generates a warning.
+ */
+ if (isalpha(*(--fbeg))) {
+ fend = fbeg;
+ while (fend != limit && (!isspace(*fend))) fend++;
+ warning ("VAX-C-style include specification found, use '#include <filename.h>' !");
+ if (fend == limit) {
+ angle_brackets = 1;
+ /* If -I-, start with the first -I dir after the -I-. */
+ if (first_bracket_include)
+ search_start = first_bracket_include;
+ break;
+ }
+ }
+#endif
+
+ fail:
+ if (retried) {
+ error ("`#%s' expects \"FILENAME\" or <FILENAME>", keyword->name);
+ return 0;
+ } else {
+ trybuf = expand_to_temp_buffer (buf, limit, 0, 0);
+ buf = (U_CHAR *) alloca (trybuf.bufp - trybuf.buf + 1);
+ bcopy ((char *) trybuf.buf, (char *) buf, trybuf.bufp - trybuf.buf);
+ limit = buf + (trybuf.bufp - trybuf.buf);
+ free (trybuf.buf);
+ retried++;
+ goto get_filename;
+ }
+ }
+
+ /* For #include_next, skip in the search path
+ past the dir in which the containing file was found. */
+ if (skip_dirs) {
+ FILE_BUF *fp;
+ for (fp = &instack[indepth]; fp >= instack; fp--)
+ if (fp->fname != NULL) {
+ /* fp->dir is null if the containing file was specified
+ with an absolute file name. In that case, don't skip anything. */
+ if (fp->dir)
+ search_start = fp->dir->next;
+ break;
+ }
+ }
+
+ flen = fend - fbeg;
+
+ if (flen == 0)
+ {
+ error ("empty file name in `#%s'", keyword->name);
+ return 0;
+ }
+
+ /* Allocate this permanently, because it gets stored in the definitions
+ of macros. */
+ fname = (char *) xmalloc (max_include_len + flen + 4);
+ /* + 2 above for slash and terminating null. */
+ /* + 2 added for '.h' on VMS (to support '#include filename') */
+
+ /* If specified file name is absolute, just open it. */
+
+ if (*fbeg == '/') {
+ strncpy (fname, fbeg, flen);
+ fname[flen] = 0;
+ if (redundant_include_p (fname))
+ return 0;
+ if (importing)
+ f = lookup_import (fname, NULL_PTR);
+ else
+ f = open_include_file (fname, NULL_PTR);
+ if (f == -2)
+ return 0; /* Already included this file */
+ } else {
+ /* Search directory path, trying to open the file.
+ Copy each filename tried into FNAME. */
+
+ for (searchptr = search_start; searchptr; searchptr = searchptr->next) {
+ if (searchptr->fname) {
+ /* The empty string in a search path is ignored.
+ This makes it possible to turn off entirely
+ a standard piece of the list. */
+ if (searchptr->fname[0] == 0)
+ continue;
+ strcpy (fname, searchptr->fname);
+ strcat (fname, "/");
+ fname[strlen (fname) + flen] = 0;
+ } else {
+ fname[0] = 0;
+ }
+ strncat (fname, fbeg, flen);
+#ifdef VMS
+ /* Change this 1/2 Unix 1/2 VMS file specification into a
+ full VMS file specification */
+ if (searchptr->fname && (searchptr->fname[0] != 0)) {
+ /* Fix up the filename */
+ hack_vms_include_specification (fname);
+ } else {
+ /* This is a normal VMS filespec, so use it unchanged. */
+ strncpy (fname, fbeg, flen);
+ fname[flen] = 0;
+ /* if it's '#include filename', add the missing .h */
+ if (index(fname,'.')==NULL) {
+ strcat (fname, ".h");
+ }
+ }
+#endif /* VMS */
+ if (importing)
+ f = lookup_import (fname, searchptr);
+ else
+ f = open_include_file (fname, searchptr);
+ if (f == -2)
+ return 0; /* Already included this file */
+#ifdef EACCES
+ else if (f == -1 && errno == EACCES)
+ warning ("Header file %s exists, but is not readable", fname);
+#endif
+ if (redundant_include_p (fname)) {
+ close (f);
+ return 0;
+ }
+ if (f >= 0)
+ break;
+ }
+ }
+
+ if (f < 0) {
+ /* A file that was not found. */
+
+ strncpy (fname, fbeg, flen);
+ fname[flen] = 0;
+ /* If generating dependencies and -MG was specified, we assume missing
+ files are leaf files, living in the same directory as the source file
+ or other similar place; these missing files may be generated from
+ other files and may not exist yet (eg: y.tab.h). */
+ if (print_deps_missing_files
+ && print_deps > (angle_brackets || (system_include_depth > 0)))
+ {
+ /* If it was requested as a system header file,
+ then assume it belongs in the first place to look for such. */
+ if (angle_brackets)
+ {
+ for (searchptr = search_start; searchptr; searchptr = searchptr->next)
+ {
+ if (searchptr->fname)
+ {
+ char *p;
+
+ if (searchptr->fname[0] == 0)
+ continue;
+ p = xmalloc (strlen (searchptr->fname)
+ + strlen (fname) + 2);
+ strcpy (p, searchptr->fname);
+ strcat (p, "/");
+ strcat (p, fname);
+ deps_output (p, ' ');
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* Otherwise, omit the directory, as if the file existed
+ in the directory with the source. */
+ deps_output (fname, ' ');
+ }
+ }
+ /* If -M was specified, and this header file won't be added to the
+ dependency list, then don't count this as an error, because we can
+ still produce correct output. Otherwise, we can't produce correct
+ output, because there may be dependencies we need inside the missing
+ file, and we don't know what directory this missing file exists in. */
+ else if (print_deps
+ && (print_deps <= (angle_brackets || (system_include_depth > 0))))
+ warning ("No include path in which to find %s", fname);
+ else if (search_start)
+ error_from_errno (fname);
+ else
+ error ("No include path in which to find %s", fname);
+ } else {
+ struct stat stat_f;
+
+ /* Check to see if this include file is a once-only include file.
+ If so, give up. */
+
+ struct file_name_list* ptr;
+
+ for (ptr = dont_repeat_files; ptr; ptr = ptr->next) {
+ if (!strcmp (ptr->fname, fname)) {
+ close (f);
+ return 0; /* This file was once'd. */
+ }
+ }
+
+ for (ptr = all_include_files; ptr; ptr = ptr->next) {
+ if (!strcmp (ptr->fname, fname))
+ break; /* This file was included before. */
+ }
+
+ if (ptr == 0) {
+ /* This is the first time for this file. */
+ /* Add it to list of files included. */
+
+ ptr = (struct file_name_list *) xmalloc (sizeof (struct file_name_list));
+ ptr->control_macro = 0;
+ ptr->c_system_include_path = 0;
+ ptr->next = all_include_files;
+ all_include_files = ptr;
+ ptr->fname = savestring (fname);
+ ptr->got_name_map = 0;
+
+ /* For -M, add this file to the dependencies. */
+ if (print_deps > (angle_brackets || (system_include_depth > 0)))
+ deps_output (fname, ' ');
+ }
+
+ /* Handle -H option. */
+ if (print_include_names) {
+ output_dots (stderr, indepth);
+ fprintf (stderr, "%s\n", fname);
+ }
+
+ if (angle_brackets)
+ system_include_depth++;
+
+ /* Actually process the file. */
+ add_import (f, fname); /* Record file on "seen" list for #import. */
+
+ pcftry = (char *) alloca (strlen (fname) + 30);
+ pcfbuf = 0;
+ pcfnum = 0;
+
+ fstat (f, &stat_f);
+
+ if (!no_precomp)
+ do {
+ sprintf (pcftry, "%s%d", fname, pcfnum++);
+
+ pcf = open (pcftry, O_RDONLY, 0666);
+ if (pcf != -1)
+ {
+ struct stat s;
+
+ fstat (pcf, &s);
+ if (bcmp ((char *) &stat_f.st_ino, (char *) &s.st_ino,
+ sizeof (s.st_ino))
+ || stat_f.st_dev != s.st_dev)
+ {
+ pcfbuf = check_precompiled (pcf, fname, &pcfbuflimit);
+ /* Don't need it any more. */
+ close (pcf);
+ }
+ else
+ {
+ /* Don't need it at all. */
+ close (pcf);
+ break;
+ }
+ }
+ } while (pcf != -1 && !pcfbuf);
+
+ /* Actually process the file */
+ if (pcfbuf) {
+ pcfname = xmalloc (strlen (pcftry) + 1);
+ strcpy (pcfname, pcftry);
+ pcfinclude (pcfbuf, pcfbuflimit, fname, op);
+ }
+ else
+ finclude (f, fname, op, is_system_include (fname), searchptr);
+
+ if (angle_brackets)
+ system_include_depth--;
+ }
+ return 0;
+}
+
+/* Return nonzero if there is no need to include file NAME
+ because it has already been included and it contains a conditional
+ to make a repeated include do nothing. */
+
+static int
+redundant_include_p (name)
+ char *name;
+{
+ struct file_name_list *l = all_include_files;
+ for (; l; l = l->next)
+ if (! strcmp (name, l->fname)
+ && l->control_macro
+ && lookup (l->control_macro, -1, -1))
+ return 1;
+ return 0;
+}
+
+/* Return nonzero if the given FILENAME is an absolute pathname which
+ designates a file within one of the known "system" include file
+ directories. We assume here that if the given FILENAME looks like
+ it is the name of a file which resides either directly in a "system"
+ include file directory, or within any subdirectory thereof, then the
+ given file must be a "system" include file. This function tells us
+ if we should suppress pedantic errors/warnings for the given FILENAME.
+
+ The value is 2 if the file is a C-language system header file
+ for which C++ should (on most systems) assume `extern "C"'. */
+
+static int
+is_system_include (filename)
+ register char *filename;
+{
+ struct file_name_list *searchptr;
+
+ for (searchptr = first_system_include; searchptr;
+ searchptr = searchptr->next)
+ if (searchptr->fname) {
+ register char *sys_dir = searchptr->fname;
+ register unsigned length = strlen (sys_dir);
+
+ if (! strncmp (sys_dir, filename, length) && filename[length] == '/')
+ {
+ if (searchptr->c_system_include_path)
+ return 2;
+ else
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* The file_name_map structure holds a mapping of file names for a
+ particular directory. This mapping is read from the file named
+ FILE_NAME_MAP_FILE in that directory. Such a file can be used to
+ map filenames on a file system with severe filename restrictions,
+ such as DOS. The format of the file name map file is just a series
+ of lines with two tokens on each line. The first token is the name
+ to map, and the second token is the actual name to use. */
+
+struct file_name_map
+{
+ struct file_name_map *map_next;
+ char *map_from;
+ char *map_to;
+};
+
+#define FILE_NAME_MAP_FILE "header.gcc"
+
+/* Read a space delimited string of unlimited length from a stdio
+ file. */
+
+static char *
+read_filename_string (ch, f)
+ int ch;
+ FILE *f;
+{
+ char *alloc, *set;
+ int len;
+
+ len = 20;
+ set = alloc = xmalloc (len + 1);
+ if (! is_space[ch])
+ {
+ *set++ = ch;
+ while ((ch = getc (f)) != EOF && ! is_space[ch])
+ {
+ if (set - alloc == len)
+ {
+ len *= 2;
+ alloc = xrealloc (alloc, len + 1);
+ set = alloc + len / 2;
+ }
+ *set++ = ch;
+ }
+ }
+ *set = '\0';
+ ungetc (ch, f);
+ return alloc;
+}
+
+/* Read the file name map file for DIRNAME. */
+
+static struct file_name_map *
+read_name_map (dirname)
+ char *dirname;
+{
+ /* This structure holds a linked list of file name maps, one per
+ directory. */
+ struct file_name_map_list
+ {
+ struct file_name_map_list *map_list_next;
+ char *map_list_name;
+ struct file_name_map *map_list_map;
+ };
+ static struct file_name_map_list *map_list;
+ register struct file_name_map_list *map_list_ptr;
+ char *name;
+ FILE *f;
+
+ for (map_list_ptr = map_list; map_list_ptr;
+ map_list_ptr = map_list_ptr->map_list_next)
+ if (! strcmp (map_list_ptr->map_list_name, dirname))
+ return map_list_ptr->map_list_map;
+
+ map_list_ptr = ((struct file_name_map_list *)
+ xmalloc (sizeof (struct file_name_map_list)));
+ map_list_ptr->map_list_name = savestring (dirname);
+ map_list_ptr->map_list_map = NULL;
+
+ name = (char *) alloca (strlen (dirname) + strlen (FILE_NAME_MAP_FILE) + 2);
+ strcpy (name, dirname);
+ if (*dirname)
+ strcat (name, "/");
+ strcat (name, FILE_NAME_MAP_FILE);
+ f = fopen (name, "r");
+ if (!f)
+ map_list_ptr->map_list_map = NULL;
+ else
+ {
+ int ch;
+ int dirlen = strlen (dirname);
+
+ while ((ch = getc (f)) != EOF)
+ {
+ char *from, *to;
+ struct file_name_map *ptr;
+
+ if (is_space[ch])
+ continue;
+ from = read_filename_string (ch, f);
+ while ((ch = getc (f)) != EOF && is_hor_space[ch])
+ ;
+ to = read_filename_string (ch, f);
+
+ ptr = ((struct file_name_map *)
+ xmalloc (sizeof (struct file_name_map)));
+ ptr->map_from = from;
+
+ /* Make the real filename absolute. */
+ if (*to == '/')
+ ptr->map_to = to;
+ else
+ {
+ ptr->map_to = xmalloc (dirlen + strlen (to) + 2);
+ strcpy (ptr->map_to, dirname);
+ ptr->map_to[dirlen] = '/';
+ strcpy (ptr->map_to + dirlen + 1, to);
+ free (to);
+ }
+
+ ptr->map_next = map_list_ptr->map_list_map;
+ map_list_ptr->map_list_map = ptr;
+
+ while ((ch = getc (f)) != '\n')
+ if (ch == EOF)
+ break;
+ }
+ fclose (f);
+ }
+
+ map_list_ptr->map_list_next = map_list;
+ map_list = map_list_ptr;
+
+ return map_list_ptr->map_list_map;
+}
+
+/* Try to open include file FILENAME. SEARCHPTR is the directory
+ being tried from the include file search path. This function maps
+ filenames on file systems based on information read by
+ read_name_map. */
+
+static int
+open_include_file (filename, searchptr)
+ char *filename;
+ struct file_name_list *searchptr;
+{
+ register struct file_name_map *map;
+ register char *from;
+ char *p, *dir;
+
+ if (searchptr && ! searchptr->got_name_map)
+ {
+ searchptr->name_map = read_name_map (searchptr->fname
+ ? searchptr->fname : ".");
+ searchptr->got_name_map = 1;
+ }
+
+ /* First check the mapping for the directory we are using. */
+ if (searchptr && searchptr->name_map)
+ {
+ from = filename;
+ if (searchptr->fname)
+ from += strlen (searchptr->fname) + 1;
+ for (map = searchptr->name_map; map; map = map->map_next)
+ {
+ if (! strcmp (map->map_from, from))
+ {
+ /* Found a match. */
+ return open (map->map_to, O_RDONLY, 0666);
+ }
+ }
+ }
+
+ /* Try to find a mapping file for the particular directory we are
+ looking in. Thus #include <sys/types.h> will look up sys/types.h
+ in /usr/include/header.gcc and look up types.h in
+ /usr/include/sys/header.gcc. */
+ p = rindex (filename, '/');
+ if (! p)
+ p = filename;
+ if (searchptr
+ && searchptr->fname
+ && strlen (searchptr->fname) == p - filename
+ && ! strncmp (searchptr->fname, filename, p - filename))
+ {
+ /* FILENAME is in SEARCHPTR, which we've already checked. */
+ return open (filename, O_RDONLY, 0666);
+ }
+
+ if (p == filename)
+ {
+ dir = ".";
+ from = filename;
+ }
+ else
+ {
+ dir = (char *) alloca (p - filename + 1);
+ bcopy (filename, dir, p - filename);
+ dir[p - filename] = '\0';
+ from = p + 1;
+ }
+ for (map = read_name_map (dir); map; map = map->map_next)
+ if (! strcmp (map->map_from, from))
+ return open (map->map_to, O_RDONLY, 0666);
+
+ return open (filename, O_RDONLY, 0666);
+}
+
+/* Process the contents of include file FNAME, already open on descriptor F,
+ with output to OP.
+ SYSTEM_HEADER_P is 1 if this file resides in any one of the known
+ "system" include directories (as decided by the `is_system_include'
+ function above).
+ DIRPTR is the link in the dir path through which this file was found,
+ or 0 if the file name was absolute. */
+
+static void
+finclude (f, fname, op, system_header_p, dirptr)
+ int f;
+ char *fname;
+ FILE_BUF *op;
+ int system_header_p;
+ struct file_name_list *dirptr;
+{
+ int st_mode;
+ long st_size;
+ long i;
+ FILE_BUF *fp; /* For input stack frame */
+ int missing_newline = 0;
+
+ CHECK_DEPTH (return;);
+
+ if (file_size_and_mode (f, &st_mode, &st_size) < 0)
+ {
+ perror_with_name (fname);
+ close (f);
+ return;
+ }
+
+ fp = &instack[indepth + 1];
+ bzero ((char *) fp, sizeof (FILE_BUF));
+ fp->nominal_fname = fp->fname = fname;
+ fp->length = 0;
+ fp->lineno = 1;
+ fp->if_stack = if_stack;
+ fp->system_header_p = system_header_p;
+ fp->dir = dirptr;
+
+ if (S_ISREG (st_mode)) {
+ fp->buf = (U_CHAR *) xmalloc (st_size + 2);
+ fp->bufp = fp->buf;
+
+ /* Read the file contents, knowing that st_size is an upper bound
+ on the number of bytes we can read. */
+ fp->length = safe_read (f, fp->buf, st_size);
+ if (fp->length < 0) goto nope;
+ }
+ else if (S_ISDIR (st_mode)) {
+ error ("directory `%s' specified in #include", fname);
+ close (f);
+ return;
+ } else {
+ /* Cannot count its file size before reading.
+ First read the entire file into heap and
+ copy them into buffer on stack. */
+
+ int bsize = 2000;
+
+ st_size = 0;
+ fp->buf = (U_CHAR *) xmalloc (bsize + 2);
+
+ for (;;) {
+ i = safe_read (f, fp->buf + st_size, bsize - st_size);
+ if (i < 0)
+ goto nope; /* error! */
+ st_size += i;
+ if (st_size != bsize)
+ break; /* End of file */
+ bsize *= 2;
+ fp->buf = (U_CHAR *) xrealloc (fp->buf, bsize + 2);
+ }
+ fp->bufp = fp->buf;
+ fp->length = st_size;
+ }
+
+ if ((fp->length > 0 && fp->buf[fp->length - 1] != '\n')
+ /* Backslash-newline at end is not good enough. */
+ || (fp->length > 1 && fp->buf[fp->length - 2] == '\\')) {
+ fp->buf[fp->length++] = '\n';
+ missing_newline = 1;
+ }
+ fp->buf[fp->length] = '\0';
+
+ /* Close descriptor now, so nesting does not use lots of descriptors. */
+ close (f);
+
+ /* Must do this before calling trigraph_pcp, so that the correct file name
+ will be printed in warning messages. */
+
+ indepth++;
+ input_file_stack_tick++;
+
+ if (!no_trigraphs)
+ trigraph_pcp (fp);
+
+ output_line_command (fp, op, 0, enter_file);
+ rescan (op, 0);
+
+ if (missing_newline)
+ fp->lineno--;
+
+ if (pedantic && missing_newline)
+ pedwarn ("file does not end in newline");
+
+ indepth--;
+ input_file_stack_tick++;
+ output_line_command (&instack[indepth], op, 0, leave_file);
+ free (fp->buf);
+ return;
+
+ nope:
+
+ perror_with_name (fname);
+ close (f);
+ free (fp->buf);
+}
+
+/* Record that inclusion of the file named FILE
+ should be controlled by the macro named MACRO_NAME.
+ This means that trying to include the file again
+ will do something if that macro is defined. */
+
+static void
+record_control_macro (file, macro_name)
+ char *file;
+ U_CHAR *macro_name;
+{
+ struct file_name_list *new;
+
+ for (new = all_include_files; new; new = new->next) {
+ if (!strcmp (new->fname, file)) {
+ new->control_macro = macro_name;
+ return;
+ }
+ }
+
+ /* If the file is not in all_include_files, something's wrong. */
+ abort ();
+}
+
+/* Maintain and search list of included files, for #import. */
+
+#define IMPORT_HASH_SIZE 31
+
+struct import_file {
+ char *name;
+ ino_t inode;
+ dev_t dev;
+ struct import_file *next;
+};
+
+/* Hash table of files already included with #include or #import. */
+
+static struct import_file *import_hash_table[IMPORT_HASH_SIZE];
+
+/* Hash a file name for import_hash_table. */
+
+static int
+import_hash (f)
+ char *f;
+{
+ int val = 0;
+
+ while (*f) val += *f++;
+ return (val%IMPORT_HASH_SIZE);
+}
+
+/* Search for file FILENAME in import_hash_table.
+ Return -2 if found, either a matching name or a matching inode.
+ Otherwise, open the file and return a file descriptor if successful
+ or -1 if unsuccessful. */
+
+static int
+lookup_import (filename, searchptr)
+ char *filename;
+ struct file_name_list *searchptr;
+{
+ struct import_file *i;
+ int h;
+ int hashval;
+ struct stat sb;
+ int fd;
+
+ hashval = import_hash (filename);
+
+ /* Attempt to find file in list of already included files */
+ i = import_hash_table[hashval];
+
+ while (i) {
+ if (!strcmp (filename, i->name))
+ return -2; /* return found */
+ i = i->next;
+ }
+ /* Open it and try a match on inode/dev */
+ fd = open_include_file (filename, searchptr);
+ if (fd < 0)
+ return fd;
+ fstat (fd, &sb);
+ for (h = 0; h < IMPORT_HASH_SIZE; h++) {
+ i = import_hash_table[h];
+ while (i) {
+ /* Compare the inode and the device.
+ Supposedly on some systems the inode is not a scalar. */
+ if (!bcmp ((char *) &i->inode, (char *) &sb.st_ino, sizeof (sb.st_ino))
+ && i->dev == sb.st_dev) {
+ close (fd);
+ return -2; /* return found */
+ }
+ i = i->next;
+ }
+ }
+ return fd; /* Not found, return open file */
+}
+
+/* Add the file FNAME, open on descriptor FD, to import_hash_table. */
+
+static void
+add_import (fd, fname)
+ int fd;
+ char *fname;
+{
+ struct import_file *i;
+ int hashval;
+ struct stat sb;
+
+ hashval = import_hash (fname);
+ fstat (fd, &sb);
+ i = (struct import_file *)xmalloc (sizeof (struct import_file));
+ i->name = (char *)xmalloc (strlen (fname)+1);
+ strcpy (i->name, fname);
+ bcopy ((char *) &sb.st_ino, (char *) &i->inode, sizeof (sb.st_ino));
+ i->dev = sb.st_dev;
+ i->next = import_hash_table[hashval];
+ import_hash_table[hashval] = i;
+}
+
+/* Load the specified precompiled header into core, and verify its
+ preconditions. PCF indicates the file descriptor to read, which must
+ be a regular file. FNAME indicates the file name of the original
+ header. *LIMIT will be set to an address one past the end of the file.
+ If the preconditions of the file are not satisfied, the buffer is
+ freed and we return 0. If the preconditions are satisfied, return
+ the address of the buffer following the preconditions. The buffer, in
+ this case, should never be freed because various pieces of it will
+ be referred to until all precompiled strings are output at the end of
+ the run.
+*/
+static char *
+check_precompiled (pcf, fname, limit)
+ int pcf;
+ char *fname;
+ char **limit;
+{
+ int st_mode;
+ long st_size;
+ int length = 0;
+ char *buf;
+ char *cp;
+
+ if (pcp_outfile)
+ return 0;
+
+ if (file_size_and_mode (pcf, &st_mode, &st_size) < 0)
+ return 0;
+
+ if (S_ISREG (st_mode))
+ {
+ buf = xmalloc (st_size + 2);
+ length = safe_read (pcf, buf, st_size);
+ if (length < 0)
+ goto nope;
+ }
+ else
+ abort ();
+
+ if (length > 0 && buf[length-1] != '\n')
+ buf[length++] = '\n';
+ buf[length] = '\0';
+
+ *limit = buf + length;
+
+ /* File is in core. Check the preconditions. */
+ if (!check_preconditions (buf))
+ goto nope;
+ for (cp = buf; *cp; cp++)
+ ;
+#ifdef DEBUG_PCP
+ fprintf (stderr, "Using preinclude %s\n", fname);
+#endif
+ return cp + 1;
+
+ nope:
+#ifdef DEBUG_PCP
+ fprintf (stderr, "Cannot use preinclude %s\n", fname);
+#endif
+ free (buf);
+ return 0;
+}
+
+/* PREC (null terminated) points to the preconditions of a
+ precompiled header. These are a series of #define and #undef
+ lines which must match the current contents of the hash
+ table. */
+static int
+check_preconditions (prec)
+ char *prec;
+{
+ MACRODEF mdef;
+ char *lineend;
+
+ while (*prec) {
+ lineend = (char *) index (prec, '\n');
+
+ if (*prec++ != '#') {
+ error ("Bad format encountered while reading precompiled file");
+ return 0;
+ }
+ if (!strncmp (prec, "define", 6)) {
+ HASHNODE *hp;
+
+ prec += 6;
+ mdef = create_definition (prec, lineend, NULL_PTR);
+
+ if (mdef.defn == 0)
+ abort ();
+
+ if ((hp = lookup (mdef.symnam, mdef.symlen, -1)) == NULL
+ || (hp->type != T_MACRO && hp->type != T_CONST)
+ || (hp->type == T_MACRO
+ && !compare_defs (mdef.defn, hp->value.defn)
+ && (mdef.defn->length != 2
+ || mdef.defn->expansion[0] != '\n'
+ || mdef.defn->expansion[1] != ' ')))
+ return 0;
+ } else if (!strncmp (prec, "undef", 5)) {
+ char *name;
+ int len;
+
+ prec += 5;
+ while (is_hor_space[(U_CHAR) *prec])
+ prec++;
+ name = prec;
+ while (is_idchar[(U_CHAR) *prec])
+ prec++;
+ len = prec - name;
+
+ if (lookup (name, len, -1))
+ return 0;
+ } else {
+ error ("Bad format encountered while reading precompiled file");
+ return 0;
+ }
+ prec = lineend + 1;
+ }
+ /* They all passed successfully */
+ return 1;
+}
+
+/* Process the main body of a precompiled file. BUF points to the
+ string section of the file, following the preconditions. LIMIT is one
+ character past the end. NAME is the name of the file being read
+ in. OP is the main output buffer */
+static void
+pcfinclude (buf, limit, name, op)
+ U_CHAR *buf, *limit, *name;
+ FILE_BUF *op;
+{
+ FILE_BUF tmpbuf;
+ int nstrings;
+ U_CHAR *cp = buf;
+
+ /* First in the file comes 4 bytes indicating the number of strings, */
+ /* in network byte order. (MSB first). */
+ nstrings = *cp++;
+ nstrings = (nstrings << 8) | *cp++;
+ nstrings = (nstrings << 8) | *cp++;
+ nstrings = (nstrings << 8) | *cp++;
+
+ /* Looping over each string... */
+ while (nstrings--) {
+ U_CHAR *string_start;
+ U_CHAR *endofthiskey;
+ STRINGDEF *str;
+ int nkeys;
+
+ /* Each string starts with a STRINGDEF structure (str), followed */
+ /* by the text of the string (string_start) */
+
+ /* First skip to a longword boundary */
+ /* ??? Why a 4-byte boundary? On all machines? */
+ /* NOTE: This works correctly even if HOST_WIDE_INT
+ is narrower than a pointer.
+ Do not try risky measures here to get another type to use!
+ Do not include stddef.h--it will fail! */
+ if ((HOST_WIDE_INT) cp & 3)
+ cp += 4 - ((HOST_WIDE_INT) cp & 3);
+
+ /* Now get the string. */
+ str = (STRINGDEF *) cp;
+ string_start = cp += sizeof (STRINGDEF);
+
+ for (; *cp; cp++) /* skip the string */
+ ;
+
+ /* We need to macro expand the string here to ensure that the
+ proper definition environment is in place. If it were only
+ expanded when we find out it is needed, macros necessary for
+ its proper expansion might have had their definitions changed. */
+ tmpbuf = expand_to_temp_buffer (string_start, cp++, 0, 0);
+ /* Lineno is already set in the precompiled file */
+ str->contents = tmpbuf.buf;
+ str->len = tmpbuf.length;
+ str->writeflag = 0;
+ str->filename = name;
+ str->output_mark = outbuf.bufp - outbuf.buf;
+
+ str->chain = 0;
+ *stringlist_tailp = str;
+ stringlist_tailp = &str->chain;
+
+ /* Next comes a fourbyte number indicating the number of keys */
+ /* for this string. */
+ nkeys = *cp++;
+ nkeys = (nkeys << 8) | *cp++;
+ nkeys = (nkeys << 8) | *cp++;
+ nkeys = (nkeys << 8) | *cp++;
+
+ /* If this number is -1, then the string is mandatory. */
+ if (nkeys == -1)
+ str->writeflag = 1;
+ else
+ /* Otherwise, for each key, */
+ for (; nkeys--; free (tmpbuf.buf), cp = endofthiskey + 1) {
+ KEYDEF *kp = (KEYDEF *) cp;
+ HASHNODE *hp;
+
+ /* It starts with a KEYDEF structure */
+ cp += sizeof (KEYDEF);
+
+ /* Find the end of the key. At the end of this for loop we
+ advance CP to the start of the next key using this variable. */
+ endofthiskey = cp + strlen (cp);
+ kp->str = str;
+
+ /* Expand the key, and enter it into the hash table. */
+ tmpbuf = expand_to_temp_buffer (cp, endofthiskey, 0, 0);
+ tmpbuf.bufp = tmpbuf.buf;
+
+ while (is_hor_space[*tmpbuf.bufp])
+ tmpbuf.bufp++;
+ if (!is_idstart[*tmpbuf.bufp]
+ || tmpbuf.bufp == tmpbuf.buf + tmpbuf.length) {
+ str->writeflag = 1;
+ continue;
+ }
+
+ hp = lookup (tmpbuf.bufp, -1, -1);
+ if (hp == NULL) {
+ kp->chain = 0;
+ install (tmpbuf.bufp, -1, T_PCSTRING, 0, (char *) kp, -1);
+ }
+ else if (hp->type == T_PCSTRING) {
+ kp->chain = hp->value.keydef;
+ hp->value.keydef = kp;
+ }
+ else
+ str->writeflag = 1;
+ }
+ }
+ /* This output_line_command serves to switch us back to the current
+ input file in case some of these strings get output (which will
+ result in line commands for the header file being output). */
+ output_line_command (&instack[indepth], op, 0, enter_file);
+}
+
+/* Called from rescan when it hits a key for strings. Mark them all */
+ /* used and clean up. */
+static void
+pcstring_used (hp)
+ HASHNODE *hp;
+{
+ KEYDEF *kp;
+
+ for (kp = hp->value.keydef; kp; kp = kp->chain)
+ kp->str->writeflag = 1;
+ delete_macro (hp);
+}
+
+/* Write the output, interspersing precompiled strings in their */
+ /* appropriate places. */
+static void
+write_output ()
+{
+ STRINGDEF *next_string;
+ U_CHAR *cur_buf_loc;
+ int line_command_len = 80;
+ char *line_command = xmalloc (line_command_len);
+ int len;
+
+ /* In each run through the loop, either cur_buf_loc == */
+ /* next_string_loc, in which case we print a series of strings, or */
+ /* it is less than next_string_loc, in which case we write some of */
+ /* the buffer. */
+ cur_buf_loc = outbuf.buf;
+ next_string = stringlist;
+
+ while (cur_buf_loc < outbuf.bufp || next_string) {
+ if (next_string
+ && cur_buf_loc - outbuf.buf == next_string->output_mark) {
+ if (next_string->writeflag) {
+ len = 4 * strlen (next_string->filename) + 32;
+ while (len > line_command_len)
+ line_command = xrealloc (line_command,
+ line_command_len *= 2);
+ sprintf (line_command, "\n# %d ", next_string->lineno);
+ strcpy (quote_string (line_command + strlen (line_command),
+ next_string->filename),
+ "\n");
+ safe_write (fileno (stdout), line_command, strlen (line_command));
+ safe_write (fileno (stdout), next_string->contents, next_string->len);
+ }
+ next_string = next_string->chain;
+ }
+ else {
+ len = (next_string
+ ? (next_string->output_mark
+ - (cur_buf_loc - outbuf.buf))
+ : outbuf.bufp - cur_buf_loc);
+
+ safe_write (fileno (stdout), cur_buf_loc, len);
+ cur_buf_loc += len;
+ }
+ }
+ free (line_command);
+}
+
+/* Pass a directive through to the output file.
+ BUF points to the contents of the directive, as a contiguous string.
+ LIMIT points to the first character past the end of the directive.
+ KEYWORD is the keyword-table entry for the directive. */
+
+static void
+pass_thru_directive (buf, limit, op, keyword)
+ U_CHAR *buf, *limit;
+ FILE_BUF *op;
+ struct directive *keyword;
+{
+ register unsigned keyword_length = keyword->length;
+
+ check_expand (op, 1 + keyword_length + (limit - buf));
+ *op->bufp++ = '#';
+ bcopy (keyword->name, (char *) op->bufp, keyword_length);
+ op->bufp += keyword_length;
+ if (limit != buf && buf[0] != ' ')
+ *op->bufp++ = ' ';
+ bcopy ((char *) buf, (char *) op->bufp, limit - buf);
+ op->bufp += (limit - buf);
+#if 0
+ *op->bufp++ = '\n';
+ /* Count the line we have just made in the output,
+ to get in sync properly. */
+ op->lineno++;
+#endif
+}
+
+/* The arglist structure is built by do_define to tell
+ collect_definition where the argument names begin. That
+ is, for a define like "#define f(x,y,z) foo+x-bar*y", the arglist
+ would contain pointers to the strings x, y, and z.
+ Collect_definition would then build a DEFINITION node,
+ with reflist nodes pointing to the places x, y, and z had
+ appeared. So the arglist is just convenience data passed
+ between these two routines. It is not kept around after
+ the current #define has been processed and entered into the
+ hash table. */
+
+struct arglist {
+ struct arglist *next;
+ U_CHAR *name;
+ int length;
+ int argno;
+ char rest_args;
+};
+
+/* Create a DEFINITION node from a #define directive. Arguments are
+ as for do_define. */
+static MACRODEF
+create_definition (buf, limit, op)
+ U_CHAR *buf, *limit;
+ FILE_BUF *op;
+{
+ U_CHAR *bp; /* temp ptr into input buffer */
+ U_CHAR *symname; /* remember where symbol name starts */
+ int sym_length; /* and how long it is */
+ int line = instack[indepth].lineno;
+ char *file = instack[indepth].nominal_fname;
+ int rest_args = 0;
+
+ DEFINITION *defn;
+ int arglengths = 0; /* Accumulate lengths of arg names
+ plus number of args. */
+ MACRODEF mdef;
+
+ bp = buf;
+
+ while (is_hor_space[*bp])
+ bp++;
+
+ symname = bp; /* remember where it starts */
+ sym_length = check_macro_name (bp, "macro");
+ bp += sym_length;
+
+ /* Lossage will occur if identifiers or control keywords are broken
+ across lines using backslash. This is not the right place to take
+ care of that. */
+
+ if (*bp == '(') {
+ struct arglist *arg_ptrs = NULL;
+ int argno = 0;
+
+ bp++; /* skip '(' */
+ SKIP_WHITE_SPACE (bp);
+
+ /* Loop over macro argument names. */
+ while (*bp != ')') {
+ struct arglist *temp;
+
+ temp = (struct arglist *) alloca (sizeof (struct arglist));
+ temp->name = bp;
+ temp->next = arg_ptrs;
+ temp->argno = argno++;
+ temp->rest_args = 0;
+ arg_ptrs = temp;
+
+ if (rest_args)
+ pedwarn ("another parameter follows `%s'",
+ rest_extension);
+
+ if (!is_idstart[*bp])
+ pedwarn ("invalid character in macro parameter name");
+
+ /* Find the end of the arg name. */
+ while (is_idchar[*bp]) {
+ bp++;
+ /* do we have a "special" rest-args extension here? */
+ if (limit - bp > REST_EXTENSION_LENGTH &&
+ strncmp (rest_extension, bp, REST_EXTENSION_LENGTH) == 0) {
+ rest_args = 1;
+ temp->rest_args = 1;
+ break;
+ }
+ }
+ temp->length = bp - temp->name;
+ if (rest_args == 1)
+ bp += REST_EXTENSION_LENGTH;
+ arglengths += temp->length + 2;
+ SKIP_WHITE_SPACE (bp);
+ if (temp->length == 0 || (*bp != ',' && *bp != ')')) {
+ error ("badly punctuated parameter list in `#define'");
+ goto nope;
+ }
+ if (*bp == ',') {
+ bp++;
+ SKIP_WHITE_SPACE (bp);
+ }
+ if (bp >= limit) {
+ error ("unterminated parameter list in `#define'");
+ goto nope;
+ }
+ {
+ struct arglist *otemp;
+
+ for (otemp = temp->next; otemp != NULL; otemp = otemp->next)
+ if (temp->length == otemp->length &&
+ strncmp (temp->name, otemp->name, temp->length) == 0) {
+ U_CHAR *name;
+
+ name = (U_CHAR *) alloca (temp->length + 1);
+ (void) strncpy (name, temp->name, temp->length);
+ name[temp->length] = '\0';
+ error ("duplicate argument name `%s' in `#define'", name);
+ goto nope;
+ }
+ }
+ }
+
+ ++bp; /* skip paren */
+ /* Skip spaces and tabs if any. */
+ while (bp < limit && (*bp == ' ' || *bp == '\t'))
+ ++bp;
+ /* now everything from bp before limit is the definition. */
+ defn = collect_expansion (bp, limit, argno, arg_ptrs);
+ defn->rest_args = rest_args;
+
+ /* Now set defn->args.argnames to the result of concatenating
+ the argument names in reverse order
+ with comma-space between them. */
+ defn->args.argnames = (U_CHAR *) xmalloc (arglengths + 1);
+ {
+ struct arglist *temp;
+ int i = 0;
+ for (temp = arg_ptrs; temp; temp = temp->next) {
+ bcopy (temp->name, &defn->args.argnames[i], temp->length);
+ i += temp->length;
+ if (temp->next != 0) {
+ defn->args.argnames[i++] = ',';
+ defn->args.argnames[i++] = ' ';
+ }
+ }
+ defn->args.argnames[i] = 0;
+ }
+ } else {
+ /* Simple expansion or empty definition. */
+
+ /* Skip spaces and tabs if any. */
+ while (bp < limit && (*bp == ' ' || *bp == '\t'))
+ ++bp;
+ /* Now everything from bp before limit is the definition. */
+ defn = collect_expansion (bp, limit, -1, NULL_PTR);
+ defn->args.argnames = (U_CHAR *) "";
+ }
+
+ defn->line = line;
+ defn->file = file;
+
+ /* OP is null if this is a predefinition */
+ defn->predefined = !op;
+ mdef.defn = defn;
+ mdef.symnam = symname;
+ mdef.symlen = sym_length;
+
+ return mdef;
+
+ nope:
+ mdef.defn = 0;
+ return mdef;
+}
+
+/* Process a #define command.
+BUF points to the contents of the #define command, as a contiguous string.
+LIMIT points to the first character past the end of the definition.
+KEYWORD is the keyword-table entry for #define. */
+
+static int
+do_define (buf, limit, op, keyword)
+ U_CHAR *buf, *limit;
+ FILE_BUF *op;
+ struct directive *keyword;
+{
+ int hashcode;
+ MACRODEF mdef;
+
+ /* If this is a precompiler run (with -pcp) pass thru #define commands. */
+ if (pcp_outfile && op)
+ pass_thru_directive (buf, limit, op, keyword);
+
+ mdef = create_definition (buf, limit, op);
+ if (mdef.defn == 0)
+ goto nope;
+
+ hashcode = hashf (mdef.symnam, mdef.symlen, HASHSIZE);
+
+ {
+ HASHNODE *hp;
+ if ((hp = lookup (mdef.symnam, mdef.symlen, hashcode)) != NULL) {
+ int ok = 0;
+ /* Redefining a precompiled key is ok. */
+ if (hp->type == T_PCSTRING)
+ ok = 1;
+ /* Redefining a macro is ok if the definitions are the same. */
+ else if (hp->type == T_MACRO)
+ ok = ! compare_defs (mdef.defn, hp->value.defn);
+ /* Redefining a constant is ok with -D. */
+ else if (hp->type == T_CONST)
+ ok = ! done_initializing;
+ /* Print the warning if it's not ok. */
+ if (!ok) {
+ U_CHAR *msg; /* what pain... */
+
+ /* If we are passing through #define and #undef directives, do
+ that for this re-definition now. */
+ if (debug_output && op)
+ pass_thru_directive (buf, limit, op, keyword);
+
+ msg = (U_CHAR *) alloca (mdef.symlen + 22);
+ *msg = '`';
+ bcopy ((char *) mdef.symnam, (char *) (msg + 1), mdef.symlen);
+ strcpy ((char *) (msg + mdef.symlen + 1), "' redefined");
+ pedwarn (msg);
+ if (hp->type == T_MACRO)
+ pedwarn_with_file_and_line (hp->value.defn->file, hp->value.defn->line,
+ "this is the location of the previous definition");
+ }
+ /* Replace the old definition. */
+ hp->type = T_MACRO;
+ hp->value.defn = mdef.defn;
+ } else {
+ /* If we are passing through #define and #undef directives, do
+ that for this new definition now. */
+ if (debug_output && op)
+ pass_thru_directive (buf, limit, op, keyword);
+ install (mdef.symnam, mdef.symlen, T_MACRO, 0,
+ (char *) mdef.defn, hashcode);
+ }
+ }
+
+ return 0;
+
+nope:
+
+ return 1;
+}
+
+/* Check a purported macro name SYMNAME, and yield its length.
+ USAGE is the kind of name this is intended for. */
+
+static int
+check_macro_name (symname, usage)
+ U_CHAR *symname;
+ char *usage;
+{
+ U_CHAR *p;
+ int sym_length;
+
+ for (p = symname; is_idchar[*p]; p++)
+ ;
+ sym_length = p - symname;
+ if (sym_length == 0)
+ error ("invalid %s name", usage);
+ else if (!is_idstart[*symname]) {
+ U_CHAR *msg; /* what pain... */
+ msg = (U_CHAR *) alloca (sym_length + 1);
+ bcopy ((char *) symname, (char *) msg, sym_length);
+ msg[sym_length] = 0;
+ error ("invalid %s name `%s'", usage, msg);
+ } else {
+ if (! strncmp (symname, "defined", 7) && sym_length == 7)
+ error ("invalid %s name `defined'", usage);
+ }
+ return sym_length;
+}
+
+/*
+ * return zero if two DEFINITIONs are isomorphic
+ */
+static int
+compare_defs (d1, d2)
+ DEFINITION *d1, *d2;
+{
+ register struct reflist *a1, *a2;
+ register U_CHAR *p1 = d1->expansion;
+ register U_CHAR *p2 = d2->expansion;
+ int first = 1;
+
+ if (d1->nargs != d2->nargs)
+ return 1;
+ if (strcmp ((char *)d1->args.argnames, (char *)d2->args.argnames))
+ return 1;
+ for (a1 = d1->pattern, a2 = d2->pattern; a1 && a2;
+ a1 = a1->next, a2 = a2->next) {
+ if (!((a1->nchars == a2->nchars && ! strncmp (p1, p2, a1->nchars))
+ || ! comp_def_part (first, p1, a1->nchars, p2, a2->nchars, 0))
+ || a1->argno != a2->argno
+ || a1->stringify != a2->stringify
+ || a1->raw_before != a2->raw_before
+ || a1->raw_after != a2->raw_after)
+ return 1;
+ first = 0;
+ p1 += a1->nchars;
+ p2 += a2->nchars;
+ }
+ if (a1 != a2)
+ return 1;
+ if (comp_def_part (first, p1, d1->length - (p1 - d1->expansion),
+ p2, d2->length - (p2 - d2->expansion), 1))
+ return 1;
+ return 0;
+}
+
+/* Return 1 if two parts of two macro definitions are effectively different.
+ One of the parts starts at BEG1 and has LEN1 chars;
+ the other has LEN2 chars at BEG2.
+ Any sequence of whitespace matches any other sequence of whitespace.
+ FIRST means these parts are the first of a macro definition;
+ so ignore leading whitespace entirely.
+ LAST means these parts are the last of a macro definition;
+ so ignore trailing whitespace entirely. */
+
+static int
+comp_def_part (first, beg1, len1, beg2, len2, last)
+ int first;
+ U_CHAR *beg1, *beg2;
+ int len1, len2;
+ int last;
+{
+ register U_CHAR *end1 = beg1 + len1;
+ register U_CHAR *end2 = beg2 + len2;
+ if (first) {
+ while (beg1 != end1 && is_space[*beg1]) beg1++;
+ while (beg2 != end2 && is_space[*beg2]) beg2++;
+ }
+ if (last) {
+ while (beg1 != end1 && is_space[end1[-1]]) end1--;
+ while (beg2 != end2 && is_space[end2[-1]]) end2--;
+ }
+ while (beg1 != end1 && beg2 != end2) {
+ if (is_space[*beg1] && is_space[*beg2]) {
+ while (beg1 != end1 && is_space[*beg1]) beg1++;
+ while (beg2 != end2 && is_space[*beg2]) beg2++;
+ } else if (*beg1 == *beg2) {
+ beg1++; beg2++;
+ } else break;
+ }
+ return (beg1 != end1) || (beg2 != end2);
+}
+
+/* Read a replacement list for a macro with parameters.
+ Build the DEFINITION structure.
+ Reads characters of text starting at BUF until END.
+ ARGLIST specifies the formal parameters to look for
+ in the text of the definition; NARGS is the number of args
+ in that list, or -1 for a macro name that wants no argument list.
+ MACRONAME is the macro name itself (so we can avoid recursive expansion)
+ and NAMELEN is its length in characters.
+
+Note that comments and backslash-newlines have already been deleted
+from the argument. */
+
+/* Leading and trailing Space, Tab, etc. are converted to markers
+ Newline Space, Newline Tab, etc.
+ Newline Space makes a space in the final output
+ but is discarded if stringified. (Newline Tab is similar but
+ makes a Tab instead.)
+
+ If there is no trailing whitespace, a Newline Space is added at the end
+ to prevent concatenation that would be contrary to the standard. */
+
+static DEFINITION *
+collect_expansion (buf, end, nargs, arglist)
+ U_CHAR *buf, *end;
+ int nargs;
+ struct arglist *arglist;
+{
+ DEFINITION *defn;
+ register U_CHAR *p, *limit, *lastp, *exp_p;
+ struct reflist *endpat = NULL;
+ /* Pointer to first nonspace after last ## seen. */
+ U_CHAR *concat = 0;
+ /* Pointer to first nonspace after last single-# seen. */
+ U_CHAR *stringify = 0;
+ int maxsize;
+ int expected_delimiter = '\0';
+
+ /* Scan thru the replacement list, ignoring comments and quoted
+ strings, picking up on the macro calls. It does a linear search
+ thru the arg list on every potential symbol. Profiling might say
+ that something smarter should happen. */
+
+ if (end < buf)
+ abort ();
+
+ /* Find the beginning of the trailing whitespace. */
+ /* Find end of leading whitespace. */
+ limit = end;
+ p = buf;
+ while (p < limit && is_space[limit[-1]]) limit--;
+ while (p < limit && is_space[*p]) p++;
+
+ /* Allocate space for the text in the macro definition.
+ Leading and trailing whitespace chars need 2 bytes each.
+ Each other input char may or may not need 1 byte,
+ so this is an upper bound.
+ The extra 2 are for invented trailing newline-marker and final null. */
+ maxsize = (sizeof (DEFINITION)
+ + 2 * (end - limit) + 2 * (p - buf)
+ + (limit - p) + 3);
+ defn = (DEFINITION *) xcalloc (1, maxsize);
+
+ defn->nargs = nargs;
+ exp_p = defn->expansion = (U_CHAR *) defn + sizeof (DEFINITION);
+ lastp = exp_p;
+
+ p = buf;
+
+ /* Convert leading whitespace to Newline-markers. */
+ while (p < limit && is_space[*p]) {
+ *exp_p++ = '\n';
+ *exp_p++ = *p++;
+ }
+
+ if (limit - p >= 2 && p[0] == '#' && p[1] == '#') {
+ error ("`##' at start of macro definition");
+ p += 2;
+ }
+
+ /* Process the main body of the definition. */
+ while (p < limit) {
+ int skipped_arg = 0;
+ register U_CHAR c = *p++;
+
+ *exp_p++ = c;
+
+ if (!traditional) {
+ switch (c) {
+ case '\'':
+ case '\"':
+ if (expected_delimiter != '\0') {
+ if (c == expected_delimiter)
+ expected_delimiter = '\0';
+ } else
+ expected_delimiter = c;
+ break;
+
+ /* Special hack: if a \# is written in the #define
+ include a # in the definition. This is useless for C code
+ but useful for preprocessing other things. */
+
+ case '\\':
+ /* \# quotes a # even outside of strings. */
+ if (p < limit && *p == '#' && !expected_delimiter) {
+ exp_p--;
+ *exp_p++ = *p++;
+ } else if (p < limit && expected_delimiter) {
+ /* In a string, backslash goes through
+ and makes next char ordinary. */
+ *exp_p++ = *p++;
+ }
+ break;
+
+ case '#':
+ /* # is ordinary inside a string. */
+ if (expected_delimiter)
+ break;
+ if (p < limit && *p == '#') {
+ /* ##: concatenate preceding and following tokens. */
+ /* Take out the first #, discard preceding whitespace. */
+ exp_p--;
+ while (exp_p > lastp && is_hor_space[exp_p[-1]])
+ --exp_p;
+ /* Skip the second #. */
+ p++;
+ /* Discard following whitespace. */
+ SKIP_WHITE_SPACE (p);
+ concat = p;
+ if (p == limit)
+ error ("`##' at end of macro definition");
+ } else if (nargs >= 0) {
+ /* Single #: stringify following argument ref.
+ Don't leave the # in the expansion. */
+ exp_p--;
+ SKIP_WHITE_SPACE (p);
+ if (p == limit || ! is_idstart[*p])
+ error ("`#' operator is not followed by a macro argument name");
+ else
+ stringify = p;
+ }
+ break;
+ }
+ } else {
+ /* In -traditional mode, recognize arguments inside strings and
+ and character constants, and ignore special properties of #.
+ Arguments inside strings are considered "stringified", but no
+ extra quote marks are supplied. */
+ switch (c) {
+ case '\'':
+ case '\"':
+ if (expected_delimiter != '\0') {
+ if (c == expected_delimiter)
+ expected_delimiter = '\0';
+ } else
+ expected_delimiter = c;
+ break;
+
+ case '\\':
+ /* Backslash quotes delimiters and itself, but not macro args. */
+ if (expected_delimiter != 0 && p < limit
+ && (*p == expected_delimiter || *p == '\\')) {
+ *exp_p++ = *p++;
+ continue;
+ }
+ break;
+
+ case '/':
+ if (expected_delimiter != '\0') /* No comments inside strings. */
+ break;
+ if (*p == '*') {
+ /* If we find a comment that wasn't removed by handle_directive,
+ this must be -traditional. So replace the comment with
+ nothing at all. */
+ exp_p--;
+ p += 1;
+ while (p < limit && !(p[-2] == '*' && p[-1] == '/'))
+ p++;
+#if 0
+ /* Mark this as a concatenation-point, as if it had been ##. */
+ concat = p;
+#endif
+ }
+ break;
+ }
+ }
+
+ /* Handle the start of a symbol. */
+ if (is_idchar[c] && nargs > 0) {
+ U_CHAR *id_beg = p - 1;
+ int id_len;
+
+ --exp_p;
+ while (p != limit && is_idchar[*p]) p++;
+ id_len = p - id_beg;
+
+ if (is_idstart[c]) {
+ register struct arglist *arg;
+
+ for (arg = arglist; arg != NULL; arg = arg->next) {
+ struct reflist *tpat;
+
+ if (arg->name[0] == c
+ && arg->length == id_len
+ && strncmp (arg->name, id_beg, id_len) == 0) {
+ if (expected_delimiter && warn_stringify) {
+ if (traditional) {
+ warning ("macro argument `%.*s' is stringified.",
+ id_len, arg->name);
+ } else {
+ warning ("macro arg `%.*s' would be stringified with -traditional.",
+ id_len, arg->name);
+ }
+ }
+ /* If ANSI, don't actually substitute inside a string. */
+ if (!traditional && expected_delimiter)
+ break;
+ /* make a pat node for this arg and append it to the end of
+ the pat list */
+ tpat = (struct reflist *) xmalloc (sizeof (struct reflist));
+ tpat->next = NULL;
+ tpat->raw_before = concat == id_beg;
+ tpat->raw_after = 0;
+ tpat->rest_args = arg->rest_args;
+ tpat->stringify = (traditional ? expected_delimiter != '\0'
+ : stringify == id_beg);
+
+ if (endpat == NULL)
+ defn->pattern = tpat;
+ else
+ endpat->next = tpat;
+ endpat = tpat;
+
+ tpat->argno = arg->argno;
+ tpat->nchars = exp_p - lastp;
+ {
+ register U_CHAR *p1 = p;
+ SKIP_WHITE_SPACE (p1);
+ if (p1 + 2 <= limit && p1[0] == '#' && p1[1] == '#')
+ tpat->raw_after = 1;
+ }
+ lastp = exp_p; /* place to start copying from next time */
+ skipped_arg = 1;
+ break;
+ }
+ }
+ }
+
+ /* If this was not a macro arg, copy it into the expansion. */
+ if (! skipped_arg) {
+ register U_CHAR *lim1 = p;
+ p = id_beg;
+ while (p != lim1)
+ *exp_p++ = *p++;
+ if (stringify == id_beg)
+ error ("`#' operator should be followed by a macro argument name");
+ }
+ }
+ }
+
+ if (!traditional && expected_delimiter == 0) {
+ /* There is no trailing whitespace, so invent some in ANSI mode.
+ But not if "inside a string" (which in ANSI mode
+ happens only for -D option). */
+ *exp_p++ = '\n';
+ *exp_p++ = ' ';
+ }
+
+ *exp_p = '\0';
+
+ defn->length = exp_p - defn->expansion;
+
+ /* Crash now if we overrun the allocated size. */
+ if (defn->length + 1 > maxsize)
+ abort ();
+
+#if 0
+/* This isn't worth the time it takes. */
+ /* give back excess storage */
+ defn->expansion = (U_CHAR *) xrealloc (defn->expansion, defn->length + 1);
+#endif
+
+ return defn;
+}
+
+static int
+do_assert (buf, limit, op, keyword)
+ U_CHAR *buf, *limit;
+ FILE_BUF *op;
+ struct directive *keyword;
+{
+ U_CHAR *bp; /* temp ptr into input buffer */
+ U_CHAR *symname; /* remember where symbol name starts */
+ int sym_length; /* and how long it is */
+ struct arglist *tokens = NULL;
+
+ if (pedantic && done_initializing && !instack[indepth].system_header_p)
+ pedwarn ("ANSI C does not allow `#assert'");
+
+ bp = buf;
+
+ while (is_hor_space[*bp])
+ bp++;
+
+ symname = bp; /* remember where it starts */
+ sym_length = check_macro_name (bp, "assertion");
+ bp += sym_length;
+ /* #define doesn't do this, but we should. */
+ SKIP_WHITE_SPACE (bp);
+
+ /* Lossage will occur if identifiers or control tokens are broken
+ across lines using backslash. This is not the right place to take
+ care of that. */
+
+ if (*bp != '(') {
+ error ("missing token-sequence in `#assert'");
+ return 1;
+ }
+
+ {
+ int error_flag = 0;
+
+ bp++; /* skip '(' */
+ SKIP_WHITE_SPACE (bp);
+
+ tokens = read_token_list (&bp, limit, &error_flag);
+ if (error_flag)
+ return 1;
+ if (tokens == 0) {
+ error ("empty token-sequence in `#assert'");
+ return 1;
+ }
+
+ ++bp; /* skip paren */
+ SKIP_WHITE_SPACE (bp);
+ }
+
+ /* If this name isn't already an assertion name, make it one.
+ Error if it was already in use in some other way. */
+
+ {
+ ASSERTION_HASHNODE *hp;
+ int hashcode = hashf (symname, sym_length, ASSERTION_HASHSIZE);
+ struct tokenlist_list *value
+ = (struct tokenlist_list *) xmalloc (sizeof (struct tokenlist_list));
+
+ hp = assertion_lookup (symname, sym_length, hashcode);
+ if (hp == NULL) {
+ if (sym_length == 7 && ! strncmp (symname, "defined", sym_length))
+ error ("`defined' redefined as assertion");
+ hp = assertion_install (symname, sym_length, hashcode);
+ }
+
+ /* Add the spec'd token-sequence to the list of such. */
+ value->tokens = tokens;
+ value->next = hp->value;
+ hp->value = value;
+ }
+
+ return 0;
+}
+
+static int
+do_unassert (buf, limit, op, keyword)
+ U_CHAR *buf, *limit;
+ FILE_BUF *op;
+ struct directive *keyword;
+{
+ U_CHAR *bp; /* temp ptr into input buffer */
+ U_CHAR *symname; /* remember where symbol name starts */
+ int sym_length; /* and how long it is */
+
+ struct arglist *tokens = NULL;
+ int tokens_specified = 0;
+
+ if (pedantic && done_initializing && !instack[indepth].system_header_p)
+ pedwarn ("ANSI C does not allow `#unassert'");
+
+ bp = buf;
+
+ while (is_hor_space[*bp])
+ bp++;
+
+ symname = bp; /* remember where it starts */
+ sym_length = check_macro_name (bp, "assertion");
+ bp += sym_length;
+ /* #define doesn't do this, but we should. */
+ SKIP_WHITE_SPACE (bp);
+
+ /* Lossage will occur if identifiers or control tokens are broken
+ across lines using backslash. This is not the right place to take
+ care of that. */
+
+ if (*bp == '(') {
+ int error_flag = 0;
+
+ bp++; /* skip '(' */
+ SKIP_WHITE_SPACE (bp);
+
+ tokens = read_token_list (&bp, limit, &error_flag);
+ if (error_flag)
+ return 1;
+ if (tokens == 0) {
+ error ("empty token list in `#unassert'");
+ return 1;
+ }
+
+ tokens_specified = 1;
+
+ ++bp; /* skip paren */
+ SKIP_WHITE_SPACE (bp);
+ }
+
+ {
+ ASSERTION_HASHNODE *hp;
+ int hashcode = hashf (symname, sym_length, ASSERTION_HASHSIZE);
+ struct tokenlist_list *tail, *prev;
+
+ hp = assertion_lookup (symname, sym_length, hashcode);
+ if (hp == NULL)
+ return 1;
+
+ /* If no token list was specified, then eliminate this assertion
+ entirely. */
+ if (! tokens_specified) {
+ struct tokenlist_list *next;
+ for (tail = hp->value; tail; tail = next) {
+ next = tail->next;
+ free_token_list (tail->tokens);
+ free (tail);
+ }
+ delete_assertion (hp);
+ } else {
+ /* If a list of tokens was given, then delete any matching list. */
+
+ tail = hp->value;
+ prev = 0;
+ while (tail) {
+ struct tokenlist_list *next = tail->next;
+ if (compare_token_lists (tail->tokens, tokens)) {
+ if (prev)
+ prev->next = next;
+ else
+ hp->value = tail->next;
+ free_token_list (tail->tokens);
+ free (tail);
+ } else {
+ prev = tail;
+ }
+ tail = next;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Test whether there is an assertion named NAME
+ and optionally whether it has an asserted token list TOKENS.
+ NAME is not null terminated; its length is SYM_LENGTH.
+ If TOKENS_SPECIFIED is 0, then don't check for any token list. */
+
+int
+check_assertion (name, sym_length, tokens_specified, tokens)
+ U_CHAR *name;
+ int sym_length;
+ int tokens_specified;
+ struct arglist *tokens;
+{
+ ASSERTION_HASHNODE *hp;
+ int hashcode = hashf (name, sym_length, ASSERTION_HASHSIZE);
+
+ if (pedantic && !instack[indepth].system_header_p)
+ pedwarn ("ANSI C does not allow testing assertions");
+
+ hp = assertion_lookup (name, sym_length, hashcode);
+ if (hp == NULL)
+ /* It is not an assertion; just return false. */
+ return 0;
+
+ /* If no token list was specified, then value is 1. */
+ if (! tokens_specified)
+ return 1;
+
+ {
+ struct tokenlist_list *tail;
+
+ tail = hp->value;
+
+ /* If a list of tokens was given,
+ then succeed if the assertion records a matching list. */
+
+ while (tail) {
+ if (compare_token_lists (tail->tokens, tokens))
+ return 1;
+ tail = tail->next;
+ }
+
+ /* Fail if the assertion has no matching list. */
+ return 0;
+ }
+}
+
+/* Compare two lists of tokens for equality including order of tokens. */
+
+static int
+compare_token_lists (l1, l2)
+ struct arglist *l1, *l2;
+{
+ while (l1 && l2) {
+ if (l1->length != l2->length)
+ return 0;
+ if (strncmp (l1->name, l2->name, l1->length))
+ return 0;
+ l1 = l1->next;
+ l2 = l2->next;
+ }
+
+ /* Succeed if both lists end at the same time. */
+ return l1 == l2;
+}
+
+/* Read a space-separated list of tokens ending in a close parenthesis.
+ Return a list of strings, in the order they were written.
+ (In case of error, return 0 and store -1 in *ERROR_FLAG.)
+ Parse the text starting at *BPP, and update *BPP.
+ Don't parse beyond LIMIT. */
+
+static struct arglist *
+read_token_list (bpp, limit, error_flag)
+ U_CHAR **bpp;
+ U_CHAR *limit;
+ int *error_flag;
+{
+ struct arglist *token_ptrs = 0;
+ U_CHAR *bp = *bpp;
+ int depth = 1;
+
+ *error_flag = 0;
+
+ /* Loop over the assertion value tokens. */
+ while (depth > 0) {
+ struct arglist *temp;
+ int eofp = 0;
+ U_CHAR *beg = bp;
+
+ /* Find the end of the token. */
+ if (*bp == '(') {
+ bp++;
+ depth++;
+ } else if (*bp == ')') {
+ depth--;
+ if (depth == 0)
+ break;
+ bp++;
+ } else if (*bp == '"' || *bp == '\'')
+ bp = skip_quoted_string (bp, limit, 0, NULL_PTR, NULL_PTR, &eofp);
+ else
+ while (! is_hor_space[*bp] && *bp != '(' && *bp != ')'
+ && *bp != '"' && *bp != '\'' && bp != limit)
+ bp++;
+
+ temp = (struct arglist *) xmalloc (sizeof (struct arglist));
+ temp->name = (U_CHAR *) xmalloc (bp - beg + 1);
+ bcopy ((char *) beg, (char *) temp->name, bp - beg);
+ temp->name[bp - beg] = 0;
+ temp->next = token_ptrs;
+ token_ptrs = temp;
+ temp->length = bp - beg;
+
+ SKIP_WHITE_SPACE (bp);
+
+ if (bp >= limit) {
+ error ("unterminated token sequence in `#assert' or `#unassert'");
+ *error_flag = -1;
+ return 0;
+ }
+ }
+ *bpp = bp;
+
+ /* We accumulated the names in reverse order.
+ Now reverse them to get the proper order. */
+ {
+ register struct arglist *prev = 0, *this, *next;
+ for (this = token_ptrs; this; this = next) {
+ next = this->next;
+ this->next = prev;
+ prev = this;
+ }
+ return prev;
+ }
+}
+
+static void
+free_token_list (tokens)
+ struct arglist *tokens;
+{
+ while (tokens) {
+ struct arglist *next = tokens->next;
+ free (tokens->name);
+ free (tokens);
+ tokens = next;
+ }
+}
+
+/*
+ * Install a name in the assertion hash table.
+ *
+ * If LEN is >= 0, it is the length of the name.
+ * Otherwise, compute the length by scanning the entire name.
+ *
+ * If HASH is >= 0, it is the precomputed hash code.
+ * Otherwise, compute the hash code.
+ */
+static ASSERTION_HASHNODE *
+assertion_install (name, len, hash)
+ U_CHAR *name;
+ int len;
+ int hash;
+{
+ register ASSERTION_HASHNODE *hp;
+ register int i, bucket;
+ register U_CHAR *p, *q;
+
+ i = sizeof (ASSERTION_HASHNODE) + len + 1;
+ hp = (ASSERTION_HASHNODE *) xmalloc (i);
+ bucket = hash;
+ hp->bucket_hdr = &assertion_hashtab[bucket];
+ hp->next = assertion_hashtab[bucket];
+ assertion_hashtab[bucket] = hp;
+ hp->prev = NULL;
+ if (hp->next != NULL)
+ hp->next->prev = hp;
+ hp->length = len;
+ hp->value = 0;
+ hp->name = ((U_CHAR *) hp) + sizeof (ASSERTION_HASHNODE);
+ p = hp->name;
+ q = name;
+ for (i = 0; i < len; i++)
+ *p++ = *q++;
+ hp->name[len] = 0;
+ return hp;
+}
+
+/*
+ * find the most recent hash node for name name (ending with first
+ * non-identifier char) installed by install
+ *
+ * If LEN is >= 0, it is the length of the name.
+ * Otherwise, compute the length by scanning the entire name.
+ *
+ * If HASH is >= 0, it is the precomputed hash code.
+ * Otherwise, compute the hash code.
+ */
+static ASSERTION_HASHNODE *
+assertion_lookup (name, len, hash)
+ U_CHAR *name;
+ int len;
+ int hash;
+{
+ register ASSERTION_HASHNODE *bucket;
+
+ bucket = assertion_hashtab[hash];
+ while (bucket) {
+ if (bucket->length == len && strncmp (bucket->name, name, len) == 0)
+ return bucket;
+ bucket = bucket->next;
+ }
+ return NULL;
+}
+
+static void
+delete_assertion (hp)
+ ASSERTION_HASHNODE *hp;
+{
+
+ if (hp->prev != NULL)
+ hp->prev->next = hp->next;
+ if (hp->next != NULL)
+ hp->next->prev = hp->prev;
+
+ /* make sure that the bucket chain header that
+ the deleted guy was on points to the right thing afterwards. */
+ if (hp == *hp->bucket_hdr)
+ *hp->bucket_hdr = hp->next;
+
+ free (hp);
+}
+
+/*
+ * interpret #line command. Remembers previously seen fnames
+ * in its very own hash table.
+ */
+#define FNAME_HASHSIZE 37
+
+static int
+do_line (buf, limit, op, keyword)
+ U_CHAR *buf, *limit;
+ FILE_BUF *op;
+ struct directive *keyword;
+{
+ register U_CHAR *bp;
+ FILE_BUF *ip = &instack[indepth];
+ FILE_BUF tem;
+ int new_lineno;
+ enum file_change_code file_change = same_file;
+
+ /* Expand any macros. */
+ tem = expand_to_temp_buffer (buf, limit, 0, 0);
+
+ /* Point to macroexpanded line, which is null-terminated now. */
+ bp = tem.buf;
+ SKIP_WHITE_SPACE (bp);
+
+ if (!isdigit (*bp)) {
+ error ("invalid format `#line' command");
+ return 0;
+ }
+
+ /* The Newline at the end of this line remains to be processed.
+ To put the next line at the specified line number,
+ we must store a line number now that is one less. */
+ new_lineno = atoi (bp) - 1;
+
+ /* NEW_LINENO is one less than the actual line number here. */
+ if (pedantic && new_lineno < 0)
+ pedwarn ("line number out of range in `#line' command");
+
+ /* skip over the line number. */
+ while (isdigit (*bp))
+ bp++;
+
+#if 0 /* #line 10"foo.c" is supposed to be allowed. */
+ if (*bp && !is_space[*bp]) {
+ error ("invalid format `#line' command");
+ return;
+ }
+#endif
+
+ SKIP_WHITE_SPACE (bp);
+
+ if (*bp == '\"') {
+ static HASHNODE *fname_table[FNAME_HASHSIZE];
+ HASHNODE *hp, **hash_bucket;
+ U_CHAR *fname, *p;
+ int fname_length;
+
+ fname = ++bp;
+
+ /* Turn the file name, which is a character string literal,
+ into a null-terminated string. Do this in place. */
+ p = bp;
+ for (;;)
+ switch ((*p++ = *bp++)) {
+ case '\0':
+ error ("invalid format `#line' command");
+ return 0;
+
+ case '\\':
+ {
+ char *bpc = (char *) bp;
+ int c = parse_escape (&bpc);
+ bp = (U_CHAR *) bpc;
+ if (c < 0)
+ p--;
+ else
+ p[-1] = c;
+ }
+ break;
+
+ case '\"':
+ p[-1] = 0;
+ goto fname_done;
+ }
+ fname_done:
+ fname_length = p - fname;
+
+ SKIP_WHITE_SPACE (bp);
+ if (*bp) {
+ if (pedantic)
+ pedwarn ("garbage at end of `#line' command");
+ if (*bp == '1')
+ file_change = enter_file;
+ else if (*bp == '2')
+ file_change = leave_file;
+ else if (*bp == '3')
+ ip->system_header_p = 1;
+ else if (*bp == '4')
+ ip->system_header_p = 2;
+ else {
+ error ("invalid format `#line' command");
+ return 0;
+ }
+
+ bp++;
+ SKIP_WHITE_SPACE (bp);
+ if (*bp == '3') {
+ ip->system_header_p = 1;
+ bp++;
+ SKIP_WHITE_SPACE (bp);
+ }
+ if (*bp == '4') {
+ ip->system_header_p = 2;
+ bp++;
+ SKIP_WHITE_SPACE (bp);
+ }
+ if (*bp) {
+ error ("invalid format `#line' command");
+ return 0;
+ }
+ }
+
+ hash_bucket =
+ &fname_table[hashf (fname, fname_length, FNAME_HASHSIZE)];
+ for (hp = *hash_bucket; hp != NULL; hp = hp->next)
+ if (hp->length == fname_length &&
+ strncmp (hp->value.cpval, fname, fname_length) == 0) {
+ ip->nominal_fname = hp->value.cpval;
+ break;
+ }
+ if (hp == 0) {
+ /* Didn't find it; cons up a new one. */
+ hp = (HASHNODE *) xcalloc (1, sizeof (HASHNODE) + fname_length + 1);
+ hp->next = *hash_bucket;
+ *hash_bucket = hp;
+
+ hp->length = fname_length;
+ ip->nominal_fname = hp->value.cpval = ((char *) hp) + sizeof (HASHNODE);
+ bcopy (fname, hp->value.cpval, fname_length);
+ }
+ } else if (*bp) {
+ error ("invalid format `#line' command");
+ return 0;
+ }
+
+ ip->lineno = new_lineno;
+ output_line_command (ip, op, 0, file_change);
+ check_expand (op, ip->length - (ip->bufp - ip->buf));
+ return 0;
+}
+
+/*
+ * remove the definition of a symbol from the symbol table.
+ * according to un*x /lib/cpp, it is not an error to undef
+ * something that has no definitions, so it isn't one here either.
+ */
+
+static int
+do_undef (buf, limit, op, keyword)
+ U_CHAR *buf, *limit;
+ FILE_BUF *op;
+ struct directive *keyword;
+{
+ int sym_length;
+ HASHNODE *hp;
+ U_CHAR *orig_buf = buf;
+
+ /* If this is a precompiler run (with -pcp) pass thru #undef commands. */
+ if (pcp_outfile && op)
+ pass_thru_directive (buf, limit, op, keyword);
+
+ SKIP_WHITE_SPACE (buf);
+ sym_length = check_macro_name (buf, "macro");
+
+ while ((hp = lookup (buf, sym_length, -1)) != NULL) {
+ /* If we are generating additional info for debugging (with -g) we
+ need to pass through all effective #undef commands. */
+ if (debug_output && op)
+ pass_thru_directive (orig_buf, limit, op, keyword);
+ if (hp->type != T_MACRO)
+ warning ("undefining `%s'", hp->name);
+ delete_macro (hp);
+ }
+
+ if (pedantic) {
+ buf += sym_length;
+ SKIP_WHITE_SPACE (buf);
+ if (buf != limit)
+ pedwarn ("garbage after `#undef' directive");
+ }
+ return 0;
+}
+
+/*
+ * Report an error detected by the program we are processing.
+ * Use the text of the line in the error message.
+ * (We use error because it prints the filename & line#.)
+ */
+
+static int
+do_error (buf, limit, op, keyword)
+ U_CHAR *buf, *limit;
+ FILE_BUF *op;
+ struct directive *keyword;
+{
+ int length = limit - buf;
+ U_CHAR *copy = (U_CHAR *) xmalloc (length + 1);
+ bcopy ((char *) buf, (char *) copy, length);
+ copy[length] = 0;
+ SKIP_WHITE_SPACE (copy);
+ error ("#error %s", copy);
+ return 0;
+}
+
+/*
+ * Report a warning detected by the program we are processing.
+ * Use the text of the line in the warning message, then continue.
+ * (We use error because it prints the filename & line#.)
+ */
+
+static int
+do_warning (buf, limit, op, keyword)
+ U_CHAR *buf, *limit;
+ FILE_BUF *op;
+ struct directive *keyword;
+{
+ int length = limit - buf;
+ U_CHAR *copy = (U_CHAR *) xmalloc (length + 1);
+ bcopy ((char *) buf, (char *) copy, length);
+ copy[length] = 0;
+ SKIP_WHITE_SPACE (copy);
+ warning ("#warning %s", copy);
+ return 0;
+}
+
+/* Remember the name of the current file being read from so that we can
+ avoid ever including it again. */
+
+static int
+do_once ()
+{
+ int i;
+ FILE_BUF *ip = NULL;
+
+ for (i = indepth; i >= 0; i--)
+ if (instack[i].fname != NULL) {
+ ip = &instack[i];
+ break;
+ }
+
+ if (ip != NULL) {
+ struct file_name_list *new;
+
+ new = (struct file_name_list *) xmalloc (sizeof (struct file_name_list));
+ new->next = dont_repeat_files;
+ dont_repeat_files = new;
+ new->fname = savestring (ip->fname);
+ new->control_macro = 0;
+ new->got_name_map = 0;
+ new->c_system_include_path = 0;
+ }
+ return 0;
+}
+
+/* #ident has already been copied to the output file, so just ignore it. */
+
+static int
+do_ident (buf, limit)
+ U_CHAR *buf, *limit;
+{
+ FILE_BUF trybuf;
+ int len;
+ FILE_BUF *op = &outbuf;
+
+ /* Allow #ident in system headers, since that's not user's fault. */
+ if (pedantic && !instack[indepth].system_header_p)
+ pedwarn ("ANSI C does not allow `#ident'");
+
+ trybuf = expand_to_temp_buffer (buf, limit, 0, 0);
+ buf = (U_CHAR *) alloca (trybuf.bufp - trybuf.buf + 1);
+ bcopy ((char *) trybuf.buf, (char *) buf, trybuf.bufp - trybuf.buf);
+ limit = buf + (trybuf.bufp - trybuf.buf);
+ len = (limit - buf);
+ free (trybuf.buf);
+
+ /* Output directive name. */
+ check_expand (op, 8);
+ bcopy ("#ident ", (char *) op->bufp, 7);
+ op->bufp += 7;
+
+ /* Output the expanded argument line. */
+ check_expand (op, len);
+ bcopy ((char *) buf, (char *) op->bufp, len);
+ op->bufp += len;
+
+ return 0;
+}
+
+/* #pragma and its argument line have already been copied to the output file.
+ Just check for some recognized pragmas that need validation here. */
+
+static int
+do_pragma (buf, limit)
+ U_CHAR *buf, *limit;
+{
+ while (*buf == ' ' || *buf == '\t')
+ buf++;
+ if (!strncmp (buf, "once", 4)) {
+ /* Allow #pragma once in system headers, since that's not the user's
+ fault. */
+ if (!instack[indepth].system_header_p)
+ warning ("`#pragma once' is obsolete");
+ do_once ();
+ }
+
+ if (!strncmp (buf, "implementation", 14)) {
+ /* Be quiet about `#pragma implementation' for a file only if it hasn't
+ been included yet. */
+ struct file_name_list *ptr;
+ U_CHAR *p = buf + 14, *fname, *inc_fname;
+ SKIP_WHITE_SPACE (p);
+ if (*p == '\n' || *p != '\"')
+ return 0;
+
+ fname = p + 1;
+ if (p = (U_CHAR *) index (fname, '\"'))
+ *p = '\0';
+
+ for (ptr = all_include_files; ptr; ptr = ptr->next) {
+ inc_fname = (U_CHAR *) rindex (ptr->fname, '/');
+ inc_fname = inc_fname ? inc_fname + 1 : (U_CHAR *) ptr->fname;
+ if (inc_fname && !strcmp (inc_fname, fname))
+ warning ("`#pragma implementation' for `%s' appears after file is included",
+ fname);
+ }
+ }
+
+ return 0;
+}
+
+#if 0
+/* This was a fun hack, but #pragma seems to start to be useful.
+ By failing to recognize it, we pass it through unchanged to cc1. */
+
+/*
+ * the behavior of the #pragma directive is implementation defined.
+ * this implementation defines it as follows.
+ */
+
+static int
+do_pragma ()
+{
+ close (0);
+ if (open ("/dev/tty", O_RDONLY, 0666) != 0)
+ goto nope;
+ close (1);
+ if (open ("/dev/tty", O_WRONLY, 0666) != 1)
+ goto nope;
+ execl ("/usr/games/hack", "#pragma", 0);
+ execl ("/usr/games/rogue", "#pragma", 0);
+ execl ("/usr/new/emacs", "-f", "hanoi", "9", "-kill", 0);
+ execl ("/usr/local/emacs", "-f", "hanoi", "9", "-kill", 0);
+nope:
+ fatal ("You are in a maze of twisty compiler features, all different");
+}
+#endif
+
+/* Just ignore #sccs, on systems where we define it at all. */
+
+static int
+do_sccs ()
+{
+ if (pedantic)
+ pedwarn ("ANSI C does not allow `#sccs'");
+ return 0;
+}
+
+/*
+ * handle #if command by
+ * 1) inserting special `defined' keyword into the hash table
+ * that gets turned into 0 or 1 by special_symbol (thus,
+ * if the luser has a symbol called `defined' already, it won't
+ * work inside the #if command)
+ * 2) rescan the input into a temporary output buffer
+ * 3) pass the output buffer to the yacc parser and collect a value
+ * 4) clean up the mess left from steps 1 and 2.
+ * 5) call conditional_skip to skip til the next #endif (etc.),
+ * or not, depending on the value from step 3.
+ */
+
+static int
+do_if (buf, limit, op, keyword)
+ U_CHAR *buf, *limit;
+ FILE_BUF *op;
+ struct directive *keyword;
+{
+ int value;
+ FILE_BUF *ip = &instack[indepth];
+
+ value = eval_if_expression (buf, limit - buf);
+ conditional_skip (ip, value == 0, T_IF, NULL_PTR);
+ return 0;
+}
+
+/*
+ * handle a #elif directive by not changing if_stack either.
+ * see the comment above do_else.
+ */
+
+static int
+do_elif (buf, limit, op, keyword)
+ U_CHAR *buf, *limit;
+ FILE_BUF *op;
+ struct directive *keyword;
+{
+ int value;
+ FILE_BUF *ip = &instack[indepth];
+
+ if (if_stack == instack[indepth].if_stack) {
+ error ("`#elif' not within a conditional");
+ return 0;
+ } else {
+ if (if_stack->type != T_IF && if_stack->type != T_ELIF) {
+ error ("`#elif' after `#else'");
+ fprintf (stderr, " (matches line %d", if_stack->lineno);
+ if (if_stack->fname != NULL && ip->fname != NULL &&
+ strcmp (if_stack->fname, ip->nominal_fname) != 0)
+ fprintf (stderr, ", file %s", if_stack->fname);
+ fprintf (stderr, ")\n");
+ }
+ if_stack->type = T_ELIF;
+ }
+
+ if (if_stack->if_succeeded)
+ skip_if_group (ip, 0);
+ else {
+ value = eval_if_expression (buf, limit - buf);
+ if (value == 0)
+ skip_if_group (ip, 0);
+ else {
+ ++if_stack->if_succeeded; /* continue processing input */
+ output_line_command (ip, op, 1, same_file);
+ }
+ }
+ return 0;
+}
+
+/*
+ * evaluate a #if expression in BUF, of length LENGTH,
+ * then parse the result as a C expression and return the value as an int.
+ */
+static int
+eval_if_expression (buf, length)
+ U_CHAR *buf;
+ int length;
+{
+ FILE_BUF temp_obuf;
+ HASHNODE *save_defined;
+ int value;
+
+ save_defined = install ("defined", -1, T_SPEC_DEFINED, 0, 0, -1);
+ pcp_inside_if = 1;
+ temp_obuf = expand_to_temp_buffer (buf, buf + length, 0, 1);
+ pcp_inside_if = 0;
+ delete_macro (save_defined); /* clean up special symbol */
+
+ value = parse_c_expression (temp_obuf.buf);
+
+ free (temp_obuf.buf);
+
+ return value;
+}
+
+/*
+ * routine to handle ifdef/ifndef. Try to look up the symbol,
+ * then do or don't skip to the #endif/#else/#elif depending
+ * on what directive is actually being processed.
+ */
+
+static int
+do_xifdef (buf, limit, op, keyword)
+ U_CHAR *buf, *limit;
+ FILE_BUF *op;
+ struct directive *keyword;
+{
+ int skip;
+ FILE_BUF *ip = &instack[indepth];
+ U_CHAR *end;
+ int start_of_file = 0;
+ U_CHAR *control_macro = 0;
+
+ /* Detect a #ifndef at start of file (not counting comments). */
+ if (ip->fname != 0 && keyword->type == T_IFNDEF) {
+ U_CHAR *p = ip->buf;
+ while (p != directive_start) {
+ U_CHAR c = *p++;
+ if (is_space[c])
+ ;
+ else if (c == '/' && p != ip->bufp && *p == '*') {
+ /* Skip this comment. */
+ int junk = 0;
+ U_CHAR *save_bufp = ip->bufp;
+ ip->bufp = p + 1;
+ p = skip_to_end_of_comment (ip, &junk, 1);
+ ip->bufp = save_bufp;
+ } else {
+ goto fail;
+ }
+ }
+ /* If we get here, this conditional is the beginning of the file. */
+ start_of_file = 1;
+ fail: ;
+ }
+
+ /* Discard leading and trailing whitespace. */
+ SKIP_WHITE_SPACE (buf);
+ while (limit != buf && is_hor_space[limit[-1]]) limit--;
+
+ /* Find the end of the identifier at the beginning. */
+ for (end = buf; is_idchar[*end]; end++);
+
+ if (end == buf) {
+ skip = (keyword->type == T_IFDEF);
+ if (! traditional)
+ pedwarn (end == limit ? "`#%s' with no argument"
+ : "`#%s' argument starts with punctuation",
+ keyword->name);
+ } else {
+ HASHNODE *hp;
+
+ if (pedantic && buf[0] >= '0' && buf[0] <= '9')
+ pedwarn ("`#%s' argument starts with a digit", keyword->name);
+ else if (end != limit && !traditional)
+ pedwarn ("garbage at end of `#%s' argument", keyword->name);
+
+ hp = lookup (buf, end-buf, -1);
+
+ if (pcp_outfile) {
+ /* Output a precondition for this macro. */
+ if (hp && hp->value.defn->predefined)
+ fprintf (pcp_outfile, "#define %s\n", hp->name);
+ else {
+ U_CHAR *cp = buf;
+ fprintf (pcp_outfile, "#undef ");
+ while (is_idchar[*cp]) /* Ick! */
+ fputc (*cp++, pcp_outfile);
+ putc ('\n', pcp_outfile);
+ }
+ }
+
+ skip = (hp == NULL) ^ (keyword->type == T_IFNDEF);
+ if (start_of_file && !skip) {
+ control_macro = (U_CHAR *) xmalloc (end - buf + 1);
+ bcopy ((char *) buf, (char *) control_macro, end - buf);
+ control_macro[end - buf] = 0;
+ }
+ }
+
+ conditional_skip (ip, skip, T_IF, control_macro);
+ return 0;
+}
+
+/* Push TYPE on stack; then, if SKIP is nonzero, skip ahead.
+ If this is a #ifndef starting at the beginning of a file,
+ CONTROL_MACRO is the macro name tested by the #ifndef.
+ Otherwise, CONTROL_MACRO is 0. */
+
+static void
+conditional_skip (ip, skip, type, control_macro)
+ FILE_BUF *ip;
+ int skip;
+ enum node_type type;
+ U_CHAR *control_macro;
+{
+ IF_STACK_FRAME *temp;
+
+ temp = (IF_STACK_FRAME *) xcalloc (1, sizeof (IF_STACK_FRAME));
+ temp->fname = ip->nominal_fname;
+ temp->lineno = ip->lineno;
+ temp->next = if_stack;
+ temp->control_macro = control_macro;
+ if_stack = temp;
+
+ if_stack->type = type;
+
+ if (skip != 0) {
+ skip_if_group (ip, 0);
+ return;
+ } else {
+ ++if_stack->if_succeeded;
+ output_line_command (ip, &outbuf, 1, same_file);
+ }
+}
+
+/*
+ * skip to #endif, #else, or #elif. adjust line numbers, etc.
+ * leaves input ptr at the sharp sign found.
+ * If ANY is nonzero, return at next directive of any sort.
+ */
+static void
+skip_if_group (ip, any)
+ FILE_BUF *ip;
+ int any;
+{
+ register U_CHAR *bp = ip->bufp, *cp;
+ register U_CHAR *endb = ip->buf + ip->length;
+ struct directive *kt;
+ IF_STACK_FRAME *save_if_stack = if_stack; /* don't pop past here */
+ U_CHAR *beg_of_line = bp;
+ register int ident_length;
+ U_CHAR *ident, *after_ident;
+
+ while (bp < endb) {
+ switch (*bp++) {
+ case '/': /* possible comment */
+ if (*bp == '\\' && bp[1] == '\n')
+ newline_fix (bp);
+ if (*bp == '*'
+ || (cplusplus_comments && *bp == '/')) {
+ ip->bufp = ++bp;
+ bp = skip_to_end_of_comment (ip, &ip->lineno, 0);
+ }
+ break;
+ case '\"':
+ case '\'':
+ bp = skip_quoted_string (bp - 1, endb, ip->lineno, &ip->lineno,
+ NULL_PTR, NULL_PTR);
+ break;
+ case '\\':
+ /* Char after backslash loses its special meaning. */
+ if (bp < endb) {
+ if (*bp == '\n')
+ ++ip->lineno; /* But do update the line-count. */
+ bp++;
+ }
+ break;
+ case '\n':
+ ++ip->lineno;
+ beg_of_line = bp;
+ break;
+ case '#':
+ ip->bufp = bp - 1;
+
+ /* # keyword: a # must be first nonblank char on the line */
+ if (beg_of_line == 0)
+ break;
+ /* Scan from start of line, skipping whitespace, comments
+ and backslash-newlines, and see if we reach this #.
+ If not, this # is not special. */
+ bp = beg_of_line;
+ /* If -traditional, require # to be at beginning of line. */
+ if (!traditional)
+ while (1) {
+ if (is_hor_space[*bp])
+ bp++;
+ else if (*bp == '\\' && bp[1] == '\n')
+ bp += 2;
+ else if (*bp == '/' && bp[1] == '*') {
+ bp += 2;
+ while (!(*bp == '*' && bp[1] == '/'))
+ bp++;
+ bp += 2;
+ }
+ /* There is no point in trying to deal with C++ // comments here,
+ because if there is one, then this # must be part of the
+ comment and we would never reach here. */
+ else break;
+ }
+ if (bp != ip->bufp) {
+ bp = ip->bufp + 1; /* Reset bp to after the #. */
+ break;
+ }
+
+ bp = ip->bufp + 1; /* Point after the '#' */
+
+ /* Skip whitespace and \-newline. */
+ while (1) {
+ if (is_hor_space[*bp])
+ bp++;
+ else if (*bp == '\\' && bp[1] == '\n')
+ bp += 2;
+ else if (*bp == '/' && bp[1] == '*') {
+ bp += 2;
+ while (!(*bp == '*' && bp[1] == '/')) {
+ if (*bp == '\n')
+ ip->lineno++;
+ bp++;
+ }
+ bp += 2;
+ } else if (cplusplus_comments && *bp == '/' && bp[1] == '/') {
+ bp += 2;
+ while (bp[-1] == '\\' || *bp != '\n') {
+ if (*bp == '\n')
+ ip->lineno++;
+ bp++;
+ }
+ }
+ else break;
+ }
+
+ cp = bp;
+
+ /* Now find end of directive name.
+ If we encounter a backslash-newline, exchange it with any following
+ symbol-constituents so that we end up with a contiguous name. */
+
+ while (1) {
+ if (is_idchar[*bp])
+ bp++;
+ else {
+ if (*bp == '\\' && bp[1] == '\n')
+ name_newline_fix (bp);
+ if (is_idchar[*bp])
+ bp++;
+ else break;
+ }
+ }
+ ident_length = bp - cp;
+ ident = cp;
+ after_ident = bp;
+
+ /* A line of just `#' becomes blank. */
+
+ if (ident_length == 0 && *after_ident == '\n') {
+ continue;
+ }
+
+ if (ident_length == 0 || !is_idstart[*ident]) {
+ U_CHAR *p = ident;
+ while (is_idchar[*p]) {
+ if (*p < '0' || *p > '9')
+ break;
+ p++;
+ }
+ /* Handle # followed by a line number. */
+ if (p != ident && !is_idchar[*p]) {
+ if (pedantic)
+ pedwarn ("`#' followed by integer");
+ continue;
+ }
+
+ /* Avoid error for `###' and similar cases unless -pedantic. */
+ if (p == ident) {
+ while (*p == '#' || is_hor_space[*p]) p++;
+ if (*p == '\n') {
+ if (pedantic && !lang_asm)
+ pedwarn ("invalid preprocessor directive");
+ continue;
+ }
+ }
+
+ if (!lang_asm && pedantic)
+ pedwarn ("invalid preprocessor directive name");
+ continue;
+ }
+
+ for (kt = directive_table; kt->length >= 0; kt++) {
+ IF_STACK_FRAME *temp;
+ if (ident_length == kt->length
+ && strncmp (cp, kt->name, kt->length) == 0) {
+ /* If we are asked to return on next directive, do so now. */
+ if (any)
+ return;
+
+ switch (kt->type) {
+ case T_IF:
+ case T_IFDEF:
+ case T_IFNDEF:
+ temp = (IF_STACK_FRAME *) xcalloc (1, sizeof (IF_STACK_FRAME));
+ temp->next = if_stack;
+ if_stack = temp;
+ temp->lineno = ip->lineno;
+ temp->fname = ip->nominal_fname;
+ temp->type = kt->type;
+ break;
+ case T_ELSE:
+ case T_ENDIF:
+ if (pedantic && if_stack != save_if_stack)
+ validate_else (bp);
+ case T_ELIF:
+ if (if_stack == instack[indepth].if_stack) {
+ error ("`#%s' not within a conditional", kt->name);
+ break;
+ }
+ else if (if_stack == save_if_stack)
+ return; /* found what we came for */
+
+ if (kt->type != T_ENDIF) {
+ if (if_stack->type == T_ELSE)
+ error ("`#else' or `#elif' after `#else'");
+ if_stack->type = kt->type;
+ break;
+ }
+
+ temp = if_stack;
+ if_stack = if_stack->next;
+ free (temp);
+ break;
+ }
+ break;
+ }
+ }
+ /* Don't let erroneous code go by. */
+ if (kt->length < 0 && !lang_asm && pedantic)
+ pedwarn ("invalid preprocessor directive name");
+ }
+ }
+ ip->bufp = bp;
+ /* after this returns, rescan will exit because ip->bufp
+ now points to the end of the buffer.
+ rescan is responsible for the error message also. */
+}
+
+/*
+ * handle a #else directive. Do this by just continuing processing
+ * without changing if_stack ; this is so that the error message
+ * for missing #endif's etc. will point to the original #if. It
+ * is possible that something different would be better.
+ */
+
+static int
+do_else (buf, limit, op, keyword)
+ U_CHAR *buf, *limit;
+ FILE_BUF *op;
+ struct directive *keyword;
+{
+ FILE_BUF *ip = &instack[indepth];
+
+ if (pedantic) {
+ SKIP_WHITE_SPACE (buf);
+ if (buf != limit)
+ pedwarn ("text following `#else' violates ANSI standard");
+ }
+
+ if (if_stack == instack[indepth].if_stack) {
+ error ("`#else' not within a conditional");
+ return 0;
+ } else {
+ /* #ifndef can't have its special treatment for containing the whole file
+ if it has a #else clause. */
+ if_stack->control_macro = 0;
+
+ if (if_stack->type != T_IF && if_stack->type != T_ELIF) {
+ error ("`#else' after `#else'");
+ fprintf (stderr, " (matches line %d", if_stack->lineno);
+ if (strcmp (if_stack->fname, ip->nominal_fname) != 0)
+ fprintf (stderr, ", file %s", if_stack->fname);
+ fprintf (stderr, ")\n");
+ }
+ if_stack->type = T_ELSE;
+ }
+
+ if (if_stack->if_succeeded)
+ skip_if_group (ip, 0);
+ else {
+ ++if_stack->if_succeeded; /* continue processing input */
+ output_line_command (ip, op, 1, same_file);
+ }
+ return 0;
+}
+
+/*
+ * unstack after #endif command
+ */
+
+static int
+do_endif (buf, limit, op, keyword)
+ U_CHAR *buf, *limit;
+ FILE_BUF *op;
+ struct directive *keyword;
+{
+ if (pedantic) {
+ SKIP_WHITE_SPACE (buf);
+ if (buf != limit)
+ pedwarn ("text following `#endif' violates ANSI standard");
+ }
+
+ if (if_stack == instack[indepth].if_stack)
+ error ("unbalanced `#endif'");
+ else {
+ IF_STACK_FRAME *temp = if_stack;
+ if_stack = if_stack->next;
+ if (temp->control_macro != 0) {
+ /* This #endif matched a #ifndef at the start of the file.
+ See if it is at the end of the file. */
+ FILE_BUF *ip = &instack[indepth];
+ U_CHAR *p = ip->bufp;
+ U_CHAR *ep = ip->buf + ip->length;
+
+ while (p != ep) {
+ U_CHAR c = *p++;
+ switch (c) {
+ case ' ':
+ case '\t':
+ case '\n':
+ break;
+ case '/':
+ if (p != ep && *p == '*') {
+ /* Skip this comment. */
+ int junk = 0;
+ U_CHAR *save_bufp = ip->bufp;
+ ip->bufp = p + 1;
+ p = skip_to_end_of_comment (ip, &junk, 1);
+ ip->bufp = save_bufp;
+ }
+ break;
+ default:
+ goto fail;
+ }
+ }
+ /* If we get here, this #endif ends a #ifndef
+ that contains all of the file (aside from whitespace).
+ Arrange not to include the file again
+ if the macro that was tested is defined.
+
+ Do not do this for the top-level file in a -include or any
+ file in a -imacros. */
+ if (indepth != 0
+ && ! (indepth == 1 && no_record_file)
+ && ! (no_record_file && no_output))
+ record_control_macro (ip->fname, temp->control_macro);
+ fail: ;
+ }
+ free (temp);
+ output_line_command (&instack[indepth], op, 1, same_file);
+ }
+ return 0;
+}
+
+/* When an #else or #endif is found while skipping failed conditional,
+ if -pedantic was specified, this is called to warn about text after
+ the command name. P points to the first char after the command name. */
+
+static void
+validate_else (p)
+ register U_CHAR *p;
+{
+ /* Advance P over whitespace and comments. */
+ while (1) {
+ if (*p == '\\' && p[1] == '\n')
+ p += 2;
+ if (is_hor_space[*p])
+ p++;
+ else if (*p == '/') {
+ if (p[1] == '\\' && p[2] == '\n')
+ newline_fix (p + 1);
+ if (p[1] == '*') {
+ p += 2;
+ /* Don't bother warning about unterminated comments
+ since that will happen later. Just be sure to exit. */
+ while (*p) {
+ if (p[1] == '\\' && p[2] == '\n')
+ newline_fix (p + 1);
+ if (*p == '*' && p[1] == '/') {
+ p += 2;
+ break;
+ }
+ p++;
+ }
+ }
+ else if (cplusplus_comments && p[1] == '/') {
+ p += 2;
+ while (*p && (*p != '\n' || p[-1] == '\\'))
+ p++;
+ }
+ } else break;
+ }
+ if (*p && *p != '\n')
+ pedwarn ("text following `#else' or `#endif' violates ANSI standard");
+}
+
+/* Skip a comment, assuming the input ptr immediately follows the
+ initial slash-star. Bump *LINE_COUNTER for each newline.
+ (The canonical line counter is &ip->lineno.)
+ Don't use this routine (or the next one) if bumping the line
+ counter is not sufficient to deal with newlines in the string.
+
+ If NOWARN is nonzero, don't warn about slash-star inside a comment.
+ This feature is useful when processing a comment that is going to be
+ processed or was processed at another point in the preprocessor,
+ to avoid a duplicate warning. Likewise for unterminated comment errors. */
+
+static U_CHAR *
+skip_to_end_of_comment (ip, line_counter, nowarn)
+ register FILE_BUF *ip;
+ int *line_counter; /* place to remember newlines, or NULL */
+ int nowarn;
+{
+ register U_CHAR *limit = ip->buf + ip->length;
+ register U_CHAR *bp = ip->bufp;
+ FILE_BUF *op = &outbuf; /* JF */
+ int output = put_out_comments && !line_counter;
+ int start_line = line_counter ? *line_counter : 0;
+
+ /* JF this line_counter stuff is a crock to make sure the
+ comment is only put out once, no matter how many times
+ the comment is skipped. It almost works */
+ if (output) {
+ *op->bufp++ = '/';
+ *op->bufp++ = '*';
+ }
+ if (cplusplus_comments && bp[-1] == '/') {
+ if (output) {
+ while (bp < limit) {
+ *op->bufp++ = *bp;
+ if (*bp == '\n' && bp[-1] != '\\')
+ break;
+ if (*bp == '\n') {
+ ++*line_counter;
+ ++op->lineno;
+ }
+ bp++;
+ }
+ op->bufp[-1] = '*';
+ *op->bufp++ = '/';
+ *op->bufp++ = '\n';
+ } else {
+ while (bp < limit) {
+ if (bp[-1] != '\\' && *bp == '\n') {
+ break;
+ } else {
+ if (*bp == '\n' && line_counter)
+ ++*line_counter;
+ bp++;
+ }
+ }
+ }
+ ip->bufp = bp;
+ return bp;
+ }
+ while (bp < limit) {
+ if (output)
+ *op->bufp++ = *bp;
+ switch (*bp++) {
+ case '/':
+ if (warn_comments && !nowarn && bp < limit && *bp == '*')
+ warning ("`/*' within comment");
+ break;
+ case '\n':
+ /* If this is the end of the file, we have an unterminated comment.
+ Don't swallow the newline. We are guaranteed that there will be a
+ trailing newline and various pieces assume it's there. */
+ if (bp == limit)
+ {
+ --bp;
+ --limit;
+ break;
+ }
+ if (line_counter != NULL)
+ ++*line_counter;
+ if (output)
+ ++op->lineno;
+ break;
+ case '*':
+ if (*bp == '\\' && bp[1] == '\n')
+ newline_fix (bp);
+ if (*bp == '/') {
+ if (output)
+ *op->bufp++ = '/';
+ ip->bufp = ++bp;
+ return bp;
+ }
+ break;
+ }
+ }
+
+ if (!nowarn)
+ error_with_line (line_for_error (start_line), "unterminated comment");
+ ip->bufp = bp;
+ return bp;
+}
+
+/*
+ * Skip over a quoted string. BP points to the opening quote.
+ * Returns a pointer after the closing quote. Don't go past LIMIT.
+ * START_LINE is the line number of the starting point (but it need
+ * not be valid if the starting point is inside a macro expansion).
+ *
+ * The input stack state is not changed.
+ *
+ * If COUNT_NEWLINES is nonzero, it points to an int to increment
+ * for each newline passed.
+ *
+ * If BACKSLASH_NEWLINES_P is nonzero, store 1 thru it
+ * if we pass a backslash-newline.
+ *
+ * If EOFP is nonzero, set *EOFP to 1 if the string is unterminated.
+ */
+static U_CHAR *
+skip_quoted_string (bp, limit, start_line, count_newlines, backslash_newlines_p, eofp)
+ register U_CHAR *bp;
+ register U_CHAR *limit;
+ int start_line;
+ int *count_newlines;
+ int *backslash_newlines_p;
+ int *eofp;
+{
+ register U_CHAR c, match;
+
+ match = *bp++;
+ while (1) {
+ if (bp >= limit) {
+ error_with_line (line_for_error (start_line),
+ "unterminated string or character constant");
+ error_with_line (multiline_string_line,
+ "possible real start of unterminated constant");
+ multiline_string_line = 0;
+ if (eofp)
+ *eofp = 1;
+ break;
+ }
+ c = *bp++;
+ if (c == '\\') {
+ while (*bp == '\\' && bp[1] == '\n') {
+ if (backslash_newlines_p)
+ *backslash_newlines_p = 1;
+ if (count_newlines)
+ ++*count_newlines;
+ bp += 2;
+ }
+ if (*bp == '\n' && count_newlines) {
+ if (backslash_newlines_p)
+ *backslash_newlines_p = 1;
+ ++*count_newlines;
+ }
+ bp++;
+ } else if (c == '\n') {
+ if (traditional) {
+ /* Unterminated strings and character constants are 'legal'. */
+ bp--; /* Don't consume the newline. */
+ if (eofp)
+ *eofp = 1;
+ break;
+ }
+ if (pedantic || match == '\'') {
+ error_with_line (line_for_error (start_line),
+ "unterminated string or character constant");
+ bp--;
+ if (eofp)
+ *eofp = 1;
+ break;
+ }
+ /* If not traditional, then allow newlines inside strings. */
+ if (count_newlines)
+ ++*count_newlines;
+ if (multiline_string_line == 0)
+ multiline_string_line = start_line;
+ } else if (c == match)
+ break;
+ }
+ return bp;
+}
+
+/* Place into DST a quoted string representing the string SRC.
+ Return the address of DST's terminating null. */
+static char *
+quote_string (dst, src)
+ char *dst, *src;
+{
+ U_CHAR c;
+
+ *dst++ = '\"';
+ for (;;)
+ switch ((c = *src++))
+ {
+ default:
+ if (isprint (c))
+ *dst++ = c;
+ else
+ {
+ sprintf (dst, "\\%03o", c);
+ dst += 4;
+ }
+ break;
+
+ case '\"':
+ case '\\':
+ *dst++ = '\\';
+ *dst++ = c;
+ break;
+
+ case '\0':
+ *dst++ = '\"';
+ *dst = '\0';
+ return dst;
+ }
+}
+
+/* Skip across a group of balanced parens, starting from IP->bufp.
+ IP->bufp is updated. Use this with IP->bufp pointing at an open-paren.
+
+ This does not handle newlines, because it's used for the arg of #if,
+ where there aren't any newlines. Also, backslash-newline can't appear. */
+
+static U_CHAR *
+skip_paren_group (ip)
+ register FILE_BUF *ip;
+{
+ U_CHAR *limit = ip->buf + ip->length;
+ U_CHAR *p = ip->bufp;
+ int depth = 0;
+ int lines_dummy = 0;
+
+ while (p != limit) {
+ int c = *p++;
+ switch (c) {
+ case '(':
+ depth++;
+ break;
+
+ case ')':
+ depth--;
+ if (depth == 0)
+ return ip->bufp = p;
+ break;
+
+ case '/':
+ if (*p == '*') {
+ ip->bufp = p;
+ p = skip_to_end_of_comment (ip, &lines_dummy, 0);
+ p = ip->bufp;
+ }
+
+ case '"':
+ case '\'':
+ {
+ int eofp = 0;
+ p = skip_quoted_string (p - 1, limit, 0, NULL_PTR, NULL_PTR, &eofp);
+ if (eofp)
+ return ip->bufp = p;
+ }
+ break;
+ }
+ }
+
+ ip->bufp = p;
+ return p;
+}
+
+/*
+ * write out a #line command, for instance, after an #include file.
+ * If CONDITIONAL is nonzero, we can omit the #line if it would
+ * appear to be a no-op, and we can output a few newlines instead
+ * if we want to increase the line number by a small amount.
+ * FILE_CHANGE says whether we are entering a file, leaving, or neither.
+ */
+
+static void
+output_line_command (ip, op, conditional, file_change)
+ FILE_BUF *ip, *op;
+ int conditional;
+ enum file_change_code file_change;
+{
+ int len;
+ char *line_cmd_buf, *line_end;
+
+ if (no_line_commands
+ || ip->fname == NULL
+ || no_output) {
+ op->lineno = ip->lineno;
+ return;
+ }
+
+ if (conditional) {
+ if (ip->lineno == op->lineno)
+ return;
+
+ /* If the inherited line number is a little too small,
+ output some newlines instead of a #line command. */
+ if (ip->lineno > op->lineno && ip->lineno < op->lineno + 8) {
+ check_expand (op, 10);
+ while (ip->lineno > op->lineno) {
+ *op->bufp++ = '\n';
+ op->lineno++;
+ }
+ return;
+ }
+ }
+
+ /* Don't output a line number of 0 if we can help it. */
+ if (ip->lineno == 0 && ip->bufp - ip->buf < ip->length
+ && *ip->bufp == '\n') {
+ ip->lineno++;
+ ip->bufp++;
+ }
+
+ line_cmd_buf = (char *) alloca (4 * strlen (ip->nominal_fname) + 100);
+#ifdef OUTPUT_LINE_COMMANDS
+ sprintf (line_cmd_buf, "#line %d ", ip->lineno);
+#else
+ sprintf (line_cmd_buf, "# %d ", ip->lineno);
+#endif
+ line_end = quote_string (line_cmd_buf + strlen (line_cmd_buf),
+ ip->nominal_fname);
+ if (file_change != same_file) {
+ *line_end++ = ' ';
+ *line_end++ = file_change == enter_file ? '1' : '2';
+ }
+ /* Tell cc1 if following text comes from a system header file. */
+ if (ip->system_header_p) {
+ *line_end++ = ' ';
+ *line_end++ = '3';
+ }
+#ifndef NO_IMPLICIT_EXTERN_C
+ /* Tell cc1plus if following text should be treated as C. */
+ if (ip->system_header_p == 2 && cplusplus) {
+ *line_end++ = ' ';
+ *line_end++ = '4';
+ }
+#endif
+ *line_end++ = '\n';
+ len = line_end - line_cmd_buf;
+ check_expand (op, len + 1);
+ if (op->bufp > op->buf && op->bufp[-1] != '\n')
+ *op->bufp++ = '\n';
+ bcopy ((char *) line_cmd_buf, (char *) op->bufp, len);
+ op->bufp += len;
+ op->lineno = ip->lineno;
+}
+
+/* This structure represents one parsed argument in a macro call.
+ `raw' points to the argument text as written (`raw_length' is its length).
+ `expanded' points to the argument's macro-expansion
+ (its length is `expand_length').
+ `stringified_length' is the length the argument would have
+ if stringified.
+ `use_count' is the number of times this macro arg is substituted
+ into the macro. If the actual use count exceeds 10,
+ the value stored is 10.
+ `free1' and `free2', if nonzero, point to blocks to be freed
+ when the macro argument data is no longer needed. */
+
+struct argdata {
+ U_CHAR *raw, *expanded;
+ int raw_length, expand_length;
+ int stringified_length;
+ U_CHAR *free1, *free2;
+ char newlines;
+ char comments;
+ char use_count;
+};
+
+/* Expand a macro call.
+ HP points to the symbol that is the macro being called.
+ Put the result of expansion onto the input stack
+ so that subsequent input by our caller will use it.
+
+ If macro wants arguments, caller has already verified that
+ an argument list follows; arguments come from the input stack. */
+
+static void
+macroexpand (hp, op)
+ HASHNODE *hp;
+ FILE_BUF *op;
+{
+ int nargs;
+ DEFINITION *defn = hp->value.defn;
+ register U_CHAR *xbuf;
+ int xbuf_len;
+ int start_line = instack[indepth].lineno;
+ int rest_args, rest_zero;
+
+ CHECK_DEPTH (return;);
+
+ /* it might not actually be a macro. */
+ if (hp->type != T_MACRO) {
+ special_symbol (hp, op);
+ return;
+ }
+
+ /* This macro is being used inside a #if, which means it must be */
+ /* recorded as a precondition. */
+ if (pcp_inside_if && pcp_outfile && defn->predefined)
+ dump_single_macro (hp, pcp_outfile);
+
+ nargs = defn->nargs;
+
+ if (nargs >= 0) {
+ register int i;
+ struct argdata *args;
+ char *parse_error = 0;
+
+ args = (struct argdata *) alloca ((nargs + 1) * sizeof (struct argdata));
+
+ for (i = 0; i < nargs; i++) {
+ args[i].raw = (U_CHAR *) "";
+ args[i].expanded = 0;
+ args[i].raw_length = args[i].expand_length
+ = args[i].stringified_length = 0;
+ args[i].free1 = args[i].free2 = 0;
+ args[i].use_count = 0;
+ }
+
+ /* Parse all the macro args that are supplied. I counts them.
+ The first NARGS args are stored in ARGS.
+ The rest are discarded.
+ If rest_args is set then we assume macarg absorbed the rest of the args.
+ */
+ i = 0;
+ rest_args = 0;
+ do {
+ /* Discard the open-parenthesis or comma before the next arg. */
+ ++instack[indepth].bufp;
+ if (rest_args)
+ continue;
+ if (i < nargs || (nargs == 0 && i == 0)) {
+ /* if we are working on last arg which absorbs rest of args... */
+ if (i == nargs - 1 && defn->rest_args)
+ rest_args = 1;
+ parse_error = macarg (&args[i], rest_args);
+ }
+ else
+ parse_error = macarg (NULL_PTR, 0);
+ if (parse_error) {
+ error_with_line (line_for_error (start_line), parse_error);
+ break;
+ }
+ i++;
+ } while (*instack[indepth].bufp != ')');
+
+ /* If we got one arg but it was just whitespace, call that 0 args. */
+ if (i == 1) {
+ register U_CHAR *bp = args[0].raw;
+ register U_CHAR *lim = bp + args[0].raw_length;
+ /* cpp.texi says for foo ( ) we provide one argument.
+ However, if foo wants just 0 arguments, treat this as 0. */
+ if (nargs == 0)
+ while (bp != lim && is_space[*bp]) bp++;
+ if (bp == lim)
+ i = 0;
+ }
+
+ /* Don't output an error message if we have already output one for
+ a parse error above. */
+ rest_zero = 0;
+ if (nargs == 0 && i > 0) {
+ if (! parse_error)
+ error ("arguments given to macro `%s'", hp->name);
+ } else if (i < nargs) {
+ /* traditional C allows foo() if foo wants one argument. */
+ if (nargs == 1 && i == 0 && traditional)
+ ;
+ /* the rest args token is allowed to absorb 0 tokens */
+ else if (i == nargs - 1 && defn->rest_args)
+ rest_zero = 1;
+ else if (parse_error)
+ ;
+ else if (i == 0)
+ error ("macro `%s' used without args", hp->name);
+ else if (i == 1)
+ error ("macro `%s' used with just one arg", hp->name);
+ else
+ error ("macro `%s' used with only %d args", hp->name, i);
+ } else if (i > nargs) {
+ if (! parse_error)
+ error ("macro `%s' used with too many (%d) args", hp->name, i);
+ }
+
+ /* Swallow the closeparen. */
+ ++instack[indepth].bufp;
+
+ /* If macro wants zero args, we parsed the arglist for checking only.
+ Read directly from the macro definition. */
+ if (nargs == 0) {
+ xbuf = defn->expansion;
+ xbuf_len = defn->length;
+ } else {
+ register U_CHAR *exp = defn->expansion;
+ register int offset; /* offset in expansion,
+ copied a piece at a time */
+ register int totlen; /* total amount of exp buffer filled so far */
+
+ register struct reflist *ap, *last_ap;
+
+ /* Macro really takes args. Compute the expansion of this call. */
+
+ /* Compute length in characters of the macro's expansion.
+ Also count number of times each arg is used. */
+ xbuf_len = defn->length;
+ for (ap = defn->pattern; ap != NULL; ap = ap->next) {
+ if (ap->stringify)
+ xbuf_len += args[ap->argno].stringified_length;
+ else if (ap->raw_before || ap->raw_after || traditional)
+ /* Add 4 for two newline-space markers to prevent
+ token concatenation. */
+ xbuf_len += args[ap->argno].raw_length + 4;
+ else {
+ /* We have an ordinary (expanded) occurrence of the arg.
+ So compute its expansion, if we have not already. */
+ if (args[ap->argno].expanded == 0) {
+ FILE_BUF obuf;
+ obuf = expand_to_temp_buffer (args[ap->argno].raw,
+ args[ap->argno].raw + args[ap->argno].raw_length,
+ 1, 0);
+
+ args[ap->argno].expanded = obuf.buf;
+ args[ap->argno].expand_length = obuf.length;
+ args[ap->argno].free2 = obuf.buf;
+ }
+
+ /* Add 4 for two newline-space markers to prevent
+ token concatenation. */
+ xbuf_len += args[ap->argno].expand_length + 4;
+ }
+ if (args[ap->argno].use_count < 10)
+ args[ap->argno].use_count++;
+ }
+
+ xbuf = (U_CHAR *) xmalloc (xbuf_len + 1);
+
+ /* Generate in XBUF the complete expansion
+ with arguments substituted in.
+ TOTLEN is the total size generated so far.
+ OFFSET is the index in the definition
+ of where we are copying from. */
+ offset = totlen = 0;
+ for (last_ap = NULL, ap = defn->pattern; ap != NULL;
+ last_ap = ap, ap = ap->next) {
+ register struct argdata *arg = &args[ap->argno];
+ int count_before = totlen;
+
+ /* Add chars to XBUF. */
+ for (i = 0; i < ap->nchars; i++, offset++)
+ xbuf[totlen++] = exp[offset];
+
+ /* If followed by an empty rest arg with concatenation,
+ delete the last run of nonwhite chars. */
+ if (rest_zero && totlen > count_before
+ && ((ap->rest_args && ap->raw_before)
+ || (last_ap != NULL && last_ap->rest_args
+ && last_ap->raw_after))) {
+ /* Delete final whitespace. */
+ while (totlen > count_before && is_space[xbuf[totlen - 1]]) {
+ totlen--;
+ }
+
+ /* Delete the nonwhites before them. */
+ while (totlen > count_before && ! is_space[xbuf[totlen - 1]]) {
+ totlen--;
+ }
+ }
+
+ if (ap->stringify != 0) {
+ int arglen = arg->raw_length;
+ int escaped = 0;
+ int in_string = 0;
+ int c;
+ i = 0;
+ while (i < arglen
+ && (c = arg->raw[i], is_space[c]))
+ i++;
+ while (i < arglen
+ && (c = arg->raw[arglen - 1], is_space[c]))
+ arglen--;
+ if (!traditional)
+ xbuf[totlen++] = '\"'; /* insert beginning quote */
+ for (; i < arglen; i++) {
+ c = arg->raw[i];
+
+ /* Special markers Newline Space
+ generate nothing for a stringified argument. */
+ if (c == '\n' && arg->raw[i+1] != '\n') {
+ i++;
+ continue;
+ }
+
+ /* Internal sequences of whitespace are replaced by one space
+ except within an string or char token. */
+ if (! in_string
+ && (c == '\n' ? arg->raw[i+1] == '\n' : is_space[c])) {
+ while (1) {
+ /* Note that Newline Space does occur within whitespace
+ sequences; consider it part of the sequence. */
+ if (c == '\n' && is_space[arg->raw[i+1]])
+ i += 2;
+ else if (c != '\n' && is_space[c])
+ i++;
+ else break;
+ c = arg->raw[i];
+ }
+ i--;
+ c = ' ';
+ }
+
+ if (escaped)
+ escaped = 0;
+ else {
+ if (c == '\\')
+ escaped = 1;
+ if (in_string) {
+ if (c == in_string)
+ in_string = 0;
+ } else if (c == '\"' || c == '\'')
+ in_string = c;
+ }
+
+ /* Escape these chars */
+ if (c == '\"' || (in_string && c == '\\'))
+ xbuf[totlen++] = '\\';
+ if (isprint (c))
+ xbuf[totlen++] = c;
+ else {
+ sprintf ((char *) &xbuf[totlen], "\\%03o", (unsigned int) c);
+ totlen += 4;
+ }
+ }
+ if (!traditional)
+ xbuf[totlen++] = '\"'; /* insert ending quote */
+ } else if (ap->raw_before || ap->raw_after || traditional) {
+ U_CHAR *p1 = arg->raw;
+ U_CHAR *l1 = p1 + arg->raw_length;
+ if (ap->raw_before) {
+ while (p1 != l1 && is_space[*p1]) p1++;
+ while (p1 != l1 && is_idchar[*p1])
+ xbuf[totlen++] = *p1++;
+ /* Delete any no-reexpansion marker that follows
+ an identifier at the beginning of the argument
+ if the argument is concatenated with what precedes it. */
+ if (p1[0] == '\n' && p1[1] == '-')
+ p1 += 2;
+ } else if (!traditional) {
+ /* Ordinary expanded use of the argument.
+ Put in newline-space markers to prevent token pasting. */
+ xbuf[totlen++] = '\n';
+ xbuf[totlen++] = ' ';
+ }
+ if (ap->raw_after) {
+ /* Arg is concatenated after: delete trailing whitespace,
+ whitespace markers, and no-reexpansion markers. */
+ while (p1 != l1) {
+ if (is_space[l1[-1]]) l1--;
+ else if (l1[-1] == '-') {
+ U_CHAR *p2 = l1 - 1;
+ /* If a `-' is preceded by an odd number of newlines then it
+ and the last newline are a no-reexpansion marker. */
+ while (p2 != p1 && p2[-1] == '\n') p2--;
+ if ((l1 - 1 - p2) & 1) {
+ l1 -= 2;
+ }
+ else break;
+ }
+ else break;
+ }
+ }
+
+ bcopy ((char *) p1, (char *) (xbuf + totlen), l1 - p1);
+ totlen += l1 - p1;
+ if (!traditional && !ap->raw_after) {
+ /* Ordinary expanded use of the argument.
+ Put in newline-space markers to prevent token pasting. */
+ xbuf[totlen++] = '\n';
+ xbuf[totlen++] = ' ';
+ }
+ } else {
+ /* Ordinary expanded use of the argument.
+ Put in newline-space markers to prevent token pasting. */
+ if (!traditional) {
+ xbuf[totlen++] = '\n';
+ xbuf[totlen++] = ' ';
+ }
+ bcopy ((char *) arg->expanded, (char *) (xbuf + totlen),
+ arg->expand_length);
+ totlen += arg->expand_length;
+ if (!traditional) {
+ xbuf[totlen++] = '\n';
+ xbuf[totlen++] = ' ';
+ }
+ /* If a macro argument with newlines is used multiple times,
+ then only expand the newlines once. This avoids creating output
+ lines which don't correspond to any input line, which confuses
+ gdb and gcov. */
+ if (arg->use_count > 1 && arg->newlines > 0) {
+ /* Don't bother doing change_newlines for subsequent
+ uses of arg. */
+ arg->use_count = 1;
+ arg->expand_length
+ = change_newlines (arg->expanded, arg->expand_length);
+ }
+ }
+
+ if (totlen > xbuf_len)
+ abort ();
+ }
+
+ /* if there is anything left of the definition
+ after handling the arg list, copy that in too. */
+
+ for (i = offset; i < defn->length; i++) {
+ /* if we've reached the end of the macro */
+ if (exp[i] == ')')
+ rest_zero = 0;
+ if (! (rest_zero && last_ap != NULL && last_ap->rest_args
+ && last_ap->raw_after))
+ xbuf[totlen++] = exp[i];
+ }
+
+ xbuf[totlen] = 0;
+ xbuf_len = totlen;
+
+ for (i = 0; i < nargs; i++) {
+ if (args[i].free1 != 0)
+ free (args[i].free1);
+ if (args[i].free2 != 0)
+ free (args[i].free2);
+ }
+ }
+ } else {
+ xbuf = defn->expansion;
+ xbuf_len = defn->length;
+ }
+
+ /* Now put the expansion on the input stack
+ so our caller will commence reading from it. */
+ {
+ register FILE_BUF *ip2;
+
+ ip2 = &instack[++indepth];
+
+ ip2->fname = 0;
+ ip2->nominal_fname = 0;
+ /* This may not be exactly correct, but will give much better error
+ messages for nested macro calls than using a line number of zero. */
+ ip2->lineno = start_line;
+ ip2->buf = xbuf;
+ ip2->length = xbuf_len;
+ ip2->bufp = xbuf;
+ ip2->free_ptr = (nargs > 0) ? xbuf : 0;
+ ip2->macro = hp;
+ ip2->if_stack = if_stack;
+ ip2->system_header_p = 0;
+
+ /* Recursive macro use sometimes works traditionally.
+ #define foo(x,y) bar (x (y,0), y)
+ foo (foo, baz) */
+
+ if (!traditional)
+ hp->type = T_DISABLED;
+ }
+}
+
+/*
+ * Parse a macro argument and store the info on it into *ARGPTR.
+ * REST_ARGS is passed to macarg1 to make it absorb the rest of the args.
+ * Return nonzero to indicate a syntax error.
+ */
+
+static char *
+macarg (argptr, rest_args)
+ register struct argdata *argptr;
+ int rest_args;
+{
+ FILE_BUF *ip = &instack[indepth];
+ int paren = 0;
+ int newlines = 0;
+ int comments = 0;
+
+ /* Try to parse as much of the argument as exists at this
+ input stack level. */
+ U_CHAR *bp = macarg1 (ip->bufp, ip->buf + ip->length,
+ &paren, &newlines, &comments, rest_args);
+
+ /* If we find the end of the argument at this level,
+ set up *ARGPTR to point at it in the input stack. */
+ if (!(ip->fname != 0 && (newlines != 0 || comments != 0))
+ && bp != ip->buf + ip->length) {
+ if (argptr != 0) {
+ argptr->raw = ip->bufp;
+ argptr->raw_length = bp - ip->bufp;
+ argptr->newlines = newlines;
+ }
+ ip->bufp = bp;
+ } else {
+ /* This input stack level ends before the macro argument does.
+ We must pop levels and keep parsing.
+ Therefore, we must allocate a temporary buffer and copy
+ the macro argument into it. */
+ int bufsize = bp - ip->bufp;
+ int extra = newlines;
+ U_CHAR *buffer = (U_CHAR *) xmalloc (bufsize + extra + 1);
+ int final_start = 0;
+
+ bcopy ((char *) ip->bufp, (char *) buffer, bufsize);
+ ip->bufp = bp;
+ ip->lineno += newlines;
+
+ while (bp == ip->buf + ip->length) {
+ if (instack[indepth].macro == 0) {
+ free (buffer);
+ return "unterminated macro call";
+ }
+ ip->macro->type = T_MACRO;
+ if (ip->free_ptr)
+ free (ip->free_ptr);
+ ip = &instack[--indepth];
+ newlines = 0;
+ comments = 0;
+ bp = macarg1 (ip->bufp, ip->buf + ip->length, &paren,
+ &newlines, &comments, rest_args);
+ final_start = bufsize;
+ bufsize += bp - ip->bufp;
+ extra += newlines;
+ buffer = (U_CHAR *) xrealloc (buffer, bufsize + extra + 1);
+ bcopy ((char *) ip->bufp, (char *) (buffer + bufsize - (bp - ip->bufp)),
+ bp - ip->bufp);
+ ip->bufp = bp;
+ ip->lineno += newlines;
+ }
+
+ /* Now, if arg is actually wanted, record its raw form,
+ discarding comments and duplicating newlines in whatever
+ part of it did not come from a macro expansion.
+ EXTRA space has been preallocated for duplicating the newlines.
+ FINAL_START is the index of the start of that part. */
+ if (argptr != 0) {
+ argptr->raw = buffer;
+ argptr->raw_length = bufsize;
+ argptr->free1 = buffer;
+ argptr->newlines = newlines;
+ argptr->comments = comments;
+ if ((newlines || comments) && ip->fname != 0)
+ argptr->raw_length
+ = final_start +
+ discard_comments (argptr->raw + final_start,
+ argptr->raw_length - final_start,
+ newlines);
+ argptr->raw[argptr->raw_length] = 0;
+ if (argptr->raw_length > bufsize + extra)
+ abort ();
+ }
+ }
+
+ /* If we are not discarding this argument,
+ macroexpand it and compute its length as stringified.
+ All this info goes into *ARGPTR. */
+
+ if (argptr != 0) {
+ register U_CHAR *buf, *lim;
+ register int totlen;
+
+ buf = argptr->raw;
+ lim = buf + argptr->raw_length;
+
+ while (buf != lim && is_space[*buf])
+ buf++;
+ while (buf != lim && is_space[lim[-1]])
+ lim--;
+ totlen = traditional ? 0 : 2; /* Count opening and closing quote. */
+ while (buf != lim) {
+ register U_CHAR c = *buf++;
+ totlen++;
+ /* Internal sequences of whitespace are replaced by one space
+ in most cases, but not always. So count all the whitespace
+ in case we need to keep it all. */
+#if 0
+ if (is_space[c])
+ SKIP_ALL_WHITE_SPACE (buf);
+ else
+#endif
+ if (c == '\"' || c == '\\') /* escape these chars */
+ totlen++;
+ else if (!isprint (c))
+ totlen += 3;
+ }
+ argptr->stringified_length = totlen;
+ }
+ return 0;
+}
+
+/* Scan text from START (inclusive) up to LIMIT (exclusive),
+ counting parens in *DEPTHPTR,
+ and return if reach LIMIT
+ or before a `)' that would make *DEPTHPTR negative
+ or before a comma when *DEPTHPTR is zero.
+ Single and double quotes are matched and termination
+ is inhibited within them. Comments also inhibit it.
+ Value returned is pointer to stopping place.
+
+ Increment *NEWLINES each time a newline is passed.
+ REST_ARGS notifies macarg1 that it should absorb the rest of the args.
+ Set *COMMENTS to 1 if a comment is seen. */
+
+static U_CHAR *
+macarg1 (start, limit, depthptr, newlines, comments, rest_args)
+ U_CHAR *start;
+ register U_CHAR *limit;
+ int *depthptr, *newlines, *comments;
+ int rest_args;
+{
+ register U_CHAR *bp = start;
+
+ while (bp < limit) {
+ switch (*bp) {
+ case '(':
+ (*depthptr)++;
+ break;
+ case ')':
+ if (--(*depthptr) < 0)
+ return bp;
+ break;
+ case '\\':
+ /* Traditionally, backslash makes following char not special. */
+ if (bp + 1 < limit && traditional)
+ {
+ bp++;
+ /* But count source lines anyway. */
+ if (*bp == '\n')
+ ++*newlines;
+ }
+ break;
+ case '\n':
+ ++*newlines;
+ break;
+ case '/':
+ if (bp[1] == '\\' && bp[2] == '\n')
+ newline_fix (bp + 1);
+ if (cplusplus_comments && bp[1] == '/') {
+ *comments = 1;
+ bp += 2;
+ while (bp < limit && (*bp != '\n' || bp[-1] == '\\')) {
+ if (*bp == '\n') ++*newlines;
+ bp++;
+ }
+ break;
+ }
+ if (bp[1] != '*' || bp + 1 >= limit)
+ break;
+ *comments = 1;
+ bp += 2;
+ while (bp + 1 < limit) {
+ if (bp[0] == '*'
+ && bp[1] == '\\' && bp[2] == '\n')
+ newline_fix (bp + 1);
+ if (bp[0] == '*' && bp[1] == '/')
+ break;
+ if (*bp == '\n') ++*newlines;
+ bp++;
+ }
+ break;
+ case '\'':
+ case '\"':
+ {
+ int quotec;
+ for (quotec = *bp++; bp + 1 < limit && *bp != quotec; bp++) {
+ if (*bp == '\\') {
+ bp++;
+ if (*bp == '\n')
+ ++*newlines;
+ while (*bp == '\\' && bp[1] == '\n') {
+ bp += 2;
+ }
+ } else if (*bp == '\n') {
+ ++*newlines;
+ if (quotec == '\'')
+ break;
+ }
+ }
+ }
+ break;
+ case ',':
+ /* if we've returned to lowest level and we aren't absorbing all args */
+ if ((*depthptr) == 0 && rest_args == 0)
+ return bp;
+ break;
+ }
+ bp++;
+ }
+
+ return bp;
+}
+
+/* Discard comments and duplicate newlines
+ in the string of length LENGTH at START,
+ except inside of string constants.
+ The string is copied into itself with its beginning staying fixed.
+
+ NEWLINES is the number of newlines that must be duplicated.
+ We assume that that much extra space is available past the end
+ of the string. */
+
+static int
+discard_comments (start, length, newlines)
+ U_CHAR *start;
+ int length;
+ int newlines;
+{
+ register U_CHAR *ibp;
+ register U_CHAR *obp;
+ register U_CHAR *limit;
+ register int c;
+
+ /* If we have newlines to duplicate, copy everything
+ that many characters up. Then, in the second part,
+ we will have room to insert the newlines
+ while copying down.
+ NEWLINES may actually be too large, because it counts
+ newlines in string constants, and we don't duplicate those.
+ But that does no harm. */
+ if (newlines > 0) {
+ ibp = start + length;
+ obp = ibp + newlines;
+ limit = start;
+ while (limit != ibp)
+ *--obp = *--ibp;
+ }
+
+ ibp = start + newlines;
+ limit = start + length + newlines;
+ obp = start;
+
+ while (ibp < limit) {
+ *obp++ = c = *ibp++;
+ switch (c) {
+ case '\n':
+ /* Duplicate the newline. */
+ *obp++ = '\n';
+ break;
+
+ case '\\':
+ if (*ibp == '\n') {
+ obp--;
+ ibp++;
+ }
+ break;
+
+ case '/':
+ if (*ibp == '\\' && ibp[1] == '\n')
+ newline_fix (ibp);
+ /* Delete any comment. */
+ if (cplusplus_comments && ibp[0] == '/') {
+ /* Comments are equivalent to spaces. */
+ obp[-1] = ' ';
+ ibp++;
+ while (ibp < limit && (*ibp != '\n' || ibp[-1] == '\\'))
+ ibp++;
+ break;
+ }
+ if (ibp[0] != '*' || ibp + 1 >= limit)
+ break;
+ /* Comments are equivalent to spaces. */
+ obp[-1] = ' ';
+ ibp++;
+ while (ibp + 1 < limit) {
+ if (ibp[0] == '*'
+ && ibp[1] == '\\' && ibp[2] == '\n')
+ newline_fix (ibp + 1);
+ if (ibp[0] == '*' && ibp[1] == '/')
+ break;
+ ibp++;
+ }
+ ibp += 2;
+ break;
+
+ case '\'':
+ case '\"':
+ /* Notice and skip strings, so that we don't
+ think that comments start inside them,
+ and so we don't duplicate newlines in them. */
+ {
+ int quotec = c;
+ while (ibp < limit) {
+ *obp++ = c = *ibp++;
+ if (c == quotec)
+ break;
+ if (c == '\n' && quotec == '\'')
+ break;
+ if (c == '\\' && ibp < limit) {
+ while (*ibp == '\\' && ibp[1] == '\n')
+ ibp += 2;
+ *obp++ = *ibp++;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ return obp - start;
+}
+
+/* Turn newlines to spaces in the string of length LENGTH at START,
+ except inside of string constants.
+ The string is copied into itself with its beginning staying fixed. */
+
+static int
+change_newlines (start, length)
+ U_CHAR *start;
+ int length;
+{
+ register U_CHAR *ibp;
+ register U_CHAR *obp;
+ register U_CHAR *limit;
+ register int c;
+
+ ibp = start;
+ limit = start + length;
+ obp = start;
+
+ while (ibp < limit) {
+ *obp++ = c = *ibp++;
+ switch (c) {
+ case '\n':
+ /* If this is a NEWLINE NEWLINE, then this is a real newline in the
+ string. Skip past the newline and its duplicate.
+ Put a space in the output. */
+ if (*ibp == '\n')
+ {
+ ibp++;
+ obp--;
+ *obp++ = ' ';
+ }
+ break;
+
+ case '\'':
+ case '\"':
+ /* Notice and skip strings, so that we don't delete newlines in them. */
+ {
+ int quotec = c;
+ while (ibp < limit) {
+ *obp++ = c = *ibp++;
+ if (c == quotec)
+ break;
+ if (c == '\n' && quotec == '\'')
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return obp - start;
+}
+
+/*
+ * error - print error message and increment count of errors.
+ */
+
+void
+error (msg, arg1, arg2, arg3)
+ char *msg;
+ char *arg1, *arg2, *arg3;
+{
+ int i;
+ FILE_BUF *ip = NULL;
+
+ print_containing_files ();
+
+ for (i = indepth; i >= 0; i--)
+ if (instack[i].fname != NULL) {
+ ip = &instack[i];
+ break;
+ }
+
+ if (ip != NULL)
+ fprintf (stderr, "%s:%d: ", ip->nominal_fname, ip->lineno);
+ fprintf (stderr, msg, arg1, arg2, arg3);
+ fprintf (stderr, "\n");
+ errors++;
+}
+
+/* Error including a message from `errno'. */
+
+static void
+error_from_errno (name)
+ char *name;
+{
+ int i;
+ FILE_BUF *ip = NULL;
+
+ print_containing_files ();
+
+ for (i = indepth; i >= 0; i--)
+ if (instack[i].fname != NULL) {
+ ip = &instack[i];
+ break;
+ }
+
+ if (ip != NULL)
+ fprintf (stderr, "%s:%d: ", ip->nominal_fname, ip->lineno);
+
+ if (errno < sys_nerr)
+ fprintf (stderr, "%s: %s\n", name, sys_errlist[errno]);
+ else
+ fprintf (stderr, "%s: undocumented I/O error\n", name);
+
+ errors++;
+}
+
+/* Print error message but don't count it. */
+
+void
+warning (msg, arg1, arg2, arg3)
+ char *msg;
+ char *arg1, *arg2, *arg3;
+{
+ int i;
+ FILE_BUF *ip = NULL;
+
+ if (inhibit_warnings)
+ return;
+
+ if (warnings_are_errors)
+ errors++;
+
+ print_containing_files ();
+
+ for (i = indepth; i >= 0; i--)
+ if (instack[i].fname != NULL) {
+ ip = &instack[i];
+ break;
+ }
+
+ if (ip != NULL)
+ fprintf (stderr, "%s:%d: ", ip->nominal_fname, ip->lineno);
+ fprintf (stderr, "warning: ");
+ fprintf (stderr, msg, arg1, arg2, arg3);
+ fprintf (stderr, "\n");
+}
+
+static void
+error_with_line (line, msg, arg1, arg2, arg3)
+ int line;
+ char *msg;
+ char *arg1, *arg2, *arg3;
+{
+ int i;
+ FILE_BUF *ip = NULL;
+
+ print_containing_files ();
+
+ for (i = indepth; i >= 0; i--)
+ if (instack[i].fname != NULL) {
+ ip = &instack[i];
+ break;
+ }
+
+ if (ip != NULL)
+ fprintf (stderr, "%s:%d: ", ip->nominal_fname, line);
+ fprintf (stderr, msg, arg1, arg2, arg3);
+ fprintf (stderr, "\n");
+ errors++;
+}
+
+static void
+warning_with_line (line, msg, arg1, arg2, arg3)
+ int line;
+ char *msg;
+ char *arg1, *arg2, *arg3;
+{
+ int i;
+ FILE_BUF *ip = NULL;
+
+ if (inhibit_warnings)
+ return;
+
+ if (warnings_are_errors)
+ errors++;
+
+ print_containing_files ();
+
+ for (i = indepth; i >= 0; i--)
+ if (instack[i].fname != NULL) {
+ ip = &instack[i];
+ break;
+ }
+
+ if (ip != NULL)
+ fprintf (stderr, "%s:%d: ", ip->nominal_fname, line);
+ fprintf (stderr, "warning: ");
+ fprintf (stderr, msg, arg1, arg2, arg3);
+ fprintf (stderr, "\n");
+}
+
+/* print an error message and maybe count it. */
+
+void
+pedwarn (msg, arg1, arg2, arg3)
+ char *msg;
+ char *arg1, *arg2, *arg3;
+{
+ if (pedantic_errors)
+ error (msg, arg1, arg2, arg3);
+ else
+ warning (msg, arg1, arg2, arg3);
+}
+
+void
+pedwarn_with_line (line, msg, arg1, arg2, arg3)
+ int line;
+ char *msg;
+ char *arg1, *arg2, *arg3;
+{
+ if (pedantic_errors)
+ error_with_line (line, msg, arg1, arg2, arg3);
+ else
+ warning_with_line (line, msg, arg1, arg2, arg3);
+}
+
+/* Report a warning (or an error if pedantic_errors)
+ giving specified file name and line number, not current. */
+
+static void
+pedwarn_with_file_and_line (file, line, msg, arg1, arg2, arg3)
+ char *file;
+ int line;
+ char *msg;
+ char *arg1, *arg2, *arg3;
+{
+ if (!pedantic_errors && inhibit_warnings)
+ return;
+ if (file != NULL)
+ fprintf (stderr, "%s:%d: ", file, line);
+ if (pedantic_errors)
+ errors++;
+ if (!pedantic_errors)
+ fprintf (stderr, "warning: ");
+ fprintf (stderr, msg, arg1, arg2, arg3);
+ fprintf (stderr, "\n");
+}
+
+/* Print the file names and line numbers of the #include
+ commands which led to the current file. */
+
+static void
+print_containing_files ()
+{
+ FILE_BUF *ip = NULL;
+ int i;
+ int first = 1;
+
+ /* If stack of files hasn't changed since we last printed
+ this info, don't repeat it. */
+ if (last_error_tick == input_file_stack_tick)
+ return;
+
+ for (i = indepth; i >= 0; i--)
+ if (instack[i].fname != NULL) {
+ ip = &instack[i];
+ break;
+ }
+
+ /* Give up if we don't find a source file. */
+ if (ip == NULL)
+ return;
+
+ /* Find the other, outer source files. */
+ for (i--; i >= 0; i--)
+ if (instack[i].fname != NULL) {
+ ip = &instack[i];
+ if (first) {
+ first = 0;
+ fprintf (stderr, "In file included");
+ } else {
+ fprintf (stderr, ",\n ");
+ }
+
+ fprintf (stderr, " from %s:%d", ip->nominal_fname, ip->lineno);
+ }
+ if (! first)
+ fprintf (stderr, ":\n");
+
+ /* Record we have printed the status as of this time. */
+ last_error_tick = input_file_stack_tick;
+}
+
+/* Return the line at which an error occurred.
+ The error is not necessarily associated with the current spot
+ in the input stack, so LINE says where. LINE will have been
+ copied from ip->lineno for the current input level.
+ If the current level is for a file, we return LINE.
+ But if the current level is not for a file, LINE is meaningless.
+ In that case, we return the lineno of the innermost file. */
+
+static int
+line_for_error (line)
+ int line;
+{
+ int i;
+ int line1 = line;
+
+ for (i = indepth; i >= 0; ) {
+ if (instack[i].fname != 0)
+ return line1;
+ i--;
+ if (i < 0)
+ return 0;
+ line1 = instack[i].lineno;
+ }
+ abort ();
+ /*NOTREACHED*/
+ return 0;
+}
+
+/*
+ * If OBUF doesn't have NEEDED bytes after OPTR, make it bigger.
+ *
+ * As things stand, nothing is ever placed in the output buffer to be
+ * removed again except when it's KNOWN to be part of an identifier,
+ * so flushing and moving down everything left, instead of expanding,
+ * should work ok.
+ */
+
+/* You might think void was cleaner for the return type,
+ but that would get type mismatch in check_expand in strict ANSI. */
+static int
+grow_outbuf (obuf, needed)
+ register FILE_BUF *obuf;
+ register int needed;
+{
+ register U_CHAR *p;
+ int minsize;
+
+ if (obuf->length - (obuf->bufp - obuf->buf) > needed)
+ return 0;
+
+ /* Make it at least twice as big as it is now. */
+ obuf->length *= 2;
+ /* Make it have at least 150% of the free space we will need. */
+ minsize = (3 * needed) / 2 + (obuf->bufp - obuf->buf);
+ if (minsize > obuf->length)
+ obuf->length = minsize;
+
+ if ((p = (U_CHAR *) xrealloc (obuf->buf, obuf->length)) == NULL)
+ memory_full ();
+
+ obuf->bufp = p + (obuf->bufp - obuf->buf);
+ obuf->buf = p;
+
+ return 0;
+}
+
+/* Symbol table for macro names and special symbols */
+
+/*
+ * install a name in the main hash table, even if it is already there.
+ * name stops with first non alphanumeric, except leading '#'.
+ * caller must check against redefinition if that is desired.
+ * delete_macro () removes things installed by install () in fifo order.
+ * this is important because of the `defined' special symbol used
+ * in #if, and also if pushdef/popdef directives are ever implemented.
+ *
+ * If LEN is >= 0, it is the length of the name.
+ * Otherwise, compute the length by scanning the entire name.
+ *
+ * If HASH is >= 0, it is the precomputed hash code.
+ * Otherwise, compute the hash code.
+ */
+static HASHNODE *
+install (name, len, type, ivalue, value, hash)
+ U_CHAR *name;
+ int len;
+ enum node_type type;
+ int ivalue;
+ char *value;
+ int hash;
+{
+ register HASHNODE *hp;
+ register int i, bucket;
+ register U_CHAR *p, *q;
+
+ if (len < 0) {
+ p = name;
+ while (is_idchar[*p])
+ p++;
+ len = p - name;
+ }
+
+ if (hash < 0)
+ hash = hashf (name, len, HASHSIZE);
+
+ i = sizeof (HASHNODE) + len + 1;
+ hp = (HASHNODE *) xmalloc (i);
+ bucket = hash;
+ hp->bucket_hdr = &hashtab[bucket];
+ hp->next = hashtab[bucket];
+ hashtab[bucket] = hp;
+ hp->prev = NULL;
+ if (hp->next != NULL)
+ hp->next->prev = hp;
+ hp->type = type;
+ hp->length = len;
+ if (hp->type == T_CONST)
+ hp->value.ival = ivalue;
+ else
+ hp->value.cpval = value;
+ hp->name = ((U_CHAR *) hp) + sizeof (HASHNODE);
+ p = hp->name;
+ q = name;
+ for (i = 0; i < len; i++)
+ *p++ = *q++;
+ hp->name[len] = 0;
+ return hp;
+}
+
+/*
+ * find the most recent hash node for name name (ending with first
+ * non-identifier char) installed by install
+ *
+ * If LEN is >= 0, it is the length of the name.
+ * Otherwise, compute the length by scanning the entire name.
+ *
+ * If HASH is >= 0, it is the precomputed hash code.
+ * Otherwise, compute the hash code.
+ */
+HASHNODE *
+lookup (name, len, hash)
+ U_CHAR *name;
+ int len;
+ int hash;
+{
+ register U_CHAR *bp;
+ register HASHNODE *bucket;
+
+ if (len < 0) {
+ for (bp = name; is_idchar[*bp]; bp++) ;
+ len = bp - name;
+ }
+
+ if (hash < 0)
+ hash = hashf (name, len, HASHSIZE);
+
+ bucket = hashtab[hash];
+ while (bucket) {
+ if (bucket->length == len && strncmp (bucket->name, name, len) == 0)
+ return bucket;
+ bucket = bucket->next;
+ }
+ return NULL;
+}
+
+/*
+ * Delete a hash node. Some weirdness to free junk from macros.
+ * More such weirdness will have to be added if you define more hash
+ * types that need it.
+ */
+
+/* Note that the DEFINITION of a macro is removed from the hash table
+ but its storage is not freed. This would be a storage leak
+ except that it is not reasonable to keep undefining and redefining
+ large numbers of macros many times.
+ In any case, this is necessary, because a macro can be #undef'd
+ in the middle of reading the arguments to a call to it.
+ If #undef freed the DEFINITION, that would crash. */
+
+static void
+delete_macro (hp)
+ HASHNODE *hp;
+{
+
+ if (hp->prev != NULL)
+ hp->prev->next = hp->next;
+ if (hp->next != NULL)
+ hp->next->prev = hp->prev;
+
+ /* make sure that the bucket chain header that
+ the deleted guy was on points to the right thing afterwards. */
+ if (hp == *hp->bucket_hdr)
+ *hp->bucket_hdr = hp->next;
+
+#if 0
+ if (hp->type == T_MACRO) {
+ DEFINITION *d = hp->value.defn;
+ struct reflist *ap, *nextap;
+
+ for (ap = d->pattern; ap != NULL; ap = nextap) {
+ nextap = ap->next;
+ free (ap);
+ }
+ free (d);
+ }
+#endif
+ free (hp);
+}
+
+/*
+ * return hash function on name. must be compatible with the one
+ * computed a step at a time, elsewhere
+ */
+static int
+hashf (name, len, hashsize)
+ register U_CHAR *name;
+ register int len;
+ int hashsize;
+{
+ register int r = 0;
+
+ while (len--)
+ r = HASHSTEP (r, *name++);
+
+ return MAKE_POS (r) % hashsize;
+}
+
+
+/* Dump the definition of a single macro HP to OF. */
+static void
+dump_single_macro (hp, of)
+ register HASHNODE *hp;
+ FILE *of;
+{
+ register DEFINITION *defn = hp->value.defn;
+ struct reflist *ap;
+ int offset;
+ int concat;
+
+
+ /* Print the definition of the macro HP. */
+
+ fprintf (of, "#define %s", hp->name);
+
+ if (defn->nargs >= 0) {
+ int i;
+
+ fprintf (of, "(");
+ for (i = 0; i < defn->nargs; i++) {
+ dump_arg_n (defn, i, of);
+ if (i + 1 < defn->nargs)
+ fprintf (of, ", ");
+ }
+ fprintf (of, ")");
+ }
+
+ fprintf (of, " ");
+
+ offset = 0;
+ concat = 0;
+ for (ap = defn->pattern; ap != NULL; ap = ap->next) {
+ dump_defn_1 (defn->expansion, offset, ap->nchars, of);
+ if (ap->nchars != 0)
+ concat = 0;
+ offset += ap->nchars;
+ if (ap->stringify)
+ fprintf (of, " #");
+ if (ap->raw_before && !concat)
+ fprintf (of, " ## ");
+ concat = 0;
+ dump_arg_n (defn, ap->argno, of);
+ if (ap->raw_after) {
+ fprintf (of, " ## ");
+ concat = 1;
+ }
+ }
+ dump_defn_1 (defn->expansion, offset, defn->length - offset, of);
+ fprintf (of, "\n");
+}
+
+/* Dump all macro definitions as #defines to stdout. */
+
+static void
+dump_all_macros ()
+{
+ int bucket;
+
+ for (bucket = 0; bucket < HASHSIZE; bucket++) {
+ register HASHNODE *hp;
+
+ for (hp = hashtab[bucket]; hp; hp= hp->next) {
+ if (hp->type == T_MACRO)
+ dump_single_macro (hp, stdout);
+ }
+ }
+}
+
+/* Output to OF a substring of a macro definition.
+ BASE is the beginning of the definition.
+ Output characters START thru LENGTH.
+ Discard newlines outside of strings, thus
+ converting funny-space markers to ordinary spaces. */
+
+static void
+dump_defn_1 (base, start, length, of)
+ U_CHAR *base;
+ int start;
+ int length;
+ FILE *of;
+{
+ U_CHAR *p = base + start;
+ U_CHAR *limit = base + start + length;
+
+ while (p < limit) {
+ if (*p != '\n')
+ putc (*p, of);
+ else if (*p == '\"' || *p =='\'') {
+ U_CHAR *p1 = skip_quoted_string (p, limit, 0, NULL_PTR,
+ NULL_PTR, NULL_PTR);
+ fwrite (p, p1 - p, 1, of);
+ p = p1 - 1;
+ }
+ p++;
+ }
+}
+
+/* Print the name of argument number ARGNUM of macro definition DEFN
+ to OF.
+ Recall that DEFN->args.argnames contains all the arg names
+ concatenated in reverse order with comma-space in between. */
+
+static void
+dump_arg_n (defn, argnum, of)
+ DEFINITION *defn;
+ int argnum;
+ FILE *of;
+{
+ register U_CHAR *p = defn->args.argnames;
+ while (argnum + 1 < defn->nargs) {
+ p = (U_CHAR *) index (p, ' ') + 1;
+ argnum++;
+ }
+
+ while (*p && *p != ',') {
+ putc (*p, of);
+ p++;
+ }
+}
+
+/* Initialize syntactic classifications of characters. */
+
+static void
+initialize_char_syntax ()
+{
+ register int i;
+
+ /*
+ * Set up is_idchar and is_idstart tables. These should be
+ * faster than saying (is_alpha (c) || c == '_'), etc.
+ * Set up these things before calling any routines tthat
+ * refer to them.
+ */
+ for (i = 'a'; i <= 'z'; i++) {
+ is_idchar[i - 'a' + 'A'] = 1;
+ is_idchar[i] = 1;
+ is_idstart[i - 'a' + 'A'] = 1;
+ is_idstart[i] = 1;
+ }
+ for (i = '0'; i <= '9'; i++)
+ is_idchar[i] = 1;
+ is_idchar['_'] = 1;
+ is_idstart['_'] = 1;
+ is_idchar['$'] = dollars_in_ident;
+ is_idstart['$'] = dollars_in_ident;
+
+ /* horizontal space table */
+ is_hor_space[' '] = 1;
+ is_hor_space['\t'] = 1;
+ is_hor_space['\v'] = 1;
+ is_hor_space['\f'] = 1;
+ is_hor_space['\r'] = 1;
+
+ is_space[' '] = 1;
+ is_space['\t'] = 1;
+ is_space['\v'] = 1;
+ is_space['\f'] = 1;
+ is_space['\n'] = 1;
+ is_space['\r'] = 1;
+}
+
+/* Initialize the built-in macros. */
+
+static void
+initialize_builtins (inp, outp)
+ FILE_BUF *inp;
+ FILE_BUF *outp;
+{
+ install ("__LINE__", -1, T_SPECLINE, 0, 0, -1);
+ install ("__DATE__", -1, T_DATE, 0, 0, -1);
+ install ("__FILE__", -1, T_FILE, 0, 0, -1);
+ install ("__BASE_FILE__", -1, T_BASE_FILE, 0, 0, -1);
+ install ("__INCLUDE_LEVEL__", -1, T_INCLUDE_LEVEL, 0, 0, -1);
+ install ("__VERSION__", -1, T_VERSION, 0, 0, -1);
+#ifndef NO_BUILTIN_SIZE_TYPE
+ install ("__SIZE_TYPE__", -1, T_SIZE_TYPE, 0, 0, -1);
+#endif
+#ifndef NO_BUILTIN_PTRDIFF_TYPE
+ install ("__PTRDIFF_TYPE__ ", -1, T_PTRDIFF_TYPE, 0, 0, -1);
+#endif
+ install ("__WCHAR_TYPE__", -1, T_WCHAR_TYPE, 0, 0, -1);
+ install ("__USER_LABEL_PREFIX__", -1, T_USER_LABEL_PREFIX_TYPE, 0, 0, -1);
+ install ("__REGISTER_PREFIX__", -1, T_REGISTER_PREFIX_TYPE, 0, 0, -1);
+ install ("__TIME__", -1, T_TIME, 0, 0, -1);
+ if (!traditional)
+ install ("__STDC__", -1, T_CONST, STDC_VALUE, 0, -1);
+ if (objc)
+ install ("__OBJC__", -1, T_CONST, 1, 0, -1);
+/* This is supplied using a -D by the compiler driver
+ so that it is present only when truly compiling with GNU C. */
+/* install ("__GNUC__", -1, T_CONST, 2, 0, -1); */
+
+ if (debug_output)
+ {
+ char directive[2048];
+ register struct directive *dp = &directive_table[0];
+ struct tm *timebuf = timestamp ();
+
+ sprintf (directive, " __BASE_FILE__ \"%s\"\n",
+ instack[0].nominal_fname);
+ output_line_command (inp, outp, 0, same_file);
+ pass_thru_directive (directive, &directive[strlen (directive)], outp, dp);
+
+ sprintf (directive, " __VERSION__ \"%s\"\n", version_string);
+ output_line_command (inp, outp, 0, same_file);
+ pass_thru_directive (directive, &directive[strlen (directive)], outp, dp);
+
+#ifndef NO_BUILTIN_SIZE_TYPE
+ sprintf (directive, " __SIZE_TYPE__ %s\n", SIZE_TYPE);
+ output_line_command (inp, outp, 0, same_file);
+ pass_thru_directive (directive, &directive[strlen (directive)], outp, dp);
+#endif
+
+#ifndef NO_BUILTIN_PTRDIFF_TYPE
+ sprintf (directive, " __PTRDIFF_TYPE__ %s\n", PTRDIFF_TYPE);
+ output_line_command (inp, outp, 0, same_file);
+ pass_thru_directive (directive, &directive[strlen (directive)], outp, dp);
+#endif
+
+ sprintf (directive, " __WCHAR_TYPE__ %s\n", wchar_type);
+ output_line_command (inp, outp, 0, same_file);
+ pass_thru_directive (directive, &directive[strlen (directive)], outp, dp);
+
+ sprintf (directive, " __DATE__ \"%s %2d %4d\"\n",
+ monthnames[timebuf->tm_mon],
+ timebuf->tm_mday, timebuf->tm_year + 1900);
+ output_line_command (inp, outp, 0, same_file);
+ pass_thru_directive (directive, &directive[strlen (directive)], outp, dp);
+
+ sprintf (directive, " __TIME__ \"%02d:%02d:%02d\"\n",
+ timebuf->tm_hour, timebuf->tm_min, timebuf->tm_sec);
+ output_line_command (inp, outp, 0, same_file);
+ pass_thru_directive (directive, &directive[strlen (directive)], outp, dp);
+
+ if (!traditional)
+ {
+ sprintf (directive, " __STDC__ 1");
+ output_line_command (inp, outp, 0, same_file);
+ pass_thru_directive (directive, &directive[strlen (directive)],
+ outp, dp);
+ }
+ if (objc)
+ {
+ sprintf (directive, " __OBJC__ 1");
+ output_line_command (inp, outp, 0, same_file);
+ pass_thru_directive (directive, &directive[strlen (directive)],
+ outp, dp);
+ }
+ }
+}
+
+/*
+ * process a given definition string, for initialization
+ * If STR is just an identifier, define it with value 1.
+ * If STR has anything after the identifier, then it should
+ * be identifier=definition.
+ */
+
+static void
+make_definition (str, op)
+ U_CHAR *str;
+ FILE_BUF *op;
+{
+ FILE_BUF *ip;
+ struct directive *kt;
+ U_CHAR *buf, *p;
+
+ buf = str;
+ p = str;
+ if (!is_idstart[*p]) {
+ error ("malformed option `-D %s'", str);
+ return;
+ }
+ while (is_idchar[*++p])
+ ;
+ if (*p == 0) {
+ buf = (U_CHAR *) alloca (p - buf + 4);
+ strcpy ((char *)buf, str);
+ strcat ((char *)buf, " 1");
+ } else if (*p != '=') {
+ error ("malformed option `-D %s'", str);
+ return;
+ } else {
+ U_CHAR *q;
+ /* Copy the entire option so we can modify it. */
+ buf = (U_CHAR *) alloca (2 * strlen (str) + 1);
+ strncpy (buf, str, p - str);
+ /* Change the = to a space. */
+ buf[p - str] = ' ';
+ /* Scan for any backslash-newline and remove it. */
+ p++;
+ q = &buf[p - str];
+ while (*p) {
+ if (*p == '\\' && p[1] == '\n')
+ p += 2;
+ /* Change newline chars into newline-markers. */
+ else if (*p == '\n')
+ {
+ *q++ = '\n';
+ *q++ = '\n';
+ p++;
+ }
+ else
+ *q++ = *p++;
+ }
+ *q = 0;
+ }
+
+ ip = &instack[++indepth];
+ ip->nominal_fname = ip->fname = "*Initialization*";
+
+ ip->buf = ip->bufp = buf;
+ ip->length = strlen (buf);
+ ip->lineno = 1;
+ ip->macro = 0;
+ ip->free_ptr = 0;
+ ip->if_stack = if_stack;
+ ip->system_header_p = 0;
+
+ for (kt = directive_table; kt->type != T_DEFINE; kt++)
+ ;
+
+ /* Pass NULL instead of OP, since this is a "predefined" macro. */
+ do_define (buf, buf + strlen (buf), NULL, kt);
+ --indepth;
+}
+
+/* JF, this does the work for the -U option */
+
+static void
+make_undef (str, op)
+ U_CHAR *str;
+ FILE_BUF *op;
+{
+ FILE_BUF *ip;
+ struct directive *kt;
+
+ ip = &instack[++indepth];
+ ip->nominal_fname = ip->fname = "*undef*";
+
+ ip->buf = ip->bufp = str;
+ ip->length = strlen (str);
+ ip->lineno = 1;
+ ip->macro = 0;
+ ip->free_ptr = 0;
+ ip->if_stack = if_stack;
+ ip->system_header_p = 0;
+
+ for (kt = directive_table; kt->type != T_UNDEF; kt++)
+ ;
+
+ do_undef (str, str + strlen (str), op, kt);
+ --indepth;
+}
+
+/* Process the string STR as if it appeared as the body of a #assert.
+ OPTION is the option name for which STR was the argument. */
+
+static void
+make_assertion (option, str)
+ char *option;
+ U_CHAR *str;
+{
+ FILE_BUF *ip;
+ struct directive *kt;
+ U_CHAR *buf, *p, *q;
+
+ /* Copy the entire option so we can modify it. */
+ buf = (U_CHAR *) alloca (strlen (str) + 1);
+ strcpy ((char *) buf, str);
+ /* Scan for any backslash-newline and remove it. */
+ p = q = buf;
+ while (*p) {
+ if (*p == '\\' && p[1] == '\n')
+ p += 2;
+ else
+ *q++ = *p++;
+ }
+ *q = 0;
+
+ p = buf;
+ if (!is_idstart[*p]) {
+ error ("malformed option `%s %s'", option, str);
+ return;
+ }
+ while (is_idchar[*++p])
+ ;
+ while (*p == ' ' || *p == '\t') p++;
+ if (! (*p == 0 || *p == '(')) {
+ error ("malformed option `%s %s'", option, str);
+ return;
+ }
+
+ ip = &instack[++indepth];
+ ip->nominal_fname = ip->fname = "*Initialization*";
+
+ ip->buf = ip->bufp = buf;
+ ip->length = strlen (buf);
+ ip->lineno = 1;
+ ip->macro = 0;
+ ip->free_ptr = 0;
+ ip->if_stack = if_stack;
+ ip->system_header_p = 0;
+
+ for (kt = directive_table; kt->type != T_ASSERT; kt++)
+ ;
+
+ /* pass NULL as output ptr to do_define since we KNOW it never
+ does any output.... */
+ do_assert (buf, buf + strlen (buf) , NULL_PTR, kt);
+ --indepth;
+}
+
+/* Append a chain of `struct file_name_list's
+ to the end of the main include chain.
+ FIRST is the beginning of the chain to append, and LAST is the end. */
+
+static void
+append_include_chain (first, last)
+ struct file_name_list *first, *last;
+{
+ struct file_name_list *dir;
+
+ if (!first || !last)
+ return;
+
+ if (include == 0)
+ include = first;
+ else
+ last_include->next = first;
+
+ if (first_bracket_include == 0)
+ first_bracket_include = first;
+
+ for (dir = first; ; dir = dir->next) {
+ int len = strlen (dir->fname) + INCLUDE_LEN_FUDGE;
+ if (len > max_include_len)
+ max_include_len = len;
+ if (dir == last)
+ break;
+ }
+
+ last->next = NULL;
+ last_include = last;
+}
+
+/* Add output to `deps_buffer' for the -M switch.
+ STRING points to the text to be output.
+ SPACER is ':' for targets, ' ' for dependencies, zero for text
+ to be inserted literally. */
+
+static void
+deps_output (string, spacer)
+ char *string;
+ int spacer;
+{
+ int size = strlen (string);
+
+ if (size == 0)
+ return;
+
+#ifndef MAX_OUTPUT_COLUMNS
+#define MAX_OUTPUT_COLUMNS 72
+#endif
+ if (spacer
+ && deps_column > 0
+ && (deps_column + size) > MAX_OUTPUT_COLUMNS)
+ {
+ deps_output (" \\\n ", 0);
+ deps_column = 0;
+ }
+
+ if (deps_size + size + 8 > deps_allocated_size) {
+ deps_allocated_size = (deps_size + size + 50) * 2;
+ deps_buffer = (char *) xrealloc (deps_buffer, deps_allocated_size);
+ }
+ if (spacer == ' ' && deps_column > 0)
+ deps_buffer[deps_size++] = ' ';
+ bcopy (string, &deps_buffer[deps_size], size);
+ deps_size += size;
+ deps_column += size;
+ if (spacer == ':')
+ deps_buffer[deps_size++] = ':';
+ deps_buffer[deps_size] = 0;
+}
+
+#if defined(USG) || defined(VMS)
+#ifndef BSTRING
+
+void
+bzero (b, length)
+ register char *b;
+ register unsigned length;
+{
+ while (length-- > 0)
+ *b++ = 0;
+}
+
+void
+bcopy (b1, b2, length)
+ register char *b1;
+ register char *b2;
+ register unsigned length;
+{
+ while (length-- > 0)
+ *b2++ = *b1++;
+}
+
+int
+bcmp (b1, b2, length) /* This could be a macro! */
+ register char *b1;
+ register char *b2;
+ register unsigned length;
+{
+ while (length-- > 0)
+ if (*b1++ != *b2++)
+ return 1;
+
+ return 0;
+}
+#endif /* not BSTRING */
+#endif /* USG or VMS */
+
+
+static void
+fatal (str, arg)
+ char *str, *arg;
+{
+ fprintf (stderr, "%s: ", progname);
+ fprintf (stderr, str, arg);
+ fprintf (stderr, "\n");
+ exit (FAILURE_EXIT_CODE);
+}
+
+/* More 'friendly' abort that prints the line and file.
+ config.h can #define abort fancy_abort if you like that sort of thing. */
+
+void
+fancy_abort ()
+{
+ fatal ("Internal gcc abort.");
+}
+
+static void
+perror_with_name (name)
+ char *name;
+{
+ fprintf (stderr, "%s: ", progname);
+ if (errno < sys_nerr)
+ fprintf (stderr, "%s: %s\n", name, sys_errlist[errno]);
+ else
+ fprintf (stderr, "%s: undocumented I/O error\n", name);
+ errors++;
+}
+
+static void
+pfatal_with_name (name)
+ char *name;
+{
+ perror_with_name (name);
+#ifdef VMS
+ exit (vaxc$errno);
+#else
+ exit (FAILURE_EXIT_CODE);
+#endif
+}
+
+/* Handler for SIGPIPE. */
+
+static void
+pipe_closed (signo)
+ /* If this is missing, some compilers complain. */
+ int signo;
+{
+ fatal ("output pipe has been closed");
+}
+
+static void
+memory_full ()
+{
+ fatal ("Memory exhausted.");
+}
+
+
+char *
+xmalloc (size)
+ unsigned size;
+{
+ register char *ptr = (char *) malloc (size);
+ if (ptr != 0) return (ptr);
+ memory_full ();
+ /*NOTREACHED*/
+ return 0;
+}
+
+static char *
+xrealloc (old, size)
+ char *old;
+ unsigned size;
+{
+ register char *ptr = (char *) realloc (old, size);
+ if (ptr != 0) return (ptr);
+ memory_full ();
+ /*NOTREACHED*/
+ return 0;
+}
+
+static char *
+xcalloc (number, size)
+ unsigned number, size;
+{
+ register unsigned total = number * size;
+ register char *ptr = (char *) malloc (total);
+ if (ptr != 0) {
+ if (total > 100)
+ bzero (ptr, total);
+ else {
+ /* It's not too long, so loop, zeroing by longs.
+ It must be safe because malloc values are always well aligned. */
+ register long *zp = (long *) ptr;
+ register long *zl = (long *) (ptr + total - 4);
+ register int i = total - 4;
+ while (zp < zl)
+ *zp++ = 0;
+ if (i < 0)
+ i = 0;
+ while (i < total)
+ ptr[i++] = 0;
+ }
+ return ptr;
+ }
+ memory_full ();
+ /*NOTREACHED*/
+ return 0;
+}
+
+static char *
+savestring (input)
+ char *input;
+{
+ unsigned size = strlen (input);
+ char *output = xmalloc (size + 1);
+ strcpy (output, input);
+ return output;
+}
+
+/* Get the file-mode and data size of the file open on FD
+ and store them in *MODE_POINTER and *SIZE_POINTER. */
+
+static int
+file_size_and_mode (fd, mode_pointer, size_pointer)
+ int fd;
+ int *mode_pointer;
+ long int *size_pointer;
+{
+ struct stat sbuf;
+
+ if (fstat (fd, &sbuf) < 0) return (-1);
+ if (mode_pointer) *mode_pointer = sbuf.st_mode;
+ if (size_pointer) *size_pointer = sbuf.st_size;
+ return 0;
+}
+
+static void
+output_dots (fd, depth)
+ FILE* fd;
+ int depth;
+{
+ while (depth > 0) {
+ putc ('.', fd);
+ depth--;
+ }
+}
+
+
+#ifdef VMS
+
+/* Under VMS we need to fix up the "include" specification
+ filename so that everything following the 1st slash is
+ changed into its correct VMS file specification. */
+
+static void
+hack_vms_include_specification (fname)
+ char *fname;
+{
+ register char *cp, *cp1, *cp2;
+ int f, check_filename_before_returning, no_prefix_seen;
+ char Local[512];
+
+ check_filename_before_returning = 0;
+ no_prefix_seen = 0;
+
+ /* Ignore leading "./"s */
+ while (fname[0] == '.' && fname[1] == '/') {
+ strcpy (fname, fname+2);
+ no_prefix_seen = 1; /* mark this for later */
+ }
+ /* Look for the boundary between the VMS and UNIX filespecs */
+ cp = rindex (fname, ']'); /* Look for end of dirspec. */
+ if (cp == 0) cp = rindex (fname, '>'); /* ... Ditto */
+ if (cp == 0) cp = rindex (fname, ':'); /* Look for end of devspec. */
+ if (cp) {
+ cp++;
+ } else {
+ cp = index (fname, '/'); /* Look for the "/" */
+ }
+
+ /*
+ * Check if we have a vax-c style '#include filename'
+ * and add the missing .h
+ */
+ if (cp == 0) {
+ if (index(fname,'.') == 0)
+ strcat(fname, ".h");
+ } else {
+ if (index(cp,'.') == 0)
+ strcat(cp, ".h");
+ }
+
+ cp2 = Local; /* initialize */
+
+ /* We are trying to do a number of things here. First of all, we are
+ trying to hammer the filenames into a standard format, such that later
+ processing can handle them.
+
+ If the file name contains something like [dir.], then it recognizes this
+ as a root, and strips the ".]". Later processing will add whatever is
+ needed to get things working properly.
+
+ If no device is specified, then the first directory name is taken to be
+ a device name (or a rooted logical). */
+
+ /* See if we found that 1st slash */
+ if (cp == 0) return; /* Nothing to do!!! */
+ if (*cp != '/') return; /* Nothing to do!!! */
+ /* Point to the UNIX filename part (which needs to be fixed!) */
+ cp1 = cp+1;
+ /* If the directory spec is not rooted, we can just copy
+ the UNIX filename part and we are done */
+ if (((cp - fname) > 1) && ((cp[-1] == ']') || (cp[-1] == '>'))) {
+ if (cp[-2] != '.') {
+ /*
+ * The VMS part ends in a `]', and the preceding character is not a `.'.
+ * We strip the `]', and then splice the two parts of the name in the
+ * usual way. Given the default locations for include files in cccp.c,
+ * we will only use this code if the user specifies alternate locations
+ * with the /include (-I) switch on the command line. */
+ cp -= 1; /* Strip "]" */
+ cp1--; /* backspace */
+ } else {
+ /*
+ * The VMS part has a ".]" at the end, and this will not do. Later
+ * processing will add a second directory spec, and this would be a syntax
+ * error. Thus we strip the ".]", and thus merge the directory specs.
+ * We also backspace cp1, so that it points to a '/'. This inhibits the
+ * generation of the 000000 root directory spec (which does not belong here
+ * in this case).
+ */
+ cp -= 2; /* Strip ".]" */
+ cp1--; }; /* backspace */
+ } else {
+
+ /* We drop in here if there is no VMS style directory specification yet.
+ * If there is no device specification either, we make the first dir a
+ * device and try that. If we do not do this, then we will be essentially
+ * searching the users default directory (as if they did a #include "asdf.h").
+ *
+ * Then all we need to do is to push a '[' into the output string. Later
+ * processing will fill this in, and close the bracket.
+ */
+ if (cp[-1] != ':') *cp2++ = ':'; /* dev not in spec. take first dir */
+ *cp2++ = '['; /* Open the directory specification */
+ }
+
+ /* at this point we assume that we have the device spec, and (at least
+ the opening "[" for a directory specification. We may have directories
+ specified already */
+
+ /* If there are no other slashes then the filename will be
+ in the "root" directory. Otherwise, we need to add
+ directory specifications. */
+ if (index (cp1, '/') == 0) {
+ /* Just add "000000]" as the directory string */
+ strcpy (cp2, "000000]");
+ cp2 += strlen (cp2);
+ check_filename_before_returning = 1; /* we might need to fool with this later */
+ } else {
+ /* As long as there are still subdirectories to add, do them. */
+ while (index (cp1, '/') != 0) {
+ /* If this token is "." we can ignore it */
+ if ((cp1[0] == '.') && (cp1[1] == '/')) {
+ cp1 += 2;
+ continue;
+ }
+ /* Add a subdirectory spec. Do not duplicate "." */
+ if (cp2[-1] != '.' && cp2[-1] != '[' && cp2[-1] != '<')
+ *cp2++ = '.';
+ /* If this is ".." then the spec becomes "-" */
+ if ((cp1[0] == '.') && (cp1[1] == '.') && (cp[2] == '/')) {
+ /* Add "-" and skip the ".." */
+ *cp2++ = '-';
+ cp1 += 3;
+ continue;
+ }
+ /* Copy the subdirectory */
+ while (*cp1 != '/') *cp2++= *cp1++;
+ cp1++; /* Skip the "/" */
+ }
+ /* Close the directory specification */
+ if (cp2[-1] == '.') /* no trailing periods */
+ cp2--;
+ *cp2++ = ']';
+ }
+ /* Now add the filename */
+ while (*cp1) *cp2++ = *cp1++;
+ *cp2 = 0;
+ /* Now append it to the original VMS spec. */
+ strcpy (cp, Local);
+
+ /* If we put a [000000] in the filename, try to open it first. If this fails,
+ remove the [000000], and return that name. This provides flexibility
+ to the user in that they can use both rooted and non-rooted logical names
+ to point to the location of the file. */
+
+ if (check_filename_before_returning && no_prefix_seen) {
+ f = open (fname, O_RDONLY, 0666);
+ if (f >= 0) {
+ /* The file name is OK as it is, so return it as is. */
+ close (f);
+ return;
+ }
+ /* The filename did not work. Try to remove the [000000] from the name,
+ and return it. */
+ cp = index (fname, '[');
+ cp2 = index (fname, ']') + 1;
+ strcpy (cp, cp2); /* this gets rid of it */
+ }
+ return;
+}
+#endif /* VMS */
+
+#ifdef VMS
+
+/* These are the read/write replacement routines for
+ VAX-11 "C". They make read/write behave enough
+ like their UNIX counterparts that CCCP will work */
+
+static int
+read (fd, buf, size)
+ int fd;
+ char *buf;
+ int size;
+{
+#undef read /* Get back the REAL read routine */
+ register int i;
+ register int total = 0;
+
+ /* Read until the buffer is exhausted */
+ while (size > 0) {
+ /* Limit each read to 32KB */
+ i = (size > (32*1024)) ? (32*1024) : size;
+ i = read (fd, buf, i);
+ if (i <= 0) {
+ if (i == 0) return (total);
+ return (i);
+ }
+ /* Account for this read */
+ total += i;
+ buf += i;
+ size -= i;
+ }
+ return (total);
+}
+
+static int
+write (fd, buf, size)
+ int fd;
+ char *buf;
+ int size;
+{
+#undef write /* Get back the REAL write routine */
+ int i;
+ int j;
+
+ /* Limit individual writes to 32Kb */
+ i = size;
+ while (i > 0) {
+ j = (i > (32*1024)) ? (32*1024) : i;
+ if (write (fd, buf, j) < 0) return (-1);
+ /* Account for the data written */
+ buf += j;
+ i -= j;
+ }
+ return (size);
+}
+
+/* The following wrapper functions supply additional arguments to the VMS
+ I/O routines to optimize performance with file handling. The arguments
+ are:
+ "mbc=16" - Set multi-block count to 16 (use a 8192 byte buffer).
+ "deq=64" - When extending the file, extend it in chunks of 32Kbytes.
+ "fop=tef"- Truncate unused portions of file when closing file.
+ "shr=nil"- Disallow file sharing while file is open.
+ */
+
+static FILE *
+freopen (fname, type, oldfile)
+ char *fname;
+ char *type;
+ FILE *oldfile;
+{
+#undef freopen /* Get back the REAL fopen routine */
+ if (strcmp (type, "w") == 0)
+ return freopen (fname, type, oldfile, "mbc=16", "deq=64", "fop=tef", "shr=nil");
+ return freopen (fname, type, oldfile, "mbc=16");
+}
+
+static FILE *
+fopen (fname, type)
+ char *fname;
+ char *type;
+{
+#undef fopen /* Get back the REAL fopen routine */
+ if (strcmp (type, "w") == 0)
+ return fopen (fname, type, "mbc=16", "deq=64", "fop=tef", "shr=nil");
+ return fopen (fname, type, "mbc=16");
+}
+
+static int
+open (fname, flags, prot)
+ char *fname;
+ int flags;
+ int prot;
+{
+#undef open /* Get back the REAL open routine */
+ return open (fname, flags, prot, "mbc=16", "deq=64", "fop=tef");
+}
+
+/* Avoid run-time library bug, where copying M out of N+M characters with
+ N >= 65535 results in VAXCRTL's strncat falling into an infinite loop.
+ gcc-cpp exercises this particular bug. */
+
+static char *
+strncat (dst, src, cnt)
+ char *dst;
+ const char *src;
+ unsigned cnt;
+{
+ register char *d = dst, *s = (char *) src;
+ register int n = cnt; /* convert to _signed_ type */
+
+ while (*d) d++; /* advance to end */
+ while (--n >= 0)
+ if (!(*d++ = *s++)) break;
+ if (n < 0) *d = '\0';
+ return dst;
+}
+#endif /* VMS */
diff --git a/gnu/usr.bin/cc/cpp/cexp.c b/gnu/usr.bin/cc/cpp/cexp.c
new file mode 100644
index 0000000..d1471aa
--- /dev/null
+++ b/gnu/usr.bin/cc/cpp/cexp.c
@@ -0,0 +1,1926 @@
+
+/* A Bison parser, made from cexp.y with Bison version GNU Bison version 1.22
+ */
+
+#define YYBISON 1 /* Identify Bison output. */
+
+#define INT 258
+#define CHAR 259
+#define NAME 260
+#define ERROR 261
+#define OR 262
+#define AND 263
+#define EQUAL 264
+#define NOTEQUAL 265
+#define LEQ 266
+#define GEQ 267
+#define LSH 268
+#define RSH 269
+#define UNARY 270
+
+#line 26 "cexp.y"
+
+#include "config.h"
+#include <setjmp.h>
+/* #define YYDEBUG 1 */
+
+#ifdef MULTIBYTE_CHARS
+#include <stdlib.h>
+#include <locale.h>
+#endif
+
+#include <stdio.h>
+
+typedef unsigned char U_CHAR;
+
+/* This is used for communicating lists of keywords with cccp.c. */
+struct arglist {
+ struct arglist *next;
+ U_CHAR *name;
+ int length;
+ int argno;
+};
+
+/* Define a generic NULL if one hasn't already been defined. */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef GENERIC_PTR
+#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
+#define GENERIC_PTR void *
+#else
+#define GENERIC_PTR char *
+#endif
+#endif
+
+#ifndef NULL_PTR
+#define NULL_PTR ((GENERIC_PTR)0)
+#endif
+
+int yylex ();
+void yyerror ();
+int expression_value;
+
+static jmp_buf parse_return_error;
+
+/* Nonzero means count most punctuation as part of a name. */
+static int keyword_parsing = 0;
+
+/* some external tables of character types */
+extern unsigned char is_idstart[], is_idchar[], is_hor_space[];
+
+extern char *xmalloc ();
+
+/* Flag for -pedantic. */
+extern int pedantic;
+
+/* Flag for -traditional. */
+extern int traditional;
+
+#ifndef CHAR_TYPE_SIZE
+#define CHAR_TYPE_SIZE BITS_PER_UNIT
+#endif
+
+#ifndef INT_TYPE_SIZE
+#define INT_TYPE_SIZE BITS_PER_WORD
+#endif
+
+#ifndef LONG_TYPE_SIZE
+#define LONG_TYPE_SIZE BITS_PER_WORD
+#endif
+
+#ifndef WCHAR_TYPE_SIZE
+#define WCHAR_TYPE_SIZE INT_TYPE_SIZE
+#endif
+
+#ifndef MAX_CHAR_TYPE_SIZE
+#define MAX_CHAR_TYPE_SIZE CHAR_TYPE_SIZE
+#endif
+
+#ifndef MAX_INT_TYPE_SIZE
+#define MAX_INT_TYPE_SIZE INT_TYPE_SIZE
+#endif
+
+#ifndef MAX_LONG_TYPE_SIZE
+#define MAX_LONG_TYPE_SIZE LONG_TYPE_SIZE
+#endif
+
+#ifndef MAX_WCHAR_TYPE_SIZE
+#define MAX_WCHAR_TYPE_SIZE WCHAR_TYPE_SIZE
+#endif
+
+/* Yield nonzero if adding two numbers with A's and B's signs can yield a
+ number with SUM's sign, where A, B, and SUM are all C integers. */
+#define possible_sum_sign(a, b, sum) ((((a) ^ (b)) | ~ ((a) ^ (sum))) < 0)
+
+static void integer_overflow ();
+static long left_shift ();
+static long right_shift ();
+
+#line 127 "cexp.y"
+typedef union {
+ struct constant {long value; int unsignedp;} integer;
+ struct name {U_CHAR *address; int length;} name;
+ struct arglist *keywords;
+ int voidval;
+ char *sval;
+} YYSTYPE;
+
+#ifndef YYLTYPE
+typedef
+ struct yyltype
+ {
+ int timestamp;
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+ char *text;
+ }
+ yyltype;
+
+#define YYLTYPE yyltype
+#endif
+
+#include <stdio.h>
+
+#ifndef __cplusplus
+#ifndef __STDC__
+#define const
+#endif
+#endif
+
+
+
+#define YYFINAL 73
+#define YYFLAG -32768
+#define YYNTBASE 34
+
+#define YYTRANSLATE(x) ((unsigned)(x) <= 270 ? yytranslate[x] : 39)
+
+static const char yytranslate[] = { 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 29, 2, 31, 2, 27, 14, 2, 32,
+ 33, 25, 23, 9, 24, 2, 26, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 8, 2, 17,
+ 2, 18, 7, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 13, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 12, 2, 30, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 1, 2, 3, 4, 5,
+ 6, 10, 11, 15, 16, 19, 20, 21, 22, 28
+};
+
+#if YYDEBUG != 0
+static const short yyprhs[] = { 0,
+ 0, 2, 4, 8, 11, 14, 17, 20, 23, 24,
+ 31, 35, 39, 43, 47, 51, 55, 59, 63, 67,
+ 71, 75, 79, 83, 87, 91, 95, 99, 103, 107,
+ 113, 115, 117, 119, 120, 125
+};
+
+static const short yyrhs[] = { 35,
+ 0, 36, 0, 35, 9, 36, 0, 24, 36, 0,
+ 29, 36, 0, 23, 36, 0, 30, 36, 0, 31,
+ 5, 0, 0, 31, 5, 37, 32, 38, 33, 0,
+ 32, 35, 33, 0, 36, 25, 36, 0, 36, 26,
+ 36, 0, 36, 27, 36, 0, 36, 23, 36, 0,
+ 36, 24, 36, 0, 36, 21, 36, 0, 36, 22,
+ 36, 0, 36, 15, 36, 0, 36, 16, 36, 0,
+ 36, 19, 36, 0, 36, 20, 36, 0, 36, 17,
+ 36, 0, 36, 18, 36, 0, 36, 14, 36, 0,
+ 36, 13, 36, 0, 36, 12, 36, 0, 36, 11,
+ 36, 0, 36, 10, 36, 0, 36, 7, 36, 8,
+ 36, 0, 3, 0, 4, 0, 5, 0, 0, 32,
+ 38, 33, 38, 0, 5, 38, 0
+};
+
+#endif
+
+#if YYDEBUG != 0
+static const short yyrline[] = { 0,
+ 159, 164, 165, 172, 177, 180, 182, 185, 189, 191,
+ 196, 201, 213, 228, 239, 246, 253, 259, 265, 268,
+ 271, 277, 283, 289, 295, 298, 301, 304, 307, 310,
+ 313, 315, 317, 322, 324, 337
+};
+
+static const char * const yytname[] = { "$","error","$illegal.","INT","CHAR",
+"NAME","ERROR","'?'","':'","','","OR","AND","'|'","'^'","'&'","EQUAL","NOTEQUAL",
+"'<'","'>'","LEQ","GEQ","LSH","RSH","'+'","'-'","'*'","'/'","'%'","UNARY","'!'",
+"'~'","'#'","'('","')'","start","exp1","exp","@1","keywords",""
+};
+#endif
+
+static const short yyr1[] = { 0,
+ 34, 35, 35, 36, 36, 36, 36, 36, 37, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 38, 38, 38
+};
+
+static const short yyr2[] = { 0,
+ 1, 1, 3, 2, 2, 2, 2, 2, 0, 6,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 5,
+ 1, 1, 1, 0, 4, 2
+};
+
+static const short yydefact[] = { 0,
+ 31, 32, 33, 0, 0, 0, 0, 0, 0, 1,
+ 2, 6, 4, 5, 7, 8, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 11, 3,
+ 0, 29, 28, 27, 26, 25, 19, 20, 23, 24,
+ 21, 22, 17, 18, 15, 16, 12, 13, 14, 34,
+ 0, 34, 34, 0, 30, 36, 0, 10, 34, 35,
+ 0, 0, 0
+};
+
+static const short yydefgoto[] = { 71,
+ 10, 11, 38, 64
+};
+
+static const short yypact[] = { 31,
+-32768,-32768,-32768, 31, 31, 31, 31, 4, 31, 3,
+ 80,-32768,-32768,-32768,-32768, 6, 32, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 7,-32768, 80,
+ 59, 97, 113, 128, 142, 155, 25, 25, 162, 162,
+ 162, 162, 167, 167, -19, -19,-32768,-32768,-32768, 5,
+ 31, 5, 5, -20, 80,-32768, 20,-32768, 5,-32768,
+ 40, 56,-32768
+};
+
+static const short yypgoto[] = {-32768,
+ 49, -4,-32768, -58
+};
+
+
+#define YYLAST 194
+
+
+static const short yytable[] = { 12,
+ 13, 14, 15, 66, 67, 35, 36, 37, 16, 62,
+ 70, 18, 68, 40, 41, 42, 43, 44, 45, 46,
+ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ 57, 58, 59, 1, 2, 3, 63, -9, 60, 72,
+ 18, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 69, 4, 5, 73, 65, 17, 0, 6,
+ 7, 8, 9, 0, 39, 19, 61, 0, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 19, 0, 0, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32, 33, 34, 35, 36, 37, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 24, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 31, 32, 33, 34, 35, 36, 37, 33,
+ 34, 35, 36, 37
+};
+
+static const short yycheck[] = { 4,
+ 5, 6, 7, 62, 63, 25, 26, 27, 5, 5,
+ 69, 9, 33, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 3, 4, 5, 32, 32, 32, 0,
+ 9, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 33, 23, 24, 0, 61, 9, -1, 29,
+ 30, 31, 32, -1, 33, 7, 8, -1, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 7, -1, -1, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 26, 27, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 21, 22, 23, 24, 25, 26, 27, 23,
+ 24, 25, 26, 27
+};
+/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
+#line 3 "/usr/local/lib/bison.simple"
+
+/* Skeleton output parser for bison,
+ Copyright (C) 1984, 1989, 1990 Bob Corbett and Richard Stallman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#ifndef alloca
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not GNU C. */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi)
+#include <alloca.h>
+#else /* not sparc */
+#if defined (MSDOS) && !defined (__TURBOC__)
+#include <malloc.h>
+#else /* not MSDOS, or __TURBOC__ */
+#if defined(_AIX)
+#include <malloc.h>
+ #pragma alloca
+#else /* not MSDOS, __TURBOC__, or _AIX */
+#ifdef __hpux
+#ifdef __cplusplus
+extern "C" {
+void *alloca (unsigned int);
+};
+#else /* not __cplusplus */
+void *alloca ();
+#endif /* not __cplusplus */
+#endif /* __hpux */
+#endif /* not _AIX */
+#endif /* not MSDOS, or __TURBOC__ */
+#endif /* not sparc. */
+#endif /* not GNU C. */
+#endif /* alloca not defined. */
+
+/* This is the parser code that is written into each bison parser
+ when the %semantic_parser declaration is not specified in the grammar.
+ It was written by Richard Stallman by simplifying the hairy parser
+ used when %semantic_parser is specified. */
+
+/* Note: there must be only one dollar sign in this file.
+ It is replaced by the list of actions, each action
+ as one case of the switch. */
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY -2
+#define YYEOF 0
+#define YYACCEPT return(0)
+#define YYABORT return(1)
+#define YYERROR goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+ This remains here temporarily to ease the
+ transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+#define YYFAIL goto yyerrlab
+#define YYRECOVERING() (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { yychar = (token), yylval = (value); \
+ yychar1 = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { yyerror ("syntax error: cannot back up"); YYERROR; } \
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+#ifndef YYPURE
+#define YYLEX yylex()
+#endif
+
+#ifdef YYPURE
+#ifdef YYLSP_NEEDED
+#define YYLEX yylex(&yylval, &yylloc)
+#else
+#define YYLEX yylex(&yylval)
+#endif
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYPURE
+
+int yychar; /* the lookahead symbol */
+YYSTYPE yylval; /* the semantic value of the */
+ /* lookahead symbol */
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc; /* location data for the lookahead */
+ /* symbol */
+#endif
+
+int yynerrs; /* number of parse errors so far */
+#endif /* not YYPURE */
+
+#if YYDEBUG != 0
+int yydebug; /* nonzero means print parse trace */
+/* Since this is uninitialized, it does not stop multiple parsers
+ from coexisting. */
+#endif
+
+/* YYINITDEPTH indicates the initial size of the parser's stacks */
+
+#ifndef YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH is the maximum size the stacks can grow to
+ (effective only if the built-in stack extension method is used). */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+
+/* Prevent warning if -Wstrict-prototypes. */
+#ifdef __GNUC__
+int yyparse (void);
+#endif
+
+#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */
+#define __yy_bcopy(FROM,TO,COUNT) __builtin_memcpy(TO,FROM,COUNT)
+#else /* not GNU C or C++ */
+#ifndef __cplusplus
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_bcopy (from, to, count)
+ char *from;
+ char *to;
+ int count;
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#else /* __cplusplus */
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_bcopy (char *from, char *to, int count)
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#endif
+#endif
+
+#line 184 "/usr/local/lib/bison.simple"
+
+/* The user can define YYPARSE_PARAM as the name of an argument to be passed
+ into yyparse. The argument should have type void *.
+ It should actually point to an object.
+ Grammar actions can access the variable by casting it
+ to the proper pointer type. */
+
+#ifdef YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
+#else
+#define YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL
+#endif
+
+int
+yyparse(YYPARSE_PARAM)
+ YYPARSE_PARAM_DECL
+{
+ register int yystate;
+ register int yyn;
+ register short *yyssp;
+ register YYSTYPE *yyvsp;
+ int yyerrstatus; /* number of tokens to shift before error messages enabled */
+ int yychar1 = 0; /* lookahead token as an internal (translated) token number */
+
+ short yyssa[YYINITDEPTH]; /* the state stack */
+ YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
+
+ short *yyss = yyssa; /* refer to the stacks thru separate pointers */
+ YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */
+ YYLTYPE *yyls = yylsa;
+ YYLTYPE *yylsp;
+
+#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--)
+#else
+#define YYPOPSTACK (yyvsp--, yyssp--)
+#endif
+
+ int yystacksize = YYINITDEPTH;
+
+#ifdef YYPURE
+ int yychar;
+ YYSTYPE yylval;
+ int yynerrs;
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylloc;
+#endif
+#endif
+
+ YYSTYPE yyval; /* the variable used to return */
+ /* semantic values from the action */
+ /* routines */
+
+ int yylen;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Starting parse\n");
+#endif
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss - 1;
+ yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in yystate . */
+/* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks. */
+yynewstate:
+
+ *++yyssp = yystate;
+
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ /* Give user a chance to reallocate the stack */
+ /* Use copies of these so that the &'s don't force the real ones into memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+ YYLTYPE *yyls1 = yyls;
+#endif
+
+ /* Get the current used size of the three stacks, in elements. */
+ int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ /* Each stack pointer address is followed by the size of
+ the data in use in that stack, in bytes. */
+#ifdef YYLSP_NEEDED
+ /* This used to be a conditional around just the two extra args,
+ but that might be undefined if yyoverflow is a macro. */
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yyls1, size * sizeof (*yylsp),
+ &yystacksize);
+#else
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yystacksize);
+#endif
+
+ yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+ yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+ /* Extend the stack our own way. */
+ if (yystacksize >= YYMAXDEPTH)
+ {
+ yyerror("parser stack overflow");
+ return 2;
+ }
+ yystacksize *= 2;
+ if (yystacksize > YYMAXDEPTH)
+ yystacksize = YYMAXDEPTH;
+ yyss = (short *) alloca (yystacksize * sizeof (*yyssp));
+ __yy_bcopy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
+ yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp));
+ __yy_bcopy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+ yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp));
+ __yy_bcopy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + size - 1;
+ yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+ if (yyssp >= yyss + yystacksize - 1)
+ YYABORT;
+ }
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+ goto yybackup;
+ yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a lookahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* yychar is either YYEMPTY or YYEOF
+ or a valid token in external form. */
+
+ if (yychar == YYEMPTY)
+ {
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Reading a token: ");
+#endif
+ yychar = YYLEX;
+ }
+
+ /* Convert token to internal form (in yychar1) for indexing tables with */
+
+ if (yychar <= 0) /* This means end of input. */
+ {
+ yychar1 = 0;
+ yychar = YYEOF; /* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Now at end of input.\n");
+#endif
+ }
+ else
+ {
+ yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
+ /* Give the individual parser a way to print the precise meaning
+ of a token, for further debugging info. */
+#ifdef YYPRINT
+ YYPRINT (stderr, yychar, yylval);
+#endif
+ fprintf (stderr, ")\n");
+ }
+#endif
+ }
+
+ yyn += yychar1;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+ goto yydefault;
+
+ yyn = yytable[yyn];
+
+ /* yyn is what to do for this token type in this state.
+ Negative => reduce, -yyn is rule number.
+ Positive => shift, yyn is new state.
+ New state is final state => don't bother to shift,
+ just return success.
+ 0, or most negative number => error. */
+
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrlab;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the lookahead token. */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
+#endif
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ /* count tokens shifted since error; after three, turn off error status. */
+ if (yyerrstatus) yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+/* Do the default action for the current state. */
+yydefault:
+
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+
+/* Do a reduction. yyn is the number of a rule to reduce with. */
+yyreduce:
+ yylen = yyr2[yyn];
+ if (yylen > 0)
+ yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ int i;
+
+ fprintf (stderr, "Reducing via rule %d (line %d), ",
+ yyn, yyrline[yyn]);
+
+ /* Print the symbols being reduced, and their result. */
+ for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+ fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+ fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+ }
+#endif
+
+
+ switch (yyn) {
+
+case 1:
+#line 160 "cexp.y"
+{ expression_value = yyvsp[0].integer.value; ;
+ break;}
+case 3:
+#line 166 "cexp.y"
+{ if (pedantic)
+ pedwarn ("comma operator in operand of `#if'");
+ yyval.integer = yyvsp[0].integer; ;
+ break;}
+case 4:
+#line 173 "cexp.y"
+{ yyval.integer.value = - yyvsp[0].integer.value;
+ if ((yyval.integer.value & yyvsp[0].integer.value) < 0 && ! yyvsp[0].integer.unsignedp)
+ integer_overflow ();
+ yyval.integer.unsignedp = yyvsp[0].integer.unsignedp; ;
+ break;}
+case 5:
+#line 178 "cexp.y"
+{ yyval.integer.value = ! yyvsp[0].integer.value;
+ yyval.integer.unsignedp = 0; ;
+ break;}
+case 6:
+#line 181 "cexp.y"
+{ yyval.integer = yyvsp[0].integer; ;
+ break;}
+case 7:
+#line 183 "cexp.y"
+{ yyval.integer.value = ~ yyvsp[0].integer.value;
+ yyval.integer.unsignedp = yyvsp[0].integer.unsignedp; ;
+ break;}
+case 8:
+#line 186 "cexp.y"
+{ yyval.integer.value = check_assertion (yyvsp[0].name.address, yyvsp[0].name.length,
+ 0, NULL_PTR);
+ yyval.integer.unsignedp = 0; ;
+ break;}
+case 9:
+#line 190 "cexp.y"
+{ keyword_parsing = 1; ;
+ break;}
+case 10:
+#line 192 "cexp.y"
+{ yyval.integer.value = check_assertion (yyvsp[-4].name.address, yyvsp[-4].name.length,
+ 1, yyvsp[-1].keywords);
+ keyword_parsing = 0;
+ yyval.integer.unsignedp = 0; ;
+ break;}
+case 11:
+#line 197 "cexp.y"
+{ yyval.integer = yyvsp[-1].integer; ;
+ break;}
+case 12:
+#line 202 "cexp.y"
+{ yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp;
+ if (yyval.integer.unsignedp)
+ yyval.integer.value = (unsigned long) yyvsp[-2].integer.value * yyvsp[0].integer.value;
+ else
+ {
+ yyval.integer.value = yyvsp[-2].integer.value * yyvsp[0].integer.value;
+ if (yyvsp[-2].integer.value
+ && (yyval.integer.value / yyvsp[-2].integer.value != yyvsp[0].integer.value
+ || (yyval.integer.value & yyvsp[-2].integer.value & yyvsp[0].integer.value) < 0))
+ integer_overflow ();
+ } ;
+ break;}
+case 13:
+#line 214 "cexp.y"
+{ if (yyvsp[0].integer.value == 0)
+ {
+ error ("division by zero in #if");
+ yyvsp[0].integer.value = 1;
+ }
+ yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp;
+ if (yyval.integer.unsignedp)
+ yyval.integer.value = (unsigned long) yyvsp[-2].integer.value / yyvsp[0].integer.value;
+ else
+ {
+ yyval.integer.value = yyvsp[-2].integer.value / yyvsp[0].integer.value;
+ if ((yyval.integer.value & yyvsp[-2].integer.value & yyvsp[0].integer.value) < 0)
+ integer_overflow ();
+ } ;
+ break;}
+case 14:
+#line 229 "cexp.y"
+{ if (yyvsp[0].integer.value == 0)
+ {
+ error ("division by zero in #if");
+ yyvsp[0].integer.value = 1;
+ }
+ yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp;
+ if (yyval.integer.unsignedp)
+ yyval.integer.value = (unsigned long) yyvsp[-2].integer.value % yyvsp[0].integer.value;
+ else
+ yyval.integer.value = yyvsp[-2].integer.value % yyvsp[0].integer.value; ;
+ break;}
+case 15:
+#line 240 "cexp.y"
+{ yyval.integer.value = yyvsp[-2].integer.value + yyvsp[0].integer.value;
+ yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp;
+ if (! yyval.integer.unsignedp
+ && ! possible_sum_sign (yyvsp[-2].integer.value, yyvsp[0].integer.value,
+ yyval.integer.value))
+ integer_overflow (); ;
+ break;}
+case 16:
+#line 247 "cexp.y"
+{ yyval.integer.value = yyvsp[-2].integer.value - yyvsp[0].integer.value;
+ yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp;
+ if (! yyval.integer.unsignedp
+ && ! possible_sum_sign (yyval.integer.value, yyvsp[0].integer.value,
+ yyvsp[-2].integer.value))
+ integer_overflow (); ;
+ break;}
+case 17:
+#line 254 "cexp.y"
+{ yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp;
+ if (yyvsp[0].integer.value < 0 && ! yyvsp[0].integer.unsignedp)
+ yyval.integer.value = right_shift (&yyvsp[-2].integer, -yyvsp[0].integer.value);
+ else
+ yyval.integer.value = left_shift (&yyvsp[-2].integer, yyvsp[0].integer.value); ;
+ break;}
+case 18:
+#line 260 "cexp.y"
+{ yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp;
+ if (yyvsp[0].integer.value < 0 && ! yyvsp[0].integer.unsignedp)
+ yyval.integer.value = left_shift (&yyvsp[-2].integer, -yyvsp[0].integer.value);
+ else
+ yyval.integer.value = right_shift (&yyvsp[-2].integer, yyvsp[0].integer.value); ;
+ break;}
+case 19:
+#line 266 "cexp.y"
+{ yyval.integer.value = (yyvsp[-2].integer.value == yyvsp[0].integer.value);
+ yyval.integer.unsignedp = 0; ;
+ break;}
+case 20:
+#line 269 "cexp.y"
+{ yyval.integer.value = (yyvsp[-2].integer.value != yyvsp[0].integer.value);
+ yyval.integer.unsignedp = 0; ;
+ break;}
+case 21:
+#line 272 "cexp.y"
+{ yyval.integer.unsignedp = 0;
+ if (yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp)
+ yyval.integer.value = (unsigned long) yyvsp[-2].integer.value <= yyvsp[0].integer.value;
+ else
+ yyval.integer.value = yyvsp[-2].integer.value <= yyvsp[0].integer.value; ;
+ break;}
+case 22:
+#line 278 "cexp.y"
+{ yyval.integer.unsignedp = 0;
+ if (yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp)
+ yyval.integer.value = (unsigned long) yyvsp[-2].integer.value >= yyvsp[0].integer.value;
+ else
+ yyval.integer.value = yyvsp[-2].integer.value >= yyvsp[0].integer.value; ;
+ break;}
+case 23:
+#line 284 "cexp.y"
+{ yyval.integer.unsignedp = 0;
+ if (yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp)
+ yyval.integer.value = (unsigned long) yyvsp[-2].integer.value < yyvsp[0].integer.value;
+ else
+ yyval.integer.value = yyvsp[-2].integer.value < yyvsp[0].integer.value; ;
+ break;}
+case 24:
+#line 290 "cexp.y"
+{ yyval.integer.unsignedp = 0;
+ if (yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp)
+ yyval.integer.value = (unsigned long) yyvsp[-2].integer.value > yyvsp[0].integer.value;
+ else
+ yyval.integer.value = yyvsp[-2].integer.value > yyvsp[0].integer.value; ;
+ break;}
+case 25:
+#line 296 "cexp.y"
+{ yyval.integer.value = yyvsp[-2].integer.value & yyvsp[0].integer.value;
+ yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp; ;
+ break;}
+case 26:
+#line 299 "cexp.y"
+{ yyval.integer.value = yyvsp[-2].integer.value ^ yyvsp[0].integer.value;
+ yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp; ;
+ break;}
+case 27:
+#line 302 "cexp.y"
+{ yyval.integer.value = yyvsp[-2].integer.value | yyvsp[0].integer.value;
+ yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp; ;
+ break;}
+case 28:
+#line 305 "cexp.y"
+{ yyval.integer.value = (yyvsp[-2].integer.value && yyvsp[0].integer.value);
+ yyval.integer.unsignedp = 0; ;
+ break;}
+case 29:
+#line 308 "cexp.y"
+{ yyval.integer.value = (yyvsp[-2].integer.value || yyvsp[0].integer.value);
+ yyval.integer.unsignedp = 0; ;
+ break;}
+case 30:
+#line 311 "cexp.y"
+{ yyval.integer.value = yyvsp[-4].integer.value ? yyvsp[-2].integer.value : yyvsp[0].integer.value;
+ yyval.integer.unsignedp = yyvsp[-2].integer.unsignedp || yyvsp[0].integer.unsignedp; ;
+ break;}
+case 31:
+#line 314 "cexp.y"
+{ yyval.integer = yylval.integer; ;
+ break;}
+case 32:
+#line 316 "cexp.y"
+{ yyval.integer = yylval.integer; ;
+ break;}
+case 33:
+#line 318 "cexp.y"
+{ yyval.integer.value = 0;
+ yyval.integer.unsignedp = 0; ;
+ break;}
+case 34:
+#line 323 "cexp.y"
+{ yyval.keywords = 0; ;
+ break;}
+case 35:
+#line 325 "cexp.y"
+{ struct arglist *temp;
+ yyval.keywords = (struct arglist *) xmalloc (sizeof (struct arglist));
+ yyval.keywords->next = yyvsp[-2].keywords;
+ yyval.keywords->name = (U_CHAR *) "(";
+ yyval.keywords->length = 1;
+ temp = yyval.keywords;
+ while (temp != 0 && temp->next != 0)
+ temp = temp->next;
+ temp->next = (struct arglist *) xmalloc (sizeof (struct arglist));
+ temp->next->next = yyvsp[0].keywords;
+ temp->next->name = (U_CHAR *) ")";
+ temp->next->length = 1; ;
+ break;}
+case 36:
+#line 338 "cexp.y"
+{ yyval.keywords = (struct arglist *) xmalloc (sizeof (struct arglist));
+ yyval.keywords->name = yyvsp[-1].name.address;
+ yyval.keywords->length = yyvsp[-1].name.length;
+ yyval.keywords->next = yyvsp[0].keywords; ;
+ break;}
+}
+ /* the action file gets copied in in place of this dollarsign */
+#line 480 "/usr/local/lib/bison.simple"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+ yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+ *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+ yylsp++;
+ if (yylen == 0)
+ {
+ yylsp->first_line = yylloc.first_line;
+ yylsp->first_column = yylloc.first_column;
+ yylsp->last_line = (yylsp-1)->last_line;
+ yylsp->last_column = (yylsp-1)->last_column;
+ yylsp->text = 0;
+ }
+ else
+ {
+ yylsp->last_line = (yylsp+yylen-1)->last_line;
+ yylsp->last_column = (yylsp+yylen-1)->last_column;
+ }
+#endif
+
+ /* Now "shift" the result of the reduction.
+ Determine what state that goes to,
+ based on the state we popped back to
+ and the rule number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+ if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTBASE];
+
+ goto yynewstate;
+
+yyerrlab: /* here on detecting error */
+
+ if (! yyerrstatus)
+ /* If not already recovering from an error, report this error. */
+ {
+ ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (yyn > YYFLAG && yyn < YYLAST)
+ {
+ int size = 0;
+ char *msg;
+ int x, count;
+
+ count = 0;
+ /* Start X at -yyn if nec to avoid negative indexes in yycheck. */
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ size += strlen(yytname[x]) + 15, count++;
+ msg = (char *) malloc(size + 15);
+ if (msg != 0)
+ {
+ strcpy(msg, "parse error");
+
+ if (count < 5)
+ {
+ count = 0;
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ {
+ strcat(msg, count == 0 ? ", expecting `" : " or `");
+ strcat(msg, yytname[x]);
+ strcat(msg, "'");
+ count++;
+ }
+ }
+ yyerror(msg);
+ free(msg);
+ }
+ else
+ yyerror ("parse error; also virtual memory exceeded");
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror("parse error");
+ }
+
+ goto yyerrlab1;
+yyerrlab1: /* here on error raised explicitly by an action */
+
+ if (yyerrstatus == 3)
+ {
+ /* if just tried and failed to reuse lookahead token after an error, discard it. */
+
+ /* return failure if at end of input */
+ if (yychar == YYEOF)
+ YYABORT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#endif
+
+ yychar = YYEMPTY;
+ }
+
+ /* Else will try to reuse lookahead token
+ after shifting the error token. */
+
+ yyerrstatus = 3; /* Each real token shifted decrements this */
+
+ goto yyerrhandle;
+
+yyerrdefault: /* current state does not do anything special for the error token. */
+
+#if 0
+ /* This is wrong; only states that explicitly want error tokens
+ should shift them. */
+ yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
+ if (yyn) goto yydefault;
+#endif
+
+yyerrpop: /* pop the current state because it cannot handle the error token */
+
+ if (yyssp == yyss) YYABORT;
+ yyvsp--;
+ yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+ yylsp--;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "Error: state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+yyerrhandle:
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yyerrdefault;
+
+ yyn += YYTERROR;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+ goto yyerrdefault;
+
+ yyn = yytable[yyn];
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrpop;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrpop;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting error token, ");
+#endif
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ yystate = yyn;
+ goto yynewstate;
+}
+#line 343 "cexp.y"
+
+
+/* During parsing of a C expression, the pointer to the next character
+ is in this variable. */
+
+static char *lexptr;
+
+/* Take care of parsing a number (anything that starts with a digit).
+ Set yylval and return the token type; update lexptr.
+ LEN is the number of characters in it. */
+
+/* maybe needs to actually deal with floating point numbers */
+
+int
+parse_number (olen)
+ int olen;
+{
+ register char *p = lexptr;
+ register int c;
+ register unsigned long n = 0, nd, ULONG_MAX_over_base;
+ register int base = 10;
+ register int len = olen;
+ register int overflow = 0;
+ register int digit, largest_digit = 0;
+ int spec_long = 0;
+
+ for (c = 0; c < len; c++)
+ if (p[c] == '.') {
+ /* It's a float since it contains a point. */
+ yyerror ("floating point numbers not allowed in #if expressions");
+ return ERROR;
+ }
+
+ yylval.integer.unsignedp = 0;
+
+ if (len >= 3 && (!strncmp (p, "0x", 2) || !strncmp (p, "0X", 2))) {
+ p += 2;
+ base = 16;
+ len -= 2;
+ }
+ else if (*p == '0')
+ base = 8;
+
+ ULONG_MAX_over_base = (unsigned long) -1 / base;
+
+ for (; len > 0; len--) {
+ c = *p++;
+
+ if (c >= '0' && c <= '9')
+ digit = c - '0';
+ else if (base == 16 && c >= 'a' && c <= 'f')
+ digit = c - 'a' + 10;
+ else if (base == 16 && c >= 'A' && c <= 'F')
+ digit = c - 'A' + 10;
+ else {
+ /* `l' means long, and `u' means unsigned. */
+ while (1) {
+ if (c == 'l' || c == 'L')
+ {
+ if (spec_long)
+ yyerror ("two `l's in integer constant");
+ spec_long = 1;
+ }
+ else if (c == 'u' || c == 'U')
+ {
+ if (yylval.integer.unsignedp)
+ yyerror ("two `u's in integer constant");
+ yylval.integer.unsignedp = 1;
+ }
+ else
+ break;
+
+ if (--len == 0)
+ break;
+ c = *p++;
+ }
+ /* Don't look for any more digits after the suffixes. */
+ break;
+ }
+ if (largest_digit < digit)
+ largest_digit = digit;
+ nd = n * base + digit;
+ overflow |= ULONG_MAX_over_base < n | nd < n;
+ n = nd;
+ }
+
+ if (len != 0) {
+ yyerror ("Invalid number in #if expression");
+ return ERROR;
+ }
+
+ if (base <= largest_digit)
+ warning ("integer constant contains digits beyond the radix");
+
+ if (overflow)
+ warning ("integer constant out of range");
+
+ /* If too big to be signed, consider it unsigned. */
+ if ((long) n < 0 && ! yylval.integer.unsignedp)
+ {
+ if (base == 10)
+ warning ("integer constant is so large that it is unsigned");
+ yylval.integer.unsignedp = 1;
+ }
+
+ lexptr = p;
+ yylval.integer.value = n;
+ return INT;
+}
+
+struct token {
+ char *operator;
+ int token;
+};
+
+static struct token tokentab2[] = {
+ {"&&", AND},
+ {"||", OR},
+ {"<<", LSH},
+ {">>", RSH},
+ {"==", EQUAL},
+ {"!=", NOTEQUAL},
+ {"<=", LEQ},
+ {">=", GEQ},
+ {"++", ERROR},
+ {"--", ERROR},
+ {NULL, ERROR}
+};
+
+/* Read one token, getting characters through lexptr. */
+
+int
+yylex ()
+{
+ register int c;
+ register int namelen;
+ register unsigned char *tokstart;
+ register struct token *toktab;
+ int wide_flag;
+
+ retry:
+
+ tokstart = (unsigned char *) lexptr;
+ c = *tokstart;
+ /* See if it is a special token of length 2. */
+ if (! keyword_parsing)
+ for (toktab = tokentab2; toktab->operator != NULL; toktab++)
+ if (c == *toktab->operator && tokstart[1] == toktab->operator[1]) {
+ lexptr += 2;
+ if (toktab->token == ERROR)
+ {
+ char *buf = (char *) alloca (40);
+ sprintf (buf, "`%s' not allowed in operand of `#if'", toktab->operator);
+ yyerror (buf);
+ }
+ return toktab->token;
+ }
+
+ switch (c) {
+ case 0:
+ return 0;
+
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ lexptr++;
+ goto retry;
+
+ case 'L':
+ /* Capital L may start a wide-string or wide-character constant. */
+ if (lexptr[1] == '\'')
+ {
+ lexptr++;
+ wide_flag = 1;
+ goto char_constant;
+ }
+ if (lexptr[1] == '"')
+ {
+ lexptr++;
+ wide_flag = 1;
+ goto string_constant;
+ }
+ break;
+
+ case '\'':
+ wide_flag = 0;
+ char_constant:
+ lexptr++;
+ if (keyword_parsing) {
+ char *start_ptr = lexptr - 1;
+ while (1) {
+ c = *lexptr++;
+ if (c == '\\')
+ c = parse_escape (&lexptr);
+ else if (c == '\'')
+ break;
+ }
+ yylval.name.address = tokstart;
+ yylval.name.length = lexptr - start_ptr;
+ return NAME;
+ }
+
+ /* This code for reading a character constant
+ handles multicharacter constants and wide characters.
+ It is mostly copied from c-lex.c. */
+ {
+ register int result = 0;
+ register num_chars = 0;
+ unsigned width = MAX_CHAR_TYPE_SIZE;
+ int max_chars;
+ char *token_buffer;
+
+ if (wide_flag)
+ {
+ width = MAX_WCHAR_TYPE_SIZE;
+#ifdef MULTIBYTE_CHARS
+ max_chars = MB_CUR_MAX;
+#else
+ max_chars = 1;
+#endif
+ }
+ else
+ max_chars = MAX_LONG_TYPE_SIZE / width;
+
+ token_buffer = (char *) alloca (max_chars + 1);
+
+ while (1)
+ {
+ c = *lexptr++;
+
+ if (c == '\'' || c == EOF)
+ break;
+
+ if (c == '\\')
+ {
+ c = parse_escape (&lexptr);
+ if (width < HOST_BITS_PER_INT
+ && (unsigned) c >= (1 << width))
+ pedwarn ("escape sequence out of range for character");
+ }
+
+ num_chars++;
+
+ /* Merge character into result; ignore excess chars. */
+ if (num_chars < max_chars + 1)
+ {
+ if (width < HOST_BITS_PER_INT)
+ result = (result << width) | (c & ((1 << width) - 1));
+ else
+ result = c;
+ token_buffer[num_chars - 1] = c;
+ }
+ }
+
+ token_buffer[num_chars] = 0;
+
+ if (c != '\'')
+ error ("malformatted character constant");
+ else if (num_chars == 0)
+ error ("empty character constant");
+ else if (num_chars > max_chars)
+ {
+ num_chars = max_chars;
+ error ("character constant too long");
+ }
+ else if (num_chars != 1 && ! traditional)
+ warning ("multi-character character constant");
+
+ /* If char type is signed, sign-extend the constant. */
+ if (! wide_flag)
+ {
+ int num_bits = num_chars * width;
+
+ if (lookup ("__CHAR_UNSIGNED__", sizeof ("__CHAR_UNSIGNED__")-1, -1)
+ || ((result >> (num_bits - 1)) & 1) == 0)
+ yylval.integer.value
+ = result & ((unsigned long) ~0 >> (HOST_BITS_PER_LONG - num_bits));
+ else
+ yylval.integer.value
+ = result | ~((unsigned long) ~0 >> (HOST_BITS_PER_LONG - num_bits));
+ }
+ else
+ {
+#ifdef MULTIBYTE_CHARS
+ /* Set the initial shift state and convert the next sequence. */
+ result = 0;
+ /* In all locales L'\0' is zero and mbtowc will return zero,
+ so don't use it. */
+ if (num_chars > 1
+ || (num_chars == 1 && token_buffer[0] != '\0'))
+ {
+ wchar_t wc;
+ (void) mbtowc (NULL_PTR, NULL_PTR, 0);
+ if (mbtowc (& wc, token_buffer, num_chars) == num_chars)
+ result = wc;
+ else
+ warning ("Ignoring invalid multibyte character");
+ }
+#endif
+ yylval.integer.value = result;
+ }
+ }
+
+ /* This is always a signed type. */
+ yylval.integer.unsignedp = 0;
+
+ return CHAR;
+
+ /* some of these chars are invalid in constant expressions;
+ maybe do something about them later */
+ case '/':
+ case '+':
+ case '-':
+ case '*':
+ case '%':
+ case '|':
+ case '&':
+ case '^':
+ case '~':
+ case '!':
+ case '@':
+ case '<':
+ case '>':
+ case '[':
+ case ']':
+ case '.':
+ case '?':
+ case ':':
+ case '=':
+ case '{':
+ case '}':
+ case ',':
+ case '#':
+ if (keyword_parsing)
+ break;
+ case '(':
+ case ')':
+ lexptr++;
+ return c;
+
+ case '"':
+ string_constant:
+ if (keyword_parsing) {
+ char *start_ptr = lexptr;
+ lexptr++;
+ while (1) {
+ c = *lexptr++;
+ if (c == '\\')
+ c = parse_escape (&lexptr);
+ else if (c == '"')
+ break;
+ }
+ yylval.name.address = tokstart;
+ yylval.name.length = lexptr - start_ptr;
+ return NAME;
+ }
+ yyerror ("string constants not allowed in #if expressions");
+ return ERROR;
+ }
+
+ if (c >= '0' && c <= '9' && !keyword_parsing) {
+ /* It's a number */
+ for (namelen = 0;
+ c = tokstart[namelen], is_idchar[c] || c == '.';
+ namelen++)
+ ;
+ return parse_number (namelen);
+ }
+
+ /* It is a name. See how long it is. */
+
+ if (keyword_parsing) {
+ for (namelen = 0;; namelen++) {
+ if (is_hor_space[tokstart[namelen]])
+ break;
+ if (tokstart[namelen] == '(' || tokstart[namelen] == ')')
+ break;
+ if (tokstart[namelen] == '"' || tokstart[namelen] == '\'')
+ break;
+ }
+ } else {
+ if (!is_idstart[c]) {
+ yyerror ("Invalid token in expression");
+ return ERROR;
+ }
+
+ for (namelen = 0; is_idchar[tokstart[namelen]]; namelen++)
+ ;
+ }
+
+ lexptr += namelen;
+ yylval.name.address = tokstart;
+ yylval.name.length = namelen;
+ return NAME;
+}
+
+
+/* Parse a C escape sequence. STRING_PTR points to a variable
+ containing a pointer to the string to parse. That pointer
+ is updated past the characters we use. The value of the
+ escape sequence is returned.
+
+ A negative value means the sequence \ newline was seen,
+ which is supposed to be equivalent to nothing at all.
+
+ If \ is followed by a null character, we return a negative
+ value and leave the string pointer pointing at the null character.
+
+ If \ is followed by 000, we return 0 and leave the string pointer
+ after the zeros. A value of 0 does not mean end of string. */
+
+int
+parse_escape (string_ptr)
+ char **string_ptr;
+{
+ register int c = *(*string_ptr)++;
+ switch (c)
+ {
+ case 'a':
+ return TARGET_BELL;
+ case 'b':
+ return TARGET_BS;
+ case 'e':
+ case 'E':
+ if (pedantic)
+ pedwarn ("non-ANSI-standard escape sequence, `\\%c'", c);
+ return 033;
+ case 'f':
+ return TARGET_FF;
+ case 'n':
+ return TARGET_NEWLINE;
+ case 'r':
+ return TARGET_CR;
+ case 't':
+ return TARGET_TAB;
+ case 'v':
+ return TARGET_VT;
+ case '\n':
+ return -2;
+ case 0:
+ (*string_ptr)--;
+ return 0;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ register int i = c - '0';
+ register int count = 0;
+ while (++count < 3)
+ {
+ c = *(*string_ptr)++;
+ if (c >= '0' && c <= '7')
+ i = (i << 3) + c - '0';
+ else
+ {
+ (*string_ptr)--;
+ break;
+ }
+ }
+ if ((i & ~((1 << MAX_CHAR_TYPE_SIZE) - 1)) != 0)
+ {
+ i &= (1 << MAX_CHAR_TYPE_SIZE) - 1;
+ warning ("octal character constant does not fit in a byte");
+ }
+ return i;
+ }
+ case 'x':
+ {
+ register unsigned i = 0, overflow = 0, digits_found = 0, digit;
+ for (;;)
+ {
+ c = *(*string_ptr)++;
+ if (c >= '0' && c <= '9')
+ digit = c - '0';
+ else if (c >= 'a' && c <= 'f')
+ digit = c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ digit = c - 'A' + 10;
+ else
+ {
+ (*string_ptr)--;
+ break;
+ }
+ overflow |= i ^ (i << 4 >> 4);
+ i = (i << 4) + digit;
+ digits_found = 1;
+ }
+ if (!digits_found)
+ yyerror ("\\x used with no following hex digits");
+ if (overflow | (i & ~((1 << BITS_PER_UNIT) - 1)))
+ {
+ i &= (1 << BITS_PER_UNIT) - 1;
+ warning ("hex character constant does not fit in a byte");
+ }
+ return i;
+ }
+ default:
+ return c;
+ }
+}
+
+void
+yyerror (s)
+ char *s;
+{
+ error (s);
+ longjmp (parse_return_error, 1);
+}
+
+static void
+integer_overflow ()
+{
+ if (pedantic)
+ pedwarn ("integer overflow in preprocessor expression");
+}
+
+static long
+left_shift (a, b)
+ struct constant *a;
+ unsigned long b;
+{
+ if (b >= HOST_BITS_PER_LONG)
+ {
+ if (! a->unsignedp && a->value != 0)
+ integer_overflow ();
+ return 0;
+ }
+ else if (a->unsignedp)
+ return (unsigned long) a->value << b;
+ else
+ {
+ long l = a->value << b;
+ if (l >> b != a->value)
+ integer_overflow ();
+ return l;
+ }
+}
+
+static long
+right_shift (a, b)
+ struct constant *a;
+ unsigned long b;
+{
+ if (b >= HOST_BITS_PER_LONG)
+ return a->unsignedp ? 0 : a->value >> (HOST_BITS_PER_LONG - 1);
+ else if (a->unsignedp)
+ return (unsigned long) a->value >> b;
+ else
+ return a->value >> b;
+}
+
+/* This page contains the entry point to this file. */
+
+/* Parse STRING as an expression, and complain if this fails
+ to use up all of the contents of STRING. */
+/* We do not support C comments. They should be removed before
+ this function is called. */
+
+int
+parse_c_expression (string)
+ char *string;
+{
+ lexptr = string;
+
+ if (lexptr == 0 || *lexptr == 0) {
+ error ("empty #if expression");
+ return 0; /* don't include the #if group */
+ }
+
+ /* if there is some sort of scanning error, just return 0 and assume
+ the parsing routine has printed an error message somewhere.
+ there is surely a better thing to do than this. */
+ if (setjmp (parse_return_error))
+ return 0;
+
+ if (yyparse ())
+ return 0; /* actually this is never reached
+ the way things stand. */
+ if (*lexptr)
+ error ("Junk after end of expression.");
+
+ return expression_value; /* set by yyparse () */
+}
+
+#ifdef TEST_EXP_READER
+extern int yydebug;
+
+/* Main program for testing purposes. */
+int
+main ()
+{
+ int n, c;
+ char buf[1024];
+
+/*
+ yydebug = 1;
+*/
+ initialize_random_junk ();
+
+ for (;;) {
+ printf ("enter expression: ");
+ n = 0;
+ while ((buf[n] = getchar ()) != '\n' && buf[n] != EOF)
+ n++;
+ if (buf[n] == EOF)
+ break;
+ buf[n] = '\0';
+ printf ("parser returned %d\n", parse_c_expression (buf));
+ }
+
+ return 0;
+}
+
+/* table to tell if char can be part of a C identifier. */
+unsigned char is_idchar[256];
+/* table to tell if char can be first char of a c identifier. */
+unsigned char is_idstart[256];
+/* table to tell if c is horizontal space. isspace () thinks that
+ newline is space; this is not a good idea for this program. */
+char is_hor_space[256];
+
+/*
+ * initialize random junk in the hash table and maybe other places
+ */
+initialize_random_junk ()
+{
+ register int i;
+
+ /*
+ * Set up is_idchar and is_idstart tables. These should be
+ * faster than saying (is_alpha (c) || c == '_'), etc.
+ * Must do set up these things before calling any routines tthat
+ * refer to them.
+ */
+ for (i = 'a'; i <= 'z'; i++) {
+ ++is_idchar[i - 'a' + 'A'];
+ ++is_idchar[i];
+ ++is_idstart[i - 'a' + 'A'];
+ ++is_idstart[i];
+ }
+ for (i = '0'; i <= '9'; i++)
+ ++is_idchar[i];
+ ++is_idchar['_'];
+ ++is_idstart['_'];
+#if DOLLARS_IN_IDENTIFIERS
+ ++is_idchar['$'];
+ ++is_idstart['$'];
+#endif
+
+ /* horizontal space table */
+ ++is_hor_space[' '];
+ ++is_hor_space['\t'];
+}
+
+error (msg)
+{
+ printf ("error: %s\n", msg);
+}
+
+warning (msg)
+{
+ printf ("warning: %s\n", msg);
+}
+
+struct hashnode *
+lookup (name, len, hash)
+ char *name;
+ int len;
+ int hash;
+{
+ return (DEFAULT_SIGNED_CHAR) ? 0 : ((struct hashnode *) -1);
+}
+#endif
diff --git a/gnu/usr.bin/cc/cpp/cpp.1 b/gnu/usr.bin/cc/cpp/cpp.1
new file mode 100644
index 0000000..54c4dfb
--- /dev/null
+++ b/gnu/usr.bin/cc/cpp/cpp.1
@@ -0,0 +1 @@
+.so man1/cccp.1
diff --git a/gnu/usr.bin/cc/include/basic-block.h b/gnu/usr.bin/cc/include/basic-block.h
new file mode 100644
index 0000000..b1bc002
--- /dev/null
+++ b/gnu/usr.bin/cc/include/basic-block.h
@@ -0,0 +1,68 @@
+/* Define control and data flow tables, and regsets.
+ Copyright (C) 1987 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* Number of bits in each actual element of a regset. */
+
+#define REGSET_ELT_BITS HOST_BITS_PER_WIDE_INT
+
+/* Type to use for a regset element. Note that lots of code assumes
+ that the initial part of a regset that contains information on the
+ hard registers is the same format as a HARD_REG_SET. */
+
+#define REGSET_ELT_TYPE unsigned HOST_WIDE_INT
+
+/* Define the type for a pointer to a set with a bit for each
+ (hard or pseudo) register. */
+
+typedef REGSET_ELT_TYPE *regset;
+
+/* Size of a regset for the current function,
+ in (1) bytes and (2) elements. */
+
+extern int regset_bytes;
+extern int regset_size;
+
+/* Number of basic blocks in the current function. */
+
+extern int n_basic_blocks;
+
+/* Index by basic block number, get first insn in the block. */
+
+extern rtx *basic_block_head;
+
+/* Index by basic block number, get last insn in the block. */
+
+extern rtx *basic_block_end;
+
+/* Index by basic block number, get address of regset
+ describing the registers live at the start of that block. */
+
+extern regset *basic_block_live_at_start;
+
+/* Indexed by n, gives number of basic block that (REG n) is used in.
+ If the value is REG_BLOCK_GLOBAL (-2),
+ it means (REG n) is used in more than one basic block.
+ REG_BLOCK_UNKNOWN (-1) means it hasn't been seen yet so we don't know.
+ This information remains valid for the rest of the compilation
+ of the current function; it is used to control register allocation. */
+
+#define REG_BLOCK_UNKNOWN -1
+#define REG_BLOCK_GLOBAL -2
+extern int *reg_basic_block;
diff --git a/gnu/usr.bin/cc/include/bc-arity.h b/gnu/usr.bin/cc/include/bc-arity.h
new file mode 100644
index 0000000..d311745
--- /dev/null
+++ b/gnu/usr.bin/cc/include/bc-arity.h
@@ -0,0 +1,232 @@
+{ 0, 0, 0, {0}},
+{ 1, 0, 0, {0}},
+{ 1, 2, 0, {0}},
+{ 1, 2, 0, {0}},
+{ 0, 0, 1, {SIcode, }},
+{ 0, 0, 1, {SIcode, }},
+{ 0, 1, 1, {QIcode, }},
+{ 0, 1, 1, {HIcode, }},
+{ 0, 1, 1, {SIcode, }},
+{ 0, 1, 1, {DIcode, }},
+{ 0, 1, 1, {SFcode, }},
+{ 0, 1, 1, {DFcode, }},
+{ 0, 1, 1, {XFcode, }},
+{ 0, 1, 1, {Pcode, }},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 2, 0, 0, {0}},
+{ 2, 0, 0, {0}},
+{ 2, 0, 0, {0}},
+{ 2, 0, 0, {0}},
+{ 2, 0, 0, {0}},
+{ 2, 0, 0, {0}},
+{ 2, 0, 0, {0}},
+{ 2, 0, 0, {0}},
+{ 3, 0, 0, {0}},
+{ 2, 0, 0, {0}},
+{ 1, 1, 1, {SIcode, }},
+{ 1, 1, 0, {0}},
+{ 0, 1, 1, {SIcode, }},
+{ 0, 1, 1, {SIcode, }},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 3, 1, 0, {0}},
+{ 3, 1, 0, {0}},
+{ 4, 0, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 1, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 4, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 4, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 4, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 2, 1, 0, {0}},
+{ 4, 1, 0, {0}},
+{ 1, 0, 1, {SIcode, }},
+{ 1, 0, 1, {SIcode, }},
+{ 0, 0, 1, {SIcode, }},
+{ 0, 0, 0, {0}},
+{ 1, 0, 3, {SIcode, SIcode, SIcode, }},
+{ 1, 0, 3, {SIcode, SIcode, SIcode, }},
+{ 1, 0, 3, {SIcode, SIcode, SIcode, }},
+{ 1, 0, 3, {SIcode, SIcode, SIcode, }},
+{ 3, 0, 0, {0}},
+{ 0, 1, 0, {0}},
+{ 0, 0, 0, {0}},
+{ 0, 0, 1, {SIcode, }},
diff --git a/gnu/usr.bin/cc/include/bc-emit.h b/gnu/usr.bin/cc/include/bc-emit.h
new file mode 100644
index 0000000..c00da5b
--- /dev/null
+++ b/gnu/usr.bin/cc/include/bc-emit.h
@@ -0,0 +1,133 @@
+/* bc-emit.h - declare entry points for producing object files of bytecodes. */
+
+/* Internal format of symbol table for the object file. */
+struct bc_sym
+{
+ /* Private copy separately malloc'd. */
+ char *name;
+
+ /* Symbol has a defined value. */
+ unsigned int defined:1;
+
+ /* Symbol has been globalized. */
+ unsigned int global:1;
+
+ /* Symbol is common. */
+ unsigned int common:1;
+
+ /* Value if defined. */
+ unsigned long int val;
+
+ /* Used in internal symbol table structure. */
+ struct bc_sym *next;
+};
+
+
+/* List of symbols defined in a particular segment. */
+struct bc_segsym
+{
+ struct bc_sym *sym;
+ struct bc_segsym *next;
+};
+
+
+/* List of relocations needed in a particular segment. */
+struct bc_segreloc
+{
+ /* Offset of datum to be relocated. */
+ unsigned int offset;
+
+ /* Symbol to be relocated by. */
+ struct bc_sym *sym;
+
+ struct bc_segreloc *next;
+};
+
+
+/* Segment of an object file. */
+struct bc_seg
+{
+ /* Size allocated to contents. */
+ unsigned int alloc;
+
+ /* Pointer to base of contents. */
+ char *data;
+
+ /* Actual size of contents. */
+ unsigned int size;
+
+ /* List of symbols defined in this segment. */
+ struct bc_segsym *syms;
+
+ /* List of relocations for this segment. */
+ struct bc_segreloc *relocs;
+};
+
+
+/* Anonymous bytecode label within a single function. */
+struct bc_label
+{
+ /* Offset of label from start of segment. */
+ unsigned int offset;
+
+ /* True when offset is valid. */
+ unsigned int defined:1;
+
+ /* Unique bytecode ID, used to determine innermost
+ block containment */
+ int uid;
+
+ /* Next node in list */
+ struct bc_label *next;
+};
+
+
+/* Reference to a bc_label; a list of all such references is kept for
+ the function, then when it is finished they are backpatched to
+ contain the correct values. */
+
+struct bc_labelref
+{
+ /* Label referenced. */
+ struct bc_label *label;
+
+ /* Code offset of reference. */
+ unsigned int offset;
+
+ /* Next labelref in list */
+ struct bc_labelref *next;
+};
+
+
+
+extern void bc_initialize();
+extern int bc_begin_function();
+extern char *bc_emit_trampoline();
+extern void bc_emit_bytecode();
+extern void bc_emit_bytecode_const();
+extern struct bc_label *bc_get_bytecode_label();
+extern int bc_emit_bytecode_labeldef();
+extern void bc_emit_bytecode_labelref();
+extern void bc_emit_code_labelref();
+extern char *bc_end_function();
+extern void bc_align_const();
+extern void bc_emit_const();
+extern void bc_emit_const_skip();
+extern int bc_emit_const_labeldef();
+extern void bc_emit_const_labelref();
+extern void bc_align_data();
+extern void bc_emit_data();
+extern void bc_emit_data_skip();
+extern int bc_emit_data_labeldef();
+extern void bc_emit_data_labelref();
+extern int bc_define_pointer ();
+extern int bc_emit_common();
+extern void bc_globalize_label();
+extern void bc_text();
+extern void bc_data();
+extern void bc_align();
+extern void bc_emit();
+extern void bc_emit_skip();
+extern int bc_emit_labeldef();
+extern void bc_emit_labelref();
+extern void bc_write_file();
diff --git a/gnu/usr.bin/cc/include/bc-opcode.h b/gnu/usr.bin/cc/include/bc-opcode.h
new file mode 100644
index 0000000..ba5cafe
--- /dev/null
+++ b/gnu/usr.bin/cc/include/bc-opcode.h
@@ -0,0 +1,238 @@
+/* This file is automatically generated from bytecode.def,
+do not make any changes here. Instead edit bytecode.def. */
+
+enum bytecode_opcode
+{ neverneverland,
+ drop,
+ duplicate,
+ over,
+ setstackSI,
+ adjstackSI,
+ constQI,
+ constHI,
+ constSI,
+ constDI,
+ constSF,
+ constDF,
+ constXF,
+ constP,
+ loadQI,
+ loadHI,
+ loadSI,
+ loadDI,
+ loadSF,
+ loadDF,
+ loadXF,
+ loadP,
+ storeQI,
+ storeHI,
+ storeSI,
+ storeDI,
+ storeSF,
+ storeDF,
+ storeXF,
+ storeP,
+ storeBLK,
+ clearBLK,
+ addconstPSI,
+ newlocalSI,
+ localP,
+ argP,
+ convertQIHI,
+ convertHISI,
+ convertSIDI,
+ convertQISI,
+ convertQUHU,
+ convertHUSU,
+ convertSUDU,
+ convertQUSU,
+ convertSFDF,
+ convertDFXF,
+ convertHIQI,
+ convertSIHI,
+ convertDISI,
+ convertSIQI,
+ convertSUQU,
+ convertDFSF,
+ convertXFDF,
+ convertSISF,
+ convertSIDF,
+ convertSIXF,
+ convertSUSF,
+ convertSUDF,
+ convertSUXF,
+ convertDISF,
+ convertDIDF,
+ convertDIXF,
+ convertDUSF,
+ convertDUDF,
+ convertDUXF,
+ convertSFSI,
+ convertDFSI,
+ convertXFSI,
+ convertSFSU,
+ convertDFSU,
+ convertXFSU,
+ convertSFDI,
+ convertDFDI,
+ convertXFDI,
+ convertSFDU,
+ convertDFDU,
+ convertXFDU,
+ convertPSI,
+ convertSIP,
+ convertSIT,
+ convertDIT,
+ convertSFT,
+ convertDFT,
+ convertXFT,
+ convertPT,
+ zxloadBI,
+ sxloadBI,
+ sstoreBI,
+ addSI,
+ addDI,
+ addSF,
+ addDF,
+ addXF,
+ addPSI,
+ subSI,
+ subDI,
+ subSF,
+ subDF,
+ subXF,
+ subPP,
+ mulSI,
+ mulDI,
+ mulSU,
+ mulDU,
+ mulSF,
+ mulDF,
+ mulXF,
+ divSI,
+ divDI,
+ divSU,
+ divDU,
+ divSF,
+ divDF,
+ divXF,
+ modSI,
+ modDI,
+ modSU,
+ modDU,
+ andSI,
+ andDI,
+ iorSI,
+ iorDI,
+ xorSI,
+ xorDI,
+ lshiftSI,
+ lshiftSU,
+ lshiftDI,
+ lshiftDU,
+ rshiftSI,
+ rshiftSU,
+ rshiftDI,
+ rshiftDU,
+ ltSI,
+ ltSU,
+ ltDI,
+ ltDU,
+ ltSF,
+ ltDF,
+ ltXF,
+ ltP,
+ leSI,
+ leSU,
+ leDI,
+ leDU,
+ leSF,
+ leDF,
+ leXF,
+ leP,
+ geSI,
+ geSU,
+ geDI,
+ geDU,
+ geSF,
+ geDF,
+ geXF,
+ geP,
+ gtSI,
+ gtSU,
+ gtDI,
+ gtDU,
+ gtSF,
+ gtDF,
+ gtXF,
+ gtP,
+ eqSI,
+ eqDI,
+ eqSF,
+ eqDF,
+ eqXF,
+ eqP,
+ neSI,
+ neDI,
+ neSF,
+ neDF,
+ neXF,
+ neP,
+ negSI,
+ negDI,
+ negSF,
+ negDF,
+ negXF,
+ notSI,
+ notDI,
+ notT,
+ predecQI,
+ predecHI,
+ predecSI,
+ predecDI,
+ predecP,
+ predecSF,
+ predecDF,
+ predecXF,
+ predecBI,
+ preincQI,
+ preincHI,
+ preincSI,
+ preincDI,
+ preincP,
+ preincSF,
+ preincDF,
+ preincXF,
+ preincBI,
+ postdecQI,
+ postdecHI,
+ postdecSI,
+ postdecDI,
+ postdecP,
+ postdecSF,
+ postdecDF,
+ postdecXF,
+ postdecBI,
+ postincQI,
+ postincHI,
+ postincSI,
+ postincDI,
+ postincP,
+ postincSF,
+ postincDF,
+ postincXF,
+ postincBI,
+ xjumpif,
+ xjumpifnot,
+ jump,
+ jumpP,
+ caseSI,
+ caseSU,
+ caseDI,
+ caseDU,
+ call,
+ returnP,
+ ret,
+ linenote,
+ LAST_AND_UNUSED_OPCODE
+};
diff --git a/gnu/usr.bin/cc/include/bc-optab.h b/gnu/usr.bin/cc/include/bc-optab.h
new file mode 100644
index 0000000..f42485f
--- /dev/null
+++ b/gnu/usr.bin/cc/include/bc-optab.h
@@ -0,0 +1,74 @@
+/* Bytecode token definitions for GNU C-compiler.
+ Copyright (C) 1993 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+extern void bc_expand_conversion ();
+extern void bc_expand_truth_conversion ();
+extern void bc_expand_binary_operation ();
+extern void bc_expand_unary_operation ();
+
+struct binary_operator
+{
+ enum bytecode_opcode opcode;
+ enum typecode result;
+ enum typecode arg0;
+ enum typecode arg1;
+};
+
+extern struct binary_operator optab_plus_expr[];
+extern struct binary_operator optab_minus_expr[];
+extern struct binary_operator optab_mult_expr[];
+extern struct binary_operator optab_trunc_div_expr[];
+extern struct binary_operator optab_trunc_mod_expr[];
+extern struct binary_operator optab_rdiv_expr[];
+extern struct binary_operator optab_bit_and_expr[];
+extern struct binary_operator optab_bit_ior_expr[];
+extern struct binary_operator optab_bit_xor_expr[];
+extern struct binary_operator optab_lshift_expr[];
+extern struct binary_operator optab_rshift_expr[];
+extern struct binary_operator optab_truth_and_expr[];
+extern struct binary_operator optab_truth_or_expr[];
+extern struct binary_operator optab_lt_expr[];
+extern struct binary_operator optab_le_expr[];
+extern struct binary_operator optab_ge_expr[];
+extern struct binary_operator optab_gt_expr[];
+extern struct binary_operator optab_eq_expr[];
+extern struct binary_operator optab_ne_expr[];
+
+struct unary_operator
+{
+ enum bytecode_opcode opcode;
+ enum typecode result;
+ enum typecode arg0;
+};
+
+extern struct unary_operator optab_negate_expr[];
+extern struct unary_operator optab_bit_not_expr[];
+extern struct unary_operator optab_truth_not_expr[];
+
+struct increment_operator
+{
+ enum bytecode_opcode opcode;
+ enum typecode arg;
+};
+
+extern struct increment_operator optab_predecrement_expr[];
+extern struct increment_operator optab_preincrement_expr[];
+extern struct increment_operator optab_postdecrement_expr[];
+extern struct increment_operator optab_postincrement_expr[];
diff --git a/gnu/usr.bin/cc/include/bc-typecd.def b/gnu/usr.bin/cc/include/bc-typecd.def
new file mode 100644
index 0000000..fd92cdd
--- /dev/null
+++ b/gnu/usr.bin/cc/include/bc-typecd.def
@@ -0,0 +1,21 @@
+/* Typecodes used by the interpreter and their related
+ machine modes and types.
+
+ The last argument is used for retrieving the given
+ type from a varargs list. Due to a bug in varargs,
+ the type has to be the generic machine type of
+ larger. */
+
+DEFTYPECODE (QIcode, "QI", QImode, SItype)
+DEFTYPECODE (QUcode, "QU", QImode, SUtype)
+DEFTYPECODE (HIcode, "HI", HImode, SItype)
+DEFTYPECODE (HUcode, "HU", HImode, SUtype)
+DEFTYPECODE (SIcode, "SI", SImode, SItype)
+DEFTYPECODE (SUcode, "SU", SImode, SUtype)
+DEFTYPECODE (DIcode, "DI", DImode, DItype)
+DEFTYPECODE (DUcode, "DU", DImode, DUtype)
+DEFTYPECODE (SFcode, "SF", SFmode, SFtype)
+DEFTYPECODE (DFcode, "DF", DFmode, DFtype)
+DEFTYPECODE (XFcode, "XF", XFmode, XFtype)
+DEFTYPECODE (Pcode, "P", PSImode, Ptype)
+DEFTYPECODE (Tcode, "T", SImode, SItype)
diff --git a/gnu/usr.bin/cc/include/bc-typecd.h b/gnu/usr.bin/cc/include/bc-typecd.h
new file mode 100644
index 0000000..097cd62
--- /dev/null
+++ b/gnu/usr.bin/cc/include/bc-typecd.h
@@ -0,0 +1,53 @@
+/* Typecode definitions for Bytecode Interpreter.
+ Copyright (C) 1993 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef TYPECODE_H
+#define TYPECODE_H
+
+enum typecode
+{
+#define DEFTYPECODE(CODE, NAME, MACHMODE, TYPE) CODE,
+#include "bc-typecd.def"
+#undef DEFTYPECODE
+
+ LAST_AND_UNUSED_TYPECODE
+};
+
+/* Determine if a given type is integer. */
+#define TYPECODE_INTEGER_P(TYPECODE) ((int) (TYPECODE) < (int) SFcode)
+
+/* Determine if a given type is unsigned. */
+#define TYPECODE_UNSIGNED_P(TYPECODE) \
+ (TYPECODE_INTEGER_P(TYPECODE) && (int) (TYPECODE) & 1)
+
+/* Determine if a given type is signed. */
+#define TYPECODE_SIGNED_P(TYPECODE) \
+ (TYPECODE_INTEGER_P(TYPECODE) && !((int) (TYPECODE) & 1))
+
+/* Determine if a given type is floating. */
+#define TYPECODE_FLOAT_P(TYPECODE) \
+ ((int) (TYPECODE) < (int) Pcode && !TYPECODE_INTEGER_P(TYPECODE))
+
+/* Determine if the given type is arithmetic. */
+#define TYPECODE_ARITH_P(TYPECODE) \
+ (TYPECODE_INTEGER_P(TYPECODE) || TYPECODE_FLOAT_P(TYPECODE))
+
+#define NUM_TYPECODES ((int) LAST_AND_UNUSED_TYPECODE)
+
+#endif
diff --git a/gnu/usr.bin/cc/include/bi-run.h b/gnu/usr.bin/cc/include/bi-run.h
new file mode 100644
index 0000000..669f2ab
--- /dev/null
+++ b/gnu/usr.bin/cc/include/bi-run.h
@@ -0,0 +1,165 @@
+/* Definitions for Bytecode Interpreter.
+ Copyright (C) 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define MAXLITERALS 5
+
+struct arityvec
+{
+ char ninputs;
+ char noutputs;
+ char nliterals;
+ char literals[MAXLITERALS];
+};
+
+struct argtype
+{
+ int modealign; /* Argument mode:alignment */
+ int size; /* Argument size, in bytes */
+};
+
+struct callinfo
+{
+ int nargs; /* Number of arguments in call */
+ struct argtype retvaltype; /* Type of return value */
+ struct argtype argtypes[1]; /* Argument types */
+};
+
+/* Structure describing a bytecode function. If this changes, we also
+ need to change expand_function_end () in bc-trans.c */
+struct bytecode
+{
+ int stacksize; /* Depth required of evaluation stack. */
+ int localsize; /* Size in bytes of local variables. */
+ unsigned char *pc0; /* Initial program counter. */
+ void **ptrlit; /* Vector of (relocatable) pointer literals. */
+ struct callinfo *callinfo; /* Vector of procedure call type info. */
+};
+
+
+#define INTERP_BPC 8 /* Bits per char */
+#define INTERP_BPI \
+ (sizeof (int) * INTERP_BPC) /* Bits per int */
+
+
+#ifndef min
+#define min(L, R) ((L) < (R) ? (L) : (R))
+#endif
+
+
+/* bit field operations. */
+
+/* Low (high) mask: int with low (high) N bits set */
+
+#define LM(N) ((1 << (N)) - 1)
+#define HM(N) ((~LM (INTERP_BPI - (N))))
+
+
+/* Sign-extend SIZE low bits of VALUE to integer (typeof VALUE)
+ Signed bitfields are loaded from memory by the sxloadBI instruction,
+ which first retrieves the bitfield with XFIELD and then sign extends
+ it to an SItype. */
+
+#define EXTEND(SIZE, VALUE) \
+ ({ SUtype value = (SUtype) (VALUE); \
+ (value & (1 << ((SIZE) - 1)) ? value | ~LM (SIZE) : value); })
+
+
+/* Given OFFSET:SIZE for a bitfield, calculate:
+
+ [1] BYTE_OFFSET = the byte offset of the bit field.
+ [2] BIT_OFFSET = the bit offset of the bit field (less than INTERP_BPC).
+ [3] NBYTES = the number of integral bytes in the bit field.
+ [4] TRAILING_BITS= the number of trailing bits (less than INTERP_BPC).
+
+
+ , , , , , (memory bytes)
+ ---------------- (bitfield)
+ | | || | | (divisions)
+ ^ ^ ^ ^
+ | | | |__ [4] (bits)
+ | | |_________ [3] (bytes)
+ | |_________________ [2] (bits)
+ |___________________________ [1] (bytes)
+
+
+ The above applies to BYTE_LOW_ENDIAN machines. In BYTE_BIG_ENDIAN machines, the
+ bit numbering is reversed (i.e. bit 0 is the sign bit).
+
+ (Alright, so I drew this to keep my tongue in cheek while writing the code below,
+ not because I'm into ASCII art.) */
+
+
+#define BI_PARAMS(OFFSET, SIZE, BYTE_OFFSET, BIT_OFFSET, NBYTES, TRAILING_BITS) \
+ { BYTE_OFFSET = (OFFSET) / (INTERP_BPC); \
+ BIT_OFFSET = (OFFSET) % (INTERP_BPC); \
+ NBYTES = ((SIZE) - (INTERP_BPC - (BIT_OFFSET))) / INTERP_BPC; \
+ if ((NBYTES) < 0 || ((NBYTES) > 64)) \
+ NBYTES = 0; \
+ if ((SIZE) + (BIT_OFFSET) <= INTERP_BPC) \
+ TRAILING_BITS = 0; \
+ else \
+ TRAILING_BITS = ((SIZE) - (INTERP_BPC - (BIT_OFFSET))) % INTERP_BPC; }
+
+
+/* SHIFT_IN_BITS retrieves NBITS bits from SOURCE and shifts into
+ DEST. The bit field starts OFFSET bits into SOURCE.
+
+ OR_IN_BITS copies the NBITS low bits from VALUE into a the bitfield in
+ DEST offset by OFFSET bits. */
+
+
+#if BYTES_BIG_ENDIAN
+
+#define SHIFT_IN_BITS(DEST, SOURCE, OFFSET, NBITS) \
+ (DEST = ((DEST) << (NBITS)) \
+ | (LM ((NBITS)) \
+ & ((SOURCE) >> (INTERP_BPC - (OFFSET) - (NBITS)))))
+
+#define OR_IN_BITS(DEST, VALUE, OFFSET, NBITS) \
+ (DEST = ((DEST) & ~(LM ((NBITS)) << (INTERP_BPC - (OFFSET) - (NBITS)))) \
+ | (((VALUE) & LM ((NBITS))) << (INTERP_BPC - (OFFSET) - (NBITS))))
+
+#else
+
+#define SHIFT_IN_BITS(DEST, SOURCE, OFFSET, NBITS) \
+ (DEST = ((DEST) << (NBITS)) \
+ | (LM ((NBITS)) \
+ & ((SOURCE) >> (OFFSET))))
+
+#define OR_IN_BITS(DEST, VALUE, OFFSET, NBITS) \
+ (DEST = ((DEST) & ~(LM ((NBITS)) << (OFFSET))) \
+ | (((VALUE) & LM ((NBITS))) << (OFFSET)))
+
+#endif
+
+
+/* Procedure call; arguments are a pointer to the function to be called,
+ a pointer to a place to store the return value, a pointer to a vector
+ describing the type of procedure call, and the interpreter's stack pointer,
+ which will point to the first of the arguments at this point. */
+
+#define CALL(FUNC, CALLDESC, RETVAL, SP) __call(FUNC, CALLDESC, RETVAL, SP)
+
+
+/* Procedure return; arguments are a pointer to the calldesc for this
+ function, and a pointer to the place where the value to be returned
+ may be found. Generally the MACHARGS above contain a machine dependent
+ cookie that is used to determine where to jump to. */
+
+#define PROCRET(CALLDESC, RETVAL) return
diff --git a/gnu/usr.bin/cc/include/bytecode.h b/gnu/usr.bin/cc/include/bytecode.h
new file mode 100644
index 0000000..87030be
--- /dev/null
+++ b/gnu/usr.bin/cc/include/bytecode.h
@@ -0,0 +1,91 @@
+/* Bytecode definitions for GNU C-compiler.
+ Copyright (C) 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+extern int output_bytecode;
+extern int stack_depth;
+extern int max_stack_depth;
+
+/* Emit DI constant according to target machine word ordering */
+
+#if WORDS_BIG_ENDIAN
+
+#define bc_emit_bytecode_DI_const(CST) \
+{ int opcode; \
+ opcode = TREE_INT_CST_HIGH (CST); \
+ bc_emit_bytecode_const ((char *) &opcode, sizeof opcode); \
+ opcode = TREE_INT_CST_LOW (CST); \
+ bc_emit_bytecode_const ((char *) &opcode, sizeof opcode); \
+}
+
+#else
+
+#define bc_emit_bytecode_DI_const(CST) \
+{ int opcode; \
+ opcode = TREE_INT_CST_LOW (CST); \
+ bc_emit_bytecode_const ((char *) &opcode, sizeof opcode); \
+ opcode = TREE_INT_CST_HIGH (CST); \
+ bc_emit_bytecode_const ((char *) &opcode, sizeof opcode); \
+}
+
+#endif
+
+
+extern void bc_expand_expr ();
+extern void bc_output_data_constructor ();
+extern void bc_store_field ();
+extern void bc_load_bit_field ();
+extern void bc_store_bit_field ();
+extern void bc_push_offset_and_size ();
+extern void bc_init_mode_to_code_map ();
+
+/* These are just stubs, so the compiler will compile for targets
+ that aren't yet supported by the bytecode generator. */
+
+#ifndef TARGET_SUPPORTS_BYTECODE
+
+#define MACHINE_SEG_ALIGN 1
+#define INT_ALIGN 1
+#define PTR_ALIGN 1
+#define NAMES_HAVE_UNDERSCORES
+#define BC_NOP (0)
+#define BC_GLOBALIZE_LABEL(FP, NAME) BC_NOP
+#define BC_OUTPUT_COMMON(FP, NAME, SIZE, ROUNDED) BC_NOP
+#define BC_OUTPUT_LOCAL(FP, NAME, SIZE, ROUNDED) BC_NOP
+#define BC_OUTPUT_ALIGN(FP, ALIGN) BC_NOP
+#define BC_OUTPUT_LABEL(FP, NAME) BC_NOP
+#define BC_OUTPUT_SKIP(FP, SIZE) BC_NOP
+#define BC_OUTPUT_LABELREF(FP, NAME) BC_NOP
+#define BC_OUTPUT_FLOAT(FP, VAL) BC_NOP
+#define BC_OUTPUT_DOUBLE(FP, VAL) BC_NOP
+#define BC_OUTPUT_BYTE(FP, VAL) BC_NOP
+#define BC_OUTPUT_FILE ASM_OUTPUT_FILE
+#define BC_OUTPUT_ASCII ASM_OUTPUT_ASCII
+#define BC_OUTPUT_IDENT ASM_OUTPUT_IDENT
+#define BCXSTR(RTX) ((RTX)->bc_label)
+#define BC_WRITE_FILE(FP) BC_NOP
+#define BC_WRITE_SEGSYM(SEGSYM, FP) BC_NOP
+#define BC_WRITE_RELOC_ENTRY(SEGRELOC, FP, OFFSET) BC_NOP
+#define BC_START_BYTECODE_LINE(FP) BC_NOP
+#define BC_WRITE_BYTECODE(SEP, VAL, FP) BC_NOP
+#define BC_WRITE_RTL(R, FP) BC_NOP
+#define BC_EMIT_TRAMPOLINE(TRAMPSEG, CALLINFO) BC_NOP
+#define VALIDATE_STACK BC_NOP
+
+#endif /* !TARGET_SUPPORTS_BYTECODE */
diff --git a/gnu/usr.bin/cc/include/bytetypes.h b/gnu/usr.bin/cc/include/bytetypes.h
new file mode 100644
index 0000000..f915669
--- /dev/null
+++ b/gnu/usr.bin/cc/include/bytetypes.h
@@ -0,0 +1,35 @@
+/* These should come from genemit */
+
+/* Use __signed__ in case compiling with -traditional. */
+
+typedef __signed__ char QItype;
+typedef unsigned char QUtype;
+typedef __signed__ short int HItype;
+typedef unsigned short int HUtype;
+typedef __signed__ long int SItype;
+typedef unsigned long int SUtype;
+typedef __signed__ long long int DItype;
+typedef unsigned long long int DUtype;
+typedef float SFtype;
+typedef double DFtype;
+typedef long double XFtype;
+typedef char *Ptype;
+typedef int Ttype;
+
+
+typedef union stacktype
+{
+ QItype QIval;
+ QUtype QUval;
+ HItype HIval;
+ HUtype HUval;
+ SItype SIval;
+ SUtype SUval;
+ DItype DIval;
+ DUtype DUval;
+ SFtype SFval;
+ DFtype DFval;
+ XFtype XFval;
+ Ptype Pval;
+ Ttype Tval;
+} stacktype;
diff --git a/gnu/usr.bin/cc/include/c-gperf.h b/gnu/usr.bin/cc/include/c-gperf.h
new file mode 100644
index 0000000..edaaf22
--- /dev/null
+++ b/gnu/usr.bin/cc/include/c-gperf.h
@@ -0,0 +1,184 @@
+/* C code produced by gperf version 2.5 (GNU C++ version) */
+/* Command-line: gperf -p -j1 -i 1 -g -o -t -G -N is_reserved_word -k1,3,$ c-parse.gperf */
+struct resword { char *name; short token; enum rid rid; };
+
+#define TOTAL_KEYWORDS 79
+#define MIN_WORD_LENGTH 2
+#define MAX_WORD_LENGTH 20
+#define MIN_HASH_VALUE 10
+#define MAX_HASH_VALUE 144
+/* maximum key range = 135, duplicates = 0 */
+
+#ifdef __GNUC__
+__inline
+#endif
+static unsigned int
+hash (str, len)
+ register char *str;
+ register int unsigned len;
+{
+ static unsigned char asso_values[] =
+ {
+ 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 25, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 145, 145, 145, 145, 145,
+ 145, 145, 145, 145, 145, 1, 145, 46, 8, 15,
+ 61, 6, 36, 48, 3, 5, 145, 18, 63, 25,
+ 29, 76, 1, 145, 13, 2, 1, 51, 37, 9,
+ 9, 1, 3, 145, 145, 145, 145, 145,
+ };
+ register int hval = len;
+
+ switch (hval)
+ {
+ default:
+ case 3:
+ hval += asso_values[str[2]];
+ case 2:
+ case 1:
+ hval += asso_values[str[0]];
+ }
+ return hval + asso_values[str[len - 1]];
+}
+
+static struct resword wordlist[] =
+{
+ {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
+ {"",},
+ {"int", TYPESPEC, RID_INT},
+ {"",}, {"",},
+ {"__typeof__", TYPEOF, NORID},
+ {"__signed__", TYPESPEC, RID_SIGNED},
+ {"__imag__", IMAGPART, NORID},
+ {"switch", SWITCH, NORID},
+ {"__inline__", SCSPEC, RID_INLINE},
+ {"else", ELSE, NORID},
+ {"__iterator__", SCSPEC, RID_ITERATOR},
+ {"__inline", SCSPEC, RID_INLINE},
+ {"__extension__", EXTENSION, NORID},
+ {"struct", STRUCT, NORID},
+ {"__real__", REALPART, NORID},
+ {"__const", TYPE_QUAL, RID_CONST},
+ {"while", WHILE, NORID},
+ {"__const__", TYPE_QUAL, RID_CONST},
+ {"case", CASE, NORID},
+ {"__complex__", TYPESPEC, RID_COMPLEX},
+ {"__iterator", SCSPEC, RID_ITERATOR},
+ {"bycopy", TYPE_QUAL, RID_BYCOPY},
+ {"",}, {"",}, {"",},
+ {"__complex", TYPESPEC, RID_COMPLEX},
+ {"",},
+ {"in", TYPE_QUAL, RID_IN},
+ {"break", BREAK, NORID},
+ {"@defs", DEFS, NORID},
+ {"",}, {"",}, {"",},
+ {"extern", SCSPEC, RID_EXTERN},
+ {"if", IF, NORID},
+ {"typeof", TYPEOF, NORID},
+ {"typedef", SCSPEC, RID_TYPEDEF},
+ {"__typeof", TYPEOF, NORID},
+ {"sizeof", SIZEOF, NORID},
+ {"",},
+ {"return", RETURN, NORID},
+ {"const", TYPE_QUAL, RID_CONST},
+ {"__volatile__", TYPE_QUAL, RID_VOLATILE},
+ {"@private", PRIVATE, NORID},
+ {"@selector", SELECTOR, NORID},
+ {"__volatile", TYPE_QUAL, RID_VOLATILE},
+ {"__asm__", ASM_KEYWORD, NORID},
+ {"",}, {"",},
+ {"continue", CONTINUE, NORID},
+ {"__alignof__", ALIGNOF, NORID},
+ {"__imag", IMAGPART, NORID},
+ {"__attribute__", ATTRIBUTE, NORID},
+ {"",}, {"",},
+ {"__attribute", ATTRIBUTE, NORID},
+ {"for", FOR, NORID},
+ {"",},
+ {"@encode", ENCODE, NORID},
+ {"id", OBJECTNAME, RID_ID},
+ {"static", SCSPEC, RID_STATIC},
+ {"@interface", INTERFACE, NORID},
+ {"",},
+ {"__signed", TYPESPEC, RID_SIGNED},
+ {"",},
+ {"__label__", LABEL, NORID},
+ {"",}, {"",},
+ {"__asm", ASM_KEYWORD, NORID},
+ {"char", TYPESPEC, RID_CHAR},
+ {"",},
+ {"inline", SCSPEC, RID_INLINE},
+ {"out", TYPE_QUAL, RID_OUT},
+ {"register", SCSPEC, RID_REGISTER},
+ {"__real", REALPART, NORID},
+ {"short", TYPESPEC, RID_SHORT},
+ {"",},
+ {"enum", ENUM, NORID},
+ {"inout", TYPE_QUAL, RID_INOUT},
+ {"",},
+ {"oneway", TYPE_QUAL, RID_ONEWAY},
+ {"union", UNION, NORID},
+ {"",},
+ {"__alignof", ALIGNOF, NORID},
+ {"",},
+ {"@implementation", IMPLEMENTATION, NORID},
+ {"",},
+ {"@class", CLASS, NORID},
+ {"",},
+ {"@public", PUBLIC, NORID},
+ {"asm", ASM_KEYWORD, NORID},
+ {"",}, {"",}, {"",}, {"",}, {"",},
+ {"default", DEFAULT, NORID},
+ {"",},
+ {"void", TYPESPEC, RID_VOID},
+ {"",},
+ {"@protected", PROTECTED, NORID},
+ {"@protocol", PROTOCOL, NORID},
+ {"",}, {"",}, {"",},
+ {"volatile", TYPE_QUAL, RID_VOLATILE},
+ {"",}, {"",},
+ {"signed", TYPESPEC, RID_SIGNED},
+ {"float", TYPESPEC, RID_FLOAT},
+ {"@end", END, NORID},
+ {"",}, {"",},
+ {"unsigned", TYPESPEC, RID_UNSIGNED},
+ {"@compatibility_alias", ALIAS, NORID},
+ {"double", TYPESPEC, RID_DOUBLE},
+ {"",}, {"",},
+ {"auto", SCSPEC, RID_AUTO},
+ {"",},
+ {"goto", GOTO, NORID},
+ {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
+ {"do", DO, NORID},
+ {"",}, {"",}, {"",}, {"",},
+ {"long", TYPESPEC, RID_LONG},
+};
+
+#ifdef __GNUC__
+__inline
+#endif
+struct resword *
+is_reserved_word (str, len)
+ register char *str;
+ register unsigned int len;
+{
+ if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+ {
+ register int key = hash (str, len);
+
+ if (key <= MAX_HASH_VALUE && key >= 0)
+ {
+ register char *s = wordlist[key].name;
+
+ if (*s == *str && !strcmp (str + 1, s + 1))
+ return &wordlist[key];
+ }
+ }
+ return 0;
+}
diff --git a/gnu/usr.bin/cc/include/c-lex.h b/gnu/usr.bin/cc/include/c-lex.h
new file mode 100644
index 0000000..ae67d4c
--- /dev/null
+++ b/gnu/usr.bin/cc/include/c-lex.h
@@ -0,0 +1,79 @@
+/* Define constants for communication with c-parse.y.
+ Copyright (C) 1987, 1992 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+
+enum rid
+{
+ RID_UNUSED,
+ RID_INT,
+ RID_CHAR,
+ RID_FLOAT,
+ RID_DOUBLE,
+ RID_VOID,
+ RID_UNUSED1,
+
+ RID_UNSIGNED,
+ RID_SHORT,
+ RID_LONG,
+ RID_AUTO,
+ RID_STATIC,
+ RID_EXTERN,
+ RID_REGISTER,
+ RID_TYPEDEF,
+ RID_SIGNED,
+ RID_CONST,
+ RID_VOLATILE,
+ RID_INLINE,
+ RID_NOALIAS,
+ RID_ITERATOR,
+ RID_COMPLEX,
+
+ RID_IN,
+ RID_OUT,
+ RID_INOUT,
+ RID_BYCOPY,
+ RID_ONEWAY,
+ RID_ID,
+
+ RID_MAX
+};
+
+#define NORID RID_UNUSED
+
+#define RID_FIRST_MODIFIER RID_UNSIGNED
+
+/* The elements of `ridpointers' are identifier nodes
+ for the reserved type names and storage classes.
+ It is indexed by a RID_... value. */
+extern tree ridpointers[(int) RID_MAX];
+
+/* the declaration found for the last IDENTIFIER token read in.
+ yylex must look this up to detect typedefs, which get token type TYPENAME,
+ so it is left around in case the identifier is not a typedef but is
+ used in a context which makes it a reference to a variable. */
+extern tree lastiddecl;
+
+extern char *token_buffer; /* Pointer to token buffer. */
+
+extern tree make_pointer_declarator ();
+extern void reinit_parse_for_function ();
+extern int yylex ();
+
+extern char *get_directive_line ();
diff --git a/gnu/usr.bin/cc/include/c-parse.h b/gnu/usr.bin/cc/include/c-parse.h
new file mode 100644
index 0000000..dab903e
--- /dev/null
+++ b/gnu/usr.bin/cc/include/c-parse.h
@@ -0,0 +1,65 @@
+typedef union {long itype; tree ttype; enum tree_code code;
+ char *filename; int lineno; } YYSTYPE;
+#define IDENTIFIER 258
+#define TYPENAME 259
+#define SCSPEC 260
+#define TYPESPEC 261
+#define TYPE_QUAL 262
+#define CONSTANT 263
+#define STRING 264
+#define ELLIPSIS 265
+#define SIZEOF 266
+#define ENUM 267
+#define STRUCT 268
+#define UNION 269
+#define IF 270
+#define ELSE 271
+#define WHILE 272
+#define DO 273
+#define FOR 274
+#define SWITCH 275
+#define CASE 276
+#define DEFAULT 277
+#define BREAK 278
+#define CONTINUE 279
+#define RETURN 280
+#define GOTO 281
+#define ASM_KEYWORD 282
+#define TYPEOF 283
+#define ALIGNOF 284
+#define ALIGN 285
+#define ATTRIBUTE 286
+#define EXTENSION 287
+#define LABEL 288
+#define REALPART 289
+#define IMAGPART 290
+#define ASSIGN 291
+#define OROR 292
+#define ANDAND 293
+#define EQCOMPARE 294
+#define ARITHCOMPARE 295
+#define LSHIFT 296
+#define RSHIFT 297
+#define UNARY 298
+#define PLUSPLUS 299
+#define MINUSMINUS 300
+#define HYPERUNARY 301
+#define POINTSAT 302
+#define INTERFACE 303
+#define IMPLEMENTATION 304
+#define END 305
+#define SELECTOR 306
+#define DEFS 307
+#define ENCODE 308
+#define CLASSNAME 309
+#define PUBLIC 310
+#define PRIVATE 311
+#define PROTECTED 312
+#define PROTOCOL 313
+#define OBJECTNAME 314
+#define CLASS 315
+#define ALIAS 316
+#define OBJC_STRING 317
+
+
+extern YYSTYPE yylval;
diff --git a/gnu/usr.bin/cc/include/c-tree.h b/gnu/usr.bin/cc/include/c-tree.h
new file mode 100644
index 0000000..2300351
--- /dev/null
+++ b/gnu/usr.bin/cc/include/c-tree.h
@@ -0,0 +1,483 @@
+/* Definitions for C parsing and type checking.
+ Copyright (C) 1987, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _C_TREE_H
+#define _C_TREE_H
+
+/* Language-dependent contents of an identifier. */
+
+/* The limbo_value is used for block level extern declarations, which need
+ to be type checked against subsequent extern declarations. They can't
+ be referenced after they fall out of scope, so they can't be global. */
+
+struct lang_identifier
+{
+ struct tree_identifier ignore;
+ tree global_value, local_value, label_value, implicit_decl;
+ tree error_locus, limbo_value;
+};
+
+/* Macros for access to language-specific slots in an identifier. */
+/* Each of these slots contains a DECL node or null. */
+
+/* This represents the value which the identifier has in the
+ file-scope namespace. */
+#define IDENTIFIER_GLOBAL_VALUE(NODE) \
+ (((struct lang_identifier *)(NODE))->global_value)
+/* This represents the value which the identifier has in the current
+ scope. */
+#define IDENTIFIER_LOCAL_VALUE(NODE) \
+ (((struct lang_identifier *)(NODE))->local_value)
+/* This represents the value which the identifier has as a label in
+ the current label scope. */
+#define IDENTIFIER_LABEL_VALUE(NODE) \
+ (((struct lang_identifier *)(NODE))->label_value)
+/* This records the extern decl of this identifier, if it has had one
+ at any point in this compilation. */
+#define IDENTIFIER_LIMBO_VALUE(NODE) \
+ (((struct lang_identifier *)(NODE))->limbo_value)
+/* This records the implicit function decl of this identifier, if it
+ has had one at any point in this compilation. */
+#define IDENTIFIER_IMPLICIT_DECL(NODE) \
+ (((struct lang_identifier *)(NODE))->implicit_decl)
+/* This is the last function in which we printed an "undefined variable"
+ message for this identifier. Value is a FUNCTION_DECL or null. */
+#define IDENTIFIER_ERROR_LOCUS(NODE) \
+ (((struct lang_identifier *)(NODE))->error_locus)
+
+/* In identifiers, C uses the following fields in a special way:
+ TREE_PUBLIC to record that there was a previous local extern decl.
+ TREE_USED to record that such a decl was used.
+ TREE_ADDRESSABLE to record that the address of such a decl was used. */
+
+/* Nonzero means reject anything that ANSI standard C forbids. */
+extern int pedantic;
+
+/* In a RECORD_TYPE or UNION_TYPE, nonzero if any component is read-only. */
+#define C_TYPE_FIELDS_READONLY(type) TREE_LANG_FLAG_1 (type)
+
+/* In a RECORD_TYPE or UNION_TYPE, nonzero if any component is volatile. */
+#define C_TYPE_FIELDS_VOLATILE(type) TREE_LANG_FLAG_2 (type)
+
+/* In a RECORD_TYPE or UNION_TYPE or ENUMERAL_TYPE
+ nonzero if the definition of the type has already started. */
+#define C_TYPE_BEING_DEFINED(type) TYPE_LANG_FLAG_0 (type)
+
+/* In a RECORD_TYPE, a sorted array of the fields of the type. */
+struct lang_type
+{
+ int len;
+ tree elts[1];
+};
+
+/* Mark which labels are explicitly declared.
+ These may be shadowed, and may be referenced from nested functions. */
+#define C_DECLARED_LABEL_FLAG(label) TREE_LANG_FLAG_1 (label)
+
+/* Record whether a type or decl was written with nonconstant size.
+ Note that TYPE_SIZE may have simplified to a constant. */
+#define C_TYPE_VARIABLE_SIZE(type) TYPE_LANG_FLAG_1 (type)
+#define C_DECL_VARIABLE_SIZE(type) DECL_LANG_FLAG_0 (type)
+
+/* Record in each node resulting from a binary operator
+ what operator was specified for it. */
+#define C_EXP_ORIGINAL_CODE(exp) ((enum tree_code) TREE_COMPLEXITY (exp))
+
+#if 0 /* Not used. */
+/* Record whether a decl for a function or function pointer has
+ already been mentioned (in a warning) because it was called
+ but didn't have a prototype. */
+#define C_MISSING_PROTOTYPE_WARNED(decl) DECL_LANG_FLAG_2(decl)
+#endif
+
+/* Store a value in that field. */
+#define C_SET_EXP_ORIGINAL_CODE(exp, code) \
+ (TREE_COMPLEXITY (exp) = (int)(code))
+
+/* Record whether a typedef for type `int' was actually `signed int'. */
+#define C_TYPEDEF_EXPLICITLY_SIGNED(exp) DECL_LANG_FLAG_1 ((exp))
+
+/* Nonzero for a declaration of a built in function if there has been no
+ occasion that would declare the function in ordinary C.
+ Using the function draws a pedantic warning in this case. */
+#define C_DECL_ANTICIPATED(exp) DECL_LANG_FLAG_3 ((exp))
+
+/* For FUNCTION_TYPE, a hidden list of types of arguments. The same as
+ TYPE_ARG_TYPES for functions with prototypes, but created for functions
+ without prototypes. */
+#define TYPE_ACTUAL_ARG_TYPES(NODE) TYPE_NONCOPIED_PARTS (NODE)
+
+/* Nonzero if the type T promotes to itself.
+ ANSI C states explicitly the list of types that promote;
+ in particular, short promotes to int even if they have the same width. */
+#define C_PROMOTING_INTEGER_TYPE_P(t) \
+ (TREE_CODE ((t)) == INTEGER_TYPE \
+ && (TYPE_MAIN_VARIANT (t) == char_type_node \
+ || TYPE_MAIN_VARIANT (t) == signed_char_type_node \
+ || TYPE_MAIN_VARIANT (t) == unsigned_char_type_node \
+ || TYPE_MAIN_VARIANT (t) == short_integer_type_node \
+ || TYPE_MAIN_VARIANT (t) == short_unsigned_type_node))
+
+/* In a VAR_DECL, means the variable is really an iterator. */
+#define ITERATOR_P(D) (DECL_LANG_FLAG_4(D))
+
+/* In a VAR_DECL for an iterator, means we are within
+ an explicit loop over that iterator. */
+#define ITERATOR_BOUND_P(NODE) ((NODE)->common.readonly_flag)
+
+/* in c-lang.c and objc-act.c */
+extern tree lookup_interface PROTO((tree));
+extern tree is_class_name PROTO((tree));
+extern void maybe_objc_check_decl PROTO((tree));
+extern int maybe_objc_comptypes PROTO((tree, tree, int));
+extern tree maybe_building_objc_message_expr PROTO((void));
+extern tree maybe_objc_method_name PROTO((tree));
+extern int recognize_objc_keyword PROTO((void));
+extern tree build_objc_string PROTO((int, char *));
+
+/* in c-aux-info.c */
+extern void gen_aux_info_record PROTO((tree, int, int, int));
+
+/* in c-common.c */
+extern void declare_function_name PROTO((void));
+extern void decl_attributes PROTO((tree, tree));
+extern void init_function_format_info PROTO((void));
+extern void record_function_format PROTO((tree, tree, int, int, int));
+extern void check_function_format PROTO((tree, tree, tree));
+/* Print an error message for invalid operands to arith operation CODE.
+ NOP_EXPR is used as a special case (see truthvalue_conversion). */
+extern void binary_op_error PROTO((enum tree_code));
+extern void c_expand_expr_stmt PROTO((tree));
+/* Validate the expression after `case' and apply default promotions. */
+extern tree check_case_value PROTO((tree));
+/* Concatenate a list of STRING_CST nodes into one STRING_CST. */
+extern tree combine_strings PROTO((tree));
+extern void constant_expression_warning PROTO((tree));
+extern tree convert_and_check PROTO((tree, tree));
+extern void overflow_warning PROTO((tree));
+extern void unsigned_conversion_warning PROTO((tree, tree));
+/* Read the rest of the current #-directive line. */
+extern char *get_directive_line STDIO_PROTO((FILE *));
+/* Subroutine of build_binary_op, used for comparison operations.
+ See if the operands have both been converted from subword integer types
+ and, if so, perhaps change them both back to their original type. */
+extern tree shorten_compare PROTO((tree *, tree *, tree *, enum tree_code *));
+/* Prepare expr to be an argument of a TRUTH_NOT_EXPR,
+ or validate its data type for an `if' or `while' statement or ?..: exp. */
+extern tree truthvalue_conversion PROTO((tree));
+extern tree type_for_mode PROTO((enum machine_mode, int));
+extern tree type_for_size PROTO((unsigned, int));
+
+/* in c-convert.c */
+extern tree convert PROTO((tree, tree));
+
+/* in c-decl.c */
+/* Standard named or nameless data types of the C compiler. */
+extern tree char_array_type_node;
+extern tree char_type_node;
+extern tree const_ptr_type_node;
+extern tree const_string_type_node;
+extern tree default_function_type;
+extern tree double_ftype_double;
+extern tree double_ftype_double_double;
+extern tree double_type_node;
+extern tree float_type_node;
+extern tree intDI_type_node;
+extern tree intHI_type_node;
+extern tree intQI_type_node;
+extern tree intSI_type_node;
+extern tree int_array_type_node;
+extern tree int_ftype_cptr_cptr_sizet;
+extern tree int_ftype_int;
+extern tree int_ftype_ptr_ptr_int;
+extern tree int_ftype_string_string;
+extern tree integer_type_node;
+extern tree long_double_type_node;
+extern tree long_ftype_long;
+extern tree long_integer_type_node;
+extern tree long_long_integer_type_node;
+extern tree long_long_unsigned_type_node;
+extern tree long_unsigned_type_node;
+extern tree complex_integer_type_node;
+extern tree complex_float_type_node;
+extern tree complex_double_type_node;
+extern tree complex_long_double_type_node;
+extern tree ptr_type_node;
+extern tree ptrdiff_type_node;
+extern tree short_integer_type_node;
+extern tree short_unsigned_type_node;
+extern tree signed_char_type_node;
+extern tree signed_wchar_type_node;
+extern tree string_ftype_ptr_ptr;
+extern tree string_type_node;
+extern tree unsigned_char_type_node;
+extern tree unsigned_intDI_type_node;
+extern tree unsigned_intHI_type_node;
+extern tree unsigned_intQI_type_node;
+extern tree unsigned_intSI_type_node;
+extern tree unsigned_type_node;
+extern tree unsigned_wchar_type_node;
+extern tree void_ftype_ptr_int_int;
+extern tree void_ftype_ptr_ptr_int;
+extern tree void_type_node;
+extern tree wchar_array_type_node;
+extern tree wchar_type_node;
+
+extern tree build_enumerator PROTO((tree, tree));
+/* Declare a predefined function. Return the declaration. */
+extern tree builtin_function PROTO((char *, tree, enum built_in_function function_, char *));
+/* Add qualifiers to a type, in the fashion for C. */
+extern tree c_build_type_variant PROTO((tree, int, int));
+extern int c_decode_option PROTO((char *));
+extern void c_mark_varargs PROTO((void));
+extern tree check_identifier PROTO((tree, tree));
+extern void clear_parm_order PROTO((void));
+extern tree combine_parm_decls PROTO((tree, tree, int));
+extern int complete_array_type PROTO((tree, tree, int));
+extern void declare_parm_level PROTO((int));
+extern tree define_label PROTO((char *, int, tree));
+extern void delete_block PROTO((tree));
+extern void finish_decl PROTO((tree, tree, tree));
+extern tree finish_enum PROTO((tree, tree));
+extern void finish_function PROTO((int));
+extern tree finish_struct PROTO((tree, tree));
+extern tree get_parm_info PROTO((int));
+extern tree getdecls PROTO((void));
+extern tree gettags PROTO((void));
+extern int global_bindings_p PROTO((void));
+extern tree grokfield PROTO((char *, int, tree, tree, tree));
+extern tree groktypename PROTO((tree));
+extern tree groktypename_in_parm_context PROTO((tree));
+extern tree implicitly_declare PROTO((tree));
+extern int in_parm_level_p PROTO((void));
+extern void init_decl_processing PROTO((void));
+extern void insert_block PROTO((tree));
+extern void keep_next_level PROTO((void));
+extern int kept_level_p PROTO((void));
+extern tree lookup_label PROTO((tree));
+extern tree lookup_name PROTO((tree));
+extern tree lookup_name_current_level PROTO((tree));
+extern tree lookup_name_current_level_global PROTO((tree));
+extern tree maybe_build_cleanup PROTO((tree));
+extern void parmlist_tags_warning PROTO((void));
+extern void pending_xref_error PROTO((void));
+extern void pop_c_function_context PROTO((void));
+extern void pop_label_level PROTO((void));
+extern tree poplevel PROTO((int, int, int));
+extern void print_lang_decl STDIO_PROTO((FILE *, tree,
+ int));
+extern void print_lang_identifier STDIO_PROTO((FILE *, tree,
+ int));
+extern void print_lang_type STDIO_PROTO((FILE *, tree,
+ int));
+extern void push_c_function_context PROTO((void));
+extern void push_label_level PROTO((void));
+extern void push_parm_decl PROTO((tree));
+extern tree pushdecl PROTO((tree));
+extern tree pushdecl_top_level PROTO((tree));
+extern void pushlevel PROTO((int));
+extern void pushtag PROTO((tree, tree));
+extern void set_block PROTO((tree));
+extern tree shadow_label PROTO((tree));
+extern void shadow_record_fields PROTO((tree));
+extern void shadow_tag PROTO((tree));
+extern void shadow_tag_warned PROTO((tree, int));
+extern tree start_enum PROTO((tree));
+extern int start_function PROTO((tree, tree, int));
+extern tree start_decl PROTO((tree, tree, int));
+extern tree start_struct PROTO((enum tree_code, tree));
+extern void store_parm_decls PROTO((void));
+extern tree xref_tag PROTO((enum tree_code, tree));
+
+/* in c-typeck.c */
+extern tree require_complete_type PROTO((tree));
+extern void incomplete_type_error PROTO((tree, tree));
+/* Given two integer or real types, return the type for their sum.
+ Given two compatible ANSI C types, returns the merged type. */
+extern tree common_type PROTO((tree, tree));
+extern int comptypes PROTO((tree, tree));
+extern int self_promoting_args_p PROTO((tree));
+extern tree c_sizeof PROTO((tree));
+extern tree c_sizeof_nowarn PROTO((tree));
+extern tree c_size_in_bytes PROTO((tree));
+extern tree c_alignof PROTO((tree));
+extern tree c_alignof_expr PROTO((tree));
+extern tree default_conversion PROTO((tree));
+extern tree build_component_ref PROTO((tree, tree));
+extern tree build_indirect_ref PROTO((tree, char *));
+extern tree build_array_ref PROTO((tree, tree));
+extern tree build_function_call PROTO((tree, tree));
+extern tree parser_build_binary_op PROTO((enum tree_code,
+ tree, tree));
+extern tree build_binary_op PROTO((enum tree_code,
+ tree, tree, int));
+extern tree build_unary_op PROTO((enum tree_code,
+ tree, int));
+extern int lvalue_p PROTO((tree));
+extern int lvalue_or_else PROTO((tree, char *));
+extern void readonly_warning PROTO((tree, char *));
+extern int mark_addressable PROTO((tree));
+extern tree build_conditional_expr PROTO((tree, tree, tree));
+extern tree build_compound_expr PROTO((tree));
+extern tree build_c_cast PROTO((tree, tree));
+extern tree build_modify_expr PROTO((tree, enum tree_code,
+ tree));
+extern tree initializer_constant_valid_p PROTO((tree, tree));
+extern void store_init_value PROTO((tree, tree));
+extern void error_init PROTO((char *, char *,
+ char *));
+extern void pedwarn_init PROTO((char *, char *,
+ char *));
+extern void start_init PROTO((tree, tree, int));
+extern void finish_init PROTO((void));
+extern void really_start_incremental_init PROTO((tree));
+extern void push_init_level PROTO((int));
+extern tree pop_init_level PROTO((int));
+extern void set_init_index PROTO((tree, tree));
+extern void set_init_label PROTO((tree));
+extern void process_init_element PROTO((tree));
+extern void c_expand_asm_operands PROTO((tree, tree, tree, tree,
+ int, char *, int));
+extern void c_expand_return PROTO((tree));
+extern tree c_expand_start_case PROTO((tree));
+
+/* in c-iterate.c */
+extern void iterator_expand PROTO((tree));
+extern void iterator_for_loop_start PROTO((tree));
+extern void iterator_for_loop_end PROTO((tree));
+extern void iterator_for_loop_record PROTO((tree));
+extern void push_iterator_stack PROTO((void));
+extern void pop_iterator_stack PROTO((void));
+
+/* Set to 0 at beginning of a function definition, set to 1 if
+ a return statement that specifies a return value is seen. */
+
+extern int current_function_returns_value;
+
+/* Set to 0 at beginning of a function definition, set to 1 if
+ a return statement with no argument is seen. */
+
+extern int current_function_returns_null;
+
+/* Nonzero means `$' can be in an identifier. */
+
+extern int dollars_in_ident;
+
+/* Nonzero means allow type mismatches in conditional expressions;
+ just make their values `void'. */
+
+extern int flag_cond_mismatch;
+
+/* Nonzero means don't recognize the keyword `asm'. */
+
+extern int flag_no_asm;
+
+/* Nonzero means ignore `#ident' directives. */
+
+extern int flag_no_ident;
+
+/* Nonzero means warn about implicit declarations. */
+
+extern int warn_implicit;
+
+/* Nonzero means give string constants the type `const char *'
+ to get extra warnings from them. These warnings will be too numerous
+ to be useful, except in thoroughly ANSIfied programs. */
+
+extern int warn_write_strings;
+
+/* Nonzero means warn about sizeof (function) or addition/subtraction
+ of function pointers. */
+
+extern int warn_pointer_arith;
+
+/* Nonzero means warn for all old-style non-prototype function decls. */
+
+extern int warn_strict_prototypes;
+
+/* Nonzero means warn about multiple (redundant) decls for the same single
+ variable or function. */
+
+extern int warn_redundant_decls;
+
+/* Nonzero means warn about extern declarations of objects not at
+ file-scope level and about *all* declarations of functions (whether
+ extern or static) not at file-scope level. Note that we exclude
+ implicit function declarations. To get warnings about those, use
+ -Wimplicit. */
+
+extern int warn_nested_externs;
+
+/* Nonzero means warn about pointer casts that can drop a type qualifier
+ from the pointer target type. */
+
+extern int warn_cast_qual;
+
+/* Nonzero means warn when casting a function call to a type that does
+ not match the return type (e.g. (float)sqrt() or (anything*)malloc()
+ when there is no previous declaration of sqrt or malloc. */
+
+extern int warn_bad_function_cast;
+
+/* Warn about traditional constructs whose meanings changed in ANSI C. */
+
+extern int warn_traditional;
+
+/* Warn about *printf or *scanf format/argument anomalies. */
+
+extern int warn_format;
+
+/* Warn about a subscript that has type char. */
+
+extern int warn_char_subscripts;
+
+/* Warn if a type conversion is done that might have confusing results. */
+
+extern int warn_conversion;
+
+/* Nonzero means do some things the same way PCC does. */
+
+extern int flag_traditional;
+
+/* Nonzero means to allow single precision math even if we're generally
+ being traditional. */
+extern int flag_allow_single_precision;
+
+/* Nonzero means warn about suggesting putting in ()'s. */
+
+extern int warn_parentheses;
+
+/* Warn if initializer is not completely bracketed. */
+
+extern int warn_missing_braces;
+
+/* Nonzero means this is a function to call to perform comptypes
+ on two record types. */
+
+extern int (*comptypes_record_hook) ();
+
+/* Nonzero means we are reading code that came from a system header file. */
+
+extern int system_header_p;
+
+/* Nonzero enables objc features. */
+
+extern int doing_objc_thang;
+
+#endif /* not _C_TREE_H */
diff --git a/gnu/usr.bin/cc/include/conditions.h b/gnu/usr.bin/cc/include/conditions.h
new file mode 100644
index 0000000..e7319377
--- /dev/null
+++ b/gnu/usr.bin/cc/include/conditions.h
@@ -0,0 +1,115 @@
+/* Definitions for condition code handling in final.c and output routines.
+ Copyright (C) 1987 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* None of the things in the files exist if we don't use CC0. */
+
+#ifdef HAVE_cc0
+
+/* The variable cc_status says how to interpret the condition code.
+ It is set by output routines for an instruction that sets the cc's
+ and examined by output routines for jump instructions.
+
+ cc_status contains two components named `value1' and `value2'
+ that record two equivalent expressions for the values that the
+ condition codes were set from. (Either or both may be null if
+ there is no useful expression to record.) These fields are
+ used for eliminating redundant test and compare instructions
+ in the cases where the condition codes were already set by the
+ previous instruction.
+
+ cc_status.flags contains flags which say that the condition codes
+ were set in a nonstandard manner. The output of jump instructions
+ uses these flags to compensate and produce the standard result
+ with the nonstandard condition codes. Standard flags are defined here.
+ The tm.h file can also define other machine-dependent flags.
+
+ cc_status also contains a machine-dependent component `mdep'
+ whose type, `CC_STATUS_MDEP', may be defined as a macro in the
+ tm.h file. */
+
+#ifndef CC_STATUS_MDEP
+#define CC_STATUS_MDEP int
+#endif
+
+#ifndef CC_STATUS_MDEP_INIT
+#define CC_STATUS_MDEP_INIT 0
+#endif
+
+typedef struct {int flags; rtx value1, value2; CC_STATUS_MDEP mdep;} CC_STATUS;
+
+/* While outputting an insn as assembler code,
+ this is the status BEFORE that insn. */
+extern CC_STATUS cc_prev_status;
+
+/* While outputting an insn as assembler code,
+ this is being altered to the status AFTER that insn. */
+extern CC_STATUS cc_status;
+
+/* These are the machine-independent flags: */
+
+/* Set if the sign of the cc value is inverted:
+ output a following jump-if-less as a jump-if-greater, etc. */
+#define CC_REVERSED 1
+
+/* This bit means that the current setting of the N bit is bogus
+ and conditional jumps should use the Z bit in its place.
+ This state obtains when an extraction of a signed single-bit field
+ or an arithmetic shift right of a byte by 7 bits
+ is turned into a btst, because btst does not set the N bit. */
+#define CC_NOT_POSITIVE 2
+
+/* This bit means that the current setting of the N bit is bogus
+ and conditional jumps should pretend that the N bit is clear.
+ Used after extraction of an unsigned bit
+ or logical shift right of a byte by 7 bits is turned into a btst.
+ The btst does not alter the N bit, but the result of that shift
+ or extract is never negative. */
+#define CC_NOT_NEGATIVE 4
+
+/* This bit means that the current setting of the overflow flag
+ is bogus and conditional jumps should pretend there is no overflow. */
+#define CC_NO_OVERFLOW 010
+
+/* This bit means that what ought to be in the Z bit
+ should be tested as the complement of the N bit. */
+#define CC_Z_IN_NOT_N 020
+
+/* This bit means that what ought to be in the Z bit
+ should be tested as the N bit. */
+#define CC_Z_IN_N 040
+
+/* Nonzero if we must invert the sense of the following branch, i.e.
+ change EQ to NE. This is not safe for IEEE floating point operations!
+ It is intended for use only when a combination of arithmetic
+ or logical insns can leave the condition codes set in a fortuitous
+ (though inverted) state. */
+#define CC_INVERTED 0100
+
+/* Nonzero if we must convert signed condition operators to unsigned.
+ This is only used by machine description files. */
+#define CC_NOT_SIGNED 0200
+
+/* This is how to initialize the variable cc_status.
+ final does this at appropriate moments. */
+
+#define CC_STATUS_INIT \
+ (cc_status.flags = 0, cc_status.value1 = 0, cc_status.value2 = 0, \
+ CC_STATUS_MDEP_INIT)
+
+#endif
diff --git a/gnu/usr.bin/cc/include/config.h b/gnu/usr.bin/cc/include/config.h
new file mode 100644
index 0000000..7886724
--- /dev/null
+++ b/gnu/usr.bin/cc/include/config.h
@@ -0,0 +1,42 @@
+/* Configuration for GNU C-compiler for Intel 80386.
+ Copyright (C) 1988, 1993 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef i386
+#define i386
+#endif
+
+/* #defines that need visibility everywhere. */
+#define FALSE 0
+#define TRUE 1
+
+/* This describes the machine the compiler is hosted on. */
+#define HOST_BITS_PER_CHAR 8
+#define HOST_BITS_PER_SHORT 16
+#define HOST_BITS_PER_INT 32
+#define HOST_BITS_PER_LONG 32
+#define HOST_BITS_PER_LONGLONG 64
+
+/* Arguments to use with `exit'. */
+#define SUCCESS_EXIT_CODE 0
+#define FATAL_EXIT_CODE 33
+
+/* target machine dependencies.
+ tm.h is a symbolic link to the actual target specific file. */
+
+#include "tm.h"
diff --git a/gnu/usr.bin/cc/include/convert.h b/gnu/usr.bin/cc/include/convert.h
new file mode 100644
index 0000000..b2c8c79
--- /dev/null
+++ b/gnu/usr.bin/cc/include/convert.h
@@ -0,0 +1,23 @@
+/* Definition of functions in convert.c.
+ Copyright (C) 1993 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+extern tree convert_to_integer PROTO ((tree, tree));
+extern tree convert_to_pointer PROTO ((tree, tree));
+extern tree convert_to_real PROTO ((tree, tree));
+extern tree convert_to_complex PROTO ((tree, tree));
diff --git a/gnu/usr.bin/cc/include/defaults.h b/gnu/usr.bin/cc/include/defaults.h
new file mode 100644
index 0000000..df5ce1c
--- /dev/null
+++ b/gnu/usr.bin/cc/include/defaults.h
@@ -0,0 +1,133 @@
+/* Definitions of various defaults for how to do assembler output
+ (most of which are designed to be appropriate for GAS or for
+ some BSD assembler).
+
+ Written by Ron Guilmette (rfg@netcom.com)
+
+Copyright (C) 1992 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Store in OUTPUT a string (made with alloca) containing
+ an assembler-name for a local static variable or function named NAME.
+ LABELNO is an integer which is different for each call. */
+
+#ifndef ASM_FORMAT_PRIVATE_NAME
+#define ASM_FORMAT_PRIVATE_NAME(OUTPUT, NAME, LABELNO) \
+ do { \
+ int len = strlen (NAME); \
+ char *temp = (char *) alloca (len + 3); \
+ temp[0] = 'L'; \
+ strcpy (&temp[1], (NAME)); \
+ temp[len + 1] = '.'; \
+ temp[len + 2] = 0; \
+ (OUTPUT) = (char *) alloca (strlen (NAME) + 11); \
+ ASM_GENERATE_INTERNAL_LABEL (OUTPUT, temp, LABELNO); \
+ } while (0)
+#endif
+
+#ifndef ASM_STABD_OP
+#define ASM_STABD_OP ".stabd"
+#endif
+
+/* This is how to output an element of a case-vector that is absolute.
+ Some targets don't use this, but we have to define it anyway. */
+
+#ifndef ASM_OUTPUT_ADDR_VEC_ELT
+#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \
+do { fprintf (FILE, "\t%s\t", ASM_LONG); \
+ ASM_OUTPUT_INTERNAL_LABEL (FILE, "L", (VALUE)); \
+ fputc ('\n', FILE); \
+ } while (0)
+#endif
+
+/* This is how to output an element of a case-vector that is relative.
+ Some targets don't use this, but we have to define it anyway. */
+
+#ifndef ASM_OUTPUT_ADDR_DIFF_ELT
+#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, VALUE, REL) \
+do { fprintf (FILE, "\t%s\t", ASM_SHORT); \
+ ASM_OUTPUT_INTERNAL_LABEL (FILE, "L", (VALUE)); \
+ fputc ('-', FILE); \
+ ASM_OUTPUT_INTERNAL_LABEL (FILE, "L", (REL)); \
+ fputc ('\n', FILE); \
+ } while (0)
+#endif
+
+/* choose a reasonable default for ASM_OUTPUT_ASCII. */
+
+#ifndef ASM_OUTPUT_ASCII
+#define ASM_OUTPUT_ASCII(MYFILE, MYSTRING, MYLENGTH) \
+ do { \
+ FILE *_hide_asm_out_file = (MYFILE); \
+ unsigned char *_hide_p = (unsigned char *) (MYSTRING); \
+ int _hide_thissize = (MYLENGTH); \
+ { \
+ FILE *asm_out_file = _hide_asm_out_file; \
+ unsigned char *p = _hide_p; \
+ int thissize = _hide_thissize; \
+ int i; \
+ fprintf (asm_out_file, "\t.ascii \""); \
+ \
+ for (i = 0; i < thissize; i++) \
+ { \
+ register int c = p[i]; \
+ if (c == '\"' || c == '\\') \
+ putc ('\\', asm_out_file); \
+ if (c >= ' ' && c < 0177) \
+ putc (c, asm_out_file); \
+ else \
+ { \
+ fprintf (asm_out_file, "\\%o", c); \
+ /* After an octal-escape, if a digit follows, \
+ terminate one string constant and start another. \
+ The Vax assembler fails to stop reading the escape \
+ after three digits, so this is the only way we \
+ can get it to parse the data properly. */ \
+ if (i < thissize - 1 \
+ && p[i + 1] >= '0' && p[i + 1] <= '9') \
+ fprintf (asm_out_file, "\"\n\t.ascii \""); \
+ } \
+ } \
+ fprintf (asm_out_file, "\"\n"); \
+ } \
+ } \
+ while (0)
+#endif
+
+#ifndef ASM_IDENTIFY_GCC
+ /* Default the definition, only if ASM_IDENTIFY_GCC is not set,
+ because if it is set, we might not want ASM_IDENTIFY_LANGUAGE
+ outputting labels, if we do want it to, then it must be defined
+ in the tm.h file. */
+#ifndef ASM_IDENTIFY_LANGUAGE
+#define ASM_IDENTIFY_LANGUAGE(FILE) output_lang_identify (FILE);
+#endif
+#endif
+
+/* This is how we tell the assembler to equate two values. */
+#ifdef SET_ASM_OP
+#ifndef ASM_OUTPUT_DEF
+#define ASM_OUTPUT_DEF(FILE,LABEL1,LABEL2) \
+ do { fprintf ((FILE), "\t%s\t", SET_ASM_OP); \
+ assemble_name (FILE, LABEL1); \
+ fprintf (FILE, ","); \
+ assemble_name (FILE, LABEL2); \
+ fprintf (FILE, "\n"); \
+ } while (0)
+#endif
+#endif
diff --git a/gnu/usr.bin/cc/include/expr.h b/gnu/usr.bin/cc/include/expr.h
new file mode 100644
index 0000000..3bb9490
--- /dev/null
+++ b/gnu/usr.bin/cc/include/expr.h
@@ -0,0 +1,834 @@
+/* Definitions for code generation pass of GNU compiler.
+ Copyright (C) 1987, 1991, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#ifndef __STDC__
+#ifndef const
+#define const
+#endif
+#endif
+
+/* The default branch cost is 1. */
+#ifndef BRANCH_COST
+#define BRANCH_COST 1
+#endif
+
+/* Macros to access the slots of a QUEUED rtx.
+ Here rather than in rtl.h because only the expansion pass
+ should ever encounter a QUEUED. */
+
+/* The variable for which an increment is queued. */
+#define QUEUED_VAR(P) XEXP (P, 0)
+/* If the increment has been emitted, this is the insn
+ that does the increment. It is zero before the increment is emitted. */
+#define QUEUED_INSN(P) XEXP (P, 1)
+/* If a pre-increment copy has been generated, this is the copy
+ (it is a temporary reg). Zero if no copy made yet. */
+#define QUEUED_COPY(P) XEXP (P, 2)
+/* This is the body to use for the insn to do the increment.
+ It is used to emit the increment. */
+#define QUEUED_BODY(P) XEXP (P, 3)
+/* Next QUEUED in the queue. */
+#define QUEUED_NEXT(P) XEXP (P, 4)
+
+/* This is the 4th arg to `expand_expr'.
+ EXPAND_SUM means it is ok to return a PLUS rtx or MULT rtx.
+ EXPAND_INITIALIZER is similar but also record any labels on forced_labels.
+ EXPAND_CONST_ADDRESS means it is ok to return a MEM whose address
+ is a constant that is not a legitimate address. */
+enum expand_modifier {EXPAND_NORMAL, EXPAND_SUM,
+ EXPAND_CONST_ADDRESS, EXPAND_INITIALIZER};
+
+/* List of labels that must never be deleted. */
+extern rtx forced_labels;
+
+/* List (chain of EXPR_LISTs) of pseudo-regs of SAVE_EXPRs.
+ So we can mark them all live at the end of the function, if stupid. */
+extern rtx save_expr_regs;
+
+extern int current_function_calls_alloca;
+extern int current_function_outgoing_args_size;
+
+/* This is the offset from the arg pointer to the place where the first
+ anonymous arg can be found, if there is one. */
+extern rtx current_function_arg_offset_rtx;
+
+/* This is nonzero if the current function uses the constant pool. */
+extern int current_function_uses_const_pool;
+
+/* This is nonzero if the current function uses pic_offset_table_rtx. */
+extern int current_function_uses_pic_offset_table;
+
+/* The arg pointer hard register, or the pseudo into which it was copied. */
+extern rtx current_function_internal_arg_pointer;
+
+/* Nonzero means stack pops must not be deferred, and deferred stack
+ pops must not be output. It is nonzero inside a function call,
+ inside a conditional expression, inside a statement expression,
+ and in other cases as well. */
+extern int inhibit_defer_pop;
+
+/* Number of function calls seen so far in current function. */
+
+extern int function_call_count;
+
+/* RTX for stack slot that holds the current handler for nonlocal gotos.
+ Zero when function does not have nonlocal labels. */
+
+extern rtx nonlocal_goto_handler_slot;
+
+/* RTX for stack slot that holds the stack pointer value to restore
+ for a nonlocal goto.
+ Zero when function does not have nonlocal labels. */
+
+extern rtx nonlocal_goto_stack_level;
+
+/* List (chain of TREE_LIST) of LABEL_DECLs for all nonlocal labels
+ (labels to which there can be nonlocal gotos from nested functions)
+ in this function. */
+
+#ifdef TREE_CODE /* Don't lose if tree.h not included. */
+extern tree nonlocal_labels;
+#endif
+
+#define NO_DEFER_POP (inhibit_defer_pop += 1)
+#define OK_DEFER_POP (inhibit_defer_pop -= 1)
+
+/* Number of units that we should eventually pop off the stack.
+ These are the arguments to function calls that have already returned. */
+extern int pending_stack_adjust;
+
+/* A list of all cleanups which belong to the arguments of
+ function calls being expanded by expand_call. */
+#ifdef TREE_CODE /* Don't lose if tree.h not included. */
+extern tree cleanups_this_call;
+#endif
+
+/* When temporaries are created by TARGET_EXPRs, they are created at
+ this level of temp_slot_level, so that they can remain allocated
+ until no longer needed. CLEANUP_POINT_EXPRs define the lifetime
+ of TARGET_EXPRs. */
+extern int target_temp_slot_level;
+
+#ifdef TREE_CODE /* Don't lose if tree.h not included. */
+/* Structure to record the size of a sequence of arguments
+ as the sum of a tree-expression and a constant. */
+
+struct args_size
+{
+ int constant;
+ tree var;
+};
+#endif
+
+/* Add the value of the tree INC to the `struct args_size' TO. */
+
+#define ADD_PARM_SIZE(TO, INC) \
+{ tree inc = (INC); \
+ if (TREE_CODE (inc) == INTEGER_CST) \
+ (TO).constant += TREE_INT_CST_LOW (inc); \
+ else if ((TO).var == 0) \
+ (TO).var = inc; \
+ else \
+ (TO).var = size_binop (PLUS_EXPR, (TO).var, inc); }
+
+#define SUB_PARM_SIZE(TO, DEC) \
+{ tree dec = (DEC); \
+ if (TREE_CODE (dec) == INTEGER_CST) \
+ (TO).constant -= TREE_INT_CST_LOW (dec); \
+ else if ((TO).var == 0) \
+ (TO).var = size_binop (MINUS_EXPR, integer_zero_node, dec); \
+ else \
+ (TO).var = size_binop (MINUS_EXPR, (TO).var, dec); }
+
+/* Convert the implicit sum in a `struct args_size' into an rtx. */
+#define ARGS_SIZE_RTX(SIZE) \
+((SIZE).var == 0 ? GEN_INT ((SIZE).constant) \
+ : expand_expr (size_binop (PLUS_EXPR, (SIZE).var, \
+ size_int ((SIZE).constant)), \
+ NULL_RTX, VOIDmode, 0))
+
+/* Convert the implicit sum in a `struct args_size' into a tree. */
+#define ARGS_SIZE_TREE(SIZE) \
+((SIZE).var == 0 ? size_int ((SIZE).constant) \
+ : size_binop (PLUS_EXPR, (SIZE).var, size_int ((SIZE).constant)))
+
+/* Supply a default definition for FUNCTION_ARG_PADDING:
+ usually pad upward, but pad short args downward on
+ big-endian machines. */
+
+enum direction {none, upward, downward}; /* Value has this type. */
+
+#ifndef FUNCTION_ARG_PADDING
+#if BYTES_BIG_ENDIAN
+#define FUNCTION_ARG_PADDING(MODE, TYPE) \
+ (((MODE) == BLKmode \
+ ? ((TYPE) && TREE_CODE (TYPE_SIZE (TYPE)) == INTEGER_CST \
+ && int_size_in_bytes (TYPE) < (PARM_BOUNDARY / BITS_PER_UNIT)) \
+ : GET_MODE_BITSIZE (MODE) < PARM_BOUNDARY) \
+ ? downward : upward)
+#else
+#define FUNCTION_ARG_PADDING(MODE, TYPE) upward
+#endif
+#endif
+
+/* Supply a default definition for FUNCTION_ARG_BOUNDARY. Normally, we let
+ FUNCTION_ARG_PADDING, which also pads the length, handle any needed
+ alignment. */
+
+#ifndef FUNCTION_ARG_BOUNDARY
+#define FUNCTION_ARG_BOUNDARY(MODE, TYPE) PARM_BOUNDARY
+#endif
+
+/* Nonzero if we do not know how to pass TYPE solely in registers.
+ We cannot do so in the following cases:
+
+ - if the type has variable size
+ - if the type is marked as addressable (it is required to be constructed
+ into the stack)
+ - if the padding and mode of the type is such that a copy into a register
+ would put it into the wrong part of the register.
+
+ Which padding can't be supported depends on the byte endianness.
+
+ A value in a register is implicitly padded at the most significant end.
+ On a big-endian machine, that is the lower end in memory.
+ So a value padded in memory at the upper end can't go in a register.
+ For a little-endian machine, the reverse is true. */
+
+#if BYTES_BIG_ENDIAN
+#define MUST_PASS_IN_STACK_BAD_PADDING upward
+#else
+#define MUST_PASS_IN_STACK_BAD_PADDING downward
+#endif
+
+#define MUST_PASS_IN_STACK(MODE,TYPE) \
+ ((TYPE) != 0 \
+ && (TREE_CODE (TYPE_SIZE (TYPE)) != INTEGER_CST \
+ || TREE_ADDRESSABLE (TYPE) \
+ || ((MODE) == BLKmode \
+ && ! ((TYPE) != 0 && TREE_CODE (TYPE_SIZE (TYPE)) == INTEGER_CST \
+ && 0 == (int_size_in_bytes (TYPE) \
+ % (PARM_BOUNDARY / BITS_PER_UNIT))) \
+ && (FUNCTION_ARG_PADDING (MODE, TYPE) \
+ == MUST_PASS_IN_STACK_BAD_PADDING))))
+
+/* Nonzero if type TYPE should be returned in memory.
+ Most machines can use the following default definition. */
+
+#ifndef RETURN_IN_MEMORY
+#define RETURN_IN_MEMORY(TYPE) (TYPE_MODE (TYPE) == BLKmode)
+#endif
+
+/* Optabs are tables saying how to generate insn bodies
+ for various machine modes and numbers of operands.
+ Each optab applies to one operation.
+ For example, add_optab applies to addition.
+
+ The insn_code slot is the enum insn_code that says how to
+ generate an insn for this operation on a particular machine mode.
+ It is CODE_FOR_nothing if there is no such insn on the target machine.
+
+ The `lib_call' slot is the name of the library function that
+ can be used to perform the operation.
+
+ A few optabs, such as move_optab and cmp_optab, are used
+ by special code. */
+
+/* Everything that uses expr.h needs to define enum insn_code
+ but we don't list it in the Makefile dependencies just for that. */
+#include "insn-codes.h"
+
+typedef struct optab
+{
+ enum rtx_code code;
+ struct {
+ enum insn_code insn_code;
+ rtx libfunc;
+ } handlers [NUM_MACHINE_MODES];
+} * optab;
+
+/* Given an enum insn_code, access the function to construct
+ the body of that kind of insn. */
+#ifdef FUNCTION_CONVERSION_BUG
+/* Some compilers fail to convert a function properly to a
+ pointer-to-function when used as an argument.
+ So produce the pointer-to-function directly.
+ Luckily, these compilers seem to work properly when you
+ call the pointer-to-function. */
+#define GEN_FCN(CODE) (insn_gen_function[(int) (CODE)])
+#else
+#define GEN_FCN(CODE) (*insn_gen_function[(int) (CODE)])
+#endif
+
+extern rtx (*const insn_gen_function[]) ();
+
+extern optab add_optab;
+extern optab sub_optab;
+extern optab smul_optab; /* Signed and floating-point multiply */
+extern optab smul_highpart_optab; /* Signed multiply, return high word */
+extern optab umul_highpart_optab;
+extern optab smul_widen_optab; /* Signed multiply with result
+ one machine mode wider than args */
+extern optab umul_widen_optab;
+extern optab sdiv_optab; /* Signed divide */
+extern optab sdivmod_optab; /* Signed divide-and-remainder in one */
+extern optab udiv_optab;
+extern optab udivmod_optab;
+extern optab smod_optab; /* Signed remainder */
+extern optab umod_optab;
+extern optab flodiv_optab; /* Optab for floating divide. */
+extern optab ftrunc_optab; /* Convert float to integer in float fmt */
+extern optab and_optab; /* Logical and */
+extern optab ior_optab; /* Logical or */
+extern optab xor_optab; /* Logical xor */
+extern optab ashl_optab; /* Arithmetic shift left */
+extern optab ashr_optab; /* Arithmetic shift right */
+extern optab lshr_optab; /* Logical shift right */
+extern optab rotl_optab; /* Rotate left */
+extern optab rotr_optab; /* Rotate right */
+extern optab smin_optab; /* Signed and floating-point minimum value */
+extern optab smax_optab; /* Signed and floating-point maximum value */
+extern optab umin_optab; /* Unsigned minimum value */
+extern optab umax_optab; /* Unsigned maximum value */
+
+extern optab mov_optab; /* Move instruction. */
+extern optab movstrict_optab; /* Move, preserving high part of register. */
+
+extern optab cmp_optab; /* Compare insn; two operands. */
+extern optab tst_optab; /* tst insn; compare one operand against 0 */
+
+/* Unary operations */
+extern optab neg_optab; /* Negation */
+extern optab abs_optab; /* Abs value */
+extern optab one_cmpl_optab; /* Bitwise not */
+extern optab ffs_optab; /* Find first bit set */
+extern optab sqrt_optab; /* Square root */
+extern optab sin_optab; /* Sine */
+extern optab cos_optab; /* Cosine */
+extern optab strlen_optab; /* String length */
+
+/* Tables of patterns for extending one integer mode to another. */
+extern enum insn_code extendtab[MAX_MACHINE_MODE][MAX_MACHINE_MODE][2];
+
+/* Tables of patterns for converting between fixed and floating point. */
+extern enum insn_code fixtab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
+extern enum insn_code fixtrunctab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
+extern enum insn_code floattab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
+
+/* Contains the optab used for each rtx code. */
+extern optab code_to_optab[NUM_RTX_CODE + 1];
+
+/* Passed to expand_binop and expand_unop to say which options to try to use
+ if the requested operation can't be open-coded on the requisite mode.
+ Either OPTAB_LIB or OPTAB_LIB_WIDEN says try using a library call.
+ Either OPTAB_WIDEN or OPTAB_LIB_WIDEN says try using a wider mode.
+ OPTAB_MUST_WIDEN says try widening and don't try anything else. */
+
+enum optab_methods
+{
+ OPTAB_DIRECT,
+ OPTAB_LIB,
+ OPTAB_WIDEN,
+ OPTAB_LIB_WIDEN,
+ OPTAB_MUST_WIDEN
+};
+
+/* SYMBOL_REF rtx's for the library functions that are called
+ implicitly and not via optabs. */
+
+extern rtx extendsfdf2_libfunc;
+extern rtx extendsfxf2_libfunc;
+extern rtx extendsftf2_libfunc;
+extern rtx extenddfxf2_libfunc;
+extern rtx extenddftf2_libfunc;
+
+extern rtx truncdfsf2_libfunc;
+extern rtx truncxfsf2_libfunc;
+extern rtx trunctfsf2_libfunc;
+extern rtx truncxfdf2_libfunc;
+extern rtx trunctfdf2_libfunc;
+
+extern rtx memcpy_libfunc;
+extern rtx bcopy_libfunc;
+extern rtx memcmp_libfunc;
+extern rtx bcmp_libfunc;
+extern rtx memset_libfunc;
+extern rtx bzero_libfunc;
+
+extern rtx eqsf2_libfunc;
+extern rtx nesf2_libfunc;
+extern rtx gtsf2_libfunc;
+extern rtx gesf2_libfunc;
+extern rtx ltsf2_libfunc;
+extern rtx lesf2_libfunc;
+
+extern rtx eqdf2_libfunc;
+extern rtx nedf2_libfunc;
+extern rtx gtdf2_libfunc;
+extern rtx gedf2_libfunc;
+extern rtx ltdf2_libfunc;
+extern rtx ledf2_libfunc;
+
+extern rtx eqxf2_libfunc;
+extern rtx nexf2_libfunc;
+extern rtx gtxf2_libfunc;
+extern rtx gexf2_libfunc;
+extern rtx ltxf2_libfunc;
+extern rtx lexf2_libfunc;
+
+extern rtx eqtf2_libfunc;
+extern rtx netf2_libfunc;
+extern rtx gttf2_libfunc;
+extern rtx getf2_libfunc;
+extern rtx lttf2_libfunc;
+extern rtx letf2_libfunc;
+
+extern rtx floatsisf_libfunc;
+extern rtx floatdisf_libfunc;
+extern rtx floattisf_libfunc;
+
+extern rtx floatsidf_libfunc;
+extern rtx floatdidf_libfunc;
+extern rtx floattidf_libfunc;
+
+extern rtx floatsixf_libfunc;
+extern rtx floatdixf_libfunc;
+extern rtx floattixf_libfunc;
+
+extern rtx floatsitf_libfunc;
+extern rtx floatditf_libfunc;
+extern rtx floattitf_libfunc;
+
+extern rtx fixsfsi_libfunc;
+extern rtx fixsfdi_libfunc;
+extern rtx fixsfti_libfunc;
+
+extern rtx fixdfsi_libfunc;
+extern rtx fixdfdi_libfunc;
+extern rtx fixdfti_libfunc;
+
+extern rtx fixxfsi_libfunc;
+extern rtx fixxfdi_libfunc;
+extern rtx fixxfti_libfunc;
+
+extern rtx fixtfsi_libfunc;
+extern rtx fixtfdi_libfunc;
+extern rtx fixtfti_libfunc;
+
+extern rtx fixunssfsi_libfunc;
+extern rtx fixunssfdi_libfunc;
+extern rtx fixunssfti_libfunc;
+
+extern rtx fixunsdfsi_libfunc;
+extern rtx fixunsdfdi_libfunc;
+extern rtx fixunsdfti_libfunc;
+
+extern rtx fixunsxfsi_libfunc;
+extern rtx fixunsxfdi_libfunc;
+extern rtx fixunsxfti_libfunc;
+
+extern rtx fixunstfsi_libfunc;
+extern rtx fixunstfdi_libfunc;
+extern rtx fixunstfti_libfunc;
+
+typedef rtx (*rtxfun) ();
+
+/* Indexed by the rtx-code for a conditional (eg. EQ, LT,...)
+ gives the gen_function to make a branch to test that condition. */
+
+extern rtxfun bcc_gen_fctn[NUM_RTX_CODE];
+
+/* Indexed by the rtx-code for a conditional (eg. EQ, LT,...)
+ gives the insn code to make a store-condition insn
+ to test that condition. */
+
+extern enum insn_code setcc_gen_code[NUM_RTX_CODE];
+
+/* This array records the insn_code of insns to perform block moves. */
+extern enum insn_code movstr_optab[NUM_MACHINE_MODES];
+
+/* Define functions given in optabs.c. */
+
+/* Expand a binary operation given optab and rtx operands. */
+extern rtx expand_binop PROTO((enum machine_mode, optab, rtx, rtx, rtx,
+ int, enum optab_methods));
+
+/* Expand a binary operation with both signed and unsigned forms. */
+extern rtx sign_expand_binop PROTO((enum machine_mode, optab, optab, rtx,
+ rtx, rtx, int, enum optab_methods));
+
+/* Generate code to perform an operation on two operands with two results. */
+extern int expand_twoval_binop PROTO((optab, rtx, rtx, rtx, rtx, int));
+
+/* Expand a unary arithmetic operation given optab rtx operand. */
+extern rtx expand_unop PROTO((enum machine_mode, optab, rtx, rtx, int));
+
+/* Expand the complex absolute value operation. */
+extern rtx expand_complex_abs PROTO((enum machine_mode, rtx, rtx, int));
+
+/* Generate an instruction with a given INSN_CODE with an output and
+ an input. */
+extern void emit_unop_insn PROTO((int, rtx, rtx, enum rtx_code));
+
+/* Emit code to perform a series of operations on a multi-word quantity, one
+ word at a time. */
+extern rtx emit_no_conflict_block PROTO((rtx, rtx, rtx, rtx, rtx));
+
+/* Emit code to make a call to a constant function or a library call. */
+extern void emit_libcall_block PROTO((rtx, rtx, rtx, rtx));
+
+/* Emit one rtl instruction to store zero in specified rtx. */
+extern void emit_clr_insn PROTO((rtx));
+
+/* Emit one rtl insn to store 1 in specified rtx assuming it contains 0. */
+extern void emit_0_to_1_insn PROTO((rtx));
+
+/* Emit one rtl insn to compare two rtx's. */
+extern void emit_cmp_insn PROTO((rtx, rtx, enum rtx_code, rtx,
+ enum machine_mode, int, int));
+
+/* Nonzero if a compare of mode MODE can be done straightforwardly
+ (without splitting it into pieces). */
+extern int can_compare_p PROTO((enum machine_mode));
+
+/* Emit a library call comparison between floating point X and Y.
+ COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.). */
+extern void emit_float_lib_cmp PROTO((rtx, rtx, enum rtx_code));
+
+/* Generate code to indirectly jump to a location given in the rtx LOC. */
+extern void emit_indirect_jump PROTO((rtx));
+
+/* Create but don't emit one rtl instruction to add one rtx into another.
+ Modes must match; operands must meet the operation's predicates.
+ Likewise for subtraction and for just copying.
+ These do not call protect_from_queue; caller must do so. */
+extern rtx gen_add2_insn PROTO((rtx, rtx));
+extern rtx gen_sub2_insn PROTO((rtx, rtx));
+extern rtx gen_move_insn PROTO((rtx, rtx));
+extern int have_add2_insn PROTO((enum machine_mode));
+extern int have_sub2_insn PROTO((enum machine_mode));
+
+/* Return the INSN_CODE to use for an extend operation. */
+extern enum insn_code can_extend_p PROTO((enum machine_mode,
+ enum machine_mode, int));
+
+/* Generate the body of an insn to extend Y (with mode MFROM)
+ into X (with mode MTO). Do zero-extension if UNSIGNEDP is nonzero. */
+extern rtx gen_extend_insn PROTO((rtx, rtx, enum machine_mode,
+ enum machine_mode, int));
+
+/* Initialize the tables that control conversion between fixed and
+ floating values. */
+extern void init_fixtab PROTO((void));
+extern void init_floattab PROTO((void));
+
+/* Generate code for a FLOAT_EXPR. */
+extern void expand_float PROTO((rtx, rtx, int));
+
+/* Generate code for a FIX_EXPR. */
+extern void expand_fix PROTO((rtx, rtx, int));
+
+/* Call this once to initialize the contents of the optabs
+ appropriately for the current target machine. */
+extern void init_optabs PROTO((void));
+
+/* Functions from expmed.c: */
+
+/* Arguments MODE, RTX: return an rtx for the negation of that value.
+ May emit insns. */
+extern rtx negate_rtx PROTO((enum machine_mode, rtx));
+
+/* Expand a logical AND operation. */
+extern rtx expand_and PROTO((rtx, rtx, rtx));
+
+/* Emit a store-flag operation. */
+extern rtx emit_store_flag PROTO((rtx, enum rtx_code, rtx, rtx,
+ enum machine_mode, int, int));
+
+/* Functions from loop.c: */
+
+/* Given a JUMP_INSN, return a description of the test being made. */
+extern rtx get_condition PROTO((rtx, rtx *));
+
+/* Functions from expr.c: */
+
+/* This is run once per compilation to set up which modes can be used
+ directly in memory and to initialize the block move optab. */
+extern void init_expr_once PROTO((void));
+
+/* This is run at the start of compiling a function. */
+extern void init_expr PROTO((void));
+
+/* Use protect_from_queue to convert a QUEUED expression
+ into something that you can put immediately into an instruction. */
+extern rtx protect_from_queue PROTO((rtx, int));
+
+/* Perform all the pending incrementations. */
+extern void emit_queue PROTO((void));
+
+/* Emit some rtl insns to move data between rtx's, converting machine modes.
+ Both modes must be floating or both fixed. */
+extern void convert_move PROTO((rtx, rtx, int));
+
+/* Convert an rtx to specified machine mode and return the result. */
+extern rtx convert_to_mode PROTO((enum machine_mode, rtx, int));
+
+/* Convert an rtx to MODE from OLDMODE and return the result. */
+extern rtx convert_modes PROTO((enum machine_mode, enum machine_mode, rtx, int));
+
+/* Emit code to move a block Y to a block X. */
+extern void emit_block_move PROTO((rtx, rtx, rtx, int));
+
+/* Copy all or part of a value X into registers starting at REGNO.
+ The number of registers to be filled is NREGS. */
+extern void move_block_to_reg PROTO((int, rtx, int, enum machine_mode));
+
+/* Copy all or part of a BLKmode value X out of registers starting at REGNO.
+ The number of registers to be filled is NREGS. */
+extern void move_block_from_reg PROTO((int, rtx, int, int));
+
+/* Mark REG as holding a parameter for the next CALL_INSN. */
+extern void use_reg PROTO((rtx*, rtx));
+/* Mark NREGS consecutive regs, starting at REGNO, as holding parameters
+ for the next CALL_INSN. */
+extern void use_regs PROTO((rtx*, int, int));
+
+/* Write zeros through the storage of OBJECT.
+ If OBJECT has BLKmode, SIZE is its length in bytes. */
+extern void clear_storage PROTO((rtx, int));
+
+/* Emit insns to set X from Y. */
+extern rtx emit_move_insn PROTO((rtx, rtx));
+
+/* Emit insns to set X from Y, with no frills. */
+extern rtx emit_move_insn_1 PROTO ((rtx, rtx));
+
+/* Push a block of length SIZE (perhaps variable)
+ and return an rtx to address the beginning of the block. */
+extern rtx push_block PROTO((rtx, int, int));
+
+/* Make an operand to push someting on the stack. */
+extern rtx gen_push_operand PROTO((void));
+
+#ifdef TREE_CODE
+/* Generate code to push something onto the stack, given its mode and type. */
+extern void emit_push_insn PROTO((rtx, enum machine_mode, tree, rtx, int,
+ int, rtx, int, rtx, rtx));
+
+/* Emit library call. */
+extern void emit_library_call PVPROTO((rtx orgfun, int no_queue,
+ enum machine_mode outmode, int nargs, ...));
+extern rtx emit_library_call_value PVPROTO((rtx orgfun, rtx value, int no_queue,
+ enum machine_mode outmode, int nargs, ...));
+
+/* Expand an assignment that stores the value of FROM into TO. */
+extern rtx expand_assignment PROTO((tree, tree, int, int));
+
+/* Generate code for computing expression EXP,
+ and storing the value into TARGET.
+ If SUGGEST_REG is nonzero, copy the value through a register
+ and return that register, if that is possible. */
+extern rtx store_expr PROTO((tree, rtx, int));
+#endif
+
+/* Given an rtx that may include add and multiply operations,
+ generate them as insns and return a pseudo-reg containing the value.
+ Useful after calling expand_expr with 1 as sum_ok. */
+extern rtx force_operand PROTO((rtx, rtx));
+
+#ifdef TREE_CODE
+/* Generate code for computing expression EXP.
+ An rtx for the computed value is returned. The value is never null.
+ In the case of a void EXP, const0_rtx is returned. */
+extern rtx expand_expr PROTO((tree, rtx, enum machine_mode,
+ enum expand_modifier));
+#endif
+
+/* At the start of a function, record that we have no previously-pushed
+ arguments waiting to be popped. */
+extern void init_pending_stack_adjust PROTO((void));
+
+/* When exiting from function, if safe, clear out any pending stack adjust
+ so the adjustment won't get done. */
+extern void clear_pending_stack_adjust PROTO((void));
+
+/* Pop any previously-pushed arguments that have not been popped yet. */
+extern void do_pending_stack_adjust PROTO((void));
+
+#ifdef TREE_CODE
+/* Expand all cleanups up to OLD_CLEANUPS. */
+extern void expand_cleanups_to PROTO((tree));
+
+/* Generate code to evaluate EXP and jump to LABEL if the value is zero. */
+extern void jumpifnot PROTO((tree, rtx));
+
+/* Generate code to evaluate EXP and jump to LABEL if the value is nonzero. */
+extern void jumpif PROTO((tree, rtx));
+
+/* Generate code to evaluate EXP and jump to IF_FALSE_LABEL if
+ the result is zero, or IF_TRUE_LABEL if the result is one. */
+extern void do_jump PROTO((tree, rtx, rtx));
+#endif
+
+/* Generate rtl to compare two rtx's, will call emit_cmp_insn. */
+extern rtx compare_from_rtx PROTO((rtx, rtx, enum rtx_code, int,
+ enum machine_mode, rtx, int));
+
+/* Generate a tablejump instruction (used for switch statements). */
+extern void do_tablejump PROTO((rtx, enum machine_mode, rtx, rtx, rtx));
+
+#ifdef TREE_CODE
+/* rtl.h and tree.h were included. */
+/* Return an rtx for the size in bytes of the value of an expr. */
+extern rtx expr_size PROTO((tree));
+
+extern rtx lookup_static_chain PROTO((tree));
+
+/* Convert a stack slot address ADDR valid in function FNDECL
+ into an address valid in this function (using a static chain). */
+extern rtx fix_lexical_addr PROTO((rtx, tree));
+
+/* Return the address of the trampoline for entering nested fn FUNCTION. */
+extern rtx trampoline_address PROTO((tree));
+
+/* Return an rtx that refers to the value returned by a function
+ in its original home. This becomes invalid if any more code is emitted. */
+extern rtx hard_function_value PROTO((tree, tree));
+
+extern rtx prepare_call_address PROTO((rtx, tree, rtx *, int));
+
+extern rtx expand_call PROTO((tree, rtx, int));
+
+extern rtx expand_shift PROTO((enum tree_code, enum machine_mode, rtx, tree, rtx, int));
+extern rtx expand_divmod PROTO((int, enum tree_code, enum machine_mode, rtx, rtx, rtx, int));
+extern void locate_and_pad_parm PROTO((enum machine_mode, tree, int, tree, struct args_size *, struct args_size *, struct args_size *));
+extern rtx expand_inline_function PROTO((tree, tree, rtx, int, tree, rtx));
+/* Return the CODE_LABEL rtx for a LABEL_DECL, creating it if necessary. */
+extern rtx label_rtx PROTO((tree));
+#endif
+
+/* Indicate how an input argument register was promoted. */
+extern rtx promoted_input_arg PROTO((int, enum machine_mode *, int *));
+
+/* Return an rtx like arg but sans any constant terms.
+ Returns the original rtx if it has no constant terms.
+ The constant terms are added and stored via a second arg. */
+extern rtx eliminate_constant_term PROTO((rtx, rtx *));
+
+/* Convert arg to a valid memory address for specified machine mode,
+ by emitting insns to perform arithmetic if nec. */
+extern rtx memory_address PROTO((enum machine_mode, rtx));
+
+/* Like `memory_address' but pretent `flag_force_addr' is 0. */
+extern rtx memory_address_noforce PROTO((enum machine_mode, rtx));
+
+/* Return a memory reference like MEMREF, but with its mode changed
+ to MODE and its address changed to ADDR.
+ (VOIDmode means don't change the mode.
+ NULL for ADDR means don't change the address.) */
+extern rtx change_address PROTO((rtx, enum machine_mode, rtx));
+
+/* Return a memory reference like MEMREF, but which is known to have a
+ valid address. */
+
+extern rtx validize_mem PROTO((rtx));
+
+/* Assemble the static constant template for function entry trampolines. */
+extern rtx assemble_trampoline_template PROTO((void));
+
+/* Return 1 if two rtx's are equivalent in structure and elements. */
+extern int rtx_equal_p PROTO((rtx, rtx));
+
+/* Given rtx, return new rtx whose address won't be affected by
+ any side effects. It has been copied to a new temporary reg. */
+extern rtx stabilize PROTO((rtx));
+
+/* Given an rtx, copy all regs it refers to into new temps
+ and return a modified copy that refers to the new temps. */
+extern rtx copy_all_regs PROTO((rtx));
+
+/* Copy given rtx to a new temp reg and return that. */
+extern rtx copy_to_reg PROTO((rtx));
+
+/* Like copy_to_reg but always make the reg Pmode. */
+extern rtx copy_addr_to_reg PROTO((rtx));
+
+/* Like copy_to_reg but always make the reg the specified mode MODE. */
+extern rtx copy_to_mode_reg PROTO((enum machine_mode, rtx));
+
+/* Copy given rtx to given temp reg and return that. */
+extern rtx copy_to_suggested_reg PROTO((rtx, rtx, enum machine_mode));
+
+/* Copy a value to a register if it isn't already a register.
+ Args are mode (in case value is a constant) and the value. */
+extern rtx force_reg PROTO((enum machine_mode, rtx));
+
+/* Return given rtx, copied into a new temp reg if it was in memory. */
+extern rtx force_not_mem PROTO((rtx));
+
+#ifdef TREE_CODE
+/* Return mode and signedness to use when object is promoted. */
+extern enum machine_mode promote_mode PROTO((tree, enum machine_mode,
+ int *, int));
+#endif
+
+/* Remove some bytes from the stack. An rtx says how many. */
+extern void adjust_stack PROTO((rtx));
+
+/* Add some bytes to the stack. An rtx says how many. */
+extern void anti_adjust_stack PROTO((rtx));
+
+/* This enum is used for the following two functions. */
+enum save_level {SAVE_BLOCK, SAVE_FUNCTION, SAVE_NONLOCAL};
+
+/* Save the stack pointer at the specified level. */
+extern void emit_stack_save PROTO((enum save_level, rtx *, rtx));
+
+/* Restore the stack pointer from a save area of the specified level. */
+extern void emit_stack_restore PROTO((enum save_level, rtx, rtx));
+
+/* Allocate some space on the stack dynamically and return its address. An rtx
+ says how many bytes. */
+extern rtx allocate_dynamic_stack_space PROTO((rtx, rtx, int));
+
+/* Emit code to copy function value to a new temp reg and return that reg. */
+extern rtx function_value ();
+
+/* Return an rtx that refers to the value returned by a library call
+ in its original home. This becomes invalid if any more code is emitted. */
+extern rtx hard_libcall_value PROTO((enum machine_mode));
+
+/* Given an rtx, return an rtx for a value rounded up to a multiple
+ of STACK_BOUNDARY / BITS_PER_UNIT. */
+extern rtx round_push PROTO((rtx));
+
+extern void emit_block_move PROTO((rtx, rtx, rtx, int));
+
+extern rtx store_bit_field PROTO((rtx, int, int, enum machine_mode, rtx, int, int));
+extern rtx extract_bit_field PROTO((rtx, int, int, int, rtx, enum machine_mode, enum machine_mode, int, int));
+extern rtx expand_mult PROTO((enum machine_mode, rtx, rtx, rtx, int));
+extern rtx expand_mult_add PROTO((rtx, rtx, rtx, rtx,enum machine_mode, int));
+
+extern rtx assemble_static_space PROTO((int));
+
+/* Hook called by expand_expr for language-specific tree codes.
+ It is up to the language front end to install a hook
+ if it has any such codes that expand_expr needs to know about. */
+extern rtx (*lang_expand_expr) ();
diff --git a/gnu/usr.bin/cc/include/flags.h b/gnu/usr.bin/cc/include/flags.h
new file mode 100644
index 0000000..07ea734
--- /dev/null
+++ b/gnu/usr.bin/cc/include/flags.h
@@ -0,0 +1,359 @@
+/* Compilation switch flag definitions for GNU CC.
+ Copyright (C) 1987, 1988, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Name of the input .c file being compiled. */
+extern char *main_input_filename;
+
+enum debug_info_type
+{
+ NO_DEBUG, /* Write no debug info. */
+ DBX_DEBUG, /* Write BSD .stabs for DBX (using dbxout.c). */
+ SDB_DEBUG, /* Write COFF for (old) SDB (using sdbout.c). */
+ DWARF_DEBUG, /* Write Dwarf debug info (using dwarfout.c). */
+ XCOFF_DEBUG /* Write IBM/Xcoff debug info (using dbxout.c). */
+};
+
+/* Specify which kind of debugging info to generate. */
+extern enum debug_info_type write_symbols;
+
+enum debug_info_level
+{
+ DINFO_LEVEL_NONE, /* Write no debugging info. */
+ DINFO_LEVEL_TERSE, /* Write minimal info to support tracebacks only. */
+ DINFO_LEVEL_NORMAL, /* Write info for all declarations (and line table). */
+ DINFO_LEVEL_VERBOSE /* Write normal info plus #define/#undef info. */
+};
+
+/* Specify how much debugging info to generate. */
+extern enum debug_info_level debug_info_level;
+
+/* Nonzero means use GNU-only extensions in the generated symbolic
+ debugging information. */
+extern int use_gnu_debug_info_extensions;
+
+/* Nonzero means do optimizations. -opt. */
+
+extern int optimize;
+
+/* Nonzero means do stupid register allocation. -noreg.
+ Currently, this is 1 if `optimize' is 0. */
+
+extern int obey_regdecls;
+
+/* Don't print functions as they are compiled and don't print
+ times taken by the various passes. -quiet. */
+
+extern int quiet_flag;
+
+/* Don't print warning messages. -w. */
+
+extern int inhibit_warnings;
+
+/* Do print extra warnings (such as for uninitialized variables). -W. */
+
+extern int extra_warnings;
+
+/* Nonzero to warn about unused local variables. */
+
+extern int warn_unused;
+
+/* Nonzero means warn if inline function is too large. */
+
+extern int warn_inline;
+
+/* Nonzero to warn about variables used before they are initialized. */
+
+extern int warn_uninitialized;
+
+/* Nonzero means warn about all declarations which shadow others. */
+
+extern int warn_shadow;
+
+/* Warn if a switch on an enum fails to have a case for every enum value. */
+
+extern int warn_switch;
+
+/* Nonzero means warn about function definitions that default the return type
+ or that use a null return and have a return-type other than void. */
+
+extern int warn_return_type;
+
+/* Nonzero means warn about pointer casts that increase the required
+ alignment of the target type (and might therefore lead to a crash
+ due to a misaligned access). */
+
+extern int warn_cast_align;
+
+/* Nonzero means warn that dbx info for template class methods isn't fully
+ supported yet. */
+
+extern int warn_template_debugging;
+
+/* Nonzero means warn about any identifiers that match in the first N
+ characters. The value N is in `id_clash_len'. */
+
+extern int warn_id_clash;
+extern unsigned id_clash_len;
+
+/* Nonzero means warn about any objects definitions whose size is larger
+ than N bytes. Also want about function definitions whose returned
+ values are larger than N bytes. The value N is in `larger_than_size'. */
+
+extern int warn_larger_than;
+extern unsigned larger_than_size;
+
+/* Warn if a function returns an aggregate,
+ since there are often incompatible calling conventions for doing this. */
+
+extern int warn_aggregate_return;
+
+/* Nonzero if generating code to do profiling. */
+
+extern int profile_flag;
+
+/* Nonzero if generating code to do profiling on the basis of basic blocks. */
+
+extern int profile_block_flag;
+
+/* Nonzero for -pedantic switch: warn about anything
+ that standard C forbids. */
+
+extern int pedantic;
+
+/* Temporarily suppress certain warnings.
+ This is set while reading code from a system header file. */
+
+extern int in_system_header;
+
+/* Nonzero for -dp: annotate the assembly with a comment describing the
+ pattern and alternative used. */
+
+extern int flag_print_asm_name;
+
+/* Now the symbols that are set with `-f' switches. */
+
+/* Nonzero means `char' should be signed. */
+
+extern int flag_signed_char;
+
+/* Nonzero means give an enum type only as many bytes as it needs. */
+
+extern int flag_short_enums;
+
+/* Nonzero for -fcaller-saves: allocate values in regs that need to
+ be saved across function calls, if that produces overall better code.
+ Optional now, so people can test it. */
+
+extern int flag_caller_saves;
+
+/* Nonzero for -fpcc-struct-return: return values the same way PCC does. */
+
+extern int flag_pcc_struct_return;
+
+/* Nonzero for -fforce-mem: load memory value into a register
+ before arithmetic on it. This makes better cse but slower compilation. */
+
+extern int flag_force_mem;
+
+/* Nonzero for -fforce-addr: load memory address into a register before
+ reference to memory. This makes better cse but slower compilation. */
+
+extern int flag_force_addr;
+
+/* Nonzero for -fdefer-pop: don't pop args after each function call;
+ instead save them up to pop many calls' args with one insns. */
+
+extern int flag_defer_pop;
+
+/* Nonzero for -ffloat-store: don't allocate floats and doubles
+ in extended-precision registers. */
+
+extern int flag_float_store;
+
+/* Nonzero enables strength-reduction in loop.c. */
+
+extern int flag_strength_reduce;
+
+/* Nonzero enables loop unrolling in unroll.c. Only loops for which the
+ number of iterations can be calculated at compile-time (UNROLL_COMPLETELY,
+ UNROLL_MODULO) or at run-time (preconditioned to be UNROLL_MODULO) are
+ unrolled. */
+
+extern int flag_unroll_loops;
+
+/* Nonzero enables loop unrolling in unroll.c. All loops are unrolled.
+ This is generally not a win. */
+
+extern int flag_unroll_all_loops;
+
+/* Nonzero for -fcse-follow-jumps:
+ have cse follow jumps to do a more extensive job. */
+
+extern int flag_cse_follow_jumps;
+
+/* Nonzero for -fcse-skip-blocks:
+ have cse follow a branch around a block. */
+
+extern int flag_cse_skip_blocks;
+
+/* Nonzero for -fexpensive-optimizations:
+ perform miscellaneous relatively-expensive optimizations. */
+extern int flag_expensive_optimizations;
+
+/* Nonzero for -fwritable-strings:
+ store string constants in data segment and don't uniquize them. */
+
+extern int flag_writable_strings;
+
+/* Nonzero means don't put addresses of constant functions in registers.
+ Used for compiling the Unix kernel, where strange substitutions are
+ done on the assembly output. */
+
+extern int flag_no_function_cse;
+
+/* Nonzero for -fomit-frame-pointer:
+ don't make a frame pointer in simple functions that don't require one. */
+
+extern int flag_omit_frame_pointer;
+
+/* Nonzero to inhibit use of define_optimization peephole opts. */
+
+extern int flag_no_peephole;
+
+/* Nonzero means all references through pointers are volatile. */
+
+extern int flag_volatile;
+
+/* Nonzero means treat all global and extern variables as global. */
+
+extern int flag_volatile_global;
+
+/* Nonzero allows GCC to violate some IEEE or ANSI rules regarding math
+ operations in the interest of optimization. For example it allows
+ GCC to assume arguments to sqrt are nonnegative numbers, allowing
+ faster code for sqrt to be generated. */
+
+extern int flag_fast_math;
+
+/* Nonzero means make functions that look like good inline candidates
+ go inline. */
+
+extern int flag_inline_functions;
+
+/* Nonzero for -fkeep-inline-functions: even if we make a function
+ go inline everywhere, keep its definition around for debugging
+ purposes. */
+
+extern int flag_keep_inline_functions;
+
+/* Nonzero means that functions declared `inline' will be treated
+ as `static'. Prevents generation of zillions of copies of unused
+ static inline functions; instead, `inlines' are written out
+ only when actually used. Used in conjunction with -g. Also
+ does the right thing with #pragma interface. */
+
+extern int flag_no_inline;
+
+/* Nonzero if we are only using compiler to check syntax errors. */
+
+extern int flag_syntax_only;
+
+/* Nonzero means we should save auxiliary info into a .X file. */
+
+extern int flag_gen_aux_info;
+
+/* Nonzero means make the text shared if supported. */
+
+extern int flag_shared_data;
+
+/* flag_schedule_insns means schedule insns within basic blocks (before
+ local_alloc).
+ flag_schedule_insns_after_reload means schedule insns after
+ global_alloc. */
+
+extern int flag_schedule_insns;
+extern int flag_schedule_insns_after_reload;
+
+/* Nonzero means put things in delayed-branch slots if supported. */
+
+extern int flag_delayed_branch;
+
+/* Nonzero means to run cleanups after CALL_EXPRs. */
+
+extern int flag_short_temps;
+
+/* Nonzero means pretend it is OK to examine bits of target floats,
+ even if that isn't true. The resulting code will have incorrect constants,
+ but the same series of instructions that the native compiler would make. */
+
+extern int flag_pretend_float;
+
+/* Nonzero means change certain warnings into errors.
+ Usually these are warnings about failure to conform to some standard. */
+
+extern int flag_pedantic_errors;
+
+/* Nonzero means generate position-independent code.
+ This is not fully implemented yet. */
+
+extern int flag_pic;
+
+/* Nonzero means place uninitialized global data in the bss section. */
+
+extern int flag_no_common;
+
+/* -finhibit-size-directive inhibits output of .size for ELF.
+ This is used only for compiling crtstuff.c,
+ and it may be extended to other effects
+ needed for crtstuff.c on other systems. */
+extern int flag_inhibit_size_directive;
+
+/* -fverbose-asm causes extra commentary information to be produced in
+ the generated assembly code (to make it more readable). This option
+ is generally only of use to those who actually need to read the
+ generated assembly code (perhaps while debugging the compiler itself). */
+
+extern int flag_verbose_asm;
+
+/* -fgnu-linker specifies use of the GNU linker for initializations.
+ -fno-gnu-linker says that collect will be used. */
+extern int flag_gnu_linker;
+
+/* Other basic status info about current function. */
+
+/* Nonzero means current function must be given a frame pointer.
+ Set in stmt.c if anything is allocated on the stack there.
+ Set in reload1.c if anything is allocated on the stack there. */
+
+extern int frame_pointer_needed;
+
+/* Set nonzero if jump_optimize finds that control falls through
+ at the end of the function. */
+
+extern int can_reach_end;
+
+/* Nonzero if function being compiled receives nonlocal gotos
+ from nested functions. */
+
+extern int current_function_has_nonlocal_label;
+
+/* Nonzero if function being compiled has nonlocal gotos to parent
+ function. */
+
+extern int current_function_has_nonlocal_goto;
diff --git a/gnu/usr.bin/cc/include/function.h b/gnu/usr.bin/cc/include/function.h
new file mode 100644
index 0000000..b37a59a
--- /dev/null
+++ b/gnu/usr.bin/cc/include/function.h
@@ -0,0 +1,216 @@
+/* Structure for saving state for a nested function.
+ Copyright (C) 1989, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#ifndef NULL_TREE
+#define tree int *
+#endif
+#ifndef GET_CODE
+#define rtx int *
+#endif
+
+struct var_refs_queue
+ {
+ rtx modified;
+ enum machine_mode promoted_mode;
+ int unsignedp;
+ struct var_refs_queue *next;
+ };
+
+/* Stack of pending (incomplete) sequences saved by `start_sequence'.
+ Each element describes one pending sequence.
+ The main insn-chain is saved in the last element of the chain,
+ unless the chain is empty. */
+
+struct sequence_stack
+{
+ /* First and last insns in the chain of the saved sequence. */
+ rtx first, last;
+ tree sequence_rtl_expr;
+ struct sequence_stack *next;
+};
+
+extern struct sequence_stack *sequence_stack;
+
+/* This structure can save all the important global and static variables
+ describing the status of the current function. */
+
+struct function
+{
+ struct function *next;
+
+ /* For function.c. */
+ char *name;
+ tree decl;
+ int pops_args;
+ int returns_struct;
+ int returns_pcc_struct;
+ int needs_context;
+ int calls_setjmp;
+ int calls_longjmp;
+ int calls_alloca;
+ int has_nonlocal_label;
+ int has_nonlocal_goto;
+ rtx nonlocal_goto_handler_slot;
+ rtx nonlocal_goto_stack_level;
+ tree nonlocal_labels;
+ int args_size;
+ int pretend_args_size;
+ rtx arg_offset_rtx;
+ int varargs;
+ int max_parm_reg;
+ rtx *parm_reg_stack_loc;
+ int outgoing_args_size;
+ rtx return_rtx;
+ rtx cleanup_label;
+ rtx return_label;
+ rtx save_expr_regs;
+ rtx stack_slot_list;
+ rtx parm_birth_insn;
+ int frame_offset;
+ rtx tail_recursion_label;
+ rtx tail_recursion_reentry;
+ rtx internal_arg_pointer;
+ rtx arg_pointer_save_area;
+ tree rtl_expr_chain;
+ rtx last_parm_insn;
+ tree context_display;
+ tree trampoline_list;
+ int function_call_count;
+ struct temp_slot *temp_slots;
+ int temp_slot_level;
+ /* This slot is initialized as 0 and is added to
+ during the nested function. */
+ struct var_refs_queue *fixup_var_refs_queue;
+
+ /* For stmt.c */
+ struct nesting *block_stack;
+ struct nesting *stack_block_stack;
+ struct nesting *cond_stack;
+ struct nesting *loop_stack;
+ struct nesting *case_stack;
+ struct nesting *nesting_stack;
+ int nesting_depth;
+ int block_start_count;
+ tree last_expr_type;
+ rtx last_expr_value;
+ int expr_stmts_for_value;
+ char *emit_filename;
+ int emit_lineno;
+ struct goto_fixup *goto_fixup_chain;
+
+ /* For expr.c. */
+ int pending_stack_adjust;
+ int inhibit_defer_pop;
+ tree cleanups_this_call;
+ rtx saveregs_value;
+ rtx apply_args_value;
+ rtx forced_labels;
+
+ /* For emit-rtl.c. */
+ int reg_rtx_no;
+ int first_label_num;
+ rtx first_insn;
+ rtx last_insn;
+ tree sequence_rtl_expr;
+ struct sequence_stack *sequence_stack;
+ int cur_insn_uid;
+ int last_linenum;
+ char *last_filename;
+ char *regno_pointer_flag;
+ int regno_pointer_flag_length;
+ rtx *regno_reg_rtx;
+
+ /* For stor-layout.c. */
+ tree permanent_type_chain;
+ tree temporary_type_chain;
+ tree permanent_type_end;
+ tree temporary_type_end;
+ tree pending_sizes;
+ int immediate_size_expand;
+
+ /* For tree.c. */
+ int all_types_permanent;
+ struct momentary_level *momentary_stack;
+ char *maybepermanent_firstobj;
+ char *temporary_firstobj;
+ char *momentary_firstobj;
+ char *momentary_function_firstobj;
+ struct obstack *current_obstack;
+ struct obstack *function_obstack;
+ struct obstack *function_maybepermanent_obstack;
+ struct obstack *expression_obstack;
+ struct obstack *saveable_obstack;
+ struct obstack *rtl_obstack;
+
+ /* For integrate.c. */
+ int uses_const_pool;
+
+ /* For md files. */
+ int uses_pic_offset_table;
+ /* tm.h can use this to store whatever it likes. */
+ struct machine_function *machine;
+
+ /* For reorg. */
+ rtx epilogue_delay_list;
+
+ /* For varasm. */
+ struct constant_descriptor **const_rtx_hash_table;
+ struct pool_sym **const_rtx_sym_hash_table;
+ struct pool_constant *first_pool, *last_pool;
+ int pool_offset;
+};
+
+/* The FUNCTION_DECL for an inline function currently being expanded. */
+extern tree inline_function_decl;
+
+/* Label that will go on function epilogue.
+ Jumping to this label serves as a "return" instruction
+ on machines which require execution of the epilogue on all returns. */
+extern rtx return_label;
+
+/* List (chain of EXPR_LISTs) of all stack slots in this function.
+ Made for the sake of unshare_all_rtl. */
+extern rtx stack_slot_list;
+
+/* Given a function decl for a containing function,
+ return the `struct function' for it. */
+struct function *find_function_data PROTO((tree));
+
+/* Pointer to chain of `struct function' for containing functions. */
+extern struct function *outer_function_chain;
+
+/* Put all this function's BLOCK nodes into a vector and return it.
+ Also store in each NOTE for the beginning or end of a block
+ the index of that block in the vector. */
+extern tree *identify_blocks PROTO((tree, rtx));
+
+/* These variables hold pointers to functions to
+ save and restore machine-specific data,
+ in push_function_context and pop_function_context. */
+extern void (*save_machine_status) ();
+extern void (*restore_machine_status) ();
+
+#ifdef rtx
+#undef rtx
+#endif
+
+#ifdef tree
+#undef tree
+#endif
diff --git a/gnu/usr.bin/cc/include/gbl-ctors.h b/gnu/usr.bin/cc/include/gbl-ctors.h
new file mode 100644
index 0000000..2e7f520
--- /dev/null
+++ b/gnu/usr.bin/cc/include/gbl-ctors.h
@@ -0,0 +1,80 @@
+/* Definitions relating to the special __do_global_init function used
+ for getting g++ file-scope static objects constructed. This file
+ will get included either by libgcc2.c (for systems that don't support
+ a .init section) or by crtstuff.c (for those that do).
+
+ Written by Ron Guilmette (rfg@netcom.com)
+
+Copyright (C) 1991 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This file contains definitions and declarations of things
+ relating to the normal start-up-time invocation of C++
+ file-scope static object constructors. These declarations
+ and definitions are used by *both* libgcc2.c and by crtstuff.c.
+
+ Note that this file should only be compiled with GCC.
+*/
+
+#ifdef HAVE_ATEXIT
+extern void atexit (void (*) (void));
+#define ON_EXIT(FUNC,ARG) atexit ((FUNC))
+#else
+#ifdef sun
+extern void on_exit (void*, void*);
+#define ON_EXIT(FUNC,ARG) on_exit ((FUNC), (ARG))
+#endif
+#endif
+
+/* Declare a pointer to void function type. */
+
+typedef void (*func_ptr) (void);
+
+/* Declare the set of symbols use as begin and end markers for the lists
+ of global object constructors and global object destructors. */
+
+extern func_ptr __CTOR_LIST__[];
+extern func_ptr __DTOR_LIST__[];
+
+/* Declare the routine which need to get invoked at program exit time. */
+
+extern void __do_global_dtors ();
+
+/* Define a macro with the code which needs to be executed at program
+ start-up time. This macro is used in two places in crtstuff.c (for
+ systems which support a .init section) and in one place in libgcc2.c
+ (for those system which do *not* support a .init section). For all
+ three places where this code might appear, it must be identical, so
+ we define it once here as a macro to avoid various instances getting
+ out-of-sync with one another. */
+
+/* The first word may or may not contain the number of pointers in the table.
+ In all cases, the table is null-terminated.
+ We ignore the first word and scan up to the null. */
+
+/* Some systems use a different strategy for finding the ctors.
+ For example, svr3. */
+#ifndef DO_GLOBAL_CTORS_BODY
+#define DO_GLOBAL_CTORS_BODY \
+do { \
+ func_ptr *p; \
+ for (p = __CTOR_LIST__ + 1; *p; ) \
+ (*p++) (); \
+} while (0)
+#endif
+
diff --git a/gnu/usr.bin/cc/include/glimits.h b/gnu/usr.bin/cc/include/glimits.h
new file mode 100644
index 0000000..ff25a97
--- /dev/null
+++ b/gnu/usr.bin/cc/include/glimits.h
@@ -0,0 +1,93 @@
+#ifndef _LIMITS_H___
+#ifndef _MACH_MACHLIMITS_H_
+
+/* _MACH_MACHLIMITS_H_ is used on OSF/1. */
+#define _LIMITS_H___
+#define _MACH_MACHLIMITS_H_
+
+/* Number of bits in a `char'. */
+#undef CHAR_BIT
+#define CHAR_BIT 8
+
+/* Maximum length of a multibyte character. */
+#ifndef MB_LEN_MAX
+#define MB_LEN_MAX 1
+#endif
+
+/* Minimum and maximum values a `signed char' can hold. */
+#undef SCHAR_MIN
+#define SCHAR_MIN (-128)
+#undef SCHAR_MAX
+#define SCHAR_MAX 127
+
+/* Maximum value an `unsigned char' can hold. (Minimum is 0). */
+#undef UCHAR_MAX
+#define UCHAR_MAX 255
+
+/* Minimum and maximum values a `char' can hold. */
+#ifdef __CHAR_UNSIGNED__
+#undef CHAR_MIN
+#define CHAR_MIN 0
+#undef CHAR_MAX
+#define CHAR_MAX 255
+#else
+#undef CHAR_MIN
+#define CHAR_MIN (-128)
+#undef CHAR_MAX
+#define CHAR_MAX 127
+#endif
+
+/* Minimum and maximum values a `signed short int' can hold. */
+#undef SHRT_MIN
+#define SHRT_MIN (-32768)
+#undef SHRT_MAX
+#define SHRT_MAX 32767
+
+/* Maximum value an `unsigned short int' can hold. (Minimum is 0). */
+#undef USHRT_MAX
+#define USHRT_MAX 65535
+
+/* Minimum and maximum values a `signed int' can hold. */
+#ifndef __INT_MAX__
+#define __INT_MAX__ 2147483647
+#endif
+#undef INT_MIN
+#define INT_MIN (-INT_MAX-1)
+#undef INT_MAX
+#define INT_MAX __INT_MAX__
+
+/* Maximum value an `unsigned int' can hold. (Minimum is 0). */
+#undef UINT_MAX
+#define UINT_MAX (INT_MAX * 2U + 1)
+
+/* Minimum and maximum values a `signed long int' can hold.
+ (Same as `int'). */
+#ifndef __LONG_MAX__
+#define __LONG_MAX__ 2147483647L
+#endif
+#undef LONG_MIN
+#define LONG_MIN (-LONG_MAX-1)
+#undef LONG_MAX
+#define LONG_MAX __LONG_MAX__
+
+/* Maximum value an `unsigned long int' can hold. (Minimum is 0). */
+#undef ULONG_MAX
+#define ULONG_MAX (LONG_MAX * 2UL + 1)
+
+#if defined (__GNU_LIBRARY__) ? defined (__USE_GNU) : !defined (__STRICT_ANSI__)
+/* Minimum and maximum values a `signed long long int' can hold. */
+#ifndef __LONG_LONG_MAX__
+#define __LONG_LONG_MAX__ 9223372036854775807LL
+#endif
+#undef LONG_LONG_MIN
+#define LONG_LONG_MIN (-LONG_LONG_MAX-1)
+#undef LONG_LONG_MAX
+#define LONG_LONG_MAX __LONG_LONG_MAX__
+
+/* Maximum value an `unsigned long long int' can hold. (Minimum is 0). */
+#undef ULONG_LONG_MAX
+#define ULONG_LONG_MAX (LONG_LONG_MAX * 2ULL + 1)
+#endif
+
+#endif /* _MACH_MACHLIMITS_H_ */
+#endif /* _LIMITS_H___ */
diff --git a/gnu/usr.bin/cc/include/hard-reg-set.h b/gnu/usr.bin/cc/include/hard-reg-set.h
new file mode 100644
index 0000000..6bc668b
--- /dev/null
+++ b/gnu/usr.bin/cc/include/hard-reg-set.h
@@ -0,0 +1,270 @@
+/* Sets (bit vectors) of hard registers, and operations on them.
+ Copyright (C) 1987, 1992, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* Define the type of a set of hard registers. */
+
+/* HARD_REG_ELT_TYPE is a typedef of the unsigned integral type which
+ will be used for hard reg sets, either alone or in an array.
+
+ If HARD_REG_SET is a macro, its definition is HARD_REG_ELT_TYPE,
+ and it has enough bits to represent all the target machine's hard
+ registers. Otherwise, it is a typedef for a suitably sized array
+ of HARD_REG_ELT_TYPEs. HARD_REG_SET_LONGS is defined as how many.
+
+ Note that lots of code assumes that the first part of a regset is
+ the same format as a HARD_REG_SET. To help make sure this is true,
+ we only try the widest integer mode (HOST_WIDE_INT) instead of all the
+ smaller types. This approach loses only if there are a very few
+ registers and then only in the few cases where we have an array of
+ HARD_REG_SETs, so it needn't be as complex as it used to be. */
+
+typedef unsigned HOST_WIDE_INT HARD_REG_ELT_TYPE;
+
+#if FIRST_PSEUDO_REGISTER <= HOST_BITS_PER_WIDE_INT
+
+#define HARD_REG_SET HARD_REG_ELT_TYPE
+
+#else
+
+#define HARD_REG_SET_LONGS \
+ ((FIRST_PSEUDO_REGISTER + HOST_BITS_PER_WIDE_INT - 1) \
+ / HOST_BITS_PER_WIDE_INT)
+typedef HARD_REG_ELT_TYPE HARD_REG_SET[HARD_REG_SET_LONGS];
+
+#endif
+
+/* HARD_CONST is used to cast a constant to the appropriate type
+ for use with a HARD_REG_SET. */
+
+#define HARD_CONST(X) ((HARD_REG_ELT_TYPE) (X))
+
+/* Define macros SET_HARD_REG_BIT, CLEAR_HARD_REG_BIT and TEST_HARD_REG_BIT
+ to set, clear or test one bit in a hard reg set of type HARD_REG_SET.
+ All three take two arguments: the set and the register number.
+
+ In the case where sets are arrays of longs, the first argument
+ is actually a pointer to a long.
+
+ Define two macros for initializing a set:
+ CLEAR_HARD_REG_SET and SET_HARD_REG_SET.
+ These take just one argument.
+
+ Also define macros for copying hard reg sets:
+ COPY_HARD_REG_SET and COMPL_HARD_REG_SET.
+ These take two arguments TO and FROM; they read from FROM
+ and store into TO. COMPL_HARD_REG_SET complements each bit.
+
+ Also define macros for combining hard reg sets:
+ IOR_HARD_REG_SET and AND_HARD_REG_SET.
+ These take two arguments TO and FROM; they read from FROM
+ and combine bitwise into TO. Define also two variants
+ IOR_COMPL_HARD_REG_SET and AND_COMPL_HARD_REG_SET
+ which use the complement of the set FROM.
+
+ Also define GO_IF_HARD_REG_SUBSET (X, Y, TO):
+ if X is a subset of Y, go to TO.
+*/
+
+#ifdef HARD_REG_SET
+
+#define SET_HARD_REG_BIT(SET, BIT) \
+ ((SET) |= HARD_CONST (1) << (BIT))
+#define CLEAR_HARD_REG_BIT(SET, BIT) \
+ ((SET) &= ~(HARD_CONST (1) << (BIT)))
+#define TEST_HARD_REG_BIT(SET, BIT) \
+ ((SET) & (HARD_CONST (1) << (BIT)))
+
+#define CLEAR_HARD_REG_SET(TO) ((TO) = HARD_CONST (0))
+#define SET_HARD_REG_SET(TO) ((TO) = ~ HARD_CONST (0))
+
+#define COPY_HARD_REG_SET(TO, FROM) ((TO) = (FROM))
+#define COMPL_HARD_REG_SET(TO, FROM) ((TO) = ~(FROM))
+
+#define IOR_HARD_REG_SET(TO, FROM) ((TO) |= (FROM))
+#define IOR_COMPL_HARD_REG_SET(TO, FROM) ((TO) |= ~ (FROM))
+#define AND_HARD_REG_SET(TO, FROM) ((TO) &= (FROM))
+#define AND_COMPL_HARD_REG_SET(TO, FROM) ((TO) &= ~ (FROM))
+
+#define GO_IF_HARD_REG_SUBSET(X,Y,TO) if (HARD_CONST (0) == ((X) & ~(Y))) goto TO
+
+#define GO_IF_HARD_REG_EQUAL(X,Y,TO) if ((X) == (Y)) goto TO
+
+#else
+
+#define UHOST_BITS_PER_WIDE_INT ((unsigned) HOST_BITS_PER_WIDE_INT)
+
+#define SET_HARD_REG_BIT(SET, BIT) \
+ ((SET)[(BIT) / UHOST_BITS_PER_WIDE_INT] \
+ |= HARD_CONST (1) << ((BIT) % UHOST_BITS_PER_WIDE_INT))
+
+#define CLEAR_HARD_REG_BIT(SET, BIT) \
+ ((SET)[(BIT) / UHOST_BITS_PER_WIDE_INT] \
+ &= ~(HARD_CONST (1) << ((BIT) % UHOST_BITS_PER_WIDE_INT)))
+
+#define TEST_HARD_REG_BIT(SET, BIT) \
+ ((SET)[(BIT) / UHOST_BITS_PER_WIDE_INT] \
+ & (HARD_CONST (1) << ((BIT) % UHOST_BITS_PER_WIDE_INT)))
+
+#define CLEAR_HARD_REG_SET(TO) \
+do { register HARD_REG_ELT_TYPE *scan_tp_ = (TO); \
+ register int i; \
+ for (i = 0; i < HARD_REG_SET_LONGS; i++) \
+ *scan_tp_++ = 0; } while (0)
+
+#define SET_HARD_REG_SET(TO) \
+do { register HARD_REG_ELT_TYPE *scan_tp_ = (TO); \
+ register int i; \
+ for (i = 0; i < HARD_REG_SET_LONGS; i++) \
+ *scan_tp_++ = -1; } while (0)
+
+#define COPY_HARD_REG_SET(TO, FROM) \
+do { register HARD_REG_ELT_TYPE *scan_tp_ = (TO), *scan_fp_ = (FROM); \
+ register int i; \
+ for (i = 0; i < HARD_REG_SET_LONGS; i++) \
+ *scan_tp_++ = *scan_fp_++; } while (0)
+
+#define COMPL_HARD_REG_SET(TO, FROM) \
+do { register HARD_REG_ELT_TYPE *scan_tp_ = (TO), *scan_fp_ = (FROM); \
+ register int i; \
+ for (i = 0; i < HARD_REG_SET_LONGS; i++) \
+ *scan_tp_++ = ~ *scan_fp_++; } while (0)
+
+#define AND_HARD_REG_SET(TO, FROM) \
+do { register HARD_REG_ELT_TYPE *scan_tp_ = (TO), *scan_fp_ = (FROM); \
+ register int i; \
+ for (i = 0; i < HARD_REG_SET_LONGS; i++) \
+ *scan_tp_++ &= *scan_fp_++; } while (0)
+
+#define AND_COMPL_HARD_REG_SET(TO, FROM) \
+do { register HARD_REG_ELT_TYPE *scan_tp_ = (TO), *scan_fp_ = (FROM); \
+ register int i; \
+ for (i = 0; i < HARD_REG_SET_LONGS; i++) \
+ *scan_tp_++ &= ~ *scan_fp_++; } while (0)
+
+#define IOR_HARD_REG_SET(TO, FROM) \
+do { register HARD_REG_ELT_TYPE *scan_tp_ = (TO), *scan_fp_ = (FROM); \
+ register int i; \
+ for (i = 0; i < HARD_REG_SET_LONGS; i++) \
+ *scan_tp_++ |= *scan_fp_++; } while (0)
+
+#define IOR_COMPL_HARD_REG_SET(TO, FROM) \
+do { register HARD_REG_ELT_TYPE *scan_tp_ = (TO), *scan_fp_ = (FROM); \
+ register int i; \
+ for (i = 0; i < HARD_REG_SET_LONGS; i++) \
+ *scan_tp_++ |= ~ *scan_fp_++; } while (0)
+
+#define GO_IF_HARD_REG_SUBSET(X,Y,TO) \
+do { register HARD_REG_ELT_TYPE *scan_xp_ = (X), *scan_yp_ = (Y); \
+ register int i; \
+ for (i = 0; i < HARD_REG_SET_LONGS; i++) \
+ if (0 != (*scan_xp_++ & ~ *scan_yp_++)) break; \
+ if (i == HARD_REG_SET_LONGS) goto TO; } while (0)
+
+#define GO_IF_HARD_REG_EQUAL(X,Y,TO) \
+do { register HARD_REG_ELT_TYPE *scan_xp_ = (X), *scan_yp_ = (Y); \
+ register int i; \
+ for (i = 0; i < HARD_REG_SET_LONGS; i++) \
+ if (*scan_xp_++ != *scan_yp_++) break; \
+ if (i == HARD_REG_SET_LONGS) goto TO; } while (0)
+
+#endif
+
+/* Define some standard sets of registers. */
+
+/* Indexed by hard register number, contains 1 for registers
+ that are fixed use (stack pointer, pc, frame pointer, etc.).
+ These are the registers that cannot be used to allocate
+ a pseudo reg whose life does not cross calls. */
+
+extern char fixed_regs[FIRST_PSEUDO_REGISTER];
+
+/* The same info as a HARD_REG_SET. */
+
+extern HARD_REG_SET fixed_reg_set;
+
+/* Indexed by hard register number, contains 1 for registers
+ that are fixed use or are clobbered by function calls.
+ These are the registers that cannot be used to allocate
+ a pseudo reg whose life crosses calls. */
+
+extern char call_used_regs[FIRST_PSEUDO_REGISTER];
+
+/* The same info as a HARD_REG_SET. */
+
+extern HARD_REG_SET call_used_reg_set;
+
+/* Indexed by hard register number, contains 1 for registers that are
+ fixed use -- i.e. in fixed_regs -- or a function value return register
+ or STRUCT_VALUE_REGNUM or STATIC_CHAIN_REGNUM. These are the
+ registers that cannot hold quantities across calls even if we are
+ willing to save and restore them. */
+
+extern char call_fixed_regs[FIRST_PSEUDO_REGISTER];
+
+/* The same info as a HARD_REG_SET. */
+
+extern HARD_REG_SET call_fixed_reg_set;
+
+/* Indexed by hard register number, contains 1 for registers
+ that are being used for global register decls.
+ These must be exempt from ordinary flow analysis
+ and are also considered fixed. */
+
+extern char global_regs[FIRST_PSEUDO_REGISTER];
+
+/* Table of register numbers in the order in which to try to use them. */
+
+#ifdef REG_ALLOC_ORDER /* Avoid undef symbol in certain broken linkers. */
+extern int reg_alloc_order[FIRST_PSEUDO_REGISTER];
+#endif
+
+/* For each reg class, a HARD_REG_SET saying which registers are in it. */
+
+extern HARD_REG_SET reg_class_contents[];
+
+/* For each reg class, number of regs it contains. */
+
+extern int reg_class_size[N_REG_CLASSES];
+
+/* For each reg class, table listing all the containing classes. */
+
+extern enum reg_class reg_class_superclasses[N_REG_CLASSES][N_REG_CLASSES];
+
+/* For each reg class, table listing all the classes contained in it. */
+
+extern enum reg_class reg_class_subclasses[N_REG_CLASSES][N_REG_CLASSES];
+
+/* For each pair of reg classes,
+ a largest reg class contained in their union. */
+
+extern enum reg_class reg_class_subunion[N_REG_CLASSES][N_REG_CLASSES];
+
+/* For each pair of reg classes,
+ the smallest reg class that contains their union. */
+
+extern enum reg_class reg_class_superunion[N_REG_CLASSES][N_REG_CLASSES];
+
+/* Number of non-fixed registers. */
+
+extern int n_non_fixed_regs;
+
+/* Vector indexed by hardware reg giving its name. */
+
+extern char *reg_names[FIRST_PSEUDO_REGISTER];
diff --git a/gnu/usr.bin/cc/include/i386/bsd.h b/gnu/usr.bin/cc/include/i386/bsd.h
new file mode 100644
index 0000000..8aec304
--- /dev/null
+++ b/gnu/usr.bin/cc/include/i386/bsd.h
@@ -0,0 +1,129 @@
+/* Definitions for BSD assembler syntax for Intel 386
+ (actually AT&T syntax for insns and operands,
+ adapted to BSD conventions for symbol names and debugging.)
+ Copyright (C) 1988 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Include common aspects of all 386 Unix assemblers. */
+#include "i386/unix.h"
+
+/* Use the Sequent Symmetry assembler syntax. */
+
+#define TARGET_VERSION fprintf (stderr, " (80386, BSD syntax)");
+
+/* Define the syntax of pseudo-ops, labels and comments. */
+
+/* Prefix for internally generated assembler labels. If we aren't using
+ underscores, we are using prefix `.'s to identify labels that should
+ be ignored, as in `i386/gas.h' --karl@cs.umb.edu */
+#ifdef NO_UNDERSCORES
+#define LPREFIX ".L"
+#else
+#define LPREFIX "L"
+#endif /* not NO_UNDERSCORES */
+
+/* Assembler pseudos to introduce constants of various size. */
+
+#define ASM_BYTE_OP "\t.byte"
+#define ASM_SHORT "\t.word"
+#define ASM_LONG "\t.long"
+#define ASM_DOUBLE "\t.double"
+
+/* Output at beginning of assembler file.
+ ??? I am skeptical of this -- RMS. */
+
+#define ASM_FILE_START(FILE) \
+ do { fprintf (FILE, "\t.file\t"); \
+ output_quoted_string (FILE, dump_base_name); \
+ fprintf (FILE, "\n"); \
+ } while (0)
+
+/* This was suggested, but it shouldn't be right for DBX output. -- RMS
+ #define ASM_OUTPUT_SOURCE_FILENAME(FILE, NAME) */
+
+
+/* Define the syntax of labels and symbol definitions/declarations. */
+
+/* This is how to output an assembler line
+ that says to advance the location counter by SIZE bytes. */
+
+#define ASM_OUTPUT_SKIP(FILE,SIZE) \
+ fprintf (FILE, "\t.space %u\n", (SIZE))
+
+/* Define the syntax of labels and symbol definitions/declarations. */
+
+/* This says how to output an assembler line
+ to define a global common symbol. */
+
+#define ASM_OUTPUT_COMMON(FILE, NAME, SIZE, ROUNDED) \
+( fputs (".comm ", (FILE)), \
+ assemble_name ((FILE), (NAME)), \
+ fprintf ((FILE), ",%u\n", (ROUNDED)))
+
+/* This says how to output an assembler line
+ to define a local common symbol. */
+
+#define ASM_OUTPUT_LOCAL(FILE, NAME, SIZE, ROUNDED) \
+( fputs (".lcomm ", (FILE)), \
+ assemble_name ((FILE), (NAME)), \
+ fprintf ((FILE), ",%u\n", (ROUNDED)))
+
+/* This is how to output an assembler line
+ that says to advance the location counter
+ to a multiple of 2**LOG bytes. */
+
+#define ASM_OUTPUT_ALIGN(FILE,LOG) \
+ if ((LOG)!=0) fprintf ((FILE), "\t.align %d\n", (LOG))
+
+/* This is how to store into the string BUF
+ the symbol_ref name of an internal numbered label where
+ PREFIX is the class of label and NUM is the number within the class.
+ This is suitable for output with `assemble_name'. */
+
+#ifdef NO_UNDERSCORES
+#define ASM_GENERATE_INTERNAL_LABEL(BUF,PREFIX,NUMBER) \
+ sprintf ((BUF), "*.%s%d", (PREFIX), (NUMBER))
+#else
+#define ASM_GENERATE_INTERNAL_LABEL(BUF,PREFIX,NUMBER) \
+ sprintf ((BUF), "*%s%d", (PREFIX), (NUMBER))
+#endif
+
+/* This is how to output an internal numbered label where
+ PREFIX is the class of label and NUM is the number within the class. */
+
+#ifdef NO_UNDERSCORES
+#define ASM_OUTPUT_INTERNAL_LABEL(FILE,PREFIX,NUM) \
+ fprintf (FILE, ".%s%d:\n", PREFIX, NUM)
+#else
+#define ASM_OUTPUT_INTERNAL_LABEL(FILE,PREFIX,NUM) \
+ fprintf (FILE, "%s%d:\n", PREFIX, NUM)
+#endif
+
+/* This is how to output a reference to a user-level label named NAME. */
+
+#ifdef NO_UNDERSCORES
+#define ASM_OUTPUT_LABELREF(FILE,NAME) fprintf (FILE, "%s", NAME)
+#else
+#define ASM_OUTPUT_LABELREF(FILE,NAME) fprintf (FILE, "_%s", NAME)
+#endif /* not NO_UNDERSCORES */
+
+/* Sequent has some changes in the format of DBX symbols. */
+#define DBX_NO_XREFS 1
+
+/* Don't split DBX symbols into continuations. */
+#define DBX_CONTIN_LENGTH 0
diff --git a/gnu/usr.bin/cc/include/i386/gas.h b/gnu/usr.bin/cc/include/i386/gas.h
new file mode 100644
index 0000000..3e8dba5
--- /dev/null
+++ b/gnu/usr.bin/cc/include/i386/gas.h
@@ -0,0 +1,154 @@
+/* Definitions for Intel 386 running system V with gnu tools
+ Copyright (C) 1988, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Note that i386/seq-gas.h is a GAS configuration that does not use this
+ file. */
+
+#include "i386/i386.h"
+
+#ifndef YES_UNDERSCORES
+/* Define this now, because i386/bsd.h tests it. */
+#define NO_UNDERSCORES
+#endif
+
+/* Use the bsd assembler syntax. */
+/* we need to do this because gas is really a bsd style assembler,
+ * and so doesn't work well this these att-isms:
+ *
+ * ASM_OUTPUT_SKIP is .set .,.+N, which isn't implemented in gas
+ * ASM_OUTPUT_LOCAL is done with .set .,.+N, but that can't be
+ * used to define bss static space
+ *
+ * Next is the question of whether to uses underscores. RMS didn't
+ * like this idea at first, but since it is now obvious that we
+ * need this separate tm file for use with gas, at least to get
+ * dbx debugging info, I think we should also switch to underscores.
+ * We can keep i386v for real att style output, and the few
+ * people who want both form will have to compile twice.
+ */
+
+#include "i386/bsd.h"
+
+/* these come from i386/bsd.h, but are specific to sequent */
+#undef DBX_NO_XREFS
+#undef DBX_CONTIN_LENGTH
+
+/* Ask for COFF symbols. */
+
+#define SDB_DEBUGGING_INFO
+
+/* Specify predefined symbols in preprocessor. */
+
+#define CPP_PREDEFINES "-Dunix -Di386 -Asystem(unix) -Acpu(i386) -Amachine(i386)"
+#define CPP_SPEC "%{posix:-D_POSIX_SOURCE}"
+
+/* Allow #sccs in preprocessor. */
+
+#define SCCS_DIRECTIVE
+
+/* Output #ident as a .ident. */
+
+#define ASM_OUTPUT_IDENT(FILE, NAME) fprintf (FILE, "\t.ident \"%s\"\n", NAME);
+
+/* Implicit library calls should use memcpy, not bcopy, etc. */
+
+#define TARGET_MEM_FUNCTIONS
+
+#if 0 /* People say gas uses the log as the arg to .align. */
+/* When using gas, .align N aligns to an N-byte boundary. */
+
+#undef ASM_OUTPUT_ALIGN
+#define ASM_OUTPUT_ALIGN(FILE,LOG) \
+ if ((LOG)!=0) fprintf ((FILE), "\t.align %d\n", 1<<(LOG))
+#endif
+
+/* Align labels, etc. at 4-byte boundaries.
+ For the 486, align to 16-byte boundary for sake of cache. */
+
+#undef ASM_OUTPUT_ALIGN_CODE
+#define ASM_OUTPUT_ALIGN_CODE(FILE) \
+ fprintf ((FILE), "\t.align %d,0x90\n", \
+ TARGET_486 ? 4 : 2); /* Use log of 16 or log of 4 as arg. */
+
+/* Align start of loop at 4-byte boundary. */
+
+#undef ASM_OUTPUT_LOOP_ALIGN
+#define ASM_OUTPUT_LOOP_ALIGN(FILE) \
+ fprintf ((FILE), "\t.align 2,0x90\n"); /* Use log of 4 as arg. */
+
+/* A C statement or statements which output an assembler instruction
+ opcode to the stdio stream STREAM. The macro-operand PTR is a
+ variable of type `char *' which points to the opcode name in its
+ "internal" form--the form that is written in the machine description.
+
+ GAS version 1.38.1 doesn't understand the `repz' opcode mnemonic.
+ So use `repe' instead. */
+
+#define ASM_OUTPUT_OPCODE(STREAM, PTR) \
+{ \
+ if ((PTR)[0] == 'r' \
+ && (PTR)[1] == 'e' \
+ && (PTR)[2] == 'p') \
+ { \
+ if ((PTR)[3] == 'z') \
+ { \
+ fprintf (STREAM, "repe"); \
+ (PTR) += 4; \
+ } \
+ else if ((PTR)[3] == 'n' && (PTR)[4] == 'z') \
+ { \
+ fprintf (STREAM, "repne"); \
+ (PTR) += 5; \
+ } \
+ } \
+}
+
+/* Define macro used to output shift-double opcodes when the shift
+ count is in %cl. Some assemblers require %cl as an argument;
+ some don't.
+
+ GAS requires the %cl argument, so override i386/unix.h. */
+
+#undef AS3_SHIFT_DOUBLE
+#define AS3_SHIFT_DOUBLE(a,b,c,d) AS3 (a,b,c,d)
+
+/* Print opcodes the way that GAS expects them. */
+#define GAS_MNEMONICS 1
+
+#ifdef NO_UNDERSCORES /* If user-symbols don't have underscores,
+ then it must take more than `L' to identify
+ a label that should be ignored. */
+
+/* This is how to store into the string BUF
+ the symbol_ref name of an internal numbered label where
+ PREFIX is the class of label and NUM is the number within the class.
+ This is suitable for output with `assemble_name'. */
+
+#undef ASM_GENERATE_INTERNAL_LABEL
+#define ASM_GENERATE_INTERNAL_LABEL(BUF,PREFIX,NUMBER) \
+ sprintf ((BUF), ".%s%d", (PREFIX), (NUMBER))
+
+/* This is how to output an internal numbered label where
+ PREFIX is the class of label and NUM is the number within the class. */
+
+#undef ASM_OUTPUT_INTERNAL_LABEL
+#define ASM_OUTPUT_INTERNAL_LABEL(FILE,PREFIX,NUM) \
+ fprintf (FILE, ".%s%d:\n", PREFIX, NUM)
+
+#endif /* NO_UNDERSCORES */
diff --git a/gnu/usr.bin/cc/include/i386/gstabs.h b/gnu/usr.bin/cc/include/i386/gstabs.h
new file mode 100644
index 0000000..5f0ae34
--- /dev/null
+++ b/gnu/usr.bin/cc/include/i386/gstabs.h
@@ -0,0 +1,9 @@
+#include "i386/gas.h"
+
+/* We do not want to output SDB debugging information. */
+
+#undef SDB_DEBUGGING_INFO
+
+/* We want to output DBX debugging information. */
+
+#define DBX_DEBUGGING_INFO
diff --git a/gnu/usr.bin/cc/include/i386/i386.h b/gnu/usr.bin/cc/include/i386/i386.h
new file mode 100644
index 0000000..983bfc5
--- /dev/null
+++ b/gnu/usr.bin/cc/include/i386/i386.h
@@ -0,0 +1,1665 @@
+/* Definitions of target machine for GNU compiler for Intel 80386.
+ Copyright (C) 1988, 1992, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* The purpose of this file is to define the characteristics of the i386,
+ independent of assembler syntax or operating system.
+
+ Three other files build on this one to describe a specific assembler syntax:
+ bsd386.h, att386.h, and sun386.h.
+
+ The actual tm.h file for a particular system should include
+ this file, and then the file for the appropriate assembler syntax.
+
+ Many macros that specify assembler syntax are omitted entirely from
+ this file because they really belong in the files for particular
+ assemblers. These include AS1, AS2, AS3, RP, IP, LPREFIX, L_SIZE,
+ PUT_OP_SIZE, USE_STAR, ADDR_BEG, ADDR_END, PRINT_IREG, PRINT_SCALE,
+ PRINT_B_I_S, and many that start with ASM_ or end in ASM_OP. */
+
+/* Names to predefine in the preprocessor for this target machine. */
+
+#define I386 1
+
+/* Stubs for half-pic support if not OSF/1 reference platform. */
+
+#ifndef HALF_PIC_P
+#define HALF_PIC_P() 0
+#define HALF_PIC_NUMBER_PTRS 0
+#define HALF_PIC_NUMBER_REFS 0
+#define HALF_PIC_ENCODE(DECL)
+#define HALF_PIC_DECLARE(NAME)
+#define HALF_PIC_INIT() error ("half-pic init called on systems that don't support it.")
+#define HALF_PIC_ADDRESS_P(X) 0
+#define HALF_PIC_PTR(X) X
+#define HALF_PIC_FINISH(STREAM)
+#endif
+
+/* Run-time compilation parameters selecting different hardware subsets. */
+
+extern int target_flags;
+
+/* Macros used in the machine description to test the flags. */
+
+/* configure can arrage to make this 2, to force a 486. */
+#ifndef TARGET_CPU_DEFAULT
+#define TARGET_CPU_DEFAULT 0
+#endif
+
+/* Compile 80387 insns for floating point (not library calls). */
+#define TARGET_80387 (target_flags & 1)
+/* Compile code for an i486. */
+#define TARGET_486 (target_flags & 2)
+/* Compile using ret insn that pops args.
+ This will not work unless you use prototypes at least
+ for all functions that can take varying numbers of args. */
+#define TARGET_RTD (target_flags & 8)
+/* Compile passing first two args in regs 0 and 1.
+ This exists only to test compiler features that will
+ be needed for RISC chips. It is not usable
+ and is not intended to be usable on this cpu. */
+#define TARGET_REGPARM (target_flags & 020)
+
+/* Put uninitialized locals into bss, not data.
+ Meaningful only on svr3. */
+#define TARGET_SVR3_SHLIB (target_flags & 040)
+
+/* Use IEEE floating point comparisons. These handle correctly the cases
+ where the result of a comparison is unordered. Normally SIGFPE is
+ generated in such cases, in which case this isn't needed. */
+#define TARGET_IEEE_FP (target_flags & 0100)
+
+/* Functions that return a floating point value may return that value
+ in the 387 FPU or in 386 integer registers. If set, this flag causes
+ the 387 to be used, which is compatible with most calling conventions. */
+#define TARGET_FLOAT_RETURNS_IN_80387 (target_flags & 0200)
+
+/* Disable generation of FP sin, cos and sqrt operations for 387.
+ This is because FreeBSD lacks these in the math-emulator-code */
+#define TARGET_NO_FANCY_MATH_387 (target_flags & 0400)
+
+/* Macro to define tables used to set the flags.
+ This is a list in braces of pairs in braces,
+ each pair being { "NAME", VALUE }
+ where VALUE is the bits to set or minus the bits to clear.
+ An empty string NAME is used to identify the default VALUE. */
+
+#define TARGET_SWITCHES \
+ { { "80387", 1}, \
+ { "no-80387", -1}, \
+ { "soft-float", -1}, \
+ { "no-soft-float", 1}, \
+ { "486", 2}, \
+ { "no-486", -2}, \
+ { "386", -2}, \
+ { "rtd", 8}, \
+ { "no-rtd", -8}, \
+ { "regparm", 020}, \
+ { "no-regparm", -020}, \
+ { "svr3-shlib", 040}, \
+ { "no-svr3-shlib", -040}, \
+ { "ieee-fp", 0100}, \
+ { "no-ieee-fp", -0100}, \
+ { "fp-ret-in-387", 0200}, \
+ { "no-fp-ret-in-387", -0200}, \
+ { "no-fancy-math-387", 0400}, \
+ { "fancy-math-387", -0400}, \
+ SUBTARGET_SWITCHES \
+ { "", TARGET_DEFAULT | TARGET_CPU_DEFAULT}}
+
+/* This is meant to be redefined in the host dependent files */
+#define SUBTARGET_SWITCHES
+
+#define OVERRIDE_OPTIONS \
+{ \
+ SUBTARGET_OVERRIDE_OPTIONS \
+}
+
+/* This is meant to be redefined in the host dependent files */
+#define SUBTARGET_OVERRIDE_OPTIONS
+
+/* target machine storage layout */
+
+/* Define for XFmode extended real floating point support.
+ This will automatically cause REAL_ARITHMETIC to be defined. */
+#define LONG_DOUBLE_TYPE_SIZE 96
+
+/* Define if you don't want extended real, but do want to use the
+ software floating point emulator for REAL_ARITHMETIC and
+ decimal <-> binary conversion. */
+/* #define REAL_ARITHMETIC */
+
+/* Define this if most significant byte of a word is the lowest numbered. */
+/* That is true on the 80386. */
+
+#define BITS_BIG_ENDIAN 0
+
+/* Define this if most significant byte of a word is the lowest numbered. */
+/* That is not true on the 80386. */
+#define BYTES_BIG_ENDIAN 0
+
+/* Define this if most significant word of a multiword number is the lowest
+ numbered. */
+/* Not true for 80386 */
+#define WORDS_BIG_ENDIAN 0
+
+/* number of bits in an addressable storage unit */
+#define BITS_PER_UNIT 8
+
+/* Width in bits of a "word", which is the contents of a machine register.
+ Note that this is not necessarily the width of data type `int';
+ if using 16-bit ints on a 80386, this would still be 32.
+ But on a machine with 16-bit registers, this would be 16. */
+#define BITS_PER_WORD 32
+
+/* Width of a word, in units (bytes). */
+#define UNITS_PER_WORD 4
+
+/* Width in bits of a pointer.
+ See also the macro `Pmode' defined below. */
+#define POINTER_SIZE 32
+
+/* Allocation boundary (in *bits*) for storing arguments in argument list. */
+#define PARM_BOUNDARY 32
+
+/* Boundary (in *bits*) on which stack pointer should be aligned. */
+#define STACK_BOUNDARY 32
+
+/* Allocation boundary (in *bits*) for the code of a function.
+ For i486, we get better performance by aligning to a cache
+ line (i.e. 16 byte) boundary. */
+#define FUNCTION_BOUNDARY (TARGET_486 ? 128 : 32)
+
+/* Alignment of field after `int : 0' in a structure. */
+
+#define EMPTY_FIELD_BOUNDARY 32
+
+/* Minimum size in bits of the largest boundary to which any
+ and all fundamental data types supported by the hardware
+ might need to be aligned. No data type wants to be aligned
+ rounder than this. The i386 supports 64-bit floating point
+ quantities, but these can be aligned on any 32-bit boundary. */
+#define BIGGEST_ALIGNMENT 32
+
+/* Set this non-zero if move instructions will actually fail to work
+ when given unaligned data. */
+#define STRICT_ALIGNMENT 0
+
+/* If bit field type is int, don't let it cross an int,
+ and give entire struct the alignment of an int. */
+/* Required on the 386 since it doesn't have bitfield insns. */
+#define PCC_BITFIELD_TYPE_MATTERS 1
+
+/* Align loop starts for optimal branching. */
+#define ASM_OUTPUT_LOOP_ALIGN(FILE) \
+ ASM_OUTPUT_ALIGN (FILE, 2)
+
+/* This is how to align an instruction for optimal branching.
+ On i486 we'll get better performance by aligning on a
+ cache line (i.e. 16 byte) boundary. */
+#define ASM_OUTPUT_ALIGN_CODE(FILE) \
+ ASM_OUTPUT_ALIGN ((FILE), (TARGET_486 ? 4 : 2))
+
+/* Standard register usage. */
+
+/* This processor has special stack-like registers. See reg-stack.c
+ for details. */
+
+#define STACK_REGS
+
+/* Number of actual hardware registers.
+ The hardware registers are assigned numbers for the compiler
+ from 0 to just below FIRST_PSEUDO_REGISTER.
+ All registers that the compiler knows about must be given numbers,
+ even those that are not normally considered general registers.
+
+ In the 80386 we give the 8 general purpose registers the numbers 0-7.
+ We number the floating point registers 8-15.
+ Note that registers 0-7 can be accessed as a short or int,
+ while only 0-3 may be used with byte `mov' instructions.
+
+ Reg 16 does not correspond to any hardware register, but instead
+ appears in the RTL as an argument pointer prior to reload, and is
+ eliminated during reloading in favor of either the stack or frame
+ pointer. */
+
+#define FIRST_PSEUDO_REGISTER 17
+
+/* 1 for registers that have pervasive standard uses
+ and are not available for the register allocator.
+ On the 80386, the stack pointer is such, as is the arg pointer. */
+#define FIXED_REGISTERS \
+/*ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg*/ \
+{ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 }
+
+/* 1 for registers not available across function calls.
+ These must include the FIXED_REGISTERS and also any
+ registers that can be used without being saved.
+ The latter must include the registers where values are returned
+ and the register where structure-value addresses are passed.
+ Aside from that, you can include as many other registers as you like. */
+
+#define CALL_USED_REGISTERS \
+/*ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg*/ \
+{ 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
+
+/* Order in which to allocate registers. Each register must be
+ listed once, even those in FIXED_REGISTERS. List frame pointer
+ late and fixed registers last. Note that, in general, we prefer
+ registers listed in CALL_USED_REGISTERS, keeping the others
+ available for storage of persistent values. */
+
+#define REG_ALLOC_ORDER \
+/*ax,cx,dx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg*/ \
+{ 0, 2, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }
+
+/* Macro to conditionally modify fixed_regs/call_used_regs. */
+#define CONDITIONAL_REGISTER_USAGE \
+ { \
+ if (flag_pic) \
+ { \
+ fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1; \
+ call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1; \
+ } \
+ if (! TARGET_80387 && ! TARGET_FLOAT_RETURNS_IN_80387) \
+ { \
+ int i; \
+ HARD_REG_SET x; \
+ COPY_HARD_REG_SET (x, reg_class_contents[(int)FLOAT_REGS]); \
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++ ) \
+ if (TEST_HARD_REG_BIT (x, i)) \
+ fixed_regs[i] = call_used_regs[i] = 1; \
+ } \
+ }
+
+/* Return number of consecutive hard regs needed starting at reg REGNO
+ to hold something of mode MODE.
+ This is ordinarily the length in words of a value of mode MODE
+ but can be less for certain modes in special long registers.
+
+ Actually there are no two word move instructions for consecutive
+ registers. And only registers 0-3 may have mov byte instructions
+ applied to them.
+ */
+
+#define HARD_REGNO_NREGS(REGNO, MODE) \
+ (FP_REGNO_P (REGNO) ? 1 \
+ : ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD))
+
+/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
+ On the 80386, the first 4 cpu registers can hold any mode
+ while the floating point registers may hold only floating point.
+ Make it clear that the fp regs could not hold a 16-byte float. */
+
+/* The casts to int placate a compiler on a microvax,
+ for cross-compiler testing. */
+
+#define HARD_REGNO_MODE_OK(REGNO, MODE) \
+ ((REGNO) < 2 ? 1 \
+ : (REGNO) < 4 ? 1 \
+ : FP_REGNO_P (REGNO) \
+ ? (((int) GET_MODE_CLASS (MODE) == (int) MODE_FLOAT \
+ || (int) GET_MODE_CLASS (MODE) == (int) MODE_COMPLEX_FLOAT) \
+ && GET_MODE_UNIT_SIZE (MODE) <= 12) \
+ : (int) (MODE) != (int) QImode)
+
+/* Value is 1 if it is a good idea to tie two pseudo registers
+ when one has mode MODE1 and one has mode MODE2.
+ If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2,
+ for any hard reg, then this must be 0 for correct output. */
+
+#define MODES_TIEABLE_P(MODE1, MODE2) ((MODE1) == (MODE2))
+
+/* A C expression returning the cost of moving data from a register of class
+ CLASS1 to one of CLASS2.
+
+ On the i386, copying between floating-point and fixed-point
+ registers is expensive. */
+
+#define REGISTER_MOVE_COST(CLASS1, CLASS2) \
+ (((FLOAT_CLASS_P (CLASS1) && ! FLOAT_CLASS_P (CLASS2)) \
+ || (! FLOAT_CLASS_P (CLASS1) && FLOAT_CLASS_P (CLASS2))) ? 10 \
+ : 2)
+
+/* Specify the registers used for certain standard purposes.
+ The values of these macros are register numbers. */
+
+/* on the 386 the pc register is %eip, and is not usable as a general
+ register. The ordinary mov instructions won't work */
+/* #define PC_REGNUM */
+
+/* Register to use for pushing function arguments. */
+#define STACK_POINTER_REGNUM 7
+
+/* Base register for access to local variables of the function. */
+#define FRAME_POINTER_REGNUM 6
+
+/* First floating point reg */
+#define FIRST_FLOAT_REG 8
+
+/* First & last stack-like regs */
+#define FIRST_STACK_REG FIRST_FLOAT_REG
+#define LAST_STACK_REG (FIRST_FLOAT_REG + 7)
+
+/* Value should be nonzero if functions must have frame pointers.
+ Zero means the frame pointer need not be set up (and parms
+ may be accessed via the stack pointer) in functions that seem suitable.
+ This is computed in `reload', in reload1.c. */
+#define FRAME_POINTER_REQUIRED 0
+
+/* Base register for access to arguments of the function. */
+#define ARG_POINTER_REGNUM 16
+
+/* Register in which static-chain is passed to a function. */
+#define STATIC_CHAIN_REGNUM 2
+
+/* Register to hold the addressing base for position independent
+ code access to data items. */
+#define PIC_OFFSET_TABLE_REGNUM 3
+
+/* Register in which address to store a structure value
+ arrives in the function. On the 386, the prologue
+ copies this from the stack to register %eax. */
+#define STRUCT_VALUE_INCOMING 0
+
+/* Place in which caller passes the structure value address.
+ 0 means push the value on the stack like an argument. */
+#define STRUCT_VALUE 0
+
+/* Define the classes of registers for register constraints in the
+ machine description. Also define ranges of constants.
+
+ One of the classes must always be named ALL_REGS and include all hard regs.
+ If there is more than one class, another class must be named NO_REGS
+ and contain no registers.
+
+ The name GENERAL_REGS must be the name of a class (or an alias for
+ another name such as ALL_REGS). This is the class of registers
+ that is allowed by "g" or "r" in a register constraint.
+ Also, registers outside this class are allocated only when
+ instructions express preferences for them.
+
+ The classes must be numbered in nondecreasing order; that is,
+ a larger-numbered class must never be contained completely
+ in a smaller-numbered class.
+
+ For any two classes, it is very desirable that there be another
+ class that represents their union.
+
+ It might seem that class BREG is unnecessary, since no useful 386
+ opcode needs reg %ebx. But some systems pass args to the OS in ebx,
+ and the "b" register constraint is useful in asms for syscalls. */
+
+enum reg_class
+{
+ NO_REGS,
+ AREG, DREG, CREG, BREG,
+ AD_REGS, /* %eax/%edx for DImode */
+ Q_REGS, /* %eax %ebx %ecx %edx */
+ SIREG, DIREG,
+ INDEX_REGS, /* %eax %ebx %ecx %edx %esi %edi %ebp */
+ GENERAL_REGS, /* %eax %ebx %ecx %edx %esi %edi %ebp %esp */
+ FP_TOP_REG, FP_SECOND_REG, /* %st(0) %st(1) */
+ FLOAT_REGS,
+ ALL_REGS, LIM_REG_CLASSES
+};
+
+#define N_REG_CLASSES (int) LIM_REG_CLASSES
+
+#define FLOAT_CLASS_P(CLASS) (reg_class_subset_p (CLASS, FLOAT_REGS))
+
+/* Give names of register classes as strings for dump file. */
+
+#define REG_CLASS_NAMES \
+{ "NO_REGS", \
+ "AREG", "DREG", "CREG", "BREG", \
+ "AD_REGS", \
+ "Q_REGS", \
+ "SIREG", "DIREG", \
+ "INDEX_REGS", \
+ "GENERAL_REGS", \
+ "FP_TOP_REG", "FP_SECOND_REG", \
+ "FLOAT_REGS", \
+ "ALL_REGS" }
+
+/* Define which registers fit in which classes.
+ This is an initializer for a vector of HARD_REG_SET
+ of length N_REG_CLASSES. */
+
+#define REG_CLASS_CONTENTS \
+{ 0, \
+ 0x1, 0x2, 0x4, 0x8, /* AREG, DREG, CREG, BREG */ \
+ 0x3, /* AD_REGS */ \
+ 0xf, /* Q_REGS */ \
+ 0x10, 0x20, /* SIREG, DIREG */ \
+ 0x1007f, /* INDEX_REGS */ \
+ 0x100ff, /* GENERAL_REGS */ \
+ 0x0100, 0x0200, /* FP_TOP_REG, FP_SECOND_REG */ \
+ 0xff00, /* FLOAT_REGS */ \
+ 0x1ffff }
+
+/* The same information, inverted:
+ Return the class number of the smallest class containing
+ reg number REGNO. This could be a conditional expression
+ or could index an array. */
+
+extern enum reg_class regclass_map[FIRST_PSEUDO_REGISTER];
+#define REGNO_REG_CLASS(REGNO) (regclass_map[REGNO])
+
+/* When defined, the compiler allows registers explicitly used in the
+ rtl to be used as spill registers but prevents the compiler from
+ extending the lifetime of these registers. */
+
+#define SMALL_REGISTER_CLASSES
+
+#define QI_REG_P(X) \
+ (REG_P (X) && REGNO (X) < 4)
+#define NON_QI_REG_P(X) \
+ (REG_P (X) && REGNO (X) >= 4 && REGNO (X) < FIRST_PSEUDO_REGISTER)
+
+#define FP_REG_P(X) (REG_P (X) && FP_REGNO_P (REGNO (X)))
+#define FP_REGNO_P(n) ((n) >= FIRST_STACK_REG && (n) <= LAST_STACK_REG)
+
+#define STACK_REG_P(xop) (REG_P (xop) && \
+ REGNO (xop) >= FIRST_STACK_REG && \
+ REGNO (xop) <= LAST_STACK_REG)
+
+#define NON_STACK_REG_P(xop) (REG_P (xop) && ! STACK_REG_P (xop))
+
+#define STACK_TOP_P(xop) (REG_P (xop) && REGNO (xop) == FIRST_STACK_REG)
+
+/* Try to maintain the accuracy of the death notes for regs satisfying the
+ following. Important for stack like regs, to know when to pop. */
+
+/* #define PRESERVE_DEATH_INFO_REGNO_P(x) FP_REGNO_P(x) */
+
+/* 1 if register REGNO can magically overlap other regs.
+ Note that nonzero values work only in very special circumstances. */
+
+/* #define OVERLAPPING_REGNO_P(REGNO) FP_REGNO_P (REGNO) */
+
+/* The class value for index registers, and the one for base regs. */
+
+#define INDEX_REG_CLASS INDEX_REGS
+#define BASE_REG_CLASS GENERAL_REGS
+
+/* Get reg_class from a letter such as appears in the machine description. */
+
+#define REG_CLASS_FROM_LETTER(C) \
+ ((C) == 'r' ? GENERAL_REGS : \
+ (C) == 'q' ? Q_REGS : \
+ (C) == 'f' ? (TARGET_80387 || TARGET_FLOAT_RETURNS_IN_80387 \
+ ? FLOAT_REGS \
+ : NO_REGS) : \
+ (C) == 't' ? (TARGET_80387 || TARGET_FLOAT_RETURNS_IN_80387 \
+ ? FP_TOP_REG \
+ : NO_REGS) : \
+ (C) == 'u' ? (TARGET_80387 || TARGET_FLOAT_RETURNS_IN_80387 \
+ ? FP_SECOND_REG \
+ : NO_REGS) : \
+ (C) == 'a' ? AREG : \
+ (C) == 'b' ? BREG : \
+ (C) == 'c' ? CREG : \
+ (C) == 'd' ? DREG : \
+ (C) == 'A' ? AD_REGS : \
+ (C) == 'D' ? DIREG : \
+ (C) == 'S' ? SIREG : NO_REGS)
+
+/* The letters I, J, K, L and M in a register constraint string
+ can be used to stand for particular ranges of immediate operands.
+ This macro defines what the ranges are.
+ C is the letter, and VALUE is a constant value.
+ Return 1 if VALUE is in the range specified by C.
+
+ I is for non-DImode shifts.
+ J is for DImode shifts.
+ K and L are for an `andsi' optimization.
+ M is for shifts that can be executed by the "lea" opcode.
+ */
+
+#define CONST_OK_FOR_LETTER_P(VALUE, C) \
+ ((C) == 'I' ? (VALUE) >= 0 && (VALUE) <= 31 : \
+ (C) == 'J' ? (VALUE) >= 0 && (VALUE) <= 63 : \
+ (C) == 'K' ? (VALUE) == 0xff : \
+ (C) == 'L' ? (VALUE) == 0xffff : \
+ (C) == 'M' ? (VALUE) >= 0 && (VALUE) <= 3 : \
+ 0)
+
+/* Similar, but for floating constants, and defining letters G and H.
+ Here VALUE is the CONST_DOUBLE rtx itself. We allow constants even if
+ TARGET_387 isn't set, because the stack register converter may need to
+ load 0.0 into the function value register. */
+
+#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) \
+ ((C) == 'G' ? standard_80387_constant_p (VALUE) : 0)
+
+/* Place additional restrictions on the register class to use when it
+ is necessary to be able to hold a value of mode MODE in a reload
+ register for which class CLASS would ordinarily be used. */
+
+#define LIMIT_RELOAD_CLASS(MODE, CLASS) \
+ ((MODE) == QImode && ((CLASS) == ALL_REGS || (CLASS) == GENERAL_REGS) \
+ ? Q_REGS : (CLASS))
+
+/* Given an rtx X being reloaded into a reg required to be
+ in class CLASS, return the class of reg to actually use.
+ In general this is just CLASS; but on some machines
+ in some cases it is preferable to use a more restrictive class.
+ On the 80386 series, we prevent floating constants from being
+ reloaded into floating registers (since no move-insn can do that)
+ and we ensure that QImodes aren't reloaded into the esi or edi reg. */
+
+/* Put float CONST_DOUBLE in the constant pool instead of fp regs.
+ QImode must go into class Q_REGS.
+ Narrow ALL_REGS to GENERAL_REGS. This supports allowing movsf and
+ movdf to do mem-to-mem moves through integer regs. */
+
+#define PREFERRED_RELOAD_CLASS(X,CLASS) \
+ (GET_CODE (X) == CONST_DOUBLE && GET_MODE (X) != VOIDmode ? NO_REGS \
+ : GET_MODE (X) == QImode && ! reg_class_subset_p (CLASS, Q_REGS) ? Q_REGS \
+ : ((CLASS) == ALL_REGS \
+ && GET_MODE_CLASS (GET_MODE (X)) == MODE_FLOAT) ? GENERAL_REGS \
+ : (CLASS))
+
+/* If we are copying between general and FP registers, we need a memory
+ location. */
+
+#define SECONDARY_MEMORY_NEEDED(CLASS1,CLASS2,MODE) \
+ ((FLOAT_CLASS_P (CLASS1) && ! FLOAT_CLASS_P (CLASS2)) \
+ || (! FLOAT_CLASS_P (CLASS1) && FLOAT_CLASS_P (CLASS2)))
+
+/* Return the maximum number of consecutive registers
+ needed to represent mode MODE in a register of class CLASS. */
+/* On the 80386, this is the size of MODE in words,
+ except in the FP regs, where a single reg is always enough. */
+#define CLASS_MAX_NREGS(CLASS, MODE) \
+ (FLOAT_CLASS_P (CLASS) ? 1 : \
+ ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD))
+
+/* Stack layout; function entry, exit and calling. */
+
+/* Define this if pushing a word on the stack
+ makes the stack pointer a smaller address. */
+#define STACK_GROWS_DOWNWARD
+
+/* Define this if the nominal address of the stack frame
+ is at the high-address end of the local variables;
+ that is, each additional local variable allocated
+ goes at a more negative offset in the frame. */
+#define FRAME_GROWS_DOWNWARD
+
+/* Offset within stack frame to start allocating local variables at.
+ If FRAME_GROWS_DOWNWARD, this is the offset to the END of the
+ first local allocated. Otherwise, it is the offset to the BEGINNING
+ of the first local allocated. */
+#define STARTING_FRAME_OFFSET 0
+
+/* If we generate an insn to push BYTES bytes,
+ this says how many the stack pointer really advances by.
+ On 386 pushw decrements by exactly 2 no matter what the position was.
+ On the 386 there is no pushb; we use pushw instead, and this
+ has the effect of rounding up to 2. */
+
+#define PUSH_ROUNDING(BYTES) (((BYTES) + 1) & (-2))
+
+/* Offset of first parameter from the argument pointer register value. */
+#define FIRST_PARM_OFFSET(FNDECL) 0
+
+/* Value is the number of bytes of arguments automatically
+ popped when returning from a subroutine call.
+ FUNTYPE is the data type of the function (as a tree),
+ or for a library call it is an identifier node for the subroutine name.
+ SIZE is the number of bytes of arguments passed on the stack.
+
+ On the 80386, the RTD insn may be used to pop them if the number
+ of args is fixed, but if the number is variable then the caller
+ must pop them all. RTD can't be used for library calls now
+ because the library is compiled with the Unix compiler.
+ Use of RTD is a selectable option, since it is incompatible with
+ standard Unix calling sequences. If the option is not selected,
+ the caller must always pop the args. */
+
+#define RETURN_POPS_ARGS(FUNTYPE,SIZE) \
+ (TREE_CODE (FUNTYPE) == IDENTIFIER_NODE ? 0 \
+ : (TARGET_RTD \
+ && (TYPE_ARG_TYPES (FUNTYPE) == 0 \
+ || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (FUNTYPE))) \
+ == void_type_node))) ? (SIZE) \
+ : (aggregate_value_p (TREE_TYPE (FUNTYPE))) ? GET_MODE_SIZE (Pmode) : 0)
+
+/* Define how to find the value returned by a function.
+ VALTYPE is the data type of the value (as a tree).
+ If the precise function being called is known, FUNC is its FUNCTION_DECL;
+ otherwise, FUNC is 0. */
+#define FUNCTION_VALUE(VALTYPE, FUNC) \
+ gen_rtx (REG, TYPE_MODE (VALTYPE), \
+ VALUE_REGNO (TYPE_MODE (VALTYPE)))
+
+/* Define how to find the value returned by a library function
+ assuming the value has mode MODE. */
+
+#define LIBCALL_VALUE(MODE) \
+ gen_rtx (REG, MODE, VALUE_REGNO (MODE))
+
+/* Define the size of the result block used for communication between
+ untyped_call and untyped_return. The block contains a DImode value
+ followed by the block used by fnsave and frstor. */
+
+#define APPLY_RESULT_SIZE (8+108)
+
+/* 1 if N is a possible register number for function argument passing.
+ On the 80386, no registers are used in this way.
+ *NOTE* -mregparm does not work.
+ It exists only to test register calling conventions. */
+
+#define FUNCTION_ARG_REGNO_P(N) 0
+
+/* Define a data type for recording info about an argument list
+ during the scan of that argument list. This data type should
+ hold all necessary information about the function itself
+ and about the args processed so far, enough to enable macros
+ such as FUNCTION_ARG to determine where the next arg should go.
+
+ On the 80386, this is a single integer, which is a number of bytes
+ of arguments scanned so far. */
+
+#define CUMULATIVE_ARGS int
+
+/* Initialize a variable CUM of type CUMULATIVE_ARGS
+ for a call to a function whose data type is FNTYPE.
+ For a library call, FNTYPE is 0.
+
+ On the 80386, the offset starts at 0. */
+
+#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME) \
+ ((CUM) = 0)
+
+/* Update the data in CUM to advance over an argument
+ of mode MODE and data type TYPE.
+ (TYPE is null for libcalls where that information may not be available.) */
+
+#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \
+ ((CUM) += ((MODE) != BLKmode \
+ ? (GET_MODE_SIZE (MODE) + 3) & ~3 \
+ : (int_size_in_bytes (TYPE) + 3) & ~3))
+
+/* Define where to put the arguments to a function.
+ Value is zero to push the argument on the stack,
+ or a hard register in which to store the argument.
+
+ MODE is the argument's machine mode.
+ TYPE is the data type of the argument (as a tree).
+ This is null for libcalls where that information may
+ not be available.
+ CUM is a variable of type CUMULATIVE_ARGS which gives info about
+ the preceding args and about the function being called.
+ NAMED is nonzero if this argument is a named parameter
+ (otherwise it is an extra parameter matching an ellipsis). */
+
+
+/* On the 80386 all args are pushed, except if -mregparm is specified
+ then the first two words of arguments are passed in EAX, EDX.
+ *NOTE* -mregparm does not work.
+ It exists only to test register calling conventions. */
+
+#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
+((TARGET_REGPARM && (CUM) < 8) ? gen_rtx (REG, (MODE), (CUM) / 4) : 0)
+
+/* For an arg passed partly in registers and partly in memory,
+ this is the number of registers used.
+ For args passed entirely in registers or entirely in memory, zero. */
+
+
+#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) \
+((TARGET_REGPARM && (CUM) < 8 \
+ && 8 < ((CUM) + ((MODE) == BLKmode \
+ ? int_size_in_bytes (TYPE) \
+ : GET_MODE_SIZE (MODE)))) \
+ ? 2 - (CUM) / 4 : 0)
+
+/* This macro generates the assembly code for function entry.
+ FILE is a stdio stream to output the code to.
+ SIZE is an int: how many units of temporary storage to allocate.
+ Refer to the array `regs_ever_live' to determine which registers
+ to save; `regs_ever_live[I]' is nonzero if register number I
+ is ever used in the function. This macro is responsible for
+ knowing which registers should not be saved even if used. */
+
+#define FUNCTION_PROLOGUE(FILE, SIZE) \
+ function_prologue (FILE, SIZE)
+
+/* Output assembler code to FILE to increment profiler label # LABELNO
+ for profiling a function entry. */
+
+#define FUNCTION_PROFILER(FILE, LABELNO) \
+{ \
+ if (flag_pic) \
+ { \
+ fprintf (FILE, "\tleal %sP%d@GOTOFF(%%ebx),%%edx\n", \
+ LPREFIX, (LABELNO)); \
+ fprintf (FILE, "\tcall *_mcount@GOT(%%ebx)\n"); \
+ } \
+ else \
+ { \
+ fprintf (FILE, "\tmovl $%sP%d,%%edx\n", LPREFIX, (LABELNO)); \
+ fprintf (FILE, "\tcall _mcount\n"); \
+ } \
+}
+
+/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function,
+ the stack pointer does not matter. The value is tested only in
+ functions that have frame pointers.
+ No definition is equivalent to always zero. */
+/* Note on the 386 it might be more efficient not to define this since
+ we have to restore it ourselves from the frame pointer, in order to
+ use pop */
+
+#define EXIT_IGNORE_STACK 1
+
+/* This macro generates the assembly code for function exit,
+ on machines that need it. If FUNCTION_EPILOGUE is not defined
+ then individual return instructions are generated for each
+ return statement. Args are same as for FUNCTION_PROLOGUE.
+
+ The function epilogue should not depend on the current stack pointer!
+ It should use the frame pointer only. This is mandatory because
+ of alloca; we also take advantage of it to omit stack adjustments
+ before returning.
+
+ If the last non-note insn in the function is a BARRIER, then there
+ is no need to emit a function prologue, because control does not fall
+ off the end. This happens if the function ends in an "exit" call, or
+ if a `return' insn is emitted directly into the function. */
+
+#define FUNCTION_EPILOGUE(FILE, SIZE) \
+do { \
+ rtx last = get_last_insn (); \
+ if (last && GET_CODE (last) == NOTE) \
+ last = prev_nonnote_insn (last); \
+ if (! last || GET_CODE (last) != BARRIER) \
+ function_epilogue (FILE, SIZE); \
+} while (0)
+
+/* Output assembler code for a block containing the constant parts
+ of a trampoline, leaving space for the variable parts. */
+
+/* On the 386, the trampoline contains three instructions:
+ mov #STATIC,ecx
+ mov #FUNCTION,eax
+ jmp @eax */
+#define TRAMPOLINE_TEMPLATE(FILE) \
+{ \
+ ASM_OUTPUT_CHAR (FILE, GEN_INT (0xb9)); \
+ ASM_OUTPUT_SHORT (FILE, const0_rtx); \
+ ASM_OUTPUT_SHORT (FILE, const0_rtx); \
+ ASM_OUTPUT_CHAR (FILE, GEN_INT (0xb8)); \
+ ASM_OUTPUT_SHORT (FILE, const0_rtx); \
+ ASM_OUTPUT_SHORT (FILE, const0_rtx); \
+ ASM_OUTPUT_CHAR (FILE, GEN_INT (0xff)); \
+ ASM_OUTPUT_CHAR (FILE, GEN_INT (0xe0)); \
+}
+
+/* Length in units of the trampoline for entering a nested function. */
+
+#define TRAMPOLINE_SIZE 12
+
+/* Emit RTL insns to initialize the variable parts of a trampoline.
+ FNADDR is an RTX for the address of the function's pure code.
+ CXT is an RTX for the static chain value for the function. */
+
+#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \
+{ \
+ emit_move_insn (gen_rtx (MEM, SImode, plus_constant (TRAMP, 1)), CXT); \
+ emit_move_insn (gen_rtx (MEM, SImode, plus_constant (TRAMP, 6)), FNADDR); \
+}
+
+/* Definitions for register eliminations.
+
+ This is an array of structures. Each structure initializes one pair
+ of eliminable registers. The "from" register number is given first,
+ followed by "to". Eliminations of the same "from" register are listed
+ in order of preference.
+
+ We have two registers that can be eliminated on the i386. First, the
+ frame pointer register can often be eliminated in favor of the stack
+ pointer register. Secondly, the argument pointer register can always be
+ eliminated; it is replaced with either the stack or frame pointer. */
+
+#define ELIMINABLE_REGS \
+{{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
+ { ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \
+ { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}}
+
+/* Given FROM and TO register numbers, say whether this elimination is allowed.
+ Frame pointer elimination is automatically handled.
+
+ For the i386, if frame pointer elimination is being done, we would like to
+ convert ap into sp, not fp.
+
+ All other eliminations are valid. */
+
+#define CAN_ELIMINATE(FROM, TO) \
+ ((FROM) == ARG_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM \
+ ? ! frame_pointer_needed \
+ : 1)
+
+/* Define the offset between two registers, one to be eliminated, and the other
+ its replacement, at the start of a routine. */
+
+#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
+{ \
+ if ((FROM) == ARG_POINTER_REGNUM && (TO) == FRAME_POINTER_REGNUM) \
+ (OFFSET) = 8; /* Skip saved PC and previous frame pointer */ \
+ else \
+ { \
+ int regno; \
+ int offset = 0; \
+ \
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) \
+ if ((regs_ever_live[regno] && ! call_used_regs[regno]) \
+ || (current_function_uses_pic_offset_table \
+ && regno == PIC_OFFSET_TABLE_REGNUM)) \
+ offset += 4; \
+ \
+ (OFFSET) = offset + get_frame_size (); \
+ \
+ if ((FROM) == ARG_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM) \
+ (OFFSET) += 4; /* Skip saved PC */ \
+ } \
+}
+
+/* Addressing modes, and classification of registers for them. */
+
+/* #define HAVE_POST_INCREMENT */
+/* #define HAVE_POST_DECREMENT */
+
+/* #define HAVE_PRE_DECREMENT */
+/* #define HAVE_PRE_INCREMENT */
+
+/* Macros to check register numbers against specific register classes. */
+
+/* These assume that REGNO is a hard or pseudo reg number.
+ They give nonzero only if REGNO is a hard reg of the suitable class
+ or a pseudo reg currently allocated to a suitable hard reg.
+ Since they use reg_renumber, they are safe only once reg_renumber
+ has been allocated, which happens in local-alloc.c. */
+
+#define REGNO_OK_FOR_INDEX_P(REGNO) \
+ ((REGNO) < STACK_POINTER_REGNUM \
+ || (unsigned) reg_renumber[REGNO] < STACK_POINTER_REGNUM)
+
+#define REGNO_OK_FOR_BASE_P(REGNO) \
+ ((REGNO) <= STACK_POINTER_REGNUM \
+ || (REGNO) == ARG_POINTER_REGNUM \
+ || (unsigned) reg_renumber[REGNO] <= STACK_POINTER_REGNUM)
+
+#define REGNO_OK_FOR_SIREG_P(REGNO) ((REGNO) == 4 || reg_renumber[REGNO] == 4)
+#define REGNO_OK_FOR_DIREG_P(REGNO) ((REGNO) == 5 || reg_renumber[REGNO] == 5)
+
+/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx
+ and check its validity for a certain class.
+ We have two alternate definitions for each of them.
+ The usual definition accepts all pseudo regs; the other rejects
+ them unless they have been allocated suitable hard regs.
+ The symbol REG_OK_STRICT causes the latter definition to be used.
+
+ Most source files want to accept pseudo regs in the hope that
+ they will get allocated to the class that the insn wants them to be in.
+ Source files for reload pass need to be strict.
+ After reload, it makes no difference, since pseudo regs have
+ been eliminated by then. */
+
+#ifndef REG_OK_STRICT
+
+/* Nonzero if X is a hard reg that can be used as an index or if
+ it is a pseudo reg. */
+
+#define REG_OK_FOR_INDEX_P(X) \
+ (REGNO (X) < STACK_POINTER_REGNUM \
+ || REGNO (X) >= FIRST_PSEUDO_REGISTER)
+
+/* Nonzero if X is a hard reg that can be used as a base reg
+ of if it is a pseudo reg. */
+ /* ?wfs */
+
+#define REG_OK_FOR_BASE_P(X) \
+ (REGNO (X) <= STACK_POINTER_REGNUM \
+ || REGNO (X) == ARG_POINTER_REGNUM \
+ || REGNO(X) >= FIRST_PSEUDO_REGISTER)
+
+#define REG_OK_FOR_STRREG_P(X) \
+ (REGNO (X) == 4 || REGNO (X) == 5 || REGNO (X) >= FIRST_PSEUDO_REGISTER)
+
+#else
+
+/* Nonzero if X is a hard reg that can be used as an index. */
+#define REG_OK_FOR_INDEX_P(X) REGNO_OK_FOR_INDEX_P (REGNO (X))
+/* Nonzero if X is a hard reg that can be used as a base reg. */
+#define REG_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_P (REGNO (X))
+#define REG_OK_FOR_STRREG_P(X) \
+ (REGNO_OK_FOR_DIREG_P (REGNO (X)) || REGNO_OK_FOR_SIREG_P (REGNO (X)))
+
+#endif
+
+/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression
+ that is a valid memory address for an instruction.
+ The MODE argument is the machine mode for the MEM expression
+ that wants to use this address.
+
+ The other macros defined here are used only in GO_IF_LEGITIMATE_ADDRESS,
+ except for CONSTANT_ADDRESS_P which is usually machine-independent.
+
+ See legitimize_pic_address in i386.c for details as to what
+ constitutes a legitimate address when -fpic is used. */
+
+#define MAX_REGS_PER_ADDRESS 2
+
+#define CONSTANT_ADDRESS_P(X) \
+ (GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF \
+ || GET_CODE (X) == CONST_INT || GET_CODE (X) == CONST \
+ || GET_CODE (X) == HIGH)
+
+/* Nonzero if the constant value X is a legitimate general operand.
+ It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. */
+
+#define LEGITIMATE_CONSTANT_P(X) 1
+
+#define GO_IF_INDEXABLE_BASE(X, ADDR) \
+ if (GET_CODE (X) == REG && REG_OK_FOR_BASE_P (X)) goto ADDR
+
+#define LEGITIMATE_INDEX_REG_P(X) \
+ (GET_CODE (X) == REG && REG_OK_FOR_INDEX_P (X))
+
+/* Return 1 if X is an index or an index times a scale. */
+
+#define LEGITIMATE_INDEX_P(X) \
+ (LEGITIMATE_INDEX_REG_P (X) \
+ || (GET_CODE (X) == MULT \
+ && LEGITIMATE_INDEX_REG_P (XEXP (X, 0)) \
+ && GET_CODE (XEXP (X, 1)) == CONST_INT \
+ && (INTVAL (XEXP (X, 1)) == 2 \
+ || INTVAL (XEXP (X, 1)) == 4 \
+ || INTVAL (XEXP (X, 1)) == 8)))
+
+/* Go to ADDR if X is an index term, a base reg, or a sum of those. */
+
+#define GO_IF_INDEXING(X, ADDR) \
+{ if (LEGITIMATE_INDEX_P (X)) goto ADDR; \
+ GO_IF_INDEXABLE_BASE (X, ADDR); \
+ if (GET_CODE (X) == PLUS && LEGITIMATE_INDEX_P (XEXP (X, 0))) \
+ { GO_IF_INDEXABLE_BASE (XEXP (X, 1), ADDR); } \
+ if (GET_CODE (X) == PLUS && LEGITIMATE_INDEX_P (XEXP (X, 1))) \
+ { GO_IF_INDEXABLE_BASE (XEXP (X, 0), ADDR); } }
+
+/* We used to allow this, but it isn't ever used.
+ || ((GET_CODE (X) == POST_DEC || GET_CODE (X) == POST_INC) \
+ && REG_P (XEXP (X, 0)) \
+ && REG_OK_FOR_STRREG_P (XEXP (X, 0))) \
+*/
+
+#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \
+{ \
+ if (CONSTANT_ADDRESS_P (X) \
+ && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (X))) \
+ goto ADDR; \
+ GO_IF_INDEXING (X, ADDR); \
+ if (GET_CODE (X) == PLUS && CONSTANT_ADDRESS_P (XEXP (X, 1))) \
+ { \
+ rtx x0 = XEXP (X, 0); \
+ if (! flag_pic || ! SYMBOLIC_CONST (XEXP (X, 1))) \
+ { GO_IF_INDEXING (x0, ADDR); } \
+ else if (x0 == pic_offset_table_rtx) \
+ goto ADDR; \
+ else if (GET_CODE (x0) == PLUS) \
+ { \
+ if (XEXP (x0, 0) == pic_offset_table_rtx) \
+ { GO_IF_INDEXABLE_BASE (XEXP (x0, 1), ADDR); } \
+ if (XEXP (x0, 1) == pic_offset_table_rtx) \
+ { GO_IF_INDEXABLE_BASE (XEXP (x0, 0), ADDR); } \
+ } \
+ } \
+}
+
+/* Try machine-dependent ways of modifying an illegitimate address
+ to be legitimate. If we find one, return the new, valid address.
+ This macro is used in only one place: `memory_address' in explow.c.
+
+ OLDX is the address as it was before break_out_memory_refs was called.
+ In some cases it is useful to look at this to decide what needs to be done.
+
+ MODE and WIN are passed so that this macro can use
+ GO_IF_LEGITIMATE_ADDRESS.
+
+ It is always safe for this macro to do nothing. It exists to recognize
+ opportunities to optimize the output.
+
+ For the 80386, we handle X+REG by loading X into a register R and
+ using R+REG. R will go in a general reg and indexing will be used.
+ However, if REG is a broken-out memory address or multiplication,
+ nothing needs to be done because REG can certainly go in a general reg.
+
+ When -fpic is used, special handling is needed for symbolic references.
+ See comments by legitimize_pic_address in i386.c for details. */
+
+#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN) \
+{ extern rtx legitimize_pic_address (); \
+ int ch = (X) != (OLDX); \
+ if (flag_pic && SYMBOLIC_CONST (X)) \
+ { \
+ (X) = legitimize_pic_address (X, 0); \
+ if (memory_address_p (MODE, X)) \
+ goto WIN; \
+ } \
+ if (GET_CODE (X) == PLUS) \
+ { if (GET_CODE (XEXP (X, 0)) == MULT) \
+ ch = 1, XEXP (X, 0) = force_operand (XEXP (X, 0), 0); \
+ if (GET_CODE (XEXP (X, 1)) == MULT) \
+ ch = 1, XEXP (X, 1) = force_operand (XEXP (X, 1), 0); \
+ if (ch && GET_CODE (XEXP (X, 1)) == REG \
+ && GET_CODE (XEXP (X, 0)) == REG) \
+ goto WIN; \
+ if (flag_pic && SYMBOLIC_CONST (XEXP (X, 1))) \
+ ch = 1, (X) = legitimize_pic_address (X, 0); \
+ if (ch) { GO_IF_LEGITIMATE_ADDRESS (MODE, X, WIN); } \
+ if (GET_CODE (XEXP (X, 0)) == REG) \
+ { register rtx temp = gen_reg_rtx (Pmode); \
+ register rtx val = force_operand (XEXP (X, 1), temp); \
+ if (val != temp) emit_move_insn (temp, val); \
+ XEXP (X, 1) = temp; \
+ goto WIN; } \
+ else if (GET_CODE (XEXP (X, 1)) == REG) \
+ { register rtx temp = gen_reg_rtx (Pmode); \
+ register rtx val = force_operand (XEXP (X, 0), temp); \
+ if (val != temp) emit_move_insn (temp, val); \
+ XEXP (X, 0) = temp; \
+ goto WIN; }}}
+
+/* Nonzero if the constant value X is a legitimate general operand
+ when generating PIC code. It is given that flag_pic is on and
+ that X satisfies CONSTANT_P or is a CONST_DOUBLE. */
+
+#define LEGITIMATE_PIC_OPERAND_P(X) \
+ (! SYMBOLIC_CONST (X) \
+ || (GET_CODE (X) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (X)))
+
+#define SYMBOLIC_CONST(X) \
+(GET_CODE (X) == SYMBOL_REF \
+ || GET_CODE (X) == LABEL_REF \
+ || (GET_CODE (X) == CONST && symbolic_reference_mentioned_p (X)))
+
+/* Go to LABEL if ADDR (a legitimate address expression)
+ has an effect that depends on the machine mode it is used for.
+ On the 80386, only postdecrement and postincrement address depend thus
+ (the amount of decrement or increment being the length of the operand). */
+#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL) \
+ if (GET_CODE (ADDR) == POST_INC || GET_CODE (ADDR) == POST_DEC) goto LABEL
+
+/* Define this macro if references to a symbol must be treated
+ differently depending on something about the variable or
+ function named by the symbol (such as what section it is in).
+
+ On i386, if using PIC, mark a SYMBOL_REF for a non-global symbol
+ so that we may access it directly in the GOT. */
+
+#define ENCODE_SECTION_INFO(DECL) \
+do \
+ { \
+ if (flag_pic) \
+ { \
+ rtx rtl = (TREE_CODE_CLASS (TREE_CODE (DECL)) != 'd' \
+ ? TREE_CST_RTL (DECL) : DECL_RTL (DECL)); \
+ SYMBOL_REF_FLAG (XEXP (rtl, 0)) \
+ = (TREE_CODE_CLASS (TREE_CODE (DECL)) != 'd' \
+ || ! TREE_PUBLIC (DECL)); \
+ } \
+ } \
+while (0)
+
+/* Initialize data used by insn expanders. This is called from
+ init_emit, once for each function, before code is generated.
+ For 386, clear stack slot assignments remembered from previous
+ functions. */
+
+#define INIT_EXPANDERS clear_386_stack_locals ()
+
+/* The `FINALIZE_PIC' macro serves as a hook to emit these special
+ codes once the function is being compiled into assembly code, but
+ not before. (It is not done before, because in the case of
+ compiling an inline function, it would lead to multiple PIC
+ prologues being included in functions which used inline functions
+ and were compiled to assembly language.) */
+
+#define FINALIZE_PIC \
+do \
+ { \
+ extern int current_function_uses_pic_offset_table; \
+ \
+ current_function_uses_pic_offset_table |= profile_flag | profile_block_flag; \
+ } \
+while (0)
+
+
+/* Specify the machine mode that this machine uses
+ for the index in the tablejump instruction. */
+#define CASE_VECTOR_MODE Pmode
+
+/* Define this if the tablejump instruction expects the table
+ to contain offsets from the address of the table.
+ Do not define this if the table should contain absolute addresses. */
+/* #define CASE_VECTOR_PC_RELATIVE */
+
+/* Specify the tree operation to be used to convert reals to integers.
+ This should be changed to take advantage of fist --wfs ??
+ */
+#define IMPLICIT_FIX_EXPR FIX_ROUND_EXPR
+
+/* This is the kind of divide that is easiest to do in the general case. */
+#define EASY_DIV_EXPR TRUNC_DIV_EXPR
+
+/* Define this as 1 if `char' should by default be signed; else as 0. */
+#define DEFAULT_SIGNED_CHAR 1
+
+/* Max number of bytes we can move from memory to memory
+ in one reasonably fast instruction. */
+#define MOVE_MAX 4
+
+/* MOVE_RATIO is the number of move instructions that is better than a
+ block move. Make this large on i386, since the block move is very
+ inefficient with small blocks, and the hard register needs of the
+ block move require much reload work. */
+#define MOVE_RATIO 5
+
+/* Define this if zero-extension is slow (more than one real instruction). */
+/* #define SLOW_ZERO_EXTEND */
+
+/* Nonzero if access to memory by bytes is slow and undesirable. */
+#define SLOW_BYTE_ACCESS 0
+
+/* Define if shifts truncate the shift count
+ which implies one can omit a sign-extension or zero-extension
+ of a shift count. */
+/* One i386, shifts do truncate the count. But bit opcodes don't. */
+
+/* #define SHIFT_COUNT_TRUNCATED */
+
+/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits
+ is done just by pretending it is already truncated. */
+#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1
+
+/* We assume that the store-condition-codes instructions store 0 for false
+ and some other value for true. This is the value stored for true. */
+
+#define STORE_FLAG_VALUE 1
+
+/* When a prototype says `char' or `short', really pass an `int'.
+ (The 386 can't easily push less than an int.) */
+
+#define PROMOTE_PROTOTYPES
+
+/* Specify the machine mode that pointers have.
+ After generation of rtl, the compiler makes no further distinction
+ between pointers and any other objects of this machine mode. */
+#define Pmode SImode
+
+/* A function address in a call instruction
+ is a byte address (for indexing purposes)
+ so give the MEM rtx a byte's mode. */
+#define FUNCTION_MODE QImode
+
+/* Define this if addresses of constant functions
+ shouldn't be put through pseudo regs where they can be cse'd.
+ Desirable on the 386 because a CALL with a constant address is
+ not much slower than one with a register address. */
+#define NO_FUNCTION_CSE
+
+/* Provide the costs of a rtl expression. This is in the body of a
+ switch on CODE. */
+
+#define RTX_COSTS(X,CODE,OUTER_CODE) \
+ case MULT: \
+ return COSTS_N_INSNS (10); \
+ case DIV: \
+ case UDIV: \
+ case MOD: \
+ case UMOD: \
+ return COSTS_N_INSNS (40); \
+ case PLUS: \
+ if (GET_CODE (XEXP (X, 0)) == REG \
+ && GET_CODE (XEXP (X, 1)) == CONST_INT) \
+ return 1; \
+ break;
+
+
+/* Compute the cost of computing a constant rtl expression RTX
+ whose rtx-code is CODE. The body of this macro is a portion
+ of a switch statement. If the code is computed here,
+ return it with a return statement. Otherwise, break from the switch. */
+
+#define CONST_COSTS(RTX,CODE,OUTER_CODE) \
+ case CONST_INT: \
+ case CONST: \
+ case LABEL_REF: \
+ case SYMBOL_REF: \
+ return flag_pic && SYMBOLIC_CONST (RTX) ? 2 : 0; \
+ case CONST_DOUBLE: \
+ { \
+ int code; \
+ if (GET_MODE (RTX) == VOIDmode) \
+ return 2; \
+ code = standard_80387_constant_p (RTX); \
+ return code == 1 ? 0 : \
+ code == 2 ? 1 : \
+ 2; \
+ }
+
+/* Compute the cost of an address. This is meant to approximate the size
+ and/or execution delay of an insn using that address. If the cost is
+ approximated by the RTL complexity, including CONST_COSTS above, as
+ is usually the case for CISC machines, this macro should not be defined.
+ For aggressively RISCy machines, only one insn format is allowed, so
+ this macro should be a constant. The value of this macro only matters
+ for valid addresses.
+
+ For i386, it is better to use a complex address than let gcc copy
+ the address into a reg and make a new pseudo. But not if the address
+ requires to two regs - that would mean more pseudos with longer
+ lifetimes. */
+
+#define ADDRESS_COST(RTX) \
+ ((CONSTANT_P (RTX) \
+ || (GET_CODE (RTX) == PLUS && CONSTANT_P (XEXP (RTX, 1)) \
+ && REG_P (XEXP (RTX, 0)))) ? 0 \
+ : REG_P (RTX) ? 1 \
+ : 2)
+
+/* Add any extra modes needed to represent the condition code.
+
+ For the i386, we need separate modes when floating-point equality
+ comparisons are being done. */
+
+#define EXTRA_CC_MODES CCFPEQmode
+
+/* Define the names for the modes specified above. */
+#define EXTRA_CC_NAMES "CCFPEQ"
+
+/* Given a comparison code (EQ, NE, etc.) and the first operand of a COMPARE,
+ return the mode to be used for the comparison.
+
+ For floating-point equality comparisons, CCFPEQmode should be used.
+ VOIDmode should be used in all other cases. */
+
+#define SELECT_CC_MODE(OP,X,Y) \
+ (GET_MODE_CLASS (GET_MODE (X)) == MODE_FLOAT \
+ && ((OP) == EQ || (OP) == NE) ? CCFPEQmode : VOIDmode)
+
+/* Define the information needed to generate branch and scc insns. This is
+ stored from the compare operation. Note that we can't use "rtx" here
+ since it hasn't been defined! */
+
+extern struct rtx_def *i386_compare_op0, *i386_compare_op1;
+extern struct rtx_def *(*i386_compare_gen)(), *(*i386_compare_gen_eq)();
+
+/* Tell final.c how to eliminate redundant test instructions. */
+
+/* Here we define machine-dependent flags and fields in cc_status
+ (see `conditions.h'). */
+
+/* Set if the cc value is actually in the 80387, so a floating point
+ conditional branch must be output. */
+#define CC_IN_80387 04000
+
+/* Set if the CC value was stored in a nonstandard way, so that
+ the state of equality is indicated by zero in the carry bit. */
+#define CC_Z_IN_NOT_C 010000
+
+/* Store in cc_status the expressions
+ that the condition codes will describe
+ after execution of an instruction whose pattern is EXP.
+ Do not alter them if the instruction would not alter the cc's. */
+
+#define NOTICE_UPDATE_CC(EXP, INSN) \
+ notice_update_cc((EXP))
+
+/* Output a signed jump insn. Use template NORMAL ordinarily, or
+ FLOAT following a floating point comparison.
+ Use NO_OV following an arithmetic insn that set the cc's
+ before a test insn that was deleted.
+ NO_OV may be zero, meaning final should reinsert the test insn
+ because the jump cannot be handled properly without it. */
+
+#define OUTPUT_JUMP(NORMAL, FLOAT, NO_OV) \
+{ \
+ if (cc_prev_status.flags & CC_IN_80387) \
+ return FLOAT; \
+ if (cc_prev_status.flags & CC_NO_OVERFLOW) \
+ return NO_OV; \
+ return NORMAL; \
+}
+
+/* Control the assembler format that we output, to the extent
+ this does not vary between assemblers. */
+
+/* How to refer to registers in assembler output.
+ This sequence is indexed by compiler's hard-register-number (see above). */
+
+/* In order to refer to the first 8 regs as 32 bit regs prefix an "e"
+ For non floating point regs, the following are the HImode names.
+
+ For float regs, the stack top is sometimes referred to as "%st(0)"
+ instead of just "%st". PRINT_REG handles this with the "y" code. */
+
+#define HI_REGISTER_NAMES \
+{"ax","dx","cx","bx","si","di","bp","sp", \
+ "st","st(1)","st(2)","st(3)","st(4)","st(5)","st(6)","st(7)","" }
+
+#define REGISTER_NAMES HI_REGISTER_NAMES
+
+/* Table of additional register names to use in user input. */
+
+#define ADDITIONAL_REGISTER_NAMES \
+{ "eax", 0, "edx", 1, "ecx", 2, "ebx", 3, \
+ "esi", 4, "edi", 5, "ebp", 6, "esp", 7, \
+ "al", 0, "dl", 1, "cl", 2, "bl", 3, \
+ "ah", 0, "dh", 1, "ch", 2, "bh", 3 }
+
+/* Note we are omitting these since currently I don't know how
+to get gcc to use these, since they want the same but different
+number as al, and ax.
+*/
+
+/* note the last four are not really qi_registers, but
+ the md will have to never output movb into one of them
+ only a movw . There is no movb into the last four regs */
+
+#define QI_REGISTER_NAMES \
+{"al", "dl", "cl", "bl", "si", "di", "bp", "sp",}
+
+/* These parallel the array above, and can be used to access bits 8:15
+ of regs 0 through 3. */
+
+#define QI_HIGH_REGISTER_NAMES \
+{"ah", "dh", "ch", "bh", }
+
+/* How to renumber registers for dbx and gdb. */
+
+/* {0,2,1,3,6,7,4,5,12,13,14,15,16,17} */
+#define DBX_REGISTER_NUMBER(n) \
+((n) == 0 ? 0 : \
+ (n) == 1 ? 2 : \
+ (n) == 2 ? 1 : \
+ (n) == 3 ? 3 : \
+ (n) == 4 ? 6 : \
+ (n) == 5 ? 7 : \
+ (n) == 6 ? 4 : \
+ (n) == 7 ? 5 : \
+ (n) + 4)
+
+/* This is how to output the definition of a user-level label named NAME,
+ such as the label on a static function or variable NAME. */
+
+#define ASM_OUTPUT_LABEL(FILE,NAME) \
+ (assemble_name (FILE, NAME), fputs (":\n", FILE))
+
+/* This is how to output an assembler line defining a `double' constant. */
+
+#define ASM_OUTPUT_DOUBLE(FILE,VALUE) \
+do { long l[2]; \
+ REAL_VALUE_TO_TARGET_DOUBLE (VALUE, l); \
+ if (sizeof (int) == sizeof (long)) \
+ fprintf (FILE, "%s 0x%x,0x%x\n", ASM_LONG, l[0], l[1]); \
+ else \
+ fprintf (FILE, "%s 0x%lx,0x%lx\n", ASM_LONG, l[0], l[1]); \
+ } while (0)
+
+/* This is how to output a `long double' extended real constant. */
+
+#undef ASM_OUTPUT_LONG_DOUBLE
+#define ASM_OUTPUT_LONG_DOUBLE(FILE,VALUE) \
+do { long l[3]; \
+ REAL_VALUE_TO_TARGET_LONG_DOUBLE (VALUE, l); \
+ if (sizeof (int) == sizeof (long)) \
+ fprintf (FILE, "%s 0x%x,0x%x,0x%x\n", ASM_LONG, l[0], l[1], l[2]); \
+ else \
+ fprintf (FILE, "%s 0x%lx,0x%lx,0x%lx\n", ASM_LONG, l[0], l[1], l[2]); \
+ } while (0)
+
+/* This is how to output an assembler line defining a `float' constant. */
+
+#define ASM_OUTPUT_FLOAT(FILE,VALUE) \
+do { long l; \
+ REAL_VALUE_TO_TARGET_SINGLE (VALUE, l); \
+ if (sizeof (int) == sizeof (long)) \
+ fprintf ((FILE), "%s 0x%x\n", ASM_LONG, l); \
+ else \
+ fprintf ((FILE), "%s 0x%lx\n", ASM_LONG, l); \
+ } while (0)
+
+/* Store in OUTPUT a string (made with alloca) containing
+ an assembler-name for a local static variable named NAME.
+ LABELNO is an integer which is different for each call. */
+
+#define ASM_FORMAT_PRIVATE_NAME(OUTPUT, NAME, LABELNO) \
+( (OUTPUT) = (char *) alloca (strlen ((NAME)) + 10), \
+ sprintf ((OUTPUT), "%s.%d", (NAME), (LABELNO)))
+
+
+
+/* This is how to output an assembler line defining an `int' constant. */
+
+#define ASM_OUTPUT_INT(FILE,VALUE) \
+( fprintf (FILE, "%s ", ASM_LONG), \
+ output_addr_const (FILE,(VALUE)), \
+ putc('\n',FILE))
+
+/* Likewise for `char' and `short' constants. */
+/* is this supposed to do align too?? */
+
+#define ASM_OUTPUT_SHORT(FILE,VALUE) \
+( fprintf (FILE, "%s ", ASM_SHORT), \
+ output_addr_const (FILE,(VALUE)), \
+ putc('\n',FILE))
+
+/*
+#define ASM_OUTPUT_SHORT(FILE,VALUE) \
+( fprintf (FILE, "%s ", ASM_BYTE_OP), \
+ output_addr_const (FILE,(VALUE)), \
+ fputs (",", FILE), \
+ output_addr_const (FILE,(VALUE)), \
+ fputs (" >> 8\n",FILE))
+*/
+
+
+#define ASM_OUTPUT_CHAR(FILE,VALUE) \
+( fprintf (FILE, "%s ", ASM_BYTE_OP), \
+ output_addr_const (FILE, (VALUE)), \
+ putc ('\n', FILE))
+
+/* This is how to output an assembler line for a numeric constant byte. */
+
+#define ASM_OUTPUT_BYTE(FILE,VALUE) \
+ fprintf ((FILE), "%s 0x%x\n", ASM_BYTE_OP, (VALUE))
+
+/* This is how to output an insn to push a register on the stack.
+ It need not be very fast code. */
+
+#define ASM_OUTPUT_REG_PUSH(FILE,REGNO) \
+ fprintf (FILE, "\tpushl e%s\n", reg_names[REGNO])
+
+/* This is how to output an insn to pop a register from the stack.
+ It need not be very fast code. */
+
+#define ASM_OUTPUT_REG_POP(FILE,REGNO) \
+ fprintf (FILE, "\tpopl e%s\n", reg_names[REGNO])
+
+/* This is how to output an element of a case-vector that is absolute.
+ */
+
+#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \
+ fprintf (FILE, "%s %s%d\n", ASM_LONG, LPREFIX, VALUE)
+
+/* This is how to output an element of a case-vector that is relative.
+ We don't use these on the 386 yet, because the ATT assembler can't do
+ forward reference the differences.
+ */
+
+#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, VALUE, REL) \
+ fprintf (FILE, "\t.word %s%d-%s%d\n",LPREFIX, VALUE,LPREFIX, REL)
+
+/* Define the parentheses used to group arithmetic operations
+ in assembler code. */
+
+#define ASM_OPEN_PAREN ""
+#define ASM_CLOSE_PAREN ""
+
+/* Define results of standard character escape sequences. */
+#define TARGET_BELL 007
+#define TARGET_BS 010
+#define TARGET_TAB 011
+#define TARGET_NEWLINE 012
+#define TARGET_VT 013
+#define TARGET_FF 014
+#define TARGET_CR 015
+
+/* Print operand X (an rtx) in assembler syntax to file FILE.
+ CODE is a letter or dot (`z' in `%z0') or 0 if no letter was specified.
+ The CODE z takes the size of operand from the following digit, and
+ outputs b,w,or l respectively.
+
+ On the 80386, we use several such letters:
+ f -- float insn (print a CONST_DOUBLE as a float rather than in hex).
+ L,W,B,Q,S,T -- print the opcode suffix for specified size of operand.
+ R -- print the prefix for register names.
+ z -- print the opcode suffix for the size of the current operand.
+ * -- print a star (in certain assembler syntax)
+ w -- print the operand as if it's a "word" (HImode) even if it isn't.
+ b -- print the operand as if it's a byte (QImode) even if it isn't.
+ c -- don't print special prefixes before constant operands. */
+
+#define PRINT_OPERAND_PUNCT_VALID_P(CODE) \
+ ((CODE) == '*')
+
+/* Print the name of a register based on its machine mode and number.
+ If CODE is 'w', pretend the mode is HImode.
+ If CODE is 'b', pretend the mode is QImode.
+ If CODE is 'k', pretend the mode is SImode.
+ If CODE is 'h', pretend the reg is the `high' byte register.
+ If CODE is 'y', print "st(0)" instead of "st", if the reg is stack op. */
+
+extern char *hi_reg_name[];
+extern char *qi_reg_name[];
+extern char *qi_high_reg_name[];
+
+#define PRINT_REG(X, CODE, FILE) \
+ do { if (REGNO (X) == ARG_POINTER_REGNUM) \
+ abort (); \
+ fprintf (FILE, "%s", RP); \
+ switch ((CODE == 'w' ? 2 \
+ : CODE == 'b' ? 1 \
+ : CODE == 'k' ? 4 \
+ : CODE == 'y' ? 3 \
+ : CODE == 'h' ? 0 \
+ : GET_MODE_SIZE (GET_MODE (X)))) \
+ { \
+ case 3: \
+ if (STACK_TOP_P (X)) \
+ { \
+ fputs ("st(0)", FILE); \
+ break; \
+ } \
+ case 4: \
+ case 8: \
+ case 12: \
+ if (! FP_REG_P (X)) fputs ("e", FILE); \
+ case 2: \
+ fputs (hi_reg_name[REGNO (X)], FILE); \
+ break; \
+ case 1: \
+ fputs (qi_reg_name[REGNO (X)], FILE); \
+ break; \
+ case 0: \
+ fputs (qi_high_reg_name[REGNO (X)], FILE); \
+ break; \
+ } \
+ } while (0)
+
+#define PRINT_OPERAND(FILE, X, CODE) \
+ print_operand (FILE, X, CODE)
+
+#define PRINT_OPERAND_ADDRESS(FILE, ADDR) \
+ print_operand_address (FILE, ADDR)
+
+/* Print the name of a register for based on its machine mode and number.
+ This macro is used to print debugging output.
+ This macro is different from PRINT_REG in that it may be used in
+ programs that are not linked with aux-output.o. */
+
+#define DEBUG_PRINT_REG(X, CODE, FILE) \
+ do { static char *hi_name[] = HI_REGISTER_NAMES; \
+ static char *qi_name[] = QI_REGISTER_NAMES; \
+ fprintf (FILE, "%d %s", REGNO (X), RP); \
+ if (REGNO (X) == ARG_POINTER_REGNUM) \
+ { fputs ("argp", FILE); break; } \
+ if (STACK_TOP_P (X)) \
+ { fputs ("st(0)", FILE); break; } \
+ if (FP_REG_P (X)) \
+ { fputs (hi_name[REGNO(X)], FILE); break; } \
+ switch (GET_MODE_SIZE (GET_MODE (X))) \
+ { \
+ default: \
+ fputs ("e", FILE); \
+ case 2: \
+ fputs (hi_name[REGNO (X)], FILE); \
+ break; \
+ case 1: \
+ fputs (qi_name[REGNO (X)], FILE); \
+ break; \
+ } \
+ } while (0)
+
+/* Output the prefix for an immediate operand, or for an offset operand. */
+#define PRINT_IMMED_PREFIX(FILE) fputs (IP, (FILE))
+#define PRINT_OFFSET_PREFIX(FILE) fputs (IP, (FILE))
+
+/* Routines in libgcc that return floats must return them in an fp reg,
+ just as other functions do which return such values.
+ These macros make that happen. */
+
+#define FLOAT_VALUE_TYPE float
+#define INTIFY(FLOATVAL) FLOATVAL
+
+/* Nonzero if INSN magically clobbers register REGNO. */
+
+/* #define INSN_CLOBBERS_REGNO_P(INSN, REGNO) \
+ (FP_REGNO_P (REGNO) \
+ && (GET_CODE (INSN) == JUMP_INSN || GET_CODE (INSN) == BARRIER))
+*/
+
+/* a letter which is not needed by the normal asm syntax, which
+ we can use for operand syntax in the extended asm */
+
+#define ASM_OPERAND_LETTER '#'
+
+#define RET return ""
+#define AT_SP(mode) (gen_rtx (MEM, (mode), stack_pointer_rtx))
+
+/*
+Local variables:
+version-control: t
+End:
+*/
diff --git a/gnu/usr.bin/cc/include/i386/perform.h b/gnu/usr.bin/cc/include/i386/perform.h
new file mode 100644
index 0000000..4fdd7b3
--- /dev/null
+++ b/gnu/usr.bin/cc/include/i386/perform.h
@@ -0,0 +1,97 @@
+/* Definitions for AT&T assembler syntax for the Intel 80386.
+ Copyright (C) 1993 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Defines to be able to build libgcc.a with GCC. */
+
+/* It might seem that these are not important, since gcc 2 will never
+ call libgcc for these functions. But programs might be linked with
+ code compiled by gcc 1, and then these will be used. */
+
+/* The arg names used to be a and b, but `a' appears inside strings
+ and that confuses non-ANSI cpp. */
+
+#define perform_udivsi3(arg0,arg1) \
+{ \
+ register int dx asm("dx"); \
+ register int ax asm("ax"); \
+ \
+ dx = 0; \
+ ax = arg0; \
+ asm ("divl %3" : "=a" (ax), "=d" (dx) : "a" (ax), "g" (arg1), "d" (dx)); \
+ return ax; \
+}
+
+#define perform_divsi3(arg0,arg1) \
+{ \
+ register int dx asm("dx"); \
+ register int ax asm("ax"); \
+ register int cx asm("cx"); \
+ \
+ ax = arg0; \
+ cx = arg1; \
+ asm ("cltd\n\tidivl %3" : "=a" (ax), "=&d" (dx) : "a" (ax), "c" (cx)); \
+ return ax; \
+}
+
+#define perform_umodsi3(arg0,arg1) \
+{ \
+ register int dx asm("dx"); \
+ register int ax asm("ax"); \
+ \
+ dx = 0; \
+ ax = arg0; \
+ asm ("divl %3" : "=a" (ax), "=d" (dx) : "a" (ax), "g" (arg1), "d" (dx)); \
+ return dx; \
+}
+
+#define perform_modsi3(arg0,arg1) \
+{ \
+ register int dx asm("dx"); \
+ register int ax asm("ax"); \
+ register int cx asm("cx"); \
+ \
+ ax = arg0; \
+ cx = arg1; \
+ asm ("cltd\n\tidivl %3" : "=a" (ax), "=&d" (dx) : "a" (ax), "c" (cx)); \
+ return dx; \
+}
+
+#define perform_fixdfsi(arg0) \
+{ \
+ auto unsigned short ostatus; \
+ auto unsigned short nstatus; \
+ auto int ret; \
+ auto double tmp; \
+ \
+ &ostatus; /* guarantee these land in memory */ \
+ &nstatus; \
+ &ret; \
+ &tmp; \
+ \
+ asm volatile ("fnstcw %0" : "=m" (ostatus)); \
+ nstatus = ostatus | 0x0c00; \
+ asm volatile ("fldcw %0" : /* no outputs */ : "m" (nstatus)); \
+ tmp = arg0; \
+ asm volatile ("fldl %0" : /* no outputs */ : "m" (tmp)); \
+ asm volatile ("fistpl %0" : "=m" (ret)); \
+ asm volatile ("fldcw %0" : /* no outputs */ : "m" (ostatus)); \
+ \
+ return ret; \
+}
+
diff --git a/gnu/usr.bin/cc/include/i386/unix.h b/gnu/usr.bin/cc/include/i386/unix.h
new file mode 100644
index 0000000..7209176
--- /dev/null
+++ b/gnu/usr.bin/cc/include/i386/unix.h
@@ -0,0 +1,145 @@
+/* Definitions for Unix assembler syntax for the Intel 80386.
+ Copyright (C) 1988 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This file defines the aspects of assembler syntax
+ that are the same for all the i386 Unix systems
+ (though they may differ in non-Unix systems). */
+
+/* Define some concatenation macros to concatenate an opcode
+ and one, two or three operands. In other assembler syntaxes
+ they may alter the order of ther operands. */
+
+/* Note that the other files fail to use these
+ in some of the places where they should. */
+
+#ifdef __STDC__
+#define AS2(a,b,c) #a " " #b "," #c
+#define AS3(a,b,c,d) #a " " #b "," #c "," #d
+#define AS1(a,b) #a " " #b
+#else
+#define AS1(a,b) "a b"
+#define AS2(a,b,c) "a b,c"
+#define AS3(a,b,c,d) "a b,c,d"
+#endif
+
+/* Define macro used to output shift-double opcodes when the shift
+ count is in %cl. Some assemblers require %cl as an argument;
+ some don't. This macro controls what to do: by default, don't
+ print %cl. */
+#define AS3_SHIFT_DOUBLE(a,b,c,d) AS2 (a,c,d)
+
+/* Output the size-letter for an opcode.
+ CODE is the letter used in an operand spec (L, B, W, S or Q).
+ CH is the corresponding lower case letter
+ (except if CODE is `Q' then CH is `l', unless GAS_MNEMONICS). */
+#define PUT_OP_SIZE(CODE,CH,FILE) putc (CH,(FILE))
+
+/* Opcode suffix for fullword insn. */
+#define L_SIZE "l"
+
+/* Prefix for register names in this syntax. */
+#define RP "%"
+
+/* Prefix for immediate operands in this syntax. */
+#define IP "$"
+
+/* Indirect call instructions should use `*'. */
+#define USE_STAR 1
+
+/* Prefix for a memory-operand X. */
+#define PRINT_PTR(X, FILE)
+
+/* Delimiters that surround base reg and index reg. */
+#define ADDR_BEG(FILE) putc('(', (FILE))
+#define ADDR_END(FILE) putc(')', (FILE))
+
+/* Print an index register (whose rtx is IREG). */
+#define PRINT_IREG(FILE,IREG) \
+ do \
+ { fputs (",", (FILE)); PRINT_REG ((IREG), 0, (FILE)); } \
+ while (0)
+
+/* Print an index scale factor SCALE. */
+#define PRINT_SCALE(FILE,SCALE) \
+ if ((SCALE) != 1) fprintf ((FILE), ",%d", (SCALE))
+
+/* Print a base/index combination.
+ BREG is the base reg rtx, IREG is the index reg rtx,
+ and SCALE is the index scale factor (an integer). */
+
+#define PRINT_B_I_S(BREG,IREG,SCALE,FILE) \
+ { ADDR_BEG (FILE); \
+ if (BREG) PRINT_REG ((BREG), 0, (FILE)); \
+ if ((IREG) != 0) \
+ { PRINT_IREG ((FILE), (IREG)); \
+ PRINT_SCALE ((FILE), (SCALE)); } \
+ ADDR_END (FILE); }
+
+/* Define the syntax of pseudo-ops, labels and comments. */
+
+/* String containing the assembler's comment-starter. */
+
+#define ASM_COMMENT_START "/"
+#define COMMENT_BEGIN "/"
+
+/* Output to assembler file text saying following lines
+ may contain character constants, extra white space, comments, etc. */
+
+#define ASM_APP_ON "/APP\n"
+
+/* Output to assembler file text saying following lines
+ no longer contain unusual constructs. */
+
+#define ASM_APP_OFF "/NO_APP\n"
+
+/* Output before read-only data. */
+
+#define TEXT_SECTION_ASM_OP ".text"
+
+/* Output before writable (initialized) data. */
+
+#define DATA_SECTION_ASM_OP ".data"
+
+/* Output before writable (uninitialized) data. */
+
+#define BSS_SECTION_ASM_OP ".bss"
+
+/* This is how to output a command to make the user-level label named NAME
+ defined for reference from other files. */
+
+#define ASM_GLOBALIZE_LABEL(FILE,NAME) \
+ (fputs (".globl ", FILE), assemble_name (FILE, NAME), fputs ("\n", FILE))
+
+/* By default, target has a 80387, uses IEEE compatible arithmetic,
+ and returns float values in the 387, ie,
+ (TARGET_80387 | TARGET_IEEE_FP | TARGET_FLOAT_RETURNS_IN_80387) */
+
+#define TARGET_DEFAULT 0301
+
+/* Floating-point return values come in the FP register. */
+
+#define VALUE_REGNO(MODE) \
+ (GET_MODE_CLASS (MODE) == MODE_FLOAT \
+ && TARGET_FLOAT_RETURNS_IN_80387 ? FIRST_FLOAT_REG : 0)
+
+/* 1 if N is a possible register number for a function value. */
+
+#define FUNCTION_VALUE_REGNO_P(N) \
+ ((N) == 0 || ((N)== FIRST_FLOAT_REG && TARGET_FLOAT_RETURNS_IN_80387))
+
diff --git a/gnu/usr.bin/cc/include/input.h b/gnu/usr.bin/cc/include/input.h
new file mode 100644
index 0000000..39590e2
--- /dev/null
+++ b/gnu/usr.bin/cc/include/input.h
@@ -0,0 +1,46 @@
+/* Declarations for variables relating to reading the source file.
+ Used by parsers, lexical analyzers, and error message routines.
+
+ Copyright (C) 1993 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Source file current line is coming from. */
+extern char *input_filename;
+
+/* Top-level source file. */
+extern char *main_input_filename;
+
+/* Line number in current source file. */
+extern int lineno;
+
+/* Stream for reading from input file. */
+extern FILE *finput;
+
+struct file_stack
+ {
+ char *name;
+ struct file_stack *next;
+ int line;
+ };
+
+/* Stack of currently pending input files.
+ The line member is not accurate for the innermost file on the stack. */
+extern struct file_stack *input_file_stack;
+
+/* Incremented on each change to input_file_stack. */
+extern int input_file_stack_tick;
diff --git a/gnu/usr.bin/cc/include/insn-attr.h b/gnu/usr.bin/cc/include/insn-attr.h
new file mode 100644
index 0000000..5fe9a2f
--- /dev/null
+++ b/gnu/usr.bin/cc/include/insn-attr.h
@@ -0,0 +1,19 @@
+/* Generated automatically by the program `genattr'
+from the machine description file `md'. */
+
+#ifndef PROTO
+#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
+#define PROTO(ARGS) ARGS
+#else
+#define PROTO(ARGS) ()
+#endif
+#endif
+#define HAVE_ATTR_alternative
+#define get_attr_alternative(insn) which_alternative
+
+#define ATTR_FLAG_forward 0x1
+#define ATTR_FLAG_backward 0x2
+#define ATTR_FLAG_likely 0x4
+#define ATTR_FLAG_very_likely 0x8
+#define ATTR_FLAG_unlikely 0x10
+#define ATTR_FLAG_very_unlikely 0x20
diff --git a/gnu/usr.bin/cc/include/insn-codes.h b/gnu/usr.bin/cc/include/insn-codes.h
new file mode 100644
index 0000000..59e24c9
--- /dev/null
+++ b/gnu/usr.bin/cc/include/insn-codes.h
@@ -0,0 +1,201 @@
+/* Generated automatically by the program `gencodes'
+from the machine description file `md'. */
+
+#ifndef MAX_INSN_CODE
+
+enum insn_code {
+ CODE_FOR_tstsi_1 = 0,
+ CODE_FOR_tstsi = 1,
+ CODE_FOR_tsthi_1 = 2,
+ CODE_FOR_tsthi = 3,
+ CODE_FOR_tstqi_1 = 4,
+ CODE_FOR_tstqi = 5,
+ CODE_FOR_tstsf_cc = 6,
+ CODE_FOR_tstsf = 7,
+ CODE_FOR_tstdf_cc = 8,
+ CODE_FOR_tstdf = 9,
+ CODE_FOR_tstxf_cc = 10,
+ CODE_FOR_tstxf = 11,
+ CODE_FOR_cmpsi_1 = 12,
+ CODE_FOR_cmpsi = 13,
+ CODE_FOR_cmphi_1 = 14,
+ CODE_FOR_cmphi = 15,
+ CODE_FOR_cmpqi_1 = 16,
+ CODE_FOR_cmpqi = 17,
+ CODE_FOR_cmpsf_cc_1 = 30,
+ CODE_FOR_cmpxf = 34,
+ CODE_FOR_cmpdf = 35,
+ CODE_FOR_cmpsf = 36,
+ CODE_FOR_cmpxf_cc = 37,
+ CODE_FOR_cmpxf_ccfpeq = 38,
+ CODE_FOR_cmpdf_cc = 39,
+ CODE_FOR_cmpdf_ccfpeq = 40,
+ CODE_FOR_cmpsf_cc = 41,
+ CODE_FOR_cmpsf_ccfpeq = 42,
+ CODE_FOR_movsi = 48,
+ CODE_FOR_movhi = 51,
+ CODE_FOR_movstricthi = 52,
+ CODE_FOR_movqi = 54,
+ CODE_FOR_movstrictqi = 55,
+ CODE_FOR_movsf = 57,
+ CODE_FOR_swapdf = 59,
+ CODE_FOR_movdf = 60,
+ CODE_FOR_swapxf = 62,
+ CODE_FOR_movxf = 63,
+ CODE_FOR_movdi = 65,
+ CODE_FOR_zero_extendhisi2 = 66,
+ CODE_FOR_zero_extendqihi2 = 67,
+ CODE_FOR_zero_extendqisi2 = 68,
+ CODE_FOR_zero_extendsidi2 = 69,
+ CODE_FOR_extendsidi2 = 70,
+ CODE_FOR_extendhisi2 = 71,
+ CODE_FOR_extendqihi2 = 72,
+ CODE_FOR_extendqisi2 = 73,
+ CODE_FOR_extendsfdf2 = 74,
+ CODE_FOR_extenddfxf2 = 75,
+ CODE_FOR_extendsfxf2 = 76,
+ CODE_FOR_truncdfsf2 = 77,
+ CODE_FOR_truncxfsf2 = 79,
+ CODE_FOR_truncxfdf2 = 80,
+ CODE_FOR_fixuns_truncxfsi2 = 81,
+ CODE_FOR_fixuns_truncdfsi2 = 82,
+ CODE_FOR_fixuns_truncsfsi2 = 83,
+ CODE_FOR_fix_truncxfdi2 = 84,
+ CODE_FOR_fix_truncdfdi2 = 85,
+ CODE_FOR_fix_truncsfdi2 = 86,
+ CODE_FOR_fix_truncxfsi2 = 90,
+ CODE_FOR_fix_truncdfsi2 = 91,
+ CODE_FOR_fix_truncsfsi2 = 92,
+ CODE_FOR_floatsisf2 = 96,
+ CODE_FOR_floatdisf2 = 97,
+ CODE_FOR_floatsidf2 = 98,
+ CODE_FOR_floatdidf2 = 99,
+ CODE_FOR_floatsixf2 = 100,
+ CODE_FOR_floatdixf2 = 101,
+ CODE_FOR_adddi3 = 108,
+ CODE_FOR_addsi3 = 109,
+ CODE_FOR_addhi3 = 110,
+ CODE_FOR_addqi3 = 111,
+ CODE_FOR_addxf3 = 113,
+ CODE_FOR_adddf3 = 114,
+ CODE_FOR_addsf3 = 115,
+ CODE_FOR_subdi3 = 116,
+ CODE_FOR_subsi3 = 117,
+ CODE_FOR_subhi3 = 118,
+ CODE_FOR_subqi3 = 119,
+ CODE_FOR_subxf3 = 120,
+ CODE_FOR_subdf3 = 121,
+ CODE_FOR_subsf3 = 122,
+ CODE_FOR_mulhi3 = 124,
+ CODE_FOR_mulsi3 = 126,
+ CODE_FOR_umulqihi3 = 127,
+ CODE_FOR_mulqihi3 = 128,
+ CODE_FOR_umulsidi3 = 129,
+ CODE_FOR_mulsidi3 = 130,
+ CODE_FOR_mulxf3 = 131,
+ CODE_FOR_muldf3 = 132,
+ CODE_FOR_mulsf3 = 133,
+ CODE_FOR_divqi3 = 134,
+ CODE_FOR_udivqi3 = 135,
+ CODE_FOR_divxf3 = 136,
+ CODE_FOR_divdf3 = 137,
+ CODE_FOR_divsf3 = 138,
+ CODE_FOR_divmodsi4 = 139,
+ CODE_FOR_divmodhi4 = 140,
+ CODE_FOR_udivmodsi4 = 141,
+ CODE_FOR_udivmodhi4 = 142,
+ CODE_FOR_andsi3 = 143,
+ CODE_FOR_andhi3 = 144,
+ CODE_FOR_andqi3 = 145,
+ CODE_FOR_iorsi3 = 146,
+ CODE_FOR_iorhi3 = 147,
+ CODE_FOR_iorqi3 = 148,
+ CODE_FOR_xorsi3 = 149,
+ CODE_FOR_xorhi3 = 150,
+ CODE_FOR_xorqi3 = 151,
+ CODE_FOR_negdi2 = 152,
+ CODE_FOR_negsi2 = 153,
+ CODE_FOR_neghi2 = 154,
+ CODE_FOR_negqi2 = 155,
+ CODE_FOR_negsf2 = 156,
+ CODE_FOR_negdf2 = 157,
+ CODE_FOR_negxf2 = 159,
+ CODE_FOR_abssf2 = 161,
+ CODE_FOR_absdf2 = 162,
+ CODE_FOR_absxf2 = 164,
+ CODE_FOR_sqrtsf2 = 166,
+ CODE_FOR_sqrtdf2 = 167,
+ CODE_FOR_sqrtxf2 = 169,
+ CODE_FOR_sindf2 = 172,
+ CODE_FOR_sinsf2 = 173,
+ CODE_FOR_cosdf2 = 175,
+ CODE_FOR_cossf2 = 176,
+ CODE_FOR_one_cmplsi2 = 178,
+ CODE_FOR_one_cmplhi2 = 179,
+ CODE_FOR_one_cmplqi2 = 180,
+ CODE_FOR_ashldi3 = 181,
+ CODE_FOR_ashldi3_const_int = 182,
+ CODE_FOR_ashldi3_non_const_int = 183,
+ CODE_FOR_ashlsi3 = 184,
+ CODE_FOR_ashlhi3 = 185,
+ CODE_FOR_ashlqi3 = 186,
+ CODE_FOR_ashrdi3 = 187,
+ CODE_FOR_ashrdi3_const_int = 188,
+ CODE_FOR_ashrdi3_non_const_int = 189,
+ CODE_FOR_ashrsi3 = 190,
+ CODE_FOR_ashrhi3 = 191,
+ CODE_FOR_ashrqi3 = 192,
+ CODE_FOR_lshrdi3 = 193,
+ CODE_FOR_lshrdi3_const_int = 194,
+ CODE_FOR_lshrdi3_non_const_int = 195,
+ CODE_FOR_lshrsi3 = 196,
+ CODE_FOR_lshrhi3 = 197,
+ CODE_FOR_lshrqi3 = 198,
+ CODE_FOR_rotlsi3 = 199,
+ CODE_FOR_rotlhi3 = 200,
+ CODE_FOR_rotlqi3 = 201,
+ CODE_FOR_rotrsi3 = 202,
+ CODE_FOR_rotrhi3 = 203,
+ CODE_FOR_rotrqi3 = 204,
+ CODE_FOR_seq = 211,
+ CODE_FOR_sne = 213,
+ CODE_FOR_sgt = 215,
+ CODE_FOR_sgtu = 217,
+ CODE_FOR_slt = 219,
+ CODE_FOR_sltu = 221,
+ CODE_FOR_sge = 223,
+ CODE_FOR_sgeu = 225,
+ CODE_FOR_sle = 227,
+ CODE_FOR_sleu = 229,
+ CODE_FOR_beq = 231,
+ CODE_FOR_bne = 233,
+ CODE_FOR_bgt = 235,
+ CODE_FOR_bgtu = 237,
+ CODE_FOR_blt = 239,
+ CODE_FOR_bltu = 241,
+ CODE_FOR_bge = 243,
+ CODE_FOR_bgeu = 245,
+ CODE_FOR_ble = 247,
+ CODE_FOR_bleu = 249,
+ CODE_FOR_jump = 261,
+ CODE_FOR_indirect_jump = 262,
+ CODE_FOR_casesi = 263,
+ CODE_FOR_tablejump = 265,
+ CODE_FOR_call_pop = 266,
+ CODE_FOR_call = 269,
+ CODE_FOR_call_value_pop = 272,
+ CODE_FOR_call_value = 275,
+ CODE_FOR_untyped_call = 278,
+ CODE_FOR_untyped_return = 281,
+ CODE_FOR_update_return = 282,
+ CODE_FOR_return = 283,
+ CODE_FOR_nop = 284,
+ CODE_FOR_movstrsi = 285,
+ CODE_FOR_cmpstrsi = 287,
+ CODE_FOR_ffssi2 = 290,
+ CODE_FOR_ffshi2 = 292,
+ CODE_FOR_strlensi = 307,
+ CODE_FOR_nothing };
+
+#define MAX_INSN_CODE ((int) CODE_FOR_nothing)
+#endif /* MAX_INSN_CODE */
diff --git a/gnu/usr.bin/cc/include/insn-config.h b/gnu/usr.bin/cc/include/insn-config.h
new file mode 100644
index 0000000..7dba886
--- /dev/null
+++ b/gnu/usr.bin/cc/include/insn-config.h
@@ -0,0 +1,12 @@
+/* Generated automatically by the program `genconfig'
+from the machine description file `md'. */
+
+
+#define MAX_RECOG_OPERANDS 10
+
+#define MAX_DUP_OPERANDS 3
+#ifndef MAX_INSNS_PER_SPLIT
+#define MAX_INSNS_PER_SPLIT 1
+#endif
+#define REGISTER_CONSTRAINTS
+#define HAVE_cc0
diff --git a/gnu/usr.bin/cc/include/insn-flags.h b/gnu/usr.bin/cc/include/insn-flags.h
new file mode 100644
index 0000000..c9dd771
--- /dev/null
+++ b/gnu/usr.bin/cc/include/insn-flags.h
@@ -0,0 +1,598 @@
+/* Generated automatically by the program `genflags'
+from the machine description file `md'. */
+
+#define HAVE_tstsi_1 1
+#define HAVE_tstsi 1
+#define HAVE_tsthi_1 1
+#define HAVE_tsthi 1
+#define HAVE_tstqi_1 1
+#define HAVE_tstqi 1
+#define HAVE_tstsf_cc (TARGET_80387 && ! TARGET_IEEE_FP)
+#define HAVE_tstsf (TARGET_80387 && ! TARGET_IEEE_FP)
+#define HAVE_tstdf_cc (TARGET_80387 && ! TARGET_IEEE_FP)
+#define HAVE_tstdf (TARGET_80387 && ! TARGET_IEEE_FP)
+#define HAVE_tstxf_cc (TARGET_80387 && ! TARGET_IEEE_FP)
+#define HAVE_tstxf (TARGET_80387 && ! TARGET_IEEE_FP)
+#define HAVE_cmpsi_1 (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)
+#define HAVE_cmpsi 1
+#define HAVE_cmphi_1 (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)
+#define HAVE_cmphi 1
+#define HAVE_cmpqi_1 (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)
+#define HAVE_cmpqi 1
+#define HAVE_cmpsf_cc_1 (TARGET_80387 \
+ && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM))
+#define HAVE_cmpxf (TARGET_80387)
+#define HAVE_cmpdf (TARGET_80387)
+#define HAVE_cmpsf (TARGET_80387)
+#define HAVE_cmpxf_cc (TARGET_80387)
+#define HAVE_cmpxf_ccfpeq (TARGET_80387)
+#define HAVE_cmpdf_cc (TARGET_80387)
+#define HAVE_cmpdf_ccfpeq (TARGET_80387)
+#define HAVE_cmpsf_cc (TARGET_80387)
+#define HAVE_cmpsf_ccfpeq (TARGET_80387)
+#define HAVE_movsi 1
+#define HAVE_movhi 1
+#define HAVE_movstricthi 1
+#define HAVE_movqi 1
+#define HAVE_movstrictqi 1
+#define HAVE_movsf 1
+#define HAVE_swapdf 1
+#define HAVE_movdf 1
+#define HAVE_swapxf 1
+#define HAVE_movxf 1
+#define HAVE_movdi 1
+#define HAVE_zero_extendhisi2 1
+#define HAVE_zero_extendqihi2 1
+#define HAVE_zero_extendqisi2 1
+#define HAVE_zero_extendsidi2 1
+#define HAVE_extendsidi2 1
+#define HAVE_extendhisi2 1
+#define HAVE_extendqihi2 1
+#define HAVE_extendqisi2 1
+#define HAVE_extendsfdf2 (TARGET_80387)
+#define HAVE_extenddfxf2 (TARGET_80387)
+#define HAVE_extendsfxf2 (TARGET_80387)
+#define HAVE_truncdfsf2 (TARGET_80387)
+#define HAVE_truncxfsf2 (TARGET_80387)
+#define HAVE_truncxfdf2 (TARGET_80387)
+#define HAVE_fixuns_truncxfsi2 (TARGET_80387)
+#define HAVE_fixuns_truncdfsi2 (TARGET_80387)
+#define HAVE_fixuns_truncsfsi2 (TARGET_80387)
+#define HAVE_fix_truncxfdi2 (TARGET_80387)
+#define HAVE_fix_truncdfdi2 (TARGET_80387)
+#define HAVE_fix_truncsfdi2 (TARGET_80387)
+#define HAVE_fix_truncxfsi2 (TARGET_80387)
+#define HAVE_fix_truncdfsi2 (TARGET_80387)
+#define HAVE_fix_truncsfsi2 (TARGET_80387)
+#define HAVE_floatsisf2 (TARGET_80387)
+#define HAVE_floatdisf2 (TARGET_80387)
+#define HAVE_floatsidf2 (TARGET_80387)
+#define HAVE_floatdidf2 (TARGET_80387)
+#define HAVE_floatsixf2 (TARGET_80387)
+#define HAVE_floatdixf2 (TARGET_80387)
+#define HAVE_adddi3 1
+#define HAVE_addsi3 1
+#define HAVE_addhi3 1
+#define HAVE_addqi3 1
+#define HAVE_addxf3 (TARGET_80387)
+#define HAVE_adddf3 (TARGET_80387)
+#define HAVE_addsf3 (TARGET_80387)
+#define HAVE_subdi3 1
+#define HAVE_subsi3 1
+#define HAVE_subhi3 1
+#define HAVE_subqi3 1
+#define HAVE_subxf3 (TARGET_80387)
+#define HAVE_subdf3 (TARGET_80387)
+#define HAVE_subsf3 (TARGET_80387)
+#define HAVE_mulhi3 1
+#define HAVE_mulsi3 1
+#define HAVE_umulqihi3 1
+#define HAVE_mulqihi3 1
+#define HAVE_umulsidi3 1
+#define HAVE_mulsidi3 1
+#define HAVE_mulxf3 (TARGET_80387)
+#define HAVE_muldf3 (TARGET_80387)
+#define HAVE_mulsf3 (TARGET_80387)
+#define HAVE_divqi3 1
+#define HAVE_udivqi3 1
+#define HAVE_divxf3 (TARGET_80387)
+#define HAVE_divdf3 (TARGET_80387)
+#define HAVE_divsf3 (TARGET_80387)
+#define HAVE_divmodsi4 1
+#define HAVE_divmodhi4 1
+#define HAVE_udivmodsi4 1
+#define HAVE_udivmodhi4 1
+#define HAVE_andsi3 1
+#define HAVE_andhi3 1
+#define HAVE_andqi3 1
+#define HAVE_iorsi3 1
+#define HAVE_iorhi3 1
+#define HAVE_iorqi3 1
+#define HAVE_xorsi3 1
+#define HAVE_xorhi3 1
+#define HAVE_xorqi3 1
+#define HAVE_negdi2 1
+#define HAVE_negsi2 1
+#define HAVE_neghi2 1
+#define HAVE_negqi2 1
+#define HAVE_negsf2 (TARGET_80387)
+#define HAVE_negdf2 (TARGET_80387)
+#define HAVE_negxf2 (TARGET_80387)
+#define HAVE_abssf2 (TARGET_80387)
+#define HAVE_absdf2 (TARGET_80387)
+#define HAVE_absxf2 (TARGET_80387)
+#define HAVE_sqrtsf2 (! TARGET_NO_FANCY_MATH_387 && TARGET_80387 \
+ && (TARGET_IEEE_FP || flag_fast_math) )
+#define HAVE_sqrtdf2 (! TARGET_NO_FANCY_MATH_387 && TARGET_80387 \
+ && (TARGET_IEEE_FP || flag_fast_math) )
+#define HAVE_sqrtxf2 (! TARGET_NO_FANCY_MATH_387 && TARGET_80387 \
+ && (TARGET_IEEE_FP || flag_fast_math) )
+#define HAVE_sindf2 (! TARGET_NO_FANCY_MATH_387 && TARGET_80387 \
+ && (TARGET_IEEE_FP || flag_fast_math) )
+#define HAVE_sinsf2 (! TARGET_NO_FANCY_MATH_387 && TARGET_80387 \
+ && (TARGET_IEEE_FP || flag_fast_math) )
+#define HAVE_cosdf2 (! TARGET_NO_FANCY_MATH_387 && TARGET_80387 \
+ && (TARGET_IEEE_FP || flag_fast_math) )
+#define HAVE_cossf2 (! TARGET_NO_FANCY_MATH_387 && TARGET_80387 \
+ && (TARGET_IEEE_FP || flag_fast_math) )
+#define HAVE_one_cmplsi2 1
+#define HAVE_one_cmplhi2 1
+#define HAVE_one_cmplqi2 1
+#define HAVE_ashldi3 1
+#define HAVE_ashldi3_const_int 1
+#define HAVE_ashldi3_non_const_int 1
+#define HAVE_ashlsi3 1
+#define HAVE_ashlhi3 1
+#define HAVE_ashlqi3 1
+#define HAVE_ashrdi3 1
+#define HAVE_ashrdi3_const_int 1
+#define HAVE_ashrdi3_non_const_int 1
+#define HAVE_ashrsi3 1
+#define HAVE_ashrhi3 1
+#define HAVE_ashrqi3 1
+#define HAVE_lshrdi3 1
+#define HAVE_lshrdi3_const_int 1
+#define HAVE_lshrdi3_non_const_int 1
+#define HAVE_lshrsi3 1
+#define HAVE_lshrhi3 1
+#define HAVE_lshrqi3 1
+#define HAVE_rotlsi3 1
+#define HAVE_rotlhi3 1
+#define HAVE_rotlqi3 1
+#define HAVE_rotrsi3 1
+#define HAVE_rotrhi3 1
+#define HAVE_rotrqi3 1
+#define HAVE_seq 1
+#define HAVE_sne 1
+#define HAVE_sgt 1
+#define HAVE_sgtu 1
+#define HAVE_slt 1
+#define HAVE_sltu 1
+#define HAVE_sge 1
+#define HAVE_sgeu 1
+#define HAVE_sle 1
+#define HAVE_sleu 1
+#define HAVE_beq 1
+#define HAVE_bne 1
+#define HAVE_bgt 1
+#define HAVE_bgtu 1
+#define HAVE_blt 1
+#define HAVE_bltu 1
+#define HAVE_bge 1
+#define HAVE_bgeu 1
+#define HAVE_ble 1
+#define HAVE_bleu 1
+#define HAVE_jump 1
+#define HAVE_indirect_jump 1
+#define HAVE_casesi (flag_pic)
+#define HAVE_tablejump 1
+#define HAVE_call_pop 1
+#define HAVE_call 1
+#define HAVE_call_value_pop 1
+#define HAVE_call_value 1
+#define HAVE_untyped_call 1
+#define HAVE_untyped_return 1
+#define HAVE_update_return 1
+#define HAVE_return (simple_386_epilogue ())
+#define HAVE_nop 1
+#define HAVE_movstrsi 1
+#define HAVE_cmpstrsi 1
+#define HAVE_ffssi2 1
+#define HAVE_ffshi2 1
+#define HAVE_strlensi 1
+
+#ifndef NO_MD_PROTOTYPES
+extern rtx gen_tstsi_1 PROTO((rtx));
+extern rtx gen_tstsi PROTO((rtx));
+extern rtx gen_tsthi_1 PROTO((rtx));
+extern rtx gen_tsthi PROTO((rtx));
+extern rtx gen_tstqi_1 PROTO((rtx));
+extern rtx gen_tstqi PROTO((rtx));
+extern rtx gen_tstsf_cc PROTO((rtx));
+extern rtx gen_tstsf PROTO((rtx));
+extern rtx gen_tstdf_cc PROTO((rtx));
+extern rtx gen_tstdf PROTO((rtx));
+extern rtx gen_tstxf_cc PROTO((rtx));
+extern rtx gen_tstxf PROTO((rtx));
+extern rtx gen_cmpsi_1 PROTO((rtx, rtx));
+extern rtx gen_cmpsi PROTO((rtx, rtx));
+extern rtx gen_cmphi_1 PROTO((rtx, rtx));
+extern rtx gen_cmphi PROTO((rtx, rtx));
+extern rtx gen_cmpqi_1 PROTO((rtx, rtx));
+extern rtx gen_cmpqi PROTO((rtx, rtx));
+extern rtx gen_cmpsf_cc_1 PROTO((rtx, rtx, rtx));
+extern rtx gen_cmpxf PROTO((rtx, rtx));
+extern rtx gen_cmpdf PROTO((rtx, rtx));
+extern rtx gen_cmpsf PROTO((rtx, rtx));
+extern rtx gen_cmpxf_cc PROTO((rtx, rtx));
+extern rtx gen_cmpxf_ccfpeq PROTO((rtx, rtx));
+extern rtx gen_cmpdf_cc PROTO((rtx, rtx));
+extern rtx gen_cmpdf_ccfpeq PROTO((rtx, rtx));
+extern rtx gen_cmpsf_cc PROTO((rtx, rtx));
+extern rtx gen_cmpsf_ccfpeq PROTO((rtx, rtx));
+extern rtx gen_movsi PROTO((rtx, rtx));
+extern rtx gen_movhi PROTO((rtx, rtx));
+extern rtx gen_movstricthi PROTO((rtx, rtx));
+extern rtx gen_movqi PROTO((rtx, rtx));
+extern rtx gen_movstrictqi PROTO((rtx, rtx));
+extern rtx gen_movsf PROTO((rtx, rtx));
+extern rtx gen_swapdf PROTO((rtx, rtx));
+extern rtx gen_movdf PROTO((rtx, rtx));
+extern rtx gen_swapxf PROTO((rtx, rtx));
+extern rtx gen_movxf PROTO((rtx, rtx));
+extern rtx gen_movdi PROTO((rtx, rtx));
+extern rtx gen_zero_extendhisi2 PROTO((rtx, rtx));
+extern rtx gen_zero_extendqihi2 PROTO((rtx, rtx));
+extern rtx gen_zero_extendqisi2 PROTO((rtx, rtx));
+extern rtx gen_zero_extendsidi2 PROTO((rtx, rtx));
+extern rtx gen_extendsidi2 PROTO((rtx, rtx));
+extern rtx gen_extendhisi2 PROTO((rtx, rtx));
+extern rtx gen_extendqihi2 PROTO((rtx, rtx));
+extern rtx gen_extendqisi2 PROTO((rtx, rtx));
+extern rtx gen_extendsfdf2 PROTO((rtx, rtx));
+extern rtx gen_extenddfxf2 PROTO((rtx, rtx));
+extern rtx gen_extendsfxf2 PROTO((rtx, rtx));
+extern rtx gen_truncdfsf2 PROTO((rtx, rtx));
+extern rtx gen_truncxfsf2 PROTO((rtx, rtx));
+extern rtx gen_truncxfdf2 PROTO((rtx, rtx));
+extern rtx gen_fixuns_truncxfsi2 PROTO((rtx, rtx));
+extern rtx gen_fixuns_truncdfsi2 PROTO((rtx, rtx));
+extern rtx gen_fixuns_truncsfsi2 PROTO((rtx, rtx));
+extern rtx gen_fix_truncxfdi2 PROTO((rtx, rtx));
+extern rtx gen_fix_truncdfdi2 PROTO((rtx, rtx));
+extern rtx gen_fix_truncsfdi2 PROTO((rtx, rtx));
+extern rtx gen_fix_truncxfsi2 PROTO((rtx, rtx));
+extern rtx gen_fix_truncdfsi2 PROTO((rtx, rtx));
+extern rtx gen_fix_truncsfsi2 PROTO((rtx, rtx));
+extern rtx gen_floatsisf2 PROTO((rtx, rtx));
+extern rtx gen_floatdisf2 PROTO((rtx, rtx));
+extern rtx gen_floatsidf2 PROTO((rtx, rtx));
+extern rtx gen_floatdidf2 PROTO((rtx, rtx));
+extern rtx gen_floatsixf2 PROTO((rtx, rtx));
+extern rtx gen_floatdixf2 PROTO((rtx, rtx));
+extern rtx gen_adddi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_addsi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_addhi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_addqi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_addxf3 PROTO((rtx, rtx, rtx));
+extern rtx gen_adddf3 PROTO((rtx, rtx, rtx));
+extern rtx gen_addsf3 PROTO((rtx, rtx, rtx));
+extern rtx gen_subdi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_subsi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_subhi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_subqi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_subxf3 PROTO((rtx, rtx, rtx));
+extern rtx gen_subdf3 PROTO((rtx, rtx, rtx));
+extern rtx gen_subsf3 PROTO((rtx, rtx, rtx));
+extern rtx gen_mulhi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_mulsi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_umulqihi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_mulqihi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_umulsidi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_mulsidi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_mulxf3 PROTO((rtx, rtx, rtx));
+extern rtx gen_muldf3 PROTO((rtx, rtx, rtx));
+extern rtx gen_mulsf3 PROTO((rtx, rtx, rtx));
+extern rtx gen_divqi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_udivqi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_divxf3 PROTO((rtx, rtx, rtx));
+extern rtx gen_divdf3 PROTO((rtx, rtx, rtx));
+extern rtx gen_divsf3 PROTO((rtx, rtx, rtx));
+extern rtx gen_divmodsi4 PROTO((rtx, rtx, rtx, rtx));
+extern rtx gen_divmodhi4 PROTO((rtx, rtx, rtx, rtx));
+extern rtx gen_udivmodsi4 PROTO((rtx, rtx, rtx, rtx));
+extern rtx gen_udivmodhi4 PROTO((rtx, rtx, rtx, rtx));
+extern rtx gen_andsi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_andhi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_andqi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_iorsi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_iorhi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_iorqi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_xorsi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_xorhi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_xorqi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_negdi2 PROTO((rtx, rtx));
+extern rtx gen_negsi2 PROTO((rtx, rtx));
+extern rtx gen_neghi2 PROTO((rtx, rtx));
+extern rtx gen_negqi2 PROTO((rtx, rtx));
+extern rtx gen_negsf2 PROTO((rtx, rtx));
+extern rtx gen_negdf2 PROTO((rtx, rtx));
+extern rtx gen_negxf2 PROTO((rtx, rtx));
+extern rtx gen_abssf2 PROTO((rtx, rtx));
+extern rtx gen_absdf2 PROTO((rtx, rtx));
+extern rtx gen_absxf2 PROTO((rtx, rtx));
+extern rtx gen_sqrtsf2 PROTO((rtx, rtx));
+extern rtx gen_sqrtdf2 PROTO((rtx, rtx));
+extern rtx gen_sqrtxf2 PROTO((rtx, rtx));
+extern rtx gen_sindf2 PROTO((rtx, rtx));
+extern rtx gen_sinsf2 PROTO((rtx, rtx));
+extern rtx gen_cosdf2 PROTO((rtx, rtx));
+extern rtx gen_cossf2 PROTO((rtx, rtx));
+extern rtx gen_one_cmplsi2 PROTO((rtx, rtx));
+extern rtx gen_one_cmplhi2 PROTO((rtx, rtx));
+extern rtx gen_one_cmplqi2 PROTO((rtx, rtx));
+extern rtx gen_ashldi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_ashldi3_const_int PROTO((rtx, rtx, rtx));
+extern rtx gen_ashldi3_non_const_int PROTO((rtx, rtx, rtx));
+extern rtx gen_ashlsi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_ashlhi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_ashlqi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_ashrdi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_ashrdi3_const_int PROTO((rtx, rtx, rtx));
+extern rtx gen_ashrdi3_non_const_int PROTO((rtx, rtx, rtx));
+extern rtx gen_ashrsi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_ashrhi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_ashrqi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_lshrdi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_lshrdi3_const_int PROTO((rtx, rtx, rtx));
+extern rtx gen_lshrdi3_non_const_int PROTO((rtx, rtx, rtx));
+extern rtx gen_lshrsi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_lshrhi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_lshrqi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_rotlsi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_rotlhi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_rotlqi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_rotrsi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_rotrhi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_rotrqi3 PROTO((rtx, rtx, rtx));
+extern rtx gen_seq PROTO((rtx));
+extern rtx gen_sne PROTO((rtx));
+extern rtx gen_sgt PROTO((rtx));
+extern rtx gen_sgtu PROTO((rtx));
+extern rtx gen_slt PROTO((rtx));
+extern rtx gen_sltu PROTO((rtx));
+extern rtx gen_sge PROTO((rtx));
+extern rtx gen_sgeu PROTO((rtx));
+extern rtx gen_sle PROTO((rtx));
+extern rtx gen_sleu PROTO((rtx));
+extern rtx gen_beq PROTO((rtx));
+extern rtx gen_bne PROTO((rtx));
+extern rtx gen_bgt PROTO((rtx));
+extern rtx gen_bgtu PROTO((rtx));
+extern rtx gen_blt PROTO((rtx));
+extern rtx gen_bltu PROTO((rtx));
+extern rtx gen_bge PROTO((rtx));
+extern rtx gen_bgeu PROTO((rtx));
+extern rtx gen_ble PROTO((rtx));
+extern rtx gen_bleu PROTO((rtx));
+extern rtx gen_jump PROTO((rtx));
+extern rtx gen_indirect_jump PROTO((rtx));
+extern rtx gen_casesi PROTO((rtx, rtx, rtx, rtx, rtx));
+extern rtx gen_tablejump PROTO((rtx, rtx));
+extern rtx gen_untyped_call PROTO((rtx, rtx, rtx));
+extern rtx gen_untyped_return PROTO((rtx, rtx));
+extern rtx gen_update_return PROTO((rtx));
+extern rtx gen_return PROTO((void));
+extern rtx gen_nop PROTO((void));
+extern rtx gen_movstrsi PROTO((rtx, rtx, rtx, rtx));
+extern rtx gen_cmpstrsi PROTO((rtx, rtx, rtx, rtx, rtx));
+extern rtx gen_ffssi2 PROTO((rtx, rtx));
+extern rtx gen_ffshi2 PROTO((rtx, rtx));
+extern rtx gen_strlensi PROTO((rtx, rtx, rtx, rtx));
+
+#ifdef MD_CALL_PROTOTYPES
+extern rtx gen_call_pop PROTO((rtx, rtx, rtx));
+extern rtx gen_call PROTO((rtx, rtx));
+extern rtx gen_call_value_pop PROTO((rtx, rtx, rtx, rtx));
+extern rtx gen_call_value PROTO((rtx, rtx, rtx));
+
+#else /* !MD_CALL_PROTOTYPES */
+extern rtx gen_call_pop ();
+extern rtx gen_call ();
+extern rtx gen_call_value_pop ();
+extern rtx gen_call_value ();
+#endif /* !MD_CALL_PROTOTYPES */
+
+#else /* NO_MD_PROTOTYPES */
+extern rtx gen_tstsi_1 ();
+extern rtx gen_tstsi ();
+extern rtx gen_tsthi_1 ();
+extern rtx gen_tsthi ();
+extern rtx gen_tstqi_1 ();
+extern rtx gen_tstqi ();
+extern rtx gen_tstsf_cc ();
+extern rtx gen_tstsf ();
+extern rtx gen_tstdf_cc ();
+extern rtx gen_tstdf ();
+extern rtx gen_tstxf_cc ();
+extern rtx gen_tstxf ();
+extern rtx gen_cmpsi_1 ();
+extern rtx gen_cmpsi ();
+extern rtx gen_cmphi_1 ();
+extern rtx gen_cmphi ();
+extern rtx gen_cmpqi_1 ();
+extern rtx gen_cmpqi ();
+extern rtx gen_cmpsf_cc_1 ();
+extern rtx gen_cmpxf ();
+extern rtx gen_cmpdf ();
+extern rtx gen_cmpsf ();
+extern rtx gen_cmpxf_cc ();
+extern rtx gen_cmpxf_ccfpeq ();
+extern rtx gen_cmpdf_cc ();
+extern rtx gen_cmpdf_ccfpeq ();
+extern rtx gen_cmpsf_cc ();
+extern rtx gen_cmpsf_ccfpeq ();
+extern rtx gen_movsi ();
+extern rtx gen_movhi ();
+extern rtx gen_movstricthi ();
+extern rtx gen_movqi ();
+extern rtx gen_movstrictqi ();
+extern rtx gen_movsf ();
+extern rtx gen_swapdf ();
+extern rtx gen_movdf ();
+extern rtx gen_swapxf ();
+extern rtx gen_movxf ();
+extern rtx gen_movdi ();
+extern rtx gen_zero_extendhisi2 ();
+extern rtx gen_zero_extendqihi2 ();
+extern rtx gen_zero_extendqisi2 ();
+extern rtx gen_zero_extendsidi2 ();
+extern rtx gen_extendsidi2 ();
+extern rtx gen_extendhisi2 ();
+extern rtx gen_extendqihi2 ();
+extern rtx gen_extendqisi2 ();
+extern rtx gen_extendsfdf2 ();
+extern rtx gen_extenddfxf2 ();
+extern rtx gen_extendsfxf2 ();
+extern rtx gen_truncdfsf2 ();
+extern rtx gen_truncxfsf2 ();
+extern rtx gen_truncxfdf2 ();
+extern rtx gen_fixuns_truncxfsi2 ();
+extern rtx gen_fixuns_truncdfsi2 ();
+extern rtx gen_fixuns_truncsfsi2 ();
+extern rtx gen_fix_truncxfdi2 ();
+extern rtx gen_fix_truncdfdi2 ();
+extern rtx gen_fix_truncsfdi2 ();
+extern rtx gen_fix_truncxfsi2 ();
+extern rtx gen_fix_truncdfsi2 ();
+extern rtx gen_fix_truncsfsi2 ();
+extern rtx gen_floatsisf2 ();
+extern rtx gen_floatdisf2 ();
+extern rtx gen_floatsidf2 ();
+extern rtx gen_floatdidf2 ();
+extern rtx gen_floatsixf2 ();
+extern rtx gen_floatdixf2 ();
+extern rtx gen_adddi3 ();
+extern rtx gen_addsi3 ();
+extern rtx gen_addhi3 ();
+extern rtx gen_addqi3 ();
+extern rtx gen_addxf3 ();
+extern rtx gen_adddf3 ();
+extern rtx gen_addsf3 ();
+extern rtx gen_subdi3 ();
+extern rtx gen_subsi3 ();
+extern rtx gen_subhi3 ();
+extern rtx gen_subqi3 ();
+extern rtx gen_subxf3 ();
+extern rtx gen_subdf3 ();
+extern rtx gen_subsf3 ();
+extern rtx gen_mulhi3 ();
+extern rtx gen_mulsi3 ();
+extern rtx gen_umulqihi3 ();
+extern rtx gen_mulqihi3 ();
+extern rtx gen_umulsidi3 ();
+extern rtx gen_mulsidi3 ();
+extern rtx gen_mulxf3 ();
+extern rtx gen_muldf3 ();
+extern rtx gen_mulsf3 ();
+extern rtx gen_divqi3 ();
+extern rtx gen_udivqi3 ();
+extern rtx gen_divxf3 ();
+extern rtx gen_divdf3 ();
+extern rtx gen_divsf3 ();
+extern rtx gen_divmodsi4 ();
+extern rtx gen_divmodhi4 ();
+extern rtx gen_udivmodsi4 ();
+extern rtx gen_udivmodhi4 ();
+extern rtx gen_andsi3 ();
+extern rtx gen_andhi3 ();
+extern rtx gen_andqi3 ();
+extern rtx gen_iorsi3 ();
+extern rtx gen_iorhi3 ();
+extern rtx gen_iorqi3 ();
+extern rtx gen_xorsi3 ();
+extern rtx gen_xorhi3 ();
+extern rtx gen_xorqi3 ();
+extern rtx gen_negdi2 ();
+extern rtx gen_negsi2 ();
+extern rtx gen_neghi2 ();
+extern rtx gen_negqi2 ();
+extern rtx gen_negsf2 ();
+extern rtx gen_negdf2 ();
+extern rtx gen_negxf2 ();
+extern rtx gen_abssf2 ();
+extern rtx gen_absdf2 ();
+extern rtx gen_absxf2 ();
+extern rtx gen_sqrtsf2 ();
+extern rtx gen_sqrtdf2 ();
+extern rtx gen_sqrtxf2 ();
+extern rtx gen_sindf2 ();
+extern rtx gen_sinsf2 ();
+extern rtx gen_cosdf2 ();
+extern rtx gen_cossf2 ();
+extern rtx gen_one_cmplsi2 ();
+extern rtx gen_one_cmplhi2 ();
+extern rtx gen_one_cmplqi2 ();
+extern rtx gen_ashldi3 ();
+extern rtx gen_ashldi3_const_int ();
+extern rtx gen_ashldi3_non_const_int ();
+extern rtx gen_ashlsi3 ();
+extern rtx gen_ashlhi3 ();
+extern rtx gen_ashlqi3 ();
+extern rtx gen_ashrdi3 ();
+extern rtx gen_ashrdi3_const_int ();
+extern rtx gen_ashrdi3_non_const_int ();
+extern rtx gen_ashrsi3 ();
+extern rtx gen_ashrhi3 ();
+extern rtx gen_ashrqi3 ();
+extern rtx gen_lshrdi3 ();
+extern rtx gen_lshrdi3_const_int ();
+extern rtx gen_lshrdi3_non_const_int ();
+extern rtx gen_lshrsi3 ();
+extern rtx gen_lshrhi3 ();
+extern rtx gen_lshrqi3 ();
+extern rtx gen_rotlsi3 ();
+extern rtx gen_rotlhi3 ();
+extern rtx gen_rotlqi3 ();
+extern rtx gen_rotrsi3 ();
+extern rtx gen_rotrhi3 ();
+extern rtx gen_rotrqi3 ();
+extern rtx gen_seq ();
+extern rtx gen_sne ();
+extern rtx gen_sgt ();
+extern rtx gen_sgtu ();
+extern rtx gen_slt ();
+extern rtx gen_sltu ();
+extern rtx gen_sge ();
+extern rtx gen_sgeu ();
+extern rtx gen_sle ();
+extern rtx gen_sleu ();
+extern rtx gen_beq ();
+extern rtx gen_bne ();
+extern rtx gen_bgt ();
+extern rtx gen_bgtu ();
+extern rtx gen_blt ();
+extern rtx gen_bltu ();
+extern rtx gen_bge ();
+extern rtx gen_bgeu ();
+extern rtx gen_ble ();
+extern rtx gen_bleu ();
+extern rtx gen_jump ();
+extern rtx gen_indirect_jump ();
+extern rtx gen_casesi ();
+extern rtx gen_tablejump ();
+extern rtx gen_untyped_call ();
+extern rtx gen_untyped_return ();
+extern rtx gen_update_return ();
+extern rtx gen_return ();
+extern rtx gen_nop ();
+extern rtx gen_movstrsi ();
+extern rtx gen_cmpstrsi ();
+extern rtx gen_ffssi2 ();
+extern rtx gen_ffshi2 ();
+extern rtx gen_strlensi ();
+extern rtx gen_call_pop ();
+extern rtx gen_call ();
+extern rtx gen_call_value_pop ();
+extern rtx gen_call_value ();
+#endif /* NO_MD_PROTOTYPES */
diff --git a/gnu/usr.bin/cc/include/integrate.h b/gnu/usr.bin/cc/include/integrate.h
new file mode 100644
index 0000000..1176ac0
--- /dev/null
+++ b/gnu/usr.bin/cc/include/integrate.h
@@ -0,0 +1,125 @@
+/* Function integration definitions for GNU C-Compiler
+ Copyright (C) 1990 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This structure is used to remap objects in the function being inlined to
+ those belonging to the calling function. It is passed by
+ expand_inline_function to its children.
+
+ This structure is also used when unrolling loops and otherwise
+ replicating code, although not all fields are needed in this case;
+ only those fields needed by copy_rtx_and_substitute() and its children
+ are used.
+
+ This structure is used instead of static variables because
+ expand_inline_function may be called recursively via expand_expr. */
+
+struct inline_remap
+{
+ /* True if we are doing function integration, false otherwise.
+ Used to control whether RTX_UNCHANGING bits are copied by
+ copy_rtx_and_substitute. */
+ int integrating;
+ /* Definition of function be inlined. */
+ union tree_node *fndecl;
+ /* Place to put insns needed at start of function. */
+ rtx insns_at_start;
+ /* Mapping from old registers to new registers.
+ It is allocated and deallocated in `expand_inline_function' */
+ rtx *reg_map;
+ /* Mapping from old code-labels to new code-labels.
+ The first element of this map is label_map[min_labelno]. */
+ rtx *label_map;
+ /* Mapping from old insn uid's to copied insns. The first element
+ of this map is insn_map[min_insnno]; the last element is
+ insn_map[max_insnno]. We keep the bounds here for when the map
+ only covers a partial range of insns (such as loop unrolling or
+ code replication). */
+ rtx *insn_map;
+ int min_insnno, max_insnno;
+
+ /* Map pseudo reg number in calling function to equivalent constant. We
+ cannot in general substitute constants into parameter pseudo registers,
+ since some machine descriptions (many RISCs) won't always handle
+ the resulting insns. So if an incoming parameter has a constant
+ equivalent, we record it here, and if the resulting insn is
+ recognizable, we go with it.
+
+ We also use this mechanism to convert references to incoming arguments
+ and stacked variables. copy_rtx_and_substitute will replace the virtual
+ incoming argument and virtual stacked variables registers with new
+ pseudos that contain pointers into the replacement area allocated for
+ this inline instance. These pseudos are then marked as being equivalent
+ to the appropriate address and substituted if valid. */
+ rtx *const_equiv_map;
+ /* Number of entries in const_equiv_map and const_arg_map. */
+ int const_equiv_map_size;
+ /* This is incremented for each new basic block.
+ It is used to store in const_age_map to record the domain of validity
+ of each entry in const_equiv_map.
+ A value of -1 indicates an entry for a reg which is a parm.
+ All other values are "positive". */
+#define CONST_AGE_PARM (-1)
+ unsigned int const_age;
+ /* In parallel with const_equiv_map, record the valid age for each entry.
+ The entry is invalid if its age is less than const_age. */
+ unsigned int *const_age_map;
+ /* Target of the inline function being expanded, or NULL if none. */
+ rtx inline_target;
+ /* When an insn is being copied by copy_rtx_and_substitute,
+ this is nonzero if we have copied an ASM_OPERANDS.
+ In that case, it is the original input-operand vector. */
+ rtvec orig_asm_operands_vector;
+ /* When an insn is being copied by copy_rtx_and_substitute,
+ this is nonzero if we have copied an ASM_OPERANDS.
+ In that case, it is the copied input-operand vector. */
+ rtvec copy_asm_operands_vector;
+ /* Likewise, this is the copied constraints vector. */
+ rtvec copy_asm_constraints_vector;
+
+ /* The next few fields are used for subst_constants to record the SETs
+ that it saw. */
+ int num_sets;
+ struct equiv_table
+ {
+ rtx dest;
+ rtx equiv;
+ } equiv_sets[MAX_RECOG_OPERANDS];
+ /* Record the last thing assigned to pc. This is used for folded
+ conditional branch insns. */
+ rtx last_pc_value;
+#ifdef HAVE_cc0
+ /* Record the last thing assigned to cc0. */
+ rtx last_cc0_value;
+#endif
+};
+
+/* Return a copy of an rtx (as needed), substituting pseudo-register,
+ labels, and frame-pointer offsets as necessary. */
+extern rtx copy_rtx_and_substitute PROTO((rtx, struct inline_remap *));
+
+extern void try_constants PROTO((rtx, struct inline_remap *));
+
+extern void mark_stores PROTO((rtx, rtx));
+
+/* Unfortunately, we need a global copy of const_equiv map for communication
+ with a function called from note_stores. Be *very* careful that this
+ is used properly in the presence of recursion. */
+
+extern rtx *global_const_equiv_map;
+extern int global_const_equiv_map_size;
diff --git a/gnu/usr.bin/cc/include/longlong.h b/gnu/usr.bin/cc/include/longlong.h
new file mode 100644
index 0000000..e811c73
--- /dev/null
+++ b/gnu/usr.bin/cc/include/longlong.h
@@ -0,0 +1,1185 @@
+/* longlong.h -- definitions for mixed size 32/64 bit arithmetic.
+ Copyright (C) 1991, 1992, 1994 Free Software Foundation, Inc.
+
+ This definition file is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2, or (at your option) any later version.
+
+ This definition file is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef SI_TYPE_SIZE
+#define SI_TYPE_SIZE 32
+#endif
+
+#define __BITS4 (SI_TYPE_SIZE / 4)
+#define __ll_B (1L << (SI_TYPE_SIZE / 2))
+#define __ll_lowpart(t) ((USItype) (t) % __ll_B)
+#define __ll_highpart(t) ((USItype) (t) / __ll_B)
+
+/* Define auxiliary asm macros.
+
+ 1) umul_ppmm(high_prod, low_prod, multipler, multiplicand)
+ multiplies two USItype integers MULTIPLER and MULTIPLICAND,
+ and generates a two-part USItype product in HIGH_PROD and
+ LOW_PROD.
+
+ 2) __umulsidi3(a,b) multiplies two USItype integers A and B,
+ and returns a UDItype product. This is just a variant of umul_ppmm.
+
+ 3) udiv_qrnnd(quotient, remainder, high_numerator, low_numerator,
+ denominator) divides a two-word unsigned integer, composed by the
+ integers HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and
+ places the quotient in QUOTIENT and the remainder in REMAINDER.
+ HIGH_NUMERATOR must be less than DENOMINATOR for correct operation.
+ If, in addition, the most significant bit of DENOMINATOR must be 1,
+ then the pre-processor symbol UDIV_NEEDS_NORMALIZATION is defined to 1.
+
+ 4) sdiv_qrnnd(quotient, remainder, high_numerator, low_numerator,
+ denominator). Like udiv_qrnnd but the numbers are signed. The
+ quotient is rounded towards 0.
+
+ 5) count_leading_zeros(count, x) counts the number of zero-bits from
+ the msb to the first non-zero bit. This is the number of steps X
+ needs to be shifted left to set the msb. Undefined for X == 0.
+
+ 6) add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1,
+ high_addend_2, low_addend_2) adds two two-word unsigned integers,
+ composed by HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and
+ LOW_ADDEND_2 respectively. The result is placed in HIGH_SUM and
+ LOW_SUM. Overflow (i.e. carry out) is not stored anywhere, and is
+ lost.
+
+ 7) sub_ddmmss(high_difference, low_difference, high_minuend,
+ low_minuend, high_subtrahend, low_subtrahend) subtracts two
+ two-word unsigned integers, composed by HIGH_MINUEND_1 and
+ LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and LOW_SUBTRAHEND_2
+ respectively. The result is placed in HIGH_DIFFERENCE and
+ LOW_DIFFERENCE. Overflow (i.e. carry out) is not stored anywhere,
+ and is lost.
+
+ If any of these macros are left undefined for a particular CPU,
+ C macros are used. */
+
+/* The CPUs come in alphabetical order below.
+
+ Please add support for more CPUs here, or improve the current support
+ for the CPUs below!
+ (E.g. WE32100, IBM360.) */
+
+#if defined (__GNUC__) && !defined (NO_ASM)
+
+/* We sometimes need to clobber "cc" with gcc2, but that would not be
+ understood by gcc1. Use cpp to avoid major code duplication. */
+#if __GNUC__ < 2
+#define __CLOBBER_CC
+#define __AND_CLOBBER_CC
+#else /* __GNUC__ >= 2 */
+#define __CLOBBER_CC : "cc"
+#define __AND_CLOBBER_CC , "cc"
+#endif /* __GNUC__ < 2 */
+
+#if defined (__a29k__) || defined (_AM29K)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add %1,%4,%5
+ addc %0,%2,%3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%r" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "%r" ((USItype)(al)), \
+ "rI" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub %1,%4,%5
+ subc %0,%2,%3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "r" ((USItype)(al)), \
+ "rI" ((USItype)(bl)))
+#define umul_ppmm(xh, xl, m0, m1) \
+ do { \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("multiplu %0,%1,%2" \
+ : "=r" ((USItype)(xl)) \
+ : "r" (__m0), \
+ "r" (__m1)); \
+ __asm__ ("multmu %0,%1,%2" \
+ : "=r" ((USItype)(xh)) \
+ : "r" (__m0), \
+ "r" (__m1)); \
+ } while (0)
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("dividu %0,%3,%4" \
+ : "=r" ((USItype)(q)), \
+ "=q" ((USItype)(r)) \
+ : "1" ((USItype)(n1)), \
+ "r" ((USItype)(n0)), \
+ "r" ((USItype)(d)))
+#define count_leading_zeros(count, x) \
+ __asm__ ("clz %0,%1" \
+ : "=r" ((USItype)(count)) \
+ : "r" ((USItype)(x)))
+#endif /* __a29k__ */
+
+#if defined (__arm__)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("adds %1, %4, %5
+ adc %0, %2, %3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%r" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "%r" ((USItype)(al)), \
+ "rI" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subs %1, %4, %5
+ sbc %0, %2, %3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "r" ((USItype)(al)), \
+ "rI" ((USItype)(bl)))
+#define umul_ppmm(xh, xl, a, b) \
+{register USItype __t0, __t1, __t2; \
+ __asm__ ("%@ Inlined umul_ppmm
+ mov %2, %5, lsr #16
+ mov %0, %6, lsr #16
+ bic %3, %5, %2, lsl #16
+ bic %4, %6, %0, lsl #16
+ mul %1, %3, %4
+ mul %4, %2, %4
+ mul %3, %0, %3
+ mul %0, %2, %0
+ adds %3, %4, %3
+ addcs %0, %0, #65536
+ adds %1, %1, %3, lsl #16
+ adc %0, %0, %3, lsr #16" \
+ : "=&r" ((USItype)(xh)), \
+ "=r" ((USItype)(xl)), \
+ "=&r" (__t0), "=&r" (__t1), "=r" (__t2) \
+ : "r" ((USItype)(a)), \
+ "r" ((USItype)(b)));}
+#define UMUL_TIME 20
+#define UDIV_TIME 100
+#endif /* __arm__ */
+
+#if defined (__clipper__)
+#define umul_ppmm(w1, w0, u, v) \
+ ({union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __xx; \
+ __asm__ ("mulwux %2,%0" \
+ : "=r" (__xx.__ll) \
+ : "%0" ((USItype)(u)), \
+ "r" ((USItype)(v))); \
+ (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#define smul_ppmm(w1, w0, u, v) \
+ ({union {DItype __ll; \
+ struct {SItype __l, __h;} __i; \
+ } __xx; \
+ __asm__ ("mulwx %2,%0" \
+ : "=r" (__xx.__ll) \
+ : "%0" ((SItype)(u)), \
+ "r" ((SItype)(v))); \
+ (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#define __umulsidi3(u, v) \
+ ({UDItype __w; \
+ __asm__ ("mulwux %2,%0" \
+ : "=r" (__w) \
+ : "%0" ((USItype)(u)), \
+ "r" ((USItype)(v))); \
+ __w; })
+#endif /* __clipper__ */
+
+#if defined (__gmicro__)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add.w %5,%1
+ addx %3,%0" \
+ : "=g" ((USItype)(sh)), \
+ "=&g" ((USItype)(sl)) \
+ : "%0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub.w %5,%1
+ subx %3,%0" \
+ : "=g" ((USItype)(sh)), \
+ "=&g" ((USItype)(sl)) \
+ : "0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define umul_ppmm(ph, pl, m0, m1) \
+ __asm__ ("mulx %3,%0,%1" \
+ : "=g" ((USItype)(ph)), \
+ "=r" ((USItype)(pl)) \
+ : "%0" ((USItype)(m0)), \
+ "g" ((USItype)(m1)))
+#define udiv_qrnnd(q, r, nh, nl, d) \
+ __asm__ ("divx %4,%0,%1" \
+ : "=g" ((USItype)(q)), \
+ "=r" ((USItype)(r)) \
+ : "1" ((USItype)(nh)), \
+ "0" ((USItype)(nl)), \
+ "g" ((USItype)(d)))
+#define count_leading_zeros(count, x) \
+ __asm__ ("bsch/1 %1,%0" \
+ : "=g" (count) \
+ : "g" ((USItype)(x)), \
+ "0" ((USItype)0))
+#endif
+
+#if defined (__hppa)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add %4,%5,%1
+ addc %2,%3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%rM" ((USItype)(ah)), \
+ "rM" ((USItype)(bh)), \
+ "%rM" ((USItype)(al)), \
+ "rM" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub %4,%5,%1
+ subb %2,%3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "rM" ((USItype)(ah)), \
+ "rM" ((USItype)(bh)), \
+ "rM" ((USItype)(al)), \
+ "rM" ((USItype)(bl)))
+#if defined (_PA_RISC1_1)
+#define umul_ppmm(w1, w0, u, v) \
+ do { \
+ union \
+ { \
+ UDItype __f; \
+ struct {USItype __w1, __w0;} __w1w0; \
+ } __t; \
+ __asm__ ("xmpyu %1,%2,%0" \
+ : "=x" (__t.__f) \
+ : "x" ((USItype)(u)), \
+ "x" ((USItype)(v))); \
+ (w1) = __t.__w1w0.__w1; \
+ (w0) = __t.__w1w0.__w0; \
+ } while (0)
+#define UMUL_TIME 8
+#else
+#define UMUL_TIME 30
+#endif
+#define UDIV_TIME 40
+#define count_leading_zeros(count, x) \
+ do { \
+ USItype __tmp; \
+ __asm__ ( \
+ "ldi 1,%0
+ extru,= %1,15,16,%%r0 ; Bits 31..16 zero?
+ extru,tr %1,15,16,%1 ; No. Shift down, skip add.
+ ldo 16(%0),%0 ; Yes. Perform add.
+ extru,= %1,23,8,%%r0 ; Bits 15..8 zero?
+ extru,tr %1,23,8,%1 ; No. Shift down, skip add.
+ ldo 8(%0),%0 ; Yes. Perform add.
+ extru,= %1,27,4,%%r0 ; Bits 7..4 zero?
+ extru,tr %1,27,4,%1 ; No. Shift down, skip add.
+ ldo 4(%0),%0 ; Yes. Perform add.
+ extru,= %1,29,2,%%r0 ; Bits 3..2 zero?
+ extru,tr %1,29,2,%1 ; No. Shift down, skip add.
+ ldo 2(%0),%0 ; Yes. Perform add.
+ extru %1,30,1,%1 ; Extract bit 1.
+ sub %0,%1,%0 ; Subtract it.
+ " : "=r" (count), "=r" (__tmp) : "1" (x)); \
+ } while (0)
+#endif
+
+#if defined (__i386__) || defined (__i486__)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addl %5,%1
+ adcl %3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subl %5,%1
+ sbbl %3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("mull %3" \
+ : "=a" ((USItype)(w0)), \
+ "=d" ((USItype)(w1)) \
+ : "%0" ((USItype)(u)), \
+ "rm" ((USItype)(v)))
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("divl %4" \
+ : "=a" ((USItype)(q)), \
+ "=d" ((USItype)(r)) \
+ : "0" ((USItype)(n0)), \
+ "1" ((USItype)(n1)), \
+ "rm" ((USItype)(d)))
+#define count_leading_zeros(count, x) \
+ do { \
+ USItype __cbtmp; \
+ __asm__ ("bsrl %1,%0" \
+ : "=r" (__cbtmp) : "rm" ((USItype)(x))); \
+ (count) = __cbtmp ^ 31; \
+ } while (0)
+#define UMUL_TIME 40
+#define UDIV_TIME 40
+#endif /* 80x86 */
+
+#if defined (__i860__)
+#if 0
+/* Make sure these patterns really improve the code before
+ switching them on. */
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ do { \
+ union \
+ { \
+ DItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __a, __b, __s; \
+ __a.__i.__l = (al); \
+ __a.__i.__h = (ah); \
+ __b.__i.__l = (bl); \
+ __b.__i.__h = (bh); \
+ __asm__ ("fiadd.dd %1,%2,%0" \
+ : "=f" (__s.__ll) \
+ : "%f" (__a.__ll), "f" (__b.__ll)); \
+ (sh) = __s.__i.__h; \
+ (sl) = __s.__i.__l; \
+ } while (0)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ do { \
+ union \
+ { \
+ DItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __a, __b, __s; \
+ __a.__i.__l = (al); \
+ __a.__i.__h = (ah); \
+ __b.__i.__l = (bl); \
+ __b.__i.__h = (bh); \
+ __asm__ ("fisub.dd %1,%2,%0" \
+ : "=f" (__s.__ll) \
+ : "%f" (__a.__ll), "f" (__b.__ll)); \
+ (sh) = __s.__i.__h; \
+ (sl) = __s.__i.__l; \
+ } while (0)
+#endif
+#endif /* __i860__ */
+
+#if defined (__i960__)
+#define umul_ppmm(w1, w0, u, v) \
+ ({union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __xx; \
+ __asm__ ("emul %2,%1,%0" \
+ : "=d" (__xx.__ll) \
+ : "%dI" ((USItype)(u)), \
+ "dI" ((USItype)(v))); \
+ (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#define __umulsidi3(u, v) \
+ ({UDItype __w; \
+ __asm__ ("emul %2,%1,%0" \
+ : "=d" (__w) \
+ : "%dI" ((USItype)(u)), \
+ "dI" ((USItype)(v))); \
+ __w; })
+#endif /* __i960__ */
+
+#if defined (__mc68000__)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add%.l %5,%1
+ addx%.l %3,%0" \
+ : "=d" ((USItype)(sh)), \
+ "=&d" ((USItype)(sl)) \
+ : "%0" ((USItype)(ah)), \
+ "d" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub%.l %5,%1
+ subx%.l %3,%0" \
+ : "=d" ((USItype)(sh)), \
+ "=&d" ((USItype)(sl)) \
+ : "0" ((USItype)(ah)), \
+ "d" ((USItype)(bh)), \
+ "1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#if defined (__mc68020__) || defined (__NeXT__) || defined(mc68020)
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("mulu%.l %3,%1:%0" \
+ : "=d" ((USItype)(w0)), \
+ "=d" ((USItype)(w1)) \
+ : "%0" ((USItype)(u)), \
+ "dmi" ((USItype)(v)))
+#define UMUL_TIME 45
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("divu%.l %4,%1:%0" \
+ : "=d" ((USItype)(q)), \
+ "=d" ((USItype)(r)) \
+ : "0" ((USItype)(n0)), \
+ "1" ((USItype)(n1)), \
+ "dmi" ((USItype)(d)))
+#define UDIV_TIME 90
+#define sdiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("divs%.l %4,%1:%0" \
+ : "=d" ((USItype)(q)), \
+ "=d" ((USItype)(r)) \
+ : "0" ((USItype)(n0)), \
+ "1" ((USItype)(n1)), \
+ "dmi" ((USItype)(d)))
+#define count_leading_zeros(count, x) \
+ __asm__ ("bfffo %1{%b2:%b2},%0" \
+ : "=d" ((USItype)(count)) \
+ : "od" ((USItype)(x)), "n" (0))
+#else /* not mc68020 */
+/* %/ inserts REGISTER_PREFIX. */
+#define umul_ppmm(xh, xl, a, b) \
+ __asm__ ("| Inlined umul_ppmm
+ move%.l %2,%/d0
+ move%.l %3,%/d1
+ move%.l %/d0,%/d2
+ swap %/d0
+ move%.l %/d1,%/d3
+ swap %/d1
+ move%.w %/d2,%/d4
+ mulu %/d3,%/d4
+ mulu %/d1,%/d2
+ mulu %/d0,%/d3
+ mulu %/d0,%/d1
+ move%.l %/d4,%/d0
+ eor%.w %/d0,%/d0
+ swap %/d0
+ add%.l %/d0,%/d2
+ add%.l %/d3,%/d2
+ jcc 1f
+ add%.l #65536,%/d1
+1: swap %/d2
+ moveq #0,%/d0
+ move%.w %/d2,%/d0
+ move%.w %/d4,%/d2
+ move%.l %/d2,%1
+ add%.l %/d1,%/d0
+ move%.l %/d0,%0" \
+ : "=g" ((USItype)(xh)), \
+ "=g" ((USItype)(xl)) \
+ : "g" ((USItype)(a)), \
+ "g" ((USItype)(b)) \
+ : "d0", "d1", "d2", "d3", "d4")
+#define UMUL_TIME 100
+#define UDIV_TIME 400
+#endif /* not mc68020 */
+#endif /* mc68000 */
+
+#if defined (__m88000__)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addu.co %1,%r4,%r5
+ addu.ci %0,%r2,%r3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%rJ" ((USItype)(ah)), \
+ "rJ" ((USItype)(bh)), \
+ "%rJ" ((USItype)(al)), \
+ "rJ" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subu.co %1,%r4,%r5
+ subu.ci %0,%r2,%r3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "rJ" ((USItype)(ah)), \
+ "rJ" ((USItype)(bh)), \
+ "rJ" ((USItype)(al)), \
+ "rJ" ((USItype)(bl)))
+#define count_leading_zeros(count, x) \
+ do { \
+ USItype __cbtmp; \
+ __asm__ ("ff1 %0,%1" \
+ : "=r" (__cbtmp) \
+ : "r" ((USItype)(x))); \
+ (count) = __cbtmp ^ 31; \
+ } while (0)
+#if defined (__mc88110__)
+#define umul_ppmm(wh, wl, u, v) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __xx; \
+ __asm__ ("mulu.d %0,%1,%2" \
+ : "=r" (__xx.__ll) \
+ : "r" ((USItype)(u)), \
+ "r" ((USItype)(v))); \
+ (wh) = __xx.__i.__h; \
+ (wl) = __xx.__i.__l; \
+ } while (0)
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ ({union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __xx; \
+ USItype __q; \
+ __xx.__i.__h = (n1); __xx.__i.__l = (n0); \
+ __asm__ ("divu.d %0,%1,%2" \
+ : "=r" (__q) \
+ : "r" (__xx.__ll), \
+ "r" ((USItype)(d))); \
+ (r) = (n0) - __q * (d); (q) = __q; })
+#define UMUL_TIME 5
+#define UDIV_TIME 25
+#else
+#define UMUL_TIME 17
+#define UDIV_TIME 150
+#endif /* __mc88110__ */
+#endif /* __m88000__ */
+
+#if defined (__mips__)
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("multu %2,%3
+ mflo %0
+ mfhi %1" \
+ : "=d" ((USItype)(w0)), \
+ "=d" ((USItype)(w1)) \
+ : "d" ((USItype)(u)), \
+ "d" ((USItype)(v)))
+#define UMUL_TIME 10
+#define UDIV_TIME 100
+#endif /* __mips__ */
+
+#if defined (__ns32000__)
+#define umul_ppmm(w1, w0, u, v) \
+ ({union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __xx; \
+ __asm__ ("meid %2,%0" \
+ : "=g" (__xx.__ll) \
+ : "%0" ((USItype)(u)), \
+ "g" ((USItype)(v))); \
+ (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#define __umulsidi3(u, v) \
+ ({UDItype __w; \
+ __asm__ ("meid %2,%0" \
+ : "=g" (__w) \
+ : "%0" ((USItype)(u)), \
+ "g" ((USItype)(v))); \
+ __w; })
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ ({union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __xx; \
+ __xx.__i.__h = (n1); __xx.__i.__l = (n0); \
+ __asm__ ("deid %2,%0" \
+ : "=g" (__xx.__ll) \
+ : "0" (__xx.__ll), \
+ "g" ((USItype)(d))); \
+ (r) = __xx.__i.__l; (q) = __xx.__i.__h; })
+#endif /* __ns32000__ */
+
+#if (defined (_ARCH_PPC) || defined (_IBMR2)) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ do { \
+ if (__builtin_constant_p (bh) && (bh) == 0) \
+ __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{aze|addze} %0,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%r" ((USItype)(ah)), \
+ "%r" ((USItype)(al)), \
+ "rI" ((USItype)(bl))); \
+ else if (__builtin_constant_p (bh) && (bh) ==~(USItype) 0) \
+ __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{ame|addme} %0,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%r" ((USItype)(ah)), \
+ "%r" ((USItype)(al)), \
+ "rI" ((USItype)(bl))); \
+ else \
+ __asm__ ("{a%I5|add%I5c} %1,%4,%5\n\t{ae|adde} %0,%2,%3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%r" ((USItype)(ah)), \
+ "r" ((USItype)(bh)), \
+ "%r" ((USItype)(al)), \
+ "rI" ((USItype)(bl))); \
+ } while (0)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ do { \
+ if (__builtin_constant_p (ah) && (ah) == 0) \
+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfze|subfze} %0,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(bh)), \
+ "rI" ((USItype)(al)), \
+ "r" ((USItype)(bl))); \
+ else if (__builtin_constant_p (ah) && (ah) ==~(USItype) 0) \
+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfme|subfme} %0,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(bh)), \
+ "rI" ((USItype)(al)), \
+ "r" ((USItype)(bl))); \
+ else if (__builtin_constant_p (bh) && (bh) == 0) \
+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{ame|addme} %0,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(ah)), \
+ "rI" ((USItype)(al)), \
+ "r" ((USItype)(bl))); \
+ else if (__builtin_constant_p (bh) && (bh) ==~(USItype) 0) \
+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{aze|addze} %0,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(ah)), \
+ "rI" ((USItype)(al)), \
+ "r" ((USItype)(bl))); \
+ else \
+ __asm__ ("{sf%I4|subf%I4c} %1,%5,%4\n\t{sfe|subfe} %0,%3,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(ah)), \
+ "r" ((USItype)(bh)), \
+ "rI" ((USItype)(al)), \
+ "r" ((USItype)(bl))); \
+ } while (0)
+#define count_leading_zeros(count, x) \
+ __asm__ ("{cntlz|cntlzw} %0,%1" \
+ : "=r" ((USItype)(count)) \
+ : "r" ((USItype)(x)))
+#if defined (_ARCH_PPC)
+#define umul_ppmm(ph, pl, m0, m1) \
+ do { \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("mulhwu %0,%1,%2" \
+ : "=r" ((USItype) ph) \
+ : "%r" (__m0), \
+ "r" (__m1)); \
+ (pl) = __m0 * __m1; \
+ } while (0)
+#define UMUL_TIME 15
+#define smul_ppmm(ph, pl, m0, m1) \
+ do { \
+ SItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("mulhw %0,%1,%2" \
+ : "=r" ((SItype) ph) \
+ : "%r" (__m0), \
+ "r" (__m1)); \
+ (pl) = __m0 * __m1; \
+ } while (0)
+#define SMUL_TIME 14
+#define UDIV_TIME 120
+#else
+#define umul_ppmm(xh, xl, m0, m1) \
+ do { \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("mul %0,%2,%3" \
+ : "=r" ((USItype)(xh)), \
+ "=q" ((USItype)(xl)) \
+ : "r" (__m0), \
+ "r" (__m1)); \
+ (xh) += ((((SItype) __m0 >> 31) & __m1) \
+ + (((SItype) __m1 >> 31) & __m0)); \
+ } while (0)
+#define UMUL_TIME 8
+#define smul_ppmm(xh, xl, m0, m1) \
+ __asm__ ("mul %0,%2,%3" \
+ : "=r" ((SItype)(xh)), \
+ "=q" ((SItype)(xl)) \
+ : "r" (m0), \
+ "r" (m1))
+#define SMUL_TIME 4
+#define sdiv_qrnnd(q, r, nh, nl, d) \
+ __asm__ ("div %0,%2,%4" \
+ : "=r" ((SItype)(q)), "=q" ((SItype)(r)) \
+ : "r" ((SItype)(nh)), "1" ((SItype)(nl)), "r" ((SItype)(d)))
+#define UDIV_TIME 100
+#endif
+#endif /* Power architecture variants. */
+
+#if defined (__pyr__)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addw %5,%1
+ addwc %3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subw %5,%1
+ subwb %3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+/* This insn doesn't work on ancient pyramids. */
+#define umul_ppmm(w1, w0, u, v) \
+ ({union { \
+ UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __xx; \
+ __xx.__i.__l = u; \
+ __asm__ ("uemul %3,%0" \
+ : "=r" (__xx.__i.__h), \
+ "=r" (__xx.__i.__l) \
+ : "1" (__xx.__i.__l), \
+ "g" ((USItype)(v))); \
+ (w1) = __xx.__i.__h; \
+ (w0) = __xx.__i.__l;})
+#endif /* __pyr__ */
+
+#if defined (__ibm032__) /* RT/ROMP */
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("a %1,%5
+ ae %0,%3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%0" ((USItype)(ah)), \
+ "r" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), \
+ "r" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("s %1,%5
+ se %0,%3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "0" ((USItype)(ah)), \
+ "r" ((USItype)(bh)), \
+ "1" ((USItype)(al)), \
+ "r" ((USItype)(bl)))
+#define umul_ppmm(ph, pl, m0, m1) \
+ do { \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ( \
+ "s r2,r2
+ mts r10,%2
+ m r2,%3
+ m r2,%3
+ m r2,%3
+ m r2,%3
+ m r2,%3
+ m r2,%3
+ m r2,%3
+ m r2,%3
+ m r2,%3
+ m r2,%3
+ m r2,%3
+ m r2,%3
+ m r2,%3
+ m r2,%3
+ m r2,%3
+ m r2,%3
+ cas %0,r2,r0
+ mfs r10,%1" \
+ : "=r" ((USItype)(ph)), \
+ "=r" ((USItype)(pl)) \
+ : "%r" (__m0), \
+ "r" (__m1) \
+ : "r2"); \
+ (ph) += ((((SItype) __m0 >> 31) & __m1) \
+ + (((SItype) __m1 >> 31) & __m0)); \
+ } while (0)
+#define UMUL_TIME 20
+#define UDIV_TIME 200
+#define count_leading_zeros(count, x) \
+ do { \
+ if ((x) >= 0x10000) \
+ __asm__ ("clz %0,%1" \
+ : "=r" ((USItype)(count)) \
+ : "r" ((USItype)(x) >> 16)); \
+ else \
+ { \
+ __asm__ ("clz %0,%1" \
+ : "=r" ((USItype)(count)) \
+ : "r" ((USItype)(x))); \
+ (count) += 16; \
+ } \
+ } while (0)
+#endif
+
+#if defined (__sparc__)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addcc %r4,%5,%1
+ addx %r2,%3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%rJ" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "%rJ" ((USItype)(al)), \
+ "rI" ((USItype)(bl)) \
+ __CLOBBER_CC)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subcc %r4,%5,%1
+ subx %r2,%3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "rJ" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "rJ" ((USItype)(al)), \
+ "rI" ((USItype)(bl)) \
+ __CLOBBER_CC)
+#if defined (__sparc_v8__)
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("umul %2,%3,%1;rd %%y,%0" \
+ : "=r" ((USItype)(w1)), \
+ "=r" ((USItype)(w0)) \
+ : "r" ((USItype)(u)), \
+ "r" ((USItype)(v)))
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("mov %2,%%y;nop;nop;nop;udiv %3,%4,%0;umul %0,%4,%1;sub %3,%1,%1"\
+ : "=&r" ((USItype)(q)), \
+ "=&r" ((USItype)(r)) \
+ : "r" ((USItype)(n1)), \
+ "r" ((USItype)(n0)), \
+ "r" ((USItype)(d)))
+#else
+#if defined (__sparclite__)
+/* This has hardware multiply but not divide. It also has two additional
+ instructions scan (ffs from high bit) and divscc. */
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("umul %2,%3,%1;rd %%y,%0" \
+ : "=r" ((USItype)(w1)), \
+ "=r" ((USItype)(w0)) \
+ : "r" ((USItype)(u)), \
+ "r" ((USItype)(v)))
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("! Inlined udiv_qrnnd
+ wr %%g0,%2,%%y ! Not a delayed write for sparclite
+ tst %%g0
+ divscc %3,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%%g1
+ divscc %%g1,%4,%0
+ rd %%y,%1
+ bl,a 1f
+ add %1,%4,%1
+1: ! End of inline udiv_qrnnd" \
+ : "=r" ((USItype)(q)), \
+ "=r" ((USItype)(r)) \
+ : "r" ((USItype)(n1)), \
+ "r" ((USItype)(n0)), \
+ "rI" ((USItype)(d)) \
+ : "%g1" __AND_CLOBBER_CC)
+#define UDIV_TIME 37
+#define count_leading_zeros(count, x) \
+ __asm__ ("scan %1,0,%0" \
+ : "=r" ((USItype)(x)) \
+ : "r" ((USItype)(count)))
+#else
+/* SPARC without integer multiplication and divide instructions.
+ (i.e. at least Sun4/20,40,60,65,75,110,260,280,330,360,380,470,490) */
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("! Inlined umul_ppmm
+ wr %%g0,%2,%%y ! SPARC has 0-3 delay insn after a wr
+ sra %3,31,%%g2 ! Don't move this insn
+ and %2,%%g2,%%g2 ! Don't move this insn
+ andcc %%g0,0,%%g1 ! Don't move this insn
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,0,%%g1
+ add %%g1,%%g2,%0
+ rd %%y,%1" \
+ : "=r" ((USItype)(w1)), \
+ "=r" ((USItype)(w0)) \
+ : "%rI" ((USItype)(u)), \
+ "r" ((USItype)(v)) \
+ : "%g1", "%g2" __AND_CLOBBER_CC)
+#define UMUL_TIME 39 /* 39 instructions */
+/* It's quite necessary to add this much assembler for the sparc.
+ The default udiv_qrnnd (in C) is more than 10 times slower! */
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("! Inlined udiv_qrnnd
+ mov 32,%%g1
+ subcc %1,%2,%%g0
+1: bcs 5f
+ addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb
+ sub %1,%2,%1 ! this kills msb of n
+ addx %1,%1,%1 ! so this can't give carry
+ subcc %%g1,1,%%g1
+2: bne 1b
+ subcc %1,%2,%%g0
+ bcs 3f
+ addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb
+ b 3f
+ sub %1,%2,%1 ! this kills msb of n
+4: sub %1,%2,%1
+5: addxcc %1,%1,%1
+ bcc 2b
+ subcc %%g1,1,%%g1
+! Got carry from n. Subtract next step to cancel this carry.
+ bne 4b
+ addcc %0,%0,%0 ! shift n1n0 and a 0-bit in lsb
+ sub %1,%2,%1
+3: xnor %0,0,%0
+ ! End of inline udiv_qrnnd" \
+ : "=&r" ((USItype)(q)), \
+ "=&r" ((USItype)(r)) \
+ : "r" ((USItype)(d)), \
+ "1" ((USItype)(n1)), \
+ "0" ((USItype)(n0)) : "%g1" __AND_CLOBBER_CC)
+#define UDIV_TIME (3+7*32) /* 7 instructions/iteration. 32 iterations. */
+#endif /* __sparclite__ */
+#endif /* __sparc_v8__ */
+#endif /* __sparc__ */
+
+#if defined (__vax__)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addl2 %5,%1
+ adwc %3,%0" \
+ : "=g" ((USItype)(sh)), \
+ "=&g" ((USItype)(sl)) \
+ : "%0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subl2 %5,%1
+ sbwc %3,%0" \
+ : "=g" ((USItype)(sh)), \
+ "=&g" ((USItype)(sl)) \
+ : "0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define umul_ppmm(xh, xl, m0, m1) \
+ do { \
+ union { \
+ UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __xx; \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("emul %1,%2,$0,%0" \
+ : "=r" (__xx.__ll) \
+ : "g" (__m0), \
+ "g" (__m1)); \
+ (xh) = __xx.__i.__h; \
+ (xl) = __xx.__i.__l; \
+ (xh) += ((((SItype) __m0 >> 31) & __m1) \
+ + (((SItype) __m1 >> 31) & __m0)); \
+ } while (0)
+#define sdiv_qrnnd(q, r, n1, n0, d) \
+ do { \
+ union {DItype __ll; \
+ struct {SItype __l, __h;} __i; \
+ } __xx; \
+ __xx.__i.__h = n1; __xx.__i.__l = n0; \
+ __asm__ ("ediv %3,%2,%0,%1" \
+ : "=g" (q), "=g" (r) \
+ : "g" (__xx.__ll), "g" (d)); \
+ } while (0)
+#endif /* __vax__ */
+
+#endif /* __GNUC__ */
+
+/* If this machine has no inline assembler, use C macros. */
+
+#if !defined (add_ssaaaa)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ do { \
+ USItype __x; \
+ __x = (al) + (bl); \
+ (sh) = (ah) + (bh) + (__x < (al)); \
+ (sl) = __x; \
+ } while (0)
+#endif
+
+#if !defined (sub_ddmmss)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ do { \
+ USItype __x; \
+ __x = (al) - (bl); \
+ (sh) = (ah) - (bh) - (__x > (al)); \
+ (sl) = __x; \
+ } while (0)
+#endif
+
+#if !defined (umul_ppmm)
+#define umul_ppmm(w1, w0, u, v) \
+ do { \
+ USItype __x0, __x1, __x2, __x3; \
+ USItype __ul, __vl, __uh, __vh; \
+ \
+ __ul = __ll_lowpart (u); \
+ __uh = __ll_highpart (u); \
+ __vl = __ll_lowpart (v); \
+ __vh = __ll_highpart (v); \
+ \
+ __x0 = (USItype) __ul * __vl; \
+ __x1 = (USItype) __ul * __vh; \
+ __x2 = (USItype) __uh * __vl; \
+ __x3 = (USItype) __uh * __vh; \
+ \
+ __x1 += __ll_highpart (__x0);/* this can't give carry */ \
+ __x1 += __x2; /* but this indeed can */ \
+ if (__x1 < __x2) /* did we get it? */ \
+ __x3 += __ll_B; /* yes, add it in the proper pos. */ \
+ \
+ (w1) = __x3 + __ll_highpart (__x1); \
+ (w0) = __ll_lowpart (__x1) * __ll_B + __ll_lowpart (__x0); \
+ } while (0)
+#endif
+
+#if !defined (__umulsidi3)
+#define __umulsidi3(u, v) \
+ ({DIunion __w; \
+ umul_ppmm (__w.s.high, __w.s.low, u, v); \
+ __w.ll; })
+#endif
+
+/* Define this unconditionally, so it can be used for debugging. */
+#define __udiv_qrnnd_c(q, r, n1, n0, d) \
+ do { \
+ USItype __d1, __d0, __q1, __q0; \
+ USItype __r1, __r0, __m; \
+ __d1 = __ll_highpart (d); \
+ __d0 = __ll_lowpart (d); \
+ \
+ __r1 = (n1) % __d1; \
+ __q1 = (n1) / __d1; \
+ __m = (USItype) __q1 * __d0; \
+ __r1 = __r1 * __ll_B | __ll_highpart (n0); \
+ if (__r1 < __m) \
+ { \
+ __q1--, __r1 += (d); \
+ if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */\
+ if (__r1 < __m) \
+ __q1--, __r1 += (d); \
+ } \
+ __r1 -= __m; \
+ \
+ __r0 = __r1 % __d1; \
+ __q0 = __r1 / __d1; \
+ __m = (USItype) __q0 * __d0; \
+ __r0 = __r0 * __ll_B | __ll_lowpart (n0); \
+ if (__r0 < __m) \
+ { \
+ __q0--, __r0 += (d); \
+ if (__r0 >= (d)) \
+ if (__r0 < __m) \
+ __q0--, __r0 += (d); \
+ } \
+ __r0 -= __m; \
+ \
+ (q) = (USItype) __q1 * __ll_B | __q0; \
+ (r) = __r0; \
+ } while (0)
+
+/* If the processor has no udiv_qrnnd but sdiv_qrnnd, go through
+ __udiv_w_sdiv (defined in libgcc or elsewhere). */
+#if !defined (udiv_qrnnd) && defined (sdiv_qrnnd)
+#define udiv_qrnnd(q, r, nh, nl, d) \
+ do { \
+ USItype __r; \
+ (q) = __udiv_w_sdiv (&__r, nh, nl, d); \
+ (r) = __r; \
+ } while (0)
+#endif
+
+/* If udiv_qrnnd was not defined for this processor, use __udiv_qrnnd_c. */
+#if !defined (udiv_qrnnd)
+#define UDIV_NEEDS_NORMALIZATION 1
+#define udiv_qrnnd __udiv_qrnnd_c
+#endif
+
+#if !defined (count_leading_zeros)
+extern const UQItype __clz_tab[];
+#define count_leading_zeros(count, x) \
+ do { \
+ USItype __xr = (x); \
+ USItype __a; \
+ \
+ if (SI_TYPE_SIZE <= 32) \
+ { \
+ __a = __xr < (1<<2*__BITS4) \
+ ? (__xr < (1<<__BITS4) ? 0 : __BITS4) \
+ : (__xr < (1<<3*__BITS4) ? 2*__BITS4 : 3*__BITS4); \
+ } \
+ else \
+ { \
+ for (__a = SI_TYPE_SIZE - 8; __a > 0; __a -= 8) \
+ if (((__xr >> __a) & 0xff) != 0) \
+ break; \
+ } \
+ \
+ (count) = SI_TYPE_SIZE - (__clz_tab[__xr >> __a] + __a); \
+ } while (0)
+#endif
+
+#ifndef UDIV_NEEDS_NORMALIZATION
+#define UDIV_NEEDS_NORMALIZATION 0
+#endif
diff --git a/gnu/usr.bin/cc/include/loop.h b/gnu/usr.bin/cc/include/loop.h
new file mode 100644
index 0000000..bb219c3
--- /dev/null
+++ b/gnu/usr.bin/cc/include/loop.h
@@ -0,0 +1,169 @@
+/* Loop optimization definitions for GNU C-Compiler
+ Copyright (C) 1991 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Get the luid of an insn. Catch the error of trying to reference the LUID
+ of an insn added during loop, since these don't have LUIDs. */
+
+#define INSN_LUID(INSN) \
+ (INSN_UID (INSN) < max_uid_for_loop ? uid_luid[INSN_UID (INSN)] \
+ : (abort (), -1))
+
+/* A "basic induction variable" or biv is a pseudo reg that is set
+ (within this loop) only by incrementing or decrementing it. */
+/* A "general induction variable" or giv is a pseudo reg whose
+ value is a linear function of a biv. */
+
+/* Bivs are recognized by `basic_induction_var';
+ Givs by `general_induct_var'. */
+
+/* An enum for the two different types of givs, those that are used
+ as memory addresses and those that are calculated into registers. */
+enum g_types { DEST_ADDR, DEST_REG };
+
+/* A `struct induction' is created for every instruction that sets
+ an induction variable (either a biv or a giv). */
+
+struct induction
+{
+ rtx insn; /* The insn that sets a biv or giv */
+ rtx new_reg; /* New register, containing strength reduced
+ version of this giv. */
+ rtx src_reg; /* Biv from which this giv is computed.
+ (If this is a biv, then this is the biv.) */
+ enum g_types giv_type; /* Indicate whether DEST_ADDR or DEST_REG */
+ rtx dest_reg; /* Destination register for insn: this is the
+ register which was the biv or giv.
+ For a biv, this equals src_reg.
+ For a DEST_ADDR type giv, this is 0. */
+ rtx *location; /* Place in the insn where this giv occurs.
+ If GIV_TYPE is DEST_REG, this is 0. */
+ enum machine_mode mode; /* The mode of this biv or giv */
+ enum machine_mode mem_mode; /* For DEST_ADDR, mode of the memory object. */
+ rtx mult_val; /* Multiplicative factor for src_reg. */
+ rtx add_val; /* Additive constant for that product. */
+ int benefit; /* Gain from eliminating this insn. */
+ rtx final_value; /* If the giv is used outside the loop, and its
+ final value could be calculated, it is put
+ here, and the giv is made replaceable. Set
+ the giv to this value before the loop. */
+ unsigned replaceable : 1; /* 1 if we can substitute the strength-reduced
+ variable for the original variable.
+ 0 means they must be kept separate and the
+ new one must be copied into the old pseudo
+ reg each time the old one is set. */
+ unsigned not_replaceable : 1; /* Used to prevent duplicating work. This is
+ 1 if we know that the giv definitely can
+ not be made replaceable, in which case we
+ don't bother checking the variable again
+ even if further info is available.
+ Both this and the above can be zero. */
+ unsigned ignore : 1; /* 1 prohibits further processing of giv */
+ unsigned always_computable : 1;/* 1 if this set occurs each iteration */
+ unsigned maybe_multiple : 1; /* Only used for a biv and 1 if this biv
+ update may be done multiple times per
+ iteration. */
+ unsigned cant_derive : 1; /* For giv's, 1 if this giv cannot derive
+ another giv. This occurs in many cases
+ where a giv's lifetime spans an update to
+ a biv. */
+ unsigned combined_with : 1; /* 1 if this giv has been combined with. It
+ then cannot combine with any other giv. */
+ unsigned maybe_dead : 1; /* 1 if this giv might be dead. In that case,
+ we won't use it to eliminate a biv, it
+ would probably lose. */
+ int lifetime; /* Length of life of this giv */
+ int times_used; /* # times this giv is used. */
+ rtx derive_adjustment; /* If nonzero, is an adjustment to be
+ subtracted from add_val when this giv
+ derives another. This occurs when the
+ giv spans a biv update by incrementation. */
+ struct induction *next_iv; /* For givs, links together all givs that are
+ based on the same biv. For bivs, links
+ together all biv entries that refer to the
+ same biv register. */
+ struct induction *same; /* If this giv has been combined with another
+ giv, this points to the base giv. The base
+ giv will have COMBINED_WITH non-zero. */
+ HOST_WIDE_INT const_adjust; /* Used by loop unrolling, when an address giv
+ is split, and a constant is eliminated from
+ the address, the -constant is stored here
+ for later use. */
+};
+
+/* A `struct iv_class' is created for each biv. */
+
+struct iv_class {
+ int regno; /* Pseudo reg which is the biv. */
+ int biv_count; /* Number of insns setting this reg. */
+ struct induction *biv; /* List of all insns that set this reg. */
+ int giv_count; /* Number of DEST_REG givs computed from this
+ biv. The resulting count is only used in
+ check_dbra_loop. */
+ struct induction *giv; /* List of all insns that compute a giv
+ from this reg. */
+ int total_benefit; /* Sum of BENEFITs of all those givs */
+ rtx initial_value; /* Value of reg at loop start */
+ rtx initial_test; /* Test performed on BIV before loop */
+ struct iv_class *next; /* Links all class structures together */
+ rtx init_insn; /* insn which initializes biv, 0 if none. */
+ rtx init_set; /* SET of INIT_INSN, if any. */
+ unsigned incremented : 1; /* 1 if somewhere incremented/decremented */
+ unsigned eliminable : 1; /* 1 if plausible candidate for elimination. */
+ unsigned nonneg : 1; /* 1 if we added a REG_NONNEG note for this. */
+ unsigned reversed : 1; /* 1 if we reversed the loop that this
+ biv controls. */
+};
+
+/* Definitions used by the basic induction variable discovery code. */
+enum iv_mode { UNKNOWN_INDUCT, BASIC_INDUCT, NOT_BASIC_INDUCT,
+ GENERAL_INDUCT };
+
+/* Variables declared in loop.c, but also needed in unroll.c. */
+
+extern int *uid_luid;
+extern int max_uid_for_loop;
+extern int *uid_loop_num;
+extern int *loop_outer_loop;
+extern rtx *loop_number_exit_labels;
+extern unsigned HOST_WIDE_INT loop_n_iterations;
+extern int max_reg_before_loop;
+
+extern FILE *loop_dump_stream;
+
+extern enum iv_mode *reg_iv_type;
+extern struct induction **reg_iv_info;
+extern struct iv_class **reg_biv_class;
+extern struct iv_class *loop_iv_list;
+
+/* Forward declarations for non-static functions declared in loop.c and
+ unroll.c. */
+int invariant_p PROTO((rtx));
+rtx get_condition_for_loop PROTO((rtx));
+void emit_iv_add_mult PROTO((rtx, rtx, rtx, rtx, rtx));
+
+/* Forward declarations for non-static functions declared in stmt.c. */
+void find_loop_tree_blocks PROTO((void));
+void unroll_block_trees PROTO((void));
+
+void unroll_loop PROTO((rtx, int, rtx, rtx, int));
+rtx biv_total_increment PROTO((struct iv_class *, rtx, rtx));
+unsigned HOST_WIDE_INT loop_iterations PROTO((rtx, rtx));
+rtx final_biv_value PROTO((struct iv_class *, rtx, rtx));
+rtx final_giv_value PROTO((struct induction *, rtx, rtx));
+void emit_unrolled_add PROTO((rtx, rtx, rtx));
diff --git a/gnu/usr.bin/cc/include/machmode.def b/gnu/usr.bin/cc/include/machmode.def
new file mode 100644
index 0000000..24d0ba5
--- /dev/null
+++ b/gnu/usr.bin/cc/include/machmode.def
@@ -0,0 +1,118 @@
+/* This file contains the definitions and documentation for the
+ machine modes used in the the GNU compiler.
+ Copyright (C) 1987, 1992, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file defines all the MACHINE MODES used by GNU CC.
+
+ A machine mode specifies a size and format of data
+ at the machine level.
+
+ Each RTL expression has a machine mode.
+
+ At the syntax tree level, each ..._TYPE and each ..._DECL node
+ has a machine mode which describes data of that type or the
+ data of the variable declared. */
+
+/* The first argument is the internal name of the machine mode
+ used in the C source.
+ By convention these are in UPPER_CASE, except for the word "mode".
+
+ The second argument is the name of the machine mode in the
+ external ASCII format used for reading and printing RTL and trees.
+ By convention these names in UPPER_CASE.
+
+ Third argument states the kind of representation:
+ MODE_INT - integer
+ MODE_FLOAT - floating
+ MODE_PARTIAL_INT - PSImode and PDImode
+ MODE_CC - modes used for representing the condition code in a register
+ MODE_COMPLEX_INT, MODE_COMPLEX_FLOAT - complex number
+ MODE_RANDOM - anything else
+
+ Fourth argument is the relative size of the object, in bytes.
+ It is zero when the size is meaningless or not determined.
+ A byte's size is determined by BITS_PER_UNIT in tm.h.
+
+
+ Fifth arg is the relative size of subunits of the object.
+ It is same as the fourth argument except for complexes,
+ since they are really made of two equal size subunits.
+
+ Sixth arg is next wider natural mode of the same class.
+ 0 if there is none. */
+
+/* VOIDmode is used when no mode needs to be specified,
+ as for example on CONST_INT RTL expressions. */
+DEF_MACHMODE (VOIDmode, "VOID", MODE_RANDOM, 0, 0, VOIDmode)
+
+DEF_MACHMODE (QImode, "QI", MODE_INT, 1, 1, HImode) /* int types */
+DEF_MACHMODE (HImode, "HI", MODE_INT, 2, 2, SImode)
+/* Pointers on some machines use this type to distinguish them from ints.
+ Useful if a pointer is 4 bytes but has some bits that are not significant,
+ so it is really not quite as wide as an integer. */
+DEF_MACHMODE (PSImode, "PSI", MODE_PARTIAL_INT, 4, 4, VOIDmode)
+DEF_MACHMODE (SImode, "SI", MODE_INT, 4, 4, DImode)
+DEF_MACHMODE (PDImode, "PDI", MODE_PARTIAL_INT, 8, 8, VOIDmode)
+DEF_MACHMODE (DImode, "DI", MODE_INT, 8, 8, TImode)
+DEF_MACHMODE (TImode, "TI", MODE_INT, 16, 16, OImode)
+DEF_MACHMODE (OImode, "OI", MODE_INT, 32, 32, VOIDmode)
+
+DEF_MACHMODE (QFmode, "QF", MODE_FLOAT, 1, 1, HFmode)
+DEF_MACHMODE (HFmode, "HF", MODE_FLOAT, 2, 2, TQFmode)
+DEF_MACHMODE (TQFmode, "TQF", MODE_FLOAT, 3, 3, SFmode) /* MIL-STD-1750A */
+DEF_MACHMODE (SFmode, "SF", MODE_FLOAT, 4, 4, DFmode)
+DEF_MACHMODE (DFmode, "DF", MODE_FLOAT, 8, 8, XFmode)
+DEF_MACHMODE (XFmode, "XF", MODE_FLOAT, 12, 12, TFmode) /* IEEE extended */
+DEF_MACHMODE (TFmode, "TF", MODE_FLOAT, 16, 16, VOIDmode)
+
+/* Complex modes. */
+DEF_MACHMODE (SCmode, "SC", MODE_COMPLEX_FLOAT, 8, 4, DCmode)
+DEF_MACHMODE (DCmode, "DC", MODE_COMPLEX_FLOAT, 16, 8, XCmode)
+DEF_MACHMODE (XCmode, "XC", MODE_COMPLEX_FLOAT, 24, 12, TCmode)
+DEF_MACHMODE (TCmode, "TC", MODE_COMPLEX_FLOAT, 32, 16, VOIDmode)
+
+DEF_MACHMODE (CQImode, "CQI", MODE_COMPLEX_INT, 2, 1, CHImode)
+DEF_MACHMODE (CHImode, "CHI", MODE_COMPLEX_INT, 4, 2, CSImode)
+DEF_MACHMODE (CSImode, "CSI", MODE_COMPLEX_INT, 8, 4, CDImode)
+DEF_MACHMODE (CDImode, "CDI", MODE_COMPLEX_INT, 16, 8, CTImode)
+DEF_MACHMODE (CTImode, "CTI", MODE_COMPLEX_INT, 32, 16, COImode)
+DEF_MACHMODE (COImode, "COI", MODE_COMPLEX_INT, 64, 32, VOIDmode)
+
+/* BLKmode is used for structures, arrays, etc.
+ that fit no more specific mode. */
+DEF_MACHMODE (BLKmode, "BLK", MODE_RANDOM, 0, 0, VOIDmode)
+
+/* The modes for representing the condition codes come last. CCmode is
+ always defined. Additional modes for the condition code can be specified
+ in the EXTRA_CC_MODES macro. Everything but the names of the modes
+ are copied from CCmode. For these modes, GET_MODE_WIDER_MODE points
+ to the next defined CC mode, if any. */
+
+DEF_MACHMODE (CCmode, "CC", MODE_CC, 4, 4, VOIDmode)
+
+/* The symbol Pmode stands for one of the above machine modes (usually SImode).
+ The tm file specifies which one. It is not a distinct mode. */
+
+/*
+Local variables:
+mode:c
+version-control: t
+End:
+*/
diff --git a/gnu/usr.bin/cc/include/machmode.h b/gnu/usr.bin/cc/include/machmode.h
new file mode 100644
index 0000000..307422b
--- /dev/null
+++ b/gnu/usr.bin/cc/include/machmode.h
@@ -0,0 +1,169 @@
+/* Machine mode definitions for GNU C-Compiler; included by rtl.h and tree.h.
+ Copyright (C) 1991, 1993 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* Add prototype support. */
+#ifndef PROTO
+#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
+#define PROTO(ARGS) ARGS
+#else
+#define PROTO(ARGS) ()
+#endif
+#endif
+
+#ifndef HAVE_MACHINE_MODES
+
+/* Strictly speaking, this isn't the proper place to include these definitions,
+ but this file is included by every GCC file.
+
+ Some systems define these in, e.g., param.h. We undefine these names
+ here to avoid the warnings. We prefer to use our definitions since we
+ know they are correct. */
+
+#undef MIN
+#undef MAX
+
+#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
+#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
+
+/* Find the largest host integer type and set its size and type. */
+
+#ifndef HOST_BITS_PER_WIDE_INT
+
+#if HOST_BITS_PER_LONG > HOST_BITS_PER_INT
+#define HOST_BITS_PER_WIDE_INT HOST_BITS_PER_LONG
+#define HOST_WIDE_INT long
+#else
+#define HOST_BITS_PER_WIDE_INT HOST_BITS_PER_INT
+#define HOST_WIDE_INT int
+#endif
+
+#endif
+
+/* Provide a default way to print an address in hex via printf. */
+
+#ifndef HOST_PTR_PRINTF
+#define HOST_PTR_PRINTF sizeof (int) == sizeof (char *) ? "%x" : "%lx"
+#endif
+
+/* Make an enum class that gives all the machine modes. */
+
+#define DEF_MACHMODE(SYM, NAME, TYPE, SIZE, UNIT, WIDER) SYM,
+
+enum machine_mode {
+#include "machmode.def"
+
+#ifdef EXTRA_CC_MODES
+ EXTRA_CC_MODES,
+#endif
+MAX_MACHINE_MODE };
+
+#undef DEF_MACHMODE
+
+#define HAVE_MACHINE_MODES
+
+#ifndef NUM_MACHINE_MODES
+#define NUM_MACHINE_MODES (int) MAX_MACHINE_MODE
+#endif
+
+/* Get the name of mode MODE as a string. */
+
+extern char *mode_name[];
+#define GET_MODE_NAME(MODE) (mode_name[(int)(MODE)])
+
+enum mode_class { MODE_RANDOM, MODE_INT, MODE_FLOAT, MODE_PARTIAL_INT, MODE_CC,
+ MODE_COMPLEX_INT, MODE_COMPLEX_FLOAT, MAX_MODE_CLASS};
+
+/* Get the general kind of object that mode MODE represents
+ (integer, floating, complex, etc.) */
+
+extern enum mode_class mode_class[];
+#define GET_MODE_CLASS(MODE) (mode_class[(int)(MODE)])
+
+/* Nonzero if MODE is an integral mode. */
+#define INTEGRAL_MODE_P(MODE) \
+ (GET_MODE_CLASS (MODE) == MODE_INT \
+ || GET_MODE_CLASS (MODE) == MODE_PARTIAL_INT \
+ || GET_MODE_CLASS (MODE) == MODE_COMPLEX_INT)
+
+/* Nonzero if MODE is a floating-point mode. */
+#define FLOAT_MODE_P(MODE) \
+ (GET_MODE_CLASS (MODE) == MODE_FLOAT \
+ || GET_MODE_CLASS (MODE) == MODE_COMPLEX_FLOAT)
+
+/* Get the size in bytes of an object of mode MODE. */
+
+extern int mode_size[];
+#define GET_MODE_SIZE(MODE) (mode_size[(int)(MODE)])
+
+/* Get the size in bytes of the basic parts of an object of mode MODE. */
+
+extern int mode_unit_size[];
+#define GET_MODE_UNIT_SIZE(MODE) (mode_unit_size[(int)(MODE)])
+
+/* Get the number of units in the object. */
+
+#define GET_MODE_NUNITS(MODE) \
+ ((GET_MODE_UNIT_SIZE ((MODE)) == 0) ? 0 \
+ : (GET_MODE_SIZE ((MODE)) / GET_MODE_UNIT_SIZE ((MODE))))
+
+/* Get the size in bits of an object of mode MODE. */
+
+#define GET_MODE_BITSIZE(MODE) (BITS_PER_UNIT * mode_size[(int)(MODE)])
+
+/* Get a bitmask containing 1 for all bits in a word
+ that fit within mode MODE. */
+
+#define GET_MODE_MASK(MODE) \
+ ((GET_MODE_BITSIZE (MODE) >= HOST_BITS_PER_WIDE_INT) \
+ ?(HOST_WIDE_INT) ~0 : (((HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (MODE)) - 1))
+
+/* Get the next wider natural mode (eg, QI -> HI -> SI -> DI -> TI). */
+
+extern enum machine_mode mode_wider_mode[];
+#define GET_MODE_WIDER_MODE(MODE) (mode_wider_mode[(int)(MODE)])
+
+/* Return the mode for data of a given size SIZE and mode class CLASS.
+ If LIMIT is nonzero, then don't use modes bigger than MAX_FIXED_MODE_SIZE.
+ The value is BLKmode if no other mode is found. */
+
+extern enum machine_mode mode_for_size PROTO((unsigned int, enum mode_class, int));
+
+/* Find the best mode to use to access a bit field. */
+
+extern enum machine_mode get_best_mode PROTO((int, int, int, enum machine_mode, int));
+
+/* Determine alignment, 1<=result<=BIGGEST_ALIGNMENT. */
+
+#define GET_MODE_ALIGNMENT(MODE) \
+ MIN (BIGGEST_ALIGNMENT, \
+ MAX (1, (GET_MODE_UNIT_SIZE (MODE) * BITS_PER_UNIT)))
+
+/* For each class, get the narrowest mode in that class. */
+
+extern enum machine_mode class_narrowest_mode[];
+#define GET_CLASS_NARROWEST_MODE(CLASS) class_narrowest_mode[(int)(CLASS)]
+
+/* Define the integer modes whose sizes are BITS_PER_UNIT
+ and BITS_PER_WORD. */
+
+extern enum machine_mode byte_mode;
+extern enum machine_mode word_mode;
+
+#endif /* not HAVE_MACHINE_MODES */
diff --git a/gnu/usr.bin/cc/include/modemap.def b/gnu/usr.bin/cc/include/modemap.def
new file mode 100644
index 0000000..3257640
--- /dev/null
+++ b/gnu/usr.bin/cc/include/modemap.def
@@ -0,0 +1,30 @@
+/* Bytecode specific machine mode info for GNU C-compiler.
+ Copyright (C) 1993 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Map mode to signed, unsigned typecodes, bytecode to push const,
+ to load, to store */
+DEF_MODEMAP(QImode, QIcode, QUcode, constQI, loadQI, storeQI)
+DEF_MODEMAP(HImode, HIcode, HUcode, constHI, loadHI, storeHI)
+DEF_MODEMAP(VOIDmode, SIcode, SUcode, constSI, loadSI, storeSI)
+DEF_MODEMAP(SImode, SIcode, SUcode, constSI, loadSI, storeSI)
+DEF_MODEMAP(DImode, DIcode, DUcode, constDI, loadDI, storeDI)
+DEF_MODEMAP(PSImode, Pcode, Pcode, constP, loadP, storeP)
+DEF_MODEMAP(BLKmode, Pcode, Pcode, constP, loadP, neverneverland)
+DEF_MODEMAP(SFmode, SFcode, SFcode, constSF, loadSF, storeSF)
+DEF_MODEMAP(DFmode, DFcode, DFcode, constDF, loadDF, storeDF)
diff --git a/gnu/usr.bin/cc/include/multilib.h b/gnu/usr.bin/cc/include/multilib.h
new file mode 100644
index 0000000..b2a5790
--- /dev/null
+++ b/gnu/usr.bin/cc/include/multilib.h
@@ -0,0 +1,3 @@
+#define MULTILIB_SELECT "\
+. ;\
+"
diff --git a/gnu/usr.bin/cc/include/obstack.h b/gnu/usr.bin/cc/include/obstack.h
new file mode 100644
index 0000000..0176719
--- /dev/null
+++ b/gnu/usr.bin/cc/include/obstack.h
@@ -0,0 +1,513 @@
+/* obstack.h - object stack macros
+ Copyright (C) 1988, 89, 90, 91, 92, 93, 94 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Summary:
+
+All the apparent functions defined here are macros. The idea
+is that you would use these pre-tested macros to solve a
+very specific set of problems, and they would run fast.
+Caution: no side-effects in arguments please!! They may be
+evaluated MANY times!!
+
+These macros operate a stack of objects. Each object starts life
+small, and may grow to maturity. (Consider building a word syllable
+by syllable.) An object can move while it is growing. Once it has
+been "finished" it never changes address again. So the "top of the
+stack" is typically an immature growing object, while the rest of the
+stack is of mature, fixed size and fixed address objects.
+
+These routines grab large chunks of memory, using a function you
+supply, called `obstack_chunk_alloc'. On occasion, they free chunks,
+by calling `obstack_chunk_free'. You must define them and declare
+them before using any obstack macros.
+
+Each independent stack is represented by a `struct obstack'.
+Each of the obstack macros expects a pointer to such a structure
+as the first argument.
+
+One motivation for this package is the problem of growing char strings
+in symbol tables. Unless you are "fascist pig with a read-only mind"
+--Gosper's immortal quote from HAKMEM item 154, out of context--you
+would not like to put any arbitrary upper limit on the length of your
+symbols.
+
+In practice this often means you will build many short symbols and a
+few long symbols. At the time you are reading a symbol you don't know
+how long it is. One traditional method is to read a symbol into a
+buffer, realloc()ating the buffer every time you try to read a symbol
+that is longer than the buffer. This is beaut, but you still will
+want to copy the symbol from the buffer to a more permanent
+symbol-table entry say about half the time.
+
+With obstacks, you can work differently. Use one obstack for all symbol
+names. As you read a symbol, grow the name in the obstack gradually.
+When the name is complete, finalize it. Then, if the symbol exists already,
+free the newly read name.
+
+The way we do this is to take a large chunk, allocating memory from
+low addresses. When you want to build a symbol in the chunk you just
+add chars above the current "high water mark" in the chunk. When you
+have finished adding chars, because you got to the end of the symbol,
+you know how long the chars are, and you can create a new object.
+Mostly the chars will not burst over the highest address of the chunk,
+because you would typically expect a chunk to be (say) 100 times as
+long as an average object.
+
+In case that isn't clear, when we have enough chars to make up
+the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed)
+so we just point to it where it lies. No moving of chars is
+needed and this is the second win: potentially long strings need
+never be explicitly shuffled. Once an object is formed, it does not
+change its address during its lifetime.
+
+When the chars burst over a chunk boundary, we allocate a larger
+chunk, and then copy the partly formed object from the end of the old
+chunk to the beginning of the new larger chunk. We then carry on
+accreting characters to the end of the object as we normally would.
+
+A special macro is provided to add a single char at a time to a
+growing object. This allows the use of register variables, which
+break the ordinary 'growth' macro.
+
+Summary:
+ We allocate large chunks.
+ We carve out one object at a time from the current chunk.
+ Once carved, an object never moves.
+ We are free to append data of any size to the currently
+ growing object.
+ Exactly one object is growing in an obstack at any one time.
+ You can run one obstack per control block.
+ You may have as many control blocks as you dare.
+ Because of the way we do it, you can `unwind' an obstack
+ back to a previous state. (You may remove objects much
+ as you would with a stack.)
+*/
+
+
+/* Don't do the contents of this file more than once. */
+
+#ifndef __OBSTACK_H__
+#define __OBSTACK_H__
+
+/* We use subtraction of (char *)0 instead of casting to int
+ because on word-addressable machines a simple cast to int
+ may ignore the byte-within-word field of the pointer. */
+
+#ifndef __PTR_TO_INT
+#define __PTR_TO_INT(P) ((P) - (char *)0)
+#endif
+
+#ifndef __INT_TO_PTR
+#define __INT_TO_PTR(P) ((P) + (char *)0)
+#endif
+
+/* We need the type of the resulting object. In ANSI C it is ptrdiff_t
+ but in traditional C it is usually long. If we are in ANSI C and
+ don't already have ptrdiff_t get it. */
+
+#if defined (__STDC__) && ! defined (offsetof)
+#if defined (__GNUC__) && defined (IN_GCC)
+/* On Next machine, the system's stddef.h screws up if included
+ after we have defined just ptrdiff_t, so include all of stddef.h.
+ Otherwise, define just ptrdiff_t, which is all we need. */
+#ifndef __NeXT__
+#define __need_ptrdiff_t
+#endif
+#endif
+
+#include <stddef.h>
+#endif
+
+#ifdef __STDC__
+#define PTR_INT_TYPE ptrdiff_t
+#else
+#define PTR_INT_TYPE long
+#endif
+
+struct _obstack_chunk /* Lives at front of each chunk. */
+{
+ char *limit; /* 1 past end of this chunk */
+ struct _obstack_chunk *prev; /* address of prior chunk or NULL */
+ char contents[4]; /* objects begin here */
+};
+
+struct obstack /* control current object in current chunk */
+{
+ long chunk_size; /* preferred size to allocate chunks in */
+ struct _obstack_chunk* chunk; /* address of current struct obstack_chunk */
+ char *object_base; /* address of object we are building */
+ char *next_free; /* where to add next char to current object */
+ char *chunk_limit; /* address of char after current chunk */
+ PTR_INT_TYPE temp; /* Temporary for some macros. */
+ int alignment_mask; /* Mask of alignment for each object. */
+ struct _obstack_chunk *(*chunkfun) (); /* User's fcn to allocate a chunk. */
+ void (*freefun) (); /* User's function to free a chunk. */
+ char *extra_arg; /* first arg for chunk alloc/dealloc funcs */
+ unsigned use_extra_arg:1; /* chunk alloc/dealloc funcs take extra arg */
+ unsigned maybe_empty_object:1;/* There is a possibility that the current
+ chunk contains a zero-length object. This
+ prevents freeing the chunk if we allocate
+ a bigger chunk to replace it. */
+ unsigned alloc_failed:1; /* chunk alloc func returned 0 */
+};
+
+/* Declare the external functions we use; they are in obstack.c. */
+
+#ifdef __STDC__
+extern void _obstack_newchunk (struct obstack *, int);
+extern void _obstack_free (struct obstack *, void *);
+extern int _obstack_begin (struct obstack *, int, int,
+ void *(*) (), void (*) ());
+extern int _obstack_begin_1 (struct obstack *, int, int,
+ void *(*) (), void (*) (), void *);
+#else
+extern void _obstack_newchunk ();
+extern void _obstack_free ();
+extern int _obstack_begin ();
+extern int _obstack_begin_1 ();
+#endif
+
+#ifdef __STDC__
+
+/* Do the function-declarations after the structs
+ but before defining the macros. */
+
+void obstack_init (struct obstack *obstack);
+
+void * obstack_alloc (struct obstack *obstack, int size);
+
+void * obstack_copy (struct obstack *obstack, void *address, int size);
+void * obstack_copy0 (struct obstack *obstack, void *address, int size);
+
+void obstack_free (struct obstack *obstack, void *block);
+
+void obstack_blank (struct obstack *obstack, int size);
+
+void obstack_grow (struct obstack *obstack, void *data, int size);
+void obstack_grow0 (struct obstack *obstack, void *data, int size);
+
+void obstack_1grow (struct obstack *obstack, int data_char);
+void obstack_ptr_grow (struct obstack *obstack, void *data);
+void obstack_int_grow (struct obstack *obstack, int data);
+
+void * obstack_finish (struct obstack *obstack);
+
+int obstack_object_size (struct obstack *obstack);
+
+int obstack_room (struct obstack *obstack);
+void obstack_1grow_fast (struct obstack *obstack, int data_char);
+void obstack_ptr_grow_fast (struct obstack *obstack, void *data);
+void obstack_int_grow_fast (struct obstack *obstack, int data);
+void obstack_blank_fast (struct obstack *obstack, int size);
+
+void * obstack_base (struct obstack *obstack);
+void * obstack_next_free (struct obstack *obstack);
+int obstack_alignment_mask (struct obstack *obstack);
+int obstack_chunk_size (struct obstack *obstack);
+
+#endif /* __STDC__ */
+
+/* Non-ANSI C cannot really support alternative functions for these macros,
+ so we do not declare them. */
+
+/* Pointer to beginning of object being allocated or to be allocated next.
+ Note that this might not be the final address of the object
+ because a new chunk might be needed to hold the final size. */
+
+#define obstack_base(h) ((h)->alloc_failed ? 0 : (h)->object_base)
+
+/* Size for allocating ordinary chunks. */
+
+#define obstack_chunk_size(h) ((h)->chunk_size)
+
+/* Pointer to next byte not yet allocated in current chunk. */
+
+#define obstack_next_free(h) ((h)->alloc_failed ? 0 : (h)->next_free)
+
+/* Mask specifying low bits that should be clear in address of an object. */
+
+#define obstack_alignment_mask(h) ((h)->alignment_mask)
+
+#define obstack_init(h) \
+ _obstack_begin ((h), 0, 0, \
+ (void *(*) ()) obstack_chunk_alloc, (void (*) ()) obstack_chunk_free)
+
+#define obstack_begin(h, size) \
+ _obstack_begin ((h), (size), 0, \
+ (void *(*) ()) obstack_chunk_alloc, (void (*) ()) obstack_chunk_free)
+
+#define obstack_specify_allocation(h, size, alignment, chunkfun, freefun) \
+ _obstack_begin ((h), (size), (alignment), \
+ (void *(*) ()) (chunkfun), (void (*) ()) (freefun))
+
+#define obstack_specify_allocation_with_arg(h, size, alignment, chunkfun, freefun, arg) \
+ _obstack_begin_1 ((h), (size), (alignment), \
+ (void *(*) ()) (chunkfun), (void (*) ()) (freefun), (arg))
+
+#define obstack_chunkfun(h, newchunkfun) \
+ ((h) -> chunkfun = (struct _obstack_chunk *(*)()) (newchunkfun))
+
+#define obstack_freefun(h, newfreefun) \
+ ((h) -> freefun = (void (*)()) (newfreefun))
+
+#define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = achar)
+
+#define obstack_blank_fast(h,n) ((h)->next_free += (n))
+
+#if defined (__GNUC__) && defined (__STDC__)
+#if __GNUC__ < 2
+#define __extension__
+#endif
+
+/* For GNU C, if not -traditional,
+ we can define these macros to compute all args only once
+ without using a global variable.
+ Also, we can avoid using the `temp' slot, to make faster code. */
+
+#define obstack_object_size(OBSTACK) \
+ __extension__ \
+ ({ struct obstack *__o = (OBSTACK); \
+ __o->alloc_failed ? 0 : \
+ (unsigned) (__o->next_free - __o->object_base); })
+
+#define obstack_room(OBSTACK) \
+ __extension__ \
+ ({ struct obstack *__o = (OBSTACK); \
+ (unsigned) (__o->chunk_limit - __o->next_free); })
+
+#define obstack_grow(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ int __len = (length); \
+ if (__o->next_free + __len > __o->chunk_limit) \
+ _obstack_newchunk (__o, __len); \
+ if (!__o->alloc_failed) \
+ { \
+ bcopy ((char *) (where), __o->next_free, __len); \
+ __o->next_free += __len; \
+ } \
+ (void) 0; })
+
+#define obstack_grow0(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ int __len = (length); \
+ if (__o->next_free + __len + 1 > __o->chunk_limit) \
+ _obstack_newchunk (__o, __len + 1); \
+ if (!__o->alloc_failed) \
+ { \
+ bcopy ((char *) (where), __o->next_free, __len); \
+ __o->next_free += __len; \
+ *(__o->next_free)++ = 0; \
+ } \
+ (void) 0; })
+
+#define obstack_1grow(OBSTACK,datum) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ if (__o->next_free + 1 > __o->chunk_limit) \
+ _obstack_newchunk (__o, 1); \
+ if (!__o->alloc_failed) \
+ *(__o->next_free)++ = (datum); \
+ (void) 0; })
+
+/* These assume that the obstack alignment is good enough for pointers or ints,
+ and that the data added so far to the current object
+ shares that much alignment. */
+
+#define obstack_ptr_grow(OBSTACK,datum) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ if (__o->next_free + sizeof (void *) > __o->chunk_limit) \
+ _obstack_newchunk (__o, sizeof (void *)); \
+ if (!__o->alloc_failed) \
+ *((void **)__o->next_free)++ = ((void *)datum); \
+ (void) 0; })
+
+#define obstack_int_grow(OBSTACK,datum) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ if (__o->next_free + sizeof (int) > __o->chunk_limit) \
+ _obstack_newchunk (__o, sizeof (int)); \
+ if (!__o->alloc_failed) \
+ *((int *)__o->next_free)++ = ((int)datum); \
+ (void) 0; })
+
+#define obstack_ptr_grow_fast(h,aptr) (*((void **)(h)->next_free)++ = (void *)aptr)
+#define obstack_int_grow_fast(h,aint) (*((int *)(h)->next_free)++ = (int)aint)
+
+#define obstack_blank(OBSTACK,length) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ int __len = (length); \
+ if (__o->chunk_limit - __o->next_free < __len) \
+ _obstack_newchunk (__o, __len); \
+ if (!__o->alloc_failed) \
+ __o->next_free += __len; \
+ (void) 0; })
+
+#define obstack_alloc(OBSTACK,length) \
+__extension__ \
+({ struct obstack *__h = (OBSTACK); \
+ obstack_blank (__h, (length)); \
+ obstack_finish (__h); })
+
+#define obstack_copy(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__h = (OBSTACK); \
+ obstack_grow (__h, (where), (length)); \
+ obstack_finish (__h); })
+
+#define obstack_copy0(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__h = (OBSTACK); \
+ obstack_grow0 (__h, (where), (length)); \
+ obstack_finish (__h); })
+
+/* The local variable is named __o1 to avoid a name conflict
+ when obstack_blank is called. */
+#define obstack_finish(OBSTACK) \
+__extension__ \
+({ struct obstack *__o1 = (OBSTACK); \
+ void *value; \
+ if (__o1->alloc_failed) \
+ value = 0; \
+ else \
+ { \
+ value = (void *) __o1->object_base; \
+ if (__o1->next_free == value) \
+ __o1->maybe_empty_object = 1; \
+ __o1->next_free \
+ = __INT_TO_PTR ((__PTR_TO_INT (__o1->next_free)+__o1->alignment_mask)\
+ & ~ (__o1->alignment_mask)); \
+ if (__o1->next_free - (char *)__o1->chunk \
+ > __o1->chunk_limit - (char *)__o1->chunk) \
+ __o1->next_free = __o1->chunk_limit; \
+ __o1->object_base = __o1->next_free; \
+ } \
+ value; })
+
+#define obstack_free(OBSTACK, OBJ) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ void *__obj = (OBJ); \
+ if (__obj > (void *)__o->chunk && __obj < (void *)__o->chunk_limit) \
+ __o->next_free = __o->object_base = __obj; \
+ else (obstack_free) (__o, __obj); })
+
+#else /* not __GNUC__ or not __STDC__ */
+
+#define obstack_object_size(h) \
+ (unsigned) ((h)->alloc_failed ? 0 : (h)->next_free - (h)->object_base)
+
+#define obstack_room(h) \
+ (unsigned) ((h)->chunk_limit - (h)->next_free)
+
+/* Note that the call to _obstack_newchunk is enclosed in (..., 0)
+ so that we can avoid having void expressions
+ in the arms of the conditional expression.
+ Casting the third operand to void was tried before,
+ but some compilers won't accept it. */
+
+#define obstack_grow(h,where,length) \
+( (h)->temp = (length), \
+ (((h)->next_free + (h)->temp > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), (h)->temp), 0) : 0), \
+ ((h)->alloc_failed ? 0 : \
+ (bcopy ((char *) (where), (h)->next_free, (h)->temp), \
+ (h)->next_free += (h)->temp)))
+
+#define obstack_grow0(h,where,length) \
+( (h)->temp = (length), \
+ (((h)->next_free + (h)->temp + 1 > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), (h)->temp + 1), 0) : 0), \
+ ((h)->alloc_failed ? 0 : \
+ (bcopy ((char *) (where), (h)->next_free, (h)->temp), \
+ (h)->next_free += (h)->temp, \
+ *((h)->next_free)++ = 0)))
+
+#define obstack_1grow(h,datum) \
+( (((h)->next_free + 1 > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), 1), 0) : 0), \
+ ((h)->alloc_failed ? 0 : \
+ (*((h)->next_free)++ = (datum))))
+
+#define obstack_ptr_grow(h,datum) \
+( (((h)->next_free + sizeof (char *) > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), sizeof (char *)), 0) : 0), \
+ ((h)->alloc_failed ? 0 : \
+ (*((char **)(((h)->next_free+=sizeof(char *))-sizeof(char *))) = ((char *)datum))))
+
+#define obstack_int_grow(h,datum) \
+( (((h)->next_free + sizeof (int) > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), sizeof (int)), 0) : 0), \
+ ((h)->alloc_failed ? 0 : \
+ (*((int *)(((h)->next_free+=sizeof(int))-sizeof(int))) = ((int)datum))))
+
+#define obstack_ptr_grow_fast(h,aptr) (*((char **)(h)->next_free)++ = (char *)aptr)
+#define obstack_int_grow_fast(h,aint) (*((int *)(h)->next_free)++ = (int)aint)
+
+#define obstack_blank(h,length) \
+( (h)->temp = (length), \
+ (((h)->chunk_limit - (h)->next_free < (h)->temp) \
+ ? (_obstack_newchunk ((h), (h)->temp), 0) : 0), \
+ ((h)->alloc_failed ? 0 : \
+ ((h)->next_free += (h)->temp)))
+
+#define obstack_alloc(h,length) \
+ (obstack_blank ((h), (length)), obstack_finish ((h)))
+
+#define obstack_copy(h,where,length) \
+ (obstack_grow ((h), (where), (length)), obstack_finish ((h)))
+
+#define obstack_copy0(h,where,length) \
+ (obstack_grow0 ((h), (where), (length)), obstack_finish ((h)))
+
+#define obstack_finish(h) \
+( (h)->alloc_failed ? 0 : \
+ (((h)->next_free == (h)->object_base \
+ ? (((h)->maybe_empty_object = 1), 0) \
+ : 0), \
+ (h)->temp = __PTR_TO_INT ((h)->object_base), \
+ (h)->next_free \
+ = __INT_TO_PTR ((__PTR_TO_INT ((h)->next_free)+(h)->alignment_mask) \
+ & ~ ((h)->alignment_mask)), \
+ (((h)->next_free - (char *)(h)->chunk \
+ > (h)->chunk_limit - (char *)(h)->chunk) \
+ ? ((h)->next_free = (h)->chunk_limit) : 0), \
+ (h)->object_base = (h)->next_free, \
+ __INT_TO_PTR ((h)->temp)))
+
+#ifdef __STDC__
+#define obstack_free(h,obj) \
+( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \
+ (((h)->temp > 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\
+ ? (int) ((h)->next_free = (h)->object_base \
+ = (h)->temp + (char *) (h)->chunk) \
+ : (((obstack_free) ((h), (h)->temp + (char *) (h)->chunk), 0), 0)))
+#else
+#define obstack_free(h,obj) \
+( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \
+ (((h)->temp > 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\
+ ? (int) ((h)->next_free = (h)->object_base \
+ = (h)->temp + (char *) (h)->chunk) \
+ : (_obstack_free ((h), (h)->temp + (char *) (h)->chunk), 0)))
+#endif
+
+#endif /* not __GNUC__ or not __STDC__ */
+
+#endif /* not __OBSTACK_H__ */
diff --git a/gnu/usr.bin/cc/include/output.h b/gnu/usr.bin/cc/include/output.h
new file mode 100644
index 0000000..ebd0a2f
--- /dev/null
+++ b/gnu/usr.bin/cc/include/output.h
@@ -0,0 +1,241 @@
+/* Declarations for insn-output.c. These functions are defined in recog.c,
+ final.c, and varasm.c.
+ Copyright (C) 1987, 1991, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Initialize data in final at the beginning of a compilation. */
+extern void init_final PROTO((char *));
+
+/* Called at end of source file,
+ to output the block-profiling table for this entire compilation. */
+extern void end_final PROTO((char *));
+
+/* Enable APP processing of subsequent output.
+ Used before the output from an `asm' statement. */
+extern void app_enable PROTO((void));
+
+/* Disable APP processing of subsequent output.
+ Called from varasm.c before most kinds of output. */
+extern void app_disable PROTO((void));
+
+/* Return the number of slots filled in the current
+ delayed branch sequence (we don't count the insn needing the
+ delay slot). Zero if not in a delayed branch sequence. */
+extern int dbr_sequence_length PROTO((void));
+
+/* Indicate that branch shortening hasn't yet been done. */
+extern void init_insn_lengths PROTO((void));
+
+/* Obtain the current length of an insn. If branch shortening has been done,
+ get its actual length. Otherwise, get its maximum length. */
+extern int get_attr_length PROTO((rtx));
+
+/* Make a pass over all insns and compute their actual lengths by shortening
+ any branches of variable length if possible. */
+extern void shorten_branches PROTO((rtx));
+
+/* Output assembler code for the start of a function,
+ and initialize some of the variables in this file
+ for the new function. The label for the function and associated
+ assembler pseudo-ops have already been output in
+ `assemble_start_function'. */
+extern void final_start_function STDIO_PROTO((rtx, FILE *, int));
+
+/* Output assembler code for the end of a function.
+ For clarity, args are same as those of `final_start_function'
+ even though not all of them are needed. */
+extern void final_end_function STDIO_PROTO((rtx, FILE *, int));
+
+/* Output assembler code for some insns: all or part of a function. */
+extern void final STDIO_PROTO((rtx, FILE *, int, int));
+
+/* The final scan for one insn, INSN. Args are same as in `final', except
+ that INSN is the insn being scanned. Value returned is the next insn to
+ be scanned. */
+extern rtx final_scan_insn STDIO_PROTO((rtx, FILE *, int, int, int));
+
+/* Replace a SUBREG with a REG or a MEM, based on the thing it is a
+ subreg of. */
+extern rtx alter_subreg PROTO((rtx));
+
+/* Report inconsistency between the assembler template and the operands.
+ In an `asm', it's the user's fault; otherwise, the compiler's fault. */
+extern void output_operand_lossage PROTO((char *));
+
+/* Output a string of assembler code, substituting insn operands.
+ Defined in final.c. */
+extern void output_asm_insn PROTO((char *, rtx *));
+
+/* Output a LABEL_REF, or a bare CODE_LABEL, as an assembler symbol. */
+extern void output_asm_label PROTO((rtx));
+
+/* Print a memory reference operand for address X
+ using machine-dependent assembler syntax. */
+extern void output_address PROTO((rtx));
+
+/* Print an integer constant expression in assembler syntax.
+ Addition and subtraction are the only arithmetic
+ that may appear in these expressions. */
+extern void output_addr_const STDIO_PROTO((FILE *, rtx));
+
+/* Output a string of assembler code, substituting numbers, strings
+ and fixed syntactic prefixes. */
+extern void asm_fprintf STDIO_PROTO(PVPROTO((FILE *file,
+ char *p, ...)));
+
+/* Split up a CONST_DOUBLE or integer constant rtx into two rtx's for single
+ words. */
+extern void split_double PROTO((rtx, rtx *, rtx *));
+
+/* Return nonzero if this function has no function calls. */
+extern int leaf_function_p PROTO((void));
+
+/* Return 1 if this function uses only the registers that can be
+ safely renumbered. */
+extern int only_leaf_regs_used PROTO((void));
+
+/* Scan IN_RTX and its subexpressions, and renumber all regs into those
+ available in leaf functions. */
+extern void leaf_renumber_regs_insn PROTO((rtx));
+
+/* Output a name (as found inside a symbol_ref) in assembler syntax. */
+extern void assemble_name STDIO_PROTO((FILE *, char *));
+
+/* When outputting assembler code, indicates which alternative
+ of the constraints was actually satisfied. */
+extern int which_alternative;
+
+/* When outputting delayed branch sequences, this rtx holds the
+ sequence being output. It is null when no delayed branch
+ sequence is being output, so it can be used as a test in the
+ insn output code.
+
+ This variable is defined in final.c. */
+extern rtx final_sequence;
+
+/* Number of bytes of args popped by function being compiled on its return.
+ Zero if no bytes are to be popped.
+ May affect compilation of return insn or of function epilogue. */
+
+extern int current_function_pops_args;
+
+/* Nonzero if function being compiled needs to be given an address
+ where the value should be stored. */
+
+extern int current_function_returns_struct;
+
+/* Nonzero if function being compiled needs to
+ return the address of where it has put a structure value. */
+
+extern int current_function_returns_pcc_struct;
+
+/* Nonzero if function being compiled needs to be passed a static chain. */
+
+extern int current_function_needs_context;
+
+/* Nonzero if function being compiled can call setjmp. */
+
+extern int current_function_calls_setjmp;
+
+/* Nonzero if function being compiled can call longjmp. */
+
+extern int current_function_calls_longjmp;
+
+/* Nonzero if function being compiled can call alloca,
+ either as a subroutine or builtin. */
+
+extern int current_function_calls_alloca;
+
+/* Nonzero if function being compiled receives nonlocal gotos
+ from nested functions. */
+
+extern int current_function_has_nonlocal_label;
+
+/* Nonzero if function being compiled contains nested functions. */
+
+extern int current_function_contains_functions;
+
+/* Nonzero if the current function returns a pointer type */
+
+extern int current_function_returns_pointer;
+
+/* If function's args have a fixed size, this is that size, in bytes.
+ Otherwise, it is -1.
+ May affect compilation of return insn or of function epilogue. */
+
+extern int current_function_args_size;
+
+/* # bytes the prologue should push and pretend that the caller pushed them.
+ The prologue must do this, but only if parms can be passed in registers. */
+
+extern int current_function_pretend_args_size;
+
+/* # of bytes of outgoing arguments required to be pushed by the prologue.
+ If this is non-zero, it means that ACCUMULATE_OUTGOING_ARGS was defined
+ and no stack adjusts will be done on function calls. */
+
+extern int current_function_outgoing_args_size;
+
+/* Nonzero if current function uses varargs.h or equivalent.
+ Zero for functions that use stdarg.h. */
+
+extern int current_function_varargs;
+
+/* Quantities of various kinds of registers
+ used for the current function's args. */
+
+extern CUMULATIVE_ARGS current_function_args_info;
+
+/* Name of function now being compiled. */
+
+extern char *current_function_name;
+
+/* If non-zero, an RTL expression for that location at which the current
+ function returns its result. Usually equal to
+ DECL_RTL (DECL_RESULT (current_function_decl)). */
+
+extern rtx current_function_return_rtx;
+
+/* If some insns can be deferred to the delay slots of the epilogue, the
+ delay list for them is recorded here. */
+
+extern rtx current_function_epilogue_delay_list;
+
+/* Nonzero means generate position-independent code.
+ This is not fully implemented yet. */
+
+extern int flag_pic;
+
+/* This is nonzero if the current function uses pic_offset_table_rtx. */
+extern int current_function_uses_pic_offset_table;
+
+/* This is nonzero if the current function uses the constant pool. */
+extern int current_function_uses_const_pool;
+
+/* The line number of the beginning of the current function.
+ sdbout.c needs this so that it can output relative linenumbers. */
+
+#ifdef SDB_DEBUGGING_INFO /* Avoid undef sym in certain broken linkers. */
+extern int sdb_begin_function_line;
+#endif
+
+/* File in which assembler code is being written. */
+
+#ifdef BUFSIZ
+extern FILE *asm_out_file;
+#endif
diff --git a/gnu/usr.bin/cc/include/pcp.h b/gnu/usr.bin/cc/include/pcp.h
new file mode 100644
index 0000000..0b86a87
--- /dev/null
+++ b/gnu/usr.bin/cc/include/pcp.h
@@ -0,0 +1,100 @@
+/* pcp.h -- Describes the format of a precompiled file
+ Copyright (C) 1990 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+
+/* Structure allocated for every string in a precompiled file */
+typedef struct stringdef STRINGDEF;
+struct stringdef
+{
+ U_CHAR *contents; /* String to include */
+ int len; /* Its length */
+ int writeflag; /* Whether we write this */
+ int lineno; /* Linenumber of source file */
+ U_CHAR *filename; /* Name of source file */
+ STRINGDEF *chain; /* Global list of strings in natural order */
+ int output_mark; /* Where in the output this goes */
+};
+
+typedef struct keydef KEYDEF;
+struct keydef
+{
+ STRINGDEF *str;
+ KEYDEF *chain;
+};
+
+/* Format: */
+/* A precompiled file starts with a series of #define and #undef
+ statements:
+ #define MAC DEF --- Indicates MAC must be defined with defn DEF
+ #define MAC --- Indicates MAC must be defined with any defn
+ #undef MAC --- Indicates MAC cannot be defined
+
+These preconditions must be true for a precompiled file to be used.
+The preconditions section is null terminated. */
+
+/* Then, there is a four byte number (in network byte order) which */
+ /* indicates the number of strings the file contains. */
+
+/* Each string contains a STRINGDEF structure. The only component of */
+ /* the STRINGDEF structure which is used is the lineno field, which */
+ /* should hold the line number in the original header file. */
+ /* Then follows the string, followed by a null. Then comes a four */
+ /* byte number (again, in network byte order) indicating the number */
+ /* of keys for this string. Each key is a KEYDEF structure, with */
+ /* irrelevant contents, followed by the null-terminated string. */
+
+/* If the number of keys is 0, then there are no keys for the string, */
+ /* in other words, the string will never be included. If the number */
+ /* of keys is -1, this is a special flag indicating there are no keys */
+ /* in the file, and the string is mandatory (that is, it must be */
+ /* included regardless in the included output). */
+
+/* A file, then, looks like this:
+
+ Precondition 1
+ Precondition 2
+ .
+ .
+ .
+ <NUL>
+ Number of strings
+ STRINGDEF
+ String . . . <NUL>
+ Number of keys
+ KEYDEF
+ Key . . . <NUL>
+ KEYDEF
+ Key . . . <NUL>
+ .
+ .
+ .
+ STRINGDEF
+ String . . . <NUL>
+ Number of keys
+ KEYDEF
+ Key . . . <NUL>
+ .
+ .
+ .
+ .
+ .
+ .
+
+*/
diff --git a/gnu/usr.bin/cc/include/real.h b/gnu/usr.bin/cc/include/real.h
new file mode 100644
index 0000000..34d6d67
--- /dev/null
+++ b/gnu/usr.bin/cc/include/real.h
@@ -0,0 +1,437 @@
+/* Front-end tree definitions for GNU compiler.
+ Copyright (C) 1989, 1991, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef REAL_H_INCLUDED
+#define REAL_H_INCLUDED
+
+/* Define codes for all the float formats that we know of. */
+#define UNKNOWN_FLOAT_FORMAT 0
+#define IEEE_FLOAT_FORMAT 1
+#define VAX_FLOAT_FORMAT 2
+#define IBM_FLOAT_FORMAT 3
+
+/* Default to IEEE float if not specified. Nearly all machines use it. */
+
+#ifndef TARGET_FLOAT_FORMAT
+#define TARGET_FLOAT_FORMAT IEEE_FLOAT_FORMAT
+#endif
+
+#ifndef HOST_FLOAT_FORMAT
+#define HOST_FLOAT_FORMAT IEEE_FLOAT_FORMAT
+#endif
+
+#if TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
+#define REAL_INFINITY
+#endif
+
+/* If FLOAT_WORDS_BIG_ENDIAN and HOST_FLOAT_WORDS_BIG_ENDIAN are not defined
+ in the header files, then this implies the word-endianness is the same as
+ for integers. */
+
+/* This is defined 0 or 1, like WORDS_BIG_ENDIAN. */
+#ifndef FLOAT_WORDS_BIG_ENDIAN
+#define FLOAT_WORDS_BIG_ENDIAN WORDS_BIG_ENDIAN
+#endif
+
+/* This is defined 0 or 1, unlike HOST_WORDS_BIG_ENDIAN. */
+#ifndef HOST_FLOAT_WORDS_BIG_ENDIAN
+#ifdef HOST_WORDS_BIG_ENDIAN
+#define HOST_FLOAT_WORDS_BIG_ENDIAN 1
+#else
+#define HOST_FLOAT_WORDS_BIG_ENDIAN 0
+#endif
+#endif
+
+/* Defining REAL_ARITHMETIC invokes a floating point emulator
+ that can produce a target machine format differing by more
+ than just endian-ness from the host's format. The emulator
+ is also used to support extended real XFmode. */
+#ifndef LONG_DOUBLE_TYPE_SIZE
+#define LONG_DOUBLE_TYPE_SIZE 64
+#endif
+#if (LONG_DOUBLE_TYPE_SIZE == 96) || (LONG_DOUBLE_TYPE_SIZE == 128)
+#ifndef REAL_ARITHMETIC
+#define REAL_ARITHMETIC
+#endif
+#endif
+#ifdef REAL_ARITHMETIC
+/* **** Start of software floating point emulator interface macros **** */
+
+/* Support 80-bit extended real XFmode if LONG_DOUBLE_TYPE_SIZE
+ has been defined to be 96 in the tm.h machine file. */
+#if (LONG_DOUBLE_TYPE_SIZE == 96)
+#define REAL_IS_NOT_DOUBLE
+#define REAL_ARITHMETIC
+typedef struct {
+ HOST_WIDE_INT r[(11 + sizeof (HOST_WIDE_INT))/(sizeof (HOST_WIDE_INT))];
+} realvaluetype;
+#define REAL_VALUE_TYPE realvaluetype
+
+#else /* no XFmode support */
+
+#if (LONG_DOUBLE_TYPE_SIZE == 128)
+
+#define REAL_IS_NOT_DOUBLE
+#define REAL_ARITHMETIC
+typedef struct {
+ HOST_WIDE_INT r[(19 + sizeof (HOST_WIDE_INT))/(sizeof (HOST_WIDE_INT))];
+} realvaluetype;
+#define REAL_VALUE_TYPE realvaluetype
+
+#else /* not TFmode */
+
+#if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
+/* If no XFmode support, then a REAL_VALUE_TYPE is 64 bits wide
+ but it is not necessarily a host machine double. */
+#define REAL_IS_NOT_DOUBLE
+typedef struct {
+ HOST_WIDE_INT r[(7 + sizeof (HOST_WIDE_INT))/(sizeof (HOST_WIDE_INT))];
+} realvaluetype;
+#define REAL_VALUE_TYPE realvaluetype
+#else
+/* If host and target formats are compatible, then a REAL_VALUE_TYPE
+ is actually a host machine double. */
+#define REAL_VALUE_TYPE double
+#endif
+
+#endif /* no TFmode support */
+#endif /* no XFmode support */
+
+extern int significand_size PROTO((enum machine_mode));
+
+/* If emulation has been enabled by defining REAL_ARITHMETIC or by
+ setting LONG_DOUBLE_TYPE_SIZE to 96 or 128, then define macros so that
+ they invoke emulator functions. This will succeed only if the machine
+ files have been updated to use these macros in place of any
+ references to host machine `double' or `float' types. */
+#ifdef REAL_ARITHMETIC
+#undef REAL_ARITHMETIC
+#define REAL_ARITHMETIC(value, code, d1, d2) \
+ earith (&(value), (code), &(d1), &(d2))
+
+/* Declare functions in real.c. */
+extern void earith PROTO((REAL_VALUE_TYPE *, int,
+ REAL_VALUE_TYPE *, REAL_VALUE_TYPE *));
+extern REAL_VALUE_TYPE etrunci PROTO((REAL_VALUE_TYPE));
+extern REAL_VALUE_TYPE etruncui PROTO((REAL_VALUE_TYPE));
+extern REAL_VALUE_TYPE ereal_atof PROTO((char *, enum machine_mode));
+extern REAL_VALUE_TYPE ereal_negate PROTO((REAL_VALUE_TYPE));
+extern HOST_WIDE_INT efixi PROTO((REAL_VALUE_TYPE));
+extern unsigned HOST_WIDE_INT efixui PROTO((REAL_VALUE_TYPE));
+extern void ereal_from_int PROTO((REAL_VALUE_TYPE *,
+ HOST_WIDE_INT, HOST_WIDE_INT));
+extern void ereal_from_uint PROTO((REAL_VALUE_TYPE *,
+ unsigned HOST_WIDE_INT,
+ unsigned HOST_WIDE_INT));
+extern void ereal_to_int PROTO((HOST_WIDE_INT *, HOST_WIDE_INT *,
+ REAL_VALUE_TYPE));
+extern REAL_VALUE_TYPE ereal_ldexp PROTO((REAL_VALUE_TYPE, int));
+
+extern void etartdouble PROTO((REAL_VALUE_TYPE, long *));
+extern void etarldouble PROTO((REAL_VALUE_TYPE, long *));
+extern void etardouble PROTO((REAL_VALUE_TYPE, long *));
+extern long etarsingle PROTO((REAL_VALUE_TYPE));
+extern void ereal_to_decimal PROTO((REAL_VALUE_TYPE, char *));
+extern int ereal_cmp PROTO((REAL_VALUE_TYPE, REAL_VALUE_TYPE));
+extern int ereal_isneg PROTO((REAL_VALUE_TYPE));
+extern REAL_VALUE_TYPE ereal_from_float PROTO((HOST_WIDE_INT));
+extern REAL_VALUE_TYPE ereal_from_double PROTO((HOST_WIDE_INT *));
+
+#define REAL_VALUES_EQUAL(x, y) (ereal_cmp ((x), (y)) == 0)
+/* true if x < y : */
+#define REAL_VALUES_LESS(x, y) (ereal_cmp ((x), (y)) == -1)
+#define REAL_VALUE_LDEXP(x, n) ereal_ldexp (x, n)
+
+/* These return REAL_VALUE_TYPE: */
+#define REAL_VALUE_RNDZINT(x) (etrunci (x))
+#define REAL_VALUE_UNSIGNED_RNDZINT(x) (etruncui (x))
+extern REAL_VALUE_TYPE real_value_truncate ();
+#define REAL_VALUE_TRUNCATE(mode, x) real_value_truncate (mode, x)
+
+/* These return HOST_WIDE_INT: */
+/* Convert a floating-point value to integer, rounding toward zero. */
+#define REAL_VALUE_FIX(x) (efixi (x))
+/* Convert a floating-point value to unsigned integer, rounding
+ toward zero. */
+#define REAL_VALUE_UNSIGNED_FIX(x) (efixui (x))
+
+#define REAL_VALUE_ATOF ereal_atof
+#define REAL_VALUE_NEGATE ereal_negate
+
+#define REAL_VALUE_MINUS_ZERO(x) \
+ ((ereal_cmp (x, dconst0) == 0) && (ereal_isneg (x) != 0 ))
+
+#define REAL_VALUE_TO_INT ereal_to_int
+
+/* Here the cast to HOST_WIDE_INT sign-extends arguments such as ~0. */
+#define REAL_VALUE_FROM_INT(d, lo, hi) \
+ ereal_from_int (&d, (HOST_WIDE_INT) (lo), (HOST_WIDE_INT) (hi))
+
+#define REAL_VALUE_FROM_UNSIGNED_INT(d, lo, hi) (ereal_from_uint (&d, lo, hi))
+
+/* IN is a REAL_VALUE_TYPE. OUT is an array of longs. */
+#if LONG_DOUBLE_TYPE_SIZE == 96
+#define REAL_VALUE_TO_TARGET_LONG_DOUBLE(IN, OUT) (etarldouble ((IN), (OUT)))
+#else
+#define REAL_VALUE_TO_TARGET_LONG_DOUBLE(IN, OUT) (etartdouble ((IN), (OUT)))
+#endif
+#define REAL_VALUE_TO_TARGET_DOUBLE(IN, OUT) (etardouble ((IN), (OUT)))
+
+/* IN is a REAL_VALUE_TYPE. OUT is a long. */
+#define REAL_VALUE_TO_TARGET_SINGLE(IN, OUT) ((OUT) = etarsingle ((IN)))
+
+/* d is an array of HOST_WIDE_INT that holds a double precision
+ value in the target computer's floating point format. */
+#define REAL_VALUE_FROM_TARGET_DOUBLE(d) (ereal_from_double (d))
+
+/* f is a HOST_WIDE_INT containing a single precision target float value. */
+#define REAL_VALUE_FROM_TARGET_SINGLE(f) (ereal_from_float (f))
+
+/* Conversions to decimal ASCII string. */
+#define REAL_VALUE_TO_DECIMAL(r, fmt, s) (ereal_to_decimal (r, s))
+
+#endif /* REAL_ARITHMETIC defined */
+
+/* **** End of software floating point emulator interface macros **** */
+#else /* No XFmode or TFmode and REAL_ARITHMETIC not defined */
+
+/* old interface */
+#ifdef REAL_ARITHMETIC
+/* Defining REAL_IS_NOT_DOUBLE breaks certain initializations
+ when REAL_ARITHMETIC etc. are not defined. */
+
+/* Now see if the host and target machines use the same format.
+ If not, define REAL_IS_NOT_DOUBLE (even if we end up representing
+ reals as doubles because we have no better way in this cross compiler.)
+ This turns off various optimizations that can happen when we know the
+ compiler's float format matches the target's float format.
+ */
+#if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
+#define REAL_IS_NOT_DOUBLE
+#ifndef REAL_VALUE_TYPE
+typedef struct {
+ HOST_WIDE_INT r[sizeof (double)/sizeof (HOST_WIDE_INT)];
+ } realvaluetype;
+#define REAL_VALUE_TYPE realvaluetype
+#endif /* no REAL_VALUE_TYPE */
+#endif /* formats differ */
+#endif /* 0 */
+
+#endif /* emulator not used */
+
+/* If we are not cross-compiling, use a `double' to represent the
+ floating-point value. Otherwise, use some other type
+ (probably a struct containing an array of longs). */
+#ifndef REAL_VALUE_TYPE
+#define REAL_VALUE_TYPE double
+#else
+#define REAL_IS_NOT_DOUBLE
+#endif
+
+#if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
+
+/* Convert a type `double' value in host format first to a type `float'
+ value in host format and then to a single type `long' value which
+ is the bitwise equivalent of the `float' value. */
+#ifndef REAL_VALUE_TO_TARGET_SINGLE
+#define REAL_VALUE_TO_TARGET_SINGLE(IN, OUT) \
+do { float f = (float) (IN); \
+ (OUT) = *(long *) &f; \
+ } while (0)
+#endif
+
+/* Convert a type `double' value in host format to a pair of type `long'
+ values which is its bitwise equivalent, but put the two words into
+ proper word order for the target. */
+#ifndef REAL_VALUE_TO_TARGET_DOUBLE
+#if HOST_FLOAT_WORDS_BIG_ENDIAN == FLOAT_WORDS_BIG_ENDIAN
+#define REAL_VALUE_TO_TARGET_DOUBLE(IN, OUT) \
+do { REAL_VALUE_TYPE in = (IN); /* Make sure it's not in a register. */\
+ (OUT)[0] = ((long *) &in)[0]; \
+ (OUT)[1] = ((long *) &in)[1]; \
+ } while (0)
+#else
+#define REAL_VALUE_TO_TARGET_DOUBLE(IN, OUT) \
+do { REAL_VALUE_TYPE in = (IN); /* Make sure it's not in a register. */\
+ (OUT)[1] = ((long *) &in)[0]; \
+ (OUT)[0] = ((long *) &in)[1]; \
+ } while (0)
+#endif
+#endif
+#endif /* HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT */
+
+/* In this configuration, double and long double are the same. */
+#ifndef REAL_VALUE_TO_TARGET_LONG_DOUBLE
+#define REAL_VALUE_TO_TARGET_LONG_DOUBLE(a, b) REAL_VALUE_TO_TARGET_DOUBLE (a, b)
+#endif
+
+/* Compare two floating-point values for equality. */
+#ifndef REAL_VALUES_EQUAL
+#define REAL_VALUES_EQUAL(x, y) ((x) == (y))
+#endif
+
+/* Compare two floating-point values for less than. */
+#ifndef REAL_VALUES_LESS
+#define REAL_VALUES_LESS(x, y) ((x) < (y))
+#endif
+
+/* Truncate toward zero to an integer floating-point value. */
+#ifndef REAL_VALUE_RNDZINT
+#define REAL_VALUE_RNDZINT(x) ((double) ((int) (x)))
+#endif
+
+/* Truncate toward zero to an unsigned integer floating-point value. */
+#ifndef REAL_VALUE_UNSIGNED_RNDZINT
+#define REAL_VALUE_UNSIGNED_RNDZINT(x) ((double) ((unsigned int) (x)))
+#endif
+
+/* Convert a floating-point value to integer, rounding toward zero. */
+#ifndef REAL_VALUE_FIX
+#define REAL_VALUE_FIX(x) ((int) (x))
+#endif
+
+/* Convert a floating-point value to unsigned integer, rounding
+ toward zero. */
+#ifndef REAL_VALUE_UNSIGNED_FIX
+#define REAL_VALUE_UNSIGNED_FIX(x) ((unsigned int) (x))
+#endif
+
+/* Scale X by Y powers of 2. */
+#ifndef REAL_VALUE_LDEXP
+#define REAL_VALUE_LDEXP(x, y) ldexp (x, y)
+extern double ldexp ();
+#endif
+
+/* Convert the string X to a floating-point value. */
+#ifndef REAL_VALUE_ATOF
+#if 1
+/* Use real.c to convert decimal numbers to binary, ... */
+REAL_VALUE_TYPE ereal_atof ();
+#define REAL_VALUE_ATOF(x, s) ereal_atof (x, s)
+#else
+/* ... or, if you like the host computer's atof, go ahead and use it: */
+#define REAL_VALUE_ATOF(x, s) atof (x)
+#if defined (MIPSEL) || defined (MIPSEB)
+/* MIPS compiler can't handle parens around the function name.
+ This problem *does not* appear to be connected with any
+ macro definition for atof. It does not seem there is one. */
+extern double atof ();
+#else
+extern double (atof) ();
+#endif
+#endif
+#endif
+
+/* Negate the floating-point value X. */
+#ifndef REAL_VALUE_NEGATE
+#define REAL_VALUE_NEGATE(x) (- (x))
+#endif
+
+/* Truncate the floating-point value X to mode MODE. This is correct only
+ for the most common case where the host and target have objects of the same
+ size and where `float' is SFmode. */
+
+/* Don't use REAL_VALUE_TRUNCATE directly--always call real_value_truncate. */
+extern REAL_VALUE_TYPE real_value_truncate ();
+
+#ifndef REAL_VALUE_TRUNCATE
+#define REAL_VALUE_TRUNCATE(mode, x) \
+ (GET_MODE_BITSIZE (mode) == sizeof (float) * HOST_BITS_PER_CHAR \
+ ? (float) (x) : (x))
+#endif
+
+/* Determine whether a floating-point value X is infinite. */
+#ifndef REAL_VALUE_ISINF
+#define REAL_VALUE_ISINF(x) (target_isinf (x))
+#endif
+
+/* Determine whether a floating-point value X is a NaN. */
+#ifndef REAL_VALUE_ISNAN
+#define REAL_VALUE_ISNAN(x) (target_isnan (x))
+#endif
+
+/* Determine whether a floating-point value X is negative. */
+#ifndef REAL_VALUE_NEGATIVE
+#define REAL_VALUE_NEGATIVE(x) (target_negative (x))
+#endif
+
+/* Determine whether a floating-point value X is minus 0. */
+#ifndef REAL_VALUE_MINUS_ZERO
+#define REAL_VALUE_MINUS_ZERO(x) ((x) == 0 && REAL_VALUE_NEGATIVE (x))
+#endif
+
+/* Constant real values 0, 1, 2, and -1. */
+
+extern REAL_VALUE_TYPE dconst0;
+extern REAL_VALUE_TYPE dconst1;
+extern REAL_VALUE_TYPE dconst2;
+extern REAL_VALUE_TYPE dconstm1;
+
+/* Union type used for extracting real values from CONST_DOUBLEs
+ or putting them in. */
+
+union real_extract
+{
+ REAL_VALUE_TYPE d;
+ HOST_WIDE_INT i[sizeof (REAL_VALUE_TYPE) / sizeof (HOST_WIDE_INT)];
+};
+
+/* For a CONST_DOUBLE:
+ The usual two ints that hold the value.
+ For a DImode, that is all there are;
+ and CONST_DOUBLE_LOW is the low-order word and ..._HIGH the high-order.
+ For a float, the number of ints varies,
+ and CONST_DOUBLE_LOW is the one that should come first *in memory*.
+ So use &CONST_DOUBLE_LOW(r) as the address of an array of ints. */
+#define CONST_DOUBLE_LOW(r) XWINT (r, 2)
+#define CONST_DOUBLE_HIGH(r) XWINT (r, 3)
+
+/* Link for chain of all CONST_DOUBLEs in use in current function. */
+#define CONST_DOUBLE_CHAIN(r) XEXP (r, 1)
+/* The MEM which represents this CONST_DOUBLE's value in memory,
+ or const0_rtx if no MEM has been made for it yet,
+ or cc0_rtx if it is not on the chain. */
+#define CONST_DOUBLE_MEM(r) XEXP (r, 0)
+
+/* Function to return a real value (not a tree node)
+ from a given integer constant. */
+REAL_VALUE_TYPE real_value_from_int_cst ();
+
+/* Given a CONST_DOUBLE in FROM, store into TO the value it represents. */
+
+#define REAL_VALUE_FROM_CONST_DOUBLE(to, from) \
+do { union real_extract u; \
+ bcopy ((char *) &CONST_DOUBLE_LOW ((from)), (char *) &u, sizeof u); \
+ to = u.d; } while (0)
+
+/* Return a CONST_DOUBLE with value R and mode M. */
+
+#define CONST_DOUBLE_FROM_REAL_VALUE(r, m) immed_real_const_1 (r, m)
+extern struct rtx_def *immed_real_const_1 PROTO((REAL_VALUE_TYPE,
+ enum machine_mode));
+
+
+/* Convert a floating point value `r', that can be interpreted
+ as a host machine float or double, to a decimal ASCII string `s'
+ using printf format string `fmt'. */
+#ifndef REAL_VALUE_TO_DECIMAL
+#define REAL_VALUE_TO_DECIMAL(r, fmt, s) (sprintf (s, fmt, r))
+#endif
+
+#endif /* Not REAL_H_INCLUDED */
diff --git a/gnu/usr.bin/cc/include/recog.h b/gnu/usr.bin/cc/include/recog.h
new file mode 100644
index 0000000..8fc2efb
--- /dev/null
+++ b/gnu/usr.bin/cc/include/recog.h
@@ -0,0 +1,120 @@
+/* Declarations for interface to insn recognizer and insn-output.c.
+ Copyright (C) 1987 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Add prototype support. */
+#ifndef PROTO
+#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
+#define PROTO(ARGS) ARGS
+#else
+#define PROTO(ARGS) ()
+#endif
+#endif
+
+/* Recognize an insn and return its insn-code,
+ which is the sequence number of the DEFINE_INSN that it matches.
+ If the insn does not match, return -1. */
+
+extern int recog_memoized PROTO((rtx));
+
+/* Determine whether a proposed change to an insn or MEM will make it
+ invalid. Make the change if not. */
+
+extern int validate_change PROTO((rtx, rtx *, rtx, int));
+
+/* Apply a group of changes if valid. */
+
+extern int apply_change_group PROTO((void));
+
+/* Return the number of changes so far in the current group. */
+
+extern int num_validated_changes PROTO((void));
+
+/* Retract some changes. */
+
+extern void cancel_changes PROTO((int));
+
+/* Nonzero means volatile operands are recognized. */
+
+extern int volatile_ok;
+
+/* Extract the operands from an insn that has been recognized. */
+
+extern void insn_extract PROTO((rtx));
+
+/* The following vectors hold the results from insn_extract. */
+
+/* Indexed by N, gives value of operand N. */
+extern rtx recog_operand[];
+
+/* Indexed by N, gives location where operand N was found. */
+extern rtx *recog_operand_loc[];
+
+/* Indexed by N, gives location where the Nth duplicate-appearance of
+ an operand was found. This is something that matched MATCH_DUP. */
+extern rtx *recog_dup_loc[];
+
+/* Indexed by N, gives the operand number that was duplicated in the
+ Nth duplicate-appearance of an operand. */
+extern char recog_dup_num[];
+
+#ifndef __STDC__
+#ifndef const
+#define const
+#endif
+#endif
+
+/* Access the output function for CODE. */
+
+#define OUT_FCN(CODE) (*insn_outfun[(int) (CODE)])
+
+/* Tables defined in insn-output.c that give information about
+ each insn-code value. */
+
+/* These are vectors indexed by insn-code. Details in genoutput.c. */
+
+extern char *const insn_template[];
+
+extern char *(*const insn_outfun[]) ();
+
+extern const int insn_n_operands[];
+
+extern const int insn_n_dups[];
+
+/* Indexed by insn code number, gives # of constraint alternatives. */
+
+extern const int insn_n_alternatives[];
+
+/* These are two-dimensional arrays indexed first by the insn-code
+ and second by the operand number. Details in genoutput.c. */
+
+#ifdef REGISTER_CONSTRAINTS /* Avoid undef sym in certain broken linkers. */
+extern char *const insn_operand_constraint[][MAX_RECOG_OPERANDS];
+#endif
+
+#ifndef REGISTER_CONSTRAINTS /* Avoid undef sym in certain broken linkers. */
+extern const char insn_operand_address_p[][MAX_RECOG_OPERANDS];
+#endif
+
+extern const enum machine_mode insn_operand_mode[][MAX_RECOG_OPERANDS];
+
+extern const char insn_operand_strict_low[][MAX_RECOG_OPERANDS];
+
+extern int (*const insn_operand_predicate[][MAX_RECOG_OPERANDS]) ();
+
+extern char * insn_name[];
diff --git a/gnu/usr.bin/cc/include/regs.h b/gnu/usr.bin/cc/include/regs.h
new file mode 100644
index 0000000..47463bf
--- /dev/null
+++ b/gnu/usr.bin/cc/include/regs.h
@@ -0,0 +1,168 @@
+/* Define per-register tables for data flow info and register allocation.
+ Copyright (C) 1987, 1993 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+
+#define REG_BYTES(R) mode_size[(int) GET_MODE (R)]
+
+/* Get the number of consecutive hard regs required to hold the REG rtx R.
+ When something may be an explicit hard reg, REG_SIZE is the only
+ valid way to get this value. You cannot get it from the regno. */
+
+#define REG_SIZE(R) \
+ ((mode_size[(int) GET_MODE (R)] + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
+
+/* Maximum register number used in this function, plus one. */
+
+extern int max_regno;
+
+/* Maximum number of SCRATCH rtx's in each block of this function. */
+
+extern int max_scratch;
+
+/* Indexed by n, gives number of times (REG n) is used or set.
+ References within loops may be counted more times. */
+
+extern int *reg_n_refs;
+
+/* Indexed by n, gives number of times (REG n) is set. */
+
+extern short *reg_n_sets;
+
+/* Indexed by N, gives number of insns in which register N dies.
+ Note that if register N is live around loops, it can die
+ in transitions between basic blocks, and that is not counted here.
+ So this is only a reliable indicator of how many regions of life there are
+ for registers that are contained in one basic block. */
+
+extern short *reg_n_deaths;
+
+/* Get the number of consecutive words required to hold pseudo-reg N. */
+
+#define PSEUDO_REGNO_SIZE(N) \
+ ((GET_MODE_SIZE (PSEUDO_REGNO_MODE (N)) + UNITS_PER_WORD - 1) \
+ / UNITS_PER_WORD)
+
+/* Get the number of bytes required to hold pseudo-reg N. */
+
+#define PSEUDO_REGNO_BYTES(N) \
+ GET_MODE_SIZE (PSEUDO_REGNO_MODE (N))
+
+/* Get the machine mode of pseudo-reg N. */
+
+#define PSEUDO_REGNO_MODE(N) GET_MODE (regno_reg_rtx[N])
+
+/* Indexed by N, gives number of CALL_INSNS across which (REG n) is live. */
+
+extern int *reg_n_calls_crossed;
+
+/* Total number of instructions at which (REG n) is live.
+ The larger this is, the less priority (REG n) gets for
+ allocation in a hard register (in global-alloc).
+ This is set in flow.c and remains valid for the rest of the compilation
+ of the function; it is used to control register allocation.
+
+ local-alloc.c may alter this number to change the priority.
+
+ Negative values are special.
+ -1 is used to mark a pseudo reg which has a constant or memory equivalent
+ and is used infrequently enough that it should not get a hard register.
+ -2 is used to mark a pseudo reg for a parameter, when a frame pointer
+ is not required. global.c makes an allocno for this but does
+ not try to assign a hard register to it. */
+
+extern int *reg_live_length;
+
+/* Vector of substitutions of register numbers,
+ used to map pseudo regs into hardware regs. */
+
+extern short *reg_renumber;
+
+/* Vector indexed by hardware reg
+ saying whether that reg is ever used. */
+
+extern char regs_ever_live[FIRST_PSEUDO_REGISTER];
+
+/* Vector indexed by hardware reg giving its name. */
+
+extern char *reg_names[FIRST_PSEUDO_REGISTER];
+
+/* For each hard register, the widest mode object that it can contain.
+ This will be a MODE_INT mode if the register can hold integers. Otherwise
+ it will be a MODE_FLOAT or a MODE_CC mode, whichever is valid for the
+ register. */
+
+extern enum machine_mode reg_raw_mode[FIRST_PSEUDO_REGISTER];
+
+/* Vector indexed by regno; gives uid of first insn using that reg.
+ This is computed by reg_scan for use by cse and loop.
+ It is sometimes adjusted for subsequent changes during loop,
+ but not adjusted by cse even if cse invalidates it. */
+
+extern int *regno_first_uid;
+
+/* Vector indexed by regno; gives uid of last insn using that reg.
+ This is computed by reg_scan for use by cse and loop.
+ It is sometimes adjusted for subsequent changes during loop,
+ but not adjusted by cse even if cse invalidates it.
+ This is harmless since cse won't scan through a loop end. */
+
+extern int *regno_last_uid;
+
+/* Similar, but includes insns that mention the reg in their notes. */
+
+extern int *regno_last_note_uid;
+
+/* Vector indexed by regno; contains 1 for a register is considered a pointer.
+ Reloading, etc. will use a pointer register rather than a non-pointer
+ as the base register in an address, when there is a choice of two regs. */
+
+extern char *regno_pointer_flag;
+#define REGNO_POINTER_FLAG(REGNO) regno_pointer_flag[REGNO]
+
+/* List made of EXPR_LIST rtx's which gives pairs of pseudo registers
+ that have to go in the same hard reg. */
+extern rtx regs_may_share;
+
+/* Vector mapping pseudo regno into the REG rtx for that register.
+ This is computed by reg_scan. */
+
+extern rtx *regno_reg_rtx;
+
+/* Flag set by local-alloc or global-alloc if they decide to allocate
+ something in a call-clobbered register. */
+
+extern int caller_save_needed;
+
+/* Predicate to decide whether to give a hard reg to a pseudo which
+ is referenced REFS times and would need to be saved and restored
+ around a call CALLS times. */
+
+#ifndef CALLER_SAVE_PROFITABLE
+#define CALLER_SAVE_PROFITABLE(REFS, CALLS) (4 * (CALLS) < (REFS))
+#endif
+
+/* Allocated in local_alloc. */
+
+/* A list of SCRATCH rtl allocated by local-alloc. */
+extern rtx *scratch_list;
+/* The basic block in which each SCRATCH is used. */
+extern int *scratch_block;
+/* The length of the arrays pointed to by scratch_block and scratch_list. */
+extern int scratch_list_length;
diff --git a/gnu/usr.bin/cc/include/reload.h b/gnu/usr.bin/cc/include/reload.h
new file mode 100644
index 0000000..4478b6a
--- /dev/null
+++ b/gnu/usr.bin/cc/include/reload.h
@@ -0,0 +1,235 @@
+/* Communication between reload.c and reload1.c.
+ Copyright (C) 1987, 1991, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* If secondary reloads are the same for inputs and outputs, define those
+ macros here. */
+
+#ifdef SECONDARY_RELOAD_CLASS
+#define SECONDARY_INPUT_RELOAD_CLASS(CLASS, MODE, X) \
+ SECONDARY_RELOAD_CLASS (CLASS, MODE, X)
+#define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS, MODE, X) \
+ SECONDARY_RELOAD_CLASS (CLASS, MODE, X)
+#endif
+
+/* If either macro is defined, show that we need secondary reloads. */
+#if defined(SECONDARY_INPUT_RELOAD_CLASS) || defined(SECONDARY_OUTPUT_RELOAD_CLASS)
+#define HAVE_SECONDARY_RELOADS
+#endif
+
+/* See reload.c and reload1.c for comments on these variables. */
+
+/* Maximum number of reloads we can need. */
+#define MAX_RELOADS (2 * MAX_RECOG_OPERANDS * (MAX_REGS_PER_ADDRESS + 1))
+
+extern rtx reload_in[MAX_RELOADS];
+extern rtx reload_out[MAX_RELOADS];
+extern rtx reload_in_reg[MAX_RELOADS];
+extern enum reg_class reload_reg_class[MAX_RELOADS];
+extern enum machine_mode reload_inmode[MAX_RELOADS];
+extern enum machine_mode reload_outmode[MAX_RELOADS];
+extern char reload_optional[MAX_RELOADS];
+extern int reload_inc[MAX_RELOADS];
+extern int reload_opnum[MAX_RELOADS];
+extern int reload_secondary_p[MAX_RELOADS];
+extern int reload_secondary_in_reload[MAX_RELOADS];
+extern int reload_secondary_out_reload[MAX_RELOADS];
+#ifdef MAX_INSN_CODE
+extern enum insn_code reload_secondary_in_icode[MAX_RELOADS];
+extern enum insn_code reload_secondary_out_icode[MAX_RELOADS];
+#endif
+extern int n_reloads;
+
+extern rtx reload_reg_rtx[MAX_RELOADS];
+
+/* Encode the usage of a reload. The following codes are supported:
+
+ RELOAD_FOR_INPUT reload of an input operand
+ RELOAD_FOR_OUTPUT likewise, for output
+ RELOAD_FOR_INSN a reload that must not conflict with anything
+ used in the insn, but may conflict with
+ something used before or after the insn
+ RELOAD_FOR_INPUT_ADDRESS reload for parts of the address of an object
+ that is an input reload
+ RELOAD_FOR_OUTPUT_ADDRESS likewise, for output reload
+ RELOAD_FOR_OPERAND_ADDRESS reload for the address of a non-reloaded
+ operand; these don't conflict with
+ any other addresses.
+ RELOAD_FOR_OPADDR_ADDR reload needed for RELOAD_FOR_OPERAND_ADDRESS
+ reloads; usually secondary reloads
+ RELOAD_OTHER none of the above, usually multiple uses
+ RELOAD_FOR_OTHER_ADDRESS reload for part of the address of an input
+ that is marked RELOAD_OTHER.
+
+ This used to be "enum reload_when_needed" but some debuggers have trouble
+ with an enum tag and variable of the same name. */
+
+enum reload_type
+{
+ RELOAD_FOR_INPUT, RELOAD_FOR_OUTPUT, RELOAD_FOR_INSN,
+ RELOAD_FOR_INPUT_ADDRESS, RELOAD_FOR_OUTPUT_ADDRESS,
+ RELOAD_FOR_OPERAND_ADDRESS, RELOAD_FOR_OPADDR_ADDR,
+ RELOAD_OTHER, RELOAD_FOR_OTHER_ADDRESS
+};
+
+extern enum reload_type reload_when_needed[MAX_RELOADS];
+
+extern rtx *reg_equiv_constant;
+extern rtx *reg_equiv_memory_loc;
+extern rtx *reg_equiv_address;
+extern rtx *reg_equiv_mem;
+
+/* All the "earlyclobber" operands of the current insn
+ are recorded here. */
+extern int n_earlyclobbers;
+extern rtx reload_earlyclobbers[MAX_RECOG_OPERANDS];
+
+/* Save the number of operands. */
+extern int reload_n_operands;
+
+/* First uid used by insns created by reload in this function.
+ Used in find_equiv_reg. */
+extern int reload_first_uid;
+
+/* Nonzero if indirect addressing is supported when the innermost MEM is
+ of the form (MEM (SYMBOL_REF sym)). It is assumed that the level to
+ which these are valid is the same as spill_indirect_levels, above. */
+
+extern char indirect_symref_ok;
+
+/* Nonzero if an address (plus (reg frame_pointer) (reg ...)) is valid. */
+extern char double_reg_address_ok;
+
+#ifdef MAX_INSN_CODE
+/* These arrays record the insn_code of insns that may be needed to
+ perform input and output reloads of special objects. They provide a
+ place to pass a scratch register. */
+extern enum insn_code reload_in_optab[];
+extern enum insn_code reload_out_optab[];
+#endif
+
+/* Functions from reload.c: */
+
+/* Return a memory location that will be used to copy X in mode MODE.
+ If we haven't already made a location for this mode in this insn,
+ call find_reloads_address on the location being returned. */
+extern rtx get_secondary_mem PROTO((rtx, enum machine_mode,
+ int, enum reload_type));
+
+/* Clear any secondary memory locations we've made. */
+extern void clear_secondary_mem PROTO((void));
+
+/* Transfer all replacements that used to be in reload FROM to be in
+ reload TO. */
+extern void transfer_replacements PROTO((int, int));
+
+/* Return 1 if ADDR is a valid memory address for mode MODE,
+ and check that each pseudo reg has the proper kind of
+ hard reg. */
+extern int strict_memory_address_p PROTO((enum machine_mode, rtx));
+
+/* Like rtx_equal_p except that it allows a REG and a SUBREG to match
+ if they are the same hard reg, and has special hacks for
+ autoincrement and autodecrement. */
+extern int operands_match_p PROTO((rtx, rtx));
+
+/* Return the number of times character C occurs in string S. */
+extern int n_occurrences PROTO((int, char *));
+
+/* Return 1 if altering OP will not modify the value of CLOBBER. */
+extern int safe_from_earlyclobber PROTO((rtx, rtx));
+
+/* Search the body of INSN for values that need reloading and record them
+ with push_reload. REPLACE nonzero means record also where the values occur
+ so that subst_reloads can be used. */
+extern void find_reloads PROTO((rtx, int, int, int, short *));
+
+/* Compute the sum of X and Y, making canonicalizations assumed in an
+ address, namely: sum constant integers, surround the sum of two
+ constants with a CONST, put the constant as the second operand, and
+ group the constant on the outermost sum. */
+extern rtx form_sum PROTO((rtx, rtx));
+
+/* Substitute into the current INSN the registers into which we have reloaded
+ the things that need reloading. */
+extern void subst_reloads PROTO((void));
+
+/* Make a copy of any replacements being done into X and move those copies
+ to locations in Y, a copy of X. We only look at the highest level of
+ the RTL. */
+extern void copy_replacements PROTO((rtx, rtx));
+
+/* If LOC was scheduled to be replaced by something, return the replacement.
+ Otherwise, return *LOC. */
+extern rtx find_replacement PROTO((rtx *));
+
+/* Return nonzero if register in range [REGNO, ENDREGNO)
+ appears either explicitly or implicitly in X
+ other than being stored into. */
+extern int refers_to_regno_for_reload_p PROTO((int, int, rtx, rtx *));
+
+/* Nonzero if modifying X will affect IN. */
+extern int reg_overlap_mentioned_for_reload_p PROTO((rtx, rtx));
+
+/* Return nonzero if anything in X contains a MEM. Look also for pseudo
+ registers. */
+extern int refers_to_mem_for_reload_p PROTO((rtx));
+
+/* Check the insns before INSN to see if there is a suitable register
+ containing the same value as GOAL. */
+extern rtx find_equiv_reg PROTO((rtx, rtx, enum reg_class, int, short *,
+ int, enum machine_mode));
+
+/* Return 1 if register REGNO is the subject of a clobber in insn INSN. */
+extern int regno_clobbered_p PROTO((int, rtx));
+
+
+/* Functions in reload1.c: */
+
+/* Initialize the reload pass once per compilation. */
+extern void init_reload PROTO((void));
+
+/* The reload pass itself. */
+extern int reload STDIO_PROTO((rtx, int, FILE *));
+
+/* Mark the slots in regs_ever_live for the hard regs
+ used by pseudo-reg number REGNO. */
+extern void mark_home_live PROTO((int));
+
+/* Scan X and replace any eliminable registers (such as fp) with a
+ replacement (such as sp), plus an offset. */
+extern rtx eliminate_regs PROTO((rtx, enum machine_mode, rtx));
+
+/* Emit code to perform an input reload of IN to RELOADREG. IN is from
+ operand OPNUM with reload type TYPE. */
+extern rtx gen_input_reload PROTO((rtx, rtx, int, enum reload_type));
+
+/* Functions in caller-save.c: */
+
+/* Initialize for caller-save. */
+extern void init_caller_save PROTO((void));
+
+/* Initialize save areas by showing that we haven't allocated any yet. */
+extern void init_save_areas PROTO((void));
+
+/* Allocate save areas for any hard registers that might need saving. */
+extern int setup_save_areas PROTO((int *));
+
+/* Find the places where hard regs are live across calls and save them. */
+extern void save_call_clobbered_regs PROTO((enum machine_mode));
diff --git a/gnu/usr.bin/cc/include/rtl.def b/gnu/usr.bin/cc/include/rtl.def
new file mode 100644
index 0000000..686ad21
--- /dev/null
+++ b/gnu/usr.bin/cc/include/rtl.def
@@ -0,0 +1,764 @@
+/* This file contains the definitions and documentation for the
+ Register Transfer Expressions (rtx's) that make up the
+ Register Transfer Language (rtl) used in the Back End of the GNU compiler.
+ Copyright (C) 1987, 1988, 1992, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* Expression definitions and descriptions for all targets are in this file.
+ Some will not be used for some targets.
+
+ The fields in the cpp macro call "DEF_RTL_EXPR()"
+ are used to create declarations in the C source of the compiler.
+
+ The fields are:
+
+ 1. The internal name of the rtx used in the C source.
+ It is a tag in the enumeration "enum rtx_code" defined in "rtl.h".
+ By convention these are in UPPER_CASE.
+
+ 2. The name of the rtx in the external ASCII format read by
+ read_rtx(), and printed by print_rtx().
+ These names are stored in rtx_name[].
+ By convention these are the internal (field 1) names in lower_case.
+
+ 3. The print format, and type of each rtx->fld[] (field) in this rtx.
+ These formats are stored in rtx_format[].
+ The meaning of the formats is documented in front of this array in rtl.c
+
+ 4. The class of the rtx. These are stored in rtx_class and are accessed
+ via the GET_RTX_CLASS macro. They are defined as follows:
+
+ "o" an rtx code that can be used to represent an object (e.g, REG, MEM)
+ "<" an rtx code for a comparison (e.g, EQ, NE, LT)
+ "1" an rtx code for a unary arithmetic expression (e.g, NEG, NOT)
+ "c" an rtx code for a commutative binary operation (e.g,, PLUS, MULT)
+ "3" an rtx code for a non-bitfield three input operation (IF_THEN_ELSE)
+ "2" an rtx code for a non-commutative binary operation (e.g., MINUS, DIV)
+ "b" an rtx code for a bit-field operation (ZERO_EXTRACT, SIGN_EXTRACT)
+ "i" an rtx code for a machine insn (INSN, JUMP_INSN, CALL_INSN)
+ "m" an rtx code for something that matches in insns (e.g, MATCH_DUP)
+ "x" everything else
+
+ */
+
+/* ---------------------------------------------------------------------
+ Expressions (and "meta" expressions) used for structuring the
+ rtl representation of a program.
+ --------------------------------------------------------------------- */
+
+/* an expression code name unknown to the reader */
+DEF_RTL_EXPR(UNKNOWN, "UnKnown", "*", 'x')
+
+/* (NIL) is used by rtl reader and printer to represent a null pointer. */
+
+DEF_RTL_EXPR(NIL, "nil", "*", 'x')
+
+/* ---------------------------------------------------------------------
+ Expressions used in constructing lists.
+ --------------------------------------------------------------------- */
+
+/* a linked list of expressions */
+DEF_RTL_EXPR(EXPR_LIST, "expr_list", "ee", 'x')
+
+/* a linked list of instructions.
+ The insns are represented in print by their uids. */
+DEF_RTL_EXPR(INSN_LIST, "insn_list", "ue", 'x')
+
+/* ----------------------------------------------------------------------
+ Expression types for machine descriptions.
+ These do not appear in actual rtl code in the compiler.
+ ---------------------------------------------------------------------- */
+
+/* Appears only in machine descriptions.
+ Means use the function named by the second arg (the string)
+ as a predicate; if matched, store the structure that was matched
+ in the operand table at index specified by the first arg (the integer).
+ If the second arg is the null string, the structure is just stored.
+
+ A third string argument indicates to the register allocator restrictions
+ on where the operand can be allocated.
+
+ If the target needs no restriction on any instruction this field should
+ be the null string.
+
+ The string is prepended by:
+ '=' to indicate the operand is only written to.
+ '+' to indicate the operand is both read and written to.
+
+ Each character in the string represents an allocatable class for an operand.
+ 'g' indicates the operand can be any valid class.
+ 'i' indicates the operand can be immediate (in the instruction) data.
+ 'r' indicates the operand can be in a register.
+ 'm' indicates the operand can be in memory.
+ 'o' a subset of the 'm' class. Those memory addressing modes that
+ can be offset at compile time (have a constant added to them).
+
+ Other characters indicate target dependent operand classes and
+ are described in each target's machine description.
+
+ For instructions with more than one operand, sets of classes can be
+ separated by a comma to indicate the appropriate multi-operand constraints.
+ There must be a 1 to 1 correspondence between these sets of classes in
+ all operands for an instruction.
+ */
+DEF_RTL_EXPR(MATCH_OPERAND, "match_operand", "iss", 'm')
+
+/* Appears only in machine descriptions.
+ Means match a SCRATCH or a register. When used to generate rtl, a
+ SCRATCH is generated. As for MATCH_OPERAND, the mode specifies
+ the desired mode and the first argument is the operand number.
+ The second argument is the constraint. */
+DEF_RTL_EXPR(MATCH_SCRATCH, "match_scratch", "is", 'm')
+
+/* Appears only in machine descriptions.
+ Means match only something equal to what is stored in the operand table
+ at the index specified by the argument. */
+DEF_RTL_EXPR(MATCH_DUP, "match_dup", "i", 'm')
+
+/* Appears only in machine descriptions.
+ Means apply a predicate, AND match recursively the operands of the rtx.
+ Operand 0 is the operand-number, as in match_operand.
+ Operand 1 is a predicate to apply (as a string, a function name).
+ Operand 2 is a vector of expressions, each of which must match
+ one subexpression of the rtx this construct is matching. */
+DEF_RTL_EXPR(MATCH_OPERATOR, "match_operator", "isE", 'm')
+
+/* Appears only in machine descriptions.
+ Means to match a PARALLEL of arbitrary length. The predicate is applied
+ to the PARALLEL and the initial expressions in the PARALLEL are matched.
+ Operand 0 is the operand-number, as in match_operand.
+ Operand 1 is a predicate to apply to the PARALLEL.
+ Operand 2 is a vector of expressions, each of which must match the
+ corresponding element in the PARALLEL. */
+DEF_RTL_EXPR(MATCH_PARALLEL, "match_parallel", "isE", 'm')
+
+/* Appears only in machine descriptions.
+ Means match only something equal to what is stored in the operand table
+ at the index specified by the argument. For MATCH_OPERATOR. */
+DEF_RTL_EXPR(MATCH_OP_DUP, "match_op_dup", "iE", 'm')
+
+/* Appears only in machine descriptions.
+ Means match only something equal to what is stored in the operand table
+ at the index specified by the argument. For MATCH_PARALLEL. */
+DEF_RTL_EXPR(MATCH_PAR_DUP, "match_par_dup", "iE", 'm')
+
+/* Appears only in machine descriptions.
+ Defines the pattern for one kind of instruction.
+ Operand:
+ 0: names this instruction.
+ If the name is the null string, the instruction is in the
+ machine description just to be recognized, and will never be emitted by
+ the tree to rtl expander.
+ 1: is the pattern.
+ 2: is a string which is a C expression
+ giving an additional condition for recognizing this pattern.
+ A null string means no extra condition.
+ 3: is the action to execute if this pattern is matched.
+ If this assembler code template starts with a * then it is a fragment of
+ C code to run to decide on a template to use. Otherwise, it is the
+ template to use.
+ 4: optionally, a vector of attributes for this insn.
+ */
+DEF_RTL_EXPR(DEFINE_INSN, "define_insn", "sEssV", 'x')
+
+/* Definition of a peephole optimization.
+ 1st operand: vector of insn patterns to match
+ 2nd operand: C expression that must be true
+ 3rd operand: template or C code to produce assembler output.
+ 4: optionally, a vector of attributes for this insn.
+ */
+DEF_RTL_EXPR(DEFINE_PEEPHOLE, "define_peephole", "EssV", 'x')
+
+/* Definition of a split operation.
+ 1st operand: insn pattern to match
+ 2nd operand: C expression that must be true
+ 3rd operand: vector of insn patterns to place into a SEQUENCE
+ 4th operand: optionally, some C code to execute before generating the
+ insns. This might, for example, create some RTX's and store them in
+ elements of `recog_operand' for use by the vector of insn-patterns.
+ (`operands' is an alias here for `recog_operand'). */
+DEF_RTL_EXPR(DEFINE_SPLIT, "define_split", "EsES", 'x')
+
+/* Definition of a combiner pattern.
+ Operands not defined yet. */
+DEF_RTL_EXPR(DEFINE_COMBINE, "define_combine", "Ess", 'x')
+
+/* Define how to generate multiple insns for a standard insn name.
+ 1st operand: the insn name.
+ 2nd operand: vector of insn-patterns.
+ Use match_operand to substitute an element of `recog_operand'.
+ 3rd operand: C expression that must be true for this to be available.
+ This may not test any operands.
+ 4th operand: Extra C code to execute before generating the insns.
+ This might, for example, create some RTX's and store them in
+ elements of `recog_operand' for use by the vector of insn-patterns.
+ (`operands' is an alias here for `recog_operand'). */
+DEF_RTL_EXPR(DEFINE_EXPAND, "define_expand", "sEss", 'x')
+
+/* Define a requirement for delay slots.
+ 1st operand: Condition involving insn attributes that, if true,
+ indicates that the insn requires the number of delay slots
+ shown.
+ 2nd operand: Vector whose length is the three times the number of delay
+ slots required.
+ Each entry gives three conditions, each involving attributes.
+ The first must be true for an insn to occupy that delay slot
+ location. The second is true for all insns that can be
+ annulled if the branch is true and the third is true for all
+ insns that can be annulled if the branch is false.
+
+ Multiple DEFINE_DELAYs may be present. They indicate differing
+ requirements for delay slots. */
+DEF_RTL_EXPR(DEFINE_DELAY, "define_delay", "eE", 'x')
+
+/* Define a set of insns that requires a function unit. This means that
+ these insns produce their result after a delay and that there may be
+ restrictions on the number of insns of this type that can be scheduled
+ simultaneously.
+
+ More than one DEFINE_FUNCTION_UNIT can be specified for a function unit.
+ Each gives a set of operations and associated delays. The first three
+ operands must be the same for each operation for the same function unit.
+
+ All delays are specified in cycles.
+
+ 1st operand: Name of function unit (mostly for documentation)
+ 2nd operand: Number of identical function units in CPU
+ 3rd operand: Total number of simultaneous insns that can execute on this
+ function unit; 0 if unlimited.
+ 4th operand: Condition involving insn attribute, that, if true, specifies
+ those insns that this expression applies to.
+ 5th operand: Constant delay after which insn result will be
+ available.
+ 6th operand: Delay until next insn can be scheduled on the function unit
+ executing this operation. The meaning depends on whether or
+ not the next operand is supplied.
+ 7th operand: If this operand is not specified, the 6th operand gives the
+ number of cycles after the instruction matching the 4th
+ operand begins using the function unit until a subsequent
+ insn can begin. A value of zero should be used for a
+ unit with no issue constraints. If only one operation can
+ be executed a time and the unit is busy for the entire time,
+ the 3rd operand should be specified as 1, the 6th operand
+ sould be specified as 0, and the 7th operand should not
+ be specified.
+
+ If this operand is specified, it is a list of attribute
+ expressions. If an insn for which any of these expressions
+ is true is currently executing on the function unit, the
+ issue delay will be given by the 6th operand. Otherwise,
+ the insn can be immediately scheduled (subject to the limit
+ on the number of simultaneous operations executing on the
+ unit.) */
+DEF_RTL_EXPR(DEFINE_FUNCTION_UNIT, "define_function_unit", "siieiiV", 'x')
+
+/* Define attribute computation for `asm' instructions. */
+DEF_RTL_EXPR(DEFINE_ASM_ATTRIBUTES, "define_asm_attributes", "V", 'x' )
+
+/* SEQUENCE appears in the result of a `gen_...' function
+ for a DEFINE_EXPAND that wants to make several insns.
+ Its elements are the bodies of the insns that should be made.
+ `emit_insn' takes the SEQUENCE apart and makes separate insns. */
+DEF_RTL_EXPR(SEQUENCE, "sequence", "E", 'x')
+
+/* Refers to the address of its argument.
+ This appears only in machine descriptions, indicating that
+ any expression that would be acceptable as the operand of MEM
+ should be matched. */
+DEF_RTL_EXPR(ADDRESS, "address", "e", 'm')
+
+/* ----------------------------------------------------------------------
+ Expressions used for insn attributes. These also do not appear in
+ actual rtl code in the compiler.
+ ---------------------------------------------------------------------- */
+
+/* Definition of an insn attribute.
+ 1st operand: name of the attribute
+ 2nd operand: comma-separated list of possible attribute values
+ 3rd operand: expression for the default value of the attribute. */
+DEF_RTL_EXPR(DEFINE_ATTR, "define_attr", "sse", 'x')
+
+/* Marker for the name of an attribute. */
+DEF_RTL_EXPR(ATTR, "attr", "s", 'x')
+
+/* For use in the last (optional) operand of DEFINE_INSN or DEFINE_PEEPHOLE and
+ in DEFINE_ASM_INSN to specify an attribute to assign to insns matching that
+ pattern.
+
+ (set_attr "name" "value") is equivalent to
+ (set (attr "name") (const_string "value")) */
+DEF_RTL_EXPR(SET_ATTR, "set_attr", "ss", 'x')
+
+/* In the last operand of DEFINE_INSN and DEFINE_PEEPHOLE, this can be used to
+ specify that attribute values are to be assigned according to the
+ alternative matched.
+
+ The following three expressions are equivalent:
+
+ (set (attr "att") (cond [(eq_attrq "alternative" "1") (const_string "a1")
+ (eq_attrq "alternative" "2") (const_string "a2")]
+ (const_string "a3")))
+ (set_attr_alternative "att" [(const_string "a1") (const_string "a2")
+ (const_string "a3")])
+ (set_attr "att" "a1,a2,a3")
+ */
+DEF_RTL_EXPR(SET_ATTR_ALTERNATIVE, "set_attr_alternative", "sE", 'x')
+
+/* A conditional expression true if the value of the specified attribute of
+ the current insn equals the specified value. The first operand is the
+ attribute name and the second is the comparison value. */
+DEF_RTL_EXPR(EQ_ATTR, "eq_attr", "ss", 'x')
+
+/* A conditional expression which is true if the specified flag is
+ true for the insn being scheduled in reorg.
+
+ genattr.c defines the following flags which can be tested by
+ (attr_flag "foo") expressions in eligible_for_delay.
+
+ forward, backward, very_likely, likely, very_unlikely, and unlikely. */
+
+DEF_RTL_EXPR (ATTR_FLAG, "attr_flag", "s", 'x')
+
+/* ----------------------------------------------------------------------
+ Expression types used for things in the instruction chain.
+
+ All formats must start with "iuu" to handle the chain.
+ Each insn expression holds an rtl instruction and its semantics
+ during back-end processing.
+ See macros's in "rtl.h" for the meaning of each rtx->fld[].
+
+ ---------------------------------------------------------------------- */
+
+/* An instruction that cannot jump. */
+DEF_RTL_EXPR(INSN, "insn", "iuueiee", 'i')
+
+/* An instruction that can possibly jump.
+ Fields ( rtx->fld[] ) have exact same meaning as INSN's. */
+DEF_RTL_EXPR(JUMP_INSN, "jump_insn", "iuueiee0", 'i')
+
+/* An instruction that can possibly call a subroutine
+ but which will not change which instruction comes next
+ in the current function.
+ Field ( rtx->fld[7] ) is CALL_INSN_FUNCTION_USAGE.
+ All other fields ( rtx->fld[] ) have exact same meaning as INSN's. */
+DEF_RTL_EXPR(CALL_INSN, "call_insn", "iuueieee", 'i')
+
+/* A marker that indicates that control will not flow through. */
+DEF_RTL_EXPR(BARRIER, "barrier", "iuu", 'x')
+
+/* Holds a label that is followed by instructions.
+ Operand:
+ 3: is a number that is unique in the entire compilation.
+ 4: is the user-given name of the label, if any.
+ 5: is used in jump.c for the use-count of the label.
+ and in flow.c to point to the chain of label_ref's to this label. */
+DEF_RTL_EXPR(CODE_LABEL, "code_label", "iuuis0", 'x')
+
+/* Say where in the code a source line starts, for symbol table's sake.
+ Contains a filename and a line number. Line numbers <= 0 are special:
+ 0 is used in a dummy placed at the front of every function
+ just so there will never be a need to delete the first insn;
+ -1 indicates a dummy; insns to be deleted by flow analysis and combining
+ are really changed to NOTEs with a number of -1.
+ -2 means beginning of a name binding contour; output N_LBRAC.
+ -3 means end of a contour; output N_RBRAC. */
+DEF_RTL_EXPR(NOTE, "note", "iuusn", 'x')
+
+/* INLINE_HEADER is use by inline function machinery. The information
+ it contains helps to build the mapping function between the rtx's of
+ the function to be inlined and the current function being expanded. */
+
+DEF_RTL_EXPR(INLINE_HEADER, "inline_header", "iuuuiiiiiieiiEe", 'x')
+
+/* ----------------------------------------------------------------------
+ Top level constituents of INSN, JUMP_INSN and CALL_INSN.
+ ---------------------------------------------------------------------- */
+
+/* Several operations to be done in parallel. */
+DEF_RTL_EXPR(PARALLEL, "parallel", "E", 'x')
+
+/* A string that is passed through to the assembler as input.
+ One can obviously pass comments through by using the
+ assembler comment syntax.
+ These occur in an insn all by themselves as the PATTERN.
+ They also appear inside an ASM_OPERANDS
+ as a convenient way to hold a string. */
+DEF_RTL_EXPR(ASM_INPUT, "asm_input", "s", 'x')
+
+/* An assembler instruction with operands.
+ 1st operand is the instruction template.
+ 2nd operand is the constraint for the output.
+ 3rd operand is the number of the output this expression refers to.
+ When an insn stores more than one value, a separate ASM_OPERANDS
+ is made for each output; this integer distinguishes them.
+ 4th is a vector of values of input operands.
+ 5th is a vector of modes and constraints for the input operands.
+ Each element is an ASM_INPUT containing a constraint string
+ and whose mode indicates the mode of the input operand.
+ 6th is the name of the containing source file.
+ 7th is the source line number. */
+DEF_RTL_EXPR(ASM_OPERANDS, "asm_operands", "ssiEEsi", 'x')
+
+/* A machine-specific operation.
+ 1st operand is a vector of operands being used by the operation so that
+ any needed reloads can be done.
+ 2nd operand is a unique value saying which of a number of machine-specific
+ operations is to be performed.
+ (Note that the vector must be the first operand because of the way that
+ genrecog.c record positions within an insn.)
+ This can occur all by itself in a PATTERN, as a component of a PARALLEL,
+ or inside an expression. */
+DEF_RTL_EXPR(UNSPEC, "unspec", "Ei", 'x')
+
+/* Similar, but a volatile operation and one which may trap. */
+DEF_RTL_EXPR(UNSPEC_VOLATILE, "unspec_volatile", "Ei", 'x')
+
+/* Vector of addresses, stored as full words. */
+/* Each element is a LABEL_REF to a CODE_LABEL whose address we want. */
+DEF_RTL_EXPR(ADDR_VEC, "addr_vec", "E", 'x')
+
+/* Vector of address differences X0 - BASE, X1 - BASE, ...
+ First operand is BASE; the vector contains the X's.
+ The machine mode of this rtx says how much space to leave
+ for each difference. */
+DEF_RTL_EXPR(ADDR_DIFF_VEC, "addr_diff_vec", "eE", 'x')
+
+/* ----------------------------------------------------------------------
+ At the top level of an instruction (perhaps under PARALLEL).
+ ---------------------------------------------------------------------- */
+
+/* Assignment.
+ Operand 1 is the location (REG, MEM, PC, CC0 or whatever) assigned to.
+ Operand 2 is the value stored there.
+ ALL assignment must use SET.
+ Instructions that do multiple assignments must use multiple SET,
+ under PARALLEL. */
+DEF_RTL_EXPR(SET, "set", "ee", 'x')
+
+/* Indicate something is used in a way that we don't want to explain.
+ For example, subroutine calls will use the register
+ in which the static chain is passed. */
+DEF_RTL_EXPR(USE, "use", "e", 'x')
+
+/* Indicate something is clobbered in a way that we don't want to explain.
+ For example, subroutine calls will clobber some physical registers
+ (the ones that are by convention not saved). */
+DEF_RTL_EXPR(CLOBBER, "clobber", "e", 'x')
+
+/* Call a subroutine.
+ Operand 1 is the address to call.
+ Operand 2 is the number of arguments. */
+
+DEF_RTL_EXPR(CALL, "call", "ee", 'x')
+
+/* Return from a subroutine. */
+
+DEF_RTL_EXPR(RETURN, "return", "", 'x')
+
+/* Conditional trap.
+ Operand 1 is the condition.
+ Operand 2 is the trap code.
+ For an unconditional trap, make the condition (const_int 1). */
+DEF_RTL_EXPR(TRAP_IF, "trap_if", "ei", 'x')
+
+/* ----------------------------------------------------------------------
+ Primitive values for use in expressions.
+ ---------------------------------------------------------------------- */
+
+/* numeric integer constant */
+DEF_RTL_EXPR(CONST_INT, "const_int", "w", 'o')
+
+/* numeric double constant.
+ Operand 0 is the MEM that stores this constant in memory,
+ or various other things (see comments at immed_double_const in varasm.c).
+ Operand 1 is a chain of all CONST_DOUBLEs in use in the current function.
+ Remaining operands hold the actual value.
+ The number of operands may be more than 2 if cross-compiling;
+ see init_rtl. */
+DEF_RTL_EXPR(CONST_DOUBLE, "const_double", "e0ww", 'o')
+
+/* String constant. Used only for attributes right now. */
+DEF_RTL_EXPR(CONST_STRING, "const_string", "s", 'o')
+
+/* This is used to encapsulate an expression whose value is constant
+ (such as the sum of a SYMBOL_REF and a CONST_INT) so that it will be
+ recognized as a constant operand rather than by arithmetic instructions. */
+
+DEF_RTL_EXPR(CONST, "const", "e", 'o')
+
+/* program counter. Ordinary jumps are represented
+ by a SET whose first operand is (PC). */
+DEF_RTL_EXPR(PC, "pc", "", 'o')
+
+/* A register. The "operand" is the register number, accessed
+ with the REGNO macro. If this number is less than FIRST_PSEUDO_REGISTER
+ than a hardware register is being referred to. */
+DEF_RTL_EXPR(REG, "reg", "i", 'o')
+
+/* A scratch register. This represents a register used only within a
+ single insn. It will be turned into a REG during register allocation
+ or reload unless the constraint indicates that the register won't be
+ needed, in which case it can remain a SCRATCH. This code is
+ marked as having one operand so it can be turned into a REG. */
+DEF_RTL_EXPR(SCRATCH, "scratch", "0", 'o')
+
+/* One word of a multi-word value.
+ The first operand is the complete value; the second says which word.
+ The WORDS_BIG_ENDIAN flag controls whether word number 0
+ (as numbered in a SUBREG) is the most or least significant word.
+
+ This is also used to refer to a value in a different machine mode.
+ For example, it can be used to refer to a SImode value as if it were
+ Qimode, or vice versa. Then the word number is always 0. */
+DEF_RTL_EXPR(SUBREG, "subreg", "ei", 'x')
+
+/* This one-argument rtx is used for move instructions
+ that are guaranteed to alter only the low part of a destination.
+ Thus, (SET (SUBREG:HI (REG...)) (MEM:HI ...))
+ has an unspecified effect on the high part of REG,
+ but (SET (STRICT_LOW_PART (SUBREG:HI (REG...))) (MEM:HI ...))
+ is guaranteed to alter only the bits of REG that are in HImode.
+
+ The actual instruction used is probably the same in both cases,
+ but the register constraints may be tighter when STRICT_LOW_PART
+ is in use. */
+
+DEF_RTL_EXPR(STRICT_LOW_PART, "strict_low_part", "e", 'x')
+
+/* (CONCAT a b) represents the virtual concatenation of a and b
+ to make a value that has as many bits as a and b put together.
+ This is used for complex values. Normally it appears only
+ in DECL_RTLs and during RTL generation, but not in the insn chain. */
+DEF_RTL_EXPR(CONCAT, "concat", "ee", 'o')
+
+/* A memory location; operand is the address.
+ Can be nested inside a VOLATILE. */
+DEF_RTL_EXPR(MEM, "mem", "e", 'o')
+
+/* Reference to an assembler label in the code for this function.
+ The operand is a CODE_LABEL found in the insn chain.
+ The unprinted fields 1 and 2 are used in flow.c for the
+ LABEL_NEXTREF and CONTAINING_INSN. */
+DEF_RTL_EXPR(LABEL_REF, "label_ref", "u00", 'o')
+
+/* Reference to a named label: the string that is the first operand,
+ with `_' added implicitly in front.
+ Exception: if the first character explicitly given is `*',
+ to give it to the assembler, remove the `*' and do not add `_'. */
+DEF_RTL_EXPR(SYMBOL_REF, "symbol_ref", "s", 'o')
+
+/* The condition code register is represented, in our imagination,
+ as a register holding a value that can be compared to zero.
+ In fact, the machine has already compared them and recorded the
+ results; but instructions that look at the condition code
+ pretend to be looking at the entire value and comparing it. */
+DEF_RTL_EXPR(CC0, "cc0", "", 'o')
+
+/* =====================================================================
+ A QUEUED expression really points to a member of the queue of instructions
+ to be output later for postincrement/postdecrement.
+ QUEUED expressions never become part of instructions.
+ When a QUEUED expression would be put into an instruction,
+ instead either the incremented variable or a copy of its previous
+ value is used.
+
+ Operands are:
+ 0. the variable to be incremented (a REG rtx).
+ 1. the incrementing instruction, or 0 if it hasn't been output yet.
+ 2. A REG rtx for a copy of the old value of the variable, or 0 if none yet.
+ 3. the body to use for the incrementing instruction
+ 4. the next QUEUED expression in the queue.
+ ====================================================================== */
+
+DEF_RTL_EXPR(QUEUED, "queued", "eeeee", 'x')
+
+/* ----------------------------------------------------------------------
+ Expressions for operators in an rtl pattern
+ ---------------------------------------------------------------------- */
+
+/* if_then_else. This is used in representing ordinary
+ conditional jump instructions.
+ Operand:
+ 0: condition
+ 1: then expr
+ 2: else expr */
+DEF_RTL_EXPR(IF_THEN_ELSE, "if_then_else", "eee", '3')
+
+/* General conditional. The first operand is a vector composed of pairs of
+ expressions. The first element of each pair is evaluated, in turn.
+ The value of the conditional is the second expression of the first pair
+ whose first expression evaluates non-zero. If none of the expressions is
+ true, the second operand will be used as the value of the conditional.
+
+ This should be replaced with use of IF_THEN_ELSE. */
+DEF_RTL_EXPR(COND, "cond", "Ee", 'x')
+
+/* Comparison, produces a condition code result. */
+DEF_RTL_EXPR(COMPARE, "compare", "ee", '2')
+
+/* plus */
+DEF_RTL_EXPR(PLUS, "plus", "ee", 'c')
+
+/* Operand 0 minus operand 1. */
+DEF_RTL_EXPR(MINUS, "minus", "ee", '2')
+
+/* Minus operand 0. */
+DEF_RTL_EXPR(NEG, "neg", "e", '1')
+
+DEF_RTL_EXPR(MULT, "mult", "ee", 'c')
+
+/* Operand 0 divided by operand 1. */
+DEF_RTL_EXPR(DIV, "div", "ee", '2')
+/* Remainder of operand 0 divided by operand 1. */
+DEF_RTL_EXPR(MOD, "mod", "ee", '2')
+
+/* Unsigned divide and remainder. */
+DEF_RTL_EXPR(UDIV, "udiv", "ee", '2')
+DEF_RTL_EXPR(UMOD, "umod", "ee", '2')
+
+/* Bitwise operations. */
+DEF_RTL_EXPR(AND, "and", "ee", 'c')
+
+DEF_RTL_EXPR(IOR, "ior", "ee", 'c')
+
+DEF_RTL_EXPR(XOR, "xor", "ee", 'c')
+
+DEF_RTL_EXPR(NOT, "not", "e", '1')
+
+/* Operand:
+ 0: value to be shifted.
+ 1: number of bits. */
+DEF_RTL_EXPR(ASHIFT, "ashift", "ee", '2')
+DEF_RTL_EXPR(ROTATE, "rotate", "ee", '2')
+
+/* Right shift operations, for machines where these are not the same
+ as left shifting with a negative argument. */
+
+DEF_RTL_EXPR(ASHIFTRT, "ashiftrt", "ee", '2')
+DEF_RTL_EXPR(LSHIFTRT, "lshiftrt", "ee", '2')
+DEF_RTL_EXPR(ROTATERT, "rotatert", "ee", '2')
+
+/* Minimum and maximum values of two operands. We need both signed and
+ unsigned forms. (We cannot use MIN for SMIN because it conflicts
+ with a macro of the same name.) */
+
+DEF_RTL_EXPR(SMIN, "smin", "ee", 'c')
+DEF_RTL_EXPR(SMAX, "smax", "ee", 'c')
+DEF_RTL_EXPR(UMIN, "umin", "ee", 'c')
+DEF_RTL_EXPR(UMAX, "umax", "ee", 'c')
+
+/* These unary operations are used to represent incrementation
+ and decrementation as they occur in memory addresses.
+ The amount of increment or decrement are not represented
+ because they can be understood from the machine-mode of the
+ containing MEM. These operations exist in only two cases:
+ 1. pushes onto the stack.
+ 2. created automatically by the life_analysis pass in flow.c. */
+DEF_RTL_EXPR(PRE_DEC, "pre_dec", "e", 'x')
+DEF_RTL_EXPR(PRE_INC, "pre_inc", "e", 'x')
+DEF_RTL_EXPR(POST_DEC, "post_dec", "e", 'x')
+DEF_RTL_EXPR(POST_INC, "post_inc", "e", 'x')
+
+/* Comparison operations. The ordered comparisons exist in two
+ flavors, signed and unsigned. */
+DEF_RTL_EXPR(NE, "ne", "ee", '<')
+DEF_RTL_EXPR(EQ, "eq", "ee", '<')
+DEF_RTL_EXPR(GE, "ge", "ee", '<')
+DEF_RTL_EXPR(GT, "gt", "ee", '<')
+DEF_RTL_EXPR(LE, "le", "ee", '<')
+DEF_RTL_EXPR(LT, "lt", "ee", '<')
+DEF_RTL_EXPR(GEU, "geu", "ee", '<')
+DEF_RTL_EXPR(GTU, "gtu", "ee", '<')
+DEF_RTL_EXPR(LEU, "leu", "ee", '<')
+DEF_RTL_EXPR(LTU, "ltu", "ee", '<')
+
+/* Represents the result of sign-extending the sole operand.
+ The machine modes of the operand and of the SIGN_EXTEND expression
+ determine how much sign-extension is going on. */
+DEF_RTL_EXPR(SIGN_EXTEND, "sign_extend", "e", '1')
+
+/* Similar for zero-extension (such as unsigned short to int). */
+DEF_RTL_EXPR(ZERO_EXTEND, "zero_extend", "e", '1')
+
+/* Similar but here the operand has a wider mode. */
+DEF_RTL_EXPR(TRUNCATE, "truncate", "e", '1')
+
+/* Similar for extending floating-point values (such as SFmode to DFmode). */
+DEF_RTL_EXPR(FLOAT_EXTEND, "float_extend", "e", '1')
+DEF_RTL_EXPR(FLOAT_TRUNCATE, "float_truncate", "e", '1')
+
+/* Conversion of fixed point operand to floating point value. */
+DEF_RTL_EXPR(FLOAT, "float", "e", '1')
+
+/* With fixed-point machine mode:
+ Conversion of floating point operand to fixed point value.
+ Value is defined only when the operand's value is an integer.
+ With floating-point machine mode (and operand with same mode):
+ Operand is rounded toward zero to produce an integer value
+ represented in floating point. */
+DEF_RTL_EXPR(FIX, "fix", "e", '1')
+
+/* Conversion of unsigned fixed point operand to floating point value. */
+DEF_RTL_EXPR(UNSIGNED_FLOAT, "unsigned_float", "e", '1')
+
+/* With fixed-point machine mode:
+ Conversion of floating point operand to *unsigned* fixed point value.
+ Value is defined only when the operand's value is an integer. */
+DEF_RTL_EXPR(UNSIGNED_FIX, "unsigned_fix", "e", '1')
+
+/* Absolute value */
+DEF_RTL_EXPR(ABS, "abs", "e", '1')
+
+/* Square root */
+DEF_RTL_EXPR(SQRT, "sqrt", "e", '1')
+
+/* Find first bit that is set.
+ Value is 1 + number of trailing zeros in the arg.,
+ or 0 if arg is 0. */
+DEF_RTL_EXPR(FFS, "ffs", "e", '1')
+
+/* Reference to a signed bit-field of specified size and position.
+ Operand 0 is the memory unit (usually SImode or QImode) which
+ contains the field's first bit. Operand 1 is the width, in bits.
+ Operand 2 is the number of bits in the memory unit before the
+ first bit of this field.
+ If BITS_BIG_ENDIAN is defined, the first bit is the msb and
+ operand 2 counts from the msb of the memory unit.
+ Otherwise, the first bit is the lsb and operand 2 counts from
+ the lsb of the memory unit. */
+DEF_RTL_EXPR(SIGN_EXTRACT, "sign_extract", "eee", 'b')
+
+/* Similar for unsigned bit-field. */
+DEF_RTL_EXPR(ZERO_EXTRACT, "zero_extract", "eee", 'b')
+
+/* For RISC machines. These save memory when splitting insns. */
+
+/* HIGH are the high-order bits of a constant expression. */
+DEF_RTL_EXPR(HIGH, "high", "e", 'o')
+
+/* LO_SUM is the sum of a register and the low-order bits
+ of a constant expression. */
+DEF_RTL_EXPR(LO_SUM, "lo_sum", "ee", 'o')
+
+/*
+Local variables:
+mode:c
+version-control: t
+End:
+*/
diff --git a/gnu/usr.bin/cc/include/rtl.h b/gnu/usr.bin/cc/include/rtl.h
new file mode 100644
index 0000000..b0eb1c52
--- /dev/null
+++ b/gnu/usr.bin/cc/include/rtl.h
@@ -0,0 +1,957 @@
+/* Register Transfer Language (RTL) definitions for GNU C-Compiler
+ Copyright (C) 1987, 1991, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "machmode.h"
+
+#undef FFS /* Some systems predefine this symbol; don't let it interfere. */
+#undef FLOAT /* Likewise. */
+#undef ABS /* Likewise. */
+#undef PC /* Likewise. */
+
+#ifndef TREE_CODE
+union tree_node;
+#endif
+
+/* Register Transfer Language EXPRESSIONS CODES */
+
+#define RTX_CODE enum rtx_code
+enum rtx_code {
+
+#define DEF_RTL_EXPR(ENUM, NAME, FORMAT, CLASS) ENUM ,
+#include "rtl.def" /* rtl expressions are documented here */
+#undef DEF_RTL_EXPR
+
+ LAST_AND_UNUSED_RTX_CODE}; /* A convenient way to get a value for
+ NUM_RTX_CODE.
+ Assumes default enum value assignment. */
+
+#define NUM_RTX_CODE ((int)LAST_AND_UNUSED_RTX_CODE)
+ /* The cast here, saves many elsewhere. */
+
+extern int rtx_length[];
+#define GET_RTX_LENGTH(CODE) (rtx_length[(int)(CODE)])
+
+extern char *rtx_name[];
+#define GET_RTX_NAME(CODE) (rtx_name[(int)(CODE)])
+
+extern char *rtx_format[];
+#define GET_RTX_FORMAT(CODE) (rtx_format[(int)(CODE)])
+
+extern char rtx_class[];
+#define GET_RTX_CLASS(CODE) (rtx_class[(int)(CODE)])
+
+/* Common union for an element of an rtx. */
+
+typedef union rtunion_def
+{
+ HOST_WIDE_INT rtwint;
+ int rtint;
+ char *rtstr;
+ struct rtx_def *rtx;
+ struct rtvec_def *rtvec;
+ enum machine_mode rttype;
+} rtunion;
+
+/* RTL expression ("rtx"). */
+
+typedef struct rtx_def
+{
+#ifdef ONLY_INT_FIELDS
+#ifdef CODE_FIELD_BUG
+ unsigned int code : 16;
+#else
+ unsigned short code;
+#endif
+#else
+ /* The kind of expression this is. */
+ enum rtx_code code : 16;
+#endif
+ /* The kind of value the expression has. */
+#ifdef ONLY_INT_FIELDS
+ int mode : 8;
+#else
+ enum machine_mode mode : 8;
+#endif
+ /* 1 in an INSN if it can alter flow of control
+ within this function. Not yet used! */
+ unsigned int jump : 1;
+ /* 1 in an INSN if it can call another function. Not yet used! */
+ unsigned int call : 1;
+ /* 1 in a MEM or REG if value of this expression will never change
+ during the current function, even though it is not
+ manifestly constant.
+ 1 in a SUBREG if it is from a promoted variable that is unsigned.
+ 1 in a SYMBOL_REF if it addresses something in the per-function
+ constants pool.
+ 1 in a CALL_INSN if it is a const call.
+ 1 in a JUMP_INSN if it is a branch that should be annulled. Valid from
+ reorg until end of compilation; cleared before used. */
+ unsigned int unchanging : 1;
+ /* 1 in a MEM expression if contents of memory are volatile.
+ 1 in an INSN, CALL_INSN, JUMP_INSN, CODE_LABEL or BARRIER
+ if it is deleted.
+ 1 in a REG expression if corresponds to a variable declared by the user.
+ 0 for an internally generated temporary.
+ In a SYMBOL_REF, this flag is used for machine-specific purposes.
+ In a LABEL_REF or in a REG_LABEL note, this is LABEL_REF_NONLOCAL_P. */
+ unsigned int volatil : 1;
+ /* 1 in a MEM referring to a field of a structure (not a union!).
+ 0 if the MEM was a variable or the result of a * operator in C;
+ 1 if it was the result of a . or -> operator (on a struct) in C.
+ 1 in a REG if the register is used only in exit code a loop.
+ 1 in a SUBREG expression if was generated from a variable with a
+ promoted mode.
+ 1 in a CODE_LABEL if the label is used for nonlocal gotos
+ and must not be deleted even if its count is zero.
+ 1 in a LABEL_REF if this is a reference to a label outside the
+ current loop.
+ 1 in an INSN, JUMP_INSN, or CALL_INSN if this insn must be scheduled
+ together with the preceding insn. Valid only within sched.
+ 1 in an INSN, JUMP_INSN, or CALL_INSN if insn is in a delay slot and
+ from the target of a branch. Valid from reorg until end of compilation;
+ cleared before used. */
+ unsigned int in_struct : 1;
+ /* 1 if this rtx is used. This is used for copying shared structure.
+ See `unshare_all_rtl'.
+ In a REG, this is not needed for that purpose, and used instead
+ in `leaf_renumber_regs_insn'.
+ In a SYMBOL_REF, means that emit_library_call
+ has used it as the function. */
+ unsigned int used : 1;
+ /* Nonzero if this rtx came from procedure integration.
+ In a REG, nonzero means this reg refers to the return value
+ of the current function. */
+ unsigned integrated : 1;
+ /* The first element of the operands of this rtx.
+ The number of operands and their types are controlled
+ by the `code' field, according to rtl.def. */
+ rtunion fld[1];
+} *rtx;
+
+
+/* Add prototype support. */
+#ifndef PROTO
+#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
+#define PROTO(ARGS) ARGS
+#else
+#define PROTO(ARGS) ()
+#endif
+#endif
+
+#ifndef VPROTO
+#ifdef __STDC__
+#define PVPROTO(ARGS) ARGS
+#define VPROTO(ARGS) ARGS
+#define VA_START(va_list,var) va_start(va_list,var)
+#else
+#define PVPROTO(ARGS) ()
+#define VPROTO(ARGS) (va_alist) va_dcl
+#define VA_START(va_list,var) va_start(va_list)
+#endif
+#endif
+
+#ifndef STDIO_PROTO
+#ifdef BUFSIZ
+#define STDIO_PROTO(ARGS) PROTO(ARGS)
+#else
+#define STDIO_PROTO(ARGS) ()
+#endif
+#endif
+
+#define NULL_RTX (rtx) 0
+
+/* Define a generic NULL if one hasn't already been defined. */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef GENERIC_PTR
+#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
+#define GENERIC_PTR void *
+#else
+#define GENERIC_PTR char *
+#endif
+#endif
+
+#ifndef NULL_PTR
+#define NULL_PTR ((GENERIC_PTR)0)
+#endif
+
+/* Define macros to access the `code' field of the rtx. */
+
+#ifdef SHORT_ENUM_BUG
+#define GET_CODE(RTX) ((enum rtx_code) ((RTX)->code))
+#define PUT_CODE(RTX, CODE) ((RTX)->code = ((short) (CODE)))
+#else
+#define GET_CODE(RTX) ((RTX)->code)
+#define PUT_CODE(RTX, CODE) ((RTX)->code = (CODE))
+#endif
+
+#define GET_MODE(RTX) ((RTX)->mode)
+#define PUT_MODE(RTX, MODE) ((RTX)->mode = (MODE))
+
+#define RTX_INTEGRATED_P(RTX) ((RTX)->integrated)
+#define RTX_UNCHANGING_P(RTX) ((RTX)->unchanging)
+
+/* RTL vector. These appear inside RTX's when there is a need
+ for a variable number of things. The principle use is inside
+ PARALLEL expressions. */
+
+typedef struct rtvec_def{
+ unsigned num_elem; /* number of elements */
+ rtunion elem[1];
+} *rtvec;
+
+#define NULL_RTVEC (rtvec) 0
+
+#define GET_NUM_ELEM(RTVEC) ((RTVEC)->num_elem)
+#define PUT_NUM_ELEM(RTVEC, NUM) ((RTVEC)->num_elem = (unsigned) NUM)
+
+#define RTVEC_ELT(RTVEC, I) ((RTVEC)->elem[(I)].rtx)
+
+/* 1 if X is a REG. */
+
+#define REG_P(X) (GET_CODE (X) == REG)
+
+/* 1 if X is a constant value that is an integer. */
+
+#define CONSTANT_P(X) \
+ (GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF \
+ || GET_CODE (X) == CONST_INT || GET_CODE (X) == CONST_DOUBLE \
+ || GET_CODE (X) == CONST || GET_CODE (X) == HIGH)
+
+/* General accessor macros for accessing the fields of an rtx. */
+
+#define XEXP(RTX, N) ((RTX)->fld[N].rtx)
+#define XINT(RTX, N) ((RTX)->fld[N].rtint)
+#define XWINT(RTX, N) ((RTX)->fld[N].rtwint)
+#define XSTR(RTX, N) ((RTX)->fld[N].rtstr)
+#define XVEC(RTX, N) ((RTX)->fld[N].rtvec)
+#define XVECLEN(RTX, N) ((RTX)->fld[N].rtvec->num_elem)
+#define XVECEXP(RTX,N,M)((RTX)->fld[N].rtvec->elem[M].rtx)
+
+/* ACCESS MACROS for particular fields of insns. */
+
+/* Holds a unique number for each insn.
+ These are not necessarily sequentially increasing. */
+#define INSN_UID(INSN) ((INSN)->fld[0].rtint)
+
+/* Chain insns together in sequence. */
+#define PREV_INSN(INSN) ((INSN)->fld[1].rtx)
+#define NEXT_INSN(INSN) ((INSN)->fld[2].rtx)
+
+/* The body of an insn. */
+#define PATTERN(INSN) ((INSN)->fld[3].rtx)
+
+/* Code number of instruction, from when it was recognized.
+ -1 means this instruction has not been recognized yet. */
+#define INSN_CODE(INSN) ((INSN)->fld[4].rtint)
+
+/* Set up in flow.c; empty before then.
+ Holds a chain of INSN_LIST rtx's whose first operands point at
+ previous insns with direct data-flow connections to this one.
+ That means that those insns set variables whose next use is in this insn.
+ They are always in the same basic block as this insn. */
+#define LOG_LINKS(INSN) ((INSN)->fld[5].rtx)
+
+/* 1 if insn has been deleted. */
+#define INSN_DELETED_P(INSN) ((INSN)->volatil)
+
+/* 1 if insn is a call to a const function. */
+#define CONST_CALL_P(INSN) ((INSN)->unchanging)
+
+/* 1 if insn is a branch that should not unconditionally execute its
+ delay slots, i.e., it is an annulled branch. */
+#define INSN_ANNULLED_BRANCH_P(INSN) ((INSN)->unchanging)
+
+/* 1 if insn is in a delay slot and is from the target of the branch. If
+ the branch insn has INSN_ANNULLED_BRANCH_P set, this insn should only be
+ executed if the branch is taken. For annulled branches with this bit
+ clear, the insn should be executed only if the branch is not taken. */
+#define INSN_FROM_TARGET_P(INSN) ((INSN)->in_struct)
+
+/* Holds a list of notes on what this insn does to various REGs.
+ It is a chain of EXPR_LIST rtx's, where the second operand
+ is the chain pointer and the first operand is the REG being described.
+ The mode field of the EXPR_LIST contains not a real machine mode
+ but a value that says what this note says about the REG:
+ REG_DEAD means that the value in REG dies in this insn (i.e., it is
+ not needed past this insn). If REG is set in this insn, the REG_DEAD
+ note may, but need not, be omitted.
+ REG_INC means that the REG is autoincremented or autodecremented.
+ REG_EQUIV describes the insn as a whole; it says that the
+ insn sets a register to a constant value or to be equivalent to
+ a memory address. If the
+ register is spilled to the stack then the constant value
+ should be substituted for it. The contents of the REG_EQUIV
+ is the constant value or memory address, which may be different
+ from the source of the SET although it has the same value.
+ REG_EQUAL is like REG_EQUIV except that the destination
+ is only momentarily equal to the specified rtx. Therefore, it
+ cannot be used for substitution; but it can be used for cse.
+ REG_RETVAL means that this insn copies the return-value of
+ a library call out of the hard reg for return values. This note
+ is actually an INSN_LIST and it points to the first insn involved
+ in setting up arguments for the call. flow.c uses this to delete
+ the entire library call when its result is dead.
+ REG_LIBCALL is the inverse of REG_RETVAL: it goes on the first insn
+ of the library call and points at the one that has the REG_RETVAL.
+ REG_WAS_0 says that the register set in this insn held 0 before the insn.
+ The contents of the note is the insn that stored the 0.
+ If that insn is deleted or patched to a NOTE, the REG_WAS_0 is inoperative.
+ The REG_WAS_0 note is actually an INSN_LIST, not an EXPR_LIST.
+ REG_NONNEG means that the register is always nonnegative during
+ the containing loop. This is used in branches so that decrement and
+ branch instructions terminating on zero can be matched. There must be
+ an insn pattern in the md file named `decrement_and_branch_until_zero'
+ or else this will never be added to any instructions.
+ REG_NO_CONFLICT means there is no conflict *after this insn*
+ between the register in the note and the destination of this insn.
+ REG_UNUSED identifies a register set in this insn and never used.
+ REG_CC_SETTER and REG_CC_USER link a pair of insns that set and use
+ CC0, respectively. Normally, these are required to be consecutive insns,
+ but we permit putting a cc0-setting insn in the delay slot of a branch
+ as long as only one copy of the insn exists. In that case, these notes
+ point from one to the other to allow code generation to determine what
+ any require information and to properly update CC_STATUS.
+ REG_LABEL points to a CODE_LABEL. Used by non-JUMP_INSNs to
+ say that the CODE_LABEL contained in the REG_LABEL note is used
+ by the insn.
+ REG_DEP_ANTI is used in LOG_LINKS which represent anti (write after read)
+ dependencies. REG_DEP_OUTPUT is used in LOG_LINKS which represent output
+ (write after write) dependencies. Data dependencies, which are the only
+ type of LOG_LINK created by flow, are represented by a 0 reg note kind. */
+
+#define REG_NOTES(INSN) ((INSN)->fld[6].rtx)
+
+/* Don't forget to change reg_note_name in rtl.c. */
+enum reg_note { REG_DEAD = 1, REG_INC = 2, REG_EQUIV = 3, REG_WAS_0 = 4,
+ REG_EQUAL = 5, REG_RETVAL = 6, REG_LIBCALL = 7,
+ REG_NONNEG = 8, REG_NO_CONFLICT = 9, REG_UNUSED = 10,
+ REG_CC_SETTER = 11, REG_CC_USER = 12, REG_LABEL = 13,
+ REG_DEP_ANTI = 14, REG_DEP_OUTPUT = 15 };
+
+/* Define macros to extract and insert the reg-note kind in an EXPR_LIST. */
+#define REG_NOTE_KIND(LINK) ((enum reg_note) GET_MODE (LINK))
+#define PUT_REG_NOTE_KIND(LINK,KIND) PUT_MODE(LINK, (enum machine_mode) (KIND))
+
+/* Names for REG_NOTE's in EXPR_LIST insn's. */
+
+extern char *reg_note_name[];
+#define GET_REG_NOTE_NAME(MODE) (reg_note_name[(int)(MODE)])
+
+/* This field is only present on CALL_INSNs. It holds a chain of EXPR_LIST of
+ USE and CLOBBER expressions.
+ USE expressions list the registers filled with arguments that
+ are passed to the function.
+ CLOBBER expressions document the registers explicitly clobbered
+ by this CALL_INSN.
+ Pseudo registers can not be mentioned in this list. */
+#define CALL_INSN_FUNCTION_USAGE(INSN) ((INSN)->fld[7].rtx)
+
+/* The label-number of a code-label. The assembler label
+ is made from `L' and the label-number printed in decimal.
+ Label numbers are unique in a compilation. */
+#define CODE_LABEL_NUMBER(INSN) ((INSN)->fld[3].rtint)
+
+#define LINE_NUMBER NOTE
+
+/* In a NOTE that is a line number, this is a string for the file name
+ that the line is in. We use the same field to record block numbers
+ temporarily in NOTE_INSN_BLOCK_BEG and NOTE_INSN_BLOCK_END notes.
+ (We avoid lots of casts between ints and pointers if we use a
+ different macro for the bock number.) */
+
+#define NOTE_SOURCE_FILE(INSN) ((INSN)->fld[3].rtstr)
+#define NOTE_BLOCK_NUMBER(INSN) ((INSN)->fld[3].rtint)
+
+/* In a NOTE that is a line number, this is the line number.
+ Other kinds of NOTEs are identified by negative numbers here. */
+#define NOTE_LINE_NUMBER(INSN) ((INSN)->fld[4].rtint)
+
+/* Codes that appear in the NOTE_LINE_NUMBER field
+ for kinds of notes that are not line numbers.
+
+ Notice that we do not try to use zero here for any of
+ the special note codes because sometimes the source line
+ actually can be zero! This happens (for example) when we
+ are generating code for the per-translation-unit constructor
+ and destructor routines for some C++ translation unit.
+
+ If you should change any of the following values, or if you
+ should add a new value here, don't forget to change the
+ note_insn_name array in rtl.c. */
+
+/* This note is used to get rid of an insn
+ when it isn't safe to patch the insn out of the chain. */
+#define NOTE_INSN_DELETED -1
+#define NOTE_INSN_BLOCK_BEG -2
+#define NOTE_INSN_BLOCK_END -3
+#define NOTE_INSN_LOOP_BEG -4
+#define NOTE_INSN_LOOP_END -5
+/* This kind of note is generated at the end of the function body,
+ just before the return insn or return label.
+ In an optimizing compilation it is deleted by the first jump optimization,
+ after enabling that optimizer to determine whether control can fall
+ off the end of the function body without a return statement. */
+#define NOTE_INSN_FUNCTION_END -6
+/* This kind of note is generated just after each call to `setjmp', et al. */
+#define NOTE_INSN_SETJMP -7
+/* Generated at the place in a loop that `continue' jumps to. */
+#define NOTE_INSN_LOOP_CONT -8
+/* Generated at the start of a duplicated exit test. */
+#define NOTE_INSN_LOOP_VTOP -9
+/* This marks the point immediately after the last prologue insn. */
+#define NOTE_INSN_PROLOGUE_END -10
+/* This marks the point immediately prior to the first epilogue insn. */
+#define NOTE_INSN_EPILOGUE_BEG -11
+/* Generated in place of user-declared labels when they are deleted. */
+#define NOTE_INSN_DELETED_LABEL -12
+/* This note indicates the start of the real body of the function,
+ i.e. the point just after all of the parms have been moved into
+ their homes, etc. */
+#define NOTE_INSN_FUNCTION_BEG -13
+
+
+#if 0 /* These are not used, and I don't know what they were for. --rms. */
+#define NOTE_DECL_NAME(INSN) ((INSN)->fld[3].rtstr)
+#define NOTE_DECL_CODE(INSN) ((INSN)->fld[4].rtint)
+#define NOTE_DECL_RTL(INSN) ((INSN)->fld[5].rtx)
+#define NOTE_DECL_IDENTIFIER(INSN) ((INSN)->fld[6].rtint)
+#define NOTE_DECL_TYPE(INSN) ((INSN)->fld[7].rtint)
+#endif /* 0 */
+
+/* Names for NOTE insn's other than line numbers. */
+
+extern char *note_insn_name[];
+#define GET_NOTE_INSN_NAME(NOTE_CODE) (note_insn_name[-(NOTE_CODE)])
+
+/* The name of a label, in case it corresponds to an explicit label
+ in the input source code. */
+#define LABEL_NAME(LABEL) ((LABEL)->fld[4].rtstr)
+
+/* In jump.c, each label contains a count of the number
+ of LABEL_REFs that point at it, so unused labels can be deleted. */
+#define LABEL_NUSES(LABEL) ((LABEL)->fld[5].rtint)
+
+/* The rest is used instead of the above, in a CODE_LABEL,
+ if bytecode is being output.
+ We make the slightly klugy assumption that a LABEL has enough slots
+ to hold these things. That happens to be true. */
+
+/* For static or external objects. */
+#define BYTECODE_LABEL(X) (XEXP ((X), 0))
+
+/* For goto labels inside bytecode functions. */
+#define BYTECODE_BC_LABEL(X) (*(struct bc_label **) &XEXP ((X), 1))
+
+/* In jump.c, each JUMP_INSN can point to a label that it can jump to,
+ so that if the JUMP_INSN is deleted, the label's LABEL_NUSES can
+ be decremented and possibly the label can be deleted. */
+#define JUMP_LABEL(INSN) ((INSN)->fld[7].rtx)
+
+/* Once basic blocks are found in flow.c,
+ each CODE_LABEL starts a chain that goes through
+ all the LABEL_REFs that jump to that label.
+ The chain eventually winds up at the CODE_LABEL; it is circular. */
+#define LABEL_REFS(LABEL) ((LABEL)->fld[5].rtx)
+
+/* This is the field in the LABEL_REF through which the circular chain
+ of references to a particular label is linked.
+ This chain is set up in flow.c. */
+
+#define LABEL_NEXTREF(REF) ((REF)->fld[1].rtx)
+
+/* Once basic blocks are found in flow.c,
+ Each LABEL_REF points to its containing instruction with this field. */
+
+#define CONTAINING_INSN(RTX) ((RTX)->fld[2].rtx)
+
+/* For a REG rtx, REGNO extracts the register number. */
+
+#define REGNO(RTX) ((RTX)->fld[0].rtint)
+
+/* For a REG rtx, REG_FUNCTION_VALUE_P is nonzero if the reg
+ is the current function's return value. */
+
+#define REG_FUNCTION_VALUE_P(RTX) ((RTX)->integrated)
+
+/* 1 in a REG rtx if it corresponds to a variable declared by the user. */
+#define REG_USERVAR_P(RTX) ((RTX)->volatil)
+
+/* For a CONST_INT rtx, INTVAL extracts the integer. */
+
+#define INTVAL(RTX) ((RTX)->fld[0].rtwint)
+
+/* For a SUBREG rtx, SUBREG_REG extracts the value we want a subreg of.
+ SUBREG_WORD extracts the word-number. */
+
+#define SUBREG_REG(RTX) ((RTX)->fld[0].rtx)
+#define SUBREG_WORD(RTX) ((RTX)->fld[1].rtint)
+
+/* 1 if the REG contained in SUBREG_REG is already known to be
+ sign- or zero-extended from the mode of the SUBREG to the mode of
+ the reg. SUBREG_PROMOTED_UNSIGNED_P gives the signedness of the
+ extension.
+
+ When used as a LHS, is means that this extension must be done
+ when assigning to SUBREG_REG. */
+
+#define SUBREG_PROMOTED_VAR_P(RTX) ((RTX)->in_struct)
+#define SUBREG_PROMOTED_UNSIGNED_P(RTX) ((RTX)->unchanging)
+
+/* Access various components of an ASM_OPERANDS rtx. */
+
+#define ASM_OPERANDS_TEMPLATE(RTX) XSTR ((RTX), 0)
+#define ASM_OPERANDS_OUTPUT_CONSTRAINT(RTX) XSTR ((RTX), 1)
+#define ASM_OPERANDS_OUTPUT_IDX(RTX) XINT ((RTX), 2)
+#define ASM_OPERANDS_INPUT_VEC(RTX) XVEC ((RTX), 3)
+#define ASM_OPERANDS_INPUT_CONSTRAINT_VEC(RTX) XVEC ((RTX), 4)
+#define ASM_OPERANDS_INPUT(RTX, N) XVECEXP ((RTX), 3, (N))
+#define ASM_OPERANDS_INPUT_LENGTH(RTX) XVECLEN ((RTX), 3)
+#define ASM_OPERANDS_INPUT_CONSTRAINT(RTX, N) XSTR (XVECEXP ((RTX), 4, (N)), 0)
+#define ASM_OPERANDS_INPUT_MODE(RTX, N) GET_MODE (XVECEXP ((RTX), 4, (N)))
+#define ASM_OPERANDS_SOURCE_FILE(RTX) XSTR ((RTX), 5)
+#define ASM_OPERANDS_SOURCE_LINE(RTX) XINT ((RTX), 6)
+
+/* For a MEM rtx, 1 if it's a volatile reference.
+ Also in an ASM_OPERANDS rtx. */
+#define MEM_VOLATILE_P(RTX) ((RTX)->volatil)
+
+/* For a MEM rtx, 1 if it refers to a structure or union component. */
+#define MEM_IN_STRUCT_P(RTX) ((RTX)->in_struct)
+
+/* For a LABEL_REF, 1 means that this reference is to a label outside the
+ loop containing the reference. */
+#define LABEL_OUTSIDE_LOOP_P(RTX) ((RTX)->in_struct)
+
+/* For a LABEL_REF, 1 means it is for a nonlocal label. */
+/* Likewise in an EXPR_LIST for a REG_LABEL note. */
+#define LABEL_REF_NONLOCAL_P(RTX) ((RTX)->volatil)
+
+/* For a CODE_LABEL, 1 means always consider this label to be needed. */
+#define LABEL_PRESERVE_P(RTX) ((RTX)->in_struct)
+
+/* For a REG, 1 means the register is used only in an exit test of a loop. */
+#define REG_LOOP_TEST_P(RTX) ((RTX)->in_struct)
+
+/* During sched, for an insn, 1 means that the insn must be scheduled together
+ with the preceding insn. */
+#define SCHED_GROUP_P(INSN) ((INSN)->in_struct)
+
+/* During sched, for the LOG_LINKS of an insn, these cache the adjusted
+ cost of the dependence link. The cost of executing an instruction
+ may vary based on how the results are used. LINK_COST_ZERO is 1 when
+ the cost through the link varies and is unchanged (i.e., the link has
+ zero additional cost). LINK_COST_FREE is 1 when the cost through the
+ link is zero (i.e., the link makes the cost free). In other cases,
+ the adjustment to the cost is recomputed each time it is needed. */
+#define LINK_COST_ZERO(X) ((X)->jump)
+#define LINK_COST_FREE(X) ((X)->call)
+
+/* For a SET rtx, SET_DEST is the place that is set
+ and SET_SRC is the value it is set to. */
+#define SET_DEST(RTX) ((RTX)->fld[0].rtx)
+#define SET_SRC(RTX) ((RTX)->fld[1].rtx)
+
+/* For a TRAP_IF rtx, TRAP_CONDITION is an expression. */
+#define TRAP_CONDITION(RTX) ((RTX)->fld[0].rtx)
+
+/* 1 in a SYMBOL_REF if it addresses this function's constants pool. */
+#define CONSTANT_POOL_ADDRESS_P(RTX) ((RTX)->unchanging)
+
+/* Flag in a SYMBOL_REF for machine-specific purposes. */
+#define SYMBOL_REF_FLAG(RTX) ((RTX)->volatil)
+
+/* 1 means a SYMBOL_REF has been the library function in emit_library_call. */
+#define SYMBOL_REF_USED(RTX) ((RTX)->used)
+
+/* For an INLINE_HEADER rtx, FIRST_FUNCTION_INSN is the first insn
+ of the function that is not involved in copying parameters to
+ pseudo-registers. FIRST_PARM_INSN is the very first insn of
+ the function, including the parameter copying.
+ We keep this around in case we must splice
+ this function into the assembly code at the end of the file.
+ FIRST_LABELNO is the first label number used by the function (inclusive).
+ LAST_LABELNO is the last label used by the function (exclusive).
+ MAX_REGNUM is the largest pseudo-register used by that function.
+ FUNCTION_ARGS_SIZE is the size of the argument block in the stack.
+ POPS_ARGS is the number of bytes of input arguments popped by the function
+ STACK_SLOT_LIST is the list of stack slots.
+ FUNCTION_FLAGS are where single-bit flags are saved.
+ OUTGOING_ARGS_SIZE is the size of the largest outgoing stack parameter list.
+ ORIGINAL_ARG_VECTOR is a vector of the original DECL_RTX values
+ for the function arguments.
+ ORIGINAL_DECL_INITIAL is a pointer to the original DECL_INITIAL for the
+ function.
+
+ We want this to lay down like an INSN. The PREV_INSN field
+ is always NULL. The NEXT_INSN field always points to the
+ first function insn of the function being squirreled away. */
+
+#define FIRST_FUNCTION_INSN(RTX) ((RTX)->fld[2].rtx)
+#define FIRST_PARM_INSN(RTX) ((RTX)->fld[3].rtx)
+#define FIRST_LABELNO(RTX) ((RTX)->fld[4].rtint)
+#define LAST_LABELNO(RTX) ((RTX)->fld[5].rtint)
+#define MAX_PARMREG(RTX) ((RTX)->fld[6].rtint)
+#define MAX_REGNUM(RTX) ((RTX)->fld[7].rtint)
+#define FUNCTION_ARGS_SIZE(RTX) ((RTX)->fld[8].rtint)
+#define POPS_ARGS(RTX) ((RTX)->fld[9].rtint)
+#define STACK_SLOT_LIST(RTX) ((RTX)->fld[10].rtx)
+#define FUNCTION_FLAGS(RTX) ((RTX)->fld[11].rtint)
+#define OUTGOING_ARGS_SIZE(RTX) ((RTX)->fld[12].rtint)
+#define ORIGINAL_ARG_VECTOR(RTX) ((RTX)->fld[13].rtvec)
+#define ORIGINAL_DECL_INITIAL(RTX) ((RTX)->fld[14].rtx)
+
+/* In FUNCTION_FLAGS we save some variables computed when emitting the code
+ for the function and which must be `or'ed into the current flag values when
+ insns from that function are being inlined. */
+
+/* These ought to be an enum, but non-ANSI compilers don't like that. */
+#define FUNCTION_FLAGS_CALLS_ALLOCA 01
+#define FUNCTION_FLAGS_CALLS_SETJMP 02
+#define FUNCTION_FLAGS_RETURNS_STRUCT 04
+#define FUNCTION_FLAGS_RETURNS_PCC_STRUCT 010
+#define FUNCTION_FLAGS_NEEDS_CONTEXT 020
+#define FUNCTION_FLAGS_HAS_NONLOCAL_LABEL 040
+#define FUNCTION_FLAGS_RETURNS_POINTER 0100
+#define FUNCTION_FLAGS_USES_CONST_POOL 0200
+#define FUNCTION_FLAGS_CALLS_LONGJMP 0400
+#define FUNCTION_FLAGS_USES_PIC_OFFSET_TABLE 01000
+
+/* Define a macro to look for REG_INC notes,
+ but save time on machines where they never exist. */
+
+/* Don't continue this line--convex cc version 4.1 would lose. */
+#if (defined (HAVE_PRE_INCREMENT) || defined (HAVE_PRE_DECREMENT) || defined (HAVE_POST_INCREMENT) || defined (HAVE_POST_DECREMENT))
+#define FIND_REG_INC_NOTE(insn, reg) (find_reg_note ((insn), REG_INC, (reg)))
+#else
+#define FIND_REG_INC_NOTE(insn, reg) 0
+#endif
+
+/* Indicate whether the machine has any sort of auto increment addressing.
+ If not, we can avoid checking for REG_INC notes. */
+
+/* Don't continue this line--convex cc version 4.1 would lose. */
+#if (defined (HAVE_PRE_INCREMENT) || defined (HAVE_PRE_DECREMENT) || defined (HAVE_POST_INCREMENT) || defined (HAVE_POST_DECREMENT))
+#define AUTO_INC_DEC
+#endif
+
+/* Generally useful functions. */
+
+/* The following functions accept a wide integer argument. Rather than
+ having to cast on every function call, we use a macro instead, that is
+ defined here and in tree.h. */
+
+#ifndef exact_log2
+#define exact_log2(N) exact_log2_wide ((HOST_WIDE_INT) (N))
+#define floor_log2(N) floor_log2_wide ((HOST_WIDE_INT) (N))
+#endif
+
+#define plus_constant(X,C) plus_constant_wide (X, (HOST_WIDE_INT) (C))
+
+#define plus_constant_for_output(X,C) \
+ plus_constant_for_output_wide (X, (HOST_WIDE_INT) (C))
+
+extern rtx plus_constant_wide PROTO((rtx, HOST_WIDE_INT));
+extern rtx plus_constant_for_output_wide PROTO((rtx, HOST_WIDE_INT));
+
+#define GEN_INT(N) gen_rtx (CONST_INT, VOIDmode, (HOST_WIDE_INT) (N))
+
+extern rtx bc_gen_rtx ();
+
+extern rtx gen_rtx PVPROTO((enum rtx_code,
+ enum machine_mode, ...));
+extern rtvec gen_rtvec PVPROTO((int, ...));
+
+extern rtx read_rtx STDIO_PROTO((FILE *));
+
+#if 0
+/* At present, don't prototype xrealloc, since all of the callers don't
+ cast their pointers to char *, and all of the xrealloc's don't use
+ void * yet. */
+extern char *xmalloc PROTO((size_t));
+extern char *xrealloc PROTO((void *, size_t));
+#else
+extern char *xmalloc ();
+extern char *xrealloc ();
+#endif
+
+extern char *oballoc PROTO((int));
+extern char *permalloc PROTO((int));
+extern void free PROTO((void *));
+extern rtx rtx_alloc PROTO((RTX_CODE));
+extern rtvec rtvec_alloc PROTO((int));
+extern rtx find_reg_note PROTO((rtx, enum reg_note, rtx));
+extern rtx find_regno_note PROTO((rtx, enum reg_note, int));
+extern int find_reg_fusage PROTO((rtx, enum rtx_code, rtx));
+extern int find_regno_fusage PROTO((rtx, enum rtx_code, int));
+extern HOST_WIDE_INT get_integer_term PROTO((rtx));
+extern rtx get_related_value PROTO((rtx));
+extern rtx single_set PROTO((rtx));
+extern rtx find_last_value PROTO((rtx, rtx *, rtx));
+extern rtx copy_rtx PROTO((rtx));
+extern rtx copy_rtx_if_shared PROTO((rtx));
+extern rtx copy_most_rtx PROTO((rtx, rtx));
+extern rtx replace_rtx PROTO((rtx, rtx, rtx));
+extern rtvec gen_rtvec_v PROTO((int, rtx *));
+extern rtx gen_reg_rtx PROTO((enum machine_mode));
+extern rtx gen_label_rtx PROTO((void));
+extern rtx gen_inline_header_rtx PROTO((rtx, rtx, int, int, int, int, int, int, rtx, int, int, rtvec, rtx));
+extern rtx gen_lowpart_common PROTO((enum machine_mode, rtx));
+extern rtx gen_lowpart PROTO((enum machine_mode, rtx));
+extern rtx gen_lowpart_if_possible PROTO((enum machine_mode, rtx));
+extern rtx gen_highpart PROTO((enum machine_mode, rtx));
+extern rtx gen_realpart PROTO((enum machine_mode, rtx));
+extern rtx gen_imagpart PROTO((enum machine_mode, rtx));
+extern rtx operand_subword PROTO((rtx, int, int, enum machine_mode));
+extern rtx operand_subword_force PROTO((rtx, int, enum machine_mode));
+extern int subreg_lowpart_p PROTO((rtx));
+extern rtx make_safe_from PROTO((rtx, rtx));
+extern rtx memory_address PROTO((enum machine_mode, rtx));
+extern rtx get_insns PROTO((void));
+extern rtx get_last_insn PROTO((void));
+extern rtx get_last_insn_anywhere PROTO((void));
+extern void start_sequence PROTO((void));
+extern void push_to_sequence PROTO((rtx));
+extern void end_sequence PROTO((void));
+extern rtx gen_sequence PROTO((void));
+extern rtx immed_double_const PROTO((HOST_WIDE_INT, HOST_WIDE_INT, enum machine_mode));
+extern rtx force_const_mem PROTO((enum machine_mode, rtx));
+extern rtx force_reg PROTO((enum machine_mode, rtx));
+extern rtx get_pool_constant PROTO((rtx));
+extern enum machine_mode get_pool_mode PROTO((rtx));
+extern int get_pool_offset PROTO((rtx));
+extern rtx simplify_subtraction PROTO((rtx));
+extern rtx assign_stack_local PROTO((enum machine_mode, int, int));
+extern rtx assign_stack_temp PROTO((enum machine_mode, int, int));
+extern rtx protect_from_queue PROTO((rtx, int));
+extern void emit_queue PROTO((void));
+extern rtx emit_move_insn PROTO((rtx, rtx));
+extern rtx emit_insn_before PROTO((rtx, rtx));
+extern rtx emit_jump_insn_before PROTO((rtx, rtx));
+extern rtx emit_call_insn_before PROTO((rtx, rtx));
+extern rtx emit_barrier_before PROTO((rtx));
+extern rtx emit_note_before PROTO((int, rtx));
+extern rtx emit_insn_after PROTO((rtx, rtx));
+extern rtx emit_jump_insn_after PROTO((rtx, rtx));
+extern rtx emit_barrier_after PROTO((rtx));
+extern rtx emit_label_after PROTO((rtx, rtx));
+extern rtx emit_note_after PROTO((int, rtx));
+extern rtx emit_line_note_after PROTO((char *, int, rtx));
+extern rtx emit_insn PROTO((rtx));
+extern rtx emit_insns PROTO((rtx));
+extern rtx emit_insns_before PROTO((rtx, rtx));
+extern rtx emit_jump_insn PROTO((rtx));
+extern rtx emit_call_insn PROTO((rtx));
+extern rtx emit_label PROTO((rtx));
+extern rtx emit_barrier PROTO((void));
+extern rtx emit_line_note PROTO((char *, int));
+extern rtx emit_note PROTO((char *, int));
+extern rtx emit_line_note_force PROTO((char *, int));
+extern rtx make_insn_raw PROTO((rtx));
+extern rtx previous_insn PROTO((rtx));
+extern rtx next_insn PROTO((rtx));
+extern rtx prev_nonnote_insn PROTO((rtx));
+extern rtx next_nonnote_insn PROTO((rtx));
+extern rtx prev_real_insn PROTO((rtx));
+extern rtx next_real_insn PROTO((rtx));
+extern rtx prev_active_insn PROTO((rtx));
+extern rtx next_active_insn PROTO((rtx));
+extern rtx prev_label PROTO((rtx));
+extern rtx next_label PROTO((rtx));
+extern rtx next_cc0_user PROTO((rtx));
+extern rtx prev_cc0_setter PROTO((rtx));
+extern rtx reg_set_last PROTO((rtx, rtx));
+extern rtx next_nondeleted_insn PROTO((rtx));
+extern enum rtx_code reverse_condition PROTO((enum rtx_code));
+extern enum rtx_code swap_condition PROTO((enum rtx_code));
+extern enum rtx_code unsigned_condition PROTO((enum rtx_code));
+extern enum rtx_code signed_condition PROTO((enum rtx_code));
+extern rtx find_equiv_reg PROTO((rtx, rtx, enum reg_class, int, short *, int, enum machine_mode));
+extern rtx squeeze_notes PROTO((rtx, rtx));
+extern rtx delete_insn PROTO((rtx));
+extern void delete_jump PROTO((rtx));
+extern rtx get_label_before PROTO((rtx));
+extern rtx get_label_after PROTO((rtx));
+extern rtx follow_jumps PROTO((rtx));
+extern rtx adj_offsettable_operand PROTO((rtx, int));
+extern rtx try_split PROTO((rtx, rtx, int));
+extern rtx split_insns PROTO((rtx, rtx));
+extern rtx simplify_unary_operation PROTO((enum rtx_code, enum machine_mode, rtx, enum machine_mode));
+extern rtx simplify_binary_operation PROTO((enum rtx_code, enum machine_mode, rtx, rtx));
+extern rtx simplify_ternary_operation PROTO((enum rtx_code, enum machine_mode, enum machine_mode, rtx, rtx, rtx));
+extern rtx simplify_relational_operation PROTO((enum rtx_code, enum machine_mode, rtx, rtx));
+extern rtx nonlocal_label_rtx_list PROTO((void));
+extern rtx gen_move_insn PROTO((rtx, rtx));
+extern rtx gen_jump PROTO((rtx));
+extern rtx gen_beq PROTO((rtx));
+extern rtx gen_bge PROTO((rtx));
+extern rtx gen_ble PROTO((rtx));
+extern rtx eliminate_constant_term PROTO((rtx, rtx *));
+extern rtx expand_complex_abs PROTO((enum machine_mode, rtx, rtx, int));
+extern enum machine_mode choose_hard_reg_mode PROTO((int, int));
+
+/* Maximum number of parallel sets and clobbers in any insn in this fn.
+ Always at least 3, since the combiner could put that many togetherm
+ and we want this to remain correct for all the remaining passes. */
+
+extern int max_parallel;
+
+extern int asm_noperands PROTO((rtx));
+extern char *decode_asm_operands PROTO((rtx, rtx *, rtx **, char **, enum machine_mode *));
+
+extern enum reg_class reg_preferred_class PROTO((int));
+extern enum reg_class reg_alternate_class PROTO((int));
+
+extern rtx get_first_nonparm_insn PROTO((void));
+
+/* Standard pieces of rtx, to be substituted directly into things. */
+extern rtx pc_rtx;
+extern rtx cc0_rtx;
+extern rtx const0_rtx;
+extern rtx const1_rtx;
+extern rtx const2_rtx;
+extern rtx constm1_rtx;
+extern rtx const_true_rtx;
+
+extern rtx const_tiny_rtx[3][(int) MAX_MACHINE_MODE];
+
+/* Returns a constant 0 rtx in mode MODE. Integer modes are treated the
+ same as VOIDmode. */
+
+#define CONST0_RTX(MODE) (const_tiny_rtx[0][(int) (MODE)])
+
+/* Likewise, for the constants 1 and 2. */
+
+#define CONST1_RTX(MODE) (const_tiny_rtx[1][(int) (MODE)])
+#define CONST2_RTX(MODE) (const_tiny_rtx[2][(int) (MODE)])
+
+/* All references to certain hard regs, except those created
+ by allocating pseudo regs into them (when that's possible),
+ go through these unique rtx objects. */
+extern rtx stack_pointer_rtx;
+extern rtx frame_pointer_rtx;
+extern rtx hard_frame_pointer_rtx;
+extern rtx arg_pointer_rtx;
+extern rtx pic_offset_table_rtx;
+extern rtx struct_value_rtx;
+extern rtx struct_value_incoming_rtx;
+extern rtx static_chain_rtx;
+extern rtx static_chain_incoming_rtx;
+
+/* If HARD_FRAME_POINTER_REGNUM is defined, then a special dummy reg
+ is used to represent the frame pointer. This is because the
+ hard frame pointer and the automatic variables are separated by an amount
+ that cannot be determined until after register allocation. We can assume
+ that in this case ELIMINABLE_REGS will be defined, one action of which
+ will be to eliminate FRAME_POINTER_REGNUM into HARD_FRAME_POINTER_REGNUM. */
+#ifndef HARD_FRAME_POINTER_REGNUM
+#define HARD_FRAME_POINTER_REGNUM FRAME_POINTER_REGNUM
+#endif
+
+/* Virtual registers are used during RTL generation to refer to locations into
+ the stack frame when the actual location isn't known until RTL generation
+ is complete. The routine instantiate_virtual_regs replaces these with
+ the proper value, which is normally {frame,arg,stack}_pointer_rtx plus
+ a constant. */
+
+#define FIRST_VIRTUAL_REGISTER (FIRST_PSEUDO_REGISTER)
+
+/* This points to the first word of the incoming arguments passed on the stack,
+ either by the caller or by the callee when pretending it was passed by the
+ caller. */
+
+extern rtx virtual_incoming_args_rtx;
+
+#define VIRTUAL_INCOMING_ARGS_REGNUM (FIRST_VIRTUAL_REGISTER)
+
+/* If FRAME_GROWS_DOWNWARD, this points to immediately above the first
+ variable on the stack. Otherwise, it points to the first variable on
+ the stack. */
+
+extern rtx virtual_stack_vars_rtx;
+
+#define VIRTUAL_STACK_VARS_REGNUM ((FIRST_VIRTUAL_REGISTER) + 1)
+
+/* This points to the location of dynamically-allocated memory on the stack
+ immediately after the stack pointer has been adjusted by the amount
+ desired. */
+
+extern rtx virtual_stack_dynamic_rtx;
+
+#define VIRTUAL_STACK_DYNAMIC_REGNUM ((FIRST_VIRTUAL_REGISTER) + 2)
+
+/* This points to the location in the stack at which outgoing arguments should
+ be written when the stack is pre-pushed (arguments pushed using push
+ insns always use sp). */
+
+extern rtx virtual_outgoing_args_rtx;
+
+#define VIRTUAL_OUTGOING_ARGS_REGNUM ((FIRST_VIRTUAL_REGISTER) + 3)
+
+#define LAST_VIRTUAL_REGISTER ((FIRST_VIRTUAL_REGISTER) + 3)
+
+extern rtx find_next_ref PROTO((rtx, rtx));
+extern rtx *find_single_use PROTO((rtx, rtx, rtx *));
+
+/* It is hard to write the prototype for expand_expr, since it needs
+ expr.h to be included for the enumeration. */
+
+extern rtx expand_expr ();
+
+extern rtx output_constant_def PROTO((union tree_node *));
+extern rtx immed_real_const PROTO((union tree_node *));
+extern union tree_node *make_tree PROTO((union tree_node *, rtx));
+
+/* Define a default value for STORE_FLAG_VALUE. */
+
+#ifndef STORE_FLAG_VALUE
+#define STORE_FLAG_VALUE 1
+#endif
+
+/* Nonzero after end of reload pass.
+ Set to 1 or 0 by toplev.c. */
+
+extern int reload_completed;
+
+/* Set to 1 while reload_as_needed is operating.
+ Required by some machines to handle any generated moves differently. */
+
+extern int reload_in_progress;
+
+/* If this is nonzero, we do not bother generating VOLATILE
+ around volatile memory references, and we are willing to
+ output indirect addresses. If cse is to follow, we reject
+ indirect addresses so a useful potential cse is generated;
+ if it is used only once, instruction combination will produce
+ the same indirect address eventually. */
+extern int cse_not_expected;
+
+/* Indexed by pseudo register number, gives the rtx for that pseudo.
+ Allocated in parallel with regno_pointer_flag. */
+extern rtx *regno_reg_rtx;
+
+/* Translates rtx code to tree code, for those codes needed by
+ REAL_ARITHMETIC. The function returns an int because the caller may not
+ know what `enum tree_code' means. */
+
+extern int rtx_to_tree_code PROTO((enum rtx_code));
diff --git a/gnu/usr.bin/cc/include/stack.h b/gnu/usr.bin/cc/include/stack.h
new file mode 100644
index 0000000..c5d9a25
--- /dev/null
+++ b/gnu/usr.bin/cc/include/stack.h
@@ -0,0 +1,41 @@
+/* stack.h - structed access to object stacks
+ Copyright (C) 1988 Free Software Foundation, Inc.
+ Contributed by Michael Tiemann (tiemann@cygnus.com).
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Summary: this file contains additional structures that layer
+ on top of obstacks for GNU C++. */
+
+/* Stack of data placed on obstacks. */
+
+struct stack_level
+{
+ /* Pointer back to previous such level. */
+ struct stack_level *prev;
+
+ /* Point to obstack we should return to. */
+ struct obstack *obstack;
+
+ /* First place we start putting data. */
+ tree *first;
+
+ /* Number of entries we can have from `first'.
+ Right now we are dumb: if we overflow, abort. */
+ int limit;
+};
+
+struct stack_level *push_stack_level PROTO((struct obstack *, char *, int));
+struct stack_level *pop_stack_level PROTO((struct stack_level *));
diff --git a/gnu/usr.bin/cc/include/tconfig.h b/gnu/usr.bin/cc/include/tconfig.h
new file mode 100644
index 0000000..7886724
--- /dev/null
+++ b/gnu/usr.bin/cc/include/tconfig.h
@@ -0,0 +1,42 @@
+/* Configuration for GNU C-compiler for Intel 80386.
+ Copyright (C) 1988, 1993 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef i386
+#define i386
+#endif
+
+/* #defines that need visibility everywhere. */
+#define FALSE 0
+#define TRUE 1
+
+/* This describes the machine the compiler is hosted on. */
+#define HOST_BITS_PER_CHAR 8
+#define HOST_BITS_PER_SHORT 16
+#define HOST_BITS_PER_INT 32
+#define HOST_BITS_PER_LONG 32
+#define HOST_BITS_PER_LONGLONG 64
+
+/* Arguments to use with `exit'. */
+#define SUCCESS_EXIT_CODE 0
+#define FATAL_EXIT_CODE 33
+
+/* target machine dependencies.
+ tm.h is a symbolic link to the actual target specific file. */
+
+#include "tm.h"
diff --git a/gnu/usr.bin/cc/include/tm.h b/gnu/usr.bin/cc/include/tm.h
new file mode 100644
index 0000000..5efd2e0
--- /dev/null
+++ b/gnu/usr.bin/cc/include/tm.h
@@ -0,0 +1,329 @@
+/* Definitions of target machine for GNU compiler for Intel 80386
+ running FreeBSD.
+ Copyright (C) 1988, 1992, 1994 Free Software Foundation, Inc.
+ Contributed by Poul-Henning Kamp <phk@login.dkuug.dk>
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This goes away when the math-emulator is fixed */
+#define TARGET_CPU_DEFAULT 0400 /* TARGET_NO_FANCY_MATH_387 */
+
+/* This is tested by i386gas.h. */
+#define YES_UNDERSCORES
+
+#include "i386/gstabs.h"
+
+/* Get perform_* macros to build libgcc.a. */
+#include "i386/perform.h"
+
+#undef CPP_PREDEFINES
+#define CPP_PREDEFINES "-Dunix -Di386 -D__FreeBSD__=2 -D__386BSD__ -Asystem(unix) -Asystem(FreeBSD) -Acpu(i386) -Amachine(i386)"
+
+#define INCLUDE_DEFAULTS { \
+ { "/usr/include", 0 }, \
+ { "/usr/include/g++", 1 }, \
+ { 0, 0} \
+ }
+
+#define ASM_SPEC " %| %{fpic:-k} %{fPIC:-k}"
+
+/* Like the default, except no -lg. */
+#define LIB_SPEC "%{!p:%{!pg:-lc}}%{p:-lc_p}%{pg:-lc_p}"
+
+#define LINK_SPEC \
+ "%{!nostdlib:%{!r*:%{!e*:-e start}}} -dc -dp %{static:-Bstatic} %{assert*} \
+ %{p:-Bstatic} %{pg:-Bstatic} %{Z}"
+
+#undef SIZE_TYPE
+#define SIZE_TYPE "unsigned int"
+
+#undef PTRDIFF_TYPE
+#define PTRDIFF_TYPE "int"
+
+#undef WCHAR_TYPE
+#define WCHAR_TYPE "int"
+
+#define WCHAR_UNSIGNED 0
+
+#undef WCHAR_TYPE_SIZE
+#define WCHAR_TYPE_SIZE BITS_PER_WORD
+
+#define HAVE_ATEXIT
+
+/* Tell final.c that we don't need a label passed to mcount. */
+
+#define NO_PROFILE_DATA
+
+/* Redefine this to not pass an unused label in %edx. */
+
+#undef FUNCTION_PROFILER
+#define FUNCTION_PROFILER(FILE, LABELNO) \
+{ \
+ if (flag_pic) \
+ fprintf (FILE, "\tcall *mcount@GOT(%%ebx)\n"); \
+ else \
+ fprintf (FILE, "\tcall mcount\n"); \
+}
+
+#if 0 /* not ready for this; it should be decided at compile time */
+#define FUNCTION_PROFILER_EPILOGUE(FILE) \
+{ \
+ if (flag_pic) \
+ fprintf (FILE, "\tcall *mexitcount@GOT(%%ebx)\n"); \
+ else \
+ fprintf (FILE, "\tcall mexitcount\n"); \
+}
+#endif
+
+/* There are conflicting reports about whether this system uses
+ a different assembler syntax. wilson@cygnus.com says # is right. */
+#undef COMMENT_BEGIN
+#define COMMENT_BEGIN "#"
+
+#undef ASM_APP_ON
+#define ASM_APP_ON "#APP\n"
+
+#undef ASM_APP_OFF
+#define ASM_APP_OFF "#NO_APP\n"
+
+/* The following macros are stolen from i386v4.h */
+/* These have to be defined to get PIC code correct */
+
+/* This is how to output an element of a case-vector that is relative.
+ This is only used for PIC code. See comments by the `casesi' insn in
+ i386.md for an explanation of the expression this outputs. */
+
+#undef ASM_OUTPUT_ADDR_DIFF_ELT
+#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, VALUE, REL) \
+ fprintf (FILE, "\t.long _GLOBAL_OFFSET_TABLE_+[.-%s%d]\n", LPREFIX, VALUE)
+
+/* Indicate that jump tables go in the text section. This is
+ necessary when compiling PIC code. */
+
+#define JUMP_TABLES_IN_TEXT_SECTION
+
+/* Don't default to pcc-struct-return, because gcc is the only compiler, and
+ we want to retain compatibility with older gcc versions. */
+#define DEFAULT_PCC_STRUCT_RETURN 0
+
+/*
+ * Some imports from svr4.h in support of shared libraries.
+ * Currently, we need the DECLARE_OBJECT_SIZE stuff.
+ */
+
+/* Define the strings used for the special svr4 .type and .size directives.
+ These strings generally do not vary from one system running svr4 to
+ another, but if a given system (e.g. m88k running svr) needs to use
+ different pseudo-op names for these, they may be overridden in the
+ file which includes this one. */
+
+#define TYPE_ASM_OP ".type"
+#define SIZE_ASM_OP ".size"
+#define WEAK_ASM_OP ".weak"
+
+/* The following macro defines the format used to output the second
+ operand of the .type assembler directive. Different svr4 assemblers
+ expect various different forms for this operand. The one given here
+ is just a default. You may need to override it in your machine-
+ specific tm.h file (depending upon the particulars of your assembler). */
+
+#define TYPE_OPERAND_FMT "@%s"
+
+/* Write the extra assembler code needed to declare a function's result.
+ Most svr4 assemblers don't require any special declaration of the
+ result value, but there are exceptions. */
+
+#ifndef ASM_DECLARE_RESULT
+#define ASM_DECLARE_RESULT(FILE, RESULT)
+#endif
+
+/* These macros generate the special .type and .size directives which
+ are used to set the corresponding fields of the linker symbol table
+ entries in an ELF object file under SVR4. These macros also output
+ the starting labels for the relevant functions/objects. */
+
+/* Write the extra assembler code needed to declare a function properly.
+ Some svr4 assemblers need to also have something extra said about the
+ function's return value. We allow for that here. */
+
+#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \
+ do { \
+ fprintf (FILE, "\t%s\t ", TYPE_ASM_OP); \
+ assemble_name (FILE, NAME); \
+ putc (',', FILE); \
+ fprintf (FILE, TYPE_OPERAND_FMT, "function"); \
+ putc ('\n', FILE); \
+ ASM_DECLARE_RESULT (FILE, DECL_RESULT (DECL)); \
+ ASM_OUTPUT_LABEL(FILE, NAME); \
+ } while (0)
+
+/* Write the extra assembler code needed to declare an object properly. */
+
+#define ASM_DECLARE_OBJECT_NAME(FILE, NAME, DECL) \
+ do { \
+ fprintf (FILE, "\t%s\t ", TYPE_ASM_OP); \
+ assemble_name (FILE, NAME); \
+ putc (',', FILE); \
+ fprintf (FILE, TYPE_OPERAND_FMT, "object"); \
+ putc ('\n', FILE); \
+ size_directive_output = 0; \
+ if (!flag_inhibit_size_directive && DECL_SIZE (DECL)) \
+ { \
+ size_directive_output = 1; \
+ fprintf (FILE, "\t%s\t ", SIZE_ASM_OP); \
+ assemble_name (FILE, NAME); \
+ fprintf (FILE, ",%d\n", int_size_in_bytes (TREE_TYPE (DECL))); \
+ } \
+ ASM_OUTPUT_LABEL(FILE, NAME); \
+ } while (0)
+
+/* Output the size directive for a decl in rest_of_decl_compilation
+ in the case where we did not do so before the initializer.
+ Once we find the error_mark_node, we know that the value of
+ size_directive_output was set
+ by ASM_DECLARE_OBJECT_NAME when it was run for the same decl. */
+
+#define ASM_FINISH_DECLARE_OBJECT(FILE, DECL, TOP_LEVEL, AT_END) \
+do { \
+ char *name = XSTR (XEXP (DECL_RTL (DECL), 0), 0); \
+ if (!flag_inhibit_size_directive && DECL_SIZE (DECL) \
+ && ! AT_END && TOP_LEVEL \
+ && DECL_INITIAL (DECL) == error_mark_node \
+ && !size_directive_output) \
+ { \
+ fprintf (FILE, "\t%s\t ", SIZE_ASM_OP); \
+ assemble_name (FILE, name); \
+ fprintf (FILE, ",%d\n", int_size_in_bytes (TREE_TYPE (DECL))); \
+ } \
+ } while (0)
+
+/* This is how to declare the size of a function. */
+
+#define ASM_DECLARE_FUNCTION_SIZE(FILE, FNAME, DECL) \
+ do { \
+ if (!flag_inhibit_size_directive) \
+ { \
+ char label[256]; \
+ static int labelno; \
+ labelno++; \
+ ASM_GENERATE_INTERNAL_LABEL (label, "Lfe", labelno); \
+ ASM_OUTPUT_INTERNAL_LABEL (FILE, "Lfe", labelno); \
+ fprintf (FILE, "\t%s\t ", SIZE_ASM_OP); \
+ assemble_name (FILE, (FNAME)); \
+ fprintf (FILE, ","); \
+ assemble_name (FILE, label); \
+ fprintf (FILE, "-"); \
+ assemble_name (FILE, (FNAME)); \
+ putc ('\n', FILE); \
+ } \
+ } while (0)
+
+/* This section copied from i386/osfrose.h */
+
+/* A C statement or compound statement to output to FILE some
+ assembler code to initialize basic-block profiling for the current
+ object module. This code should call the subroutine
+ `__bb_init_func' once per object module, passing it as its sole
+ argument the address of a block allocated in the object module.
+
+ The name of the block is a local symbol made with this statement:
+
+ ASM_GENERATE_INTERNAL_LABEL (BUFFER, "LPBX", 0);
+
+ Of course, since you are writing the definition of
+ `ASM_GENERATE_INTERNAL_LABEL' as well as that of this macro, you
+ can take a short cut in the definition of this macro and use the
+ name that you know will result.
+
+ The first word of this block is a flag which will be nonzero if the
+ object module has already been initialized. So test this word
+ first, and do not call `__bb_init_func' if the flag is nonzero. */
+
+#undef FUNCTION_BLOCK_PROFILER
+#define FUNCTION_BLOCK_PROFILER(STREAM, LABELNO) \
+do \
+ { \
+ if (!flag_pic) \
+ { \
+ fprintf (STREAM, "\tcmpl $0,%sPBX0\n", LPREFIX); \
+ fprintf (STREAM, "\tjne 0f\n"); \
+ fprintf (STREAM, "\tpushl $%sPBX0\n", LPREFIX); \
+ fprintf (STREAM, "\tcall ___bb_init_func\n"); \
+ fprintf (STREAM, "0:\n"); \
+ } \
+ else \
+ { \
+ fprintf (STREAM, "\tpushl %eax\n"); \
+ fprintf (STREAM, "\tmovl %sPBX0@GOT(%ebx),%eax\n"); \
+ fprintf (STREAM, "\tcmpl $0,(%eax)\n"); \
+ fprintf (STREAM, "\tjne 0f\n"); \
+ fprintf (STREAM, "\tpushl %eax\n"); \
+ fprintf (STREAM, "\tcall ___bb_init_func@PLT\n"); \
+ fprintf (STREAM, "0:\n"); \
+ fprintf (STREAM, "\tpopl %eax\n"); \
+ } \
+ } \
+while (0)
+
+/* A C statement or compound statement to increment the count
+ associated with the basic block number BLOCKNO. Basic blocks are
+ numbered separately from zero within each compilation. The count
+ associated with block number BLOCKNO is at index BLOCKNO in a
+ vector of words; the name of this array is a local symbol made
+ with this statement:
+
+ ASM_GENERATE_INTERNAL_LABEL (BUFFER, "LPBX", 2);
+
+ Of course, since you are writing the definition of
+ `ASM_GENERATE_INTERNAL_LABEL' as well as that of this macro, you
+ can take a short cut in the definition of this macro and use the
+ name that you know will result. */
+
+#undef BLOCK_PROFILER
+#define BLOCK_PROFILER(STREAM, BLOCKNO) \
+do \
+ { \
+ if (!flag_pic) \
+ fprintf (STREAM, "\tincl %sPBX2+%d\n", LPREFIX, (BLOCKNO)*4); \
+ else \
+ { \
+ fprintf (STREAM, "\tpushl %eax\n"); \
+ fprintf (STREAM, "\tmovl %sPBX2@GOT(%ebx),%eax\n", LPREFIX); \
+ fprintf (STREAM, "\tincl %d(%eax)\n", (BLOCKNO)*4); \
+ fprintf (STREAM, "\tpopl %eax\n"); \
+ } \
+ } \
+while (0)
+
+/* This is defined when gcc is compiled in the BSD-directory-tree, and must
+ * make up for the gap to all the stuff done in the GNU-makefiles.
+ */
+
+#ifdef FREEBSD_NATIVE
+
+#undef MD_EXEC_PREFIX
+#define MD_EXEC_PREFIX "/usr/libexec/"
+
+#undef STANDARD_STARTFILE_PREFIX
+#define STANDARD_STARTFILE_PREFIX "/usr/lib"
+
+#define DEFAULT_TARGET_MACHINE "i386-unknown-freebsd_1.0"
+#define GPLUSPLUS_INCLUDE_DIR "/usr/local/lib/gcc-lib/i386-unknown-freebsd_1.0/2.5.8/include"
+#define TOOL_INCLUDE_DIR "/usr/local/i386-unknown-freebsd_1.0/include"
+#define GCC_INCLUDE_DIR "/usr/local/lib/gcc-lib/i386-unknown-freebsd_1.0/2.5.8/include"
+
+#endif /* FREEBSD_NATIVE */
diff --git a/gnu/usr.bin/cc/include/tree.def b/gnu/usr.bin/cc/include/tree.def
new file mode 100644
index 0000000..71d6386
--- /dev/null
+++ b/gnu/usr.bin/cc/include/tree.def
@@ -0,0 +1,695 @@
+/* This file contains the definitions and documentation for the
+ tree codes used in the GNU C compiler.
+ Copyright (C) 1987, 1988, 1993 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* The third argument can be:
+ "x" for an exceptional code (fits no category).
+ "t" for a type object code.
+ "b" for a lexical block.
+ "c" for codes for constants.
+ "d" for codes for declarations (also serving as variable refs).
+ "r" for codes for references to storage.
+ "<" for codes for comparison expressions.
+ "1" for codes for unary arithmetic expressions.
+ "2" for codes for binary arithmetic expressions.
+ "s" for codes for expressions with inherent side effects.
+ "e" for codes for other kinds of expressions. */
+
+/* For `r', `e', `<', `1', `2', `s' and `x' nodes,
+ the 4th element is the number of argument slots to allocate.
+ This determines the size of the tree node object. */
+
+/* Any erroneous construct is parsed into a node of this type.
+ This type of node is accepted without complaint in all contexts
+ by later parsing activities, to avoid multiple error messages
+ for one error.
+ No fields in these nodes are used except the TREE_CODE. */
+DEFTREECODE (ERROR_MARK, "error_mark", "x", 0)
+
+/* Used to represent a name (such as, in the DECL_NAME of a decl node).
+ Internally it looks like a STRING_CST node.
+ There is only one IDENTIFIER_NODE ever made for any particular name.
+ Use `get_identifier' to get it (or create it, the first time). */
+DEFTREECODE (IDENTIFIER_NODE, "identifier_node", "x", -1)
+
+/* Used to hold information to identify an operator (or combination
+ of two operators) considered as a `noun' rather than a `verb'.
+ The first operand is encoded in the TREE_TYPE field. */
+DEFTREECODE (OP_IDENTIFIER, "op_identifier", "x", 2)
+
+/* Has the TREE_VALUE and TREE_PURPOSE fields. */
+/* These nodes are made into lists by chaining through the
+ TREE_CHAIN field. The elements of the list live in the
+ TREE_VALUE fields, while TREE_PURPOSE fields are occasionally
+ used as well to get the effect of Lisp association lists. */
+DEFTREECODE (TREE_LIST, "tree_list", "x", 2)
+
+/* These nodes contain an array of tree nodes. */
+DEFTREECODE (TREE_VEC, "tree_vec", "x", 2)
+
+/* A symbol binding block. These are arranged in a tree,
+ where the BLOCK_SUBBLOCKS field contains a chain of subblocks
+ chained through the BLOCK_CHAIN field.
+ BLOCK_SUPERCONTEXT points to the parent block.
+ For a block which represents the outermost scope of a function, it
+ points to the FUNCTION_DECL node.
+ BLOCK_VARS points to a chain of decl nodes.
+ BLOCK_TYPE_TAGS points to a chain of types which have their own names.
+ BLOCK_CHAIN points to the next BLOCK at the same level.
+ BLOCK_ABSTRACT_ORIGIN points to the original (abstract) tree node which
+ this block is an instance of, or else is NULL to indicate that this
+ block is not an instance of anything else. When non-NULL, the value
+ could either point to another BLOCK node or it could point to a
+ FUNCTION_DECL node (e.g. in the case of a block representing the
+ outermost scope of a particular inlining of a function).
+ BLOCK_ABSTRACT is non-zero if the block represents an abstract
+ instance of a block (i.e. one which is nested within an abstract
+ instance of a inline function. */
+DEFTREECODE (BLOCK, "block", "b", 0)
+
+/* Each data type is represented by a tree node whose code is one of
+ the following: */
+/* Each node that represents a data type has a component TYPE_SIZE
+ containing a tree that is an expression for the size in bits.
+ The TYPE_MODE contains the machine mode for values of this type.
+ The TYPE_POINTER_TO field contains a type for a pointer to this type,
+ or zero if no such has been created yet.
+ The TYPE_NEXT_VARIANT field is used to chain together types
+ that are variants made by type modifiers such as "const" and "volatile".
+ The TYPE_MAIN_VARIANT field, in any member of such a chain,
+ points to the start of the chain.
+ The TYPE_NONCOPIED_PARTS field is a list specifying which parts
+ of an object of this type should *not* be copied by assignment.
+ The TREE_PURPOSE of each element is the offset of the part
+ and the TREE_VALUE is the size in bits of the part.
+ The TYPE_NAME field contains info on the name used in the program
+ for this type (for GDB symbol table output). It is either a
+ TYPE_DECL node, for types that are typedefs, or an IDENTIFIER_NODE
+ in the case of structs, unions or enums that are known with a tag,
+ or zero for types that have no special name.
+ The TYPE_CONTEXT for any sort of type which could have a name or
+ which could have named members (e.g. tagged types in C/C++) will
+ point to the node which represents the scope of the given type, or
+ will be NULL_TREE if the type has "file scope". For most types, this
+ will point to a BLOCK node or a FUNCTION_DECL node, but it could also
+ point to a FUNCTION_TYPE node (for types whose scope is limited to the
+ formal parameter list of some function type specification) or it
+ could point to a RECORD_TYPE, UNION_TYPE or QUAL_UNION_TYPE node
+ (for C++ "member" types).
+ For non-tagged-types, TYPE_CONTEXT need not be set to anything in
+ particular, since any type which is of some type category (e.g.
+ an array type or a function type) which cannot either have a name
+ itself or have named members doesn't really have a "scope" per se.
+ The TREE_CHAIN field is used as a forward-references to names for
+ ENUMERAL_TYPE, RECORD_TYPE, UNION_TYPE, and QUAL_UNION_TYPE nodes;
+ see below. */
+
+DEFTREECODE (VOID_TYPE, "void_type", "t", 0) /* The void type in C */
+
+/* Integer types in all languages, including char in C.
+ Also used for sub-ranges of other discrete types.
+ Has components TYPE_MIN_VALUE, TYPE_MAX_VALUE (expressions, inclusive)
+ and TYPE_PRECISION (number of bits used by this type).
+ In the case of a subrange type in Pascal, the TREE_TYPE
+ of this will point at the supertype (another INTEGER_TYPE,
+ or an ENUMERAL_TYPE, CHAR_TYPE, or BOOLEAN_TYPE).
+ Otherwise, the TREE_TYPE is zero. */
+DEFTREECODE (INTEGER_TYPE, "integer_type", "t", 0)
+
+/* C's float and double. Different floating types are distinguished
+ by machine mode and by the TYPE_SIZE and the TYPE_PRECISION. */
+DEFTREECODE (REAL_TYPE, "real_type", "t", 0)
+
+/* Complex number types. The TREE_TYPE field is the data type
+ of the real and imaginary parts. */
+DEFTREECODE (COMPLEX_TYPE, "complex_type", "t", 0)
+
+/* C enums. The type node looks just like an INTEGER_TYPE node.
+ The symbols for the values of the enum type are defined by
+ CONST_DECL nodes, but the type does not point to them;
+ however, the TYPE_VALUES is a list in which each element's TREE_PURPOSE
+ is a name and the TREE_VALUE is the value (an INTEGER_CST node). */
+/* A forward reference `enum foo' when no enum named foo is defined yet
+ has zero (a null pointer) in its TYPE_SIZE. The tag name is in
+ the TYPE_NAME field. If the type is later defined, the normal
+ fields are filled in.
+ RECORD_TYPE, UNION_TYPE, and QUAL_UNION_TYPE forward refs are
+ treated similarly. */
+DEFTREECODE (ENUMERAL_TYPE, "enumeral_type", "t", 0)
+
+/* Pascal's boolean type (true or false are the only values);
+ no special fields needed. */
+DEFTREECODE (BOOLEAN_TYPE, "boolean_type", "t", 0)
+
+/* CHAR in Pascal; not used in C.
+ No special fields needed. */
+DEFTREECODE (CHAR_TYPE, "char_type", "t", 0)
+
+/* All pointer-to-x types have code POINTER_TYPE.
+ The TREE_TYPE points to the node for the type pointed to. */
+DEFTREECODE (POINTER_TYPE, "pointer_type", "t", 0)
+
+/* An offset is a pointer relative to an object.
+ The TREE_TYPE field is the type of the object at the offset.
+ The TYPE_OFFSET_BASETYPE points to the node for the type of object
+ that the offset is relative to. */
+DEFTREECODE (OFFSET_TYPE, "offset_type", "t", 0)
+
+/* A reference is like a pointer except that it is coerced
+ automatically to the value it points to. Used in C++. */
+DEFTREECODE (REFERENCE_TYPE, "reference_type", "t", 0)
+
+/* METHOD_TYPE is the type of a function which takes an extra first
+ argument for "self", which is not present in the declared argument list.
+ The TREE_TYPE is the return type of the method. The TYPE_METHOD_BASETYPE
+ is the type of "self". TYPE_ARG_TYPES is the real argument list, which
+ includes the hidden argument for "self". */
+DEFTREECODE (METHOD_TYPE, "method_type", "t", 0)
+
+/* Used for Pascal; details not determined right now. */
+DEFTREECODE (FILE_TYPE, "file_type", "t", 0)
+
+/* Types of arrays. Special fields:
+ TREE_TYPE Type of an array element.
+ TYPE_DOMAIN Type to index by.
+ Its range of values specifies the array length.
+ TYPE_SEP Expression for units from one elt to the next.
+ TYPE_SEP_UNIT Number of bits in a unit for previous.
+ The field TYPE_POINTER_TO (TREE_TYPE (array_type)) is always nonzero
+ and holds the type to coerce a value of that array type to in C.
+ TYPE_STRING_FLAG indicates a string (in contrast to an array of chars)
+ in languages (such as Chill) that make a distinction. */
+/* Array types in C or Pascal */
+DEFTREECODE (ARRAY_TYPE, "array_type", "t", 0)
+
+/* Types of sets for Pascal. Special fields are the same as
+ in an array type. The target type is always a boolean type. */
+DEFTREECODE (SET_TYPE, "set_type", "t", 0)
+
+/* Struct in C, or record in Pascal. */
+/* Special fields:
+ TYPE_FIELDS chain of FIELD_DECLs for the fields of the struct.
+ A few may need to be added for Pascal. */
+/* See the comment above, before ENUMERAL_TYPE, for how
+ forward references to struct tags are handled in C. */
+DEFTREECODE (RECORD_TYPE, "record_type", "t", 0)
+
+/* Union in C. Like a struct, except that the offsets of the fields
+ will all be zero. */
+/* See the comment above, before ENUMERAL_TYPE, for how
+ forward references to union tags are handled in C. */
+DEFTREECODE (UNION_TYPE, "union_type", "t", 0) /* C union type */
+
+/* Similar to UNION_TYPE, except that the expressions in DECL_QUALIFIER
+ in each FIELD_DECL determine what the union contains. The first
+ field whose DECL_QUALIFIER expression is true is deemed to occupy
+ the union. */
+DEFTREECODE (QUAL_UNION_TYPE, "qual_union_type", "t", 0)
+
+/* Type of functions. Special fields:
+ TREE_TYPE type of value returned.
+ TYPE_ARG_TYPES list of types of arguments expected.
+ this list is made of TREE_LIST nodes.
+ Types of "Procedures" in languages where they are different from functions
+ have code FUNCTION_TYPE also, but then TREE_TYPE is zero or void type. */
+DEFTREECODE (FUNCTION_TYPE, "function_type", "t", 0)
+
+/* This is a language-specific kind of type.
+ Its meaning is defined by the language front end.
+ layout_type does not know how to lay this out,
+ so the front-end must do so manually. */
+DEFTREECODE (LANG_TYPE, "lang_type", "t", 0)
+
+/* Expressions */
+
+/* First, the constants. */
+
+/* Contents are in TREE_INT_CST_LOW and TREE_INT_CST_HIGH fields,
+ 32 bits each, giving us a 64 bit constant capability.
+ Note: constants of type char in Pascal are INTEGER_CST,
+ and so are pointer constants such as nil in Pascal or NULL in C.
+ `(int *) 1' in C also results in an INTEGER_CST. */
+DEFTREECODE (INTEGER_CST, "integer_cst", "c", 2)
+
+/* Contents are in TREE_REAL_CST field. Also there is TREE_CST_RTL. */
+DEFTREECODE (REAL_CST, "real_cst", "c", 3)
+
+/* Contents are in TREE_REALPART and TREE_IMAGPART fields,
+ whose contents are other constant nodes.
+ Also there is TREE_CST_RTL. */
+DEFTREECODE (COMPLEX_CST, "complex_cst", "c", 3)
+
+/* Contents are TREE_STRING_LENGTH and TREE_STRING_POINTER fields.
+ Also there is TREE_CST_RTL. */
+DEFTREECODE (STRING_CST, "string_cst", "c", 3)
+
+/* Declarations. All references to names are represented as ..._DECL nodes.
+ The decls in one binding context are chained through the TREE_CHAIN field.
+ Each DECL has a DECL_NAME field which contains an IDENTIFIER_NODE.
+ (Some decls, most often labels, may have zero as the DECL_NAME).
+ DECL_CONTEXT points to the node representing the context in which
+ this declaration has its scope. For FIELD_DECLs, this is the
+ RECORD_TYPE, UNION_TYPE, or QUAL_UNION_TYPE node that the field
+ is a member of. For VAR_DECL, PARM_DECL, FUNCTION_DECL, LABEL_DECL,
+ and CONST_DECL nodes, this points to the FUNCTION_DECL for the
+ containing function, or else yields NULL_TREE if the given decl
+ has "file scope".
+ DECL_ABSTRACT_ORIGIN, if non-NULL, points to the original (abstract)
+ ..._DECL node of which this decl is an (inlined or template expanded)
+ instance.
+ The TREE_TYPE field holds the data type of the object, when relevant.
+ LABEL_DECLs have no data type. For TYPE_DECL, the TREE_TYPE field
+ contents are the type whose name is being declared.
+ The DECL_ALIGN, DECL_SIZE,
+ and DECL_MODE fields exist in decl nodes just as in type nodes.
+ They are unused in LABEL_DECL, TYPE_DECL and CONST_DECL nodes.
+
+ DECL_OFFSET holds an integer number of bits offset for the location.
+ DECL_VOFFSET holds an expression for a variable offset; it is
+ to be multiplied by DECL_VOFFSET_UNIT (an integer).
+ These fields are relevant only in FIELD_DECLs and PARM_DECLs.
+
+ DECL_INITIAL holds the value to initialize a variable to,
+ or the value of a constant. For a function, it holds the body
+ (a node of type BLOCK representing the function's binding contour
+ and whose body contains the function's statements.) For a LABEL_DECL
+ in C, it is a flag, nonzero if the label's definition has been seen.
+
+ PARM_DECLs use a special field:
+ DECL_ARG_TYPE is the type in which the argument is actually
+ passed, which may be different from its type within the function.
+
+ FUNCTION_DECLs use four special fields:
+ DECL_ARGUMENTS holds a chain of PARM_DECL nodes for the arguments.
+ DECL_RESULT holds a RESULT_DECL node for the value of a function,
+ or it is 0 for a function that returns no value.
+ (C functions returning void have zero here.)
+ DECL_RESULT_TYPE holds the type in which the result is actually
+ returned. This is usually the same as the type of DECL_RESULT,
+ but (1) it may be a wider integer type and
+ (2) it remains valid, for the sake of inlining, even after the
+ function's compilation is done.
+ DECL_FUNCTION_CODE is a code number that is nonzero for
+ built-in functions. Its value is an enum built_in_function
+ that says which built-in function it is.
+
+ DECL_SOURCE_FILE holds a filename string and DECL_SOURCE_LINE
+ holds a line number. In some cases these can be the location of
+ a reference, if no definition has been seen.
+
+ DECL_ABSTRACT is non-zero if the decl represents an abstract instance
+ of a decl (i.e. one which is nested within an abstract instance of a
+ inline function. */
+
+DEFTREECODE (FUNCTION_DECL, "function_decl", "d", 0)
+DEFTREECODE (LABEL_DECL, "label_decl", "d", 0)
+DEFTREECODE (CONST_DECL, "const_decl", "d", 0)
+DEFTREECODE (TYPE_DECL, "type_decl", "d", 0)
+DEFTREECODE (VAR_DECL, "var_decl", "d", 0)
+DEFTREECODE (PARM_DECL, "parm_decl", "d", 0)
+DEFTREECODE (RESULT_DECL, "result_decl", "d", 0)
+DEFTREECODE (FIELD_DECL, "field_decl", "d", 0)
+
+/* References to storage. */
+
+/* Value is structure or union component.
+ Operand 0 is the structure or union (an expression);
+ operand 1 is the field (a node of type FIELD_DECL). */
+DEFTREECODE (COMPONENT_REF, "component_ref", "r", 2)
+
+/* Reference to a group of bits within an object. Similar to COMPONENT_REF
+ except the position is given explicitly rather than via a FIELD_DECL.
+ Operand 0 is the structure or union expression;
+ operand 1 is a tree giving the number of bits being referenced;
+ operand 2 is a tree giving the position of the first referenced bit.
+ The field can be either a signed or unsigned field;
+ TREE_UNSIGNED says which. */
+DEFTREECODE (BIT_FIELD_REF, "bit_field_ref", "r", 3)
+
+/* C unary `*' or Pascal `^'. One operand, an expression for a pointer. */
+DEFTREECODE (INDIRECT_REF, "indirect_ref", "r", 1)
+
+/* Reference to the contents of an offset
+ (a value whose type is an OFFSET_TYPE).
+ Operand 0 is the object within which the offset is taken.
+ Operand 1 is the offset. */
+DEFTREECODE (OFFSET_REF, "offset_ref", "r", 2)
+
+/* Pascal `^` on a file. One operand, an expression for the file. */
+DEFTREECODE (BUFFER_REF, "buffer_ref", "r", 1)
+
+/* Array indexing in languages other than C.
+ Operand 0 is the array; operand 1 is a list of indices
+ stored as a chain of TREE_LIST nodes. */
+DEFTREECODE (ARRAY_REF, "array_ref", "r", 2)
+
+/* Constructor: return an aggregate value made from specified components.
+ In C, this is used only for structure and array initializers.
+ The first "operand" is really a pointer to the RTL,
+ for constant constructors only.
+ The second operand is a list of component values
+ made out of a chain of TREE_LIST nodes. */
+DEFTREECODE (CONSTRUCTOR, "constructor", "e", 2)
+
+/* The expression types are mostly straightforward,
+ with the fourth argument of DEFTREECODE saying
+ how many operands there are.
+ Unless otherwise specified, the operands are expressions. */
+
+/* Contains two expressions to compute, one followed by the other.
+ the first value is ignored. The second one's value is used. */
+DEFTREECODE (COMPOUND_EXPR, "compound_expr", "e", 2)
+
+/* Assignment expression. Operand 0 is the what to set; 1, the new value. */
+DEFTREECODE (MODIFY_EXPR, "modify_expr", "e", 2)
+
+/* Initialization expression. Operand 0 is the variable to initialize;
+ Operand 1 is the initializer. */
+DEFTREECODE (INIT_EXPR, "init_expr", "e", 2)
+
+/* For TARGET_EXPR, operand 0 is the target of an initialization,
+ operand 1 is the initializer for the target,
+ and operand 2 is the cleanup for this node, if any. */
+DEFTREECODE (TARGET_EXPR, "target_expr", "e", 3)
+
+/* Conditional expression ( ... ? ... : ... in C).
+ Operand 0 is the condition.
+ Operand 1 is the then-value.
+ Operand 2 is the else-value. */
+DEFTREECODE (COND_EXPR, "cond_expr", "e", 3)
+
+/* Declare local variables, including making RTL and allocating space.
+ Operand 0 is a chain of VAR_DECL nodes for the variables.
+ Operand 1 is the body, the expression to be computed using
+ the variables. The value of operand 1 becomes that of the BIND_EXPR.
+ Operand 2 is the BLOCK that corresponds to these bindings
+ for debugging purposes. If this BIND_EXPR is actually expanded,
+ that sets the TREE_USED flag in the BLOCK.
+
+ The BIND_EXPR is not responsible for informing parsers
+ about these variables. If the body is coming from the input file,
+ then the code that creates the BIND_EXPR is also responsible for
+ informing the parser of the variables.
+
+ If the BIND_EXPR is ever expanded, its TREE_USED flag is set.
+ This tells the code for debugging symbol tables not to ignore the BIND_EXPR.
+ If the BIND_EXPR should be output for debugging but will not be expanded,
+ set the TREE_USED flag by hand.
+
+ In order for the BIND_EXPR to be known at all, the code that creates it
+ must also install it as a subblock in the tree of BLOCK
+ nodes for the function. */
+DEFTREECODE (BIND_EXPR, "bind_expr", "e", 3)
+
+/* Function call. Operand 0 is the function.
+ Operand 1 is the argument list, a list of expressions
+ made out of a chain of TREE_LIST nodes.
+ There is no operand 2. That slot is used for the
+ CALL_EXPR_RTL macro (see preexpand_calls). */
+DEFTREECODE (CALL_EXPR, "call_expr", "e", 3)
+
+/* Call a method. Operand 0 is the method, whose type is a METHOD_TYPE.
+ Operand 1 is the expression for "self".
+ Operand 2 is the list of explicit arguments. */
+DEFTREECODE (METHOD_CALL_EXPR, "method_call_expr", "e", 4)
+
+/* Specify a value to compute along with its corresponding cleanup.
+ Operand 0 argument is an expression whose value needs a cleanup.
+ Operand 1 is an RTL_EXPR which will eventually represent that value.
+ Operand 2 is the cleanup expression for the object.
+ The RTL_EXPR is used in this expression, which is how the expression
+ manages to act on the proper value.
+ The cleanup is executed by the first enclosing CLEANUP_POINT_EXPR, if
+ it exists, otherwise it is the responsibility of the caller to manually
+ call expand_cleanups_to, as needed. */
+DEFTREECODE (WITH_CLEANUP_EXPR, "with_cleanup_expr", "e", 3)
+
+/* Specify a cleanup point.
+ Operand 0 is the expression that has cleanups that we want ensure are
+ cleaned up. */
+DEFTREECODE (CLEANUP_POINT_EXPR, "cleanup_point_expr", "e", 1)
+
+/* The following two codes are used in languages that have types where
+ the position and/or sizes of fields vary from object to object of the
+ same type, i.e., where some other field in the object contains a value
+ that is used in the computation of another field's offset or size.
+
+ For example, a record type with a discriminant in Ada is such a type.
+ This mechanism is also used to create "fat pointers" for unconstrained
+ array types in Ada; the fat pointer is a structure one of whose fields is
+ a pointer to the actual array type and the other field is a pointer to a
+ template, which is a structure containing the bounds of the array. The
+ bounds in the type pointed to by the first field in the fat pointer refer
+ to the values in the template.
+
+ These "self-references" are doing using a PLACEHOLDER_EXPR. This is a
+ node that will later be replaced with the object being referenced. Its type
+ is that of the object and selects which object to use from a chain of
+ references (see below).
+
+ When we wish to evaluate a size or offset, we check it is contains a
+ placeholder. If it does, we construct a WITH_RECORD_EXPR that contains
+ both the expression we wish to evaluate and an expression within which the
+ object may be found. The latter expression is the object itself in
+ the simple case of an Ada record with discriminant, but it can be the
+ array in the case of an unconstrained array.
+
+ In the latter case, we need the fat pointer, because the bounds of the
+ array can only be accessed from it. However, we rely here on the fact that
+ the expression for the array contains the dereference of the fat pointer
+ that obtained the array pointer.
+
+ Accordingly, when looking for the object to substitute in place of
+ a PLACEHOLDER_EXPR, we look down the first operand of the expression
+ passed as the second operand to WITH_RECORD_EXPR until we find something
+ of the desired type or reach a constant. */
+
+/* Denotes a record to later be supplied with a WITH_RECORD_EXPR when
+ evaluating this expression. The type of this expression is used to
+ find the record to replace it. */
+DEFTREECODE (PLACEHOLDER_EXPR, "placeholder_expr", "x", 0)
+
+/* Provide an expression that references a record to be used in place
+ of a PLACEHOLDER_EXPR. The record to be used is the record within
+ operand 1 that has the same type as the PLACEHOLDER_EXPR in
+ operand 0. */
+DEFTREECODE (WITH_RECORD_EXPR, "with_record_expr", "e", 2)
+
+/* Simple arithmetic. Operands must have the same machine mode
+ and the value shares that mode. */
+DEFTREECODE (PLUS_EXPR, "plus_expr", "2", 2)
+DEFTREECODE (MINUS_EXPR, "minus_expr", "2", 2)
+DEFTREECODE (MULT_EXPR, "mult_expr", "2", 2)
+
+/* Division for integer result that rounds the quotient toward zero. */
+/* Operands must have the same machine mode.
+ In principle they may be real, but that is not currently supported.
+ The result is always fixed point, and it has the same type as the
+ operands if they are fixed point. */
+DEFTREECODE (TRUNC_DIV_EXPR, "trunc_div_expr", "2", 2)
+
+/* Division for integer result that rounds the quotient toward infinity. */
+DEFTREECODE (CEIL_DIV_EXPR, "ceil_div_expr", "2", 2)
+
+/* Division for integer result that rounds toward minus infinity. */
+DEFTREECODE (FLOOR_DIV_EXPR, "floor_div_expr", "2", 2)
+
+/* Division for integer result that rounds toward nearest integer. */
+DEFTREECODE (ROUND_DIV_EXPR, "round_div_expr", "2", 2)
+
+/* Four kinds of remainder that go with the four kinds of division. */
+DEFTREECODE (TRUNC_MOD_EXPR, "trunc_mod_expr", "2", 2)
+DEFTREECODE (CEIL_MOD_EXPR, "ceil_mod_expr", "2", 2)
+DEFTREECODE (FLOOR_MOD_EXPR, "floor_mod_expr", "2", 2)
+DEFTREECODE (ROUND_MOD_EXPR, "round_mod_expr", "2", 2)
+
+/* Division for real result. The two operands must have the same type.
+ In principle they could be integers, but currently only real
+ operands are supported. The result must have the same type
+ as the operands. */
+DEFTREECODE (RDIV_EXPR, "rdiv_expr", "2", 2)
+
+/* Division which is not supposed to need rounding.
+ Used for pointer subtraction in C. */
+DEFTREECODE (EXACT_DIV_EXPR, "exact_div_expr", "2", 2)
+
+/* Conversion of real to fixed point: four ways to round,
+ like the four ways to divide.
+ CONVERT_EXPR can also be used to convert a real to an integer,
+ and that is what is used in languages that do not have ways of
+ specifying which of these is wanted. Maybe these are not needed. */
+DEFTREECODE (FIX_TRUNC_EXPR, "fix_trunc_expr", "1", 1)
+DEFTREECODE (FIX_CEIL_EXPR, "fix_ceil_expr", "1", 1)
+DEFTREECODE (FIX_FLOOR_EXPR, "fix_floor_expr", "1", 1)
+DEFTREECODE (FIX_ROUND_EXPR, "fix_round_expr", "1", 1)
+
+/* Conversion of an integer to a real. */
+DEFTREECODE (FLOAT_EXPR, "float_expr", "1", 1)
+
+/* Exponentiation. Operands may have any types;
+ constraints on value type are not known yet. */
+DEFTREECODE (EXPON_EXPR, "expon_expr", "2", 2)
+
+/* Unary negation. Value has same type as operand. */
+DEFTREECODE (NEGATE_EXPR, "negate_expr", "1", 1)
+
+DEFTREECODE (MIN_EXPR, "min_expr", "2", 2)
+DEFTREECODE (MAX_EXPR, "max_expr", "2", 2)
+DEFTREECODE (ABS_EXPR, "abs_expr", "1", 1)
+DEFTREECODE (FFS_EXPR, "ffs_expr", "1", 1)
+
+/* Shift operations for shift and rotate.
+ Shift is supposed to mean logical shift if done on an
+ unsigned type, arithmetic shift on a signed type.
+ The second operand is the number of bits to
+ shift by, and must always have mode SImode.
+ The result has the same mode as the first operand. */
+DEFTREECODE (LSHIFT_EXPR, "alshift_expr", "2", 2)
+DEFTREECODE (RSHIFT_EXPR, "arshift_expr", "2", 2)
+DEFTREECODE (LROTATE_EXPR, "lrotate_expr", "2", 2)
+DEFTREECODE (RROTATE_EXPR, "rrotate_expr", "2", 2)
+
+/* Bitwise operations. Operands have same mode as result. */
+DEFTREECODE (BIT_IOR_EXPR, "bit_ior_expr", "2", 2)
+DEFTREECODE (BIT_XOR_EXPR, "bit_xor_expr", "2", 2)
+DEFTREECODE (BIT_AND_EXPR, "bit_and_expr", "2", 2)
+DEFTREECODE (BIT_ANDTC_EXPR, "bit_andtc_expr", "2", 2)
+DEFTREECODE (BIT_NOT_EXPR, "bit_not_expr", "1", 1)
+
+/* Combination of boolean values or of integers considered only
+ as zero or nonzero. ANDIF and ORIF allow the second operand
+ not to be computed if the value of the expression is determined
+ from the first operand. AND, OR, and XOR always compute the second
+ operand whether its value is needed or not (for side effects). */
+DEFTREECODE (TRUTH_ANDIF_EXPR, "truth_andif_expr", "e", 2)
+DEFTREECODE (TRUTH_ORIF_EXPR, "truth_orif_expr", "e", 2)
+DEFTREECODE (TRUTH_AND_EXPR, "truth_and_expr", "e", 2)
+DEFTREECODE (TRUTH_OR_EXPR, "truth_or_expr", "e", 2)
+DEFTREECODE (TRUTH_XOR_EXPR, "truth_xor_expr", "e", 2)
+DEFTREECODE (TRUTH_NOT_EXPR, "truth_not_expr", "e", 1)
+
+/* Relational operators.
+ `EQ_EXPR' and `NE_EXPR' are allowed for any types.
+ The others are allowed only for integer (or pointer or enumeral)
+ or real types.
+ In all cases the operands will have the same type,
+ and the value is always the type used by the language for booleans. */
+DEFTREECODE (LT_EXPR, "lt_expr", "<", 2)
+DEFTREECODE (LE_EXPR, "le_expr", "<", 2)
+DEFTREECODE (GT_EXPR, "gt_expr", "<", 2)
+DEFTREECODE (GE_EXPR, "ge_expr", "<", 2)
+DEFTREECODE (EQ_EXPR, "eq_expr", "<", 2)
+DEFTREECODE (NE_EXPR, "ne_expr", "<", 2)
+
+/* Operations for Pascal sets. Not used now. */
+DEFTREECODE (IN_EXPR, "in_expr", "2", 2)
+DEFTREECODE (SET_LE_EXPR, "set_le_expr", "<", 2)
+DEFTREECODE (CARD_EXPR, "card_expr", "1", 1)
+DEFTREECODE (RANGE_EXPR, "range_expr", "2", 2)
+
+/* Represents a conversion of type of a value.
+ All conversions, including implicit ones, must be
+ represented by CONVERT_EXPR nodes. */
+DEFTREECODE (CONVERT_EXPR, "convert_expr", "1", 1)
+
+/* Represents a conversion expected to require no code to be generated. */
+DEFTREECODE (NOP_EXPR, "nop_expr", "1", 1)
+
+/* Value is same as argument, but guaranteed not an lvalue. */
+DEFTREECODE (NON_LVALUE_EXPR, "non_lvalue_expr", "1", 1)
+
+/* Represents something we computed once and will use multiple times.
+ First operand is that expression. Second is the function decl
+ in which the SAVE_EXPR was created. The third operand is the RTL,
+ nonzero only after the expression has been computed. */
+DEFTREECODE (SAVE_EXPR, "save_expr", "e", 3)
+
+/* Represents something whose RTL has already been expanded
+ as a sequence which should be emitted when this expression is expanded.
+ The first operand is the RTL to emit. It is the first of a chain of insns.
+ The second is the RTL expression for the result. */
+DEFTREECODE (RTL_EXPR, "rtl_expr", "e", 2)
+
+/* & in C. Value is the address at which the operand's value resides.
+ Operand may have any mode. Result mode is Pmode. */
+DEFTREECODE (ADDR_EXPR, "addr_expr", "e", 1)
+
+/* Non-lvalue reference or pointer to an object. */
+DEFTREECODE (REFERENCE_EXPR, "reference_expr", "e", 1)
+
+/* Operand is a function constant; result is a function variable value
+ of typeEPmode. Used only for languages that need static chains. */
+DEFTREECODE (ENTRY_VALUE_EXPR, "entry_value_expr", "e", 1)
+
+/* Given two real or integer operands of the same type,
+ returns a complex value of the corresponding complex type. */
+DEFTREECODE (COMPLEX_EXPR, "complex_expr", "2", 2)
+
+/* Complex conjugate of operand. Used only on complex types.
+ The value has the same type as the operand. */
+DEFTREECODE (CONJ_EXPR, "conj_expr", "1", 1)
+
+/* Used only on an operand of complex type, these return
+ a value of the corresponding component type. */
+DEFTREECODE (REALPART_EXPR, "realpart_expr", "1", 1)
+DEFTREECODE (IMAGPART_EXPR, "imagpart_expr", "1", 1)
+
+/* Nodes for ++ and -- in C.
+ The second arg is how much to increment or decrement by.
+ For a pointer, it would be the size of the object pointed to. */
+DEFTREECODE (PREDECREMENT_EXPR, "predecrement_expr", "e", 2)
+DEFTREECODE (PREINCREMENT_EXPR, "preincrement_expr", "e", 2)
+DEFTREECODE (POSTDECREMENT_EXPR, "postdecrement_expr", "e", 2)
+DEFTREECODE (POSTINCREMENT_EXPR, "postincrement_expr", "e", 2)
+
+/* These types of expressions have no useful value,
+ and always have side effects. */
+
+/* A label definition, encapsulated as a statement.
+ Operand 0 is the LABEL_DECL node for the label that appears here.
+ The type should be void and the value should be ignored. */
+DEFTREECODE (LABEL_EXPR, "label_expr", "s", 1)
+
+/* GOTO. Operand 0 is a LABEL_DECL node.
+ The type should be void and the value should be ignored. */
+DEFTREECODE (GOTO_EXPR, "goto_expr", "s", 1)
+
+/* RETURN. Evaluates operand 0, then returns from the current function.
+ Presumably that operand is an assignment that stores into the
+ RESULT_DECL that hold the value to be returned.
+ The operand may be null.
+ The type should be void and the value should be ignored. */
+DEFTREECODE (RETURN_EXPR, "return_expr", "s", 1)
+
+/* Exit the inner most loop conditionally. Operand 0 is the condition.
+ The type should be void and the value should be ignored. */
+DEFTREECODE (EXIT_EXPR, "exit_expr", "s", 1)
+
+/* A loop. Operand 0 is the body of the loop.
+ It must contain an EXIT_EXPR or is an infinite loop.
+ The type should be void and the value should be ignored. */
+DEFTREECODE (LOOP_EXPR, "loop_expr", "s", 1)
+
+/*
+Local variables:
+mode:c
+version-control: t
+End:
+*/
diff --git a/gnu/usr.bin/cc/include/tree.h b/gnu/usr.bin/cc/include/tree.h
new file mode 100644
index 0000000..dbe5ff9
--- /dev/null
+++ b/gnu/usr.bin/cc/include/tree.h
@@ -0,0 +1,1638 @@
+/* Front-end tree definitions for GNU compiler.
+ Copyright (C) 1989, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "machmode.h"
+
+#ifndef RTX_CODE
+struct rtx_def;
+#endif
+
+/* Codes of tree nodes */
+
+#define DEFTREECODE(SYM, STRING, TYPE, NARGS) SYM,
+
+enum tree_code {
+#include "tree.def"
+
+ LAST_AND_UNUSED_TREE_CODE /* A convenient way to get a value for
+ NUM_TREE_CODE. */
+};
+
+#undef DEFTREECODE
+
+/* Number of tree codes. */
+#define NUM_TREE_CODES ((int)LAST_AND_UNUSED_TREE_CODE)
+
+/* Indexed by enum tree_code, contains a character which is
+ `<' for a comparison expression, `1', for a unary arithmetic
+ expression, `2' for a binary arithmetic expression, `e' for
+ other types of expressions, `r' for a reference, `c' for a
+ constant, `d' for a decl, `t' for a type, `s' for a statement,
+ and `x' for anything else (TREE_LIST, IDENTIFIER, etc). */
+
+extern char **tree_code_type;
+#define TREE_CODE_CLASS(CODE) (*tree_code_type[(int) (CODE)])
+
+/* Number of argument-words in each kind of tree-node. */
+
+extern int *tree_code_length;
+
+/* Names of tree components. */
+
+extern char **tree_code_name;
+
+/* Codes that identify the various built in functions
+ so that expand_call can identify them quickly. */
+
+enum built_in_function
+{
+ NOT_BUILT_IN,
+ BUILT_IN_ALLOCA,
+ BUILT_IN_ABS,
+ BUILT_IN_FABS,
+ BUILT_IN_LABS,
+ BUILT_IN_FFS,
+ BUILT_IN_DIV,
+ BUILT_IN_LDIV,
+ BUILT_IN_FFLOOR,
+ BUILT_IN_FCEIL,
+ BUILT_IN_FMOD,
+ BUILT_IN_FREM,
+ BUILT_IN_MEMCPY,
+ BUILT_IN_MEMCMP,
+ BUILT_IN_MEMSET,
+ BUILT_IN_STRCPY,
+ BUILT_IN_STRCMP,
+ BUILT_IN_STRLEN,
+ BUILT_IN_FSQRT,
+ BUILT_IN_SIN,
+ BUILT_IN_COS,
+ BUILT_IN_GETEXP,
+ BUILT_IN_GETMAN,
+ BUILT_IN_SAVEREGS,
+ BUILT_IN_CLASSIFY_TYPE,
+ BUILT_IN_NEXT_ARG,
+ BUILT_IN_ARGS_INFO,
+ BUILT_IN_CONSTANT_P,
+ BUILT_IN_FRAME_ADDRESS,
+ BUILT_IN_RETURN_ADDRESS,
+ BUILT_IN_CALLER_RETURN_ADDRESS,
+ BUILT_IN_APPLY_ARGS,
+ BUILT_IN_APPLY,
+ BUILT_IN_RETURN,
+
+ /* C++ extensions */
+ BUILT_IN_NEW,
+ BUILT_IN_VEC_NEW,
+ BUILT_IN_DELETE,
+ BUILT_IN_VEC_DELETE,
+
+ /* Upper bound on non-language-specific builtins. */
+ END_BUILTINS
+};
+
+/* The definition of tree nodes fills the next several pages. */
+
+/* A tree node can represent a data type, a variable, an expression
+ or a statement. Each node has a TREE_CODE which says what kind of
+ thing it represents. Some common codes are:
+ INTEGER_TYPE -- represents a type of integers.
+ ARRAY_TYPE -- represents a type of pointer.
+ VAR_DECL -- represents a declared variable.
+ INTEGER_CST -- represents a constant integer value.
+ PLUS_EXPR -- represents a sum (an expression).
+
+ As for the contents of a tree node: there are some fields
+ that all nodes share. Each TREE_CODE has various special-purpose
+ fields as well. The fields of a node are never accessed directly,
+ always through accessor macros. */
+
+/* This type is used everywhere to refer to a tree node. */
+
+typedef union tree_node *tree;
+
+/* Every kind of tree node starts with this structure,
+ so all nodes have these fields.
+
+ See the accessor macros, defined below, for documentation of the fields. */
+
+struct tree_common
+{
+ union tree_node *chain;
+ union tree_node *type;
+#ifdef ONLY_INT_FIELDS
+ unsigned int code : 8;
+#else
+ enum tree_code code : 8;
+#endif
+
+ unsigned side_effects_flag : 1;
+ unsigned constant_flag : 1;
+ unsigned permanent_flag : 1;
+ unsigned addressable_flag : 1;
+ unsigned volatile_flag : 1;
+ unsigned readonly_flag : 1;
+ unsigned unsigned_flag : 1;
+ unsigned asm_written_flag: 1;
+
+ unsigned used_flag : 1;
+ unsigned raises_flag : 1;
+ unsigned static_flag : 1;
+ unsigned public_flag : 1;
+ unsigned private_flag : 1;
+ unsigned protected_flag : 1;
+
+ unsigned lang_flag_0 : 1;
+ unsigned lang_flag_1 : 1;
+ unsigned lang_flag_2 : 1;
+ unsigned lang_flag_3 : 1;
+ unsigned lang_flag_4 : 1;
+ unsigned lang_flag_5 : 1;
+ unsigned lang_flag_6 : 1;
+ /* There is room for two more flags. */
+};
+
+/* Define accessors for the fields that all tree nodes have
+ (though some fields are not used for all kinds of nodes). */
+
+/* The tree-code says what kind of node it is.
+ Codes are defined in tree.def. */
+#define TREE_CODE(NODE) ((enum tree_code) (NODE)->common.code)
+#define TREE_SET_CODE(NODE, VALUE) ((NODE)->common.code = (int) (VALUE))
+
+/* In all nodes that are expressions, this is the data type of the expression.
+ In POINTER_TYPE nodes, this is the type that the pointer points to.
+ In ARRAY_TYPE nodes, this is the type of the elements. */
+#define TREE_TYPE(NODE) ((NODE)->common.type)
+
+/* Nodes are chained together for many purposes.
+ Types are chained together to record them for being output to the debugger
+ (see the function `chain_type').
+ Decls in the same scope are chained together to record the contents
+ of the scope.
+ Statement nodes for successive statements used to be chained together.
+ Often lists of things are represented by TREE_LIST nodes that
+ are chained together. */
+
+#define TREE_CHAIN(NODE) ((NODE)->common.chain)
+
+/* Given an expression as a tree, strip any NON_LVALUE_EXPRs and NOP_EXPRs
+ that don't change the machine mode. */
+
+#define STRIP_NOPS(EXP) \
+ while ((TREE_CODE (EXP) == NOP_EXPR \
+ || TREE_CODE (EXP) == CONVERT_EXPR \
+ || TREE_CODE (EXP) == NON_LVALUE_EXPR) \
+ && (TYPE_MODE (TREE_TYPE (EXP)) \
+ == TYPE_MODE (TREE_TYPE (TREE_OPERAND (EXP, 0))))) \
+ (EXP) = TREE_OPERAND (EXP, 0);
+
+/* Like STRIP_NOPS, but don't alter the TREE_TYPE either. */
+
+#define STRIP_TYPE_NOPS(EXP) \
+ while ((TREE_CODE (EXP) == NOP_EXPR \
+ || TREE_CODE (EXP) == CONVERT_EXPR \
+ || TREE_CODE (EXP) == NON_LVALUE_EXPR) \
+ && (TREE_TYPE (EXP) \
+ == TREE_TYPE (TREE_OPERAND (EXP, 0)))) \
+ (EXP) = TREE_OPERAND (EXP, 0);
+
+/* Nonzero if TYPE represents an integral type. Note that we do not
+ include COMPLEX types here. */
+
+#define INTEGRAL_TYPE_P(TYPE) \
+ (TREE_CODE (TYPE) == INTEGER_TYPE || TREE_CODE (TYPE) == ENUMERAL_TYPE \
+ || TREE_CODE (TYPE) == BOOLEAN_TYPE || TREE_CODE (TYPE) == CHAR_TYPE)
+
+/* Nonzero if TYPE represents a floating-point type, including complex
+ floating-point types. */
+
+#define FLOAT_TYPE_P(TYPE) \
+ (TREE_CODE (TYPE) == REAL_TYPE \
+ || (TREE_CODE (TYPE) == COMPLEX_TYPE \
+ && TREE_CODE (TREE_TYPE (TYPE)) == REAL_TYPE))
+
+/* Nonzero if TYPE represents an aggregate (multi-component) type. */
+
+#define AGGREGATE_TYPE_P(TYPE) \
+ (TREE_CODE (TYPE) == ARRAY_TYPE || TREE_CODE (TYPE) == RECORD_TYPE \
+ || TREE_CODE (TYPE) == UNION_TYPE || TREE_CODE (TYPE) == QUAL_UNION_TYPE \
+ || TREE_CODE (TYPE) == SET_TYPE)
+
+/* Define many boolean fields that all tree nodes have. */
+
+/* In VAR_DECL nodes, nonzero means address of this is needed.
+ So it cannot be in a register.
+ In a FUNCTION_DECL, nonzero means its address is needed.
+ So it must be compiled even if it is an inline function.
+ In CONSTRUCTOR nodes, it means object constructed must be in memory.
+ In LABEL_DECL nodes, it means a goto for this label has been seen
+ from a place outside all binding contours that restore stack levels.
+ In ..._TYPE nodes, it means that objects of this type must
+ be fully addressable. This means that pieces of this
+ object cannot go into register parameters, for example.
+ In IDENTIFIER_NODEs, this means that some extern decl for this name
+ had its address taken. That matters for inline functions. */
+#define TREE_ADDRESSABLE(NODE) ((NODE)->common.addressable_flag)
+
+/* In a VAR_DECL, nonzero means allocate static storage.
+ In a FUNCTION_DECL, nonzero if function has been defined.
+ In a CONSTRUCTOR, nonzero means allocate static storage. */
+#define TREE_STATIC(NODE) ((NODE)->common.static_flag)
+
+/* In a CONVERT_EXPR, NOP_EXPR or COMPOUND_EXPR, this means the node was
+ made implicitly and should not lead to an "unused value" warning. */
+#define TREE_NO_UNUSED_WARNING(NODE) ((NODE)->common.static_flag)
+
+/* Nonzero for a TREE_LIST or TREE_VEC node means that the derivation
+ chain is via a `virtual' declaration. */
+#define TREE_VIA_VIRTUAL(NODE) ((NODE)->common.static_flag)
+
+/* In an INTEGER_CST, REAL_CST, or COMPLEX_CST, this means there was an
+ overflow in folding. This is distinct from TREE_OVERFLOW because ANSI C
+ requires a diagnostic when overflows occur in constant expressions. */
+#define TREE_CONSTANT_OVERFLOW(NODE) ((NODE)->common.static_flag)
+
+/* In an IDENTIFIER_NODE, this means that assemble_name was called with
+ this string as an argument. */
+#define TREE_SYMBOL_REFERENCED(NODE) ((NODE)->common.static_flag)
+
+/* In an INTEGER_CST, REAL_CST, of COMPLEX_CST, this means there was an
+ overflow in folding, and no warning has been issued for this subexpression.
+ TREE_OVERFLOW implies TREE_CONSTANT_OVERFLOW, but not vice versa. */
+#define TREE_OVERFLOW(NODE) ((NODE)->common.public_flag)
+
+/* In a VAR_DECL or FUNCTION_DECL,
+ nonzero means name is to be accessible from outside this module.
+ In an identifier node, nonzero means an external declaration
+ accessible from outside this module was previously seen
+ for this name in an inner scope. */
+#define TREE_PUBLIC(NODE) ((NODE)->common.public_flag)
+
+/* Nonzero for TREE_LIST or TREE_VEC node means that the path to the
+ base class is via a `public' declaration, which preserves public
+ fields from the base class as public. */
+#define TREE_VIA_PUBLIC(NODE) ((NODE)->common.public_flag)
+
+/* Ditto, for `private' declarations. */
+#define TREE_VIA_PRIVATE(NODE) ((NODE)->common.private_flag)
+
+/* Nonzero for TREE_LIST node means that the path to the
+ base class is via a `protected' declaration, which preserves
+ protected fields from the base class as protected.
+ OVERLOADED. */
+#define TREE_VIA_PROTECTED(NODE) ((NODE)->common.protected_flag)
+
+/* In any expression, nonzero means it has side effects or reevaluation
+ of the whole expression could produce a different value.
+ This is set if any subexpression is a function call, a side effect
+ or a reference to a volatile variable.
+ In a ..._DECL, this is set only if the declaration said `volatile'. */
+#define TREE_SIDE_EFFECTS(NODE) ((NODE)->common.side_effects_flag)
+
+/* Nonzero means this expression is volatile in the C sense:
+ its address should be of type `volatile WHATEVER *'.
+ In other words, the declared item is volatile qualified.
+ This is used in _DECL nodes and _REF nodes.
+
+ In a ..._TYPE node, means this type is volatile-qualified.
+ But use TYPE_VOLATILE instead of this macro when the node is a type,
+ because eventually we may make that a different bit.
+
+ If this bit is set in an expression, so is TREE_SIDE_EFFECTS. */
+#define TREE_THIS_VOLATILE(NODE) ((NODE)->common.volatile_flag)
+
+/* In a VAR_DECL, PARM_DECL or FIELD_DECL, or any kind of ..._REF node,
+ nonzero means it may not be the lhs of an assignment.
+ In a ..._TYPE node, means this type is const-qualified
+ (but the macro TYPE_READONLY should be used instead of this macro
+ when the node is a type). */
+#define TREE_READONLY(NODE) ((NODE)->common.readonly_flag)
+
+/* Value of expression is constant.
+ Always appears in all ..._CST nodes.
+ May also appear in an arithmetic expression, an ADDR_EXPR or a CONSTRUCTOR
+ if the value is constant. */
+#define TREE_CONSTANT(NODE) ((NODE)->common.constant_flag)
+
+/* Nonzero means permanent node;
+ node will continue to exist for the entire compiler run.
+ Otherwise it will be recycled at the end of the function. */
+#define TREE_PERMANENT(NODE) ((NODE)->common.permanent_flag)
+
+/* In INTEGER_TYPE or ENUMERAL_TYPE nodes, means an unsigned type.
+ In FIELD_DECL nodes, means an unsigned bit field.
+ The same bit is used in functions as DECL_BUILT_IN_NONANSI. */
+#define TREE_UNSIGNED(NODE) ((NODE)->common.unsigned_flag)
+
+/* Nonzero in a VAR_DECL means assembler code has been written.
+ Nonzero in a FUNCTION_DECL means that the function has been compiled.
+ This is interesting in an inline function, since it might not need
+ to be compiled separately.
+ Nonzero in a RECORD_TYPE, UNION_TYPE, QUAL_UNION_TYPE or ENUMERAL_TYPE
+ if the sdb debugging info for the type has been written.
+ In a BLOCK node, nonzero if reorder_blocks has already seen this block. */
+#define TREE_ASM_WRITTEN(NODE) ((NODE)->common.asm_written_flag)
+
+/* Nonzero in a _DECL if the name is used in its scope.
+ Nonzero in an expr node means inhibit warning if value is unused.
+ In IDENTIFIER_NODEs, this means that some extern decl for this name
+ was used. */
+#define TREE_USED(NODE) ((NODE)->common.used_flag)
+
+/* Nonzero for a tree node whose evaluation could result
+ in the raising of an exception. Not implemented yet. */
+#define TREE_RAISES(NODE) ((NODE)->common.raises_flag)
+
+/* Used in classes in C++. */
+#define TREE_PRIVATE(NODE) ((NODE)->common.private_flag)
+/* Used in classes in C++.
+ In a BLOCK node, this is BLOCK_HANDLER_BLOCK. */
+#define TREE_PROTECTED(NODE) ((NODE)->common.protected_flag)
+
+/* These flags are available for each language front end to use internally. */
+#define TREE_LANG_FLAG_0(NODE) ((NODE)->common.lang_flag_0)
+#define TREE_LANG_FLAG_1(NODE) ((NODE)->common.lang_flag_1)
+#define TREE_LANG_FLAG_2(NODE) ((NODE)->common.lang_flag_2)
+#define TREE_LANG_FLAG_3(NODE) ((NODE)->common.lang_flag_3)
+#define TREE_LANG_FLAG_4(NODE) ((NODE)->common.lang_flag_4)
+#define TREE_LANG_FLAG_5(NODE) ((NODE)->common.lang_flag_5)
+#define TREE_LANG_FLAG_6(NODE) ((NODE)->common.lang_flag_6)
+
+/* Define additional fields and accessors for nodes representing constants. */
+
+/* In an INTEGER_CST node. These two together make a 2-word integer.
+ If the data type is signed, the value is sign-extended to 2 words
+ even though not all of them may really be in use.
+ In an unsigned constant shorter than 2 words, the extra bits are 0. */
+#define TREE_INT_CST_LOW(NODE) ((NODE)->int_cst.int_cst_low)
+#define TREE_INT_CST_HIGH(NODE) ((NODE)->int_cst.int_cst_high)
+
+#define INT_CST_LT(A, B) \
+(TREE_INT_CST_HIGH (A) < TREE_INT_CST_HIGH (B) \
+ || (TREE_INT_CST_HIGH (A) == TREE_INT_CST_HIGH (B) \
+ && ((unsigned HOST_WIDE_INT) TREE_INT_CST_LOW (A) \
+ < (unsigned HOST_WIDE_INT) TREE_INT_CST_LOW (B))))
+
+#define INT_CST_LT_UNSIGNED(A, B) \
+(((unsigned HOST_WIDE_INT) TREE_INT_CST_HIGH (A) \
+ < (unsigned HOST_WIDE_INT) TREE_INT_CST_HIGH (B)) \
+ || (((unsigned HOST_WIDE_INT) TREE_INT_CST_HIGH (A) \
+ == (unsigned HOST_WIDE_INT ) TREE_INT_CST_HIGH (B)) \
+ && (((unsigned HOST_WIDE_INT) TREE_INT_CST_LOW (A) \
+ < (unsigned HOST_WIDE_INT) TREE_INT_CST_LOW (B)))))
+
+struct tree_int_cst
+{
+ char common[sizeof (struct tree_common)];
+ HOST_WIDE_INT int_cst_low;
+ HOST_WIDE_INT int_cst_high;
+};
+
+/* In REAL_CST, STRING_CST, COMPLEX_CST nodes, and CONSTRUCTOR nodes,
+ and generally in all kinds of constants that could
+ be given labels (rather than being immediate). */
+
+#define TREE_CST_RTL(NODE) ((NODE)->real_cst.rtl)
+
+/* In a REAL_CST node. */
+/* We can represent a real value as either a `double' or a string.
+ Strings don't allow for any optimization, but they do allow
+ for cross-compilation. */
+
+#define TREE_REAL_CST(NODE) ((NODE)->real_cst.real_cst)
+
+#include "real.h"
+
+struct tree_real_cst
+{
+ char common[sizeof (struct tree_common)];
+ struct rtx_def *rtl; /* acts as link to register transfer language
+ (rtl) info */
+ REAL_VALUE_TYPE real_cst;
+};
+
+/* In a STRING_CST */
+#define TREE_STRING_LENGTH(NODE) ((NODE)->string.length)
+#define TREE_STRING_POINTER(NODE) ((NODE)->string.pointer)
+
+struct tree_string
+{
+ char common[sizeof (struct tree_common)];
+ struct rtx_def *rtl; /* acts as link to register transfer language
+ (rtl) info */
+ int length;
+ char *pointer;
+};
+
+/* In a COMPLEX_CST node. */
+#define TREE_REALPART(NODE) ((NODE)->complex.real)
+#define TREE_IMAGPART(NODE) ((NODE)->complex.imag)
+
+struct tree_complex
+{
+ char common[sizeof (struct tree_common)];
+ struct rtx_def *rtl; /* acts as link to register transfer language
+ (rtl) info */
+ union tree_node *real;
+ union tree_node *imag;
+};
+
+/* Define fields and accessors for some special-purpose tree nodes. */
+
+#define IDENTIFIER_LENGTH(NODE) ((NODE)->identifier.length)
+#define IDENTIFIER_POINTER(NODE) ((NODE)->identifier.pointer)
+
+struct tree_identifier
+{
+ char common[sizeof (struct tree_common)];
+ int length;
+ char *pointer;
+};
+
+/* In a TREE_LIST node. */
+#define TREE_PURPOSE(NODE) ((NODE)->list.purpose)
+#define TREE_VALUE(NODE) ((NODE)->list.value)
+
+struct tree_list
+{
+ char common[sizeof (struct tree_common)];
+ union tree_node *purpose;
+ union tree_node *value;
+};
+
+/* In a TREE_VEC node. */
+#define TREE_VEC_LENGTH(NODE) ((NODE)->vec.length)
+#define TREE_VEC_ELT(NODE,I) ((NODE)->vec.a[I])
+#define TREE_VEC_END(NODE) (&((NODE)->vec.a[(NODE)->vec.length]))
+
+struct tree_vec
+{
+ char common[sizeof (struct tree_common)];
+ int length;
+ union tree_node *a[1];
+};
+
+/* Define fields and accessors for some nodes that represent expressions. */
+
+/* In a SAVE_EXPR node. */
+#define SAVE_EXPR_CONTEXT(NODE) TREE_OPERAND(NODE, 1)
+#define SAVE_EXPR_RTL(NODE) (*(struct rtx_def **) &(NODE)->exp.operands[2])
+
+/* In a RTL_EXPR node. */
+#define RTL_EXPR_SEQUENCE(NODE) (*(struct rtx_def **) &(NODE)->exp.operands[0])
+#define RTL_EXPR_RTL(NODE) (*(struct rtx_def **) &(NODE)->exp.operands[1])
+
+/* In a CALL_EXPR node. */
+#define CALL_EXPR_RTL(NODE) (*(struct rtx_def **) &(NODE)->exp.operands[2])
+
+/* In a CONSTRUCTOR node. */
+#define CONSTRUCTOR_ELTS(NODE) TREE_OPERAND (NODE, 1)
+
+/* In ordinary expression nodes. */
+#define TREE_OPERAND(NODE, I) ((NODE)->exp.operands[I])
+#define TREE_COMPLEXITY(NODE) ((NODE)->exp.complexity)
+
+struct tree_exp
+{
+ char common[sizeof (struct tree_common)];
+ int complexity;
+ union tree_node *operands[1];
+};
+
+/* In a BLOCK node. */
+#define BLOCK_VARS(NODE) ((NODE)->block.vars)
+#define BLOCK_TYPE_TAGS(NODE) ((NODE)->block.type_tags)
+#define BLOCK_SUBBLOCKS(NODE) ((NODE)->block.subblocks)
+#define BLOCK_SUPERCONTEXT(NODE) ((NODE)->block.supercontext)
+/* Note: when changing this, make sure to find the places
+ that use chainon or nreverse. */
+#define BLOCK_CHAIN(NODE) TREE_CHAIN (NODE)
+#define BLOCK_ABSTRACT_ORIGIN(NODE) ((NODE)->block.abstract_origin)
+#define BLOCK_ABSTRACT(NODE) ((NODE)->block.abstract_flag)
+#define BLOCK_END_NOTE(NODE) ((NODE)->block.end_note)
+
+/* Nonzero means that this block is prepared to handle exceptions
+ listed in the BLOCK_VARS slot. */
+#define BLOCK_HANDLER_BLOCK(NODE) ((NODE)->block.handler_block_flag)
+
+struct tree_block
+{
+ char common[sizeof (struct tree_common)];
+
+ unsigned handler_block_flag : 1;
+ unsigned abstract_flag : 1;
+
+ union tree_node *vars;
+ union tree_node *type_tags;
+ union tree_node *subblocks;
+ union tree_node *supercontext;
+ union tree_node *abstract_origin;
+ struct rtx_def *end_note;
+};
+
+/* Define fields and accessors for nodes representing data types. */
+
+/* See tree.def for documentation of the use of these fields.
+ Look at the documentation of the various ..._TYPE tree codes. */
+
+#define TYPE_UID(NODE) ((NODE)->type.uid)
+#define TYPE_SIZE(NODE) ((NODE)->type.size)
+#define TYPE_MODE(NODE) ((NODE)->type.mode)
+#define TYPE_VALUES(NODE) ((NODE)->type.values)
+#define TYPE_DOMAIN(NODE) ((NODE)->type.values)
+#define TYPE_FIELDS(NODE) ((NODE)->type.values)
+#define TYPE_METHODS(NODE) ((NODE)->type.maxval)
+#define TYPE_VFIELD(NODE) ((NODE)->type.minval)
+#define TYPE_ARG_TYPES(NODE) ((NODE)->type.values)
+#define TYPE_METHOD_BASETYPE(NODE) ((NODE)->type.maxval)
+#define TYPE_OFFSET_BASETYPE(NODE) ((NODE)->type.maxval)
+#define TYPE_POINTER_TO(NODE) ((NODE)->type.pointer_to)
+#define TYPE_REFERENCE_TO(NODE) ((NODE)->type.reference_to)
+#define TYPE_MIN_VALUE(NODE) ((NODE)->type.minval)
+#define TYPE_MAX_VALUE(NODE) ((NODE)->type.maxval)
+#define TYPE_PRECISION(NODE) ((NODE)->type.precision)
+#define TYPE_PARSE_INFO(NODE) ((NODE)->type.parse_info)
+#define TYPE_SYMTAB_ADDRESS(NODE) ((NODE)->type.symtab.address)
+#define TYPE_SYMTAB_POINTER(NODE) ((NODE)->type.symtab.pointer)
+#define TYPE_NAME(NODE) ((NODE)->type.name)
+#define TYPE_NEXT_VARIANT(NODE) ((NODE)->type.next_variant)
+#define TYPE_MAIN_VARIANT(NODE) ((NODE)->type.main_variant)
+#define TYPE_BINFO(NODE) ((NODE)->type.binfo)
+#define TYPE_NONCOPIED_PARTS(NODE) ((NODE)->type.noncopied_parts)
+#define TYPE_CONTEXT(NODE) ((NODE)->type.context)
+#define TYPE_OBSTACK(NODE) ((NODE)->type.obstack)
+#define TYPE_LANG_SPECIFIC(NODE) ((NODE)->type.lang_specific)
+
+/* A TREE_LIST of IDENTIFIER nodes of the attributes that apply
+ to this type. */
+#define TYPE_ATTRIBUTES(NODE) ((NODE)->type.attributes)
+
+/* The alignment necessary for objects of this type.
+ The value is an int, measured in bits. */
+#define TYPE_ALIGN(NODE) ((NODE)->type.align)
+
+#define TYPE_STUB_DECL(NODE) (TREE_CHAIN (NODE))
+
+/* In a RECORD_TYPE, UNION_TYPE or QUAL_UNION_TYPE, it means the type
+ has BLKmode only because it lacks the alignment requirement for
+ its size. */
+#define TYPE_NO_FORCE_BLK(NODE) ((NODE)->type.no_force_blk_flag)
+
+/* Nonzero in a type considered volatile as a whole. */
+#define TYPE_VOLATILE(NODE) ((NODE)->common.volatile_flag)
+
+/* Means this type is const-qualified. */
+#define TYPE_READONLY(NODE) ((NODE)->common.readonly_flag)
+
+/* These flags are available for each language front end to use internally. */
+#define TYPE_LANG_FLAG_0(NODE) ((NODE)->type.lang_flag_0)
+#define TYPE_LANG_FLAG_1(NODE) ((NODE)->type.lang_flag_1)
+#define TYPE_LANG_FLAG_2(NODE) ((NODE)->type.lang_flag_2)
+#define TYPE_LANG_FLAG_3(NODE) ((NODE)->type.lang_flag_3)
+#define TYPE_LANG_FLAG_4(NODE) ((NODE)->type.lang_flag_4)
+#define TYPE_LANG_FLAG_5(NODE) ((NODE)->type.lang_flag_5)
+#define TYPE_LANG_FLAG_6(NODE) ((NODE)->type.lang_flag_6)
+
+/* If set in an ARRAY_TYPE, indicates a string type (for languages
+ that distinguish string from array of char).
+ If set in a SET_TYPE, indicates a bitstring type. */
+#define TYPE_STRING_FLAG(NODE) ((NODE)->type.string_flag)
+
+/* Indicates that objects of this type must be initialized by calling a
+ function when they are created. */
+#define TYPE_NEEDS_CONSTRUCTING(NODE) ((NODE)->type.needs_constructing_flag)
+
+struct tree_type
+{
+ char common[sizeof (struct tree_common)];
+ union tree_node *values;
+ union tree_node *size;
+ union tree_node *attributes;
+ unsigned uid;
+
+ unsigned char precision;
+#ifdef ONLY_INT_FIELDS
+ int mode : 8;
+#else
+ enum machine_mode mode : 8;
+#endif
+
+ unsigned string_flag : 1;
+ unsigned no_force_blk_flag : 1;
+ unsigned needs_constructing_flag : 1;
+ unsigned lang_flag_0 : 1;
+ unsigned lang_flag_1 : 1;
+ unsigned lang_flag_2 : 1;
+ unsigned lang_flag_3 : 1;
+ unsigned lang_flag_4 : 1;
+ unsigned lang_flag_5 : 1;
+ unsigned lang_flag_6 : 1;
+ /* room for 6 more bits */
+
+ unsigned int align;
+ union tree_node *pointer_to;
+ union tree_node *reference_to;
+ int parse_info;
+ union {int address; char *pointer; } symtab;
+ union tree_node *name;
+ union tree_node *minval;
+ union tree_node *maxval;
+ union tree_node *next_variant;
+ union tree_node *main_variant;
+ union tree_node *binfo;
+ union tree_node *noncopied_parts;
+ union tree_node *context;
+ struct obstack *obstack;
+ /* Points to a structure whose details depend on the language in use. */
+ struct lang_type *lang_specific;
+};
+
+/* Define accessor macros for information about type inheritance
+ and basetypes.
+
+ A "basetype" means a particular usage of a data type for inheritance
+ in another type. Each such basetype usage has its own "binfo"
+ object to describe it. The binfo object is a TREE_VEC node.
+
+ Inheritance is represented by the binfo nodes allocated for a
+ given type. For example, given types C and D, such that D is
+ inherited by C, 3 binfo nodes will be allocated: one for describing
+ the binfo properties of C, similarly one for D, and one for
+ describing the binfo properties of D as a base type for C.
+ Thus, given a pointer to class C, one can get a pointer to the binfo
+ of D acting as a basetype for C by looking at C's binfo's basetypes. */
+
+/* The actual data type node being inherited in this basetype. */
+#define BINFO_TYPE(NODE) TREE_TYPE (NODE)
+
+/* The offset where this basetype appears in its containing type.
+ BINFO_OFFSET slot holds the offset (in bytes)
+ from the base of the complete object to the base of the part of the
+ object that is allocated on behalf of this `type'.
+ This is always 0 except when there is multiple inheritance. */
+
+#define BINFO_OFFSET(NODE) TREE_VEC_ELT ((NODE), 1)
+#define TYPE_BINFO_OFFSET(NODE) BINFO_OFFSET (TYPE_BINFO (NODE))
+#define BINFO_OFFSET_ZEROP(NODE) (BINFO_OFFSET (NODE) == integer_zero_node)
+
+/* The virtual function table belonging to this basetype. Virtual
+ function tables provide a mechanism for run-time method dispatching.
+ The entries of a virtual function table are language-dependent. */
+
+#define BINFO_VTABLE(NODE) TREE_VEC_ELT ((NODE), 2)
+#define TYPE_BINFO_VTABLE(NODE) BINFO_VTABLE (TYPE_BINFO (NODE))
+
+/* The virtual functions in the virtual function table. This is
+ a TREE_LIST that is used as an initial approximation for building
+ a virtual function table for this basetype. */
+#define BINFO_VIRTUALS(NODE) TREE_VEC_ELT ((NODE), 3)
+#define TYPE_BINFO_VIRTUALS(NODE) BINFO_VIRTUALS (TYPE_BINFO (NODE))
+
+/* A vector of additional binfos for the types inherited by this basetype.
+
+ If this basetype describes type D as inherited in C,
+ and if the basetypes of D are E anf F,
+ then this vector contains binfos for inheritance of E and F by C.
+
+ ??? This could probably be done by just allocating the
+ base types at the end of this TREE_VEC (instead of using
+ another TREE_VEC). This would simplify the calculation
+ of how many basetypes a given type had. */
+#define BINFO_BASETYPES(NODE) TREE_VEC_ELT ((NODE), 4)
+#define TYPE_BINFO_BASETYPES(NODE) TREE_VEC_ELT (TYPE_BINFO (NODE), 4)
+
+/* For a BINFO record describing an inheritance, this yields a pointer
+ to the artificial FIELD_DECL node which contains the "virtual base
+ class pointer" for the given inheritance. */
+
+#define BINFO_VPTR_FIELD(NODE) TREE_VEC_ELT ((NODE), 5)
+
+/* Accessor macro to get to the Nth basetype of this basetype. */
+#define BINFO_BASETYPE(NODE,N) TREE_VEC_ELT (BINFO_BASETYPES (NODE), (N))
+#define TYPE_BINFO_BASETYPE(NODE,N) BINFO_TYPE (TREE_VEC_ELT (BINFO_BASETYPES (TYPE_BINFO (NODE)), (N)))
+
+/* Slot used to build a chain that represents a use of inheritance.
+ For example, if X is derived from Y, and Y is derived from Z,
+ then this field can be used to link the binfo node for X to
+ the binfo node for X's Y to represent the use of inheritance
+ from X to Y. Similarly, this slot of the binfo node for X's Y
+ can point to the Z from which Y is inherited (in X's inheritance
+ hierarchy). In this fashion, one can represent and traverse specific
+ uses of inheritance using the binfo nodes themselves (instead of
+ consing new space pointing to binfo nodes).
+ It is up to the language-dependent front-ends to maintain
+ this information as necessary. */
+#define BINFO_INHERITANCE_CHAIN(NODE) TREE_VEC_ELT ((NODE), 0)
+
+/* Define fields and accessors for nodes representing declared names. */
+
+/* This is the name of the object as written by the user.
+ It is an IDENTIFIER_NODE. */
+#define DECL_NAME(NODE) ((NODE)->decl.name)
+/* This is the name of the object as the assembler will see it
+ (but before any translations made by ASM_OUTPUT_LABELREF).
+ Often this is the same as DECL_NAME.
+ It is an IDENTIFIER_NODE. */
+#define DECL_ASSEMBLER_NAME(NODE) ((NODE)->decl.assembler_name)
+/* Records the section name in a section attribute. Used to pass
+ the name from decl_attributes to make_function_rtl and make_decl_rtl. */
+#define DECL_SECTION_NAME(NODE) ((NODE)->decl.section_name)
+/* For FIELD_DECLs, this is the
+ RECORD_TYPE, UNION_TYPE, or QUAL_UNION_TYPE node that the field is
+ a member of. For VAR_DECL, PARM_DECL, FUNCTION_DECL, LABEL_DECL,
+ and CONST_DECL nodes, this points to the FUNCTION_DECL for the
+ containing function, or else yields NULL_TREE if the given decl has "file scope". */
+#define DECL_CONTEXT(NODE) ((NODE)->decl.context)
+#define DECL_FIELD_CONTEXT(NODE) ((NODE)->decl.context)
+/* In a FIELD_DECL, this is the field position, counting in bits,
+ of the bit closest to the beginning of the structure. */
+#define DECL_FIELD_BITPOS(NODE) ((NODE)->decl.arguments)
+/* In a FIELD_DECL, this indicates whether the field was a bit-field and
+ if so, the type that was originally specified for it.
+ TREE_TYPE may have been modified (in finish_struct). */
+#define DECL_BIT_FIELD_TYPE(NODE) ((NODE)->decl.result)
+/* In FUNCTION_DECL, a chain of ..._DECL nodes. */
+/* VAR_DECL and PARM_DECL reserve the arguments slot
+ for language-specific uses. */
+#define DECL_ARGUMENTS(NODE) ((NODE)->decl.arguments)
+/* In FUNCTION_DECL, holds the decl for the return value. */
+#define DECL_RESULT(NODE) ((NODE)->decl.result)
+/* In PARM_DECL, holds the type as written (perhaps a function or array). */
+#define DECL_ARG_TYPE_AS_WRITTEN(NODE) ((NODE)->decl.result)
+/* For a FUNCTION_DECL, holds the tree of BINDINGs.
+ For a VAR_DECL, holds the initial value.
+ For a PARM_DECL, not used--default
+ values for parameters are encoded in the type of the function,
+ not in the PARM_DECL slot. */
+#define DECL_INITIAL(NODE) ((NODE)->decl.initial)
+/* For a PARM_DECL, records the data type used to pass the argument,
+ which may be different from the type seen in the program. */
+#define DECL_ARG_TYPE(NODE) ((NODE)->decl.initial) /* In PARM_DECL. */
+/* For a FIELD_DECL in a QUAL_UNION_TYPE, records the expression, which
+ if nonzero, indicates that the field occupies the type. */
+#define DECL_QUALIFIER(NODE) ((NODE)->decl.initial)
+/* These two fields describe where in the source code the declaration was. */
+#define DECL_SOURCE_FILE(NODE) ((NODE)->decl.filename)
+#define DECL_SOURCE_LINE(NODE) ((NODE)->decl.linenum)
+/* Holds the size of the datum, as a tree expression.
+ Need not be constant. */
+#define DECL_SIZE(NODE) ((NODE)->decl.size)
+/* Holds the alignment required for the datum. */
+#define DECL_ALIGN(NODE) ((NODE)->decl.frame_size.u)
+/* Holds the machine mode corresponding to the declaration of a variable or
+ field. Always equal to TYPE_MODE (TREE_TYPE (decl)) except for a
+ FIELD_DECL. */
+#define DECL_MODE(NODE) ((NODE)->decl.mode)
+/* Holds the RTL expression for the value of a variable or function. If
+ PROMOTED_MODE is defined, the mode of this expression may not be same
+ as DECL_MODE. In that case, DECL_MODE contains the mode corresponding
+ to the variable's data type, while the mode
+ of DECL_RTL is the mode actually used to contain the data. */
+#define DECL_RTL(NODE) ((NODE)->decl.rtl)
+/* For PARM_DECL, holds an RTL for the stack slot or register
+ where the data was actually passed. */
+#define DECL_INCOMING_RTL(NODE) ((NODE)->decl.saved_insns.r)
+/* For FUNCTION_DECL, if it is inline, holds the saved insn chain. */
+#define DECL_SAVED_INSNS(NODE) ((NODE)->decl.saved_insns.r)
+/* For FUNCTION_DECL, if it is inline,
+ holds the size of the stack frame, as an integer. */
+#define DECL_FRAME_SIZE(NODE) ((NODE)->decl.frame_size.i)
+/* For FUNCTION_DECL, if it is built-in,
+ this identifies which built-in operation it is. */
+#define DECL_FUNCTION_CODE(NODE) ((NODE)->decl.frame_size.f)
+#define DECL_SET_FUNCTION_CODE(NODE,VAL) ((NODE)->decl.frame_size.f = (VAL))
+/* For a FIELD_DECL, holds the size of the member as an integer. */
+#define DECL_FIELD_SIZE(NODE) ((NODE)->decl.saved_insns.i)
+
+/* The DECL_VINDEX is used for FUNCTION_DECLS in two different ways.
+ Before the struct containing the FUNCTION_DECL is laid out,
+ DECL_VINDEX may point to a FUNCTION_DECL in a base class which
+ is the FUNCTION_DECL which this FUNCTION_DECL will replace as a virtual
+ function. When the class is laid out, this pointer is changed
+ to an INTEGER_CST node which is suitable for use as an index
+ into the virtual function table. */
+#define DECL_VINDEX(NODE) ((NODE)->decl.vindex)
+/* For FIELD_DECLS, DECL_FCONTEXT is the *first* baseclass in
+ which this FIELD_DECL is defined. This information is needed when
+ writing debugging information about vfield and vbase decls for C++. */
+#define DECL_FCONTEXT(NODE) ((NODE)->decl.vindex)
+
+/* Every ..._DECL node gets a unique number. */
+#define DECL_UID(NODE) ((NODE)->decl.uid)
+
+/* For any sort of a ..._DECL node, this points to the original (abstract)
+ decl node which this decl is an instance of, or else it is NULL indicating
+ that this decl is not an instance of some other decl. */
+#define DECL_ABSTRACT_ORIGIN(NODE) ((NODE)->decl.abstract_origin)
+
+/* Nonzero for any sort of ..._DECL node means this decl node represents
+ an inline instance of some original (abstract) decl from an inline function;
+ suppress any warnings about shadowing some other variable. */
+#define DECL_FROM_INLINE(NODE) (DECL_ABSTRACT_ORIGIN (NODE) != (tree) 0)
+
+/* Nonzero if a _DECL means that the name of this decl should be ignored
+ for symbolic debug purposes. */
+#define DECL_IGNORED_P(NODE) ((NODE)->decl.ignored_flag)
+
+/* Nonzero for a given ..._DECL node means that this node represents an
+ "abstract instance" of the given declaration (e.g. in the original
+ declaration of an inline function). When generating symbolic debugging
+ information, we musn't try to generate any address information for nodes
+ marked as "abstract instances" because we don't actually generate
+ any code or allocate any data space for such instances. */
+#define DECL_ABSTRACT(NODE) ((NODE)->decl.abstract_flag)
+
+/* Nonzero if a _DECL means that no warnings should be generated just
+ because this decl is unused. */
+#define DECL_IN_SYSTEM_HEADER(NODE) ((NODE)->decl.in_system_header_flag)
+
+/* Nonzero for a given ..._DECL node means that this node should be
+ put in .common, if possible. If a DECL_INITIAL is given, and it
+ is not error_mark_node, then the decl cannot be put in .common. */
+#define DECL_COMMON(NODE) ((NODE)->decl.common_flag)
+
+/* Language-specific decl information. */
+#define DECL_LANG_SPECIFIC(NODE) ((NODE)->decl.lang_specific)
+
+/* In a VAR_DECL or FUNCTION_DECL,
+ nonzero means external reference:
+ do not allocate storage, and refer to a definition elsewhere. */
+#define DECL_EXTERNAL(NODE) ((NODE)->decl.external_flag)
+
+/* In a TYPE_DECL
+ nonzero means the detail info about this type is not dumped into stabs.
+ Instead it will generate cross reference ('x') of names.
+ This uses the same flag as DECL_EXTERNAL. */
+#define TYPE_DECL_SUPPRESS_DEBUG(NODE) ((NODE)->decl.external_flag)
+
+
+/* In VAR_DECL and PARM_DECL nodes, nonzero means declared `register'.
+ In LABEL_DECL nodes, nonzero means that an error message about
+ jumping into such a binding contour has been printed for this label. */
+#define DECL_REGISTER(NODE) ((NODE)->decl.regdecl_flag)
+/* In a FIELD_DECL, indicates this field should be bit-packed. */
+#define DECL_PACKED(NODE) ((NODE)->decl.regdecl_flag)
+
+/* Nonzero in a ..._DECL means this variable is ref'd from a nested function.
+ For VAR_DECL nodes, PARM_DECL nodes, and FUNCTION_DECL nodes.
+
+ For LABEL_DECL nodes, nonzero if nonlocal gotos to the label are permitted.
+
+ Also set in some languages for variables, etc., outside the normal
+ lexical scope, such as class instance variables. */
+#define DECL_NONLOCAL(NODE) ((NODE)->decl.nonlocal_flag)
+
+/* Nonzero in a FUNCTION_DECL means this function can be substituted
+ where it is called. */
+#define DECL_INLINE(NODE) ((NODE)->decl.inline_flag)
+
+/* Nonzero in a FUNCTION_DECL means this is a built-in function
+ that is not specified by ansi C and that users are supposed to be allowed
+ to redefine for any purpose whatever. */
+#define DECL_BUILT_IN_NONANSI(NODE) ((NODE)->common.unsigned_flag)
+
+/* Nonzero in a FIELD_DECL means it is a bit field, and must be accessed
+ specially. */
+#define DECL_BIT_FIELD(NODE) ((NODE)->decl.bit_field_flag)
+/* In a LABEL_DECL, nonzero means label was defined inside a binding
+ contour that restored a stack level and which is now exited. */
+#define DECL_TOO_LATE(NODE) ((NODE)->decl.bit_field_flag)
+/* In a FUNCTION_DECL, nonzero means a built in function. */
+#define DECL_BUILT_IN(NODE) ((NODE)->decl.bit_field_flag)
+/* In a VAR_DECL that's static,
+ nonzero if the space is in the text section. */
+#define DECL_IN_TEXT_SECTION(NODE) ((NODE)->decl.bit_field_flag)
+
+/* Used in VAR_DECLs to indicate that the variable is a vtable.
+ It is also used in FIELD_DECLs for vtable pointers. */
+#define DECL_VIRTUAL_P(NODE) ((NODE)->decl.virtual_flag)
+
+/* Used to indicate that the linkage status of this DECL is not yet known,
+ so it should not be output now. */
+#define DECL_DEFER_OUTPUT(NODE) ((NODE)->decl.defer_output)
+
+/* Additional flags for language-specific uses. */
+#define DECL_LANG_FLAG_0(NODE) ((NODE)->decl.lang_flag_0)
+#define DECL_LANG_FLAG_1(NODE) ((NODE)->decl.lang_flag_1)
+#define DECL_LANG_FLAG_2(NODE) ((NODE)->decl.lang_flag_2)
+#define DECL_LANG_FLAG_3(NODE) ((NODE)->decl.lang_flag_3)
+#define DECL_LANG_FLAG_4(NODE) ((NODE)->decl.lang_flag_4)
+#define DECL_LANG_FLAG_5(NODE) ((NODE)->decl.lang_flag_5)
+#define DECL_LANG_FLAG_6(NODE) ((NODE)->decl.lang_flag_6)
+#define DECL_LANG_FLAG_7(NODE) ((NODE)->decl.lang_flag_7)
+
+struct tree_decl
+{
+ char common[sizeof (struct tree_common)];
+ char *filename;
+ int linenum;
+ union tree_node *size;
+ unsigned int uid;
+#ifdef ONLY_INT_FIELDS
+ int mode : 8;
+#else
+ enum machine_mode mode : 8;
+#endif
+
+ unsigned external_flag : 1;
+ unsigned nonlocal_flag : 1;
+ unsigned regdecl_flag : 1;
+ unsigned inline_flag : 1;
+ unsigned bit_field_flag : 1;
+ unsigned virtual_flag : 1;
+ unsigned ignored_flag : 1;
+ unsigned abstract_flag : 1;
+
+ unsigned in_system_header_flag : 1;
+ unsigned common_flag : 1;
+ unsigned defer_output : 1;
+ /* room for five more */
+
+ unsigned lang_flag_0 : 1;
+ unsigned lang_flag_1 : 1;
+ unsigned lang_flag_2 : 1;
+ unsigned lang_flag_3 : 1;
+ unsigned lang_flag_4 : 1;
+ unsigned lang_flag_5 : 1;
+ unsigned lang_flag_6 : 1;
+ unsigned lang_flag_7 : 1;
+
+ union tree_node *name;
+ union tree_node *context;
+ union tree_node *arguments;
+ union tree_node *result;
+ union tree_node *initial;
+ union tree_node *abstract_origin;
+ union tree_node *assembler_name;
+ union tree_node *section_name;
+ struct rtx_def *rtl; /* acts as link to register transfer language
+ (rtl) info */
+ /* For a FUNCTION_DECL, if inline, this is the size of frame needed.
+ If built-in, this is the code for which built-in function.
+ For other kinds of decls, this is DECL_ALIGN. */
+ union {
+ int i;
+ unsigned int u;
+ enum built_in_function f;
+ } frame_size;
+ /* For FUNCTION_DECLs: points to insn that constitutes its definition
+ on the permanent obstack. For any other kind of decl, this is the
+ alignment. */
+ union {
+ struct rtx_def *r;
+ int i;
+ } saved_insns;
+ union tree_node *vindex;
+ /* Points to a structure whose details depend on the language in use. */
+ struct lang_decl *lang_specific;
+};
+
+/* Define the overall contents of a tree node.
+ It may be any of the structures declared above
+ for various types of node. */
+
+union tree_node
+{
+ struct tree_common common;
+ struct tree_int_cst int_cst;
+ struct tree_real_cst real_cst;
+ struct tree_string string;
+ struct tree_complex complex;
+ struct tree_identifier identifier;
+ struct tree_decl decl;
+ struct tree_type type;
+ struct tree_list list;
+ struct tree_vec vec;
+ struct tree_exp exp;
+ struct tree_block block;
+ };
+
+/* Add prototype support. */
+#ifndef PROTO
+#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
+#define PROTO(ARGS) ARGS
+#else
+#define PROTO(ARGS) ()
+#endif
+#endif
+
+#ifndef VPROTO
+#ifdef __STDC__
+#define PVPROTO(ARGS) ARGS
+#define VPROTO(ARGS) ARGS
+#define VA_START(va_list,var) va_start(va_list,var)
+#else
+#define PVPROTO(ARGS) ()
+#define VPROTO(ARGS) (va_alist) va_dcl
+#define VA_START(va_list,var) va_start(va_list)
+#endif
+#endif
+
+#ifndef STDIO_PROTO
+#ifdef BUFSIZ
+#define STDIO_PROTO(ARGS) PROTO(ARGS)
+#else
+#define STDIO_PROTO(ARGS) ()
+#endif
+#endif
+
+#define NULL_TREE (tree) NULL
+
+/* Define a generic NULL if one hasn't already been defined. */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef GENERIC_PTR
+#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
+#define GENERIC_PTR void *
+#else
+#define GENERIC_PTR char *
+#endif
+#endif
+
+#ifndef NULL_PTR
+#define NULL_PTR ((GENERIC_PTR)0)
+#endif
+
+/* The following functions accept a wide integer argument. Rather than
+ having to cast on every function call, we use a macro instead, that is
+ defined here and in rtl.h. */
+
+#ifndef exact_log2
+#define exact_log2(N) exact_log2_wide ((HOST_WIDE_INT) (N))
+#define floor_log2(N) floor_log2_wide ((HOST_WIDE_INT) (N))
+#endif
+
+#if 0
+/* At present, don't prototype xrealloc, since all of the callers don't
+ cast their pointers to char *, and all of the xrealloc's don't use
+ void * yet. */
+extern char *xmalloc PROTO((size_t));
+extern char *xrealloc PROTO((void *, size_t));
+#else
+extern char *xmalloc ();
+extern char *xrealloc ();
+#endif
+
+extern char *oballoc PROTO((int));
+extern char *permalloc PROTO((int));
+extern char *savealloc PROTO((int));
+extern void free PROTO((void *));
+
+/* Lowest level primitive for allocating a node.
+ The TREE_CODE is the only argument. Contents are initialized
+ to zero except for a few of the common fields. */
+
+extern tree make_node PROTO((enum tree_code));
+
+/* Make a copy of a node, with all the same contents except
+ for TREE_PERMANENT. (The copy is permanent
+ iff nodes being made now are permanent.) */
+
+extern tree copy_node PROTO((tree));
+
+/* Make a copy of a chain of TREE_LIST nodes. */
+
+extern tree copy_list PROTO((tree));
+
+/* Make a TREE_VEC. */
+
+extern tree make_tree_vec PROTO((int));
+
+/* Return the (unique) IDENTIFIER_NODE node for a given name.
+ The name is supplied as a char *. */
+
+extern tree get_identifier PROTO((char *));
+
+/* Construct various types of nodes. */
+
+#define build_int_2(LO,HI) \
+ build_int_2_wide ((HOST_WIDE_INT) (LO), (HOST_WIDE_INT) (HI))
+
+extern tree build PVPROTO((enum tree_code, tree, ...));
+extern tree build_nt PVPROTO((enum tree_code, ...));
+extern tree build_parse_node PVPROTO((enum tree_code, ...));
+
+extern tree build_int_2_wide PROTO((HOST_WIDE_INT, HOST_WIDE_INT));
+extern tree build_real PROTO((tree, REAL_VALUE_TYPE));
+extern tree build_real_from_int_cst PROTO((tree, tree));
+extern tree build_complex PROTO((tree, tree));
+extern tree build_string PROTO((int, char *));
+extern tree build1 PROTO((enum tree_code, tree, tree));
+extern tree build_tree_list PROTO((tree, tree));
+extern tree build_decl_list PROTO((tree, tree));
+extern tree build_decl PROTO((enum tree_code, tree, tree));
+extern tree build_block PROTO((tree, tree, tree, tree, tree));
+
+/* Construct various nodes representing data types. */
+
+extern tree make_signed_type PROTO((int));
+extern tree make_unsigned_type PROTO((int));
+extern tree signed_or_unsigned_type PROTO((int, tree));
+extern void fixup_unsigned_type PROTO((tree));
+extern tree build_pointer_type PROTO((tree));
+extern tree build_reference_type PROTO((tree));
+extern tree build_index_type PROTO((tree));
+extern tree build_index_2_type PROTO((tree, tree));
+extern tree build_array_type PROTO((tree, tree));
+extern tree build_function_type PROTO((tree, tree));
+extern tree build_method_type PROTO((tree, tree));
+extern tree build_offset_type PROTO((tree, tree));
+extern tree build_complex_type PROTO((tree));
+extern tree array_type_nelts PROTO((tree));
+
+extern tree value_member PROTO((tree, tree));
+extern tree purpose_member PROTO((tree, tree));
+extern tree binfo_member PROTO((tree, tree));
+extern int attribute_list_equal PROTO((tree, tree));
+extern int attribute_list_contained PROTO((tree, tree));
+extern int tree_int_cst_equal PROTO((tree, tree));
+extern int tree_int_cst_lt PROTO((tree, tree));
+extern int tree_int_cst_sgn PROTO((tree));
+extern int index_type_equal PROTO((tree, tree));
+
+/* From expmed.c. Since rtl.h is included after tree.h, we can't
+ put the prototype here. Rtl.h does declare the prototype if
+ tree.h had been included. */
+
+extern tree make_tree ();
+
+/* Return a type like TTYPE except that its TYPE_ATTRIBUTES
+ is ATTRIBUTE.
+
+ Such modified types already made are recorded so that duplicates
+ are not made. */
+
+extern tree build_type_attribute_variant PROTO((tree, tree));
+
+/* Given a type node TYPE, and CONSTP and VOLATILEP, return a type
+ for the same kind of data as TYPE describes.
+ Variants point to the "main variant" (which has neither CONST nor VOLATILE)
+ via TYPE_MAIN_VARIANT, and it points to a chain of other variants
+ so that duplicate variants are never made.
+ Only main variants should ever appear as types of expressions. */
+
+extern tree build_type_variant PROTO((tree, int, int));
+
+/* Make a copy of a type node. */
+
+extern tree build_type_copy PROTO((tree));
+
+/* Given a ..._TYPE node, calculate the TYPE_SIZE, TYPE_SIZE_UNIT,
+ TYPE_ALIGN and TYPE_MODE fields.
+ If called more than once on one node, does nothing except
+ for the first time. */
+
+extern void layout_type PROTO((tree));
+
+/* Given a hashcode and a ..._TYPE node (for which the hashcode was made),
+ return a canonicalized ..._TYPE node, so that duplicates are not made.
+ How the hash code is computed is up to the caller, as long as any two
+ callers that could hash identical-looking type nodes agree. */
+
+extern tree type_hash_canon PROTO((int, tree));
+
+/* Given a VAR_DECL, PARM_DECL, RESULT_DECL or FIELD_DECL node,
+ calculates the DECL_SIZE, DECL_SIZE_UNIT, DECL_ALIGN and DECL_MODE
+ fields. Call this only once for any given decl node.
+
+ Second argument is the boundary that this field can be assumed to
+ be starting at (in bits). Zero means it can be assumed aligned
+ on any boundary that may be needed. */
+
+extern void layout_decl PROTO((tree, unsigned));
+
+/* Return an expr equal to X but certainly not valid as an lvalue. */
+
+extern tree non_lvalue PROTO((tree));
+extern tree pedantic_non_lvalue PROTO((tree));
+
+extern tree convert PROTO((tree, tree));
+extern tree size_in_bytes PROTO((tree));
+extern int int_size_in_bytes PROTO((tree));
+extern tree size_binop PROTO((enum tree_code, tree, tree));
+extern tree size_int PROTO((unsigned));
+extern tree round_up PROTO((tree, int));
+extern tree get_pending_sizes PROTO((void));
+
+/* Type for sizes of data-type. */
+
+extern tree sizetype;
+
+/* Concatenate two lists (chains of TREE_LIST nodes) X and Y
+ by making the last node in X point to Y.
+ Returns X, except if X is 0 returns Y. */
+
+extern tree chainon PROTO((tree, tree));
+
+/* Make a new TREE_LIST node from specified PURPOSE, VALUE and CHAIN. */
+
+extern tree tree_cons PROTO((tree, tree, tree));
+extern tree perm_tree_cons PROTO((tree, tree, tree));
+extern tree temp_tree_cons PROTO((tree, tree, tree));
+extern tree saveable_tree_cons PROTO((tree, tree, tree));
+extern tree decl_tree_cons PROTO((tree, tree, tree));
+
+/* Return the last tree node in a chain. */
+
+extern tree tree_last PROTO((tree));
+
+/* Reverse the order of elements in a chain, and return the new head. */
+
+extern tree nreverse PROTO((tree));
+
+/* Returns the length of a chain of nodes
+ (number of chain pointers to follow before reaching a null pointer). */
+
+extern int list_length PROTO((tree));
+
+/* integer_zerop (tree x) is nonzero if X is an integer constant of value 0 */
+
+extern int integer_zerop PROTO((tree));
+
+/* integer_onep (tree x) is nonzero if X is an integer constant of value 1 */
+
+extern int integer_onep PROTO((tree));
+
+/* integer_all_onesp (tree x) is nonzero if X is an integer constant
+ all of whose significant bits are 1. */
+
+extern int integer_all_onesp PROTO((tree));
+
+/* integer_pow2p (tree x) is nonzero is X is an integer constant with
+ exactly one bit 1. */
+
+extern int integer_pow2p PROTO((tree));
+
+/* staticp (tree x) is nonzero if X is a reference to data allocated
+ at a fixed address in memory. */
+
+extern int staticp PROTO((tree));
+
+/* Gets an error if argument X is not an lvalue.
+ Also returns 1 if X is an lvalue, 0 if not. */
+
+extern int lvalue_or_else PROTO((tree, char *));
+
+/* save_expr (EXP) returns an expression equivalent to EXP
+ but it can be used multiple times within context CTX
+ and only evaluate EXP once. */
+
+extern tree save_expr PROTO((tree));
+
+/* Return 1 if EXP contains a PLACEHOLDER_EXPR; i.e., if it represents a size
+ or offset that depends on a field within a record.
+
+ Note that we only allow such expressions within simple arithmetic
+ or a COND_EXPR. */
+
+extern int contains_placeholder_p PROTO((tree));
+
+/* Given a tree EXP, a FIELD_DECL F, and a replacement value R,
+ return a tree with all occurrences of references to F in a
+ PLACEHOLDER_EXPR replaced by R. Note that we assume here that EXP
+ contains only arithmetic expressions. */
+
+extern tree substitute_in_expr PROTO((tree, tree, tree));
+
+/* Given a type T, a FIELD_DECL F, and a replacement value R,
+ return a new type with all size expressions that contain F
+ updated by replacing the reference to F with R. */
+
+extern tree substitute_in_type PROTO((tree, tree, tree));
+
+/* variable_size (EXP) is like save_expr (EXP) except that it
+ is for the special case of something that is part of a
+ variable size for a data type. It makes special arrangements
+ to compute the value at the right time when the data type
+ belongs to a function parameter. */
+
+extern tree variable_size PROTO((tree));
+
+/* stabilize_reference (EXP) returns an reference equivalent to EXP
+ but it can be used multiple times
+ and only evaluate the subexpressions once. */
+
+extern tree stabilize_reference PROTO((tree));
+
+/* Return EXP, stripped of any conversions to wider types
+ in such a way that the result of converting to type FOR_TYPE
+ is the same as if EXP were converted to FOR_TYPE.
+ If FOR_TYPE is 0, it signifies EXP's type. */
+
+extern tree get_unwidened PROTO((tree, tree));
+
+/* Return OP or a simpler expression for a narrower value
+ which can be sign-extended or zero-extended to give back OP.
+ Store in *UNSIGNEDP_PTR either 1 if the value should be zero-extended
+ or 0 if the value should be sign-extended. */
+
+extern tree get_narrower PROTO((tree, int *));
+
+/* Given MODE and UNSIGNEDP, return a suitable type-tree
+ with that mode.
+ The definition of this resides in language-specific code
+ as the repertoire of available types may vary. */
+
+extern tree type_for_mode PROTO((enum machine_mode, int));
+
+/* Given PRECISION and UNSIGNEDP, return a suitable type-tree
+ for an integer type with at least that precision.
+ The definition of this resides in language-specific code
+ as the repertoire of available types may vary. */
+
+extern tree type_for_size PROTO((unsigned, int));
+
+/* Given an integer type T, return a type like T but unsigned.
+ If T is unsigned, the value is T.
+ The definition of this resides in language-specific code
+ as the repertoire of available types may vary. */
+
+extern tree unsigned_type PROTO((tree));
+
+/* Given an integer type T, return a type like T but signed.
+ If T is signed, the value is T.
+ The definition of this resides in language-specific code
+ as the repertoire of available types may vary. */
+
+extern tree signed_type PROTO((tree));
+
+/* This function must be defined in the language-specific files.
+ expand_expr calls it to build the cleanup-expression for a TARGET_EXPR.
+ This is defined in a language-specific file. */
+
+extern tree maybe_build_cleanup PROTO((tree));
+
+/* Given an expression EXP that may be a COMPONENT_REF or an ARRAY_REF,
+ look for nested component-refs or array-refs at constant positions
+ and find the ultimate containing object, which is returned. */
+
+extern tree get_inner_reference PROTO((tree, int *, int *, tree *, enum machine_mode *, int *, int *));
+
+/* Return the FUNCTION_DECL which provides this _DECL with its context,
+ or zero if none. */
+extern tree decl_function_context PROTO((tree));
+
+/* Return the RECORD_TYPE, UNION_TYPE, or QUAL_UNION_TYPE which provides
+ this _DECL with its context, or zero if none. */
+extern tree decl_type_context PROTO((tree));
+
+/* Given the FUNCTION_DECL for the current function,
+ return zero if it is ok for this function to be inline.
+ Otherwise return a warning message with a single %s
+ for the function's name. */
+
+extern char *function_cannot_inline_p PROTO((tree));
+
+/* Return 1 if EXPR is the real constant zero. */
+extern int real_zerop PROTO((tree));
+
+/* Declare commonly used variables for tree structure. */
+
+/* An integer constant with value 0 */
+extern tree integer_zero_node;
+
+/* An integer constant with value 1 */
+extern tree integer_one_node;
+
+/* An integer constant with value 0 whose type is sizetype. */
+extern tree size_zero_node;
+
+/* An integer constant with value 1 whose type is sizetype. */
+extern tree size_one_node;
+
+/* A constant of type pointer-to-int and value 0 */
+extern tree null_pointer_node;
+
+/* A node of type ERROR_MARK. */
+extern tree error_mark_node;
+
+/* The type node for the void type. */
+extern tree void_type_node;
+
+/* The type node for the ordinary (signed) integer type. */
+extern tree integer_type_node;
+
+/* The type node for the unsigned integer type. */
+extern tree unsigned_type_node;
+
+/* The type node for the ordinary character type. */
+extern tree char_type_node;
+
+/* Points to the name of the input file from which the current input
+ being parsed originally came (before it went into cpp). */
+extern char *input_filename;
+
+/* Current line number in input file. */
+extern int lineno;
+
+/* Nonzero for -pedantic switch: warn about anything
+ that standard C forbids. */
+extern int pedantic;
+
+/* Nonzero means can safely call expand_expr now;
+ otherwise layout_type puts variable sizes onto `pending_sizes' instead. */
+
+extern int immediate_size_expand;
+
+/* Points to the FUNCTION_DECL of the function whose body we are reading. */
+
+extern tree current_function_decl;
+
+/* Nonzero if function being compiled can call setjmp. */
+
+extern int current_function_calls_setjmp;
+
+/* Nonzero if function being compiled can call longjmp. */
+
+extern int current_function_calls_longjmp;
+
+/* Nonzero means all ..._TYPE nodes should be allocated permanently. */
+
+extern int all_types_permanent;
+
+/* Pointer to function to compute the name to use to print a declaration. */
+
+extern char *(*decl_printable_name) ();
+
+/* Pointer to function to finish handling an incomplete decl at the
+ end of compilation. */
+
+extern void (*incomplete_decl_finalize_hook) ();
+
+/* In tree.c */
+extern char *perm_calloc PROTO((int, long));
+
+/* In stmt.c */
+
+extern void expand_fixups PROTO((struct rtx_def *));
+extern tree expand_start_stmt_expr PROTO((void));
+extern tree expand_end_stmt_expr PROTO((tree));
+extern void expand_expr_stmt PROTO((tree));
+extern void expand_decl_init PROTO((tree));
+extern void clear_last_expr PROTO((void));
+extern void expand_label PROTO((tree));
+extern void expand_goto PROTO((tree));
+extern void expand_asm PROTO((tree));
+extern void expand_start_cond PROTO((tree, int));
+extern void expand_end_cond PROTO((void));
+extern void expand_start_else PROTO((void));
+extern void expand_start_elseif PROTO((tree));
+extern struct nesting *expand_start_loop PROTO((int));
+extern struct nesting *expand_start_loop_continue_elsewhere PROTO((int));
+extern void expand_loop_continue_here PROTO((void));
+extern void expand_end_loop PROTO((void));
+extern int expand_continue_loop PROTO((struct nesting *));
+extern int expand_exit_loop PROTO((struct nesting *));
+extern int expand_exit_loop_if_false PROTO((struct nesting *,
+ tree));
+extern int expand_exit_something PROTO((void));
+
+extern void expand_null_return PROTO((void));
+extern void expand_return PROTO((tree));
+extern void expand_start_bindings PROTO((int));
+extern void expand_end_bindings PROTO((tree, int, int));
+extern tree last_cleanup_this_contour PROTO((void));
+extern void expand_start_case PROTO((int, tree, tree,
+ char *));
+extern void expand_end_case PROTO((tree));
+extern int pushcase PROTO((tree,
+ tree (*) (tree, tree),
+ tree, tree *));
+extern int pushcase_range PROTO((tree, tree,
+ tree (*) (tree, tree),
+ tree, tree *));
+
+/* In fold-const.c */
+
+/* Fold constants as much as possible in an expression.
+ Returns the simplified expression.
+ Acts only on the top level of the expression;
+ if the argument itself cannot be simplified, its
+ subexpressions are not changed. */
+
+extern tree fold PROTO((tree));
+
+extern int force_fit_type PROTO((tree, int));
+extern int add_double PROTO((HOST_WIDE_INT, HOST_WIDE_INT,
+ HOST_WIDE_INT, HOST_WIDE_INT,
+ HOST_WIDE_INT *, HOST_WIDE_INT *));
+extern int neg_double PROTO((HOST_WIDE_INT, HOST_WIDE_INT,
+ HOST_WIDE_INT *, HOST_WIDE_INT *));
+extern int mul_double PROTO((HOST_WIDE_INT, HOST_WIDE_INT,
+ HOST_WIDE_INT, HOST_WIDE_INT,
+ HOST_WIDE_INT *, HOST_WIDE_INT *));
+extern void lshift_double PROTO((HOST_WIDE_INT, HOST_WIDE_INT,
+ HOST_WIDE_INT, int, HOST_WIDE_INT *,
+ HOST_WIDE_INT *, int));
+extern void rshift_double PROTO((HOST_WIDE_INT, HOST_WIDE_INT,
+ HOST_WIDE_INT, int,
+ HOST_WIDE_INT *, HOST_WIDE_INT *, int));
+extern void lrotate_double PROTO((HOST_WIDE_INT, HOST_WIDE_INT,
+ HOST_WIDE_INT, int, HOST_WIDE_INT *,
+ HOST_WIDE_INT *));
+extern void rrotate_double PROTO((HOST_WIDE_INT, HOST_WIDE_INT,
+ HOST_WIDE_INT, int, HOST_WIDE_INT *,
+ HOST_WIDE_INT *));
+extern int operand_equal_p PROTO((tree, tree, int));
+extern tree invert_truthvalue PROTO((tree));
+
+/* The language front-end must define these functions. */
+
+/* Function of no arguments for initializing lexical scanning. */
+extern void init_lex PROTO((void));
+/* Function of no arguments for initializing the symbol table. */
+extern void init_decl_processing PROTO((void));
+
+/* Functions called with no arguments at the beginning and end or processing
+ the input source file. */
+extern void lang_init PROTO((void));
+extern void lang_finish PROTO((void));
+
+/* Funtion to identify which front-end produced the output file. */
+extern char *lang_identify PROTO((void));
+
+/* Function to replace the DECL_LANG_SPECIFIC field of a DECL with a copy. */
+extern void copy_lang_decl PROTO((tree));
+
+/* Function called with no arguments to parse and compile the input. */
+extern int yyparse PROTO((void));
+/* Function called with option as argument
+ to decode options starting with -f or -W or +.
+ It should return nonzero if it handles the option. */
+extern int lang_decode_option PROTO((char *));
+
+/* Functions for processing symbol declarations. */
+/* Function to enter a new lexical scope.
+ Takes one argument: always zero when called from outside the front end. */
+extern void pushlevel PROTO((int));
+/* Function to exit a lexical scope. It returns a BINDING for that scope.
+ Takes three arguments:
+ KEEP -- nonzero if there were declarations in this scope.
+ REVERSE -- reverse the order of decls before returning them.
+ FUNCTIONBODY -- nonzero if this level is the body of a function. */
+extern tree poplevel PROTO((int, int, int));
+/* Set the BLOCK node for the current scope level. */
+extern void set_block PROTO((tree));
+/* Function to add a decl to the current scope level.
+ Takes one argument, a decl to add.
+ Returns that decl, or, if the same symbol is already declared, may
+ return a different decl for that name. */
+extern tree pushdecl PROTO((tree));
+/* Function to return the chain of decls so far in the current scope level. */
+extern tree getdecls PROTO((void));
+/* Function to return the chain of structure tags in the current scope level. */
+extern tree gettags PROTO((void));
+
+extern tree build_range_type PROTO((tree, tree, tree));
+
+/* Call when starting to parse a declaration:
+ make expressions in the declaration last the length of the function.
+ Returns an argument that should be passed to resume_momentary later. */
+extern int suspend_momentary PROTO((void));
+
+extern int allocation_temporary_p PROTO((void));
+
+/* Call when finished parsing a declaration:
+ restore the treatment of node-allocation that was
+ in effect before the suspension.
+ YES should be the value previously returned by suspend_momentary. */
+extern void resume_momentary PROTO((int));
+
+/* Called after finishing a record, union or enumeral type. */
+extern void rest_of_type_compilation PROTO((tree, int));
+
+/* Save the current set of obstacks, but don't change them. */
+extern void push_obstacks_nochange PROTO((void));
+
+extern void permanent_allocation PROTO((int));
+
+extern void push_momentary PROTO((void));
+
+extern void clear_momentary PROTO((void));
+
+extern void pop_momentary PROTO((void));
+
+extern void end_temporary_allocation PROTO((void));
+
+/* Pop the obstack selection stack. */
+extern void pop_obstacks PROTO((void));
diff --git a/gnu/usr.bin/cc/include/typeclass.h b/gnu/usr.bin/cc/include/typeclass.h
new file mode 100644
index 0000000..b166042
--- /dev/null
+++ b/gnu/usr.bin/cc/include/typeclass.h
@@ -0,0 +1,14 @@
+/* Values returned by __builtin_classify_type. */
+
+enum type_class
+{
+ no_type_class = -1,
+ void_type_class, integer_type_class, char_type_class,
+ enumeral_type_class, boolean_type_class,
+ pointer_type_class, reference_type_class, offset_type_class,
+ real_type_class, complex_type_class,
+ function_type_class, method_type_class,
+ record_type_class, union_type_class,
+ array_type_class, string_type_class, set_type_class, file_type_class,
+ lang_type_class
+};
diff --git a/gnu/usr.bin/cc/legal/gen-protos.c b/gnu/usr.bin/cc/legal/gen-protos.c
new file mode 100644
index 0000000..094ce2a
--- /dev/null
+++ b/gnu/usr.bin/cc/legal/gen-protos.c
@@ -0,0 +1,155 @@
+/* gen-protos.c - massages a list of prototypes, for use by fixproto.
+ Copyright (C) 1993, 1994 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "hconfig.h"
+#include "scan.h"
+
+#define HASH_SIZE 2503 /* a prime */
+
+int hash_tab[HASH_SIZE];
+int verbose = 0;
+
+sstring linebuf;
+
+/* Avoid error if config defines abort as fancy_abort.
+ It's not worth "really" implementing this because ordinary
+ compiler users never run fix-header. */
+
+void
+fancy_abort ()
+{
+ abort ();
+}
+
+int
+main (argc, argv)
+ int argc;
+ char** argv;
+{
+ FILE *inf = stdin;
+ FILE *outf = stdout;
+ int next_index = 0;
+ int i, i0;
+
+ fprintf (outf, "struct fn_decl std_protos[] = {\n");
+
+ for (;;)
+ {
+ int c = skip_spaces (inf, ' ');
+ int param_nesting = 1;
+ char *param_start, *param_end, *decl_start,
+ *name_start, *name_end;
+ register char *ptr;
+ if (c == EOF)
+ break;
+ linebuf.ptr = linebuf.base;
+ ungetc (c, inf);
+ c = read_upto (inf, &linebuf, '\n');
+ if (linebuf.base[0] == '#') /* skip cpp command */
+ continue;
+ if (linebuf.base[0] == '\0') /* skip empty line */
+ continue;
+
+ ptr = linebuf.ptr - 1;
+ while (*ptr == ' ' || *ptr == '\t') ptr--;
+ if (*ptr-- != ';')
+ {
+ fprintf (stderr, "Funny input line: %s\n", linebuf.base);
+ continue;
+ }
+ while (*ptr == ' ' || *ptr == '\t') ptr--;
+ if (*ptr != ')')
+ {
+ fprintf (stderr, "Funny input line: %s\n", linebuf.base);
+ continue;
+ }
+ param_end = ptr;
+ for (;;)
+ {
+ int c = *--ptr;
+ if (c == '(' && --param_nesting == 0)
+ break;
+ else if (c == ')')
+ param_nesting++;
+ }
+ param_start = ptr+1;
+
+ ptr--;
+ while (*ptr == ' ' || *ptr == '\t') ptr--;
+
+ if (!isalnum (*ptr))
+ {
+ if (verbose)
+ fprintf (stderr, "%s: Can't handle this complex prototype: %s\n",
+ argv[0], linebuf.base);
+ continue;
+ }
+ name_end = ptr+1;
+
+ while (isalnum (*ptr) || *ptr == '_') --ptr;
+ name_start = ptr+1;
+ while (*ptr == ' ' || *ptr == '\t') ptr--;
+ ptr[1] = 0;
+ *name_end = 0;
+ *param_end = 0;
+ *name_end = 0;
+
+ decl_start = linebuf.base;
+ if (strncmp (decl_start, "typedef ", 8) == 0)
+ continue;
+ if (strncmp (decl_start, "extern ", 7) == 0)
+ decl_start += 7;
+
+
+ /* NOTE: If you edit this,
+ also edit lookup_std_proto in fix-header.c !! */
+ i = hash (name_start) % HASH_SIZE;
+ i0 = i;
+ if (hash_tab[i] != 0)
+ {
+ for (;;)
+ {
+ i = (i+1) % HASH_SIZE;
+ if (i == i0)
+ abort ();
+ if (hash_tab[i] == 0)
+ break;
+ }
+ }
+ hash_tab[i] = next_index;
+
+ fprintf (outf, " {\"%s\", \"%s\", \"%s\" },\n",
+ name_start, decl_start, param_start);
+
+ next_index++;
+
+ if (c == EOF)
+ break;
+ }
+ fprintf (outf, "{0, 0, 0}\n};\n");
+
+
+ fprintf (outf, "#define HASH_SIZE %d\n", HASH_SIZE);
+ fprintf (outf, "short hash_tab[HASH_SIZE] = {\n");
+ for (i = 0; i < HASH_SIZE; i++)
+ fprintf (outf, " %d,\n", hash_tab[i]);
+ fprintf (outf, "};\n");
+
+ return 0;
+}
diff --git a/gnu/usr.bin/cc/legal/md b/gnu/usr.bin/cc/legal/md
new file mode 100644
index 0000000..3e43fb0
--- /dev/null
+++ b/gnu/usr.bin/cc/legal/md
@@ -0,0 +1,5295 @@
+;; GCC machine description for Intel 80386.
+;; Copyright (C) 1988, 1994 Free Software Foundation, Inc.
+;; Mostly by William Schelter.
+
+;; This file is part of GNU CC.
+
+;; GNU CC is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU CC is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU CC; see the file COPYING. If not, write to
+;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+;; The original PO technology requires these to be ordered by speed,
+;; so that assigner will pick the fastest.
+
+;; See file "rtl.def" for documentation on define_insn, match_*, et. al.
+
+;; Macro #define NOTICE_UPDATE_CC in file i386.h handles condition code
+;; updates for most instructions.
+
+;; Macro REG_CLASS_FROM_LETTER in file i386.h defines the register
+;; constraint letters.
+
+;; the special asm out single letter directives following a '%' are:
+;; 'z' mov%z1 would be movl, movw, or movb depending on the mode of
+;; operands[1].
+;; 'L' Print the opcode suffix for a 32-bit integer opcode.
+;; 'W' Print the opcode suffix for a 16-bit integer opcode.
+;; 'B' Print the opcode suffix for an 8-bit integer opcode.
+;; 'S' Print the opcode suffix for a 32-bit float opcode.
+;; 'Q' Print the opcode suffix for a 64-bit float opcode.
+
+;; 'b' Print the QImode name of the register for the indicated operand.
+;; %b0 would print %al if operands[0] is reg 0.
+;; 'w' Likewise, print the HImode name of the register.
+;; 'k' Likewise, print the SImode name of the register.
+;; 'h' Print the QImode name for a "high" register, either ah, bh, ch or dh.
+;; 'y' Print "st(0)" instead of "st" as a register.
+;; 'T' Print the opcode suffix for an 80-bit extended real XFmode float opcode.
+
+;; UNSPEC usage:
+;; 0 This is a `scas' operation. The mode of the UNSPEC is always SImode.
+;; operand 0 is the memory address to scan.
+;; operand 1 is a register containing the value to scan for. The mode
+;; of the scas opcode will be the same as the mode of this operand.
+;; operand 2 is the known alignment of operand 0.
+;; 1 This is a `sin' operation. The mode of the UNSPEC is MODE_FLOAT.
+;; operand 0 is the argument for `sin'.
+;; 2 This is a `cos' operation. The mode of the UNSPEC is MODE_FLOAT.
+;; operand 0 is the argument for `cos'.
+
+;; "movl MEM,REG / testl REG,REG" is faster on a 486 than "cmpl $0,MEM".
+;; But restricting MEM here would mean that gcc could not remove a redundant
+;; test in cases like "incl MEM / je TARGET".
+;;
+;; We don't want to allow a constant operand for test insns because
+;; (set (cc0) (const_int foo)) has no mode information. Such insns will
+;; be folded while optimizing anyway.
+
+;; All test insns have expanders that save the operands away without
+;; actually generating RTL. The bCOND or sCOND (emitted immediately
+;; after the tstM or cmp) will actually emit the tstM or cmpM.
+
+(define_insn "tstsi_1"
+ [(set (cc0)
+ (match_operand:SI 0 "nonimmediate_operand" "rm"))]
+ ""
+ "*
+{
+ if (REG_P (operands[0]))
+ return AS2 (test%L0,%0,%0);
+
+ operands[1] = const0_rtx;
+ return AS2 (cmp%L0,%1,%0);
+}")
+
+(define_expand "tstsi"
+ [(set (cc0)
+ (match_operand:SI 0 "nonimmediate_operand" ""))]
+ ""
+ "
+{
+ i386_compare_gen = gen_tstsi_1;
+ i386_compare_op0 = operands[0];
+ DONE;
+}")
+
+(define_insn "tsthi_1"
+ [(set (cc0)
+ (match_operand:HI 0 "nonimmediate_operand" "rm"))]
+ ""
+ "*
+{
+ if (REG_P (operands[0]))
+ return AS2 (test%W0,%0,%0);
+
+ operands[1] = const0_rtx;
+ return AS2 (cmp%W0,%1,%0);
+}")
+
+(define_expand "tsthi"
+ [(set (cc0)
+ (match_operand:HI 0 "nonimmediate_operand" ""))]
+ ""
+ "
+{
+ i386_compare_gen = gen_tsthi_1;
+ i386_compare_op0 = operands[0];
+ DONE;
+}")
+
+(define_insn "tstqi_1"
+ [(set (cc0)
+ (match_operand:QI 0 "nonimmediate_operand" "qm"))]
+ ""
+ "*
+{
+ if (REG_P (operands[0]))
+ return AS2 (test%B0,%0,%0);
+
+ operands[1] = const0_rtx;
+ return AS2 (cmp%B0,%1,%0);
+}")
+
+(define_expand "tstqi"
+ [(set (cc0)
+ (match_operand:QI 0 "nonimmediate_operand" ""))]
+ ""
+ "
+{
+ i386_compare_gen = gen_tstqi_1;
+ i386_compare_op0 = operands[0];
+ DONE;
+}")
+
+(define_insn "tstsf_cc"
+ [(set (cc0)
+ (match_operand:SF 0 "register_operand" "f"))
+ (clobber (match_scratch:HI 1 "=a"))]
+ "TARGET_80387 && ! TARGET_IEEE_FP"
+ "*
+{
+ if (! STACK_TOP_P (operands[0]))
+ abort ();
+
+ output_asm_insn (\"ftst\", operands);
+
+ if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
+ output_asm_insn (AS1 (fstp,%y0), operands);
+
+ return (char *) output_fp_cc0_set (insn);
+}")
+
+;; Don't generate tstsf if generating IEEE code, since the `ftst' opcode
+;; isn't IEEE compliant.
+
+(define_expand "tstsf"
+ [(parallel [(set (cc0)
+ (match_operand:SF 0 "register_operand" ""))
+ (clobber (match_scratch:HI 1 ""))])]
+ "TARGET_80387 && ! TARGET_IEEE_FP"
+ "
+{
+ i386_compare_gen = gen_tstsf_cc;
+ i386_compare_op0 = operands[0];
+ DONE;
+}")
+
+(define_insn "tstdf_cc"
+ [(set (cc0)
+ (match_operand:DF 0 "register_operand" "f"))
+ (clobber (match_scratch:HI 1 "=a"))]
+ "TARGET_80387 && ! TARGET_IEEE_FP"
+ "*
+{
+ if (! STACK_TOP_P (operands[0]))
+ abort ();
+
+ output_asm_insn (\"ftst\", operands);
+
+ if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
+ output_asm_insn (AS1 (fstp,%y0), operands);
+
+ return (char *) output_fp_cc0_set (insn);
+}")
+
+;; Don't generate tstdf if generating IEEE code, since the `ftst' opcode
+;; isn't IEEE compliant.
+
+(define_expand "tstdf"
+ [(parallel [(set (cc0)
+ (match_operand:DF 0 "register_operand" ""))
+ (clobber (match_scratch:HI 1 ""))])]
+ "TARGET_80387 && ! TARGET_IEEE_FP"
+ "
+{
+ i386_compare_gen = gen_tstdf_cc;
+ i386_compare_op0 = operands[0];
+ DONE;
+}")
+
+(define_insn "tstxf_cc"
+ [(set (cc0)
+ (match_operand:XF 0 "register_operand" "f"))
+ (clobber (match_scratch:HI 1 "=a"))]
+ "TARGET_80387 && ! TARGET_IEEE_FP"
+ "*
+{
+ if (! STACK_TOP_P (operands[0]))
+ abort ();
+
+ output_asm_insn (\"ftst\", operands);
+
+ if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
+ output_asm_insn (AS1 (fstp,%y0), operands);
+
+ return (char *) output_fp_cc0_set (insn);
+}")
+
+;; Don't generate tstdf if generating IEEE code, since the `ftst' opcode
+;; isn't IEEE compliant.
+
+(define_expand "tstxf"
+ [(parallel [(set (cc0)
+ (match_operand:XF 0 "register_operand" ""))
+ (clobber (match_scratch:HI 1 ""))])]
+ "TARGET_80387 && ! TARGET_IEEE_FP"
+ "
+{
+ i386_compare_gen = gen_tstxf_cc;
+ i386_compare_op0 = operands[0];
+ DONE;
+}")
+
+;;- compare instructions. See comments above tstM patterns about
+;; expansion of these insns.
+
+(define_insn "cmpsi_1"
+ [(set (cc0)
+ (compare (match_operand:SI 0 "nonimmediate_operand" "mr,r")
+ (match_operand:SI 1 "general_operand" "ri,mr")))]
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+ "*
+{
+ if (CONSTANT_P (operands[0]) || GET_CODE (operands[1]) == MEM)
+ {
+ cc_status.flags |= CC_REVERSED;
+ return AS2 (cmp%L0,%0,%1);
+ }
+ return AS2 (cmp%L0,%1,%0);
+}")
+
+(define_expand "cmpsi"
+ [(set (cc0)
+ (compare (match_operand:SI 0 "nonimmediate_operand" "")
+ (match_operand:SI 1 "general_operand" "")))]
+ ""
+ "
+{
+ if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
+ operands[0] = force_reg (SImode, operands[0]);
+
+ i386_compare_gen = gen_cmpsi_1;
+ i386_compare_op0 = operands[0];
+ i386_compare_op1 = operands[1];
+ DONE;
+}")
+
+(define_insn "cmphi_1"
+ [(set (cc0)
+ (compare (match_operand:HI 0 "nonimmediate_operand" "mr,r")
+ (match_operand:HI 1 "general_operand" "ri,mr")))]
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+ "*
+{
+ if (CONSTANT_P (operands[0]) || GET_CODE (operands[1]) == MEM)
+ {
+ cc_status.flags |= CC_REVERSED;
+ return AS2 (cmp%W0,%0,%1);
+ }
+ return AS2 (cmp%W0,%1,%0);
+}")
+
+(define_expand "cmphi"
+ [(set (cc0)
+ (compare (match_operand:HI 0 "nonimmediate_operand" "")
+ (match_operand:HI 1 "general_operand" "")))]
+ ""
+ "
+{
+ if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
+ operands[0] = force_reg (HImode, operands[0]);
+
+ i386_compare_gen = gen_cmphi_1;
+ i386_compare_op0 = operands[0];
+ i386_compare_op1 = operands[1];
+ DONE;
+}")
+
+(define_insn "cmpqi_1"
+ [(set (cc0)
+ (compare (match_operand:QI 0 "nonimmediate_operand" "q,mq")
+ (match_operand:QI 1 "general_operand" "qm,nq")))]
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+ "*
+{
+ if (CONSTANT_P (operands[0]) || GET_CODE (operands[1]) == MEM)
+ {
+ cc_status.flags |= CC_REVERSED;
+ return AS2 (cmp%B0,%0,%1);
+ }
+ return AS2 (cmp%B0,%1,%0);
+}")
+
+(define_expand "cmpqi"
+ [(set (cc0)
+ (compare (match_operand:QI 0 "nonimmediate_operand" "")
+ (match_operand:QI 1 "general_operand" "")))]
+ ""
+ "
+{
+ if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
+ operands[0] = force_reg (QImode, operands[0]);
+
+ i386_compare_gen = gen_cmpqi_1;
+ i386_compare_op0 = operands[0];
+ i386_compare_op1 = operands[1];
+ DONE;
+}")
+
+;; These implement float point compares. For each of DFmode and
+;; SFmode, there is the normal insn, and an insn where the second operand
+;; is converted to the desired mode.
+
+(define_insn ""
+ [(set (cc0)
+ (match_operator 2 "VOIDmode_compare_op"
+ [(match_operand:XF 0 "nonimmediate_operand" "f")
+ (match_operand:XF 1 "nonimmediate_operand" "f")]))
+ (clobber (match_scratch:HI 3 "=a"))]
+ "TARGET_80387
+ && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)"
+ "* return (char *) output_float_compare (insn, operands);")
+
+(define_insn ""
+ [(set (cc0)
+ (match_operator 2 "VOIDmode_compare_op"
+ [(match_operand:XF 0 "register_operand" "f")
+ (float:XF
+ (match_operand:SI 1 "nonimmediate_operand" "rm"))]))
+ (clobber (match_scratch:HI 3 "=a"))]
+ "TARGET_80387"
+ "* return (char *) output_float_compare (insn, operands);")
+
+(define_insn ""
+ [(set (cc0)
+ (match_operator 2 "VOIDmode_compare_op"
+ [(float:XF
+ (match_operand:SI 0 "nonimmediate_operand" "rm"))
+ (match_operand:XF 1 "register_operand" "f")]))
+ (clobber (match_scratch:HI 3 "=a"))]
+ "TARGET_80387"
+ "* return (char *) output_float_compare (insn, operands);")
+
+(define_insn ""
+ [(set (cc0)
+ (match_operator 2 "VOIDmode_compare_op"
+ [(match_operand:XF 0 "register_operand" "f")
+ (float_extend:XF
+ (match_operand:DF 1 "nonimmediate_operand" "fm"))]))
+ (clobber (match_scratch:HI 3 "=a"))]
+ "TARGET_80387"
+ "* return (char *) output_float_compare (insn, operands);")
+
+(define_insn ""
+ [(set (cc0)
+ (match_operator 2 "VOIDmode_compare_op"
+ [(match_operand:XF 0 "register_operand" "f")
+ (float_extend:XF
+ (match_operand:SF 1 "nonimmediate_operand" "fm"))]))
+ (clobber (match_scratch:HI 3 "=a"))]
+ "TARGET_80387"
+ "* return (char *) output_float_compare (insn, operands);")
+
+(define_insn ""
+ [(set (cc0)
+ (compare:CCFPEQ (match_operand:XF 0 "register_operand" "f")
+ (match_operand:XF 1 "register_operand" "f")))
+ (clobber (match_scratch:HI 2 "=a"))]
+ "TARGET_80387"
+ "* return (char *) output_float_compare (insn, operands);")
+
+(define_insn ""
+ [(set (cc0)
+ (match_operator 2 "VOIDmode_compare_op"
+ [(match_operand:DF 0 "nonimmediate_operand" "f,fm")
+ (match_operand:DF 1 "nonimmediate_operand" "fm,f")]))
+ (clobber (match_scratch:HI 3 "=a,a"))]
+ "TARGET_80387
+ && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)"
+ "* return (char *) output_float_compare (insn, operands);")
+
+(define_insn ""
+ [(set (cc0)
+ (match_operator 2 "VOIDmode_compare_op"
+ [(match_operand:DF 0 "register_operand" "f")
+ (float:DF
+ (match_operand:SI 1 "nonimmediate_operand" "rm"))]))
+ (clobber (match_scratch:HI 3 "=a"))]
+ "TARGET_80387"
+ "* return (char *) output_float_compare (insn, operands);")
+
+(define_insn ""
+ [(set (cc0)
+ (match_operator 2 "VOIDmode_compare_op"
+ [(float:DF
+ (match_operand:SI 0 "nonimmediate_operand" "rm"))
+ (match_operand:DF 1 "register_operand" "f")]))
+ (clobber (match_scratch:HI 3 "=a"))]
+ "TARGET_80387"
+ "* return (char *) output_float_compare (insn, operands);")
+
+(define_insn ""
+ [(set (cc0)
+ (match_operator 2 "VOIDmode_compare_op"
+ [(match_operand:DF 0 "register_operand" "f")
+ (float_extend:DF
+ (match_operand:SF 1 "nonimmediate_operand" "fm"))]))
+ (clobber (match_scratch:HI 3 "=a"))]
+ "TARGET_80387"
+ "* return (char *) output_float_compare (insn, operands);")
+
+(define_insn ""
+ [(set (cc0)
+ (match_operator 2 "VOIDmode_compare_op"
+ [(float_extend:DF
+ (match_operand:SF 0 "nonimmediate_operand" "fm"))
+ (match_operand:DF 1 "register_operand" "f")]))
+ (clobber (match_scratch:HI 3 "=a"))]
+ "TARGET_80387"
+ "* return (char *) output_float_compare (insn, operands);")
+
+(define_insn ""
+ [(set (cc0)
+ (compare:CCFPEQ (match_operand:DF 0 "register_operand" "f")
+ (match_operand:DF 1 "register_operand" "f")))
+ (clobber (match_scratch:HI 2 "=a"))]
+ "TARGET_80387"
+ "* return (char *) output_float_compare (insn, operands);")
+
+;; These two insns will never be generated by combine due to the mode of
+;; the COMPARE.
+;(define_insn ""
+; [(set (cc0)
+; (compare:CCFPEQ (match_operand:DF 0 "register_operand" "f")
+; (float_extend:DF
+; (match_operand:SF 1 "register_operand" "f"))))
+; (clobber (match_scratch:HI 2 "=a"))]
+; "TARGET_80387"
+; "* return (char *) output_float_compare (insn, operands);")
+;
+;(define_insn ""
+; [(set (cc0)
+; (compare:CCFPEQ (float_extend:DF
+; (match_operand:SF 0 "register_operand" "f"))
+; (match_operand:DF 1 "register_operand" "f")))
+; (clobber (match_scratch:HI 2 "=a"))]
+; "TARGET_80387"
+; "* return (char *) output_float_compare (insn, operands);")
+
+(define_insn "cmpsf_cc_1"
+ [(set (cc0)
+ (match_operator 2 "VOIDmode_compare_op"
+ [(match_operand:SF 0 "nonimmediate_operand" "f,fm")
+ (match_operand:SF 1 "nonimmediate_operand" "fm,f")]))
+ (clobber (match_scratch:HI 3 "=a,a"))]
+ "TARGET_80387
+ && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)"
+ "* return (char *) output_float_compare (insn, operands);")
+
+(define_insn ""
+ [(set (cc0)
+ (match_operator 2 "VOIDmode_compare_op"
+ [(match_operand:SF 0 "register_operand" "f")
+ (float:SF
+ (match_operand:SI 1 "nonimmediate_operand" "rm"))]))
+ (clobber (match_scratch:HI 3 "=a"))]
+ "TARGET_80387"
+ "* return (char *) output_float_compare (insn, operands);")
+
+(define_insn ""
+ [(set (cc0)
+ (match_operator 2 "VOIDmode_compare_op"
+ [(float:SF
+ (match_operand:SI 0 "nonimmediate_operand" "rm"))
+ (match_operand:SF 1 "register_operand" "f")]))
+ (clobber (match_scratch:HI 3 "=a"))]
+ "TARGET_80387"
+ "* return (char *) output_float_compare (insn, operands);")
+
+(define_insn ""
+ [(set (cc0)
+ (compare:CCFPEQ (match_operand:SF 0 "register_operand" "f")
+ (match_operand:SF 1 "register_operand" "f")))
+ (clobber (match_scratch:HI 2 "=a"))]
+ "TARGET_80387"
+ "* return (char *) output_float_compare (insn, operands);")
+
+(define_expand "cmpxf"
+ [(set (cc0)
+ (compare (match_operand:XF 0 "register_operand" "")
+ (match_operand:XF 1 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "
+{
+ i386_compare_gen = gen_cmpxf_cc;
+ i386_compare_gen_eq = gen_cmpxf_ccfpeq;
+ i386_compare_op0 = operands[0];
+ i386_compare_op1 = operands[1];
+ DONE;
+}")
+
+(define_expand "cmpdf"
+ [(set (cc0)
+ (compare (match_operand:DF 0 "register_operand" "")
+ (match_operand:DF 1 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "
+{
+ i386_compare_gen = gen_cmpdf_cc;
+ i386_compare_gen_eq = gen_cmpdf_ccfpeq;
+ i386_compare_op0 = operands[0];
+ i386_compare_op1 = operands[1];
+ DONE;
+}")
+
+(define_expand "cmpsf"
+ [(set (cc0)
+ (compare (match_operand:SF 0 "register_operand" "")
+ (match_operand:SF 1 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "
+{
+ i386_compare_gen = gen_cmpsf_cc;
+ i386_compare_gen_eq = gen_cmpsf_ccfpeq;
+ i386_compare_op0 = operands[0];
+ i386_compare_op1 = operands[1];
+ DONE;
+}")
+
+(define_expand "cmpxf_cc"
+ [(parallel [(set (cc0)
+ (compare (match_operand:XF 0 "register_operand" "")
+ (match_operand:XF 1 "register_operand" "")))
+ (clobber (match_scratch:HI 2 ""))])]
+ "TARGET_80387"
+ "")
+
+(define_expand "cmpxf_ccfpeq"
+ [(parallel [(set (cc0)
+ (compare:CCFPEQ (match_operand:XF 0 "register_operand" "")
+ (match_operand:XF 1 "register_operand" "")))
+ (clobber (match_scratch:HI 2 ""))])]
+ "TARGET_80387"
+ "
+{
+ if (! register_operand (operands[1], XFmode))
+ operands[1] = copy_to_mode_reg (XFmode, operands[1]);
+}")
+
+(define_expand "cmpdf_cc"
+ [(parallel [(set (cc0)
+ (compare (match_operand:DF 0 "register_operand" "")
+ (match_operand:DF 1 "register_operand" "")))
+ (clobber (match_scratch:HI 2 ""))])]
+ "TARGET_80387"
+ "")
+
+(define_expand "cmpdf_ccfpeq"
+ [(parallel [(set (cc0)
+ (compare:CCFPEQ (match_operand:DF 0 "register_operand" "")
+ (match_operand:DF 1 "register_operand" "")))
+ (clobber (match_scratch:HI 2 ""))])]
+ "TARGET_80387"
+ "
+{
+ if (! register_operand (operands[1], DFmode))
+ operands[1] = copy_to_mode_reg (DFmode, operands[1]);
+}")
+
+(define_expand "cmpsf_cc"
+ [(parallel [(set (cc0)
+ (compare (match_operand:SF 0 "register_operand" "")
+ (match_operand:SF 1 "register_operand" "")))
+ (clobber (match_scratch:HI 2 ""))])]
+ "TARGET_80387"
+ "")
+
+(define_expand "cmpsf_ccfpeq"
+ [(parallel [(set (cc0)
+ (compare:CCFPEQ (match_operand:SF 0 "register_operand" "")
+ (match_operand:SF 1 "register_operand" "")))
+ (clobber (match_scratch:HI 2 ""))])]
+ "TARGET_80387"
+ "
+{
+ if (! register_operand (operands[1], SFmode))
+ operands[1] = copy_to_mode_reg (SFmode, operands[1]);
+}")
+
+;; logical compare
+
+(define_insn ""
+ [(set (cc0)
+ (and:SI (match_operand:SI 0 "general_operand" "%ro")
+ (match_operand:SI 1 "general_operand" "ri")))]
+ ""
+ "*
+{
+ /* For small integers, we may actually use testb. */
+ if (GET_CODE (operands[1]) == CONST_INT
+ && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))
+ && (! REG_P (operands[0]) || QI_REG_P (operands[0])))
+ {
+ /* We may set the sign bit spuriously. */
+
+ if ((INTVAL (operands[1]) & ~0xff) == 0)
+ {
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ return AS2 (test%B0,%1,%b0);
+ }
+
+ if ((INTVAL (operands[1]) & ~0xff00) == 0)
+ {
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ operands[1] = GEN_INT (INTVAL (operands[1]) >> 8);
+
+ if (QI_REG_P (operands[0]))
+ return AS2 (test%B0,%1,%h0);
+ else
+ {
+ operands[0] = adj_offsettable_operand (operands[0], 1);
+ return AS2 (test%B0,%1,%b0);
+ }
+ }
+
+ if (GET_CODE (operands[0]) == MEM
+ && (INTVAL (operands[1]) & ~0xff0000) == 0)
+ {
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ operands[1] = GEN_INT (INTVAL (operands[1]) >> 16);
+ operands[0] = adj_offsettable_operand (operands[0], 2);
+ return AS2 (test%B0,%1,%b0);
+ }
+
+ if (GET_CODE (operands[0]) == MEM
+ && (INTVAL (operands[1]) & ~0xff000000) == 0)
+ {
+ operands[1] = GEN_INT ((INTVAL (operands[1]) >> 24) & 0xff);
+ operands[0] = adj_offsettable_operand (operands[0], 3);
+ return AS2 (test%B0,%1,%b0);
+ }
+ }
+
+ if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM)
+ return AS2 (test%L0,%1,%0);
+
+ return AS2 (test%L1,%0,%1);
+}")
+
+(define_insn ""
+ [(set (cc0)
+ (and:HI (match_operand:HI 0 "general_operand" "%ro")
+ (match_operand:HI 1 "general_operand" "ri")))]
+ ""
+ "*
+{
+ if (GET_CODE (operands[1]) == CONST_INT
+ && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))
+ && (! REG_P (operands[0]) || QI_REG_P (operands[0])))
+ {
+ if ((INTVAL (operands[1]) & 0xff00) == 0)
+ {
+ /* ??? This might not be necessary. */
+ if (INTVAL (operands[1]) & 0xffff0000)
+ operands[1] = GEN_INT (INTVAL (operands[1]) & 0xff);
+
+ /* We may set the sign bit spuriously. */
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ return AS2 (test%B0,%1,%b0);
+ }
+
+ if ((INTVAL (operands[1]) & 0xff) == 0)
+ {
+ operands[1] = GEN_INT ((INTVAL (operands[1]) >> 8) & 0xff);
+
+ if (QI_REG_P (operands[0]))
+ return AS2 (test%B0,%1,%h0);
+ else
+ {
+ operands[0] = adj_offsettable_operand (operands[0], 1);
+ return AS2 (test%B0,%1,%b0);
+ }
+ }
+ }
+
+ if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM)
+ return AS2 (test%W0,%1,%0);
+
+ return AS2 (test%W1,%0,%1);
+}")
+
+(define_insn ""
+ [(set (cc0)
+ (and:QI (match_operand:QI 0 "general_operand" "%qm")
+ (match_operand:QI 1 "general_operand" "qi")))]
+ ""
+ "*
+{
+ if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM)
+ return AS2 (test%B0,%1,%0);
+
+ return AS2 (test%B1,%0,%1);
+}")
+
+;; move instructions.
+;; There is one for each machine mode,
+;; and each is preceded by a corresponding push-insn pattern
+;; (since pushes are not general_operands on the 386).
+
+(define_insn ""
+ [(set (match_operand:SI 0 "push_operand" "=<")
+ (match_operand:SI 1 "general_operand" "g"))]
+ "! TARGET_486"
+ "push%L0 %1")
+
+;; On a 486, it is faster to move MEM to a REG and then push, rather than
+;; push MEM directly.
+
+(define_insn ""
+ [(set (match_operand:SI 0 "push_operand" "=<")
+ (match_operand:SI 1 "general_operand" "ri"))]
+ "TARGET_486"
+ "push%L0 %1")
+
+;; General case of fullword move.
+
+;; If generating PIC code and operands[1] is a symbolic CONST, emit a
+;; move to get the address of the symbolic object from the GOT.
+
+(define_expand "movsi"
+ [(set (match_operand:SI 0 "general_operand" "")
+ (match_operand:SI 1 "general_operand" ""))]
+ ""
+ "
+{
+ extern int flag_pic;
+
+ if (flag_pic && SYMBOLIC_CONST (operands[1]))
+ emit_pic_move (operands, SImode);
+}")
+
+;; On i486, incl reg is faster than movl $1,reg.
+
+(define_insn ""
+ [(set (match_operand:SI 0 "general_operand" "=g,r")
+ (match_operand:SI 1 "general_operand" "ri,m"))]
+ ""
+ "*
+{
+ rtx link;
+ if (operands[1] == const0_rtx && REG_P (operands[0]))
+ return AS2 (xor%L0,%0,%0);
+
+ if (operands[1] == const1_rtx
+ && (link = find_reg_note (insn, REG_WAS_0, 0))
+ /* Make sure the insn that stored the 0 is still present. */
+ && ! INSN_DELETED_P (XEXP (link, 0))
+ && GET_CODE (XEXP (link, 0)) != NOTE
+ /* Make sure cross jumping didn't happen here. */
+ && no_labels_between_p (XEXP (link, 0), insn)
+ /* Make sure the reg hasn't been clobbered. */
+ && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
+ /* Fastest way to change a 0 to a 1. */
+ return AS1 (inc%L0,%0);
+
+ return AS2 (mov%L0,%1,%0);
+}")
+
+(define_insn ""
+ [(set (match_operand:HI 0 "push_operand" "=<")
+ (match_operand:HI 1 "general_operand" "g"))]
+ ""
+ "push%W0 %1")
+
+;; On i486, an incl and movl are both faster than incw and movw.
+
+(define_insn "movhi"
+ [(set (match_operand:HI 0 "general_operand" "=g,r")
+ (match_operand:HI 1 "general_operand" "ri,m"))]
+ ""
+ "*
+{
+ rtx link;
+ if (REG_P (operands[0]) && operands[1] == const0_rtx)
+ return AS2 (xor%L0,%k0,%k0);
+
+ if (REG_P (operands[0]) && operands[1] == const1_rtx
+ && (link = find_reg_note (insn, REG_WAS_0, 0))
+ /* Make sure the insn that stored the 0 is still present. */
+ && ! INSN_DELETED_P (XEXP (link, 0))
+ && GET_CODE (XEXP (link, 0)) != NOTE
+ /* Make sure cross jumping didn't happen here. */
+ && no_labels_between_p (XEXP (link, 0), insn)
+ /* Make sure the reg hasn't been clobbered. */
+ && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
+ /* Fastest way to change a 0 to a 1. */
+ return AS1 (inc%L0,%k0);
+
+ if (REG_P (operands[0]))
+ {
+ if (REG_P (operands[1]))
+ return AS2 (mov%L0,%k1,%k0);
+ else if (CONSTANT_P (operands[1]))
+ return AS2 (mov%L0,%1,%k0);
+ }
+
+ return AS2 (mov%W0,%1,%0);
+}")
+
+(define_insn "movstricthi"
+ [(set (strict_low_part (match_operand:HI 0 "general_operand" "+g,r"))
+ (match_operand:HI 1 "general_operand" "ri,m"))]
+ ""
+ "*
+{
+ rtx link;
+ if (operands[1] == const0_rtx && REG_P (operands[0]))
+ return AS2 (xor%W0,%0,%0);
+
+ if (operands[1] == const1_rtx
+ && (link = find_reg_note (insn, REG_WAS_0, 0))
+ /* Make sure the insn that stored the 0 is still present. */
+ && ! INSN_DELETED_P (XEXP (link, 0))
+ && GET_CODE (XEXP (link, 0)) != NOTE
+ /* Make sure cross jumping didn't happen here. */
+ && no_labels_between_p (XEXP (link, 0), insn)
+ /* Make sure the reg hasn't been clobbered. */
+ && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
+ /* Fastest way to change a 0 to a 1. */
+ return AS1 (inc%W0,%0);
+
+ return AS2 (mov%W0,%1,%0);
+}")
+
+;; emit_push_insn when it calls move_by_pieces
+;; requires an insn to "push a byte".
+;; But actually we use pushw, which has the effect of rounding
+;; the amount pushed up to a halfword.
+(define_insn ""
+ [(set (match_operand:QI 0 "push_operand" "=<")
+ (match_operand:QI 1 "general_operand" "q"))]
+ ""
+ "*
+{
+ operands[1] = gen_rtx (REG, HImode, REGNO (operands[1]));
+ return AS1 (push%W0,%1);
+}")
+
+;; On i486, incb reg is faster than movb $1,reg.
+
+;; ??? Do a recognizer for zero_extract that looks just like this, but reads
+;; or writes %ah, %bh, %ch, %dh.
+
+(define_insn "movqi"
+ [(set (match_operand:QI 0 "general_operand" "=q,*r,qm")
+ (match_operand:QI 1 "general_operand" "*g,q,qn"))]
+ ""
+ "*
+{
+ rtx link;
+ if (operands[1] == const0_rtx && REG_P (operands[0]))
+ return AS2 (xor%B0,%0,%0);
+
+ if (operands[1] == const1_rtx
+ && (link = find_reg_note (insn, REG_WAS_0, 0))
+ /* Make sure the insn that stored the 0 is still present. */
+ && ! INSN_DELETED_P (XEXP (link, 0))
+ && GET_CODE (XEXP (link, 0)) != NOTE
+ /* Make sure cross jumping didn't happen here. */
+ && no_labels_between_p (XEXP (link, 0), insn)
+ /* Make sure the reg hasn't been clobbered. */
+ && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
+ /* Fastest way to change a 0 to a 1. */
+ return AS1 (inc%B0,%0);
+
+ /* If mov%B0 isn't allowed for one of these regs, use mov%L0. */
+ if (NON_QI_REG_P (operands[0]) || NON_QI_REG_P (operands[1]))
+ return (AS2 (mov%L0,%k1,%k0));
+
+ return (AS2 (mov%B0,%1,%0));
+}")
+
+;; If it becomes necessary to support movstrictqi into %esi or %edi,
+;; use the insn sequence:
+;;
+;; shrdl $8,srcreg,dstreg
+;; rorl $24,dstreg
+;;
+;; If operands[1] is a constant, then an andl/orl sequence would be
+;; faster.
+
+(define_insn "movstrictqi"
+ [(set (strict_low_part (match_operand:QI 0 "general_operand" "+qm,q"))
+ (match_operand:QI 1 "general_operand" "*qn,m"))]
+ ""
+ "*
+{
+ rtx link;
+ if (operands[1] == const0_rtx && REG_P (operands[0]))
+ return AS2 (xor%B0,%0,%0);
+
+ if (operands[1] == const1_rtx
+ && (link = find_reg_note (insn, REG_WAS_0, 0))
+ /* Make sure the insn that stored the 0 is still present. */
+ && ! INSN_DELETED_P (XEXP (link, 0))
+ && GET_CODE (XEXP (link, 0)) != NOTE
+ /* Make sure cross jumping didn't happen here. */
+ && no_labels_between_p (XEXP (link, 0), insn)
+ /* Make sure the reg hasn't been clobbered. */
+ && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
+ /* Fastest way to change a 0 to a 1. */
+ return AS1 (inc%B0,%0);
+
+ /* If mov%B0 isn't allowed for one of these regs, use mov%L0. */
+ if (NON_QI_REG_P (operands[0]) || NON_QI_REG_P (operands[1]))
+ {
+ abort ();
+ return (AS2 (mov%L0,%k1,%k0));
+ }
+
+ return AS2 (mov%B0,%1,%0);
+}")
+
+(define_insn ""
+ [(set (match_operand:SF 0 "push_operand" "=<,<")
+ (match_operand:SF 1 "general_operand" "gF,f"))]
+ ""
+ "*
+{
+ if (STACK_REG_P (operands[1]))
+ {
+ rtx xops[3];
+
+ if (! STACK_TOP_P (operands[1]))
+ abort ();
+
+ xops[0] = AT_SP (SFmode);
+ xops[1] = GEN_INT (4);
+ xops[2] = stack_pointer_rtx;
+
+ output_asm_insn (AS2 (sub%L2,%1,%2), xops);
+
+ if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
+ output_asm_insn (AS1 (fstp%S0,%0), xops);
+ else
+ output_asm_insn (AS1 (fst%S0,%0), xops);
+ RET;
+ }
+ return AS1 (push%L1,%1);
+}")
+
+;; Allow MEM-MEM moves before reload. The reload class for such a
+;; move will be ALL_REGS. PREFERRED_RELOAD_CLASS will narrow this to
+;; GENERAL_REGS. For the purposes of regclass, prefer FLOAT_REGS.
+
+(define_insn "movsf"
+ [(set (match_operand:SF 0 "general_operand" "=*rfm,*rf,f,!*rm")
+ (match_operand:SF 1 "general_operand" "*rf,*rfm,fG,fF"))]
+ ""
+ "*
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ /* First handle a `pop' insn or a `fld %st(0)' */
+
+ if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1]))
+ {
+ if (stack_top_dies)
+ return AS1 (fstp,%y0);
+ else
+ return AS1 (fld,%y0);
+ }
+
+ /* Handle a transfer between the 387 and a 386 register */
+
+ if (STACK_TOP_P (operands[0]) && NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fld%z0,%y1));
+ RET;
+ }
+
+ if (STACK_TOP_P (operands[1]) && NON_STACK_REG_P (operands[0]))
+ {
+ output_to_reg (operands[0], stack_top_dies);
+ RET;
+ }
+
+ /* Handle other kinds of writes from the 387 */
+
+ if (STACK_TOP_P (operands[1]))
+ {
+ if (stack_top_dies)
+ return AS1 (fstp%z0,%y0);
+ else
+ return AS1 (fst%z0,%y0);
+ }
+
+ /* Handle other kinds of reads to the 387 */
+
+ if (STACK_TOP_P (operands[0]) && GET_CODE (operands[1]) == CONST_DOUBLE)
+ return (char *) output_move_const_single (operands);
+
+ if (STACK_TOP_P (operands[0]))
+ return AS1 (fld%z1,%y1);
+
+ /* Handle all SFmode moves not involving the 387 */
+
+ return (char *) singlemove_string (operands);
+}")
+
+;;should change to handle the memory operands[1] without doing df push..
+(define_insn ""
+ [(set (match_operand:DF 0 "push_operand" "=<,<")
+ (match_operand:DF 1 "general_operand" "gF,f"))]
+ ""
+ "*
+{
+ if (STACK_REG_P (operands[1]))
+ {
+ rtx xops[3];
+
+ xops[0] = AT_SP (SFmode);
+ xops[1] = GEN_INT (8);
+ xops[2] = stack_pointer_rtx;
+
+ output_asm_insn (AS2 (sub%L2,%1,%2), xops);
+
+ if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
+ output_asm_insn (AS1 (fstp%Q0,%0), xops);
+ else
+ output_asm_insn (AS1 (fst%Q0,%0), xops);
+
+ RET;
+ }
+ else
+ return (char *) output_move_double (operands);
+}")
+
+(define_insn "swapdf"
+ [(set (match_operand:DF 0 "register_operand" "f")
+ (match_operand:DF 1 "register_operand" "f"))
+ (set (match_dup 1)
+ (match_dup 0))]
+ ""
+ "*
+{
+ if (STACK_TOP_P (operands[0]))
+ return AS1 (fxch,%1);
+ else
+ return AS1 (fxch,%0);
+}")
+
+;; Allow MEM-MEM moves before reload. The reload class for such a
+;; move will be ALL_REGS. PREFERRED_RELOAD_CLASS will narrow this to
+;; GENERAL_REGS. For the purposes of regclass, prefer FLOAT_REGS.
+
+(define_insn "movdf"
+ [(set (match_operand:DF 0 "general_operand" "=*rfm,*rf,f,!*rm")
+ (match_operand:DF 1 "general_operand" "*rf,*rfm,fG,fF"))]
+ ""
+ "*
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ /* First handle a `pop' insn or a `fld %st(0)' */
+
+ if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1]))
+ {
+ if (stack_top_dies)
+ return AS1 (fstp,%y0);
+ else
+ return AS1 (fld,%y0);
+ }
+
+ /* Handle a transfer between the 387 and a 386 register */
+
+ if (STACK_TOP_P (operands[0]) && NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fld%z0,%y1));
+ RET;
+ }
+
+ if (STACK_TOP_P (operands[1]) && NON_STACK_REG_P (operands[0]))
+ {
+ output_to_reg (operands[0], stack_top_dies);
+ RET;
+ }
+
+ /* Handle other kinds of writes from the 387 */
+
+ if (STACK_TOP_P (operands[1]))
+ {
+ if (stack_top_dies)
+ return AS1 (fstp%z0,%y0);
+ else
+ return AS1 (fst%z0,%y0);
+ }
+
+ /* Handle other kinds of reads to the 387 */
+
+ if (STACK_TOP_P (operands[0]) && GET_CODE (operands[1]) == CONST_DOUBLE)
+ return (char *) output_move_const_single (operands);
+
+ if (STACK_TOP_P (operands[0]))
+ return AS1 (fld%z1,%y1);
+
+ /* Handle all DFmode moves not involving the 387 */
+
+ return (char *) output_move_double (operands);
+}")
+
+(define_insn ""
+ [(set (match_operand:XF 0 "push_operand" "=<,<")
+ (match_operand:XF 1 "general_operand" "gF,f"))]
+ ""
+ "*
+{
+ if (STACK_REG_P (operands[1]))
+ {
+ rtx xops[3];
+
+ xops[0] = AT_SP (SFmode);
+ xops[1] = GEN_INT (12);
+ xops[2] = stack_pointer_rtx;
+
+ output_asm_insn (AS2 (sub%L2,%1,%2), xops);
+ output_asm_insn (AS1 (fstp%T0,%0), xops);
+ if (! find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
+ output_asm_insn (AS1 (fld%T0,%0), xops);
+
+ RET;
+ }
+ else
+ return (char *) output_move_double (operands);
+ }")
+
+(define_insn "swapxf"
+ [(set (match_operand:XF 0 "register_operand" "f")
+ (match_operand:XF 1 "register_operand" "f"))
+ (set (match_dup 1)
+ (match_dup 0))]
+ ""
+ "*
+{
+ if (STACK_TOP_P (operands[0]))
+ return AS1 (fxch,%1);
+ else
+ return AS1 (fxch,%0);
+}")
+
+(define_insn "movxf"
+ [(set (match_operand:XF 0 "general_operand" "=f,fm,!*rf,!*rm")
+ (match_operand:XF 1 "general_operand" "fmG,f,*rfm,*rfF"))]
+;; [(set (match_operand:XF 0 "general_operand" "=*rf,*rfm,f,!*rm")
+;; (match_operand:XF 1 "general_operand" "*rfm,*rf,fG,fF"))]
+ ""
+ "*
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ /* First handle a `pop' insn or a `fld %st(0)' */
+
+ if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1]))
+ {
+ if (stack_top_dies)
+ return AS1 (fstp,%y0);
+ else
+ return AS1 (fld,%y0);
+ }
+
+ /* Handle a transfer between the 387 and a 386 register */
+
+ if (STACK_TOP_P (operands[0]) && NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fld%z0,%y1));
+ RET;
+ }
+
+ if (STACK_TOP_P (operands[1]) && NON_STACK_REG_P (operands[0]))
+ {
+ output_to_reg (operands[0], stack_top_dies);
+ RET;
+ }
+
+ /* Handle other kinds of writes from the 387 */
+
+ if (STACK_TOP_P (operands[1]))
+ {
+ output_asm_insn (AS1 (fstp%z0,%y0), operands);
+ if (! stack_top_dies)
+ return AS1 (fld%z0,%y0);
+
+ RET;
+ }
+
+ /* Handle other kinds of reads to the 387 */
+
+ if (STACK_TOP_P (operands[0]) && GET_CODE (operands[1]) == CONST_DOUBLE)
+ return (char *) output_move_const_single (operands);
+
+ if (STACK_TOP_P (operands[0]))
+ return AS1 (fld%z1,%y1);
+
+ /* Handle all XFmode moves not involving the 387 */
+
+ return (char *) output_move_double (operands);
+}")
+
+(define_insn ""
+ [(set (match_operand:DI 0 "push_operand" "=<")
+ (match_operand:DI 1 "general_operand" "roiF"))]
+ ""
+ "*
+{
+ return (char *) output_move_double (operands);
+}")
+
+(define_insn "movdi"
+ [(set (match_operand:DI 0 "general_operand" "=r,rm")
+ (match_operand:DI 1 "general_operand" "m,riF"))]
+ ""
+ "*
+{
+ return (char *) output_move_double (operands);
+}")
+
+;;- conversion instructions
+;;- NONE
+
+;;- zero extension instructions
+;; See comments by `andsi' for when andl is faster than movzx.
+
+(define_insn "zero_extendhisi2"
+ [(set (match_operand:SI 0 "general_operand" "=r")
+ (zero_extend:SI
+ (match_operand:HI 1 "nonimmediate_operand" "rm")))]
+ ""
+ "*
+{
+ if ((TARGET_486 || REGNO (operands[0]) == 0)
+ && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1]))
+ {
+ rtx xops[2];
+ xops[0] = operands[0];
+ xops[1] = GEN_INT (0xffff);
+ output_asm_insn (AS2 (and%L0,%1,%k0), xops);
+ RET;
+ }
+
+#ifdef INTEL_SYNTAX
+ return AS2 (movzx,%1,%0);
+#else
+ return AS2 (movz%W0%L0,%1,%0);
+#endif
+}")
+
+(define_insn "zero_extendqihi2"
+ [(set (match_operand:HI 0 "general_operand" "=r")
+ (zero_extend:HI
+ (match_operand:QI 1 "nonimmediate_operand" "qm")))]
+ ""
+ "*
+{
+ if ((TARGET_486 || REGNO (operands[0]) == 0)
+ && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1]))
+ {
+ rtx xops[2];
+ xops[0] = operands[0];
+ xops[1] = GEN_INT (0xff);
+ output_asm_insn (AS2 (and%L0,%1,%k0), xops);
+ RET;
+ }
+
+#ifdef INTEL_SYNTAX
+ return AS2 (movzx,%1,%0);
+#else
+ return AS2 (movz%B0%W0,%1,%0);
+#endif
+}")
+
+(define_insn "zero_extendqisi2"
+ [(set (match_operand:SI 0 "general_operand" "=r")
+ (zero_extend:SI
+ (match_operand:QI 1 "nonimmediate_operand" "qm")))]
+ ""
+ "*
+{
+ if ((TARGET_486 || REGNO (operands[0]) == 0)
+ && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1]))
+ {
+ rtx xops[2];
+ xops[0] = operands[0];
+ xops[1] = GEN_INT (0xff);
+ output_asm_insn (AS2 (and%L0,%1,%k0), xops);
+ RET;
+ }
+
+#ifdef INTEL_SYNTAX
+ return AS2 (movzx,%1,%0);
+#else
+ return AS2 (movz%B0%L0,%1,%0);
+#endif
+}")
+
+(define_insn "zero_extendsidi2"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (zero_extend:DI
+ (match_operand:SI 1 "register_operand" "0")))]
+ ""
+ "*
+{
+ operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
+ return AS2 (xor%L0,%0,%0);
+}")
+
+;;- sign extension instructions
+
+(define_insn "extendsidi2"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (sign_extend:DI
+ (match_operand:SI 1 "register_operand" "0")))]
+ ""
+ "*
+{
+ if (REGNO (operands[0]) == 0)
+ {
+ /* This used to be cwtl, but that extends HI to SI somehow. */
+#ifdef INTEL_SYNTAX
+ return \"cdq\";
+#else
+ return \"cltd\";
+#endif
+ }
+
+ operands[1] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
+ output_asm_insn (AS2 (mov%L0,%0,%1), operands);
+
+ operands[0] = GEN_INT (31);
+ return AS2 (sar%L1,%0,%1);
+}")
+
+;; Note that the i386 programmers' manual says that the opcodes
+;; are named movsx..., but the assembler on Unix does not accept that.
+;; We use what the Unix assembler expects.
+
+(define_insn "extendhisi2"
+ [(set (match_operand:SI 0 "general_operand" "=r")
+ (sign_extend:SI
+ (match_operand:HI 1 "nonimmediate_operand" "rm")))]
+ ""
+ "*
+{
+ if (REGNO (operands[0]) == 0
+ && REG_P (operands[1]) && REGNO (operands[1]) == 0)
+#ifdef INTEL_SYNTAX
+ return \"cwde\";
+#else
+ return \"cwtl\";
+#endif
+
+#ifdef INTEL_SYNTAX
+ return AS2 (movsx,%1,%0);
+#else
+ return AS2 (movs%W0%L0,%1,%0);
+#endif
+}")
+
+(define_insn "extendqihi2"
+ [(set (match_operand:HI 0 "general_operand" "=r")
+ (sign_extend:HI
+ (match_operand:QI 1 "nonimmediate_operand" "qm")))]
+ ""
+ "*
+{
+ if (REGNO (operands[0]) == 0
+ && REG_P (operands[1]) && REGNO (operands[1]) == 0)
+ return \"cbtw\";
+
+#ifdef INTEL_SYNTAX
+ return AS2 (movsx,%1,%0);
+#else
+ return AS2 (movs%B0%W0,%1,%0);
+#endif
+}")
+
+(define_insn "extendqisi2"
+ [(set (match_operand:SI 0 "general_operand" "=r")
+ (sign_extend:SI
+ (match_operand:QI 1 "nonimmediate_operand" "qm")))]
+ ""
+ "*
+{
+#ifdef INTEL_SYNTAX
+ return AS2 (movsx,%1,%0);
+#else
+ return AS2 (movs%B0%L0,%1,%0);
+#endif
+}")
+
+;; Conversions between float and double.
+
+(define_insn "extendsfdf2"
+ [(set (match_operand:DF 0 "general_operand" "=fm,f")
+ (float_extend:DF
+ (match_operand:SF 1 "general_operand" "f,fm")))]
+ "TARGET_80387"
+ "*
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fld%z0,%y1));
+ RET;
+ }
+
+ if (NON_STACK_REG_P (operands[0]))
+ {
+ output_to_reg (operands[0], stack_top_dies);
+ RET;
+ }
+
+ if (STACK_TOP_P (operands[0]))
+ return AS1 (fld%z1,%y1);
+
+ if (GET_CODE (operands[0]) == MEM)
+ {
+ if (stack_top_dies)
+ return AS1 (fstp%z0,%y0);
+ else
+ return AS1 (fst%z0,%y0);
+ }
+
+ abort ();
+}")
+
+(define_insn "extenddfxf2"
+ [(set (match_operand:XF 0 "general_operand" "=fm,f,f,!*r")
+ (float_extend:XF
+ (match_operand:DF 1 "general_operand" "f,fm,!*r,f")))]
+ "TARGET_80387"
+ "*
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fld%z0,%y1));
+ RET;
+ }
+
+ if (NON_STACK_REG_P (operands[0]))
+ {
+ output_to_reg (operands[0], stack_top_dies);
+ RET;
+ }
+
+ if (STACK_TOP_P (operands[0]))
+ return AS1 (fld%z1,%y1);
+
+ if (GET_CODE (operands[0]) == MEM)
+ {
+ output_asm_insn (AS1 (fstp%z0,%y0), operands);
+ if (! stack_top_dies)
+ return AS1 (fld%z0,%y0);
+ RET;
+ }
+
+ abort ();
+}")
+
+(define_insn "extendsfxf2"
+ [(set (match_operand:XF 0 "general_operand" "=fm,f,f,!*r")
+ (float_extend:XF
+ (match_operand:SF 1 "general_operand" "f,fm,!*r,f")))]
+ "TARGET_80387"
+ "*
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fld%z0,%y1));
+ RET;
+ }
+
+ if (NON_STACK_REG_P (operands[0]))
+ {
+ output_to_reg (operands[0], stack_top_dies);
+ RET;
+ }
+
+ if (STACK_TOP_P (operands[0]))
+ return AS1 (fld%z1,%y1);
+
+ if (GET_CODE (operands[0]) == MEM)
+ {
+ output_asm_insn (AS1 (fstp%z0,%y0), operands);
+ if (! stack_top_dies)
+ return AS1 (fld%z0,%y0);
+ RET;
+ }
+
+ abort ();
+}")
+
+(define_expand "truncdfsf2"
+ [(parallel [(set (match_operand:SF 0 "nonimmediate_operand" "")
+ (float_truncate:SF
+ (match_operand:DF 1 "register_operand" "")))
+ (clobber (match_dup 2))])]
+ "TARGET_80387"
+ "
+{
+ operands[2] = (rtx) assign_386_stack_local (SFmode, 0);
+}")
+
+;; This cannot output into an f-reg because there is no way to be sure
+;; of truncating in that case. Otherwise this is just like a simple move
+;; insn. So we pretend we can output to a reg in order to get better
+;; register preferencing, but we really use a stack slot.
+
+(define_insn ""
+ [(set (match_operand:SF 0 "nonimmediate_operand" "=f,m")
+ (float_truncate:SF
+ (match_operand:DF 1 "register_operand" "0,f")))
+ (clobber (match_operand:SF 2 "memory_operand" "m,m"))]
+ "TARGET_80387"
+ "*
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ if (GET_CODE (operands[0]) == MEM)
+ {
+ if (stack_top_dies)
+ return AS1 (fstp%z0,%0);
+ else
+ return AS1 (fst%z0,%0);
+ }
+ else if (STACK_TOP_P (operands[0]))
+ {
+ output_asm_insn (AS1 (fstp%z2,%y2), operands);
+ return AS1 (fld%z2,%y2);
+ }
+ else
+ abort ();
+}")
+
+(define_insn "truncxfsf2"
+ [(set (match_operand:SF 0 "general_operand" "=m,!*r")
+ (float_truncate:SF
+ (match_operand:XF 1 "register_operand" "f,f")))]
+ "TARGET_80387"
+ "*
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ if (NON_STACK_REG_P (operands[0]))
+ {
+ if (stack_top_dies == 0)
+ {
+ output_asm_insn (AS1 (fld,%y1), operands);
+ stack_top_dies = 1;
+ }
+ output_to_reg (operands[0], stack_top_dies);
+ RET;
+ }
+ else if (GET_CODE (operands[0]) == MEM)
+ {
+ if (stack_top_dies)
+ return AS1 (fstp%z0,%0);
+ else
+ {
+ output_asm_insn (AS1 (fld,%y1), operands);
+ return AS1 (fstp%z0,%0);
+ }
+ }
+ else
+ abort ();
+}")
+
+(define_insn "truncxfdf2"
+ [(set (match_operand:DF 0 "general_operand" "=m,!*r")
+ (float_truncate:DF
+ (match_operand:XF 1 "register_operand" "f,f")))]
+ "TARGET_80387"
+ "*
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ if (NON_STACK_REG_P (operands[0]))
+ {
+ if (stack_top_dies == 0)
+ {
+ output_asm_insn (AS1 (fld,%y1), operands);
+ stack_top_dies = 1;
+ }
+ output_to_reg (operands[0], stack_top_dies);
+ RET;
+ }
+ else if (GET_CODE (operands[0]) == MEM)
+ {
+ if (stack_top_dies)
+ return AS1 (fstp%z0,%0);
+ else
+ {
+ output_asm_insn (AS1 (fld,%y1), operands);
+ return AS1 (fstp%z0,%0);
+ }
+ }
+ else
+ abort ();
+}")
+
+
+;; The 387 requires that the stack top dies after converting to DImode.
+
+;; Represent an unsigned conversion from SImode to MODE_FLOAT by first
+;; doing a signed conversion to DImode, and then taking just the low
+;; part.
+
+(define_expand "fixuns_truncxfsi2"
+ [(set (match_dup 4)
+ (match_operand:XF 1 "register_operand" ""))
+ (parallel [(set (match_dup 2)
+ (fix:DI (fix:XF (match_dup 4))))
+ (clobber (match_dup 4))
+ (clobber (match_dup 5))
+ (clobber (match_dup 6))
+ (clobber (match_scratch:SI 7 ""))])
+ (set (match_operand:SI 0 "general_operand" "")
+ (match_dup 3))]
+ "TARGET_80387"
+ "
+{
+ operands[2] = gen_reg_rtx (DImode);
+ operands[3] = gen_lowpart (SImode, operands[2]);
+ operands[4] = gen_reg_rtx (XFmode);
+ operands[5] = (rtx) assign_386_stack_local (SImode, 0);
+ operands[6] = (rtx) assign_386_stack_local (SImode, 1);
+}")
+
+(define_expand "fixuns_truncdfsi2"
+ [(set (match_dup 4)
+ (match_operand:DF 1 "register_operand" ""))
+ (parallel [(set (match_dup 2)
+ (fix:DI (fix:DF (match_dup 4))))
+ (clobber (match_dup 4))
+ (clobber (match_dup 5))
+ (clobber (match_dup 6))
+ (clobber (match_scratch:SI 7 ""))])
+ (set (match_operand:SI 0 "general_operand" "")
+ (match_dup 3))]
+ "TARGET_80387"
+ "
+{
+ operands[2] = gen_reg_rtx (DImode);
+ operands[3] = gen_lowpart (SImode, operands[2]);
+ operands[4] = gen_reg_rtx (DFmode);
+ operands[5] = (rtx) assign_386_stack_local (SImode, 0);
+ operands[6] = (rtx) assign_386_stack_local (SImode, 1);
+}")
+
+(define_expand "fixuns_truncsfsi2"
+ [(set (match_dup 4)
+ (match_operand:SF 1 "register_operand" ""))
+ (parallel [(set (match_dup 2)
+ (fix:DI (fix:SF (match_dup 4))))
+ (clobber (match_dup 4))
+ (clobber (match_dup 5))
+ (clobber (match_dup 6))
+ (clobber (match_scratch:SI 7 ""))])
+ (set (match_operand:SI 0 "general_operand" "")
+ (match_dup 3))]
+ "TARGET_80387"
+ "
+{
+ operands[2] = gen_reg_rtx (DImode);
+ operands[3] = gen_lowpart (SImode, operands[2]);
+ operands[4] = gen_reg_rtx (SFmode);
+ operands[5] = (rtx) assign_386_stack_local (SImode, 0);
+ operands[6] = (rtx) assign_386_stack_local (SImode, 1);
+}")
+
+;; Signed conversion to DImode.
+
+(define_expand "fix_truncxfdi2"
+ [(set (match_dup 2)
+ (match_operand:XF 1 "register_operand" ""))
+ (parallel [(set (match_operand:DI 0 "general_operand" "")
+ (fix:DI (fix:XF (match_dup 2))))
+ (clobber (match_dup 2))
+ (clobber (match_dup 3))
+ (clobber (match_dup 4))
+ (clobber (match_scratch:SI 5 ""))])]
+ "TARGET_80387"
+ "
+{
+ operands[1] = copy_to_mode_reg (XFmode, operands[1]);
+ operands[2] = gen_reg_rtx (XFmode);
+ operands[3] = (rtx) assign_386_stack_local (SImode, 0);
+ operands[4] = (rtx) assign_386_stack_local (SImode, 1);
+}")
+
+(define_expand "fix_truncdfdi2"
+ [(set (match_dup 2)
+ (match_operand:DF 1 "register_operand" ""))
+ (parallel [(set (match_operand:DI 0 "general_operand" "")
+ (fix:DI (fix:DF (match_dup 2))))
+ (clobber (match_dup 2))
+ (clobber (match_dup 3))
+ (clobber (match_dup 4))
+ (clobber (match_scratch:SI 5 ""))])]
+ "TARGET_80387"
+ "
+{
+ operands[1] = copy_to_mode_reg (DFmode, operands[1]);
+ operands[2] = gen_reg_rtx (DFmode);
+ operands[3] = (rtx) assign_386_stack_local (SImode, 0);
+ operands[4] = (rtx) assign_386_stack_local (SImode, 1);
+}")
+
+(define_expand "fix_truncsfdi2"
+ [(set (match_dup 2)
+ (match_operand:SF 1 "register_operand" ""))
+ (parallel [(set (match_operand:DI 0 "general_operand" "")
+ (fix:DI (fix:SF (match_dup 2))))
+ (clobber (match_dup 2))
+ (clobber (match_dup 3))
+ (clobber (match_dup 4))
+ (clobber (match_scratch:SI 5 ""))])]
+ "TARGET_80387"
+ "
+{
+ operands[1] = copy_to_mode_reg (SFmode, operands[1]);
+ operands[2] = gen_reg_rtx (SFmode);
+ operands[3] = (rtx) assign_386_stack_local (SImode, 0);
+ operands[4] = (rtx) assign_386_stack_local (SImode, 1);
+}")
+
+;; These match a signed conversion of either DFmode or SFmode to DImode.
+
+(define_insn ""
+ [(set (match_operand:DI 0 "general_operand" "=rm")
+ (fix:DI (fix:XF (match_operand:XF 1 "register_operand" "f"))))
+ (clobber (match_dup 1))
+ (clobber (match_operand:SI 2 "memory_operand" "m"))
+ (clobber (match_operand:SI 3 "memory_operand" "m"))
+ (clobber (match_scratch:SI 4 "=&q"))]
+ "TARGET_80387"
+ "* return (char *) output_fix_trunc (insn, operands);")
+
+(define_insn ""
+ [(set (match_operand:DI 0 "general_operand" "=rm")
+ (fix:DI (fix:DF (match_operand:DF 1 "register_operand" "f"))))
+ (clobber (match_dup 1))
+ (clobber (match_operand:SI 2 "memory_operand" "m"))
+ (clobber (match_operand:SI 3 "memory_operand" "m"))
+ (clobber (match_scratch:SI 4 "=&q"))]
+ "TARGET_80387"
+ "* return (char *) output_fix_trunc (insn, operands);")
+
+(define_insn ""
+ [(set (match_operand:DI 0 "general_operand" "=rm")
+ (fix:DI (fix:SF (match_operand:SF 1 "register_operand" "f"))))
+ (clobber (match_dup 1))
+ (clobber (match_operand:SI 2 "memory_operand" "m"))
+ (clobber (match_operand:SI 3 "memory_operand" "m"))
+ (clobber (match_scratch:SI 4 "=&q"))]
+ "TARGET_80387"
+ "* return (char *) output_fix_trunc (insn, operands);")
+
+;; Signed MODE_FLOAT conversion to SImode.
+
+(define_expand "fix_truncxfsi2"
+ [(parallel [(set (match_operand:SI 0 "general_operand" "")
+ (fix:SI
+ (fix:XF (match_operand:XF 1 "register_operand" ""))))
+ (clobber (match_dup 2))
+ (clobber (match_dup 3))
+ (clobber (match_scratch:SI 4 ""))])]
+ "TARGET_80387"
+ "
+{
+ operands[2] = (rtx) assign_386_stack_local (SImode, 0);
+ operands[3] = (rtx) assign_386_stack_local (SImode, 1);
+}")
+
+(define_expand "fix_truncdfsi2"
+ [(parallel [(set (match_operand:SI 0 "general_operand" "")
+ (fix:SI
+ (fix:DF (match_operand:DF 1 "register_operand" ""))))
+ (clobber (match_dup 2))
+ (clobber (match_dup 3))
+ (clobber (match_scratch:SI 4 ""))])]
+ "TARGET_80387"
+ "
+{
+ operands[2] = (rtx) assign_386_stack_local (SImode, 0);
+ operands[3] = (rtx) assign_386_stack_local (SImode, 1);
+}")
+
+(define_expand "fix_truncsfsi2"
+ [(parallel [(set (match_operand:SI 0 "general_operand" "")
+ (fix:SI
+ (fix:SF (match_operand:SF 1 "register_operand" ""))))
+ (clobber (match_dup 2))
+ (clobber (match_dup 3))
+ (clobber (match_scratch:SI 4 ""))])]
+ "TARGET_80387"
+ "
+{
+ operands[2] = (rtx) assign_386_stack_local (SImode, 0);
+ operands[3] = (rtx) assign_386_stack_local (SImode, 1);
+}")
+
+(define_insn ""
+ [(set (match_operand:SI 0 "general_operand" "=rm")
+ (fix:SI (fix:XF (match_operand:XF 1 "register_operand" "f"))))
+ (clobber (match_operand:SI 2 "memory_operand" "m"))
+ (clobber (match_operand:SI 3 "memory_operand" "m"))
+ (clobber (match_scratch:SI 4 "=&q"))]
+ "TARGET_80387"
+ "* return (char *) output_fix_trunc (insn, operands);")
+
+(define_insn ""
+ [(set (match_operand:SI 0 "general_operand" "=rm")
+ (fix:SI (fix:DF (match_operand:DF 1 "register_operand" "f"))))
+ (clobber (match_operand:SI 2 "memory_operand" "m"))
+ (clobber (match_operand:SI 3 "memory_operand" "m"))
+ (clobber (match_scratch:SI 4 "=&q"))]
+ "TARGET_80387"
+ "* return (char *) output_fix_trunc (insn, operands);")
+
+(define_insn ""
+ [(set (match_operand:SI 0 "general_operand" "=rm")
+ (fix:SI (fix:SF (match_operand:SF 1 "register_operand" "f"))))
+ (clobber (match_operand:SI 2 "memory_operand" "m"))
+ (clobber (match_operand:SI 3 "memory_operand" "m"))
+ (clobber (match_scratch:SI 4 "=&q"))]
+ "TARGET_80387"
+ "* return (char *) output_fix_trunc (insn, operands);")
+
+;; Conversion between fixed point and floating point.
+;; The actual pattern that matches these is at the end of this file.
+
+;; ??? Possibly represent floatunssidf2 here in gcc2.
+
+(define_expand "floatsisf2"
+ [(set (match_operand:SF 0 "register_operand" "")
+ (float:SF (match_operand:SI 1 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "")
+
+(define_expand "floatdisf2"
+ [(set (match_operand:SF 0 "register_operand" "")
+ (float:SF (match_operand:DI 1 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "")
+
+(define_expand "floatsidf2"
+ [(set (match_operand:DF 0 "register_operand" "")
+ (float:DF (match_operand:SI 1 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "")
+
+(define_expand "floatdidf2"
+ [(set (match_operand:DF 0 "register_operand" "")
+ (float:DF (match_operand:DI 1 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "")
+
+(define_expand "floatsixf2"
+ [(set (match_operand:XF 0 "register_operand" "")
+ (float:XF (match_operand:SI 1 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "")
+
+(define_expand "floatdixf2"
+ [(set (match_operand:XF 0 "register_operand" "")
+ (float:XF (match_operand:DI 1 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "")
+
+;; This will convert from SImode or DImode to MODE_FLOAT.
+
+(define_insn ""
+ [(set (match_operand:XF 0 "register_operand" "=f")
+ (float:XF (match_operand:DI 1 "general_operand" "rm")))]
+ "TARGET_80387"
+ "*
+{
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fild%z0,%1));
+ RET;
+ }
+ else if (GET_CODE (operands[1]) == MEM)
+ return AS1 (fild%z1,%1);
+ else
+ abort ();
+}")
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (float:DF (match_operand:DI 1 "nonimmediate_operand" "rm")))]
+ "TARGET_80387"
+ "*
+{
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fild%z0,%1));
+ RET;
+ }
+ else if (GET_CODE (operands[1]) == MEM)
+ return AS1 (fild%z1,%1);
+ else
+ abort ();
+}")
+
+(define_insn ""
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (float:SF (match_operand:DI 1 "nonimmediate_operand" "rm")))]
+ "TARGET_80387"
+ "*
+{
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fild%z0,%1));
+ RET;
+ }
+ else if (GET_CODE (operands[1]) == MEM)
+ return AS1 (fild%z1,%1);
+ else
+ abort ();
+}")
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (float:DF (match_operand:SI 1 "nonimmediate_operand" "rm")))]
+ "TARGET_80387"
+ "*
+{
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fild%z0,%1));
+ RET;
+ }
+ else if (GET_CODE (operands[1]) == MEM)
+ return AS1 (fild%z1,%1);
+ else
+ abort ();
+}")
+
+(define_insn ""
+ [(set (match_operand:XF 0 "register_operand" "=f,f")
+ (float:XF (match_operand:SI 1 "general_operand" "m,!*r")))]
+ "TARGET_80387"
+ "*
+{
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fild%z0,%1));
+ RET;
+ }
+ else if (GET_CODE (operands[1]) == MEM)
+ return AS1 (fild%z1,%1);
+ else
+ abort ();
+}")
+
+(define_insn ""
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (float:SF (match_operand:SI 1 "nonimmediate_operand" "rm")))]
+ "TARGET_80387"
+ "*
+{
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], AS1 (fild%z0,%1));
+ RET;
+ }
+ else if (GET_CODE (operands[1]) == MEM)
+ return AS1 (fild%z1,%1);
+ else
+ abort ();
+}")
+
+;;- add instructions
+
+(define_insn "adddi3"
+ [(set (match_operand:DI 0 "general_operand" "=&r,ro")
+ (plus:DI (match_operand:DI 1 "general_operand" "%0,0")
+ (match_operand:DI 2 "general_operand" "o,riF")))]
+ ""
+ "*
+{
+ rtx low[3], high[3];
+
+ CC_STATUS_INIT;
+
+ split_di (operands, 3, low, high);
+
+ if (GET_CODE (low[2]) != CONST_INT || INTVAL (low[2]) != 0)
+ {
+ output_asm_insn (AS2 (add%L0,%2,%0), low);
+ output_asm_insn (AS2 (adc%L0,%2,%0), high);
+ }
+ else
+ output_asm_insn (AS2 (add%L0,%2,%0), high);
+ RET;
+}")
+
+;; On a 486, it is faster to do movl/addl than to do a single leal if
+;; operands[1] and operands[2] are both registers.
+
+(define_insn "addsi3"
+ [(set (match_operand:SI 0 "general_operand" "=?r,rm,r")
+ (plus:SI (match_operand:SI 1 "general_operand" "%r,0,0")
+ (match_operand:SI 2 "general_operand" "ri,ri,rm")))]
+ ""
+ "*
+{
+ if (REG_P (operands[0]) && REGNO (operands[0]) != REGNO (operands[1]))
+ {
+ if (REG_P (operands[2]) && REGNO (operands[0]) == REGNO (operands[2]))
+ return AS2 (add%L0,%1,%0);
+
+ if (! TARGET_486 || ! REG_P (operands[2]))
+ {
+ CC_STATUS_INIT;
+
+ if (operands[2] == stack_pointer_rtx)
+ {
+ rtx temp;
+
+ temp = operands[1];
+ operands[1] = operands[2];
+ operands[2] = temp;
+ }
+ if (operands[2] != stack_pointer_rtx)
+ {
+ operands[1] = SET_SRC (PATTERN (insn));
+ return AS2 (lea%L0,%a1,%0);
+ }
+ }
+
+ output_asm_insn (AS2 (mov%L0,%1,%0), operands);
+ }
+
+ if (operands[2] == const1_rtx)
+ return AS1 (inc%L0,%0);
+
+ if (operands[2] == constm1_rtx)
+ return AS1 (dec%L0,%0);
+
+ return AS2 (add%L0,%2,%0);
+}")
+
+;; ??? `lea' here, for three operand add? If leaw is used, only %bx,
+;; %si and %di can appear in SET_SRC, and output_asm_insn might not be
+;; able to handle the operand. But leal always works?
+
+(define_insn "addhi3"
+ [(set (match_operand:HI 0 "general_operand" "=rm,r")
+ (plus:HI (match_operand:HI 1 "general_operand" "%0,0")
+ (match_operand:HI 2 "general_operand" "ri,rm")))]
+ ""
+ "*
+{
+ /* ??? what about offsettable memory references? */
+ if (QI_REG_P (operands[0])
+ && GET_CODE (operands[2]) == CONST_INT
+ && (INTVAL (operands[2]) & 0xff) == 0)
+ {
+ int byteval = (INTVAL (operands[2]) >> 8) & 0xff;
+ CC_STATUS_INIT;
+
+ if (byteval == 1)
+ return AS1 (inc%B0,%h0);
+ else if (byteval == 255)
+ return AS1 (dec%B0,%h0);
+
+ operands[2] = GEN_INT (byteval);
+ return AS2 (add%B0,%2,%h0);
+ }
+
+ if (operands[2] == const1_rtx)
+ return AS1 (inc%W0,%0);
+
+ if (operands[2] == constm1_rtx
+ || (GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) == 65535))
+ return AS1 (dec%W0,%0);
+
+ return AS2 (add%W0,%2,%0);
+}")
+
+(define_insn "addqi3"
+ [(set (match_operand:QI 0 "general_operand" "=qm,q")
+ (plus:QI (match_operand:QI 1 "general_operand" "%0,0")
+ (match_operand:QI 2 "general_operand" "qn,qmn")))]
+ ""
+ "*
+{
+ if (operands[2] == const1_rtx)
+ return AS1 (inc%B0,%0);
+
+ if (operands[2] == constm1_rtx
+ || (GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) == 255))
+ return AS1 (dec%B0,%0);
+
+ return AS2 (add%B0,%2,%0);
+}")
+
+;Lennart Augustsson <augustss@cs.chalmers.se>
+;says this pattern just makes slower code:
+; pushl %ebp
+; addl $-80,(%esp)
+;instead of
+; leal -80(%ebp),%eax
+; pushl %eax
+;
+;(define_insn ""
+; [(set (match_operand:SI 0 "push_operand" "=<")
+; (plus:SI (match_operand:SI 1 "general_operand" "%r")
+; (match_operand:SI 2 "general_operand" "ri")))]
+; ""
+; "*
+;{
+; rtx xops[4];
+; xops[0] = operands[0];
+; xops[1] = operands[1];
+; xops[2] = operands[2];
+; xops[3] = gen_rtx (MEM, SImode, stack_pointer_rtx);
+; output_asm_insn (\"push%z1 %1\", xops);
+; output_asm_insn (AS2 (add%z3,%2,%3), xops);
+; RET;
+;}")
+
+;; addsi3 is faster, so put this after.
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (match_operand:QI 1 "address_operand" "p"))]
+ ""
+ "*
+{
+ CC_STATUS_INIT;
+ /* Adding a constant to a register is faster with an add. */
+ /* ??? can this ever happen? */
+ if (GET_CODE (operands[1]) == PLUS
+ && GET_CODE (XEXP (operands[1], 1)) == CONST_INT
+ && rtx_equal_p (operands[0], XEXP (operands[1], 0)))
+ {
+ operands[1] = XEXP (operands[1], 1);
+
+ if (operands[1] == const1_rtx)
+ return AS1 (inc%L0,%0);
+
+ if (operands[1] == constm1_rtx)
+ return AS1 (dec%L0,%0);
+
+ return AS2 (add%L0,%1,%0);
+ }
+ return AS2 (lea%L0,%a1,%0);
+}")
+
+;; The patterns that match these are at the end of this file.
+
+(define_expand "addxf3"
+ [(set (match_operand:XF 0 "register_operand" "")
+ (plus:XF (match_operand:XF 1 "nonimmediate_operand" "")
+ (match_operand:XF 2 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "")
+
+(define_expand "adddf3"
+ [(set (match_operand:DF 0 "register_operand" "")
+ (plus:DF (match_operand:DF 1 "nonimmediate_operand" "")
+ (match_operand:DF 2 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "")
+
+(define_expand "addsf3"
+ [(set (match_operand:SF 0 "register_operand" "")
+ (plus:SF (match_operand:SF 1 "nonimmediate_operand" "")
+ (match_operand:SF 2 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "")
+
+;;- subtract instructions
+
+(define_insn "subdi3"
+ [(set (match_operand:DI 0 "general_operand" "=&r,ro")
+ (minus:DI (match_operand:DI 1 "general_operand" "0,0")
+ (match_operand:DI 2 "general_operand" "o,riF")))]
+ ""
+ "*
+{
+ rtx low[3], high[3];
+
+ CC_STATUS_INIT;
+
+ split_di (operands, 3, low, high);
+
+ if (GET_CODE (low[2]) != CONST_INT || INTVAL (low[2]) != 0)
+ {
+ output_asm_insn (AS2 (sub%L0,%2,%0), low);
+ output_asm_insn (AS2 (sbb%L0,%2,%0), high);
+ }
+ else
+ output_asm_insn (AS2 (sub%L0,%2,%0), high);
+
+ RET;
+}")
+
+(define_insn "subsi3"
+ [(set (match_operand:SI 0 "general_operand" "=rm,r")
+ (minus:SI (match_operand:SI 1 "general_operand" "0,0")
+ (match_operand:SI 2 "general_operand" "ri,rm")))]
+ ""
+ "* return AS2 (sub%L0,%2,%0);")
+
+(define_insn "subhi3"
+ [(set (match_operand:HI 0 "general_operand" "=rm,r")
+ (minus:HI (match_operand:HI 1 "general_operand" "0,0")
+ (match_operand:HI 2 "general_operand" "ri,rm")))]
+ ""
+ "* return AS2 (sub%W0,%2,%0);")
+
+(define_insn "subqi3"
+ [(set (match_operand:QI 0 "general_operand" "=qm,q")
+ (minus:QI (match_operand:QI 1 "general_operand" "0,0")
+ (match_operand:QI 2 "general_operand" "qn,qmn")))]
+ ""
+ "* return AS2 (sub%B0,%2,%0);")
+
+;; The patterns that match these are at the end of this file.
+
+(define_expand "subxf3"
+ [(set (match_operand:XF 0 "register_operand" "")
+ (minus:XF (match_operand:XF 1 "nonimmediate_operand" "")
+ (match_operand:XF 2 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "")
+
+(define_expand "subdf3"
+ [(set (match_operand:DF 0 "register_operand" "")
+ (minus:DF (match_operand:DF 1 "nonimmediate_operand" "")
+ (match_operand:DF 2 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "")
+
+(define_expand "subsf3"
+ [(set (match_operand:SF 0 "register_operand" "")
+ (minus:SF (match_operand:SF 1 "nonimmediate_operand" "")
+ (match_operand:SF 2 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "")
+
+;;- multiply instructions
+
+;(define_insn "mulqi3"
+; [(set (match_operand:QI 0 "general_operand" "=a")
+; (mult:QI (match_operand:QI 1 "general_operand" "%0")
+; (match_operand:QI 2 "general_operand" "qm")))]
+; ""
+; "imul%B0 %2,%0")
+
+(define_insn ""
+ [(set (match_operand:HI 0 "general_operand" "=r")
+ (mult:HI (match_operand:HI 1 "general_operand" "%0")
+ (match_operand:HI 2 "general_operand" "r")))]
+ "GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x80"
+ "* return AS2 (imul%W0,%2,%0);")
+
+(define_insn "mulhi3"
+ [(set (match_operand:HI 0 "general_operand" "=r,r")
+ (mult:HI (match_operand:HI 1 "general_operand" "%0,rm")
+ (match_operand:HI 2 "general_operand" "g,i")))]
+ ""
+ "*
+{
+ if (GET_CODE (operands[1]) == REG
+ && REGNO (operands[1]) == REGNO (operands[0])
+ && (GET_CODE (operands[2]) == MEM || GET_CODE (operands[2]) == REG))
+ /* Assembler has weird restrictions. */
+ return AS2 (imul%W0,%2,%0);
+ return AS3 (imul%W0,%2,%1,%0);
+}")
+
+(define_insn ""
+ [(set (match_operand:SI 0 "general_operand" "=r")
+ (mult:SI (match_operand:SI 1 "general_operand" "%0")
+ (match_operand:SI 2 "general_operand" "r")))]
+ "GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x80"
+ "* return AS2 (imul%L0,%2,%0);")
+
+(define_insn "mulsi3"
+ [(set (match_operand:SI 0 "general_operand" "=r,r")
+ (mult:SI (match_operand:SI 1 "general_operand" "%0,rm")
+ (match_operand:SI 2 "general_operand" "g,i")))]
+ ""
+ "*
+{
+ if (GET_CODE (operands[1]) == REG
+ && REGNO (operands[1]) == REGNO (operands[0])
+ && (GET_CODE (operands[2]) == MEM || GET_CODE (operands[2]) == REG))
+ /* Assembler has weird restrictions. */
+ return AS2 (imul%L0,%2,%0);
+ return AS3 (imul%L0,%2,%1,%0);
+}")
+
+(define_insn "umulqihi3"
+ [(set (match_operand:HI 0 "general_operand" "=a")
+ (mult:HI (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "%0"))
+ (zero_extend:HI (match_operand:QI 2 "nonimmediate_operand" "qm"))))]
+ ""
+ "mul%B0 %2")
+
+(define_insn "mulqihi3"
+ [(set (match_operand:HI 0 "general_operand" "=a")
+ (mult:HI (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "%0"))
+ (sign_extend:HI (match_operand:QI 2 "nonimmediate_operand" "qm"))))]
+ ""
+ "imul%B0 %2")
+
+(define_insn "umulsidi3"
+ [(set (match_operand:DI 0 "register_operand" "=A")
+ (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "%0"))
+ (zero_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm"))))]
+ ""
+ "mul%L0 %2")
+
+(define_insn "mulsidi3"
+ [(set (match_operand:DI 0 "register_operand" "=A")
+ (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "%0"))
+ (sign_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm"))))]
+ ""
+ "imul%L0 %2")
+
+;; The patterns that match these are at the end of this file.
+
+(define_expand "mulxf3"
+ [(set (match_operand:XF 0 "register_operand" "")
+ (mult:XF (match_operand:XF 1 "nonimmediate_operand" "")
+ (match_operand:XF 2 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "")
+
+(define_expand "muldf3"
+ [(set (match_operand:DF 0 "register_operand" "")
+ (mult:DF (match_operand:DF 1 "nonimmediate_operand" "")
+ (match_operand:DF 2 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "")
+
+(define_expand "mulsf3"
+ [(set (match_operand:SF 0 "register_operand" "")
+ (mult:SF (match_operand:SF 1 "nonimmediate_operand" "")
+ (match_operand:SF 2 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "")
+
+;;- divide instructions
+
+(define_insn "divqi3"
+ [(set (match_operand:QI 0 "general_operand" "=a")
+ (div:QI (match_operand:HI 1 "general_operand" "0")
+ (match_operand:QI 2 "general_operand" "qm")))]
+ ""
+ "idiv%B0 %2")
+
+(define_insn "udivqi3"
+ [(set (match_operand:QI 0 "general_operand" "=a")
+ (udiv:QI (match_operand:HI 1 "general_operand" "0")
+ (match_operand:QI 2 "general_operand" "qm")))]
+ ""
+ "div%B0 %2")
+
+;; The patterns that match these are at the end of this file.
+
+(define_expand "divxf3"
+ [(set (match_operand:XF 0 "register_operand" "")
+ (div:XF (match_operand:XF 1 "nonimmediate_operand" "")
+ (match_operand:XF 2 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "")
+
+(define_expand "divdf3"
+ [(set (match_operand:DF 0 "register_operand" "")
+ (div:DF (match_operand:DF 1 "nonimmediate_operand" "")
+ (match_operand:DF 2 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "")
+
+(define_expand "divsf3"
+ [(set (match_operand:SF 0 "register_operand" "")
+ (div:SF (match_operand:SF 1 "nonimmediate_operand" "")
+ (match_operand:SF 2 "nonimmediate_operand" "")))]
+ "TARGET_80387"
+ "")
+
+;; Remainder instructions.
+
+(define_insn "divmodsi4"
+ [(set (match_operand:SI 0 "register_operand" "=a")
+ (div:SI (match_operand:SI 1 "register_operand" "0")
+ (match_operand:SI 2 "general_operand" "rm")))
+ (set (match_operand:SI 3 "register_operand" "=&d")
+ (mod:SI (match_dup 1) (match_dup 2)))]
+ ""
+ "*
+{
+#ifdef INTEL_SYNTAX
+ output_asm_insn (\"cdq\", operands);
+#else
+ output_asm_insn (\"cltd\", operands);
+#endif
+ return AS1 (idiv%L0,%2);
+}")
+
+(define_insn "divmodhi4"
+ [(set (match_operand:HI 0 "register_operand" "=a")
+ (div:HI (match_operand:HI 1 "register_operand" "0")
+ (match_operand:HI 2 "general_operand" "rm")))
+ (set (match_operand:HI 3 "register_operand" "=&d")
+ (mod:HI (match_dup 1) (match_dup 2)))]
+ ""
+ "cwtd\;idiv%W0 %2")
+
+;; ??? Can we make gcc zero extend operand[0]?
+(define_insn "udivmodsi4"
+ [(set (match_operand:SI 0 "register_operand" "=a")
+ (udiv:SI (match_operand:SI 1 "register_operand" "0")
+ (match_operand:SI 2 "general_operand" "rm")))
+ (set (match_operand:SI 3 "register_operand" "=&d")
+ (umod:SI (match_dup 1) (match_dup 2)))]
+ ""
+ "*
+{
+ output_asm_insn (AS2 (xor%L3,%3,%3), operands);
+ return AS1 (div%L0,%2);
+}")
+
+;; ??? Can we make gcc zero extend operand[0]?
+(define_insn "udivmodhi4"
+ [(set (match_operand:HI 0 "register_operand" "=a")
+ (udiv:HI (match_operand:HI 1 "register_operand" "0")
+ (match_operand:HI 2 "general_operand" "rm")))
+ (set (match_operand:HI 3 "register_operand" "=&d")
+ (umod:HI (match_dup 1) (match_dup 2)))]
+ ""
+ "*
+{
+ output_asm_insn (AS2 (xor%W0,%3,%3), operands);
+ return AS1 (div%W0,%2);
+}")
+
+/*
+;;this should be a valid double division which we may want to add
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=a")
+ (udiv:DI (match_operand:DI 1 "register_operand" "a")
+ (match_operand:SI 2 "general_operand" "rm")))
+ (set (match_operand:SI 3 "register_operand" "=d")
+ (umod:SI (match_dup 1) (match_dup 2)))]
+ ""
+ "div%L0 %2,%0")
+*/
+
+;;- and instructions
+
+;; On i386,
+;; movzbl %bl,%ebx
+;; is faster than
+;; andl $255,%ebx
+;;
+;; but if the reg is %eax, then the "andl" is faster.
+;;
+;; On i486, the "andl" is always faster than the "movzbl".
+;;
+;; On both i386 and i486, a three operand AND is as fast with movzbl or
+;; movzwl as with andl, if operands[0] != operands[1].
+
+;; The `r' in `rm' for operand 3 looks redundant, but it causes
+;; optional reloads to be generated if op 3 is a pseudo in a stack slot.
+
+;; ??? What if we only change one byte of an offsettable memory reference?
+(define_insn "andsi3"
+ [(set (match_operand:SI 0 "general_operand" "=r,r,rm,r")
+ (and:SI (match_operand:SI 1 "general_operand" "%rm,qm,0,0")
+ (match_operand:SI 2 "general_operand" "L,K,ri,rm")))]
+ ""
+ "*
+{
+ if (GET_CODE (operands[2]) == CONST_INT
+ && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
+ {
+ if (INTVAL (operands[2]) == 0xffff && REG_P (operands[0])
+ && (! REG_P (operands[1])
+ || REGNO (operands[0]) != 0 || REGNO (operands[1]) != 0)
+ && (! TARGET_486 || ! rtx_equal_p (operands[0], operands[1])))
+ {
+ /* ??? tege: Should forget CC_STATUS only if we clobber a
+ remembered operand. Fix that later. */
+ CC_STATUS_INIT;
+#ifdef INTEL_SYNTAX
+ return AS2 (movzx,%w1,%0);
+#else
+ return AS2 (movz%W0%L0,%w1,%0);
+#endif
+ }
+
+ if (INTVAL (operands[2]) == 0xff && REG_P (operands[0])
+ && !(REG_P (operands[1]) && NON_QI_REG_P (operands[1]))
+ && (! REG_P (operands[1])
+ || REGNO (operands[0]) != 0 || REGNO (operands[1]) != 0)
+ && (! TARGET_486 || ! rtx_equal_p (operands[0], operands[1])))
+ {
+ /* ??? tege: Should forget CC_STATUS only if we clobber a
+ remembered operand. Fix that later. */
+ CC_STATUS_INIT;
+#ifdef INTEL_SYNTAX
+ return AS2 (movzx,%b1,%0);
+#else
+ return AS2 (movz%B0%L0,%b1,%0);
+#endif
+ }
+
+ if (QI_REG_P (operands[0]) && ~(INTVAL (operands[2]) | 0xff) == 0)
+ {
+ CC_STATUS_INIT;
+
+ if (INTVAL (operands[2]) == 0xffffff00)
+ {
+ operands[2] = const0_rtx;
+ return AS2 (mov%B0,%2,%b0);
+ }
+
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0xff);
+ return AS2 (and%B0,%2,%b0);
+ }
+
+ if (QI_REG_P (operands[0]) && ~(INTVAL (operands[2]) | 0xff00) == 0)
+ {
+ CC_STATUS_INIT;
+
+ if (INTVAL (operands[2]) == 0xffff00ff)
+ {
+ operands[2] = const0_rtx;
+ return AS2 (mov%B0,%2,%h0);
+ }
+
+ operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff);
+ return AS2 (and%B0,%2,%h0);
+ }
+
+ if (GET_CODE (operands[0]) == MEM && INTVAL (operands[2]) == 0xffff0000)
+ {
+ operands[2] = const0_rtx;
+ return AS2 (mov%W0,%2,%w0);
+ }
+ }
+
+ return AS2 (and%L0,%2,%0);
+}")
+
+(define_insn "andhi3"
+ [(set (match_operand:HI 0 "general_operand" "=rm,r")
+ (and:HI (match_operand:HI 1 "general_operand" "%0,0")
+ (match_operand:HI 2 "general_operand" "ri,rm")))]
+ ""
+ "*
+{
+ if (GET_CODE (operands[2]) == CONST_INT
+ && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
+ {
+ /* Can we ignore the upper byte? */
+ if ((! REG_P (operands[0]) || QI_REG_P (operands[0]))
+ && (INTVAL (operands[2]) & 0xff00) == 0xff00)
+ {
+ CC_STATUS_INIT;
+
+ if ((INTVAL (operands[2]) & 0xff) == 0)
+ {
+ operands[2] = const0_rtx;
+ return AS2 (mov%B0,%2,%b0);
+ }
+
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0xff);
+ return AS2 (and%B0,%2,%b0);
+ }
+
+ /* Can we ignore the lower byte? */
+ /* ??? what about offsettable memory references? */
+ if (QI_REG_P (operands[0]) && (INTVAL (operands[2]) & 0xff) == 0xff)
+ {
+ CC_STATUS_INIT;
+
+ if ((INTVAL (operands[2]) & 0xff00) == 0)
+ {
+ operands[2] = const0_rtx;
+ return AS2 (mov%B0,%2,%h0);
+ }
+
+ operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff);
+ return AS2 (and%B0,%2,%h0);
+ }
+ }
+
+ return AS2 (and%W0,%2,%0);
+}")
+
+(define_insn "andqi3"
+ [(set (match_operand:QI 0 "general_operand" "=qm,q")
+ (and:QI (match_operand:QI 1 "general_operand" "%0,0")
+ (match_operand:QI 2 "general_operand" "qn,qmn")))]
+ ""
+ "* return AS2 (and%B0,%2,%0);")
+
+/* I am nervous about these two.. add them later..
+;I presume this means that we have something in say op0= eax which is small
+;and we want to and it with memory so we can do this by just an
+;andb m,%al and have success.
+(define_insn ""
+ [(set (match_operand:SI 0 "general_operand" "=r")
+ (and:SI (zero_extend:SI
+ (match_operand:HI 1 "nonimmediate_operand" "rm"))
+ (match_operand:SI 2 "general_operand" "0")))]
+ "GET_CODE (operands[2]) == CONST_INT
+ && (unsigned int) INTVAL (operands[2]) < (1 << GET_MODE_BITSIZE (HImode))"
+ "and%W0 %1,%0")
+
+(define_insn ""
+ [(set (match_operand:SI 0 "general_operand" "=q")
+ (and:SI
+ (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "qm"))
+ (match_operand:SI 2 "general_operand" "0")))]
+ "GET_CODE (operands[2]) == CONST_INT
+ && (unsigned int) INTVAL (operands[2]) < (1 << GET_MODE_BITSIZE (QImode))"
+ "and%L0 %1,%0")
+
+*/
+
+;;- Bit set (inclusive or) instructions
+
+;; ??? What if we only change one byte of an offsettable memory reference?
+(define_insn "iorsi3"
+ [(set (match_operand:SI 0 "general_operand" "=rm,r")
+ (ior:SI (match_operand:SI 1 "general_operand" "%0,0")
+ (match_operand:SI 2 "general_operand" "ri,rm")))]
+ ""
+ "*
+{
+ if (GET_CODE (operands[2]) == CONST_INT
+ && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
+ {
+ if ((! REG_P (operands[0]) || QI_REG_P (operands[0]))
+ && (INTVAL (operands[2]) & ~0xff) == 0)
+ {
+ CC_STATUS_INIT;
+
+ if (INTVAL (operands[2]) == 0xff)
+ return AS2 (mov%B0,%2,%b0);
+
+ return AS2 (or%B0,%2,%b0);
+ }
+
+ if (QI_REG_P (operands[0]) && (INTVAL (operands[2]) & ~0xff00) == 0)
+ {
+ CC_STATUS_INIT;
+ operands[2] = GEN_INT (INTVAL (operands[2]) >> 8);
+
+ if (INTVAL (operands[2]) == 0xff)
+ return AS2 (mov%B0,%2,%h0);
+
+ return AS2 (or%B0,%2,%h0);
+ }
+ }
+
+ return AS2 (or%L0,%2,%0);
+}")
+
+(define_insn "iorhi3"
+ [(set (match_operand:HI 0 "general_operand" "=rm,r")
+ (ior:HI (match_operand:HI 1 "general_operand" "%0,0")
+ (match_operand:HI 2 "general_operand" "ri,rm")))]
+ ""
+ "*
+{
+ if (GET_CODE (operands[2]) == CONST_INT
+ && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
+ {
+ /* Can we ignore the upper byte? */
+ if ((! REG_P (operands[0]) || QI_REG_P (operands[0]))
+ && (INTVAL (operands[2]) & 0xff00) == 0)
+ {
+ CC_STATUS_INIT;
+ if (INTVAL (operands[2]) & 0xffff0000)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff);
+
+ if (INTVAL (operands[2]) == 0xff)
+ return AS2 (mov%B0,%2,%b0);
+
+ return AS2 (or%B0,%2,%b0);
+ }
+
+ /* Can we ignore the lower byte? */
+ /* ??? what about offsettable memory references? */
+ if (QI_REG_P (operands[0])
+ && (INTVAL (operands[2]) & 0xff) == 0)
+ {
+ CC_STATUS_INIT;
+ operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff);
+
+ if (INTVAL (operands[2]) == 0xff)
+ return AS2 (mov%B0,%2,%h0);
+
+ return AS2 (or%B0,%2,%h0);
+ }
+ }
+
+ return AS2 (or%W0,%2,%0);
+}")
+
+(define_insn "iorqi3"
+ [(set (match_operand:QI 0 "general_operand" "=qm,q")
+ (ior:QI (match_operand:QI 1 "general_operand" "%0,0")
+ (match_operand:QI 2 "general_operand" "qn,qmn")))]
+ ""
+ "* return AS2 (or%B0,%2,%0);")
+
+;;- xor instructions
+
+;; ??? What if we only change one byte of an offsettable memory reference?
+(define_insn "xorsi3"
+ [(set (match_operand:SI 0 "general_operand" "=rm,r")
+ (xor:SI (match_operand:SI 1 "general_operand" "%0,0")
+ (match_operand:SI 2 "general_operand" "ri,rm")))]
+ ""
+ "*
+{
+ if (GET_CODE (operands[2]) == CONST_INT
+ && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
+ {
+ if ((! REG_P (operands[0]) || QI_REG_P (operands[0]))
+ && (INTVAL (operands[2]) & ~0xff) == 0)
+ {
+ CC_STATUS_INIT;
+
+ if (INTVAL (operands[2]) == 0xff)
+ return AS1 (not%B0,%b0);
+
+ return AS2 (xor%B0,%2,%b0);
+ }
+
+ if (QI_REG_P (operands[0]) && (INTVAL (operands[2]) & ~0xff00) == 0)
+ {
+ CC_STATUS_INIT;
+ operands[2] = GEN_INT (INTVAL (operands[2]) >> 8);
+
+ if (INTVAL (operands[2]) == 0xff)
+ return AS1 (not%B0,%h0);
+
+ return AS2 (xor%B0,%2,%h0);
+ }
+ }
+
+ return AS2 (xor%L0,%2,%0);
+}")
+
+(define_insn "xorhi3"
+ [(set (match_operand:HI 0 "general_operand" "=rm,r")
+ (xor:HI (match_operand:HI 1 "general_operand" "%0,0")
+ (match_operand:HI 2 "general_operand" "ri,rm")))]
+ ""
+ "*
+{
+ if (GET_CODE (operands[2]) == CONST_INT
+ && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
+ {
+ /* Can we ignore the upper byte? */
+ if ((! REG_P (operands[0]) || QI_REG_P (operands[0]))
+ && (INTVAL (operands[2]) & 0xff00) == 0)
+ {
+ CC_STATUS_INIT;
+ if (INTVAL (operands[2]) & 0xffff0000)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff);
+
+ if (INTVAL (operands[2]) == 0xff)
+ return AS1 (not%B0,%b0);
+
+ return AS2 (xor%B0,%2,%b0);
+ }
+
+ /* Can we ignore the lower byte? */
+ /* ??? what about offsettable memory references? */
+ if (QI_REG_P (operands[0])
+ && (INTVAL (operands[2]) & 0xff) == 0)
+ {
+ CC_STATUS_INIT;
+ operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff);
+
+ if (INTVAL (operands[2]) == 0xff)
+ return AS1 (not%B0,%h0);
+
+ return AS2 (xor%B0,%2,%h0);
+ }
+ }
+
+ return AS2 (xor%W0,%2,%0);
+}")
+
+(define_insn "xorqi3"
+ [(set (match_operand:QI 0 "general_operand" "=qm,q")
+ (xor:QI (match_operand:QI 1 "general_operand" "%0,0")
+ (match_operand:QI 2 "general_operand" "qn,qm")))]
+ ""
+ "* return AS2 (xor%B0,%2,%0);")
+
+;;- negation instructions
+
+(define_insn "negdi2"
+ [(set (match_operand:DI 0 "general_operand" "=&ro")
+ (neg:DI (match_operand:DI 1 "general_operand" "0")))]
+ ""
+ "*
+{
+ rtx xops[2], low[1], high[1];
+
+ CC_STATUS_INIT;
+
+ split_di (operands, 1, low, high);
+ xops[0] = const0_rtx;
+ xops[1] = high[0];
+
+ output_asm_insn (AS1 (neg%L0,%0), low);
+ output_asm_insn (AS2 (adc%L1,%0,%1), xops);
+ output_asm_insn (AS1 (neg%L0,%0), high);
+ RET;
+}")
+
+(define_insn "negsi2"
+ [(set (match_operand:SI 0 "general_operand" "=rm")
+ (neg:SI (match_operand:SI 1 "general_operand" "0")))]
+ ""
+ "neg%L0 %0")
+
+(define_insn "neghi2"
+ [(set (match_operand:HI 0 "general_operand" "=rm")
+ (neg:HI (match_operand:HI 1 "general_operand" "0")))]
+ ""
+ "neg%W0 %0")
+
+(define_insn "negqi2"
+ [(set (match_operand:QI 0 "general_operand" "=qm")
+ (neg:QI (match_operand:QI 1 "general_operand" "0")))]
+ ""
+ "neg%B0 %0")
+
+(define_insn "negsf2"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (neg:SF (match_operand:SF 1 "general_operand" "0")))]
+ "TARGET_80387"
+ "fchs")
+
+(define_insn "negdf2"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (neg:DF (match_operand:DF 1 "general_operand" "0")))]
+ "TARGET_80387"
+ "fchs")
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (neg:DF (float_extend:DF (match_operand:SF 1 "general_operand" "0"))))]
+ "TARGET_80387"
+ "fchs")
+
+(define_insn "negxf2"
+ [(set (match_operand:XF 0 "register_operand" "=f")
+ (neg:XF (match_operand:XF 1 "general_operand" "0")))]
+ "TARGET_80387"
+ "fchs")
+
+(define_insn ""
+ [(set (match_operand:XF 0 "register_operand" "=f")
+ (neg:XF (float_extend:XF (match_operand:DF 1 "general_operand" "0"))))]
+ "TARGET_80387"
+ "fchs")
+
+;; Absolute value instructions
+
+(define_insn "abssf2"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (abs:SF (match_operand:SF 1 "general_operand" "0")))]
+ "TARGET_80387"
+ "fabs")
+
+(define_insn "absdf2"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (abs:DF (match_operand:DF 1 "general_operand" "0")))]
+ "TARGET_80387"
+ "fabs")
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (abs:DF (float_extend:DF (match_operand:SF 1 "general_operand" "0"))))]
+ "TARGET_80387"
+ "fabs")
+
+(define_insn "absxf2"
+ [(set (match_operand:XF 0 "register_operand" "=f")
+ (abs:XF (match_operand:XF 1 "general_operand" "0")))]
+ "TARGET_80387"
+ "fabs")
+
+(define_insn ""
+ [(set (match_operand:XF 0 "register_operand" "=f")
+ (abs:XF (float_extend:XF (match_operand:DF 1 "general_operand" "0"))))]
+ "TARGET_80387"
+ "fabs")
+
+(define_insn "sqrtsf2"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (sqrt:SF (match_operand:SF 1 "general_operand" "0")))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) "
+ "fsqrt")
+
+(define_insn "sqrtdf2"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (sqrt:DF (match_operand:DF 1 "general_operand" "0")))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) "
+ "fsqrt")
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (sqrt:DF (float_extend:DF
+ (match_operand:SF 1 "general_operand" "0"))))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) "
+ "fsqrt")
+
+(define_insn "sqrtxf2"
+ [(set (match_operand:XF 0 "register_operand" "=f")
+ (sqrt:XF (match_operand:XF 1 "general_operand" "0")))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) "
+ "fsqrt")
+
+(define_insn ""
+ [(set (match_operand:XF 0 "register_operand" "=f")
+ (sqrt:XF (float_extend:XF
+ (match_operand:DF 1 "general_operand" "0"))))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) "
+ "fsqrt")
+
+(define_insn ""
+ [(set (match_operand:XF 0 "register_operand" "=f")
+ (sqrt:XF (float_extend:XF
+ (match_operand:SF 1 "general_operand" "0"))))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) "
+ "fsqrt")
+
+(define_insn "sindf2"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (unspec:DF [(match_operand:DF 1 "register_operand" "0")] 1))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) "
+ "fsin")
+
+(define_insn "sinsf2"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (unspec:SF [(match_operand:SF 1 "register_operand" "0")] 1))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) "
+ "fsin")
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (unspec:DF [(float_extend:DF
+ (match_operand:SF 1 "register_operand" "0"))] 1))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) "
+ "fsin")
+
+(define_insn "cosdf2"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (unspec:DF [(match_operand:DF 1 "register_operand" "0")] 2))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) "
+ "fcos")
+
+(define_insn "cossf2"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (unspec:SF [(match_operand:SF 1 "register_operand" "0")] 2))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) "
+ "fcos")
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (unspec:DF [(float_extend:DF
+ (match_operand:SF 1 "register_operand" "0"))] 2))]
+ "! TARGET_NO_FANCY_MATH_387 && TARGET_80387
+ && (TARGET_IEEE_FP || flag_fast_math) "
+ "fcos")
+
+;;- one complement instructions
+
+(define_insn "one_cmplsi2"
+ [(set (match_operand:SI 0 "general_operand" "=rm")
+ (not:SI (match_operand:SI 1 "general_operand" "0")))]
+ ""
+ "not%L0 %0")
+
+(define_insn "one_cmplhi2"
+ [(set (match_operand:HI 0 "general_operand" "=rm")
+ (not:HI (match_operand:HI 1 "general_operand" "0")))]
+ ""
+ "not%W0 %0")
+
+(define_insn "one_cmplqi2"
+ [(set (match_operand:QI 0 "general_operand" "=qm")
+ (not:QI (match_operand:QI 1 "general_operand" "0")))]
+ ""
+ "not%B0 %0")
+
+;;- arithmetic shift instructions
+
+;; DImode shifts are implemented using the i386 "shift double" opcode,
+;; which is written as "sh[lr]d[lw] imm,reg,reg/mem". If the shift count
+;; is variable, then the count is in %cl and the "imm" operand is dropped
+;; from the assembler input.
+
+;; This instruction shifts the target reg/mem as usual, but instead of
+;; shifting in zeros, bits are shifted in from reg operand. If the insn
+;; is a left shift double, bits are taken from the high order bits of
+;; reg, else if the insn is a shift right double, bits are taken from the
+;; low order bits of reg. So if %eax is "1234" and %edx is "5678",
+;; "shldl $8,%edx,%eax" leaves %edx unchanged and sets %eax to "2345".
+
+;; Since sh[lr]d does not change the `reg' operand, that is done
+;; separately, making all shifts emit pairs of shift double and normal
+;; shift. Since sh[lr]d does not shift more than 31 bits, and we wish to
+;; support a 63 bit shift, each shift where the count is in a reg expands
+;; to three pairs. If the overall shift is by N bits, then the first two
+;; pairs shift by N / 2 and the last pair by N & 1.
+
+;; If the shift count is a constant, we need never emit more than one
+;; shift pair, instead using moves and sign extension for counts greater
+;; than 31.
+
+(define_expand "ashldi3"
+ [(set (match_operand:DI 0 "register_operand" "")
+ (ashift:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:QI 2 "nonmemory_operand" "")))]
+ ""
+ "
+{
+ if (GET_CODE (operands[2]) != CONST_INT
+ || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J'))
+ {
+ operands[2] = copy_to_mode_reg (QImode, operands[2]);
+ emit_insn (gen_ashldi3_non_const_int (operands[0], operands[1],
+ operands[2]));
+ }
+ else
+ emit_insn (gen_ashldi3_const_int (operands[0], operands[1], operands[2]));
+
+ DONE;
+}")
+
+(define_insn "ashldi3_const_int"
+ [(set (match_operand:DI 0 "register_operand" "=&r")
+ (ashift:DI (match_operand:DI 1 "register_operand" "0")
+ (match_operand:QI 2 "const_int_operand" "J")))]
+ ""
+ "*
+{
+ rtx xops[4], low[1], high[1];
+
+ CC_STATUS_INIT;
+
+ split_di (operands, 1, low, high);
+ xops[0] = operands[2];
+ xops[1] = const1_rtx;
+ xops[2] = low[0];
+ xops[3] = high[0];
+
+ if (INTVAL (xops[0]) > 31)
+ {
+ output_asm_insn (AS2 (mov%L3,%2,%3), xops); /* Fast shift by 32 */
+ output_asm_insn (AS2 (xor%L2,%2,%2), xops);
+
+ if (INTVAL (xops[0]) > 32)
+ {
+ xops[0] = GEN_INT (INTVAL (xops[0]) - 32);
+ output_asm_insn (AS2 (sal%L3,%0,%3), xops); /* Remaining shift */
+ }
+ }
+ else
+ {
+ output_asm_insn (AS3 (shld%L3,%0,%2,%3), xops);
+ output_asm_insn (AS2 (sal%L2,%0,%2), xops);
+ }
+ RET;
+}")
+
+(define_insn "ashldi3_non_const_int"
+ [(set (match_operand:DI 0 "register_operand" "=&r")
+ (ashift:DI (match_operand:DI 1 "register_operand" "0")
+ (match_operand:QI 2 "register_operand" "c")))
+ (clobber (match_dup 2))]
+ ""
+ "*
+{
+ rtx xops[4], low[1], high[1];
+
+ CC_STATUS_INIT;
+
+ split_di (operands, 1, low, high);
+ xops[0] = operands[2];
+ xops[1] = const1_rtx;
+ xops[2] = low[0];
+ xops[3] = high[0];
+
+ output_asm_insn (AS2 (ror%B0,%1,%0), xops); /* shift count / 2 */
+
+ output_asm_insn (AS3_SHIFT_DOUBLE (shld%L3,%0,%2,%3), xops);
+ output_asm_insn (AS2 (sal%L2,%0,%2), xops);
+ output_asm_insn (AS3_SHIFT_DOUBLE (shld%L3,%0,%2,%3), xops);
+ output_asm_insn (AS2 (sal%L2,%0,%2), xops);
+
+ xops[1] = GEN_INT (7); /* shift count & 1 */
+
+ output_asm_insn (AS2 (shr%B0,%1,%0), xops);
+
+ output_asm_insn (AS3_SHIFT_DOUBLE (shld%L3,%0,%2,%3), xops);
+ output_asm_insn (AS2 (sal%L2,%0,%2), xops);
+
+ RET;
+}")
+
+;; On i386 and i486, "addl reg,reg" is faster than "sall $1,reg"
+;; On i486, movl/sall appears slightly faster than leal, but the leal
+;; is smaller - use leal for now unless the shift count is 1.
+
+(define_insn "ashlsi3"
+ [(set (match_operand:SI 0 "general_operand" "=r,rm")
+ (ashift:SI (match_operand:SI 1 "general_operand" "r,0")
+ (match_operand:SI 2 "nonmemory_operand" "M,cI")))]
+ ""
+ "*
+{
+ if (REG_P (operands[0]) && REGNO (operands[0]) != REGNO (operands[1]))
+ {
+ if (TARGET_486 && INTVAL (operands[2]) == 1)
+ {
+ output_asm_insn (AS2 (mov%L0,%1,%0), operands);
+ return AS2 (add%L0,%1,%0);
+ }
+ else
+ {
+ CC_STATUS_INIT;
+
+ if (operands[1] == stack_pointer_rtx)
+ {
+ output_asm_insn (AS2 (mov%L0,%1,%0), operands);
+ operands[1] = operands[0];
+ }
+ operands[1] = gen_rtx (MULT, SImode, operands[1],
+ GEN_INT (1 << INTVAL (operands[2])));
+ return AS2 (lea%L0,%a1,%0);
+ }
+ }
+
+ if (REG_P (operands[2]))
+ return AS2 (sal%L0,%b2,%0);
+
+ if (REG_P (operands[0]) && operands[2] == const1_rtx)
+ return AS2 (add%L0,%0,%0);
+
+ return AS2 (sal%L0,%2,%0);
+}")
+
+(define_insn "ashlhi3"
+ [(set (match_operand:HI 0 "general_operand" "=rm")
+ (ashift:HI (match_operand:HI 1 "general_operand" "0")
+ (match_operand:HI 2 "nonmemory_operand" "cI")))]
+ ""
+ "*
+{
+ if (REG_P (operands[2]))
+ return AS2 (sal%W0,%b2,%0);
+
+ if (REG_P (operands[0]) && operands[2] == const1_rtx)
+ return AS2 (add%W0,%0,%0);
+
+ return AS2 (sal%W0,%2,%0);
+}")
+
+(define_insn "ashlqi3"
+ [(set (match_operand:QI 0 "general_operand" "=qm")
+ (ashift:QI (match_operand:QI 1 "general_operand" "0")
+ (match_operand:QI 2 "nonmemory_operand" "cI")))]
+ ""
+ "*
+{
+ if (REG_P (operands[2]))
+ return AS2 (sal%B0,%b2,%0);
+
+ if (REG_P (operands[0]) && operands[2] == const1_rtx)
+ return AS2 (add%B0,%0,%0);
+
+ return AS2 (sal%B0,%2,%0);
+}")
+
+;; See comment above `ashldi3' about how this works.
+
+(define_expand "ashrdi3"
+ [(set (match_operand:DI 0 "register_operand" "")
+ (ashiftrt:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:QI 2 "nonmemory_operand" "")))]
+ ""
+ "
+{
+ if (GET_CODE (operands[2]) != CONST_INT
+ || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J'))
+ {
+ operands[2] = copy_to_mode_reg (QImode, operands[2]);
+ emit_insn (gen_ashrdi3_non_const_int (operands[0], operands[1],
+ operands[2]));
+ }
+ else
+ emit_insn (gen_ashrdi3_const_int (operands[0], operands[1], operands[2]));
+
+ DONE;
+}")
+
+(define_insn "ashrdi3_const_int"
+ [(set (match_operand:DI 0 "register_operand" "=&r")
+ (ashiftrt:DI (match_operand:DI 1 "register_operand" "0")
+ (match_operand:QI 2 "const_int_operand" "J")))]
+ ""
+ "*
+{
+ rtx xops[4], low[1], high[1];
+
+ CC_STATUS_INIT;
+
+ split_di (operands, 1, low, high);
+ xops[0] = operands[2];
+ xops[1] = const1_rtx;
+ xops[2] = low[0];
+ xops[3] = high[0];
+
+ if (INTVAL (xops[0]) > 31)
+ {
+ xops[1] = GEN_INT (31);
+ output_asm_insn (AS2 (mov%L2,%3,%2), xops);
+ output_asm_insn (AS2 (sar%L3,%1,%3), xops); /* shift by 32 */
+
+ if (INTVAL (xops[0]) > 32)
+ {
+ xops[0] = GEN_INT (INTVAL (xops[0]) - 32);
+ output_asm_insn (AS2 (sar%L2,%0,%2), xops); /* Remaining shift */
+ }
+ }
+ else
+ {
+ output_asm_insn (AS3 (shrd%L2,%0,%3,%2), xops);
+ output_asm_insn (AS2 (sar%L3,%0,%3), xops);
+ }
+
+ RET;
+}")
+
+(define_insn "ashrdi3_non_const_int"
+ [(set (match_operand:DI 0 "register_operand" "=&r")
+ (ashiftrt:DI (match_operand:DI 1 "register_operand" "0")
+ (match_operand:QI 2 "register_operand" "c")))
+ (clobber (match_dup 2))]
+ ""
+ "*
+{
+ rtx xops[4], low[1], high[1];
+
+ CC_STATUS_INIT;
+
+ split_di (operands, 1, low, high);
+ xops[0] = operands[2];
+ xops[1] = const1_rtx;
+ xops[2] = low[0];
+ xops[3] = high[0];
+
+ output_asm_insn (AS2 (ror%B0,%1,%0), xops); /* shift count / 2 */
+
+ output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
+ output_asm_insn (AS2 (sar%L3,%0,%3), xops);
+ output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
+ output_asm_insn (AS2 (sar%L3,%0,%3), xops);
+
+ xops[1] = GEN_INT (7); /* shift count & 1 */
+
+ output_asm_insn (AS2 (shr%B0,%1,%0), xops);
+
+ output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
+ output_asm_insn (AS2 (sar%L3,%0,%3), xops);
+
+ RET;
+}")
+
+(define_insn "ashrsi3"
+ [(set (match_operand:SI 0 "general_operand" "=rm")
+ (ashiftrt:SI (match_operand:SI 1 "general_operand" "0")
+ (match_operand:SI 2 "nonmemory_operand" "cI")))]
+ ""
+ "*
+{
+ if (REG_P (operands[2]))
+ return AS2 (sar%L0,%b2,%0);
+ else
+ return AS2 (sar%L0,%2,%0);
+}")
+
+(define_insn "ashrhi3"
+ [(set (match_operand:HI 0 "general_operand" "=rm")
+ (ashiftrt:HI (match_operand:HI 1 "general_operand" "0")
+ (match_operand:HI 2 "nonmemory_operand" "cI")))]
+ ""
+ "*
+{
+ if (REG_P (operands[2]))
+ return AS2 (sar%W0,%b2,%0);
+ else
+ return AS2 (sar%W0,%2,%0);
+}")
+
+(define_insn "ashrqi3"
+ [(set (match_operand:QI 0 "general_operand" "=qm")
+ (ashiftrt:QI (match_operand:QI 1 "general_operand" "0")
+ (match_operand:QI 2 "nonmemory_operand" "cI")))]
+ ""
+ "*
+{
+ if (REG_P (operands[2]))
+ return AS2 (sar%B0,%b2,%0);
+ else
+ return AS2 (sar%B0,%2,%0);
+}")
+
+;;- logical shift instructions
+
+;; See comment above `ashldi3' about how this works.
+
+(define_expand "lshrdi3"
+ [(set (match_operand:DI 0 "register_operand" "")
+ (lshiftrt:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:QI 2 "nonmemory_operand" "")))]
+ ""
+ "
+{
+ if (GET_CODE (operands[2]) != CONST_INT
+ || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J'))
+ {
+ operands[2] = copy_to_mode_reg (QImode, operands[2]);
+ emit_insn (gen_lshrdi3_non_const_int (operands[0], operands[1],
+ operands[2]));
+ }
+ else
+ emit_insn (gen_lshrdi3_const_int (operands[0], operands[1], operands[2]));
+
+ DONE;
+}")
+
+(define_insn "lshrdi3_const_int"
+ [(set (match_operand:DI 0 "register_operand" "=&r")
+ (lshiftrt:DI (match_operand:DI 1 "register_operand" "0")
+ (match_operand:QI 2 "const_int_operand" "J")))]
+ ""
+ "*
+{
+ rtx xops[4], low[1], high[1];
+
+ CC_STATUS_INIT;
+
+ split_di (operands, 1, low, high);
+ xops[0] = operands[2];
+ xops[1] = const1_rtx;
+ xops[2] = low[0];
+ xops[3] = high[0];
+
+ if (INTVAL (xops[0]) > 31)
+ {
+ output_asm_insn (AS2 (mov%L2,%3,%2), xops); /* Fast shift by 32 */
+ output_asm_insn (AS2 (xor%L3,%3,%3), xops);
+
+ if (INTVAL (xops[0]) > 32)
+ {
+ xops[0] = GEN_INT (INTVAL (xops[0]) - 32);
+ output_asm_insn (AS2 (shr%L2,%0,%2), xops); /* Remaining shift */
+ }
+ }
+ else
+ {
+ output_asm_insn (AS3 (shrd%L2,%0,%3,%2), xops);
+ output_asm_insn (AS2 (shr%L3,%0,%3), xops);
+ }
+
+ RET;
+}")
+
+(define_insn "lshrdi3_non_const_int"
+ [(set (match_operand:DI 0 "register_operand" "=&r")
+ (lshiftrt:DI (match_operand:DI 1 "register_operand" "0")
+ (match_operand:QI 2 "register_operand" "c")))
+ (clobber (match_dup 2))]
+ ""
+ "*
+{
+ rtx xops[4], low[1], high[1];
+
+ CC_STATUS_INIT;
+
+ split_di (operands, 1, low, high);
+ xops[0] = operands[2];
+ xops[1] = const1_rtx;
+ xops[2] = low[0];
+ xops[3] = high[0];
+
+ output_asm_insn (AS2 (ror%B0,%1,%0), xops); /* shift count / 2 */
+
+ output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
+ output_asm_insn (AS2 (shr%L3,%0,%3), xops);
+ output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
+ output_asm_insn (AS2 (shr%L3,%0,%3), xops);
+
+ xops[1] = GEN_INT (7); /* shift count & 1 */
+
+ output_asm_insn (AS2 (shr%B0,%1,%0), xops);
+
+ output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
+ output_asm_insn (AS2 (shr%L3,%0,%3), xops);
+
+ RET;
+}")
+
+(define_insn "lshrsi3"
+ [(set (match_operand:SI 0 "general_operand" "=rm")
+ (lshiftrt:SI (match_operand:SI 1 "general_operand" "0")
+ (match_operand:SI 2 "nonmemory_operand" "cI")))]
+ ""
+ "*
+{
+ if (REG_P (operands[2]))
+ return AS2 (shr%L0,%b2,%0);
+ else
+ return AS2 (shr%L0,%2,%1);
+}")
+
+(define_insn "lshrhi3"
+ [(set (match_operand:HI 0 "general_operand" "=rm")
+ (lshiftrt:HI (match_operand:HI 1 "general_operand" "0")
+ (match_operand:HI 2 "nonmemory_operand" "cI")))]
+ ""
+ "*
+{
+ if (REG_P (operands[2]))
+ return AS2 (shr%W0,%b2,%0);
+ else
+ return AS2 (shr%W0,%2,%0);
+}")
+
+(define_insn "lshrqi3"
+ [(set (match_operand:QI 0 "general_operand" "=qm")
+ (lshiftrt:QI (match_operand:QI 1 "general_operand" "0")
+ (match_operand:QI 2 "nonmemory_operand" "cI")))]
+ ""
+ "*
+{
+ if (REG_P (operands[2]))
+ return AS2 (shr%B0,%b2,%0);
+ else
+ return AS2 (shr%B0,%2,%0);
+}")
+
+;;- rotate instructions
+
+(define_insn "rotlsi3"
+ [(set (match_operand:SI 0 "general_operand" "=rm")
+ (rotate:SI (match_operand:SI 1 "general_operand" "0")
+ (match_operand:SI 2 "nonmemory_operand" "cI")))]
+ ""
+ "*
+{
+ if (REG_P (operands[2]))
+ return AS2 (rol%L0,%b2,%0);
+ else
+ return AS2 (rol%L0,%2,%0);
+}")
+
+(define_insn "rotlhi3"
+ [(set (match_operand:HI 0 "general_operand" "=rm")
+ (rotate:HI (match_operand:HI 1 "general_operand" "0")
+ (match_operand:HI 2 "nonmemory_operand" "cI")))]
+ ""
+ "*
+{
+ if (REG_P (operands[2]))
+ return AS2 (rol%W0,%b2,%0);
+ else
+ return AS2 (rol%W0,%2,%0);
+}")
+
+(define_insn "rotlqi3"
+ [(set (match_operand:QI 0 "general_operand" "=qm")
+ (rotate:QI (match_operand:QI 1 "general_operand" "0")
+ (match_operand:QI 2 "nonmemory_operand" "cI")))]
+ ""
+ "*
+{
+ if (REG_P (operands[2]))
+ return AS2 (rol%B0,%b2,%0);
+ else
+ return AS2 (rol%B0,%2,%0);
+}")
+
+(define_insn "rotrsi3"
+ [(set (match_operand:SI 0 "general_operand" "=rm")
+ (rotatert:SI (match_operand:SI 1 "general_operand" "0")
+ (match_operand:SI 2 "nonmemory_operand" "cI")))]
+ ""
+ "*
+{
+ if (REG_P (operands[2]))
+ return AS2 (ror%L0,%b2,%0);
+ else
+ return AS2 (ror%L0,%2,%0);
+}")
+
+(define_insn "rotrhi3"
+ [(set (match_operand:HI 0 "general_operand" "=rm")
+ (rotatert:HI (match_operand:HI 1 "general_operand" "0")
+ (match_operand:HI 2 "nonmemory_operand" "cI")))]
+ ""
+ "*
+{
+ if (REG_P (operands[2]))
+ return AS2 (ror%W0,%b2,%0);
+ else
+ return AS2 (ror%W0,%2,%0);
+}")
+
+(define_insn "rotrqi3"
+ [(set (match_operand:QI 0 "general_operand" "=qm")
+ (rotatert:QI (match_operand:QI 1 "general_operand" "0")
+ (match_operand:QI 2 "nonmemory_operand" "cI")))]
+ ""
+ "*
+{
+ if (REG_P (operands[2]))
+ return AS2 (ror%B0,%b2,%0);
+ else
+ return AS2 (ror%B0,%2,%0);
+}")
+
+/*
+;; This usually looses. But try a define_expand to recognize a few case
+;; we can do efficiently, such as accessing the "high" QImode registers,
+;; %ah, %bh, %ch, %dh.
+(define_insn "insv"
+ [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+&r")
+ (match_operand:SI 1 "general_operand" "i")
+ (match_operand:SI 2 "general_operand" "i"))
+ (match_operand:SI 3 "general_operand" "ri"))]
+ ""
+ "*
+{
+ if (INTVAL (operands[1]) + INTVAL (operands[2]) > GET_MODE_BITSIZE (SImode))
+ abort ();
+ if (GET_CODE (operands[3]) == CONST_INT)
+ {
+ unsigned int mask = (1 << INTVAL (operands[1])) - 1;
+ operands[1] = GEN_INT (~(mask << INTVAL (operands[2])));
+ output_asm_insn (AS2 (and%L0,%1,%0), operands);
+ operands[3] = GEN_INT (INTVAL (operands[3]) << INTVAL (operands[2]));
+ output_asm_insn (AS2 (or%L0,%3,%0), operands);
+ }
+ else
+ {
+ operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]));
+ if (INTVAL (operands[2]))
+ output_asm_insn (AS2 (ror%L0,%2,%0), operands);
+ output_asm_insn (AS3 (shrd%L0,%1,%3,%0), operands);
+ operands[2] = GEN_INT (BITS_PER_WORD
+ - INTVAL (operands[1]) - INTVAL (operands[2]));
+ if (INTVAL (operands[2]))
+ output_asm_insn (AS2 (ror%L0,%2,%0), operands);
+ }
+ RET;
+}")
+*/
+/*
+;; ??? There are problems with the mode of operand[3]. The point of this
+;; is to represent an HImode move to a "high byte" register.
+
+(define_expand "insv"
+ [(set (zero_extract:SI (match_operand:SI 0 "general_operand" "")
+ (match_operand:SI 1 "immediate_operand" "")
+ (match_operand:SI 2 "immediate_operand" ""))
+ (match_operand:QI 3 "general_operand" "ri"))]
+ ""
+ "
+{
+ if (GET_CODE (operands[1]) != CONST_INT
+ || GET_CODE (operands[2]) != CONST_INT)
+ FAIL;
+
+ if (! (INTVAL (operands[1]) == 8
+ && (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 0))
+ && ! INTVAL (operands[1]) == 1)
+ FAIL;
+}")
+
+;; ??? Are these constraints right?
+(define_insn ""
+ [(set (zero_extract:SI (match_operand:SI 0 "general_operand" "+&qo")
+ (const_int 8)
+ (const_int 8))
+ (match_operand:QI 1 "general_operand" "qn"))]
+ ""
+ "*
+{
+ if (REG_P (operands[0]))
+ return AS2 (mov%B0,%1,%h0);
+
+ operands[0] = adj_offsettable_operand (operands[0], 1);
+ return AS2 (mov%B0,%1,%0);
+}")
+*/
+
+;; On i386, the register count for a bit operation is *not* truncated,
+;; so SHIFT_COUNT_TRUNCATED must not be defined.
+
+;; On i486, the shift & or/and code is faster than bts or btr. If
+;; operands[0] is a MEM, the bt[sr] is half as fast as the normal code.
+
+;; On i386, bts is a little faster if operands[0] is a reg, and a
+;; little slower if operands[0] is a MEM, than the shift & or/and code.
+;; Use bts & btr, since they reload better.
+
+;; General bit set and clear.
+(define_insn ""
+ [(set (zero_extract:SI (match_operand:SI 0 "general_operand" "+rm")
+ (const_int 1)
+ (match_operand:SI 2 "general_operand" "r"))
+ (match_operand:SI 3 "const_int_operand" "n"))]
+ "! TARGET_486 && GET_CODE (operands[2]) != CONST_INT"
+ "*
+{
+ CC_STATUS_INIT;
+
+ if (INTVAL (operands[3]) == 1)
+ return AS2 (bts%L0,%2,%0);
+ else
+ return AS2 (btr%L0,%2,%0);
+}")
+
+;; Bit complement. See comments on previous pattern.
+;; ??? Is this really worthwhile?
+(define_insn ""
+ [(set (match_operand:SI 0 "general_operand" "=rm")
+ (xor:SI (ashift:SI (const_int 1)
+ (match_operand:SI 1 "general_operand" "r"))
+ (match_operand:SI 2 "general_operand" "0")))]
+ "! TARGET_486 && GET_CODE (operands[1]) != CONST_INT"
+ "*
+{
+ CC_STATUS_INIT;
+
+ return AS2 (btc%L0,%1,%0);
+}")
+
+(define_insn ""
+ [(set (match_operand:SI 0 "general_operand" "=rm")
+ (xor:SI (match_operand:SI 1 "general_operand" "0")
+ (ashift:SI (const_int 1)
+ (match_operand:SI 2 "general_operand" "r"))))]
+ "! TARGET_486 && GET_CODE (operands[2]) != CONST_INT"
+ "*
+{
+ CC_STATUS_INIT;
+
+ return AS2 (btc%L0,%2,%0);
+}")
+
+;; Recognizers for bit-test instructions.
+
+;; The bt opcode allows a MEM in operands[0]. But on both i386 and
+;; i486, it is faster to copy a MEM to REG and then use bt, than to use
+;; bt on the MEM directly.
+
+;; ??? The first argument of a zero_extract must not be reloaded, so
+;; don't allow a MEM in the operand predicate without allowing it in the
+;; constraint.
+
+(define_insn ""
+ [(set (cc0) (zero_extract (match_operand:SI 0 "register_operand" "r")
+ (const_int 1)
+ (match_operand:SI 1 "general_operand" "r")))]
+ "GET_CODE (operands[1]) != CONST_INT"
+ "*
+{
+ cc_status.flags |= CC_Z_IN_NOT_C;
+ return AS2 (bt%L0,%1,%0);
+}")
+
+(define_insn ""
+ [(set (cc0) (zero_extract (match_operand:SI 0 "register_operand" "r")
+ (match_operand:SI 1 "const_int_operand" "n")
+ (match_operand:SI 2 "const_int_operand" "n")))]
+ ""
+ "*
+{
+ unsigned int mask;
+
+ mask = ((1 << INTVAL (operands[1])) - 1) << INTVAL (operands[2]);
+ operands[1] = GEN_INT (mask);
+
+ if (QI_REG_P (operands[0]))
+ {
+ if ((mask & ~0xff) == 0)
+ {
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ return AS2 (test%B0,%1,%b0);
+ }
+
+ if ((mask & ~0xff00) == 0)
+ {
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ operands[1] = GEN_INT (mask >> 8);
+ return AS2 (test%B0,%1,%h0);
+ }
+ }
+
+ return AS2 (test%L0,%1,%0);
+}")
+
+;; ??? All bets are off if operand 0 is a volatile MEM reference.
+;; The CPU may access unspecified bytes around the actual target byte.
+
+(define_insn ""
+ [(set (cc0) (zero_extract (match_operand:QI 0 "general_operand" "rm")
+ (match_operand:SI 1 "const_int_operand" "n")
+ (match_operand:SI 2 "const_int_operand" "n")))]
+ "GET_CODE (operands[0]) != MEM || ! MEM_VOLATILE_P (operands[0])"
+ "*
+{
+ unsigned int mask;
+
+ mask = ((1 << INTVAL (operands[1])) - 1) << INTVAL (operands[2]);
+ operands[1] = GEN_INT (mask);
+
+ if (! REG_P (operands[0]) || QI_REG_P (operands[0]))
+ {
+ if ((mask & ~0xff) == 0)
+ {
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ return AS2 (test%B0,%1,%b0);
+ }
+
+ if ((mask & ~0xff00) == 0)
+ {
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ operands[1] = GEN_INT (mask >> 8);
+
+ if (QI_REG_P (operands[0]))
+ return AS2 (test%B0,%1,%h0);
+ else
+ {
+ operands[0] = adj_offsettable_operand (operands[0], 1);
+ return AS2 (test%B0,%1,%b0);
+ }
+ }
+
+ if (GET_CODE (operands[0]) == MEM && (mask & ~0xff0000) == 0)
+ {
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ operands[1] = GEN_INT (mask >> 16);
+ operands[0] = adj_offsettable_operand (operands[0], 2);
+ return AS2 (test%B0,%1,%b0);
+ }
+
+ if (GET_CODE (operands[0]) == MEM && (mask & ~0xff000000) == 0)
+ {
+ cc_status.flags |= CC_NOT_NEGATIVE;
+ operands[1] = GEN_INT (mask >> 24);
+ operands[0] = adj_offsettable_operand (operands[0], 3);
+ return AS2 (test%B0,%1,%b0);
+ }
+ }
+
+ if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM)
+ return AS2 (test%L0,%1,%0);
+
+ return AS2 (test%L1,%0,%1);
+}")
+
+;; Store-flag instructions.
+
+;; For all sCOND expanders, also expand the compare or test insn that
+;; generates cc0. Generate an equality comparison if `seq' or `sne'.
+
+;; The 386 sCOND opcodes can write to memory. But a gcc sCOND insn may
+;; not have any input reloads. A MEM write might need an input reload
+;; for the address of the MEM. So don't allow MEM as the SET_DEST.
+
+(define_expand "seq"
+ [(match_dup 1)
+ (set (match_operand:QI 0 "register_operand" "")
+ (eq:QI (cc0) (const_int 0)))]
+ ""
+ "
+{
+ if (TARGET_IEEE_FP
+ && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT)
+ operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1);
+ else
+ operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+}")
+
+(define_insn ""
+ [(set (match_operand:QI 0 "register_operand" "=q")
+ (eq:QI (cc0) (const_int 0)))]
+ ""
+ "*
+{
+ if (cc_prev_status.flags & CC_Z_IN_NOT_C)
+ return AS1 (setnb,%0);
+ else
+ return AS1 (sete,%0);
+}")
+
+(define_expand "sne"
+ [(match_dup 1)
+ (set (match_operand:QI 0 "register_operand" "")
+ (ne:QI (cc0) (const_int 0)))]
+ ""
+ "
+{
+ if (TARGET_IEEE_FP
+ && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT)
+ operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1);
+ else
+ operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+}")
+
+(define_insn ""
+ [(set (match_operand:QI 0 "register_operand" "=q")
+ (ne:QI (cc0) (const_int 0)))]
+ ""
+ "*
+{
+ if (cc_prev_status.flags & CC_Z_IN_NOT_C)
+ return AS1 (setb,%0);
+ else
+ return AS1 (setne,%0);
+}
+")
+
+(define_expand "sgt"
+ [(match_dup 1)
+ (set (match_operand:QI 0 "register_operand" "")
+ (gt:QI (cc0) (const_int 0)))]
+ ""
+ "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+
+(define_insn ""
+ [(set (match_operand:QI 0 "register_operand" "=q")
+ (gt:QI (cc0) (const_int 0)))]
+ ""
+ "*
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (sete,%0);
+
+ OUTPUT_JUMP (\"setg %0\", \"seta %0\", NULL_PTR);
+}")
+
+(define_expand "sgtu"
+ [(match_dup 1)
+ (set (match_operand:QI 0 "register_operand" "")
+ (gtu:QI (cc0) (const_int 0)))]
+ ""
+ "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+
+(define_insn ""
+ [(set (match_operand:QI 0 "register_operand" "=q")
+ (gtu:QI (cc0) (const_int 0)))]
+ ""
+ "* return \"seta %0\"; ")
+
+(define_expand "slt"
+ [(match_dup 1)
+ (set (match_operand:QI 0 "register_operand" "")
+ (lt:QI (cc0) (const_int 0)))]
+ ""
+ "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+
+(define_insn ""
+ [(set (match_operand:QI 0 "register_operand" "=q")
+ (lt:QI (cc0) (const_int 0)))]
+ ""
+ "*
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (sete,%0);
+
+ OUTPUT_JUMP (\"setl %0\", \"setb %0\", \"sets %0\");
+}")
+
+(define_expand "sltu"
+ [(match_dup 1)
+ (set (match_operand:QI 0 "register_operand" "")
+ (ltu:QI (cc0) (const_int 0)))]
+ ""
+ "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+
+(define_insn ""
+ [(set (match_operand:QI 0 "register_operand" "=q")
+ (ltu:QI (cc0) (const_int 0)))]
+ ""
+ "* return \"setb %0\"; ")
+
+(define_expand "sge"
+ [(match_dup 1)
+ (set (match_operand:QI 0 "register_operand" "")
+ (ge:QI (cc0) (const_int 0)))]
+ ""
+ "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+
+(define_insn ""
+ [(set (match_operand:QI 0 "register_operand" "=q")
+ (ge:QI (cc0) (const_int 0)))]
+ ""
+ "*
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (sete,%0);
+
+ OUTPUT_JUMP (\"setge %0\", \"setae %0\", \"setns %0\");
+}")
+
+(define_expand "sgeu"
+ [(match_dup 1)
+ (set (match_operand:QI 0 "register_operand" "")
+ (geu:QI (cc0) (const_int 0)))]
+ ""
+ "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+
+(define_insn ""
+ [(set (match_operand:QI 0 "register_operand" "=q")
+ (geu:QI (cc0) (const_int 0)))]
+ ""
+ "* return \"setae %0\"; ")
+
+(define_expand "sle"
+ [(match_dup 1)
+ (set (match_operand:QI 0 "register_operand" "")
+ (le:QI (cc0) (const_int 0)))]
+ ""
+ "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+
+(define_insn ""
+ [(set (match_operand:QI 0 "register_operand" "=q")
+ (le:QI (cc0) (const_int 0)))]
+ ""
+ "*
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (setb,%0);
+
+ OUTPUT_JUMP (\"setle %0\", \"setbe %0\", NULL_PTR);
+}")
+
+(define_expand "sleu"
+ [(match_dup 1)
+ (set (match_operand:QI 0 "register_operand" "")
+ (leu:QI (cc0) (const_int 0)))]
+ ""
+ "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+
+(define_insn ""
+ [(set (match_operand:QI 0 "register_operand" "=q")
+ (leu:QI (cc0) (const_int 0)))]
+ ""
+ "* return \"setbe %0\"; ")
+
+;; Basic conditional jump instructions.
+;; We ignore the overflow flag for signed branch instructions.
+
+;; For all bCOND expanders, also expand the compare or test insn that
+;; generates cc0. Generate an equality comparison if `beq' or `bne'.
+
+(define_expand "beq"
+ [(match_dup 1)
+ (set (pc)
+ (if_then_else (eq (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ if (TARGET_IEEE_FP
+ && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT)
+ operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1);
+ else
+ operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+}")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (eq (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "*
+{
+ if (cc_prev_status.flags & CC_Z_IN_NOT_C)
+ return \"jnc %l0\";
+ else
+ return \"je %l0\";
+}")
+
+(define_expand "bne"
+ [(match_dup 1)
+ (set (pc)
+ (if_then_else (ne (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ if (TARGET_IEEE_FP
+ && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT)
+ operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1);
+ else
+ operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
+}")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (ne (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "*
+{
+ if (cc_prev_status.flags & CC_Z_IN_NOT_C)
+ return \"jc %l0\";
+ else
+ return \"jne %l0\";
+}")
+
+(define_expand "bgt"
+ [(match_dup 1)
+ (set (pc)
+ (if_then_else (gt (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (gt (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "*
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (je,%l0);
+
+ OUTPUT_JUMP (\"jg %l0\", \"ja %l0\", NULL_PTR);
+}")
+
+(define_expand "bgtu"
+ [(match_dup 1)
+ (set (pc)
+ (if_then_else (gtu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (gtu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "ja %l0")
+
+(define_expand "blt"
+ [(match_dup 1)
+ (set (pc)
+ (if_then_else (lt (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (lt (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "*
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (je,%l0);
+
+ OUTPUT_JUMP (\"jl %l0\", \"jb %l0\", \"js %l0\");
+}")
+
+(define_expand "bltu"
+ [(match_dup 1)
+ (set (pc)
+ (if_then_else (ltu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (ltu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "jb %l0")
+
+(define_expand "bge"
+ [(match_dup 1)
+ (set (pc)
+ (if_then_else (ge (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (ge (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "*
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (je,%l0);
+
+ OUTPUT_JUMP (\"jge %l0\", \"jae %l0\", \"jns %l0\");
+}")
+
+(define_expand "bgeu"
+ [(match_dup 1)
+ (set (pc)
+ (if_then_else (geu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (geu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "jae %l0")
+
+(define_expand "ble"
+ [(match_dup 1)
+ (set (pc)
+ (if_then_else (le (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (le (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "*
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (jb,%l0);
+
+ OUTPUT_JUMP (\"jle %l0\", \"jbe %l0\", NULL_PTR);
+}")
+
+(define_expand "bleu"
+ [(match_dup 1)
+ (set (pc)
+ (if_then_else (leu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (leu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "jbe %l0")
+
+;; Negated conditional jump instructions.
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (eq (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "*
+{
+ if (cc_prev_status.flags & CC_Z_IN_NOT_C)
+ return \"jc %l0\";
+ else
+ return \"jne %l0\";
+}")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (ne (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "*
+{
+ if (cc_prev_status.flags & CC_Z_IN_NOT_C)
+ return \"jnc %l0\";
+ else
+ return \"je %l0\";
+}")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (gt (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "*
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (jne,%l0);
+
+ OUTPUT_JUMP (\"jle %l0\", \"jbe %l0\", NULL_PTR);
+}")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (gtu (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "jbe %l0")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (lt (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "*
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (jne,%l0);
+
+ OUTPUT_JUMP (\"jge %l0\", \"jae %l0\", \"jns %l0\");
+}")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (ltu (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "jae %l0")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (ge (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "*
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (jne,%l0);
+
+ OUTPUT_JUMP (\"jl %l0\", \"jb %l0\", \"js %l0\");
+}")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (geu (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "jb %l0")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (le (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "*
+{
+ if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
+ return AS1 (jae,%l0);
+
+ OUTPUT_JUMP (\"jg %l0\", \"ja %l0\", NULL_PTR);
+}")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (leu (cc0)
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ "ja %l0")
+
+;; Unconditional and other jump instructions
+
+(define_insn "jump"
+ [(set (pc)
+ (label_ref (match_operand 0 "" "")))]
+ ""
+ "jmp %l0")
+
+(define_insn "indirect_jump"
+ [(set (pc) (match_operand:SI 0 "general_operand" "rm"))]
+ ""
+ "*
+{
+ CC_STATUS_INIT;
+
+ return AS1 (jmp,%*%0);
+}")
+
+;; Implement switch statements when generating PIC code. Switches are
+;; implemented by `tablejump' when not using -fpic.
+
+;; Emit code here to do the range checking and make the index zero based.
+
+(define_expand "casesi"
+ [(set (match_dup 5)
+ (minus:SI (match_operand:SI 0 "general_operand" "")
+ (match_operand:SI 1 "general_operand" "")))
+ (set (cc0)
+ (compare:CC (match_dup 5)
+ (match_operand:SI 2 "general_operand" "")))
+ (set (pc)
+ (if_then_else (gtu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 4 "" ""))
+ (pc)))
+ (parallel
+ [(set (pc)
+ (minus:SI (reg:SI 3)
+ (mem:SI (plus:SI (mult:SI (match_dup 5)
+ (const_int 4))
+ (label_ref (match_operand 3 "" ""))))))
+ (clobber (match_scratch:SI 6 ""))])]
+ "flag_pic"
+ "
+{
+ operands[5] = gen_reg_rtx (SImode);
+ current_function_uses_pic_offset_table = 1;
+}")
+
+;; Implement a casesi insn.
+
+;; Each entry in the "addr_diff_vec" looks like this as the result of the
+;; two rules below:
+;;
+;; .long _GLOBAL_OFFSET_TABLE_+[.-.L2]
+;;
+;; 1. An expression involving an external reference may only use the
+;; addition operator, and only with an assembly-time constant.
+;; The example above satisfies this because ".-.L2" is a constant.
+;;
+;; 2. The symbol _GLOBAL_OFFSET_TABLE_ is magic, and at link time is
+;; given the value of "GOT - .", where GOT is the actual address of
+;; the Global Offset Table. Therefore, the .long above actually
+;; stores the value "( GOT - . ) + [ . - .L2 ]", or "GOT - .L2". The
+;; expression "GOT - .L2" by itself would generate an error from as(1).
+;;
+;; The pattern below emits code that looks like this:
+;;
+;; movl %ebx,reg
+;; subl TABLE@GOTOFF(%ebx,index,4),reg
+;; jmp reg
+;;
+;; The addr_diff_vec contents may be directly referenced with @GOTOFF, since
+;; the addr_diff_vec is known to be part of this module.
+;;
+;; The subl above calculates "GOT - (( GOT - . ) + [ . - .L2 ])", which
+;; evaluates to just ".L2".
+
+(define_insn ""
+ [(set (pc)
+ (minus:SI (reg:SI 3)
+ (mem:SI (plus:SI
+ (mult:SI (match_operand:SI 0 "register_operand" "r")
+ (const_int 4))
+ (label_ref (match_operand 1 "" ""))))))
+ (clobber (match_scratch:SI 2 "=&r"))]
+ ""
+ "*
+{
+ rtx xops[4];
+
+ xops[0] = operands[0];
+ xops[1] = operands[1];
+ xops[2] = operands[2];
+ xops[3] = pic_offset_table_rtx;
+
+ output_asm_insn (AS2 (mov%L2,%3,%2), xops);
+ output_asm_insn (\"sub%L2 %l1@GOTOFF(%3,%0,4),%2\", xops);
+ output_asm_insn (AS1 (jmp,%*%2), xops);
+ ASM_OUTPUT_ALIGN_CODE (asm_out_file);
+ RET;
+}")
+
+(define_insn "tablejump"
+ [(set (pc) (match_operand:SI 0 "general_operand" "rm"))
+ (use (label_ref (match_operand 1 "" "")))]
+ ""
+ "*
+{
+ CC_STATUS_INIT;
+
+ return AS1 (jmp,%*%0);
+}")
+
+;; Call insns.
+
+;; If generating PIC code, the predicate indirect_operand will fail
+;; for operands[0] containing symbolic references on all of the named
+;; call* patterns. Each named pattern is followed by an unnamed pattern
+;; that matches any call to a symbolic CONST (ie, a symbol_ref). The
+;; unnamed patterns are only used while generating PIC code, because
+;; otherwise the named patterns match.
+
+;; Call subroutine returning no value.
+
+(define_expand "call_pop"
+ [(parallel [(call (match_operand:QI 0 "indirect_operand" "")
+ (match_operand:SI 1 "general_operand" ""))
+ (set (reg:SI 7)
+ (plus:SI (reg:SI 7)
+ (match_operand:SI 3 "immediate_operand" "")))])]
+ ""
+ "
+{
+ rtx addr;
+
+ if (flag_pic)
+ current_function_uses_pic_offset_table = 1;
+
+ /* With half-pic, force the address into a register. */
+ addr = XEXP (operands[0], 0);
+ if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr))
+ XEXP (operands[0], 0) = force_reg (Pmode, addr);
+
+ if (! expander_call_insn_operand (operands[0], QImode))
+ operands[0]
+ = change_address (operands[0], VOIDmode,
+ copy_to_mode_reg (Pmode, XEXP (operands[0], 0)));
+}")
+
+(define_insn ""
+ [(call (match_operand:QI 0 "call_insn_operand" "m")
+ (match_operand:SI 1 "general_operand" "g"))
+ (set (reg:SI 7) (plus:SI (reg:SI 7)
+ (match_operand:SI 3 "immediate_operand" "i")))]
+ ""
+ "*
+{
+ if (GET_CODE (operands[0]) == MEM
+ && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
+ {
+ operands[0] = XEXP (operands[0], 0);
+ return AS1 (call,%*%0);
+ }
+ else
+ return AS1 (call,%P0);
+}")
+
+(define_insn ""
+ [(call (mem:QI (match_operand:SI 0 "symbolic_operand" ""))
+ (match_operand:SI 1 "general_operand" "g"))
+ (set (reg:SI 7) (plus:SI (reg:SI 7)
+ (match_operand:SI 3 "immediate_operand" "i")))]
+ "!HALF_PIC_P ()"
+ "call %P0")
+
+(define_expand "call"
+ [(call (match_operand:QI 0 "indirect_operand" "")
+ (match_operand:SI 1 "general_operand" ""))]
+ ;; Operand 1 not used on the i386.
+ ""
+ "
+{
+ rtx addr;
+
+ if (flag_pic)
+ current_function_uses_pic_offset_table = 1;
+
+ /* With half-pic, force the address into a register. */
+ addr = XEXP (operands[0], 0);
+ if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr))
+ XEXP (operands[0], 0) = force_reg (Pmode, addr);
+
+ if (! expander_call_insn_operand (operands[0], QImode))
+ operands[0]
+ = change_address (operands[0], VOIDmode,
+ copy_to_mode_reg (Pmode, XEXP (operands[0], 0)));
+}")
+
+(define_insn ""
+ [(call (match_operand:QI 0 "call_insn_operand" "m")
+ (match_operand:SI 1 "general_operand" "g"))]
+ ;; Operand 1 not used on the i386.
+ ""
+ "*
+{
+ if (GET_CODE (operands[0]) == MEM
+ && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
+ {
+ operands[0] = XEXP (operands[0], 0);
+ return AS1 (call,%*%0);
+ }
+ else
+ return AS1 (call,%P0);
+}")
+
+(define_insn ""
+ [(call (mem:QI (match_operand:SI 0 "symbolic_operand" ""))
+ (match_operand:SI 1 "general_operand" "g"))]
+ ;; Operand 1 not used on the i386.
+ "!HALF_PIC_P ()"
+ "call %P0")
+
+;; Call subroutine, returning value in operand 0
+;; (which must be a hard register).
+
+(define_expand "call_value_pop"
+ [(parallel [(set (match_operand 0 "" "")
+ (call (match_operand:QI 1 "indirect_operand" "")
+ (match_operand:SI 2 "general_operand" "")))
+ (set (reg:SI 7)
+ (plus:SI (reg:SI 7)
+ (match_operand:SI 4 "immediate_operand" "")))])]
+ ""
+ "
+{
+ rtx addr;
+
+ if (flag_pic)
+ current_function_uses_pic_offset_table = 1;
+
+ /* With half-pic, force the address into a register. */
+ addr = XEXP (operands[1], 0);
+ if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr))
+ XEXP (operands[1], 0) = force_reg (Pmode, addr);
+
+ if (! expander_call_insn_operand (operands[1], QImode))
+ operands[1]
+ = change_address (operands[1], VOIDmode,
+ copy_to_mode_reg (Pmode, XEXP (operands[1], 0)));
+}")
+
+(define_insn ""
+ [(set (match_operand 0 "" "=rf")
+ (call (match_operand:QI 1 "call_insn_operand" "m")
+ (match_operand:SI 2 "general_operand" "g")))
+ (set (reg:SI 7) (plus:SI (reg:SI 7)
+ (match_operand:SI 4 "immediate_operand" "i")))]
+ ""
+ "*
+{
+ if (GET_CODE (operands[1]) == MEM
+ && ! CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
+ {
+ operands[1] = XEXP (operands[1], 0);
+ output_asm_insn (AS1 (call,%*%1), operands);
+ }
+ else
+ output_asm_insn (AS1 (call,%P1), operands);
+
+ RET;
+}")
+
+(define_insn ""
+ [(set (match_operand 0 "" "=rf")
+ (call (mem:QI (match_operand:SI 1 "symbolic_operand" ""))
+ (match_operand:SI 2 "general_operand" "g")))
+ (set (reg:SI 7) (plus:SI (reg:SI 7)
+ (match_operand:SI 4 "immediate_operand" "i")))]
+ "!HALF_PIC_P ()"
+ "call %P1")
+
+(define_expand "call_value"
+ [(set (match_operand 0 "" "")
+ (call (match_operand:QI 1 "indirect_operand" "")
+ (match_operand:SI 2 "general_operand" "")))]
+ ;; Operand 2 not used on the i386.
+ ""
+ "
+{
+ rtx addr;
+
+ if (flag_pic)
+ current_function_uses_pic_offset_table = 1;
+
+ /* With half-pic, force the address into a register. */
+ addr = XEXP (operands[1], 0);
+ if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr))
+ XEXP (operands[1], 0) = force_reg (Pmode, addr);
+
+ if (! expander_call_insn_operand (operands[1], QImode))
+ operands[1]
+ = change_address (operands[1], VOIDmode,
+ copy_to_mode_reg (Pmode, XEXP (operands[1], 0)));
+}")
+
+(define_insn ""
+ [(set (match_operand 0 "" "=rf")
+ (call (match_operand:QI 1 "call_insn_operand" "m")
+ (match_operand:SI 2 "general_operand" "g")))]
+ ;; Operand 2 not used on the i386.
+ ""
+ "*
+{
+ if (GET_CODE (operands[1]) == MEM
+ && ! CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
+ {
+ operands[1] = XEXP (operands[1], 0);
+ output_asm_insn (AS1 (call,%*%1), operands);
+ }
+ else
+ output_asm_insn (AS1 (call,%P1), operands);
+
+ RET;
+}")
+
+(define_insn ""
+ [(set (match_operand 0 "" "=rf")
+ (call (mem:QI (match_operand:SI 1 "symbolic_operand" ""))
+ (match_operand:SI 2 "general_operand" "g")))]
+ ;; Operand 2 not used on the i386.
+ "!HALF_PIC_P ()"
+ "call %P1")
+
+(define_expand "untyped_call"
+ [(parallel [(call (match_operand:QI 0 "indirect_operand" "")
+ (const_int 0))
+ (match_operand:BLK 1 "memory_operand" "")
+ (match_operand 2 "" "")])]
+ ""
+ "
+{
+ rtx addr;
+
+ if (flag_pic)
+ current_function_uses_pic_offset_table = 1;
+
+ /* With half-pic, force the address into a register. */
+ addr = XEXP (operands[0], 0);
+ if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr))
+ XEXP (operands[0], 0) = force_reg (Pmode, addr);
+
+ operands[1] = change_address (operands[1], DImode, XEXP (operands[1], 0));
+ if (! expander_call_insn_operand (operands[1], QImode))
+ operands[1]
+ = change_address (operands[1], VOIDmode,
+ copy_to_mode_reg (Pmode, XEXP (operands[1], 0)));
+}")
+
+(define_insn ""
+ [(call (match_operand:QI 0 "call_insn_operand" "m")
+ (const_int 0))
+ (match_operand:DI 1 "memory_operand" "o")
+ (match_operand 2 "" "")]
+ ""
+ "*
+{
+ rtx addr = operands[1];
+
+ if (GET_CODE (operands[0]) == MEM
+ && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
+ {
+ operands[0] = XEXP (operands[0], 0);
+ output_asm_insn (AS1 (call,%*%0), operands);
+ }
+ else
+ output_asm_insn (AS1 (call,%P0), operands);
+
+ operands[2] = gen_rtx (REG, SImode, 0);
+ output_asm_insn (AS2 (mov%L2,%2,%1), operands);
+
+ operands[2] = gen_rtx (REG, SImode, 1);
+ operands[1] = adj_offsettable_operand (addr, 4);
+ output_asm_insn (AS2 (mov%L2,%2,%1), operands);
+
+ operands[1] = adj_offsettable_operand (addr, 8);
+ return AS1 (fnsave,%1);
+}")
+
+(define_insn ""
+ [(call (mem:QI (match_operand:SI 0 "symbolic_operand" ""))
+ (const_int 0))
+ (match_operand:DI 1 "memory_operand" "o")
+ (match_operand 2 "" "")]
+ "!HALF_PIC_P ()"
+ "*
+{
+ rtx addr = operands[1];
+
+ output_asm_insn (AS1 (call,%P0), operands);
+
+ operands[2] = gen_rtx (REG, SImode, 0);
+ output_asm_insn (AS2 (mov%L2,%2,%1), operands);
+
+ operands[2] = gen_rtx (REG, SImode, 1);
+ operands[1] = adj_offsettable_operand (addr, 4);
+ output_asm_insn (AS2 (mov%L2,%2,%1), operands);
+
+ operands[1] = adj_offsettable_operand (addr, 8);
+ return AS1 (fnsave,%1);
+}")
+
+;; We use fnsave and frstor to save and restore the floating point result.
+;; These are expensive instructions and require a large space to save the
+;; FPU state. An more complicated alternative is to use fnstenv to store
+;; the FPU environment and test whether the stack top is valid. Store the
+;; result of the test, and if it is valid, pop and save the value. The
+;; untyped_return would check the test and optionally push the saved value.
+
+(define_expand "untyped_return"
+ [(match_operand:BLK 0 "memory_operand" "")
+ (match_operand 1 "" "")]
+ ""
+ "
+{
+ rtx valreg1 = gen_rtx (REG, SImode, 0);
+ rtx valreg2 = gen_rtx (REG, SImode, 1);
+ rtx result = operands[0];
+
+ /* Restore the FPU state. */
+ emit_insn (gen_update_return (change_address (result, SImode,
+ plus_constant (XEXP (result, 0),
+ 8))));
+
+ /* Reload the function value registers. */
+ emit_move_insn (valreg1, change_address (result, SImode, XEXP (result, 0)));
+ emit_move_insn (valreg2,
+ change_address (result, SImode,
+ plus_constant (XEXP (result, 0), 4)));
+
+ /* Put USE insns before the return. */
+ emit_insn (gen_rtx (USE, VOIDmode, valreg1));
+ emit_insn (gen_rtx (USE, VOIDmode, valreg2));
+
+ /* Construct the return. */
+ expand_null_return ();
+
+ DONE;
+}")
+
+(define_insn "update_return"
+ [(unspec:SI [(match_operand:SI 0 "memory_operand" "m")] 0)]
+ ""
+ "frstor %0")
+
+;; Insn emitted into the body of a function to return from a function.
+;; This is only done if the function's epilogue is known to be simple.
+;; See comments for simple_386_epilogue in i386.c.
+
+(define_insn "return"
+ [(return)]
+ "simple_386_epilogue ()"
+ "*
+{
+ function_epilogue (asm_out_file, get_frame_size ());
+ RET;
+}")
+
+(define_insn "nop"
+ [(const_int 0)]
+ ""
+ "nop")
+
+(define_expand "movstrsi"
+ [(parallel [(set (match_operand:BLK 0 "memory_operand" "")
+ (match_operand:BLK 1 "memory_operand" ""))
+ (use (match_operand:SI 2 "const_int_operand" ""))
+ (use (match_operand:SI 3 "const_int_operand" ""))
+ (clobber (match_scratch:SI 4 ""))
+ (clobber (match_dup 5))
+ (clobber (match_dup 6))])]
+ ""
+ "
+{
+ rtx addr0, addr1;
+
+ if (GET_CODE (operands[2]) != CONST_INT)
+ FAIL;
+
+ addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
+ addr1 = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
+
+ operands[5] = addr0;
+ operands[6] = addr1;
+
+ operands[0] = gen_rtx (MEM, BLKmode, addr0);
+ operands[1] = gen_rtx (MEM, BLKmode, addr1);
+}")
+
+;; It might seem that operands 0 & 1 could use predicate register_operand.
+;; But strength reduction might offset the MEM expression. So we let
+;; reload put the address into %edi & %esi.
+
+(define_insn ""
+ [(set (mem:BLK (match_operand:SI 0 "address_operand" "D"))
+ (mem:BLK (match_operand:SI 1 "address_operand" "S")))
+ (use (match_operand:SI 2 "const_int_operand" "n"))
+ (use (match_operand:SI 3 "immediate_operand" "i"))
+ (clobber (match_scratch:SI 4 "=&c"))
+ (clobber (match_dup 0))
+ (clobber (match_dup 1))]
+ ""
+ "*
+{
+ rtx xops[2];
+
+ output_asm_insn (\"cld\", operands);
+ if (GET_CODE (operands[2]) == CONST_INT)
+ {
+ if (INTVAL (operands[2]) & ~0x03)
+ {
+ xops[0] = GEN_INT ((INTVAL (operands[2]) >> 2) & 0x3fffffff);
+ xops[1] = operands[4];
+
+ output_asm_insn (AS2 (mov%L1,%0,%1), xops);
+#ifdef INTEL_SYNTAX
+ output_asm_insn (\"rep movsd\", xops);
+#else
+ output_asm_insn (\"rep\;movsl\", xops);
+#endif
+ }
+ if (INTVAL (operands[2]) & 0x02)
+ output_asm_insn (\"movsw\", operands);
+ if (INTVAL (operands[2]) & 0x01)
+ output_asm_insn (\"movsb\", operands);
+ }
+ else
+ abort ();
+ RET;
+}")
+
+(define_expand "cmpstrsi"
+ [(parallel [(set (match_operand:SI 0 "general_operand" "")
+ (compare:SI (match_operand:BLK 1 "general_operand" "")
+ (match_operand:BLK 2 "general_operand" "")))
+ (use (match_operand:SI 3 "general_operand" ""))
+ (use (match_operand:SI 4 "immediate_operand" ""))
+ (clobber (match_dup 5))
+ (clobber (match_dup 6))
+ (clobber (match_dup 3))])]
+ ""
+ "
+{
+ rtx addr1, addr2;
+
+ addr1 = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
+ addr2 = copy_to_mode_reg (Pmode, XEXP (operands[2], 0));
+ operands[3] = copy_to_mode_reg (SImode, operands[3]);
+
+ operands[5] = addr1;
+ operands[6] = addr2;
+
+ operands[1] = gen_rtx (MEM, BLKmode, addr1);
+ operands[2] = gen_rtx (MEM, BLKmode, addr2);
+
+}")
+
+;; memcmp recognizers. The `cmpsb' opcode does nothing if the count is
+;; zero. Emit extra code to make sure that a zero-length compare is EQ.
+
+;; It might seem that operands 0 & 1 could use predicate register_operand.
+;; But strength reduction might offset the MEM expression. So we let
+;; reload put the address into %edi & %esi.
+
+;; ??? Most comparisons have a constant length, and it's therefore
+;; possible to know that the length is non-zero, and to avoid the extra
+;; code to handle zero-length compares.
+
+(define_insn ""
+ [(set (match_operand:SI 0 "general_operand" "=&r")
+ (compare:SI (mem:BLK (match_operand:SI 1 "address_operand" "S"))
+ (mem:BLK (match_operand:SI 2 "address_operand" "D"))))
+ (use (match_operand:SI 3 "register_operand" "c"))
+ (use (match_operand:SI 4 "immediate_operand" "i"))
+ (clobber (match_dup 1))
+ (clobber (match_dup 2))
+ (clobber (match_dup 3))]
+ ""
+ "*
+{
+ rtx xops[4], label;
+
+ label = gen_label_rtx ();
+
+ output_asm_insn (\"cld\", operands);
+ output_asm_insn (AS2 (xor%L0,%0,%0), operands);
+ output_asm_insn (\"repz\;cmps%B2\", operands);
+ output_asm_insn (\"je %l0\", &label);
+
+ xops[0] = operands[0];
+ xops[1] = gen_rtx (MEM, QImode,
+ gen_rtx (PLUS, SImode, operands[1], constm1_rtx));
+ xops[2] = gen_rtx (MEM, QImode,
+ gen_rtx (PLUS, SImode, operands[2], constm1_rtx));
+ xops[3] = operands[3];
+
+ output_asm_insn (AS2 (movz%B1%L0,%1,%0), xops);
+ output_asm_insn (AS2 (movz%B2%L3,%2,%3), xops);
+
+ output_asm_insn (AS2 (sub%L0,%3,%0), xops);
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", CODE_LABEL_NUMBER (label));
+ RET;
+}")
+
+(define_insn ""
+ [(set (cc0)
+ (compare:SI (mem:BLK (match_operand:SI 0 "address_operand" "S"))
+ (mem:BLK (match_operand:SI 1 "address_operand" "D"))))
+ (use (match_operand:SI 2 "register_operand" "c"))
+ (use (match_operand:SI 3 "immediate_operand" "i"))
+ (clobber (match_dup 0))
+ (clobber (match_dup 1))
+ (clobber (match_dup 2))]
+ ""
+ "*
+{
+ rtx xops[2];
+
+ cc_status.flags |= CC_NOT_SIGNED;
+
+ xops[0] = gen_rtx (REG, QImode, 0);
+ xops[1] = CONST0_RTX (QImode);
+
+ output_asm_insn (\"cld\", operands);
+ output_asm_insn (AS2 (test%B0,%1,%0), xops);
+ return \"repz\;cmps%B2\";
+}")
+
+(define_expand "ffssi2"
+ [(set (match_dup 2)
+ (plus:SI (ffs:SI (match_operand:SI 1 "general_operand" ""))
+ (const_int -1)))
+ (set (match_operand:SI 0 "general_operand" "")
+ (plus:SI (match_dup 2) (const_int 1)))]
+ ""
+ "operands[2] = gen_reg_rtx (SImode);")
+
+(define_insn ""
+ [(set (match_operand:SI 0 "general_operand" "=&r")
+ (plus:SI (ffs:SI (match_operand:SI 1 "general_operand" "rm"))
+ (const_int -1)))]
+ ""
+ "*
+{
+ rtx xops[3];
+ static int ffssi_label_number;
+ char buffer[30];
+
+ xops[0] = operands[0];
+ xops[1] = operands[1];
+ xops[2] = constm1_rtx;
+ /* Can there be a way to avoid the jump here? */
+ output_asm_insn (AS2 (bsf%L0,%1,%0), xops);
+#ifdef LOCAL_LABEL_PREFIX
+ sprintf (buffer, \"jnz %sLFFSSI%d\",
+ LOCAL_LABEL_PREFIX, ffssi_label_number);
+#else
+ sprintf (buffer, \"jnz %sLFFSSI%d\",
+ \"\", ffssi_label_number);
+#endif
+ output_asm_insn (buffer, xops);
+ output_asm_insn (AS2 (mov%L0,%2,%0), xops);
+#ifdef LOCAL_LABEL_PREFIX
+ sprintf (buffer, \"%sLFFSSI%d:\",
+ LOCAL_LABEL_PREFIX, ffssi_label_number);
+#else
+ sprintf (buffer, \"%sLFFSSI%d:\",
+ \"\", ffssi_label_number);
+#endif
+ output_asm_insn (buffer, xops);
+
+ ffssi_label_number++;
+ return \"\";
+}")
+
+(define_expand "ffshi2"
+ [(set (match_dup 2)
+ (plus:HI (ffs:HI (match_operand:HI 1 "general_operand" ""))
+ (const_int -1)))
+ (set (match_operand:HI 0 "general_operand" "")
+ (plus:HI (match_dup 2) (const_int 1)))]
+ ""
+ "operands[2] = gen_reg_rtx (HImode);")
+
+(define_insn ""
+ [(set (match_operand:HI 0 "general_operand" "=&r")
+ (plus:HI (ffs:HI (match_operand:SI 1 "general_operand" "rm"))
+ (const_int -1)))]
+ ""
+ "*
+{
+ rtx xops[3];
+ static int ffshi_label_number;
+ char buffer[30];
+
+ xops[0] = operands[0];
+ xops[1] = operands[1];
+ xops[2] = constm1_rtx;
+ output_asm_insn (AS2 (bsf%W0,%1,%0), xops);
+#ifdef LOCAL_LABEL_PREFIX
+ sprintf (buffer, \"jnz %sLFFSHI%d\",
+ LOCAL_LABEL_PREFIX, ffshi_label_number);
+#else
+ sprintf (buffer, \"jnz %sLFFSHI%d\",
+ \"\", ffshi_label_number);
+#endif
+ output_asm_insn (buffer, xops);
+ output_asm_insn (AS2 (mov%W0,%2,%0), xops);
+#ifdef LOCAL_LABEL_PREFIX
+ sprintf (buffer, \"%sLFFSHI%d:\",
+ LOCAL_LABEL_PREFIX, ffshi_label_number);
+#else
+ sprintf (buffer, \"%sLFFSHI%d:\",
+ \"\", ffshi_label_number);
+#endif
+ output_asm_insn (buffer, xops);
+
+ ffshi_label_number++;
+ return \"\";
+}")
+
+;; These patterns match the binary 387 instructions for addM3, subM3,
+;; mulM3 and divM3. There are three patterns for each of DFmode and
+;; SFmode. The first is the normal insn, the second the same insn but
+;; with one operand a conversion, and the third the same insn but with
+;; the other operand a conversion. The conversion may be SFmode or
+;; SImode if the target mode DFmode, but only SImode if the target mode
+;; is SFmode.
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f,f")
+ (match_operator:DF 3 "binary_387_op"
+ [(match_operand:DF 1 "nonimmediate_operand" "0,fm")
+ (match_operand:DF 2 "nonimmediate_operand" "fm,0")]))]
+ "TARGET_80387"
+ "* return (char *) output_387_binary_op (insn, operands);")
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (match_operator:DF 3 "binary_387_op"
+ [(float:DF (match_operand:SI 1 "general_operand" "rm"))
+ (match_operand:DF 2 "general_operand" "0")]))]
+ "TARGET_80387"
+ "* return (char *) output_387_binary_op (insn, operands);")
+
+(define_insn ""
+ [(set (match_operand:XF 0 "register_operand" "=f,f")
+ (match_operator:XF 3 "binary_387_op"
+ [(match_operand:XF 1 "nonimmediate_operand" "0,f")
+ (match_operand:XF 2 "nonimmediate_operand" "f,0")]))]
+ "TARGET_80387"
+ "* return (char *) output_387_binary_op (insn, operands);")
+
+(define_insn ""
+ [(set (match_operand:XF 0 "register_operand" "=f")
+ (match_operator:XF 3 "binary_387_op"
+ [(float:XF (match_operand:SI 1 "general_operand" "rm"))
+ (match_operand:XF 2 "general_operand" "0")]))]
+ "TARGET_80387"
+ "* return (char *) output_387_binary_op (insn, operands);")
+
+(define_insn ""
+ [(set (match_operand:XF 0 "register_operand" "=f,f")
+ (match_operator:XF 3 "binary_387_op"
+ [(float_extend:XF (match_operand:SF 1 "general_operand" "fm,0"))
+ (match_operand:XF 2 "general_operand" "0,f")]))]
+ "TARGET_80387"
+ "* return (char *) output_387_binary_op (insn, operands);")
+
+(define_insn ""
+ [(set (match_operand:XF 0 "register_operand" "=f")
+ (match_operator:XF 3 "binary_387_op"
+ [(match_operand:XF 1 "general_operand" "0")
+ (float:XF (match_operand:SI 2 "general_operand" "rm"))]))]
+ "TARGET_80387"
+ "* return (char *) output_387_binary_op (insn, operands);")
+
+(define_insn ""
+ [(set (match_operand:XF 0 "register_operand" "=f,f")
+ (match_operator:XF 3 "binary_387_op"
+ [(match_operand:XF 1 "general_operand" "0,f")
+ (float_extend:XF
+ (match_operand:SF 2 "general_operand" "fm,0"))]))]
+ "TARGET_80387"
+ "* return (char *) output_387_binary_op (insn, operands);")
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f,f")
+ (match_operator:DF 3 "binary_387_op"
+ [(float_extend:DF (match_operand:SF 1 "general_operand" "fm,0"))
+ (match_operand:DF 2 "general_operand" "0,f")]))]
+ "TARGET_80387"
+ "* return (char *) output_387_binary_op (insn, operands);")
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (match_operator:DF 3 "binary_387_op"
+ [(match_operand:DF 1 "general_operand" "0")
+ (float:DF (match_operand:SI 2 "general_operand" "rm"))]))]
+ "TARGET_80387"
+ "* return (char *) output_387_binary_op (insn, operands);")
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f,f")
+ (match_operator:DF 3 "binary_387_op"
+ [(match_operand:DF 1 "general_operand" "0,f")
+ (float_extend:DF
+ (match_operand:SF 2 "general_operand" "fm,0"))]))]
+ "TARGET_80387"
+ "* return (char *) output_387_binary_op (insn, operands);")
+
+(define_insn ""
+ [(set (match_operand:SF 0 "register_operand" "=f,f")
+ (match_operator:SF 3 "binary_387_op"
+ [(match_operand:SF 1 "nonimmediate_operand" "0,fm")
+ (match_operand:SF 2 "nonimmediate_operand" "fm,0")]))]
+ "TARGET_80387"
+ "* return (char *) output_387_binary_op (insn, operands);")
+
+(define_insn ""
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (match_operator:SF 3 "binary_387_op"
+ [(float:SF (match_operand:SI 1 "general_operand" "rm"))
+ (match_operand:SF 2 "general_operand" "0")]))]
+ "TARGET_80387"
+ "* return (char *) output_387_binary_op (insn, operands);")
+
+(define_insn ""
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (match_operator:SF 3 "binary_387_op"
+ [(match_operand:SF 1 "general_operand" "0")
+ (float:SF (match_operand:SI 2 "general_operand" "rm"))]))]
+ "TARGET_80387"
+ "* return (char *) output_387_binary_op (insn, operands);")
+
+(define_expand "strlensi"
+ [(parallel [(set (match_dup 4)
+ (unspec:SI [(mem:BLK (match_operand:BLK 1 "general_operand" ""))
+ (match_operand:QI 2 "register_operand" "")
+ (match_operand:SI 3 "immediate_operand" "")] 0))
+ (clobber (match_dup 1))])
+ (set (match_dup 5)
+ (not:SI (match_dup 4)))
+ (set (match_operand:SI 0 "register_operand" "")
+ (minus:SI (match_dup 5)
+ (const_int 1)))]
+ ""
+ "
+{
+ operands[1] = copy_to_mode_reg (SImode, XEXP (operands[1], 0));
+ operands[4] = gen_reg_rtx (SImode);
+ operands[5] = gen_reg_rtx (SImode);
+}")
+
+;; It might seem that operands 0 & 1 could use predicate register_operand.
+;; But strength reduction might offset the MEM expression. So we let
+;; reload put the address into %edi.
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=&c")
+ (unspec:SI [(mem:BLK (match_operand:SI 1 "address_operand" "D"))
+ (match_operand:QI 2 "register_operand" "a")
+ (match_operand:SI 3 "immediate_operand" "i")] 0))
+ (clobber (match_dup 1))]
+ ""
+ "*
+{
+ rtx xops[2];
+
+ xops[0] = operands[0];
+ xops[1] = constm1_rtx;
+ output_asm_insn (\"cld\", operands);
+ output_asm_insn (AS2 (mov%L0,%1,%0), xops);
+ return \"repnz\;scas%B2\";
+}")
diff --git a/gnu/usr.bin/cc/libgcc/Makefile b/gnu/usr.bin/cc/libgcc/Makefile
new file mode 100644
index 0000000..4b2c5a4
--- /dev/null
+++ b/gnu/usr.bin/cc/libgcc/Makefile
@@ -0,0 +1,46 @@
+#
+# $FreeBSD$
+#
+
+LIB= gcc
+INSTALL_PIC_ARCHIVE= yes
+SHLIB_MAJOR= 26
+SHLIB_MINOR= 0
+
+LIB1OBJS= _mulsi3.o _udivsi3.o _divsi3.o _umodsi3.o _modsi3.o _lshrsi3.o _lshlsi3.o _ashrsi3.o _ashlsi3.o _divdf3.o _muldf3.o _negdf2.o _adddf3.o _subdf3.o _fixdfsi.o _fixsfsi.o _floatsidf.o _floatsisf.o _truncdfsf2.o _extendsfdf2.o _addsf3.o _negsf2.o _subsf3.o _mulsf3.o _divsf3.o _eqdf2.o _nedf2.o _gtdf2.o _gedf2.o _ltdf2.o _ledf2.o _eqsf2.o _nesf2.o _gtsf2.o _gesf2.o _ltsf2.o _lesf2.o
+LIB2OBJS= _muldi3.o _divdi3.o _moddi3.o _udivdi3.o _umoddi3.o _negdi2.o _lshrdi3.o _lshldi3.o _ashldi3.o _ashrdi3.o _ffsdi2.o _udiv_w_sdiv.o _udivmoddi4.o _cmpdi2.o _ucmpdi2.o _floatdidf.o _floatdisf.o _fixunsdfsi.o _fixunssfsi.o _fixunsdfdi.o _fixdfdi.o _fixunssfdi.o _fixsfdi.o _fixxfdi.o _fixunsxfdi.o _floatdixf.o _fixunsxfsi.o _fixtfdi.o _fixunstfdi.o _floatditf.o __gcc_bcmp.o _varargs.o _eprintf.o _op_new.o _op_vnew.o _new_handler.o _op_delete.o _op_vdel.o _bb.o _shtab.o _clear_cache.o _trampoline.o __main.o _exit.o _ctors.o
+
+OBJS= ${LIB1OBJS} ${LIB2OBJS}
+LIB1SOBJS=${LIB1OBJS:.o=.so}
+LIB2SOBJS=${LIB2OBJS:.o=.so}
+P1OBJS=${LIB1OBJS:.o=.po}
+P2OBJS=${LIB2OBJS:.o=.po}
+
+${LIB1OBJS}: libgcc1.c
+ ${CC} -c ${CFLAGS} -DL${.PREFIX} -o ${.TARGET} ${.CURDIR}/libgcc1.c
+ @${LD} -x -r ${.TARGET}
+ @mv a.out ${.TARGET}
+
+${LIB2OBJS}: libgcc2.c
+ ${CC} -c ${CFLAGS} -DL${.PREFIX} -o ${.TARGET} ${.CURDIR}/libgcc2.c
+ @${LD} -x -r ${.TARGET}
+ @mv a.out ${.TARGET}
+
+.if !defined(NOPIC)
+${LIB1SOBJS}: libgcc1.c
+ ${CC} -c -fpic ${CFLAGS} -DL${.PREFIX} -o ${.TARGET} ${.CURDIR}/libgcc1.c
+
+${LIB2SOBJS}: libgcc2.c
+ ${CC} -c -fpic ${CFLAGS} -DL${.PREFIX} -o ${.TARGET} ${.CURDIR}/libgcc2.c
+.endif
+
+.if !defined(NOPROFILE)
+${P1OBJS}: libgcc1.c
+ ${CC} -c -p ${CFLAGS} -DL${.PREFIX} -o ${.TARGET} ${.CURDIR}/libgcc1.c
+
+${P2OBJS}: libgcc2.c
+ ${CC} -c -p ${CFLAGS} -DL${.PREFIX} -o ${.TARGET} ${.CURDIR}/libgcc2.c
+.endif
+
+.include <bsd.lib.mk>
+
diff --git a/gnu/usr.bin/cc/libgcc/libgcc1.c b/gnu/usr.bin/cc/libgcc/libgcc1.c
new file mode 100644
index 0000000..7c0e0c1
--- /dev/null
+++ b/gnu/usr.bin/cc/libgcc/libgcc1.c
@@ -0,0 +1,608 @@
+/* Subroutines needed by GCC output code on some machines. */
+/* Compile this file with the Unix C compiler! */
+/* Copyright (C) 1987, 1988, 1992, 1994 Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file with other programs, and to distribute
+those programs without any restriction coming from the use of this
+file. (The General Public License restrictions do apply in other
+respects; for example, they cover modification of the file, and
+distribution when not linked into another program.)
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* As a special exception, if you link this library with other files,
+ some of which are compiled with GCC, to produce an executable,
+ this library does not by itself cause the resulting executable
+ to be covered by the GNU General Public License.
+ This exception does not however invalidate any other reasons why
+ the executable file might be covered by the GNU General Public License. */
+
+#include "config.h"
+
+/* Don't use `fancy_abort' here even if config.h says to use it. */
+#ifdef abort
+#undef abort
+#endif
+
+/* On some machines, cc is really GCC. For these machines, we can't
+ expect these functions to be properly compiled unless GCC open codes
+ the operation (which is precisely when the function won't be used).
+ So allow tm.h to specify ways of accomplishing the operations
+ by defining the macros perform_*.
+
+ On a machine where cc is some other compiler, there is usually no
+ reason to define perform_*. The other compiler normally has other ways
+ of implementing all of these operations.
+
+ In some cases a certain machine may come with GCC installed as cc
+ or may have some other compiler. Then it may make sense for tm.h
+ to define perform_* only if __GNUC__ is defined. */
+
+#ifndef perform_mulsi3
+#define perform_mulsi3(a, b) return a * b
+#endif
+
+#ifndef perform_divsi3
+#define perform_divsi3(a, b) return a / b
+#endif
+
+#ifndef perform_udivsi3
+#define perform_udivsi3(a, b) return a / b
+#endif
+
+#ifndef perform_modsi3
+#define perform_modsi3(a, b) return a % b
+#endif
+
+#ifndef perform_umodsi3
+#define perform_umodsi3(a, b) return a % b
+#endif
+
+#ifndef perform_lshrsi3
+#define perform_lshrsi3(a, b) return a >> b
+#endif
+
+#ifndef perform_lshlsi3
+#define perform_lshlsi3(a, b) return a << b
+#endif
+
+#ifndef perform_ashrsi3
+#define perform_ashrsi3(a, b) return a >> b
+#endif
+
+#ifndef perform_ashlsi3
+#define perform_ashlsi3(a, b) return a << b
+#endif
+
+#ifndef perform_adddf3
+#define perform_adddf3(a, b) return a + b
+#endif
+
+#ifndef perform_subdf3
+#define perform_subdf3(a, b) return a - b
+#endif
+
+#ifndef perform_muldf3
+#define perform_muldf3(a, b) return a * b
+#endif
+
+#ifndef perform_divdf3
+#define perform_divdf3(a, b) return a / b
+#endif
+
+#ifndef perform_addsf3
+#define perform_addsf3(a, b) return INTIFY (a + b)
+#endif
+
+#ifndef perform_subsf3
+#define perform_subsf3(a, b) return INTIFY (a - b)
+#endif
+
+#ifndef perform_mulsf3
+#define perform_mulsf3(a, b) return INTIFY (a * b)
+#endif
+
+#ifndef perform_divsf3
+#define perform_divsf3(a, b) return INTIFY (a / b)
+#endif
+
+#ifndef perform_negdf2
+#define perform_negdf2(a) return -a
+#endif
+
+#ifndef perform_negsf2
+#define perform_negsf2(a) return INTIFY (-a)
+#endif
+
+#ifndef perform_fixdfsi
+#define perform_fixdfsi(a) return (nongcc_SI_type) a;
+#endif
+
+#ifndef perform_fixsfsi
+#define perform_fixsfsi(a) return (nongcc_SI_type) a
+#endif
+
+#ifndef perform_floatsidf
+#define perform_floatsidf(a) return (double) a
+#endif
+
+#ifndef perform_floatsisf
+#define perform_floatsisf(a) return INTIFY ((float) a)
+#endif
+
+#ifndef perform_extendsfdf2
+#define perform_extendsfdf2(a) return a
+#endif
+
+#ifndef perform_truncdfsf2
+#define perform_truncdfsf2(a) return INTIFY (a)
+#endif
+
+/* Note that eqdf2 returns a value for "true" that is == 0,
+ nedf2 returns a value for "true" that is != 0,
+ gtdf2 returns a value for "true" that is > 0,
+ and so on. */
+
+#ifndef perform_eqdf2
+#define perform_eqdf2(a, b) return !(a == b)
+#endif
+
+#ifndef perform_nedf2
+#define perform_nedf2(a, b) return a != b
+#endif
+
+#ifndef perform_gtdf2
+#define perform_gtdf2(a, b) return a > b
+#endif
+
+#ifndef perform_gedf2
+#define perform_gedf2(a, b) return (a >= b) - 1
+#endif
+
+#ifndef perform_ltdf2
+#define perform_ltdf2(a, b) return -(a < b)
+#endif
+
+#ifndef perform_ledf2
+#define perform_ledf2(a, b) return 1 - (a <= b)
+#endif
+
+#ifndef perform_eqsf2
+#define perform_eqsf2(a, b) return !(a == b)
+#endif
+
+#ifndef perform_nesf2
+#define perform_nesf2(a, b) return a != b
+#endif
+
+#ifndef perform_gtsf2
+#define perform_gtsf2(a, b) return a > b
+#endif
+
+#ifndef perform_gesf2
+#define perform_gesf2(a, b) return (a >= b) - 1
+#endif
+
+#ifndef perform_ltsf2
+#define perform_ltsf2(a, b) return -(a < b)
+#endif
+
+#ifndef perform_lesf2
+#define perform_lesf2(a, b) return 1 - (a <= b);
+#endif
+
+/* Define the C data type to use for an SImode value. */
+
+#ifndef nongcc_SI_type
+#define nongcc_SI_type long int
+#endif
+
+/* Define the C data type to use for a value of word size */
+#ifndef nongcc_word_type
+#define nongcc_word_type nongcc_SI_type
+#endif
+
+/* Define the type to be used for returning an SF mode value
+ and the method for turning a float into that type.
+ These definitions work for machines where an SF value is
+ returned in the same register as an int. */
+
+#ifndef FLOAT_VALUE_TYPE
+#define FLOAT_VALUE_TYPE int
+#endif
+
+#ifndef INTIFY
+#define INTIFY(FLOATVAL) (intify.f = (FLOATVAL), intify.i)
+#endif
+
+#ifndef FLOATIFY
+#define FLOATIFY(INTVAL) ((INTVAL).f)
+#endif
+
+#ifndef FLOAT_ARG_TYPE
+#define FLOAT_ARG_TYPE union flt_or_int
+#endif
+
+union flt_or_value { FLOAT_VALUE_TYPE i; float f; };
+
+union flt_or_int { int i; float f; };
+
+
+#ifdef L_mulsi3
+nongcc_SI_type
+__mulsi3 (a, b)
+ nongcc_SI_type a, b;
+{
+ perform_mulsi3 (a, b);
+}
+#endif
+
+#ifdef L_udivsi3
+nongcc_SI_type
+__udivsi3 (a, b)
+ unsigned nongcc_SI_type a, b;
+{
+ perform_udivsi3 (a, b);
+}
+#endif
+
+#ifdef L_divsi3
+nongcc_SI_type
+__divsi3 (a, b)
+ nongcc_SI_type a, b;
+{
+ perform_divsi3 (a, b);
+}
+#endif
+
+#ifdef L_umodsi3
+nongcc_SI_type
+__umodsi3 (a, b)
+ unsigned nongcc_SI_type a, b;
+{
+ perform_umodsi3 (a, b);
+}
+#endif
+
+#ifdef L_modsi3
+nongcc_SI_type
+__modsi3 (a, b)
+ nongcc_SI_type a, b;
+{
+ perform_modsi3 (a, b);
+}
+#endif
+
+#ifdef L_lshrsi3
+nongcc_SI_type
+__lshrsi3 (a, b)
+ unsigned nongcc_SI_type a, b;
+{
+ perform_lshrsi3 (a, b);
+}
+#endif
+
+#ifdef L_lshlsi3
+nongcc_SI_type
+__lshlsi3 (a, b)
+ unsigned nongcc_SI_type a, b;
+{
+ perform_lshlsi3 (a, b);
+}
+#endif
+
+#ifdef L_ashrsi3
+nongcc_SI_type
+__ashrsi3 (a, b)
+ nongcc_SI_type a, b;
+{
+ perform_ashrsi3 (a, b);
+}
+#endif
+
+#ifdef L_ashlsi3
+nongcc_SI_type
+__ashlsi3 (a, b)
+ nongcc_SI_type a, b;
+{
+ perform_ashlsi3 (a, b);
+}
+#endif
+
+#ifdef L_divdf3
+double
+__divdf3 (a, b)
+ double a, b;
+{
+ perform_divdf3 (a, b);
+}
+#endif
+
+#ifdef L_muldf3
+double
+__muldf3 (a, b)
+ double a, b;
+{
+ perform_muldf3 (a, b);
+}
+#endif
+
+#ifdef L_negdf2
+double
+__negdf2 (a)
+ double a;
+{
+ perform_negdf2 (a);
+}
+#endif
+
+#ifdef L_adddf3
+double
+__adddf3 (a, b)
+ double a, b;
+{
+ perform_adddf3 (a, b);
+}
+#endif
+
+#ifdef L_subdf3
+double
+__subdf3 (a, b)
+ double a, b;
+{
+ perform_subdf3 (a, b);
+}
+#endif
+
+/* Note that eqdf2 returns a value for "true" that is == 0,
+ nedf2 returns a value for "true" that is != 0,
+ gtdf2 returns a value for "true" that is > 0,
+ and so on. */
+
+#ifdef L_eqdf2
+nongcc_word_type
+__eqdf2 (a, b)
+ double a, b;
+{
+ /* Value == 0 iff a == b. */
+ perform_eqdf2 (a, b);
+}
+#endif
+
+#ifdef L_nedf2
+nongcc_word_type
+__nedf2 (a, b)
+ double a, b;
+{
+ /* Value != 0 iff a != b. */
+ perform_nedf2 (a, b);
+}
+#endif
+
+#ifdef L_gtdf2
+nongcc_word_type
+__gtdf2 (a, b)
+ double a, b;
+{
+ /* Value > 0 iff a > b. */
+ perform_gtdf2 (a, b);
+}
+#endif
+
+#ifdef L_gedf2
+nongcc_word_type
+__gedf2 (a, b)
+ double a, b;
+{
+ /* Value >= 0 iff a >= b. */
+ perform_gedf2 (a, b);
+}
+#endif
+
+#ifdef L_ltdf2
+nongcc_word_type
+__ltdf2 (a, b)
+ double a, b;
+{
+ /* Value < 0 iff a < b. */
+ perform_ltdf2 (a, b);
+}
+#endif
+
+#ifdef L_ledf2
+nongcc_word_type
+__ledf2 (a, b)
+ double a, b;
+{
+ /* Value <= 0 iff a <= b. */
+ perform_ledf2 (a, b);
+}
+#endif
+
+#ifdef L_fixdfsi
+nongcc_SI_type
+__fixdfsi (a)
+ double a;
+{
+ perform_fixdfsi (a);
+}
+#endif
+
+#ifdef L_fixsfsi
+nongcc_SI_type
+__fixsfsi (a)
+ FLOAT_ARG_TYPE a;
+{
+ union flt_or_value intify;
+ perform_fixsfsi (FLOATIFY (a));
+}
+#endif
+
+#ifdef L_floatsidf
+double
+__floatsidf (a)
+ nongcc_SI_type a;
+{
+ perform_floatsidf (a);
+}
+#endif
+
+#ifdef L_floatsisf
+FLOAT_VALUE_TYPE
+__floatsisf (a)
+ nongcc_SI_type a;
+{
+ union flt_or_value intify;
+ perform_floatsisf (a);
+}
+#endif
+
+#ifdef L_addsf3
+FLOAT_VALUE_TYPE
+__addsf3 (a, b)
+ FLOAT_ARG_TYPE a, b;
+{
+ union flt_or_value intify;
+ perform_addsf3 (FLOATIFY (a), FLOATIFY (b));
+}
+#endif
+
+#ifdef L_negsf2
+FLOAT_VALUE_TYPE
+__negsf2 (a)
+ FLOAT_ARG_TYPE a;
+{
+ union flt_or_value intify;
+ perform_negsf2 (FLOATIFY (a));
+}
+#endif
+
+#ifdef L_subsf3
+FLOAT_VALUE_TYPE
+__subsf3 (a, b)
+ FLOAT_ARG_TYPE a, b;
+{
+ union flt_or_value intify;
+ perform_subsf3 (FLOATIFY (a), FLOATIFY (b));
+}
+#endif
+
+#ifdef L_eqsf2
+nongcc_word_type
+__eqsf2 (a, b)
+ FLOAT_ARG_TYPE a, b;
+{
+ union flt_or_int intify;
+ /* Value == 0 iff a == b. */
+ perform_eqsf2 (FLOATIFY (a), FLOATIFY (b));
+}
+#endif
+
+#ifdef L_nesf2
+nongcc_word_type
+__nesf2 (a, b)
+ FLOAT_ARG_TYPE a, b;
+{
+ union flt_or_int intify;
+ /* Value != 0 iff a != b. */
+ perform_nesf2 (FLOATIFY (a), FLOATIFY (b));
+}
+#endif
+
+#ifdef L_gtsf2
+nongcc_word_type
+__gtsf2 (a, b)
+ FLOAT_ARG_TYPE a, b;
+{
+ union flt_or_int intify;
+ /* Value > 0 iff a > b. */
+ perform_gtsf2 (FLOATIFY (a), FLOATIFY (b));
+}
+#endif
+
+#ifdef L_gesf2
+nongcc_word_type
+__gesf2 (a, b)
+ FLOAT_ARG_TYPE a, b;
+{
+ union flt_or_int intify;
+ /* Value >= 0 iff a >= b. */
+ perform_gesf2 (FLOATIFY (a), FLOATIFY (b));
+}
+#endif
+
+#ifdef L_ltsf2
+nongcc_word_type
+__ltsf2 (a, b)
+ FLOAT_ARG_TYPE a, b;
+{
+ union flt_or_int intify;
+ /* Value < 0 iff a < b. */
+ perform_ltsf2 (FLOATIFY (a), FLOATIFY (b));
+}
+#endif
+
+#ifdef L_lesf2
+nongcc_word_type
+__lesf2 (a, b)
+ FLOAT_ARG_TYPE a, b;
+{
+ union flt_or_int intify;
+ /* Value <= 0 iff a <= b. */
+ perform_lesf2 (FLOATIFY (a), FLOATIFY (b));
+}
+#endif
+
+#ifdef L_mulsf3
+FLOAT_VALUE_TYPE
+__mulsf3 (a, b)
+ FLOAT_ARG_TYPE a, b;
+{
+ union flt_or_value intify;
+ perform_mulsf3 (FLOATIFY (a), FLOATIFY (b));
+}
+#endif
+
+#ifdef L_divsf3
+FLOAT_VALUE_TYPE
+__divsf3 (a, b)
+ FLOAT_ARG_TYPE a, b;
+{
+ union flt_or_value intify;
+ perform_divsf3 (FLOATIFY (a), FLOATIFY (b));
+}
+#endif
+
+#ifdef L_truncdfsf2
+FLOAT_VALUE_TYPE
+__truncdfsf2 (a)
+ double a;
+{
+ union flt_or_value intify;
+ perform_truncdfsf2 (a);
+}
+#endif
+
+#ifdef L_extendsfdf2
+double
+__extendsfdf2 (a)
+ FLOAT_ARG_TYPE a;
+{
+ union flt_or_value intify;
+ perform_extendsfdf2 (FLOATIFY (a));
+}
+#endif
diff --git a/gnu/usr.bin/cc/libgcc/libgcc2.c b/gnu/usr.bin/cc/libgcc/libgcc2.c
new file mode 100644
index 0000000..fc2e1ac
--- /dev/null
+++ b/gnu/usr.bin/cc/libgcc/libgcc2.c
@@ -0,0 +1,2151 @@
+/* More subroutines needed by GCC output code on some machines. */
+/* Compile this one with gcc. */
+/* Copyright (C) 1989, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* As a special exception, if you link this library with other files,
+ some of which are compiled with GCC, to produce an executable,
+ this library does not by itself cause the resulting executable
+ to be covered by the GNU General Public License.
+ This exception does not however invalidate any other reasons why
+ the executable file might be covered by the GNU General Public License. */
+
+/* It is incorrect to include config.h here, because this file is being
+ compiled for the target, and hence definitions concerning only the host
+ do not apply. */
+
+#include "tconfig.h"
+#include "machmode.h"
+#ifndef L_trampoline
+#include <stddef.h>
+#endif
+
+/* Don't use `fancy_abort' here even if config.h says to use it. */
+#ifdef abort
+#undef abort
+#endif
+
+/* In the first part of this file, we are interfacing to calls generated
+ by the compiler itself. These calls pass values into these routines
+ which have very specific modes (rather than very specific types), and
+ these compiler-generated calls also expect any return values to have
+ very specific modes (rather than very specific types). Thus, we need
+ to avoid using regular C language type names in this part of the file
+ because the sizes for those types can be configured to be anything.
+ Instead we use the following special type names. */
+
+typedef unsigned int UQItype __attribute__ ((mode (QI)));
+typedef int SItype __attribute__ ((mode (SI)));
+typedef unsigned int USItype __attribute__ ((mode (SI)));
+typedef int DItype __attribute__ ((mode (DI)));
+typedef unsigned int UDItype __attribute__ ((mode (DI)));
+typedef float SFtype __attribute__ ((mode (SF)));
+typedef float DFtype __attribute__ ((mode (DF)));
+#if LONG_DOUBLE_TYPE_SIZE == 96
+typedef float XFtype __attribute__ ((mode (XF)));
+#endif
+#if LONG_DOUBLE_TYPE_SIZE == 128
+typedef float TFtype __attribute__ ((mode (TF)));
+#endif
+
+#if BITS_PER_WORD==16
+typedef int word_type __attribute__ ((mode (HI)));
+#endif
+#if BITS_PER_WORD==32
+typedef int word_type __attribute__ ((mode (SI)));
+#endif
+#if BITS_PER_WORD==64
+typedef int word_type __attribute__ ((mode (DI)));
+#endif
+
+/* Make sure that we don't accidentally use any normal C language built-in
+ type names in the first part of this file. Instead we want to use *only*
+ the type names defined above. The following macro definitions insure
+ that if we *do* accidentally use some normal C language built-in type name,
+ we will get a syntax error. */
+
+#define char bogus_type
+#define short bogus_type
+#define int bogus_type
+#define long bogus_type
+#define unsigned bogus_type
+#define float bogus_type
+#define double bogus_type
+
+#define SI_TYPE_SIZE (sizeof (SItype) * BITS_PER_UNIT)
+
+/* DIstructs are pairs of SItype values in the order determined by
+ WORDS_BIG_ENDIAN. */
+
+#if WORDS_BIG_ENDIAN
+ struct DIstruct {SItype high, low;};
+#else
+ struct DIstruct {SItype low, high;};
+#endif
+
+/* We need this union to unpack/pack DImode values, since we don't have
+ any arithmetic yet. Incoming DImode parameters are stored into the
+ `ll' field, and the unpacked result is read from the struct `s'. */
+
+typedef union
+{
+ struct DIstruct s;
+ DItype ll;
+} DIunion;
+
+#if defined (L_udivmoddi4) || defined (L_muldi3) || defined (L_udiv_w_sdiv)
+
+#include "longlong.h"
+
+#endif /* udiv or mul */
+
+extern DItype __fixunssfdi (SFtype a);
+extern DItype __fixunsdfdi (DFtype a);
+#if LONG_DOUBLE_TYPE_SIZE == 96
+extern DItype __fixunsxfdi (XFtype a);
+#endif
+#if LONG_DOUBLE_TYPE_SIZE == 128
+extern DItype __fixunstfdi (TFtype a);
+#endif
+
+#if defined (L_negdi2) || defined (L_divdi3) || defined (L_moddi3)
+#if defined (L_divdi3) || defined (L_moddi3)
+static inline
+#endif
+DItype
+__negdi2 (u)
+ DItype u;
+{
+ DIunion w;
+ DIunion uu;
+
+ uu.ll = u;
+
+ w.s.low = -uu.s.low;
+ w.s.high = -uu.s.high - ((USItype) w.s.low > 0);
+
+ return w.ll;
+}
+#endif
+
+#ifdef L_lshldi3
+DItype
+__lshldi3 (u, b)
+ DItype u;
+ SItype b;
+{
+ DIunion w;
+ SItype bm;
+ DIunion uu;
+
+ if (b == 0)
+ return u;
+
+ uu.ll = u;
+
+ bm = (sizeof (SItype) * BITS_PER_UNIT) - b;
+ if (bm <= 0)
+ {
+ w.s.low = 0;
+ w.s.high = (USItype)uu.s.low << -bm;
+ }
+ else
+ {
+ USItype carries = (USItype)uu.s.low >> bm;
+ w.s.low = (USItype)uu.s.low << b;
+ w.s.high = ((USItype)uu.s.high << b) | carries;
+ }
+
+ return w.ll;
+}
+#endif
+
+#ifdef L_lshrdi3
+DItype
+__lshrdi3 (u, b)
+ DItype u;
+ SItype b;
+{
+ DIunion w;
+ SItype bm;
+ DIunion uu;
+
+ if (b == 0)
+ return u;
+
+ uu.ll = u;
+
+ bm = (sizeof (SItype) * BITS_PER_UNIT) - b;
+ if (bm <= 0)
+ {
+ w.s.high = 0;
+ w.s.low = (USItype)uu.s.high >> -bm;
+ }
+ else
+ {
+ USItype carries = (USItype)uu.s.high << bm;
+ w.s.high = (USItype)uu.s.high >> b;
+ w.s.low = ((USItype)uu.s.low >> b) | carries;
+ }
+
+ return w.ll;
+}
+#endif
+
+#ifdef L_ashldi3
+DItype
+__ashldi3 (u, b)
+ DItype u;
+ SItype b;
+{
+ DIunion w;
+ SItype bm;
+ DIunion uu;
+
+ if (b == 0)
+ return u;
+
+ uu.ll = u;
+
+ bm = (sizeof (SItype) * BITS_PER_UNIT) - b;
+ if (bm <= 0)
+ {
+ w.s.low = 0;
+ w.s.high = (USItype)uu.s.low << -bm;
+ }
+ else
+ {
+ USItype carries = (USItype)uu.s.low >> bm;
+ w.s.low = (USItype)uu.s.low << b;
+ w.s.high = ((USItype)uu.s.high << b) | carries;
+ }
+
+ return w.ll;
+}
+#endif
+
+#ifdef L_ashrdi3
+DItype
+__ashrdi3 (u, b)
+ DItype u;
+ SItype b;
+{
+ DIunion w;
+ SItype bm;
+ DIunion uu;
+
+ if (b == 0)
+ return u;
+
+ uu.ll = u;
+
+ bm = (sizeof (SItype) * BITS_PER_UNIT) - b;
+ if (bm <= 0)
+ {
+ /* w.s.high = 1..1 or 0..0 */
+ w.s.high = uu.s.high >> (sizeof (SItype) * BITS_PER_UNIT - 1);
+ w.s.low = uu.s.high >> -bm;
+ }
+ else
+ {
+ USItype carries = (USItype)uu.s.high << bm;
+ w.s.high = uu.s.high >> b;
+ w.s.low = ((USItype)uu.s.low >> b) | carries;
+ }
+
+ return w.ll;
+}
+#endif
+
+#ifdef L_ffsdi2
+DItype
+__ffsdi2 (u)
+ DItype u;
+{
+ DIunion uu, w;
+ uu.ll = u;
+ w.s.high = 0;
+ w.s.low = ffs (uu.s.low);
+ if (w.s.low != 0)
+ return w.ll;
+ w.s.low = ffs (uu.s.high);
+ if (w.s.low != 0)
+ {
+ w.s.low += BITS_PER_UNIT * sizeof (SItype);
+ return w.ll;
+ }
+ return w.ll;
+}
+#endif
+
+#ifdef L_muldi3
+DItype
+__muldi3 (u, v)
+ DItype u, v;
+{
+ DIunion w;
+ DIunion uu, vv;
+
+ uu.ll = u,
+ vv.ll = v;
+
+ w.ll = __umulsidi3 (uu.s.low, vv.s.low);
+ w.s.high += ((USItype) uu.s.low * (USItype) vv.s.high
+ + (USItype) uu.s.high * (USItype) vv.s.low);
+
+ return w.ll;
+}
+#endif
+
+#ifdef L_udiv_w_sdiv
+USItype
+__udiv_w_sdiv (rp, a1, a0, d)
+ USItype *rp, a1, a0, d;
+{
+ USItype q, r;
+ USItype c0, c1, b1;
+
+ if ((SItype) d >= 0)
+ {
+ if (a1 < d - a1 - (a0 >> (SI_TYPE_SIZE - 1)))
+ {
+ /* dividend, divisor, and quotient are nonnegative */
+ sdiv_qrnnd (q, r, a1, a0, d);
+ }
+ else
+ {
+ /* Compute c1*2^32 + c0 = a1*2^32 + a0 - 2^31*d */
+ sub_ddmmss (c1, c0, a1, a0, d >> 1, d << (SI_TYPE_SIZE - 1));
+ /* Divide (c1*2^32 + c0) by d */
+ sdiv_qrnnd (q, r, c1, c0, d);
+ /* Add 2^31 to quotient */
+ q += (USItype) 1 << (SI_TYPE_SIZE - 1);
+ }
+ }
+ else
+ {
+ b1 = d >> 1; /* d/2, between 2^30 and 2^31 - 1 */
+ c1 = a1 >> 1; /* A/2 */
+ c0 = (a1 << (SI_TYPE_SIZE - 1)) + (a0 >> 1);
+
+ if (a1 < b1) /* A < 2^32*b1, so A/2 < 2^31*b1 */
+ {
+ sdiv_qrnnd (q, r, c1, c0, b1); /* (A/2) / (d/2) */
+
+ r = 2*r + (a0 & 1); /* Remainder from A/(2*b1) */
+ if ((d & 1) != 0)
+ {
+ if (r >= q)
+ r = r - q;
+ else if (q - r <= d)
+ {
+ r = r - q + d;
+ q--;
+ }
+ else
+ {
+ r = r - q + 2*d;
+ q -= 2;
+ }
+ }
+ }
+ else if (c1 < b1) /* So 2^31 <= (A/2)/b1 < 2^32 */
+ {
+ c1 = (b1 - 1) - c1;
+ c0 = ~c0; /* logical NOT */
+
+ sdiv_qrnnd (q, r, c1, c0, b1); /* (A/2) / (d/2) */
+
+ q = ~q; /* (A/2)/b1 */
+ r = (b1 - 1) - r;
+
+ r = 2*r + (a0 & 1); /* A/(2*b1) */
+
+ if ((d & 1) != 0)
+ {
+ if (r >= q)
+ r = r - q;
+ else if (q - r <= d)
+ {
+ r = r - q + d;
+ q--;
+ }
+ else
+ {
+ r = r - q + 2*d;
+ q -= 2;
+ }
+ }
+ }
+ else /* Implies c1 = b1 */
+ { /* Hence a1 = d - 1 = 2*b1 - 1 */
+ if (a0 >= -d)
+ {
+ q = -1;
+ r = a0 + d;
+ }
+ else
+ {
+ q = -2;
+ r = a0 + 2*d;
+ }
+ }
+ }
+
+ *rp = r;
+ return q;
+}
+#endif
+
+#ifdef L_udivmoddi4
+static const UQItype __clz_tab[] =
+{
+ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+};
+
+UDItype
+__udivmoddi4 (n, d, rp)
+ UDItype n, d;
+ UDItype *rp;
+{
+ DIunion ww;
+ DIunion nn, dd;
+ DIunion rr;
+ USItype d0, d1, n0, n1, n2;
+ USItype q0, q1;
+ USItype b, bm;
+
+ nn.ll = n;
+ dd.ll = d;
+
+ d0 = dd.s.low;
+ d1 = dd.s.high;
+ n0 = nn.s.low;
+ n1 = nn.s.high;
+
+#if !UDIV_NEEDS_NORMALIZATION
+ if (d1 == 0)
+ {
+ if (d0 > n1)
+ {
+ /* 0q = nn / 0D */
+
+ udiv_qrnnd (q0, n0, n1, n0, d0);
+ q1 = 0;
+
+ /* Remainder in n0. */
+ }
+ else
+ {
+ /* qq = NN / 0d */
+
+ if (d0 == 0)
+ d0 = 1 / d0; /* Divide intentionally by zero. */
+
+ udiv_qrnnd (q1, n1, 0, n1, d0);
+ udiv_qrnnd (q0, n0, n1, n0, d0);
+
+ /* Remainder in n0. */
+ }
+
+ if (rp != 0)
+ {
+ rr.s.low = n0;
+ rr.s.high = 0;
+ *rp = rr.ll;
+ }
+ }
+
+#else /* UDIV_NEEDS_NORMALIZATION */
+
+ if (d1 == 0)
+ {
+ if (d0 > n1)
+ {
+ /* 0q = nn / 0D */
+
+ count_leading_zeros (bm, d0);
+
+ if (bm != 0)
+ {
+ /* Normalize, i.e. make the most significant bit of the
+ denominator set. */
+
+ d0 = d0 << bm;
+ n1 = (n1 << bm) | (n0 >> (SI_TYPE_SIZE - bm));
+ n0 = n0 << bm;
+ }
+
+ udiv_qrnnd (q0, n0, n1, n0, d0);
+ q1 = 0;
+
+ /* Remainder in n0 >> bm. */
+ }
+ else
+ {
+ /* qq = NN / 0d */
+
+ if (d0 == 0)
+ d0 = 1 / d0; /* Divide intentionally by zero. */
+
+ count_leading_zeros (bm, d0);
+
+ if (bm == 0)
+ {
+ /* From (n1 >= d0) /\ (the most significant bit of d0 is set),
+ conclude (the most significant bit of n1 is set) /\ (the
+ leading quotient digit q1 = 1).
+
+ This special case is necessary, not an optimization.
+ (Shifts counts of SI_TYPE_SIZE are undefined.) */
+
+ n1 -= d0;
+ q1 = 1;
+ }
+ else
+ {
+ /* Normalize. */
+
+ b = SI_TYPE_SIZE - bm;
+
+ d0 = d0 << bm;
+ n2 = n1 >> b;
+ n1 = (n1 << bm) | (n0 >> b);
+ n0 = n0 << bm;
+
+ udiv_qrnnd (q1, n1, n2, n1, d0);
+ }
+
+ /* n1 != d0... */
+
+ udiv_qrnnd (q0, n0, n1, n0, d0);
+
+ /* Remainder in n0 >> bm. */
+ }
+
+ if (rp != 0)
+ {
+ rr.s.low = n0 >> bm;
+ rr.s.high = 0;
+ *rp = rr.ll;
+ }
+ }
+#endif /* UDIV_NEEDS_NORMALIZATION */
+
+ else
+ {
+ if (d1 > n1)
+ {
+ /* 00 = nn / DD */
+
+ q0 = 0;
+ q1 = 0;
+
+ /* Remainder in n1n0. */
+ if (rp != 0)
+ {
+ rr.s.low = n0;
+ rr.s.high = n1;
+ *rp = rr.ll;
+ }
+ }
+ else
+ {
+ /* 0q = NN / dd */
+
+ count_leading_zeros (bm, d1);
+ if (bm == 0)
+ {
+ /* From (n1 >= d1) /\ (the most significant bit of d1 is set),
+ conclude (the most significant bit of n1 is set) /\ (the
+ quotient digit q0 = 0 or 1).
+
+ This special case is necessary, not an optimization. */
+
+ /* The condition on the next line takes advantage of that
+ n1 >= d1 (true due to program flow). */
+ if (n1 > d1 || n0 >= d0)
+ {
+ q0 = 1;
+ sub_ddmmss (n1, n0, n1, n0, d1, d0);
+ }
+ else
+ q0 = 0;
+
+ q1 = 0;
+
+ if (rp != 0)
+ {
+ rr.s.low = n0;
+ rr.s.high = n1;
+ *rp = rr.ll;
+ }
+ }
+ else
+ {
+ USItype m1, m0;
+ /* Normalize. */
+
+ b = SI_TYPE_SIZE - bm;
+
+ d1 = (d1 << bm) | (d0 >> b);
+ d0 = d0 << bm;
+ n2 = n1 >> b;
+ n1 = (n1 << bm) | (n0 >> b);
+ n0 = n0 << bm;
+
+ udiv_qrnnd (q0, n1, n2, n1, d1);
+ umul_ppmm (m1, m0, q0, d0);
+
+ if (m1 > n1 || (m1 == n1 && m0 > n0))
+ {
+ q0--;
+ sub_ddmmss (m1, m0, m1, m0, d1, d0);
+ }
+
+ q1 = 0;
+
+ /* Remainder in (n1n0 - m1m0) >> bm. */
+ if (rp != 0)
+ {
+ sub_ddmmss (n1, n0, n1, n0, m1, m0);
+ rr.s.low = (n1 << b) | (n0 >> bm);
+ rr.s.high = n1 >> bm;
+ *rp = rr.ll;
+ }
+ }
+ }
+ }
+
+ ww.s.low = q0;
+ ww.s.high = q1;
+ return ww.ll;
+}
+#endif
+
+#ifdef L_divdi3
+UDItype __udivmoddi4 ();
+
+DItype
+__divdi3 (u, v)
+ DItype u, v;
+{
+ SItype c = 0;
+ DIunion uu, vv;
+ DItype w;
+
+ uu.ll = u;
+ vv.ll = v;
+
+ if (uu.s.high < 0)
+ c = ~c,
+ uu.ll = __negdi2 (uu.ll);
+ if (vv.s.high < 0)
+ c = ~c,
+ vv.ll = __negdi2 (vv.ll);
+
+ w = __udivmoddi4 (uu.ll, vv.ll, (UDItype *) 0);
+ if (c)
+ w = __negdi2 (w);
+
+ return w;
+}
+#endif
+
+#ifdef L_moddi3
+UDItype __udivmoddi4 ();
+DItype
+__moddi3 (u, v)
+ DItype u, v;
+{
+ SItype c = 0;
+ DIunion uu, vv;
+ DItype w;
+
+ uu.ll = u;
+ vv.ll = v;
+
+ if (uu.s.high < 0)
+ c = ~c,
+ uu.ll = __negdi2 (uu.ll);
+ if (vv.s.high < 0)
+ vv.ll = __negdi2 (vv.ll);
+
+ (void) __udivmoddi4 (uu.ll, vv.ll, &w);
+ if (c)
+ w = __negdi2 (w);
+
+ return w;
+}
+#endif
+
+#ifdef L_umoddi3
+UDItype __udivmoddi4 ();
+UDItype
+__umoddi3 (u, v)
+ UDItype u, v;
+{
+ UDItype w;
+
+ (void) __udivmoddi4 (u, v, &w);
+
+ return w;
+}
+#endif
+
+#ifdef L_udivdi3
+UDItype __udivmoddi4 ();
+UDItype
+__udivdi3 (n, d)
+ UDItype n, d;
+{
+ return __udivmoddi4 (n, d, (UDItype *) 0);
+}
+#endif
+
+#ifdef L_cmpdi2
+word_type
+__cmpdi2 (a, b)
+ DItype a, b;
+{
+ DIunion au, bu;
+
+ au.ll = a, bu.ll = b;
+
+ if (au.s.high < bu.s.high)
+ return 0;
+ else if (au.s.high > bu.s.high)
+ return 2;
+ if ((USItype) au.s.low < (USItype) bu.s.low)
+ return 0;
+ else if ((USItype) au.s.low > (USItype) bu.s.low)
+ return 2;
+ return 1;
+}
+#endif
+
+#ifdef L_ucmpdi2
+word_type
+__ucmpdi2 (a, b)
+ DItype a, b;
+{
+ DIunion au, bu;
+
+ au.ll = a, bu.ll = b;
+
+ if ((USItype) au.s.high < (USItype) bu.s.high)
+ return 0;
+ else if ((USItype) au.s.high > (USItype) bu.s.high)
+ return 2;
+ if ((USItype) au.s.low < (USItype) bu.s.low)
+ return 0;
+ else if ((USItype) au.s.low > (USItype) bu.s.low)
+ return 2;
+ return 1;
+}
+#endif
+
+#if defined(L_fixunstfdi) && (LONG_DOUBLE_TYPE_SIZE == 128)
+#define WORD_SIZE (sizeof (SItype) * BITS_PER_UNIT)
+#define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE)
+
+DItype
+__fixunstfdi (a)
+ TFtype a;
+{
+ TFtype b;
+ UDItype v;
+
+ if (a < 0)
+ return 0;
+
+ /* Compute high word of result, as a flonum. */
+ b = (a / HIGH_WORD_COEFF);
+ /* Convert that to fixed (but not to DItype!),
+ and shift it into the high word. */
+ v = (USItype) b;
+ v <<= WORD_SIZE;
+ /* Remove high part from the TFtype, leaving the low part as flonum. */
+ a -= (TFtype)v;
+ /* Convert that to fixed (but not to DItype!) and add it in.
+ Sometimes A comes out negative. This is significant, since
+ A has more bits than a long int does. */
+ if (a < 0)
+ v -= (USItype) (- a);
+ else
+ v += (USItype) a;
+ return v;
+}
+#endif
+
+#if defined(L_fixtfdi) && (LONG_DOUBLE_TYPE_SIZE == 128)
+DItype
+__fixtfdi (a)
+ TFtype a;
+{
+ if (a < 0)
+ return - __fixunstfdi (-a);
+ return __fixunstfdi (a);
+}
+#endif
+
+#if defined(L_fixunsxfdi) && (LONG_DOUBLE_TYPE_SIZE == 96)
+#define WORD_SIZE (sizeof (SItype) * BITS_PER_UNIT)
+#define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE)
+
+DItype
+__fixunsxfdi (a)
+ XFtype a;
+{
+ XFtype b;
+ UDItype v;
+
+ if (a < 0)
+ return 0;
+
+ /* Compute high word of result, as a flonum. */
+ b = (a / HIGH_WORD_COEFF);
+ /* Convert that to fixed (but not to DItype!),
+ and shift it into the high word. */
+ v = (USItype) b;
+ v <<= WORD_SIZE;
+ /* Remove high part from the XFtype, leaving the low part as flonum. */
+ a -= (XFtype)v;
+ /* Convert that to fixed (but not to DItype!) and add it in.
+ Sometimes A comes out negative. This is significant, since
+ A has more bits than a long int does. */
+ if (a < 0)
+ v -= (USItype) (- a);
+ else
+ v += (USItype) a;
+ return v;
+}
+#endif
+
+#if defined(L_fixxfdi) && (LONG_DOUBLE_TYPE_SIZE == 96)
+DItype
+__fixxfdi (a)
+ XFtype a;
+{
+ if (a < 0)
+ return - __fixunsxfdi (-a);
+ return __fixunsxfdi (a);
+}
+#endif
+
+#ifdef L_fixunsdfdi
+#define WORD_SIZE (sizeof (SItype) * BITS_PER_UNIT)
+#define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE)
+
+DItype
+__fixunsdfdi (a)
+ DFtype a;
+{
+ DFtype b;
+ UDItype v;
+
+ if (a < 0)
+ return 0;
+
+ /* Compute high word of result, as a flonum. */
+ b = (a / HIGH_WORD_COEFF);
+ /* Convert that to fixed (but not to DItype!),
+ and shift it into the high word. */
+ v = (USItype) b;
+ v <<= WORD_SIZE;
+ /* Remove high part from the DFtype, leaving the low part as flonum. */
+ a -= (DFtype)v;
+ /* Convert that to fixed (but not to DItype!) and add it in.
+ Sometimes A comes out negative. This is significant, since
+ A has more bits than a long int does. */
+ if (a < 0)
+ v -= (USItype) (- a);
+ else
+ v += (USItype) a;
+ return v;
+}
+#endif
+
+#ifdef L_fixdfdi
+DItype
+__fixdfdi (a)
+ DFtype a;
+{
+ if (a < 0)
+ return - __fixunsdfdi (-a);
+ return __fixunsdfdi (a);
+}
+#endif
+
+#ifdef L_fixunssfdi
+#define WORD_SIZE (sizeof (SItype) * BITS_PER_UNIT)
+#define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE)
+
+DItype
+__fixunssfdi (SFtype original_a)
+{
+ /* Convert the SFtype to a DFtype, because that is surely not going
+ to lose any bits. Some day someone else can write a faster version
+ that avoids converting to DFtype, and verify it really works right. */
+ DFtype a = original_a;
+ DFtype b;
+ UDItype v;
+
+ if (a < 0)
+ return 0;
+
+ /* Compute high word of result, as a flonum. */
+ b = (a / HIGH_WORD_COEFF);
+ /* Convert that to fixed (but not to DItype!),
+ and shift it into the high word. */
+ v = (USItype) b;
+ v <<= WORD_SIZE;
+ /* Remove high part from the DFtype, leaving the low part as flonum. */
+ a -= (DFtype)v;
+ /* Convert that to fixed (but not to DItype!) and add it in.
+ Sometimes A comes out negative. This is significant, since
+ A has more bits than a long int does. */
+ if (a < 0)
+ v -= (USItype) (- a);
+ else
+ v += (USItype) a;
+ return v;
+}
+#endif
+
+#ifdef L_fixsfdi
+DItype
+__fixsfdi (SFtype a)
+{
+ if (a < 0)
+ return - __fixunssfdi (-a);
+ return __fixunssfdi (a);
+}
+#endif
+
+#if defined(L_floatdixf) && (LONG_DOUBLE_TYPE_SIZE == 96)
+#define WORD_SIZE (sizeof (SItype) * BITS_PER_UNIT)
+#define HIGH_HALFWORD_COEFF (((UDItype) 1) << (WORD_SIZE / 2))
+#define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE)
+
+XFtype
+__floatdixf (u)
+ DItype u;
+{
+ XFtype d;
+ SItype negate = 0;
+
+ if (u < 0)
+ u = -u, negate = 1;
+
+ d = (USItype) (u >> WORD_SIZE);
+ d *= HIGH_HALFWORD_COEFF;
+ d *= HIGH_HALFWORD_COEFF;
+ d += (USItype) (u & (HIGH_WORD_COEFF - 1));
+
+ return (negate ? -d : d);
+}
+#endif
+
+#if defined(L_floatditf) && (LONG_DOUBLE_TYPE_SIZE == 128)
+#define WORD_SIZE (sizeof (SItype) * BITS_PER_UNIT)
+#define HIGH_HALFWORD_COEFF (((UDItype) 1) << (WORD_SIZE / 2))
+#define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE)
+
+TFtype
+__floatditf (u)
+ DItype u;
+{
+ TFtype d;
+ SItype negate = 0;
+
+ if (u < 0)
+ u = -u, negate = 1;
+
+ d = (USItype) (u >> WORD_SIZE);
+ d *= HIGH_HALFWORD_COEFF;
+ d *= HIGH_HALFWORD_COEFF;
+ d += (USItype) (u & (HIGH_WORD_COEFF - 1));
+
+ return (negate ? -d : d);
+}
+#endif
+
+#ifdef L_floatdidf
+#define WORD_SIZE (sizeof (SItype) * BITS_PER_UNIT)
+#define HIGH_HALFWORD_COEFF (((UDItype) 1) << (WORD_SIZE / 2))
+#define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE)
+
+DFtype
+__floatdidf (u)
+ DItype u;
+{
+ DFtype d;
+ SItype negate = 0;
+
+ if (u < 0)
+ u = -u, negate = 1;
+
+ d = (USItype) (u >> WORD_SIZE);
+ d *= HIGH_HALFWORD_COEFF;
+ d *= HIGH_HALFWORD_COEFF;
+ d += (USItype) (u & (HIGH_WORD_COEFF - 1));
+
+ return (negate ? -d : d);
+}
+#endif
+
+#ifdef L_floatdisf
+#define WORD_SIZE (sizeof (SItype) * BITS_PER_UNIT)
+#define HIGH_HALFWORD_COEFF (((UDItype) 1) << (WORD_SIZE / 2))
+#define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE)
+#define DI_SIZE (sizeof (DItype) * BITS_PER_UNIT)
+#if TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
+#define DF_SIZE 53
+#define SF_SIZE 24
+#else
+#if TARGET_FLOAT_FORMAT == IBM_FLOAT_FORMAT
+#define DF_SIZE 56
+#define SF_SIZE 24
+#else
+#if TARGET_FLOAT_FORMAT == VAX_FLOAT_FORMAT
+#define DF_SIZE 56
+#define SF_SIZE 24
+#else
+#define DF_SIZE 0
+#define SF_SIZE 0
+#endif
+#endif
+#endif
+
+
+SFtype
+__floatdisf (u)
+ DItype u;
+{
+ /* Do the calculation in DFmode
+ so that we don't lose any of the precision of the high word
+ while multiplying it. */
+ DFtype f;
+ SItype negate = 0;
+
+ if (u < 0)
+ u = -u, negate = 1;
+
+ /* Protect against double-rounding error.
+ Represent any low-order bits, that might be truncated in DFmode,
+ by a bit that won't be lost. The bit can go in anywhere below the
+ rounding position of the SFmode. A fixed mask and bit position
+ handles all usual configurations. It doesn't handle the case
+ of 128-bit DImode, however. */
+ if (DF_SIZE < DI_SIZE
+ && DF_SIZE > (DI_SIZE - DF_SIZE + SF_SIZE))
+ {
+#define REP_BIT ((USItype) 1 << (DI_SIZE - DF_SIZE))
+ if (u >= ((UDItype) 1 << DF_SIZE))
+ {
+ if ((USItype) u & (REP_BIT - 1))
+ u |= REP_BIT;
+ }
+ }
+ f = (USItype) (u >> WORD_SIZE);
+ f *= HIGH_HALFWORD_COEFF;
+ f *= HIGH_HALFWORD_COEFF;
+ f += (USItype) (u & (HIGH_WORD_COEFF - 1));
+
+ return (SFtype) (negate ? -f : f);
+}
+#endif
+
+#if defined(L_fixunsxfsi) && LONG_DOUBLE_TYPE_SIZE == 96
+#include "glimits.h"
+
+USItype
+__fixunsxfsi (a)
+ XFtype a;
+{
+ if (a >= - (DFtype) LONG_MIN)
+ return (SItype) (a + LONG_MIN) - LONG_MIN;
+ return (SItype) a;
+}
+#endif
+
+#ifdef L_fixunsdfsi
+#include "glimits.h"
+
+USItype
+__fixunsdfsi (a)
+ DFtype a;
+{
+ if (a >= - (DFtype) LONG_MIN)
+ return (SItype) (a + LONG_MIN) - LONG_MIN;
+ return (SItype) a;
+}
+#endif
+
+#ifdef L_fixunssfsi
+#include "glimits.h"
+
+USItype
+__fixunssfsi (SFtype a)
+{
+ if (a >= - (SFtype) LONG_MIN)
+ return (SItype) (a + LONG_MIN) - LONG_MIN;
+ return (SItype) a;
+}
+#endif
+
+/* From here on down, the routines use normal data types. */
+
+#define SItype bogus_type
+#define USItype bogus_type
+#define DItype bogus_type
+#define UDItype bogus_type
+#define SFtype bogus_type
+#define DFtype bogus_type
+
+#undef char
+#undef short
+#undef int
+#undef long
+#undef unsigned
+#undef float
+#undef double
+
+#ifdef L__gcc_bcmp
+
+/* Like bcmp except the sign is meaningful.
+ Reult is negative if S1 is less than S2,
+ positive if S1 is greater, 0 if S1 and S2 are equal. */
+
+int
+__gcc_bcmp (s1, s2, size)
+ unsigned char *s1, *s2;
+ size_t size;
+{
+ while (size > 0)
+ {
+ unsigned char c1 = *s1++, c2 = *s2++;
+ if (c1 != c2)
+ return c1 - c2;
+ size--;
+ }
+ return 0;
+}
+
+#endif
+
+#ifdef L_varargs
+#ifdef __i860__
+#if defined(__svr4__) || defined(__alliant__)
+ asm (" .text");
+ asm (" .align 4");
+
+/* The Alliant needs the added underscore. */
+ asm (".globl __builtin_saveregs");
+asm ("__builtin_saveregs:");
+ asm (".globl ___builtin_saveregs");
+asm ("___builtin_saveregs:");
+
+ asm (" andnot 0x0f,%sp,%sp"); /* round down to 16-byte boundary */
+ asm (" adds -96,%sp,%sp"); /* allocate stack space for reg save
+ area and also for a new va_list
+ structure */
+ /* Save all argument registers in the arg reg save area. The
+ arg reg save area must have the following layout (according
+ to the svr4 ABI):
+
+ struct {
+ union {
+ float freg[8];
+ double dreg[4];
+ } float_regs;
+ long ireg[12];
+ };
+ */
+
+ asm (" fst.q %f8, 0(%sp)"); /* save floating regs (f8-f15) */
+ asm (" fst.q %f12,16(%sp)");
+
+ asm (" st.l %r16,32(%sp)"); /* save integer regs (r16-r27) */
+ asm (" st.l %r17,36(%sp)");
+ asm (" st.l %r18,40(%sp)");
+ asm (" st.l %r19,44(%sp)");
+ asm (" st.l %r20,48(%sp)");
+ asm (" st.l %r21,52(%sp)");
+ asm (" st.l %r22,56(%sp)");
+ asm (" st.l %r23,60(%sp)");
+ asm (" st.l %r24,64(%sp)");
+ asm (" st.l %r25,68(%sp)");
+ asm (" st.l %r26,72(%sp)");
+ asm (" st.l %r27,76(%sp)");
+
+ asm (" adds 80,%sp,%r16"); /* compute the address of the new
+ va_list structure. Put in into
+ r16 so that it will be returned
+ to the caller. */
+
+ /* Initialize all fields of the new va_list structure. This
+ structure looks like:
+
+ typedef struct {
+ unsigned long ireg_used;
+ unsigned long freg_used;
+ long *reg_base;
+ long *mem_ptr;
+ } va_list;
+ */
+
+ asm (" st.l %r0, 0(%r16)"); /* nfixed */
+ asm (" st.l %r0, 4(%r16)"); /* nfloating */
+ asm (" st.l %sp, 8(%r16)"); /* __va_ctl points to __va_struct. */
+ asm (" bri %r1"); /* delayed return */
+ asm (" st.l %r28,12(%r16)"); /* pointer to overflow args */
+
+#else /* not __svr4__ */
+#if defined(__PARAGON__)
+ /*
+ * we'll use SVR4-ish varargs but need SVR3.2 assembler syntax,
+ * and we stand a better chance of hooking into libraries
+ * compiled by PGI. [andyp@ssd.intel.com]
+ */
+ asm (" .text");
+ asm (" .align 4");
+ asm (".globl __builtin_saveregs");
+asm ("__builtin_saveregs:");
+ asm (".globl ___builtin_saveregs");
+asm ("___builtin_saveregs:");
+
+ asm (" andnot 0x0f,sp,sp"); /* round down to 16-byte boundary */
+ asm (" adds -96,sp,sp"); /* allocate stack space for reg save
+ area and also for a new va_list
+ structure */
+ /* Save all argument registers in the arg reg save area. The
+ arg reg save area must have the following layout (according
+ to the svr4 ABI):
+
+ struct {
+ union {
+ float freg[8];
+ double dreg[4];
+ } float_regs;
+ long ireg[12];
+ };
+ */
+
+ asm (" fst.q f8, 0(sp)");
+ asm (" fst.q f12,16(sp)");
+ asm (" st.l r16,32(sp)");
+ asm (" st.l r17,36(sp)");
+ asm (" st.l r18,40(sp)");
+ asm (" st.l r19,44(sp)");
+ asm (" st.l r20,48(sp)");
+ asm (" st.l r21,52(sp)");
+ asm (" st.l r22,56(sp)");
+ asm (" st.l r23,60(sp)");
+ asm (" st.l r24,64(sp)");
+ asm (" st.l r25,68(sp)");
+ asm (" st.l r26,72(sp)");
+ asm (" st.l r27,76(sp)");
+
+ asm (" adds 80,sp,r16"); /* compute the address of the new
+ va_list structure. Put in into
+ r16 so that it will be returned
+ to the caller. */
+
+ /* Initialize all fields of the new va_list structure. This
+ structure looks like:
+
+ typedef struct {
+ unsigned long ireg_used;
+ unsigned long freg_used;
+ long *reg_base;
+ long *mem_ptr;
+ } va_list;
+ */
+
+ asm (" st.l r0, 0(r16)"); /* nfixed */
+ asm (" st.l r0, 4(r16)"); /* nfloating */
+ asm (" st.l sp, 8(r16)"); /* __va_ctl points to __va_struct. */
+ asm (" bri r1"); /* delayed return */
+ asm (" st.l r28,12(r16)"); /* pointer to overflow args */
+#else /* not __PARAGON__ */
+ asm (" .text");
+ asm (" .align 4");
+
+ asm (".globl ___builtin_saveregs");
+ asm ("___builtin_saveregs:");
+ asm (" mov sp,r30");
+ asm (" andnot 0x0f,sp,sp");
+ asm (" adds -96,sp,sp"); /* allocate sufficient space on the stack */
+
+/* Fill in the __va_struct. */
+ asm (" st.l r16, 0(sp)"); /* save integer regs (r16-r27) */
+ asm (" st.l r17, 4(sp)"); /* int fixed[12] */
+ asm (" st.l r18, 8(sp)");
+ asm (" st.l r19,12(sp)");
+ asm (" st.l r20,16(sp)");
+ asm (" st.l r21,20(sp)");
+ asm (" st.l r22,24(sp)");
+ asm (" st.l r23,28(sp)");
+ asm (" st.l r24,32(sp)");
+ asm (" st.l r25,36(sp)");
+ asm (" st.l r26,40(sp)");
+ asm (" st.l r27,44(sp)");
+
+ asm (" fst.q f8, 48(sp)"); /* save floating regs (f8-f15) */
+ asm (" fst.q f12,64(sp)"); /* int floating[8] */
+
+/* Fill in the __va_ctl. */
+ asm (" st.l sp, 80(sp)"); /* __va_ctl points to __va_struct. */
+ asm (" st.l r28,84(sp)"); /* pointer to more args */
+ asm (" st.l r0, 88(sp)"); /* nfixed */
+ asm (" st.l r0, 92(sp)"); /* nfloating */
+
+ asm (" adds 80,sp,r16"); /* return address of the __va_ctl. */
+ asm (" bri r1");
+ asm (" mov r30,sp");
+ /* recover stack and pass address to start
+ of data. */
+#endif /* not __PARAGON__ */
+#endif /* not __svr4__ */
+#else /* not __i860__ */
+#ifdef __sparc__
+ asm (".global __builtin_saveregs");
+ asm ("__builtin_saveregs:");
+ asm (".global ___builtin_saveregs");
+ asm ("___builtin_saveregs:");
+#ifdef NEED_PROC_COMMAND
+ asm (".proc 020");
+#endif
+ asm ("st %i0,[%fp+68]");
+ asm ("st %i1,[%fp+72]");
+ asm ("st %i2,[%fp+76]");
+ asm ("st %i3,[%fp+80]");
+ asm ("st %i4,[%fp+84]");
+ asm ("retl");
+ asm ("st %i5,[%fp+88]");
+#ifdef NEED_TYPE_COMMAND
+ asm (".type __builtin_saveregs,#function");
+ asm (".size __builtin_saveregs,.-__builtin_saveregs");
+#endif
+#else /* not __sparc__ */
+#if defined(__MIPSEL__) | defined(__R3000__) | defined(__R2000__) | defined(__mips__)
+
+ asm (" .text");
+ asm (" .ent __builtin_saveregs");
+ asm (" .globl __builtin_saveregs");
+ asm ("__builtin_saveregs:");
+ asm (" sw $4,0($30)");
+ asm (" sw $5,4($30)");
+ asm (" sw $6,8($30)");
+ asm (" sw $7,12($30)");
+ asm (" j $31");
+ asm (" .end __builtin_saveregs");
+#else /* not __mips__, etc. */
+
+void *
+__builtin_saveregs ()
+{
+ abort ();
+}
+
+#endif /* not __mips__ */
+#endif /* not __sparc__ */
+#endif /* not __i860__ */
+#endif
+
+#ifdef L_eprintf
+#ifndef inhibit_libc
+
+#undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */
+#include <stdio.h>
+/* This is used by the `assert' macro. */
+void
+__eprintf (string, expression, line, filename)
+ const char *string;
+ const char *expression;
+ int line;
+ const char *filename;
+{
+ fprintf (stderr, string, expression, line, filename);
+ fflush (stderr);
+ abort ();
+}
+
+#endif
+#endif
+
+#ifdef L_bb
+
+/* Structure emitted by -a */
+struct bb
+{
+ long zero_word;
+ const char *filename;
+ long *counts;
+ long ncounts;
+ struct bb *next;
+ const unsigned long *addresses;
+
+ /* Older GCC's did not emit these fields. */
+ long nwords;
+ const char **functions;
+ const long *line_nums;
+ const char **filenames;
+};
+
+#ifdef BLOCK_PROFILER_CODE
+BLOCK_PROFILER_CODE
+#else
+#ifndef inhibit_libc
+
+/* Simple minded basic block profiling output dumper for
+ systems that don't provde tcov support. At present,
+ it requires atexit and stdio. */
+
+#undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */
+#include <stdio.h>
+
+#ifdef HAVE_ATEXIT
+extern void atexit (void (*) (void));
+#define ON_EXIT(FUNC,ARG) atexit ((FUNC))
+#else
+#ifdef sun
+extern void on_exit (void*, void*);
+#define ON_EXIT(FUNC,ARG) on_exit ((FUNC), (ARG))
+#endif
+#endif
+
+static struct bb *bb_head = (struct bb *)0;
+
+/* Return the number of digits needed to print a value */
+/* __inline__ */ static int num_digits (long value, int base)
+{
+ int minus = (value < 0 && base != 16);
+ unsigned long v = (minus) ? -value : value;
+ int ret = minus;
+
+ do
+ {
+ v /= base;
+ ret++;
+ }
+ while (v);
+
+ return ret;
+}
+
+void
+__bb_exit_func (void)
+{
+ FILE *file = fopen ("bb.out", "a");
+ long time_value;
+
+ if (!file)
+ perror ("bb.out");
+
+ else
+ {
+ struct bb *ptr;
+
+ /* This is somewhat type incorrect, but it avoids worrying about
+ exactly where time.h is included from. It should be ok unless
+ a void * differs from other pointer formats, or if sizeof(long)
+ is < sizeof (time_t). It would be nice if we could assume the
+ use of rationale standards here. */
+
+ time((void *) &time_value);
+ fprintf (file, "Basic block profiling finished on %s\n", ctime ((void *) &time_value));
+
+ /* We check the length field explicitly in order to allow compatibility
+ with older GCC's which did not provide it. */
+
+ for (ptr = bb_head; ptr != (struct bb *)0; ptr = ptr->next)
+ {
+ int i;
+ int func_p = (ptr->nwords >= sizeof (struct bb) && ptr->nwords <= 1000);
+ int line_p = (func_p && ptr->line_nums);
+ int file_p = (func_p && ptr->filenames);
+ long ncounts = ptr->ncounts;
+ long cnt_max = 0;
+ long line_max = 0;
+ long addr_max = 0;
+ int file_len = 0;
+ int func_len = 0;
+ int blk_len = num_digits (ncounts, 10);
+ int cnt_len;
+ int line_len;
+ int addr_len;
+
+ fprintf (file, "File %s, %ld basic blocks \n\n",
+ ptr->filename, ncounts);
+
+ /* Get max values for each field. */
+ for (i = 0; i < ncounts; i++)
+ {
+ const char *p;
+ int len;
+
+ if (cnt_max < ptr->counts[i])
+ cnt_max = ptr->counts[i];
+
+ if (addr_max < ptr->addresses[i])
+ addr_max = ptr->addresses[i];
+
+ if (line_p && line_max < ptr->line_nums[i])
+ line_max = ptr->line_nums[i];
+
+ if (func_p)
+ {
+ p = (ptr->functions[i]) ? (ptr->functions[i]) : "<none>";
+ len = strlen (p);
+ if (func_len < len)
+ func_len = len;
+ }
+
+ if (file_p)
+ {
+ p = (ptr->filenames[i]) ? (ptr->filenames[i]) : "<none>";
+ len = strlen (p);
+ if (file_len < len)
+ file_len = len;
+ }
+ }
+
+ addr_len = num_digits (addr_max, 16);
+ cnt_len = num_digits (cnt_max, 10);
+ line_len = num_digits (line_max, 10);
+
+ /* Now print out the basic block information. */
+ for (i = 0; i < ncounts; i++)
+ {
+ fprintf (file,
+ " Block #%*d: executed %*ld time(s) address= 0x%.*lx",
+ blk_len, i+1,
+ cnt_len, ptr->counts[i],
+ addr_len, ptr->addresses[i]);
+
+ if (func_p)
+ fprintf (file, " function= %-*s", func_len,
+ (ptr->functions[i]) ? ptr->functions[i] : "<none>");
+
+ if (line_p)
+ fprintf (file, " line= %*ld", line_len, ptr->line_nums[i]);
+
+ if (file_p)
+ fprintf (file, " file= %s",
+ (ptr->filenames[i]) ? ptr->filenames[i] : "<none>");
+
+ fprintf (file, "\n");
+ }
+
+ fprintf (file, "\n");
+ fflush (file);
+ }
+
+ fprintf (file, "\n\n");
+ fclose (file);
+ }
+}
+
+void
+__bb_init_func (struct bb *blocks)
+{
+ /* User is supposed to check whether the first word is non-0,
+ but just in case.... */
+
+ if (blocks->zero_word)
+ return;
+
+#ifdef ON_EXIT
+ /* Initialize destructor. */
+ if (!bb_head)
+ ON_EXIT (__bb_exit_func, 0);
+#endif
+
+ /* Set up linked list. */
+ blocks->zero_word = 1;
+ blocks->next = bb_head;
+ bb_head = blocks;
+}
+
+#endif /* not inhibit_libc */
+#endif /* not BLOCK_PROFILER_CODE */
+#endif /* L_bb */
+
+/* Default free-store management functions for C++, per sections 12.5 and
+ 17.3.3 of the Working Paper. */
+
+#ifdef L_op_new
+/* operator new (size_t), described in 17.3.3.5. This function is used by
+ C++ programs to allocate a block of memory to hold a single object. */
+
+typedef void (*vfp)(void);
+extern vfp __new_handler;
+
+void *
+__builtin_new (size_t sz)
+{
+ void *p;
+
+ /* malloc (0) is unpredictable; avoid it. */
+ if (sz == 0)
+ sz = 1;
+ p = (void *) malloc (sz);
+ while (p == 0)
+ {
+ (*__new_handler) ();
+ p = (void *) malloc (sz);
+ }
+
+ return p;
+}
+#endif /* L_op_new */
+
+#ifdef L_op_vnew
+/* void * operator new [] (size_t), described in 17.3.3.6. This function
+ is used by C++ programs to allocate a block of memory for an array. */
+
+extern void * __builtin_new (size_t);
+
+void *
+__builtin_vec_new (size_t sz)
+{
+ return __builtin_new (sz);
+}
+#endif /* L_op_vnew */
+
+#ifdef L_new_handler
+/* set_new_handler (fvoid_t *) and the default new handler, described in
+ 17.3.3.2 and 17.3.3.5. These functions define the result of a failure
+ to allocate the amount of memory requested from operator new or new []. */
+
+#ifndef inhibit_libc
+/* This gets us __GNU_LIBRARY__. */
+#undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */
+#include <stdio.h>
+
+#ifdef __GNU_LIBRARY__
+ /* Avoid forcing the library's meaning of `write' on the user program
+ by using the "internal" name (for use within the library) */
+#define write(fd, buf, n) __write((fd), (buf), (n))
+#endif
+#endif /* inhibit_libc */
+
+typedef void (*vfp)(void);
+void __default_new_handler (void);
+
+vfp __new_handler = __default_new_handler;
+
+vfp
+set_new_handler (vfp handler)
+{
+ vfp prev_handler;
+
+ prev_handler = __new_handler;
+ if (handler == 0) handler = __default_new_handler;
+ __new_handler = handler;
+ return prev_handler;
+}
+
+#define MESSAGE "Virtual memory exceeded in `new'\n"
+
+void
+__default_new_handler ()
+{
+ /* don't use fprintf (stderr, ...) because it may need to call malloc. */
+ /* This should really print the name of the program, but that is hard to
+ do. We need a standard, clean way to get at the name. */
+ write (2, MESSAGE, sizeof (MESSAGE));
+ /* don't call exit () because that may call global destructors which
+ may cause a loop. */
+ _exit (-1);
+}
+#endif
+
+#ifdef L_op_delete
+/* operator delete (void *), described in 17.3.3.3. This function is used
+ by C++ programs to return to the free store a block of memory allocated
+ as a single object. */
+
+void
+__builtin_delete (void *ptr)
+{
+ if (ptr)
+ free (ptr);
+}
+#endif
+
+#ifdef L_op_vdel
+/* operator delete [] (void *), described in 17.3.3.4. This function is
+ used by C++ programs to return to the free store a block of memory
+ allocated as an array. */
+
+extern void __builtin_delete (void *);
+
+void
+__builtin_vec_delete (void *ptr)
+{
+ __builtin_delete (ptr);
+}
+#endif
+
+/* End of C++ free-store management functions */
+
+#ifdef L_shtab
+unsigned int __shtab[] = {
+ 0x00000001, 0x00000002, 0x00000004, 0x00000008,
+ 0x00000010, 0x00000020, 0x00000040, 0x00000080,
+ 0x00000100, 0x00000200, 0x00000400, 0x00000800,
+ 0x00001000, 0x00002000, 0x00004000, 0x00008000,
+ 0x00010000, 0x00020000, 0x00040000, 0x00080000,
+ 0x00100000, 0x00200000, 0x00400000, 0x00800000,
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000
+ };
+#endif
+
+#ifdef L_clear_cache
+/* Clear part of an instruction cache. */
+
+#define INSN_CACHE_PLANE_SIZE (INSN_CACHE_SIZE / INSN_CACHE_DEPTH)
+
+void
+__clear_cache (beg, end)
+ char *beg, *end;
+{
+#ifdef CLEAR_INSN_CACHE
+ CLEAR_INSN_CACHE (beg, end);
+#else
+#ifdef INSN_CACHE_SIZE
+ static char array[INSN_CACHE_SIZE + INSN_CACHE_PLANE_SIZE + INSN_CACHE_LINE_WIDTH];
+ static int initialized = 0;
+ int offset;
+ void *start_addr
+ void *end_addr;
+ typedef (*function_ptr) ();
+
+#if (INSN_CACHE_SIZE / INSN_CACHE_LINE_WIDTH) < 16
+ /* It's cheaper to clear the whole cache.
+ Put in a series of jump instructions so that calling the beginning
+ of the cache will clear the whole thing. */
+
+ if (! initialized)
+ {
+ int ptr = (((int) array + INSN_CACHE_LINE_WIDTH - 1)
+ & -INSN_CACHE_LINE_WIDTH);
+ int end_ptr = ptr + INSN_CACHE_SIZE;
+
+ while (ptr < end_ptr)
+ {
+ *(INSTRUCTION_TYPE *)ptr
+ = JUMP_AHEAD_INSTRUCTION + INSN_CACHE_LINE_WIDTH;
+ ptr += INSN_CACHE_LINE_WIDTH;
+ }
+ *(INSTRUCTION_TYPE *)(ptr - INSN_CACHE_LINE_WIDTH) = RETURN_INSTRUCTION;
+
+ initialized = 1;
+ }
+
+ /* Call the beginning of the sequence. */
+ (((function_ptr) (((int) array + INSN_CACHE_LINE_WIDTH - 1)
+ & -INSN_CACHE_LINE_WIDTH))
+ ());
+
+#else /* Cache is large. */
+
+ if (! initialized)
+ {
+ int ptr = (((int) array + INSN_CACHE_LINE_WIDTH - 1)
+ & -INSN_CACHE_LINE_WIDTH);
+
+ while (ptr < (int) array + sizeof array)
+ {
+ *(INSTRUCTION_TYPE *)ptr = RETURN_INSTRUCTION;
+ ptr += INSN_CACHE_LINE_WIDTH;
+ }
+
+ initialized = 1;
+ }
+
+ /* Find the location in array that occupies the same cache line as BEG. */
+
+ offset = ((int) beg & -INSN_CACHE_LINE_WIDTH) & (INSN_CACHE_PLANE_SIZE - 1);
+ start_addr = (((int) (array + INSN_CACHE_PLANE_SIZE - 1)
+ & -INSN_CACHE_PLANE_SIZE)
+ + offset);
+
+ /* Compute the cache alignment of the place to stop clearing. */
+#if 0 /* This is not needed for gcc's purposes. */
+ /* If the block to clear is bigger than a cache plane,
+ we clear the entire cache, and OFFSET is already correct. */
+ if (end < beg + INSN_CACHE_PLANE_SIZE)
+#endif
+ offset = (((int) (end + INSN_CACHE_LINE_WIDTH - 1)
+ & -INSN_CACHE_LINE_WIDTH)
+ & (INSN_CACHE_PLANE_SIZE - 1));
+
+#if INSN_CACHE_DEPTH > 1
+ end_addr = (start_addr & -INSN_CACHE_PLANE_SIZE) + offset;
+ if (end_addr <= start_addr)
+ end_addr += INSN_CACHE_PLANE_SIZE;
+
+ for (plane = 0; plane < INSN_CACHE_DEPTH; plane++)
+ {
+ int addr = start_addr + plane * INSN_CACHE_PLANE_SIZE;
+ int stop = end_addr + plane * INSN_CACHE_PLANE_SIZE;
+
+ while (addr != stop)
+ {
+ /* Call the return instruction at ADDR. */
+ ((function_ptr) addr) ();
+
+ addr += INSN_CACHE_LINE_WIDTH;
+ }
+ }
+#else /* just one plane */
+ do
+ {
+ /* Call the return instruction at START_ADDR. */
+ ((function_ptr) start_addr) ();
+
+ start_addr += INSN_CACHE_LINE_WIDTH;
+ }
+ while ((start_addr % INSN_CACHE_SIZE) != offset);
+#endif /* just one plane */
+#endif /* Cache is large */
+#endif /* Cache exists */
+#endif /* CLEAR_INSN_CACHE */
+}
+
+#endif /* L_clear_cache */
+
+#ifdef L_trampoline
+
+/* Jump to a trampoline, loading the static chain address. */
+
+#ifdef TRANSFER_FROM_TRAMPOLINE
+TRANSFER_FROM_TRAMPOLINE
+#endif
+
+#if defined (NeXT) && defined (__MACH__)
+
+/* Make stack executable so we can call trampolines on stack.
+ This is called from INITIALIZE_TRAMPOLINE in next.h. */
+#ifdef NeXTStep21
+ #include <mach.h>
+#else
+ #include <mach/mach.h>
+#endif
+
+void
+__enable_execute_stack (addr)
+ char *addr;
+{
+ kern_return_t r;
+ char *eaddr = addr + TRAMPOLINE_SIZE;
+ vm_address_t a = (vm_address_t) addr;
+
+ /* turn on execute access on stack */
+ r = vm_protect (task_self (), a, TRAMPOLINE_SIZE, FALSE, VM_PROT_ALL);
+ if (r != KERN_SUCCESS)
+ {
+ mach_error("vm_protect VM_PROT_ALL", r);
+ exit(1);
+ }
+
+ /* We inline the i-cache invalidation for speed */
+
+#ifdef CLEAR_INSN_CACHE
+ CLEAR_INSN_CACHE (addr, eaddr);
+#else
+ __clear_cache ((int) addr, (int) eaddr);
+#endif
+}
+
+#endif /* defined (NeXT) && defined (__MACH__) */
+
+#ifdef __convex__
+
+/* Make stack executable so we can call trampolines on stack.
+ This is called from INITIALIZE_TRAMPOLINE in convex.h. */
+
+#include <sys/mman.h>
+#include <sys/vmparam.h>
+#include <machine/machparam.h>
+
+void
+__enable_execute_stack ()
+{
+ int fp;
+ static unsigned lowest = USRSTACK;
+ unsigned current = (unsigned) &fp & -NBPG;
+
+ if (lowest > current)
+ {
+ unsigned len = lowest - current;
+ mremap (current, &len, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE);
+ lowest = current;
+ }
+
+ /* Clear instruction cache in case an old trampoline is in it. */
+ asm ("pich");
+}
+#endif /* __convex__ */
+
+#ifdef __DOLPHIN__
+
+/* Modified from the convex -code above. */
+
+#include <sys/param.h>
+#include <errno.h>
+#include <sys/m88kbcs.h>
+
+void
+__enable_execute_stack ()
+{
+ int save_errno;
+ static unsigned long lowest = USRSTACK;
+ unsigned long current = (unsigned long) &save_errno & -NBPC;
+
+ /* Ignore errno being set. memctl sets errno to EINVAL whenever the
+ address is seen as 'negative'. That is the case with the stack. */
+
+ save_errno=errno;
+ if (lowest > current)
+ {
+ unsigned len=lowest-current;
+ memctl(current,len,MCT_TEXT);
+ lowest = current;
+ }
+ else
+ memctl(current,NBPC,MCT_TEXT);
+ errno=save_errno;
+}
+
+#endif /* __DOLPHIN__ */
+
+#ifdef __pyr__
+
+#undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/vmmac.h>
+
+/* Modified from the convex -code above.
+ mremap promises to clear the i-cache. */
+
+void
+__enable_execute_stack ()
+{
+ int fp;
+ if (mprotect (((unsigned int)&fp/PAGSIZ)*PAGSIZ, PAGSIZ,
+ PROT_READ|PROT_WRITE|PROT_EXEC))
+ {
+ perror ("mprotect in __enable_execute_stack");
+ fflush (stderr);
+ abort ();
+ }
+}
+#endif /* __pyr__ */
+#endif /* L_trampoline */
+
+#ifdef L__main
+
+#include "gbl-ctors.h"
+/* Some systems use __main in a way incompatible with its use in gcc, in these
+ cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to
+ give the same symbol without quotes for an alternative entry point. You
+ must define both, or niether. */
+#ifndef NAME__MAIN
+#define NAME__MAIN "__main"
+#define SYMBOL__MAIN __main
+#endif
+
+/* Run all the global destructors on exit from the program. */
+
+void
+__do_global_dtors ()
+{
+#ifdef DO_GLOBAL_DTORS_BODY
+ DO_GLOBAL_DTORS_BODY;
+#else
+ unsigned nptrs = (unsigned HOST_WIDE_INT) __DTOR_LIST__[0];
+ unsigned i;
+
+ /* Some systems place the number of pointers
+ in the first word of the table.
+ On other systems, that word is -1.
+ In all cases, the table is null-terminated. */
+
+ /* If the length is not recorded, count up to the null. */
+ if (nptrs == -1)
+ for (nptrs = 0; __DTOR_LIST__[nptrs + 1] != 0; nptrs++);
+
+ /* GNU LD format. */
+ for (i = nptrs; i >= 1; i--)
+ __DTOR_LIST__[i] ();
+#endif
+}
+
+#ifndef INIT_SECTION_ASM_OP
+/* Run all the global constructors on entry to the program. */
+
+#ifndef ON_EXIT
+#define ON_EXIT(a, b)
+#else
+/* Make sure the exit routine is pulled in to define the globals as
+ bss symbols, just in case the linker does not automatically pull
+ bss definitions from the library. */
+
+extern int _exit_dummy_decl;
+int *_exit_dummy_ref = &_exit_dummy_decl;
+#endif /* ON_EXIT */
+
+void
+__do_global_ctors ()
+{
+ DO_GLOBAL_CTORS_BODY;
+ ON_EXIT (__do_global_dtors, 0);
+}
+#endif /* no INIT_SECTION_ASM_OP */
+
+#if !defined (INIT_SECTION_ASM_OP) || defined (INVOKE__main)
+/* Subroutine called automatically by `main'.
+ Compiling a global function named `main'
+ produces an automatic call to this function at the beginning.
+
+ For many systems, this routine calls __do_global_ctors.
+ For systems which support a .init section we use the .init section
+ to run __do_global_ctors, so we need not do anything here. */
+
+void
+SYMBOL__MAIN ()
+{
+ /* Support recursive calls to `main': run initializers just once. */
+ static int initialized = 0;
+ if (! initialized)
+ {
+ initialized = 1;
+ __do_global_ctors ();
+ }
+}
+#endif /* no INIT_SECTION_ASM_OP or INVOKE__main */
+
+#endif /* L__main */
+
+#ifdef L_ctors
+
+#include "gbl-ctors.h"
+
+/* Provide default definitions for the lists of constructors and
+ destructors, so that we don't get linker errors. These symbols are
+ intentionally bss symbols, so that gld and/or collect will provide
+ the right values. */
+
+/* We declare the lists here with two elements each,
+ so that they are valid empty lists if no other definition is loaded. */
+#if !defined(INIT_SECTION_ASM_OP) && !defined(CTOR_LISTS_DEFINED_EXTERNALLY)
+#ifdef __NeXT__
+/* After 2.3, try this definition on all systems. */
+func_ptr __CTOR_LIST__[2] = {0, 0};
+func_ptr __DTOR_LIST__[2] = {0, 0};
+#else
+func_ptr __CTOR_LIST__[2];
+func_ptr __DTOR_LIST__[2];
+#endif
+#endif /* no INIT_SECTION_ASM_OP and not CTOR_LISTS_DEFINED_EXTERNALLY */
+#endif /* L_ctors */
+
+#ifdef L_exit
+
+#include "gbl-ctors.h"
+
+#ifndef ON_EXIT
+
+/* If we have no known way of registering our own __do_global_dtors
+ routine so that it will be invoked at program exit time, then we
+ have to define our own exit routine which will get this to happen. */
+
+extern void __do_global_dtors ();
+extern void _cleanup ();
+extern void _exit () __attribute__ ((noreturn));
+
+void
+exit (status)
+ int status;
+{
+ __do_global_dtors ();
+#ifdef EXIT_BODY
+ EXIT_BODY;
+#else
+ _cleanup ();
+#endif
+ _exit (status);
+}
+
+#else
+int _exit_dummy_decl = 0; /* prevent compiler & linker warnings */
+#endif
+
+#endif /* L_exit */
+
+/* In a.out systems, we need to have these dummy constructor and destructor
+ lists in the library.
+
+ When using `collect', the first link will resolve __CTOR_LIST__
+ and __DTOR_LIST__ to these symbols. We will then run "nm" on the
+ result, build the correct __CTOR_LIST__ and __DTOR_LIST__, and relink.
+ Since we don't do the second link if no constructors existed, these
+ dummies must be fully functional empty lists.
+
+ When using `gnu ld', these symbols will be used if there are no
+ constructors. If there are constructors, the N_SETV symbol defined
+ by the linker from the N_SETT's in input files will define __CTOR_LIST__
+ and __DTOR_LIST__ rather than its being allocated as common storage
+ by the definitions below.
+
+ When using a linker that supports constructor and destructor segments,
+ these definitions will not be used, since crtbegin.o and crtend.o
+ (from crtstuff.c) will have already defined __CTOR_LIST__ and
+ __DTOR_LIST__. The crt*.o files are passed directly to the linker
+ on its command line, by gcc. */
+
+/* The list needs two elements: one is ignored (the old count); the
+ second is the terminating zero. Since both values are zero, this
+ declaration is not initialized, and it becomes `common'. */
+
+#ifdef L_ctor_list
+#include "gbl-ctors.h"
+func_ptr __CTOR_LIST__[2];
+#endif
+
+#ifdef L_dtor_list
+#include "gbl-ctors.h"
+func_ptr __DTOR_LIST__[2];
+#endif
diff --git a/gnu/usr.bin/cpio/Makefile b/gnu/usr.bin/cpio/Makefile
new file mode 100644
index 0000000..104fe3c
--- /dev/null
+++ b/gnu/usr.bin/cpio/Makefile
@@ -0,0 +1,9 @@
+PROG= cpio
+CFLAGS+= -I${.CURDIR} -DRETSIGTYPE=void -DHAVE_SYS_MTIO_H=1 -DSTDC_HEADERS=1 -DHAVE_UNISTD_H=1 -DHAVE_STRING_H=1 -DHAVE_FCNTL_H=1 -DHAVE_UTIME_H=1 -DHAVE_STRERROR=1 -DHAVE_VPRINTF=1 -DDIRENT=1
+
+SRCS = copyin.c copyout.c copypass.c defer.c dstring.c fnmatch.c global.c \
+ main.c tar.c util.c error.c filemode.c getopt.c getopt1.c version.c \
+ rtapelib.c dirname.c idcache.c makepath.c xmalloc.c stripslash.c \
+ userspec.c xstrdup.c
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/cpio/cpio.1 b/gnu/usr.bin/cpio/cpio.1
index 7ef74c4..e2aeefd 100644
--- a/gnu/usr.bin/cpio/cpio.1
+++ b/gnu/usr.bin/cpio/cpio.1
@@ -91,7 +91,7 @@ and verifies when the file is extracted from the archive.
The HPUX formats are provided for compatibility with HPUX's cpio which
stores device files differently.
.PP
-The tar format is provided for compatability with
+The tar format is provided for compatibility with
the
.B tar
program. It can not be used to archive files with names
@@ -229,7 +229,9 @@ Ignored; for compatibility with other versions of
.BR cpio .
.TP
.I "\-l, \-\-link"
-Link files instead of copying them, when possible.
+Link files instead of copying them, when possible (usable only with the
+.I \-p
+option).
.TP
.I "\-L, \-\-dereference"
Dereference symbolic links (copy the files that they point to instead
diff --git a/gnu/usr.bin/cpio/fnmatch.c b/gnu/usr.bin/cpio/fnmatch.c
new file mode 100644
index 0000000..8a25a90
--- /dev/null
+++ b/gnu/usr.bin/cpio/fnmatch.c
@@ -0,0 +1,200 @@
+/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <fnmatch.h>
+#include <ctype.h>
+
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+#if !defined(__GNU_LIBRARY__) && !defined(STDC_HEADERS)
+extern int errno;
+#endif
+
+/* Match STRING against the filename pattern PATTERN, returning zero if
+ it matches, nonzero if not. */
+int
+fnmatch (pattern, string, flags)
+ const char *pattern;
+ const char *string;
+ int flags;
+{
+ register const char *p = pattern, *n = string;
+ register char c;
+
+/* Note that this evalutes C many times. */
+#define FOLD(c) ((flags & FNM_CASEFOLD) && isupper (c) ? tolower (c) : (c))
+
+ while ((c = *p++) != '\0')
+ {
+ c = FOLD (c);
+
+ switch (c)
+ {
+ case '?':
+ if (*n == '\0')
+ return FNM_NOMATCH;
+ else if ((flags & FNM_FILE_NAME) && *n == '/')
+ return FNM_NOMATCH;
+ else if ((flags & FNM_PERIOD) && *n == '.' &&
+ (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
+ return FNM_NOMATCH;
+ break;
+
+ case '\\':
+ if (!(flags & FNM_NOESCAPE))
+ {
+ c = *p++;
+ c = FOLD (c);
+ }
+ if (FOLD (*n) != c)
+ return FNM_NOMATCH;
+ break;
+
+ case '*':
+ if ((flags & FNM_PERIOD) && *n == '.' &&
+ (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
+ return FNM_NOMATCH;
+
+ for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
+ if (((flags & FNM_FILE_NAME) && *n == '/') ||
+ (c == '?' && *n == '\0'))
+ return FNM_NOMATCH;
+
+ if (c == '\0')
+ return 0;
+
+ {
+ char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
+ c1 = FOLD (c1);
+ for (--p; *n != '\0'; ++n)
+ if ((c == '[' || FOLD (*n) == c1) &&
+ fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
+ return 0;
+ return FNM_NOMATCH;
+ }
+
+ case '[':
+ {
+ /* Nonzero if the sense of the character class is inverted. */
+ register int not;
+
+ if (*n == '\0')
+ return FNM_NOMATCH;
+
+ if ((flags & FNM_PERIOD) && *n == '.' &&
+ (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
+ return FNM_NOMATCH;
+
+ not = (*p == '!' || *p == '^');
+ if (not)
+ ++p;
+
+ c = *p++;
+ for (;;)
+ {
+ register char cstart = c, cend = c;
+
+ if (!(flags & FNM_NOESCAPE) && c == '\\')
+ cstart = cend = *p++;
+
+ cstart = cend = FOLD (cstart);
+
+ if (c == '\0')
+ /* [ (unterminated) loses. */
+ return FNM_NOMATCH;
+
+ c = *p++;
+ c = FOLD (c);
+
+ if ((flags & FNM_FILE_NAME) && c == '/')
+ /* [/] can never match. */
+ return FNM_NOMATCH;
+
+ if (c == '-' && *p != ']')
+ {
+ cend = *p++;
+ if (!(flags & FNM_NOESCAPE) && cend == '\\')
+ cend = *p++;
+ if (cend == '\0')
+ return FNM_NOMATCH;
+ cend = FOLD (cend);
+
+ c = *p++;
+ }
+
+ if (FOLD (*n) >= cstart && FOLD (*n) <= cend)
+ goto matched;
+
+ if (c == ']')
+ break;
+ }
+ if (!not)
+ return FNM_NOMATCH;
+ break;
+
+ matched:;
+ /* Skip the rest of the [...] that already matched. */
+ while (c != ']')
+ {
+ if (c == '\0')
+ /* [... (unterminated) loses. */
+ return FNM_NOMATCH;
+
+ c = *p++;
+ if (!(flags & FNM_NOESCAPE) && c == '\\')
+ /* XXX 1003.2d11 is unclear if this is right. */
+ ++p;
+ }
+ if (not)
+ return FNM_NOMATCH;
+ }
+ break;
+
+ default:
+ if (c != FOLD (*n))
+ return FNM_NOMATCH;
+ }
+
+ ++n;
+ }
+
+ if (*n == '\0')
+ return 0;
+
+ if ((flags & FNM_LEADING_DIR) && *n == '/')
+ /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */
+ return 0;
+
+ return FNM_NOMATCH;
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
diff --git a/gnu/usr.bin/cpio/fnmatch.h b/gnu/usr.bin/cpio/fnmatch.h
new file mode 100644
index 0000000..1157bf0
--- /dev/null
+++ b/gnu/usr.bin/cpio/fnmatch.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#ifndef _FNMATCH_H
+
+#define _FNMATCH_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined (__cplusplus) || (defined (__STDC__) && __STDC__)
+#undef __GNU_P
+#define __GNU_P(args) args
+#else /* Not C++ or ANSI C. */
+#undef __GNU_P
+#define __GNU_P(args) ()
+/* We can get away without defining `const' here only because in this file
+ it is used only inside the prototype for `fnmatch', which is elided in
+ non-ANSI C where `const' is problematical. */
+#endif /* C++ or ANSI C. */
+
+/* Bits set in the FLAGS argument to `fnmatch'. */
+#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */
+#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */
+#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */
+
+#if !defined (_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined (_GNU_SOURCE)
+#define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */
+#define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */
+#define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */
+#endif
+
+/* Value returned by `fnmatch' if STRING does not match PATTERN. */
+#define FNM_NOMATCH 1
+
+/* Match STRING against the filename pattern PATTERN,
+ returning zero if it matches, FNM_NOMATCH if not. */
+extern int fnmatch __GNU_P ((const char *__pattern, const char *__string,
+ int __flags));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* fnmatch.h */
diff --git a/gnu/usr.bin/cpio/mt.1 b/gnu/usr.bin/cpio/mt.1
new file mode 100644
index 0000000..fc9cb64
--- /dev/null
+++ b/gnu/usr.bin/cpio/mt.1
@@ -0,0 +1,107 @@
+.TH MT 1L \" -*- nroff -*-
+.SH NAME
+mt \- control magnetic tape drive operation
+.SH SYNOPSIS
+.B mt
+[\-V] [\-f device] [\-\-file=device] [\-\-version]
+operation [count]
+.SH DESCRIPTION
+This manual page
+documents the GNU version of
+.BR mt .
+.B mt
+performs the given
+.IR operation ,
+which must be one of the tape operations listed below, on a tape
+drive.
+.PP
+The default tape device to operate on is taken from the file
+.I /usr/include/sys/mtio.h
+when
+.B mt
+is compiled. It can be overridden by giving a device file name in
+the environment variable
+.BR TAPE
+or by a command line option (see below), which also overrides the
+environment variable.
+.PP
+The device must be either a character special file or a
+remote tape drive. To use a tape drive on another machine as the
+archive, use a filename that starts with `HOSTNAME:'. The
+hostname can be preceded by a username and an `@' to access the remote
+tape drive as that user, if you have permission to do so (typically an
+entry in that user's `~/.rhosts' file).
+.PP
+The available operations are listed below. Unique abbreviations are
+accepted. Not all operations are available on all systems, or work on
+all types of tape drives.
+Some operations optionally take a repeat count, which can be given
+after the operation name and defaults to 1.
+.IP "eof, weof"
+Write
+.I count
+EOF marks at current position.
+.IP fsf
+Forward space
+.I count
+files.
+The tape is positioned on the first block of the next file.
+.IP bsf
+Backward space
+.I count
+files.
+The tape is positioned on the first block of the next file.
+.IP fsr
+Forward space
+.I count
+records.
+.IP bsr
+Backward space
+.I count
+records.
+.IP bsfm
+Backward space
+.I count
+file marks.
+The tape is positioned on the beginning-of-the-tape side of
+the file mark.
+.IP asf
+Absolute space to file number
+.IR count .
+Equivalent to rewind followed by fsf
+.IR count .
+.IP eom
+Space to the end of the recorded media on the tape
+(for appending files onto tapes).
+.IP rewind
+Rewind the tape.
+.IP "offline, rewoffl"
+Rewind the tape and, if applicable, unload the tape.
+.IP status
+Print status information about the tape unit.
+.IP retension
+Rewind the tape, then wind it to the end of the reel,
+then rewind it again.
+.IP erase
+Erase the tape.
+.PP
+.B mt
+exits with a status of 0 if the operation succeeded, 1 if the
+operation or device name given was invalid, or 2 if the operation
+failed.
+.SS OPTIONS
+.TP
+.I "\-f, \-\-file=device"
+Use
+.I device
+as the file name of the tape drive to operate on.
+To use a
+tape drive on another machine, use a filename that
+starts with `HOSTNAME:'. The hostname can be preceded by a
+username and an `@' to access the remote tape drive as that user, if
+you have permission to do so (typically an entry in that user's
+`~/.rhosts' file).
+.TP
+.I "\-V, \-\-version"
+Print the version number of
+.BR mt .
diff --git a/gnu/usr.bin/cpio/rmt.c b/gnu/usr.bin/cpio/rmt.c
new file mode 100644
index 0000000..442a831
--- /dev/null
+++ b/gnu/usr.bin/cpio/rmt.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+/*
+ * rmt
+ */
+#include <stdio.h>
+#include <sgtty.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#ifdef HAVE_SYS_GENTAPE_H /* e.g., ISC UNIX */
+#include <sys/gentape.h>
+#else
+#include <sys/mtio.h>
+#endif
+#include <errno.h>
+
+#if defined (_I386) && defined (_AIX)
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#else
+long lseek ();
+#endif
+
+#ifdef STDC_HEADERS
+#include <string.h>
+#include <stdlib.h>
+#else
+extern char *malloc ();
+#endif
+
+int tape = -1;
+
+char *record;
+int maxrecsize = -1;
+char *checkbuf ();
+void getstring ();
+void error ();
+
+#define SSIZE 64
+char device[SSIZE];
+char count[SSIZE], mode[SSIZE], pos[SSIZE], op[SSIZE];
+
+extern errno;
+extern char *sys_errlist[];
+char resp[BUFSIZ];
+
+FILE *debug;
+#define DEBUG(f) if (debug) fprintf(debug, f)
+#define DEBUG1(f,a) if (debug) fprintf(debug, f, a)
+#define DEBUG2(f,a1,a2) if (debug) fprintf(debug, f, a1, a2)
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int rval;
+ char c;
+ int n, i, cc;
+
+ argc--, argv++;
+ if (argc > 0)
+ {
+ debug = fopen (*argv, "w");
+ if (debug == 0)
+ exit (1);
+ (void) setbuf (debug, (char *) 0);
+ }
+top:
+ errno = 0;
+ rval = 0;
+ if (read (0, &c, 1) != 1)
+ exit (0);
+ switch (c)
+ {
+
+ case 'O':
+ if (tape >= 0)
+ (void) close (tape);
+ getstring (device);
+ getstring (mode);
+ DEBUG2 ("rmtd: O %s %s\n", device, mode);
+#if defined (i386) && defined (AIX)
+ /* This is alleged to fix a byte ordering problem. */
+ /* I'm quite suspicious if it's right. -- mib */
+ {
+ int oflag = atoi (mode);
+ int nflag = 0;
+ if ((oflag & 3) == 0)
+ nflag |= O_RDONLY;
+ if (oflag & 1)
+ nflag |= O_WRONLY;
+ if (oflag & 2)
+ nflag |= O_RDWR;
+ if (oflag & 0x0008)
+ nflag |= O_APPEND;
+ if (oflag & 0x0200)
+ nflag |= O_CREAT;
+ if (oflag & 0x0400)
+ nflag |= O_TRUNC;
+ if (oflag & 0x0800)
+ nflag |= O_EXCL;
+ tape = open (device, nflag, 0666);
+ }
+#else
+ tape = open (device, atoi (mode), 0666);
+#endif
+ if (tape < 0)
+ goto ioerror;
+ goto respond;
+
+ case 'C':
+ DEBUG ("rmtd: C\n");
+ getstring (device); /* discard */
+ if (close (tape) < 0)
+ goto ioerror;
+ tape = -1;
+ goto respond;
+
+ case 'L':
+ getstring (count);
+ getstring (pos);
+ DEBUG2 ("rmtd: L %s %s\n", count, pos);
+ rval = lseek (tape, (long) atoi (count), atoi (pos));
+ if (rval < 0)
+ goto ioerror;
+ goto respond;
+
+ case 'W':
+ getstring (count);
+ n = atoi (count);
+ DEBUG1 ("rmtd: W %s\n", count);
+ record = checkbuf (record, n);
+ for (i = 0; i < n; i += cc)
+ {
+ cc = read (0, &record[i], n - i);
+ if (cc <= 0)
+ {
+ DEBUG ("rmtd: premature eof\n");
+ exit (2);
+ }
+ }
+ rval = write (tape, record, n);
+ if (rval < 0)
+ goto ioerror;
+ goto respond;
+
+ case 'R':
+ getstring (count);
+ DEBUG1 ("rmtd: R %s\n", count);
+ n = atoi (count);
+ record = checkbuf (record, n);
+ rval = read (tape, record, n);
+ if (rval < 0)
+ goto ioerror;
+ (void) sprintf (resp, "A%d\n", rval);
+ (void) write (1, resp, strlen (resp));
+ (void) write (1, record, rval);
+ goto top;
+
+ case 'I':
+ getstring (op);
+ getstring (count);
+ DEBUG2 ("rmtd: I %s %s\n", op, count);
+#ifdef MTIOCTOP
+ {
+ struct mtop mtop;
+ mtop.mt_op = atoi (op);
+ mtop.mt_count = atoi (count);
+ if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0)
+ goto ioerror;
+ rval = mtop.mt_count;
+ }
+#endif
+ goto respond;
+
+ case 'S': /* status */
+ DEBUG ("rmtd: S\n");
+ {
+#ifdef MTIOCGET
+ struct mtget mtget;
+ if (ioctl (tape, MTIOCGET, (char *) &mtget) < 0)
+ goto ioerror;
+ rval = sizeof (mtget);
+ (void) sprintf (resp, "A%d\n", rval);
+ (void) write (1, resp, strlen (resp));
+ (void) write (1, (char *) &mtget, sizeof (mtget));
+#endif
+ goto top;
+ }
+
+ default:
+ DEBUG1 ("rmtd: garbage command %c\n", c);
+ exit (3);
+ }
+respond:
+ DEBUG1 ("rmtd: A %d\n", rval);
+ (void) sprintf (resp, "A%d\n", rval);
+ (void) write (1, resp, strlen (resp));
+ goto top;
+ioerror:
+ error (errno);
+ goto top;
+}
+
+void
+getstring (bp)
+ char *bp;
+{
+ int i;
+ char *cp = bp;
+
+ for (i = 0; i < SSIZE; i++)
+ {
+ if (read (0, cp + i, 1) != 1)
+ exit (0);
+ if (cp[i] == '\n')
+ break;
+ }
+ cp[i] = '\0';
+}
+
+char *
+checkbuf (record, size)
+ char *record;
+ int size;
+{
+ if (size <= maxrecsize)
+ return (record);
+ if (record != 0)
+ free (record);
+ record = malloc (size);
+ if (record == 0)
+ {
+ DEBUG ("rmtd: cannot allocate buffer space\n");
+ exit (4);
+ }
+ maxrecsize = size;
+#ifdef SO_RCVBUF
+ while (size > 1024 &&
+ setsockopt (0, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof (size)) < 0)
+ size -= 1024;
+#else
+ size = 1 + ((size - 1) % 1024);
+#endif
+ return (record);
+}
+
+void
+error (num)
+ int num;
+{
+
+ DEBUG2 ("rmtd: E %d (%s)\n", num, sys_errlist[num]);
+ (void) sprintf (resp, "E%d\n%s\n", num, sys_errlist[num]);
+ (void) write (1, resp, strlen (resp));
+}
diff --git a/gnu/usr.bin/cvs/Makefile b/gnu/usr.bin/cvs/Makefile
new file mode 100644
index 0000000..d75858b
--- /dev/null
+++ b/gnu/usr.bin/cvs/Makefile
@@ -0,0 +1,3 @@
+SUBDIR = lib cvs mkmodules
+
+.include <bsd.subdir.mk>
diff --git a/gnu/usr.bin/cvs/Makefile.inc b/gnu/usr.bin/cvs/Makefile.inc
new file mode 100644
index 0000000..9c26b86
--- /dev/null
+++ b/gnu/usr.bin/cvs/Makefile.inc
@@ -0,0 +1,8 @@
+.if exists(${.CURDIR}/../lib/obj)
+LIBDESTDIR= ${.CURDIR}/../lib/obj
+.else
+LIBDESTDIR= ${.CURDIR}/../lib
+.endif
+
+LDDESTDIR= -L${LIBDESTDIR}
+LIBCVS= ${LIBDESTDIR}/libcvs.a
diff --git a/gnu/usr.bin/cvs/contrib/README b/gnu/usr.bin/cvs/contrib/README
new file mode 100644
index 0000000..412be26
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/README
@@ -0,0 +1,68 @@
+@(#)README 1.8 92/04/10
+
+This "contrib" directory is a place holder for code/scripts sent to
+me by contributors around the world. This READM file will be kept
+up-to-date from release to release. BUT, I must point out that these
+contributions are really, REALLY UNSSUPPORTED. In fact, I probably
+don't even know what they do. Nor do I guarantee to have tried them,
+or ported them to work with this CVS distribution. If you have questions,
+you might contact the author, but you should not necessarily expect
+a reply. USE AT YOUR OWN RISK -- and all that stuff.
+
+Contents of this directory:
+
+ README This file.
+ log.pl A perl script suitable for including in your
+ $CVSROOT/CVSROOT/loginfo file for logging commit
+ changes. Includes the RCS revision of the change
+ as part of the log.
+ Contributed by Kevin Samborn <samborn@sunrise.com>.
+ pcl-cvs A directory that contains GNU Emacs lisp code which
+ implements a CVS-mode for emacs.
+ Contributed by Per Cederqvist <ceder@lysator.liu.se>.
+ commit_prep.pl A perl script, to be combined with log_accum.pl, to
+ log_accum.pl provide for a way to combine the individual log
+ messages of a multi-directory "commit" into a
+ single log message, and mail the result somewhere.
+ Also does other checks for $Id and that you are
+ committing the correct revision of the file.
+ Read the comments carefully.
+ Contributed by David Hampton <hampton@cisco.com>.
+ mfpipe.pl Another perl script for logging. Allows you to
+ pipe the log message to a file and/or send mail
+ to some alias.
+ Contributed by John Clyne <clyne@niwot.scd.ucar.edu>.
+ rcs-to-cvs Script to import sources that may have been under
+ RCS control already.
+ Contributed by Per Cederqvist <ceder@lysator.liu.se>.
+ cvscheck Identifies files added, changed, or removed in a
+ cvscheck.man checked out CVS tree; also notices unknown files.
+ Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net>
+ cvshelp.man An introductory manual page written by Lowell Skoog
+ <fluke!lowell@uunet.uu.net>. It is most likely
+ out-of-date relative to CVS 1.3, but still may be
+ useful.
+ dirfns A shar file which contains some code that might
+ help your system support opendir/readdir/closedir,
+ if it does not already.
+ Copied from the C-News distribution.
+ rcslock.pl A perl script that can be added to your commitinfo
+ file that tries to determine if your RCS file is
+ currently locked by someone else, as might be the
+ case for a binary file.
+ Contributed by John Rouillard <rouilj@cs.umb.edu>.
+ cvs_acls.pl A perl script that implements Access Control Lists
+ by using the "commitinfo" hook provided with the
+ "cvs commit" command.
+ Contributed by David G. Grubbs <dgg@ksr.com>.
+ descend A shell script that can be used to recursively
+ descend.man descend through a directory. In CVS 1.2, this was
+ very useful, since many of the commands were not
+ recursive. In CVS 1.3 (and later), however, most of
+ the commands are recursive. However, this may still
+ come in handy.
+ Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net>
+ cln_hist.pl A perl script to compress your
+ $CVSROOT/CVSROOT/history file, as it can grow quite
+ large after extended use.
+ Contributed by David G. Grubbs <dgg@ksr.com>
diff --git a/gnu/usr.bin/cvs/contrib/cln_hist.pl b/gnu/usr.bin/cvs/contrib/cln_hist.pl
new file mode 100644
index 0000000..b353edc
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/cln_hist.pl
@@ -0,0 +1,91 @@
+#!/usr/bin/perl -- # -*-Perl-*-
+#
+# cln_hist.pl,v 1.1 1992/04/10 03:04:15 berliner Exp
+# Contributed by David G. Grubbs <dgg@ksr.com>
+#
+# Clean up the history file. 10 Record types: MAR OFT WUCG
+#
+# WUCG records are thrown out.
+# MAR records are retained.
+# T records: retain only last tag with same combined tag/module.
+#
+# Two passes: Walk through the first time and remember the
+# 1. Last Tag record with same "tag" and "module" names.
+# 2. Last O record with unique user/module/directory, unless followed
+# by a matching F record.
+#
+
+$r = $ENV{"CVSROOT"};
+$c = "$r/CVSROOT";
+$h = "$c/history";
+
+eval "print STDERR \$die='Unknown parameter $1\n' if !defined \$$1; \$$1=\$';"
+ while ($ARGV[0] =~ /^(\w+)=/ && shift(@ARGV));
+exit 255 if $die; # process any variable=value switches
+
+%tags = ();
+%outs = ();
+
+#
+# Move history file to safe place and re-initialize a new one.
+#
+rename($h, "$h.bak");
+open(XX, ">$h");
+close(XX);
+
+#
+# Pass1 -- remember last tag and checkout.
+#
+open(HIST, "$h.bak");
+while (<HIST>) {
+ next if /^[MARWUCG]/;
+
+ # Save whole line keyed by tag|module
+ if (/^T/) {
+ @tmp = split(/\|/, $_);
+ $tags{$tmp[4] . '|' . $tmp[5]} = $_;
+ }
+ # Save whole line
+ if (/^[OF]/) {
+ @tmp = split(/\|/, $_);
+ $outs{$tmp[1] . '|' . $tmp[2] . '|' . $tmp[5]} = $_;
+ }
+}
+
+#
+# Pass2 -- print out what we want to save.
+#
+open(SAVE, ">$h.work");
+open(HIST, "$h.bak");
+while (<HIST>) {
+ next if /^[FWUCG]/;
+
+ # If whole line matches saved (i.e. "last") one, print it.
+ if (/^T/) {
+ @tmp = split(/\|/, $_);
+ next if $tags{$tmp[4] . '|' . $tmp[5]} ne $_;
+ }
+ # Save whole line
+ if (/^O/) {
+ @tmp = split(/\|/, $_);
+ next if $outs{$tmp[1] . '|' . $tmp[2] . '|' . $tmp[5]} ne $_;
+ }
+
+ print SAVE $_;
+}
+
+#
+# Put back the saved stuff
+#
+system "cat $h >> $h.work";
+
+if (-s $h) {
+ rename ($h, "$h.interim");
+ print "history.interim has non-zero size.\n";
+} else {
+ unlink($h);
+}
+
+rename ("$h.work", $h);
+
+exit(0);
diff --git a/gnu/usr.bin/cvs/contrib/commit_prep.pl b/gnu/usr.bin/cvs/contrib/commit_prep.pl
new file mode 100644
index 0000000..b3f7e9a
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/commit_prep.pl
@@ -0,0 +1,168 @@
+#!/usr/local/bin/perl -w
+#
+#
+# Perl filter to handle pre-commit checking of files. This program
+# records the last directory where commits will be taking place for
+# use by the log_accumulate script. For new file, it forcing the
+# existence of a RCS "Id" keyword in the first ten lines of the file.
+# For existing files, it checks version number in the "Id" line to
+# prevent losing changes because an old version of a file was copied
+# into the direcory.
+#
+# Possible future enhancements:
+#
+#
+# Check for cruft left by unresolved conflicts. Search for
+# "^<<<<<<<$", "^-------$", and "^>>>>>>>$".
+#
+# Look for a copyright and automagically update it to the
+# current year.
+#
+# Contributed by David Hampton <hampton@cisco.com>
+#
+
+############################################################
+#
+# Configurable options
+#
+############################################################
+#
+# Check each file (except dot files) for an RCS "Id" keyword.
+#
+$check_id = 1;
+
+#
+# Record the directory for later use by the log_accumulate stript.
+#
+$record_directory = 1;
+
+############################################################
+#
+# Constants
+#
+############################################################
+$LAST_FILE = "/tmp/#cvs.lastdir";
+$ENTRIES = "CVS/Entries";
+
+$NoId = "
+%s - Does not contain a line with the keyword \"Id:\".
+ Please see the template files for an example.\n";
+
+# Protect string from substitution by RCS.
+$NoName = "
+%s - The ID line should contain only \"\$\I\d\:\ \$\" for a newly created file.\n";
+
+$BadName = "
+%s - The file name '%s' in the ID line does not match
+ the actual filename.\n";
+
+$BadVersion = "
+%s - How dare you!! You replaced your copy of the file '%s',
+ which was based upon version %s, with an %s version based
+ upon %s. Please move your '%s' out of the way, perform an
+ update to get the current version, and them merge your changes
+ into that file.\n";
+
+############################################################
+#
+# Subroutines
+#
+############################################################
+
+sub write_line {
+ local($filename, $line) = @_;
+ open(FILE, ">$filename") || die("Cannot open $filename, stopped");
+ print(FILE $line, "\n");
+ close(FILE);
+}
+
+sub check_version {
+ local($i, $id, $rname, $version);
+ local($filename, $cvsversion) = @_;
+
+ open(FILE, $filename) || die("Cannot open $filename, stopped");
+ for ($i = 1; $i < 10; $i++) {
+ $pos = -1;
+ last if eof(FILE);
+ $line = <FILE>;
+ $pos = index($line, "Id: ");
+ last if ($pos >= 0);
+ }
+
+ if ($pos == -1) {
+ printf($NoId, $filename);
+ return(1);
+ }
+
+ ($id, $rname, $version) = split(' ', substr($line, $pos));
+ if ($cvsversion{$filename} == 0) {
+ if ($rname ne "\$") {
+ printf($NoName, $filename);
+ return(1);
+ }
+ return(0);
+ }
+
+ if ($rname ne "$filename,v") {
+ printf($BadName, $filename, substr($rname, 0, length($rname)-2));
+ return(1);
+ }
+ if ($cvsversion{$filename} < $version) {
+ printf($BadVersion, $filename, $filename, $cvsversion{$filename},
+ "newer", $version, $filename);
+ return(1);
+ }
+ if ($cvsversion{$filename} > $version) {
+ printf($BadVersion, $filename, $filename, $cvsversion{$filename},
+ "older", $version, $filename);
+ return(1);
+ }
+ return(0);
+}
+
+#############################################################
+#
+# Main Body
+#
+############################################################
+
+$id = getpgrp();
+#print("ARGV - ", join(":", @ARGV), "\n");
+#print("id - ", id, "\n");
+
+#
+# Suck in the Entries file
+#
+open(ENTRIES, $ENTRIES) || die("Cannot open $ENTRIES.\n");
+while (<ENTRIES>) {
+ local($filename, $version) = split('/', substr($_, 1));
+ $cvsversion{$filename} = $version;
+}
+
+#
+# Now check each file name passed in, except for dot files. Dot files
+# are considered to be administrative files by this script.
+#
+if ($check_id != 0) {
+ $failed = 0;
+ $directory = $ARGV[0];
+ shift @ARGV;
+ foreach $arg (@ARGV) {
+ next if (index($arg, ".") == 0);
+ $failed += &check_version($arg);
+ }
+ if ($failed) {
+ print "\n";
+ exit(1);
+ }
+}
+
+#
+# Record this directory as the last one checked. This will be used
+# by the log_accumulate script to determine when it is processing
+# the final directory of a multi-directory commit.
+#
+if ($record_directory != 0) {
+ &write_line("$LAST_FILE.$id", $directory);
+}
+exit(0);
diff --git a/gnu/usr.bin/cvs/contrib/cvs_acls.pl b/gnu/usr.bin/cvs/contrib/cvs_acls.pl
new file mode 100644
index 0000000..1a0096a
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/cvs_acls.pl
@@ -0,0 +1,142 @@
+#!/usr/bin/perl -- # -*-Perl-*-
+#
+# cvs_acls.pl,v 1.2 1992/04/11 16:01:24 berliner Exp
+#
+# Access control lists for CVS. dgg@ksr.com (David G. Grubbs)
+#
+# CVS "commitinfo" for matching repository names, running the program it finds
+# on the same line. More information is available in the CVS man pages.
+#
+# ==== INSTALLATION:
+#
+# To use this program as I intended, do the following four things:
+#
+# 0. Install PERL. :-)
+#
+# 1. Put one line, as the *only* non-comment line, in your commitinfo file:
+#
+# DEFAULT /usr/local/bin/cvs_acls
+#
+# 2. Install this file as /usr/local/bin/cvs_acls and make it executable.
+#
+# 3. Create a file named $CVSROOT/CVSROOT/avail.
+#
+# ==== FORMAT OF THE avail FILE:
+#
+# The avail file determines whether you may commit files. It contains lines
+# read from top to bottom, keeping track of a single "bit". The "bit"
+# defaults to "on". It can be turned "off" by "unavail" lines and "on" by
+# "avail" lines. ==> Last one counts.
+#
+# Any line not beginning with "avail" or "unavail" is ignored.
+#
+# Lines beginning with "avail" or "unavail" are assumed to be '|'-separated
+# triples: (All spaces and tabs are ignored in a line.)
+#
+# {avail.*,unavail.*} [| user,user,... [| repos,repos,...]]
+#
+# 1. String starting with "avail" or "unavail".
+# 2. Optional, comma-separated list of usernames.
+# 3. Optional, comma-separated list of repository pathnames.
+# These are pathnames relative to $CVSROOT. They can be directories or
+# filenames. A directory name allows access to all files and
+# directories below it.
+#
+# Example: (Text from the ';;' rightward may not appear in the file.)
+#
+# unavail ;; Make whole repository unavailable.
+# avail|dgg ;; Except for user "dgg".
+# avail|fred, john|bin/ls ;; Except when "fred" or "john" commit to
+# ;; the module whose repository is "bin/ls"
+#
+# PROGRAM LOGIC:
+#
+# CVS passes to @ARGV an absolute directory pathname (the repository
+# appended to your $CVSROOT variable), followed by a list of filenames
+# within that directory.
+#
+# We walk through the avail file looking for a line that matches both
+# the username and repository.
+#
+# A username match is simply the user's name appearing in the second
+# column of the avail line in a space-or-comma separate list.
+#
+# A repository match is either:
+# - One element of the third column matches $ARGV[0], or some
+# parent directory of $ARGV[0].
+# - Otherwise *all* file arguments ($ARGV[1..$#ARGV]) must be
+# in the file list in one avail line.
+# - In other words, using directory names in the third column of
+# the avail file allows committing of any file (or group of
+# files in a single commit) in the tree below that directory.
+# - If individual file names are used in the third column of
+# the avail file, then files must be committed individually or
+# all files specified in a single commit must all appear in
+# third column of a single avail line.
+#
+
+$debug = 0;
+$cvsroot = $ENV{'CVSROOT'};
+$availfile = $cvsroot . "/CVSROOT/avail";
+$myname = $ENV{"USER"} if !($myname = $ENV{"LOGNAME"});
+
+eval "print STDERR \$die='Unknown parameter $1\n' if !defined \$$1; \$$1=\$';"
+ while ($ARGV[0] =~ /^(\w+)=/ && shift(@ARGV));
+exit 255 if $die; # process any variable=value switches
+
+die "Must set CVSROOT\n" if !$cvsroot;
+($repos = shift) =~ s:^$cvsroot/::;
+grep($_ = $repos . '/' . $_, @ARGV);
+
+print "$$ Repos: $repos\n","$$ ==== ",join("\n$$ ==== ",@ARGV),"\n" if $debug;
+
+$exit_val = 0; # Good Exit value
+
+$universal_off = 0;
+open (AVAIL, $availfile) || exit(0); # It is ok for avail file not to exist
+while (<AVAIL>) {
+ chop;
+ next if /^\s*\#/;
+ next if /^\s*$/;
+ ($flagstr, $u, $m) = split(/[\s,]*\|[\s,]*/, $_);
+
+ # Skip anything not starting with "avail" or "unavail" and complain.
+ (print "Bad avail line: $_\n"), next
+ if ($flagstr !~ /^avail/ && $flagstr !~ /^unavail/);
+
+ # Set which bit we are playing with. ('0' is OK == Available).
+ $flag = (($& eq "avail") ? 0 : 1);
+
+ # If we find a "universal off" flag (i.e. a simple "unavail") remember it
+ $universal_off = 1 if ($flag && !$u && !$m);
+
+ # $myname considered "in user list" if actually in list or is NULL
+ $in_user = (!$u || grep ($_ eq $myname, split(/[\s,]+/,$u)));
+ print "$$ \$myname($myname) in user list: $_\n" if $debug && $in_user;
+
+ # Module matches if it is a NULL module list in the avail line. If module
+ # list is not null, we check every argument combination.
+ if (!($in_repo = !$m)) {
+ @tmp = split(/[\s,]+/,$m);
+ for $j (@tmp) {
+ # If the repos from avail is a parent(or equal) dir of $repos, OK
+ $in_repo = 1, last if ($repos eq $j || $repos =~ /^$j\//);
+ }
+ if (!$in_repo) {
+ $in_repo = 1;
+ for $j (@ARGV) {
+ last if !($in_repo = grep ($_ eq $j, @tmp));
+ }
+ }
+ }
+ print "$$ \$repos($repos) in repository list: $_\n" if $debug && $in_repo;
+
+ $exit_val = $flag if ($in_user && $in_repo);
+ print "$$ ==== \$exit_val = $exit_val\n$$ ==== \$flag = $flag\n" if $debug;
+}
+close(AVAIL);
+print "$$ ==== \$exit_val = $exit_val\n" if $debug;
+print "**** Access denied: Insufficient Karma ($myname|$repos)\n" if $exit_val;
+print "**** Access allowed: Personal Karma exceeds Environmental Karma.\n"
+ if $universal_off && !$exit_val;
+exit($exit_val);
diff --git a/gnu/usr.bin/cvs/contrib/cvscheck b/gnu/usr.bin/cvs/contrib/cvscheck
new file mode 100644
index 0000000..67fdbbd
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/cvscheck
@@ -0,0 +1,84 @@
+#! /bin/sh
+# cvscheck,v 1.2 1992/04/10 03:04:19 berliner Exp
+#
+# cvscheck - identify files added, changed, or removed
+# in CVS working directory
+#
+# Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net>
+#
+# This program should be run in a working directory that has been
+# checked out using CVS. It identifies files that have been added,
+# changed, or removed in the working directory, but not "cvs
+# committed". It also determines whether the files have been "cvs
+# added" or "cvs removed". For directories, it is only practical to
+# determine whether they have been added.
+
+name=cvscheck
+changes=0
+
+# If we can't run CVS commands in this directory
+cvs status . > /dev/null 2>&1
+if [ $? != 0 ] ; then
+
+ # Bail out
+ echo "$name: there is no version here; bailing out" 1>&2
+ exit 1
+fi
+
+# Identify files added to working directory
+for file in .* * ; do
+
+ # Skip '.' and '..'
+ if [ $file = '.' -o $file = '..' ] ; then
+ continue
+ fi
+
+ # If a regular file
+ if [ -f $file ] ; then
+ if cvs status $file | grep -s '^From:[ ]*New file' ; then
+ echo "file added: $file - not CVS committed"
+ changes=`expr $changes + 1`
+ elif cvs status $file | grep -s '^From:[ ]*no entry for' ; then
+ echo "file added: $file - not CVS added, not CVS committed"
+ changes=`expr $changes + 1`
+ fi
+
+ # Else if a directory
+ elif [ -d $file -a $file != CVS.adm ] ; then
+
+ # Move into it
+ cd $file
+
+ # If CVS commands don't work inside
+ cvs status . > /dev/null 2>&1
+ if [ $? != 0 ] ; then
+ echo "directory added: $file - not CVS added"
+ changes=`expr $changes + 1`
+ fi
+
+ # Move back up
+ cd ..
+ fi
+done
+
+# Identify changed files
+changedfiles=`cvs diff | egrep '^diff' | awk '{print $3}'`
+for file in $changedfiles ; do
+ echo "file changed: $file - not CVS committed"
+ changes=`expr $changes + 1`
+done
+
+# Identify files removed from working directory
+removedfiles=`cvs status | egrep '^File:[ ]*no file' | awk '{print $4}'`
+
+# Determine whether each file has been cvs removed
+for file in $removedfiles ; do
+ if cvs status $file | grep -s '^From:[ ]*-' ; then
+ echo "file removed: $file - not CVS committed"
+ else
+ echo "file removed: $file - not CVS removed, not CVS committed"
+ fi
+ changes=`expr $changes + 1`
+done
+
+exit $changes
diff --git a/gnu/usr.bin/cvs/contrib/cvscheck.man b/gnu/usr.bin/cvs/contrib/cvscheck.man
new file mode 100644
index 0000000..10d47f5
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/cvscheck.man
@@ -0,0 +1,53 @@
+.\" cvscheck.man,v 1.1 1992/04/10 03:04:20 berliner Exp
+.\" Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net>
+.TH CVSCHECK LOCAL "4 March 1991" FLUKE
+.SH NAME
+cvscheck \- identify files added, changed, or removed in a CVS working
+directory
+.SH SYNOPSIS
+.B cvscheck
+.SH DESCRIPTION
+This command is a housekeeping aid. It should be run in a working
+directory that has been checked out using CVS. It identifies files
+that have been added, changed, or removed in the working directory, but
+not CVS
+.BR commit ted.
+It also determines whether the files have been CVS
+.BR add ed
+or CVS
+.BR remove d.
+For directories, this command determines only whether they have been
+.BR add ed.
+It operates in the current directory only.
+.LP
+This command provides information that is available using CVS
+.B status
+and CVS
+.BR diff .
+The advantage of
+.B cvscheck
+is that its output is very concise. It saves you the strain (and
+potential error) of interpreting the output of CVS
+.B status
+and
+.BR diff .
+.LP
+See
+.BR cvs (local)
+or
+.BR cvshelp (local)
+for instructions on how to add or remove a file or directory in a
+CVS-controlled package.
+.SH DIAGNOSTICS
+The exit status is 0 if no files have been added, changed, or removed
+from the current directory. Otherwise, the command returns a count of
+the adds, changes, and deletes.
+.SH SEE ALSO
+.BR cvs (local),
+.BR cvshelp (local)
+.SH AUTHOR
+Lowell Skoog
+.br
+Software Technology Group
+.br
+Technical Computing
diff --git a/gnu/usr.bin/cvs/contrib/cvshelp.man b/gnu/usr.bin/cvs/contrib/cvshelp.man
new file mode 100644
index 0000000..a7128a8
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/cvshelp.man
@@ -0,0 +1,562 @@
+.\" cvshelp.man,v 1.1 1992/04/10 03:04:21 berliner Exp
+.\" Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net>
+.\" Full space in nroff; half space in troff
+.de SP
+.if n .sp
+.if t .sp .5
+..
+.\" Start a command example
+.de XS
+.SP
+.in +.5i
+.ft B
+.nf
+..
+.\" End a command example
+.de XE
+.fi
+.ft P
+.in -.5i
+.SP
+..
+.TH CVSHELP LOCAL "17 March 1991" FLUKE
+.SH NAME
+cvshelp \- advice on using the Concurrent Versions System
+.SH DESCRIPTION
+This man page is based on experience using CVS.
+It is bound to change as we gain more experience.
+If you come up with better advice than is found here,
+contact the Software Technology
+Group and we will add it to this page.
+.SS "Getting Started"
+Use the following steps to prepare to use CVS:
+.TP
+\(bu
+Take a look at the CVS manual page to see what it can do for you, and
+if it fits your environment (or can possibly be made to fit your
+environment).
+.XS
+man cvs
+.XE
+If things look good, continue on...
+.TP
+\(bu
+Setup the master source repository. Choose a directory with
+ample disk space available for source files. This is where the RCS
+`,v' files will be stored. Say you choose
+.B /src/master
+as the root
+of your source repository. Make the
+.SB CVSROOT.adm
+directory in the root of the source repository:
+.XS
+mkdir /src/master/CVSROOT.adm
+.XE
+.TP
+\(bu
+Populate this directory with the
+.I loginfo
+and
+.I modules
+files from the
+.B "/usr/doc/local/cvs"
+directory. Edit these files to reflect your local source repository
+environment \- they may be quite small initially, but will grow as
+sources are added to your source repository. Turn these files into
+RCS controlled files:
+.XS
+cd /src/master/CVSROOT.adm
+ci \-m'Initial loginfo file' loginfo
+ci \-m'Initial modules file' modules
+.XE
+.TP
+\(bu
+Run the command:
+.XS
+mkmodules /src/master/CVSROOT.adm
+.XE
+This will build the
+.BR ndbm (3)
+file for the modules database.
+.TP
+\(bu
+Remember to edit the
+.I modules
+file manually when sources are checked
+in with
+.B checkin
+or CVS
+.BR add .
+A copy of the
+.I modules
+file for editing can be retrieved with the command:
+.XS
+cvs checkout CVSROOT.adm
+.XE
+.TP
+\(bu
+Have all users of the CVS system set the
+.SM CVSROOT
+environment variable appropriately to reflect the placement of your
+source repository. If the above example is used, the following
+commands can be placed in a
+.I .login
+or
+.I .profile
+file:
+.XS
+setenv CVSROOT /src/master
+.XE
+for csh users, and
+.XS
+CVSROOT=/src/master; export CVSROOT
+.XE
+for sh users.
+.SS "Placing Locally Written Sources Under CVS Control"
+Say you want to place the `whizbang' sources under
+CVS control. Say further that the sources have never
+been under revision control before.
+.TP
+\(bu
+Move the source hierarchy (lock, stock, and barrel)
+into the master source repository:
+.XS
+mv ~/whizbang $CVSROOT
+.XE
+.TP
+\(bu
+Clean out unwanted object files:
+.XS
+cd $CVSROOT/whizbang
+make clean
+.XE
+.TP
+\(bu
+Turn every file in the hierarchy into an RCS controlled file:
+.XS
+descend \-f 'ci \-t/dev/null \-m"Placed under CVS control" \-nV\fR\fIx\fR\fB_\fR\fIy\fR\fB *'
+.XE
+In this example, the initial release tag is \fBV\fIx\fB_\fIy\fR,
+representing version \fIx\fR.\fIy\fR.
+.LP
+You can use CVS on sources that are already under RCS control.
+The following example shows how.
+In this example, the source package is called `skunkworks'.
+.TP
+\(bu
+Move the source hierarchy into the master source
+repository:
+.XS
+mv ~/skunkworks $CVSROOT
+.XE
+.TP
+\(bu
+Clean out unwanted object files:
+.XS
+cd $CVSROOT/skunkworks
+make clean
+.XE
+.TP
+\(bu
+Clean out unwanted working files, leaving only the RCS `,v' files:
+.XS
+descend \-r rcsclean
+.XE
+Note: If any working files have been checked out and changed,
+.B rcsclean
+will fail. Check in the modified working files
+and run the command again.
+.TP
+\(bu
+Get rid of
+.SB RCS
+subdirectories. CVS does not use them.
+.XS
+descend \-r \-f 'mv RCS/*,v .'
+descend \-r \-f 'rmdir RCS'
+.XE
+.TP
+\(bu
+Delete any unwanted files that remain in the source hierarchy. Then
+make sure all files are under RCS control:
+.XS
+descend \-f 'ci \-t/dev/null \-m"Placed under CVS control" \-n\fR\fItag\fR\fB *'
+.XE
+.I tag
+is the latest symbolic revision tag that you applied to your package
+(if any). Note: This command will probably generate lots of error
+messages (for directories and existing RCS files) that you can
+ignore.
+.SS "Placing a Third-Party Source Distribution Under CVS Control"
+The
+.B checkin
+command checks third-party sources into CVS. The
+difference between third-party sources and locally
+written sources is that third-party sources must be checked into a
+separate branch (called the
+.IR "vendor branch" )
+of the RCS tree. This makes it possible to merge local changes to
+the sources with later releases from the vendor.
+.TP
+\(bu
+Save the original distribution kit somewhere. For example, if the
+master source repository is
+.B /src/master
+the distribution kit could be saved in
+.BR /src/dist .
+Organize the distribution directory so that each release
+is clearly identifiable.
+.TP
+\(bu
+Unpack the package in a scratch directory, for example
+.BR ~/scratch .
+.TP
+\(bu
+Create a repository for the package.
+In this example, the package is called `Bugs-R-Us 4.3'.
+.XS
+mkdir $CVSROOT/bugs
+.XE
+.TP
+\(bu
+Check in the unpacked files:
+.XS
+cd ~/scratch
+checkin \-m 'Bugs-R-Us 4.3 distribution' bugs VENDOR V4_3
+.XE
+There is nothing magic about the tag `VENDOR', which is applied to
+the vendor branch. You can use whatever tag you want. `VENDOR' is a
+useful convention.
+.TP
+\(bu
+Never modify vendor files before checking them in.
+Check in the files
+.I exactly
+as you unpacked them.
+If you check in locally modified files, future vendor releases may
+wipe out your local changes.
+.SS "Working With CVS-Controlled Sources"
+To use or edit the sources, you must check out a private copy.
+For the following examples, the master files are assumed to reside in
+.BR "$CVSROOT/behemoth" .
+The working directory is
+.BR "~/work" .
+See
+.BR cvs (local)
+for more details on the commands mentioned below.
+.TP
+.I "To Check Out Working Files
+Use CVS
+.BR checkout :
+.XS
+cd ~/work
+cvs checkout behemoth
+.XE
+There is nothing magic about the working directory. CVS will check
+out sources anywhere you like. Once you have a working copy of the
+sources, you can compile or edit them as desired.
+.TP
+.I "To Display Changes You Have Made"
+Use CVS
+.BR diff
+to display detailed changes, equivalent to
+.BR rcsdiff (local).
+You can also use
+.BR cvscheck (local)
+to list files added, changed, and removed in
+the directory, but not yet
+.BR commit ted.
+You must be in a directory containing working files.
+.TP
+.I "To Display Revision Information"
+Use CVS
+.BR log ,
+which is equivalent to
+.BR rlog (local).
+You must be in a directory containing working files.
+.TP
+.I "To Update Working Files"
+Use CVS
+.BR update
+in a directory containing working files.
+This command brings your working files up
+to date with changes checked into the
+master repository since you last checked out or updated
+your files.
+.TP
+.I "To Check In Your Changes"
+Use CVS
+.BR commit
+in a directory containing working files.
+This command checks your changes into the master repository.
+You can specify files by name or use
+.XS
+cvs commit \-a
+.XE
+to
+.B commit
+all the files you have changed.
+.TP
+.I "To Add a File"
+Add the file to the working directory.
+Use CVS
+.B add
+to mark the file as added.
+Use CVS
+.B commit
+to add the file to the master repository.
+.TP
+.I "To Remove a File"
+Remove the file from the working directory.
+Use CVS
+.B remove
+to mark the file as removed.
+Use CVS
+.B commit
+to move the file from its current location in the master repository
+to the CVS
+.IR Attic
+directory.
+.TP
+.I "To Add a Directory"
+Add the directory to the working directory.
+Use CVS
+.B add
+to add the directory to the master repository.
+.TP
+.I "To Remove a Directory"
+.br
+You shouldn't remove directories under CVS. You should instead remove
+their contents and then prune them (using the
+.B \-f
+and
+.B \-p
+options) when you
+.B checkout
+or
+.B update
+your working files.
+.TP
+.I "To Tag a Release"
+Use CVS
+.B tag
+to apply a symbolic tag to the latest revision of each file in the
+master repository. For example:
+.XS
+cvs tag V2_1 behemoth
+.XE
+.TP
+.I "To Retrieve an Exact Copy of a Previous Release"
+During a CVS
+.B checkout
+or
+.BR update ,
+use the
+.B \-r
+option to retrieve revisions associated with a symbolic tag.
+Use the
+.B \-f
+option to ignore all RCS files that do not contain the
+tag.
+Use the
+.B \-p
+option to prune directories that wind up empty because none
+of their files matched the tag. Example:
+.XS
+cd ~/work
+cvs checkout \-r V2_1 \-f \-p behemoth
+.XE
+.SS "Logging Changes"
+It is a good idea to keep a change log together with the
+sources. As a minimum, the change log should name and describe each
+tagged release. The change log should also be under CVS control and
+should be tagged along with the sources.
+.LP
+.BR cvslog (local)
+can help. This command logs
+changes reported during CVS
+.B commit
+operations. It automatically
+updates a change log file in your working directory. When you are
+finished making changes, you (optionally) edit the change log file and
+then commit it to the master repository.
+.LP
+Note: You must edit the change log to describe a new release
+and
+.B commit
+it to the master repository
+.I before
+.BR tag ging
+the release using CVS. Otherwise, the release description will not be
+included in the tagged package.
+.LP
+See
+.BR cvslog (local)
+for more information.
+.SS "Merging a Subsequent Third-Party Distribution"
+The initial steps in this process are identical to placing a
+third-party distribution under CVS for the first time: save the
+distribution kit and unpack the package in a scratch directory. From
+that point the steps diverge.
+The following example considers release 5.0 of the
+Bugs-R-Us package.
+.TP
+\(bu
+Check in the sources after unpacking them:
+.XS
+cd ~/scratch
+checkin \-m 'Bugs-R-Us 5.0 distribution' bugs VENDOR V5_0 \\
+ | tee ~/WARNINGS
+.XE
+It is important to save the output of
+.B checkin
+in a file
+because it lists the sources that have been locally modified.
+It is best to save the file in a different directory (for example,
+your home directory). Otherwise,
+.B checkin
+will try to check it into the master repository.
+.TP
+\(bu
+In your usual working directory, check out a fresh copy of the
+distribution that you just checked in.
+.XS
+cd ~/work
+cvs checkout \-r VENDOR bugs
+.XE
+The
+.B checkout
+command shown above retrieves the latest revision on the vendor branch.
+.TP
+\(bu
+See the `WARNINGS' file for a list of all locally modified
+sources.
+For each locally modified source,
+look at the differences between
+the new distribution and the latest local revision:
+.XS
+cvs diff \-r \fR\fILocalRev file\fR\fB
+.XE
+In this command,
+.I LocalRev
+is the latest
+numeric or symbolic revision
+on the RCS trunk of
+.IR file .
+You can use CVS
+.B log
+to get the revision history.
+.TP
+\(bu
+If your local modifications to a file have been incorporated into
+the vendor's distribution, then you should reset the default RCS
+branch for that file to the vendor branch. CVS doesn't provide a
+mechanism to do this. You have to do it by hand in the master
+repository:
+.XS
+rcs \-bVENDOR \fR\fIfile\fR\fB,v
+.XE
+.TP
+\(bu
+If your local modifications need to be merged with the
+new distribution, use CVS
+.B join
+to do it:
+.XS
+cvs join \-r VENDOR \fR\fIfile\fR\fB
+.XE
+The resulting file will be placed in your working directory.
+Edit it to resolve any overlaps.
+.TP
+\(bu
+Test the merged package.
+.TP
+\(bu
+Commit all modified files to the repository:
+.XS
+cvs commit \-a
+.XE
+.TP
+\(bu
+Tag the repository with a new local tag.
+.SS "Applying Patches to Third-Party Sources"
+Patches are handled in a manner very similar to complete
+third-party distributions. This example considers patches applied to
+Bugs-R-Us release 5.0.
+.TP
+\(bu
+Save the patch files together with the distribution kit
+to which they apply.
+The patch file names should clearly indicate the patch
+level.
+.TP
+\(bu
+In a scratch directory, check out the last `clean' vendor copy \- the
+highest revision on the vendor branch with
+.IR "no local changes" :
+.XS
+cd ~/scratch
+cvs checkout \-r VENDOR bugs
+.XE
+.TP
+\(bu
+Use
+.BR patch (local)
+to apply the patches. You should now have an image of the
+vendor's software just as though you had received a complete,
+new release.
+.TP
+\(bu
+Proceed with the steps described for merging a subsequent third-party
+distribution.
+.TP
+\(bu
+Note: When you get to the step that requires you
+to check out the new distribution after you have
+checked it into the vendor branch, you should move to a different
+directory. Do not attempt to
+.B checkout
+files in the directory in
+which you applied the patches. If you do, CVS will try to merge the
+changes that you made during patching with the version being checked
+out and things will get very confusing. Instead,
+go to a different directory (like your working directory) and
+check out the files there.
+.SS "Advice to Third-Party Source Hackers"
+As you can see from the preceding sections, merging local changes
+into third-party distributions remains difficult, and probably
+always will. This fact suggests some guidelines:
+.TP
+\(bu
+Minimize local changes.
+.I Never
+make stylistic changes.
+Change makefiles only as much as needed for installation. Avoid
+overhauling anything. Pray that the vendor does the same.
+.TP
+\(bu
+Avoid renaming files or moving them around.
+.TP
+\(bu
+Put independent, locally written files like help documents, local
+tools, or man pages in a sub-directory called `local-additions'.
+Locally written files that are linked into an existing executable
+should be added right in with the vendor's sources (not in a
+`local-additions' directory).
+If, in the future,
+the vendor distributes something
+equivalent to your locally written files
+you can CVS
+.B remove
+the files from the `local-additions' directory at that time.
+.SH SEE ALSO
+.BR cvs (local),
+.BR checkin (local),
+.BR cvslog (local),
+.BR cvscheck (local)
+.SH AUTHOR
+Lowell Skoog
+.br
+Software Technology Group
+.br
+Technical Computing
diff --git a/gnu/usr.bin/cvs/contrib/descend b/gnu/usr.bin/cvs/contrib/descend
new file mode 100644
index 0000000..b63e4a7
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/descend
@@ -0,0 +1,116 @@
+#! /bin/sh
+# descend,v 1.1 1992/04/03 05:22:52 berliner Exp
+#
+# descend - walk down a directory tree and execute a command at each node
+
+fullname=$0
+name=descend
+usage="Usage: $name [-afqrv] command [directory ...]\n
+\040\040-a\040\040All: descend into directories starting with '.'\n
+\040\040-f\040\040Force: ignore errors during descent\n
+\040\040-q\040\040Quiet: don't print directory names\n
+\040\040-r\040\040Restricted: don't descend into RCS, CVS.adm, SCCS directories\n
+\040\040-v\040\040Verbose: print command before executing it"
+
+# Scan for options
+while getopts afqrv option; do
+ case $option in
+ a)
+ alldirs=$option
+ options=$options" "-$option
+ ;;
+ f)
+ force=$option
+ options=$options" "-$option
+ ;;
+ q)
+ verbose=
+ quiet=$option
+ options=$options" "-$option
+ ;;
+ r)
+ restricted=$option
+ options=$options" "-$option
+ ;;
+ v)
+ verbose=$option
+ quiet=
+ options=$options" "-$option
+ ;;
+ \?)
+ /usr/5bin/echo $usage 1>&2
+ exit 1
+ ;;
+ esac
+done
+shift `expr $OPTIND - 1`
+
+# Get command to execute
+if [ $# -lt 1 ] ; then
+ /usr/5bin/echo $usage 1>&2
+ exit 1
+else
+ command=$1
+ shift
+fi
+
+# If no directory specified, use '.'
+if [ $# -lt 1 ] ; then
+ default_dir=.
+fi
+
+# For each directory specified
+for dir in $default_dir "$@" ; do
+
+ # Spawn sub-shell so we return to starting directory afterward
+ (cd $dir
+
+ # Execute specified command
+ if [ -z "$quiet" ] ; then
+ echo In directory `hostname`:`pwd`
+ fi
+ if [ -n "$verbose" ] ; then
+ echo $command
+ fi
+ eval "$command" || if [ -z "$force" ] ; then exit 1; fi
+
+ # Collect dot file names if necessary
+ if [ -n "$alldirs" ] ; then
+ dotfiles=.*
+ else
+ dotfiles=
+ fi
+
+ # For each file in current directory
+ for file in $dotfiles * ; do
+
+ # Skip '.' and '..'
+ if [ "$file" = "." -o "$file" = ".." ] ; then
+ continue
+ fi
+
+ # If a directory but not a symbolic link
+ if [ -d "$file" -a ! -h "$file" ] ; then
+
+ # If not skipping this type of directory
+ if [ \( "$file" != "RCS" -a \
+ "$file" != "SCCS" -a \
+ "$file" != "CVS" -a \
+ "$file" != "CVS.adm" \) \
+ -o -z "$restricted" ] ; then
+
+ # Recursively descend into it
+ $fullname $options "$command" "$file" \
+ || if [ -z "$force" ] ; then exit 1; fi
+ fi
+
+ # Else if a directory AND a symbolic link
+ elif [ -d "$file" -a -h "$file" ] ; then
+
+ if [ -z "$quiet" ] ; then
+ echo In directory `hostname`:`pwd`/$file: symbolic link: skipping
+ fi
+ fi
+ done
+ ) || if [ -z "$force" ] ; then exit 1; fi
+done
diff --git a/gnu/usr.bin/cvs/contrib/descend.man b/gnu/usr.bin/cvs/contrib/descend.man
new file mode 100644
index 0000000..adeab3b
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/descend.man
@@ -0,0 +1,115 @@
+.\" descend.man,v 1.1 1992/04/03 05:22:53 berliner Exp
+.TH DESCEND 1 "31 March 1992"
+.SH NAME
+descend \- walk directory tree and execute a command at each node
+.SH SYNOPSIS
+.B descend
+[
+.B \-afqrv
+]
+.I command
+[
+.I directory
+\&.\|.\|.
+]
+.SH DESCRIPTION
+.B descend
+walks down a directory tree and executes a command at each node. It
+is not as versatile as
+.BR find (1),
+but it has a simpler syntax. If no
+.I directory
+is specified,
+.B descend
+starts at the current one.
+.LP
+Unlike
+.BR find ,
+.B descend
+can be told to skip the special directories associated with RCS,
+CVS, and SCCS. This makes
+.B descend
+especially handy for use with these packages. It can be used with
+other commands too, of course.
+.LP
+.B descend
+is a poor man's way to make any command recursive. Note:
+.B descend
+does not follow symbolic links to directories unless they are
+specified on the command line.
+.SH OPTIONS
+.TP 15
+.B \-a
+.I All.
+Descend into directories that begin with '.'.
+.TP
+.B \-f
+.I Force.
+Ignore errors during descent. Normally,
+.B descend
+quits when an error occurs.
+.TP
+.B \-q
+.I Quiet.
+Suppress the message `In directory
+.IR directory '
+that is normally printed during the descent.
+.TP
+.B \-r
+.I Restricted.
+Don't descend into the special directories
+.SB RCS,
+.SB CVS,
+.SB CVS.adm,
+and
+.SB SCCS.
+.TP
+.B \-v
+.I Verbose.
+Print
+.I command
+before executing it.
+.SH EXAMPLES
+.TP 15
+.B "descend ls"
+Cheap substitute for `ls -R'.
+.TP 15
+.B "descend -f 'rm *' tree"
+Strip `tree' of its leaves. This command descends the `tree'
+directory, removing all regular files. Since
+.BR rm (1)
+does not remove directories, this command leaves the directory
+structure of `tree' intact, but denuded. The
+.B \-f
+option is required to keep
+.B descend
+from quitting. You could use `rm \-f' instead.
+.TP
+.B "descend -r 'co RCS/*'" /project/src/
+Check out every RCS file under the directory
+.BR "/project/src" .
+.TP
+.B "descend -r 'cvs diff'"
+Perform CVS `diff' operation on every directory below (and including)
+the current one.
+.SH DIAGNOSTICS
+Returns 1 if errors occur (and the
+.B \-f
+option is not used). Otherwise returns 0.
+.SH SEE ALSO
+.BR find (1),
+.BR rcsintro (1),
+.BR cvs (1),
+.BR sccs (1)
+.SH AUTHOR
+Lowell Skoog
+.br
+Software Technology Group
+.br
+John Fluke Mfg. Co., Inc.
+.SH BUGS
+Shell metacharacters in
+.I command
+may have bizarre effects. In particular, compound commands
+(containing ';', '[', and ']' characters) will not work. It is best
+to enclose complicated commands in single quotes \(aa\ \(aa.
diff --git a/gnu/usr.bin/cvs/contrib/dirfns b/gnu/usr.bin/cvs/contrib/dirfns
new file mode 100644
index 0000000..8324c41
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/dirfns
@@ -0,0 +1,481 @@
+echo 'directory.3':
+sed 's/^X//' >'directory.3' <<'!'
+X.TH DIRECTORY 3 imported
+X.DA 9 Oct 1985
+X.SH NAME
+Xopendir, readdir, telldir, seekdir, rewinddir, closedir \- high-level directory operations
+X.SH SYNOPSIS
+X.B #include <sys/types.h>
+X.br
+X.B #include <ndir.h>
+X.PP
+X.SM
+X.B DIR
+X.B *opendir(filename)
+X.br
+X.B char *filename;
+X.PP
+X.SM
+X.B struct direct
+X.B *readdir(dirp)
+X.br
+X.B DIR *dirp;
+X.PP
+X.SM
+X.B long
+X.B telldir(dirp)
+X.br
+X.B DIR *dirp;
+X.PP
+X.SM
+X.B seekdir(dirp, loc)
+X.br
+X.B DIR *dirp;
+X.br
+X.B long loc;
+X.PP
+X.SM
+X.B rewinddir(dirp)
+X.br
+X.B DIR *dirp;
+X.PP
+X.SM
+X.B closedir(dirp)
+X.br
+X.B DIR *dirp;
+X.SH DESCRIPTION
+XThis library provides high-level primitives for directory scanning,
+Xsimilar to those available for 4.2BSD's (very different) directory system.
+X.\"The purpose of this library is to simulate
+X.\"the new flexible length directory names of 4.2bsd UNIX
+X.\"on top of the old directory structure of v7.
+XIt incidentally provides easy portability to and from 4.2BSD (insofar
+Xas such portability is not compromised by other 4.2/VAX dependencies).
+X.\"It allows programs to be converted immediately
+X.\"to the new directory access interface,
+X.\"so that they need only be relinked
+X.\"when moved to 4.2bsd.
+X.\"It is obtained with the loader option
+X.\".BR \-lndir .
+X.PP
+X.I Opendir
+Xopens the directory named by
+X.I filename
+Xand associates a
+X.I directory stream
+Xwith it.
+X.I Opendir
+Xreturns a pointer to be used to identify the
+X.I directory stream
+Xin subsequent operations.
+XThe pointer
+X.SM
+X.B NULL
+Xis returned if
+X.I filename
+Xcannot be accessed or is not a directory.
+X.PP
+X.I Readdir
+Xreturns a pointer to the next directory entry.
+XIt returns
+X.B NULL
+Xupon reaching the end of the directory or detecting
+Xan invalid
+X.I seekdir
+Xoperation.
+X.PP
+X.I Telldir
+Xreturns the current location associated with the named
+X.I directory stream.
+X.PP
+X.I Seekdir
+Xsets the position of the next
+X.I readdir
+Xoperation on the
+X.I directory stream.
+XThe new position reverts to the one associated with the
+X.I directory stream
+Xwhen the
+X.I telldir
+Xoperation was performed.
+XValues returned by
+X.I telldir
+Xare good only for the lifetime of the DIR pointer from
+Xwhich they are derived.
+XIf the directory is closed and then reopened,
+Xthe
+X.I telldir
+Xvalue may be invalidated
+Xdue to undetected directory compaction in 4.2BSD.
+XIt is safe to use a previous
+X.I telldir
+Xvalue immediately after a call to
+X.I opendir
+Xand before any calls to
+X.I readdir.
+X.PP
+X.I Rewinddir
+Xresets the position of the named
+X.I directory stream
+Xto the beginning of the directory.
+X.PP
+X.I Closedir
+Xcauses the named
+X.I directory stream
+Xto be closed,
+Xand the structure associated with the DIR pointer to be freed.
+X.PP
+XA
+X.I direct
+Xstructure is as follows:
+X.PP
+X.RS
+X.nf
+Xstruct direct {
+X /* unsigned */ long d_ino; /* inode number of entry */
+X unsigned short d_reclen; /* length of this record */
+X unsigned short d_namlen; /* length of string in d_name */
+X char d_name[MAXNAMLEN + 1]; /* name must be no longer than this */
+X};
+X.fi
+X.RE
+X.PP
+XThe
+X.I d_reclen
+Xfield is meaningless in non-4.2BSD systems and should be ignored.
+XThe use of a
+X.I long
+Xfor
+X.I d_ino
+Xis also a 4.2BSDism;
+X.I ino_t
+X(see
+X.IR types (5))
+Xshould be used elsewhere.
+XThe macro
+X.I DIRSIZ(dp)
+Xgives the minimum memory size needed to hold the
+X.I direct
+Xvalue pointed to by
+X.IR dp ,
+Xwith the minimum necessary allocation for
+X.IR d_name .
+X.PP
+XThe preferred way to search the current directory for entry ``name'' is:
+X.PP
+X.RS
+X.nf
+X len = strlen(name);
+X dirp = opendir(".");
+X if (dirp == NULL) {
+X fprintf(stderr, "%s: can't read directory .\\n", argv[0]);
+X return NOT_FOUND;
+X }
+X while ((dp = readdir(dirp)) != NULL)
+X if (dp->d_namlen == len && strcmp(dp->d_name, name) == 0) {
+X closedir(dirp);
+X return FOUND;
+X }
+X closedir(dirp);
+X return NOT_FOUND;
+X.RE
+X.\".SH LINKING
+X.\"This library is accessed by specifying ``-lndir'' as the
+X.\"last argument to the compile line, e.g.:
+X.\".PP
+X.\" cc -I/usr/include/ndir -o prog prog.c -lndir
+X.SH "SEE ALSO"
+Xopen(2),
+Xclose(2),
+Xread(2),
+Xlseek(2)
+X.SH HISTORY
+XWritten by
+XKirk McKusick at Berkeley (ucbvax!mckusick).
+XMiscellaneous bug fixes from elsewhere.
+XThe size of the data structure has been decreased to avoid excessive
+Xspace waste under V7 (where filenames are 14 characters at most).
+XFor obscure historical reasons, the include file is also available
+Xas
+X.IR <ndir/sys/dir.h> .
+XThe Berkeley version lived in a separate library (\fI\-lndir\fR),
+Xwhereas ours is
+Xpart of the C library, although the separate library is retained to
+Xmaximize compatibility.
+X.PP
+XThis manual page has been substantially rewritten to be informative in
+Xthe absence of a 4.2BSD manual.
+X.SH BUGS
+XThe
+X.I DIRSIZ
+Xmacro actually wastes a bit of space due to some padding requirements
+Xthat are an artifact of 4.2BSD.
+X.PP
+XThe returned value of
+X.I readdir
+Xpoints to a static area that will be overwritten by subsequent calls.
+X.PP
+XThere are some unfortunate name conflicts with the \fIreal\fR V7
+Xdirectory structure definitions.
+!
+echo 'dir.h':
+sed 's/^X//' >'dir.h' <<'!'
+X/* dir.h 4.4 82/07/25 */
+X
+X/*
+X * A directory consists of some number of blocks of DIRBLKSIZ
+X * bytes, where DIRBLKSIZ is chosen such that it can be transferred
+X * to disk in a single atomic operation (e.g. 512 bytes on most machines).
+X *
+X * Each DIRBLKSIZ byte block contains some number of directory entry
+X * structures, which are of variable length. Each directory entry has
+X * a struct direct at the front of it, containing its inode number,
+X * the length of the entry, and the length of the name contained in
+X * the entry. These are followed by the name padded to a 4 byte boundary
+X * with null bytes. All names are guaranteed null terminated.
+X * The maximum length of a name in a directory is MAXNAMLEN.
+X *
+X * The macro DIRSIZ(dp) gives the amount of space required to represent
+X * a directory entry. Free space in a directory is represented by
+X * entries which have dp->d_reclen >= DIRSIZ(dp). All DIRBLKSIZ bytes
+X * in a directory block are claimed by the directory entries. This
+X * usually results in the last entry in a directory having a large
+X * dp->d_reclen. When entries are deleted from a directory, the
+X * space is returned to the previous entry in the same directory
+X * block by increasing its dp->d_reclen. If the first entry of
+X * a directory block is free, then its dp->d_ino is set to 0.
+X * Entries other than the first in a directory do not normally have
+X * dp->d_ino set to 0.
+X */
+X#define DIRBLKSIZ 512
+X#ifdef VMUNIX
+X#define MAXNAMLEN 255
+X#else
+X#define MAXNAMLEN 14
+X#endif
+X
+Xstruct direct {
+X /* unsigned */ long d_ino; /* inode number of entry */
+X unsigned short d_reclen; /* length of this record */
+X unsigned short d_namlen; /* length of string in d_name */
+X char d_name[MAXNAMLEN + 1]; /* name must be no longer than this */
+X};
+X
+X/*
+X * The DIRSIZ macro gives the minimum record length which will hold
+X * the directory entry. This requires the amount of space in struct direct
+X * without the d_name field, plus enough space for the name with a terminating
+X * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary.
+X */
+X#undef DIRSIZ
+X#define DIRSIZ(dp) \
+X ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
+X
+X#ifndef KERNEL
+X/*
+X * Definitions for library routines operating on directories.
+X */
+Xtypedef struct _dirdesc {
+X int dd_fd;
+X long dd_loc;
+X long dd_size;
+X char dd_buf[DIRBLKSIZ];
+X} DIR;
+X#ifndef NULL
+X#define NULL 0
+X#endif
+Xextern DIR *opendir();
+Xextern struct direct *readdir();
+Xextern long telldir();
+X#ifdef void
+Xextern void seekdir();
+Xextern void closedir();
+X#endif
+X#define rewinddir(dirp) seekdir((dirp), (long)0)
+X#endif KERNEL
+!
+echo 'makefile':
+sed 's/^X//' >'makefile' <<'!'
+XDIR = closedir.o opendir.o readdir.o seekdir.o telldir.o
+XCFLAGS=-O -I. -Dvoid=int
+XDEST=..
+X
+Xall: $(DIR)
+X
+Xmv: $(DIR)
+X mv $(DIR) $(DEST)
+X
+Xcpif: dir.h
+X cp dir.h /usr/include/ndir.h
+X
+Xclean:
+X rm -f *.o
+!
+echo 'closedir.c':
+sed 's/^X//' >'closedir.c' <<'!'
+Xstatic char sccsid[] = "@(#)closedir.c 4.2 3/10/82";
+X
+X#include <sys/types.h>
+X#include <dir.h>
+X
+X/*
+X * close a directory.
+X */
+Xvoid
+Xclosedir(dirp)
+X register DIR *dirp;
+X{
+X close(dirp->dd_fd);
+X dirp->dd_fd = -1;
+X dirp->dd_loc = 0;
+X free((char *)dirp);
+X}
+!
+echo 'opendir.c':
+sed 's/^X//' >'opendir.c' <<'!'
+X/* Copyright (c) 1982 Regents of the University of California */
+X
+Xstatic char sccsid[] = "@(#)opendir.c 4.4 11/12/82";
+X
+X#include <sys/types.h>
+X#include <sys/stat.h>
+X#include <dir.h>
+X
+X/*
+X * open a directory.
+X */
+XDIR *
+Xopendir(name)
+X char *name;
+X{
+X register DIR *dirp;
+X register int fd;
+X struct stat statbuf;
+X char *malloc();
+X
+X if ((fd = open(name, 0)) == -1)
+X return NULL;
+X if (fstat(fd, &statbuf) == -1 || !(statbuf.st_mode & S_IFDIR)) {
+X close(fd);
+X return NULL;
+X }
+X if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
+X close (fd);
+X return NULL;
+X }
+X dirp->dd_fd = fd;
+X dirp->dd_loc = 0;
+X dirp->dd_size = 0; /* so that telldir will work before readdir */
+X return dirp;
+X}
+!
+echo 'readdir.c':
+sed 's/^X//' >'readdir.c' <<'!'
+X/* Copyright (c) 1982 Regents of the University of California */
+X
+Xstatic char sccsid[] = "@(#)readdir.c 4.3 8/8/82";
+X
+X#include <sys/types.h>
+X#include <dir.h>
+X
+X/*
+X * read an old stlye directory entry and present it as a new one
+X */
+X#define ODIRSIZ 14
+X
+Xstruct olddirect {
+X ino_t od_ino;
+X char od_name[ODIRSIZ];
+X};
+X
+X/*
+X * get next entry in a directory.
+X */
+Xstruct direct *
+Xreaddir(dirp)
+X register DIR *dirp;
+X{
+X register struct olddirect *dp;
+X static struct direct dir;
+X
+X for (;;) {
+X if (dirp->dd_loc == 0) {
+X dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
+X DIRBLKSIZ);
+X if (dirp->dd_size <= 0) {
+X dirp->dd_size = 0;
+X return NULL;
+X }
+X }
+X if (dirp->dd_loc >= dirp->dd_size) {
+X dirp->dd_loc = 0;
+X continue;
+X }
+X dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc);
+X dirp->dd_loc += sizeof(struct olddirect);
+X if (dp->od_ino == 0)
+X continue;
+X dir.d_ino = dp->od_ino;
+X strncpy(dir.d_name, dp->od_name, ODIRSIZ);
+X dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */
+X dir.d_namlen = strlen(dir.d_name);
+X dir.d_reclen = DIRBLKSIZ;
+X return (&dir);
+X }
+X}
+!
+echo 'seekdir.c':
+sed 's/^X//' >'seekdir.c' <<'!'
+Xstatic char sccsid[] = "@(#)seekdir.c 4.9 3/25/83";
+X
+X#include <sys/param.h>
+X#include <dir.h>
+X
+X/*
+X * seek to an entry in a directory.
+X * Only values returned by "telldir" should be passed to seekdir.
+X */
+Xvoid
+Xseekdir(dirp, loc)
+X register DIR *dirp;
+X long loc;
+X{
+X long curloc, base, offset;
+X struct direct *dp;
+X extern long lseek();
+X
+X curloc = telldir(dirp);
+X if (loc == curloc)
+X return;
+X base = loc & ~(DIRBLKSIZ - 1);
+X offset = loc & (DIRBLKSIZ - 1);
+X (void) lseek(dirp->dd_fd, base, 0);
+X dirp->dd_size = 0;
+X dirp->dd_loc = 0;
+X while (dirp->dd_loc < offset) {
+X dp = readdir(dirp);
+X if (dp == NULL)
+X return;
+X }
+X}
+!
+echo 'telldir.c':
+sed 's/^X//' >'telldir.c' <<'!'
+Xstatic char sccsid[] = "@(#)telldir.c 4.1 2/21/82";
+X
+X#include <sys/types.h>
+X#include <dir.h>
+X
+X/*
+X * return a pointer into a directory
+X */
+Xlong
+Xtelldir(dirp)
+X DIR *dirp;
+X{
+X long lseek();
+X
+X return (lseek(dirp->dd_fd, 0L, 1) - dirp->dd_size + dirp->dd_loc);
+X}
+!
+echo done
diff --git a/gnu/usr.bin/cvs/contrib/log.pl b/gnu/usr.bin/cvs/contrib/log.pl
new file mode 100644
index 0000000..a6c75f6
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/log.pl
@@ -0,0 +1,104 @@
+#!/usr/bin/perl
+
+# Modified by berliner@Sun.COM to add support for CVS 1.3 2/27/92
+#
+# Date: Tue, 6 Aug 91 13:27 EDT
+# From: samborn@sunrise.com (Kevin Samborn)
+#
+# I revised the perl script I sent you yesterday to use the info you
+# send in on stdin. (I am appending the newer script to the end)
+#
+# now the output looks like this:
+#
+# **************************************
+# date: Tuesday, August 6, 1991 @ 13:17
+# author: samborn
+# Update of /elmer/cvs/CVSROOT.adm
+# In directory astro:/home/samborn/CVSROOT.adm
+#
+# Modified Files:
+# test3
+#
+# Added Files:
+# test6
+#
+# Removed Files:
+# test4
+#
+# Log Message:
+# wow, what a test
+#
+# RCS: 1.4 /elmer/cvs/CVSROOT.adm/test3,v
+# RCS: 1.1 /elmer/cvs/CVSROOT.adm/test6,v
+# RCS: 1.1 /elmer/cvs/CVSROOT.adm/Attic/test4,v
+#
+
+#
+# turn off setgid
+#
+$) = $(;
+
+#
+# parse command line arguments
+#
+@files = split(/ /,$ARGV[0]);
+$logfile = $ARGV[1];
+$cvsroot = $ENV{'CVSROOT'};
+
+#
+# Some date and time arrays
+#
+@mos = (January,February,March,April,May,June,July,August,September,
+ October,November,December);
+@days = (Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday);
+($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime;
+
+#
+# get login name
+#
+$login = getlogin || (getpwuid($<))[0] || "nobody";
+
+#
+# open log file for appending
+#
+if ((open(OUT, ">>" . $logfile)) != 1) {
+ die "Could not open logfile " . $logfile . "\n";
+}
+
+#
+# Header
+#
+print OUT "\n";
+print OUT "**************************************\n";
+print OUT "date: " . $days[$wday] . ", " . $mos[$mon] . " " . $mday . ", 19" . $year .
+ " @ " . $hour . ":" . sprintf("%02d", $min) . "\n";
+print OUT "author: " . $login . "\n";
+
+#
+#print the stuff on stdin to the logfile
+#
+open(IN, "-");
+while(<IN>) {
+ print OUT $_;
+}
+close(IN);
+
+print OUT "\n";
+
+#
+# after log information, do an 'cvs -Qn status' on each file in the arguments.
+#
+for $file (@files[1..$#files]) {
+ if ($file eq "-") {
+ last;
+ }
+ open(RCS,"-|") || exec 'cvs', '-Qn', 'status', $file;
+ while (<RCS>) {
+ if (substr($_, 0, 7) eq " RCS") {
+ print OUT;
+ }
+ }
+ close (RCS);
+}
+
+close (OUT);
diff --git a/gnu/usr.bin/cvs/contrib/log_accum.pl b/gnu/usr.bin/cvs/contrib/log_accum.pl
new file mode 100644
index 0000000..798e25f
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/log_accum.pl
@@ -0,0 +1,331 @@
+#!/usr/local/bin/perl -w
+#
+# Perl filter to handle the log messages from the checkin of files in
+# a directory. This script will group the lists of files by log
+# message, and mail a single consolidated log message at the end of
+# the commit.
+#
+# This file assumes a pre-commit checking program that leaves the
+# names of the first and last commit directories in a temporary file.
+#
+# Contributed by David Hampton <hampton@cisco.com>
+#
+
+############################################################
+#
+# Configurable options
+#
+############################################################
+#
+# Do cisco Systems, Inc. specific nonsense.
+#
+$cisco_systems = 1;
+
+#
+# Recipient of all mail messages
+#
+$mailto = "sw-notification@cisco.com";
+
+############################################################
+#
+# Constants
+#
+############################################################
+$STATE_NONE = 0;
+$STATE_CHANGED = 1;
+$STATE_ADDED = 2;
+$STATE_REMOVED = 3;
+$STATE_LOG = 4;
+
+$LAST_FILE = "/tmp/#cvs.lastdir";
+$CHANGED_FILE = "/tmp/#cvs.files.changed";
+$ADDED_FILE = "/tmp/#cvs.files.added";
+$REMOVED_FILE = "/tmp/#cvs.files.removed";
+$LOG_FILE = "/tmp/#cvs.files.log";
+$FILE_PREFIX = "#cvs.files";
+
+$VERSION_FILE = "version";
+$TRUNKREV_FILE = "TrunkRev";
+$CHANGES_FILE = "Changes";
+$CHANGES_TEMP = "Changes.tmp";
+
+############################################################
+#
+# Subroutines
+#
+############################################################
+
+sub format_names {
+ local($dir, @files) = @_;
+ local(@lines);
+ $lines[0] = sprintf(" %-08s", $dir);
+ foreach $file (@files) {
+ if (length($lines[$#lines]) + length($file) > 60) {
+ $lines[++$#lines] = sprintf(" %8s", " ");
+ }
+ $lines[$#lines] .= " ".$file;
+ }
+ @lines;
+}
+
+sub cleanup_tmpfiles {
+ local($all) = @_;
+ local($wd, @files);
+
+ $wd = `pwd`;
+ chdir("/tmp");
+ opendir(DIR, ".");
+ if ($all == 1) {
+ push(@files, grep(/$id$/, readdir(DIR)));
+ } else {
+ push(@files, grep(/^$FILE_PREFIX.*$id$/, readdir(DIR)));
+ }
+ closedir(DIR);
+ foreach (@files) {
+ unlink $_;
+ }
+ chdir($wd);
+}
+
+sub write_logfile {
+ local($filename, @lines) = @_;
+ open(FILE, ">$filename") || die ("Cannot open log file $filename.\n");
+ print(FILE join("\n", @lines), "\n");
+ close(FILE);
+}
+
+sub append_to_file {
+ local($filename, $dir, @files) = @_;
+ if (@files) {
+ local(@lines) = &format_names($dir, @files);
+ open(FILE, ">>$filename") || die ("Cannot open file $filename.\n");
+ print(FILE join("\n", @lines), "\n");
+ close(FILE);
+ }
+}
+
+sub write_line {
+ local($filename, $line) = @_;
+ open(FILE, ">$filename") || die("Cannot open file $filename.\n");
+ print(FILE $line, "\n");
+ close(FILE);
+}
+
+sub read_line {
+ local($line);
+ local($filename) = @_;
+ open(FILE, "<$filename") || die("Cannot open file $filename.\n");
+ $line = <FILE>;
+ close(FILE);
+ chop($line);
+ $line;
+}
+
+sub read_file {
+ local(@text);
+ local($filename, $leader) = @_;
+ open(FILE, "<$filename") || return ();
+ while (<FILE>) {
+ chop;
+ push(@text, sprintf(" %-10s %s", $leader, $_));
+ $leader = "";
+ }
+ close(FILE);
+ @text;
+}
+
+sub read_logfile {
+ local(@text);
+ local($filename, $leader) = @_;
+ open(FILE, "<$filename") || die ("Cannot open log file $filename.\n");
+ while (<FILE>) {
+ chop;
+ push(@text, $leader.$_);
+ }
+ close(FILE);
+ @text;
+}
+
+sub bump_version {
+ local($trunkrev, $editnum, $version);
+
+ $trunkrev = &read_line("$ENV{'CVSROOT'}/$repository/$TRUNKREV_FILE");
+ $editnum = &read_line("$ENV{'CVSROOT'}/$repository/$VERSION_FILE");
+ &write_line("$ENV{'CVSROOT'}/$repository/$VERSION_FILE", $editnum+1);
+ $version = $trunkrev . "(" . $editnum . ")";
+}
+
+sub build_header {
+ local($version) = @_;
+ local($header);
+ local($sec,$min,$hour,$mday,$mon,$year) = localtime(time);
+ $header = sprintf("%-8s %s %02d/%02d/%02d %02d:%02d:%02d",
+ $login, $version, $year%100, $mon+1, $mday,
+ $hour, $min, $sec);
+}
+
+sub do_changes_file {
+ local($changes, $tmpchanges);
+ local(@text) = @_;
+
+ $changes = "$ENV{'CVSROOT'}/$repository/$CHANGES_FILE";
+ $tmpchanges = "$ENV{'CVSROOT'}/$repository/$CHANGES_TEMP";
+ if (rename($changes, $tmpchanges) != 1) {
+ die("Cannot rename $changes to $tmpchanges.\n");
+ }
+ open(CHANGES, ">$changes") || die("Cannot open $changes.\n");
+ open(TMPCHANGES, "<$tmpchanges") || die("Cannot open $tmpchanges.\n");
+ print(CHANGES join("\n", @text), "\n\n");
+ print(CHANGES <TMPCHANGES>);
+ close(CHANGES);
+ close(TMPCHANGES);
+ unlink($tmpchanges);
+}
+
+sub mail_notification {
+ local($name, @text) = @_;
+ open(MAIL, "| mail -s \"Source Repository Modification\" $name");
+ print(MAIL join("\n", @text));
+ close(MAIL);
+}
+
+#############################################################
+#
+# Main Body
+#
+############################################################
+
+#
+# Initialize basic variables
+#
+$id = getpgrp();
+$state = $STATE_NONE;
+$login = getlogin || (getpwuid($<))[0] || die("Unknown user $<.\n");
+@files = split(' ', $ARGV[0]);
+@path = split('/', $files[0]);
+$repository = @path[0];
+if ($#path == 0) {
+ $dir = ".";
+} else {
+ $dir = join('/', @path[1..$#path]);
+}
+#print("ARGV - ", join(":", @ARGV), "\n");
+#print("files - ", join(":", @files), "\n");
+#print("path - ", join(":", @path), "\n");
+#print("dir - ", $dir, "\n");
+#print("id - ", $id, "\n");
+
+#
+# Check for a new directory first. This will always appear as a
+# single item in the argument list, and an empty log message.
+#
+if ($ARGV[0] =~ /New directory/) {
+ $version = &bump_version if ($cisco_systems != 0);
+ $header = &build_header($version);
+ @text = ();
+ push(@text, $header);
+ push(@text, "");
+ push(@text, " ".$ARGV[0]);
+ &do_changes_file(@text) if ($cisco_systems != 0);
+ &mail_notification($mailto, @text);
+ exit 0;
+}
+
+#
+# Iterate over the body of the message collecting information.
+#
+while (<STDIN>) {
+ chop; # Drop the newline
+ if (/^Modified Files/) { $state = $STATE_CHANGED; next; }
+ if (/^Added Files/) { $state = $STATE_ADDED; next; }
+ if (/^Removed Files/) { $state = $STATE_REMOVED; next; }
+ if (/^Log Message/) { $state = $STATE_LOG; next; }
+ s/^[ \t\n]+//; # delete leading space
+ s/[ \t\n]+$//; # delete trailing space
+
+ push (@changed_files, split) if ($state == $STATE_CHANGED);
+ push (@added_files, split) if ($state == $STATE_ADDED);
+ push (@removed_files, split) if ($state == $STATE_REMOVED);
+ push (@log_lines, $_) if ($state == $STATE_LOG);
+}
+
+#
+# Strip leading and trailing blank lines from the log message. Also
+# compress multiple blank lines in the body of the message down to a
+# single blank line.
+#
+while ($#log_lines > -1) {
+ last if ($log_lines[0] ne "");
+ shift(@log_lines);
+}
+while ($#log_lines > -1) {
+ last if ($log_lines[$#log_lines] ne "");
+ pop(@log_lines);
+}
+for ($i = $#log_lines; $i > 0; $i--) {
+ if (($log_lines[$i - 1] eq "") && ($log_lines[$i] eq "")) {
+ splice(@log_lines, $i, 1);
+ }
+}
+
+#
+# Find the log file that matches this log message
+#
+for ($i = 0; ; $i++) {
+ last if (! -e "$LOG_FILE.$i.$id");
+ @text = &read_logfile("$LOG_FILE.$i.$id", "");
+ last if ($#text == -1);
+ last if (join(" ", @log_lines) eq join(" ", @text));
+}
+
+#
+# Spit out the information gathered in this pass.
+#
+&write_logfile("$LOG_FILE.$i.$id", @log_lines);
+&append_to_file("$ADDED_FILE.$i.$id", $dir, @added_files);
+&append_to_file("$CHANGED_FILE.$i.$id", $dir, @changed_files);
+&append_to_file("$REMOVED_FILE.$i.$id", $dir, @removed_files);
+
+#
+# Check whether this is the last directory. If not, quit.
+#
+$_ = &read_line("$LAST_FILE.$id");
+exit 0 if (! grep(/$files[0]$/, $_));
+
+#
+# This is it. The commits are all finished. Lump everything together
+# into a single message, fire a copy off to the mailing list, and drop
+# it on the end of the Changes file.
+#
+# Get the full version number
+#
+$version = &bump_version if ($cisco_systems != 0);
+$header = &build_header($version);
+
+#
+# Produce the final compilation of the log messages
+#
+@text = ();
+push(@text, $header);
+push(@text, "");
+for ($i = 0; ; $i++) {
+ last if (! -e "$LOG_FILE.$i.$id");
+ push(@text, &read_file("$CHANGED_FILE.$i.$id", "Modified:"));
+ push(@text, &read_file("$ADDED_FILE.$i.$id", "Added:"));
+ push(@text, &read_file("$REMOVED_FILE.$i.$id", "Removed:"));
+ push(@text, " Log:");
+ push(@text, &read_logfile("$LOG_FILE.$i.$id", " "));
+ push(@text, "");
+}
+if ($cisco_systems != 0) {
+ @ddts = grep(/^CSCdi/, split(' ', join(" ", @text)));
+ $text[0] .= " " . join(" ", @ddts);
+}
+#
+# Put the log message at the beginning of the Changes file and mail
+# out the notification.
+#
+&do_changes_file(@text) if ($cisco_systems != 0);
+&mail_notification($mailto, @text);
+&cleanup_tmpfiles(1);
+exit 0;
diff --git a/gnu/usr.bin/cvs/contrib/mfpipe.pl b/gnu/usr.bin/cvs/contrib/mfpipe.pl
new file mode 100644
index 0000000..74cc5e1
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/mfpipe.pl
@@ -0,0 +1,87 @@
+#!/usr/bin/perl
+#
+# From: clyne@niwot.scd.ucar.EDU (John Clyne)
+# Date: Fri, 28 Feb 92 09:54:21 MST
+#
+# BTW, i wrote a perl script that is similar to 'nfpipe' except that in
+# addition to logging to a file it provides a command line option for mailing
+# change notices to a group of users. Obviously you probably wouldn't want
+# to mail every change. But there may be certain directories that are commonly
+# accessed by a group of users who would benefit from an email notice.
+# Especially if they regularly beat on the same directory. Anyway if you
+# think anyone would be interested here it is.
+#
+# mfpipe.pl,v 1.1 1992/03/02 01:22:41 berliner Exp
+#
+#
+# File: mfpipe
+#
+# Author: John Clyne
+# National Center for Atmospheric Research
+# PO 3000, Boulder, Colorado
+#
+# Date: Wed Feb 26 18:34:53 MST 1992
+#
+# Description: Tee standard input to mail a list of users and to
+# a file. Used by CVS logging.
+#
+# Usage: mfpipe [-f file] [user@host...]
+#
+# Environment: CVSROOT
+# Path to CVS root.
+#
+# Files:
+#
+#
+# Options: -f file
+# Capture output to 'file'
+#
+
+$header = "Log Message:\n";
+
+$mailcmd = "| mail -s 'CVS update notice'";
+$whoami = `whoami`;
+chop $whoami;
+$date = `date`;
+chop $date;
+
+$cvsroot = $ENV{'CVSROOT'};
+
+while (@ARGV) {
+ $arg = shift @ARGV;
+
+ if ($arg eq '-f') {
+ $file = shift @ARGV;
+ }
+ else {
+ $users = "$users $arg";
+ }
+}
+
+if ($users) {
+ $mailcmd = "$mailcmd $users";
+ open(MAIL, $mailcmd) || die "Execing $mail: $!\n";
+}
+
+if ($file) {
+ $logfile = "$cvsroot/LOG/$file";
+ open(FILE, ">> $logfile") || die "Opening $logfile: $!\n";
+}
+
+print FILE "$whoami $date--------BEGIN LOG ENTRY-------------\n" if ($logfile);
+
+while (<>) {
+ print FILE $log if ($log && $logfile);
+
+ print FILE $_ if ($logfile);
+ print MAIL $_ if ($users);
+
+ $log = "log: " if ($_ eq $header);
+}
+
+close FILE;
+die "Write failed" if $?;
+close MAIL;
+die "Mail failed" if $?;
+
+exit 0;
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/ChangeLog b/gnu/usr.bin/cvs/contrib/pcl-cvs/ChangeLog
new file mode 100644
index 0000000..fab9a7d
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/ChangeLog
@@ -0,0 +1,119 @@
+Tue Apr 7 09:11:27 1992 Per Cederqvist (ceder@leopold)
+
+ * Release 1.02.
+
+ * pcl-cvs.el (cvs-diff-backup, cvs-edit-done, cvs-status): Call
+ save-some-buffers.
+
+ * pcl-cvs.el (cvs-diff-backup-extractor): Fixed syntax error.
+
+ * Makefile, README, compile-all.el, dist-makefile, pcl-cvs.el,
+ pcl-cvs.texinfo (XXRELEASEXX): A magic string that is substituted
+ for the current release number when a distribution is made.
+ (Release 1.01 says that it is release 1.00).
+
+ * pcl-cvs.el (cvs-find-file): Added missing pair of parenthesis.
+
+Mon Mar 30 14:25:26 1992 Per Cederqvist (ceder@leopold)
+
+ * Release 1.01.
+
+ * pcl-cvs.el (cvs-parse-buffer): The message when waiting for a
+ lock has been changed.
+
+Sun Mar 29 05:29:57 1992 Per Cederqvist (ceder@leopold)
+
+ * Release 1.00.
+
+ * pcl-cvs.el (cvs-do-update, cvs-sentinel, cvs-parse-buffer):
+ Major rewrite of buffer and window selection and handling.
+ The *cvs* buffer is now killed whenever a new "cvs update" is
+ initiated. The -update buffer is replaced with the *cvs*
+ buffer when the update is completed.
+
+Sat Mar 28 21:03:05 1992 Per Cederqvist (ceder@robin)
+
+ * pcl-cvs.el (cvs-delete-unused-temporary-buffers): Fixed it.
+
+ * pcl-cvs.el (cvs-auto-remove-handled): New variable.
+ * pcl-cvs.el (cvs-edit-done): Use it.
+ * pcl-cvs.texinfo (Customization, Removing handled entries):
+ Document it.
+
+ * pcl-cvs.el (cvs-mode): Turn of the undo feature. It really
+ isn't useful in a cookie buffer...
+
+ * pcl-cvs.el (cvs-edit-done): Committing a file now looks more
+ like diffing a file. The window handling is better.
+ * pcl-cvs.el (cvs-use-temp-buffer): The &optional switch is no
+ longer needed.
+
+Mon Mar 23 00:20:33 1992 Per Cederqvist (ceder@robin)
+
+ * Release 0.97.
+
+ * pcl-cvs.el (default-directory): Make sure it always ends in a
+ slash. fileinfo->dir does NOT end in a slash, and I had forgotten
+ to call file-name-as-directory in various places.
+
+ * pcl-cvs.el (cvs-diff-backup-extractor): Signal an error if a
+ fileinfo without backup file is given.
+
+ * pcl-cvs.el (cvs-mode): Added documentation.
+
+ * pcl-cvs.el (cvs-execute-list): Fix the order of files in the
+ same directory.
+
+ * pcl-cvs.el (cvs-log-flags, cvs-status-flags): New variables.
+ * pcl-cvs.el (cvs-log, cvs-status): Use them.
+ * pcl-cvs.texinfo (Customization): Document them.
+
+ * pcl-cvs.el (cvs-diff-backup): Filter non-backup-diffable files
+ at an earlier stage, like cvs-commit does.
+
+ * pcl-cvs.el (cvs-diff-flags): New variable.
+ * pcl-cvs.el (cvs-diff-backup): Use it.
+ * pcl-cvs.texinfo (Customization): Document it.
+
+ * pcl-cvs.el (cvs-execute-single-file-list): Remove &rest before
+ last argument. No callers needed updating.
+
+ * pcl-cvs.el (cvs-execute-list): Remove the &rest before the last
+ argument (constant-args). Update all callers of cvs-execute-list
+ to use the new calling convention.
+ * pcl-cvs.el (cvs-cvs-diff-flags): Now a list of strings instead
+ of a string.
+ * pcl-cvs.texinfo (Customization): Document the change to
+ cvs-cvs-diff-flags.
+
+ * Release 0.96.
+
+ * pcl-cvs.el (cvs-cvs-diff-flags): New variable.
+ * pcl-cvs.el (cvs-diff-cvs): Use it.
+ * pcl-cvs.texinfo (Customization, Viewing differences): Document it.
+
+ * pcl-cvs.el (cvs-use-temp-buffe): Don't switch to the temporary
+ buffer. Use display-buffer and set-buffer instead. This way
+ cvs-log, cvs-status, cvs-diff-cvs and friends don't select the
+ temporary buffer. The cursor will remain in the *cvs* buffer.
+
+Sun Mar 22 21:50:18 1992 Per Cederqvist (ceder@robin)
+
+ * pcl-cvs.el (cvs-find-file, cvs-find-file-other-window): Don't
+ prompt when reading in a directory in dired.
+
+ * Makefile (pcl-cvs-$(VER)): Include pcl-cvs-startup.el in the
+ distribution.
+
+ * dist-makefile (pcl-cvs.dvi): Don't fail even if texindex does
+ not exist.
+
+ * pcl-cvs.texinfo (@setchapternewpage): Changed from 'off' to 'on'.
+ * pcl-cvs.texinfo (Variable index): Joined into function index.
+ * pcl-cvs.texinfo (Key index): add a description about the key.
+ * pcl-cvs.texinfo: Many other small changes.
+
+Wed Mar 18 01:58:38 1992 Per Cederqvist (ceder@leopold)
+
+ * Use GNU General Public License version 2.
+
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/INSTALL b/gnu/usr.bin/cvs/contrib/pcl-cvs/INSTALL
new file mode 100644
index 0000000..8c89053
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/INSTALL
@@ -0,0 +1,83 @@
+This text is copied from the TeXinfo manual for pcl-cvs.
+
+Installation of the pcl-cvs program
+===================================
+
+ 1. Edit the file `Makefile' to reflect the situation at your site.
+ The only things you have to change is the definition of
+ `lispdir' and `infodir'. The elisp files will be copied to
+ `lispdir', and the info file to `infodir'.
+
+ 2. Configure pcl-cvs.el
+
+ There are a couple of paths that you have to check to make
+ sure that they match you system. They appear early in the file
+ pcl-cvs.el.
+
+ *NOTE:* If your system is running emacs 18.57 or earlier
+ you MUST uncomment the line that says:
+
+ (setq delete-exited-processes nil)
+
+ Setting `delete-exited-processes' to `nil' works around a bug
+ in emacs that causes it to dump core. The bug was fixed in
+ emacs 18.58.
+
+ 3. Type `make install' in the source directory. This will
+ byte-compile all `.el' files and copy both the `.el' and the
+ `.elc' into the directory you specified in step 1.
+
+ If you don't want to install the `.el' files but only the
+ `.elc' files (the byte-compiled files), you can type ``make
+ install_elc'' instead of ``make install''.
+
+ If you only want to create the compiled elisp files, but
+ don't want to install them, you can type `make elcfiles'
+ instead. This is what happens if you only type `make' without
+ parameters.
+
+ 4. Edit the file `default.el' in your emacs lisp directory (usually
+ `/usr/gnu/emacs/lisp' or something similar) and enter the
+ contents of the file `pcl-cvs-startup.el' into it. It contains
+ a couple of `auto-load's that facilitates the use of pcl-cvs.
+
+
+
+
+Installation of the on-line manual.
+===================================
+
+ 1. Create the info file `pcl-cvs' from `pcl-cvs.texinfo' by typing
+ `make info'. If you don't have the program `makeinfo' you can
+ get it by anonymous ftp from e.g. `ftp.gnu.ai.mit.edu' as
+ `pub/gnu/texinfo-2.14.tar.Z' (there might be a newer version
+ there when you read this), or you could use the preformatted
+ info file `pcl-cvs.info' that is included in the distribution
+ (type `cp pcl-cvs.info pcl-cvs').
+
+ 2. Move the info file `pcl-cvs' to your standard info directory.
+ This might be called something like `/usr/gnu/emacs/info'.
+
+ 3. Edit the file `dir' in the info directory and enter one line to
+ contain a pointer to the info file `pcl-cvs'. The line can, for
+ instance, look like this:
+
+ * Pcl-cvs: (pcl-cvs). An Emacs front-end to CVS.
+
+
+
+
+How to make typeset documentation from pcl-cvs.texinfo
+======================================================
+
+ If you have TeX installed at your site, you can make a typeset
+manual from `pcl-cvs.texinfo'.
+
+ 1. Run TeX by typing ``make pcl-cvs.dvi''. You will not get the
+ indices unless you have the `texindex' program.
+
+ 2. Convert the resulting device independent file `pcl-cvs.dvi' to a
+ form which your printer can output and print it. If you have a
+ postscript printer there is a program, `dvi2ps', which does.
+ There is also a program which comes together with TeX, `dvips',
+ which you can use.
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/Makefile b/gnu/usr.bin/cvs/contrib/pcl-cvs/Makefile
new file mode 100644
index 0000000..f0ded69
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/Makefile
@@ -0,0 +1,78 @@
+# Makefile,v 1.2 1992/04/07 20:49:07 berliner Exp
+# Makefile for pcl-cvs release 1.02.
+# Copyright (C) 1992 Per Cederqvist
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# This is the directory in which the ELFILES and ELCFILES will be
+# installed.
+
+lispdir = /usr/local/lib/elisp
+
+# Where to install the info file.
+
+prefix=/usr/local
+infodir = $(prefix)/info
+
+#
+# The rest of this file should not need to be modified.
+#
+
+# Just in case...
+SHELL = /bin/sh
+
+ELFILES = pcl-cvs.el cookie.el elib-dll.el elib-node.el
+ELCFILES = pcl-cvs.elc cookie.elc elib-dll.elc elib-node.elc
+INFOFILES = pcl-cvs
+TEXTMPS = pcl-cvs.aux pcl-cvs.log pcl-cvs.toc pcl-cvs.dvi pcl-cvs.cp \
+ pcl-cvs.fn pcl-cvs.vr pcl-cvs.tp pcl-cvs.ky pcl-cvs.pg \
+ pcl-cvs.cps pcl-cvs.fns pcl-cvs.kys pcl-cvs.pgs pcl-cvs.tps \
+ pcl-cvs.vrs
+
+INSTALL = install
+INSTALL_DATA = $(INSTALL)
+
+elcfiles:
+ emacs -batch -l ./compile-all.el -f compile-pcl-cvs
+
+all: elcfiles info
+
+# Don't install the info file yet, since it requires makeinfo
+# version 2.something (and version 1.something is distributed with emacs).
+#
+# install: install_elc install_info
+install: install_elc
+ for i in $(ELFILES); do $(INSTALL_DATA) $$i $(lispdir)/$$i; done
+
+install_elc: elcfiles
+ for i in $(ELCFILES); do $(INSTALL_DATA) $$i $(lispdir)/$$i; done
+
+install_info: pcl-cvs
+ $(INSTALL_DATA) pcl-cvs $(infodir)/pcl-cvs
+
+info pcl-cvs: pcl-cvs.texinfo
+ makeinfo +fill-column=70 pcl-cvs.texinfo
+
+pcl-cvs.dvi: pcl-cvs.texinfo
+ tex pcl-cvs.texinfo
+ -texindex pcl-cvs.cp pcl-cvs.fn pcl-cvs.vr pcl-cvs.tp pcl-cvs.ky \
+ pcl-cvs.pg
+ tex pcl-cvs.texinfo
+
+mostlyclean clean realclean:
+ rm -f *~ core $(ELCFILES) $(INFOFILES) $(TEXTMPS)
+
+tags TAGS:
+ etags *.el
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/README b/gnu/usr.bin/cvs/contrib/pcl-cvs/README
new file mode 100644
index 0000000..6f0a5fe
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/README
@@ -0,0 +1,14 @@
+README,v 1.2 1992/04/07 20:49:09 berliner Exp
+
+This is the readme file for pcl-cvs, release 1.02.
+
+Pcl-cvs is a front-end to CVS version 1.3. It integrates the most
+frequently used CVS commands into emacs.
+
+There is some configuration that needs to be done in pcl-cvs.el to get
+it to work. See the instructions in file INSTALL.
+
+Full documentation is in pcl-cvs.texinfo. Since it requires makeinfo
+2.14 a preformatted info file is also included (pcl-cvs.info).
+
+ ceder@lysator.liu.se
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/compile-all.el b/gnu/usr.bin/cvs/contrib/pcl-cvs/compile-all.el
new file mode 100644
index 0000000..74f1bca
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/compile-all.el
@@ -0,0 +1,52 @@
+;;;; compile-all.el,v 1.2 1992/04/07 20:49:10 berliner Exp
+;;;; This file byte-compiles all .el files in pcl-cvs release 1.02.
+;;;;
+;;;; Copyright (C) 1991 Inge Wallin
+;;;;
+;;;; This file is part of the GNU Emacs lisp library, Elib.
+;;;;
+;;;; GNU Elib is free software; you can redistribute it and/or modify
+;;;; it under the terms of the GNU General Public License as published by
+;;;; the Free Software Foundation; either version 1, or (at your option)
+;;;; any later version.
+;;;;
+;;;; GNU Elib is distributed in the hope that it will be useful,
+;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;;; GNU General Public License for more details.
+;;;;
+;;;; You should have received a copy of the GNU General Public License
+;;;; along with GNU Emacs; see the file COPYING. If not, write to
+;;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;;;;
+
+
+(setq elib-files '("elib-node"
+ "elib-dll"
+ "cookie"
+ "pcl-cvs"))
+
+
+(defun compile-file-if-necessary (file)
+ "Compile the Elib file FILE if necessary.
+
+This is done if FILE.el is newer than FILE.elc or if FILE.elc doesn't exist."
+ (let ((el-name (concat file ".el"))
+ (elc-name (concat file ".elc")))
+ (if (or (not (file-exists-p elc-name))
+ (file-newer-than-file-p el-name elc-name))
+ (progn
+ (message (format "Byte-compiling %s..." el-name))
+ (byte-compile-file el-name)))))
+
+
+(defun compile-pcl-cvs ()
+ "Byte-compile all uncompiled files of elib.
+Be sure to have . in load-path since a number of files in elib
+depend on other files and we always want the newer one even if
+a previous version of elib exists."
+
+ (interactive)
+ (setq load-path (append '(".") load-path))
+ (mapcar (function compile-file-if-necessary)
+ elib-files))
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/cookie.el b/gnu/usr.bin/cvs/contrib/pcl-cvs/cookie.el
new file mode 100644
index 0000000..8bd4bdf
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/cookie.el
@@ -0,0 +1,884 @@
+;;; cookie.el,v 1.2 1992/04/07 20:49:12 berliner Exp
+;;; cookie.el -- Utility to display cookies in buffers
+;;; Copyright (C) 1991, 1992 Per Cederqvist
+;;;
+;;; This program is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 2 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; This program is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with this program; if not, write to the Free Software
+;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;;;; TO-DO: Byt namn! tin -> wrapper (eller n}got b{ttre).
+
+;;; Note that this file is still under development. Comments,
+;;; enhancements and bug fixes are welcome.
+;;; Send them to ceder@lysator.liu.se.
+
+(defun impl nil (error "Not yet implemented!"))
+
+;;; Cookie is a package that imlements a connection between an
+;;; elib-dll and the contents of a buffer. Possible uses are dired
+;;; (have all files in a list, and show them), buffer-list,
+;;; kom-prioritize (in the LysKOM elisp client) and others. pcl-cvs.el
+;;; uses cookie.el.
+;;;
+;;; A cookie buffer contains a header, any number of cookies, and a
+;;; footer. The header and footer are constant strings that are given
+;;; to cookie-create when the buffer is placed under cookie. Each cookie
+;;; is displayed in the buffer by calling a user-supplied function
+;;; that takes a cookie and returns a string. The string may be
+;;; empty, or contain any number of lines. An extra newline is always
+;;; appended unless the string is empty.
+;;;
+;;; Cookie does not affect the mode of the buffer in any way. It
+;;; merely makes it easy to connect an underlying data representation
+;;; to the buffer contents.
+;;;
+;;; The cookie-node data type:
+;;; start-marker
+;;; ;; end-marker This field is no longer present.
+;;; cookie The user-supplied element.
+;;;
+;;; A dll of cookie-nodes are held in the buffer local variable
+;;; cake-tin.
+;;;
+;;; A tin is an object that contains one cookie. You can get the next
+;;; and previous tin.
+;;;
+
+(require 'elib-dll)
+(provide 'cookie)
+
+(defvar cookies nil
+ "A doubly linked list that contains the underlying data representation
+for the contents of a cookie buffer. The package elib-dll is used to
+manipulate this list.")
+
+(defvar cookie-pretty-printer nil
+ "The function that is used to pretty-print a cookie in this buffer.")
+
+(defvar cookie-header nil
+ "The tin that holds the header cookie.")
+
+(defvar cookie-footer nil
+ "The tin that holds the footer cookie.")
+
+(defvar cookie-last-tin nil
+ "The tin the cursor was positioned at, the last time the cookie
+package checked the cursor position. Buffer local in all buffers
+the cookie package works on. You may set this if your package
+thinks it knows where the cursor will be the next time this
+package is called. It can speed things up.
+
+It must never be set to a tin that has been deleted.")
+
+;;; ================================================================
+;;; Internal functions for use in the cookie package
+
+(put 'cookie-set-buffer 'lisp-indent-hook 1)
+
+(defmacro cookie-set-buffer (buffer &rest forms)
+
+ ;; Execute FORMS with BUFFER selected as current buffer.
+ ;; Return value of last form in FORMS. INTERNAL USE ONLY.
+
+ (let ((old-buffer (make-symbol "old-buffer")))
+ (` (let (((, old-buffer) (current-buffer)))
+ (set-buffer (get-buffer-create (, buffer)))
+ (unwind-protect
+ (progn (,@ forms))
+ (set-buffer (, old-buffer)))))))
+
+
+(defmacro cookie-filter-hf (tin)
+
+ ;; Evaluate TIN once and return it. BUT if it is
+ ;; equal to cookie-header or cookie-footer return nil instead.
+ ;; INTERNAL USE ONLY.
+
+ (let ((tempvar (make-symbol "tin")))
+ (` (let (((, tempvar) (, tin)))
+ (if (or (eq (, tempvar) cookie-header)
+ (eq (, tempvar) cookie-footer))
+ nil
+ (, tempvar))))))
+
+
+;;; cookie-tin
+;;; Constructor:
+
+(defun cookie-create-tin (start-marker
+ cookie)
+ ;; Create a tin. INTERNAL USE ONLY.
+ (cons 'COOKIE-TIN (vector start-marker nil cookie)))
+
+
+;;; Selectors:
+
+(defun cookie-tin-start-marker (cookie-tin)
+ ;; Get start-marker from cookie-tin. INTERNAL USE ONLY.
+ (elt (cdr cookie-tin) 0))
+
+;(defun cookie-tin-end-marker (cookie-tin)
+; ;;Get end-marker from cookie-tin. INTERNAL USE ONLY.
+; (elt (cdr cookie-tin) 1))
+
+(defun cookie-tin-cookie-safe (cookie-tin)
+ ;; Get cookie from cookie-tin. INTERNAL USE ONLY.
+ ;; Returns nil if given nil as input.
+ ;; This is the same as cookie-tin-cookie in version 18.57
+ ;; of emacs, but elt should signal an error when given nil
+ ;; as input (according to the info files).
+ (elt (cdr cookie-tin) 2))
+
+(defun cookie-tin-cookie (cookie-tin)
+ ;; Get cookie from cookie-tin. INTERNAL USE ONLY.
+ (elt (cdr cookie-tin) 2))
+
+
+;;; Modifiers:
+
+(defun set-cookie-tin-start-marker (cookie-tin newval)
+ ;; Set start-marker in cookie-tin to NEWVAL. INTERNAL USE ONLY.
+ (aset (cdr cookie-tin) 0 newval))
+
+;(defun set-cookie-tin-end-marker (cookie-tin newval)
+; ;; Set end-marker in cookie-tin to NEWVAL. INTERNAL USE ONLY.
+; (aset (cdr cookie-tin) 1 newval))
+
+(defun set-cookie-tin-cookie (cookie-tin newval)
+ ;; Set cookie in cookie-tin to NEWVAL. INTERNAL USE ONLY.
+ (aset (cdr cookie-tin) 2 newval))
+
+
+
+;;; Predicate:
+
+(defun cookie-tin-p (object)
+ ;; Return t if OBJECT is a tin. INTERNAL USE ONLY.
+ (eq (car-safe object) 'COOKIE-TIN))
+
+;;; end of cookie-tin data type.
+
+
+(defun cookie-create-tin-and-insert (cookie string pos)
+ ;; Insert STRING at POS in current buffer. Remember start
+ ;; position. Create a tin containing them and the COOKIE.
+ ;; INTERNAL USE ONLY.
+
+ (save-excursion
+ (goto-char pos)
+ ;; Remember the position as a number so that it doesn't move
+ ;; when we insert the string.
+ (let ((start (if (markerp pos)
+ (marker-position pos)
+ pos)))
+ ;; Use insert-before-markers so that the marker for the
+ ;; next cookie is updated.
+ (insert-before-markers string)
+ (insert-before-markers ?\n)
+ (cookie-create-tin (copy-marker start) cookie))))
+
+
+(defun cookie-delete-tin-internal (tin)
+ ;; Delete a cookie from the buffer. INTERNAL USE ONLY.
+ ;; Can not be used on the footer.
+ (delete-region (cookie-tin-start-marker (dll-element cookies tin))
+ (cookie-tin-start-marker
+ (dll-element cookies
+ (dll-next cookies tin)))))
+
+
+
+(defun cookie-refresh-tin (tin)
+ ;; Redisplay the cookie represented by TIN. INTERNAL USE ONLY.
+ ;; Can not be used on the footer.
+
+ (save-excursion
+ ;; First, remove the string:
+ (delete-region (cookie-tin-start-marker (dll-element cookies tin))
+ (1- (marker-position
+ (cookie-tin-start-marker
+ (dll-element cookies
+ (dll-next cookies tin))))))
+
+ ;; Calculate and insert the string.
+
+ (goto-char (cookie-tin-start-marker (dll-element cookies tin)))
+ (insert
+ (funcall cookie-pretty-printer
+ (cookie-tin-cookie (dll-element cookies tin))))))
+
+
+;;; ================================================================
+;;; The public members of the cookie package
+
+
+(defun cookie-cookie (buffer tin)
+ "Get the cookie from a TIN. Args: BUFFER TIN."
+ (cookie-set-buffer buffer
+ (cookie-tin-cookie (dll-element cookies tin))))
+
+
+
+
+(defun cookie-create (buffer pretty-printer &optional header footer)
+
+ "Start to use the cookie package in BUFFER.
+BUFFER may be a buffer or a buffer name. It is created if it does not exist.
+Beware that the entire contents of the buffer will be erased.
+PRETTY-PRINTER is a function that takes one cookie and returns a string
+to be displayed in the buffer. The string may be empty. If it is not
+empty a newline will be added automatically. It may span several lines.
+Optional third argument HEADER is a string that will always be present
+at the top of the buffer. HEADER should end with a newline. Optionaly
+fourth argument FOOTER is similar, and will always be inserted at the
+bottom of the buffer."
+
+ (cookie-set-buffer buffer
+
+ (erase-buffer)
+
+ (make-local-variable 'cookie-last-tin)
+ (make-local-variable 'cookie-pretty-printer)
+ (make-local-variable 'cookie-header)
+ (make-local-variable 'cookie-footer)
+ (make-local-variable 'cookies)
+
+ (setq cookie-last-tin nil)
+ (setq cookie-pretty-printer pretty-printer)
+ (setq cookies (dll-create))
+
+ (dll-enter-first cookies
+ (cookie-create-tin-and-insert
+ header header 0))
+ (setq cookie-header (dll-nth cookies 0))
+
+ (dll-enter-last cookies
+ (cookie-create-tin-and-insert
+ footer footer (point-max)))
+ (setq cookie-footer (dll-nth cookies -1))
+
+ (goto-char (point-min))
+ (forward-line 1)))
+
+
+(defun cookie-set-header (buffer header)
+ "Change the header. Args: BUFFER HEADER."
+ (impl))
+
+
+(defun cookie-set-footer (buffer header)
+ "Change the footer. Args: BUFFER FOOTER."
+ (impl))
+
+
+
+(defun cookie-enter-first (buffer cookie)
+ "Enter a COOKIE first in BUFFER.
+Args: BUFFER COOKIE."
+
+ (cookie-set-buffer buffer
+
+ ;; It is always safe to insert an element after the first element,
+ ;; because the header is always present. (dll-nth cookies 0) should
+ ;; never return nil.
+
+ (dll-enter-after
+ cookies
+ (dll-nth cookies 0)
+ (cookie-create-tin-and-insert
+ cookie
+ (funcall cookie-pretty-printer cookie)
+ (cookie-tin-start-marker
+ (dll-element cookies (dll-nth cookies 1)))))))
+
+
+
+(defun cookie-enter-last (buffer cookie)
+ "Enter a COOKIE last in BUFFER.
+Args: BUFFER COOKIE."
+
+ (cookie-set-buffer buffer
+
+ ;; Remember that the header and footer are always present. There
+ ;; is no need to check if (dll-nth cookies -2) returns nil.
+
+ (dll-enter-before
+ cookies
+ (dll-nth cookies -1)
+ (cookie-create-tin-and-insert
+ cookie
+ (funcall cookie-pretty-printer cookie)
+ (cookie-tin-start-marker (dll-last cookies))))))
+
+
+(defun cookie-enter-after (buffer node cookie)
+ (impl))
+
+
+(defun cookie-enter-before (buffer node cookie)
+ (impl))
+
+
+
+(defun cookie-next (buffer tin)
+ "Get the next tin. Args: BUFFER TIN.
+Returns nil if TIN is nil or the last cookie."
+ (if tin
+ (cookie-set-buffer buffer
+ (cookie-filter-hf (dll-next cookies tin)))))
+
+
+
+(defun cookie-previous (buffer tin)
+ "Get the previous tin. Args: BUFFER TIN.
+Returns nil if TIN is nil or the first cookie."
+ (if tin
+ (cookie-set-buffer buffer
+ (cookie-filter-hf (dll-previous cookies tin)))))
+
+
+(defun cookie-nth (buffer n)
+
+ "Return the Nth tin. Args: BUFFER N.
+N counts from zero. Nil is returned if there is less than N cookies.
+If N is negative, return the -(N+1)th last element.
+Thus, (cookie-nth dll 0) returns the first node,
+and (cookie-nth dll -1) returns the last node.
+
+Use cookie-cookie to extract the cookie from the tin."
+
+ (cookie-set-buffer buffer
+
+ ;; Skip the header (or footer, if n is negative).
+ (if (< n 0)
+ (setq n (1- n))
+ (setq n (1+ n)))
+
+ (cookie-filter-hf (dll-nth cookies n))))
+
+
+
+(defun cookie-delete (buffer tin)
+ "Delete a cookie. Args: BUFFER TIN."
+
+ (cookie-set-buffer buffer
+ (if (eq cookie-last-tin tin)
+ (setq cookie-last-tin nil))
+
+ (cookie-delete-tin-internal tin)
+ (dll-delete cookies tin)))
+
+
+
+(defun cookie-delete-first (buffer)
+ "Delete first cookie and return it. Args: BUFFER.
+Returns nil if there is no cookie left."
+
+ (cookie-set-buffer buffer
+
+ ;; We have to check that we do not try to delete the footer.
+
+ (let ((tin (dll-nth cookies 1))) ;Skip the header.
+ (if (eq tin cookie-footer)
+ nil
+ (cookie-delete-tin-internal tin)
+ (cookie-tin-cookie (dll-delete cookies tin))))))
+
+
+
+(defun cookie-delete-last (buffer)
+ "Delete last cookie and return it. Args: BUFFER.
+Returns nil if there is no cookie left."
+
+ (cookie-set-buffer buffer
+
+ ;; We have to check that we do not try to delete the header.
+
+ (let ((tin (dll-nth cookies -2))) ;Skip the footer.
+ (if (eq tin cookie-header)
+ nil
+ (cookie-delete-tin-internal tin)
+ (cookie-tin-cookie (dll-delete cookies tin))))))
+
+
+
+(defun cookie-first (buffer)
+
+ "Return the first cookie in BUFFER. The cookie is not removed."
+
+ (cookie-set-buffer buffer
+ (let ((tin (cookie-filter-hf (dll-nth cookies -1))))
+ (if tin
+ (cookie-tin-cookie-safe
+ (dll-element cookies tin))))))
+
+
+(defun cookie-last (buffer)
+
+ "Return the last cookie in BUFFER. The cookie is not removed."
+
+ (cookie-set-buffer buffer
+ (let ((tin (cookie-filter-hf (dll-nth cookies -2))))
+ (if tin
+ (cookie-tin-cookie-safe
+ (dll-element cookies tin))))))
+
+
+(defun cookie-empty (buffer)
+
+ "Return true if there are no cookies in BUFFER."
+
+ (cookie-set-buffer buffer
+ (eq (dll-nth cookies 1) cookie-footer)))
+
+
+(defun cookie-length (buffer)
+
+ "Return number of cookies in BUFFER."
+
+ ;; Don't count the footer and header.
+
+ (cookie-set-buffer buffer
+ (- (dll-length cookies) 2)))
+
+
+(defun cookie-all (buffer)
+
+ "Return a list of all cookies in BUFFER."
+
+ (cookie-set-buffer buffer
+ (let (result
+ (tin (dll-nth cookies -2)))
+ (while (not (eq tin cookie-header))
+ (setq result (cons (cookie-tin-cookie (dll-element cookies tin))
+ result))
+ (setq tin (dll-previous cookies tin)))
+ result)))
+
+(defun cookie-clear (buffer)
+
+ "Remove all cookies in buffer."
+
+ (cookie-set-buffer buffer
+ (cookie-create buffer cookie-pretty-printer
+ (cookie-tin-cookie (dll-element cookies cookie-header))
+ (cookie-tin-cookie (dll-element cookies cookie-footer)))))
+
+
+
+(defun cookie-map (map-function buffer &rest map-args)
+
+ "Apply MAP-FUNCTION to all cookies in BUFFER.
+MAP-FUNCTION is applied to the first element first.
+If MAP-FUNCTION returns non-nil the cookie will be refreshed.
+
+Note that BUFFER will be current buffer when MAP-FUNCTION is called.
+
+If more than two arguments are given to cookie-map, remaining
+arguments will be passed to MAP-FUNCTION."
+
+ (cookie-set-buffer buffer
+ (let ((tin (dll-nth cookies 1))
+ result)
+
+ (while (not (eq tin cookie-footer))
+
+ (if (apply map-function
+ (cookie-tin-cookie (dll-element cookies tin))
+ map-args)
+ (cookie-refresh-tin tin))
+
+ (setq tin (dll-next cookies tin))))))
+
+
+
+(defun cookie-map-reverse (map-function buffer &rest map-args)
+
+ "Apply MAP-FUNCTION to all cookies in BUFFER.
+MAP-FUNCTION is applied to the last cookie first.
+If MAP-FUNCTION returns non-nil the cookie will be refreshed.
+
+Note that BUFFER will be current buffer when MAP-FUNCTION is called.
+
+If more than two arguments are given to cookie-map, remaining
+arguments will be passed to MAP-FUNCTION."
+
+ (cookie-set-buffer buffer
+ (let ((tin (dll-nth cookies -2))
+ result)
+
+ (while (not (eq tin cookie-header))
+
+ (if (apply map-function
+ (cookie-tin-cookie (dll-element cookies tin))
+ map-args)
+ (cookie-refresh-tin tin))
+
+ (setq tin (dll-previous cookies tin))))))
+
+
+
+(defun cookie-enter-cookies (buffer cookie-list)
+
+ "Insert all cookies in the list COOKIE-LIST last in BUFFER.
+Args: BUFFER COOKIE-LIST."
+
+ (while cookie-list
+ (cookie-enter-last buffer (car cookie-list))
+ (setq cookie-list (cdr cookie-list))))
+
+
+(defun cookie-filter (buffer predicate)
+
+ "Remove all cookies in BUFFER for which PREDICATE returns nil.
+Note that BUFFER will be current-buffer when PREDICATE is called.
+
+The PREDICATE is called with one argument, the cookie."
+
+ (cookie-set-buffer buffer
+ (let ((tin (dll-nth cookies 1))
+ next)
+ (while (not (eq tin cookie-footer))
+ (setq next (dll-next cookies tin))
+ (if (funcall predicate (cookie-tin-cookie (dll-element cookies tin)))
+ nil
+ (cookie-delete-tin-internal tin)
+ (dll-delete cookies tin))
+ (setq tin next)))))
+
+
+(defun cookie-filter-tins (buffer predicate)
+
+ "Remove all cookies in BUFFER for which PREDICATE returns nil.
+Note that BUFFER will be current-buffer when PREDICATE is called.
+
+The PREDICATE is called with one argument, the tin."
+
+ (cookie-set-buffer buffer
+ (let ((tin (dll-nth cookies 1))
+ next)
+ (while (not (eq tin cookie-footer))
+ (setq next (dll-next cookies tin))
+ (if (funcall predicate tin)
+ nil
+ (cookie-delete-tin-internal tin)
+ (dll-delete cookies tin))
+ (setq tin next)))))
+
+(defun cookie-pos-before-middle-p (pos tin1 tin2)
+
+ "Return true if POS is in the first half of the region defined by TIN1 and
+TIN2."
+
+ (< pos (/ (+ (cookie-tin-start-marker (dll-element cookeis tin1))
+ (cookie-tin-start-marker (dll-element cookeis tin2)))
+ 2)))
+
+
+(defun cookie-get-selection (buffer pos &optional guess force-guess)
+
+ "Return the tin the POS is within.
+Args: BUFFER POS &optional GUESS FORCE-GUESS.
+GUESS should be a tin that it is likely that POS is near. If FORCE-GUESS
+is non-nil GUESS is always used as a first guess, otherwise the first
+guess is the first tin, last tin, or GUESS, whichever is nearest to
+pos in the BUFFER.
+
+If pos points within the header, the first cookie is returned.
+If pos points within the footer, the last cookie is returned.
+Nil is returned if there is no cookie.
+
+It is often good to specify cookie-last-tin as GUESS, but remember
+that cookie-last-tin is buffer local in all buffers that cookie
+operates on."
+
+ (cookie-set-buffer buffer
+
+ (cond
+ ; No cookies present?
+ ((eq (dll-nth cookies 1) (dll-nth cookies -1))
+ nil)
+
+ ; Before first cookie?
+ ((< pos (cookie-tin-start-marker
+ (dll-element cookies (dll-nth cookies 1))))
+ (dll-nth cookies 1))
+
+ ; After last cookie?
+ ((>= pos (cookie-tin-start-marker (dll-last cookies)))
+ (dll-nth cookies -2))
+
+ ; We now now that pos is within a cookie.
+ (t
+ ; Make an educated guess about which of the three known
+ ; cookies (the first, the last, or GUESS) is nearest.
+ (setq
+ guess
+ (cond
+ (force-guess guess)
+ (guess
+ (cond
+ ;; Closest to first cookie?
+ ((cookie-pos-before-middle-p
+ pos guess
+ (dll-nth cookies 1))
+ (dll-nth cookies 1))
+ ;; Closest to GUESS?
+ ((cookie-pos-before-middle-p
+ pos guess
+ cookie-footer)
+ guess)
+ ;; Closest to last cookie.
+ (t (dll-previous cookies cookie-footer))))
+ (t
+ ;; No guess given.
+ (cond
+ ;; First half?
+ ((cookie-pos-before-middle-p
+ pos (dll-nth cookies 1)
+ cookie-footer)
+ (dll-nth cookies 1))
+ (t (dll-previous cookies cookie-footer))))))
+
+ ;; GUESS is now a "best guess".
+
+ ;; Find the correct cookie. First determine in which direction
+ ;; it lies, and then move in that direction until it is found.
+
+ (cond
+ ;; Is pos after the guess?
+ ((>= pos (cookie-tin-start-marker (dll-element cookiess guess)))
+
+ ;; Loop until we are exactly one cookie too far down...
+ (while (>= pos (cookie-tin-start-marker (dll-element cookiess guess)))
+ (setq guess (dll-next cookies guess)))
+
+ ;; ...and return the previous cookie.
+ (dll-previous cookies guess))
+
+ ;; Pos is before guess
+ (t
+
+ (while (< pos (cookie-tin-start-marker (dll-element cookiess guess)))
+ (setq guess (dll-previous cookies guess)))
+
+ guess))))))
+
+
+(defun cookie-start-marker (buffer tin)
+
+ "Return start-position of a cookie in BUFFER.
+Args: BUFFER TIN.
+The marker that is returned should not be modified in any way,
+and is only valid until the contents of the cookie buffer changes."
+
+ (cookie-set-buffer buffer
+ (cookie-tin-start-marker (dll-element cookies tin))))
+
+
+(defun cookie-end-marker (buffer tin)
+
+ "Return end-position of a cookie in BUFFER.
+Args: BUFFER TIN.
+The marker that is returned should not be modified in any way,
+and is only valid until the contents of the cookie buffer changes."
+
+ (cookie-set-buffer buffer
+ (cookie-tin-start-marker
+ (dll-element cookies (dll-next cookies tin)))))
+
+
+
+(defun cookie-refresh (buffer)
+
+ "Refresh all cookies in BUFFER.
+Cookie-pretty-printer will be called for all cookies and the new result
+displayed.
+
+See also cookie-invalidate-tins."
+
+ (cookie-set-buffer buffer
+
+ (erase-buffer)
+
+ (set-marker (cookie-tin-start-marker (dll-element cookies cookie-header))
+ (point) buffer)
+ (insert (cookie-tin-cookie (dll-element cookies cookie-header)))
+ (insert "\n")
+
+ (let ((tin (dll-nth cookies 1)))
+ (while (not (eq tin cookie-footer))
+
+ (set-marker (cookie-tin-start-marker (dll-element cookies tin))
+ (point) buffer)
+ (insert
+ (funcall cookie-pretty-printer
+ (cookie-tin-cookie (dll-element cookies tin))))
+ (insert "\n")
+ (setq tin (dll-next cookies tin))))
+
+ (set-marker (cookie-tin-start-marker (dll-element cookies cookie-footer))
+ (point) buffer)
+ (insert (cookie-tin-cookie (dll-element cookies cookie-footer)))
+ (insert "\n")))
+
+
+(defun cookie-invalidate-tins (buffer &rest tins)
+
+ "Refresh some cookies.
+Args: BUFFER &rest TINS."
+
+ (cookie-set-buffer buffer
+
+ (while tins
+ (cookie-refresh-tin (car tins))
+ (setq tins (cdr tins)))))
+
+
+;;; Cookie movement commands.
+
+(defun cookie-set-goal-column (buffer goal)
+ "Set goal-column for BUFFER.
+Args: BUFFER GOAL.
+goal-column is made buffer-local."
+ (cookie-set-buffer buffer
+ (make-local-variable 'goal-column)
+ (setq goal-column goal)))
+
+
+(defun cookie-previous-cookie (buffer pos arg)
+ "Move point to the ARGth previous cookie.
+Don't move if we are at the first cookie.
+ARG is the prefix argument when called interactively.
+Args: BUFFER POS ARG.
+Sets cookie-last-tin to the cookie we move to."
+
+ (interactive (list (current-buffer) (point)
+ (prefix-numeric-value current-prefix-arg)))
+
+ (cookie-set-buffer buffer
+ (setq cookie-last-tin
+ (cookie-get-selection buffer pos cookie-last-tin))
+
+ (while (and cookie-last-tin (> arg 0))
+ (setq arg (1- arg))
+ (setq cookie-last-tin
+ (dll-previous cookies cookie-last-tin)))
+
+ ;; Never step above the first cookie.
+
+ (if (null (cookie-filter-hf cookie-last-tin))
+ (setq cookie-last-tin (dll-nth cookies 1)))
+
+ (goto-char
+ (cookie-tin-start-marker
+ (dll-element cookies cookie-last-tin)))
+
+ (if goal-column
+ (move-to-column goal-column))))
+
+
+
+(defun cookie-next-cookie (buffer pos arg)
+ "Move point to the ARGth next cookie.
+Don't move if we are at the last cookie.
+ARG is the prefix argument when called interactively.
+Args: BUFFER POS ARG.
+Sets cookie-last-tin to the cookie we move to."
+
+ (interactive (list (current-buffer) (point)
+ (prefix-numeric-value current-prefix-arg)))
+
+ (cookie-set-buffer buffer
+ (setq cookie-last-tin
+ (cookie-get-selection buffer pos cookie-last-tin))
+
+ (while (and cookie-last-tin (> arg 0))
+ (setq arg (1- arg))
+ (setq cookie-last-tin
+ (dll-next cookies cookie-last-tin)))
+
+ (if (null (cookie-filter-hf cookie-last-tin))
+ (setq cookie-last-tin (dll-nth cookies -2)))
+
+ (goto-char
+ (cookie-tin-start-marker
+ (dll-element cookies cookie-last-tin)))
+
+ (if goal-column
+ (move-to-column goal-column))))
+
+
+(defun cookie-collect-tins (buffer predicate &rest predicate-args)
+
+ "Return a list of all tins in BUFFER whose cookie PREDICATE
+returns true for.
+PREDICATE is a function that takes a cookie as its argument.
+The tins on the returned list will appear in the same order
+as in the buffer. You should not rely on in which order PREDICATE
+is called. Note that BUFFER is current-buffer when PREDICATE
+is called. (If you call cookie-collect with another buffer set
+as current-buffer and need to access buffer-local variables
+from that buffer within PREDICATE you must send them via
+PREDICATE-ARGS).
+
+If more than two arguments are given to cookie-collect the remaining
+arguments will be passed to PREDICATE.
+
+Use cookie-cookie to get the cookie from the tin."
+
+ (cookie-set-buffer buffer
+ (let ((tin (dll-nth cookies -2))
+ result)
+
+ (while (not (eq tin cookie-header))
+
+ (if (apply predicate
+ (cookie-tin-cookie (dll-element cookies tin))
+ predicate-args)
+ (setq result (cons tin result)))
+
+ (setq tin (dll-previous cookies tin)))
+ result)))
+
+
+(defun cookie-collect-cookies (buffer predicate &rest predicate-args)
+
+ "Return a list of all cookies in BUFFER that PREDICATE
+returns true for.
+PREDICATE is a function that takes a cookie as its argument.
+The cookie on the returned list will appear in the same order
+as in the buffer. You should not rely on in which order PREDICATE
+is called. Note that BUFFER is current-buffer when PREDICATE
+is called. (If you call cookie-collect with another buffer set
+as current-buffer and need to access buffer-local variables
+from that buffer within PREDICATE you must send them via
+PREDICATE-ARGS).
+
+If more than two arguments are given to cookie-collect the remaining
+arguments will be passed to PREDICATE."
+
+ (cookie-set-buffer buffer
+ (let ((tin (dll-nth cookies -2))
+ result)
+
+ (while (not (eq tin cookie-header))
+
+ (if (apply predicate
+ (cookie-tin-cookie (dll-element cookies tin))
+ predicate-args)
+ (setq result (cons (cookie-tin-cookie (dll-element cookies tin))
+ result)))
+
+ (setq tin (dll-previous cookies tin)))
+ result)))
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/elib-dll-debug.el b/gnu/usr.bin/cvs/contrib/pcl-cvs/elib-dll-debug.el
new file mode 100644
index 0000000..733ff86
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/elib-dll-debug.el
@@ -0,0 +1,298 @@
+;;; elib-dll-debug -- A slow implementation of elib-dll for debugging.
+;;; elib-dll-debug.el,v 1.2 1992/04/07 20:49:13 berliner Exp
+;;; Copyright (C) 1991,1992 Per Cederqvist
+;;;
+;;; This program is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 2 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; This program is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with this program; if not, write to the Free Software
+;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+;;; This is a plug-in replacement for elib-dll.el. It is dreadfully
+;;; slow, but it facilitates debugging. Don't trust the comments in
+;;; this file too much.
+(provide 'elib-dll)
+
+;;;
+;;; A doubly linked list consists of one cons cell which holds the tag
+;;; 'DL-LIST in the car cell and the list in the cdr
+;;; cell. The doubly linked list is implemented as a normal list. You
+;;; should use elib-dll.el and not this package in debugged code. This
+;;; package is not written for speed...
+;;;
+
+;;; ================================================================
+;;; Internal functions for use in the doubly linked list package
+
+(defun dll-get-dummy-node (dll)
+
+ ;; Return the dummy node. INTERNAL USE ONLY.
+ dll)
+
+(defun dll-list-nodes (dll)
+
+ ;; Return a list of all nodes in DLL. INTERNAL USE ONLY.
+
+ (cdr dll))
+
+(defun dll-set-from-node-list (dll list)
+
+ ;; Set the contents of DLL to the nodes in LIST.
+ ;; INTERNAL USE ONLY.
+
+ (setcdr dll list))
+
+(defun dll-get-node-before (dll node)
+ ;; Return the node in DLL that points to NODE. Use
+ ;; (dll-get-node-before some-list nil) to get the last node.
+ ;; INTERNAL USE ONLY.
+ (while (and dll (not (eq (cdr dll) node)))
+ (setq dll (cdr dll)))
+ (if (not dll)
+ (error "Node not on list"))
+ dll)
+
+(defmacro dll-insert-after (node element)
+ (let ((node-v (make-symbol "node"))
+ (element-v (make-symbol "element")))
+ (` (let (((, node-v) (, node))
+ ((, element-v) (, element)))
+ (setcdr (, node-v) (cons (, element-v) (cdr (, node-v))))))))
+
+;;; ===================================================================
+;;; The public functions which operate on doubly linked lists.
+
+(defmacro dll-element (dll node)
+
+ "Get the element of a NODE in a doubly linked list DLL.
+Args: DLL NODE."
+
+ (` (car (, node))))
+
+
+(defun dll-create ()
+ "Create an empty doubly linked list."
+ (cons 'DL-LIST nil))
+
+
+(defun dll-p (object)
+ "Return t if OBJECT is a doubly linked list, otherwise return nil."
+ (eq (car-safe object) 'DL-LIST))
+
+
+(defun dll-enter-first (dll element)
+ "Add an element first on a doubly linked list.
+Args: DLL ELEMENT."
+ (setcdr dll (cons element (cdr dll))))
+
+
+(defun dll-enter-last (dll element)
+ "Add an element last on a doubly linked list.
+Args: DLL ELEMENT."
+ (dll-insert-after (dll-get-node-before dll nil) element))
+
+
+(defun dll-enter-after (dll node element)
+ "In the doubly linked list DLL, insert a node containing ELEMENT after NODE.
+Args: DLL NODE ELEMENT."
+
+ (dll-get-node-before dll node)
+ (dll-insert-after node element))
+
+
+(defun dll-enter-before (dll node element)
+ "In the doubly linked list DLL, insert a node containing ELEMENT before NODE.
+Args: DLL NODE ELEMENT."
+
+ (dll-insert-after (dll-get-node-before dll node) element))
+
+
+
+(defun dll-next (dll node)
+ "Return the node after NODE, or nil if NODE is the last node.
+Args: DLL NODE."
+
+ (dll-get-node-before dll node)
+ (cdr node))
+
+
+(defun dll-previous (dll node)
+ "Return the node before NODE, or nil if NODE is the first node.
+Args: DLL NODE."
+
+ (dll-get-node-before dll node))
+
+
+(defun dll-delete (dll node)
+
+ "Delete NODE from the doubly linked list DLL.
+Args: DLL NODE. Return the element of node."
+
+ ;; This is a no-op when applied to the dummy node. This will return
+ ;; nil if applied to the dummy node since it always contains nil.
+
+ (setcdr (dll-get-node-before dll node) (cdr node)))
+
+
+(defun dll-delete-first (dll)
+
+ "Delete the first NODE from the doubly linked list DLL.
+Return the element. Args: DLL. Returns nil if the DLL was empty."
+
+ ;; Relies on the fact that dll-delete does nothing and
+ ;; returns nil if given the dummy node.
+
+ (setcdr dll (cdr (cdr dll))))
+
+
+(defun dll-delete-last (dll)
+
+ "Delete the last NODE from the doubly linked list DLL.
+Return the element. Args: DLL. Returns nil if the DLL was empty."
+
+ ;; Relies on the fact that dll-delete does nothing and
+ ;; returns nil if given the dummy node.
+
+ (setcdr dll (dll-get-node-before dll nil) nil))
+
+
+(defun dll-first (dll)
+
+ "Return the first element on the doubly linked list DLL.
+Return nil if the list is empty. The element is not removed."
+
+ (car (cdr dll)))
+
+
+
+
+(defun dll-last (dll)
+
+ "Return the last element on the doubly linked list DLL.
+Return nil if the list is empty. The element is not removed."
+
+ (car (dll-get-node-before dll nil)))
+
+
+
+(defun dll-nth (dll n)
+
+ "Return the Nth node from the doubly linked list DLL.
+ Args: DLL N
+N counts from zero. If DLL is not that long, nil is returned.
+If N is negative, return the -(N+1)th last element.
+Thus, (dll-nth dll 0) returns the first node,
+and (dll-nth dll -1) returns the last node."
+
+ ;; Branch 0 ("follow left pointer") is used when n is negative.
+ ;; Branch 1 ("follow right pointer") is used otherwise.
+
+ (if (>= n 0)
+ (nthcdr n (cdr dll))
+ (unwind-protect
+ (progn (setcdr dll (nreverse (cdr dll)))
+ (nthcdr (- n) dll))
+ (setcdr dll (nreverse (cdr dll))))))
+
+(defun dll-empty (dll)
+
+ "Return t if the doubly linked list DLL is empty, nil otherwise"
+
+ (not (cdr dll)))
+
+(defun dll-length (dll)
+
+ "Returns the number of elements in the doubly linked list DLL."
+
+ (length (cdr dll)))
+
+
+
+(defun dll-copy (dll &optional element-copy-fnc)
+
+ "Return a copy of the doubly linked list DLL.
+If optional second argument ELEMENT-COPY-FNC is non-nil it should be
+a function that takes one argument, an element, and returns a copy of it.
+If ELEMENT-COPY-FNC is not given the elements are not copied."
+
+ (if element-copy-fnc
+ (cons 'DL-LIST (mapcar element-copy-fnc (cdr dll)))
+ (copy-sequence dll)))
+
+
+(defun dll-all (dll)
+
+ "Return all elements on the double linked list DLL as an ordinary list."
+
+ (cdr dll))
+
+
+(defun dll-clear (dll)
+
+ "Clear the doubly linked list DLL, i.e. make it completely empty."
+
+ (setcdr dll nil))
+
+
+(defun dll-map (map-function dll)
+
+ "Apply MAP-FUNCTION to all elements in the doubly linked list DLL.
+The function is applied to the first element first."
+
+ (mapcar map-function (cdr dll)))
+
+
+(defun dll-map-reverse (map-function dll)
+
+ "Apply MAP-FUNCTION to all elements in the doubly linked list DLL.
+The function is applied to the last element first."
+
+ (unwind-protect
+ (setcdr dll (nreverse (cdr dll)))
+ (mapcar map-function (cdr dll))
+ (setcdr dll (nreverse (cdr dll)))))
+
+
+(defun dll-create-from-list (list)
+
+ "Given an elisp LIST create a doubly linked list with the same elements."
+
+ (cons 'DL-LIST list))
+
+
+
+(defun dll-sort (dll predicate)
+
+ "Sort the doubly linked list DLL, stably, comparing elements using PREDICATE.
+Returns the sorted list. DLL is modified by side effects.
+PREDICATE is called with two elements of DLL, and should return T
+if the first element is \"less\" than the second."
+
+ (setcdr dll (sort (cdr dll) predicate))
+ dll)
+
+
+(defun dll-filter (dll predicate)
+
+ "Remove all elements in the doubly linked list DLL for which PREDICATE
+return nil."
+
+ (let* ((prev dll)
+ (node (cdr dll)))
+
+ (while node
+ (cond
+ ((funcall predicate (car node))
+ (setq prev node))
+ (t
+ (setcdr prev (cdr node))))
+ (setq node (cdr node)))))
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/elib-dll.el b/gnu/usr.bin/cvs/contrib/pcl-cvs/elib-dll.el
new file mode 100644
index 0000000..855bd19
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/elib-dll.el
@@ -0,0 +1,386 @@
+;;; elib-dll.el,v 1.2 1992/04/07 20:49:15 berliner Exp
+;;; elib-dll.el -- Some primitives for Doubly linked lists.
+;;; Copyright (C) 1991, 1992 Per Cederqvist
+;;;
+;;; This program is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 2 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; This program is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with this program; if not, write to the Free Software
+;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;;; Mail bug reports to ceder@lysator.liu.se.
+
+(require 'elib-node)
+(provide 'elib-dll)
+
+;;;
+;;; A doubly linked list consists of one cons cell which holds the tag
+;;; 'DL-LIST in the car cell and a pointer to a dummy node in the cdr
+;;; cell. The doubly linked list is implemented as a circular list
+;;; with the dummy node first and last. The dummy node is recognized
+;;; by comparing it to the node which the cdr of the cons cell points
+;;; to.
+;;;
+
+;;; ================================================================
+;;; Internal functions for use in the doubly linked list package
+
+(defun dll-get-dummy-node (dll)
+
+ ;; Return the dummy node. INTERNAL USE ONLY.
+ (cdr dll))
+
+(defun dll-list-nodes (dll)
+
+ ;; Return a list of all nodes in DLL. INTERNAL USE ONLY.
+
+ (let* ((result nil)
+ (dummy (dll-get-dummy-node dll))
+ (node (elib-node-left dummy)))
+
+ (while (not (eq node dummy))
+ (setq result (cons node result))
+ (setq node (elib-node-left node)))
+
+ result))
+
+(defun dll-set-from-node-list (dll list)
+
+ ;; Set the contents of DLL to the nodes in LIST.
+ ;; INTERNAL USE ONLY.
+
+ (dll-clear dll)
+ (let* ((dummy (dll-get-dummy-node dll))
+ (left dummy))
+ (while list
+ (elib-node-set-left (car list) left)
+ (elib-node-set-right left (car list))
+ (setq left (car list))
+ (setq list (cdr list)))
+
+ (elib-node-set-right left dummy)
+ (elib-node-set-left dummy left)))
+
+
+;;; ===================================================================
+;;; The public functions which operate on doubly linked lists.
+
+(defmacro dll-element (dll node)
+
+ "Get the element of a NODE in a doubly linked list DLL.
+Args: DLL NODE."
+
+ (` (elib-node-data (, node))))
+
+
+(defun dll-create ()
+ "Create an empty doubly linked list."
+ (let ((dummy-node (elib-node-create nil nil nil)))
+ (elib-node-set-right dummy-node dummy-node)
+ (elib-node-set-left dummy-node dummy-node)
+ (cons 'DL-LIST dummy-node)))
+
+(defun dll-p (object)
+ "Return t if OBJECT is a doubly linked list, otherwise return nil."
+ (eq (car-safe object) 'DL-LIST))
+
+(defun dll-enter-first (dll element)
+ "Add an element first on a doubly linked list.
+Args: DLL ELEMENT."
+ (dll-enter-after
+ dll
+ (dll-get-dummy-node dll)
+ element))
+
+
+(defun dll-enter-last (dll element)
+ "Add an element last on a doubly linked list.
+Args: DLL ELEMENT."
+ (dll-enter-before
+ dll
+ (dll-get-dummy-node dll)
+ element))
+
+
+(defun dll-enter-after (dll node element)
+ "In the doubly linked list DLL, insert a node containing ELEMENT after NODE.
+Args: DLL NODE ELEMENT."
+
+ (let ((new-node (elib-node-create
+ node (elib-node-right node)
+ element)))
+ (elib-node-set-left (elib-node-right node) new-node)
+ (elib-node-set-right node new-node)))
+
+
+(defun dll-enter-before (dll node element)
+ "In the doubly linked list DLL, insert a node containing ELEMENT before NODE.
+Args: DLL NODE ELEMENT."
+
+ (let ((new-node (elib-node-create
+ (elib-node-left node) node
+ element)))
+ (elib-node-set-right (elib-node-left node) new-node)
+ (elib-node-set-left node new-node)))
+
+
+
+(defun dll-next (dll node)
+ "Return the node after NODE, or nil if NODE is the last node.
+Args: DLL NODE."
+
+ (if (eq (elib-node-right node) (dll-get-dummy-node dll))
+ nil
+ (elib-node-right node)))
+
+
+(defun dll-previous (dll node)
+ "Return the node before NODE, or nil if NODE is the first node.
+Args: DLL NODE."
+
+ (if (eq (elib-node-left node) (dll-get-dummy-node dll))
+ nil
+ (elib-node-left node)))
+
+
+(defun dll-delete (dll node)
+
+ "Delete NODE from the doubly linked list DLL.
+Args: DLL NODE. Return the element of node."
+
+ ;; This is a no-op when applied to the dummy node. This will return
+ ;; nil if applied to the dummy node since it always contains nil.
+
+ (elib-node-set-right (elib-node-left node) (elib-node-right node))
+ (elib-node-set-left (elib-node-right node) (elib-node-left node))
+ (dll-element dll node))
+
+
+
+(defun dll-delete-first (dll)
+
+ "Delete the first NODE from the doubly linked list DLL.
+Return the element. Args: DLL. Returns nil if the DLL was empty."
+
+ ;; Relies on the fact that dll-delete does nothing and
+ ;; returns nil if given the dummy node.
+
+ (dll-delete dll (elib-node-right (dll-get-dummy-node dll))))
+
+
+(defun dll-delete-last (dll)
+
+ "Delete the last NODE from the doubly linked list DLL.
+Return the element. Args: DLL. Returns nil if the DLL was empty."
+
+ ;; Relies on the fact that dll-delete does nothing and
+ ;; returns nil if given the dummy node.
+
+ (dll-delete dll (elib-node-left (dll-get-dummy-node dll))))
+
+
+(defun dll-first (dll)
+
+ "Return the first element on the doubly linked list DLL.
+Return nil if the list is empty. The element is not removed."
+
+ (if (eq (elib-node-right (dll-get-dummy-node dll))
+ (dll-get-dummy-node dll))
+ nil
+ (elib-node-data (elib-node-right (dll-get-dummy-node dll)))))
+
+
+
+
+(defun dll-last (dll)
+
+ "Return the last element on the doubly linked list DLL.
+Return nil if the list is empty. The element is not removed."
+
+ (if (eq (elib-node-left (dll-get-dummy-node dll))
+ (dll-get-dummy-node dll))
+ nil
+ (elib-node-data (elib-node-left (dll-get-dummy-node dll)))))
+
+
+
+(defun dll-nth (dll n)
+
+ "Return the Nth node from the doubly linked list DLL.
+ Args: DLL N
+N counts from zero. If DLL is not that long, nil is returned.
+If N is negative, return the -(N+1)th last element.
+Thus, (dll-nth dll 0) returns the first node,
+and (dll-nth dll -1) returns the last node."
+
+ ;; Branch 0 ("follow left pointer") is used when n is negative.
+ ;; Branch 1 ("follow right pointer") is used otherwise.
+
+ (let* ((dummy (dll-get-dummy-node dll))
+ (branch (if (< n 0) 0 1))
+ (node (elib-node-branch dummy branch)))
+
+ (if (< n 0)
+ (setq n (- -1 n)))
+
+ (while (and (not (eq dummy node))
+ (> n 0))
+ (setq node (elib-node-branch node branch))
+ (setq n (1- n)))
+
+ (if (eq dummy node)
+ nil
+ node)))
+
+
+(defun dll-empty (dll)
+
+ "Return t if the doubly linked list DLL is empty, nil otherwise"
+
+ (eq (elib-node-left (dll-get-dummy-node dll))
+ (dll-get-dummy-node dll)))
+
+(defun dll-length (dll)
+
+ "Returns the number of elements in the doubly linked list DLL."
+
+ (let* ((dummy (dll-get-dummy-node dll))
+ (node (elib-node-right dummy))
+ (n 0))
+
+ (while (not (eq node dummy))
+ (setq node (elib-node-right node))
+ (setq n (1+ n)))
+
+ n))
+
+
+
+(defun dll-copy (dll &optional element-copy-fnc)
+
+ "Return a copy of the doubly linked list DLL.
+If optional second argument ELEMENT-COPY-FNC is non-nil it should be
+a function that takes one argument, an element, and returns a copy of it.
+If ELEMENT-COPY-FNC is not given the elements are not copied."
+
+ (let ((result (dll-create))
+ (node (dll-nth dll 0)))
+ (if element-copy-fnc
+
+ ;; Copy the elements with the user-supplied function.
+ (while node
+ (dll-enter-last result
+ (funcall element-copy-fnc
+ (dll-element dll node)))
+ (setq node (dll-next dll node)))
+
+ ;; Don't try to copy the elements - they might be
+ ;; circular lists, or anything at all...
+ (while node
+ (dll-enter-last result (dll-element dll node))
+ (setq node (dll-next dll node))))
+
+ result))
+
+
+
+(defun dll-all (dll)
+
+ "Return all elements on the double linked list DLL as an ordinary list."
+
+ (let* ((result nil)
+ (dummy (dll-get-dummy-node dll))
+ (node (elib-node-left dummy)))
+
+ (while (not (eq node dummy))
+ (setq result (cons (dll-element dll node) result))
+ (setq node (elib-node-left node)))
+
+ result))
+
+
+(defun dll-clear (dll)
+
+ "Clear the doubly linked list DLL, i.e. make it completely empty."
+
+ (elib-node-set-left (dll-get-dummy-node dll) (dll-get-dummy-node dll))
+ (elib-node-set-right (dll-get-dummy-node dll) (dll-get-dummy-node dll)))
+
+
+(defun dll-map (map-function dll)
+
+ "Apply MAP-FUNCTION to all elements in the doubly linked list DLL.
+The function is applied to the first element first."
+
+ (let* ((dummy (dll-get-dummy-node dll))
+ (node (elib-node-right dummy)))
+
+ (while (not (eq node dummy))
+ (funcall map-function (dll-element dll node))
+ (setq node (elib-node-right node)))))
+
+
+(defun dll-map-reverse (map-function dll)
+
+ "Apply MAP-FUNCTION to all elements in the doubly linked list DLL.
+The function is applied to the last element first."
+
+ (let* ((dummy (dll-get-dummy-node dll))
+ (node (elib-node-left dummy)))
+
+ (while (not (eq node dummy))
+ (funcall map-function (dll-element dll node))
+ (setq node (elib-node-left node)))))
+
+
+(defun dll-create-from-list (list)
+
+ "Given an elisp LIST create a doubly linked list with the same elements."
+
+ (let ((dll (dll-create)))
+ (while list
+ (dll-enter-last dll (car list))
+ (setq list (cdr list)))
+ dll))
+
+
+
+(defun dll-sort (dll predicate)
+
+ "Sort the doubly linked list DLL, stably, comparing elements using PREDICATE.
+Returns the sorted list. DLL is modified by side effects.
+PREDICATE is called with two elements of DLL, and should return T
+if the first element is \"less\" than the second."
+
+ (dll-set-from-node-list
+ dll (sort (dll-list-nodes dll)
+ (function (lambda (x1 x2)
+ (funcall predicate
+ (dll-element dll x1)
+ (dll-element dll x2))))))
+ dll)
+
+
+(defun dll-filter (dll predicate)
+
+ "Remove all elements in the doubly linked list DLL for which PREDICATE
+return nil."
+
+ (let* ((dummy (dll-get-dummy-node dll))
+ (node (elib-node-right dummy))
+ next)
+
+ (while (not (eq node dummy))
+ (setq next (elib-node-right node))
+ (if (funcall predicate (dll-element dll node))
+ nil
+ (dll-delete dll node))
+ (setq node next))))
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/elib-node.el b/gnu/usr.bin/cvs/contrib/pcl-cvs/elib-node.el
new file mode 100644
index 0000000..6c476a3
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/elib-node.el
@@ -0,0 +1,89 @@
+;;;; elib-node.el,v 1.2 1992/04/07 20:49:16 berliner Exp
+;;;; This file implements the nodes used in binary trees and
+;;;; doubly linked lists
+;;;;
+;;;; Copyright (C) 1991 Inge Wallin
+;;;;
+;;;; This file is part of the GNU Emacs lisp library, Elib.
+;;;;
+;;;; GNU Elib is free software; you can redistribute it and/or modify
+;;;; it under the terms of the GNU General Public License as published by
+;;;; the Free Software Foundation; either version 1, or (at your option)
+;;;; any later version.
+;;;;
+;;;; GNU Elib is distributed in the hope that it will be useful,
+;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;;; GNU General Public License for more details.
+;;;;
+;;;; You should have received a copy of the GNU General Public License
+;;;; along with GNU Emacs; see the file COPYING. If not, write to
+;;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;;;;
+;;;; Author: Inge Wallin
+;;;;
+
+;;;
+;;; A node is implemented as an array with three elements, using
+;;; (elt node 0) as the left pointer
+;;; (elt node 1) as the right pointer
+;;; (elt node 2) as the data
+;;;
+;;; Some types of trees, e.g. AVL trees, need bigger nodes, but
+;;; as long as the first three parts are the left pointer, the
+;;; right pointer and the data field, these macros can be used.
+;;;
+
+
+(provide 'elib-node)
+
+
+(defmacro elib-node-create (left right data)
+ "Create a tree node from LEFT, RIGHT and DATA."
+ (` (vector (, left) (, right) (, data))))
+
+
+(defmacro elib-node-left (node)
+ "Return the left pointer of NODE."
+ (` (aref (, node) 0)))
+
+
+(defmacro elib-node-right (node)
+ "Return the right pointer of NODE."
+ (` (aref (, node) 1)))
+
+
+(defmacro elib-node-data (node)
+ "Return the data of NODE."
+ (` (aref (, node) 2)))
+
+
+(defmacro elib-node-set-left (node newleft)
+ "Set the left pointer of NODE to NEWLEFT."
+ (` (aset (, node) 0 (, newleft))))
+
+
+(defmacro elib-node-set-right (node newright)
+ "Set the right pointer of NODE to NEWRIGHT."
+ (` (aset (, node) 1 (, newright))))
+
+
+(defmacro elib-node-set-data (node newdata)
+ "Set the data of NODE to NEWDATA."
+ (` (aset (, node) 2 (, newdata))))
+
+
+
+(defmacro elib-node-branch (node branch)
+ "Get value of a branch of a node.
+NODE is the node, and BRANCH is the branch.
+0 for left pointer, 1 for right pointer and 2 for the data."
+ (` (aref (, node) (, branch))))
+
+
+(defmacro elib-node-set-branch (node branch newval)
+ "Set value of a branch of a node.
+NODE is the node, and BRANCH is the branch.
+0 for left pointer, 1 for the right pointer and 2 for the data.
+NEWVAL is new value of the branch."
+ (` (aset (, node) (, branch) (, newval))))
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs-startup.el b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs-startup.el
new file mode 100644
index 0000000..27bb57c
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs-startup.el
@@ -0,0 +1,6 @@
+;;; pcl-cvs-startup.el,v 1.2 1992/04/07 20:49:17 berliner Exp
+(autoload 'cvs-update "pcl-cvs"
+ "Run a 'cvs update' in the current working directory. Feed the
+output to a *cvs* buffer and run cvs-mode on it.
+If optional prefix argument LOCAL is non-nil, 'cvs update -l' is run."
+ t)
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.el b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.el
new file mode 100644
index 0000000..99da369
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.el
@@ -0,0 +1,1476 @@
+;;; pcl-cvs.el,v 1.2 1992/04/07 20:49:19 berliner Exp
+;;; pcl-cvs.el -- A Front-end to CVS 1.3 or later. Release 1.02.
+;;; Copyright (C) 1991, 1992 Per Cederqvist
+;;;
+;;; This program is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 2 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; This program is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with this program; if not, write to the Free Software
+;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;;;; See below for installation instructions.
+;;;;
+;;;; There is an TeXinfo file that describes this package. The GNU
+;;;; General Public License is included in that file. You should read
+;;;; it to get the most from this package.
+
+;;; Don't try to use this with CVS 1.2 or earlier. It won't work. Get
+;;; CVS 1.3.
+
+;;; Mail questions and bug reports to ceder@lysator.liu.se.
+
+(require 'cookie)
+(provide 'pcl-cvs)
+
+;;; -------------------------------------------------------
+;;; START OF THINGS TO CHECK WHEN INSTALLING
+
+(defvar cvs-program "/usr/gnu/bin/cvs"
+ "*Full path to the cvs executable.")
+
+(defvar cvs-diff-program "/usr/gnu/bin/diff"
+ "*Full path to the diff program.")
+
+(defvar cvs-rm-program "/usr/gnu/bin/rm"
+ "*Full path to the rm program. Typically /bin/rm.")
+
+;; Uncomment the following line if you are running on 18.57 or earlier.
+;(setq delete-exited-processes nil)
+;; Emacs version 18.57 and earlier is likely to crash if
+;; delete-exited-processes is t, since the sentinel uses lots of
+;; memory, and 18.57 forgets to GCPROT a variable if
+;; delete-exited-processes is t.
+
+;;; END OF THINGS TO CHECK WHEN INSTALLING
+;;; --------------------------------------------------------
+
+(defvar cvs-bakprefix ".#"
+ "The prefix that CVS prepends to files when rcsmerge'ing.")
+
+(defvar cvs-erase-input-buffer nil
+ "*Non-nil if input buffers should be cleared before asking for new info.")
+
+(defvar cvs-auto-remove-handled nil
+ "*Non-nil if cvs-remove-handled should be called automatically.
+If this is set to any non-nil value entries that does not need to be
+checked in will be removed from the *cvs* buffer after every cvs-commit
+command.")
+
+(defconst cvs-cursor-column 14
+ "Column to position cursor in in cvs-mode.
+Column 0 is left-most column.")
+
+(defvar cvs-mode-map nil
+ "Keymap for the cvs mode.")
+
+(defvar cvs-edit-mode-map nil
+ "Keymap for the cvs edit mode (used when editing cvs log messages).")
+
+(defvar cvs-buffer-name "*cvs*"
+ "Name of the cvs buffer.")
+
+(defvar cvs-commit-prompt-buffer "*cvs-commit-message*"
+ "Name of buffer in which the user is prompted for a log message when
+committing files.")
+
+(defvar cvs-temp-buffer-name "*cvs-tmp*"
+ "*Name of the cvs temporary buffer.
+Output from cvs is placed here by synchronous commands.")
+
+(defvar cvs-cvs-diff-flags nil
+ "*List of strings to use as flags to pass to ``cvs diff''.
+Used by cvs-diff-cvs.
+Set this to '("-u") to get a Unidiff format, or '("-c") to get context diffs.")
+
+(defvar cvs-status-flags nil
+ "*List of strings to pass to ``cvs status''.")
+
+(defvar cvs-log-flags nil
+ "*List of strings to pass to ``cvs log''.")
+
+(defvar cvs-diff-flags nil
+ "*List of strings to use as flags to pass to ``diff''.
+Do not confuse with cvs-cvs-diff-flags. Used by cvs-diff-backup.")
+
+(defvar cvs-buffers-to-delete nil
+ "List of temporary buffers that should be discarded as soon as possible.
+Due to a bug in emacs 18.57 the sentinel can't discard them reliably.")
+
+;; You are NOT allowed to disable this message by default. However, you
+;; are encouraged to inform your users that by adding
+;; (setq cvs-inhibit-copyright-message t)
+;; to their .emacs they can get rid of it. Just don't add that line
+;; to your default.el!
+(defvar cvs-inhibit-copyright-message nil
+ "*Don't display a Copyright message in the ``*cvs*'' buffer.")
+
+(defvar cvs-startup-message
+ (if cvs-inhibit-copyright-message
+ "PCL-CVS release 1.02"
+ "PCL-CVS release 1.02. Copyright (C) 1992 Per Cederqvist
+Pcl-cvs comes with absolutely no warranty; for details consult the manual.
+This is free software, and you are welcome to redistribute it under certain
+conditions; again, consult the TeXinfo manual for details.")
+ "*Startup message for CVS.")
+
+(defvar cvs-cvs-buffer nil
+ "Internal to pcl-cvs.el.
+This variable exists in the *cvs-commit-message* buffer and names
+the *cvs* buffer.")
+
+;;; The cvs data structure:
+;;;
+;;; When the `cvs update' is ready we parse the output. Every file
+;;; that is affected in some way is added as a cookie of fileinfo
+;;; (as defined below).
+;;;
+
+;;; cvs-fileinfo
+;;;
+;;; marked t/nil
+;;; type One of
+;;; UPDATED - file copied from repository
+;;; MODIFIED - modified by you, unchanged in
+;;; repository
+;;; ADDED - added by you, not yet committed
+;;; REMOVED - removed by you, not yet committed
+;;; CVS-REMOVED- removed, since file no longer exists
+;;; in the repository.
+;;; MERGED - successful merge
+;;; CONFLICT - conflict when merging
+;;; REM-CONFLICT-removed in repository, changed locally.
+;;; MOD-CONFLICT-removed locally, changed in repository.
+;;; DIRCHANGE - A change of directory.
+;;; UNKNOWN - An unknown file.
+;;; MOVE-AWAY - A file that is in the way.
+;;; REPOS-MISSING- The directory is removed from the
+;;; repository. Go fetch a backup.
+;;; dir Directory the file resides in. Should not end with
+;;; slash.
+;;; file-name The file name.
+;;; backup-file Name of the backup file if MERGED or CONFLICT.
+;;; cvs-diff-buffer A buffer that contains a 'cvs diff file'.
+;;; backup-diff-buffer A buffer that contains a 'diff file backup-file'.
+;;; full-log The output from cvs, unparsed.
+;;; mod-time Modification time of file used for *-diff-buffer.
+;;; handled True if this file doesn't require further action.
+;;;
+;;; Constructor:
+
+;;; cvs-fileinfo
+
+;;; Constructor:
+
+(defun cvs-create-fileinfo (type
+ dir
+ file-name
+ full-log)
+ "Create a fileinfo from all parameters.
+Arguments: TYPE DIR FILE-NAME FULL-LOG.
+A fileinfo has the following fields:
+
+ marked t/nil
+ type One of
+ UPDATED - file copied from repository
+ MODIFIED - modified by you, unchanged in
+ repository
+ ADDED - added by you, not yet committed
+ REMOVED - removed by you, not yet committed
+ CVS-REMOVED- removed, since file no longer exists
+ in the repository.
+ MERGED - successful merge
+ CONFLICT - conflict when merging
+ REM-CONFLICT-removed in repository, but altered
+ locally.
+ MOD-CONFLICT-removed locally, changed in repository.
+ DIRCHANGE - A change of directory.
+ UNKNOWN - An unknown file.
+ MOVE-AWAY - A file that is in the way.
+ REPOS-MISSING- The directory has vanished from the
+ repository.
+ dir Directory the file resides in. Should not end with slash.
+ file-name The file name.
+ backup-file Name of the backup file if MERGED or CONFLICT.
+ cvs-diff-buffer A buffer that contains a 'cvs diff file'.
+ backup-diff-buffer A buffer that contains a 'diff file backup-file'.
+ full-log The output from cvs, unparsed.
+ mod-time Modification time of file used for *-diff-buffer.
+ handled True if this file doesn't require further action."
+ (cons
+ 'CVS-FILEINFO
+ (vector nil nil type dir file-name nil nil nil full-log nil)))
+
+
+;;; Selectors:
+
+(defun cvs-fileinfo->handled (cvs-fileinfo)
+ "Get the `handled' field from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 0))
+
+(defun cvs-fileinfo->marked (cvs-fileinfo)
+ "Check if CVS-FILEINFO is marked."
+ (elt (cdr cvs-fileinfo) 1))
+
+(defun cvs-fileinfo->type (cvs-fileinfo)
+ "Get type from CVS-FILEINFO.
+Type is one of UPDATED, MODIFIED, ADDED, REMOVED, CVS-REMOVED, MERGED,
+CONFLICT, REM-CONFLICT, MOD-CONFLICT, DIRCHANGE, UNKNOWN, MOVE-AWAY
+or REPOS-MISSING."
+ (elt (cdr cvs-fileinfo) 2))
+
+(defun cvs-fileinfo->dir (cvs-fileinfo)
+ "Get dir from CVS-FILEINFO.
+The directory name does not end with a slash. "
+ (elt (cdr cvs-fileinfo) 3))
+
+(defun cvs-fileinfo->file-name (cvs-fileinfo)
+ "Get file-name from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 4))
+
+(defun cvs-fileinfo->backup-file (cvs-fileinfo)
+ "Get backup-file from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 5))
+
+(defun cvs-fileinfo->cvs-diff-buffer (cvs-fileinfo)
+ "Get cvs-diff-buffer from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 6))
+
+(defun cvs-fileinfo->backup-diff-buffer (cvs-fileinfo)
+ "Get backup-diff-buffer from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 7))
+
+(defun cvs-fileinfo->full-log (cvs-fileinfo)
+ "Get full-log from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 8))
+
+(defun cvs-fileinfo->mod-time (cvs-fileinfo)
+ "Get mod-time from CVS-FILEINFO."
+ (elt (cdr cvs-fileinfo) 9))
+
+;;; Modifiers:
+
+(defun cvs-set-fileinfo->handled (cvs-fileinfo newval)
+ "Set handled in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 0 newval))
+
+(defun cvs-set-fileinfo->marked (cvs-fileinfo newval)
+ "Set marked in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 1 newval))
+
+(defun cvs-set-fileinfo->type (cvs-fileinfo newval)
+ "Set type in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 2 newval))
+
+(defun cvs-set-fileinfo->dir (cvs-fileinfo newval)
+ "Set dir in CVS-FILEINFO to NEWVAL.
+The directory should now end with a slash."
+ (aset (cdr cvs-fileinfo) 3 newval))
+
+(defun cvs-set-fileinfo->file-name (cvs-fileinfo newval)
+ "Set file-name in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 4 newval))
+
+(defun cvs-set-fileinfo->backup-file (cvs-fileinfo newval)
+ "Set backup-file in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 5 newval))
+
+(defun cvs-set-fileinfo->cvs-diff-buffer (cvs-fileinfo newval)
+ "Set cvs-diff-buffer in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 6 newval))
+
+(defun cvs-set-fileinfo->backup-diff-buffer (cvs-fileinfo newval)
+ "Set backup-diff-buffer in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 7 newval))
+
+(defun cvs-set-fileinfo->full-log (cvs-fileinfo newval)
+ "Set full-log in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 8 newval))
+
+(defun cvs-set-fileinfo->mod-time (cvs-fileinfo newval)
+ "Set full-log in CVS-FILEINFO to NEWVAL."
+ (aset (cdr cvs-fileinfo) 9 newval))
+
+
+
+;;; Predicate:
+
+(defun cvs-fileinfo-p (object)
+ "Return t if OBJECT is a cvs-fileinfo."
+ (eq (car-safe object) 'CVS-FILEINFO))
+
+;;;; End of types.
+
+(defun cvs-use-temp-buffer ()
+ "Display a temporary buffer in another window and select it.
+The selected window will not be changed. The temporary buffer will
+be erased and writable."
+
+ (display-buffer (get-buffer-create cvs-temp-buffer-name))
+ (set-buffer cvs-temp-buffer-name)
+ (setq buffer-read-only nil)
+ (erase-buffer))
+
+; Too complicated to handle all the cases that are generated.
+; Maybe later.
+;(defun cvs-examine (directory &optional local)
+; "Run a 'cvs -n update' in the current working directory.
+;That is, check what needs to be done, but don't change the disc.
+;Feed the output to a *cvs* buffer and run cvs-mode on it.
+;If optional prefix argument LOCAL is non-nil, 'cvs update -l' is run."
+; (interactive (list (read-file-name "CVS Update (directory): "
+; nil default-directory nil)
+; current-prefix-arg))
+; (cvs-do-update directory local 'noupdate))
+
+(defun cvs-update (directory &optional local)
+ "Run a 'cvs update' in the current working directory. Feed the
+output to a *cvs* buffer and run cvs-mode on it.
+If optional prefix argument LOCAL is non-nil, 'cvs update -l' is run."
+ (interactive (list (read-file-name "CVS Update (directory): "
+ nil default-directory nil)
+ current-prefix-arg))
+ (cvs-do-update directory local nil))
+
+(defun cvs-filter (predicate list &rest extra-args)
+ "Apply PREDICATE to each element on LIST.
+Args: PREDICATE LIST &rest EXTRA-ARGS.
+Return a new list consisting of those elements that PREDICATE
+returns non-nil for.
+
+If more than two arguments are given the remaining args are
+passed to PREDICATE."
+ ;; Avoid recursion - this should work for LONG lists also!
+ (let* ((head (cons 'dummy-header nil))
+ (tail head))
+ (while list
+ (if (apply predicate (car list) extra-args)
+ (setq tail (setcdr tail (list (car list)))))
+ (setq list (cdr list)))
+ (cdr head)))
+
+(defun cvs-update-no-prompt ()
+ "Run cvs update in current directory."
+ (interactive)
+ (cvs-do-update default-directory nil nil))
+
+(defun cvs-do-update (directory local dont-change-disc)
+ "Do a 'cvs update' in DIRECTORY.
+If LOCAL is non-nil 'cvs update -l' is executed.
+If DONT-CHANGE-DISC is non-nil 'cvs -n update' is executed.
+Both LOCAL and DONT-CHANGE-DISC may be non-nil simultaneously.
+
+*Note*: DONT-CHANGE-DISC does not yet work. The parser gets confused."
+ (save-some-buffers)
+ (let* ((this-dir (file-name-as-directory (expand-file-name directory)))
+ (use-this-window (equal (buffer-name (current-buffer))
+ cvs-buffer-name))
+ (update-buffer (generate-new-buffer
+ (concat (file-name-nondirectory
+ (substring this-dir 0 -1))
+ "-update")))
+ cvs-process args)
+
+ ;; The *cvs* buffer is killed to avoid confusion - is the update ready
+ ;; or not?
+ (if (get-buffer cvs-buffer-name)
+ (kill-buffer cvs-buffer-name))
+
+ ;; Generate "-n update -l".
+ (if local (setq args (list "-l")))
+ (setq args (cons "update" args))
+ (if dont-change-disc (setq args (cons "-n" args)))
+
+ ;; Set up the buffer that receives the output from "cvs update".
+ (if use-this-window
+ (switch-to-buffer update-buffer)
+ (set-buffer update-buffer)
+ (display-buffer update-buffer))
+
+ (setq default-directory this-dir)
+ (setq cvs-process
+ (let ((process-connection-type nil)) ; Use a pipe, not a pty.
+ (apply 'start-process "cvs" update-buffer cvs-program args)))
+
+ (setq mode-line-process
+ (concat ": "
+ (symbol-name (process-status cvs-process))))
+ (set-buffer-modified-p (buffer-modified-p)) ; Update the mode line.
+ (set-process-sentinel cvs-process 'cvs-sentinel)
+
+ ;; Work around a bug in emacs 18.57 and earlier.
+ (setq cvs-buffers-to-delete
+ (cvs-delete-unused-temporary-buffers cvs-buffers-to-delete))))
+
+(defun cvs-delete-unused-temporary-buffers (list)
+ "Delete all buffers on LIST that is not visible.
+Return a list of all buffers that still is alive."
+
+ (cond
+ ((null list) nil)
+ ((get-buffer-window (car list))
+ (cons (car list)
+ (cvs-delete-unused-temporary-buffers (cdr list))))
+ (t
+ (kill-buffer (car list))
+ (cvs-delete-unused-temporary-buffers (cdr list)))))
+
+
+(put 'cvs-mode 'mode-class 'special)
+
+(defun cvs-mode ()
+ "\\<cvs-mode-map>Mode used for pcl-cvs, a frontend to CVS.
+
+To get the *cvs* buffer you should use ``\\[cvs-update]''.
+
+Full documentation is in the TeXinfo file. These are the most useful commands:
+
+\\[cookie-previous-cookie] Move up. \\[cookie-next-cookie] Move down.
+\\[cvs-commit] Commit file. \\[cvs-update-no-prompt] Reupdate directory.
+\\[cvs-mark] Mark file/dir. \\[cvs-unmark] Unmark file/dir.
+\\[cvs-mark-all-files] Mark all files. \\[cvs-unmark-all-files] Unmark all files.
+\\[cvs-find-file] Edit file/run Dired. \\[cvs-find-file-other-window] Find file or run Dired in other window.
+\\[cvs-remove-handled] Remove processed entries. \\[cvs-add-change-log-entry-other-window] Write ChangeLog in other window.
+\\[cvs-add] Add to repository. \\[cvs-remove-file] Remove file.
+\\[cvs-diff-cvs] Diff between base revision. \\[cvs-diff-backup] Diff backup file.
+\\[cvs-acknowledge] Delete line from buffer. \\[cvs-ignore] Add file to the .cvsignore file.
+\\[cvs-log] Run ``cvs log''. \\[cvs-status] Run ``cvs status''.
+
+Entry to this mode runs cvs-mode-hook.
+This description is updated for release 1.02 of pcl-cvs.
+All bindings:
+\\{cvs-mode-map}"
+ (interactive)
+ (setq major-mode 'cvs-mode)
+ (setq mode-name "CVS")
+ (setq buffer-read-only nil)
+ (buffer-flush-undo (current-buffer))
+ (make-local-variable 'goal-column)
+ (setq goal-column cvs-cursor-column)
+ (use-local-map cvs-mode-map)
+ (run-hooks 'cvs-mode-hook))
+
+(defun cvs-sentinel (proc msg)
+ "Sentinel for the cvs update process.
+This is responsible for parsing the output from the cvs update when
+it is finished."
+ (cond
+ ((null (buffer-name (process-buffer proc)))
+ ;; buffer killed
+ (set-process-buffer proc nil))
+ ((memq (process-status proc) '(signal exit))
+ (let* ((obuf (current-buffer))
+ (omax (point-max))
+ (opoint (point)))
+ ;; save-excursion isn't the right thing if
+ ;; process-buffer is current-buffer
+ (unwind-protect
+ (progn
+ (set-buffer (process-buffer proc))
+ (setq mode-line-process
+ (concat ": "
+ (symbol-name (process-status proc))))
+ (cvs-parse-buffer)
+ (setq cvs-buffers-to-delete
+ (cons (process-buffer proc) cvs-buffers-to-delete)))
+ (set-buffer-modified-p (buffer-modified-p)))
+ (if (equal obuf (process-buffer proc))
+ nil
+ (set-buffer (process-buffer proc))
+ (if (< opoint omax)
+ (goto-char opoint))
+ (set-buffer obuf))))))
+
+(defun cvs-skip-line (regexp errormsg &optional arg)
+ "Like forward-line, but check that the skipped line matches REGEXP.
+If it doesn't match REGEXP (error ERRORMSG) is called.
+If optional ARG, a number, is given the ARGth parenthesized expression
+in the REGEXP is returned as a string.
+Point should be in column 1 when this function is called."
+ (cond
+ ((looking-at regexp)
+ (forward-line 1)
+ (if arg
+ (buffer-substring (match-beginning arg)
+ (match-end arg))))
+ (t
+ (error errormsg))))
+
+(defun cvs-get-current-dir (dirname)
+ "Return current working directory, suitable for cvs-parse-buffer.
+Args: DIRNAME.
+Concatenates default-directory and DIRNAME to form an absolute path."
+ (if (string= "." dirname)
+ (substring default-directory 0 -1)
+ (concat default-directory dirname)))
+
+
+(defun cvs-parse-buffer ()
+ "Parse the current buffer and select a *cvs* buffer.
+Signals an error if unexpected output was detected in the buffer."
+ (goto-char (point-min))
+ (let ((buf (get-buffer-create cvs-buffer-name))
+ (current-dir default-directory)
+ (root-dir default-directory)
+ (parse-buf (current-buffer)))
+
+ (cookie-create
+ buf 'cvs-pp cvs-startup-message ;Se comment above cvs-startup-message.
+ "---------- End -----")
+
+ (cookie-enter-first
+ buf
+ (cvs-create-fileinfo
+ 'DIRCHANGE current-dir
+ nil ""))
+
+ (while (< (point) (point-max))
+ (cond
+
+ ;; CVS is descending a subdirectory.
+
+ ((looking-at "cvs update: Updating \\(.*\\)$")
+ (setq current-dir
+ (cvs-get-current-dir
+ (buffer-substring (match-beginning 1) (match-end 1))))
+
+ ;; Omit empty directories.
+ (if (eq (cvs-fileinfo->type (cookie-last buf))
+ 'DIRCHANGE)
+ (cookie-delete-last buf))
+
+ (cookie-enter-last
+ buf
+ (cvs-create-fileinfo
+ 'DIRCHANGE current-dir
+ nil (buffer-substring (match-beginning 0)
+ (match-end 0))))
+ (forward-line 1))
+
+ ;; File removed, since it is removed (by third party) in repository.
+
+ ((or (looking-at "cvs update: warning: \\(.*\\) is not (any longer) \
+pertinent")
+ (looking-at "cvs update: \\(.*\\) is no longer in the repository"))
+ (cookie-enter-last
+ buf
+ (cvs-create-fileinfo
+ 'CVS-REMOVED current-dir
+ (file-name-nondirectory
+ (buffer-substring (match-beginning 1) (match-end 1)))
+ (buffer-substring (match-beginning 0)
+ (match-end 0))))
+ (forward-line 1))
+
+ ;; File removed by you, but recreated by cvs. Ignored.
+
+ ((looking-at "cvs update: warning: .* was lost$")
+ (forward-line 1))
+
+ ;; A file that has been created by you, but added to the cvs
+ ;; repository by another.
+
+ ((looking-at "^cvs update: move away \\(.*\\); it is in the way$")
+ (cookie-enter-last
+ buf
+ (cvs-create-fileinfo
+ 'MOVE-AWAY current-dir
+ (file-name-nondirectory
+ (buffer-substring (match-beginning 1) (match-end 1)))
+ (buffer-substring (match-beginning 0)
+ (match-end 0))))
+ (forward-line 1))
+
+ ;; Empty line. Probably inserted by mistake by user (or developer :-)
+ ;; Ignore.
+
+ ((looking-at "^$")
+ (forward-line 1))
+
+ ;; Cvs waits for a lock. Ignore.
+
+ ((looking-at
+ "^cvs update: \\[..:..:..\\] waiting for .*lock in ")
+ (forward-line 1))
+
+ ;; File removed in repository, but edited by you.
+
+ ((looking-at
+ "cvs update: conflict: \\(.*\\) is modified but no longer \
+in the repository$")
+ (cookie-enter-last
+ buf
+ (cvs-create-fileinfo
+ 'REM-CONFLICT current-dir
+ (file-name-nondirectory
+ (buffer-substring (match-beginning 1) (match-end 1)))
+ (buffer-substring (match-beginning 0)
+ (match-end 0))))
+ (forward-line 1))
+
+ ((looking-at
+ "cvs update: conflict: removed \\(.*\\) was modified by second party")
+ (cvs-create-fileinfo
+ 'MOD-CONFLICT current-dir
+ (buffer-substring (match-beginning 1) (match-end 1))
+ (buffer-substring (match-beginning 0) (match-end 0)))
+ (forward-line 1))
+
+ ((looking-at "cvs update: in directory ")
+ (let ((start (point)))
+ (forward-line 1)
+ (cvs-skip-line
+ (regexp-quote "cvs [update aborted]: there is no repository ")
+ "Unexpected cvs output.")
+ (cookie-enter-last
+ buf
+ (cvs-create-fileinfo
+ 'REPOS-MISSING current-dir
+ nil
+ (buffer-substring start (point))))))
+
+ ;; The file is copied from the repository.
+
+ ((looking-at "U \\(.*\\)$")
+ (cookie-enter-last
+ buf
+ (let ((fileinfo
+ (cvs-create-fileinfo
+ 'UPDATED current-dir
+ (file-name-nondirectory
+ (buffer-substring (match-beginning 1) (match-end 1)))
+ (buffer-substring (match-beginning 0) (match-end 0)))))
+ (cvs-set-fileinfo->handled fileinfo t)
+ fileinfo))
+ (forward-line 1))
+
+ ;; The file is modified by the user, and untouched in the repository.
+
+ ((looking-at "M \\(.*\\)$")
+ (cookie-enter-last
+ buf
+ (cvs-create-fileinfo
+ 'MODIFIED current-dir
+ (file-name-nondirectory
+ (buffer-substring (match-beginning 1) (match-end 1)))
+ (buffer-substring (match-beginning 0) (match-end 0))))
+ (forward-line 1))
+
+ ;; The file is "cvs add"ed, but not "cvs ci"ed.
+
+ ((looking-at "A \\(.*\\)$")
+ (cookie-enter-last
+ buf
+ (cvs-create-fileinfo
+ 'ADDED current-dir
+ (file-name-nondirectory
+ (buffer-substring (match-beginning 1) (match-end 1)))
+ (buffer-substring (match-beginning 0) (match-end 0))))
+ (forward-line 1))
+
+ ;; The file is "cvs remove"ed, but not "cvs ci"ed.
+
+ ((looking-at "R \\(.*\\)$")
+ (cookie-enter-last
+ buf
+ (cvs-create-fileinfo
+ 'REMOVED current-dir
+ (file-name-nondirectory
+ (buffer-substring (match-beginning 1) (match-end 1)))
+ (buffer-substring (match-beginning 0) (match-end 0))))
+ (forward-line 1))
+
+ ;; Unknown file.
+
+ ((looking-at "? \\(.*\\)$")
+ (cookie-enter-last
+ buf
+ (cvs-create-fileinfo
+ 'UNKNOWN current-dir
+ (file-name-nondirectory
+ (buffer-substring (match-beginning 1) (match-end 1)))
+ (buffer-substring (match-beginning 0) (match-end 0))))
+ (forward-line 1))
+ (t
+
+ ;; CVS has decided to merge someone elses changes into this
+ ;; document. This leads to a lot of garbage being printed.
+ ;; First there is two lines that contains no information
+ ;; that we skip (but we check that we recognize them).
+
+ (let ((complex-start (point))
+ initial-revision filename)
+
+ (cvs-skip-line "^RCS file: .*$" "Parse error.")
+ (setq initial-revision
+ (cvs-skip-line "^retrieving revision \\(.*\\)$"
+ "Unexpected output from cvs." 1))
+ (cvs-skip-line "^retrieving revision .*$"
+ "Unexpected output from cvs.")
+
+ ;; Get the file name from the next line.
+
+ (setq
+ filename
+ (cvs-skip-line
+ "^Merging differences between [0-9.]+ and [0-9.]+ into \\(.*\\)$"
+ "Unexpected output from cvs."
+ 1))
+
+ (cond
+
+ ;; The file was successfully merged.
+
+ ((looking-at "^M ")
+ (forward-line 1)
+ (let ((fileinfo
+ (cvs-create-fileinfo
+ 'MERGED current-dir
+ filename
+ (buffer-substring complex-start (point)))))
+ (cvs-set-fileinfo->backup-file
+ fileinfo
+ (concat cvs-bakprefix filename "." initial-revision))
+ (cookie-enter-last
+ buf fileinfo)))
+
+ ;; A conflicting merge.
+
+ (t
+ (cvs-skip-line "^merge: overlaps during merge$"
+ "Unexpected output from cvs.")
+ (cvs-skip-line "^cvs update: conflicts found in "
+ "Unexpected output from cvs.")
+ (cvs-skip-line "^C " "Unexpected cvs output.")
+ (let ((fileinfo
+ (cvs-create-fileinfo
+ 'CONFLICT current-dir
+ filename
+ (buffer-substring complex-start (point)))))
+
+ (cvs-set-fileinfo->backup-file
+ fileinfo
+ (concat cvs-bakprefix filename "." initial-revision))
+
+ (cookie-enter-last buf fileinfo))))))))
+
+ ;; All parsing is done.
+
+ ;; If the last entry is a directory, remove it.
+ (if (eq (cvs-fileinfo->type (cookie-last buf))
+ 'DIRCHANGE)
+ (cookie-delete-last buf))
+
+ (set-buffer buf)
+ (cvs-mode)
+ (setq cookie-last-tin (cookie-nth buf 0))
+ (goto-char (point-min))
+ (cookie-previous-cookie buf (point-min) 1)
+ (setq default-directory root-dir)
+ (if (get-buffer-window parse-buf)
+ (set-window-buffer (get-buffer-window parse-buf) buf)
+ (display-buffer buf))))
+
+
+(defun cvs-pp (fileinfo)
+ "Pretty print FILEINFO into a string."
+
+ (let ((a (cvs-fileinfo->type fileinfo))
+ (s (if (cvs-fileinfo->marked fileinfo)
+ "*" " "))
+ (f (cvs-fileinfo->file-name fileinfo))
+ (ci (if (cvs-fileinfo->handled fileinfo)
+ " " "ci")))
+ (cond
+ ((eq a 'UPDATED)
+ (format "%s Updated %s" s f))
+ ((eq a 'MODIFIED)
+ (format "%s Modified %s %s" s ci f))
+ ((eq a 'MERGED)
+ (format "%s Merged %s %s" s ci f))
+ ((eq a 'CONFLICT)
+ (format "%s Conflict %s" s f))
+ ((eq a 'ADDED)
+ (format "%s Added %s %s" s ci f))
+ ((eq a 'REMOVED)
+ (format "%s Removed %s %s" s ci f))
+ ((eq a 'UNKNOWN)
+ (format "%s Unknown %s" s f))
+ ((eq a 'CVS-REMOVED)
+ (format "%s Removed from repository: %s" s f))
+ ((eq a 'REM-CONFLICT)
+ (format "%s Conflict: Removed from repository, changed by you: %s" s f))
+ ((eq a 'MOD-CONFLICT)
+ (format "%s Conflict: Removed by you, changed in repository: %s" s f))
+ ((eq a 'DIRCHANGE)
+ (format "\nIn directory %s:"
+ (cvs-fileinfo->dir fileinfo)))
+ ((eq a 'MOVE-AWAY)
+ (format "%s Move away %s - it is in the way" s f))
+ ((eq a 'REPOS-MISSING)
+ (format " This repository is missing! Remove this dir manually."))
+ (t
+ (format "%s Internal error! %s" s f)))))
+
+
+;;; You can define your own keymap in .emacs. pcl-cvs.el won't overwrite it.
+
+(if cvs-mode-map
+ nil
+ (setq cvs-mode-map (make-keymap))
+ (suppress-keymap cvs-mode-map)
+ (define-key cvs-mode-map " " 'cookie-next-cookie)
+ (define-key cvs-mode-map "?" 'describe-mode)
+ (define-key cvs-mode-map "A" 'cvs-add-change-log-entry-other-window)
+ (define-key cvs-mode-map "M" 'cvs-mark-all-files)
+ (define-key cvs-mode-map "U" 'cvs-unmark-all-files)
+ (define-key cvs-mode-map "\C-?" 'cvs-unmark-up)
+ (define-key cvs-mode-map "\C-n" 'cookie-next-cookie)
+ (define-key cvs-mode-map "\C-p" 'cookie-previous-cookie)
+ (define-key cvs-mode-map "a" 'cvs-add)
+ (define-key cvs-mode-map "b" 'cvs-diff-backup)
+ (define-key cvs-mode-map "c" 'cvs-commit)
+ (define-key cvs-mode-map "d" 'cvs-diff-cvs)
+ (define-key cvs-mode-map "f" 'cvs-find-file)
+ (define-key cvs-mode-map "g" 'cvs-update-no-prompt)
+ (define-key cvs-mode-map "i" 'cvs-ignore)
+ (define-key cvs-mode-map "l" 'cvs-log)
+ (define-key cvs-mode-map "m" 'cvs-mark)
+ (define-key cvs-mode-map "n" 'cookie-next-cookie)
+ (define-key cvs-mode-map "o" 'cvs-find-file-other-window)
+ (define-key cvs-mode-map "p" 'cookie-previous-cookie)
+ (define-key cvs-mode-map "r" 'cvs-remove-file)
+ (define-key cvs-mode-map "s" 'cvs-status)
+ (define-key cvs-mode-map "\C-k" 'cvs-acknowledge)
+ (define-key cvs-mode-map "x" 'cvs-remove-handled)
+ (define-key cvs-mode-map "u" 'cvs-unmark))
+
+
+(defun cvs-get-marked ()
+ "Return a list of all selected tins.
+If there are any marked tins, return them.
+Otherwise, if the cursor selects a directory, return all files in it.
+Otherwise return (a list containing) the file the cursor points to, or
+an empty list if it doesn't point to a file at all."
+
+ (cond
+ ;; Any marked cookies?
+ ((cookie-collect-tins (current-buffer)
+ 'cvs-fileinfo->marked))
+ ;; Nope.
+ (t
+ (let ((sel (cookie-get-selection
+ (current-buffer) (point) cookie-last-tin)))
+ (cond
+ ;; If a directory is selected, all it members are returned.
+ ((and sel (eq (cvs-fileinfo->type
+ (cookie-cookie (current-buffer) sel))
+ 'DIRCHANGE))
+ (cookie-collect-tins
+ (current-buffer) 'cvs-dir-member-p
+ (cvs-fileinfo->dir (cookie-cookie (current-buffer) sel))))
+ (t
+ (list sel)))))))
+
+
+(defun cvs-dir-member-p (fileinfo dir)
+ "Return true if FILEINFO represents a file in directory DIR."
+ (and (not (eq (cvs-fileinfo->type fileinfo) 'DIRCHANGE))
+ (string= (cvs-fileinfo->dir fileinfo) dir)))
+
+(defun cvs-dir-empty-p (cvs-buf tin)
+ "Return non-nil if TIN is a directory that is empty.
+Args: CVS-BUF TIN."
+ (and (eq (cvs-fileinfo->type (cookie-cookie cvs-buf tin)) 'DIRCHANGE)
+ (or (not (cookie-next cvs-buf tin))
+ (eq (cvs-fileinfo->type (cookie-cookie cvs-buf
+ (cookie-next cvs-buf tin)))
+ 'DIRCHANGE))))
+
+(defun cvs-remove-handled ()
+ "Remove all lines that are handled.
+Empty directories are removed."
+ (interactive)
+ ;; Pass one: remove files that are handled.
+ (cookie-filter (current-buffer)
+ (function
+ (lambda (fileinfo) (not (cvs-fileinfo->handled fileinfo)))))
+ ;; Pass two: remove empty directories.
+ (cookie-filter-tins (current-buffer)
+ (function
+ (lambda (tin)
+ (not (cvs-dir-empty-p (current-buffer) tin))))))
+
+(defun cvs-mark (pos)
+ "Mark a fileinfo. Args: POS.
+If the fileinfo is a directory, all the contents of that directory are
+marked instead. A directory can never be marked.
+POS is a buffer position."
+
+ (interactive "d")
+
+ (let* ((tin (cookie-get-selection
+ (current-buffer) pos cookie-last-tin))
+ (sel (cookie-cookie (current-buffer) tin)))
+
+ (cond
+ ;; Does POS point to a directory? If so, mark all files in that directory.
+ ((eq (cvs-fileinfo->type sel) 'DIRCHANGE)
+ (cookie-map
+ (function (lambda (f dir)
+ (cond
+ ((cvs-dir-member-p f dir)
+ (cvs-set-fileinfo->marked f t)
+ t)))) ;Tell cookie to redisplay this cookie.
+ (current-buffer)
+ (cvs-fileinfo->dir sel)))
+ (t
+ (cvs-set-fileinfo->marked sel t)
+ (cookie-invalidate-tins (current-buffer) tin)
+ (cookie-next-cookie (current-buffer) pos 1)))))
+
+
+(defun cvs-committable (tin cvs-buf)
+ "Check if the TIN is committable.
+It is committable if it
+ a) is not handled and
+ b) is either MODIFIED, ADDED, REMOVED, MERGED or CONFLICT."
+ (let* ((fileinfo (cookie-cookie cvs-buf tin))
+ (type (cvs-fileinfo->type fileinfo)))
+ (and (not (cvs-fileinfo->handled fileinfo))
+ (or (eq type 'MODIFIED)
+ (eq type 'ADDED)
+ (eq type 'REMOVED)
+ (eq type 'MERGED)
+ (eq type 'CONFLICT)))))
+
+(defun cvs-commit ()
+
+ "Check in all marked files, or the current file.
+The user will be asked for a log message in a buffer.
+If cvs-erase-input-buffer is non-nil that buffer will be erased.
+Otherwise mark and point will be set around the entire contents of the
+buffer so that it is easy to kill the contents of the buffer with \\[kill-region]."
+
+ (interactive)
+
+ (let* ((cvs-buf (current-buffer))
+ (marked (cvs-filter (function cvs-committable)
+ (cvs-get-marked)
+ cvs-buf)))
+ (if (null marked)
+ (error "Nothing to commit!")
+ (pop-to-buffer (get-buffer-create cvs-commit-prompt-buffer))
+ (goto-char (point-min))
+
+ (if cvs-erase-input-buffer
+ (erase-buffer)
+ (push-mark (point-max)))
+ (cvs-edit-mode)
+ (make-local-variable 'cvs-commit-list)
+ (setq cvs-commit-list marked)
+ (make-local-variable 'cvs-cvs-buffer)
+ (setq cvs-cvs-buffer cvs-buf)
+ (message "Press C-c C-c when you are done editing."))))
+
+
+(defun cvs-edit-done ()
+ "Commit the files to the repository."
+ (interactive)
+ (save-some-buffers)
+ (let ((cc-list cvs-commit-list)
+ (cc-buffer cvs-cvs-buffer)
+ (msg-buffer (current-buffer))
+ (msg (buffer-substring (point-min) (point-max))))
+ (pop-to-buffer cc-buffer)
+ (bury-buffer msg-buffer)
+ (cvs-use-temp-buffer)
+ (message "Committing...")
+ (cvs-execute-list cc-list cvs-program (list "commit" "-m" msg))
+ (mapcar (function
+ (lambda (tin)
+ (cvs-set-fileinfo->handled (cookie-cookie cc-buffer tin) t)))
+ cc-list)
+ (apply 'cookie-invalidate-tins cc-buffer cc-list)
+ (set-buffer cc-buffer)
+ (if cvs-auto-remove-handled
+ (cvs-remove-handled)))
+
+ (message "Committing... Done."))
+
+
+(defun cvs-execute-list (tin-list program constant-args)
+ "Run PROGRAM on all elements on TIN-LIST.
+Args: TIN-LIST PROGRAM CONSTANT-ARGS
+The PROGRAM will be called with pwd set to the directory the
+files reside in. CONSTANT-ARGS should be a list of strings. The
+arguments given to the program will be CONSTANT-ARGS followed by all
+the files (from TIN-LIST) that resides in that directory. If the files
+in TIN-LIST resides in different directories the PROGRAM will be run
+once for each directory (if all files in the same directory appears
+after each other."
+
+ (while tin-list
+ (let ((current-dir (cvs-fileinfo->dir
+ (cookie-cookie cvs-buffer-name
+ (car tin-list))))
+ arg-list arg-str)
+
+ ;; Collect all marked files in this directory.
+
+ (while (and tin-list
+ (string=
+ current-dir
+ (cvs-fileinfo->dir
+ (cookie-cookie cvs-buffer-name (car tin-list)))))
+ (setq arg-list
+ (cons (cvs-fileinfo->file-name
+ (cookie-cookie cvs-buffer-name (car tin-list)))
+ arg-list))
+ (setq tin-list (cdr tin-list)))
+
+ (setq arg-list (nreverse arg-list))
+
+ ;; Execute the command on all the files that were collected.
+
+ (setq default-directory (file-name-as-directory current-dir))
+ (insert (format "=== cd %s\n" default-directory))
+ (insert (format "=== %s %s\n\n"
+ program
+ (mapconcat '(lambda (foo) foo)
+ (nconc (copy-sequence constant-args)
+ arg-list)
+ " ")))
+ (apply 'call-process program nil t t
+ (nconc (copy-sequence constant-args) arg-list))
+ (goto-char (point-max)))))
+
+
+(defun cvs-execute-single-file-list (tin-list extractor program constant-args)
+ "Run PROGRAM on all elements on TIN-LIST.
+
+Args: TIN-LIST EXTRACTOR PROGRAM CONSTANT-ARGS
+
+The PROGRAM will be called with pwd set to the directory the files
+reside in. CONSTANT-ARGS is a list of strings to pass as arguments to
+PROGRAM. The arguments given to the program will be CONSTANT-ARGS
+followed by the list that EXTRACTOR returns.
+
+EXTRACTOR will be called once for each file on TIN-LIST. It is given
+one argument, the cvs-fileinfo. It can return t, which means ignore
+this file, or a list of arguments to send to the program."
+
+ (while tin-list
+ (let ((default-directory (file-name-as-directory
+ (cvs-fileinfo->dir
+ (cookie-cookie cvs-buffer-name
+ (car tin-list)))))
+ (arg-list
+ (funcall extractor
+ (cookie-cookie cvs-buffer-name (car tin-list)))))
+
+ ;; Execute the command unless extractor returned t.
+
+ (if (eq arg-list t)
+ nil
+ (insert (format "=== cd %s\n" default-directory))
+ (insert (format "=== %s %s\n\n"
+ program
+ (mapconcat '(lambda (foo) foo)
+ (nconc (copy-sequence constant-args)
+ arg-list)
+ " ")))
+ (apply 'call-process program nil t t
+ (nconc (copy-sequence constant-args) arg-list))
+ (goto-char (point-max))))
+ (setq tin-list (cdr tin-list))))
+
+
+(defun cvs-edit-mode ()
+ "\\<cvs-edit-mode-map>Mode for editing cvs log messages.
+Commands:
+\\[cvs-edit-done] checks in the file when you are ready.
+This mode is based on fundamental mode."
+ (interactive)
+ (use-local-map cvs-edit-mode-map)
+ (setq major-mode 'cvs-edit-mode)
+ (setq mode-name "CVS Log")
+ (auto-fill-mode 1))
+
+
+(if cvs-edit-mode-map
+ nil
+ (setq cvs-edit-mode-map (make-sparse-keymap))
+ (define-prefix-command 'cvs-control-c-prefix)
+ (define-key cvs-edit-mode-map "\C-c" 'cvs-control-c-prefix)
+ (define-key cvs-edit-mode-map "\C-c\C-c" 'cvs-edit-done))
+
+
+(defun cvs-diff-cvs ()
+ "Diff the selected files against the repository.
+The flags the variable cvs-cvs-diff-flags will be passed to ``cvs diff''."
+ (interactive)
+
+ (save-some-buffers)
+ (let ((marked (cvs-get-marked)))
+ (cvs-use-temp-buffer)
+ (message "cvsdiffing...")
+ (cvs-execute-list marked cvs-program (cons "diff" cvs-cvs-diff-flags)))
+ (message "cvsdiffing... Done."))
+
+
+(defun cvs-backup-diffable (tin cvs-buf)
+ "Check if the TIN is backup-diffable.
+It must have a backup file to be diffable."
+ (cvs-fileinfo->backup-file (cookie-cookie cvs-buf tin)))
+
+(defun cvs-diff-backup ()
+ "Diff the files against the backup file.
+This command can be used on files that are marked with \"Merged\"
+or \"Conflict\" in the *cvs* buffer.
+
+The flags in cvs-diff-flags will be passed to ``diff''."
+
+ (interactive)
+ (save-some-buffers)
+ (let ((marked (cvs-filter (function cvs-backup-diffable)
+ (cvs-get-marked)
+ (current-buffer))))
+ (if (null marked)
+ (error "No ``Conflict'' or ``Merged'' file selected!"))
+ (cvs-use-temp-buffer)
+ (message "diffing...")
+ (cvs-execute-single-file-list
+ marked 'cvs-diff-backup-extractor cvs-diff-program cvs-diff-flags))
+ (message "diffing... Done."))
+
+
+(defun cvs-diff-backup-extractor (fileinfo)
+ "Return the filename and the name of the backup file as a list.
+Signal an error if there is no backup file."
+ (if (null (cvs-fileinfo->backup-file fileinfo))
+ (error "%s has no backup file."
+ (concat
+ (file-name-as-directory (cvs-fileinfo->dir fileinfo))
+ (cvs-fileinfo->file-name fileinfo))))
+ (list (cvs-fileinfo->file-name fileinfo)
+ (cvs-fileinfo->backup-file fileinfo)))
+
+(defun cvs-find-file-other-window (pos)
+ "Select a buffer containing the file in another window.
+Args: POS"
+ (interactive "d")
+ (save-some-buffers)
+ (let* ((cookie-last-tin
+ (cookie-get-selection (current-buffer) pos cookie-last-tin))
+ (type (cvs-fileinfo->type (cookie-cookie (current-buffer)
+ cookie-last-tin))))
+ (cond
+ ((or (eq type 'REMOVED)
+ (eq type 'CVS-REMOVED))
+ (error "Can't visit a removed file."))
+ ((eq type 'DIRCHANGE)
+ (let ((obuf (current-buffer))
+ (odir default-directory))
+ (setq default-directory
+ (file-name-as-directory
+ (cvs-fileinfo->dir
+ (cookie-cookie (current-buffer) cookie-last-tin))))
+ (dired-other-window default-directory)
+ (set-buffer obuf)
+ (setq default-directory odir)))
+ (t
+ (find-file-other-window (cvs-full-path (current-buffer)
+ cookie-last-tin))))))
+
+(defun cvs-full-path (buffer tin)
+ "Return the full path for the file that is described in TIN.
+Args: BUFFER TIN."
+ (concat
+ (file-name-as-directory
+ (cvs-fileinfo->dir (cookie-cookie buffer tin)))
+ (cvs-fileinfo->file-name (cookie-cookie buffer tin))))
+
+(defun cvs-find-file (pos)
+ "Select a buffer containing the file in another window.
+Args: POS"
+ (interactive "d")
+ (let* ((cvs-buf (current-buffer))
+ (cookie-last-tin (cookie-get-selection cvs-buf pos cookie-last-tin))
+ (fileinfo (cookie-cookie cvs-buf cookie-last-tin))
+ (type (cvs-fileinfo->type fileinfo)))
+ (cond
+ ((or (eq type 'REMOVED)
+ (eq type 'CVS-REMOVED))
+ (error "Can't visit a removed file."))
+ ((eq type 'DIRCHANGE)
+ (let ((odir default-directory))
+ (setq default-directory
+ (file-name-as-directory (cvs-fileinfo->dir fileinfo)))
+ (dired default-directory)
+ (set-buffer cvs-buf)
+ (setq default-directory odir)))
+ (t
+ (find-file (cvs-full-path cvs-buf cookie-last-tin))))))
+
+(defun cvs-mark-all-files ()
+ "Mark all files.
+Directories are not marked."
+ (interactive)
+ (cookie-map (function (lambda (cookie)
+ (cond
+ ((not (eq (cvs-fileinfo->type cookie) 'DIRCHANGE))
+ (cvs-set-fileinfo->marked cookie t)
+ t))))
+ (current-buffer)))
+
+
+(defun cvs-unmark (pos)
+ "Unmark a fileinfo. Args: POS."
+ (interactive "d")
+
+ (let* ((tin (cookie-get-selection
+ (current-buffer) pos cookie-last-tin))
+ (sel (cookie-cookie (current-buffer) tin)))
+
+ (cond
+ ((eq (cvs-fileinfo->type sel) 'DIRCHANGE)
+ (cookie-map
+ (function (lambda (f dir)
+ (cond
+ ((cvs-dir-member-p f dir)
+ (cvs-set-fileinfo->marked f nil)
+ t))))
+ (current-buffer)
+ (cvs-fileinfo->dir sel)))
+ (t
+ (cvs-set-fileinfo->marked sel nil)
+ (cookie-invalidate-tins (current-buffer) tin)
+ (cookie-next-cookie (current-buffer) pos 1)))))
+
+(defun cvs-unmark-all-files ()
+ "Unmark all files.
+Directories are also unmarked, but that doesn't matter, since
+they should always be unmarked."
+ (interactive)
+ (cookie-map (function (lambda (cookie)
+ (cvs-set-fileinfo->marked cookie nil)
+ t))
+ (current-buffer)))
+
+
+(defun cvs-do-removal (cvs-buf tins)
+ "Remove files.
+Args: CVS-BUF TINS.
+CVS-BUF is the cvs buffer. TINS is a list of tins that the
+user wants to delete. The files are deleted. If the type of
+the tin is 'UNKNOWN the tin is removed from the buffer. If it
+is anything else the file is added to a list that should be
+`cvs remove'd and the tin is changed to be of type 'REMOVED.
+
+Returns a list of tins files that should be `cvs remove'd."
+ (cvs-use-temp-buffer)
+ (mapcar 'cvs-insert-full-path tins)
+ (cond
+ ((and tins (yes-or-no-p (format "Delete %d files? " (length tins))))
+ (let (files-to-remove)
+ (while tins
+ (let* ((tin (car tins))
+ (fileinfo (cookie-cookie cvs-buf tin))
+ (type (cvs-fileinfo->type fileinfo)))
+ (if (not (or (eq type 'REMOVED) (eq type 'CVS-REMOVED)))
+ (progn
+ (delete-file (cvs-full-path cvs-buf tin))
+ (cond
+ ((or (eq type 'UNKNOWN) (eq type 'MOVE-AWAY))
+ (cookie-delete cvs-buf tin))
+ (t
+ (setq files-to-remove (cons tin files-to-remove))
+ (cvs-set-fileinfo->type fileinfo 'REMOVED)
+ (cvs-set-fileinfo->handled fileinfo nil)
+ (cookie-invalidate-tins cvs-buf tin))))))
+ (setq tins (cdr tins)))
+ files-to-remove))
+ (t nil)))
+
+
+
+(defun cvs-remove-file ()
+ "Remove all marked files."
+ (interactive)
+ (let ((files-to-remove (cvs-do-removal (current-buffer) (cvs-get-marked))))
+ (if (null files-to-remove)
+ nil
+ (cvs-use-temp-buffer)
+ (message "removing from repository...")
+ (cvs-execute-list files-to-remove cvs-program '("remove"))
+ (message "removing from repository... done."))))
+
+(defun cvs-acknowledge ()
+ "Remove all marked files from the buffer."
+ (interactive)
+
+ (mapcar (function (lambda (tin)
+ (cookie-delete (current-buffer) tin)))
+ (cvs-get-marked))
+ (setq cookie-last-tin nil))
+
+
+(defun cvs-unmark-up (pos)
+ "Unmark the file on the previous line.
+Takes one argument POS, a buffer position."
+ (interactive "d")
+ (cookie-previous-cookie (current-buffer) pos 1)
+ (cvs-set-fileinfo->marked (cookie-cookie (current-buffer) cookie-last-tin)
+ nil)
+ (cookie-invalidate-tins (current-buffer) cookie-last-tin))
+
+(defun cvs-add-file-update-buffer (cvs-buf tin)
+ "Subfunction to cvs-add. Internal use only.
+Update the display. Return non-nil if `cvs add' should be called on this
+file. Args: CVS-BUF TIN.
+Returns 'ADD or 'RESURRECT."
+ (let ((fileinfo (cookie-cookie cvs-buf tin)))
+ (cond
+ ((eq (cvs-fileinfo->type fileinfo) 'UNKNOWN)
+ (cvs-set-fileinfo->type fileinfo 'ADDED)
+ (cookie-invalidate-tins cvs-buf tin)
+ 'ADD)
+ ((eq (cvs-fileinfo->type fileinfo) 'REMOVED)
+ (cvs-set-fileinfo->type fileinfo 'UPDATED)
+ (cvs-set-fileinfo->handled fileinfo t)
+ (cookie-invalidate-tins cvs-buf tin)
+ 'RESURRECT))))
+
+(defun cvs-add-sub (cvs-buf candidates)
+ "Internal use only.
+Args: CVS-BUF CANDIDATES.
+CANDIDATES is a list of tins. Updates the CVS-BUF and returns a pair of lists.
+The first list is unknown tins that shall be `cvs add -m msg'ed.
+The second list is removed files that shall be `cvs add'ed (resurrected)."
+ (let (add resurrect)
+ (while candidates
+ (let ((type (cvs-add-file-update-buffer cvs-buf (car candidates))))
+ (cond ((eq type 'ADD)
+ (setq add (cons (car candidates) add)))
+ ((eq type 'RESURRECT)
+ (setq resurrect (cons (car candidates) resurrect)))))
+ (setq candidates (cdr candidates)))
+ (cons add resurrect)))
+
+(defun cvs-add ()
+ "Add marked files to the cvs repository."
+ (interactive)
+
+ (let* ((buf (current-buffer))
+ (result (cvs-add-sub buf (cvs-get-marked)))
+ (added (car result))
+ (resurrect (cdr result))
+ (msg (if added (read-from-minibuffer "Enter description: "))))
+
+ (if (or resurrect added)
+ (cvs-use-temp-buffer))
+
+ (cond (resurrect
+ (message "Resurrecting files from repository...")
+ (cvs-execute-list resurrect cvs-program '("add"))
+ (message "Done.")))
+
+ (cond (added
+ (message "Adding new files to repository...")
+ (cvs-execute-list added cvs-program (list "add" "-m" msg))
+ (message "Done.")))))
+
+(defun cvs-ignore ()
+ "Arrange so that CVS ignores the selected files.
+This command ignores files that are not flagged as `Unknown'."
+ (interactive)
+
+ (mapcar (function (lambda (tin)
+ (cond
+ ((eq (cvs-fileinfo->type
+ (cookie-cookie (current-buffer) tin)) 'UNKNOWN)
+ (cvs-append-to-ignore
+ (cookie-cookie (current-buffer) tin))
+ (cookie-delete (current-buffer) tin)))))
+ (cvs-get-marked))
+ (setq cookie-last-tin nil))
+
+(defun cvs-append-to-ignore (fileinfo)
+ "Append the file in fileinfo to the .cvsignore file"
+ (save-window-excursion
+ (set-buffer (find-file-noselect (concat (file-name-as-directory
+ (cvs-fileinfo->dir fileinfo))
+ ".cvsignore")))
+ (goto-char (point-max))
+ (if (not (zerop (current-column)))
+ (insert "\n"))
+ (insert (cvs-fileinfo->file-name fileinfo) "\n")
+ (save-buffer)))
+
+(defun cvs-status ()
+ "Show cvs status for all marked files."
+ (interactive)
+
+ (save-some-buffers)
+ (let ((marked (cvs-get-marked)))
+ (cvs-use-temp-buffer)
+ (message "Running cvs status ...")
+ (cvs-execute-list marked cvs-program (cons "status" cvs-status-flags)))
+ (message "Running cvs status ... Done."))
+
+(defun cvs-log ()
+ "Display the cvs log of all selected files."
+ (interactive)
+
+ (let ((marked (cvs-get-marked)))
+ (cvs-use-temp-buffer)
+ (message "Running cvs log ...")
+ (cvs-execute-list marked cvs-program (cons "log" cvs-log-flags)))
+ (message "Running cvs log ... Done."))
+
+
+(defun cvs-insert-full-path (tin)
+ "Insert full path to the file described in TIN."
+ (insert (format "%s\n" (cvs-full-path cvs-buffer-name tin))))
+
+
+(defun cvs-add-change-log-entry-other-window (pos)
+ "Add a ChangeLog entry in the ChangeLog of the current directory.
+Args: POS."
+ (interactive "d")
+ (let* ((cvs-buf (current-buffer))
+ (odir default-directory))
+ (setq default-directory
+ (file-name-as-directory
+ (cvs-fileinfo->dir
+ (cookie-cookie
+ cvs-buf
+ (cookie-get-selection cvs-buf pos cookie-last-tin)))))
+ (if (not default-directory) ;In case there was no entries.
+ (setq default-directory odir))
+ (add-change-log-entry-other-window)
+ (set-buffer cvs-buf)
+ (setq default-directory odir)))
+
+
+(defun print-cvs-tin (foo)
+ "Debug utility."
+ (let ((cookie (cookie-cookie (current-buffer) foo))
+ (stream (get-buffer-create "debug")))
+ (princ "==============\n" stream)
+ (princ (cvs-fileinfo->file-name cookie) stream)
+ (princ "\n" stream)
+ (princ (cvs-fileinfo->dir cookie) stream)
+ (princ "\n" stream)
+ (princ (cvs-fileinfo->full-log cookie) stream)
+ (princ "\n" stream)
+ (princ (cvs-fileinfo->marked cookie) stream)
+ (princ "\n" stream)))
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.info b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.info
new file mode 100644
index 0000000..3c0d3c0
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.info
@@ -0,0 +1,1367 @@
+Info file pcl-cvs, produced by Makeinfo, -*- Text -*- from input
+file pcl-cvs.texinfo.
+
+ Copyright (C) 1992 Per Cederqvist
+
+ Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of
+this manual under the conditions for verbatim copying, provided also
+that the section entitled "GNU General Public License" is included
+exactly as in the original, and provided that the entire resulting
+derived work is distributed under the terms of a permission notice
+identical to this one.
+
+ Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for
+modified versions, except that the section entitled "GNU General
+Public License" and this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+
+
+File: pcl-cvs, Node: Top, Next: Copying, Prev: (dir), Up: (dir)
+
+ This info manual describes pcl-cvs which is a GNU Emacs front-end
+to CVS. It works with CVS version 1.3. This manual is updated to
+release 1.02 of pcl-cvs.
+
+* Menu:
+
+* Copying:: GNU General Public License
+* Installation:: How to install pcl-cvs on your system.
+* About pcl-cvs:: Authors and ftp sites.
+
+* Getting started:: An introduction with a walk-through example.
+* Buffer contents:: An explanation of the buffer contents.
+* Commands:: All commands, grouped by type.
+
+* Customization:: How you can tailor pcl-cvs to suit your needs.
+* Future enhancements:: Future enhancements of pcl-cvs.
+* Reporting bugs and ideas:: Where to report bugs.
+
+* Function and Variable Index:: List of functions and variables.
+* Concept Index:: List of concepts.
+* Key Index:: List of keystrokes.
+
+ -- The Detailed Node Listing --
+
+Installation
+
+* Pcl-cvs installation:: How to install pcl-cvs on your system.
+* On-line manual installation:: How to install the on-line manual.
+* Typeset manual installation:: How to create typeset documentation
+ about pcl-cvs.
+
+About pcl-cvs
+
+* Contributors:: Contributors to pcl-cvs.
+* Archives:: Where can I get a copy of Pcl-Cvs?
+
+Buffer contents
+
+* File status:: The meaning of the second field.
+* Selected files:: How selection works.
+
+Commands
+
+* Updating the directory:: Commands to update the local directory
+* Movement commands:: How to move up and down in the buffer
+* Marking files:: How to mark files that other commands
+ will later operate on.
+* Committing changes:: Checking in your modifications to the
+ CVS repository.
+* Editing files:: Loading files into Emacs.
+* Getting info about files:: Display the log and status of files.
+* Adding and removing files:: Adding and removing files
+* Removing handled entries:: Uninteresting lines can easily be removed.
+* Ignoring files:: Telling CVS to ignore generated files.
+* Viewing differences:: Commands to `diff' different versions.
+
+
+File: pcl-cvs, Node: Copying, Next: Installation, Prev: Top, Up: Top
+
+GNU GENERAL PUBLIC LICENSE
+**************************
+
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+Preamble
+========
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+This General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit
+to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can
+apply it to your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and
+charge for this service if you wish), that you receive source code
+or can get it if you want it, that you can change the software or
+use pieces of it in new free programs; and that you know you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the
+rights. These restrictions translate to certain responsibilities
+for you if you distribute copies of the software, or if you modify
+it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the
+software, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make
+certain that everyone understands that there is no warranty for this
+free software. If the software is modified by someone else and
+passed on, we want its recipients to know that what they have is not
+the original, so that any problems introduced by others will not
+reflect on the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making
+the program proprietary. To prevent this, we have made it clear
+that any patent must be licensed for everyone's free use or not
+licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 1. This License applies to any program or other work which contains
+ a notice placed by the copyright holder saying it may be
+ distributed under the terms of this General Public License.
+ The "Program", below, refers to any such program or work, and a
+ "work based on the Program" means either the Program or any
+ derivative work under copyright law: that is to say, a work
+ containing the Program or a portion of it, either verbatim or
+ with modifications and/or translated into another language.
+ (Hereinafter, translation is included without limitation in the
+ term "modification".) Each licensee is addressed as "you".
+
+ Activities other than copying, distribution and modification
+ are not covered by this License; they are outside its scope.
+ The act of running the Program is not restricted, and the
+ output from the Program is covered only if its contents
+ constitute a work based on the Program (independent of having
+ been made by running the Program). Whether that is true
+ depends on what the Program does.
+
+ 2. You may copy and distribute verbatim copies of the Program's
+ source code as you receive it, in any medium, provided that you
+ conspicuously and appropriately publish on each copy an
+ appropriate copyright notice and disclaimer of warranty; keep
+ intact all the notices that refer to this License and to the
+ absence of any warranty; and give any other recipients of the
+ Program a copy of this License along with the Program.
+
+ You may charge a fee for the physical act of transferring a
+ copy, and you may at your option offer warranty protection in
+ exchange for a fee.
+
+ 3. You may modify your copy or copies of the Program or any portion
+ of it, thus forming a work based on the Program, and copy and
+ distribute such modifications or work under the terms of
+ Section 1 above, provided that you also meet all of these
+ conditions:
+
+ 1. You must cause the modified files to carry prominent
+ notices stating that you changed the files and the date of
+ any change.
+
+ 2. You must cause any work that you distribute or publish,
+ that in whole or in part contains or is derived from the
+ Program or any part thereof, to be licensed as a whole at
+ no charge to all third parties under the terms of this
+ License.
+
+ 3. If the modified program normally reads commands
+ interactively when run, you must cause it, when started
+ running for such interactive use in the most ordinary way,
+ to print or display an announcement including an
+ appropriate copyright notice and a notice that there is no
+ warranty (or else, saying that you provide a warranty) and
+ that users may redistribute the program under these
+ conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive
+ but does not normally print such an announcement, your
+ work based on the Program is not required to print an
+ announcement.)
+
+ These requirements apply to the modified work as a whole. If
+ identifiable sections of that work are not derived from the
+ Program, and can be reasonably considered independent and
+ separate works in themselves, then this License, and its terms,
+ do not apply to those sections when you distribute them as
+ separate works. But when you distribute the same sections as
+ part of a whole which is a work based on the Program, the
+ distribution of the whole must be on the terms of this License,
+ whose permissions for other licensees extend to the entire
+ whole, and thus to each and every part regardless of who wrote
+ it.
+
+ Thus, it is not the intent of this section to claim rights
+ or contest your rights to work written entirely by you; rather,
+ the intent is to exercise the right to control the distribution
+ of derivative or collective works based on the Program.
+
+ In addition, mere aggregation of another work not based on
+ the Program with the Program (or with a work based on the
+ Program) on a volume of a storage or distribution medium does
+ not bring the other work under the scope of this License.
+
+ 4. You may copy and distribute the Program (or a work based on it,
+ under Section 2) in object code or executable form under the
+ terms of Sections 1 and 2 above provided that you also do one
+ of the following:
+
+ 1. Accompany it with the complete corresponding
+ machine-readable source code, which must be distributed
+ under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ 2. Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than
+ your cost of physically performing source distribution, a
+ complete machine-readable copy of the corresponding source
+ code, to be distributed under the terms of Sections 1 and
+ 2 above on a medium customarily used for software
+ interchange; or,
+
+ 3. Accompany it with the information you received as to the
+ offer to distribute corresponding source code. (This
+ alternative is allowed only for noncommercial distribution
+ and only if you received the program in object code or
+ executable form with such an offer, in accord with
+ Subsection b above.)
+
+ The source code for a work means the preferred form of the
+ work for making modifications to it. For an executable work,
+ complete source code means all the source code for all modules
+ it contains, plus any associated interface definition files,
+ plus the scripts used to control compilation and installation
+ of the executable. However, as a special exception, the source
+ code distributed need not include anything that is normally
+ distributed (in either source or binary form) with the major
+ components (compiler, kernel, and so on) of the operating
+ system on which the executable runs, unless that component
+ itself accompanies the executable.
+
+ If distribution of executable or object code is made by
+ offering access to copy from a designated place, then offering
+ equivalent access to copy the source code from the same place
+ counts as distribution of the source code, even though third
+ parties are not compelled to copy the source along with the
+ object code.
+
+ 5. You may not copy, modify, sublicense, or distribute the Program
+ except as expressly provided under this License. Any attempt
+ otherwise to copy, modify, sublicense or distribute the Program
+ is void, and will automatically terminate your rights under
+ this License. However, parties who have received copies, or
+ rights, from you under this License will not have their
+ licenses terminated so long as such parties remain in full
+ compliance.
+
+ 6. You are not required to accept this License, since you have not
+ signed it. However, nothing else grants you permission to
+ modify or distribute the Program or its derivative works.
+ These actions are prohibited by law if you do not accept this
+ License. Therefore, by modifying or distributing the Program
+ (or any work based on the Program), you indicate your
+ acceptance of this License to do so, and all its terms and
+ conditions for copying, distributing or modifying the Program
+ or works based on it.
+
+ 7. Each time you redistribute the Program (or any work based on the
+ Program), the recipient automatically receives a license from
+ the original licensor to copy, distribute or modify the Program
+ subject to these terms and conditions. You may not impose any
+ further restrictions on the recipients' exercise of the rights
+ granted herein. You are not responsible for enforcing
+ compliance by third parties to this License.
+
+ 8. If, as a consequence of a court judgment or allegation of patent
+ infringement or for any other reason (not limited to patent
+ issues), conditions are imposed on you (whether by court order,
+ agreement or otherwise) that contradict the conditions of this
+ License, they do not excuse you from the conditions of this
+ License. If you cannot distribute so as to satisfy
+ simultaneously your obligations under this License and any
+ other pertinent obligations, then as a consequence you may not
+ distribute the Program at all. For example, if a patent
+ license would not permit royalty-free redistribution of the
+ Program by all those who receive copies directly or indirectly
+ through you, then the only way you could satisfy both it and
+ this License would be to refrain entirely from distribution of
+ the Program.
+
+ If any portion of this section is held invalid or
+ unenforceable under any particular circumstance, the balance of
+ the section is intended to apply and the section as a whole is
+ intended to apply in other circumstances.
+
+ It is not the purpose of this section to induce you to
+ infringe any patents or other property right claims or to
+ contest validity of any such claims; this section has the sole
+ purpose of protecting the integrity of the free software
+ distribution system, which is implemented by public license
+ practices. Many people have made generous contributions to the
+ wide range of software distributed through that system in
+ reliance on consistent application of that system; it is up to
+ the author/donor to decide if he or she is willing to
+ distribute software through any other system and a licensee
+ cannot impose that choice.
+
+ This section is intended to make thoroughly clear what is
+ believed to be a consequence of the rest of this License.
+
+ 9. If the distribution and/or use of the Program is restricted in
+ certain countries either by patents or by copyrighted
+ interfaces, the original copyright holder who places the
+ Program under this License may add an explicit geographical
+ distribution limitation excluding those countries, so that
+ distribution is permitted only in or among countries not thus
+ excluded. In such case, this License incorporates the
+ limitation as if written in the body of this License.
+
+ 10. The Free Software Foundation may publish revised and/or new
+ versions of the General Public License from time to time. Such
+ new versions will be similar in spirit to the present version,
+ but may differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If
+ the Program specifies a version number of this License which
+ applies to it and "any later version", you have the option of
+ following the terms and conditions either of that version or of
+ any later version published by the Free Software Foundation.
+ If the Program does not specify a version number of this
+ License, you may choose any version ever published by the Free
+ Software Foundation.
+
+ 11. If you wish to incorporate parts of the Program into other free
+ programs whose distribution conditions are different, write to
+ the author to ask for permission. For software which is
+ copyrighted by the Free Software Foundation, write to the Free
+ Software Foundation; we sometimes make exceptions for this.
+ Our decision will be guided by the two goals of preserving the
+ free status of all derivatives of our free software and of
+ promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 12. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
+ WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
+ LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
+ WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS
+ WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE
+ COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 13. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+ WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY
+ MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE
+ LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
+ INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
+ INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS
+ OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE
+ WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY
+ HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+Appendix: How to Apply These Terms to Your New Programs
+=======================================================
+
+ If you develop a new program, and you want it to be of the
+greatest possible use to the public, the best way to achieve this is
+to make it free software which everyone can redistribute and change
+under these terms.
+
+ To do so, attach the following notices to the program. It is
+safest to attach them to the start of each source file to most
+effectively convey the exclusion of warranty; and each file should
+have at least the "copyright" line and a pointer to where the full
+notice is found.
+
+ ONE LINE TO GIVE THE PROGRAM'S NAME AND A BRIEF IDEA OF WHAT IT DOES.
+ Copyright (C) 19YY NAME OF AUTHOR
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Also add information on how to contact you by electronic and
+paper mail.
+
+ If the program is interactive, make it output a short notice like
+this when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19YY NAME OF AUTHOR
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+ The hypothetical commands `show w' and `show c' should show the
+appropriate parts of the General Public License. Of course, the
+commands you use may be called something other than `show w' and
+`show c'; they could even be mouse-clicks or menu items--whatever
+suits your program.
+
+ You should also get your employer (if you work as a programmer)
+or your school, if any, to sign a "copyright disclaimer" for the
+program, if necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ SIGNATURE OF TY COON, 1 April 1989
+ Ty Coon, President of Vice
+
+ This General Public License does not permit incorporating your
+program into proprietary programs. If your program is a subroutine
+library, you may consider it more useful to permit linking
+proprietary applications with the library. If this is what you want
+to do, use the GNU Library General Public License instead of this
+License.
+
+
+File: pcl-cvs, Node: Installation, Next: About pcl-cvs, Prev: Copying, Up: Top
+
+Installation
+************
+
+ This section describes the installation of pcl-cvs, the GNU Emacs
+CVS front-end. You should install not only the elisp files
+themselves, but also the on-line documentation so that your users
+will know how to use it. You can create typeset documentation from
+the file `pcl-cvs.texinfo' as well as an on-line info file. The
+following steps are also described in the file `INSTALL' in the
+source directory.
+
+* Menu:
+
+* Pcl-cvs installation:: How to install pcl-cvs on your system.
+* On-line manual installation:: How to install the on-line manual.
+* Typeset manual installation:: How to create typeset documentation
+ about pcl-cvs.
+
+
+File: pcl-cvs, Node: Pcl-cvs installation, Next: On-line manual installation, Prev: Installation, Up: Installation
+
+Installation of the pcl-cvs program
+===================================
+
+ 1. Edit the file `Makefile' to reflect the situation at your site.
+ The only things you have to change is the definition of
+ `lispdir' and `infodir'. The elisp files will be copied to
+ `lispdir', and the info file to `infodir'.
+
+ 2. Configure pcl-cvs.el
+
+ There are a couple of paths that you have to check to make
+ sure that they match you system. They appear early in the file
+ pcl-cvs.el.
+
+ *NOTE:* If your system is running emacs 18.57 or earlier
+ you MUST uncomment the line that says:
+
+ (setq delete-exited-processes nil)
+
+ Setting `delete-exited-processes' to `nil' works around a bug
+ in emacs that causes it to dump core. The bug was fixed in
+ emacs 18.58.
+
+ 3. Type `make install' in the source directory. This will
+ byte-compile all `.el' files and copy both the `.el' and the
+ `.elc' into the directory you specified in step 1.
+
+ If you don't want to install the `.el' files but only the
+ `.elc' files (the byte-compiled files), you can type ``make
+ install_elc'' instead of ``make install''.
+
+ If you only want to create the compiled elisp files, but
+ don't want to install them, you can type `make elcfiles'
+ instead. This is what happens if you only type `make' without
+ parameters.
+
+ 4. Edit the file `default.el' in your emacs lisp directory (usually
+ `/usr/gnu/emacs/lisp' or something similar) and enter the
+ contents of the file `pcl-cvs-startup.el' into it. It contains
+ a couple of `auto-load's that facilitates the use of pcl-cvs.
+
+
+File: pcl-cvs, Node: On-line manual installation, Next: Typeset manual installation, Prev: Pcl-cvs installation, Up: Installation
+
+Installation of the on-line manual.
+===================================
+
+ 1. Create the info file `pcl-cvs' from `pcl-cvs.texinfo' by typing
+ `make info'. If you don't have the program `makeinfo' you can
+ get it by anonymous ftp from e.g. `ftp.gnu.ai.mit.edu' as
+ `pub/gnu/texinfo-2.14.tar.Z' (there might be a newer version
+ there when you read this), or you could use the preformatted
+ info file `pcl-cvs.info' that is included in the distribution
+ (type `cp pcl-cvs.info pcl-cvs').
+
+ 2. Move the info file `pcl-cvs' to your standard info directory.
+ This might be called something like `/usr/gnu/emacs/info'.
+
+ 3. Edit the file `dir' in the info directory and enter one line to
+ contain a pointer to the info file `pcl-cvs'. The line can, for
+ instance, look like this:
+
+ * Pcl-cvs: (pcl-cvs). An Emacs front-end to CVS.
+
+
+File: pcl-cvs, Node: Typeset manual installation, Prev: On-line manual installation, Up: Installation
+
+How to make typeset documentation from pcl-cvs.texinfo
+======================================================
+
+ If you have TeX installed at your site, you can make a typeset
+manual from `pcl-cvs.texinfo'.
+
+ 1. Run TeX by typing ``make pcl-cvs.dvi''. You will not get the
+ indices unless you have the `texindex' program.
+
+ 2. Convert the resulting device independent file `pcl-cvs.dvi' to a
+ form which your printer can output and print it. If you have a
+ postscript printer there is a program, `dvi2ps', which does.
+ There is also a program which comes together with TeX, `dvips',
+ which you can use.
+
+
+File: pcl-cvs, Node: About pcl-cvs, Next: Getting started, Prev: Installation, Up: Top
+
+About pcl-cvs
+*************
+
+ Pcl-cvs is a front-end to CVS version 1.3. It integrates the most
+frequently used CVS commands into emacs.
+
+* Menu:
+
+* Contributors:: Contributors to pcl-cvs.
+* Archives:: Where can I get a copy of Pcl-Cvs?
+
+
+File: pcl-cvs, Node: Contributors, Next: Archives, Prev: About pcl-cvs, Up: About pcl-cvs
+
+Contributors to pcl-cvs
+=======================
+
+ Contributions to the package are welcome. I have limited time to
+work on this project, but I will gladly add any code that you
+contribute to me to this package (*note Reporting bugs and ideas::.).
+
+ The following persons have made contributions to pcl-cvs.
+
+ * Brian Berliner wrote CVS, together with some other contributors.
+ Without his work on CVS this package would be useless...
+
+ * Per Cederqvist wrote most of the otherwise unattributed
+ functions in pcl-cvs as well as all documentation.
+
+ * Inge Wallin (`inge@lysator.liu.se') wrote the skeleton to
+ `pcl-cvs.texinfo', and gave useful comments on it. He also
+ wrote the files `elib-node.el' and `compile-all.el'. The file
+ `cookie.el' was inspired by Inge.
+
+ * Linus Tolke (`linus@lysator.liu.se') contributed useful comments
+ on both the functionality and the documentation.
+
+
+File: pcl-cvs, Node: Archives, Prev: Contributors, Up: About pcl-cvs
+
+Where can I get pcl-cvs?
+========================
+
+ This release of pcl-cvs is included in the CVS 1.3 distribution.
+However, since pcl-cvs has had less time to mature (the first line of
+code was written less than a year ago) it is likely that there will
+be a new release of pcl-cvs before the next release of CVS.
+
+ The latest release of pcl-cvs can be fetched via anonymous ftp
+from `ftp.lysator.liu.se', (IP no. 130.236.254.1) in the directory
+`pub/emacs'. If you don't live in Scandinavia you should probably
+check with archie to see if there is a site closer to you that
+archives pcl-cvs.
+
+ New releases will be announced to appropriate newsgroups. If you
+send your email address to me I will add you to my list of people to
+mail when I make a new release.
+
+
+File: pcl-cvs, Node: Getting started, Next: Buffer contents, Prev: About pcl-cvs, Up: Top
+
+Getting started
+***************
+
+ This document assumes that you know what CVS is, and that you at
+least knows the fundamental concepts of CVS. If that is not the
+case you should read the man page for CVS.
+
+ Pcl-cvs is only useful once you have checked out a module. So
+before you invoke it you must have a copy of a module somewhere in
+the file system.
+
+ You invoke pcl-cvs by typing `M-x pcl-cvs RET'. If your emacs
+responds with `[No match]' your system administrator has not
+installed pcl-cvs properly. Try `M-x load-library RET pcl-cvs RET'.
+If that also fails - talk to your root. If it succeeds you might put
+this line in your `.emacs' file so that you don't have to type the
+`load-library' command every time you wish to use pcl-cvs:
+
+ (autoload 'cvs-update "pcl-cvs" nil t)
+
+ The function `cvs-update' will ask for a directory. The command
+`cvs update' will be run in that directory. (It should contain
+files that have been checked out from a CVS archive.) The output
+from `cvs' will be parsed and presented in a table in a buffer called
+`*cvs*'. It might look something like this:
+
+ PCL-CVS release 1.02.
+
+ In directory /users/ceder/FOO/test:
+ Updated bar
+ Updated file.txt
+ Modified ci namechange
+ Updated newer
+
+ In directory /users/ceder/FOO/test/sub:
+ Modified ci ChangeLog
+ ---------- End -----
+
+ In this example the three files (`bar', `file.txt' and `newer')
+that are marked with `Updated' have been copied from the CVS
+repository to `/users/ceder/FOO/test/' since someone else have
+checked in newer versions of them. Two files (`namechange' and
+`sub/ChangeLog') have been modified locally, and needs to be checked
+in.
+
+ You can move the cursor up and down in the buffer with `C-n' and
+`C-p' or `n' and `p'. If you press `c' on one of the `Modified'
+files that file will be checked in to the CVS repository. *Note
+Committing changes::. You can press `x' to get rid of the
+"uninteresting" files that have only been `Updated' (and don't
+require any further action from you).
+
+ You can also easily get a `diff' between your modified file and
+the base version that you started from, and you can get the output
+from `cvs log' and `cvs status' on the listed files simply by
+pressing a key (*note Getting info about files::.).
+
+
+File: pcl-cvs, Node: Buffer contents, Next: Commands, Prev: Getting started, Up: Top
+
+Buffer contents
+***************
+
+ The display contains four columns. They contain, from left to
+right:
+
+ * An asterisk when the file is "marked" (*note Selected files::.).
+
+ * The status of the file. See *Note File status::, for more
+ information.
+
+ * A "need to be checked in"-marker (`ci').
+
+ * The file name.
+
+* Menu:
+
+* File status:: The meaning of the second field.
+* Selected files:: How selection works.
+
+
+File: pcl-cvs, Node: File status, Next: Selected files, Prev: Buffer contents, Up: Buffer contents
+
+File status
+===========
+
+ The `file status' field can have the following values:
+
+`Updated'
+ The file was brought up to date with respect to the repository.
+ This is done for any file that exists in the repository but
+ not in your source, and for files that you haven't changed but
+ are not the most recent versions available in the repository.
+
+`Modified'
+ The file is modified in your working directory, and there
+ was no modification to the same file in the repository.
+
+`Merged'
+ The file is modified in your working directory, and there were
+ modifications in the repository as well as in your copy, but
+ they were merged successfully, without conflict, in your
+ working directory.
+
+`Conflict'
+ A conflict was detected while trying to merge your changes to
+ FILE with changes from the source repository. FILE (the copy
+ in your working directory) is now the output of the `rcsmerge'
+ command on the two versions; an unmodified copy of your file is
+ also in your working directory, with the name `.#FILE.VERSION',
+ where VERSION is the RCS revision that your modified file
+ started from. *Note Viewing differences::, for more details.
+
+`Added'
+ The file has been added by you, but it still needs to be
+ checked in to the repository.
+
+`Removed'
+ The file has been removed by you, but it needs to be checked in
+ to the repository. You can resurrect it by typing `a' (*note
+ Adding and removing files::.).
+
+`Unknown'
+ A file that was detected in your directory, but that neither
+ appears in the repository, nor is present on the list of files
+ that CVS should ignore.
+
+ There are also a few special cases, that rarely occur, which have
+longer strings in the fields:
+
+`Removed from repository'
+ The file has been removed from your directory since someone has
+ removed it from the repository. (It is still present in the
+ Attic directory, so no permanent loss has occurred). This,
+ unlike the other entries in this table, is not an error
+ condition.
+
+`Removed from repository, changed by you'
+ You have modified a file that someone have removed from the
+ repository. You can correct this situation by removing the
+ file manually (see *note Adding and removing files::.).
+
+`Removed by you, changed in repository'
+ You have removed a file, and before you committed the removal
+ someone committed a change to that file. You could use `a' to
+ resurrect the file (see *note Adding and removing files::.).
+
+`Move away FILE - it is in the way'
+ For some reason CVS does not like the file FILE. Rename or
+ remove it.
+
+`This repository is missing! Remove this dir manually.'
+ It is impossible to remove a directory in the CVS repository in
+ a clean way. Someone have tried to remove one, and CVS gets
+ confused. Remove your copy of the directory.
+
+
+File: pcl-cvs, Node: Selected files, Prev: File status, Up: Buffer contents
+
+Selected files
+==============
+
+ Many of the commands works on the current set of "selected" files.
+
+ * If there are any files that are marked they constitute the set
+ of selected files.
+
+ * Otherwise, if the cursor points to a file, that file is the
+ selected file.
+
+ * Otherwise, if the cursor points to a directory, all the files
+ in that directory that appears in the buffer are the selected
+ files.
+
+ This scheme might seem a little complicated, but once one get
+used to it, it is quite powerful.
+
+ *Note Marking files:: tells how you mark and unmark files.
+
+
+File: pcl-cvs, Node: Commands, Next: Customization, Prev: Buffer contents, Up: Top
+
+Commands
+********
+
+ The nodes in this menu contains explanations about all the
+commands that you can use in pcl-cvs. They are grouped together by
+type.
+
+* Menu:
+
+* Updating the directory:: Commands to update the local directory
+* Movement commands:: How to move up and down in the buffer
+* Marking files:: How to mark files that other commands
+ will later operate on.
+* Committing changes:: Checking in your modifications to the
+ CVS repository.
+* Editing files:: Loading files into Emacs.
+* Getting info about files:: Display the log and status of files.
+* Adding and removing files:: Adding and removing files
+* Removing handled entries:: Uninteresting lines can easily be removed.
+* Ignoring files:: Telling CVS to ignore generated files.
+* Viewing differences:: Commands to `diff' different versions.
+
+
+File: pcl-cvs, Node: Updating the directory, Next: Movement commands, Prev: Commands, Up: Commands
+
+Updating the directory
+======================
+
+`M-x cvs-update'
+ Run a `cvs update' command. You will be asked for the
+ directory in which the `cvs update' will be run. The output
+ will be parsed by pcl-cvs, and the result printed in the
+ `*cvs*' buffer (see *note Buffer contents::. for a description
+ of the contents).
+
+ By default, `cvs-update' will descend recursively into
+ subdirectories. You can avoid that behavior by giving a prefix
+ argument to it (e.g., by typing `C-u M-x cvs-update RET').
+
+ All other commands in pcl-cvs requires that you have a `*cvs*'
+ buffer. This is the command that you use to get one.
+
+`g'
+ This will run `cvs update' again. It will always use the same
+ buffer that was used with the previous `cvs update'. Give a
+ prefix argument to avoid descending into subdirectories. This
+ runs the command `cvs-update-no-prompt'.
+
+
+File: pcl-cvs, Node: Movement commands, Next: Marking files, Prev: Updating the directory, Up: Commands
+
+Movement Commands
+=================
+
+ You can use most normal Emacs commands to move forward and
+backward in the buffer. Some keys are rebound to functions that
+take advantage of the fact that the buffer is a pcl-cvs buffer:
+
+`SPC'
+`C-n'
+`n'
+ These keys move the cursor one file forward, towards the end of
+ the buffer (`cookie-next-cookie').
+
+`C-p'
+`p'
+ These keys move one file backward, towards the beginning of the
+ buffer (`cookie-previous-cookie').
+
+
+File: pcl-cvs, Node: Marking files, Next: Committing changes, Prev: Movement commands, Up: Commands
+
+Marking files
+=============
+
+ Pcl-cvs works on a set of "selected files" (*note Selected
+files::.). You can mark and unmark files with these commands:
+
+`m'
+ This marks the file that the cursor is positioned on. If the
+ cursor is positioned on a directory all files in that directory
+ will be marked. (`cvs-mark').
+
+`u'
+ Unmark the file that the cursor is positioned on. If the cursor
+ is on a directory, all files in that directory will be unmarked.
+ (`cvs-unmark').
+
+`M'
+ Mark *all* files in the buffer (`cvs-mark-all-files').
+
+`U'
+ Unmark *all* files (`cvs-unmark-all-files').
+
+`DEL'
+ Unmark the file on the previous line, and move point to that
+ line (`cvs-unmark-up').
+
+
+File: pcl-cvs, Node: Committing changes, Next: Editing files, Prev: Marking files, Up: Commands
+
+Committing changes
+==================
+
+`c'
+ All files that have a "need to be checked in"-marker (*note
+ Buffer contents::.) can be checked in with the `c' command. It
+ checks in all selected files (*note Selected files::.) (except
+ those who lack the "ci"-marker - they are ignored). Pressing
+ `c' causes `cvs-commit' to be run.
+
+ When you press `c' you will get a buffer called
+ `*cvs-commit-message*'. Enter the log message for the file(s)
+ in it. When you are ready you should press `C-c C-c' to
+ actually commit the files (using `cvs-edit-done').
+
+ Normally the `*cvs-commit-message*' buffer will retain the log
+ message from the previous commit, but if the variable
+ `cvs-erase-input-buffer' is set to a non-nil value the buffer
+ will be erased. Point and mark will always be located around
+ the entire buffer so that you can easily erase it with `C-w'
+ (`kill-region').
+
+
+File: pcl-cvs, Node: Editing files, Next: Getting info about files, Prev: Committing changes, Up: Commands
+
+Editing files
+=============
+
+ There are currently three commands that can be used to find a
+file (that is, load it into a buffer and start editing it there).
+These commands work on the line that the cursor is situated at.
+They ignore any marked files.
+
+`f'
+ Find the file that the cursor points to. Run `dired'
+
+ (*note Dired: (Emacs)Dired.)
+
+ if the cursor points to a directory (`cvs-find-file').
+
+`o'
+ Like `f', but use another window (`cvs-find-file-other-window').
+
+`A'
+ Invoke `add-change-log-entry-other-window' to edit a
+ `ChangeLog' file. The `ChangeLog' will be found in the
+ directory of the file the cursor points to.
+ (`cvs-add-change-log-entry-other-window').
+
+
+File: pcl-cvs, Node: Getting info about files, Next: Adding and removing files, Prev: Editing files, Up: Commands
+
+Getting info about files
+========================
+
+ Both of the following commands can be customized. *Note
+Customization::.
+
+`l'
+ Run `cvs log' on all selected files, and show the result in a
+ temporary buffer (`cvs-log').
+
+`s'
+ Run `cvs status' on all selected files, and show the result in a
+ temporary buffer (`cvs-status').
+
+
+File: pcl-cvs, Node: Adding and removing files, Next: Removing handled entries, Prev: Getting info about files, Up: Commands
+
+Adding and removing files
+=========================
+
+ The following commands are available to make it easy to add and
+remove files from the CVS repository.
+
+`a'
+ Add all selected files. This command can be used on `Unknown'
+ files (see *note File status::.). The status of the file will
+ change to `Added', and you will have to use `c' (`cvs-commit',
+ see *note Committing changes::.) to really add the file to the
+ repository.
+
+ This command can also be used on `Removed' files (before you
+ commit them) to resurrect them.
+
+ Selected files that are neither `Unknown' nor `Removed' will be
+ ignored by this command.
+
+ The command that is run is `cvs-add'.
+
+`r'
+ This command removes the selected files (after prompting for
+ confirmation). The files are `rm'ed from your directory and
+ (unless the status was `Unknown'; *note File status::.) they
+ will also be `cvs remove'd. If the files were `Unknown' they
+ will disappear from the buffer. Otherwise their status will
+ change to `Removed', and you must use `c' (`cvs-commit', *note
+ Committing changes::.) to commit the removal.
+
+ The command that is run is `cvs-remove-file'.
+
+
+File: pcl-cvs, Node: Removing handled entries, Next: Ignoring files, Prev: Adding and removing files, Up: Commands
+
+Removing handled entries
+========================
+
+`x'
+ This command allows you to remove all entries that you have
+ processed. More specifically, the lines for `Updated' files
+ (*note File status::. and files that have been checked in
+ (*note Committing changes::.) are removed from the buffer. If
+ a directory becomes empty the heading for that directory is
+ also removed. This makes it easier to get an overview of what
+ needs to be done.
+
+ The command is called `cvs-remove-handled'. If
+ `cvs-auto-remove-handled' is set to non-`nil' this will
+ automatically be performed after every commit.
+
+`C-k'
+ This command can be used for lines that `cvs-remove-handled'
+ would not delete, but that you want to delete
+ (`cvs-acknowledge').
+
+
+File: pcl-cvs, Node: Ignoring files, Next: Viewing differences, Prev: Removing handled entries, Up: Commands
+
+Ignoring files
+==============
+
+`i'
+ Arrange so that CVS will ignore the selected files. The file
+ names are added to the `.cvsignore' file in the corresponding
+ directory. If the `.cvsignore' doesn't exist it will be
+ created.
+
+ The `.cvsignore' file should normally be added to the
+ repository, but you could ignore it also if you like it better
+ that way.
+
+ This runs `cvs-ignore'.
+
+
+File: pcl-cvs, Node: Viewing differences, Prev: Ignoring files, Up: Commands
+
+Viewing differences
+===================
+
+`d'
+ Display a `cvs diff' between the selected files and the RCS
+ version that they are based on. *Note Customization::
+ describes how you can send flags to `cvs diff'. (The function
+ that does the job is `cvs-diff-cvs').
+
+`b'
+ If CVS finds a conflict while merging two versions of a file
+ (during a `cvs update', *note Updating the directory::.) it
+ will save the original file in a file called `.#FILE.VERSION'
+ where FILE is the name of the file, and VERSION is the RCS
+ version number that your file was based on.
+
+ With the `b' command you can run a `diff' on the files
+ `.#FILE.VERSION' and `FILE'. You can get a context- or Unidiff
+ by setting `cvs-diff-flags' - *note Customization::.. This
+ command only works on files that have status `Conflict' or
+ `Merged'. The name of the command is `cvs-diff-backup'.
+
+
+File: pcl-cvs, Node: Customization, Next: Future enhancements, Prev: Commands, Up: Top
+
+Customization
+*************
+
+ If you have an idea about any customization that would be handy
+but isn't present in this list, please tell me! *Note Reporting
+bugs and ideas:: for info on how to reach me.
+
+`cvs-erase-input-buffer'
+ If set to anything else than `nil' the edit buffer will be
+ erased before you write the log message (*note Committing
+ changes::.).
+
+`cvs-inhibit-copyright-message'
+ The copyright message that is displayed on startup can be
+ annoying after a while. Set this variable to `t' if you want
+ to get rid of it. (But don't set this to `t' in the system
+ defaults file - new users should see this message at least
+ once).
+
+`cvs-cvs-diff-flags'
+ A list of strings to pass as arguments to the `cvs diff'
+ program. This is used by `cvs-diff-cvs' (key `d', *note
+ Viewing differences::.). If you prefer the Unidiff format you
+ could add this line to your `.emacs' file:
+
+ (setq cvs-cvs-diff-flags '("-u"))
+
+`cvs-diff-flags'
+ Like `cvs-cvs-diff-flags', but passed to `diff'. This is used
+ by `cvs-diff-backup' (key `b', *note Viewing differences::.).
+
+`cvs-log-flags'
+ List of strings to send to `cvs log'. Used by `cvs-log' (key
+ `l', *note Getting info about files::.).
+
+`cvs-status-flags'
+ List of strings to send to `cvs status'. Used by `cvs-status'
+ (key `s', *note Getting info about files::.).
+
+`cvs-auto-remove-handled'
+ If this variable is set to any non-`nil' value
+ `cvs-remove-handled' will be called every time you check in
+ files, after the check-in is ready. *Note Removing handled
+ entries::.
+
+
+File: pcl-cvs, Node: Future enhancements, Next: Reporting bugs and ideas, Prev: Customization, Up: Top
+
+Future enhancements
+*******************
+
+ Pcl-cvs is still under development and needs a number of
+enhancements to be called complete. Here is my current wish-list
+for future releases of pcl-cvs:
+
+ * Dired support. I have an experimental `dired-cvs.el' that works
+ together with CVS 1.2. Unfortunately I wrote it on top of a
+ non-standard `dired.el', so it must be rewritten.
+
+ * It should be possible to run commands such as `cvs log', `cvs
+ status' and `cvs commit' directly from a buffer containing a
+ file, instead of having to `cvs-update'. If the directory
+ contains many files the `cvs-update' can take quite some time,
+ especially on a slow machine.
+
+ If you miss something in this wish-list, let me know! I don't
+promise that I will write it, but I will at least try to coordinate
+the efforts of making a good Emacs front end to CVS. See *Note
+Reporting bugs and ideas:: for information about how to reach me.
+
+
+File: pcl-cvs, Node: Reporting bugs and ideas, Next: Function and Variable Index, Prev: Future enhancements, Up: Top
+
+Reporting bugs and ideas
+************************
+
+ If you find a bug or misfeature, don't hesitate to tell me! Send
+email to `ceder@lysator.liu.se'.
+
+ If you have ideas for improvements, or if you have written some
+extensions to this package, I would like to hear from you. I hope
+that you find this package useful!
+
+
+File: pcl-cvs, Node: Function and Variable Index, Next: Concept Index, Prev: Reporting bugs and ideas, Up: Top
+
+Function and Variable Index
+***************************
+
+* Menu:
+
+* cookie-next-cookie: Movement commands.
+* cookie-previous-cookie: Movement commands.
+* cvs-acknowledge: Removing handled entries.
+* cvs-add: Adding and removing files.
+* cvs-add-change-log-entry-other-window: Editing files.
+* cvs-auto-remove-handled (variable): Customization.
+* cvs-commit: Committing changes.
+* cvs-cvs-diff-flags (variable): Customization.
+* cvs-diff-backup: Viewing differences.
+* cvs-diff-cvs: Viewing differences.
+* cvs-diff-flags (variable): Customization.
+* cvs-erase-input-buffer (variable): Committing changes.
+* cvs-erase-input-buffer (variable): Customization.
+* cvs-find-file: Editing files.
+* cvs-find-file-other-window: Editing files.
+* cvs-inhibit-copyright-message (variable): Customization.
+* cvs-log: Getting info about files.
+* cvs-log-flags (variable): Customization.
+* cvs-mark: Marking files.
+* cvs-mark-all-files: Marking files.
+* cvs-remove-file: Adding and removing files.
+* cvs-remove-handled: Removing handled entries.
+* cvs-status: Getting info about files.
+* cvs-status-flags (variable): Customization.
+* cvs-unmark: Marking files.
+* cvs-unmark-all-files: Marking files.
+* cvs-unmark-up: Marking files.
+* cvs-update: Updating the directory.
+* cvs-update-no-prompt: Updating the directory.
+
+
+File: pcl-cvs, Node: Concept Index, Next: Key Index, Prev: Function and Variable Index, Up: Top
+
+Concept Index
+*************
+
+* Menu:
+
+* About pcl-cvs: About pcl-cvs.
+* Active files: Selected files.
+* Added (file status): File status.
+* Adding files: Adding and removing files.
+* Archives: Archives.
+* Author, how to reach: Reporting bugs and ideas.
+* Authors: Contributors.
+* Automatically remove handled files: Customization.
+* Buffer contents: Buffer contents.
+* Bugs, how to report them: Reporting bugs and ideas.
+* Ci: Committing changes.
+* Commit buffer: Committing changes.
+* Committing changes: Committing changes.
+* Conflict (file status): File status.
+* Conflicts, how to resolve them: Viewing differences.
+* Context diff, how to get: Customization.
+* Contributors: Contributors.
+* Copyright message, getting rid of it: Customization.
+* Customization: Customization.
+* Deleting files: Adding and removing files.
+* Diff: Viewing differences.
+* Dired: Editing files.
+* Edit buffer: Committing changes.
+* Editing files: Editing files.
+* Email archives: Archives.
+* Email to the author: Reporting bugs and ideas.
+* Enhancements: Future enhancements.
+* Erasing commit message: Committing changes.
+* Erasing the input buffer: Customization.
+* Example run: Getting started.
+* Expunging uninteresting entries: Removing handled entries.
+* File selection: Selected files.
+* File status: File status.
+* Finding files: Editing files.
+* Ftp-sites: Archives.
+* Generating a typeset manual: Typeset manual installation.
+* Generating the on-line manual: On-line manual installation.
+* Getting pcl-cvs: Archives.
+* Getting rid of the Copyright message.: Customization.
+* Getting rid of uninteresting lines: Removing handled entries.
+* Getting status: Getting info about files.
+* Handled lines, removing them: Removing handled entries.
+* Info-file (how to generate): On-line manual installation.
+* Inhibiting the Copyright message.: Customization.
+* Installation: Installation.
+* Installation of elisp files: Pcl-cvs installation.
+* Installation of on-line manual: On-line manual installation.
+* Installation of typeset manual: Typeset manual installation.
+* Introduction: Getting started.
+* Invoking dired: Editing files.
+* Loading files: Editing files.
+* Log (RCS/cvs command): Getting info about files.
+* Manual installation (on-line): On-line manual installation.
+* Manual installation (typeset): Typeset manual installation.
+* Marked files: Selected files.
+* Marking files: Marking files.
+* Merged (file status): File status.
+* Modified (file status): File status.
+* Move away FILE - it is in the way (file status): File status.
+* Movement Commands: Movement commands.
+* On-line manual (how to generate): On-line manual installation.
+* Printing a manual: Typeset manual installation.
+* Putting files under CVS control: Adding and removing files.
+* Removed (file status): File status.
+* Removed by you, changed in repository (file status): File status.
+* Removed from repository (file status): File status.
+* Removed from repository, changed by you (file status): File status.
+* Removing files: Adding and removing files.
+* Removing uninteresting (processed) lines: Removing handled entries.
+* Reporting bugs and ideas: Reporting bugs and ideas.
+* Resurrecting files: Adding and removing files.
+* Selected files: Selected files.
+* Selecting files (commands to mark files): Marking files.
+* Sites: Archives.
+* Status (cvs command): Getting info about files.
+* TeX - generating a typeset manual: Typeset manual installation.
+* This repository is missing!... (file status): File status.
+* Unidiff, how to get: Customization.
+* Uninteresting entries, getting rid of them: Removing handled entries.
+* Unknown (file status): File status.
+* Updated (file status): File status.
+* Variables, list of all: Customization.
+* Viewing differences: Viewing differences.
+
+
+File: pcl-cvs, Node: Key Index, Prev: Concept Index, Up: Top
+
+Key Index
+*********
+
+* Menu:
+
+* A - add ChangeLog entry: Editing files.
+* C-k - remove selected entries: Removing handled entries.
+* C-n - Move down one file: Movement commands.
+* C-p - Move up one file: Movement commands.
+* DEL - unmark previous file: Marking files.
+* M - marking all files: Marking files.
+* SPC - Move down one file: Movement commands.
+* U - unmark all files: Marking files.
+* a - add a file: Adding and removing files.
+* b - diff backup file: Viewing differences.
+* c - commit files: Committing changes.
+* d - run cvs diff: Viewing differences.
+* f - find file or directory: Editing files.
+* g - Rerun cvs update: Updating the directory.
+* l - run cvs log: Getting info about files.
+* m - marking a file: Marking files.
+* n - Move down one file: Movement commands.
+* o - find file in other window: Editing files.
+* p - Move up on file: Movement commands.
+* r - remove a file: Adding and removing files.
+* s - run cvs status: Getting info about files.
+* u - unmark a file: Marking files.
+* x - remove processed entries: Removing handled entries.
+
+
+
+Tag Table:
+Node: Top1004
+Node: Copying3396
+Node: Installation22716
+Node: Pcl-cvs installation23507
+Node: On-line manual installation25291
+Node: Typeset manual installation26310
+Node: About pcl-cvs27048
+Node: Contributors27417
+Node: Archives28440
+Node: Getting started29287
+Node: Buffer contents31728
+Node: File status32277
+Node: Selected files35303
+Node: Commands35976
+Node: Updating the directory37018
+Node: Movement commands38043
+Node: Marking files38629
+Node: Committing changes39456
+Node: Editing files40502
+Node: Getting info about files41335
+Node: Adding and removing files41805
+Node: Removing handled entries43145
+Node: Ignoring files44058
+Node: Viewing differences44593
+Node: Customization45595
+Node: Future enhancements47326
+Node: Reporting bugs and ideas48394
+Node: Function and Variable Index48842
+Node: Concept Index50743
+Node: Key Index55865
+
+End Tag Table
diff --git a/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.texinfo b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.texinfo
new file mode 100644
index 0000000..5ad8db1
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/pcl-cvs/pcl-cvs.texinfo
@@ -0,0 +1,1437 @@
+\input texinfo @c -*-texinfo-*-
+
+@comment pcl-cvs.texinfo,v 1.2 1992/04/07 20:49:23 berliner Exp
+@comment Documentation for the GNU Emacs CVS mode.
+@comment Copyright (C) 1992 Per Cederqvist
+
+@comment This file is part of the pcl-cvs distribution.
+
+@comment Pcl-cvs is free software; you can redistribute it and/or modify
+@comment it under the terms of the GNU General Public License as published by
+@comment the Free Software Foundation; either version 1, or (at your option)
+@comment any later version.
+
+@comment Pcl-cvs is distributed in the hope that it will be useful,
+@comment but WITHOUT ANY WARRANTY; without even the implied warranty of
+@comment MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+@comment GNU General Public License for more details.
+
+@comment You should have received a copy of the GNU General Public License
+@comment along with pcl-cvs; see the file COPYING. If not, write to
+@comment the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+@setfilename pcl-cvs
+@settitle Pcl-cvs - The Emacs Front-End to CVS
+@setchapternewpage on
+
+@ifinfo
+Copyright @copyright{} 1992 Per Cederqvist
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+@ignore
+Permission is granted to process this file through Tex and print the
+results, provided the printed document carries copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+@end ignore
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided also that the
+section entitled ``GNU General Public License'' is included exactly as
+in the original, and provided that the entire resulting derived work is
+distributed under the terms of a permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that the section entitled ``GNU General Public License'' and
+this permission notice may be included in translations approved by the
+Free Software Foundation instead of in the original English.
+@end ifinfo
+
+@synindex vr fn
+@comment The titlepage section does not appear in the Info file.
+@titlepage
+@sp 4
+@comment The title is printed in a large font.
+@center @titlefont{User's Guide}
+@sp
+@center @titlefont{to}
+@sp
+@center @titlefont{pcl-cvs - the Emacs Front-End to CVS}
+@sp 2
+@center release 1.02
+@comment -release-
+@sp 3
+@center Per Cederqvist
+@sp 3
+@center last updated 29 Mar 1992
+@comment -date-
+
+@comment The following two commands start the copyright page
+@comment for the printed manual. This will not appear in the Info file.
+@page
+@vskip 0pt plus 1filll
+Copyright @copyright{} 1992 Per Cederqvist
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided also that the
+section entitled ``GNU General Public License'' is included exactly as
+in the original, and provided that the entire resulting derived work is
+distributed under the terms of a permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that the section entitled ``GNU General Public License'' and
+this permission notice may be included in translations approved by the
+Free Software Foundation instead of in the original English.
+@end titlepage
+
+@comment ================================================================
+@comment The real text starts here
+@comment ================================================================
+
+@node Top, Copying, (dir), (dir)
+@comment node-name, next, previous, up
+
+
+@ifinfo
+This info manual describes pcl-cvs which is a GNU Emacs front-end to
+CVS. It works with CVS version 1.3. This manual is updated to release
+1.02 of pcl-cvs.
+@end ifinfo
+@comment -release-
+
+@menu
+* Copying:: GNU General Public License
+* Installation:: How to install pcl-cvs on your system.
+* About pcl-cvs:: Authors and ftp sites.
+
+* Getting started:: An introduction with a walk-through example.
+* Buffer contents:: An explanation of the buffer contents.
+* Commands:: All commands, grouped by type.
+
+* Customization:: How you can tailor pcl-cvs to suit your needs.
+* Future enhancements:: Future enhancements of pcl-cvs.
+* Reporting bugs and ideas:: Where to report bugs.
+
+* Function and Variable Index:: List of functions and variables.
+* Concept Index:: List of concepts.
+* Key Index:: List of keystrokes.
+
+ --- The Detailed Node Listing ---
+
+Installation
+
+* Pcl-cvs installation:: How to install pcl-cvs on your system.
+* On-line manual installation:: How to install the on-line manual.
+* Typeset manual installation:: How to create typeset documentation
+ about pcl-cvs.
+
+About pcl-cvs
+
+* Contributors:: Contributors to pcl-cvs.
+* Archives:: Where can I get a copy of Pcl-Cvs?
+
+Buffer contents
+
+* File status:: The meaning of the second field.
+* Selected files:: How selection works.
+
+Commands
+
+* Updating the directory:: Commands to update the local directory
+* Movement commands:: How to move up and down in the buffer
+* Marking files:: How to mark files that other commands
+ will later operate on.
+* Committing changes:: Checking in your modifications to the
+ CVS repository.
+* Editing files:: Loading files into Emacs.
+* Getting info about files:: Display the log and status of files.
+* Adding and removing files:: Adding and removing files
+* Removing handled entries:: Uninteresting lines can easily be removed.
+* Ignoring files:: Telling CVS to ignore generated files.
+* Viewing differences:: Commands to @samp{diff} different versions.
+@end menu
+
+@node Copying, Installation, Top, Top
+@unnumbered GNU GENERAL PUBLIC LICENSE
+@center Version 2, June 1991
+
+@display
+Copyright @copyright{} 1989, 1991 Free Software Foundation, Inc.
+675 Mass Ave, Cambridge, MA 02139, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+@end display
+
+@unnumberedsec Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software---to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+@iftex
+@unnumberedsec TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+@end iftex
+@ifinfo
+@center TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+@end ifinfo
+
+@enumerate
+@item
+This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The ``Program'', below,
+refers to any such program or work, and a ``work based on the Program''
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term ``modification''.) Each licensee is addressed as ``you''.
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+@item
+You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+@item
+You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+@enumerate a
+@item
+You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+@item
+You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+@item
+If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+@end enumerate
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+@item
+You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+@enumerate a
+@item
+Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+@item
+Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+@item
+Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+@end enumerate
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+@item
+You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+@item
+You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+@item
+Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+@item
+If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+@item
+If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+@item
+The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and ``any
+later version'', you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+@item
+If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+@iftex
+@heading NO WARRANTY
+@end iftex
+@ifinfo
+@center NO WARRANTY
+@end ifinfo
+
+@item
+BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM ``AS IS'' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+@item
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+@end enumerate
+
+@iftex
+@heading END OF TERMS AND CONDITIONS
+@end iftex
+@ifinfo
+@center END OF TERMS AND CONDITIONS
+@end ifinfo
+
+@page
+@unnumberedsec Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the ``copyright'' line and a pointer to where the full notice is found.
+
+@smallexample
+@var{one line to give the program's name and a brief idea of what it does.}
+Copyright (C) 19@var{yy} @var{name of author}
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+@end smallexample
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+@smallexample
+Gnomovision version 69, Copyright (C) 19@var{yy} @var{name of author}
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+@end smallexample
+
+The hypothetical commands @samp{show w} and @samp{show c} should show
+the appropriate parts of the General Public License. Of course, the
+commands you use may be called something other than @samp{show w} and
+@samp{show c}; they could even be mouse-clicks or menu items---whatever
+suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a ``copyright disclaimer'' for the program, if
+necessary. Here is a sample; alter the names:
+
+@example
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+@var{signature of Ty Coon}, 1 April 1989
+Ty Coon, President of Vice
+@end example
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
+@node Installation, About pcl-cvs, Copying, Top
+@comment node-name, next, previous, up
+@chapter Installation
+@cindex Installation
+
+This section describes the installation of pcl-cvs, the GNU Emacs CVS
+front-end. You should install not only the elisp files themselves, but
+also the on-line documentation so that your users will know how to use
+it. You can create typeset documentation from the file
+@file{pcl-cvs.texinfo} as well as an on-line info file. The following
+steps are also described in the file @file{INSTALL} in the source
+directory.
+
+@menu
+* Pcl-cvs installation:: How to install pcl-cvs on your system.
+* On-line manual installation:: How to install the on-line manual.
+* Typeset manual installation:: How to create typeset documentation
+ about pcl-cvs.
+@end menu
+
+@node Pcl-cvs installation, On-line manual installation, Installation, Installation
+@comment node-name, next, previous, up
+@section Installation of the pcl-cvs program
+@cindex Installation of elisp files
+
+@enumerate
+@item
+Edit the file @file{Makefile} to reflect the situation at your site.
+The only things you have to change is the definition of @code{lispdir}
+and @code{infodir}. The elisp files will be copied to @code{lispdir},
+and the info file to @code{infodir}.
+
+@item
+Configure pcl-cvs.el
+
+There are a couple of paths that you have to check to make sure that
+they match you system. They appear early in the file pcl-cvs.el.
+
+@strong{NOTE:} If your system is running emacs 18.57 or earlier you MUST
+uncomment the line that says:
+
+@example
+(setq delete-exited-processes nil)
+@end example
+
+Setting @code{delete-exited-processes} to @code{nil} works around a bug
+in emacs that causes it to dump core. The bug was fixed in emacs
+18.58.@refill
+
+@item
+Type @samp{make install} in the source directory. This will
+byte-compile all @file{.el} files and copy both the @file{.el} and the
+@file{.elc} into the directory you specified in step 1.
+
+If you don't want to install the @file{.el} files but only the
+@file{.elc} files (the byte-compiled files), you can type `@samp{make
+install_elc}' instead of `@samp{make install}'.
+
+If you only want to create the compiled elisp files, but don't want to
+install them, you can type @samp{make elcfiles} instead. This is what
+happens if you only type @samp{make} without parameters.
+
+@item
+Edit the file @file{default.el} in your emacs lisp directory (usually
+@file{/usr/gnu/emacs/lisp} or something similar) and enter the contents
+of the file @file{pcl-cvs-startup.el} into it. It contains a couple of
+@code{auto-load}s that facilitates the use of pcl-cvs.
+
+@end enumerate
+
+@node On-line manual installation, Typeset manual installation, Pcl-cvs installation, Installation
+@comment node-name, next, previous, up
+@section Installation of the on-line manual.
+@cindex Manual installation (on-line)
+@cindex Installation of on-line manual
+@cindex Generating the on-line manual
+@cindex On-line manual (how to generate)
+@cindex Info-file (how to generate)
+
+@enumerate
+@item
+Create the info file @file{pcl-cvs} from @file{pcl-cvs.texinfo} by
+typing @samp{make info}. If you don't have the program @samp{makeinfo}
+you can get it by anonymous ftp from e.g. @samp{ftp.gnu.ai.mit.edu} as
+@file{pub/gnu/texinfo-2.14.tar.Z} (there might be a newer version there
+when you read this), or you could use the preformatted info file
+@file{pcl-cvs.info} that is included in the distribution (type
+@samp{cp pcl-cvs.info pcl-cvs}).@refill
+
+@item
+Move the info file @file{pcl-cvs} to your standard info directory.
+This might be called something like @file{/usr/gnu/emacs/info}.@refill
+
+@item
+Edit the file @file{dir} in the info directory and enter one line to
+contain a pointer to the info file @file{pcl-cvs}. The line can, for
+instance, look like this:@refill
+
+@example
+* Pcl-cvs: (pcl-cvs). An Emacs front-end to CVS.
+@end example
+@end enumerate
+
+@node Typeset manual installation, , On-line manual installation, Installation
+@comment node-name, next, previous, up
+@section How to make typeset documentation from pcl-cvs.texinfo
+@cindex Manual installation (typeset)
+@cindex Installation of typeset manual
+@cindex Printing a manual
+@cindex TeX - generating a typeset manual
+@cindex Generating a typeset manual
+
+If you have @TeX{} installed at your site, you can make a typeset manual
+from @file{pcl-cvs.texinfo}.
+
+@enumerate
+@item
+Run @TeX{} by typing `@samp{make pcl-cvs.dvi}'. You will not get the
+indices unless you have the @code{texindex} program.
+
+@item
+Convert the resulting device independent file @file{pcl-cvs.dvi} to a
+form which your printer can output and print it. If you have a
+postscript printer there is a program, @code{dvi2ps}, which does. There
+is also a program which comes together with @TeX{}, @code{dvips}, which
+you can use.
+
+@end enumerate
+
+@node About pcl-cvs, Getting started, Installation, Top
+@comment node-name, next, previous, up
+@chapter About pcl-cvs
+@cindex About pcl-cvs
+
+Pcl-cvs is a front-end to CVS version 1.3. It integrates the most
+frequently used CVS commands into emacs.
+
+@menu
+* Contributors:: Contributors to pcl-cvs.
+* Archives:: Where can I get a copy of Pcl-Cvs?
+@end menu
+
+@node Contributors, Archives, About pcl-cvs, About pcl-cvs
+@comment node-name, next, previous, up
+@section Contributors to pcl-cvs
+@cindex Contributors
+@cindex Authors
+
+Contributions to the package are welcome. I have limited time to work
+on this project, but I will gladly add any code that you contribute to
+me to this package (@pxref{Reporting bugs and ideas}).
+
+The following persons have made contributions to pcl-cvs.
+
+@itemize @bullet
+@item
+Brian Berliner wrote CVS, together with some other contributors.
+Without his work on CVS this package would be useless@dots{}
+
+@item
+Per Cederqvist wrote most of the otherwise unattributed functions in
+pcl-cvs as well as all documentation.
+
+@item
+Inge Wallin (@samp{inge@@lysator.liu.se}) wrote the skeleton to
+@file{pcl-cvs.texinfo}, and gave useful comments on it. He also wrote
+the files @file{elib-node.el} and @file{compile-all.el}. The file
+@file{cookie.el} was inspired by Inge.@refill
+
+@item
+Linus Tolke (@samp{linus@@lysator.liu.se}) contributed useful comments
+on both the functionality and the documentation.@refill
+
+@end itemize
+
+
+@node Archives, , Contributors, About pcl-cvs
+@comment node-name, next, previous, up
+@section Where can I get pcl-cvs?
+@cindex Sites
+@cindex Archives
+@cindex Ftp-sites
+@cindex Getting pcl-cvs
+@cindex Email archives
+
+This release of pcl-cvs is included in the CVS 1.3 distribution.
+However, since pcl-cvs has had less time to mature (the first line of
+code was written less than a year ago) it is likely that there will be a
+new release of pcl-cvs before the next release of CVS.
+
+The latest release of pcl-cvs can be fetched via anonymous ftp from
+@code{ftp.lysator.liu.se}, (IP no. 130.236.254.1) in the directory
+@code{pub/emacs}. If you don't live in Scandinavia you should probably
+check with archie to see if there is a site closer to you that archives
+pcl-cvs.
+
+New releases will be announced to appropriate newsgroups. If you send
+your email address to me I will add you to my list of people to mail
+when I make a new release.
+
+@node Getting started, Buffer contents, About pcl-cvs, Top
+@comment node-name, next, previous, up
+@chapter Getting started
+@cindex Introduction
+@cindex Example run
+
+This document assumes that you know what CVS is, and that you at least
+knows the fundamental concepts of CVS. If that is not the case you
+should read the man page for CVS.
+
+Pcl-cvs is only useful once you have checked out a module. So before
+you invoke it you must have a copy of a module somewhere in the file
+system.
+
+You invoke pcl-cvs by typing @kbd{M-x pcl-cvs RET}. If your emacs
+responds with @samp{[No match]} your system administrator has not
+installed pcl-cvs properly. Try @kbd{M-x load-library RET pcl-cvs RET}.
+If that also fails - talk to your root. If it succeeds you might put
+this line in your @file{.emacs} file so that you don't have to type the
+@samp{load-library} command every time you wish to use pcl-cvs:
+
+@example
+(autoload 'cvs-update "pcl-cvs" nil t)
+@end example
+
+The function @code{cvs-update} will ask for a directory. The command
+@samp{cvs update} will be run in that directory. (It should contain
+files that have been checked out from a CVS archive.) The output from
+@code{cvs} will be parsed and presented in a table in a buffer called
+@samp{*cvs*}. It might look something like this:
+
+@example
+PCL-CVS release 1.02.
+@comment -release-
+
+In directory /users/ceder/FOO/test:
+ Updated bar
+ Updated file.txt
+ Modified ci namechange
+ Updated newer
+
+In directory /users/ceder/FOO/test/sub:
+ Modified ci ChangeLog
+---------- End -----
+@end example
+
+In this example the three files (@file{bar}, @file{file.txt} and
+@file{newer}) that are marked with @samp{Updated} have been copied from
+the CVS repository to @file{/users/ceder/FOO/test/} since someone else
+have checked in newer versions of them. Two files (@file{namechange}
+and @file{sub/ChangeLog}) have been modified locally, and needs to be
+checked in.
+
+You can move the cursor up and down in the buffer with @kbd{C-n} and
+@kbd{C-p} or @kbd{n} and @kbd{p}. If you press @kbd{c} on one of the
+@samp{Modified} files that file will be checked in to the CVS
+repository. @xref{Committing changes}. You can press @kbd{x} to get rid
+of the "uninteresting" files that have only been @samp{Updated} (and
+don't require any further action from you).@refill
+
+You can also easily get a @samp{diff} between your modified file and the
+base version that you started from, and you can get the output from
+@samp{cvs log} and @samp{cvs status} on the listed files simply by
+pressing a key (@pxref{Getting info about files}).
+
+@node Buffer contents, Commands, Getting started, Top
+@comment node-name, next, previous, up
+@chapter Buffer contents
+@cindex Buffer contents
+
+The display contains four columns. They contain, from left to right:
+
+@itemize @bullet
+@item
+An asterisk when the file is @dfn{marked} (@pxref{Selected
+files}).@refill
+@item
+The status of the file. See @xref{File status}, for more information.@refill
+@item
+A "need to be checked in"-marker (@samp{ci}).
+@item
+The file name.
+@end itemize
+
+@menu
+* File status:: The meaning of the second field.
+* Selected files:: How selection works.
+@end menu
+
+@node File status, Selected files, Buffer contents, Buffer contents
+@comment node-name, next, previous, up
+@section File status
+@cindex File status
+@cindex Updated (file status)
+@cindex Modified (file status)
+@cindex Merged (file status)
+@cindex Conflict (file status)
+@cindex Added (file status)
+@cindex Removed (file status)
+@cindex Unknown (file status)
+@cindex Removed from repository (file status)
+@cindex Removed from repository, changed by you (file status)
+@cindex Removed by you, changed in repository (file status)
+@cindex Move away @var{file} - it is in the way (file status)
+@cindex This repository is missing!@dots{} (file status)
+
+The @samp{file status} field can have the following values:
+
+@table @samp
+@item Updated
+The file was brought up to date with respect to the repository. This is
+done for any file that exists in the repository but not in your source,
+and for files that you haven't changed but are not the most recent
+versions available in the repository.@refill
+
+@item Modified
+The file is modified in your working directory, and there was no
+modification to the same file in the repository.@refill
+
+@item Merged
+The file is modified in your working directory, and there were
+modifications in the repository as well as in your copy, but they were
+merged successfully, without conflict, in your working directory.@refill
+
+@item Conflict
+A conflict was detected while trying to merge your changes to @var{file}
+with changes from the source repository. @var{file} (the copy in your
+working directory) is now the output of the @samp{rcsmerge} command on
+the two versions; an unmodified copy of your file is also in your
+working directory, with the name @file{.#@var{file}.@var{version}},
+where @var{version} is the RCS revision that your modified file started
+from. @xref{Viewing differences}, for more details.@refill
+
+@item Added
+The file has been added by you, but it still needs to be checked in to
+the repository.@refill
+
+@item Removed
+The file has been removed by you, but it needs to be checked in to the
+repository. You can resurrect it by typing @kbd{a} (@pxref{Adding and
+removing files}).@refill
+
+@item Unknown
+A file that was detected in your directory, but that neither appears in
+the repository, nor is present on the list of files that CVS should
+ignore.@refill
+
+@end table
+
+There are also a few special cases, that rarely occur, which have longer
+strings in the fields:
+
+@table @samp
+@item Removed from repository
+The file has been removed from your directory since someone has removed
+it from the repository. (It is still present in the Attic directory, so
+no permanent loss has occurred). This, unlike the other entries in this
+table, is not an error condition.@refill
+
+@item Removed from repository, changed by you
+You have modified a file that someone have removed from the repository.
+You can correct this situation by removing the file manually (see
+@pxref{Adding and removing files}).@refill
+
+@item Removed by you, changed in repository
+You have removed a file, and before you committed the removal someone
+committed a change to that file. You could use @kbd{a} to resurrect the
+file (see @pxref{Adding and removing files}).@refill
+
+@item Move away @var{file} - it is in the way
+For some reason CVS does not like the file @var{file}. Rename or remove
+it.@refill
+
+@item This repository is missing! Remove this dir manually.
+It is impossible to remove a directory in the CVS repository in a clean
+way. Someone have tried to remove one, and CVS gets confused. Remove
+your copy of the directory.@refill
+@end table
+
+@node Selected files, , File status, Buffer contents
+@comment node-name, next, previous, up
+@section Selected files
+@cindex Selected files
+@cindex Marked files
+@cindex File selection
+@cindex Active files
+
+Many of the commands works on the current set of @dfn{selected} files.
+
+@itemize @bullet
+@item
+If there are any files that are marked they constitute the set of
+selected files.@refill
+@item
+Otherwise, if the cursor points to a file, that file is the selected
+file.@refill
+@item
+Otherwise, if the cursor points to a directory, all the files in that
+directory that appears in the buffer are the selected files.
+@end itemize
+
+This scheme might seem a little complicated, but once one get used to
+it, it is quite powerful.
+
+@xref{Marking files} tells how you mark and unmark files.
+
+@node Commands, Customization, Buffer contents, Top
+@comment node-name, next, previous, up
+@chapter Commands
+
+@iftex
+This chapter describes all the commands that you can use in pcl-cvs.
+@end iftex
+@ifinfo
+The nodes in this menu contains explanations about all the commands that
+you can use in pcl-cvs. They are grouped together by type.
+@end ifinfo
+
+@menu
+* Updating the directory:: Commands to update the local directory
+* Movement commands:: How to move up and down in the buffer
+* Marking files:: How to mark files that other commands
+ will later operate on.
+* Committing changes:: Checking in your modifications to the
+ CVS repository.
+* Editing files:: Loading files into Emacs.
+* Getting info about files:: Display the log and status of files.
+* Adding and removing files:: Adding and removing files
+* Removing handled entries:: Uninteresting lines can easily be removed.
+* Ignoring files:: Telling CVS to ignore generated files.
+* Viewing differences:: Commands to @samp{diff} different versions.
+@end menu
+
+@node Updating the directory, Movement commands, Commands, Commands
+@comment node-name, next, previous, up
+@section Updating the directory
+@findex cvs-update
+@findex cvs-update-no-prompt
+@kindex g - Rerun @samp{cvs update}
+
+
+@table @kbd
+
+@item M-x cvs-update
+Run a @samp{cvs update} command. You will be asked for the directory in
+which the @samp{cvs update} will be run. The output will be parsed by
+pcl-cvs, and the result printed in the @samp{*cvs*} buffer (see
+@pxref{Buffer contents} for a description of the contents).@refill
+
+By default, @samp{cvs-update} will descend recursively into
+subdirectories. You can avoid that behavior by giving a prefix
+argument to it (e.g., by typing @kbd{C-u M-x cvs-update RET}).@refill
+
+All other commands in pcl-cvs requires that you have a @samp{*cvs*}
+buffer. This is the command that you use to get one.@refill
+
+@item g
+This will run @samp{cvs update} again. It will always use the same
+buffer that was used with the previous @samp{cvs update}. Give a prefix
+argument to avoid descending into subdirectories. This runs the command
+@samp{cvs-update-no-prompt}.@refill
+@end table
+@node Movement commands, Marking files, Updating the directory, Commands
+@comment node-name, next, previous, up
+@section Movement Commands
+@cindex Movement Commands
+@findex cookie-next-cookie
+@findex cookie-previous-cookie
+@kindex SPC - Move down one file
+@kindex C-n - Move down one file
+@kindex n - Move down one file
+@kindex C-p - Move up one file
+@kindex p - Move up on file
+
+You can use most normal Emacs commands to move forward and backward in
+the buffer. Some keys are rebound to functions that take advantage of
+the fact that the buffer is a pcl-cvs buffer:
+
+
+@table @kbd
+@item SPC
+@itemx C-n
+@itemx n
+These keys move the cursor one file forward, towards the end of the
+buffer (@code{cookie-next-cookie}).
+
+@item C-p
+@itemx p
+These keys move one file backward, towards the beginning of the buffer
+(@code{cookie-previous-cookie}).
+@end table
+
+@node Marking files, Committing changes, Movement commands, Commands
+@comment node-name, next, previous, up
+@section Marking files
+@cindex Selecting files (commands to mark files)
+@cindex Marking files
+@kindex m - marking a file
+@kindex M - marking all files
+@kindex u - unmark a file
+@kindex U - unmark all files
+@kindex DEL - unmark previous file
+@findex cvs-mark
+@findex cvs-unmark
+@findex cvs-mark-all-files
+@findex cvs-unmark-all-files
+@findex cvs-unmark-up
+
+Pcl-cvs works on a set of @dfn{selected files} (@pxref{Selected files}).
+You can mark and unmark files with these commands:
+
+@table @kbd
+@item m
+This marks the file that the cursor is positioned on. If the cursor is
+positioned on a directory all files in that directory will be marked.
+(@code{cvs-mark}).
+
+@item u
+Unmark the file that the cursor is positioned on. If the cursor is on a
+directory, all files in that directory will be unmarked.
+(@code{cvs-unmark}).@refill
+
+@item M
+Mark @emph{all} files in the buffer (@code{cvs-mark-all-files}).
+
+@item U
+Unmark @emph{all} files (@code{cvs-unmark-all-files}).
+
+@item @key{DEL}
+Unmark the file on the previous line, and move point to that line
+(@code{cvs-unmark-up}).
+@end table
+
+@node Committing changes, Editing files, Marking files, Commands
+@comment node-name, next, previous, up
+@section Committing changes
+@cindex Committing changes
+@cindex Ci
+@findex cvs-commit
+@kindex c - commit files
+@vindex cvs-erase-input-buffer (variable)
+@cindex Commit buffer
+@cindex Edit buffer
+@cindex Erasing commit message
+
+@table @kbd
+@item c
+All files that have a "need to be checked in"-marker (@pxref{Buffer
+contents}) can be checked in with the @kbd{c} command. It checks in all
+selected files (@pxref{Selected files}) (except those who lack the
+"ci"-marker - they are ignored). Pressing @kbd{c} causes
+@code{cvs-commit} to be run.@refill
+
+When you press @kbd{c} you will get a buffer called
+@samp{*cvs-commit-message*}. Enter the log message for the file(s) in
+it. When you are ready you should press @kbd{C-c C-c} to actually
+commit the files (using @code{cvs-edit-done}).
+
+Normally the @samp{*cvs-commit-message*} buffer will retain the log
+message from the previous commit, but if the variable
+@code{cvs-erase-input-buffer} is set to a non-nil value the buffer will
+be erased. Point and mark will always be located around the entire
+buffer so that you can easily erase it with @kbd{C-w}
+(@samp{kill-region}).@refill
+@end table
+
+@node Editing files, Getting info about files, Committing changes, Commands
+@comment node-name, next, previous, up
+@section Editing files
+
+@cindex Editing files
+@cindex Finding files
+@cindex Loading files
+@cindex Dired
+@cindex Invoking dired
+@findex cvs-find-file
+@findex cvs-find-file-other-window
+@findex cvs-add-change-log-entry-other-window
+@kindex f - find file or directory
+@kindex o - find file in other window
+@kindex A - add ChangeLog entry
+
+There are currently three commands that can be used to find a file (that
+is, load it into a buffer and start editing it there). These commands
+work on the line that the cursor is situated at. They ignore any marked
+files.
+
+@table @kbd
+@item f
+Find the file that the cursor points to. Run @samp{dired}
+@ifinfo
+(@pxref{Dired,,,Emacs})
+@end ifinfo
+if the cursor points to a directory (@code{cvs-find-file}).@refill
+
+@item o
+Like @kbd{f}, but use another window
+(@code{cvs-find-file-other-window}).@refill
+
+@item A
+Invoke @samp{add-change-log-entry-other-window} to edit a
+@samp{ChangeLog} file. The @samp{ChangeLog} will be found in the
+directory of the file the cursor points to.
+(@code{cvs-add-change-log-entry-other-window}).@refill
+@end table
+
+@node Getting info about files, Adding and removing files, Editing files, Commands
+@comment node-name, next, previous, up
+@section Getting info about files
+@cindex Status (cvs command)
+@cindex Log (RCS/cvs command)
+@cindex Getting status
+@kindex l - run @samp{cvs log}
+@kindex s - run @samp{cvs status}
+@findex cvs-log
+@findex cvs-status
+
+Both of the following commands can be customized.
+@xref{Customization}.@refill
+
+@table @kbd
+@item l
+Run @samp{cvs log} on all selected files, and show the result in a
+temporary buffer (@code{cvs-log}).
+
+@item s
+Run @samp{cvs status} on all selected files, and show the result in a
+temporary buffer (@code{cvs-status}).
+@end table
+
+@node Adding and removing files, Removing handled entries, Getting info about files, Commands
+@comment node-name, next, previous, up
+@section Adding and removing files
+@cindex Adding files
+@cindex Removing files
+@cindex Resurrecting files
+@cindex Deleting files
+@cindex Putting files under CVS control
+@kindex a - add a file
+@kindex r - remove a file
+@findex cvs-add
+@findex cvs-remove-file
+
+The following commands are available to make it easy to add and remove
+files from the CVS repository.
+
+@table @kbd
+@item a
+Add all selected files. This command can be used on @samp{Unknown}
+files (see @pxref{File status}). The status of the file will change to
+@samp{Added}, and you will have to use @kbd{c} (@samp{cvs-commit}, see
+@pxref{Committing changes}) to really add the file to the
+repository.@refill
+
+This command can also be used on @samp{Removed} files (before you commit
+them) to resurrect them.
+
+Selected files that are neither @samp{Unknown} nor @samp{Removed} will
+be ignored by this command.
+
+The command that is run is @code{cvs-add}.
+
+@item r
+This command removes the selected files (after prompting for
+confirmation). The files are @samp{rm}ed from your directory and
+(unless the status was @samp{Unknown}; @pxref{File status}) they will
+also be @samp{cvs remove}d. If the files were @samp{Unknown} they will
+disappear from the buffer. Otherwise their status will change to
+@samp{Removed}, and you must use @kbd{c} (@samp{cvs-commit},
+@pxref{Committing changes}) to commit the removal.@refill
+
+The command that is run is @code{cvs-remove-file}.
+@end table
+
+@node Removing handled entries, Ignoring files, Adding and removing files, Commands
+@comment node-name, next, previous, up
+@section Removing handled entries
+@cindex Expunging uninteresting entries
+@cindex Uninteresting entries, getting rid of them
+@cindex Getting rid of uninteresting lines
+@cindex Removing uninteresting (processed) lines
+@cindex Handled lines, removing them
+@kindex x - remove processed entries
+@kindex C-k - remove selected entries
+@findex cvs-remove-handled
+@findex cvs-acknowledge
+
+@table @kbd
+@item x
+This command allows you to remove all entries that you have processed.
+More specifically, the lines for @samp{Updated} files (@pxref{File
+status} and files that have been checked in (@pxref{Committing changes})
+are removed from the buffer. If a directory becomes empty the heading
+for that directory is also removed. This makes it easier to get an
+overview of what needs to be done.
+
+The command is called @code{cvs-remove-handled}. If
+@samp{cvs-auto-remove-handled} is set to non-@samp{nil} this will
+automatically be performed after every commit.@refill
+
+@item C-k
+This command can be used for lines that @samp{cvs-remove-handled} would
+not delete, but that you want to delete (@code{cvs-acknowledge}).
+@end table
+
+@node Ignoring files, Viewing differences, Removing handled entries, Commands
+@comment node-name, next, previous, up
+@section Ignoring files
+
+@table @kbd
+@item i
+Arrange so that CVS will ignore the selected files. The file names are
+added to the @file{.cvsignore} file in the corresponding directory. If
+the @file{.cvsignore} doesn't exist it will be created.
+
+The @file{.cvsignore} file should normally be added to the repository,
+but you could ignore it also if you like it better that way.
+
+This runs @code{cvs-ignore}.
+@end table
+
+@node Viewing differences, , Ignoring files, Commands
+@comment node-name, next, previous, up
+@section Viewing differences
+@cindex Diff
+@cindex Conflicts, how to resolve them
+@cindex Viewing differences
+@kindex d - run @samp{cvs diff}
+@kindex b - diff backup file
+@findex cvs-diff-cvs
+@findex cvs-diff-backup
+
+@table @kbd
+@item d
+Display a @samp{cvs diff} between the selected files and the RCS version
+that they are based on. @xref{Customization} describes how you can send
+flags to @samp{cvs diff}. (The function that does the job is
+@code{cvs-diff-cvs}).@refill
+
+@item b
+If CVS finds a conflict while merging two versions of a file (during a
+@samp{cvs update}, @pxref{Updating the directory}) it will save the
+original file in a file called @file{.#@var{FILE}.@var{VERSION}} where
+@var{FILE} is the name of the file, and @var{VERSION} is the RCS version
+number that your file was based on.
+
+With the @kbd{b} command you can run a @samp{diff} on the files
+@file{.#@var{FILE}.@var{VERSION}} and @file{@var{FILE}}. You can get a
+context- or Unidiff by setting @samp{cvs-diff-flags} -
+@pxref{Customization}. This command only works on files that have
+status @samp{Conflict} or @samp{Merged}. The name of the command is
+@code{cvs-diff-backup}. @refill
+@end table
+
+@node Customization, Future enhancements, Commands, Top
+@comment node-name, next, previous, up
+@chapter Customization
+@vindex cvs-erase-input-buffer (variable)
+@vindex cvs-inhibit-copyright-message (variable)
+@vindex cvs-cvs-diff-flags (variable)
+@vindex cvs-diff-flags (variable)
+@vindex cvs-log-flags (variable)
+@vindex cvs-status-flags (variable)
+@vindex cvs-auto-remove-handled (variable)
+@cindex Inhibiting the Copyright message.
+@cindex Copyright message, getting rid of it
+@cindex Getting rid of the Copyright message.
+@cindex Customization
+@cindex Variables, list of all
+@cindex Erasing the input buffer
+@cindex Context diff, how to get
+@cindex Unidiff, how to get
+@cindex Automatically remove handled files
+
+If you have an idea about any customization that would be handy but
+isn't present in this list, please tell me! @xref{Reporting bugs and
+ideas} for info on how to reach me.@refill
+
+@table @samp
+@item cvs-erase-input-buffer
+If set to anything else than @samp{nil} the edit buffer will be erased
+before you write the log message (@pxref{Committing changes}).
+
+@item cvs-inhibit-copyright-message
+The copyright message that is displayed on startup can be annoying after
+a while. Set this variable to @samp{t} if you want to get rid of it.
+(But don't set this to @samp{t} in the system defaults file - new users
+should see this message at least once).
+
+@item cvs-cvs-diff-flags
+A list of strings to pass as arguments to the @samp{cvs diff} program.
+This is used by @samp{cvs-diff-cvs} (key @kbd{d}, @pxref{Viewing
+differences}). If you prefer the Unidiff format you could add this line
+to your @file{.emacs} file:@refill
+
+@example
+(setq cvs-cvs-diff-flags '("-u"))
+@end example
+
+@item cvs-diff-flags
+Like @samp{cvs-cvs-diff-flags}, but passed to @samp{diff}. This is used
+by @samp{cvs-diff-backup} (key @kbd{b}, @pxref{Viewing differences}).
+
+@item cvs-log-flags
+List of strings to send to @samp{cvs log}. Used by @samp{cvs-log} (key
+@kbd{l}, @pxref{Getting info about files}).
+
+@item cvs-status-flags
+List of strings to send to @samp{cvs status}. Used by @samp{cvs-status}
+(key @kbd{s}, @pxref{Getting info about files}).
+
+@item cvs-auto-remove-handled
+If this variable is set to any non-@samp{nil} value
+@samp{cvs-remove-handled} will be called every time you check in files,
+after the check-in is ready. @xref{Removing handled entries}.@refill
+
+@end table
+@node Future enhancements, Reporting bugs and ideas, Customization, Top
+@comment node-name, next, previous, up
+@chapter Future enhancements
+@cindex Enhancements
+
+Pcl-cvs is still under development and needs a number of enhancements to
+be called complete. Here is my current wish-list for future releases of
+pcl-cvs:
+
+@itemize @bullet
+@item
+Dired support. I have an experimental @file{dired-cvs.el} that works
+together with CVS 1.2. Unfortunately I wrote it on top of a
+non-standard @file{dired.el}, so it must be rewritten.@refill
+
+@item
+It should be possible to run commands such as @samp{cvs log}, @samp{cvs
+status} and @samp{cvs commit} directly from a buffer containing a file,
+instead of having to @samp{cvs-update}. If the directory contains many
+files the @samp{cvs-update} can take quite some time, especially on a
+slow machine.
+@end itemize
+
+
+If you miss something in this wish-list, let me know! I don't promise
+that I will write it, but I will at least try to coordinate the efforts
+of making a good Emacs front end to CVS. See @xref{Reporting bugs and
+ideas} for information about how to reach me.@refill
+
+
+@node Reporting bugs and ideas, Function and Variable Index, Future enhancements, Top
+@comment node-name, next, previous, up
+@chapter Reporting bugs and ideas
+@cindex Reporting bugs and ideas
+@cindex Bugs, how to report them
+@cindex Author, how to reach
+@cindex Email to the author
+
+If you find a bug or misfeature, don't hesitate to tell me! Send email
+to @samp{ceder@@lysator.liu.se}.
+
+If you have ideas for improvements, or if you have written some
+extensions to this package, I would like to hear from you. I hope that
+you find this package useful!
+
+
+@node Function and Variable Index, Concept Index, Reporting bugs and ideas, Top
+@comment node-name, next, previous, up
+@unnumbered Function and Variable Index
+
+@printindex fn
+
+@node Concept Index, Key Index, Function and Variable Index, Top
+@comment node-name, next, previous, up
+@unnumbered Concept Index
+
+@printindex cp
+
+@node Key Index, , Concept Index, Top
+@comment node-name, next, previous, up
+@unnumbered Key Index
+
+@printindex ky
+
+@summarycontents
+@contents
+@bye
diff --git a/gnu/usr.bin/cvs/contrib/rcs-to-cvs b/gnu/usr.bin/cvs/contrib/rcs-to-cvs
new file mode 100644
index 0000000..1a241b9
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/rcs-to-cvs
@@ -0,0 +1,208 @@
+#!/bin/csh
+#
+# rcs-to-cvs,v 1.3 1992/04/10 03:04:25 berliner Exp
+# Contributed by Per Cederqvist <ceder@lysator.liu.se>.
+#
+# Copyright (c) 1989, Brian Berliner
+#
+# You may distribute under the terms of the GNU General Public License
+# as specified in the README file that comes with the CVS 1.0 kit.
+#
+#############################################################################
+# #
+# This script is used to check in sources that previously was under RCS or #
+# no source control system. #
+# #
+# Usage: rcs-to-cvs repository #
+# #
+# The repository is the directory where the sources should #
+# be deposited.
+# #
+# checkin traverses the current directory, ensuring that an #
+# identical directory structure exists in the repository directory. It #
+# then checks the files in in the following manner: #
+# #
+# 1) If the file doesn't yet exist, check it in #
+# as revision 0.1 #
+# #
+# The script also is somewhat verbose in letting the user know what is #
+# going on. It prints a diagnostic when it creates a new file, or updates #
+# a file that has been modified on the trunk. #
+# #
+#############################################################################
+
+set vbose = 0
+set message = ""
+set cvsbin = /usr/gnu/bin
+set rcsbin = /usr/gnu/bin
+set grep = /bin/grep
+set message_file = /usr/tmp/checkin.$$
+set got_one = 0
+
+if ( $#argv < 1 ) then
+ echo "Usage: rcs-to-cvs [-v] [-m message] [-f message_file] repository"
+ exit 1
+endif
+while ( $#argv )
+ switch ( $argv[1] )
+ case -v:
+ set vbose = 1
+ breaksw
+ case -m:
+ shift
+ echo $argv[1] > $message_file
+ set got_one = 1
+ breaksw
+ case -f:
+ shift
+ set message_file = $argv[1]
+ set got_one = 2
+ breaksw
+ default:
+ break
+ endsw
+ shift
+end
+if ( $#argv < 1 ) then
+ echo "Usage: rcs-to-cvs [-v] [-m message] [-f message_file] repository"
+ exit 1
+endif
+set repository = $argv[1]
+shift
+
+if ( ! $?CVSROOT ) then
+ echo "Please set the environmental variable CVSROOT to the root"
+ echo " of the tree you wish to update"
+ exit 1
+endif
+
+if ( $got_one == 0 ) then
+ echo "Please Edit this file to contain the RCS log information" >$message_file
+ echo "to be associated with this file (please remove these lines)">>$message_file
+ if ( $?EDITOR ) then
+ $EDITOR $message_file > /dev/tty
+ else
+ /usr/ucb/vi $message_file > /dev/tty
+ endif
+ set got_one = 1
+endif
+
+umask 22
+
+set update_dir = ${CVSROOT}/${repository}
+if ( -d SCCS ) then
+ echo SCCS files detected!
+ exit 1
+endif
+if ( -d RCS ) then
+ $rcsbin/co RCS/* >& /dev/null
+endif
+foreach name ( * .[a-zA-Z0-9]* )
+ echo $name
+ if ( "$name" == SCCS ) then
+ continue
+ endif
+ if ( "$name" == RCS ) then
+ continue
+ endif
+ if ( $vbose ) then
+ echo "Updating ${repository}/${name}"
+ endif
+ if ( -d "$name" ) then
+ if ( ! -d "${update_dir}/${name}" ) then
+ echo "WARNING: Creating new directory ${repository}/${name}"
+ mkdir "${update_dir}/${name}"
+ if ( $status ) then
+ echo "ERROR: mkdir failed - aborting"
+ exit 1
+ endif
+ endif
+ chdir "$name"
+ if ( $status ) then
+ echo "ERROR: Couldn\'t chdir to "$name" - aborting"
+ exit 1
+ endif
+ if ( $vbose ) then
+ rcs-to-cvs -v -f $message_file "${repository}/${name}"
+ else
+ rcs-to-cvs -f $message_file "${repository}/${name}"
+ endif
+ if ( $status ) then
+ exit 1
+ endif
+ chdir ..
+ else # if not directory
+ if ( ! -f "$name" ) then
+ echo "WARNING: "$name" is neither a regular file"
+ echo " nor a directory - ignored"
+ continue
+ endif
+ set file = "${update_dir}/${name},v"
+ set new = 0
+ set comment = ""
+ grep -s '\$Log.*\$' "${name}"
+ if ( $status == 0 ) then # If $Log keyword
+ set myext = ${name:e}
+ set knownext = 0
+ foreach xx ( "c" "csh" "e" "f" "h" "l" "mac" "me" "mm" "ms" "p" "r" "red" "s" "sh" "sl" "cl" "ml" "el" "tex" "y" "ye" "yr" "" )
+ if ( "${myext}" == "${xx}" ) then
+ set knownext = 1
+ break
+ endif
+ end
+ if ( $knownext == 0 ) then
+ echo For file ${file}:
+ grep '\$Log.*\$' "${name}"
+ echo -n "Please insert a comment leader for file ${name} > "
+ set comment = $<
+ endif
+ endif
+ if ( ! -f "$file" ) then # If not exists in repository
+ if ( ! -f "${update_dir}/Attic/${name},v" ) then
+ echo "WARNING: Creating new file ${repository}/${name}"
+ if ( -f RCS/"${name}",v ) then
+ echo "MSG: Copying old rcs file."
+ cp RCS/"${name}",v "$file"
+ else
+ if ( "${comment}" != "" ) then
+ $rcsbin/rcs -q -i -c"${comment}" -t${message_file} -m'.' "$file"
+ endif
+ $rcsbin/ci -q -u0.1 -t${message_file} -m'.' "$file"
+ if ( $status ) then
+ echo "ERROR: Initial check-in of $file failed - aborting"
+ exit 1
+ endif
+ set new = 1
+ endif
+ else
+ set file = "${update_dir}/Attic/${name},v"
+ echo "WARNING: IGNORED: ${repository}/Attic/${name}"
+ continue
+ endif
+ else # File existed
+ echo ERROR: File exists: Ignored: "$file"
+ continue
+# set headbranch = `sed -n '/^head/p; /^branch/p; 2q' $file`
+# if ( $#headbranch != 2 && $#headbranch != 4 ) then
+# echo "ERROR: corrupted RCS file $file - aborting"
+# endif
+# set head = "$headbranch[2]"
+# set branch = ""
+# if ( $#headbranch == 4 ) then
+# set branch = "$headbranch[4]"
+# endif
+# if ( "$head" == "1.1;" && "$branch" != "1.1.1;" ) then
+# ${rcsbin}/rcsdiff -q -r1.1 $file > /dev/null
+# if ( ! $status ) then
+# set new = 1
+# endif
+# else
+# if ( "$branch" != "1.1.1;" ) then
+# echo -n "WARNING: Updating locally modified file "
+# echo "${repository}/${name}"
+# endif
+# endif
+ endif
+ endif
+end
+if ( $got_one == 1 ) rm $message_file
diff --git a/gnu/usr.bin/cvs/contrib/rcslock.pl b/gnu/usr.bin/cvs/contrib/rcslock.pl
new file mode 100644
index 0000000..db09b4b
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/rcslock.pl
@@ -0,0 +1,234 @@
+#!/usr/bin/perl
+
+# Author: John Rouillard (rouilj@cs.umb.edu)
+# Supported: Yeah right. (Well what do you expect for 2 hours work?)
+# Blame-to: rouilj@cs.umb.edu
+# Complaints to: Anybody except Brian Berliner, he's blameless for
+# this script.
+# Acknowlegements: The base code for this script has been acquired
+# from the log.pl script.
+
+# rcslock.pl - A program to prevent commits when a file to be ckecked
+# in is locked in the repository.
+
+# There are times when you need exclusive access to a file. This
+# often occurs when binaries are checked into the repository, since
+# cvs's (actually rcs's) text based merging mechanism won't work. This
+# script allows you to use the rcs lock mechanism (rcs -l) to make
+# sure that no changes to a repository are able to be committed if
+# those changes would result in a locked file being changed.
+
+# WARNING:
+# This script will work only if locking is set to strict.
+#
+
+# Setup:
+# Add the following line to the commitinfo file:
+
+# ALL /local/location/for/script/lockcheck [options]
+
+# Where ALL is replaced by any suitable regular expression.
+# Options are -v for verbose info, or -d for debugging info.
+# The %s will provide the repository directory name and the names of
+# all changed files.
+
+# Use:
+# When a developer needs exclusive access to a version of a file, s/he
+# should use "rcs -l" in the repository tree to lock the version they
+# are working on. CVS will automagically release the lock when the
+# commit is performed.
+
+# Method:
+# An "rlog -h" is exec'ed to give info on all about to be
+# committed files. This (header) information is parsed to determine
+# if any locks are outstanding and what versions of the file are
+# locked. This filename, version number info is used to index an
+# associative array. All of the files to be committed are checked to
+# see if any locks are outstanding. If locks are outstanding, the
+# version number of the current file (taken from the CVS/Entries
+# subdirectory) is used in the key to determine if that version is
+# locked. If the file being checked in is locked by the person doing
+# the checkin, the commit is allowed, but if the lock is held on that
+# version of a file by another person, the commit is not allowed.
+
+$ext = ",v"; # The extension on your rcs files.
+
+$\="\n"; # I hate having to put \n's at the end of my print statements
+$,=' '; # Spaces should occur between arguments to print when printed
+
+# turn off setgid
+#
+$) = $(;
+
+#
+# parse command line arguments
+#
+require 'getopts.pl';
+
+&Getopts("vd"); # verbose or debugging
+
+# Verbose is useful when debugging
+$opt_v = $opt_d if defined $opt_d;
+
+# $files[0] is really the name of the subdirectory.
+# @files = split(/ /,$ARGV[0]);
+@files = @ARGV[0..$#ARGV];
+$cvsroot = $ENV{'CVSROOT'};
+
+#
+# get login name
+#
+$login = getlogin || (getpwuid($<))[0] || "nobody";
+
+#
+# save the current directory since we have to return here to parse the
+# CVS/Entries file if a lock is found.
+#
+$pwd = `/bin/pwd`;
+chop $pwd;
+
+print "Starting directory is $pwd" if defined $opt_d ;
+
+#
+# cd to the repository directory and check on the files.
+#
+print "Checking directory ", $files[0] if defined $opt_v ;
+
+if ( $files[0] =~ /^\// )
+{
+ print "Directory path is $files[0]" if defined $opt_d ;
+ chdir $files[0] || die "Can't change to repository directory $files[0]" ;
+}
+else
+{
+ print "Directory path is $cvsroot/$files[0]" if defined $opt_d ;
+ chdir ($cvsroot . "/" . $files[0]) ||
+ die "Can't change to repository directory $files[0] in $cvsroot" ;
+}
+
+
+# Open the rlog process and apss all of the file names to that one
+# process to cut down on exec overhead. This may backfire if there
+# are too many files for the system buffer to handle, but if there are
+# that many files, chances are that the cvs repository is not set up
+# cleanly.
+
+print "opening rlog -h @files[1..$#files] |" if defined $opt_d;
+
+open( RLOG, "rlog -h @files[1..$#files] |") || die "Can't run rlog command" ;
+
+# Create the locks associative array. The elements in the array are
+# of two types:
+#
+# The name of the RCS file with a value of the total number of locks found
+# for that file,
+# or
+#
+# The name of the rcs file concatenated with the version number of the lock.
+# The value of this element is the name of the locker.
+
+# The regular expressions used to split the rcs info may have to be changed.
+# The current ones work for rcs 5.6.
+
+$lock = 0;
+
+while (<RLOG>)
+{
+ chop;
+ next if /^$/; # ditch blank lines
+
+ if ( $_ =~ /^RCS file: (.*)$/ )
+ {
+ $curfile = $1;
+ next;
+ }
+
+ if ( $_ =~ /^locks: strict$/ )
+ {
+ $lock = 1 ;
+ next;
+ }
+
+ if ( $lock )
+ {
+ # access list: is the line immediately following the list of locks.
+ if ( /^access list:/ )
+ { # we are done getting lock info for this file.
+ $lock = 0;
+ }
+ else
+ { # We are accumulating lock info.
+
+ # increment the lock count
+ $locks{$curfile}++;
+ # save the info on the version that is locked. $2 is the
+ # version number $1 is the name of the locker.
+ $locks{"$curfile" . "$2"} = $1
+ if /[ ]*([a-zA-Z._]*): ([0-9.]*)$/;
+
+ print "lock by $1 found on $curfile version $2" if defined $opt_d;
+
+ }
+ }
+}
+
+# Lets go back to the starting directory and see if any locked files
+# are ones we are interested in.
+
+chdir $pwd;
+
+# fo all of the file names (remember $files[0] is the directory name
+foreach $i (@files[1..$#files])
+{
+ if ( defined $locks{$i . $ext} )
+ { # well the file has at least one lock outstanding
+
+ # find the base version number of our file
+ &parse_cvs_entry($i,*entry);
+
+ # is our version of this file locked?
+ if ( defined $locks{$i . $ext . $entry{"version"}} )
+ { # if so, it is by us?
+ if ( $login ne ($by = $locks{$i . $ext . $entry{"version"}}) )
+ {# crud somebody else has it locked.
+ $outstanding_lock++ ;
+ print "$by has file $i locked for version " , $entry{"version"};
+ }
+ else
+ { # yeah I have it locked.
+ print "You have a lock on file $i for version " , $entry{"version"}
+ if defined $opt_v;
+ }
+ }
+ }
+}
+
+exit $outstanding_lock;
+
+
+### End of main program
+
+sub parse_cvs_entry
+{ # a very simple minded hack at parsing an entries file.
+local ( $file, *entry ) = @_;
+local ( @pp );
+
+
+open(ENTRIES, "< CVS/Entries") || die "Can't open entries file";
+
+while (<ENTRIES>)
+ {
+ if ( $_ =~ /^\/$file\// )
+ {
+ @pp = split('/');
+
+ $entry{"name"} = $pp[1];
+ $entry{"version"} = $pp[2];
+ $entry{"dates"} = $pp[3];
+ $entry{"name"} = $pp[4];
+ $entry{"name"} = $pp[5];
+ $entry{"sticky"} = $pp[6];
+ return;
+ }
+ }
+}
diff --git a/gnu/usr.bin/cvs/contrib/sccs2rcs b/gnu/usr.bin/cvs/contrib/sccs2rcs
new file mode 100644
index 0000000..a208645
--- /dev/null
+++ b/gnu/usr.bin/cvs/contrib/sccs2rcs
@@ -0,0 +1,277 @@
+#!/bin/csh -f
+#
+# Sccs2rcs is a script to convert an existing SCCS
+# history into an RCS history without losing any of
+# the information contained therein.
+# It has been tested under the following OS's:
+# SunOS 3.5, 4.0.3, 4.1
+# Ultrix-32 2.0, 3.1
+#
+# Things to note:
+# + It will NOT delete or alter your ./SCCS history under any circumstances.
+#
+# + Run in a directory where ./SCCS exists and where you can
+# create ./RCS
+#
+# + /usr/local/bin is put in front of the default path.
+# (SCCS under Ultrix is set-uid sccs, bad bad bad, so
+# /usr/local/bin/sccs here fixes that)
+#
+# + Date, time, author, comments, branches, are all preserved.
+#
+# + If a command fails somewhere in the middle, it bombs with
+# a message -- remove what it's done so far and try again.
+# "rm -rf RCS; sccs unedit `sccs tell`; sccs clean"
+# There is no recovery and exit is far from graceful.
+# If a particular module is hanging you up, consider
+# doing it separately; move it from the current area so that
+# the next run will have a better chance or working.
+# Also (for the brave only) you might consider hacking
+# the s-file for simpler problems: I've successfully changed
+# the date of a delta to be in sync, then run "sccs admin -z"
+# on the thing.
+#
+# + After everything finishes, ./SCCS will be moved to ./old-SCCS.
+#
+# This file may be copied, processed, hacked, mutilated, and
+# even destroyed as long as you don't tell anyone you wrote it.
+#
+# Ken Cox
+# Viewlogic Systems, Inc.
+# kenstir@viewlogic.com
+# ...!harvard!cg-atla!viewlog!kenstir
+#
+# Various hacks made by Brian Berliner before inclusion in CVS contrib area.
+#
+# sccs2rcs,v 1.1 1992/04/10 03:04:26 berliner Exp
+
+
+#we'll assume the user set up the path correctly
+# for the Pmax, /usr/ucb/sccs is suid sccs, what a pain
+# /usr/local/bin/sccs should override /usr/ucb/sccs there
+set path = (/usr/local/bin $path)
+
+
+############################################################
+# Error checking
+#
+if (! -w .) then
+ echo "Error: ./ not writeable by you."
+ exit 1
+endif
+if (! -d SCCS) then
+ echo "Error: ./SCCS directory not found."
+ exit 1
+endif
+set edits = (`sccs tell`)
+if ($#edits) then
+ echo "Error: $#edits file(s) out for edit...clean up before converting."
+ exit 1
+endif
+if (-d RCS) then
+ echo "Warning: RCS directory exists"
+ if (`ls -a RCS | wc -l` > 2) then
+ echo "Error: RCS directory not empty
+ exit 1
+ endif
+else
+ mkdir RCS
+endif
+
+sccs clean
+
+set logfile = /tmp/sccs2rcs_$$_log
+rm -f $logfile
+set tmpfile = /tmp/sccs2rcs_$$_tmp
+rm -f $tmpfile
+set emptyfile = /tmp/sccs2rcs_$$_empty
+echo -n "" > $emptyfile
+set initialfile = /tmp/sccs2rcs_$$_init
+echo "Initial revision" > $initialfile
+set sedfile = /tmp/sccs2rcs_$$_sed
+rm -f $sedfile
+set revfile = /tmp/sccs2rcs_$$_rev
+rm -f $revfile
+
+# the quotes surround the dollar signs to fool RCS when I check in this script
+set sccs_keywords = (\
+ '%W%[ ]*%G%'\
+ '%W%[ ]*%E%'\
+ '%W%'\
+ '%Z%%M%[ ]*%I%[ ]*%G%'\
+ '%Z%%M%[ ]*%I%[ ]*%E%'\
+ '%M%[ ]*%I%[ ]*%G%'\
+ '%M%[ ]*%I%[ ]*%E%'\
+ '%M%'\
+ '%I%'\
+ '%G%'\
+ '%E%'\
+ '%U%')
+set rcs_keywords = (\
+ '$'Id'$'\
+ '$'Id'$'\
+ '$'Id'$'\
+ '$'SunId'$'\
+ '$'SunId'$'\
+ '$'Id'$'\
+ '$'Id'$'\
+ '$'RCSfile'$'\
+ '$'Revision'$'\
+ '$'Date'$'\
+ '$'Date'$'\
+ '')
+
+
+############################################################
+# Get some answers from user
+#
+echo ""
+echo "Do you want to be prompted for a description of each"
+echo "file as it is checked in to RCS initially?"
+echo -n "(y=prompt for description, n=null description) [y] ?"
+set ans = $<
+if ((_$ans == _) || (_$ans == _y) || (_$ans == _Y)) then
+ set nodesc = 0
+else
+ set nodesc = 1
+endif
+echo ""
+echo "The default keyword substitutions are as follows and are"
+echo "applied in the order specified:"
+set i = 1
+while ($i <= $#sccs_keywords)
+# echo ' '\"$sccs_keywords[$i]\"' ==> '\"$rcs_keywords[$i]\"
+ echo " $sccs_keywords[$i] ==> $rcs_keywords[$i]"
+ @ i = $i + 1
+end
+echo ""
+echo -n "Do you want to change them [n] ?"
+set ans = $<
+if ((_$ans != _) && (_$ans != _n) && (_$ans != _N)) then
+ echo "You can't always get what you want."
+ echo "Edit this script file and change the variables:"
+ echo ' $sccs_keywords'
+ echo ' $rcs_keywords'
+else
+ echo "good idea."
+endif
+
+# create the sed script
+set i = 1
+while ($i <= $#sccs_keywords)
+ echo "s,$sccs_keywords[$i],$rcs_keywords[$i],g" >> $sedfile
+ @ i = $i + 1
+end
+
+onintr ERROR
+
+############################################################
+# Loop over every s-file in SCCS dir
+#
+foreach sfile (SCCS/s.*)
+ # get rid of the "s." at the beginning of the name
+ set file = `echo $sfile:t | sed -e "s/^..//"`
+
+ # work on each rev of that file in ascending order
+ set firsttime = 1
+ sccs prs $file | grep "^D " | awk '{print $2}' | sed -e 's/\./ /g' | sort -n -u +0 +1 +2 +3 +4 +5 +6 +7 +8 | sed -e 's/ /./g' > $revfile
+ foreach rev (`cat $revfile`)
+ if ($status != 0) goto ERROR
+
+ # get file into current dir and get stats
+ set date = `sccs prs -r$rev $file | grep "^D " | awk '{printf("19%s %s", $3, $4); exit}'`
+ set author = `sccs prs -r$rev $file | grep "^D " | awk '{print $5; exit}'`
+ echo ""
+ echo "==> file $file, rev=$rev, date=$date, author=$author"
+ sccs edit -r$rev $file >>& $logfile
+ if ($status != 0) goto ERROR
+ echo checked out of SCCS
+
+ # add RCS keywords in place of SCCS keywords
+ sed -f $sedfile $file > $tmpfile
+ if ($status != 0) goto ERROR
+ echo performed keyword substitutions
+ cp $tmpfile $file
+
+ # check file into RCS
+ if ($firsttime) then
+ set firsttime = 0
+ if ($nodesc) then
+ echo about to do ci
+ echo ci -f -r$rev -d"$date" -w$author -t$emptyfile $file
+ ci -f -r$rev -d"$date" -w$author -t$emptyfile $file < $initialfile >>& $logfile
+ if ($status != 0) goto ERROR
+ echo initial rev checked into RCS without description
+ else
+ echo ""
+ echo Enter a brief description of the file $file \(end w/ Ctrl-D\):
+ cat > $tmpfile
+ ci -f -r$rev -d"$date" -w$author -t$tmpfile $file < $initialfile >>& $logfile
+ if ($status != 0) goto ERROR
+ echo initial rev checked into RCS
+ endif
+ else
+ # get RCS lock
+ set lckrev = `echo $rev | sed -e 's/\.[0-9]*$//'`
+ if ("$lckrev" =~ [0-9]*.*) then
+ # need to lock the brach -- it is OK if the lock fails
+ rcs -l$lckrev $file >>& $logfile
+ else
+ # need to lock the trunk -- must succeed
+ rcs -l $file >>& $logfile
+ if ($status != 0) goto ERROR
+ endif
+ echo got lock
+ sccs prs -r$rev $file | grep "." > $tmpfile
+ # it's OK if grep fails here and gives status == 1
+ # put the delta message in $tmpfile
+ ed $tmpfile >>& $logfile <<EOF
+/COMMENTS
+1,.d
+w
+q
+EOF
+ ci -f -r$rev -d"$date" -w$author $file < $tmpfile >>& $logfile
+ if ($status != 0) goto ERROR
+ echo checked into RCS
+ endif
+ sccs unedit $file >>& $logfile
+ if ($status != 0) goto ERROR
+ end
+ rm -f $file
+end
+
+
+############################################################
+# Clean up
+#
+echo cleaning up...
+mv SCCS old-SCCS
+rm -f $tmpfile $emptyfile $initialfile $sedfile
+echo ===================================================
+echo " Conversion Completed Successfully"
+echo ""
+echo " SCCS history now in old-SCCS/"
+echo ===================================================
+set exitval = 0
+goto cleanup
+
+ERROR:
+foreach f (`sccs tell`)
+ sccs unedit $f
+end
+echo ""
+echo ""
+echo Danger\! Danger\!
+echo Some command exited with a non-zero exit status.
+echo Log file exists in $logfile.
+echo ""
+echo Incomplete history in ./RCS -- remove it
+echo Original unchanged history in ./SCCS
+set exitval = 1
+
+cleanup:
+# leave log file
+rm -f $tmpfile $emptyfile $initialfile $sedfile $revfile
+
+exit $exitval
diff --git a/gnu/usr.bin/cvs/cvs/Makefile b/gnu/usr.bin/cvs/cvs/Makefile
new file mode 100644
index 0000000..d506fd1
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/Makefile
@@ -0,0 +1,33 @@
+.if !defined(FREEBSD_DEVELOPER)
+PROG = cvs
+.else
+PROG = ncvs
+.endif
+
+CFLAGS += -I${.CURDIR}/../lib \
+ -DDIRENT -DSTDC_HEADERS -DPOSIX -DBROKEN_SIGISMEMBER \
+ -DFTIME_MISSING -DHAVE_TIMEZONE -DUTIME_NULL_MISSING
+DPADD+= ${LIBCVS}
+LDADD+= -lcvs
+
+.if defined(FREEBSD_DEVELOPER)
+CFLAGS+= -DFREEBSD_DEVELOPER
+BINGRP= ncvs
+#BINMODE=2555
+.endif
+
+SRCS = add.c admin.c checkin.c checkout.c classify.c commit.c \
+create_adm.c diff.c entries.c find_names.c history.c ignore.c \
+import.c lock.c log.c logmsg.c main.c rcs.c modules.c \
+no_diff.c parseinfo.c patch.c recurse.c release.c remove.c repos.c rtag.c \
+status.c tag.c update.c vers_ts.c version.c
+
+MAN1= cvs.1
+MAN5= cvs.5
+
+check:
+ @echo `pwd` ${.CURDIR}
+
+.include "../../Makefile.inc"
+.include "../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/cvs/cvs/add.c b/gnu/usr.bin/cvs/cvs/add.c
new file mode 100644
index 0000000..b4925bf
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/add.c
@@ -0,0 +1,447 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Add
+ *
+ * Adds a file or directory to the RCS source repository. For a file,
+ * the entry is marked as "needing to be added" in the user's own CVS
+ * directory, and really added to the repository when it is committed.
+ * For a directory, it is added at the appropriate place in the source
+ * repository and a CVS directory is generated within the directory.
+ *
+ * The -m option is currently the only supported option. Some may wish to
+ * supply standard "rcs" options here, but I've found that this causes more
+ * trouble than anything else.
+ *
+ * The user files or directories must already exist. For a directory, it must
+ * not already have a CVS file in it.
+ *
+ * An "add" on a file that has been "remove"d but not committed will cause the
+ * file to be resurrected.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)add.c 1.46 92/04/03";
+#endif
+
+#if __STDC__
+static int add_directory (char *repository, char *dir);
+static int build_entry (char *repository, char *user, char *options,
+ char *message, List * entries);
+#else
+static int add_directory ();
+static int build_entry ();
+#endif /* __STDC__ */
+
+static char *add_usage[] =
+{
+ "Usage: %s %s [-k rcs-kflag] [-m message] files...\n",
+ "\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n",
+ "\t-m\tUse \"message\" for the creation log.\n",
+ NULL
+};
+
+int
+add (argc, argv)
+ int argc;
+ char *argv[];
+{
+ char message[MAXMESGLEN];
+ char *user;
+ int i;
+ char *repository;
+ int c;
+ int err = 0;
+ int added_files = 0;
+ char *options = NULL;
+ List *entries;
+ Vers_TS *vers;
+
+ if (argc == 1 || argc == -1)
+ usage (add_usage);
+
+ /* parse args */
+ message[0] = '\0';
+ optind = 1;
+ while ((c = gnu_getopt (argc, argv, "k:m:")) != -1)
+ {
+ switch (c)
+ {
+ case 'k':
+ if (options)
+ free (options);
+ options = RCS_check_kflag (optarg);
+ break;
+
+ case 'm':
+ if (strlen (optarg) >= sizeof (message))
+ {
+ error (0, 0, "warning: message too long; truncated!");
+ (void) strncpy (message, optarg, sizeof (message));
+ message[sizeof (message) - 1] = '\0';
+ }
+ else
+ (void) strcpy (message, optarg);
+ break;
+ case '?':
+ default:
+ usage (add_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc <= 0)
+ usage (add_usage);
+
+ /* find the repository associated with our current dir */
+ repository = Name_Repository ((char *) NULL, (char *) NULL);
+ entries = ParseEntries (0);
+
+ /* walk the arg list adding files/dirs */
+ for (i = 0; i < argc; i++)
+ {
+ int begin_err = err;
+
+ user = argv[i];
+ if (index (user, '/') != NULL)
+ {
+ error (0, 0,
+ "cannot add files with '/' in their name; %s not added", user);
+ err++;
+ continue;
+ }
+
+ vers = Version_TS (repository, options, (char *) NULL, (char *) NULL,
+ user, 0, 0, entries, (List *) NULL);
+ if (vers->vn_user == NULL)
+ {
+ /* No entry available, ts_rcs is invalid */
+ if (vers->vn_rcs == NULL)
+ {
+ /* There is no RCS file either */
+ if (vers->ts_user == NULL)
+ {
+ /* There is no user file either */
+ error (0, 0, "nothing known about %s", user);
+ err++;
+ }
+ else if (!isdir (user))
+ {
+ /*
+ * See if a directory exists in the repository with
+ * the same name. If so, blow this request off.
+ */
+ char dname[PATH_MAX];
+ (void) sprintf (dname, "%s/%s", repository, user);
+ if (isdir (dname))
+ {
+ error (0, 0,
+ "cannot add file `%s' since the directory",
+ user);
+ error (0, 0, "`%s' already exists in the repository",
+ dname);
+ error (1, 0, "illegal filename overlap");
+ }
+
+ /* There is a user file, so build the entry for it */
+ if (build_entry (repository, user, vers->options,
+ message, entries) != 0)
+ err++;
+ else if (!quiet)
+ {
+ added_files++;
+ error (0, 0, "scheduling file `%s' for addition",
+ user);
+ }
+ }
+ }
+ else
+ {
+
+ /*
+ * There is an RCS file already, so somebody else must've
+ * added it
+ */
+ error (0, 0, "%s added independently by second party", user);
+ err++;
+ }
+ }
+ else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
+ {
+
+ /*
+ * An entry for a new-born file, ts_rcs is dummy, but that is
+ * inappropriate here
+ */
+ error (0, 0, "%s has already been entered", user);
+ err++;
+ }
+ else if (vers->vn_user[0] == '-')
+ {
+ /* An entry for a removed file, ts_rcs is invalid */
+ if (vers->ts_user == NULL)
+ {
+ /* There is no user file (as it should be) */
+ if (vers->vn_rcs == NULL)
+ {
+
+ /*
+ * There is no RCS file, so somebody else must've removed
+ * it from under us
+ */
+ error (0, 0,
+ "cannot resurrect %s; RCS file removed by second party", user);
+ err++;
+ }
+ else
+ {
+
+ /*
+ * There is an RCS file, so remove the "-" from the
+ * version number and restore the file
+ */
+ char *tmp = xmalloc (strlen (user) + 50);
+
+ (void) strcpy (tmp, vers->vn_user + 1);
+ (void) strcpy (vers->vn_user, tmp);
+ (void) sprintf (tmp, "Resurrected %s", user);
+ Register (entries, user, vers->vn_user, tmp, vers->options,
+ vers->tag, vers->date);
+ free (tmp);
+
+ /* XXX - bugs here; this really resurrect the head */
+ if (update (2, argv + i - 1) == 0)
+ {
+ error (0, 0, "%s, version %s, resurrected", user,
+ vers->vn_user);
+ }
+ else
+ {
+ error (0, 0, "could not resurrect %s", user);
+ err++;
+ }
+ }
+ }
+ else
+ {
+ /* The user file shouldn't be there */
+ error (0, 0, "%s should be removed and is still there (or is back again)", user);
+ err++;
+ }
+ }
+ else
+ {
+ /* A normal entry, ts_rcs is valid, so it must already be there */
+ error (0, 0, "%s already exists, with version number %s", user,
+ vers->vn_user);
+ err++;
+ }
+ freevers_ts (&vers);
+
+ /* passed all the checks. Go ahead and add it if its a directory */
+ if (begin_err == err && isdir (user))
+ {
+ err += add_directory (repository, user);
+ continue;
+ }
+ }
+ if (added_files)
+ error (0, 0, "use 'cvs commit' to add %s permanently",
+ (added_files == 1) ? "this file" : "these files");
+ dellist (&entries);
+ return (err);
+}
+
+/*
+ * The specified user file is really a directory. So, let's make sure that
+ * it is created in the RCS source repository, and that the user's directory
+ * is updated to include a CVS directory.
+ *
+ * Returns 1 on failure, 0 on success.
+ */
+static int
+add_directory (repository, dir)
+ char *repository;
+ char *dir;
+{
+ char cwd[PATH_MAX], rcsdir[PATH_MAX];
+ char message[PATH_MAX + 100];
+ char *tag, *date;
+
+ if (index (dir, '/') != NULL)
+ {
+ error (0, 0,
+ "directory %s not added; must be a direct sub-directory", dir);
+ return (1);
+ }
+ if (strcmp (dir, CVSADM) == 0 || strcmp (dir, OCVSADM) == 0)
+ {
+ error (0, 0, "cannot add a `%s' or a `%s' directory", CVSADM, OCVSADM);
+ return (1);
+ }
+
+ /* before we do anything else, see if we have any per-directory tags */
+ ParseTag (&tag, &date);
+
+ /* now, remember where we were, so we can get back */
+ if (getwd (cwd) == NULL)
+ {
+ error (0, 0, "cannot get working directory: %s", cwd);
+ return (1);
+ }
+ if (chdir (dir) < 0)
+ {
+ error (0, errno, "cannot chdir to %s", dir);
+ return (1);
+ }
+ if (isfile (CVSADM) || isfile (OCVSADM))
+ {
+ error (0, 0,
+ "%s/%s (or %s/%s) already exists", dir, CVSADM, dir, OCVSADM);
+ goto out;
+ }
+
+ (void) sprintf (rcsdir, "%s/%s", repository, dir);
+ if (isfile (rcsdir) && !isdir (rcsdir))
+ {
+ error (0, 0, "%s is not a directory; %s not added", rcsdir, dir);
+ goto out;
+ }
+
+ /* setup the log message */
+ (void) sprintf (message, "Directory %s added to the repository\n", rcsdir);
+ if (tag)
+ {
+ (void) strcat (message, "--> Using per-directory sticky tag `");
+ (void) strcat (message, tag);
+ (void) strcat (message, "'\n");
+ }
+ if (date)
+ {
+ (void) strcat (message, "--> Using per-directory sticky date `");
+ (void) strcat (message, date);
+ (void) strcat (message, "'\n");
+ }
+
+ if (!isdir (rcsdir))
+ {
+ mode_t omask;
+ char line[MAXLINELEN];
+ Node *p;
+ List *ulist;
+
+ (void) printf ("Add directory %s to the repository (y/n) [n] ? ",
+ rcsdir);
+ (void) fflush (stdout);
+ clearerr (stdin);
+ if (fgets (line, sizeof (line), stdin) == NULL ||
+ (line[0] != 'y' && line[0] != 'Y'))
+ {
+ error (0, 0, "directory %s not added", rcsdir);
+ goto out;
+ }
+ omask = umask (2);
+ if (mkdir (rcsdir, 0777) < 0)
+ {
+ error (0, errno, "cannot mkdir %s", rcsdir);
+ (void) umask ((int) omask);
+ goto out;
+ }
+ (void) umask ((int) omask);
+
+ /*
+ * Set up an update list with a single title node for Update_Logfile
+ */
+ ulist = getlist ();
+ p = getnode ();
+ p->type = UPDATE;
+ p->delproc = update_delproc;
+ p->key = xstrdup ("- New directory");
+ p->data = (char *) T_TITLE;
+ (void) addnode (ulist, p);
+ Update_Logfile (rcsdir, message, (char *) NULL, (FILE *) NULL, ulist);
+ dellist (&ulist);
+ }
+
+ Create_Admin (".", rcsdir, tag, date);
+ if (tag)
+ free (tag);
+ if (date)
+ free (date);
+
+ (void) printf ("%s", message);
+out:
+ if (chdir (cwd) < 0)
+ error (1, errno, "cannot chdir to %s", cwd);
+ return (0);
+}
+
+/*
+ * Builds an entry for a new file and sets up "CVS/file",[pt] by
+ * interrogating the user. Returns non-zero on error.
+ */
+static int
+build_entry (repository, user, options, message, entries)
+ char *repository;
+ char *user;
+ char *options;
+ char *message;
+ List *entries;
+{
+ char fname[PATH_MAX];
+ char line[MAXLINELEN];
+ FILE *fp;
+
+ /*
+ * There may be an old file with the same name in the Attic! This is,
+ * perhaps, an awkward place to check for this, but other places are
+ * equally awkward.
+ */
+ (void) sprintf (fname, "%s/%s/%s%s", repository, CVSATTIC, user, RCSEXT);
+ if (isreadable (fname))
+ {
+ error (0, 0, "there is an old file %s already in %s/%s", user,
+ repository, CVSATTIC);
+ return (1);
+ }
+
+ if (noexec)
+ return (0);
+
+ /*
+ * The options for the "add" command are store in the file CVS/user,p
+ */
+ (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_OPT);
+ fp = open_file (fname, "w+");
+ if (fclose (fp) == EOF)
+ error(1, errno, "cannot close %s", fname);
+
+ /*
+ * And the requested log is read directly from the user and stored in the
+ * file user,t. If the "message" argument is set, use it as the
+ * initial creation log (which typically describes the file).
+ */
+ (void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_LOG);
+ fp = open_file (fname, "w+");
+ if (*message && fputs (message, fp) == EOF)
+ error (1, errno, "cannot write to %s", fname);
+ if (fclose(fp) == EOF)
+ error(1, errno, "cannot close %s", fname);
+
+ /*
+ * Create the entry now, since this allows the user to interrupt us above
+ * without needing to clean anything up (well, we could clean up the ,p
+ * and ,t files, but who cares).
+ */
+ (void) sprintf (line, "Initial %s", user);
+ Register (entries, user, "0", line, options, (char *) 0, (char *) 0);
+ return (0);
+}
diff --git a/gnu/usr.bin/cvs/cvs/admin.c b/gnu/usr.bin/cvs/cvs/admin.c
new file mode 100644
index 0000000..91d3929
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/admin.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Administration
+ *
+ * For now, this is basically a front end for rcs. All options are passed
+ * directly on.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)admin.c 1.17 92/03/31";
+#endif
+
+#if __STDC__
+static Dtype admin_dirproc (char *dir, char *repos, char *update_dir);
+static int admin_fileproc (char *file, char *update_dir,
+ char *repository, List *entries,
+ List *srcfiles);
+#else
+static int admin_fileproc ();
+static Dtype admin_dirproc ();
+#endif /* __STDC__ */
+
+static char *admin_usage[] =
+{
+ "Usage: %s %s rcs-options files...\n",
+ NULL
+};
+
+static int ac;
+static char **av;
+
+int
+admin (argc, argv)
+ int argc;
+ char *argv[];
+{
+ int err;
+
+ if (argc <= 1)
+ usage (admin_usage);
+
+ /* skip all optional arguments to see if we have any file names */
+ for (ac = 1; ac < argc; ac++)
+ if (argv[ac][0] != '-')
+ break;
+ argc -= ac;
+ av = argv + 1;
+ argv += ac;
+ ac--;
+ if (ac == 0 || argc == 0)
+ usage (admin_usage);
+
+ /* start the recursion processor */
+ err = start_recursion (admin_fileproc, (int (*) ()) NULL, admin_dirproc,
+ (int (*) ()) NULL, argc, argv, 0,
+ W_LOCAL, 0, 1, (char *) NULL, 1);
+ return (err);
+}
+
+/*
+ * Called to run "rcs" on a particular file.
+ */
+/* ARGSUSED */
+static int
+admin_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ Vers_TS *vers;
+ char *version;
+ char **argv;
+ int argc;
+ int retcode = 0;
+
+ vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL,
+ file, 0, 0, entries, srcfiles);
+
+ version = vers->vn_user;
+ if (version == NULL)
+ return (0);
+ else if (strcmp (version, "0") == 0)
+ {
+ error (0, 0, "cannot admin newly added file `%s'", file);
+ return (0);
+ }
+
+ run_setup ("%s%s", Rcsbin, RCS);
+ for (argc = ac, argv = av; argc; argc--, argv++)
+ run_arg (*argv);
+ run_arg (vers->srcfile->path);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
+ {
+ if (!quiet)
+ error (0, retcode == -1 ? errno : 0,
+ "%s failed for `%s'", RCS, file);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Print a warm fuzzy message
+ */
+/* ARGSUSED */
+static Dtype
+admin_dirproc (dir, repos, update_dir)
+ char *dir;
+ char *repos;
+ char *update_dir;
+{
+ if (!quiet)
+ error (0, 0, "Administrating %s", update_dir);
+ return (R_PROCESS);
+}
diff --git a/gnu/usr.bin/cvs/cvs/checkin.c b/gnu/usr.bin/cvs/cvs/checkin.c
new file mode 100644
index 0000000..44b733e
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/checkin.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Check In
+ *
+ * Does a very careful checkin of the file "user", and tries not to spoil its
+ * modification time (to avoid needless recompilations). When RCS ID keywords
+ * get expanded on checkout, however, the modification time is updated and
+ * there is no good way to get around this.
+ *
+ * Returns non-zero on error.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)checkin.c 1.40 92/03/31";
+#endif
+
+int
+Checkin (type, file, repository, rcs, rev, tag, message, entries)
+ int type;
+ char *file;
+ char *repository;
+ char *rcs;
+ char *rev;
+ char *tag;
+ char *message;
+ List *entries;
+{
+ char fname[PATH_MAX];
+ Vers_TS *vers;
+
+ (void) printf ("Checking in %s;\n", file);
+ (void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, file);
+
+ /*
+ * Move the user file to a backup file, so as to preserve its
+ * modification times, then place a copy back in the original file name
+ * for the checkin and checkout.
+ */
+ if (!noexec)
+ copy_file (file, fname);
+
+ run_setup ("%s%s -f %s%s", Rcsbin, RCS_CI,
+ rev ? "-r" : "", rev ? rev : "");
+ run_args ("-m%s", message);
+ run_arg (rcs);
+
+ switch (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL))
+ {
+ case 0: /* everything normal */
+
+ /*
+ * The checkin succeeded, so now check the new file back out and
+ * see if it matches exactly with the one we checked in. If it
+ * does, just move the original user file back, thus preserving
+ * the modes; otherwise, we have no recourse but to leave the
+ * newly checkout file as the user file and remove the old
+ * original user file.
+ */
+
+ /* XXX - make sure -k options are used on the co; and tag/date? */
+#ifdef FREEBSD_DEVELOPER
+ run_setup ("%s%s -q %s%s %s", Rcsbin, RCS_CO,
+ rev ? "-r" : "", rev ? rev : "",
+ freebsd ? "-KeAuthor,Date,Header,Id,Locker,Log,"
+ "RCSfile,Revision,Source,State -KiFreeBSD" : "");
+#else
+ run_setup ("%s%s -q %s%s", Rcsbin, RCS_CO,
+ rev ? "-r" : "", rev ? rev : "");
+#endif /* FREEBSD_DEVELOPER */
+ run_arg (rcs);
+ (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ xchmod (file, 1);
+ if (xcmp (file, fname) == 0)
+ rename_file (fname, file);
+ else
+ (void) unlink_file (fname);
+
+ /*
+ * If we want read-only files, muck the permissions here, before
+ * getting the file time-stamp.
+ */
+ if (cvswrite == FALSE)
+ xchmod (file, 0);
+
+ /* for added files with symbolic tags, need to add the tag too */
+ if (type == 'A' && tag && !isdigit (*tag))
+ {
+ run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, tag, rev);
+ run_arg (rcs);
+ (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ }
+
+ /* re-register with the new data */
+ vers = Version_TS (repository, (char *) NULL, tag, (char *) NULL,
+ file, 1, 1, entries, (List *) NULL);
+ if (strcmp (vers->options, "-V4") == 0)
+ vers->options[0] = '\0';
+ Register (entries, file, vers->vn_rcs, vers->ts_user, vers->options,
+ vers->tag, vers->date);
+ history_write (type, (char *) 0, vers->vn_rcs, file, repository);
+ freevers_ts (&vers);
+ break;
+
+ case -1: /* fork failed */
+ if (!noexec)
+ error (1, errno, "could not check in %s -- fork failed", file);
+ return (1);
+
+ default: /* ci failed */
+
+ /*
+ * The checkin failed, for some unknown reason, so we restore the
+ * original user file, print an error, and return an error
+ */
+ if (!noexec)
+ {
+ rename_file (fname, file);
+ error (0, 0, "could not check in %s", file);
+ }
+ return (1);
+ }
+
+ /*
+ * When checking in a specific revision, we may have locked the wrong
+ * branch, so to be sure, we do an extra unlock here before
+ * returning.
+ */
+ if (rev)
+ {
+ run_setup ("%s%s -q -u", Rcsbin, RCS);
+ run_arg (rcs);
+ (void) run_exec (RUN_TTY, RUN_TTY, DEVNULL, RUN_NORMAL);
+ }
+ return (0);
+}
diff --git a/gnu/usr.bin/cvs/cvs/checkout.c b/gnu/usr.bin/cvs/cvs/checkout.c
new file mode 100644
index 0000000..e7bc608
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/checkout.c
@@ -0,0 +1,730 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Create Version
+ *
+ * "checkout" creates a "version" of an RCS repository. This version is owned
+ * totally by the user and is actually an independent copy, to be dealt with
+ * as seen fit. Once "checkout" has been called in a given directory, it
+ * never needs to be called again. The user can keep up-to-date by calling
+ * "update" when he feels like it; this will supply him with a merge of his
+ * own modifications and the changes made in the RCS original. See "update"
+ * for details.
+ *
+ * "checkout" can be given a list of directories or files to be updated and in
+ * the case of a directory, will recursivley create any sub-directories that
+ * exist in the repository.
+ *
+ * When the user is satisfied with his own modifications, the present version
+ * can be committed by "commit"; this keeps the present version in tact,
+ * usually.
+ *
+ * The call is cvs checkout [options] <module-name>...
+ *
+ * "checkout" creates a directory ./CVS, in which it keeps its administration,
+ * in two files, Repository and Entries. The first contains the name of the
+ * repository. The second contains one line for each registered file,
+ * consisting of the version number it derives from, its time stamp at
+ * derivation time and its name. Both files are normal files and can be
+ * edited by the user, if necessary (when the repository is moved, e.g.)
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)checkout.c 1.67 92/04/10";
+#endif
+
+#if __STDC__
+static char *findslash (char *start, char *p);
+static int build_dirs_and_chdir (char *dir, char *prepath, char *realdir,
+ int sticky);
+static int checkout_proc (int *pargc, char *argv[], char *where,
+ char *mwhere, char *mfile, int shorten,
+ int local_specified, char *omodule,
+ char *msg);
+#else
+static int checkout_proc ();
+static char *findslash ();
+static int build_dirs_and_chdir ();
+#endif /* __STDC__ */
+
+static char *checkout_usage[] =
+{
+ "Usage:\n %s %s [-ANPQcflnpqs] [-r rev | -D date] [-d dir] [-k kopt] modules...\n",
+ "\t-A\tReset any sticky tags/date/kopts.\n",
+ "\t-N\tDon't shorten module paths if -d specified.\n",
+ "\t-P\tPrune empty directories.\n",
+ "\t-Q\tReally quiet.\n",
+ "\t-c\t\"cat\" the module database.\n",
+ "\t-f\tForce a head revision match if tag/date not found.\n",
+ "\t-l\tLocal directory only, not recursive\n",
+ "\t-n\tDo not run module program (if any).\n",
+ "\t-p\tCheck out files to standard output.\n",
+ "\t-q\tSomewhat quiet.\n",
+ "\t-s\tLike -c, but include module status.\n",
+ "\t-r rev\tCheck out revision or tag. (implies -P)\n",
+ "\t-D date\tCheck out revisions as of date. (implies -P)\n",
+ "\t-d dir\tCheck out into dir instead of module name.\n",
+ "\t-K key\tUse RCS key -K option on checkout.\n",
+ "\t-k kopt\tUse RCS kopt -k option on checkout.\n",
+ "\t-j rev\tMerge in changes made between current revision and rev.\n",
+ NULL
+};
+
+static char *export_usage[] =
+{
+ "Usage: %s %s [-NPQflnq] [-r rev | -D date] [-d dir] module...\n",
+ "\t-N\tDon't shorten module paths if -d specified.\n",
+ "\t-Q\tReally quiet.\n",
+ "\t-f\tForce a head revision match if tag/date not found.\n",
+ "\t-l\tLocal directory only, not recursive\n",
+ "\t-n\tDo not run module program (if any).\n",
+ "\t-q\tSomewhat quiet.\n",
+ "\t-r rev\tCheck out revision or tag. (implies -P)\n",
+ "\t-D date\tCheck out revisions as of date. (implies -P)\n",
+ "\t-d dir\tCheck out into dir instead of module name.\n",
+ NULL
+};
+
+static int checkout_prune_dirs;
+static int force_tag_match = 1;
+static int pipeout;
+static int aflag;
+static char *options = NULL;
+static char *tag = NULL;
+static char *date = NULL;
+static char *join_rev1 = NULL;
+static char *join_rev2 = NULL;
+static char *preload_update_dir = NULL;
+static char *K_flag = NULL;
+
+int
+checkout (argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int i;
+ int c;
+ DBM *db;
+ int cat = 0, err = 0, status = 0;
+ int run_module_prog = 1;
+ int local = 0;
+ int shorten = -1;
+ char *where = NULL;
+ char *valid_options, **valid_usage;
+
+ /*
+ * A smaller subset of options are allowed for the export command, which
+ * is essentially like checkout, except that it hard-codes certain
+ * options to be on (like -kv) and takes care to remove the CVS directory
+ * when it has done its duty
+ */
+ if (strcmp (command_name, "export") == 0)
+ {
+ valid_options = "Nnd:flRQqr:D:";
+ valid_usage = export_usage;
+ }
+ else
+ {
+ valid_options = "ANnk:d:flRpQqcsr:D:j:PK:";
+ valid_usage = checkout_usage;
+ }
+
+ if (argc == -1)
+ usage (valid_usage);
+
+ ign_setup ();
+
+ optind = 1;
+ while ((c = gnu_getopt (argc, argv, valid_options)) != -1)
+ {
+ switch (c)
+ {
+ case 'A':
+ aflag = 1;
+ break;
+ case 'N':
+ shorten = 0;
+ break;
+ case 'k':
+ if (options)
+ free (options);
+ options = RCS_check_kflag (optarg);
+ break;
+ case 'n':
+ run_module_prog = 0;
+ break;
+ case 'Q':
+ really_quiet = 1;
+ /* FALL THROUGH */
+ case 'q':
+ quiet = 1;
+ break;
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case 'P':
+ checkout_prune_dirs = 1;
+ break;
+ case 'p':
+ pipeout = 1;
+ run_module_prog = 0; /* don't run module prog when piping */
+ noexec = 1; /* so no locks will be created */
+ break;
+ case 'c':
+ cat = 1;
+ break;
+ case 'd':
+ where = optarg;
+ if (shorten == -1)
+ shorten = 1;
+ break;
+ case 's':
+ status = 1;
+ break;
+ case 'f':
+ force_tag_match = 0;
+ break;
+ case 'r':
+ tag = optarg;
+ checkout_prune_dirs = 1;
+ break;
+ case 'D':
+ date = Make_Date (optarg);
+ checkout_prune_dirs = 1;
+ break;
+ case 'j':
+ if (join_rev2)
+ error (1, 0, "only two -j options can be specified");
+ if (join_rev1)
+ join_rev2 = optarg;
+ else
+ join_rev1 = optarg;
+ break;
+ case 'K':
+ K_flag = optarg;
+ break;
+ case '?':
+ default:
+ usage (valid_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+#ifdef FREEBSD_DEVELOPER
+ if (!K_flag && freebsd) {
+ /* XXX Note: The leading -K is not needed, it gets added later! */
+ K_flag = "eAuthor,Date,Header,Id,Locker,Log,RCSfile,Revision,Source,State -KiFreeBSD";
+ }
+#endif /* FREEBSD_DEVELOPER */
+ if (shorten == -1)
+ shorten = 0;
+
+ if ((!(cat + status) && argc == 0) || ((cat + status) && argc != 0)
+ || (tag && date))
+ usage (valid_usage);
+
+ if (where && pipeout)
+ error (1, 0, "-d and -p are mutually exclusive");
+
+ if (strcmp (command_name, "export") == 0)
+ {
+ if (!tag && !date)
+ {
+ error (0, 0, "must specify a tag or date");
+ usage (valid_usage);
+ }
+ if (tag && isdigit (tag[0]))
+ error (1, 0, "tag `%s' must be a symbolic tag", tag);
+ options = RCS_check_kflag ("v");/* -kv must be on */
+ }
+
+ if (cat || status)
+ {
+ cat_module (status);
+ return (0);
+ }
+ db = open_module ();
+
+ /*
+ * if we have more than one argument and where was specified, we make the
+ * where, cd into it, and try to shorten names as much as possible.
+ * Otherwise, we pass the where as a single argument to do_module.
+ */
+ if (argc > 1 && where != NULL)
+ {
+ char repository[PATH_MAX];
+
+ (void) mkdir (where, 0777);
+ if (chdir (where) < 0)
+ error (1, errno, "cannot chdir to %s", where);
+ preload_update_dir = xstrdup (where);
+ where = (char *) NULL;
+ if (!isfile (CVSADM) && !isfile (OCVSADM))
+ {
+ (void) sprintf (repository, "%s/%s", CVSroot, CVSNULLREPOS);
+ if (!isfile (repository))
+ (void) mkdir (repository, 0777);
+ Create_Admin (".", repository, (char *) NULL, (char *) NULL);
+ if (!noexec)
+ {
+ FILE *fp;
+
+ fp = open_file (CVSADM_ENTSTAT, "w+");
+ if (fclose(fp) == EOF)
+ error(1, errno, "cannot close %s", CVSADM_ENTSTAT);
+ }
+ }
+ }
+
+ /*
+ * if where was specified (-d) and we have not taken care of it already
+ * with the multiple arg stuff, and it was not a simple directory name
+ * but rather a path, we strip off everything but the last component and
+ * attempt to cd to the indicated place. where then becomes simply the
+ * last component
+ */
+ if (where != NULL && index (where, '/') != NULL)
+ {
+ char *slash;
+
+ slash = rindex (where, '/');
+ *slash = '\0';
+
+ if (chdir (where) < 0)
+ error (1, errno, "cannot chdir to %s", where);
+
+ preload_update_dir = xstrdup (where);
+
+ where = slash + 1;
+ if (*where == '\0')
+ where = NULL;
+ }
+
+ for (i = 0; i < argc; i++)
+ err += do_module (db, argv[i], CHECKOUT, "Updating", checkout_proc,
+ where, shorten, local, run_module_prog,
+ (char *) NULL);
+ close_module (db);
+ return (err);
+}
+
+/*
+ * process_module calls us back here so we do the actual checkout stuff
+ */
+/* ARGSUSED */
+static int
+checkout_proc (pargc, argv, where, mwhere, mfile, shorten,
+ local_specified, omodule, msg)
+ int *pargc;
+ char *argv[];
+ char *where;
+ char *mwhere;
+ char *mfile;
+ int shorten;
+ int local_specified;
+ char *omodule;
+ char *msg;
+{
+ int err = 0;
+ int which;
+ char *cp;
+ char *cp2;
+ char repository[PATH_MAX];
+ char xwhere[PATH_MAX];
+ char *oldupdate = NULL;
+ char *prepath;
+ char *realdirs;
+
+ /*
+ * OK, so we're doing the checkout! Our args are as follows:
+ * argc,argv contain either dir or dir followed by a list of files
+ * where contains where to put it (if supplied by checkout)
+ * mwhere contains the module name or -d from module file
+ * mfile says do only that part of the module
+ * shorten = TRUE says shorten as much as possible
+ * omodule is the original arg to do_module()
+ */
+
+ /* set up the repository (maybe) for the bottom directory */
+ (void) sprintf (repository, "%s/%s", CVSroot, argv[0]);
+
+ /* save the original value of preload_update_dir */
+ if (preload_update_dir != NULL)
+ oldupdate = xstrdup (preload_update_dir);
+
+ /* fix up argv[] for the case of partial modules */
+ if (mfile != NULL)
+ {
+ char file[PATH_MAX];
+
+ /* if mfile is really a path, straighten it out first */
+ if ((cp = rindex (mfile, '/')) != NULL)
+ {
+ *cp = 0;
+ (void) strcat (repository, "/");
+ (void) strcat (repository, mfile);
+
+ /*
+ * Now we need to fill in the where correctly. if !shorten, tack
+ * the rest of the path onto where if where is filled in
+ * otherwise tack the rest of the path onto mwhere and make that
+ * the where
+ *
+ * If shorten is enabled, we might use mwhere to set where if
+ * nobody set it yet, so we'll need to setup mwhere as the last
+ * component of the path we are tacking onto repository
+ */
+ if (!shorten)
+ {
+ if (where != NULL)
+ (void) sprintf (xwhere, "%s/%s", where, mfile);
+ else
+ (void) sprintf (xwhere, "%s/%s", mwhere, mfile);
+ where = xwhere;
+ }
+ else
+ {
+ char *slash;
+
+ if ((slash = rindex (mfile, '/')) != NULL)
+ mwhere = slash + 1;
+ else
+ mwhere = mfile;
+ }
+ mfile = cp + 1;
+ }
+
+ (void) sprintf (file, "%s/%s", repository, mfile);
+ if (isdir (file))
+ {
+
+ /*
+ * The portion of a module was a directory, so kludge up where to
+ * be the subdir, and fix up repository
+ */
+ (void) strcpy (repository, file);
+
+ /*
+ * At this point, if shorten is not enabled, we make where either
+ * where with mfile concatenated, or if where hadn't been set we
+ * set it to mwhere with mfile concatenated.
+ *
+ * If shorten is enabled and where hasn't been set yet, then where
+ * becomes mfile
+ */
+ if (!shorten)
+ {
+ if (where != NULL)
+ (void) sprintf (xwhere, "%s/%s", where, mfile);
+ else
+ (void) sprintf (xwhere, "%s/%s", mwhere, mfile);
+ where = xwhere;
+ }
+ else if (where == NULL)
+ where = mfile;
+ }
+ else
+ {
+ int i;
+
+ /*
+ * The portion of a module was a file, so kludge up argv to be
+ * correct
+ */
+ for (i = 1; i < *pargc; i++)/* free the old ones */
+ free (argv[i]);
+ argv[1] = xstrdup (mfile); /* set up the new one */
+ *pargc = 2;
+
+ /* where gets mwhere if where isn't set */
+ if (where == NULL)
+ where = mwhere;
+ }
+ }
+
+ /*
+ * if shorten is enabled and where isn't specified yet, we pluck the last
+ * directory component of argv[0] and make it the where
+ */
+ if (shorten && where == NULL)
+ {
+ if ((cp = rindex (argv[0], '/')) != NULL)
+ {
+ (void) strcpy (xwhere, cp + 1);
+ where = xwhere;
+ }
+ }
+
+ /* if where is still NULL, use mwhere if set or the argv[0] dir */
+ if (where == NULL)
+ {
+ if (mwhere)
+ where = mwhere;
+ else
+ {
+ (void) strcpy (xwhere, argv[0]);
+ where = xwhere;
+ }
+ }
+
+ if (preload_update_dir != NULL)
+ {
+ char tmp[PATH_MAX];
+
+ (void) sprintf (tmp, "%s/%s", preload_update_dir, where);
+ free (preload_update_dir);
+ preload_update_dir = xstrdup (tmp);
+ }
+ else
+ preload_update_dir = xstrdup (where);
+
+ /*
+ * At this point, where is the directory we want to build, repository is
+ * the repository for the lowest level of the path.
+ */
+
+ /*
+ * If we are sending everything to stdout, we can skip a whole bunch of
+ * work from here
+ */
+ if (!pipeout)
+ {
+
+ /*
+ * We need to tell build_dirs not only the path we want it to build,
+ * but also the repositories we want it to populate the path with. To
+ * accomplish this, we pass build_dirs a ``real path'' with valid
+ * repositories and a string to pre-pend based on how many path
+ * elements exist in where. Big Black Magic
+ */
+ prepath = xstrdup (repository);
+ cp = rindex (where, '/');
+ cp2 = rindex (prepath, '/');
+ while (cp != NULL)
+ {
+ cp = findslash (where, cp - 1);
+ cp2 = findslash (prepath, cp2 - 1);
+ }
+ *cp2 = '\0';
+ realdirs = cp2 + 1;
+
+ /*
+ * build dirs on the path if necessary and leave us in the bottom
+ * directory (where if where was specified) doesn't contain a CVS
+ * subdir yet, but all the others contain CVS and Entries.Static
+ * files
+ */
+ if (build_dirs_and_chdir (where, prepath, realdirs, *pargc <= 1) != 0)
+ {
+ error (0, 0, "ignoring module %s", omodule);
+ free (prepath);
+ free (preload_update_dir);
+ preload_update_dir = oldupdate;
+ return (1);
+ }
+
+ /* clean up */
+ free (prepath);
+
+ /* set up the repository (or make sure the old one matches) */
+ if (!isfile (CVSADM) && !isfile (OCVSADM))
+ {
+ FILE *fp;
+
+ if (!noexec && *pargc > 1)
+ {
+ Create_Admin (".", repository, (char *) NULL, (char *) NULL);
+ fp = open_file (CVSADM_ENTSTAT, "w+");
+ if (fclose(fp) == EOF)
+ error(1, errno, "cannot close %s", CVSADM_ENTSTAT);
+ }
+ else
+ Create_Admin (".", repository, tag, date);
+ }
+ else
+ {
+ char *repos;
+
+ /* get the contents of the previously existing repository */
+ repos = Name_Repository ((char *) NULL, preload_update_dir);
+ if (strcmp (repository, repos) != 0)
+ {
+ error (0, 0, "existing repository %s does not match %s",
+ repos, repository);
+ error (0, 0, "ignoring module %s", omodule);
+ free (repos);
+ free (preload_update_dir);
+ preload_update_dir = oldupdate;
+ return (1);
+ }
+ free (repos);
+ }
+ }
+
+ /*
+ * If we are going to be updating to stdout, we need to cd to the
+ * repository directory so the recursion processor can use the current
+ * directory as the place to find repository information
+ */
+ if (pipeout)
+ {
+ if (chdir (repository) < 0)
+ {
+ error (0, errno, "cannot chdir to %s", repository);
+ free (preload_update_dir);
+ preload_update_dir = oldupdate;
+ return (1);
+ }
+ which = W_REPOS;
+ }
+ else
+ which = W_LOCAL | W_REPOS;
+
+ if (tag != NULL || date != NULL)
+ which |= W_ATTIC;
+
+ /*
+ * if we are going to be recursive (building dirs), go ahead and call the
+ * update recursion processor. We will be recursive unless either local
+ * only was specified, or we were passed arguments
+ */
+ if (!(local_specified || *pargc > 1))
+ {
+ if (strcmp (command_name, "export") != 0 && !pipeout)
+ history_write ('O', preload_update_dir, tag ? tag : date, where,
+ repository);
+ err += do_update (0, (char **) NULL, options, tag, date,
+ force_tag_match, 0 /* !local */ ,
+ 1 /* update -d */ , aflag, checkout_prune_dirs,
+ pipeout, which, join_rev1, join_rev2,
+ K_flag,
+ preload_update_dir);
+ free (preload_update_dir);
+ preload_update_dir = oldupdate;
+ return (err);
+ }
+
+ if (!pipeout)
+ {
+ int i;
+ List *entries;
+
+ /* we are only doing files, so register them */
+ entries = ParseEntries (0);
+ for (i = 1; i < *pargc; i++)
+ {
+ char line[MAXLINELEN];
+ char *user;
+ Vers_TS *vers;
+
+ user = argv[i];
+ vers = Version_TS (repository, options, tag, date, user,
+ force_tag_match, 0, entries, (List *) NULL);
+ if (vers->ts_user == NULL)
+ {
+ (void) sprintf (line, "Initial %s", user);
+ Register (entries, user, vers->vn_rcs, line, vers->options,
+ vers->tag, vers->date);
+ }
+ freevers_ts (&vers);
+ }
+ dellist (&entries);
+ }
+
+ /* Don't log "export", just regular "checkouts" */
+ if (strcmp (command_name, "export") != 0 && !pipeout)
+ history_write ('O', preload_update_dir, (tag ? tag : date), where,
+ repository);
+
+ /* go ahead and call update now that everything is set */
+ err += do_update (*pargc - 1, argv + 1, options, tag, date,
+ force_tag_match, local_specified, 1 /* update -d */,
+ aflag, checkout_prune_dirs, pipeout, which, join_rev1,
+ join_rev2, K_flag, preload_update_dir);
+ free (preload_update_dir);
+ preload_update_dir = oldupdate;
+ return (err);
+}
+
+static char *
+findslash (start, p)
+ char *start;
+ char *p;
+{
+ while ((int) p >= (int) start && *p != '/')
+ p--;
+ if ((int) p < (int) start)
+ return (NULL);
+ else
+ return (p);
+}
+
+/*
+ * build all the dirs along the path to dir with CVS subdirs with appropriate
+ * repositories and Entries.Static files
+ */
+static int
+build_dirs_and_chdir (dir, prepath, realdir, sticky)
+ char *dir;
+ char *prepath;
+ char *realdir;
+ int sticky;
+{
+ FILE *fp;
+ char repository[PATH_MAX];
+ char path[PATH_MAX];
+ char path2[PATH_MAX];
+ char *slash;
+ char *slash2;
+ char *cp;
+ char *cp2;
+
+ (void) strcpy (path, dir);
+ (void) strcpy (path2, realdir);
+ for (cp = path, cp2 = path2;
+ (slash = index (cp, '/')) != NULL && (slash2 = index (cp2, '/')) != NULL;
+ cp = slash + 1, cp2 = slash2 + 1)
+ {
+ *slash = '\0';
+ *slash2 = '\0';
+ (void) mkdir (cp, 0777);
+ if (chdir (cp) < 0)
+ {
+ error (0, errno, "cannot chdir to %s", cp);
+ return (1);
+ }
+ if (!isfile (CVSADM) && !isfile (OCVSADM) &&
+ strcmp (command_name, "export") != 0)
+ {
+ (void) sprintf (repository, "%s/%s", prepath, path2);
+ Create_Admin (".", repository, sticky ? (char *) NULL : tag,
+ sticky ? (char *) NULL : date);
+ if (!noexec)
+ {
+ fp = open_file (CVSADM_ENTSTAT, "w+");
+ if (fclose(fp) == EOF)
+ error(1, errno, "cannot close %s", CVSADM_ENTSTAT);
+ }
+ }
+ *slash = '/';
+ *slash2 = '/';
+ }
+ (void) mkdir (cp, 0777);
+ if (chdir (cp) < 0)
+ {
+ error (0, errno, "cannot chdir to %s", cp);
+ return (1);
+ }
+ return (0);
+}
diff --git a/gnu/usr.bin/cvs/cvs/classify.c b/gnu/usr.bin/cvs/cvs/classify.c
new file mode 100644
index 0000000..318fab8
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/classify.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)classify.c 1.11 92/03/31";
+#endif
+
+#if __STDC__
+static void sticky_ck (char *file, int aflag, Vers_TS * vers, List * entries);
+#else
+static void sticky_ck ();
+#endif /* __STDC__ */
+
+/*
+ * Classify the state of a file
+ */
+Ctype
+Classify_File (file, tag, date, options, force_tag_match, aflag, repository,
+ entries, srcfiles, versp)
+ char *file;
+ char *tag;
+ char *date;
+ char *options;
+ int force_tag_match;
+ int aflag;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+ Vers_TS **versp;
+{
+ Vers_TS *vers;
+ Ctype ret;
+
+ /* get all kinds of good data about the file */
+ vers = Version_TS (repository, options, tag, date, file,
+ force_tag_match, 0, entries, srcfiles);
+
+ if (vers->vn_user == NULL)
+ {
+ /* No entry available, ts_rcs is invalid */
+ if (vers->vn_rcs == NULL)
+ {
+ /* there is no RCS file either */
+ if (vers->ts_user == NULL)
+ {
+ /* there is no user file */
+ if (!force_tag_match || !(vers->tag || vers->date))
+ if (!really_quiet)
+ error (0, 0, "nothing known about %s", file);
+ ret = T_UNKNOWN;
+ }
+ else
+ {
+ /* there is a user file */
+ if (!force_tag_match || !(vers->tag || vers->date))
+ if (!really_quiet)
+ error (0, 0, "use `cvs add' to create an entry for %s",
+ file);
+ ret = T_UNKNOWN;
+ }
+ }
+ else
+ {
+ /* there is an rcs file */
+
+ if (vers->ts_user == NULL)
+ {
+ /* There is no user file; needs checkout */
+ ret = T_CHECKOUT;
+ }
+ else
+ {
+ /*
+ * There is a user file; print a warning and add it to the
+ * conflict list, only if it is indeed different from what we
+ * plan to extract
+ */
+ if (No_Difference (file, vers, entries))
+ {
+ /* the files were different so it is a conflict */
+ if (!really_quiet)
+ error (0, 0, "move away %s; it is in the way", file);
+ ret = T_CONFLICT;
+ }
+ else
+ /* since there was no difference, still needs checkout */
+ ret = T_CHECKOUT;
+ }
+ }
+ }
+ else if (strcmp (vers->vn_user, "0") == 0)
+ {
+ /* An entry for a new-born file; ts_rcs is dummy */
+
+ if (vers->ts_user == NULL)
+ {
+ /*
+ * There is no user file, but there should be one; remove the
+ * entry
+ */
+ if (!really_quiet)
+ error (0, 0, "warning: new-born %s has disappeared", file);
+ ret = T_REMOVE_ENTRY;
+ }
+ else
+ {
+ /* There is a user file */
+
+ if (vers->vn_rcs == NULL)
+ /* There is no RCS file, added file */
+ ret = T_ADDED;
+ else
+ {
+ /*
+ * There is an RCS file, so someone else must have checked
+ * one in behind our back; conflict
+ */
+ if (!really_quiet)
+ error (0, 0,
+ "conflict: %s created independently by second party",
+ file);
+ ret = T_CONFLICT;
+ }
+ }
+ }
+ else if (vers->vn_user[0] == '-')
+ {
+ /* An entry for a removed file, ts_rcs is invalid */
+
+ if (vers->ts_user == NULL)
+ {
+ char tmp[PATH_MAX];
+
+ /* There is no user file (as it should be) */
+
+ (void) sprintf (tmp, "-%s", vers->vn_rcs ? vers->vn_rcs : "");
+
+ if (vers->vn_rcs == NULL)
+ {
+
+ /*
+ * There is no RCS file; this is all-right, but it has been
+ * removed independently by a second party; remove the entry
+ */
+ ret = T_REMOVE_ENTRY;
+ }
+ else if (strcmp (tmp, vers->vn_user) == 0)
+
+ /*
+ * The RCS file is the same version as the user file was, and
+ * that's OK; remove it
+ */
+ ret = T_REMOVED;
+ else
+ {
+
+ /*
+ * The RCS file is a newer version than the removed user file
+ * and this is definitely not OK; make it a conflict.
+ */
+ if (!really_quiet)
+ error (0, 0,
+ "conflict: removed %s was modified by second party",
+ file);
+ ret = T_CONFLICT;
+ }
+ }
+ else
+ {
+ /* The user file shouldn't be there */
+ if (!really_quiet)
+ error (0, 0, "%s should be removed and is still there", file);
+ ret = T_REMOVED;
+ }
+ }
+ else
+ {
+ /* A normal entry, TS_Rcs is valid */
+ if (vers->vn_rcs == NULL)
+ {
+ /* There is no RCS file */
+
+ if (vers->ts_user == NULL)
+ {
+ /* There is no user file, so just remove the entry */
+ if (!really_quiet)
+ error (0, 0, "warning: %s is not (any longer) pertinent",
+ file);
+ ret = T_REMOVE_ENTRY;
+ }
+ else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
+ {
+
+ /*
+ * The user file is still unmodified, so just remove it from
+ * the entry list
+ */
+ if (!really_quiet)
+ error (0, 0, "%s is no longer in the repository", file);
+ ret = T_REMOVE_ENTRY;
+ }
+ else
+ {
+ /*
+ * The user file has been modified and since it is no longer
+ * in the repository, a conflict is raised
+ */
+ if (No_Difference (file, vers, entries))
+ {
+ /* they are different -> conflict */
+ if (!really_quiet)
+ error (0, 0,
+ "conflict: %s is modified but no longer in the repository",
+ file);
+ ret = T_CONFLICT;
+ }
+ else
+ {
+ /* they weren't really different */
+ if (!really_quiet)
+ error (0, 0,
+ "warning: %s is not (any longer) pertinent",
+ file);
+ ret = T_REMOVE_ENTRY;
+ }
+ }
+ }
+ else if (strcmp (vers->vn_rcs, vers->vn_user) == 0)
+ {
+ /* The RCS file is the same version as the user file */
+
+ if (vers->ts_user == NULL)
+ {
+
+ /*
+ * There is no user file, so note that it was lost and
+ * extract a new version
+ */
+ if (strcmp (command_name, "update") == 0)
+ if (!really_quiet)
+ error (0, 0, "warning: %s was lost", file);
+ ret = T_CHECKOUT;
+ }
+ else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
+ {
+
+ /*
+ * The user file is still unmodified, so nothing special at
+ * all to do -- no lists updated, unless the sticky -k option
+ * has changed. If the sticky tag has changed, we just need
+ * to re-register the entry
+ */
+ if (vers->entdata->options &&
+ strcmp (vers->entdata->options, vers->options) != 0)
+ ret = T_CHECKOUT;
+ else
+ {
+ sticky_ck (file, aflag, vers, entries);
+ ret = T_UPTODATE;
+ }
+ }
+ else
+ {
+
+ /*
+ * The user file appears to have been modified, but we call
+ * No_Difference to verify that it really has been modified
+ */
+ if (No_Difference (file, vers, entries))
+ {
+
+ /*
+ * they really are different; modified if we aren't
+ * changing any sticky -k options, else needs merge
+ */
+#ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED
+ if (strcmp (vers->entdata->options ?
+ vers->entdata->options : "", vers->options) == 0)
+ ret = T_MODIFIED;
+ else
+ ret = T_NEEDS_MERGE;
+#else
+ ret = T_MODIFIED;
+ sticky_ck (file, aflag, vers, entries);
+#endif
+ }
+ else
+ {
+ /* file has not changed; check out if -k changed */
+ if (strcmp (vers->entdata->options ?
+ vers->entdata->options : "", vers->options) != 0)
+ {
+ ret = T_CHECKOUT;
+ }
+ else
+ {
+
+ /*
+ * else -> note that No_Difference will Register the
+ * file already for us, using the new tag/date. This
+ * is the desired behaviour
+ */
+ ret = T_UPTODATE;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* The RCS file is a newer version than the user file */
+
+ if (vers->ts_user == NULL)
+ {
+ /* There is no user file, so just get it */
+
+ if (strcmp (command_name, "update") == 0)
+ if (!really_quiet)
+ error (0, 0, "warning: %s was lost", file);
+ ret = T_CHECKOUT;
+ }
+ else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
+ {
+
+ /*
+ * The user file is still unmodified, so just get it as well
+ */
+ ret = T_CHECKOUT;
+ }
+ else
+ {
+ if (No_Difference (file, vers, entries))
+ /* really modified, needs to merge */
+ ret = T_NEEDS_MERGE;
+ else
+ /* not really modified, check it out */
+ ret = T_CHECKOUT;
+ }
+ }
+ }
+
+ /* free up the vers struct, or just return it */
+ if (versp != (Vers_TS **) NULL)
+ *versp = vers;
+ else
+ freevers_ts (&vers);
+
+ /* return the status of the file */
+ return (ret);
+}
+
+static void
+sticky_ck (file, aflag, vers, entries)
+ char *file;
+ int aflag;
+ Vers_TS *vers;
+ List *entries;
+{
+ if (aflag || vers->tag || vers->date)
+ {
+ char *enttag = vers->entdata->tag;
+ char *entdate = vers->entdata->date;
+
+ if ((enttag && vers->tag && strcmp (enttag, vers->tag)) ||
+ ((enttag && !vers->tag) || (!enttag && vers->tag)) ||
+ (entdate && vers->date && strcmp (entdate, vers->date)) ||
+ ((entdate && !vers->date) || (!entdate && vers->date)))
+ {
+ Register (entries, file, vers->vn_user, vers->ts_rcs,
+ vers->options, vers->tag, vers->date);
+ }
+ }
+}
diff --git a/gnu/usr.bin/cvs/cvs/commit.c b/gnu/usr.bin/cvs/cvs/commit.c
new file mode 100644
index 0000000..1b2f3be
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/commit.c
@@ -0,0 +1,1229 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Commit Files
+ *
+ * "commit" commits the present version to the RCS repository, AFTER
+ * having done a test on conflicts.
+ *
+ * The call is: cvs commit [options] files...
+ *
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)commit.c 1.84 92/03/31";
+#endif
+
+#if __STDC__
+static Dtype check_direntproc (char *dir, char *repos, char *update_dir);
+static int check_fileproc (char *file, char *update_dir, char *repository,
+ List * entries, List * srcfiles);
+static int check_filesdoneproc (int err, char *repos, char *update_dir);
+static int checkaddfile (char *file, char *repository, char *tag);
+static Dtype commit_direntproc (char *dir, char *repos, char *update_dir);
+static int commit_dirleaveproc (char *dir, int err, char *update_dir);
+static int commit_fileproc (char *file, char *update_dir, char *repository,
+ List * entries, List * srcfiles);
+static int commit_filesdoneproc (int err, char *repository, char *update_dir);
+static int finaladd (char *file, char *revision, char *tag, char *repository,
+ List *entries);
+static int findmaxrev (Node * p);
+static int fsortcmp (Node * p, Node * q);
+static int lock_RCS (char *user, char *rcs, char *rev, char *repository);
+static int lock_filesdoneproc (int err, char *repository, char *update_dir);
+static int lockrcsfile (char *file, char *repository, char *rev);
+static int precommit_list_proc (Node * p);
+static int precommit_proc (char *repository, char *filter);
+static int remove_file (char *file, char *repository, char *tag,
+ List *entries);
+static void fix_rcs_modes (char *rcs, char *user);
+static void fixaddfile (char *file, char *repository);
+static void fixbranch (char *file, char *repository, char *branch);
+static void unlockrcs (char *file, char *repository);
+static void ci_delproc (Node *p);
+static void locate_rcs (char *file, char *repository, char *rcs);
+#else
+static int fsortcmp ();
+static int lock_filesdoneproc ();
+static int check_fileproc ();
+static Dtype check_direntproc ();
+static int precommit_list_proc ();
+static int precommit_proc ();
+static int check_filesdoneproc ();
+static int commit_fileproc ();
+static int commit_filesdoneproc ();
+static Dtype commit_direntproc ();
+static int commit_dirleaveproc ();
+static int findmaxrev ();
+static int remove_file ();
+static int finaladd ();
+static void unlockrcs ();
+static void fixaddfile ();
+static void fixbranch ();
+static int checkaddfile ();
+static int lockrcsfile ();
+static int lock_RCS ();
+static void fix_rcs_modes ();
+static void ci_delproc ();
+static void locate_rcs ();
+#endif /* __STDC__ */
+
+struct commit_info
+{
+ Ctype status; /* as returned from Classify_File() */
+ char *rev; /* a numeric rev, if we know it */
+ char *tag; /* any sticky tag, or -r option */
+};
+struct master_lists
+{
+ List *ulist; /* list for Update_Logfile */
+ List *cilist; /* list with commit_info structs */
+};
+
+static int got_message;
+static int run_module_prog = 1;
+static int aflag;
+static char *tag;
+static char *write_dirtag;
+static char *logfile;
+static List *mulist;
+static List *locklist;
+static char *message;
+
+static char *commit_usage[] =
+{
+ "Usage: %s %s [-nRl] [-m msg | -f logfile] [-r rev] files...\n",
+ "\t-n\tDo not run the module program (if any).\n",
+ "\t-R\tProcess directories recursively.\n",
+ "\t-l\tLocal directory only (not recursive).\n",
+ "\t-f file\tRead the log message from file.\n",
+ "\t-m msg\tLog message.\n",
+ "\t-r rev\tCommit to this branch or trunk revision.\n",
+ NULL
+};
+
+int
+commit (argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ int err = 0;
+ int local = 0;
+
+ if (argc == -1)
+ usage (commit_usage);
+
+#ifdef CVS_BADROOT
+ /*
+ * For log purposes, do not allow "root" to commit files. If you look
+ * like root, but are really logged in as a non-root user, it's OK.
+ */
+ if (geteuid () == (uid_t) 0)
+ {
+ struct passwd *pw;
+
+ if ((pw = (struct passwd *) getpwnam (getcaller ())) == NULL)
+ error (1, 0, "you are unknown to this system");
+ if (pw->pw_uid == (uid_t) 0)
+ error (1, 0, "cannot commit files as 'root'");
+ }
+#endif /* CVS_BADROOT */
+
+ message = xmalloc (MAXMESGLEN + 1);
+ message[0] = '\0'; /* Null message by default */
+ optind = 1;
+ while ((c = gnu_getopt (argc, argv, "nlRm:f:r:")) != -1)
+ {
+ switch (c)
+ {
+ case 'n':
+ run_module_prog = 0;
+ break;
+ case 'm':
+#ifdef FORCE_USE_EDITOR
+ use_editor = TRUE;
+#else
+ use_editor = FALSE;
+#endif
+ if (strlen (optarg) >= (size_t) MAXMESGLEN)
+ {
+ error (0, 0, "warning: message too long; truncated!");
+ (void) strncpy (message, optarg, MAXMESGLEN);
+ message[MAXMESGLEN] = '\0';
+ }
+ else
+ (void) strcpy (message, optarg);
+ break;
+ case 'r':
+ if (tag)
+ free (tag);
+ tag = xstrdup (optarg);
+ break;
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case 'f':
+#ifdef FORCE_USE_EDITOR
+ use_editor = TRUE;
+#else
+ use_editor = FALSE;
+#endif
+ logfile = optarg;
+ break;
+ case '?':
+ default:
+ usage (commit_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* numeric specified revision means we ignore sticky tags... */
+ if (tag && isdigit (*tag))
+ {
+ aflag = 1;
+ /* strip trailing dots */
+ while (tag[strlen (tag) - 1] == '.')
+ tag[strlen (tag) - 1] = '\0';
+ }
+
+ /* some checks related to the "-f logfile" option */
+ if (logfile)
+ {
+ int n, logfd;
+
+ if (*message)
+ error (1, 0, "cannot specify both a message and a log file");
+
+ if ((logfd = open (logfile, O_RDONLY)) < 0 ||
+ (n = read (logfd, message, MAXMESGLEN)) < 0)
+ {
+ error (1, errno, "cannot read log message from %s", logfile);
+ }
+ (void) close (logfd);
+ message[n] = '\0';
+ }
+
+ /* XXX - this is not the perfect check for this */
+ if (argc <= 0)
+ write_dirtag = tag;
+
+ /*
+ * Run the recursion processor to find all the dirs to lock and lock all
+ * the dirs
+ */
+ locklist = getlist ();
+ err = start_recursion ((int (*) ()) NULL, lock_filesdoneproc,
+ (Dtype (*) ()) NULL, (int (*) ()) NULL, argc,
+ argv, local, W_LOCAL, aflag, 0, (char *) NULL, 0);
+ sortlist (locklist, fsortcmp);
+ if (Writer_Lock (locklist) != 0)
+ error (1, 0, "lock failed - giving up");
+
+ /*
+ * Set up the master update list
+ */
+ mulist = getlist ();
+
+ /*
+ * Run the recursion processor to verify the files are all up-to-date
+ */
+ err = start_recursion (check_fileproc, check_filesdoneproc,
+ check_direntproc, (int (*) ()) NULL, argc,
+ argv, local, W_LOCAL, aflag, 0, (char *) NULL, 1);
+ if (err)
+ {
+ Lock_Cleanup ();
+ error (1, 0, "correct above errors first!");
+ }
+
+ /*
+ * Run the recursion processor to commit the files
+ */
+ if (noexec == 0)
+ err = start_recursion (commit_fileproc, commit_filesdoneproc,
+ commit_direntproc, commit_dirleaveproc,
+ argc, argv, local, W_LOCAL, aflag, 0,
+ (char *) NULL, 1);
+
+ /*
+ * Unlock all the dirs and clean up
+ */
+ Lock_Cleanup ();
+ dellist (&mulist);
+ dellist (&locklist);
+ return (err);
+}
+
+/*
+ * compare two lock list nodes (for sort)
+ */
+static int
+fsortcmp (p, q)
+ Node *p, *q;
+{
+ return (strcmp (p->key, q->key));
+}
+
+/*
+ * Create a list of repositories to lock
+ */
+/* ARGSUSED */
+static int
+lock_filesdoneproc (err, repository, update_dir)
+ int err;
+ char *repository;
+ char *update_dir;
+{
+ Node *p;
+
+ p = getnode ();
+ p->type = LOCK;
+ p->key = xstrdup (repository);
+ if (p->key == NULL || addnode (locklist, p) != 0)
+ freenode (p);
+ return (err);
+}
+
+/*
+ * Check to see if a file is ok to commit and make sure all files are
+ * up-to-date
+ */
+/* ARGSUSED */
+static int
+check_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ Ctype status;
+ char *xdir;
+ Node *p;
+ List *ulist, *cilist;
+ Vers_TS *vers;
+ struct commit_info *ci;
+ int save_noexec, save_quiet, save_really_quiet;
+
+ save_noexec = noexec;
+ save_quiet = quiet;
+ save_really_quiet = really_quiet;
+ noexec = quiet = really_quiet = 1;
+
+ /* handle specified numeric revision specially */
+ if (tag && isdigit (*tag))
+ {
+ /* If the tag is for the trunk, make sure we're at the head */
+ if (numdots (tag) < 2)
+ {
+ status = Classify_File (file, (char *) NULL, (char *) NULL,
+ (char *) NULL, 1, aflag, repository,
+ entries, srcfiles, &vers);
+ if (status == T_UPTODATE)
+ {
+ freevers_ts (&vers);
+ status = Classify_File (file, tag, (char *) NULL,
+ (char *) NULL, 1, aflag, repository,
+ entries, srcfiles, &vers);
+ if (status == T_REMOVE_ENTRY)
+ status = T_MODIFIED;
+ }
+ }
+ else
+ {
+ char *xtag, *cp;
+
+ /*
+ * The revision is off the main trunk; make sure we're
+ * up-to-date with the head of the specified branch.
+ */
+ xtag = xstrdup (tag);
+ if ((numdots (xtag) & 1) != 0)
+ {
+ cp = rindex (xtag, '.');
+ *cp = '\0';
+ }
+ status = Classify_File (file, xtag, (char *) NULL,
+ (char *) NULL, 1, aflag, repository,
+ entries, srcfiles, &vers);
+ if ((status == T_REMOVE_ENTRY || status == T_CONFLICT)
+ && (cp = rindex (xtag, '.')) != NULL)
+ {
+ /* pluck one more dot off the revision */
+ *cp = '\0';
+ freevers_ts (&vers);
+ status = Classify_File (file, xtag, (char *) NULL,
+ (char *) NULL, 1, aflag, repository,
+ entries, srcfiles, &vers);
+ if (status == T_UPTODATE || status == T_REMOVE_ENTRY)
+ status = T_MODIFIED;
+ }
+ /* now, muck with vers to make the tag correct */
+ free (vers->tag);
+ vers->tag = xstrdup (tag);
+ free (xtag);
+ }
+ }
+ else
+ status = Classify_File (file, tag, (char *) NULL, (char *) NULL,
+ 1, 0, repository, entries, srcfiles, &vers);
+ noexec = save_noexec;
+ quiet = save_quiet;
+ really_quiet = save_really_quiet;
+
+ switch (status)
+ {
+ case T_CHECKOUT:
+ case T_NEEDS_MERGE:
+ case T_CONFLICT:
+ case T_REMOVE_ENTRY:
+ error (0, 0, "Up-to-date check failed for `%s'", file);
+ freevers_ts (&vers);
+ return (1);
+ case T_MODIFIED:
+ case T_ADDED:
+ case T_REMOVED:
+ /*
+ * some quick sanity checks; if no numeric -r option specified:
+ * - can't have a sticky date
+ * - can't have a sticky tag that is not a branch
+ * Also,
+ * - if status is T_REMOVED, can't have a numeric tag
+ * - if status is T_ADDED, rcs file must not exist
+ * - if status is T_ADDED, can't have a non-trunk numeric rev
+ */
+ if (!tag || !isdigit (*tag))
+ {
+ if (vers->date)
+ {
+ error (0, 0,
+ "cannot commit with sticky date for file `%s'",
+ file);
+ freevers_ts (&vers);
+ return (1);
+ }
+ if (status == T_MODIFIED && vers->tag &&
+ !RCS_isbranch (file, vers->tag, srcfiles))
+ {
+ error (0, 0,
+ "sticky tag `%s' for file `%s' is not a branch",
+ vers->tag, file);
+ freevers_ts (&vers);
+ return (1);
+ }
+ }
+ if (status == T_REMOVED && vers->tag && isdigit (*vers->tag))
+ {
+ error (0, 0,
+ "cannot remove file `%s' which has a numeric sticky tag of `%s'",
+ file, vers->tag);
+ freevers_ts (&vers);
+ return (1);
+ }
+ if (status == T_ADDED)
+ {
+ char rcs[PATH_MAX];
+
+ locate_rcs (file, repository, rcs);
+ if (isreadable (rcs))
+ {
+ error (0, 0,
+ "cannot add file `%s' when RCS file `%s' already exists",
+ file, rcs);
+ freevers_ts (&vers);
+ return (1);
+ }
+ if (vers->tag && isdigit (*vers->tag) &&
+ numdots (vers->tag) > 1)
+ {
+ error (0, 0,
+ "cannot add file `%s' with revision `%s'; must be on trunk",
+ file, vers->tag);
+ freevers_ts (&vers);
+ return (1);
+ }
+ }
+
+ /* done with consistency checks; now, to get on with the commit */
+ if (update_dir[0] == '\0')
+ xdir = ".";
+ else
+ xdir = update_dir;
+ if ((p = findnode (mulist, xdir)) != NULL)
+ {
+ ulist = ((struct master_lists *) p->data)->ulist;
+ cilist = ((struct master_lists *) p->data)->cilist;
+ }
+ else
+ {
+ struct master_lists *ml;
+
+ ulist = getlist ();
+ cilist = getlist ();
+ p = getnode ();
+ p->key = xstrdup (xdir);
+ p->type = UPDATE;
+ ml = (struct master_lists *)
+ xmalloc (sizeof (struct master_lists));
+ ml->ulist = ulist;
+ ml->cilist = cilist;
+ p->data = (char *) ml;
+ (void) addnode (mulist, p);
+ }
+
+ /* first do ulist, then cilist */
+ p = getnode ();
+ p->key = xstrdup (file);
+ p->type = UPDATE;
+ p->delproc = update_delproc;
+ p->data = (char *) status;
+ (void) addnode (ulist, p);
+
+ p = getnode ();
+ p->key = xstrdup (file);
+ p->type = UPDATE;
+ p->delproc = ci_delproc;
+ ci = (struct commit_info *) xmalloc (sizeof (struct commit_info));
+ ci->status = status;
+ if (vers->tag)
+ if (isdigit (*vers->tag))
+ ci->rev = xstrdup (vers->tag);
+ else
+ ci->rev = RCS_whatbranch (file, vers->tag, srcfiles);
+ else
+ ci->rev = (char *) NULL;
+ ci->tag = xstrdup (vers->tag);
+ p->data = (char *) ci;
+ (void) addnode (cilist, p);
+ break;
+ case T_UNKNOWN:
+ error (0, 0, "nothing known about `%s'", file);
+ freevers_ts (&vers);
+ return (1);
+ case T_UPTODATE:
+ break;
+ default:
+ error (0, 0, "Unknown status 0x%x for `%s'", status, file);
+ break;
+ }
+
+ freevers_ts (&vers);
+ return (0);
+}
+
+/*
+ * Print warm fuzzies while examining the dirs
+ */
+/* ARGSUSED */
+static Dtype
+check_direntproc (dir, repos, update_dir)
+ char *dir;
+ char *repos;
+ char *update_dir;
+{
+ if (!quiet)
+ error (0, 0, "Examining %s", update_dir);
+
+ return (R_PROCESS);
+}
+
+/*
+ * Walklist proc to run pre-commit checks
+ */
+static int
+precommit_list_proc (p)
+ Node *p;
+{
+ if (p->data == (char *) T_ADDED || p->data == (char *) T_MODIFIED)
+ run_arg (p->key);
+ return (0);
+}
+
+/*
+ * Callback proc for pre-commit checking
+ */
+static List *ulist;
+static int
+precommit_proc (repository, filter)
+ char *repository;
+ char *filter;
+{
+ /* see if the filter is there, only if it's a full path */
+ if (filter[0] == '/' && !isfile (filter))
+ {
+ error (0, errno, "cannot find pre-commit filter `%s'", filter);
+ return (1); /* so it fails! */
+ }
+
+ run_setup ("%s %s", filter, repository);
+ (void) walklist (ulist, precommit_list_proc);
+ return (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY));
+}
+
+/*
+ * Run the pre-commit checks for the dir
+ */
+/* ARGSUSED */
+static int
+check_filesdoneproc (err, repos, update_dir)
+ int err;
+ char *repos;
+ char *update_dir;
+{
+ int n;
+ Node *p;
+
+ /* find the update list for this dir */
+ p = findnode (mulist, update_dir);
+ if (p != NULL)
+ ulist = ((struct master_lists *) p->data)->ulist;
+ else
+ ulist = (List *) NULL;
+
+ /* skip the checks if there's nothing to do */
+ if (ulist == NULL || ulist->list->next == ulist->list)
+ return (err);
+
+ /* run any pre-commit checks */
+ if ((n = Parse_Info (CVSROOTADM_COMMITINFO, repos, precommit_proc, 1)) > 0)
+ {
+ error (0, 0, "Pre-commit check failed");
+ err += n;
+ }
+
+ return (err);
+}
+
+/*
+ * Do the work of committing a file
+ */
+static int maxrev;
+static char sbranch[PATH_MAX];
+
+/* ARGSUSED */
+static int
+commit_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ Node *p;
+ int err = 0;
+ List *ulist, *cilist;
+ struct commit_info *ci;
+ char rcs[PATH_MAX];
+
+ if (update_dir[0] == '\0')
+ p = findnode (mulist, ".");
+ else
+ p = findnode (mulist, update_dir);
+
+ /*
+ * if p is null, there were file type command line args which were
+ * all up-to-date so nothing really needs to be done
+ */
+ if (p == NULL)
+ return (0);
+ ulist = ((struct master_lists *) p->data)->ulist;
+ cilist = ((struct master_lists *) p->data)->cilist;
+
+ /*
+ * At this point, we should have the commit message unless we were called
+ * with files as args from the command line. In that latter case, we
+ * need to get the commit message ourselves
+ */
+ if (use_editor && !got_message)
+ {
+ got_message = 1;
+ do_editor (update_dir, message, repository, ulist);
+ }
+
+ p = findnode (cilist, file);
+ if (p == NULL)
+ return (0);
+
+ ci = (struct commit_info *) p->data;
+ if (ci->status == T_MODIFIED)
+ {
+ if (lockrcsfile (file, repository, ci->rev) != 0)
+ {
+ unlockrcs (file, repository);
+ return (1);
+ }
+ }
+ else if (ci->status == T_ADDED)
+ {
+ if (checkaddfile (file, repository, ci->tag) != 0)
+ {
+ fixaddfile (file, repository);
+ return (1);
+ }
+ }
+
+ /*
+ * Add the file for real
+ */
+ if (ci->status == T_ADDED)
+ {
+ char *xrev = (char *) NULL;
+
+ if (ci->rev == NULL)
+ {
+ /* find the max major rev number in this directory */
+ maxrev = 0;
+ (void) walklist (entries, findmaxrev);
+ if (maxrev == 0)
+ maxrev = 1;
+ xrev = xmalloc (20);
+ (void) sprintf (xrev, "%d", maxrev);
+ }
+
+ /* XXX - an added file with symbolic -r should add tag as well */
+ err = finaladd (file, ci->rev ? ci->rev : xrev, ci->tag,
+ repository, entries);
+ if (xrev)
+ free (xrev);
+ return (err);
+ }
+
+ if (ci->status == T_MODIFIED)
+ {
+ locate_rcs (file, repository, rcs);
+ err = Checkin ('M', file, repository, rcs, ci->rev, ci->tag,
+ message, entries);
+ if (err != 0)
+ {
+ unlockrcs (file, repository);
+ fixbranch (file, repository, sbranch);
+ }
+ }
+
+ if (ci->status == T_REMOVED)
+ err = remove_file (file, repository, ci->tag, entries);
+
+ return (err);
+}
+
+/*
+ * Log the commit and clean up the update list
+ */
+/* ARGSUSED */
+static int
+commit_filesdoneproc (err, repository, update_dir)
+ int err;
+ char *repository;
+ char *update_dir;
+{
+ List *ulist, *cilist;
+ char *xtag = (char *) NULL;
+ Node *p;
+
+ p = findnode (mulist, update_dir);
+ if (p != NULL)
+ {
+ ulist = ((struct master_lists *) p->data)->ulist;
+ cilist = ((struct master_lists *) p->data)->cilist;
+ }
+ else
+ return (err);
+
+ got_message = 0;
+
+ /* see if we need to specify a per-directory or -r option tag */
+ if (tag == NULL)
+ ParseTag (&xtag, (char **) NULL);
+
+ Update_Logfile (repository, message, tag ? tag : xtag, (FILE *) 0, ulist);
+ dellist (&ulist);
+ dellist (&cilist);
+ if (xtag)
+ free (xtag);
+
+ if (err == 0 && run_module_prog)
+ {
+ char *cp;
+ FILE *fp;
+ char line[MAXLINELEN];
+ char *repository;
+
+ /* It is not an error if Checkin.prog does not exist. */
+ if ((fp = fopen (CVSADM_CIPROG, "r")) != NULL)
+ {
+ if (fgets (line, sizeof (line), fp) != NULL)
+ {
+ if ((cp = rindex (line, '\n')) != NULL)
+ *cp = '\0';
+ repository = Name_Repository ((char *) NULL, update_dir);
+ run_setup ("%s %s", line, repository);
+ (void) printf ("%s %s: Executing '", program_name,
+ command_name);
+ run_print (stdout);
+ (void) printf ("'\n");
+ (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ free (repository);
+ }
+ (void) fclose (fp);
+ }
+ }
+
+ return (err);
+}
+
+/*
+ * Get the log message for a dir and print a warm fuzzy
+ */
+/* ARGSUSED */
+static Dtype
+commit_direntproc (dir, repos, update_dir)
+ char *dir;
+ char *repos;
+ char *update_dir;
+{
+ Node *p;
+ List *ulist;
+ char *real_repos;
+
+ /* find the update list for this dir */
+ p = findnode (mulist, update_dir);
+ if (p != NULL)
+ ulist = ((struct master_lists *) p->data)->ulist;
+ else
+ ulist = (List *) NULL;
+
+ /* skip the files as an optimization */
+ if (ulist == NULL || ulist->list->next == ulist->list)
+ return (R_SKIP_FILES);
+
+ /* print the warm fuzzy */
+ if (!quiet)
+ error (0, 0, "Committing %s", update_dir);
+
+ /* get commit message */
+ if (use_editor)
+ {
+ got_message = 1;
+ real_repos = Name_Repository (dir, update_dir);
+ do_editor (update_dir, message, real_repos, ulist);
+ free (real_repos);
+ }
+ return (R_PROCESS);
+}
+
+/*
+ * Process the post-commit proc if necessary
+ */
+/* ARGSUSED */
+static int
+commit_dirleaveproc (dir, err, update_dir)
+ char *dir;
+ int err;
+ char *update_dir;
+{
+ /* update the per-directory tag info */
+ if (err == 0 && write_dirtag != NULL)
+ WriteTag ((char *) NULL, write_dirtag, (char *) NULL);
+
+ return (err);
+}
+
+/*
+ * find the maximum major rev number in an entries file
+ */
+static int
+findmaxrev (p)
+ Node *p;
+{
+ char *cp;
+ int thisrev;
+ Entnode *entdata;
+
+ entdata = (Entnode *) p->data;
+ cp = index (entdata->version, '.');
+ if (cp != NULL)
+ *cp = '\0';
+ thisrev = atoi (entdata->version);
+ if (cp != NULL)
+ *cp = '.';
+ if (thisrev > maxrev)
+ maxrev = thisrev;
+ return (0);
+}
+
+/*
+ * Actually remove a file by moving it to the attic
+ * XXX - if removing a ,v file that is a relative symbolic link to
+ * another ,v file, we probably should add a ".." component to the
+ * link to keep it relative after we move it into the attic.
+ */
+static int
+remove_file (file, repository, tag, entries)
+ char *file;
+ char *repository;
+ char *tag;
+ List *entries;
+{
+ int omask;
+ int retcode;
+ char rcs[PATH_MAX];
+ char tmp[PATH_MAX];
+
+ locate_rcs (file, repository, rcs);
+ if (tag)
+ {
+ /* a symbolic tag is specified; just remove the tag from the file */
+ run_setup ("%s%s -q -N%s", Rcsbin, RCS, tag);
+ run_arg (rcs);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, DEVNULL, RUN_NORMAL)) != 0)
+ {
+ if (!quiet)
+ error (0, retcode == -1 ? errno : 0,
+ "failed to remove tag `%s' from `%s'", tag, rcs);
+ return (1);
+ }
+ return (0);
+ }
+ else
+ {
+ /* no symbolic tag specified; really move it into the Attic */
+ (void) sprintf (tmp, "%s/%s", repository, CVSATTIC);
+ omask = umask (2);
+ (void) mkdir (tmp, 0777);
+ (void) umask (omask);
+ (void) sprintf (tmp, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT);
+
+ if ((strcmp (rcs, tmp) == 0 || rename (rcs, tmp) != -1) ||
+ (!isreadable (rcs) && isreadable (tmp)))
+ {
+ Scratch_Entry (entries, file);
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Do the actual checkin for added files
+ */
+static int
+finaladd (file, rev, tag, repository, entries)
+ char *file;
+ char *rev;
+ char *tag;
+ char *repository;
+ List *entries;
+{
+ int ret;
+ char tmp[PATH_MAX];
+ char rcs[PATH_MAX];
+
+ locate_rcs (file, repository, rcs);
+ ret = Checkin ('A', file, repository, rcs, rev, tag,
+ message, entries);
+ if (ret == 0)
+ {
+ (void) sprintf (tmp, "%s/%s%s", CVSADM, file, CVSEXT_OPT);
+ (void) unlink_file (tmp);
+ (void) sprintf (tmp, "%s/%s%s", CVSADM, file, CVSEXT_LOG);
+ (void) unlink_file (tmp);
+ }
+ else
+ fixaddfile (file, repository);
+ return (ret);
+}
+
+/*
+ * Unlock an rcs file
+ */
+static void
+unlockrcs (file, repository)
+ char *file;
+ char *repository;
+{
+ char rcs[PATH_MAX];
+ int retcode = 0;
+
+ locate_rcs (file, repository, rcs);
+ run_setup ("%s%s -q -u", Rcsbin, RCS);
+ run_arg (rcs);
+
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
+ error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
+ "could not unlock %s", rcs);
+}
+
+/*
+ * remove a partially added file. if we can parse it, leave it alone.
+ */
+static void
+fixaddfile (file, repository)
+ char *file;
+ char *repository;
+{
+ RCSNode *rcsfile;
+ char rcs[PATH_MAX];
+ int save_really_quiet;
+
+ locate_rcs (file, repository, rcs);
+ save_really_quiet = really_quiet;
+ really_quiet = 1;
+ if ((rcsfile = RCS_parsercsfile (rcs)) == NULL)
+ (void) unlink_file (rcs);
+ else
+ freercsnode (&rcsfile);
+ really_quiet = save_really_quiet;
+}
+
+/*
+ * put the branch back on an rcs file
+ */
+static void
+fixbranch (file, repository, branch)
+ char *file;
+ char *repository;
+ char *branch;
+{
+ char rcs[PATH_MAX];
+ int retcode = 0;
+
+ if (branch != NULL && branch[0] != '\0')
+ {
+ locate_rcs (file, repository, rcs);
+ run_setup ("%s%s -q -b%s", Rcsbin, RCS, branch);
+ run_arg (rcs);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
+ error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
+ "cannot restore branch to %s for %s", branch, rcs);
+ }
+}
+
+/*
+ * do the initial part of a file add for the named file. if adding
+ * with a tag, put the file in the Attic and point the symbolic tag
+ * at the committed revision.
+ */
+static int
+checkaddfile (file, repository, tag)
+ char *file;
+ char *repository;
+ char *tag;
+{
+ FILE *fp;
+ char *cp;
+ char rcs[PATH_MAX];
+ char fname[PATH_MAX];
+ int omask;
+ int retcode = 0;
+
+ if (tag)
+ {
+ (void) sprintf(rcs, "%s/%s", repository, CVSATTIC);
+ omask = umask (2);
+ (void) mkdir (rcs, 0777);
+ (void) umask (omask);
+ (void) sprintf (rcs, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT);
+ }
+ else
+ locate_rcs (file, repository, rcs);
+
+ run_setup ("%s%s -i", Rcsbin, RCS);
+ run_args ("-t%s/%s%s", CVSADM, file, CVSEXT_LOG);
+ (void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_OPT);
+ fp = open_file (fname, "r");
+ while (fgets (fname, sizeof (fname), fp) != NULL)
+ {
+ if ((cp = rindex (fname, '\n')) != NULL)
+ *cp = '\0';
+ if (*fname)
+ run_arg (fname);
+ }
+ (void) fclose (fp);
+ run_arg (rcs);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
+ {
+ error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
+ "could not create %s", rcs);
+ return (1);
+ }
+ fix_rcs_modes (rcs, file);
+ return (0);
+}
+
+/*
+ * Lock the rcs file ``file''
+ */
+static int
+lockrcsfile (file, repository, rev)
+ char *file;
+ char *repository;
+ char *rev;
+{
+ char rcs[PATH_MAX];
+
+ locate_rcs (file, repository, rcs);
+ if (lock_RCS (file, rcs, rev, repository) != 0)
+ return (1);
+ else
+ return (0);
+}
+
+/*
+ * Attempt to place a lock on the RCS file; returns 0 if it could and 1 if it
+ * couldn't. If the RCS file currently has a branch as the head, we must
+ * move the head back to the trunk before locking the file, and be sure to
+ * put the branch back as the head if there are any errors.
+ */
+static int
+lock_RCS (user, rcs, rev, repository)
+ char *user;
+ char *rcs;
+ char *rev;
+ char *repository;
+{
+ RCSNode *rcsfile;
+ char *branch = NULL;
+ int err = 0;
+
+ /*
+ * For a specified, numeric revision of the form "1" or "1.1", (or when
+ * no revision is specified ""), definitely move the branch to the trunk
+ * before locking the RCS file.
+ *
+ * The assumption is that if there is more than one revision on the trunk,
+ * the head points to the trunk, not a branch... and as such, it's not
+ * necessary to move the head in this case.
+ */
+ if (rev == NULL || (rev && isdigit (*rev) && numdots (rev) < 2))
+ {
+ if ((rcsfile = RCS_parsercsfile (rcs)) == NULL)
+ {
+ /* invalid rcs file? */
+ err = 1;
+ }
+ else
+ {
+ /* rcsfile is valid */
+ branch = xstrdup (rcsfile->branch);
+ freercsnode (&rcsfile);
+ if (branch != NULL)
+ {
+ run_setup ("%s%s -q -b", Rcsbin, RCS);
+ run_arg (rcs);
+ if (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL) != 0)
+ {
+ error (0, 0, "cannot change branch to default for %s",
+ rcs);
+ if (branch)
+ free (branch);
+ return (1);
+ }
+ }
+ run_setup ("%s%s -q -l", Rcsbin, RCS);
+ run_arg (rcs);
+ err = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ }
+ }
+ else
+ {
+ run_setup ("%s%s -q -l%s", Rcsbin, RCS, rev ? rev : "");
+ run_arg (rcs);
+ (void) run_exec (RUN_TTY, RUN_TTY, DEVNULL, RUN_NORMAL);
+ }
+
+ if (err == 0)
+ {
+ if (branch)
+ {
+ (void) strcpy (sbranch, branch);
+ free (branch);
+ }
+ else
+ sbranch[0] = '\0';
+ return (0);
+ }
+
+ /* try to restore the branch if we can on error */
+ if (branch != NULL)
+ fixbranch (user, repository, branch);
+
+ if (branch)
+ free (branch);
+ return (1);
+}
+
+/*
+ * Called when "add"ing files to the RCS respository, as it is necessary to
+ * preserve the file modes in the same fashion that RCS does. This would be
+ * automatic except that we are placing the RCS ,v file very far away from
+ * the user file, and I can't seem to convince RCS of the location of the
+ * user file. So we munge it here, after the ,v file has been successfully
+ * initialized with "rcs -i".
+ */
+static void
+fix_rcs_modes (rcs, user)
+ char *rcs;
+ char *user;
+{
+ struct stat sb;
+
+ if (stat (user, &sb) != -1)
+ (void) chmod (rcs, (int) sb.st_mode & ~0222);
+}
+
+/*
+ * free an UPDATE node's data (really nothing to do)
+ */
+void
+update_delproc (p)
+ Node *p;
+{
+ p->data = (char *) NULL;
+}
+
+/*
+ * Free the commit_info structure in p.
+ */
+static void
+ci_delproc (p)
+ Node *p;
+{
+ struct commit_info *ci;
+
+ ci = (struct commit_info *) p->data;
+ if (ci->rev)
+ free (ci->rev);
+ if (ci->tag)
+ free (ci->tag);
+ free (ci);
+}
+
+/*
+ * Find an RCS file in the repository.
+ */
+static void
+locate_rcs (file, repository, rcs)
+ char *file;
+ char *repository;
+ char *rcs;
+{
+ (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT);
+ if (!isreadable (rcs))
+ {
+ (void) sprintf (rcs, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT);
+ if (!isreadable (rcs))
+ (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT);
+ }
+}
diff --git a/gnu/usr.bin/cvs/cvs/config.h b/gnu/usr.bin/cvs/cvs/config.h
new file mode 100644
index 0000000..b3bee5f
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/config.h
@@ -0,0 +1,217 @@
+/* @(#)config.h 1.19 92/03/31 */
+
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * This file holds (most of) the configuration tweaks that can be made to
+ * customize CVS for your site. CVS comes configured for a typical SunOS 4.x
+ * environment. The comments for each configurable item are intended to be
+ * self-explanatory. All #defines are tested first to see if an over-riding
+ * option was specified on the "make" command line.
+ *
+ * If special libraries are needed, you will have to edit the Makefile.in file
+ * or the configure script directly. Sorry.
+ */
+
+/*
+ * CVS provides the most features when used in conjunction with the Version-5
+ * release of RCS. Thus, it is the default. This also assumes that GNU diff
+ * Version-1.15 is being used as well -- you will have to configure your RCS
+ * V5 release separately to make this the case. If you do not have RCS V5 and
+ * GNU diff V1.15, comment out this define. You should not try mixing and
+ * matching other combinations of these tools.
+ */
+#ifndef HAVE_RCS5
+#define HAVE_RCS5
+#endif
+
+/*
+ * If, before installing this version of CVS, you were running RCS V4 AND you
+ * are installing this CVS and RCS V5 and GNU diff 1.15 all at the same time,
+ * you should turn on the following define. It only exists to try to do
+ * reasonable things with your existing checked out files when you upgrade to
+ * RCS V5, since the keyword expansion formats have changed with RCS V5.
+ *
+ * If you already have been running with RCS5, or haven't been running with CVS
+ * yet at all, or are sticking with RCS V4 for now, leave the commented out.
+ */
+#ifndef HAD_RCS4
+/* #define HAD_RCS4 */
+#endif
+
+/*
+ * For portability and heterogeneity reasons, CVS is shipped by default using
+ * my own text-file version of the ndbm database library in the src/myndbm.c
+ * file. If you want better performance and are not concerned about
+ * heterogeneous hosts accessing your modules file, turn this option off.
+ */
+#ifndef MY_NDBM
+#define MY_NDBM
+#endif
+
+/*
+ * The "diff" program to execute when creating patch output. This "diff"
+ * must support the "-c" option for context diffing. Specify a full pathname
+ * if your site wants to use a particular diff. If you are using the GNU
+ * version of diff (version 1.15 or later), this should be "diff -a".
+ *
+ * NOTE: this program is only used for the ``patch'' sub-command. The other
+ * commands use rcsdiff which will use whatever version of diff was specified
+ * when rcsdiff was built on your system.
+ */
+#ifndef DIFF
+#define DIFF "diff"
+#endif
+
+/*
+ * The "grep" program to execute when checking to see if a merged file had
+ * any conflicts. This "grep" must support the "-s" option and a standard
+ * regular expression as an argument. Specify a full pathname if your site
+ * wants to use a particular grep.
+ */
+#ifndef GREP
+#define GREP "grep"
+#endif
+
+/*
+ * The "rm" program to execute when pruning directories that are not part of
+ * a release. This "rm" must support the "-fr" options. Specify a full
+ * pathname if your site wants to use a particular rm.
+ */
+#ifndef RM
+#define RM "rm"
+#endif
+
+/*
+ * The "sort" program to execute when displaying the module database. Specify
+ * a full pathname if your site wants to use a particular sort.
+ */
+#ifndef SORT
+#define SORT "sort"
+#endif
+
+/*
+ * By default, RCS programs are executed with the shell or through execlp(),
+ * so the user's PATH environment variable is searched. If you'd like to
+ * bind all RCS programs to a certain directory (perhaps one not in most
+ * people's PATH) then set the default in RCSBIN_DFLT. Note that setting
+ * this here will cause all RCS programs to be executed from this directory,
+ * unless the user overrides the default with the RCSBIN environment variable
+ * or the "-b" option to CVS.
+ *
+ * This define should be either the empty string ("") or a full pathname to the
+ * directory containing all the installed programs from the RCS distribution.
+ */
+#ifndef RCSBIN_DFLT
+#define RCSBIN_DFLT ""
+#endif
+
+/*
+ * The default editor to use, if one does not specify the "-e" option to cvs,
+ * or does not have an EDITOR environment variable. I set this to just "vi",
+ * and use the shell to find where "vi" actually is. This allows sites with
+ * /usr/bin/vi or /usr/ucb/vi to work equally well (assuming that your PATH
+ * is reasonable).
+ */
+#ifndef EDITOR_DFLT
+#define EDITOR_DFLT "vi"
+#endif
+
+/*
+ * The Repository file holds the path to the directory within the source
+ * repository that contains the RCS ,v files for each CVS working directory.
+ * This path is either a full-path or a path relative to CVSROOT.
+ *
+ * The only advantage that I can see to having a relative path is that One can
+ * change the physical location of the master source repository, change one's
+ * CVSROOT environment variable, and CVS will work without problems. I
+ * recommend using full-paths.
+ */
+#ifndef RELATIVE_REPOS
+/* #define RELATIVE_REPOS */
+#endif
+
+/*
+ * When committing or importing files, you must enter a log message.
+ * Normally, you can do this either via the -m flag on the command line or an
+ * editor will be started for you. If you like to use logging templates (the
+ * rcsinfo file within the $CVSROOT/CVSROOT directory), you might want to
+ * force people to use the editor even if they specify a message with -m.
+ * Enabling FORCE_USE_EDITOR will cause the -m message to be appended to the
+ * temp file when the editor is started.
+ */
+#ifndef FORCE_USE_EDITOR
+/* #define FORCE_USE_EDITOR */
+#endif
+
+/*
+ * When locking the repository, some sites like to remove locks and assume
+ * the program that created them went away if the lock has existed for a long
+ * time. This used to be the default for previous versions of CVS. CVS now
+ * attempts to be much more robust, so lock files should not be left around
+ * by mistake. The new behaviour will never remove old locks (they must now
+ * be removed by hand). Enabling CVS_FUDGELOCKS will cause CVS to remove
+ * locks that are older than CVSLCKAGE seconds.
+ * Use of this option is NOT recommended.
+ */
+#ifndef CVS_FUDGELOCKS
+/* #define CVS_FUDGELOCKS */
+#endif
+
+/*
+ * When committing a permanent change, CVS and RCS make a log entry of
+ * who committed the change. If you are committing the change logged in
+ * as "root" (not under "su" or other root-priv giving program), CVS/RCS
+ * cannot determine who is actually making the change.
+ *
+ * As such, by default, CVS disallows changes to be committed by users
+ * logged in as "root". You can disable this option by commenting
+ * out the lines below.
+ */
+#ifndef CVS_BADROOT
+#define CVS_BADROOT
+#endif
+
+/*
+ * The "cvs diff" command accepts all the single-character options that GNU
+ * diff (1.15) accepts. Except -D. GNU diff uses -D as a way to put
+ * cpp-style #define's around the output differences. CVS, by default, uses
+ * -D to specify a free-form date (like "cvs diff -D '1 week ago'"). If
+ * you would prefer that the -D option of "cvs diff" work like the GNU diff
+ * option, then comment out this define.
+ */
+#ifndef CVS_DIFFDATE
+#define CVS_DIFFDATE
+#endif
+
+/* End of CVS configuration section */
+
+/*
+ * Externs that are included in libc, but are used frequently enough to
+ * warrant defining here.
+ */
+#ifndef STDC_HEADERS
+extern void exit ();
+#endif
+
+#ifndef getwd
+extern char *getwd ();
+#endif
+
+/*
+ * Some UNIX distributions don't include these in their stat.h Defined here
+ * because "config.h" is always included last.
+ */
+#ifndef S_IWRITE
+#define S_IWRITE 0000200 /* write permission, owner */
+#endif
+#ifndef S_IWGRP
+#define S_IWGRP 0000020 /* write permission, grougroup */
+#endif
+#ifndef S_IWOTH
+#define S_IWOTH 0000002 /* write permission, other */
+#endif
diff --git a/gnu/usr.bin/cvs/cvs/create_adm.c b/gnu/usr.bin/cvs/cvs/create_adm.c
new file mode 100644
index 0000000..911258e
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/create_adm.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Create Administration.
+ *
+ * Creates a CVS administration directory based on the argument repository; the
+ * "Entries" file is prefilled from the "initrecord" argument.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)create_adm.c 1.24 92/03/31";
+#endif
+
+void
+Create_Admin (dir, repository, tag, date)
+ char *dir;
+ char *repository;
+ char *tag;
+ char *date;
+{
+ FILE *fout;
+ char *cp;
+ char tmp[PATH_MAX];
+
+ if (noexec)
+ return;
+
+ if (!isdir (repository))
+ error (1, 0, "there is no repository %s", repository);
+
+ if (dir != NULL)
+ (void) sprintf (tmp, "%s/%s", dir, CVSADM);
+ else
+ (void) strcpy (tmp, CVSADM);
+
+ if (isfile (tmp))
+ error (1, 0, "there is a version here already");
+ else
+ {
+ if (dir != NULL)
+ (void) sprintf (tmp, "%s/%s", dir, OCVSADM);
+ else
+ (void) strcpy (tmp, OCVSADM);
+
+ if (isfile (tmp))
+ error (1, 0, "there is a version here already");
+ }
+
+ if (dir != NULL)
+ (void) sprintf (tmp, "%s/%s", dir, CVSADM);
+ else
+ (void) strcpy (tmp, CVSADM);
+ make_directory (tmp);
+
+ if (dir != NULL)
+ (void) sprintf (tmp, "%s/%s", dir, CVSADM_REP);
+ else
+ (void) strcpy (tmp, CVSADM_REP);
+ fout = open_file (tmp, "w+");
+ cp = repository;
+ strip_path (cp);
+
+#ifdef RELATIVE_REPOS
+ /*
+ * If the Repository file is to hold a relative path, try to strip off
+ * the leading CVSroot argument.
+ */
+ if (CVSroot != NULL)
+ {
+ char path[PATH_MAX];
+
+ (void) sprintf (path, "%s/", CVSroot);
+ if (strncmp (repository, path, strlen (path)) == 0)
+ cp = repository + strlen (path);
+ }
+#endif
+
+ if (fprintf (fout, "%s\n", cp) == EOF)
+ error (1, errno, "write to %s failed", tmp);
+ if (fclose (fout) == EOF)
+ error (1, errno, "cannot close %s", tmp);
+
+ /* now, do the Entries file */
+ if (dir != NULL)
+ (void) sprintf (tmp, "%s/%s", dir, CVSADM_ENT);
+ else
+ (void) strcpy (tmp, CVSADM_ENT);
+ fout = open_file (tmp, "w+");
+ if (fclose (fout) == EOF)
+ error (1, errno, "cannot close %s", tmp);
+
+ /* Create a new CVS/Tag file */
+ WriteTag (dir, tag, date);
+}
diff --git a/gnu/usr.bin/cvs/cvs/cvs.1 b/gnu/usr.bin/cvs/cvs/cvs.1
new file mode 100644
index 0000000..f0c648f
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/cvs.1
@@ -0,0 +1,1991 @@
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id cvs.1,v 1.12 1992/04/10 03:05:16 berliner Exp
+.TH CVS 1 "\*(Dt"
+.\" Full space in nroff; half space in troff
+.de SP
+.if n .sp
+.if t .sp .5
+..
+.\" quoted command
+.de `
+.RB ` "\|\\$1\|" '\\$2
+..
+.SH "NAME"
+cvs \- Concurrent Versions System
+.SH "SYNOPSIS"
+.TP
+\fBcvs\fP [ \fIcvs_options\fP ]
+.I cvs_command
+[
+.I command_options
+] [
+.I command_args
+]
+.SH "DESCRIPTION"
+.IX "revision control system" "\fLcvs\fR"
+.IX cvs "" "\fLcvs\fP \- concurrent versions system"
+.IX "concurrent versions system \- \fLcvs\fP"
+.IX "release control system" "cvs command" "" "\fLcvs\fP \- concurrent versions system"
+.IX "source control system" "cvs command" "" "\fLcvs\fP \- concurrent versions system"
+.IX revisions "cvs command" "" "\fLcvs\fP \- source control"
+.B cvs
+is a front end to the
+.BR rcs ( 1 )
+revision control system which extends
+the notion of revision control from a collection of files in a single
+directory to a hierarchical collection of directories consisting of
+revision controlled files.
+These directories and files can be combined together to form a software
+release.
+.B cvs
+provides the functions necessary to manage these software releases and to
+control the concurrent editing of source files among multiple software
+developers.
+.SP
+.B cvs
+keeps a single copy of the master sources.
+This copy is called the source ``repository''; it contains all the
+information to permit extracting previous software releases at any
+time based on either a symbolic revision tag, or a date in the past.
+.SH "ESSENTIAL COMMANDS"
+.B cvs
+provides a rich variety of commands (\fIcvs_command\fP in the
+Synopsis), each of which often has a wealth of options, to satisfy the
+many needs of source management in distributed environments. However,
+you don't have to master every detail to do useful work with
+.BR cvs ;
+in fact, five commands are sufficient to use (and contribute to)
+the source repository.
+.TP
+\fBcvs checkout\fP \fImodules\fP\|.\|.\|.
+A necessary preliminary for most \fBcvs\fP work: creates your private
+copy of the source for \fImodules\fP (named collections of source; you
+can also use a path relative to the source repository here). You can
+work with this copy without interfering with others' work. At least
+one subdirectory level is always created.
+.TP
+.B cvs update
+Execute this command from \fIwithin\fP your private source
+directory when you wish to update your copies of source files from
+changes that other developers have made to the source in the
+repository.
+.TP
+\fBcvs add\fP \fIfile\fP\|.\|.\|.
+Use this command to enroll new files in \fBcvs\fP records of your
+working directory. The files will be added to the repository the next
+time you run
+.` "cvs commit".
+Note:
+You should use the
+.` "cvs import"
+command to bootstrap new sources into the source repository.
+.` "cvs add"
+is only used for new files to an already checked-out module.
+.TP
+\fBcvs remove\fP \fIfile\fP\|.\|.\|.
+Use this command (after erasing any files listed) to declare that you
+wish to eliminate files from the repository. The removal does not
+affect others until you run
+.` "cvs commit".
+.TP
+\fBcvs commit\fP \fIfile\fP\|.\|.\|.
+Use this command when you wish to ``publish'' your changes to other
+developers, by incorporating them in the source repository.
+.SH "OPTIONS"
+The
+.B cvs
+command line can include
+.IR cvs_options ,
+which apply to the overall
+.B cvs
+program; a
+.IR cvs_command ,
+which specifies a particular action on the source repository; and
+.I command_options
+and
+.I command_arguments
+to fully specify what the
+.I cvs_command
+will do.
+.SP
+.I Warning:
+you must be careful of precisely where you place options relative to the
+.IR cvs_command .
+The same option can mean different things depending on whether it
+is in the
+.I cvs_options
+position (to the left of a
+.B cvs
+command) or in the
+.I command_options
+position (to the right of a
+.B cvs
+command).
+.SP
+There are only two situations where you may omit
+.IR cvs_command :
+.` "cvs \-H"
+elicits a list of available commands, and
+.` "cvs \-v "
+displays version information on \fBcvs\fP itself.
+.SP
+.SH "CVS OPTIONS"
+Use these options to control the overall
+.B cvs
+program:
+.TP
+.B \-H
+Display usage information about the specified
+.I cvs_command
+(but do not actually execute the command). If you don't specify a
+command name,
+.` "cvs \-H"
+displays a summary of all the commands available.
+.TP
+.B \-Q
+Causes the command to be
+.I really
+quiet; the command will generate output only for serious problems.
+.TP
+.B \-q
+Causes the command to be somewhat quiet; informational messages, such
+as reports of recursion through subdirectories, are suppressed.
+.TP
+\fB\-b\fP \fIbindir\fP
+Use
+.I bindir
+as the directory where
+.SM RCS
+programs are located.
+Overrides the setting of the
+.SM RCSBIN
+environment variable.
+This value should be specified as an absolute pathname.
+.TP
+\fB\-d\fP \fICVS_root_directory\fP
+Use
+.I CVS_root_directory
+as the root directory pathname of the master
+.SM RCS
+source repository.
+Overrides the setting of the
+.SM CVSROOT
+environment variable.
+This value should be specified as an absolute pathname.
+.TP
+\fB\-e\fP \fIeditor\fP
+Use
+.I editor
+to enter revision log information.
+Overrides the setting of the
+.SM EDITOR
+environment variable.
+.TP
+.B \-l
+Do not log the
+.I cvs_command
+in the command history (but execute it anyway). See the description
+of the
+.B history
+command for information on command history.
+.TP
+.B \-n
+Do not change any files. Attempt to execute the
+.IR cvs_command ,
+but only to issue reports; do not remove, update, or merge any
+existing files, or create any new files.
+.TP
+.B \-t
+Trace program execution; display messages showing the steps of
+.B cvs
+activity. Particularly useful with
+.B \-n
+to explore the potential impact of an unfamiliar command.
+.TP
+.B \-r
+Makes new working files files read-only.
+Same effect as if the
+.SM CVSREAD
+environment variable is set.
+.TP
+.B \-v
+Displays version and copyright information for
+.BR cvs .
+.TP
+.B \-w
+Makes new working files read-write (default).
+Overrides the setting of the
+.SM CVSREAD
+environment variable.
+.SH "USAGE"
+Except when requesting general help with
+.` "cvs \-H",
+you must specify a
+.I cvs_command
+to
+.B cvs
+to select a specific release control function to perform.
+Each
+.B cvs
+command accepts its own collection of options and arguments.
+However, many options are available across several commands.
+You can display a usage summary for each command by specifying the
+.B \-H
+option with the command.
+.SH "CVS COMMAND SUMMARY"
+Here are brief descriptions of all the
+.B cvs
+commands:
+.TP
+.B add
+Add a new file or directory to the repository, pending a
+.` "cvs commit"
+on the same file.
+Can only be done from within sources created by a previous
+.` "cvs checkout"
+invocation.
+Use
+.` "cvs import"
+to place whole new hierarchies of sources under
+.B cvs
+control.
+(Does not directly affect repository; changes
+working directory.)
+.TP
+.B admin
+Execute
+.SM RCS
+control functions on the source repository. (Changes
+repository directly; uses working directory without changing it.)
+.TP
+.B checkout
+Make a working directory of source files for editing. (Creates or changes
+working directory.)
+.TP
+.B commit
+Apply to the source repository changes, additions, and deletions from your
+working directory. (Changes repository.)
+.TP
+.B diff
+Show differences between files in working directory and source
+repository, or between two revisions in source repository.
+(Does not change either repository or working directory.)
+.TP
+.B export
+Prepare copies of a set of source files for shipment off site.
+Differs from
+.` "cvs checkout"
+in that no
+.B cvs
+administrative directories are created (and therefore
+.` "cvs commit"
+cannot be executed from a directory prepared with
+.` "cvs export"),
+and a symbolic tag must be specified.
+(Does not change repository; creates directory similar to working
+directories).
+.TP
+.B history
+Show reports on
+.B cvs
+commands that you or others have executed on a particular file or
+directory in the source repository. (Does not change repository or
+working directory.) History logs are kept only if enabled by creation
+of the
+.` "$CVSROOT/CVSROOT/history"
+file; see
+.BR cvs ( 5 ).
+.TP
+.B import
+Incorporate a set of updates from off-site into the source repository,
+as a ``vendor branch''. (Changes repository.)
+.TP
+.B log
+Display
+.SM RCS
+log information.
+(Does not change repository or working directory.)
+.TP
+.B rdiff
+Prepare a collection of diffs as a patch file between two releases in
+the repository. (Does not change repository or working directory.)
+.TP
+.B release
+Cancel a
+.` "cvs checkout",
+abandoning any changes.
+(Can delete working directory; no effect on repository.)
+.TP
+.B remove
+Remove files from the source repository, pending a
+.` "cvs commit"
+on the same files. (Does not directly affect repository;
+changes working directory.)
+.TP
+.B rtag
+Explicitly specify a symbolic tag for particular revisions of files in the
+source repository. See also
+.` "cvs tag".
+(Changes repository directly; does not require or affect
+working directory.)
+.TP
+.B status
+Show current status of files: latest version, version in working
+directory, whether working version has been edited and, optionally,
+symbolic tags in the
+.SM RCS
+file. (Does not change
+repository or working directory.)
+.TP
+.B tag
+Specify a symbolic tag for files in the repository. Tags the revisions
+that were last synchronized with your working directory. (Changes
+repository directly; uses working directory without changing it.)
+.TP
+.B update
+Bring your working directory up to date with changes from the
+repository. Merges are performed automatically when possible; a
+warning is issued if manual resolution is required for conflicting
+changes. (Changes working directory; does not change repository.)
+.SH "COMMON COMMAND OPTIONS"
+This section describes the
+.I command_options
+that are available across several
+.B cvs
+commands. Not all commands support all of these options; each option
+is only supported for commands where it makes sense. However, when
+a command has one of these options you can count on the same meaning
+for the option as in other commands. (Other command
+options, which are listed with the individual commands, may have
+different meanings from one
+.B cvs
+command to another.)
+.I "Warning:"
+the
+.B history
+command is an exception;
+it supports many options that conflict
+even with these standard options.
+.TP
+\fB\-D\fP \fIdate_spec\fP
+Use the most recent revision no later than \fIdate_spec\fP (a single
+argument, date description specifying a date in the
+past). A wide variety of date formats are supported by the underlying
+.SM RCS
+facilities, similar to those described in
+.BR co ( 1 ),
+but not exactly the same.
+The \fIdate_spec\fP is interpreted as being in the local timezone, unless a
+specific timezone is specified.
+The specification is ``sticky'' when you use it to make a
+private copy of a source file; that is, when you get a working file
+using \fB\-D\fP, \fBcvs\fP records the date you
+specified, so that further updates in the same directory will use the
+same date (unless you explicitly override it; see the description of
+the \fBupdate\fP command).
+.B \-D
+is available with the
+.BR checkout ", " diff, ", " history ", " export ", "
+.BR rdiff ", " rtag ", and "
+.B update
+commands.
+Examples of valid date specifications include:
+.in +1i
+.ft B
+.nf
+1 month ago
+2 hours ago
+400000 seconds ago
+last year
+last Monday
+yesterday
+a fortnight ago
+3/31/92 10:00:07 PST
+January 23, 1987 10:05pm
+22:00 GMT
+.fi
+.ft P
+.in -1i
+.TP
+.B \-f
+When you specify a particular date or tag to \fBcvs\fP commands, they
+normally ignore files that do not contain the tag (or did not exist on
+the date) that you specified. Use the \fB\-f\fP option if you want
+files retrieved even when there is no match for the tag or date. (The
+most recent version is used in this situation.)
+.B \-f
+is available with these commands:
+.BR checkout ", " export ", "
+.BR rdiff ", " rtag ", and " update .
+.TP
+.B \-H
+Help; describe the options available for this command. This is the
+only option supported for
+.I all
+.B cvs
+commands.
+.TP
+\fB\-k\fP \fIkflag\fP
+Alter the default
+.SM RCS
+processing of keywords; all the
+.B \-k
+options described in
+.BR rcs ( 1 )
+are available. The \fB\-k\fP option is available with the
+.BR add ", " checkout ", " diff ", "
+.RB rdiff ", and " update
+commands. Your \fIkflag\fP specification is ``sticky'' when you use
+it to create a private copy of a source file; that is, when you use
+this option with the \fBcheckout\fP or \fBupdate\fP commands,
+\fBcvs\fP associates your selected \fIkflag\fP with the file, and
+continues to use it with future \fBupdate\fP commands on the same file
+until you specify otherwise.
+.TP
+.B \-l
+Local; run only in current working directory, rather than recurring through
+subdirectories. Available with the following commands:
+.BR checkout ", " commit ", " diff ", "
+.BR export ", " remove ", " rdiff ", " rtag ", "
+.BR status ", " tag ", and " update .
+.I Warning:
+this is not the same
+as the overall
+.` "cvs \-l"
+option, which you can specify to the
+.I left
+of a
+.B cvs
+command!
+.TP
+.B \-n
+Do
+.I not
+run any
+.BR checkout / commit / tag / update
+program. (A program can be specified to run on each of these
+activities, in the modules database; this option bypasses it.)
+Available with the
+.BR checkout ", " commit ", " export ", and "
+.B rtag
+commands.
+.I Warning:
+this is not the same
+as the overall
+.` "cvs \-n"
+option, which you can specify to the
+.I left
+of a
+.B cvs
+command!
+.TP
+.B \-P
+Prune (remove) directories that are empty after being updated, on
+.BR checkout ", or " update .
+Normally, an empty directory (one that is void of revision-controlled
+files) is left alone.
+Specifying
+.B \-P
+will cause these directories to be silently removed from your checked-out
+sources.
+This does not remove the directory from the repository, only from your
+checked out copy.
+Note that this option is implied by the
+.B \-r
+or
+.B \-D
+options of
+.BR checkout " and " export .
+.TP
+.B \-p
+Pipe the files retrieved from the repository to standard output,
+rather than writing them in the current directory. Available with the
+.BR checkout " and " update
+commands.
+.TP
+.B \-Q
+Causes the command to be
+.I really
+quiet; the command will generate output only for serious problems.
+Available with the following commands:
+.BR checkout ", " import ", " export ", "
+.BR rdiff ", " rtag ", "
+.BR tag ", and " update .
+.TP
+.B \-q
+Causes the command to be somewhat quiet; informational messages, such
+as reports of recursion through subdirectories, are suppressed.
+Available with the following commands:
+.BR checkout ", " import ", " export ", "
+.BR rtag ", "
+.BR tag ", and " update .
+.TP
+\fB\-r\fP \fItag\fP
+Use the revision specified by the
+.I tag
+argument instead of the default ``head'' revision. As well as
+arbitrary tags defined with the \fBtag\fP or \fBrtag\fP command, two
+special tags are always available:
+.` "HEAD"
+refers to the most
+recent version available in the repository, and
+.` "BASE"
+refers to the revision you last checked out into the current working
+directory.
+.SP
+The \fItag\fP specification is ``sticky'' when you use
+this option with
+.` "cvs checkout"
+or
+.` "cvs update"
+to
+make your own copy of a file: \fBcvs\fP remembers the \fItag\fP and
+continues to use it on future \fBupdate\fP commands, until you specify
+otherwise.
+.I tag
+can be either a symbolic or numeric tag, in
+.SM RCS
+fashion.
+Specifying the
+.B \-q
+option along with the
+.B \-r
+option is often useful, to suppress the warning messages when the
+.SM RCS
+file does not contain the specified tag.
+.B \-r
+is available with the
+.BR checkout ", " commit ", " diff ", "
+.BR history ", " export ", "
+.BR rdiff ", " rtag ", and " update
+commands.
+.I Warning:
+this is not the same
+as the overall
+.` "cvs \-r"
+option, which you can specify to the
+.I left
+of a
+.B cvs
+command!
+.SH "CVS COMMANDS"
+Here (finally) are details on all the
+.B cvs
+commands and the options each accepts. The summary lines at the top
+of each command's description highlight three kinds of things:
+.TP 1i
+\ \ \ \ Command Options and Arguments
+Special options are described in detail below; common command options
+may appear only in the summary line.
+.TP 1i
+\ \ \ \ Working Directory, or Repository?
+Some \fBcvs\fP commands require a working directory to operate; some
+require a repository. Also, some commands \fIchange\fP the
+repository, some change the working directory, and some change
+nothing.
+.TP 1i
+\ \ \ \ Synonyms
+Many commands have synonyms, which you may find easier to
+remember (or type) than the principal name.
+.PP
+.TP
+\fBadd\fP [\fB\-k\fP \fIkflag\fP] [\fB\-m '\fP\fImessage\fP\fB'\fP] \fIfiles.\|.\|.\fP
+.I Requires:
+repository, working directory.
+.br
+.I Changes:
+working directory.
+.br
+.I Synonym:
+.B new
+.br
+Use the
+.B add
+command to create a new file or directory in the
+.SM RCS
+source repository.
+The files or directories specified with
+.B add
+must already exist in the current directory (which must have been created
+with the
+.B checkout
+command).
+To add a whole new directory hierarchy to the source repository
+(for example, files received from a third-party vendor), use the
+.` "cvs import"
+command instead.
+.SP
+If the argument to
+.` "cvs add"
+refers to an immediate sub-directory, the directory is
+created at the correct place in the
+.SM RCS
+source repository, and the necessary
+.B cvs
+administration files are created in your working directory.
+If the directory already exists in the source repository,
+.` "cvs add"
+still creates the administration files in your version of the directory.
+This allows you to use
+.` "cvs add"
+to add a particular directory to your private sources even if
+someone else created that directory after your
+.B checkout
+of the sources. You can do the following:
+.SP
+.in +1i
+.ft B
+.nf
+example% mkdir new_directory
+example% cvs add new_directory
+example% cvs update new_directory
+.fi
+.ft P
+.in -1i
+.SP
+An alternate approach using
+.` "cvs update"
+might be:
+.SP
+.in +1i
+.ft B
+.nf
+example% cvs update -d new_directory
+.fi
+.ft P
+.in -1i
+.SP
+(To add \fIany available\fP new directories to your working directory, it's
+probably simpler to use
+.` "cvs checkout"
+or
+.` "cvs update -d".)
+.SP
+The added files are not placed in the
+.SM RCS
+source repository until you use
+.` "cvs commit"
+to make the change permanent.
+Doing a
+.` "cvs add"
+on a file that was removed with the
+.` "cvs remove"
+command will resurrect the file, if no
+.` "cvs commit"
+command intervened.
+.SP
+You will have the opportunity to specify a logging message, as usual,
+when you use
+.` "cvs commit"
+to make the new file permanent. If you'd like to have another
+logging message associated with just
+.I creation
+of the file (for example, to describe the file's purpose), you can
+specify it with the
+.` "\-m \fImessage\fP"
+option to the
+.B add
+command.
+.SP
+The
+.` "-k kflag"
+option specifies the default way that this
+file will be checked out.
+The
+.` "kflag"
+argument is stored in the
+.SM RCS
+file and can be changed with
+.` "cvs admin".
+Specifying
+.` "-ko"
+is useful for checking in binaries that
+shouldn't have the
+.SM RCS
+id strings expanded.
+.TP
+\fBadmin\fP [\fIrcs-options\fP] \fIfiles.\|.\|.\fP
+.I Requires:
+repository, working directory.
+.br
+.I Changes:
+repository.
+.br
+.I Synonym:
+.B rcs
+.br
+This is the
+.B cvs
+interface to assorted administrative
+.SM RCS
+facilities, documented in
+.BR rcs ( 1 ).
+.` "cvs admin"
+simply passes all its options and arguments to the
+.B rcs
+command; it does no filtering or other processing.
+This command does work recursively, however, so extreme care should be
+used.
+.TP
+\fBcheckout\fP [\fBoptions\fP] \fImodules\fP.\|.\|.
+.I Requires:
+repository.
+.br
+.I Changes:
+working directory.
+.br
+.I Synonyms:
+.BR co ", " get
+.br
+Make a working directory containing copies of the source files specified by
+.IR modules .
+You must execute
+.` "cvs checkout"
+before using most of the other
+.B cvs
+commands, since most of them operate on your working directory.
+.SP
+\fImodules\fP are either symbolic names (themselves defined as the
+module
+.` "modules"
+in the source repository; see
+.BR cvs ( 5 ))
+for some collection of source directories and files, or paths to
+directories or files in the repository.
+.SP
+Depending on the
+.I modules
+you specify,
+.B checkout
+may recursively create directories and populate them with the appropriate
+source files.
+You can then edit these source files at any time (regardless of whether
+other software developers are editing their own copies of the sources);
+update them to include new changes applied by others to the source
+repository; or commit your work as a permanent change to the
+.SM RCS
+repository.
+.SP
+Note that
+.B checkout
+is used to create directories.
+The top-level directory created is always added to the directory
+where
+.B checkout
+is invoked, and usually has the same name as the specified
+.IR module .
+In the case of a
+.I module
+alias, the created sub-directory may have a different name, but you can be
+sure that it will be a sub-directory, and that
+.B checkout
+will show the relative path leading to each file as it is extracted into
+your private work area (unless you specify the
+.B \-Q
+option).
+.SP
+Running
+.` "cvs checkout"
+on a directory that was already built by a prior
+.B checkout
+is also permitted, and
+has the same effect as specifying the
+.B \-d
+option to the
+.B update
+command described below.
+.SP
+The
+.I options
+permitted with
+.` "cvs checkout"
+include the standard command options
+.BR \-P ", " \-Q ", " \-f ", "
+.BI \-k " kflag"
+\&,
+.BR \-l ", " \-n ", " \-p ", "
+.BR \-q ", " \-r
+.IR tag ", and"
+.BI \-D " date"\c
+\&.
+.SP
+In addition to those, you can use these special command options
+with
+.BR checkout :
+.SP
+Use the
+.B \-A
+option to reset any sticky tags, dates, or
+.B \-k
+options. (If you get a working file using one of the
+\fB\-r\fP, \fB\-D\fP, or \fB\-k\fP options, \fBcvs\fP remembers the
+corresponding tag, date, or \fIkflag\fP and continues using it on
+future updates; use the \fB\-A\fP option to make \fBcvs\fP forget these
+specifications, and retrieve the ``head'' version of the file).
+.SP
+The
+.BI \-j " branch"
+option merges the changes made between the
+resulting revision and the revision that it is based on (e.g., if
+the tag refers to a branch,
+.B cvs
+will merge all changes made in that branch into your working file).
+.SP
+With two \fB-j\fP options,
+.B cvs
+will merge in the changes between the two respective revisions.
+This can be used to ``remove'' a certain delta from your working file.
+.SP
+In addition, each \fB-j\fP option can contain on optional date
+specification which, when used with branches, can limit the chosen
+revision to one within a specific date.
+An optional date is specified by adding a colon (:) to the tag.
+An example might be what
+.` "cvs import"
+tells you to do when you have
+just imported sources that have conflicts with local changes:
+.SP
+.in +1i
+.ft B
+.nf
+example% cvs checkout -jTAG:yesterday -jTAG module
+.fi
+.ft P
+.in -1i
+.SP
+Use the
+.B \-N
+option with
+.` "\-d \fIdir\fP"
+to avoid shortening module paths in your working directory. (Normally, \fBcvs\fP shortens paths as much as possible when you specify an explicit target directory.)
+.SP
+Use the
+.B \-c
+option to copy the module file, sorted, to the standard output,
+instead of creating or modifying any files or directories in your
+working directory.
+.SP
+Use the
+.BI \-d " dir"
+option to create a directory called
+.I dir
+for the working files, instead of using the module name. Unless you
+also use \fB\-N\fP, the paths created under \fIdir\fP will be as short
+as possible.
+.SP
+Use the
+.B \-s
+option to display per-module status information stored with
+the
+.B \-s
+option within the modules file.
+.TP
+\fBcommit\fP [\fB\-lnR\fP] [\fB\-m\fP '\fIlog_message\fP' | \fB\-f\fP \fIfile\fP] [\fB\-r\fP \fIrevision\fP] [\fIfiles.\|.\|.\fP]
+.I Requires:
+working directory, repository.
+.br
+.I Changes:
+repository.
+.br
+.I Synonym:
+.B ci
+.br
+Use
+.` "cvs commit"
+when you want to incorporate changes from your working source
+files into the general source repository.
+.SP
+If you don't specify particular \fIfiles\fP to commit, all
+of the files in your working current directory are examined.
+.B commit
+is careful to change in the repository only those files that you have
+really changed. By default (or if you explicitly specify the
+.B \-R
+option), files
+in subdirectories are also examined and committed if they have
+changed; you can use the
+.B \-l
+option to limit
+.B commit
+to the current directory only.
+.SP
+.B commit
+verifies that the selected files are up to date with the current revisions
+in the source repository; it will notify you, and exit without
+committing, if any of the specified files must be made current first
+with
+.` "cvs update".
+.B commit
+does not call the
+.B update
+command for you, but rather leaves that for you to do when
+the time is right.
+.SP
+When all is well, an editor is invoked to allow you to enter a log
+message that will be written to one or more logging programs and placed in the
+.SM RCS
+source repository file.
+You can instead specify the log message on the command line with the
+.B \-m
+option, thus suppressing the editor invocation, or use the
+.B \-f
+option to specify that the argument \fIfile\fP contains the log message.
+.SP
+The
+.B \-r
+option can be used to commit to a particular symbolic or numeric revision
+within the
+.SM RCS
+file.
+For example, to bring all your files up to the
+.SM RCS
+revision ``3.0'' (including those that haven't changed), you might do:
+.SP
+.in +1i
+.ft B
+.nf
+example% cvs commit -r3.0
+.fi
+.ft P
+.in -1i
+.SP
+.B cvs
+will only allow you to commit to a revision that is on the main trunk (a
+revision with a single dot).
+However, you can also commit to a branch revision (one that has an even
+number of dots) with the
+.B \-r
+option.
+To create a branch revision, one typically use the
+.B \-b option of the
+.BR rtag " or " tag
+commands.
+Then, either
+.BR checkout " or " update
+can be used to base your sources on the newly created branch.
+From that point on, all
+.B commit
+changes made within these working sources will be automatically added
+to a branch revision, thereby not perturbing main-line development in any
+way.
+For example, if you had to create a patch to the 1.2 version of the
+product, even though the 2.0 version is already under development, you
+might do:
+.SP
+.in +1i
+.ft B
+.nf
+example% cvs rtag -b -rFCS1_2 FCS1_2_Patch product_module
+example% cvs checkout -rFCS1_2_Patch product_module
+example% cd product_module
+[[ hack away ]]
+example% cvs commit
+.fi
+.ft P
+.in -1i
+.SP
+Say you have been working on some extremely experimental software, based on
+whatever revision you happened to checkout last week.
+If others in your group would like to work on this software with you, but
+without disturbing main-line development, you could commit your change to a
+new branch.
+Others can then checkout your experimental stuff and utilize the full
+benefit of
+.B cvs
+conflict resolution.
+The scenario might look like:
+.SP
+.in +1i
+.ft B
+.nf
+example% cvs tag -b EXPR1
+example% cvs update -rEXPR1
+[[ hack away ]]
+example% cvs commit
+.fi
+.ft P
+.in -1i
+.SP
+Others would simply do
+.` "cvs checkout -rEXPR1 whatever_module"
+to work with you on the experimental change.
+.TP
+\fBdiff\fP [\fB\-kl\fP] [\fIrcsdiff_options\fP] [[\fB\-r\fP \fIrev1\fP | \fB\-D\fP \fIdate1\fP] [\fB\-r\fP \fIrev2\fP | \fB\-D\fP \fIdate2\fP]] [\fIfiles.\|.\|.\fP]
+.I Requires:
+working directory, repository.
+.br
+.I Changes:
+nothing.
+.br
+You can compare your working files with revisions in the source
+repository, with the
+.` "cvs diff"
+command. If you don't specify a particular revision, your files
+are compared with the revisions they were based on. You can also use
+the standard
+.B cvs
+command option
+.B \-r
+to specify a particular revision to compare your files with. Finally,
+if you use
+.B \-r
+twice, you can see differences between two revisions in the
+repository.
+You can also specify
+.B \-D
+options to diff against a revision in the past.
+The
+.B \-r
+and
+.B \-D
+options can be mixed together with at most two options ever specified.
+.SP
+See
+.BR rcsdiff ( 1 )
+for a list of other accepted options.
+.SP
+If you don't specify any files,
+.B diff
+will display differences for all those files in the current directory
+(and its subdirectories, unless you use the standard option
+.BR \-l )
+that
+differ from the corresponding revision in the source repository
+(i.e. files that
+.I you
+have changed), or that differ from the revision specified.
+.TP
+\fBexport\fP [\-\fBf\|lNnQq\fP] \fB\-r\fP \fIrev\fP\||\|\fB\-D\fP \fIdate\fP [\fB\-d\fP \fIdir\fP] \fImodule\fP.\|.\|.
+.I Requires:
+repository.
+.br
+.I Changes:
+current directory.
+.br
+This command is a variant of
+.` "cvs checkout";
+use it when you want a copy of the source for \fImodule\fP
+without the \fBcvs\fP administrative directories. For example, you
+might use
+.` "cvs export"
+to prepare source for shipment
+off-site. This command \fIrequires\fP that you specify a date or tag
+(with \fB\-D\fP or \fB\-r\fP), so that you can count on reproducing
+the source you ship to others.
+.SP
+The only non-standard options are
+.` "\-d \fIdir\fP"
+(write the
+source into directory \fIdir\fP) and
+.` "\-N"
+(don't shorten
+module paths).
+These have the same meanings as the same options in
+.` "cvs checkout".
+.SP
+The
+.B \-kv
+option is always set when
+.B export
+is used.
+This causes any
+.SM RCS
+keywords to be expanded such that an
+.B import
+done at some other site will not lose the keyword revision information.
+There is no way to override this.
+.TP
+\fBhistory\fP [\fB\-\fP\fIreport\fP] [\fB\-\fP\fIflags\fP] [\fB\-\fP\fIoptions args\fP] [\fIfiles\fP.\|.\|.]
+.I Requires:
+the file
+.` "$CVSROOT/CVSROOT/history"
+.br
+.I Changes:
+nothing.
+.br
+\fBcvs\fP keeps a history file that tracks each use of the
+\fBcheckout\fP, \fBcommit\fP, \fBrtag\fP, \fBupdate\fP, and \fBrelease\fP
+commands. You can use
+.` "cvs history"
+to display this
+information in various formats.
+.SP
+.I Warning:
+.` "cvs history"
+uses
+.` "\-f",
+.` "\-l",
+.` "\-n",
+and
+.` "\-p"
+in ways that conflict with the
+descriptions in
+.SM
+COMMON COMMAND OPTIONS\c
+\&.
+.SP
+Several options (shown above as \fB\-\fP\fIreport\fP) control what
+kind of report is generated:
+.TP 1i
+.B \ \ \ \ \ \ \-c
+Report on each time \fBcommit\fP was used (i.e., each time the
+repository was modified).
+.TP 1i
+\fB\ \ \ \ \ \ \-m\fP \fImodule\fP
+Report on a particular \fImodule\fP. (You can meaningfully use
+\fB\-m\fP more than once on the command line.)
+.TP 1i
+.B \ \ \ \ \ \ \-o
+Report on checked-out modules.
+.TP 1i
+.B \ \ \ \ \ \ \-T
+Report on all tags.
+.TP 1i
+\fB\ \ \ \ \ \ \-x\fP \fItyp\fP
+Extract a particular set of record types \fIX\fP from the \fBcvs\fP
+history. The types are indicated by single letters, which you may
+specify in combination.
+Certain commands have a single record type: \fBcheckout\fP (type `O'),
+\fBrelease\fP (type `F'), and \fBrtag\fP (type `T'). One of four
+record types may result from an \fBupdate\fP: `W', when the working copy
+of a file is deleted during update (because it was gone from the
+repository); `U', when a working file was copied from the
+repository; `G', when a merge was necessary and it succeeded; and 'C',
+when a merge was necessary but collisions were detected (requiring
+manual merging). Finally, one of three record types results from
+\fBcommit\fP: `M', when a file was modified; `A', when a file is first
+added; and `R', when a file is removed.
+.TP 1i
+.B \ \ \ \ \ \ \-e
+Everything (all record types); equivalent to specifying
+.` "\-xMACFROGWUT".
+.PP
+.RS .5i
+The options shown as \fB\-\fP\fIflags\fP constrain the report without
+requiring option arguments:
+.RE
+.TP 1i
+.B \ \ \ \ \ \ \-a
+Show data for all users (the default is to show data only for the user
+executing
+.` "cvs history").
+.TP 1i
+.B \ \ \ \ \ \ \-l
+Show last modification only.
+.TP 1i
+.B \ \ \ \ \ \ \-w
+Show only the records for modifications done from the same working
+directory where
+.` "cvs history"
+is executing.
+.PP
+.RS .5i
+The options shown as \fB\-\fP\fIoptions args\fP constrain the report
+based on an argument:
+.RE
+.TP 1i
+\fB\ \ \ \ \ \ \-b\fP \fIstr\fP
+Show data back to a record containing the string \fIstr\fP in either
+the module name, the file name, or the repository path.
+.TP 1i
+\fB\ \ \ \ \ \ \-D\fP \fIdate\fP
+Show data since \fIdate\fP.
+.TP 1i
+\fB\ \ \ \ \ \ \-p\fP \fIrepository\fP
+Show data for a particular source repository (you can specify several
+\fB\-p\fP options on the same command line).
+.TP 1i
+\fB\ \ \ \ \ \ \-r\fP \fIrev\fP
+Show records referring to revisions since the revision or tag
+named \fIrev\fP appears in individual RCS files.
+Each
+.SM RCS
+file is searched for the revision or tag.
+.TP 1i
+\fB\ \ \ \ \ \ \-t\fP \fItag\fP
+Show records since tag \fItag\fP was last added to the the history file.
+This differs from the \fB-r\fP flag above in that it reads
+only the history file, not the
+.SM RCS
+files, and is much faster.
+.TP 1i
+\fB\ \ \ \ \ \ \-u\fP \fIname\fP
+Show records for user \fIname\fP.
+.PP
+.TP
+\fBimport\fP [\fB\-\fP\fIoptions\fP] \fIrepository vendortag releasetag\fP.\|.\|.
+.I Requires:
+Repository, source distribution directory.
+.br
+.I Changes:
+repository.
+.br
+Use
+.` "cvs import"
+to incorporate an entire source
+distribution from an outside source (e.g., a source vendor) into your
+source repository directory. You can use this command both for
+initial creation of a repository, and for wholesale updates to the
+module form the outside source.
+.SP
+The \fIrepository\fP argument gives a directory name (or a path to a
+directory) under the CVS root directory for repositories; if the
+directory did not exist, \fBimport\fP creates it.
+.SP
+When you use \fBimport\fP for updates to source that has been modified in your
+source repository (since a prior \fBimport\fP), it
+will notify you of any files that conflict in the two branches of
+development; use
+.` "cvs checkout -j"
+to reconcile the differences, as \fBimport\fP instructs you to do.
+.SP
+By default, certain file names are ignored during
+.` "cvs import":
+names associated with
+.SM CVS
+administration, or with other common source control systems; common
+names for patch files, object files, archive files, and editor backup
+files; and other names that are usually artifacts of assorted utilities.
+Currently, the default list of ignored files includes files matching
+these names:
+.SP
+.in +1i
+.ft B
+.nf
+RCSLOG RCS SCCS
+CVS* cvslog.*
+tags TAGS
+\&.make.state .nse_depinfo
+*~ #* .#* ,*
+*.old *.bak *.orig *.rej .del\-*
+*.a *.o *.Z *.elc *.ln core
+.fi
+.ft P
+.in -1i
+.SP
+The outside source is saved in a first-level
+.SM RCS
+branch, by default
+.` "1.1.1".
+Updates are leaves of this
+branch; for example, files from the first imported collection of
+source will be revision
+.` "1.1.1.1",
+then files from the first
+imported update will be revision
+.` "1.1.1.2",
+and so on.
+.SP
+At least three arguments are required. \fIrepository\fP is needed to
+identify the collection of source. \fIvendortag\fP is a tag for the
+entire branch (e.g., for
+.` "1.1.1").
+You must also specify at
+least one \fIreleasetag\fP to identify the files at the leaves created
+each time you execute
+.` "cvs import".
+.SP
+Three of the standard
+.B cvs
+command options are available: \fB\-Q\fP, \fB\-q\fP, and \fB\-m\fP
+\fImessage\fP. If you do not specify a logging message with
+\fB\-m\fP, your editor is invoked (as with \fBcommit\fP) to allow you
+to enter one.
+.SP
+There are two additional special options.
+.SP
+Use
+.` "\-b \fIbranch\fP"
+to specify a first-level branch other
+than
+.` "1.1.1".
+.SP
+Use
+.` "\-I \fIname\fP"
+to specify file names that should be
+ignored during \fBimport\fP. You can use this option repeatedly.
+To avoid ignoring any files at all (even those ignored by default),
+specify
+.` "\-I !".
+.TP
+\fBlog\fP [\fB\-l\fP] \fIrlog-options [files\fP\|.\|.\|.]
+.I Requires:
+repository, working directory.
+.br
+.I Changes:
+nothing.
+.br
+.I Synonym:
+.B rlog
+.br
+Display log information for \fIfiles\fP.
+.` "cvs log"
+calls
+the
+.SM RCS
+utility \fBrlog\fP; all the options described in
+.BR rlog ( 1 )
+are available. Among the more useful \fBrlog\fP options are \fB\-h\fP
+to display only the header (including tag definitions, but omitting
+most of the full log); \fB\-r\fP to select logs on particular
+revisions or ranges of revisions; and \fB\-d\fP to select particular
+dates or date ranges. See
+.BR rlog ( 1 )
+for full explanations.
+This command is recursive by default, unless the
+.B \-l
+option is specified.
+.TP
+\fBrdiff\fP [\fB\-\fP\fIflags\fP] [\fB\-V\fP \fIvn\fP] [\fB\-r\fP \fIt\fP|\fB\-D\fP \fId\fP [\fB\-r\fP \fIt2\fP|\fB\-D\fP \fId2\fP]] \fImodules\|.\|.\|.\fP
+.I Requires:
+repository.
+.br
+.I Changes:
+nothing.
+.br
+.I Synonym:
+.B patch
+.br
+Builds a Larry Wall format
+.BR patch ( 1 )
+file between two releases, that can be fed directly into the
+.B patch
+program to bring an old release up-to-date with the new release.
+(This is one of the few \fBcvs\fP commands that operates directly from
+the repository, and doesn't require a prior
+.BR checkout .)
+The diff output is sent to the standard output device.
+You can specify (using the standard \fB\-r\fP and \fB\-D\fP options)
+any combination of one or two revisions or dates.
+If only one revision or date is specified, the
+patch file reflects differences between that revision or date and the
+current ``head'' revisions in the
+.SM RCS
+file.
+.SP
+Note that if the software release affected
+is contained in more than one directory, then it may be necessary to
+specify the
+.B \-p
+option to the
+.B patch
+command when patching the old sources, so that
+.B patch
+is able to find the files that are located in other directories.
+.SP
+If you use the option \fB\-V\fP \fIvn\fP,
+.SM RCS
+keywords are expanded according to the rules current in
+.SM RCS
+version \fIvn\fP (the expansion format changed with
+.SM RCS
+version 5).
+.SP
+The standard option \fIflags\fP \fB\-f\fP, \fB\-l\fP, \fB\-Q\fP, and
+\fB\-q\fP are available with this command. There are also several
+special options flags:
+.SP
+If you use the
+.B \-s
+option, no patch output is produced.
+Instead, a summary of the changed or added files between the two
+releases is sent to the standard output device.
+This is useful for finding out, for example, which files have changed
+between two dates or revisions.
+.SP
+If you use the
+.B \-t
+option, a diff of the top two revisions is sent to the standard output device.
+This is most useful for seeing what the last change to a file was.
+.SP
+If you use the
+.B \-u
+option, the patch output uses the newer ``unidiff'' format for context
+diffs.
+.SP
+You can use
+.B \-c
+to explicitly specify the
+.` "diff \-c"
+form of context diffs
+(which is the default), if you like.
+.TP
+\fBrelease\fP [\fB\-dQq\fP] \fImodules\fP\|.\|.\|.
+.I Requires:
+Working directory.
+.br
+.I Changes:
+Working directory, history log.
+.br
+This command is meant to safely cancel the effect of
+.` "cvs checkout'.
+Since
+.B cvs
+doesn't lock files, it isn't strictly necessary to use this command.
+You can always simply delete your working directory, if you
+like; but you risk losing changes you may have forgotten, and you
+leave no trace in the
+.B cvs
+history file that you've abandoned your checkout.
+.SP
+Use
+.` "cvs release"
+to avoid these problems. This command
+checks that no un-committed changes are present; that you are
+executing it from immediately above, or inside, a \fBcvs\fP working
+directory; and that the repository recorded for your files is the same
+as the repository defined in the module database.
+.SP
+If all these conditions are true,
+.` "cvs release"
+leaves a
+record of its execution (attesting to your intentionally abandoning
+your checkout) in the
+.B cvs
+history log.
+.SP
+You can use the \fB\-d\fP flag to request that your working copies of
+the source files be deleted if the \fBrelease\fP succeeds.
+.TP
+\fBremove\fP [\fB\-lR\fP] [\fIfiles\|.\|.\|.\fP]
+.I Requires:
+Working directory.
+.br
+.I Changes:
+Working directory.
+.br
+.I Synonyms:
+.BR rm ", " delete
+.br
+Use this command to declare that you wish to remove \fIfiles\fP from
+the source repository. Like most
+.B cvs
+commands,
+.` "cvs remove"
+works on files in your working
+directory, not directly on the repository. As a safeguard, it also
+requires that you first erase the specified files from your working
+directory.
+.SP
+The files are not actually removed until you apply your changes to the
+repository with
+.BR commit ;
+at that point, the corresponding
+.SM RCS
+files in the source repository are
+.I moved
+into the
+.` "Attic"
+directory (also within the source repository).
+.SP
+This command is recursive by default, scheduing all physically removed
+files that it finds for removal by the next
+.BR commit .
+Use the
+.B \-l
+option to avoid this recursion, or just specify that actual files that you
+wish remove to consider.
+.TP
+\fBrtag\fP [\fB\-f\|alnRQq\fP] [\fB\-b\fP] [\fB\-d\fP] [\fB\-r\fP \fItag\fP | \fB\-D\fP \fIdate\fP] \fIsymbolic_tag\fP \fImodules\|.\|.\|.\fP
+.I Requires:
+repository.
+.br
+.I Changes:
+repository.
+.br
+.I Synonym:
+.B rfreeze
+.br
+You can use this command to assign symbolic tags to particular,
+explicitly specified source versions in the repository.
+.` "cvs rtag"
+works directly on the repository contents (and requires no
+prior
+.BR checkout ).
+Use
+.` "cvs tag"
+instead, to base the selection of
+versions to tag on the contents of your working directory.
+.SP
+In general, tags (often the symbolic names of software distributions)
+should not be removed, but the
+.B \-d
+option is available as a means to remove completely obsolete symbolic names
+if necessary (as might be the case for an Alpha release, say).
+.SP
+The \fB-b\fP option makes the tag a ``branch'' tag, allowing
+concurrent, isolated development.
+This is most useful for creating a patch to a previously released software
+distribution.
+.SP
+You can use the standard \fB\-r\fP and \fB\-D\fP options to tag only those
+files that already contain a certain tag. This method would be used
+to rename a tag: tag only the files identified by the old tag, then delete the
+old tag, leaving the new tag on exactly the same files as the old tag.
+.SP
+.B rtag
+executes recursively by default, tagging all subdirectories of
+\fImodules\fP you specify in the argument. You can restrict its
+operation to top-level directories with the standard \fB\-l\fP option;
+or you can explicitly request recursion with \fB\-R\fP.
+.SP
+The modules database can specify a program to execute whenever a tag
+is specified; a typical use is to send electronic mail to a group of
+interested parties. If you want to bypass that program, use the
+standard \fB\-n\fP option.
+.SP
+The standard options \fB\-Q\fP and \fB\-q\fP are also available with
+this command.
+.SP
+Use the
+.B \-a
+option to have
+.B rtag
+look in the
+.` "Attic"
+for removed files that contin the specified tag.
+The tag is removed from these files, which makes it convenient to re-use a
+symbolic tag as development continues (and files get removed from the
+up-coming distribution).
+.TP
+\fBstatus\fP [\fB\-lR\fP] [\fB\-v\fP] [\fIfiles\fP\|.\|.\|.]
+.I Requires:
+working directory, repository.
+.br
+.I Changes:
+nothing.
+.br
+Display a brief report on the current status of \fIfiles\fP with
+respect to the source repository, including any ``sticky'' tags,
+dates, or \fB\-k\fP options. (``Sticky'' options will restrict how
+.` "cvs update"
+operates until you reset them; see the
+description of
+.` "cvs update \-A\|.\|.\|.".)
+.SP
+You can also use this command to anticipate the potential impact of a
+.` "cvs update"
+on your working source directory. If you do
+not specify any \fIfiles\fP explicitly, reports are shown for all
+files that \fBcvs\fP has placed in your working directory. You can
+limit the scope of this search to the current directory itself (not
+its subdirectories) with the standard \fB\-l\fP option flag; or you
+can explicitly request recursive status reports with the \fB\-R\fP
+option.
+.SP
+The
+.B \-v
+option causes the symbolic tags for the
+.SM RCS
+file to be displayed as well.
+.TP
+\fBtag\fP [\fB\-lQqR\fP] [\fB\-b\fP] [\fB\-d\fP] \fIsymbolic_tag\fP [\fIfiles\fP\|.\|.\|.\|]
+.I Requires:
+working directory, repository.
+.br
+.I Changes:
+repository.
+.br
+.I Synonym:
+.B freeze
+.br
+Use this command to assign symbolic tags to the nearest repository
+versions to your working sources. The tags are applied immediately to
+the repository, as with \fBrtag\fP, but the versions are supplied
+implicitly by the \fBcvs\fP records of your working files' history
+rather than applied explicitly.
+.SP
+One use for tags is to record a ``snapshot'' of the current sources
+when the software freeze date of a project arrives. As bugs are fixed
+after the freeze date, only those changed sources that are to be part
+of the release need be re-tagged.
+.SP
+The symbolic tags are meant to permanently record which revisions of which
+files were used in creating a software distribution.
+The
+.B checkout
+and
+.B update
+commands allow you to extract an exact copy of a tagged release at any time in
+the future, regardless of whether files have been changed, added, or removed
+since the release was tagged.
+.SP
+If you use
+.` "cvs tag \-d \fIsymbolic_tag\fP\|.\|.\|.",
+the
+symbolic tag you specify is
+.I deleted
+instead of being added. \fIWarning\fP: Be very certain of your ground
+before you delete a tag; doing this effectively discards some
+historical information, which may later turn out to have been valuable.
+.SP
+The \fB-b\fP option makes the tag a ``branch'' tag, allowing
+concurrent, isolated development.
+This is most useful for creating a patch to a previously released software
+distribution.
+.SP
+Normally,
+.B tag
+executes recursively through subdirectories; you can prevent this by
+using the standard \fB\-l\fP option, or specify the recursion
+explicitly by using \fB\-R\fP.
+.TP
+\fBupdate\fP [\fB\-Adf\|lPpQqR\fP] [\fB\-d\fP] [\fB\-r\fP \fItag\fP|\fB\-D\fP \fIdate\fP] \fIfiles\|.\|.\|.\fP
+.I Requires:
+repository, working directory.
+.br
+.I Changes:
+working directory.
+.br
+After you've run
+.B checkout
+to create your private copy of source from the common repository,
+other developers will continue changing the central source. From time
+to time, when it is convenient in your development process, you can
+use the
+.B update
+command
+from within your working directory to reconcile your work with any
+revisions applied to the source repository since your last
+.B checkout
+or
+.BR update .
+.SP
+.B update
+keeps you informed of its progress by printing a line for each file,
+prefaced with one of the characters
+.` "U A R M C ?"
+to indicate the status of the file:
+.TP 1i
+\fBU\fP \fIfile\fP
+The file was brought \fIup to date\fP with respect to the repository.
+This is done for any file that exists in the repository but not in
+your source, and for files that you haven't changed but are not the most
+recent versions available in the repository.
+.TP 1i
+\fBA\fP \fIfile\fP
+The file has been \fIadded\fP to your private copy of the sources, and
+will be added to the
+.SM RCS
+source repository when you run
+.` "cvs commit"
+on the file.
+This is a reminder to you that the file needs to be committed.
+.TP 1i
+\fBR\fP \fIfile\fP
+The file has been \fIremoved\fP from your private copy of the sources, and
+will be removed from the
+.SM RCS
+source repository when you run
+.` "cvs commit"
+on the file.
+This is a reminder to you that the file needs to be committed.
+.TP 1i
+\fBM\fP \fIfile\fP
+The file is \fImodified\fP in your working directory.
+.` "M"
+can indicate one of two states for a file you're working on: either
+there were no modifications to the same file in the repository, so
+that your file remains as you last saw it; or there were modifications
+in the repository as well as in your copy, but they were
+\fImerged\fP successfully, without conflict, in your working
+directory.
+.TP 1i
+\fBC\fP \fIfile\fP
+A \fIconflict\fP was detected while trying to merge your changes to
+\fIfile\fP with changes from the source repository. \fIfile\fP (the
+copy in your working directory) is now the output of the
+.BR rcsmerge ( 1 )
+command on the two versions; an unmodified copy of your file is also
+in your working directory, with the name `\fB.#\fP\fIfile\fP\fB.\fP\fIversion\fP',
+where
+.I version
+is the
+.SM RCS
+revision that your modified file started from.
+(Note that some systems automatically purge files that begin with
+\&
+.` ".#"
+if they have not been accessed for a few days.
+If you intend to keep a copy of your original file, it is a very good
+idea to rename it.)
+.TP 1i
+\fB?\fP \fIfile\fP
+\fIfile\fP is in your working directory, but does not correspond to
+anything in the source repository, and is not in the list of files
+for \fBcvs\fP to ignore (see the description of the \fB\-I\fP option).
+.PP
+.RS .5i
+.SP
+Use the
+.B \-A
+option to reset any sticky tags, dates, or
+.B \-k
+options. (If you get a working copy of a file by using one of the
+\fB\-r\fP, \fB\-D\fP, or \fB\-k\fP options, \fBcvs\fP remembers the
+corresponding tag, date, or \fIkflag\fP and continues using it on
+future updates; use the \fB\-A\fP option to make \fBcvs\fP forget these
+specifications, and retrieve the ``head'' version of the file).
+.SP
+The \fB\-j\fP\fIbranch\fP option
+merges the changes made between the
+resulting revision and the revision that it is based on (e.g., if
+the tag refers to a branch,
+.B cvs
+will merge all changes made in
+that branch into your working file).
+.SP
+With two \fB-j\fP options,
+.B cvs
+will merge in the changes between the two respective revisions.
+This can be used to ``remove'' a certain delta from your working file.
+E.g., If the file foo.c is based on
+revision 1.6 and I want to remove the changes made between 1.3 and
+1.5, I might do:
+.SP
+.in +1i
+.ft B
+.nf
+example% cvs update -j1.5 -j1.3 foo.c # note the order...
+.fi
+.ft P
+.in -1i
+.SP
+In addition, each \fB-j\fP option can contain on optional date
+specification which, when used with branches, can limit the chosen
+revision to one within a specific date.
+An optional date is specified by adding a colon (:) to the tag.
+.SP
+.in +1i
+.ft B
+.nf
+-jSymbolic_Tag:Date_Specifier
+.fi
+.ft P
+.in -1i
+.SP
+Use the
+.B \-d
+option to create any directories that exist in the repository if they're
+missing from the working directory. (Normally, update acts only on
+directories and files that were already enrolled in your
+working directory.) This is useful for updating directories
+that were created in the repository since the initial
+\fBcheckout\fP; but it has an unfortunate side effect. If you
+deliberately avoided certain directories in the repository when you
+created your working directory (either through use of a module name or by
+listing explicitly the files and directories you wanted on the
+command line), then updating with
+.B \-d
+will create those directories, which may not be what you want.
+.SP
+Use \fB\-I\fP \fIname\fP to ignore files whose names match \fIname\fP
+(in your working directory) during the update. You can specify
+\fB\-I\fP more than once on the command line to specify several files
+to ignore. By default,
+\fBupdate\fP ignores files whose names match any of the following:
+.SP
+.in +1i
+.ft B
+.nf
+RCSLOG RCS SCCS
+CVS* cvslog.*
+tags TAGS
+\&.make.state .nse_depinfo
+*~ #* .#* ,*
+*.old *.bak *.orig *.rej .del\-*
+*.a *.o *.Z *.elc *.ln core
+.fi
+.ft P
+.in -1i
+.SP
+Use
+.` "\-I !"
+to avoid ignoring any files at all.
+.SP
+The standard \fBcvs\fP command options \fB\-f\fP, \fB\-k\fP,
+\fB\-l\fP, \fB\-P\fP, \fB\-p\fP, \fB\-Q\fP, \fB\-q\fP, and \fB\-r\fP
+are also available with \fBupdate\fP.
+.RE
+.SH "FILES"
+For more detailed information on
+.B cvs
+supporting files, see
+.BR cvs ( 5 ).
+.LP
+.I
+Files in working directories:
+.TP
+CVS
+A directory of \fBcvs\fP administrative files.
+.I
+Do not delete.
+.TP
+CVS/Entries
+List and status of files in your working directory.
+.TP
+CVS/Entries.Backup
+A backup of
+.` "CVS/Entries".
+.TP
+CVS/Entries.Static
+Flag: do not add more entries on
+.` "cvs update".
+.TP
+CVS/Repository
+Pathname to the corresponding directory in the source repository.
+.TP
+CVS/Tag
+Contains the per-directory ``sticky'' tag or date information.
+This file is created/updated when you specify
+.B \-r
+or
+.B \-D
+to the
+.B checkout
+or
+.B update
+commands, and no files are specified.
+.TP
+CVS/Checkin.prog
+Name of program to run on
+.` "cvs commit".
+.TP
+CVS/Update.prog
+Name of program to run on
+.` "cvs update".
+.LP
+.I
+Files in source repositories:
+.TP
+$CVSROOT/CVSROOT
+Directory of global administrative files for repository.
+.TP
+CVSROOT/commitinfo,v
+Records programs for filtering
+.` "cvs commit"
+requests.
+.TP
+CVSROOT/history
+Log file of \fBcvs\fP transactions.
+.TP
+CVSROOT/modules,v
+Definitions for modules in this repository.
+.TP
+CVSROOT/loginfo,v
+Records programs for piping
+.` "cvs commit"
+log entries.
+.TP
+CVSROOT/rcsinfo,v
+Records pathnames to templates used dueing a
+.` "cvs commit"
+operation.
+.TP
+CVSROOT/editinfo,v
+Records programs for editing/validating
+.` "cvs commit"
+log entries.
+.TP
+Attic
+Directory for removed source files.
+.TP
+#cvs.lock
+A lock directory created by
+.B cvs
+when doing sensitive changes to the
+.SM RCS
+source repository.
+.TP
+#cvs.tfl.\fIpid\fP
+Temporary lock file for repository.
+.TP
+#cvs.rfl.\fIpid\fP
+A read lock.
+.TP
+#cvs.wfl.\fIpid\fP
+A write lock.
+.SH "ENVIRONMENT VARIABLES"
+.TP
+.SM CVSROOT
+Should contain the full pathname to the root of the
+.B cvs
+source repository (where the
+.SM RCS
+files are kept). This information must be available to \fBcvs\fP for
+most commands to execute; if
+.SM CVSROOT
+is not set, or if you wish to override it for one invocation, you can
+supply it on the command line:
+.` "cvs \-d \fIcvsroot cvs_command\fP\|.\|.\|."
+You may not need to set
+.SM CVSROOT
+if your \fBcvs\fP binary has the right path compiled in; use
+.` "cvs \-v"
+to display all compiled-in paths.
+.TP
+.SM CVSREAD
+If this is set,
+.B checkout
+and
+.B update
+will try hard to make the files in your working directory read-only.
+When this is not set, the default behavior is to permit modification
+of your working files.
+.TP
+.SM RCSBIN
+Specifies the full pathname where to find
+.SM RCS
+programs, such as
+.BR co ( 1 )
+and
+.BR ci ( 1 ).
+If not set, a compiled-in value is used; see the display from
+.` "cvs \-v".
+.TP
+.SM EDITOR
+Specifies the program to use for recording log messages during
+.BR commit .
+If not set, the default is
+.BR /usr/ucb/vi .
+.SH "AUTHORS"
+.TP
+Dick Grune
+Original author of the
+.B cvs
+shell script version posted to
+.B comp.sources.unix
+in the volume6 release of December, 1986.
+Credited with much of the
+.B cvs
+conflict resolution algorithms.
+.TP
+Brian Berliner
+Coder and designer of the
+.B cvs
+program itself in April, 1989, based on the original work done by Dick.
+.TP
+Jeff Polk
+Helped Brian with the design of the
+.B cvs
+module and vendor branch support and author of the
+.BR checkin ( 1 )
+shell script (the ancestor of
+.` "cvs import").
+.SH "SEE ALSO"
+.BR ci ( 1 ),
+.BR co ( 1 ),
+.BR cvs ( 5 ),
+.BR diff ( 1 ),
+.BR grep ( 1 ),
+.BR mkmodules ( 1 ),
+.BR patch ( 1 ),
+.BR rcs ( 1 ),
+.BR rcsdiff ( 1 ),
+.BR rcsmerge ( 1 ),
+.BR rlog ( 1 ),
+.BR rm ( 1 ),
+.BR sort ( 1 ).
diff --git a/gnu/usr.bin/cvs/cvs/cvs.5 b/gnu/usr.bin/cvs/cvs/cvs.5
new file mode 100644
index 0000000..49ca562
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/cvs.5
@@ -0,0 +1,326 @@
+.TH cvs 5 "12 February 1992"
+.\" Full space in nroff; half space in troff
+.de SP
+.if n .sp
+.if t .sp .5
+..
+.SH NAME
+cvs \- Concurrent Versions System support files
+.SH SYNOPSIS
+.hy 0
+.na
+.TP
+.B $CVSROOT/CVSROOT/modules,v
+.TP
+.B $CVSROOT/CVSROOT/commitinfo,v
+.TP
+.B $CVSROOT/CVSROOT/loginfo,v
+.TP
+.B $CVSROOT/CVSROOT/rcsinfo,v
+.TP
+.B $CVSROOT/CVSROOT/editinfo,v
+.TP
+.B $CVSROOT/CVSROOT/cvsignore,v
+.TP
+.B $CVSROOT/CVSROOT/history
+.ad b
+.hy 1
+.SH DESCRIPTION
+.B cvs
+is a system for providing source control to hierarchical collections
+of source directories. Commands and procedures for using \fBcvs\fP
+are described in
+.BR cvs ( 1 ).
+.SP
+.B cvs
+manages \fIsource repositories\fP, the directories containing master
+copies of the revision-controlled files, by copying particular
+revisions of the files to (and modifications back from) developers'
+private \fIworking directories\fP. In terms of file structure, each
+individual source repository is an immediate subdirectory of
+\fB$CVSROOT\fP.
+.SP
+The files described here are supporting files; they do not have to
+exist for \fBcvs\fP to operate, but they allow you to make \fBcvs\fP
+operation more flexible.
+.SP
+The
+.BR cvsinit ( 1 )
+shell script included at the top-level of the
+.B cvs
+distribution can be used to setup an initial
+.B $CVSROOT/CVSROOT
+area, if you don't have one already.
+.SP
+You can use the `\|modules\|' file to define symbolic names for
+collections of source maintained with \fBcvs\fP. If there is no
+`\|modules\|' file, developers must specify complete path names
+(absolute, or relative to \fB$CVSROOT\fP) for the files they wish to
+manage with \fBcvs\fP commands.
+.SP
+You can use the `\|commitinfo\|' file to define programs to execute
+whenever `\|\fBcvs commit\fP\|' is about to execute.
+These programs are used for ``pre-commit'' checking to verify that the
+modified, added, and removed files are really ready to be committed.
+Some uses for this check might be to turn off a portion (or all) of the
+source repository from a particular person or group.
+Or, perhaps, to verify that the changed files conform to the site's
+standards for coding practice.
+.SP
+You can use the `\|loginfo\|' file to define programs to execute after
+any
+.BR commit ,
+which writes a log entry for changes in the repository.
+These logging programs might be used to append the log message to a file.
+Or send the log message through electronic mail to a group of developers.
+Or, perhaps, post the log message to a particular newsgroup.
+.SP
+You can use the `\|rcsinfo\|' file to define forms for log messages.
+.SP
+You can use the `\|editinfo\|' file to define a program to execute for
+editing/validating `\|\fBcvs commit\fP\|' log entries.
+This is most useful when used with a `\|rcsinfo\|' forms specification, as
+it can verify that the proper fields of the form have been filled in by the
+user committing the change.
+.SP
+You can use the `\|cvsignore\|' file to specify the default list of
+files to ignore during \fBupdate\fP.
+.SP
+You can use the `\|history\|' file to record the \fBcvs\fP commands
+that affect the repository.
+The creation of this file enables history logging.
+.SH FILES
+.TP
+.B modules
+The `\|modules\|' file records your definitions of names for
+collections of source code. \fBcvs\fP will use these definitions if
+you create a file with the right format in
+`\|\fB$CVSROOT/CVSROOT/modules,v\fP\|'.
+The
+.BR mkmodules ( 1 )
+command should be run whenever the modules file changes, so that the
+appropriate files can be generated (depending on how you have configured
+.B cvs
+operation).
+.SP
+To allow convenient editing of the `\|modules\|' file itself, the file should
+include an entry like the following (where \fIlocalbin\fP represents the
+directory where your site installs programs like
+.BR mkmodules ( 1 )):
+.SP
+.nf
+\&\fBmodules \-i /\fP\fIlocalbin\fP\fB/mkmodules CVSROOT modules\fP
+.fi
+.SP
+This defines the name `\|\fBmodules\fP\|' as the module name for the
+file itself, so that you can use
+.SP
+.in +1i
+.ft B
+.nf
+example% cvs checkout modules
+.fi
+.ft P
+.in -1i
+.SP
+to get an editable copy of the file. You should define similar module
+entries for the other configuration files described here (except
+\&`\|history\|').
+The
+.BR cvsinit ( 1 )
+script will setup a smilar `\|modules\|' file for you automatically.
+.SP
+The `\|modules\|' file may contain blank lines and comments (lines
+beginning with `\|\fB#\fP\|') as well as module definitions.
+Long lines can be continued on the next line by specifying a backslash
+(``\e'') as the last character on the line.
+.SP
+A \fImodule definition\fP is a single line of the `\|modules\|' file,
+in either of two formats. In both cases, \fImname\fP represents the
+symbolic module name, and the remainder of the line is its definition.
+.SP
+\fImname\fP \fB\-a\fP \fIaliases\fP\|.\|.\|.
+.br
+This represents the simplest way of defining a module \fImname\fP.
+The `\|\fB\-a\fP\|' flags the definition as a simple alias: \fBcvs\fP
+will treat any use of \fImname\fP (as a command argument) as if the list
+of names \fIaliases\fP had been specified instead. \fIaliases\fP may
+contain either other module names or paths. When you use paths in
+\fIaliases\fP, `\|\fBcvs checkout\fP\|' creates all intermediate
+directories in the working directory, just as if the path had been
+specified explicitly in the \fBcvs\fP arguments.
+.SP
+.nf
+\fImname\fP [ \fIoptions\fP ] \fIdir\fP [ \fIfiles\fP\|.\|.\|. ] [ \fB&\fP\fImodule\fP\|.\|.\|. ]
+.fi
+.SP
+In the simplest case, this form of module definition reduces to
+`\|\fImname dir\fP\|'. This defines all the files in directory
+\fIdir\fP as module \fImname\fP. \fIdir\fP is a relative path (from
+\fB$CVSROOT\fP) to a directory of source in one of the source
+repositories. In this case, on \fBcheckout\fP, a single directory
+called \fImname\fP is created as a working directory; no intermediate
+directory levels are used by default, even if \fIdir\fP was a path
+involving several directory levels.
+.SP
+By explicitly specifying \fIfiles\fP in the module definition after
+\fIdir\fP, you can select particular files from directory
+\fIdir\fP. The sample definition for \fBmodules\fP is an example of
+a module defined with a single file from a particular directory. Here
+is another example:
+.SP
+.nf
+.ft B
+m4test unsupported/gnu/m4 foreach.m4 forloop.m4
+.ft P
+.fi
+.SP
+With this definition, executing `\|\fBcvs checkout m4test\fP\|'
+will create a single working directory `\|m4test\|' containing the two
+files listed, which both come from a common directory several levels
+deep in the \fBcvs\fP source repository.
+.SP
+A module definition can refer to other modules by including
+`\|\fB&\fP\fImodule\fP\|' in its definition. \fBcheckout\fP creates
+a subdirectory for each such \fImodule\fP, in your working directory.
+.br
+.I
+New in \fBcvs\fP 1.3;
+avoid this feature if sharing module definitions with older versions
+of \fBcvs\fP.
+.SP
+Finally, you can use one or more of the following \fIoptions\fP in
+module definitions:
+.SP
+\&`\|\fB\-d\fP \fIname\fP\|', to name the working directory something
+other than the module name.
+.br
+.I
+New in \fBcvs\fP 1.3;
+avoid this feature if sharing module definitions with older versions
+of \fBcvs\fP.
+.SP
+\&`\|\fB\-i\fP \fIprog\fP\|' allows you to specify a program \fIprog\fP
+to run whenever files in a module are committed. \fIprog\fP runs with a
+single argument, the full pathname of the affected directory in a
+source repository. The `\|commitinfo\|', `\|loginfo\|', and
+`\|editinfo\|' files provide other ways to call a program on \fBcommit\fP.
+.SP
+`\|\fB\-o\fP \fIprog\fP\|' allows you to specify a program \fIprog\fP
+to run whenever files in a module are checked out. \fIprog\fP runs
+with a single argument, the module name.
+.SP
+`\|\fB\-t\fP \fIprog\fP\|' allows you to specify a program \fIprog\fP
+to run whenever files in a module are tagged. \fIprog\fP runs with two
+arguments: the module name and the symbolic tag specified to \fBrtag\fP.
+.SP
+`\|\fB\-u\fP \fIprog\fP\|' allows you to specify a program \fIprog\fP
+to run whenever `\|\fBcvs update\fP\|' is executed from the top-level
+directory of the checked-out module. \fIprog\fP runs with a
+single argument, the full path to the source repository for this module.
+.TP
+\&\fBcommitinfo\fP, \fBloginfo\fP, \fBrcsinfo\fP, \fBeditinfo\fP
+These files all specify programs to call at different points in the
+`\|\fBcvs commit\fP\|' process. They have a common structure.
+Each line is a pair of fields: a regular expression, separated by
+whitespace from a filename or command-line template.
+Whenever one of the regular expression matches a directory name in the
+repository, the rest of the line is used.
+If the line begins with a \fB#\fP character, the entire line is considered
+a comment and is ignored.
+Whitespace between the fields is also ignored.
+.SP
+For `\|loginfo\|', the rest of the
+line is a command-line template to execute.
+The templates can include not only
+a program name, but whatever list of arguments you wish. If you write
+`\|\fB%s\fP\|' somewhere on the argument list, \fBcvs\fP supplies, at
+that point, the list of files affected by the \fBcommit\fP.
+The first entry in the list is the relative path within the source
+repository where the change is being made.
+The remaining arguments list the files that are being modified, added, or
+removed by this \fBcommit\fP invocation.
+.SP
+For `\|commitinfo\|', the rest of the line is a command-line template to
+execute.
+The template can include can include not only a program name, but whatever
+list of arguments you wish.
+The full path to the current source repository is appended to the template,
+followed by the file names of any files involved in the commit (added,
+removed, and modified files).
+.SP
+For `\|rcsinfo\|', the rest of the line is the full path to a file that
+should be loaded into the log message template.
+.SP
+For `\|editinfo\|', the rest of the line is a command-line template to
+execute.
+The template can include can include not only a program name, but whatever
+list of arguments you wish.
+The full path to the current log message template file is appended to the
+template.
+.SP
+You can use one of two special strings instead of a regular
+expression: `\|\fBALL\fP\|' specifies a command line template that
+must always be executed, and `\|\fBDEFAULT\fP\|' specifies a command
+line template to use if no regular expression is a match.
+.SP
+The `\|commitinfo\|' file contains commands to execute \fIbefore\fP any
+other \fBcommit\fP activity, to allow you to check any conditions that
+must be satisfied before \fBcommit\fP can proceed. The rest of the
+\fBcommit\fP will execute only if all selected commands from this file
+exit with exit status \fB0\fP.
+.SP
+The `\|rcsinfo\|' file allows you to specify \fIlog templates\fP for
+the \fBcommit\fP logging session; you can use this to provide a form
+to edit when filling out the \fBcommit\fP log. The field after the
+regular expression, in this file, contains filenames (of files
+containing the logging forms) rather than command templates.
+.SP
+The `\|editinfo\|' file allows you to execute a script \fIbefore the
+commit starts\fP, but after the log information is recorded. These
+"edit" scripts can verify information recorded in the log file. If
+the edit script exits wth a non-zero exit status, the commit is aborted.
+.SP
+The `\|loginfo\|' file contains commands to execute \fIat the end\fP
+of a commit. The text specified as a commit log message is piped
+through the command; typical uses include sending mail, filing an
+article in a newsgroup, or appending to a central file.
+.TP
+\&\fBcvsignore\fP, \fB.cvsignore\fP
+The default list of files (or
+.BR sh ( 1 )
+file name patterns) to ignore during `\|\fBcvs update\fP\|'.
+At startup time, \fBcvs\fP loads the compiled in default list of file name
+patterns (see
+.BR cvs ( 1 )).
+Then the per-repository list included in \fB$CVSROOT/CVSROOT/cvsignore\fP
+is loaded, if it exists.
+Then the per-user list is loaded from `\|$HOME/.cvsignore\|'.
+Finally, as \fBcvs\fP traverses through your directories, it will load any
+per-directory `\|.cvsignore\|' files whenever it finds one.
+These per-directory files are only valid for exactly the directory that
+contains them, not for any sub-directories.
+.TP
+.B history
+Create this file in \fB$CVSROOT/CVSROOT\fP to enable history logging
+(see the description of `\|\fBcvs history\fP\|').
+.SH "SEE ALSO"
+.BR cvs ( 1 ),
+.BR mkmodules ( 1 ).
+.SH COPYING
+Copyright \(co 1992 Cygnus Support, Brian Berliner, and Jeff Polk
+.PP
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+.PP
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+.PP
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
diff --git a/gnu/usr.bin/cvs/cvs/cvs.h b/gnu/usr.bin/cvs/cvs/cvs.h
new file mode 100644
index 0000000..3e1f8f4
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/cvs.h
@@ -0,0 +1,442 @@
+/* @(#)cvs.h 1.72 92/03/31 */
+
+#include "system.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <signal.h>
+#include "hash.h"
+#include "rcs.h"
+#include "regex.h"
+#include "fnmatch.h"
+#include "getopt.h"
+#include "wait.h"
+#include "config.h"
+#ifdef MY_NDBM
+#include "myndbm.h"
+#else
+#include <ndbm.h>
+#endif /* !MY_NDBM */
+
+/* XXX - for now this is static */
+#undef PATH_MAX
+#ifdef MAXPATHLEN
+#define PATH_MAX MAXPATHLEN+2
+#else
+#define PATH_MAX 1024+2
+#endif
+
+/* just in case this implementation does not define this */
+#ifndef L_tmpnam
+#define L_tmpnam 50
+#endif
+
+#if __STDC__
+#define CONST const
+#define PTR void *
+#else
+#define CONST
+#define PTR char *
+#endif
+
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Definitions for the CVS Administrative directory and the files it contains.
+ * Here as #define's to make changing the names a simple task.
+ */
+#define CVSADM "CVS"
+#define CVSADM_ENT "CVS/Entries"
+#define CVSADM_ENTBAK "CVS/Entries.Backup"
+#define CVSADM_ENTSTAT "CVS/Entries.Static"
+#define CVSADM_REP "CVS/Repository"
+#define CVSADM_CIPROG "CVS/Checkin.prog"
+#define CVSADM_UPROG "CVS/Update.prog"
+#define CVSADM_TAG "CVS/Tag"
+
+/*
+ * The following are obsolete and are maintained here only so that they can be
+ * cleaned up during the transition
+ */
+#define OCVSADM "CVS.adm" /* for CVS 1.2 and earlier */
+#define CVSADM_FILE "CVS/Files"
+#define CVSADM_MOD "CVS/Mod"
+
+/*
+ * Definitions for the CVSROOT Administrative directory and the files it
+ * contains. This directory is created as a sub-directory of the $CVSROOT
+ * environment variable, and holds global administration information for the
+ * entire source repository beginning at $CVSROOT.
+ */
+#define CVSROOTADM "CVSROOT"
+#define CVSROOTADM_MODULES "modules"
+#define CVSROOTADM_LOGINFO "loginfo"
+#define CVSROOTADM_RCSINFO "rcsinfo"
+#define CVSROOTADM_COMMITINFO "commitinfo"
+#define CVSROOTADM_EDITINFO "editinfo"
+#define CVSROOTADM_HISTORY "history"
+#define CVSROOTADM_IGNORE "cvsignore"
+#define CVSNULLREPOS "Emptydir" /* an empty directory */
+
+/* support for the modules file (CVSROOTADM_MODULES) */
+#define CVSMODULE_OPTS "ad:i:lo:s:t:u:"/* options in modules file */
+#define CVSMODULE_SPEC '&' /* special delimiter */
+
+/*
+ * The following are obsolete and are maintained here only so that they can be
+ * cleaned up during the transition
+ */
+#define OCVSROOTADM "CVSROOT.adm" /* for CVS 1.2 and earlier */
+
+/* Other CVS file names */
+#define CVSATTIC "Attic"
+#define CVSLCK "#cvs.lock"
+#define CVSTFL "#cvs.tfl"
+#define CVSRFL "#cvs.rfl"
+#define CVSWFL "#cvs.wfl"
+#define CVSEXT_OPT ",p"
+#define CVSEXT_LOG ",t"
+#define CVSPREFIX ",,"
+#define CVSDOTIGNORE ".cvsignore"
+
+/* miscellaneous CVS defines */
+#define CVSEDITPREFIX "CVS: "
+#define CVSLCKAGE (60*60) /* 1-hour old lock files cleaned up */
+#define CVSLCKSLEEP 30 /* wait 30 seconds before retrying */
+#define CVSBRANCH "1.1.1" /* RCS branch used for vendor srcs */
+#define BAKPREFIX ".#" /* when rcsmerge'ing */
+#define DEVNULL "/dev/null"
+
+#define FALSE 0
+#define TRUE 1
+
+/*
+ * Special tags. -rHEAD refers to the head of an RCS file, regardless of any
+ * sticky tags. -rBASE refers to the current revision the user has checked
+ * out This mimics the behaviour of RCS.
+ */
+#define TAG_HEAD "HEAD"
+#define TAG_BASE "BASE"
+
+/* Environment variable used by CVS */
+#define CVSREAD_ENV "CVSREAD" /* make files read-only */
+#define CVSREAD_DFLT FALSE /* writable files by default */
+
+#define RCSBIN_ENV "RCSBIN" /* RCS binary directory */
+/* #define RCSBIN_DFLT Set by config.h */
+
+#define EDITOR_ENV "EDITOR" /* which editor to use */
+/* #define EDITOR_DFLT Set by config.h */
+
+#define CVSROOT_ENV "CVSROOT" /* source directory root */
+#define CVSROOT_DFLT NULL /* No dflt; must set for checkout */
+
+#define IGNORE_ENV "CVSIGNORE" /* More files to ignore */
+
+/*
+ * If the beginning of the Repository matches the following string, strip it
+ * so that the output to the logfile does not contain a full pathname.
+ *
+ * If the CVSROOT environment variable is set, it overrides this define.
+ */
+#define REPOS_STRIP "/master/"
+
+/*
+ * The maximum number of files per each CVS directory. This is mainly for
+ * sizing arrays statically rather than dynamically. 3000 seems plenty for
+ * now.
+ */
+#define MAXFILEPERDIR 3000
+#define MAXLINELEN 5000 /* max input line from a file */
+#define MAXPROGLEN 30000 /* max program length to system() */
+#define MAXLISTLEN 40000 /* For [A-Z]list holders */
+#define MAXMESGLEN 10000 /* max RCS log message size */
+#define MAXDATELEN 50 /* max length for a date */
+
+/* The type of request that is being done in do_module() */
+enum mtype
+{
+ CHECKOUT, TAG, PATCH
+};
+
+/*
+ * defines for Classify_File() to determine the current state of a file.
+ * These are also used as types in the data field for the list we make for
+ * Update_Logfile in commit, import, and add.
+ */
+enum classify_type
+{
+ T_UNKNOWN = 1, /* no old-style analog existed */
+ T_CONFLICT, /* C (conflict) list */
+ T_NEEDS_MERGE, /* G (needs merging) list */
+ T_MODIFIED, /* M (needs checked in) list */
+ T_CHECKOUT, /* O (needs checkout) list */
+ T_ADDED, /* A (added file) list */
+ T_REMOVED, /* R (removed file) list */
+ T_REMOVE_ENTRY, /* W (removed entry) list */
+ T_UPTODATE, /* File is up-to-date */
+ T_TITLE /* title for node type */
+};
+typedef enum classify_type Ctype;
+
+/*
+ * a struct vers_ts contains all the information about a file including the
+ * user and rcs file names, and the version checked out and the head.
+ *
+ * this is usually obtained from a call to Version_TS which takes a tag argument
+ * for the RCS file if desired
+ */
+struct vers_ts
+{
+ char *vn_user; /* rcs version user file derives from
+ * it can have the following special
+ * values:
+ * empty = no user file
+ * 0 = user file is new
+ * -vers = user file to be removed */
+ char *vn_rcs; /* the verion for the rcs file
+ * (tag version?) */
+ char *ts_user; /* the timestamp for the user file */
+ char *ts_rcs; /* the user timestamp from entries */
+ char *options; /* opts from Entries file
+ * (keyword expansion) */
+ char *tag; /* tag stored in the Entries file */
+ char *date; /* date stored in the Entries file */
+ Entnode *entdata; /* pointer to entries file node */
+ RCSNode *srcfile; /* pointer to parsed src file info */
+};
+typedef struct vers_ts Vers_TS;
+
+/*
+ * structure used for list-private storage by ParseEntries() and
+ * Version_TS().
+ */
+struct stickydirtag
+{
+ int aflag;
+ char *tag;
+ char *date;
+ char *options;
+};
+
+/* flags for run_exec(), the fast system() for CVS */
+#define RUN_NORMAL 0x0000 /* no special behaviour */
+#define RUN_COMBINED 0x0001 /* stdout is duped to stderr */
+#define RUN_REALLY 0x0002 /* do the exec, even if noexec is on */
+#define RUN_STDOUT_APPEND 0x0004 /* append to stdout, don't truncate */
+#define RUN_STDERR_APPEND 0x0008 /* append to stderr, don't truncate */
+#define RUN_SIGIGNORE 0x0010 /* ignore interrupts for command */
+#define RUN_TTY (char *)0 /* for the benefit of lint */
+
+/* Flags for find_{names,dirs} routines */
+#define W_LOCAL 0x01 /* look for files locally */
+#define W_REPOS 0x02 /* look for files in the repository */
+#define W_ATTIC 0x04 /* look for files in the attic */
+
+/* Flags for return values of direnter procs for the recursion processor */
+enum direnter_type
+{
+ R_PROCESS = 1, /* process files and maybe dirs */
+ R_SKIP_FILES, /* don't process files in this dir */
+ R_SKIP_DIRS, /* don't process sub-dirs */
+ R_SKIP_ALL /* don't process files or dirs */
+};
+typedef enum direnter_type Dtype;
+
+extern char *program_name, *command_name;
+extern char *Rcsbin, *Editor, *CVSroot;
+extern char *CurDir;
+extern int really_quiet, quiet;
+extern int use_editor;
+extern int cvswrite;
+
+extern int trace; /* Show all commands */
+extern int noexec; /* Don't modify disk anywhere */
+extern int logoff; /* Don't write history entry */
+#ifdef FREEBSD_DEVELOPER
+extern int freebsd; /* Assume option defaults for FreBSD */
+#endif /* FREEBSD_DEVELOPER */
+
+/* Externs that are included directly in the CVS sources */
+#if __STDC__
+int Reader_Lock (char *xrepository);
+DBM *open_module (void);
+FILE *Fopen (char *name, char *mode);
+FILE *open_file (char *name, char *mode);
+List *Find_Dirs (char *repository, int which);
+List *ParseEntries (int aflag);
+char *Make_Date (char *rawdate);
+char *Name_Repository (char *dir, char *update_dir);
+char *Short_Repository (char *repository);
+char *getcaller (void);
+char *time_stamp (char *file);
+char *xmalloc (int bytes);
+char *xrealloc (char *ptr, int bytes);
+char *xstrdup (char *str);
+int No_Difference (char *file, Vers_TS * vers, List * entries);
+int Parse_Info (char *infofile, char *repository, int (*callproc) (), int all);
+int Reader_Lock (char *xrepository);
+int SIG_register (int sig, SIGTYPE (*fn) ());
+int Writer_Lock (List * list);
+int gethostname (char *name, int namelen);
+int ign_name (char *name);
+int isdir (char *file);
+int isfile (char *file);
+int islink (char *file);
+int isreadable (char *file);
+int iswritable (char *file);
+int link_file (char *from, char *to);
+int numdots (char *s);
+int run_exec (char *stin, char *stout, char *sterr, int flags);
+int unlink_file (char *f);
+int update (int argc, char *argv[]);
+int xcmp (char *file1, char *file2);
+int yesno (void);
+time_t get_date (char *date, struct timeb *now);
+void Create_Admin (char *dir, char *repository, char *tag, char *date);
+void Lock_Cleanup (void);
+void ParseTag (char **tagp, char **datep);
+void Scratch_Entry (List * list, char *fname);
+void WriteTag (char *dir, char *tag, char *date);
+void cat_module (int status);
+void check_entries (char *dir);
+void close_module (DBM * db);
+void copy_file (char *from, char *to);
+void error (int status, int errnum, char *message,...);
+void fperror (FILE * fp, int status, int errnum, char *message,...);
+void free_names (int *pargc, char *argv[]);
+void freevers_ts (Vers_TS ** versp);
+void ign_add (char *ign, int hold);
+void ign_add_file (char *file, int hold);
+void ign_setup (void);
+void line2argv (int *pargc, char *argv[], char *line);
+void make_directories (char *name);
+void make_directory (char *name);
+void rename_file (char *from, char *to);
+void run_arg (char *s);
+void run_args (char *fmt,...);
+void run_print (FILE * fp);
+void run_setup (char *fmt,...);
+void strip_path (char *path);
+void update_delproc (Node * p);
+void usage (char **cpp);
+void xchmod (char *fname, int writable);
+int Checkin (int type, char *file, char *repository, char *rcs, char *rev,
+ char *tag, char *message, List * entries);
+Ctype Classify_File (char *file, char *tag, char *date, char *options,
+ int force_tag_match, int aflag, char *repository,
+ List *entries, List *srcfiles, Vers_TS **versp);
+List *Find_Names (char *repository, int which, int aflag,
+ List ** optentries);
+void Register (List * list, char *fname, char *vn, char *ts,
+ char *options, char *tag, char *date);
+void Update_Logfile (char *repository, char *xmessage, char *xrevision,
+ FILE * xlogfp, List * xchanges);
+Vers_TS *Version_TS (char *repository, char *options, char *tag,
+ char *date, char *user, int force_tag_match,
+ int set_time, List * entries, List * xfiles);
+void do_editor (char *dir, char *message, char *repository,
+ List * changes);
+int do_module (DBM * db, char *mname, enum mtype m_type, char *msg,
+ int (*callback_proc) (), char *where, int shorten,
+ int local_specified, int run_module_prog, char *extra_arg);
+int do_recursion (int (*xfileproc) (), int (*xfilesdoneproc) (),
+ Dtype (*xdirentproc) (), int (*xdirleaveproc) (),
+ Dtype xflags, int xwhich, int xaflag, int xreadlock,
+ int xdosrcs);
+int do_update (int argc, char *argv[], char *xoptions, char *xtag,
+ char *xdate, int xforce, int local, int xbuild,
+ int xaflag, int xprune, int xpipeout, int which,
+ char *xjoin_rev1, char *xjoin_rev2,
+ char *xK_flag, char *preload_update_dir);
+void history_write (int type, char *update_dir, char *revs, char *name,
+ char *repository);
+int start_recursion (int (*fileproc) (), int (*filesdoneproc) (),
+ Dtype (*direntproc) (), int (*dirleaveproc) (),
+ int argc, char *argv[], int local, int which,
+ int aflag, int readlock, char *update_preload,
+ int dosrcs);
+void SIG_beginCrSect ();
+void SIG_endCrSect ();
+#else /* !__STDC__ */
+DBM *open_module ();
+FILE *Fopen ();
+FILE *open_file ();
+List *Find_Dirs ();
+List *Find_Names ();
+List *ParseEntries ();
+Vers_TS *Version_TS ();
+char *Make_Date ();
+char *Name_Repository ();
+char *Short_Repository ();
+char *getcaller ();
+char *time_stamp ();
+char *xmalloc ();
+char *xrealloc ();
+char *xstrdup ();
+int Checkin ();
+Ctype Classify_File ();
+int No_Difference ();
+int Parse_Info ();
+int Reader_Lock ();
+int SIG_register ();
+int Writer_Lock ();
+int do_module ();
+int do_recursion ();
+int do_update ();
+int gethostname ();
+int ign_name ();
+int isdir ();
+int isfile ();
+int islink ();
+int isreadable ();
+int iswritable ();
+int link_file ();
+int numdots ();
+int run_exec ();
+int start_recursion ();
+int unlink_file ();
+int update ();
+int xcmp ();
+int yesno ();
+time_t get_date ();
+void Create_Admin ();
+void Lock_Cleanup ();
+void ParseTag ();
+void ParseTag ();
+void Register ();
+void Scratch_Entry ();
+void Update_Logfile ();
+void WriteTag ();
+void cat_module ();
+void check_entries ();
+void close_module ();
+void copy_file ();
+void do_editor ();
+void error ();
+void fperror ();
+void free_names ();
+void freevers_ts ();
+void history_write ();
+void ign_add ();
+void ign_add_file ();
+void ign_setup ();
+void line2argv ();
+void make_directories ();
+void make_directory ();
+void rename_file ();
+void run_arg ();
+void run_args ();
+void run_print ();
+void run_setup ();
+void strip_path ();
+void update_delproc ();
+void usage ();
+void xchmod ();
+void SIG_beginCrSect ();
+void SIG_endCrSect ();
+#endif /* __STDC__ */
diff --git a/gnu/usr.bin/cvs/cvs/diff.c b/gnu/usr.bin/cvs/cvs/diff.c
new file mode 100644
index 0000000..003c2e3
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/diff.c
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Difference
+ *
+ * Run diff against versions in the repository. Options that are specified are
+ * passed on directly to "rcsdiff".
+ *
+ * Without any file arguments, runs diff against all the currently modified
+ * files.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)diff.c 1.52 92/04/10";
+#endif
+
+#if __STDC__
+static Dtype diff_dirproc (char *dir, char *pos_repos, char *update_dir);
+static int diff_filesdoneproc (int err, char *repos, char *update_dir);
+static int diff_dirleaveproc (char *dir, int err, char *update_dir);
+static int diff_file_nodiff (char *file, char *repository, List *entries,
+ List *srcfiles, Vers_TS *vers);
+static int diff_fileproc (char *file, char *update_dir, char *repository,
+ List * entries, List * srcfiles);
+static void diff_mark_errors (int err);
+#else
+static int diff_fileproc ();
+static Dtype diff_dirproc ();
+static int diff_filesdoneproc ();
+static int diff_dirleaveproc ();
+static int diff_file_nodiff ();
+static void diff_mark_errors ();
+#endif /* __STDC__ */
+
+static char *diff_rev1, *diff_rev2;
+static char *diff_date1, *diff_date2;
+static char *use_rev1, *use_rev2;
+static char *options;
+static char opts[PATH_MAX];
+static int diff_errors;
+
+static char *diff_usage[] =
+{
+ "Usage: %s %s [-l] [rcsdiff-options]\n",
+#ifdef CVS_DIFFDATE
+ " [[-r rev1 | -D date1] [-r rev2 | -D date2]] [files...] \n",
+#else
+ " [-r rev1 [-r rev2]] [files...] \n",
+#endif
+ "\t-l\tLocal directory only, not recursive\n",
+ "\t-D d1\tDiff revision for date against working file.\n",
+ "\t-D d2\tDiff rev1/date1 against date2.\n",
+ "\t-r rev1\tDiff revision for rev1 against working file.\n",
+ "\t-r rev2\tDiff rev1/date1 against rev2.\n",
+ NULL
+};
+
+int
+diff (argc, argv)
+ int argc;
+ char *argv[];
+{
+ char tmp[50];
+ int c, err = 0;
+ int local = 0;
+
+ if (argc == -1)
+ usage (diff_usage);
+
+ /*
+ * Note that we catch all the valid arguments here, so that we can
+ * intercept the -r arguments for doing revision diffs; and -l/-R for a
+ * non-recursive/recursive diff.
+ */
+ optind = 1;
+ while ((c = gnu_getopt (argc, argv,
+ "abcdefhilnpqtuw0123456789BHQRTC:D:F:I:L:V:k:r:")) != -1)
+ {
+ switch (c)
+ {
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'h': case 'i': case 'n': case 'p': case 't': case 'u':
+ case 'w': case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9': case 'B':
+ case 'H': case 'T': case 'Q':
+ (void) sprintf (tmp, " -%c", (char) c);
+ (void) strcat (opts, tmp);
+ if (c == 'Q')
+ {
+ quiet = 1;
+ really_quiet = 1;
+ c = 'q';
+ }
+ break;
+ case 'C': case 'F': case 'I': case 'L': case 'V':
+#ifndef CVS_DIFFDATE
+ case 'D':
+#endif
+ (void) sprintf (tmp, " -%c%s", (char) c, optarg);
+ (void) strcat (opts, tmp);
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case 'l':
+ local = 1;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'k':
+ if (options)
+ free (options);
+ options = RCS_check_kflag (optarg);
+ break;
+ case 'r':
+ if (diff_rev2 != NULL || diff_date2 != NULL)
+ error (1, 0,
+ "no more than two revisions/dates can be specified");
+ if (diff_rev1 != NULL || diff_date1 != NULL)
+ diff_rev2 = optarg;
+ else
+ diff_rev1 = optarg;
+ break;
+#ifdef CVS_DIFFDATE
+ case 'D':
+ if (diff_rev2 != NULL || diff_date2 != NULL)
+ error (1, 0,
+ "no more than two revisions/dates can be specified");
+ if (diff_rev1 != NULL || diff_date1 != NULL)
+ diff_date2 = Make_Date (optarg);
+ else
+ diff_date1 = Make_Date (optarg);
+ break;
+#endif
+ case '?':
+ default:
+ usage (diff_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* make sure options is non-null */
+ if (!options)
+ options = xstrdup ("");
+
+ /* start the recursion processor */
+ err = start_recursion (diff_fileproc, diff_filesdoneproc, diff_dirproc,
+ diff_dirleaveproc, argc, argv, local,
+ W_LOCAL, 0, 1, (char *) NULL, 1);
+
+ /* clean up */
+ free (options);
+ return (err);
+}
+
+/*
+ * Do a file diff
+ */
+/* ARGSUSED */
+static int
+diff_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ int status, err = 2; /* 2 == trouble, like rcsdiff */
+ Vers_TS *vers;
+
+ vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL,
+ file, 1, 0, entries, srcfiles);
+
+ if (vers->vn_user == NULL)
+ {
+ error (0, 0, "I know nothing about %s", file);
+ freevers_ts (&vers);
+ diff_mark_errors (err);
+ return (err);
+ }
+ else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
+ {
+ error (0, 0, "%s is a new entry, no comparison available", file);
+ freevers_ts (&vers);
+ diff_mark_errors (err);
+ return (err);
+ }
+ else if (vers->vn_user[0] == '-')
+ {
+ error (0, 0, "%s was removed, no comparison available", file);
+ freevers_ts (&vers);
+ diff_mark_errors (err);
+ return (err);
+ }
+ else
+ {
+ if (vers->vn_rcs == NULL && vers->srcfile == NULL)
+ {
+ error (0, 0, "cannot find revision control file for %s", file);
+ freevers_ts (&vers);
+ diff_mark_errors (err);
+ return (err);
+ }
+ else
+ {
+ if (vers->ts_user == NULL)
+ {
+ error (0, 0, "cannot find %s", file);
+ freevers_ts (&vers);
+ diff_mark_errors (err);
+ return (err);
+ }
+ }
+ }
+
+ if (diff_file_nodiff (file, repository, entries, srcfiles, vers))
+ {
+ freevers_ts (&vers);
+ return (0);
+ }
+
+ /* Output an "Index:" line for patch to use */
+ (void) fflush (stdout);
+ if (update_dir[0])
+ (void) printf ("Index: %s/%s\n", update_dir, file);
+ else
+ (void) printf ("Index: %s\n", file);
+ (void) fflush (stdout);
+
+ if (use_rev2)
+ {
+ run_setup ("%s%s %s %s -r%s -r%s", Rcsbin, RCS_DIFF,
+ opts, *options ? options : vers->options,
+ use_rev1, use_rev2);
+ }
+ else
+ {
+ run_setup ("%s%s %s %s -r%s", Rcsbin, RCS_DIFF, opts,
+ *options ? options : vers->options, use_rev1);
+ }
+ run_arg (vers->srcfile->path);
+
+ switch ((status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
+ RUN_REALLY|RUN_COMBINED)))
+ {
+ case -1: /* fork failed */
+ error (1, errno, "fork failed during rcsdiff of %s",
+ vers->srcfile->path);
+ case 0: /* everything ok */
+ err = 0;
+ break;
+ default: /* other error */
+ err = status;
+ break;
+ }
+
+ (void) fflush (stdout);
+ freevers_ts (&vers);
+ diff_mark_errors (err);
+ return (err);
+}
+
+/*
+ * Remember the exit status for each file.
+ */
+static void
+diff_mark_errors (err)
+ int err;
+{
+ if (err > diff_errors)
+ diff_errors = err;
+}
+
+/*
+ * Print a warm fuzzy message when we enter a dir
+ */
+/* ARGSUSED */
+static Dtype
+diff_dirproc (dir, pos_repos, update_dir)
+ char *dir;
+ char *pos_repos;
+ char *update_dir;
+{
+ /* XXX - check for dirs we don't want to process??? */
+ if (!quiet)
+ error (0, 0, "Diffing %s", update_dir);
+ return (R_PROCESS);
+}
+
+/*
+ * Concoct the proper exit status - done with files
+ */
+/* ARGSUSED */
+static int
+diff_filesdoneproc (err, repos, update_dir)
+ int err;
+ char *repos;
+ char *update_dir;
+{
+ return (diff_errors);
+}
+
+/*
+ * Concoct the proper exit status - leaving directories
+ */
+/* ARGSUSED */
+static int
+diff_dirleaveproc (dir, err, update_dir)
+ char *dir;
+ int err;
+ char *update_dir;
+{
+ return (diff_errors);
+}
+
+/*
+ * verify that a file is different 0=same 1=different
+ */
+static int
+diff_file_nodiff (file, repository, entries, srcfiles, vers)
+ char *file;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+ Vers_TS *vers;
+{
+ Vers_TS *xvers;
+ char tmp[L_tmpnam+1];
+
+ /* free up any old use_rev* variables and reset 'em */
+ if (use_rev1)
+ free (use_rev1);
+ if (use_rev2)
+ free (use_rev2);
+ use_rev1 = use_rev2 = (char *) NULL;
+
+ if (diff_rev1 || diff_date1)
+ {
+ /* special handling for TAG_HEAD */
+ if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0)
+ use_rev1 = xstrdup (vers->vn_rcs);
+ else
+ {
+ xvers = Version_TS (repository, (char *) NULL, diff_rev1,
+ diff_date1, file, 1, 0, entries, srcfiles);
+ if (xvers->vn_rcs == NULL)
+ {
+ if (diff_rev1)
+ error (0, 0, "tag %s is not in file %s", diff_rev1, file);
+ else
+ error (0, 0, "no revision for date %s in file %s",
+ diff_date1, file);
+ return (1);
+ }
+ use_rev1 = xstrdup (xvers->vn_rcs);
+ freevers_ts (&xvers);
+ }
+ }
+ if (diff_rev2 || diff_date2)
+ {
+ /* special handling for TAG_HEAD */
+ if (diff_rev2 && strcmp (diff_rev2, TAG_HEAD) == 0)
+ use_rev2 = xstrdup (vers->vn_rcs);
+ else
+ {
+ xvers = Version_TS (repository, (char *) NULL, diff_rev2,
+ diff_date2, file, 1, 0, entries, srcfiles);
+ if (xvers->vn_rcs == NULL)
+ {
+ if (diff_rev1)
+ error (0, 0, "tag %s is not in file %s", diff_rev2, file);
+ else
+ error (0, 0, "no revision for date %s in file %s",
+ diff_date2, file);
+ return (1);
+ }
+ use_rev2 = xstrdup (xvers->vn_rcs);
+ freevers_ts (&xvers);
+ }
+
+ /* now, see if we really need to do the diff */
+ return (strcmp (use_rev1, use_rev2) == 0);
+ }
+ if (use_rev1 == NULL || strcmp (use_rev1, vers->vn_user) == 0)
+ {
+ if (strcmp (vers->ts_rcs, vers->ts_user) == 0 &&
+ (!(*options) || strcmp (options, vers->options) == 0))
+ {
+ return (1);
+ }
+ if (use_rev1 == NULL)
+ use_rev1 = xstrdup (vers->vn_user);
+ }
+
+ /*
+ * with 0 or 1 -r option specified, run a quick diff to see if we
+ * should bother with it at all.
+ */
+ run_setup ("%s%s -p -q %s -r%s", Rcsbin, RCS_CO,
+ *options ? options : vers->options, use_rev1);
+ run_arg (vers->srcfile->path);
+ switch (run_exec (RUN_TTY, tmpnam (tmp), RUN_TTY, RUN_REALLY))
+ {
+ case 0: /* everything ok */
+ if (xcmp (file, tmp) == 0)
+ {
+ (void) unlink (tmp);
+ return (1);
+ }
+ break;
+ case -1: /* fork failed */
+ (void) unlink (tmp);
+ error (1, errno, "fork failed during checkout of %s",
+ vers->srcfile->path);
+ default:
+ break;
+ }
+ (void) unlink (tmp);
+ return (0);
+}
diff --git a/gnu/usr.bin/cvs/cvs/entries.c b/gnu/usr.bin/cvs/cvs/entries.c
new file mode 100644
index 0000000..6453e4f
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/entries.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Entries file to Files file
+ *
+ * Creates the file Files containing the names that comprise the project, from
+ * the Entries file.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)entries.c 1.37 92/03/31";
+#endif
+
+#if __STDC__
+static Node *AddEntryNode (List * list, char *name, char *version,
+ char *timestamp, char *options, char *tag,
+ char *date);
+#else
+static Node *AddEntryNode ();
+#endif /* __STDC__ */
+
+static FILE *entfile;
+static char *entfilename; /* for error messages */
+
+/*
+ * Write out the line associated with a node of an entries file
+ */
+static int
+write_ent_proc (node)
+ Node *node;
+{
+ Entnode *p;
+
+ p = (Entnode *) node->data;
+ if (fprintf (entfile, "/%s/%s/%s/%s/", node->key, p->version,
+ p->timestamp, p->options) == EOF)
+ error (1, errno, "cannot write %s", entfilename);
+ if (p->tag)
+ {
+ if (fprintf (entfile, "T%s\n", p->tag) == EOF)
+ error (1, errno, "cannot write %s", entfilename);
+ }
+ else if (p->date)
+ {
+ if (fprintf (entfile, "D%s\n", p->date) == EOF)
+ error (1, errno, "cannot write %s", entfilename);
+ }
+ else if (fprintf (entfile, "\n") == EOF)
+ error (1, errno, "cannot write %s", entfilename);
+ return (0);
+}
+
+/*
+ * write out the current entries file given a list, making a backup copy
+ * first of course
+ */
+static void
+write_entries (list)
+ List *list;
+{
+ /* open the new one and walk the list writing entries */
+ entfilename = CVSADM_ENTBAK;
+ entfile = open_file (entfilename, "w+");
+ (void) walklist (list, write_ent_proc);
+ if (fclose (entfile) == EOF)
+ error (1, errno, "error closing %s", entfilename);
+
+ /* now, atomically (on systems that support it) rename it */
+ rename_file (entfilename, CVSADM_ENT);
+}
+
+/*
+ * Removes the argument file from the Entries file if necessary.
+ */
+void
+Scratch_Entry (list, fname)
+ List *list;
+ char *fname;
+{
+ Node *node;
+
+ if (trace)
+ (void) fprintf (stderr, "-> Scratch_Entry(%s)\n", fname);
+
+ /* hashlookup to see if it is there */
+ if ((node = findnode (list, fname)) != NULL)
+ {
+ delnode (node); /* delete the node */
+ if (!noexec)
+ write_entries (list); /* re-write the file */
+ }
+}
+
+/*
+ * Enters the given file name/version/time-stamp into the Entries file,
+ * removing the old entry first, if necessary.
+ */
+void
+Register (list, fname, vn, ts, options, tag, date)
+ List *list;
+ char *fname;
+ char *vn;
+ char *ts;
+ char *options;
+ char *tag;
+ char *date;
+{
+ Node *node;
+
+ if (trace)
+ (void) fprintf (stderr, "-> Register(%s, %s, %s, %s, %s %s)\n",
+ fname, vn, ts, options, tag ? tag : "",
+ date ? date : "");
+ /* was it already there? */
+ if ((node = findnode (list, fname)) != NULL)
+ {
+ /* take it out */
+ delnode (node);
+
+ /* add the new one and re-write the file */
+ (void) AddEntryNode (list, fname, vn, ts, options, tag, date);
+ if (!noexec)
+ write_entries (list);
+ }
+ else
+ {
+ /* add the new one */
+ node = AddEntryNode (list, fname, vn, ts, options, tag, date);
+
+ if (!noexec)
+ {
+ /* append it to the end */
+ entfilename = CVSADM_ENT;
+ entfile = open_file (entfilename, "a");
+ (void) write_ent_proc (node);
+ if (fclose (entfile) == EOF)
+ error (1, errno, "error closing %s", entfilename);
+ }
+ }
+}
+
+/*
+ * Node delete procedure for list-private sticky dir tag/date info
+ */
+static void
+freesdt (p)
+ Node *p;
+{
+ struct stickydirtag *sdtp;
+
+ sdtp = (struct stickydirtag *) p->data;
+ if (sdtp->tag)
+ free (sdtp->tag);
+ if (sdtp->date)
+ free (sdtp->date);
+ if (sdtp->options)
+ free (sdtp->options);
+ free ((char *) sdtp);
+}
+
+/*
+ * Read the entries file into a list, hashing on the file name.
+ */
+List *
+ParseEntries (aflag)
+ int aflag;
+{
+ List *entries;
+ char line[MAXLINELEN];
+ char *cp, *user, *vn, *ts, *options;
+ char *tag_or_date, *tag, *date;
+ char *dirtag, *dirdate;
+ int lineno = 0;
+ FILE *fpin;
+
+ /* get a fresh list... */
+ entries = getlist ();
+
+ /*
+ * Parse the CVS/Tag file, to get any default tag/date settings. Use
+ * list-private storage to tuck them away for Version_TS().
+ */
+ ParseTag (&dirtag, &dirdate);
+ if (aflag || dirtag || dirdate)
+ {
+ struct stickydirtag *sdtp;
+
+ sdtp = (struct stickydirtag *) xmalloc (sizeof (*sdtp));
+ bzero ((char *) sdtp, sizeof (*sdtp));
+ sdtp->aflag = aflag;
+ sdtp->tag = xstrdup (dirtag);
+ sdtp->date = xstrdup (dirdate);
+
+ /* feed it into the list-private area */
+ entries->list->data = (char *) sdtp;
+ entries->list->delproc = freesdt;
+ }
+
+ again:
+ fpin = fopen (CVSADM_ENT, "r");
+ if (fpin == NULL)
+ error (0, errno, "cannot open %s for reading", CVSADM_ENT);
+ else
+ {
+ while (fgets (line, sizeof (line), fpin) != NULL)
+ {
+ lineno++;
+ if (line[0] == '/')
+ {
+ user = line + 1;
+ if ((cp = index (user, '/')) == NULL)
+ continue;
+ *cp++ = '\0';
+ vn = cp;
+ if ((cp = index (vn, '/')) == NULL)
+ continue;
+ *cp++ = '\0';
+ ts = cp;
+ if ((cp = index (ts, '/')) == NULL)
+ continue;
+ *cp++ = '\0';
+ options = cp;
+ if ((cp = index (options, '/')) == NULL)
+ continue;
+ *cp++ = '\0';
+ tag_or_date = cp;
+ if ((cp = index (tag_or_date, '\n')) == NULL)
+ continue;
+ *cp = '\0';
+ tag = (char *) NULL;
+ date = (char *) NULL;
+ if (*tag_or_date == 'T')
+ tag = tag_or_date + 1;
+ else if (*tag_or_date == 'D')
+ date = tag_or_date + 1;
+ (void) AddEntryNode (entries, user, vn, ts, options, tag, date);
+ }
+ else
+ {
+ /* try conversion only on first line */
+ if (lineno == 1)
+ {
+ (void) fclose (fpin);
+ check_entries ((char *) NULL);
+ goto again;
+ }
+ }
+ }
+ }
+
+ /* clean up and return */
+ if (fpin)
+ (void) fclose (fpin);
+ if (dirtag)
+ free (dirtag);
+ if (dirdate)
+ free (dirdate);
+ return (entries);
+}
+
+/*
+ * Look at the entries file to determine if it is in the old entries format.
+ * If so, convert it to the new format.
+ */
+void
+check_entries (dir)
+ char *dir;
+{
+ FILE *fpin, *fpout;
+ char tmp[MAXLINELEN];
+ char line[MAXLINELEN];
+ char entname[MAXLINELEN];
+ char entbak[MAXLINELEN];
+ char *cp, *user, *rev, *ts, *opt;
+
+ if (dir != NULL)
+ {
+ (void) sprintf (entname, "%s/%s", dir, CVSADM_ENT);
+ (void) sprintf (entbak, "%s/%s", dir, CVSADM_ENTBAK);
+ }
+ else
+ {
+ (void) strcpy (entname, CVSADM_ENT);
+ (void) strcpy (entbak, CVSADM_ENTBAK);
+ }
+
+ fpin = open_file (entname, "r");
+ if (fgets (line, sizeof (line), fpin) == NULL)
+ {
+ (void) fclose (fpin);
+ return;
+ }
+ (void) fclose (fpin);
+ if (line[0] != '/')
+ {
+ rename_file (entname, entbak);
+ fpin = open_file (entbak, "r");
+ fpout = open_file (entname, "w+");
+ while (fgets (line, sizeof (line), fpin) != NULL)
+ {
+ if (line[0] == '/')
+ {
+ if (fputs (line, fpout) == EOF)
+ error (1, errno, "cannot write %s", CVSADM_ENT);
+ continue;
+ }
+ rev = line;
+ if ((ts = index (line, '|')) == NULL)
+ continue;
+ *ts++ = '\0';
+ if ((user = rindex (ts, ' ')) == NULL)
+ continue;
+ *user++ = '\0';
+ if ((cp = index (user, '|')) == NULL)
+ continue;
+ *cp = '\0';
+ opt = "";
+#ifdef HAVE_RCS5
+#ifdef HAD_RCS4
+ opt = "-V4";
+#endif
+#endif
+ if (fprintf (fpout, "/%s/%s/%s/%s/\n", user, rev, ts, opt) == EOF)
+ error (1, errno, "cannot write %s", CVSADM_ENT);
+ }
+ (void) fclose (fpin);
+ if (fclose (fpout) == EOF)
+ error (1, errno, "cannot close %s", entname);
+
+ /* clean up any old Files or Mod files */
+ if (dir != NULL)
+ (void) sprintf (tmp, "%s/%s", dir, CVSADM_FILE);
+ else
+ (void) strcpy (tmp, CVSADM_FILE);
+ if (isfile (tmp))
+ (void) unlink (tmp);
+
+ if (dir != NULL)
+ (void) sprintf (tmp, "%s/%s", dir, CVSADM_MOD);
+ else
+ (void) strcpy (tmp, CVSADM_MOD);
+ if (isfile (tmp))
+ (void) unlink (tmp);
+ }
+}
+
+/*
+ * Free up the memory associated with the data section of an ENTRIES type
+ * node
+ */
+static void
+Entries_delproc (node)
+ Node *node;
+{
+ Entnode *p;
+
+ p = (Entnode *) node->data;
+ free (p->version);
+ free (p->timestamp);
+ free (p->options);
+ if (p->tag)
+ free (p->tag);
+ if (p->date)
+ free (p->date);
+ free ((char *) p);
+}
+
+/*
+ * Get an Entries file list node, initialize it, and add it to the specified
+ * list
+ */
+static Node *
+AddEntryNode (list, name, version, timestamp, options, tag, date)
+ List *list;
+ char *name;
+ char *version;
+ char *timestamp;
+ char *options;
+ char *tag;
+ char *date;
+{
+ Node *p;
+ Entnode *entdata;
+
+ /* get a node and fill in the regular stuff */
+ p = getnode ();
+ p->type = ENTRIES;
+ p->delproc = Entries_delproc;
+
+ /* this one gets a key of the name for hashing */
+ p->key = xstrdup (name);
+
+ /* malloc the data parts and fill them in */
+ p->data = xmalloc (sizeof (Entnode));
+ entdata = (Entnode *) p->data;
+ entdata->version = xstrdup (version);
+ entdata->timestamp = xstrdup (timestamp);
+ entdata->options = xstrdup (options);
+ if (entdata->options == NULL)
+ entdata->options = xstrdup ("");/* must be non-NULL */
+ entdata->tag = xstrdup (tag);
+ entdata->date = xstrdup (date);
+
+ /* put the node into the list */
+ if (addnode (list, p) != 0)
+ error (0, 0, "Duplicate filename in entries file (%s) -- ignored",
+ name);
+
+ return (p);
+}
+
+/*
+ * Write out/Clear the CVS/Tag file.
+ */
+void
+WriteTag (dir, tag, date)
+ char *dir;
+ char *tag;
+ char *date;
+{
+ FILE *fout;
+ char tmp[PATH_MAX];
+
+ if (noexec)
+ return;
+
+ if (dir == NULL)
+ (void) strcpy (tmp, CVSADM_TAG);
+ else
+ (void) sprintf (tmp, "%s/%s", dir, CVSADM_TAG);
+
+ if (tag || date)
+ {
+ fout = open_file (tmp, "w+");
+ if (tag)
+ {
+ if (fprintf (fout, "T%s\n", tag) == EOF)
+ error (1, errno, "write to %s failed", tmp);
+ }
+ else
+ {
+ if (fprintf (fout, "D%s\n", date) == EOF)
+ error (1, errno, "write to %s failed", tmp);
+ }
+ if (fclose (fout) == EOF)
+ error (1, errno, "cannot close %s", tmp);
+ }
+ else
+ (void) unlink_file (tmp);
+}
+
+/*
+ * Parse the CVS/Tag file for the current directory.
+ */
+void
+ParseTag (tagp, datep)
+ char **tagp;
+ char **datep;
+{
+ FILE *fp;
+ char line[MAXLINELEN];
+ char *cp;
+
+ if (tagp)
+ *tagp = (char *) NULL;
+ if (datep)
+ *datep = (char *) NULL;
+ fp = fopen (CVSADM_TAG, "r");
+ if (fp)
+ {
+ if (fgets (line, sizeof (line), fp) != NULL)
+ {
+ if ((cp = rindex (line, '\n')) != NULL)
+ *cp = '\0';
+ if (*line == 'T' && tagp)
+ *tagp = xstrdup (line + 1);
+ else if (*line == 'D' && datep)
+ *datep = xstrdup (line + 1);
+ }
+ (void) fclose (fp);
+ }
+}
diff --git a/gnu/usr.bin/cvs/cvs/find_names.c b/gnu/usr.bin/cvs/cvs/find_names.c
new file mode 100644
index 0000000..187bd23
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/find_names.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Find Names
+ *
+ * Finds all the pertinent file names, both from the administration and from the
+ * repository
+ *
+ * Find Dirs
+ *
+ * Finds all pertinent sub-directories of the checked out instantiation and the
+ * repository (and optionally the attic)
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)find_names.c 1.38 92/04/10";
+#endif
+
+#if __STDC__
+static int find_dirs (char *dir, List * list, int checkadm);
+static int find_rcs (char *dir, List * list);
+#else
+static int find_rcs ();
+static int find_dirs ();
+#endif /* __STDC__ */
+
+static List *filelist;
+
+/*
+ * add the key from entry on entries list to the files list
+ */
+static int
+add_entries_proc (node)
+ Node *node;
+{
+ Node *fnode;
+
+ fnode = getnode ();
+ fnode->type = FILES;
+ fnode->key = xstrdup (node->key);
+ if (addnode (filelist, fnode) != 0)
+ freenode (fnode);
+ return (0);
+}
+
+/*
+ * compare two files list node (for sort)
+ */
+static int
+fsortcmp (p, q)
+ Node *p, *q;
+{
+ return (strcmp (p->key, q->key));
+}
+
+List *
+Find_Names (repository, which, aflag, optentries)
+ char *repository;
+ int which;
+ int aflag;
+ List **optentries;
+{
+ List *entries;
+ List *files;
+ char dir[PATH_MAX];
+
+ /* make a list for the files */
+ files = filelist = getlist ();
+
+ /* look at entries (if necessary) */
+ if (which & W_LOCAL)
+ {
+ /* parse the entries file (if it exists) */
+ entries = ParseEntries (aflag);
+
+ if (entries != NULL)
+ {
+ /* walk the entries file adding elements to the files list */
+ (void) walklist (entries, add_entries_proc);
+
+ /* if our caller wanted the entries list, return it; else free it */
+ if (optentries != NULL)
+ *optentries = entries;
+ else
+ dellist (&entries);
+ }
+ }
+
+ if ((which & W_REPOS) && repository && !isreadable (CVSADM_ENTSTAT))
+ {
+ /* search the repository */
+ if (find_rcs (repository, files) != 0)
+ error (1, errno, "cannot open directory %s", repository);
+
+ /* search the attic too */
+ if (which & W_ATTIC)
+ {
+ (void) sprintf (dir, "%s/%s", repository, CVSATTIC);
+ (void) find_rcs (dir, files);
+ }
+ }
+
+ /* sort the list into alphabetical order and return it */
+ sortlist (files, fsortcmp);
+ return (files);
+}
+
+/*
+ * create a list of directories to traverse from the current directory
+ */
+List *
+Find_Dirs (repository, which)
+ char *repository;
+ int which;
+{
+ List *dirlist;
+
+ /* make a list for the directories */
+ dirlist = getlist ();
+
+ /* find the local ones */
+ if (which & W_LOCAL)
+ {
+ /* look only for CVS controlled sub-directories */
+ if (find_dirs (".", dirlist, 1) != 0)
+ error (1, errno, "cannot open current directory");
+ }
+
+ /* look for sub-dirs in the repository */
+ if ((which & W_REPOS) && repository)
+ {
+ /* search the repository */
+ if (find_dirs (repository, dirlist, 0) != 0)
+ error (1, errno, "cannot open directory %s", repository);
+
+#ifdef ATTIC_DIR_SUPPORT /* XXX - FIXME */
+ /* search the attic too */
+ if (which & W_ATTIC)
+ {
+ char dir[PATH_MAX];
+
+ (void) sprintf (dir, "%s/%s", repository, CVSATTIC);
+ (void) find_dirs (dir, dirlist, 0);
+ }
+#endif
+ }
+
+ /* sort the list into alphabetical order and return it */
+ sortlist (dirlist, fsortcmp);
+ return (dirlist);
+}
+
+/*
+ * Finds all the ,v files in the argument directory, and adds them to the
+ * files list. Returns 0 for success and non-zero if the argument directory
+ * cannot be opened.
+ */
+static int
+find_rcs (dir, list)
+ char *dir;
+ List *list;
+{
+ Node *p;
+ CONST char *regex_err;
+ char line[50];
+ struct direct *dp;
+ DIR *dirp;
+
+ /* set up to read the dir */
+ if ((dirp = opendir (dir)) == NULL)
+ return (1);
+
+ /* set up a regular expression to find the ,v files */
+ (void) sprintf (line, ".*%s$", RCSEXT);
+ if ((regex_err = re_comp (line)) != NULL)
+ error (1, 0, "%s", regex_err);
+
+ /* read the dir, grabbing the ,v files */
+ while ((dp = readdir (dirp)) != NULL)
+ {
+ if (re_exec (dp->d_name))
+ {
+ char *comma;
+
+ comma = rindex (dp->d_name, ','); /* strip the ,v */
+ *comma = '\0';
+ p = getnode ();
+ p->type = FILES;
+ p->key = xstrdup (dp->d_name);
+ if (addnode (list, p) != 0)
+ freenode (p);
+ }
+ }
+ (void) closedir (dirp);
+ return (0);
+}
+
+/*
+ * Finds all the subdirectories of the argument dir and adds them to the
+ * specified list. Sub-directories without a CVS administration directory
+ * are optionally ignored Returns 0 for success or 1 on error.
+ */
+static int
+find_dirs (dir, list, checkadm)
+ char *dir;
+ List *list;
+ int checkadm;
+{
+ Node *p;
+ CONST char *regex_err;
+ char tmp[PATH_MAX];
+ char admdir[PATH_MAX];
+ struct direct *dp;
+ DIR *dirp;
+
+ /* build a regex to blow off ,v files */
+ (void) sprintf (tmp, ".*%s$", RCSEXT);
+ if ((regex_err = re_comp (tmp)) != NULL)
+ error (1, 0, "%s", regex_err);
+
+ /* set up to read the dir */
+ if ((dirp = opendir (dir)) == NULL)
+ return (1);
+
+ /* read the dir, grabbing sub-dirs */
+ while ((dp = readdir (dirp)) != NULL)
+ {
+ if (strcmp (dp->d_name, ".") == 0 ||
+ strcmp (dp->d_name, "..") == 0 ||
+ strcmp (dp->d_name, CVSATTIC) == 0 ||
+ strcmp (dp->d_name, CVSLCK) == 0 ||
+ re_exec (dp->d_name)) /* don't bother stating ,v files */
+ continue;
+
+ (void) sprintf (tmp, "%s/%s", dir, dp->d_name);
+ if (isdir (tmp))
+ {
+ /* check for administration directories (if needed) */
+ if (checkadm)
+ {
+ /* blow off symbolic links to dirs in local dir */
+ if (islink (tmp))
+ continue;
+
+ /* check for new style */
+ (void) sprintf (admdir, "%s/%s", tmp, CVSADM);
+ if (!isdir (admdir))
+ {
+ /* and old style */
+ (void) sprintf (admdir, "%s/%s", tmp, OCVSADM);
+ if (!isdir (admdir))
+ continue;
+ }
+ }
+
+ /* put it in the list */
+ p = getnode ();
+ p->type = DIRS;
+ p->key = xstrdup (dp->d_name);
+ if (addnode (list, p) != 0)
+ freenode (p);
+ }
+ }
+ (void) closedir (dirp);
+ return (0);
+}
diff --git a/gnu/usr.bin/cvs/cvs/history.c b/gnu/usr.bin/cvs/cvs/history.c
new file mode 100644
index 0000000..9675c4c
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/history.c
@@ -0,0 +1,1373 @@
+/*
+ *
+ * You may distribute under the terms of the GNU General Public License
+ * as specified in the README file that comes with the CVS 1.0 kit.
+ *
+ * **************** History of Users and Module ****************
+ *
+ * LOGGING: Append record to "${CVSROOT}/CVSROOTADM/CVSROOTADM_HISTORY".
+ *
+ * On For each Tag, Add, Checkout, Commit, Update or Release command,
+ * one line of text is written to a History log.
+ *
+ * X date | user | CurDir | special | rev(s) | argument '\n'
+ *
+ * where: [The spaces in the example line above are not in the history file.]
+ *
+ * X is a single character showing the type of event:
+ * T "Tag" cmd.
+ * O "Checkout" cmd.
+ * F "Release" cmd.
+ * W "Update" cmd - No User file, Remove from Entries file.
+ * U "Update" cmd - File was checked out over User file.
+ * G "Update" cmd - File was merged successfully.
+ * C "Update" cmd - File was merged and shows overlaps.
+ * M "Commit" cmd - "Modified" file.
+ * A "Commit" cmd - "Added" file.
+ * R "Commit" cmd - "Removed" file.
+ *
+ * date is a fixed length 8-char hex representation of a Unix time_t.
+ * [Starting here, variable fields are delimited by '|' chars.]
+ *
+ * user is the username of the person who typed the command.
+ *
+ * CurDir The directory where the action occurred. This should be the
+ * absolute path of the directory which is at the same level as
+ * the "Repository" field (for W,U,G,C & M,A,R).
+ *
+ * Repository For record types [W,U,G,C,M,A,R] this field holds the
+ * repository read from the administrative data where the
+ * command was typed.
+ * T "A" --> New Tag, "D" --> Delete Tag
+ * Otherwise it is the Tag or Date to modify.
+ * O,F A "" (null field)
+ *
+ * rev(s) Revision number or tag.
+ * T The Tag to apply.
+ * O The Tag or Date, if specified, else "" (null field).
+ * F "" (null field)
+ * W The Tag or Date, if specified, else "" (null field).
+ * U The Revision checked out over the User file.
+ * G,C The Revision(s) involved in merge.
+ * M,A,R RCS Revision affected.
+ *
+ * argument The module (for [TOUF]) or file (for [WUGCMAR]) affected.
+ *
+ *
+ *** Report categories: "User" and "Since" modifiers apply to all reports.
+ * [For "sort" ordering see the "sort_order" routine.]
+ *
+ * Extract list of record types
+ *
+ * -e, -x [TOFWUGCMAR]
+ *
+ * Extracted records are simply printed, No analysis is performed.
+ * All "field" modifiers apply. -e chooses all types.
+ *
+ * Checked 'O'ut modules
+ *
+ * -o, -w
+ * Checked out modules. 'F' and 'O' records are examined and if
+ * the last record for a repository/file is an 'O', a line is
+ * printed. "-w" forces the "working dir" to be used in the
+ * comparison instead of the repository.
+ *
+ * Committed (Modified) files
+ *
+ * -c, -l, -w
+ * All 'M'odified, 'A'dded and 'R'emoved records are examined.
+ * "Field" modifiers apply. -l forces a sort by file within user
+ * and shows only the last modifier. -w works as in Checkout.
+ *
+ * Warning: Be careful with what you infer from the output of
+ * "cvs hi -c -l". It means the last time *you*
+ * changed the file, not the list of files for which
+ * you were the last changer!!!
+ *
+ * Module history for named modules.
+ * -m module, -l
+ *
+ * This is special. If one or more modules are specified, the
+ * module names are remembered and the files making up the
+ * modules are remembered. Only records matching exactly those
+ * files and repositories are shown. Sorting by "module", then
+ * filename, is implied. If -l ("last modified") is specified,
+ * then "update" records (types WUCG), tag and release records
+ * are ignored and the last (by date) "modified" record.
+ *
+ * TAG history
+ *
+ * -T All Tag records are displayed.
+ *
+ *** Modifiers.
+ *
+ * Since ... [All records contain a timestamp, so any report
+ * category can be limited by date.]
+ *
+ * -D date - The "date" is parsed into a Unix "time_t" and
+ * records with an earlier time stamp are ignored.
+ * -r rev/tag - A "rev" begins with a digit. A "tag" does not. If
+ * you use this option, every file is searched for the
+ * indicated rev/tag.
+ * -t tag - The "tag" is searched for in the history file and no
+ * record is displayed before the tag is found. An
+ * error is printed if the tag is never found.
+ * -b string - Records are printed only back to the last reference
+ * to the string in the "module", "file" or
+ * "repository" fields.
+ *
+ * Field Selections [Simple comparisons on existing fields. All field
+ * selections are repeatable.]
+ *
+ * -a - All users.
+ * -u user - If no user is given and '-a' is not given, only
+ * records for the user typing the command are shown.
+ * ==> If -a or -u is not specified, just use "self".
+ *
+ * -f filematch - Only records in which the "file" field contains the
+ * string "filematch" are considered.
+ *
+ * -p repository - Only records in which the "repository" string is a
+ * prefix of the "repos" field are considered.
+ *
+ * -m modulename - Only records which contain "modulename" in the
+ * "module" field are considered.
+ *
+ *
+ * EXAMPLES: ("cvs history", "cvs his" or "cvs hi")
+ *
+ *** Checked out files for username. (default self, e.g. "dgg")
+ * cvs hi [equivalent to: "cvs hi -o -u dgg"]
+ * cvs hi -u user [equivalent to: "cvs hi -o -u user"]
+ * cvs hi -o [equivalent to: "cvs hi -o -u dgg"]
+ *
+ *** Committed (modified) files from the beginning of the file.
+ * cvs hi -c [-u user]
+ *
+ *** Committed (modified) files since Midnight, January 1, 1990:
+ * cvs hi -c -D 'Jan 1 1990' [-u user]
+ *
+ *** Committed (modified) files since tag "TAG" was stored in the history file:
+ * cvs hi -c -t TAG [-u user]
+ *
+ *** Committed (modified) files since tag "TAG" was placed on the files:
+ * cvs hi -c -r TAG [-u user]
+ *
+ *** Who last committed file/repository X?
+ * cvs hi -c -l -[fp] X
+ *
+ *** Modified files since tag/date/file/repos?
+ * cvs hi -c {-r TAG | -D Date | -b string}
+ *
+ *** Tag history
+ * cvs hi -T
+ *
+ *** History of file/repository/module X.
+ * cvs hi -[fpn] X
+ *
+ *** History of user "user".
+ * cvs hi -e -u user
+ *
+ *** Dump (eXtract) specified record types
+ * cvs hi -x [TOFWUGCMAR]
+ *
+ *
+ * FUTURE: J[Join], I[Import] (Not currently implemented.)
+ *
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)history.c 1.31 92/04/10";
+#endif
+
+static struct hrec
+{
+ char *type; /* Type of record (In history record) */
+ char *user; /* Username (In history record) */
+ char *dir; /* "Compressed" Working dir (In history record) */
+ char *repos; /* (Tag is special.) Repository (In history record) */
+ char *rev; /* Revision affected (In history record) */
+ char *file; /* Filename (In history record) */
+ char *end; /* Ptr into repository to copy at end of workdir */
+ char *mod; /* The module within which the file is contained */
+ time_t date; /* Calculated from date stored in record */
+ int idx; /* Index of record, for "stable" sort. */
+} *hrec_head;
+
+
+#if __STDC__
+static char *fill_hrec (char *line, struct hrec * hr);
+static int accept_hrec (struct hrec * hr, struct hrec * lr);
+static int select_hrec (struct hrec * hr);
+static int sort_order (CONST PTR l, CONST PTR r);
+static int within (char *find, char *string);
+static time_t date_and_time (char *date_str);
+static void expand_modules (void);
+static void read_hrecs (char *fname);
+static void report_hrecs (void);
+static void save_file (char *dir, char *name, char *module);
+static void save_module (char *module);
+static void save_user (char *name);
+#else
+static int sort_order ();
+static time_t date_and_time ();
+static void save_user ();
+static void save_file ();
+static void save_module ();
+static void expand_modules ();
+static char *fill_hrec ();
+static void read_hrecs ();
+static int within ();
+static int select_hrec ();
+static void report_hrecs ();
+static int accept_hrec ();
+#endif /* __STDC__ */
+
+#define ALL_REC_TYPES "TOFWUCGMAR"
+#define USER_INCREMENT 2
+#define FILE_INCREMENT 128
+#define MODULE_INCREMENT 5
+#define HREC_INCREMENT 128
+
+static short report_count;
+
+static short extract;
+static short v_checkout;
+static short modified;
+static short tag_report;
+static short module_report;
+static short working;
+static short last_entry;
+static short all_users;
+
+static short user_sort;
+static short repos_sort;
+static short file_sort;
+static short module_sort;
+
+static time_t since_date;
+static char since_rev[20]; /* Maxrev ~= 99.99.99.999 */
+static char since_tag[64];
+static struct hrec *last_since_tag;
+static char backto[128];
+static struct hrec *last_backto;
+static char rec_types[20];
+
+static int hrec_count;
+static int hrec_max;
+
+static char **user_list; /* Ptr to array of ptrs to user names */
+static int user_max; /* Number of elements allocated */
+static int user_count; /* Number of elements used */
+
+static struct file_list_str
+{
+ char *l_file;
+ char *l_module;
+} *file_list; /* Ptr to array file name structs */
+static int file_max; /* Number of elements allocated */
+static int file_count; /* Number of elements used */
+
+static char **mod_list; /* Ptr to array of ptrs to module names */
+static int mod_max; /* Number of elements allocated */
+static int mod_count; /* Number of elements used */
+
+static int histsize;
+static char *histdata;
+static char *histfile; /* Ptr to the history file name */
+
+static char *history_usg[] =
+{
+ "Usage: %s %s [-report] [-flags] [-options args] [files...]\n\n",
+ " Reports:\n",
+ " -T Produce report on all TAGs\n",
+ " -c Committed (Modified) files\n",
+ " -o Checked out modules\n",
+ " -m <module> Look for specified module (repeatable)\n",
+ " -x [TOFWUCGMAR] Extract by record type\n",
+ " Flags:\n",
+ " -a All users (Default is self)\n",
+ " -e Everything (same as -x, but all record types)\n",
+ " -l Last modified (committed or modified report)\n",
+ " -w Working directory must match\n",
+ " Options:\n",
+ " -D <date> Since date (Many formats)\n",
+ " -b <str> Back to record with str in module/file/repos field\n",
+ " -f <file> Specified file (same as command line) (repeatable)\n",
+ " -n <modulename> In module (repeatable)\n",
+ " -p <repos> In repository (repeatable)\n",
+ " -r <rev/tag> Since rev or tag (looks inside RCS files!)\n",
+ " -t <tag> Since tag record placed in history file (by anyone).\n",
+ " -u <user> For user name (repeatable)\n",
+ NULL};
+
+/* Sort routine for qsort:
+ - If a user is selected at all, sort it first. User-within-file is useless.
+ - If a module was selected explicitly, sort next on module.
+ - Then sort by file. "File" is "repository/file" unless "working" is set,
+ then it is "workdir/file". (Revision order should always track date.)
+ - Always sort timestamp last.
+*/
+static int
+sort_order (l, r)
+ CONST PTR l;
+ CONST PTR r;
+{
+ int i;
+ CONST struct hrec *left = (CONST struct hrec *) l;
+ CONST struct hrec *right = (CONST struct hrec *) r;
+
+ if (user_sort) /* If Sort by username, compare users */
+ {
+ if ((i = strcmp (left->user, right->user)) != 0)
+ return (i);
+ }
+ if (module_sort) /* If sort by modules, compare module names */
+ {
+ if (left->mod && right->mod)
+ if ((i = strcmp (left->mod, right->mod)) != 0)
+ return (i);
+ }
+ if (repos_sort) /* If sort by repository, compare them. */
+ {
+ if ((i = strcmp (left->repos, right->repos)) != 0)
+ return (i);
+ }
+ if (file_sort) /* If sort by filename, compare files, NOT dirs. */
+ {
+ if ((i = strcmp (left->file, right->file)) != 0)
+ return (i);
+
+ if (working)
+ {
+ if ((i = strcmp (left->dir, right->dir)) != 0)
+ return (i);
+
+ if ((i = strcmp (left->end, right->end)) != 0)
+ return (i);
+ }
+ }
+
+ /*
+ * By default, sort by date, time
+ * XXX: This fails after 2030 when date slides into sign bit
+ */
+ if ((i = ((long) (left->date) - (long) (right->date))) != 0)
+ return (i);
+
+ /* For matching dates, keep the sort stable by using record index */
+ return (left->idx - right->idx);
+}
+
+static time_t
+date_and_time (date_str)
+ char *date_str;
+{
+ time_t t;
+
+ t = get_date (date_str, (struct timeb *) NULL);
+ if (t == (time_t) - 1)
+ error (1, 0, "Can't parse date/time: %s", date_str);
+ return (t);
+}
+
+int
+history (argc, argv)
+ int argc;
+ char **argv;
+{
+ int i, c;
+ char fname[PATH_MAX];
+
+ if (argc == -1)
+ usage (history_usg);
+
+ optind = 1;
+ while ((c = gnu_getopt (argc, argv, "Tacelow?D:b:f:m:n:p:r:t:u:x:X:")) != -1)
+ {
+ switch (c)
+ {
+ case 'T': /* Tag list */
+ report_count++;
+ tag_report++;
+ break;
+ case 'a': /* For all usernames */
+ all_users++;
+ break;
+ case 'c':
+ report_count++;
+ modified = 1;
+ break;
+ case 'e':
+ report_count++;
+ extract++;
+ (void) strcpy (rec_types, ALL_REC_TYPES);
+ break;
+ case 'l': /* Find Last file record */
+ last_entry = 1;
+ break;
+ case 'o':
+ report_count++;
+ v_checkout = 1;
+ break;
+ case 'w': /* Match Working Dir (CurDir) fields */
+ working = 1;
+ break;
+ case 'X': /* Undocumented debugging flag */
+ histfile = optarg;
+ break;
+ case 'D': /* Since specified date */
+ if (*since_rev || *since_tag || *backto)
+ {
+ error (0, 0, "date overriding rev/tag/backto");
+ *since_rev = *since_tag = *backto = '\0';
+ }
+ since_date = date_and_time (optarg);
+ break;
+ case 'b': /* Since specified file/Repos */
+ if (since_date || *since_rev || *since_tag)
+ {
+ error (0, 0, "backto overriding date/rev/tag");
+ *since_rev = *since_tag = '\0';
+ since_date = 0;
+ }
+ if (strlen (optarg) >= sizeof (backto))
+ {
+ error (0, 0, "backto truncated to %d bytes",
+ sizeof (backto) - 1);
+ optarg[sizeof (backto) - 1] = '\0';
+ }
+ (void) strcpy (backto, optarg);
+ break;
+ case 'f': /* For specified file */
+ save_file ("", optarg, (char *) NULL);
+ break;
+ case 'm': /* Full module report */
+ report_count++;
+ module_report++;
+ case 'n': /* Look for specified module */
+ save_module (optarg);
+ break;
+ case 'p': /* For specified directory */
+ save_file (optarg, "", (char *) NULL);
+ break;
+ case 'r': /* Since specified Tag/Rev */
+ if (since_date || *since_tag || *backto)
+ {
+ error (0, 0, "rev overriding date/tag/backto");
+ *since_tag = *backto = '\0';
+ since_date = 0;
+ }
+ (void) strcpy (since_rev, optarg);
+ break;
+ case 't': /* Since specified Tag/Rev */
+ if (since_date || *since_rev || *backto)
+ {
+ error (0, 0, "tag overriding date/marker/file/repos");
+ *since_rev = *backto = '\0';
+ since_date = 0;
+ }
+ (void) strcpy (since_tag, optarg); /* tag */
+ break;
+ case 'u': /* For specified username */
+ save_user (optarg);
+ break;
+ case 'x':
+ report_count++;
+ extract++;
+ {
+ char *cp;
+
+ for (cp = optarg; *cp; cp++)
+ if (!index (ALL_REC_TYPES, *cp))
+ error (1, 0, "%c is not a valid report type", cp);
+ }
+ (void) strcpy (rec_types, optarg);
+ break;
+ case '?':
+ default:
+ usage (history_usg);
+ break;
+ }
+ }
+ c = optind; /* Save the handled option count */
+
+ /* ================ Now analyze the arguments a bit */
+ if (!report_count)
+ v_checkout++;
+ else if (report_count > 1)
+ error (1, 0, "Only one report type allowed from: \"-Tcomx\".");
+
+ if (all_users)
+ save_user ("");
+
+ if (mod_list)
+ expand_modules ();
+
+ if (tag_report)
+ {
+ if (!index (rec_types, 'T'))
+ (void) strcat (rec_types, "T");
+ }
+ else if (extract)
+ {
+ if (user_list)
+ user_sort++;
+ }
+ else if (modified)
+ {
+ (void) strcpy (rec_types, "MAR");
+ /*
+ * If the user has not specified a date oriented flag ("Since"), sort
+ * by Repository/file before date. Default is "just" date.
+ */
+ if (!since_date && !*since_rev && !*since_tag && !*backto)
+ {
+ repos_sort++;
+ file_sort++;
+ /*
+ * If we are not looking for last_modified and the user specified
+ * one or more users to look at, sort by user before filename.
+ */
+ if (!last_entry && user_list)
+ user_sort++;
+ }
+ }
+ else if (module_report)
+ {
+ (void) strcpy (rec_types, last_entry ? "OMAR" : ALL_REC_TYPES);
+ module_sort++;
+ repos_sort++;
+ file_sort++;
+ working = 0; /* User's workdir doesn't count here */
+ }
+ else
+ /* Must be "checkout" or default */
+ {
+ (void) strcpy (rec_types, "OF");
+ /* See comments in "modified" above */
+ if (!last_entry && user_list)
+ user_sort++;
+ if (!since_date && !*since_rev && !*since_tag && !*backto)
+ file_sort++;
+ }
+
+ /* If no users were specified, use self (-a saves a universal ("") user) */
+ if (!user_list)
+ save_user (getcaller ());
+
+ /* If we're looking back to a Tag value, must consider "Tag" records */
+ if (*since_tag && !index (rec_types, 'T'))
+ (void) strcat (rec_types, "T");
+
+ argc -= c;
+ argv += c;
+ for (i = 0; i < argc; i++)
+ save_file ("", argv[i], (char *) NULL);
+
+ if (histfile)
+ (void) strcpy (fname, histfile);
+ else
+ (void) sprintf (fname, "%s/%s/%s", CVSroot,
+ CVSROOTADM, CVSROOTADM_HISTORY);
+
+ read_hrecs (fname);
+ qsort ((PTR) hrec_head, hrec_count, sizeof (struct hrec), sort_order);
+ report_hrecs ();
+
+ return (0);
+}
+
+void
+history_write (type, update_dir, revs, name, repository)
+ int type;
+ char *update_dir;
+ char *revs;
+ char *name;
+ char *repository;
+{
+ char fname[PATH_MAX], workdir[PATH_MAX], homedir[PATH_MAX];
+ static char username[20]; /* !!! Should be global */
+ FILE *fp;
+ char *slash = "", *cp, *cp2, *repos;
+ int i;
+ static char *tilde = "";
+ static char *PrCurDir = NULL;
+
+ if (logoff) /* History is turned off by cmd line switch */
+ return;
+ (void) sprintf (fname, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_HISTORY);
+
+ /* turn off history logging if the history file does not exist */
+ if (!isfile (fname))
+ {
+ logoff = 1;
+ return;
+ }
+
+ if (!(fp = Fopen (fname, "a"))) /* Some directory not there! */
+ return;
+
+ repos = Short_Repository (repository);
+
+ if (!PrCurDir)
+ {
+ struct passwd *pw;
+
+ (void) strcpy (username, getcaller ());
+ PrCurDir = CurDir;
+ if (!(pw = (struct passwd *) getpwnam (username)))
+ error (0, 0, "cannot find own username");
+ else
+ {
+ /* Assumes neither CurDir nor pw->pw_dir ends in '/' */
+ i = strlen (pw->pw_dir);
+ if (!strncmp (CurDir, pw->pw_dir, i))
+ {
+ PrCurDir += i; /* Point to '/' separator */
+ tilde = "~";
+ }
+ else
+ {
+ /* Try harder to find a "homedir" */
+ if (!getwd (workdir))
+ error (1, errno, "can't getwd in history");
+ if (chdir (pw->pw_dir) < 0)
+ error (1, errno, "can't chdir(%s)", pw->pw_dir);
+ if (!getwd (homedir))
+ error (1, errno, "can't getwd in:", pw->pw_dir);
+ (void) chdir (workdir);
+
+ i = strlen (homedir);
+ if (!strncmp (CurDir, homedir, i))
+ {
+ PrCurDir += i; /* Point to '/' separator */
+ tilde = "~";
+ }
+ }
+ }
+ }
+
+ if (type == 'T')
+ {
+ repos = update_dir;
+ update_dir = "";
+ }
+ else if (update_dir && *update_dir)
+ slash = "/";
+ else
+ update_dir = "";
+
+ (void) sprintf (workdir, "%s%s%s%s", tilde, PrCurDir, slash, update_dir);
+
+ /*
+ * "workdir" is the directory where the file "name" is. ("^~" == $HOME)
+ * "repos" is the Repository, relative to $CVSROOT where the RCS file is.
+ *
+ * "$workdir/$name" is the working file name.
+ * "$CVSROOT/$repos/$name,v" is the RCS file in the Repository.
+ *
+ * First, note that the history format was intended to save space, not
+ * to be human readable.
+ *
+ * The working file directory ("workdir") and the Repository ("repos")
+ * usually end with the same one or more directory elements. To avoid
+ * duplication (and save space), the "workdir" field ends with
+ * an integer offset into the "repos" field. This offset indicates the
+ * beginning of the "tail" of "repos", after which all characters are
+ * duplicates.
+ *
+ * In other words, if the "workdir" field has a '*' (a very stupid thing
+ * to put in a filename) in it, then every thing following the last '*'
+ * is a hex offset into "repos" of the first character from "repos" to
+ * append to "workdir" to finish the pathname.
+ *
+ * It might be easier to look at an example:
+ *
+ * M273b3463|dgg|~/work*9|usr/local/cvs/examples|1.2|loginfo
+ *
+ * Indicates that the workdir is really "~/work/cvs/examples", saving
+ * 10 characters, where "~/work*d" would save 6 characters and mean that
+ * the workdir is really "~/work/examples". It will mean more on
+ * directories like: usr/local/gnu/emacs/dist-19.17/lisp/term
+ *
+ * "workdir" is always an absolute pathname (~/xxx is an absolute path)
+ * "repos" is always a relative pathname. So we can assume that we will
+ * never run into the top of "workdir" -- there will always be a '/' or
+ * a '~' at the head of "workdir" that is not matched by anything in
+ * "repos". On the other hand, we *can* run off the top of "repos".
+ *
+ * Only "compress" if we save characters.
+ */
+
+ if (!repos)
+ repos = "";
+
+ cp = workdir + strlen (workdir) - 1;
+ cp2 = repos + strlen (repos) - 1;
+ for (i = 0; cp2 >= repos && cp > workdir && *cp == *cp2--; cp--)
+ i++;
+
+ if (i > 2)
+ {
+ i = strlen (repos) - i;
+ (void) sprintf ((cp + 1), "*%x", i);
+ }
+
+ if (fprintf (fp, "%c%08x|%s|%s|%s|%s|%s\n", type, time ((time_t *) NULL),
+ username, workdir, repos, revs ? revs : "", name) == EOF)
+ error (1, errno, "cannot write to history file: %s", fname);
+ (void) fclose (fp);
+}
+
+/*
+ * save_user() adds a user name to the user list to select. Zero-length
+ * username ("") matches any user.
+ */
+static void
+save_user (name)
+ char *name;
+{
+ if (user_count == user_max)
+ {
+ user_max += USER_INCREMENT;
+ user_list = (char **) xrealloc ((char *) user_list,
+ (int) user_max * sizeof (char *));
+ }
+ user_list[user_count++] = xstrdup (name);
+}
+
+/*
+ * save_file() adds file name and associated module to the file list to select.
+ *
+ * If "dir" is null, store a file name as is.
+ * If "name" is null, store a directory name with a '*' on the front.
+ * Else, store concatenated "dir/name".
+ *
+ * Later, in the "select" stage:
+ * - if it starts with '*', it is prefix-matched against the repository.
+ * - if it has a '/' in it, it is matched against the repository/file.
+ * - else it is matched against the file name.
+ */
+static void
+save_file (dir, name, module)
+ char *dir;
+ char *name;
+ char *module;
+{
+ char *cp;
+ struct file_list_str *fl;
+
+ if (file_count == file_max)
+ {
+ file_max += FILE_INCREMENT;
+ file_list = (struct file_list_str *) xrealloc ((char *) file_list,
+ file_max * sizeof (*fl));
+ }
+ fl = &file_list[file_count++];
+ fl->l_file = cp = xmalloc (strlen (dir) + strlen (name) + 2);
+ fl->l_module = module;
+
+ if (dir && *dir)
+ {
+ if (name && *name)
+ {
+ (void) strcpy (cp, dir);
+ (void) strcat (cp, "/");
+ (void) strcat (cp, name);
+ }
+ else
+ {
+ *cp++ = '*';
+ (void) strcpy (cp, dir);
+ }
+ }
+ else
+ {
+ if (name && *name)
+ {
+ (void) strcpy (cp, name);
+ }
+ else
+ {
+ error (0, 0, "save_file: null dir and file name");
+ }
+ }
+}
+
+static void
+save_module (module)
+ char *module;
+{
+ if (mod_count == mod_max)
+ {
+ mod_max += MODULE_INCREMENT;
+ mod_list = (char **) xrealloc ((char *) mod_list,
+ mod_max * sizeof (char *));
+ }
+ mod_list[mod_count++] = xstrdup (module);
+}
+
+static void
+expand_modules ()
+{
+}
+
+/* fill_hrec
+ *
+ * Take a ptr to 7-part history line, ending with a newline, for example:
+ *
+ * M273b3463|dgg|~/work*9|usr/local/cvs/examples|1.2|loginfo
+ *
+ * Split it into 7 parts and drop the parts into a "struct hrec".
+ * Return a pointer to the character following the newline.
+ */
+
+#define NEXT_BAR(here) do { while (isspace(*line)) line++; hr->here = line; while ((c = *line++) && c != '|') ; if (!c) return(rtn); *(line - 1) = '\0'; } while (0)
+
+static char *
+fill_hrec (line, hr)
+ char *line;
+ struct hrec *hr;
+{
+ char *cp, *rtn;
+ int c;
+ int off;
+ static int idx = 0;
+
+ bzero ((char *) hr, sizeof (*hr));
+ while (isspace (*line))
+ line++;
+ if (!(rtn = index (line, '\n')))
+ return ("");
+ *rtn++ = '\0';
+
+ hr->type = line++;
+ (void) sscanf (line, "%x", &hr->date);
+ while (*line && index ("0123456789abcdefABCDEF", *line))
+ line++;
+ if (*line == '\0')
+ return (rtn);
+
+ line++;
+ NEXT_BAR (user);
+ NEXT_BAR (dir);
+ if ((cp = rindex (hr->dir, '*')) != NULL)
+ {
+ *cp++ = '\0';
+ (void) sscanf (cp, "%x", &off);
+ hr->end = line + off;
+ }
+ else
+ hr->end = line - 1; /* A handy pointer to '\0' */
+ NEXT_BAR (repos);
+ NEXT_BAR (rev);
+ hr->idx = idx++;
+ if (index ("FOT", *(hr->type)))
+ hr->mod = line;
+
+ NEXT_BAR (file); /* This returns ptr to next line or final '\0' */
+ return (rtn); /* If it falls through, go on to next record */
+}
+
+/* read_hrecs's job is to read the history file and fill in all the "hrec"
+ * (history record) array elements with the ones we need to print.
+ *
+ * Logic:
+ * - Read the whole history file into a single buffer.
+ * - Walk through the buffer, parsing lines out of the buffer.
+ * 1. Split line into pointer and integer fields in the "next" hrec.
+ * 2. Apply tests to the hrec to see if it is wanted.
+ * 3. If it *is* wanted, bump the hrec pointer down by one.
+ */
+static void
+read_hrecs (fname)
+ char *fname;
+{
+ char *cp, *cp2;
+ int i, fd;
+ struct hrec *hr;
+ struct stat st_buf;
+
+ if ((fd = open (fname, O_RDONLY)) < 0)
+ error (1, errno, "cannot open history file: %s", fname);
+
+ if (fstat (fd, &st_buf) < 0)
+ error (1, errno, "can't stat history file");
+
+ /* Exactly enough space for lines data */
+ if (!(i = st_buf.st_size))
+ error (1, 0, "history file is empty");
+ histdata = cp = xmalloc (i + 2);
+ histsize = i;
+
+ if (read (fd, cp, i) != i)
+ error (1, errno, "cannot read log file");
+ (void) close (fd);
+
+ if (*(cp + i - 1) != '\n')
+ {
+ *(cp + i) = '\n'; /* Make sure last line ends in '\n' */
+ i++;
+ }
+ *(cp + i) = '\0';
+ for (cp2 = cp; cp2 - cp < i; cp2++)
+ {
+ if (*cp2 != '\n' && !isprint (*cp2))
+ *cp2 = ' ';
+ }
+
+ hrec_max = HREC_INCREMENT;
+ hrec_head = (struct hrec *) xmalloc (hrec_max * sizeof (struct hrec));
+
+ while (*cp)
+ {
+ if (hrec_count == hrec_max)
+ {
+ struct hrec *old_head = hrec_head;
+
+ hrec_max += HREC_INCREMENT;
+ hrec_head = (struct hrec *) xrealloc ((char *) hrec_head,
+ hrec_max * sizeof (struct hrec));
+ if (hrec_head != old_head)
+ {
+ if (last_since_tag)
+ last_since_tag = hrec_head + (last_since_tag - old_head);
+ if (last_backto)
+ last_backto = hrec_head + (last_backto - old_head);
+ }
+ }
+
+ hr = hrec_head + hrec_count;
+ cp = fill_hrec (cp, hr); /* cp == next line or '\0' at end of buffer */
+
+ if (select_hrec (hr))
+ hrec_count++;
+ }
+
+ /* Special selection problem: If "since_tag" is set, we have saved every
+ * record from the 1st occurrence of "since_tag", when we want to save
+ * records since the *last* occurrence of "since_tag". So what we have
+ * to do is bump hrec_head forward and reduce hrec_count accordingly.
+ */
+ if (last_since_tag)
+ {
+ hrec_count -= (last_since_tag - hrec_head);
+ hrec_head = last_since_tag;
+ }
+
+ /* Much the same thing is necessary for the "backto" option. */
+ if (last_backto)
+ {
+ hrec_count -= (last_backto - hrec_head);
+ hrec_head = last_backto;
+ }
+}
+
+/* Utility program for determining whether "find" is inside "string" */
+static int
+within (find, string)
+ char *find, *string;
+{
+ int c, len;
+
+ if (!find || !string)
+ return (0);
+
+ c = *find++;
+ len = strlen (find);
+
+ while (*string)
+ {
+ if (!(string = index (string, c)))
+ return (0);
+ string++;
+ if (!strncmp (find, string, len))
+ return (1);
+ }
+ return (0);
+}
+
+/* The purpose of "select_hrec" is to apply the selection criteria based on
+ * the command arguments and defaults and return a flag indicating whether
+ * this record should be remembered for printing.
+ */
+static int
+select_hrec (hr)
+ struct hrec *hr;
+{
+ char **cpp, *cp, *cp2;
+ struct file_list_str *fl;
+ int count;
+
+ /* "Since" checking: The argument parser guarantees that only one of the
+ * following four choices is set:
+ *
+ * 1. If "since_date" is set, it contains a Unix time_t specified on the
+ * command line. hr->date fields earlier than "since_date" are ignored.
+ * 2. If "since_rev" is set, it contains either an RCS "dotted" revision
+ * number (which is of limited use) or a symbolic TAG. Each RCS file
+ * is examined and the date on the specified revision (or the revision
+ * corresponding to the TAG) in the RCS file (CVSROOT/repos/file) is
+ * compared against hr->date as in 1. above.
+ * 3. If "since_tag" is set, matching tag records are saved. The field
+ * "last_since_tag" is set to the last one of these. Since we don't
+ * know where the last one will be, all records are saved from the
+ * first occurrence of the TAG. Later, at the end of "select_hrec"
+ * records before the last occurrence of "since_tag" are skipped.
+ * 4. If "backto" is set, all records with a module name or file name
+ * matching "backto" are saved. In addition, all records with a
+ * repository field with a *prefix* matching "backto" are saved.
+ * The field "last_backto" is set to the last one of these. As in
+ * 3. above, "select_hrec" adjusts to include the last one later on.
+ */
+ if (since_date)
+ {
+ if (hr->date < since_date)
+ return (0);
+ }
+ else if (*since_rev)
+ {
+ Vers_TS *vers;
+ time_t t;
+
+ vers = Version_TS (hr->repos, (char *) NULL, since_rev, (char *) NULL,
+ hr->file, 1, 0, (List *) NULL, (List *) NULL);
+ if (vers->vn_rcs)
+ {
+ if ((t = RCS_getrevtime (vers->srcfile, vers->vn_rcs, (char *) 0, 0))
+ != (time_t) 0)
+ {
+ if (hr->date < t)
+ {
+ freevers_ts (&vers);
+ return (0);
+ }
+ }
+ }
+ freevers_ts (&vers);
+ }
+ else if (*since_tag)
+ {
+ if (*(hr->type) == 'T')
+ {
+ /*
+ * A 'T'ag record, the "rev" field holds the tag to be set,
+ * while the "repos" field holds "D"elete, "A"dd or a rev.
+ */
+ if (within (since_tag, hr->rev))
+ {
+ last_since_tag = hr;
+ return (1);
+ }
+ else
+ return (0);
+ }
+ if (!last_since_tag)
+ return (0);
+ }
+ else if (*backto)
+ {
+ if (within (backto, hr->file) || within (backto, hr->mod) ||
+ within (backto, hr->repos))
+ last_backto = hr;
+ else
+ return (0);
+ }
+
+ /* User checking:
+ *
+ * Run down "user_list", match username ("" matches anything)
+ * If "" is not there and actual username is not there, return failure.
+ */
+ if (user_list && hr->user)
+ {
+ for (cpp = user_list, count = user_count; count; cpp++, count--)
+ {
+ if (!**cpp)
+ break; /* null user == accept */
+ if (!strcmp (hr->user, *cpp)) /* found listed user */
+ break;
+ }
+ if (!count)
+ return (0); /* Not this user */
+ }
+
+ /* Record type checking:
+ *
+ * 1. If Record type is not in rec_types field, skip it.
+ * 2. If mod_list is null, keep everything. Otherwise keep only modules
+ * on mod_list.
+ * 3. If neither a 'T', 'F' nor 'O' record, run through "file_list". If
+ * file_list is null, keep everything. Otherwise, keep only files on
+ * file_list, matched appropriately.
+ */
+ if (!index (rec_types, *(hr->type)))
+ return (0);
+ if (!index ("TFO", *(hr->type))) /* Don't bother with "file" if "TFO" */
+ {
+ if (file_list) /* If file_list is null, accept all */
+ {
+ for (fl = file_list, count = file_count; count; fl++, count--)
+ {
+ /* 1. If file_list entry starts with '*', skip the '*' and
+ * compare it against the repository in the hrec.
+ * 2. If file_list entry has a '/' in it, compare it against
+ * the concatenation of the repository and file from hrec.
+ * 3. Else compare the file_list entry against the hrec file.
+ */
+ char cmpfile[PATH_MAX];
+
+ if (*(cp = fl->l_file) == '*')
+ {
+ cp++;
+ /* if argument to -p is a prefix of repository */
+ if (!strncmp (cp, hr->repos, strlen (cp)))
+ {
+ hr->mod = fl->l_module;
+ break;
+ }
+ }
+ else
+ {
+ if (index (cp, '/'))
+ {
+ (void) sprintf (cp2 = cmpfile, "%s/%s",
+ hr->repos, hr->file);
+ }
+ else
+ {
+ cp2 = hr->file;
+ }
+
+ /* if requested file is found within {repos}/file fields */
+ if (within (cp, cp2))
+ {
+ hr->mod = fl->l_module;
+ break;
+ }
+ }
+ }
+ if (!count)
+ return (0); /* String specified and no match */
+ }
+ }
+ if (mod_list)
+ {
+ for (cpp = mod_list, count = mod_count; count; cpp++, count--)
+ {
+ if (hr->mod && !strcmp (hr->mod, *cpp)) /* found module */
+ break;
+ }
+ if (!count)
+ return (0); /* Module specified & this record is not one of them. */
+ }
+
+ return (1); /* Select this record unless rejected above. */
+}
+
+/* The "sort_order" routine (when handed to qsort) has arranged for the
+ * hrecs files to be in the right order for the report.
+ *
+ * Most of the "selections" are done in the select_hrec routine, but some
+ * selections are more easily done after the qsort by "accept_hrec".
+ */
+static void
+report_hrecs ()
+{
+ struct hrec *hr, *lr;
+ struct tm *tm;
+ int i, count, ty;
+ char *cp;
+ int user_len, file_len, rev_len, mod_len, repos_len;
+
+ if (*since_tag && !last_since_tag)
+ {
+ (void) printf ("No tag found: %s\n", since_tag);
+ return;
+ }
+ else if (*backto && !last_backto)
+ {
+ (void) printf ("No module, file or repository with: %s\n", backto);
+ return;
+ }
+ else if (hrec_count < 1)
+ {
+ (void) printf ("No records selected.\n");
+ return;
+ }
+
+ user_len = file_len = rev_len = mod_len = repos_len = 0;
+
+ /* Run through lists and find maximum field widths */
+ hr = lr = hrec_head;
+ hr++;
+ for (count = hrec_count; count--; lr = hr, hr++)
+ {
+ char repos[PATH_MAX];
+
+ if (!count)
+ hr = NULL;
+ if (!accept_hrec (lr, hr))
+ continue;
+
+ ty = *(lr->type);
+ (void) strcpy (repos, lr->repos);
+ if ((cp = rindex (repos, '/')) != NULL)
+ {
+ if (lr->mod && !strcmp (++cp, lr->mod))
+ {
+ (void) strcpy (cp, "*");
+ }
+ }
+ if ((i = strlen (lr->user)) > user_len)
+ user_len = i;
+ if ((i = strlen (lr->file)) > file_len)
+ file_len = i;
+ if (ty != 'T' && (i = strlen (repos)) > repos_len)
+ repos_len = i;
+ if (ty != 'T' && (i = strlen (lr->rev)) > rev_len)
+ rev_len = i;
+ if (lr->mod && (i = strlen (lr->mod)) > mod_len)
+ mod_len = i;
+ }
+
+ /* Walk through hrec array setting "lr" (Last Record) to each element.
+ * "hr" points to the record following "lr" -- It is NULL in the last
+ * pass.
+ *
+ * There are two sections in the loop below:
+ * 1. Based on the report type (e.g. extract, checkout, tag, etc.),
+ * decide whether the record should be printed.
+ * 2. Based on the record type, format and print the data.
+ */
+ for (lr = hrec_head, hr = (lr + 1); hrec_count--; lr = hr, hr++)
+ {
+ char workdir[PATH_MAX], repos[PATH_MAX];
+
+ if (!hrec_count)
+ hr = NULL;
+ if (!accept_hrec (lr, hr))
+ continue;
+
+ ty = *(lr->type);
+ tm = localtime (&(lr->date));
+ (void) printf ("%c %02d/%02d %02d:%02d %-*s", ty, tm->tm_mon + 1,
+ tm->tm_mday, tm->tm_hour, tm->tm_min, user_len, lr->user);
+
+ (void) sprintf (workdir, "%s%s", lr->dir, lr->end);
+ if ((cp = rindex (workdir, '/')) != NULL)
+ {
+ if (lr->mod && !strcmp (++cp, lr->mod))
+ {
+ (void) strcpy (cp, "*");
+ }
+ }
+ (void) strcpy (repos, lr->repos);
+ if ((cp = rindex (repos, '/')) != NULL)
+ {
+ if (lr->mod && !strcmp (++cp, lr->mod))
+ {
+ (void) strcpy (cp, "*");
+ }
+ }
+
+ switch (ty)
+ {
+ case 'T':
+ /* 'T'ag records: repository is a "tag type", rev is the tag */
+ (void) printf (" %-*s [%s:%s]", mod_len, lr->mod, lr->rev,
+ repos);
+ if (working)
+ (void) printf (" {%s}", workdir);
+ break;
+ case 'F':
+ case 'O':
+ if (lr->rev && *(lr->rev))
+ (void) printf (" [%s]", lr->rev);
+ (void) printf (" %-*s =%s%-*s %s", repos_len, repos, lr->mod,
+ mod_len + 1 - strlen (lr->mod), "=", workdir);
+ break;
+ case 'W':
+ case 'U':
+ case 'C':
+ case 'G':
+ case 'M':
+ case 'A':
+ case 'R':
+ (void) printf (" %-*s %-*s %-*s =%s= %s", rev_len, lr->rev,
+ file_len, lr->file, repos_len, repos,
+ lr->mod ? lr->mod : "", workdir);
+ break;
+ default:
+ (void) printf ("Hey! What is this junk? RecType[0x%2.2x]", ty);
+ break;
+ }
+ (void) putchar ('\n');
+ }
+}
+
+static int
+accept_hrec (lr, hr)
+ struct hrec *hr, *lr;
+{
+ int ty;
+
+ ty = *(lr->type);
+
+ if (last_since_tag && ty == 'T')
+ return (1);
+
+ if (v_checkout)
+ {
+ if (ty != 'O')
+ return (0); /* Only interested in 'O' records */
+
+ /* We want to identify all the states that cause the next record
+ * ("hr") to be different from the current one ("lr") and only
+ * print a line at the allowed boundaries.
+ */
+
+ if (!hr || /* The last record */
+ strcmp (hr->user, lr->user) || /* User has changed */
+ strcmp (hr->mod, lr->mod) ||/* Module has changed */
+ (working && /* If must match "workdir" */
+ (strcmp (hr->dir, lr->dir) || /* and the 1st parts or */
+ strcmp (hr->end, lr->end)))) /* the 2nd parts differ */
+
+ return (1);
+ }
+ else if (modified)
+ {
+ if (!last_entry || /* Don't want only last rec */
+ !hr || /* Last entry is a "last entry" */
+ strcmp (hr->repos, lr->repos) || /* Repository has changed */
+ strcmp (hr->file, lr->file))/* File has changed */
+ return (1);
+
+ if (working)
+ { /* If must match "workdir" */
+ if (strcmp (hr->dir, lr->dir) || /* and the 1st parts or */
+ strcmp (hr->end, lr->end)) /* the 2nd parts differ */
+ return (1);
+ }
+ }
+ else if (module_report)
+ {
+ if (!last_entry || /* Don't want only last rec */
+ !hr || /* Last entry is a "last entry" */
+ strcmp (hr->mod, lr->mod) ||/* Module has changed */
+ strcmp (hr->repos, lr->repos) || /* Repository has changed */
+ strcmp (hr->file, lr->file))/* File has changed */
+ return (1);
+ }
+ else
+ {
+ /* "extract" and "tag_report" always print selected records. */
+ return (1);
+ }
+
+ return (0);
+}
diff --git a/gnu/usr.bin/cvs/cvs/ignore.c b/gnu/usr.bin/cvs/cvs/ignore.c
new file mode 100644
index 0000000..8236487
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/ignore.c
@@ -0,0 +1,227 @@
+/*
+ * .cvsignore file support contributed by David G. Grubbs <dgg@ksr.com>
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)ignore.c 1.13 92/04/03";
+#endif
+
+/*
+ * Ignore file section.
+ *
+ * "!" may be included any time to reset the list (i.e. ignore nothing);
+ * "*" may be specified to ignore everything. It stays as the first
+ * element forever, unless a "!" clears it out.
+ */
+
+static char **ign_list; /* List of files to ignore in update
+ * and import */
+static char **s_ign_list = NULL;
+static int ign_count; /* Number of active entries */
+static int s_ign_count = 0;
+static int ign_size; /* This many slots available (plus
+ * one for a NULL) */
+static int ign_hold; /* Index where first "temporary" item
+ * is held */
+
+char *ign_default = ". .. core RCSLOG tags TAGS RCS SCCS .make.state .nse_depinfo #* .#* cvslog.* ,* CVS* .del-* *.a *.o *.so *.Z *~ *.old *.elc *.ln *.bak *.BAK *.orig *.rej";
+
+#define IGN_GROW 16 /* grow the list by 16 elements at a
+ * time */
+
+/*
+ * To the "ignore list", add the hard-coded default ignored wildcards above,
+ * the wildcards found in $CVSROOT/CVSROOT/cvsignore, the wildcards found in
+ * ~/.cvsignore and the wildcards found in the CVSIGNORE environment
+ * variable.
+ */
+void
+ign_setup ()
+{
+ extern char *getenv ();
+ struct passwd *pw;
+ char file[PATH_MAX];
+ char *tmp;
+
+ /* Start with default list and special case */
+ tmp = xstrdup (ign_default);
+ ign_add (tmp, 0);
+ free (tmp);
+
+ /* Then add entries found in repository, if it exists */
+ (void) sprintf (file, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_IGNORE);
+ if (isfile (file))
+ ign_add_file (file, 0);
+
+ /* Then add entries found in home dir, (if user has one) and file exists */
+ if ((pw = (struct passwd *) getpwuid (getuid ())) && pw->pw_dir)
+ {
+ (void) sprintf (file, "%s/%s", pw->pw_dir, CVSDOTIGNORE);
+ if (isfile (file))
+ ign_add_file (file, 0);
+ }
+
+ /* Then add entries found in CVSIGNORE environment variable. */
+ ign_add (getenv (IGNORE_ENV), 0);
+
+ /* Later, add ignore entries found in -I arguments */
+}
+
+/*
+ * Open a file and read lines, feeding each line to a line parser. Arrange
+ * for keeping a temporary list of wildcards at the end, if the "hold"
+ * argument is set.
+ */
+void
+ign_add_file (file, hold)
+ char *file;
+ int hold;
+{
+ FILE *fp;
+ char line[1024];
+
+ /* restore the saved list (if any) */
+ if (s_ign_list != NULL)
+ {
+ int i;
+
+ for (i = 0; i < s_ign_count; i++)
+ ign_list[i] = s_ign_list[i];
+ ign_count = s_ign_count;
+ ign_list[ign_count] = NULL;
+
+ s_ign_count = 0;
+ free (s_ign_list);
+ s_ign_list = NULL;
+ }
+
+ /* is this a temporary ignore file? */
+ if (hold)
+ {
+ /* re-set if we had already done a temporary file */
+ if (ign_hold)
+ {
+ int i;
+
+ for (i = ign_hold; i < ign_count; i++)
+ free (ign_list[i]);
+ ign_count = ign_hold;
+ ign_list[ign_count] = NULL;
+ }
+ else
+ {
+ ign_hold = ign_count;
+ }
+ }
+
+ /* load the file */
+ if (!(fp = fopen (file, "r")))
+ return;
+ while (fgets (line, sizeof (line), fp))
+ ign_add (line, hold);
+ (void) fclose (fp);
+}
+
+/* Parse a line of space-separated wildcards and add them to the list. */
+void
+ign_add (ign, hold)
+ char *ign;
+ int hold;
+{
+ if (!ign || !*ign)
+ return;
+
+ for (; *ign; ign++)
+ {
+ char *mark;
+ char save;
+
+ /* ignore whitespace before the token */
+ if (isspace (*ign))
+ continue;
+
+ /*
+ * if we find a single character !, we must re-set the ignore list
+ * (saving it if necessary). We also catch * as a special case in a
+ * global ignore file as an optimization
+ */
+ if ((!*(ign+1) || isspace (*(ign+1))) && (*ign == '!' || *ign == '*'))
+ {
+ if (!hold)
+ {
+ /* permanently reset the ignore list */
+ int i;
+
+ for (i = 0; i < ign_count; i++)
+ free (ign_list[i]);
+ ign_count = 0;
+ ign_list[0] = NULL;
+
+ /* if we are doing a '!', continue; otherwise add the '*' */
+ if (*ign == '!')
+ continue;
+ }
+ else if (*ign == '!')
+ {
+ /* temporarily reset the ignore list */
+ int i;
+
+ if (ign_hold)
+ {
+ for (i = ign_hold; i < ign_count; i++)
+ free (ign_list[i]);
+ ign_hold = 0;
+ }
+ s_ign_list = (char **) xmalloc (ign_count * sizeof (char *));
+ for (i = 0; i < ign_count; i++)
+ s_ign_list[i] = ign_list[i];
+ s_ign_count = ign_count;
+ ign_count = 0;
+ ign_list[0] = NULL;
+ continue;
+ }
+ }
+
+ /* If we have used up all the space, add some more */
+ if (ign_count >= ign_size)
+ {
+ ign_size += IGN_GROW;
+ ign_list = (char **) xrealloc ((char *) ign_list,
+ (ign_size + 1) * sizeof (char *));
+ }
+
+ /* find the end of this token */
+ for (mark = ign; *mark && !isspace (*mark); mark++)
+ /* do nothing */ ;
+
+ save = *mark;
+ *mark = '\0';
+
+ ign_list[ign_count++] = xstrdup (ign);
+ ign_list[ign_count] = NULL;
+
+ *mark = save;
+ if (save)
+ ign = mark;
+ else
+ ign = mark - 1;
+ }
+}
+
+/* Return 1 if the given filename should be ignored by update or import. */
+int
+ign_name (name)
+ char *name;
+{
+ char **cpp = ign_list;
+
+ if (cpp == NULL)
+ return (0);
+
+ while (*cpp)
+ if (fnmatch (*cpp++, name, 0) == 0)
+ return (1);
+ return (0);
+}
diff --git a/gnu/usr.bin/cvs/cvs/import.c b/gnu/usr.bin/cvs/cvs/import.c
new file mode 100644
index 0000000..288b9b2
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/import.c
@@ -0,0 +1,1011 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * "import" checks in the vendor release located in the current directory into
+ * the CVS source repository. The CVS vendor branch support is utilized.
+ *
+ * At least three arguments are expected to follow the options:
+ * repository Where the source belongs relative to the CVSROOT
+ * VendorTag Vendor's major tag
+ * VendorReleTag Tag for this particular release
+ *
+ * Additional arguments specify more Vendor Release Tags.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)import.c 1.52 92/03/31";
+#endif
+
+#define FILE_HOLDER ".#cvsxxx"
+
+#if __STDC__
+static char *get_comment (char *user);
+static int add_rcs_file (char *message, char *rcs, char *user, char *vtag,
+ int targc, char *targv[]);
+static int expand_at_signs (char *buf, off_t size, FILE *fp);
+static int add_rev (char *message, char *rcs, char *vfile, char *vers);
+static int add_tags (char *rcs, char *vfile, char *vtag, int targc,
+ char *targv[]);
+static int import_descend (char *message, char *vtag, int targc, char *targv[]);
+static int import_descend_dir (char *message, char *dir, char *vtag,
+ int targc, char *targv[]);
+static int process_import_file (char *message, char *vfile, char *vtag,
+ int targc, char *targv[]);
+static int update_rcs_file (char *message, char *vfile, char *vtag, int targc,
+ char *targv[]);
+static void add_log (int ch, char *fname);
+#else
+static int import_descend ();
+static int process_import_file ();
+static int update_rcs_file ();
+static int add_rev ();
+static int add_tags ();
+static char *get_comment ();
+static int add_rcs_file ();
+static int expand_at_signs ();
+static void add_log ();
+static int import_descend_dir ();
+#endif /* __STDC__ */
+
+static int repos_len;
+static char vhead[50];
+static char vbranch[50];
+static FILE *logfp;
+static char repository[PATH_MAX];
+static int conflicts;
+
+static char *import_usage[] =
+{
+ "Usage: %s %s [-Qq] [-I ign] [-m msg] [-b branch]\n",
+ " repository vendor-tag release-tags...\n",
+ "\t-Q\tReally quiet.\n",
+ "\t-q\tSomewhat quiet.\n",
+ "\t-I ign\tMore files to ignore (! to reset).\n",
+ "\t-b bra\tVendor branch id.\n",
+ "\t-m msg\tLog message.\n",
+ NULL
+};
+
+int
+import (argc, argv)
+ int argc;
+ char *argv[];
+{
+ char message[MAXMESGLEN];
+ char tmpfile[L_tmpnam+1];
+ char *cp;
+ int i, c, msglen, err;
+ List *ulist;
+ Node *p;
+
+ if (argc == -1)
+ usage (import_usage);
+
+ ign_setup ();
+
+ (void) strcpy (vbranch, CVSBRANCH);
+ message[0] = '\0';
+ optind = 1;
+ while ((c = gnu_getopt (argc, argv, "Qqb:m:I:")) != -1)
+ {
+ switch (c)
+ {
+ case 'Q':
+ really_quiet = 1;
+ /* FALL THROUGH */
+ case 'q':
+ quiet = 1;
+ break;
+ case 'b':
+ (void) strcpy (vbranch, optarg);
+ break;
+ case 'm':
+#ifdef FORCE_USE_EDITOR
+ use_editor = TRUE;
+#else
+ use_editor = FALSE;
+#endif
+ if (strlen (optarg) >= (sizeof (message) - 1))
+ {
+ error (0, 0, "warning: message too long; truncated!");
+ (void) strncpy (message, optarg, sizeof (message));
+ message[sizeof (message) - 2] = '\0';
+ }
+ else
+ (void) strcpy (message, optarg);
+ break;
+ case 'I':
+ ign_add (optarg, 0);
+ break;
+ case '?':
+ default:
+ usage (import_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 3)
+ usage (import_usage);
+
+ for (i = 1; i < argc; i++) /* check the tags for validity */
+ RCS_check_tag (argv[i]);
+
+ /* XXX - this should be a module, not just a pathname */
+ if (argv[0][0] != '/')
+ {
+ if (CVSroot == NULL)
+ {
+ error (0, 0, "missing CVSROOT environment variable\n");
+ error (1, 0, "Set it or specify the '-d' option to %s.",
+ program_name);
+ }
+ (void) sprintf (repository, "%s/%s", CVSroot, argv[0]);
+ repos_len = strlen (CVSroot);
+ }
+ else
+ {
+ (void) strcpy (repository, argv[0]);
+ repos_len = 0;
+ }
+
+ /*
+ * Consistency checks on the specified vendor branch. It must be
+ * composed of only numbers and dots ('.'). Also, for now we only
+ * support branching to a single level, so the specified vendor branch
+ * must only have two dots in it (like "1.1.1").
+ */
+ for (cp = vbranch; *cp != '\0'; cp++)
+ if (!isdigit (*cp) && *cp != '.')
+ error (1, 0, "%s is not a numeric branch", vbranch);
+ if (numdots (vbranch) != 2)
+ error (1, 0, "Only branches with two dots are supported: %s", vbranch);
+ (void) strcpy (vhead, vbranch);
+ cp = rindex (vhead, '.');
+ *cp = '\0';
+ if (use_editor)
+ do_editor ((char *) NULL, message, repository, (List *) NULL);
+ msglen = strlen (message);
+ if (msglen == 0 || message[msglen - 1] != '\n')
+ {
+ message[msglen] = '\n';
+ message[msglen + 1] = '\0';
+ }
+
+ /*
+ * Make all newly created directories writable. Should really use a more
+ * sophisticated security mechanism here.
+ */
+ (void) umask (2);
+ make_directories (repository);
+
+ /* Create the logfile that will be logged upon completion */
+ if ((logfp = fopen (tmpnam (tmpfile), "w+")) == NULL)
+ error (1, errno, "cannot create temporary file `%s'", tmpfile);
+ (void) unlink (tmpfile); /* to be sure it goes away */
+ (void) fprintf (logfp, "\nVendor Tag:\t%s\n", argv[1]);
+ (void) fprintf (logfp, "Release Tags:\t");
+ for (i = 2; i < argc; i++)
+ (void) fprintf (logfp, "%s\n\t\t", argv[i]);
+ (void) fprintf (logfp, "\n");
+
+ /* Just Do It. */
+ err = import_descend (message, argv[1], argc - 2, argv + 2);
+ if (conflicts)
+ {
+ if (!really_quiet)
+ {
+ (void) printf ("\n%d conflicts created by this import.\n",
+ conflicts);
+ (void) printf ("Use the following command to help the merge:\n\n");
+ (void) printf ("\t%s checkout -j%s:yesterday -j%s %s\n\n",
+ program_name, argv[1], argv[1], argv[0]);
+ }
+
+ (void) fprintf (logfp, "\n%d conflicts created by this import.\n",
+ conflicts);
+ (void) fprintf (logfp,
+ "Use the following command to help the merge:\n\n");
+ (void) fprintf (logfp, "\t%s checkout -j%s:yesterday -j%s %s\n\n",
+ program_name, argv[1], argv[1], argv[0]);
+ }
+ else
+ {
+ if (!really_quiet)
+ (void) printf ("\nNo conflicts created by this import\n\n");
+ (void) fprintf (logfp, "\nNo conflicts created by this import\n\n");
+ }
+
+ /*
+ * Write out the logfile and clean up.
+ */
+ ulist = getlist ();
+ p = getnode ();
+ p->type = UPDATE;
+ p->delproc = update_delproc;
+ p->key = xstrdup ("- Imported sources");
+ p->data = (char *) T_TITLE;
+ (void) addnode (ulist, p);
+ Update_Logfile (repository, message, vbranch, logfp, ulist);
+ dellist (&ulist);
+ (void) fclose (logfp);
+ return (err);
+}
+
+/*
+ * process all the files in ".", then descend into other directories.
+ */
+static int
+import_descend (message, vtag, targc, targv)
+ char *message;
+ char *vtag;
+ int targc;
+ char *targv[];
+{
+ DIR *dirp;
+ struct direct *dp;
+ int err = 0;
+ int has_dirs = 0;
+ FILE *links = (FILE *)0;
+
+ /* first, load up any per-directory ignore lists */
+ ign_add_file (CVSDOTIGNORE, 1);
+
+ if ((dirp = opendir (".")) == NULL)
+ {
+ err++;
+ }
+ else
+ {
+ while ((dp = readdir (dirp)) != NULL)
+ {
+ if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
+ continue;
+ if (ign_name (dp->d_name))
+ {
+ add_log ('I', dp->d_name);
+ continue;
+ }
+ if (isdir (dp->d_name))
+ {
+ has_dirs = 1;
+ }
+ else
+ {
+ if (islink (dp->d_name))
+ {
+#ifdef DO_LINKS
+ char lnbuf[PATH_MAX];
+ int lln;
+
+ add_log ('L', dp->d_name);
+ if ((lln = readlink(dp->d_name, lnbuf, PATH_MAX)) == -1) {
+ error(0, errno, "Can't read contents of symlink %s",
+ dp->d_name);
+ return (1);
+ }
+ else {
+ if (!links) {
+ char lnrep[PATH_MAX];
+
+ sprintf(lnrep, "%s/SymLinks", repository);
+ links = fopen(lnrep, "a+");
+ if (!links) {
+ error (0, errno,
+ "Can't open SymLinks file %s", lnrep);
+ return (1);
+ }
+ }
+ lnbuf[lln] = '\0';
+ fputs(dp->d_name, links);
+ fputc('\n', links);
+ fputs(lnbuf, links);
+ fputc('\n', links);
+ }
+#else
+ add_log ('L', dp->d_name);
+ err++;
+#endif
+ }
+ else
+ {
+ err += process_import_file (message, dp->d_name,
+ vtag, targc, targv);
+ }
+ }
+ }
+ (void) closedir (dirp);
+ }
+ if (has_dirs)
+ {
+ if ((dirp = opendir (".")) == NULL)
+ err++;
+ else
+ {
+ while ((dp = readdir (dirp)) != NULL)
+ {
+ if (!strcmp(".", dp->d_name) || !strcmp("..", dp->d_name))
+ continue;
+ if (!isdir (dp->d_name) || ign_name (dp->d_name))
+ continue;
+ err += import_descend_dir (message, dp->d_name,
+ vtag, targc, targv);
+ /* need to re-load .cvsignore after each dir traversal */
+ ign_add_file (CVSDOTIGNORE, 1);
+ }
+ (void) closedir (dirp);
+ }
+ }
+ if (links)
+ fclose(links);
+ return (err);
+}
+
+/*
+ * Process the argument import file.
+ */
+static int
+process_import_file (message, vfile, vtag, targc, targv)
+ char *message;
+ char *vfile;
+ char *vtag;
+ int targc;
+ char *targv[];
+{
+ char attic_name[PATH_MAX];
+ char rcs[PATH_MAX];
+
+ (void) sprintf (rcs, "%s/%s%s", repository, vfile, RCSEXT);
+ if (!isfile (rcs))
+ {
+ (void) sprintf (attic_name, "%s/%s/%s%s", repository, CVSATTIC,
+ vfile, RCSEXT);
+ if (!isfile (attic_name))
+ {
+
+ /*
+ * A new import source file; it doesn't exist as a ,v within the
+ * repository nor in the Attic -- create it anew.
+ */
+ add_log ('N', vfile);
+ return (add_rcs_file (message, rcs, vfile, vtag, targc, targv));
+ }
+ }
+
+ /*
+ * an rcs file exists. have to do things the official, slow, way.
+ */
+ return (update_rcs_file (message, vfile, vtag, targc, targv));
+}
+
+/*
+ * The RCS file exists; update it by adding the new import file to the
+ * (possibly already existing) vendor branch.
+ */
+static int
+update_rcs_file (message, vfile, vtag, targc, targv)
+ char *message;
+ char *vfile;
+ char *vtag;
+ int targc;
+ char *targv[];
+{
+ Vers_TS *vers;
+ char letter;
+ int ierrno;
+
+ vers = Version_TS (repository, (char *) NULL, vbranch, (char *) NULL, vfile,
+ 1, 0, (List *) NULL, (List *) NULL);
+ if (vers->vn_rcs != NULL)
+ {
+ char xtmpfile[50];
+ int different;
+ int retcode = 0;
+
+ /* XXX - should be more unique */
+ (void) sprintf (xtmpfile, "/tmp/%s", FILE_HOLDER);
+
+ /*
+ * The rcs file does have a revision on the vendor branch. Compare
+ * this revision with the import file; if they match exactly, there
+ * is no need to install the new import file as a new revision to the
+ * branch. Just tag the revision with the new import tags.
+ *
+ * This is to try to cut down the number of "C" conflict messages for
+ * locally modified import source files.
+ */
+#ifdef HAVE_RCS5
+ run_setup ("%s%s -q -f -r%s -p -ko", Rcsbin, RCS_CO, vers->vn_rcs);
+#else
+ run_setup ("%s%s -q -f -r%s -p", Rcsbin, RCS_CO, vers->vn_rcs);
+#endif
+ run_arg (vers->srcfile->path);
+ if ((retcode = run_exec (RUN_TTY, xtmpfile, RUN_TTY,
+ RUN_NORMAL|RUN_REALLY)) != 0)
+ {
+ ierrno = errno;
+ fperror (logfp, 0, retcode == -1 ? ierrno : 0,
+ "ERROR: cannot co revision %s of file %s", vers->vn_rcs,
+ vers->srcfile->path);
+ error (0, retcode == -1 ? ierrno : 0,
+ "ERROR: cannot co revision %s of file %s", vers->vn_rcs,
+ vers->srcfile->path);
+ (void) unlink_file (xtmpfile);
+ return (1);
+ }
+ different = xcmp (xtmpfile, vfile);
+ (void) unlink_file (xtmpfile);
+ if (!different)
+ {
+ int retval = 0;
+
+ /*
+ * The two files are identical. Just update the tags, print the
+ * "U", signifying that the file has changed, but needs no
+ * attention, and we're done.
+ */
+ if (add_tags (vers->srcfile->path, vfile, vtag, targc, targv))
+ retval = 1;
+ add_log ('U', vfile);
+ freevers_ts (&vers);
+ return (retval);
+ }
+ }
+
+ /* We may have failed to parse the RCS file; check just in case */
+ if (vers->srcfile == NULL || add_rev (message, vers->srcfile->path,
+ vfile, vers->vn_rcs) ||
+ add_tags (vers->srcfile->path, vfile, vtag, targc, targv))
+ {
+ freevers_ts (&vers);
+ return (1);
+ }
+
+ if (vers->srcfile->branch == NULL ||
+ strcmp (vers->srcfile->branch, vbranch) != 0)
+ {
+ conflicts++;
+ letter = 'C';
+ }
+ else
+ letter = 'U';
+ add_log (letter, vfile);
+
+ freevers_ts (&vers);
+ return (0);
+}
+
+/*
+ * Add the revision to the vendor branch
+ */
+static int
+add_rev (message, rcs, vfile, vers)
+ char *message;
+ char *rcs;
+ char *vfile;
+ char *vers;
+{
+ int locked, status, ierrno;
+ int retcode = 0;
+
+ if (noexec)
+ return (0);
+
+ locked = 0;
+ if (vers != NULL)
+ {
+ run_setup ("%s%s -q -l%s", Rcsbin, RCS, vbranch);
+ run_arg (rcs);
+ if ((retcode = run_exec (RUN_TTY, DEVNULL, DEVNULL, RUN_NORMAL)) == 0)
+ locked = 1;
+ else if (retcode == -1)
+ {
+ error (0, errno, "fork failed");
+ return (1);
+ }
+ }
+ if (link_file (vfile, FILE_HOLDER) < 0)
+ {
+ if (errno == EEXIST)
+ {
+ (void) unlink_file (FILE_HOLDER);
+ (void) link_file (vfile, FILE_HOLDER);
+ }
+ else
+ {
+ ierrno = errno;
+ fperror (logfp, 0, ierrno, "ERROR: cannot create link to %s", vfile);
+ error (0, ierrno, "ERROR: cannot create link to %s", vfile);
+ return (1);
+ }
+ }
+ run_setup ("%s%s -q -f -r%s", Rcsbin, RCS_CI, vbranch);
+ run_args ("-m%s", message);
+ run_arg (rcs);
+ status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ ierrno = errno;
+ rename_file (FILE_HOLDER, vfile);
+ if (status)
+ {
+ if (!noexec)
+ {
+ fperror (logfp, 0, status == -1 ? ierrno : 0, "ERROR: Check-in of %s failed", rcs);
+ error (0, status == -1 ? ierrno : 0, "ERROR: Check-in of %s failed", rcs);
+ }
+ if (locked)
+ {
+ run_setup ("%s%s -q -u%s", Rcsbin, RCS, vbranch);
+ run_arg (rcs);
+ (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ }
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Add the vendor branch tag and all the specified import release tags to the
+ * RCS file. The vendor branch tag goes on the branch root (1.1.1) while the
+ * vendor release tags go on the newly added leaf of the branch (1.1.1.1,
+ * 1.1.1.2, ...).
+ */
+static int
+add_tags (rcs, vfile, vtag, targc, targv)
+ char *rcs;
+ char *vfile;
+ char *vtag;
+ int targc;
+ char *targv[];
+{
+ int i, ierrno;
+ Vers_TS *vers;
+ int retcode = 0;
+
+ if (noexec)
+ return (0);
+
+ run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, vtag, vbranch);
+ run_arg (rcs);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
+ {
+ ierrno = errno;
+ fperror (logfp, 0, retcode == -1 ? ierrno : 0,
+ "ERROR: Failed to set tag %s in %s", vtag, rcs);
+ error (0, retcode == -1 ? ierrno : 0,
+ "ERROR: Failed to set tag %s in %s", vtag, rcs);
+ return (1);
+ }
+ vers = Version_TS (repository, (char *) NULL, vtag, (char *) NULL, vfile,
+ 1, 0, (List *) NULL, (List *) NULL);
+ for (i = 0; i < targc; i++)
+ {
+ run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, targv[i], vers->vn_rcs);
+ run_arg (rcs);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
+ {
+ ierrno = errno;
+ fperror (logfp, 0, retcode == -1 ? ierrno : 0,
+ "WARNING: Couldn't add tag %s to %s", targv[i], rcs);
+ error (0, retcode == -1 ? ierrno : 0,
+ "WARNING: Couldn't add tag %s to %s", targv[i], rcs);
+ }
+ }
+ freevers_ts (&vers);
+ return (0);
+}
+
+/*
+ * Stolen from rcs/src/rcsfnms.c, and adapted/extended.
+ */
+struct compair
+{
+ char *suffix, *comlead;
+};
+
+struct compair comtable[] =
+{
+
+/*
+ * comtable pairs each filename suffix with a comment leader. The comment
+ * leader is placed before each line generated by the $Log keyword. This
+ * table is used to guess the proper comment leader from the working file's
+ * suffix during initial ci (see InitAdmin()). Comment leaders are needed for
+ * languages without multiline comments; for others they are optional.
+ */
+ "a", "-- ", /* Ada */
+ "ada", "-- ",
+ "asm", ";; ", /* assembler (MS-DOS) */
+ "bat", ":: ", /* batch (MS-DOS) */
+ "c", " * ", /* C */
+ "c++", "// ", /* C++ in all its infinite guises */
+ "cc", "// ",
+ "cpp", "// ",
+ "cxx", "// ",
+ "cl", ";;; ", /* Common Lisp */
+ "cmd", ":: ", /* command (OS/2) */
+ "cmf", "c ", /* CM Fortran */
+ "cs", " * ", /* C* */
+ "csh", "# ", /* shell */
+ "e", "# ", /* efl */
+ "el", "; ", /* Emacs Lisp */
+ "f", "c ", /* Fortran */
+ "for", "c ",
+ "h", " * ", /* C-header */
+ "hh", "// ", /* C++ header */
+ "hpp", "// ",
+ "hxx", "// ",
+ "in", "# ", /* for Makefile.in */
+ "l", " * ", /* lex (conflict between lex and
+ * franzlisp) */
+ "mac", ";; ", /* macro (DEC-10, MS-DOS, PDP-11,
+ * VMS, etc) */
+ "me", ".\\\" ", /* me-macros t/nroff */
+ "ml", "; ", /* mocklisp */
+ "mm", ".\\\" ", /* mm-macros t/nroff */
+ "ms", ".\\\" ", /* ms-macros t/nroff */
+ "man", ".\\\" ", /* man-macros t/nroff */
+ "1", ".\\\" ", /* feeble attempt at man pages... */
+ "2", ".\\\" ",
+ "3", ".\\\" ",
+ "4", ".\\\" ",
+ "5", ".\\\" ",
+ "6", ".\\\" ",
+ "7", ".\\\" ",
+ "8", ".\\\" ",
+ "9", ".\\\" ",
+ "p", " * ", /* pascal */
+ "pas", " * ",
+ "pl", "# ", /* perl (conflict with Prolog) */
+ "ps", "% ", /* postscript */
+ "r", "# ", /* ratfor */
+ "red", "% ", /* psl/rlisp */
+#ifdef sparc
+ "s", "! ", /* assembler */
+#endif
+#ifdef mc68000
+ "s", "| ", /* assembler */
+#endif
+#ifdef pdp11
+ "s", "/ ", /* assembler */
+#endif
+#ifdef vax
+ "s", "# ", /* assembler */
+#endif
+#ifdef __ksr__
+ "s", "# ", /* assembler */
+ "S", "# ", /* Macro assembler */
+#endif
+ "sh", "# ", /* shell */
+ "sl", "% ", /* psl */
+ "tex", "% ", /* tex */
+ "y", " * ", /* yacc */
+ "ye", " * ", /* yacc-efl */
+ "yr", " * ", /* yacc-ratfor */
+ "", "# ", /* default for empty suffix */
+ NULL, "# " /* default for unknown suffix; */
+/* must always be last */
+};
+
+static char *
+get_comment (user)
+ char *user;
+{
+ char *cp, *suffix;
+ char suffix_path[PATH_MAX];
+ int i;
+
+ cp = rindex (user, '.');
+ if (cp != NULL)
+ {
+ cp++;
+
+ /*
+ * Convert to lower-case, since we are not concerned about the
+ * case-ness of the suffix.
+ */
+ (void) strcpy (suffix_path, cp);
+ for (cp = suffix_path; *cp; cp++)
+ if (isupper (*cp))
+ *cp = tolower (*cp);
+ suffix = suffix_path;
+ }
+ else
+ suffix = ""; /* will use the default */
+ for (i = 0;; i++)
+ {
+ if (comtable[i].suffix == NULL) /* default */
+ return (comtable[i].comlead);
+ if (strcmp (suffix, comtable[i].suffix) == 0)
+ return (comtable[i].comlead);
+ }
+}
+
+static int
+add_rcs_file (message, rcs, user, vtag, targc, targv)
+ char *message;
+ char *rcs;
+ char *user;
+ char *vtag;
+ int targc;
+ char *targv[];
+{
+ FILE *fprcs, *fpuser;
+ struct stat sb;
+ struct tm *ftm;
+ time_t now;
+ char altdate1[50], altdate2[50];
+ char *author, *buf;
+ int i, mode, ierrno, err = 0;
+
+ if (noexec)
+ return (0);
+
+ fprcs = open_file (rcs, "w+");
+ fpuser = open_file (user, "r");
+
+ /*
+ * putadmin()
+ */
+ if (fprintf (fprcs, "head %s;\n", vhead) == EOF ||
+ fprintf (fprcs, "branch %s;\n", vbranch) == EOF ||
+ fprintf (fprcs, "access ;\n") == EOF ||
+ fprintf (fprcs, "symbols ") == EOF)
+ {
+ goto write_error;
+ }
+
+ for (i = targc - 1; i >= 0; i--) /* RCS writes the symbols backwards */
+ if (fprintf (fprcs, "%s:%s.1 ", targv[i], vbranch) == EOF)
+ goto write_error;
+
+ if (fprintf (fprcs, "%s:%s;\n", vtag, vbranch) == EOF ||
+ fprintf (fprcs, "locks ; strict;\n") == EOF ||
+ /* XXX - make sure @@ processing works in the RCS file */
+ fprintf (fprcs, "comment @%s@;\n\n", get_comment (user)) == EOF)
+ {
+ goto write_error;
+ }
+
+ /*
+ * puttree()
+ */
+ (void) time (&now);
+#ifdef HAVE_RCS5
+ ftm = gmtime (&now);
+#else
+ ftm = localtime (&now);
+#endif
+ (void) sprintf (altdate1, DATEFORM,
+ ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
+ ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
+ ftm->tm_min, ftm->tm_sec);
+ now++;
+#ifdef HAVE_RCS5
+ ftm = gmtime (&now);
+#else
+ ftm = localtime (&now);
+#endif
+ (void) sprintf (altdate2, DATEFORM,
+ ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
+ ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
+ ftm->tm_min, ftm->tm_sec);
+ author = getcaller ();
+
+ if (fprintf (fprcs, "\n%s\n", vhead) == EOF ||
+ fprintf (fprcs, "date %s; author %s; state Exp;\n",
+ altdate1, author) == EOF ||
+ fprintf (fprcs, "branches %s.1;\n", vbranch) == EOF ||
+ fprintf (fprcs, "next ;\n") == EOF ||
+ fprintf (fprcs, "\n%s.1\n", vbranch) == EOF ||
+ fprintf (fprcs, "date %s; author %s; state Exp;\n",
+ altdate2, author) == EOF ||
+ fprintf (fprcs, "branches ;\n") == EOF ||
+ fprintf (fprcs, "next ;\n\n") == EOF ||
+ /*
+ * putdesc()
+ */
+ fprintf (fprcs, "\ndesc\n") == EOF ||
+ fprintf (fprcs, "@@\n\n\n") == EOF ||
+ /*
+ * putdelta()
+ */
+ fprintf (fprcs, "\n%s\n", vhead) == EOF ||
+ fprintf (fprcs, "log\n") == EOF ||
+ fprintf (fprcs, "@Initial revision\n@\n") == EOF ||
+ fprintf (fprcs, "text\n@") == EOF)
+ {
+ goto write_error;
+ }
+
+ if (fstat (fileno (fpuser), &sb) < 0)
+ error (1, errno, "cannot fstat %s", user);
+ if (sb.st_size > 0)
+ {
+ off_t size;
+
+ size = sb.st_size;
+ buf = xmalloc ((int) size);
+ if (fread (buf, (int) size, 1, fpuser) != 1)
+ error (1, errno, "cannot read file %s for copying", user);
+ if (expand_at_signs (buf, size, fprcs) == EOF)
+ goto write_error;
+ free (buf);
+ }
+ if (fprintf (fprcs, "@\n\n") == EOF ||
+ fprintf (fprcs, "\n%s.1\n", vbranch) == EOF ||
+ fprintf (fprcs, "log\n@") == EOF ||
+ expand_at_signs (message, (off_t) strlen (message), fprcs) == EOF ||
+ fprintf (fprcs, "@\ntext\n") == EOF ||
+ fprintf (fprcs, "@@\n") == EOF)
+ {
+ goto write_error;
+ }
+ if (fclose (fprcs) == EOF)
+ {
+ ierrno = errno;
+ goto write_error_noclose;
+ }
+ (void) fclose (fpuser);
+
+ /*
+ * Fix the modes on the RCS files. They must maintain the same modes as
+ * the original user file, except that all write permissions must be
+ * turned off.
+ */
+ mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH);
+ if (chmod (rcs, mode) < 0)
+ {
+ ierrno = errno;
+ fperror (logfp, 0, ierrno,
+ "WARNING: cannot change mode of file %s", rcs);
+ error (0, ierrno, "WARNING: cannot change mode of file %s", rcs);
+ err++;
+ }
+ return (err);
+
+write_error:
+ ierrno = errno;
+ (void) fclose (fprcs);
+write_error_noclose:
+ (void) fclose (fpuser);
+ fperror (logfp, 0, ierrno, "ERROR: cannot write file %s", rcs);
+ error (0, ierrno, "ERROR: cannot write file %s", rcs);
+ if (ierrno == ENOSPC)
+ {
+ (void) unlink (rcs);
+ fperror (logfp, 0, 0, "ERROR: out of space - aborting");
+ error (1, 0, "ERROR: out of space - aborting");
+ }
+ return (err + 1);
+}
+
+/*
+ * Sigh.. need to expand @ signs into double @ signs
+ */
+static int
+expand_at_signs (buf, size, fp)
+ char *buf;
+ off_t size;
+ FILE *fp;
+{
+ char *cp, *end;
+
+ for (cp = buf, end = buf + size; cp < end; cp++)
+ {
+ if (*cp == '@')
+ (void) putc ('@', fp);
+ if (putc (*cp, fp) == EOF)
+ return (EOF);
+ }
+ return (1);
+}
+
+/*
+ * Write an update message to (potentially) the screen and the log file.
+ */
+static void
+add_log (ch, fname)
+ char ch;
+ char *fname;
+{
+ if (!really_quiet) /* write to terminal */
+ {
+ if (repos_len)
+ (void) printf ("%c %s/%s\n", ch, repository + repos_len + 1, fname);
+ else if (repository[0])
+ (void) printf ("%c %s/%s\n", ch, repository, fname);
+ else
+ (void) printf ("%c %s\n", ch, fname);
+ }
+
+ if (repos_len) /* write to logfile */
+ (void) fprintf (logfp, "%c %s/%s\n", ch,
+ repository + repos_len + 1, fname);
+ else if (repository[0])
+ (void) fprintf (logfp, "%c %s/%s\n", ch, repository, fname);
+ else
+ (void) fprintf (logfp, "%c %s\n", ch, fname);
+}
+
+/*
+ * This is the recursive function that walks the argument directory looking
+ * for sub-directories that have CVS administration files in them and updates
+ * them recursively.
+ *
+ * Note that we do not follow symbolic links here, which is a feature!
+ */
+static int
+import_descend_dir (message, dir, vtag, targc, targv)
+ char *message;
+ char *dir;
+ char *vtag;
+ int targc;
+ char *targv[];
+{
+ char cwd[PATH_MAX];
+ char *cp;
+ int ierrno, err;
+
+ if (islink (dir))
+ return (0);
+ if (getwd (cwd) == NULL)
+ {
+ fperror (logfp, 0, 0, "ERROR: cannot get working directory: %s", cwd);
+ error (0, 0, "ERROR: cannot get working directory: %s", cwd);
+ return (1);
+ }
+ if (repository[0] == '\0')
+ (void) strcpy (repository, dir);
+ else
+ {
+ (void) strcat (repository, "/");
+ (void) strcat (repository, dir);
+ }
+ if (!quiet)
+ error (0, 0, "Importing %s", repository);
+ if (chdir (dir) < 0)
+ {
+ ierrno = errno;
+ fperror (logfp, 0, ierrno, "ERROR: cannot chdir to %s", repository);
+ error (0, ierrno, "ERROR: cannot chdir to %s", repository);
+ err = 1;
+ goto out;
+ }
+ if (!isdir (repository))
+ {
+ if (isfile (repository))
+ {
+ fperror (logfp, 0, 0, "ERROR: %s is a file, should be a directory!",
+ repository);
+ error (0, 0, "ERROR: %s is a file, should be a directory!",
+ repository);
+ err = 1;
+ goto out;
+ }
+ if (noexec == 0 && mkdir (repository, 0777) < 0)
+ {
+ ierrno = errno;
+ fperror (logfp, 0, ierrno,
+ "ERROR: cannot mkdir %s -- not added", repository);
+ error (0, ierrno,
+ "ERROR: cannot mkdir %s -- not added", repository);
+ err = 1;
+ goto out;
+ }
+ }
+ err = import_descend (message, vtag, targc, targv);
+ out:
+ if ((cp = rindex (repository, '/')) != NULL)
+ *cp = '\0';
+ else
+ repository[0] = '\0';
+ if (chdir (cwd) < 0)
+ error (1, errno, "cannot chdir to %s", cwd);
+ return (err);
+}
diff --git a/gnu/usr.bin/cvs/cvs/lock.c b/gnu/usr.bin/cvs/cvs/lock.c
new file mode 100644
index 0000000..5da23b3
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/lock.c
@@ -0,0 +1,522 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Set Lock
+ *
+ * Lock file support for CVS.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)lock.c 1.42 92/04/10";
+#endif
+
+extern char *ctime ();
+
+#if __STDC__
+static int readers_exist (char *repository);
+static int set_lock (char *lockdir, int will_wait, char *repository);
+static void set_lockers_name (struct stat *statp);
+static int set_writelock_proc (Node * p);
+static int unlock_proc (Node * p);
+static int write_lock (char *repository);
+static void unlock (char *repository);
+static void lock_wait ();
+#else
+static int unlock_proc ();
+static void unlock ();
+static int set_writelock_proc ();
+static int write_lock ();
+static int readers_exist ();
+static int set_lock ();
+static void set_lockers_name ();
+static void lock_wait ();
+#endif /* __STDC__ */
+
+static char lockers_name[20];
+static char *repository;
+static char readlock[PATH_MAX], writelock[PATH_MAX];
+static int cleanup_lckdir;
+static List *locklist;
+
+#define L_OK 0 /* success */
+#define L_ERROR 1 /* error condition */
+#define L_LOCK_OWNED 2 /* lock already owned by us */
+#define L_LOCKED 3 /* lock owned by someone else */
+
+/*
+ * Clean up all outstanding locks
+ */
+void
+Lock_Cleanup ()
+{
+ /* clean up simple locks (if any) */
+ if (repository != NULL)
+ {
+ unlock (repository);
+ repository = (char *) NULL;
+ }
+
+ /* clean up multiple locks (if any) */
+ if (locklist != (List *) NULL)
+ {
+ (void) walklist (locklist, unlock_proc);
+ locklist = (List *) NULL;
+ }
+}
+
+/*
+ * walklist proc for removing a list of locks
+ */
+static int
+unlock_proc (p)
+ Node *p;
+{
+ unlock (p->key);
+ return (0);
+}
+
+/*
+ * Remove the lock files (without complaining if they are not there),
+ */
+static void
+unlock (repository)
+ char *repository;
+{
+ char tmp[PATH_MAX];
+ struct stat sb;
+
+ if (readlock[0] != '\0')
+ {
+ (void) sprintf (tmp, "%s/%s", repository, readlock);
+ (void) unlink (tmp);
+ }
+
+ if (writelock[0] != '\0')
+ {
+ (void) sprintf (tmp, "%s/%s", repository, writelock);
+ (void) unlink (tmp);
+ }
+
+ /*
+ * Only remove the lock directory if it is ours, note that this does
+ * lead to the limitation that one user ID should not be committing
+ * files into the same Repository directory at the same time. Oh well.
+ */
+ (void) sprintf (tmp, "%s/%s", repository, CVSLCK);
+ if (stat (tmp, &sb) != -1 && sb.st_uid == geteuid () &&
+ (writelock[0] != '\0' || (readlock[0] != '\0' && cleanup_lckdir)))
+ {
+ (void) rmdir (tmp);
+ }
+ cleanup_lckdir = 0;
+}
+
+/*
+ * Create a lock file for readers
+ */
+int
+Reader_Lock (xrepository)
+ char *xrepository;
+{
+ int err = 0;
+ FILE *fp;
+ char tmp[PATH_MAX];
+
+ if (noexec)
+ return (0);
+
+ /* we only do one directory at a time for read locks! */
+ if (repository != NULL)
+ {
+ error (0, 0, "Reader_Lock called while read locks set - Help!");
+ return (1);
+ }
+
+ if (readlock[0] == '\0')
+ (void) sprintf (readlock, "%s.%d", CVSRFL, getpid ());
+
+ /* remember what we're locking (for lock_cleanup) */
+ repository = xrepository;
+
+ /* make sure we clean up on error */
+ (void) SIG_register (SIGHUP, Lock_Cleanup);
+ (void) SIG_register (SIGINT, Lock_Cleanup);
+ (void) SIG_register (SIGQUIT, Lock_Cleanup);
+ (void) SIG_register (SIGPIPE, Lock_Cleanup);
+ (void) SIG_register (SIGTERM, Lock_Cleanup);
+
+ /* make sure we can write the repository */
+ (void) sprintf (tmp, "%s/%s.%d", xrepository, CVSTFL, getpid ());
+ if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
+ {
+ error (0, errno, "cannot create read lock in repository `%s'",
+ xrepository);
+ readlock[0] = '\0';
+ (void) unlink (tmp);
+ return (1);
+ }
+ (void) unlink (tmp);
+
+ /* get the lock dir for our own */
+ (void) sprintf (tmp, "%s/%s", xrepository, CVSLCK);
+ if (set_lock (tmp, 1, xrepository) != L_OK)
+ {
+ error (0, 0, "failed to obtain dir lock in repository `%s'",
+ xrepository);
+ readlock[0] = '\0';
+ return (1);
+ }
+
+ /* write a read-lock */
+ (void) sprintf (tmp, "%s/%s", xrepository, readlock);
+ if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
+ {
+ error (0, errno, "cannot create read lock in repository `%s'",
+ xrepository);
+ readlock[0] = '\0';
+ err = 1;
+ }
+
+ /* free the lock dir */
+ (void) sprintf (tmp, "%s/%s", xrepository, CVSLCK);
+ if (rmdir (tmp) < 0)
+ error (0, errno, "failed to remove lock dir `%s'", tmp);
+
+ return (err);
+}
+
+/*
+ * Lock a list of directories for writing
+ */
+static char *lock_error_repos;
+static int lock_error;
+int
+Writer_Lock (list)
+ List *list;
+{
+ if (noexec)
+ return (0);
+
+ /* We only know how to do one list at a time */
+ if (locklist != (List *) NULL)
+ {
+ error (0, 0, "Writer_Lock called while write locks set - Help!");
+ return (1);
+ }
+
+ for (;;)
+ {
+ /* try to lock everything on the list */
+ lock_error = L_OK; /* init for set_writelock_proc */
+ lock_error_repos = (char *) NULL; /* init for set_writelock_proc */
+ locklist = list; /* init for Lock_Cleanup */
+ (void) strcpy (lockers_name, "unknown");
+
+ (void) walklist (list, set_writelock_proc);
+
+ switch (lock_error)
+ {
+ case L_ERROR: /* Real Error */
+ Lock_Cleanup (); /* clean up any locks we set */
+ error (0, 0, "lock failed - giving up");
+ return (1);
+
+ case L_LOCKED: /* Someone already had a lock */
+ Lock_Cleanup (); /* clean up any locks we set */
+ lock_wait (lock_error_repos); /* sleep a while and try again */
+ continue;
+
+ case L_OK: /* we got the locks set */
+ return (0);
+
+ default:
+ error (0, 0, "unknown lock status %d in Writer_Lock",
+ lock_error);
+ return (1);
+ }
+ }
+}
+
+/*
+ * walklist proc for setting write locks
+ */
+static int
+set_writelock_proc (p)
+ Node *p;
+{
+ /* if some lock was not OK, just skip this one */
+ if (lock_error != L_OK)
+ return (0);
+
+ /* apply the write lock */
+ lock_error_repos = p->key;
+ lock_error = write_lock (p->key);
+ return (0);
+}
+
+/*
+ * Create a lock file for writers returns L_OK if lock set ok, L_LOCKED if
+ * lock held by someone else or L_ERROR if an error occurred
+ */
+static int
+write_lock (repository)
+ char *repository;
+{
+ int status;
+ FILE *fp;
+ char tmp[PATH_MAX];
+
+ if (writelock[0] == '\0')
+ (void) sprintf (writelock, "%s.%d", CVSWFL, getpid ());
+
+ /* make sure we clean up on error */
+ (void) SIG_register (SIGHUP, Lock_Cleanup);
+ (void) SIG_register (SIGINT, Lock_Cleanup);
+ (void) SIG_register (SIGQUIT, Lock_Cleanup);
+ (void) SIG_register (SIGPIPE, Lock_Cleanup);
+ (void) SIG_register (SIGTERM, Lock_Cleanup);
+
+ /* make sure we can write the repository */
+ (void) sprintf (tmp, "%s/%s.%d", repository, CVSTFL, getpid ());
+ if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
+ {
+ error (0, errno, "cannot create write lock in repository `%s'",
+ repository);
+ (void) unlink (tmp);
+ return (L_ERROR);
+ }
+ (void) unlink (tmp);
+
+ /* make sure the lock dir is ours (not necessarily unique to us!) */
+ (void) sprintf (tmp, "%s/%s", repository, CVSLCK);
+ status = set_lock (tmp, 0, repository);
+ if (status == L_OK || status == L_LOCK_OWNED)
+ {
+ /* we now own a writer - make sure there are no readers */
+ if (readers_exist (repository))
+ {
+ /* clean up the lock dir if we created it */
+ if (status == L_OK)
+ {
+ if (rmdir (tmp) < 0)
+ error (0, errno, "failed to remove lock dir `%s'", tmp);
+ }
+
+ /* indicate we failed due to read locks instead of error */
+ return (L_LOCKED);
+ }
+
+ /* write the write-lock file */
+ (void) sprintf (tmp, "%s/%s", repository, writelock);
+ if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
+ {
+ int xerrno = errno;
+
+ (void) unlink (tmp);
+ /* free the lock dir if we created it */
+ if (status == L_OK)
+ {
+ (void) sprintf (tmp, "%s/%s", repository, CVSLCK);
+ if (rmdir (tmp) < 0)
+ error (0, errno, "failed to remove lock dir `%s'", tmp);
+ }
+
+ /* return the error */
+ error (0, xerrno, "cannot create write lock in repository `%s'",
+ repository);
+ return (L_ERROR);
+ }
+ return (L_OK);
+ }
+ else
+ return (status);
+}
+
+/*
+ * readers_exist() returns 0 if there are no reader lock files remaining in
+ * the repository; else 1 is returned, to indicate that the caller should
+ * sleep a while and try again.
+ */
+static int
+readers_exist (repository)
+ char *repository;
+{
+ char line[MAXLINELEN];
+ DIR *dirp;
+ struct direct *dp;
+ struct stat sb;
+ CONST char *regex_err;
+ int ret = 0;
+
+#ifdef CVS_FUDGELOCKS
+again:
+#endif
+
+ if ((dirp = opendir (repository)) == NULL)
+ error (1, 0, "cannot open directory %s", repository);
+
+ (void) sprintf (line, "^%s.*", CVSRFL);
+ if ((regex_err = re_comp (line)) != NULL)
+ error (1, 0, "%s", regex_err);
+
+ while ((dp = readdir (dirp)) != NULL)
+ {
+ (void) sprintf (line, "%s/%s", repository, dp->d_name);
+ if (re_exec (dp->d_name))
+ {
+#ifdef CVS_FUDGELOCKS
+ time_t now;
+
+ (void) time (&now);
+
+ /*
+ * If the create time of the file is more than CVSLCKAGE seconds
+ * ago, try to clean-up the lock file, and if successful, re-open
+ * the directory and try again.
+ */
+ if (stat (line, &sb) != -1)
+ {
+ if (now >= (sb.st_ctime + CVSLCKAGE) && unlink (line) != -1)
+ {
+ (void) closedir (dirp);
+ goto again;
+ }
+ set_lockers_name (&sb);
+ }
+#else
+ if (stat (line, &sb) != -1)
+ set_lockers_name (&sb);
+#endif
+ ret = 1;
+ break;
+ }
+ }
+ (void) closedir (dirp);
+ return (ret);
+}
+
+/*
+ * Set the static variable lockers_name appropriately, based on the stat
+ * structure passed in.
+ */
+static void
+set_lockers_name (statp)
+ struct stat *statp;
+{
+ struct passwd *pw;
+
+ if ((pw = (struct passwd *) getpwuid (statp->st_uid)) !=
+ (struct passwd *) NULL)
+ {
+ (void) strcpy (lockers_name, pw->pw_name);
+ }
+ else
+ (void) sprintf (lockers_name, "uid%d", statp->st_uid);
+}
+
+/*
+ * Persistently tries to make the directory "lckdir",, which serves as a
+ * lock. If the create time on the directory is greater than CVSLCKAGE
+ * seconds old, just try to remove the directory.
+ */
+static int
+set_lock (lockdir, will_wait, repository)
+ char *lockdir;
+ int will_wait;
+ char *repository;
+{
+ struct stat sb;
+#ifdef CVS_FUDGELOCKS
+ time_t now;
+#endif
+
+ /*
+ * Note that it is up to the callers of set_lock() to arrange for signal
+ * handlers that do the appropriate things, like remove the lock
+ * directory before they exit.
+ */
+ cleanup_lckdir = 0;
+ for (;;)
+ {
+ SIG_beginCrSect ();
+ if (mkdir (lockdir, 0777) == 0)
+ {
+ cleanup_lckdir = 1;
+ SIG_endCrSect ();
+ return (L_OK);
+ }
+ SIG_endCrSect ();
+
+ if (errno != EEXIST)
+ {
+ error (0, errno,
+ "failed to create lock directory in repository `%s'",
+ repository);
+ return (L_ERROR);
+ }
+
+ /*
+ * stat the dir - if it is non-existent, re-try the loop since
+ * someone probably just removed it (thus releasing the lock)
+ */
+ if (stat (lockdir, &sb) < 0)
+ {
+ if (errno == ENOENT)
+ continue;
+
+ error (0, errno, "couldn't stat lock directory `%s'", lockdir);
+ return (L_ERROR);
+ }
+
+ /*
+ * if we already own the lock, go ahead and return 1 which means it
+ * existed but we owned it
+ */
+ if (sb.st_uid == geteuid () && !will_wait)
+ return (L_LOCK_OWNED);
+
+#ifdef CVS_FUDGELOCKS
+
+ /*
+ * If the create time of the directory is more than CVSLCKAGE seconds
+ * ago, try to clean-up the lock directory, and if successful, just
+ * quietly retry to make it.
+ */
+ (void) time (&now);
+ if (now >= (sb.st_ctime + CVSLCKAGE))
+ {
+ if (rmdir (lockdir) >= 0)
+ continue;
+ }
+#endif
+
+ /* set the lockers name */
+ set_lockers_name (&sb);
+
+ /* if he wasn't willing to wait, return an error */
+ if (!will_wait)
+ return (L_LOCKED);
+ lock_wait (repository);
+ }
+}
+
+/*
+ * Print out a message that the lock is still held, then sleep a while.
+ */
+static void
+lock_wait (repos)
+ char *repos;
+{
+ time_t now;
+
+ (void) time (&now);
+ error (0, 0, "[%8.8s] waiting for %s's lock in %s", ctime (&now) + 11,
+ lockers_name, repos);
+ (void) sleep (CVSLCKSLEEP);
+}
diff --git a/gnu/usr.bin/cvs/cvs/log.c b/gnu/usr.bin/cvs/cvs/log.c
new file mode 100644
index 0000000..9e7ab8b
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/log.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Print Log Information
+ *
+ * Prints the RCS "log" (rlog) information for the specified files. With no
+ * argument, prints the log information for all the files in the directory
+ * (recursive by default).
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)log.c 1.39 92/03/31";
+#endif
+
+#if __STDC__
+static Dtype log_dirproc (char *dir, char *repository, char *update_dir);
+static int log_fileproc (char *file, char *update_dir, char *repository,
+ List * entries, List * srcfiles);
+#else
+static int log_fileproc ();
+static Dtype log_dirproc ();
+#endif /* __STDC__ */
+
+static char options[PATH_MAX];
+
+static char *log_usage[] =
+{
+ "Usage: %s %s [-l] [rlog-options] [files...]\n",
+ "\t-l\tLocal directory only, no recursion.\n",
+ NULL
+};
+
+int
+cvslog (argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i;
+ int numopt = 1;
+ int err = 0;
+ int local = 0;
+
+ if (argc == -1)
+ usage (log_usage);
+
+ /*
+ * All 'log' command options except -l are passed directly on to 'rlog'
+ */
+ options[0] = '\0'; /* Assume none */
+ for (i = 1; i < argc; i++)
+ {
+ if (argv[i][0] == '-' || argv[i][0] == '\0')
+ {
+ numopt++;
+ switch (argv[i][1])
+ {
+ case 'l':
+ local = 1;
+ break;
+ default:
+ (void) strcat (options, " ");
+ (void) strcat (options, argv[i]);
+ break;
+ }
+ }
+ }
+ argc -= numopt;
+ argv += numopt;
+
+ err = start_recursion (log_fileproc, (int (*) ()) NULL, log_dirproc,
+ (int (*) ()) NULL, argc, argv, local,
+ W_LOCAL | W_REPOS | W_ATTIC, 0, 1,
+ (char *) NULL, 1);
+ return (err);
+}
+
+/*
+ * Do an rlog on a file
+ */
+/* ARGSUSED */
+static int
+log_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ Node *p;
+ RCSNode *rcsfile;
+ int retcode = 0;
+
+ p = findnode (srcfiles, file);
+ if (p == NULL || (rcsfile = (RCSNode *) p->data) == NULL)
+ {
+ if (!really_quiet)
+ error (0, 0, "nothing known about %s", file);
+ return (1);
+ }
+
+ run_setup ("%s%s %s", Rcsbin, RCS_RLOG, options);
+ run_arg (rcsfile->path);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_REALLY)) == -1)
+ {
+ error (1, errno, "fork failed for rlog on %s", file);
+ }
+ return (retcode);
+}
+
+/*
+ * Print a warm fuzzy message
+ */
+/* ARGSUSED */
+static Dtype
+log_dirproc (dir, repository, update_dir)
+ char *dir;
+ char *repository;
+ char *update_dir;
+{
+ if (!isdir (dir))
+ return (R_SKIP_ALL);
+
+ if (!quiet)
+ error (0, 0, "Logging %s", update_dir);
+ return (R_PROCESS);
+}
diff --git a/gnu/usr.bin/cvs/cvs/logmsg.c b/gnu/usr.bin/cvs/cvs/logmsg.c
new file mode 100644
index 0000000..ce1da92
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/logmsg.c
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)logmsg.c 1.40 92/04/10";
+#endif
+
+#if __STDC__
+static int find_type (Node * p);
+static int fmt_proc (Node * p);
+static int logfile_write (char *repository, char *filter, char *title,
+ char *message, char *revision, FILE * logfp,
+ List * changes);
+static int rcsinfo_proc (char *repository, char *template);
+static int title_proc (Node * p);
+static int update_logfile_proc (char *repository, char *filter);
+static void setup_tmpfile (FILE * xfp, char *xprefix, List * changes);
+static int editinfo_proc (char *repository, char *template);
+#else
+static void setup_tmpfile ();
+static int find_type ();
+static int fmt_proc ();
+static int rcsinfo_proc ();
+static int update_logfile_proc ();
+static int title_proc ();
+static int logfile_write ();
+static int editinfo_proc ();
+#endif /* __STDC__ */
+
+static FILE *fp;
+static char *strlist;
+static char *editinfo_editor;
+static Ctype type;
+
+/*
+ * Puts a standard header on the output which is either being prepared for an
+ * editor session, or being sent to a logfile program. The modified, added,
+ * and removed files are included (if any) and formatted to look pretty.
+ */
+static char *prefix;
+static int col;
+static void
+setup_tmpfile (xfp, xprefix, changes)
+ FILE *xfp;
+ char *xprefix;
+ List *changes;
+{
+ /* set up statics */
+ fp = xfp;
+ prefix = xprefix;
+
+ type = T_MODIFIED;
+ if (walklist (changes, find_type) != 0)
+ {
+ (void) fprintf (fp, "%sModified Files:\n", prefix);
+ (void) fprintf (fp, "%s\t", prefix);
+ col = 8;
+ (void) walklist (changes, fmt_proc);
+ (void) fprintf (fp, "\n");
+ }
+ type = T_ADDED;
+ if (walklist (changes, find_type) != 0)
+ {
+ (void) fprintf (fp, "%sAdded Files:\n", prefix);
+ (void) fprintf (fp, "%s\t", prefix);
+ col = 8;
+ (void) walklist (changes, fmt_proc);
+ (void) fprintf (fp, "\n");
+ }
+ type = T_REMOVED;
+ if (walklist (changes, find_type) != 0)
+ {
+ (void) fprintf (fp, "%sRemoved Files:\n", prefix);
+ (void) fprintf (fp, "%s\t", prefix);
+ col = 8;
+ (void) walklist (changes, fmt_proc);
+ (void) fprintf (fp, "\n");
+ }
+}
+
+/*
+ * Looks for nodes of a specified type and returns 1 if found
+ */
+static int
+find_type (p)
+ Node *p;
+{
+ if (p->data == (char *) type)
+ return (1);
+ else
+ return (0);
+}
+
+/*
+ * Breaks the files list into reasonable sized lines to avoid line wrap...
+ * all in the name of pretty output. It only works on nodes whose types
+ * match the one we're looking for
+ */
+static int
+fmt_proc (p)
+ Node *p;
+{
+ if (p->data == (char *) type)
+ {
+ if ((col + (int) strlen (p->key)) > 70)
+ {
+ (void) fprintf (fp, "\n%s\t", prefix);
+ col = 8;
+ }
+ (void) fprintf (fp, "%s ", p->key);
+ col += strlen (p->key) + 1;
+ }
+ return (0);
+}
+
+/*
+ * Builds a temporary file using setup_tmpfile() and invokes the user's
+ * editor on the file. The header garbage in the resultant file is then
+ * stripped and the log message is stored in the "message" argument.
+ *
+ * rcsinfo - is the name of a file containing lines tacked onto the end of the
+ * RCS info offered to the user for editing. If specified, the '-m' flag to
+ * "commit" is disabled -- users are forced to run the editor.
+ *
+ */
+void
+do_editor (dir, message, repository, changes)
+ char *dir;
+ char *message;
+ char *repository;
+ List *changes;
+{
+ static int reuse_log_message = 0;
+ char line[MAXLINELEN], fname[L_tmpnam+1];
+ char *orig_message;
+ struct stat pre_stbuf, post_stbuf;
+ int retcode = 0;
+
+ if (noexec || reuse_log_message)
+ return;
+
+ orig_message = xstrdup (message); /* save it for later */
+
+ /* Create a temporary file */
+ (void) tmpnam (fname);
+ again:
+ if ((fp = fopen (fname, "w+")) == NULL)
+ error (1, 0, "cannot create temporary file %s", fname);
+
+ /* set up the file so that the first line is blank if no msg specified */
+ if (*orig_message)
+ {
+ (void) fprintf (fp, "%s", orig_message);
+ if (orig_message[strlen (orig_message) - 1] != '\n')
+ (void) fprintf (fp, "\n");
+ }
+ else
+ (void) fprintf (fp, "\n");
+
+ /* tack templates on if necessary */
+ (void) Parse_Info (CVSROOTADM_RCSINFO, repository, rcsinfo_proc, 1);
+
+ (void) fprintf (fp,
+ "%s----------------------------------------------------------------------\n",
+ CVSEDITPREFIX);
+ (void) fprintf (fp,
+ "%sEnter Log. Lines beginning with `%s' are removed automatically\n%s\n",
+ CVSEDITPREFIX, CVSEDITPREFIX, CVSEDITPREFIX);
+ if (dir != NULL)
+ (void) fprintf (fp, "%sCommitting in %s\n%s\n", CVSEDITPREFIX,
+ dir, CVSEDITPREFIX);
+ setup_tmpfile (fp, CVSEDITPREFIX, changes);
+ (void) fprintf (fp,
+ "%s----------------------------------------------------------------------\n",
+ CVSEDITPREFIX);
+
+ /* finish off the temp file */
+ (void) fclose (fp);
+ if (stat (fname, &pre_stbuf) == -1)
+ pre_stbuf.st_mtime = 0;
+
+ if (editinfo_editor)
+ free (editinfo_editor);
+ editinfo_editor = (char *) NULL;
+ (void) Parse_Info (CVSROOTADM_EDITINFO, repository, editinfo_proc, 0);
+
+ /* run the editor */
+ run_setup ("%s", editinfo_editor ? editinfo_editor : Editor);
+ run_arg (fname);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
+ RUN_NORMAL | RUN_SIGIGNORE)) != 0)
+ error (editinfo_editor ? 1 : 0, retcode == -1 ? errno : 0,
+ editinfo_editor ? "Logfile verification failed" :
+ "warning: editor session failed");
+
+ /* put the entire message back into the message variable */
+ fp = open_file (fname, "r");
+ *message = '\0';
+ while (fgets (line, sizeof (line), fp) != NULL)
+ {
+ if (strncmp (line, CVSEDITPREFIX, sizeof (CVSEDITPREFIX) - 1) == 0)
+ continue;
+ if (((int) strlen (message) + (int) strlen (line)) >= MAXMESGLEN)
+ {
+ error (0, 0, "warning: log message truncated!");
+ break;
+ }
+ (void) strcat (message, line);
+ }
+ (void) fclose (fp);
+ if ((stat (fname, &post_stbuf) == 0 &&
+ pre_stbuf.st_mtime == post_stbuf.st_mtime) ||
+ (*message == '\0' || strcmp (message, "\n") == 0))
+ {
+ for (;;)
+ {
+ (void) printf ("\nLog message unchanged or not specified\n");
+ (void) printf ("a)bort, c)continue, e)dit, !)reuse this message unchanged for remaining dirs\n");
+ (void) printf ("Action: (continue) ");
+ (void) fflush (stdout);
+ *line = '\0';
+ (void) fgets (line, sizeof (line), stdin);
+ if (*line == '\0' || *line == '\n' || *line == 'c' || *line == 'C')
+ break;
+ if (*line == 'a' || *line == 'A')
+ error (1, 0, "aborted by user");
+ if (*line == 'e' || *line == 'E')
+ goto again;
+ if (*line == '!')
+ {
+ reuse_log_message = 1;
+ break;
+ }
+ (void) printf ("Unknown input\n");
+ }
+ }
+ free (orig_message);
+ (void) unlink_file (fname);
+}
+
+/*
+ * callback proc for Parse_Info for rcsinfo templates this routine basically
+ * copies the matching template onto the end of the tempfile we are setting
+ * up
+ */
+/* ARGSUSED */
+static int
+rcsinfo_proc (repository, template)
+ char *repository;
+ char *template;
+{
+ static char *last_template;
+ FILE *tfp;
+ char line[MAXLINELEN];
+
+ /* nothing to do if the last one included is the same as this one */
+ if (last_template && strcmp (last_template, template) == 0)
+ return (0);
+ if (last_template)
+ free (last_template);
+ last_template = xstrdup (template);
+
+ if ((tfp = fopen (template, "r")) != NULL)
+ {
+ while (fgets (line, sizeof (line), tfp) != NULL)
+ (void) fputs (line, fp);
+ (void) fclose (tfp);
+ return (0);
+ }
+ else
+ {
+ error (0, 0, "Couldn't open rcsinfo template file %s", template);
+ return (1);
+ }
+}
+
+/*
+ * Uses setup_tmpfile() to pass the updated message on directly to any
+ * logfile programs that have a regular expression match for the checked in
+ * directory in the source repository. The log information is fed into the
+ * specified program as standard input.
+ */
+static char *title;
+static FILE *logfp;
+static char *message;
+static char *revision;
+static List *changes;
+
+void
+Update_Logfile (repository, xmessage, xrevision, xlogfp, xchanges)
+ char *repository;
+ char *xmessage;
+ char *xrevision;
+ FILE *xlogfp;
+ List *xchanges;
+{
+ char *srepos;
+
+ /* set up static vars for update_logfile_proc */
+ message = xmessage;
+ revision = xrevision;
+ logfp = xlogfp;
+ changes = xchanges;
+
+ /* figure out a good title string */
+ srepos = Short_Repository (repository);
+
+ /* allocate a chunk of memory to hold the title string */
+ if (!strlist)
+ strlist = xmalloc (MAXLISTLEN);
+ strlist[0] = '\0';
+
+ type = T_TITLE;
+ (void) walklist (changes, title_proc);
+ type = T_ADDED;
+ (void) walklist (changes, title_proc);
+ type = T_MODIFIED;
+ (void) walklist (changes, title_proc);
+ type = T_REMOVED;
+ (void) walklist (changes, title_proc);
+ title = xmalloc (strlen (srepos) + strlen (strlist) + 1 + 2); /* for 's */
+ (void) sprintf (title, "'%s%s'", srepos, strlist);
+
+ /* to be nice, free up this chunk of memory */
+ free (strlist);
+ strlist = (char *) NULL;
+
+ /* call Parse_Info to do the actual logfile updates */
+ (void) Parse_Info (CVSROOTADM_LOGINFO, repository, update_logfile_proc, 1);
+
+ /* clean up */
+ free (title);
+}
+
+/*
+ * callback proc to actually do the logfile write from Update_Logfile
+ */
+static int
+update_logfile_proc (repository, filter)
+ char *repository;
+ char *filter;
+{
+ return (logfile_write (repository, filter, title, message, revision,
+ logfp, changes));
+}
+
+/*
+ * concatenate each name onto strlist
+ */
+static int
+title_proc (p)
+ Node *p;
+{
+ if (p->data == (char *) type)
+ {
+ (void) strcat (strlist, " ");
+ (void) strcat (strlist, p->key);
+ }
+ return (0);
+}
+
+/*
+ * Since some systems don't define this...
+ */
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+
+/*
+ * Writes some stuff to the logfile "filter" and returns the status of the
+ * filter program.
+ */
+static int
+logfile_write (repository, filter, title, message, revision, logfp, changes)
+ char *repository;
+ char *filter;
+ char *title;
+ char *message;
+ char *revision;
+ FILE *logfp;
+ List *changes;
+{
+ char cwd[PATH_MAX], host[MAXHOSTNAMELEN];
+ FILE *pipefp, *Popen ();
+ char *prog = xmalloc (MAXPROGLEN);
+ char *cp;
+ int c;
+
+ /*
+ * A maximum of 6 %s arguments are supported in the filter
+ */
+ (void) sprintf (prog, filter, title, title, title, title, title, title);
+ if ((pipefp = Popen (prog, "w")) == NULL)
+ {
+ if (!noexec)
+ error (0, 0, "cannot write entry to log filter: %s", prog);
+ free (prog);
+ return (1);
+ }
+ if (gethostname (host, sizeof (host)) < 0)
+ (void) strcpy (host, "(unknown)");
+ (void) fprintf (pipefp, "Update of %s\n", repository);
+ (void) fprintf (pipefp, "In directory %s:%s\n\n", host,
+ ((cp = getwd (cwd)) != NULL) ? cp : cwd);
+ if (revision && *revision)
+ (void) fprintf (pipefp, "Revision/Branch: %s\n\n", revision);
+ setup_tmpfile (pipefp, "", changes);
+ (void) fprintf (pipefp, "Log Message:\n%s\n", message);
+ if (logfp != (FILE *) 0)
+ {
+ (void) fprintf (pipefp, "Status:\n");
+ (void) rewind (logfp);
+ while ((c = getc (logfp)) != EOF)
+ (void) putc ((char) c, pipefp);
+ }
+ free (prog);
+ return (pclose (pipefp));
+}
+
+/*
+ * We choose to use the *last* match within the editinfo file for this
+ * repository. This allows us to have a global editinfo program for the
+ * root of some hierarchy, for example, and different ones within different
+ * sub-directories of the root (like a special checker for changes made to
+ * the "src" directory versus changes made to the "doc" or "test"
+ * directories.
+ */
+/* ARGSUSED */
+static int
+editinfo_proc(repository, editor)
+ char *repository;
+ char *editor;
+{
+ /* nothing to do if the last match is the same as this one */
+ if (editinfo_editor && strcmp (editinfo_editor, editor) == 0)
+ return (0);
+ if (editinfo_editor)
+ free (editinfo_editor);
+
+ editinfo_editor = xstrdup (editor);
+ return (0);
+}
diff --git a/gnu/usr.bin/cvs/cvs/main.c b/gnu/usr.bin/cvs/cvs/main.c
new file mode 100644
index 0000000..d6ad258
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/main.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License
+ * as specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * This is the main C driver for the CVS system.
+ *
+ * Credit to Dick Grune, Vrije Universiteit, Amsterdam, for writing
+ * the shell-script CVS system that this is based on.
+ *
+ * Usage:
+ * cvs [options] command [options] [files/modules...]
+ *
+ * Where "command" is composed of:
+ * admin RCS command
+ * checkout Check out a module/dir/file
+ * export Like checkout, but used for exporting sources
+ * update Brings work tree in sync with repository
+ * commit Checks files into the repository
+ * diff Runs diffs between revisions
+ * log Prints "rlog" information for files
+ * add Adds an entry to the repository
+ * remove Removes an entry from the repository
+ * status Status info on the revisions
+ * rdiff "patch" format diff listing between releases
+ * tag Add/delete a symbolic tag to the RCS file
+ * rtag Add/delete a symbolic tag to the RCS file
+ * import Import sources into CVS, using vendor branches
+ * release Indicate that Module is no longer in use.
+ * history Display history of Users and Modules.
+ */
+
+#include "cvs.h"
+#include "patchlevel.h"
+
+char rcsid[] = "@(#)main.c 1.64 92/03/31\n";
+
+extern char *getenv ();
+
+char *program_name;
+char *command_name = "";
+
+int use_editor = TRUE;
+int cvswrite = !CVSREAD_DFLT;
+int really_quiet = FALSE;
+int quiet = FALSE;
+int trace = FALSE;
+int noexec = FALSE;
+int logoff = FALSE;
+
+char *CurDir;
+
+/*
+ * Defaults, for the environment variables that are not set
+ */
+char *Rcsbin = RCSBIN_DFLT;
+char *Editor = EDITOR_DFLT;
+char *CVSroot = CVSROOT_DFLT;
+
+#if __STDC__
+int add (int argc, char **argv);
+int admin (int argc, char **argv);
+int checkout (int argc, char **argv);
+int commit (int argc, char **argv);
+int diff (int argc, char **argv);
+int history (int argc, char **argv);
+int import (int argc, char **argv);
+int cvslog (int argc, char **argv);
+int patch (int argc, char **argv);
+int release (int argc, char **argv);
+int cvsremove (int argc, char **argv);
+int rtag (int argc, char **argv);
+int status (int argc, char **argv);
+int tag (int argc, char **argv);
+int update (int argc, char **argv);
+#else
+int add ();
+int admin ();
+int checkout ();
+int commit ();
+int diff ();
+int history ();
+int import ();
+int cvslog ();
+int patch ();
+int release ();
+int cvsremove ();
+int rtag ();
+int status ();
+int tag ();
+int update ();
+#endif /* __STDC__ */
+
+#ifdef FREEBSD_DEVELOPER
+int freebsd = TRUE; /* Use the FreeBSD -K flags!! */
+#endif
+
+struct cmd
+{
+ char *fullname; /* Full name of the function (e.g. "commit") */
+ char *nick1; /* alternate name (e.g. "ci") */
+ char *nick2; /* another alternate names (e.g. "ci") */
+ int (*func) (); /* Function takes (argc, argv) arguments. */
+} cmds[] =
+
+{
+ { "add", "ad", "new", add },
+ { "admin", "adm", "rcs", admin },
+ { "checkout", "co", "get", checkout },
+ { "commit", "ci", "com", commit },
+ { "diff", "di", "dif", diff },
+ { "export", "exp", "ex", checkout },
+ { "history", "hi", "his", history },
+ { "import", "im", "imp", import },
+ { "log", "lo", "rlog", cvslog },
+ { "rdiff", "patch", "pa", patch },
+ { "release", "re", "rel", release },
+ { "remove", "rm", "delete", cvsremove },
+ { "status", "st", "stat", status },
+ { "rtag", "rt", "rfreeze", rtag },
+ { "tag", "ta", "freeze", tag },
+ { "update", "up", "upd", update },
+ { NULL, NULL, NULL, NULL },
+};
+
+static char *usg[] =
+{
+ "Usage: %s [cvs-options] command [command-options] [files...]\n",
+ " Where 'cvs-options' are:\n",
+ " -H Displays Usage information for command\n",
+ " -Q Cause CVS to be really quiet.\n",
+ " -q Cause CVS to be somewhat quiet.\n",
+ " -r Make checked-out files read-only\n",
+ " -w Make checked-out files read-write (default)\n",
+ " -l Turn History logging off\n",
+ " -n Do not execute anything that will change the disk\n",
+ " -t Show trace of program execution -- Try with -n\n",
+ " -v CVS version and copyright\n",
+ " -b bindir Find RCS programs in 'bindir'\n",
+ " -e editor Use 'editor' for editing log information\n",
+ " -d CVS_root Overrides $CVSROOT as the root of the CVS tree\n",
+#ifdef FREEBSD_DEVELOPER
+ " -x Do NOT use the FreeBSD -K default flags\n",
+#endif
+ "\n",
+ " and where 'command' is:\n",
+ " add Adds a new file/directory to the repository\n",
+ " admin Administration front end for rcs\n",
+ " checkout Checkout sources for editing\n",
+ " commit Checks files into the repository\n",
+ " diff Runs diffs between revisions\n",
+ " history Shows status of files and users\n",
+ " import Import sources into CVS, using vendor branches\n",
+ " export Export sources from CVS, similar to checkout\n",
+ " log Prints out 'rlog' information for files\n",
+ " rdiff 'patch' format diffs between releases\n",
+ " release Indicate that a Module is no longer in use\n",
+ " remove Removes an entry from the repository\n",
+ " status Status info on the revisions\n",
+ " tag Add a symbolic tag to checked out version of RCS file\n",
+ " rtag Add a symbolic tag to the RCS file\n",
+ " update Brings work tree in sync with repository\n",
+ NULL,
+};
+
+static SIGTYPE
+main_cleanup ()
+{
+ exit (1);
+}
+
+int
+main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *version_string;
+ char *cp;
+ struct cmd *cm;
+ int c, help = FALSE, err = 0;
+ int rcsbin_update_env, cvs_update_env;
+ char tmp[PATH_MAX];
+
+ /*
+ * Just save the last component of the path for error messages
+ */
+ if ((program_name = rindex (argv[0], '/')) == NULL)
+ program_name = argv[0];
+ else
+ program_name++;
+
+ CurDir = xmalloc (PATH_MAX);
+ if (!getwd (CurDir))
+ error (1, 0, "cannot get working directory: %s", CurDir);
+
+ /*
+ * Query the environment variables up-front, so that
+ * they can be overridden by command line arguments
+ */
+ rcsbin_update_env = *Rcsbin; /* RCSBIN_DFLT must be set */
+ if ((cp = getenv (RCSBIN_ENV)) != NULL)
+ {
+ Rcsbin = cp;
+ rcsbin_update_env = 0; /* it's already there */
+ }
+ if ((cp = getenv (EDITOR_ENV)) != NULL)
+ Editor = cp;
+ if ((cp = getenv (CVSROOT_ENV)) != NULL)
+ {
+ CVSroot = cp;
+ cvs_update_env = 0; /* it's already there */
+ }
+ if (getenv (CVSREAD_ENV) != NULL)
+ cvswrite = FALSE;
+
+ optind = 1;
+#ifdef FREEBSD_DEVELOPER
+ while ((c = gnu_getopt (argc, argv, "Qqrwtnlvb:e:d:Hx")) != -1)
+#else
+ while ((c = gnu_getopt (argc, argv, "Qqrwtnlvb:e:d:H")) != -1)
+#endif /* FREEBSD_DEVELOPER */
+ {
+ switch (c)
+ {
+ case 'Q':
+ really_quiet = TRUE;
+ /* FALL THROUGH */
+ case 'q':
+ quiet = TRUE;
+ break;
+ case 'r':
+ cvswrite = FALSE;
+ break;
+ case 'w':
+ cvswrite = TRUE;
+ break;
+ case 't':
+ trace = TRUE;
+ break;
+ case 'n':
+ noexec = TRUE;
+ case 'l': /* Fall through */
+ logoff = TRUE;
+ break;
+ case 'v':
+ (void) fputs (rcsid, stdout);
+ (void) fputs (version_string, stdout);
+ (void) sprintf (tmp, "Patch Level: %d\n", PATCHLEVEL);
+ (void) fputs (tmp, stdout);
+ (void) fputs ("\nCopyright (c) 1992, Brian Berliner and Jeff Polk\nCopyright (c) 1989-1992, Brian Berliner\n\nCVS may be copied only under the terms of the GNU General Public License,\na copy of which can be found with the CVS 1.3 distribution kit.\n", stdout);
+ exit (0);
+ break;
+ case 'b':
+ Rcsbin = optarg;
+ rcsbin_update_env = 1; /* need to update environment */
+ break;
+ case 'e':
+ Editor = optarg;
+ break;
+ case 'd':
+ CVSroot = optarg;
+ cvs_update_env = 1; /* need to update environment */
+ break;
+ case 'H':
+ help = TRUE;
+ break;
+#ifdef FREEBSD_DEVELOPER
+ case 'x':
+ freebsd = FALSE;
+ break;
+#endif /* FREEBSD_DEVELOPER */
+ case '?':
+ default:
+ usage (usg);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 1)
+ usage (usg);
+
+ /*
+ * XXX - Compatibility. This can be removed in the release after CVS 1.3.
+ * Try to rename the CVSROOT.adm file to CVSROOT, unless there already is
+ * a CVSROOT directory.
+ */
+ if (CVSroot != NULL)
+ {
+ char rootadm[PATH_MAX];
+ char orootadm[PATH_MAX];
+
+ (void) sprintf (rootadm, "%s/%s", CVSroot, CVSROOTADM);
+ if (!isdir (rootadm))
+ {
+ (void) sprintf (orootadm, "%s/%s", CVSroot, OCVSROOTADM);
+ if (isdir (orootadm))
+ (void) rename (orootadm, rootadm);
+ }
+ strip_path (CVSroot);
+ }
+
+ /*
+ * Specifying just the '-H' flag to the sub-command causes a Usage
+ * message to be displayed.
+ */
+ command_name = cp = argv[0];
+ if (help == TRUE || (argc > 1 && strcmp (argv[1], "-H") == 0))
+ argc = -1;
+ else
+ {
+ /*
+ * Check to see if we can write into the history file. If not,
+ * we assume that we can't work in the repository.
+ * BUT, only if the history file exists.
+ */
+ {
+ char path[PATH_MAX];
+ int save_errno;
+
+ if (!CVSroot || !*CVSroot)
+ error (1, 0, "You don't have a %s environment variable",
+ CVSROOT_ENV);
+ (void) sprintf (path, "%s/%s", CVSroot, CVSROOTADM);
+ if (access (path, R_OK | X_OK))
+ {
+ save_errno = errno;
+ error (0, 0,
+ "Sorry, you don't have sufficient access to %s", CVSroot);
+ error (1, save_errno, "%s", path);
+ }
+ (void) strcat (path, "/");
+ (void) strcat (path, CVSROOTADM_HISTORY);
+ if (isfile (path) && access (path, R_OK | (logoff ? 0 : W_OK)))
+ {
+ save_errno = errno;
+ error (0, 0,
+ "Sorry, you don't have read/write access to the history file");
+ error (1, save_errno, "%s", path);
+ }
+ }
+ }
+
+#ifndef PUTENV_MISSING
+ /* Now, see if we should update the environment with the Rcsbin value */
+ if (cvs_update_env)
+ {
+ char *env;
+
+ env = xmalloc (strlen (CVSROOT_ENV) + strlen (CVSroot) + 1 + 1);
+ (void) sprintf (env, "%s=%s", CVSROOT_ENV, CVSroot);
+ (void) putenv (env);
+ /* do not free env, as putenv has control of it */
+ }
+ if (rcsbin_update_env)
+ {
+ char *env;
+
+ env = xmalloc (strlen (RCSBIN_ENV) + strlen (Rcsbin) + 1 + 1);
+ (void) sprintf (env, "%s=%s", RCSBIN_ENV, Rcsbin);
+ (void) putenv (env);
+ /* do not free env, as putenv has control of it */
+ }
+#endif
+
+ /*
+ * If Rcsbin is set to something, make sure it is terminated with
+ * a slash character. If not, add one.
+ */
+ if (*Rcsbin)
+ {
+ int len = strlen (Rcsbin);
+ char *rcsbin;
+
+ if (Rcsbin[len - 1] != '/')
+ {
+ rcsbin = Rcsbin;
+ Rcsbin = xmalloc (len + 2); /* one for '/', one for NULL */
+ (void) strcpy (Rcsbin, rcsbin);
+ (void) strcat (Rcsbin, "/");
+ }
+ }
+
+ for (cm = cmds; cm->fullname; cm++)
+ {
+ if (cm->nick1 && !strcmp (cp, cm->nick1))
+ break;
+ if (cm->nick2 && !strcmp (cp, cm->nick2))
+ break;
+ if (!strcmp (cp, cm->fullname))
+ break;
+ }
+
+ if (!cm->fullname)
+ usage (usg); /* no match */
+ else
+ {
+ command_name = cm->fullname; /* Global pointer for later use */
+ (void) SIG_register (SIGHUP, main_cleanup);
+ (void) SIG_register (SIGINT, main_cleanup);
+ (void) SIG_register (SIGQUIT, main_cleanup);
+ (void) SIG_register (SIGPIPE, main_cleanup);
+ (void) SIG_register (SIGTERM, main_cleanup);
+
+#ifndef SETVBUF_MISSING
+ /*
+ * Make stdout line buffered, so 'tail -f' can monitor progress.
+ * Patch creates too much output to monitor and it runs slowly.
+ */
+ if (strcmp (cm->fullname, "patch"))
+ (void) setvbuf (stdout, (char *) NULL, _IOLBF, 0);
+#endif
+
+ err = (*(cm->func)) (argc, argv);
+ }
+ /*
+ * If the command's error count is modulo 256, we need to change it
+ * so that we don't overflow the 8-bits we get to report exit status
+ */
+ if (err && (err % 256) == 0)
+ err = 1;
+ Lock_Cleanup ();
+ return (err);
+}
+
+char *
+Make_Date (rawdate)
+ char *rawdate;
+{
+ struct tm *ftm;
+ time_t unixtime;
+ char date[256]; /* XXX bigger than we'll ever need? */
+ char *ret;
+
+ unixtime = get_date (rawdate, (struct timeb *) NULL);
+ if (unixtime == (time_t) - 1)
+ error (1, 0, "Can't parse date/time: %s", rawdate);
+#ifdef HAVE_RCS5
+ ftm = gmtime (&unixtime);
+#else
+ ftm = localtime (&unixtime);
+#endif
+ (void) sprintf (date, DATEFORM,
+ ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
+ ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
+ ftm->tm_min, ftm->tm_sec);
+ ret = xstrdup (date);
+ return (ret);
+}
+
+void
+usage (cpp)
+ register char **cpp;
+{
+ (void) fprintf (stderr, *cpp++, program_name, command_name);
+ for (; *cpp; cpp++)
+ (void) fprintf (stderr, *cpp);
+ exit (1);
+}
diff --git a/gnu/usr.bin/cvs/cvs/modules.c b/gnu/usr.bin/cvs/cvs/modules.c
new file mode 100644
index 0000000..b0658d7
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/modules.c
@@ -0,0 +1,810 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License
+ * as specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Modules
+ *
+ * Functions for accessing the modules file.
+ *
+ * The modules file supports basically three formats of lines:
+ * key [options] directory files... [ -x directory [files] ] ...
+ * key [options] directory [ -x directory [files] ] ...
+ * key -a aliases...
+ *
+ * The -a option allows an aliasing step in the parsing of the modules
+ * file. The "aliases" listed on a line following the -a are
+ * processed one-by-one, as if they were specified as arguments on the
+ * command line.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)modules.c 1.57 92/04/10";
+#endif
+
+struct sortrec
+{
+ char *modname;
+ char *status;
+ char *rest;
+ char *comment;
+};
+
+#if __STDC__
+static int sort_order (CONST PTR l, CONST PTR r);
+static void save_d (char *k, int ks, char *d, int ds);
+#else
+static int sort_order ();
+static void save_d ();
+#endif /* __STDC__ */
+
+
+/*
+ * Open the modules file, and die if the CVSROOT environment variable
+ * was not set. If the modules file does not exist, that's fine, and
+ * a warning message is displayed and a NULL is returned.
+ */
+DBM *
+open_module ()
+{
+ char mfile[PATH_MAX];
+
+ if (CVSroot == NULL)
+ {
+ (void) fprintf (stderr,
+ "%s: must set the CVSROOT environment variable\n",
+ program_name);
+ error (1, 0, "or specify the '-d' option to %s", program_name);
+ }
+ (void) sprintf (mfile, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_MODULES);
+ return (dbm_open (mfile, O_RDONLY, 0666));
+}
+
+/*
+ * Close the modules file, if the open succeeded, that is
+ */
+void
+close_module (db)
+ DBM *db;
+{
+ if (db != NULL)
+ dbm_close (db);
+}
+
+/*
+ * This is the recursive function that processes a module name.
+ * It calls back the passed routine for each directory of a module
+ * It runs the post checkout or post tag proc from the modules file
+ */
+int
+do_module (db, mname, m_type, msg, callback_proc, where,
+ shorten, local_specified, run_module_prog, extra_arg)
+ DBM *db;
+ char *mname;
+ enum mtype m_type;
+ char *msg;
+ int (*callback_proc) ();
+ char *where;
+ int shorten;
+ int local_specified;
+ int run_module_prog;
+ char *extra_arg;
+{
+ char *checkin_prog = NULL;
+ char *checkout_prog = NULL;
+ char *tag_prog = NULL;
+ char *update_prog = NULL;
+ char cwd[PATH_MAX];
+ char line[MAXLINELEN];
+ char *xmodargv[MAXFILEPERDIR];
+ char **modargv;
+ char *value;
+ char *zvalue;
+ char *mwhere = NULL;
+ char *mfile = NULL;
+ char *spec_opt = NULL;
+ char xvalue[PATH_MAX];
+ int modargc, alias = 0;
+ datum key, val;
+ char *cp;
+ int c, err = 0;
+
+ /* remember where we start */
+ if (getwd (cwd) == NULL)
+ error (1, 0, "cannot get current working directory: %s", cwd);
+
+ /* strip extra stuff from the module name */
+ strip_path (mname);
+
+ /*
+ * Look up the module using the following scheme:
+ * 1) look for mname as a module name
+ * 2) look for mname as a directory
+ * 3) look for mname as a file
+ * 4) take mname up to the first slash and look it up as a module name
+ * (this is for checking out only part of a module)
+ */
+
+ /* look it up as a module name */
+ key.dptr = mname;
+ key.dsize = strlen (key.dptr);
+ if (db != NULL)
+ val = dbm_fetch (db, key);
+ else
+ val.dptr = NULL;
+ if (val.dptr != NULL)
+ {
+ /* null terminate the value XXX - is this space ours? */
+ val.dptr[val.dsize] = '\0';
+
+ /* If the line ends in a comment, strip it off */
+ if ((cp = index (val.dptr, '#')) != NULL)
+ {
+ do
+ *cp-- = '\0';
+ while (isspace (*cp));
+ }
+ value = val.dptr;
+ mwhere = xstrdup (mname);
+ goto found;
+ }
+ else
+ {
+ char file[PATH_MAX];
+ char attic_file[PATH_MAX];
+ char *acp;
+
+ /* check to see if mname is a directory or file */
+
+ (void) sprintf (file, "%s/%s", CVSroot, mname);
+ if ((acp = rindex (mname, '/')) != NULL)
+ {
+ *acp = '\0';
+ (void) sprintf (attic_file, "%s/%s/%s/%s%s", CVSroot, mname,
+ CVSATTIC, acp + 1, RCSEXT);
+ *acp = '/';
+ }
+ else
+ (void) sprintf (attic_file, "%s/%s/%s%s", CVSroot, CVSATTIC,
+ mname, RCSEXT);
+
+ if (isdir (file))
+ {
+ value = mname;
+ goto found;
+ }
+ else
+ {
+ (void) strcat (file, RCSEXT);
+ if (isfile (file) || isfile (attic_file))
+ {
+ /* if mname was a file, we have to split it into "dir file" */
+ if ((cp = rindex (mname, '/')) != NULL && cp != mname)
+ {
+ char *slashp;
+
+ /* put the ' ' in a copy so we don't mess up the original */
+ value = strcpy (xvalue, mname);
+ slashp = rindex (value, '/');
+ *slashp = ' ';
+ }
+ else
+ {
+ /*
+ * the only '/' at the beginning or no '/' at all
+ * means the file we are interested in is in CVSROOT
+ * itself so the directory should be '.'
+ */
+ if (cp == mname)
+ {
+ /* drop the leading / if specified */
+ value = strcpy (xvalue, ". ");
+ (void) strcat (xvalue, mname + 1);
+ }
+ else
+ {
+ /* otherwise just copy it */
+ value = strcpy (xvalue, ". ");
+ (void) strcat (xvalue, mname);
+ }
+ }
+ goto found;
+ }
+ }
+ }
+
+ /* look up everything to the first / as a module */
+ if (mname[0] != '/' && (cp = index (mname, '/')) != NULL)
+ {
+ /* Make the slash the new end of the string temporarily */
+ *cp = '\0';
+ key.dptr = mname;
+ key.dsize = strlen (key.dptr);
+
+ /* do the lookup */
+ if (db != NULL)
+ val = dbm_fetch (db, key);
+ else
+ val.dptr = NULL;
+
+ /* if we found it, clean up the value and life is good */
+ if (val.dptr != NULL)
+ {
+ char *cp2;
+
+ /* null terminate the value XXX - is this space ours? */
+ val.dptr[val.dsize] = '\0';
+
+ /* If the line ends in a comment, strip it off */
+ if ((cp2 = index (val.dptr, '#')) != NULL)
+ {
+ do
+ *cp2-- = '\0';
+ while (isspace (*cp2));
+ }
+ value = val.dptr;
+
+ /* mwhere gets just the module name */
+ mwhere = xstrdup (mname);
+ mfile = cp + 1;
+
+ /* put the / back in mname */
+ *cp = '/';
+
+ goto found;
+ }
+
+ /* put the / back in mname */
+ *cp = '/';
+ }
+
+ /* if we got here, we couldn't find it using our search, so give up */
+ error (0, 0, "cannot find module `%s' - ignored", mname);
+ err++;
+ if (mwhere)
+ free (mwhere);
+ return (err);
+
+
+ /*
+ * At this point, we found what we were looking for in one
+ * of the many different forms.
+ */
+ found:
+
+ /* copy value to our own string since if we go recursive we'll be
+ really screwed if we do another dbm lookup */
+ zvalue = xstrdup (value);
+ value = zvalue;
+
+ /* search the value for the special delimiter and save for later */
+ if ((cp = index (value, CVSMODULE_SPEC)) != NULL)
+ {
+ *cp = '\0'; /* null out the special char */
+ spec_opt = cp + 1; /* save the options for later */
+
+ if (cp != value) /* strip whitespace if necessary */
+ while (isspace (*--cp))
+ *cp = '\0';
+
+ if (cp == value)
+ {
+ /*
+ * we had nothing but special options, so skip arg
+ * parsing and regular stuff entirely
+ *
+ * If there were only special ones though, we must
+ * make the appropriate directory and cd to it
+ */
+ char *dir;
+
+ /* XXX - XXX - MAJOR HACK - DO NOT SHIP - this needs to
+ be !pipeout, but we don't know that here yet */
+ if (!run_module_prog)
+ goto out;
+
+ dir = where ? where : mname;
+ /* XXX - think about making null repositories at each dir here
+ instead of just at the bottom */
+ make_directories (dir);
+ if (chdir (dir) < 0)
+ {
+ error (0, errno, "cannot chdir to %s", dir);
+ spec_opt = NULL;
+ err++;
+ goto out;
+ }
+ if (!isfile (CVSADM) && !isfile (OCVSADM))
+ {
+ char nullrepos[PATH_MAX];
+
+ (void) sprintf (nullrepos, "%s/%s/%s", CVSroot,
+ CVSROOTADM, CVSNULLREPOS);
+ if (!isfile (nullrepos))
+ (void) mkdir (nullrepos, 0777);
+ Create_Admin (".", nullrepos, (char *) NULL, (char *) NULL);
+ if (!noexec)
+ {
+ FILE *fp;
+
+ fp = open_file (CVSADM_ENTSTAT, "w+");
+ if (fclose (fp) == EOF)
+ error (1, errno, "cannot close %s", CVSADM_ENTSTAT);
+ }
+ }
+ out:
+ goto do_special;
+ }
+ }
+
+ /* don't do special options only part of a module was specified */
+ if (mfile != NULL)
+ spec_opt = NULL;
+
+ /*
+ * value now contains one of the following:
+ * 1) dir
+ * 2) dir file
+ * 3) the value from modules without any special args
+ * [ args ] dir [file] [file] ...
+ * or -a module [ module ] ...
+ */
+
+ /* Put the value on a line with XXX prepended for getopt to eat */
+ (void) sprintf (line, "%s %s", "XXX", value);
+
+ /* turn the line into an argv[] array */
+ line2argv (&modargc, xmodargv, line);
+ modargv = xmodargv;
+
+ /* parse the args */
+ optind = 1;
+ while ((c = gnu_getopt (modargc, modargv, CVSMODULE_OPTS)) != -1)
+ {
+ switch (c)
+ {
+ case 'a':
+ alias = 1;
+ break;
+ case 'd':
+ if (mwhere)
+ free (mwhere);
+ mwhere = xstrdup (optarg);
+ break;
+ case 'i':
+ checkin_prog = optarg;
+ break;
+ case 'l':
+ local_specified = 1;
+ case 'o':
+ checkout_prog = optarg;
+ break;
+ case 't':
+ tag_prog = optarg;
+ break;
+ case 'u':
+ update_prog = optarg;
+ break;
+ case '?':
+ error (0, 0,
+ "modules file has invalid option for key %s value %s",
+ key.dptr, val.dptr);
+ err++;
+ if (mwhere)
+ free (mwhere);
+ free (zvalue);
+ return (err);
+ }
+ }
+ modargc -= optind;
+ modargv += optind;
+ if (modargc == 0)
+ {
+ error (0, 0, "modules file missing directory for module %s", mname);
+ if (mwhere)
+ free (mwhere);
+ free (zvalue);
+ return (++err);
+ }
+
+ /* if this was an alias, call ourselves recursively for each module */
+ if (alias)
+ {
+ int i;
+
+ for (i = 0; i < modargc; i++)
+ err += do_module (db, modargv[i], m_type, msg, callback_proc,
+ where, shorten, local_specified,
+ run_module_prog, extra_arg);
+ if (mwhere)
+ free (mwhere);
+ free (zvalue);
+ return (err);
+ }
+
+ /* otherwise, process this module */
+ err += callback_proc (&modargc, modargv, where, mwhere, mfile, shorten,
+ local_specified, mname, msg);
+
+ /* clean up */
+ free_names (&modargc, modargv);
+
+ /* if there were special include args, process them now */
+
+ do_special:
+
+ /* blow off special options if -l was specified */
+ if (local_specified)
+ spec_opt = NULL;
+
+ while (spec_opt != NULL)
+ {
+ char *next_opt;
+
+ cp = index (spec_opt, CVSMODULE_SPEC);
+ if (cp != NULL)
+ {
+ /* save the beginning of the next arg */
+ next_opt = cp + 1;
+
+ /* strip whitespace off the end */
+ do
+ *cp = '\0';
+ while (isspace (*--cp));
+ }
+ else
+ next_opt = NULL;
+
+ /* strip whitespace from front */
+ while (isspace (*spec_opt))
+ spec_opt++;
+
+ if (*spec_opt == '\0')
+ error (0, 0, "Mal-formed %c option for module %s - ignored",
+ CVSMODULE_SPEC, mname);
+ else
+ err += do_module (db, spec_opt, m_type, msg, callback_proc,
+ (char *) NULL, 0, local_specified,
+ run_module_prog, extra_arg);
+ spec_opt = next_opt;
+ }
+
+ /* write out the checkin/update prog files if necessary */
+ if (err == 0 && !noexec && m_type == CHECKOUT && run_module_prog)
+ {
+ FILE *fp;
+
+ if (checkin_prog != NULL)
+ {
+ fp = open_file (CVSADM_CIPROG, "w+");
+ (void) fprintf (fp, "%s\n", checkin_prog);
+ if (fclose (fp) == EOF)
+ error (1, errno, "cannot close %s", CVSADM_CIPROG);
+ }
+ if (update_prog != NULL)
+ {
+ fp = open_file (CVSADM_UPROG, "w+");
+ (void) fprintf (fp, "%s\n", update_prog);
+ if (fclose (fp) == EOF)
+ error (1, errno, "cannot close %s", CVSADM_UPROG);
+ }
+ }
+
+ /* cd back to where we started */
+ if (chdir (cwd) < 0)
+ error (1, errno, "failed chdir to %s!", cwd);
+
+ /* run checkout or tag prog if appropriate */
+ if (err == 0 && run_module_prog)
+ {
+ if ((m_type == TAG && tag_prog != NULL) ||
+ (m_type == CHECKOUT && checkout_prog != NULL))
+ {
+ /*
+ * If a relative pathname is specified as the checkout or
+ * tag proc, try to tack on the current "where" value.
+ * if we can't find a matching program, just punt and use
+ * whatever is specified in the modules file.
+ */
+ char real_prog[PATH_MAX];
+ char *prog = (m_type == TAG ? tag_prog : checkout_prog);
+ char *real_where = (where != NULL ? where : mwhere);
+
+ if ((*prog != '/') && (*prog != '.'))
+ {
+ (void) sprintf (real_prog, "%s/%s", real_where, prog);
+ if (isfile (real_prog))
+ prog = real_prog;
+ }
+
+ run_setup ("%s %s", prog, real_where);
+ if (extra_arg)
+ run_arg (extra_arg);
+
+ if (!quiet)
+ {
+ (void) printf ("%s %s: Executing '", program_name,
+ command_name);
+ run_print (stdout);
+ (void) printf ("'\n");
+ }
+ err += run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ }
+ }
+
+ /* clean up */
+ if (mwhere)
+ free (mwhere);
+ free (zvalue);
+
+ return (err);
+}
+
+/* - Read all the records from the modules database into an array.
+ - Sort the array depending on what format is desired.
+ - Print the array in the format desired.
+
+ Currently, there are only two "desires":
+
+ 1. Sort by module name and format the whole entry including switches,
+ files and the comment field: (Including aliases)
+
+ modulename -s switches, one per line, even if
+ -i it has many switches.
+ Directories and files involved, formatted
+ to cover multiple lines if necessary.
+ # Comment, also formatted to cover multiple
+ # lines if necessary.
+
+ 2. Sort by status field string and print: (*not* including aliases)
+
+ modulename STATUS Directories and files involved, formatted
+ to cover multiple lines if necessary.
+ # Comment, also formatted to cover multiple
+ # lines if necessary.
+*/
+
+static struct sortrec *s_head;
+
+static int s_max = 0; /* Number of elements allocated */
+static int s_count = 0; /* Number of elements used */
+
+static int Status;
+static char def_status[] = "NONE";
+
+/* Sort routine for qsort:
+ - If we want the "Status" field to be sorted, check it first.
+ - Then compare the "module name" fields. Since they are unique, we don't
+ have to look further.
+*/
+static int
+sort_order (l, r)
+ CONST PTR l;
+ CONST PTR r;
+{
+ int i;
+ CONST struct sortrec *left = (CONST struct sortrec *) l;
+ CONST struct sortrec *right = (CONST struct sortrec *) r;
+
+ if (Status)
+ {
+ /* If Sort by status field, compare them. */
+ if ((i = strcmp (left->status, right->status)) != 0)
+ return (i);
+ }
+ return (strcmp (left->modname, right->modname));
+}
+
+static void
+save_d (k, ks, d, ds)
+ char *k;
+ int ks;
+ char *d;
+ int ds;
+{
+ char *cp, *cp2;
+ struct sortrec *s_rec;
+
+ if (Status && *d == '-' && *(d + 1) == 'a')
+ return; /* We want "cvs co -s" and it is an alias! */
+
+ if (s_count == s_max)
+ {
+ s_max += 64;
+ s_head = (struct sortrec *) xrealloc ((char *) s_head, s_max * sizeof (*s_head));
+ }
+ s_rec = &s_head[s_count];
+ s_rec->modname = cp = xmalloc (ks + 1);
+ (void) strncpy (cp, k, ks);
+ *(cp + ks) = '\0';
+
+ s_rec->rest = cp2 = xmalloc (ds + 1);
+ cp = d;
+ *(cp + ds) = '\0'; /* Assumes an extra byte at end of static dbm buffer */
+
+ while (isspace (*cp))
+ cp++;
+ /* Turn <spaces> into one ' ' -- makes the rest of this routine simpler */
+ while (*cp)
+ {
+ if (isspace (*cp))
+ {
+ *cp2++ = ' ';
+ while (isspace (*cp))
+ cp++;
+ }
+ else
+ *cp2++ = *cp++;
+ }
+ *cp2 = '\0';
+
+ /* Look for the "-s statusvalue" text */
+ if (Status)
+ {
+ s_rec->status = def_status;
+
+ /* Minor kluge, but general enough to maintain */
+ for (cp = s_rec->rest; (cp2 = index (cp, '-')) != NULL; cp = ++cp2)
+ {
+ if (*(cp2 + 1) == 's' && *(cp2 + 2) == ' ')
+ {
+ s_rec->status = (cp2 += 3);
+ while (*cp2 != ' ')
+ cp2++;
+ *cp2++ = '\0';
+ cp = cp2;
+ break;
+ }
+ }
+ }
+ else
+ cp = s_rec->rest;
+
+ /* Find comment field, clean up on all three sides & compress blanks */
+ if ((cp2 = cp = index (cp, '#')) != NULL)
+ {
+ if (*--cp2 == ' ')
+ *cp2 = '\0';
+ if (*++cp == ' ')
+ cp++;
+ s_rec->comment = cp;
+ }
+ else
+ s_rec->comment = "";
+
+ s_count++;
+}
+
+void
+cat_module (status)
+ int status;
+{
+ DBM *db;
+ datum key, val;
+ int i, c, wid, argc, cols = 80, indent, fill;
+ int moduleargc;
+ struct sortrec *s_h;
+ char *cp, *cp2, **argv;
+ char line[MAXLINELEN], *moduleargv[MAXFILEPERDIR];
+
+#ifdef sun
+#ifdef TIOCGSIZE
+ struct ttysize ts;
+
+ (void) ioctl (0, TIOCGSIZE, &ts);
+ cols = ts.ts_cols;
+#endif
+#else
+#ifdef TIOCGWINSZ
+ struct winsize ws;
+
+ (void) ioctl (0, TIOCGWINSZ, &ws);
+ cols = ws.ws_col;
+#endif
+#endif
+
+ Status = status;
+
+ /* Read the whole modules file into allocated records */
+ if (!(db = open_module ()))
+ error (1, 0, "failed to open the modules file");
+
+ for (key = dbm_firstkey (db); key.dptr != NULL; key = dbm_nextkey (db))
+ {
+ val = dbm_fetch (db, key);
+ if (val.dptr != NULL)
+ save_d (key.dptr, key.dsize, val.dptr, val.dsize);
+ }
+
+ /* Sort the list as requested */
+ qsort ((PTR) s_head, s_count, sizeof (struct sortrec), sort_order);
+
+ /*
+ * Run through the sorted array and format the entries
+ * indent = space for modulename + space for status field
+ */
+ indent = 12 + (status * 12);
+ fill = cols - (indent + 2);
+ for (s_h = s_head, i = 0; i < s_count; i++, s_h++)
+ {
+ /* Print module name (and status, if wanted) */
+ (void) printf ("%-12s", s_h->modname);
+ if (status)
+ {
+ (void) printf (" %-11s", s_h->status);
+ if (s_h->status != def_status)
+ *(s_h->status + strlen (s_h->status)) = ' ';
+ }
+
+ /* Parse module file entry as command line and print options */
+ (void) sprintf (line, "%s %s", s_h->modname, s_h->rest);
+ line2argv (&moduleargc, moduleargv, line);
+ argc = moduleargc;
+ argv = moduleargv;
+
+ optind = 1;
+ wid = 0;
+ while ((c = gnu_getopt (argc, argv, CVSMODULE_OPTS)) != -1)
+ {
+ if (!status)
+ {
+ if (c == 'a')
+ {
+ (void) printf (" -a");
+ wid += 3; /* Could just set it to 3 */
+ }
+ else
+ {
+ if (strlen (optarg) + 4 + wid > (unsigned) fill)
+ {
+ (void) printf ("\n%*s", indent, "");
+ wid = 0;
+ }
+ (void) printf (" -%c %s", c, optarg);
+ wid += strlen (optarg) + 4;
+ }
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Format and Print all the files and directories */
+ for (; argc--; argv++)
+ {
+ if (strlen (*argv) + wid > (unsigned) fill)
+ {
+ (void) printf ("\n%*s", indent, "");
+ wid = 0;
+ }
+ (void) printf (" %s", *argv);
+ wid += strlen (*argv) + 1;
+ }
+ (void) printf ("\n");
+
+ /* Format the comment field -- save_d (), compressed spaces */
+ for (cp2 = cp = s_h->comment; *cp; cp2 = cp)
+ {
+ (void) printf ("%*s # ", indent, "");
+ if (strlen (cp2) < (unsigned) (fill - 2))
+ {
+ (void) printf ("%s\n", cp2);
+ break;
+ }
+ cp += fill - 2;
+ while (*cp != ' ' && cp > cp2)
+ cp--;
+ if (cp == cp2)
+ {
+ (void) printf ("%s\n", cp2);
+ break;
+ }
+
+ *cp++ = '\0';
+ (void) printf ("%s\n", cp2);
+ }
+ }
+}
diff --git a/gnu/usr.bin/cvs/cvs/no_diff.c b/gnu/usr.bin/cvs/cvs/no_diff.c
new file mode 100644
index 0000000..4057c88
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/no_diff.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * No Difference
+ *
+ * The user file looks modified judging from its time stamp; however it needn't
+ * be. No_difference() finds out whether it is or not. If it is not, it
+ * updates the administration.
+ *
+ * returns 0 if no differences are found and non-zero otherwise
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)no_diff.c 1.35 92/03/31";
+#endif
+
+int
+No_Difference (file, vers, entries)
+ char *file;
+ Vers_TS *vers;
+ List *entries;
+{
+ Node *p;
+ char tmp[L_tmpnam+1];
+ int ret;
+ char *ts, *options;
+ int retcode = 0;
+
+ if (!vers->srcfile || !vers->srcfile->path)
+ return (-1); /* different since we couldn't tell */
+
+ if (vers->entdata && vers->entdata->options)
+ options = xstrdup (vers->entdata->options);
+ else
+ options = xstrdup ("");
+
+ run_setup ("%s%s -p -q -r%s %s", Rcsbin, RCS_CO,
+ vers->vn_user ? vers->vn_user : "", options);
+ run_arg (vers->srcfile->path);
+ if ((retcode = run_exec (RUN_TTY, tmpnam (tmp), RUN_TTY, RUN_REALLY)) == 0)
+ {
+ if (!iswritable (file)) /* fix the modes as a side effect */
+ xchmod (file, 1);
+
+ /* do the byte by byte compare */
+ if (xcmp (file, tmp) == 0)
+ {
+ if (cvswrite == FALSE) /* fix the modes as a side effect */
+ xchmod (file, 0);
+
+ /* no difference was found, so fix the entries file */
+ ts = time_stamp (file);
+ Register (entries, file,
+ vers->vn_user ? vers->vn_user : vers->vn_rcs, ts,
+ options, vers->tag, vers->date);
+ free (ts);
+
+ /* update the entdata pointer in the vers_ts structure */
+ p = findnode (entries, file);
+ vers->entdata = (Entnode *) p->data;
+
+ ret = 0;
+ }
+ else
+ ret = 1; /* files were really different */
+ }
+ else
+ {
+ error (0, retcode == -1 ? errno : 0,
+ "could not check out revision %s of %s", vers->vn_user, file);
+ ret = -1; /* different since we couldn't tell */
+ }
+
+ if (trace)
+ (void) fprintf (stderr, "-> unlink(%s)\n", tmp);
+ (void) unlink (tmp);
+ free (options);
+ return (ret);
+}
diff --git a/gnu/usr.bin/cvs/cvs/parseinfo.c b/gnu/usr.bin/cvs/cvs/parseinfo.c
new file mode 100644
index 0000000..65343f5
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/parseinfo.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)parseinfo.c 1.16 92/04/10";
+#endif
+
+/*
+ * Parse the INFOFILE file for the specified REPOSITORY. Invoke CALLPROC for
+ * each line in the file that matches the REPOSITORY.
+ * Return 0 for success, -1 if there was not an INFOFILE, and >0 for failure.
+ */
+int
+Parse_Info (infofile, repository, callproc, all)
+ char *infofile;
+ char *repository;
+ int (*callproc) ();
+ int all;
+{
+ int err = 0;
+ FILE *fp_info;
+ char infopath[PATH_MAX];
+ char line[MAXLINELEN];
+ char *default_value = NULL;
+ int callback_done, line_number;
+ char *cp, *exp, *value, *srepos;
+ CONST char *regex_err;
+
+ if (CVSroot == NULL)
+ {
+ /* XXX - should be error maybe? */
+ error (0, 0, "CVSROOT variable not set");
+ return (1);
+ }
+
+ /* find the info file and open it */
+ (void) sprintf (infopath, "%s/%s/%s", CVSroot,
+ CVSROOTADM, infofile);
+ if ((fp_info = fopen (infopath, "r")) == NULL)
+ return (0); /* no file -> nothing special done */
+
+ /* strip off the CVSROOT if repository was absolute */
+ srepos = Short_Repository (repository);
+
+ /* search the info file for lines that match */
+ callback_done = line_number = 0;
+ while (fgets (line, sizeof (line), fp_info) != NULL)
+ {
+ line_number++;
+
+ /* skip lines starting with # */
+ if (line[0] == '#')
+ continue;
+
+ /* skip whitespace at beginning of line */
+ for (cp = line; *cp && isspace (*cp); cp++)
+ ;
+
+ /* if *cp is null, the whole line was blank */
+ if (*cp == '\0')
+ continue;
+
+ /* the regular expression is everything up to the first space */
+ for (exp = cp; *cp && !isspace (*cp); cp++)
+ ;
+ if (*cp != '\0')
+ *cp++ = '\0';
+
+ /* skip whitespace up to the start of the matching value */
+ while (*cp && isspace (*cp))
+ cp++;
+
+ /* no value to match with the regular expression is an error */
+ if (*cp == '\0')
+ {
+ error (0, 0, "syntax error at line %d file %s; ignored",
+ line_number, infofile);
+ continue;
+ }
+ value = cp;
+
+ /* strip the newline off the end of the value */
+ if ((cp = rindex (value, '\n')) != NULL)
+ *cp = '\0';
+
+ /*
+ * At this point, exp points to the regular expression, and value
+ * points to the value to call the callback routine with. Evaluate
+ * the regular expression against srepos and callback with the value
+ * if it matches.
+ */
+
+ /* save the default value so we have it later if we need it */
+ if (strcmp (exp, "DEFAULT") == 0)
+ {
+ default_value = xstrdup (value);
+ continue;
+ }
+
+ /*
+ * For a regular expression of "ALL", do the callback always We may
+ * execute lots of ALL callbacks in addition to one regular matching
+ * callback or default
+ */
+ if (strcmp (exp, "ALL") == 0)
+ {
+ if (all)
+ err += callproc (repository, value);
+ else
+ error(0, 0, "Keyword `ALL' is ignored at line %d in %s file",
+ line_number, infofile);
+ continue;
+ }
+
+ /* see if the repository matched this regular expression */
+ if ((regex_err = re_comp (exp)) != NULL)
+ {
+ error (0, 0, "bad regular expression at line %d file %s: %s",
+ line_number, infofile, regex_err);
+ continue;
+ }
+ if (re_exec (srepos) == 0)
+ continue; /* no match */
+
+ /* it did, so do the callback and note that we did one */
+ err += callproc (repository, value);
+ callback_done = 1;
+ }
+ (void) fclose (fp_info);
+
+ /* if we fell through and didn't callback at all, do the default */
+ if (callback_done == 0 && default_value != NULL)
+ err += callproc (repository, default_value);
+
+ /* free up space if necessary */
+ if (default_value != NULL)
+ free (default_value);
+
+ return (err);
+}
diff --git a/gnu/usr.bin/cvs/cvs/patch.c b/gnu/usr.bin/cvs/cvs/patch.c
new file mode 100644
index 0000000..abc02fd
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/patch.c
@@ -0,0 +1,541 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Patch
+ *
+ * Create a Larry Wall format "patch" file between a previous release and the
+ * current head of a module, or between two releases. Can specify the
+ * release as either a date or a revision number.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)patch.c 1.50 92/04/10";
+
+#endif
+
+#if __STDC__
+static SIGTYPE patch_cleanup (void);
+static Dtype patch_dirproc (char *dir, char *repos, char *update_dir);
+static int patch_fileproc (char *file, char *update_dir, char *repository,
+ List * entries, List * srcfiles);
+static int patch_proc (int *pargc, char *argv[], char *xwhere,
+ char *mwhere, char *mfile, int shorten,
+ int local_specified, char *mname, char *msg);
+#else
+static int patch_proc ();
+static int patch_fileproc ();
+static Dtype patch_dirproc ();
+static SIGTYPE patch_cleanup ();
+#endif /* __STDC__ */
+
+static int force_tag_match = 1;
+static int patch_short = 0;
+static int toptwo_diffs = 0;
+static int local = 0;
+static char *options = NULL;
+static char *rev1 = NULL;
+static char *rev2 = NULL;
+static char *date1 = NULL;
+static char *date2 = NULL;
+static char *K_flag1 = NULL;
+static char *K_flag2 = NULL;
+static char tmpfile1[L_tmpnam+1], tmpfile2[L_tmpnam+1], tmpfile3[L_tmpnam+1];
+static int unidiff = 0;
+
+static char *patch_usage[] =
+{
+ "Usage: %s %s [-Qflq] [-c|-u] [-s|-t] [-V %%d]\n",
+ " -r rev|-D date [-r rev2 | -D date2] modules...\n",
+ "\t-Q\tReally quiet.\n",
+ "\t-f\tForce a head revision match if tag/date not found.\n",
+ "\t-l\tLocal directory only, not recursive\n",
+ "\t-c\tContext diffs (default)\n",
+ "\t-u\tUnidiff format.\n",
+ "\t-s\tShort patch - one liner per file.\n",
+ "\t-t\tTop two diffs - last change made to the file.\n",
+ "\t-D date\tDate.\n",
+ "\t-r rev\tRevision - symbolic or numeric.\n",
+ "\t-V vers\tUse RCS Version \"vers\" for keyword expansion.\n",
+ "\t-K key\tUse RCS key -K option on checkout.\n",
+ NULL
+};
+
+int
+patch (argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int i;
+ int c;
+ int err = 0;
+ DBM *db;
+
+ if (argc == -1)
+ usage (patch_usage);
+
+ optind = 1;
+ while ((c = gnu_getopt (argc, argv, "V:k:cuftsQqlRD:r:K:")) != -1)
+ {
+ switch (c)
+ {
+ case 'Q':
+ really_quiet = 1;
+ /* FALL THROUGH */
+ case 'q':
+ quiet = 1;
+ break;
+ case 'f':
+ force_tag_match = 0;
+ break;
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case 't':
+ toptwo_diffs = 1;
+ break;
+ case 's':
+ patch_short = 1;
+ break;
+ case 'D':
+ if (rev2 != NULL || date2 != NULL)
+ error (1, 0,
+ "no more than two revisions/dates can be specified");
+ if (rev1 != NULL || date1 != NULL)
+ date2 = Make_Date (optarg);
+ else
+ date1 = Make_Date (optarg);
+ break;
+ case 'r':
+ if (rev2 != NULL || date2 != NULL)
+ error (1, 0,
+ "no more than two revisions/dates can be specified");
+ if (rev1 != NULL || date1 != NULL)
+ rev2 = optarg;
+ else
+ rev1 = optarg;
+ break;
+ case 'k':
+ if (options)
+ free (options);
+ options = RCS_check_kflag (optarg);
+ break;
+ case 'V':
+ if (atoi (optarg) <= 0)
+ error (1, 0, "must specify a version number to -V");
+ if (options)
+ free (options);
+ options = xmalloc (strlen (optarg) + 1 + 2); /* for the -V */
+ (void) sprintf (options, "-V%s", optarg);
+ break;
+ case 'u':
+ unidiff = 1; /* Unidiff */
+ break;
+ case 'c': /* Context diff */
+ unidiff = 0;
+ break;
+ case 'K':
+ if (K_flag2 != NULL)
+ error (1, 0, "no more than two -K flags can be specified");
+ if (K_flag1 != NULL)
+ K_flag2 = optarg;
+ else
+ K_flag1 = optarg;
+ break;
+ case '?':
+ default:
+ usage (patch_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Sanity checks */
+ /* Check for dummy -K flags */
+ if (K_flag1 && K_flag1[0] != 'e' && K_flag1[0] != 'i')
+ error (1, 0, "-K flag does not start e or i");
+ if (K_flag2 && K_flag2[0] != 'e' && K_flag2[0] != 'i')
+ error (1, 0, "-K flag does not start e or i");
+ if (argc < 1)
+ usage (patch_usage);
+
+ if (toptwo_diffs && patch_short)
+ error (1, 0, "-t and -s options are mutually exclusive");
+ if (toptwo_diffs && (date1 != NULL || date2 != NULL ||
+ rev1 != NULL || rev2 != NULL))
+ error (1, 0, "must not specify revisions/dates with -t option!");
+
+ if (!toptwo_diffs && (date1 == NULL && date2 == NULL &&
+ rev1 == NULL && rev2 == NULL))
+ error (1, 0, "must specify at least one revision/date!");
+ if (date1 != NULL && date2 != NULL)
+ if (RCS_datecmp (date1, date2) >= 0)
+ error (1, 0, "second date must come after first date!");
+
+ /* if options is NULL, make it a NULL string */
+ if (options == NULL)
+ options = xstrdup ("");
+
+ /* clean up if we get a signal */
+ (void) SIG_register (SIGHUP, patch_cleanup);
+ (void) SIG_register (SIGINT, patch_cleanup);
+ (void) SIG_register (SIGQUIT, patch_cleanup);
+ (void) SIG_register (SIGPIPE, patch_cleanup);
+ (void) SIG_register (SIGTERM, patch_cleanup);
+
+ db = open_module ();
+ for (i = 0; i < argc; i++)
+ err += do_module (db, argv[i], PATCH, "Patching", patch_proc,
+ (char *) NULL, 0, 0, 0, (char *) NULL);
+ close_module (db);
+ free (options);
+ patch_cleanup ();
+ return (err);
+}
+
+/*
+ * callback proc for doing the real work of patching
+ */
+/* ARGSUSED */
+static char where[PATH_MAX];
+static int
+patch_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified,
+ mname, msg)
+ int *pargc;
+ char *argv[];
+ char *xwhere;
+ char *mwhere;
+ char *mfile;
+ int shorten;
+ int local_specified;
+ char *mname;
+ char *msg;
+{
+ int err = 0;
+ int which;
+ char repository[PATH_MAX];
+
+ (void) sprintf (repository, "%s/%s", CVSroot, argv[0]);
+ (void) strcpy (where, argv[0]);
+
+ /* if mfile isn't null, we need to set up to do only part of the module */
+ if (mfile != NULL)
+ {
+ char *cp;
+ char path[PATH_MAX];
+
+ /* if the portion of the module is a path, put the dir part on repos */
+ if ((cp = rindex (mfile, '/')) != NULL)
+ {
+ *cp = '\0';
+ (void) strcat (repository, "/");
+ (void) strcat (repository, mfile);
+ (void) strcat (where, "/");
+ (void) strcat (where, mfile);
+ mfile = cp + 1;
+ }
+
+ /* take care of the rest */
+ (void) sprintf (path, "%s/%s", repository, mfile);
+ if (isdir (path))
+ {
+ /* directory means repository gets the dir tacked on */
+ (void) strcpy (repository, path);
+ (void) strcat (where, "/");
+ (void) strcat (where, mfile);
+ }
+ else
+ {
+ int i;
+
+ /* a file means muck argv */
+ for (i = 1; i < *pargc; i++)
+ free (argv[i]);
+ argv[1] = xstrdup (mfile);
+ (*pargc) = 2;
+ }
+ }
+
+ /* cd to the starting repository */
+ if (chdir (repository) < 0)
+ {
+ error (0, errno, "cannot chdir to %s", repository);
+ return (1);
+ }
+
+ if (force_tag_match)
+ which = W_REPOS | W_ATTIC;
+ else
+ which = W_REPOS;
+
+ /* start the recursion processor */
+ err = start_recursion (patch_fileproc, (int (*) ()) NULL, patch_dirproc,
+ (int (*) ()) NULL, *pargc - 1, argv + 1, local,
+ which, 0, 1, where, 1);
+
+ return (err);
+}
+
+/*
+ * Called to examine a particular RCS file, as appropriate with the options
+ * that were set above.
+ */
+/* ARGSUSED */
+static int
+patch_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ char *vers_tag, *vers_head;
+ char rcsspace[PATH_MAX];
+ char *rcs = rcsspace;
+ Node *p;
+ RCSNode *rcsfile;
+ FILE *fp1, *fp2, *fp3;
+ int ret = 0;
+ int isattic = 0;
+ int retcode = 0;
+ char file1[PATH_MAX], file2[PATH_MAX], strippath[PATH_MAX];
+ char line1[MAXLINELEN], line2[MAXLINELEN];
+ char *cp1, *cp2, *commap;
+ FILE *fp;
+
+
+ /* find the parsed rcs file */
+ p = findnode (srcfiles, file);
+ if (p == NULL)
+ return (1);
+ rcsfile = (RCSNode *) p->data;
+ if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
+ isattic = 1;
+
+ (void) sprintf (rcs, "%s%s", file, RCSEXT);
+
+ /* if vers_head is NULL, may have been removed from the release */
+ if (isattic && rev2 == NULL && date2 == NULL)
+ vers_head = NULL;
+ else
+ vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match);
+
+ if (toptwo_diffs)
+ {
+ if (vers_head == NULL)
+ return (1);
+
+ if (!date1)
+ date1 = xmalloc (50); /* plenty big :-) */
+ *date1 = '\0';
+ if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == -1)
+ {
+ if (!really_quiet)
+ error (0, 0, "cannot find date in rcs file %s revision %s",
+ rcs, vers_head);
+ return (1);
+ }
+ }
+ vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match);
+
+ if (vers_tag == NULL && (vers_head == NULL || isattic))
+ return (0); /* nothing known about specified revs */
+
+ if (vers_tag && vers_head && strcmp (vers_head, vers_tag) == 0)
+ return (0); /* not changed between releases */
+
+ if (patch_short)
+ {
+ (void) printf ("File ");
+ if (vers_tag == NULL)
+ (void) printf ("%s is new; current revision %s\n", rcs, vers_head);
+ else if (vers_head == NULL)
+ (void) printf ("%s is removed; not included in release %s\n",
+ rcs, rev2 ? rev2 : date2);
+ else
+ (void) printf ("%s changed from revision %s to %s\n",
+ rcs, vers_tag, vers_head);
+ return (0);
+ }
+ if ((fp1 = fopen (tmpnam (tmpfile1), "w+")) != NULL)
+ (void) fclose (fp1);
+ if ((fp2 = fopen (tmpnam (tmpfile2), "w+")) != NULL)
+ (void) fclose (fp2);
+ if ((fp3 = fopen (tmpnam (tmpfile3), "w+")) != NULL)
+ (void) fclose (fp3);
+ if (fp1 == NULL || fp2 == NULL || fp3 == NULL)
+ {
+ error (0, 0, "cannot create temporary files");
+ ret = 1;
+ goto out;
+ }
+ if (vers_tag != NULL)
+ {
+ run_setup ("%s%s %s -p -q -r%s %s%s", Rcsbin, RCS_CO, options,
+ vers_tag, K_flag1 ? "-K" : "", K_flag1 ? K_flag1 : "");
+ run_arg (rcsfile->path);
+ if ((retcode = run_exec (RUN_TTY, tmpfile1, RUN_TTY, RUN_NORMAL)) != 0)
+ {
+ if (!really_quiet)
+ error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
+ "co of revision %s in %s failed", vers_tag, rcs);
+ ret = 1;
+ goto out;
+ }
+ }
+ else if (toptwo_diffs)
+ {
+ ret = 1;
+ goto out;
+ }
+ if (vers_head != NULL)
+ {
+ run_setup ("%s%s %s -p -q -r%s %s%s", Rcsbin, RCS_CO, options,
+ vers_head, K_flag2 ? "-K" : "", K_flag2 ? K_flag2 : "");
+ run_arg (rcsfile->path);
+ if ((retcode = run_exec (RUN_TTY, tmpfile2, RUN_TTY, RUN_NORMAL)) != 0)
+ {
+ if (!really_quiet)
+ error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
+ "co of revision %s in %s failed", vers_head, rcs);
+ ret = 1;
+ goto out;
+ }
+ }
+ run_setup ("%s -%c", DIFF, unidiff ? 'u' : 'c');
+ run_arg (tmpfile1);
+ run_arg (tmpfile2);
+ switch (run_exec (RUN_TTY, tmpfile3, RUN_TTY, RUN_NORMAL))
+ {
+ case -1: /* fork/wait failure */
+ error (1, errno, "fork for diff failed on %s", rcs);
+ break;
+ case 0: /* nothing to do */
+ break;
+ case 1:
+ /*
+ * The two revisions are really different, so read the first two
+ * lines of the diff output file, and munge them to include more
+ * reasonable file names that "patch" will understand.
+ */
+ fp = open_file (tmpfile3, "r");
+ if (fgets (line1, sizeof (line1), fp) == NULL ||
+ fgets (line2, sizeof (line2), fp) == NULL)
+ {
+ error (0, errno, "failed to read diff file header %s for %s",
+ tmpfile3, rcs);
+ ret = 1;
+ (void) fclose (fp);
+ goto out;
+ }
+ if (!unidiff)
+ {
+ if (strncmp (line1, "*** ", 4) != 0 ||
+ strncmp (line2, "--- ", 4) != 0 ||
+ (cp1 = index (line1, '\t')) == NULL ||
+ (cp2 = index (line2, '\t')) == NULL)
+ {
+ error (0, 0, "invalid diff header for %s", rcs);
+ ret = 1;
+ (void) fclose (fp);
+ goto out;
+ }
+ }
+ else
+ {
+ if (strncmp (line1, "--- ", 4) != 0 ||
+ strncmp (line2, "+++ ", 4) != 0 ||
+ (cp1 = index (line1, '\t')) == NULL ||
+ (cp2 = index (line2, '\t')) == NULL)
+ {
+ error (0, 0, "invalid unidiff header for %s", rcs);
+ ret = 1;
+ (void) fclose (fp);
+ goto out;
+ }
+ }
+ if (CVSroot != NULL)
+ (void) sprintf (strippath, "%s/", CVSroot);
+ else
+ (void) strcpy (strippath, REPOS_STRIP);
+ if (strncmp (rcs, strippath, strlen (strippath)) == 0)
+ rcs += strlen (strippath);
+ commap = rindex (rcs, ',');
+ *commap = '\0';
+ if (vers_tag != NULL)
+ {
+ (void) sprintf (file1, "%s%s%s:%s", update_dir,
+ update_dir[0] ? "/" : "", rcs, vers_tag);
+ }
+ else
+ {
+ (void) strcpy (file1, DEVNULL);
+ }
+ (void) sprintf (file2, "%s%s%s:%s", update_dir,
+ update_dir[0] ? "/" : "", rcs,
+ vers_head ? vers_head : "removed");
+ if (unidiff)
+ {
+ (void) printf ("diff -u %s %s\n", file1, file2);
+ (void) printf ("--- %s%s+++ ", file1, cp1);
+ }
+ else
+ {
+ (void) printf ("diff -c %s %s\n", file1, file2);
+ (void) printf ("*** %s%s--- ", file1, cp1);
+ }
+
+ if (update_dir[0] != '\0')
+ (void) printf ("%s/", update_dir);
+ (void) printf ("%s%s", rcs, cp2);
+ while (fgets (line1, sizeof (line1), fp) != NULL)
+ (void) printf ("%s", line1);
+ (void) fclose (fp);
+ break;
+ default:
+ error (0, 0, "diff failed for %s", rcs);
+ }
+ out:
+ (void) unlink_file (tmpfile1);
+ (void) unlink_file (tmpfile2);
+ (void) unlink_file (tmpfile3);
+ return (ret);
+}
+
+/*
+ * Print a warm fuzzy message
+ */
+/* ARGSUSED */
+static Dtype
+patch_dirproc (dir, repos, update_dir)
+ char *dir;
+ char *repos;
+ char *update_dir;
+{
+ if (!quiet)
+ error (0, 0, "Diffing %s", update_dir);
+ return (R_PROCESS);
+}
+
+/*
+ * Clean up temporary files
+ */
+static SIGTYPE
+patch_cleanup ()
+{
+ if (tmpfile1[0] != '\0')
+ (void) unlink_file (tmpfile1);
+ if (tmpfile2[0] != '\0')
+ (void) unlink_file (tmpfile2);
+ if (tmpfile3[0] != '\0')
+ (void) unlink_file (tmpfile3);
+}
diff --git a/gnu/usr.bin/cvs/cvs/patchlevel.h b/gnu/usr.bin/cvs/cvs/patchlevel.h
new file mode 100644
index 0000000..dc2214d
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/patchlevel.h
@@ -0,0 +1 @@
+#define PATCHLEVEL 0
diff --git a/gnu/usr.bin/cvs/cvs/rcs.c b/gnu/usr.bin/cvs/cvs/rcs.c
new file mode 100644
index 0000000..e882b8c
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/rcs.c
@@ -0,0 +1,1449 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * The routines contained in this file do all the rcs file parsing and
+ * manipulation
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)rcs.c 1.28 92/03/31";
+#endif
+
+#if __STDC__
+static char *RCS_getbranch (RCSNode * rcs, char *tag, int force_tag_match);
+static char *RCS_getdatebranch (RCSNode * rcs, char *date, char *branch);
+static int getrcskey (FILE * fp, char **keyp, char **valp);
+static int parse_rcs_proc (Node * file);
+static int checkmagic_proc (Node *p);
+static void do_branches (List * list, char *val);
+static void do_symbols (List * list, char *val);
+static void null_delproc (Node * p);
+static void rcsnode_delproc (Node * p);
+static void rcsvers_delproc (Node * p);
+#else
+static int parse_rcs_proc ();
+static int checkmagic_proc ();
+static void rcsnode_delproc ();
+static void rcsvers_delproc ();
+static void null_delproc ();
+static int getrcskey ();
+static void do_symbols ();
+static void do_branches ();
+static char *RCS_getbranch ();
+static char *RCS_getdatebranch ();
+#endif /* __STDC__ */
+
+static List *rcslist;
+static char *repository;
+
+/*
+ * Parse all the rcs files specified and return a list
+ */
+List *
+RCS_parsefiles (files, xrepos)
+ List *files;
+ char *xrepos;
+{
+ /* initialize */
+ repository = xrepos;
+ rcslist = getlist ();
+
+ /* walk the list parsing files */
+ if (walklist (files, parse_rcs_proc) != 0)
+ {
+ /* free the list and return NULL on error */
+ dellist (&rcslist);
+ return ((List *) NULL);
+ }
+ else
+ /* return the list we built */
+ return (rcslist);
+}
+
+/*
+ * Parse an rcs file into a node on the rcs list
+ */
+static int
+parse_rcs_proc (file)
+ Node *file;
+{
+ Node *p;
+ RCSNode *rdata;
+
+ /* parse the rcs file into rdata */
+ rdata = RCS_parse (file->key, repository);
+
+ /* if we got a valid RCSNode back, put it on the list */
+ if (rdata != (RCSNode *) NULL)
+ {
+ p = getnode ();
+ p->key = xstrdup (file->key);
+ p->delproc = rcsnode_delproc;
+ p->type = RCSNODE;
+ p->data = (char *) rdata;
+ (void) addnode (rcslist, p);
+ }
+ return (0);
+}
+
+/*
+ * Parse an rcsfile given a user file name and a repository
+ */
+RCSNode *
+RCS_parse (file, repos)
+ char *file;
+ char *repos;
+{
+ RCSNode *rcs;
+ char rcsfile[PATH_MAX];
+
+ (void) sprintf (rcsfile, "%s/%s%s", repos, file, RCSEXT);
+ if (!isreadable (rcsfile))
+ {
+ (void) sprintf (rcsfile, "%s/%s/%s%s", repos, CVSATTIC,
+ file, RCSEXT);
+ if (!isreadable (rcsfile))
+ return (NULL);
+ rcs = RCS_parsercsfile (rcsfile);
+ if (rcs != NULL)
+ {
+ rcs->flags |= INATTIC;
+ rcs->flags |= VALID;
+ }
+ return (rcs);
+ }
+ rcs = RCS_parsercsfile (rcsfile);
+ if (rcs != NULL)
+ rcs->flags |= VALID;
+ return (rcs);
+}
+
+/*
+ * Do the real work of parsing an RCS file
+ */
+RCSNode *
+RCS_parsercsfile (rcsfile)
+ char *rcsfile;
+{
+ Node *q, *r;
+ RCSNode *rdata;
+ RCSVers *vnode;
+ int n;
+ char *cp;
+ char *key, *value;
+ FILE *fp;
+
+ /* open the rcsfile */
+ if ((fp = fopen (rcsfile, "r")) == NULL)
+ {
+ error (0, errno, "Couldn't open rcs file `%s'", rcsfile);
+ return (NULL);
+ }
+
+ /* make a node */
+ rdata = (RCSNode *) xmalloc (sizeof (RCSNode));
+ bzero ((char *) rdata, sizeof (RCSNode));
+ rdata->refcount = 1;
+ rdata->path = xstrdup (rcsfile);
+ rdata->versions = getlist ();
+ rdata->dates = getlist ();
+
+ /*
+ * process all the special header information, break out when we get to
+ * the first revision delta
+ */
+ for (;;)
+ {
+ /* get the next key/value pair */
+
+ /* if key is NULL here, then the file is missing some headers */
+ if (getrcskey (fp, &key, &value) == -1 || key == NULL)
+ {
+ if (!really_quiet)
+ error (0, 0, "`%s' does not appear to be a valid rcs file",
+ rcsfile);
+ freercsnode (&rdata);
+ (void) fclose (fp);
+ return (NULL);
+ }
+
+ /* process it */
+ if (strcmp (RCSHEAD, key) == 0 && value != NULL)
+ {
+ rdata->head = xstrdup (value);
+ continue;
+ }
+ if (strcmp (RCSBRANCH, key) == 0 && value != NULL)
+ {
+ rdata->branch = xstrdup (value);
+ if ((numdots (rdata->branch) & 1) != 0)
+ {
+ /* turn it into a branch if it's a revision */
+ cp = rindex (rdata->branch, '.');
+ *cp = '\0';
+ }
+ continue;
+ }
+ if (strcmp (RCSSYMBOLS, key) == 0)
+ {
+ if (value != NULL)
+ {
+ /* if there are tags, set up the tag list */
+ rdata->symbols = getlist ();
+ do_symbols (rdata->symbols, value);
+ continue;
+ }
+ }
+
+ /*
+ * check key for '.''s and digits (probably a rev) if it is a
+ * revision, we are done with the headers and are down to the
+ * revision deltas, so we break out of the loop
+ */
+ for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++)
+ /* do nothing */ ;
+ if (*cp == '\0' && strncmp (RCSDATE, value, strlen (RCSDATE)) == 0)
+ break;
+
+ /* if we haven't grabbed it yet, we didn't want it */
+ }
+
+ /*
+ * we got out of the loop, so we have the first part of the first
+ * revision delta in our hand key=the revision and value=the date key and
+ * its value
+ */
+ for (;;)
+ {
+ char *valp;
+ char date[MAXDATELEN];
+
+ /* grab the value of the date from value */
+ valp = value + strlen (RCSDATE);/* skip the "date" keyword */
+ while (isspace (*valp)) /* take space off front of value */
+ valp++;
+ (void) strcpy (date, valp);
+
+ /* get the nodes (q is by version, r is by date) */
+ q = getnode ();
+ r = getnode ();
+ q->type = RCSVERS;
+ r->type = RCSVERS;
+ q->delproc = rcsvers_delproc;
+ r->delproc = null_delproc;
+ q->data = r->data = xmalloc (sizeof (RCSVers));
+ bzero (q->data, sizeof (RCSVers));
+ vnode = (RCSVers *) q->data;
+
+ /* fill in the version before we forget it */
+ q->key = vnode->version = xstrdup (key);
+
+ /* throw away the author field */
+ (void) getrcskey (fp, &key, &value);
+
+ /* throw away the state field */
+ (void) getrcskey (fp, &key, &value);
+
+ /* fill in the date field */
+ r->key = vnode->date = xstrdup (date);
+
+ /* fill in the branch list (if any branches exist) */
+ (void) getrcskey (fp, &key, &value);
+ if (value != (char *) NULL)
+ {
+ vnode->branches = getlist ();
+ do_branches (vnode->branches, value);
+ }
+
+ /* fill in the next field if there is a next revision */
+ (void) getrcskey (fp, &key, &value);
+ if (value != (char *) NULL)
+ vnode->next = xstrdup (value);
+
+ /*
+ * at this point, we skip any user defined fields XXX - this is where
+ * we put the symbolic link stuff???
+ */
+ while ((n = getrcskey (fp, &key, &value)) >= 0)
+ {
+ /* if we have a revision, break and do it */
+ for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++)
+ /* do nothing */ ;
+ if (*cp == '\0' && strncmp (RCSDATE, value, strlen (RCSDATE)) == 0)
+ break;
+ }
+
+ /* add the nodes to the lists */
+ (void) addnode (rdata->versions, q);
+ (void) addnode (rdata->dates, r);
+
+ /*
+ * if we left the loop because there were no more keys, we break out
+ * of the revision processing loop
+ */
+ if (n < 0)
+ break;
+ }
+
+ (void) fclose (fp);
+ return (rdata);
+}
+
+/*
+ * rcsnode_delproc - free up an RCS type node
+ */
+static void
+rcsnode_delproc (p)
+ Node *p;
+{
+ freercsnode ((RCSNode **) & p->data);
+}
+
+/*
+ * freercsnode - free up the info for an RCSNode
+ */
+void
+freercsnode (rnodep)
+ RCSNode **rnodep;
+{
+ if (rnodep == NULL || *rnodep == NULL)
+ return;
+
+ ((*rnodep)->refcount)--;
+ if ((*rnodep)->refcount != 0)
+ {
+ *rnodep = (RCSNode *) NULL;
+ return;
+ }
+ free ((*rnodep)->path);
+ dellist (&(*rnodep)->versions);
+ dellist (&(*rnodep)->dates);
+ if ((*rnodep)->symbols != (List *) NULL)
+ dellist (&(*rnodep)->symbols);
+ if ((*rnodep)->head != (char *) NULL)
+ free ((*rnodep)->head);
+ if ((*rnodep)->branch != (char *) NULL)
+ free ((*rnodep)->branch);
+ free ((char *) *rnodep);
+ *rnodep = (RCSNode *) NULL;
+}
+
+/*
+ * rcsvers_delproc - free up an RCSVers type node
+ */
+static void
+rcsvers_delproc (p)
+ Node *p;
+{
+ RCSVers *rnode;
+
+ rnode = (RCSVers *) p->data;
+
+ if (rnode->branches != (List *) NULL)
+ dellist (&rnode->branches);
+ if (rnode->next != (char *) NULL)
+ free (rnode->next);
+ free ((char *) rnode);
+}
+
+/*
+ * null_delproc - don't free anything since it will be free'd by someone else
+ */
+/* ARGSUSED */
+static void
+null_delproc (p)
+ Node *p;
+{
+ /* don't do anything */
+}
+
+/*
+ * getrcskey - fill in the key and value from the rcs file the algorithm is
+ * as follows
+ *
+ * o skip whitespace o fill in key with everything up to next white
+ * space or semicolon
+ * o if key == "desc" then key and data are NULL and return -1
+ * o if key wasn't terminated by a semicolon, skip white space and fill
+ * in value with everything up to a semicolon o compress all whitespace
+ * down to a single space
+ * o if a word starts with @, do funky rcs processing
+ * o strip whitespace off end of value or set value to NULL if it empty
+ * o return 0 since we found something besides "desc"
+ */
+
+static char *key = NULL;
+static int keysize = 0;
+static char *value = NULL;
+static int valsize = 0;
+
+#define ALLOCINCR 1024
+
+static int
+getrcskey (fp, keyp, valp)
+ FILE *fp;
+ char **keyp;
+ char **valp;
+{
+ char *cur, *max;
+ int c;
+ int funky = 0;
+ int white = 1;
+
+ /* skip leading whitespace */
+ while (1)
+ {
+ c = getc (fp);
+ if (c == EOF)
+ {
+ *keyp = (char *) NULL;
+ *valp = (char *) NULL;
+ return (-1);
+ }
+ if (!isspace (c))
+ break;
+ }
+
+ /* fill in key */
+ cur = key;
+ max = key + keysize;
+ while (!isspace (c) && c != ';')
+ {
+ if (cur < max)
+ *cur++ = c;
+ else
+ {
+ key = xrealloc (key, keysize + ALLOCINCR);
+ cur = key + keysize;
+ keysize += ALLOCINCR;
+ max = key + keysize;
+ *cur++ = c;
+ }
+ c = getc (fp);
+ if (c == EOF)
+ {
+ *keyp = (char *) NULL;
+ *valp = (char *) NULL;
+ return (-1);
+ }
+ }
+ *cur = '\0';
+
+ /* if we got "desc", we are done with the file */
+ if (strcmp (RCSDESC, key) == 0)
+ {
+ *keyp = (char *) NULL;
+ *valp = (char *) NULL;
+ return (-1);
+ }
+
+ /* if we ended key with a semicolon, there is no value */
+ if (c == ';')
+ {
+ *keyp = key;
+ *valp = (char *) NULL;
+ return (0);
+ }
+
+ /* otherwise, there might be a value, so fill it in */
+ (void) ungetc (c, fp);
+ cur = value;
+ max = value + valsize;
+
+ /* process the value */
+ for (;;)
+ {
+ /* get a character */
+ c = getc (fp);
+ if (c == EOF)
+ {
+ *keyp = (char *) NULL;
+ *valp = (char *) NULL;
+ return (-1);
+ }
+
+ /* if we are in funky mode, do the rest of this string */
+ if (funky)
+ {
+
+ /*
+ * funky mode processing does the following: o @@ means one @ o
+ * all other characters are literal up to a single @ (including
+ * ';')
+ */
+ for (;;)
+ {
+ if (c == '@')
+ {
+ c = getc (fp);
+ if (c == EOF)
+ {
+ *keyp = (char *) NULL;
+ *valp = (char *) NULL;
+ return (-1);
+ }
+ if (c != '@')
+ {
+ /* @ followed by non @ turns off funky mode */
+ funky = 0;
+ break;
+ }
+ /* otherwise, we already ate one @ so copy the other one */
+ }
+
+ /* put the character on the value (maybe allocating space) */
+ if (cur >= max)
+ {
+ value = xrealloc (value, valsize + ALLOCINCR);
+ cur = value + valsize;
+ valsize += ALLOCINCR;
+ max = value + valsize;
+ }
+ *cur++ = c;
+ c = getc (fp);
+ if (c == EOF)
+ {
+ *keyp = (char *) NULL;
+ *valp = (char *) NULL;
+ return (-1);
+ }
+ }
+ }
+
+ /* if we got the semi-colon we are done with the entire value */
+ if (c == ';')
+ break;
+
+ /* process the character we got */
+ if (white && c == '@')
+ {
+
+ /*
+ * if we are starting a word with an '@', enable funky processing
+ */
+ white = 0; /* you can't be funky and white :-) */
+ funky = 1;
+ }
+ else
+ {
+
+ /*
+ * we put the character on the list, compressing all whitespace
+ * to a single space
+ */
+
+ /* whitespace with white set means compress it out */
+ if (white && isspace (c))
+ continue;
+
+ if (isspace (c))
+ {
+ /* make c a space and set white */
+ white = 1;
+ c = ' ';
+ }
+ else
+ white = 0;
+
+ /* put the char on the end of value (maybe allocating space) */
+ if (cur >= max)
+ {
+ value = xrealloc (value, valsize + ALLOCINCR);
+ cur = value + valsize;
+ valsize += ALLOCINCR;
+ max = value + valsize;
+ }
+ *cur++ = c;
+ }
+ }
+
+ /* if the last char was white space, take it off */
+ if (white && cur != value)
+ cur--;
+
+ /* terminate the string */
+ if (cur)
+ *cur = '\0';
+
+ /* if the string is empty, make it null */
+ if (value && *value != '\0')
+ *valp = value;
+ else
+ *valp = NULL;
+ *keyp = key;
+ return (0);
+}
+
+/*
+ * process the symbols list of the rcs file
+ */
+static void
+do_symbols (list, val)
+ List *list;
+ char *val;
+{
+ Node *p;
+ char *cp = val;
+ char *tag, *rev;
+
+ for (;;)
+ {
+ /* skip leading whitespace */
+ while (isspace (*cp))
+ cp++;
+
+ /* if we got to the end, we are done */
+ if (*cp == '\0')
+ break;
+
+ /* split it up into tag and rev */
+ tag = cp;
+ cp = index (cp, ':');
+ *cp++ = '\0';
+ rev = cp;
+ while (!isspace (*cp) && *cp != '\0')
+ cp++;
+ if (*cp != '\0')
+ *cp++ = '\0';
+
+ /* make a new node and add it to the list */
+ p = getnode ();
+ p->key = xstrdup (tag);
+ p->data = xstrdup (rev);
+ (void) addnode (list, p);
+ }
+}
+
+/*
+ * process the branches list of a revision delta
+ */
+static void
+do_branches (list, val)
+ List *list;
+ char *val;
+{
+ Node *p;
+ char *cp = val;
+ char *branch;
+
+ for (;;)
+ {
+ /* skip leading whitespace */
+ while (isspace (*cp))
+ cp++;
+
+ /* if we got to the end, we are done */
+ if (*cp == '\0')
+ break;
+
+ /* find the end of this branch */
+ branch = cp;
+ while (!isspace (*cp) && *cp != '\0')
+ cp++;
+ if (*cp != '\0')
+ *cp++ = '\0';
+
+ /* make a new node and add it to the list */
+ p = getnode ();
+ p->key = xstrdup (branch);
+ (void) addnode (list, p);
+ }
+}
+
+/*
+ * Version Number
+ *
+ * Returns the requested version number of the RCS file, satisfying tags and/or
+ * dates, and walking branches, if necessary.
+ *
+ * The result is returned; null-string if error.
+ */
+char *
+RCS_getversion (rcs, tag, date, force_tag_match)
+ RCSNode *rcs;
+ char *tag;
+ char *date;
+ int force_tag_match;
+{
+ /* make sure we have something to look at... */
+ if (rcs == NULL)
+ return ((char *) NULL);
+
+ if (tag && date)
+ {
+ char *cp, *rev, *tagrev;
+
+ /*
+ * first lookup the tag; if that works, turn the revision into
+ * a branch and lookup the date.
+ */
+ tagrev = RCS_gettag (rcs, tag, force_tag_match);
+ if (tagrev == NULL)
+ return ((char *) NULL);
+
+ if ((cp = rindex (tagrev, '.')) != NULL)
+ *cp = '\0';
+ rev = RCS_getdatebranch (rcs, date, tagrev);
+ free (tagrev);
+ return (rev);
+ }
+ else if (tag)
+ return (RCS_gettag (rcs, tag, force_tag_match));
+ else if (date)
+ return (RCS_getdate (rcs, date, force_tag_match));
+ else
+ return (RCS_head (rcs));
+
+}
+
+/*
+ * Find the revision for a specific tag.
+ * If force_tag_match is set, return NULL if an exact match is not
+ * possible otherwise return RCS_head (). We are careful to look for
+ * and handle "magic" revisions specially.
+ *
+ * If the matched tag is a branch tag, find the head of the branch.
+ */
+char *
+RCS_gettag (rcs, tag, force_tag_match)
+ RCSNode *rcs;
+ char *tag;
+ int force_tag_match;
+{
+ Node *p;
+
+ /* make sure we have something to look at... */
+ if (rcs == NULL)
+ return ((char *) NULL);
+
+ /* If tag is "HEAD", special case to get head RCS revision */
+ if (tag && (strcmp (tag, TAG_HEAD) == 0 || *tag == '\0'))
+ if (force_tag_match && (rcs->flags & VALID) && (rcs->flags & INATTIC))
+ return ((char *) NULL); /* head request for removed file */
+ else
+ return (RCS_head (rcs));
+
+ if (!isdigit (tag[0]))
+ {
+ /* If we got a symbolic tag, resolve it to a numeric */
+ if (rcs == NULL)
+ p = NULL;
+ else
+ p = findnode (rcs->symbols, tag);
+ if (p != NULL)
+ {
+ int dots;
+ char *magic, *branch, *cp;
+
+ tag = p->data;
+
+ /*
+ * If this is a magic revision, we turn it into either its
+ * physical branch equivalent (if one exists) or into
+ * its base revision, which we assume exists.
+ */
+ dots = numdots (tag);
+ if (dots > 2 && (dots & 1) != 0)
+ {
+ branch = rindex (tag, '.');
+ cp = branch++ - 1;
+ while (*cp != '.')
+ cp--;
+
+ /* see if we have .magic-branch. (".0.") */
+ magic = xmalloc (strlen (tag) + 1);
+ (void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH);
+ if (strncmp (magic, cp, strlen (magic)) == 0)
+ {
+ char *xtag;
+
+ /* it's magic. See if the branch exists */
+ *cp = '\0'; /* turn it into a revision */
+ xtag = xstrdup (tag);
+ *cp = '.'; /* and back again */
+ (void) sprintf (magic, "%s.%s", xtag, branch);
+ branch = RCS_getbranch (rcs, magic, 1);
+ free (magic);
+ if (branch != NULL)
+ {
+ free (xtag);
+ return (branch);
+ }
+ return (xtag);
+ }
+ free (magic);
+ }
+ }
+ else
+ {
+ /* The tag wasn't there, so return the head or NULL */
+ if (force_tag_match)
+ return (NULL);
+ else
+ return (RCS_head (rcs));
+ }
+ }
+
+ /*
+ * numeric tag processing:
+ * 1) revision number - just return it
+ * 2) branch number - find head of branch
+ */
+
+ /* strip trailing dots */
+ while (tag[strlen (tag) - 1] == '.')
+ tag[strlen (tag) - 1] = '\0';
+
+ if ((numdots (tag) & 1) == 0)
+ {
+ /* we have a branch tag, so we need to walk the branch */
+ return (RCS_getbranch (rcs, tag, force_tag_match));
+ }
+ else
+ {
+ /* we have a revision tag, so make sure it exists */
+ if (rcs == NULL)
+ p = NULL;
+ else
+ p = findnode (rcs->versions, tag);
+ if (p != NULL)
+ return (xstrdup (tag));
+ else
+ {
+ /* The revision wasn't there, so return the head or NULL */
+ if (force_tag_match)
+ return (NULL);
+ else
+ return (RCS_head (rcs));
+ }
+ }
+}
+
+/*
+ * Return a "magic" revision as a virtual branch off of REV for the RCS file.
+ * A "magic" revision is one which is unique in the RCS file. By unique, I
+ * mean we return a revision which:
+ * - has a branch of 0 (see rcs.h RCS_MAGIC_BRANCH)
+ * - has a revision component which is not an existing branch off REV
+ * - has a revision component which is not an existing magic revision
+ * - is an even-numbered revision, to avoid conflicts with vendor branches
+ * The first point is what makes it "magic".
+ *
+ * As an example, if we pass in 1.37 as REV, we will look for an existing
+ * branch called 1.37.2. If it did not exist, we would look for an
+ * existing symbolic tag with a numeric part equal to 1.37.0.2. If that
+ * didn't exist, then we know that the 1.37.2 branch can be reserved by
+ * creating a symbolic tag with 1.37.0.2 as the numeric part.
+ *
+ * This allows us to fork development with very little overhead -- just a
+ * symbolic tag is used in the RCS file. When a commit is done, a physical
+ * branch is dynamically created to hold the new revision.
+ *
+ * Note: We assume that REV is an RCS revision and not a branch number.
+ */
+static char *check_rev;
+char *
+RCS_magicrev (rcs, rev)
+ RCSNode *rcs;
+ char *rev;
+{
+ int rev_num;
+ char *xrev, *test_branch;
+
+ xrev = xmalloc (strlen (rev) + 14); /* enough for .0.number */
+ check_rev = xrev;
+
+ /* only look at even numbered branches */
+ for (rev_num = 2; ; rev_num += 2)
+ {
+ /* see if the physical branch exists */
+ (void) sprintf (xrev, "%s.%d", rev, rev_num);
+ test_branch = RCS_getbranch (rcs, xrev, 1);
+ if (test_branch != NULL) /* it did, so keep looking */
+ {
+ free (test_branch);
+ continue;
+ }
+
+ /* now, create a "magic" revision */
+ (void) sprintf (xrev, "%s.%d.%d", rev, RCS_MAGIC_BRANCH, rev_num);
+
+ /* walk the symbols list to see if a magic one already exists */
+ if (walklist (rcs->symbols, checkmagic_proc) != 0)
+ continue;
+
+ /* we found a free magic branch. Claim it as ours */
+ return (xrev);
+ }
+}
+
+/*
+ * walklist proc to look for a match in the symbols list.
+ * Returns 0 if the symbol does not match, 1 if it does.
+ */
+static int
+checkmagic_proc (p)
+ Node *p;
+{
+ if (strcmp (check_rev, p->data) == 0)
+ return (1);
+ else
+ return (0);
+}
+
+/*
+ * Returns non-zero if the specified revision number or symbolic tag
+ * resolves to a "branch" within the rcs file. We do take into account
+ * any magic branches as well.
+ */
+int
+RCS_isbranch (file, rev, srcfiles)
+ char *file;
+ char *rev;
+ List *srcfiles;
+{
+ int dots;
+ Node *p;
+ RCSNode *rcs;
+
+ /* numeric revisions are easy -- even number of dots is a branch */
+ if (isdigit (*rev))
+ return ((numdots (rev) & 1) == 0);
+
+ /* assume a revision if you can't find the RCS info */
+ p = findnode (srcfiles, file);
+ if (p == NULL)
+ return (0);
+
+ /* now, look for a match in the symbols list */
+ rcs = (RCSNode *) p->data;
+ p = findnode (rcs->symbols, rev);
+ if (p == NULL)
+ return (0);
+ dots = numdots (p->data);
+ if ((dots & 1) == 0)
+ return (1);
+
+ /* got a symbolic tag match, but it's not a branch; see if it's magic */
+ if (dots > 2)
+ {
+ char *magic;
+ char *branch = rindex (p->data, '.');
+ char *cp = branch - 1;
+ while (*cp != '.')
+ cp--;
+
+ /* see if we have .magic-branch. (".0.") */
+ magic = xmalloc (strlen (p->data) + 1);
+ (void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH);
+ if (strncmp (magic, cp, strlen (magic)) == 0)
+ {
+ free (magic);
+ return (1);
+ }
+ free (magic);
+ }
+ return (0);
+}
+
+/*
+ * Returns a pointer to malloc'ed memory which contains the branch
+ * for the specified *symbolic* tag. Magic branches are handled correctly.
+ */
+char *
+RCS_whatbranch (file, rev, srcfiles)
+ char *file;
+ char *rev;
+ List *srcfiles;
+{
+ int dots;
+ Node *p;
+ RCSNode *rcs;
+
+ /* assume no branch if you can't find the RCS info */
+ p = findnode (srcfiles, file);
+ if (p == NULL)
+ return ((char *) NULL);
+
+ /* now, look for a match in the symbols list */
+ rcs = (RCSNode *) p->data;
+ p = findnode (rcs->symbols, rev);
+ if (p == NULL)
+ return ((char *) NULL);
+ dots = numdots (p->data);
+ if ((dots & 1) == 0)
+ return (xstrdup (p->data));
+
+ /* got a symbolic tag match, but it's not a branch; see if it's magic */
+ if (dots > 2)
+ {
+ char *magic;
+ char *branch = rindex (p->data, '.');
+ char *cp = branch++ - 1;
+ while (*cp != '.')
+ cp--;
+
+ /* see if we have .magic-branch. (".0.") */
+ magic = xmalloc (strlen (p->data) + 1);
+ (void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH);
+ if (strncmp (magic, cp, strlen (magic)) == 0)
+ {
+ /* yep. it's magic. now, construct the real branch */
+ *cp = '\0'; /* turn it into a revision */
+ (void) sprintf (magic, "%s.%s", p->data, branch);
+ *cp = '.'; /* and turn it back */
+ return (magic);
+ }
+ free (magic);
+ }
+ return ((char *) NULL);
+}
+
+/*
+ * Get the head of the specified branch. If the branch does not exist,
+ * return NULL or RCS_head depending on force_tag_match
+ */
+static char *
+RCS_getbranch (rcs, tag, force_tag_match)
+ RCSNode *rcs;
+ char *tag;
+ int force_tag_match;
+{
+ Node *p, *head;
+ RCSVers *vn;
+ char *xtag;
+ char *nextvers;
+ char *cp;
+
+ /* make sure we have something to look at... */
+ if (rcs == NULL)
+ return ((char *) NULL);
+
+ /* find out if the tag contains a dot, or is on the trunk */
+ cp = rindex (tag, '.');
+
+ /* trunk processing is the special case */
+ if (cp == NULL)
+ {
+ xtag = xmalloc (strlen (tag) + 1 + 1); /* +1 for an extra . */
+ (void) strcpy (xtag, tag);
+ (void) strcat (xtag, ".");
+ for (cp = rcs->head; cp != NULL;)
+ {
+ if (strncmp (xtag, cp, strlen (xtag)) == 0)
+ break;
+ p = findnode (rcs->versions, cp);
+ if (p == NULL)
+ {
+ free (xtag);
+ if (force_tag_match)
+ return (NULL);
+ else
+ return (RCS_head (rcs));
+ }
+ vn = (RCSVers *) p->data;
+ cp = vn->next;
+ }
+ free (xtag);
+ if (cp == NULL)
+ {
+ if (force_tag_match)
+ return (NULL);
+ else
+ return (RCS_head (rcs));
+ }
+ return (xstrdup (cp));
+ }
+
+ /* if it had a `.', terminate the string so we have the base revision */
+ *cp = '\0';
+
+ /* look up the revision this branch is based on */
+ p = findnode (rcs->versions, tag);
+
+ /* put the . back so we have the branch again */
+ *cp = '.';
+
+ if (p == NULL)
+ {
+ /* if the base revision didn't exist, return head or NULL */
+ if (force_tag_match)
+ return (NULL);
+ else
+ return (RCS_head (rcs));
+ }
+
+ /* find the first element of the branch we are looking for */
+ vn = (RCSVers *) p->data;
+ if (vn->branches == NULL)
+ return (NULL);
+ xtag = xmalloc (strlen (tag) + 1 + 1); /* 1 for the extra '.' */
+ (void) strcpy (xtag, tag);
+ (void) strcat (xtag, ".");
+ head = vn->branches->list;
+ for (p = head->next; p != head; p = p->next)
+ if (strncmp (p->key, xtag, strlen (xtag)) == 0)
+ break;
+ free (xtag);
+
+ if (p == head)
+ {
+ /* we didn't find a match so return head or NULL */
+ if (force_tag_match)
+ return (NULL);
+ else
+ return (RCS_head (rcs));
+ }
+
+ /* now walk the next pointers of the branch */
+ nextvers = p->key;
+ do
+ {
+ p = findnode (rcs->versions, nextvers);
+ if (p == NULL)
+ {
+ /* a link in the chain is missing - return head or NULL */
+ if (force_tag_match)
+ return (NULL);
+ else
+ return (RCS_head (rcs));
+ }
+ vn = (RCSVers *) p->data;
+ nextvers = vn->next;
+ } while (nextvers != NULL);
+
+ /* we have the version in our hand, so go for it */
+ return (xstrdup (vn->version));
+}
+
+/*
+ * Get the head of the RCS file. If branch is set, this is the head of the
+ * branch, otherwise the real head
+ */
+char *
+RCS_head (rcs)
+ RCSNode *rcs;
+{
+ /* make sure we have something to look at... */
+ if (rcs == NULL)
+ return ((char *) NULL);
+
+ if (rcs->branch)
+ return (RCS_getbranch (rcs, rcs->branch, 1));
+
+ /*
+ * NOTE: we call getbranch with force_tag_match set to avoid any
+ * possibility of recursion
+ */
+ else
+ return (xstrdup (rcs->head));
+}
+
+/*
+ * Get the most recent revision, based on the supplied date, but use some
+ * funky stuff and follow the vendor branch maybe
+ */
+char *
+RCS_getdate (rcs, date, force_tag_match)
+ RCSNode *rcs;
+ char *date;
+ int force_tag_match;
+{
+ char *cur_rev = NULL;
+ char *retval = NULL;
+ Node *p;
+ RCSVers *vers = NULL;
+
+ /* make sure we have something to look at... */
+ if (rcs == NULL)
+ return ((char *) NULL);
+
+ /* if the head is on a branch, try the branch first */
+ if (rcs->branch != NULL)
+ retval = RCS_getdatebranch (rcs, date, rcs->branch);
+
+ /* if we found a match, we are done */
+ if (retval != NULL)
+ return (retval);
+
+ /* otherwise if we have a trunk, try it */
+ if (rcs->head)
+ {
+ p = findnode (rcs->versions, rcs->head);
+ while (p != NULL)
+ {
+ /* if the date of this one is before date, take it */
+ vers = (RCSVers *) p->data;
+ if (RCS_datecmp (vers->date, date) <= 0)
+ {
+ cur_rev = vers->version;
+ break;
+ }
+
+ /* if there is a next version, find the node */
+ if (vers->next != NULL)
+ p = findnode (rcs->versions, vers->next);
+ else
+ p = (Node *) NULL;
+ }
+ }
+
+ /*
+ * at this point, either we have the revision we want, or we have the
+ * first revision on the trunk (1.1?) in our hands
+ */
+
+ /* if we found what we're looking for, and it's not 1.1 return it */
+ if (cur_rev != NULL && strcmp (cur_rev, "1.1") != 0)
+ return (xstrdup (cur_rev));
+
+ /* look on the vendor branch */
+ retval = RCS_getdatebranch (rcs, date, CVSBRANCH);
+
+ /*
+ * if we found a match, return it; otherwise, we return the first
+ * revision on the trunk or NULL depending on force_tag_match and the
+ * date of the first rev
+ */
+ if (retval != NULL)
+ return (retval);
+
+ if (!force_tag_match || RCS_datecmp (vers->date, date) <= 0)
+ return (xstrdup (vers->version));
+ else
+ return (NULL);
+}
+
+/*
+ * Look up the last element on a branch that was put in before the specified
+ * date (return the rev or NULL)
+ */
+static char *
+RCS_getdatebranch (rcs, date, branch)
+ RCSNode *rcs;
+ char *date;
+ char *branch;
+{
+ char *cur_rev = NULL;
+ char *cp;
+ char *xbranch, *xrev;
+ Node *p;
+ RCSVers *vers;
+
+ /* look up the first revision on the branch */
+ xrev = xstrdup (branch);
+ cp = rindex (xrev, '.');
+ if (cp == NULL)
+ {
+ free (xrev);
+ return (NULL);
+ }
+ *cp = '\0'; /* turn it into a revision */
+ p = findnode (rcs->versions, xrev);
+ free (xrev);
+ if (p == NULL)
+ return (NULL);
+ vers = (RCSVers *) p->data;
+
+ /* if no branches list, return NULL */
+ if (vers->branches == NULL)
+ return (NULL);
+
+ /* walk the branches list looking for the branch number */
+ xbranch = xmalloc (strlen (branch) + 1 + 1); /* +1 for the extra dot */
+ (void) strcpy (xbranch, branch);
+ (void) strcat (xbranch, ".");
+ for (p = vers->branches->list->next; p != vers->branches->list; p = p->next)
+ if (strncmp (p->key, xbranch, strlen (xbranch)) == 0)
+ break;
+ free (xbranch);
+ if (p == vers->branches->list)
+ return (NULL);
+
+ p = findnode (rcs->versions, p->key);
+
+ /* walk the next pointers until you find the end, or the date is too late */
+ while (p != NULL)
+ {
+ vers = (RCSVers *) p->data;
+ if (RCS_datecmp (vers->date, date) <= 0)
+ cur_rev = vers->version;
+ else
+ break;
+
+ /* if there is a next version, find the node */
+ if (vers->next != NULL)
+ p = findnode (rcs->versions, vers->next);
+ else
+ p = (Node *) NULL;
+ }
+
+ /* if we found something acceptable, return it - otherwise NULL */
+ if (cur_rev != NULL)
+ return (xstrdup (cur_rev));
+ else
+ return (NULL);
+}
+
+/*
+ * Compare two dates in RCS format. Beware the change in format on January 1,
+ * 2000, when years go from 2-digit to full format.
+ */
+int
+RCS_datecmp (date1, date2)
+ char *date1, *date2;
+{
+ int length_diff = strlen (date1) - strlen (date2);
+
+ return (length_diff ? length_diff : strcmp (date1, date2));
+}
+
+/*
+ * Lookup the specified revision in the ,v file and return, in the date
+ * argument, the date specified for the revision *minus one second*, so that
+ * the logically previous revision will be found later.
+ *
+ * Returns zero on failure, RCS revision time as a Unix "time_t" on success.
+ */
+time_t
+RCS_getrevtime (rcs, rev, date, fudge)
+ RCSNode *rcs;
+ char *rev;
+ char *date;
+ int fudge;
+{
+ char tdate[MAXDATELEN];
+ struct tm xtm, *ftm;
+ time_t revdate = 0;
+ Node *p;
+ RCSVers *vers;
+
+ /* make sure we have something to look at... */
+ if (rcs == NULL)
+ return (revdate);
+
+ /* look up the revision */
+ p = findnode (rcs->versions, rev);
+ if (p == NULL)
+ return (-1);
+ vers = (RCSVers *) p->data;
+
+ /* split up the date */
+ ftm = &xtm;
+ (void) sscanf (vers->date, SDATEFORM, &ftm->tm_year, &ftm->tm_mon,
+ &ftm->tm_mday, &ftm->tm_hour, &ftm->tm_min,
+ &ftm->tm_sec);
+ if (ftm->tm_year > 1900)
+ ftm->tm_year -= 1900;
+
+ /* put the date in a form getdate can grok */
+#ifdef HAVE_RCS5
+ (void) sprintf (tdate, "%d/%d/%d GMT %d:%d:%d", ftm->tm_mon,
+ ftm->tm_mday, ftm->tm_year, ftm->tm_hour,
+ ftm->tm_min, ftm->tm_sec);
+#else
+ (void) sprintf (tdate, "%d/%d/%d %d:%d:%d", ftm->tm_mon,
+ ftm->tm_mday, ftm->tm_year, ftm->tm_hour,
+ ftm->tm_min, ftm->tm_sec);
+#endif
+
+ /* turn it into seconds since the epoch */
+ revdate = get_date (tdate, (struct timeb *) NULL);
+ if (revdate != (time_t) -1)
+ {
+ revdate -= fudge; /* remove "fudge" seconds */
+ if (date)
+ {
+ /* put an appropriate string into ``date'' if we were given one */
+#ifdef HAVE_RCS5
+ ftm = gmtime (&revdate);
+#else
+ ftm = localtime (&revdate);
+#endif
+ (void) sprintf (date, DATEFORM,
+ ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
+ ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
+ ftm->tm_min, ftm->tm_sec);
+ }
+ }
+ return (revdate);
+}
+
+/*
+ * The argument ARG is the getopt remainder of the -k option specified on the
+ * command line. This function returns malloc'ed space that can be used
+ * directly in calls to RCS V5, with the -k flag munged correctly.
+ */
+char *
+RCS_check_kflag (arg)
+ char *arg;
+{
+ static char *kflags[] =
+ {"kv", "kvl", "k", "v", "o", (char *) NULL};
+ char karg[10];
+ char **cpp = NULL;
+
+#ifndef HAVE_RCS5
+ error (1, 0, "%s %s: your version of RCS does not support the -k option",
+ program_name, command_name);
+#endif
+
+ if (arg)
+ {
+ for (cpp = kflags; *cpp != NULL; cpp++)
+ {
+ if (strcmp (arg, *cpp) == 0)
+ break;
+ }
+ }
+
+ if (arg == NULL || *cpp == NULL)
+ {
+ (void) fprintf (stderr, "%s %s: invalid -k option\n",
+ program_name, command_name);
+ (void) fprintf (stderr, "\tvalid options are:\n");
+ for (cpp = kflags; *cpp != NULL; cpp++)
+ (void) fprintf (stderr, "\t\t-k%s\n", *cpp);
+ error (1, 0, "Please retry with a valid -k option");
+ }
+
+ (void) sprintf (karg, "-k%s", *cpp);
+ return (xstrdup (karg));
+}
+
+/*
+ * Do some consistency checks on the symbolic tag... These should equate
+ * pretty close to what RCS checks, though I don't know for certain.
+ */
+void
+RCS_check_tag (tag)
+ char *tag;
+{
+ char *invalid = "$,.:;@"; /* invalid RCS tag characters */
+ char *cp;
+
+ /*
+ * The first character must be an alphabetic letter. The remaining
+ * characters cannot be non-visible graphic characters, and must not be
+ * in the set of "invalid" RCS identifier characters.
+ */
+ if (isalpha (*tag))
+ {
+ for (cp = tag; *cp; cp++)
+ {
+ if (!isgraph (*cp))
+ error (1, 0, "tag `%s' has non-visible graphic characters",
+ tag);
+ if (index (invalid, *cp))
+ error (1, 0, "tag `%s' must not contain the characters `%s'",
+ tag, invalid);
+ }
+ }
+ else
+ error (1, 0, "tag `%s' must start with a letter", tag);
+}
diff --git a/gnu/usr.bin/cvs/cvs/rcs.h b/gnu/usr.bin/cvs/cvs/rcs.h
new file mode 100644
index 0000000..ba5bdae
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/rcs.h
@@ -0,0 +1,102 @@
+/* @(#)rcs.h 1.14 92/03/31 */
+
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * RCS source control definitions needed by rcs.c and friends
+ */
+
+#define RCS "rcs"
+#define RCS_CI "ci"
+#define RCS_CO "co"
+#define RCS_RLOG "rlog"
+#define RCS_DIFF "rcsdiff"
+#define RCS_MERGE "merge"
+#define RCS_RCSMERGE "rcsmerge"
+#define RCS_MERGE_PAT "^>>>>>>> " /* runs "grep" with this pattern */
+#define RCSEXT ",v"
+#define RCSHEAD "head"
+#define RCSBRANCH "branch"
+#define RCSSYMBOLS "symbols"
+#define RCSDATE "date"
+#define RCSDESC "desc"
+#define DATEFORM "%02d.%02d.%02d.%02d.%02d.%02d"
+#define SDATEFORM "%d.%d.%d.%d.%d.%d"
+
+/*
+ * Opaque structure definitions used by RCS specific lookup routines
+ */
+#define VALID 0x1 /* flags field contains valid data */
+#define INATTIC 0x2 /* RCS file is located in the Attic */
+struct rcsnode
+{
+ int refcount;
+ int flags;
+ char *path;
+ char *head;
+ char *branch;
+ List *symbols;
+ List *versions;
+ List *dates;
+};
+typedef struct rcsnode RCSNode;
+
+struct rcsversnode
+{
+ char *version;
+ char *date;
+ char *next;
+ List *branches;
+};
+typedef struct rcsversnode RCSVers;
+
+/*
+ * CVS reserves all even-numbered branches for its own use. "magic" branches
+ * (see rcs.c) are contained as virtual revision numbers (within symbolic
+ * tags only) off the RCS_MAGIC_BRANCH, which is 0. CVS also reserves the
+ * ".1" branch for vendor revisions. So, if you do your own branching, you
+ * should limit your use to odd branch numbers starting at 3.
+ */
+#define RCS_MAGIC_BRANCH 0
+
+/*
+ * exported interfaces
+ */
+#if __STDC__
+List *RCS_parsefiles (List * files, char *xrepos);
+RCSNode *RCS_parse (char *file, char *repos);
+RCSNode *RCS_parsercsfile (char *rcsfile);
+char *RCS_check_kflag (char *arg);
+char *RCS_getdate (RCSNode * rcs, char *date, int force_tag_match);
+char *RCS_gettag (RCSNode * rcs, char *tag, int force_tag_match);
+char *RCS_getversion (RCSNode * rcs, char *tag, char *date,
+ int force_tag_match);
+char *RCS_magicrev (RCSNode *rcs, char *rev);
+int RCS_isbranch (char *file, char *rev, List *srcfiles);
+char *RCS_whatbranch (char *file, char *tag, List *srcfiles);
+char *RCS_head (RCSNode * rcs);
+int RCS_datecmp (char *date1, char *date2);
+time_t RCS_getrevtime (RCSNode * rcs, char *rev, char *date, int fudge);
+void RCS_check_tag (char *tag);
+void freercsnode (RCSNode ** rnodep);
+#else
+List *RCS_parsefiles ();
+RCSNode *RCS_parse ();
+char *RCS_head ();
+char *RCS_getversion ();
+char *RCS_magicrev ();
+int RCS_isbranch ();
+char *RCS_whatbranch ();
+char *RCS_gettag ();
+char *RCS_getdate ();
+char *RCS_check_kflag ();
+void RCS_check_tag ();
+time_t RCS_getrevtime ();
+RCSNode *RCS_parsercsfile ();
+int RCS_datecmp ();
+void freercsnode ();
+#endif /* __STDC__ */
diff --git a/gnu/usr.bin/cvs/cvs/recurse.c b/gnu/usr.bin/cvs/cvs/recurse.c
new file mode 100644
index 0000000..82301d5
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/recurse.c
@@ -0,0 +1,535 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * General recursion handler
+ *
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)recurse.c 1.22 92/04/10";
+#endif
+
+#if __STDC__
+static int do_dir_proc (Node * p);
+static int do_file_proc (Node * p);
+static void addlist (List ** listp, char *key);
+#else
+static int do_file_proc ();
+static int do_dir_proc ();
+static void addlist ();
+#endif /* __STDC__ */
+
+
+/*
+ * Local static versions eliminates the need for globals
+ */
+static int (*fileproc) ();
+static int (*filesdoneproc) ();
+static Dtype (*direntproc) ();
+static int (*dirleaveproc) ();
+static int which;
+static Dtype flags;
+static int aflag;
+static int readlock;
+static int dosrcs;
+static char update_dir[PATH_MAX];
+static char *repository = NULL;
+static List *entries = NULL;
+static List *srcfiles = NULL;
+static List *filelist = NULL;
+static List *dirlist = NULL;
+
+/*
+ * Called to start a recursive command Command line arguments are processed
+ * if present, otherwise the local directory is processed.
+ */
+int
+start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc,
+ argc, argv, local, which, aflag, readlock,
+ update_preload, dosrcs)
+ int (*fileproc) ();
+ int (*filesdoneproc) ();
+ Dtype (*direntproc) ();
+ int (*dirleaveproc) ();
+ int argc;
+ char *argv[];
+ int local;
+ int which;
+ int aflag;
+ int readlock;
+ char *update_preload;
+ int dosrcs;
+{
+ int i, err = 0;
+ Dtype flags;
+
+ if (update_preload == NULL)
+ update_dir[0] = '\0';
+ else
+ (void) strcpy (update_dir, update_preload);
+
+ if (local)
+ flags = R_SKIP_DIRS;
+ else
+ flags = R_PROCESS;
+
+ /* clean up from any previous calls to start_recursion */
+ if (repository)
+ {
+ free (repository);
+ repository = (char *) NULL;
+ }
+ if (entries)
+ dellist (&entries);
+ if (srcfiles)
+ dellist (&srcfiles);
+ if (filelist)
+ dellist (&filelist);
+ if (dirlist)
+ dellist (&dirlist);
+
+ if (argc == 0)
+ {
+
+ /*
+ * There were no arguments, so we'll probably just recurse. The
+ * exception to the rule is when we are called from a directory
+ * without any CVS administration files. That has always meant to
+ * process each of the sub-directories, so we pretend like we were
+ * called with the list of sub-dirs of the current dir as args
+ */
+ if ((which & W_LOCAL) && !isdir (CVSADM) && !isdir (OCVSADM))
+ dirlist = Find_Dirs ((char *) NULL, W_LOCAL);
+ else
+ addlist (&dirlist, ".");
+
+ err += do_recursion (fileproc, filesdoneproc, direntproc,
+ dirleaveproc, flags, which, aflag,
+ readlock, dosrcs);
+ }
+ else
+ {
+
+ /*
+ * There were arguments, so we have to handle them by hand. To do
+ * that, we set up the filelist and dirlist with the arguments and
+ * call do_recursion. do_recursion recognizes the fact that the
+ * lists are non-null when it starts and doesn't update them
+ */
+
+ /* look for args with /-s in them */
+ for (i = 0; i < argc; i++)
+ if (index (argv[i], '/') != NULL)
+ break;
+
+ /* if we didn't find any hard one's, do it the easy way */
+ if (i == argc)
+ {
+ /* set up the lists */
+ for (i = 0; i < argc; i++)
+ {
+ if (isdir (argv[i]))
+ addlist (&dirlist, argv[i]);
+ else
+ {
+ if (isdir (CVSADM) || isdir (OCVSADM))
+ {
+ char *repos;
+ char tmp[PATH_MAX];
+
+ repos = Name_Repository ((char *) NULL, update_dir);
+ (void) sprintf (tmp, "%s/%s", repos, argv[i]);
+ if (isdir (tmp))
+ addlist (&dirlist, argv[i]);
+ else
+ addlist (&filelist, argv[i]);
+ free (repos);
+ }
+ else
+ addlist (&filelist, argv[i]);
+ }
+ }
+
+ /* we aren't recursive if no directories were specified */
+ if (dirlist == NULL)
+ local = 1;
+
+ /* process the lists */
+ err += do_recursion (fileproc, filesdoneproc, direntproc,
+ dirleaveproc, flags, which, aflag,
+ readlock, dosrcs);
+ }
+ /* otherwise - do it the hard way */
+ else
+ {
+ char *cp;
+ char *dir = (char *) NULL;
+ char *comp = (char *) NULL;
+ char *oldupdate = (char *) NULL;
+ char savewd[PATH_MAX];
+
+ if (getwd (savewd) == NULL)
+ error (1, 0, "could not get working directory: %s", savewd);
+
+ for (i = 0; i < argc; i++)
+ {
+ /* split the arg into the dir and component parts */
+ dir = xstrdup (argv[i]);
+ if ((cp = rindex (dir, '/')) != NULL)
+ {
+ *cp = '\0';
+ comp = xstrdup (cp + 1);
+ oldupdate = xstrdup (update_dir);
+ if (update_dir[0] != '\0')
+ (void) strcat (update_dir, "/");
+ (void) strcat (update_dir, dir);
+ }
+ else
+ {
+ comp = xstrdup (dir);
+ if (dir)
+ free (dir);
+ dir = (char *) NULL;
+ }
+
+ /* chdir to the appropriate place if necessary */
+ if (dir && chdir (dir) < 0)
+ error (1, errno, "could not chdir to %s", dir);
+
+ /* set up the list */
+ if (isdir (comp))
+ addlist (&dirlist, comp);
+ else
+ {
+ if (isdir (CVSADM) || isdir (OCVSADM))
+ {
+ char *repos;
+ char tmp[PATH_MAX];
+
+ repos = Name_Repository ((char *) NULL, update_dir);
+ (void) sprintf (tmp, "%s/%s", repos, comp);
+ if (isdir (tmp))
+ addlist (&dirlist, comp);
+ else
+ addlist (&filelist, comp);
+ free (repos);
+ }
+ else
+ addlist (&filelist, comp);
+ }
+
+ /* do the recursion */
+ err += do_recursion (fileproc, filesdoneproc, direntproc,
+ dirleaveproc, flags, which,
+ aflag, readlock, dosrcs);
+
+ /* chdir back and fix update_dir if necessary */
+ if (dir && chdir (savewd) < 0)
+ error (1, errno, "could not chdir to %s", dir);
+ if (oldupdate)
+ {
+ (void) strcpy (update_dir, oldupdate);
+ free (oldupdate);
+ }
+
+ }
+ if (dir)
+ free (dir);
+ if (comp)
+ free (comp);
+ }
+ }
+ return (err);
+}
+
+/*
+ * Implement the recursive policies on the local directory. This may be
+ * called directly, or may be called by start_recursion
+ */
+int
+do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc,
+ xflags, xwhich, xaflag, xreadlock, xdosrcs)
+ int (*xfileproc) ();
+ int (*xfilesdoneproc) ();
+ Dtype (*xdirentproc) ();
+ int (*xdirleaveproc) ();
+ Dtype xflags;
+ int xwhich;
+ int xaflag;
+ int xreadlock;
+ int xdosrcs;
+{
+ int err = 0;
+ int dodoneproc = 1;
+ char *srepository;
+
+ /* do nothing if told */
+ if (xflags == R_SKIP_ALL)
+ return (0);
+
+ /* set up the static vars */
+ fileproc = xfileproc;
+ filesdoneproc = xfilesdoneproc;
+ direntproc = xdirentproc;
+ dirleaveproc = xdirleaveproc;
+ flags = xflags;
+ which = xwhich;
+ aflag = xaflag;
+ readlock = noexec ? 0 : xreadlock;
+ dosrcs = xdosrcs;
+
+ /*
+ * Fill in repository with the current repository
+ */
+ if (which & W_LOCAL)
+ {
+ if (isdir (CVSADM) || isdir (OCVSADM))
+ repository = Name_Repository ((char *) NULL, update_dir);
+ else
+ repository = NULL;
+ }
+ else
+ {
+ repository = xmalloc (PATH_MAX);
+ (void) getwd (repository);
+ }
+ srepository = repository; /* remember what to free */
+
+ /*
+ * The filesdoneproc needs to be called for each directory where files
+ * processed, or each directory that is processed by a call where no
+ * directories were passed in. In fact, the only time we don't want to
+ * call back the filesdoneproc is when we are processing directories that
+ * were passed in on the command line (or in the special case of `.' when
+ * we were called with no args
+ */
+ if (dirlist != NULL && filelist == NULL)
+ dodoneproc = 0;
+
+ /*
+ * If filelist or dirlist is already set, we don't look again. Otherwise,
+ * find the files and directories
+ */
+ if (filelist == NULL && dirlist == NULL)
+ {
+ /* both lists were NULL, so start from scratch */
+ if (fileproc != NULL && flags != R_SKIP_FILES)
+ {
+ int lwhich = which;
+
+ /* be sure to look in the attic if we have sticky tags/date */
+ if ((lwhich & W_ATTIC) == 0)
+ if (isreadable (CVSADM_TAG))
+ lwhich |= W_ATTIC;
+
+ /* find the files and fill in entries if appropriate */
+ filelist = Find_Names (repository, lwhich, aflag, &entries);
+ }
+
+ /* find sub-directories if we will recurse */
+ if (flags != R_SKIP_DIRS)
+ dirlist = Find_Dirs (repository, which);
+ }
+ else
+ {
+ /* something was passed on the command line */
+ if (filelist != NULL && fileproc != NULL)
+ {
+ /* we will process files, so pre-parse entries */
+ if (which & W_LOCAL)
+ entries = ParseEntries (aflag);
+ }
+ }
+
+ /* process the files (if any) */
+ if (filelist != NULL)
+ {
+ /* read lock it if necessary */
+ if (readlock && repository && Reader_Lock (repository) != 0)
+ error (1, 0, "read lock failed - giving up");
+
+ /* pre-parse the source files */
+ if (dosrcs && repository)
+ srcfiles = RCS_parsefiles (filelist, repository);
+ else
+ srcfiles = (List *) NULL;
+
+ /* process the files */
+ err += walklist (filelist, do_file_proc);
+
+ /* unlock it */
+ if (readlock)
+ Lock_Cleanup ();
+
+ /* clean up */
+ dellist (&filelist);
+ dellist (&srcfiles);
+ dellist (&entries);
+ }
+
+ /* call-back files done proc (if any) */
+ if (dodoneproc && filesdoneproc != NULL)
+ err = filesdoneproc (err, repository, update_dir[0] ? update_dir : ".");
+
+ /* process the directories (if necessary) */
+ if (dirlist != NULL)
+ err += walklist (dirlist, do_dir_proc);
+#ifdef notdef
+ else if (dirleaveproc != NULL)
+ err += dirleaveproc(".", err, ".");
+#endif
+ dellist (&dirlist);
+
+ /* free the saved copy of the pointer if necessary */
+ if (srepository)
+ {
+ (void) free (srepository);
+ repository = (char *) NULL;
+ }
+
+ return (err);
+}
+
+/*
+ * Process each of the files in the list with the callback proc
+ */
+static int
+do_file_proc (p)
+ Node *p;
+{
+ if (fileproc != NULL)
+ return (fileproc (p->key, update_dir, repository, entries, srcfiles));
+ else
+ return (0);
+}
+
+/*
+ * Process each of the directories in the list (recursing as we go)
+ */
+static int
+do_dir_proc (p)
+ Node *p;
+{
+ char *dir = p->key;
+ char savewd[PATH_MAX];
+ char newrepos[PATH_MAX];
+ List *sdirlist;
+ char *srepository;
+ char *cp;
+ Dtype dir_return = R_PROCESS;
+ int stripped_dot = 0;
+ int err = 0;
+
+ /* set up update_dir - skip dots if not at start */
+ if (strcmp (dir, ".") != 0)
+ {
+ if (update_dir[0] != '\0')
+ {
+ (void) strcat (update_dir, "/");
+ (void) strcat (update_dir, dir);
+ }
+ else
+ (void) strcpy (update_dir, dir);
+
+ /*
+ * Here we need a plausible repository name for the sub-directory. We
+ * create one by concatenating the new directory name onto the
+ * previous repository name. The only case where the name should be
+ * used is in the case where we are creating a new sub-directory for
+ * update -d and in that case the generated name will be correct.
+ */
+ if (repository == NULL)
+ newrepos[0] = '\0';
+ else
+ (void) sprintf (newrepos, "%s/%s", repository, dir);
+ }
+ else
+ {
+ if (update_dir[0] == '\0')
+ (void) strcpy (update_dir, dir);
+
+ if (repository == NULL)
+ newrepos[0] = '\0';
+ else
+ (void) strcpy (newrepos, repository);
+ }
+
+ /* call-back dir entry proc (if any) */
+ if (direntproc != NULL)
+ dir_return = direntproc (dir, newrepos, update_dir);
+
+ /* only process the dir if the return code was 0 */
+ if (dir_return != R_SKIP_ALL)
+ {
+ /* save our current directory and static vars */
+ if (getwd (savewd) == NULL)
+ error (1, 0, "could not get working directory: %s", savewd);
+ sdirlist = dirlist;
+ srepository = repository;
+ dirlist = NULL;
+
+ /* cd to the sub-directory */
+ if (chdir (dir) < 0)
+ error (1, errno, "could not chdir to %s", dir);
+
+ /* honor the global SKIP_DIRS (a.k.a. local) */
+ if (flags == R_SKIP_DIRS)
+ dir_return = R_SKIP_DIRS;
+
+ /* remember if the `.' will be stripped for subsequent dirs */
+ if (strcmp (update_dir, ".") == 0)
+ {
+ update_dir[0] = '\0';
+ stripped_dot = 1;
+ }
+
+ /* make the recursive call */
+ err += do_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc,
+ dir_return, which, aflag, readlock, dosrcs);
+
+ /* put the `.' back if necessary */
+ if (stripped_dot)
+ (void) strcpy (update_dir, ".");
+
+ /* call-back dir leave proc (if any) */
+ if (dirleaveproc != NULL)
+ err = dirleaveproc (dir, err, update_dir);
+
+ /* get back to where we started and restore state vars */
+ if (chdir (savewd) < 0)
+ error (1, errno, "could not chdir to %s", savewd);
+ dirlist = sdirlist;
+ repository = srepository;
+ }
+
+ /* put back update_dir */
+ if ((cp = rindex (update_dir, '/')) != NULL)
+ *cp = '\0';
+ else
+ update_dir[0] = '\0';
+
+ return (err);
+}
+
+/*
+ * Add a node to a list allocating the list if necessary
+ */
+static void
+addlist (listp, key)
+ List **listp;
+ char *key;
+{
+ Node *p;
+
+ if (*listp == NULL)
+ *listp = getlist ();
+ p = getnode ();
+ p->type = FILES;
+ p->key = xstrdup (key);
+ (void) addnode (*listp, p);
+}
diff --git a/gnu/usr.bin/cvs/cvs/release.c b/gnu/usr.bin/cvs/cvs/release.c
new file mode 100644
index 0000000..e2a941a
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/release.c
@@ -0,0 +1,223 @@
+/*
+ * Release: "cancel" a checkout in the history log.
+ *
+ * - Don't allow release if anything is active - Don't allow release if not
+ * above or inside repository. - Don't allow release if ./CVS/Repository is
+ * not the same as the directory specified in the module database.
+ *
+ * - Enter a line in the history log indicating the "release". - If asked to,
+ * delete the local working directory.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)release.c 1.21 92/02/29";
+#endif
+
+#if __STDC__
+static void release_delete (char *dir);
+#else
+static void release_delete ();
+#endif /* __STDC__ */
+
+static char *release_usage[] =
+{
+ "Usage: %s %s [-d] modules...\n",
+ "\t-Q\tReally quiet.\n",
+ "\t-d\tDelete the given directory.\n",
+ "\t-q\tSomewhat quiet.\n",
+ NULL
+};
+
+static short delete;
+
+int
+release (argc, argv)
+ int argc;
+ char **argv;
+{
+ FILE *fp;
+ register int i, c;
+ register char *cp;
+ int margc;
+ DBM *db;
+ datum key, val;
+ char *repository, *srepos;
+ char **margv, *modargv[MAXFILEPERDIR], line[PATH_MAX];
+
+ if (argc == -1)
+ usage (release_usage);
+ optind = 1;
+ while ((c = gnu_getopt (argc, argv, "Qdq")) != -1)
+ {
+ switch (c)
+ {
+ case 'Q':
+ really_quiet = 1;
+ /* FALL THROUGH */
+ case 'q':
+ quiet = 1;
+ break;
+ case 'd':
+ delete++;
+ break;
+ case '?':
+ default:
+ usage (release_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!(db = open_module ()))
+ return (1);
+ for (i = 0; i < argc; i++)
+ {
+
+ /*
+ * If we are in a repository, do it. Else if we are in the parent of
+ * a directory with the same name as the module, "cd" into it and
+ * look for a repository there.
+ */
+ if (isdir (argv[i]))
+ {
+ if (chdir (argv[i]) < 0)
+ {
+ if (!really_quiet)
+ error (0, 0, "can't chdir to: %s", argv[i]);
+ continue;
+ }
+ if (!isdir (CVSADM) && !isdir (OCVSADM))
+ {
+ if (!really_quiet)
+ error (0, 0, "no repository module: %s", argv[i]);
+ continue;
+ }
+ }
+ else
+ {
+ if (!really_quiet)
+ error (0, 0, "no such directory/module: %s", argv[i]);
+ continue;
+ }
+
+ repository = Name_Repository ((char *) NULL, (char *) NULL);
+ srepos = Short_Repository (repository);
+
+ /* grab module entry from database and check against short repos */
+ key.dptr = argv[i];
+ key.dsize = strlen (key.dptr);
+ val = dbm_fetch (db, key);
+ if (!val.dptr)
+ {
+ error (0, 0, "no such module name: %s", argv[i]);
+ continue;
+ }
+ val.dptr[val.dsize] = '\0';
+ if ((cp = index (val.dptr, '#')) != NULL) /* Strip out a comment */
+ {
+ do
+ {
+ *cp-- = '\0';
+ } while (isspace (*cp));
+ }
+ (void) sprintf (line, "%s %s", key.dptr, val.dptr);
+ line2argv (&margc, modargv, line);
+ margv = modargv;
+
+ optind = 1;
+ while (gnu_getopt (margc, margv, CVSMODULE_OPTS) != -1)
+ /* do nothing */ ;
+ margc -= optind;
+ margv += optind;
+
+ if (margc < 1)
+ {
+ error (0, 0, "modules file missing directory for key %s value %s",
+ key.dptr, val.dptr);
+ continue;
+ }
+ if (strcmp (*margv, srepos))
+ {
+ error (0, 0, "repository mismatch: module[%s], here[%s]",
+ *margv, srepos);
+ free (repository);
+ continue;
+ }
+
+ if (!really_quiet)
+ {
+
+ /*
+ * Now see if there is any reason not to allow a "Release" This
+ * is "popen()" instead of "Popen()" since we don't want "-n" to
+ * stop it.
+ */
+#ifdef FREEBSD_DEVELOPER
+ fp = popen ("ncvs -n -q update", "r");
+#else
+ fp = popen ("cvs -n -q update", "r");
+#endif /* FREEBSD_DEVELOPER */
+ c = 0;
+ while (fgets (line, sizeof (line), fp))
+ {
+ if (index ("MARCZ", *line))
+ c++;
+ (void) printf (line);
+ }
+ (void) pclose (fp);
+ (void) printf ("You have [%d] altered files in this repository.\n",
+ c);
+ (void) printf ("Are you sure you want to release %smodule `%s': ",
+ delete ? "(and delete) " : "", argv[i]);
+ c = !yesno ();
+ if (c) /* "No" */
+ {
+ (void) fprintf (stderr, "** `%s' aborted by user choice.\n",
+ command_name);
+ free (repository);
+ continue;
+ }
+ }
+
+ /*
+ * So, we've passed all the tests, go ahead and release it. First,
+ * log the release, then attempt to delete it.
+ */
+ history_write ('F', argv[i], "", argv[i], ""); /* F == Free */
+ free (repository);
+
+ if (delete)
+ release_delete (argv[i]);
+ }
+ close_module (db);
+ return (0);
+}
+
+/* We want to "rm -r" the repository, but let us be a little paranoid. */
+static void
+release_delete (dir)
+ char *dir;
+{
+ struct stat st;
+ ino_t ino;
+ int retcode = 0;
+
+ (void) stat (".", &st);
+ ino = st.st_ino;
+ (void) chdir ("..");
+ (void) stat (dir, &st);
+ if (ino != st.st_ino)
+ {
+ error (0, 0,
+ "Parent dir on a different disk, delete of %s aborted", dir);
+ return;
+ }
+ run_setup ("%s -r", RM);
+ run_arg (dir);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
+ error (0, retcode == -1 ? errno : 0,
+ "deletion of module %s failed.", dir);
+}
diff --git a/gnu/usr.bin/cvs/cvs/remove.c b/gnu/usr.bin/cvs/cvs/remove.c
new file mode 100644
index 0000000..5cce883
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/remove.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Remove a File
+ *
+ * Removes entries from the present version. The entries will be removed from
+ * the RCS repository upon the next "commit".
+ *
+ * "remove" accepts no options, only file names that are to be removed. The
+ * file must not exist in the current directory for "remove" to work
+ * correctly.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)remove.c 1.34 92/04/10";
+#endif
+
+#if __STDC__
+static int remove_fileproc (char *file, char *update_dir,
+ char *repository, List *entries,
+ List *srcfiles);
+static Dtype remove_dirproc (char *dir, char *repos, char *update_dir);
+#else
+static Dtype remove_dirproc ();
+static int remove_fileproc ();
+#endif
+
+static int local;
+static int removed_files;
+static int auto_removed_files;
+
+static char *remove_usage[] =
+{
+ "Usage: %s %s [-lR] [files...]\n",
+ "\t-l\tProcess this directory only (not recursive).\n",
+ "\t-R\tProcess directories recursively.\n",
+ NULL
+};
+
+int
+cvsremove (argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c, err;
+
+ if (argc == -1)
+ usage (remove_usage);
+
+ optind = 1;
+ while ((c = gnu_getopt (argc, argv, "lR")) != -1)
+ {
+ switch (c)
+ {
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case '?':
+ default:
+ usage (remove_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* start the recursion processor */
+ err = start_recursion (remove_fileproc, (int (*) ()) NULL, remove_dirproc,
+ (int (*) ()) NULL, argc, argv, local,
+ W_LOCAL, 0, 1, (char *) NULL, 1);
+
+ if (removed_files)
+ error (0, 0, "use '%s commit' to remove %s permanently", program_name,
+ (removed_files == 1) ? "this file" : "these files");
+ else
+ if (!auto_removed_files)
+ error (0, 0, "no files removed; use `%s' to remove the file first",
+ RM);
+
+ return (err);
+}
+
+/*
+ * remove the file, only if it has already been physically removed
+ */
+/* ARGSUSED */
+static int
+remove_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ char fname[PATH_MAX];
+ Vers_TS *vers;
+
+ vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL,
+ file, 0, 0, entries, srcfiles);
+
+ if (vers->ts_user != NULL)
+ {
+ freevers_ts (&vers);
+ return (0);
+ }
+
+ if (vers->vn_user == NULL)
+ {
+ if (!quiet)
+ error (0, 0, "nothing known about %s", file);
+ freevers_ts (&vers);
+ return (0);
+ }
+
+ if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
+ {
+ /*
+ * It's a file that has been added, but not commited yet. So,
+ * remove the ,p and ,t file for it and scratch it from the
+ * entries file.
+ */
+ Scratch_Entry (entries, file);
+ (void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_OPT);
+ (void) unlink_file (fname);
+ (void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_LOG);
+ (void) unlink_file (fname);
+ if (!quiet)
+ error (0, 0, "removed `%s'.", file);
+ auto_removed_files++;
+ }
+ else if (vers->vn_user[0] == '-')
+ {
+ freevers_ts (&vers);
+ return (0);
+ }
+ else
+ {
+ /* Re-register it with a negative version number. */
+ (void) strcpy (fname, "-");
+ (void) strcat (fname, vers->vn_user);
+ Register (entries, file, fname, vers->ts_rcs, vers->options,
+ vers->tag, vers->date);
+ if (!quiet)
+ {
+ error (0, 0, "scheduling %s for removal", file);
+ removed_files++;
+ }
+ }
+
+ freevers_ts (&vers);
+ return (0);
+}
+
+/*
+ * Print a warm fuzzy message
+ */
+/* ARGSUSED */
+static Dtype
+remove_dirproc (dir, repos, update_dir)
+ char *dir;
+ char *repos;
+ char *update_dir;
+{
+ if (!quiet)
+ error (0, 0, "Removing %s", update_dir);
+ return (R_PROCESS);
+}
diff --git a/gnu/usr.bin/cvs/cvs/repos.c b/gnu/usr.bin/cvs/cvs/repos.c
new file mode 100644
index 0000000..43a9dfe
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/repos.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Name of Repository
+ *
+ * Determine the name of the RCS repository and sets "Repository" accordingly.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)repos.c 1.28 92/03/31";
+#endif
+
+char *
+Name_Repository (dir, update_dir)
+ char *dir;
+ char *update_dir;
+{
+ FILE *fpin;
+ char *ret, *xupdate_dir;
+ char repos[PATH_MAX];
+ char path[PATH_MAX];
+ char tmp[PATH_MAX];
+ char cvsadm[PATH_MAX];
+ char ocvsadm[PATH_MAX];
+ char *cp;
+ int has_cvsadm = 0, has_ocvsadm = 0;
+
+ if (update_dir && *update_dir)
+ xupdate_dir = update_dir;
+ else
+ xupdate_dir = ".";
+
+ if (dir != NULL)
+ {
+ (void) sprintf (cvsadm, "%s/%s", dir, CVSADM);
+ (void) sprintf (ocvsadm, "%s/%s", dir, OCVSADM);
+ }
+ else
+ {
+ (void) strcpy (cvsadm, CVSADM);
+ (void) strcpy (ocvsadm, OCVSADM);
+ }
+
+ /* sanity checks */
+ if (!(has_cvsadm = isdir (cvsadm)) && !(has_ocvsadm = isdir (ocvsadm)))
+ {
+ error (0, 0, "in directory %s:", xupdate_dir);
+ error (1, 0, "there is no version here; do '%s checkout' first",
+ program_name);
+ }
+
+ if (has_ocvsadm)
+ {
+ if (has_cvsadm)
+ {
+ error (0, 0, "in directory %s:", xupdate_dir);
+ error (1, 0, "error: both `%s' and `%s' exist; I give up",
+ CVSADM, OCVSADM);
+ }
+ if (rename (ocvsadm, cvsadm) < 0)
+ {
+ error (0, 0, "in directory %s:", xupdate_dir);
+ error (1, errno, "cannot rename `%s' to `%s'; I give up",
+ OCVSADM, CVSADM);
+ }
+
+ /*
+ * We have converted the old CVS.adm directory to the new CVS
+ * directory. Now, convert the Entries file to the new format, if
+ * necessary.
+ */
+ check_entries (dir);
+ }
+
+ if (dir != NULL)
+ (void) sprintf (tmp, "%s/%s", dir, CVSADM_ENT);
+ else
+ (void) strcpy (tmp, CVSADM_ENT);
+
+ if (!isreadable (tmp))
+ {
+ error (0, 0, "in directory %s:", xupdate_dir);
+ error (1, 0, "*PANIC* administration files missing");
+ }
+
+ if (dir != NULL)
+ (void) sprintf (tmp, "%s/%s", dir, CVSADM_REP);
+ else
+ (void) strcpy (tmp, CVSADM_REP);
+
+ if (!isreadable (tmp))
+ {
+ error (0, 0, "in directory %s:", xupdate_dir);
+ error (1, 0, "*PANIC* administration files missing");
+ }
+
+ /*
+ * The assumption here is that the repository is always contained in the
+ * first line of the "Repository" file.
+ */
+ fpin = open_file (tmp, "r");
+
+ if (fgets (repos, PATH_MAX, fpin) == NULL)
+ {
+ error (0, 0, "in directory %s:", xupdate_dir);
+ error (1, errno, "cannot read %s", CVSADM_REP);
+ }
+ (void) fclose (fpin);
+ if ((cp = rindex (repos, '\n')) != NULL)
+ *cp = '\0'; /* strip the newline */
+
+ /*
+ * If this is a relative repository pathname, turn it into an absolute
+ * one by tacking on the CVSROOT environment variable. If the CVSROOT
+ * environment variable is not set, die now.
+ */
+ if (strcmp (repos, "..") == 0 || strncmp (repos, "../", 3) == 0)
+ {
+ error (0, 0, "in directory %s:", xupdate_dir);
+ error (0, 0, "`..'-relative repositories are not supported.");
+ error (1, 0, "illegal source repository");
+ }
+ if (repos[0] != '/')
+ {
+ if (CVSroot == NULL)
+ {
+ error (0, 0, "in directory %s:", xupdate_dir);
+ error (0, 0, "must set the CVSROOT environment variable\n");
+ error (0, 0, "or specify the '-d' option to %s.", program_name);
+ error (1, 0, "illegal repository setting");
+ }
+ (void) strcpy (path, repos);
+ (void) sprintf (repos, "%s/%s", CVSroot, path);
+ }
+ if (!isdir (repos))
+ {
+ error (0, 0, "in directory %s:", xupdate_dir);
+ error (1, 0, "there is no repository %s", repos);
+ }
+
+ /* allocate space to return and fill it in */
+ strip_path (repos);
+ ret = xstrdup (repos);
+ return (ret);
+}
+
+/*
+ * Return a pointer to the repository name relative to CVSROOT from a
+ * possibly fully qualified repository
+ */
+char *
+Short_Repository (repository)
+ char *repository;
+{
+ if (repository == NULL)
+ return (NULL);
+
+ /* if repository matches CVSroot at the beginning, strip off CVSroot */
+ if (strncmp (CVSroot, repository, strlen (CVSroot)) == 0)
+ return (repository + strlen (CVSroot) + 1);
+ else
+ return (repository);
+}
diff --git a/gnu/usr.bin/cvs/cvs/rtag.c b/gnu/usr.bin/cvs/cvs/rtag.c
new file mode 100644
index 0000000..a53e8c7
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/rtag.c
@@ -0,0 +1,403 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Rtag
+ *
+ * Add or delete a symbolic name to an RCS file, or a collection of RCS files.
+ * Uses the modules database, if necessary.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)rtag.c 1.57 92/04/10";
+#endif
+
+#if __STDC__
+static Dtype rtag_dirproc (char *dir, char *repos, char *update_dir);
+static int rtag_fileproc (char *file, char *update_dir,
+ char *repository, List * entries,
+ List * srcfiles);
+static int rtag_proc (int *pargc, char *argv[], char *xwhere,
+ char *mwhere, char *mfile, int shorten,
+ int local_specified, char *mname, char *msg);
+static int rtag_delete (RCSNode *rcsfile);
+#else
+static int rtag_proc ();
+static int rtag_fileproc ();
+static Dtype rtag_dirproc ();
+static int rtag_delete ();
+#endif /* __STDC__ */
+
+static char *symtag;
+static char *numtag;
+static int delete; /* adding a tag by default */
+static int attic_too; /* remove tag from Attic files */
+static int branch_mode; /* make an automagic "branch" tag */
+static char *date;
+static int local; /* recursive by default */
+static int force_tag_match = 1; /* force by default */
+
+static char *rtag_usage[] =
+{
+ "Usage: %s %s [-QaflRnq] [-b] [-d] [-r tag|-D date] tag modules...\n",
+ "\t-Q\tReally quiet.\n",
+ "\t-a\tClear tag from removed files that would not otherwise be tagged.\n",
+ "\t-f\tForce a head revision match if tag/date not found.\n",
+ "\t-l\tLocal directory only, not recursive\n",
+ "\t-R\tProcess directories recursively.\n",
+ "\t-n\tNo execution of 'tag program'\n",
+ "\t-q\tSomewhat quiet.\n",
+ "\t-d\tDelete the given Tag.\n",
+ "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
+ "\t-[rD]\tExisting tag or Date.\n",
+ NULL
+};
+
+int
+rtag (argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int i;
+ int c;
+ DBM *db;
+ int run_module_prog = 1;
+ int err = 0;
+
+ if (argc == -1)
+ usage (rtag_usage);
+
+ optind = 1;
+ while ((c = gnu_getopt (argc, argv, "anfQqlRdbr:D:")) != -1)
+ {
+ switch (c)
+ {
+ case 'a':
+ attic_too = 1;
+ break;
+ case 'n':
+ run_module_prog = 0;
+ break;
+ case 'Q':
+ really_quiet = 1;
+ /* FALL THROUGH */
+ case 'q':
+ quiet = 1;
+ break;
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case 'd':
+ delete = 1;
+ break;
+ case 'f':
+ force_tag_match = 0;
+ break;
+ case 'b':
+ branch_mode = 1;
+ break;
+ case 'r':
+ numtag = optarg;
+ break;
+ case 'D':
+ if (date)
+ free (date);
+ date = Make_Date (optarg);
+ break;
+ case '?':
+ default:
+ usage (rtag_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 2)
+ usage (rtag_usage);
+ symtag = argv[0];
+ argc--;
+ argv++;
+
+ if (date && numtag)
+ error (1, 0, "-r and -D options are mutually exclusive");
+ if (delete && branch_mode)
+ error (0, 0, "warning: -b ignored with -d options");
+ RCS_check_tag (symtag);
+
+ db = open_module ();
+ for (i = 0; i < argc; i++)
+ {
+ /* XXX last arg should be repository, but doesn't make sense here */
+ history_write ('T', (delete ? "D" : (numtag ? numtag :
+ (date ? date : "A"))), symtag, argv[i], "");
+ err += do_module (db, argv[i], TAG, delete ? "Untagging" : "Tagging",
+ rtag_proc, (char *) NULL, 0, 0, run_module_prog,
+ symtag);
+ }
+ close_module (db);
+ return (err);
+}
+
+/*
+ * callback proc for doing the real work of tagging
+ */
+/* ARGSUSED */
+static int
+rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified,
+ mname, msg)
+ int *pargc;
+ char *argv[];
+ char *xwhere;
+ char *mwhere;
+ char *mfile;
+ int shorten;
+ int local_specified;
+ char *mname;
+ char *msg;
+{
+ int err = 0;
+ int which;
+ char repository[PATH_MAX];
+ char where[PATH_MAX];
+
+ (void) sprintf (repository, "%s/%s", CVSroot, argv[0]);
+ (void) strcpy (where, argv[0]);
+
+ /* if mfile isn't null, we need to set up to do only part of the module */
+ if (mfile != NULL)
+ {
+ char *cp;
+ char path[PATH_MAX];
+
+ /* if the portion of the module is a path, put the dir part on repos */
+ if ((cp = rindex (mfile, '/')) != NULL)
+ {
+ *cp = '\0';
+ (void) strcat (repository, "/");
+ (void) strcat (repository, mfile);
+ (void) strcat (where, "/");
+ (void) strcat (where, mfile);
+ mfile = cp + 1;
+ }
+
+ /* take care of the rest */
+ (void) sprintf (path, "%s/%s", repository, mfile);
+ if (isdir (path))
+ {
+ /* directory means repository gets the dir tacked on */
+ (void) strcpy (repository, path);
+ (void) strcat (where, "/");
+ (void) strcat (where, mfile);
+ }
+ else
+ {
+ int i;
+
+ /* a file means muck argv */
+ for (i = 1; i < *pargc; i++)
+ free (argv[i]);
+ argv[1] = xstrdup (mfile);
+ (*pargc) = 2;
+ }
+ }
+
+ /* chdir to the starting directory */
+ if (chdir (repository) < 0)
+ {
+ error (0, errno, "cannot chdir to %s", repository);
+ return (1);
+ }
+
+ if (delete || attic_too || (force_tag_match && numtag))
+ which = W_REPOS | W_ATTIC;
+ else
+ which = W_REPOS;
+
+ /* start the recursion processor */
+ err = start_recursion (rtag_fileproc, (int (*) ()) NULL, rtag_dirproc,
+ (int (*) ()) NULL, *pargc - 1, argv + 1, local,
+ which, 0, 1, where, 1);
+
+ return (err);
+}
+
+/*
+ * Called to tag a particular file, as appropriate with the options that were
+ * set above.
+ */
+/* ARGSUSED */
+static int
+rtag_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ Node *p;
+ RCSNode *rcsfile;
+ char *version, *rev;
+ int retcode = 0;
+
+ /* find the parsed RCS data */
+ p = findnode (srcfiles, file);
+ if (p == NULL)
+ return (1);
+ rcsfile = (RCSNode *) p->data;
+
+ /*
+ * For tagging an RCS file which is a symbolic link, you'd best be
+ * running with RCS 5.6, since it knows how to handle symbolic links
+ * correctly without breaking your link!
+ */
+
+ if (delete)
+ return (rtag_delete (rcsfile));
+
+ /*
+ * If we get here, we are adding a tag. But, if -a was specified, we
+ * need to check to see if a -r or -D option was specified. If neither
+ * was specified and the file is in the Attic, remove the tag.
+ */
+ if (attic_too && (!numtag && !date))
+ {
+ if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
+ return (rtag_delete (rcsfile));
+ }
+
+ version = RCS_getversion (rcsfile, numtag, date, force_tag_match);
+ if (version == NULL)
+ {
+ /* If -a specified, clean up any old tags */
+ if (attic_too)
+ (void) rtag_delete (rcsfile);
+
+ if (!quiet && !force_tag_match)
+ {
+ error (0, 0, "cannot find tag `%s' in `%s'",
+ numtag ? numtag : "head", rcsfile->path);
+ return (1);
+ }
+ return (0);
+ }
+ if (numtag && isdigit (*numtag) && strcmp (numtag, version) != 0)
+ {
+
+ /*
+ * We didn't find a match for the numeric tag that was specified, but
+ * that's OK. just pass the numeric tag on to rcs, to be tagged as
+ * specified. Could get here if one tried to tag "1.1.1" and there
+ * was a 1.1.1 branch with some head revision. In this case, we want
+ * the tag to reference "1.1.1" and not the revision at the head of
+ * the branch. Use a symbolic tag for that.
+ */
+ rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag;
+ run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, symtag, numtag);
+ }
+ else
+ {
+ char *oversion;
+
+ /*
+ * As an enhancement for the case where a tag is being re-applied to
+ * a large body of a module, make one extra call to Version_Number to
+ * see if the tag is already set in the RCS file. If so, check to
+ * see if it needs to be moved. If not, do nothing. This will
+ * likely save a lot of time when simply moving the tag to the
+ * "current" head revisions of a module -- which I have found to be a
+ * typical tagging operation.
+ */
+ oversion = RCS_getversion (rcsfile, symtag, (char *) 0, 1);
+ if (oversion != NULL)
+ {
+ if (strcmp (version, oversion) == 0)
+ {
+ free (version);
+ free (oversion);
+ return (0);
+ }
+ free (oversion);
+ }
+ rev = branch_mode ? RCS_magicrev (rcsfile, version) : version;
+ run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, symtag, rev);
+ }
+ run_arg (rcsfile->path);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
+ {
+ if (!quiet)
+ error (0, retcode == -1 ? errno : 0,
+ "failed to set tag `%s' to revision `%s' in `%s'",
+ symtag, rev, rcsfile->path);
+ free (version);
+ return (1);
+ }
+ free (version);
+ return (0);
+}
+
+/*
+ * If -d is specified, "force_tag_match" is set, so that this call to
+ * Version_Number() will return a NULL version string if the symbolic
+ * tag does not exist in the RCS file.
+ *
+ * If the -r flag was used, numtag is set, and we only delete the
+ * symtag from files that have numtag.
+ *
+ * This is done here because it's MUCH faster than just blindly calling
+ * "rcs" to remove the tag... trust me.
+ */
+static int
+rtag_delete (rcsfile)
+ RCSNode *rcsfile;
+{
+ char *version;
+ int retcode;
+
+ if (numtag)
+ {
+ version = RCS_getversion (rcsfile, numtag, (char *) 0, 1);
+ if (version == NULL)
+ return (0);
+ free (version);
+ }
+
+ version = RCS_getversion (rcsfile, symtag, (char *) 0, 1);
+ if (version == NULL)
+ return (0);
+ free (version);
+
+ run_setup ("%s%s -q -N%s", Rcsbin, RCS, symtag);
+ run_arg (rcsfile->path);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, DEVNULL, RUN_NORMAL)) != 0)
+ {
+ if (!quiet)
+ error (0, retcode == -1 ? errno : 0,
+ "failed to remove tag `%s' from `%s'", symtag,
+ rcsfile->path);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Print a warm fuzzy message
+ */
+/* ARGSUSED */
+static Dtype
+rtag_dirproc (dir, repos, update_dir)
+ char *dir;
+ char *repos;
+ char *update_dir;
+{
+ if (!quiet)
+ error (0, 0, "%s %s", delete ? "Untagging" : "Tagging", update_dir);
+ return (R_PROCESS);
+}
diff --git a/gnu/usr.bin/cvs/cvs/status.c b/gnu/usr.bin/cvs/cvs/status.c
new file mode 100644
index 0000000..9749740
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/status.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Status Information
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)status.c 1.48 92/03/31";
+#endif
+
+#if __STDC__
+static Dtype status_dirproc (char *dir, char *repos, char *update_dir);
+static int status_fileproc (char *file, char *update_dir,
+ char *repository, List * entries,
+ List * srcfiles);
+static int tag_list_proc (Node * p);
+#else
+static int tag_list_proc ();
+static int status_fileproc ();
+static Dtype status_dirproc ();
+#endif /* __STDC__ */
+
+static int local = 0;
+static int long_format = 0;
+
+static char *status_usage[] =
+{
+ "Usage: %s %s [-vlR] [files...]\n",
+ "\t-v\tVerbose format; includes tag information for the file\n",
+ "\t-l\tProcess this directory only (not recursive).\n",
+ "\t-R\tProcess directories recursively.\n",
+ NULL
+};
+
+int
+status (argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ int err = 0;
+
+ if (argc == -1)
+ usage (status_usage);
+
+ optind = 1;
+ while ((c = gnu_getopt (argc, argv, "vlR")) != -1)
+ {
+ switch (c)
+ {
+ case 'v':
+ long_format = 1;
+ break;
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case '?':
+ default:
+ usage (status_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* start the recursion processor */
+ err = start_recursion (status_fileproc, (int (*) ()) NULL, status_dirproc,
+ (int (*) ()) NULL, argc, argv, local,
+ W_LOCAL, 0, 1, (char *) NULL, 1);
+
+ return (err);
+}
+
+/*
+ * display the status of a file
+ */
+/* ARGSUSED */
+static int
+status_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ Ctype status;
+ char *sstat;
+ Vers_TS *vers;
+
+ status = Classify_File (file, (char *) NULL, (char *) NULL, (char *) NULL,
+ 1, 0, repository, entries, srcfiles, &vers);
+ switch (status)
+ {
+ case T_UNKNOWN:
+ sstat = "Unknown";
+ break;
+ case T_CHECKOUT:
+ sstat = "Needs Checkout";
+ break;
+ case T_CONFLICT:
+ sstat = "Unresolved Conflict";
+ break;
+ case T_ADDED:
+ sstat = "Locally Added";
+ break;
+ case T_REMOVED:
+ sstat = "Locally Removed";
+ break;
+ case T_MODIFIED:
+ sstat = "Locally Modified";
+ break;
+ case T_REMOVE_ENTRY:
+ sstat = "Entry Invalid";
+ break;
+ case T_UPTODATE:
+ sstat = "Up-to-date";
+ break;
+ case T_NEEDS_MERGE:
+ sstat = "Needs Merge";
+ break;
+ default:
+ sstat = "Classify Error";
+ break;
+ }
+
+ (void) printf ("===================================================================\n");
+ if (vers->ts_user == NULL)
+ (void) printf ("File: no file %s\t\tStatus: %s\n\n", file, sstat);
+ else
+ (void) printf ("File: %-17.17s\tStatus: %s\n\n", file, sstat);
+
+ if (vers->vn_user == NULL)
+ (void) printf (" Version:\t\tNo entry for %s\n", file);
+ else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
+ (void) printf (" Version:\t\tNew file!\n");
+ else
+ (void) printf (" Version:\t\t%s\t%s\n", vers->vn_user,
+ &vers->ts_rcs[25]);
+
+ if (vers->vn_rcs == NULL)
+ (void) printf (" RCS Version:\tNo revision control file\n");
+ else
+ (void) printf (" RCS Version:\t%s\t%s\n", vers->vn_rcs,
+ vers->srcfile->path);
+
+ if (vers->entdata)
+ {
+ Entnode *edata;
+
+ edata = vers->entdata;
+ if (edata->tag)
+ {
+ if (vers->vn_rcs == NULL)
+ (void) printf (
+ " Sticky Tag:\t\t%s - MISSING from RCS file!\n",
+ edata->tag);
+ else
+ {
+ if (isdigit (edata->tag[0]))
+ (void) printf (" Sticky Tag:\t\t%s\n", edata->tag);
+ else
+ (void) printf (" Sticky Tag:\t\t%s (%s: %s)\n",
+ edata->tag, numdots (vers->vn_rcs) % 2 ?
+ "revision" : "branch", vers->vn_rcs);
+ }
+ }
+ else
+ (void) printf (" Sticky Tag:\t\t(none)\n");
+
+ if (edata->date)
+ (void) printf (" Sticky Date:\t%s\n", edata->date);
+ else
+ (void) printf (" Sticky Date:\t(none)\n");
+
+ if (edata->options && edata->options[0])
+ (void) printf (" Sticky Options:\t%s\n", edata->options);
+ else
+ (void) printf (" Sticky Options:\t(none)\n");
+
+ if (long_format && vers->srcfile)
+ {
+ (void) printf ("\n Existing Tags:\n");
+ if (vers->srcfile->symbols)
+ (void) walklist (vers->srcfile->symbols, tag_list_proc);
+ else
+ (void) printf ("\tNo Tags Exist\n");
+ }
+ }
+
+ (void) printf ("\n");
+ freevers_ts (&vers);
+ return (0);
+}
+
+/*
+ * Print a warm fuzzy message
+ */
+/* ARGSUSED */
+static Dtype
+status_dirproc (dir, repos, update_dir)
+ char *dir;
+ char *repos;
+ char *update_dir;
+{
+ if (!quiet)
+ error (0, 0, "Examining %s", update_dir);
+ return (R_PROCESS);
+}
+
+/*
+ * Print out a tag and its type
+ */
+static int
+tag_list_proc (p)
+ Node *p;
+{
+ (void) printf ("\t%-25.25s\t(%s: %s)\n", p->key,
+ numdots (p->data) % 2 ? "revision" : "branch",
+ p->data);
+ return (0);
+}
diff --git a/gnu/usr.bin/cvs/cvs/tag.c b/gnu/usr.bin/cvs/cvs/tag.c
new file mode 100644
index 0000000..71a8c46
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/tag.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Tag
+ *
+ * Add or delete a symbolic name to an RCS file, or a collection of RCS files.
+ * Uses the checked out revision in the current directory.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)tag.c 1.56 92/03/31";
+#endif
+
+#if __STDC__
+static Dtype tag_dirproc (char *dir, char *repos, char *update_dir);
+static int tag_fileproc (char *file, char *update_dir,
+ char *repository, List * entries,
+ List * srcfiles);
+#else
+static int tag_fileproc ();
+static Dtype tag_dirproc ();
+#endif /* __STDC__ */
+
+static char *symtag;
+static int delete; /* adding a tag by default */
+static int branch_mode; /* make an automagic "branch" tag */
+static int local; /* recursive by default */
+
+static char *tag_usage[] =
+{
+ "Usage: %s %s [-QlRq] [-b] [-d] tag [files...]\n",
+ "\t-Q\tReally quiet.\n",
+ "\t-l\tLocal directory only, not recursive.\n",
+ "\t-R\tProcess directories recursively.\n",
+ "\t-q\tSomewhat quiet.\n",
+ "\t-d\tDelete the given Tag.\n",
+ "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
+ NULL
+};
+
+int
+tag (argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ int err = 0;
+
+ if (argc == -1)
+ usage (tag_usage);
+
+ optind = 1;
+ while ((c = gnu_getopt (argc, argv, "QqlRdb")) != -1)
+ {
+ switch (c)
+ {
+ case 'Q':
+ really_quiet = 1;
+ /* FALL THROUGH */
+ case 'q':
+ quiet = 1;
+ break;
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case 'd':
+ delete = 1;
+ break;
+ case 'b':
+ branch_mode = 1;
+ break;
+ case '?':
+ default:
+ usage (tag_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ usage (tag_usage);
+ symtag = argv[0];
+ argc--;
+ argv++;
+
+ if (delete && branch_mode)
+ error (0, 0, "warning: -b ignored with -d options");
+ RCS_check_tag (symtag);
+
+ /* start the recursion processor */
+ err = start_recursion (tag_fileproc, (int (*) ()) NULL, tag_dirproc,
+ (int (*) ()) NULL, argc, argv, local,
+ W_LOCAL, 0, 1, (char *) NULL, 1);
+ return (err);
+}
+
+/*
+ * Called to tag a particular file (the currently checked out version is
+ * tagged with the specified tag - or the specified tag is deleted).
+ */
+/* ARGSUSED */
+static int
+tag_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ char *version, *oversion;
+ char *rev;
+ Vers_TS *vers;
+ int retcode = 0;
+
+ vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL,
+ file, 0, 0, entries, srcfiles);
+
+ if (delete)
+ {
+
+ /*
+ * If -d is specified, "force_tag_match" is set, so that this call to
+ * Version_Number() will return a NULL version string if the symbolic
+ * tag does not exist in the RCS file.
+ *
+ * This is done here because it's MUCH faster than just blindly calling
+ * "rcs" to remove the tag... trust me.
+ */
+
+ version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1);
+ if (version == NULL || vers->srcfile == NULL)
+ {
+ freevers_ts (&vers);
+ return (0);
+ }
+ free (version);
+
+ run_setup ("%s%s -q -N%s", Rcsbin, RCS, symtag);
+ run_arg (vers->srcfile->path);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, DEVNULL, RUN_NORMAL)) != 0)
+ {
+ if (!quiet)
+ error (0, retcode == -1 ? errno : 0,
+ "failed to remove tag %s from %s", symtag,
+ vers->srcfile->path);
+ freevers_ts (&vers);
+ return (1);
+ }
+
+ /* warm fuzzies */
+ if (!really_quiet)
+ {
+ if (update_dir[0])
+ (void) printf ("D %s/%s\n", update_dir, file);
+ else
+ (void) printf ("D %s\n", file);
+ }
+
+ freevers_ts (&vers);
+ return (0);
+ }
+
+ /*
+ * If we are adding a tag, we need to know which version we have checked
+ * out and we'll tag that version.
+ */
+ version = vers->vn_user;
+ if (version == NULL)
+ {
+ freevers_ts (&vers);
+ return (0);
+ }
+ else if (strcmp (version, "0") == 0)
+ {
+ if (!quiet)
+ error (0, 0, "couldn't tag added but un-commited file `%s'", file);
+ freevers_ts (&vers);
+ return (0);
+ }
+ else if (version[0] == '-')
+ {
+ if (!quiet)
+ error (0, 0, "skipping removed but un-commited file `%s'", file);
+ freevers_ts (&vers);
+ return (0);
+ }
+ else if (vers->srcfile == NULL)
+ {
+ if (!quiet)
+ error (0, 0, "cannot find revision control file for `%s'", file);
+ freevers_ts (&vers);
+ return (0);
+ }
+
+ /*
+ * As an enhancement for the case where a tag is being re-applied to a
+ * large number of files, make one extra call to Version_Number to see if
+ * the tag is already set in the RCS file. If so, check to see if it
+ * needs to be moved. If not, do nothing. This will likely save a lot of
+ * time when simply moving the tag to the "current" head revisions of a
+ * module -- which I have found to be a typical tagging operation.
+ */
+ oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1);
+ if (oversion != NULL)
+ {
+ if (strcmp (version, oversion) == 0)
+ {
+ free (oversion);
+ freevers_ts (&vers);
+ return (0);
+ }
+ free (oversion);
+ }
+ rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version;
+ run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, symtag, rev);
+ run_arg (vers->srcfile->path);
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
+ {
+ if (!quiet)
+ error (0, retcode == -1 ? errno : 0,
+ "failed to set tag %s to revision %s in %s",
+ symtag, rev, vers->srcfile->path);
+ freevers_ts (&vers);
+ return (1);
+ }
+
+ /* more warm fuzzies */
+ if (!really_quiet)
+ {
+ if (update_dir[0])
+ (void) printf ("T %s/%s\n", update_dir, file);
+ else
+ (void) printf ("T %s\n", file);
+ }
+
+ freevers_ts (&vers);
+ return (0);
+}
+
+/*
+ * Print a warm fuzzy message
+ */
+/* ARGSUSED */
+static Dtype
+tag_dirproc (dir, repos, update_dir)
+ char *dir;
+ char *repos;
+ char *update_dir;
+{
+ if (!quiet)
+ error (0, 0, "%s %s", delete ? "Untagging" : "Tagging", update_dir);
+ return (R_PROCESS);
+}
diff --git a/gnu/usr.bin/cvs/cvs/update.c b/gnu/usr.bin/cvs/cvs/update.c
new file mode 100644
index 0000000..d91ffdd
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/update.c
@@ -0,0 +1,1076 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * "update" updates the version in the present directory with respect to the RCS
+ * repository. The present version must have been created by "checkout". The
+ * user can keep up-to-date by calling "update" whenever he feels like it.
+ *
+ * The present version can be committed by "commit", but this keeps the version
+ * in tact.
+ *
+ * Arguments following the options are taken to be file names to be updated,
+ * rather than updating the entire directory.
+ *
+ * Modified or non-existent RCS files are checked out and reported as U
+ * <user_file>
+ *
+ * Modified user files are reported as M <user_file>. If both the RCS file and
+ * the user file have been modified, the user file is replaced by the result
+ * of rcsmerge, and a backup file is written for the user in .#file.version.
+ * If this throws up irreconcilable differences, the file is reported as C
+ * <user_file>, and as M <user_file> otherwise.
+ *
+ * Files added but not yet committed are reported as A <user_file>. Files
+ * removed but not yet committed are reported as R <user_file>.
+ *
+ * If the current directory contains subdirectories that hold concurrent
+ * versions, these are updated too. If the -d option was specified, new
+ * directories added to the repository are automatically created and updated
+ * as well.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)update.c 1.83 92/04/10";
+#endif
+
+#if __STDC__
+static int checkout_file (char *file, char *repository, List *entries,
+ List *srcfiles, Vers_TS *vers_ts, char *update_dir);
+static int isemptydir (char *dir);
+static int merge_file (char *file, char *repository, List *entries,
+ Vers_TS *vers, char *update_dir);
+static int scratch_file (char *file, char *repository, List * entries,
+ char *update_dir);
+static Dtype update_dirent_proc (char *dir, char *repository, char *update_dir);
+static int update_dirleave_proc (char *dir, int err, char *update_dir);
+static int update_file_proc (char *file, char *update_dir, char *repository,
+ List * entries, List * srcfiles);
+static int update_filesdone_proc (int err, char *repository, char *update_dir);
+static int write_letter (char *file, int letter, char *update_dir);
+static void ignore_files (List * ilist, char *update_dir);
+static void join_file (char *file, List *srcfiles, Vers_TS *vers_ts,
+ char *update_dir);
+#else
+static int update_file_proc ();
+static int update_filesdone_proc ();
+static Dtype update_dirent_proc ();
+static int update_dirleave_proc ();
+static int isemptydir ();
+static int scratch_file ();
+static int checkout_file ();
+static int write_letter ();
+static int merge_file ();
+static void ignore_files ();
+static void join_file ();
+#endif /* __STDC__ */
+
+static char *options = NULL;
+static char *tag = NULL;
+static char *date = NULL;
+static char *join_rev1, *date_rev1;
+static char *join_rev2, *date_rev2;
+static char *K_flag;
+static int aflag = 0;
+static int force_tag_match = 1;
+static int update_build_dirs = 0;
+static int update_prune_dirs = 0;
+static int pipeout = 0;
+static List *ignlist = (List *) NULL;
+
+static char *update_usage[] =
+{
+ "Usage:\n %s %s [-APQdflRpq] [-k kopt] [-r rev|-D date] [-j rev] [-I ign] [files...]\n",
+ "\t-A\tReset any sticky tags/date/kopts.\n",
+ "\t-P\tPrune empty directories.\n",
+ "\t-Q\tReally quiet.\n",
+ "\t-d\tBuild directories, like checkout does.\n",
+ "\t-f\tForce a head revision match if tag/date not found.\n",
+ "\t-l\tLocal directory only, no recursion.\n",
+ "\t-R\tProcess directories recursively.\n",
+ "\t-p\tSend updates to standard output.\n",
+ "\t-q\tSomewhat quiet.\n",
+ "\t-k kopt\tUse RCS kopt -k option on checkout.\n",
+ "\t-r rev\tUpdate using specified revision/tag.\n",
+ "\t-D date\tSet date to update from.\n",
+ "\t-j rev\tMerge in changes made between current revision and rev.\n",
+ "\t-I ign\tMore files to ignore (! to reset).\n",
+ "\t-K key\tUse RCS key -K option on checkout.\n",
+ NULL
+};
+
+/*
+ * update is the argv,argc based front end for arg parsing
+ */
+int
+update (argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c, err;
+ int local = 0; /* recursive by default */
+ int which; /* where to look for files and dirs */
+
+ if (argc == -1)
+ usage (update_usage);
+
+ ign_setup ();
+
+ /* parse the args */
+ optind = 1;
+ while ((c = gnu_getopt (argc, argv, "ApPflRQqdk:r:D:j:I:K:")) != -1)
+ {
+ switch (c)
+ {
+ case 'A':
+ aflag = 1;
+ break;
+ case 'I':
+ ign_add (optarg, 0);
+ break;
+ case 'k':
+ if (options)
+ free (options);
+ options = RCS_check_kflag (optarg);
+ break;
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case 'Q':
+ really_quiet = 1;
+ /* FALL THROUGH */
+ case 'q':
+ quiet = 1;
+ break;
+ case 'd':
+ update_build_dirs = 1;
+ break;
+ case 'f':
+ force_tag_match = 0;
+ break;
+ case 'r':
+ tag = optarg;
+ break;
+ case 'D':
+ date = Make_Date (optarg);
+ break;
+ case 'P':
+ update_prune_dirs = 1;
+ break;
+ case 'p':
+ pipeout = 1;
+ noexec = 1; /* so no locks will be created */
+ break;
+ case 'j':
+ if (join_rev2)
+ error (1, 0, "only two -j options can be specified");
+ if (join_rev1)
+ join_rev2 = optarg;
+ else
+ join_rev1 = optarg;
+ break;
+ case 'K':
+ K_flag = optarg;
+ break;
+ case '?':
+ default:
+ usage (update_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+#ifdef FREEBSD_DEVELOPER
+ if (!K_flag && freebsd) {
+ /* XXX Note: The leading -K is not needed, it gets added later! */
+ K_flag = "eAuthor,eDate,eHeader,eId,eLocker,eLog,eRCSfile,eRevision,eSource,eState,iFreeBSD";
+ }
+#endif /* FREEBSD_DEVELOPER */
+
+ /*
+ * If we are updating the entire directory (for real) and building dirs
+ * as we go, we make sure there is no static entries file and write the
+ * tag file as appropriate
+ */
+ if (argc <= 0 && !pipeout)
+ {
+ if (update_build_dirs)
+ (void) unlink_file (CVSADM_ENTSTAT);
+
+ /* keep the CVS/Tag file current with the specified arguments */
+ if (aflag || tag || date)
+ WriteTag ((char *) NULL, tag, date);
+ }
+
+ /* look for files/dirs locally and in the repository */
+ which = W_LOCAL | W_REPOS;
+
+ /* look in the attic too if a tag or date is specified */
+ if (tag != NULL || date != NULL)
+ which |= W_ATTIC;
+
+ /* call the command line interface */
+ err = do_update (argc, argv, options, tag, date, force_tag_match,
+ local, update_build_dirs, aflag, update_prune_dirs,
+ pipeout, which, join_rev1, join_rev2,
+ K_flag, (char *) NULL);
+
+ /* free the space Make_Date allocated if necessary */
+ if (date != NULL)
+ free (date);
+
+ return (err);
+}
+
+/*
+ * Command line interface to update (used by checkout)
+ */
+int
+do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag,
+ xprune, xpipeout, which, xjoin_rev1, xjoin_rev2,
+ xK_flag, preload_update_dir)
+ int argc;
+ char *argv[];
+ char *xoptions;
+ char *xtag;
+ char *xdate;
+ int xforce;
+ int local;
+ int xbuild;
+ int xaflag;
+ int xprune;
+ int xpipeout;
+ int which;
+ char *xjoin_rev1;
+ char *xjoin_rev2;
+ char *xK_flag;
+ char *preload_update_dir;
+{
+ int err = 0;
+ char *cp;
+
+ /* fill in the statics */
+ options = xoptions;
+ tag = xtag;
+ date = xdate;
+ force_tag_match = xforce;
+ update_build_dirs = xbuild;
+ aflag = xaflag;
+ update_prune_dirs = xprune;
+ pipeout = xpipeout;
+
+ K_flag = xK_flag;
+
+ /* setup the join support */
+ join_rev1 = xjoin_rev1;
+ join_rev2 = xjoin_rev2;
+ if (join_rev1 && (cp = index (join_rev1, ':')) != NULL)
+ {
+ *cp++ = '\0';
+ date_rev1 = Make_Date (cp);
+ }
+ else
+ date_rev1 = (char *) NULL;
+ if (join_rev2 && (cp = index (join_rev2, ':')) != NULL)
+ {
+ *cp++ = '\0';
+ date_rev2 = Make_Date (cp);
+ }
+ else
+ date_rev2 = (char *) NULL;
+
+ /* call the recursion processor */
+ err = start_recursion (update_file_proc, update_filesdone_proc,
+ update_dirent_proc, update_dirleave_proc,
+ argc, argv, local, which, aflag, 1,
+ preload_update_dir, 1);
+ return (err);
+}
+
+/*
+ * This is the callback proc for update. It is called for each file in each
+ * directory by the recursion code. The current directory is the local
+ * instantiation. file is the file name we are to operate on. update_dir is
+ * set to the path relative to where we started (for pretty printing).
+ * repository is the repository. entries and srcfiles are the pre-parsed
+ * entries and source control files.
+ *
+ * This routine decides what needs to be done for each file and does the
+ * appropriate magic for checkout
+ */
+static int
+update_file_proc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ int retval;
+ Ctype status;
+ Vers_TS *vers;
+
+ status = Classify_File (file, tag, date, options, force_tag_match,
+ aflag, repository, entries, srcfiles, &vers);
+ if (pipeout)
+ {
+ /*
+ * We just return success without doing anything if any of the really
+ * funky cases occur
+ *
+ * If there is still a valid RCS file, do a regular checkout type
+ * operation
+ */
+ switch (status)
+ {
+ case T_UNKNOWN: /* unknown file was explicitly asked
+ * about */
+ case T_REMOVE_ENTRY: /* needs to be un-registered */
+ case T_ADDED: /* added but not committed */
+ retval = 0;
+ break;
+ case T_CONFLICT: /* old punt-type errors */
+ retval = 1;
+ break;
+ case T_UPTODATE: /* file was already up-to-date */
+ case T_NEEDS_MERGE: /* needs merging */
+ case T_MODIFIED: /* locally modified */
+ case T_REMOVED: /* removed but not committed */
+ case T_CHECKOUT: /* needs checkout */
+ retval = checkout_file (file, repository, entries, srcfiles,
+ vers, update_dir);
+ break;
+
+ default: /* can't ever happen :-) */
+ error (0, 0,
+ "unknown file status %d for file %s", status, file);
+ retval = 0;
+ break;
+ }
+ }
+ else
+ {
+ switch (status)
+ {
+ case T_UNKNOWN: /* unknown file was explicitly asked
+ * about */
+ case T_UPTODATE: /* file was already up-to-date */
+ retval = 0;
+ break;
+ case T_CONFLICT: /* old punt-type errors */
+ retval = 1;
+ break;
+ case T_NEEDS_MERGE: /* needs merging */
+ retval = merge_file (file, repository, entries,
+ vers, update_dir);
+ break;
+ case T_MODIFIED: /* locally modified */
+ retval = write_letter (file, 'M', update_dir);
+ break;
+ case T_CHECKOUT: /* needs checkout */
+ retval = checkout_file (file, repository, entries, srcfiles,
+ vers, update_dir);
+ break;
+ case T_ADDED: /* added but not committed */
+ retval = write_letter (file, 'A', update_dir);
+ break;
+ case T_REMOVED: /* removed but not committed */
+ retval = write_letter (file, 'R', update_dir);
+ break;
+ case T_REMOVE_ENTRY: /* needs to be un-registered */
+ retval = scratch_file (file, repository, entries, update_dir);
+ break;
+ default: /* can't ever happen :-) */
+ error (0, 0,
+ "unknown file status %d for file %s", status, file);
+ retval = 0;
+ break;
+ }
+ }
+
+ /* only try to join if things have gone well thus far */
+ if (retval == 0 && join_rev1)
+ join_file (file, srcfiles, vers, update_dir);
+
+ /* if this directory has an ignore list, add this file to it */
+ if (ignlist)
+ {
+ Node *p;
+
+ p = getnode ();
+ p->type = FILES;
+ p->key = xstrdup (file);
+ (void) addnode (ignlist, p);
+ }
+
+ freevers_ts (&vers);
+ return (retval);
+}
+
+/*
+ * update_filesdone_proc () is used
+ */
+/* ARGSUSED */
+static int
+update_filesdone_proc (err, repository, update_dir)
+ int err;
+ char *repository;
+ char *update_dir;
+{
+ /* if this directory has an ignore list, process it then free it */
+ if (ignlist)
+ {
+ ignore_files (ignlist, update_dir);
+ dellist (&ignlist);
+ }
+
+ /* Clean up CVS admin dirs if we are export */
+ if (strcmp (command_name, "export") == 0)
+ {
+ run_setup ("%s -fr", RM);
+ run_arg (CVSADM);
+ (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ }
+
+#ifdef DO_LINKS
+ {
+ char lnfile[PATH_MAX];
+ FILE *links;
+
+ sprintf(lnfile, "%s/SymLinks", repository);
+ links = fopen(lnfile, "r");
+ if (links) {
+ char from[PATH_MAX], to[PATH_MAX];
+
+ /* Read all the link pairs from the symlinks file */
+ while (fgets(to, PATH_MAX, links)) {
+ fgets(from, PATH_MAX, links);
+
+ /* Strip off the newlines */
+ to[strlen(to) - 1] = '\0';
+ from[strlen(from) - 1] = '\0';
+
+ /* Do it */
+ if (symlink(from, to) == -1) {
+ error (0, errno, "Unable to create symlink `%s'", to);
+ return 1;
+ }
+ else if (!quiet)
+ error (0, 0, "Creating symlink %s", to);
+ }
+ fclose(links);
+ }
+ }
+#endif
+
+ return (err);
+}
+
+/*
+ * update_dirent_proc () is called back by the recursion processor before a
+ * sub-directory is processed for update. In this case, update_dirent proc
+ * will probably create the directory unless -d isn't specified and this is a
+ * new directory. A return code of 0 indicates the directory should be
+ * processed by the recursion code. A return of non-zero indicates the
+ * recursion code should skip this directory.
+ */
+static Dtype
+update_dirent_proc (dir, repository, update_dir)
+ char *dir;
+ char *repository;
+ char *update_dir;
+{
+ if (!isdir (dir))
+ {
+ /* if we aren't building dirs, blow it off */
+ if (!update_build_dirs)
+ return (R_SKIP_ALL);
+
+ if (noexec)
+ {
+ /* ignore the missing dir if -n is specified */
+ error (0, 0, "New directory `%s' -- ignored", dir);
+ return (R_SKIP_ALL);
+ }
+ else
+ {
+ /* otherwise, create the dir and appropriate adm files */
+ make_directory (dir);
+ Create_Admin (dir, repository, tag, date);
+ }
+ }
+
+ /*
+ * If we are building dirs and not going to stdout, we make sure there is
+ * no static entries file and write the tag file as appropriate
+ */
+ if (!pipeout)
+ {
+ if (update_build_dirs)
+ {
+ char tmp[PATH_MAX];
+
+ (void) sprintf (tmp, "%s/%s", dir, CVSADM_ENTSTAT);
+ (void) unlink_file (tmp);
+ }
+
+ /* keep the CVS/Tag file current with the specified arguments */
+ if (aflag || tag || date)
+ WriteTag (dir, tag, date);
+
+ /* initialize the ignore list for this directory */
+ ignlist = getlist ();
+ }
+
+ /* print the warm fuzzy message */
+ if (!quiet)
+ error (0, 0, "Updating %s", update_dir);
+
+ return (R_PROCESS);
+}
+
+/*
+ * update_dirleave_proc () is called back by the recursion code upon leaving
+ * a directory. It will prune empty directories if needed and will execute
+ * any appropriate update programs.
+ */
+/* ARGSUSED */
+static int
+update_dirleave_proc (dir, err, update_dir)
+ char *dir;
+ int err;
+ char *update_dir;
+{
+ FILE *fp;
+
+ /* run the update_prog if there is one */
+ if (err == 0 && !pipeout && !noexec &&
+ (fp = fopen (CVSADM_UPROG, "r")) != NULL)
+ {
+ char *cp;
+ char *repository;
+ char line[MAXLINELEN];
+
+ repository = Name_Repository ((char *) NULL, update_dir);
+ if (fgets (line, sizeof (line), fp) != NULL)
+ {
+ if ((cp = rindex (line, '\n')) != NULL)
+ *cp = '\0';
+ run_setup ("%s %s", line, repository);
+ (void) printf ("%s %s: Executing '", program_name, command_name);
+ run_print (stdout);
+ (void) printf ("'\n");
+ (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ }
+ (void) fclose (fp);
+ free (repository);
+ }
+
+ /* Clean up CVS admin dirs if we are export */
+ if (strcmp (command_name, "export") == 0)
+ {
+ run_setup ("%s -fr", RM);
+ run_arg (CVSADM);
+ (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ }
+
+ /* Prune empty dirs on the way out - if necessary */
+ (void) chdir ("..");
+ if (update_prune_dirs && isemptydir (dir))
+ {
+ run_setup ("%s -fr", RM);
+ run_arg (dir);
+ (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ }
+
+ return (err);
+}
+
+/*
+ * Returns 1 if the argument directory is completely empty, other than the
+ * existence of the CVS directory entry. Zero otherwise.
+ */
+static int
+isemptydir (dir)
+ char *dir;
+{
+ DIR *dirp;
+ struct direct *dp;
+
+ if ((dirp = opendir (dir)) == NULL)
+ {
+ error (0, 0, "cannot open directory %s for empty check", dir);
+ return (0);
+ }
+ while ((dp = readdir (dirp)) != NULL)
+ {
+ if (strcmp (dp->d_name, ".") != 0 && strcmp (dp->d_name, "..") != 0 &&
+ strcmp (dp->d_name, CVSADM) != 0 &&
+ strcmp (dp->d_name, OCVSADM) != 0)
+ {
+ (void) closedir (dirp);
+ return (0);
+ }
+ }
+ (void) closedir (dirp);
+ return (1);
+}
+
+/*
+ * scratch the Entries file entry associated with a file
+ */
+static int
+scratch_file (file, repository, entries, update_dir)
+ char *file;
+ char *repository;
+ List *entries;
+ char *update_dir;
+{
+ history_write ('W', update_dir, "", file, repository);
+ Scratch_Entry (entries, file);
+ (void) unlink_file (file);
+ return (0);
+}
+
+/*
+ * check out a file - essentially returns the result of the fork on "co".
+ */
+static int
+checkout_file (file, repository, entries, srcfiles, vers_ts, update_dir)
+ char *file;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+ Vers_TS *vers_ts;
+ char *update_dir;
+{
+ char backup[PATH_MAX];
+ int set_time, retval = 0;
+ int retcode = 0;
+
+ /* don't screw with backup files if we're going to stdout */
+ if (!pipeout)
+ {
+ (void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, file);
+ if (isfile (file))
+ rename_file (file, backup);
+ else
+ (void) unlink_file (backup);
+ }
+
+ run_setup ("%s%s -q -r%s %s %s%s", Rcsbin, RCS_CO, vers_ts->vn_rcs,
+ vers_ts->options, K_flag ? "-K" : "", K_flag ? K_flag : "");
+
+ /*
+ * if we are checking out to stdout, print a nice message to stderr, and
+ * add the -p flag to the command
+ */
+ if (pipeout)
+ {
+ run_arg ("-p");
+ if (!quiet)
+ {
+ (void) fprintf (stderr, "===================================================================\n");
+ if (update_dir[0])
+ (void) fprintf (stderr, "Checking out %s/%s\n",
+ update_dir, file);
+ else
+ (void) fprintf (stderr, "Checking out %s\n", file);
+ (void) fprintf (stderr, "RCS: %s\n", vers_ts->srcfile->path);
+ (void) fprintf (stderr, "VERS: %s\n", vers_ts->vn_rcs);
+ (void) fprintf (stderr, "***************\n");
+ }
+ }
+
+ /* tack on the rcs and maybe the user file */
+ run_arg (vers_ts->srcfile->path);
+ if (!pipeout)
+ run_arg (file);
+
+ if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
+ (pipeout ? (RUN_NORMAL|RUN_REALLY) : RUN_NORMAL))) == 0)
+ {
+ if (!pipeout)
+ {
+ Vers_TS *xvers_ts;
+
+ if (cvswrite == TRUE)
+ xchmod (file, 1);
+
+ /* set the time from the RCS file iff it was unknown before */
+ if (vers_ts->vn_user == NULL ||
+ strncmp (vers_ts->ts_rcs, "Initial", 7) == 0)
+ {
+ set_time = 1;
+ }
+ else
+ set_time = 0;
+
+ xvers_ts = Version_TS (repository, options, tag, date, file,
+ force_tag_match, set_time, entries, srcfiles);
+ if (strcmp (xvers_ts->options, "-V4") == 0)
+ xvers_ts->options[0] = '\0';
+ Register (entries, file, xvers_ts->vn_rcs, xvers_ts->ts_user,
+ xvers_ts->options, xvers_ts->tag, xvers_ts->date);
+
+ /* fix up the vers structure, in case it is used by join */
+ if (join_rev1)
+ {
+ if (vers_ts->vn_user != NULL)
+ free (vers_ts->vn_user);
+ if (vers_ts->vn_rcs != NULL)
+ free (vers_ts->vn_rcs);
+ vers_ts->vn_user = xstrdup (xvers_ts->vn_rcs);
+ vers_ts->vn_rcs = xstrdup (xvers_ts->vn_rcs);
+ }
+
+ /* If this is really Update and not Checkout, recode history */
+ if (strcmp (command_name, "update") == 0)
+ history_write ('U', update_dir, xvers_ts->vn_rcs, file,
+ repository);
+
+ freevers_ts (&xvers_ts);
+
+ if (!really_quiet)
+ {
+ if (update_dir[0])
+ (void) printf ("U %s/%s\n", update_dir, file);
+ else
+ (void) printf ("U %s\n", file);
+ }
+ }
+ }
+ else
+ {
+ int old_errno = errno; /* save errno value over the rename */
+
+ if (!pipeout && isfile (backup))
+ rename_file (backup, file);
+
+ error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0,
+ "could not check out %s", file);
+
+ retval = retcode;
+ }
+
+ if (!pipeout)
+ (void) unlink_file (backup);
+
+ return (retval);
+}
+
+/*
+ * Several of the types we process only print a bit of information consisting
+ * of a single letter and the name.
+ */
+static int
+write_letter (file, letter, update_dir)
+ char *file;
+ char letter;
+ char *update_dir;
+{
+ if (!really_quiet)
+ {
+ if (update_dir[0])
+ (void) printf ("%c %s/%s\n", letter, update_dir, file);
+ else
+ (void) printf ("%c %s\n", letter, file);
+ }
+ return (0);
+}
+
+/*
+ * Do all the magic associated with a file which needs to be merged
+ */
+static int
+merge_file (file, repository, entries, vers, update_dir)
+ char *file;
+ char *repository;
+ List *entries;
+ Vers_TS *vers;
+ char *update_dir;
+{
+ char user[PATH_MAX];
+ char backup[PATH_MAX];
+ int status;
+ int retcode = 0;
+
+ /*
+ * The users currently modified file is moved to a backup file name
+ * ".#filename.version", so that it will stay around for a few days
+ * before being automatically removed by some cron daemon. The "version"
+ * is the version of the file that the user was most up-to-date with
+ * before the merge.
+ */
+ (void) sprintf (backup, "%s%s.%s", BAKPREFIX, file, vers->vn_user);
+ if (update_dir[0])
+ (void) sprintf (user, "%s/%s", update_dir, file);
+ else
+ (void) strcpy (user, file);
+
+ (void) unlink_file (backup);
+ copy_file (file, backup);
+ xchmod (file, 1);
+
+ /* XXX - Do merge by hand instead of using rcsmerge, due to -k handling */
+ run_setup ("%s%s %s -r%s -r%s", Rcsbin, RCS_RCSMERGE, vers->options,
+ vers->vn_user, vers->vn_rcs);
+ run_arg (vers->srcfile->path);
+ status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ if (status != 0
+#ifdef HAVE_RCS5
+ && status != 1
+#endif
+ )
+ {
+ error (0, status == -1 ? errno : 0,
+ "could not merge revision %s of %s", vers->vn_user, user);
+ error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
+ user, backup);
+ rename_file (backup, file);
+ return (1);
+ }
+ /* XXX - Might want to make sure that rcsmerge changed the file */
+ if (strcmp (vers->options, "-V4") == 0)
+ vers->options[0] = '\0';
+ Register (entries, file, vers->vn_rcs, vers->ts_rcs, vers->options,
+ vers->tag, vers->date);
+
+ /* fix up the vers structure, in case it is used by join */
+ if (join_rev1)
+ {
+ if (vers->vn_user != NULL)
+ free (vers->vn_user);
+ vers->vn_user = xstrdup (vers->vn_rcs);
+ }
+
+ /* possibly run GREP to see if there appear to be conflicts in the file */
+ run_setup ("%s -s", GREP);
+ run_arg (RCS_MERGE_PAT);
+ run_arg (file);
+ if (status == 1 ||
+ (retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) == 0)
+ {
+ if (!noexec)
+ error (0, 0, "conflicts found in %s", user);
+
+ if (!really_quiet)
+ (void) printf ("C %s\n", user);
+
+ history_write ('C', update_dir, vers->vn_rcs, file, repository);
+
+ }
+ else if (retcode == -1)
+ {
+ error (1, errno, "fork failed while examining update of %s", user);
+ }
+ else
+ {
+ if (!really_quiet)
+ (void) printf ("M %s\n", user);
+ history_write ('G', update_dir, vers->vn_rcs, file, repository);
+ }
+ return (0);
+}
+
+/*
+ * Do all the magic associated with a file which needs to be joined
+ * (-j option)
+ */
+static void
+join_file (file, srcfiles, vers, update_dir)
+ char *file;
+ List *srcfiles;
+ Vers_TS *vers;
+ char *update_dir;
+{
+ char user[PATH_MAX];
+ char backup[PATH_MAX];
+ char *rev, *baserev;
+ char *options;
+ int status;
+
+ /* determine if we need to do anything at all */
+ if (vers->vn_user == NULL || vers->srcfile == NULL ||
+ vers->srcfile->path == NULL)
+ {
+ return;
+ }
+
+ /* special handling when two revisions are specified */
+ if (join_rev1 && join_rev2)
+ {
+ rev = RCS_getversion (vers->srcfile, join_rev2, date_rev2, 1);
+ if (rev == NULL)
+ {
+ if (!quiet && date_rev2 == NULL)
+ error (0, 0,
+ "cannot find revision %s in file %s", join_rev2, file);
+ return;
+ }
+
+ baserev = RCS_getversion (vers->srcfile, join_rev1, date_rev1, 1);
+ if (baserev == NULL)
+ {
+ if (!quiet && date_rev1 == NULL)
+ error (0, 0,
+ "cannot find revision %s in file %s", join_rev1, file);
+ free (rev);
+ return;
+ }
+
+ /*
+ * nothing to do if:
+ * second revision matches our BASE revision (vn_user) &&
+ * both revisions are on the same branch
+ */
+ if (strcmp (vers->vn_user, rev) == 0 &&
+ numdots (baserev) == numdots (rev))
+ {
+ /* might be the same branch. take a real look */
+ char *dot = rindex (baserev, '.');
+ int len = (dot - baserev) + 1;
+
+ if (strncmp (baserev, rev, len) == 0)
+ return;
+ }
+ }
+ else
+ {
+ rev = RCS_getversion (vers->srcfile, join_rev1, date_rev1, 1);
+ if (rev == NULL)
+ return;
+ if (strcmp (rev, vers->vn_user) == 0) /* no merge necessary */
+ {
+ free (rev);
+ return;
+ }
+
+ baserev = RCS_whatbranch (file, join_rev1, srcfiles);
+ if (baserev)
+ {
+ char *cp;
+
+ /* we get a branch -- turn it into a revision, or NULL if trunk */
+ if ((cp = rindex (baserev, '.')) == NULL)
+ {
+ free (baserev);
+ baserev = (char *) NULL;
+ }
+ else
+ *cp = '\0';
+ }
+ }
+ if (baserev && strcmp (baserev, rev) == 0)
+ {
+ /* they match -> nothing to do */
+ free (rev);
+ free (baserev);
+ return;
+ }
+
+ /* OK, so we have a revision and possibly a base revision; continue on */
+
+ /*
+ * The users currently modified file is moved to a backup file name
+ * ".#filename.version", so that it will stay around for a few days
+ * before being automatically removed by some cron daemon. The "version"
+ * is the version of the file that the user was most up-to-date with
+ * before the merge.
+ */
+ (void) sprintf (backup, "%s%s.%s", BAKPREFIX, file, vers->vn_user);
+ if (update_dir[0])
+ (void) sprintf (user, "%s/%s", update_dir, file);
+ else
+ (void) strcpy (user, file);
+
+ (void) unlink_file (backup);
+ copy_file (file, backup);
+ xchmod (file, 1);
+
+ options = vers->options;
+#ifdef HAVE_RCS5
+ if (*options == '\0')
+ options = "-kk"; /* to ignore keyword expansions */
+#endif
+
+ /* XXX - Do merge by hand instead of using rcsmerge, due to -k handling */
+ run_setup ("%s%s %s %s%s -r%s", Rcsbin, RCS_RCSMERGE, options,
+ baserev ? "-r" : "", baserev ? baserev : "", rev);
+ run_arg (vers->srcfile->path);
+ status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ if (status != 0
+#ifdef HAVE_RCS5
+ && status != 1
+#endif
+ )
+ {
+ error (0, status == -1 ? errno : 0,
+ "could not merge revision %s of %s", rev, user);
+ error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
+ user, backup);
+ rename_file (backup, file);
+ }
+ free (rev);
+ if (baserev)
+ free (baserev);
+ return;
+}
+
+/*
+ * Process the current directory, looking for files not in ILIST and not on
+ * the global ignore list for this directory.
+ */
+static void
+ignore_files (ilist, update_dir)
+ List *ilist;
+ char *update_dir;
+{
+ DIR *dirp;
+ struct direct *dp;
+ struct stat sb;
+ char *file;
+ char *xdir;
+
+ /* we get called with update_dir set to "." sometimes... strip it */
+ if (strcmp (update_dir, ".") == 0)
+ xdir = "";
+ else
+ xdir = update_dir;
+
+ dirp = opendir (".");
+ if (dirp == NULL)
+ return;
+
+ ign_add_file (CVSDOTIGNORE, 1);
+ while ((dp = readdir (dirp)) != NULL)
+ {
+ file = dp->d_name;
+ if (strcmp (file, ".") == 0 || strcmp (file, "..") == 0)
+ continue;
+ if (findnode (ilist, file) != NULL)
+ continue;
+ if (lstat (file, &sb) != -1)
+ {
+ if (S_ISDIR (sb.st_mode))
+ continue;
+#ifdef S_IFLNK
+ if (S_ISLNK (sb.st_mode))
+ continue;
+#endif
+ }
+ if (ign_name (file))
+ continue;
+ (void) write_letter (file, '?', xdir);
+ }
+ (void) closedir (dirp);
+}
diff --git a/gnu/usr.bin/cvs/cvs/vers_ts.c b/gnu/usr.bin/cvs/cvs/vers_ts.c
new file mode 100644
index 0000000..6ac2488
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/vers_ts.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)vers_ts.c 1.36 92/03/31";
+#endif
+
+extern char *ctime (); /* XXX - should use gmtime/asctime */
+
+/*
+ * Fill in and return a Vers_TS structure "user" is the name of the local
+ * file; entries is the entries file - preparsed for our pleasure. xfiles is
+ * all source code control files, preparsed for our pleasure
+ */
+Vers_TS *
+Version_TS (repository, options, tag, date, user, force_tag_match,
+ set_time, entries, xfiles)
+ char *repository;
+ char *options;
+ char *tag;
+ char *date;
+ char *user;
+ int force_tag_match;
+ int set_time;
+ List *entries;
+ List *xfiles;
+{
+ Node *p;
+ RCSNode *rcsdata;
+ Vers_TS *vers_ts;
+ struct stickydirtag *sdtp;
+
+ /* get a new Vers_TS struct */
+ vers_ts = (Vers_TS *) xmalloc (sizeof (Vers_TS));
+ bzero ((char *) vers_ts, sizeof (*vers_ts));
+
+ /*
+ * look up the entries file entry and fill in the version and timestamp
+ * if entries is NULL, there is no entries file so don't bother trying to
+ * look it up (used by checkout -P)
+ */
+ if (entries == NULL)
+ {
+ sdtp = NULL;
+ p = NULL;
+ }
+ else
+ {
+ p = findnode (entries, user);
+ sdtp = (struct stickydirtag *) entries->list->data; /* list-private */
+ }
+
+ if (p != NULL)
+ {
+ Entnode *entdata = (Entnode *) p->data;
+
+ vers_ts->vn_user = xstrdup (entdata->version);
+ vers_ts->ts_rcs = xstrdup (entdata->timestamp);
+ if (!tag)
+ {
+ if (!(sdtp && sdtp->aflag))
+ vers_ts->tag = xstrdup (entdata->tag);
+ }
+ if (!date)
+ {
+ if (!(sdtp && sdtp->aflag))
+ vers_ts->date = xstrdup (entdata->date);
+ }
+ if (!options || (options && *options == '\0'))
+ {
+ if (!(sdtp && sdtp->aflag))
+ vers_ts->options = xstrdup (entdata->options);
+ }
+ vers_ts->entdata = entdata;
+ }
+
+ /*
+ * -k options specified on the command line override (and overwrite)
+ * options stored in the entries file
+ */
+ if (options)
+ vers_ts->options = xstrdup (options);
+ else if (sdtp && sdtp->aflag == 0)
+ {
+ if (!vers_ts->options)
+ vers_ts->options = xstrdup (sdtp->options);
+ }
+ if (!vers_ts->options)
+ vers_ts->options = xstrdup ("");
+
+ /*
+ * if tags were specified on the command line, they override what is in
+ * the Entries file
+ */
+ if (tag || date)
+ {
+ vers_ts->tag = xstrdup (tag);
+ vers_ts->date = xstrdup (date);
+ }
+ else if (!vers_ts->entdata && (sdtp && sdtp->aflag == 0))
+ {
+ if (!vers_ts->tag)
+ vers_ts->tag = xstrdup (sdtp->tag);
+ if (!vers_ts->date)
+ vers_ts->date = xstrdup (sdtp->date);
+ }
+
+ /* Now look up the info on the source controlled file */
+ if (xfiles != (List *) NULL)
+ {
+ p = findnode (xfiles, user);
+ if (p != NULL)
+ {
+ rcsdata = (RCSNode *) p->data;
+ rcsdata->refcount++;
+ }
+ else
+ rcsdata = NULL;
+ }
+ else
+ rcsdata = RCS_parse (user, repository);
+
+ if (rcsdata != NULL)
+ {
+ /* squirrel away the rcsdata pointer for others */
+ vers_ts->srcfile = rcsdata;
+
+ /* get RCS version number into vn_rcs (if appropriate) */
+ if (((vers_ts->tag || vers_ts->date) && force_tag_match) ||
+ ((rcsdata->flags & VALID) && (rcsdata->flags & INATTIC) == 0))
+ {
+ if (vers_ts->tag && strcmp (vers_ts->tag, TAG_BASE) == 0)
+ vers_ts->vn_rcs = xstrdup (vers_ts->vn_user);
+ else
+ vers_ts->vn_rcs = RCS_getversion (rcsdata, vers_ts->tag,
+ vers_ts->date, force_tag_match);
+ }
+
+ /*
+ * If the source control file exists and has the requested revision,
+ * get the Date the revision was checked in. If "user" exists, set
+ * its mtime.
+ */
+ if (set_time)
+ {
+ struct utimbuf t;
+
+ if (vers_ts->vn_rcs &&
+ (t.actime = t.modtime = RCS_getrevtime (rcsdata, vers_ts->vn_rcs,
+ (char *) 0, 0)) != -1)
+ (void) utime (user, &t);
+ }
+ }
+
+ /* get user file time-stamp in ts_user */
+ if (entries != (List *) NULL)
+ vers_ts->ts_user = time_stamp (user);
+
+ return (vers_ts);
+}
+
+/*
+ * Gets the time-stamp for the file "file" and returns it in space it
+ * allocates
+ */
+char *
+time_stamp (file)
+ char *file;
+{
+ struct stat sb;
+ char *cp;
+ char *ts;
+
+ if (stat (file, &sb) < 0)
+ {
+ ts = NULL;
+ }
+ else
+ {
+ ts = xmalloc (51); /* 51 = 2 ctime strings + NULL */
+ cp = ctime (&sb.st_ctime); /* copy in the create time */
+ cp[24] = ' ';
+ (void) strcpy (ts, cp);
+ cp = ctime (&sb.st_mtime); /* copy in the modify time */
+ cp[24] = '\0';
+ (void) strcat (ts, cp);
+ }
+
+ return (ts);
+}
+
+/*
+ * free up a Vers_TS struct
+ */
+void
+freevers_ts (versp)
+ Vers_TS **versp;
+{
+ if ((*versp)->srcfile)
+ freercsnode (&((*versp)->srcfile));
+ if ((*versp)->vn_user)
+ free ((*versp)->vn_user);
+ if ((*versp)->vn_rcs)
+ free ((*versp)->vn_rcs);
+ if ((*versp)->ts_user)
+ free ((*versp)->ts_user);
+ if ((*versp)->ts_rcs)
+ free ((*versp)->ts_rcs);
+ if ((*versp)->options)
+ free ((*versp)->options);
+ if ((*versp)->tag)
+ free ((*versp)->tag);
+ if ((*versp)->date)
+ free ((*versp)->date);
+ free ((char *) *versp);
+ *versp = (Vers_TS *) NULL;
+}
diff --git a/gnu/usr.bin/cvs/cvs/version.c b/gnu/usr.bin/cvs/cvs/version.c
new file mode 100644
index 0000000..18a9d14
--- /dev/null
+++ b/gnu/usr.bin/cvs/cvs/version.c
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * version.c - the CVS version number
+ */
+
+char *version_string = "\nConcurrent Versions System (CVS) 1.3\n";
diff --git a/gnu/usr.bin/cvs/doc/cvs.ms b/gnu/usr.bin/cvs/doc/cvs.ms
new file mode 100644
index 0000000..567179b
--- /dev/null
+++ b/gnu/usr.bin/cvs/doc/cvs.ms
@@ -0,0 +1,1073 @@
+.\" soelim cvs.ms | pic | tbl | troff -ms
+.\" @(#)cvs.ms 1.2 92/01/30
+.\"
+.\" troff source to the cvs USENIX article, Winter 1990, Washington, D.C.
+.\" Copyright (c) 1989, Brian Berliner
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 1, or (at your option)
+.\" any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\" The author can be reached at: berliner@prisma.com
+.\"
+.de SP
+.if n .sp
+.if t .sp .5
+..
+.de hl
+.br
+.in +0.5i
+\l'\\n(LLu-1i'
+.in -0.5i
+.sp
+..
+.OH ""
+.nr PS 11
+.nr PO 1.25i
+.pl -0.2i
+.TL
+.ps 14
+.ft B
+.nf
+CVS II:
+Parallelizing Software Development
+.fi
+.ft
+.ps
+.AU
+.ps 12
+.ft I
+Brian Berliner
+.ft
+.ps
+.AI
+.ps 12
+.ft I
+Prisma, Inc.
+5465 Mark Dabling Blvd.
+Colorado Springs, CO 80918
+berliner@prisma.com
+.ft
+.ps
+.AB
+The program described in this paper fills a need in the UNIX
+community for a freely available tool to manage software revision and
+release control in a multi-developer, multi-directory, multi-group
+environment.
+This tool also addresses the increasing need for tracking third-party vendor
+source distributions while trying to maintain local modifications to
+earlier releases.
+.AE
+.NH
+Background
+.PP
+In large software development projects, it is usually necessary for more
+than one software developer to be modifying (usually different) modules of the
+code at the same time.
+Some of these code modifications are done in an
+experimental sense, at least until the code functions correctly, and some
+testing of the entire program is usually necessary.
+Then, the modifications are returned to a master source repository
+so that others in the project can
+enjoy the new bug-fix or functionality.
+In order to manage such a project, some sort of revision control system is
+necessary.
+.PP
+Specifically, UNIX\**
+.FS
+UNIX is a registered trademark of AT&T.
+.FE
+kernel development is an excellent example of the
+problems that an adequate revision control system must address.
+The SunOS\**
+.FS
+SunOS is a trademark of Sun Microsystems, Inc.
+.FE
+kernel is composed of over a thousand files spread across a
+hierarchy of dozens of directories.\**
+.FS
+Yes, the SunOS 4.0 kernel is composed of over a \fIthousand\fP files!
+.FE
+Pieces of the kernel must be edited
+by many software developers within an organization.
+While undesirable in
+theory, it is not uncommon to have two or more people making
+modifications to the same file within the kernel sources in
+order to facilitate a desired change.
+Existing revision control systems like
+.SM
+RCS
+.LG
+[Tichy] or
+.SM
+SCCS
+.LG
+[Bell] serialize file modifications by
+allowing only one developer to have a writable copy of a particular file at
+any one point in time.
+That developer is said to
+have \*Qlocked\*U the file for his exclusive use, and no other developer is
+allowed to check out a writable copy of the file until the locking
+developer has finished impeding others' productivity.
+Development pressures of productivity and deadlines
+often force organizations to require that multiple developers be able to
+simultaneously edit
+copies of the same revision controlled file.
+.PP
+The necessity for multiple developers to modify the same file concurrently
+questions the value of serialization-based policies in traditional revision
+control.
+This paper discusses the approach that
+Prisma took in adapting a standard revision control system,
+.SM
+RCS\c
+.LG
+, along with an existing public-domain collection of shell scripts that sits
+atop
+.SM
+RCS
+.LG
+and provides the basic conflict-resolution algorithms.
+The resulting
+program, \fBcvs\fP, addresses not only the issue of conflict-resolution in
+a multi-developer open-editing environment, but also the issues of
+software release control and vendor source support and integration.
+.NH
+The CVS Program
+.PP
+\fBcvs\fP
+(Concurrent Versions System)
+is a front end to the
+.SM
+RCS
+.LG
+revision control system which extends
+the notion of revision control from a collection of files in a single
+directory to a hierarchical collection of directories each containing
+revision controlled files.
+Directories and files in the \fBcvs\fP system can be combined together in
+many ways to form a software release.
+\fBcvs\fP
+provides the functions necessary to manage these software releases and to
+control the concurrent editing of source files among multiple software
+developers.
+.PP
+The six major features of \fBcvs\fP are listed below, and will be
+described in more detail in the following sections:
+.RS
+.IP 1.
+Concurrent access and conflict-resolution algorithms to guarantee that
+source changes are not \*Qlost.\*U
+.IP 2.
+Support for tracking third-party vendor source distributions while
+maintaining the local modifications made to those sources.
+.IP 3.
+A flexible module database that provides a symbolic mapping of names to
+components of a larger software distribution.
+This symbolic mapping provides for location independence within the software
+release and, for example, allows one to check out a copy of the \*Qdiff\*U
+program without ever knowing that the sources to \*Qdiff\*U actually reside
+in the \*Qbin/diff\*U directory.
+.IP 4.
+Configurable logging support allows all \*Qcommitted\*U source file changes
+to be logged using an arbitrary program to save the log messages in a file,
+notesfile, or news database.
+.IP 5.
+A software release can be symbolically tagged and checked out at any time
+based on that tag.
+An exact copy of a previous software release can be checked out at
+any time, \fIregardless\fP of whether files or directories have been
+added/removed from the \*Qcurrent\*U software release.
+As well,
+a \*Qdate\*U can be used to check out the \fIexact\fP version of the software
+release as of the specified date.
+.IP 6.
+A \*Qpatch\*U format file [Wall] can be produced between two software
+releases, even if the releases span multiple directories.
+.RE
+.PP
+The sources maintained by \fBcvs\fP are kept within a single directory
+hierarchy known as the \*Qsource repository.\*U
+This \*Qsource repository\*U holds the actual
+.SM
+RCS
+.LG
+\*Q,v\*U files directly, as well as a special per-repository directory
+(\c
+.SM
+CVSROOT.adm\c
+.LG
+) which contains a small number of administrative files that describe the
+repository and how it can be accessed.
+See Figure 1 for a picture of the \fBcvs\fP tree.
+.KF
+.hl
+.DS B
+.PS
+line from 4.112,9.200 to 5.550,8.887
+line from 5.447,8.884 to 5.550,8.887 to 5.458,8.933
+line from 4.112,9.200 to 4.550,8.950
+line from 4.451,8.978 to 4.550,8.950 to 4.476,9.021
+line from 4.112,9.200 to 3.737,8.887
+line from 3.798,8.971 to 3.737,8.887 to 3.830,8.932
+line from 3.612,8.762 to 4.737,8.137
+line from 4.638,8.164 to 4.737,8.137 to 4.662,8.208
+line from 3.612,8.762 to 3.737,8.137
+line from 3.693,8.231 to 3.737,8.137 to 3.742,8.240
+line from 3.612,8.762 to 2.612,8.200
+line from 2.687,8.271 to 2.612,8.200 to 2.712,8.227
+line from 2.362,9.262 to 2.737,8.950
+line from 2.645,8.995 to 2.737,8.950 to 2.677,9.033
+line from 2.362,9.262 to 1.925,8.950
+line from 1.992,9.028 to 1.925,8.950 to 2.021,8.988
+line from 3.362,9.762 to 4.050,9.387
+line from 3.950,9.413 to 4.050,9.387 to 3.974,9.457
+line from 3.362,9.762 to 2.487,9.387
+line from 2.570,9.450 to 2.487,9.387 to 2.589,9.404
+.ps 11
+"newfs.c,v" at 4.487,8.043 ljust
+.ps 11
+"mkfs.c,v" at 3.487,8.043 ljust
+.ps 11
+"Makefile,v" at 2.237,8.043 ljust
+.ps 11
+"newfs" at 3.487,8.793 ljust
+.ps 11
+"halt.c,v" at 5.487,8.793 ljust
+.ps 11
+"Makefile,v" at 4.237,8.793 ljust
+.ps 11
+"modules,v" at 2.487,8.793 ljust
+.ps 11
+"loginfo,v" at 1.488,8.793 ljust
+.ps 11
+"etc" at 3.987,9.293 ljust
+.ps 11
+"CVSROOT.adm" at 1.988,9.293 ljust
+.ps 11
+"/src/master" at 2.987,9.793 ljust
+.PE
+.DE
+.hl
+.ce 100
+.LG
+\fBFigure 1.\fP
+.SM
+\fBcvs\fP Source Repository
+.ce 0
+.sp
+.KE
+.NH 2
+Software Conflict Resolution\**
+.FS
+The basic conflict-resolution algorithms
+used in the \fBcvs\fP program find their roots
+in the original work done by Dick Grune at Vrije Universiteit in Amsterdam
+and posted to \fBcomp.sources.unix\fP in the volume 6 release sometime in 1986.
+This original version of \fBcvs\fP was a collection of shell scripts that
+combined to form a front end to the
+.SM
+RCS
+.LG
+programs.
+.FE
+.PP
+\fBcvs\fP allows several software developers to edit personal copies of a
+revision controlled file concurrently.
+The revision number of each checked out file is maintained independently
+for each user, and \fBcvs\fP forces the checked out file to be current with
+the \*Qhead\*U revision before it can be \*Qcommitted\*U as a permanent change.
+A checked out file is brought up-to-date with the \*Qhead\*U revision using
+the \*Qupdate\*U command of \fBcvs\fP.
+This command compares the \*Qhead\*U revision number with that of the user's
+file and performs an
+.SM
+RCS
+.LG
+merge operation if they are not the same.
+The result of the merge is a file that contains the user's modifications
+and those modifications that were \*Qcommitted\*U after the user
+checked out his version of the file (as well as a backup copy of the
+user's original file).
+\fBcvs\fP points out any conflicts during the merge.
+It is the user's responsibility to resolve these conflicts
+and to \*Qcommit\*U his/her changes when ready.
+.PP
+Although the \fBcvs\fP conflict-resolution algorithm was defined in 1986,
+it is remarkably similar to the \*QCopy-Modify-Merge\*U scenario included
+with NSE\**
+.FS
+NSE is the Network Software Environment, a product of Sun Microsystems, Inc.
+.FE
+and described in [Honda] and [Courington].
+The following explanation from [Honda] also applies to \fBcvs\fP:
+.QP
+Simply stated, a developer copies an object without locking it, modifies
+the copy, and then merges the modified copy with the original.
+This paradigm allows developers to work in isolation from one another since
+changes are made to copies of objects.
+Because locks are not used, development is not serialized and can proceed
+in parallel.
+Developers, however, must merge objects after the changes have been made.
+In particular, a developer must resolve conflicts when the same object has
+been modified by someone else.
+.PP
+In practice, Prisma has found that conflicts that occur when the same
+object has been modified by someone else are quite rare.
+When they do happen, the changes made by the other developer are usually
+easily resolved.
+This practical use has shown that the \*QCopy-Modify-Merge\*U paradigm is a
+correct and useful one.
+.NH 2
+Tracking Third-Party Source Distributions
+.PP
+Currently, a large amount of software is based on source
+distributions from a third-party distributor.
+It is often the case that local modifications are to be made to this
+distribution, \fIand\fP that the vendor's future releases should be
+tracked.
+Rolling your local modifications forward into the new vendor release is a
+time-consuming task, but \fBcvs\fP can ease this burden somewhat.
+The \fBcheckin\fP program of \fBcvs\fP initially sets up a source
+repository by integrating the source modules directly from the vendor's
+release, preserving the directory hierarchy of the vendor's distribution.
+The branch support of
+.SM
+RCS
+.LG
+is used to build this vendor release as a branch of the main
+.SM
+RCS
+.LG
+trunk.
+Figure 2 shows how the \*Qhead\*U tracks a sample vendor
+branch when no local modifications have been made to the file.
+.KF
+.hl
+.DS B
+.PS
+ellipse at 3.237,6.763 wid 1.000 ht 0.500
+dashwid = 0.050i
+line dashed from 3.237,7.513 to 3.737,7.513 to 3.737,9.762 to 4.237,9.762
+line from 4.138,9.737 to 4.237,9.762 to 4.138,9.787
+line dashed from 2.237,8.262 to 3.237,8.262 to 3.237,7.013
+line from 3.212,7.112 to 3.237,7.013 to 3.262,7.112
+line from 3.737,6.763 to 4.237,6.763
+line from 4.138,6.737 to 4.237,6.763 to 4.138,6.788
+line from 2.237,6.763 to 2.737,6.763
+line from 2.637,6.737 to 2.737,6.763 to 2.637,6.788
+line from 1.738,6.013 to 1.738,6.513
+line from 1.762,6.413 to 1.738,6.513 to 1.713,6.413
+line from 1.238,7.013 to 2.237,7.013 to 2.237,6.513 to 1.238,6.513 to 1.238,7.013
+line from 4.237,9.012 to 5.237,9.012 to 5.237,8.512 to 4.237,8.512 to 4.237,9.012
+line from 4.237,8.012 to 5.237,8.012 to 5.237,7.513 to 4.237,7.513 to 4.237,8.012
+line from 4.237,7.013 to 5.237,7.013 to 5.237,6.513 to 4.237,6.513 to 4.237,7.013
+line from 4.737,7.013 to 4.737,7.513
+line from 4.763,7.413 to 4.737,7.513 to 4.712,7.413
+line from 4.737,8.012 to 4.737,8.512
+line from 4.763,8.412 to 4.737,8.512 to 4.712,8.412
+line from 4.237,10.012 to 5.237,10.012 to 5.237,9.512 to 4.237,9.512 to 4.237,10.012
+line from 4.737,9.012 to 4.737,9.512
+line from 4.763,9.412 to 4.737,9.512 to 4.712,9.412
+line from 5.987,5.013 to 5.987,6.013 to 0.988,6.013 to 0.988,5.013 to 5.987,5.013
+.ps 11
+"\"HEAD\"" at 1.550,8.231 ljust
+.ps 11
+"'SunOS'" at 2.987,6.293 ljust
+.ps 11
+"1.1.1" at 3.050,6.793 ljust
+.ps 11
+"1.1" at 1.613,6.793 ljust
+.ps 11
+"1.1.1.1" at 4.487,6.793 ljust
+.ps 11
+"1.1.1.2" at 4.487,7.793 ljust
+.ps 11
+"1.1.1.3" at 4.487,8.793 ljust
+.ps 11
+"1.1.1.4" at 4.487,9.793 ljust
+.ps 11
+"'SunOS_4_0'" at 5.487,6.793 ljust
+.ps 11
+"'SunOS_4_0_1'" at 5.487,7.793 ljust
+.ps 11
+"'YAPT_5_5C'" at 5.487,8.793 ljust
+.ps 11
+"'SunOS_4_0_3'" at 5.487,9.793 ljust
+.ps 11
+"rcsfile.c,v" at 2.987,5.543 ljust
+.PE
+.DE
+.hl
+.ce 100
+.LG
+\fBFigure 2.\fP
+.SM
+\fBcvs\fP Vendor Branch Example
+.ce 0
+.sp .3
+.KE
+Once this is done, developers can check out files and make local changes to
+the vendor's source distribution.
+These local changes form a new branch to the tree which is then used as the
+source for future check outs.
+Figure 3 shows how the \*Qhead\*U moves to the main
+.SM
+RCS
+.LG
+trunk when a local modification is made.
+.KF
+.hl
+.DS B
+.PS
+ellipse at 3.237,6.763 wid 1.000 ht 0.500
+dashwid = 0.050i
+line dashed from 2.800,9.075 to 1.738,9.075 to 1.738,8.012
+line from 1.713,8.112 to 1.738,8.012 to 1.762,8.112
+line from 1.738,7.013 to 1.738,7.513
+line from 1.762,7.413 to 1.738,7.513 to 1.713,7.413
+line from 1.238,8.012 to 2.237,8.012 to 2.237,7.513 to 1.238,7.513 to 1.238,8.012
+line from 3.737,6.763 to 4.237,6.763
+line from 4.138,6.737 to 4.237,6.763 to 4.138,6.788
+line from 2.237,6.763 to 2.737,6.763
+line from 2.637,6.737 to 2.737,6.763 to 2.637,6.788
+line from 1.738,6.013 to 1.738,6.513
+line from 1.762,6.413 to 1.738,6.513 to 1.713,6.413
+line from 1.238,7.013 to 2.237,7.013 to 2.237,6.513 to 1.238,6.513 to 1.238,7.013
+line from 4.237,9.012 to 5.237,9.012 to 5.237,8.512 to 4.237,8.512 to 4.237,9.012
+line from 4.237,8.012 to 5.237,8.012 to 5.237,7.513 to 4.237,7.513 to 4.237,8.012
+line from 4.237,7.013 to 5.237,7.013 to 5.237,6.513 to 4.237,6.513 to 4.237,7.013
+line from 4.737,7.013 to 4.737,7.513
+line from 4.763,7.413 to 4.737,7.513 to 4.712,7.413
+line from 4.737,8.012 to 4.737,8.512
+line from 4.763,8.412 to 4.737,8.512 to 4.712,8.412
+line from 4.237,10.012 to 5.237,10.012 to 5.237,9.512 to 4.237,9.512 to 4.237,10.012
+line from 4.737,9.012 to 4.737,9.512
+line from 4.763,9.412 to 4.737,9.512 to 4.712,9.412
+line from 5.987,5.013 to 5.987,6.013 to 0.988,6.013 to 0.988,5.013 to 5.987,5.013
+.ps 11
+"1.2" at 1.613,7.793 ljust
+.ps 11
+"\"HEAD\"" at 2.862,9.043 ljust
+.ps 11
+"'SunOS'" at 2.987,6.293 ljust
+.ps 11
+"1.1.1" at 3.050,6.793 ljust
+.ps 11
+"1.1" at 1.613,6.793 ljust
+.ps 11
+"1.1.1.1" at 4.487,6.793 ljust
+.ps 11
+"1.1.1.2" at 4.487,7.793 ljust
+.ps 11
+"1.1.1.3" at 4.487,8.793 ljust
+.ps 11
+"1.1.1.4" at 4.487,9.793 ljust
+.ps 11
+"'SunOS_4_0'" at 5.487,6.793 ljust
+.ps 11
+"'SunOS_4_0_1'" at 5.487,7.793 ljust
+.ps 11
+"'YAPT_5_5C'" at 5.487,8.793 ljust
+.ps 11
+"'SunOS_4_0_3'" at 5.487,9.793 ljust
+.ps 11
+"rcsfile.c,v" at 2.987,5.543 ljust
+.PE
+.DE
+.hl
+.ce 100
+.LG
+\fBFigure 3.\fP
+.SM
+\fBcvs\fP Local Modification to Vendor Branch
+.ce 0
+.sp
+.KE
+.PP
+When a new version of the vendor's source distribution arrives, the
+\fBcheckin\fP program adds the new and changed vendor's files to the
+already existing source repository.
+For files that have not been changed locally, the new file from the
+vendor becomes the current \*Qhead\*U revision.
+For files that have been modified locally, \fBcheckin\fP warns that the
+file must be merged with the new vendor release.
+The \fBcvs\fP \*Qjoin\*U command is a useful tool that aids this process by
+performing the necessary
+.SM
+RCS
+.LG
+merge, as is done above when performing an \*Qupdate.\*U
+.PP
+There is also limited support for \*Qdual\*U derivations for source files.
+See Figure 4 for a sample dual-derived file.
+.KF
+.hl
+.DS B
+.PS
+ellipse at 2.337,8.575 wid 0.700 ht 0.375
+ellipse at 2.312,9.137 wid 0.700 ht 0.375
+line from 1.225,9.012 to 1.225,9.363
+line from 1.250,9.263 to 1.225,9.363 to 1.200,9.263
+line from 0.875,9.725 to 1.600,9.725 to 1.600,9.363 to 0.875,9.363 to 0.875,9.725
+line from 0.875,9.012 to 1.600,9.012 to 1.600,8.650 to 0.875,8.650 to 0.875,9.012
+line from 4.050,10.200 to 4.775,10.200 to 4.775,9.850 to 4.050,9.850 to 4.050,10.200
+line from 4.050,9.475 to 4.775,9.475 to 4.775,9.113 to 4.050,9.113 to 4.050,9.475
+line from 4.050,8.762 to 4.775,8.762 to 4.775,8.400 to 4.050,8.400 to 4.050,8.762
+line from 4.425,8.762 to 4.425,9.113
+line from 4.450,9.013 to 4.425,9.113 to 4.400,9.013
+line from 4.425,9.475 to 4.425,9.850
+line from 4.450,9.750 to 4.425,9.850 to 4.400,9.750
+line from 3.050,10.000 to 3.775,10.000 to 3.775,9.637 to 3.050,9.637 to 3.050,10.000
+line from 3.050,9.312 to 3.775,9.312 to 3.775,8.950 to 3.050,8.950 to 3.050,9.312
+line from 0.713,7.325 to 0.713,8.075 to 4.925,8.075 to 4.925,7.325 to 0.713,7.325
+line from 1.238,8.075 to 1.238,8.637
+line from 1.262,8.537 to 1.238,8.637 to 1.213,8.537
+line from 1.613,8.825 to 1.975,8.575
+line from 1.878,8.611 to 1.975,8.575 to 1.907,8.652
+line from 2.675,8.575 to 4.050,8.575
+line from 3.950,8.550 to 4.050,8.575 to 3.950,8.600
+line from 2.675,9.137 to 3.050,9.137
+line from 2.950,9.112 to 3.050,9.137 to 2.950,9.162
+line from 3.425,9.325 to 3.425,9.637
+line from 3.450,9.537 to 3.425,9.637 to 3.400,9.537
+line from 1.613,8.825 to 1.925,9.137
+line from 1.872,9.049 to 1.925,9.137 to 1.837,9.084
+.ps 11
+"'BSD'" at 2.138,9.481 ljust
+.ps 11
+"1.2" at 1.113,9.543 ljust
+.ps 11
+"1.1" at 1.125,8.831 ljust
+.ps 11
+"1.1.1.1" at 4.175,8.543 ljust
+.ps 11
+"1.1.1.2" at 4.175,9.281 ljust
+.ps 11
+"1.1.1.3" at 4.175,9.993 ljust
+.ps 11
+"1.1.2.2" at 3.175,9.793 ljust
+.ps 11
+"1.1.2.1" at 3.175,9.106 ljust
+.ps 11
+"rcsfile.c,v" at 2.425,7.706 ljust
+.ps 11
+"1.1.1" at 2.175,8.568 ljust
+.ps 11
+"'SunOS'" at 2.125,8.243 ljust
+.ps 11
+"1.1.2" at 2.163,9.131 ljust
+.PE
+.DE
+.hl
+.ce 100
+.LG
+\fBFigure 4.\fP
+.SM
+\fBcvs\fP Support For \*QDual\*U Derivations
+.ce 0
+.sp
+.KE
+This example tracks the SunOS distribution but includes major changes from
+Berkeley.
+These BSD files are saved directly in the
+.SM
+RCS
+.LG
+file off a new branch.
+.NH 2
+Location Independent Module Database
+.PP
+\fBcvs\fP contains support for a simple, yet powerful, \*Qmodule\*U database.
+For reasons of efficiency, this database is stored in \fBndbm\fP\|(3) format.
+The module database is used to apply names to collections of directories
+and files as a matter of convenience for checking out pieces of a large
+software distribution.
+The database records the physical location of the sources as a form of
+information hiding, allowing one to check out whole directory hierarchies
+or individual files without regard for their actual location within the
+global source distribution.
+.PP
+Consider the following small sample of a module database, which must be
+tailored manually to each specific source repository environment:
+.DS
+\f(CW #key [-option argument] directory [files...]
+ diff bin/diff
+ libc lib/libc
+ sys -o sys/tools/make_links sys
+ modules -i mkmodules CVSROOT.adm modules
+ kernel -a sys lang/adb
+ ps bin Makefile ps.c\fP
+.DE
+.PP
+The \*Qdiff\*U and \*Qlibc\*U modules refer to whole directory hierarchies that
+are extracted on check out.
+The \*Qsys\*U module extracts the \*Qsys\*U hierarchy, and runs the
+\*Qmake_links\*U program at the end of the check out process (the \fI-o\fP
+option specifies a program to run on check\fIo\fPut).
+The \*Qmodules\*U module allows one to edit the module database file and
+runs the \*Qmkmodules\*U program on check\fIi\fPn to regenerate the
+\fBndbm\fP database that \fBcvs\fP uses.
+The \*Qkernel\*U module is an alias (as the \fI-a\fP option specifies)
+which causes the remaining arguments after the \fI-a\fP to be interpreted
+exactly as if they had been specified on the command line.
+This is useful for objects that require shared pieces of code from far away
+places to be compiled (as is the case with the kernel debugger, \fBkadb\fP,
+which shares code with the standard \fBadb\fP debugger).
+The \*Qps\*U module shows that the source for \*Qps\*U lives in the \*Qbin\*U
+directory, but only \fIMakefile\fP and \fIps.c\fP are required to build the
+object.
+.PP
+The module database at Prisma is now populated for the entire UNIX
+distribution and thereby allows us to issue the
+following convenient commands to check out components of the UNIX
+distribution without regard for their actual location within the master source
+repository:
+.DS
+\f(CW example% cvs checkout diff
+ example% cvs checkout libc ps
+ example% cd diff; make\fP
+.DE
+.PP
+In building the module database file, it is quite possible to have name
+conflicts within a global software distribution.
+For example, SunOS provides two \fBcat\fP programs:
+one for the standard environment, \fI/bin/cat\fP, and one for the System V
+environment, \fI/usr/5bin/cat\fP.
+We resolved this conflict by naming the standard \fBcat\fP module
+\*Qcat\*U, and the System V \fBcat\fP module \*Q5cat\*U.
+Similar name modifications must be applied to other conflicting names, as
+might be found between a utility program and a library function, though
+Prisma chose not to include individual library functions within the module
+database at this time.
+.NH 2
+Configurable Logging Support
+.PP
+The \fBcvs\fP \*Qcommit\*U command is used to make a permanent change to the
+master source repository (where the
+.SM
+RCS
+.LG
+\*Q,v\*U files live).
+Whenever a \*Qcommit\*U is done, the log message for the change is carefully
+logged by an arbitrary program (in a file, notesfile, news database, or
+mail).
+For example, a collection of these updates can be used to produce release
+notices.
+\fBcvs\fP can be configured to send log updates through one or more filter
+programs, based on a regular expression match on the directory that is
+being changed.
+This allows multiple related or unrelated projects to exist within a single
+\fBcvs\fP source repository tree, with each different project sending its
+\*Qcommit\*U reports to a unique log device.
+.PP
+A sample logging configuration file might look as follows:
+.DS
+\f(CW #regex filter-program
+ DEFAULT /usr/local/bin/nfpipe -t %s utils.updates
+ ^diag /usr/local/bin/nfpipe -t %s diag.updates
+ ^local /usr/local/bin/nfpipe -t %s local.updates
+ ^perf /usr/local/bin/nfpipe -t %s perf.updates
+ ^sys /usr/local/bin/nfpipe -t %s kernel.updates\fP
+.DE
+.PP
+This sample allows the diagnostics and performance groups to
+share the same source repository with the kernel and utilities groups.
+Changes that they make are sent directly to their own notesfile [Essick]
+through the \*Qnfpipe\*U program.
+A sufficiently simple title is substituted for the \*Q%s\*U argument before
+the filter program is executed.
+This logging configuration file is tailored manually to each specific
+source repository environment.
+.NH 2
+Tagged Releases and Dates
+.PP
+Any release can be given a symbolic tag name that is stored directly in the
+.SM
+RCS
+.LG
+files.
+This tag can be used at any time to get an exact copy of any previous
+release.
+With equal ease, one can also extract an exact copy of the source files as
+of any arbitrary date in the past as well.
+Thus, all that's required to tag the current kernel, and to tag the kernel
+as of the Fourth of July is:
+.DS
+\f(CW example% cvs tag TEST_KERNEL kernel
+ example% cvs tag -D 'July 4' PATRIOTIC_KERNEL kernel\fP
+.DE
+The following command would retrieve an exact copy of the test kernel at
+some later date:
+.DS
+\f(CW example% cvs checkout -fp -rTEST_KERNEL kernel\fP
+.DE
+The \fI-f\fP option causes only files that match the specified tag to be
+extracted, while the \fI-p\fP option automatically prunes empty directories.
+Consequently, directories added to the kernel after the test kernel was
+tagged are not included in the newly extracted copy of the test kernel.
+.PP
+The \fBcvs\fP date support has exactly the same interface as that provided
+with
+.SM
+RCS\c
+.LG
+, however \fBcvs\fP must process the \*Q,v\*U files directly due to the
+special handling required by the vendor branch support.
+The standard
+.SM
+RCS
+.LG
+date handling only processes one branch (or the main trunk) when checking
+out based on a date specification.
+\fBcvs\fP must instead process the current \*Qhead\*U branch and, if a
+match is not found, proceed to look for a match on the vendor branch.
+This, combined with reasons of performance, is why \fBcvs\fP processes
+revision (symbolic and numeric) and date specifications directly from the
+\*Q,v\*U files.
+.NH 2
+Building \*Qpatch\*U Source Distributions
+.PP
+\fBcvs\fP can produce a \*Qpatch\*U format [Wall] output file which can be
+used to bring a previously released software distribution current with the
+newest release.
+This patch file supports an entire directory hierarchy within a single
+patch, as well as being able to add whole new files to the previous
+release.
+One can combine symbolic revisions and dates together to display changes in
+a very generic way:
+.DS
+\f(CW example% cvs patch -D 'December 1, 1988' \e
+ -D 'January 1, 1989' sys\fP
+.DE
+This example displays the kernel changes made in the month of December,
+1988.
+To release a patch file, for example, to take the \fBcvs\fP distribution
+from version 1.0 to version 1.4 might be done as follows:
+.DS
+\f(CW example% cvs patch -rCVS_1_0 -rCVS_1_4 cvs\fP
+.DE
+.NH
+CVS Experience
+.NH 2
+Statistics
+.PP
+A quick summary of the scale that \fBcvs\fP is addressing today
+can be found in Table 1.
+.KF
+.TS
+box center tab(:);
+c s
+c s
+c | c
+l | n .
+\fB\s+2Revision Control Statistics at Prisma
+as of 11/11/89\fP\s-2
+_
+How Many...:Total
+=
+Files:17243
+Directories:1005
+Lines of code:3927255
+Removed files:131
+Software developers:14
+Software groups:6
+Megabytes of source:128
+.TE
+.ce 100
+.LG
+\fBTable 1.\fP
+.SM
+\fBcvs\fP Statistics
+.ce 0
+.sp .3
+.KE
+Table 2 shows the history of files changed or added and the number
+of source lines affected by the change at Prisma.
+Only changes made to the kernel sources are included.
+.KF
+.TS
+box center tab(:);
+c s s s s
+c s s s s
+c || c | c || c | c
+c || c | c || c | c
+l || n | n || n | n.
+\fB\s+2Prisma Kernel Source File Changes
+By Month, 1988-1989\fP\s-2
+_
+Month:# Changed:# Lines:# Added:# Lines
+\^:Files:Changed:Files:Added
+=
+Dec:87:3619:68:9266
+Jan:39:4324:0:0
+Feb:73:1578:5:3550
+Mar:99:5301:18:11461
+Apr:112:7333:11:5759
+May:138:5371:17:13986
+Jun:65:2261:27:12875
+Jul:34:2000:1:58
+Aug:65:6378:8:4724
+Sep:266:23410:113:39965
+Oct:22:621:1:155
+Total:1000:62196:269:101799
+.TE
+.ce 100
+.LG
+\fBTable 2.\fP
+.SM
+\fBcvs\fP Usage History for the Kernel
+.ce 0
+.sp
+.KE
+The large number of source file changes made in September are the result of
+merging the SunOS 4.0.3 sources into the kernel.
+This merge process is described in section 3.3.
+.NH 2
+Performance
+.PP
+The performance of \fBcvs\fP is currently quite reasonable.
+Little effort has been expended on tuning \fBcvs\fP, although performance
+related decisions were made during the \fBcvs\fP design.
+For example, \fBcvs\fP parses the
+.SM
+RCS
+.LG
+\*Q,v\*U files directly instead of running an
+.SM
+RCS
+.LG
+process.
+This includes following branches as well as integrating with the vendor
+source branches and the main trunk when checking out files based on a date.
+.PP
+Checking out the entire kernel source tree (1223 files/59 directories)
+currently takes 16 wall clock minutes on a Sun-4/280.
+However, bringing the tree up-to-date with the current kernel sources, once
+it has been checked out, takes only 1.5 wall clock minutes.
+Updating the \fIcomplete\fP 128 MByte source tree under \fBcvs\fP control
+(17243 files/1005 directories) takes roughly 28 wall clock minutes and
+utilizes one-third of the machine.
+For now this is entirely acceptable; improvements on these numbers will
+possibly be made in the future.
+.NH 2
+The SunOS 4.0.3 Merge
+.PP
+The true test of the \fBcvs\fP vendor branch support came with the arrival
+of the SunOS 4.0.3 source upgrade tape.
+As described above, the \fBcheckin\fP program was used to install the new
+sources and the resulting output file listed the files that had been
+locally modified, needing to be merged manually.
+For the kernel, there were 94 files in conflict.
+The \fBcvs\fP \*Qjoin\*U command was used on each of the 94 conflicting
+files, and the remaining conflicts were resolved.
+.PP
+The \*Qjoin\*U command performs an \fBrcsmerge\fP operation.
+This in turn uses \fI/usr/lib/diff3\fP to produce a three-way diff file.
+As it happens, the \fBdiff3\fP program has a hard-coded limit of 200
+source-file changes maximum.
+This proved to be too small for a few of the kernel files that needed
+merging by hand, due to the large number of local changes that Prisma had
+made.
+The \fBdiff3\fP problem was solved by increasing the hard-coded limit by an
+order of magnitude.
+.PP
+The SunOS 4.0.3 kernel source upgrade distribution contained
+346 files, 233 of which were modifications to previously released files,
+and 113 of which were newly added files.
+\fBcheckin\fP added the 113 new files to the source repository
+without intervention.
+Of the 233 modified files, 139 dropped in cleanly by \fBcheckin\fP, since
+Prisma had not made any local changes to them, and 94 required manual
+merging due to local modifications.
+The 233 modified files consisted of 20,766 lines of differences.
+It took one developer two days to manually merge the 94 files using the
+\*Qjoin\*U command and resolving conflicts manually.
+An additional day was required for kernel debugging.
+The entire process of merging over 20,000 lines of differences was
+completed in less than a week.
+This one time-savings alone was justification enough for the \fBcvs\fP
+development effort; we expect to gain even more when tracking future SunOS
+releases.
+.NH
+Future Enhancements and Current Bugs
+.PP
+Since \fBcvs\fP was designed to be incomplete, for reasons of design
+simplicity, there are naturally a good
+number of enhancements that can be made to make it more useful.
+As well, some nuisances exist in the current implementation.
+.RS
+.IP \(bu 3
+\fBcvs\fP does not currently \*Qremember\*U who has a checked out a copy of a
+module.
+As a result, it is impossible to know who might be working on the same
+module that you are.
+A simple-minded database that is updated nightly would likely suffice.
+.IP \(bu 3
+Signal processing, keyboard interrupt handling in particular, is currently
+somewhat weak.
+This is due to the heavy use of the \fBsystem\fP\|(3) library
+function to execute
+.SM
+RCS
+.LG
+programs like \fBco\fP and \fBci\fP.
+It sometimes takes multiple interrupts to make \fBcvs\fP quit.
+This can be fixed by using a home-grown \fBsystem\fP\|() replacement.
+.IP \(bu 3
+Security of the source repository is currently not dealt with directly.
+The usual UNIX approach of user-group-other security permissions through
+the file system is utilized, but nothing else.
+\fBcvs\fP could likely be a set-group-id executable that checks a
+protected database to verify user access permissions for particular objects
+before allowing any operations to affect those objects.
+.IP \(bu 3
+With every checked-out directory, \fBcvs\fP maintains some administrative
+files that record the current revision numbers of the checked-out files as
+well as the location of the respective source repository.
+\fBcvs\fP does not recover nicely at all if these administrative files are
+removed.
+.IP \(bu 3
+The source code for \fBcvs\fP has been tested extensively on Sun-3 and
+Sun-4 systems, all running SunOS 4.0 or later versions of the operating
+system.
+Since the code has not yet been compiled under other platforms, the overall
+portability of the code is still questionable.
+.IP \(bu 3
+As witnessed in the previous section, the \fBcvs\fP method for tracking
+third party vendor source distributions can work quite nicely.
+However, if the vendor changes the directory structure or the file names
+within the source distribution, \fBcvs\fP has no way of matching the old
+release with the new one.
+It is currently unclear as to how to solve this, though it is certain to
+happen in practice.
+.RE
+.NH
+Availability
+.PP
+The \fBcvs\fP program sources can be found in a recent posting to the
+\fBcomp.sources.unix\fP newsgroup.
+It is also currently available via anonymous ftp from \*Qprisma.com\*U.
+Copying rights for \fBcvs\fP will be covered by the GNU General Public
+License.
+.NH
+Summary
+.PP
+Prisma has used \fBcvs\fP since December, 1988.
+It has evolved to meet our specific needs of revision and release control.
+We will make our code freely available so that others can
+benefit from our work, and can enhance \fBcvs\fP to meet broader needs yet.
+.PP
+Many of the other software release and revision control systems, like the
+one described in [Glew], appear to use a collection of tools that are
+geared toward specific environments \(em one set of tools for the kernel,
+one set for \*Qgeneric\*U software, one set for utilities, and one set for
+kernel and utilities.
+Each of these tool sets apparently handle some specific aspect of the
+problem uniquely.
+\fBcvs\fP took a somewhat different approach.
+File sharing through symbolic or hard links is not addressed; instead, the
+disk space is simply burned since it is \*Qcheap.\*U
+Support for producing objects for multiple architectures is not addressed;
+instead, a parallel checked-out source tree must be used for each
+architecture, again wasting disk space to simplify complexity and ease of
+use \(em punting on this issue allowed \fIMakefile\fPs to remain
+unchanged, unlike the approach taken in [Mahler], thereby maintaining closer
+compatibility with the third-party vendor sources.
+\fBcvs\fP is essentially a source-file server, making no assumptions or
+special handling of the sources that it controls.
+To \fBcvs\fP:
+.QP
+A source is a source, of course, of course, unless of course the source is
+Mr. Ed.\**
+.FS
+\fBcvs\fP, of course, does not really discriminate against Mr. Ed.\**
+.FE
+.FS
+Yet.
+.FE
+.LP
+Sources are maintained, saved, and retrievable at any time based on
+symbolic or numeric revision or date in the past.
+It is entirely up to \fBcvs\fP wrapper programs to provide for release
+environments and such.
+.PP
+The major advantage of \fBcvs\fP over the
+many other similar systems that have already been designed is the
+simplicity of \fBcvs\fP.
+\fBcvs\fP contains only three programs that do all the work of release
+and revision control, and two manually-maintained administrative
+files for each source repository.
+Of course, the deciding factor of any tool is whether people use it, and if
+they even \fIlike\fP to use it.
+At Prisma, \fBcvs\fP prevented members of the kernel
+group from killing each other.
+.NH
+Acknowledgements
+.PP
+Many thanks to Dick Grune at Vrije Universiteit in Amsterdam for his work
+on the original version of \fBcvs\fP and for making it available to the
+world.
+Thanks to Jeff Polk of Prisma for helping with the design of the module
+database, vendor branch support, and for writing the \fBcheckin\fP shell
+script.
+Thanks also to the entire software group at Prisma for taking the
+time to review the paper and correct my grammar.
+.NH
+References
+.IP [Bell] 12
+Bell Telephone Laboratories.
+\*QSource Code Control System User's Guide.\*U
+\fIUNIX System III Programmer's Manual\fP, October 1981.
+.IP [Courington] 12
+Courington, W.
+\fIThe Network Software Environment\fP,
+Sun Technical Report FE197-0, Sun Microsystems Inc, February 1989.
+.IP [Essick] 12
+Essick, Raymond B. and Robert Bruce Kolstad.
+\fINotesfile Reference Manual\fP,
+Department of Computer Science Technical Report #1081,
+University of Illinois at Urbana-Champaign, Urbana, Illinois,
+1982, p. 26.
+.IP [Glew] 12
+Glew, Andy.
+\*QBoxes, Links, and Parallel Trees:
+Elements of a Configuration Management System.\*U
+\fIWorkshop Proceedings of the Software Management Conference\fP, USENIX,
+New Orleans, April 1989.
+.IP [Grune] 12
+Grune, Dick.
+Distributed the original shell script version of \fBcvs\fP in the
+\fBcomp.sources.unix\fP volume 6 release in 1986.
+.IP [Honda] 12
+Honda, Masahiro and Terrence Miller.
+\*QSoftware Management Using a CASE Environment.\*U
+\fIWorkshop Proceedings of the Software Management Conference\fP, USENIX,
+New Orleans, April 1989.
+.IP [Mahler] 12
+Mahler, Alex and Andreas Lampen.
+\*QAn Integrated Toolset for Engineering Software Configurations.\*U
+\fIProceedings of the ACM SIGSOFT/SIGPLAN Software Engineering Symposium on
+Practical Software Development Environments\fP, ACM, Boston, November 1988.
+Described is the \fBshape\fP toolkit posted to the
+\fBcomp.sources.unix\fP newsgroup in the volume 19 release.
+.IP [Tichy] 12
+Tichy, Walter F.
+\*QDesign, Implementation, and Evaluation of a Revision Control System.\*U
+\fIProceedings of the 6th International Conference on Software
+Engineering\fP, IEEE, Tokyo, September 1982.
+.IP [Wall] 12
+Wall, Larry.
+The \fBpatch\fP program is an indispensable tool for applying a diff file
+to an original.
+Can be found on uunet.uu.net in ~ftp/pub/patch.tar.
diff --git a/gnu/usr.bin/cvs/examples/commitinfo b/gnu/usr.bin/cvs/examples/commitinfo
new file mode 100644
index 0000000..bdb94b6
--- /dev/null
+++ b/gnu/usr.bin/cvs/examples/commitinfo
@@ -0,0 +1,21 @@
+#
+# commitinfo,v 1.2 1992/03/31 04:19:47 berliner Exp
+#
+# The "commitinfo" file is used to control pre-commit checks.
+# The filter on the right is invoked with the repository and a list
+# of files to check. A non-zero exit of the filter program will
+# cause the commit to be aborted.
+#
+# The first entry on a line is a regular expression which is tested
+# against the directory that the change is being committed to, relative to the
+# $CVSROOT. If a match is found, then the remainder of the line is the
+# name of the filter to run.
+#
+# If the repository name does not match any of the regular expressions in this
+# file, the "DEFAULT" line is used, if it is specified.
+#
+# If the name ALL appears as a regular expression it is always used
+# in addition to the first matching regex or DEFAULT.
+#
+^cvs checkforcvsid
+DEFAULT checkforid
diff --git a/gnu/usr.bin/cvs/examples/editinfo b/gnu/usr.bin/cvs/examples/editinfo
new file mode 100644
index 0000000..b454abd
--- /dev/null
+++ b/gnu/usr.bin/cvs/examples/editinfo
@@ -0,0 +1,30 @@
+#
+# editinfo,v 1.1 1992/03/21 06:49:39 berliner Exp
+#
+# The "editinfo" file is used to allow verification of logging
+# information. It works best when a template (as specified in the
+# rcsinfo file) is provided for the logging procedure. Given a
+# template with locations for, a bug-id number, a list of people who
+# reviewed the code before it can be checked in, and an external
+# process to catalog the differences that were code reviewed, the
+# following test can be applied to the code:
+#
+# Making sure that the entered bug-id number is correct.
+# Validating that the code that was reviewed is indeed the code being
+# checked in (using the bug-id number or a seperate review
+# number to identify this particular code set.).
+#
+# If any of the above test failed, then the commit would be aborted.
+#
+# Actions such as mailing a copy of the report to each reviewer are
+# better handled by an entry in the loginfo file.
+#
+# Although these test could be handled by an interactive script being
+# called via an entry in commitinfo, The information reported in
+# such a script can't be easily merged into the report.
+#
+# One thing that should be noted is the the ALL keyword is not
+# supported. There can be only one entry that matches a given
+# repository.
+#
+DEFAULT $CVSROOT/CVSROOT/edit "%s"
diff --git a/gnu/usr.bin/cvs/examples/loginfo b/gnu/usr.bin/cvs/examples/loginfo
new file mode 100644
index 0000000..6339439
--- /dev/null
+++ b/gnu/usr.bin/cvs/examples/loginfo
@@ -0,0 +1,20 @@
+#
+# @(#)loginfo 1.5 92/03/31
+#
+# The "loginfo" file is used to control where "cvs commit" log information
+# is sent. The first entry on a line is a regular expression which is tested
+# against the directory that the change is being made to, relative to the
+# $CVSROOT. If a match is found, then the remainder of the line is a filter
+# program that should expect log information on its standard input.
+#
+# The filter program may use one and only one % modifier (ala printf). If
+# %s is specified in the filter program, a brief title is included (enclosed
+# in single quotes) showing the modified file names.
+#
+# If the repository name does not match any of the regular expressions in this
+# file, the "DEFAULT" line is used, if it is specified.
+#
+# If the name ALL appears as a regular expression it is always used
+# in addition to the first matching regex or DEFAULT.
+#
+DEFAULT $CVSROOT/CVSROOT/log.pl %s $CVSROOT/CVSROOT/commitlog
diff --git a/gnu/usr.bin/cvs/examples/modules b/gnu/usr.bin/cvs/examples/modules
new file mode 100644
index 0000000..893b47b
--- /dev/null
+++ b/gnu/usr.bin/cvs/examples/modules
@@ -0,0 +1,566 @@
+#
+# CVS Modules file for Prisma sources
+# @(#)modules 1.5 92/03/31
+#
+# Three different line formats are valid:
+# key -a aliases...
+# key [options] directory
+# key [options] directory files...
+#
+# Where "options" are composed of:
+# -i prog Run "prog" on "cvs commit" from top-level of module.
+# -o prog Run "prog" on "cvs checkout" of module.
+# -t prog Run "prog" on "cvs rtag" of module.
+# -u prog Run "prog" on "cvs update" of module.
+# -d dir Place module in directory "dir" instead of module name.
+# -l Top-level directory only -- do not recurse.
+#
+# And "directory" is a path to a directory relative to $CVSROOT.
+#
+# The "-a" option specifies an alias. An alias is interpreted as if
+# everything on the right of the "-a" had been typed on the command line.
+#
+# You can encode a module within a module by using the special '&'
+# character to interpose another module into the current module. This
+# can be useful for creating a module that consists of many directories
+# spread out over the entire source repository.
+#
+
+# Convenient aliases
+world -a .
+kernel -a sys lang/adb sparcsim
+
+# CVSROOT support
+CVSROOT -i /usr/local/bin/mkmodules CVSROOT
+modules -i /usr/local/bin/mkmodules CVSROOT modules
+loginfo -i /usr/local/bin/mkmodules CVSROOT loginfo
+commitinfo -i /usr/local/bin/mkmodules CVSROOT commitinfo
+rcsinfo -i /usr/local/bin/mkmodules CVSROOT rcsinfo
+
+# The "sys" entry exists only to make symbolic links after checkout
+sys -o sys/tools/make_links sys
+
+# Sub-directories of "bin"
+awk bin/awk
+csh bin/csh
+diff bin/diff
+make bin/make
+sed bin/sed
+sh bin/sh
+
+# Programs that live in "bin"
+cat bin Makefile cat.c
+chgrp bin Makefile chgrp.c
+chmod bin Makefile chmod.c
+cmp bin Makefile cmp.c
+cp bin Makefile cp.c
+date bin Makefile date.c
+dd bin Makefile dd.c
+df bin Makefile df.c
+domainname bin Makefile domainname.c
+du bin Makefile du.c
+echo bin Makefile echo.c
+ed bin Makefile ed.c
+env bin Makefile env.c
+expr bin Makefile expr.c
+grep bin Makefile grep.c
+hostid bin Makefile hostid.c
+hostname bin Makefile hostname.c
+kill bin Makefile kill.c
+ldd bin Makefile ldd.c
+line bin Makefile line.c
+ln bin Makefile ln.c
+login bin Makefile login.c
+ls bin Makefile ls.c
+mail bin Makefile mail.c
+mkdir bin Makefile mkdir.c
+mt bin Makefile mt.c
+mv bin Makefile mv.c
+newgrp bin Makefile newgrp.c
+nice bin Makefile nice.c
+od bin Makefile od.c
+pagesize bin Makefile pagesize.c
+passwd bin Makefile passwd.c
+pr bin Makefile pr.c
+ps bin Makefile ps.c
+pwd bin Makefile pwd.c
+rm bin Makefile rm.c
+rmail bin Makefile rmail.c
+rmdir bin Makefile rmdir.c
+stty bin Makefile stty.c
+su bin Makefile su.c
+sync bin Makefile sync.c
+tar bin Makefile tar.c
+tee bin Makefile tee.c
+test bin Makefile test.c
+time bin Makefile time.c
+wall bin Makefile wall.c
+who bin Makefile who.c
+write bin Makefile write.c
+
+# Sub-directories of "etc"
+dump etc/dump
+files etc/files
+fsck etc/fsck
+getty etc/getty
+in.routed etc/in.routed
+restore etc/restore
+rpc.lockd etc/rpc.lockd
+rpc.statd etc/rpc.statd
+
+# Programs that live in "etc"
+arp etc Makefile arp.c
+biod etc Makefile biod.c
+chown etc Makefile chown.c
+clri etc Makefile clri.c
+dkinfo etc Makefile dkinfo.c
+dmesg etc Makefile dmesg.c
+fsirand etc Makefile fsirand.c
+halt etc Makefile halt.c
+ifconfig etc Makefile ifconfig.c
+in.rlogind etc Makefile in.rlogind.c
+in.rshd etc Makefile in.rshd.c
+inetd etc Makefile inetd.c
+init etc Makefile init.c
+mkfs etc Makefile mkfs.c
+mknod etc Makefile mknod.c
+mount etc Makefile mount.c
+newfs etc Makefile newfs.c
+nfsd etc Makefile nfsd.c
+portmap etc Makefile portmap.c
+pstat etc Makefile pstat.c
+reboot etc Makefile reboot.c
+renice etc Makefile renice.c
+rmt etc Makefile rmt.c
+shutdown etc Makefile shutdown.c
+syslogd etc Makefile syslogd.c
+umount etc Makefile umount.c
+update etc Makefile update.c
+vipw etc Makefile vipw.c
+ypbind etc Makefile ypbind.c
+
+# Sub-directories of "games"
+adventure games/adventure
+backgammon games/backgammon
+battlestar games/battlestar
+boggle games/boggle
+chess games/chess
+ching games/ching
+cribbage games/cribbage
+fortune games/fortune
+hack games/hack
+hangman games/hangman
+hunt games/hunt
+life games/life
+mille games/mille
+monop games/monop
+quiz games/quiz
+robots games/robots
+sail games/sail
+snake games/snake
+trek games/trek
+
+# Programs that live in "games"
+arithmetic games Makefile arithmetic.c
+banner games Makefile banner.c
+bcd games Makefile bcd.c
+bj games Makefile bj.c
+btlgammon games Makefile btlgammon.c
+canfield games Makefile canfield.c
+cfscores games Makefile cfscores.c
+craps games Makefile craps.c
+factor games Makefile factor.c
+fish games Makefile fish.c
+moo games Makefile moo.c
+number games Makefile number.c
+primes games Makefile primes.c
+rain games Makefile rain.c
+random games Makefile random.c
+worm games Makefile worm.c
+worms games Makefile worms.c
+wump games Makefile wump.c
+
+# Sub-directories of "lang"
+adb lang/adb
+as lang/as
+boot lang/boot
+c2 lang/c2
+cgrdr lang/cgrdr
+compile lang/compile
+cpp lang/cpp
+dbx lang/dbx
+f77 lang/f77
+inline lang/inline
+iropt lang/iropt
+ld lang/ld
+lint lang/lint
+m4 lang/m4
+pascal lang/pascal
+pcc lang/pcc
+ratfor lang/ratfor
+rtld lang/rtld
+tcov lang/tcov
+vroot lang/vroot
+
+# Programs that live in "lang"
+ar lang Makefile ar.c
+nm lang Makefile nm.c
+ranlib lang Makefile ranlib.c
+size lang Makefile size.c
+strip lang Makefile strip.c
+symorder lang Makefile symorder.c
+
+# Sub-directories of "lib"
+csu lib/csu
+libc lib/libc
+
+# Programs that live in "lib"
+# NONE
+
+# Sub-directories of "lib/libc"
+libc_compat lib/libc/compat
+libc_crt lib/libc/crt
+libc_des lib/libc/des
+libc_gen lib/libc/gen
+libc_net lib/libc/net
+libc_inet lib/libc/inet
+libc_rpc lib/libc/rpc
+libc_stdio lib/libc/stdio
+libc_sun lib/libc/sun
+libc_sys lib/libc/sys
+libc_yp lib/libc/yp
+
+# Programs that live in "lib/libc"
+# NONE
+
+#Sub-directories of "local"
+notes local/notes
+
+# Sub-directories of "man"
+man1 man/man1
+man2 man/man2
+man3 man/man3
+man4 man/man4
+man5 man/man5
+man6 man/man6
+man7 man/man7
+man8 man/man8
+manl man/manl
+
+# Programs that live in "man"
+# NONE
+
+# Sub-directories of "old"
+old_compact old/compact
+old_eyacc old/eyacc
+old_filemerge old/filemerge
+old_make old/make
+
+# Programs that live in "old"
+old_analyze old Makefile analyze.c
+old_prmail old Makefile prmail.c
+old_pti old Makefile pti.c
+old_syslog old Makefile syslog.c
+
+# Sub-directories of "ucb"
+Mail ucb/Mail
+compress ucb/compress
+error ucb/error
+ex ucb/ex
+ftp ucb/ftp
+gprof ucb/gprof
+indent ucb/indent
+lpr ucb/lpr
+more ucb/more
+msgs ucb/msgs
+netstat ucb/netstat
+rdist ucb/rdist
+talk ucb/talk
+tftp ucb/tftp
+tset ucb/tset
+vgrind ucb/vgrind
+
+# Programs that live in "ucb"
+biff ucb Makefile biff.c
+checknr ucb Makefile checknr.c
+clear ucb Makefile clear.c
+colcrt ucb Makefile colcrt.c
+colrm ucb Makefile colrm.c
+ctags ucb Makefile ctags.c
+expand ucb Makefile expand.c
+finger ucb Makefile finger.c
+fold ucb Makefile fold.c
+from ucb Makefile from.c
+fsplit ucb Makefile fsplit.c
+gcore ucb Makefile gcore.c
+groups ucb Makefile groups.c
+head ucb Makefile head.c
+last ucb Makefile last.c
+lastcomm ucb Makefile lastcomm.c
+leave ucb Makefile leave.c
+logger ucb Makefile logger.c
+man_prog ucb Makefile man.c
+mkstr ucb Makefile mkstr.c
+printenv ucb Makefile printenv.c
+quota ucb Makefile quota.c
+rcp ucb Makefile rcp.c
+rdate ucb Makefile rdate.c
+rlogin ucb Makefile rlogin.c
+rsh ucb Makefile rsh.c
+rup ucb Makefile rup.c
+ruptime ucb Makefile ruptime.c
+rusers ucb Makefile rusers.c
+rwho ucb Makefile rwho.c
+sccs ucb Makefile sccs.c
+script ucb Makefile script.c
+soelim ucb Makefile soelim.c
+strings ucb Makefile strings.c
+tail ucb Makefile tail.c
+tcopy ucb Makefile tcopy.c
+telnet ucb Makefile telnet.c
+ul ucb Makefile ul.c
+unexpand ucb Makefile unexpand.c
+unifdef ucb Makefile unifdef.c
+users ucb Makefile users.c
+vmstat ucb Makefile vmstat.c
+w ucb Makefile w.c
+wc ucb Makefile wc.c
+what ucb Makefile what.c
+whatis ucb Makefile whatis.c
+whereis ucb Makefile whereis.c
+whoami ucb Makefile whoami.c
+whois ucb Makefile whois.c
+xstr ucb Makefile xstr.c
+yes ucb Makefile yes.c
+
+# Sub-directories of "usr.bin"
+calendar usr.bin/calendar
+cflow usr.bin/cflow
+ctrace usr.bin/ctrace
+cxref usr.bin/cxref
+dc usr.bin/dc
+des usr.bin/des
+diff3 usr.bin/diff3
+sun_eqn usr.bin/eqn
+file usr.bin/file
+find usr.bin/find
+graph usr.bin/graph
+lex usr.bin/lex
+sun_neqn usr.bin/neqn
+sun_nroff usr.bin/nroff
+sun_plot usr.bin/plot
+prof usr.bin/prof
+refer usr.bin/refer
+rpcgen usr.bin/rpcgen
+spell usr.bin/spell
+sun_tbl usr.bin/tbl
+tip usr.bin/tip
+trace usr.bin/trace
+sun_troff usr.bin/troff
+uucp usr.bin/uucp
+xsend usr.bin/xsend
+yacc usr.bin/yacc
+
+# Programs that live in "usr.bin"
+basename usr.bin Makefile basename.c
+bc usr.bin Makefile bc.c
+cal usr.bin Makefile cal.c
+cb usr.bin Makefile cb.c
+checkeq usr.bin Makefile checkeq.c
+chkey usr.bin Makefile chkey.c
+click usr.bin Makefile click.c
+col usr.bin Makefile col.c
+comm usr.bin Makefile comm.c
+cpio usr.bin Makefile cpio.c
+crypt usr.bin Makefile crypt.c
+csplit usr.bin Makefile csplit.c
+cut usr.bin Makefile cut.c
+deroff usr.bin Makefile deroff.c
+egrep usr.bin Makefile egrep.c
+fgrep usr.bin Makefile fgrep.c
+getopt usr.bin Makefile getopt.c
+id usr.bin Makefile id.c
+installcmd usr.bin Makefile installcmd.c
+iostat usr.bin Makefile iostat.c
+ipcrm usr.bin Makefile ipcrm.c
+ipcs usr.bin Makefile ipcs.c
+join usr.bin Makefile join.c
+keylogin usr.bin Makefile keylogin.c
+logname usr.bin Makefile logname.c
+look usr.bin Makefile look.c
+mesg usr.bin Makefile mesg.c
+nl usr.bin Makefile nl.c
+pack usr.bin Makefile pack.c
+paste usr.bin Makefile paste.c
+ptx usr.bin Makefile ptx.c
+rev usr.bin Makefile rev.c
+screenblank usr.bin Makefile screenblank.c
+sdiff usr.bin Makefile sdiff.c
+sleep usr.bin Makefile sleep.c
+sort usr.bin Makefile sort.c
+spline usr.bin Makefile spline.c
+split usr.bin Makefile split.c
+sum usr.bin Makefile sum.c
+touch usr.bin Makefile touch.c
+tr usr.bin Makefile tr.c
+tsort usr.bin Makefile tsort.c
+tty usr.bin Makefile tty.c
+uniq usr.bin Makefile uniq.c
+units usr.bin Makefile units.c
+unpack usr.bin Makefile unpack.c
+xargs usr.bin Makefile xargs.c
+ypcat usr.bin Makefile ypcat.c
+ypmatch usr.bin Makefile ypmatch.c
+yppasswd usr.bin Makefile yppasswd.c
+ypwhich usr.bin Makefile ypwhich.c
+
+# Sub-directories of "usr.etc"
+automount usr.etc/automount
+c2convert usr.etc/c2convert
+config usr.etc/config
+cron usr.etc/cron
+eeprom usr.etc/eeprom
+etherfind usr.etc/etherfind
+format usr.etc/format
+htable usr.etc/htable
+implog usr.etc/implog
+in.ftpd -a usr.etc/in.ftpd ucb/ftp
+in.named usr.etc/in.named
+in.rwhod usr.etc/in.rwhod
+keyserv usr.etc/keyserv
+ndbootd usr.etc/ndbootd
+praudit usr.etc/praudit
+rexd usr.etc/rexd
+rpc.bootparamd usr.etc/rpc.bootparamd
+termcap usr.etc/termcap
+upgrade usr.etc/upgrade
+yp usr.etc/yp
+zic usr.etc/zic
+
+# Programs that live in "usr.etc"
+ac usr.etc Makefile ac.c
+accton usr.etc Makefile accton.c
+audit usr.etc Makefile audit.c
+auditd usr.etc Makefile auditd.c
+catman usr.etc Makefile catman.c
+chroot usr.etc Makefile chroot.c
+dcheck usr.etc Makefile dcheck.c
+devnm usr.etc Makefile devnm.c
+dumpfs usr.etc Makefile dumpfs.c
+edquota usr.etc Makefile edquota.c
+exportfs usr.etc Makefile exportfs.c
+foption usr.etc Makefile foption.c
+gettable usr.etc Makefile gettable.c
+grpck usr.etc Makefile grpck.c
+icheck usr.etc Makefile icheck.c
+in.comsat usr.etc Makefile in.comsat.c
+in.fingerd usr.etc Makefile in.fingerd.c
+in.rexecd usr.etc Makefile in.rexecd.c
+in.telnetd usr.etc Makefile in.telnetd.c
+in.tnamed usr.etc Makefile in.tnamed.c
+kgmon usr.etc Makefile kgmon.c
+link usr.etc Makefile link.c
+mkfile usr.etc Makefile mkfile.c
+mkproto usr.etc Makefile mkproto.c
+mount_lo usr.etc Makefile mount_lo.c
+ncheck usr.etc Makefile ncheck.c
+nfsstat usr.etc Makefile nfsstat.c
+ping usr.etc Makefile ping.c
+pwck usr.etc Makefile pwck.c
+quot usr.etc Makefile quot.c
+quotacheck usr.etc Makefile quotacheck.c
+quotaon usr.etc Makefile quotaon.c
+rarpd usr.etc Makefile rarpd.c
+repquota usr.etc Makefile repquota.c
+route usr.etc Makefile route.c
+rpc.etherd usr.etc Makefile rpc.etherd.c
+rpc.mountd usr.etc Makefile rpc.mountd.c
+rpc.pwdauthd usr.etc Makefile rpc.pwdauthd.c
+rpc.rquotad usr.etc Makefile rpc.rquotad.c
+rpc.rstatd usr.etc Makefile rpc.rstatd.c
+rpc.rusersd usr.etc Makefile rpc.rusersd.c
+rpc.rwalld usr.etc Makefile rpc.rwalld.c
+rpc.sprayd usr.etc Makefile rpc.sprayd.c
+rpc.yppasswdd usr.etc Makefile rpc.yppasswdd.c
+rpc.ypupdated usr.etc Makefile rpc.ypupdated.c
+rpcinfo usr.etc Makefile rpcinfo.c
+rwall usr.etc Makefile rwall.c
+sa usr.etc Makefile sa.c
+savecore usr.etc Makefile savecore.c
+showmount usr.etc Makefile showmount.c
+spray usr.etc Makefile spray.c
+swapon usr.etc Makefile swapon.c
+trpt usr.etc Makefile trpt.c
+tunefs usr.etc Makefile tunefs.c
+unlink usr.etc Makefile unlink.c
+
+# Sub-directories of "usr.lib"
+bb_count usr.lib/bb_count
+fixedwidthfonts usr.lib/fixedwidthfonts
+libcurses usr.lib/libcurses
+libdbm usr.lib/libdbm
+libg usr.lib/libg
+libkvm usr.lib/libkvm
+libln usr.lib/libln
+liblwp usr.lib/liblwp
+libm usr.lib/libm
+libmp usr.lib/libmp
+libpixrect usr.lib/libpixrect
+libplot usr.lib/libplot
+libresolv usr.lib/libresolv
+librpcsvc usr.lib/librpcsvc
+libtermlib usr.lib/libtermlib
+liby usr.lib/liby
+me usr.lib/me
+ms usr.lib/ms
+sendmail usr.lib/sendmail
+sun_tmac usr.lib/tmac
+vfont usr.lib/vfont
+
+# Programs that live in "usr.lib"
+getNAME usr.lib Makefile getNAME
+makekey usr.lib Makefile makekey
+
+# Sub-directories of "5bin"
+5diff3 5bin/diff3
+5m4 5bin/m4
+
+# Sub-directories of "5bin", but use sources from other places
+5cxref -a 5bin/cxref usr.bin/cxref
+5sed -a 5bin/sed bin/sed
+5lint -a 5bin/lint lang/pcc lang/lint
+
+# Programs that live in "5bin"
+5banner 5bin Makefile banner.c
+5cat 5bin Makefile cat.c
+5du 5bin Makefile du.c
+5echo 5bin Makefile echo.c
+5expr 5bin Makefile expr.c
+5ls 5bin Makefile ls.c
+5nohup 5bin Makefile nohup.c
+5od 5bin Makefile od.c
+5pg 5bin Makefile pg.c
+5pr 5bin Makefile pr.c
+5sum 5bin Makefile sum.c
+5tabs 5bin Makefile tabs.c
+5time 5bin Makefile time.c
+5tr 5bin Makefile tr.c
+5uname 5bin Makefile uname.c
+
+# Programs that live in "5bin", but use sources from other places
+5chmod -a 5bin/Makefile bin/chmod.c
+5date -a 5bin/Makefile bin/date.c
+5grep -a 5bin/Makefile bin/grep.c
+5stty -a 5bin/Makefile bin/stty.c
+5col -a 5bin/Makefile usr.bin/col.c
+5sort -a 5bin/Makefile usr.bin/sort.c
+5touch -a 5bin/Makefile usr.bin/touch.c
+
+# Sub-directories of "5lib"
+5compile 5lib/compile
+5libcurses 5lib/libcurses
+5liby 5lib/liby
+5terminfo 5lib/terminfo
+
+# Programs that live in "5lib"
+# NONE
diff --git a/gnu/usr.bin/cvs/examples/rcsinfo b/gnu/usr.bin/cvs/examples/rcsinfo
new file mode 100644
index 0000000..6d91455
--- /dev/null
+++ b/gnu/usr.bin/cvs/examples/rcsinfo
@@ -0,0 +1,18 @@
+#
+# rcsinfo,v 1.3 1992/04/10 18:59:14 berliner Exp
+#
+# The "rcsinfo" file is used to control templates with which the editor
+# is invoked on commit and import.
+#
+# The first entry on a line is a regular expression which is tested
+# against the directory that the change is being made to, relative to the
+# $CVSROOT. If a match is found, then the remainder of the line is the
+# name of the file that contains the template.
+#
+# If the repository name does not match any of the regular expressions in this
+# file, the "DEFAULT" line is used, if it is specified.
+#
+# If the name ALL appears as a regular expression it is always used
+# in addition to the first matching regex or DEFAULT.
+#
+DEFAULT /src/master/CVSROOT/rcstemplate
diff --git a/gnu/usr.bin/cvs/lib/Makefile b/gnu/usr.bin/cvs/lib/Makefile
new file mode 100644
index 0000000..5233869
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/Makefile
@@ -0,0 +1,13 @@
+LIB = cvs
+
+CFLAGS += -I${.CURDIR} -I${.CURDIR}/../cvs -DFTIME_MISSING -DHAVE_TIMEZONE
+
+SRCS = argmatch.c error.c getopt.c sighandle.c strippath.c stripslash.c yesno.c \
+ getdate.y fnmatch.c regex.c subr.c myndbm.c hash.c
+
+CLEANFILES+= getdate.c y.tab.h
+
+install:
+ @echo -n
+
+.include <bsd.lib.mk>
diff --git a/gnu/usr.bin/cvs/lib/Makefile.in b/gnu/usr.bin/cvs/lib/Makefile.in
new file mode 100644
index 0000000..a8309f2
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/Makefile.in
@@ -0,0 +1,91 @@
+# Makefile for library files used by GNU CVS.
+# Do not use this makefile directly, but only from `../Makefile'.
+# Copyright (C) 1986, 1988-1992 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# @(#)Makefile.in 1.12 92/03/31
+
+SHELL = /bin/sh
+
+srcdir = @srcdir@
+@VPATH@
+
+SOURCES = argmatch.c \
+error.c getopt.c getopt1.c \
+sighandle.c \
+strippath.c stripslash.c yesno.c \
+getdate.y \
+hostname.c fnmatch.c ftruncate.c mkdir.c rename.c regex.c \
+strdup.c getwd.c alloca.c
+
+OBJECTS = argmatch.o \
+error.o getopt.o getopt1.o \
+sighandle.o \
+strippath.o stripslash.o yesno.o \
+getdate.o \
+@LIBOBJS@
+
+DISTFILES = Makefile.in getopt.h \
+fnmatch.h regex.h system.h wait.h $(SOURCES)
+
+xxx:
+ @cd ..; $(MAKE) all SUBDIRS=lib
+
+all: libcvs.a
+.PHONY: all
+
+install: all
+.PHONY: install
+
+tags: $(DISTFILES)
+ ctags $(DISTFILES)
+
+TAGS: $(DISTFILES)
+ etags $(DISTFILES)
+
+ls:
+ @echo $(DISTFILES)
+.PHONY: ls
+
+clean:
+ rm -f *.a *.o *.tab.c getdate.c
+.PHONY: clean
+
+distclean: clean
+ rm -f tags TAGS Makefile
+.PHONY: distclean
+
+realclean: distclean
+.PHONY: realclean
+
+dist:
+ ln $(DISTFILES) ../`cat ../.fname`/lib
+.PHONY: dist
+
+libcvs.a: $(OBJECTS)
+ $(AR) cr $@ $(OBJECTS)
+ -$(RANLIB) $@
+
+getdate.c: getdate.y
+ @echo expect 8 shift/reduce conflicts
+ $(YACC) $(srcdir)/getdate.y
+ -if test -f y.tab.c ; then mv y.tab.c getdate.c ; fi
+ -if test -f getdate.tab.c ; then mv getdate.tab.c getdate.c ; fi
+
+fnmatch.o: fnmatch.h
+getopt1.o: getopt.h
+regex.o: regex.h
+getwd.o: system.h
diff --git a/gnu/usr.bin/cvs/lib/alloca.c b/gnu/usr.bin/cvs/lib/alloca.c
new file mode 100644
index 0000000..d2a54b3
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/alloca.c
@@ -0,0 +1,191 @@
+/*
+ alloca -- (mostly) portable public-domain implementation -- D A Gwyn
+
+ last edit: 86/05/30 rms
+ include config.h, since on VMS it renames some symbols.
+ Use xmalloc instead of malloc.
+
+ This implementation of the PWB library alloca() function,
+ which is used to allocate space off the run-time stack so
+ that it is automatically reclaimed upon procedure exit,
+ was inspired by discussions with J. Q. Johnson of Cornell.
+
+ It should work under any C implementation that uses an
+ actual procedure stack (as opposed to a linked list of
+ frames). There are some preprocessor constants that can
+ be defined when compiling for your specific system, for
+ improved efficiency; however, the defaults should be okay.
+
+ The general concept of this implementation is to keep
+ track of all alloca()-allocated blocks, and reclaim any
+ that are found to be deeper in the stack than the current
+ invocation. This heuristic does not reclaim storage as
+ soon as it becomes invalid, but it will do so eventually.
+
+ As a special case, alloca(0) reclaims storage without
+ allocating any. It is a good idea to use alloca(0) in
+ your main control loop, etc. to force garbage collection.
+*/
+#ifndef lint
+static char SCCSid[] = "@(#)alloca.c 1.1"; /* for the "what" utility */
+#endif
+
+#ifdef emacs
+#include "config.h"
+#ifdef static
+/* actually, only want this if static is defined as ""
+ -- this is for usg, in which emacs must undefine static
+ in order to make unexec workable
+ */
+#ifndef STACK_DIRECTION
+you
+lose
+-- must know STACK_DIRECTION at compile-time
+#endif /* STACK_DIRECTION undefined */
+#endif /* static */
+#endif /* emacs */
+
+#if __STDC__
+typedef void *pointer; /* generic pointer type */
+#else
+typedef char *pointer; /* generic pointer type */
+#endif
+
+#define NULL 0 /* null pointer constant */
+
+extern void free();
+extern pointer xmalloc();
+
+/*
+ Define STACK_DIRECTION if you know the direction of stack
+ growth for your system; otherwise it will be automatically
+ deduced at run-time.
+
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown
+*/
+
+#ifndef STACK_DIRECTION
+#define STACK_DIRECTION 0 /* direction unknown */
+#endif
+
+#if STACK_DIRECTION != 0
+
+#define STACK_DIR STACK_DIRECTION /* known at compile-time */
+
+#else /* STACK_DIRECTION == 0; need run-time code */
+
+static int stack_dir; /* 1 or -1 once known */
+#define STACK_DIR stack_dir
+
+static void
+find_stack_direction (/* void */)
+{
+ static char *addr = NULL; /* address of first
+ `dummy', once known */
+ auto char dummy; /* to get stack address */
+
+ if (addr == NULL)
+ { /* initial entry */
+ addr = &dummy;
+
+ find_stack_direction (); /* recurse once */
+ }
+ else /* second entry */
+ if (&dummy > addr)
+ stack_dir = 1; /* stack grew upward */
+ else
+ stack_dir = -1; /* stack grew downward */
+}
+
+#endif /* STACK_DIRECTION == 0 */
+
+/*
+ An "alloca header" is used to:
+ (a) chain together all alloca()ed blocks;
+ (b) keep track of stack depth.
+
+ It is very important that sizeof(header) agree with malloc()
+ alignment chunk size. The following default should work okay.
+*/
+
+#ifndef ALIGN_SIZE
+#define ALIGN_SIZE sizeof(double)
+#endif
+
+typedef union hdr
+{
+ char align[ALIGN_SIZE]; /* to force sizeof(header) */
+ struct
+ {
+ union hdr *next; /* for chaining headers */
+ char *deep; /* for stack depth measure */
+ } h;
+} header;
+
+/*
+ alloca( size ) returns a pointer to at least `size' bytes of
+ storage which will be automatically reclaimed upon exit from
+ the procedure that called alloca(). Originally, this space
+ was supposed to be taken from the current stack frame of the
+ caller, but that method cannot be made to work for some
+ implementations of C, for example under Gould's UTX/32.
+*/
+
+static header *last_alloca_header = NULL; /* -> last alloca header */
+
+pointer
+alloca (size) /* returns pointer to storage */
+ unsigned size; /* # bytes to allocate */
+{
+ auto char probe; /* probes stack depth: */
+ register char *depth = &probe;
+
+#if STACK_DIRECTION == 0
+ if (STACK_DIR == 0) /* unknown growth direction */
+ find_stack_direction ();
+#endif
+
+ /* Reclaim garbage, defined as all alloca()ed storage that
+ was allocated from deeper in the stack than currently. */
+
+ {
+ register header *hp; /* traverses linked list */
+
+ for (hp = last_alloca_header; hp != NULL;)
+ if (STACK_DIR > 0 && hp->h.deep > depth
+ || STACK_DIR < 0 && hp->h.deep < depth)
+ {
+ register header *np = hp->h.next;
+
+ free ((pointer) hp); /* collect garbage */
+
+ hp = np; /* -> next header */
+ }
+ else
+ break; /* rest are not deeper */
+
+ last_alloca_header = hp; /* -> last valid storage */
+ }
+
+ if (size == 0)
+ return NULL; /* no allocation required */
+
+ /* Allocate combined header + user data storage. */
+
+ {
+ register pointer new = xmalloc (sizeof (header) + size);
+ /* address of header */
+
+ ((header *)new)->h.next = last_alloca_header;
+ ((header *)new)->h.deep = depth;
+
+ last_alloca_header = (header *)new;
+
+ /* User storage begins just after header. */
+
+ return (pointer)((char *)new + sizeof(header));
+ }
+}
+
diff --git a/gnu/usr.bin/cvs/lib/argmatch.c b/gnu/usr.bin/cvs/lib/argmatch.c
new file mode 100644
index 0000000..3f765fe
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/argmatch.c
@@ -0,0 +1,83 @@
+/* argmatch.c -- find a match for a string in an array
+ Copyright (C) 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by David MacKenzie <djm@ai.mit.edu> */
+
+#include <stdio.h>
+#ifdef STDC_HEADERS
+#include <string.h>
+#endif
+
+extern char *program_name;
+
+/* If ARG is an unambiguous match for an element of the
+ null-terminated array OPTLIST, return the index in OPTLIST
+ of the matched element, else -1 if it does not match any element
+ or -2 if it is ambiguous (is a prefix of more than one element). */
+
+int
+argmatch (arg, optlist)
+ char *arg;
+ char **optlist;
+{
+ int i; /* Temporary index in OPTLIST. */
+ int arglen; /* Length of ARG. */
+ int matchind = -1; /* Index of first nonexact match. */
+ int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */
+
+ arglen = strlen (arg);
+
+ /* Test all elements for either exact match or abbreviated matches. */
+ for (i = 0; optlist[i]; i++)
+ {
+ if (!strncmp (optlist[i], arg, arglen))
+ {
+ if (strlen (optlist[i]) == arglen)
+ /* Exact match found. */
+ return i;
+ else if (matchind == -1)
+ /* First nonexact match found. */
+ matchind = i;
+ else
+ /* Second nonexact match found. */
+ ambiguous = 1;
+ }
+ }
+ if (ambiguous)
+ return -2;
+ else
+ return matchind;
+}
+
+/* Error reporting for argmatch.
+ KIND is a description of the type of entity that was being matched.
+ VALUE is the invalid value that was given.
+ PROBLEM is the return value from argmatch. */
+
+void
+invalid_arg (kind, value, problem)
+ char *kind;
+ char *value;
+ int problem;
+{
+ fprintf (stderr, "%s: ", program_name);
+ if (problem == -1)
+ fprintf (stderr, "invalid");
+ else /* Assume -2. */
+ fprintf (stderr, "ambiguous");
+ fprintf (stderr, " %s `%s'\n", kind, value);
+}
diff --git a/gnu/usr.bin/cvs/lib/dup2.c b/gnu/usr.bin/cvs/lib/dup2.c
new file mode 100644
index 0000000..0bd3aca
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/dup2.c
@@ -0,0 +1,36 @@
+/*
+ dup2 -- 7th Edition UNIX system call emulation for UNIX System V
+
+ last edit: 11-Feb-1987 D A Gwyn
+*/
+
+#include <errno.h>
+#include <fcntl.h>
+
+extern int close(), fcntl();
+
+int
+dup2( oldfd, newfd )
+ int oldfd; /* already-open file descriptor */
+ int newfd; /* desired duplicate descriptor */
+{
+ register int ret; /* for fcntl() return value */
+ register int save; /* for saving entry errno */
+
+ if ( oldfd == newfd )
+ return oldfd; /* be careful not to close() */
+
+ save = errno; /* save entry errno */
+ (void) close( newfd ); /* in case newfd is open */
+ /* (may have just clobbered the original errno value) */
+
+ ret = fcntl( oldfd, F_DUPFD, newfd ); /* dupe it */
+
+ if ( ret >= 0 )
+ errno = save; /* restore entry errno */
+ else /* fcntl() returned error */
+ if ( errno == EINVAL )
+ errno = EBADF; /* we think of everything */
+
+ return ret; /* return file descriptor */
+}
diff --git a/gnu/usr.bin/cvs/lib/error.c b/gnu/usr.bin/cvs/lib/error.c
new file mode 100644
index 0000000..fadb1c5
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/error.c
@@ -0,0 +1,193 @@
+/* error.c -- error handler for noninteractive utilities
+ Copyright (C) 1990-1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* David MacKenzie */
+/* Brian Berliner added support for CVS */
+
+#ifndef lint
+static char rcsid[] = "@(#)error.c 1.9 92/03/31";
+#endif /* not lint */
+
+#include <stdio.h>
+
+/* turn on CVS support by default, since this is the CVS distribution */
+#define CVS_SUPPORT
+
+#ifdef CVS_SUPPORT
+#if __STDC__
+void Lock_Cleanup(void);
+#else
+void Lock_Cleanup();
+#endif /* __STDC__ */
+#endif /* CVS_SUPPORT */
+
+#ifndef VPRINTF_MISSING
+
+#if __STDC__
+#include <stdarg.h>
+#define VA_START(args, lastarg) va_start(args, lastarg)
+#else
+#include <varargs.h>
+#define VA_START(args, lastarg) va_start(args)
+#endif
+
+#else
+
+#ifndef DOPRNT_MISSING
+#define va_alist args
+#define va_dcl int args;
+#else
+#define va_alist a1, a2, a3, a4, a5, a6, a7, a8
+#define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
+#endif
+
+#endif
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#include <string.h>
+#else
+#if __STDC__
+void exit(int status);
+#else
+void exit ();
+#endif /* __STDC__ */
+#endif
+
+#ifdef STRERROR_MISSING
+static char *
+strerror (errnum)
+ int errnum;
+{
+ extern char *sys_errlist[];
+ extern int sys_nerr;
+
+ if (errnum > 0 && errnum < sys_nerr)
+ return sys_errlist[errnum];
+ return "Unknown system error";
+}
+#endif /* STRERROR_MISSING */
+
+/* Print the program name and error message MESSAGE, which is a printf-style
+ format string with optional args.
+ If ERRNUM is nonzero, print its corresponding system error message.
+ Exit with status STATUS if it is nonzero. */
+/* VARARGS */
+void
+#if !defined (VPRINTF_MISSING) && __STDC__
+error (int status, int errnum, char *message, ...)
+#else
+error (status, errnum, message, va_alist)
+ int status;
+ int errnum;
+ char *message;
+ va_dcl
+#endif
+{
+ extern char *program_name;
+#ifdef CVS_SUPPORT
+ extern char *command_name;
+#endif
+#ifndef VPRINTF_MISSING
+ va_list args;
+#endif
+
+#ifdef CVS_SUPPORT
+ if (command_name && *command_name)
+ if (status)
+ fprintf (stderr, "%s [%s aborted]: ", program_name, command_name);
+ else
+ fprintf (stderr, "%s %s: ", program_name, command_name);
+ else
+ fprintf (stderr, "%s: ", program_name);
+#else
+ fprintf (stderr, "%s: ", program_name);
+#endif
+#ifndef VPRINTF_MISSING
+ VA_START (args, message);
+ vfprintf (stderr, message, args);
+ va_end (args);
+#else
+#ifndef DOPRNT_MISSING
+ _doprnt (message, &args, stderr);
+#else
+ fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif
+#endif
+ if (errnum)
+ fprintf (stderr, ": %s", strerror (errnum));
+ putc ('\n', stderr);
+ fflush (stderr);
+ if (status)
+ {
+#ifdef CVS_SUPPORT
+ Lock_Cleanup();
+#endif
+ exit (status);
+ }
+}
+
+#ifdef CVS_SUPPORT
+
+/* Print the program name and error message MESSAGE, which is a printf-style
+ format string with optional args to the file specified by FP.
+ If ERRNUM is nonzero, print its corresponding system error message.
+ Exit with status STATUS if it is nonzero. */
+/* VARARGS */
+void
+#if !defined (VPRINTF_MISSING) && __STDC__
+fperror (FILE *fp, int status, int errnum, char *message, ...)
+#else
+fperror (fp, status, errnum, message, va_alist)
+ FILE *fp;
+ int status;
+ int errnum;
+ char *message;
+ va_dcl
+#endif
+{
+ extern char *program_name;
+#ifndef VPRINTF_MISSING
+ va_list args;
+#endif
+
+ fprintf (fp, "%s: ", program_name);
+#ifndef VPRINTF_MISSING
+ VA_START (args, message);
+ vfprintf (fp, message, args);
+ va_end (args);
+#else
+#ifndef DOPRNT_MISSING
+ _doprnt (message, &args, fp);
+#else
+ fprintf (fp, message, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif
+#endif
+ if (errnum)
+ fprintf (fp, ": %s", strerror (errnum));
+ putc ('\n', fp);
+ fflush (fp);
+ if (status)
+ {
+#ifdef CVS_SUPPORT
+ Lock_Cleanup();
+#endif
+ exit (status);
+ }
+}
+
+#endif /* CVS_SUPPORT */
diff --git a/gnu/usr.bin/cvs/lib/fnmatch.c b/gnu/usr.bin/cvs/lib/fnmatch.c
new file mode 100644
index 0000000..50fa94c
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/fnmatch.c
@@ -0,0 +1,183 @@
+/* Copyright (C) 1992 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+/* Modified slightly by Brian Berliner <berliner@sun.com> for CVS use */
+
+/* IGNORE(@ */
+/* #include <ansidecl.h> */
+/* @) */
+#include <errno.h>
+#include <fnmatch.h>
+
+#if !defined(__GNU_LIBRARY__) && !defined(STDC_HEADERS)
+extern int errno;
+#endif
+
+#if !__STDC__
+#define const
+#endif
+
+/* Match STRING against the filename pattern PATTERN, returning zero if
+ it matches, nonzero if not. */
+int
+#if __STDC__
+fnmatch (const char *pattern, const char *string, int flags)
+#else
+fnmatch (pattern, string, flags)
+ char *pattern;
+ char *string;
+ int flags;
+#endif
+{
+ register const char *p = pattern, *n = string;
+ register char c;
+
+ if ((flags & ~__FNM_FLAGS) != 0)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ while ((c = *p++) != '\0')
+ {
+ switch (c)
+ {
+ case '?':
+ if (*n == '\0')
+ return FNM_NOMATCH;
+ else if ((flags & FNM_PATHNAME) && *n == '/')
+ return FNM_NOMATCH;
+ else if ((flags & FNM_PERIOD) && *n == '.' &&
+ (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
+ return FNM_NOMATCH;
+ break;
+
+ case '\\':
+ if (!(flags & FNM_NOESCAPE))
+ c = *p++;
+ if (*n != c)
+ return FNM_NOMATCH;
+ break;
+
+ case '*':
+ if ((flags & FNM_PERIOD) && *n == '.' &&
+ (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
+ return FNM_NOMATCH;
+
+ for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
+ if (((flags & FNM_PATHNAME) && *n == '/') ||
+ (c == '?' && *n == '\0'))
+ return FNM_NOMATCH;
+
+ if (c == '\0')
+ return 0;
+
+ {
+ char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
+ for (--p; *n != '\0'; ++n)
+ if ((c == '[' || *n == c1) &&
+ fnmatch(p, n, flags & ~FNM_PERIOD) == 0)
+ return 0;
+ return FNM_NOMATCH;
+ }
+
+ case '[':
+ {
+ /* Nonzero if the sense of the character class is inverted. */
+ register int not;
+
+ if (*n == '\0')
+ return FNM_NOMATCH;
+
+ if ((flags & FNM_PERIOD) && *n == '.' &&
+ (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
+ return FNM_NOMATCH;
+
+ not = (*p == '!' || *p == '^');
+ if (not)
+ ++p;
+
+ c = *p++;
+ for (;;)
+ {
+ register char cstart = c, cend = c;
+
+ if (!(flags & FNM_NOESCAPE) && c == '\\')
+ cstart = cend = *p++;
+
+ if (c == '\0')
+ /* [ (unterminated) loses. */
+ return FNM_NOMATCH;
+
+ c = *p++;
+
+ if ((flags & FNM_PATHNAME) && c == '/')
+ /* [/] can never match. */
+ return FNM_NOMATCH;
+
+ if (c == '-' && *p != ']')
+ {
+ cend = *p++;
+ if (!(flags & FNM_NOESCAPE) && cend == '\\')
+ cend = *p++;
+ if (cend == '\0')
+ return FNM_NOMATCH;
+ c = *p++;
+ }
+
+ if (*n >= cstart && *n <= cend)
+ goto matched;
+
+ if (c == ']')
+ break;
+ }
+ if (!not)
+ return FNM_NOMATCH;
+ break;
+
+ matched:;
+ /* Skip the rest of the [...] that already matched. */
+ while (c != ']')
+ {
+ if (c == '\0')
+ /* [... (unterminated) loses. */
+ return FNM_NOMATCH;
+
+ c = *p++;
+ if (!(flags & FNM_NOESCAPE) && c == '\\')
+ /* 1003.2d11 is unclear if this is right. %%% */
+ ++p;
+ }
+ if (not)
+ return FNM_NOMATCH;
+ }
+ break;
+
+ default:
+ if (c != *n)
+ return FNM_NOMATCH;
+ }
+
+ ++n;
+ }
+
+ if (*n == '\0')
+ return 0;
+
+ return FNM_NOMATCH;
+}
diff --git a/gnu/usr.bin/cvs/lib/fnmatch.h b/gnu/usr.bin/cvs/lib/fnmatch.h
new file mode 100644
index 0000000..a1e4f87
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/fnmatch.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 1992 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#ifndef _FNMATCH_H
+
+#define _FNMATCH_H 1
+
+/* Bits set in the FLAGS argument to `fnmatch'. */
+#undef FNM_PATHNAME
+#define FNM_PATHNAME (1 << 0)/* No wildcard can ever match `/'. */
+#undef FNM_NOESCAPE
+#define FNM_NOESCAPE (1 << 1)/* Backslashes don't quote special chars. */
+#undef FNM_PERIOD
+#define FNM_PERIOD (1 << 2)/* Leading `.' is matched only explicitly. */
+#undef __FNM_FLAGS
+#define __FNM_FLAGS (FNM_PATHNAME|FNM_NOESCAPE|FNM_PERIOD)
+
+/* Value returned by `fnmatch' if STRING does not match PATTERN. */
+#undef FNM_NOMATCH
+#define FNM_NOMATCH 1
+
+/* Match STRING against the filename pattern PATTERN,
+ returning zero if it matches, FNM_NOMATCH if not. */
+#if __STDC__
+extern int fnmatch (const char *pattern, const char *string, int flags);
+#else
+extern int fnmatch ();
+#endif
+
+#endif /* fnmatch.h */
diff --git a/gnu/usr.bin/cvs/lib/ftruncate.c b/gnu/usr.bin/cvs/lib/ftruncate.c
new file mode 100644
index 0000000..17d263d
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/ftruncate.c
@@ -0,0 +1,72 @@
+/* ftruncate emulations that work on some System V's.
+ This file is in the public domain. */
+
+#include <sys/types.h>
+#include <fcntl.h>
+
+#ifdef F_CHSIZE
+int
+ftruncate (fd, length)
+ int fd;
+ off_t length;
+{
+ return fcntl (fd, F_CHSIZE, length);
+}
+#else
+#ifdef F_FREESP
+/* The following function was written by
+ kucharsk@Solbourne.com (William Kucharski) */
+
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+
+int
+ftruncate (fd, length)
+ int fd;
+ off_t length;
+{
+ struct flock fl;
+ struct stat filebuf;
+
+ if (fstat (fd, &filebuf) < 0)
+ return -1;
+
+ if (filebuf.st_size < length)
+ {
+ /* Extend file length. */
+ if (lseek (fd, (length - 1), SEEK_SET) < 0)
+ return -1;
+
+ /* Write a "0" byte. */
+ if (write (fd, "", 1) != 1)
+ return -1;
+ }
+ else
+ {
+ /* Truncate length. */
+ fl.l_whence = 0;
+ fl.l_len = 0;
+ fl.l_start = length;
+ fl.l_type = F_WRLCK; /* Write lock on file space. */
+
+ /* This relies on the UNDOCUMENTED F_FREESP argument to
+ fcntl, which truncates the file so that it ends at the
+ position indicated by fl.l_start.
+ Will minor miracles never cease? */
+ if (fcntl (fd, F_FREESP, &fl) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+#else
+int
+ftruncate (fd, length)
+ int fd;
+ off_t length;
+{
+ return chsize (fd, length);
+}
+#endif
+#endif
diff --git a/gnu/usr.bin/cvs/lib/getdate.y b/gnu/usr.bin/cvs/lib/getdate.y
new file mode 100644
index 0000000..d010cb6
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/getdate.y
@@ -0,0 +1,889 @@
+%{
+/* 1.8
+** @(#)getdate.y 1.8 92/03/03
+**
+** Originally written by Steven M. Bellovin <smb@research.att.com> while
+** at the University of North Carolina at Chapel Hill. Later tweaked by
+** a couple of people on Usenet. Completely overhauled by Rich $alz
+** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
+** send any email to Rich.
+**
+** This grammar has eight shift/reduce conflicts.
+**
+** This code is in the public domain and has no copyright.
+*/
+/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
+/* SUPPRESS 288 on yyerrlab *//* Label unused */
+
+#include "system.h"
+#include <ctype.h>
+
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__)
+#ifdef __GNUC__
+#undef alloca /* might get redefined below */
+#endif
+#endif
+
+extern struct tm *localtime();
+
+#define yyparse getdate_yyparse
+#define yylex getdate_yylex
+#define yyerror getdate_yyerror
+
+#if !defined(lint) && !defined(SABER)
+static char RCS[] = "@(#)getdate.y 1.8 92/03/03";
+#endif /* !defined(lint) && !defined(SABER) */
+
+
+#define EPOCH 1970
+#define HOUR(x) ((time_t)(x) * 60)
+#define SECSPERDAY (24L * 60L * 60L)
+
+
+/*
+** An entry in the lexical lookup table.
+*/
+typedef struct _TABLE {
+ char *name;
+ int type;
+ time_t value;
+} TABLE;
+
+
+/*
+** Daylight-savings mode: on, off, or not yet known.
+*/
+typedef enum _DSTMODE {
+ DSTon, DSToff, DSTmaybe
+} DSTMODE;
+
+/*
+** Meridian: am, pm, or 24-hour style.
+*/
+typedef enum _MERIDIAN {
+ MERam, MERpm, MER24
+} MERIDIAN;
+
+
+/*
+** Global variables. We could get rid of most of these by using a good
+** union as the yacc stack. (This routine was originally written before
+** yacc had the %union construct.) Maybe someday; right now we only use
+** the %union very rarely.
+*/
+static char *yyInput;
+static DSTMODE yyDSTmode;
+static time_t yyDayOrdinal;
+static time_t yyDayNumber;
+static int yyHaveDate;
+static int yyHaveDay;
+static int yyHaveRel;
+static int yyHaveTime;
+static int yyHaveZone;
+static time_t yyTimezone;
+static time_t yyDay;
+static time_t yyHour;
+static time_t yyMinutes;
+static time_t yyMonth;
+static time_t yySeconds;
+static time_t yyYear;
+static MERIDIAN yyMeridian;
+static time_t yyRelMonth;
+static time_t yyRelSeconds;
+
+%}
+
+%union {
+ time_t Number;
+ enum _MERIDIAN Meridian;
+}
+
+%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
+%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
+
+%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
+%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
+%type <Meridian> tMERIDIAN o_merid
+
+%%
+
+spec : /* NULL */
+ | spec item
+ ;
+
+item : time {
+ yyHaveTime++;
+ }
+ | zone {
+ yyHaveZone++;
+ }
+ | date {
+ yyHaveDate++;
+ }
+ | day {
+ yyHaveDay++;
+ }
+ | rel {
+ yyHaveRel++;
+ }
+ | number
+ ;
+
+time : tUNUMBER tMERIDIAN {
+ yyHour = $1;
+ yyMinutes = 0;
+ yySeconds = 0;
+ yyMeridian = $2;
+ }
+ | tUNUMBER ':' tUNUMBER o_merid {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = 0;
+ yyMeridian = $4;
+ }
+ | tUNUMBER ':' tUNUMBER tSNUMBER {
+ yyHour = $1;
+ yyMinutes = $3;
+ yyMeridian = MER24;
+ yyDSTmode = DSToff;
+ yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
+ }
+ | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = $5;
+ yyMeridian = $6;
+ }
+ | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = $5;
+ yyMeridian = MER24;
+ yyDSTmode = DSToff;
+ yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
+ }
+ ;
+
+zone : tZONE {
+ yyTimezone = $1;
+ yyDSTmode = DSToff;
+ }
+ | tDAYZONE {
+ yyTimezone = $1;
+ yyDSTmode = DSTon;
+ }
+ |
+ tZONE tDST {
+ yyTimezone = $1;
+ yyDSTmode = DSTon;
+ }
+ ;
+
+day : tDAY {
+ yyDayOrdinal = 1;
+ yyDayNumber = $1;
+ }
+ | tDAY ',' {
+ yyDayOrdinal = 1;
+ yyDayNumber = $1;
+ }
+ | tUNUMBER tDAY {
+ yyDayOrdinal = $1;
+ yyDayNumber = $2;
+ }
+ ;
+
+date : tUNUMBER '/' tUNUMBER {
+ yyMonth = $1;
+ yyDay = $3;
+ }
+ | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
+ yyMonth = $1;
+ yyDay = $3;
+ yyYear = $5;
+ }
+ | tMONTH tUNUMBER {
+ yyMonth = $1;
+ yyDay = $2;
+ }
+ | tMONTH tUNUMBER ',' tUNUMBER {
+ yyMonth = $1;
+ yyDay = $2;
+ yyYear = $4;
+ }
+ | tUNUMBER tMONTH {
+ yyMonth = $2;
+ yyDay = $1;
+ }
+ | tUNUMBER tMONTH tUNUMBER {
+ yyMonth = $2;
+ yyDay = $1;
+ yyYear = $3;
+ }
+ ;
+
+rel : relunit tAGO {
+ yyRelSeconds = -yyRelSeconds;
+ yyRelMonth = -yyRelMonth;
+ }
+ | relunit
+ ;
+
+relunit : tUNUMBER tMINUTE_UNIT {
+ yyRelSeconds += $1 * $2 * 60L;
+ }
+ | tSNUMBER tMINUTE_UNIT {
+ yyRelSeconds += $1 * $2 * 60L;
+ }
+ | tMINUTE_UNIT {
+ yyRelSeconds += $1 * 60L;
+ }
+ | tSNUMBER tSEC_UNIT {
+ yyRelSeconds += $1;
+ }
+ | tUNUMBER tSEC_UNIT {
+ yyRelSeconds += $1;
+ }
+ | tSEC_UNIT {
+ yyRelSeconds++;
+ }
+ | tSNUMBER tMONTH_UNIT {
+ yyRelMonth += $1 * $2;
+ }
+ | tUNUMBER tMONTH_UNIT {
+ yyRelMonth += $1 * $2;
+ }
+ | tMONTH_UNIT {
+ yyRelMonth += $1;
+ }
+ ;
+
+number : tUNUMBER {
+ if (yyHaveTime && yyHaveDate && !yyHaveRel)
+ yyYear = $1;
+ else {
+ if($1>10000) {
+ time_t date_part;
+
+ date_part= $1/10000;
+ yyHaveDate++;
+ yyDay= (date_part)%100;
+ yyMonth= (date_part/100)%100;
+ yyYear = date_part/10000;
+ }
+ yyHaveTime++;
+ if ($1 < 100) {
+ yyHour = $1;
+ yyMinutes = 0;
+ }
+ else {
+ yyHour = $1 / 100;
+ yyMinutes = $1 % 100;
+ }
+ yySeconds = 0;
+ yyMeridian = MER24;
+ }
+ }
+ ;
+
+o_merid : /* NULL */ {
+ $$ = MER24;
+ }
+ | tMERIDIAN {
+ $$ = $1;
+ }
+ ;
+
+%%
+
+/* Month and day table. */
+static TABLE MonthDayTable[] = {
+ { "january", tMONTH, 1 },
+ { "february", tMONTH, 2 },
+ { "march", tMONTH, 3 },
+ { "april", tMONTH, 4 },
+ { "may", tMONTH, 5 },
+ { "june", tMONTH, 6 },
+ { "july", tMONTH, 7 },
+ { "august", tMONTH, 8 },
+ { "september", tMONTH, 9 },
+ { "sept", tMONTH, 9 },
+ { "october", tMONTH, 10 },
+ { "november", tMONTH, 11 },
+ { "december", tMONTH, 12 },
+ { "sunday", tDAY, 0 },
+ { "monday", tDAY, 1 },
+ { "tuesday", tDAY, 2 },
+ { "tues", tDAY, 2 },
+ { "wednesday", tDAY, 3 },
+ { "wednes", tDAY, 3 },
+ { "thursday", tDAY, 4 },
+ { "thur", tDAY, 4 },
+ { "thurs", tDAY, 4 },
+ { "friday", tDAY, 5 },
+ { "saturday", tDAY, 6 },
+ { NULL }
+};
+
+/* Time units table. */
+static TABLE UnitsTable[] = {
+ { "year", tMONTH_UNIT, 12 },
+ { "month", tMONTH_UNIT, 1 },
+ { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
+ { "week", tMINUTE_UNIT, 7 * 24 * 60 },
+ { "day", tMINUTE_UNIT, 1 * 24 * 60 },
+ { "hour", tMINUTE_UNIT, 60 },
+ { "minute", tMINUTE_UNIT, 1 },
+ { "min", tMINUTE_UNIT, 1 },
+ { "second", tSEC_UNIT, 1 },
+ { "sec", tSEC_UNIT, 1 },
+ { NULL }
+};
+
+/* Assorted relative-time words. */
+static TABLE OtherTable[] = {
+ { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
+ { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
+ { "today", tMINUTE_UNIT, 0 },
+ { "now", tMINUTE_UNIT, 0 },
+ { "last", tUNUMBER, -1 },
+ { "this", tMINUTE_UNIT, 0 },
+ { "next", tUNUMBER, 2 },
+ { "first", tUNUMBER, 1 },
+/* { "second", tUNUMBER, 2 }, */
+ { "third", tUNUMBER, 3 },
+ { "fourth", tUNUMBER, 4 },
+ { "fifth", tUNUMBER, 5 },
+ { "sixth", tUNUMBER, 6 },
+ { "seventh", tUNUMBER, 7 },
+ { "eighth", tUNUMBER, 8 },
+ { "ninth", tUNUMBER, 9 },
+ { "tenth", tUNUMBER, 10 },
+ { "eleventh", tUNUMBER, 11 },
+ { "twelfth", tUNUMBER, 12 },
+ { "ago", tAGO, 1 },
+ { NULL }
+};
+
+/* The timezone table. */
+/* Some of these are commented out because a time_t can't store a float. */
+static TABLE TimezoneTable[] = {
+ { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
+ { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
+ { "utc", tZONE, HOUR( 0) },
+ { "wet", tZONE, HOUR( 0) }, /* Western European */
+ { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
+ { "wat", tZONE, HOUR( 1) }, /* West Africa */
+ { "at", tZONE, HOUR( 2) }, /* Azores */
+#if 0
+ /* For completeness. BST is also British Summer, and GST is
+ * also Guam Standard. */
+ { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
+ { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
+#endif
+#if 0
+ { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
+ { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
+ { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
+#endif
+ { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
+ { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
+ { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
+ { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
+ { "cst", tZONE, HOUR( 6) }, /* Central Standard */
+ { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
+ { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
+ { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
+ { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
+ { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
+ { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
+ { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
+ { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
+ { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
+ { "cat", tZONE, HOUR(10) }, /* Central Alaska */
+ { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
+ { "nt", tZONE, HOUR(11) }, /* Nome */
+ { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
+ { "cet", tZONE, -HOUR(1) }, /* Central European */
+ { "met", tZONE, -HOUR(1) }, /* Middle European */
+ { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
+ { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
+ { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
+ { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
+ { "fwt", tZONE, -HOUR(1) }, /* French Winter */
+ { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
+ { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
+ { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
+#if 0
+ { "it", tZONE, -HOUR(3.5) },/* Iran */
+#endif
+ { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
+ { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
+#if 0
+ { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
+#endif
+ { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
+#if 0
+ /* For completeness. NST is also Newfoundland Stanard, and SST is
+ * also Swedish Summer. */
+ { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
+ { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
+#endif /* 0 */
+ { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
+ { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
+#if 0
+ { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
+#endif
+ { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
+ { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
+#if 0
+ { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
+ { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
+#endif
+ { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
+ { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
+ { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
+ { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
+ { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
+ { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
+ { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
+ { NULL }
+};
+
+/* Military timezone table. */
+static TABLE MilitaryTable[] = {
+ { "a", tZONE, HOUR( 1) },
+ { "b", tZONE, HOUR( 2) },
+ { "c", tZONE, HOUR( 3) },
+ { "d", tZONE, HOUR( 4) },
+ { "e", tZONE, HOUR( 5) },
+ { "f", tZONE, HOUR( 6) },
+ { "g", tZONE, HOUR( 7) },
+ { "h", tZONE, HOUR( 8) },
+ { "i", tZONE, HOUR( 9) },
+ { "k", tZONE, HOUR( 10) },
+ { "l", tZONE, HOUR( 11) },
+ { "m", tZONE, HOUR( 12) },
+ { "n", tZONE, HOUR(- 1) },
+ { "o", tZONE, HOUR(- 2) },
+ { "p", tZONE, HOUR(- 3) },
+ { "q", tZONE, HOUR(- 4) },
+ { "r", tZONE, HOUR(- 5) },
+ { "s", tZONE, HOUR(- 6) },
+ { "t", tZONE, HOUR(- 7) },
+ { "u", tZONE, HOUR(- 8) },
+ { "v", tZONE, HOUR(- 9) },
+ { "w", tZONE, HOUR(-10) },
+ { "x", tZONE, HOUR(-11) },
+ { "y", tZONE, HOUR(-12) },
+ { "z", tZONE, HOUR( 0) },
+ { NULL }
+};
+
+
+
+
+/* ARGSUSED */
+int
+yyerror(s)
+ char *s;
+{
+ return 0;
+}
+
+
+static time_t
+ToSeconds(Hours, Minutes, Seconds, Meridian)
+ time_t Hours;
+ time_t Minutes;
+ time_t Seconds;
+ MERIDIAN Meridian;
+{
+ if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
+ return -1;
+ switch (Meridian) {
+ case MER24:
+ if (Hours < 0 || Hours > 23)
+ return -1;
+ return (Hours * 60L + Minutes) * 60L + Seconds;
+ case MERam:
+ if (Hours < 1 || Hours > 12)
+ return -1;
+ return (Hours * 60L + Minutes) * 60L + Seconds;
+ case MERpm:
+ if (Hours < 1 || Hours > 12)
+ return -1;
+ return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
+ }
+ /* NOTREACHED */
+}
+
+
+static time_t
+Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
+ time_t Month;
+ time_t Day;
+ time_t Year;
+ time_t Hours;
+ time_t Minutes;
+ time_t Seconds;
+ MERIDIAN Meridian;
+ DSTMODE DSTmode;
+{
+ static int DaysInMonth[12] = {
+ 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ time_t tod;
+ time_t Julian;
+ int i;
+
+ if (Year < 0)
+ Year = -Year;
+ if (Year < 100)
+ Year += 1900;
+ DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
+ ? 29 : 28;
+ if (Year < EPOCH || Year > 1999
+ || Month < 1 || Month > 12
+ /* Lint fluff: "conversion from long may lose accuracy" */
+ || Day < 1 || Day > DaysInMonth[(int)--Month])
+ return -1;
+
+ for (Julian = Day - 1, i = 0; i < Month; i++)
+ Julian += DaysInMonth[i];
+ for (i = EPOCH; i < Year; i++)
+ Julian += 365 + (i % 4 == 0);
+ Julian *= SECSPERDAY;
+ Julian += yyTimezone * 60L;
+ if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
+ return -1;
+ Julian += tod;
+ if (DSTmode == DSTon
+ || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
+ Julian -= 60 * 60;
+ return Julian;
+}
+
+
+static time_t
+DSTcorrect(Start, Future)
+ time_t Start;
+ time_t Future;
+{
+ time_t StartDay;
+ time_t FutureDay;
+
+ StartDay = (localtime(&Start)->tm_hour + 1) % 24;
+ FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
+ return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
+}
+
+
+static time_t
+RelativeDate(Start, DayOrdinal, DayNumber)
+ time_t Start;
+ time_t DayOrdinal;
+ time_t DayNumber;
+{
+ struct tm *tm;
+ time_t now;
+
+ now = Start;
+ tm = localtime(&now);
+ now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
+ now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
+ return DSTcorrect(Start, now);
+}
+
+
+static time_t
+RelativeMonth(Start, RelMonth)
+ time_t Start;
+ time_t RelMonth;
+{
+ struct tm *tm;
+ time_t Month;
+ time_t Year;
+
+ if (RelMonth == 0)
+ return 0;
+ tm = localtime(&Start);
+ Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
+ Year = Month / 12;
+ Month = Month % 12 + 1;
+ return DSTcorrect(Start,
+ Convert(Month, (time_t)tm->tm_mday, Year,
+ (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
+ MER24, DSTmaybe));
+}
+
+
+static int
+LookupWord(buff)
+ char *buff;
+{
+ register char *p;
+ register char *q;
+ register TABLE *tp;
+ int i;
+ int abbrev;
+
+ /* Make it lowercase. */
+ for (p = buff; *p; p++)
+ if (isupper(*p))
+ *p = tolower(*p);
+
+ if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
+ yylval.Meridian = MERam;
+ return tMERIDIAN;
+ }
+ if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
+ yylval.Meridian = MERpm;
+ return tMERIDIAN;
+ }
+
+ /* See if we have an abbreviation for a month. */
+ if (strlen(buff) == 3)
+ abbrev = 1;
+ else if (strlen(buff) == 4 && buff[3] == '.') {
+ abbrev = 1;
+ buff[3] = '\0';
+ }
+ else
+ abbrev = 0;
+
+ for (tp = MonthDayTable; tp->name; tp++) {
+ if (abbrev) {
+ if (strncmp(buff, tp->name, 3) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+ else if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ for (tp = TimezoneTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ if (strcmp(buff, "dst") == 0)
+ return tDST;
+
+ for (tp = UnitsTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ /* Strip off any plural and try the units table again. */
+ i = strlen(buff) - 1;
+ if (buff[i] == 's') {
+ buff[i] = '\0';
+ for (tp = UnitsTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ buff[i] = 's'; /* Put back for "this" in OtherTable. */
+ }
+
+ for (tp = OtherTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ /* Military timezones. */
+ if (buff[1] == '\0' && isalpha(*buff)) {
+ for (tp = MilitaryTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ /* Drop out any periods and try the timezone table again. */
+ for (i = 0, p = q = buff; *q; q++)
+ if (*q != '.')
+ *p++ = *q;
+ else
+ i++;
+ *p = '\0';
+ if (i)
+ for (tp = TimezoneTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ return tID;
+}
+
+
+int
+yylex()
+{
+ register char c;
+ register char *p;
+ char buff[20];
+ int Count;
+ int sign;
+
+ for ( ; ; ) {
+ while (isspace(*yyInput))
+ yyInput++;
+
+ if (isdigit(c = *yyInput) || c == '-' || c == '+') {
+ if (c == '-' || c == '+') {
+ sign = c == '-' ? -1 : 1;
+ if (!isdigit(*++yyInput))
+ /* skip the '-' sign */
+ continue;
+ }
+ else
+ sign = 0;
+ for (yylval.Number = 0; isdigit(c = *yyInput++); )
+ yylval.Number = 10 * yylval.Number + c - '0';
+ yyInput--;
+ if (sign < 0)
+ yylval.Number = -yylval.Number;
+ return sign ? tSNUMBER : tUNUMBER;
+ }
+ if (isalpha(c)) {
+ for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
+ if (p < &buff[sizeof buff - 1])
+ *p++ = c;
+ *p = '\0';
+ yyInput--;
+ return LookupWord(buff);
+ }
+ if (c != '(')
+ return *yyInput++;
+ Count = 0;
+ do {
+ c = *yyInput++;
+ if (c == '\0')
+ return c;
+ if (c == '(')
+ Count++;
+ else if (c == ')')
+ Count--;
+ } while (Count > 0);
+ }
+}
+
+
+time_t
+get_date(p, now)
+ char *p;
+ struct timeb *now;
+{
+ struct tm *tm;
+ struct timeb ftz;
+ time_t Start;
+ time_t tod;
+
+ yyInput = p;
+ if (now == NULL) {
+ now = &ftz;
+#if defined(FTIME_MISSING)
+ (void)time(&ftz.time);
+ /* Set the timezone global. */
+ tzset();
+#if defined(HAVE_TIMEZONE)
+ tm = localtime(&ftz.time);
+ ftz.timezone = tm->tm_gmtoff / 60;
+#else
+#if defined(timezone)
+ ftz.tzone = (int) timezone / 60;
+#else
+ ftz.timezone = (int) timezone / 60;
+#endif /* defined(timezone) */
+#endif /* defined(HAVE_TIMEZONE) */
+#else
+ (void)ftime(&ftz);
+#endif /* defined(FTIME_MISSING) */
+ }
+
+ tm = localtime(&now->time);
+ yyYear = tm->tm_year;
+ yyMonth = tm->tm_mon + 1;
+ yyDay = tm->tm_mday;
+#if defined(timezone)
+ yyTimezone = now->tzone;
+#else
+ yyTimezone = now->timezone;
+#endif /* defined(timezone) */
+ yyDSTmode = DSTmaybe;
+ yyHour = 0;
+ yyMinutes = 0;
+ yySeconds = 0;
+ yyMeridian = MER24;
+ yyRelSeconds = 0;
+ yyRelMonth = 0;
+ yyHaveDate = 0;
+ yyHaveDay = 0;
+ yyHaveRel = 0;
+ yyHaveTime = 0;
+ yyHaveZone = 0;
+
+ if (yyparse()
+ || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
+ return -1;
+
+ if (yyHaveDate || yyHaveTime || yyHaveDay) {
+ Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
+ yyMeridian, yyDSTmode);
+ if (Start < 0)
+ return -1;
+ }
+ else {
+ Start = now->time;
+ if (!yyHaveRel)
+ Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
+ }
+
+ Start += yyRelSeconds;
+ Start += RelativeMonth(Start, yyRelMonth);
+
+ if (yyHaveDay && !yyHaveDate) {
+ tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
+ Start += tod;
+ }
+
+ /* Have to do *something* with a legitimate -1 so it's distinguishable
+ * from the error return value. (Alternately could set errno on error.) */
+ return Start == -1 ? 0 : Start;
+}
+
+
+#if defined(TEST)
+
+/* ARGSUSED */
+main(ac, av)
+ int ac;
+ char *av[];
+{
+ char buff[128];
+ time_t d;
+
+ (void)printf("Enter date, or blank line to exit.\n\t> ");
+ (void)fflush(stdout);
+ while (gets(buff) && buff[0]) {
+ d = get_date(buff, (struct timeb *)NULL);
+ if (d == -1)
+ (void)printf("Bad format - couldn't convert.\n");
+ else
+ (void)printf("%s", ctime(&d));
+ (void)printf("\t> ");
+ (void)fflush(stdout);
+ }
+ exit(0);
+ /* NOTREACHED */
+}
+#endif /* defined(TEST) */
diff --git a/gnu/usr.bin/cvs/lib/getopt.c b/gnu/usr.bin/cvs/lib/getopt.c
new file mode 100644
index 0000000..c322fc2
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/getopt.c
@@ -0,0 +1,604 @@
+/* Getopt for GNU.
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !__STDC__
+#define const
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of `argv' so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable _POSIX_OPTION_ORDER disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#ifndef lint
+static char rcsid[] = "@(#)getopt.c 1.7 92/03/31";
+#endif
+
+#include <stdio.h>
+
+#if defined(STDC_HEADERS) || defined(__GNU_LIBRARY__)
+#include <stdlib.h>
+#else /* STDC_HEADERS or __GNU_LIBRARY__ */
+char *getenv ();
+char *malloc ();
+#endif /* STDC_HEADERS or __GNU_LIBRARY__ */
+
+/* AIX requires this to be the first thing in the file. */
+#ifdef __GNUC__
+#if !defined(bsdi) && !defined(__386BSD__)
+#define alloca __builtin_alloca
+#endif
+#else /* not __GNUC__ */
+#ifdef sparc
+#include <alloca.h>
+#else
+#ifdef _AIX
+ #pragma alloca
+#else
+char *alloca ();
+#endif
+#endif /* sparc */
+#endif /* not __GNUC__ */
+
+#if defined(USG) || defined(STDC_HEADERS) || defined(__GNU_LIBRARY__)
+#include <string.h>
+#ifndef bcopy
+#define bcopy(s, d, n) memcpy ((d), (s), (n))
+#endif
+#ifndef index
+#define index strchr
+#endif
+#else /* USG or STDC_HEADERS or __GNU_LIBRARY__ */
+#ifdef VMS
+#include <string.h>
+#else /* VMS */
+#include <strings.h>
+#endif /* VMS */
+/* Declaring bcopy causes errors on systems whose declarations are different.
+ If the declaration is omitted, everything works fine. */
+#endif /* USG or STDC_HEADERS or __GNU_LIBRARY__ */
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = 0;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ _POSIX_OPTION_ORDER is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIX_ME_HARDER, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return EOF with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Describe the long-named options requested by the application.
+ _GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+ The field `has_arg' is 1 if the option takes an argument,
+ 2 if it takes an optional argument. */
+
+struct option
+{
+ char *name;
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+const struct option *_getopt_long_options;
+
+int _getopt_long_only = 0;
+
+/* Index in _GETOPT_LONG_OPTIONS of the long-named option actually found.
+ Only valid when a long-named option was found. */
+
+int option_index;
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *);
+ char **temp = (char **) alloca (nonopts_size);
+
+ /* Interchange the two blocks of data in ARGV. */
+
+ bcopy (&argv[first_nonopt], temp, nonopts_size);
+ bcopy (&argv[last_nonopt], &argv[first_nonopt],
+ (optind - last_nonopt) * sizeof (char *));
+ bcopy (temp, &argv[first_nonopt + optind - last_nonopt], nonopts_size);
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns `EOF'.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `+' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ otherwise. */
+
+int
+gnu_getopt (argc, argv, optstring)
+ int argc;
+ char **argv;
+ const char *optstring;
+{
+ optarg = 0;
+
+ /* Initialize the internal data when the first call is made.
+ Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ if (optind == 0)
+ {
+ first_nonopt = last_nonopt = optind = 1;
+
+ nextchar = 0;
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (getenv ("POSIX_ME_HARDER") != 0)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+ }
+
+ if (nextchar == 0 || *nextchar == 0)
+ {
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange (argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Now skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc
+ && (argv[optind][0] != '-'
+ || argv[optind][1] == 0)
+ && (_getopt_long_options == 0
+ || argv[optind][0] != '+'
+ || argv[optind][1] == 0))
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* Special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange (argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return EOF;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if ((argv[optind][0] != '-' || argv[optind][1] == 0)
+ && (_getopt_long_options == 0
+ || argv[optind][0] != '+' || argv[optind][1] == 0))
+ {
+ if (ordering == REQUIRE_ORDER)
+ return EOF;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Start decoding its characters. */
+
+ nextchar = argv[optind] + 1;
+ }
+
+ if (_getopt_long_options != 0
+ && (argv[optind][0] == '+'
+ || (_getopt_long_only && argv[optind][0] == '-'))
+ )
+ {
+ const struct option *p;
+ char *s = nextchar;
+ int exact = 0;
+ int ambig = 0;
+ const struct option *pfound = 0;
+ int indfound = 0;
+
+ while (*s && *s != '=')
+ s++;
+
+ /* Test all options for either exact match or abbreviated matches. */
+ for (p = _getopt_long_options, option_index = 0; p->name;
+ p++, option_index++)
+ if (!strncmp (p->name, nextchar, s - nextchar))
+ {
+ if (s - nextchar == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == 0)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ fprintf (stderr, "%s: option `%s' is ambiguous\n",
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+
+ if (pfound != 0)
+ {
+ option_index = indfound;
+ optind++;
+ if (*s)
+ {
+ if (pfound->has_arg > 0)
+ optarg = s + 1;
+ else
+ {
+ fprintf (stderr,
+ "%s: option `%c%s' doesn't allow an argument\n",
+ argv[0], argv[optind - 1][0], pfound->name);
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ fprintf (stderr, "%s: option `%s' requires an argument\n",
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ /* Can't find it as a long option. If this is getopt_long_only,
+ and the option starts with '-' and is a valid short
+ option, then interpret it as a short option. Otherwise it's
+ an error. */
+ if (_getopt_long_only == 0 || argv[optind][0] == '+' ||
+ index (optstring, *nextchar) == 0)
+ {
+ if (opterr != 0)
+ fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+ argv[0], argv[optind][0], nextchar);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == 0)
+ optind++;
+
+ if (temp == 0 || c == ':')
+ {
+ if (opterr != 0)
+ {
+ if (c < 040 || c >= 0177)
+ fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
+ argv[0], c);
+ else
+ fprintf (stderr, "%s: unrecognized option `-%c'\n",
+ argv[0], c);
+ }
+ return '?';
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != 0)
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = 0;
+ nextchar = 0;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != 0)
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr != 0)
+ fprintf (stderr, "%s: option `-%c' requires an argument\n",
+ argv[0], c);
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = 0;
+ }
+ }
+ return c;
+ }
+}
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = gnu_getopt (argc, argv, "abc:d:0123456789");
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/gnu/usr.bin/cvs/lib/getopt.h b/gnu/usr.bin/cvs/lib/getopt.h
new file mode 100644
index 0000000..5f902de
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/getopt.h
@@ -0,0 +1,102 @@
+/* declarations for getopt
+ Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* @(#)getopt.h 1.6 92/03/31 */
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Describe the long-named options requested by the application.
+ _GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ The field `has_arg' is:
+ 0 if the option does not take an argument,
+ 1 if the option requires an argument,
+ 2 if the option takes an optional argument.
+
+ If the field `flag' is nonzero, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+ char *name;
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+#if __STDC__
+extern const struct option *_getopt_long_options;
+#else
+extern struct option *_getopt_long_options;
+#endif
+
+/* If nonzero, '-' can introduce long-named options.
+ Set by getopt_long_only. */
+
+extern int _getopt_long_only;
+
+/* The index in GETOPT_LONG_OPTIONS of the long-named option found.
+ Only valid when a long-named option has been found by the most
+ recent call to `getopt'. */
+
+extern int option_index;
+
+#if __STDC__
+int gnu_getopt (int argc, char **argv, const char *shortopts);
+int gnu_getopt_long (int argc, char **argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+int gnu_getopt_long_only (int argc, char **argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+#else
+int gnu_getopt ();
+int gnu_getopt_long ();
+int gnu_getopt_long_only ();
+#endif
diff --git a/gnu/usr.bin/cvs/lib/getopt1.c b/gnu/usr.bin/cvs/lib/getopt1.c
new file mode 100644
index 0000000..8606462
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/getopt1.c
@@ -0,0 +1,166 @@
+/* Getopt for GNU.
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "getopt.h"
+
+#if !__STDC__
+#define const
+#endif
+
+#if defined(STDC_HEADERS) || defined(__GNU_LIBRARY__)
+#include <stdlib.h>
+#else /* STDC_HEADERS or __GNU_LIBRARY__ */
+char *getenv ();
+#endif /* STDC_HEADERS or __GNU_LIBRARY__ */
+
+#if !defined (NULL)
+#define NULL 0
+#endif
+
+int
+gnu_getopt_long (argc, argv, options, long_options, opt_index)
+ int argc;
+ char **argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ int val;
+
+ /* For strict POSIX compatibility, we must turn off long options. */
+ if (getenv ("POSIX_ME_HARDER") == 0)
+ _getopt_long_options = long_options;
+ val = gnu_getopt (argc, argv, options);
+ if (val == 0 && opt_index != NULL)
+ *opt_index = option_index;
+ return val;
+}
+
+/* Like getopt_long, but '-' as well as '+' can indicate a long option.
+ If an option that starts with '-' doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+gnu_getopt_long_only (argc, argv, options, long_options, opt_index)
+ int argc;
+ char **argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ int val;
+
+ _getopt_long_options = long_options;
+ _getopt_long_only = 1;
+ val = gnu_getopt (argc, argv, options);
+ if (val == 0 && opt_index != NULL)
+ *opt_index = option_index;
+ return val;
+}
+
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ char *name = '\0';
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", (long_options[option_index]).name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/gnu/usr.bin/cvs/lib/getwd.c b/gnu/usr.bin/cvs/lib/getwd.c
new file mode 100644
index 0000000..854feaf
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/getwd.c
@@ -0,0 +1,31 @@
+/* getwd.c -- get current working directory pathname
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Some systems which include both getwd() and getcwd() have an implementation
+ of getwd() which is much faster than getcwd(). As a result, we use the
+ system's getwd() if it is available */
+
+#include "system.h"
+
+/* Get the current working directory into PATHNAME */
+
+char *
+getwd (pathname)
+ char *pathname;
+{
+ return (getcwd(pathname, PATH_MAX));
+}
diff --git a/gnu/usr.bin/cvs/lib/hash.c b/gnu/usr.bin/cvs/lib/hash.c
new file mode 100644
index 0000000..fb29497
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/hash.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Polk's hash list manager. So cool.
+ */
+
+#include "cvs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#)hash.c 1.14 92/03/31";
+#endif
+
+/* global caches */
+static List *listcache = NULL;
+static Node *nodecache = NULL;
+
+#if __STDC__
+static void freenode_mem (Node * p);
+#else
+static void freenode_mem ();
+#endif /* __STDC__ */
+
+/* hash function */
+static int
+hashp (key)
+ char *key;
+{
+ register char *p;
+ register int n = 0;
+
+ for (p = key; *p; p++)
+ n += *p;
+
+ return (n % HASHSIZE);
+}
+
+/*
+ * create a new list (or get an old one from the cache)
+ */
+List *
+getlist ()
+{
+ int i;
+ List *list;
+ Node *node;
+
+ if (listcache != NULL)
+ {
+ /* get a list from the cache and clear it */
+ list = listcache;
+ listcache = listcache->next;
+ list->next = (List *) NULL;
+ for (i = 0; i < HASHSIZE; i++)
+ list->hasharray[i] = (Node *) NULL;
+ }
+ else
+ {
+ /* make a new list from scratch */
+ list = (List *) xmalloc (sizeof (List));
+ bzero ((char *) list, sizeof (List));
+ node = getnode ();
+ list->list = node;
+ node->type = HEADER;
+ node->next = node->prev = node;
+ }
+ return (list);
+}
+
+/*
+ * free up a list
+ */
+void
+dellist (listp)
+ List **listp;
+{
+ int i;
+ Node *p;
+
+ if (*listp == (List *) NULL)
+ return;
+
+ p = (*listp)->list;
+
+ /* free each node in the list (except header) */
+ while (p->next != p)
+ delnode (p->next);
+
+ /* free any list-private data, without freeing the actual header */
+ freenode_mem (p);
+
+ /* free up the header nodes for hash lists (if any) */
+ for (i = 0; i < HASHSIZE; i++)
+ {
+ if ((p = (*listp)->hasharray[i]) != (Node *) NULL)
+ {
+ /* put the nodes into the cache */
+ p->type = UNKNOWN;
+ p->next = nodecache;
+ nodecache = p;
+ }
+ }
+
+ /* put it on the cache */
+ (*listp)->next = listcache;
+ listcache = *listp;
+ *listp = (List *) NULL;
+}
+
+/*
+ * get a new list node
+ */
+Node *
+getnode ()
+{
+ Node *p;
+
+ if (nodecache != (Node *) NULL)
+ {
+ /* get one from the cache */
+ p = nodecache;
+ nodecache = p->next;
+ }
+ else
+ {
+ /* make a new one */
+ p = (Node *) xmalloc (sizeof (Node));
+ }
+
+ /* always make it clean */
+ bzero ((char *) p, sizeof (Node));
+ p->type = UNKNOWN;
+
+ return (p);
+}
+
+/*
+ * remove a node from it's list (maybe hash list too) and free it
+ */
+void
+delnode (p)
+ Node *p;
+{
+ if (p == (Node *) NULL)
+ return;
+
+ /* take it out of the list */
+ p->next->prev = p->prev;
+ p->prev->next = p->next;
+
+ /* if it was hashed, remove it from there too */
+ if (p->hashnext != (Node *) NULL)
+ {
+ p->hashnext->hashprev = p->hashprev;
+ p->hashprev->hashnext = p->hashnext;
+ }
+
+ /* free up the storage */
+ freenode (p);
+}
+
+/*
+ * free up the storage associated with a node
+ */
+static void
+freenode_mem (p)
+ Node *p;
+{
+ if (p->delproc != (void (*) ()) NULL)
+ p->delproc (p); /* call the specified delproc */
+ else
+ {
+ if (p->data != NULL) /* otherwise free() it if necessary */
+ free (p->data);
+ }
+ if (p->key != NULL) /* free the key if necessary */
+ free (p->key);
+
+ /* to be safe, re-initialize these */
+ p->key = p->data = (char *) NULL;
+ p->delproc = (void (*) ()) NULL;
+}
+
+/*
+ * free up the storage associated with a node and recycle it
+ */
+void
+freenode (p)
+ Node *p;
+{
+ /* first free the memory */
+ freenode_mem (p);
+
+ /* then put it in the cache */
+ p->type = UNKNOWN;
+ p->next = nodecache;
+ nodecache = p;
+}
+
+/*
+ * insert item p at end of list "list" (maybe hash it too) if hashing and it
+ * already exists, return -1 and don't actually put it in the list
+ *
+ * return 0 on success
+ */
+int
+addnode (list, p)
+ List *list;
+ Node *p;
+{
+ int hashval;
+ Node *q;
+
+ if (p->key != NULL) /* hash it too? */
+ {
+ hashval = hashp (p->key);
+ if (list->hasharray[hashval] == NULL) /* make a header for list? */
+ {
+ q = getnode ();
+ q->type = HEADER;
+ list->hasharray[hashval] = q->hashnext = q->hashprev = q;
+ }
+
+ /* put it into the hash list if it's not already there */
+ for (q = list->hasharray[hashval]->hashnext;
+ q != list->hasharray[hashval]; q = q->hashnext)
+ {
+ if (strcmp (p->key, q->key) == 0)
+ return (-1);
+ }
+ q = list->hasharray[hashval];
+ p->hashprev = q->hashprev;
+ p->hashnext = q;
+ p->hashprev->hashnext = p;
+ q->hashprev = p;
+ }
+
+ /* put it into the regular list */
+ p->prev = list->list->prev;
+ p->next = list->list;
+ list->list->prev->next = p;
+ list->list->prev = p;
+
+ return (0);
+}
+
+/*
+ * look up an entry in hash list table
+ */
+Node *
+findnode (list, key)
+ List *list;
+ char *key;
+{
+ Node *head, *p;
+
+ if (list == (List *) NULL)
+ return ((Node *) NULL);
+
+ head = list->hasharray[hashp (key)];
+ if (head == (Node *) NULL)
+ return ((Node *) NULL);
+
+ for (p = head->hashnext; p != head; p = p->hashnext)
+ if (strcmp (p->key, key) == 0)
+ return (p);
+ return ((Node *) NULL);
+}
+
+/*
+ * walk a list with a specific proc
+ */
+int
+walklist (list, proc)
+ List *list;
+ int (*proc) ();
+{
+ Node *head, *p;
+ int err = 0;
+
+ if (list == NULL)
+ return (0);
+
+ head = list->list;
+ for (p = head->next; p != head; p = p->next)
+ err += proc (p);
+ return (err);
+}
+
+/*
+ * sort the elements of a list (in place)
+ */
+void
+sortlist (list, comp)
+ List *list;
+ int (*comp) ();
+{
+ Node *head, *remain, *p, *q;
+
+ /* save the old first element of the list */
+ head = list->list;
+ remain = head->next;
+
+ /* make the header node into a null list of it's own */
+ head->next = head->prev = head;
+
+ /* while there are nodes remaining, do insert sort */
+ while (remain != head)
+ {
+ /* take one from the list */
+ p = remain;
+ remain = remain->next;
+
+ /* traverse the sorted list looking for the place to insert it */
+ for (q = head->next; q != head; q = q->next)
+ {
+ if (comp (p, q) < 0)
+ {
+ /* p comes before q */
+ p->next = q;
+ p->prev = q->prev;
+ p->prev->next = p;
+ q->prev = p;
+ break;
+ }
+ }
+ if (q == head)
+ {
+ /* it belongs at the end of the list */
+ p->next = head;
+ p->prev = head->prev;
+ p->prev->next = p;
+ head->prev = p;
+ }
+ }
+}
diff --git a/gnu/usr.bin/cvs/lib/hash.h b/gnu/usr.bin/cvs/lib/hash.h
new file mode 100644
index 0000000..54f227e
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/hash.h
@@ -0,0 +1,77 @@
+/* @(#)hash.h 1.18 92/03/31 */
+
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ */
+
+/*
+ * The number of buckets for the hash table contained in each list. This
+ * should probably be prime.
+ */
+#define HASHSIZE 151
+
+/*
+ * Types of nodes
+ */
+enum ntype
+{
+ UNKNOWN, HEADER, ENTRIES, FILES, LIST, RCSNODE,
+ RCSVERS, DIRS, UPDATE, LOCK, NDBMNODE
+};
+typedef enum ntype Ntype;
+
+struct node
+{
+ Ntype type;
+ struct node *next;
+ struct node *prev;
+ struct node *hashnext;
+ struct node *hashprev;
+ char *key;
+ char *data;
+ void (*delproc) ();
+};
+typedef struct node Node;
+
+struct list
+{
+ Node *list;
+ Node *hasharray[HASHSIZE];
+ struct list *next;
+};
+typedef struct list List;
+
+struct entnode
+{
+ char *version;
+ char *timestamp;
+ char *options;
+ char *tag;
+ char *date;
+};
+typedef struct entnode Entnode;
+
+#if __STDC__
+List *getlist (void);
+Node *findnode (List * list, char *key);
+Node *getnode (void);
+int addnode (List * list, Node * p);
+int walklist (List * list, int (*proc) ());
+void dellist (List ** listp);
+void delnode (Node * p);
+void freenode (Node * p);
+void sortlist (List * list, int (*comp) ());
+#else
+List *getlist ();
+Node *findnode ();
+Node *getnode ();
+int addnode ();
+int walklist ();
+void dellist ();
+void delnode ();
+void freenode ();
+void sortlist ();
+#endif /* __STDC__ */
diff --git a/gnu/usr.bin/cvs/lib/mkdir.c b/gnu/usr.bin/cvs/lib/mkdir.c
new file mode 100644
index 0000000..b17cca2
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/mkdir.c
@@ -0,0 +1,125 @@
+/* mkrmdir.c -- BSD compatible directory functions for System V
+ Copyright (C) 1988, 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+
+/* mkdir and rmdir adapted from GNU tar. */
+
+/* Make directory DPATH, with permission mode DMODE.
+
+ Written by Robert Rother, Mariah Corporation, August 1985
+ (sdcsvax!rmr or rmr@uscd). If you want it, it's yours.
+
+ Severely hacked over by John Gilmore to make a 4.2BSD compatible
+ subroutine. 11Mar86; hoptoad!gnu
+
+ Modified by rmtodd@uokmax 6-28-87 -- when making an already existing dir,
+ subroutine didn't return EEXIST. It does now. */
+
+int
+mkdir (dpath, dmode)
+ char *dpath;
+ int dmode;
+{
+ int cpid, status;
+ struct stat statbuf;
+
+ if (stat (dpath, &statbuf) == 0)
+ {
+ errno = EEXIST; /* stat worked, so it already exists. */
+ return -1;
+ }
+
+ /* If stat fails for a reason other than non-existence, return error. */
+ if (errno != ENOENT)
+ return -1;
+
+ cpid = fork ();
+ switch (cpid)
+ {
+ case -1: /* Cannot fork. */
+ return -1; /* errno is set already. */
+
+ case 0: /* Child process. */
+ /* Cheap hack to set mode of new directory. Since this child
+ process is going away anyway, we zap its umask.
+ This won't suffice to set SUID, SGID, etc. on this
+ directory, so the parent process calls chmod afterward. */
+ status = umask (0); /* Get current umask. */
+ umask (status | (0777 & ~dmode)); /* Set for mkdir. */
+ execl ("/bin/mkdir", "mkdir", dpath, (char *) 0);
+ _exit (1);
+
+ default: /* Parent process. */
+ while (wait (&status) != cpid) /* Wait for kid to finish. */
+ /* Do nothing. */ ;
+
+ if (status & 0xFFFF)
+ {
+ errno = EIO; /* /bin/mkdir failed. */
+ return -1;
+ }
+ return chmod (dpath, dmode);
+ }
+}
+
+/* Remove directory DPATH.
+ Return 0 if successful, -1 if not. */
+
+int
+rmdir (dpath)
+ char *dpath;
+{
+ int cpid, status;
+ struct stat statbuf;
+
+ if (stat (dpath, &statbuf) != 0)
+ return -1; /* stat set errno. */
+
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
+ {
+ errno = ENOTDIR;
+ return -1;
+ }
+
+ cpid = fork ();
+ switch (cpid)
+ {
+ case -1: /* Cannot fork. */
+ return -1; /* errno is set already. */
+
+ case 0: /* Child process. */
+ execl ("/bin/rmdir", "rmdir", dpath, (char *) 0);
+ _exit (1);
+
+ default: /* Parent process. */
+ while (wait (&status) != cpid) /* Wait for kid to finish. */
+ /* Do nothing. */ ;
+
+ if (status & 0xFFFF)
+ {
+ errno = EIO; /* /bin/rmdir failed. */
+ return -1;
+ }
+ return 0;
+ }
+}
diff --git a/gnu/usr.bin/cvs/lib/myndbm.c b/gnu/usr.bin/cvs/lib/myndbm.c
new file mode 100644
index 0000000..8069698
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/myndbm.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * A simple ndbm-emulator for CVS. It parses a text file of the format:
+ *
+ * key value
+ *
+ * at dbm_open time, and loads the entire file into memory. As such, it is
+ * probably only good for fairly small modules files. Ours is about 30K in
+ * size, and this code works fine.
+ */
+
+#include "cvs.h"
+
+#ifdef MY_NDBM
+
+#ifndef lint
+static char rcsid[] = "@(#)myndbm.c 1.5 92/03/31";
+#endif
+
+static void mydbm_load_file ();
+
+/* ARGSUSED */
+DBM *
+mydbm_open (file, flags, mode)
+ char *file;
+ int flags;
+ int mode;
+{
+ FILE *fp;
+ DBM *db;
+
+ if ((fp = fopen (file, "r")) == NULL)
+ return ((DBM *) 0);
+
+ db = (DBM *) xmalloc (sizeof (*db));
+ db->dbm_list = getlist ();
+
+ mydbm_load_file (fp, db->dbm_list);
+ (void) fclose (fp);
+ return (db);
+}
+
+void
+mydbm_close (db)
+ DBM *db;
+{
+ dellist (&db->dbm_list);
+ free ((char *) db);
+}
+
+datum
+mydbm_fetch (db, key)
+ DBM *db;
+ datum key;
+{
+ Node *p;
+ char *s;
+ datum val;
+
+ /* make sure it's null-terminated */
+ s = xmalloc (key.dsize + 1);
+ (void) strncpy (s, key.dptr, key.dsize);
+ s[key.dsize] = '\0';
+
+ p = findnode (db->dbm_list, s);
+ if (p)
+ {
+ val.dptr = p->data;
+ val.dsize = strlen (p->data);
+ }
+ else
+ {
+ val.dptr = (char *) NULL;
+ val.dsize = 0;
+ }
+ free (s);
+ return (val);
+}
+
+datum
+mydbm_firstkey (db)
+ DBM *db;
+{
+ Node *head, *p;
+ datum key;
+
+ head = db->dbm_list->list;
+ p = head->next;
+ if (p != head)
+ {
+ key.dptr = p->key;
+ key.dsize = strlen (p->key);
+ }
+ else
+ {
+ key.dptr = (char *) NULL;
+ key.dsize = 0;
+ }
+ db->dbm_next = p->next;
+ return (key);
+}
+
+datum
+mydbm_nextkey (db)
+ DBM *db;
+{
+ Node *head, *p;
+ datum key;
+
+ head = db->dbm_list->list;
+ p = db->dbm_next;
+ if (p != head)
+ {
+ key.dptr = p->key;
+ key.dsize = strlen (p->key);
+ }
+ else
+ {
+ key.dptr = (char *) NULL;
+ key.dsize = 0;
+ }
+ db->dbm_next = p->next;
+ return (key);
+}
+
+static void
+mydbm_load_file (fp, list)
+ FILE *fp;
+ List *list;
+{
+ char line[MAXLINELEN], value[MAXLINELEN];
+ char *cp, *vp;
+ int len, cont;
+
+ for (cont = 0; fgets (line, sizeof (line), fp) != NULL;)
+ {
+ if ((cp = rindex (line, '\n')) != NULL)
+ *cp = '\0'; /* strip the newline */
+
+ /*
+ * Add the line to the value, at the end if this is a continuation
+ * line; otherwise at the beginning, but only after any trailing
+ * backslash is removed.
+ */
+ vp = value;
+ if (cont)
+ vp += strlen (value);
+
+ /*
+ * See if the line we read is a continuation line, and strip the
+ * backslash if so.
+ */
+ len = strlen (line);
+ if (len > 0)
+ cp = &line[len - 1];
+ else
+ cp = line;
+ if (*cp == '\\')
+ {
+ cont = 1;
+ *cp = '\0';
+ }
+ else
+ {
+ cont = 0;
+ }
+ (void) strcpy (vp, line);
+ if (value[0] == '#')
+ continue; /* comment line */
+ vp = value;
+ while (*vp && isspace (*vp))
+ vp++;
+ if (*vp == '\0')
+ continue; /* empty line */
+
+ /*
+ * If this was not a continuation line, add the entry to the database
+ */
+ if (!cont)
+ {
+ Node *p = getnode ();
+ char *kp;
+
+ kp = vp;
+ while (*vp && !isspace (*vp))
+ vp++;
+ *vp++ = '\0'; /* NULL terminate the key */
+ p->type = NDBMNODE;
+ p->key = xstrdup (kp);
+ while (*vp && isspace (*vp))
+ vp++; /* skip whitespace to value */
+ if (*vp == '\0')
+ {
+ error (0, 0, "warning: NULL value for key `%s'", p->key);
+ freenode (p);
+ continue;
+ }
+ p->data = xstrdup (vp);
+ if (addnode (list, p) == -1)
+ {
+ error (0, 0, "duplicate key found for `%s'", p->key);
+ freenode (p);
+ }
+ }
+ }
+}
+
+#endif /* MY_NDBM */
diff --git a/gnu/usr.bin/cvs/lib/myndbm.h b/gnu/usr.bin/cvs/lib/myndbm.h
new file mode 100644
index 0000000..d71acdf
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/myndbm.h
@@ -0,0 +1,44 @@
+/* @(#)myndbm.h 1.3 92/02/29 */
+
+#ifdef MY_NDBM
+
+#define DBLKSIZ 4096
+
+typedef struct
+{
+ List *dbm_list; /* cached database */
+ Node *dbm_next; /* next key to return for nextkey() */
+} DBM;
+
+typedef struct
+{
+ char *dptr;
+ int dsize;
+} datum;
+
+/*
+ * So as not to conflict with other dbm_open, etc., routines that may
+ * be included by someone's libc, all of my emulation routines are prefixed
+ * by "my" and we define the "standard" ones to be "my" ones here.
+ */
+#define dbm_open mydbm_open
+#define dbm_close mydbm_close
+#define dbm_fetch mydbm_fetch
+#define dbm_firstkey mydbm_firstkey
+#define dbm_nextkey mydbm_nextkey
+
+#if __STDC__
+DBM *mydbm_open (char *file, int flags, int mode);
+void mydbm_close (DBM * db);
+datum mydbm_fetch (DBM * db, datum key);
+datum mydbm_firstkey (DBM * db);
+datum mydbm_nextkey (DBM * db);
+#else
+DBM *mydbm_open ();
+void mydbm_close ();
+datum mydbm_fetch ();
+datum mydbm_firstkey ();
+datum mydbm_nextkey ();
+#endif /* __STDC__ */
+
+#endif /* MY_NDBM */
diff --git a/gnu/usr.bin/cvs/lib/regex.c b/gnu/usr.bin/cvs/lib/regex.c
new file mode 100644
index 0000000..3bccfd3
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/regex.c
@@ -0,0 +1,4867 @@
+/* Extended regular expression matching and search library,
+ version 0.4.
+ (Implements POSIX draft P10003.2/D11.2, except for multibyte characters.)
+
+ Copyright (C) 1985, 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if defined (_AIX) && !defined (REGEX_MALLOC)
+ #pragma alloca
+#endif
+
+#define _GNU_SOURCE
+
+/* For interactive testing, compile with -Dtest. Then this becomes
+ a self-contained program which reads a pattern, describes how it
+ compiles, then reads a string and searches for it. If a command-line
+ argument is present, it is taken to be the value for obscure_syntax (in
+ decimal). The default is 0 (Emacs-style syntax).
+
+ If DEBUG is defined, this prints many voluminous messages about what
+ it is doing (if the variable `debug' is nonzero). */
+
+
+/* The `emacs' switch turns on certain matching commands
+ that make sense only in Emacs. */
+#ifdef emacs
+#include "config.h"
+#include "lisp.h"
+#include "buffer.h"
+#include "syntax.h"
+
+/* Emacs uses `NULL' as a predicate. */
+#undef NULL
+
+#else /* not emacs */
+
+/* POSIX.1 says that <unistd.h> might need <sys/types.h>. We also need
+ it for regex.h. */
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if defined (USG) || defined (POSIX) || defined (STDC_HEADERS)
+#ifndef BSTRING
+#include <string.h>
+#ifndef bcopy
+#define bcopy(s,d,n) memcpy ((d), (s), (n))
+#endif
+#ifndef bcmp
+#define bcmp(s1,s2,n) memcmp ((s1), (s2), (n))
+#endif
+#ifndef bzero
+#define bzero(s,n) memset ((s), 0, (n))
+#endif
+#endif /* not BSTRING */
+#endif /* USG or POSIX or STDC_HEADERS */
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else /* not STDC_HEADERS */
+char *malloc ();
+char *realloc ();
+#endif /* not STDC_HEADERS */
+
+/* If debugging, we use standard I/O. */
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+/* Define the syntax stuff for \<, \>, etc. */
+
+/* This must be nonzero for the wordchar and notwordchar pattern
+ commands in re_match_2. */
+#ifndef Sword
+#define Sword 1
+#endif
+
+#ifdef SYNTAX_TABLE
+
+extern char *re_syntax_table;
+
+#else /* not SYNTAX_TABLE */
+
+/* How many characters in the character set. */
+#define CHAR_SET_SIZE 256
+
+static char re_syntax_table[CHAR_SET_SIZE];
+
+static void
+init_syntax_once ()
+{
+ register int c;
+ static int done = 0;
+
+ if (done)
+ return;
+
+ bzero (re_syntax_table, sizeof re_syntax_table);
+
+ for (c = 'a'; c <= 'z'; c++)
+ re_syntax_table[c] = Sword;
+
+ for (c = 'A'; c <= 'Z'; c++)
+ re_syntax_table[c] = Sword;
+
+ for (c = '0'; c <= '9'; c++)
+ re_syntax_table[c] = Sword;
+
+ re_syntax_table['_'] = Sword;
+
+ done = 1;
+}
+
+#endif /* not SYNTAX_TABLE */
+
+#define SYNTAX(c) re_syntax_table[c]
+
+#endif /* not emacs */
+
+
+/* Get the interface, including the syntax bits. */
+#include "regex.h"
+
+
+/* isalpha(3) etc. are used for the character classes. */
+#include <ctype.h>
+#ifndef isgraph
+#define isgraph(c) (isprint (c) && !isspace (c))
+#endif
+#ifndef isblank
+#define isblank(c) ((c) == ' ' || (c) == '\t')
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef SIGN_EXTEND_CHAR
+#ifdef __CHAR_UNSIGNED__ /* for, e.g., IBM RT */
+#define SIGN_EXTEND_CHAR(c) (((c)^128) - 128) /* As in Harbison and Steele. */
+#else
+#define SIGN_EXTEND_CHAR /* As nothing. */
+#endif /* not CHAR_UNSIGNED */
+#endif /* not SIGN_EXTEND_CHAR */
+
+/* Should we use malloc or alloca? If REGEX_MALLOC is not defined, we
+ use `alloca' instead of `malloc'. This is because using malloc in
+ re_search* or re_match* could cause memory leaks when C-g is used in
+ Emacs; also, malloc is slower and causes storage fragmentation. On
+ the other hand, malloc is more portable, and easier to debug.
+
+ Because we sometimes use alloca, some routines have to be macros,
+ not functions---alloca-allocated space disappears at the end of the
+ function it is called in. */
+#ifdef REGEX_MALLOC
+
+#define REGEX_ALLOCATE malloc
+#define REGEX_REALLOCATE(source, size) (realloc (source, size))
+
+#else /* not REGEX_MALLOC */
+
+/* Emacs already defines alloca, sometimes. */
+#ifndef alloca
+
+/* Make alloca work the best possible way. */
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not __GNUC__ */
+#ifdef sparc
+#include <alloca.h>
+#else /* not __GNUC__ or sparc */
+char *alloca ();
+#endif /* not sparc */
+#endif /* not __GNUC__ */
+
+#endif /* not alloca */
+
+/* Still not REGEX_MALLOC. */
+
+#define REGEX_ALLOCATE alloca
+
+/* Requires a `char *destination' declared. */
+#define REGEX_REALLOCATE(source, size) \
+ (destination = (char *) alloca (size), \
+ bcopy (source, destination, size), \
+ destination)
+
+#endif /* not REGEX_MALLOC */
+
+/* (Re)Allocate N items of type T using malloc, or fail. */
+#define TALLOC(n, t) (t *) malloc ((n) * sizeof (t))
+#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t)))
+
+
+#define BYTEWIDTH 8 /* In bits. */
+
+#define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+/* These are the command codes that appear in compiled regular
+ expressions. Some opcodes are followed by argument bytes. A
+ command code can specify any interpretation whatsoever for its
+ arguments. Zero bytes may appear in the compiled regular expression.
+
+ The value of `exactn' is needed in search.c (search_buffer) in Emacs.
+ So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of
+ `exactn' we use here must also be 1. */
+
+typedef enum
+{
+ no_op = 0,
+
+ /* Followed by one byte giving n, then by n literal bytes. */
+ exactn = 1,
+
+ /* Matches any (more or less) character. */
+ anychar,
+
+ /* Matches any one char belonging to specified set. First
+ following byte is number of bitmap bytes. Then come bytes
+ for a bitmap saying which chars are in. Bits in each byte
+ are ordered low-bit-first. A character is in the set if its
+ bit is 1. A character too large to have a bit in the map is
+ automatically not in the set. */
+ charset,
+
+ /* Same parameters as charset, but match any character that is
+ not one of those specified. */
+ charset_not,
+
+ /* Start remembering the text that is matched, for storing in a
+ register. Followed by one byte with the register number, in
+ the range 0 to one less than the pattern buffer's re_nsub
+ field. Then followed by one byte with the number of groups
+ inner to this one. (This last has to be part of the
+ start_memory only because we need it in the on_failure_jump
+ of re_match_2.) */
+ start_memory,
+
+ /* Stop remembering the text that is matched and store it in a
+ memory register. Followed by one byte with the register
+ number, in the range 0 to one less than `re_nsub' in the
+ pattern buffer, and one byte with the number of inner groups,
+ just like `start_memory'. (We need the number of inner
+ groups here because we don't have any easy way of finding the
+ corresponding start_memory when we're at a stop_memory.) */
+ stop_memory,
+
+ /* Match a duplicate of something remembered. Followed by one
+ byte containing the register number. */
+ duplicate,
+
+ /* Fail unless at beginning of line. */
+ begline,
+
+ /* Fail unless at end of line. */
+ endline,
+
+ /* Succeeds if at beginning of buffer (if emacs) or at beginning
+ of string to be matched (if not). */
+ begbuf,
+
+ /* Analogously, for end of buffer/string. */
+ endbuf,
+
+ /* Followed by two byte relative address to which to jump. */
+ no_pop_jump,
+
+ /* Same as no_pop_jump, but marks the end of an alternative. */
+ jump_past_next_alt,
+
+ /* Followed by two-byte relative address of place to resume at
+ in case of failure. */
+ on_failure_jump,
+
+ /* Like on_failure_jump, but pushes a placeholder instead of the
+ current string position. */
+ on_failure_keep_string_jump,
+
+ /* Throw away latest failure point and then jump to following
+ two-byte relative address. */
+ pop_failure_jump,
+
+ /* Change to pop_failure_jump if know won't have to backtrack to
+ match; otherwise change to no_pop_jump. This is used to jump
+ back to the beginning of a repeat. If what follows this jump
+ clearly won't match what the repeat does, such that we can be
+ sure that there is no use backtracking out of repetitions
+ already matched, then we change it to a pop_failure_jump.
+ Followed by two-byte address. */
+ maybe_pop_jump,
+
+ /* Jump to following two-byte address, and push a dummy failure
+ point. This failure point will be thrown away if an attempt
+ is made to use it for a failure. A `+' construct makes this
+ before the first repeat. Also used as an intermediary kind
+ of jump when compiling an alternative. */
+ dummy_failure_jump,
+
+ /* Used like on_failure_jump except has to succeed n times; The
+ two-byte relative address following it is useless until then.
+ The address is followed by two more bytes containing n. */
+ succeed_n,
+
+ /* Similar to no_pop_jump, but jump n times only; also the
+ relative address following is in turn followed by yet two
+ more bytes containing n. */
+ no_pop_jump_n,
+
+ /* Set the following relative location (two bytes) to the
+ subsequent (two-byte) number. */
+ set_number_at,
+
+ wordchar, /* Matches any word-constituent character. */
+ notwordchar, /* Matches any char that is not a word-constituent. */
+
+ wordbeg, /* Succeeds if at word beginning. */
+ wordend, /* Succeeds if at word end. */
+
+ wordbound, /* Succeeds if at a word boundary. */
+ notwordbound /* Succeeds if not at a word boundary. */
+
+#ifdef emacs
+ ,before_dot, /* Succeeds if before point. */
+ at_dot, /* Succeeds if at point. */
+ after_dot, /* Succeeds if after point. */
+
+ /* Matches any character whose syntax is specified. Followed by
+ a byte which contains a syntax code, e.g., Sword. */
+ syntaxspec,
+
+ /* Matches any character whose syntax is not that specified. */
+ notsyntaxspec
+#endif /* emacs */
+} re_opcode_t;
+
+/* Common operations on the compiled pattern. */
+
+/* Store NUMBER in two contiguous bytes starting at DESTINATION. */
+
+#define STORE_NUMBER(destination, number) \
+ do { \
+ (destination)[0] = (number) & 0377; \
+ (destination)[1] = (number) >> 8; \
+ } while (0)
+
+
+/* Same as STORE_NUMBER, except increment DESTINATION to
+ the byte after where the number is stored. Therefore, DESTINATION
+ must be an lvalue. */
+
+#define STORE_NUMBER_AND_INCR(destination, number) \
+ do { \
+ STORE_NUMBER (destination, number); \
+ (destination) += 2; \
+ } while (0)
+
+
+/* Put into DESTINATION a number stored in two contiguous bytes starting
+ at SOURCE. */
+
+#define EXTRACT_NUMBER(destination, source) \
+ do { \
+ (destination) = *(source) & 0377; \
+ (destination) += SIGN_EXTEND_CHAR (*(const char *)((source) + 1)) << 8;\
+ } while (0)
+
+#ifdef DEBUG
+static int
+extract_number (source)
+ unsigned char *source;
+{
+ int answer = *source & 0377;
+ answer += (SIGN_EXTEND_CHAR (*(char *)((source) + 1))) << 8;
+
+ return answer;
+}
+#endif
+
+
+/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number.
+ SOURCE must be an lvalue. */
+
+#define EXTRACT_NUMBER_AND_INCR(destination, source) \
+ do { \
+ EXTRACT_NUMBER (destination, source); \
+ (source) += 2; \
+ } while (0)
+
+#ifdef DEBUG
+static void
+extract_number_and_incr (destination, source)
+ int *destination;
+ unsigned char **source;
+{
+ *destination = extract_number (*source);
+ *source += 2;
+}
+#endif
+
+
+/* Is true if there is a first string and if PTR is pointing anywhere
+ inside it or just past the end. */
+
+#define IS_IN_FIRST_STRING(ptr) \
+ (size1 && string1 <= (ptr) && (ptr) <= string1 + size1)
+
+#ifdef DEBUG
+
+extern void printchar ();
+
+/* Print a compiled pattern buffer in human-readable form, starting at
+ the START pointer into it and ending just before the pointer END. */
+
+static void
+partial_compiled_pattern_printer (pbufp, start, end)
+ struct re_pattern_buffer *pbufp;
+ unsigned char *start;
+ unsigned char *end;
+{
+
+ int mcnt, mcnt2;
+ unsigned char *p = start;
+ unsigned char *pend = end;
+
+ if (start == NULL)
+ {
+ printf ("(null)\n");
+ return;
+ }
+
+ /* This loop loops over pattern commands. */
+ while (p < pend)
+ {
+ switch ((re_opcode_t) *p++)
+ {
+ case no_op:
+ printf ("/no_op");
+ break;
+
+ case exactn:
+ mcnt = *p++;
+ printf ("/exactn/%d", mcnt);
+ do
+ {
+ putchar ('/');
+ printchar (*p++);
+ }
+ while (--mcnt);
+ break;
+
+ case start_memory:
+ mcnt = *p++;
+ printf ("/start_memory/%d/%d", mcnt, *p++);
+ break;
+
+ case stop_memory:
+ mcnt = *p++;
+ printf ("/stop_memory/%d/%d", mcnt, *p++);
+ break;
+
+ case duplicate:
+ printf ("/duplicate/%d", *p++);
+ break;
+
+ case anychar:
+ printf ("/anychar");
+ break;
+
+ case charset:
+ case charset_not:
+ {
+ register int c;
+
+ printf ("/charset%s/", *(p - 1) == charset_not ? "_not" : "");
+
+ for (c = 0; p < pend && c < *p * BYTEWIDTH; c++)
+ {
+ if (p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+ printchar (c);
+ }
+ p += 1 + *p;
+ break;
+ }
+
+ case begline:
+ printf ("/begline");
+ break;
+
+ case endline:
+ printf ("/endline");
+ break;
+
+ case on_failure_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/on_failure_jump/0/%d", mcnt);
+ break;
+
+ case on_failure_keep_string_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/on_failure_keep_string_jump/0/%d", mcnt);
+ break;
+
+ case dummy_failure_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/dummy_failure_jump/0/%d", mcnt);
+ break;
+
+ case maybe_pop_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/maybe_pop_jump/0/%d", mcnt);
+ break;
+
+ case pop_failure_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/pop_failure_jump/0/%d", mcnt);
+ break;
+
+ case jump_past_next_alt:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/jump_past_next_alt/0/%d", mcnt);
+ break;
+
+ case no_pop_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/no_pop_jump/0/%d", mcnt);
+ break;
+
+ case succeed_n:
+ extract_number_and_incr (&mcnt, &p);
+ extract_number_and_incr (&mcnt2, &p);
+ printf ("/succeed_n/0/%d/0/%d", mcnt, mcnt2);
+ break;
+
+ case no_pop_jump_n:
+ extract_number_and_incr (&mcnt, &p);
+ extract_number_and_incr (&mcnt2, &p);
+ printf ("/no_pop_jump_n/0/%d/0/%d", mcnt, mcnt2);
+ break;
+
+ case set_number_at:
+ extract_number_and_incr (&mcnt, &p);
+ extract_number_and_incr (&mcnt2, &p);
+ printf ("/set_number_at/0/%d/0/%d", mcnt, mcnt2);
+ break;
+
+ case wordbound:
+ printf ("/wordbound");
+ break;
+
+ case notwordbound:
+ printf ("/notwordbound");
+ break;
+
+ case wordbeg:
+ printf ("/wordbeg");
+ break;
+
+ case wordend:
+ printf ("/wordend");
+
+#ifdef emacs
+ case before_dot:
+ printf ("/before_dot");
+ break;
+
+ case at_dot:
+ printf ("/at_dot");
+ break;
+
+ case after_dot:
+ printf ("/after_dot");
+ break;
+
+ case wordchar:
+ printf ("/wordchar-emacs");
+ mcnt = (int) Sword;
+ break;
+
+ case syntaxspec:
+ printf ("/syntaxspec");
+ mcnt = *p++;
+ printf ("/%d", mcnt);
+ break;
+
+ case notwordchar:
+ printf ("/notwordchar-emacs");
+ mcnt = (int) Sword;
+ break;
+
+ case notsyntaxspec:
+ printf ("/notsyntaxspec");
+ mcnt = *p++;
+ printf ("/%d", mcnt);
+ break;
+#else /* not emacs */
+ case wordchar:
+ printf ("/wordchar-notemacs");
+ break;
+
+ case notwordchar:
+ printf ("/notwordchar-notemacs");
+ break;
+#endif /* not emacs */
+
+ case begbuf:
+ printf ("/begbuf");
+ break;
+
+ case endbuf:
+ printf ("/endbuf");
+ break;
+
+ default:
+ printf ("?%d", *(p-1));
+ }
+ }
+ printf ("/\n");
+}
+
+static void
+compiled_pattern_printer (pbufp)
+ struct re_pattern_buffer *pbufp;
+{
+ partial_compiled_pattern_printer (pbufp, pbufp->buffer,
+ pbufp->buffer + pbufp->used);
+}
+
+
+static void
+double_string_printer (where, string1, size1, string2, size2)
+ unsigned char *where;
+ unsigned char *string1;
+ unsigned char *string2;
+ int size1;
+ int size2;
+{
+ unsigned this_char;
+
+ if (where == NULL)
+ printf ("(null)");
+ else
+ {
+ if (IS_IN_FIRST_STRING (where))
+ {
+ for (this_char = where - string1; this_char < size1; this_char++)
+ printchar (string1[this_char]);
+
+ where = string2;
+ }
+
+ for (this_char = where - string2; this_char < size2; this_char++)
+ printchar (string2[this_char]);
+ }
+}
+
+#endif /* DEBUG */
+
+#ifdef DEBUG
+
+/* It is useful to test things that must to be true when debugging. */
+#include <assert.h>
+
+static int debug = 0;
+
+#define DEBUG_STATEMENT(e) e
+#define DEBUG_PRINT1(x) if (debug) printf (x)
+#define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2)
+#define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3)
+#define DEBUG_COMPILED_PATTERN_PRINTER(p, s, e) \
+ if (debug) partial_compiled_pattern_printer (p, s, e)
+#define DEBUG_DOUBLE_STRING_PRINTER(w, s1, sz1, s2, sz2) \
+ if (debug) double_string_printer (w, s1, sz1, s2, sz2)
+
+#else /* not DEBUG */
+
+#undef assert
+#define assert(e)
+
+#define DEBUG_STATEMENT(e)
+#define DEBUG_PRINT1(x)
+#define DEBUG_PRINT2(x1, x2)
+#define DEBUG_PRINT3(x1, x2, x3)
+#define DEBUG_COMPILED_PATTERN_PRINTER(p, s, e)
+#define DEBUG_DOUBLE_STRING_PRINTER(w, s1, sz1, s2, sz2)
+
+#endif /* not DEBUG */
+
+typedef char boolean;
+#define false 0
+#define true 1
+
+/* Set by re_set_syntax to the current regexp syntax to recognize. Can
+ also be assigned to more or less arbitrarily. Since we use this as a
+ collection of bits, declaring it unsigned maximizes portability. */
+reg_syntax_t obscure_syntax = 0;
+
+
+/* Specify the precise syntax of regexps for compilation. This provides
+ for compatibility for various utilities which historically have
+ different, incompatible syntaxes.
+
+ The argument SYNTAX is a bit mask comprised of the various bits
+ defined in regex.h. We return the old syntax. */
+
+reg_syntax_t
+re_set_syntax (syntax)
+ reg_syntax_t syntax;
+{
+ reg_syntax_t ret = obscure_syntax;
+
+ obscure_syntax = syntax;
+ return ret;
+}
+
+/* This table gives an error message for each of the error codes listed
+ in regex.h. Obviously the order here has to be same as there. */
+
+static const char *re_error_msg[] =
+ { NULL, /* REG_NOERROR */
+ "No match", /* REG_NOMATCH */
+ "Invalid regular expression", /* REG_BADPAT */
+ "Invalid collation character", /* REG_ECOLLATE */
+ "Invalid character class name", /* REG_ECTYPE */
+ "Trailing backslash", /* REG_EESCAPE */
+ "Invalid back reference", /* REG_ESUBREG */
+ "Unmatched [ or [^", /* REG_EBRACK */
+ "Unmatched ( or \\(", /* REG_EPAREN */
+ "Unmatched \\{", /* REG_EBRACE */
+ "Invalid content of \\{\\}", /* REG_BADBR */
+ "Invalid range end", /* REG_ERANGE */
+ "Memory exhausted", /* REG_ESPACE */
+ "Invalid preceding regular expression", /* REG_BADRPT */
+ "Premature end of regular expression", /* REG_EEND */
+ "Regular expression too big", /* REG_ESIZE */
+ "Unmatched ) or \\)", /* REG_ERPAREN */
+ };
+
+/* Other subroutine declarations and macros for regex_compile. */
+
+static void store_jump (), insert_jump (), store_jump_n (),
+ insert_jump_n (), insert_op_2 ();
+
+static boolean at_endline_op_p (), group_in_compile_stack ();
+
+/* Fetch the next character in the uncompiled pattern---translating it
+ if necessary. Also cast from a signed character in the constant
+ string passed to us by the user to an unsigned char that we can use
+ as an array index (in, e.g., `translate'). */
+#define PATFETCH(c) \
+ do {if (p == pend) return REG_EEND; \
+ c = (unsigned char) *p++; \
+ if (translate) c = translate[c]; \
+ } while (0)
+
+/* Fetch the next character in the uncompiled pattern, with no
+ translation. */
+#define PATFETCH_RAW(c) \
+ do {if (p == pend) return REG_EEND; \
+ c = (unsigned char) *p++; \
+ } while (0)
+
+/* Go backwards one character in the pattern. */
+#define PATUNFETCH p--
+
+
+/* If `translate' is non-null, return translate[D], else just D. We
+ cast the subscript to translate because some data is declared as
+ `char *', to avoid warnings when a string constant is passed. But
+ when we use a character as a subscript we must make it unsigned. */
+#define TRANSLATE(d) (translate ? translate[(unsigned char) (d)] : (d))
+
+
+/* Macros for outputting the compiled pattern into `buffer'. */
+
+/* If the buffer isn't allocated when it comes in, use this. */
+#define INIT_BUF_SIZE 32
+
+/* Make sure we have at least N more bytes of space in buffer. */
+#define GET_BUFFER_SPACE(n) \
+ { \
+ while (b - bufp->buffer + (n) > bufp->allocated) \
+ EXTEND_BUFFER (); \
+ }
+
+/* Make sure we have one more byte of buffer space and then add C to it. */
+#define PAT_PUSH(c) \
+ do { \
+ GET_BUFFER_SPACE (1); \
+ *b++ = (unsigned char) (c); \
+ } while (0)
+
+
+/* Make sure we have two more bytes of buffer space and then add C1 and
+ C2 to it. */
+#define PAT_PUSH_2(c1, c2) \
+ do { \
+ GET_BUFFER_SPACE (2); \
+ *b++ = (unsigned char) (c1); \
+ *b++ = (unsigned char) (c2); \
+ } while (0)
+
+
+/* Make sure we have two more bytes of buffer space and then add C1, C2
+ and C3 to it. */
+#define PAT_PUSH_3(c1, c2, c3) \
+ do { \
+ GET_BUFFER_SPACE (3); \
+ *b++ = (unsigned char) (c1); \
+ *b++ = (unsigned char) (c2); \
+ *b++ = (unsigned char) (c3); \
+ } while (0)
+
+/* This is not an arbitrary limit: the arguments to the opcodes which
+ represent offsets into the pattern are two bytes long. So if 2^16
+ bytes turns out to be too small, many things would have to change. */
+#define MAX_BUF_SIZE (1L << 16)
+
+/* Extend the buffer by twice its current size via realloc and
+ reset the pointers that pointed into the old block to point to the
+ correct places in the new one. If extending the buffer results in it
+ being larger than MAX_BUF_SIZE, then flag memory exhausted. */
+#define EXTEND_BUFFER() \
+ do { \
+ unsigned char *old_buffer = bufp->buffer; \
+ if (bufp->allocated == MAX_BUF_SIZE) \
+ return REG_ESIZE; \
+ bufp->allocated <<= 1; \
+ if (bufp->allocated > MAX_BUF_SIZE) \
+ bufp->allocated = MAX_BUF_SIZE; \
+ bufp->buffer = (unsigned char *) realloc (bufp->buffer, bufp->allocated);\
+ if (bufp->buffer == NULL) \
+ return REG_ESPACE; \
+ /* If the buffer moved, move all the pointers into it. */ \
+ if (old_buffer != bufp->buffer) \
+ { \
+ b = (b - old_buffer) + bufp->buffer; \
+ begalt = (begalt - old_buffer) + bufp->buffer; \
+ if (fixup_alt_jump) \
+ fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\
+ if (laststart) \
+ laststart = (laststart - old_buffer) + bufp->buffer; \
+ if (pending_exact) \
+ pending_exact = (pending_exact - old_buffer) + bufp->buffer; \
+ } \
+ } while (0)
+
+
+/* Since we have one byte reserved for the register number argument to
+ {start,stop}_memory, the maximum number of groups we can report
+ things about is what fits in that byte. */
+typedef unsigned char regnum_t;
+#define MAX_REGNUM ((regnum_t) ((1 << BYTEWIDTH) - 1))
+
+
+/* Macros for the compile stack. */
+
+/* This type needs to be able to hold values from 0 to MAX_BUF_SIZE - 1. */
+typedef short pattern_offset_t;
+
+typedef struct
+{
+ pattern_offset_t begalt_offset;
+ pattern_offset_t fixup_alt_jump;
+ pattern_offset_t inner_group_offset;
+ pattern_offset_t laststart_offset;
+ regnum_t regnum;
+} compile_stack_elt_t;
+
+
+typedef struct
+{
+ compile_stack_elt_t *stack;
+ unsigned size;
+ unsigned avail; /* Offset of next open position. */
+} compile_stack_type;
+
+
+#define INIT_COMPILE_STACK_SIZE 32
+
+#define COMPILE_STACK_EMPTY (compile_stack.avail == 0)
+#define COMPILE_STACK_FULL (compile_stack.avail == compile_stack.size)
+
+/* The next available element. */
+#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail])
+
+
+/* Set the bit for character C in a list. */
+#define SET_LIST_BIT(c) (b[(c) / BYTEWIDTH] |= 1 << ((c) % BYTEWIDTH))
+
+
+/* Get the next unsigned number in the uncompiled pattern. */
+#define GET_UNSIGNED_NUMBER(num) \
+ { if (p != pend) \
+ { \
+ PATFETCH (c); \
+ while (isdigit (c)) \
+ { \
+ if (num < 0) \
+ num = 0; \
+ num = num * 10 + c - '0'; \
+ if (p == pend) \
+ break; \
+ PATFETCH (c); \
+ } \
+ } \
+ }
+
+
+/* Read the endpoint of a range from the uncompiled pattern and set the
+ corresponding bits in the compiled pattern. */
+
+#define DO_RANGE \
+ { \
+ char end; \
+ char this_char = p[-2]; \
+ \
+ if (p == pend) \
+ return REG_ERANGE; \
+ PATFETCH (end); \
+ if (syntax & RE_NO_EMPTY_RANGES && this_char > end) \
+ return REG_ERANGE; \
+ while (this_char <= end) \
+ { \
+ SET_LIST_BIT (TRANSLATE (this_char)); \
+ this_char++; \
+ } \
+ }
+
+
+#define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */
+
+#define IS_CHAR_CLASS(string) \
+ (STREQ (string, "alpha") || STREQ (string, "upper") \
+ || STREQ (string, "lower") || STREQ (string, "digit") \
+ || STREQ (string, "alnum") || STREQ (string, "xdigit") \
+ || STREQ (string, "space") || STREQ (string, "print") \
+ || STREQ (string, "punct") || STREQ (string, "graph") \
+ || STREQ (string, "cntrl") || STREQ (string, "blank"))
+
+
+/* regex_compile compiles PATTERN (of length SIZE) according to SYNTAX.
+ Returns one of error codes defined in regex.h, or zero for success.
+
+ Assumes the `allocated' (and perhaps `buffer') and `translate'
+ fields are set in BUFP on entry.
+
+ If it succeeds, results are put in BUFP (if it returns an error, the
+ contents of BUFP are undefined):
+ `buffer' is the compiled pattern;
+ `syntax' is set to SYNTAX;
+ `used' is set to the length of the compiled pattern;
+ `fastmap_accurate' is set to zero;
+ `re_nsub' is set to the number of groups in PATTERN;
+ `not_bol' and `not_eol' are set to zero.
+
+ The `fastmap' and `newline_anchor' fields are neither
+ examined nor set. */
+
+static reg_errcode_t
+regex_compile (pattern, size, syntax, bufp)
+ const char *pattern;
+ int size;
+ reg_syntax_t syntax;
+ struct re_pattern_buffer *bufp;
+{
+ register unsigned char c, c1;
+ const char *p1;
+
+ /* Points to the end of the buffer, where we should append. */
+ register unsigned char *b;
+
+ /* Points to the current (ending) position in the pattern. */
+ const char *p = pattern;
+ const char *pend = pattern + size;
+
+ /* How to translate the characters in the pattern. */
+ char *translate = bufp->translate;
+
+ /* Address of the count-byte of the most recently inserted `exactn'
+ command. This makes it possible to tell if a new exact-match
+ character can be added to that command or if the character requires
+ a new `exactn' command. */
+ unsigned char *pending_exact = 0;
+
+ /* Address of start of the most recently finished expression.
+ This tells, e.g., postfix * where to find the start of its
+ operand. Reset at the beginning of groups and alternatives. */
+ unsigned char *laststart = 0;
+
+ /* Place in the uncompiled pattern (i.e., the {) to
+ which to go back if the interval is invalid. */
+ const char *beg_interval; /* The `{'. */
+ const char *following_left_brace;
+
+ /* Address of beginning of regexp, or inside of last group. */
+ unsigned char *begalt;
+
+ /* Address of the place where a forward jump should go to the end of
+ the containing expression. Each alternative of an `or'---except the
+ last---ends with a forward jump of this sort. */
+ unsigned char *fixup_alt_jump = 0;
+
+ /* Counts open-groups as they are encountered. Remembered for the
+ matching close-group on the compile stack, so the same register
+ number is put in the stop_memory as the start_memory. The type
+ here is determined by MAX_REGNUM. */
+ regnum_t regnum = 0;
+
+ /* Keeps track of unclosed groups. */
+ compile_stack_type compile_stack;
+
+#ifdef DEBUG
+ DEBUG_PRINT1 ("\nCompiling pattern: ");
+ if (debug)
+ {
+ unsigned debug_count;
+
+ for (debug_count = 0; debug_count < size; debug_count++)
+ printchar (pattern[debug_count]);
+
+ DEBUG_PRINT1 ("\n");
+ }
+#endif /* DEBUG */
+
+ /* Initialize the compile stack. */
+ compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t);
+ if (compile_stack.stack == NULL)
+ return REG_ESPACE;
+
+ compile_stack.size = INIT_COMPILE_STACK_SIZE;
+ compile_stack.avail = 0;
+
+ /* Initialize the pattern buffer. */
+ bufp->syntax = syntax;
+ bufp->fastmap_accurate = 0;
+ bufp->not_bol = bufp->not_eol = 0;
+
+ /* Set `used' to zero, so that if we return an error, the pattern
+ printer (for debugging) will think there's no pattern. We reset it
+ at the end. */
+ bufp->used = 0;
+
+ /* Always count groups, whether or not bufp->no_sub is set. */
+ bufp->re_nsub = 0;
+
+#if !defined (emacs) && !defined (SYNTAX_TABLE)
+ /* Initialize the syntax table. */
+ init_syntax_once ();
+#endif
+
+ if (bufp->allocated == 0)
+ {
+ if (bufp->buffer)
+ { /* EXTEND_BUFFER loses when bufp->allocated is 0. This loses if
+ buffer's address is bogus, but that is the user's
+ responsibility. */
+ RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char);
+ }
+ else
+ { /* Caller did not allocate a buffer. Do it for them. */
+ bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char);
+ }
+ if (!bufp->buffer) return REG_ESPACE;
+
+ bufp->allocated = INIT_BUF_SIZE;
+ }
+
+ begalt = b = bufp->buffer;
+
+ /* Loop through the uncompiled pattern until we're at the end. */
+ while (p != pend)
+ {
+ PATFETCH (c);
+
+ switch (c)
+ {
+ /* ^ matches the empty string at the beginning of a string (or
+ possibly a line). If RE_CONTEXT_INDEP_ANCHORS is set, ^ is
+ always an operator (and foo^bar is unmatchable). If that bit
+ isn't set, it's an operator only at the beginning of the
+ pattern or after an alternation or open-group operator, or,
+ if RE_NEWLINE_ORDINARY is not set, after a newline (except it
+ can be preceded by other operators that match the empty
+ string); otherwise, it's a normal character. */
+ case '^':
+ {
+ if ( /* If at start of (sub)pattern, it's an operator. */
+ laststart == 0
+ /* If context independent, it's an operator. */
+ || syntax & RE_CONTEXT_INDEP_ANCHORS
+ /* If after a newline, might be an operator. (Since
+ laststart is nonzero here, we know we have at
+ least one byte before the ^.) */
+ || (!(syntax & RE_NEWLINE_ORDINARY) && p[-2] == '\n'))
+ PAT_PUSH (begline);
+ else
+ goto normal_char;
+ }
+ break;
+
+
+ /* $ matches the empty string following the end of the string (or
+ possibly a line). It follows rules dual to those for ^. */
+ case '$':
+ {
+ if ( /* If at end of pattern, it's an operator. */
+ p == pend
+ /* If context independent, it's an operator. */
+ || syntax & RE_CONTEXT_INDEP_ANCHORS
+ /* Otherwise, depends on what's next. */
+ || at_endline_op_p (p, pend, syntax))
+ PAT_PUSH (endline);
+ else
+ goto normal_char;
+ }
+ break;
+
+
+ case '+':
+ case '?':
+ if ((syntax & RE_BK_PLUS_QM)
+ || (syntax & RE_LIMITED_OPS))
+ goto normal_char;
+ handle_plus:
+ case '*':
+ /* If there is no previous pattern... */
+ if (!laststart)
+ {
+ if (syntax & RE_CONTEXT_INVALID_OPS)
+ return REG_BADRPT;
+ else if (!(syntax & RE_CONTEXT_INDEP_OPS))
+ goto normal_char;
+ }
+
+ {
+ /* Are we optimizing this jump? */
+ boolean keep_string_p = false;
+
+ /* 1 means zero (many) matches is allowed. */
+ char zero_times_ok = 0, many_times_ok = 0;
+
+ /* If there is a sequence of repetition chars, collapse it
+ down to just one (the right one). We can't combine
+ interval operators with these because of, e.g., `a{2}*',
+ which should only match an even number of `a's. */
+
+ for (;;)
+ {
+ zero_times_ok |= c != '+';
+ many_times_ok |= c != '?';
+
+ if (p == pend)
+ break;
+
+ PATFETCH (c);
+
+ if (c == '*'
+ || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?')))
+ ;
+
+ else if (syntax & RE_BK_PLUS_QM && c == '\\')
+ {
+ if (p == pend) return REG_EESCAPE;
+
+ PATFETCH (c1);
+ if (!(c1 == '+' || c1 == '?'))
+ {
+ PATUNFETCH;
+ PATUNFETCH;
+ break;
+ }
+
+ c = c1;
+ }
+ else
+ {
+ PATUNFETCH;
+ break;
+ }
+
+ /* If we get here, we found another repeat character. */
+ }
+
+ /* Star, etc. applied to an empty pattern is equivalent
+ to an empty pattern. */
+ if (!laststart)
+ break;
+
+ /* Now we know whether or not zero matches is allowed
+ and also whether or not two or more matches is allowed. */
+ if (many_times_ok)
+ { /* More than one repetition is allowed, so put in at the
+ end a backward relative jump from `b' to before the next
+ jump we're going to put in below (which jumps from
+ laststart to after this jump).
+
+ But if we are at the `*' in the exact sequence `.*\n',
+ insert an unconditional jump backwards to the .,
+ instead of the beginning of the loop. This way we only
+ push a failure point once, instead of every time
+ through the loop. */
+ assert (p - 1 > pattern);
+
+ /* Get the space for the jump. */
+ GET_BUFFER_SPACE (3);
+
+ /* We know we are not at the first character of the pattern,
+ because laststart was nonzero. And we've already
+ incremented `p', by the way, to be the character after
+ the `*'. Do we have to do something analogous here
+ for null bytes, because of RE_DOT_NOT_NULL? */
+ if (TRANSLATE (*(p - 2)) == TRANSLATE ('.')
+ && p < pend && TRANSLATE (*p) == TRANSLATE ('\n')
+ && !(syntax & RE_DOT_NEWLINE))
+ { /* We have .*\n. */
+ store_jump (b, no_pop_jump, laststart);
+ keep_string_p = true;
+ }
+ else
+ /* Anything else. */
+ store_jump (b, maybe_pop_jump, laststart - 3);
+
+ /* We've added more stuff to the buffer. */
+ b += 3;
+ }
+
+ /* On failure, jump from laststart to b + 3, which will be the
+ end of the buffer after this jump is inserted. */
+ GET_BUFFER_SPACE (3);
+ insert_jump (keep_string_p ? on_failure_keep_string_jump
+ : on_failure_jump,
+ laststart, b + 3, b);
+ pending_exact = 0;
+ b += 3;
+
+ if (!zero_times_ok)
+ {
+ /* At least one repetition is required, so insert a
+ dummy_failure before the initial on_failure_jump
+ instruction of the loop. This effects a skip over that
+ instruction the first time we hit that loop. */
+ GET_BUFFER_SPACE (3);
+ insert_jump (dummy_failure_jump, laststart, laststart + 6, b);
+ b += 3;
+ }
+ }
+ break;
+
+
+ case '.':
+ laststart = b;
+ PAT_PUSH (anychar);
+ break;
+
+
+ case '[':
+ {
+ boolean just_had_a_char_class = false;
+
+ if (p == pend) return REG_EBRACK;
+
+ /* Ensure that we have enough space to push an entire
+ charset: the opcode, the byte count, and the bitmap. */
+ while (b - bufp->buffer + 2 + (1 << BYTEWIDTH) / BYTEWIDTH
+ > bufp->allocated)
+ EXTEND_BUFFER ();
+
+ laststart = b;
+
+ PAT_PUSH (*p == '^' ? charset_not : charset);
+ if (*p == '^')
+ p++;
+
+ /* Remember the first position in the bracket expression. */
+ p1 = p;
+
+ /* Push the number of bytes in the bitmap. */
+ PAT_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
+
+ /* Clear the whole map. */
+ bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH);
+
+ /* charset_not matches newline according to a syntax bit. */
+ if ((re_opcode_t) b[-2] == charset_not
+ && (syntax & RE_HAT_LISTS_NOT_NEWLINE))
+ SET_LIST_BIT ('\n');
+
+ /* Read in characters and ranges, setting map bits. */
+ for (;;)
+ {
+ if (p == pend) return REG_EBRACK;
+
+ PATFETCH (c);
+
+ /* \ might escape characters inside [...] and [^...]. */
+ if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\')
+ {
+ if (p == pend) return REG_EESCAPE;
+
+ PATFETCH (c1);
+ SET_LIST_BIT (c1);
+ continue;
+ }
+
+ /* Could be the end of the bracket expression. If it's
+ not (i.e., when the bracket expression is `[]' so
+ far), the ']' character bit gets set way below. */
+ if (c == ']' && p != p1 + 1)
+ break;
+
+ /* Look ahead to see if it's a range when the last thing
+ was a character class. */
+ if (just_had_a_char_class && c == '-' && *p != ']')
+ return REG_ERANGE;
+
+ /* Look ahead to see if it's a range when the last thing
+ was a character: if this is a hyphen not at the
+ beginning or the end of a list, then it's the range
+ operator. */
+ if (c == '-'
+ && !(p - 2 >= pattern && p[-2] == '[')
+ && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^')
+ && *p != ']')
+ {
+ DO_RANGE;
+ }
+
+ else if (p[0] == '-' && p[1] != ']')
+ { /* This handles ranges made up of characters only. */
+ PATFETCH (c1); /* The `-'. */
+ DO_RANGE;
+ }
+
+ /* See if we're at the beginning of a possible character
+ class. */
+
+ else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':')
+ { /* Leave room for the null. */
+ char str[CHAR_CLASS_MAX_LENGTH + 1];
+
+ PATFETCH (c);
+ c1 = 0;
+
+ /* If pattern is `[[:'. */
+ if (p == pend) return REG_EBRACK;
+
+ for (;;)
+ {
+ PATFETCH (c);
+ if (c == ':' || c == ']' || p == pend
+ || c1 == CHAR_CLASS_MAX_LENGTH)
+ break;
+ str[c1++] = c;
+ }
+ str[c1] = '\0';
+
+ /* If isn't a word bracketed by `[:' and:`]':
+ undo the ending character, the letters, and leave
+ the leading `:' and `[' (but set bits for them). */
+ if (c == ':' && *p == ']')
+ {
+ int ch;
+ boolean is_alnum = STREQ (str, "alnum");
+ boolean is_alpha = STREQ (str, "alpha");
+ boolean is_blank = STREQ (str, "blank");
+ boolean is_cntrl = STREQ (str, "cntrl");
+ boolean is_digit = STREQ (str, "digit");
+ boolean is_graph = STREQ (str, "graph");
+ boolean is_lower = STREQ (str, "lower");
+ boolean is_print = STREQ (str, "print");
+ boolean is_punct = STREQ (str, "punct");
+ boolean is_space = STREQ (str, "space");
+ boolean is_upper = STREQ (str, "upper");
+ boolean is_xdigit = STREQ (str, "xdigit");
+
+ if (!IS_CHAR_CLASS (str)) return REG_ECTYPE;
+
+ /* Throw away the ] at the end of the character
+ class. */
+ PATFETCH (c);
+
+ if (p == pend) return REG_EBRACK;
+
+ for (ch = 0; ch < 1 << BYTEWIDTH; ch++)
+ {
+ if ( (is_alnum && isalnum (ch))
+ || (is_alpha && isalpha (ch))
+ || (is_blank && isblank (ch))
+ || (is_cntrl && iscntrl (ch))
+ || (is_digit && isdigit (ch))
+ || (is_graph && isgraph (ch))
+ || (is_lower && islower (ch))
+ || (is_print && isprint (ch))
+ || (is_punct && ispunct (ch))
+ || (is_space && isspace (ch))
+ || (is_upper && isupper (ch))
+ || (is_xdigit && isxdigit (ch)))
+ SET_LIST_BIT (ch);
+ }
+ just_had_a_char_class = true;
+ }
+ else
+ {
+ c1++;
+ while (c1--)
+ PATUNFETCH;
+ SET_LIST_BIT ('[');
+ SET_LIST_BIT (':');
+ just_had_a_char_class = false;
+ }
+ }
+ else
+ {
+ just_had_a_char_class = false;
+ SET_LIST_BIT (c);
+ }
+ }
+
+ /* Discard any (non)matching list bytes that are all 0 at the
+ end of the map. Decrease the map-length byte too. */
+ while ((int) b[-1] > 0 && b[b[-1] - 1] == 0)
+ b[-1]--;
+ b += b[-1];
+ }
+ break;
+
+
+ case '(':
+ if (syntax & RE_NO_BK_PARENS)
+ goto handle_open;
+ else
+ goto normal_char;
+
+
+ case ')':
+ if (syntax & RE_NO_BK_PARENS)
+ goto handle_close;
+ else
+ goto normal_char;
+
+
+ case '\n':
+ if (syntax & RE_NEWLINE_ALT)
+ goto handle_bar;
+ else
+ goto normal_char;
+
+
+ case '|':
+ if (syntax & RE_NO_BK_VBAR)
+ goto handle_bar;
+ else
+ goto normal_char;
+
+
+ case '{':
+ if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES)
+ goto handle_interval;
+ else
+ goto normal_char;
+
+
+ case '\\':
+ if (p == pend) return REG_EESCAPE;
+
+ /* Do not translate the character after the \, so that we can
+ distinguish, e.g., \B from \b, even if we normally would
+ translate, e.g., B to b. */
+ PATFETCH_RAW (c);
+
+ switch (c)
+ {
+ case '(':
+ if (syntax & RE_NO_BK_PARENS)
+ goto normal_backslash;
+ handle_open:
+ if (syntax & RE_NO_EMPTY_GROUPS)
+ {
+ p1 = p;
+ if (!(syntax & RE_NO_BK_PARENS) && *p1 == '\\') p1++;
+
+ /* If found an empty group... */
+ if (*p1 == ')') return REG_BADPAT;
+ }
+
+ bufp->re_nsub++;
+ regnum++;
+
+ if (COMPILE_STACK_FULL)
+ {
+ RETALLOC (compile_stack.stack, compile_stack.size << 1,
+ compile_stack_elt_t);
+ if (compile_stack.stack == NULL) return REG_ESPACE;
+
+ compile_stack.size <<= 1;
+ }
+
+ /* These are the values to restore when we hit end of this
+ group. They are all relative offsets, so that if the
+ whole pattern moves because of realloc, they will still
+ be valid. */
+ COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer;
+ COMPILE_STACK_TOP.fixup_alt_jump
+ = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0;
+ COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer;
+ COMPILE_STACK_TOP.regnum = regnum;
+
+ /* We will eventually replace the 0 with the number of
+ groups inner to this one. */
+ if (regnum <= MAX_REGNUM)
+ {
+ COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2;
+ PAT_PUSH_3 (start_memory, regnum, 0);
+ }
+
+ compile_stack.avail++;
+
+ fixup_alt_jump = 0;
+ laststart = 0;
+ begalt = b;
+ break;
+
+
+ case ')':
+ if (syntax & RE_NO_BK_PARENS) goto normal_backslash;
+
+ if (COMPILE_STACK_EMPTY)
+ if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+ goto normal_backslash;
+ else
+ return REG_ERPAREN;
+
+ handle_close:
+ if (fixup_alt_jump)
+ store_jump (fixup_alt_jump, jump_past_next_alt, b);
+
+ /* See similar code for backslashed left paren above. */
+
+ if (COMPILE_STACK_EMPTY)
+ if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+ goto normal_char;
+ else
+ return REG_ERPAREN;
+
+ /* Since we just checked for an empty stack above, this
+ ``can't happen''. */
+ assert (compile_stack.avail != 0);
+ {
+ /* We don't just want to restore into `regnum', because
+ later groups should continue to be numbered higher,
+ as in `(ab)c(de)' -- the second group is #2. */
+ regnum_t this_group_regnum;
+
+ compile_stack.avail--;
+ begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset;
+ fixup_alt_jump
+ = COMPILE_STACK_TOP.fixup_alt_jump
+ ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1
+ : 0;
+ laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset;
+ this_group_regnum = COMPILE_STACK_TOP.regnum;
+
+ /* We're at the end of the group, so now we know how many
+ groups were inside this one. */
+ if (this_group_regnum <= MAX_REGNUM)
+ {
+ unsigned char *inner_group_loc
+ = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset;
+
+ *inner_group_loc = regnum - this_group_regnum;
+ PAT_PUSH_3 (stop_memory, this_group_regnum,
+ regnum - this_group_regnum);
+ }
+ }
+ break;
+
+
+ case '|': /* `\|'. */
+ if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR)
+ goto normal_backslash;
+ handle_bar:
+ if (syntax & RE_LIMITED_OPS)
+ goto normal_char;
+
+ /* Disallow empty alternatives if RE_NO_EMPTY_ALTS is set.
+ Caveat: can't detect if the vbar is followed by a
+ trailing '$' yet, unless it's the last thing in a
+ pattern; the routine for verifying endlines has to do
+ the rest. */
+ if ((syntax & RE_NO_EMPTY_ALTS)
+ && (!laststart || p == pend
+ || (*p == '$' && p + 1 == pend)
+ || ((syntax & RE_NO_BK_PARENS)
+ ? (p < pend && *p == ')')
+ : (p + 1 < pend && p[0] == '\\' && p[1] == ')'))))
+ return REG_BADPAT;
+
+ /* Insert before the previous alternative a jump which
+ jumps to this alternative if the former fails. */
+ GET_BUFFER_SPACE (3);
+ insert_jump (on_failure_jump, begalt, b + 6, b);
+ pending_exact = 0;
+ b += 3;
+
+ /* The alternative before this one has a jump after it
+ which gets executed if it gets matched. Adjust that
+ jump so it will jump to this alternative's analogous
+ jump (put in below, which in turn will jump to the next
+ (if any) alternative's such jump, etc.). The last such
+ jump jumps to the correct final destination. A picture:
+ _____ _____
+ | | | |
+ | v | v
+ a | b | c
+
+ If we are at `b,' then fixup_alt_jump right now points to a
+ three-byte space after `a.' We'll put in the jump, set
+ fixup_alt_jump to right after `b,' and leave behind three
+ bytes which we'll fill in when we get to after `c.' */
+
+ if (fixup_alt_jump)
+ store_jump (fixup_alt_jump, jump_past_next_alt, b);
+
+ /* Mark and leave space for a jump after this alternative,
+ to be filled in later either by next alternative or
+ when know we're at the end of a series of alternatives. */
+ fixup_alt_jump = b;
+ GET_BUFFER_SPACE (3);
+ b += 3;
+
+ laststart = 0;
+ begalt = b;
+ break;
+
+
+ case '{':
+ /* If \{ is a literal. */
+ if (!(syntax & RE_INTERVALS)
+ /* If we're at `\{' and it's not the open-interval
+ operator. */
+ || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ || (p - 2 == pattern && p == pend))
+ goto normal_backslash;
+
+ handle_interval:
+ {
+ /* If got here, then intervals must be allowed. */
+
+ /* For intervals, at least (most) this many matches must
+ be made. */
+ int lower_bound = -1, upper_bound = -1;
+
+ beg_interval = p - 1; /* The `{'. */
+ following_left_brace = NULL;
+
+ if (p == pend)
+ {
+ if (syntax & RE_NO_BK_BRACES)
+ goto unfetch_interval;
+ else
+ return REG_EBRACE;
+ }
+
+ GET_UNSIGNED_NUMBER (lower_bound);
+
+ if (c == ',')
+ {
+ GET_UNSIGNED_NUMBER (upper_bound);
+ if (upper_bound < 0) upper_bound = RE_DUP_MAX;
+ }
+
+ if (upper_bound < 0)
+ upper_bound = lower_bound;
+
+ if (lower_bound < 0 || upper_bound > RE_DUP_MAX
+ || lower_bound > upper_bound)
+ {
+ if (syntax & RE_NO_BK_BRACES)
+ goto unfetch_interval;
+ else
+ return REG_BADBR;
+ }
+
+ if (!(syntax & RE_NO_BK_BRACES))
+ {
+ if (c != '\\') return REG_EBRACE;
+
+ PATFETCH (c);
+ }
+
+ if (c != '}')
+ {
+ if (syntax & RE_NO_BK_BRACES)
+ goto unfetch_interval;
+ else
+ return REG_BADBR;
+ }
+
+ /* We just parsed a valid interval. */
+
+ /* If it's invalid to have no preceding re. */
+ if (!laststart)
+ {
+ if (syntax & RE_CONTEXT_INVALID_OPS)
+ return REG_BADRPT;
+ else if (syntax & RE_CONTEXT_INDEP_OPS)
+ laststart = b;
+ else
+ goto unfetch_interval;
+ }
+
+ /* If upper_bound is zero, don't want to succeed at all;
+ jump from laststart to b + 3, which will be the end of
+ the buffer after this jump is inserted. */
+ if (upper_bound == 0)
+ {
+ GET_BUFFER_SPACE (3);
+ insert_jump (no_pop_jump, laststart, b + 3, b);
+ b += 3;
+ }
+
+ /* Otherwise, after lower_bound number of succeeds, jump
+ to after the no_pop_jump_n which will be inserted at
+ the end of the buffer, and insert that
+ no_pop_jump_n. */
+ else
+ { /* Set to 5 if only one repetition is allowed and
+ hence no no_pop_jump_n is inserted at the current
+ end of the buffer. Otherwise, need 10 bytes total
+ for the succeed_n and the no_pop_jump_n. */
+ unsigned slots_needed = upper_bound == 1 ? 5 : 10;
+
+ GET_BUFFER_SPACE (slots_needed);
+ /* Initialize the succeed_n to n, even though it will
+ be set by its attendant set_number_at, because
+ re_compile_fastmap will need to know it. Jump to
+ what the end of buffer will be after inserting
+ this succeed_n and possibly appending a
+ no_pop_jump_n. */
+ insert_jump_n (succeed_n, laststart, b + slots_needed,
+ b, lower_bound);
+ b += 5; /* Just increment for the succeed_n here. */
+
+
+ /* More than one repetition is allowed, so put in at
+ the end of the buffer a backward jump from b to the
+ succeed_n we put in above. By the time we've gotten
+ to this jump when matching, we'll have matched once
+ already, so jump back only upper_bound - 1 times. */
+ if (upper_bound > 1)
+ {
+ store_jump_n (b, no_pop_jump_n, laststart,
+ upper_bound - 1);
+ b += 5;
+
+ /* When hit this when matching, reset the
+ preceding no_pop_jump_n's n to upper_bound - 1. */
+ PAT_PUSH (set_number_at);
+
+ /* Only need to get space for the numbers. */
+ GET_BUFFER_SPACE (4);
+ STORE_NUMBER_AND_INCR (b, -5);
+ STORE_NUMBER_AND_INCR (b, upper_bound - 1);
+ }
+
+ /* When hit this when matching, set the succeed_n's n. */
+ GET_BUFFER_SPACE (5);
+ insert_op_2 (set_number_at, laststart, b, 5, lower_bound);
+ b += 5;
+ }
+ pending_exact = 0;
+ beg_interval = NULL;
+
+ if (following_left_brace)
+ goto normal_char;
+ }
+ break;
+
+ unfetch_interval:
+ /* If an invalid interval, match the characters as literals. */
+ assert (beg_interval);
+ p = beg_interval;
+ beg_interval = NULL;
+
+ /* normal_char and normal_backslash need `c'. */
+ PATFETCH (c);
+
+ if (!(syntax & RE_NO_BK_BRACES))
+ {
+ if (p > pattern && p[-1] == '\\')
+ goto normal_backslash;
+ }
+ goto normal_char;
+
+#ifdef emacs
+ /* There is no way to specify the before_dot and after_dot
+ operators. rms says this is ok. --karl */
+ case '=':
+ PAT_PUSH (at_dot);
+ break;
+
+ case 's':
+ laststart = b;
+ PATFETCH (c);
+ PAT_PUSH_2 (syntaxspec, syntax_spec_code[c]);
+ break;
+
+ case 'S':
+ laststart = b;
+ PATFETCH (c);
+ PAT_PUSH_2 (notsyntaxspec, syntax_spec_code[c]);
+ break;
+#endif /* emacs */
+
+
+ case 'w':
+ laststart = b;
+ PAT_PUSH (wordchar);
+ break;
+
+
+ case 'W':
+ laststart = b;
+ PAT_PUSH (notwordchar);
+ break;
+
+
+ case '<':
+ PAT_PUSH (wordbeg);
+ break;
+
+ case '>':
+ PAT_PUSH (wordend);
+ break;
+
+ case 'b':
+ PAT_PUSH (wordbound);
+ break;
+
+ case 'B':
+ PAT_PUSH (notwordbound);
+ break;
+
+ case '`':
+ PAT_PUSH (begbuf);
+ break;
+
+ case '\'':
+ PAT_PUSH (endbuf);
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (syntax & RE_NO_BK_REFS)
+ goto normal_char;
+
+ c1 = c - '0';
+
+ if (c1 > regnum)
+ {
+ if (syntax & RE_NO_MISSING_BK_REF)
+ return REG_ESUBREG;
+ else
+ goto normal_char;
+ }
+
+ /* Can't back reference to a subexpression if inside of it. */
+ if (group_in_compile_stack (compile_stack, c1))
+ goto normal_char;
+
+ laststart = b;
+ PAT_PUSH_2 (duplicate, c1);
+ break;
+
+
+ case '+':
+ case '?':
+ if (syntax & RE_BK_PLUS_QM)
+ goto handle_plus;
+ else
+ goto normal_backslash;
+
+ default:
+ normal_backslash:
+ /* You might think it would be useful for \ to mean
+ not to translate; but if we don't translate it
+ it will never match anything. */
+ c = TRANSLATE (c);
+ goto normal_char;
+ }
+ break;
+
+
+ default:
+ /* Expects the character in `c'. */
+ normal_char:
+ /* If no exactn currently being built. */
+ if (!pending_exact
+
+ /* If last exactn not at current position. */
+ || pending_exact + *pending_exact + 1 != b
+
+ /* We have only one byte following the exactn for the count. */
+ || *pending_exact == (1 << BYTEWIDTH) - 1
+
+ /* If followed by a repetition operator. */
+ || *p == '*' || *p == '^'
+ || ((syntax & RE_BK_PLUS_QM)
+ ? *p == '\\' && (p[1] == '+' || p[1] == '?')
+ : (*p == '+' || *p == '?'))
+ || ((syntax & RE_INTERVALS)
+ && ((syntax & RE_NO_BK_BRACES)
+ ? *p == '{'
+ : (p[0] == '\\' && p[1] == '{'))))
+ {
+ /* Start building a new exactn. */
+
+ laststart = b;
+
+ PAT_PUSH_2 (exactn, 0);
+ pending_exact = b - 1;
+ }
+
+ PAT_PUSH (c);
+ (*pending_exact)++;
+ break;
+ } /* switch (c) */
+ } /* while p != pend */
+
+
+ /* Through the pattern now. */
+
+ if (fixup_alt_jump)
+ store_jump (fixup_alt_jump, jump_past_next_alt, b);
+
+ if (!COMPILE_STACK_EMPTY)
+ return REG_EPAREN;
+
+ free (compile_stack.stack);
+
+ /* We have succeeded; set the length of the buffer. */
+ bufp->used = b - bufp->buffer;
+ return REG_NOERROR;
+} /* regex_compile */
+
+/* Subroutines for regex_compile. */
+
+/* Store a jump of the form <OPCODE> <relative address>.
+ Store in the location FROM a jump operation to jump to relative
+ address FROM - TO. OPCODE is the opcode to store. */
+
+static void
+store_jump (from, op, to)
+ unsigned char *from, *to;
+ re_opcode_t op;
+{
+ from[0] = (unsigned char) op;
+ STORE_NUMBER (from + 1, to - (from + 3));
+}
+
+
+/* Open up space before char FROM, and insert there a jump to TO.
+ CURRENT_END gives the end of the storage not in use, so we know
+ how much data to copy up. OP is the opcode of the jump to insert.
+
+ If you call this function, you must zero out pending_exact. */
+
+static void
+insert_jump (op, from, to, current_end)
+ re_opcode_t op;
+ unsigned char *from, *to, *current_end;
+{
+ register unsigned char *pfrom = current_end; /* Copy from here... */
+ register unsigned char *pto = current_end + 3; /* ...to here. */
+
+ while (pfrom != from)
+ *--pto = *--pfrom;
+
+ store_jump (from, op, to);
+}
+
+
+/* Store a jump of the form <opcode> <relative address> <n>.
+
+ Store in the location FROM a jump operation to jump to relative
+ address FROM - TO. OPCODE is the opcode to store, N is a number the
+ jump uses, say, to decide how many times to jump.
+
+ If you call this function, you must zero out pending_exact. */
+
+static void
+store_jump_n (from, op, to, n)
+ unsigned char *from, *to;
+ re_opcode_t op;
+ unsigned n;
+{
+ from[0] = (unsigned char) op;
+ STORE_NUMBER (from + 1, to - (from + 3));
+ STORE_NUMBER (from + 3, n);
+}
+
+
+/* Similar to insert_jump, but handles a jump which needs an extra
+ number to handle minimum and maximum cases. Open up space at
+ location FROM, and insert there a jump to TO. CURRENT_END gives the
+ end of the storage in use, so we know how much data to copy up. OP is
+ the opcode of the jump to insert.
+
+ If you call this function, you must zero out pending_exact. */
+
+static void
+insert_jump_n (op, from, to, current_end, n)
+ re_opcode_t op;
+ unsigned char *from, *to, *current_end;
+ unsigned n;
+{
+ register unsigned char *pfrom = current_end;
+ register unsigned char *pto = current_end + 5;
+
+ while (pfrom != from)
+ *--pto = *--pfrom;
+
+ store_jump_n (from, op, to, n);
+}
+
+
+/* Open up space at location THERE, and insert operation OP followed by
+ NUM_1 and NUM_2. CURRENT_END gives the end of the storage in use, so
+ we know how much data to copy up.
+
+ If you call this function, you must zero out pending_exact. */
+
+static void
+insert_op_2 (op, there, current_end, num_1, num_2)
+ re_opcode_t op;
+ unsigned char *there, *current_end;
+ int num_1, num_2;
+{
+ register unsigned char *pfrom = current_end;
+ register unsigned char *pto = current_end + 5;
+
+ while (pfrom != there)
+ *--pto = *--pfrom;
+
+ there[0] = (unsigned char) op;
+ STORE_NUMBER (there + 1, num_1);
+ STORE_NUMBER (there + 3, num_2);
+}
+
+
+/* Return true if the pattern position P is at a close-group or
+ alternation operator, or if it is a newline and RE_NEWLINE_ORDINARY
+ is not set in SYNTAX. Before checking, though, we skip past all
+ operators that match the empty string.
+
+ This is not quite the dual of what happens with ^. There, we can
+ easily check if the (sub)pattern so far can match only the empty
+ string, because we have seen the pattern, and `laststart' is set to
+ exactly that. But we cannot easily look at the pattern yet to come
+ to see if it matches the empty string; that would require us to compile
+ the pattern, then go back and analyze the pattern after every
+ endline. POSIX required this at one point (that $ be in a
+ ``trailing'' position to be considered an anchor), so we implemented
+ it, but it was slow and took lots of code, and we were never really
+ convinced it worked in all cases. So now it's gone, and we live with
+ the slight inconsistency between ^ and $. */
+
+static boolean
+at_endline_op_p (p, pend, syntax)
+ const char *p, *pend;
+ int syntax;
+{
+ boolean context_indep = !!(syntax & RE_CONTEXT_INDEP_ANCHORS);
+
+ /* Skip past operators that match the empty string. (Except we don't
+ handle empty groups.) */
+ while (p < pend)
+ {
+ if (context_indep && (*p == '^' || *p == '$'))
+ p++;
+
+ /* All others start with \. */
+ else if (*p == '\\' && p + 1 < pend
+ && (p[1] == 'b' || p[1] == 'B'
+ || p[1] == '<' || p[1] == '>'
+ || p[1] == '`' || p[1] == '\''
+#ifdef emacs
+ || p[1] == '='
+#endif
+ ))
+ p += 2;
+
+ else /* Not an empty string operator. */
+ break;
+ }
+
+ /* See what we're at now. */
+ return p < pend
+ && ((!(syntax & RE_NEWLINE_ORDINARY) && *p == '\n')
+ || (syntax & RE_NO_BK_PARENS
+ ? *p == ')'
+ : *p == '\\' && p + 1 < pend && p[1] == ')')
+ || (syntax & RE_NO_BK_VBAR
+ ? *p == '|'
+ : (*p == '\\' && p + 1 < pend && p[1] == '|')));
+}
+
+
+/* Returns true if REGNUM is in one of COMPILE_STACK's elements and
+ false if it's not. */
+
+static boolean
+group_in_compile_stack (compile_stack, regnum)
+ compile_stack_type compile_stack;
+ regnum_t regnum;
+{
+ int this_element;
+
+ for (this_element = compile_stack.avail - 1;
+ this_element >= 0;
+ this_element--)
+ if (compile_stack.stack[this_element].regnum == regnum)
+ return true;
+
+ return false;
+}
+
+/* Failure stack declarations and macros; both re_compile_fastmap and
+ re_match_2 use a failure stack. These have to be macros because of
+ REGEX_ALLOCATE. */
+
+
+/* Number of failure points for which to initially allocate space
+ when matching. If this number is exceeded, we allocate more
+ space---so it is not a hard limit. */
+#ifndef INIT_FAILURE_ALLOC
+#define INIT_FAILURE_ALLOC 5
+#endif
+
+/* Roughly the maximum number of failure points on the stack. Would be
+ exactly that if always used MAX_FAILURE_SPACE each time we failed.
+ This is a variable only so users of regex can assign to it; we never
+ change it ourselves. */
+int re_max_failures = 2000;
+
+typedef const unsigned char *failure_stack_elt_t;
+
+typedef struct
+{
+ failure_stack_elt_t *stack;
+ unsigned size;
+ unsigned avail; /* Offset of next open position. */
+} failure_stack_type;
+
+#define FAILURE_STACK_EMPTY() (failure_stack.avail == 0)
+#define FAILURE_STACK_PTR_EMPTY() (failure_stack_ptr->avail == 0)
+#define FAILURE_STACK_FULL() (failure_stack.avail == failure_stack.size)
+#define FAILURE_STACK_TOP() (failure_stack.stack[failure_stack.avail])
+
+
+/* Initialize FAILURE_STACK. Return 1 if success, 0 if not. */
+
+#define INIT_FAILURE_STACK(failure_stack) \
+ ((failure_stack).stack = (failure_stack_elt_t *) \
+ REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (failure_stack_elt_t)), \
+ (failure_stack).stack == NULL \
+ ? 0 \
+ : ((failure_stack).size = INIT_FAILURE_ALLOC, \
+ (failure_stack).avail = 0, \
+ 1))
+
+
+/* Double the size of FAILURE_STACK, up to approximately
+ `re_max_failures' items.
+
+ Return 1 if succeeds, and 0 if either ran out of memory
+ allocating space for it or it was already too large.
+
+ REGEX_REALLOCATE requires `destination' be declared. */
+
+#define DOUBLE_FAILURE_STACK(failure_stack) \
+ ((failure_stack).size > re_max_failures * MAX_FAILURE_ITEMS \
+ ? 0 \
+ : ((failure_stack).stack = (failure_stack_elt_t *) \
+ REGEX_REALLOCATE ((failure_stack).stack, \
+ ((failure_stack).size << 1) * sizeof (failure_stack_elt_t)), \
+ \
+ (failure_stack).stack == NULL \
+ ? 0 \
+ : ((failure_stack).size <<= 1, \
+ 1)))
+
+
+/* Push PATTERN_OP on FAILURE_STACK.
+
+ Return 1 if was able to do so and 0 if ran out of memory allocating
+ space to do so. */
+#define PUSH_PATTERN_OP(pattern_op, failure_stack) \
+ ((FAILURE_STACK_FULL () \
+ && !DOUBLE_FAILURE_STACK (failure_stack)) \
+ ? 0 \
+ : ((failure_stack).stack[(failure_stack).avail++] = pattern_op, \
+ 1))
+
+/* This pushes an item onto the failure stack. Must be a four-byte
+ value. Assumes the variable `failure_stack'. Probably should only
+ be called from within `PUSH_FAILURE_POINT'. */
+#define PUSH_FAILURE_ITEM(item) \
+ failure_stack.stack[failure_stack.avail++] = (failure_stack_elt_t) item
+
+/* The complement operation. Assumes stack is nonempty, and pointed to
+ `failure_stack_ptr'. */
+#define POP_FAILURE_ITEM() \
+ failure_stack_ptr->stack[--failure_stack_ptr->avail]
+
+/* Used to omit pushing failure point id's when we're not debugging. */
+#ifdef DEBUG
+#define DEBUG_PUSH PUSH_FAILURE_ITEM
+#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_ITEM ()
+#else
+#define DEBUG_PUSH(item)
+#define DEBUG_POP(item_addr)
+#endif
+
+
+/* Push the information about the state we will need
+ if we ever fail back to it.
+
+ Requires variables failure_stack, regstart, regend, reg_info, and
+ num_regs be declared. DOUBLE_FAILURE_STACK requires `destination' be
+ declared.
+
+ Does `return FAILURE_CODE' if runs out of memory. */
+
+#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \
+ do { \
+ char *destination; \
+ /* Must be int, so when we don't save any registers, the arithmetic \
+ of 0 + -1 isn't done as unsigned. */ \
+ int this_reg; \
+ \
+ DEBUG_STATEMENT (failure_id++); \
+ DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \
+ DEBUG_PRINT2 (" Before push, next avail: %d\n", (failure_stack).avail);\
+ DEBUG_PRINT2 (" size: %d\n", (failure_stack).size);\
+ \
+ DEBUG_PRINT2 (" slots needed: %d\n", NUM_FAILURE_ITEMS); \
+ DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \
+ \
+ /* Ensure we have enough space allocated for what we will push. */ \
+ while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \
+ { \
+ if (!DOUBLE_FAILURE_STACK (failure_stack)) \
+ return failure_code; \
+ \
+ DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \
+ (failure_stack).size); \
+ DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\
+ } \
+ \
+ /* Push the info, starting with the registers. */ \
+ DEBUG_PRINT1 ("\n"); \
+ \
+ for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \
+ this_reg++) \
+ { \
+ DEBUG_PRINT2 (" Pushing reg: %d\n", this_reg); \
+ DEBUG_STATEMENT (num_regs_pushed++); \
+ \
+ DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \
+ PUSH_FAILURE_ITEM (regstart[this_reg]); \
+ \
+ DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \
+ PUSH_FAILURE_ITEM (regend[this_reg]); \
+ \
+ DEBUG_PRINT2 (" info: 0x%x\n ", reg_info[this_reg]); \
+ DEBUG_PRINT2 (" match_nothing=%d", \
+ REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" matched_something=%d", \
+ MATCHED_SOMETHING (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" ever_matched=%d", \
+ EVER_MATCHED_SOMETHING (reg_info[this_reg])); \
+ DEBUG_PRINT1 ("\n"); \
+ PUSH_FAILURE_ITEM (reg_info[this_reg].word); \
+ } \
+ \
+ DEBUG_PRINT2 (" Pushing low active reg: %d\n", lowest_active_reg); \
+ PUSH_FAILURE_ITEM (lowest_active_reg); \
+ \
+ DEBUG_PRINT2 (" Pushing high active reg: %d\n", highest_active_reg);\
+ PUSH_FAILURE_ITEM (highest_active_reg); \
+ \
+ DEBUG_PRINT2 (" Pushing pattern 0x%x: ", pattern_place); \
+ DEBUG_COMPILED_PATTERN_PRINTER (bufp, pattern_place, pend); \
+ PUSH_FAILURE_ITEM (pattern_place); \
+ \
+ DEBUG_PRINT2 (" Pushing string 0x%x: `", string_place); \
+ DEBUG_DOUBLE_STRING_PRINTER (string_place, string1, size1, string2, \
+ size2); \
+ DEBUG_PRINT1 ("'\n"); \
+ PUSH_FAILURE_ITEM (string_place); \
+ \
+ DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \
+ DEBUG_PUSH (failure_id); \
+ } while (0)
+
+/* This is the number of items that are pushed and popped on the stack
+ for each register. */
+#define NUM_REG_ITEMS 3
+
+/* Individual items aside from the registers. */
+#ifdef DEBUG
+#define NUM_NONREG_ITEMS 5 /* Includes failure point id. */
+#else
+#define NUM_NONREG_ITEMS 4
+#endif
+
+/* We push at most this many items on the stack. */
+#define MAX_FAILURE_ITEMS \
+ ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS)
+
+/* We actually push this many items. */
+#define NUM_FAILURE_ITEMS \
+ ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS \
+ + NUM_NONREG_ITEMS)
+
+/* How many items can still be added to the stack without overflowing it. */
+#define REMAINING_AVAIL_SLOTS \
+ ((failure_stack).size - (failure_stack).avail)
+
+/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in
+ BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible
+ characters can start a string that matches the pattern. This fastmap
+ is used by re_search to skip quickly over impossible starting points.
+
+ The caller must supply the address of a (1 << BYTEWIDTH)-byte data
+ area as BUFP->fastmap. The other components of BUFP describe the
+ pattern to be used.
+
+ We set the `can_be_null' and `fastmap_accurate' fields in the pattern
+
+ Returns 0 if it can compile a fastmap. Returns -2 if there is an
+ internal error. */
+
+int
+re_compile_fastmap (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ int j, k;
+ failure_stack_type failure_stack;
+#ifndef REGEX_MALLOC
+ char *destination;
+#endif
+ /* We don't push any register information onto the failure stack. */
+ unsigned num_regs = 0;
+
+ register char *fastmap = bufp->fastmap;
+ unsigned char *pattern = bufp->buffer;
+ unsigned long size = bufp->used;
+ const unsigned char *p = pattern;
+ register unsigned char *pend = pattern + size;
+
+ INIT_FAILURE_STACK (failure_stack);
+
+ bzero (fastmap, 1 << BYTEWIDTH);
+ bufp->fastmap_accurate = 1; /* It will be when we're done. */
+ bufp->can_be_null = 0;
+
+ while (p)
+ {
+ boolean is_a_succeed_n = false;
+
+ if (p == pend)
+ if (FAILURE_STACK_EMPTY ())
+ {
+ bufp->can_be_null = 1;
+ break;
+ }
+ else
+ p = failure_stack.stack[--failure_stack.avail];
+
+#ifdef SWITCH_ENUM_BUG
+ switch ((int) ((re_opcode_t) *p++))
+#else
+ switch ((re_opcode_t) *p++)
+#endif
+ {
+ case exactn:
+ fastmap[p[1]] = 1;
+ break;
+
+
+ case charset:
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
+ fastmap[j] = 1;
+ break;
+
+
+ case charset_not:
+ /* Chars beyond end of map must be allowed. */
+ for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
+ fastmap[j] = 1;
+
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
+ fastmap[j] = 1;
+ break;
+
+
+ case no_op:
+ case begline:
+ case begbuf:
+ case endbuf:
+ case wordbound:
+ case notwordbound:
+ case wordbeg:
+ case wordend:
+ continue;
+
+
+ case endline:
+ if (!bufp->can_be_null)
+ bufp->can_be_null = 2;
+ break;
+
+
+ case no_pop_jump_n:
+ case pop_failure_jump:
+ case maybe_pop_jump:
+ case no_pop_jump:
+ case jump_past_next_alt:
+ case dummy_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (j, p);
+ p += j;
+ if (j > 0)
+ continue;
+
+ /* Jump backward reached implies we just went through
+ the body of a loop and matched nothing. Opcode jumped to
+ should be an on_failure_jump or succeed_n. Just treat it
+ like an ordinary jump. For a * loop, it has pushed its
+ failure point already; if so, discard that as redundant. */
+
+ if ((re_opcode_t) *p != on_failure_jump
+ && (re_opcode_t) *p != succeed_n)
+ continue;
+
+ p++;
+ EXTRACT_NUMBER_AND_INCR (j, p);
+ p += j;
+
+ /* If what's on the stack is where we are now, pop it. */
+ if (!FAILURE_STACK_EMPTY ()
+ && failure_stack.stack[failure_stack.avail - 1] == p)
+ failure_stack.avail--;
+
+ continue;
+
+
+ case on_failure_jump:
+ handle_on_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (j, p);
+
+ /* For some patterns, e.g., `(a?)?', `p+j' here points to the
+ end of the pattern. We don't want to push such a point,
+ since when we restore it above, entering the switch will
+ increment `p' past the end of the pattern. We don't need
+ to push such a point since there can't be any more
+ possibilities for the fastmap beyond pend. */
+ if (p + j < pend)
+ {
+ if (!PUSH_PATTERN_OP (p + j, failure_stack))
+ return -2;
+ }
+
+ if (is_a_succeed_n)
+ EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */
+
+ continue;
+
+
+ case succeed_n:
+ is_a_succeed_n = true;
+
+ /* Get to the number of times to succeed. */
+ p += 2;
+
+ /* Increment p past the n for when k != 0. */
+ EXTRACT_NUMBER_AND_INCR (k, p);
+ if (k == 0)
+ {
+ p -= 4;
+ goto handle_on_failure_jump;
+ }
+ continue;
+
+
+ case set_number_at:
+ p += 4;
+ continue;
+
+
+ case start_memory:
+ case stop_memory:
+ p += 2;
+ continue;
+
+
+ /* I don't understand this case (any of it). --karl */
+ case duplicate:
+ bufp->can_be_null = 1;
+ fastmap['\n'] = 1;
+
+
+ case anychar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (j != '\n')
+ fastmap[j] = 1;
+ if (bufp->can_be_null)
+ return 0;
+
+ /* Don't return; check the alternative paths
+ so we can set can_be_null if appropriate. */
+ break;
+
+
+ case wordchar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) == Sword)
+ fastmap[j] = 1;
+ break;
+
+
+ case notwordchar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) != Sword)
+ fastmap[j] = 1;
+ break;
+
+
+#ifdef emacs
+ case before_dot:
+ case at_dot:
+ case after_dot:
+ continue;
+
+
+ case syntaxspec:
+ k = *p++;
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) == (enum syntaxcode) k)
+ fastmap[j] = 1;
+ break;
+
+
+ case notsyntaxspec:
+ k = *p++;
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) != (enum syntaxcode) k)
+ fastmap[j] = 1;
+ break;
+#endif /* not emacs */
+
+ default:
+ abort ();
+ } /* switch *p++ */
+
+ /* Getting here means we have successfully found the possible starting
+ characters of one path of the pattern. We need not follow this
+ path any farther. Instead, look at the next alternative
+ remembered in the stack, or quit. The test at the top of the
+ loop does these things. */
+ p = pend;
+ } /* while p */
+
+ return 0;
+} /* re_compile_fastmap */
+
+/* Searching routines. */
+
+/* Like re_search_2, below, but only one string is specified, and
+ doesn't let you say where to stop matching. */
+
+int
+re_search (bufp, string, size, startpos, range, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ int size, startpos, range;
+ struct re_registers *regs;
+{
+ return re_search_2 (bufp, NULL, 0, string, size, startpos, range,
+ regs, size);
+}
+
+
+/* Using the compiled pattern in BUFP->buffer, first tries to match the
+ virtual concatenation of STRING1 and STRING2, starting first at index
+ STARTPOS, then at STARTPOS + 1, and so on.
+
+ STRING1 and STRING2 have length SIZE1 and SIZE2, respectively.
+
+ RANGE is how far to scan while trying to match. RANGE = 0 means try
+ only at STARTPOS; in general, the last start tried is STARTPOS +
+ RANGE.
+
+ In REGS, return the indices of the virtual concatenation of STRING1
+ and STRING2 that matched the entire BUFP->buffer and its contained
+ subexpressions.
+
+ Do not consider matching one past the index STOP in the virtual
+ concatenation of STRING1 and STRING2.
+
+ We return either the position in the strings at which the match was
+ found, -1 if no match, or -2 if error (such as failure
+ stack overflow). */
+
+int
+re_search_2 (bufp, string1, size1, string2, size2, startpos, range,
+ regs, stop)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ int size1, size2;
+ int startpos;
+ int range;
+ struct re_registers *regs;
+ int stop;
+{
+ int val;
+ register char *fastmap = bufp->fastmap;
+ register char *translate = bufp->translate;
+ int total_size = size1 + size2;
+ int endpos = startpos + range;
+
+ /* Check for out-of-range STARTPOS. */
+ if (startpos < 0 || startpos > total_size)
+ return -1;
+
+ /* Fix up RANGE if it might eventually take us outside
+ the virtual concatenation of STRING1 and STRING2. */
+ if (endpos < -1)
+ range = -1 - startpos;
+ else if (endpos > total_size)
+ range = total_size - startpos;
+
+ /* Update the fastmap now if not correct already. */
+ if (fastmap && !bufp->fastmap_accurate)
+ if (re_compile_fastmap (bufp) == -2)
+ return -2;
+
+ /* If the search isn't to be a backwards one, don't waste time in a
+ long search for a pattern that says it is anchored. */
+ if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf
+ && range > 0)
+ {
+ if (startpos > 0)
+ return -1;
+ else
+ range = 1;
+ }
+
+ for (;;)
+ {
+ /* If a fastmap is supplied, skip quickly over characters that
+ cannot be the start of a match. If the pattern can match the
+ null string, however, we don't want to skip over characters; we
+ want the first null string. */
+ if (fastmap && startpos < total_size && !bufp->can_be_null)
+ {
+ if (range > 0) /* Searching forwards. */
+ {
+ register const char *d;
+ register int lim = 0;
+ int irange = range;
+
+ if (startpos < size1 && startpos + range >= size1)
+ lim = range - (size1 - startpos);
+
+ d = (startpos >= size1 ? string2 - size1 : string1) + startpos;
+
+ /* Written out as an if-else to avoid testing `translate'
+ inside the loop. */
+ if (translate)
+ {
+ while (range > lim
+ && !fastmap[(unsigned char) translate[*d++]])
+ range--;
+ }
+ else
+ {
+ while (range > lim && !fastmap[(unsigned char) *d++])
+ range--;
+ }
+
+ startpos += irange - range;
+ }
+ else /* Searching backwards. */
+ {
+ register char c
+ = (size1 == 0 || startpos >= size1
+ ? string2[startpos - size1]
+ : string1[startpos]);
+
+ if (translate
+ ? !fastmap[(unsigned char) translate[(unsigned char) c]]
+ : !fastmap[(unsigned char) c])
+ goto advance;
+ }
+ }
+
+ /* If can't match the null string, and that's all we have left, fail. */
+ if (range >= 0 && startpos == total_size
+ && fastmap && bufp->can_be_null == 0)
+ return -1;
+
+ val = re_match_2 (bufp, string1, size1, string2, size2,
+ startpos, regs, stop);
+ if (val >= 0)
+ return startpos;
+
+ if (val == -2)
+ return -2;
+
+ advance:
+ if (!range)
+ break;
+ else if (range > 0)
+ {
+ range--;
+ startpos++;
+ }
+ else
+ {
+ range++;
+ startpos--;
+ }
+ }
+ return -1;
+} /* re_search_2 */
+
+/* Declarations and macros for re_match_2. */
+
+static int bcmp_translate ();
+static boolean alt_match_null_string_p (),
+ common_op_match_null_string_p (),
+ group_match_null_string_p ();
+static void pop_failure_point ();
+
+
+/* Structure for per-register (a.k.a. per-group) information.
+ This must not be longer than one word, because we push this value
+ onto the failure stack. Other register information, such as the
+ starting and ending positions (which are addresses), and the list of
+ inner groups (which is a bits list) are maintained in separate
+ variables.
+
+ We are making a (strictly speaking) nonportable assumption here: that
+ the compiler will pack our bit fields into something that fits into
+ the type of `word', i.e., is something that fits into one item on the
+ failure stack. */
+typedef union
+{
+ failure_stack_elt_t word;
+ struct
+ {
+ /* This field is one if this group can match the empty string,
+ zero if not. If not yet determined, `MATCH_NOTHING_UNSET_VALUE'. */
+#define MATCH_NOTHING_UNSET_VALUE 3
+ unsigned match_null_string_p : 2;
+ unsigned is_active : 1;
+ unsigned matched_something : 1;
+ unsigned ever_matched_something : 1;
+ } bits;
+} register_info_type;
+
+#define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p)
+#define IS_ACTIVE(R) ((R).bits.is_active)
+#define MATCHED_SOMETHING(R) ((R).bits.matched_something)
+#define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something)
+
+
+/* Call this when have matched something; it sets `matched' flags for the
+ registers corresponding to the group of which we currently are inside.
+ Also records whether this group ever matched something. We only care
+ about this information at `stop_memory', and then only about the
+ previous time through the loop (if the group is starred or whatever).
+ So it is ok to clear all the nonactive registers here. */
+#define SET_REGS_MATCHED() \
+ do \
+ { \
+ unsigned r; \
+ for (r = lowest_active_reg; r <= highest_active_reg; r++) \
+ { \
+ MATCHED_SOMETHING (reg_info[r]) \
+ = EVER_MATCHED_SOMETHING (reg_info[r]) \
+ = 1; \
+ } \
+ } \
+ while (0)
+
+
+/* This converts a pointer into one or the other of the strings into an
+ offset from the beginning of that string. */
+#define POINTER_TO_OFFSET(pointer) IS_IN_FIRST_STRING (pointer) \
+ ? (pointer) - string1 \
+ : (pointer) - string2 + size1
+
+/* Registers are set to a sentinel value when they haven't yet matched
+ anything. */
+#define REG_UNSET_VALUE ((char *) -1)
+#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
+
+
+/* Macros for dealing with the split strings in re_match_2. */
+
+#define MATCHING_IN_FIRST_STRING (dend == end_match_1)
+
+/* Call before fetching a character with *d. This switches over to
+ string2 if necessary. */
+#define PREFETCH \
+ while (d == dend) \
+ { \
+ /* End of string2 => fail. */ \
+ if (dend == end_match_2) \
+ goto fail; \
+ /* End of string1 => advance to string2. */ \
+ d = string2; \
+ dend = end_match_2; \
+ }
+
+
+/* Test if at very beginning or at very end of the virtual concatenation
+ of string1 and string2. If there is only one string, we've put it in
+ string2. */
+#define AT_STRINGS_BEG (d == (size1 ? string1 : string2) || !size2)
+#define AT_STRINGS_END (d == end2)
+
+
+/* Test if D points to a character which is word-constituent. We have
+ two special cases to check for: if past the end of string1, look at
+ the first character in string2; and if before the beginning of
+ string2, look at the last character in string1.
+
+ We assume there is a string1, so use this in conjunction with
+ AT_STRINGS_BEG. */
+#define LETTER_P(d) \
+ (SYNTAX ((d) == end1 ? *string2 : (d) == string2 - 1 ? *(end1 - 1) : *(d))\
+ == Sword)
+
+/* Test if the character before D and the one at D differ with respect
+ to being word-constituent. */
+#define AT_WORD_BOUNDARY(d) \
+ (AT_STRINGS_BEG || AT_STRINGS_END || LETTER_P (d - 1) != LETTER_P (d))
+
+
+/* Free everything we malloc. */
+#ifdef REGEX_MALLOC
+#define FREE_VARIABLES() \
+ do { \
+ free (failure_stack.stack); \
+ free (regstart); \
+ free (regend); \
+ free (old_regstart); \
+ free (old_regend); \
+ free (reg_info); \
+ free (best_regstart); \
+ free (best_regend); \
+ reg_info = NULL; \
+ failure_stack.stack = NULL; \
+ regstart = regend = old_regstart = old_regend \
+ = best_regstart = best_regend = NULL; \
+ } while (0)
+#else /* not REGEX_MALLOC */
+#define FREE_VARIABLES() /* As nothing, since we use alloca. */
+#endif /* not REGEX_MALLOC */
+
+
+/* These values must meet several constraints. They must not be valid
+ register values; since we have a limit of 255 registers (because
+ we use only one byte in the pattern for the register number), we can
+ use numbers larger than 255. They must differ by 1, because of
+ NUM_FAILURE_ITEMS above. And the value for the lowest register must
+ be larger than the value for the highest register, so we do not try
+ to actually save any registers when none are active. */
+#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH)
+#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1)
+
+/* Matching routines. */
+
+#ifndef emacs /* Emacs never uses this. */
+
+/* re_match is like re_match_2 except it takes only a single string. */
+
+int
+re_match (bufp, string, size, pos, regs)
+ const struct re_pattern_buffer *bufp;
+ const char *string;
+ int size, pos;
+ struct re_registers *regs;
+ {
+ return re_match_2 (bufp, NULL, 0, string, size, pos, regs, size);
+}
+#endif /* not emacs */
+
+
+/* re_match_2 matches the compiled pattern in BUFP against the
+ the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
+ and SIZE2, respectively). We start matching at POS, and stop
+ matching at STOP.
+
+ If REGS is non-null and the `no_sub' field of BUFP is nonzero, we
+ store offsets for the substring each group matched in REGS. (If
+ BUFP->caller_allocated_regs is nonzero, we fill REGS->num_regs
+ registers; if zero, we set REGS->num_regs to max (RE_NREGS,
+ re_nsub+1) and allocate the space with malloc before filling.)
+
+ We return -1 if no match, -2 if an internal error (such as the
+ failure stack overflowing). Otherwise, we return the length of the
+ matched substring. */
+
+int
+re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+ const struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ int size1, size2;
+ int pos;
+ struct re_registers *regs;
+ int stop;
+{
+ /* General temporaries. */
+ int mcnt;
+ unsigned char *p1;
+
+ /* Just past the end of the corresponding string. */
+ const char *end1, *end2;
+
+ /* Pointers into string1 and string2, just past the last characters in
+ each to consider matching. */
+ const char *end_match_1, *end_match_2;
+
+ /* Where we are in the data, and the end of the current string. */
+ const char *d, *dend;
+
+ /* Where we are in the pattern, and the end of the pattern. */
+ unsigned char *p = bufp->buffer;
+ register unsigned char *pend = p + bufp->used;
+
+ /* We use this to map every character in the string. */
+ char *translate = bufp->translate;
+
+ /* Failure point stack. Each place that can handle a failure further
+ down the line pushes a failure point on this stack. It consists of
+ restart, regend, and reg_info for all registers corresponding to the
+ subexpressions we're currently inside, plus the number of such
+ registers, and, finally, two char *'s. The first char * is where to
+ resume scanning the pattern; the second one is where to resume
+ scanning the strings. If the latter is zero, the failure point is a
+ ``dummy''; if a failure happens and the failure point is a dummy, it
+ gets discarded and the next next one is tried. */
+ failure_stack_type failure_stack;
+#ifdef DEBUG
+ static unsigned failure_id = 0;
+#endif
+
+ /* We fill all the registers internally, independent of what we
+ return, for use in backreferences. The number here includes
+ register zero. */
+ unsigned num_regs = bufp->re_nsub + 1;
+
+ /* The currently active registers. */
+ unsigned lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+ unsigned highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+
+ /* Information on the contents of registers. These are pointers into
+ the input strings; they record just what was matched (on this
+ attempt) by a subexpression part of the pattern, that is, the
+ regnum-th regstart pointer points to where in the pattern we began
+ matching and the regnum-th regend points to right after where we
+ stopped matching the regnum-th subexpression. (The zeroth register
+ keeps track of what the whole pattern matches.) */
+ const char **regstart
+ = (const char **) REGEX_ALLOCATE (num_regs * sizeof (char *));
+ const char **regend
+ = (const char **) REGEX_ALLOCATE (num_regs * sizeof (char *));
+
+ /* If a group that's operated upon by a repetition operator fails to
+ match anything, then the register for its start will need to be
+ restored because it will have been set to wherever in the string we
+ are when we last see its open-group operator. Similarly for a
+ register's end. */
+ const char **old_regstart
+ = (const char **) REGEX_ALLOCATE (num_regs * sizeof (char *));
+ const char **old_regend
+ = (const char **) REGEX_ALLOCATE (num_regs * sizeof (char *));
+
+ /* The is_active field of reg_info helps us keep track of which (possibly
+ nested) subexpressions we are currently in. The matched_something
+ field of reg_info[reg_num] helps us tell whether or not we have
+ matched any of the pattern so far this time through the reg_num-th
+ subexpression. These two fields get reset each time through any
+ loop their register is in. */
+ register_info_type *reg_info = (register_info_type *)
+ REGEX_ALLOCATE (num_regs * sizeof (register_info_type));
+
+ /* The following record the register info as found in the above
+ variables when we find a match better than any we've seen before.
+ This happens as we backtrack through the failure points, which in
+ turn happens only if we have not yet matched the entire string. */
+ unsigned best_regs_set = 0;
+ const char **best_regstart
+ = (const char **) REGEX_ALLOCATE (num_regs * sizeof (char *));
+ const char **best_regend
+ = (const char **) REGEX_ALLOCATE (num_regs * sizeof (char *));
+
+ /* Used when we pop values we don't care about. */
+ const char **reg_dummy
+ = (const char **) REGEX_ALLOCATE (num_regs * sizeof (char *));
+ register_info_type *reg_info_dummy = (register_info_type *)
+ REGEX_ALLOCATE (num_regs * sizeof (register_info_type));
+
+#ifdef DEBUG
+ /* Counts the total number of registers pushed. */
+ unsigned num_regs_pushed = 0;
+#endif
+
+ DEBUG_PRINT1 ("\n\nEntering re_match_2.\n");
+
+ if (!INIT_FAILURE_STACK (failure_stack))
+ return -2;
+
+ if (!(regstart && regend && old_regstart && old_regend && reg_info
+ && best_regstart && best_regend))
+ {
+ FREE_VARIABLES ();
+ return -2;
+ }
+
+ /* The starting position is bogus. */
+ if (pos < 0 || pos > size1 + size2)
+ {
+ FREE_VARIABLES ();
+ return -1;
+ }
+
+
+ /* Initialize subexpression text positions to -1 to mark ones that no
+ \( or ( and \) or ) has been seen for. Also set all registers to
+ inactive and mark them as not having any inner groups, able to
+ match the empty string, matched anything so far, or ever failed. */
+ for (mcnt = 0; mcnt < num_regs; mcnt++)
+ {
+ regstart[mcnt] = regend[mcnt]
+ = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE;
+
+ REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NOTHING_UNSET_VALUE;
+ IS_ACTIVE (reg_info[mcnt]) = 0;
+ MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+ EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+ }
+
+ IS_ACTIVE (reg_info[0]) = 1;
+
+ /* We move string1 into string2 if the latter's empty---but not if
+ string1 is null. */
+ if (size2 == 0 && string1 != NULL)
+ {
+ string2 = string1;
+ size2 = size1;
+ string1 = 0;
+ size1 = 0;
+ }
+ end1 = string1 + size1;
+ end2 = string2 + size2;
+
+ /* Compute where to stop matching, within the two strings. */
+ if (stop <= size1)
+ {
+ end_match_1 = string1 + stop;
+ end_match_2 = string2;
+ }
+ else
+ {
+ end_match_1 = end1;
+ end_match_2 = string2 + stop - size1;
+ }
+
+ /* `p' scans through the pattern as `d' scans through the data. `dend'
+ is the end of the input string that `d' points within. `d' is
+ advanced into the following input string whenever necessary, but
+ this happens before fetching; therefore, at the beginning of the
+ loop, `d' can be pointing at the end of a string, but it cannot
+ equal `string2'. */
+ if (size1 > 0 && pos <= size1)
+ {
+ d = string1 + pos;
+ dend = end_match_1;
+ }
+ else
+ {
+ d = string2 + pos - size1;
+ dend = end_match_2;
+ }
+
+ DEBUG_PRINT1 ("The compiled pattern is: ");
+ DEBUG_COMPILED_PATTERN_PRINTER (bufp, p, pend);
+ DEBUG_PRINT1 ("The string to match is: `");
+ DEBUG_DOUBLE_STRING_PRINTER (d, string1, size1, string2, size2);
+ DEBUG_PRINT1 ("'\n");
+
+ /* This loops over pattern commands. It exits by returning from the
+ function if the match is complete, or it drops through if the match
+ fails at this starting point in the input data. */
+ for (;;)
+ {
+ DEBUG_PRINT2 ("\n0x%x: ", p);
+
+ if (p == pend)
+ { /* End of pattern means we might have succeeded. */
+ DEBUG_PRINT1 ("End of pattern: ");
+ /* If not end of string, try backtracking. Otherwise done. */
+ if (d != end_match_2)
+ {
+ DEBUG_PRINT1 ("backtracking.\n");
+
+ if (!FAILURE_STACK_EMPTY ())
+ { /* More failure points to try. */
+
+ boolean in_same_string =
+ IS_IN_FIRST_STRING (best_regend[0])
+ == MATCHING_IN_FIRST_STRING;
+
+ /* If exceeds best match so far, save it. */
+ if (!best_regs_set
+ || (in_same_string && d > best_regend[0])
+ || (!in_same_string && !MATCHING_IN_FIRST_STRING))
+ {
+ best_regs_set = 1;
+ best_regend[0] = d; /* Never use regstart[0]. */
+
+ for (mcnt = 1; mcnt < num_regs; mcnt++)
+ {
+ best_regstart[mcnt] = regstart[mcnt];
+ best_regend[mcnt] = regend[mcnt];
+ }
+ }
+ goto fail;
+ }
+
+ /* If no failure points, don't restore garbage. */
+ else if (best_regs_set)
+ {
+ restore_best_regs:
+ /* Restore best match. */
+ d = best_regend[0];
+
+ if (d >= string1 && d <= end1)
+ dend = end_match_1;
+
+ for (mcnt = 0; mcnt < num_regs; mcnt++)
+ {
+ regstart[mcnt] = best_regstart[mcnt];
+ regend[mcnt] = best_regend[mcnt];
+ }
+ }
+ } /* d != end_match_2 */
+
+ DEBUG_PRINT1 ("accepting match.\n");
+
+ /* If caller wants register contents data back, do it. */
+ if (regs && !bufp->no_sub)
+ {
+ /* If they haven't allocated it, we'll do it. */
+ if (!bufp->caller_allocated_regs)
+ {
+ regs->num_regs = MAX (RE_NREGS, num_regs + 1);
+ regs->start = TALLOC (regs->num_regs, regoff_t);
+ regs->end = TALLOC (regs->num_regs, regoff_t);
+ if (regs->start == NULL || regs->end == NULL)
+ return -2;
+ }
+
+ /* Convert the pointer data in `regstart' and `regend' to
+ indices. Register zero has to be set differently,
+ since we haven't kept track of any info for it. */
+ if (regs->num_regs > 0)
+ {
+ regs->start[0] = pos;
+ regs->end[0] = MATCHING_IN_FIRST_STRING
+ ? d - string1
+ : d - string2 + size1;
+ }
+
+ /* Go through the first min (num_regs, regs->num_regs)
+ registers, since that is all we initialized at the
+ beginning. */
+ for (mcnt = 1; mcnt < MIN (num_regs, regs->num_regs); mcnt++)
+ {
+ if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt]))
+ regs->start[mcnt] = regs->end[mcnt] = -1;
+ else
+ {
+ regs->start[mcnt] = POINTER_TO_OFFSET (regstart[mcnt]);
+ regs->end[mcnt] = POINTER_TO_OFFSET (regend[mcnt]);
+ }
+ }
+
+ /* If the regs structure we return has more elements than
+ it than were in the pattern, set the extra elements to
+ -1. If we allocated the registers, this is the case,
+ because we always allocate enough to have at least -1
+ at the end. */
+ for (mcnt = num_regs; mcnt < regs->num_regs; mcnt++)
+ regs->start[mcnt] = regs->end[mcnt] = -1;
+ } /* regs && !bufp->no_sub */
+
+ FREE_VARIABLES ();
+ DEBUG_PRINT2 ("%d registers pushed.\n", num_regs_pushed);
+
+ mcnt = d - pos - (MATCHING_IN_FIRST_STRING
+ ? string1
+ : string2 - size1);
+
+ DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt);
+
+ return mcnt;
+ }
+
+ /* Otherwise match next pattern command. */
+#ifdef SWITCH_ENUM_BUG
+ switch ((int) ((re_opcode_t) *p++))
+#else
+ switch ((re_opcode_t) *p++)
+#endif
+ {
+ /* Ignore these. Used to ignore the n of succeed_n's which
+ currently have n == 0. */
+ case no_op:
+ DEBUG_PRINT1 ("EXECUTING no_op.\n");
+ break;
+
+
+ /* Match the next n pattern characters exactly. The following
+ byte in the pattern defines n, and the n bytes after that
+ are the characters to match. */
+ case exactn:
+ mcnt = *p++;
+ DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt);
+
+ /* This is written out as an if-else so we don't waste time
+ testing `translate' inside the loop. */
+ if (translate)
+ {
+ do
+ {
+ PREFETCH;
+ if (translate[(unsigned char) *d++] != (char) *p++)
+ goto fail;
+ }
+ while (--mcnt);
+ }
+ else
+ {
+ do
+ {
+ PREFETCH;
+ if (*d++ != (char) *p++) goto fail;
+ }
+ while (--mcnt);
+ }
+ SET_REGS_MATCHED ();
+ break;
+
+
+ /* Match anything but possibly a newline or a null. */
+ case anychar:
+ DEBUG_PRINT1 ("EXECUTING anychar.\n");
+
+ PREFETCH;
+
+ if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n')
+ || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000'))
+ goto fail;
+
+ SET_REGS_MATCHED ();
+ d++;
+ break;
+
+
+ case charset:
+ case charset_not:
+ {
+ register unsigned char c;
+ boolean not = (re_opcode_t) *(p - 1) == charset_not;
+
+ DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : "");
+
+ PREFETCH;
+ c = TRANSLATE (*d); /* The character to match. */
+
+ if (c < (unsigned char) (*p * BYTEWIDTH)
+ && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+ not = !not;
+
+ p += 1 + *p;
+
+ if (!not) goto fail;
+
+ SET_REGS_MATCHED ();
+ d++;
+ break;
+ }
+
+
+ /* The beginning of a group is represented by start_memory.
+ The arguments are the register number in the next byte, and the
+ number of groups inner to this one in the next. The text
+ matched within the group is recorded (in the internal
+ registers data structure) under the register number. */
+ case start_memory:
+ DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]);
+
+ /* Find out if this group can match the empty string. */
+ p1 = p; /* To send to group_match_null_string_p. */
+
+ if (REG_MATCH_NULL_STRING_P (reg_info[*p])
+ == MATCH_NOTHING_UNSET_VALUE)
+ REG_MATCH_NULL_STRING_P (reg_info[*p])
+ = group_match_null_string_p (&p1, pend, reg_info);
+
+ /* Save the position in the string where we were the last time
+ we were at this open-group operator in case the group is
+ operated upon by a repetition operator, e.g., with `(a*)*b'
+ against `ab'; then we want to ignore where we are now in
+ the string in case this attempt to match fails. */
+ old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+ ? REG_UNSET (regstart[*p]) ? d : regstart[*p]
+ : regstart[*p];
+ DEBUG_PRINT2 (" old_regstart: %d\n",
+ POINTER_TO_OFFSET (old_regstart[*p]));
+
+ regstart[*p] = d;
+ DEBUG_PRINT2 (" regstart: %d\n", POINTER_TO_OFFSET (regstart[*p]));
+
+ IS_ACTIVE (reg_info[*p]) = 1;
+ MATCHED_SOMETHING (reg_info[*p]) = 0;
+
+ /* This is the new highest active register. */
+ highest_active_reg = *p;
+
+ /* If nothing was active before, this is the new lowest active
+ register. */
+ if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+ lowest_active_reg = *p;
+
+ /* Move past the register number and inner group count. */
+ p += 2;
+ break;
+
+
+ /* The stop_memory opcode represents the end of a group. Its
+ arguments are the same as start_memory's: the register
+ number, and the number of inner groups. */
+ case stop_memory:
+ DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]);
+
+ /* We need to save the string position the last time we were at
+ this close-group operator in case the group is operated
+ upon by a repetition operator, e.g., with `((a*)*(b*)*)*'
+ against `aba'; then we want to ignore where we are now in
+ the string in case this attempt to match fails. */
+ old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+ ? REG_UNSET (regend[*p]) ? d : regend[*p]
+ : regend[*p];
+ DEBUG_PRINT2 (" old_regend: %d\n",
+ POINTER_TO_OFFSET (old_regend[*p]));
+
+ regend[*p] = d;
+ DEBUG_PRINT2 (" regend: %d\n", POINTER_TO_OFFSET (regend[*p]));
+
+ /* This register isn't active anymore. */
+ IS_ACTIVE (reg_info[*p]) = 0;
+
+ /* If this was the only register active, nothing is active
+ anymore. */
+ if (lowest_active_reg == highest_active_reg)
+ {
+ lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+ highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+ }
+ else
+ { /* We must scan for the new highest active register, since
+ it isn't necessarily one less than now: consider
+ (a(b)c(d(e)f)g). When group 3 ends, after the f), the
+ new highest active register is 1. */
+ unsigned char r = *p - 1;
+
+ /* This loop will always terminate, because register 0 is
+ always active. */
+ assert (IS_ACTIVE (reg_info[0]));
+ while (!IS_ACTIVE (reg_info[r]))
+ r--;
+
+ /* If we end up at register zero, that means that we saved
+ the registers as the result of an on_failure_jump, not
+ a start_memory, and we jumped to past the innermost
+ stop_memory. For example, in ((.)*). We save
+ registers 1 and 2 as a result of the *, but when we pop
+ back to the second ), we are at the stop_memory 1.
+ Thus, nothing is active. */
+ if (r != 0)
+ highest_active_reg = r;
+ else
+ {
+ lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+ highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+ }
+ }
+
+ /* If just failed to match something this time around with a
+ group that's operated on by a repetition operator, try to
+ force exit from the ``loop,'' and restore the register
+ information for this group that we had before trying this
+ last match. */
+ if ((!MATCHED_SOMETHING (reg_info[*p])
+ || (re_opcode_t) p[-3] == start_memory)
+ && (p + 2) < pend)
+ {
+ boolean is_a_jump_n = false;
+
+ p1 = p + 2;
+ mcnt = 0;
+ switch ((re_opcode_t) *p1++)
+ {
+ case no_pop_jump_n:
+ is_a_jump_n = true;
+ case pop_failure_jump:
+ case maybe_pop_jump:
+ case no_pop_jump:
+ case dummy_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ if (is_a_jump_n)
+ p1 += 2;
+ break;
+
+ default:
+ /* do nothing */ ;
+ }
+ p1 += mcnt;
+
+ /* If the next operation is a jump backwards in the pattern
+ to an on_failure_jump right before the start_memory
+ corresponding to this stop_memory, exit from the loop
+ by forcing a failure after pushing on the stack the
+ on_failure_jump's jump in the pattern, and d. */
+ if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump
+ && (re_opcode_t) p1[3] == start_memory && p1[4] == *p)
+ {
+ /* If this group ever matched anything, then restore
+ what its registers were before trying this last
+ failed match, e.g., with `(a*)*b' against `ab' for
+ regstart[1], and, e.g., with `((a*)*(b*)*)*'
+ against `aba' for regend[3].
+
+ Also restore the registers for inner groups for,
+ e.g., `((a*)(b*))*' against `aba' (register 3 would
+ otherwise get trashed). */
+
+ if (EVER_MATCHED_SOMETHING (reg_info[*p]))
+ {
+ unsigned r;
+
+ EVER_MATCHED_SOMETHING (reg_info[*p]) = 0;
+
+ /* Restore this and inner groups' (if any) registers. */
+ for (r = *p; r < *p + *(p + 1); r++)
+ {
+ regstart[r] = old_regstart[r];
+
+ /* xx why this test? */
+ if ((int) old_regend[r] >= (int) regstart[r])
+ regend[r] = old_regend[r];
+ }
+ }
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ PUSH_FAILURE_POINT (p1 + mcnt, d, -2);
+
+ goto fail;
+ }
+ }
+
+ /* Move past the register number and the inner group count. */
+ p += 2;
+ break;
+
+
+ /* \<digit> has been turned into a `duplicate' command which is
+ followed by the numeric value of <digit> as the register number. */
+ case duplicate:
+ {
+ register const char *d2, *dend2;
+ int regno = *p++; /* Get which register to match against. */
+ DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno);
+
+ /* Can't back reference a group which we've never matched. */
+ if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno]))
+ goto fail;
+
+ /* Where in input to try to start matching. */
+ d2 = regstart[regno];
+
+ /* Where to stop matching; if both the place to start and
+ the place to stop matching are in the same string, then
+ set to the place to stop, otherwise, for now have to use
+ the end of the first string. */
+
+ dend2 = ((IS_IN_FIRST_STRING (regstart[regno])
+ == IS_IN_FIRST_STRING (regend[regno]))
+ ? regend[regno] : end_match_1);
+ for (;;)
+ {
+ /* If necessary, advance to next segment in register
+ contents. */
+ while (d2 == dend2)
+ {
+ if (dend2 == end_match_2) break;
+ if (dend2 == regend[regno]) break;
+
+ /* End of string1 => advance to string2. */
+ d2 = string2;
+ dend2 = regend[regno];
+ }
+ /* At end of register contents => success */
+ if (d2 == dend2) break;
+
+ /* If necessary, advance to next segment in data. */
+ PREFETCH;
+
+ /* How many characters left in this segment to match. */
+ mcnt = dend - d;
+
+ /* Want how many consecutive characters we can match in
+ one shot, so, if necessary, adjust the count. */
+ if (mcnt > dend2 - d2)
+ mcnt = dend2 - d2;
+
+ /* Compare that many; failure if mismatch, else move
+ past them. */
+ if (translate
+ ? bcmp_translate (d, d2, mcnt, translate)
+ : bcmp (d, d2, mcnt))
+ goto fail;
+ d += mcnt, d2 += mcnt;
+ }
+ }
+ break;
+
+
+ /* begline matches the empty string at the beginning of the string
+ (unless `not_bol' is set in `bufp'), and, if
+ `newline_anchor' is set, after newlines. */
+ case begline:
+ DEBUG_PRINT1 ("EXECUTING begline.\n");
+
+ if (AT_STRINGS_BEG)
+ {
+ if (!bufp->not_bol) break;
+ }
+ else if (d[-1] == '\n' && bufp->newline_anchor)
+ {
+ break;
+ }
+ /* In all other cases, we fail. */
+ goto fail;
+
+
+ /* endline is the dual of begline. */
+ case endline:
+ DEBUG_PRINT1 ("EXECUTING endline.\n");
+
+ if (AT_STRINGS_END)
+ {
+ if (!bufp->not_eol) break;
+ }
+
+ /* We have to ``prefetch'' the next character. */
+ else if ((d == end1 ? *string2 : *d) == '\n'
+ && bufp->newline_anchor)
+ {
+ break;
+ }
+ goto fail;
+
+
+ /* Match at the very beginning of the data. */
+ case begbuf:
+ DEBUG_PRINT1 ("EXECUTING begbuf.\n");
+ if (AT_STRINGS_BEG)
+ break;
+ goto fail;
+
+
+ /* Match at the very end of the data. */
+ case endbuf:
+ DEBUG_PRINT1 ("EXECUTING endbuf.\n");
+ if (AT_STRINGS_END)
+ break;
+ goto fail;
+
+
+ /* on_failure_keep_string_jump is used to optimize `.*\n'. It
+ pushes NULL as the value for the string on the stack. Then
+ pop_failure_point will keep the current value for the string,
+ instead of restoring it. To see why, consider matching
+ `foo\nbar' against `.*\n'. The .* matches the foo; then the
+ . fails against the \n. But the next thing we want to do is
+ match the \n against the \n; if we restored the string value,
+ we would be back at the foo.
+
+ Because this is used only in specific cases, we don't need to
+ go through the hassle of checking all the things that
+ on_failure_jump does, to make sure the right things get saved
+ on the stack. Hence we don't share its code. The only
+ reason to push anything on the stack at all is that otherwise
+ we would have to change anychar's code to do something
+ besides goto fail in this case; that seems worse than this. */
+ case on_failure_keep_string_jump:
+ DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump");
+
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt);
+
+ PUSH_FAILURE_POINT (p + mcnt, NULL, -2);
+ break;
+
+
+ /* Uses of on_failure_jump:
+
+ Each alternative starts with an on_failure_jump that points
+ to the beginning of the next alternative. Each alternative
+ except the last ends with a jump that in effect jumps past
+ the rest of the alternatives. (They really jump to the
+ ending jump of the following alternative, because tensioning
+ these jumps is a hassle.)
+
+ Repeats start with an on_failure_jump that points past both
+ the repetition text and either the following jump or
+ pop_failure_jump back to this on_failure_jump. */
+ case on_failure_jump:
+ on_failure:
+ DEBUG_PRINT1 ("EXECUTING on_failure_jump");
+
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt);
+
+ /* If this on_failure_jump comes right before a group (i.e.,
+ the original * applied to a group), save the information
+ for that group and all inner ones, so that if we fail back
+ to this point, the group's information will be correct.
+ For example, in \(a*\)*\1, we only need the preceding group,
+ and in \(\(a*\)b*\)\2, we need the inner group. */
+
+ /* We can't use `p' to check ahead because we push
+ a failure point to `p + mcnt' after we do this. */
+ p1 = p;
+
+ /* We need to skip no_op's before we look for the
+ start_memory in case this on_failure_jump is happening as
+ the result of a completed succeed_n, as in \(a\)\{1,3\}b\1
+ against aba. */
+ while (p1 < pend && (re_opcode_t) *p1 == no_op)
+ p1++;
+
+ if (p1 < pend && (re_opcode_t) *p1 == start_memory)
+ {
+ /* We have a new highest active register now. This will
+ get reset at the start_memory we are about to get to,
+ but we will have saved all the registers relevant to
+ this repetition op, as described above. */
+ highest_active_reg = *(p1 + 1) + *(p1 + 2);
+ if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+ lowest_active_reg = *(p1 + 1);
+ }
+
+ DEBUG_PRINT1 (":\n");
+ PUSH_FAILURE_POINT (p + mcnt, d, -2);
+ break;
+
+
+ /* A smart repeat ends with a maybe_pop_jump.
+ We change it either to a pop_failure_jump or a no_pop_jump. */
+ case maybe_pop_jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt);
+ {
+ register unsigned char *p2 = p;
+
+ /* Compare the beginning of the repeat with what in the
+ pattern follows its end. If we can establish that there
+ is nothing that they would both match, i.e., that we
+ would have to backtrack because of (as in, e.g., `a*a')
+ then we can change to pop_failure_jump, because we'll
+ never have to backtrack. */
+
+ /* Skip over open/close-group commands. */
+ while (p2 + 2 < pend
+ && ((re_opcode_t) *p2 == stop_memory
+ || (re_opcode_t) *p2 == start_memory))
+ p2 += 3; /* Skip over args, too. */
+
+ /* If we're at the end of the pattern, we can change. */
+ if (p2 == pend)
+ p[-3] = (unsigned char) pop_failure_jump;
+
+ else if ((re_opcode_t) *p2 == exactn
+ || (bufp->newline_anchor && (re_opcode_t) *p2 == endline))
+ {
+ register unsigned char c
+ = *p2 == (unsigned char) endline ? '\n' : p2[2];
+ p1 = p + mcnt;
+
+ /* p1[0] ... p1[2] are the on_failure_jump corresponding
+ to the maybe_finalize_jump of this case. Examine what
+ follows it. */
+ if ((re_opcode_t) p1[3] == exactn && p1[5] != c)
+ p[-3] = (unsigned char) pop_failure_jump;
+ else if ((re_opcode_t) p1[3] == charset
+ || (re_opcode_t) p1[3] == charset_not)
+ {
+ int not = (re_opcode_t) p1[3] == charset_not;
+
+ if (c < (unsigned char) (p1[4] * BYTEWIDTH)
+ && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+ not = !not;
+
+ /* `not' is equal to 1 if c would match, which means
+ that we can't change to pop_failure_jump. */
+ if (!not)
+ p[-3] = (unsigned char) pop_failure_jump;
+ }
+ }
+ }
+ p -= 2; /* Point at relative address again. */
+ if ((re_opcode_t) p[-1] != pop_failure_jump)
+ {
+ p[-1] = (unsigned char) no_pop_jump;
+ goto no_pop;
+ }
+ /* Note fall through. */
+
+
+ /* The end of a simple repeat has a pop_failure_jump back to
+ its matching on_failure_jump, where the latter will push a
+ failure point. The pop_failure_jump takes off failure
+ points put on by this pop_failure_jump's matching
+ on_failure_jump; we got through the pattern to here from the
+ matching on_failure_jump, so didn't fail. */
+ case pop_failure_jump:
+ {
+ /* We need to pass separate storage for the lowest and
+ highest registers, even though we aren't interested.
+ Otherwise, we will restore only one register from the
+ stack, since lowest will equal highest in
+ pop_failure_point (since they'll be the same memory
+ location). */
+ unsigned dummy_low, dummy_high;
+ unsigned char *pdummy = NULL;
+
+ DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n");
+ pop_failure_point (bufp, pend,
+#ifdef DEBUG
+ string1, size1, string2, size2,
+#endif
+ &failure_stack, &pdummy, &pdummy,
+ &dummy_low, &dummy_high,
+ &reg_dummy, &reg_dummy, &reg_info_dummy);
+ }
+ /* Note fall through. */
+
+
+ /* Jump without taking off any failure points. */
+ case no_pop_jump:
+ no_pop:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p); /* Get the amount to jump. */
+ DEBUG_PRINT2 ("EXECUTING no_pop_jump %d ", mcnt);
+ p += mcnt; /* Do the jump. */
+ DEBUG_PRINT2 ("(to 0x%x).\n", p);
+ break;
+
+
+ /* We need this opcode so we can detect where alternatives end
+ in `group_match_null_string_p' et al. */
+ case jump_past_next_alt:
+ DEBUG_PRINT1 ("EXECUTING jump_past_next_alt.\n");
+ goto no_pop;
+
+
+ /* Normally, the on_failure_jump pushes a failure point, which
+ then gets popped at pop_failure_jump. We will end up at
+ pop_failure_jump, also, and with a pattern of, say, `a+', we
+ are skipping over the on_failure_jump, so we have to push
+ something meaningless for pop_failure_jump to pop. */
+ case dummy_failure_jump:
+ DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n");
+ /* It doesn't matter what we push for the string here. What
+ the code at `fail' tests is the value for the pattern. */
+ PUSH_FAILURE_POINT (0, 0, -2);
+ goto no_pop;
+
+
+ /* Have to succeed matching what follows at least n times. Then
+ just handle like an on_failure_jump. */
+ case succeed_n:
+ EXTRACT_NUMBER (mcnt, p + 2);
+ DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt);
+
+ /* Originally, this is how many times we HAVE to succeed. */
+ if (mcnt)
+ {
+ mcnt--;
+ p += 2;
+ STORE_NUMBER_AND_INCR (p, mcnt);
+ DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p, mcnt);
+ }
+ else if (mcnt == 0)
+ {
+ DEBUG_PRINT2 (" Setting two bytes from 0x%x to no_op.\n", p+2);
+ p[2] = (unsigned char) no_op;
+ p[3] = (unsigned char) no_op;
+ goto on_failure;
+ }
+#ifdef DEBUG
+ else
+ {
+ fprintf (stderr, "regex: negative n at succeed_n.\n");
+ abort ();
+ }
+#endif /* DEBUG */
+ break;
+
+ case no_pop_jump_n:
+ EXTRACT_NUMBER (mcnt, p + 2);
+ DEBUG_PRINT2 ("EXECUTING no_pop_jump_n %d.\n", mcnt);
+
+ /* Originally, this is how many times we CAN jump. */
+ if (mcnt)
+ {
+ mcnt--;
+ STORE_NUMBER(p + 2, mcnt);
+ goto no_pop;
+ }
+ /* If don't have to jump any more, skip over the rest of command. */
+ else
+ p += 4;
+ break;
+
+ case set_number_at:
+ {
+ DEBUG_PRINT1 ("EXECUTING set_number_at.\n");
+
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ p1 = p + mcnt;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ STORE_NUMBER (p1, mcnt);
+ break;
+ }
+
+ case wordbound:
+ DEBUG_PRINT1 ("EXECUTING wordbound.\n");
+ if (AT_WORD_BOUNDARY (d))
+ break;
+ goto fail;
+
+ case notwordbound:
+ DEBUG_PRINT1 ("EXECUTING notwordbound.\n");
+ if (AT_WORD_BOUNDARY (d))
+ goto fail;
+ break;
+
+ case wordbeg:
+ DEBUG_PRINT1 ("EXECUTING wordbeg.\n");
+ if (LETTER_P (d) && (AT_STRINGS_BEG || !LETTER_P (d - 1)))
+ break;
+ goto fail;
+
+ case wordend:
+ DEBUG_PRINT1 ("EXECUTING wordend.\n");
+ if (!AT_STRINGS_BEG && LETTER_P (d - 1)
+ && (!LETTER_P (d) || AT_STRINGS_END))
+ break;
+ goto fail;
+
+#ifdef emacs
+#ifdef emacs19
+ case before_dot:
+ DEBUG_PRINT1 ("EXECUTING before_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) >= point)
+ goto fail;
+ break;
+
+ case at_dot:
+ DEBUG_PRINT1 ("EXECUTING at_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) != point)
+ goto fail;
+ break;
+
+ case after_dot:
+ DEBUG_PRINT1 ("EXECUTING after_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) <= point)
+ goto fail;
+ break;
+#else /* not emacs19 */
+ case at_dot:
+ DEBUG_PRINT1 ("EXECUTING at_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) + 1 != point)
+ goto fail;
+ break;
+#endif /* not emacs19 */
+
+ case syntaxspec:
+ DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt);
+ mcnt = *p++;
+ goto matchsyntax;
+
+ case wordchar:
+ DEBUG_PRINT1 ("EXECUTING wordchar.\n");
+ mcnt = (int) Sword;
+ matchsyntax:
+ PREFETCH;
+ if (SYNTAX (*d++) != (enum syntaxcode) mcnt) goto fail;
+ SET_REGS_MATCHED ();
+ break;
+
+ case notsyntaxspec:
+ DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt);
+ mcnt = *p++;
+ goto matchnotsyntax;
+
+ case notwordchar:
+ DEBUG_PRINT1 ("EXECUTING notwordchar.\n");
+ mcnt = (int) Sword;
+ matchnotsyntax: /* We goto here from notsyntaxspec. */
+ PREFETCH;
+ if (SYNTAX (*d++) == (enum syntaxcode) mcnt) goto fail;
+ SET_REGS_MATCHED ();
+ break;
+
+#else /* not emacs */
+ case wordchar:
+ DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n");
+ PREFETCH;
+ if (!LETTER_P (d))
+ goto fail;
+ SET_REGS_MATCHED ();
+ break;
+
+ case notwordchar:
+ DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n");
+ PREFETCH;
+ if (LETTER_P (d))
+ goto fail;
+ SET_REGS_MATCHED ();
+ break;
+#endif /* not emacs */
+
+ default:
+ abort ();
+ }
+ continue; /* Successfully executed one pattern command; keep going. */
+
+
+ /* We goto here if a matching operation fails. */
+ fail:
+ if (!FAILURE_STACK_EMPTY ())
+ { /* A restart point is known. Restore to that state. */
+ DEBUG_PRINT1 ("\nFAIL:\n");
+ pop_failure_point (bufp, pend,
+#ifdef DEBUG
+ string1, size1, string2, size2,
+#endif
+ &failure_stack, &p, &d, &lowest_active_reg,
+ &highest_active_reg, &regstart, &regend,
+ &reg_info);
+
+ /* If this failure point is a dummy, try the next one. */
+ if (!p)
+ goto fail;
+
+ /* If we failed to the end of the pattern, don't examine *p. */
+ assert (p <= pend);
+ if (p < pend)
+ {
+ boolean is_a_jump_n = false;
+
+ /* If failed to a backwards jump that's part of a repetition
+ loop, need to pop this failure point and use the next one. */
+ switch ((re_opcode_t) *p)
+ {
+ case no_pop_jump_n:
+ is_a_jump_n = true;
+ case maybe_pop_jump:
+ case pop_failure_jump:
+ case no_pop_jump:
+ p1 = p + 1;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ p1 += mcnt;
+
+ if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n)
+ || (!is_a_jump_n
+ && (re_opcode_t) *p1 == on_failure_jump))
+ goto fail;
+ break;
+ default:
+ /* do nothing */ ;
+ }
+ }
+
+ if (d >= string1 && d <= end1)
+ dend = end_match_1;
+ }
+ else
+ break; /* Matching at this starting point really fails. */
+ } /* for (;;) */
+
+ if (best_regs_set)
+ goto restore_best_regs;
+
+ FREE_VARIABLES ();
+
+ return -1; /* Failure to match. */
+} /* re_match_2 */
+
+/* Subroutine definitions for re_match_2. */
+
+
+/* Pops what PUSH_FAILURE_STACK pushes. */
+
+static void
+pop_failure_point (bufp, pattern_end,
+#ifdef DEBUG
+ string1, size1, string2, size2,
+#endif
+ failure_stack_ptr, pattern_place, string_place,
+ lowest_active_reg, highest_active_reg,
+ regstart, regend, reg_info)
+ const struct re_pattern_buffer *bufp; /* These not modified. */
+ unsigned char *pattern_end;
+#ifdef DEBUG
+ unsigned char *string1, *string2;
+ int size1, size2;
+#endif
+ failure_stack_type *failure_stack_ptr; /* These get modified. */
+ const unsigned char **pattern_place;
+ const unsigned char **string_place;
+ unsigned *lowest_active_reg, *highest_active_reg;
+ const unsigned char ***regstart;
+ const unsigned char ***regend;
+ register_info_type **reg_info;
+{
+#ifdef DEBUG
+ /* Type is really unsigned; it's declared this way just to avoid a
+ compiler warning. */
+ failure_stack_elt_t failure_id;
+#endif
+ int this_reg;
+ const unsigned char *string_temp;
+
+ assert (!FAILURE_STACK_PTR_EMPTY ());
+
+ /* Remove failure points and point to how many regs pushed. */
+ DEBUG_PRINT1 ("pop_failure_point:\n");
+ DEBUG_PRINT2 (" Before pop, next avail: %d\n", failure_stack_ptr->avail);
+ DEBUG_PRINT2 (" size: %d\n", failure_stack_ptr->size);
+
+ assert (failure_stack_ptr->avail >= NUM_NONREG_ITEMS);
+
+ DEBUG_POP (&failure_id);
+ DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id);
+
+ /* If the saved string location is NULL, it came from an
+ on_failure_keep_string_jump opcode, and we want to throw away the
+ saved NULL, thus retaining our current position in the string. */
+ string_temp = POP_FAILURE_ITEM ();
+ if (string_temp != NULL)
+ *string_place = string_temp;
+
+ DEBUG_PRINT2 (" Popping string 0x%x: `", *string_place);
+ DEBUG_DOUBLE_STRING_PRINTER (*string_place, string1, size1, string2, size2);
+ DEBUG_PRINT1 ("'\n");
+
+ *pattern_place = POP_FAILURE_ITEM ();
+ DEBUG_PRINT2 (" Popping pattern 0x%x: ", *pattern_place);
+ DEBUG_COMPILED_PATTERN_PRINTER (bufp, *pattern_place, pattern_end);
+
+ /* Restore register info. */
+ *highest_active_reg = (unsigned) POP_FAILURE_ITEM ();
+ DEBUG_PRINT2 (" Popping high active reg: %d\n", *highest_active_reg);
+
+ *lowest_active_reg = (unsigned) POP_FAILURE_ITEM ();
+ DEBUG_PRINT2 (" Popping low active reg: %d\n", *lowest_active_reg);
+
+ for (this_reg = *highest_active_reg; this_reg >= *lowest_active_reg;
+ this_reg--)
+ {
+ DEBUG_PRINT2 (" Popping reg: %d\n", this_reg);
+
+ (*reg_info)[this_reg].word = POP_FAILURE_ITEM ();
+ DEBUG_PRINT2 (" info: 0x%x\n", (*reg_info)[this_reg]);
+
+ (*regend)[this_reg] = POP_FAILURE_ITEM ();
+ DEBUG_PRINT2 (" end: 0x%x\n", (*regend)[this_reg]);
+
+ (*regstart)[this_reg] = POP_FAILURE_ITEM ();
+ DEBUG_PRINT2 (" start: 0x%x\n", (*regstart)[this_reg]);
+ }
+} /* pop_failure_point */
+
+
+/* We are passed P pointing to a register number after a start_memory.
+
+ Return true if the pattern up to the corresponding stop_memory can
+ match the empty string, and false otherwise.
+
+ If we find the matching stop_memory, sets P to point to one past its number.
+ Otherwise, sets P to an undefined byte less than or equal to END.
+
+ We don't handle duplicates properly (yet). */
+
+static boolean
+group_match_null_string_p (p, end, reg_info)
+ unsigned char **p, *end;
+ register_info_type *reg_info;
+{
+ int mcnt;
+ /* Point to after the args to the start_memory. */
+ unsigned char *p1 = *p + 2;
+
+ while (p1 < end)
+ {
+ /* Skip over opcodes that can match nothing, and return true or
+ false, as appropriate, when we get to one that can't, or to the
+ matching stop_memory. */
+
+ switch ((re_opcode_t) *p1)
+ {
+ /* Could be either a loop or a series of alternatives. */
+ case on_failure_jump:
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+
+ /* If the next operation is not a jump backwards in the
+ pattern. */
+
+ if (mcnt >= 0)
+ {
+ /* Go through the on_failure_jumps of the alternatives,
+ seeing if any of the alternatives cannot match nothing.
+ The last alternative starts with only a no_pop_jump,
+ whereas the rest start with on_failure_jump and end
+ with a no_pop_jump, e.g., here is the pattern for `a|b|c':
+
+ /on_failure_jump/0/6/exactn/1/a/jump_past_next_alt/0/6
+ /on_failure_jump/0/6/exactn/1/b/jump_past_next_alt/0/3
+ /exactn/1/c
+
+ So, we have to first go through the first (n-1)
+ alternatives and then deal with the last one separately. */
+
+
+ /* Deal with the first (n-1) alternatives, which start
+ with an on_failure_jump (see above) that jumps to right
+ past a jump_past_next_alt. */
+
+ while ((re_opcode_t) p1[mcnt-3] == jump_past_next_alt)
+ {
+ /* `mcnt' holds how many bytes long the alternative
+ is, including the ending `jump_past_next_alt' and
+ its number. */
+
+ if (!alt_match_null_string_p (p1, p1 + mcnt - 3,
+ reg_info))
+ return false;
+
+ /* Move to right after this alternative, including the
+ jump_past_next_alt. */
+ p1 += mcnt;
+
+ /* Break if it's the beginning of an n-th alternative
+ that doesn't begin with an on_failure_jump. */
+ if ((re_opcode_t) *p1 != on_failure_jump)
+ break;
+
+ /* Still have to check that it's not an n-th
+ alternative that starts with an on_failure_jump. */
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ if ((re_opcode_t) p1[mcnt-3] != jump_past_next_alt)
+ {
+ /* Get to the beginning of the n-th alternative. */
+ p1 -= 3;
+ break;
+ }
+ }
+
+ /* Deal with the last alternative: go back and get number
+ of the jump_past_next_alt just before it. `mcnt'
+ contains how many bytes long the alternative is. */
+ EXTRACT_NUMBER (mcnt, p1 - 2);
+
+ if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info))
+ return false;
+
+ p1 += mcnt; /* Get past the n-th alternative. */
+ } /* if mcnt > 0 */
+ break;
+
+
+ case stop_memory:
+ assert (p1[1] == **p);
+ *p = p1 + 2;
+ return true;
+
+
+ default:
+ if (!common_op_match_null_string_p (&p1, end, reg_info))
+ return false;
+ }
+ } /* while p1 < end */
+
+ return false;
+} /* group_match_null_string_p */
+
+
+/* Similar to group_match_null_string_p, but doesn't deal with alternatives:
+ It expects P to be the first byte of a single alternative and END one
+ byte past the last. The alternative can contain groups. */
+
+static boolean
+alt_match_null_string_p (p, end, reg_info)
+ unsigned char *p, *end;
+ register_info_type *reg_info;
+{
+ int mcnt;
+ unsigned char *p1 = p;
+
+ while (p1 < end)
+ {
+ /* Skip over opcodes that can match nothing, and break when we get
+ to one that can't. */
+
+ switch ((re_opcode_t) *p1)
+ {
+ /* It's a loop. */
+ case on_failure_jump:
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ p1 += mcnt;
+ break;
+
+ default:
+ if (!common_op_match_null_string_p (&p1, end, reg_info))
+ return false;
+ }
+ } /* while p1 < end */
+
+ return true;
+} /* alt_match_null_string_p */
+
+
+/* Deals with the ops common to group_match_null_string_p and
+ alt_match_null_string_p.
+
+ Sets P to one after the op and its arguments, if any. */
+
+static boolean
+common_op_match_null_string_p (p, end, reg_info)
+ unsigned char **p, *end;
+ register_info_type *reg_info;
+{
+ int mcnt;
+ boolean ret;
+ int reg_no;
+ unsigned char *p1 = *p;
+
+ switch ((re_opcode_t) *p1++)
+ {
+ case no_op:
+ case begline:
+ case endline:
+ case begbuf:
+ case endbuf:
+ case wordbeg:
+ case wordend:
+ case wordbound:
+ case notwordbound:
+#ifdef emacs
+ case before_dot:
+ case at_dot:
+ case after_dot:
+#endif
+ break;
+
+ case start_memory:
+ reg_no = *p1;
+ ret = group_match_null_string_p (&p1, end, reg_info);
+
+ /* Have to set this here in case we're checking a group which
+ contains a group and a back reference to it. */
+
+ if (REG_MATCH_NULL_STRING_P (reg_info[reg_no])
+ == MATCH_NOTHING_UNSET_VALUE)
+ REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret;
+
+ if (!ret)
+ return false;
+ break;
+
+ /* If this is an optimized succeed_n for zero times, make the jump. */
+ case no_pop_jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ if (mcnt >= 0)
+ p1 += mcnt;
+ else
+ return false;
+ break;
+
+ case succeed_n:
+ /* Get to the number of times to succeed. */
+ p1 += 2;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+
+ if (mcnt == 0)
+ {
+ p1 -= 4;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ p1 += mcnt;
+ }
+ else
+ return false;
+ break;
+
+ case duplicate:
+ if (!REG_MATCH_NULL_STRING_P (reg_info[*p1]))
+ return false;
+ break;
+
+ case set_number_at:
+ p1 += 4;
+
+ default:
+ /* All other opcodes mean we cannot match the empty string. */
+ return false;
+ }
+
+ *p = p1;
+ return true;
+} /* common_op_match_null_string_p */
+
+
+/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN
+ bytes; nonzero otherwise. */
+
+static int
+bcmp_translate (s1, s2, len, translate)
+ unsigned char *s1, *s2;
+ register int len;
+ char *translate;
+{
+ register unsigned char *p1 = s1, *p2 = s2;
+ while (len)
+ {
+ if (translate[*p1++] != translate[*p2++]) return 1;
+ len--;
+ }
+ return 0;
+}
+
+/* Entry points for GNU code. */
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+ compiles PATTERN (of length SIZE) and puts the result in BUFP.
+ Returns 0 if the pattern was valid, otherwise an error string.
+
+ Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+ are set in BUFP on entry.
+
+ We call regex_compile to do the actual compilation. */
+
+const char *
+re_compile_pattern (pattern, length, bufp)
+ const char *pattern;
+ int length;
+ struct re_pattern_buffer *bufp;
+{
+ reg_errcode_t ret;
+
+ /* GNU code is written to assume RE_NREGS registers will be set
+ (and extraneous ones will be filled with -1). */
+ bufp->caller_allocated_regs = 0;
+
+ /* And GNU code determines whether or not to get register information
+ by passing null for the REGS argument to re_match, etc., not by
+ setting no_sub. */
+ bufp->no_sub = 0;
+
+ /* Match anchors at newline. */
+ bufp->newline_anchor = 1;
+
+ ret = regex_compile (pattern, length, obscure_syntax, bufp);
+
+ return re_error_msg[(int) ret];
+}
+
+/* Entry points compatible with 4.2 BSD regex library. We don't define
+ them if this is an Emacs compilation. */
+
+#if !defined (emacs)
+
+static struct re_pattern_buffer re_comp_buf;
+
+const char *
+re_comp (s)
+ const char *s;
+{
+ reg_errcode_t ret;
+
+ if (!s)
+ {
+ if (!re_comp_buf.buffer)
+ return "No previous regular expression";
+ return 0;
+ }
+
+ if (!re_comp_buf.buffer)
+ {
+ re_comp_buf.buffer = (unsigned char *) malloc (200);
+ if (re_comp_buf.buffer == NULL)
+ return "Memory exhausted";
+ re_comp_buf.allocated = 200;
+
+ re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH);
+ if (re_comp_buf.fastmap == NULL)
+ return "Memory exhausted";
+ }
+
+ /* Match anchors at newlines. */
+ re_comp_buf.newline_anchor = 1;
+
+ ret = regex_compile (s, strlen (s), obscure_syntax, &re_comp_buf);
+
+ return re_error_msg[(int) ret];
+}
+
+
+int
+re_exec (s)
+ const char *s;
+{
+ const int len = strlen (s);
+ return 0 <= re_search (&re_comp_buf, s, len, 0, len,
+ (struct re_registers *) 0);
+}
+#endif /* not emacs */
+
+/* Entry points compatible with POSIX regex library. Don't define these
+ for Emacs. */
+
+#ifndef emacs
+
+/* regcomp takes a regular expression as a string and compiles it.
+
+ PREG is a regex_t *. We do not expect any fields to be initialized,
+ since POSIX says we shouldn't. Thus, we set
+
+ `buffer' to the compiled pattern;
+ `used' to the length of the compiled pattern;
+ `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+ REG_EXTENDED bit in CFLAGS is set; otherwise, to
+ RE_SYNTAX_POSIX_BASIC;
+ `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+ `fastmap' and `fastmap_accurate' to zero;
+ `re_nsub' to the number of subexpressions in PATTERN.
+
+ PATTERN is the address of the pattern string.
+
+ CFLAGS is a series of bits which affect compilation.
+
+ If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+ use POSIX basic syntax.
+
+ If REG_NEWLINE is set, then . and [^...] don't match newline.
+ Also, regexec will try a match beginning after every newline.
+
+ If REG_ICASE is set, then we considers upper- and lowercase
+ versions of letters to be equivalent when matching.
+
+ If REG_NOSUB is set, then when PREG is passed to regexec, that
+ routine will report only success or failure, and nothing about the
+ registers.
+
+ It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for
+ the return codes and their meanings.) */
+
+int
+regcomp (preg, pattern, cflags)
+ regex_t *preg;
+ const char *pattern;
+ int cflags;
+{
+ reg_errcode_t ret;
+ unsigned syntax
+ = cflags & REG_EXTENDED ? RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC;
+
+ /* regex_compile will allocate the space for the compiled pattern. */
+ preg->buffer = 0;
+
+ /* Don't bother to use a fastmap when searching. This simplifies the
+ REG_NEWLINE case: if we used a fastmap, we'd have to put all the
+ characters after newlines into the fastmap. This way, we just try
+ every character. */
+ preg->fastmap = 0;
+
+ if (cflags & REG_ICASE)
+ {
+ unsigned i;
+
+ preg->translate = (char *) malloc (CHAR_SET_SIZE);
+ if (preg->translate == NULL)
+ return (int) REG_ESPACE;
+
+ /* Map uppercase characters to corresponding lowercase ones. */
+ for (i = 0; i < CHAR_SET_SIZE; i++)
+ preg->translate[i] = isupper (i) ? tolower (i) : i;
+ }
+ else
+ preg->translate = NULL;
+
+ /* If REG_NEWLINE is set, newlines are treated differently. */
+ if (cflags & REG_NEWLINE)
+ { /* REG_NEWLINE implies neither . nor [^...] match newline. */
+ syntax &= ~RE_DOT_NEWLINE;
+ syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+ /* It also changes the matching behavior. */
+ preg->newline_anchor = 1;
+ }
+ else
+ preg->newline_anchor = 0;
+
+ preg->no_sub = !!(cflags & REG_NOSUB);
+
+ /* POSIX says a null character in the pattern terminates it, so we
+ can use strlen here in compiling the pattern. */
+ ret = regex_compile (pattern, strlen (pattern), syntax, preg);
+
+ /* POSIX doesn't distinguish between an unmatched open-group and an
+ unmatched close-group: both are REG_EPAREN. */
+ if (ret == REG_ERPAREN) ret = REG_EPAREN;
+
+ return (int) ret;
+}
+
+
+/* regexec searches for a given pattern, specified by PREG, in the
+ string STRING.
+
+ If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+ `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at
+ least NMATCH elements, and we set them to the offsets of the
+ corresponding matched substrings.
+
+ EFLAGS specifies `execution flags' which affect matching: if
+ REG_NOTBOL is set, then ^ does not match at the beginning of the
+ string; if REG_NOTEOL is set, then $ does not match at the end.
+
+ We return 0 if we find a match and REG_NOMATCH if not. */
+
+int
+regexec (preg, string, nmatch, pmatch, eflags)
+ const regex_t *preg;
+ const char *string;
+ size_t nmatch;
+ regmatch_t pmatch[];
+ int eflags;
+{
+ int ret;
+ struct re_registers regs;
+ regex_t private_preg;
+ int len = strlen (string);
+ boolean want_reg_info = !preg->no_sub && nmatch > 0;
+
+ private_preg = *preg;
+
+ private_preg.not_bol = !!(eflags & REG_NOTBOL);
+ private_preg.not_eol = !!(eflags & REG_NOTEOL);
+
+ /* The user has told us how many registers to return information
+ about, via `nmatch'. We have to pass that on to the matching
+ routines. */
+ private_preg.caller_allocated_regs = 1;
+
+ if (want_reg_info)
+ {
+ regs.num_regs = nmatch;
+ regs.start = TALLOC (nmatch, regoff_t);
+ regs.end = TALLOC (nmatch, regoff_t);
+ if (regs.start == NULL || regs.end == NULL)
+ return (int) REG_NOMATCH;
+ }
+
+ /* Perform the searching operation. */
+ ret = re_search (&private_preg, string, len,
+ /* start: */ 0, /* range: */ len,
+ want_reg_info ? &regs : NULL);
+
+ /* Copy the register information to the POSIX structure. */
+ if (want_reg_info)
+ {
+ if (ret >= 0)
+ {
+ unsigned r;
+
+ for (r = 0; r < nmatch; r++)
+ {
+ pmatch[r].rm_so = regs.start[r];
+ pmatch[r].rm_eo = regs.end[r];
+ }
+ }
+
+ /* If we needed the temporary register info, free the space now. */
+ free (regs.start);
+ free (regs.end);
+ }
+
+ /* We want zero return to mean success, unlike `re_search'. */
+ return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH;
+}
+
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+ from either regcomp or regexec. */
+
+size_t
+regerror (errcode, preg, errbuf, errbuf_size)
+ int errcode;
+ const regex_t *preg;
+ char *errbuf;
+ size_t errbuf_size;
+{
+ const char *msg
+ = re_error_msg[errcode] == NULL ? "Success" : re_error_msg[errcode];
+ size_t msg_size = strlen (msg) + 1; /* Includes the null. */
+
+ if (errbuf_size != 0)
+ {
+ if (msg_size > errbuf_size)
+ {
+ strncpy (errbuf, msg, errbuf_size - 1);
+ errbuf[errbuf_size - 1] = 0;
+ }
+ else
+ strcpy (errbuf, msg);
+ }
+
+ return msg_size;
+}
+
+
+/* Free dynamically allocated space used by PREG. */
+
+void
+regfree (preg)
+ regex_t *preg;
+{
+ if (preg->buffer != NULL)
+ free (preg->buffer);
+ preg->buffer = NULL;
+
+ preg->allocated = 0;
+ preg->used = 0;
+
+ if (preg->fastmap != NULL)
+ free (preg->fastmap);
+ preg->fastmap = NULL;
+ preg->fastmap_accurate = 0;
+
+ if (preg->translate != NULL)
+ free (preg->translate);
+ preg->translate = NULL;
+}
+
+#endif /* not emacs */
+
+#ifdef test
+
+#include <stdio.h>
+
+/* Indexed by a character, gives the upper case equivalent of the
+ character. */
+
+char upcase[0400] =
+ { 000, 001, 002, 003, 004, 005, 006, 007,
+ 010, 011, 012, 013, 014, 015, 016, 017,
+ 020, 021, 022, 023, 024, 025, 026, 027,
+ 030, 031, 032, 033, 034, 035, 036, 037,
+ 040, 041, 042, 043, 044, 045, 046, 047,
+ 050, 051, 052, 053, 054, 055, 056, 057,
+ 060, 061, 062, 063, 064, 065, 066, 067,
+ 070, 071, 072, 073, 074, 075, 076, 077,
+ 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
+ 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
+ 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
+ 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137,
+ 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
+ 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
+ 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
+ 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177,
+ 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
+ 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
+ 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
+ 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
+ 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
+ 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
+ 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
+ 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
+ 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
+ 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
+ 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
+ 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
+ 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
+ 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
+ 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
+ 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
+ };
+
+
+/* Use this to run interactive tests. */
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ char pat[500];
+ struct re_pattern_buffer buf;
+ int i;
+ char c;
+ char fastmap[(1 << BYTEWIDTH)];
+
+ /* Allow a command argument to specify the style of syntax. */
+ if (argc > 1)
+ re_set_syntax (atoi (argv[1]));
+
+ buf.allocated = 40;
+ buf.buffer = (unsigned char *) malloc (buf.allocated);
+ buf.fastmap = fastmap;
+ buf.translate = upcase;
+
+ for (;;)
+ {
+ printf ("Pattern = ");
+ gets (pat);
+
+ if (*pat)
+ {
+ void printchar ();
+ re_compile_pattern (pat, strlen (pat), &buf);
+
+ for (i = 0; i < buf.used; i++)
+ printchar (buf.buffer[i]);
+
+ putchar ('\n');
+
+ printf ("%d allocated, %d used.\n", buf.allocated, buf.used);
+
+ re_compile_fastmap (&buf);
+ printf ("Allowed by fastmap: ");
+ for (i = 0; i < (1 << BYTEWIDTH); i++)
+ if (fastmap[i]) printchar (i);
+ putchar ('\n');
+ }
+
+ printf ("String = ");
+ gets (pat); /* Now read the string to match against */
+
+ i = re_match (&buf, pat, strlen (pat), 0, 0);
+ printf ("Match value %d.\n\n", i);
+ }
+}
+
+
+#if 0
+/* We have a fancier version now, compiled_pattern_printer. */
+print_buf (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ int i;
+
+ printf ("buf is :\n----------------\n");
+ for (i = 0; i < bufp->used; i++)
+ printchar (bufp->buffer[i]);
+
+ printf ("\n%d allocated, %d used.\n", bufp->allocated, bufp->used);
+
+ printf ("Allowed by fastmap: ");
+ for (i = 0; i < (1 << BYTEWIDTH); i++)
+ if (bufp->fastmap[i])
+ printchar (i);
+ printf ("\nAllowed by translate: ");
+ if (bufp->translate)
+ for (i = 0; i < (1 << BYTEWIDTH); i++)
+ if (bufp->translate[i])
+ printchar (i);
+ printf ("\nfastmap is%s accurate\n", bufp->fastmap_accurate ? "" : "n't");
+ printf ("can %s be null\n----------", bufp->can_be_null ? "" : "not");
+}
+#endif /* 0 */
+
+
+void
+printchar (c)
+ char c;
+{
+ if (c < 040 || c >= 0177)
+ {
+ putchar ('\\');
+ putchar (((c >> 6) & 3) + '0');
+ putchar (((c >> 3) & 7) + '0');
+ putchar ((c & 7) + '0');
+ }
+ else
+ putchar (c);
+}
+#endif /* test */
+
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
diff --git a/gnu/usr.bin/cvs/lib/regex.h b/gnu/usr.bin/cvs/lib/regex.h
new file mode 100644
index 0000000..211ad09
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/regex.h
@@ -0,0 +1,479 @@
+/* Definitions for data structures and routines for the regular
+ expression library, version REPLACE-WITH-VERSION.
+
+ Copyright (C) 1985, 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __REGEXP_LIBRARY_H__
+#define __REGEXP_LIBRARY_H__
+
+/* POSIX says that <sys/types.h> must be included before <regex.h>. */
+
+/* The following bits are used to determine the regexp syntax we
+ recognize. The set/not-set meanings are chosen so that Emacs syntax
+ remains the value 0. The bits are given in alphabetical order, and
+ the definitions shifted by one from the previous bit; thus, when we
+ add or remove a bit, only one other definition need change. */
+typedef unsigned reg_syntax_t;
+
+/* If this bit is not set, then \ inside a bracket expression is literal.
+ If set, then such a \ quotes the following character. */
+#define RE_BACKSLASH_ESCAPE_IN_LISTS (1)
+
+/* If this bit is not set, then + and ? are operators, and \+ and \? are
+ literals.
+ If set, then \+ and \? are operators and + and ? are literals. */
+#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
+
+/* If this bit is set, then character classes are supported. They are:
+ [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
+ [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+ If not set, then character classes are not supported. */
+#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
+
+/* If this bit is set, then ^ and $ are always anchors (outside bracket
+ expressions).
+ If this bit is not set, then it depends:
+ ^ is an anchor if it is at the beginning of a regular
+ expression or after an open-group or an alternation operator;
+ $ is an anchor if it is at the end of a regular expression, or
+ before a close-group or an alternation operator.
+ This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
+ POSIX now says that the behavior of * etc. in leading positions is
+ undefined. We have already implemented a previous draft which
+ made those constructs invalid, so we may as well not change the code
+ back. */
+#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
+
+/* If this bit is set, then special characters are always special
+ regardless of where they are in the pattern.
+ If this bit is not set, then special characters are special only in
+ some contexts; otherwise they are ordinary. Specifically,
+ * + ? and intervals are only special when not after the beginning,
+ open-group, or alternation operator. */
+#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
+
+/* If this bit is set, then *, +, ?, and { cannot be first in an re or
+ immediately after an alternation or begin-group operator.
+ Furthermore, alternation cannot be first or last in an re, or
+ immediately follow another alternation or begin-group. */
+#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
+
+/* If this bit is set, then . matches a newline.
+ If not set, then it doesn't. */
+#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
+
+/* If this bit is set, then period doesn't match a null.
+ If not set, then it does. */
+#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
+
+/* If this bit is set, nonmatching lists [^...] do not match newline.
+ If not set, they do. */
+#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
+
+/* If this bit is set, either \{...\} or {...} defines an
+ interval, depending on RE_NO_BK_BRACES.
+ If not set, \{, \}, {, and } are literals. */
+#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
+
+/* If this bit is set, +, ? and | aren't recognized as operators.
+ If not set, they are. */
+#define RE_LIMITED_OPS (RE_INTERVALS << 1)
+
+/* If this bit is set, newline is an alternation operator.
+ If not set, newline is literal. */
+#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
+
+/* If this bit is set, newline in the pattern is an ordinary character.
+ If not set, newline before ^ or after $ allows the ^ or $ to be an
+ anchor. */
+#define RE_NEWLINE_ORDINARY (RE_NEWLINE_ALT << 1)
+
+/* If this bit is not set, then \{ and \} defines an interval,
+ and { and } are literals.
+ If set, then { and } defines an interval, and \{ and \} are literals. */
+#define RE_NO_BK_BRACES (RE_NEWLINE_ORDINARY << 1)
+
+/* If this bit is set, (...) defines a group, and \( and \) are literals.
+ If not set, \(...\) defines a group, and ( and ) are literals. */
+#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
+
+/* If this bit is set, then back references (i.e., \<digit>) are not
+ recognized.
+ If not set, then they are. */
+#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
+
+/* If this bit is set, then | is an alternation operator, and \| is literal.
+ If not set, then \| is an alternation operator, and | is literal. */
+#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
+
+/* If this bit is set, then you can't have empty alternatives.
+ If not set, then you can. */
+#define RE_NO_EMPTY_ALTS (RE_NO_BK_VBAR << 1)
+
+/* If this bit is set, then you can't have empty groups.
+ If not set, then you can. */
+#define RE_NO_EMPTY_GROUPS (RE_NO_EMPTY_ALTS << 1)
+
+/* If this bit is set, then an ending range point has to collate higher
+ than or equal to the starting range point.
+ If not set, then when the ending range point collates higher than the
+ starting range point, we consider such a range to be empty. */
+#define RE_NO_EMPTY_RANGES (RE_NO_EMPTY_GROUPS << 1)
+
+/* If this bit is set, then all back references must refer to a preceding
+ subexpression.
+ If not set, then a back reference to a nonexistent subexpression is
+ treated as literal characters. */
+#define RE_NO_MISSING_BK_REF (RE_NO_EMPTY_RANGES << 1)
+
+/* If this bit is set, then Regex considers an unmatched close-group
+ operator to be the ordinary character parenthesis.
+ If not set, then an unmatched close-group operator is invalid. */
+#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_MISSING_BK_REF << 1)
+
+/* This global variable defines the particular regexp syntax to use (for
+ some interfaces). When a regexp is compiled, the syntax used is
+ stored in the pattern buffer, so changing this does not affect
+ already-compiled regexps. */
+extern reg_syntax_t obscure_syntax;
+
+
+
+/* Define combinations of the above bits for the standard possibilities.
+ (The [[[ comments delimit what gets put into the Texinfo file.) */
+/* [[[begin syntaxes]]] */
+#define RE_SYNTAX_EMACS 0
+
+#define RE_SYNTAX_POSIX_AWK \
+ (RE_CONTEXT_INDEP_ANCHORS | RE_CONTEXT_INDEP_OPS | RE_NO_BK_PARENS \
+ | RE_NO_BK_VBAR)
+
+#define RE_SYNTAX_AWK \
+ (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_SYNTAX_POSIX_AWK)
+
+#define RE_SYNTAX_GREP \
+ (RE_BK_PLUS_QM | RE_NEWLINE_ALT)
+
+#define RE_SYNTAX_EGREP \
+ (RE_CONTEXT_INDEP_ANCHORS | RE_CONTEXT_INDEP_OPS \
+ | RE_NEWLINE_ALT | RE_NO_BK_PARENS | RE_NO_BK_VBAR)
+
+#define RE_SYNTAX_POSIX_BASIC \
+ (RE_CHAR_CLASSES | RE_DOT_NEWLINE \
+ | RE_DOT_NOT_NULL | RE_INTERVALS | RE_LIMITED_OPS \
+ | RE_NEWLINE_ORDINARY | RE_NO_EMPTY_RANGES | RE_NO_MISSING_BK_REF)
+
+#define RE_SYNTAX_POSIX_EXTENDED \
+ (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INVALID_OPS | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \
+ | RE_INTERVALS | RE_NEWLINE_ORDINARY | RE_NO_BK_BRACES \
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS | RE_NO_BK_VBAR \
+ | RE_NO_EMPTY_ALTS | RE_NO_EMPTY_GROUPS | RE_NO_EMPTY_RANGES \
+ | RE_UNMATCHED_RIGHT_PAREN_ORD)
+/* [[[end syntaxes]]] */
+
+
+
+
+/* Maximum number of duplicates an interval can allow. */
+#undef RE_DUP_MAX
+#define RE_DUP_MAX ((1 << 15) - 1)
+
+
+/* POSIX `cflags' bits (i.e., information for regcomp). */
+
+/* If this bit is set, then use extended regular expression syntax.
+ If not set, then use basic regular expression syntax. */
+#define REG_EXTENDED 1
+
+/* If this bit is set, then ignore case when matching.
+ If not set, then case is significant. */
+#define REG_ICASE (REG_EXTENDED << 1)
+
+/* If this bit is set, then anchors do not match at newline
+ characters in the string.
+ If not set, then anchors do match at newlines. */
+#define REG_NEWLINE (REG_ICASE << 1)
+
+/* If this bit is set, then report only success or fail in regexec.
+ If not set, then returns differ between not matching and errors. */
+#define REG_NOSUB (REG_NEWLINE << 1)
+
+
+/* POSIX `eflags' bits (i.e., information for regexec). */
+
+/* If this bit is set, then the beginning-of-line operator doesn't match
+ the beginning of the string (presumably because it's not the
+ beginning of a line).
+ If not set, then the beginning-of-line operator does match the
+ beginning of the string. */
+#define REG_NOTBOL 1
+
+/* Like REG_NOTBOL, except for the end-of-line. */
+#define REG_NOTEOL (1 << 1)
+
+
+/* If any error codes are removed, changed, or added, update the
+ `re_error_msg' table in regex.c. */
+typedef enum
+{
+ REG_NOERROR = 0, /* Success. */
+ REG_NOMATCH, /* Didn't find a match (for regexec). */
+
+ /* POSIX regcomp return error codes. (In the order listed in the
+ standard.) */
+ REG_BADPAT, /* Invalid pattern. */
+ REG_ECOLLATE, /* Not implemented. */
+ REG_ECTYPE, /* Invalid character class name. */
+ REG_EESCAPE, /* Trailing backslash. */
+ REG_ESUBREG, /* Invalid back reference. */
+ REG_EBRACK, /* Unmatched left bracket. */
+ REG_EPAREN, /* Parenthesis imbalance. */
+ REG_EBRACE, /* Unmatched \{. */
+ REG_BADBR, /* Invalid contents of \{\}. */
+ REG_ERANGE, /* Invalid range end. */
+ REG_ESPACE, /* Ran out of memory. */
+ REG_BADRPT, /* No preceding re for repetition op. */
+
+ /* Error codes we've added. */
+ REG_EEND, /* Premature end. */
+ REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */
+ REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */
+} reg_errcode_t;
+
+
+
+
+/* This data structure represents a compiled pattern. Before calling
+ the pattern compiler, the fields `buffer', `allocated', `fastmap',
+ `translate', and `no_sub' can be set. After the pattern has been
+ compiled, the `re_nsub' field is available. All other fields are
+ private to the regex routines. */
+
+struct re_pattern_buffer
+{
+/* [[[begin pattern_buffer]]] */
+ /* Space that holds the compiled pattern. It is declared as
+ `unsigned char *' because its elements are
+ sometimes used as array indexes. */
+ unsigned char *buffer;
+
+ /* Number of bytes to which `buffer' points. */
+ unsigned long allocated;
+
+ /* Number of bytes actually used in `buffer'. */
+ unsigned long used;
+
+ /* Syntax setting with which the pattern was compiled. */
+ reg_syntax_t syntax;
+
+ /* Pointer to a fastmap, if any, otherwise zero. re_search uses
+ the fastmap, if there is one, to skip over impossible
+ starting points for matches. */
+ char *fastmap;
+
+ /* Either a translate table to apply to all characters before
+ comparing them, or zero for no translation. The translation
+ is applied to a pattern when it is compiled and to a string
+ when it is matched. */
+ char *translate;
+
+ /* Number of subexpressions found by the compiler. */
+ size_t re_nsub;
+
+ /* Set to 1 by re_compile_fastmap if this pattern can match the
+ null string; 0 prevents the searcher from matching it with
+ the null string. Set to 2 if it might match the null string
+ either at the end of a search range or just before a
+ character listed in the fastmap. */
+ unsigned can_be_null : 2;
+
+ /* Set to zero when regex_compile compiles a pattern; set to one
+ by re_compile_fastmap when it updates the fastmap, if any. */
+ unsigned fastmap_accurate : 1;
+
+ /* If set, regexec reports only success or failure and does not
+ return anything in pmatch. */
+ unsigned no_sub : 1;
+
+ /* If set, a beginning-of-line anchor doesn't match at the
+ beginning of the string. */
+ unsigned not_bol : 1;
+
+ /* Similarly for an end-of-line anchor. */
+ unsigned not_eol : 1;
+
+ /* If true, an anchor at a newline matches. */
+ unsigned newline_anchor : 1;
+
+ /* If set, re_match_2 assumes a non-null REGS argument is
+ initialized. If not set, REGS is initialized to the max of
+ RE_NREGS and re_nsub + 1 registers. */
+ unsigned caller_allocated_regs : 1;
+/* [[[end pattern_buffer]]] */
+};
+
+typedef struct re_pattern_buffer regex_t;
+
+
+/* search.c (search_buffer) in Emacs needs this one opcode value. It is
+ defined both in `regex.c' and here. */
+#define RE_EXACTN_VALUE 1
+
+
+
+
+/* Type for byte offsets within the string. POSIX mandates us defining
+ this. */
+typedef int regoff_t;
+
+
+/* This is the structure we store register match data in. See
+ regex.texinfo for a full description of what registers match. */
+struct re_registers
+{
+ unsigned num_regs;
+ regoff_t *start;
+ regoff_t *end;
+};
+
+
+/* If `caller_allocated_regs' is zero in the pattern buffer, re_match_2
+ returns information about this many registers. */
+#ifndef RE_NREGS
+#define RE_NREGS 30
+#endif
+
+
+/* POSIX specification for registers. Aside from the different names than
+ `re_registers', POSIX uses an array of structures, instead of a
+ structure of arrays. */
+typedef struct
+{
+ regoff_t rm_so; /* Byte offset from string's start to substring's start. */
+ regoff_t rm_eo; /* Byte offset from string's start to substring's end. */
+} regmatch_t;
+
+
+
+
+/* Declarations for routines. */
+
+#if __STDC__
+
+/* Sets the current syntax to SYNTAX. You can also simply assign to the
+ `obscure_syntax' variable. */
+extern reg_syntax_t re_set_syntax (reg_syntax_t syntax);
+
+/* Compile the regular expression PATTERN, with length LENGTH
+ and syntax given by the global `obscure_syntax', into the buffer
+ BUFFER. Return NULL if successful, and an error string if not. */
+extern const char *re_compile_pattern (const char *pattern, int length,
+ struct re_pattern_buffer *buffer);
+
+
+/* Compile a fastmap for the compiled pattern in BUFFER; used to
+ accelerate searches. Return 0 if successful and -2 if was an
+ internal error. */
+extern int re_compile_fastmap (struct re_pattern_buffer *buffer);
+
+
+/* Search in the string STRING (with length LENGTH) for the pattern
+ compiled into BUFFER. Start searching at position START, for RANGE
+ characters. Return the starting position of the match, -1 for no
+ match, or -2 for an internal error. Also return register
+ information in REGS (if REGS and BUFFER->no_sub are nonzero). */
+extern int re_search (struct re_pattern_buffer *buffer,
+ const char *string, int length,
+ int start, int range,
+ struct re_registers *regs);
+
+
+/* Like `re_search', but search in the concatenation of STRING1 and
+ STRING2. Also, stop searching at index START + STOP. */
+extern int re_search_2 (struct re_pattern_buffer *buffer,
+ const char *string1, int length1,
+ const char *string2, int length2,
+ int start, int range,
+ struct re_registers *regs,
+ int stop);
+
+
+/* Like `re_search', but return how many characters in STRING the regexp
+ in BUFFER matched, starting at position START. */
+extern int re_match (const struct re_pattern_buffer *buffer,
+ const char *string, int length,
+ int start, struct re_registers *regs);
+
+
+/* Relates to `re_match' as `re_search_2' relates to `re_search'. */
+extern int re_match_2 (const struct re_pattern_buffer *buffer,
+ const char *string1, int length1,
+ const char *string2, int length2,
+ int start,
+ struct re_registers *regs,
+ int stop);
+
+
+#ifndef __386BSD__
+/* 4.2 bsd compatibility. */
+#ifndef bsdi
+extern const char *re_comp (const char *);
+#endif
+extern int re_exec (const char *);
+#endif
+
+/* POSIX compatibility. */
+extern int regcomp (regex_t *preg, const char *pattern, int cflags);
+extern int regexec (const regex_t *preg, const char *string, size_t nmatch,
+ regmatch_t pmatch[], int eflags);
+extern size_t regerror (int errcode, const regex_t *preg, char *errbuf,
+ size_t errbuf_size);
+extern void regfree (regex_t *preg);
+
+#else /* not __STDC__ */
+
+/* Support old C compilers. */
+#define const
+
+extern reg_syntax_t re_set_syntax ();
+extern char *re_compile_pattern ();
+extern int re_search (), re_search_2 ();
+extern int re_match (), re_match_2 ();
+
+/* 4.2 BSD compatibility. */
+extern char *re_comp ();
+extern int re_exec ();
+
+/* POSIX compatibility. */
+extern int regcomp ();
+extern int regexec ();
+extern size_t regerror ();
+extern void regfree ();
+
+#endif /* not __STDC__ */
+#endif /* not __REGEXP_LIBRARY_H__ */
+
+
+
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
diff --git a/gnu/usr.bin/cvs/lib/rename.c b/gnu/usr.bin/cvs/lib/rename.c
new file mode 100644
index 0000000..3e0b481
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/rename.c
@@ -0,0 +1,68 @@
+/* rename.c -- BSD compatible directory function for System V
+ Copyright (C) 1988, 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+
+/* Rename file FROM to file TO.
+ Return 0 if successful, -1 if not. */
+
+int
+rename (from, to)
+ char *from;
+ char *to;
+{
+ struct stat from_stats;
+ int pid, status;
+
+ if (stat (from, &from_stats) == 0)
+ {
+ if (unlink (to) && errno != ENOENT)
+ return -1;
+ if ((from_stats.st_mode & S_IFMT) == S_IFDIR)
+ {
+ /* Need a setuid root process to link and unlink directories. */
+ pid = fork ();
+ switch (pid)
+ {
+ case -1: /* Error. */
+ error (1, errno, "cannot fork");
+
+ case 0: /* Child. */
+ execl (MVDIR, "mvdir", from, to, (char *) 0);
+ error (255, errno, "cannot run `%s'", MVDIR);
+
+ default: /* Parent. */
+ while (wait (&status) != pid)
+ /* Do nothing. */ ;
+
+ errno = 0; /* mvdir printed the system error message. */
+ return status != 0 ? -1 : 0;
+ }
+ }
+ else
+ {
+ if (link (from, to) == 0 && (unlink (from) == 0 || errno == ENOENT))
+ return 0;
+ }
+ }
+ return -1;
+}
diff --git a/gnu/usr.bin/cvs/lib/sighandle.c b/gnu/usr.bin/cvs/lib/sighandle.c
new file mode 100644
index 0000000..1b73b93
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/sighandle.c
@@ -0,0 +1,412 @@
+/* sighandle.c -- Library routines for manipulating chains of signal handlers
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Paul Sander, HaL Computer Systems, Inc. <paul@hal.com>
+ Brian Berliner <berliner@Sun.COM> added POSIX support */
+
+/*************************************************************************
+ *
+ * signal.c -- This file contains code that manipulates chains of signal
+ * handlers.
+ *
+ * Facilities are provided to register a signal handler for
+ * any specific signal. When a signal is received, all of the
+ * registered signal handlers are invoked in the reverse order
+ * in which they are registered. Note that the signal handlers
+ * must not themselves make calls to the signal handling
+ * facilities.
+ *
+ * @(#)sighandle.c 1.9 92/03/31
+ *
+ *************************************************************************/
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <signal.h>
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+#if __STDC__
+char *calloc(unsigned nelem, unsigned size);
+char *malloc(unsigned size);
+#else
+char *calloc();
+char *malloc();
+#endif /* __STDC__ */
+#endif /* STDC_HEADERS */
+
+#ifdef _MINIX
+#undef POSIX /* Minix 1.6 doesn't support POSIX.1 sigaction yet */
+#endif
+
+#ifndef SIGTYPE
+#define SIGTYPE void
+#endif
+
+/* Define the highest signal number (usually) */
+#ifndef SIGMAX
+#define SIGMAX 32
+#endif
+
+/* Define linked list of signal handlers structure */
+struct SIG_hlist {
+ SIGTYPE (*handler)();
+ struct SIG_hlist *next;
+};
+
+/*
+ * Define array of lists of signal handlers. Note that this depends on
+ * the implementation to initialize each element to a null pointer.
+ */
+
+static struct SIG_hlist **SIG_handlers;
+
+/* Define array of default signal vectors */
+
+#ifdef POSIX
+static struct sigaction *SIG_defaults;
+#else
+#ifdef BSD_SIGNALS
+static struct sigvec *SIG_defaults;
+#else
+static SIGTYPE (**SIG_defaults)();
+#endif
+#endif
+
+/* Critical section housekeeping */
+static int SIG_crSectNest = 0; /* Nesting level */
+#ifdef POSIX
+static sigset_t SIG_crSectMask; /* Signal mask */
+#else
+static int SIG_crSectMask; /* Signal mask */
+#endif
+
+/*
+ * Initialize the signal handler arrays
+ */
+
+static int SIG_init()
+{
+ int i;
+#ifdef POSIX
+ sigset_t sigset_test;
+#endif
+
+ if (SIG_defaults && SIG_handlers) /* already allocated */
+ return (0);
+
+#ifdef POSIX
+ (void) sigfillset(&sigset_test);
+ for (i = 1; sigismember(&sigset_test, i) == 1; i++)
+#ifdef BROKEN_SIGISMEMBER
+ if ( i >= NSIG )
+ break
+#endif
+ ;
+ if (i < SIGMAX)
+ i = SIGMAX;
+ i++;
+ if (!SIG_defaults)
+ SIG_defaults = (struct sigaction *)
+ calloc(i, sizeof(struct sigaction));
+ (void) sigemptyset(&SIG_crSectMask);
+#else
+ i = SIGMAX+1;
+#ifdef BSD_SIGNALS
+ if (!SIG_defaults)
+ SIG_defaults = (struct sigvec *)
+ calloc(i, sizeof(struct sigvec));
+#else
+ if (!SIG_defaults)
+ SIG_defaults = (SIGTYPE (**)())
+ calloc(i, sizeof(SIGTYPE (**)()));
+#endif
+ SIG_crSectMask = 0;
+#endif
+ if (!SIG_handlers)
+ SIG_handlers = (struct SIG_hlist **)
+ calloc(i, sizeof(struct SIG_hlist *));
+ return (!SIG_defaults || !SIG_handlers);
+}
+
+/*
+ * The following invokes each signal handler in the reverse order in which
+ * they were registered.
+ */
+
+static SIGTYPE SIG_handle(sig)
+int sig;
+{
+ struct SIG_hlist *this;
+
+ /* Dispatch signal handlers */
+ this = SIG_handlers[sig];
+ while (this != (struct SIG_hlist *) NULL)
+ {
+ (*this->handler)(sig);
+ this = this->next;
+ }
+
+ return;
+}
+
+/*
+ * The following registers a signal handler. If the handler is already
+ * registered, it is not registered twice, nor is the order in which signal
+ * handlers are invoked changed. If this is the first signal handler
+ * registered for a given signal, the old sigvec structure is saved for
+ * restoration later.
+ */
+
+int SIG_register(sig,fn)
+int sig;
+SIGTYPE (*fn)();
+{
+ int val;
+ struct SIG_hlist *this;
+#ifdef POSIX
+ struct sigaction act;
+ sigset_t sigset_mask, sigset_omask;
+#else
+#ifdef BSD_SIGNALS
+ struct sigvec vec;
+ int mask;
+#endif
+#endif
+
+ /* Initialize */
+ if (SIG_init() != 0)
+ return (-1);
+ val = 0;
+
+ /* Block this signal while we look at handler chain */
+#ifdef POSIX
+ (void) sigemptyset(&sigset_mask);
+ (void) sigaddset(&sigset_mask, sig);
+ (void) sigprocmask(SIG_BLOCK, &sigset_mask, &sigset_omask);
+#else
+#ifdef BSD_SIGNALS
+ mask = sigblock(sigmask(sig));
+#endif
+#endif
+
+ /* See if this handler was already registered */
+ this = SIG_handlers[sig];
+ while (this != (struct SIG_hlist *) NULL)
+ {
+ if (this->handler == fn) break;
+ this = this->next;
+ }
+
+ /* Register the new handler only if it is not already registered. */
+ if (this == (struct SIG_hlist *) NULL)
+ {
+
+ /*
+ * If this is the first handler registered for this signal,
+ * set up the signal handler dispatcher
+ */
+
+ if (SIG_handlers[sig] == (struct SIG_hlist *) NULL)
+ {
+#ifdef POSIX
+ act.sa_handler = SIG_handle;
+ (void) sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ val = sigaction(sig, &act, &SIG_defaults[sig]);
+#else
+#ifdef BSD_SIGNALS
+ bzero((char *)&vec, sizeof(vec));
+ vec.sv_handler = SIG_handle;
+ val = sigvec(sig, &vec, &SIG_defaults[sig]);
+#else
+ if ((SIG_defaults[sig] = signal(sig, SIG_handle)) ==
+ (SIGTYPE (*)()) -1)
+ val = -1;
+#endif
+#endif
+ }
+
+ /* If not, register it */
+ if ((val == 0) && (this == (struct SIG_hlist *) NULL))
+ {
+ this = (struct SIG_hlist *)
+ malloc(sizeof(struct SIG_hlist));
+ if (this == NULL)
+ {
+ val = -1;
+ }
+ else
+ {
+ this->handler = fn;
+ this->next = SIG_handlers[sig];
+ SIG_handlers[sig] = this;
+ }
+ }
+ }
+
+ /* Unblock the signal */
+#ifdef POSIX
+ (void) sigprocmask(SIG_SETMASK, &sigset_omask, NULL);
+#else
+#ifdef BSD_SIGNALS
+ (void) sigsetmask(mask);
+#endif
+#endif
+
+ return val;
+}
+
+/*
+ * The following deregisters a signal handler. If the last signal handler for
+ * a given signal is deregistered, the default sigvec information is restored.
+ */
+
+int SIG_deregister(sig,fn)
+int sig;
+SIGTYPE (*fn)();
+{
+ int val;
+ struct SIG_hlist *this;
+ struct SIG_hlist *last;
+#ifdef POSIX
+ sigset_t sigset_mask, sigset_omask;
+#else
+#ifdef BSD_SIGNALS
+ int mask;
+#endif
+#endif
+
+ /* Initialize */
+ if (SIG_init() != 0)
+ return (-1);
+ val = 0;
+ last = (struct SIG_hlist *) NULL;
+
+ /* Block this signal while we look at handler chain */
+#ifdef POSIX
+ (void) sigemptyset(&sigset_mask);
+ (void) sigaddset(&sigset_mask, sig);
+ (void) sigprocmask(SIG_BLOCK, &sigset_mask, &sigset_omask);
+#else
+#ifdef BSD_SIGNALS
+ mask = sigblock(sigmask(sig));
+#endif
+#endif
+
+ /* Search for the signal handler */
+ this = SIG_handlers[sig];
+ while ((this != (struct SIG_hlist *) NULL) && (this->handler != fn))
+ {
+ last = this;
+ this = this->next;
+ }
+
+ /* If it was registered, remove it */
+ if (this != (struct SIG_hlist *) NULL)
+ {
+ if (last == (struct SIG_hlist *) NULL)
+ {
+ SIG_handlers[sig] = this->next;
+ }
+ else
+ {
+ last->next = this->next;
+ }
+ free((char *) this);
+ }
+
+ /* Restore default behavior if there are no registered handlers */
+ if (SIG_handlers[sig] == (struct SIG_hlist *) NULL)
+ {
+#ifdef POSIX
+ val = sigaction(sig, &SIG_defaults[sig],
+ (struct sigaction *) NULL);
+#else
+#ifdef BSD_SIGNALS
+ val = sigvec(sig, &SIG_defaults[sig], (struct sigvec *) NULL);
+#else
+ if (signal(sig, SIG_defaults[sig]) == (SIGTYPE (*)()) -1)
+ val = -1;
+#endif
+#endif
+ }
+
+ /* Unblock the signal */
+#ifdef POSIX
+ (void) sigprocmask(SIG_SETMASK, &sigset_omask, NULL);
+#else
+#ifdef BSD_SIGNALS
+ (void) sigsetmask(mask);
+#endif
+#endif
+
+ return val;
+}
+
+/*
+ * The following begins a critical section.
+ */
+
+void SIG_beginCrSect()
+{
+ if (SIG_init() == 0)
+ {
+ if (SIG_crSectNest == 0)
+ {
+#ifdef POSIX
+ sigset_t sigset_mask;
+
+ (void) sigfillset(&sigset_mask);
+ (void) sigprocmask(SIG_SETMASK,
+ &sigset_mask, &SIG_crSectMask);
+#else
+#ifdef BSD_SIGNALS
+ SIG_crSectMask = sigblock(~0);
+#else
+ /* TBD */
+#endif
+#endif
+ }
+ SIG_crSectNest++;
+ }
+}
+
+/*
+ * The following ends a critical section.
+ */
+
+void SIG_endCrSect()
+{
+ if (SIG_init() == 0)
+ {
+ SIG_crSectNest--;
+ if (SIG_crSectNest == 0)
+ {
+#ifdef POSIX
+ (void) sigprocmask(SIG_SETMASK, &SIG_crSectMask, NULL);
+#else
+#ifdef BSD_SIGNALS
+ (void) sigsetmask(SIG_crSectMask);
+#else
+ /* TBD */
+#endif
+#endif
+ }
+ }
+}
diff --git a/gnu/usr.bin/cvs/lib/strdup.c b/gnu/usr.bin/cvs/lib/strdup.c
new file mode 100644
index 0000000..4e5af07
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/strdup.c
@@ -0,0 +1,39 @@
+/* strdup.c -- return a newly allocated copy of a string
+ Copyright (C) 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef STDC_HEADERS
+#include <string.h>
+#include <stdlib.h>
+#else
+char *malloc ();
+char *strcpy ();
+#endif
+
+/* Return a newly allocated copy of STR,
+ or 0 if out of memory. */
+
+char *
+strdup (str)
+ char *str;
+{
+ char *newstr;
+
+ newstr = (char *) malloc (strlen (str) + 1);
+ if (newstr)
+ strcpy (newstr, str);
+ return newstr;
+}
diff --git a/gnu/usr.bin/cvs/lib/strippath.c b/gnu/usr.bin/cvs/lib/strippath.c
new file mode 100644
index 0000000..3d606a8
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/strippath.c
@@ -0,0 +1,74 @@
+/* strippath.c -- remove unnecessary components from a path specifier
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if defined(STDC_HEADERS) || defined(USG)
+#include <string.h>
+#ifndef index
+#define index strchr
+#endif
+#else
+#include <strings.h>
+#endif
+
+#include <stdio.h>
+
+#if __STDC__
+static void remove_component(char *beginc, char *endc);
+void strip_trailing_slashes(char *path);
+#else
+static void remove_component();
+void strip_trailing_slashes();
+#endif /* __STDC__ */
+
+/* Remove unnecessary components from PATH. */
+
+void
+strip_path (path)
+ char *path;
+{
+ int stripped = 0;
+ char *cp, *slash;
+
+ for (cp = path; (slash = index(cp, '/')) != NULL; cp = slash)
+ {
+ *slash = '\0';
+ if ((!*cp && (cp != path || stripped)) ||
+ strcmp(cp, ".") == 0 || strcmp(cp, "/") == 0)
+ {
+ stripped = 1;
+ remove_component(cp, slash);
+ slash = cp;
+ }
+ else
+ {
+ *slash++ = '/';
+ }
+ }
+ strip_trailing_slashes(path);
+}
+
+/* Remove the component delimited by BEGINC and ENDC from the path */
+
+static void
+remove_component (beginc, endc)
+ char *beginc;
+ char *endc;
+{
+ for (endc++; *endc; endc++)
+ *beginc++ = *endc;
+ *beginc = '\0';
+}
diff --git a/gnu/usr.bin/cvs/lib/stripslash.c b/gnu/usr.bin/cvs/lib/stripslash.c
new file mode 100644
index 0000000..0c26ac6
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/stripslash.c
@@ -0,0 +1,35 @@
+/* stripslash.c -- remove trailing slashes from a string
+ Copyright (C) 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if defined(STDC_HEADERS) || defined(USG)
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+/* Remove trailing slashes from PATH. */
+
+void
+strip_trailing_slashes (path)
+ char *path;
+{
+ int last;
+
+ last = strlen (path) - 1;
+ while (last > 0 && path[last] == '/')
+ path[last--] = '\0';
+}
diff --git a/gnu/usr.bin/cvs/lib/subr.c b/gnu/usr.bin/cvs/lib/subr.c
new file mode 100644
index 0000000..2b728c2
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/subr.c
@@ -0,0 +1,912 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * Various useful functions for the CVS support code.
+ */
+
+#include "cvs.h"
+
+#ifdef _MINIX
+#undef POSIX /* Minix 1.6 doesn't support POSIX.1 sigaction yet */
+#endif
+
+#ifndef VPRINTF_MISSING
+#if __STDC__
+#include <stdarg.h>
+#define VA_START(args, lastarg) va_start(args, lastarg)
+#else
+#include <varargs.h>
+#define VA_START(args, lastarg) va_start(args)
+#endif
+#else
+#define va_alist a1, a2, a3, a4, a5, a6, a7, a8
+#define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
+#endif
+
+#ifndef lint
+static char rcsid[] = "@(#)subr.c 1.52 92/03/31";
+#endif
+
+#if __STDC__
+static void run_add_arg (char *s);
+static void run_init_prog (void);
+#else
+static void run_add_arg ();
+static void run_init_prog ();
+#endif /* __STDC__ */
+
+extern char *getlogin ();
+extern char *strtok ();
+
+/*
+ * Copies "from" to "to". mallocs a buffer large enough to hold the entire
+ * file and does one read/one write to do the copy. This is reasonable,
+ * since source files are typically not too large.
+ */
+void
+copy_file (from, to)
+ char *from;
+ char *to;
+{
+ struct stat sb;
+ struct utimbuf t;
+ int fdin, fdout;
+ char *buf;
+
+ if (trace)
+ (void) fprintf (stderr, "-> copy(%s,%s)\n", from, to);
+ if (noexec)
+ return;
+
+ if ((fdin = open (from, O_RDONLY)) < 0)
+ error (1, errno, "cannot open %s for copying", from);
+ if (fstat (fdin, &sb) < 0)
+ error (1, errno, "cannot fstat %s", from);
+ if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0)
+ error (1, errno, "cannot create %s for copying", to);
+ if (sb.st_size > 0)
+ {
+ buf = xmalloc ((int) sb.st_size);
+ if (read (fdin, buf, (int) sb.st_size) != (int) sb.st_size)
+ error (1, errno, "cannot read file %s for copying", from);
+ if (write (fdout, buf, (int) sb.st_size) != (int) sb.st_size
+#ifndef FSYNC_MISSING
+ || fsync (fdout) == -1
+#endif
+ )
+ {
+ error (1, errno, "cannot write file %s for copying", to);
+ }
+ free (buf);
+ }
+ (void) close (fdin);
+ if (close (fdout) < 0)
+ error (1, errno, "cannot close %s", to);
+
+ /* now, set the times for the copied file to match those of the original */
+ t.actime = sb.st_atime;
+ t.modtime = sb.st_mtime;
+ (void) utime (to, &t);
+}
+
+/*
+ * Returns non-zero if the argument file is a directory, or is a symbolic
+ * link which points to a directory.
+ */
+int
+isdir (file)
+ char *file;
+{
+ struct stat sb;
+
+ if (stat (file, &sb) < 0)
+ return (0);
+ return (S_ISDIR (sb.st_mode));
+}
+
+/*
+ * Returns non-zero if the argument file is a symbolic link.
+ */
+int
+islink (file)
+ char *file;
+{
+#ifdef S_ISLNK
+ struct stat sb;
+
+ if (lstat (file, &sb) < 0)
+ return (0);
+ return (S_ISLNK (sb.st_mode));
+#else
+ return (0);
+#endif
+}
+
+/*
+ * Returns non-zero if the argument file exists.
+ */
+int
+isfile (file)
+ char *file;
+{
+ struct stat sb;
+
+ if (stat (file, &sb) < 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * Returns non-zero if the argument file is readable.
+ * XXX - must be careful if "cvs" is ever made setuid!
+ */
+int
+isreadable (file)
+ char *file;
+{
+ return (access (file, R_OK) != -1);
+}
+
+/*
+ * Returns non-zero if the argument file is writable
+ * XXX - muct be careful if "cvs" is ever made setuid!
+ */
+int
+iswritable (file)
+ char *file;
+{
+ return (access (file, W_OK) != -1);
+}
+
+/*
+ * Open a file and die if it fails
+ */
+FILE *
+open_file (name, mode)
+ char *name;
+ char *mode;
+{
+ FILE *fp;
+
+ if ((fp = fopen (name, mode)) == NULL)
+ error (1, errno, "cannot open %s", name);
+ return (fp);
+}
+
+/*
+ * Open a file if allowed and return.
+ */
+FILE *
+Fopen (name, mode)
+ char *name;
+ char *mode;
+{
+ if (trace)
+ (void) fprintf (stderr, "-> fopen(%s,%s)\n", name, mode);
+ if (noexec)
+ return (NULL);
+
+ return (fopen (name, mode));
+}
+
+/*
+ * Make a directory and die if it fails
+ */
+void
+make_directory (name)
+ char *name;
+{
+ struct stat buf;
+
+ if (stat (name, &buf) == 0)
+ {
+ if (S_ISDIR (buf.st_mode))
+ {
+ if (access (name, (R_OK | W_OK | X_OK)) == 0)
+ {
+ error (0, 0, "Directory %s already exists", name);
+ return;
+ }
+ else
+ {
+ error (0, 0,
+ "Directory %s already exists but is protected from you",
+ name);
+ }
+ }
+ else
+ error (0, 0, "%s already exists but is not a directory", name);
+ }
+ if (!noexec && mkdir (name, 0777) < 0)
+ error (1, errno, "cannot make directory %s", name);
+}
+
+/*
+ * Make a path to the argument directory, printing a message if something
+ * goes wrong.
+ */
+void
+make_directories (name)
+ char *name;
+{
+ char *cp;
+
+ if (noexec)
+ return;
+
+ if (mkdir (name, 0777) == 0 || errno == EEXIST)
+ return;
+ if (errno != ENOENT)
+ {
+ error (0, errno, "cannot make path to %s", name);
+ return;
+ }
+ if ((cp = rindex (name, '/')) == NULL)
+ return;
+ *cp = '\0';
+ make_directories (name);
+ *cp++ = '/';
+ if (*cp == '\0')
+ return;
+ (void) mkdir (name, 0777);
+}
+
+/*
+ * malloc some data and die if it fails
+ */
+char *
+xmalloc (bytes)
+ int bytes;
+{
+ char *cp;
+
+ if (bytes <= 0)
+ error (1, 0, "bad malloc size %d", bytes);
+ if ((cp = malloc ((unsigned) bytes)) == NULL)
+ error (1, 0, "malloc failed");
+ return (cp);
+}
+
+/*
+ * realloc data and die if it fails [I've always wanted to have "realloc" do
+ * a "malloc" if the argument is NULL, but you can't depend on it. Here, I
+ * can *force* it.
+ */
+char *
+xrealloc (ptr, bytes)
+ char *ptr;
+ int bytes;
+{
+ char *cp;
+
+ if (!ptr)
+ return (xmalloc (bytes));
+
+ if (bytes <= 0)
+ error (1, 0, "bad realloc size %d", bytes);
+ if ((cp = realloc (ptr, (unsigned) bytes)) == NULL)
+ error (1, 0, "realloc failed");
+ return (cp);
+}
+
+/*
+ * Duplicate a string, calling xmalloc to allocate some dynamic space
+ */
+char *
+xstrdup (str)
+ char *str;
+{
+ char *s;
+
+ if (str == NULL)
+ return ((char *) NULL);
+ s = xmalloc (strlen (str) + 1);
+ (void) strcpy (s, str);
+ return (s);
+}
+
+/*
+ * Change the mode of a file, either adding write permissions, or removing
+ * all write permissions. Adding write permissions honors the current umask
+ * setting.
+ */
+void
+xchmod (fname, writable)
+ char *fname;
+ int writable;
+{
+ struct stat sb;
+ int mode, oumask;
+
+ if (stat (fname, &sb) < 0)
+ {
+ if (!noexec)
+ error (0, errno, "cannot stat %s", fname);
+ return;
+ }
+ if (writable)
+ {
+ oumask = umask (0);
+ (void) umask (oumask);
+ mode = sb.st_mode | ((S_IWRITE | S_IWGRP | S_IWOTH) & ~oumask);
+ }
+ else
+ {
+ mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH);
+ }
+
+ if (trace)
+ (void) fprintf (stderr, "-> chmod(%s,%o)\n", fname, mode);
+ if (noexec)
+ return;
+
+ if (chmod (fname, mode) < 0)
+ error (0, errno, "cannot change mode of file %s", fname);
+}
+
+/*
+ * Rename a file and die if it fails
+ */
+void
+rename_file (from, to)
+ char *from;
+ char *to;
+{
+ if (trace)
+ (void) fprintf (stderr, "-> rename(%s,%s)\n", from, to);
+ if (noexec)
+ return;
+
+ if (rename (from, to) < 0)
+ error (1, errno, "cannot rename file %s to %s", from, to);
+}
+
+/*
+ * link a file, if possible.
+ */
+int
+link_file (from, to)
+ char *from, *to;
+{
+ if (trace)
+ (void) fprintf (stderr, "-> link(%s,%s)\n", from, to);
+ if (noexec)
+ return (0);
+
+ return (link (from, to));
+}
+
+/*
+ * unlink a file, if possible.
+ */
+int
+unlink_file (f)
+ char *f;
+{
+ if (trace)
+ (void) fprintf (stderr, "-> unlink(%s)\n", f);
+ if (noexec)
+ return (0);
+
+ return (unlink (f));
+}
+
+/*
+ * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
+ *
+ * mallocs a buffer large enough to hold the entire file and does two reads to
+ * load the buffer and calls bcmp to do the cmp. This is reasonable, since
+ * source files are typically not too large.
+ */
+int
+xcmp (file1, file2)
+ char *file1;
+ char *file2;
+{
+ register char *buf1, *buf2;
+ struct stat sb;
+ off_t size;
+ int ret, fd1, fd2;
+
+ if ((fd1 = open (file1, O_RDONLY)) < 0)
+ error (1, errno, "cannot open file %s for comparing", file1);
+ if ((fd2 = open (file2, O_RDONLY)) < 0)
+ error (1, errno, "cannot open file %s for comparing", file2);
+ if (fstat (fd1, &sb) < 0)
+ error (1, errno, "cannot fstat %s", file1);
+ size = sb.st_size;
+ if (fstat (fd2, &sb) < 0)
+ error (1, errno, "cannot fstat %s", file2);
+ if (size == sb.st_size)
+ {
+ if (size == 0)
+ ret = 0;
+ else
+ {
+ buf1 = xmalloc ((int) size);
+ buf2 = xmalloc ((int) size);
+ if (read (fd1, buf1, (int) size) != (int) size)
+ error (1, errno, "cannot read file %s cor comparing", file1);
+ if (read (fd2, buf2, (int) size) != (int) size)
+ error (1, errno, "cannot read file %s for comparing", file2);
+ ret = bcmp (buf1, buf2, (int) size);
+ free (buf1);
+ free (buf2);
+ }
+ }
+ else
+ ret = 1;
+ (void) close (fd1);
+ (void) close (fd2);
+ return (ret);
+}
+
+/*
+ * Recover the space allocated by Find_Names() and line2argv()
+ */
+void
+free_names (pargc, argv)
+ int *pargc;
+ char *argv[];
+{
+ register int i;
+
+ for (i = 0; i < *pargc; i++)
+ { /* only do through *pargc */
+ free (argv[i]);
+ }
+ *pargc = 0; /* and set it to zero when done */
+}
+
+/*
+ * Convert a line into argc/argv components and return the result in the
+ * arguments as passed. Use free_names() to return the memory allocated here
+ * back to the free pool.
+ */
+void
+line2argv (pargc, argv, line)
+ int *pargc;
+ char *argv[];
+ char *line;
+{
+ char *cp;
+
+ *pargc = 0;
+ for (cp = strtok (line, " \t"); cp; cp = strtok ((char *) NULL, " \t"))
+ {
+ argv[*pargc] = xstrdup (cp);
+ (*pargc)++;
+ }
+}
+
+/*
+ * Returns the number of dots ('.') found in an RCS revision number
+ */
+int
+numdots (s)
+ char *s;
+{
+ char *cp;
+ int dots = 0;
+
+ for (cp = s; *cp; cp++)
+ {
+ if (*cp == '.')
+ dots++;
+ }
+ return (dots);
+}
+
+/*
+ * Get the caller's login from his uid. If the real uid is "root" try LOGNAME
+ * USER or getlogin(). If getlogin() and getpwuid() both fail, return
+ * the uid as a string.
+ */
+char *
+getcaller ()
+{
+ static char uidname[20];
+ struct passwd *pw;
+ char *name;
+ int uid;
+
+ uid = getuid ();
+ if (uid == 0)
+ {
+ /* super-user; try getlogin() to distinguish */
+ if (((name = getenv("LOGNAME")) || (name = getenv("USER")) ||
+ (name = getlogin ())) && *name)
+ return (name);
+ }
+ if ((pw = (struct passwd *) getpwuid (uid)) == NULL)
+ {
+ (void) sprintf (uidname, "uid%d", uid);
+ return (uidname);
+ }
+ return (pw->pw_name);
+}
+
+/*
+ * To exec a program under CVS, first call run_setup() to setup any initial
+ * arguments. The options to run_setup are essentially like printf(). The
+ * arguments will be parsed into whitespace separated words and added to the
+ * global run_argv list.
+ *
+ * Then, optionally call run_arg() for each additional argument that you'd like
+ * to pass to the executed program.
+ *
+ * Finally, call run_exec() to execute the program with the specified arguments.
+ * The execvp() syscall will be used, so that the PATH is searched correctly.
+ * File redirections can be performed in the call to run_exec().
+ */
+static char *run_prog;
+static char **run_argv;
+static int run_argc;
+static int run_argc_allocated;
+
+/* VARARGS */
+#if !defined (VPRINTF_MISSING) && __STDC__
+void
+run_setup (char *fmt,...)
+#else
+void
+run_setup (fmt, va_alist)
+ char *fmt;
+ va_dcl
+
+#endif
+{
+#ifndef VPRINTF_MISSING
+ va_list args;
+
+#endif
+ char *cp;
+ int i;
+
+ run_init_prog ();
+
+ /* clean out any malloc'ed values from run_argv */
+ for (i = 0; i < run_argc; i++)
+ {
+ if (run_argv[i])
+ {
+ free (run_argv[i]);
+ run_argv[i] = (char *) 0;
+ }
+ }
+ run_argc = 0;
+
+ /* process the varargs into run_prog */
+#ifndef VPRINTF_MISSING
+ VA_START (args, fmt);
+ (void) vsprintf (run_prog, fmt, args);
+ va_end (args);
+#else
+ (void) sprintf (run_prog, fmt, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif
+
+ /* put each word into run_argv, allocating it as we go */
+ for (cp = strtok (run_prog, " \t"); cp; cp = strtok ((char *) NULL, " \t"))
+ run_add_arg (cp);
+}
+
+void
+run_arg (s)
+ char *s;
+{
+ run_add_arg (s);
+}
+
+/* VARARGS */
+#if !defined (VPRINTF_MISSING) && __STDC__
+void
+run_args (char *fmt,...)
+#else
+void
+run_args (fmt, va_alist)
+ char *fmt;
+ va_dcl
+
+#endif
+{
+#ifndef VPRINTF_MISSING
+ va_list args;
+
+#endif
+
+ run_init_prog ();
+
+ /* process the varargs into run_prog */
+#ifndef VPRINTF_MISSING
+ VA_START (args, fmt);
+ (void) vsprintf (run_prog, fmt, args);
+ va_end (args);
+#else
+ (void) sprintf (run_prog, fmt, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif
+
+ /* and add the (single) argument to the run_argv list */
+ run_add_arg (run_prog);
+}
+
+static void
+run_add_arg (s)
+ char *s;
+{
+ /* allocate more argv entries if we've run out */
+ if (run_argc >= run_argc_allocated)
+ {
+ run_argc_allocated += 50;
+ run_argv = (char **) xrealloc ((char *) run_argv,
+ run_argc_allocated * sizeof (char **));
+ }
+
+ if (s)
+ run_argv[run_argc++] = xstrdup (s);
+ else
+ run_argv[run_argc] = (char *) 0;/* not post-incremented on purpose! */
+}
+
+static void
+run_init_prog ()
+{
+ /* make sure that run_prog is allocated once */
+ if (run_prog == (char *) 0)
+ run_prog = xmalloc (10 * 1024); /* 10K of args for _setup and _arg */
+}
+
+int
+run_exec (stin, stout, sterr, flags)
+ char *stin;
+ char *stout;
+ char *sterr;
+ int flags;
+{
+ int shin, shout, sherr;
+ int mode_out, mode_err;
+ int status = -1;
+ int rerrno = 0;
+ int pid, w;
+
+#ifdef POSIX
+ sigset_t sigset_mask, sigset_omask;
+ struct sigaction act, iact, qact;
+
+#else
+#ifdef BSD_SIGNALS
+ int mask;
+ struct sigvec vec, ivec, qvec;
+
+#else
+ SIGTYPE (*istat) (), (*qstat) ();
+#endif
+#endif
+
+ if (trace)
+ {
+ (void) fprintf (stderr, "-> system(");
+ run_print (stderr);
+ (void) fprintf (stderr, ")\n");
+ }
+ if (noexec && (flags & RUN_REALLY) == 0)
+ return (0);
+
+ /* make sure that we are null terminated, since we didn't calloc */
+ run_add_arg ((char *) 0);
+
+ /* setup default file descriptor numbers */
+ shin = 0;
+ shout = 1;
+ sherr = 2;
+
+ /* set the file modes for stdout and stderr */
+ mode_out = mode_err = O_WRONLY | O_CREAT;
+ mode_out |= ((flags & RUN_STDOUT_APPEND) ? O_APPEND : O_TRUNC);
+ mode_err |= ((flags & RUN_STDERR_APPEND) ? O_APPEND : O_TRUNC);
+
+ if (stin && (shin = open (stin, O_RDONLY)) == -1)
+ {
+ rerrno = errno;
+ error (0, errno, "cannot open %s for reading (prog %s)",
+ stin, run_argv[0]);
+ goto out0;
+ }
+ if (stout && (shout = open (stout, mode_out, 0666)) == -1)
+ {
+ rerrno = errno;
+ error (0, errno, "cannot open %s for writing (prog %s)",
+ stout, run_argv[0]);
+ goto out1;
+ }
+ if (sterr && (flags & RUN_COMBINED) == 0)
+ {
+ if ((sherr = open (sterr, mode_err, 0666)) == -1)
+ {
+ rerrno = errno;
+ error (0, errno, "cannot open %s for writing (prog %s)",
+ sterr, run_argv[0]);
+ goto out2;
+ }
+ }
+
+ /* The output files, if any, are now created. Do the fork and dups */
+#ifdef VFORK_MISSING
+ pid = fork ();
+#else
+ pid = vfork ();
+#endif
+ if (pid == 0)
+ {
+ if (shin != 0)
+ {
+ (void) dup2 (shin, 0);
+ (void) close (shin);
+ }
+ if (shout != 1)
+ {
+ (void) dup2 (shout, 1);
+ (void) close (shout);
+ }
+ if (flags & RUN_COMBINED)
+ (void) dup2 (1, 2);
+ else if (sherr != 2)
+ {
+ (void) dup2 (sherr, 2);
+ (void) close (sherr);
+ }
+
+ /* dup'ing is done. try to run it now */
+ (void) execvp (run_argv[0], run_argv);
+ _exit (127);
+ }
+ else if (pid == -1)
+ {
+ rerrno = errno;
+ goto out;
+ }
+
+ /* the parent. Ignore some signals for now */
+#ifdef POSIX
+ if (flags & RUN_SIGIGNORE)
+ {
+ act.sa_handler = SIG_IGN;
+ (void) sigemptyset (&act.sa_mask);
+ act.sa_flags = 0;
+ (void) sigaction (SIGINT, &act, &iact);
+ (void) sigaction (SIGQUIT, &act, &qact);
+ }
+ else
+ {
+ (void) sigemptyset (&sigset_mask);
+ (void) sigaddset (&sigset_mask, SIGINT);
+ (void) sigaddset (&sigset_mask, SIGQUIT);
+ (void) sigprocmask (SIG_SETMASK, &sigset_mask, &sigset_omask);
+ }
+#else
+#ifdef BSD_SIGNALS
+ if (flags & RUN_SIGIGNORE)
+ {
+ bzero ((char *) &vec, sizeof (vec));
+ vec.sv_handler = SIG_IGN;
+ (void) sigvec (SIGINT, &vec, &ivec);
+ (void) sigvec (SIGQUIT, &vec, &qvec);
+ }
+ else
+ mask = sigblock (sigmask (SIGINT) | sigmask (SIGQUIT));
+#else
+ istat = signal (SIGINT, SIG_IGN);
+ qstat = signal (SIGQUIT, SIG_IGN);
+#endif
+#endif
+
+ /* wait for our process to die and munge return status */
+#ifdef POSIX
+ while ((w = waitpid (pid, &status, 0)) == -1 && errno == EINTR)
+ ;
+#else
+ while ((w = wait (&status)) != pid)
+ {
+ if (w == -1 && errno != EINTR)
+ break;
+ }
+#endif
+ if (w == -1)
+ {
+ status = -1;
+ rerrno = errno;
+ }
+ else if (WIFEXITED (status))
+ status = WEXITSTATUS (status);
+ else if (WIFSIGNALED (status))
+ {
+ if (WTERMSIG (status) == SIGPIPE)
+ error (1, 0, "broken pipe");
+ status = 2;
+ }
+ else
+ status = 1;
+
+ /* restore the signals */
+#ifdef POSIX
+ if (flags & RUN_SIGIGNORE)
+ {
+ (void) sigaction (SIGINT, &iact, (struct sigaction *) NULL);
+ (void) sigaction (SIGQUIT, &qact, (struct sigaction *) NULL);
+ }
+ else
+ (void) sigprocmask (SIG_SETMASK, &sigset_omask, (sigset_t *) NULL);
+#else
+#ifdef BSD_SIGNALS
+ if (flags & RUN_SIGIGNORE)
+ {
+ (void) sigvec (SIGINT, &ivec, (struct sigvec *) NULL);
+ (void) sigvec (SIGQUIT, &qvec, (struct sigvec *) NULL);
+ }
+ else
+ (void) sigsetmask (mask);
+#else
+ (void) signal (SIGINT, istat);
+ (void) signal (SIGQUIT, qstat);
+#endif
+#endif
+
+ /* cleanup the open file descriptors */
+ out:
+ if (sterr)
+ (void) close (sherr);
+ out2:
+ if (stout)
+ (void) close (shout);
+ out1:
+ if (stin)
+ (void) close (shin);
+
+ out0:
+ if (rerrno)
+ errno = rerrno;
+ return (status);
+}
+
+void
+run_print (fp)
+ FILE *fp;
+{
+ int i;
+
+ for (i = 0; i < run_argc; i++)
+ {
+ (void) fprintf (fp, "%s", run_argv[i]);
+ if (i != run_argc - 1)
+ (void) fprintf (fp, " ");
+ }
+}
+
+FILE *
+Popen (cmd, mode)
+ char *cmd, *mode;
+{
+ if (trace)
+ (void) fprintf (stderr, "-> Popen(%s,%s)\n", cmd, mode);
+ if (noexec)
+ return (NULL);
+ return (popen (cmd, mode));
+}
+
+#ifdef lint
+#ifndef __GNUC__
+/* ARGSUSED */
+time_t
+get_date (date, now)
+ char *date;
+ struct timeb *now;
+{
+ time_t foo = 0;
+
+ return (foo);
+}
+#endif
+#endif
diff --git a/gnu/usr.bin/cvs/lib/system.h b/gnu/usr.bin/cvs/lib/system.h
new file mode 100644
index 0000000..6cfd68f
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/system.h
@@ -0,0 +1,223 @@
+/* system-dependent definitions for CVS.
+ Copyright (C) 1989-1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* @(#)system.h 1.14 92/04/10 */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifndef S_ISREG /* Doesn't have POSIX.1 stat stuff. */
+#ifndef mode_t
+#define mode_t unsigned short
+#endif
+#endif
+#if !defined(S_ISBLK) && defined(S_IFBLK)
+#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
+#endif
+#if !defined(S_ISCHR) && defined(S_IFCHR)
+#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
+#endif
+#if !defined(S_ISDIR) && defined(S_IFDIR)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISREG) && defined(S_IFREG)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISFIFO) && defined(S_IFIFO)
+#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
+#endif
+#if !defined(S_ISLNK) && defined(S_IFLNK)
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#endif
+#if !defined(S_ISSOCK) && defined(S_IFSOCK)
+#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
+#endif
+#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */
+#define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB)
+#define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC)
+#endif
+#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */
+#define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK)
+#endif
+#if defined(MKFIFO_MISSING)
+#define mkfifo(path, mode) (mknod ((path), (mode) | S_IFIFO, 0))
+#endif
+
+#ifdef POSIX
+#include <unistd.h>
+#include <limits.h>
+#ifndef PATH_MAX
+#define PATH_MAX pathconf ("/", _PC_PATH_MAX)
+#endif
+#else
+off_t lseek ();
+#endif
+
+#ifdef TM_IN_SYS_TIME
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+
+#ifdef TIMEB_H_MISSING
+struct timeb {
+ time_t time; /* Seconds since the epoch */
+ unsigned short millitm; /* Field not used */
+#ifdef timezone
+ short tzone;
+#else
+ short timezone;
+#endif
+ short dstflag; /* Field not used */
+};
+#else
+#include <sys/timeb.h>
+#endif
+
+#if defined(FTIME_MISSING) && !defined(HAVE_TIMEZONE)
+#if !defined(timezone)
+extern char *timezone();
+#endif
+#endif
+
+#ifndef POSIX
+#include <sys/param.h>
+#endif
+
+#ifndef _POSIX_PATH_MAX
+#define _POSIX_PATH_MAX 255
+#endif
+
+#ifndef PATH_MAX
+#ifdef MAXPATHLEN
+#define PATH_MAX MAXPATHLEN
+#else
+#define PATH_MAX _POSIX_PATH_MAX
+#endif
+#endif
+
+#ifdef POSIX
+#include <utime.h>
+#else
+#ifndef ALTOS
+struct utimbuf
+{
+ long actime;
+ long modtime;
+};
+#endif
+int utime ();
+#endif
+
+#if defined(USG) || defined(STDC_HEADERS)
+#include <string.h>
+#ifndef STDC_HEADERS
+#include <memory.h>
+#endif
+#ifndef index
+#define index strchr
+#endif
+#ifndef rindex
+#define rindex strrchr
+#endif
+#ifndef bcopy
+#define bcopy(from, to, len) memcpy ((to), (from), (len))
+#endif
+#ifndef bzero
+#define bzero(s, n) (void) memset ((s), 0, (n))
+#endif
+#ifndef bcmp
+#define bcmp(s1, s2, n) memcmp((s1), (s2), (n))
+#endif
+#else
+#include <strings.h>
+#endif
+
+#include <errno.h>
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+char *getenv ();
+char *malloc ();
+char *realloc ();
+char *calloc ();
+extern int errno;
+#endif
+
+#ifdef __GNUC__
+#ifdef bsdi
+#define alloca __builtin_alloca
+#endif
+#else
+#ifdef sparc
+#include <alloca.h>
+#else
+#ifndef _AIX
+/* AIX alloca decl has to be the first thing in the file, bletch! */
+char *alloca ();
+#endif
+#endif
+#endif
+
+#if defined(USG) || defined(POSIX)
+#include <fcntl.h>
+char *getcwd ();
+#else
+#include <sys/file.h>
+char *getwd ();
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+#endif
+#ifndef F_OK
+#define F_OK 0
+#define X_OK 1
+#define W_OK 2
+#define R_OK 4
+#endif
+
+#ifdef DIRENT
+#include <dirent.h>
+#ifdef direct
+#undef direct
+#endif
+#define direct dirent
+#else
+#ifdef SYSNDIR
+#include <sys/ndir.h>
+#else
+#ifdef NDIR
+#include <ndir.h>
+#else /* must be BSD */
+#include <sys/dir.h>
+#endif
+#endif
+#endif
+
+/* Convert B 512-byte blocks to kilobytes if K is nonzero,
+ otherwise return it unchanged. */
+#define convert_blocks(b, k) ((k) ? ((b) + 1) / 2 : (b))
+
+#ifndef S_ISLNK
+#define lstat stat
+#endif
+
+#ifndef SIGTYPE
+#define SIGTYPE void
+#endif
diff --git a/gnu/usr.bin/cvs/lib/wait.h b/gnu/usr.bin/cvs/lib/wait.h
new file mode 100644
index 0000000..49cfb6d
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/wait.h
@@ -0,0 +1,29 @@
+/* wait.h -- POSIX macros for evaluating exit statuses
+ Copyright (C) 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef POSIX
+#include <sys/types.h> /* For pid_t. */
+#include <sys/wait.h>
+#else
+#define WIFSTOPPED(w) (((w) & 0xff) == 0x7f)
+#define WIFSIGNALED(w) (((w) & 0xff) != 0x7f && ((w) & 0xff) != 0)
+#define WIFEXITED(w) (((w) & 0xff) == 0)
+
+#define WSTOPSIG(w) (((w) >> 8) & 0xff)
+#define WTERMSIG(w) ((w) & 0x7f)
+#define WEXITSTATUS(w) (((w) >> 8) & 0xff)
+#endif
diff --git a/gnu/usr.bin/cvs/lib/y.tab.h b/gnu/usr.bin/cvs/lib/y.tab.h
new file mode 100644
index 0000000..4a541d2
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/y.tab.h
@@ -0,0 +1,18 @@
+#define tAGO 257
+#define tDAY 258
+#define tDAYZONE 259
+#define tID 260
+#define tMERIDIAN 261
+#define tMINUTE_UNIT 262
+#define tMONTH 263
+#define tMONTH_UNIT 264
+#define tSEC_UNIT 265
+#define tSNUMBER 266
+#define tUNUMBER 267
+#define tZONE 268
+#define tDST 269
+typedef union {
+ time_t Number;
+ enum _MERIDIAN Meridian;
+} YYSTYPE;
+extern YYSTYPE yylval;
diff --git a/gnu/usr.bin/cvs/lib/yesno.c b/gnu/usr.bin/cvs/lib/yesno.c
new file mode 100644
index 0000000..a705da7
--- /dev/null
+++ b/gnu/usr.bin/cvs/lib/yesno.c
@@ -0,0 +1,37 @@
+/* yesno.c -- read a yes/no response from stdin
+ Copyright (C) 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+
+/* Read one line from standard input
+ and return nonzero if that line begins with y or Y,
+ otherwise return 0. */
+
+int
+yesno ()
+{
+ int c;
+ int rv;
+
+ fflush (stderr);
+ c = getchar ();
+ rv = (c == 'y') || (c == 'Y');
+ while (c != EOF && c != '\n')
+ c = getchar ();
+
+ return rv;
+}
diff --git a/gnu/usr.bin/cvs/mkmodules/Makefile b/gnu/usr.bin/cvs/mkmodules/Makefile
new file mode 100644
index 0000000..4a6ec7c
--- /dev/null
+++ b/gnu/usr.bin/cvs/mkmodules/Makefile
@@ -0,0 +1,9 @@
+PROG = mkmodules
+SRCS = mkmodules.c
+CFLAGS += -I${.CURDIR}/../cvs -I${.CURDIR}/../lib
+DPADD+= ${LIBCVS}
+LDADD+= -lcvs
+
+.include "../../Makefile.inc"
+.include "../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/cvs/mkmodules/mkmodules.1 b/gnu/usr.bin/cvs/mkmodules/mkmodules.1
new file mode 100644
index 0000000..07ba3f5
--- /dev/null
+++ b/gnu/usr.bin/cvs/mkmodules/mkmodules.1
@@ -0,0 +1,65 @@
+.\"
+.\" @(#)mkmodules.1 1.3 92/01/30
+.\"
+.TH MKMODULES 1 "12 October 1991"
+.SH "NAME"
+mkmodules \- Rebuild modules database for CVS
+.SH "SYNOPSIS"
+.B mkmodules
+.I directory
+.SH "DESCRIPTION"
+.B mkmodules
+rebuilds the modules database that
+.BR cvs (1)
+uses.
+The
+.I directory
+specified is expected to contain the
+.BR modules,v " and " loginfo,v
+files.
+.B mkmodules
+carefully checks out the current head revisions of each of these files and
+reuilds the
+.BR ndbm (3)
+format modules database.
+A warning is generated if the modules file contains a duplicate key.
+.SH "FILES"
+.TP
+modules,v
+The modules
+.SM RCS
+file.
+.TP
+modules
+The checked out modules file.
+.TP
+loginfo,v
+The loginfo
+.SM RCS
+file.
+.TP
+loginfo
+The checked out loginfo file.
+.TP
+modules.dir, modules.pag
+The
+.BR ndbm (1)
+format modules database.
+.SH "ENVIRONMENT VARIABLES"
+.TP
+.SM RCSBIN
+Specifies the full pathname where to find
+.SM RCS
+programs, such as
+.BR co (1)
+and
+.BR ci (1).
+If not set, the default is
+.BR /usr/local/bin .
+.SH "SEE ALSO"
+.BR checkin (1),
+.BR co (1),
+.BR cvs (1),
+.BR ndbm (3),
+.BR rcs (1),
+.SH "BUGS"
diff --git a/gnu/usr.bin/cvs/mkmodules/mkmodules.c b/gnu/usr.bin/cvs/mkmodules/mkmodules.c
new file mode 100644
index 0000000..59ec13a
--- /dev/null
+++ b/gnu/usr.bin/cvs/mkmodules/mkmodules.c
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * Copyright (c) 1989-1992, Brian Berliner
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS 1.3 kit.
+ *
+ * mkmodules
+ *
+ * Re-build the modules database for the CVS system. Accepts one argument,
+ * which is the directory that the modules,v file lives in.
+ */
+
+#include "cvs.h"
+
+#undef PATH_MAX
+#define PATH_MAX 1024 /* max number of bytes in pathname */
+
+#ifndef lint
+static char rcsid[] = "@(#)mkmodules.c 1.39 92/03/31";
+#endif
+
+#ifndef DBLKSIZ
+#define DBLKSIZ 4096 /* since GNU ndbm doesn't define it */
+#endif
+
+char *program_name, *command_name;
+
+char *Rcsbin = RCSBIN_DFLT;
+int noexec = 0; /* Here only to satisfy use in subr.c */
+int trace = 0; /* Here only to satisfy use in subr.c */
+
+#if __STDC__
+static int checkout_file (char *file, char *temp);
+static void make_tempfile (char *temp);
+static void mkmodules_usage (void);
+static void rename_rcsfile (char *temp, char *real);
+
+#ifndef MY_NDBM
+static void rename_dbmfile (char *temp);
+static void write_dbmfile (char *temp);
+#endif /* !MY_NDBM */
+
+#else /* !__STDC__ */
+
+static void make_tempfile ();
+static int checkout_file ();
+static void rename_rcsfile ();
+static void mkmodules_usage ();
+
+#ifndef MY_NDBM
+static void write_dbmfile ();
+static void rename_dbmfile ();
+#endif /* !MY_NDBM */
+
+#endif /* __STDC__ */
+
+int
+main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *getenv ();
+ char temp[PATH_MAX];
+ char *cp;
+#ifdef MY_NDBM
+ DBM *db;
+#endif
+
+ /*
+ * Just save the last component of the path for error messages
+ */
+ if ((program_name = rindex (argv[0], '/')) == NULL)
+ program_name = argv[0];
+ else
+ program_name++;
+
+ if (argc != 2)
+ mkmodules_usage ();
+
+ if ((cp = getenv (RCSBIN_ENV)) != NULL)
+ Rcsbin = cp;
+
+ /*
+ * If Rcsbin is set to something, make sure it is terminated with a slash
+ * character. If not, add one.
+ */
+ if (Rcsbin[0] != '\0')
+ {
+ int len = strlen (Rcsbin);
+ char *rcsbin;
+
+ if (Rcsbin[len - 1] != '/')
+ {
+ rcsbin = Rcsbin;
+ Rcsbin = xmalloc (len + 2); /* one for '/', one for NULL */
+ (void) strcpy (Rcsbin, rcsbin);
+ (void) strcat (Rcsbin, "/");
+ }
+ }
+
+ if (chdir (argv[1]) < 0)
+ error (1, errno, "cannot chdir to %s", argv[1]);
+
+ /*
+ * First, do the work necessary to update the "modules" database.
+ */
+ make_tempfile (temp);
+ switch (checkout_file (CVSROOTADM_MODULES, temp))
+ {
+
+ case 0: /* everything ok */
+#ifdef MY_NDBM
+ /* open it, to generate any duplicate errors */
+ if ((db = dbm_open (temp, O_RDONLY, 0666)) != NULL)
+ dbm_close (db);
+#else
+ write_dbmfile (temp);
+ rename_dbmfile (temp);
+#endif
+ rename_rcsfile (temp, CVSROOTADM_MODULES);
+ break;
+
+ case -1: /* fork failed */
+ (void) unlink_file (temp);
+ exit (1);
+ /* NOTREACHED */
+
+ default:
+ error (0, 0,
+ "'cvs checkout' is less functional without a %s file",
+ CVSROOTADM_MODULES);
+ break;
+ } /* switch on checkout_file() */
+
+ (void) unlink_file (temp);
+
+ /*
+ * Now, check out the "loginfo" file, so that it is always up-to-date in
+ * the CVSROOT directory.
+ */
+ make_tempfile (temp);
+ if (checkout_file (CVSROOTADM_LOGINFO, temp) == 0)
+ rename_rcsfile (temp, CVSROOTADM_LOGINFO);
+ else
+ error (0, 0,
+ "no logging of 'cvs commit' messages is done without a %s file",
+ CVSROOTADM_LOGINFO);
+ (void) unlink_file (temp);
+
+ /*
+ * Now, check out the "rcsinfo" file, so that it is always up-to-date in
+ * the CVSROOT directory.
+ */
+ make_tempfile (temp);
+ if (checkout_file (CVSROOTADM_RCSINFO, temp) == 0)
+ rename_rcsfile (temp, CVSROOTADM_RCSINFO);
+ else
+ error (0, 0,
+ "a %s file can be used to configure 'cvs commit' templates",
+ CVSROOTADM_RCSINFO);
+ (void) unlink_file (temp);
+
+ /*
+ * Now, check out the "editinfo" file, so that it is always up-to-date in
+ * the CVSROOT directory.
+ */
+ make_tempfile (temp);
+ if (checkout_file (CVSROOTADM_EDITINFO, temp) == 0)
+ rename_rcsfile (temp, CVSROOTADM_EDITINFO);
+ else
+ error (0, 0,
+ "a %s file can be used to validate log messages",
+ CVSROOTADM_EDITINFO);
+ (void) unlink_file (temp);
+
+ /*
+ * Now, check out the "commitinfo" file, so that it is always up-to-date
+ * in the CVSROOT directory.
+ */
+ make_tempfile (temp);
+ if (checkout_file (CVSROOTADM_COMMITINFO, temp) == 0)
+ rename_rcsfile (temp, CVSROOTADM_COMMITINFO);
+ else
+ error (0, 0,
+ "a %s file can be used to configure 'cvs commit' checking",
+ CVSROOTADM_COMMITINFO);
+ (void) unlink_file (temp);
+ return (0);
+}
+
+/*
+ * Yeah, I know, there are NFS race conditions here.
+ */
+static void
+make_tempfile (temp)
+ char *temp;
+{
+ static int seed = 0;
+ int fd;
+
+ if (seed == 0)
+ seed = getpid ();
+ while (1)
+ {
+ (void) sprintf (temp, "%s%d", BAKPREFIX, seed++);
+ if ((fd = open (temp, O_CREAT|O_EXCL|O_RDWR, 0666)) != -1)
+ break;
+ if (errno != EEXIST)
+ error (1, errno, "cannot create temporary file %s", temp);
+ }
+ if (close(fd) < 0)
+ error(1, errno, "cannot close temporary file %s", temp);
+}
+
+static int
+checkout_file (file, temp)
+ char *file;
+ char *temp;
+{
+ char rcs[PATH_MAX];
+ int retcode = 0;
+
+ (void) sprintf (rcs, "%s%s", file, RCSEXT);
+ if (!isfile (rcs))
+ return (1);
+ run_setup ("%s%s -q -p", Rcsbin, RCS_CO);
+ run_arg (rcs);
+ if ((retcode = run_exec (RUN_TTY, temp, RUN_TTY, RUN_NORMAL)) != 0)
+ {
+ error (0, retcode == -1 ? errno : 0, "failed to check out %s file", file);
+ }
+ return (retcode);
+}
+
+#ifndef MY_NDBM
+
+static void
+write_dbmfile (temp)
+ char *temp;
+{
+ char line[DBLKSIZ], value[DBLKSIZ];
+ FILE *fp;
+ DBM *db;
+ char *cp, *vp;
+ datum key, val;
+ int len, cont, err = 0;
+
+ fp = open_file (temp, "r");
+ if ((db = dbm_open (temp, O_RDWR | O_CREAT | O_TRUNC, 0666)) == NULL)
+ error (1, errno, "cannot open dbm file %s for creation", temp);
+ for (cont = 0; fgets (line, sizeof (line), fp) != NULL;)
+ {
+ if ((cp = rindex (line, '\n')) != NULL)
+ *cp = '\0'; /* strip the newline */
+
+ /*
+ * Add the line to the value, at the end if this is a continuation
+ * line; otherwise at the beginning, but only after any trailing
+ * backslash is removed.
+ */
+ vp = value;
+ if (cont)
+ vp += strlen (value);
+
+ /*
+ * See if the line we read is a continuation line, and strip the
+ * backslash if so.
+ */
+ len = strlen (line);
+ if (len > 0)
+ cp = &line[len - 1];
+ else
+ cp = line;
+ if (*cp == '\\')
+ {
+ cont = 1;
+ *cp = '\0';
+ }
+ else
+ {
+ cont = 0;
+ }
+ (void) strcpy (vp, line);
+ if (value[0] == '#')
+ continue; /* comment line */
+ vp = value;
+ while (*vp && isspace (*vp))
+ vp++;
+ if (*vp == '\0')
+ continue; /* empty line */
+
+ /*
+ * If this was not a continuation line, add the entry to the database
+ */
+ if (!cont)
+ {
+ key.dptr = vp;
+ while (*vp && !isspace (*vp))
+ vp++;
+ key.dsize = vp - key.dptr;
+ *vp++ = '\0'; /* NULL terminate the key */
+ while (*vp && isspace (*vp))
+ vp++; /* skip whitespace to value */
+ if (*vp == '\0')
+ {
+ error (0, 0, "warning: NULL value for key `%s'", key.dptr);
+ continue;
+ }
+ val.dptr = vp;
+ val.dsize = strlen (vp);
+ if (dbm_store (db, key, val, DBM_INSERT) == 1)
+ {
+ error (0, 0, "duplicate key found for `%s'", key.dptr);
+ err++;
+ }
+ }
+ }
+ dbm_close (db);
+ (void) fclose (fp);
+ if (err)
+ {
+ char dotdir[50], dotpag[50];
+
+ (void) sprintf (dotdir, "%s.dir", temp);
+ (void) sprintf (dotpag, "%s.pag", temp);
+ (void) unlink_file (dotdir);
+ (void) unlink_file (dotpag);
+ error (1, 0, "DBM creation failed; correct above errors");
+ }
+}
+
+static void
+rename_dbmfile (temp)
+ char *temp;
+{
+ char newdir[50], newpag[50];
+ char dotdir[50], dotpag[50];
+ char bakdir[50], bakpag[50];
+
+ (void) sprintf (dotdir, "%s.dir", CVSROOTADM_MODULES);
+ (void) sprintf (dotpag, "%s.pag", CVSROOTADM_MODULES);
+ (void) sprintf (bakdir, "%s%s.dir", BAKPREFIX, CVSROOTADM_MODULES);
+ (void) sprintf (bakpag, "%s%s.pag", BAKPREFIX, CVSROOTADM_MODULES);
+ (void) sprintf (newdir, "%s.dir", temp);
+ (void) sprintf (newpag, "%s.pag", temp);
+
+ (void) chmod (newdir, 0666);
+ (void) chmod (newpag, 0666);
+
+ /* don't mess with me */
+ SIG_beginCrSect ();
+
+ (void) unlink_file (bakdir); /* rm .#modules.dir .#modules.pag */
+ (void) unlink_file (bakpag);
+ (void) rename (dotdir, bakdir); /* mv modules.dir .#modules.dir */
+ (void) rename (dotpag, bakpag); /* mv modules.pag .#modules.pag */
+ (void) rename (newdir, dotdir); /* mv "temp".dir modules.dir */
+ (void) rename (newpag, dotpag); /* mv "temp".pag modules.pag */
+
+ /* OK -- make my day */
+ SIG_endCrSect ();
+}
+
+#endif /* !MY_NDBM */
+
+static void
+rename_rcsfile (temp, real)
+ char *temp;
+ char *real;
+{
+ char bak[50];
+
+ if (chmod (temp, 0444) < 0) /* chmod 444 "temp" */
+ error (0, errno, "warning: cannot chmod %s", temp);
+ (void) sprintf (bak, "%s%s", BAKPREFIX, real);
+ (void) unlink_file (bak); /* rm .#loginfo */
+ (void) rename (real, bak); /* mv loginfo .#loginfo */
+ (void) rename (temp, real); /* mv "temp" loginfo */
+}
+
+/*
+ * For error() only
+ */
+void
+Lock_Cleanup ()
+{
+}
+
+static void
+mkmodules_usage ()
+{
+ (void) fprintf (stderr, "Usage: %s modules-directory\n", program_name);
+ exit (1);
+}
diff --git a/gnu/usr.bin/cvs/mkmodules/xxx b/gnu/usr.bin/cvs/mkmodules/xxx
new file mode 100644
index 0000000..f0dd87d
--- /dev/null
+++ b/gnu/usr.bin/cvs/mkmodules/xxx
@@ -0,0 +1,5320 @@
+# 1 "/usr/src/gnu/cvs/mkmodules/mkmodules.c"
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/sys/syslimits.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 14 "/usr/src/gnu/cvs/mkmodules/mkmodules.c" 2
+
+# 1 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 1
+
+
+# 1 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/sys/types.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+typedef unsigned char u_char;
+typedef unsigned short u_short;
+typedef unsigned int u_int;
+typedef unsigned long u_long;
+typedef unsigned short ushort;
+
+typedef char * caddr_t;
+typedef long daddr_t;
+typedef short dev_t;
+typedef u_long ino_t;
+typedef long off_t;
+typedef u_short nlink_t;
+typedef long swblk_t;
+typedef long segsz_t;
+typedef u_short uid_t;
+typedef u_short gid_t;
+typedef short pid_t;
+typedef u_short mode_t;
+typedef u_long fixpt_t;
+
+
+typedef struct _uquad { u_long val[2]; } u_quad;
+typedef struct _quad { long val[2]; } quad;
+typedef long * qaddr_t;
+
+
+
+
+
+
+# 1 "/usr/include/machine/ansi.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 69 "/usr/include/sys/types.h" 2
+
+
+# 1 "/usr/include/machine/types.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+typedef struct _physadr {
+ int r[1];
+} *physadr;
+
+typedef struct label_t {
+ int val[6];
+} label_t;
+
+typedef u_long vm_offset_t;
+typedef u_long vm_size_t;
+
+
+# 71 "/usr/include/sys/types.h" 2
+
+
+
+
+typedef unsigned long clock_t;
+
+
+
+
+typedef unsigned int size_t;
+
+
+
+
+typedef long time_t;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+typedef long fd_mask;
+
+
+
+
+
+
+typedef struct fd_set {
+ fd_mask fds_bits[(((256 )+(( (sizeof(fd_mask) * 8 ) )-1))/( (sizeof(fd_mask) * 8 ) )) ];
+} fd_set;
+
+
+
+
+
+
+# 132 "/usr/include/sys/types.h"
+
+
+
+
+# 20 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 2
+
+# 1 "/usr/include/sys/stat.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+struct stat
+{
+ dev_t st_dev;
+ ino_t st_ino;
+ mode_t st_mode;
+ nlink_t st_nlink;
+ uid_t st_uid;
+ gid_t st_gid;
+ dev_t st_rdev;
+ off_t st_size;
+ time_t st_atime;
+ long st_spare1;
+ time_t st_mtime;
+ long st_spare2;
+ time_t st_ctime;
+ long st_spare3;
+ long st_blksize;
+ long st_blocks;
+ u_long st_flags;
+ u_long st_gen;
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/sys/cdefs.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 76 "/usr/include/sys/cdefs.h"
+
+
+
+# 114 "/usr/include/sys/stat.h" 2
+
+
+
+mode_t umask (mode_t) ;
+int chmod (const char *, mode_t) ;
+int fstat (int, struct stat *) ;
+int mkdir (const char *, mode_t) ;
+int mkfifo (const char *, mode_t) ;
+int stat (const char *, struct stat *) ;
+
+int fchmod (int, mode_t) ;
+int lstat (const char *, struct stat *) ;
+
+
+
+# 21 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+off_t lseek ();
+
+
+
+
+
+# 1 "/usr/include/time.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/machine/ansi.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 57 "/usr/include/machine/ansi.h"
+
+# 39 "/usr/include/time.h" 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+struct tm {
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_mday;
+ int tm_mon;
+ int tm_year;
+ int tm_wday;
+ int tm_yday;
+ int tm_isdst;
+ long tm_gmtoff;
+ char *tm_zone;
+};
+
+# 1 "/usr/include/sys/cdefs.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 78 "/usr/include/sys/cdefs.h"
+
+# 74 "/usr/include/time.h" 2
+
+
+
+char *asctime (const struct tm *) ;
+clock_t clock (void) ;
+char *ctime (const time_t *) ;
+double difftime (time_t, time_t) ;
+struct tm *gmtime (const time_t *) ;
+struct tm *localtime (const time_t *) ;
+time_t mktime (struct tm *) ;
+size_t strftime (char *, size_t, const char *, const struct tm *) ;
+time_t time (time_t *) ;
+
+
+void tzset (void) ;
+
+
+
+char *timezone (int, int) ;
+void tzsetwall (void) ;
+
+
+
+
+# 72 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 2
+
+
+
+# 86 "/usr/src/gnu/cvs/mkmodules/../lib/system.h"
+
+# 1 "/usr/include/sys/timeb.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+struct timeb {
+ time_t time;
+ unsigned short millitm;
+ short timezone;
+ short dstflag;
+};
+# 87 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 2
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/sys/param.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/sys/types.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 135 "/usr/include/sys/types.h"
+
+# 46 "/usr/include/sys/param.h" 2
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/sys/syslimits.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 56 "/usr/include/sys/param.h" 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/sys/signal.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/machine/trap.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 42 "/usr/include/sys/signal.h" 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/sys/cdefs.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 78 "/usr/include/sys/cdefs.h"
+
+# 90 "/usr/include/sys/signal.h" 2
+
+
+
+typedef void (*sig_t) (int) ;
+
+
+typedef void (*__sighandler_t) (int) ;
+typedef unsigned int sigset_t;
+
+
+int sigaddset (sigset_t *, int) ;
+int sigdelset (sigset_t *, int) ;
+int sigemptyset (sigset_t *) ;
+int sigfillset (sigset_t *) ;
+int sigismember (const sigset_t *, int) ;
+
+
+
+
+
+
+
+
+
+
+
+struct sigaction {
+ __sighandler_t sa_handler;
+ sigset_t sa_mask;
+ int sa_flags;
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+struct sigvec {
+ void (*sv_handler)();
+ int sv_mask;
+ int sv_flags;
+};
+
+
+
+
+
+
+
+struct sigaltstack {
+ char *ss_base;
+ int ss_len;
+ int ss_onstack;
+};
+
+
+
+
+struct sigstack {
+ char *ss_sp;
+ int ss_onstack;
+};
+
+
+
+
+
+
+
+
+struct sigcontext {
+ int sc_onstack;
+ int sc_mask;
+ int sc_sp;
+ int sc_fp;
+ int sc_ap;
+ int sc_pc;
+ int sc_ps;
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/sys/types.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 135 "/usr/include/sys/types.h"
+
+# 195 "/usr/include/sys/signal.h" 2
+
+
+
+
+__sighandler_t signal (int, __sighandler_t) ;
+int raise (int) ;
+
+int kill (pid_t, int) ;
+int sigaction (int, const struct sigaction *, struct sigaction *) ;
+int sigpending (sigset_t *) ;
+int sigprocmask (int, const sigset_t *, sigset_t *) ;
+int sigsuspend (const sigset_t *) ;
+
+
+int killpg (pid_t, int) ;
+void psignal (unsigned, const char *) ;
+int sigblock (int) ;
+int siginterrupt (int, int) ;
+int sigpause (int) ;
+int sigreturn (struct sigcontext *) ;
+int sigsetmask (int) ;
+int sigstack (const struct sigstack *, struct sigstack *) ;
+int sigvec (int, struct sigvec *, struct sigvec *) ;
+
+
+
+
+
+# 79 "/usr/include/sys/param.h" 2
+
+
+
+# 1 "/usr/include/machine/param.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 82 "/usr/include/sys/param.h" 2
+
+# 1 "/usr/include/machine/endian.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/sys/cdefs.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 78 "/usr/include/sys/cdefs.h"
+
+# 55 "/usr/include/machine/endian.h" 2
+
+
+
+
+
+
+
+
+
+# 77 "/usr/include/machine/endian.h"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 106 "/usr/include/machine/endian.h"
+
+
+
+
+
+
+
+
+
+
+
+
+# 83 "/usr/include/sys/param.h" 2
+
+# 1 "/usr/include/machine/limits.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 84 "/usr/include/sys/param.h" 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 97 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+struct utimbuf
+{
+ long actime;
+ long modtime;
+};
+
+int utime ();
+
+
+# 145 "/usr/src/gnu/cvs/mkmodules/../lib/system.h"
+
+# 1 "/usr/include/strings.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/string.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/machine/ansi.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 57 "/usr/include/machine/ansi.h"
+
+# 38 "/usr/include/string.h" 2
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/sys/cdefs.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 78 "/usr/include/sys/cdefs.h"
+
+# 49 "/usr/include/string.h" 2
+
+
+
+void *memchr (const void *, int, size_t) ;
+int memcmp (const void *, const void *, size_t) ;
+void *memcpy (void *, const void *, size_t) ;
+void *memmove (void *, const void *, size_t) ;
+void *memset (void *, int, size_t) ;
+char *strcat (char *, const char *) ;
+char *strchr (const char *, int) ;
+int strcmp (const char *, const char *) ;
+int strcoll (const char *, const char *) ;
+char *strcpy (char *, const char *) ;
+size_t strcspn (const char *, const char *) ;
+char *strerror (int) ;
+size_t strlen (const char *) ;
+char *strncat (char *, const char *, size_t) ;
+int strncmp (const char *, const char *, size_t) ;
+char *strncpy (char *, const char *, size_t) ;
+char *strpbrk (const char *, const char *) ;
+char *strrchr (const char *, int) ;
+size_t strspn (const char *, const char *) ;
+char *strstr (const char *, const char *) ;
+char *strtok (char *, const char *) ;
+size_t strxfrm (char *, const char *, size_t) ;
+
+
+
+int bcmp (const void *, const void *, size_t) ;
+void bcopy (const void *, void *, size_t) ;
+void bzero (void *, size_t) ;
+int ffs (int) ;
+char *index (const char *, int) ;
+void *memccpy (void *, const void *, int, size_t) ;
+char *rindex (const char *, int) ;
+int strcasecmp (const char *, const char *) ;
+char *strdup (const char *) ;
+void strmode (int, char *) ;
+int strncasecmp (const char *, const char *, size_t) ;
+char *strsep (char **, const char *) ;
+void swab (const void *, void *, size_t) ;
+
+
+
+
+# 36 "/usr/include/strings.h" 2
+
+# 146 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 2
+
+
+
+# 1 "/usr/include/errno.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+extern int errno;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 149 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 2
+
+
+
+
+char *getenv ();
+char *malloc ();
+char *realloc ();
+char *calloc ();
+extern int errno;
+
+
+
+
+
+
+# 173 "/usr/src/gnu/cvs/mkmodules/../lib/system.h"
+
+
+
+
+
+
+# 1 "/usr/include/sys/file.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/sys/fcntl.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/sys/types.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 135 "/usr/include/sys/types.h"
+
+# 46 "/usr/include/sys/fcntl.h" 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 99 "/usr/include/sys/fcntl.h"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+struct flock {
+ short l_type;
+ short l_whence;
+ off_t l_start;
+ off_t l_len;
+ pid_t l_pid;
+};
+
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/sys/cdefs.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 78 "/usr/include/sys/cdefs.h"
+
+# 169 "/usr/include/sys/fcntl.h" 2
+
+
+
+int open (const char *, int, ...) ;
+int creat (const char *, mode_t) ;
+int fcntl (int, int, ...) ;
+
+int flock (int, int) ;
+
+
+
+
+
+# 36 "/usr/include/sys/file.h" 2
+
+# 1 "/usr/include/sys/unistd.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 37 "/usr/include/sys/file.h" 2
+
+
+# 73 "/usr/include/sys/file.h"
+
+# 179 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 2
+
+char *getwd ();
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/sys/dir.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/dirent.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+struct dirent {
+ u_long d_fileno;
+ u_short d_reclen;
+ u_short d_namlen;
+
+
+
+
+ char d_name[255 + 1];
+
+};
+
+
+
+
+
+
+
+
+
+
+
+typedef struct _dirdesc {
+ int dd_fd;
+ long dd_loc;
+ long dd_size;
+ char *dd_buf;
+ int dd_len;
+ long dd_seek;
+} DIR;
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/sys/cdefs.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 78 "/usr/include/sys/cdefs.h"
+
+# 88 "/usr/include/dirent.h" 2
+
+
+
+DIR *opendir (const char *) ;
+struct dirent *readdir (DIR *) ;
+void rewinddir (DIR *) ;
+int closedir (DIR *) ;
+
+long telldir (const DIR *) ;
+void seekdir (DIR *, long) ;
+int scandir (const char *, struct dirent ***,
+ int (*)(struct dirent *), int (*)(const void *, const void *)) ;
+int alphasort (const void *, const void *) ;
+int getdirentries (int, char *, int, long *) ;
+
+
+
+
+
+
+# 44 "/usr/include/sys/dir.h" 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 208 "/usr/src/gnu/cvs/mkmodules/../lib/system.h" 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 3 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2
+
+# 1 "/usr/include/stdio.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/sys/cdefs.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 78 "/usr/include/sys/cdefs.h"
+
+# 42 "/usr/include/stdio.h" 2
+
+
+# 1 "/usr/include/machine/ansi.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 57 "/usr/include/machine/ansi.h"
+
+# 44 "/usr/include/stdio.h" 2
+
+
+
+
+
+
+
+
+
+
+typedef long fpos_t;
+
+
+
+
+
+
+
+
+
+
+struct __sbuf {
+ unsigned char *_base;
+ int _size;
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+typedef struct __sFILE {
+ unsigned char *_p;
+ int _r;
+ int _w;
+ short _flags;
+ short _file;
+ struct __sbuf _bf;
+ int _lbfsize;
+
+
+ void *_cookie;
+ int (*_close) (void *) ;
+ int (*_read) (void *, char *, int) ;
+ fpos_t (*_seek) (void *, fpos_t, int) ;
+ int (*_write) (void *, const char *, int) ;
+
+
+ struct __sbuf _ub;
+ unsigned char *_up;
+ int _ur;
+
+
+ unsigned char _ubuf[3];
+ unsigned char _nbuf[1];
+
+
+ struct __sbuf _lb;
+
+
+ int _blksize;
+ int _offset;
+} FILE;
+
+
+extern FILE __sF[];
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void clearerr (FILE *) ;
+int fclose (FILE *) ;
+int feof (FILE *) ;
+int ferror (FILE *) ;
+int fflush (FILE *) ;
+int fgetc (FILE *) ;
+int fgetpos (FILE *, fpos_t *) ;
+char *fgets (char *, size_t, FILE *) ;
+FILE *fopen (const char *, const char *) ;
+int fprintf (FILE *, const char *, ...) ;
+int fputc (int, FILE *) ;
+int fputs (const char *, FILE *) ;
+int fread (void *, size_t, size_t, FILE *) ;
+FILE *freopen (const char *, const char *, FILE *) ;
+int fscanf (FILE *, const char *, ...) ;
+int fseek (FILE *, long, int) ;
+int fsetpos (FILE *, const fpos_t *) ;
+long ftell (const FILE *) ;
+int fwrite (const void *, size_t, size_t, FILE *) ;
+int getc (FILE *) ;
+int getchar (void) ;
+char *gets (char *) ;
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+void perror (const char *) ;
+int printf (const char *, ...) ;
+int putc (int, FILE *) ;
+int putchar (int) ;
+int puts (const char *) ;
+int remove (const char *) ;
+int rename (const char *, const char *) ;
+void rewind (FILE *) ;
+int scanf (const char *, ...) ;
+void setbuf (FILE *, char *) ;
+int setvbuf (FILE *, char *, int, size_t) ;
+int sprintf (char *, const char *, ...) ;
+int sscanf (char *, const char *, ...) ;
+FILE *tmpfile (void) ;
+char *tmpnam (char *) ;
+int ungetc (int, FILE *) ;
+int vfprintf (FILE *, const char *, char * ) ;
+int vprintf (const char *, char * ) ;
+int vsprintf (char *, const char *, char * ) ;
+
+
+
+
+
+
+
+
+
+
+char *ctermid (char *) ;
+FILE *fdopen (int, const char *) ;
+int fileno (FILE *) ;
+
+
+
+
+
+
+
+
+char *fgetline (FILE *, size_t *) ;
+int fpurge (FILE *) ;
+int getw (FILE *) ;
+int pclose (FILE *) ;
+FILE *popen (const char *, const char *) ;
+int putw (int, FILE *) ;
+void setbuffer (FILE *, char *, int) ;
+int setlinebuf (FILE *) ;
+char *tempnam (const char *, const char *) ;
+int snprintf (char *, size_t, const char *, ...) ;
+int vsnprintf (char *, size_t, const char *, char * ) ;
+int vscanf (const char *, char * ) ;
+int vsscanf (const char *, const char *, char * ) ;
+
+
+
+
+
+
+
+
+
+
+
+
+
+FILE *funopen (const void *,
+ int (*)(void *, char *, int),
+ int (*)(void *, const char *, int),
+ fpos_t (*)(void *, fpos_t, int),
+ int (*)(void *)) ;
+
+
+
+
+
+
+
+
+
+int __srget (FILE *) ;
+int __svfscanf (FILE *, const char *, char * ) ;
+int __swbuf (int, FILE *) ;
+
+
+
+
+
+
+
+
+static inline int __sputc(int _c, FILE *_p) {
+ if (--_p->_w >= 0 || (_p->_w >= _p->_lbfsize && (char)_c != '\n'))
+ return (*_p->_p++ = _c);
+ else
+ return (__swbuf(_c, _p));
+}
+# 331 "/usr/include/stdio.h"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 4 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2
+
+# 1 "/usr/include/ctype.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+extern char _ctype_[];
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 5 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2
+
+# 1 "/usr/include/pwd.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 1 "/usr/include/sys/types.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 135 "/usr/include/sys/types.h"
+
+# 39 "/usr/include/pwd.h" 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+struct passwd {
+ char *pw_name;
+ char *pw_passwd;
+ int pw_uid;
+ int pw_gid;
+ time_t pw_change;
+ char *pw_class;
+ char *pw_gecos;
+ char *pw_dir;
+ char *pw_shell;
+ time_t pw_expire;
+};
+
+# 1 "/usr/include/sys/cdefs.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 78 "/usr/include/sys/cdefs.h"
+
+# 72 "/usr/include/pwd.h" 2
+
+
+
+struct passwd *getpwuid (uid_t) ;
+struct passwd *getpwnam (const char *) ;
+
+struct passwd *getpwent (void) ;
+int setpassent (int) ;
+int setpwent (void) ;
+void endpwent (void) ;
+
+
+
+
+# 6 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2
+
+# 1 "/usr/include/signal.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 222 "/usr/include/signal.h"
+
+# 7 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2
+
+# 1 "/usr/src/gnu/cvs/mkmodules/../cvs/hash.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+enum ntype
+{
+ UNKNOWN, HEADER, ENTRIES, FILES, LIST, RCSNODE,
+ RCSVERS, DIRS, UPDATE, LOCK, NDBMNODE
+};
+typedef enum ntype Ntype;
+
+struct node
+{
+ Ntype type;
+ struct node *next;
+ struct node *prev;
+ struct node *hashnext;
+ struct node *hashprev;
+ char *key;
+ char *data;
+ void (*delproc) ();
+};
+typedef struct node Node;
+
+struct list
+{
+ Node *list;
+ Node *hasharray[151 ];
+ struct list *next;
+};
+typedef struct list List;
+
+struct entnode
+{
+ char *version;
+ char *timestamp;
+ char *options;
+ char *tag;
+ char *date;
+};
+typedef struct entnode Entnode;
+
+
+List *getlist (void);
+Node *findnode (List * list, char *key);
+Node *getnode (void);
+int addnode (List * list, Node * p);
+int walklist (List * list, int (*proc) ());
+void dellist (List ** listp);
+void delnode (Node * p);
+void freenode (Node * p);
+void sortlist (List * list, int (*comp) ());
+# 77 "/usr/src/gnu/cvs/mkmodules/../cvs/hash.h"
+
+# 8 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2
+
+# 1 "/usr/src/gnu/cvs/mkmodules/../cvs/rcs.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+struct rcsnode
+{
+ int refcount;
+ int flags;
+ char *path;
+ char *head;
+ char *branch;
+ List *symbols;
+ List *versions;
+ List *dates;
+};
+typedef struct rcsnode RCSNode;
+
+struct rcsversnode
+{
+ char *version;
+ char *date;
+ char *next;
+ List *branches;
+};
+typedef struct rcsversnode RCSVers;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+List *RCS_parsefiles (List * files, char *xrepos);
+RCSNode *RCS_parse (char *file, char *repos);
+RCSNode *RCS_parsercsfile (char *rcsfile);
+char *RCS_check_kflag (char *arg);
+char *RCS_getdate (RCSNode * rcs, char *date, int force_tag_match);
+char *RCS_gettag (RCSNode * rcs, char *tag, int force_tag_match);
+char *RCS_getversion (RCSNode * rcs, char *tag, char *date,
+ int force_tag_match);
+char *RCS_magicrev (RCSNode *rcs, char *rev);
+int RCS_isbranch (char *file, char *rev, List *srcfiles);
+char *RCS_whatbranch (char *file, char *tag, List *srcfiles);
+char *RCS_head (RCSNode * rcs);
+int RCS_datecmp (char *date1, char *date2);
+time_t RCS_getrevtime (RCSNode * rcs, char *rev, char *date, int fudge);
+void RCS_check_tag (char *tag);
+void freercsnode (RCSNode ** rnodep);
+# 102 "/usr/src/gnu/cvs/mkmodules/../cvs/rcs.h"
+
+# 9 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2
+
+# 1 "/usr/src/gnu/cvs/mkmodules/../lib/regex.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+typedef unsigned reg_syntax_t;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+extern reg_syntax_t obscure_syntax;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+typedef enum
+{
+ REG_NOERROR = 0,
+ REG_NOMATCH,
+
+
+
+ REG_BADPAT,
+ REG_ECOLLATE,
+ REG_ECTYPE,
+ REG_EESCAPE,
+ REG_ESUBREG,
+ REG_EBRACK,
+ REG_EPAREN,
+ REG_EBRACE,
+ REG_BADBR,
+ REG_ERANGE,
+ REG_ESPACE,
+ REG_BADRPT,
+
+
+ REG_EEND,
+ REG_ESIZE,
+ REG_ERPAREN
+} reg_errcode_t;
+
+
+
+
+
+
+
+
+
+
+struct re_pattern_buffer
+{
+
+
+
+
+ unsigned char *buffer;
+
+
+ unsigned long allocated;
+
+
+ unsigned long used;
+
+
+ reg_syntax_t syntax;
+
+
+
+
+ char *fastmap;
+
+
+
+
+
+ char *translate;
+
+
+ size_t re_nsub;
+
+
+
+
+
+
+ unsigned can_be_null : 2;
+
+
+
+ unsigned fastmap_accurate : 1;
+
+
+
+ unsigned no_sub : 1;
+
+
+
+ unsigned not_bol : 1;
+
+
+ unsigned not_eol : 1;
+
+
+ unsigned newline_anchor : 1;
+
+
+
+
+ unsigned caller_allocated_regs : 1;
+
+};
+
+typedef struct re_pattern_buffer regex_t;
+
+
+
+
+
+
+
+
+
+
+
+typedef int regoff_t;
+
+
+
+
+struct re_registers
+{
+ unsigned num_regs;
+ regoff_t *start;
+ regoff_t *end;
+};
+
+
+
+
+
+
+
+
+
+
+
+
+typedef struct
+{
+ regoff_t rm_so;
+ regoff_t rm_eo;
+} regmatch_t;
+
+
+
+
+
+
+
+
+
+
+extern reg_syntax_t re_set_syntax (reg_syntax_t syntax);
+
+
+
+
+extern const char *re_compile_pattern (const char *pattern, int length,
+ struct re_pattern_buffer *buffer);
+
+
+
+
+
+extern int re_compile_fastmap (struct re_pattern_buffer *buffer);
+
+
+
+
+
+
+
+extern int re_search (struct re_pattern_buffer *buffer,
+ const char *string, int length,
+ int start, int range,
+ struct re_registers *regs);
+
+
+
+
+extern int re_search_2 (struct re_pattern_buffer *buffer,
+ const char *string1, int length1,
+ const char *string2, int length2,
+ int start, int range,
+ struct re_registers *regs,
+ int stop);
+
+
+
+
+extern int re_match (const struct re_pattern_buffer *buffer,
+ const char *string, int length,
+ int start, struct re_registers *regs);
+
+
+
+extern int re_match_2 (const struct re_pattern_buffer *buffer,
+ const char *string1, int length1,
+ const char *string2, int length2,
+ int start,
+ struct re_registers *regs,
+ int stop);
+
+
+
+
+
+
+
+
+
+
+
+extern int regcomp (regex_t *preg, const char *pattern, int cflags);
+extern int regexec (const regex_t *preg, const char *string, size_t nmatch,
+ regmatch_t pmatch[], int eflags);
+extern size_t regerror (int errcode, const regex_t *preg, char *errbuf,
+ size_t errbuf_size);
+extern void regfree (regex_t *preg);
+
+# 468 "/usr/src/gnu/cvs/mkmodules/../lib/regex.h"
+
+
+
+
+
+
+
+
+
+
+
+
+# 10 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2
+
+# 1 "/usr/src/gnu/cvs/mkmodules/../lib/fnmatch.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+extern int fnmatch (const char *pattern, const char *string, int flags);
+
+
+
+
+
+# 11 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2
+
+# 1 "/usr/src/gnu/cvs/mkmodules/../lib/getopt.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+extern char *optarg;
+
+
+
+
+
+
+
+
+
+
+
+
+
+extern int optind;
+
+
+
+
+extern int opterr;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+struct option
+{
+ char *name;
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+
+extern const struct option *_getopt_long_options;
+
+
+
+
+
+
+
+extern int _getopt_long_only;
+
+
+
+
+
+extern int option_index;
+
+
+int gnu_getopt (int argc, char **argv, const char *shortopts);
+int gnu_getopt_long (int argc, char **argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+int gnu_getopt_long_only (int argc, char **argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+
+
+
+
+
+# 12 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2
+
+# 1 "/usr/src/gnu/cvs/mkmodules/../lib/wait.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 13 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2
+
+# 1 "/usr/src/gnu/cvs/mkmodules/../cvs/config.h" 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+extern void exit ();
+
+
+
+extern char *getwd ();
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# 14 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2
+
+
+# 1 "/usr/src/gnu/cvs/mkmodules/../cvs/myndbm.h" 1
+
+
+
+
+
+
+typedef struct
+{
+ List *dbm_list;
+ Node *dbm_next;
+} DBM;
+
+typedef struct
+{
+ char *dptr;
+ int dsize;
+} datum;
+
+
+
+
+
+
+
+
+
+
+
+
+
+DBM *mydbm_open (char *file, int flags, int mode);
+void mydbm_close (DBM * db);
+datum mydbm_fetch (DBM * db, datum key);
+datum mydbm_firstkey (DBM * db);
+datum mydbm_nextkey (DBM * db);
+
+
+
+
+
+
+
+
+
+# 16 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h" 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+enum mtype
+{
+ CHECKOUT, TAG, PATCH
+};
+
+
+
+
+
+
+enum classify_type
+{
+ T_UNKNOWN = 1,
+ T_CONFLICT,
+ T_NEEDS_MERGE,
+ T_MODIFIED,
+ T_CHECKOUT,
+ T_ADDED,
+ T_REMOVED,
+ T_REMOVE_ENTRY,
+ T_UPTODATE,
+ T_TITLE
+};
+typedef enum classify_type Ctype;
+
+
+
+
+
+
+
+
+struct vers_ts
+{
+ char *vn_user;
+
+
+
+
+
+ char *vn_rcs;
+
+ char *ts_user;
+ char *ts_rcs;
+ char *options;
+
+ char *tag;
+ char *date;
+ Entnode *entdata;
+ RCSNode *srcfile;
+};
+typedef struct vers_ts Vers_TS;
+
+
+
+
+
+struct stickydirtag
+{
+ int aflag;
+ char *tag;
+ char *date;
+ char *options;
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+enum direnter_type
+{
+ R_PROCESS = 1,
+ R_SKIP_FILES,
+ R_SKIP_DIRS,
+ R_SKIP_ALL
+};
+typedef enum direnter_type Dtype;
+
+extern char *program_name, *command_name;
+extern char *Rcsbin, *Editor, *CVSroot;
+extern char *CurDir;
+extern int really_quiet, quiet;
+extern int use_editor;
+extern int cvswrite;
+
+extern int trace;
+extern int noexec;
+extern int logoff;
+
+
+
+int Reader_Lock (char *xrepository);
+DBM *open_module (void);
+FILE *Fopen (char *name, char *mode);
+FILE *open_file (char *name, char *mode);
+List *Find_Dirs (char *repository, int which);
+List *ParseEntries (int aflag);
+char *Make_Date (char *rawdate);
+char *Name_Repository (char *dir, char *update_dir);
+char *Short_Repository (char *repository);
+char *getcaller (void);
+char *time_stamp (char *file);
+char *xmalloc (int bytes);
+char *xrealloc (char *ptr, int bytes);
+char *xstrdup (char *str);
+int No_Difference (char *file, Vers_TS * vers, List * entries);
+int Parse_Info (char *infofile, char *repository, int (*callproc) (), int all);
+int Reader_Lock (char *xrepository);
+int SIG_register (int sig, void (*fn) ());
+int Writer_Lock (List * list);
+int gethostname (char *name, int namelen);
+int ign_name (char *name);
+int isdir (char *file);
+int isfile (char *file);
+int islink (char *file);
+int isreadable (char *file);
+int iswritable (char *file);
+int link_file (char *from, char *to);
+int numdots (char *s);
+int run_exec (char *stin, char *stout, char *sterr, int flags);
+int unlink_file (char *f);
+int update (int argc, char *argv[]);
+int xcmp (char *file1, char *file2);
+int yesno (void);
+time_t get_date (char *date, struct timeb *now);
+void Create_Admin (char *dir, char *repository, char *tag, char *date);
+void Lock_Cleanup (void);
+void ParseTag (char **tagp, char **datep);
+void Scratch_Entry (List * list, char *fname);
+void WriteTag (char *dir, char *tag, char *date);
+void cat_module (int status);
+void check_entries (char *dir);
+void close_module (DBM * db);
+void copy_file (char *from, char *to);
+void error (int status, int errnum, char *message,...);
+void fperror (FILE * fp, int status, int errnum, char *message,...);
+void free_names (int *pargc, char *argv[]);
+void freevers_ts (Vers_TS ** versp);
+void ign_add (char *ign, int hold);
+void ign_add_file (char *file, int hold);
+void ign_setup (void);
+void line2argv (int *pargc, char *argv[], char *line);
+void make_directories (char *name);
+void make_directory (char *name);
+void rename_file (char *from, char *to);
+void run_arg (char *s);
+void run_args (char *fmt,...);
+void run_print (FILE * fp);
+void run_setup (char *fmt,...);
+void strip_path (char *path);
+void update_delproc (Node * p);
+void usage (char **cpp);
+void xchmod (char *fname, int writable);
+int Checkin (int type, char *file, char *repository, char *rcs, char *rev,
+ char *tag, char *message, List * entries);
+Ctype Classify_File (char *file, char *tag, char *date, char *options,
+ int force_tag_match, int aflag, char *repository,
+ List *entries, List *srcfiles, Vers_TS **versp);
+List *Find_Names (char *repository, int which, int aflag,
+ List ** optentries);
+void Register (List * list, char *fname, char *vn, char *ts,
+ char *options, char *tag, char *date);
+void Update_Logfile (char *repository, char *xmessage, char *xrevision,
+ FILE * xlogfp, List * xchanges);
+Vers_TS *Version_TS (char *repository, char *options, char *tag,
+ char *date, char *user, int force_tag_match,
+ int set_time, List * entries, List * xfiles);
+void do_editor (char *dir, char *message, char *repository,
+ List * changes);
+int do_module (DBM * db, char *mname, enum mtype m_type, char *msg,
+ int (*callback_proc) (), char *where, int shorten,
+ int local_specified, int run_module_prog, char *extra_arg);
+int do_recursion (int (*xfileproc) (), int (*xfilesdoneproc) (),
+ Dtype (*xdirentproc) (), int (*xdirleaveproc) (),
+ Dtype xflags, int xwhich, int xaflag, int xreadlock,
+ int xdosrcs);
+int do_update (int argc, char *argv[], char *xoptions, char *xtag,
+ char *xdate, int xforce, int local, int xbuild,
+ int xaflag, int xprune, int xpipeout, int which,
+ char *xjoin_rev1, char *xjoin_rev2, char *preload_update_dir);
+void history_write (int type, char *update_dir, char *revs, char *name,
+ char *repository);
+int start_recursion (int (*fileproc) (), int (*filesdoneproc) (),
+ Dtype (*direntproc) (), int (*dirleaveproc) (),
+ int argc, char *argv[], int local, int which,
+ int aflag, int readlock, char *update_preload,
+ int dosrcs);
+void SIG_beginCrSect ();
+void SIG_endCrSect ();
+# 438 "/usr/src/gnu/cvs/mkmodules/../cvs/cvs.h"
+
+# 15 "/usr/src/gnu/cvs/mkmodules/mkmodules.c" 2
+
+
+
+
+
+
+PATH_MAX +2
+
+
+static char rcsid[] = "@(#)mkmodules.c 1.39 92/03/31";
+
+
+
+
+
+
+char *program_name, *command_name;
+
+char *Rcsbin = "" ;
+int noexec = 0;
+int trace = 0;
+
+
+static int checkout_file (char *file, char *temp);
+static void make_tempfile (char *temp);
+static void mkmodules_usage (void);
+static void rename_rcsfile (char *temp, char *real);
+
+
+
+
+
+
+# 60 "/usr/src/gnu/cvs/mkmodules/mkmodules.c"
+
+
+int
+main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *getenv ();
+ char temp[PATH_MAX +2 ];
+ char *cp;
+
+ DBM *db;
+
+
+
+
+
+ if ((program_name = rindex (argv[0], '/')) == 0 )
+ program_name = argv[0];
+ else
+ program_name++;
+
+ if (argc != 2)
+ mkmodules_usage ();
+
+ if ((cp = getenv ("RCSBIN" )) != 0 )
+ Rcsbin = cp;
+
+
+
+
+
+ if (Rcsbin[0] != '\0')
+ {
+ int len = strlen (Rcsbin);
+ char *rcsbin;
+
+ if (Rcsbin[len - 1] != '/')
+ {
+ rcsbin = Rcsbin;
+ Rcsbin = xmalloc (len + 2);
+ (void) strcpy (Rcsbin, rcsbin);
+ (void) strcat (Rcsbin, "/");
+ }
+ }
+
+ if (chdir (argv[1]) < 0)
+ error (1, errno, "cannot chdir to %s", argv[1]);
+
+
+
+
+ make_tempfile (temp);
+ switch (checkout_file ("modules" , temp))
+ {
+
+ case 0:
+
+
+ if ((db = mydbm_open (temp, 0x0000 , 0666)) != 0 )
+ mydbm_close (db);
+
+
+
+
+ rename_rcsfile (temp, "modules" );
+ break;
+
+ case -1:
+ (void) unlink_file (temp);
+ exit (1);
+
+
+ default:
+ error (0, 0,
+ "'cvs checkout' is less functional without a %s file",
+ "modules" );
+ break;
+ }
+
+ (void) unlink_file (temp);
+
+
+
+
+
+ make_tempfile (temp);
+ if (checkout_file ("loginfo" , temp) == 0)
+ rename_rcsfile (temp, "loginfo" );
+ else
+ error (0, 0,
+ "no logging of 'cvs commit' messages is done without a %s file",
+ "loginfo" );
+ (void) unlink_file (temp);
+
+
+
+
+
+ make_tempfile (temp);
+ if (checkout_file ("rcsinfo" , temp) == 0)
+ rename_rcsfile (temp, "rcsinfo" );
+ else
+ error (0, 0,
+ "a %s file can be used to configure 'cvs commit' templates",
+ "rcsinfo" );
+ (void) unlink_file (temp);
+
+
+
+
+
+ make_tempfile (temp);
+ if (checkout_file ("editinfo" , temp) == 0)
+ rename_rcsfile (temp, "editinfo" );
+ else
+ error (0, 0,
+ "a %s file can be used to validate log messages",
+ "editinfo" );
+ (void) unlink_file (temp);
+
+
+
+
+
+ make_tempfile (temp);
+ if (checkout_file ("commitinfo" , temp) == 0)
+ rename_rcsfile (temp, "commitinfo" );
+ else
+ error (0, 0,
+ "a %s file can be used to configure 'cvs commit' checking",
+ "commitinfo" );
+ (void) unlink_file (temp);
+ return (0);
+}
+
+
+
+
+static void
+make_tempfile (temp)
+ char *temp;
+{
+ static int seed = 0;
+ int fd;
+
+ if (seed == 0)
+ seed = getpid ();
+ while (1)
+ {
+ (void) sprintf (temp, "%s%d", ".#" , seed++);
+ if ((fd = open (temp, 0x0200 | 0x0800 | 0x0002 , 0666)) != -1)
+ break;
+ if (errno != 17 )
+ error (1, errno, "cannot create temporary file %s", temp);
+ }
+ if (close(fd) < 0)
+ error(1, errno, "cannot close temporary file %s", temp);
+}
+
+static int
+checkout_file (file, temp)
+ char *file;
+ char *temp;
+{
+ char rcs[PATH_MAX +2 ];
+ int retcode = 0;
+
+ (void) sprintf (rcs, "%s%s", file, ",v" );
+ if (!isfile (rcs))
+ return (1);
+ run_setup ("%s%s -q -p", Rcsbin, "co" );
+ run_arg (rcs);
+ if ((retcode = run_exec ( (char *)0 , temp, (char *)0 , 0x0000 )) != 0)
+ {
+ error (0, retcode == -1 ? errno : 0, "failed to check out %s file", file);
+ }
+ return (retcode);
+}
+
+# 369 "/usr/src/gnu/cvs/mkmodules/mkmodules.c"
+
+
+static void
+rename_rcsfile (temp, real)
+ char *temp;
+ char *real;
+{
+ char bak[50];
+
+ if (chmod (temp, 0444) < 0)
+ error (0, errno, "warning: cannot chmod %s", temp);
+ (void) sprintf (bak, "%s%s", ".#" , real);
+ (void) unlink_file (bak);
+ (void) rename (real, bak);
+ (void) rename (temp, real);
+}
+
+
+
+
+void
+Lock_Cleanup ()
+{
+}
+
+static void
+mkmodules_usage ()
+{
+ (void) fprintf ((&__sF[2]) , "Usage: %s modules-directory\n", program_name);
+ exit (1);
+}
diff --git a/gnu/usr.bin/dc/COPYING b/gnu/usr.bin/dc/COPYING
new file mode 100644
index 0000000..e77696a
--- /dev/null
+++ b/gnu/usr.bin/dc/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/gnu/usr.bin/dc/ChangeLog b/gnu/usr.bin/dc/ChangeLog
new file mode 100644
index 0000000..09aaf47
--- /dev/null
+++ b/gnu/usr.bin/dc/ChangeLog
@@ -0,0 +1,77 @@
+Fri May 21 15:02:52 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * Version 0.2 released.
+
+Fri May 21 11:48:11 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu)
+
+ * decimal.c (decimal_rem): Update to match fixes in decimal_div.
+
+Thu May 20 03:12:41 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * Makefile.in (realclean): Delete dc.info* and configure.
+ (DISTFILES): Add `texinfo.tex' and `NEWS'.
+ texinfo.tex: New file (symlink to canonical source).
+ NEWS: New file.
+
+Wed May 19 11:30:09 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu)
+
+ * dc.c (dec_read): Accept only A through F.
+
+Tue May 18 12:35:54 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu)
+
+ * dc.c (read_string): New arg STARTC to handle nested brackets.
+ (execute): Change calls to read_string.
+ (condop): Don't assume result of decimal_compare has abs value <= 1.
+ (popmacro): If no macro in progress, exit.
+
+Sun May 2 00:42:47 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu)
+
+ * decimal.c (decimal_div): Include in trial_dividend the digit
+ at length2 + i - 2, if there is one.
+
+Sat May 1 09:54:35 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu)
+
+ * decimal.c (decimal_parse): Don't use digits without recalculation
+ if some digit exceeds the radix.
+
+ * dc.c (execute): Treat A...F as digits.
+ (dec_read): Treat A...F as digits.
+
+Thu Apr 29 14:17:30 1993 Richard Stallman (rms@mole.gnu.ai.mit.edu)
+
+ * decimal.h (bcopy): Use memcpy, not memmove.
+
+ * decimal.c (flush_trailing_digits): Use explicit loop, not bcopy.
+
+Tue Apr 20 17:21:27 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * dc.c (pushsqrt): `precision' is an argument to `decimal_sqrt', not
+ `push'.
+
+Sat Apr 17 15:47:55 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * All files: Updated GPL version number.
+
+ * decimal.c: Include decimal.h and delete duplicate declarations.
+
+ * decimal.h [!HAVE_BCOPY]: #define bcopy.
+ [!HAVE_BZERO]: #define bzero.
+
+Sun Feb 10 22:06:15 1991 Richard Stallman (rms at mole.ai.mit.edu)
+
+ * dc.c (execute): Insert break; in \n case.
+
+Sun Jul 29 17:50:14 1990 Richard Stallman (rms at sugar-bombs.ai.mit.edu)
+
+ * decimal.c (decimal_neg): New function.
+
+Fri Jul 27 04:11:34 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
+
+ * bceval.c, bclex.c, bcprint.c, bcsym.c: Declare some functions
+ static.
+
+Mon Dec 25 03:01:49 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu)
+
+ * Makefile: add some missing rules.
+
+ * decimal.c: change a 'max' to 'MAX'.
diff --git a/gnu/usr.bin/dc/Makefile b/gnu/usr.bin/dc/Makefile
new file mode 100644
index 0000000..937c7d9
--- /dev/null
+++ b/gnu/usr.bin/dc/Makefile
@@ -0,0 +1,7 @@
+PROG= dc
+SRCS= dc.c decimal.c
+CFLAGS+=-I${.CURDIR} -DHAVE_BCOPY=1 -DHAVE_BZERO=1
+DPADD= ${LIBM}
+LDADD= -lm
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/dc/NEWS b/gnu/usr.bin/dc/NEWS
new file mode 100644
index 0000000..6486afb
--- /dev/null
+++ b/gnu/usr.bin/dc/NEWS
@@ -0,0 +1,7 @@
+Changes between version 0.2 and 0.1:
+
+* You can now have nested square bracket pairs within a string.
+
+* The letters A-F can now be part of a number when the input radix is
+large enough to make them meaningful.
+
diff --git a/gnu/usr.bin/dc/README b/gnu/usr.bin/dc/README
new file mode 100644
index 0000000..c23cc66
--- /dev/null
+++ b/gnu/usr.bin/dc/README
@@ -0,0 +1,13 @@
+This is a preliminary release of GNU `dc', since people asked for it. GNU
+`bc' (which doesn't rely on a separate `dc') has been available separately
+for a couple of years. Eventually this version of `dc' will be merged with
+the bc package.
+
+See comments in the file decimal.c for some limitations in the arbitrary
+precision library. It's questionable whether it's worth fixing these
+problems since the merged dc will probably use bc's math library instead.
+However, you might want to be aware of known problems.
+
+See the file `INSTALL' for instructions on building and installing dc.
+
+Please report bugs to bug-gnu-utils@prep.ai.mit.edu.
diff --git a/gnu/usr.bin/dc/dc.1 b/gnu/usr.bin/dc/dc.1
new file mode 100644
index 0000000..17d9356
--- /dev/null
+++ b/gnu/usr.bin/dc/dc.1
@@ -0,0 +1,278 @@
+.TH DC 1 "03 Aug 1993" "GNU Project"
+.SH NAME
+dc, An Arbitrary Precision Calculator
+.SH SYNOPSIS
+.B dc
+.SH DESCRIPTION
+.PP
+DC is a reverse-polish desk calculator which supports unlimited
+precision arithmetic. It also allows you to define and call macros.
+Normally DC reads from the standard input; if any command arguments
+are given to it, they are filenames, and DC reads and executes the
+contents of the files before reading from standard input. All output
+is to standard output.
+
+A reverse-polish calculator stores numbers on a stack. Entering a
+number pushes it on the stack. Arithmetic operations pop arguments off
+the stack and push the results.
+
+To enter a number in DC, type the digits, with an optional decimal
+point. Exponential notation is not supported. To enter a negative
+number, begin the number with `_'. `-' cannot be used for this, as it
+is a binary operator for subtraction instead. To enter two numbers in
+succession, separate them with spaces or newlines. These have no
+meaning as commands.
+.PD
+.SH "Printing Commands"
+.PP
+.B p
+Prints the value on the top of the stack,
+without altering the stack. A newline is printed
+after the value.
+.PP
+.B P
+Prints the value on the top of the stack,
+popping it off, and does not print a newline after.
+.PP
+.B f
+Prints the entire contents of the stack
+and the contents of all of the registers,
+without altering anything. This is a good command
+to use if you are lost or want to figure out
+what the effect of some command has been.
+.PD
+.SH "Arithmetic"
+.PP
+.B +
+Pops two values off the stack, adds them,
+and pushes the result. The precision of the result
+is determined only by the values of the arguments,
+and is enough to be exact.
+.PP
+.B -
+Pops two values, subtracts the first one popped
+from the second one popped, and pushes the result.
+.PP
+.B *
+Pops two values, multiplies them, and pushes the result.
+The number of fraction digits in the result is controlled
+by the current precision flag (see below) and does not
+depend on the values being multiplied.
+.PP
+.B /
+Pops two values, divides the second one popped from
+the first one popped, and pushes the result.
+The number of fraction digits is specified by the precision flag.
+.PP
+.B %
+Pops two values, computes the remainder of the division
+that the \fB/\fR command would do, and pushes that.
+The division is done with as many fraction digits
+as the precision flag specifies, and the remainder
+is also computed with that many fraction digits.
+.PP
+.B ^
+Pops two values and exponentiates, using the first
+value popped as the exponent and the second popped as the base.
+The fraction part of the exponent is ignored.
+The precision flag specifies the number of fraction
+digits in the result.
+.PP
+.B v
+Pops one value, computes its square root, and pushes that.
+The precision flag specifies the number of fraction digits
+in the result.
+.PP
+Most arithmetic operations are affected by the "precision flag",
+which you can set with the
+.BR k
+command. The default precision
+value is zero, which means that all arithmetic except for
+addition and subtraction produces integer results.
+.PP
+The remainder operation
+.BR %
+requires some explanation: applied to
+arguments `a' and `b' it produces `a - (b * (a / b))',
+where `a / b' is computed in the current precision.
+.PP
+.SH "Stack Control"
+.PP
+.B c
+Clears the stack, rendering it empty.
+.PP
+.B d
+Duplicates the value on the top of the stack,
+pushing another copy of it. Thus,
+`4d*p' computes 4 squared and prints it.
+.SH "Registers"
+.PP
+DC provides 128 memory registers, each named by a single
+ASCII character. You can store a number in a register
+and retrieve it later.
+.PP
+.B s\fIr\fR
+Pop the value off the top of the stack and store
+it into register \fIr\fR.
+.PP
+.B l\fIr\fR
+Copy the value in register \fIr\fR and push it onto the stack. This
+does not alter the contents of \fIr\fR.
+.PP
+Each register also contains its own stack. The current
+register value is the top of the register's stack.
+.PP
+.B S\fIr\fR
+Pop the value off the top of the (main) stack and
+push it onto the stack of register \fIr\fR.
+The previous value of the register becomes inaccessible.
+.PP
+.B L\fIr\fR
+Pop the value off the top of register \fIr\fR's stack
+and push it onto the main stack. The previous value
+in register \fIr\fR's stack, if any, is now accessible
+via the
+.BR Ir
+command.
+.PP
+The
+.BR f
+command prints a list of all registers that have contents
+stored in them, together with their contents. Only the
+current contents of each register (the top of its stack)
+is printed.
+.PP
+.SH "Parameters"
+.PP
+DC has three parameters that control its operation: the precision, the
+input radix, and the output radix. The precision specifies the number
+of fraction digits to keep in the result of most arithmetic operations.
+The input radix controls the interpretation of numbers typed in;
+allnumbers typed in use this radix. The output radix is used
+for printing numbers.
+.PP
+The input and output radices are separate parameters; you can make them
+unequal, which can be useful or confusing. Each radix must be between 2
+and 36 inclusive. The precision must be zero or greater. The precision
+is always measured in decimal digits, regardless of the current input or
+output radix.
+.PP
+.B i
+Pops the value off the top of the stack
+and uses it to set the input radix.
+.PP
+.B o
+.PP
+.B k
+Similarly set the output radix and the precision.
+.PP
+.B I
+Pushes the current input radix on the stack.
+.PP
+.B O
+.PP
+.B K
+Similarly push the current output radix and the current precision.
+.PP
+.SH "Strings"
+.PP
+DC can operate on strings as well as on numbers. The only things you
+can do with strings are print them and execute them as macros (which
+means that the contents of the string are processed as DC commands).
+Both registers and the stack can hold strings, and DC always knows
+whether any given object is a string or a number. Some commands such as
+arithmetic operations demand numbers as arguments and print errors if
+given strings. Other commands can accept either a number or a string;
+for example, the
+.BR p
+command can accept either and prints the object
+according to its type.
+.PP
+.B [characters]
+Makes a string containing
+.BR characters
+and pushes it
+on the stack. For example,
+.BR [foo]p
+prints the
+characters \fBfoo\fR (with no newline).
+.PP
+.B x
+Pops a value off the stack and executes it as a macro.
+Normally it should be a string; if it is a number,
+it is simply pushed back onto the stack.
+For example,
+.BR [1p]x
+executes the macro
+.BR 1p
+which pushes \fB1\fR on the stack and prints \fB1\fR
+on a separate line.
+.PP
+Macros are most often stored in registers;
+\fB[1p]sa\fR stores a macro to print \fB1\fR into register \fBa\fR,
+and \fBlax\fR invokes the macro.
+.PP
+.B >\fIr\fR
+Pops two values off the stack and compares them
+assuming they are numbers, executing the contents
+of register \fIr\fR as a macro if the original top-of-stack
+is greater. Thus, \fB1 2>a\fR will invoke register \fBa\fR's contents
+and \fB2 1>a\fR will not.
+.PP
+.B <\fIr\fB
+Similar but invokes the macro if the original top-of-stack
+is less.
+.PP
+.B =\fIr\fR
+Similar but invokes the macro if the two numbers popped
+are equal. This can also be validly used to compare two
+strings for equality.
+.PP
+.B ?
+Reads a line from the terminal and executes it.
+This command allows a macro to request input from the user.
+.PP
+.B q
+During the execution of a macro, this comand
+does not exit DC. Instead, it exits from that
+macro and also from the macro which invoked it (if any).
+.PP
+.B Q
+Pops a value off the stack and uses it as a count
+of levels of macro execution to be exited. Thus,
+\fB3Q\fR exits three levels.
+.SH "Status Inquiry"
+.PP
+.B Z
+Pops a value off the stack, calculates the number of
+digits it has (or number of characters, if it is a string)
+and pushes that number.
+.PP
+.B X
+Pops a value off the stack, calculates the number of
+fraction digits it has, and pushes that number.
+For a string, the value pushed is -1.
+.PP
+.B z
+Pushes the current stack depth; the number of
+objects on the stack before the execution of the \fBz\fR command.
+.PP
+.B I
+Pushes the current value of the input radix.
+.PP
+.B O
+Pushes the current value of the output radix.
+.PP
+.B K
+Pushes the current value of the precision.
+.SH "Notes"
+.PP
+The \fB:\fR and \fB;\fR commands of the Unix DC program are
+not supported, as the documentation does not say what they do.
+The \fB!\fR command is not supported, but will be supported
+as soon as a library for executing a line as a command exists.
+.SH BUGS
+.PP
+Email bug reports to
+.BR bug-gnu-utils@prep.ai.mit.edu .
+Be sure to include the word ``dc'' somewhere in the ``Subject:'' field.
diff --git a/gnu/usr.bin/dc/dc.c b/gnu/usr.bin/dc/dc.c
new file mode 100644
index 0000000..229308f
--- /dev/null
+++ b/gnu/usr.bin/dc/dc.c
@@ -0,0 +1,908 @@
+/*
+ * `dc' desk calculator utility.
+ *
+ * Copyright (C) 1984, 1993 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can either send email to this
+ * program's author (see below) or write to: The Free Software Foundation,
+ * Inc.; 675 Mass Ave. Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include "decimal.h" /* definitions for our decimal arithmetic package */
+
+FILE *open_file; /* input file now open */
+int file_count; /* Number of input files not yet opened */
+char **next_file; /* Pointer to vector of names of input files left */
+
+struct regstack
+ {
+ decimal value; /* Saved value of register */
+ struct regstack *rest; /* Tail of list */
+ };
+
+typedef struct regstack *regstack;
+
+regstack freeregstacks; /* Chain of free regstack structures for fast realloc */
+
+decimal regs[128]; /* "registers", with single-character names */
+regstack regstacks[128]; /* For each register, a stack of previous values */
+
+int stacktop; /* index of last used element in stack */
+int stacksize; /* Current allocates size of stack */
+decimal *stack; /* Pointer to computation stack */
+
+/* A decimal number can be regarded as a string by
+ treating its contents as characters and ignoring the
+ position of its decimal point.
+ Decimal numbers are marked as strings by having an `after' field of -1
+ One use of strings is to execute them as macros.
+*/
+
+#define STRING -1
+
+int macrolevel; /* Current macro nesting; 0 if taking keyboard input */
+int macrostacksize; /* Current allocated size of macrostack and macroindex */
+decimal *macrostack; /* Pointer to macro stack array */
+int *macroindex; /* Pointer to index-within-macro stack array */
+ /* Note that an empty macro is popped from the stack
+ only when an trying to read a character from it
+ or trying to push another macro. */
+
+int ibase; /* Radix for numeric input. */
+int obase; /* Radix for numeric output. */
+int precision; /* Number of digits to keep in multiply and divide. */
+
+char *buffer; /* Address of buffer used for reading numbers */
+int bufsize; /* Current size of buffer (made bigger when nec) */
+
+decimal dec_read ();
+regstack get_regstack ();
+int fetch ();
+int fgetchar ();
+char *concat ();
+void pushsqrt ();
+void condop ();
+void setibase ();
+void setobase ();
+void setprecision ();
+void pushmacro ();
+decimal read_string ();
+void pushlength ();
+void pushscale ();
+void unfetch ();
+void popmacros ();
+void popmacro ();
+void popstack ();
+void print_obj ();
+void print_string ();
+void free_regstack ();
+void pushreg ();
+void execute ();
+void fputchar ();
+void push ();
+void incref ();
+void decref ();
+void binop ();
+
+main (argc, argv, env)
+ int argc;
+ char **argv, **env;
+{
+
+ ibase = 10;
+ obase = 10;
+ precision = 0;
+
+ freeregstacks = 0;
+
+ bzero (regs, sizeof regs);
+ bzero (regstacks, sizeof regstacks);
+
+ bufsize = 40;
+ buffer = (char *) xmalloc (40);
+
+ stacksize = 40;
+ stack = (decimal *) xmalloc (stacksize * sizeof (decimal));
+ stacktop = -1;
+
+ macrostacksize = 40;
+ macrostack = (decimal *) xmalloc (macrostacksize * sizeof (decimal));
+ macroindex = (int *) xmalloc (macrostacksize * sizeof (int));
+ macrolevel = 0;
+ /* Initialize for reading input files if any */
+
+ open_file = 0;
+
+ file_count = argc - 1;
+ next_file = argv + 1;
+
+
+ while (1)
+ {
+ execute ();
+ }
+}
+
+/* Read and execute one command from the current source of input */
+
+void
+execute ()
+{
+ int c = fetch ();
+
+ if (c < 0) exit (0);
+
+ {
+ switch (c)
+ {
+ case '+': /* Arithmetic operators... */
+ binop (decimal_add);
+ break;
+
+ case '-':
+ binop (decimal_sub);
+ break;
+
+ case '*':
+ binop (decimal_mul_dc); /* Like decimal_mul but hairy
+ way of deciding precision to keep */
+ break;
+
+ case '/':
+ binop (decimal_div);
+ break;
+
+ case '%':
+ binop (decimal_rem);
+ break;
+
+ case '^':
+ binop (decimal_expt);
+ break;
+
+ case '_': /* Begin a negative decimal constant */
+ {
+ decimal tem = dec_read (stdin);
+ tem->sign = !tem->sign;
+ push (tem);
+ }
+ break;
+
+ case '.':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': /* All these begin decimal constants */
+ unfetch (c);
+ push (dec_read (stdin));
+ break;
+
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ unfetch (c);
+ push (dec_read (stdin));
+ break;
+
+ case 'c': /* Clear the stack */
+ while (stacktop >= 0)
+ decref (stack[stacktop--]);
+ break;
+
+ case 'd': /* Duplicate top of stack */
+ if (stacktop < 0)
+ error ("stack empty", 0);
+ else push (stack[stacktop]);
+ break;
+
+ case 'f': /* Describe all registers and stack contents */
+ {
+ int regno;
+ int somereg = 0; /* set to 1 if we print any registers */
+ for (regno = 0; regno < 128; regno++)
+ {
+ if (regs[regno])
+ {
+ printf ("register %c: ", regno);
+ print_obj (regs[regno]);
+ somereg = 1;
+ printf ("\n");
+ }
+ }
+ if (somereg)
+ printf ("\n");
+ if (stacktop < 0)
+ printf ("stack empty\n");
+ else
+ {
+ int i;
+ printf ("stack:\n");
+ for (i = 0; i <= stacktop; i++)
+ {
+ print_obj (stack[stacktop - i]);
+ printf ("\n");
+ }
+ }
+ }
+ break;
+
+ case 'i': /* ibase <- top of stack */
+ popstack (setibase);
+ break;
+
+ case 'I': /* Push current ibase */
+ push (decimal_from_int (ibase));
+ break;
+
+ case 'k': /* like i, I but for precision instead of ibase */
+ popstack (setprecision);
+ break;
+
+ case 'K':
+ push (decimal_from_int (precision));
+ break;
+
+ case 'l': /* l<x> load register <x> onto stack */
+ {
+ char c1 = fetch ();
+ if (c1 < 0) exit (0);
+ if (!regs[c1])
+ error ("register %c empty", c1);
+ else
+ push (regs[c1]);
+ }
+ break;
+
+ case 'L': /* L<x> load register <x> to stack, pop <x>'s own stack */
+ {
+ char c1 = fetch ();
+ if (c1 < 0) exit (0);
+ if (!regstacks[c1])
+ error ("nothing pushed on register %c", c1);
+ else
+ {
+ regstack r = regstacks[c1];
+ if (!regs[c1])
+ error ("register %c empty after pop", c1);
+ else
+ push (regs[c1]);
+ regs[c1] = r->value;
+ regstacks[c1] = r->rest;
+ free_regstack (r);
+ }
+ }
+ break;
+
+ case 'o': /* o, O like i, I but for obase instead of ibase */
+ popstack (setobase);
+ break;
+
+ case 'O':
+ push (decimal_from_int (obase));
+ break;
+
+ case 'p': /* Print tos, don't pop, do print newline afterward */
+ if (stacktop < 0)
+ error ("stack empty", 0);
+ else
+ {
+ print_obj (stack[stacktop]);
+ printf ("\n");
+ }
+ break;
+
+ case 'P': /* Print tos, do pop, no newline afterward */
+ popstack (print_obj);
+ break;
+
+ case 'q': /* Exit */
+ if (macrolevel)
+ { popmacro (); popmacro (); } /* decrease recursion level by 2 */
+ else
+ exit (0); /* If not in a macro, exit the program. */
+
+ break;
+
+ case 'Q': /* Tos says how many levels to exit */
+ popstack (popmacros);
+ break;
+
+ case 's': /* s<x> -- Pop stack and set register <x> */
+ if (stacktop < 0)
+ empty ();
+ else
+ {
+ int c1 = fetch ();
+ if (c1 < 0) exit (0);
+ if (regs[c1]) decref (regs[c1]);
+ regs[c1] = stack[stacktop--];
+ }
+ break;
+
+ case 'S': /* S<x> -- pop stack and push as new value of register <x> */
+ if (stacktop < 0)
+ empty ();
+ else
+ {
+ int c1 = fetch ();
+ if (c1 < 0) exit (0);
+ pushreg (c1);
+ regs[c1] = stack[stacktop--];
+ }
+ break;
+
+ case 'v': /* tos gets square root of tos */
+ popstack (pushsqrt);
+ break;
+
+ case 'x': /* pop stack , call as macro */
+ popstack (pushmacro);
+ break;
+
+ case 'X': /* Pop stack, get # fraction digits, push that */
+ popstack (pushscale);
+ break;
+
+ case 'z': /* Compute depth of stack, push that */
+ push (decimal_from_int (stacktop + 1));
+ break;
+
+ case 'Z': /* Pop stack, get # digits, push that */
+ popstack (pushlength);
+ break;
+
+ case '<': /* Conditional: pop two numbers, compare, maybe execute register */
+ /* Note: for no obvious reason, the standard Unix `dc'
+ considers < to be true if the top of stack is less
+ than the next-to-top of stack,
+ and vice versa for >.
+ This seems backwards to me, but I am preserving compatibility. */
+ condop (1);
+ break;
+
+ case '>':
+ condop (-1);
+ break;
+
+ case '=':
+ condop (0);
+ break;
+
+ case '?': /* Read expression from terminal and execute it */
+ /* First ignore any leading newlines */
+ {
+ int c1;
+ while ((c1 = getchar ()) == '\n');
+ ungetc (c1, stdin);
+ }
+ /* Read a line from the terminal and execute it. */
+ pushmacro (read_string ('\n', fgetchar, 0));
+ break;
+
+ case '[': /* Begin string constant */
+ push (read_string (']', fetch, '['));
+ break;
+
+ case ' ':
+ case '\n':
+ break;
+
+ default:
+ error ("undefined command %c", c);
+ }
+ }
+}
+
+/* Functionals for performing arithmetic, etc */
+
+/* Call the function `op', with the top of stack value as argument,
+ and then pop the stack.
+ If the stack is empty, print a message and do not call `op'. */
+
+void
+popstack (op)
+ void (*op) ();
+{
+ if (stacktop < 0)
+ empty ();
+ else
+ {
+ decimal value = stack[stacktop--];
+ op (value);
+ decref (value);
+ }
+}
+
+/* Call the function `op' with two arguments taken from the stack top,
+ then pop those arguments and push the value returned by `op'.
+ `op' is assumed to return a decimal number.
+ If there are not two values on the stack, print a message
+ and do not call `op'. */
+
+void
+binop (op)
+ decimal (*op) ();
+{
+ if (stacktop < 1)
+ error ("stack empty", 0);
+ else if (stack[stacktop]->after == STRING || stack[stacktop - 1]->after == STRING)
+ error ("operands not both numeric");
+ else
+ {
+ decimal arg2 = stack [stacktop--];
+ decimal arg1 = stack [stacktop--];
+
+ push (op (arg1, arg2, precision));
+
+ decref (arg1);
+ decref (arg2);
+ }
+}
+
+void
+condop (cond)
+ int cond;
+{
+ int regno = fetch ();
+ if (!regs[regno])
+ error ("register %c is empty", regno);
+ else if (stacktop < 1)
+ empty ();
+ else
+ {
+ decimal arg2 = stack[stacktop--];
+ decimal arg1 = stack[stacktop--];
+ int relation = decimal_compare (arg1, arg2);
+ decref (arg1);
+ decref (arg2);
+ if (cond == relation
+ || (cond < 0 && relation < 0)
+ || (cond > 0 && relation > 0))
+ pushmacro (regs[regno]);
+ }
+}
+
+/* Handle the command input source */
+
+/* Fetch the next command character from a macro or from the terminal */
+
+int
+fetch()
+{
+ int c = -1;
+
+ while (macrolevel &&
+ LENGTH (macrostack[macrolevel-1]) == macroindex[macrolevel-1])
+ popmacro();
+ if (macrolevel)
+ return macrostack[macrolevel - 1]->contents[macroindex[macrolevel-1]++];
+ while (1)
+ {
+ if (open_file)
+ {
+ c = getc (open_file);
+ if (c >= 0) break;
+ fclose (open_file);
+ open_file = 0;
+ }
+ else if (file_count)
+ {
+ open_file = fopen (*next_file++, "r");
+ file_count--;
+ if (!open_file)
+ perror_with_name (*(next_file - 1));
+ }
+ else break;
+ }
+ if (c >= 0) return c;
+ return getc (stdin);
+}
+
+/* Unread character c on command input stream, whatever it is */
+
+void
+unfetch (c)
+ char c;
+{
+ if (macrolevel)
+ macroindex[macrolevel-1]--;
+ else if (open_file)
+ ungetc (c, open_file);
+ else
+ ungetc (c, stdin);
+}
+
+/* Begin execution of macro m. */
+
+void
+pushmacro (m)
+ decimal m;
+{
+ while (macrolevel &&
+ LENGTH (macrostack[macrolevel-1]) == macroindex[macrolevel-1])
+ popmacro();
+ if (m->after == STRING)
+ {
+ if (macrolevel == macrostacksize)
+ {
+ macrostacksize *= 2;
+ macrostack = (decimal *) xrealloc (macrostack, macrostacksize * sizeof (decimal));
+ macroindex = (int *) xrealloc (macroindex, macrostacksize * sizeof (int));
+ }
+ macroindex[macrolevel] = 0;
+ macrostack[macrolevel++] = m;
+ incref (m);
+ }
+ else
+ { /* Number supplied as a macro! */
+ push (m); /* Its effect wouyld be to push the number. */
+ }
+}
+
+/* Pop a specified number of levels of macro execution.
+ The number of levels is specified by a decimal number d. */
+
+void
+popmacros (d)
+ decimal d;
+{
+ int num_pops = decimal_to_int (d);
+ int i;
+ for (i = 0; i < num_pops; i++)
+ popmacro ();
+}
+/* Exit one level of macro execution. */
+
+void
+popmacro ()
+{
+ if (!macrolevel)
+ exit (0);
+ else
+ {
+ decref (macrostack[--macrolevel]);
+ }
+}
+
+void
+push (d)
+ decimal d;
+{
+ if (stacktop == stacksize - 1)
+ stack = (decimal *) xrealloc (stack, (stacksize *= 2) * sizeof (decimal));
+
+ incref (d);
+
+ stack[++stacktop] = d;
+}
+
+/* Reference counting and storage freeing */
+
+void
+decref (d)
+ decimal d;
+{
+ if (!--d->refcnt)
+ free (d);
+}
+
+void
+incref (d)
+ decimal d;
+{
+ d->refcnt++;
+}
+
+empty ()
+{
+ error ("stack empty", 0);
+}
+
+regstack
+get_regstack ()
+{
+ if (freeregstacks)
+ {
+ regstack r = freeregstacks;
+ freeregstacks = r ->rest;
+ return r;
+ }
+ else
+ return (regstack) xmalloc (sizeof (struct regstack));
+}
+
+void
+free_regstack (r)
+ regstack r;
+{
+ r->rest = freeregstacks;
+ freeregstacks = r;
+}
+
+void
+pushreg (c)
+ char c;
+{
+ regstack r = get_regstack ();
+
+ r->rest = regstacks[c];
+ r->value = regs[c];
+ regstacks[c] = r;
+ regs[c] = 0;
+}
+
+/* Input of numbers and strings */
+
+/* Return a character read from the terminal. */
+
+fgetchar ()
+{
+ return getchar ();
+}
+
+void
+fputchar (c)
+ char (c);
+{
+ putchar (c);
+}
+
+/* Read text from command input source up to a close-bracket,
+ make a string out of it, and return it.
+ If STARTC is nonzero, then it and STOPC must balance when nested. */
+
+decimal
+read_string (stopc, inputfn, startc)
+ char stopc;
+ int (*inputfn) ();
+ int startc;
+{
+ int c;
+ decimal result;
+ int i = 0;
+ int count = 0;
+
+ while (1)
+ {
+ c = inputfn ();
+ if (c < 0 || (c == stopc && count == 0))
+ {
+ if (count != 0)
+ error ("Unmatched `%c'", startc);
+ break;
+ }
+ if (c == stopc)
+ count--;
+ if (c == startc)
+ count++;
+ if (i + 1 >= bufsize)
+ buffer = (char *) xrealloc (buffer, bufsize *= 2);
+ buffer[i++] = c;
+ }
+ result = make_decimal (i, 0);
+ result->after = -1; /* Mark it as a string */
+ result->before++; /* but keep the length unchanged */
+ bcopy (buffer, result->contents, i);
+ return result;
+}
+
+/* Read a number from the current input source */
+
+decimal
+dec_read ()
+{
+ int c;
+ int i = 0;
+
+ while (1)
+ {
+ c = fetch ();
+ if (! ((c >= '0' && c <= '9')
+ || (c >= 'A' && c <= 'F')
+ || c == '.'))
+ break;
+ if (i + 1 >= bufsize)
+ buffer = (char *) xrealloc (buffer, bufsize *= 2);
+ buffer[i++] = c;
+ }
+ buffer[i++] = 0;
+ unfetch (c);
+
+ return decimal_parse (buffer, ibase);
+}
+
+/* Output of numbers and strings */
+
+/* Print the contents of obj, either numerically or as a string,
+ according to what obj says it is. */
+
+void
+print_obj (obj)
+ decimal obj;
+{
+ if (obj->after == STRING)
+ print_string (obj);
+ else
+ decimal_print (obj, fputchar, obase);
+}
+
+/* Print the contents of the decimal number `string', treated as a string. */
+
+void
+print_string (string)
+ decimal string;
+{
+ char *p = string->contents;
+ int len = LENGTH (string);
+ int i;
+
+ for (i = 0; i < len; i++)
+ {
+ putchar (*p++);
+ }
+}
+
+/* Set the input radix from the value of the decimal number d, if valid. */
+
+void
+setibase (d)
+ decimal d;
+{
+ int value = decimal_to_int (d);
+ if (value < 2 || value > 36)
+ error ("input radix must be from 2 to 36", 0);
+ else
+ ibase = value;
+}
+
+/* Set the output radix from the value of the decimal number d, if valid. */
+
+void
+setobase (d)
+ decimal d;
+{
+ int value = decimal_to_int (d);
+ if (value < 2 || value > 36)
+ error ("output radix must be from 2 to 36", 0);
+ else
+ obase = value;
+}
+
+/* Set the precision for mul and div from the value of the decimal number d, if valid. */
+
+void
+setprecision (d)
+ decimal d;
+{
+ int value = decimal_to_int (d);
+ if (value < 0 || value > 30000)
+ error ("precision must be nonnegative and < 30000", 0);
+ else
+ precision = value;
+}
+
+/* Push the number of digits in decimal number d, as a decimal number. */
+
+void
+pushlength (d)
+ decimal d;
+{
+ push (decimal_from_int (LENGTH (d)));
+}
+
+/* Push the number of fraction digits in d. */
+
+void
+pushscale (d)
+ decimal d;
+{
+ push (decimal_from_int (d->after));
+}
+
+/* Push the square root of decimal number d. */
+
+void
+pushsqrt (d)
+ decimal d;
+{
+ push (decimal_sqrt (d, precision));
+}
+
+/* Print error message and exit. */
+
+fatal (s1, s2)
+ char *s1, *s2;
+{
+ error (s1, s2);
+ exit (1);
+}
+
+/* Print error message. `s1' is printf control string, `s2' is arg for it. */
+
+error (s1, s2)
+ char *s1, *s2;
+{
+ printf ("dc: ");
+ printf (s1, s2);
+ printf ("\n");
+}
+
+decimal_error (s1, s2)
+ char *s1, *s2;
+{
+ error (s1, s2);
+}
+
+perror_with_name (name)
+ char *name;
+{
+ extern int errno, sys_nerr;
+ char *s;
+
+ if (errno < sys_nerr)
+ s = concat ("", sys_errlist[errno], " for %s");
+ else
+ s = "cannot open %s";
+ error (s, name);
+}
+
+/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
+
+char *
+concat (s1, s2, s3)
+ char *s1, *s2, *s3;
+{
+ int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
+ char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
+
+ strcpy (result, s1);
+ strcpy (result + len1, s2);
+ strcpy (result + len1 + len2, s3);
+ *(result + len1 + len2 + len3) = 0;
+
+ return result;
+}
+
+/* Like malloc but get fatal error if memory is exhausted. */
+
+int
+xmalloc (size)
+ int size;
+{
+ int result = malloc (size);
+ if (!result)
+ fatal ("virtual memory exhausted", 0);
+ return result;
+}
+
+int
+xrealloc (ptr, size)
+ char *ptr;
+ int size;
+{
+ int result = realloc (ptr, size);
+ if (!result)
+ fatal ("virtual memory exhausted");
+ return result;
+}
diff --git a/gnu/usr.bin/dc/dc.info b/gnu/usr.bin/dc/dc.info
new file mode 100644
index 0000000..a30fea9
--- /dev/null
+++ b/gnu/usr.bin/dc/dc.info
@@ -0,0 +1,330 @@
+This is Info file dc.info, produced by Makeinfo-1.52 from the input
+file dc.texinfo.
+
+ This file documents DC, an arbitrary precision calculator.
+
+ Published by the Free Software Foundation, 675 Massachusetts Avenue,
+Cambridge, MA 02139 USA
+
+ Copyright (C) 1984 Free Software Foundation, Inc.
+
+ Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of
+this manual under the conditions for verbatim copying, provided that
+the entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+ Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be stated in a
+translation approved by the Foundation.
+
+
+File: dc.info, Node: Top, Next: Introduction, Prev: (dir), Up: (dir)
+
+* Menu:
+
+* Introduction:: Introduction
+* Printing Commands:: Printing Commands
+* Arithmetic:: Arithmetic
+* Stack Control:: Stack Control
+* Registers:: Registers
+* Parameters:: Parameters
+* Strings:: Strings
+* Status Inquiry:: Status Inquiry
+* Notes:: Notes
+
+
+File: dc.info, Node: Introduction, Next: Printing Commands, Prev: Top, Up: Top
+
+Introduction
+************
+
+ DC is a reverse-polish desk calculator which supports unlimited
+precision arithmetic. It also allows you to define and call macros.
+Normally DC reads from the standard input; if any command arguments are
+given to it, they are filenames, and DC reads and executes the contents
+of the files before reading from standard input. All output is to
+standard output.
+
+ To exit, use `q'. `C-c' does not exit; it is used to abort macros
+that are looping, etc. (Currently this is not true; `C-c' does exit.)
+
+ A reverse-polish calculator stores numbers on a stack. Entering a
+number pushes it on the stack. Arithmetic operations pop arguments off
+the stack and push the results.
+
+ To enter a number in DC, type the digits, with an optional decimal
+point. Exponential notation is not supported. To enter a negative
+number, begin the number with `_'. `-' cannot be used for this, as it
+is a binary operator for subtraction instead. To enter two numbers in
+succession, separate them with spaces or newlines. These have no
+meaning as commands.
+
+
+File: dc.info, Node: Printing Commands, Next: Arithmetic, Prev: Introduction, Up: Top
+
+Printing Commands
+*****************
+
+`p'
+ Prints the value on the top of the stack, without altering the
+ stack. A newline is printed after the value.
+
+`P'
+ Prints the value on the top of the stack, popping it off, and does
+ not print a newline after.
+
+`f'
+ Prints the entire contents of the stack and the contents of all of
+ the registers, without altering anything. This is a good command
+ to use if you are lost or want to figure out what the effect of
+ some command has been.
+
+
+File: dc.info, Node: Arithmetic, Next: Stack Control, Prev: Printing Commands, Up: Top
+
+Arithmetic
+**********
+
+`+'
+ Pops two values off the stack, adds them, and pushes the result.
+ The precision of the result is determined only by the values of
+ the arguments, and is enough to be exact.
+
+`-'
+ Pops two values, subtracts the first one popped from the second
+ one popped, and pushes the result.
+
+`*'
+ Pops two values, multiplies them, and pushes the result. The
+ number of fraction digits in the result is controlled by the
+ current precision flag (see below) and does not depend on the
+ values being multiplied.
+
+`/'
+ Pops two values, divides the second one popped from the first one
+ popped, and pushes the result. The number of fraction digits is
+ specified by the precision flag.
+
+`%'
+ Pops two values, computes the remainder of the division that the
+ `/' command would do, and pushes that. The division is done with
+ as many fraction digits as the precision flag specifies, and the
+ remainder is also computed with that many fraction digits.
+
+`^'
+ Pops two values and exponentiates, using the first value popped as
+ the exponent and the second popped as the base. The fraction part
+ of the exponent is ignored. The precision flag specifies the
+ number of fraction digits in the result.
+
+`v'
+ Pops one value, computes its square root, and pushes that. The
+ precision flag specifies the number of fraction digits in the
+ result.
+
+ Most arithmetic operations are affected by the "precision flag",
+which you can set with the `k' command. The default precision value is
+zero, which means that all arithmetic except for addition and
+subtraction produces integer results.
+
+ The remainder operation (`%') requires some explanation: applied to
+arguments `a' and `b' it produces `a - (b * (a / b))', where `a / b' is
+computed in the current precision.
+
+
+File: dc.info, Node: Stack Control, Next: Registers, Prev: Arithmetic, Up: Top
+
+Stack Control
+*************
+
+`c'
+ Clears the stack, rendering it empty.
+
+`d'
+ Duplicates the value on the top of the stack, pushing another copy
+ of it. Thus, `4d*p' computes 4 squared and prints it.
+
+
+File: dc.info, Node: Registers, Next: Parameters, Prev: Stack Control, Up: Top
+
+Registers
+*********
+
+ DC provides 128 memory registers, each named by a single ASCII
+character. You can store a number in a register and retrieve it later.
+
+`sR'
+ Pop the value off the top of the stack and store it into register
+ R.
+
+`lR'
+ Copy the value in register R, and push it onto the stack. This
+ does not alter the contents of R.
+
+ Each register also contains its own stack. The current register
+ value is the top of the register's stack.
+
+`SR'
+ Pop the value off the top of the (main) stack and push it onto the
+ stack of register R. The previous value of the register becomes
+ inaccessible.
+
+`LR'
+ Pop the value off the top of register R's stack and push it onto
+ the main stack. The previous value in register R's stack, if any,
+ is now accessible via the `lR' command.
+
+ The `f' command prints a list of all registers that have contents
+stored in them, together with their contents. Only the current
+contents of each register (the top of its stack) is printed.
+
+
+File: dc.info, Node: Parameters, Next: Strings, Prev: Registers, Up: Top
+
+Parameters
+**********
+
+ DC has three parameters that control its operation: the precision,
+the input radix, and the output radix. The precision specifies the
+number of fraction digits to keep in the result of most arithmetic
+operations. The input radix controls the interpretation of numbers
+typed in; *all* numbers typed in use this radix. The output radix is
+used for printing numbers.
+
+ The input and output radices are separate parameters; you can make
+them unequal, which can be useful or confusing. Each radix must be
+between 2 and 36 inclusive. The precision must be zero or greater.
+The precision is always measured in decimal digits, regardless of the
+current input or output radix.
+
+`i'
+ Pops the value off the top of the stack and uses it to set the
+ input radix.
+
+`o'
+`k'
+ Similarly set the output radix and the precision.
+
+`I'
+ Pushes the current input radix on the stack.
+
+`O'
+`K'
+ Similarly push the current output radix and the current precision.
+
+
+File: dc.info, Node: Strings, Next: Status Inquiry, Prev: Parameters, Up: Top
+
+Strings
+*******
+
+ DC can operate on strings as well as on numbers. The only things you
+can do with strings are print them and execute them as macros (which
+means that the contents of the string are processed as DC commands).
+Both registers and the stack can hold strings, and DC always knows
+whether any given object is a string or a number. Some commands such as
+arithmetic operations demand numbers as arguments and print errors if
+given strings. Other commands can accept either a number or a string;
+for example, the `p' command can accept either and prints the object
+according to its type.
+
+`[CHARACTERS]'
+ Makes a string containing CHARACTERS and pushes it on the stack.
+ For example, `[foo]P' prints the characters `foo' (with no
+ newline).
+
+`x'
+ Pops a value off the stack and executes it as a macro. Normally
+ it should be a string; if it is a number, it is simply pushed back
+ onto the stack. For example, `[1p]x' executes the macro `1p',
+ which pushes 1 on the stack and prints `1' on a separate line.
+
+ Macros are most often stored in registers; `[1p]sa' stores a macro
+ to print `1' into register `a', and `lax' invokes the macro.
+
+`>R'
+ Pops two values off the stack and compares them assuming they are
+ numbers, executing the contents of register R as a macro if the
+ original top-of-stack is greater. Thus, `1 2>a' will invoke
+ register `a''s contents and `2 1>a' will not.
+
+`<R'
+ Similar but invokes the macro if the original top-of-stack is less.
+
+`=R'
+ Similar but invokes the macro if the two numbers popped are equal.
+ This can also be validly used to compare two strings for equality.
+
+`?'
+ Reads a line from the terminal and executes it. This command
+ allows a macro to request input from the user.
+
+`q'
+ During the execution of a macro, this comand does not exit DC.
+ Instead, it exits from that macro and also from the macro which
+ invoked it (if any).
+
+`Q'
+ Pops a value off the stack and uses it as a count of levels of
+ macro execution to be exited. Thus, `3Q' exits three levels.
+
+
+File: dc.info, Node: Status Inquiry, Next: Notes, Prev: Strings, Up: Top
+
+Status Inquiry
+**************
+
+`Z'
+ Pops a value off the stack, calculates the number of digits it has
+ (or number of characters, if it is a string) and pushes that
+ number.
+
+`X'
+ Pops a value off the stack, calculates the number of fraction
+ digits it has, and pushes that number. For a string, the value
+ pushed is -1.
+
+`z'
+ Pushes the current stack depth; the number of objects on the stack
+ before the execution of the `z' command.
+
+`I'
+ Pushes the current value of the input radix.
+
+`O'
+ Pushes the current value of the output radix.
+
+`K'
+ Pushes the current value of the precision.
+
+
+File: dc.info, Node: Notes, Prev: Status Inquiry, Up: Top
+
+Notes
+*****
+
+ The `:' and `;' commands of the Unix DC program are not supported,
+as the documentation does not say what they do. The `!' command is not
+supported, but will be supported as soon as a library for executing a
+line as a command exists.
+
+
+
+Tag Table:
+Node: Top960
+Node: Introduction1440
+Node: Printing Commands2603
+Node: Arithmetic3211
+Node: Stack Control5168
+Node: Registers5468
+Node: Parameters6586
+Node: Strings7659
+Node: Status Inquiry9857
+Node: Notes10571
+
+End Tag Table
diff --git a/gnu/usr.bin/dc/dc.texinfo b/gnu/usr.bin/dc/dc.texinfo
new file mode 100644
index 0000000..15b285f
--- /dev/null
+++ b/gnu/usr.bin/dc/dc.texinfo
@@ -0,0 +1,381 @@
+\input texinfo @c -*-texinfo-*-
+@c %**start of header
+@setfilename dc.info
+@settitle DC, An Arbitrary Precision Calculator
+@c %**end of header
+
+@c This file has the new style title page commands.
+@c Run `makeinfo' rather than `texinfo-format-buffer'.
+
+@c smallbook
+
+@c tex
+@c \overfullrule=0pt
+@c end tex
+
+@c Combine indices.
+@synindex cp fn
+@syncodeindex vr fn
+@syncodeindex ky fn
+@syncodeindex pg fn
+@syncodeindex tp fn
+
+@ifinfo
+This file documents DC, an arbitrary precision calculator.
+
+Published by the Free Software Foundation,
+675 Massachusetts Avenue,
+Cambridge, MA 02139 USA
+
+Copyright (C) 1984 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+@ignore
+Permission is granted to process this file through TeX and print the
+results, provided the printed document carries copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+@end ignore
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the entire
+resulting derived work is distributed under the terms of a permission
+notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation approved
+by the Foundation.
+@end ifinfo
+
+@setchapternewpage odd
+
+@titlepage
+@title DC, An Arbitrary Precision Calculator
+
+@author by Richard Stallman
+@page
+@vskip 0pt plus 1filll
+Copyright @copyright{} 1984 Free Software Foundation, Inc.
+
+@sp 2
+Published by the Free Software Foundation, @*
+675 Massachusetts Avenue, @*
+Cambridge, MA 02139 USA
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the entire
+resulting derived work is distributed under the terms of a permission
+notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation approved
+by the Foundation.
+
+@end titlepage
+@page
+
+@node Top, Introduction, (dir), (dir)
+
+@menu
+* Introduction:: Introduction
+* Printing Commands:: Printing Commands
+* Arithmetic:: Arithmetic
+* Stack Control:: Stack Control
+* Registers:: Registers
+* Parameters:: Parameters
+* Strings:: Strings
+* Status Inquiry:: Status Inquiry
+* Notes:: Notes
+@end menu
+
+@node Introduction, Printing Commands, Top, Top
+@comment node-name, next, previous, up
+@chapter Introduction
+
+DC is a reverse-polish desk calculator which supports unlimited
+precision arithmetic. It also allows you to define and call macros.
+Normally DC reads from the standard input; if any command arguments
+are given to it, they are filenames, and DC reads and executes the
+contents of the files before reading from standard input. All output
+is to standard output.
+
+To exit, use @samp{q}. @kbd{C-c} does not exit; it is used to abort
+macros that are looping, etc. (Currently this is not true; @kbd{C-c}
+does exit.)
+
+A reverse-polish calculator stores numbers on a stack. Entering a
+number pushes it on the stack. Arithmetic operations pop arguments off
+the stack and push the results.
+
+To enter a number in DC, type the digits, with an optional decimal
+point. Exponential notation is not supported. To enter a negative
+number, begin the number with @samp{_}. @samp{-} cannot be used for
+this, as it is a binary operator for subtraction instead.
+To enter two numbers in succession, separate them with spaces or
+newlines. These have no meaning as commands.
+
+@node Printing Commands, Arithmetic, Introduction, Top
+@chapter Printing Commands
+
+@table @samp
+@item p
+Prints the value on the top of the stack,
+without altering the stack. A newline is printed
+after the value.
+
+@item P
+Prints the value on the top of the stack,
+popping it off, and does not print a newline after.
+
+@item f
+Prints the entire contents of the stack
+and the contents of all of the registers,
+without altering anything. This is a good command
+to use if you are lost or want to figure out
+what the effect of some command has been.
+@end table
+
+@node Arithmetic, Stack Control, Printing Commands, Top
+@chapter Arithmetic
+
+@table @samp
+@item +
+Pops two values off the stack, adds them,
+and pushes the result. The precision of the result
+is determined only by the values of the arguments,
+and is enough to be exact.
+
+@item -
+Pops two values, subtracts the first one popped
+from the second one popped, and pushes the result.
+
+@item *
+Pops two values, multiplies them, and pushes the result.
+The number of fraction digits in the result is controlled
+by the current precision flag (see below) and does not
+depend on the values being multiplied.
+
+@item /
+Pops two values, divides the second one popped from
+the first one popped, and pushes the result.
+The number of fraction digits is specified by the precision flag.
+
+@item %
+Pops two values, computes the remainder of the division
+that the @samp{/} command would do, and pushes that.
+The division is done with as many fraction digits
+as the precision flag specifies, and the remainder
+is also computed with that many fraction digits.
+
+@item ^
+Pops two values and exponentiates, using the first
+value popped as the exponent and the second popped as the base.
+The fraction part of the exponent is ignored.
+The precision flag specifies the number of fraction
+digits in the result.
+
+@item v
+Pops one value, computes its square root, and pushes that.
+The precision flag specifies the number of fraction digits
+in the result.
+@end table
+
+Most arithmetic operations are affected by the "precision flag",
+which you can set with the @samp{k} command. The default precision
+value is zero, which means that all arithmetic except for
+addition and subtraction produces integer results.
+
+The remainder operation (@samp{%}) requires some explanation: applied to
+arguments @samp{a} and @samp{b} it produces @samp{a - (b * (a / b))},
+where @samp{a / b} is computed in the current precision.
+
+@node Stack Control, Registers, Arithmetic, Top
+@chapter Stack Control
+
+@table @samp
+@item c
+Clears the stack, rendering it empty.
+
+@item d
+Duplicates the value on the top of the stack,
+pushing another copy of it. Thus,
+`4d*p' computes 4 squared and prints it.
+@end table
+
+@node Registers, Parameters, Stack Control, Top
+@chapter Registers
+
+DC provides 128 memory registers, each named by a single
+ASCII character. You can store a number in a register
+and retrieve it later.
+
+@table @samp
+@item s@var{r}
+Pop the value off the top of the stack and store
+it into register @var{r}.
+
+@item l@var{r}
+Copy the value in register @var{r}, and push it onto
+the stack. This does not alter the contents of @var{r}.
+
+Each register also contains its own stack. The current
+register value is the top of the register's stack.
+
+@item S@var{r}
+Pop the value off the top of the (main) stack and
+push it onto the stack of register @var{r}.
+The previous value of the register becomes inaccessible.
+
+@item L@var{r}
+Pop the value off the top of register @var{r}'s stack
+and push it onto the main stack. The previous value
+in register @var{r}'s stack, if any, is now accessible
+via the `l@var{r}' command.
+@end table
+
+The @samp{f} command prints a list of all registers that have contents
+stored in them, together with their contents. Only the
+current contents of each register (the top of its stack)
+is printed.
+
+@node Parameters, Strings, Registers, Top
+@chapter Parameters
+
+DC has three parameters that control its operation: the precision, the
+input radix, and the output radix. The precision specifies the number
+of fraction digits to keep in the result of most arithmetic operations.
+The input radix controls the interpretation of numbers typed in;
+@emph{all} numbers typed in use this radix. The output radix is used
+for printing numbers.
+
+The input and output radices are separate parameters; you can make them
+unequal, which can be useful or confusing. Each radix must be between 2
+and 36 inclusive. The precision must be zero or greater. The precision
+is always measured in decimal digits, regardless of the current input or
+output radix.
+
+@table @samp
+@item i
+Pops the value off the top of the stack
+and uses it to set the input radix.
+
+@item o
+@itemx k
+Similarly set the output radix and the precision.
+
+@item I
+Pushes the current input radix on the stack.
+
+@item O
+@itemx K
+Similarly push the current output radix and the current precision.
+@end table
+
+@node Strings, Status Inquiry, Parameters, Top
+@chapter Strings
+
+DC can operate on strings as well as on numbers. The only things you
+can do with strings are print them and execute them as macros (which
+means that the contents of the string are processed as DC commands).
+Both registers and the stack can hold strings, and DC always knows
+whether any given object is a string or a number. Some commands such as
+arithmetic operations demand numbers as arguments and print errors if
+given strings. Other commands can accept either a number or a string;
+for example, the @samp{p} command can accept either and prints the object
+according to its type.
+
+@table @samp
+@item [@var{characters}]
+Makes a string containing @var{characters} and pushes it
+on the stack. For example, @samp{[foo]P} prints the
+characters @samp{foo} (with no newline).
+
+@item x
+Pops a value off the stack and executes it as a macro.
+Normally it should be a string; if it is a number,
+it is simply pushed back onto the stack.
+For example, @samp{[1p]x} executes the macro @samp{1p}, which
+pushes 1 on the stack and prints @samp{1} on a separate line.
+
+Macros are most often stored in registers;
+@samp{[1p]sa} stores a macro to print @samp{1} into register @samp{a},
+and @samp{lax} invokes the macro.
+
+@item >@var{r}
+Pops two values off the stack and compares them
+assuming they are numbers, executing the contents
+of register @var{r} as a macro if the original top-of-stack
+is greater. Thus, @samp{1 2>a} will invoke register @samp{a}'s contents
+and @samp{2 1>a} will not.
+
+@item <@var{r}
+Similar but invokes the macro if the original top-of-stack
+is less.
+
+@item =@var{r}
+Similar but invokes the macro if the two numbers popped
+are equal. This can also be validly used to compare two
+strings for equality.
+
+@item ?
+Reads a line from the terminal and executes it.
+This command allows a macro to request input from the user.
+
+@item q
+During the execution of a macro, this comand
+does not exit DC. Instead, it exits from that
+macro and also from the macro which invoked it (if any).
+
+@item Q
+Pops a value off the stack and uses it as a count
+of levels of macro execution to be exited. Thus,
+@samp{3Q} exits three levels.
+@end table
+
+@node Status Inquiry, Notes, Strings, Top
+@chapter Status Inquiry
+
+@table @samp
+@item Z
+Pops a value off the stack, calculates the number of
+digits it has (or number of characters, if it is a string)
+and pushes that number.
+
+@item X
+Pops a value off the stack, calculates the number of
+fraction digits it has, and pushes that number.
+For a string, the value pushed is -1.
+
+@item z
+Pushes the current stack depth; the number of
+objects on the stack before the execution of the @samp{z} command.
+
+@item I
+Pushes the current value of the input radix.
+
+@item O
+Pushes the current value of the output radix.
+
+@item K
+Pushes the current value of the precision.
+@end table
+
+@node Notes, , Status Inquiry, Top
+@chapter Notes
+
+The @samp{:} and @samp{;} commands of the Unix DC program are
+not supported, as the documentation does not say what they do.
+The @samp{!} command is not supported, but will be supported
+as soon as a library for executing a line as a command exists.
+
+@contents
+@bye
diff --git a/gnu/usr.bin/dc/decimal.c b/gnu/usr.bin/dc/decimal.c
new file mode 100644
index 0000000..1fb95c1
--- /dev/null
+++ b/gnu/usr.bin/dc/decimal.c
@@ -0,0 +1,1235 @@
+/*
+ * Arbitrary precision decimal arithmetic.
+ *
+ * Copyright (C) 1984 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can either send email to this
+ * program's author (see below) or write to: The Free Software Foundation,
+ * Inc.; 675 Mass Ave. Cambridge, MA 02139, USA.
+ */
+
+/* Some known problems:
+
+ Another problem with decimal_div is found when you try to
+ divide a number with > scale fraction digits by 1. The
+ expected result is simply truncation, but all sorts of things
+ happen instead. An example is that the result of .99999998/1
+ with scale set to 6 is .000001
+
+ There are some problems in the behavior of the decimal package
+ related to printing and parsing. The
+ printer is weird about very large output radices, tending to want
+ to output single ASCII characters for any and all digits (even
+ in radices > 127). The UNIX bc approach is to print digit groups
+ separated by spaces. There is a rather overwrought workaround in
+ the function decputc() in bcmisc.c, but it would be better if
+ decimal.c got a fix for this. */
+
+/* For stand-alone testing, compile with -DTEST.
+ This DTESTable feature defines a `main' function
+ which is a simple loop that accepts input of the form
+ number space op space number newline
+ where op is +, -, *, /, %, p or r,
+ and performs the operation and prints the operands and result.
+ `p' means print the first number in the radix spec'd by the second.
+ `r' means read the first one in the radix specified by the second
+ (and print the result in decimal).
+ Divide in this test keeps three fraction digits. */
+
+#include "decimal.h"
+
+#define MAX(a, b) (((a) > (b) ? (a) : (b)))
+
+/* Some constant decimal numbers */
+
+struct decimal decimal_zero = {0, 0, 0, 0, 0};
+
+struct decimal decimal_one = {0, 0, 1, 0, 1};
+
+/*** Assumes RADIX is even ***/
+struct decimal decimal_half = {0, 1, 0, 0, RADIX / 2};
+
+decimal static decimal_add1 (), decimal_sub1 ();
+static void add_scaled ();
+static int subtract_scaled ();
+
+/* Create and return a decimal number that has `before' digits before
+ the decimal point and `after' digits after. The digits themselves are
+ initialized to zero. */
+
+decimal
+make_decimal (before, after)
+ int before, after;
+{
+ decimal result;
+ if (before >= 1<<16)
+ {
+ decimal_error ("%d too many decimal digits", before);
+ return 0;
+ }
+ if (after >= 1<<15)
+ {
+ decimal_error ("%d too many decimal digits", after);
+ return 0;
+ }
+ result = (decimal) malloc (sizeof (struct decimal) + before + after - 1);
+ result->sign = 0;
+ result->before = before;
+ result->after = after;
+ result->refcnt = 0;
+ bzero (result->contents, before + after);
+ return result;
+}
+
+/* Create a copy of the decimal number `b' and return it. */
+
+decimal
+decimal_copy (b)
+ decimal b;
+{
+ decimal result = make_decimal (b->before, b->after);
+ bcopy (b->contents, result->contents, LENGTH(b));
+ result->sign = b->sign;
+ return result;
+}
+
+/* Copy a decimal number `b' but extend or truncate to exactly
+ `digits' fraction digits. */
+
+static decimal
+decimal_copy_1 (b, digits)
+ decimal b;
+ int digits;
+{
+ if (digits > b->after)
+ {
+ decimal result = make_decimal (b->before, digits);
+ bcopy (b->contents, result->contents + (digits - (int) b->after), LENGTH(b));
+ return result;
+ }
+ else
+ return decimal_round_digits (b, digits);
+}
+
+/* flush specified number `digits' of trailing fraction digits,
+ and flush any trailing fraction zero digits exposed after they are gone.
+ The number `b' is actually modified; no new storage is allocated.
+ That is why this is not global. */
+
+static void
+flush_trailing_digits (b, digits)
+ decimal b;
+ int digits;
+{
+ int flush = digits;
+ int maxdig = b->after;
+
+ while (flush < maxdig && !b->contents [flush])
+ flush++;
+
+ if (flush)
+ {
+ int i;
+
+ b->after -= flush;
+ for (i = 0; i < LENGTH (b); i++)
+ b->contents[i] = b->contents[flush + i];
+ }
+
+}
+
+/* Return nonzero integer if the value of decimal number `b' is zero. */
+
+int
+decimal_zerop (b)
+ decimal b;
+{
+ return !LENGTH(b);
+}
+
+/* Compare two decimal numbers arithmetically.
+ The value is < 0 if b1 < b2, > 0 if b1 > b2, 0 if b1 = b2.
+ This is the same way that `strcmp' reports the result of comparing
+ strings. */
+
+int
+decimal_compare (b1, b2)
+ decimal b1, b2;
+{
+ int l1, l2;
+ char *p1, *p2, *s1, *s2;
+ int i;
+
+ /* If signs differ, deduce result from the signs */
+
+ if (b2->sign && !b1->sign) return 1;
+ if (b1->sign && !b2->sign) return -1;
+
+ /* If same sign but number of nonfraction digits differs,
+ the one with more of them is farther from zero. */
+
+ if (b1->before != b2->before)
+ if (b1->sign)
+ return (int) (b2->before - b1->before);
+ else
+ return (int) (b1->before - b2->before);
+
+ /* Else compare the numbers digit by digit from high end */
+ l1 = LENGTH(b1);
+ l2 = LENGTH(b2);
+ s1 = b1->contents; /* Start of number -- don't back up digit pointer past here */
+ s2 = b2->contents;
+ p1 = b1->contents + l1; /* Scanning pointer, for fetching digits. */
+ p2 = b2->contents + l2;
+ for (i = MAX(l1, l2); i >= 0; i--)
+ {
+ int r = ((p1 != s1) ? *--p1 : 0) - ((p2 != s2) ? *--p2 : 0);
+ if (r)
+ return b1->sign ? -r : r;
+ }
+ return 0;
+}
+
+/* Return the number of digits stored in decimal number `b' */
+
+int
+decimal_length (b)
+ decimal b;
+{
+ return LENGTH(b);
+}
+
+/* Return the number of fraction digits stored in decimal number `b'. */
+
+int
+decimal_after (b)
+ decimal b;
+{
+ return b->after;
+}
+
+/* Round decimal number `b' to have only `digits' fraction digits.
+ Result is rounded to nearest unit in the last remaining digit.
+ Return the result, another decimal number. */
+
+decimal
+decimal_round_digits (b, digits)
+ decimal b;
+ int digits;
+{
+ decimal result;
+ int old;
+
+ if (b->after <= digits) return decimal_copy (b);
+
+ if (digits < 0)
+ {
+ decimal_error ("request to keep negative number of digits %d", digits);
+ return decimal_copy (b);
+ }
+
+ result = make_decimal (b->before + 1, b->after);
+ result->sign = b->sign;
+ bcopy (b->contents, result->contents, LENGTH(b));
+
+ old = result->after;
+
+ /* Add .5 * last place to keep, so that we round rather than truncate */
+ /* Note this ignores sign of result, so if result is negative
+ it is subtracting */
+
+ add_scaled (result, DECIMAL_HALF, 1, old - digits - 1);
+
+ /* Flush desired digits, and any trailing zeros exposed by them. */
+
+ flush_trailing_digits (result, old - digits);
+
+ /* Flush leading digits -- always is one, unless was a carry into it */
+
+ while (result->before > 0
+ && result->contents[LENGTH(result) - 1] == 0)
+ result->before--;
+
+ return result;
+}
+
+/* Truncate decimal number `b' to have only `digits' fraction digits.
+ Any fraction digits in `b' beyond that are dropped and ignored.
+ Truncation is toward zero.
+ Return the result, another decimal number. */
+
+decimal
+decimal_trunc_digits (b, digits)
+ decimal b;
+ int digits;
+{
+ decimal result = decimal_copy (b);
+ int old = result->after;
+
+ if (old <= digits) return result;
+
+ if (digits < 0)
+ {
+ decimal_error ("request to keep negative number of digits %d", digits);
+ return result;
+ }
+
+ flush_trailing_digits (result, old - digits);
+
+ return result;
+}
+
+/* Return the fractional part of decimal number `b':
+ that is, `b' - decimal_trunc_digits (`b') */
+
+decimal
+decimal_fraction (b)
+ decimal b;
+{
+ decimal result = make_decimal (0, b->after);
+ bcopy (b->contents, result->contents, b->after);
+ return result;
+}
+
+/* return an integer whose value is that of decimal `b', sans its fraction. */
+
+int
+decimal_to_int (b)
+ decimal b;
+{
+ int result = 0;
+ int i;
+ int end = b->after;
+
+ for (i = LENGTH(b) - 1; i >= end; i--)
+ {
+ result *= RADIX;
+ result += b->contents[i];
+ }
+ return result;
+}
+
+/* return a decimal whose value is the integer i. */
+
+decimal
+decimal_from_int (i)
+ int i;
+{
+ int log, tem;
+ decimal result;
+
+ for (log = 0, tem = (i > 0 ? i : - i); tem; log++, tem /= RADIX);
+
+ result = make_decimal (log, 0);
+
+ for (log = 0, tem = (i > 0 ? i : - i); tem; log++, tem /= RADIX)
+ result->contents[log] = tem % RADIX;
+
+ if (i < 0) result->sign = 1;
+ return result;
+}
+
+/* Return (as an integer) the result of dividing decimal number `b' by
+ integer `divisor'.
+ This is used in printing decimal numbers in other radices. */
+
+int
+decimal_int_rem (b, divisor)
+ decimal b;
+ int divisor;
+{
+ int len = LENGTH(b);
+ int end = b->after;
+ int accum = 0;
+ int i;
+
+ for (i = len - 1; i >= end; i--)
+ {
+ accum %= divisor;
+ accum *= RADIX;
+ accum += b->contents[i];
+ }
+ return accum % divisor;
+}
+
+/* Convert digit `digit' to a character and output it by calling
+ `charout' with it as arg. */
+
+static void
+print_digit (digit, charout)
+ int digit;
+ void (*charout) ();
+{
+ if (digit < 10)
+ charout ('0' + digit);
+ else
+ charout ('A' + digit - 10);
+}
+
+/* print decimal number `b' in radix `radix', assuming it is an integer.
+ `r' is `radix' expressed as a decimal number. */
+
+static
+decimal_print_1 (b, r, radix, charout)
+ decimal b, r;
+ int radix;
+ void (*charout) ();
+{
+ int digit = decimal_int_rem (b, radix);
+ decimal rest = decimal_div (b, r, 0);
+
+ if (!decimal_zerop (rest))
+ decimal_print_1 (rest, r, radix, charout);
+
+ print_digit (digit, charout);
+
+ free (rest);
+}
+
+/* User entry: print decimal number `b' in radix `radix' (an integer),
+ outputting characters by calling `charout'. */
+
+void
+decimal_print (b, charout, radix)
+ decimal b;
+ void (*charout) ();
+ int radix;
+{
+ if (b->sign) charout ('-');
+
+ if (radix == RADIX)
+ {
+ /* decimal output => just print the digits, inserting a point in
+ the proper place. */
+ int i;
+ int before = b->before;
+ int len = before + b->after;
+ for (i = 0; i < len; i++)
+ {
+ if (i == before) charout ('.');
+ /* Broken if RADIX /= 10
+ charout ('0' + b->contents [len - 1 - i]); */
+ print_digit (b->contents [len - 1 - i], charout);
+ }
+ if (!len)
+ charout ('0');
+ }
+ else
+ {
+ /* nonstandard radix: must use multiply and divide to determine the
+ digits of the number in that radix. */
+
+ int i;
+ extern double log10 ();
+ /* Compute the number of fraction digits we want to have in the
+ new radix. They should contain the same amount of
+ information as the decimal digits we have. */
+ int nfrac = (b->after / log10 ((double) radix) + .99);
+ decimal r = decimal_from_int (radix);
+ decimal intpart = decimal_trunc_digits (b, 0);
+
+ /* print integer part */
+ decimal_print_1 (intpart, r, radix, charout);
+ free (intpart);
+
+ /* print fraction part */
+ if (nfrac)
+ {
+ decimal tem1, tem2;
+ tem1 = decimal_fraction (b);
+ charout ('.');
+ /* repeatedly multiply by `radix', print integer part as one digit,
+ and flush the integer part. */
+ for (i = 0; i < nfrac; i++)
+ {
+ tem2 = decimal_mul (tem1, r);
+ free (tem1);
+ print_digit (decimal_to_int (tem2), charout);
+ tem1 = decimal_fraction (tem2);
+ free (tem2);
+ }
+ free (tem1);
+ }
+ free (r);
+ }
+}
+
+static int
+decode_digit (digitchar)
+ char digitchar;
+{
+ if ('0' <= digitchar && digitchar <= '9')
+ return digitchar - '0';
+ if ('a' <= digitchar && digitchar <= 'z')
+ return digitchar - 'a' + 10;
+ if ('A' <= digitchar && digitchar <= 'Z')
+ return digitchar - 'A' + 10;
+ return -1;
+}
+
+/* Parse string `s' into a number using radix `radix'
+ and return result as a decimal number. */
+
+decimal
+decimal_parse (s, radix)
+ char *s;
+ int radix;
+{
+ int i, len, before = -1;
+ char *p;
+ char c;
+ decimal result;
+ int negative = 0;
+ int excess_digit = 0;
+
+ if (*s == '-')
+ {
+ s++;
+ negative = 1;
+ }
+
+ /* First scan for valid characters.
+ Count total num digits, and count num before the decimal point. */
+
+ p = s;
+ i = 0;
+ while (c = *p++)
+ {
+ if (c == '.')
+ {
+ if (before >= 0)
+ decimal_error ("two decimal points in %s", s);
+ before = i;
+ }
+ else if (c == '0' && !i && before < 0)
+ s++; /* Discard leading zeros */
+ else if (decode_digit (c) >= 0)
+ {
+ i++;
+ if (decode_digit (c) > RADIX)
+ excess_digit = 1;
+ }
+ else
+ decimal_error ("invalid number %s", s);
+ }
+
+ len = i;
+ if (before < 0) before = i;
+
+ p = s;
+
+ /* Now parse those digits */
+
+ if (radix != RADIX || excess_digit)
+ {
+ decimal r = decimal_from_int (radix);
+ extern double log10 ();
+ int digits = (len - before) * log10 ((double) radix) + .99;
+ result = decimal_copy (DECIMAL_ZERO);
+
+ /* Parse all the digits into an integer, ignoring decimal point,
+ by multiplying by `radix'. */
+
+ while (i > 0 && (c = *p++))
+ {
+ if (c != '.')
+ {
+ decimal newdig = decimal_from_int (decode_digit (c));
+ decimal prod = decimal_mul (result, r);
+ decimal newresult = decimal_add (newdig, prod);
+
+ free (newdig); free (prod); free (result);
+ result = newresult;
+ i--;
+ }
+ }
+
+ /* Now put decimal point in right place
+ by dividing by `radix' once for each digit
+ that really should have followed the decimal point. */
+
+ for (i = before; i < len; i++)
+ {
+ decimal newresult = decimal_div (result, r, digits);
+ free (result);
+ result = newresult;
+ }
+ free (r);
+ }
+ else
+ {
+ /* radix is standard - just copy the digits into a decimal number. */
+
+ int tem;
+ result = make_decimal (before, len - before);
+
+ while (i > 0 && (c = *p++))
+ {
+ if ((c != '.') &&
+ ((tem = decode_digit (c)) >= 0))
+ result->contents [--i] = tem;
+ }
+ }
+
+ if (negative) result->sign = 1;
+ flush_trailing_digits (result, 0);
+ return result;
+}
+
+/* Add b1 and b2, considering their signs */
+
+decimal
+decimal_add (b1, b2)
+ decimal b1, b2;
+{
+ decimal v;
+
+ if (b1->sign != b2->sign)
+ v = decimal_sub1 (b1, b2);
+ else
+ v = decimal_add1 (b1, b2);
+ if (b1->sign && !decimal_zerop (v))
+ v->sign = !v->sign;
+ return v;
+}
+
+/* Add b1 and minus b2, considering their signs */
+
+decimal
+decimal_sub (b1, b2)
+ decimal b1, b2;
+{
+ decimal v;
+
+ if (b1->sign != b2->sign)
+ v = decimal_add1 (b1, b2);
+ else
+ v = decimal_sub1 (b1, b2);
+ if (b1->sign && !decimal_zerop (v))
+ v->sign = !v->sign;
+ return v;
+}
+
+/* Return the negation of b2. */
+
+decimal
+decimal_neg (b2)
+ decimal b2;
+{
+ decimal v = decimal_copy (b2);
+
+ if (!decimal_zerop (v))
+ v->sign = !v->sign;
+ return v;
+}
+
+/* add magnitudes of b1 and b2, ignoring their signs. */
+
+static decimal
+decimal_add1 (b1, b2)
+ decimal b1, b2;
+{
+ int before = MAX (b1->before, b2->before);
+ int after = MAX (b1->after, b2->after);
+
+ int len = before+after+1;
+ decimal result = make_decimal (before+1, after);
+
+ int i;
+ char *s1 = b1->contents;
+ char *s2 = b2->contents;
+ char *p1 = s1 + b1->after - after;
+ char *p2 = s2 + b2->after - after;
+ char *e1 = s1 + b1->before + b1->after;
+ char *e2 = s2 + b2->before + b2->after;
+ char *pr = result->contents;
+ int accum = 0;
+
+ for (i = 0; i < len; i++, p1++, p2++)
+ {
+ accum /= RADIX;
+ if (p1 >= s1 && p1 < e1) accum += *p1;
+ if (p2 >= s2 && p2 < e2) accum += *p2;
+ *pr++ = accum % RADIX;
+ }
+ if (!accum)
+ (result->before)--;
+
+ flush_trailing_digits (result, 0);
+
+ return result;
+}
+
+/* subtract magnitude of b2 from that or b1, returning signed decimal
+ number. */
+
+static decimal
+decimal_sub1 (b1, b2)
+ decimal b1, b2;
+{
+ int before = MAX (b1->before, b2->before);
+ int after = MAX (b1->after, b2->after);
+
+ int len = before+after;
+ decimal result = make_decimal (before, after);
+
+ int i;
+ char *s1 = b1->contents;
+ char *s2 = b2->contents;
+ char *p1 = s1 + b1->after - after;
+ char *p2 = s2 + b2->after - after;
+ char *e1 = s1 + b1->before + b1->after;
+ char *e2 = s2 + b2->before + b2->after;
+ char *pr = result->contents;
+ int accum = 0;
+
+ for (i = 0; i < len; i++, p1++, p2++)
+ {
+ if (p1 >= s1 && p1 < e1) accum += *p1;
+ if (p2 >= s2 && p2 < e2) accum -= *p2;
+ if (accum < 0 && accum % RADIX)
+ *pr = RADIX - (- accum) % RADIX;
+ else
+ *pr = accum % RADIX;
+ accum -= *pr++;
+ accum /= RADIX;
+ }
+
+ /* If result is negative, subtract it from RADIX**length
+ so that we get the right digits for sign-magnitude
+ rather than RADIX-complement */
+
+ if (accum)
+ {
+ result->sign = 1;
+ pr = result->contents;
+ accum = 0;
+ for (i = 0; i < len; i++)
+ {
+ accum -= *pr;
+ if (accum)
+ *pr = accum + RADIX;
+ else
+ *pr = 0;
+ accum -= *pr++;
+ accum /= RADIX;
+ }
+ }
+
+ /* flush leading nonfraction zero digits */
+
+ while (result->before && *--pr == 0)
+ (result->before)--;
+
+ flush_trailing_digits (result, 0);
+
+ return result;
+}
+
+/* multiply b1 and b2 keeping `digits' fraction digits */
+
+decimal
+decimal_mul_rounded (b1, b2, digits)
+ decimal b1, b2;
+ int digits;
+{
+ decimal tem = decimal_mul (b1, b2);
+ decimal result = decimal_round_digits (tem, digits);
+ free (tem);
+ return result;
+}
+
+/* multiply b1 and b2 keeping the right number of fraction digits
+ for the `dc' program with precision = `digits'. */
+
+decimal
+decimal_mul_dc (b1, b2, digits)
+ decimal b1, b2;
+ int digits;
+{
+ decimal tem = decimal_mul (b1, b2);
+ decimal result
+ = decimal_round_digits (tem, MAX (digits, MAX (b1->after, b2->after)));
+ free (tem);
+ return result;
+}
+
+/* multiply b1 and b2 as decimal error-free values;
+ keep LENGTH(b1) plus LENGTH(b2) significant figures. */
+
+decimal
+decimal_mul (b1, b2)
+ decimal b1, b2;
+{
+ decimal result = make_decimal (b1->before + b2->before, b1->after + b2->after);
+ int i;
+ int length2 = LENGTH(b2);
+ char *pr;
+
+ for (i = 0; i < length2; i++)
+ add_scaled (result, b1, b2->contents[i], i);
+
+ /* flush leading nonfraction zero digits */
+
+ pr = result->contents + LENGTH(result);
+ while (result->before && *--pr == 0)
+ (result->before)--;
+
+ flush_trailing_digits (result, 0); /* flush trailing zeros */
+
+ /* Set sign properly */
+
+ if (b1->sign != b2->sign && LENGTH(result))
+ result->sign = 1;
+
+ return result;
+}
+
+/* Modify decimal number `into' by adding `from',
+ multiplied by `factor' (which should be nonnegative and less than RADIX)
+ and shifted left `scale' digits at the least significant end. */
+
+static void
+add_scaled (into, from, factor, scale)
+ decimal into, from;
+ int factor, scale;
+{
+ char *pf = from->contents;
+ char *pi = into->contents + scale;
+ int lengthf = LENGTH(from);
+ int lengthi = LENGTH(into) - scale;
+
+ int accum = 0;
+ int i;
+
+ for (i = 0; i < lengthi; i++)
+ {
+ accum /= RADIX;
+ if (i < lengthf)
+ accum += *pf++ * factor;
+ accum += *pi;
+ *pi++ = accum % RADIX;
+ }
+}
+
+/* Divide decimal number `b1' by `b2', keeping at most `digits'
+ fraction digits.
+ Returns the result as a decimal number.
+
+ When division is not exact, the quotient is truncated toward zero. */
+
+decimal
+decimal_div (b1, b2, digits)
+ decimal b1, b2;
+ int digits;
+{
+ decimal result = make_decimal (MAX(1, (int) (1 + b1->before - b2->before)), digits);
+
+ /* b1copy holds what is left of the dividend,
+ that is not accounted for by the quotient digits already known */
+
+ decimal b1copy = decimal_copy_1 (b1, b2->after + digits);
+ int length1 = LENGTH(b1copy);
+ int length2 = LENGTH(b2);
+ int lengthr = LENGTH(result);
+ int i;
+
+ /* leading_divisor_digits contains the first two divisor digits, as
+ an integer */
+
+ int leading_divisor_digits = b2->contents[length2-1]*RADIX;
+ if (length2 > 1)
+ leading_divisor_digits += b2->contents[length2-2];
+
+ if (decimal_zerop (b2))
+ {
+ decimal_error ("divisor is zero", 0);
+ return decimal_copy (DECIMAL_ZERO);
+ }
+
+ if (lengthr <= (length1 - length2))
+ abort(); /* My reasoning says this cannot happen, I hope */
+
+ for (i = length1 - length2; i >= 0; i--)
+ {
+ /* Guess the next quotient digit (in order of decreasing significance)
+ using integer division */
+
+ int guess;
+ int trial_dividend = b1copy->contents[length2+i-1]*RADIX;
+ if (i != length1 - length2)
+ trial_dividend += b1copy->contents[length2+i]*RADIX*RADIX;
+ if (length2 + i > 1)
+ trial_dividend += b1copy->contents[length2+i-2];
+
+ guess = trial_dividend / leading_divisor_digits;
+
+ /* Remove the quotient times this digit from the dividend left */
+ /* We may find that the quotient digit is too large,
+ when we consider the entire divisor.
+ Then we decrement the quotient digit and add the divisor back in */
+
+ if (guess && 0 > subtract_scaled (b1copy, b2, guess, i))
+ {
+ guess--;
+ add_scaled (b1copy, b2, 1, i);
+ }
+
+ if (guess >= RADIX)
+ {
+ result->contents[i + 1] += guess / RADIX;
+ guess %= RADIX;
+ }
+ result->contents[i] = guess;
+ }
+
+ free (b1copy);
+
+ result->sign = (b1->sign != b2->sign);
+
+ /* flush leading nonfraction zero digits */
+
+ {
+ char *pr = result->contents + lengthr;
+ while (result->before && *--pr == 0)
+ (result->before)--;
+ }
+
+ flush_trailing_digits (result, 0); /* Flush trailing zero fraction digits */
+
+ return result;
+}
+
+/* The remainder for the above division.
+ Same as `b1' - (`b1' / `b2') * 'b2'.
+ Note that the value depends on the number of fraction digits
+ that were kept in computing `b1' / `b2';
+ the argument `digits' specifies this.
+
+ The remainder has the same sign as the dividend.
+ The divisor's sign is ignored. */
+
+decimal
+decimal_rem (b1, b2, digits)
+ decimal b1, b2;
+ int digits;
+{
+ decimal b1copy = decimal_copy_1 (b1, b2->after + digits);
+ int length1 = LENGTH(b1copy);
+ int length2 = LENGTH(b2);
+ int i;
+
+ int leading_divisor_digits = b2->contents[length2-1]*RADIX;
+
+ if (length2 > 1)
+ leading_divisor_digits += b2->contents[length2-2];
+
+ if (decimal_zerop (b2))
+ {
+ decimal_error ("divisor is zero", 0);
+ return decimal_copy (DECIMAL_ZERO);
+ }
+
+ /* Do like division, above, but throw away the quotient.
+ Keep only the final `rest of dividend', which becomes the remainder. */
+
+ for (i = length1 - length2; i >= 0; i--)
+ {
+ int guess;
+ int trial_dividend = b1copy->contents[length2+i-1]*RADIX;
+ if (i != length1 - length2)
+ trial_dividend += b1copy->contents[length2+i]*RADIX*RADIX;
+ if (length2 + i > 1)
+ trial_dividend += b1copy->contents[length2+i-2];
+
+ guess = trial_dividend / leading_divisor_digits;
+
+ if (guess && 0 > subtract_scaled (b1copy, b2, guess, i))
+ {
+ guess--;
+ add_scaled (b1copy, b2, 1, i);
+ }
+ /* No need to check whether guess exceeds RADIX
+ since we are not saving guess. */
+ }
+
+ /* flush leading nonfraction zero digits */
+
+ {
+ char *pr = b1copy->contents + length1;
+ while (b1copy->before && *--pr == 0)
+ (b1copy->before)--;
+ }
+
+ flush_trailing_digits (b1copy, 0);
+ return b1copy;
+}
+
+/* returns negative number if we chose factor too large */
+
+static int
+subtract_scaled (into, from, factor, scale)
+ decimal into, from;
+ int factor, scale;
+{
+ char *pf = from->contents;
+ char *pi = into->contents + scale;
+ int lengthf = LENGTH(from);
+ int lengthi = LENGTH(into) - scale;
+ int accum = 0;
+ int i;
+
+ for (i = 0; i < lengthi && i <= lengthf; i++)
+ {
+ if (i < lengthf)
+ accum -= *pf++ * factor;
+ accum += *pi;
+ if (accum < 0 && accum % RADIX)
+ *pi = RADIX - (- accum) % RADIX;
+ else
+ *pi = accum % RADIX;
+ accum -= *pi++;
+ accum /= RADIX;
+ }
+ return accum;
+}
+
+/* Return the square root of decimal number D, using Newton's method.
+ Number of fraction digits returned is max of FRAC_DIGITS
+ and D's number of fraction digits. */
+
+decimal
+decimal_sqrt (d, frac_digits)
+ decimal d;
+ int frac_digits;
+{
+ decimal guess;
+ int notdone = 1;
+
+ if (decimal_zerop (d)) return d;
+ if (d->sign)
+ {
+ decimal_error ("square root argument negative", 0);
+ return decimal_copy (DECIMAL_ZERO);
+ }
+
+ frac_digits = MAX (frac_digits, d->after);
+
+ /* Compute an initial guess by taking the square root
+ of a nearby power of RADIX. */
+
+ if (d->before)
+ {
+ guess = make_decimal ((d->before + 1) / 2, 0);
+ guess->contents[guess->before - 1] = 1;
+ }
+ else
+ {
+ /* Arg is less than 1; compute nearest power of RADIX */
+ char *p = d->contents + LENGTH(d);
+ char *sp = p;
+
+ while (!*--p); /* Find most significant nonzero digit */
+ if (sp - p == 1)
+ {
+ /* Arg is bigger than 1/RADIX; use 1 as a guess */
+ guess = decimal_copy (DECIMAL_ONE);
+ }
+ else
+ {
+ guess = make_decimal (0, (sp - p) / 2);
+ guess->contents[0] = 1;
+ }
+ }
+
+ /* Iterate doing guess = (guess + d/guess) / 2 */
+
+ while (notdone)
+ {
+ decimal tem1 = decimal_div (d, guess, frac_digits + 1);
+ decimal tem2 = decimal_add (guess, tem1);
+ decimal tem3 = decimal_mul_rounded (tem2, DECIMAL_HALF, frac_digits);
+ notdone = decimal_compare (guess, tem3);
+ free (tem1);
+ free (tem2);
+ free (guess);
+ guess = tem3;
+ if (decimal_zerop (guess)) return guess; /* Avoid divide-by-zero */
+ }
+
+ return guess;
+}
+
+/* Raise decimal number `base' to power of integer part of decimal
+ number `expt'.
+ This function depends on using radix 10.
+ It is too hard to write it to work for any value of RADIX,
+ so instead it is simply not available if RADIX is not ten. */
+
+#if !(RADIX - 10)
+
+decimal
+decimal_expt (base, expt, frac_digits)
+ decimal base, expt;
+ int frac_digits;
+{
+ decimal accum = decimal_copy (DECIMAL_ONE);
+ decimal basis1 = base;
+ int digits = expt->before;
+ int dig = 0; /* Expt digit being processed */
+
+ if (expt->sign)
+ /* If negative power, take reciprocal first thing
+ so that fraction digit truncation won't destroy
+ what will ultimately be nonfraction digits. */
+ basis1 = decimal_div (DECIMAL_ONE, base, frac_digits);
+ while (dig < digits)
+ {
+ decimal basis2, basis4, basis8, basis10;
+ int thisdigit = expt->contents[expt->after + dig];
+
+ /* Compute factors to multiply in for each bit of this digit */
+
+ basis2 = decimal_mul_rounded (basis1, basis1, frac_digits);
+ basis4 = decimal_mul_rounded (basis2, basis2, frac_digits);
+ basis8 = decimal_mul_rounded (basis4, basis4, frac_digits);
+
+ /* Now accumulate the factors this digit value selects */
+
+ if (thisdigit & 1)
+ {
+ decimal accum1 = decimal_mul_rounded (accum, basis1, frac_digits);
+ free (accum);
+ accum = accum1;
+ }
+
+ if (thisdigit & 2)
+ {
+ decimal accum1 = decimal_mul_rounded (accum, basis2, frac_digits);
+ free (accum);
+ accum = accum1;
+ }
+
+ if (thisdigit & 4)
+ {
+ decimal accum1 = decimal_mul_rounded (accum, basis4, frac_digits);
+ free (accum);
+ accum = accum1;
+ }
+
+ if (thisdigit & 8)
+ {
+ decimal accum1 = decimal_mul_rounded (accum, basis8, frac_digits);
+ free (accum);
+ accum = accum1;
+ }
+
+ /* If there are further digits, compute the basis1 for the next digit */
+
+ if (++dig < digits)
+ basis10 = decimal_mul_rounded (basis2, basis8, frac_digits);
+
+ /* Free intermediate results */
+
+ if (basis1 != base) free (basis1);
+ free (basis2);
+ free (basis4);
+ free (basis8);
+ basis1 = basis10;
+ }
+ return accum;
+}
+#endif
+
+#ifdef TEST
+
+fputchar (c)
+ char c;
+{
+ putchar (c);
+}
+
+/* Top level that can be used to test the arithmetic functions */
+
+main ()
+{
+ char s1[40], s2[40];
+ decimal b1, b2, b3;
+ char c;
+
+ while (1)
+ {
+ scanf ("%s %c %s", s1, &c, s2);
+ b1 = decimal_parse (s1, RADIX);
+ b2 = decimal_parse (s2, RADIX);
+ switch (c)
+ {
+ default:
+ c = '+';
+ case '+':
+ b3 = decimal_add (b1, b2);
+ break;
+ case '*':
+ b3 = decimal_mul (b1, b2);
+ break;
+ case '/':
+ b3 = decimal_div (b1, b2, 3);
+ break;
+ case '%':
+ b3 = decimal_rem (b1, b2, 3);
+ break;
+ case 'p':
+ decimal_print (b1, fputchar, RADIX);
+ printf (" printed in base %d is ", decimal_to_int (b2));
+ decimal_print (b1, fputchar, decimal_to_int (b2));
+ printf ("\n");
+ continue;
+ case 'r':
+ printf ("%s read in base %d is ", s1, decimal_to_int (b2));
+ decimal_print (decimal_parse (s1, decimal_to_int (b2)), fputchar, RADIX);
+ printf ("\n");
+ continue;
+ }
+ decimal_print (b1, fputchar, RADIX);
+ printf (" %c ", c);
+ decimal_print (b2, fputchar, RADIX);
+ printf (" = ");
+ decimal_print (b3, fputchar, RADIX);
+ printf ("\n");
+ }
+}
+
+decimal_error (s1, s2)
+ char *s1, *s2;
+{
+ printf ("\n");
+ printf (s1, s2);
+ printf ("\n");
+}
+
+static void
+pbi (b)
+ int b;
+{
+ decimal_print ((decimal) b, fputchar, RADIX);
+}
+
+static void
+pb (b)
+ decimal b;
+{
+ decimal_print (b, fputchar, RADIX);
+}
+
+#endif
diff --git a/gnu/usr.bin/dc/decimal.h b/gnu/usr.bin/dc/decimal.h
new file mode 100644
index 0000000..2b41158
--- /dev/null
+++ b/gnu/usr.bin/dc/decimal.h
@@ -0,0 +1,93 @@
+/*
+ * Header file for decimal.c (arbitrary precision decimal arithmetic)
+ *
+ * Copyright (C) 1984 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can either send email to this
+ * program's author (see below) or write to: The Free Software Foundation,
+ * Inc.; 675 Mass Ave. Cambridge, MA 02139, USA.
+ */
+
+/* Autoconf stuff */
+#ifndef HAVE_BCOPY
+#undef bcopy
+#define bcopy(s2, s1, n) memcpy (s1, s2, n)
+#endif
+
+#ifndef HAVE_BZERO
+#undef bzero
+#define bzero(b, l) memset (b, 0, l)
+#endif
+
+/* Define the radix to use by default, and for representing the
+ numbers internally. This does not need to be decimal; that is just
+ the default for it. */
+
+/* Currently, this is required to be even for this program to work. */
+
+#ifndef RADIX
+#define RADIX 10
+#endif
+
+/* The user must define the external function `decimal_error'
+ which is called with two arguments to report errors in this package.
+ The two arguments may be passed to `printf' to print a message. */
+
+/* Structure that represents a decimal number */
+
+struct decimal
+{
+ unsigned int sign: 1; /* One for negative number */
+ /* The sign should always be zero for the number 0 */
+ int after: 15; /* number of fraction digits */
+ unsigned short before; /* number of non-fraction digits */
+ unsigned short refcnt; /* number of pointers to this number */
+ /* (used by calling program) */
+ char contents[1]; /* the digits themselves, least significant first. */
+ /* digits are just numbers 0 .. RADIX-1 */
+};
+
+/* There may never be leading nonfraction zeros or trailing fraction
+ zeros in a number. They must be removed by all the arithmetic
+ functions. Therefore, the number zero always has no digits stored. */
+
+typedef struct decimal *decimal;
+
+/* Decimal numbers are always passed around as pointers.
+ All the external entries in this file allocate new numbers
+ using `malloc' to store values in.
+ They never modify their arguments or any existing numbers. */
+
+/* Return the total number of digits stored in the number `b' */
+#define LENGTH(b) ((b)->before + (b)->after)
+
+/* Some constant decimal numbers */
+
+
+#define DECIMAL_ZERO &decimal_zero
+
+
+#define DECIMAL_ONE &decimal_one
+
+#define DECIMAL_HALF &decimal_half
+
+decimal decimal_add (), decimal_sub (), decimal_mul (), decimal_div ();
+decimal decimal_mul_dc (), decimal_mul_rounded (), decimal_rem ();
+decimal decimal_round_digits ();
+decimal make_decimal (), decimal_copy (), decimal_parse ();
+decimal decimal_sqrt (), decimal_expt ();
+
+void decimal_print ();
+
+/* End of decimal.h */
diff --git a/gnu/usr.bin/diff/Makefile b/gnu/usr.bin/diff/Makefile
index e75c0a0..fe3a5e9 100644
--- a/gnu/usr.bin/diff/Makefile
+++ b/gnu/usr.bin/diff/Makefile
@@ -1,11 +1,8 @@
PROG= diff
SRCS= diff.c analyze.c io.c context.c ed.c normal.c ifdef.c util.c dir.c \
- version.c regex.c getopt.c getopt1.c side.c
-CFLAGS+=-DDIRENT=1 -DHAVE_UNISTD_H=1 -DHAVE_DUP2=1 -DHAVE_MEMCHR=1 \
- -DHAVE_STRERROR=1 -DHAVE_WAITPID=1 -DHAVE_FCNTL_H=1 \
- -DHAVE_STRING_H=1 -DHAVE_SYS_WAIT_H=1 -DHAVE_TIME_H=1 \
- -DHAVE_ST_BLKSIZE=1
-NOMAN=noman
+ version.c regex.c getopt.c getopt1.c side.c cmpbuf.c
+CFLAGS+=-DHAVE_CONFIG_H
+MAN= diff.1
.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/diff/analyze.c b/gnu/usr.bin/diff/analyze.c
index 42167b9..556d388 100644
--- a/gnu/usr.bin/diff/analyze.c
+++ b/gnu/usr.bin/diff/analyze.c
@@ -1,5 +1,5 @@
/* Analyze file differences for GNU DIFF.
- Copyright (C) 1988, 1989, 1992 Free Software Foundation, Inc.
+ Copyright (C) 1988, 1989, 1992, 1993 Free Software Foundation, Inc.
This file is part of GNU DIFF.
@@ -17,72 +17,98 @@ You should have received a copy of the GNU General Public License
along with GNU DIFF; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-/* The basic algorithm is described in:
+/* The basic algorithm is described in:
"An O(ND) Difference Algorithm and its Variations", Eugene Myers,
- Algorithmica Vol. 1 No. 2, 1986, p 251. */
+ Algorithmica Vol. 1 No. 2, 1986, pp. 251-266;
+ see especially section 4.2, which describes the variation used below.
+ Unless the --minimal option is specified, this code uses the TOO_EXPENSIVE
+ heuristic, by Paul Eggert, to limit the cost to O(N**1.5 log N)
+ at the price of producing suboptimal output for large inputs with
+ many differences.
-#include "diff.h"
+ The basic algorithm was independently discovered as described in:
+ "Algorithms for Approximate String Matching", E. Ukkonen,
+ Information and Control Vol. 64, 1985, pp. 100-118. */
-int read_files ();
-void finish_output ();
-void print_context_script ();
-void print_ed_script ();
-void print_ifdef_script ();
-void print_sdiff_script ();
-void print_normal_script ();
-void print_rcs_script ();
-void pr_forward_ed_script ();
-void setup_output ();
+#include "diff.h"
+#include "cmpbuf.h"
extern int no_discards;
static int *xvec, *yvec; /* Vectors being compared. */
static int *fdiag; /* Vector, indexed by diagonal, containing
- the X coordinate of the point furthest
+ 1 + the X coordinate of the point furthest
along the given diagonal in the forward
search of the edit matrix. */
static int *bdiag; /* Vector, indexed by diagonal, containing
the X coordinate of the point furthest
along the given diagonal in the backward
search of the edit matrix. */
+static int too_expensive; /* Edit scripts longer than this are too
+ expensive to compute. */
+
+#define SNAKE_LIMIT 20 /* Snakes bigger than this are considered `big'. */
+
+struct partition
+{
+ int xmid, ymid; /* Midpoints of this partition. */
+ int lo_minimal; /* Nonzero if low half will be analyzed minimally. */
+ int hi_minimal; /* Likewise for high half. */
+};
+
+static int diag PARAMS((int, int, int, int, int, struct partition *));
+static struct change *add_change PARAMS((int, int, int, int, struct change *));
+static struct change *build_reverse_script PARAMS((struct file_data const[]));
+static struct change *build_script PARAMS((struct file_data const[]));
+static void briefly_report PARAMS((int, struct file_data const[]));
+static void compareseq PARAMS((int, int, int, int, int));
+static void discard_confusing_lines PARAMS((struct file_data[]));
+static void shift_boundaries PARAMS((struct file_data[]));
/* Find the midpoint of the shortest edit script for a specified
portion of the two files.
- We scan from the beginnings of the files, and simultaneously from the ends,
+ Scan from the beginnings of the files, and simultaneously from the ends,
doing a breadth-first search through the space of edit-sequence.
When the two searches meet, we have found the midpoint of the shortest
edit sequence.
- The value returned is the number of the diagonal on which the midpoint lies.
- The diagonal number equals the number of inserted lines minus the number
+ If MINIMAL is nonzero, find the minimal edit script regardless
+ of expense. Otherwise, if the search is too expensive, use
+ heuristics to stop the search and report a suboptimal answer.
+
+ Set PART->(XMID,YMID) to the midpoint (XMID,YMID). The diagonal number
+ XMID - YMID equals the number of inserted lines minus the number
of deleted lines (counting only lines before the midpoint).
- The edit cost is stored into *COST; this is the total number of
- lines inserted or deleted (counting only lines before the midpoint).
+ Return the approximate edit cost; this is the total number of
+ lines inserted or deleted (counting only lines before the midpoint),
+ unless a heuristic is used to terminate the search prematurely.
+
+ Set PART->LEFT_MINIMAL to nonzero iff the minimal edit script for the
+ left half of the partition is known; similarly for PART->RIGHT_MINIMAL.
This function assumes that the first lines of the specified portions
of the two files do not match, and likewise that the last lines do not
match. The caller must trim matching lines from the beginning and end
of the portions it is going to specify.
- Note that if we return the "wrong" diagonal value, or if
- the value of bdiag at that diagonal is "wrong",
+ If we return the "wrong" partitions,
the worst this can do is cause suboptimal diff output.
It cannot cause incorrect diff output. */
static int
-diag (xoff, xlim, yoff, ylim, cost)
- int xoff, xlim, yoff, ylim;
- int *cost;
+diag (xoff, xlim, yoff, ylim, minimal, part)
+ int xoff, xlim, yoff, ylim, minimal;
+ struct partition *part;
{
int *const fd = fdiag; /* Give the compiler a chance. */
int *const bd = bdiag; /* Additional help for the compiler. */
- int *const xv = xvec; /* Still more help for the compiler. */
- int *const yv = yvec; /* And more and more . . . */
- const int dmin = xoff - ylim; /* Minimum valid diagonal. */
- const int dmax = xlim - yoff; /* Maximum valid diagonal. */
- const int fmid = xoff - yoff; /* Center diagonal of top-down search. */
- const int bmid = xlim - ylim; /* Center diagonal of bottom-up search. */
+ int const *const xv = xvec; /* Still more help for the compiler. */
+ int const *const yv = yvec; /* And more and more . . . */
+ int const dmin = xoff - ylim; /* Minimum valid diagonal. */
+ int const dmax = xlim - yoff; /* Maximum valid diagonal. */
+ int const fmid = xoff - yoff; /* Center diagonal of top-down search. */
+ int const bmid = xlim - ylim; /* Center diagonal of bottom-up search. */
int fmin = fmid, fmax = fmid; /* Limits of top-down search. */
int bmin = bmid, bmax = bmid; /* Limits of bottom-up search. */
int c; /* Cost. */
@@ -112,17 +138,19 @@ diag (xoff, xlim, yoff, ylim, cost)
y = x - d;
while (x < xlim && y < ylim && xv[x] == yv[y])
++x, ++y;
- if (x - oldx > 20)
+ if (x - oldx > SNAKE_LIMIT)
big_snake = 1;
fd[d] = x;
- if (odd && bmin <= d && d <= bmax && bd[d] <= fd[d])
+ if (odd && bmin <= d && d <= bmax && bd[d] <= x)
{
- *cost = 2 * c - 1;
- return d;
+ part->xmid = x;
+ part->ymid = y;
+ part->lo_minimal = part->hi_minimal = 1;
+ return 2 * c - 1;
}
}
- /* Similar extend the bottom-up search. */
+ /* Similarly extend the bottom-up search. */
bmin > dmin ? bd[--bmin - 1] = INT_MAX : ++bmin;
bmax < dmax ? bd[++bmax + 1] = INT_MAX : --bmax;
for (d = bmax; d >= bmin; d -= 2)
@@ -137,16 +165,21 @@ diag (xoff, xlim, yoff, ylim, cost)
y = x - d;
while (x > xoff && y > yoff && xv[x - 1] == yv[y - 1])
--x, --y;
- if (oldx - x > 20)
+ if (oldx - x > SNAKE_LIMIT)
big_snake = 1;
bd[d] = x;
- if (!odd && fmin <= d && d <= fmax && bd[d] <= fd[d])
+ if (!odd && fmin <= d && d <= fmax && x <= fd[d])
{
- *cost = 2 * c;
- return d;
+ part->xmid = x;
+ part->ymid = y;
+ part->lo_minimal = part->hi_minimal = 1;
+ return 2 * c;
}
}
+ if (minimal)
+ continue;
+
/* Heuristic: check occasionally for a diagonal that has made
lots of progress compared with the edit distance.
If we have any such, find the one that has made the most
@@ -158,73 +191,134 @@ diag (xoff, xlim, yoff, ylim, cost)
if (c > 200 && big_snake && heuristic)
{
int best;
- int bestpos;
best = 0;
for (d = fmax; d >= fmin; d -= 2)
{
int dd = d - fmid;
- if ((fd[d] - xoff)*2 - dd > 12 * (c + (dd > 0 ? dd : -dd)))
+ int x = fd[d];
+ int y = x - d;
+ int v = (x - xoff) * 2 - dd;
+ if (v > 12 * (c + (dd < 0 ? -dd : dd)))
{
- if (fd[d] * 2 - dd > best
- && fd[d] - xoff > 20
- && fd[d] - d - yoff > 20)
+ if (v > best
+ && xoff + SNAKE_LIMIT <= x && x < xlim
+ && yoff + SNAKE_LIMIT <= y && y < ylim)
{
- int k;
- int x = fd[d];
-
/* We have a good enough best diagonal;
now insist that it end with a significant snake. */
- for (k = 1; k <= 20; k++)
- if (xvec[x - k] != yvec[x - d - k])
- break;
-
- if (k == 21)
- {
- best = fd[d] * 2 - dd;
- bestpos = d;
- }
+ int k;
+
+ for (k = 1; xv[x - k] == yv[y - k]; k++)
+ if (k == SNAKE_LIMIT)
+ {
+ best = v;
+ part->xmid = x;
+ part->ymid = y;
+ break;
+ }
}
}
}
if (best > 0)
{
- *cost = 2 * c - 1;
- return bestpos;
+ part->lo_minimal = 1;
+ part->hi_minimal = 0;
+ return 2 * c - 1;
}
best = 0;
for (d = bmax; d >= bmin; d -= 2)
{
int dd = d - bmid;
- if ((xlim - bd[d])*2 + dd > 12 * (c + (dd > 0 ? dd : -dd)))
+ int x = bd[d];
+ int y = x - d;
+ int v = (xlim - x) * 2 + dd;
+ if (v > 12 * (c + (dd < 0 ? -dd : dd)))
{
- if ((xlim - bd[d]) * 2 + dd > best
- && xlim - bd[d] > 20
- && ylim - (bd[d] - d) > 20)
+ if (v > best
+ && xoff < x && x <= xlim - SNAKE_LIMIT
+ && yoff < y && y <= ylim - SNAKE_LIMIT)
{
/* We have a good enough best diagonal;
now insist that it end with a significant snake. */
int k;
- int x = bd[d];
-
- for (k = 0; k < 20; k++)
- if (xvec[x + k] != yvec[x - d + k])
- break;
- if (k == 20)
- {
- best = (xlim - bd[d]) * 2 + dd;
- bestpos = d;
- }
+
+ for (k = 0; xv[x + k] == yv[y + k]; k++)
+ if (k == SNAKE_LIMIT - 1)
+ {
+ best = v;
+ part->xmid = x;
+ part->ymid = y;
+ break;
+ }
}
}
}
if (best > 0)
{
- *cost = 2 * c - 1;
- return bestpos;
+ part->lo_minimal = 0;
+ part->hi_minimal = 1;
+ return 2 * c - 1;
}
}
+
+ /* Heuristic: if we've gone well beyond the call of duty,
+ give up and report halfway between our best results so far. */
+ if (c >= too_expensive)
+ {
+ int fxybest, fxbest;
+ int bxybest, bxbest;
+
+ fxbest = bxbest = 0; /* Pacify `gcc -Wall'. */
+
+ /* Find forward diagonal that maximizes X + Y. */
+ fxybest = -1;
+ for (d = fmax; d >= fmin; d -= 2)
+ {
+ int x = min (fd[d], xlim);
+ int y = x - d;
+ if (ylim < y)
+ x = ylim + d, y = ylim;
+ if (fxybest < x + y)
+ {
+ fxybest = x + y;
+ fxbest = x;
+ }
+ }
+
+ /* Find backward diagonal that minimizes X + Y. */
+ bxybest = INT_MAX;
+ for (d = bmax; d >= bmin; d -= 2)
+ {
+ int x = max (xoff, bd[d]);
+ int y = x - d;
+ if (y < yoff)
+ x = yoff + d, y = yoff;
+ if (x + y < bxybest)
+ {
+ bxybest = x + y;
+ bxbest = x;
+ }
+ }
+
+ /* Use the better of the two diagonals. */
+ if ((xlim + ylim) - bxybest < fxybest - (xoff + yoff))
+ {
+ part->xmid = fxbest;
+ part->ymid = fxybest - fxbest;
+ part->lo_minimal = 1;
+ part->hi_minimal = 0;
+ }
+ else
+ {
+ part->xmid = bxbest;
+ part->ymid = bxybest - bxbest;
+ part->lo_minimal = 0;
+ part->hi_minimal = 1;
+ }
+ return 2 * c - 1;
+ }
}
}
@@ -237,19 +331,25 @@ diag (xoff, xlim, yoff, ylim, cost)
The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1.
Note that XLIM, YLIM are exclusive bounds.
- All line numbers are origin-0 and discarded lines are not counted. */
+ All line numbers are origin-0 and discarded lines are not counted.
+
+ If MINIMAL is nonzero, find a minimal difference no matter how
+ expensive it is. */
static void
-compareseq (xoff, xlim, yoff, ylim)
- int xoff, xlim, yoff, ylim;
+compareseq (xoff, xlim, yoff, ylim, minimal)
+ int xoff, xlim, yoff, ylim, minimal;
{
+ int * const xv = xvec; /* Help the compiler. */
+ int * const yv = yvec;
+
/* Slide down the bottom initial diagonal. */
- while (xoff < xlim && yoff < ylim && xvec[xoff] == yvec[yoff])
+ while (xoff < xlim && yoff < ylim && xv[xoff] == yv[yoff])
++xoff, ++yoff;
/* Slide up the top initial diagonal. */
- while (xlim > xoff && ylim > yoff && xvec[xlim - 1] == yvec[ylim - 1])
+ while (xlim > xoff && ylim > yoff && xv[xlim - 1] == yv[ylim - 1])
--xlim, --ylim;
-
+
/* Handle simple cases. */
if (xoff == xlim)
while (yoff < ylim)
@@ -259,13 +359,12 @@ compareseq (xoff, xlim, yoff, ylim)
files[0].changed_flag[files[0].realindexes[xoff++]] = 1;
else
{
- int c, d, f, b;
+ int c;
+ struct partition part;
/* Find a point of correspondence in the middle of the files. */
- d = diag (xoff, xlim, yoff, ylim, &c);
- f = fdiag[d];
- b = bdiag[d];
+ c = diag (xoff, xlim, yoff, ylim, minimal, &part);
if (c == 1)
{
@@ -277,21 +376,17 @@ compareseq (xoff, xlim, yoff, ylim)
#if 0
/* The two subsequences differ by a single insert or delete;
record it and we are done. */
- if (d < xoff - yoff)
- files[1].changed_flag[files[1].realindexes[b - d - 1]] = 1;
+ if (part.xmid - part.ymid < xoff - yoff)
+ files[1].changed_flag[files[1].realindexes[part.ymid - 1]] = 1;
else
- files[0].changed_flag[files[0].realindexes[b]] = 1;
+ files[0].changed_flag[files[0].realindexes[part.xmid]] = 1;
#endif
}
else
{
- /* Use that point to split this problem into two subproblems. */
- compareseq (xoff, b, yoff, b - d);
- /* This used to use f instead of b,
- but that is incorrect!
- It is not necessarily the case that diagonal d
- has a snake from b to f. */
- compareseq (b, xlim, b - d, ylim);
+ /* Use the partitions to split this problem into subproblems. */
+ compareseq (xoff, part.xmid, yoff, part.ymid, part.lo_minimal);
+ compareseq (part.xmid, xlim, part.ymid, ylim, part.hi_minimal);
}
}
}
@@ -308,7 +403,7 @@ compareseq (xoff, xlim, yoff, ylim)
When we discard a line, we also mark it as a deletion or insertion
so that it will be printed in the output. */
-void
+static void
discard_confusing_lines (filevec)
struct file_data filevec[];
{
@@ -341,10 +436,12 @@ discard_confusing_lines (filevec)
/* Set up tables of which lines are going to be discarded. */
- discarded[0] = (char *) xmalloc (filevec[0].buffered_lines
- + filevec[1].buffered_lines);
+ discarded[0] = xmalloc (sizeof (char)
+ * (filevec[0].buffered_lines
+ + filevec[1].buffered_lines));
discarded[1] = discarded[0] + filevec[0].buffered_lines;
- bzero (discarded[0], filevec[0].buffered_lines + filevec[1].buffered_lines);
+ bzero (discarded[0], sizeof (char) * (filevec[0].buffered_lines
+ + filevec[1].buffered_lines));
/* Mark to be discarded each line that matches no line of the other file.
If a line matches many lines, mark it as provisionally discardable. */
@@ -509,16 +606,15 @@ discard_confusing_lines (filevec)
free (equiv_count[0]);
}
-/* Adjust inserts/deletes of blank lines to join changes
+/* Adjust inserts/deletes of identical lines to join changes
as much as possible.
- We do something when a run of changed lines include a blank
- line at one end and have an excluded blank line at the other.
- We are free to choose which blank line is included.
- `compareseq' always chooses the one at the beginning,
- but usually it is cleaner to consider the following blank line
- to be the "change". The only exception is if the preceding blank line
- would join this change to other changes. */
+ We do something when a run of changed lines include a
+ line at one end and have an excluded, identical line at the other.
+ We are free to choose which identical line is included.
+ `compareseq' usually chooses the one at the beginning,
+ but usually it is cleaner to consider the following identical line
+ to be the "change". */
int inhibit;
@@ -534,16 +630,15 @@ shift_boundaries (filevec)
for (f = 0; f < 2; f++)
{
char *changed = filevec[f].changed_flag;
- char *other_changed = filevec[1-f].changed_flag;
+ char const *other_changed = filevec[1-f].changed_flag;
+ int const *equivs = filevec[f].equivs;
int i = 0;
int j = 0;
int i_end = filevec[f].buffered_lines;
- int preceding = -1;
- int other_preceding = -1;
while (1)
{
- int start, other_start;
+ int runlength, start, corresponding;
/* Scan forwards to find beginning of another run of changes.
Also keep track of the corresponding point in the other file. */
@@ -551,9 +646,7 @@ shift_boundaries (filevec)
while (i < i_end && changed[i] == 0)
{
while (other_changed[j++])
- /* Non-corresponding lines in the other file
- will count as the preceding batch of changes. */
- other_preceding = j;
+ continue;
i++;
}
@@ -561,42 +654,67 @@ shift_boundaries (filevec)
break;
start = i;
- other_start = j;
- while (1)
+ /* Find the end of this run of changes. */
+
+ while (changed[++i])
+ continue;
+ while (other_changed[j])
+ j++;
+
+ do
{
- /* Now find the end of this run of changes. */
+ /* Record the length of this run of changes, so that
+ we can later determine whether the run has grown. */
+ runlength = i - start;
- while (changed[++i] != 0)
- ;
+ /* Move the changed region back, so long as the
+ previous unchanged line matches the last changed one.
+ This merges with previous changed regions. */
- /* If the first changed line matches the following unchanged one,
- and this run does not follow right after a previous run,
- and there are no lines deleted from the other file here,
- then classify the first changed line as unchanged
- and the following line as changed in its place. */
+ while (start && equivs[start - 1] == equivs[i - 1])
+ {
+ changed[--start] = 1;
+ changed[--i] = 0;
+ while (changed[start - 1])
+ start--;
+ while (other_changed[--j])
+ continue;
+ }
+
+ /* Set CORRESPONDING to the end of the changed run, at the last
+ point where it corresponds to a changed run in the other file.
+ CORRESPONDING == I_END means no such point has been found. */
+ corresponding = other_changed[j - 1] ? i : i_end;
- /* You might ask, how could this run follow right after another?
- Only because the previous run was shifted here. */
+ /* Move the changed region forward, so long as the
+ first changed line matches the following unchanged one.
+ This merges with following changed regions.
+ Do this second, so that if there are no merges,
+ the changed region is moved forward as far as possible. */
- if (i != i_end
- && files[f].equivs[start] == files[f].equivs[i]
- && !other_changed[j]
- && !(start == preceding || other_start == other_preceding))
+ while (i != i_end && equivs[start] == equivs[i])
{
changed[start++] = 0;
- changed[i] = 1;
- /* Since one line-that-matches is now before this run
- instead of after, we must advance in the other file
- to keep in synch. */
- ++j;
+ changed[i++] = 1;
+ while (changed[i])
+ i++;
+ while (other_changed[++j])
+ corresponding = i;
}
- else
- break;
}
+ while (runlength != i - start);
+
+ /* If possible, move the fully-merged run of changes
+ back to a corresponding run in the other file. */
- preceding = i;
- other_preceding = j;
+ while (corresponding < i)
+ {
+ changed[--start] = 1;
+ changed[--i] = 0;
+ while (other_changed[--j])
+ continue;
+ }
}
}
}
@@ -629,7 +747,7 @@ add_change (line0, line1, deleted, inserted, old)
static struct change *
build_reverse_script (filevec)
- struct file_data filevec[];
+ struct file_data const filevec[];
{
struct change *script = 0;
char *changed0 = filevec[0].changed_flag;
@@ -667,7 +785,7 @@ build_reverse_script (filevec)
static struct change *
build_script (filevec)
- struct file_data filevec[];
+ struct file_data const filevec[];
{
struct change *script = 0;
char *changed0 = filevec[0].changed_flag;
@@ -697,6 +815,18 @@ build_script (filevec)
return script;
}
+/* If CHANGES, briefly report that two files differed. */
+static void
+briefly_report (changes, filevec)
+ int changes;
+ struct file_data const filevec[];
+{
+ if (changes)
+ message (no_details_flag ? "Files %s and %s differ\n"
+ : "Binary files %s and %s differ\n",
+ filevec[0].name, filevec[1].name);
+}
+
/* Report the differences of two files. DEPTH is the current directory
depth. */
int
@@ -714,9 +844,10 @@ diff_2_files (filevec, depth)
/* If we have detected that either file is binary,
compare the two files as binary. This can happen
only when the first chunk is read.
- Also, -q means treat all files as binary. */
+ Also, --brief without any --ignore-* options means
+ we can speed things up by treating the files as binary. */
- if (read_files (filevec))
+ if (read_files (filevec, no_details_flag & ~ignore_some_changes))
{
/* Files with different lengths must be different. */
if (filevec[0].stat.st_size != filevec[1].stat.st_size
@@ -732,8 +863,8 @@ diff_2_files (filevec, depth)
/* Scan both files, a buffer at a time, looking for a difference. */
{
/* Allocate same-sized buffers for both files. */
- int buffer_size = max (STAT_BLOCKSIZE (filevec[0].stat),
- STAT_BLOCKSIZE (filevec[1].stat));
+ size_t buffer_size = buffer_lcm (STAT_BLOCKSIZE (filevec[0].stat),
+ STAT_BLOCKSIZE (filevec[1].stat));
for (i = 0; i < 2; i++)
filevec[i].buffer = xrealloc (filevec[i].buffer, buffer_size);
@@ -757,9 +888,10 @@ diff_2_files (filevec, depth)
/* If the buffers differ, the files differ. */
if (filevec[0].buffered_chars != filevec[1].buffered_chars
- || bcmp (filevec[0].buffer,
- filevec[1].buffer,
- filevec[0].buffered_chars) != 0)
+ || (filevec[0].buffered_chars != 0
+ && memcmp (filevec[0].buffer,
+ filevec[1].buffer,
+ filevec[0].buffered_chars) != 0))
{
changes = 1;
break;
@@ -774,10 +906,7 @@ diff_2_files (filevec, depth)
}
}
- if (changes)
- message (no_details_flag ? "Files %s and %s differ\n"
- : "Binary files %s and %s differ\n",
- filevec[0].name, filevec[1].name);
+ briefly_report (changes, filevec);
}
else
{
@@ -786,11 +915,9 @@ diff_2_files (filevec, depth)
is an insertion or deletion.
Allocate an extra element, always zero, at each end of each vector. */
- filevec[0].changed_flag = (char *) xmalloc (filevec[0].buffered_lines
- + filevec[1].buffered_lines
- + 4);
- bzero (filevec[0].changed_flag, filevec[0].buffered_lines
- + filevec[1].buffered_lines + 4);
+ size_t s = filevec[0].buffered_lines + filevec[1].buffered_lines + 4;
+ filevec[0].changed_flag = xmalloc (s);
+ bzero (filevec[0].changed_flag, s);
filevec[0].changed_flag++;
filevec[1].changed_flag = filevec[0].changed_flag
+ filevec[0].buffered_lines + 2;
@@ -812,11 +939,19 @@ diff_2_files (filevec, depth)
fdiag += filevec[1].nondiscarded_lines + 1;
bdiag += filevec[1].nondiscarded_lines + 1;
+ /* Set TOO_EXPENSIVE to be approximate square root of input size,
+ bounded below by 256. */
+ too_expensive = 1;
+ for (i = filevec[0].nondiscarded_lines + filevec[1].nondiscarded_lines;
+ i != 0; i >>= 2)
+ too_expensive <<= 1;
+ too_expensive = max (256, too_expensive);
+
files[0] = filevec[0];
files[1] = filevec[1];
compareseq (0, filevec[0].nondiscarded_lines,
- 0, filevec[1].nondiscarded_lines);
+ 0, filevec[1].nondiscarded_lines, no_discards);
free (fdiag - (filevec[1].nondiscarded_lines + 1));
@@ -833,50 +968,7 @@ diff_2_files (filevec, depth)
else
script = build_script (filevec);
- if (script || ! no_diff_means_no_output)
- {
- /* Record info for starting up output,
- to be used if and when we have some output to print. */
- setup_output (files[0].name, files[1].name, depth);
-
- switch (output_style)
- {
- case OUTPUT_CONTEXT:
- print_context_script (script, 0);
- break;
-
- case OUTPUT_UNIFIED:
- print_context_script (script, 1);
- break;
-
- case OUTPUT_ED:
- print_ed_script (script);
- break;
-
- case OUTPUT_FORWARD_ED:
- pr_forward_ed_script (script);
- break;
-
- case OUTPUT_RCS:
- print_rcs_script (script);
- break;
-
- case OUTPUT_NORMAL:
- print_normal_script (script);
- break;
-
- case OUTPUT_IFDEF:
- print_ifdef_script (script);
- break;
-
- case OUTPUT_SDIFF:
- print_sdiff_script (script);
- }
-
- finish_output ();
- }
-
- /* Set CHANGES if we had any diffs that were printed.
+ /* Set CHANGES if we had any diffs.
If some changes are ignored, we must scan the script to decide. */
if (ignore_blank_lines_flag || ignore_regexp_list)
{
@@ -895,9 +987,9 @@ diff_2_files (filevec, depth)
/* Disconnect them from the rest of the changes, making them
a hunk, and remember the rest for next iteration. */
next = end->link;
- end->link = NULL;
+ end->link = 0;
- /* Determine whether this hunk was printed. */
+ /* Determine whether this hunk is really a difference. */
analyze_hunk (this, &first0, &last0, &first1, &last1,
&deletes, &inserts);
@@ -911,6 +1003,54 @@ diff_2_files (filevec, depth)
else
changes = (script != 0);
+ if (no_details_flag)
+ briefly_report (changes, filevec);
+ else
+ {
+ if (changes || ! no_diff_means_no_output)
+ {
+ /* Record info for starting up output,
+ to be used if and when we have some output to print. */
+ setup_output (files[0].name, files[1].name, depth);
+
+ switch (output_style)
+ {
+ case OUTPUT_CONTEXT:
+ print_context_script (script, 0);
+ break;
+
+ case OUTPUT_UNIFIED:
+ print_context_script (script, 1);
+ break;
+
+ case OUTPUT_ED:
+ print_ed_script (script);
+ break;
+
+ case OUTPUT_FORWARD_ED:
+ pr_forward_ed_script (script);
+ break;
+
+ case OUTPUT_RCS:
+ print_rcs_script (script);
+ break;
+
+ case OUTPUT_NORMAL:
+ print_normal_script (script);
+ break;
+
+ case OUTPUT_IFDEF:
+ print_ifdef_script (script);
+ break;
+
+ case OUTPUT_SDIFF:
+ print_sdiff_script (script);
+ }
+
+ finish_output ();
+ }
+ }
+
free (filevec[0].undiscarded);
free (filevec[0].changed_flag - 1);
diff --git a/gnu/usr.bin/diff/context.c b/gnu/usr.bin/diff/context.c
index 9b8fc3e..5b4b73f 100644
--- a/gnu/usr.bin/diff/context.c
+++ b/gnu/usr.bin/diff/context.c
@@ -1,5 +1,5 @@
/* Context-format output routines for GNU DIFF.
- Copyright (C) 1988, 89, 91, 92 Free Software Foundation, Inc.
+ Copyright (C) 1988, 89, 91, 92, 93 Free Software Foundation, Inc.
This file is part of GNU DIFF.
@@ -19,11 +19,14 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "diff.h"
-static void pr_context_hunk ();
-static void pr_unidiff_hunk ();
-static struct change *find_hunk ();
-static void mark_ignorable ();
-static void find_function ();
+static struct change *find_hunk PARAMS((struct change *));
+static void find_function PARAMS((struct file_data const *, int, char const **, size_t *));
+static void mark_ignorable PARAMS((struct change *));
+static void pr_context_hunk PARAMS((struct change *));
+static void pr_unidiff_hunk PARAMS((struct change *));
+static void print_context_label PARAMS ((char const *, struct file_data *, char const *));
+static void print_context_number_range PARAMS((struct file_data const *, int, int));
+static void print_unidiff_number_range PARAMS((struct file_data const *, int, int));
/* Last place find_function started searching from. */
static int find_function_last_search;
@@ -35,24 +38,23 @@ static int find_function_last_match;
static void
print_context_label (mark, inf, label)
- const char *mark;
+ char const *mark;
struct file_data *inf;
- const char *label;
+ char const *label;
{
if (label)
fprintf (outfile, "%s %s\n", mark, label);
- else if (inf->stat.st_mtime)
- fprintf (outfile, "%s %s\t%s", mark, inf->name, ctime(&inf->stat.st_mtime));
else
- /* Don't pretend that standard input is ancient. */
- fprintf (outfile, "%s %s\n", mark, inf->name);
+ /* See Posix.2 section 4.17.6.1.4 for this format. */
+ fprintf (outfile, "%s %s\t%s",
+ mark, inf->name, ctime (&inf->stat.st_mtime));
}
/* Print a header for a context diff, with the file names and dates. */
void
print_context_header (inf, unidiff_flag)
- struct file_data *inf;
+ struct file_data inf[];
int unidiff_flag;
{
if (unidiff_flag)
@@ -100,7 +102,7 @@ print_context_script (script, unidiff_flag)
static void
print_context_number_range (file, a, b)
- struct file_data *file;
+ struct file_data const *file;
int a, b;
{
int trans_a, trans_b;
@@ -128,9 +130,9 @@ pr_context_hunk (hunk)
{
int first0, last0, first1, last1, show_from, show_to, i;
struct change *next;
- char *prefix;
- const char *function;
- int function_length;
+ char const *prefix;
+ char const *function;
+ size_t function_length;
FILE *out;
/* Determine range of line numbers involved in each file. */
@@ -234,7 +236,7 @@ pr_context_hunk (hunk)
static void
print_unidiff_number_range (file, a, b)
- struct file_data *file;
+ struct file_data const *file;
int a, b;
{
int trans_a, trans_b;
@@ -262,8 +264,8 @@ pr_unidiff_hunk (hunk)
{
int first0, last0, first1, last1, show_from, show_to, i, j, k;
struct change *next;
- char *function;
- int function_length;
+ char const *function;
+ size_t function_length;
FILE *out;
/* Determine range of line numbers involved in each file. */
@@ -317,7 +319,7 @@ pr_unidiff_hunk (hunk)
if (!next || i < next->line0)
{
putc (tab_align_flag ? '\t' : ' ', out);
- print_1_line ((char *)0, &files[0].linbuf[i++]);
+ print_1_line (0, &files[0].linbuf[i++]);
j++;
}
else
@@ -330,7 +332,7 @@ pr_unidiff_hunk (hunk)
putc ('-', out);
if (tab_align_flag)
putc ('\t', out);
- print_1_line ((char *)0, &files[0].linbuf[i++]);
+ print_1_line (0, &files[0].linbuf[i++]);
}
/* Then output the inserted part. */
@@ -341,7 +343,7 @@ pr_unidiff_hunk (hunk)
putc ('+', out);
if (tab_align_flag)
putc ('\t', out);
- print_1_line ((char *)0, &files[1].linbuf[j++]);
+ print_1_line (0, &files[1].linbuf[j++]);
}
/* We're done with this hunk, so on to the next! */
@@ -424,10 +426,10 @@ mark_ignorable (script)
static void
find_function (file, linenum, linep, lenp)
- struct file_data *file;
+ struct file_data const *file;
int linenum;
- const char **linep;
- int *lenp;
+ char const **linep;
+ size_t *lenp;
{
int i = linenum;
int last = find_function_last_search;
@@ -437,8 +439,8 @@ find_function (file, linenum, linep, lenp)
{
/* See if this line is what we want. */
struct regexp_list *r;
- const char *line = file->linbuf[i];
- int len = file->linbuf[i + 1] - line;
+ char const *line = file->linbuf[i];
+ size_t len = file->linbuf[i + 1] - line;
for (r = function_regexp_list; r; r = r->next)
if (0 <= re_search (&r->buf, line, len, 0, len, 0))
diff --git a/gnu/usr.bin/diff/diff.1 b/gnu/usr.bin/diff/diff.1
new file mode 100644
index 0000000..e16f6d9
--- /dev/null
+++ b/gnu/usr.bin/diff/diff.1
@@ -0,0 +1,473 @@
+.TH DIFF 1 "22sep1993" "GNU Tools" "GNU Tools"
+.SH NAME
+diff \- find differences between two files
+.SH SYNOPSIS
+.B diff
+[options] from-file to-file
+.SH DESCRIPTION
+In the simplest case,
+.I diff
+compares the contents of the two files
+.I from-file
+and
+.IR to-file .
+A file name of
+.B \-
+stands for
+text read from the standard input. As a special case,
+.B "diff \- \-"
+compares a copy of standard input to itself.
+
+If
+.I from-file
+is a directory and
+.I to-file
+is not,
+.I diff
+compares the file in
+.I from-file
+whose file name is that of
+.IR to-file ,
+and vice versa. The non-directory file must not be
+.BR \- .
+
+If both
+.I from-file
+and
+.I to-file
+are directories,
+.I diff
+compares corresponding files in both directories, in
+alphabetical order; this comparison is not recursive unless the
+.B \-r
+or
+.B \-\-recursive
+option is given.
+.I diff
+never
+compares the actual contents of a directory as if it were a file. The
+file that is fully specified may not be standard input, because standard
+input is nameless and the notion of ``file with the same name'' does not
+apply.
+
+.B diff
+options begin with
+.BR \- ,
+so normally
+.I from-file
+and
+.I to-file
+may not begin with
+.BR \- .
+However,
+.B \-\-
+as an
+argument by itself treats the remaining arguments as file names even if
+they begin with
+.BR \- .
+.SS Options
+Below is a summary of all of the options that GNU
+.I diff
+accepts.
+Most options have two equivalent names, one of which is a single letter
+preceded by
+.BR \- ,
+and the other of which is a long name preceded by
+.BR \-\- .
+Multiple single letter options (unless they take an
+argument) can be combined into a single command line word:
+.B \-ac
+is
+equivalent to
+.BR "\-a \-c" .
+Long named options can be abbreviated to
+any unique prefix of their name. Brackets
+.RB ( [
+and
+.BR ] )
+indicate that an
+option takes an optional argument.
+.TP
+.BI \- lines
+Show
+.I lines
+(an integer) lines of context. This option does not
+specify an output format by itself; it has no effect unless it is
+combined with
+.B \-c
+or
+.BR \-u .
+This option is obsolete. For proper
+operation,
+.I patch
+typically needs at least two lines of context.
+.TP
+.B \-a
+Treat all files as text and compare them line-by-line, even if they
+do not seem to be text.
+.TP
+.B \-b
+Ignore changes in amount of white space.
+.TP
+.B \-B
+Ignore changes that just insert or delete blank lines.
+.TP
+.B \-\-brief
+Report only whether the files differ, not the details of the
+differences.
+.TP
+.B \-c
+Use the context output format.
+.TP
+.BI "\-C " lines
+.br
+.ns
+.TP
+.BI \-\-context[= lines ]
+Use the context output format, showing
+.I lines
+(an integer) lines of
+context, or three if
+.I lines
+is not given.
+For proper operation,
+.I patch
+typically needs at least two lines of
+context.
+.TP
+.BI \-\-changed\-group\-format= format
+Use
+.I format
+to output a line group containing differing lines from
+both files in if-then-else format.
+.TP
+.B \-d
+Change the algorithm to perhaps find a smaller set of changes. This makes
+.I diff
+slower (sometimes much slower).
+.TP
+.BI "\-D " name
+Make merged if-then-else format output, conditional on the preprocessor
+macro
+.IR name .
+.TP
+.B \-e
+.br
+.ns
+.TP
+.B \-\-ed
+Make output that is a valid
+.I ed
+script.
+.TP
+.BI \-\-exclude= pattern
+When comparing directories, ignore files and subdirectories whose basenames
+match
+.IR pattern .
+.TP
+.BI \-\-exclude\-from= file
+When comparing directories, ignore files and subdirectories whose basenames
+match any pattern contained in
+.IR file .
+.TP
+.B \-\-expand\-tabs
+Expand tabs to spaces in the output, to preserve the alignment of tabs
+in the input files.
+.TP
+.B \-f
+Make output that looks vaguely like an
+.I ed
+script but has changes
+in the order they appear in the file.
+.TP
+.BI "\-F " regexp
+In context and unified format, for each hunk of differences, show some
+of the last preceding line that matches
+.IR regexp .
+.TP
+.B \-\-forward\-ed
+Make output that looks vaguely like an
+.B ed
+script but has changes
+in the order they appear in the file.
+.TP
+.B \-h
+This option currently has no effect; it is present for Unix
+compatibility.
+.TP
+.B \-H
+Use heuristics to speed handling of large files that have numerous
+scattered small changes.
+.TP
+.BI \-\-horizon\-lines= lines
+Do not discard the last
+.I lines
+lines of the common prefix
+and the first
+.I lines
+lines of the common suffix.
+.TP
+.B \-i
+Ignore changes in case; consider upper- and lower-case letters
+equivalent.
+.TP
+.BI "\-I " regexp
+Ignore changes that just insert or delete lines that match
+.IR regexp .
+.TP
+.BI \-\-ifdef= name
+Make merged if-then-else format output, conditional on the preprocessor
+macro
+.IR name .
+.TP
+.B \-\-ignore\-all\-space
+Ignore white space when comparing lines.
+.TP
+.B \-\-ignore\-blank\-lines
+Ignore changes that just insert or delete blank lines.
+.TP
+.B \-\-ignore\-case
+Ignore changes in case; consider upper- and lower-case to be the same.
+.TP
+.BI \-\-ignore\-matching\-lines= regexp
+Ignore changes that just insert or delete lines that match
+.IR regexp .
+.TP
+.B \-\-ignore\-space\-change
+Ignore changes in amount of white space.
+.TP
+.B \-\-initial\-tab
+Output a tab rather than a space before the text of a line in normal or
+context format. This causes the alignment of tabs in the line to look
+normal.
+.TP
+.B \-l
+Pass the output through
+.I pr
+to paginate it.
+.TP
+.BI "\-L " label
+.br
+.ns
+.TP
+.BI \-\-label= label
+Use
+.I label
+instead of the file name in the context format
+and unified format
+headers.
+.TP
+.B \-\-left\-column
+Print only the left column of two common lines in side by side format.
+.TP
+.BI \-\-line\-format= format
+Use
+.I format
+to output all input lines in in-then-else format.
+.TP
+.B \-\-minimal
+Change the algorithm to perhaps find a smaller set of changes. This
+makes
+.I diff
+slower (sometimes much slower).
+.TP
+.B \-n
+Output RCS-format diffs; like
+.B \-f
+except that each command
+specifies the number of lines affected.
+.TP
+.B \-N
+.br
+.ns
+.TP
+.B \-\-new\-file
+In directory comparison, if a file is found in only one directory,
+treat it as present but empty in the other directory.
+.TP
+.BI \-\-new\-group\-format= format
+Use
+.I format
+to output a group of lines taken from just the second
+file in if-then-else format.
+.TP
+.BI \-\-new\-line\-format= format
+Use
+.I format
+to output a line taken from just the second file in
+if-then-else format.
+.TP
+.BI \-\-old\-group\-format= format
+Use
+.I format
+to output a group of lines taken from just the first
+file in if-then-else format.
+.TP
+.BI \-\-old\-line\-format= format
+Use
+.I format
+to output a line taken from just the first file in
+if-then-else format.
+.TP
+.B \-p
+Show which C function each change is in.
+.TP
+.B \-P
+When comparing directories, if a file appears only in the second
+directory of the two, treat it as present but empty in the other.
+.TP
+.B \-\-paginate
+Pass the output through
+.I pr
+to paginate it.
+.TP
+.B \-q
+Report only whether the files differ, not the details of the
+differences.
+.TP
+.B \-r
+When comparing directories, recursively compare any subdirectories
+found.
+.TP
+.B \-\-rcs
+Output RCS-format diffs; like
+.B \-f
+except that each command
+specifies the number of lines affected.
+.TP
+.B \-\-recursive
+When comparing directories, recursively compare any subdirectories
+found.
+.TP
+.B \-\-report\-identical\-files
+.br
+.ns
+.TP
+.B \-s
+Report when two files are the same.
+.TP
+.BI "\-S " file
+When comparing directories, start with the file
+.IR file .
+This is
+used for resuming an aborted comparison.
+.TP
+.B \-\-sdiff\-merge\-assist
+Print extra information to help
+.IR sdiff .
+.I sdiff
+uses this
+option when it runs
+.IR diff .
+This option is not intended for users
+to use directly.
+.TP
+.B \-\-show\-c\-function
+Show which C function each change is in.
+.TP
+.BI \-\-show\-function\-line= regexp
+In context and unified format, for each hunk of differences, show some
+of the last preceding line that matches
+.IR regexp .
+.TP
+.B \-\-side\-by\-side
+Use the side by side output format.
+.TP
+.B \-\-speed\-large\-files
+Use heuristics to speed handling of large files that have numerous
+scattered small changes.
+.TP
+.BI \-\-starting\-file= file
+When comparing directories, start with the file
+.IR file .
+This is
+used for resuming an aborted comparison.
+.TP
+.B \-\-suppress\-common\-lines
+Do not print common lines in side by side format.
+.TP
+.B \-t
+Expand tabs to spaces in the output, to preserve the alignment of tabs
+in the input files.
+.TP
+.B \-T
+Output a tab rather than a space before the text of a line in normal or
+context format. This causes the alignment of tabs in the line to look
+normal.
+.TP
+.B \-\-text
+Treat all files as text and compare them line-by-line, even if they
+do not appear to be text.
+.TP
+.B \-u
+Use the unified output format.
+.TP
+.BI \-\-unchanged\-group\-format= format
+Use
+.I format
+to output a group of common lines taken from both files
+in if-then-else format.
+.TP
+.BI \-\-unchanged\-line\-format= format
+Use
+.I format
+to output a line common to both files in if-then-else
+format.
+.TP
+.B \-\-unidirectional\-new\-file
+When comparing directories, if a file appears only in the second
+directory of the two, treat it as present but empty in the other.
+.TP
+.BI "\-U " lines
+.br
+.ns
+.TP
+.BI \-\-unified[= lines ]
+Use the unified output format, showing
+.I lines
+(an integer) lines of
+context, or three if
+.I lines
+is not given.
+For proper operation,
+.I patch
+typically needs at least two lines of
+context.
+.TP
+.B \-v
+.br
+.ns
+.TP
+.B \-\-version
+Output the version number of
+.IR diff .
+.TP
+.B \-w
+Ignore white space when comparing lines.
+.TP
+.BI "\-W " columns
+.br
+.ns
+.TP
+.BI \-\-width= columns
+Use an output width of
+.I columns
+in side by side format.
+.TP
+.BI "\-x " pattern
+When comparing directories, ignore files and subdirectories whose basenames
+match
+.IR pattern .
+.TP
+.BI "\-X " file
+When comparing directories, ignore files and subdirectories whose basenames
+match any pattern contained in
+.IR file .
+.TP
+.B \-y
+Use the side by side output format.
+.SH SEE ALSO
+cmp(1), comm(1), diff3(1), ed(1), patch(1), pr(1), sdiff(1).
+.SH DIAGNOSTICS
+An exit status of 0 means no differences were found, 1 means some
+differences were found, and 2 means trouble.
diff --git a/gnu/usr.bin/diff/diff.c b/gnu/usr.bin/diff/diff.c
index e4eb66f..6004f84 100644
--- a/gnu/usr.bin/diff/diff.c
+++ b/gnu/usr.bin/diff/diff.c
@@ -1,5 +1,5 @@
/* GNU DIFF main routine.
- Copyright (C) 1988, 1989, 1992 Free Software Foundation, Inc.
+ Copyright (C) 1988, 1989, 1992, 1993 Free Software Foundation, Inc.
This file is part of GNU DIFF.
@@ -33,14 +33,16 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#define GUTTER_WIDTH_MINIMUM 3
#endif
-int diff_dirs ();
-int diff_2_files ();
-
-static int compare_files ();
-static int specify_format ();
-static void add_regexp();
-static void specify_style ();
-static void usage ();
+static char const *filetype PARAMS((struct stat const *));
+static char *option_list PARAMS((char **, int));
+static int add_exclude_file PARAMS((char const *));
+static int ck_atoi PARAMS((char const *, int *));
+static int compare_files PARAMS((char const *, char const *, char const *, char const *, int));
+static int specify_format PARAMS((char **, char *));
+static void add_exclude PARAMS((char const *));
+static void add_regexp PARAMS((struct regexp_list **, char const *));
+static void specify_style PARAMS((enum output_style));
+static void usage PARAMS((char const *));
/* Nonzero for -r: if comparing two directories,
compare their common subdirectories recursively. */
@@ -65,13 +67,13 @@ option_list (optionvec, count)
int count;
{
int i;
- int length = 0;
+ size_t length = 0;
char *result;
for (i = 0; i < count; i++)
length += strlen (optionvec[i]) + 1;
- result = (char *) xmalloc (length + 1);
+ result = xmalloc (length + 1);
result[0] = 0;
for (i = 0; i < count; i++)
@@ -83,14 +85,14 @@ option_list (optionvec, count)
return result;
}
-/* Convert STR to a positive integer, storing the result in *OUT.
+/* Convert STR to a positive integer, storing the result in *OUT.
If STR is not a valid integer, return -1 (otherwise 0). */
static int
ck_atoi (str, out)
- char *str;
+ char const *str;
int *out;
{
- char *p;
+ char const *p;
for (p = str; *p; p++)
if (*p < '0' || *p > '9')
return -1;
@@ -101,12 +103,12 @@ ck_atoi (str, out)
/* Keep track of excluded file name patterns. */
-static const char **exclude;
+static char const **exclude;
static int exclude_alloc, exclude_count;
int
excluded_filename (f)
- const char *f;
+ char const *f;
{
int i;
for (i = 0; i < exclude_count; i++)
@@ -117,10 +119,10 @@ excluded_filename (f)
static void
add_exclude (pattern)
- const char *pattern;
+ char const *pattern;
{
if (exclude_alloc <= exclude_count)
- exclude = (const char **)
+ exclude = (char const **)
(exclude_alloc == 0
? xmalloc ((exclude_alloc = 64) * sizeof (*exclude))
: xrealloc (exclude, (exclude_alloc *= 2) * sizeof (*exclude)));
@@ -130,13 +132,15 @@ add_exclude (pattern)
static int
add_exclude_file (name)
- const char *name;
+ char const *name;
{
struct file_data f;
char *p, *q, *lim;
f.name = optarg;
- f.desc = strcmp (optarg, "-") == 0 ? 0 : open (optarg, O_RDONLY, 0);
+ f.desc = (strcmp (optarg, "-") == 0
+ ? STDIN_FILENO
+ : open (optarg, O_RDONLY, 0));
if (f.desc < 0 || fstat (f.desc, &f.stat) != 0)
return -1;
@@ -145,7 +149,7 @@ add_exclude_file (name)
for (p = f.buffer, lim = p + f.buffered_chars; p < lim; p = q)
{
- q = memchr (p, '\n', lim - p);
+ q = (char *) memchr (p, '\n', lim - p);
if (!q)
q = lim;
*q++ = 0;
@@ -158,7 +162,7 @@ add_exclude_file (name)
/* The numbers 129- that appear in the fourth element of some entries
tell the big switch in `main' how to process those options. */
-static struct option longopts[] =
+static struct option const longopts[] =
{
{"ignore-blank-lines", 0, 0, 'B'},
{"context", 2, 0, 'C'},
@@ -202,11 +206,13 @@ static struct option longopts[] =
{"old-line-format", 1, 0, 132},
{"new-line-format", 1, 0, 133},
{"unchanged-line-format", 1, 0, 134},
- {"old-group-format", 1, 0, 135},
- {"new-group-format", 1, 0, 136},
- {"unchanged-group-format", 1, 0, 137},
- {"changed-group-format", 1, 0, 138},
- {"horizon-lines", 1, 0, 139},
+ {"line-format", 1, 0, 135},
+ {"old-group-format", 1, 0, 136},
+ {"new-group-format", 1, 0, 137},
+ {"unchanged-group-format", 1, 0, 138},
+ {"changed-group-format", 1, 0, 139},
+ {"horizon-lines", 1, 0, 140},
+ {"help", 0, 0, 141},
{0, 0, 0, 0}
};
@@ -218,42 +224,19 @@ main (argc, argv)
int val;
int c;
int prev = -1;
- extern char *version_string;
int width = DEFAULT_WIDTH;
+ /* Do our initializations. */
program = argv[0];
-
- /* Do our initializations. */
output_style = OUTPUT_NORMAL;
- always_text_flag = FALSE;
- ignore_space_change_flag = FALSE;
- ignore_all_space_flag = FALSE;
- length_varies = FALSE;
- ignore_case_flag = FALSE;
- ignore_blank_lines_flag = FALSE;
- ignore_regexp_list = NULL;
- function_regexp_list = NULL;
- print_file_same_flag = FALSE;
- entire_new_file_flag = FALSE;
- unidirectional_new_file_flag = FALSE;
- no_details_flag = FALSE;
context = -1;
line_end_char = '\n';
- tab_align_flag = FALSE;
- tab_expand_flag = FALSE;
- recursive = FALSE;
- paginate_flag = FALSE;
- heuristic = FALSE;
- dir_start_file = NULL;
- msg_chain = NULL;
- msg_chain_end = NULL;
- no_discards = 0;
/* Decode the options. */
while ((c = getopt_long (argc, argv,
"0123456789abBcC:dD:efF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:y",
- longopts, (int *)0)) != EOF)
+ longopts, 0)) != EOF)
{
switch (c)
{
@@ -285,14 +268,16 @@ main (argc, argv)
break;
case 'b':
- /* Ignore changes in amount of whitespace. */
+ /* Ignore changes in amount of white space. */
ignore_space_change_flag = 1;
length_varies = 1;
+ ignore_some_changes = 1;
break;
case 'B':
/* Ignore changes affecting only blank lines. */
ignore_blank_lines_flag = 1;
+ ignore_some_changes = 1;
break;
case 'C': /* +context[=lines] */
@@ -323,7 +308,7 @@ main (argc, argv)
specify_style (OUTPUT_IFDEF);
{
int i, err = 0;
- static const char C_ifdef_group_formats[] =
+ static char const C_ifdef_group_formats[] =
"#ifndef %s\n%%<#endif /* not %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c%%=%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n";
char *b = xmalloc (sizeof (C_ifdef_group_formats)
+ 7 * strlen(optarg) - 14 /* 7*"%s" */
@@ -376,12 +361,14 @@ main (argc, argv)
case 'i':
/* Ignore changes in case. */
ignore_case_flag = 1;
+ ignore_some_changes = 1;
break;
case 'I':
/* Ignore changes affecting only lines that match the
specified regexp. */
add_regexp (&ignore_regexp_list, optarg);
+ ignore_some_changes = 1;
break;
case 'l':
@@ -398,7 +385,7 @@ main (argc, argv)
else
fatal ("too many file label options");
break;
-
+
case 'n':
/* Output RCS-style diffs, like `-f' except that each command
specifies the number of lines affected. */
@@ -429,7 +416,7 @@ main (argc, argv)
break;
case 'r':
- /* When comparing directories,
+ /* When comparing directories,
recursively compare any subdirectories found. */
recursive = 1;
break;
@@ -464,12 +451,13 @@ main (argc, argv)
break;
case 'v':
- fprintf (stderr, "GNU diff version %s\n", version_string);
- break;
+ printf ("GNU diff version %s\n", version_string);
+ exit (0);
case 'w':
- /* Ignore horizontal whitespace when comparing lines. */
+ /* Ignore horizontal white space when comparing lines. */
ignore_all_space_flag = 1;
+ ignore_some_changes = 1;
length_varies = 1;
break;
@@ -492,15 +480,15 @@ main (argc, argv)
if (ck_atoi (optarg, &width) || width <= 0)
fatal ("column width must be a positive integer");
break;
-
+
case 129:
sdiff_left_only = 1;
break;
-
+
case 130:
sdiff_skip_common_lines = 1;
break;
-
+
case 131:
/* sdiff-style columns output. */
specify_style (OUTPUT_SDIFF);
@@ -511,40 +499,46 @@ main (argc, argv)
case 133:
case 134:
specify_style (OUTPUT_IFDEF);
+ if (specify_format (&line_format[c - 132], optarg) != 0)
+ error ("conflicting line format", 0, 0);
+ break;
+
+ case 135:
+ specify_style (OUTPUT_IFDEF);
{
- const char **form = &line_format[c - 132];
- if (*form && strcmp (*form, optarg) != 0)
+ int i, err = 0;
+ for (i = 0; i < sizeof (line_format) / sizeof (*line_format); i++)
+ err |= specify_format (&line_format[i], optarg);
+ if (err)
error ("conflicting line format", 0, 0);
- *form = optarg;
}
break;
- case 135:
case 136:
case 137:
case 138:
+ case 139:
specify_style (OUTPUT_IFDEF);
- {
- const char **form = &group_format[c - 135];
- if (*form && strcmp (*form, optarg) != 0)
- error ("conflicting group format", 0, 0);
- *form = optarg;
- }
+ if (specify_format (&group_format[c - 136], optarg) != 0)
+ error ("conflicting group format", 0, 0);
break;
- case 139:
+ case 140:
if (ck_atoi (optarg, &horizon_lines) || horizon_lines < 0)
fatal ("horizon must be a nonnegative integer");
break;
+ case 141:
+ usage (0);
+
default:
- usage ();
+ usage ("");
}
prev = c;
}
if (optind != argc - 2)
- usage ();
+ usage (optind < argc - 2 ? "extra operand" : "missing operand");
{
@@ -569,7 +563,7 @@ main (argc, argv)
else if (context == -1)
/* Default amount of context for -c. */
context = 3;
-
+
if (output_style == OUTPUT_IFDEF)
{
int i;
@@ -598,7 +592,7 @@ main (argc, argv)
switch_string = option_list (argv + 1, optind - 1);
- val = compare_files (NULL, argv[optind], NULL, argv[optind + 1], 0);
+ val = compare_files (0, argv[optind], 0, argv[optind + 1], 0);
/* Print any messages that were saved up for last. */
print_message_queue ();
@@ -614,14 +608,14 @@ main (argc, argv)
static void
add_regexp (reglist, pattern)
struct regexp_list **reglist;
- char *pattern;
+ char const *pattern;
{
struct regexp_list *r;
- const char *m;
+ char const *m;
r = (struct regexp_list *) xmalloc (sizeof (*r));
bzero (r, sizeof (*r));
- r->buf.fastmap = (char *) xmalloc (256);
+ r->buf.fastmap = xmalloc (256);
m = re_compile_pattern (pattern, strlen (pattern), &r->buf);
if (m != 0)
error ("%s: %s", pattern, m);
@@ -632,37 +626,43 @@ add_regexp (reglist, pattern)
}
static void
-usage ()
+usage (reason)
+ char const *reason;
{
- fprintf (stderr, "Usage: %s [options] from-file to-file\n", program);
- fprintf (stderr, "Options:\n\
- [-abBcdefhHilnNpPqrstTuvwy] [-C lines] [-D name] [-F regexp]\n\
- [-I regexp] [-L from-label [-L to-label]] [-S starting-file] [-U lines]\n\
- [-W columns] [-x pattern] [-X pattern-file] [--exclude=pattern]\n\
- [--exclude-from=pattern-file] [--ignore-blank-lines] [--context[=lines]]\n\
- [--ifdef=name] [--show-function-line=regexp] [--speed-large-files]\n\
- [--label=from-label [--label=to-label]] [--new-file]\n");
- fprintf (stderr, "\
- [--ignore-matching-lines=regexp] [--unidirectional-new-file]\n\
- [--starting-file=starting-file] [--initial-tab] [--width=columns]\n\
- [--text] [--ignore-space-change] [--minimal] [--ed] [--forward-ed]\n\
- [--ignore-case] [--paginate] [--rcs] [--show-c-function] [--brief]\n\
- [--recursive] [--report-identical-files] [--expand-tabs] [--version]\n");
- fprintf (stderr, "\
- [--ignore-all-space] [--side-by-side] [--unified[=lines]]\n\
- [--left-column] [--suppress-common-lines] [--sdiff-merge-assist]\n\
- [--old-line-format=format] [--new-line-format=format]\n\
- [--unchanged-line-format=format]\n\
- [--old-group-format=format] [--new-group-format=format]\n\
- [--unchanged-group-format=format] [--changed-group-format=format]\n\
- [--horizon-lines=lines]\n");
- exit (2);
-}
+ if (reason && *reason)
+ fprintf (stderr, "%s: %s\n", program, reason);
+ fflush (stderr);
+ printf ("Usage: %s [options] from-file to-file\n", program);
+ printf ("Options:\n\
+ [-abBcdefhHilnNpPqrstTuvwy] [-C lines] [-D name] [-F regexp]\n\
+ [-I regexp] [-L from-label [-L to-label]] [-S starting-file] [-U lines]\n\
+ [-W columns] [-x pattern] [-X pattern-file]\n");
+ printf ("\
+ [--brief] [--changed-group-format=format] [--context[=lines]] [--ed]\n\
+ [--exclude=pattern] [--exclude-from=pattern-file] [--expand-tabs]\n\
+ [--forward-ed] [--help] [--horizon-lines=lines] [--ifdef=name]\n\
+ [--ignore-all-space] [--ignore-blank-lines] [--ignore-case]\n");
+ printf ("\
+ [--ignore-matching-lines=regexp] [--ignore-space-change]\n\
+ [--initial-tab] [--label=from-label [--label=to-label]]\n\
+ [--left-column] [--minimal] [--new-file] [--new-group-format=format]\n\
+ [--new-line-format=format] [--old-group-format=format]\n");
+ printf ("\
+ [--old-line-format=format] [--paginate] [--rcs] [--recursive]\n\
+ [--report-identical-files] [--sdiff-merge-assist] [--show-c-function]\n\
+ [--show-function-line=regexp] [--side-by-side] [--speed-large-files]\n\
+ [--starting-file=starting-file] [--suppress-common-lines] [--text]\n");
+ printf ("\
+ [--unchanged-group-format=format] [--unchanged-line-format=format]\n\
+ [--unidirectional-new-file] [--unified[=lines]] [--version]\n\
+ [--width=columns]\n");
+ exit (reason ? 2 : 0);
+}
static int
specify_format (var, value)
- const char **var;
- const char *value;
+ char **var;
+ char *value;
{
int err = *var ? strcmp (*var, value) : 0;
*var = value;
@@ -679,6 +679,46 @@ specify_style (style)
output_style = style;
}
+static char const *
+filetype (st)
+ struct stat const *st;
+{
+ /* See Posix.2 section 4.17.6.1.1 and Table 5-1 for these formats.
+ To keep diagnostics grammatical, the returned string must start
+ with a consonant. */
+
+ if (S_ISREG (st->st_mode))
+ {
+ if (st->st_size == 0)
+ return "regular empty file";
+ /* Posix.2 section 5.14.2 seems to suggest that we must read the file
+ and guess whether it's C, Fortran, etc., but this is somewhat useless
+ and doesn't reflect historical practice. We're allowed to guess
+ wrong, so we don't bother to read the file. */
+ return "regular file";
+ }
+ if (S_ISDIR (st->st_mode)) return "directory";
+
+ /* other Posix.1 file types */
+#ifdef S_ISBLK
+ if (S_ISBLK (st->st_mode)) return "block special file";
+#endif
+#ifdef S_ISCHR
+ if (S_ISCHR (st->st_mode)) return "character special file";
+#endif
+#ifdef S_ISFIFO
+ if (S_ISFIFO (st->st_mode)) return "fifo";
+#endif
+
+ /* other popular file types */
+ /* S_ISLNK is impossible with `stat'. */
+#ifdef S_ISSOCK
+ if (S_ISSOCK (st->st_mode)) return "socket";
+#endif
+
+ return "weird file";
+}
+
/* Compare two files (or dirs) with specified names
DIR0/NAME0 and DIR1/NAME1, at level DEPTH in directory recursion.
(if DIR0 is 0, then the name is just NAME0, etc.)
@@ -689,15 +729,16 @@ specify_style (style)
static int
compare_files (dir0, name0, dir1, name1, depth)
- char *dir0, *dir1;
- char *name0, *name1;
+ char const *dir0, *dir1;
+ char const *name0, *name1;
int depth;
{
struct file_data inf[2];
register int i;
int val;
int same_files;
- int errorcount = 0;
+ int failed = 0;
+ char *free0 = 0, *free1 = 0;
/* If this is directory comparison, perhaps we have a file
that exists only in one of the directories.
@@ -707,15 +748,17 @@ compare_files (dir0, name0, dir1, name1, depth)
|| (unidirectional_new_file_flag && name1 != 0)
|| entire_new_file_flag))
{
- char *name = name0 == 0 ? name1 : name0;
- char *dir = name0 == 0 ? dir1 : dir0;
+ char const *name = name0 == 0 ? name1 : name0;
+ char const *dir = name0 == 0 ? dir1 : dir0;
message ("Only in %s: %s\n", dir, name);
/* Return 1 so that diff_dirs will return 1 ("some files differ"). */
return 1;
}
+ bzero (inf, sizeof (inf));
+
/* Mark any nonexistent file with -1 in the desc field. */
- /* Mark unopened files (i.e. directories) with -2. */
+ /* Mark unopened files (e.g. directories) with -2. */
inf[0].desc = name0 == 0 ? -1 : -2;
inf[1].desc = name1 == 0 ? -1 : -2;
@@ -727,25 +770,41 @@ compare_files (dir0, name0, dir1, name1, depth)
if (name1 == 0)
name1 = name0;
- inf[0].name = dir0 == 0 ? name0 : concat (dir0, "/", name0);
- inf[1].name = dir1 == 0 ? name1 : concat (dir1, "/", name1);
+ inf[0].name = dir0 == 0 ? name0 : (free0 = dir_file_pathname (dir0, name0));
+ inf[1].name = dir1 == 0 ? name1 : (free1 = dir_file_pathname (dir1, name1));
/* Stat the files. Record whether they are directories. */
for (i = 0; i <= 1; i++)
{
- bzero (&inf[i].stat, sizeof (struct stat));
- inf[i].dir_p = 0;
-
if (inf[i].desc != -1)
{
int stat_result;
- if (strcmp (inf[i].name, "-") == 0)
+ if (i && strcmp (inf[i].name, inf[0].name) == 0)
{
- inf[i].desc = 0;
- inf[i].name = "Standard Input";
- stat_result = fstat (0, &inf[i].stat);
+ inf[i].stat = inf[0].stat;
+ stat_result = 0;
+ }
+ else if (strcmp (inf[i].name, "-") == 0)
+ {
+ inf[i].desc = STDIN_FILENO;
+ stat_result = fstat (STDIN_FILENO, &inf[i].stat);
+ if (stat_result == 0 && S_ISREG (inf[i].stat.st_mode))
+ {
+ off_t pos = lseek (STDIN_FILENO, (off_t) 0, SEEK_CUR);
+ if (pos == -1)
+ stat_result = -1;
+ else
+ {
+ if (pos <= inf[i].stat.st_size)
+ inf[i].stat.st_size -= pos;
+ else
+ inf[i].stat.st_size = 0;
+ /* Posix.2 4.17.6.1.4 requires current time for stdin. */
+ time (&inf[i].stat.st_mtime);
+ }
+ }
}
else
stat_result = stat (inf[i].name, &inf[i].stat);
@@ -753,42 +812,46 @@ compare_files (dir0, name0, dir1, name1, depth)
if (stat_result != 0)
{
perror_with_name (inf[i].name);
- errorcount = 1;
+ failed = 1;
}
else
- inf[i].dir_p = S_ISDIR (inf[i].stat.st_mode) && inf[i].desc != 0;
+ {
+ inf[i].dir_p = S_ISDIR (inf[i].stat.st_mode) && inf[i].desc != 0;
+ if (inf[1 - i].desc == -1)
+ {
+ inf[1 - i].dir_p = inf[i].dir_p;
+ inf[1 - i].stat.st_mode = inf[i].stat.st_mode;
+ }
+ }
}
}
- if (name0 == 0)
- inf[0].dir_p = inf[1].dir_p;
- if (name1 == 0)
- inf[1].dir_p = inf[0].dir_p;
-
- if (errorcount == 0 && depth == 0 && inf[0].dir_p != inf[1].dir_p)
+ if (! failed && depth == 0 && inf[0].dir_p != inf[1].dir_p)
{
/* If one is a directory, and it was specified in the command line,
use the file in that dir with the other file's basename. */
int fnm_arg = inf[0].dir_p;
int dir_arg = 1 - fnm_arg;
- char *p = rindex (inf[fnm_arg].name, '/');
- char *filename = inf[dir_arg].name
- = concat (inf[dir_arg].name, "/", (p ? p+1 : inf[fnm_arg].name));
+ char const *fnm = inf[fnm_arg].name;
+ char const *dir = inf[dir_arg].name;
+ char const *p = strrchr (fnm, '/');
+ char const *filename = inf[dir_arg].name
+ = dir_file_pathname (dir, p ? p + 1 : fnm);
- if (inf[fnm_arg].desc == 0)
+ if (strcmp (fnm, "-") == 0)
fatal ("can't compare - to a directory");
if (stat (filename, &inf[dir_arg].stat) != 0)
{
perror_with_name (filename);
- errorcount = 1;
+ failed = 1;
}
else
inf[dir_arg].dir_p = S_ISDIR (inf[dir_arg].stat.st_mode);
}
- if (errorcount)
+ if (failed)
{
/* If either file should exist but does not, return 2. */
@@ -798,6 +861,7 @@ compare_files (dir0, name0, dir1, name1, depth)
}
else if ((same_files = inf[0].stat.st_ino == inf[1].stat.st_ino
&& inf[0].stat.st_dev == inf[1].stat.st_dev
+ && inf[0].stat.st_size == inf[1].stat.st_size
&& inf[0].desc != -1
&& inf[1].desc != -1)
&& no_diff_means_no_output)
@@ -828,37 +892,43 @@ compare_files (dir0, name0, dir1, name1, depth)
}
}
- else if (inf[0].dir_p | inf[1].dir_p)
+ else if ((inf[0].dir_p | inf[1].dir_p)
+ || (depth > 0
+ && (! S_ISREG (inf[0].stat.st_mode)
+ || ! S_ISREG (inf[1].stat.st_mode))))
{
/* Perhaps we have a subdirectory that exists only in one directory.
If so, just print a message to that effect. */
if (inf[0].desc == -1 || inf[1].desc == -1)
{
- if (recursive
+ if ((inf[0].dir_p | inf[1].dir_p)
+ && recursive
&& (entire_new_file_flag
|| (unidirectional_new_file_flag && inf[0].desc == -1)))
val = diff_dirs (inf, compare_files, depth);
else
{
- char *dir = (inf[0].desc == -1) ? dir1 : dir0;
+ char const *dir = (inf[0].desc == -1) ? dir1 : dir0;
+ /* See Posix.2 section 4.17.6.1.1 for this format. */
message ("Only in %s: %s\n", dir, name0);
val = 1;
}
}
else
{
- /* We have a subdirectory in one directory
- and a file in the other. */
+ /* We have two files that are not to be compared. */
- message ("%s is a directory but %s is not\n",
- inf[1 - inf[0].dir_p].name, inf[inf[0].dir_p].name);
+ /* See Posix.2 section 4.17.6.1.1 for this format. */
+ message5 ("File %s is a %s while file %s is a %s\n",
+ inf[0].name, filetype (&inf[0].stat),
+ inf[1].name, filetype (&inf[1].stat));
/* This is a difference. */
val = 1;
}
}
- else if (no_details_flag
+ else if ((no_details_flag & ~ignore_some_changes)
&& inf[0].stat.st_size != inf[1].stat.st_size
&& (inf[0].desc == -1 || S_ISREG (inf[0].stat.st_mode))
&& (inf[1].desc == -1 || S_ISREG (inf[1].stat.st_mode)))
@@ -876,7 +946,7 @@ compare_files (dir0, name0, dir1, name1, depth)
if ((inf[0].desc = open (inf[0].name, O_RDONLY, 0)) < 0)
{
perror_with_name (inf[0].name);
- errorcount = 1;
+ failed = 1;
}
if (inf[1].desc == -2)
if (same_files)
@@ -884,12 +954,12 @@ compare_files (dir0, name0, dir1, name1, depth)
else if ((inf[1].desc = open (inf[1].name, O_RDONLY, 0)) < 0)
{
perror_with_name (inf[1].name);
- errorcount = 1;
+ failed = 1;
}
-
+
/* Compare the files, if no error was found. */
- val = errorcount ? 2 : diff_2_files (inf, depth);
+ val = failed ? 2 : diff_2_files (inf, depth);
/* Close the file descriptors. */
@@ -918,10 +988,10 @@ compare_files (dir0, name0, dir1, name1, depth)
else
fflush (stdout);
- if (dir0 != 0)
- free (inf[0].name);
- if (dir1 != 0)
- free (inf[1].name);
+ if (free0)
+ free (free0);
+ if (free1)
+ free (free1);
return val;
}
diff --git a/gnu/usr.bin/diff/diff.h b/gnu/usr.bin/diff/diff.h
index 74f025d..e75050f 100644
--- a/gnu/usr.bin/diff/diff.h
+++ b/gnu/usr.bin/diff/diff.h
@@ -1,5 +1,5 @@
/* Shared definitions for GNU DIFF
- Copyright (C) 1988, 89, 91, 92 Free Software Foundation, Inc.
+ Copyright (C) 1988, 89, 91, 92, 93 Free Software Foundation, Inc.
This file is part of GNU DIFF.
@@ -17,22 +17,15 @@ You should have received a copy of the GNU General Public License
along with GNU DIFF; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+#include "system.h"
#include <ctype.h>
#include <stdio.h>
-#include "system.h"
#include "regex.h"
#ifndef PR_FILE_NAME
#define PR_FILE_NAME "/bin/pr"
#endif
-/* Character classes. */
-extern const char textchar[];
-
-/* Is_space is a little broader than ctype.h's isspace,
- because it also includes backspace and no-break space. */
-#define Is_space(c) (textchar[c] & 2)
-
#define TAB_WIDTH 8
/* Variables for command line options */
@@ -82,10 +75,10 @@ EXTERN int always_text_flag;
/* Number of lines to keep in identical prefix and suffix. */
EXTERN int horizon_lines;
-/* Ignore changes in horizontal whitespace (-b). */
+/* Ignore changes in horizontal white space (-b). */
EXTERN int ignore_space_change_flag;
-/* Ignore all horizontal whitespace (-w). */
+/* Ignore all horizontal white space (-w). */
EXTERN int ignore_all_space_flag;
/* Ignore changes that affect only blank lines (-B). */
@@ -95,6 +88,10 @@ EXTERN int ignore_blank_lines_flag;
This depends on various options. */
EXTERN int length_varies;
+/* 1 if files may match even if their contents are not byte-for-byte identical.
+ This depends on various options. */
+EXTERN int ignore_some_changes;
+
/* Ignore differences in case of letters (-i). */
EXTERN int ignore_case_flag;
@@ -160,13 +157,13 @@ enum line_class {
};
/* Line group formats for old, new, unchanged, and changed groups. */
-EXTERN const char *group_format[CHANGED + 1];
+EXTERN char *group_format[CHANGED + 1];
/* Line formats for old, new, and unchanged lines. */
-EXTERN const char *line_format[UNCHANGED + 1];
+EXTERN char *line_format[UNCHANGED + 1];
/* If using OUTPUT_SDIFF print extra information to help the sdiff filter. */
-EXTERN int sdiff_help_sdiff;
+EXTERN int sdiff_help_sdiff;
/* Tell OUTPUT_SDIFF to show only the left version of common lines. */
EXTERN int sdiff_left_only;
@@ -192,7 +189,7 @@ EXTERN char * program;
/* The result of comparison is an "edit script": a chain of `struct change'.
Each `struct change' represents one place where some lines are deleted
and some are inserted.
-
+
LINE0 and LINE1 are the first affected lines in the two files (origin 0).
DELETED is the number of lines deleted here from file 0.
INSERTED is the number of lines inserted here in file 1.
@@ -216,19 +213,19 @@ struct change
struct file_data {
int desc; /* File descriptor */
- char *name; /* File name */
+ char const *name; /* File name */
struct stat stat; /* File status from fstat() */
int dir_p; /* nonzero if file is a directory */
/* Buffer in which text of file is read. */
char * buffer;
/* Allocated size of buffer. */
- int bufsize;
+ size_t bufsize;
/* Number of valid characters now in the buffer. */
- int buffered_chars;
+ size_t buffered_chars;
/* Array of pointers to lines in the file. */
- const char **linbuf;
+ char const **linbuf;
/* linbuf_base <= buffered_lines <= valid_lines <= alloc_lines.
linebuf[linbuf_base ... buffered_lines - 1] are possibly differing.
@@ -237,14 +234,14 @@ struct file_data {
int linbuf_base, buffered_lines, valid_lines, alloc_lines;
/* Pointer to end of prefix of this file to ignore when hashing. */
- const char *prefix_end;
+ char const *prefix_end;
/* Count of lines in the prefix.
There are this many lines in the file before linbuf[0]. */
int prefix_lines;
/* Pointer to start of suffix of this file to ignore when hashing. */
- const char *suffix_begin;
+ char const *suffix_begin;
/* Vector, indexed by line number, containing an equivalence code for
each line. It is this vector that is actually compared with that
@@ -279,57 +276,73 @@ struct file_data {
EXTERN struct file_data files[2];
-/* Queue up one-line messages to be printed at the end,
- when -l is specified. Each message is recorded with a `struct msg'. */
-
-struct msg
-{
- struct msg *next;
- char *format;
- char *arg1;
- char *arg2;
-};
-
-/* Head of the chain of queues messages. */
-
-EXTERN struct msg *msg_chain;
-
-/* Tail of the chain of queues messages. */
-
-EXTERN struct msg *msg_chain_end;
-
/* Stdio stream to output diffs to. */
EXTERN FILE *outfile;
/* Declare various functions. */
-#if __STDC__
-#define VOID void
-#else
-#define VOID char
-#endif
-VOID *xmalloc ();
-VOID *xrealloc ();
-char *concat ();
-
-int excluded_filename ();
-int sip ();
-
-struct change *find_change ();
-
-void analyze_hunk ();
-void begin_output ();
-void error ();
-void fatal ();
-void message ();
-void output_1_line ();
-void perror_with_name ();
-void pfatal_with_name ();
-void print_1_line ();
-void print_context_header ();
-void print_message_queue ();
-void print_number_range ();
-void print_script ();
-void slurp ();
-void translate_range ();
+/* analyze.c */
+int diff_2_files PARAMS((struct file_data[], int));
+
+/* context.c */
+void print_context_header PARAMS((struct file_data[], int));
+void print_context_script PARAMS((struct change *, int));
+
+/* diff.c */
+int excluded_filename PARAMS((char const *));
+
+/* dir.c */
+int diff_dirs PARAMS((struct file_data const[], int (*) PARAMS((char const *, char const *, char const *, char const *, int)), int));
+
+/* ed.c */
+void print_ed_script PARAMS((struct change *));
+void pr_forward_ed_script PARAMS((struct change *));
+
+/* ifdef.c */
+void print_ifdef_script PARAMS((struct change *));
+
+/* io.c */
+int read_files PARAMS((struct file_data[], int));
+int sip PARAMS((struct file_data *, int));
+void slurp PARAMS((struct file_data *));
+
+/* normal.c */
+void print_normal_script PARAMS((struct change *));
+
+/* rcs.c */
+void print_rcs_script PARAMS((struct change *));
+
+/* side.c */
+void print_sdiff_script PARAMS((struct change *));
+
+/* util.c */
+VOID *xmalloc PARAMS((size_t));
+VOID *xrealloc PARAMS((VOID *, size_t));
+char *concat PARAMS((char const *, char const *, char const *));
+char *dir_file_pathname PARAMS((char const *, char const *));
+int change_letter PARAMS((int, int));
+int line_cmp PARAMS((char const *, size_t, char const *, size_t));
+int translate_line_number PARAMS((struct file_data const *, int));
+struct change *find_change PARAMS((struct change *));
+struct change *find_reverse_change PARAMS((struct change *));
+void analyze_hunk PARAMS((struct change *, int *, int *, int *, int *, int *, int *));
+void begin_output PARAMS((void));
+void debug_script PARAMS((struct change *));
+void error PARAMS((char const *, char const *, char const *));
+void fatal PARAMS((char const *));
+void finish_output PARAMS((void));
+void message PARAMS((char const *, char const *, char const *));
+void message5 PARAMS((char const *, char const *, char const *, char const *, char const *));
+void output_1_line PARAMS((char const *, char const *, char const *, char const *));
+void perror_with_name PARAMS((char const *));
+void pfatal_with_name PARAMS((char const *));
+void print_1_line PARAMS((char const *, char const * const *));
+void print_message_queue PARAMS((void));
+void print_number_range PARAMS((int, struct file_data *, int, int));
+void print_script PARAMS((struct change *, struct change * (*) PARAMS((struct change *)), void (*) PARAMS((struct change *))));
+void setup_output PARAMS((char const *, char const *, int));
+void translate_range PARAMS((struct file_data const *, int, int, int *, int *));
+
+/* version.c */
+extern char const version_string[];
diff --git a/gnu/usr.bin/diff/diff3.c b/gnu/usr.bin/diff/diff3.c
index b9952fc..1ac3887 100644
--- a/gnu/usr.bin/diff/diff3.c
+++ b/gnu/usr.bin/diff/diff3.c
@@ -1,5 +1,5 @@
/* Three way file comparison program (diff3) for Project GNU.
- Copyright (C) 1988, 1989, 1992 Free Software Foundation, Inc.
+ Copyright (C) 1988, 1989, 1992, 1993 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,16 +17,12 @@
/* Written by Randy Smith */
-#if __STDC__
-#define VOID void
-#else
-#define VOID char
-#endif
-
+#include "system.h"
#include <stdio.h>
#include <ctype.h>
#include "getopt.h"
-#include "system.h"
+
+extern char const version_string[];
/*
* Internal data structures and macros for the diff3 program; includes
@@ -70,7 +66,7 @@ enum diff_type {
struct diff_block {
int ranges[2][2]; /* Ranges are inclusive */
char **lines[2]; /* The actual lines (may contain nulls) */
- int *lengths[2]; /* Line lengths (including newlines, if any) */
+ size_t *lengths[2]; /* Line lengths (including newlines, if any) */
struct diff_block *next;
};
@@ -80,7 +76,7 @@ struct diff3_block {
enum diff_type correspond; /* Type of diff */
int ranges[3][2]; /* Ranges are inclusive */
char **lines[3]; /* The actual lines (may contain nulls) */
- int *lengths[3]; /* Line lengths (including newlines, if any) */
+ size_t *lengths[3]; /* Line lengths (including newlines, if any) */
struct diff3_block *next;
};
@@ -158,6 +154,9 @@ static int flagging;
/* Number of lines to keep in identical prefix and suffix. */
static int horizon_lines = 10;
+/* Use a tab to align output lines (-T). */
+static int tab_align_flag;
+
/* If nonzero, do not output information for overlapping diffs. */
static int simple_only;
@@ -176,46 +175,44 @@ static int merge;
static char *argv0;
-/*
- * Forward function declarations.
- */
-static int myread ();
-static void fatal ();
-static void perror_with_exit ();
-static struct diff_block *process_diff ();
-static struct diff3_block *make_3way_diff ();
-static void output_diff3 ();
-static int output_diff3_edscript ();
-static int output_diff3_merge ();
-static void usage ();
-
-static struct diff3_block *using_to_diff3_block ();
-static int copy_stringlist ();
-static struct diff3_block *create_diff3_block ();
-static int compare_line_list ();
-
-static char *read_diff ();
-static enum diff_type process_diff_control ();
-static char *scan_diff_line ();
-
-static struct diff3_block *reverse_diff3_blocklist ();
-
-VOID *xmalloc ();
-static VOID *xrealloc ();
-
-static char diff_program[] = DIFF_PROGRAM;
-
-static struct option longopts[] =
+static VOID *xmalloc PARAMS((size_t));
+static VOID *xrealloc PARAMS((VOID *, size_t));
+
+static char *read_diff PARAMS((char const *, char const *, char **));
+static char *scan_diff_line PARAMS((char *, char **, size_t *, char *, int));
+static enum diff_type process_diff_control PARAMS((char **, struct diff_block *));
+static int compare_line_list PARAMS((char * const[], size_t const[], char * const[], size_t const[], int));
+static int copy_stringlist PARAMS((char * const[], size_t const[], char *[], size_t[], int));
+static int dotlines PARAMS((FILE *, struct diff3_block *, int));
+static int output_diff3_edscript PARAMS((FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *));
+static int output_diff3_merge PARAMS((FILE *, FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *));
+static size_t myread PARAMS((int, char *, size_t));
+static struct diff3_block *create_diff3_block PARAMS((int, int, int, int, int, int));
+static struct diff3_block *make_3way_diff PARAMS((struct diff_block *, struct diff_block *));
+static struct diff3_block *reverse_diff3_blocklist PARAMS((struct diff3_block *));
+static struct diff3_block *using_to_diff3_block PARAMS((struct diff_block *[2], struct diff_block *[2], int, int, struct diff3_block const *));
+static struct diff_block *process_diff PARAMS((char const *, char const *, struct diff_block **));
+static void fatal PARAMS((char const *));
+static void output_diff3 PARAMS((FILE *, struct diff3_block *, int const[3], int const[3]));
+static void perror_with_exit PARAMS((char const *));
+static void undotlines PARAMS((FILE *, int, int, int));
+static void usage PARAMS((int));
+
+static char const diff_program[] = DIFF_PROGRAM;
+
+static struct option const longopts[] =
{
- {"text", 0, NULL, 'a'},
- {"show-all", 0, NULL, 'A'},
- {"ed", 0, NULL, 'e'},
- {"show-overlap", 0, NULL, 'E'},
- {"label", 1, NULL, 'L'},
- {"merge", 0, NULL, 'm'},
- {"overlap-only", 0, NULL, 'x'},
- {"easy-only", 0, NULL, '3'},
- {"version", 0, NULL, 'v'},
+ {"text", 0, 0, 'a'},
+ {"show-all", 0, 0, 'A'},
+ {"ed", 0, 0, 'e'},
+ {"show-overlap", 0, 0, 'E'},
+ {"label", 1, 0, 'L'},
+ {"merge", 0, 0, 'm'},
+ {"initial-tab", 0, 0, 'T'},
+ {"overlap-only", 0, 0, 'x'},
+ {"easy-only", 0, 0, '3'},
+ {"version", 0, 0, 'v'},
+ {"help", 0, 0, 129},
{0, 0, 0, 0}
};
@@ -228,7 +225,6 @@ main (argc, argv)
int argc;
char **argv;
{
- extern char *version_string;
int c, i;
int mapping[3];
int rev_mapping[3];
@@ -238,7 +234,6 @@ main (argc, argv)
struct diff3_block *diff3;
int tag_count = 0;
char *tag_strings[3];
- extern char *optarg;
char *commonname;
char **file;
struct stat statb;
@@ -247,8 +242,7 @@ main (argc, argv)
argv0 = argv[0];
- while ((c = getopt_long (argc, argv, "aeimvx3AEXL:", longopts, (int *) 0))
- != EOF)
+ while ((c = getopt_long (argc, argv, "aeimvx3AEL:TX", longopts, 0)) != EOF)
{
switch (c)
{
@@ -283,9 +277,14 @@ main (argc, argv)
case 'e':
incompat++;
break;
- case 'v':
- fprintf (stderr, "GNU diff3 version %s\n", version_string);
+ case 'T':
+ tab_align_flag = 1;
break;
+ case 'v':
+ printf ("GNU diff3 version %s\n", version_string);
+ exit (0);
+ case 129:
+ usage (0);
case 'L':
/* Handle up to three -L options. */
if (tag_count < 3)
@@ -295,8 +294,7 @@ main (argc, argv)
}
/* Falls through */
default:
- usage ();
- /* NOTREACHED */
+ usage (2);
}
}
@@ -308,7 +306,7 @@ main (argc, argv)
|| finalwrite & merge /* -i -m would rewrite input file. */
|| (tag_count && ! flagging) /* -L requires one of -AEX. */
|| argc - optind != 3)
- usage ();
+ usage (2);
file = &argv[optind];
@@ -346,14 +344,15 @@ main (argc, argv)
for (i = 0; i < 3; i++)
if (strcmp (file[i], "-") != 0)
- if (stat (file[i], &statb) < 0)
- perror_with_exit (file[i]);
- else if (S_ISDIR(statb.st_mode))
- {
- fprintf (stderr, "%s: %s: Is a directory\n", argv0, file[i]);
- exit (2);
- }
-
+ {
+ if (stat (file[i], &statb) < 0)
+ perror_with_exit (file[i]);
+ else if (S_ISDIR(statb.st_mode))
+ {
+ fprintf (stderr, "%s: %s: Is a directory\n", argv0, file[i]);
+ exit (2);
+ }
+ }
commonname = file[rev_mapping[FILEC]];
thread1 = process_diff (file[rev_mapping[FILE1]], commonname, &last_block);
@@ -395,16 +394,19 @@ main (argc, argv)
* Explain, patiently and kindly, how to use this program. Then exit.
*/
static void
-usage ()
+usage (status)
+ int status;
{
- fprintf (stderr, "\
+ fflush (stderr);
+ printf ("\
Usage: %s [options] my-file older-file your-file\n\
Options:\n\
- [-exAEX3v] [-i|-m] [-L label1 [-L label2 [-L label3]]] [--text] [--ed]\n\
- [--merge] [--show-all] [--show-overlap] [--overlap-only] [--easy-only]\n\
- [--label=label1 [--label=label2 [--label=label3]]] [--version]\n\
- Only one of [exAEX3] is allowed\n", argv0);
- exit (2);
+ [-exAEX3aTv] [-i|-m] [-L label1 [-L label2 [-L label3]]]\n\
+ [--easy-only] [--ed] [--help] [--initial-tab]\n\
+ [--label=label1 [--label=label2 [--label=label3]]] [--merge]\n\
+ [--overlap-only] [--show-all] [--show-overlap] [--text] [--version]\n\
+ Only one of [exAEX3] is allowed\n", argv0);
+ exit (status);
}
/*
@@ -436,7 +438,7 @@ Options:\n\
* Create a diff3_block, reserving space as indicated by the ranges.
*
* 3) Copy all of the pointers for file2 in. At least for now,
- * do bcmp's between corresponding strings in the two diffs.
+ * do memcmp's between corresponding strings in the two diffs.
*
* 4) Copy all of the pointers for file0 and 1 in. Get what you
* need from file2 (when there isn't a diff block, it's
@@ -447,8 +449,8 @@ Options:\n\
* the common file in that diff) is the odd person out. If you used
* diff blocks from both sets, check to see if files 0 and 1 match:
*
- * Same number of lines? If so, do a set of bcmp's (if a
- * bcmp matches; copy the pointer over; it'll be easier later
+ * Same number of lines? If so, do a set of memcmp's (if a
+ * memcmp matches; copy the pointer over; it'll be easier later
* if you have to do any compares). If they match, 0 & 1 are
* the same. If not, all three different.
*
@@ -518,16 +520,11 @@ make_3way_diff (thread0, thread1)
struct diff3_block
*result,
*tmpblock,
- **result_end,
- *last_diff3;
+ **result_end;
+
+ struct diff3_block const *last_diff3;
- static struct diff3_block zero_diff3 = {
- ERROR,
- { {0, 0}, {0, 0}, {0, 0} },
- { (char **) 0, (char **) 0, (char **) 0 },
- { (int *) 0, (int *) 0, (int *) 0 },
- (struct diff3_block *) 0
- };
+ static struct diff3_block const zero_diff3;
/* Initialization */
result = 0;
@@ -539,8 +536,7 @@ make_3way_diff (thread0, thread1)
while (current[0] || current[1])
{
- using[0] = using[1] = last_using[0] = last_using[1] =
- (struct diff_block *) 0;
+ using[0] = using[1] = last_using[0] = last_using[1] = 0;
/* Setup low and high water threads, diffs, and marks. */
if (!current[0])
@@ -567,8 +563,7 @@ make_3way_diff (thread0, thread1)
= last_using[high_water_thread]
= high_water_diff;
current[high_water_thread] = high_water_diff->next;
- last_using[high_water_thread]->next
- = (struct diff_block *) 0;
+ last_using[high_water_thread]->next = 0;
/* And mark the other diff */
other_thread = high_water_thread ^ 0x1;
@@ -591,10 +586,8 @@ make_3way_diff (thread0, thread1)
/* Take it off the current list. Note that this following
code assumes that other_diff enters it equal to
current[high_water_thread ^ 0x1] */
- current[other_thread]
- = current[other_thread]->next;
- other_diff->next
- = (struct diff_block *) 0;
+ current[other_thread] = current[other_thread]->next;
+ other_diff->next = 0;
/* Set the high_water stuff
If this comparison is equal, then this is the last pass
@@ -660,7 +653,7 @@ using_to_diff3_block (using, last_using, low_thread, high_thread, last_diff3)
*using[2],
*last_using[2];
int low_thread, high_thread;
- struct diff3_block *last_diff3;
+ struct diff3_block const *last_diff3;
{
int low[2], high[2];
struct diff3_block *result;
@@ -780,21 +773,21 @@ using_to_diff3_block (using, last_using, low_thread, high_thread, last_diff3)
*/
static int
copy_stringlist (fromptrs, fromlengths, toptrs, tolengths, copynum)
- char *fromptrs[], *toptrs[];
- int *fromlengths, *tolengths;
+ char * const fromptrs[];
+ char *toptrs[];
+ size_t const fromlengths[];
+ size_t tolengths[];
int copynum;
{
- register char
- **f = fromptrs,
- **t = toptrs;
- register int
- *fl = fromlengths,
- *tl = tolengths;
+ register char * const *f = fromptrs;
+ register char **t = toptrs;
+ register size_t const *fl = fromlengths;
+ register size_t *tl = tolengths;
while (copynum--)
{
if (*t)
- { if (*fl != *tl || bcmp (*f, *t, *fl)) return 0; }
+ { if (*fl != *tl || memcmp (*f, *t, *fl)) return 0; }
else
{ *t = *f ; *tl = *fl; }
@@ -831,42 +824,42 @@ create_diff3_block (low0, high0, low1, high1, low2, high2)
if (numlines)
{
D_LINEARRAY (result, FILE0) = ALLOCATE (numlines, char *);
- D_LENARRAY (result, FILE0) = ALLOCATE (numlines, int);
+ D_LENARRAY (result, FILE0) = ALLOCATE (numlines, size_t);
bzero (D_LINEARRAY (result, FILE0), (numlines * sizeof (char *)));
- bzero (D_LENARRAY (result, FILE0), (numlines * sizeof (int)));
+ bzero (D_LENARRAY (result, FILE0), (numlines * sizeof (size_t)));
}
else
{
- D_LINEARRAY (result, FILE0) = (char **) 0;
- D_LENARRAY (result, FILE0) = (int *) 0;
+ D_LINEARRAY (result, FILE0) = 0;
+ D_LENARRAY (result, FILE0) = 0;
}
numlines = D_NUMLINES (result, FILE1);
if (numlines)
{
D_LINEARRAY (result, FILE1) = ALLOCATE (numlines, char *);
- D_LENARRAY (result, FILE1) = ALLOCATE (numlines, int);
+ D_LENARRAY (result, FILE1) = ALLOCATE (numlines, size_t);
bzero (D_LINEARRAY (result, FILE1), (numlines * sizeof (char *)));
- bzero (D_LENARRAY (result, FILE1), (numlines * sizeof (int)));
+ bzero (D_LENARRAY (result, FILE1), (numlines * sizeof (size_t)));
}
else
{
- D_LINEARRAY (result, FILE1) = (char **) 0;
- D_LENARRAY (result, FILE1) = (int *) 0;
+ D_LINEARRAY (result, FILE1) = 0;
+ D_LENARRAY (result, FILE1) = 0;
}
numlines = D_NUMLINES (result, FILE2);
if (numlines)
{
D_LINEARRAY (result, FILE2) = ALLOCATE (numlines, char *);
- D_LENARRAY (result, FILE2) = ALLOCATE (numlines, int);
+ D_LENARRAY (result, FILE2) = ALLOCATE (numlines, size_t);
bzero (D_LINEARRAY (result, FILE2), (numlines * sizeof (char *)));
- bzero (D_LENARRAY (result, FILE2), (numlines * sizeof (int)));
+ bzero (D_LENARRAY (result, FILE2), (numlines * sizeof (size_t)));
}
else
{
- D_LINEARRAY (result, FILE2) = (char **) 0;
- D_LENARRAY (result, FILE2) = (int *) 0;
+ D_LINEARRAY (result, FILE2) = 0;
+ D_LENARRAY (result, FILE2) = 0;
}
/* Return */
@@ -879,20 +872,20 @@ create_diff3_block (low0, high0, low1, high1, low2, high2)
*/
static int
compare_line_list (list1, lengths1, list2, lengths2, nl)
- char *list1[], *list2[];
- int *lengths1, *lengths2;
+ char * const list1[], * const list2[];
+ size_t const lengths1[], lengths2[];
int nl;
{
char
- **l1 = list1,
- **l2 = list2;
- int
+ * const *l1 = list1,
+ * const *l2 = list2;
+ size_t const
*lgths1 = lengths1,
*lgths2 = lengths2;
while (nl--)
if (!*l1 || !*l2 || *lgths1 != *lgths2++
- || bcmp (*l1++, *l2++, *lgths1++))
+ || memcmp (*l1++, *l2++, *lgths1++))
return 0;
return 1;
}
@@ -907,7 +900,7 @@ extern char **environ;
static struct diff_block *
process_diff (filea, fileb, last_block)
- char *filea, *fileb;
+ char const *filea, *fileb;
struct diff_block **last_block;
{
char *diff_contents;
@@ -920,12 +913,13 @@ process_diff (filea, fileb, last_block)
diff_limit = read_diff (filea, fileb, &diff_contents);
scan_diff = diff_contents;
block_list_end = &block_list;
+ bptr = 0; /* Pacify `gcc -W'. */
while (scan_diff < diff_limit)
{
bptr = ALLOCATE (1, struct diff_block);
- bptr->lines[0] = bptr->lines[1] = (char **) 0;
- bptr->lengths[0] = bptr->lengths[1] = (int *) 0;
+ bptr->lines[0] = bptr->lines[1] = 0;
+ bptr->lengths[0] = bptr->lengths[1] = 0;
dt = process_diff_control (&scan_diff, bptr);
if (dt == ERROR || *scan_diff != '\n')
@@ -962,7 +956,7 @@ process_diff (filea, fileb, last_block)
{
int numlines = D_NUMLINES (bptr, 0);
bptr->lines[0] = ALLOCATE (numlines, char *);
- bptr->lengths[0] = ALLOCATE (numlines, int);
+ bptr->lengths[0] = ALLOCATE (numlines, size_t);
for (i = 0; i < numlines; i++)
scan_diff = scan_diff_line (scan_diff,
&(bptr->lines[0][i]),
@@ -985,7 +979,7 @@ process_diff (filea, fileb, last_block)
{
int numlines = D_NUMLINES (bptr, 1);
bptr->lines[1] = ALLOCATE (numlines, char *);
- bptr->lengths[1] = ALLOCATE (numlines, int);
+ bptr->lengths[1] = ALLOCATE (numlines, size_t);
for (i = 0; i < numlines; i++)
scan_diff = scan_diff_line (scan_diff,
&(bptr->lines[1][i]),
@@ -1038,9 +1032,9 @@ process_diff_control (string, db)
#define SKIPWHITE(s) { while (*s == ' ' || *s == '\t') s++; }
#define READNUM(s, num) \
- { if (!isdigit (*s)) return ERROR; holdnum = 0; \
- do { holdnum = (*s++ - '0' + holdnum * 10); } \
- while (isdigit (*s)); (num) = holdnum; }
+ { unsigned char c = *s; if (!isdigit (c)) return ERROR; holdnum = 0; \
+ do { holdnum = (c - '0' + holdnum * 10); } \
+ while (isdigit (c = *++s)); (num) = holdnum; }
/* Read first set of digits */
SKIPWHITE (s);
@@ -1097,18 +1091,16 @@ process_diff_control (string, db)
static char *
read_diff (filea, fileb, output_placement)
- char *filea, *fileb;
+ char const *filea, *fileb;
char **output_placement;
{
- char *argv[7];
+ char *diff_result;
+ size_t bytes, current_chunk_size, total;
+ char const *argv[7];
char horizon_arg[256];
- char **ap;
+ char const **ap;
int fds[2];
- char *diff_result;
- int current_chunk_size;
- int bytes;
- int total;
- int pid, w;
+ pid_t pid;
int wstatus;
ap = argv;
@@ -1120,7 +1112,7 @@ read_diff (filea, fileb, output_placement)
*ap++ = "--";
*ap++ = filea;
*ap++ = fileb;
- *ap = (char *) 0;
+ *ap = 0;
if (pipe (fds) < 0)
perror_with_exit ("pipe failed");
@@ -1130,15 +1122,15 @@ read_diff (filea, fileb, output_placement)
{
/* Child */
close (fds[0]);
- if (fds[1] != fileno (stdout))
+ if (fds[1] != STDOUT_FILENO)
{
- dup2 (fds[1], fileno (stdout));
+ dup2 (fds[1], STDOUT_FILENO);
close (fds[1]);
}
- execve (diff_program, argv, environ);
+ execve (diff_program, (char **) argv, environ);
/* Avoid stdio, because the parent process's buffers are inherited. */
- write (fileno (stderr), diff_program, strlen (diff_program));
- write (fileno (stderr), ": not found\n", 12);
+ write (STDERR_FILENO, diff_program, strlen (diff_program));
+ write (STDERR_FILENO, ": not found\n", 12);
_exit (2);
}
@@ -1147,7 +1139,7 @@ read_diff (filea, fileb, output_placement)
close (fds[1]); /* Prevent erroneous lack of EOF */
current_chunk_size = DIFF_CHUNK_SIZE;
- diff_result = (char *) xmalloc (current_chunk_size);
+ diff_result = xmalloc (current_chunk_size);
total = 0;
do {
bytes = myread (fds[0],
@@ -1155,7 +1147,15 @@ read_diff (filea, fileb, output_placement)
current_chunk_size - total);
total += bytes;
if (total == current_chunk_size)
- diff_result = (char *) xrealloc (diff_result, (current_chunk_size *= 2));
+ {
+ if (current_chunk_size < 2 * current_chunk_size)
+ current_chunk_size = 2 * current_chunk_size;
+ else if (current_chunk_size < (size_t) -1)
+ current_chunk_size = (size_t) -1;
+ else
+ fatal ("files are too large to fit into memory");
+ diff_result = xrealloc (diff_result, (current_chunk_size *= 2));
+ }
} while (bytes);
if (total != 0 && diff_result[total-1] != '\n')
@@ -1163,10 +1163,18 @@ read_diff (filea, fileb, output_placement)
*output_placement = diff_result;
- do
- if ((w = wait (&wstatus)) == -1)
+#if HAVE_WAITPID
+ if (waitpid (pid, &wstatus, 0) < 0)
+ perror_with_exit ("waitpid failed");
+#else
+ for (;;) {
+ pid_t w = wait (&wstatus);
+ if (w < 0)
perror_with_exit ("wait failed");
- while (w != pid);
+ if (w == pid)
+ break;
+ }
+#endif
if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2))
fatal ("subsidiary diff failed");
@@ -1185,7 +1193,7 @@ read_diff (filea, fileb, output_placement)
static char *
scan_diff_line (scan_ptr, set_start, set_length, limit, firstchar)
char *scan_ptr, **set_start;
- int *set_length;
+ size_t *set_length;
char *limit;
char firstchar;
{
@@ -1238,16 +1246,17 @@ static void
output_diff3 (outputfile, diff, mapping, rev_mapping)
FILE *outputfile;
struct diff3_block *diff;
- int mapping[3], rev_mapping[3];
+ int const mapping[3], rev_mapping[3];
{
int i;
int oddoneout;
char *cp;
struct diff3_block *ptr;
int line;
- int length;
+ size_t length;
int dontprint;
static int skew_increment[3] = { 2, 3, 1 }; /* 0==>2==>1==>3 */
+ char const *line_prefix = tab_align_flag ? "\t" : " ";
for (ptr = diff; ptr; ptr = D_NEXT (ptr))
{
@@ -1299,15 +1308,20 @@ output_diff3 (outputfile, diff, mapping, rev_mapping)
if (i == dontprint) continue;
- for (line = 0; line < hight - lowt + 1; line++)
+ if (lowt <= hight)
{
- fprintf (outputfile, " ");
- cp = D_RELNUM (ptr, realfile, line);
- length = D_RELLEN (ptr, realfile, line);
- fwrite (cp, sizeof (char), length, outputfile);
+ line = 0;
+ do
+ {
+ fprintf (outputfile, line_prefix);
+ cp = D_RELNUM (ptr, realfile, line);
+ length = D_RELLEN (ptr, realfile, line);
+ fwrite (cp, sizeof (char), length, outputfile);
+ }
+ while (++line < hight - lowt + 1);
+ if (cp[length - 1] != '\n')
+ fprintf (outputfile, "\n\\ No newline at end of file\n");
}
- if (line != 0 && cp[length - 1] != '\n')
- fprintf (outputfile, "\n\\ No newline at end of file\n");
}
}
}
@@ -1388,8 +1402,8 @@ output_diff3_edscript (outputfile, diff, mapping, rev_mapping,
file0, file1, file2)
FILE *outputfile;
struct diff3_block *diff;
- int mapping[3], rev_mapping[3];
- char *file0, *file1, *file2;
+ int const mapping[3], rev_mapping[3];
+ char const *file0, *file1, *file2;
{
int leading_dot;
int conflicts_found = 0, conflict;
@@ -1515,8 +1529,8 @@ output_diff3_merge (infile, outputfile, diff, mapping, rev_mapping,
file0, file1, file2)
FILE *infile, *outputfile;
struct diff3_block *diff;
- int mapping[3], rev_mapping[3];
- char *file0, *file1, *file2;
+ int const mapping[3], rev_mapping[3];
+ char const *file0, *file1, *file2;
{
int c, i;
int conflicts_found = 0, conflict;
@@ -1532,7 +1546,7 @@ output_diff3_merge (infile, outputfile, diff, mapping, rev_mapping,
((enum diff_type)
(((int) DIFF_1ST)
+ rev_mapping[(int) b->correspond - (int) DIFF_1ST])));
- char *format_2nd = "<<<<<<< %s\n";
+ char const *format_2nd = "<<<<<<< %s\n";
/* If we aren't supposed to do this output block, skip it. */
switch (type)
@@ -1630,8 +1644,7 @@ reverse_diff3_blocklist (diff)
{
register struct diff3_block *tmp, *next, *prev;
- for (tmp = diff, prev = (struct diff3_block *) 0;
- tmp; tmp = next)
+ for (tmp = diff, prev = 0; tmp; tmp = next)
{
next = tmp->next;
tmp->next = prev;
@@ -1641,41 +1654,42 @@ reverse_diff3_blocklist (diff)
return prev;
}
-static int
+static size_t
myread (fd, ptr, size)
- int fd, size;
+ int fd;
char *ptr;
+ size_t size;
{
- int result = read (fd, ptr, size);
- if (result < 0)
+ size_t result = read (fd, ptr, size);
+ if (result == -1)
perror_with_exit ("read failed");
return result;
}
-VOID *
+static VOID *
xmalloc (size)
- unsigned size;
+ size_t size;
{
VOID *result = (VOID *) malloc (size ? size : 1);
if (!result)
- fatal ("virtual memory exhausted");
+ fatal ("memory exhausted");
return result;
}
static VOID *
xrealloc (ptr, size)
VOID *ptr;
- unsigned size;
+ size_t size;
{
VOID *result = (VOID *) realloc (ptr, size ? size : 1);
if (!result)
- fatal ("virtual memory exhausted");
+ fatal ("memory exhausted");
return result;
}
static void
fatal (string)
- char *string;
+ char const *string;
{
fprintf (stderr, "%s: %s\n", argv0, string);
exit (2);
@@ -1683,7 +1697,7 @@ fatal (string)
static void
perror_with_exit (string)
- char *string;
+ char const *string;
{
int e = errno;
fprintf (stderr, "%s: ", argv0);
diff --git a/gnu/usr.bin/diff/dir.c b/gnu/usr.bin/diff/dir.c
index a5e3e2c..baadfbf 100644
--- a/gnu/usr.bin/diff/dir.c
+++ b/gnu/usr.bin/diff/dir.c
@@ -1,5 +1,5 @@
/* Read, sort and compare two directories. Used for GNU DIFF.
- Copyright (C) 1988, 1989, 1992 Free Software Foundation, Inc.
+ Copyright (C) 1988, 1989, 1992, 1993 Free Software Foundation, Inc.
This file is part of GNU DIFF.
@@ -19,8 +19,6 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "diff.h"
-static int compare_names ();
-
/* Read the directory named by DIR and store into DIRDATA a sorted vector
of filenames for its contents. DIR->desc == -1 means this directory is
known to be nonexistent, so set DIRDATA to an empty vector.
@@ -28,31 +26,35 @@ static int compare_names ();
struct dirdata
{
- char **files; /* Sorted names of files in dir, terminated by (char *) 0. */
+ char const **names; /* Sorted names of files in dir, 0-terminated. */
char *data; /* Allocated storage for file names. */
};
+static int compare_names PARAMS((void const *, void const *));
+static int dir_sort PARAMS((struct file_data const *, struct dirdata *));
+
static int
dir_sort (dir, dirdata)
- struct file_data *dir;
+ struct file_data const *dir;
struct dirdata *dirdata;
{
- register struct direct *next;
+ register struct dirent *next;
register int i;
/* Address of block containing the files that are described. */
- char **files;
+ char const **names;
/* Number of files in directory. */
- int nfiles;
+ size_t nnames;
/* Allocated and used storage for file name data. */
char *data;
size_t data_alloc, data_used;
- dirdata->files = 0;
+ dirdata->names = 0;
dirdata->data = 0;
- nfiles = 0;
+ nnames = 0;
+ data = 0;
if (dir->desc != -1)
{
@@ -65,7 +67,7 @@ dir_sort (dir, dirdata)
data_alloc = max (1, (size_t) dir->stat.st_size);
data_used = 0;
- dirdata->data = data = (char *) xmalloc (data_alloc);
+ dirdata->data = data = xmalloc (data_alloc);
/* Read the directory entries, and insert the subfiles
into the `data' table. */
@@ -85,10 +87,10 @@ dir_sort (dir, dirdata)
d_size = strlen (d_name) + 1;
while (data_alloc < data_used + d_size)
- dirdata->data = data = (char *) xrealloc (data, data_alloc *= 2);
- bcopy (d_name, data + data_used, d_size);
+ dirdata->data = data = xrealloc (data, data_alloc *= 2);
+ memcpy (data + data_used, d_name, d_size);
data_used += d_size;
- nfiles++;
+ nnames++;
}
if (errno)
{
@@ -97,7 +99,7 @@ dir_sort (dir, dirdata)
errno = e;
return -1;
}
-#ifdef VOID_CLOSEDIR
+#if VOID_CLOSEDIR
closedir (reading);
#else
if (closedir (reading) != 0)
@@ -105,17 +107,18 @@ dir_sort (dir, dirdata)
#endif
}
- /* Create the `files' table from the `data' table. */
- dirdata->files = files = (char **) xmalloc (sizeof (char *) * (nfiles + 1));
- for (i = 0; i < nfiles; i++)
+ /* Create the `names' table from the `data' table. */
+ dirdata->names = names = (char const **) xmalloc (sizeof (char *)
+ * (nnames + 1));
+ for (i = 0; i < nnames; i++)
{
- files[i] = data;
+ names[i] = data;
data += strlen (data) + 1;
}
- files[nfiles] = 0;
+ names[nnames] = 0;
/* Sort the table. */
- qsort (files, nfiles, sizeof (char *), compare_names);
+ qsort (names, nnames, sizeof (char *), compare_names);
return 0;
}
@@ -124,9 +127,9 @@ dir_sort (dir, dirdata)
static int
compare_names (file1, file2)
- char **file1, **file2;
+ void const *file1, *file2;
{
- return strcmp (*file1, *file2);
+ return strcmp (* (char const *const *) file1, * (char const *const *) file2);
}
/* Compare the contents of two directories named in FILEVEC[0] and FILEVEC[1].
@@ -151,8 +154,8 @@ compare_names (file1, file2)
int
diff_dirs (filevec, handle_file, depth)
- struct file_data filevec[];
- int (*handle_file) ();
+ struct file_data const filevec[];
+ int (*handle_file) PARAMS((char const *, char const *, char const *, char const *, int));
int depth;
{
struct dirdata dirdata[2];
@@ -169,42 +172,42 @@ diff_dirs (filevec, handle_file, depth)
if (val == 0)
{
- register char **files0 = dirdata[0].files;
- register char **files1 = dirdata[1].files;
- char *name0 = filevec[0].name;
- char *name1 = filevec[1].name;
+ register char const * const *names0 = dirdata[0].names;
+ register char const * const *names1 = dirdata[1].names;
+ char const *name0 = filevec[0].name;
+ char const *name1 = filevec[1].name;
/* If `-S name' was given, and this is the topmost level of comparison,
ignore all file names less than the specified starting name. */
if (dir_start_file && depth == 0)
{
- while (*files0 && strcmp (*files0, dir_start_file) < 0)
- files0++;
- while (*files1 && strcmp (*files1, dir_start_file) < 0)
- files1++;
+ while (*names0 && strcmp (*names0, dir_start_file) < 0)
+ names0++;
+ while (*names1 && strcmp (*names1, dir_start_file) < 0)
+ names1++;
}
/* Loop while files remain in one or both dirs. */
- while (*files0 || *files1)
+ while (*names0 || *names1)
{
/* Compare next name in dir 0 with next name in dir 1.
At the end of a dir,
pretend the "next name" in that dir is very large. */
- int nameorder = (!*files0 ? 1 : !*files1 ? -1
- : strcmp (*files0, *files1));
- int v1 = (*handle_file) (name0, 0 < nameorder ? 0 : *files0++,
- name1, nameorder < 0 ? 0 : *files1++,
+ int nameorder = (!*names0 ? 1 : !*names1 ? -1
+ : strcmp (*names0, *names1));
+ int v1 = (*handle_file) (name0, 0 < nameorder ? 0 : *names0++,
+ name1, nameorder < 0 ? 0 : *names1++,
depth + 1);
if (v1 > val)
val = v1;
}
}
-
+
for (i = 0; i < 2; i++)
{
- if (dirdata[i].files)
- free (dirdata[i].files);
+ if (dirdata[i].names)
+ free (dirdata[i].names);
if (dirdata[i].data)
free (dirdata[i].data);
}
diff --git a/gnu/usr.bin/diff/ed.c b/gnu/usr.bin/diff/ed.c
index fd051f2..717ef35 100644
--- a/gnu/usr.bin/diff/ed.c
+++ b/gnu/usr.bin/diff/ed.c
@@ -1,5 +1,5 @@
/* Output routines for ed-script format.
- Copyright (C) 1988, 89, 91, 92 Free Software Foundation, Inc.
+ Copyright (C) 1988, 89, 91, 92, 93 Free Software Foundation, Inc.
This file is part of GNU DIFF.
@@ -19,14 +19,9 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "diff.h"
-int change_letter ();
-int translate_line_number ();
-static void print_rcs_hunk ();
-static void print_ed_hunk ();
-static void pr_forward_ed_hunk ();
-void translate_range ();
-struct change *find_change ();
-struct change *find_reverse_change ();
+static void print_ed_hunk PARAMS((struct change *));
+static void print_rcs_hunk PARAMS((struct change *));
+static void pr_forward_ed_hunk PARAMS((struct change *));
/* Print our script as ed commands. */
diff --git a/gnu/usr.bin/diff/getopt.c b/gnu/usr.bin/diff/getopt.c
index a59a013..7a4673b 100644
--- a/gnu/usr.bin/diff/getopt.c
+++ b/gnu/usr.bin/diff/getopt.c
@@ -20,31 +20,24 @@
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-/* NOTE!!! AIX requires this to be the first thing in the file.
- Do not put ANYTHING before it! */
-#if !defined (__GNUC__) && defined (_AIX)
- #pragma alloca
-#endif
-
#ifdef HAVE_CONFIG_H
+#if defined (emacs) || defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
#include "config.h"
#endif
-
-#ifdef __GNUC__
-#define alloca __builtin_alloca
-#else /* not __GNUC__ */
-#if defined (HAVE_ALLOCA_H) || (defined(sparc) && (defined(sun) || (!defined(USG) && !defined(SVR4) && !defined(__svr4__))))
-#include <alloca.h>
-#else
-#ifndef _AIX
-char *alloca ();
#endif
-#endif /* alloca.h */
-#endif /* not __GNUC__ */
-#if !__STDC__ && !defined(const) && IN_GCC
+#ifndef __STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
#define const
#endif
+#endif
/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. */
#ifndef _NO_PROTO
@@ -67,12 +60,9 @@ char *alloca ();
/* This needs to come after some library #include
to get __GNU_LIBRARY__ defined. */
#ifdef __GNU_LIBRARY__
-#undef alloca
/* Don't include stdlib.h for non-GNU C libraries because some of them
contain conflicting prototypes for getopt. */
#include <stdlib.h>
-#else /* Not GNU C library. */
-#define __alloca alloca
#endif /* GNU C library. */
/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
@@ -180,7 +170,6 @@ static enum
in GCC. */
#include <string.h>
#define my_index strchr
-#define my_bcopy(src, dst, n) memcpy ((dst), (src), (n))
#else
/* Avoid depending on library functions or files
@@ -202,16 +191,19 @@ my_index (str, chr)
return 0;
}
-static void
-my_bcopy (from, to, size)
- const char *from;
- char *to;
- int size;
-{
- int i;
- for (i = 0; i < size; i++)
- to[i] = from[i];
-}
+/* If using GCC, we can safely declare strlen this way.
+ If not using GCC, it is ok not to declare it.
+ (Supposedly there are some machines where it might get a warning,
+ but changing this conditional to __STDC__ is too risky.) */
+#ifdef __GNUC__
+#ifdef IN_GCC
+#include "gstddef.h"
+#else
+#include <stddef.h>
+#endif
+extern size_t strlen (const char *);
+#endif
+
#endif /* GNU C library. */
/* Handle permutation of arguments. */
@@ -236,17 +228,51 @@ static void
exchange (argv)
char **argv;
{
- int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *);
- char **temp = (char **) __alloca (nonopts_size);
+ int bottom = first_nonopt;
+ int middle = last_nonopt;
+ int top = optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
- /* Interchange the two blocks of data in ARGV. */
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
- my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size);
- my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt],
- (optind - last_nonopt) * sizeof (char *));
- my_bcopy ((char *) temp,
- (char *) &argv[first_nonopt + optind - last_nonopt],
- nonopts_size);
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
/* Update records for the slots the non-options now occupy. */
diff --git a/gnu/usr.bin/diff/getopt1.c b/gnu/usr.bin/diff/getopt1.c
index a32615c..f784b57 100644
--- a/gnu/usr.bin/diff/getopt1.c
+++ b/gnu/usr.bin/diff/getopt1.c
@@ -17,14 +17,25 @@
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifdef HAVE_CONFIG_H
+#if defined (emacs) || defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
#include "config.h"
#endif
+#endif
#include "getopt.h"
-#if !__STDC__ && !defined(const) && IN_GCC
+#ifndef __STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
#define const
#endif
+#endif
#include <stdio.h>
diff --git a/gnu/usr.bin/diff/ifdef.c b/gnu/usr.bin/diff/ifdef.c
index c5dde5c..4e81ef8 100644
--- a/gnu/usr.bin/diff/ifdef.c
+++ b/gnu/usr.bin/diff/ifdef.c
@@ -1,5 +1,5 @@
/* #ifdef-format output routines for GNU DIFF.
- Copyright (C) 1989, 91, 92 Free Software Foundation, Inc.
+ Copyright (C) 1989, 91, 92, 93 Free Software Foundation, Inc.
This file is part of GNU DIFF.
@@ -21,10 +21,19 @@ and this notice must be preserved on all copies. */
#include "diff.h"
-static void format_ifdef ();
-static void print_ifdef_hunk ();
-static void print_ifdef_lines ();
-struct change *find_change ();
+struct group
+{
+ struct file_data const *file;
+ int from, upto; /* start and limit lines for this group of lines */
+};
+
+static char *format_group PARAMS((FILE *, char *, int, struct group const[]));
+static char *scan_char_literal PARAMS((char *, int *));
+static char *scan_printf_spec PARAMS((char *));
+static int groups_letter_value PARAMS((struct group const[], int));
+static void format_ifdef PARAMS((char *, int, int, int, int));
+static void print_ifdef_hunk PARAMS((struct change *));
+static void print_ifdef_lines PARAMS((FILE *, char *, struct group const *));
static int next_line;
@@ -40,7 +49,8 @@ print_ifdef_script (script)
{
begin_output ();
format_ifdef (group_format[UNCHANGED], next_line, files[0].valid_lines,
- 0, -1);
+ next_line - files[0].valid_lines + files[1].valid_lines,
+ files[1].valid_lines);
}
}
@@ -53,7 +63,7 @@ print_ifdef_hunk (hunk)
struct change *hunk;
{
int first0, last0, first1, last1, deletes, inserts;
- const char *format;
+ char *format;
/* Determine range of line numbers involved in each file. */
analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts);
@@ -68,7 +78,8 @@ print_ifdef_hunk (hunk)
/* Print lines up to this change. */
if (next_line < first0)
- format_ifdef (group_format[UNCHANGED], next_line, first0, 0, -1);
+ format_ifdef (group_format[UNCHANGED], next_line, first0,
+ next_line - first0 + first1, first1);
/* Print this change. */
next_line = last0 + 1;
@@ -76,87 +87,223 @@ print_ifdef_hunk (hunk)
}
/* Print a set of lines according to FORMAT.
- Lines BEG0 up to END0 are from the first file.
- If END1 is -1, then the second file's lines are identical to the first;
- otherwise, lines BEG1 up to END1 are from the second file. */
+ Lines BEG0 up to END0 are from the first file;
+ lines BEG1 up to END1 are from the second file. */
static void
format_ifdef (format, beg0, end0, beg1, end1)
- const char *format;
+ char *format;
int beg0, end0, beg1, end1;
{
- register FILE *out = outfile;
+ struct group groups[2];
+
+ groups[0].file = &files[0];
+ groups[0].from = beg0;
+ groups[0].upto = end0;
+ groups[1].file = &files[1];
+ groups[1].from = beg1;
+ groups[1].upto = end1;
+ format_group (outfile, format, '\0', groups);
+}
+
+/* Print to file OUT a set of lines according to FORMAT.
+ The format ends at the first free instance of ENDCHAR.
+ Yield the address of the terminating character.
+ GROUPS specifies which lines to print.
+ If OUT is zero, do not actually print anything; just scan the format. */
+
+static char *
+format_group (out, format, endchar, groups)
+ register FILE *out;
+ char *format;
+ int endchar;
+ struct group const groups[];
+{
register char c;
- register const char *f = format;
+ register char *f = format;
- while ((c = *f++) != 0)
+ while ((c = *f) != endchar && c != 0)
{
+ f++;
if (c == '%')
- switch ((c = *f++))
- {
- case 0:
- return;
-
- case '<':
- /* Print lines deleted from first file. */
- print_ifdef_lines (line_format[OLD], &files[0], beg0, end0);
- continue;
-
- case '=':
- /* Print common lines. */
- print_ifdef_lines (line_format[UNCHANGED], &files[0], beg0, end0);
- continue;
-
- case '>':
- /* Print lines inserted from second file. */
- if (end1 == -1)
- print_ifdef_lines (line_format[NEW], &files[0], beg0, end0);
- else
- print_ifdef_lines (line_format[NEW], &files[1], beg1, end1);
- continue;
-
- case '0':
- c = 0;
- break;
-
- default:
- break;
- }
- putc (c, out);
- }
+ {
+ char *spec = f;
+ switch ((c = *f++))
+ {
+ case '%':
+ break;
+
+ case '(':
+ /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'. */
+ {
+ int i, value[2];
+ FILE *thenout, *elseout;
+
+ for (i = 0; i < 2; i++)
+ {
+ unsigned char f0 = f[0];
+ if (isdigit (f0))
+ {
+ value[i] = atoi (f);
+ while (isdigit ((unsigned char) *++f))
+ continue;
+ }
+ else
+ {
+ value[i] = groups_letter_value (groups, f0);
+ if (value[i] < 0)
+ goto bad_format;
+ f++;
+ }
+ if (*f++ != "=?"[i])
+ goto bad_format;
+ }
+ if (value[0] == value[1])
+ thenout = out, elseout = 0;
+ else
+ thenout = 0, elseout = out;
+ f = format_group (thenout, f, ':', groups);
+ if (*f)
+ {
+ f = format_group (elseout, f + 1, ')', groups);
+ if (*f)
+ f++;
+ }
+ }
+ continue;
+
+ case '<':
+ /* Print lines deleted from first file. */
+ print_ifdef_lines (out, line_format[OLD], &groups[0]);
+ continue;
+
+ case '=':
+ /* Print common lines. */
+ print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]);
+ continue;
+
+ case '>':
+ /* Print lines inserted from second file. */
+ print_ifdef_lines (out, line_format[NEW], &groups[1]);
+ continue;
+
+ default:
+ {
+ int value;
+ char *speclim;
+
+ f = scan_printf_spec (spec);
+ if (!f)
+ goto bad_format;
+ speclim = f;
+ c = *f++;
+ switch (c)
+ {
+ case '\'':
+ f = scan_char_literal (f, &value);
+ if (!f)
+ goto bad_format;
+ break;
+
+ default:
+ value = groups_letter_value (groups, c);
+ if (value < 0)
+ goto bad_format;
+ break;
+ }
+ if (out)
+ {
+ /* Temporarily replace e.g. "%3dnx" with "%3d\0x". */
+ *speclim = 0;
+ fprintf (out, spec - 1, value);
+ /* Undo the temporary replacement. */
+ *speclim = c;
+ }
+ }
+ continue;
+
+ bad_format:
+ c = '%';
+ f = spec;
+ break;
+ }
+ }
+ if (out)
+ putc (c, out);
+ }
+ return f;
+}
+
+/* For the line group pair G, return the number corresponding to LETTER.
+ Return -1 if LETTER is not a group format letter. */
+static int
+groups_letter_value (g, letter)
+ struct group const g[];
+ int letter;
+{
+ if (isupper (letter))
+ {
+ g++;
+ letter = tolower (letter);
+ }
+ switch (letter)
+ {
+ case 'e': return translate_line_number (g->file, g->from) - 1;
+ case 'f': return translate_line_number (g->file, g->from);
+ case 'l': return translate_line_number (g->file, g->upto) - 1;
+ case 'm': return translate_line_number (g->file, g->upto);
+ case 'n': return g->upto - g->from;
+ default: return -1;
+ }
}
-/* Use FORMAT to print each line of CURRENT starting with FROM
- and continuing up to UPTO. */
+/* Print to file OUT, using FORMAT to print the line group GROUP.
+ But do nothing if OUT is zero. */
static void
-print_ifdef_lines (format, current, from, upto)
- const char *format;
- const struct file_data *current;
- int from, upto;
+print_ifdef_lines (out, format, group)
+ register FILE *out;
+ char *format;
+ struct group const *group;
{
- const char * const *linbuf = current->linbuf;
+ struct file_data const *file = group->file;
+ char const * const *linbuf = file->linbuf;
+ int from = group->from, upto = group->upto;
+
+ if (!out)
+ return;
/* If possible, use a single fwrite; it's faster. */
- if (!tab_expand_flag && strcmp (format, "%l\n") == 0)
- fwrite (linbuf[from], sizeof (char),
- linbuf[upto] + (linbuf[upto][-1] != '\n') - linbuf[from],
- outfile);
- else if (!tab_expand_flag && strcmp (format, "%L") == 0)
- fwrite (linbuf[from], sizeof (char), linbuf[upto] - linbuf[from], outfile);
- else
- for (; from < upto; from++)
- {
- register FILE *out = outfile;
- register char c;
- register const char *f = format;
+ if (!tab_expand_flag && format[0] == '%')
+ {
+ if (format[1] == 'l' && format[2] == '\n' && !format[3])
+ {
+ fwrite (linbuf[from], sizeof (char),
+ linbuf[upto] + (linbuf[upto][-1] != '\n') - linbuf[from],
+ out);
+ return;
+ }
+ if (format[1] == 'L' && !format[2])
+ {
+ fwrite (linbuf[from], sizeof (char),
+ linbuf[upto] - linbuf[from], out);
+ return;
+ }
+ }
- while ((c = *f++) != 0)
- {
- if (c == '%')
+ for (; from < upto; from++)
+ {
+ register char c;
+ register char *f = format;
+
+ while ((c = *f++) != 0)
+ {
+ if (c == '%')
+ {
+ char *spec = f;
switch ((c = *f++))
{
- case 0:
- goto format_done;
+ case '%':
+ break;
case 'l':
output_1_line (linbuf[from],
@@ -168,16 +315,114 @@ print_ifdef_lines (format, current, from, upto)
output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
continue;
- case '0':
- c = 0;
- break;
-
default:
+ {
+ int value;
+ char *speclim;
+
+ f = scan_printf_spec (spec);
+ if (!f)
+ goto bad_format;
+ speclim = f;
+ c = *f++;
+ switch (c)
+ {
+ case '\'':
+ f = scan_char_literal (f, &value);
+ if (!f)
+ goto bad_format;
+ break;
+
+ case 'n':
+ value = translate_line_number (file, from);
+ break;
+
+ default:
+ goto bad_format;
+ }
+ /* Temporarily replace e.g. "%3dnx" with "%3d\0x". */
+ *speclim = 0;
+ fprintf (out, spec - 1, value);
+ /* Undo the temporary replacement. */
+ *speclim = c;
+ }
+ continue;
+
+ bad_format:
+ c = '%';
+ f = spec;
break;
}
- putc (c, out);
+ }
+ putc (c, out);
+ }
+ }
+}
+
+/* Scan the character literal represented in the string LIT; LIT points just
+ after the initial apostrophe. Put the literal's value into *INTPTR.
+ Yield the address of the first character after the closing apostrophe,
+ or zero if the literal is ill-formed. */
+static char *
+scan_char_literal (lit, intptr)
+ char *lit;
+ int *intptr;
+{
+ register char *p = lit;
+ int value, digits;
+ char c = *p++;
+
+ switch (c)
+ {
+ case 0:
+ case '\'':
+ return 0;
+
+ case '\\':
+ value = 0;
+ while ((c = *p++) != '\'')
+ {
+ unsigned digit = c - '0';
+ if (8 <= digit)
+ return 0;
+ value = 8 * value + digit;
}
+ digits = p - lit - 2;
+ if (! (1 <= digits && digits <= 3))
+ return 0;
+ break;
+
+ default:
+ value = c;
+ if (*p++ != '\'')
+ return 0;
+ break;
+ }
+ *intptr = value;
+ return p;
+}
+
+/* Scan optional printf-style SPEC of the form `-*[0-9]*(.[0-9]*)?[cdoxX]'.
+ Return the address of the character following SPEC, or zero if failure. */
+static char *
+scan_printf_spec (spec)
+ register char *spec;
+{
+ register unsigned char c;
- format_done:;
- }
+ while ((c = *spec++) == '-')
+ continue;
+ while (isdigit (c))
+ c = *spec++;
+ if (c == '.')
+ while (isdigit (c = *spec++))
+ continue;
+ switch (c)
+ {
+ case 'c': case 'd': case 'o': case 'x': case 'X':
+ return spec;
+
+ default:
+ return 0;
+ }
}
diff --git a/gnu/usr.bin/diff/io.c b/gnu/usr.bin/diff/io.c
index a0633cd..95702ba 100644
--- a/gnu/usr.bin/diff/io.c
+++ b/gnu/usr.bin/diff/io.c
@@ -1,5 +1,5 @@
/* File I/O for GNU DIFF.
- Copyright (C) 1988, 1989, 1992 Free Software Foundation, Inc.
+ Copyright (C) 1988, 1989, 1992, 1993 Free Software Foundation, Inc.
This file is part of GNU DIFF.
@@ -26,51 +26,12 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/* Given a hash value and a new character, return a new hash value. */
#define HASH(h, c) ((c) + ROL (h, 7))
-int line_cmp ();
-
/* Guess remaining number of lines from number N of lines so far,
size S so far, and total size T. */
#define GUESS_LINES(n,s,t) (((t) - (s)) / ((n) < 10 ? 32 : (s) / ((n)-1)) + 5)
/* Type used for fast prefix comparison in find_identical_ends. */
-typedef long word;
-
-/* Character classes. */
-const char textchar[] = {
- /* ISO 8859 */
- 0, 0, 0, 0, 0, 0, 0, 0,
- 2, 2, 1, 2, 2, 2, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 2, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 2, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1
-};
+typedef int word;
/* Lines are put into equivalence classes (of lines that match in line_cmp).
Each equivalence class is represented by one of these structures,
@@ -80,8 +41,8 @@ struct equivclass
{
int next; /* Next item in this bucket. */
unsigned hash; /* Hash of lines in this class. */
- const char *line; /* A line that fits this class. */
- int length; /* The length of that line. */
+ char const *line; /* A line that fits this class. */
+ size_t length; /* The length of that line. */
};
/* Hash-table: array of buckets, each being a chain of equivalence classes. */
@@ -100,6 +61,10 @@ static int equivs_index;
/* Number of elements allocated in the array `equivs'. */
static int equivs_alloc;
+
+static void find_and_hash_each_line PARAMS((struct file_data *));
+static void find_identical_ends PARAMS((struct file_data[]));
+static void prepare_text_end PARAMS((struct file_data *));
/* Check for binary files and compare them for exact identity. */
@@ -121,38 +86,27 @@ sip (current, skip_test)
if (current->desc < 0)
{
/* Leave room for a sentinel. */
- current->buffer = xmalloc (sizeof (word));
current->bufsize = sizeof (word);
- current->buffered_chars = 0;
+ current->buffer = xmalloc (current->bufsize);
}
else
{
- current->bufsize = current->buffered_chars
- = STAT_BLOCKSIZE (current->stat);
-
- if (S_ISREG (current->stat.st_mode))
- /* Get the size out of the stat block.
- Allocate enough room for appended newline and sentinel.
- Allocate at least one block, to prevent overrunning the buffer
- when comparing growing binary files. */
- current->bufsize = max (current->bufsize,
- current->stat.st_size + sizeof (word) + 1);
-
+ current->bufsize = STAT_BLOCKSIZE (current->stat);
current->buffer = xmalloc (current->bufsize);
- if (skip_test)
- current->buffered_chars = 0;
- else
+
+ if (! skip_test)
{
/* Check first part of file to see if it's a binary file. */
current->buffered_chars = read (current->desc,
current->buffer,
- current->buffered_chars);
- if (current->buffered_chars < 0)
+ current->bufsize);
+ if (current->buffered_chars == -1)
pfatal_with_name (current->name);
return binary_file_p (current->buffer, current->buffered_chars);
}
}
+ current->buffered_chars = 0;
return 0;
}
@@ -162,7 +116,7 @@ void
slurp (current)
struct file_data *current;
{
- int cc;
+ size_t cc;
if (current->desc < 0)
/* The file is nonexistent. */
@@ -170,13 +124,22 @@ slurp (current)
else if (S_ISREG (current->stat.st_mode))
{
/* It's a regular file; slurp in the rest all at once. */
- cc = current->stat.st_size - current->buffered_chars;
- if (cc)
+
+ /* Get the size out of the stat block.
+ Allocate enough room for appended newline and sentinel. */
+ cc = current->stat.st_size + 1 + sizeof (word);
+ if (current->bufsize < cc)
+ {
+ current->bufsize = cc;
+ current->buffer = xrealloc (current->buffer, cc);
+ }
+
+ if (current->buffered_chars < current->stat.st_size)
{
cc = read (current->desc,
current->buffer + current->buffered_chars,
- cc);
- if (cc < 0)
+ current->stat.st_size - current->buffered_chars);
+ if (cc == -1)
pfatal_with_name (current->name);
current->buffered_chars += cc;
}
@@ -189,21 +152,20 @@ slurp (current)
if (current->buffered_chars == current->bufsize)
{
current->bufsize = current->bufsize * 2;
- current->buffer = (char *) xrealloc (current->buffer,
- current->bufsize);
+ current->buffer = xrealloc (current->buffer, current->bufsize);
}
cc = read (current->desc,
current->buffer + current->buffered_chars,
current->bufsize - current->buffered_chars);
if (cc == 0)
break;
- if (cc < 0)
+ if (cc == -1)
pfatal_with_name (current->name);
current->buffered_chars += cc;
}
/* Allocate just enough room for appended newline and sentinel. */
- current->bufsize = current->buffered_chars + sizeof (word) + 1;
- current->buffer = (char *) xrealloc (current->buffer, current->bufsize);
+ current->bufsize = current->buffered_chars + 1 + sizeof (word);
+ current->buffer = xrealloc (current->buffer, current->bufsize);
}
}
@@ -215,12 +177,13 @@ find_and_hash_each_line (current)
struct file_data *current;
{
unsigned h;
- const unsigned char *p = (const unsigned char *) current->prefix_end;
+ unsigned char const *p = (unsigned char const *) current->prefix_end;
unsigned char c;
- int i, length, *bucket;
+ int i, *bucket;
+ size_t length;
/* Cache often-used quantities in local variables to help the compiler. */
- const char **linbuf = current->linbuf;
+ char const **linbuf = current->linbuf;
int alloc_lines = current->alloc_lines;
int line = 0;
int linbuf_base = current->linbuf_base;
@@ -228,16 +191,16 @@ find_and_hash_each_line (current)
struct equivclass *eqs = equivs;
int eqs_index = equivs_index;
int eqs_alloc = equivs_alloc;
- const char *suffix_begin = current->suffix_begin;
- const char *bufend = current->buffer + current->buffered_chars;
- const char *incomplete_tail
+ char const *suffix_begin = current->suffix_begin;
+ char const *bufend = current->buffer + current->buffered_chars;
+ char const *incomplete_tail
= current->missing_newline && ROBUST_OUTPUT_STYLE (output_style)
- ? bufend : (const char *) 0;
+ ? bufend : (char const *) 0;
int varies = length_varies;
- while ((const char *) p < suffix_begin)
+ while ((char const *) p < suffix_begin)
{
- const char *ip = (const char *) p;
+ char const *ip = (char const *) p;
/* Compute the equivalence class for this line. */
@@ -249,18 +212,17 @@ find_and_hash_each_line (current)
if (ignore_all_space_flag)
while ((c = *p++) != '\n')
{
- if (! Is_space (c))
+ if (! isspace (c))
h = HASH (h, isupper (c) ? tolower (c) : c);
}
else if (ignore_space_change_flag)
while ((c = *p++) != '\n')
{
- if (c == ' ' || c == '\t')
+ if (isspace (c))
{
- while ((c = *p++) == ' ' || c == '\t')
- ;
- if (c == '\n')
- break;
+ while (isspace (c = *p++))
+ if (c == '\n')
+ goto hashing_done;
h = HASH (h, ' ');
}
/* C is now the first non-space. */
@@ -275,18 +237,17 @@ find_and_hash_each_line (current)
if (ignore_all_space_flag)
while ((c = *p++) != '\n')
{
- if (! Is_space (c))
+ if (! isspace (c))
h = HASH (h, c);
}
else if (ignore_space_change_flag)
while ((c = *p++) != '\n')
{
- if (c == ' ' || c == '\t')
+ if (isspace (c))
{
- while ((c = *p++) == ' ' || c == '\t')
- ;
- if (c == '\n')
- break;
+ while (isspace (c = *p++))
+ if (c == '\n')
+ goto hashing_done;
h = HASH (h, ' ');
}
/* C is now the first non-space. */
@@ -296,9 +257,10 @@ find_and_hash_each_line (current)
while ((c = *p++) != '\n')
h = HASH (h, c);
}
+ hashing_done:;
bucket = &buckets[h % nbuckets];
- length = (const char *) p - ip - ((const char *) p == incomplete_tail);
+ length = (char const *) p - ip - ((char const *) p == incomplete_tail);
for (i = *bucket; ; i = eqs[i].next)
if (!i)
{
@@ -326,7 +288,7 @@ find_and_hash_each_line (current)
/* Double (alloc_lines - linbuf_base) by adding to alloc_lines. */
alloc_lines = 2 * alloc_lines - linbuf_base;
cureqs = (int *) xrealloc (cureqs, alloc_lines * sizeof (*cureqs));
- linbuf = (const char **) xrealloc (linbuf + linbuf_base,
+ linbuf = (char const **) xrealloc (linbuf + linbuf_base,
(alloc_lines - linbuf_base)
* sizeof (*linbuf))
- linbuf_base;
@@ -347,16 +309,16 @@ find_and_hash_each_line (current)
{
/* Double (alloc_lines - linbuf_base) by adding to alloc_lines. */
alloc_lines = 2 * alloc_lines - linbuf_base;
- linbuf = (const char **) xrealloc (linbuf + linbuf_base,
+ linbuf = (char const **) xrealloc (linbuf + linbuf_base,
(alloc_lines - linbuf_base)
* sizeof (*linbuf))
- linbuf_base;
}
- linbuf[line] = (const char *) p;
+ linbuf[line] = (char const *) p;
- if ((const char *) p == bufend)
+ if ((char const *) p == bufend)
{
- linbuf[line] -= (const char *) p == incomplete_tail;
+ linbuf[line] -= (char const *) p == incomplete_tail;
break;
}
@@ -387,7 +349,7 @@ static void
prepare_text_end (current)
struct file_data *current;
{
- int buffered_chars = current->buffered_chars;
+ size_t buffered_chars = current->buffered_chars;
char *p = current->buffer;
if (buffered_chars == 0 || p[buffered_chars - 1] == '\n')
@@ -413,12 +375,12 @@ find_identical_ends (filevec)
{
word *w0, *w1;
char *p0, *p1, *buffer0, *buffer1;
- const char *end0, *beg0;
- const char **linbuf0, **linbuf1;
+ char const *end0, *beg0;
+ char const **linbuf0, **linbuf1;
int i, lines;
- int n0, n1, alloc_lines0, alloc_lines1;
+ size_t n0, n1, tem;
+ int alloc_lines0, alloc_lines1;
int buffered_prefix, prefix_count, prefix_mask;
- int tem;
slurp (&filevec[0]);
if (filevec[0].desc != filevec[1].desc)
@@ -564,7 +526,7 @@ find_identical_ends (filevec)
}
lines = 0;
- linbuf0 = (const char **) xmalloc (alloc_lines0 * sizeof (*linbuf0));
+ linbuf0 = (char const **) xmalloc (alloc_lines0 * sizeof (*linbuf0));
/* If the prefix is needed, find the prefix lines. */
if (! (no_diff_means_no_output
@@ -577,7 +539,7 @@ find_identical_ends (filevec)
{
int l = lines++ & prefix_mask;
if (l == alloc_lines0)
- linbuf0 = (const char **) xrealloc (linbuf0, (alloc_lines0 *= 2)
+ linbuf0 = (char const **) xrealloc (linbuf0, (alloc_lines0 *= 2)
* sizeof(*linbuf0));
linbuf0[l] = p0;
while (*p0++ != '\n')
@@ -593,7 +555,7 @@ find_identical_ends (filevec)
= (buffered_prefix
+ GUESS_LINES (lines, filevec[1].prefix_end - buffer1, tem)
+ context);
- linbuf1 = (const char **) xmalloc (alloc_lines1 * sizeof (*linbuf1));
+ linbuf1 = (char const **) xmalloc (alloc_lines1 * sizeof (*linbuf1));
if (buffered_prefix != lines)
{
@@ -621,7 +583,7 @@ find_identical_ends (filevec)
/* Largest primes less than some power of two, for nbuckets. Values range
from useful to preposterous. If one of these numbers isn't prime
after all, don't blame it on me, blame it on primes (6) . . . */
-static const int primes[] =
+static int const primes[] =
{
509,
1021,
@@ -630,6 +592,7 @@ static const int primes[] =
8191,
16381,
32749,
+#if 32767 < INT_MAX
65521,
131071,
262139,
@@ -646,20 +609,23 @@ static const int primes[] =
536870909,
1073741789,
2147483647,
+#endif
0
};
/* Given a vector of two file_data objects, read the file associated
with each one, and build the table of equivalence classes.
- Return 1 if either file appears to be a binary file. */
+ Return 1 if either file appears to be a binary file.
+ If PRETEND_BINARY is nonzero, pretend they are binary regardless. */
int
-read_files (filevec)
+read_files (filevec, pretend_binary)
struct file_data filevec[];
+ int pretend_binary;
{
int i;
- int skip_test = always_text_flag | no_details_flag;
- int appears_binary = no_details_flag | sip (&filevec[0], skip_test);
+ int skip_test = always_text_flag | pretend_binary;
+ int appears_binary = pretend_binary | sip (&filevec[0], skip_test);
if (filevec[0].desc != filevec[1].desc)
appears_binary |= sip (&filevec[1], skip_test | appears_binary);
diff --git a/gnu/usr.bin/diff/normal.c b/gnu/usr.bin/diff/normal.c
index a0cf479..4d9e23c 100644
--- a/gnu/usr.bin/diff/normal.c
+++ b/gnu/usr.bin/diff/normal.c
@@ -1,5 +1,5 @@
/* Normal-format output routines for GNU DIFF.
- Copyright (C) 1988, 1989 Free Software Foundation, Inc.
+ Copyright (C) 1988, 1989, 1993 Free Software Foundation, Inc.
This file is part of GNU DIFF.
@@ -20,10 +20,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "diff.h"
-int change_letter ();
-void print_normal_hunk ();
-void print_number_range ();
-struct change *find_change ();
+static void print_normal_hunk PARAMS((struct change *));
/* Print the edit-script SCRIPT as a normal diff.
INF points to an array of descriptions of the two files. */
@@ -39,7 +36,7 @@ print_normal_script (script)
This is a contiguous portion of a complete edit script,
describing changes in consecutive lines. */
-void
+static void
print_normal_hunk (hunk)
struct change *hunk;
{
diff --git a/gnu/usr.bin/diff/regex.c b/gnu/usr.bin/diff/regex.c
index e8b5882..81b06ff 100644
--- a/gnu/usr.bin/diff/regex.c
+++ b/gnu/usr.bin/diff/regex.c
@@ -26,6 +26,17 @@
#define _GNU_SOURCE
+#ifdef HAVE_CONFIG_H
+#if defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
/* We need this for `regex.h', and perhaps for the Emacs include files. */
#include <sys/types.h>
@@ -46,6 +57,14 @@
#else /* not emacs */
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+char *malloc ();
+char *realloc ();
+#endif
+
+
/* We used to test for `BSTRING' here, but only GCC and Emacs define
`BSTRING', as far as I know, and neither of them use this code. */
#if HAVE_STRING_H || STDC_HEADERS
@@ -63,14 +82,6 @@
#include <strings.h>
#endif
-#ifdef STDC_HEADERS
-#include <stdlib.h>
-#else
-char *malloc ();
-char *realloc ();
-#endif
-
-
/* Define the syntax stuff for \<, \>, etc. */
/* This must be nonzero for the wordchar and notwordchar pattern
@@ -235,6 +246,8 @@ char *alloca ();
/* (Re)Allocate N items of type T using malloc, or fail. */
#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t)))
#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t)))
+#define RETALLOC_IF(addr, n, t) \
+ if (addr) RETALLOC((addr), (n), t); else (addr) = TALLOC ((n), t)
#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t)))
#define BYTEWIDTH 8 /* In bits. */
@@ -871,6 +884,387 @@ static const char *re_error_msg[] =
"Unmatched ) or \\)", /* REG_ERPAREN */
};
+/* Avoiding alloca during matching, to placate r_alloc. */
+
+/* Define MATCH_MAY_ALLOCATE if we need to make sure that the
+ searching and matching functions should not call alloca. On some
+ systems, alloca is implemented in terms of malloc, and if we're
+ using the relocating allocator routines, then malloc could cause a
+ relocation, which might (if the strings being searched are in the
+ ralloc heap) shift the data out from underneath the regexp
+ routines.
+
+ Here's another reason to avoid allocation: Emacs insists on
+ processing input from X in a signal handler; processing X input may
+ call malloc; if input arrives while a matching routine is calling
+ malloc, then we're scrod. But Emacs can't just block input while
+ calling matching routines; then we don't notice interrupts when
+ they come in. So, Emacs blocks input around all regexp calls
+ except the matching calls, which it leaves unprotected, in the
+ faith that they will not malloc. */
+
+/* Normally, this is fine. */
+#define MATCH_MAY_ALLOCATE
+
+/* But under some circumstances, it's not. */
+#if defined (emacs) || (defined (REL_ALLOC) && defined (C_ALLOCA))
+#undef MATCH_MAY_ALLOCATE
+#endif
+
+
+/* Failure stack declarations and macros; both re_compile_fastmap and
+ re_match_2 use a failure stack. These have to be macros because of
+ REGEX_ALLOCATE. */
+
+
+/* Number of failure points for which to initially allocate space
+ when matching. If this number is exceeded, we allocate more
+ space, so it is not a hard limit. */
+#ifndef INIT_FAILURE_ALLOC
+#define INIT_FAILURE_ALLOC 5
+#endif
+
+/* Roughly the maximum number of failure points on the stack. Would be
+ exactly that if always used MAX_FAILURE_SPACE each time we failed.
+ This is a variable only so users of regex can assign to it; we never
+ change it ourselves. */
+int re_max_failures = 2000;
+
+typedef unsigned char *fail_stack_elt_t;
+
+typedef struct
+{
+ fail_stack_elt_t *stack;
+ unsigned size;
+ unsigned avail; /* Offset of next open position. */
+} fail_stack_type;
+
+#define FAIL_STACK_EMPTY() (fail_stack.avail == 0)
+#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0)
+#define FAIL_STACK_FULL() (fail_stack.avail == fail_stack.size)
+#define FAIL_STACK_TOP() (fail_stack.stack[fail_stack.avail])
+
+
+/* Initialize `fail_stack'. Do `return -2' if the alloc fails. */
+
+#ifdef MATCH_MAY_ALLOCATE
+#define INIT_FAIL_STACK() \
+ do { \
+ fail_stack.stack = (fail_stack_elt_t *) \
+ REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \
+ \
+ if (fail_stack.stack == NULL) \
+ return -2; \
+ \
+ fail_stack.size = INIT_FAILURE_ALLOC; \
+ fail_stack.avail = 0; \
+ } while (0)
+#else
+#define INIT_FAIL_STACK() \
+ do { \
+ fail_stack.avail = 0; \
+ } while (0)
+#endif
+
+
+/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items.
+
+ Return 1 if succeeds, and 0 if either ran out of memory
+ allocating space for it or it was already too large.
+
+ REGEX_REALLOCATE requires `destination' be declared. */
+
+#define DOUBLE_FAIL_STACK(fail_stack) \
+ ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS \
+ ? 0 \
+ : ((fail_stack).stack = (fail_stack_elt_t *) \
+ REGEX_REALLOCATE ((fail_stack).stack, \
+ (fail_stack).size * sizeof (fail_stack_elt_t), \
+ ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)), \
+ \
+ (fail_stack).stack == NULL \
+ ? 0 \
+ : ((fail_stack).size <<= 1, \
+ 1)))
+
+
+/* Push PATTERN_OP on FAIL_STACK.
+
+ Return 1 if was able to do so and 0 if ran out of memory allocating
+ space to do so. */
+#define PUSH_PATTERN_OP(pattern_op, fail_stack) \
+ ((FAIL_STACK_FULL () \
+ && !DOUBLE_FAIL_STACK (fail_stack)) \
+ ? 0 \
+ : ((fail_stack).stack[(fail_stack).avail++] = pattern_op, \
+ 1))
+
+/* This pushes an item onto the failure stack. Must be a four-byte
+ value. Assumes the variable `fail_stack'. Probably should only
+ be called from within `PUSH_FAILURE_POINT'. */
+#define PUSH_FAILURE_ITEM(item) \
+ fail_stack.stack[fail_stack.avail++] = (fail_stack_elt_t) item
+
+/* The complement operation. Assumes `fail_stack' is nonempty. */
+#define POP_FAILURE_ITEM() fail_stack.stack[--fail_stack.avail]
+
+/* Used to omit pushing failure point id's when we're not debugging. */
+#ifdef DEBUG
+#define DEBUG_PUSH PUSH_FAILURE_ITEM
+#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_ITEM ()
+#else
+#define DEBUG_PUSH(item)
+#define DEBUG_POP(item_addr)
+#endif
+
+
+/* Push the information about the state we will need
+ if we ever fail back to it.
+
+ Requires variables fail_stack, regstart, regend, reg_info, and
+ num_regs be declared. DOUBLE_FAIL_STACK requires `destination' be
+ declared.
+
+ Does `return FAILURE_CODE' if runs out of memory. */
+
+#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \
+ do { \
+ char *destination; \
+ /* Must be int, so when we don't save any registers, the arithmetic \
+ of 0 + -1 isn't done as unsigned. */ \
+ int this_reg; \
+ \
+ DEBUG_STATEMENT (failure_id++); \
+ DEBUG_STATEMENT (nfailure_points_pushed++); \
+ DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \
+ DEBUG_PRINT2 (" Before push, next avail: %d\n", (fail_stack).avail);\
+ DEBUG_PRINT2 (" size: %d\n", (fail_stack).size);\
+ \
+ DEBUG_PRINT2 (" slots needed: %d\n", NUM_FAILURE_ITEMS); \
+ DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \
+ \
+ /* Ensure we have enough space allocated for what we will push. */ \
+ while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \
+ { \
+ if (!DOUBLE_FAIL_STACK (fail_stack)) \
+ return failure_code; \
+ \
+ DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \
+ (fail_stack).size); \
+ DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\
+ } \
+ \
+ /* Push the info, starting with the registers. */ \
+ DEBUG_PRINT1 ("\n"); \
+ \
+ for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \
+ this_reg++) \
+ { \
+ DEBUG_PRINT2 (" Pushing reg: %d\n", this_reg); \
+ DEBUG_STATEMENT (num_regs_pushed++); \
+ \
+ DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \
+ PUSH_FAILURE_ITEM (regstart[this_reg]); \
+ \
+ DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \
+ PUSH_FAILURE_ITEM (regend[this_reg]); \
+ \
+ DEBUG_PRINT2 (" info: 0x%x\n ", reg_info[this_reg]); \
+ DEBUG_PRINT2 (" match_null=%d", \
+ REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" matched_something=%d", \
+ MATCHED_SOMETHING (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" ever_matched=%d", \
+ EVER_MATCHED_SOMETHING (reg_info[this_reg])); \
+ DEBUG_PRINT1 ("\n"); \
+ PUSH_FAILURE_ITEM (reg_info[this_reg].word); \
+ } \
+ \
+ DEBUG_PRINT2 (" Pushing low active reg: %d\n", lowest_active_reg);\
+ PUSH_FAILURE_ITEM (lowest_active_reg); \
+ \
+ DEBUG_PRINT2 (" Pushing high active reg: %d\n", highest_active_reg);\
+ PUSH_FAILURE_ITEM (highest_active_reg); \
+ \
+ DEBUG_PRINT2 (" Pushing pattern 0x%x: ", pattern_place); \
+ DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \
+ PUSH_FAILURE_ITEM (pattern_place); \
+ \
+ DEBUG_PRINT2 (" Pushing string 0x%x: `", string_place); \
+ DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \
+ size2); \
+ DEBUG_PRINT1 ("'\n"); \
+ PUSH_FAILURE_ITEM (string_place); \
+ \
+ DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \
+ DEBUG_PUSH (failure_id); \
+ } while (0)
+
+/* This is the number of items that are pushed and popped on the stack
+ for each register. */
+#define NUM_REG_ITEMS 3
+
+/* Individual items aside from the registers. */
+#ifdef DEBUG
+#define NUM_NONREG_ITEMS 5 /* Includes failure point id. */
+#else
+#define NUM_NONREG_ITEMS 4
+#endif
+
+/* We push at most this many items on the stack. */
+#define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS)
+
+/* We actually push this many items. */
+#define NUM_FAILURE_ITEMS \
+ ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS \
+ + NUM_NONREG_ITEMS)
+
+/* How many items can still be added to the stack without overflowing it. */
+#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail)
+
+
+/* Pops what PUSH_FAIL_STACK pushes.
+
+ We restore into the parameters, all of which should be lvalues:
+ STR -- the saved data position.
+ PAT -- the saved pattern position.
+ LOW_REG, HIGH_REG -- the highest and lowest active registers.
+ REGSTART, REGEND -- arrays of string positions.
+ REG_INFO -- array of information about each subexpression.
+
+ Also assumes the variables `fail_stack' and (if debugging), `bufp',
+ `pend', `string1', `size1', `string2', and `size2'. */
+
+#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\
+{ \
+ DEBUG_STATEMENT (fail_stack_elt_t failure_id;) \
+ int this_reg; \
+ const unsigned char *string_temp; \
+ \
+ assert (!FAIL_STACK_EMPTY ()); \
+ \
+ /* Remove failure points and point to how many regs pushed. */ \
+ DEBUG_PRINT1 ("POP_FAILURE_POINT:\n"); \
+ DEBUG_PRINT2 (" Before pop, next avail: %d\n", fail_stack.avail); \
+ DEBUG_PRINT2 (" size: %d\n", fail_stack.size); \
+ \
+ assert (fail_stack.avail >= NUM_NONREG_ITEMS); \
+ \
+ DEBUG_POP (&failure_id); \
+ DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id); \
+ \
+ /* If the saved string location is NULL, it came from an \
+ on_failure_keep_string_jump opcode, and we want to throw away the \
+ saved NULL, thus retaining our current position in the string. */ \
+ string_temp = POP_FAILURE_ITEM (); \
+ if (string_temp != NULL) \
+ str = (const char *) string_temp; \
+ \
+ DEBUG_PRINT2 (" Popping string 0x%x: `", str); \
+ DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \
+ DEBUG_PRINT1 ("'\n"); \
+ \
+ pat = (unsigned char *) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" Popping pattern 0x%x: ", pat); \
+ DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \
+ \
+ /* Restore register info. */ \
+ high_reg = (unsigned) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" Popping high active reg: %d\n", high_reg); \
+ \
+ low_reg = (unsigned) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" Popping low active reg: %d\n", low_reg); \
+ \
+ for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \
+ { \
+ DEBUG_PRINT2 (" Popping reg: %d\n", this_reg); \
+ \
+ reg_info[this_reg].word = POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" info: 0x%x\n", reg_info[this_reg]); \
+ \
+ regend[this_reg] = (const char *) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \
+ \
+ regstart[this_reg] = (const char *) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \
+ } \
+ \
+ DEBUG_STATEMENT (nfailure_points_popped++); \
+} /* POP_FAILURE_POINT */
+
+
+
+/* Structure for per-register (a.k.a. per-group) information.
+ This must not be longer than one word, because we push this value
+ onto the failure stack. Other register information, such as the
+ starting and ending positions (which are addresses), and the list of
+ inner groups (which is a bits list) are maintained in separate
+ variables.
+
+ We are making a (strictly speaking) nonportable assumption here: that
+ the compiler will pack our bit fields into something that fits into
+ the type of `word', i.e., is something that fits into one item on the
+ failure stack. */
+typedef union
+{
+ fail_stack_elt_t word;
+ struct
+ {
+ /* This field is one if this group can match the empty string,
+ zero if not. If not yet determined, `MATCH_NULL_UNSET_VALUE'. */
+#define MATCH_NULL_UNSET_VALUE 3
+ unsigned match_null_string_p : 2;
+ unsigned is_active : 1;
+ unsigned matched_something : 1;
+ unsigned ever_matched_something : 1;
+ } bits;
+} register_info_type;
+
+#define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p)
+#define IS_ACTIVE(R) ((R).bits.is_active)
+#define MATCHED_SOMETHING(R) ((R).bits.matched_something)
+#define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something)
+
+
+/* Call this when have matched a real character; it sets `matched' flags
+ for the subexpressions which we are currently inside. Also records
+ that those subexprs have matched. */
+#define SET_REGS_MATCHED() \
+ do \
+ { \
+ unsigned r; \
+ for (r = lowest_active_reg; r <= highest_active_reg; r++) \
+ { \
+ MATCHED_SOMETHING (reg_info[r]) \
+ = EVER_MATCHED_SOMETHING (reg_info[r]) \
+ = 1; \
+ } \
+ } \
+ while (0)
+
+
+/* Registers are set to a sentinel when they haven't yet matched. */
+#define REG_UNSET_VALUE ((char *) -1)
+#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
+
+
+
+/* How do we implement a missing MATCH_MAY_ALLOCATE?
+ We make the fail stack a global thing, and then grow it to
+ re_max_failures when we compile. */
+#ifndef MATCH_MAY_ALLOCATE
+static fail_stack_type fail_stack;
+
+static const char ** regstart, ** regend;
+static const char ** old_regstart, ** old_regend;
+static const char **best_regstart, **best_regend;
+static register_info_type *reg_info;
+static const char **reg_dummy;
+static register_info_type *reg_info_dummy;
+#endif
+
+
/* Subroutine declarations and macros for regex_compile. */
static void store_op1 (), store_op2 ();
@@ -2086,6 +2480,40 @@ regex_compile (pattern, size, syntax, bufp)
}
#endif /* DEBUG */
+#ifndef MATCH_MAY_ALLOCATE
+ /* Initialize the failure stack to the largest possible stack. This
+ isn't necessary unless we're trying to avoid calling alloca in
+ the search and match routines. */
+ {
+ int num_regs = bufp->re_nsub + 1;
+
+ /* Since DOUBLE_FAIL_STACK refuses to double only if the current size
+ is strictly greater than re_max_failures, the largest possible stack
+ is 2 * re_max_failures failure points. */
+ fail_stack.size = (2 * re_max_failures * MAX_FAILURE_ITEMS);
+ if (fail_stack.stack)
+ fail_stack.stack =
+ (fail_stack_elt_t *) realloc (fail_stack.stack,
+ (fail_stack.size
+ * sizeof (fail_stack_elt_t)));
+ else
+ fail_stack.stack =
+ (fail_stack_elt_t *) malloc (fail_stack.size
+ * sizeof (fail_stack_elt_t));
+
+ /* Initialize some other variables the matcher uses. */
+ RETALLOC_IF (regstart, num_regs, const char *);
+ RETALLOC_IF (regend, num_regs, const char *);
+ RETALLOC_IF (old_regstart, num_regs, const char *);
+ RETALLOC_IF (old_regend, num_regs, const char *);
+ RETALLOC_IF (best_regstart, num_regs, const char *);
+ RETALLOC_IF (best_regend, num_regs, const char *);
+ RETALLOC_IF (reg_info, num_regs, register_info_type);
+ RETALLOC_IF (reg_dummy, num_regs, const char *);
+ RETALLOC_IF (reg_info_dummy, num_regs, register_info_type);
+ }
+#endif
+
return REG_NOERROR;
} /* regex_compile */
@@ -2275,280 +2703,6 @@ compile_range (p_ptr, pend, translate, syntax, b)
return REG_NOERROR;
}
-/* Failure stack declarations and macros; both re_compile_fastmap and
- re_match_2 use a failure stack. These have to be macros because of
- REGEX_ALLOCATE. */
-
-
-/* Number of failure points for which to initially allocate space
- when matching. If this number is exceeded, we allocate more
- space, so it is not a hard limit. */
-#ifndef INIT_FAILURE_ALLOC
-#define INIT_FAILURE_ALLOC 5
-#endif
-
-/* Roughly the maximum number of failure points on the stack. Would be
- exactly that if always used MAX_FAILURE_SPACE each time we failed.
- This is a variable only so users of regex can assign to it; we never
- change it ourselves. */
-int re_max_failures = 2000;
-
-typedef const unsigned char *fail_stack_elt_t;
-
-typedef struct
-{
- fail_stack_elt_t *stack;
- unsigned size;
- unsigned avail; /* Offset of next open position. */
-} fail_stack_type;
-
-#define FAIL_STACK_EMPTY() (fail_stack.avail == 0)
-#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0)
-#define FAIL_STACK_FULL() (fail_stack.avail == fail_stack.size)
-#define FAIL_STACK_TOP() (fail_stack.stack[fail_stack.avail])
-
-
-/* Initialize `fail_stack'. Do `return -2' if the alloc fails. */
-
-#define INIT_FAIL_STACK() \
- do { \
- fail_stack.stack = (fail_stack_elt_t *) \
- REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \
- \
- if (fail_stack.stack == NULL) \
- return -2; \
- \
- fail_stack.size = INIT_FAILURE_ALLOC; \
- fail_stack.avail = 0; \
- } while (0)
-
-
-/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items.
-
- Return 1 if succeeds, and 0 if either ran out of memory
- allocating space for it or it was already too large.
-
- REGEX_REALLOCATE requires `destination' be declared. */
-
-#define DOUBLE_FAIL_STACK(fail_stack) \
- ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS \
- ? 0 \
- : ((fail_stack).stack = (fail_stack_elt_t *) \
- REGEX_REALLOCATE ((fail_stack).stack, \
- (fail_stack).size * sizeof (fail_stack_elt_t), \
- ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)), \
- \
- (fail_stack).stack == NULL \
- ? 0 \
- : ((fail_stack).size <<= 1, \
- 1)))
-
-
-/* Push PATTERN_OP on FAIL_STACK.
-
- Return 1 if was able to do so and 0 if ran out of memory allocating
- space to do so. */
-#define PUSH_PATTERN_OP(pattern_op, fail_stack) \
- ((FAIL_STACK_FULL () \
- && !DOUBLE_FAIL_STACK (fail_stack)) \
- ? 0 \
- : ((fail_stack).stack[(fail_stack).avail++] = pattern_op, \
- 1))
-
-/* This pushes an item onto the failure stack. Must be a four-byte
- value. Assumes the variable `fail_stack'. Probably should only
- be called from within `PUSH_FAILURE_POINT'. */
-#define PUSH_FAILURE_ITEM(item) \
- fail_stack.stack[fail_stack.avail++] = (fail_stack_elt_t) item
-
-/* The complement operation. Assumes `fail_stack' is nonempty. */
-#define POP_FAILURE_ITEM() fail_stack.stack[--fail_stack.avail]
-
-/* Used to omit pushing failure point id's when we're not debugging. */
-#ifdef DEBUG
-#define DEBUG_PUSH PUSH_FAILURE_ITEM
-#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_ITEM ()
-#else
-#define DEBUG_PUSH(item)
-#define DEBUG_POP(item_addr)
-#endif
-
-
-/* Push the information about the state we will need
- if we ever fail back to it.
-
- Requires variables fail_stack, regstart, regend, reg_info, and
- num_regs be declared. DOUBLE_FAIL_STACK requires `destination' be
- declared.
-
- Does `return FAILURE_CODE' if runs out of memory. */
-
-#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \
- do { \
- char *destination; \
- /* Must be int, so when we don't save any registers, the arithmetic \
- of 0 + -1 isn't done as unsigned. */ \
- int this_reg; \
- \
- DEBUG_STATEMENT (failure_id++); \
- DEBUG_STATEMENT (nfailure_points_pushed++); \
- DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \
- DEBUG_PRINT2 (" Before push, next avail: %d\n", (fail_stack).avail);\
- DEBUG_PRINT2 (" size: %d\n", (fail_stack).size);\
- \
- DEBUG_PRINT2 (" slots needed: %d\n", NUM_FAILURE_ITEMS); \
- DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \
- \
- /* Ensure we have enough space allocated for what we will push. */ \
- while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \
- { \
- if (!DOUBLE_FAIL_STACK (fail_stack)) \
- return failure_code; \
- \
- DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \
- (fail_stack).size); \
- DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\
- } \
- \
- /* Push the info, starting with the registers. */ \
- DEBUG_PRINT1 ("\n"); \
- \
- for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \
- this_reg++) \
- { \
- DEBUG_PRINT2 (" Pushing reg: %d\n", this_reg); \
- DEBUG_STATEMENT (num_regs_pushed++); \
- \
- DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \
- PUSH_FAILURE_ITEM (regstart[this_reg]); \
- \
- DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \
- PUSH_FAILURE_ITEM (regend[this_reg]); \
- \
- DEBUG_PRINT2 (" info: 0x%x\n ", reg_info[this_reg]); \
- DEBUG_PRINT2 (" match_null=%d", \
- REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \
- DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \
- DEBUG_PRINT2 (" matched_something=%d", \
- MATCHED_SOMETHING (reg_info[this_reg])); \
- DEBUG_PRINT2 (" ever_matched=%d", \
- EVER_MATCHED_SOMETHING (reg_info[this_reg])); \
- DEBUG_PRINT1 ("\n"); \
- PUSH_FAILURE_ITEM (reg_info[this_reg].word); \
- } \
- \
- DEBUG_PRINT2 (" Pushing low active reg: %d\n", lowest_active_reg);\
- PUSH_FAILURE_ITEM (lowest_active_reg); \
- \
- DEBUG_PRINT2 (" Pushing high active reg: %d\n", highest_active_reg);\
- PUSH_FAILURE_ITEM (highest_active_reg); \
- \
- DEBUG_PRINT2 (" Pushing pattern 0x%x: ", pattern_place); \
- DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \
- PUSH_FAILURE_ITEM (pattern_place); \
- \
- DEBUG_PRINT2 (" Pushing string 0x%x: `", string_place); \
- DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \
- size2); \
- DEBUG_PRINT1 ("'\n"); \
- PUSH_FAILURE_ITEM (string_place); \
- \
- DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \
- DEBUG_PUSH (failure_id); \
- } while (0)
-
-/* This is the number of items that are pushed and popped on the stack
- for each register. */
-#define NUM_REG_ITEMS 3
-
-/* Individual items aside from the registers. */
-#ifdef DEBUG
-#define NUM_NONREG_ITEMS 5 /* Includes failure point id. */
-#else
-#define NUM_NONREG_ITEMS 4
-#endif
-
-/* We push at most this many items on the stack. */
-#define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS)
-
-/* We actually push this many items. */
-#define NUM_FAILURE_ITEMS \
- ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS \
- + NUM_NONREG_ITEMS)
-
-/* How many items can still be added to the stack without overflowing it. */
-#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail)
-
-
-/* Pops what PUSH_FAIL_STACK pushes.
-
- We restore into the parameters, all of which should be lvalues:
- STR -- the saved data position.
- PAT -- the saved pattern position.
- LOW_REG, HIGH_REG -- the highest and lowest active registers.
- REGSTART, REGEND -- arrays of string positions.
- REG_INFO -- array of information about each subexpression.
-
- Also assumes the variables `fail_stack' and (if debugging), `bufp',
- `pend', `string1', `size1', `string2', and `size2'. */
-
-#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\
-{ \
- DEBUG_STATEMENT (fail_stack_elt_t failure_id;) \
- int this_reg; \
- const unsigned char *string_temp; \
- \
- assert (!FAIL_STACK_EMPTY ()); \
- \
- /* Remove failure points and point to how many regs pushed. */ \
- DEBUG_PRINT1 ("POP_FAILURE_POINT:\n"); \
- DEBUG_PRINT2 (" Before pop, next avail: %d\n", fail_stack.avail); \
- DEBUG_PRINT2 (" size: %d\n", fail_stack.size); \
- \
- assert (fail_stack.avail >= NUM_NONREG_ITEMS); \
- \
- DEBUG_POP (&failure_id); \
- DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id); \
- \
- /* If the saved string location is NULL, it came from an \
- on_failure_keep_string_jump opcode, and we want to throw away the \
- saved NULL, thus retaining our current position in the string. */ \
- string_temp = POP_FAILURE_ITEM (); \
- if (string_temp != NULL) \
- str = (const char *) string_temp; \
- \
- DEBUG_PRINT2 (" Popping string 0x%x: `", str); \
- DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \
- DEBUG_PRINT1 ("'\n"); \
- \
- pat = (unsigned char *) POP_FAILURE_ITEM (); \
- DEBUG_PRINT2 (" Popping pattern 0x%x: ", pat); \
- DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \
- \
- /* Restore register info. */ \
- high_reg = (unsigned) POP_FAILURE_ITEM (); \
- DEBUG_PRINT2 (" Popping high active reg: %d\n", high_reg); \
- \
- low_reg = (unsigned) POP_FAILURE_ITEM (); \
- DEBUG_PRINT2 (" Popping low active reg: %d\n", low_reg); \
- \
- for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \
- { \
- DEBUG_PRINT2 (" Popping reg: %d\n", this_reg); \
- \
- reg_info[this_reg].word = POP_FAILURE_ITEM (); \
- DEBUG_PRINT2 (" info: 0x%x\n", reg_info[this_reg]); \
- \
- regend[this_reg] = (const char *) POP_FAILURE_ITEM (); \
- DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \
- \
- regstart[this_reg] = (const char *) POP_FAILURE_ITEM (); \
- DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \
- } \
- \
- DEBUG_STATEMENT (nfailure_points_popped++); \
-} /* POP_FAILURE_POINT */
-
/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in
BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible
characters can start a string that matches the pattern. This fastmap
@@ -2567,7 +2721,9 @@ re_compile_fastmap (bufp)
struct re_pattern_buffer *bufp;
{
int j, k;
+#ifdef MATCH_MAY_ALLOCATE
fail_stack_type fail_stack;
+#endif
#ifndef REGEX_MALLOC
char *destination;
#endif
@@ -2577,7 +2733,7 @@ re_compile_fastmap (bufp)
register char *fastmap = bufp->fastmap;
unsigned char *pattern = bufp->buffer;
unsigned long size = bufp->used;
- const unsigned char *p = pattern;
+ unsigned char *p = pattern;
register unsigned char *pend = pattern + size;
/* Assume that each path through the pattern can be null until
@@ -3030,64 +3186,12 @@ static boolean alt_match_null_string_p (),
common_op_match_null_string_p (),
group_match_null_string_p ();
-/* Structure for per-register (a.k.a. per-group) information.
- This must not be longer than one word, because we push this value
- onto the failure stack. Other register information, such as the
- starting and ending positions (which are addresses), and the list of
- inner groups (which is a bits list) are maintained in separate
- variables.
-
- We are making a (strictly speaking) nonportable assumption here: that
- the compiler will pack our bit fields into something that fits into
- the type of `word', i.e., is something that fits into one item on the
- failure stack. */
-typedef union
-{
- fail_stack_elt_t word;
- struct
- {
- /* This field is one if this group can match the empty string,
- zero if not. If not yet determined, `MATCH_NULL_UNSET_VALUE'. */
-#define MATCH_NULL_UNSET_VALUE 3
- unsigned match_null_string_p : 2;
- unsigned is_active : 1;
- unsigned matched_something : 1;
- unsigned ever_matched_something : 1;
- } bits;
-} register_info_type;
-
-#define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p)
-#define IS_ACTIVE(R) ((R).bits.is_active)
-#define MATCHED_SOMETHING(R) ((R).bits.matched_something)
-#define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something)
-
-
-/* Call this when have matched a real character; it sets `matched' flags
- for the subexpressions which we are currently inside. Also records
- that those subexprs have matched. */
-#define SET_REGS_MATCHED() \
- do \
- { \
- unsigned r; \
- for (r = lowest_active_reg; r <= highest_active_reg; r++) \
- { \
- MATCHED_SOMETHING (reg_info[r]) \
- = EVER_MATCHED_SOMETHING (reg_info[r]) \
- = 1; \
- } \
- } \
- while (0)
-
-
/* This converts PTR, a pointer into one of the search strings `string1'
and `string2' into an offset from the beginning of that string. */
-#define POINTER_TO_OFFSET(ptr) \
- (FIRST_STRING_P (ptr) ? (ptr) - string1 : (ptr) - string2 + size1)
-
-/* Registers are set to a sentinel when they haven't yet matched. */
-#define REG_UNSET_VALUE ((char *) -1)
-#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
-
+#define POINTER_TO_OFFSET(ptr) \
+ (FIRST_STRING_P (ptr) \
+ ? ((regoff_t) ((ptr) - string1)) \
+ : ((regoff_t) ((ptr) - string2 + size1)))
/* Macros for dealing with the split strings in re_match_2. */
@@ -3130,6 +3234,7 @@ typedef union
/* Free everything we malloc. */
+#ifdef MATCH_MAY_ALLOCATE
#ifdef REGEX_MALLOC
#define FREE_VAR(var) if (var) free (var); var = NULL
#define FREE_VARIABLES() \
@@ -3149,7 +3254,9 @@ typedef union
/* Some MIPS systems (at least) want this to free alloca'd storage. */
#define FREE_VARIABLES() alloca (0)
#endif /* not REGEX_MALLOC */
-
+#else
+#define FREE_VARIABLES() /* Do nothing! */
+#endif /* not MATCH_MAY_ALLOCATE */
/* These values must meet several constraints. They must not be valid
register values; since we have a limit of 255 registers (because
@@ -3230,7 +3337,9 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
scanning the strings. If the latter is zero, the failure point is
a ``dummy''; if a failure happens and the failure point is a dummy,
it gets discarded and the next next one is tried. */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global. */
fail_stack_type fail_stack;
+#endif
#ifdef DEBUG
static unsigned failure_id = 0;
unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0;
@@ -3252,14 +3361,18 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
matching and the regnum-th regend points to right after where we
stopped matching the regnum-th subexpression. (The zeroth register
keeps track of what the whole pattern matches.) */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */
const char **regstart, **regend;
+#endif
/* If a group that's operated upon by a repetition operator fails to
match anything, then the register for its start will need to be
restored because it will have been set to wherever in the string we
are when we last see its open-group operator. Similarly for a
register's end. */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */
const char **old_regstart, **old_regend;
+#endif
/* The is_active field of reg_info helps us keep track of which (possibly
nested) subexpressions we are currently in. The matched_something
@@ -3267,14 +3380,18 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
matched any of the pattern so far this time through the reg_num-th
subexpression. These two fields get reset each time through any
loop their register is in. */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global. */
register_info_type *reg_info;
+#endif
/* The following record the register info as found in the above
variables when we find a match better than any we've seen before.
This happens as we backtrack through the failure points, which in
turn happens only if we have not yet matched the entire string. */
unsigned best_regs_set = false;
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */
const char **best_regstart, **best_regend;
+#endif
/* Logically, this is `best_regend[0]'. But we don't want to have to
allocate space for that if we're not allocating space for anything
@@ -3287,8 +3404,10 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
const char *match_end = NULL;
/* Used when we pop values we don't care about. */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */
const char **reg_dummy;
register_info_type *reg_info_dummy;
+#endif
#ifdef DEBUG
/* Counts the total number of registers pushed. */
@@ -3299,6 +3418,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
INIT_FAIL_STACK ();
+#ifdef MATCH_MAY_ALLOCATE
/* Do not bother to initialize all the register variables if there are
no groups in the pattern, as it takes a fair amount of time. If
there are groups, we include space for register 0 (the whole
@@ -3323,7 +3443,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
return -2;
}
}
-#ifdef REGEX_MALLOC
+#if defined (REGEX_MALLOC)
else
{
/* We must initialize all our variables to NULL, so that
@@ -3333,6 +3453,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
reg_info = reg_info_dummy = (register_info_type *) NULL;
}
#endif /* REGEX_MALLOC */
+#endif /* MATCH_MAY_ALLOCATE */
/* The starting position is bogus. */
if (pos < 0 || pos > size1 + size2)
@@ -3509,8 +3630,9 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
if (regs->num_regs > 0)
{
regs->start[0] = pos;
- regs->end[0] = (MATCHING_IN_FIRST_STRING ? d - string1
- : d - string2 + size1);
+ regs->end[0] = (MATCHING_IN_FIRST_STRING
+ ? ((regoff_t) (d - string1))
+ : ((regoff_t) (d - string2 + size1)));
}
/* Go through the first `min (num_regs, regs->num_regs)'
@@ -3521,8 +3643,10 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
regs->start[mcnt] = regs->end[mcnt] = -1;
else
{
- regs->start[mcnt] = POINTER_TO_OFFSET (regstart[mcnt]);
- regs->end[mcnt] = POINTER_TO_OFFSET (regend[mcnt]);
+ regs->start[mcnt]
+ = (regoff_t) POINTER_TO_OFFSET (regstart[mcnt]);
+ regs->end[mcnt]
+ = (regoff_t) POINTER_TO_OFFSET (regend[mcnt]);
}
}
@@ -4031,11 +4155,27 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
detect that here, the alternative has put on a dummy
failure point which is what we will end up popping. */
- /* Skip over open/close-group commands. */
- while (p2 + 2 < pend
- && ((re_opcode_t) *p2 == stop_memory
- || (re_opcode_t) *p2 == start_memory))
- p2 += 3; /* Skip over args, too. */
+ /* Skip over open/close-group commands.
+ If what follows this loop is a ...+ construct,
+ look at what begins its body, since we will have to
+ match at least one of that. */
+ while (1)
+ {
+ if (p2 + 2 < pend
+ && ((re_opcode_t) *p2 == stop_memory
+ || (re_opcode_t) *p2 == start_memory))
+ p2 += 3;
+ else if (p2 + 6 < pend
+ && (re_opcode_t) *p2 == dummy_failure_jump)
+ p2 += 6;
+ else
+ break;
+ }
+
+ p1 = p + mcnt;
+ /* p1[0] ... p1[2] are the `on_failure_jump' corresponding
+ to the `maybe_finalize_jump' of this case. Examine what
+ follows. */
/* If we're at the end of the pattern, we can change. */
if (p2 == pend)
@@ -4053,11 +4193,7 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
{
register unsigned char c
= *p2 == (unsigned char) endline ? '\n' : p2[2];
- p1 = p + mcnt;
- /* p1[0] ... p1[2] are the `on_failure_jump' corresponding
- to the `maybe_finalize_jump' of this case. Examine what
- follows. */
if ((re_opcode_t) p1[3] == exactn && p1[5] != c)
{
p[-3] = (unsigned char) pop_failure_jump;
@@ -4083,6 +4219,54 @@ re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
}
}
}
+ else if ((re_opcode_t) *p2 == charset)
+ {
+ register unsigned char c
+ = *p2 == (unsigned char) endline ? '\n' : p2[2];
+
+ if ((re_opcode_t) p1[3] == exactn
+ && ! (p2[1] * BYTEWIDTH > p1[4]
+ && (p2[1 + p1[4] / BYTEWIDTH]
+ & (1 << (p1[4] % BYTEWIDTH)))))
+ {
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n",
+ c, p1[5]);
+ }
+
+ else if ((re_opcode_t) p1[3] == charset_not)
+ {
+ int idx;
+ /* We win if the charset_not inside the loop
+ lists every character listed in the charset after. */
+ for (idx = 0; idx < p2[1]; idx++)
+ if (! (p2[2 + idx] == 0
+ || (idx < p1[4]
+ && ((p2[2 + idx] & ~ p1[5 + idx]) == 0))))
+ break;
+
+ if (idx == p2[1])
+ {
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT1 (" No match => pop_failure_jump.\n");
+ }
+ }
+ else if ((re_opcode_t) p1[3] == charset)
+ {
+ int idx;
+ /* We win if the charset inside the loop
+ has no overlap with the one after the loop. */
+ for (idx = 0; idx < p2[1] && idx < p1[4]; idx++)
+ if ((p2[2 + idx] & p1[5 + idx]) != 0)
+ break;
+
+ if (idx == p2[1] || idx == p1[4])
+ {
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT1 (" No match => pop_failure_jump.\n");
+ }
+ }
+ }
}
p -= 2; /* Point at relative address again. */
if ((re_opcode_t) p[-1] != pop_failure_jump)
diff --git a/gnu/usr.bin/diff/regex.h b/gnu/usr.bin/diff/regex.h
index 408dd21..a495005 100644
--- a/gnu/usr.bin/diff/regex.h
+++ b/gnu/usr.bin/diff/regex.h
@@ -1,7 +1,7 @@
/* Definitions for data structures and routines for the regular
expression library, version 0.12.
- Copyright (C) 1985, 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+ Copyright (C) 1985, 89, 90, 91, 92, 1993 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gnu/usr.bin/diff/sdiff.c b/gnu/usr.bin/diff/sdiff.c
index 7f1019e..783f101 100644
--- a/gnu/usr.bin/diff/sdiff.c
+++ b/gnu/usr.bin/diff/sdiff.c
@@ -1,5 +1,5 @@
/* SDIFF -- interactive merge front end to diff
- Copyright (C) 1992 Free Software Foundation, Inc.
+ Copyright (C) 1992, 1993 Free Software Foundation, Inc.
This file is part of GNU DIFF.
@@ -19,18 +19,14 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/* GNU SDIFF was written by Thomas Lord. */
+#include "system.h"
#include <stdio.h>
#include <ctype.h>
-#include "system.h"
#include <signal.h>
#include "getopt.h"
-#ifndef SEEK_SET
-#define SEEK_SET 0
-#endif
-
/* Size of chunks read from files which must be parsed into lines. */
-#define SDIFF_BUFSIZE 65536
+#define SDIFF_BUFSIZE ((size_t) 65536)
/* Default name of the diff program */
#ifndef DIFF_PROGRAM
@@ -42,7 +38,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#define DEFAULT_EDITOR "ed"
#endif
-extern char *version_string;
+extern char version_string[];
static char const *prog;
static char const *diffbin = DIFF_PROGRAM;
static char const *edbin = DEFAULT_EDITOR;
@@ -52,19 +48,42 @@ static int volatile tmpmade;
static pid_t volatile diffpid;
struct line_filter;
-static void diffarg (); /* (char *); */
-static void execdiff (); /* (int, char const *, char const *, char const *); */
-static int edit (); /* (struct line_filter *left, int lenl, struct
- line_filter *right, int lenr, FILE *outfile); */
-static int interact (); /* (struct line_filter *diff,
- struct line_filter *left,
- struct line_filter *right, FILE *outfile); */
-static void trapsigs (); /* (void); */
+
+static FILE *ck_fdopen PARAMS((int, char const *));
+static FILE *ck_fopen PARAMS((char const *, char const *));
+static RETSIGTYPE catchsig PARAMS((int));
+static VOID *xmalloc PARAMS((size_t));
+static char const *expand_name PARAMS((char *, int, char const *));
+static int edit PARAMS((struct line_filter *, int, struct line_filter *, int, FILE*));
+static int interact PARAMS((struct line_filter *, struct line_filter *, struct line_filter *, FILE*));
+static int lf_snarf PARAMS((struct line_filter *, char *, size_t));
+static int skip_white PARAMS((void));
+static size_t ck_fread PARAMS((char *, size_t, FILE *));
+static size_t lf_refill PARAMS((struct line_filter *));
+static void checksigs PARAMS((void));
+static void ck_fclose PARAMS((FILE *));
+static void ck_fflush PARAMS((FILE *));
+static void ck_fwrite PARAMS((char const *, size_t, FILE *));
+static void cleanup PARAMS((void));
+static void diffarg PARAMS((char const *));
+static void execdiff PARAMS((int, char const *, char const *, char const *));
+static void exiterr PARAMS((void));
+static void fatal PARAMS((char const *));
+static void flush_line PARAMS((void));
+static void give_help PARAMS((void));
+static void lf_copy PARAMS((struct line_filter *, int, FILE *));
+static void lf_init PARAMS((struct line_filter *, FILE *));
+static void lf_skip PARAMS((struct line_filter *, int));
+static void perror_fatal PARAMS((char const *));
+static void trapsigs PARAMS((void));
+static void untrapsig PARAMS((int));
+static void usage PARAMS((int));
+
/* this lossage until the gnu libc conquers the universe */
-#define TMPNAMSIZE 1024
#define PVT_tmpdir "/tmp"
-static char *private_tempnam (); /* (const char *, const char *, int, int *); */
-static int diraccess ();
+static char *private_tempnam PARAMS((char const *, char const *, int, size_t *));
+static int diraccess PARAMS((char const *));
+static int exists PARAMS((char const *));
/* Options: */
@@ -74,38 +93,40 @@ static char *out_file;
/* do not print common lines if true, set by -s option */
static int suppress_common_flag;
-static struct option longopts[] =
+static struct option const longopts[] =
{
- {"ignore-blank-lines", 0, NULL, 'B'},
- {"speed-large-files", 0, NULL, 'H'},
- {"ignore-matching-lines", 1, NULL, 'I'},
- {"ignore-all-space", 0, NULL, 'W'}, /* swap W and w for historical reasons */
- {"text", 0, NULL, 'a'},
- {"ignore-space-change", 0, NULL, 'b'},
- {"minimal", 0, NULL, 'd'},
- {"ignore-case", 0, NULL, 'i'},
- {"left-column", 0, NULL, 'l'},
- {"output", 1, NULL, 'o'},
- {"suppress-common-lines", 0, NULL, 's'},
- {"expand-tabs", 0, NULL, 't'},
- {"width", 1, NULL, 'w'},
- {"version", 0, NULL, 'v'},
- {NULL, 0, NULL, 0}
+ {"ignore-blank-lines", 0, 0, 'B'},
+ {"speed-large-files", 0, 0, 'H'},
+ {"ignore-matching-lines", 1, 0, 'I'},
+ {"ignore-all-space", 0, 0, 'W'}, /* swap W and w for historical reasons */
+ {"text", 0, 0, 'a'},
+ {"ignore-space-change", 0, 0, 'b'},
+ {"minimal", 0, 0, 'd'},
+ {"ignore-case", 0, 0, 'i'},
+ {"left-column", 0, 0, 'l'},
+ {"output", 1, 0, 'o'},
+ {"suppress-common-lines", 0, 0, 's'},
+ {"expand-tabs", 0, 0, 't'},
+ {"width", 1, 0, 'w'},
+ {"version", 0, 0, 'v'},
+ {"help", 0, 0, 129},
+ {0, 0, 0, 0}
};
/* prints usage message and quits */
static void
-usage ()
+usage (status)
+ int status;
{
- fprintf (stderr, "Usage: %s [options] from-file to-file\n", prog);
- fprintf (stderr, "Options:\n\
- [-abBdHilstv] [-I regexp] [-o outfile] [-w columns]\n\
- [--text] [--minimal] [--speed-large-files] [--expand-tabs]\n\
- [--ignore-case] [--ignore-matching-lines=regexp]\n\
- [--ignore-space-change] [--ignore-blank-lines] [--ignore-all-space]\n\
- [--suppress-common-lines] [--left-column] [--output=outfile]\n\
- [--version] [--width=columns]\n");
- exit (2);
+ printf ("Usage: %s [options] from-file to-file\n", prog);
+ printf ("Options:\n\
+ [-abBdHilstv] [-I regexp] [-o outfile] [-w columns]\n\
+ [--expand-tabs] [--help] [--ignore-all-space] [--ignore-blank-lines]\n\
+ [--ignore-case] [--ignore-matching-lines=regexp]\n\
+ [--ignore-space-change] [--left-column] [--minimal]\n\
+ [--output=outfile] [--speed-large-files] [--suppress-common-lines]\n\
+ [--text] [--version] [--width=columns]\n");
+ exit (status);
}
static void
@@ -121,12 +142,14 @@ static void
exiterr ()
{
cleanup ();
+ untrapsig (0);
+ checksigs ();
exit (2);
}
static void
fatal (msg)
- char *msg;
+ char const *msg;
{
fprintf (stderr, "%s: %s\n", prog, msg);
exiterr ();
@@ -134,9 +157,10 @@ fatal (msg)
static void
perror_fatal (msg)
- char *msg;
+ char const *msg;
{
int e = errno;
+ checksigs ();
fprintf (stderr, "%s: ", prog);
errno = e;
perror (msg);
@@ -145,19 +169,19 @@ perror_fatal (msg)
/* malloc freely or DIE! */
-char *
+static VOID *
xmalloc (size)
size_t size;
{
- char *r = malloc (size);
+ VOID *r = (VOID *) malloc (size);
if (!r)
- fatal ("virtual memory exhausted");
+ fatal ("memory exhausted");
return r;
}
static FILE *
ck_fopen (fname, type)
- char *fname, *type;
+ char const *fname, *type;
{
FILE *r = fopen (fname, type);
if (!r)
@@ -169,7 +193,7 @@ ck_fopen (fname, type)
static FILE *
ck_fdopen (fd, type)
int fd;
- char *type;
+ char const *type;
{
FILE *r = fdopen (fd, type);
if (!r)
@@ -199,7 +223,7 @@ ck_fread (buf, size, f)
static void
ck_fwrite (buf, size, f)
- char *buf;
+ char const *buf;
size_t size;
FILE *f;
{
@@ -218,11 +242,11 @@ ck_fflush (f)
#if !HAVE_MEMCHR
char *
memchr (s, c, n)
- char *s;
+ char const *s;
int c;
size_t n;
{
- unsigned char *p = (unsigned char *) s, *lim = p + n;
+ unsigned char const *p = (unsigned char const *) s, *lim = p + n;
for (; p < lim; p++)
if (*p == c)
return (char *) p;
@@ -273,14 +297,14 @@ expand_name (name, isdir, other_name)
else
{
/* Yield NAME/BASE, where BASE is OTHER_NAME's basename. */
- const char
- *p = rindex (other_name, '/'),
+ char const
+ *p = strrchr (other_name, '/'),
*base = p ? p+1 : other_name;
size_t namelen = strlen (name), baselen = strlen (base);
char *r = xmalloc (namelen + baselen + 2);
- bcopy (name, r, namelen);
+ memcpy (r, name, namelen);
r[namelen] = '/';
- bcopy (base, r + namelen + 1, baselen + 1);
+ memcpy (r + namelen + 1, base, baselen + 1);
return r;
}
}
@@ -313,6 +337,7 @@ lf_refill (lf)
lf->bufpos = lf->buffer;
lf->buflim = lf->buffer + s;
lf->buflim[0] = '\n';
+ checksigs ();
return s;
}
@@ -327,7 +352,7 @@ lf_copy (lf, lines, outfile)
while (lines)
{
- lf->bufpos = memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
+ lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
if (! lf->bufpos)
{
ck_fwrite (start, lf->buflim - start, outfile);
@@ -353,7 +378,7 @@ lf_skip (lf, lines)
{
while (lines)
{
- lf->bufpos = memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
+ lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
if (! lf->bufpos)
{
if (! lf_refill (lf))
@@ -378,11 +403,11 @@ lf_snarf (lf, buffer, bufsize)
for (;;)
{
- char *next = memchr (start, '\n', lf->buflim + 1 - start);
+ char *next = (char *) memchr (start, '\n', lf->buflim + 1 - start);
size_t s = next - start;
if (bufsize <= s)
return 0;
- bcopy (start, buffer, s);
+ memcpy (buffer, start, s);
if (next < lf->buflim)
{
buffer[s] = 0;
@@ -405,7 +430,6 @@ main (argc, argv)
char *argv[];
{
int opt;
- int version_requested = 0;
char *editor = getenv ("EDITOR");
char *differ = getenv ("DIFF");
@@ -418,7 +442,8 @@ main (argc, argv)
diffarg ("diff");
/* parse command line args */
- while ((opt=getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, (int *)0)) != EOF)
+ while ((opt = getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, 0))
+ != EOF)
{
switch (opt)
{
@@ -468,10 +493,8 @@ main (argc, argv)
break;
case 'v':
- version_requested = 1;
- fprintf (stderr, "GNU sdiff version %s\n", version_string);
- ck_fflush (stderr);
- break;
+ printf ("GNU sdiff version %s\n", version_string);
+ exit (0);
case 'w':
diffarg ("-W");
@@ -482,17 +505,16 @@ main (argc, argv)
diffarg ("-w");
break;
+ case 129:
+ usage (0);
+
default:
- usage ();
+ usage (2);
}
}
- /* check: did user just want version message? if so exit. */
- if (version_requested && argc - optind == 0)
- exit (0);
-
if (argc - optind != 2)
- usage ();
+ usage (2);
if (! out_file)
/* easy case: diff does everything for us */
@@ -530,9 +552,9 @@ main (argc, argv)
signal (SIGPIPE, SIG_DFL);
close (diff_fds[0]);
- if (diff_fds[1] != fileno (stdout))
+ if (diff_fds[1] != STDOUT_FILENO)
{
- dup2 (diff_fds[1], fileno (stdout));
+ dup2 (diff_fds[1], STDOUT_FILENO);
close (diff_fds[1]);
}
@@ -559,8 +581,11 @@ main (argc, argv)
{
int wstatus;
- if (waitpid (pid, &wstatus, 0) < 0)
- perror_fatal ("wait failed");
+ while (waitpid (pid, &wstatus, 0) < 0)
+ if (errno == EINTR)
+ checksigs ();
+ else
+ perror_fatal ("wait failed");
diffpid = 0;
if (tmpmade)
@@ -570,22 +595,24 @@ main (argc, argv)
}
if (! interact_ok)
- exit (2);
+ exiterr ();
if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2))
fatal ("Subsidiary diff failed");
+ untrapsig (0);
+ checksigs ();
exit (WEXITSTATUS (wstatus));
}
}
return 0; /* Fool -Wall . . . */
}
-static char **diffargv;
+static char const **diffargv;
static void
diffarg (a)
- char *a;
+ char const *a;
{
static unsigned diffargs, diffargsmax;
@@ -593,11 +620,12 @@ diffarg (a)
{
if (! diffargsmax)
{
- diffargv = (char **) xmalloc (sizeof (char));
+ diffargv = (char const **) xmalloc (sizeof (char));
diffargsmax = 8;
}
diffargsmax *= 2;
- diffargv = (char **) realloc (diffargv, diffargsmax * sizeof (char *));
+ diffargv = (char const **) realloc (diffargv,
+ diffargsmax * sizeof (char const *));
if (! diffargv)
fatal ("out of memory");
}
@@ -607,7 +635,7 @@ diffarg (a)
static void
execdiff (differences_only, option, file1, file2)
int differences_only;
- char *option, *file1, *file2;
+ char const *option, *file1, *file2;
{
if (differences_only)
diffarg ("--suppress-common-lines");
@@ -617,9 +645,9 @@ execdiff (differences_only, option, file1, file2)
diffarg (file2);
diffarg (0);
- execvp (diffbin, diffargv);
- write (fileno (stderr), diffbin, strlen (diffbin));
- write (fileno (stderr), ": not found\n", 12);
+ execvp (diffbin, (char **) diffargv);
+ write (STDERR_FILENO, diffbin, strlen (diffbin));
+ write (STDERR_FILENO, ": not found\n", 12);
_exit (2);
}
@@ -628,47 +656,120 @@ execdiff (differences_only, option, file1, file2)
/* Signal handling */
-static int volatile ignore_signals;
+#define NUM_SIGS (sizeof (sigs) / sizeof (*sigs))
+static int const sigs[] = {
+#ifdef SIGHUP
+ SIGHUP,
+#endif
+#ifdef SIGQUIT
+ SIGQUIT,
+#endif
+#ifdef SIGTERM
+ SIGTERM,
+#endif
+#ifdef SIGXCPU
+ SIGXCPU,
+#endif
+#ifdef SIGXFSZ
+ SIGXFSZ,
+#endif
+ SIGINT,
+ SIGPIPE
+};
+
+/* Prefer `sigaction' if it is available, since `signal' can lose signals. */
+#if HAVE_SIGACTION
+static struct sigaction initial_action[NUM_SIGS];
+#define initial_handler(i) (initial_action[i].sa_handler)
+#else
+static RETSIGTYPE (*initial_action[NUM_SIGS]) ();
+#define initial_handler(i) (initial_action[i])
+#endif
-static void
+static int volatile ignore_SIGINT;
+static int volatile signal_received;
+static int sigs_trapped;
+
+static RETSIGTYPE
catchsig (s)
int s;
{
- signal (s, catchsig);
- if (! ignore_signals)
+#if ! HAVE_SIGACTION
+ signal (s, SIG_IGN);
+#endif
+ if (! (s == SIGINT && ignore_SIGINT))
+ signal_received = s;
+}
+
+static void
+trapsigs ()
+{
+ int i;
+
+#if HAVE_SIGACTION
+ struct sigaction catchaction;
+ bzero (&catchaction, sizeof (catchaction));
+ catchaction.sa_handler = catchsig;
+#ifdef SA_INTERRUPT
+ /* Non-Posix BSD-style systems like SunOS 4.1.x need this
+ so that `read' calls are interrupted properly. */
+ catchaction.sa_flags = SA_INTERRUPT;
+#endif
+ sigemptyset (&catchaction.sa_mask);
+ for (i = 0; i < NUM_SIGS; i++)
+ sigaddset (&catchaction.sa_mask, sigs[i]);
+ for (i = 0; i < NUM_SIGS; i++)
{
- cleanup ();
- _exit (2);
+ sigaction (sigs[i], 0, &initial_action[i]);
+ if (initial_handler (i) != SIG_IGN
+ && sigaction (sigs[i], &catchaction, 0) != 0)
+ fatal ("signal error");
}
+#else /* ! HAVE_SIGACTION */
+ for (i = 0; i < NUM_SIGS; i++)
+ {
+ initial_action[i] = signal (sigs[i], SIG_IGN);
+ if (initial_handler (i) != SIG_IGN
+ && signal (sigs[i], catchsig) != SIG_IGN)
+ fatal ("signal error");
+ }
+#endif /* ! HAVE_SIGACTION */
+ sigs_trapped = 1;
}
+/* Untrap signal S, or all trapped signals if S is zero. */
static void
-trapsigs ()
+untrapsig (s)
+ int s;
{
- static int const sigs[] = {
-# ifdef SIGHUP
- SIGHUP,
-# endif
-# ifdef SIGQUIT
- SIGQUIT,
-# endif
-# ifdef SIGTERM
- SIGTERM,
-# endif
-# ifdef SIGXCPU
- SIGXCPU,
-# endif
-# ifdef SIGXFSZ
- SIGXFSZ,
-# endif
- SIGINT,
- SIGPIPE
- };
- int const *p;
-
- for (p = sigs; p < sigs + sizeof (sigs) / sizeof (*sigs); p++)
- if (signal (*p, SIG_IGN) != SIG_IGN && signal (*p, catchsig) != SIG_IGN)
- fatal ("signal error");
+ int i;
+
+ if (sigs_trapped)
+ for (i = 0; i < NUM_SIGS; i++)
+ if ((!s || sigs[i] == s) && initial_handler (i) != SIG_IGN)
+#if HAVE_SIGACTION
+ sigaction (sigs[i], &initial_action[i], 0);
+#else
+ signal (sigs[i], initial_action[i]);
+#endif
+}
+
+/* Exit if a signal has been received. */
+static void
+checksigs ()
+{
+ int s = signal_received;
+ if (s)
+ {
+ cleanup ();
+
+ /* Yield an exit status indicating that a signal was received. */
+ untrapsig (s);
+ kill (getpid (), s);
+
+ /* That didn't work, so exit with error status. */
+ exit (2);
+ }
}
@@ -692,7 +793,7 @@ skip_white ()
{
int c;
while (isspace (c = getchar ()) && c != '\n')
- ;
+ checksigs ();
if (ferror (stdin))
perror_fatal ("input error");
return c;
@@ -723,6 +824,8 @@ edit (left, lenl, right, lenr, outfile)
int cmd0, cmd1;
int gotcmd = 0;
+ cmd1 = 0; /* Pacify `gcc -W'. */
+
while (!gotcmd)
{
if (putchar ('%') != '%')
@@ -773,8 +876,10 @@ edit (left, lenl, right, lenr, outfile)
}
/* falls through */
default:
- give_help ();
flush_line ();
+ /* falls through */
+ case '\n':
+ give_help ();
continue;
}
}
@@ -822,7 +927,8 @@ edit (left, lenl, right, lenr, outfile)
pid_t pid;
int wstatus;
- ignore_signals = 1;
+ ignore_SIGINT = 1;
+ checksigs ();
pid = vfork ();
if (pid == 0)
@@ -835,8 +941,8 @@ edit (left, lenl, right, lenr, outfile)
argv[i++] = 0;
execvp (edbin, (char **) argv);
- write (fileno (stderr), edbin, strlen (edbin));
- write (fileno (stderr), ": not found\n", 12);
+ write (STDERR_FILENO, edbin, strlen (edbin));
+ write (STDERR_FILENO, ": not found\n", 12);
_exit (1);
}
@@ -844,10 +950,12 @@ edit (left, lenl, right, lenr, outfile)
perror_fatal ("fork failed");
while (waitpid (pid, &wstatus, 0) < 0)
- if (errno != EINTR)
+ if (errno == EINTR)
+ checksigs ();
+ else
perror_fatal ("wait failed");
- ignore_signals = 0;
+ ignore_SIGINT = 0;
if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 1))
fatal ("Subsidiary editor failed");
@@ -858,11 +966,14 @@ edit (left, lenl, right, lenr, outfile)
{
/* SDIFF_BUFSIZE is too big for a local var
in some compilers, so we allocate it dynamically. */
- char *buf = (char *) xmalloc (SDIFF_BUFSIZE);
+ char *buf = xmalloc (SDIFF_BUFSIZE);
size_t size;
while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
- ck_fwrite (buf, size, outfile);
+ {
+ checksigs ();
+ ck_fwrite (buf, size, outfile);
+ }
ck_fclose (tmp);
free (buf);
@@ -878,7 +989,7 @@ edit (left, lenl, right, lenr, outfile)
-/* Alternately reveal bursts of diff output and handle user editing comands. */
+/* Alternately reveal bursts of diff output and handle user commands. */
static int
interact (diff, left, right, outfile)
struct line_filter *diff;
@@ -894,6 +1005,8 @@ interact (diff, left, right, outfile)
if (snarfed <= 0)
return snarfed;
+ checksigs ();
+
switch (diff_help[0])
{
case ' ':
@@ -902,7 +1015,7 @@ interact (diff, left, right, outfile)
case 'i':
{
int lenl = atoi (diff_help + 1), lenr, lenmax;
- char *p = index (diff_help, ',');
+ char *p = strchr (diff_help, ',');
if (!p)
fatal (diff_help);
@@ -921,7 +1034,7 @@ interact (diff, left, right, outfile)
case 'c':
{
int lenl = atoi (diff_help + 1), lenr;
- char *p = index (diff_help, ',');
+ char *p = strchr (diff_help, ',');
if (!p)
fatal (diff_help);
@@ -941,10 +1054,10 @@ interact (diff, left, right, outfile)
/* temporary lossage: this is torn from gnu libc */
-/* Return nonzero if DIR is an existent directory. */
+/* Return nonzero if DIR is an existing directory. */
static int
diraccess (dir)
- const char *dir;
+ char const *dir;
{
struct stat buf;
return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
@@ -953,21 +1066,21 @@ diraccess (dir)
/* Return nonzero if FILE exists. */
static int
exists (file)
- const char *file;
+ char const *file;
{
struct stat buf;
return stat (file, &buf) == 0;
}
/* These are the characters used in temporary filenames. */
-static const char letters[] =
+static char const letters[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
/* Generate a temporary filename.
If DIR_SEARCH is nonzero, DIR and PFX are used as
described for tempnam. If not, a temporary filename
in P_tmpdir with no special prefix is generated. If LENPTR
- is not NULL, *LENPTR is set the to length (including the
+ is not 0, *LENPTR is set the to length (including the
terminating '\0') of the resultant filename, which is returned.
This goes through a cyclic pattern of all possible filenames
consisting of five decimal digits of the current pid and three
@@ -976,50 +1089,51 @@ static const char letters[] =
prefix (i.e, it is identical to tmpnam), the same data is used.
Each potential filename is tested for an already-existing file of
the same name, and no name of an existing file will be returned.
- When the cycle reaches its end (12345ZZZ), NULL is returned. */
+ When the cycle reaches its end (12345ZZZ), 0 is returned. */
static char *
private_tempnam (dir, pfx, dir_search, lenptr)
- const char *dir;
- const char *pfx;
+ char const *dir;
+ char const *pfx;
int dir_search;
size_t *lenptr;
{
- static const char tmpdir[] = PVT_tmpdir;
+ static char const tmpdir[] = PVT_tmpdir;
static struct
{
char buf[3];
char *s;
size_t i;
} infos[2], *info;
- static char buf[TMPNAMSIZE];
+ static char *buf;
+ static size_t bufsize = 1;
static pid_t oldpid = 0;
pid_t pid = getpid ();
register size_t len, plen;
if (dir_search)
{
- register const char *d = getenv ("TMPDIR");
- if (d != NULL && !diraccess (d))
- d = NULL;
- if (d == NULL && dir != NULL && diraccess (dir))
+ register char const *d = getenv ("TMPDIR");
+ if (d && !diraccess (d))
+ d = 0;
+ if (!d && dir && diraccess (dir))
d = dir;
- if (d == NULL && diraccess (tmpdir))
+ if (!d && diraccess (tmpdir))
d = tmpdir;
- if (d == NULL && diraccess ("/tmp"))
+ if (!d && diraccess ("/tmp"))
d = "/tmp";
- if (d == NULL)
+ if (!d)
{
errno = ENOENT;
- return NULL;
+ return 0;
}
dir = d;
}
else
dir = tmpdir;
- if (pfx != NULL && *pfx != '\0')
+ if (pfx && *pfx)
{
plen = strlen (pfx);
if (plen > 5)
@@ -1041,11 +1155,23 @@ private_tempnam (dir, pfx, dir_search, lenptr)
}
len = strlen (dir) + 1 + plen + 8;
+ if (bufsize <= len)
+ {
+ do
+ {
+ bufsize *= 2;
+ }
+ while (bufsize <= len);
+
+ if (buf)
+ free (buf);
+ buf = xmalloc (bufsize);
+ }
for (;;)
{
*info->s = letters[info->i];
- sprintf (buf, "%s/%.*s%.5d%.3s", dir, (int) plen, pfx,
- pid % 100000, info->buf);
+ sprintf (buf, "%s/%.*s%.5lu%.3s", dir, (int) plen, pfx,
+ (unsigned long) pid % 100000, info->buf);
if (!exists (buf))
break;
++info->i;
@@ -1055,13 +1181,13 @@ private_tempnam (dir, pfx, dir_search, lenptr)
if (info->s == &info->buf[2])
{
errno = EEXIST;
- return NULL;
+ return 0;
}
++info->s;
}
}
- if (lenptr != NULL)
+ if (lenptr)
*lenptr = len;
return buf;
}
diff --git a/gnu/usr.bin/diff/side.c b/gnu/usr.bin/diff/side.c
index a3d6d8b..9e03c27 100644
--- a/gnu/usr.bin/diff/side.c
+++ b/gnu/usr.bin/diff/side.c
@@ -21,10 +21,11 @@ and this notice must be preserved on all copies. */
#include "diff.h"
-
-static void print_sdiff_hunk ();
-static void print_sdiff_common_lines ();
-static void print_1sdiff_line ();
+static unsigned print_half_line PARAMS((char const * const *, unsigned, unsigned));
+static unsigned tab_from_to PARAMS((unsigned, unsigned));
+static void print_1sdiff_line PARAMS((char const * const *, int, char const * const *));
+static void print_sdiff_common_lines PARAMS((int, int));
+static void print_sdiff_hunk PARAMS((struct change *));
/* Next line number to be printed in the two input files. */
static int next0, next1;
@@ -70,12 +71,12 @@ tab_from_to (from, to)
*/
static unsigned
print_half_line (line, indent, out_bound)
- const char * const *line;
+ char const * const *line;
unsigned indent, out_bound;
{
FILE *out = outfile;
register unsigned in_position = 0, out_position = 0;
- register const char
+ register char const
*text_pointer = line[0],
*text_limit = line[1];
@@ -132,21 +133,21 @@ print_half_line (line, indent, out_bound)
case '\f':
case '\v':
+ control_char:
if (in_position < out_bound)
putc (c, out);
break;
default:
- {
- register unsigned p = in_position;
- if (textchar[c])
- in_position++;
- if (p < out_bound)
- {
- out_position = in_position;
- putc (c, out);
- }
- }
+ if (! isprint (c))
+ goto control_char;
+ /* falls through */
+ case ' ':
+ if (in_position++ < out_bound)
+ {
+ out_position = in_position;
+ putc (c, out);
+ }
break;
case '\n':
@@ -159,15 +160,15 @@ print_half_line (line, indent, out_bound)
/*
* Print side by side lines with a separator in the middle.
- * NULL parameters are taken to indicate whitespace text.
+ * 0 parameters are taken to indicate white space text.
* Blank lines that can easily be caught are reduced to a single newline.
*/
static void
print_1sdiff_line (left, sep, right)
- const char * const *left;
+ char const * const *left;
int sep;
- const char * const *right;
+ char const * const *right;
{
FILE *out = outfile;
unsigned hw = sdiff_half_width, c2o = sdiff_column2_offset;
diff --git a/gnu/usr.bin/diff/system.h b/gnu/usr.bin/diff/system.h
index b17d39a..2da6247 100644
--- a/gnu/usr.bin/diff/system.h
+++ b/gnu/usr.bin/diff/system.h
@@ -17,20 +17,84 @@ You should have received a copy of the GNU General Public License
along with GNU DIFF; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+/* We must define `volatile' and `const' first (the latter inside config.h),
+ so that they're used consistently in all system includes. */
+#if !__STDC__
+#ifndef volatile
+#define volatile
+#endif
+#endif
+#include "config.h"
+
#include <sys/types.h>
#include <sys/stat.h>
+#if __STDC__
+#define PARAMS(args) args
+#define VOID void
+#else
+#define PARAMS(args) ()
+#define VOID char
+#endif
+
+#if STAT_MACROS_BROKEN
+#undef S_ISBLK
+#undef S_ISCHR
+#undef S_ISDIR
+#undef S_ISFIFO
+#undef S_ISREG
+#undef S_ISSOCK
+#endif
#ifndef S_ISDIR
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#endif
#ifndef S_ISREG
#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
#endif
+#if !defined(S_ISBLK) && defined(S_IFBLK)
+#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
+#endif
+#if !defined(S_ISCHR) && defined(S_IFCHR)
+#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
+#endif
+#if !defined(S_ISFIFO) && defined(S_IFFIFO)
+#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFFIFO)
+#endif
+#if !defined(S_ISSOCK) && defined(S_IFSOCK)
+#define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
+#endif
+
+#ifndef S_IXOTH
+#define S_IXOTH 1
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP (S_IXOTH << 3)
+#endif
+#ifndef S_IXUSR
+#define S_IXUSR (S_IXGRP << 3)
+#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+
+#ifndef STDIN_FILENO
+#define STDIN_FILENO 0
+#endif
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO 1
+#endif
+#ifndef STDERR_FILENO
+#define STDERR_FILENO 2
+#endif
+
#if HAVE_TIME_H
#include <time.h>
#else
@@ -73,15 +137,11 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#if HAVE_ST_BLKSIZE
#define STAT_BLOCKSIZE(s) (s).st_blksize
#else
-#define STAT_BLOCKSIZE(s) (S_ISREG ((s).st_mode) ? 8192 : 4096)
+#define STAT_BLOCKSIZE(s) (8 * 1024)
#endif
#if DIRENT || defined (_POSIX_VERSION)
#include <dirent.h>
-#ifdef direct
-#undef direct
-#endif
-#define direct dirent
#else /* ! (DIRENT || defined (_POSIX_VERSION)) */
#if SYSNDIR
#include <sys/ndir.h>
@@ -92,49 +152,58 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <ndir.h>
#endif
#endif
+#ifdef dirent
+#undef dirent
+#endif
+#define dirent direct
#endif /* ! (DIRENT || defined (_POSIX_VERSION)) */
#if HAVE_VFORK_H
#include <vfork.h>
#endif
-#if HAVE_STRING_H || STDC_HEADERS
-#include <string.h>
-#ifndef index
-#define index strchr
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#else
+VOID *malloc ();
+VOID *realloc ();
#endif
-#ifndef rindex
-#define rindex strrchr
+#ifndef getenv
+char *getenv ();
#endif
-#ifndef bcopy
-#define bcopy(s,d,n) memcpy (d,s,n)
+
+#if HAVE_LIMITS_H
+#include <limits.h>
#endif
-#ifndef bcmp
-#define bcmp(s1,s2,n) memcmp (s1,s2,n)
+#ifndef INT_MAX
+#define INT_MAX 2147483647
+#endif
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
#endif
+
+#if HAVE_STRING_H
+#include <string.h>
#ifndef bzero
-#define bzero(s,n) memset (s,0,n)
+#define bzero(s,n) memset (s,0,n)
#endif
-#else
+#else /* !HAVE_STRING_H */
#include <strings.h>
+#ifndef strchr
+#define strchr index
#endif
-#if !HAVE_MEMCHR && !STDC_HEADERS
-char *memchr ();
+#ifndef strrchr
+#define strrchr rindex
#endif
-
-#if STDC_HEADERS
-#include <stdlib.h>
-#include <limits.h>
-#else
-char *getenv ();
-char *malloc ();
-char *realloc ();
-#if __STDC__ || __GNUC__
-#include "limits.h"
-#else
-#define INT_MAX 2147483647
-#define CHAR_BIT 8
+#ifndef memcpy
+#define memcpy(d,s,n) bcopy (s,d,n)
+#endif
+#ifndef memcmp
+#define memcmp(s1,s2,n) bcmp (s1,s2,n)
#endif
+#endif /* !HAVE_STRING_H */
+#if !HAVE_MEMCHR
+char *memchr ();
#endif
#include <errno.h>
@@ -142,18 +211,5 @@ char *realloc ();
extern int errno;
#endif
-#ifdef TRUE
-#undef TRUE
-#endif
-#ifdef FALSE
-#undef FALSE
-#endif
-#define TRUE 1
-#define FALSE 0
-
-#if !__STDC__
-#define volatile
-#endif
-
#define min(a,b) ((a) <= (b) ? (a) : (b))
#define max(a,b) ((a) >= (b) ? (a) : (b))
diff --git a/gnu/usr.bin/diff/util.c b/gnu/usr.bin/diff/util.c
index e72fd4d..38e20f4 100644
--- a/gnu/usr.bin/diff/util.c
+++ b/gnu/usr.bin/diff/util.c
@@ -1,5 +1,5 @@
/* Support routines for GNU DIFF.
- Copyright (C) 1988, 1989, 1992 Free Software Foundation, Inc.
+ Copyright (C) 1988, 1989, 1992, 1993 Free Software Foundation, Inc.
This file is part of GNU DIFF.
@@ -19,12 +19,33 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "diff.h"
+/* Queue up one-line messages to be printed at the end,
+ when -l is specified. Each message is recorded with a `struct msg'. */
+
+struct msg
+{
+ struct msg *next;
+ char const *format;
+ char const *arg1;
+ char const *arg2;
+ char const *arg3;
+ char const *arg4;
+};
+
+/* Head of the chain of queues messages. */
+
+static struct msg *msg_chain;
+
+/* Tail of the chain of queues messages. */
+
+static struct msg **msg_chain_end = &msg_chain;
+
/* Use when a system call returns non-zero status.
TEXT should normally be the file name. */
void
perror_with_name (text)
- char *text;
+ char const *text;
{
int e = errno;
fprintf (stderr, "%s: ", program);
@@ -36,7 +57,7 @@ perror_with_name (text)
void
pfatal_with_name (text)
- char *text;
+ char const *text;
{
int e = errno;
print_message_queue ();
@@ -51,9 +72,7 @@ pfatal_with_name (text)
void
error (format, arg, arg1)
- char *format;
- char *arg;
- char *arg1;
+ char const *format, *arg, *arg1;
{
fprintf (stderr, "%s: ", program);
fprintf (stderr, format, arg, arg1);
@@ -64,7 +83,7 @@ error (format, arg, arg1)
void
fatal (m)
- char *m;
+ char const *m;
{
print_message_queue ();
error ("%s", m, 0);
@@ -76,28 +95,32 @@ fatal (m)
void
message (format, arg1, arg2)
- char *format, *arg1, *arg2;
+ char const *format, *arg1, *arg2;
+{
+ message5 (format, arg1, arg2, 0, 0);
+}
+
+void
+message5 (format, arg1, arg2, arg3, arg4)
+ char const *format, *arg1, *arg2, *arg3, *arg4;
{
if (paginate_flag)
{
struct msg *new = (struct msg *) xmalloc (sizeof (struct msg));
- if (msg_chain_end == 0)
- msg_chain = msg_chain_end = new;
- else
- {
- msg_chain_end->next = new;
- msg_chain_end = new;
- }
new->format = format;
new->arg1 = concat (arg1, "", "");
new->arg2 = concat (arg2, "", "");
+ new->arg3 = arg3 ? concat (arg3, "", "") : 0;
+ new->arg4 = arg4 ? concat (arg4, "", "") : 0;
new->next = 0;
+ *msg_chain_end = new;
+ msg_chain_end = &new->next;
}
else
{
if (sdiff_help_sdiff)
putchar (' ');
- printf (format, arg1, arg2);
+ printf (format, arg1, arg2, arg3, arg4);
}
}
@@ -109,7 +132,7 @@ print_message_queue ()
struct msg *m;
for (m = msg_chain; m; m = m->next)
- printf (m->format, m->arg1, m->arg2);
+ printf (m->format, m->arg1, m->arg2, m->arg3, m->arg4);
}
/* Call before outputting the results of comparing files NAME0 and NAME1
@@ -119,13 +142,13 @@ print_message_queue ()
we fork off a `pr' and make OUTFILE a pipe to it.
`pr' then outputs to our stdout. */
-static char *current_name0;
-static char *current_name1;
+static char const *current_name0;
+static char const *current_name1;
static int current_depth;
void
setup_output (name0, name1, depth)
- char *name0, *name1;
+ char const *name0, *name1;
int depth;
{
current_name0 = name0;
@@ -134,6 +157,8 @@ setup_output (name0, name1, depth)
outfile = 0;
}
+static pid_t pr_pid;
+
void
begin_output ()
{
@@ -143,49 +168,47 @@ begin_output ()
return;
/* Construct the header of this piece of diff. */
- name = (char *) xmalloc (strlen (current_name0) + strlen (current_name1)
- + strlen (switch_string) + 15);
-
- strcpy (name, "diff");
- strcat (name, switch_string);
- strcat (name, " ");
- strcat (name, current_name0);
- strcat (name, " ");
- strcat (name, current_name1);
+ name = xmalloc (strlen (current_name0) + strlen (current_name1)
+ + strlen (switch_string) + 7);
+ /* Posix.2 section 4.17.6.1.1 specifies this format. But there are some
+ bugs in the first printing (IEEE Std 1003.2-1992 p 251 l 3304):
+ it says that we must print only the last component of the pathnames,
+ and it requires two spaces after "diff" if there are no options.
+ These requirements are silly and do not match historical practice. */
+ sprintf (name, "diff%s %s %s", switch_string, current_name0, current_name1);
if (paginate_flag)
{
int pipes[2];
- int desc;
- /* For a `pr' and make OUTFILE a pipe to it. */
+ /* Fork a `pr' and make OUTFILE a pipe to it. */
if (pipe (pipes) < 0)
pfatal_with_name ("pipe");
fflush (stdout);
- desc = vfork ();
- if (desc < 0)
+ pr_pid = vfork ();
+ if (pr_pid < 0)
pfatal_with_name ("vfork");
- if (desc == 0)
+ if (pr_pid == 0)
{
close (pipes[1]);
- if (pipes[0] != fileno (stdin))
+ if (pipes[0] != STDIN_FILENO)
{
- if (dup2 (pipes[0], fileno (stdin)) < 0)
+ if (dup2 (pipes[0], STDIN_FILENO) < 0)
pfatal_with_name ("dup2");
close (pipes[0]);
}
- if (execl (PR_FILE_NAME, PR_FILE_NAME, "-f", "-h", name, 0) < 0)
- pfatal_with_name (PR_FILE_NAME);
+ execl (PR_FILE_NAME, PR_FILE_NAME, "-f", "-h", name, 0);
+ pfatal_with_name (PR_FILE_NAME);
}
else
{
close (pipes[0]);
outfile = fdopen (pipes[1], "w");
- }
+ }
}
else
{
@@ -226,8 +249,25 @@ finish_output ()
{
if (outfile != 0 && outfile != stdout)
{
- fclose (outfile);
- wait (0);
+ int wstatus;
+ if (ferror (outfile))
+ fatal ("write error");
+ if (fclose (outfile) != 0)
+ pfatal_with_name ("write error");
+#if HAVE_WAITPID
+ if (waitpid (pr_pid, &wstatus, 0) < 0)
+ pfatal_with_name ("waitpid");
+#else
+ for (;;) {
+ pid_t w = wait (&wstatus);
+ if (w < 0)
+ pfatal_with_name ("wait");
+ if (w == pr_pid)
+ break;
+ }
+#endif
+ if (! WIFEXITED (wstatus) || WEXITSTATUS (wstatus) != 0)
+ fatal ("subsidiary pr failed");
}
outfile = 0;
@@ -235,14 +275,14 @@ finish_output ()
/* Compare two lines (typically one from each input file)
according to the command line options.
- Return 1 if the lines differ, like `bcmp'. */
+ Return 1 if the lines differ, like `memcmp'. */
int
line_cmp (s1, len1, s2, len2)
- const char *s1, *s2;
- int len1, len2;
+ char const *s1, *s2;
+ size_t len1, len2;
{
- register const unsigned char *t1, *t2;
+ register unsigned char const *t1, *t2;
register unsigned char end_char = line_end_char;
/* Check first for exact identity.
@@ -250,43 +290,41 @@ line_cmp (s1, len1, s2, len2)
This detects the common case of exact identity
faster than complete comparison would. */
- if (len1 == len2 && bcmp (s1, s2, len1) == 0)
+ if (len1 == len2 && memcmp (s1, s2, len1) == 0)
return 0;
/* Not exactly identical, but perhaps they match anyway
- when case or whitespace is ignored. */
+ when case or white space is ignored. */
- if (ignore_case_flag || ignore_space_change_flag || ignore_all_space_flag)
+ if (ignore_case_flag | ignore_space_change_flag | ignore_all_space_flag)
{
- t1 = (const unsigned char *) s1;
- t2 = (const unsigned char *) s2;
+ t1 = (unsigned char const *) s1;
+ t2 = (unsigned char const *) s2;
while (1)
{
register unsigned char c1 = *t1++;
register unsigned char c2 = *t2++;
- /* Ignore horizontal whitespace if -b or -w is specified. */
+ /* Ignore horizontal white space if -b or -w is specified. */
if (ignore_all_space_flag)
{
/* For -w, just skip past any white space. */
- while (Is_space (c1)) c1 = *t1++;
- while (Is_space (c2)) c2 = *t2++;
+ while (isspace (c1) && c1 != end_char) c1 = *t1++;
+ while (isspace (c2) && c2 != end_char) c2 = *t2++;
}
else if (ignore_space_change_flag)
{
- /* For -b, advance past any sequence of whitespace in line 1
+ /* For -b, advance past any sequence of white space in line 1
and consider it just one Space, or nothing at all
if it is at the end of the line. */
- if (c1 == ' ' || c1 == '\t')
+ if (isspace (c1))
{
- while (1)
+ while (c1 != end_char)
{
c1 = *t1++;
- if (c1 == end_char)
- break;
- if (c1 != ' ' && c1 != '\t')
+ if (! isspace (c1))
{
--t1;
c1 = ' ';
@@ -296,14 +334,12 @@ line_cmp (s1, len1, s2, len2)
}
/* Likewise for line 2. */
- if (c2 == ' ' || c2 == '\t')
+ if (isspace (c2))
{
- while (1)
+ while (c2 != end_char)
{
c2 = *t2++;
- if (c2 == end_char)
- break;
- if (c2 != ' ' && c2 != '\t')
+ if (! isspace (c2))
{
--t2;
c2 = ' ';
@@ -364,8 +400,8 @@ find_reverse_change (start)
void
print_script (script, hunkfun, printfun)
struct change *script;
- struct change * (*hunkfun) ();
- void (*printfun) ();
+ struct change * (*hunkfun) PARAMS((struct change *));
+ void (*printfun) PARAMS((struct change *));
{
struct change *next = script;
@@ -380,7 +416,7 @@ print_script (script, hunkfun, printfun)
/* Disconnect them from the rest of the changes,
making them a hunk, and remember the rest for next iteration. */
next = end->link;
- end->link = NULL;
+ end->link = 0;
#ifdef DEBUG
debug_script (this);
#endif
@@ -399,18 +435,18 @@ print_script (script, hunkfun, printfun)
void
print_1_line (line_flag, line)
- const char *line_flag;
- const char * const *line;
+ char const *line_flag;
+ char const * const *line;
{
- const char *text = line[0], *limit = line[1]; /* Help the compiler. */
+ char const *text = line[0], *limit = line[1]; /* Help the compiler. */
FILE *out = outfile; /* Help the compiler some more. */
- const char *flag_format = 0;
+ char const *flag_format = 0;
/* If -T was specified, use a Tab between the line-flag and the text.
Otherwise use a Space (as Unix diff does).
Print neither space nor tab if line-flags are empty. */
- if (line_flag != NULL && line_flag[0] != 0)
+ if (line_flag && *line_flag)
{
flag_format = tab_align_flag ? "%s\t" : "%s ";
fprintf (out, flag_format, line_flag);
@@ -418,7 +454,7 @@ print_1_line (line_flag, line)
output_1_line (text, limit, flag_format, line_flag);
- if ((line_flag == NULL || line_flag[0] != 0) && limit[-1] != '\n'
+ if ((!line_flag || line_flag[0]) && limit[-1] != '\n'
&& line_end_char == '\n')
fprintf (out, "\n\\ No newline at end of file\n");
}
@@ -430,15 +466,15 @@ print_1_line (line_flag, line)
void
output_1_line (text, limit, flag_format, line_flag)
- const char *text, *limit, *flag_format, *line_flag;
+ char const *text, *limit, *flag_format, *line_flag;
{
if (!tab_expand_flag)
fwrite (text, sizeof (char), limit - text, outfile);
else
{
register FILE *out = outfile;
- register char c;
- register const char *t = text;
+ register unsigned char c;
+ register char const *t = text;
register unsigned column = 0;
while (t < limit)
@@ -469,11 +505,8 @@ output_1_line (text, limit, flag_format, line_flag)
break;
default:
- if (textchar[(unsigned char) c])
+ if (isprint (c))
column++;
- /* fall into */
- case '\f':
- case '\v':
putc (c, out);
break;
}
@@ -501,7 +534,7 @@ change_letter (inserts, deletes)
int
translate_line_number (file, lnum)
- struct file_data *file;
+ struct file_data const *file;
int lnum;
{
return lnum + file->prefix_lines + 1;
@@ -509,7 +542,7 @@ translate_line_number (file, lnum)
void
translate_range (file, a, b, aptr, bptr)
- struct file_data *file;
+ struct file_data const *file;
int a, b;
int *aptr, *bptr;
{
@@ -544,7 +577,7 @@ print_number_range (sepchar, file, a, b)
/* Look at a hunk of edit script and report the range of lines in each file
that it applies to. HUNK is the start of the hunk, which is a chain
of `struct change'. The first and last line numbers of file 0 are stored in
- *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1.
+ *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1.
Note that these are internal line numbers that count from 0.
If no lines from file 0 are deleted, then FIRST0 is LAST0+1.
@@ -560,28 +593,29 @@ analyze_hunk (hunk, first0, last0, first1, last1, deletes, inserts)
int *first0, *last0, *first1, *last1;
int *deletes, *inserts;
{
- int f0, l0, f1, l1, show_from, show_to;
+ int l0, l1, show_from, show_to;
int i;
- int nontrivial = !(ignore_blank_lines_flag || ignore_regexp_list);
+ int trivial = ignore_blank_lines_flag || ignore_regexp_list;
struct change *next;
show_from = show_to = 0;
- f0 = hunk->line0;
- f1 = hunk->line1;
+ *first0 = hunk->line0;
+ *first1 = hunk->line1;
- for (next = hunk; next; next = next->link)
+ next = hunk;
+ do
{
l0 = next->line0 + next->deleted - 1;
l1 = next->line1 + next->inserted - 1;
show_from += next->deleted;
show_to += next->inserted;
- for (i = next->line0; i <= l0 && ! nontrivial; i++)
+ for (i = next->line0; i <= l0 && trivial; i++)
if (!ignore_blank_lines_flag || files[0].linbuf[i][0] != '\n')
{
struct regexp_list *r;
- const char *line = files[0].linbuf[i];
+ char const *line = files[0].linbuf[i];
int len = files[0].linbuf[i + 1] - line;
for (r = ignore_regexp_list; r; r = r->next)
@@ -589,15 +623,15 @@ analyze_hunk (hunk, first0, last0, first1, last1, deletes, inserts)
break; /* Found a match. Ignore this line. */
/* If we got all the way through the regexp list without
finding a match, then it's nontrivial. */
- if (r == NULL)
- nontrivial = 1;
+ if (!r)
+ trivial = 0;
}
- for (i = next->line1; i <= l1 && ! nontrivial; i++)
+ for (i = next->line1; i <= l1 && trivial; i++)
if (!ignore_blank_lines_flag || files[1].linbuf[i][0] != '\n')
{
struct regexp_list *r;
- const char *line = files[1].linbuf[i];
+ char const *line = files[1].linbuf[i];
int len = files[1].linbuf[i + 1] - line;
for (r = ignore_regexp_list; r; r = r->next)
@@ -605,20 +639,19 @@ analyze_hunk (hunk, first0, last0, first1, last1, deletes, inserts)
break; /* Found a match. Ignore this line. */
/* If we got all the way through the regexp list without
finding a match, then it's nontrivial. */
- if (r == NULL)
- nontrivial = 1;
+ if (!r)
+ trivial = 0;
}
}
+ while ((next = next->link) != 0);
- *first0 = f0;
*last0 = l0;
- *first1 = f1;
*last1 = l1;
/* If all inserted or deleted lines are ignorable,
tell the caller to ignore this hunk. */
- if (!nontrivial)
+ if (trivial)
show_from = show_to = 0;
*deletes = show_from;
@@ -629,7 +662,7 @@ analyze_hunk (hunk, first0, last0, first1, last1, deletes, inserts)
VOID *
xmalloc (size)
- unsigned size;
+ size_t size;
{
register VOID *value;
@@ -639,7 +672,7 @@ xmalloc (size)
value = (VOID *) malloc (size);
if (!value)
- fatal ("virtual memory exhausted");
+ fatal ("memory exhausted");
return value;
}
@@ -648,7 +681,7 @@ xmalloc (size)
VOID *
xrealloc (old, size)
VOID *old;
- unsigned int size;
+ size_t size;
{
register VOID *value;
@@ -658,7 +691,7 @@ xrealloc (old, size)
value = (VOID *) realloc (old, size);
if (!value)
- fatal ("virtual memory exhausted");
+ fatal ("memory exhausted");
return value;
}
@@ -666,15 +699,23 @@ xrealloc (old, size)
char *
concat (s1, s2, s3)
- char *s1, *s2, *s3;
+ char const *s1, *s2, *s3;
{
- int len = strlen (s1) + strlen (s2) + strlen (s3);
- char *new = (char *) xmalloc (len + 1);
- strcpy (new, s1);
- strcat (new, s2);
- strcat (new, s3);
+ size_t len = strlen (s1) + strlen (s2) + strlen (s3);
+ char *new = xmalloc (len + 1);
+ sprintf (new, "%s%s%s", s1, s2, s3);
return new;
}
+
+/* Yield the newly malloc'd pathname
+ of the file in DIR whose filename is FILE. */
+
+char *
+dir_file_pathname (dir, file)
+ char const *dir, *file;
+{
+ return concat (dir, "/" + (*dir && dir[strlen (dir) - 1] == '/'), file);
+}
void
debug_script (sp)
@@ -690,11 +731,11 @@ debug_script (sp)
#if !HAVE_MEMCHR
char *
memchr (s, c, n)
- char *s;
+ char const *s;
int c;
size_t n;
{
- unsigned char *p = (unsigned char *) s, *lim = p + n;
+ unsigned char const *p = (unsigned char const *) s, *lim = p + n;
for (; p < lim; p++)
if (*p == c)
return (char *) p;
diff --git a/gnu/usr.bin/diff/version.c b/gnu/usr.bin/diff/version.c
index cb9d3b9..234ec29 100644
--- a/gnu/usr.bin/diff/version.c
+++ b/gnu/usr.bin/diff/version.c
@@ -1,3 +1,5 @@
/* Version number of GNU diff. */
-char *version_string = "2.3";
+#include "config.h"
+
+char const version_string[] = "2.6";
diff --git a/gnu/usr.bin/diff3/Makefile b/gnu/usr.bin/diff3/Makefile
index 7510536..3d2b422 100644
--- a/gnu/usr.bin/diff3/Makefile
+++ b/gnu/usr.bin/diff3/Makefile
@@ -1,9 +1,8 @@
-PROG= diff3
-SRCS= diff3.c getopt.c getopt1.c version.c
-CFLAGS+=-DDIRENT=1 -DHAVE_UNISTD_H=1 -DHAVE_DUP2=1 -DHAVE_MEMCHR=1 \
- -DHAVE_STRERROR=1 -DHAVE_WAITPID=1 -DHAVE_FCNTL_H=1 \
- -DHAVE_STRING_H=1 -DHAVE_SYS_WAIT_H=1 -DHAVE_TIME_H=1 \
- -DHAVE_ST_BLKSIZE=1 -DDIFF_PROGRAM=\"/usr/bin/diff\"
-NOMAN=noman
+PROG= diff3
+SRCS= diff3.c getopt.c getopt1.c version.c
+CFLAGS+= -I$(.CURDIR)/../diff -DHAVE_CONFIG_H \
+ -DDIFF_PROGRAM=\"/usr/bin/diff\"
+MAN= diff3.1
.include <bsd.prog.mk>
+.PATH: $(.CURDIR)/../diff
diff --git a/gnu/usr.bin/diff3/diff3.1 b/gnu/usr.bin/diff3/diff3.1
new file mode 100644
index 0000000..b190328
--- /dev/null
+++ b/gnu/usr.bin/diff3/diff3.1
@@ -0,0 +1,207 @@
+.TH DIFF3 1 "22sep1993" "GNU Tools" "GNU Tools"
+.SH NAME
+diff3 \- find differences between three files
+.SH SYNOPSIS
+.B diff3
+[options] mine older yours
+.SH DESCRIPTION
+The
+.I diff3
+command compares three files and outputs descriptions
+of their differences.
+
+The files to compare are
+.IR mine ,
+.IR older ,
+and
+.IR yours .
+At most one of these three file names may be
+.BR \- ,
+which tells
+.I diff3
+to read the standard input for that file.
+.SS Options
+Below is a summary of all of the options that GNU
+.I diff3
+accepts. Multiple single letter options (unless they take an argument)
+can be combined into a single command line argument.
+.TP
+.B \-a
+Treat all files as text and compare them line-by-line, even if they
+do not appear to be text.
+.TP
+.B \-A
+Incorporate all changes from
+.I older
+to
+.I yours
+into
+.IR mine ,
+surrounding all conflicts with bracket lines.
+.TP
+.B \-e
+Generate an
+.I ed
+script that incorporates all the changes from
+.I older
+to
+.I yours
+into
+.IR mine .
+.TP
+.B \-E
+Like
+.BR \-e ,
+except bracket lines from overlapping changes' first
+and third files.
+With
+.BR \-e ,
+an overlapping change looks like this:
+.sp
+.nf
+<<<<<<< \fImine\fP
+lines from \fImine\fP
+=======
+lines from \fIyours\fP
+>>>>>>> \fIyours\fP
+.fi
+.TP
+.B \-\-ed
+Generate an
+.I ed
+script that incorporates all the changes from
+.I older
+to
+.I yours
+into
+.IR mine .
+.TP
+.B \-\-easy\-only
+Like
+.BR \-e ,
+except output only the nonoverlapping changes.
+.TP
+.B \-i
+Generate
+.B w
+and
+.B q
+commands at the end of the
+.I ed
+script for System V compatibility. This option must be combined with
+one of the
+.B \-AeExX3
+options, and may not be combined with
+.BR \-m .
+.TP
+.B \-\-initial\-tab
+Output a tab rather than two spaces before the text of a line in normal format.
+This causes the alignment of tabs in the line to look normal.
+.TP
+.BI "\-L " label
+.ns
+.TP
+.BI \-\-label= label
+Use the label
+.I label
+for the brackets output by the
+.BR \-A ,
+.B \-E
+and
+.B \-X
+options. This option may be given up to three
+times, one for each input file. The default labels are the names of
+the input files. Thus
+.B "diff3 \-L X \-L Y \-L Z \-m A B C"
+acts like
+.BR "diff3 \-m A B C ,
+except that the output looks like it came from
+files named
+.BR X ,
+.B Y
+and
+.B Z
+rather than from files
+named
+.BR A ,
+.B B
+and
+.BR C .
+.TP
+.B \-m
+.br
+.ns
+.TP
+.B \-\-merge
+Apply the edit script to the first file and send the result to standard
+output. Unlike piping the output from
+.I diff3
+to
+.IR ed ,
+this
+works even for binary files and incomplete lines.
+.B \-A
+is assumed
+if no edit script option is specified.
+.TP
+.B \-\-overlap\-only
+Like
+.BR \-e ,
+except output only the overlapping changes.
+.TP
+.B \-\-show\-all
+Incorporate all unmerged changes from
+.I older
+to
+.I yours
+into
+.IR mine ,
+surrounding all overlapping changes with bracket lines.
+.TP
+.B \-\-show\-overlap
+Like
+.BR \-e ,
+except bracket lines from overlapping changes' first
+and third files.
+.TP
+.B \-T
+Output a tab rather than two spaces before the text of a line in normal format.
+This causes the alignment of tabs in the line to look normal.
+.TP
+.B \-\-text
+Treat all files as text and compare them line-by-line, even if they
+do not appear to be text.
+.TP
+.B \-v
+.br
+.ns
+.TP
+.B \-\-version
+Output the version number of
+.IR diff3 .
+.TP
+.B \-x
+Like
+.BR \-e ,
+except output only the overlapping changes.
+.TP
+.B \-X
+Like
+.BR \-E ,
+except output only the overlapping changes.
+In other words, like
+.BR \-x ,
+except bracket changes as in
+.BR \-E .
+.TP
+.B \-3
+Like
+.BR \-e ,
+except output only the nonoverlapping changes.
+.SH SEE ALSO
+cmp(1), comm(1), diff(1), ed(1), patch(1), sdiff(1).
+.SH DIAGNOSTICS
+An exit status of 0 means
+.I diff3
+was successful, 1 means some
+conflicts were found, and 2 means trouble.
diff --git a/gnu/usr.bin/gdb/COPYING b/gnu/usr.bin/gdb/COPYING
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/gnu/usr.bin/gdb/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/gnu/usr.bin/gdb/COPYING.LIB b/gnu/usr.bin/gdb/COPYING.LIB
new file mode 100644
index 0000000..eb685a5
--- /dev/null
+++ b/gnu/usr.bin/gdb/COPYING.LIB
@@ -0,0 +1,481 @@
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/gnu/usr.bin/gdb/ChangeLog b/gnu/usr.bin/gdb/ChangeLog
new file mode 100644
index 0000000..1f2342b
--- /dev/null
+++ b/gnu/usr.bin/gdb/ChangeLog
@@ -0,0 +1,4887 @@
+Thu Feb 8 01:11:55 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu)
+
+ * GDB 3.5 released.
+
+ * version.c: Change version number to 3.5
+
+Tue Feb 6 15:58:06 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu)
+
+ * m-hp9k320.h: define ATTACH_DETACH.
+ hp9k320-dep.c [ATTACH_DETACH]: New code.
+
+Thu Feb 1 17:43:00 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu)
+
+ * valprint.c (is_nan, val_print): Use char * not void *.
+
+ * symmisc.c (print_symbol): Print newline after label.
+
+Tue Jan 30 15:35:52 1990 Jim Kingdon (kingdon at albert.ai.mit.edu)
+
+ * Makefile.dist (READLINE): Add {readline,history}.texinfo.
+
+ * m-merlin.h: Put in clarifying comments about SHELL_FILE.
+ config.gdb (merlin): Explain about /usr/local/lib/gdb-sh.
+
+Sat Jan 27 02:30:27 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu)
+
+ * version.c: Change version number to 3.5alpha.1.
+
+ * dbxread.c (process_one_symbol): Compare context_stack_depth
+ with !VARIABLES_INSIDE_BLOCK, not VARIABLES_INSIDE_BLOCK.
+
+Fri Jan 26 01:21:51 1990 Jim Kingdon (kingdon at mole.ai.mit.edu)
+
+ * main.c [ALIGN_STACK_ON_STARTUP]: New code.
+ m-i386.h: Define ALIGN_STACK_ON_STARTUP.
+
+ * m-merlin.h (NO_SIGINTERRUPT, SHELL_FILE): Define.
+
+ * umax-dep.c (exec_file_command): Add commas to call to
+ read_section_hdr.
+
+Tue Jan 23 15:49:47 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu)
+
+ * dbxread.c (define_symbol): Deal with deftype 'X'.
+
+ * convex-dep.c (wait): Make it pid_t.
+
+ * convex-dep.c (comm_registers_info): accept decimal comm register
+ specification, as "i comm 32768".
+
+ * dbxread.c (process_one_symbol): Make VARIABLES_INSIDE_BLOCK
+ macro say by itself where variables are. Pass it desc.
+ m-convex.h (VARIABLES_INSIDE_BLOCK): Nonzero for native compiler.
+
+ * m-convex.h (SET_STACK_LIMIT_HUGE): Define.
+ (IGNORE_SYMBOL): Take out #ifdef N_MONPT and put in 0xc4.
+
+Fri Jan 19 20:04:15 1990 Jim Kingdon (kingdon at albert.ai.mit.edu)
+
+ * printcmd.c (print_frame_args): Always set highest_offset to
+ current_offset when former is -1.
+
+ * dbxread.c (read_struct_type): Print nice error message
+ when encountering multiple inheritance.
+
+Thu Jan 18 13:43:30 1990 Jim Kingdon (kingdon at mole.ai.mit.edu)
+
+ * dbxread.c (read_dbx_symtab): Always treat N_FN as a potential
+ source for a x.o or -lx symbol, ignoring OFILE_FN_FLAGGED.
+
+ * printcmd.c (print_frame_args): Cast -1 to (CORE_ADDR).
+
+ * hp300bsd-dep.c (_initialize_hp300_dep): Get kernel_u_addr.
+ m-hp300bsd.h (KERNEL_U_ADDR): Use kernel_u_addr.
+
+ * infcmd.c (run_command): #if 0 out call to
+ breakpoint_clear_ignore_counts.
+
+Thu Jan 11 12:58:12 1990 Jim Kingdon (kingdon at mole)
+
+ * printcmd.c (print_frame_args) [STRUCT_ARG_SYM_GARBAGE]:
+ Try looking up name of var before giving up & printing '?'.
+
+Wed Jan 10 14:00:14 1990 Jim Kingdon (kingdon at pogo)
+
+ * many files: Move stdio.h before param.h.
+
+ * sun3-dep.c (store_inferior_registers): Only try to write FP
+ regs #ifdef FP0_REGNUM.
+
+Mon Jan 8 17:56:15 1990 Jim Kingdon (kingdon at pogo)
+
+ * symtab.c: #if 0 out "info methods" code.
+
+Sat Jan 6 12:33:04 1990 Jim Kingdon (kingdon at pogo)
+
+ * dbxread.c (read_struct_type): Set TYPE_NFN_FIELDS_TOTAL
+ from all baseclasses; remove vestigial variable baseclass.
+
+ * findvar.c (read_var_value): Check REG_STRUCT_HAS_ADDR.
+ printcmd.c (print_frame_args): Check STRUCT_ARG_SYM_GARBAGE.
+ m-sparc.h: Define REG_STRUCT_HAS_ADDR and STRUCT_ARG_SYM_GARBAGE.
+
+ * blockframe.c (get_frame_block): Subtract one from pc if not
+ innermost frame.
+
+Fri Dec 29 15:26:33 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * printcmd.c (print_frame_args): check highest_offset != -1, not i.
+
+Thu Dec 28 16:21:02 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * valops.c (value_struct_elt): Clean up error msg.
+
+ * breakpoint.c (describe_other_breakpoints):
+ Delete extra space before "also set at" and add period at end.
+
+Tue Dec 19 10:28:42 1989 Jim Kingdon (kingdon at pogo)
+
+ * source.c (print_source_lines): Tell user which line number
+ was out of range when printing error message.
+
+Sun Dec 17 14:14:09 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * blockframe.c (find_pc_partial_function): Use
+ BLOCK_START (SYMBOL_BLOCK_VALUE (f)) instead of
+ SYMBOL_VALUE (f) to get start of function.
+
+ * dbxread.c: Make xxmalloc just a #define for xmalloc.
+
+Thu Dec 14 16:13:16 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * m68k-opcode.h (fseq & following fp instructions):
+ Change @ to $.
+
+Fri Dec 8 19:06:44 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * breakpoint.c (breakpoint_clear_ignore_counts): New function.
+ infcmd.c (run_command): Call it.
+
+Wed Dec 6 15:03:38 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * valprint.c: Change it so "array-max 0" means there is
+ no limit.
+
+ * expread.y (yylex): Change error message "invalid token in
+ expression" to "invalid character '%c' in expression".
+
+Mon Dec 4 16:12:54 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * blockframe.c (find_pc_partial_function): Always return 1
+ for success, 0 for failure, and set *NAME and *ADDRESS to
+ match the return value.
+
+ * dbxread.c (symbol_file_command): Use perror_with_name on
+ error from stat.
+ (psymtab_to_symtab, add_file_command),
+ core.c (validate_files), source.c (find_source_lines),
+ default-dep.c (exec_file_command): Check for errors from stat,
+ fstat, and myread.
+
+Fri Dec 1 05:16:42 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * valops.c (check_field): When following pointers, just get
+ their types; don't call value_ind.
+
+Thu Nov 30 14:45:29 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * config.gdb (pyr): New machine.
+ core.c [REG_STACK_SEGMENT]: New code.
+ dbxread.c (process_one_symbol): Cast return from copy_pending
+ to long before casting to enum namespace.
+ infrun.c: Split registers_info into DO_REGISTERS_INFO
+ and registers_info.
+ m-pyr.h, pyr-{dep.c,opcode.h,pinsn.c}: New files.
+
+ * hp300bsd-dep.c: Stay in sync with default-dep.c.
+
+ * m-hp300bsd.h (IN_SIGTRAMP): Define.
+
+Mon Nov 27 23:48:21 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * m-sparc.h (EXTRACT_RETURN_VALUE, STORE_RETURN_VALUE):
+ Return floating point values in %f0.
+
+Tue Nov 21 00:34:46 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * dbxread.c (read_type): #if 0 out code which skips to
+ comma following x-ref.
+
+Sat Nov 18 20:10:54 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * valprint.c (val_print): Undo changes of Nov 11 & 16.
+ (print_string): Add parameter force_ellipses.
+ (val_print): Pass force_ellipses true when we stop fetching string
+ before we get to the end, else pass false.
+
+Thu Nov 16 11:59:50 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * infrun.c (restore_inferior_status): Don't try to restore
+ selected frame if the inferior no longer exists.
+
+ * valprint.c (val_print): Rewrite string printing code not to
+ call print_string.
+
+ * Makefile.dist (clean): Remove xgdb and xgdb.o.
+
+Tue Nov 14 12:41:47 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * Makefile.dist (XGDB, bindir, xbindir, install, all): New stuff.
+
+Sat Nov 11 15:29:38 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * valprint.c (val_print): chars_to_get: New variable.
+
+Thu Nov 9 12:31:47 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * main.c (main): Process "-help" as a switch that doesn't
+ take an argument.
+
+Wed Nov 8 13:07:02 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * Makefile.dist (gdb.tar.Z): Add "else true".
+
+Tue Nov 7 12:25:14 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * infrun.c (restore_inferior_status): Don't dereference fid if NULL.
+
+ * config.gdb (sun3, sun4): Accept "sun3" and "sun4".
+
+Mon Nov 6 09:49:23 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * Makefile.dist (Makefile): Move comments after commands.
+
+ * *-dep.c [READ_COFF_SYMTAB]: Pass optional header size to
+ read_section_hdr().
+
+ * inflow.c: Include <fcntl.h> regardless of USG.
+
+ * coffread.c (read_section_hdr): Add optional_header_size.
+ (symbol_file_command): Pass optional header size to
+ read_section_hdr().
+ (read_coff_symtab): Initialize filestring.
+
+ * version.c: Change version to 3.4.xxx.
+
+ * GDB 3.4 released.
+
+Sun Nov 5 11:39:01 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * version.c: Change version to 3.4.
+
+ * symtab.c (decode_line_1): Only skip past "struct" if it
+ is there.
+
+ * valops.c (value_ind), eval.c (evaluate_subexp, case UNOP_IND):
+ Have "*" <int-valued-exp> return an int, not a LONGEST.
+
+ * utils.c (fprintf_filtered): Pass arg{4,5,6} to sprintf.
+
+ * printcmd.c (x_command): Use variable itself rather
+ than treating it as a pointer only if it is a function.
+ (See comment "this makes x/i main work").
+
+ * coffread.c (symbol_file_command): Use error for
+ "%s does not have a symbol-table.\n".
+
+Wed Nov 1 19:56:18 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * dbxread.c [BELIEVE_PCC_PROMOTION_TYPE]: New code.
+ m-sparc.h: Define BELIEVE_PCC_PROMOTION_TYPE.
+
+Thu Oct 26 12:45:00 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * infrun.c: Include <sys/dir.h>.
+
+ * dbxread.c (read_dbx_symtab, case N_LSYM, case 'T'):
+ Check for enum types and put constants in psymtab.
+
+Mon Oct 23 15:02:25 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * dbxread.c (define_symbol, read_dbx_symtab): Handle enum
+ constants (e.g. "b:c=e6,0").
+
+Thu Oct 19 14:57:26 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * stack.c (frame_info): Use FRAME_ARGS_ADDRESS_CORRECT
+ m-vax.h (FRAME_ARGS_ADDRESS_CORRECT): New macro.
+ (FRAME_ARGS_ADDRESS): Restore old meaning.
+
+ * frame.h (Frame_unknown): New macro.
+ stack.c (frame_info): Check for Frame_unknown return from
+ FRAME_ARGS_ADDRESS.
+ m-vax.h (FRAME_ARGS_ADDRESS): Sometimes return Frame_unknown.
+
+ * utils.c (fatal_dump_core): Add "internal error" to message.
+
+ * infrun.c (IN_SIGTRAMP): New macro.
+ (wait_for_inferior): Use IN_SIGTRAMP.
+ m-vax.h (IN_SIGTRAMP): New macro.
+
+Wed Oct 18 15:09:22 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * config.gdb, Makefile.dist: Shorten m-i386-sv32.h.
+
+ * coffread.c (symbol_file_command): Pass 0 to select_source_symtab.
+
+Tue Oct 17 12:24:41 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * i386-dep.c (i386_frame_num_args): Take function from m-i386.h
+ file. Check for pfi null.
+ m-i386.h (FRAME_NUM_ARGS): Use i386_frame_num_args.
+
+ * infrun.c (wait_for_inferior): set stop_func_name to 0
+ before calling find_pc_partial_function.
+
+Thu Oct 12 01:08:50 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * breakpoint.c (_initialize_breakpoint): Add "disa".
+
+ * Makefile.dist: Add GLOBAL_CFLAGS and pass to readline.
+
+ * config.gdb (various): "$machine =" -> "machine =".
+
+Wed Oct 11 11:54:31 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * inflow.c (try_writing_regs): #if 0 out this function.
+
+ * main.c (main): Add "-help" option.
+
+ * dbxread.c (read_dbx_symtab): Merge code for N_FUN with
+ N_STSYM, etc.
+
+Mon Oct 9 14:21:55 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * inflow.c (try_writing_regs_command): Don't write past end
+ of struct user.
+
+ * dbxread.c (read_struct_type): #if 0 out code which checks for
+ bitpos and bitsize 0.
+
+ * config.gdb: Accept sequent-i386 (not seq386).
+ (symmetry): Set depfile and paramfile.
+
+ * m-convex.h (IGNORE_SYMBOL): Check for N_MONPT if defined.
+
+Thu Oct 5 10:14:26 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * default-dep.c (read_inferior_memory): Put #if 0'd out comment
+ within /* */.
+
+Wed Oct 4 18:44:41 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * config.gdb: Change /dev/null to m-i386.h for various
+ 386 machine "opcodefile" entries.
+
+ * config.gdb: Accept seq386 for sequent symmetry.
+
+Mon Oct 2 09:59:50 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * hp300bsd-dep.c: Fix copyright notice.
+
+Sun Oct 1 16:25:30 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * Makefile.dist (DEPFILES): Add isi-dep.c.
+
+ * default-dep.c (read_inferior_memory): Move #endif after else.
+
+Sat Sep 30 12:50:16 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * version.c: Change version number to 3.3.xxx.
+
+ * GDB 3.3 released.
+
+ * version.c: Change version number to 3.3.
+
+ * Makefile.dist (READLINE): Add vi_mode.c
+
+ * config.gdb (i386): Change /dev/null to m-i386.h
+
+ * config.gdb: Add ';;' before 'esac'.
+
+ * Makefile.dist (gdb.tar.Z): Move comment above dependency.
+
+ * dbxread.c (read_ofile_symtab): Check symbol before start
+ of source file for GCC_COMPILED_FLAG_SYMBOL.
+ (start_symtab): Don't clear processing_gcc_compilation.
+
+Thu Sep 28 22:30:23 1989 Roland McGrath (roland at hobbes.ai.mit.edu)
+
+ * valprint.c (print_string): If LENGTH is zero, print "".
+
+Wed Sep 27 10:15:10 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * config.gdb: "rm tmp.c" -> "rm -f tmp.c".
+
+Tue Sep 26 13:02:10 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * utils.c (_initialize_utils): Use termcap to set lines_per_page
+ and chars_per_line.
+
+Mon Sep 25 10:06:43 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * dbxread.c (read_dbx_symtab, N_SOL): Do not add the same file
+ more than once.
+
+Thu Sep 21 12:43:18 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * infcmd.c (unset_environment_command): Delete all variables
+ if called with no arg.
+
+ * remote.c, inferior.h (remote_{read,write}_inferior_memory):
+ New functions.
+ core.c ({read,write}_memory): Use remote_{read,write}_inferior_memory.
+
+ * valops.c (call_function): When reserving stack space for
+ arguments, call value_arg_coerce.
+
+ * m-hp9k320.h: define BROKEN_LARGE_ALLOCA.
+
+ * breakpoint.c (delete_command): Ask for confirmation only
+ when there are breakpoints.
+
+ * dbxread.c (read_struct_type): If lookup_basetype_type has
+ copied a stub type, call add_undefined_type.
+
+ * sparc_pinsn.c (compare_opcodes): Check for "1+i" anywhere
+ in args.
+
+ * val_print.c (type_print_base): Print stub types as
+ "<incomplete type>".
+
+Wed Sep 20 07:32:00 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * sparc-opcode.h (swapa): Remove i bit from match.
+ (all alternate space instructions): Delete surplus "foo rs1+0"
+ patterns.
+
+ * Makefile.dist (LDFLAGS): Set to $(CFLAGS).
+
+ * remote-multi.shar (remote_utils.c, putpkt): Change csum to unsigned.
+
+Tue Sep 19 14:15:16 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * sparc-opcode.h: Set i bit in lose for many instructions which
+ aren't immediate.
+
+ * stack.c (print_frame_info): add "func = 0".
+
+Mon Sep 18 16:19:48 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * sparc-opcode.h (mov): Add mov to/from %tbr, %psr, %wim.
+
+ * sparc-opcode.h (rett): Fix notation to use suggested assembler
+ syntax from architecture manual.
+
+ * symmetry-dep.c (I386_REGNO_TO_SYMMETRY): New macro.
+ (i386_frame_find_saved_regs): Use I386_REGNO_TO_SYMMETRY.
+
+Sat Sep 16 22:21:17 1989 Jim Kingdon (kingdon at spiff)
+
+ * remote.c (remote_close): Set remote_desc to -1.
+
+ * gdb.texinfo (Output): Fix description of echo to match
+ reality and ANSI C.
+
+Fri Sep 15 14:28:59 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * symtab.c (lookup_symbol): Add comment about "asm".
+
+ * sparc-pinsn.c: Use NUMOPCODES.
+
+ * sparc-opcode.h (NUMOPCODES): Use sparc_opcodes[0] not *sparc_opcodes.
+
+Thu Sep 14 15:25:20 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * dbxread.c (xxmalloc): Print error message before calling abort().
+
+ * infrun.c (wait_for_inferior): Check for {stop,prev}_func_name
+ null before passing to strcmp.
+
+Wed Sep 13 12:34:15 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * sparc-opcode.h: New field delayed.
+ sparc-pinsn.c (is_delayed_branch): New function.
+ (print_insn): Check for delayed branches.
+
+ * stack.c (print_frame_info): Use misc_function_vector in
+ case where ar truncates file names.
+
+Tue Sep 12 00:16:14 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * convex-dep.c (psw_info): Move "struct pswbit *p" with declarations.
+
+Mon Sep 11 14:59:57 1989 Jim Kingdon (kingdon at spiff)
+
+ * convex-dep.c (core_file_command): Delete redundant printing
+ of "Program %s".
+
+ * m-convex.h (ENTRY_POINT): New macro.
+
+ * m-convex.h (FRAME_CHAIN_VALID): Change outside_first_object_file
+ to outside_startup_file
+
+ * main.c: #if 0 out catch_termination and related code.
+
+ * command.c (lookup_cmd_1): Consider underscores part of
+ command names.
+
+Sun Sep 10 09:20:12 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * printcmd.c: Change asdump_command to disassemble_command
+ (_initialize_printcmd): Change asdump to diassemble.
+
+ * main.c (main): Exit with code 0 if we hit the end of a batch
+ file.
+
+ * Makefile.dist (libreadline.a): Fix syntax of "CC=${CC}".
+
+Sat Sep 9 01:07:18 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * values.c (history_info): Renamed to value_history_info.
+ Command renamed to "info value" (with "info history" still
+ accepted).
+
+ * sparc-pinsn.c (print_insn): Extend symbolic address printing
+ to cover "sethi" following by an insn which uses 1+i.
+
+Fri Sep 8 14:24:01 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * m-hp9k320.h, m-hp300bsd.h, m-altos.h, m-sparc.h, m-sun3.h
+ (READ_GDB_SYMSEGS): Remove.
+ dbxread.c [READ_GDB_SYMSEGS]: Remove code to read symsegs.
+
+ * sparc-pinsn.c (print_insn): Detect "sethi-or" pairs and
+ print symbolic address.
+
+ * sparc-opcode.h (sethi, set): Change lose from 0xc0000000 to
+ 0xc0c00000000.
+
+ * remote.c (remote_desc): Initialize to -1.
+
+ * Makefile.dist (libreadline.a): Pass CC='${CC}' to readline makefile.
+
+Thu Sep 7 00:07:17 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * dbxread.c (read_struct_type): Check for static member functions.
+ values.c, eval.c, valarith.c, valprint.c, valops.c: Merge changes
+ from Tiemann for static member functions.
+
+ * sparc-opcode.h (tst): Fix all 3 patterns.
+
+ * Makefile.dist (gdb1): New rule.
+
+ * sparc-opcode.h: Change comment about what the disassembler
+ does with the order of the opcodes.
+
+ * sparc-pinsn.c (compare_opcodes): Put 1+i before i+1.
+ Also fix mistaken comment about preserving order of original table.
+
+ * sparc-opcode.h (clr, mov): Fix incorrect lose entries.
+
+ * m-symmetry.h (FRAME_NUM_ARGS): Add check to deal with code that
+ GCC sometimes generates.
+
+ * config.gdb: Change all occurances of "skip" to "/dev/null".
+
+ * README (about languages other than C): Update comments about
+ Pascal and FORTRAN.
+
+ * sparc-opcode.h (nop): Change lose from 0xae3fffff to 0xfe3fffff.
+
+ * values.c (value_virtual_fn_field): #if 0-out assignment to
+ VALUE_TYPE(vtbl).
+
+Wed Sep 6 12:19:22 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * utils.c (fatal_dump_core): New function.
+ Makefile.dist (MALLOC_FLAGS): use -Dbotch=fatal_dump_core
+
+Tue Sep 5 15:47:18 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * breakpoint.c (enable_command): With no arg, enable all bkpts.
+
+ * Makefile.dist (Makefile): Remove \"'s around $(MD).
+
+ * Makefile.dist: In "cd readline; make . . ." change first
+ SYSV_DEFINE to SYSV.
+
+ * m68k-pinsn.c (_initialize_pinsn): Use alternate assembler
+ syntax #ifdef HPUX_ASM
+
+Sat Sep 2 23:24:43 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * values.c (history_info): Don't check num_exp[0] if num_exp
+ is nil (just like recent editing_info change).
+
+Fri Sep 1 19:19:01 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * gdb.texinfo (inc-history, inc-readline): Copy in the inc-* files
+ because people might not have makeinfo.
+
+ * README (xgdb): Strengthen nasty comments.
+
+ * gdb.texinfo: Change @setfilename to "gdb.info".
+
+Thu Aug 31 17:23:50 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * main.c (editing_info): Don't check arg[0] if arg is null.
+
+ * m-vax.h: Add comment about known sigtramp bug.
+
+ * sun3-dep.c, sparc-dep.c (IS_OBJECT_FILE, exec_file_command):
+ Get right text & data addresses for .o files.
+
+Wed Aug 30 13:54:19 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * utils.c (tilde_expand): Remove function (it's in readline).
+
+ * sparc-opcode.h (call): Change "8" to "9" in first two
+ patterns (%g7->%o7).
+
+Tue Aug 29 16:44:41 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * printcmd.c (whatis_command): Change 4th arg to type_print
+ from 1 to -1.
+
+Mon Aug 28 12:22:41 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * dbxread.c (psymtab_to_symtab_1): In "and %s ..." change
+ pst->filename to pst->dependencies[i]->filename.
+
+ * blockframe.c (FRAMELESS_LOOK_FOR_PROLOGUE): New macro
+ made from FRAMELESS_FUNCTION_INVOCATION from m-sun3.h except
+ that it checks for zero return from get_pc_function_start.
+ m-hp9k320.h, m-hp300bsd.h, m-i386.h, m-isi.h, m-altos.h,
+ m-news.h, m-sparc.h, m-sun2.h, m-sun3.h, m-symmetry.h
+ (FRAMELESS_FUNCTION_INVOCATION): Use FRAMELESS_LOOK_FOR_PROLOGUE.
+
+ * dbxread.c (read_struct_type): Give warning and ignore field
+ if bitpos and bitsize are zero.
+
+Sun Aug 27 04:55:20 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * dbxread.c (psymtab_to_symtab{,_1}): Print message about
+ reading in symbols before reading stringtab, not after.
+
+Sat Aug 26 02:01:53 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * dbxread.c (IS_OBJECT_FILE, ADDR_OF_TEXT_SEGMENT): New macros.
+ (read_dbx_symtab): Use text_addr & text_size to set end_of_text_addr.
+ (symbol_file_command): pass text_addr & text_size to read_dbx_symtab.
+
+Fri Aug 25 23:08:13 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * valprint.c (value_print): Try to give the name of function
+ pointed to when printing a function pointer.
+
+Thu Aug 24 23:18:40 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * core.c (xfer_core_file): In cases where MEMADDR is above the
+ largest address that makes sense, set i to len.
+
+Thu Aug 24 16:04:17 1989 Roland McGrath (roland at hobbes.ai.mit.edu)
+
+ * valprint.c (print_string): New function to print a character
+ string, doing array-max limiting and repeat count processing.
+ (val_print, value_print): Use print_string.
+ (REPEAT_COUNT_THRESHOLD): New #define, the max number of elts to print
+ without using a repeat count. Set to ten.
+ (value_print, val_print): Use REPEAT_COUNT_THRESHOLD.
+
+ * utils.c (printchar): Use {fputs,fprintf}_filtered.
+
+ * valprint.c (val_print): Pass the repeat count arg to the
+ fprintf_filtered call for "<repeats N times>" messages.
+
+Wed Aug 23 22:53:47 1989 Roland McGrath (roland at hobbes.ai.mit.edu)
+
+ * utils.c: Include <pwd.h>.
+
+ * main.c: Declare free.
+
+Wed Aug 23 05:05:59 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * utils.c, defs.h: Add tilde_expand.
+ source.c (directory_command),
+ main.c (cd_command),
+ main.c (set_history_filename),
+ dbxread.c (symbol_file_command),
+ coffread.c (symbol_file_command),
+ dbxread.c (add_file_command),
+ symmisc.c (print_symtabs),
+ *-dep.c (exec_file_command, core_file_command),
+ main.c (source_command): Use tilde_expand.
+
+ * dbxread.c (read_type): When we get a cross-reference, resolve
+ it immediately if possible, only calling add_undefined_type if
+ necessary.
+
+ * gdb.texinfo: Uncomment @includes and put comment at start
+ of file telling people to use makeinfo.
+
+ * valprint.c (type_print_base): Print the right thing for
+ bitfields.
+
+ * config.gdb (sun3os3): Set paramfile and depfile.
+
+Tue Aug 22 05:38:36 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * dbxread.c (symbol_file_command): Pass string table size to
+ read_dbx_symtab().
+ (read_dbx_symtab): Before indexing into string table, check
+ string table index for reasonableness.
+ (psymtab_to_symtab{,_1}, read_ofile_symtab): Same.
+
+Tue Aug 22 04:04:39 1989 Roland McGrath (roland at hobbes.ai.mit.edu)
+
+ * m68k-pinsn.c: Replaced many calls to fprintf and fputs with
+ calls to fprintf_filtered and fputs_filtered.
+ (print_insn_arg): Use normal MIT 68k syntax for postincrement,
+ predecrement, and register indirect addressing modes.
+
+Mon Aug 21 10:08:02 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * main.c (initialize_signals): Set signal handler for SIGQUIT
+ and SIGHUP to do_nothing.
+
+ * ns32k-opcode.h (ord): Change 1D1D to 1D2D.
+
+ * ns32k-pinsn.c (print_insn_arg, print_insn): Handle index
+ bytes correctly.
+
+ * ns32k-opcode.h: Add comments.
+
+ * dbxread.c (read_type): Put enum fields in type.fields in order
+ that they were found in the debugging symbols (not reverse order).
+
+Sun Aug 20 21:17:13 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * main.c (source_command): Read .gdbinit if run without argument.
+
+ * source.c (directory_command): Only print "foo already in path"
+ if from_tty.
+
+ * version.c: Change version number to 3.2.xxx
+
+Sat Aug 19 00:24:08 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * m-news.h: Define HAVE_WAIT_STRUCT.
+
+ * m-isi.h, isi-dep.c: Replace with new version from Adam de Boor.
+ config.gdb: Remove isibsd43.
+
+ * main.c (catch_termination): Don't say we have written
+ .gdb_history until after we really have.
+
+ * convex-dep.c (attach): Add "sleep (1)".
+ (write_vector_register): Use "LL" with long long constant.
+ (wait): Close comment.
+ (wait): Change "unix 7.1 bug" to "unix 7.1 feature" & related
+ changes in comment.
+ (scan_stack): And fp with 0x80000000 in while loop test.
+ (core_file_command): Move code to set COREFILE.
+ (many places): Change printf to printf_filtered.
+ (psw_info): Allow argument giving value to print as a psw.
+ (_initialize_convex_dep): Update docstrings.
+
+ * m-convex.h (WORDS_BIG_ENDIAN): Correct typo ("WRODS")
+ define NO_SIGINTERRUPT.
+ define SET_STACK_LIMIT_HUGE.
+ add "undef BUILTIN_TYPE_LONGEST" before defining it.
+ Use "LL" after constants in CALL_DUMMY.
+
+ * dbxread.c: In the 3 places it says error "ridiculous string
+ table size"... delete extra parameter to error.
+
+ * dbxread.c (scan_file_globals): Check for FORTRAN common block.
+ Allow multiple references for the sake of common blocks.
+
+ * main.c (initialize_main): Set history_filename to include
+ current directory.
+
+ * valprint.c (decode_format): Don't return a defaulted size
+ field if osize is zero.
+
+ * gdb.texinfo (Compilation): Update information on -gg symbols.
+ Document problem with ar.
+
+Fri Aug 18 19:45:20 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * valprint.c (val_print, value_print): Add "<repeats %d times>" code.
+ Also put "..." outside quotes for strings.
+
+ * main.c (initialize_main): Add comment about history output file
+ being different from history input file.
+
+ * m-newsos3.h: Undefine NO_SIGINTERRUPT. Rearrange a few comments.
+
+ * m-newsos3.h (REGISTER_U_ADDR): Use new version from Hikichi.
+
+ * sparc-opcode.h: Add comment clarifying meaning of the order of
+ the entries in sparc_opcodes.
+
+ * eval.c (evaluate_subexp, case UNOP_IND): Deal with deferencing
+ things that are not pointers.
+
+ * valops.c (value_ind): Make dereferencing an int give a LONGEST.
+
+ * expprint.c (print_subexp): Add (int) cast in OP_LAST case.
+
+ * dbxread.c (read_array_type): Set lower and upper if adjustable.
+
+ * symtab.c (lookup_symbol): Don't abort if symbol found in psymtab
+ but not in symtab.
+
+Thu Aug 17 15:51:20 1989 Randy Smith (randy at hobbes.ai.mit.edu)
+
+ * config.gdb: Changed "Makefile.c" to "Makefile.dist".
+
+Thu Aug 17 01:58:04 1989 Roland McGrath (roland at apple-gunkies.ai.mit.edu)
+
+ * sparc-opcode.h (or): Removed incorrect lose bit 0x08000000.
+ [many]: Changed many `lose' entries to have the 0x10 bit set, so
+ they don't think %l0 is %g0.
+
+Wed Aug 16 00:30:44 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * m-symmetry.h (STORE_STRUCT_RETURN): Also write reg 0.
+ (EXTRACT_RETURN_VALUE): Call symmetry_extract_return_value.
+ symmetry-dep.c (symmetry_extract_return_value): New fn.
+
+ * main.c (symbol_completion_function): Deal with changed
+ result_list from lookup_cmd_1 for ambiguous return.
+ command.c (lookup_cmd): Same.
+
+ * inflow.c [TIOCGETC]: Move #include "param.h" back before
+ system #includes. Change all #ifdef TIOCGETC to
+ #if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN)
+ m-i386-sysv3.2.h, m-i386gas-sysv3.2.h: Remove "#undef TIOCGETC"
+ and add "#define TIOCGETC_BROKEN".
+
+ * command.c (lookup_cmd_1): Give the correct result_list in the
+ case of an ambiguous return where there is a partial match
+ (e.g. "info a"). Add comment clarifying what is the correct
+ result_list.
+
+ * gdb.texinfo (GDB History): Document the two changes below.
+
+ * main.c (command_line_input): Make history expansion not
+ just occur at the beginning of a line.
+
+ * main.c (initialize_main): Make history expansion off by default.
+
+ * inflow.c: Move #include "param.h" after system #includes.
+
+ * i386-dep.c (i386_float_info): Use U_FPSTATE macro.
+
+ * m-i386-sysv3.2.h, m-i386gas-sysv3.2.h: New files.
+ Makefile.dist, config.gdb: Know about these new files.
+
+Tue Aug 15 21:36:11 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * symtab.c (lookup_struct_elt_type): Use type_print rather
+ than assuming type has a name.
+
+Tue Aug 15 02:25:43 1989 Roland McGrath (roland at apple-gunkies.ai.mit.edu)
+
+ * sparc-opcode.h (mov): Removed bogus "or i,0,d" pattern.
+
+ * sparc-opcode.h (mov, or): Fixed incorrect `lose' members.
+
+ * sparc-dep.c: Don't include "sparc-opcode.h".
+ (skip_prologue, isanulled): Declare special types to recognize
+ instructions, and use them.
+
+ * sparc-pinsn.c (print_insn): Sign-extend 13-bit immediate args.
+ If they are less than +9, print them in signed decimal instead
+ of unsigned hex.
+
+ * sparc-opcode.h, sparc-pinsn.c: Completely rewritten to share an
+ opcode table with gas, and thus produce disassembly that looks
+ like what the assembler accepts.
+
+Tue Aug 15 16:20:52 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * symtab.c (find_pc_psymbol): Move best_pc=psymtab->textlow-1
+ after test for psymtab null.
+
+ * main.c (editing_info): Remove variable retval.
+
+ * config.gdb (sun3, isi): Comment out obsolete message about telling
+ it whether you have an FPU (now that it detects it).
+
+ * config.gdb (sun3): Accept sun3os3.
+
+ * m68k-insn.h: Include <signal.h>.
+
+ * m68k-pinsn.h (convert_{to,from}_68881): Add have_fpu code
+
+ * m-newsos3.h: Undefine USE_PCB. That code didn't seem to work.
+
+ * sparc-dep.c: Put in insn_fmt and other stuff from the old
+ sparc-opcode.h.
+
+ * sparc-opcode.h, sparc-pinsn.c: Correct copyright notice.
+
+ * sparc-opcode.h, sparc-pinsn.c: Replace the old ones with the new
+ ones by roland.
+
+Tue Aug 15 02:25:43 1989 Roland McGrath (roland at apple-gunkies.ai.mit.edu)
+
+ * Makefile.dist: Don't define CC at all.
+
+ * Makefile.dist (Makefile): Remove tmp.c after preprocessing.
+ Use $(MD) instead of M_MAKEDEFINE in the cc command.
+
+ * Makefile.dist: Don't define RL_LIB as
+ "${READLINE}/libreadline.a", since READLINE is a list of files.
+
+Mon Aug 14 23:49:29 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * main.c (print_version): Change 1988 to 1989.
+
+ * main.c (copying_info, initialize_main): Remove #if 0'd code.
+
+Tue Aug 1 14:44:56 1989 Hikichi (hikichi at sran203)
+
+ * m-newsos3.h
+ (NO_SIGINTERRUPT): have SIGINTERRUPT on NEWS os 3.
+
+ * m-news.h(FRAME_FIND_SAVED_REGS): use the sun3's instead of old
+ one.
+
+Mon Aug 14 15:27:01 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * m-news.h, m-newsos3.h, news-dep.c: Merge additional changes
+ by Hikichi (ChangeLog entries above).
+
+ * Makefile.dist (READLINE): List readline files individually
+ so we don't accidently get random files from the readline
+ directory.
+
+ * m-news.h (STORE_RETURN_VALUE, EXTRACT_RETURN_VALUE):
+ Expect floating point returns to be in fp0.
+
+ * gdb.texinfo (Format options): New node.
+
+ * gdb.texinfo: Comment out "@include"s until bfox fixes the
+ readline & history docs.
+
+ * dbxread.c (read_addl_syms): Set startup_file_* if necessary at
+ the end (as well as when we hit ".o").
+
+ * printcmd.c (decode_format): Set val.format & val.size to '?' at
+ start and set defaults at end.
+
+ * symtab.c (decode_line_1): Check for class_name null.
+
+ * valops.c: Each place where it compares against field names,
+ check for null field names. (new t_field_name variables).
+
+ * utils.c (fputs_filtered): Check for linebuffer null before
+ checking whether to call fputs. Remove later check for linebuffer
+ null.
+
+Sun Aug 13 15:56:50 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * m-isi.h, m-sun3.h ({PUSH,POP}_FP_REGS): New macros.
+ m-sun3.h (NUM_REGS): Conditionalize on FPU.
+ config.gdb (sun3, isi): Add message about support for machines
+ without FPU.
+
+ * main.c (catch_termination, initialize_signals): new functions.
+
+ * main.c (editing_info): Add "info editing n" and "info editing +".
+ Rewrite much of this function.
+ gdb.texinfo (GDB Readline): Document it.
+
+ * values.c (history_info): Add "info history +". Also add code to
+ do "info history +" when command is repeated.
+ gdb.texinfo (Value History): Document "info history +".
+
+ * expprint.c (print_subexp): Add OP_THIS to case stmt.
+
+ * config.gdb (sun4os4): Put quotes around make define.
+
+ * config.gdb: Canonicalize machine name at beginning.
+
+Sat Aug 12 00:50:59 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * config.gdb: define M_MAKEDEFINE
+ Makefile (Makefile, MD): Be able to re-make Makefile.
+
+ * main.c (command_line_input): Add comments to
+ the command history.
+
+ * Makefile.dist (Makefile): Add /bin/false.
+
+Fri Aug 11 14:35:33 1989 Jim Kingdon (kingdon at spiff)
+
+ * Makefile.dist: Comment out .c.o rule and add TARGET_ARCH.
+
+ * m-altos.h: Include sys/page.h & sys/net.h
+
+ * m-altos.h (FRAME_CHAIN{,_VALID}): Use outside_startup_file.
+
+ * config.gdb (altos, altosgas): Add M_SYSV & M_BSD_NM and remove
+ M_ALLOCA=alloca.o from makedefine.
+
+ * coffread.c (complete_symtab): Change a_entry to entry.
+
+ * m-altosgas.h: New file.
+
+ * m-symmetry (REGISTER_BYTE): Fix dumb mistake.
+
+Fri Aug 11 06:39:49 1989 Roland McGrath (roland at hobbes.ai.mit.edu)
+
+ * utils.c (set_screensize_command): Check for ARG being nil, since
+ that's what execute_command will pass if there's no argument.
+
+ * expread.y (yylex): Recognize "0x" or "0X" as the beginning of a
+ number.
+
+Thu Aug 10 15:43:12 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * config.gdb, Makefile.dist: Rename Makefile.c to Makefile.dist.
+
+ * m-altos.h: Add comment about porting to USGR2.
+
+ * config.gdb (sparc): Add -Usparc.
+
+Wed Aug 9 14:20:39 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * m-sun3os4.h: Define BROKEN_LARGE_ALLOCA.
+
+ * values.c (modify_field): Check for value too large to fit in
+ bitfield.
+
+ * utils.c (fputs_filtered): Allow LINEBUFFER to be NULL.
+
+ * breakpoint.c (condition_command): Check for attempt to specify
+ non-numeric breakpoint number.
+
+ * config.gdb, Makefile, m-altos.h, altos-dep.c: Merge Altos
+ port.
+
+ * README: Change message about editing Makefile.
+
+ * config.gdb: Edit Makefile.
+ Copied Makefile to Makefile.c and changed to let config.gdb
+ run us through the C preprocessor.
+
+ * expread.y (yylex): Test correctly for definition of number.
+
+Wed Aug 9 11:56:05 1989 Randy Smith (randy at hobbes.ai.mit.edu)
+
+ * dbxread.c (read_dbx_symtab): Put bracketing of entry point in
+ test case for .o symbols so that it will be correct even without
+ debugging symbols.
+ (end_psymtab): Took bracketing out.
+
+ * blockframe.c (outside_startup_file): Reverse the sense of the
+ return value to make the functionality implied by the name
+ correct.
+
+Tue Aug 8 11:48:38 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * coffread.c (symbol_file_command): Do not assume presence of a.out
+ header.
+
+ * blockframe.c: Replace first_object_file_end with
+ startup_file_{start,end}
+ (outside_startup_file): New function.
+ dbxread.c (read_addl_syms, read_dbx_symtab, end_psymbol): set
+ startup_file_*. Delete first_object_file_end code.
+ Add entry_point and ENTRY_POINT
+ coffread.c (complete_symtab): Set startup_file_*.
+ (first_object_file_end): Add as static.
+ m-*.h (FRAME_CHAIN, FRAME_CHAIN_VALID): Call outside_startup_file
+ instead of comparing with first_object_file_end.
+
+ * breakpoint.c (breakpoint_1): Change -1 to (CORE_ADDR)-1.
+
+ * config.gdb (i386, i386gas): Add missing quotes at end of "echo"
+
+ * source.c (directory_command): Add dont_repeat ();
+
+Mon Aug 7 18:03:51 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * dbxread.c (read_addl_syms): Change strcmp to strncmp and put 3rd
+ arg back.
+
+ * command.h (struct cmd_list_element): Add comment clarifying
+ purpose of abbrev_flag.
+
+Mon Aug 7 12:51:03 1989 Randy Smith (randy at hobbes.ai.mit.edu)
+
+ * printcmd.c (_initialize_printcmd): Changed "undisplay" not to
+ have abbrev flag set; it isn't an abbreviation of "delete
+ display", it's an alias.
+
+Mon Aug 7 00:25:15 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * symtab.c (lookup_symtab_1): Remove filematch (never used).
+
+ * expread.y [type]: Add second argument to 2 calls to
+ lookup_member_type which were missing them.
+
+ * dbxread.c (symbol_file_command): Add from_tty arg.
+ Check it before calling query.
+
+ * infcmd.c (tty_command): Add from_tty arg.
+
+ * eval.c (evaluate_subexp): Remove 3rd argument from
+ calls to value_x_unop.
+
+ * dbxread.c (read_addl_syms): Remove 3rd argument from
+ call to strcmp.
+
+ * gdb.texinfo (Command editing): @include inc-readline.texinfo
+ and inc-history.texinfo and reorganize GDB-specific stuff.
+
+ * Makefile: Add line MAKE=make.
+
+ * README (second paragraph): Fix trivial errors.
+
+ * dbxread.c (read_struct_type): Make sure p is initialized.
+
+ * main.c (symbol_completion_function): Complete correctly
+ on the empty string.
+
+Sun Aug 6 21:01:59 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * symmetry-dep.c: Remove "long" from definition of i386_follow_jump.
+
+ * gdb.texinfo (Backtrace): Document "where" and "info stack".
+
+ * dbxread.c (cleanup_undefined_types): Strip off "struct "
+ or "union " from type names before doing comparison
+
+Sat Aug 5 02:05:36 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * config.gdb (i386, i386gas): Improve makefile editing instructions.
+
+ * Makefile: Fix typo in CLIBS for SYSV.
+
+ * dbxread.c (read_dbx_symtab): Deal with N_GSYM typedefs.
+
+ * dbxread.c (add_file_command): Do not free name. We didn't
+ allocate it; it just points into arg_string.
+
+ * Makefile, m-*.h: Change LACK_VPRINTF to HAVE_VPRINTF.
+
+Fri Jul 28 00:07:48 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * valprint.c (val_print): Made sure that all returns returned a
+ value (usually 0, indicating no memory printed).
+
+ * core.c (read_memory): Changed "return" to "return 0".
+
+ * expread.y (parse_number): Handle scientific notation when the
+ string does not contain a '.'.
+
+Thu Jul 27 15:14:03 1989 Randy Smith (randy at hobbes.ai.mit.edu)
+
+ * infrun.c (signals_info): Error if signal number passed is out of
+ bounds.
+
+ * defs.h: Define alloca to be __builtin_alloca if compiling with
+ gcc and localized inclusion of alloca.h on the sparc with the
+ other alloca stuff.
+ * command.c: Doesn't need to include alloca.h on the sparc; defs.h
+ does it for you.
+
+ * printcmd.c (print_frame_args): Changed test for call to
+ print_frame_nameless_args to check i to tell if any args had been
+ printed.
+
+Thu Jul 27 04:40:56 1989 Roland McGrath (roland at hobbes.ai.mit.edu)
+
+ * blockframe.c (find_pc_partial_function): Always check that NAME
+ and/or ADDRESS are not nil before storing into them.
+
+Wed Jul 26 23:41:21 1989 Roland McGrath (roland at hobbes.ai.mit.edu)
+
+ * m-newsos3.h: Define BROKEN_LARGE_ALLOCA.
+ * dbxread.c (symbol_file_command, psymtab_to_symtab):
+ Use xmalloc #ifdef BROKEN_LARGE_ALLOCA.
+
+Tue Jul 25 16:28:18 1989 Jay Fenlason (hack at apple-gunkies.ai.mit.edu)
+
+ * m68k-opcode.h: moved some of the fmovem entries so they're
+ all consecutive. This way the assembler doesn't bomb.
+
+Mon Jul 24 22:45:54 1989 Randy Smith (randy at hobbes.ai.mit.edu)
+
+ * symtab.c (lookup_symbol): Changed error to an informational (if
+ not very comforting) message about internal problems. This will
+ get a null symbol returned to decode_line_1, which should force
+ things to be looked up in the misc function vector.
+
+Wed Jul 19 13:47:34 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * symtab.c (lookup_symbol): Changed "fatal" to "error" in
+ external symbol not found in symtab in which it was supposed to be
+ found. This can be reached because of a bug in ar.
+
+Tue Jul 18 22:57:43 1989 Randy Smith (roland at hobbes.ai.mit.edu)
+
+ * m-news.h [REGISTER_U_ADDR]: Decreased the assumed offset of fp0
+ by 4 to bring it into (apparently) appropriate alignment with
+ reality.
+
+Tue Jul 18 18:14:42 1989 Randy Smith (randy at hobbes.ai.mit.edu)
+
+ * Makefile: pinsn.o should depend on opcode.h
+
+ * m68k-opcode.h: Moved fmovemx with register lists to before other
+ fmovemx.
+
+Tue Jul 18 11:21:42 1989 Jim Kingdon (kingdon at susie)
+
+ * Makefile, m*.h: Only #define vprintf (to _doprnt or printf,
+ depends on the system) if the library lacks it (controlled by
+ LACK_VPRINTF_DEFINE in makefile). Unpleasant, but necessary to
+ make this work with the GNU C library.
+
+Mon Jul 17 15:17:48 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * breakpoint.c (breakpoint_1): Change addr-b->address to
+ b->address-addr.
+
+Sun Jul 16 16:23:39 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * eval.c (evaluate_subexp): Change error message printed when
+ right operand of '@' is not an integer to English.
+
+ * infcmd.c (registers_info): Fix call to print_spaces_filtered
+ to specify right # of arguments.
+
+ * gdb.texinfo (Command Editing): Document info editing command.
+
+ * coffread.c (read_file_hdr): Add MC68MAGIC.
+
+ * source.c (select_source_symtab): Change MAX to max.
+
+Fri Jul 14 21:19:11 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * infcmd.c (registers_info): Clean up display to look good with long
+ register names, to say "register" instead of "reg", and to put the
+ "relative to selected stack frame" bit at the top.
+
+Fri Jul 14 18:23:09 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * dbxread.c (record_misc_function): Put parens around | to force
+ correct evaluation.
+
+Wed Jul 12 12:25:53 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * m-newsos3, m-news, infrun.c, Makefile, config.gdb, news-dep.c:
+ Merge in Hikichi's changes for Sony/News-OS 3 support.
+
+Tue Jul 11 21:41:32 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * utils.c (fputs_filtered): Don't do any filtering if output is
+ not to stdout, or if stdout is not a tty.
+ (fprintf_filtered): Rely on fputs_filtered's check for whether to
+ do filtering.
+
+Tue Jul 11 00:33:58 1989 Randy Smith (randy at hobbes.ai.mit.edu)
+
+ * GDB 3.2 Released.
+
+ * valprint.h: Deleted.
+
+ * utils.c (fputs_filtered): Don't do any filtering if filtering is
+ disabled (lines_per_page == 0).
+
+Mon Jul 10 22:27:53 1989 Randy Smith (roland at hobbes.ai.mit.edu)
+
+ * expread.y [typebase]: Added "unsigned long int" and "unsigned
+ short int" to specs.
+
+Mon Jul 10 21:44:55 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * main.c (main): Make -cd use cd_command to avoid
+ current_directory with non-absolute pathname.
+
+Mon Jul 10 00:34:29 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * dbxread.c (symbol_file_command): Catch errors from stat (even
+ though they should never happen).
+
+ * source.c (openp): If the path is null, use the current
+ directory.
+
+ * dbxread.c (read_dbx_symtab): Put N_SETV symbols into the misc
+ function vector ...
+ (record_misc_function): ... as data symbols.
+
+ * utils.c (fprintf_filtered): Return after printing if we aren't
+ going to do filtering.
+
+ * Makefile: Added several things for make clean to take care of.
+
+ * expread.y: Lowered "@" in precedence below +,-,*,/,%.
+
+ * eval.c (evaluate_subexp): Return an error if the rhs of "@"
+ isn't integral.
+
+ * Makefile: Added removal of core and gdb[0-9] files to clean
+ target.
+
+ * Makefile: Made a new target "distclean", which cleans things up
+ correctly for making a distribution.
+
+Sun Jul 9 23:21:27 1989 Randy Smith (randy at hobbes.ai.mit.edu)
+
+ * dbxread.c: Surrounded define of gnu symbols with an #ifndef
+ NO_GNU_STABS in case you don't want them on some machines.
+ * m-npl.h, m-pn.h: Defined NO_GNU_STABS.
+
+Sun Jul 9 19:25:22 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * utils.c (fputs_filtered): New function.
+ (fprintf_filtered): Use fputs_filtered.
+ utils.c (print_spaces_filtered),
+ command.c (help_cmd,help_cmd_list),
+ printcmd.c (print_frame_args),
+ stack.c (print_block_frame_locals, print_frame_arg_vars),
+ valprint.c (many functions): Use fputs_filtered instead of
+ fprintf_filtered to avoid arbitrary limit.
+
+ * utils.c (fprintf_filtered): Fix incorrect comment.
+
+Sat Jul 8 18:12:01 1989 Randy Smith (randy at hobbes.ai.mit.edu)
+
+ * valprint.c (val_print): Changed assignment of pretty to use
+ prettyprint as a conditional rather than rely on values of the
+ enum.
+
+ * Projects: Cleaned up a little for release.
+
+ * main.c (initialize_main): Initialize
+ rl_completion_entry_function instead of completion_entry_function.
+
+ * Makefile: Modified to use the new readline library setup.
+
+ * breakpoint.c (break_command_1, delete_breakpoint,
+ enable_breakpoint, disable_breakpoint): Put in new printouts for
+ xgdb usage triggered off of xgdb_verbose.
+ * main.c (main): Added check for flag to set xgdb_verbose.
+ * stack.c (frame_command): Set frame_changed when frame command
+ used.
+
+Fri Jul 7 16:20:58 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * Remove valprint.h and move contents to value.h (more logical).
+
+Fri Jul 7 02:28:06 1989 Randall Smith (randy at rice-chex)
+
+ * m68k-pinsn.c (print_insn): Included a check for register list;
+ if there is one, make sure to start p after it.
+
+ * breakpoint.c (break_command_1, delete_breakpoint,
+ enable_breakpoint, disable_breakpoint): #ifdef'd out changes
+ below; they produce unwanted output in gdb mode in gnu-emacs.
+
+ * gdb.texinfo: Spelled. Also removed index references from
+ command editing section; the relevance/volume ratio was too low.
+ Removed all references to the function index.
+
+ * ns32k-opcode.h, ns32k-pinsn.c: Backed out changes of June 24th;
+ haven't yet received legal papers.
+
+ * .gdbinit: Included message telling the user what it is doing.
+
+ * symmetry-dep.c: Added static decls for i386_get_frame_setup,
+ i386_follow_jump.
+ * values.c (unpack_double): Added a return (double)0 at the end to
+ silence a compiler warning.
+
+ * printcmd.c (containing_function_bounds, asdump_command): Created
+ to dump the assembly code of a function (support for xgdb and a
+ useful hack).
+ (_initialize_printcmd): Added this to command list.
+ * gdb.texinfo [Memory]: Added documentation for the asdump
+ command.
+ * breakpoint.c (break_command_1, delete_breakpoint,
+ enable_breakpoint, disable_breakpoint): Added extra verbosity for
+ xgdb conditionalized on the new external frame_full_file_name.
+ * source.c (identify_source_line): Increase verbosity of fullname
+ prointout to include pc value.
+ * stack.c: Added a new variable; "frame_changed" to indicate when
+ a frame has been changed so that gdb can print out a frame change
+ message when the frame only changes implicitly.
+ (print_frame_info): Check the new variable in determining when to
+ print out a new message and set it to zero when done.
+ (up_command): Increment it.
+ (down_command): Decrement it.
+
+ * m68k-pinsn.c (print_insn_arg [lL]): Modified cases for register
+ lists to reset the point to point to after the word from which the
+ list is grabbed *if* that would cause point to point farther than
+ it currently is.
+
+Thu Jul 6 14:28:11 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * valprint.c (val_print, value_print): Add parameter to control
+ prettyprinting.
+ valprint.h: New file containing constants used for passing
+ prettyprinting parameter to val{,ue}_print.
+ expprint.c, infcmd.c, printcmd.c, valprint.c, values.c:
+ Change all calls to val{,ue}_print to use new parameter.
+
+Mon Jul 3 22:38:11 1989 Randy Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * dbxread.c (,process_one_symbol): Moved extern declaration for
+ index out of function to beginning of file.
+
+Mon Jul 3 18:40:14 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * gdb.texinfo (Registers): Add "ps" to list of standard registers.
+
+Sun Jul 2 23:13:03 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * printcmd.c (enable_display): Change d->next to d = d->next so
+ that "enable display" without args works.
+
+Fri Jun 30 23:42:04 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * source.c (list_command): Made error message given when no
+ symtab is loaded clearer.
+
+ * valops.c (value_assign): Make it so that when assigning to an
+ internal variable, the type of the assignment exp is the type of
+ the value being assigned.
+
+Fri Jun 30 12:12:43 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * main.c (verbose_info): Created.
+ (initialize_main): Put "info verbose" into command list.
+
+ * utils.c (screensize_info): Created.
+ (_initialize_utils): Defined "info screensize" as a normal command.
+
+ * valprint.c (format_info): Added information about maximum number
+ of array elements to function.
+
+ * blockframe.c (find_pc_partial_function): Again.
+
+ * blockframe.c (find_pc_partial_function): Replaced a "shouldn't
+ happen" (which does) with a zero return.
+
+ * main.c (dont_repeat): Moved ahead of first use.
+
+Thu Jun 29 19:15:08 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * vax-opcode.h: Made minor modifications (moved an instruction and
+ removed a typo) to bring this into accord with gas' table; also
+ changed copyright to reflect it being part of both gdb and gas.
+
+ * m68k-opcode.h: Added whole scads and bunches of new stuff for
+ the m68851 and changed the coptyrightto recognize that the file
+ was shared between gdb and gas.
+
+ * main.c (stop_sig): Use "dont_repeat ()" instead of *line = 0;
+
+ * core.c (read_memory): Don't do anything if length is 0.
+
+ * Makefile: Added readline.c to the list of files screwed by
+ having the ansi ioctl.h compilation with gcc.
+
+ * config.gdb: Added sun4os3 & sun4-os3 as availible options.
+
+Wed Jun 28 02:01:26 1989 Jim Kingdon (kingdon at apple-gunkies.ai.mit.edu)
+
+ * command.c (lookup_cmd): Add ignore_help_classes argument.
+ (lookup_cmd_1): Add ignore_help_classes argument.
+ command.c, main.c: Change callers of lookup_cmd{,_1} to supply
+ value for ignore_help_classes.
+
+Tue Jun 27 18:01:31 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * utils.c (print_spaces_filtered): Made more efficient.
+ * defs.h: Declaration.
+ * valprint.c (val_print): Used in a couple of new places.
+
+Mon Jun 26 18:27:28 1989 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * m68k-pinsn.c (print_insn_arg ['#', '^']): Combined them into one
+ case which always gets the argument from the word immediately
+ following the instruction.
+ (print_insn_arg ["[lL]w"]): Make sure to always get the register
+ mask from the word immediately following the instruction.
+
+Sun Jun 25 19:14:56 1989 Randall Smith (randy at galapas.ai.mit.edu)
+
+ * Makefile: Added hp-include back in as something to distribute.
+
+ * stack.c (print_block_frame_locals): Return value changed from
+ void to int; return 1 if values printed. Use _filtered.
+ (print_frame_local_vars): Use return value from
+ print_block_frame_locals to mention if nothing printed; mention
+ lack of symbol table, use _filtered.
+ (print_frame_arg_vars): Tell the user if no symbol table
+ or no values printed. Use fprintf_filtered instead of fprintf.
+ * blockframe.c (get_prev_frame_info): Check for no inferior or
+ core file before crashing.
+
+ * inflow.c (inferior_died): Set current frame to zero to keep from
+ looking like we're in start.
+
+Sat Jun 24 15:50:53 1989 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * stack.c (frame_command): Added a check to make sure that there
+ was an inferior or a core file.
+
+ * expread.y (yylex): Allow floating point numbers of the form ".5"
+ to be parsed.
+
+ Changes by David Taylor at TMC:
+ * ns32k-pinsn.c: Added define for ?floating point coprocessor? and
+ tables for register names to be used for each of the possibilities.
+ (list_search): Created; searches a list of options for a specific
+ value.
+ (print_insn_arg): Added 'Q', 'b', 'M', 'P', 'g', and 'G' options
+ to the value location switch.
+ * ns32k-opcode.h: Added several new location flags.
+ [addr, enter, exit, ext[bwd], exts[bwd], lmr, lpr[bwd], restore,
+ rett, spr[bwd], smr]: Improved insn format output.
+
+ * symtab.c (list_symbols): Rearrange printing to produce readable
+ output for "info types".
+
+ * eval.c (evaluate_subexp_for_address): Fixed typo.
+
+ * dbxread.c (read_type): Don't output an error message when
+ there isn't a ',' after a cross-reference.
+
+ * dbxread.c (read_dbx_symtab): #if'd out N_FN case in
+ read_dbx_symtab if it has the EXT bit set (otherwise multiple
+ cases with the same value).
+
+Fri Jun 23 13:12:08 1989 Randall Smith (randy at plantaris.ai.mit.edu)
+
+ * symmisc.c: Changed decl of print_spaces from static to extern
+ (since it's defined in utils.c).
+
+ * remote.c (remote_open): Close remote_desc if it's already been
+ opened.
+
+ * Remote_Makefile, remote_gutils.c, remote_inflow.c,
+ remote_server.c, remote_utils.c: Combined into remote-multi.shar.
+ * remote-multi.shar: Created (Vikram Koka's remote stub).
+ * remote-sa.m68k.shar: Created (Glenn Engel's remcom.c).
+ * README: Updated to reflect new organization of remote stubs.
+
+ * dbxread.c (read_dbx_symtab): Put an N_FN in with N_FN | N_EXT to
+ account for those machines which don't use the external bit here.
+ Sigh.
+
+ * m-symmetry.h: Defined NO_SIGINTERRUPT.
+
+Thu Jun 22 12:51:37 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * printcmd.c (decode_format): Make sure characters are printed
+ using a byte size.
+
+ * utils.c (error): Added a terminal_ours here.
+
+ * stack.c (locals_info): Added check for selected frame.
+
+ * dbxread.c (read_type): Checked to make sure that a "," was
+ actually found in the symbol to end a cross reference.
+
+Wed Jun 21 10:30:01 1989 Randy Smith (randy at tartarus.uchicago.edu)
+
+ * expread.y (parse_number, [exp]): Allowed for the return of a
+ number marked as unsigned; this will allow inclusion of unsigned
+ constants.
+
+ * symtab.h: Put in default definitions for BUILTIN_TYPE_LONGEST
+ and BUILTIN_TYPE_UNSIGNED_LONGEST.
+
+ * expread.y (parse_number): Will now accept integers suffixed with
+ a 'u' (though does nothing special with it).
+
+ * valarith.c (value_binop): Added cases to deal with unsigned
+ arithmetic correctly.
+
+Tue Jun 20 14:25:54 1989 Randy Smith (randy at tartarus.uchicago.edu)
+
+ * dbxread.c (psymtab_to_symtab_1): Changed reading in info message
+ to go through printf_filtered.
+
+ * symtab.c (list_symbols): Placed header message after all calls
+ to psymtab_to_symtab.
+
+ * symtab.c (smash_to_{function, reference, pointer}_type): Carried
+ attribute of permanence for the type being smashed over the bzero
+ and allowed any type to point at this one if it is permanent.
+
+ * symtab.c (smash_to_{function, reference, pointer}_type): Fix
+ typo: check flags of to_type instead of type.
+
+ * m-hp9k320.h: Changed check on __GNU__ predefine to __GNUC__.
+
+ * Makefile: Made MUNCH_DEFINE seperate and based on SYSV_DEFINE;
+ they aren't the same on hp's.
+
+Mon Jun 19 17:10:16 1989 Randy Smith (randy at tartarus.uchicago.edu)
+
+ * Makefile: Fixed typo.
+
+ * valops.c (call_function): Error if the inferior has not been
+ started.
+
+ * ns32k-opcode.h [check[wc], cmpm[bwd], movm[bwd], skpsb]: Fixed
+ typos.
+
+Fri Jun 9 16:23:04 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * m-news.h [NO_SIGINTERRUPT]: Defined.
+
+ * dbxread.c (read_type): Start copy of undefined structure name
+ past [sue] defining type of cross ref.
+
+ * dbxread.c (process_one_symbol): Changed strchr to index.
+
+ * ns32k-opcode.h, ns32k-pinsn.c: More changes to number of
+ operands, addition of all of the set condition opcodes, addition
+ of several flag letters, all patterned after the gas code.
+
+ * ns32k-opcode.h [mov{su,us}[bwd], or[bwd]]: Changed number of
+ operands from 1 to 2.
+
+Wed Jun 7 15:04:24 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * symseg.h [TYPE_FLAG_STUB]: Created.
+ * dbxread.c (read_type): Set flag bit if type is stub.
+ (cleanup_undefined_types): Don't mark it as a stub if it's been
+ defined since we first learned about it.
+ * valprint.c (val_print): Print out a message to that effect if
+ this type is encountered.
+
+ * symseg.h, symtab.h: Moved the definition of TYPE_FLAG_PERM over
+ to symseg.h so that all such definitions would be in the same place.
+
+ * valprint.c (val_print): Print out <No data fields> for a
+ structure if there aren't any.
+
+ * dbxread.c (read_type): Set type name of a cross reference type
+ to "struct whatever" or something.
+
+Tue Jun 6 19:40:52 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * breakpoint.c (breakpoint_1): Print out symbolic location of
+ breakpoints for which there are no debugging symbols.
+
+Mon Jun 5 15:14:51 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * command.c (help_cmd_list): Made line_size static.
+
+Sat Jun 3 17:33:45 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * Makefile: Don't include the binutils hp-include directory in the
+ distribution anymore; refer the users to the binutils distribution.
+
+Thu Jun 1 16:33:07 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * printcmd.c (disable_display_command): Fixed loop iteration for
+ no arg case.
+
+ * printcmd.c (disable_display_command): Added from_tty parameter
+ to function.
+
+ * valops.c (value_of_variable): Call read_var_value with 0 cast to
+ FRAME instead of CORE_ADDR.
+
+ * eval.c (evaluate_subexp): Corrected number of args passed to
+ value_subscript (to 2).
+
+ * infrun.c (wait_for_inferior), symtab.c (decode_line_1),
+ m-convex.h: Changed name of FIRSTLINE_DEBUG_BROKEN to
+ PROLOGUE_FIRSTLINE_OVERLAP.
+
+ * m-merlin.h: Fixed typo.
+ * ns32k-opcode.h: Added ns32381 opcodes and "cinv" insn, and fixed
+ errors in movm[wd], rett, and sfsr.
+
+ * eval.c (evaluate_subexp, evaluate_subexp_for_address), valops.c
+ (value_zero): Change value_zero over to taking two arguments
+ instead of three.
+
+ * eval.c (evaluate_subexp)
+ [OP_VAR_VALUE]: Get correct lval type for AVOID_SIDE_EFFECTS for
+ all types of symbols.
+ [BINOP_DIV]: Don't divide if avoiding side effects; just return
+ an object of the correct type.
+ [BINOP_REPEAT]: Don't call value_repeat, just allocate a
+ repeated value.
+ (evaluete_subexp_for_address) [OP_VAR_VALUE]: Just return a thing
+ of the right type (after checking to make sure that we are allowed
+ to take the address of whatever variable has been passed).
+
+Mon May 29 11:01:02 1989 Randall Smith (randy at galapas.ai.mit.edu)
+
+ * breakpoint.c (until_break_command): Set the breakpoint with a
+ frame specification so that it won't trip in inferior calls to the
+ function. Also set things up so that it works based on selected
+ frame, not current one.
+
+Sun May 28 15:05:33 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * eval.c (evalue_subexp): Change subscript case to use value_zero
+ in EVAL_AVOID_SIDE_EFFECTS case.
+
+Fri May 26 12:03:56 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * dbxread.c (read_addl_syms, psymtab_to_symtab): Removed
+ cleanup_undefined_types; this needs to be done on a symtab basis.
+ (end_symtab): Called cleanup_undefined_types from here.
+ (cleanup_undefined_types): No longer uses lookup_symbol (brain
+ dead idea; oh, well), now it searches through file_symbols.
+
+Wed May 24 15:52:43 1989 Randall Smith (randy at galapas)
+
+ * source.c (select_source_symtab): Only run through
+ partial_symtab_list if it exists.
+
+ * coffread.c (read_coff_symtab): Don't unrecord a misc function
+ when a function symbol is seen for it.
+
+ * expread.y [variable]: Make sure to write a type for memvals if
+ you don't get a mft you recognize.
+
+Tue May 23 12:15:57 1989 Randall Smith (randy at plantaris.ai.mit.edu)
+
+ * dbxread.c (read_ofile_symtab, psymtab_to_symtab): Moved cleanup
+ of undefined types to psymtab_to_symtab. That way it will be
+ called once for all readins (which will, among other things,
+ help reduce infinite loops).
+
+ * symtab.h [misc_function_type]: Forced mf_unknown to 0.
+ * dbxread.c (record_misc_function): Cast enum to unsigned char (to
+ fit).
+ * expread.y [variable]: Cast unsigned char back to enum to test.
+
+Mon May 22 13:08:25 1989 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ Patches by John Gilmore for dealing well with floating point:
+ * findvar.c (value_from_register, locate_var_value): Used
+ BYTES_BIG_ENDIAN instead of an inline test.
+ * m-sparc.h [IEEE_FLOAT]: Created to indicate that the sparc is
+ IEEE compatible.
+ * printcmd.c (print_scalar_formatted): Use BYTES_BIG_ENDIAN and
+ the stream argument for printing; also modify default type for
+ 'f'. Change handling of invalid floats; changed call syntax for
+ is_nan.
+ (print_command): Don't print out anything indicating that
+ something was recorded on the history list if it wasn't.
+ * valprint.c (val_print): Fixed to deal properley with new format
+ of is_nan and unpacking doubles without errors occuring.
+ (is_nan): Changed argument list and how it figures big endianness
+ (uses macros).
+ * values.c (record_latest_value): Return -1 and don't record if
+ it's an invalid float.
+ (value_as_double): Changed to use new unpack_double calling
+ convention.
+ (unpack_double): Changed not to call error if the float was
+ invalid; simply to set invp and return. Changed calling syntax.
+ (unpack_field_as_long, modify_field): Changed to use
+ BITS_BIG_ENDIAN to determine correct action.
+
+ * m-hp9k320.h [HP_OS_BUG]: Created; deals with problem where a
+ trap happens after a continue.
+ * infrun.c (wait_for_inferior): Used.
+
+ * m-convex.h [FIRSTLINE_DEBUG_BROKEN]: Defined a flag to indicate
+ that the debugging symbols output by the compiler for the first
+ line of a function were broken.
+ * infrun.c (wait_for_inferior), symtab.c (decode_line_1): Used.
+
+ * gdb.texinfo [Data, Memory]: Minor cleanups of phrasing.
+
+Fri May 19 00:16:59 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * dbxread.c (add_undefined_type, cleanup_undefined_types): Created
+ to keep a list of cross references to as yet undefined types.
+ (read_type): Call add_undefined_type when we run into such a case.
+ (read_addl_syms, read_ofile_symtab): Call cleanup_undefined_types
+ when we're done.
+
+ * dbxread.c (psymtab_to_symtab, psymtab_to_symtab_1): Broke
+ psymtab_to_symtab out into two routines; made sure the string
+ table was only readin once and the globals were only scanned once,
+ for any number of dependencies.
+
+Thu May 18 19:59:18 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * m-*.h: Defined (or not, as appropriate per machine)
+ BITS_BIG_ENDIAN, BYTES_BIG_ENDIAN, and WORDS_BIG_ENDIAN.
+
+Wed May 17 13:37:45 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * main.c (symbol_completion_function): Always complete on result
+ command list, even if exact match found. If it's really an exact
+ match, it'll find it again; if there's something longer than it,
+ it'll get the right result.
+
+ * symtab.c (make_symbol_completion_function): Fixed typo; strcmp
+ ==> strncmp.
+
+ * dbxread.c (read_dbx_symtab): Change 'G' case to mark symbols as
+ LOC_EXTERNAL.
+
+ * expread.y [variables]: Changed default type of text symbols to
+ function returning int so that one can use, eg. strcmp.
+
+ * infrun.c (wait_for_inferior): Include a special flag indicating
+ that one shouldn't insert the breakpoints on the next step for
+ returning from a sigtramp and forcing at least one move forward.
+
+ * infrun.c (wait_for_inferior): Change test for nexting into a
+ function to check for current stack pointer inner than previous
+ stack pointer.
+
+ * infrun.c (wait_for_inferior): Check for step resume break
+ address before dealing with normal breakpoints.
+
+ * infrun.c (wait_for_inferior): Added a case to deal with taking
+ and passing along a signal when single stepping past breakpoints
+ before inserting breakpoints.
+
+ * infrun.c (wait_for_inferior): Inserted special case to keep
+ going after taking a signal we are supposed to be taking.
+
+Tue May 16 12:49:55 1989 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * inflow.c (terminal_ours_1): Cast result of signal to (int
+ (*)()).
+
+ * gdb.texinfo: Made sure that references to the program were in
+ upper case. Modify description of the "set prompt" command.
+ [Running]: Cleaned up introduction.
+ [Attach]: Cleaned up.
+ [Stepping]: Change "Proceed" to "Continue running" or "Execute".
+ Minor cleanup.
+ [Source Path]: Cleaned up intro. Cleared up distinction between
+ the executable search path and the source path. Restated effect
+ of the "directory" command with no arguments.
+ [Data]: Fixed typos and trivial details.
+ [Stepping]: Fixed up explanation of "until".
+
+ * source.c (print_source_lines): Print through filter.
+
+ * printcmd.c (x_command): If the format with which to print is
+ "i", use the address of anything that isn't a pointer instead of
+ the value. This is for, eg. "x/10i main".
+
+ * gdb.texinfo: Updated last modification date on manual.
+
+Mon May 15 12:11:33 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * symtab.c (lookup_symtab): Fixed typo (name ==> copy) in call to
+ lookup_symtab_1.
+
+ * gdb.texinfo: Added documentation for "break [+-]n" and for new
+ actions of "directory" command (taking multiple directory names at
+ the same time).
+
+ * m68k-opcode.h: Replaced the version in gdb with an up-to-date
+ version from the assembler directory.
+ * m68k-pinsn.c (print_insn_arg): Added cases 'l' & 'L' to switch
+ to print register lists for movem instructions.
+
+ * dbxread.c, m-convex.h: Moved convex dependent include files over
+ from dbxread.c to m-convex.h.
+
+ * printcmd.c (disable_display, disable_display_command): Changed
+ name of first to second, and created first which takes an int as
+ arg rather than a char pointer. Changed second to use first.
+ (_initialize_printcmd): Changed to use second as command to call.
+ (delete_current_display, disable_current_display): Changed name of
+ first to second, and changed functionality to match.
+ * infrun.c (normal_stop), main.c (return_to_top_level): Changed to
+ call disable_current_display.
+
+ * dbxread.c (process_one_symbol, read_dbx_symtab): Changed N_FN to
+ be N_FN | N_EXT to deal with new Berkeley define; this works with
+ either the old or the new.
+
+ * Remote_Makefile, remote_gutils.c, remote_inflow.c,
+ remote_server.c, remote_utils.c: Created.
+ * Makefile: Included in tag and tar files.
+ * README: Included a note about them.
+
+ * printcmd.c (print_address): Use find_pc_partial_function to
+ remove need to readin symtabs for symbolic addresses.
+
+ * source.c (directory_command): Replaced function with new one
+ that can accept lists of directories seperated by spaces or :'s.
+
+ * inflow.c (new_tty): Replaced calls to dup2 with calls to dup.
+
+Sun May 14 12:33:16 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * stack.c (args_info): Make sure that you have an inferior or core
+ file before taking action.
+
+ * ns32k-opcode.h [deiw, deid]: Fixed machine code values for these
+ opcodes.
+
+ * dbxread.c (scan_file_globals): Modified to use misc function
+ vector instead of file itself. Killed all arguments to the
+ funciton; no longer needed.
+ (psymtab_to_symtab): Changed call for above to reflect new (void)
+ argument list.
+
+ * dbxread.c (read_dbx_symtab, ): Moved HASH_OFFSET define out of
+ read_dbx_symtab.
+
+ * expread.y [variable]: Changed default type of misc function in
+ text space to be (void ()).
+
+ * Makefile: Modified for proper number of s/r conflicts (order is
+ confusing; the mod that necessitated this change was on May 12th,
+ not today).
+
+ * expread.y (yylex): Added SIGNED, LONG, SHORT, and INT keywords.
+ [typename]: Created.
+ [typebase]: Added rules for LONG, LONG INT, SHORT, SHORT INT,
+ SIGNED name, and UNSIGNED name (a good approximation of ansi
+ standard).
+
+ * Makefile: Included .c.o rule to avoid sun's make from throwing
+ any curves at us.
+
+ * blockframe.c: Included <obstack.h>
+
+ * command.c (lookup_cmd): Clear out trailing whitespace.
+
+ * command.c (lookup_cmd_1): Changed malloc to alloca.
+
+Fri May 12 12:13:12 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * printcmd.c (print_frame_args): Only print nameless args when you
+ know how many args there are supposed to be and when you've
+ printed fewer than them. Don't print nameless args between
+ printed args.
+
+ * symtab.c (make_symbol_completion_function): Fixed typo (= ==>
+ ==).
+
+ * remote.c (remote_open): ifdef'd out siginterrupt call by #ifndef
+ NO_SIGINTERRUPT.
+ * m-umax.h: Defined NO_SIGINTERRUPT.
+
+ * expread.y [ptype, array_mod, func_mod, direct_abs_decl,
+ abs_decl]: Added rules for parsing and creating arbitrarily
+ strange types for casts and sizeofs.
+
+ * symtab.c, symtab.h (create_array_type): Created. Some minor
+ misfeatures; see comments for details (main one being that you
+ might end up creating two arrays when you only needed one).
+
+Thu May 11 13:11:49 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * valops.c (value_zero): Add an argument for type of lval.
+ * eval.c (evaluate_subexp_for_address): Take address properly in
+ the avoid side affects case (ie. keep track of whether we have an
+ lval in memory and we can take the address).
+ (evaluate_subexp): Set the lval type of expressions created with
+ value_zero properley.
+
+ * valops.c, value.h (value_zero): Created--will return a value of
+ any type with contents filled with zero.
+ * symtab.c, symtab.h (lookup_struct_elt_type): Created.
+ * eval.c (evaluate_subexp): Modified to not read memory when
+ called with EVAL_AVOID_SIDE_EFFECTS.
+
+ * Makefile: Moved dbxread.c ahead of coffread.c in the list of
+ source files.
+
+Wed May 10 11:29:19 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * munch: Make sure that sysv version substitutes for the whole
+ line.
+
+ * symtab.h: Created an enum misc_function_type to hold the type of
+ the misc function being recorded.
+ * dbxread.c (record_misc_function): Branched on dbx symbols to
+ decide which type to assign to a misc function.
+ * coffread.c (record_misc_function): Always assign type unknown.
+ * expread.y [variable]: Now tests based on new values.
+
+Tue May 9 13:03:54 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * symtab.c: Changed inclusion of <strings.h> (doesn't work on
+ SYSV) to declaration of index.
+
+ * Makefile: Changed last couple of READLINE_FLAGS SYSV_DEFINE
+
+ * source.c ({forward, reverse}_search_command): Made a default
+ search file similar to for the list command.
+
+Mon May 8 18:07:51 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * printcmd.c (print_frame_args): If we don't know how many
+ arguments there are to this function, don't print the nameless
+ arguments. We don't know enough to find them.
+
+ * printcmd.c (print_frame_args): Call print_frame_nameless_args
+ with proper arguments (start & end as offsets from addr).
+
+ * dbxread.c (read_addl_syms): Removed cases to deal with global
+ symbols; this should all be done in scan_global_symbols.
+
+Sun May 7 11:36:23 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * Makefile: Added copying.awk to ${OTHERS}.
+
+Fri May 5 16:49:01 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * valprint.c (type_print_varspec_prefix): Don't pass
+ passed_a_pointer onto children.
+
+ * valprint.c (type_print_varspec_suffix): Print "array of" with
+ whatever the "of" is after tha array brackets.
+
+ * valprint.c (type_print_varspec_{prefix,suffix}): Arrange to
+ parenthesisze pointers to arrays as well as pointers to other
+ objects.
+
+ * valprint.c (type_print_varspec_suffix): Make sure to print
+ subscripts of multi-dimensional arrays in the right order.
+
+ * infcmd.c (run_command): Fixed improper usages of variables
+ within remote debugging branch.
+
+ * Makefile: Added Convex.notes to the list of extra files to carry
+ around.
+
+ * dbxread.c (symbol_file_command): Made use of alloca or malloc
+ dependent on macro define.
+
+Thu May 4 15:47:04 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * Makefile: Changed READLINE_FLAGS to SYSV_DEFINE and called munch
+ with it also.
+ * munch: Check first argument for -DSYSV and be looser about
+ picking up init routines if you find it.
+
+ * coffread.c: Made fclose be of type int.
+
+ * breakpoint.c (_initialize_breakpoint): Put "unset" into class
+ alias.
+
+Wed May 3 14:09:12 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * m-sparc.h [STACK_END_ADDR]: Parameterized off of
+ machine/vmparam.h (as per John Gilmore's suggestion).
+
+ * blockframe.c (get_prev_frame_info): Changed this function back
+ to checking frameless invocation first before checking frame
+ chain. This means that a backtrace up from start will produce the
+ wrong value, but that a backtrace from a frameless function called
+ in main will show up correctly.
+
+ * breakpoint.c (_initialize_breakpoint): Added entry in help for
+ delete that indicates that unset is an alias for it.
+
+ * main.c (symbol_completion_function): Modified recognition of
+ being within a single command.
+
+Tue May 2 15:13:45 1989 Randy Smith (randy at gnu)
+
+ * expread.y [variable]: Add some parens to get checking of the
+ misc function vector right.
+
+Mon May 1 13:07:03 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * default-dep.c (core_file_command): Made reg_offset unsigned.
+
+ * default-dep.c (core_file_command): Improved error messages for
+ reading in registers.
+
+ * expread.y: Allowed a BLOCKNAME to be ok for a variable name (as
+ per C syntax).
+
+ * dbxread.c (psymtab_to_symtab): Flushed stdout after printing
+ starting message about reading in symbols.
+
+ * printcmd.c (print_frame_args): Switched starting place for
+ printing of frameless args to be sizeof int above last real arg
+ printed.
+
+ * printcmd.c (print_frame_args): Modified final call to
+ print_nameless_args to not use frame slots used array if none had
+ been used.
+
+ * infrun.c (wait_for_inferior): Take FUNCTION_START_OFFSET into
+ account when dealing with comparison of pc values to function
+ addresses.
+
+ * Makefile: Added note about compiling gdb on a Vax running 4.3.
+
+Sun Apr 30 12:59:46 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * command.c (lookup_cmd): Got correct error message on bad
+ command.
+
+ * m-sun3.h [ABOUT_TO_RETURN]: Modified to allow any of the return
+ instructions, including trapv and return from interupt.
+
+ * command.c (lookup_cmd): If a command is found, use it's values
+ for error reporting and determination of needed subcommands.
+
+ * command.c (lookup_cmd): Use null string for error if cmdtype is
+ null; pass *line to error instead of **.
+
+ * command.c (lookup_cmd_1): End of command marked by anything but
+ alpha numeric or '-'. Included ctype.h.
+
+Fri Apr 28 18:30:49 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * source.c (select_source_symtab): Kept line number from ever
+ being less than 1 in main decode.
+
+Wed Apr 26 13:03:20 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * default-dep.c (core_file_command): Fixed typo.
+
+ * utils.c (fprintf_filtered): Don't use return value from
+ numchars.
+
+ * main.c, command.c (complete_on_cmdlist): Moved function to
+ command.c.
+
+ * command.c (lookup_cmd): Modified to use my new routine. Old
+ version is still there, ifdef'd out.
+
+ * command.c, command.h (lookup_cmd_1): Added a routine to do all
+ of the work of lookup_cmd with no error reporting and full return
+ of information garnered in search.
+
+Tue Apr 25 12:37:54 1989 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * breakpoint.c (_initialize_breakpoint): Change "delete
+ breakpionts" to be in class alias and not have the abbrev flag
+ set.
+
+ * main.c (symbol_completion_function): Fix to correctly complete
+ things that correspond to multiword aliases.
+
+ * main.c (complete_on_cmdlist): Don't complete on something if it
+ isn't a command or prefix (ie. if it's just a help topic).
+
+ * main.c (symbol_completion_function): Set list index to be 0 if
+ creating a list with just one element.
+
+ * main.c (complete_on_cmdlist): Don't allow things with
+ abbrev_flag set to be completion values.
+ (symbol_completion_function): Don't accept an exact match if the
+ abbrev flag is set.
+
+ * dbxread.c (read_type): Fixed typo in comparision to check if
+ type number existed.
+
+ * dbxread.c (read_type): Made sure to only call dbx_lookup_type on
+ typenums if typenums were not -1.
+
+Mon Apr 24 17:52:12 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * symtab.c: Added strings.h as an include file.
+
+Fri Apr 21 15:28:38 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * symtab.c (lookup_partial_symtab): Changed to only return a match
+ if the name match is exact (which is what I want in all cases in
+ which this is currently used.
+
+Thu Apr 20 11:12:34 1989 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * m-isi.h [REGISTER_U_ADDR]: Installed new version from net.
+ * default-dep.c: Deleted inclusion of fcntl.h; apparently not
+ necessary.
+ * Makefile: Added comment about compiling on isi under 4.3.
+
+ * breakpoint.c (break_command_1): Only give decode_line_1 the
+ default_breakpoint_defaults if there's nothing better (ie. make
+ the default be off of the current_source notes if at all
+ possible).
+
+ * blockframe.c (get_prev_frame_info): Clean up comments and
+ delete code ifdefed out around FRAMELESS_FUNCTION_INVOCATION test.
+
+ * remote.c: Added a "?" message to protocol.
+ (remote_open): Used at startup.
+ (putpkt): Read whatever garbage comes over the line until we see a
+ '+' (ie. don't treat garbage as a timeout).
+
+ * valops.c (call_function): Eliminated no longer appropriate
+ comment.
+
+ * infrun.c (wait_for_inferior): Changed several convex conditional
+ compilations to be conditional on CANNOT_EXECUTE_STACK.
+
+Wed Apr 19 10:18:17 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * printcmd.c (print_frame_args): Added code to attempt to deal
+ with arguments that are bigger than an int.
+
+ Continuation of Convex/Fortran changes:
+ * printcmd.c (print_scalar_formatted): Added leading zeros to
+ printing of large integers.
+ (address_info, print_frame_args): Added code to deal with
+ LOC_REF_ARG.
+ (print_nameless_args): Allow param file to specify a routine with
+ which to print typeless integers.
+ (printf_command): Deal with long long values well.
+ * stack.c (print_frame_arg_vars): Change to deal with LOC_REF_ARG.
+ * symmisc.c (print_symbol): Change to deal with LOC_REF_ARG.
+ * symseg.h: Added LOC_REF_ARG to enum address_class.
+ * symtab.c (lookup_block_symbol): Changed to deal with
+ LOC_REF_ARG.
+ * valarith.c (value_subscripted_rvalue): Created.
+ (value_subscript): Used above when app.
+ (value_less, value_equal): Change to cast to (char *) before doing
+ comparison, for machines where that casting does something.
+ * valops.c (call_function): Setup to deal with machines where you
+ cannot execute code on the stack segment.
+ * valprint.c (val_print): Make sure that array element size isn't
+ zero before printing. Set address of default array to address of
+ first element. Put in a couple of int cast. Removed some convex
+ specific code. Added check for endianness of machine in case of a
+ packed structure. Added code for printing typeless integers and
+ for LONG LONG's.
+ (set_maximum_command): Change to use parse_and_eval_address to get
+ argument (so can use expressions there).
+ * values.c (value_of_internalvar, set_internalvar_component,
+ set_internalvar, convenience_info): Add in hooks for trapped
+ internal vars.
+ (unpack_long): Deal with LONG_LONG.
+ (value_field): Remove LONGEST cast.
+ (using_struct_return): Fixed typo ENUM ==> UNION.
+ * xgdb.c (_initialize_xgdb): Make sure that specify_exec_file_hook
+ is not called unless we are setting up a windowing environ.
+
+Tue Apr 18 13:43:37 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ Various changes involved in 1) getting gdb to work on the convex,
+ and 2) Getting gdb to work with fortran (due to convex!csmith):
+ * convex-dep.c, convex-opcode.h, m-convex.h, convex-pinsn.c:
+ Created (or replaced with new files).
+ * Makefile: Add convex dependent files. Changed default flags to
+ gnu malloc to be CFLAGS.
+ * config.gdb: Added convex to list of machines.
+ * core.c (files_info): Added a FILES_INFO_HOOK to be used if
+ defined.
+ (xfer_core_file): Conditionalized compilation of xfer_core_file on
+ the macro XFER_CORE_FILE.
+ * coffread.c (record_misc_function): Made sure it zerod type field
+ (which is now being used; see next).
+ * dbxread.c: Included some convex dependent include files.
+ (copy_pending, fix_common_blocks): Created.
+ [STAB_REG_REGNUM, BELIEVE_PCC_PROMOTION]: Created default values;
+ may be overridden in m-*.h.
+ Included data structures for keeping track of common blocks.
+ (dbx_alloc_type): Modified; if called with negative 1's will
+ create a type without putting it into the type vector.
+ (read_dbx_symtab, read_addl_syms): Modified calls to
+ record_misc_function to include the new information.
+ (symbol_file_command, psymtab_to_symtab, add_file_command):
+ Modified reading in of string table to adapt to machines which
+ *don't* store the size of the string table in the first four bytes
+ of the string table.
+ (read_dbx_symtab, scan_file_globals, read_ofile_symtab,
+ read_addl_syms): Modified assignment of namestring to accept null
+ index into symtab as ok.
+ (read_addl_syms): Modified readin of a new object file to fiddle
+ with common blocks correctly.
+ (process_one_symbol): Fixed incorrect comment about convex. Get
+ symbols local to a lexical context from correct spot on a per
+ machine basis. Catch a bug in pcc which occaisionally puts an SO
+ where there should be an SOL. Seperate sections for N_BCOMM &
+ N_ECOMM.
+ (define_symbol): Ignore symbols with no ":". Use
+ STAB_REG_TO_REGNUM. Added support for function args calling by
+ reference.
+ (read_type): Only read type number if one is there. Remove old
+ (#if 0'd out) array code.
+ (read_array_type): Added code for dealing with adjustable (by
+ parameter) arrays half-heartedly.
+ (read_enum_type): Allow a ',' to end a list of values.
+ (read_range_type): Added code to check for long long.
+ * expread.y: Modified to use LONGEST instead of long where
+ necessary. Modified to use a default type of int for objects that
+ weren't in text space.
+ * findvar.c (locate_var_value, read_var_value): Modified to deal
+ with args passed by reference.
+ * inflow.c (create_inferior): Used CREATE_INFERIOR_HOOK if it
+ exists.
+ * infrun.c (attach_program): Run terminal inferior when attaching.
+ (wait_for_inferior): Removed several convex dependencies.
+ * main.c (float_handler): Created.
+ Made whatever signal indicates a stop configurable (via macro
+ STOP_SIGNAL).
+ (main): Setup use of above as a signal handler. Added check for
+ "-nw" in args already processed.
+ (command_line_input): SIGTSTP ==>STOP_SIGNAL.
+
+ * expread.y: Added token BLOCKNAME to remove reduce/reduce
+ conflict.
+ * Makefile: Change message to reflect new grammar.
+
+Mon Apr 17 13:24:59 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * printcmd.c (compare_ints): Created.
+ (print_frame_args): Modified to always print arguments in the
+ order in which they were found in the symbol table. Figure out
+ what apots are missing on the fly.
+
+ * stack.c (up_command): Error if no inferior or core file.
+
+ * m-i386.h, m-symmetry.h [FRAMELESS_FUNCTION_INVOCATION]: Created;
+ same as m68k.
+
+ * dbxread.c (define_symbol): Changed "desc==0" test to
+ "processing_gcc_compilation", which is the correct way to do it.
+
+Sat Apr 15 17:18:38 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * expread.y: Added precedence rules for arglists, ?:, and sizeof
+ to eliminate some shift-reduce conflicts.
+ * Makefile: Modified "Expect" message to conform to new results.
+
+Thu Apr 13 12:29:26 1989 Randall Smith (randy at plantaris.ai.mit.edu)
+
+ * inflow.c (terminal_init_inferior): Fixed typo in recent diff
+ installation; TIOGETC ==> TIOCGETC.
+
+ * m-vax.h, m-sun2.h, m-sun3.h, m-sparc.h, m-hp*.h, m-isi.h,
+ m-news.h [FRAMELESS_FUNCTION_INVOCATION]: Created macro with
+ appropriate definition.
+
+Wed Apr 12 15:30:29 1989 Randall Smith (randy at plantaris.ai.mit.edu)
+
+ * blockframe.c (get_prev_frame_info): Added in a macro to specify
+ when a "frame" is called without a frame pointer being setup.
+
+ * Makefile [clean]: Made sure to delete gnu malloc if it was being
+ used.
+
+Mon Apr 10 12:43:49 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * dbxread.c (process_one_symbol): Reset within_function to 0 after
+ last RBRAC of a function.
+
+ * dbxread.c (read_struct_type): Changed check for filling in of
+ TYPE_MAIN_VARIANT of type.
+
+ * inflow.c (create_inferior): Conditionalized fork so that it
+ would be used if USG was defined and HAVE_VFORK was not defined.
+
+ * defs.h: Added comment about enum command_class element
+ class_alias.
+
+ * dbxread.c (process_one_symbol): Fixed a typo with interesting
+ implications for associative processing in the brain (':' ==> 'c').
+
+ * sparc-dep.c (isabranch): Changed name to isannulled, modified to
+ deal with coprocessor branches, and improved comment.
+ (single_step): Changed to trap at npc + 4 instead of pc +8 on
+ annulled branches. Changed name in call to isabranch as above.
+
+ * m-sun4os4.h (STACK_END_ADDRESS): Changed it to 0xf8000000 under
+ os 4.0.
+
+Sat Apr 8 17:04:07 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * dbxread.c (process_one_symbol): In the case N_FUN or N_FNAME the
+ value being refered to is sometimes just a text segment variable.
+ Catch this case.
+
+ * infrun.c (wait_for_inferior), breakpoint.c
+ (breakpoint_stop_status): Move the selection of the frame to
+ inside breakpoint_stop_status so that the frame only gets selected
+ (and the symbols potentially read in) if the symbols are needed.
+
+ * symtab.c (find_pc_psymbol): Fixed minor misthough (pc >=
+ fucntion start, not >).
+
+ * breakpoint.c (_initialize_breakpoint): Change "delete" internal
+ help entry to simply refer to it being a prefix command (since the
+ list of subcommands is right there on a "help delete").
+
+Fri Apr 7 15:22:18 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * blockframe.c (find_pc_partial_function): Created; figures out
+ what function pc is in (name and address) without reading in any
+ new symbols.
+ * symtab.h: Added decl for above.
+ * infrun.c (wait_for_inferior): Used instead of
+ find_pc_function_start.
+ * stack.c (print_frame_info): Used instead of hand coding for same
+ thing.
+
+ * dbxread.c (psymtab_to_symtab): No longer patch readin pst's out
+ of the partial_symtab_list; need them there for some checks.
+ * blockframe.c (block_for_pc), source.c (select_source_symtab),
+ symtab.c (lookup_symbol, find_pc_symtab, list_symbols): Made extra
+ sure not to call psymtab_to_symtab with ->readin == 1, since these
+ psymtab now stay on the list.
+ * symtab.c (sources_info): Now distinguishes between psymtabs with
+ readin set and those with it not set.
+
+ * symtab.c (lookup_symtab): Added check through partial symtabs
+ for name with .c appended.
+
+ * source.c (select_source_symtab): Changed semantics a little so
+ that the argument means something.
+ * source.c (list_command), symtab.c (decode_line_1): Changed call
+ to select_source_symtab to match new conventions.
+
+ * dbxread.c (add_file_command): This command no longer selects a
+ symbol table to list from.
+
+ * infrun.c (wait_for_inferior): Only call find_pc_function (to
+ find out if we have debugging symbols for a function and hence if
+ we should step over or into it) if we are doing a "step".
+
+Thu Apr 6 12:42:28 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * main.c (command_line_input): Added a local buffer and only
+ copied information into the global main.c buffer when it is
+ appropriate for it to be saved (and repeated).
+ (dont_repeat): Only nail line when we are reading from stdin
+ (otherwise null lines won't repeat and what's in line needs to be
+ saved).
+ (read_command_lines): Fixed typo; you don't what to repeat when
+ reading command lines from the input stream unless it's standard
+ input.
+
+ John Gilmore's (gnu@toad.com) mods for USG gdb:
+ * inflow.c: Removed inclusion of sys/user.h; no longer necessary.
+ (, terminal_init_inferior, terminal_inferior, terminal_ours_1,
+ term_status_command, _initialize_inflow) Seperated out declaration
+ and usage of terminal mode structures based on the existence of
+ the individual ioctls.
+ * utils.c (request_quit): Restore signal handler under USG. If
+ running under USG initialize sys_siglist at run time (too much
+ variation between systems).
+
+Wed Apr 5 13:47:24 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ John Gilmore's (gnu@toad.com) mods for USG gdb:
+ * default-dep.c: Moved include of sys/user.h to after include of
+ a.out.h.
+ (store_inferior_registers): Fixed error message.
+ (core_file_command): Improved error messages from reading in of
+ u area in core file. Changed calculation of offset of registers
+ to account for some machines putting it in as an offset rather
+ than an absolute address. Changed error messages for reading of
+ registers from core file.
+
+ * coffread.c (read_file_hdr): Added final check for BADMAG macro
+ to use if couldn't recognize magic number.
+ * Makefile: Added explicit directions for alloca addition.
+ Included alloca.c in list of possible library files. Cleaned up
+ possible library usage. Included additional information on gcc
+ and include files.
+
+ * source.c, remote.c, inflow.c, dbxread.c, core.c, coffread.c:
+ Changed include of sys/fcntl.h to an include of fcntl.h (as per
+ posix; presumably this will break fewer machines. I hopw).
+ * README: Added a pointer to comments at top of Makefile.
+ * Makefile: Added a comment about machines which need fcntl.h in
+ sys.
+
+Tue Apr 4 11:29:04 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * valprint.c (set_prettyprint_command, set_unionprint_command,
+ format_info): Created.
+ (_initialize_valprint): Added to lists of commands.
+
+ * gdb.texinfo [Backtrace]: Added a section describing the format
+ if symbols have not yet been read in.
+
+ * valprint.c (val_print): Added code to prettyprint structures if
+ "prettyprint" is set and only to print unions below the top level
+ if "unionprint" is set.
+
+ * infcmd.c (registers_info), valprint.c (value_print, val_print):
+ Added argument to call to val_print indicating deptch of recursion.
+
+ * symtab.[ch] (find_pc_psymbol): Created; finds static function
+ psymbol with value nearest to but under value passed.
+ * stack.c (print_frame_info): Used above to make sure I have best
+ fit to pc value.
+
+ * symseg.h (struct partial_symbol): Added value field.
+ * dbxread.c (read_dbx_symtab): Set value field for partial symbols
+ saved (so that we can lookup static symbols).
+
+ * symtab.[ch] (find_pc_symtab): Changed to external.
+ * stack.c (select_frame): Call above to make sure that symbols for
+ a selected frame is readin.
+
+Mon Apr 3 12:48:16 1989 Randall Smith (randy at plantaris.ai.mit.edu)
+
+ * stack.c (print_frame_info): Modified to only print out full
+ stack frame info on symbols whose tables have been read in.
+ * symtab.c, symtab.h (find_pc_psymtab): Made function external;
+ above needed it.
+
+ * main.c (,set_verbose_command, initialize_main): Created a
+ variable "info_verbose" which says to talk it up in various and
+ sundry places. Added command to set this variable.
+ * gdb.texinfo (GDB Output): Added documentation on "set verbose"
+ and changed the name of the "Screen Output" section to "GDB
+ Output".
+ * dbxread.c (psymtab_to_symtab): Added information message about
+ symbol readin. Conditionalized on above.
+
+ * dbxread.c (define_symbol): Made an "i" constant be of class
+ LOC_CONST and an "r" constant be of class LOC_CONST_BYTES.
+
+ * README: Made a note about modifications which may be necessary
+ to the manual for this version of gdb.
+
+ * blockframe.c (get_prev_frame_info): Now we get saved address and
+ check for validity before we check for leafism. This means that
+ we will catch the fact that we are in start, but we will miss any
+ fns that start calls without an fp. This should be fine.
+
+ * m-*.h (FRAME_CHAIN): Modified to return 0 if we are in start.
+ This is usually a test for within the first object file.
+ * m-sparc.h (FRAME_CHAIN): The test here is simply if the fp saved
+ off the the start sp is 0.
+
+ * blockframe.c (get_prev_frame_info): Removed check to see if we
+ were in start. Screws up sparc.
+
+ * m-sparc.h (FRAME_FIND_SAVED_REGISTERS): Changed test for dummy
+ frame to not need frame to be innermost.
+
+ * gdb.texinfo: Added section on frameless invocations of functions
+ and when gdb can and can't deal with this.
+
+ * stack.c (frame_info): Disallowed call if no inferior or core
+ file; fails gracefully if truely bad stack specfication has been
+ given (ie. parse_frame_specification returns 0).
+
+Fri Mar 31 13:59:33 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * infrun.c (normal_stop): Changed references to "unset-env" to
+ "delete env".
+
+ * infcmd.c (_initialize_infcmd): Change reference to set-args in
+ help run to "set args".
+
+ * remote.c (getpkt): Allow immediate quit when reading from
+ device; it could be hung.
+
+ * coffread.c (process_coff_symbol): Modify handling of REG
+ parameter symbols.
+
+Thu Mar 30 15:27:23 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * dbxread.c (symbol_file_command): Use malloc to allocate the
+ space for the string table in symbol_file_command (and setup a
+ cleanup for this). This allows a more graceful error failure if
+ there isn't any memory availible (and probably allows more memory
+ to be avail, depending on the machine).
+
+ Additional mods for handling GNU C++ (from Tiemann):
+ * dbxread.c (read_type): Added case for '#' type (method type, I
+ believe).
+ (read_struct_type): If type code is undefined, make the main
+ variant for the type be itself. Allow recognition of bad format
+ in reading of structure fields.
+ * eval.c (evaluate_subexp): Modify evaluation of a member of a
+ structure and pointer to same to make sure that the syntax is
+ being used correctly and that the member is being accessed correctly.
+ * symseg.h: Added TYPE_CODE_METHOD to enum type_code. Add a
+ pointer to an array of argument types to the type structure.
+ * symtab.c (lookout_method_type, smash_to_method_type): Created.
+ * symtab.h (TYPE_ARG_TYPES): Created.
+ * valops.c (call_function): Modified handling of methods to be the
+ same as handling of functions; no longer check for members.
+ * valprint.c (val_print, type_print_varspec_{prefix,suffix},
+ type_print_base): Added code to print method args correctly.
+ * values.c (value_virtual_fn_field): Modify access to virtual
+ function table.
+
+Wed Mar 29 13:19:34 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * findvar.c: Special cases for REGISTER_WINDOWS: 1) Return 0 if we
+ are the innermost frame, and 2) return the next frame in's value
+ if the SP is being looked for.
+
+ * blockframe.c (get_next_frame): Created; returns the next (inner)
+ frame of the called frame.
+ * frame.h: Extern delcaration for above.
+
+ * main.c (command_line_input): Stick null at end before doing
+ history expansion.
+
+Tue Mar 28 17:35:50 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * dbxread.c (read_dbx_symtab): Added namestring assignment to
+ N_DATA/BSS/ABS case. Sigh.
+
+Sat Mar 25 17:49:07 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * expread.y: Defined YYDEBUG.
+
+Fri Mar 24 20:46:55 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * symtab.c (make_symbol_completion_list): Completely rewrote to
+ never call psymtab_to_symtab, to do a correct search (no
+ duplicates) through the visible symbols, and to include structure
+ and union fields in the things that it can match.
+
+Thu Mar 23 15:27:44 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * dbxread.c (dbx_create_type): Created; allocates and inits space
+ for a type without putting it on the type vector lists.
+ (dbx_alloc_type): Uses above.
+
+ * Makefile: xgdb.o now produced by default rules for .o.c.
+
+Fri Mar 17 14:27:50 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * infrun.c: Fixed up inclusion of aouthdr.h on UMAX_PTRACE.
+
+ * Makefile, config.gdb: Added hp300bsd to potential
+ configurations.
+ * hp300bsd-dep.c, m-hp300bsd.h: Created.
+
+ * infrun.c (wait_for_inferior): Rewrote to do no access to
+ inferior until we make sure it's still there.
+
+ * inflow.c (inferior_died): Added a select to force the selected
+ frame to null when inferior dies.
+
+ * dbxread.c (symbol_file_command): free and zero symfile when
+ discarding symbols.
+
+ * core.c (xfer_core_file): Extended and cleaned up logic in
+ interpeting memory address.
+
+ * core.c (xfer_core_file): Extended opening comment.
+
+Thu Mar 16 15:39:42 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * coffread.c (symbol_file_command): Free symfile name when freeing
+ contents.
+
+ * blockframe.c (get_prev_frame_info): Added to fatal error message
+ to indicate that it should never happen.
+
+ * stack.c (frame_info): Printed out value of "saved" sp seperately
+ to call attention to the fact that it isn't stored in memory
+ anywhere; the actual previous frames address is printed.
+
+ * m-sparc.h (FRAME_FIND_SAVED_REGS): Set address of sp saved in
+ frame to value of fp (rather than value of sp in current frame).
+
+ * expread.y: Allow "unsigned" as a type itself, as well as a type
+ modifier.
+
+ * coffread.c: Added declaration for fclose
+
+Fri Mar 10 17:22:31 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * main.c (command_line_input): Checked for -1 return from
+ readline; indicates EOF.
+
+Fri Mar 3 00:31:27 1989 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * remote.c (remote_open): Cast return from signal to (void (*)) to
+ avoid problems on machines where the return type of signal is (int
+ (*)).
+
+ * Makefile: Removed deletion of version control from it (users
+ will need it for their changes).
+
+Thu Mar 2 15:32:21 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * symmetry-dep.c (print_1167_regs): Print out effective doubles on
+ even number regs.
+ (fetch_inferior_registers): Get the floating point regs also.
+
+ * xgdb.c (do_command): Copied command before calling execute
+ command (so that execute_command wouldn't write into text space).
+
+ * copying.awk: Created (will produce copying.c as output when
+ given COPYING as input).
+ * Makefile: Used above to create copying.c.
+ * main.c: Took out info_warranty and info_copying.
+
+ * *.*: Changed copyright notice to use new GNU General Public
+ License (includes necessary changes to manual).
+
+ * xgdb.c (create_text_widget): Created text_widget before I create
+ the source and sink.
+ (print_prompt): Added fflush (stdout).
+
+ * Makefile: Added -lXmu to the compilation line for xgdb. Left
+ the old one there incase people still had R2.
+
+ * README: Added note about -gg format.
+
+ * remote.c (getpkt): Fixed typo; && ==> &.
+
+ * Makefile: Added new variable READLINE_FLAGS so that I could
+ force compilation of readline.c and history.c with -DSYSV on
+ system V machines. Mentioned in Makefile comments at top.
+
+Wed Mar 1 17:01:01 1989 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * hp9k320-dep.c (store_inferior_registers): Fixed typo.
+
+Fri Feb 24 14:58:45 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * hp9k320-dep.c (store_inferior_registers,
+ fetch_inferior_registers): Added support for remote debugging.
+
+ * remote.c (remote_timer): Created.
+ (remote_open, readchar): Setup to timeout reads if they take
+ longer than "timeout". This allows one to debug how long such
+ things take.
+ (putpkt): Modified to print a debugging message (if such things
+ are enabled) each time it resends a packet.
+ (getpkt): Modified to make the variable CSUM unsigned and read it
+ CSUM with an & 0xff (presumably to deal with poor sign extension
+ on some machines). Also made c1 and c2 unsigned.
+ (remote_wait): Changed buffer to unsigned status.
+ (remote_store_registers, remote_write_bytes): Puts a null byte at
+ the end of the control string.
+
+ * infcmd.c (attach_command, detach_command, _initialize_infcmd):
+ Made attach_command and detach_command always availible, but
+ modified them to only allow device file attaches if ATTACH_DETACH
+ is not defined.
+
+ * gdb.texinfo: Added cross reference from attach command to remote
+ debugging.
+
+Thu Feb 23 12:37:59 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * remote.c (remote_close): Created to close the remote connection
+ and set the remote_debugging flag to 0.
+ * infcmd.c (detach_command): Now calls the above when appropriate.
+
+ * gdb.texinfo: Removed references to the ``Distribution'' section
+ in the copyright.
+
+ * main.c, utils.c (ISATTY): Created default defintions of this
+ macro which use isatty and fileno.
+ * utils.c (fprintf_filtered, print_spaces_filtered), main.c
+ (command_loop, command_line_input): Used this macro.
+ * m-news.h: Created a definition to override this one.
+
+ * utils.c (fprintf_filtered): Made line_size static (clueless).
+
+ * utils.c (fprintf_filtered): Changed max length of line printed
+ to be 255 chars or twice the format length.
+
+ * symmetry-dep.c, m-symmetry: Fixed typo (^L ==> ).
+
+ * printcmd.c (do_examine): Fixed typo (\n ==> \t).
+
+Wed Feb 22 16:00:33 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ Contributed by Jay Vosburgh (jay@mentor.cc.purdue.edu)
+ * m-symmetry.h, symmetry-dep.c: Created.
+ * Makefile: Added above in appropriate lists.
+ * config.gdb: Added "symmetry" target.
+
+ * utils.c (prompt_for_continue): Zero'd chars_printed also.
+
+ * utils.c (fprintf_filtered): Call prompt for continue instead of
+ doing it yourself.
+
+ * dbxread.c (read_dbx_symtab): Added code to conditionalize what
+ symbol type holds to "x.o" or "-lx" symbol that indicates the
+ beginning of a new file.
+
+Tue Feb 21 16:22:13 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * gdb.texinfo: Deleted @ignore block at end of file.
+
+ * findvar.c, stack.c: Changed comments that refered to "frame
+ address" to "frame id".
+
+ * findvar.c (locate_var_value): Modified so that taking the
+ address of an array generates an object whose type is a pointer to
+ the elements of the array.
+
+Sat Feb 18 16:35:14 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * gdb.texinfo: Removed reference to "!" as a shell escape
+ character. Added a section on controling screen output
+ (pagination); changing "Input" section to "User Interface"
+ section. Changed many inappropriate subsubsection nodes into
+ subsections nodes (in the readline and history expansion
+ sections).
+
+Fri Feb 17 11:10:54 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * utils.c (set_screensize_command): Created.
+ (_initialize_utils): Added above to setlist.
+
+ * main.c (main): Added check to see if ~/.gdbinit and .gdbinit
+ were the same file; only one gets read if so. Had to include
+ sys/stat.h for this.
+
+ * valprint.c (type_print_base): Changed calls to print_spaces to
+ print_spaces_filtered.
+
+ * main.c (command_line_input): Chaned test for command line
+ editing to check for stdin and isatty.
+
+ * main.c (command_loop): Call reinitialize_more_filter before each
+ command (if reading from stdin and it's a tty).
+ utils.c (initialize_more_filter): Changed name to
+ reinitialize_more_filter; killed arguments.
+ utils.c (_initialize_utils): Created; initialized lines_per_page
+ and chars_per_line here.
+
+ * utils.c (fprintf_filtered): Removed printing of "\\\n" after
+ printing linesize - 1 chars; assume that the screen display will
+ take care of that. Still watching that overflow.
+
+ * main.c: Created the global variables linesize and pagesize to
+ describe the number of chars per line and lines per page.
+
+Thu Feb 16 17:27:43 1989 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * printcmd.c (do_examine, print_scalar_formatted, print_address,
+ whatis_command, do_one_display, ptype_command), valprint.c
+ (value_print, val_print, type_print_method_args, type_print_1,
+ type_print_derivation_info, type_print_varspec_suffix,
+ type_print_base), breakpoint.c (breakpoints_info, breakpoint_1),
+ values.c (history_info), main.c (editing_info, warranty_info,
+ copying_info), infcmd.c (registers_info), inflow.c
+ (term_status_command), infrun.c (signals_info), stack.c
+ (backtrace_command, print_frame_info), symtab.c (list_symbols,
+ output_source_filename), command.c (help_cmd, help_list,
+ help_command_list): Replaced calls to printf, fprintf, and putc
+ with calls to [f]printf_filtered to handle more processing.
+ Killed local more emulations where I noticed them.
+
+Wed Feb 15 15:27:36 1989 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * defs.h, utils.c (initialize_more_filter, fprintf_filtered,
+ printf_filtered): Created a printf that will also act as a more
+ filter, prompting the user for a <return> whenever the page length
+ is overflowed.
+
+ * symtab.c (list_symbols): Elminated some code inside of an #if 0.
+
+Tue Feb 14 11:11:24 1989 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * Makefile: Turned off backup versions for this file; it changes
+ too often.
+
+ * command.c (lookup_cmd, _initialize_command): Changed '!' so that
+ it was no longer a shell escape. "sh" must be used.
+
+ * main.c (command_line_input, set_history_expansion,
+ initialize_main): Turned history expansion on, made it the
+ default, and only execute it if the first character in the line is
+ a '!'.
+
+ * version.c, gdb.texinfo: Moved version to 3.2 (as usual, jumping
+ the gun some time before release).
+
+ * gdb.texinfo: Added sections (adapted from Brian's notes) on
+ command line editing and history expansion.
+
+ * main.c (set_command_editing, initialize_main): Modified name to
+ set_editing and modified command to "set editing".
+
+ * Makefile: Put in dependencies for READLINEOBJS.
+
+ * main.c (history_info, command_info): Combined into new command
+ info; deleted history_info.
+ (initialize_main): Deleted "info history" command; it was
+ interfering with the value history.
+
+ * coffread.c (enter_linenos): Modified to do bit copy instead of
+ pointer dereference, since the clipper machine can't handle having
+ longs on short boundaries.
+ (read_file_hdr): Added code to get number of syms for clipper.
+
+ * stack.c (return_command): Fixed method for checking when all of
+ the necessary frames had been popped.
+
+ * dbxread.c (read_dbx_symtab (ADD_PSYMBOL_TO_LIST)): Fixed typo in
+ allocation length.
+
+Mon Feb 13 10:03:27 1989 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * dbxread.c (read_dbx_symtab): Split assignment to namestring into
+ several different assignments (so that it wouldn't be done except
+ when it had to be). Shortened switches and duplicated code to
+ produce the lowest possible execution time. Commented (at top of
+ switch) which code I duplicated.
+
+ * dbxread.c (read_dbx_symtab): Modified which variables were
+ register and deleted several variables which weren't used. Also
+ eliminated 'F' choice from subswitch, broke out strcmp's, reversed
+ compare on line 1986, and elminated test for !namestring[0]; it is
+ caught by following test for null index of ':'.
+
+Sun Feb 12 12:57:56 1989 Randall Smith (randy at plantaris.ai.mit.edu)
+
+ * main.c (gdb_completer_word_break_characters): Turned \~ into ~.
+
+Sat Feb 11 15:39:06 1989 Randall Smith (randy at plantaris.ai.mit.edu)
+
+ * symtab.c (find_pc_psymtab): Created; checks all psymtab's till
+ it finds pc.
+ (find_pc_symtab): Used; fatal error if psymtab found is readin
+ (should have been caught in symtab loop).
+ (lookup_symbol): Added check before scan through partial symtab
+ list for symbol name to be on the misc function vector (only if in
+ VAR_NAMESPACE). Also made sure that psymtab's weren't fooled with
+ if they had already been read in.
+ (list_symbols): Checked through misc_function_vector for matching
+ names if we were looking for functions.
+ (make_symbol_completion_list): Checked through
+ misc_function_vector for matching names.
+ * dbxread.c (read_dbx_symtab): Don't bother to do processing on
+ global function types; this will be taken care of by the
+ misc_function hack.
+
+ * symtab.h: Modified comment on misc_function structure.
+
+Fri Feb 10 18:09:33 1989 Randall Smith (randy at plantaris.ai.mit.edu)
+
+ * symseg.h, dbxread.c (read_dbx_symtab, init_psymbol_list,
+ start_psymtab, end_psymtab), coffread.c (_initialize_coff),
+ symtab.c (lookup_partial_symbol, list_symbols,
+ make_symbol_completion_list): Changed separate variables for
+ description of partial symbol allocation into a specific kind of
+ structure.
+
+ (read_dbx_symtab, process_symbol_for_psymtab): Moved most of
+ process_symbol_for_psymtab up into read_dbx_symtab, moved a couple
+ of symbol types down to the ingore section, streamlined (I hope)
+ code some, modularized access to psymbol lists.
+
+Thu Feb 9 13:21:19 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * main.c (command_line_input): Made sure that it could recognize
+ newlines as indications to repeat the last line.
+
+ * symtab.c (_initialize_symtab): Changed size of builtin_type_void
+ to be 1 for compatibility with gcc.
+
+ * main.c (initialize_main): Made history_expansion the default
+ when gdb is compiled with HISTORY_EXPANSION.
+
+ * readline.c, readline.h, history.c, history.h, general.h,
+ emacs_keymap.c, vi_keymap.c, keymaps.c, funmap.c: Made all of
+ these links to /gp/gnu/bash/* to keep them updated.
+ * main.c (initialize_main): Made default be command editing on.
+
+Wed Feb 8 13:32:04 1989 & Smith (randy at hobbes)
+
+ * dbxread.c (read_dbx_symtab): Ignore N_BSLINE on first
+ readthrough.
+
+ * Makefile: Removed convex-dep.c from list of distribution files.
+
+Tue Feb 7 14:06:25 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * main.c: Added command lists sethistlist and unsethistlist to
+ accesible command lists.
+ (parse_binary_operation): Created to parse a on/1/yes vs. off/0/no
+ spec.
+ (set_command_edit, set_history, set_history_expansion,
+ set_history_write, set_history_size, set_history_filename,
+ command_info, history_info): Created to allow users to control
+ various aspects of command line editing.
+
+ * main.c (symbol_creation_function): Created.
+ (command_line_input, initialize_main): Added rest of stuff
+ necessary for calling bfox' command editing routines under
+ run-time control.
+ * Makefile: Included readline and history source files for command
+ editing; also made arrangements to make sure that the termcap
+ library was available.
+ * symtab.c (make_symbol_completion_list): Created.
+
+Mon Feb 6 16:25:25 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * main.c: Invented variables to control command editing.
+ command_editing_p, history_expansion_p, history_size,
+ write_history_p, history_filename. Initialized them to default
+ values in initialize_main.
+
+ * infcmd.c (registers_info), infrun.c (signals_info),
+ * main.c (gdb_read_line): Changed name to command_line_input.
+ (readline): Changed name to gdb_readline; added second argument
+ indicating that the read value shouldn't be saved (via malloc).
+ * infcmd.c (registers_info), infrun.c (signals_info), main.c
+ (copying_info), symtab.c (output_source_filename, MORE,
+ list_symbols): Converted to use gdb_readline in place of
+ gdb_read_line.
+
+
+Sun Feb 5 17:34:38 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * blockframe.c (get_frame_saved_regs): Removed macro expansion
+ that had accidentally been left in the code.
+
+Sat Feb 4 17:54:14 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * main.c (gdb_read_line, readline): Added function readline and
+ converted gdb_read_line to use it. This was a conversion to the
+ line at a time style of input, in preparation for full command
+ editing.
+
+Fri Feb 3 12:39:03 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * dbxread.c (read_dbx_symtab): Call end_psymtab at the end of
+ read_dbx_symtab if any psymtab still needs to be completed.
+
+ * config.gdb, sun3-dep.c: Brought these into accord with the
+ actual sun2 status (no floating point period; sun3-dep.c unless
+ has os > 3.0).
+ * m-sun2os2.h: Deleted; not needed.
+
+ * config.gdb: Added a couple of aliases for machines in the
+ script.
+
+ * infrun.c: Added inclusion of aouthdr.h inside of #ifdef UMAX
+ because ptrace needs to know about the a.out header.
+
+ * Makefile: Made dep.o depend on dep.c and config.status only.
+
+ * expread.y: Added declarations of all of the new write_exp_elt
+ functions at the include section in the top.
+
+ * Makefile: Added a YACC definition so that people can use bison
+ if they wish.
+
+ * Makefile: Added rms' XGDB-README to the distribution.
+
+ * Makefile: Added removal of init.o on a "make clean".
+
+Thu Feb 2 16:27:06 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * *-dep.c: Deleted definition of COFF_FORMAT if AOUTHDR was
+ defined since 1) We *may* (recent mail message) want to define
+ AOUTHDR under a basically BSD system, and 2) AOUTHDR is sometimes
+ a typedef in coff encapsulation setups. Also removed #define's of
+ AOUTHDR if AOUTHDR is already defined (inside of coff format).
+ * core.c, dbxread.c: Removed #define's of AOUTHDR if AOUTHDR is
+ already defined (inside of coff format).
+
+Tue Jan 31 12:56:01 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * GDB 3.1 released.
+
+ * values.c (modify_field): Changed test for endianness to assign
+ to integer and reference character (so that all bits would be
+ defined).
+
+Mon Jan 30 11:41:21 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * news-dep.c: Deleted inclusion of fcntl.h; just duplicates stuff
+ found in sys/file.h.
+
+ * i386-dep.c: Included default definition of N_SET_MAGIC for
+ COFF_FORMAT.
+
+ * config.gdb: Added checks for several different operating
+ systems.
+
+ * coffread.c (read_struct_type): Put in a flag variable so that
+ one could tell when you got to the end of a structure.
+
+ * sun3-dep.c (core_file_command): Changed #ifdef based on SUNOS4
+ to ifdef based on FPU.
+
+ * infrun.c (restore_inferior_status): Changed error message to
+ "unable to restore previously selected frame".
+
+ * dbxread.c (read_dbx_symtab): Used intermediate variable in error
+ message reporting a bad symbol type. (scan_file_globals,
+ read_ofile_symtab, read_addl_syms): Data type of "type" changed to
+ unsigned char (which is what it is).
+ * i386-dep.c: Removed define of COFF_FORMAT if AOUTHDR is defined.
+ Removed define of a_magic to magic (taken care of by N_MAGIC).
+ (core_file_command): Zero'd core_aouthdr instead of setting magic
+ to zero.
+ * i386-pinsn.c: Changed jcxz == jCcxz in jump table.
+ (putop): Added a case for 'C'.
+ (OP_J): Added code to handle possible masking of PC value on
+ certain kinds of data.
+ m-i386gas.h: Moved COFF_ENCAPSULATE to before inclusion of
+ m-i386.h and defined NAMES_HAVE_UNDERSCORE.
+
+ * coffread.c (unrecrod_misc_function, read_coff_symtab): Added
+ symbol number on which error occured to error output.
+
+Fri Jan 27 11:55:04 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * Makefile: Removed init.c in make clean. Removed it without -f
+ and with leading - in make ?gdb.
+
+Thu Jan 26 15:08:03 1989 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ Changes to get it to work on gould NP1.
+ * dbxread.c (read_dbx_symtab): Included cases for N_NBDATA and
+ N_NBBSS.
+ (psymtab_to_symtab): Changed declaration of hdr to
+ DECLARE_FILE_HEADERS. Changed access to use STRING_TABLE_SIZE and
+ SYMBOL_TABLE_SIZE.
+ * gld-pinsn.c (findframe): Added declaration of framechain() as
+ FRAME_ADDR.
+
+ * coffread.c (read_coff_symtab): Avoided treating typedefs as
+ external symbol definitions.
+
+Wed Jan 25 14:45:43 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * Makefile: Removed reference to alloca.c. If they need it, they
+ can pull alloca.o from the gnu-emacs directory.
+
+ * version.c, gdb.texinfo: Updated version to 3.1 (jumping the gun
+ a bit so that I won't forget when I release).
+
+ * m-sun2.h, m-sun2os2.h, m-sun3os4.h, config.gdb: Modified code so
+ that default includes new sun core, ptrace, and attach-detach.
+ Added defaults for sun 2 os 2.
+
+ Modifications to reset stack limit back to what it used to be just
+ before exec. All mods inside of #ifdef SET_STACK_LIMIT_HUGE.
+ * main.c: Added global variable original_stack_limit.
+ (main): Set original_stack_limit to original stack limit.
+ * inflow.c: Added inclusion of necessary files and external
+ reference to original_stack_limit.
+ (create_inferior): Reset stack limit to original_stack_limit.
+
+ * dbxread.c (read_dbx_symtab): Killed PROFILE_SYMBOLS ifdef.
+
+ * sparc-dep.c (isabranch): Multiplied offset by 4 before adding it
+ to addr to get target.
+
+ * Makefile: Added definition of SHELL to Makefile.
+
+ * m-sun2os4.h: Added code to define NEW_SUN_PTRACE, NEW_SUN_CORE,
+ and ATTACH_DETACH.
+ * sun3-dep.c: Added code to avoid fp regs if we are on a sun2.
+
+Tue Jan 24 17:59:14 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * dbxread.c (read_array_type): Added function.
+ (read_type): Added call to above instead of inline code.
+
+ * Makefile: Added ${GNU_MALLOC} to the list of dependencies for
+ the executables.
+
+Mon Jan 23 15:08:51 1989 Randall Smith (randy at plantaris.ai.mit.edu)
+
+ * gdb.texinfo: Added paragraph to summary describing languages
+ with which gdb can be run. Also added descriptions of the
+ "info-methods" and "add-file" commands.
+
+ * symseg.h: Commented a range type as having TYPE_TARGET_TYPE
+ pointing at the containing type for the range (often int).
+ * dbxread.c (read_range_type): Added code to do actual range types
+ if they are defined. Assumed that the length of a range type is
+ the length of the target type; this is a lie, but will do until
+ somebody gets back to me as to what these silly dbx symbols mean.
+
+ * dbxread.c (read_range_type): Added code to be more picky about
+ recognizing builtins as range types, to treat types defined as
+ subranges of themselves to be subranges of int, and to recognize
+ the char type idiom from dbx as a special case.
+
+Sun Jan 22 01:00:13 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * m-vax.h: Removed definition of FUNCTION_HAS_FRAME_POINTER.
+ * blockframe.c (get_prev_frame_info): Removed default definition
+ and use of above. Instead conditionalized checking for leaf nodes
+ on FUNCTION_START_OFFSET (see comment in code).
+
+Sat Jan 21 16:59:19 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * dbxread.c (read_range_type): Fixed assumption that integer was
+ always type 1.
+
+ * gdb.texinfo: Fixed spelling mistake and added a note in the
+ running section making it clear that users may invoke subroutines
+ directly from gdb.
+
+ * blockframe.c: Setup a default definition for the macro
+ FUNCTION_HAS_FRAME_POINTER.
+ (get_prev_frame_info): Used this macro instead of checking
+ SKIP_PROLOGUE directly.
+ * m-vax.h: Overroad definition; all functions on the vax have
+ frame pointers.
+
+Fri Jan 20 12:25:35 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * core.c: Added default definition of N_MAGIC for COFF_FORMAT.
+
+ * xgdb.c: Installed a fix to keep the thing from dying when there
+ isn't any frame selected.
+
+ * core.c: Made a change for the UMAX system; needs a different
+ file included if using that core format.
+
+ * Makefile: Deleted duplicate obstack.h in dbxread.c dependency.
+
+ * munch: Modified (much simpler) to cover (I hope) all cases.
+
+ * utils.c (save_cleanups, restore_cleanups): Added functions to
+ allow you to push and pop the chain of cleanups to be done.
+ * defs.h: Declared the new functions.
+ * main.c (catch_errors): Made sure that the only cleanups which
+ would be done were the ones put on the chain *after* the current
+ location.
+
+ * m-*.h (FRAME_CHAIN_VALID): Removed check on pc in the current
+ frame being valid.
+ * blockframe.c (get_prev_frame_info): Made the assumption that if
+ a frame's pc value was within the first object file (presumed to
+ be /lib/crt0.o), that we shouldn't go any higher.
+
+ * infrun.c (wait_for_inferior): Do *not* execute check for stop pc
+ at step_resume_break if we are proceeding over a breakpoint (ie.
+ if trap_expected != 0).
+
+ * Makefile: Added -g to LDFLAGS.
+
+ * m-news.h (POP_FRAME) Fixed typo.
+
+ * printcmd.c (print_frame_args): Modified to print out register
+ params in order by .stabs entry, not by register number.
+
+ * sparc-opcode.h: Changed declaration of (struct
+ arith_imm_fmt).simm to be signed (as per architecture manual).
+ * sparc-pinsn.c (fprint_addr1, print_insn): Forced a cast to an
+ int, so that we really would get signed behaivior (default for sun
+ cc is unsigned).
+
+ * i386-dep.c (i386_get_frame_setup): Replace function with new
+ function provided by pace to fix bug in recognizing prologue.
+
+Thu Jan 19 11:01:22 1989 Randall Smith (randy at plantaris.ai.mit.edu)
+
+ * infcmd.c (run_command): Changed error message to "Program not
+ restarted."
+
+ * value.h: Changed "frame" field in value structure to be a
+ FRAME_ADDR (actually CORE_ADDR) so that it could survive across
+ calls.
+
+ * m-sun.h (FRAME_FIND_SAVED_REGS): Fixed a typo.
+
+ * value.h: Added lval: "lval_reg_frame_relative" to indicate a
+ register that must be interpeted relative to a frame. Added
+ single entry to value structure: "frame", used to indicate which
+ frame a relative regnum is relative to.
+ * findvar.c (value_from_register): Modified to correctly setup
+ these fields when needed. Deleted section to fiddle with last
+ register copied on little endian machine; multi register
+ structures will always occupy an integral number of registers.
+ (find_saved_register): Made extern.
+ * values.c (allocate_value, allocate_repeat_value): Zero frame
+ field on creation.
+ * valops.c (value_assign): Added case for lval_reg_frame_relative;
+ copy value out, modify it, and copy it back. Desclared
+ find_saved_register as being external.
+ * value.h: Removed addition of kludgy structure; thoroughly
+ commented file.
+ * values.c (free_value, free_all_values, clear_value_history,
+ set_internalvar, clear_internavars): Killed free_value.
+
+Wed Jan 18 20:09:39 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * value.h: Deleted struct partial_storage; left over from
+ yesterday.
+
+ * findvar.c (value_from_register): Added code to create a value of
+ type lval_reg_partsaved if a value is in seperate registers and
+ saved in different places.
+
+Tue Jan 17 13:50:18 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * value.h: Added lval_reg_partsaved to enum lval_type and
+ commented enum lval_type. Commented value structure.
+ Added "struct partial_register_saved" to value struct; added
+ macros to deal with structure to value.h.
+ * values.c (free_value): Created; special cases lval_reg_partsaved
+ (which has a pointer to an array which also needs to be free).
+ (free_all_values, clear_value_history, set_internalvar,
+ clear_internalvars): Modified to use free_values.
+
+ * m-sunos4.h: Changed name to sun3os4.h.
+ * m-sun2os4.h, m-sun4os4.h: Created.
+ * config.gdb: Added configuration entries for each of the above.
+ * Makefile: Added into correct lists.
+
+ * Makefile: Added dependencies on a.out.encap.h. Made
+ a.out.encap.h dependent on a.out.gnu.h and dbxread.c dependent on
+ stab.gnu.h.
+
+ * infrun.c, remote.c: Removed inclusion of any a.out.h files in
+ these files; they aren't needed.
+
+ * README: Added comment about bug reporting and comment about
+ xgdb.
+
+ * Makefile: Added note to HPUX dependent section warning about
+ problems if compiled with gcc and mentioning the need to add
+ -Ihp-include to CFLAGS if you compile on those systems. Added a
+ note about needing the GNU nm with compilers *of gdb* that use the
+ coff encapsulate feature also. * hp-include: Made symbolic link
+ over to /gp/gnu/binutils.
+
+ * Makefile: Added TSOBS NTSOBS OBSTACK and REGEX to list of things
+ to delete in "make clean". Also changed "squeakyclean" target as
+ "realclean".
+
+ * findvar.c (value_from_register): Added assignment of VALUE_LVAL
+ to be lval_memory when that is appropriate (original code didn't
+ bother because it assumed that it was working with a pre lval
+ memoried value).
+
+ * expread.y (yylex): Changed to only return type THIS if the
+ symbol "$this" is defined in some block superior or equal to the
+ current expression context block.
+
+Mon Jan 16 13:56:44 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * m-*.h (FRAME_CHAIN_VALID): On machines which check the relation
+ of FRAME_SAVED_PC (thisframe) to first_object_file_end (all except
+ gould), make sure that the pc of the current frame also passes (in
+ case someone stops in _start).
+
+ * findvar.c (value_of_register): Changed error message in case of
+ no inferior or core file.
+
+ * infcmd.c (registers_info): Added a check for inferior or core
+ file; error message if not.
+
+ * main.c (gdb_read_line): Modified to take prompt as argument and
+ output it to stdout.
+ * infcmd.c (registers_info, signals_info), main.c (command_loop,
+ read_command_lines, copying_info), symtab.c (decode_line_2,
+ output_source_filename, MORE, list_symbols): Changed calling
+ convention used to call gdb_read_line.
+
+ * infcmd.c, infrun.c, main.c, symtab.c: Changed the name of the
+ function "read_line" to "gdb_read_line".
+ * breakpoint.c: Deleted external referenced to function
+ "read_line" (not needed by code).
+
+Fri Jan 13 12:22:05 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * i386-dep.c: Include a.out.encap.h if COFF_ENCAPSULATE.
+ (N_SET_MAGIC): Defined if not defined by include file.
+ (core_file_command): Used N_SET_MAGIC instead of assignment to
+ a_magic.
+ (exec_file_command): Stuck in a HEADER_SEEK_FD.
+
+ * config.gdb: Added i386-dep.c as depfile for i386gas choice.
+
+ * munch: Added -I. to cc to pick up things included by the param
+ file.
+
+ * stab.gnu.def: Changed name to stab.def (stab.gnu.h needs this name).
+ * Makefile: Changed name here also.
+ * dbxread.c: Changed name of gnu-stab.h to stab.gnu.h.
+
+ * gnu-stab.h: Changed name to stab.gnu.h.
+ * stab.gnu.def: Added as link to binutils.
+ * Makefile: Put both in in the distribution.
+
+Thu Jan 12 11:33:49 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * dbxread.c: Made which stab.h is included dependent on
+ COFF_ENCAPSULATE; either <stab.h> or "gnu-stab.h".
+ * Makefile: Included gnu-stab.h in the list of files to include in
+ the distribution.
+ * gnu-stab.h: Made a link to /gp/gnu/binutils/stab.h
+
+ * Makefile: Included a.out.gnu.h and m-i386gas.h in list of
+ distribution files.
+ * m-i386gas.h: Changed to include m-i386.h and fiddle with it
+ instead of being a whole new file.
+ * a.out.gnu.h: Made a link to /gp/gnu/binutils/a.out.gnu.h.
+
+ Chris Hanson's changes to gdb for hp Unix.
+ * Makefile: Modified comments on hpux.
+ * hp9k320-dep.c: #define'd WOPR & moved inclusion of signal.h
+ * inflow.c: Moved around declaratiosn of <sys/fcntl.h> and
+ <sys/ioctl.h> inside of USG depends and deleted all SYSV ifdef's
+ (use USG instead).
+ * munch: Modified to accept any number of spaces between the T and
+ the symbol name.
+
+ Pace's changes to gdb to work with COFF_ENCAPSULATE (robotussin):
+ * config.gdb: Added i386gas to targets.
+ * default-dep.c: Include a.out.encap.h if COFF_ENCAPSULATE.
+ (N_SET_MAGIC): Defined if not defined by include file.
+ (core_file_command): Used N_SET_MAGIC instead of assignment to a_magic.
+ (exec_file_command): Stuck in a HEADER_SEEK_FD.
+ * infrun.c, remote.c: Added an include of a.out.encap.h if
+ COFF_ENCAPSULATE defined. This is commented out in these two
+ files, I presume because the definitions aren't used.
+ * m-i386gas.h: Created.
+ * dbxread.c: Included defintions for USG.
+ (READ_FILE_HEADERS): Now uses HEADER_SEEK_FD if it exists.
+ (symbol_file_command): Deleted use of HEADER_SEEK_FD.
+ * core.c: Deleted extra definition of COFF_FORMAT.
+ (N_MAGIC): Defined to be a_magic if not already defined.
+ (validate_files): USed N_MAGIC instead of reading a_magic.
+
+Wed Jan 11 12:51:00 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * remote.c: Upped PBUFSIZ.
+ (getpkt): Added zeroing of c inside loop in case of error retry.
+
+ * dbxread.c (read_dbx_symtab, process_symbol_for_psymtab): Removed
+ code to not put stuff with debugging symbols in the misc function
+ list. Had been ifdef'd out.
+
+ * gdb.texinfo: Added the fact that the return value for a function
+ is printed if you use return.
+
+ * infrun.c (wait_for_inferior): Removed test in "Have we hit
+ step_resume_breakpoint" for sp values in proper orientation. Was
+ in there for recursive calls in functions without frame pointers
+ and it was screwing up calls to alloca.
+
+ * dbxread.c: Added #ifdef COFF_ENCAPSULATE to include
+ a.out.encap.h.
+ (symbol_file_command): Do HEADER_SEEK_FD when defined.
+ * dbxread.c, core.c: Deleted #ifdef ROBOTUSSIN stuff.
+ * robotussin.h: Deleted local copy (was symlink).
+ * a.out.encap.h: Created symlink to
+ /gp/gnu/binutils/a.out.encap.h.
+ * Makefile: Removed robotussin.h and included a.out.encap.h in
+ list of files.
+
+ * valprint.c (val_print, print_scalar_formatted): Changed default
+ precision of printing float value; now 6 for a float and 16 for a
+ double.
+
+ * findvar.c (value_from_register): Added code to deal with the
+ case where a value is spread over several registers. Still don't
+ deal with the case when some registers are saved in memory and
+ some aren't.
+
+Tue Jan 10 17:04:04 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * xgdb.c (xgdb_create_window): Removed third arg (XtDepth) to
+ frameArgs.
+
+ * infrun.c (handle_command): Error if signal number is less or
+ equal to 0 or greater or equal to NSIG or a signal number is not
+ provided.
+
+ * command.c (lookup_cmd): Modified to not convert command section
+ of command line to lower case in place (in case it isn't a
+ subcommand, but an argument to a command).
+
+Fri Jan 6 17:57:34 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * dbxread.c: Changed "text area" to "data area" in comments on
+ N_SETV.
+
+Wed Jan 4 12:29:54 1989 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * dbxread.c: Added definitions of gnu symbol types after inclusion
+ of a.out.h and stab.h.
+
+Mon Jan 2 20:38:31 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * eval.c (evaluate_subexp): Binary logical operations needed to
+ know type to determine whether second value should be evaluated.
+ Modified to discover type before binup_user_defined_p branch.
+ Also commented "enum noside".
+
+ * Makefile: Changed invocations of munch to be "./munch".
+
+ * gdb.texinfo: Updated to refer to current version of gdb with
+ January 1989 last update.
+
+ * coffread.c (end_symtab): Zero context stack when finishing
+ lexical contexts.
+ (read_coff_symtab): error if context stack 0 in ".ef" else case.
+
+ * m-*.h (FRAME_SAVED_PC): Changed name of argument from "frame" to
+ "FRAME" to avoid problems with replacement of "->frame" part of
+ macro.
+
+ * i386-dep.c (i386_get_frame_setup): Added codestream_get() to
+ move codestream pointer up to the correct location in "subl $X,
+ %esp" case.
+
+Sun Jan 1 14:24:35 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * valprint.c (val_print): Rewrote routine to print string pointed
+ to by char pointer; was producing incorrect results when print_max
+ was 0.
+
+Fri Dec 30 12:13:35 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * dbxread.c (read_dbx_symtab, process_symbol_for_psymtab): Put
+ everything on the misc function list.
+
+ * Checkpointed distribution.
+
+ * Makefile: Added expread.tab.c to the list of things slated for
+ distribution.
+
+Thu Dec 29 10:06:41 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * stack.c (set_backtrace_limit_command, backtrace_limit_info,
+ bactrace_command, _initialize_stack): Removed modifications for
+ limit on backtrace. Piping the backtrace through an interuptable
+ "more" emulation is a better way to do it.
+
+Wed Dec 28 11:43:09 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * stack.c
+ (set_backtrace_limit_command): Added command to set a limit to the
+ number of frames for a backtrace to print by default.
+ (backtrace_limit_info): To print the current limit.
+ (backtrace_command): To use the limit.
+ (_initialize_stack): To initialize the limit to its default value
+ (30), and add the set and info commands onto the appropriate
+ command lists.
+
+ * gdb.texinfo: Documented changes to "backtrace" and "commands"
+ commands.
+
+ * stack.c (backtrace_command): Altered so that a negative argument
+ would show the last few frames on the stack instead of the first
+ few.
+ (_initialize_stack): Modified help documentation.
+
+ * breakpoint.c (commands_command): Altered so that "commands" with
+ no argument would refer to the last breakpoint set.
+ (_initialize_breakpoint): Modified help documentation.
+
+ * infrun.c (wait_for_inferior): Removed ifdef on Sun4; now you can
+ single step through compiler generated sub calls and will die if
+ you next off of the end of a function.
+
+ * sparc-dep.c (single_step): Fixed typo; "break_insn" ==> "sizeof
+ break_insn".
+
+ * m-sparc.h (INIT_EXTRA_FRAME_INFO): Set the bottom of a stack
+ frame to be the bottom of the stack frame inner from this, if that
+ inner one is a leaf node.
+
+ * dbxread.c (read_dbx_symtab): Check to make sure we don't add a
+ psymtab to it's own dependency list.
+
+ * dbxread.c (read_dbx_symtab): Modified check for duplicate
+ dependencies to catch them correctly.
+
+Tue Dec 27 17:02:09 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * m-*.h (FRAME_SAVED_PC): Modified macro to take frame info
+ pointer as argument.
+ * stack.c (frame_info), blockframe.c (get_prev_frame_info),
+ gld-pinsn.c (findframe), m-*.h (SAVED_PC_AFTER_CALL,
+ FRAME_CHAIN_VALID, FRAME_NUM_ARGS): Changed usage of macros to
+ conform to above.
+ * m-sparc.h (FRAME_SAVED_PC), sparc-dep.c (frame_saved_pc):
+ Changed frame_saved_pc to have a frame info pointer as an
+ argument.
+
+ * m-vax.h, m-umax.h, m-npl.h, infrun.c (wait_for_inferior),
+ blockframe.c (get_prev_frame_info): Modified SAVED_PC_AFTER_CALL
+ to take a frame info pointer as an argument.
+
+ * blockframe.c (get_prev_frame_info): Altered the use of the
+ macros FRAME_CHAIN, FRAME_CHAIN_VALID, and FRAME_CHAIN_COMBINE to
+ use frame info pointers as arguments instead of frame addresses.
+ * m-vax.h, m-umax.h, m-sun3.h, m-sun3.h, m-sparc.h, m-pn.h,
+ m-npl.h, m-news.h, m-merlin.h, m-isi.h, m-hp9k320.h, m-i386.h:
+ Modified definitions of the above macros to suit.
+ * m-pn.h, m-npl.h, gould-dep.c (findframe): Modified findframe to
+ use a frame info argument; also fixed internals (wouldn't work
+ before).
+
+ * m-sparc.h: Cosmetic changes; reordered some macros and made sure
+ that nothing went over 80 lines.
+
+Thu Dec 22 11:49:15 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * Version 3.0 released.
+
+ * README: Deleted note about changing -lobstack to obstack.o.
+
+Wed Dec 21 11:12:47 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * m-vax.h (SKIP_PROLOGUE): Now recognizes gcc prologue also.
+
+ * blockframe.c (get_prev_frame_info): Added FUNCTION_START_OFFSET
+ to result of get_pc_function_start.
+ * infrun.c (wait_for_inferior): Same.
+
+ * gdb.texinfo: Documented new "step" and "next" behavior in
+ functions without line number information.
+
+Tue Dec 20 18:00:45 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * infcmd.c (step_1): Changed behavior of "step" or "next" in a
+ function witout line number information. It now sets the step
+ range around the function (to single step out of it) using the
+ misc function vector, warns the user, and continues.
+
+ * symtab.c (find_pc_line): Zero "end" subsection of returned
+ symtab_and_line if no symtab found.
+
+Mon Dec 19 17:44:35 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * i386-pinsn.c (OP_REG): Added code from pace to streamline
+ disassembly and corrected types.
+ * i386-dep.c
+ (i386_follow_jump): Code added to follow byte and word offset
+ branches.
+ (i386_get_frame_setup): Expanded to deal with more wide ranging
+ function prologue.
+ (i386_frame_find_saved_regs, i386_skip_prologue): Changed to use
+ i386_get_frame_setup.
+
+
+Sun Dec 18 11:15:03 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * m-sparc.h: Deleted definition of SUN4_COMPILER_BUG; was designed
+ to avoid something that I consider a bug in our code, not theirs,
+ and which I fixed earlier. Also deleted definition of
+ CANNOT_USE_ARBITRARY_FRAME; no longer used anywhere.
+ FRAME_SPECIFICATION_DYADIC used instead.
+
+ * infrun.c (wait_for_inferior): On the sun 4, if a function
+ doesn't have a prologue, a next over it single steps into it.
+ This gets around the problem of a "call .stret4" at the end of
+ functions returning structures.
+ * m-sparc.h: Defined SUN4_COMPILER_FEATURE.
+
+ * main.c (copying_info): Seperated the last printf into two
+ printfs. The 386 compiler will now handle it.
+
+ * i386-pinsn.c, i386-dep.c: Moved print_387_control_word,
+ print_387_status_word, print_387_status, and i386_float_info to
+ dep.c Also included reg.h in dep.c.
+
+Sat Dec 17 15:31:38 1988 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * main.c (source_command): Don't close instream if it's null
+ (indicating execution of a user-defined command).
+ (execute_command): Set instream to null before executing
+ commands and setup clean stuff to put it back on error.
+
+ * inflow.c (terminal_inferior): Went back to not checking the
+ ioctl returns; there are some systems when this will simply fail.
+ It seems that, on most of these systems, nothing bad will happen
+ by that failure.
+
+ * values.c (value_static_field): Fixed dereferencing of null
+ pointer.
+
+ * i386-dep.c (i386_follow_jump): Modified to deal with
+ unconditional byte offsets also.
+
+ * dbxread.c (read_type): Fixed typo in function type case of switch.
+
+ * infcmd.c (run_command): Does not prompt to restart if command is
+ not from a tty.
+
+Fri Dec 16 15:21:58 1988 Randy Smith (randy at calvin)
+
+ * gdb.texinfo: Added a third option under the "Cannot Insert
+ Breakpoints" workarounds.
+
+ * printcmd.c (display_command): Don't do the display unless there
+ is an active inferior; only set it.
+
+ * findvar.c (value_of_register): Added an error check for calling
+ this when the inferior isn't active and a core file isn't being
+ read.
+
+ * config.gdb: Added reminder about modifying REGEX in the
+ makefile for the 386.
+
+ * i386-pinsn.c, i386-dep.c: Moved m-i386.h helper functions over
+ to i386-dep.c.b
+
+Thu Dec 15 14:04:25 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * README: Added a couple of notes about compiling gdb with itself.
+
+ * breakpoint.c (set_momentary_breakpoint): Only takes FRAME_FP of
+ frame if frame is non-zero.
+
+ * printcmd.c (print_scalar_formatted): Implemented /g size for
+ hexadecimal format on machines without an 8 byte integer type. It
+ seems to be non-trivial to implement /g for other formats.
+ (decode_format): Allowed hexadecimal format to make it through /g
+ fileter.
+
+Wed Dec 14 13:27:04 1988 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * expread.y: Converted all calls to write_exp_elt from the parser
+ to calls to one of write_exp_elt_{opcode, sym, longcst, dblcst,
+ char, type, intern}. Created all of these routines. This gets
+ around possible problems in passing one of these things in one ear
+ and getting something different out the other. Eliminated
+ SUN4_COMPILER_BUG ifdef's; they are now superfluous.
+
+ * symmisc.c (free_all_psymtabs): Reinited partial_symtab_list to 0.
+ (_initialize_symmisc): Initialized both symtab_list and
+ partial_symtab_list.
+
+ * dbxread.c (start_psymtab): Didn't allocate anything on
+ dependency list.
+ (end_psymtab): Allocate dependency list on psymbol obstack from
+ local list.
+ (add_psymtab_dependency): Deleted.
+ (read_dbx_symtab): Put dependency on local list if it isn't on it
+ already.
+
+ * symtab.c: Added definition of psymbol_obstack.
+ * symtab.h: Added declaration of psymbol_obstack.
+ * symmisc.c (free_all_psymtabs): Added freeing and
+ reinitionaliztion of psymbol_obstack.
+ * dbxread.c (free_all_psymbols): Deleted.
+ (start_psymtab, end_psymtab,
+ process_symbol_for_psymtab): Changed most allocation
+ of partial symbol stuff to be off of psymbol_obstack.
+
+ * symmisc.c (free_psymtab, free_all_psymtabs): Deleted
+ free_psymtab subroutine.
+
+ * symtab.h: Removed num_includes and includes from partial_symtab
+ structure; no longer needed now that all include files have their
+ own psymtab.
+ * dbxread.c (start_psymtab): Eliminated initialization of above.
+ (end_psymtab): Eliminated finalization of above; get
+ includes from seperate list.
+ (read_dbx_symtab): Moved includes from psymtab list to
+ their own list; included in call to end_psymtab.
+ * symmisc.c (free_psymtab): Don't free includes.
+
+Tue Dec 13 14:48:14 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * i386-pinsn.c: Reformatted entire file to correspond to gnu
+ software indentation conventions.
+
+ * sparc-dep.c (skip_prologue): Added capability of recognizign
+ stores of input register parameters into stack slots.
+
+ * sparc-dep.c: Added an include of sparc-opcode.h.
+ * sparc-pinsn.c, sparc-opcode.h: Moved insn_fmt structures and
+ unions from pinsn.c to opcode.h.
+ * sparc-pinsn.c, sparc-dep.c (isabranch, skip_prologue): Moved
+ this function from pinsn.c to dep.c.
+
+ * Makefile: Put in warnings about compiling with gcc (non-ansi
+ include files) and compiling with shared libs on Sunos 4.0 (can't
+ debug something that's been compiled that way).
+
+ * sparc-pinsn.c: Put in a completely new file (provided by
+ Tiemann) to handle floating point disassembly, load and store
+ instructions, and etc. better. Made the modifications this file
+ (ChangeLog) list for sparc-pinsn.c again.
+
+ * symtab.c (output_source_filename): Included "more" emulation hack.
+
+ * symtab.c (output_source_filename): Initialized COLUMN to 0.
+ (sources_info): Modified to not print out a line for
+ all of the include files within a partial symtab (since
+ they have pst's of their own now). Also modified to
+ make a distinction between those pst's read in and
+ those not.
+
+ * infrun.c: Included void declaration of single_step() if it's
+ going to be used.
+ * sparc-dep.c (single_step): Moved function previous to use of it.
+
+ * Makefile: Took removal of expread.tab.c out of make clean entry
+ and put it into a new "squeakyclean" entry.
+
+Mon Dec 12 13:21:02 1988 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * sparc-pinsn.c (skip_prologue): Changed a struct insn_fmt to a
+ union insn_fmt.
+
+ * inflow.c (terminal_inferior): Checked *all* return codes from
+ ioctl's and fcntl's in routine.
+
+ * inflow.c (terminal_inferior): Added check for sucess of
+ TIOCSPGRP ioctl call. Just notifies if bad.
+
+ * dbxread.c (symbol_file_command): Close was getting called twice;
+ once directly and once through cleanup. Killed the direct call.
+
+Sun Dec 11 19:40:40 1988 & Smith (randy at hobbes.ai.mit.edu)
+
+ * valprint.c (val_print): Deleted spurious printing of "=" from
+ TYPE_CODE_REF case.
+
+Sat Dec 10 16:41:07 1988 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * dbxread.c: Changed allocation of psymbols from using malloc and
+ realloc to using obstacks. This means they aren't realloc'd out
+ from under the pointers to them.
+
+Fri Dec 9 10:33:24 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * sparc-dep.c inflow.c core.c expread.y command.c infrun.c
+ infcmd.c dbxread.c symmisc.c symtab.c printcmd.c valprint.c
+ values.c source.c stack.c findvar.c breakpoint.c blockframe.c
+ main.c: Various cleanups inspired by "gcc -Wall" (without checking
+ for implicit declarations).
+
+ * Makefile: Cleaned up some more.
+
+ * valops.c, m-*.h (FIX_CALL_DUMMY): Modified to take 5 arguments
+ as per what sparc needs (programming for a superset of needed
+ args).
+
+ * dbxread.c (process_symbol_for_psymtab): Modified to be slightly
+ more picky about what it puts on the list of things *not* to be
+ put on the misc function list. When/if I shift everything over to
+ being placed on the misc_function_list, this will go away.
+
+ * inferior.h, infrun.c: Added fields to save in inferior_status
+ structure.
+
+ * maketarfile: Deleted; functionality is in Makefile now.
+
+ * infrun.c (wait_for_inferior): Modified algorithm for determining
+ whether or not a single-step was through a subroutine call. See
+ comments at top of file.
+
+ * dbxread.c (read_dbx_symtab): Made sure that the IGNORE_SYMBOL
+ macro would be checked during initial readin.
+
+ * dbxread.c (read_ofile_symtab): Added macro GCC_COMPILED_FLAG_SYMBOL
+ into dbxread.c to indicate what string in a local text symbol will
+ indicate a file compiled with gcc. Defaults to "gcc_compiled.".
+
+Thu Dec 8 11:46:22 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * m-sparc.h (FRAME_FIND_SAVED_REGS): Cleaned up a little to take
+ advantage of the new frame cache system.
+
+ * inferior.h, infrun.c, valops.c, valops.c, infcmd.c: Changed
+ mechanism to save inferior status over calls to inferior (eg.
+ call_function); implemented save_inferior_info and
+ restore_inferior_info.
+
+ * blockframe.c (get_prev_frame): Simplified this by a direct call
+ to get_prev_frame_info.
+
+ * frame.h, stack.c, printcmd.c, m-sparc.h, sparc-dep.c: Removed
+ all uses of frame_id_from_addr. There are short routines like it
+ still in frame_saved_pc (m-sparc.h) and parse_frame_spec
+ (stack.c). Eventually the one in frame_saved_pc will go away.
+
+ * infcmd.c, sparc-dep.c: Implemented a new mechanism for
+ re-selecting the selected frame on return from a call.
+
+ * blockframe.c, stack.c, findvar.c, printcmd.c, m-*.h: Changed
+ all routines and macros that took a "struct frame_info" as an
+ argument to take a "struct frame_info *". Routines: findarg,
+ framechain, print_frame_args, FRAME_ARGS_ADDRESS,
+ FRAME_STRUCT_ARGS_ADDRESS, FRAME_LOCALS_ADDRESS, FRAME_NUM_ARGS,
+ FRAME_FIND_SAVED_REGS.
+
+ * frame.h, stack.c, printcmd.c, infcmd.c, findvar.c, breakpoint.c,
+ blockframe.c, xgdb.c, i386-pinsn.c, gld-pinsn.c, m-umax.h,
+ m-sun2.h, m-sun3.h, m-sparc.h, m-pn.h, m-npl.h, m-news.h,
+ m-merlin.h, m-isi.h, m-i386.h, m-hp9k320.h: Changed routines to
+ use "struct frame_info *" internally.
+
+Wed Dec 7 12:07:54 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * frame.h, blockframe.c, m-sparc.h, sparc-dep.c: Changed all calls
+ to get_[prev_]frame_cache_item to get_[prev_]frame_info.
+
+ * blockframe.c: Elminated get_frame_cache_item and
+ get_prev_frame_cache_item; functionality now taken care of by
+ get_frame_info and get_prev_frame_info.
+
+ * blockframe.c: Put allocation on an obstack and eliminated fancy
+ reallocation routines, several variables, and various nasty
+ things.
+
+ * frame.h, stack.c, infrun.c, blockframe.c, sparc-dep.c: Changed
+ type FRAME to be a typedef to "struct frame_info *". Had to also
+ change routines that returned frame id's to return the pointer
+ instead of the cache index.
+
+ * infcmd.c (finish_command): Used proper method of getting from
+ function symbol to start of function. Was treating a symbol as a
+ value.
+
+ * blockframe.c, breakpoint.c, findvar.c, infcmd.c, stack.c,
+ xgdb.c, i386-pinsn.c, frame.h, m-hp9k320.h, m-i386.h, m-isi.h,
+ m-merlin.h, m-news.h, m-npl.h, m-pn.h, m-sparc.h, m-sun2.h,
+ m-sun3.h, m-umax.h: Changed get_frame_info and get_prev_frame_info
+ to return pointers instead of structures.
+
+ * blockframe.c (get_pc_function_start): Modified to go to misc
+ function table instead of bombing if pc was in a block without a
+ containing function.
+
+ * coffread.c: Dup'd descriptor passed to read_coff_symtab and
+ fdopen'd it so that there wouldn't be multiple closes on the same
+ fd. Also put (fclose, stream) on the cleanup list.
+
+ * printcmd.c, stack.c: Changed print_frame_args to take a
+ frame_info struct as argument instead of the address of the args
+ to the frame.
+
+ * m-i386.h (STORE_STRUCT_RETURN): Decremented sp by sizeof object
+ to store (an address) rather than 1.
+
+ * dbxread.c (read_dbx_symtab): Set first_object_file_end in
+ read_dbx_symtab (oops).
+
+ * coffread.c (fill_in_vptr_fieldno): Rewrote TYPE_BASECLASS as
+ necessary.
+
+Tue Dec 6 13:03:43 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * coffread.c: Added fake support for partial_symtabs to allow
+ compilation and execution without there use.
+ * inflow.c: Added a couple of minor USG mods.
+ * munch: Put in appropriate conditionals so that it would work on
+ USG systems.
+ * Makefile: Made regex.* handled same as obstack.*; made sure tar
+ file included everything I wanted it to include (including
+ malloc.c).
+
+ * dbxread.c (end_psymtab): Create an entry in the
+ partial_symtab_list for each subfile of the .o file just read in.
+ This allows a "list expread.y:10" to work when we haven't read in
+ expread.o's symbol stuff yet.
+
+ * symtab.h, dbxread.c (psymtab_to_symtab): Recognize pst->ldsymlen
+ == 0 as indicating a dummy psymtab, only in existence to cause the
+ dependency list to be read in.
+
+ * dbxread.c (sort_symtab_syms): Elminated reversal of symbols to
+ make sure that register debug symbol decls always come before
+ parameter symbols. After mod below, this is not needed.
+
+ * symtab.c (lookup_block_symbol): Take parameter type symbols
+ (LOC_ARG or LOC_REGPARM) after any other symbols which match.
+
+ * dbxread.c (read_type): When defining a type in terms of some
+ other type and the other type is supposed to have a pointer back
+ to this specific kind of type (pointer, reference, or function),
+ check to see if *that* type has been created yet. If it has, use
+ it and fill in the appropriate slot with a pointer to it.
+
+Mon Dec 5 11:25:04 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * symmisc.c: Eliminated existence of free_inclink_symtabs and
+ init_free_inclink_symtabs; they aren't called from anywhere, and
+ if they were they could disrupt gdb's data structure badly
+ (elimination of struct type's which values that stick around past
+ elimination of inclink symtabs).
+
+ * dbxread.c (symbol_file_command): Fixed a return pathway out of
+ the routine to do_cleanups before it left.
+
+ * infcmd.c (set_environment_command), gdb.texinfo: Added
+ capability to set environmental variable values to null.
+
+ * gdb.texinfo: Modified doc on "break" without args slightly.
+
+Sun Dec 4 17:03:16 1988 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * dbxread.c (symbol_file_command): Added check; if there weren't
+ any debugging symbols in the file just read, the user is warned.
+
+ * infcmd.c: Commented set_environment_command (a little).
+
+ * createtags: Cleaned up and commented.
+
+ * Makefile: Updated depen_memory and write_inferior_memory in that errno is
+ checked after each ptrace and returned to the caller. Used in
+ value_at to detect references to addresses which are out of
+ bounds. Also core.c (xfer_core_file): return 1 if invalid
+ address, 0 otherwise.
+
+ * inflow.c, <machine>-infdep.c: removed all calls to ptrace from
+ inflo, m-sun3.h: Cleaned up dealings with
+ functions returning structu0 19:19:36 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * symmisc.c: (read_symsegs) Accept only format number 2. Since
+ the size of the type structure changed when C++ support was added,
+ format 1 can no longer be used.
+
+ * core.c, m-sunos4.h: (core_file_command) support for SunOS 4.0.
+ Slight change in the core structure. #ifdef SUNOS4. New file
+ m-sunos4.h. May want to change config.gdb also.
+
+Fri Jul 8 19:59:49 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * breakpoint.c: (break_command_1) Allow `break if condition'
+ rather than parsing `if' as a function name and returning an
+ error.
+
+Thu Jul 7 22:22:47 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * C++: valops.c, valprint.c, value.h, values.c: merged code to deal
+ with C++ expressions.
+
+Wed Jul 6 03:28:18 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * C++: dbxread.c: (read_dbx_symtab, condense_misc_bunches,
+ add_file_command) Merged code to read symbol information from
+ an incrementally linked file. symmisc.c:
+ (init_free_inclink_symtabs, free_inclink_symtabs) Cleanup
+ routines.
+
+Tue Jul 5 02:50:41 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * C++: symtab.c, breakpoint.c, source.c: Merged code to deal with
+ ambiguous line specifications. In C++ one can have overloaded
+ function names, so that `list classname::overloadedfuncname'
+ refers to several different lines, possibly sure currently configured machine
+ dependent files come first in e at corn-chex.ai.mit.edu)
+
+ * C++: symtab.c: replaced lookup_symtab_1 and lookup_symtab_2 with
+ a modified lookup_symbol which checks for fields of the current
+ implied argument `this'. printcmd.c, source.c, symtab.c,
+ valops.c: Need to change callers once callers are
+ installed.
+
+Wed Jun 29 01:26:56 1988 Peter TerMaat (pete at frosted-flakes.ai.mit.edu)
+
+ * C++: eval.c, expprint.c, expread.y, expression.h, valarith.c,
+ Merged code to deal with evaluation of user-defined operators,
+ member functions, and virtual functions.
+ binop_must_be_user_defined tests for user-defined binops,
+ value_x_binop calls the appropriate operator function.
+
+Tue Jun 28 02:56:42 1988 Peter TerMaat (pete at frosted-flakes.ai.mit.edu)
+
+ * C++: Makefile: changed the echo: expect 101 shift/reduce conflicts
+ and 1 reduce/reduce conflict.
+
+
+Local Variables:
+mode: indented-text
+eval: (auto-fill-mode 1)
+left-margin: 8
+fill-column: 74
+version-control: never
+End:
+
+ constructors, and flags being defined via public and via
+ virtual paths. Added fields NEXT_VARIANT, N_BASECLASSES,
+ and BASECLASSES to this type (tr: Changed types from
+ having to be derived from a single baseclass to a multiple
+ base class).
+ * symtab.h: Added macros to access new fields defined in symseg.h.
+ Added decl for lookup_basetype_type.
+ * dbxread.c
+ (condense_addl_misc_bunches): Function added to condense the misc
+ function bunches added by reading in a new .o file.
+ (read_addl_syms): Function added to read in symbols
+ from a new .o file (incremental linking).
+ (add_file_command): Command interface function to indicate
+ incrmental linking of a new .o file; this now calls
+ read_addl_syms and condense_addl_misc_bunches.
+ (define_symbol): Modified code to handle types defined from base
+ types which were not known when the derived class was
+ output.
+ (read_struct_type): Modified to better handle description of
+ struct types as derived types. Possibly derived from
+ several different base classes. Also added new code to
+ mark definitions via virtual paths or via public paths.
+ Killed seperate code to handle classes with destructors
+ but without constructors and improved marking of classes
+ as having destructors and constructors.
+ * infcmd.c: Modified call to val_print (one more argument).
+ * symtab.c (lookup_member_type): Modified to deal with new
+ structure in symseg.h.
+ (lookup_basetype_type): Function added to find or construct a type
+ ?derived? from the given type.
+ (decode_line_1): Modified to deal with new type data structures.
+ Modified to deal with new number of args for
+ decode_line_2.
+ (decode_line_2): Changed number of args (?why?).
+ (init_type): Added inits for new C++ fields from
+ symseg.h.
+ *valarith.c
+ (value_x_binop, value_binop): Added cases for BINOP_MIN &
+ BINOP_MAX.
+ * valops.c
+ (value_struct_elt, check_field, value_struct_elt_for_address):
+ Changed to deal with multiple possible baseclasses.
+ (value_of_this): Made SELECTED_FRAME an extern variable.
+ * valprint.c
+ (val_print): Added an argument DEREF_REF to dereference references
+ automatically, instead of printing them like pointers.
+ Changed number of arguments in recursive calls to itself.
+ Changed to deal with varibale numbers of base classes.
+ (value_print): Changed number of arguments to val_print. Print
+ type of value also if value is a reference.
+ (type_print_derivation_info): Added function to print out
+ derivation info a a type.
+ (type_print_base): Modified to use type_print_derivation_info and
+ to handle multiple baseclasses.
+
+Mon Nov 21 10:32:07 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * inflow.c (term_status_command): Add trailing newline to output.
+
+ * sparc-dep.c (do_save_insn, do_restore_insn): Saved
+ "stop_registers" over the call for the sake of normal_stop and
+ run_stack_dummy.
+
+ * m-sparc.h (EXTRACT_RETURN_VALUE): Put in parenthesis to force
+ addition of 8 to the int pointer, not the char pointer.
+
+ * sparc-pinsn.c (print_addr1): Believe that I have gotten the
+ syntax right for loads and stores as adb does it.
+
+ * symtab.c (list_symbols): Turned search for match on rexegp into
+ a single loop.
+
+ * dbxread.c (psymtab_to_symtab): Don't read it in if it's already
+ been read in.
+
+ * dbxread.c (psymtab_to_symtab): Changed error to fatal in
+ psymtab_to_symtab.
+
+ * expread.y (parse_number): Fixed bug which treated 'l' at end of
+ number as '0'.
+
+Fri Nov 18 13:57:33 1988 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * dbxread.c (read_dbx_symtab, process_symbol_for_psymtab): Was
+ being foolish and using pointers into an array I could realloc.
+ Converted these pointers into integers.
+
+Wed Nov 16 11:43:10 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * m-sparc.h (POP_FRAME): Made the new frame be PC_ADJUST of the
+ old frame.
+
+ * i386-pinsn.c, m-hp9k320.h, m-isi.h, m-merlin.h, m-news.h,
+ m-npl.h, m-pn.h, m-sparc.h, m-sun2.h, m-sun3.h, m-umax.h, m-vax.h:
+ Modified POP_FRAME to use the current frame instead of
+ read_register (FP_REGNUM) and to flush_cached_frames before
+ setting the current frame. Also added a call to set the current
+ frame in those POP_FRAMEs that didn't have it.
+
+ * infrun.c (wait_for_inferior): Moved call to set_current_frame up
+ to guarrantee that the current frame will always be set when a
+ POP_FRAME is done.
+
+ * infrun.c (normal_stop): Added something to reset the pc of the
+ current frame (was incorrect because of DECR_PC_AFTER_BREAK).
+
+ * valprint.c (val_print): Changed to check to see if a string was
+ out of bounds when being printed and to indicate this if so.
+
+ * convex-dep.c (read_inferior_memory): Changed to return the value
+ of errno if the call failed (which will be 0 if the call
+ suceeded).
+
+Tue Nov 15 10:17:15 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * infrun.c (wait_for_inferior): Two changes: 1) Added code to
+ not trigger the step breakpoint on recursive calls to functions
+ without frame info, and 2) Added calls to distinguish recursive
+ calls within a function without a frame (which next/nexti might
+ wish to step over) from jumps to the beginning of a function
+ (which it generally doesn't).
+
+ * m-sparc.h (INIT_EXTRA_FRAME_INFO): Bottom set correctly for leaf
+ parents.
+
+ * blockframe.c (get_prev_frame_cache_item): Put in mod to check
+ for a leaf node (by presence or lack of function prologue). If
+ there is a leaf node, it is assumed that SAVED_PC_AFTER_CALL is
+ valid. Otherwise, FRAME_SAVED_PC or read_pc is used.
+
+ * blockframe.c, frame.h: Did final deletion of unused routines and
+ commented problems with getting a pointer into the frame cache in
+ the frame_info structure comment.
+
+ * blockframe.c, frame.h, stack.c: Killed use of
+ frame_id_from_frame_info; used frame_id_from_addr instead.
+
+ * blockframe.c, frame.h, stack.c, others (oops): Combined stack
+ cache and frame info structures.
+
+ * blockframe.c, sparc-dep.c, stack.c: Created the function
+ create_new_frame and used it in place of bad calls to
+ frame_id_from_addr.
+
+ * blockframe.c, inflow.c, infrun.c, i386-pinsn.c, m-hp9k320.h,
+ m-npl.h, m-pn.h, m-sparc.h, m-sun3.h, m-vax.h, default-dep.c,
+ convex-dep.c, gould-dep.c, hp9k320-dep.c, news-dep.c, sparc-dep.c,
+ sun3-dep.c, umax-dep.c: Killed use of
+ set_current_Frame_by_address. Used set_current_frame
+ (create_new_frame...) instead.
+
+ * frame.h: Killed use of FRAME_FP_ID.
+
+ * infrun.c, blockframe.c: Killed select_frame_by_address. Used
+ select_frame (get_current_frame (), 0) (which was correct in all
+ cases that we need to worry about.
+
+Mon Nov 14 14:19:32 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * frame.h, blockframe.c, stack.c, m-sparc.h, sparc-dep.c: Added
+ mechanisms to deal with possible specification of frames
+ dyadically.
+
+Sun Nov 13 16:03:32 1988 Richard Stallman (rms at sugar-bombs.ai.mit.edu)
+
+ * ns32k-opcode.h: Add insns acbw, acbd.
+
+Sun Nov 13 15:09:58 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * breakpoint.c: Changed breakpoint structure to use the address of
+ a given frame (constant across inferior runs) as the criteria for
+ stopping instead of the frame ident (which varies across inferior
+ calls).
+
+Fri Nov 11 13:00:22 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * gld-pinsn.c (findframe): Modified to work with the new frame
+ id's. Actually, it looks as if this routine should be called with
+ an address anyway.
+
+ * findvar.c (find_saved_register): Altered bactrace loop to work
+ off of frames and not frame infos.
+
+ * frame.h, blockframe.c, stack.c, sparc-dep.c, m-sparc.h: Changed
+ FRAME from being the address of the frame to being a simple ident
+ which is an index into the frame_cache_item list.
+ * convex-dep.c, default-dep.c, gould-dep.c, hp9k320-dep.c,
+ i386-pinsn.c, inflow.c, infrun.c, news-dep.c, sparc-dep.c,
+ sun3-dep.c, umax-dep.c, m-hp9k320.h, m-npl.h, m-pn.h, m-sparc.h,
+ m-sun3.h, m-vax.h: Changed calls of the form set_current_frame
+ (read_register (FP_REGNUM)) to set_current_frame_by_address (...).
+
+Thu Nov 10 16:57:57 1988 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * frame.h, blockframe.c, gld-pinsn.c, sparc-dep.c, stack.c,
+ infrun.c, findvar.c, m-sparc.h: Changed the FRAME type to be
+ purely an identifier, using FRAME_FP and FRAME_FP_ID to convert
+ back and forth between the two. The identifier is *currently*
+ still the frame pointer value for that frame.
+
+Wed Nov 9 17:28:14 1988 Chris Hanson (cph at kleph)
+
+ * m-hp9k320.h (FP_REGISTER_ADDR): Redefine this to return
+ difference between address of given FP register, and beginning of
+ `struct user' that it occurs in.
+
+ * hp9k320-dep.c (core_file_command): Fix sign error in size
+ argument to myread. Change buffer argument to pointer; was
+ copying entire structure.
+ (fetch_inferior_registers, store_inferior_registers): Replace
+ occurrences of `FP_REGISTER_ADDR_DIFF' with `FP_REGISTER_ADDR'.
+ Flush former definition.
+
+Wed Nov 9 12:11:37 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * xgdb.c: Killed include of initialize.h.
+
+ * Pulled in xgdb.c from the net.
+
+ * Checkpointed distribution (to provide to 3b2 guy).
+
+ * coffread.c, dbxread.c, symmisc.c, symtab.c, symseg.h: Changed
+ format of table of line number--pc mapping information. Can
+ handle negative pc's now.
+
+ * command.c: Deleted local copy of savestring; code in utils.c is
+ identical.
+
+Tue Nov 8 11:12:16 1988 Randall Smith (randy at plantaris.ai.mit.edu)
+
+ * gdb.texinfo: Added documentation for shell escape.
+
+Mon Nov 7 12:27:16 1988 Randall Smith (randy at sugar-bombs.ai.mit.edu)
+
+ * command.c: Added commands for shell escape.
+
+ * core.c, dbxread.c: Added ROBOTUSSIN mods.
+
+ * Checkpointed distribution.
+
+ * printcmd.c (x_command): Yanked error if there is no memory to
+ examine (could be looking at executable straight).
+
+ * sparc-pinsn.c (print_insn): Amount to leftshift sethi imm by is
+ now 10 (matches adb in output).
+
+ * printcmd.c (x_command): Don't attempt to set $_ & $__ if there
+ is no last_examine_value (can happen if you did an x/0).
+
+Fri Nov 4 13:44:49 1988 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * printcmd.c (x_command): Error if there is no memory to examine.
+
+ * gdb.texinfo: Added "cont" to the command index.
+
+ * sparc-dep.c (do_save_insn): Fixed typo in shift amount.
+
+ * m68k-opcode.h: Fixed opcodes for 68881.
+
+ * breakpoint.c, infcmd.c, source.c: Changed defaults in several
+ places for decode_line_1 to work off of the default_breakpoint_*
+ values instead of current_source_* values (the current_source_*
+ values are off by 5 or so because of listing defaults).
+
+ * stack.c (frame_info): ifdef'd out FRAME_SPECIFCATION_DYADIC in
+ the stack.c module. If I can't do this right, I don't want to do
+ it at all. Read the comment there for more info.
+
+Mon Oct 31 16:23:06 1988 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * gdb.texinfo: Added documentation on the "until" command.
+
+Sat Oct 29 17:47:10 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * breakpoint.c, infcmd.c: Added UNTIL_COMMAND and subroutines of
+ it.
+
+ * breakpoint.c, infcmd.c, infrun.c: Added new field to breakpoint
+ structure (silent, indicating a silent breakpoint), and modified
+ breakpoint_stop_status and things that read it's return value to
+ understand it.
+
+Fri Oct 28 17:45:33 1988 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * dbxread.c, symmisc.c: Assorted speedups for readin, including
+ special casing most common symbols, and doing buffering instead of
+ calling malloc.
+
+Thu Oct 27 11:11:15 1988 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * stack.c, sparc-dep.c, m-sparc.h: Modified to allow "info frame"
+ to take two arguments on the sparc and do the right thing with
+ them.
+
+ * dbxread.c (read_dbx_symtab, process_symbol_for_psymtab): Put
+ stuff to put only symbols that didn't have debugging info on the
+ misc functions list back in.
+
+Wed Oct 26 10:10:32 1988 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * valprint.c (type_print_varspec_suffix): Added check for
+ TYPE_LENGTH(TYPE_TARGET_TYPE(type)) > 0 to prevent divide by 0.
+
+ * printcmd.c (print_formatted): Added check for VALUE_REPEATED;
+ value_print needs to be called for that.
+
+ * infrun.c (wait_for_inferior): Added break when you decide to
+ stop on a null function prologue rather than continue stepping.
+
+ * m-sun3.h: Added explanatory comment to REGISTER_RAW_SIZE.
+
+ * expread.y (parse_c_1): Initialized paren_depth for each parse.
+
+Tue Oct 25 14:19:38 1988 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * valprint.c, coffread.c, dbxread.c: Enum constant values in enum
+ type now accessed through TYPE_FIELD_BITPOS.
+
+ * dbxread.c (process_symbol_for_psymtab): Added code to deal with
+ possible lack of a ":" in a debugging symbol (do nothing).
+
+ * symtab.c (decode_line_1): Added check in case of all numbers for
+ complete lack of symbols.
+
+ * source.c (select_source_symtab): Made sure that this wouldn't
+ bomb on complete lack of symbols.
+
+Mon Oct 24 12:28:29 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * m-sparc.h, findvar.c: Ditched REGISTER_SAVED_UNIQUELY and based
+ code on REGISTER_IN_WINDOW_P and HAVE_REGISTER_WINDOWS. This will
+ break when we find a register window machine which saves the
+ window registers within the context of an inferior frame.
+
+ * sparc-dep.c (frame_saved_pc): Put PC_ADJUST return back in for
+ frame_saved_pc. Seems correct.
+
+ * findvar.c, m-sparc.h: Created the macro REGISTER_SAVED_UNIQUELY
+ to handle register window issues (ie. that find_saved_register
+ wasn't checking the selected frame itself for shit).
+
+ * sparc-dep.c (core_file_command): Offset target of o & g register
+ bcopy by 1 to hit correct registers.
+
+ * m-sparc.h: Changed STACK_END_ADDR.
+
+Sun Oct 23 19:41:51 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * sparc-dep.c (core_file_command): Added in code to get the i & l
+ registers from the stack in the corefile, and blew away some wrong
+ code to get i & l from inferior.
+
+Fri Oct 21 15:09:19 1988 Randall Smith (randy at apple-gunkies.ai.mit.edu)
+
+ * m-sparc.h (PUSH_DUMMY_FRAME): Saved the value of the RP register
+ in the location reserved for i7 (in the created frame); this way
+ the rp value won't get lost. The pc (what we put into the rp in
+ this routine) gets saved seperately, so we loose no information.
+
+ * sparc-dep.c (do_save_insn & do_restore_insn): Added a wrapper to
+ preserve the proceed status state variables around each call to
+ proceed (the current frame was getting munged because this wasn't
+ being done).
+
+ * m-sparc.h (FRAME_FIND_SAVED_REGS): Fix bug: saved registers
+ addresses were being computed using absolute registers number,
+ rather than numbers relative to each group of regs.
+
+ * m-sparc.h (POP_FRAME): Fixed a bug (I hope) in the context
+ within which saved reg numbers were being interpetted. The
+ values to be restored were being gotten in the inferior frame, and
+ the restoring was done in the superior frame. This means that i
+ registers must be restored into o registers.
+
+ * sparc-dep.c (do_restore_insn): Modified to take a pc as an
+ argument, instead of a raw_buffer. This matches (at least it
+ appears to match) usage from POP_FRAME, which is the only place
+ from which do_restore_insn is called.
+
+ * sparc-dep.c (do_save_insn and do_restore_insn): Added comments.
+
+ * m-sparc.h (FRAME_FIND_SAVED_REGS): Modified my code to find the
+ save addresses of out registers to use the in regs off the stack
+ pointer when the current frame is 1 from the innermost.
+
+Thu Oct 20 13:56:15 1988 & Smith (randy at hobbes.ai.mit.edu)
+
+ * blockframe.c, m-sparc.h: Removed code associated with
+ GET_PREV_FRAME_FROM_CACHE_ITEM. This code was not needed for the
+ sparc; you can always find the previous frames fp from the fp of
+ the current frame (which is the sp of the previous). It's getting
+ the information associated with a given frame (ie. saved
+ registers) that's a bitch, because that stuff is saved relative to
+ the stack pointer rather than the frame pointer.
+
+ * m-sparc.h (GET_PREV_FRAME_FROM_CACHE_ITEM): Modified to return
+ the frame pointer of the previous frame instead of the stack
+ pointer of same.
+
+ * blockframe.c (flush_cached_frames): Modified call to
+ obstack_free to free back to frame_cache instead of back to zero.
+ This leaves the obstack control structure in finite state (and
+ still frees the entry allocated at frame_cache).
+
+Sat Oct 15 16:30:47 1988 & Smith (randy at tartarus.uchicago.edu)
+
+ * valops.c (call_function): Suicide material here. Fixed a typo;
+ CALL_DUMMY_STACK_ADJUST was spelled CAll_DUMMY_STACK_ADJUST on
+ line 530 of the file. This cost me three days. I'm giving up
+ typing for lent.
+
+Fri Oct 14 15:10:43 1988 & Smith (randy at tartarus.uchicago.edu)
+
+ * m-sparc.h: Corrected a minor mistake in the dummy frame code
+ that was getting the 5th argument and the first argument from the
+ same place.
+
+Tue Oct 11 11:49:33 1988 & Smith (randy at tartarus.uchicago.edu)
+
+ * infrun.c: Made stop_after_trap and stop_after_attach extern
+ instead of static so that code which used proceed from machine
+ dependent files could fiddle with them.
+
+ * blockframe.c, frame.h, sparc-dep.c, m-sparc.h: Changed sense of
+ ->prev and ->next in struct frame_cache_item to fit usage in rest
+ of gdb (oops).
+
+Mon Oct 10 15:32:42 1988 Randy Smith (randy at gargoyle.uchicago.edu)
+
+ * m-sparc.h, sparc-dep.c, blockframe.c, frame.h: Wrote
+ get_frame_cache_item. Modified FRAME_SAVED_PC and frame_saved_pc
+ to take only one argument and do the correct thing with it. Added
+ the two macros I recently defined in blockframe.c to m-sparc.h.
+ Have yet to compile this thing on a sparc, but I've now merged in
+ everything that I received from tiemann, either exactly, or simply
+ effectively.
+
+ * source.c: Added code to allocated space to sals.sals in the case
+ where no line was specified.
+
+ * blockframe.c, infrun.c: Modified to cache stack frames requested
+ to minimize accesses to subprocess.
+
+Tue Oct 4 15:10:39 1988 Randall Smith (randy at cream-of-wheat.ai.mit.edu)
+
+ * config.gdb: Added sparc.
+
+Mon Oct 3 23:01:22 1988 Randall Smith (randy at cream-of-wheat.ai.mit.edu)
+
+ * Makefile, blockframe.c, command.c, core.c, dbxread.c, defs.h,
+ expread.y, findvar.c, infcmd.c, inflow.c, infrun.c, sparc-pinsn.c,
+ m-sparc.h, sparc-def.c, printcmd.c, stack.c, symmisc.c, symseg.h,
+ valops.c, values.c: Did initial merge of sparc port. This will
+ not compile; have to do stack frame caching and finish port.
+
+ * inflow.c, gdb.texinfo: `tty' now resets the controling terminal.
+
+Fri Sep 30 11:31:16 1988 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * inferior.h, infcmd.c, infrun.c: Changed the variable
+ stop_random_signal to stopped_by_random signal to fit in better
+ with name conventions (variable is not a direction to the
+ proceed/resume set; it is information from it).
+
+Thu Sep 29 13:30:46 1988 Randall Smith (randy at cream-of-wheat.ai.mit.edu)
+
+ * infcmd.c (finish_command): Value type of return value is now
+ whatever the function returns, not the type of the function (fixed
+ a bug in printing said value).
+
+ * dbxread.c (read_dbx_symtab, process_symbol_for_psymtab):
+ Put *all* global symbols into misc_functions. This is what was
+ happening anyway, and we need it for find_pc_misc_function.
+
+ ** This was eventually taken out, but I didn't mark it in the
+ ChangeLog. Oops.
+
+ * dbxread.c (process_symbol_for_psymtab): Put every debugger
+ symbol which survives the top case except for constants on the
+ symchain. This means that all of these *won't* show up in misc
+ functions (this will be fixed once I make sure it's broken the way
+ it's supposed to be).
+
+ * dbxread.c: Modified placement of debugger globals onto the hash
+ list; now we exclude the stuff after the colon and don't skip the
+ first character (debugger symbols don't have underscores).
+
+ * dbxread.c: Killed debuginfo stuff with ifdef's.
+
+Wed Sep 28 14:31:51 1988 Randall Smith (randy at cream-of-wheat.ai.mit.edu)
+
+ * symtab.h, dbxread.c: Modified to deal with BINCL, EINCL, and
+ EXCL symbols produced by the sun loader by adding a list of
+ pre-requisite partial_symtabs that each partial symtab needs.
+
+ * symtab.h, dbxread.c, symtab.c, symmisc.c: Modified to avoid
+ doing a qsort on the local (static) psymbols for each file to
+ speed startup. This feature is not completely debugged, but it's
+ inclusion has forced the inclusion of another feature (dealing
+ with EINCL's, BINCL's and EXCL's) and so I'm going to go in and
+ deal with them.
+
+ * dbxread.c (process_symbol_for_psymtab): Made sure that the class
+ of the symbol made it into the partial_symbol entry.
+
+Tue Sep 27 15:10:26 1988 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * dbxread.c: Fixed bug; init_psymbol_list was not being called
+ with the right number of arguments (1).
+
+ * dbxread.c: Put ifdef's around N_MAIN, N_M2C, and N_SCOPE to
+ allow compilation on a microvax.
+
+ * config.gdb: Modified so that "config.gdb vax" would work.
+
+ * dbxread.c, symtab.h, symmisc.h, symtab.c, source.c: Put in many
+ and varied hacks to speed up gdb startup including: A complete
+ rewrite of read_dbx_symtab, a modification of the partial_symtab
+ data type, deletion of select_source_symtab from
+ symbol_file_command, and optimiztion of the call to strcmp in
+ compare_psymbols.
+
+Thu Sep 22 11:08:54 1988 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * dbxread.c (psymtab_to_symtab): Removed call to
+ init_misc_functions.
+
+ * dbxread.c: Fixed enumeration type clash (used enum instead of
+ integer constant).
+
+ * breakpoint.c: Fixed typo; lack of \ at end of line in middle of
+ string constant.
+
+ * symseg.h: Fixed typo; lack of semicolon after structure
+ definition.
+
+ * command.c, breakpoint.c, printcmd.c: Added cmdlist editing
+ functions to add commands with the abbrev flag set. Changed
+ help_cmd_list to recognize this flag and modified unset,
+ undisplay, and enable, disable, and delete breakpoints to have
+ this flag set.
+
+Wed Sep 21 13:34:19 1988 Randall Smith (randy at plantaris.ai.mit.edu)
+
+ * breakpoint.c, infcmd.c, gdb.texinfo: Created "unset" as an alias
+ for delete, and changed "unset-environment" to be the
+ "environment" subcommand of "delete".
+
+ * gdb.texinfo, valprint.c: Added documentation in the manual for
+ breaking the set-* commands into subcommands of set. Changed "set
+ maximum" to "set array-max".
+
+ * main.c, printcmd.c, breakpoint.c: Moved the declaration of
+ command lists into main and setup a function in main initializing
+ them to guarrantee that they would be initialized before calling
+ any of the individual files initialize routines.
+
+ * command.c (lookup_cmd): A null string subcommand is treated as
+ an unknown subcommand rather than an ambiguous one (eg. "set $x =
+ 1" will now work).
+
+ * infrun.c (wait_for_inferior): Put in ifdef for Sony News in
+ check for trap by INNER_THAN macro.
+
+ * eval.c (evaluate_subexp): Put in catch to keep the user from
+ attempting to call a non function as a function.
+
+Tue Sep 20 10:35:53 1988 Randall Smith (randy at oatmeal.ai.mit.edu)
+
+ * dbxread.c (read_dbx_symtab): Installed code to keep track of
+ which global symbols did not have debugger symbols refering to
+ them, and recording these via record_misc_function.
+
+ * dbxread.c: Killed code to check for extra global symbols in the
+ debugger symbol table.
+
+ * printcmd.c, breakpoint.c: Modified help entries for several
+ commands to make sure that abbreviations were clearly marked and
+ that the right commands showed up in the help listings.
+
+ * main.c, command.c, breakpoint.c, infcmd.c, printcmd.c,
+ valprint.c, defs.h: Modified help system to allow help on a class
+ name to show subcommands as well as commands and help on a command
+ to show *all* subcommands of that command.
+
+Fri Sep 16 16:51:19 1988 Randall Smith (randy at gluteus.ai.mit.edu)
+
+ * breakpoint.c (_initialize_breakpoint): Made "breakpoints"
+ subcommands of enable, disable, and delete use class 0 (ie. they
+ show up when you do a help xxx now).
+
+ * infcmd.c,printcmd,c,main.c,valprint.c: Changed the set-*
+ commands into subcommands of set. Created "set variable" for use
+ with variables whose names might conflict with other subcommands.
+
+ * blockframe.c, dbxread.c, coffread.c, expread.y, source.c:
+ Fixed mostly minor (and one major one in block_for_pc) bugs
+ involving checking the partial_symtab_list when a scan through the
+ symtab_list fails.
+
+Wed Sep 14 12:02:05 1988 Randall Smith (randy at sugar-smacks.ai.mit.edu)
+
+ * breakpoint.c, gdb.texinfo: Added enable breakpoints, disable
+ breakpoints and delete breakpoints as synonyms for enable,
+ disable, and delete. This seemed reasonable because of the
+ immeninent arrival of watchpoints & etc.
+
+ * gdb.texinfo: Added enable display, disable display, and delete
+ display to manual.
+
+Tue Sep 13 16:53:56 1988 Randall Smith (randy at sugar-smacks.ai.mit.edu)
+
+ * inferior.h, infrun.c, infcmd.c: Added variable
+ stop_random_signal to indicate when a proceed had been stopped by
+ an unexpected signal. Used this to determine (in normal_stop)
+ whether the current display point should be deleted.
+
+ * valops.c: Fix to value_ind to check for reference before doing a
+ COERCE_ARRAY.
+
+Sun Jul 31 11:42:36 1988 Richard Stallman (rms at frosted-flakes.ai.mit.edu)
+
+ * breakpoint.c (_initialize_breakpoint): Clean up doc for commands
+ that can now apply also to auto-displays.
+
+ * coffread.c (record_line): Corrected a spazz in editing.
+ Also removed the two lines that assume line-numbers appear
+ only in increasing order.
+
+Tue Jul 26 22:19:06 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * expression.h, eval.c, expprint.c, printcmd.c, valarith.c,
+ valops.c, valprint.c, values.c, m-*.h: Changes for evaluating and
+ displaying 64-bit `long long' integers. Each machine must define
+ a LONGEST type, and a BUILTIN_TYPE_LONGEST.
+
+ * symmisc.c: (print_symtab) check the status of the fopen and call
+ perror_with_name if needed.
+
+Thu Jul 21 00:56:11 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * Convex: core.c: changes required by Convex's SOFF format were
+ isolated in convex-dep.c.
+
+Wed Jul 20 21:26:10 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * coffread.c, core.c, expread.y, i386-pinsn.c, infcmd.c, inflow.c,
+ infrun.c, m-i386.h, main.c, remote.c, source.c, valops.c:
+ Improvements for the handling of the i386 and other machines
+ running USG. (Several of these files just needed extra header files
+ such as types.h.) utils.c: added bcopy, bcmp, bzero, getwd, list
+ of signals, and queue routines for USG systems. Added vfork macro
+ to i386
+
+ * printcmd.c, breakpoint.c: New commands to enable/disable
+ auto-displays. Also `delete display displaynumber' works like
+ `undisplay displaynumber'.
+
+Tue Jul 19 02:17:18 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * coffread.c: (coff_lookup_type) Wrong portion of type_vector was
+ being bzero'd after type_vector was reallocated.
+
+ * printcmd.c: (delete_display) Check for a display chain before
+ attempting to delete a display.
+
+ * core.c, *-dep.c (*-infdep moved to *-dep): machine-dependent
+ parts of core.c (core_file_command, exec_file_command) moved to
+ *-dep.c.
+
+Mon Jul 18 19:45:51 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * dbxread.c: typo in read_struct_type (missing '=') was causing a
+ C struct to be parsed as a C++ struct, resulting in a `invalid
+ character' message.
+
+Sun Jul 17 22:27:32 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * printcmd.c, symtab.c, valops.c, expread.y: When an expression is
+ read, the innermost block required to evaluate the expression is
+ saved in the global variable `innermost_block'. This information
+ is saved in the `block' field of an auto-display so that
+ expressions with inactive variables can be skipped. `info display'
+ tells the user which displays are active and which are not. New
+ fn `contained_in' returns nonzero if one block is contained within
+ another.
+
+Fri Jul 15 01:53:14 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * infrun.c, m-i386.h: Use macro TRAPS_EXPECTED to set number of
+ traps to skip when sh execs the program. Default is 2, m-i386.h
+ overrides this and sets to 4.
+
+ * coffread.c, infrun.c: minor changes for the i386. May be able
+ to eliminate them with more general code.
+
+ * default-infdep.c: #ifdef SYSTEMV, include header file types.h.
+ Also switched the order of signal.h and user.h, since System 5
+ requires signal.h to come first.
+
+ * core.c main.c, remote,c, source.c, inflow.c: #ifdef SYSTEMV,
+ include various header files. Usually types.h and fcntl.h.
+
+ * utils.c: added queue routines needed by the i386 (and other sys
+ 5 machines).
+
+ * sys5.c, regex.c, regex.h: new files for sys 5 systems. (The
+ regex files are simply links to /gp/gnu/lib.)
+
+Thu Jul 14 01:47:14 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * config.gdb, README: Provide a list of known machines when user
+ enters an invalid machine. New second arg is operating system,
+ currently only used with `sunos4' or `os4'. Entry for i386 added.
+
+ * news-infdep.c: new file.
+
+ * m-news.h: new version which deals with new bugs in news800's OS.
+
+Tue Jul 12 19:52:16 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * Makefile, *.c, munch, config.gdb, README: New initialization
+ scheme uses nm to find functions whose names begin with
+ `_initialize_'. Files `initialize.h', `firstfile.c',
+ `lastfile.c', `m-*init.h' no longer needed.
+
+ * eval.c, symtab.c, valarith.c, valops.c, value.h, values.c: Bug
+ fixes from gdb+ 2.5.4. evaluate_subexp takes a new arg, type
+ expected. New fn value_virtual_fn_field.
+
+Mon Jul 11 00:48:49 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * core.c (read_memory): xfer_core_file was being called with an
+ extra argument (0) by read_memory.
+
+ * core.c (read_memory), *-infdep.c (read_inferior_memory),
+ valops.c (value_at): read_memory and read_inferior_memory now work
+ like write_memory and write_inferior_memory in that errno is
+ checked after each ptrace and returned to the caller. Used in
+ value_at to detect references to addresses which are out of
+ bounds. Also core.c (xfer_core_file): return 1 if invalid
+ address, 0 otherwise.
+
+ * inflow.c, <machine>-infdep.c: removed all calls to ptrace from
+ inflow.c and put them in machine-dependent files *-infdep.c.
+
+Sun Jul 10 19:19:36 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * symmisc.c: (read_symsegs) Accept only format number 2. Since
+ the size of the type structure changed when C++ support was added,
+ format 1 can no longer be used.
+
+ * core.c, m-sunos4.h: (core_file_command) support for SunOS 4.0.
+ Slight change in the core structure. #ifdef SUNOS4. New file
+ m-sunos4.h. May want to change config.gdb also.
+
+Fri Jul 8 19:59:49 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * breakpoint.c: (break_command_1) Allow `break if condition'
+ rather than parsing `if' as a function name and returning an
+ error.
+
+Thu Jul 7 22:22:47 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * C++: valops.c, valprint.c, value.h, values.c: merged code to deal
+ with C++ expressions.
+
+Wed Jul 6 03:28:18 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * C++: dbxread.c: (read_dbx_symtab, condense_misc_bunches,
+ add_file_command) Merged code to read symbol information from
+ an incrementally linked file. symmisc.c:
+ (init_free_inclink_symtabs, free_inclink_symtabs) Cleanup
+ routines.
+
+Tue Jul 5 02:50:41 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * C++: symtab.c, breakpoint.c, source.c: Merged code to deal with
+ ambiguous line specifications. In C++ one can have overloaded
+ function names, so that `list classname::overloadedfuncname'
+ refers to several different lines, possibly in different files.
+
+Fri Jul 1 02:44:20 1988 Peter TerMaat (pete at corn-chex.ai.mit.edu)
+
+ * C++: symtab.c: replaced lookup_symtab_1 and lookup_symtab_2 with
+ a modified lookup_symbol which checks for fields of the current
+ implied argument `this'. printcmd.c, source.c, symtab.c,
+ valops.c: Need to change callers once callers are
+ installed.
+
+Wed Jun 29 01:26:56 1988 Peter TerMaat (pete at frosted-flakes.ai.mit.edu)
+
+ * C++: eval.c, expprint.c, expread.y, expression.h, valarith.c,
+ Merged code to deal with evaluation of user-defined operators,
+ member functions, and virtual functions.
+ binop_must_be_user_defined tests for user-defined binops,
+ value_x_binop calls the appropriate operator function.
+
+Tue Jun 28 02:56:42 1988 Peter TerMaat (pete at frosted-flakes.ai.mit.edu)
+
+ * C++: Makefile: changed the echo: expect 101 shift/reduce conflicts
+ and 1 reduce/reduce conflict.
+
+Local Variables:
+mode: indented-text
+left-margin: 8
+fill-column: 74
+version-control: never
+End:
diff --git a/gnu/usr.bin/gdb/Gdbinit b/gnu/usr.bin/gdb/Gdbinit
new file mode 100644
index 0000000..bcacd5d
--- /dev/null
+++ b/gnu/usr.bin/gdb/Gdbinit
@@ -0,0 +1,15 @@
+echo Setting up the environment for debugging gdb.\n
+
+b fatal
+
+b info_command
+commands
+ silent
+ return
+end
+
+define rr
+ run
+end
+
+set prompt (top-gdb)
diff --git a/gnu/usr.bin/gdb/Makefile b/gnu/usr.bin/gdb/Makefile
new file mode 100644
index 0000000..8536444
--- /dev/null
+++ b/gnu/usr.bin/gdb/Makefile
@@ -0,0 +1,3 @@
+SUBDIR= bfd libiberty mmalloc gdb
+
+.include <bsd.subdir.mk>
diff --git a/gnu/usr.bin/gdb/Makefile.dist b/gnu/usr.bin/gdb/Makefile.dist
new file mode 100644
index 0000000..3cbc91f
--- /dev/null
+++ b/gnu/usr.bin/gdb/Makefile.dist
@@ -0,0 +1,371 @@
+/* This file should be run through the C preprocessor by config.gdb
+ to produce the Makefile. */
+
+/* Define this to xgdb if you want to compile xgdb as well as gdb. */
+XGDB=
+/* Place to install binaries. */
+bindir=/usr/local/bin
+/* Place to install X binaries. */
+xbindir=$(bindir)
+
+/* System V: If you compile gdb with a compiler which uses the coff
+ encapsulation feature (this is a function of the compiler used, NOT
+ of the m-?.h file selected by config.gdb), you must make sure that
+ the GNU nm is the one that is used by munch. */
+
+/* If you are compiling with GCC, make sure that either 1) You use the
+ -traditional flag, or 2) You have the fixed include files where GCC
+ can reach them. Otherwise the ioctl calls in inflow.c and readline.c
+ will be incorrectly compiled. The "fixincludes" script in the gcc
+ distribution will fix your include files up. */
+/* CC=gcc -traditional */
+CC=cc
+
+/* It is also possible that you will need to add -I/usr/include/sys to the
+ CFLAGS section if your system doesn't have fcntl.h in /usr/include (which
+ is where it should be according to Posix). */
+
+YACC=bison -y -v
+/* YACC=yacc */
+SHELL=/bin/sh
+MAKE=make
+
+/* Set this up with gcc if you have gnu ld and the loader will print out
+ line numbers for undefinded refs. */
+/* CC-LD=gcc -static */
+CC-LD=${CC}
+
+/* If you are using the GNU C library, uncomment the following line. */
+/* HAVE_VPRINTF_DEFINE = -DHAVE_VPRINTF */
+
+/* -I. for "#include <obstack.h>". Possibly regex.h also. */
+
+/* M_CFLAGS, if defined, has system-dependent CFLAGS. */
+#if !defined(M_CFLAGS)
+#define M_CFLAGS
+#endif
+
+/* CFLAGS for both GDB and readline. */
+GLOBAL_CFLAGS = -g M_CFLAGS
+CFLAGS = -I. ${HAVE_VPRINTF_DEFINE} ${GLOBAL_CFLAGS}
+/* None of the things in CFLAGS will do any harm, and on some systems
+ (e.g. SunOS4) it is important to use the M_CFLAGS. */
+LDFLAGS = $(CFLAGS)
+
+/*
+ define this to be "obstack.o" if you don't have the obstack library installed
+ you must at the same time define OBSTACK1 as "obstack.o"
+ so that the dependencies work right. Similarly with REGEX and "regex.o".
+ You must define REGEX and REGEX1 on USG machines.
+ If your sysyem is missing alloca(), or, more likely, it's there but
+ it doesn't work, define ALLOCA & ALLOCA1 */
+OBSTACK = obstack.o
+OBSTACK1 = obstack.o
+
+#ifdef M_REGEX
+REGEX = M_REGEX
+REGEX1 = M_REGEX
+#else
+REGEX =
+REGEX1 =
+#endif
+
+#ifdef M_ALLOCA
+ALLOCA = M_ALLOCA
+ALLOCA1 = M_ALLOCA
+#else
+ALLOCA =
+ALLOCA1 =
+#endif
+
+/*
+ define this to be "malloc.o" if you want to use the gnu malloc routine
+ (useful for debugging memory allocation problems in gdb). Otherwise, leave
+ it blank. */
+/* GNU_MALLOC = */
+GNU_MALLOC = malloc.o
+
+/* Flags to be used in compiling malloc.o
+ Specify range checking for storage allocation. */
+/* MALLOC_FLAGS = ${CFLAGS} */
+MALLOC_FLAGS = ${CFLAGS} -Drcheck -Dbotch=fatal_dump_core -DMSTATS
+
+/* Define SYSV if compiling on a system V or HP machine. */
+#ifdef M_SYSV
+SYSV_DEFINE = -DSYSV
+#else
+SYSV_DEFINE =
+#endif
+
+/* MUNCH_DEFINE should be -DSYSV if have System V-style nm,
+ or null if have BSD-style nm. */
+#ifdef M_BSD_NM
+MUNCH_DEFINE =
+#else
+MUNCH_DEFINE = ${SYSV_DEFINE}
+#endif
+
+/* Flags that describe where you can find the termcap library.
+ You may need to make other arrangements for USG. */
+TERMCAP = -ltermcap
+
+/* M_CLIBS, if defined, has system-dependent libs
+ For example, -lPW for System V to get alloca(). */
+#ifndef M_CLIBS
+#define M_CLIBS
+#endif
+CLIBS = ${ADD_FILES} ${TERMCAP} M_CLIBS
+
+ADD_FILES = ${OBSTACK} ${REGEX} ${ALLOCA} ${GNU_MALLOC}
+ADD_DEPS = ${OBSTACK1} ${REGEX1} ${ALLOCA1} ${GNU_MALLOC}
+
+SFILES = blockframe.c breakpoint.c dbxread.c coffread.c command.c core.c \
+ environ.c eval.c expprint.c findvar.c infcmd.c inflow.c infrun.c \
+ kdb-start.c main.c printcmd.c \
+ remote.c source.c stack.c standalone.c stuff.c symmisc.c symtab.c \
+ utils.c valarith.c valops.c valprint.c values.c version.c expread.y \
+ xgdb.c
+
+DEPFILES = umax-dep.c gould-dep.c default-dep.c sun3-dep.c \
+ sparc-dep.c hp9k320-dep.c hp300bsd-dep.c news-dep.c i386-dep.c \
+ symmetry-dep.c convex-dep.c altos-dep.c isi-dep.c pyr-dep.c
+
+PINSNS = gld-pinsn.c i386-pinsn.c sparc-pinsn.c vax-pinsn.c m68k-pinsn.c \
+ ns32k-pinsn.c convex-pinsn.c pyr-pinsn.c
+
+HFILES = command.h defs.h environ.h expression.h frame.h getpagesize.h \
+ inferior.h symseg.h symtab.h value.h wait.h \
+ a.out.encap.h a.out.gnu.h stab.gnu.h
+
+OPCODES = m68k-opcode.h pn-opcode.h sparc-opcode.h npl-opcode.h vax-opcode.h \
+ ns32k-opcode.h convex-opcode.h pyr-opcode.h
+
+MFILES = m-hp9k320.h m-hp300bsd.h m-i386.h m-i386gas.h \
+ m-i386-sv32.h m-i386g-sv32.h m-isi.h m-merlin.h \
+ m-altos.h m-news.h m-newsos3.h m-npl.h m-pn.h \
+ m-sparc.h m-sun2.h m-sun3.h m-sun2os4.h \
+ m-sun3os4.h m-sun4os4.h m-umax.h m-vax.h m-symmetry.h m-convex.h \
+ m-pyr.h
+
+/* This list of files really shouldn't be in this makefile, but I can't think
+ of any good way to get the readline makefile to tell us what files
+ to put in our tarfile. */
+READLINE = readline.c history.c funmap.c \
+ emacs_keymap.c vi_keymap.c vi_mode.c keymaps.c \
+ readline.h history.h keymaps.h chardefs.h \
+ inc-readline.texinfo inc-history.texinfo \
+ readline.texinfo history.texinfo \
+ Makefile ChangeLog
+
+REMOTE_EXAMPLES = remote-sa.m68k.shar remote-multi.shar
+
+POSSLIBS = obstack.h obstack.c regex.c regex.h malloc.c alloca.c
+
+TESTS = testbpt.c testfun.c testrec.c testreg.c testregs.c
+
+OTHERS = Makefile.dist createtags munch config.gdb ChangeLog README TAGS \
+ gdb.texinfo .gdbinit COPYING expread.tab.c stab.def \
+ XGDB-README copying.c Projects Convex.notes copying.awk hp-include
+
+TAGFILES = ${SFILES} ${DEPFILES} ${PINSNS} ${HFILES} ${OPCODES} ${MFILES} \
+ ${POSSLIBS}
+TARFILES = ${TAGFILES} ${OTHERS} ${REMOTE_EXAMPLES}
+
+OBS = main.o blockframe.o breakpoint.o findvar.o stack.o source.o \
+ values.o eval.o valops.o valarith.o valprint.o printcmd.o \
+ symtab.o symmisc.o coffread.o dbxread.o infcmd.o infrun.o remote.o \
+ command.o utils.o expread.o expprint.o pinsn.o environ.o version.o \
+ copying.o ${READLINEOBS}
+
+TSOBS = core.o inflow.o dep.o
+
+NTSOBS = standalone.o
+
+TSSTART = /lib/crt0.o
+
+NTSSTART = kdb-start.o
+
+RL_LIB = readline/libreadline.a
+
+/* Do some fancy trickery to produce a line like
+ -DM_MAKEDEFINE="-DM_SYSV -DM_BSD_NM".
+*/
+MD=M_MAKEDEFINE
+
+/* Avoid funny things that Sun's make throws in for us. */
+/* TARGET_ARCH is supposed to get around it putting in the machine type.
+ If the "things" up there really is plural, we'll need to do something
+ else as well. */
+/*.c.o:
+ ${CC} -c ${CFLAGS} $< */
+TARGET_ARCH=
+
+all: gdb $(XGDB)
+
+install: gdb $(XGDB)
+ cp gdb $(bindir)/gdb.new
+ mv $(bindir)/gdb.new $(bindir)/gdb
+ -if [ "$(XGDB)" = xgdb ]; then \
+ cp xgdb $(xbindir)/xgdb.new; \
+ mv $(xbindir)/xgdb.new $(xbindir)xgdb; \
+ fi
+
+gdb : $(OBS) $(TSOBS) ${ADD_DEPS} ${RL_LIB}
+ rm -f init.c
+ ./munch ${MUNCH_DEFINE} $(OBS) $(TSOBS) > init.c
+ ${CC-LD} $(LDFLAGS) -o gdb init.c $(OBS) $(TSOBS) ${RL_LIB} $(CLIBS)
+
+/* This is useful when debugging GDB, because Unix doesn't let you run GDB
+ on itself without copying the executable. So "make gdb1" will make
+ gdb and put a copy in gdb1, and you can run it with "gdb gdb1". */
+gdb1 : gdb
+ cp gdb gdb1
+
+Makefile : Makefile.dist
+ cp Makefile.dist tmp.c
+ $(CC) -E >Makefile tmp.c $(MD) "-DM_MAKEDEFINE=$(MD)"
+ -rm tmp.c
+/* This did not work-- -Usparc became "-Usparc" became "-Usparc.
+ Or something like that. */
+/* $(CC) -E >Makefile tmp.c $(MD) "-DM_MAKEDEFINE=\"$(MD)\"" */
+
+xgdb : $(OBS) $(TSOBS) xgdb.o ${ADD_DEPS} ${RL_LIB}
+ rm -f init.c
+ ./munch ${MUNCH_DEFINE} $(OBS) $(TSOBS) xgdb.o > init.c
+ $(CC-LD) $(LDFLAGS) -o xgdb init.c $(OBS) $(TSOBS) xgdb.o \
+ -lXaw -lXmu -lXt -lX11 ${RL_LIB} $(CLIBS)
+
+/* Old (pre R3) xgdb comp.
+ $(CC-LD) $(LDFLAGS) -o xgdb init.c $(OBS) $(TSOBS) xgdb.o \
+ -lXaw -lXt -lX11 $(CLIBS) */
+
+kdb : $(NTSSTART) $(OBS) $(NTSOBS) ${ADD_DEPS} ${RL_LIB}
+ rm -f init.c
+ ./munch ${MUNCH_DEFINE} $(OBS) $(NTSOBS) > init.c
+ $(CC) $(LDFLAGS) -c init.c $(CLIBS)
+ ld -o kdb $(NTSSTART) $(OBS) $(NTSOBS) init.o ${RL_LIB} -lc $(CLIBS)
+
+/* If it can figure out the appropriate order, createtags will make sure
+ that the proper m-*, *-dep, *-pinsn, and *-opcode files come first
+ in the tags list. It will attempt to do the same for dbxread.c and
+ coffread.c. This makes using M-. on machine dependent routines much
+ easier. */
+
+TAGS: ${TAGFILES}
+ createtags ${TAGFILES}
+tags: TAGS
+
+gdb.tar: ${TARFILES}
+ rm -f gdb.tar
+ mkdir dist-gdb
+ cd dist-gdb ; for i in ${TARFILES} ; do ln -s ../$$i . ; done
+ mkdir dist-gdb/readline
+ cd dist-gdb/readline ; for i in ${READLINE} ; do ln -s ../../readline/$$i . ; done
+ tar chf gdb.tar dist-gdb
+ rm -rf dist-gdb
+
+/* Remove gdb.tar.Z so stupid compress doesn't ask whether we want to
+ overwrite it. compress -f is not what we want, because we do want
+ to know if compress would not make it smaller. */
+gdb.tar.Z: gdb.tar
+ if [ -f gdb.tar.Z ]; then rm -f gdb.tar.Z; else true; fi
+ compress gdb.tar
+
+clean:
+ rm -f ${OBS} ${TSOBS} ${NTSOBS} ${OBSTACK} ${REGEX} ${GNU_MALLOC}
+ rm -f init.c init.o
+ rm -f xgdb.o xgdb
+ rm -f gdb core gdb.tar gdb.tar.Z make.log
+ rm -f gdb[0-9]
+ cd readline ; make clean
+
+distclean: clean expread.tab.c TAGS
+ rm -f dep.c opcode.h param.h pinsn.c config.status
+ rm -f y.output yacc.acts yacc.tmp
+ rm -f ${TESTS} Makefile
+
+realclean: clean
+ rm -f expread.tab.c TAGS
+ rm -f dep.c opcode.h param.h pinsn.c config.status
+ rm -f Makefile
+
+xgdb.o : defs.h param.h symtab.h frame.h
+
+/* Make copying.c from COPYING */
+copying.c : COPYING copying.awk
+ awk -f copying.awk < COPYING > copying.c
+
+expread.tab.c : expread.y
+ @echo 'Expect 4 shift/reduce conflict.'
+ ${YACC} expread.y
+ mv y.tab.c expread.tab.c
+
+expread.o : expread.tab.c defs.h param.h symtab.h frame.h expression.h
+ $(CC) -c ${CFLAGS} expread.tab.c
+ mv expread.tab.o expread.o
+
+readline/libreadline.a : force_update
+ cd readline ; ${MAKE} "SYSV=${SYSV_DEFINE}" \
+ "DEBUG_FLAGS=${GLOBAL_CFLAGS}" "CC=${CC}" libreadline.a
+
+force_update :
+
+/* Only useful if you are using the gnu malloc routines. */
+malloc.o : malloc.c
+ ${CC} -c ${MALLOC_FLAGS} malloc.c
+
+/* dep.o depends on config.status in case someone reconfigures gdb out
+ from under an already compiled gdb. */
+dep.o : dep.c config.status defs.h param.h frame.h inferior.h obstack.h \
+ a.out.encap.h
+
+/* pinsn.o depends on config.status in case someone reconfigures gdb out
+ from under an already compiled gdb. */
+pinsn.o : pinsn.c config.status defs.h param.h symtab.h obstack.h symseg.h \
+ frame.h opcode.h
+
+/* The rest of this is a standard dependencies list (hand edited output of
+ cpp -M). It does not include dependencies of .o files on .c files. */
+/* All files which depend on config.status also depend on param.h in case
+ someone reconfigures gdb out from under an already compiled gdb. */
+blockframe.o : defs.h param.h config.status symtab.h obstack.h symseg.h frame.h
+breakpoint.o : defs.h param.h config.status symtab.h obstack.h symseg.h frame.h
+coffread.o : defs.h param.h config.status
+command.o : command.h defs.h
+core.o : defs.h param.h config.status a.out.encap.h
+dbxread.o : param.h config.status defs.h symtab.h obstack.h symseg.h a.out.encap.h \
+ stab.gnu.h
+environ.o : environ.h
+eval.o : defs.h param.h config.status symtab.h obstack.h symseg.h value.h expression.h
+expprint.o : defs.h symtab.h obstack.h symseg.h param.h config.status expression.h
+findvar.o : defs.h param.h config.status symtab.h obstack.h symseg.h frame.h value.h
+infcmd.o : defs.h param.h config.status symtab.h obstack.h symseg.h frame.h inferior.h \
+ environ.h value.h
+inflow.o : defs.h param.h config.status frame.h inferior.h
+infrun.o : defs.h param.h config.status symtab.h obstack.h symseg.h frame.h inferior.h \
+ wait.h
+kdb-start.o : defs.h param.h config.status
+main.o : defs.h command.h param.h config.status
+malloc.o : getpagesize.h
+obstack.o : obstack.h
+printcmd.o : defs.h param.h config.status frame.h symtab.h obstack.h symseg.h value.h \
+ expression.h
+regex.o : regex.h
+remote.o : defs.h param.h config.status frame.h inferior.h wait.h
+source.o : defs.h symtab.h obstack.h symseg.h param.h config.status
+stack.o : defs.h param.h config.status symtab.h obstack.h symseg.h frame.h
+standalone.o : defs.h param.h config.status symtab.h obstack.h symseg.h frame.h \
+ inferior.h wait.h
+symmisc.o : defs.h symtab.h obstack.h symseg.h obstack.h
+symtab.o : defs.h symtab.h obstack.h symseg.h param.h config.status obstack.h
+utils.o : defs.h param.h config.status
+valarith.o : defs.h param.h config.status symtab.h obstack.h symseg.h value.h expression.h
+valops.o : defs.h param.h config.status symtab.h obstack.h symseg.h value.h frame.h \
+ inferior.h
+valprint.o : defs.h param.h config.status symtab.h obstack.h symseg.h value.h
+values.o : defs.h param.h config.status symtab.h obstack.h symseg.h value.h
+
+robotussin.h : getpagesize.h
+symtab.h : obstack.h symseg.h
+a.out.encap.h : a.out.gnu.h
+
diff --git a/gnu/usr.bin/gdb/Projects b/gnu/usr.bin/gdb/Projects
new file mode 100644
index 0000000..f38f6c7
--- /dev/null
+++ b/gnu/usr.bin/gdb/Projects
@@ -0,0 +1,114 @@
+
+ Suggested projects for aspiring or current GDB hackers
+ ======================================================
+
+ (You should probably chat with kingdon@ai.mit.edu to make sure that
+ no one else is doing the project you chose).
+
+Add watchpoints (break if a memory location changes). This would
+usually have to involve constant single stepping, but occasionally
+there is operating system support which gdb should be able to cleanly
+use (e.g. on the 80386, there are 4 debug registers. By ptracing an
+address into them, you can get a trap on writes or on reads and
+writes).
+
+Rewrite proceed, wait_for_inferior, and normal_stop to clean them up.
+Suggestions:
+
+ 1) Make each test in wait_for_inferior a seperate subroutine
+ call.
+ 2) Combine wait_for_inferior and normal_stop to clean up
+ communication via global variables.
+ 3) See if you can find some way to clean up the global
+ variables that are used; possibly group them by data flow
+ and information content?
+
+Work out some kind of way to allow running the inferior to be done as
+a sub-execution of, eg. breakpoint command lists. Currently running
+the inferior interupts any command list execution. This would require
+some rewriting of wait_for_inferior & friends, and hence should
+probably be done in concert with the above.
+
+Add function arguments to gdb user defined functions.
+
+Add convenience variables that refer to exec file, symbol file,
+selected frame source file, selected frame function, selected frame
+line number, etc.
+
+Add a "suspend" subcommand of the "continue" command to suspend gdb
+while continuing execution of the subprocess. Useful when you are
+debugging servers and you want to dodge out and initiate a connection
+to a server running under gdb.
+
+Make "handle" understand symbolic signal names.
+
+Work out and implement a reasonably general mechanism for multi-threaded
+processies. There are parts of one implemented in convex-dep.c, if
+you want an example.
+
+A standalone version of gdb on the i386 exists. Anyone who wants to
+do some serious working cleaning it up and making it a general
+standalone gdb should contact pace@wheaties.ai.mit.edu.
+
+Add stab information to allow reasonable debugging of inline functions
+(possibly they should show up on a stack backtrace? With a note
+indicating that they weren't "real"?).
+
+Implement support for specifying arbitrary locations of stack frames
+(in practice, this usually requires specification of both the top and
+bottom of the stack frame (fp and sp), since you *must* retrieve the
+pc that was saved in the innermost frame).
+
+Modify the naked "until" command to step until past the current source
+line, rather than past the current pc value. This is tricky simply
+because the low level routines have no way of specifying a multi-line
+step range, and there is no way of saying "don't print stuff when we
+stop" from above (otherwise could just call step many times).
+
+Modify the handling of symbols grouped through BINCL/EINCL stabs to
+allocate a partial symtab for each BINCL/EINCL grouping. This will
+seriously decrease the size of inter-psymtab dependencies and hence
+lessen the amount that needs to be read in when a new source file is
+accessed.
+
+Work out some method of saving breakpoints across the reloading of an
+executable. Probably this should be by saving the commands by which
+the breakpoints were set and re-executing them (as text locations may
+change).
+
+Do an "x/i $pc" after each stepi or nexti.
+
+Modify all of the disassemblers to use printf_filtered to get correct
+more filtering.
+
+Modify gdb to work correctly with Pascal.
+
+Rewrite macros that handle frame chaining and frameless functions.
+They should be able to tell the difference between start, main, and a
+frameless function called from main.
+
+Work out what information would need to be included in an executable
+by the compiler to allow gdb to debug functions which do not have a
+frame pointer. Modify gdb and gcc to do this.
+
+When `attached' to a program (via either OS support or remote
+debugging), gdb should arrange to catch signals which the terminal
+might send, as it is unlikely that the program will be able to notice
+them. SIGINT and SIGTSTP are obvious examples.
+
+Enhance the gdb manual with extra examples where needed.
+
+Arrange for list_command not to use decode_line_1 and thus not require
+symbols to be read in simply to read a source file.
+
+Problem in xgdb; the readline library needs the terminal in CBREAK
+mode for command line editing, but this makes it difficult to dispatch
+on button presses. Possible solution: use a define to replace getc in
+readline.c with a routine that does button dispatches. You should
+probably see XGDB-README before you fiddle with XGDB. Also, someone
+is implementing a new xgdb; it may not be worth while fiddling with
+the old one.
+
+# Local Variables:
+# mode: text
+# End:
diff --git a/gnu/usr.bin/gdb/README.FreeBSD b/gnu/usr.bin/gdb/README.FreeBSD
new file mode 100644
index 0000000..75a30b2
--- /dev/null
+++ b/gnu/usr.bin/gdb/README.FreeBSD
@@ -0,0 +1,15 @@
+This is a greatly pared down version of GDB-4.12 for FreeBSD 1.1.5 that
+supports debugging of shared executables and the new a.out format.
+
+I've collapsed some of the directories and removed lots of files from others.
+All that's included is those necessary to compile on FreeBSD 1.1.5, this was
+to keep the replacement for GDB-3 compact since none of the multiple
+architecture stuff is used. All the documentation has been put in the doc/
+directory.
+
+A full port of each library will probably be done for FreeBSD 2.0 and
+included as system libraries so that other build tools can share them.
+At that time a more complete port of GDB-4 will be done that allows
+configuration for different systems and uses the full libraries.
+
+paul@freefall.cdrom.com
diff --git a/gnu/usr.bin/gdb/README.gnu b/gnu/usr.bin/gdb/README.gnu
new file mode 100644
index 0000000..fa54dec
--- /dev/null
+++ b/gnu/usr.bin/gdb/README.gnu
@@ -0,0 +1,142 @@
+This is GDB, the GNU source-level debugger, presently running under un*x.
+
+Before compiling GDB, you must tell GDB what kind of machine you are
+running on. To do this, type `config.gdb machine', where machine is
+something like `vax' or `sun2'. For a list of valid machine types,
+type `config.gdb'.
+
+Normally config.gdb edits the makefile as necessary. If you have to
+edit the makefile on a standard machine listed in config.gdb this
+should be considered a bug and reported as such.
+
+Once these files are set up, just `make' will do everything,
+producing an executable `gdb' in this directory.
+
+If you want a new (current to this release) version of the manual, you
+will have to use the gdb.texinfo file provided with this distribution.
+The gdb.texinfo file requires the texinfo-format-buffer command from
+emacs 18.55 or later.
+
+About languages other than C...
+
+C++ support has been integrated into gdb. GDB should work with
+FORTRAN programs (if you have problem, please send a bug report), but
+I am not aware of anyone who is working on getting it to use the
+syntax of any language other than C or C++. Pascal programs which use
+sets, subranges, file variables, or nested functions will not
+currently work.
+
+About -gg format...
+
+Currently GDB version 3.x does *not* support GCC's -gg format. This
+is because it (in theory) has fast enough startup on dbx debugging
+format object files that -gg format is unnecessary (and hence
+undesirable, since it wastes space and processing power in gcc). I
+would like to hear people's opinions on the amount of time currently
+spent in startup; is it fast enough?
+
+About remote debugging...
+
+The two files remote-multi.shar and remote-sa.m68k.shar contain two
+examples of a remote stub to be used with remote.c. The the -multi
+file is a general stub that can probably be running on various
+different flavors of unix to allow debugging over a serial line from
+one machine to another. The remote-sa.m68k.shar is designed to run
+standalone on a 68k type cpu and communicate properley with the
+remote.c stub over a serial line.
+
+About reporting bugs...
+
+The correct address for reporting bugs found with gdb is
+"bug-gdb@prep.ai.mit.edu". Please send all bugs to that address.
+
+About xgdb...
+
+xgdb.c was provided to us by the user community; it is not an integral
+part of the gdb distribution. The problem of providing visual
+debugging support on top of gdb is peripheral to the GNU project and
+(at least right now) we can't afford to put time into it. So while we
+will be happy to incorporate user fixes to xgdb.c, we do not guarantee
+that it will work and we will not fix bugs reported in it. Someone is
+working on writing a new XGDB, so improving (e.g. by fixing it so that
+it will work, if it doesn't currently) the current one is not worth it.
+
+For those intersted in auto display of source and the availability of
+an editor while debugging I suggest trying gdb-mode in gnu-emacs.
+Comments on this mode are welcome.
+
+About the machine-dependent files...
+
+m-<machine>.h (param.h is a link to this file).
+This file contains macro definitions that express information
+about the machine's registers, stack frame format and instructions.
+
+<machine>-opcode.h (opcode.h is a link to this file).
+<machine>-pinsn.c (pinsn.c is a link to this file).
+These files contain the information necessary to print instructions
+for your cpu type.
+
+<machine>-dep.c (dep.c is a link to this file).
+Those routines which provide a low level interface to ptrace and which
+tend to be machine-dependent. (The machine-independent routines are in
+`infrun.c' and `inflow.c')
+
+About writing code for GDB...
+
+We appreciate having users contribute code that is of general use, but
+for it to be included in future GDB releases it must be cleanly
+written. We do not want to include changes that will needlessly make future
+maintainance difficult. It is not much harder to do things right, and
+in the long term it is worth it to the GNU project, and probably to
+you individually as well.
+
+Please code according to the GNU coding standards. If you do not have
+a copy, you can request one by sending mail to gnu@prep.ai.mit.edu.
+
+Please try to avoid making machine-specific changes to
+machine-independent files (i.e. all files except "param.h" and
+"dep.c". "pinsn.c" and "opcode.h" are processor-specific but not
+operating system-dependent). If this is unavoidable, put a hook in
+the machine-independent file which calls a (possibly)
+machine-dependent macro (for example, the IGNORE_SYMBOL macro can be
+used for any symbols which need to be ignored on a specific machine.
+Calling IGNORE_SYMBOL in dbxread.c is a lot cleaner than a maze of #if
+defined's). The machine-independent code should do whatever "most"
+machines want if the macro is not defined in param.h. Using #if
+defined can sometimes be OK (e.g. SET_STACK_LIMIT_HUGE) but should be
+conditionalized on a specific feature of an operating system (set in
+param.h) rather than something like #if defined(vax) or #if
+defined(SYSV).
+
+It is better to replace entire routines which may be system-specific,
+rather than put in a whole bunch of hooks which are probably not going
+to be helpful for any purpose other than your changes. For example,
+if you want to modify dbxread.c to deal with DBX debugging symbols
+which are in COFF files rather than BSD a.out files, do something
+along the lines of a macro GET_NEXT_SYMBOL, which could have
+different definitions for COFF and a.out, rather than trying to put
+the necessary changes throughout all the code in dbxread.c that
+currently assumes BSD format.
+
+Please avoid duplicating code. For example, if something needs to be
+changed in read_inferior_memory, it is very painful because there is a
+copy in every dep.c file. The correct way to do this is to put (in
+this case) the standard ptrace interfaces in a separate file ptrace.c,
+which is used by all systems which have ptrace. ptrace.c would deal
+with variations between systems the same way any system-independent
+file would (hooks, #if defined, etc.).
+
+About debugging gdb with itself...
+
+You probably want to do a "make TAGS" after you configure your
+distribution; this will put the machine dependent routines for your
+local machine where they will be accessed first by a M-period .
+
+Also, make sure that you've compiled gdb with your local cc or taken
+appropriate precautions regarding ansification of include files. See
+the Makefile for more information.
+
+The "info" command, when executed without a subcommand in a gdb being
+debugged by gdb, will pop you back up to the top level gdb. See
+.gdbinit for more details.
+
diff --git a/gnu/usr.bin/gdb/VERSION b/gnu/usr.bin/gdb/VERSION
new file mode 100644
index 0000000..b849ff8
--- /dev/null
+++ b/gnu/usr.bin/gdb/VERSION
@@ -0,0 +1 @@
+4.11
diff --git a/gnu/usr.bin/gdb/XGdbinit.samp b/gnu/usr.bin/gdb/XGdbinit.samp
new file mode 100644
index 0000000..a99f106
--- /dev/null
+++ b/gnu/usr.bin/gdb/XGdbinit.samp
@@ -0,0 +1,15 @@
+button "show" push-to-file %S
+button "back" pop-file
+button "break in" break %S
+button "break at" break %l
+button delete delete %b%e
+button backtrace
+button up
+button down
+button print print %E
+button print* print *(%E)
+button next
+button step
+button "do upto" until %l%e
+button finish
+button continue cont%e
diff --git a/gnu/usr.bin/gdb/Xgdb.ad b/gnu/usr.bin/gdb/Xgdb.ad
new file mode 100644
index 0000000..5f9fe99
--- /dev/null
+++ b/gnu/usr.bin/gdb/Xgdb.ad
@@ -0,0 +1,8 @@
+Xgdb*geometry: 580x874-0+28
+Xgdb*src*scrollVertical: whenneeded
+Xgdb*src*scrollHorizontal: whenneeded
+Xgdb*src*wrap: never
+Xgdb*src*editType: read
+Xgdb*frame.buttons.allowResize: true
+Xgdb*frame.buttons.skipAdjust: true
+Xgdb*frame*showGrip: false
diff --git a/gnu/usr.bin/gdb/bfd/COPYING b/gnu/usr.bin/gdb/bfd/COPYING
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/gnu/usr.bin/gdb/bfd/Makefile b/gnu/usr.bin/gdb/bfd/Makefile
new file mode 100644
index 0000000..ea29afe
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/Makefile
@@ -0,0 +1,17 @@
+LIB = bfd
+SRCS = archive.c archures.c bfd.c cache.c coffgen.c core.c ctor.c \
+ format.c init.c libbfd.c opncls.c reloc.c seclet.c section.c syms.c \
+ targets.c ecoff.c elf.c srec.c freebsd386.c aout32.c stab-syms.c \
+ cpu-i386.c trad-core.c
+
+CFLAGS+= -I$(.CURDIR)/. -I$(.CURDIR)/../gdb/.
+CFLAGS+= -DDEFAULT_VECTOR=freebsd386_vec -DSELECT_VECS='&freebsd386_vec' \
+ -DSELECT_ARCHITECTURES='bfd_i386_arch' -DTRAD_CORE
+
+NOPROFILE=no
+NOPIC=no
+
+install:
+ @echo -n
+
+.include <bsd.lib.mk>
diff --git a/gnu/usr.bin/gdb/bfd/README.FreeBSD b/gnu/usr.bin/gdb/bfd/README.FreeBSD
new file mode 100644
index 0000000..204119c
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/README.FreeBSD
@@ -0,0 +1,7 @@
+This is a greatly pared down libbfd directory. Only what's required to build
+gdb-4.12 on FreeBSD was kept.
+
+This is temporary. In FreeBSD 2.0 a fully ported libbfd will likely appear
+as a system library for use by all the build tools.
+
+paul@freefall.cdrom.com
diff --git a/gnu/usr.bin/gdb/bfd/VERSION b/gnu/usr.bin/gdb/bfd/VERSION
new file mode 100644
index 0000000..8bbe6cf
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/VERSION
@@ -0,0 +1 @@
+2.2
diff --git a/gnu/usr.bin/gdb/bfd/aout-target.h b/gnu/usr.bin/gdb/bfd/aout-target.h
new file mode 100644
index 0000000..65a22ff
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/aout-target.h
@@ -0,0 +1,429 @@
+/* Define a target vector and some small routines for a variant of a.out.
+ Copyright (C) 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "aout/aout64.h"
+#include "aout/stab_gnu.h"
+#include "aout/ar.h"
+/*#include "libaout.h"*/
+
+extern CONST struct reloc_howto_struct * NAME(aout,reloc_type_lookup) ();
+
+/* Set parameters about this a.out file that are machine-dependent.
+ This routine is called from some_aout_object_p just before it returns. */
+#ifndef MY_callback
+static bfd_target *
+DEFUN(MY(callback),(abfd),
+ bfd *abfd)
+{
+ struct internal_exec *execp = exec_hdr (abfd);
+
+ /* Calculate the file positions of the parts of a newly read aout header */
+ obj_textsec (abfd)->_raw_size = N_TXTSIZE(*execp);
+
+ /* The virtual memory addresses of the sections */
+ obj_textsec (abfd)->vma = N_TXTADDR(*execp);
+ obj_datasec (abfd)->vma = N_DATADDR(*execp);
+ obj_bsssec (abfd)->vma = N_BSSADDR(*execp);
+
+ /* The file offsets of the sections */
+ obj_textsec (abfd)->filepos = N_TXTOFF (*execp);
+ obj_datasec (abfd)->filepos = N_DATOFF (*execp);
+
+ /* The file offsets of the relocation info */
+ obj_textsec (abfd)->rel_filepos = N_TRELOFF(*execp);
+ obj_datasec (abfd)->rel_filepos = N_DRELOFF(*execp);
+
+ /* The file offsets of the string table and symbol table. */
+ obj_sym_filepos (abfd) = N_SYMOFF (*execp);
+ obj_str_filepos (abfd) = N_STROFF (*execp);
+
+ /* Determine the architecture and machine type of the object file. */
+#ifdef SET_ARCH_MACH
+ SET_ARCH_MACH(abfd, *execp);
+#else
+ bfd_default_set_arch_mach(abfd, DEFAULT_ARCH, 0);
+#endif
+
+ /* Don't set sizes now -- can't be sure until we know arch & mach.
+ Sizes get set in set_sizes callback, later. */
+#if 0
+ adata(abfd).page_size = PAGE_SIZE;
+#ifdef SEGMENT_SIZE
+ adata(abfd).segment_size = SEGMENT_SIZE;
+#else
+ adata(abfd).segment_size = PAGE_SIZE;
+#endif
+ adata(abfd).exec_bytes_size = EXEC_BYTES_SIZE;
+#endif
+
+ return abfd->xvec;
+}
+#endif
+
+#ifndef MY_object_p
+/* Finish up the reading of an a.out file header */
+
+static bfd_target *
+DEFUN(MY(object_p),(abfd),
+ bfd *abfd)
+{
+ struct external_exec exec_bytes; /* Raw exec header from file */
+ struct internal_exec exec; /* Cleaned-up exec header */
+ bfd_target *target;
+
+ if (bfd_read ((PTR) &exec_bytes, 1, EXEC_BYTES_SIZE, abfd)
+ != EXEC_BYTES_SIZE) {
+ bfd_error = wrong_format;
+ return 0;
+ }
+
+#ifdef NO_SWAP_MAGIC
+ memcpy (&exec.a_info, exec_bytes.e_info, sizeof(exec.a_info));
+#else
+ exec.a_info = bfd_h_get_32 (abfd, exec_bytes.e_info);
+#endif /* NO_SWAP_MAGIC */
+
+ if (N_BADMAG (exec)) return 0;
+#ifdef MACHTYPE_OK
+ if (!(MACHTYPE_OK (N_MACHTYPE (exec)))) return 0;
+#endif
+
+ NAME(aout,swap_exec_header_in)(abfd, &exec_bytes, &exec);
+ target = NAME(aout,some_aout_object_p) (abfd, &exec, MY(callback));
+
+#ifdef ENTRY_CAN_BE_ZERO
+ /* The NEWSOS3 entry-point is/was 0, which (amongst other lossage)
+ * means that it isn't obvious if EXEC_P should be set.
+ * All of the following must be true for an executable:
+ * There must be no relocations, the bfd can be neither an
+ * archive nor an archive element, and the file must be executable. */
+
+ if (exec.a_trsize + exec.a_drsize == 0
+ && bfd_get_format(abfd) == bfd_object && abfd->my_archive == NULL)
+ {
+ struct stat buf;
+#ifndef S_IXUSR
+#define S_IXUSR 0100 /* Execute by owner. */
+#endif
+ if (stat(abfd->filename, &buf) == 0 && (buf.st_mode & S_IXUSR))
+ abfd->flags |= EXEC_P;
+ }
+#endif /* ENTRY_CAN_BE_ZERO */
+
+ return target;
+}
+#define MY_object_p MY(object_p)
+#endif
+
+
+#ifndef MY_mkobject
+static boolean
+DEFUN(MY(mkobject),(abfd),
+ bfd *abfd)
+{
+ if (NAME(aout,mkobject)(abfd) == false)
+ return false;
+#if 0 /* Sizes get set in set_sizes callback, later, after we know
+ the architecture and machine. */
+ adata(abfd).page_size = PAGE_SIZE;
+#ifdef SEGMENT_SIZE
+ adata(abfd).segment_size = SEGMENT_SIZE;
+#else
+ adata(abfd).segment_size = PAGE_SIZE;
+#endif
+ adata(abfd).exec_bytes_size = EXEC_BYTES_SIZE;
+#endif
+ return true;
+}
+#define MY_mkobject MY(mkobject)
+#endif
+
+/* Write an object file.
+ Section contents have already been written. We write the
+ file header, symbols, and relocation. */
+
+#ifndef MY_write_object_contents
+static boolean
+DEFUN(MY(write_object_contents),(abfd),
+ bfd *abfd)
+{
+ struct external_exec exec_bytes;
+ struct internal_exec *execp = exec_hdr (abfd);
+
+#if CHOOSE_RELOC_SIZE
+ CHOOSE_RELOC_SIZE(abfd);
+#else
+ obj_reloc_entry_size (abfd) = RELOC_STD_SIZE;
+#endif
+
+ WRITE_HEADERS(abfd, execp);
+
+ return true;
+}
+#define MY_write_object_contents MY(write_object_contents)
+#endif
+
+#ifndef MY_set_sizes
+static boolean
+DEFUN(MY(set_sizes),(abfd), bfd *abfd)
+{
+ adata(abfd).page_size = PAGE_SIZE;
+#ifdef SEGMENT_SIZE
+ adata(abfd).segment_size = SEGMENT_SIZE;
+#else
+ adata(abfd).segment_size = PAGE_SIZE;
+#endif
+ adata(abfd).exec_bytes_size = EXEC_BYTES_SIZE;
+ return true;
+}
+#define MY_set_sizes MY(set_sizes)
+#endif
+
+#ifndef MY_backend_data
+static CONST struct aout_backend_data MY(backend_data) = {
+ 0, /* zmagic contiguous */
+ 0, /* text incl header */
+ 0, /* text vma? */
+ MY_set_sizes,
+ 0, /* exec header is counted */
+};
+#define MY_backend_data &MY(backend_data)
+#endif
+
+/* We assume BFD generic archive files. */
+#ifndef MY_openr_next_archived_file
+#define MY_openr_next_archived_file bfd_generic_openr_next_archived_file
+#endif
+#ifndef MY_generic_stat_arch_elt
+#define MY_generic_stat_arch_elt bfd_generic_stat_arch_elt
+#endif
+#ifndef MY_slurp_armap
+#define MY_slurp_armap bfd_slurp_bsd_armap
+#endif
+#ifndef MY_slurp_extended_name_table
+#define MY_slurp_extended_name_table _bfd_slurp_extended_name_table
+#endif
+#ifndef MY_write_armap
+#define MY_write_armap bsd_write_armap
+#endif
+#ifndef MY_truncate_arname
+#define MY_truncate_arname bfd_bsd_truncate_arname
+#endif
+
+/* No core file defined here -- configure in trad-core.c separately. */
+#ifndef MY_core_file_failing_command
+#define MY_core_file_failing_command _bfd_dummy_core_file_failing_command
+#endif
+#ifndef MY_core_file_failing_signal
+#define MY_core_file_failing_signal _bfd_dummy_core_file_failing_signal
+#endif
+#ifndef MY_core_file_matches_executable_p
+#define MY_core_file_matches_executable_p \
+ _bfd_dummy_core_file_matches_executable_p
+#endif
+#ifndef MY_core_file_p
+#define MY_core_file_p _bfd_dummy_target
+#endif
+
+#ifndef MY_bfd_debug_info_start
+#define MY_bfd_debug_info_start bfd_void
+#endif
+#ifndef MY_bfd_debug_info_end
+#define MY_bfd_debug_info_end bfd_void
+#endif
+#ifndef MY_bfd_debug_info_accumulate
+#define MY_bfd_debug_info_accumulate \
+ (void (*) PARAMS ((bfd*, struct sec *))) bfd_void
+#endif
+
+#ifndef MY_core_file_failing_command
+#define MY_core_file_failing_command NAME(aout,core_file_failing_command)
+#endif
+#ifndef MY_core_file_failing_signal
+#define MY_core_file_failing_signal NAME(aout,core_file_failing_signal)
+#endif
+#ifndef MY_core_file_matches_executable_p
+#define MY_core_file_matches_executable_p NAME(aout,core_file_matches_executable_p)
+#endif
+#ifndef MY_slurp_armap
+#define MY_slurp_armap NAME(aout,slurp_armap)
+#endif
+#ifndef MY_slurp_extended_name_table
+#define MY_slurp_extended_name_table NAME(aout,slurp_extended_name_table)
+#endif
+#ifndef MY_truncate_arname
+#define MY_truncate_arname NAME(aout,truncate_arname)
+#endif
+#ifndef MY_write_armap
+#define MY_write_armap NAME(aout,write_armap)
+#endif
+#ifndef MY_close_and_cleanup
+#define MY_close_and_cleanup NAME(aout,close_and_cleanup)
+#endif
+#ifndef MY_set_section_contents
+#define MY_set_section_contents NAME(aout,set_section_contents)
+#endif
+#ifndef MY_get_section_contents
+#define MY_get_section_contents NAME(aout,get_section_contents)
+#endif
+#ifndef MY_new_section_hook
+#define MY_new_section_hook NAME(aout,new_section_hook)
+#endif
+#ifndef MY_get_symtab_upper_bound
+#define MY_get_symtab_upper_bound NAME(aout,get_symtab_upper_bound)
+#endif
+#ifndef MY_get_symtab
+#define MY_get_symtab NAME(aout,get_symtab)
+#endif
+#ifndef MY_get_reloc_upper_bound
+#define MY_get_reloc_upper_bound NAME(aout,get_reloc_upper_bound)
+#endif
+#ifndef MY_canonicalize_reloc
+#define MY_canonicalize_reloc NAME(aout,canonicalize_reloc)
+#endif
+#ifndef MY_make_empty_symbol
+#define MY_make_empty_symbol NAME(aout,make_empty_symbol)
+#endif
+#ifndef MY_print_symbol
+#define MY_print_symbol NAME(aout,print_symbol)
+#endif
+#ifndef MY_get_symbol_info
+#define MY_get_symbol_info NAME(aout,get_symbol_info)
+#endif
+#ifndef MY_get_lineno
+#define MY_get_lineno NAME(aout,get_lineno)
+#endif
+#ifndef MY_set_arch_mach
+#define MY_set_arch_mach NAME(aout,set_arch_mach)
+#endif
+#ifndef MY_openr_next_archived_file
+#define MY_openr_next_archived_file NAME(aout,openr_next_archived_file)
+#endif
+#ifndef MY_find_nearest_line
+#define MY_find_nearest_line NAME(aout,find_nearest_line)
+#endif
+#ifndef MY_generic_stat_arch_elt
+#define MY_generic_stat_arch_elt NAME(aout,generic_stat_arch_elt)
+#endif
+#ifndef MY_sizeof_headers
+#define MY_sizeof_headers NAME(aout,sizeof_headers)
+#endif
+#ifndef MY_bfd_debug_info_start
+#define MY_bfd_debug_info_start NAME(aout,bfd_debug_info_start)
+#endif
+#ifndef MY_bfd_debug_info_end
+#define MY_bfd_debug_info_end NAME(aout,bfd_debug_info_end)
+#endif
+#ifndef MY_bfd_debug_info_accumulat
+#define MY_bfd_debug_info_accumulat NAME(aout,bfd_debug_info_accumulat)
+#endif
+#ifndef MY_reloc_howto_type_lookup
+#define MY_reloc_howto_type_lookup NAME(aout,reloc_type_lookup)
+#endif
+#ifndef MY_make_debug_symbol
+#define MY_make_debug_symbol 0
+#endif
+
+/* Aout symbols normally have leading underscores */
+#ifndef MY_symbol_leading_char
+#define MY_symbol_leading_char '_'
+#endif
+
+/* Aout archives normally use spaces for padding */
+#ifndef AR_PAD_CHAR
+#define AR_PAD_CHAR ' '
+#endif
+
+#ifndef MY_BFD_TARGET
+bfd_target MY(vec) =
+{
+ TARGETNAME, /* name */
+ bfd_target_aout_flavour,
+#ifdef TARGET_IS_BIG_ENDIAN_P
+ true, /* target byte order (big) */
+ true, /* target headers byte order (big) */
+#else
+ false, /* target byte order (little) */
+ false, /* target headers byte order (little) */
+#endif
+ (HAS_RELOC | EXEC_P | /* object flags */
+ HAS_LINENO | HAS_DEBUG |
+ HAS_SYMS | HAS_LOCALS | DYNAMIC | WP_TEXT | D_PAGED),
+ (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* section flags */
+ MY_symbol_leading_char,
+ AR_PAD_CHAR, /* ar_pad_char */
+ 15, /* ar_max_namelen */
+ 3, /* minimum alignment */
+#ifdef TARGET_IS_BIG_ENDIAN_P
+ bfd_getb64, bfd_getb_signed_64, bfd_putb64,
+ bfd_getb32, bfd_getb_signed_32, bfd_putb32,
+ bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* data */
+ bfd_getb64, bfd_getb_signed_64, bfd_putb64,
+ bfd_getb32, bfd_getb_signed_32, bfd_putb32,
+ bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* hdrs */
+#else
+ bfd_getl64, bfd_getl_signed_64, bfd_putl64,
+ bfd_getl32, bfd_getl_signed_32, bfd_putl32,
+ bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* data */
+ bfd_getl64, bfd_getl_signed_64, bfd_putl64,
+ bfd_getl32, bfd_getl_signed_32, bfd_putl32,
+ bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* hdrs */
+#endif
+ {_bfd_dummy_target, MY_object_p, /* bfd_check_format */
+ bfd_generic_archive_p, MY_core_file_p},
+ {bfd_false, MY_mkobject, /* bfd_set_format */
+ _bfd_generic_mkarchive, bfd_false},
+ {bfd_false, MY_write_object_contents, /* bfd_write_contents */
+ _bfd_write_archive_contents, bfd_false},
+
+ MY_core_file_failing_command,
+ MY_core_file_failing_signal,
+ MY_core_file_matches_executable_p,
+ MY_slurp_armap,
+ MY_slurp_extended_name_table,
+ MY_truncate_arname,
+ MY_write_armap,
+ MY_close_and_cleanup,
+ MY_set_section_contents,
+ MY_get_section_contents,
+ MY_new_section_hook,
+ MY_get_symtab_upper_bound,
+ MY_get_symtab,
+ MY_get_reloc_upper_bound,
+ MY_canonicalize_reloc,
+ MY_make_empty_symbol,
+ MY_print_symbol,
+ MY_get_symbol_info,
+ MY_get_lineno,
+ MY_set_arch_mach,
+ MY_openr_next_archived_file,
+ MY_find_nearest_line,
+ MY_generic_stat_arch_elt,
+ MY_sizeof_headers,
+ MY_bfd_debug_info_start,
+ MY_bfd_debug_info_end,
+ MY_bfd_debug_info_accumulate,
+ bfd_generic_get_relocated_section_contents,
+ bfd_generic_relax_section,
+ bfd_generic_seclet_link,
+ MY_reloc_howto_type_lookup,
+ MY_make_debug_symbol,
+ (PTR) MY_backend_data,
+};
+#endif /* MY_BFD_TARGET */
diff --git a/gnu/usr.bin/gdb/bfd/aout32.c b/gnu/usr.bin/gdb/bfd/aout32.c
new file mode 100644
index 0000000..84d2087
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/aout32.c
@@ -0,0 +1,23 @@
+/* BFD back-end for 32-bit a.out files.
+ Copyright (C) 1990-1991 Free Software Foundation, Inc.
+ Written by Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define ARCH_SIZE 32
+
+#include "aoutx.h"
diff --git a/gnu/usr.bin/gdb/bfd/aoutx.h b/gnu/usr.bin/gdb/bfd/aoutx.h
new file mode 100644
index 0000000..1ab4429
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/aoutx.h
@@ -0,0 +1,2568 @@
+/* BFD semi-generic back-end for a.out binaries.
+ Copyright 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+ Written by Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+SECTION
+ a.out backends
+
+
+DESCRIPTION
+
+ BFD supports a number of different flavours of a.out format,
+ though the major differences are only the sizes of the
+ structures on disk, and the shape of the relocation
+ information.
+
+ The support is split into a basic support file @code{aoutx.h}
+ and other files which derive functions from the base. One
+ derivation file is @code{aoutf1.h} (for a.out flavour 1), and
+ adds to the basic a.out functions support for sun3, sun4, 386
+ and 29k a.out files, to create a target jump vector for a
+ specific target.
+
+ This information is further split out into more specific files
+ for each machine, including @code{sunos.c} for sun3 and sun4,
+ @code{newsos3.c} for the Sony NEWS, and @code{demo64.c} for a
+ demonstration of a 64 bit a.out format.
+
+ The base file @code{aoutx.h} defines general mechanisms for
+ reading and writing records to and from disk, and various
+ other methods which BFD requires. It is included by
+ @code{aout32.c} and @code{aout64.c} to form the names
+ aout_32_swap_exec_header_in, aout_64_swap_exec_header_in, etc.
+
+ As an example, this is what goes on to make the back end for a
+ sun4, from aout32.c
+
+| #define ARCH_SIZE 32
+| #include "aoutx.h"
+
+ Which exports names:
+
+| ...
+| aout_32_canonicalize_reloc
+| aout_32_find_nearest_line
+| aout_32_get_lineno
+| aout_32_get_reloc_upper_bound
+| ...
+
+ from sunos.c
+
+| #define ARCH 32
+| #define TARGET_NAME "a.out-sunos-big"
+| #define VECNAME sunos_big_vec
+| #include "aoutf1.h"
+
+ requires all the names from aout32.c, and produces the jump vector
+
+| sunos_big_vec
+
+ The file host-aout.c is a special case. It is for a large set
+ of hosts that use ``more or less standard'' a.out files, and
+ for which cross-debugging is not interesting. It uses the
+ standard 32-bit a.out support routines, but determines the
+ file offsets and addresses of the text, data, and BSS
+ sections, the machine architecture and machine type, and the
+ entry point address, in a host-dependent manner. Once these
+ values have been determined, generic code is used to handle
+ the object file.
+
+ When porting it to run on a new system, you must supply:
+
+| HOST_PAGE_SIZE
+| HOST_SEGMENT_SIZE
+| HOST_MACHINE_ARCH (optional)
+| HOST_MACHINE_MACHINE (optional)
+| HOST_TEXT_START_ADDR
+| HOST_STACK_END_ADDR
+
+ in the file <<../include/sys/h-XXX.h>> (for your host). These
+ values, plus the structures and macros defined in <<a.out.h>> on
+ your host system, will produce a BFD target that will access
+ ordinary a.out files on your host. To configure a new machine
+ to use <<host-aout.c>., specify:
+
+| TDEFAULTS = -DDEFAULT_VECTOR=host_aout_big_vec
+| TDEPFILES= host-aout.o trad-core.o
+
+ in the <<config/mt-XXX>> file, and modify configure.in to use the
+ <<mt-XXX>> file (by setting "<<bfd_target=XXX>>") when your
+ configuration is selected.
+
+*/
+
+/* Some assumptions:
+ * Any BFD with D_PAGED set is ZMAGIC, and vice versa.
+ Doesn't matter what the setting of WP_TEXT is on output, but it'll
+ get set on input.
+ * Any BFD with D_PAGED clear and WP_TEXT set is NMAGIC.
+ * Any BFD with both flags clear is OMAGIC.
+ (Just want to make these explicit, so the conditions tested in this
+ file make sense if you're more familiar with a.out than with BFD.) */
+
+#define KEEPIT flags
+#define KEEPITTYPE int
+
+#include <assert.h>
+#include <string.h> /* For strchr and friends */
+#include "bfd.h"
+#include <sysdep.h>
+#include <ansidecl.h>
+
+struct external_exec;
+#include "libaout.h"
+#include "libbfd.h"
+#include "aout/aout64.h"
+#include "aout/stab_gnu.h"
+#include "aout/ar.h"
+
+extern void (*bfd_error_trap)();
+
+/*
+SUBSECTION
+ relocations
+
+DESCRIPTION
+ The file @code{aoutx.h} caters for both the @emph{standard}
+ and @emph{extended} forms of a.out relocation records.
+
+ The standard records are characterised by containing only an
+ address, a symbol index and a type field. The extended records
+ (used on 29ks and sparcs) also have a full integer for an
+ addend.
+
+*/
+#define CTOR_TABLE_RELOC_IDX 2
+
+#define howto_table_ext NAME(aout,ext_howto_table)
+#define howto_table_std NAME(aout,std_howto_table)
+
+reloc_howto_type howto_table_ext[] =
+{
+ /* type rs size bsz pcrel bitpos ovrf sf name part_inpl readmask setmask pcdone */
+ HOWTO(RELOC_8, 0, 0, 8, false, 0, complain_overflow_bitfield,0,"8", false, 0,0x000000ff, false),
+ HOWTO(RELOC_16, 0, 1, 16, false, 0, complain_overflow_bitfield,0,"16", false, 0,0x0000ffff, false),
+ HOWTO(RELOC_32, 0, 2, 32, false, 0, complain_overflow_bitfield,0,"32", false, 0,0xffffffff, false),
+ HOWTO(RELOC_DISP8, 0, 0, 8, true, 0, complain_overflow_signed,0,"DISP8", false, 0,0x000000ff, false),
+ HOWTO(RELOC_DISP16, 0, 1, 16, true, 0, complain_overflow_signed,0,"DISP16", false, 0,0x0000ffff, false),
+ HOWTO(RELOC_DISP32, 0, 2, 32, true, 0, complain_overflow_signed,0,"DISP32", false, 0,0xffffffff, false),
+ HOWTO(RELOC_WDISP30,2, 2, 30, true, 0, complain_overflow_signed,0,"WDISP30", false, 0,0x3fffffff, false),
+ HOWTO(RELOC_WDISP22,2, 2, 22, true, 0, complain_overflow_signed,0,"WDISP22", false, 0,0x003fffff, false),
+ HOWTO(RELOC_HI22, 10, 2, 22, false, 0, complain_overflow_bitfield,0,"HI22", false, 0,0x003fffff, false),
+ HOWTO(RELOC_22, 0, 2, 22, false, 0, complain_overflow_bitfield,0,"22", false, 0,0x003fffff, false),
+ HOWTO(RELOC_13, 0, 2, 13, false, 0, complain_overflow_bitfield,0,"13", false, 0,0x00001fff, false),
+ HOWTO(RELOC_LO10, 0, 2, 10, false, 0, complain_overflow_dont,0,"LO10", false, 0,0x000003ff, false),
+ HOWTO(RELOC_SFA_BASE,0, 2, 32, false, 0, complain_overflow_bitfield,0,"SFA_BASE", false, 0,0xffffffff, false),
+ HOWTO(RELOC_SFA_OFF13,0,2, 32, false, 0, complain_overflow_bitfield,0,"SFA_OFF13",false, 0,0xffffffff, false),
+ HOWTO(RELOC_BASE10, 0, 2, 16, false, 0, complain_overflow_bitfield,0,"BASE10", false, 0,0x0000ffff, false),
+ HOWTO(RELOC_BASE13, 0, 2, 13, false, 0, complain_overflow_bitfield,0,"BASE13", false, 0,0x00001fff, false),
+ HOWTO(RELOC_BASE22, 0, 2, 0, false, 0, complain_overflow_bitfield,0,"BASE22", false, 0,0x00000000, false),
+ HOWTO(RELOC_PC10, 0, 2, 10, false, 0, complain_overflow_bitfield,0,"PC10", false, 0,0x000003ff, false),
+ HOWTO(RELOC_PC22, 0, 2, 22, false, 0, complain_overflow_bitfield,0,"PC22", false, 0,0x003fffff, false),
+ HOWTO(RELOC_JMP_TBL,0, 2, 32, false, 0, complain_overflow_bitfield,0,"JMP_TBL", false, 0,0xffffffff, false),
+ HOWTO(RELOC_SEGOFF16,0, 2, 0, false, 0, complain_overflow_bitfield,0,"SEGOFF16", false, 0,0x00000000, false),
+ HOWTO(RELOC_GLOB_DAT,0, 2, 0, false, 0, complain_overflow_bitfield,0,"GLOB_DAT", false, 0,0x00000000, false),
+ HOWTO(RELOC_JMP_SLOT,0, 2, 0, false, 0, complain_overflow_bitfield,0,"JMP_SLOT", false, 0,0x00000000, false),
+ HOWTO(RELOC_RELATIVE,0, 2, 0, false, 0, complain_overflow_bitfield,0,"RELATIVE", false, 0,0x00000000, false),
+};
+
+/* Convert standard reloc records to "arelent" format (incl byte swap). */
+
+reloc_howto_type howto_table_std[] = {
+ /* type rs size bsz pcrel bitpos ovrf sf name part_inpl readmask setmask pcdone */
+HOWTO( 0, 0, 0, 8, false, 0, complain_overflow_bitfield,0,"8", true, 0x000000ff,0x000000ff, false),
+HOWTO( 1, 0, 1, 16, false, 0, complain_overflow_bitfield,0,"16", true, 0x0000ffff,0x0000ffff, false),
+HOWTO( 2, 0, 2, 32, false, 0, complain_overflow_bitfield,0,"32", true, 0xffffffff,0xffffffff, false),
+HOWTO( 3, 0, 4, 64, false, 0, complain_overflow_bitfield,0,"64", true, 0xdeaddead,0xdeaddead, false),
+HOWTO( 4, 0, 0, 8, true, 0, complain_overflow_signed, 0,"DISP8", true, 0x000000ff,0x000000ff, false),
+HOWTO( 5, 0, 1, 16, true, 0, complain_overflow_signed, 0,"DISP16", true, 0x0000ffff,0x0000ffff, false),
+HOWTO( 6, 0, 2, 32, true, 0, complain_overflow_signed, 0,"DISP32", true, 0xffffffff,0xffffffff, false),
+HOWTO( 7, 0, 4, 64, true, 0, complain_overflow_signed, 0,"DISP64", true, 0xfeedface,0xfeedface, false),
+{ -1 },
+HOWTO( 9, 0, 1, 16, false, 0, complain_overflow_bitfield,0,"BASE16", false,0xffffffff,0xffffffff, false),
+HOWTO(10, 0, 2, 32, false, 0, complain_overflow_bitfield,0,"BASE32", false,0xffffffff,0xffffffff, false),
+};
+
+#define TABLE_SIZE(TABLE) (sizeof(TABLE)/sizeof(TABLE[0]))
+
+CONST struct reloc_howto_struct *
+DEFUN(NAME(aout,reloc_type_lookup),(abfd,code),
+ bfd *abfd AND
+ bfd_reloc_code_real_type code)
+{
+#define EXT(i,j) case i: return &howto_table_ext[j]
+#define STD(i,j) case i: return &howto_table_std[j]
+ int ext = obj_reloc_entry_size (abfd) == RELOC_EXT_SIZE;
+ if (code == BFD_RELOC_CTOR)
+ switch (bfd_get_arch_info (abfd)->bits_per_address)
+ {
+ case 32:
+ code = BFD_RELOC_32;
+ break;
+ }
+ if (ext)
+ switch (code)
+ {
+ EXT (BFD_RELOC_32, 2);
+ EXT (BFD_RELOC_HI22, 8);
+ EXT (BFD_RELOC_LO10, 11);
+ EXT (BFD_RELOC_32_PCREL_S2, 6);
+ default: return (CONST struct reloc_howto_struct *) 0;
+ }
+ else
+ /* std relocs */
+ switch (code)
+ {
+ STD (BFD_RELOC_16, 1);
+ STD (BFD_RELOC_32, 2);
+ STD (BFD_RELOC_8_PCREL, 4);
+ STD (BFD_RELOC_16_PCREL, 5);
+ STD (BFD_RELOC_32_PCREL, 6);
+ STD (BFD_RELOC_16_BASEREL, 9);
+ STD (BFD_RELOC_32_BASEREL, 10);
+ default: return (CONST struct reloc_howto_struct *) 0;
+ }
+}
+
+extern bfd_error_vector_type bfd_error_vector;
+
+/*
+SUBSECTION
+ Internal Entry Points
+
+DESCRIPTION
+ @code{aoutx.h} exports several routines for accessing the
+ contents of an a.out file, which are gathered and exported in
+ turn by various format specific files (eg sunos.c).
+
+*/
+
+/*
+FUNCTION
+ aout_<size>_swap_exec_header_in
+
+DESCRIPTION
+ Swaps the information in an executable header taken from a raw
+ byte stream memory image, into the internal exec_header
+ structure.
+
+SYNOPSIS
+ void aout_<size>_swap_exec_header_in,
+ (bfd *abfd,
+ struct external_exec *raw_bytes,
+ struct internal_exec *execp);
+*/
+
+#ifndef NAME_swap_exec_header_in
+void
+DEFUN(NAME(aout,swap_exec_header_in),(abfd, raw_bytes, execp),
+ bfd *abfd AND
+ struct external_exec *raw_bytes AND
+ struct internal_exec *execp)
+{
+ struct external_exec *bytes = (struct external_exec *)raw_bytes;
+
+ /* The internal_exec structure has some fields that are unused in this
+ configuration (IE for i960), so ensure that all such uninitialized
+ fields are zero'd out. There are places where two of these structs
+ are memcmp'd, and thus the contents do matter. */
+ memset (execp, 0, sizeof (struct internal_exec));
+ /* Now fill in fields in the execp, from the bytes in the raw data. */
+ execp->a_info = bfd_h_get_32 (abfd, bytes->e_info);
+ execp->a_text = GET_WORD (abfd, bytes->e_text);
+ execp->a_data = GET_WORD (abfd, bytes->e_data);
+ execp->a_bss = GET_WORD (abfd, bytes->e_bss);
+ execp->a_syms = GET_WORD (abfd, bytes->e_syms);
+ execp->a_entry = GET_WORD (abfd, bytes->e_entry);
+ execp->a_trsize = GET_WORD (abfd, bytes->e_trsize);
+ execp->a_drsize = GET_WORD (abfd, bytes->e_drsize);
+}
+#define NAME_swap_exec_header_in NAME(aout,swap_exec_header_in)
+#endif
+
+/*
+FUNCTION
+ aout_<size>_swap_exec_header_out
+
+DESCRIPTION
+ Swaps the information in an internal exec header structure
+ into the supplied buffer ready for writing to disk.
+
+SYNOPSIS
+ void aout_<size>_swap_exec_header_out
+ (bfd *abfd,
+ struct internal_exec *execp,
+ struct external_exec *raw_bytes);
+*/
+void
+DEFUN(NAME(aout,swap_exec_header_out),(abfd, execp, raw_bytes),
+ bfd *abfd AND
+ struct internal_exec *execp AND
+ struct external_exec *raw_bytes)
+{
+ struct external_exec *bytes = (struct external_exec *)raw_bytes;
+
+ /* Now fill in fields in the raw data, from the fields in the exec struct. */
+ bfd_h_put_32 (abfd, execp->a_info , bytes->e_info);
+ PUT_WORD (abfd, execp->a_text , bytes->e_text);
+ PUT_WORD (abfd, execp->a_data , bytes->e_data);
+ PUT_WORD (abfd, execp->a_bss , bytes->e_bss);
+ PUT_WORD (abfd, execp->a_syms , bytes->e_syms);
+ PUT_WORD (abfd, execp->a_entry , bytes->e_entry);
+ PUT_WORD (abfd, execp->a_trsize, bytes->e_trsize);
+ PUT_WORD (abfd, execp->a_drsize, bytes->e_drsize);
+}
+
+
+
+/*
+FUNCTION
+ aout_<size>_some_aout_object_p
+
+DESCRIPTION
+ Some A.OUT variant thinks that the file whose format we're
+ checking is an a.out file. Do some more checking, and set up
+ for access if it really is. Call back to the calling
+ environments "finish up" function just before returning, to
+ handle any last-minute setup.
+
+SYNOPSIS
+ bfd_target *aout_<size>_some_aout_object_p
+ (bfd *abfd,
+ bfd_target *(*callback_to_real_object_p)());
+*/
+
+bfd_target *
+DEFUN(NAME(aout,some_aout_object_p),(abfd, execp, callback_to_real_object_p),
+ bfd *abfd AND
+ struct internal_exec *execp AND
+ bfd_target *(*callback_to_real_object_p) PARAMS ((bfd *)))
+{
+ struct aout_data_struct *rawptr, *oldrawptr;
+ bfd_target *result;
+
+ rawptr = (struct aout_data_struct *) bfd_zalloc (abfd, sizeof (struct aout_data_struct ));
+ if (rawptr == NULL) {
+ bfd_error = no_memory;
+ return 0;
+ }
+
+ oldrawptr = abfd->tdata.aout_data;
+ abfd->tdata.aout_data = rawptr;
+
+ /* Copy the contents of the old tdata struct.
+ In particular, we want the subformat, since for hpux it was set in
+ hp300hpux.c:swap_exec_header_in and will be used in
+ hp300hpux.c:callback. */
+ if (oldrawptr != NULL)
+ *abfd->tdata.aout_data = *oldrawptr;
+
+ abfd->tdata.aout_data->a.hdr = &rawptr->e;
+ *(abfd->tdata.aout_data->a.hdr) = *execp; /* Copy in the internal_exec struct */
+ execp = abfd->tdata.aout_data->a.hdr;
+
+ /* Set the file flags */
+ abfd->flags = NO_FLAGS;
+ if (execp->a_drsize || execp->a_trsize)
+ abfd->flags |= HAS_RELOC;
+ /* Setting of EXEC_P has been deferred to the bottom of this function */
+ if (execp->a_syms)
+ abfd->flags |= HAS_LINENO | HAS_DEBUG | HAS_SYMS | HAS_LOCALS;
+
+ if (N_MAGIC (*execp) == ZMAGIC)
+ {
+ abfd->flags |= D_PAGED|WP_TEXT;
+ adata(abfd).magic = z_magic;
+ }
+ else if (N_MAGIC (*execp) == NMAGIC)
+ {
+ abfd->flags |= WP_TEXT;
+ adata(abfd).magic = n_magic;
+ }
+ else
+ adata(abfd).magic = o_magic;
+
+ bfd_get_start_address (abfd) = execp->a_entry;
+
+ obj_aout_symbols (abfd) = (aout_symbol_type *)NULL;
+ bfd_get_symcount (abfd) = execp->a_syms / sizeof (struct external_nlist);
+
+ /* The default relocation entry size is that of traditional V7 Unix. */
+ obj_reloc_entry_size (abfd) = RELOC_STD_SIZE;
+
+ /* The default symbol entry size is that of traditional Unix. */
+ obj_symbol_entry_size (abfd) = EXTERNAL_NLIST_SIZE;
+
+ /* Create the sections. This is raunchy, but bfd_close wants to reclaim
+ them. */
+
+ obj_textsec (abfd) = bfd_make_section_old_way (abfd, ".text");
+ obj_datasec (abfd) = bfd_make_section_old_way (abfd, ".data");
+ obj_bsssec (abfd) = bfd_make_section_old_way (abfd, ".bss");
+
+#if 0
+ (void)bfd_make_section (abfd, ".text");
+ (void)bfd_make_section (abfd, ".data");
+ (void)bfd_make_section (abfd, ".bss");
+#endif
+
+ obj_datasec (abfd)->_raw_size = execp->a_data;
+ obj_bsssec (abfd)->_raw_size = execp->a_bss;
+
+ obj_textsec (abfd)->flags = (execp->a_trsize != 0 ?
+ (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS | SEC_RELOC) :
+ (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS));
+ obj_datasec (abfd)->flags = (execp->a_drsize != 0 ?
+ (SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_HAS_CONTENTS | SEC_RELOC) :
+ (SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_HAS_CONTENTS));
+ obj_bsssec (abfd)->flags = SEC_ALLOC;
+
+#ifdef THIS_IS_ONLY_DOCUMENTATION
+ /* The common code can't fill in these things because they depend
+ on either the start address of the text segment, the rounding
+ up of virtual addersses between segments, or the starting file
+ position of the text segment -- all of which varies among different
+ versions of a.out. */
+
+ /* Call back to the format-dependent code to fill in the rest of the
+ fields and do any further cleanup. Things that should be filled
+ in by the callback: */
+
+ struct exec *execp = exec_hdr (abfd);
+
+ obj_textsec (abfd)->size = N_TXTSIZE(*execp);
+ obj_textsec (abfd)->raw_size = N_TXTSIZE(*execp);
+ /* data and bss are already filled in since they're so standard */
+
+ /* The virtual memory addresses of the sections */
+ obj_textsec (abfd)->vma = N_TXTADDR(*execp);
+ obj_datasec (abfd)->vma = N_DATADDR(*execp);
+ obj_bsssec (abfd)->vma = N_BSSADDR(*execp);
+
+ /* The file offsets of the sections */
+ obj_textsec (abfd)->filepos = N_TXTOFF(*execp);
+ obj_datasec (abfd)->filepos = N_DATOFF(*execp);
+
+ /* The file offsets of the relocation info */
+ obj_textsec (abfd)->rel_filepos = N_TRELOFF(*execp);
+ obj_datasec (abfd)->rel_filepos = N_DRELOFF(*execp);
+
+ /* The file offsets of the string table and symbol table. */
+ obj_str_filepos (abfd) = N_STROFF (*execp);
+ obj_sym_filepos (abfd) = N_SYMOFF (*execp);
+
+ /* Determine the architecture and machine type of the object file. */
+ switch (N_MACHTYPE (*exec_hdr (abfd))) {
+ default:
+ abfd->obj_arch = bfd_arch_obscure;
+ break;
+ }
+
+ adata(abfd)->page_size = PAGE_SIZE;
+ adata(abfd)->segment_size = SEGMENT_SIZE;
+ adata(abfd)->exec_bytes_size = EXEC_BYTES_SIZE;
+
+ return abfd->xvec;
+
+ /* The architecture is encoded in various ways in various a.out variants,
+ or is not encoded at all in some of them. The relocation size depends
+ on the architecture and the a.out variant. Finally, the return value
+ is the bfd_target vector in use. If an error occurs, return zero and
+ set bfd_error to the appropriate error code.
+
+ Formats such as b.out, which have additional fields in the a.out
+ header, should cope with them in this callback as well. */
+#endif /* DOCUMENTATION */
+
+ result = (*callback_to_real_object_p)(abfd);
+
+ /* Now that the segment addresses have been worked out, take a better
+ guess at whether the file is executable. If the entry point
+ is within the text segment, assume it is. (This makes files
+ executable even if their entry point address is 0, as long as
+ their text starts at zero.)
+
+ At some point we should probably break down and stat the file and
+ declare it executable if (one of) its 'x' bits are on... */
+ if ((execp->a_entry >= obj_textsec(abfd)->vma) &&
+ (execp->a_entry < obj_textsec(abfd)->vma + obj_textsec(abfd)->_raw_size))
+ abfd->flags |= EXEC_P;
+ if (result)
+ {
+#if 0 /* These should be set correctly anyways. */
+ abfd->sections = obj_textsec (abfd);
+ obj_textsec (abfd)->next = obj_datasec (abfd);
+ obj_datasec (abfd)->next = obj_bsssec (abfd);
+#endif
+ }
+ else
+ {
+ free (rawptr);
+ abfd->tdata.aout_data = oldrawptr;
+ }
+ return result;
+}
+
+/*
+FUNCTION
+ aout_<size>_mkobject
+
+DESCRIPTION
+ This routine initializes a BFD for use with a.out files.
+
+SYNOPSIS
+ boolean aout_<size>_mkobject, (bfd *);
+*/
+
+boolean
+DEFUN(NAME(aout,mkobject),(abfd),
+ bfd *abfd)
+{
+ struct aout_data_struct *rawptr;
+
+ bfd_error = system_call_error;
+
+ /* Use an intermediate variable for clarity */
+ rawptr = (struct aout_data_struct *)bfd_zalloc (abfd, sizeof (struct aout_data_struct ));
+
+ if (rawptr == NULL) {
+ bfd_error = no_memory;
+ return false;
+ }
+
+ abfd->tdata.aout_data = rawptr;
+ exec_hdr (abfd) = &(rawptr->e);
+
+ /* For simplicity's sake we just make all the sections right here. */
+
+ obj_textsec (abfd) = (asection *)NULL;
+ obj_datasec (abfd) = (asection *)NULL;
+ obj_bsssec (abfd) = (asection *)NULL;
+ bfd_make_section (abfd, ".text");
+ bfd_make_section (abfd, ".data");
+ bfd_make_section (abfd, ".bss");
+ bfd_make_section (abfd, BFD_ABS_SECTION_NAME);
+ bfd_make_section (abfd, BFD_UND_SECTION_NAME);
+ bfd_make_section (abfd, BFD_COM_SECTION_NAME);
+
+ return true;
+}
+
+
+/*
+FUNCTION
+ aout_<size>_machine_type
+
+DESCRIPTION
+ Keep track of machine architecture and machine type for
+ a.out's. Return the machine_type for a particular
+ arch&machine, or M_UNKNOWN if that exact arch&machine can't be
+ represented in a.out format.
+
+ If the architecture is understood, machine type 0 (default)
+ should always be understood.
+
+SYNOPSIS
+ enum machine_type aout_<size>_machine_type
+ (enum bfd_architecture arch,
+ unsigned long machine));
+*/
+
+enum machine_type
+DEFUN(NAME(aout,machine_type),(arch, machine),
+ enum bfd_architecture arch AND
+ unsigned long machine)
+{
+ enum machine_type arch_flags;
+
+ arch_flags = M_UNKNOWN;
+
+ switch (arch) {
+ case bfd_arch_sparc:
+ if (machine == 0) arch_flags = M_SPARC;
+ break;
+
+ case bfd_arch_m68k:
+ switch (machine) {
+ case 0: arch_flags = M_68010; break;
+ case 68000: arch_flags = M_UNKNOWN; break;
+ case 68010: arch_flags = M_68010; break;
+ case 68020: arch_flags = M_68020; break;
+ default: arch_flags = M_UNKNOWN; break;
+ }
+ break;
+
+ case bfd_arch_i386:
+ if (machine == 0) arch_flags = M_386;
+ break;
+
+ case bfd_arch_a29k:
+ if (machine == 0) arch_flags = M_29K;
+ break;
+
+ case bfd_arch_mips:
+ switch (machine) {
+ case 0:
+ case 2000:
+ case 3000: arch_flags = M_MIPS1; break;
+ case 4000:
+ case 4400:
+ case 6000: arch_flags = M_MIPS2; break;
+ default: arch_flags = M_UNKNOWN; break;
+ }
+ break;
+
+ default:
+ arch_flags = M_UNKNOWN;
+ }
+ return arch_flags;
+}
+
+
+/*
+FUNCTION
+ aout_<size>_set_arch_mach
+
+DESCRIPTION
+ Sets the architecture and the machine of the BFD to those
+ values supplied. Verifies that the format can support the
+ architecture required.
+
+SYNOPSIS
+ boolean aout_<size>_set_arch_mach,
+ (bfd *,
+ enum bfd_architecture,
+ unsigned long machine));
+*/
+
+boolean
+DEFUN(NAME(aout,set_arch_mach),(abfd, arch, machine),
+ bfd *abfd AND
+ enum bfd_architecture arch AND
+ unsigned long machine)
+{
+ if (! bfd_default_set_arch_mach (abfd, arch, machine))
+ return false;
+
+ if (arch != bfd_arch_unknown &&
+ NAME(aout,machine_type) (arch, machine) == M_UNKNOWN)
+ return false; /* We can't represent this type */
+
+ /* Determine the size of a relocation entry */
+ switch (arch) {
+ case bfd_arch_sparc:
+ case bfd_arch_a29k:
+ case bfd_arch_mips:
+ obj_reloc_entry_size (abfd) = RELOC_EXT_SIZE;
+ break;
+ default:
+ obj_reloc_entry_size (abfd) = RELOC_STD_SIZE;
+ break;
+ }
+
+ return (*aout_backend_info(abfd)->set_sizes) (abfd);
+}
+
+boolean
+DEFUN (NAME (aout,adjust_sizes_and_vmas), (abfd, text_size, text_end),
+ bfd *abfd AND bfd_size_type *text_size AND file_ptr *text_end)
+{
+ struct internal_exec *execp = exec_hdr (abfd);
+ if ((obj_textsec (abfd) == NULL) || (obj_datasec (abfd) == NULL))
+ {
+ bfd_error = invalid_operation;
+ return false;
+ }
+ if (adata(abfd).magic != undecided_magic) return true;
+ obj_textsec(abfd)->_raw_size =
+ align_power(obj_textsec(abfd)->_raw_size,
+ obj_textsec(abfd)->alignment_power);
+
+ *text_size = obj_textsec (abfd)->_raw_size;
+ /* Rule (heuristic) for when to pad to a new page. Note that there
+ * are (at least) two ways demand-paged (ZMAGIC) files have been
+ * handled. Most Berkeley-based systems start the text segment at
+ * (PAGE_SIZE). However, newer versions of SUNOS start the text
+ * segment right after the exec header; the latter is counted in the
+ * text segment size, and is paged in by the kernel with the rest of
+ * the text. */
+
+ /* This perhaps isn't the right way to do this, but made it simpler for me
+ to understand enough to implement it. Better would probably be to go
+ right from BFD flags to alignment/positioning characteristics. But the
+ old code was sloppy enough about handling the flags, and had enough
+ other magic, that it was a little hard for me to understand. I think
+ I understand it better now, but I haven't time to do the cleanup this
+ minute. */
+ if (adata(abfd).magic == undecided_magic)
+ {
+ if (abfd->flags & D_PAGED)
+ /* Whether or not WP_TEXT is set -- let D_PAGED override. */
+ /* @@ What about QMAGIC? */
+ adata(abfd).magic = z_magic;
+ else if (abfd->flags & WP_TEXT)
+ adata(abfd).magic = n_magic;
+ else
+ adata(abfd).magic = o_magic;
+ }
+
+#ifdef BFD_AOUT_DEBUG /* requires gcc2 */
+#if __GNUC__ >= 2
+ fprintf (stderr, "%s text=<%x,%x,%x> data=<%x,%x,%x> bss=<%x,%x,%x>\n",
+ ({ char *str;
+ switch (adata(abfd).magic) {
+ case n_magic: str = "NMAGIC"; break;
+ case o_magic: str = "OMAGIC"; break;
+ case z_magic: str = "ZMAGIC"; break;
+ default: abort ();
+ }
+ str;
+ }),
+ obj_textsec(abfd)->vma, obj_textsec(abfd)->_raw_size, obj_textsec(abfd)->alignment_power,
+ obj_datasec(abfd)->vma, obj_datasec(abfd)->_raw_size, obj_datasec(abfd)->alignment_power,
+ obj_bsssec(abfd)->vma, obj_bsssec(abfd)->_raw_size, obj_bsssec(abfd)->alignment_power);
+#endif
+#endif
+
+ switch (adata(abfd).magic)
+ {
+ case o_magic:
+ {
+ file_ptr pos = adata (abfd).exec_bytes_size;
+ bfd_vma vma = 0;
+ int pad = 0;
+
+ obj_textsec(abfd)->filepos = pos;
+ pos += obj_textsec(abfd)->_raw_size;
+ vma += obj_textsec(abfd)->_raw_size;
+ if (!obj_datasec(abfd)->user_set_vma)
+ {
+#if 0 /* ?? Does alignment in the file image really matter? */
+ pad = align_power (vma, obj_datasec(abfd)->alignment_power) - vma;
+#endif
+ obj_textsec(abfd)->_raw_size += pad;
+ pos += pad;
+ vma += pad;
+ obj_datasec(abfd)->vma = vma;
+ }
+ obj_datasec(abfd)->filepos = pos;
+ pos += obj_datasec(abfd)->_raw_size;
+ vma += obj_datasec(abfd)->_raw_size;
+ if (!obj_bsssec(abfd)->user_set_vma)
+ {
+#if 0
+ pad = align_power (vma, obj_bsssec(abfd)->alignment_power) - vma;
+#endif
+ obj_datasec(abfd)->_raw_size += pad;
+ pos += pad;
+ vma += pad;
+ obj_bsssec(abfd)->vma = vma;
+ }
+ obj_bsssec(abfd)->filepos = pos;
+ execp->a_text = obj_textsec(abfd)->_raw_size;
+ execp->a_data = obj_datasec(abfd)->_raw_size;
+ execp->a_bss = obj_bsssec(abfd)->_raw_size;
+ N_SET_MAGIC (*execp, OMAGIC);
+ }
+ break;
+ case z_magic:
+ {
+ bfd_size_type data_pad, text_pad;
+ file_ptr text_end;
+ CONST struct aout_backend_data *abdp;
+ int ztih;
+ bfd_vma data_vma;
+
+ abdp = aout_backend_info (abfd);
+ ztih = abdp && abdp->text_includes_header;
+ obj_textsec(abfd)->filepos = (ztih
+ ? adata(abfd).exec_bytes_size
+ : adata(abfd).page_size);
+ if (! obj_textsec(abfd)->user_set_vma)
+ /* ?? Do we really need to check for relocs here? */
+ obj_textsec(abfd)->vma = ((abfd->flags & HAS_RELOC)
+ ? 0
+ : (ztih
+ ? (abdp->default_text_vma
+ + adata(abfd).exec_bytes_size)
+ : abdp->default_text_vma));
+ /* Could take strange alignment of text section into account here? */
+
+ /* Find start of data. */
+ text_end = obj_textsec(abfd)->filepos + obj_textsec(abfd)->_raw_size;
+ text_pad = BFD_ALIGN (text_end, adata(abfd).page_size) - text_end;
+ obj_textsec(abfd)->_raw_size += text_pad;
+ text_end += text_pad;
+
+ if (!obj_datasec(abfd)->user_set_vma)
+ {
+ bfd_vma vma;
+ vma = obj_textsec(abfd)->vma + obj_textsec(abfd)->_raw_size;
+ obj_datasec(abfd)->vma = BFD_ALIGN (vma, adata(abfd).segment_size);
+ }
+ data_vma = obj_datasec(abfd)->vma;
+ if (abdp && abdp->zmagic_mapped_contiguous)
+ {
+ text_pad = (obj_datasec(abfd)->vma
+ - obj_textsec(abfd)->vma
+ - obj_textsec(abfd)->_raw_size);
+ obj_textsec(abfd)->_raw_size += text_pad;
+ }
+ obj_datasec(abfd)->filepos = (obj_textsec(abfd)->filepos
+ + obj_textsec(abfd)->_raw_size);
+
+ /* Fix up exec header while we're at it. */
+ execp->a_text = obj_textsec(abfd)->_raw_size;
+ if (ztih && (!abdp || (abdp && !abdp->exec_header_not_counted)))
+ execp->a_text += adata(abfd).exec_bytes_size;
+ N_SET_MAGIC (*execp, ZMAGIC);
+ /* Spec says data section should be rounded up to page boundary. */
+ /* If extra space in page is left after data section, fudge data
+ in the header so that the bss section looks smaller by that
+ amount. We'll start the bss section there, and lie to the OS. */
+ obj_datasec(abfd)->_raw_size
+ = align_power (obj_datasec(abfd)->_raw_size,
+ obj_bsssec(abfd)->alignment_power);
+ execp->a_data = BFD_ALIGN (obj_datasec(abfd)->_raw_size,
+ adata(abfd).page_size);
+ data_pad = execp->a_data - obj_datasec(abfd)->_raw_size;
+
+ if (!obj_bsssec(abfd)->user_set_vma)
+ obj_bsssec(abfd)->vma = (obj_datasec(abfd)->vma
+ + obj_datasec(abfd)->_raw_size);
+ if (data_pad > obj_bsssec(abfd)->_raw_size)
+ execp->a_bss = 0;
+ else
+ execp->a_bss = obj_bsssec(abfd)->_raw_size - data_pad;
+ }
+ break;
+ case n_magic:
+ {
+ file_ptr pos = adata(abfd).exec_bytes_size;
+ bfd_vma vma = 0;
+ int pad;
+
+ obj_textsec(abfd)->filepos = pos;
+ if (!obj_textsec(abfd)->user_set_vma)
+ obj_textsec(abfd)->vma = vma;
+ else
+ vma = obj_textsec(abfd)->vma;
+ pos += obj_textsec(abfd)->_raw_size;
+ vma += obj_textsec(abfd)->_raw_size;
+ obj_datasec(abfd)->filepos = pos;
+ if (!obj_datasec(abfd)->user_set_vma)
+ obj_datasec(abfd)->vma = BFD_ALIGN (vma, adata(abfd).segment_size);
+ vma = obj_datasec(abfd)->vma;
+
+ /* Since BSS follows data immediately, see if it needs alignment. */
+ vma += obj_datasec(abfd)->_raw_size;
+ pad = align_power (vma, obj_bsssec(abfd)->alignment_power) - vma;
+ obj_datasec(abfd)->_raw_size += pad;
+ pos += obj_datasec(abfd)->_raw_size;
+
+ if (!obj_bsssec(abfd)->user_set_vma)
+ obj_bsssec(abfd)->vma = vma;
+ else
+ vma = obj_bsssec(abfd)->vma;
+ }
+ execp->a_text = obj_textsec(abfd)->_raw_size;
+ execp->a_data = obj_datasec(abfd)->_raw_size;
+ execp->a_bss = obj_bsssec(abfd)->_raw_size;
+ N_SET_MAGIC (*execp, NMAGIC);
+ break;
+ default:
+ abort ();
+ }
+#ifdef BFD_AOUT_DEBUG
+ fprintf (stderr, " text=<%x,%x,%x> data=<%x,%x,%x> bss=<%x,%x>\n",
+ obj_textsec(abfd)->vma, obj_textsec(abfd)->_raw_size, obj_textsec(abfd)->filepos,
+ obj_datasec(abfd)->vma, obj_datasec(abfd)->_raw_size, obj_datasec(abfd)->filepos,
+ obj_bsssec(abfd)->vma, obj_bsssec(abfd)->_raw_size);
+#endif
+ return true;
+}
+
+/*
+FUNCTION
+ aout_<size>_new_section_hook
+
+DESCRIPTION
+ Called by the BFD in response to a @code{bfd_make_section}
+ request.
+
+SYNOPSIS
+ boolean aout_<size>_new_section_hook,
+ (bfd *abfd,
+ asection *newsect));
+*/
+boolean
+DEFUN(NAME(aout,new_section_hook),(abfd, newsect),
+ bfd *abfd AND
+ asection *newsect)
+{
+ /* align to double at least */
+ newsect->alignment_power = bfd_get_arch_info(abfd)->section_align_power;
+
+
+ if (bfd_get_format (abfd) == bfd_object)
+ {
+ if (obj_textsec(abfd) == NULL && !strcmp(newsect->name, ".text")) {
+ obj_textsec(abfd)= newsect;
+ newsect->target_index = N_TEXT | N_EXT;
+ return true;
+ }
+
+ if (obj_datasec(abfd) == NULL && !strcmp(newsect->name, ".data")) {
+ obj_datasec(abfd) = newsect;
+ newsect->target_index = N_DATA | N_EXT;
+ return true;
+ }
+
+ if (obj_bsssec(abfd) == NULL && !strcmp(newsect->name, ".bss")) {
+ obj_bsssec(abfd) = newsect;
+ newsect->target_index = N_BSS | N_EXT;
+ return true;
+ }
+
+ }
+
+ /* We allow more than three sections internally */
+ return true;
+}
+
+boolean
+DEFUN(NAME(aout,set_section_contents),(abfd, section, location, offset, count),
+ bfd *abfd AND
+ sec_ptr section AND
+ PTR location AND
+ file_ptr offset AND
+ bfd_size_type count)
+{
+ file_ptr text_end;
+ bfd_size_type text_size;
+
+ if (abfd->output_has_begun == false)
+ {
+ if (NAME(aout,adjust_sizes_and_vmas) (abfd,
+ &text_size,
+ &text_end) == false)
+ return false;
+ }
+
+ /* regardless, once we know what we're doing, we might as well get going */
+ if (section != obj_bsssec(abfd))
+ {
+ bfd_seek (abfd, section->filepos + offset, SEEK_SET);
+
+ if (count) {
+ return (bfd_write ((PTR)location, 1, count, abfd) == count) ?
+ true : false;
+ }
+ return true;
+ }
+ return true;
+}
+
+/* Classify stabs symbols */
+
+#define sym_in_text_section(sym) \
+ (((sym)->type & (N_ABS | N_TEXT | N_DATA | N_BSS))== N_TEXT)
+
+#define sym_in_data_section(sym) \
+ (((sym)->type & (N_ABS | N_TEXT | N_DATA | N_BSS))== N_DATA)
+
+#define sym_in_bss_section(sym) \
+ (((sym)->type & (N_ABS | N_TEXT | N_DATA | N_BSS))== N_BSS)
+
+/* Symbol is undefined if type is N_UNDF|N_EXT and if it has
+ zero in the "value" field. Nonzeroes there are fortrancommon
+ symbols. */
+#define sym_is_undefined(sym) \
+ ((sym)->type == (N_UNDF | N_EXT) && (sym)->symbol.value == 0)
+
+/* Symbol is a global definition if N_EXT is on and if it has
+ a nonzero type field. */
+#define sym_is_global_defn(sym) \
+ (((sym)->type & N_EXT) && (sym)->type & N_TYPE)
+
+/* Symbol is debugger info if any bits outside N_TYPE or N_EXT
+ are on. */
+#define sym_is_debugger_info(sym) \
+ (((sym)->type & ~(N_EXT | N_TYPE)) || (sym)->type == N_FN)
+
+#define sym_is_fortrancommon(sym) \
+ (((sym)->type == (N_EXT)) && (sym)->symbol.value != 0)
+
+/* Symbol is absolute if it has N_ABS set */
+#define sym_is_absolute(sym) \
+ (((sym)->type & N_TYPE)== N_ABS)
+
+
+#define sym_is_indirect(sym) \
+ (((sym)->type & N_ABS)== N_ABS)
+
+/* Only in their own functions for ease of debugging; when sym flags have
+ stabilised these should be inlined into their (single) caller */
+
+static void
+DEFUN (translate_from_native_sym_flags, (sym_pointer, cache_ptr, abfd),
+ struct external_nlist *sym_pointer AND
+ aout_symbol_type * cache_ptr AND
+ bfd * abfd)
+{
+ cache_ptr->symbol.section = 0;
+ switch (cache_ptr->type & N_TYPE)
+ {
+ case N_SETA:
+ case N_SETT:
+ case N_SETD:
+ case N_SETB:
+ {
+ char *copy = bfd_alloc (abfd, strlen (cache_ptr->symbol.name) + 1);
+ asection *section;
+ asection *into_section;
+
+ arelent_chain *reloc = (arelent_chain *) bfd_alloc (abfd, sizeof (arelent_chain));
+ strcpy (copy, cache_ptr->symbol.name);
+
+ /* Make sure that this bfd has a section with the right contructor
+ name */
+ section = bfd_get_section_by_name (abfd, copy);
+ if (!section)
+ section = bfd_make_section (abfd, copy);
+
+ /* Build a relocation entry for the constructor */
+ switch ((cache_ptr->type & N_TYPE))
+ {
+ case N_SETA:
+ into_section = &bfd_abs_section;
+ cache_ptr->type = N_ABS;
+ break;
+ case N_SETT:
+ into_section = (asection *) obj_textsec (abfd);
+ cache_ptr->type = N_TEXT;
+ break;
+ case N_SETD:
+ into_section = (asection *) obj_datasec (abfd);
+ cache_ptr->type = N_DATA;
+ break;
+ case N_SETB:
+ into_section = (asection *) obj_bsssec (abfd);
+ cache_ptr->type = N_BSS;
+ break;
+ default:
+ abort ();
+ }
+
+ /* Build a relocation pointing into the constuctor section
+ pointing at the symbol in the set vector specified */
+
+ reloc->relent.addend = cache_ptr->symbol.value;
+ cache_ptr->symbol.section = into_section->symbol->section;
+ reloc->relent.sym_ptr_ptr = into_section->symbol_ptr_ptr;
+
+
+ /* We modify the symbol to belong to a section depending upon the
+ name of the symbol - probably __CTOR__ or __DTOR__ but we don't
+ really care, and add to the size of the section to contain a
+ pointer to the symbol. Build a reloc entry to relocate to this
+ symbol attached to this section. */
+
+ section->flags = SEC_CONSTRUCTOR;
+
+
+ section->reloc_count++;
+ section->alignment_power = 2;
+
+ reloc->next = section->constructor_chain;
+ section->constructor_chain = reloc;
+ reloc->relent.address = section->_raw_size;
+ section->_raw_size += sizeof (int *);
+
+ reloc->relent.howto
+ = (obj_reloc_entry_size(abfd) == RELOC_EXT_SIZE
+ ? howto_table_ext : howto_table_std)
+ + CTOR_TABLE_RELOC_IDX;
+ cache_ptr->symbol.flags |= BSF_CONSTRUCTOR;
+ }
+ break;
+ default:
+ if (cache_ptr->type == N_WARNING)
+ {
+ /* This symbol is the text of a warning message, the next symbol
+ is the symbol to associate the warning with */
+ cache_ptr->symbol.flags = BSF_DEBUGGING | BSF_WARNING;
+
+ /* @@ Stuffing pointers into integers is a no-no.
+ We can usually get away with it if the integer is
+ large enough though. */
+ if (sizeof (cache_ptr + 1) > sizeof (bfd_vma))
+ abort ();
+ cache_ptr->symbol.value = (bfd_vma) ((cache_ptr + 1));
+
+ /* We furgle with the next symbol in place.
+ We don't want it to be undefined, we'll trample the type */
+ (sym_pointer + 1)->e_type[0] = 0xff;
+ break;
+ }
+ if ((cache_ptr->type | N_EXT) == (N_INDR | N_EXT))
+ {
+ /* Two symbols in a row for an INDR message. The first symbol
+ contains the name we will match, the second symbol contains
+ the name the first name is translated into. It is supplied to
+ us undefined. This is good, since we want to pull in any files
+ which define it */
+ cache_ptr->symbol.flags = BSF_DEBUGGING | BSF_INDIRECT;
+
+ /* @@ Stuffing pointers into integers is a no-no.
+ We can usually get away with it if the integer is
+ large enough though. */
+ if (sizeof (cache_ptr + 1) > sizeof (bfd_vma))
+ abort ();
+
+ cache_ptr->symbol.value = (bfd_vma) ((cache_ptr + 1));
+ cache_ptr->symbol.section = &bfd_ind_section;
+ }
+
+ else if (sym_is_debugger_info (cache_ptr))
+ {
+ cache_ptr->symbol.flags = BSF_DEBUGGING;
+ /* Work out the section correct for this symbol */
+ switch (cache_ptr->type & N_TYPE)
+ {
+ case N_TEXT:
+ case N_FN:
+ cache_ptr->symbol.section = obj_textsec (abfd);
+ cache_ptr->symbol.value -= obj_textsec (abfd)->vma;
+ break;
+ case N_DATA:
+ cache_ptr->symbol.value -= obj_datasec (abfd)->vma;
+ cache_ptr->symbol.section = obj_datasec (abfd);
+ break;
+ case N_BSS:
+ cache_ptr->symbol.section = obj_bsssec (abfd);
+ cache_ptr->symbol.value -= obj_bsssec (abfd)->vma;
+ break;
+ default:
+ case N_ABS:
+
+ cache_ptr->symbol.section = &bfd_abs_section;
+ break;
+ }
+ }
+ else
+ {
+
+ if (sym_is_fortrancommon (cache_ptr))
+ {
+ cache_ptr->symbol.flags = 0;
+ cache_ptr->symbol.section = &bfd_com_section;
+ }
+ else
+ {
+
+
+ }
+
+ /* In a.out, the value of a symbol is always relative to the
+ * start of the file, if this is a data symbol we'll subtract
+ * the size of the text section to get the section relative
+ * value. If this is a bss symbol (which would be strange)
+ * we'll subtract the size of the previous two sections
+ * to find the section relative address.
+ */
+
+ if (sym_in_text_section (cache_ptr))
+ {
+ cache_ptr->symbol.value -= obj_textsec (abfd)->vma;
+ cache_ptr->symbol.section = obj_textsec (abfd);
+ }
+ else if (sym_in_data_section (cache_ptr))
+ {
+ cache_ptr->symbol.value -= obj_datasec (abfd)->vma;
+ cache_ptr->symbol.section = obj_datasec (abfd);
+ }
+ else if (sym_in_bss_section (cache_ptr))
+ {
+ cache_ptr->symbol.section = obj_bsssec (abfd);
+ cache_ptr->symbol.value -= obj_bsssec (abfd)->vma;
+ }
+ else if (sym_is_undefined (cache_ptr))
+ {
+ cache_ptr->symbol.flags = 0;
+ cache_ptr->symbol.section = &bfd_und_section;
+ }
+ else if (sym_is_absolute (cache_ptr))
+ {
+ cache_ptr->symbol.section = &bfd_abs_section;
+ }
+
+ if (sym_is_global_defn (cache_ptr))
+ {
+ cache_ptr->symbol.flags = BSF_GLOBAL | BSF_EXPORT;
+ }
+ else
+ {
+ cache_ptr->symbol.flags = BSF_LOCAL;
+ }
+ }
+ }
+ if (cache_ptr->symbol.section == 0)
+ abort ();
+}
+
+
+
+static void
+DEFUN(translate_to_native_sym_flags,(sym_pointer, cache_ptr, abfd),
+ struct external_nlist *sym_pointer AND
+ asymbol *cache_ptr AND
+ bfd *abfd)
+{
+ bfd_vma value = cache_ptr->value;
+
+ /* mask out any existing type bits in case copying from one section
+ to another */
+ sym_pointer->e_type[0] &= ~N_TYPE;
+
+
+ /* We attempt to order these tests by decreasing frequency of success,
+ according to tcov when linking the linker. */
+ if (bfd_get_output_section(cache_ptr) == &bfd_abs_section) {
+ sym_pointer->e_type[0] |= N_ABS;
+ }
+ else if (bfd_get_output_section(cache_ptr) == obj_textsec (abfd)) {
+ sym_pointer->e_type[0] |= N_TEXT;
+ }
+ else if (bfd_get_output_section(cache_ptr) == obj_datasec (abfd)) {
+ sym_pointer->e_type[0] |= N_DATA;
+ }
+ else if (bfd_get_output_section(cache_ptr) == obj_bsssec (abfd)) {
+ sym_pointer->e_type[0] |= N_BSS;
+ }
+ else if (bfd_get_output_section(cache_ptr) == &bfd_und_section)
+ {
+ sym_pointer->e_type[0] = (N_UNDF | N_EXT);
+ }
+ else if (bfd_get_output_section(cache_ptr) == &bfd_ind_section)
+ {
+ sym_pointer->e_type[0] = N_INDR;
+ }
+ else if (bfd_is_com_section (bfd_get_output_section (cache_ptr))) {
+ sym_pointer->e_type[0] = (N_UNDF | N_EXT);
+ }
+ else {
+ if (cache_ptr->section->output_section)
+ {
+
+ bfd_error_vector.nonrepresentable_section(abfd,
+ bfd_get_output_section(cache_ptr)->name);
+ }
+ else
+ {
+ bfd_error_vector.nonrepresentable_section(abfd,
+ cache_ptr->section->name);
+
+ }
+
+ }
+ /* Turn the symbol from section relative to absolute again */
+
+ value += cache_ptr->section->output_section->vma + cache_ptr->section->output_offset ;
+
+
+ if (cache_ptr->flags & (BSF_WARNING)) {
+ (sym_pointer+1)->e_type[0] = 1;
+ }
+
+ if (cache_ptr->flags & BSF_DEBUGGING) {
+ sym_pointer->e_type[0] = ((aout_symbol_type *)cache_ptr)->type;
+ }
+ else if (cache_ptr->flags & (BSF_GLOBAL | BSF_EXPORT)) {
+ sym_pointer->e_type[0] |= N_EXT;
+ }
+ if (cache_ptr->flags & BSF_CONSTRUCTOR) {
+ int type = ((aout_symbol_type *)cache_ptr)->type;
+ switch (type)
+ {
+ case N_ABS: type = N_SETA; break;
+ case N_TEXT: type = N_SETT; break;
+ case N_DATA: type = N_SETD; break;
+ case N_BSS: type = N_SETB; break;
+ }
+ sym_pointer->e_type[0] = type;
+ }
+
+ PUT_WORD(abfd, value, sym_pointer->e_value);
+}
+
+/* Native-level interface to symbols. */
+
+/* We read the symbols into a buffer, which is discarded when this
+function exits. We read the strings into a buffer large enough to
+hold them all plus all the cached symbol entries. */
+
+asymbol *
+DEFUN(NAME(aout,make_empty_symbol),(abfd),
+ bfd *abfd)
+{
+ aout_symbol_type *new =
+ (aout_symbol_type *)bfd_zalloc (abfd, sizeof (aout_symbol_type));
+ new->symbol.the_bfd = abfd;
+
+ return &new->symbol;
+}
+
+boolean
+DEFUN(NAME(aout,slurp_symbol_table),(abfd),
+ bfd *abfd)
+{
+ bfd_size_type symbol_size;
+ bfd_size_type string_size;
+ unsigned char string_chars[BYTES_IN_WORD];
+ struct external_nlist *syms;
+ char *strings;
+ aout_symbol_type *cached;
+
+ /* If there's no work to be done, don't do any */
+ if (obj_aout_symbols (abfd) != (aout_symbol_type *)NULL) return true;
+ symbol_size = exec_hdr(abfd)->a_syms;
+ if (symbol_size == 0)
+ {
+ bfd_error = no_symbols;
+ return false;
+ }
+
+ bfd_seek (abfd, obj_str_filepos (abfd), SEEK_SET);
+ if (bfd_read ((PTR)string_chars, BYTES_IN_WORD, 1, abfd) != BYTES_IN_WORD)
+ return false;
+ string_size = GET_WORD (abfd, string_chars);
+
+ strings =(char *) bfd_alloc(abfd, string_size + 1);
+ cached = (aout_symbol_type *)
+ bfd_zalloc(abfd, (bfd_size_type)(bfd_get_symcount (abfd) * sizeof(aout_symbol_type)));
+
+ /* malloc this, so we can free it if simply. The symbol caching
+ might want to allocate onto the bfd's obstack */
+ syms = (struct external_nlist *) bfd_xmalloc(symbol_size);
+ bfd_seek (abfd, obj_sym_filepos (abfd), SEEK_SET);
+ if (bfd_read ((PTR)syms, 1, symbol_size, abfd) != symbol_size)
+ {
+ bailout:
+ if (syms)
+ free (syms);
+ if (cached)
+ bfd_release (abfd, cached);
+ if (strings)
+ bfd_release (abfd, strings);
+ return false;
+ }
+
+ bfd_seek (abfd, obj_str_filepos (abfd), SEEK_SET);
+ if (bfd_read ((PTR)strings, 1, string_size, abfd) != string_size)
+ {
+ goto bailout;
+ }
+ strings[string_size] = 0; /* Just in case. */
+
+ /* OK, now walk the new symtable, cacheing symbol properties */
+ {
+ register struct external_nlist *sym_pointer;
+ register struct external_nlist *sym_end = syms + bfd_get_symcount (abfd);
+ register aout_symbol_type *cache_ptr = cached;
+
+ /* Run through table and copy values */
+ for (sym_pointer = syms, cache_ptr = cached;
+ sym_pointer < sym_end; sym_pointer ++, cache_ptr++)
+ {
+ long x = GET_WORD(abfd, sym_pointer->e_strx);
+ cache_ptr->symbol.the_bfd = abfd;
+ if (x == 0)
+ cache_ptr->symbol.name = "";
+ else if (x >= 0 && x < string_size)
+ cache_ptr->symbol.name = x + strings;
+ else
+ goto bailout;
+
+ cache_ptr->symbol.value = GET_SWORD(abfd, sym_pointer->e_value);
+ cache_ptr->desc = bfd_h_get_16(abfd, sym_pointer->e_desc);
+ cache_ptr->other = bfd_h_get_8(abfd, sym_pointer->e_other);
+ cache_ptr->type = bfd_h_get_8(abfd, sym_pointer->e_type);
+ cache_ptr->symbol.udata = 0;
+ translate_from_native_sym_flags (sym_pointer, cache_ptr, abfd);
+ }
+ }
+
+ obj_aout_symbols (abfd) = cached;
+ free((PTR)syms);
+
+ return true;
+}
+
+
+/* Possible improvements:
+ + look for strings matching trailing substrings of other strings
+ + better data structures? balanced trees?
+ + smaller per-string or per-symbol data? re-use some of the symbol's
+ data fields?
+ + also look at reducing memory use elsewhere -- maybe if we didn't have to
+ construct the entire symbol table at once, we could get by with smaller
+ amounts of VM? (What effect does that have on the string table
+ reductions?)
+ + rip this out of here, put it into its own file in bfd or libiberty, so
+ coff and elf can use it too. I'll work on this soon, but have more
+ pressing tasks right now.
+
+ A hash table might(?) be more efficient for handling exactly the cases that
+ are handled now, but for trailing substring matches, I think we want to
+ examine the `nearest' values (reverse-)lexically, not merely impose a strict
+ order, nor look only for exact-match or not-match. I don't think a hash
+ table would be very useful for that, and I don't feel like fleshing out two
+ completely different implementations. [raeburn:930419.0331EDT] */
+
+struct stringtab_entry {
+ /* Hash value for this string. Only useful so long as we aren't doing
+ substring matches. */
+ unsigned int hash;
+
+ /* Next node to look at, depending on whether the hash value of the string
+ being searched for is less than or greater than the hash value of the
+ current node. For now, `equal to' is lumped in with `greater than', for
+ space efficiency. It's not a common enough case to warrant another field
+ to be used for all nodes. */
+ struct stringtab_entry *less;
+ struct stringtab_entry *greater;
+
+ /* The string itself. */
+ CONST char *string;
+
+ /* The index allocated for this string. */
+ bfd_size_type index;
+
+#ifdef GATHER_STATISTICS
+ /* How many references have there been to this string? (Not currently used;
+ could be dumped out for anaylsis, if anyone's interested.) */
+ unsigned long count;
+#endif
+
+ /* Next node in linked list, in suggested output order. */
+ struct stringtab_entry *next_to_output;
+};
+
+struct stringtab_data {
+ /* Tree of string table entries. */
+ struct stringtab_entry *strings;
+
+ /* Fudge factor used to center top node of tree. */
+ int hash_zero;
+
+ /* Next index value to issue. */
+ bfd_size_type index;
+
+ /* Index used for empty strings. Cached here because checking for them
+ is really easy, and we can avoid searching the tree. */
+ bfd_size_type empty_string_index;
+
+ /* These fields indicate the two ends of a singly-linked list that indicates
+ the order strings should be written out in. Use this order, and no
+ seeking will need to be done, so output efficiency should be maximized. */
+ struct stringtab_entry **end;
+ struct stringtab_entry *output_order;
+
+#ifdef GATHER_STATISTICS
+ /* Number of strings which duplicate strings already in the table. */
+ unsigned long duplicates;
+
+ /* Number of bytes saved by not having to write all the duplicate strings. */
+ unsigned long bytes_saved;
+
+ /* Number of zero-length strings. Currently, these all turn into
+ references to the null byte at the end of the first string. In some
+ cases (possibly not all? explore this...), it should be possible to
+ simply write out a zero index value. */
+ unsigned long empty_strings;
+
+ /* Number of times the hash values matched but the strings were different.
+ Note that this includes the number of times the other string(s) occurs, so
+ there may only be two strings hashing to the same value, even if this
+ number is very large. */
+ unsigned long bad_hash_matches;
+
+ /* Null strings aren't counted in this one.
+ This will probably only be nonzero if we've got an input file
+ which was produced by `ld -r' (i.e., it's already been processed
+ through this code). Under some operating systems, native tools
+ may make all empty strings have the same index; but the pointer
+ check won't catch those, because to get to that stage we'd already
+ have to compute the checksum, which requires reading the string,
+ so we short-circuit that case with empty_string_index above. */
+ unsigned long pointer_matches;
+
+ /* Number of comparisons done. I figure with the algorithms in use below,
+ the average number of comparisons done (per symbol) should be roughly
+ log-base-2 of the number of unique strings. */
+ unsigned long n_compares;
+#endif
+};
+
+/* Some utility functions for the string table code. */
+
+/* For speed, only hash on the first this many bytes of strings.
+ This number was chosen by profiling ld linking itself, with -g. */
+#define HASHMAXLEN 25
+
+#define HASH_CHAR(c) (sum ^= sum >> 20, sum ^= sum << 7, sum += (c))
+
+static INLINE unsigned int
+hash (string, len)
+ unsigned char *string;
+ register unsigned int len;
+{
+ register unsigned int sum = 0;
+
+ if (len > HASHMAXLEN)
+ {
+ HASH_CHAR (len);
+ len = HASHMAXLEN;
+ }
+
+ while (len--)
+ {
+ HASH_CHAR (*string++);
+ }
+ return sum;
+}
+
+static INLINE void
+stringtab_init (tab)
+ struct stringtab_data *tab;
+{
+ tab->strings = 0;
+ tab->output_order = 0;
+ tab->end = &tab->output_order;
+
+ /* Initial string table length includes size of length field. */
+ tab->index = BYTES_IN_WORD;
+ tab->empty_string_index = -1;
+#ifdef GATHER_STATISTICS
+ tab->duplicates = 0;
+ tab->empty_strings = 0;
+ tab->bad_hash_matches = 0;
+ tab->pointer_matches = 0;
+ tab->bytes_saved = 0;
+ tab->n_compares = 0;
+#endif
+}
+
+static INLINE int
+compare (entry, str, hash)
+ struct stringtab_entry *entry;
+ CONST char *str;
+ unsigned int hash;
+{
+ return hash - entry->hash;
+}
+
+#ifdef GATHER_STATISTICS
+/* Don't want to have to link in math library with all bfd applications... */
+static INLINE double
+log2 (num)
+ int num;
+{
+ double d = num;
+ int n = 0;
+ while (d >= 2.0)
+ n++, d /= 2.0;
+ return ((d > 1.41) ? 0.5 : 0) + n;
+}
+#endif
+
+/* Main string table routines. */
+/* Returns index in string table. Whether or not this actually adds an
+ entry into the string table should be irrelevant -- it just has to
+ return a valid index. */
+static bfd_size_type
+add_to_stringtab (abfd, str, tab, check)
+ bfd *abfd;
+ CONST char *str;
+ struct stringtab_data *tab;
+ int check;
+{
+ struct stringtab_entry **ep;
+ register struct stringtab_entry *entry;
+ unsigned int hashval, len;
+
+ if (str[0] == 0)
+ {
+ bfd_size_type index;
+ CONST bfd_size_type minus_one = -1;
+
+#ifdef GATHER_STATISTICS
+ tab->empty_strings++;
+#endif
+ index = tab->empty_string_index;
+ if (index != minus_one)
+ {
+ got_empty:
+#ifdef GATHER_STATISTICS
+ tab->bytes_saved++;
+ tab->duplicates++;
+#endif
+ return index;
+ }
+
+ /* Need to find it. */
+ entry = tab->strings;
+ if (entry)
+ {
+ index = entry->index + strlen (entry->string);
+ tab->empty_string_index = index;
+ goto got_empty;
+ }
+ len = 0;
+ }
+ else
+ len = strlen (str);
+
+ /* The hash_zero value is chosen such that the first symbol gets a value of
+ zero. With a balanced tree, this wouldn't be very useful, but without it,
+ we might get a more even split at the top level, instead of skewing it
+ badly should hash("/usr/lib/crt0.o") (or whatever) be far from zero. */
+ hashval = hash (str, len) ^ tab->hash_zero;
+ ep = &tab->strings;
+ if (!*ep)
+ {
+ tab->hash_zero = hashval;
+ hashval = 0;
+ goto add_it;
+ }
+
+ while (*ep)
+ {
+ register int cmp;
+
+ entry = *ep;
+#ifdef GATHER_STATISTICS
+ tab->n_compares++;
+#endif
+ cmp = compare (entry, str, hashval);
+ /* The not-equal cases are more frequent, so check them first. */
+ if (cmp > 0)
+ ep = &entry->greater;
+ else if (cmp < 0)
+ ep = &entry->less;
+ else
+ {
+ if (entry->string == str)
+ {
+#ifdef GATHER_STATISTICS
+ tab->pointer_matches++;
+#endif
+ goto match;
+ }
+ /* Compare the first bytes to save a function call if they
+ don't match. */
+ if (entry->string[0] == str[0] && !strcmp (entry->string, str))
+ {
+ match:
+#ifdef GATHER_STATISTICS
+ entry->count++;
+ tab->bytes_saved += len + 1;
+ tab->duplicates++;
+#endif
+ /* If we're in the linker, and the new string is from a new
+ input file which might have already had these reductions
+ run over it, we want to keep the new string pointer. I
+ don't think we're likely to see any (or nearly as many,
+ at least) cases where a later string is in the same location
+ as an earlier one rather than this one. */
+ entry->string = str;
+ return entry->index;
+ }
+#ifdef GATHER_STATISTICS
+ tab->bad_hash_matches++;
+#endif
+ ep = &entry->greater;
+ }
+ }
+
+ /* If we get here, nothing that's in the table already matched.
+ EP points to the `next' field at the end of the chain; stick a
+ new entry on here. */
+ add_it:
+ entry = (struct stringtab_entry *)
+ bfd_alloc_by_size_t (abfd, sizeof (struct stringtab_entry));
+
+ entry->less = entry->greater = 0;
+ entry->hash = hashval;
+ entry->index = tab->index;
+ entry->string = str;
+ entry->next_to_output = 0;
+#ifdef GATHER_STATISTICS
+ entry->count = 1;
+#endif
+
+ assert (*tab->end == 0);
+ *(tab->end) = entry;
+ tab->end = &entry->next_to_output;
+ assert (*tab->end == 0);
+
+ {
+ tab->index += len + 1;
+ if (len == 0)
+ tab->empty_string_index = entry->index;
+ }
+ assert (*ep == 0);
+ *ep = entry;
+ return entry->index;
+}
+
+static void
+emit_strtab (abfd, tab)
+ bfd *abfd;
+ struct stringtab_data *tab;
+{
+ struct stringtab_entry *entry;
+#ifdef GATHER_STATISTICS
+ int count = 0;
+#endif
+
+ /* Be sure to put string length into correct byte ordering before writing
+ it out. */
+ char buffer[BYTES_IN_WORD];
+
+ PUT_WORD (abfd, tab->index, (unsigned char *) buffer);
+ bfd_write ((PTR) buffer, 1, BYTES_IN_WORD, abfd);
+
+ for (entry = tab->output_order; entry; entry = entry->next_to_output)
+ {
+ bfd_write ((PTR) entry->string, 1, strlen (entry->string) + 1, abfd);
+#ifdef GATHER_STATISTICS
+ count++;
+#endif
+ }
+
+#ifdef GATHER_STATISTICS
+ /* Short form only, for now.
+ To do: Specify output file. Conditionalize on environment? Detailed
+ analysis if desired. */
+ {
+ int n_syms = bfd_get_symcount (abfd);
+
+ fprintf (stderr, "String table data for output file:\n");
+ fprintf (stderr, " %8d symbols output\n", n_syms);
+ fprintf (stderr, " %8d duplicate strings\n", tab->duplicates);
+ fprintf (stderr, " %8d empty strings\n", tab->empty_strings);
+ fprintf (stderr, " %8d unique strings output\n", count);
+ fprintf (stderr, " %8d pointer matches\n", tab->pointer_matches);
+ fprintf (stderr, " %8d bytes saved\n", tab->bytes_saved);
+ fprintf (stderr, " %8d bad hash matches\n", tab->bad_hash_matches);
+ fprintf (stderr, " %8d hash-val comparisons\n", tab->n_compares);
+ if (n_syms)
+ {
+ double n_compares = tab->n_compares;
+ double avg_compares = n_compares / n_syms;
+ /* The second value here should usually be near one. */
+ fprintf (stderr,
+ "\t average %f comparisons per symbol (%f * log2 nstrings)\n",
+ avg_compares, avg_compares / log2 (count));
+ }
+ }
+#endif
+
+/* Old code:
+ unsigned int count;
+ generic = bfd_get_outsymbols(abfd);
+ for (count = 0; count < bfd_get_symcount(abfd); count++)
+ {
+ asymbol *g = *(generic++);
+
+ if (g->name)
+ {
+ size_t length = strlen(g->name)+1;
+ bfd_write((PTR)g->name, 1, length, abfd);
+ }
+ g->KEEPIT = (KEEPITTYPE) count;
+ } */
+}
+
+void
+DEFUN(NAME(aout,write_syms),(abfd),
+ bfd *abfd)
+{
+ unsigned int count ;
+ asymbol **generic = bfd_get_outsymbols (abfd);
+ struct stringtab_data strtab;
+
+ stringtab_init (&strtab);
+
+ for (count = 0; count < bfd_get_symcount (abfd); count++)
+ {
+ asymbol *g = generic[count];
+ struct external_nlist nsp;
+
+ if (g->name)
+ PUT_WORD (abfd, add_to_stringtab (abfd, g->name, &strtab),
+ (unsigned char *) nsp.e_strx);
+ else
+ PUT_WORD (abfd, 0, (unsigned char *)nsp.e_strx);
+
+ if (bfd_asymbol_flavour(g) == abfd->xvec->flavour)
+ {
+ bfd_h_put_16(abfd, aout_symbol(g)->desc, nsp.e_desc);
+ bfd_h_put_8(abfd, aout_symbol(g)->other, nsp.e_other);
+ bfd_h_put_8(abfd, aout_symbol(g)->type, nsp.e_type);
+ }
+ else
+ {
+ bfd_h_put_16(abfd,0, nsp.e_desc);
+ bfd_h_put_8(abfd, 0, nsp.e_other);
+ bfd_h_put_8(abfd, 0, nsp.e_type);
+ }
+
+ translate_to_native_sym_flags (&nsp, g, abfd);
+
+ bfd_write((PTR)&nsp,1,EXTERNAL_NLIST_SIZE, abfd);
+
+ /* NB: `KEEPIT' currently overlays `flags', so set this only
+ here, at the end. */
+ g->KEEPIT = count;
+ }
+
+ emit_strtab (abfd, &strtab);
+}
+
+
+unsigned int
+DEFUN(NAME(aout,get_symtab),(abfd, location),
+ bfd *abfd AND
+ asymbol **location)
+{
+ unsigned int counter = 0;
+ aout_symbol_type *symbase;
+
+ if (!NAME(aout,slurp_symbol_table)(abfd)) return 0;
+
+ for (symbase = obj_aout_symbols(abfd); counter++ < bfd_get_symcount (abfd);)
+ *(location++) = (asymbol *)( symbase++);
+ *location++ =0;
+ return bfd_get_symcount (abfd);
+}
+
+
+/* Standard reloc stuff */
+/* Output standard relocation information to a file in target byte order. */
+
+void
+DEFUN(NAME(aout,swap_std_reloc_out),(abfd, g, natptr),
+ bfd *abfd AND
+ arelent *g AND
+ struct reloc_std_external *natptr)
+{
+ int r_index;
+ asymbol *sym = *(g->sym_ptr_ptr);
+ int r_extern;
+ unsigned int r_length;
+ int r_pcrel;
+ int r_baserel, r_jmptable, r_relative;
+ unsigned int r_addend;
+ asection *output_section = sym->section->output_section;
+
+ PUT_WORD(abfd, g->address, natptr->r_address);
+
+ r_length = g->howto->size ; /* Size as a power of two */
+ r_pcrel = (int) g->howto->pc_relative; /* Relative to PC? */
+ /* XXX This relies on relocs coming from a.out files. */
+ r_baserel = (g->howto->type & 8) != 0;
+ /* r_jmptable, r_relative??? FIXME-soon */
+ r_jmptable = 0;
+ r_relative = 0;
+
+ r_addend = g->addend + (*(g->sym_ptr_ptr))->section->output_section->vma;
+
+ /* name was clobbered by aout_write_syms to be symbol index */
+
+ /* If this relocation is relative to a symbol then set the
+ r_index to the symbols index, and the r_extern bit.
+
+ Absolute symbols can come in in two ways, either as an offset
+ from the abs section, or as a symbol which has an abs value.
+ check for that here
+ */
+
+
+ if (bfd_is_com_section (output_section)
+ || output_section == &bfd_abs_section
+ || output_section == &bfd_und_section)
+ {
+ if (bfd_abs_section.symbol == sym)
+ {
+ /* Whoops, looked like an abs symbol, but is really an offset
+ from the abs section */
+ r_index = 0;
+ r_extern = 0;
+ }
+ else
+ {
+ /* Fill in symbol */
+ r_extern = 1;
+ r_index = stoi((*(g->sym_ptr_ptr))->KEEPIT);
+
+ }
+ }
+ else
+ {
+ /* Just an ordinary section */
+ r_extern = 0;
+ r_index = output_section->target_index;
+ }
+
+ /* now the fun stuff */
+ if (abfd->xvec->header_byteorder_big_p != false) {
+ natptr->r_index[0] = r_index >> 16;
+ natptr->r_index[1] = r_index >> 8;
+ natptr->r_index[2] = r_index;
+ natptr->r_type[0] =
+ (r_extern? RELOC_STD_BITS_EXTERN_BIG: 0)
+ | (r_pcrel? RELOC_STD_BITS_PCREL_BIG: 0)
+ | (r_baserel? RELOC_STD_BITS_BASEREL_BIG: 0)
+ | (r_jmptable? RELOC_STD_BITS_JMPTABLE_BIG: 0)
+ | (r_relative? RELOC_STD_BITS_RELATIVE_BIG: 0)
+ | (r_length << RELOC_STD_BITS_LENGTH_SH_BIG);
+ } else {
+ natptr->r_index[2] = r_index >> 16;
+ natptr->r_index[1] = r_index >> 8;
+ natptr->r_index[0] = r_index;
+ natptr->r_type[0] =
+ (r_extern? RELOC_STD_BITS_EXTERN_LITTLE: 0)
+ | (r_pcrel? RELOC_STD_BITS_PCREL_LITTLE: 0)
+ | (r_baserel? RELOC_STD_BITS_BASEREL_LITTLE: 0)
+ | (r_jmptable? RELOC_STD_BITS_JMPTABLE_LITTLE: 0)
+ | (r_relative? RELOC_STD_BITS_RELATIVE_LITTLE: 0)
+ | (r_length << RELOC_STD_BITS_LENGTH_SH_LITTLE);
+ }
+}
+
+
+/* Extended stuff */
+/* Output extended relocation information to a file in target byte order. */
+
+void
+DEFUN(NAME(aout,swap_ext_reloc_out),(abfd, g, natptr),
+ bfd *abfd AND
+ arelent *g AND
+ register struct reloc_ext_external *natptr)
+{
+ int r_index;
+ int r_extern;
+ unsigned int r_type;
+ unsigned int r_addend;
+ asymbol *sym = *(g->sym_ptr_ptr);
+ asection *output_section = sym->section->output_section;
+
+ PUT_WORD (abfd, g->address, natptr->r_address);
+
+ r_type = (unsigned int) g->howto->type;
+
+ r_addend = g->addend + (*(g->sym_ptr_ptr))->section->output_section->vma;
+
+ /* If this relocation is relative to a symbol then set the
+ r_index to the symbols index, and the r_extern bit.
+
+ Absolute symbols can come in in two ways, either as an offset
+ from the abs section, or as a symbol which has an abs value.
+ check for that here. */
+
+ if (bfd_is_com_section (output_section)
+ || output_section == &bfd_abs_section
+ || output_section == &bfd_und_section)
+ {
+ if (bfd_abs_section.symbol == sym)
+ {
+ /* Whoops, looked like an abs symbol, but is really an offset
+ from the abs section */
+ r_index = 0;
+ r_extern = 0;
+ }
+ else
+ {
+ r_extern = 1;
+ r_index = stoi((*(g->sym_ptr_ptr))->KEEPIT);
+ }
+ }
+ else
+ {
+ /* Just an ordinary section */
+ r_extern = 0;
+ r_index = output_section->target_index;
+ }
+
+ /* now the fun stuff */
+ if (abfd->xvec->header_byteorder_big_p != false) {
+ natptr->r_index[0] = r_index >> 16;
+ natptr->r_index[1] = r_index >> 8;
+ natptr->r_index[2] = r_index;
+ natptr->r_type[0] =
+ ((r_extern? RELOC_EXT_BITS_EXTERN_BIG: 0)
+ | (r_type << RELOC_EXT_BITS_TYPE_SH_BIG));
+ } else {
+ natptr->r_index[2] = r_index >> 16;
+ natptr->r_index[1] = r_index >> 8;
+ natptr->r_index[0] = r_index;
+ natptr->r_type[0] =
+ (r_extern? RELOC_EXT_BITS_EXTERN_LITTLE: 0)
+ | (r_type << RELOC_EXT_BITS_TYPE_SH_LITTLE);
+ }
+
+ PUT_WORD (abfd, r_addend, natptr->r_addend);
+}
+
+/* BFD deals internally with all things based from the section they're
+ in. so, something in 10 bytes into a text section with a base of
+ 50 would have a symbol (.text+10) and know .text vma was 50.
+
+ Aout keeps all it's symbols based from zero, so the symbol would
+ contain 60. This macro subs the base of each section from the value
+ to give the true offset from the section */
+
+
+#define MOVE_ADDRESS(ad) \
+ if (r_extern) { \
+ /* undefined symbol */ \
+ cache_ptr->sym_ptr_ptr = symbols + r_index; \
+ cache_ptr->addend = ad; \
+ } else { \
+ /* defined, section relative. replace symbol with pointer to \
+ symbol which points to section */ \
+ switch (r_index) { \
+ case N_TEXT: \
+ case N_TEXT | N_EXT: \
+ cache_ptr->sym_ptr_ptr = obj_textsec(abfd)->symbol_ptr_ptr; \
+ cache_ptr->addend = ad - su->textsec->vma; \
+ break; \
+ case N_DATA: \
+ case N_DATA | N_EXT: \
+ cache_ptr->sym_ptr_ptr = obj_datasec(abfd)->symbol_ptr_ptr; \
+ cache_ptr->addend = ad - su->datasec->vma; \
+ break; \
+ case N_BSS: \
+ case N_BSS | N_EXT: \
+ cache_ptr->sym_ptr_ptr = obj_bsssec(abfd)->symbol_ptr_ptr; \
+ cache_ptr->addend = ad - su->bsssec->vma; \
+ break; \
+ default: \
+ case N_ABS: \
+ case N_ABS | N_EXT: \
+ cache_ptr->sym_ptr_ptr = bfd_abs_section.symbol_ptr_ptr; \
+ cache_ptr->addend = ad; \
+ break; \
+ } \
+ } \
+
+void
+DEFUN(NAME(aout,swap_ext_reloc_in), (abfd, bytes, cache_ptr, symbols),
+ bfd *abfd AND
+ struct reloc_ext_external *bytes AND
+ arelent *cache_ptr AND
+ asymbol **symbols)
+{
+ int r_index;
+ int r_extern;
+ unsigned int r_type;
+ struct aoutdata *su = &(abfd->tdata.aout_data->a);
+
+ cache_ptr->address = (GET_SWORD (abfd, bytes->r_address));
+
+ /* now the fun stuff */
+ if (abfd->xvec->header_byteorder_big_p != false) {
+ r_index = (bytes->r_index[0] << 16)
+ | (bytes->r_index[1] << 8)
+ | bytes->r_index[2];
+ r_extern = (0 != (bytes->r_type[0] & RELOC_EXT_BITS_EXTERN_BIG));
+ r_type = (bytes->r_type[0] & RELOC_EXT_BITS_TYPE_BIG)
+ >> RELOC_EXT_BITS_TYPE_SH_BIG;
+ } else {
+ r_index = (bytes->r_index[2] << 16)
+ | (bytes->r_index[1] << 8)
+ | bytes->r_index[0];
+ r_extern = (0 != (bytes->r_type[0] & RELOC_EXT_BITS_EXTERN_LITTLE));
+ r_type = (bytes->r_type[0] & RELOC_EXT_BITS_TYPE_LITTLE)
+ >> RELOC_EXT_BITS_TYPE_SH_LITTLE;
+ }
+
+ cache_ptr->howto = howto_table_ext + r_type;
+ MOVE_ADDRESS(GET_SWORD(abfd, bytes->r_addend));
+}
+
+void
+DEFUN(NAME(aout,swap_std_reloc_in), (abfd, bytes, cache_ptr, symbols),
+ bfd *abfd AND
+ struct reloc_std_external *bytes AND
+ arelent *cache_ptr AND
+ asymbol **symbols)
+{
+ int r_index;
+ int r_extern;
+ unsigned int r_length;
+ int r_pcrel;
+ int r_baserel, r_jmptable, r_relative;
+ struct aoutdata *su = &(abfd->tdata.aout_data->a);
+ int howto_idx;
+
+ cache_ptr->address = bfd_h_get_32 (abfd, bytes->r_address);
+
+ /* now the fun stuff */
+ if (abfd->xvec->header_byteorder_big_p != false) {
+ r_index = (bytes->r_index[0] << 16)
+ | (bytes->r_index[1] << 8)
+ | bytes->r_index[2];
+ r_extern = (0 != (bytes->r_type[0] & RELOC_STD_BITS_EXTERN_BIG));
+ r_pcrel = (0 != (bytes->r_type[0] & RELOC_STD_BITS_PCREL_BIG));
+ r_baserel = (0 != (bytes->r_type[0] & RELOC_STD_BITS_BASEREL_BIG));
+ r_jmptable= (0 != (bytes->r_type[0] & RELOC_STD_BITS_JMPTABLE_BIG));
+ r_relative= (0 != (bytes->r_type[0] & RELOC_STD_BITS_RELATIVE_BIG));
+ r_length = (bytes->r_type[0] & RELOC_STD_BITS_LENGTH_BIG)
+ >> RELOC_STD_BITS_LENGTH_SH_BIG;
+ } else {
+ r_index = (bytes->r_index[2] << 16)
+ | (bytes->r_index[1] << 8)
+ | bytes->r_index[0];
+ r_extern = (0 != (bytes->r_type[0] & RELOC_STD_BITS_EXTERN_LITTLE));
+ r_pcrel = (0 != (bytes->r_type[0] & RELOC_STD_BITS_PCREL_LITTLE));
+ r_baserel = (0 != (bytes->r_type[0] & RELOC_STD_BITS_BASEREL_LITTLE));
+ r_jmptable= (0 != (bytes->r_type[0] & RELOC_STD_BITS_JMPTABLE_LITTLE));
+ r_relative= (0 != (bytes->r_type[0] & RELOC_STD_BITS_RELATIVE_LITTLE));
+ r_length = (bytes->r_type[0] & RELOC_STD_BITS_LENGTH_LITTLE)
+ >> RELOC_STD_BITS_LENGTH_SH_LITTLE;
+ }
+
+ howto_idx = r_length + 4 * r_pcrel + 8 * r_baserel;
+ BFD_ASSERT (howto_idx < TABLE_SIZE (howto_table_std));
+ cache_ptr->howto = howto_table_std + howto_idx;
+ BFD_ASSERT (cache_ptr->howto->type != -1);
+ BFD_ASSERT (r_jmptable == 0);
+ BFD_ASSERT (r_relative == 0);
+ /* FIXME-soon: Roll jmptable, relative bits into howto setting */
+
+ MOVE_ADDRESS(0);
+}
+
+/* Reloc hackery */
+
+boolean
+DEFUN(NAME(aout,slurp_reloc_table),(abfd, asect, symbols),
+ bfd *abfd AND
+ sec_ptr asect AND
+ asymbol **symbols)
+{
+ unsigned int count;
+ bfd_size_type reloc_size;
+ PTR relocs;
+ arelent *reloc_cache;
+ size_t each_size;
+
+ if (asect->relocation) return true;
+
+ if (asect->flags & SEC_CONSTRUCTOR) return true;
+
+ if (asect == obj_datasec (abfd)) {
+ reloc_size = exec_hdr(abfd)->a_drsize;
+ goto doit;
+ }
+
+ if (asect == obj_textsec (abfd)) {
+ reloc_size = exec_hdr(abfd)->a_trsize;
+ goto doit;
+ }
+
+ bfd_error = invalid_operation;
+ return false;
+
+ doit:
+ bfd_seek (abfd, asect->rel_filepos, SEEK_SET);
+ each_size = obj_reloc_entry_size (abfd);
+
+ count = reloc_size / each_size;
+
+
+ reloc_cache = (arelent *) bfd_zalloc (abfd, (size_t)(count * sizeof
+ (arelent)));
+ if (!reloc_cache) {
+nomem:
+ bfd_error = no_memory;
+ return false;
+ }
+
+ relocs = (PTR) bfd_alloc (abfd, reloc_size);
+ if (!relocs) {
+ bfd_release (abfd, reloc_cache);
+ goto nomem;
+ }
+
+ if (bfd_read (relocs, 1, reloc_size, abfd) != reloc_size) {
+ bfd_release (abfd, relocs);
+ bfd_release (abfd, reloc_cache);
+ bfd_error = system_call_error;
+ return false;
+ }
+
+ if (each_size == RELOC_EXT_SIZE) {
+ register struct reloc_ext_external *rptr = (struct reloc_ext_external *) relocs;
+ unsigned int counter = 0;
+ arelent *cache_ptr = reloc_cache;
+
+ for (; counter < count; counter++, rptr++, cache_ptr++) {
+ NAME(aout,swap_ext_reloc_in)(abfd, rptr, cache_ptr, symbols);
+ }
+ } else {
+ register struct reloc_std_external *rptr = (struct reloc_std_external*) relocs;
+ unsigned int counter = 0;
+ arelent *cache_ptr = reloc_cache;
+
+ for (; counter < count; counter++, rptr++, cache_ptr++) {
+ NAME(aout,swap_std_reloc_in)(abfd, rptr, cache_ptr, symbols);
+ }
+
+ }
+
+ bfd_release (abfd,relocs);
+ asect->relocation = reloc_cache;
+ asect->reloc_count = count;
+ return true;
+}
+
+
+
+/* Write out a relocation section into an object file. */
+
+boolean
+DEFUN(NAME(aout,squirt_out_relocs),(abfd, section),
+ bfd *abfd AND
+ asection *section)
+{
+ arelent **generic;
+ unsigned char *native, *natptr;
+ size_t each_size;
+
+ unsigned int count = section->reloc_count;
+ size_t natsize;
+
+ if (count == 0) return true;
+
+ each_size = obj_reloc_entry_size (abfd);
+ natsize = each_size * count;
+ native = (unsigned char *) bfd_zalloc (abfd, natsize);
+ if (!native) {
+ bfd_error = no_memory;
+ return false;
+ }
+
+ generic = section->orelocation;
+
+ if (each_size == RELOC_EXT_SIZE)
+ {
+ for (natptr = native;
+ count != 0;
+ --count, natptr += each_size, ++generic)
+ NAME(aout,swap_ext_reloc_out) (abfd, *generic, (struct reloc_ext_external *)natptr);
+ }
+ else
+ {
+ for (natptr = native;
+ count != 0;
+ --count, natptr += each_size, ++generic)
+ NAME(aout,swap_std_reloc_out)(abfd, *generic, (struct reloc_std_external *)natptr);
+ }
+
+ if ( bfd_write ((PTR) native, 1, natsize, abfd) != natsize) {
+ bfd_release(abfd, native);
+ return false;
+ }
+ bfd_release (abfd, native);
+
+ return true;
+}
+
+/* This is stupid. This function should be a boolean predicate */
+unsigned int
+DEFUN(NAME(aout,canonicalize_reloc),(abfd, section, relptr, symbols),
+ bfd *abfd AND
+ sec_ptr section AND
+ arelent **relptr AND
+ asymbol **symbols)
+{
+ arelent *tblptr = section->relocation;
+ unsigned int count;
+
+ if (!(tblptr || NAME(aout,slurp_reloc_table)(abfd, section, symbols)))
+ return 0;
+
+ if (section->flags & SEC_CONSTRUCTOR) {
+ arelent_chain *chain = section->constructor_chain;
+ for (count = 0; count < section->reloc_count; count ++) {
+ *relptr ++ = &chain->relent;
+ chain = chain->next;
+ }
+ }
+ else {
+ tblptr = section->relocation;
+ if (!tblptr) return 0;
+
+ for (count = 0; count++ < section->reloc_count;)
+ {
+ *relptr++ = tblptr++;
+ }
+ }
+ *relptr = 0;
+
+ return section->reloc_count;
+}
+
+unsigned int
+DEFUN(NAME(aout,get_reloc_upper_bound),(abfd, asect),
+ bfd *abfd AND
+ sec_ptr asect)
+{
+ if (bfd_get_format (abfd) != bfd_object) {
+ bfd_error = invalid_operation;
+ return 0;
+ }
+ if (asect->flags & SEC_CONSTRUCTOR) {
+ return (sizeof (arelent *) * (asect->reloc_count+1));
+ }
+
+
+ if (asect == obj_datasec (abfd))
+ return (sizeof (arelent *) *
+ ((exec_hdr(abfd)->a_drsize / obj_reloc_entry_size (abfd))
+ +1));
+
+ if (asect == obj_textsec (abfd))
+ return (sizeof (arelent *) *
+ ((exec_hdr(abfd)->a_trsize / obj_reloc_entry_size (abfd))
+ +1));
+
+ bfd_error = invalid_operation;
+ return 0;
+}
+
+
+ unsigned int
+DEFUN(NAME(aout,get_symtab_upper_bound),(abfd),
+ bfd *abfd)
+{
+ if (!NAME(aout,slurp_symbol_table)(abfd)) return 0;
+
+ return (bfd_get_symcount (abfd)+1) * (sizeof (aout_symbol_type *));
+}
+ alent *
+DEFUN(NAME(aout,get_lineno),(ignore_abfd, ignore_symbol),
+ bfd *ignore_abfd AND
+ asymbol *ignore_symbol)
+{
+return (alent *)NULL;
+}
+
+void
+DEFUN(NAME(aout,get_symbol_info),(ignore_abfd, symbol, ret),
+ bfd *ignore_abfd AND
+ asymbol *symbol AND
+ symbol_info *ret)
+{
+ bfd_symbol_info (symbol, ret);
+
+ if (ret->type == '?')
+ {
+ int type_code = aout_symbol(symbol)->type & 0xff;
+ CONST char *stab_name = aout_stab_name(type_code);
+ static char buf[10];
+
+ if (stab_name == NULL)
+ {
+ sprintf(buf, "(%d)", type_code);
+ stab_name = buf;
+ }
+ ret->type = '-';
+ ret->stab_other = (unsigned)(aout_symbol(symbol)->other & 0xff);
+ ret->stab_desc = (unsigned)(aout_symbol(symbol)->desc & 0xffff);
+ ret->stab_name = stab_name;
+ }
+}
+
+void
+DEFUN(NAME(aout,print_symbol),(ignore_abfd, afile, symbol, how),
+ bfd *ignore_abfd AND
+ PTR afile AND
+ asymbol *symbol AND
+ bfd_print_symbol_type how)
+{
+ FILE *file = (FILE *)afile;
+
+ switch (how) {
+ case bfd_print_symbol_name:
+ if (symbol->name)
+ fprintf(file,"%s", symbol->name);
+ break;
+ case bfd_print_symbol_more:
+ fprintf(file,"%4x %2x %2x",(unsigned)(aout_symbol(symbol)->desc & 0xffff),
+ (unsigned)(aout_symbol(symbol)->other & 0xff),
+ (unsigned)(aout_symbol(symbol)->type));
+ break;
+ case bfd_print_symbol_all:
+ {
+ CONST char *section_name = symbol->section->name;
+
+
+ bfd_print_symbol_vandf((PTR)file,symbol);
+
+ fprintf(file," %-5s %04x %02x %02x",
+ section_name,
+ (unsigned)(aout_symbol(symbol)->desc & 0xffff),
+ (unsigned)(aout_symbol(symbol)->other & 0xff),
+ (unsigned)(aout_symbol(symbol)->type & 0xff));
+ if (symbol->name)
+ fprintf(file," %s", symbol->name);
+ }
+ break;
+ }
+}
+
+/*
+ provided a BFD, a section and an offset into the section, calculate
+ and return the name of the source file and the line nearest to the
+ wanted location.
+*/
+
+boolean
+DEFUN(NAME(aout,find_nearest_line),(abfd,
+ section,
+ symbols,
+ offset,
+ filename_ptr,
+ functionname_ptr,
+ line_ptr),
+ bfd *abfd AND
+ asection *section AND
+ asymbol **symbols AND
+ bfd_vma offset AND
+ CONST char **filename_ptr AND
+ CONST char **functionname_ptr AND
+ unsigned int *line_ptr)
+{
+ /* Run down the file looking for the filename, function and linenumber */
+ asymbol **p;
+ static char buffer[100];
+ static char filename_buffer[200];
+ CONST char *directory_name = NULL;
+ CONST char *main_file_name = NULL;
+ CONST char *current_file_name = NULL;
+ CONST char *line_file_name = NULL; /* Value of current_file_name at line number. */
+ bfd_vma high_line_vma = ~0;
+ bfd_vma low_func_vma = 0;
+ asymbol *func = 0;
+ *filename_ptr = abfd->filename;
+ *functionname_ptr = 0;
+ *line_ptr = 0;
+ if (symbols != (asymbol **)NULL) {
+ for (p = symbols; *p; p++) {
+ aout_symbol_type *q = (aout_symbol_type *)(*p);
+ next:
+ switch (q->type){
+ case N_SO:
+ main_file_name = current_file_name = q->symbol.name;
+ /* Look ahead to next symbol to check if that too is an N_SO. */
+ p++;
+ if (*p == NULL)
+ break;
+ q = (aout_symbol_type *)(*p);
+ if (q->type != (int)N_SO)
+ goto next;
+
+ /* Found a second N_SO First is directory; second is filename. */
+ directory_name = current_file_name;
+ main_file_name = current_file_name = q->symbol.name;
+ if (obj_textsec(abfd) != section)
+ goto done;
+ break;
+ case N_SOL:
+ current_file_name = q->symbol.name;
+ break;
+
+ case N_SLINE:
+
+ case N_DSLINE:
+ case N_BSLINE:
+ /* We'll keep this if it resolves nearer than the one we have already */
+ if (q->symbol.value >= offset &&
+ q->symbol.value < high_line_vma) {
+ *line_ptr = q->desc;
+ high_line_vma = q->symbol.value;
+ line_file_name = current_file_name;
+ }
+ break;
+ case N_FUN:
+ {
+ /* We'll keep this if it is nearer than the one we have already */
+ if (q->symbol.value >= low_func_vma &&
+ q->symbol.value <= offset) {
+ low_func_vma = q->symbol.value;
+ func = (asymbol *)q;
+ }
+ if (*line_ptr && func) {
+ CONST char *function = func->name;
+ char *p;
+ strncpy(buffer, function, sizeof(buffer)-1);
+ buffer[sizeof(buffer)-1] = 0;
+ /* Have to remove : stuff */
+ p = strchr(buffer,':');
+ if (p != NULL) { *p = '\0'; }
+ *functionname_ptr = buffer;
+ goto done;
+
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ done:
+ if (*line_ptr)
+ main_file_name = line_file_name;
+ if (main_file_name) {
+ if (main_file_name[0] == '/' || directory_name == NULL)
+ *filename_ptr = main_file_name;
+ else {
+ sprintf(filename_buffer, "%.140s%.50s",
+ directory_name, main_file_name);
+ *filename_ptr = filename_buffer;
+ }
+ }
+ return true;
+
+}
+
+int
+DEFUN(NAME(aout,sizeof_headers),(abfd, execable),
+ bfd *abfd AND
+ boolean execable)
+{
+ return adata(abfd).exec_bytes_size;
+}
diff --git a/gnu/usr.bin/gdb/bfd/archive.c b/gnu/usr.bin/gdb/bfd/archive.c
new file mode 100644
index 0000000..5edae9d
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/archive.c
@@ -0,0 +1,1770 @@
+/* BFD back-end for archive files (libraries).
+ Copyright 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+ Written by Cygnus Support. Mostly Gumby Henkel-Wallace's fault.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+@setfilename archive-info
+SECTION
+ Archives
+
+DESCRIPTION
+ Archives are supported in BFD in <<archive.c>>.
+
+ An archive (or library) is just another BFD. It has a symbol
+ table, although there's not much a user program will do with it.
+
+ The big difference between an archive BFD and an ordinary BFD
+ is that the archive doesn't have sections. Instead it has a
+ chain of BFDs considered its contents. These BFDs can be
+ manipulated just like any other. The BFDs contained in an
+ archive opened for reading will all be opened for reading; you
+ may put either input or output BFDs into an archive opened for
+ output; it will be handled correctly when the archive is closed.
+
+ Use <<bfd_openr_next_archived_file>> to step through all
+ the contents of an archive opened for input. It's not
+ required that you read the entire archive if you don't want
+ to! Read it until you find what you want.
+
+ Archive contents of output BFDs are chained through the
+ <<next>> pointer in a BFD. The first one is findable through
+ the <<archive_head>> slot of the archive. Set it with
+ <<set_archive_head>> (q.v.). A given BFD may be in only one
+ open output archive at a time.
+
+ As expected, the BFD archive code is more general than the
+ archive code of any given environment. BFD archives may
+ contain files of different formats (e.g., a.out and coff) and
+ even different architectures. You may even place archives
+ recursively into archives!
+
+ This can cause unexpected confusion, since some archive
+ formats are more expressive than others. For instance, Intel
+ COFF archives can preserve long filenames; Sun a.out archives
+ cannot. If you move a file from the first to the second
+ format and back again, the filename may be truncated.
+ Likewise, different a.out environments have different
+ conventions as to how they truncate filenames, whether they
+ preserve directory names in filenames, etc. When
+ interoperating with native tools, be sure your files are
+ homogeneous.
+
+ Beware: most of these formats do not react well to the
+ presence of spaces in filenames. We do the best we can, but
+ can't always handle this due to restrctions in the format of
+ archives. Many unix utilities are braindead in regards to
+ spaces and such in filenames anyway, so this shouldn't be much
+ of a restriction.
+*/
+
+/* Assumes:
+ o - all archive elements start on an even boundary, newline padded;
+ o - all arch headers are char *;
+ o - all arch headers are the same size (across architectures).
+*/
+
+/* Some formats provide a way to cram a long filename into the short
+ (16 chars) space provided by a bsd archive. The trick is: make a
+ special "file" in the front of the archive, sort of like the SYMDEF
+ entry. If the filename is too long to fit, put it in the extended
+ name table, and use its index as the filename. To prevent
+ confusion prepend the index with a space. This means you can't
+ have filenames that start with a space, but then again, many unix
+ utilities can't handle that anyway.
+
+ This scheme unfortunately requires that you stand on your head in
+ order to write an archive since you need to put a magic file at the
+ front, and need to touch every entry to do so. C'est la vie.
+
+ We support two variants of this idea:
+ The SVR4 format (extended name table is named "//"),
+ and an extended pseudo-BSD variant (extended name table is named
+ "ARFILENAMES/"). The origin of the latter format is uncertain.
+
+ BSD 4.4 uses a third scheme: It writes a long filename
+ directly after the header. This allows 'ar q' to work.
+ We current can read BSD 4.4 archives, but not write them.
+*/
+
+/* Summary of archive member names:
+
+ Symbol table (must be first):
+ "__.SYMDEF " - Symbol table, Berkeley style, produced by ranlib.
+ "/ " - Symbol table, system 5 style.
+
+ Long name table (must be before regular file members):
+ "// " - Long name table, System 5 R4 style.
+ "ARFILENAMES/ " - Long name table, non-standard extended BSD (not BSD 4.4).
+
+ Regular file members with short names:
+ "filename.o/ " - Regular file, System 5 style (embedded spaces ok).
+ "filename.o " - Regular file, Berkeley style (no embedded spaces).
+
+ Regular files with long names (or embedded spaces, for BSD variants):
+ "/18 " - SVR4 style, name at offset 18 in name table.
+ "#1/23 " - Long name (or embedded paces) 23 characters long,
+ BSD 4.4 style, full name follows header.
+ Implemented for reading, not writing.
+ " 18 " - Long name 18 characters long, extended pseudo-BSD.
+ */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "aout/ar.h"
+#include "aout/ranlib.h"
+#include <errno.h>
+#include <string.h> /* For memchr, strrchr and friends */
+#include <ctype.h>
+
+#ifndef errno
+extern int errno;
+#endif
+
+#ifdef GNU960
+#define BFD_GNU960_ARMAG(abfd) (BFD_COFF_FILE_P((abfd)) ? ARMAG : ARMAGB)
+#endif
+
+/* Can't define this in hosts/*.h, because (e.g. in gprof) the hosts file
+ is included, then obstack.h, which thinks if offsetof is defined, it
+ doesn't need to include stddef.h. */
+/* Define offsetof for those systems which lack it */
+
+#if !defined (offsetof)
+#define offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE *)0)->MEMBER)
+#endif
+
+/* We keep a cache of archive filepointers to archive elements to
+ speed up searching the archive by filepos. We only add an entry to
+ the cache when we actually read one. We also don't sort the cache;
+ it's generally short enough to search linearly.
+ Note that the pointers here point to the front of the ar_hdr, not
+ to the front of the contents!
+*/
+struct ar_cache {
+ file_ptr ptr;
+ bfd* arelt;
+ struct ar_cache *next;
+};
+
+#define ar_padchar(abfd) ((abfd)->xvec->ar_pad_char)
+#define ar_maxnamelen(abfd) ((abfd)->xvec->ar_max_namelen)
+
+#define arch_eltdata(bfd) ((struct areltdata *)((bfd)->arelt_data))
+#define arch_hdr(bfd) ((struct ar_hdr *)arch_eltdata(bfd)->arch_header)
+
+/* Forward declarations of functions */
+
+boolean
+compute_and_write_armap PARAMS ((bfd *arch, unsigned int elength));
+
+static boolean
+bsd_update_armap_timestamp PARAMS ((bfd *arch));
+
+
+
+boolean
+_bfd_generic_mkarchive (abfd)
+ bfd *abfd;
+{
+ abfd->tdata.aout_ar_data = (struct artdata *)bfd_zalloc(abfd,
+ sizeof (struct artdata));
+
+ if (bfd_ardata (abfd) == NULL) {
+ bfd_error = no_memory;
+ return false;
+ }
+ bfd_ardata(abfd)->cache = 0;
+ return true;
+}
+
+/*
+FUNCTION
+ bfd_get_next_mapent
+
+SYNOPSIS
+ symindex bfd_get_next_mapent(bfd *, symindex previous, carsym ** sym);
+
+DESCRIPTION
+ This function steps through an archive's symbol table (if it
+ has one). Successively updates <<sym>> with the next symbol's
+ information, returning that symbol's (internal) index into the
+ symbol table.
+
+ Supply BFD_NO_MORE_SYMBOLS as the <<previous>> entry to get
+ the first one; returns BFD_NO_MORE_SYMBOLS when you're already
+ got the last one.
+
+ A <<carsym>> is a canonical archive symbol. The only
+ user-visible element is its name, a null-terminated string.
+*/
+
+symindex
+DEFUN(bfd_get_next_mapent,(abfd, prev, entry),
+ bfd *abfd AND
+ symindex prev AND
+ carsym **entry)
+{
+ if (!bfd_has_map (abfd)) {
+ bfd_error = invalid_operation;
+ return BFD_NO_MORE_SYMBOLS;
+ }
+
+ if (prev == BFD_NO_MORE_SYMBOLS) prev = 0;
+ else if (++prev >= bfd_ardata (abfd)->symdef_count)
+ return BFD_NO_MORE_SYMBOLS;
+
+ *entry = (bfd_ardata (abfd)->symdefs + prev);
+ return prev;
+}
+
+/* To be called by backends only */
+
+bfd *
+_bfd_create_empty_archive_element_shell (obfd)
+ bfd *obfd;
+{
+ bfd *nbfd;
+
+ nbfd = new_bfd_contained_in(obfd);
+ if (nbfd == NULL)
+ {
+ bfd_error = no_memory;
+ return NULL;
+ }
+ return nbfd;
+}
+
+/*
+FUNCTION
+ bfd_set_archive_head
+
+SYNOPSIS
+ boolean bfd_set_archive_head(bfd *output, bfd *new_head);
+
+DESCRIPTION
+ Used whilst processing archives. Sets the head of the chain of
+ BFDs contained in an archive to @var{new_head}.
+*/
+
+boolean
+DEFUN(bfd_set_archive_head,(output_archive, new_head),
+ bfd *output_archive AND
+ bfd *new_head)
+{
+
+ output_archive->archive_head = new_head;
+ return true;
+}
+
+bfd *
+look_for_bfd_in_cache (arch_bfd, filepos)
+ bfd *arch_bfd;
+ file_ptr filepos;
+{
+ struct ar_cache *current;
+
+ for (current = bfd_ardata (arch_bfd)->cache; current != NULL;
+ current = current->next)
+ if (current->ptr == filepos) return current->arelt;
+
+ return NULL;
+}
+
+/* Kind of stupid to call cons for each one, but we don't do too many */
+boolean
+add_bfd_to_cache (arch_bfd, filepos, new_elt)
+ bfd *arch_bfd, *new_elt;
+ file_ptr filepos;
+{
+ struct ar_cache *new_cache = (struct ar_cache *)
+ bfd_zalloc(arch_bfd, sizeof (struct ar_cache));
+
+ if (new_cache == NULL) {
+ bfd_error = no_memory;
+ return false;
+ }
+
+ new_cache->ptr = filepos;
+ new_cache->arelt = new_elt;
+ new_cache->next = (struct ar_cache *)NULL;
+ if (bfd_ardata (arch_bfd)->cache == NULL)
+ bfd_ardata (arch_bfd)->cache = new_cache;
+ else {
+ struct ar_cache *current = bfd_ardata (arch_bfd)->cache;
+
+ for (; current->next != NULL; current = current->next);
+ current->next = new_cache;
+ }
+
+ return true;
+}
+
+
+
+/* The name begins with space. Hence the rest of the name is an index into
+ the string table. */
+char *
+get_extended_arelt_filename (arch, name)
+ bfd *arch;
+ char *name;
+{
+ unsigned long index = 0;
+
+ /* Should extract string so that I can guarantee not to overflow into
+ the next region, but I'm too lazy. */
+ errno = 0;
+ /* Skip first char, which is '/' in SVR4 or ' ' in some other variants. */
+ index = strtol (name+1, NULL, 10);
+ if (errno != 0) {
+ bfd_error = malformed_archive;
+ return NULL;
+ }
+
+ return bfd_ardata (arch)->extended_names + index;
+}
+
+/* This functions reads an arch header and returns an areltdata pointer, or
+ NULL on error.
+
+ Presumes the file pointer is already in the right place (ie pointing
+ to the ar_hdr in the file). Moves the file pointer; on success it
+ should be pointing to the front of the file contents; on failure it
+ could have been moved arbitrarily.
+*/
+
+struct areltdata *
+snarf_ar_hdr (abfd)
+ bfd *abfd;
+{
+#ifndef errno
+ extern int errno;
+#endif
+
+ struct ar_hdr hdr;
+ char *hdrp = (char *) &hdr;
+ unsigned int parsed_size;
+ struct areltdata *ared;
+ char *filename = NULL;
+ unsigned int namelen = 0;
+ unsigned int allocsize = sizeof (struct areltdata) + sizeof (struct ar_hdr);
+ char *allocptr = 0;
+
+ if (bfd_read ((PTR)hdrp, 1, sizeof (struct ar_hdr), abfd)
+ != sizeof (struct ar_hdr)) {
+ bfd_error = no_more_archived_files;
+ return NULL;
+ }
+ if (strncmp ((hdr.ar_fmag), ARFMAG, 2)) {
+ bfd_error = malformed_archive;
+ return NULL;
+ }
+
+ errno = 0;
+ parsed_size = strtol (hdr.ar_size, NULL, 10);
+ if (errno != 0) {
+ bfd_error = malformed_archive;
+ return NULL;
+ }
+
+ /* extract the filename from the archive - there are two ways to
+ specify an extendend name table, either the first char of the
+ name is a space, or it's a slash. */
+ if ((hdr.ar_name[0] == '/'
+ || (hdr.ar_name[0] == ' '
+ && memchr (hdr.ar_name, '/', ar_maxnamelen(abfd)) == NULL))
+ && bfd_ardata (abfd)->extended_names != NULL) {
+ filename = get_extended_arelt_filename (abfd, hdr.ar_name);
+ if (filename == NULL) {
+ bfd_error = malformed_archive;
+ return NULL;
+ }
+ }
+ /* BSD4.4-style long filename.
+ Only implemented for reading, so far! */
+ else if (hdr.ar_name[0] == '#' && hdr.ar_name[1] == '1'
+ && hdr.ar_name[2] == '/' && isdigit(hdr.ar_name[3]))
+ {
+ /* BSD-4.4 extended name */
+ namelen = atoi (&hdr.ar_name[3]);
+ allocsize += namelen + 1;
+ parsed_size -= namelen;
+
+ allocptr = bfd_zalloc(abfd, allocsize);
+ if (allocptr == NULL) {
+ bfd_error = no_memory;
+ return NULL;
+ }
+ filename = allocptr
+ + (sizeof (struct areltdata) + sizeof (struct ar_hdr));
+ if (bfd_read (filename, 1, namelen, abfd) != namelen) {
+ bfd_error = no_more_archived_files;
+ return NULL;
+ }
+ filename[namelen] = '\0';
+ }
+ else
+ {
+ /* We judge the end of the name by looking for '/' or ' '.
+ Note: The SYSV format (terminated by '/') allows embedded
+ spaces, so only look for ' ' if we don't find '/'. */
+
+ namelen = 0;
+ while (hdr.ar_name[namelen] != '\0' &&
+ hdr.ar_name[namelen] != '/') {
+ namelen++;
+ if (namelen == (unsigned)ar_maxnamelen(abfd)) {
+ namelen = 0;
+ while (hdr.ar_name[namelen] != ' '
+ && namelen < (unsigned)ar_maxnamelen(abfd)) {
+ namelen++;
+ }
+ break;
+ }
+ }
+
+ allocsize += namelen + 1;
+ }
+
+ if (!allocptr) {
+ allocptr = bfd_zalloc(abfd, allocsize);
+ if (allocptr == NULL) {
+ bfd_error = no_memory;
+ return NULL;
+ }
+ }
+
+ ared = (struct areltdata *) allocptr;
+
+ ared->arch_header = allocptr + sizeof (struct areltdata);
+ memcpy ((char *) ared->arch_header, (char *) &hdr, sizeof (struct ar_hdr));
+ ared->parsed_size = parsed_size;
+
+ if (filename != NULL) ared->filename = filename;
+ else {
+ ared->filename = allocptr + (sizeof (struct areltdata) +
+ sizeof (struct ar_hdr));
+ if (namelen)
+ memcpy (ared->filename, hdr.ar_name, namelen);
+ ared->filename[namelen] = '\0';
+ }
+
+ return ared;
+}
+
+/* This is an internal function; it's mainly used when indexing
+ through the archive symbol table, but also used to get the next
+ element, since it handles the bookkeeping so nicely for us.
+*/
+
+bfd *
+get_elt_at_filepos (archive, filepos)
+ bfd *archive;
+ file_ptr filepos;
+{
+ struct areltdata *new_areldata;
+ bfd *n_nfd;
+
+ n_nfd = look_for_bfd_in_cache (archive, filepos);
+ if (n_nfd)
+ return n_nfd;
+
+ if (0 > bfd_seek (archive, filepos, SEEK_SET))
+ {
+ bfd_error = system_call_error;
+ return NULL;
+ }
+
+ if ((new_areldata = snarf_ar_hdr (archive)) == NULL)
+ return NULL;
+
+ n_nfd = _bfd_create_empty_archive_element_shell (archive);
+ if (n_nfd == NULL)
+ {
+ bfd_release (archive, (PTR)new_areldata);
+ return NULL;
+ }
+
+ n_nfd->origin = bfd_tell (archive);
+ n_nfd->arelt_data = (PTR) new_areldata;
+ n_nfd->filename = new_areldata->filename;
+
+ if (add_bfd_to_cache (archive, filepos, n_nfd))
+ return n_nfd;
+
+ /* huh? */
+ bfd_release (archive, (PTR)n_nfd);
+ bfd_release (archive, (PTR)new_areldata);
+ return NULL;
+}
+
+/*
+FUNCTION
+ bfd_get_elt_at_index
+
+SYNOPSIS
+ bfd *bfd_get_elt_at_index(bfd * archive, int index);
+
+DESCRIPTION
+ Return the bfd which is referenced by the symbol indexed by <<index>>.
+ <<index>> should have been returned by <<bfd_get_next_mapent>> (q.v.).
+
+*/
+bfd *
+DEFUN(bfd_get_elt_at_index,(abfd, index),
+ bfd *abfd AND
+ int index)
+{
+ bfd *result =
+ get_elt_at_filepos
+ (abfd, (bfd_ardata (abfd)->symdefs + index)->file_offset);
+ return result;
+}
+
+/*
+FUNCTION
+ bfd_openr_next_archived_file
+
+SYNOPSIS
+ bfd* bfd_openr_next_archived_file(bfd *archive, bfd *previous);
+
+DESCRIPTION
+ Initially provided a BFD containing an archive and NULL, opens
+ an inpout BFD on the first contained element and returns that.
+ Subsequent calls to bfd_openr_next_archived_file should pass
+ the archive and the previous return value to return a created
+ BFD to the next contained element. NULL is returned when there
+ are no more.
+
+*/
+
+bfd *
+bfd_openr_next_archived_file (archive, last_file)
+ bfd *archive;
+ bfd *last_file;
+{
+ if ((bfd_get_format (archive) != bfd_archive) ||
+ (archive->direction == write_direction))
+ {
+ bfd_error = invalid_operation;
+ return NULL;
+ }
+
+ return BFD_SEND (archive,
+ openr_next_archived_file,
+ (archive,
+ last_file));
+}
+
+bfd *
+bfd_generic_openr_next_archived_file (archive, last_file)
+ bfd *archive;
+ bfd *last_file;
+{
+ file_ptr filestart;
+
+ if (!last_file)
+ filestart = bfd_ardata (archive)->first_file_filepos;
+ else {
+ unsigned int size = arelt_size(last_file);
+ /* Pad to an even boundary...
+ Note that last_file->origin can be odd in the case of
+ BSD-4.4-style element with a long odd size. */
+ filestart = last_file->origin + size;
+ filestart += filestart % 2;
+ }
+
+ return get_elt_at_filepos (archive, filestart);
+}
+
+
+bfd_target *
+bfd_generic_archive_p (abfd)
+ bfd *abfd;
+{
+ char armag[SARMAG+1];
+
+ if (bfd_read ((PTR)armag, 1, SARMAG, abfd) != SARMAG) {
+ bfd_error = wrong_format;
+ return 0;
+ }
+
+#ifdef GNU960
+ if (strncmp (armag, BFD_GNU960_ARMAG(abfd), SARMAG)) return 0;
+#else
+ if (strncmp (armag, ARMAG, SARMAG) &&
+ strncmp (armag, ARMAGB, SARMAG)) return 0;
+#endif
+
+
+
+ /* We are setting bfd_ardata(abfd) here, but since bfd_ardata
+ involves a cast, we can't do it as the left operand of assignment. */
+ abfd->tdata.aout_ar_data = (struct artdata *) bfd_zalloc(abfd,sizeof (struct artdata));
+
+ if (bfd_ardata (abfd) == NULL) {
+ bfd_error = no_memory;
+ return 0;
+ }
+
+ bfd_ardata (abfd)->first_file_filepos = SARMAG;
+
+ if (!BFD_SEND (abfd, _bfd_slurp_armap, (abfd))) {
+ bfd_release(abfd, bfd_ardata (abfd));
+ abfd->tdata.aout_ar_data = NULL;
+ return 0;
+ }
+
+ if (!BFD_SEND (abfd, _bfd_slurp_extended_name_table, (abfd))) {
+ bfd_release(abfd, bfd_ardata (abfd));
+ abfd->tdata.aout_ar_data = NULL;
+ return 0;
+ }
+
+ return abfd->xvec;
+}
+
+/* Returns false on error, true otherwise */
+static boolean
+DEFUN (do_slurp_bsd_armap, (abfd),
+ bfd *abfd)
+{
+ struct areltdata *mapdata;
+ unsigned int counter = 0;
+ int *raw_armap, *rbase;
+ struct artdata *ardata = bfd_ardata (abfd);
+ char *stringbase;
+ unsigned int parsed_size;
+
+ mapdata = snarf_ar_hdr (abfd);
+ if (mapdata == NULL) return false;
+ parsed_size = mapdata->parsed_size;
+ bfd_release (abfd, (PTR)mapdata); /* Don't need it any more. */
+
+ raw_armap = (int *) bfd_zalloc(abfd, parsed_size);
+ if (raw_armap == NULL) {
+ bfd_error = no_memory;
+ return false;
+ }
+
+ if (bfd_read ((PTR)raw_armap, 1, parsed_size, abfd) != parsed_size) {
+ bfd_error = malformed_archive;
+ byebye:
+ bfd_release (abfd, (PTR)raw_armap);
+ return false;
+ }
+
+ ardata->symdef_count =
+ bfd_h_get_32(abfd, (PTR)raw_armap) / sizeof (struct symdef);
+
+ if (ardata->symdef_count * sizeof (struct symdef)
+ > parsed_size - sizeof (*raw_armap)) {
+ /* Probably we're using the wrong byte ordering. */
+ bfd_error = wrong_format;
+ goto byebye;
+ }
+
+ ardata->cache = 0;
+ rbase = raw_armap+1;
+ ardata->symdefs = (carsym *) rbase;
+ stringbase = ((char *) (ardata->symdefs + ardata->symdef_count)) + 4;
+
+ for (;counter < ardata->symdef_count; counter++) {
+ struct symdef *sym = ((struct symdef *) rbase) + counter;
+ sym->s.name = bfd_h_get_32(abfd, (PTR)(&(sym->s.string_offset))) + stringbase;
+ sym->file_offset = bfd_h_get_32(abfd, (PTR)( &(sym->file_offset)));
+ }
+
+ ardata->first_file_filepos = bfd_tell (abfd);
+ /* Pad to an even boundary if you have to */
+ ardata->first_file_filepos += (ardata-> first_file_filepos) %2;
+ /* FIXME, we should provide some way to free raw_ardata when
+ we are done using the strings from it. For now, it seems
+ to be allocated on an obstack anyway... */
+ bfd_has_map (abfd) = true;
+ return true;
+}
+
+/* Returns false on error, true otherwise */
+static boolean
+DEFUN (do_slurp_coff_armap, (abfd),
+ bfd *abfd)
+{
+ struct areltdata *mapdata;
+ int *raw_armap, *rawptr;
+ struct artdata *ardata = bfd_ardata (abfd);
+ char *stringbase;
+ unsigned int stringsize;
+ unsigned int parsed_size;
+ carsym *carsyms;
+ unsigned int nsymz; /* Number of symbols in armap. */
+
+ bfd_vma (*swap) PARAMS ((bfd_byte*));
+ char int_buf[sizeof(long)];
+ unsigned int carsym_size, ptrsize, i;
+
+ mapdata = snarf_ar_hdr (abfd);
+ if (mapdata == NULL) return false;
+ parsed_size = mapdata->parsed_size;
+ bfd_release (abfd, (PTR)mapdata); /* Don't need it any more. */
+
+ if (bfd_read ((PTR)int_buf, 1, 4, abfd) != 4) {
+ bfd_error = malformed_archive;
+ return false;
+ }
+ /* It seems that all numeric information in a coff archive is always
+ in big endian format, nomatter the host or target. */
+ swap = bfd_getb32;
+ nsymz = bfd_getb32((PTR)int_buf);
+ stringsize = parsed_size - (4 * nsymz) - 4;
+
+#if 1
+ /* ... except that some archive formats are broken, and it may be our
+ fault - the i960 little endian coff sometimes has big and sometimes
+ little, because our tools changed. Here's a horrible hack to clean
+ up the crap. */
+
+ if (stringsize > 0xfffff) {
+ /* This looks dangerous, let's do it the other way around */
+ nsymz = bfd_getl32((PTR)int_buf);
+ stringsize = parsed_size - (4 * nsymz) - 4;
+ swap = bfd_getl32;
+ }
+#endif
+
+ /* The coff armap must be read sequentially. So we construct a bsd-style
+ one in core all at once, for simplicity. */
+
+ carsym_size = (nsymz * sizeof (carsym));
+ ptrsize = (4 * nsymz);
+
+ ardata->symdefs = (carsym *) bfd_zalloc(abfd, carsym_size + stringsize + 1);
+ if (ardata->symdefs == NULL) {
+ bfd_error = no_memory;
+ return false;
+ }
+ carsyms = ardata->symdefs;
+ stringbase = ((char *) ardata->symdefs) + carsym_size;
+
+ /* Allocate and read in the raw offsets. */
+ raw_armap = (int *) bfd_alloc(abfd, ptrsize);
+ if (raw_armap == NULL) {
+ bfd_error = no_memory;
+ goto release_symdefs;
+ }
+ if (bfd_read ((PTR)raw_armap, 1, ptrsize, abfd) != ptrsize
+ || bfd_read ((PTR)stringbase, 1, stringsize, abfd) != stringsize) {
+ bfd_error = malformed_archive;
+ goto release_raw_armap;
+ }
+
+ /* OK, build the carsyms */
+ for (i = 0; i < nsymz; i++) {
+ rawptr = raw_armap + i;
+ carsyms->file_offset = swap((PTR)rawptr);
+ carsyms->name = stringbase;
+ while (*stringbase++) ;
+ carsyms++;
+ }
+ *stringbase = 0;
+
+ ardata->symdef_count = nsymz;
+ ardata->first_file_filepos = bfd_tell (abfd);
+ /* Pad to an even boundary if you have to */
+ ardata->first_file_filepos += (ardata->first_file_filepos) %2;
+
+ bfd_has_map (abfd) = true;
+ bfd_release (abfd, (PTR)raw_armap);
+ return true;
+
+ release_raw_armap:
+ bfd_release (abfd, (PTR)raw_armap);
+ release_symdefs:
+ bfd_release (abfd, (PTR)(ardata)->symdefs);
+ return false;
+}
+
+/* This routine can handle either coff-style or bsd-style armaps.
+ Returns false on error, true otherwise */
+
+boolean
+bfd_slurp_armap (abfd)
+ bfd *abfd;
+{
+ char nextname[17];
+ int i = bfd_read ((PTR)nextname, 1, 16, abfd);
+
+ if (i == 0)
+ return true;
+ if (i != 16)
+ return false;
+
+ bfd_seek (abfd, (file_ptr) -16, SEEK_CUR);
+
+ if (!strncmp (nextname, "__.SYMDEF ", 16))
+ return do_slurp_bsd_armap (abfd);
+ else if (!strncmp (nextname, "/ ", 16))
+ return do_slurp_coff_armap (abfd);
+
+ bfd_has_map (abfd) = false;
+ return true;
+}
+
+/* Returns false on error, true otherwise */
+/* flavor 2 of a bsd armap, similar to bfd_slurp_bsd_armap except the
+ header is in a slightly different order and the map name is '/'.
+ This flavour is used by hp300hpux. */
+boolean
+bfd_slurp_bsd_armap_f2 (abfd)
+ bfd *abfd;
+{
+ struct areltdata *mapdata;
+ char nextname[17];
+ unsigned int counter = 0;
+ int *raw_armap, *rbase;
+ struct artdata *ardata = bfd_ardata (abfd);
+ char *stringbase;
+ unsigned int stringsize;
+ int i = bfd_read ((PTR)nextname, 1, 16, abfd);
+
+ if (i == 0)
+ return true;
+ if (i != 16)
+ return false;
+
+ /* The archive has at least 16 bytes in it */
+ bfd_seek (abfd, -16L, SEEK_CUR);
+
+ if (!strncmp (nextname, "__.SYMDEF ", 16))
+ return do_slurp_bsd_armap (abfd);
+
+ if (strncmp (nextname, "/ ", 16))
+ {
+ bfd_has_map (abfd) = false;
+ return true;
+ }
+
+ mapdata = snarf_ar_hdr (abfd);
+ if (mapdata == NULL) return false;
+
+ raw_armap = (int *) bfd_zalloc(abfd,mapdata->parsed_size);
+ if (raw_armap == NULL)
+ {
+ bfd_error = no_memory;
+ byebye:
+ bfd_release (abfd, (PTR)mapdata);
+ return false;
+ }
+
+ if (bfd_read ((PTR)raw_armap, 1, mapdata->parsed_size, abfd) !=
+ mapdata->parsed_size)
+ {
+ bfd_error = malformed_archive;
+ byebyebye:
+ bfd_release (abfd, (PTR)raw_armap);
+ goto byebye;
+ }
+
+ ardata->symdef_count = bfd_h_get_16(abfd, (PTR)raw_armap);
+
+ if (ardata->symdef_count * sizeof (struct symdef)
+ > mapdata->parsed_size - sizeof (*raw_armap))
+ {
+ /* Probably we're using the wrong byte ordering. */
+ bfd_error = wrong_format;
+ goto byebyebye;
+ }
+
+ ardata->cache = 0;
+
+ stringsize = bfd_h_get_32(abfd, (PTR)(((char*)raw_armap)+2));
+ /* skip sym count and string sz */
+ rbase = (int*)(((char*)raw_armap) + 6);
+ stringbase = (char *) rbase;
+ ardata->symdefs = (carsym *)(((char*) rbase) + stringsize);
+
+ for (;counter < ardata->symdef_count; counter++)
+ {
+ struct symdef *sym = ((struct symdef *) ardata->symdefs) + counter;
+ sym->s.name = bfd_h_get_32(abfd, (PTR)(&(sym->s.string_offset))) + stringbase;
+ sym->file_offset = bfd_h_get_32(abfd, (PTR)( &(sym->file_offset)));
+ }
+
+ ardata->first_file_filepos = bfd_tell (abfd);
+ /* Pad to an even boundary if you have to */
+ ardata->first_file_filepos += (ardata-> first_file_filepos) %2;
+ /* FIXME, we should provide some way to free raw_ardata when
+ we are done using the strings from it. For now, it seems
+ to be allocated on an obstack anyway... */
+ bfd_has_map (abfd) = true;
+ return true;
+}
+
+/** Extended name table.
+
+ Normally archives support only 14-character filenames.
+
+ Intel has extended the format: longer names are stored in a special
+ element (the first in the archive, or second if there is an armap);
+ the name in the ar_hdr is replaced by <space><index into filename
+ element>. Index is the P.R. of an int (decimal). Data General have
+ extended the format by using the prefix // for the special element */
+
+/* Returns false on error, true otherwise */
+boolean
+_bfd_slurp_extended_name_table (abfd)
+ bfd *abfd;
+{
+ char nextname[17];
+ struct areltdata *namedata;
+
+ /* FIXME: Formatting sucks here, and in case of failure of BFD_READ,
+ we probably don't want to return true. */
+ if (bfd_read ((PTR)nextname, 1, 16, abfd) == 16) {
+
+ bfd_seek (abfd, (file_ptr) -16, SEEK_CUR);
+
+ if (strncmp (nextname, "ARFILENAMES/ ", 16) != 0 &&
+ strncmp (nextname, "// ", 16) != 0)
+ {
+ bfd_ardata (abfd)->extended_names = NULL;
+ return true;
+ }
+
+ namedata = snarf_ar_hdr (abfd);
+ if (namedata == NULL) return false;
+
+ bfd_ardata (abfd)->extended_names = bfd_zalloc(abfd,namedata->parsed_size);
+ if (bfd_ardata (abfd)->extended_names == NULL) {
+ bfd_error = no_memory;
+ byebye:
+ bfd_release (abfd, (PTR)namedata);
+ return false;
+ }
+
+ if (bfd_read ((PTR)bfd_ardata (abfd)->extended_names, 1,
+ namedata->parsed_size, abfd) != namedata->parsed_size) {
+ bfd_error = malformed_archive;
+ bfd_release (abfd, (PTR)(bfd_ardata (abfd)->extended_names));
+ bfd_ardata (abfd)->extended_names = NULL;
+ goto byebye;
+ }
+
+ /* Since the archive is supposed to be printable if it contains
+ text, the entries in the list are newline-padded, not null
+ padded. In SVR4-style archives, the names also have a
+ trailing '/'. We'll fix both problems here.. */
+ {
+ char *temp = bfd_ardata (abfd)->extended_names;
+ char *limit = temp + namedata->parsed_size;
+ for (; temp < limit; ++temp)
+ if (*temp == '\n')
+ temp[temp[-1] == '/' ? -1 : 0] = '\0';
+ }
+
+ /* Pad to an even boundary if you have to */
+ bfd_ardata (abfd)->first_file_filepos = bfd_tell (abfd);
+ bfd_ardata (abfd)->first_file_filepos +=
+ (bfd_ardata (abfd)->first_file_filepos) %2;
+
+ /* FIXME, we can't release namedata here because it was allocated
+ below extended_names on the obstack... */
+ /* bfd_release (abfd, namedata); */
+ }
+ return true;
+}
+
+#ifdef VMS
+
+/* Return a copy of the stuff in the filename between any :]> and a
+ semicolon */
+static CONST char *
+DEFUN(normalize,(file),
+ CONST char *file)
+{
+ CONST char *first;
+ CONST char *last;
+ char *copy;
+
+ first = file + strlen(file)-1;
+ last = first+1;
+
+ while (first != file)
+ {
+ if (*first == ';')
+ last = first;
+ if (*first == ':' || *first == ']' ||*first == '>')
+ {
+ first++;
+ break;
+ }
+ first --;
+ }
+
+
+ copy = bfd_xmalloc(last - first + 1);
+ memcpy(copy, first, last-first);
+ copy[last-first] = 0;
+
+ return copy;
+}
+
+#else
+static CONST char *
+DEFUN (normalize, (file),
+ CONST char *file)
+{
+ CONST char * filename = strrchr(file, '/');
+
+ if (filename != (char *)NULL) {
+ filename ++;
+ }
+ else {
+ filename = file;
+ }
+ return filename;
+}
+#endif
+/* Follows archive_head and produces an extended name table if necessary.
+ Returns (in tabloc) a pointer to an extended name table, and in tablen
+ the length of the table. If it makes an entry it clobbers the filename
+ so that the element may be written without further massage.
+ Returns true if it ran successfully, false if something went wrong.
+ A successful return may still involve a zero-length tablen!
+ */
+boolean
+DEFUN (bfd_construct_extended_name_table, (abfd, tabloc, tablen),
+ bfd *abfd AND
+ char **tabloc AND
+ unsigned int *tablen)
+{
+ unsigned int maxname = abfd->xvec->ar_max_namelen;
+ unsigned int total_namelen = 0;
+ bfd *current;
+ char *strptr;
+
+ *tablen = 0;
+
+ /* Figure out how long the table should be */
+ for (current = abfd->archive_head; current != NULL; current = current->next){
+ unsigned int thislen = strlen (normalize(current->filename));
+ if (thislen > maxname) total_namelen += thislen + 1; /* leave room for \n */
+ }
+
+ if (total_namelen == 0) return true;
+
+ *tabloc = bfd_zalloc (abfd,total_namelen);
+ if (*tabloc == NULL) {
+ bfd_error = no_memory;
+ return false;
+ }
+
+ *tablen = total_namelen;
+ strptr = *tabloc;
+
+ for (current = abfd->archive_head; current != NULL; current =
+ current->next) {
+ CONST char *normal =normalize( current->filename);
+ unsigned int thislen = strlen (normal);
+ if (thislen > maxname) {
+ /* Works for now; may need to be re-engineered if we encounter an oddball
+ archive format and want to generalise this hack. */
+ struct ar_hdr *hdr = arch_hdr(current);
+ strcpy (strptr, normal);
+ strptr[thislen] = '\n';
+ hdr->ar_name[0] = ' ';
+ /* We know there will always be enough room (one of the few cases
+ where you may safely use sprintf). */
+ sprintf ((hdr->ar_name) + 1, "%-d", (unsigned) (strptr - *tabloc));
+ /* Kinda Kludgy. We should just use the returned value of sprintf
+ but not all implementations get this right */
+ {
+ char *temp = hdr->ar_name +2;
+ for (; temp < hdr->ar_name + maxname; temp++)
+ if (*temp == '\0') *temp = ' ';
+ }
+ strptr += thislen + 1;
+ }
+ }
+
+ return true;
+}
+
+/** A couple of functions for creating ar_hdrs */
+
+/* Takes a filename, returns an arelt_data for it, or NULL if it can't make one.
+ The filename must refer to a filename in the filesystem.
+ The filename field of the ar_hdr will NOT be initialized
+*/
+
+struct areltdata *
+DEFUN(bfd_ar_hdr_from_filesystem, (abfd,filename),
+ bfd* abfd AND
+ CONST char *filename)
+{
+ struct stat status;
+ struct areltdata *ared;
+ struct ar_hdr *hdr;
+ char *temp, *temp1;
+
+
+ if (stat (filename, &status) != 0) {
+ bfd_error = system_call_error;
+ return NULL;
+ }
+
+ ared = (struct areltdata *) bfd_zalloc(abfd, sizeof (struct ar_hdr) +
+ sizeof (struct areltdata));
+ if (ared == NULL) {
+ bfd_error = no_memory;
+ return NULL;
+ }
+ hdr = (struct ar_hdr *) (((char *) ared) + sizeof (struct areltdata));
+
+ /* ar headers are space padded, not null padded! */
+ temp = (char *) hdr;
+ temp1 = temp + sizeof (struct ar_hdr) - 2;
+ for (; temp < temp1; *(temp++) = ' ');
+ strncpy (hdr->ar_fmag, ARFMAG, 2);
+
+ /* Goddamned sprintf doesn't permit MAXIMUM field lengths */
+ sprintf ((hdr->ar_date), "%-12ld", status.st_mtime);
+ sprintf ((hdr->ar_uid), "%d", status.st_uid);
+ sprintf ((hdr->ar_gid), "%d", status.st_gid);
+ sprintf ((hdr->ar_mode), "%-8o", (unsigned) status.st_mode);
+ sprintf ((hdr->ar_size), "%-10ld", status.st_size);
+ /* Correct for a lossage in sprintf whereby it null-terminates. I cannot
+ understand how these C losers could design such a ramshackle bunch of
+ IO operations */
+ temp = (char *) hdr;
+ temp1 = temp + sizeof (struct ar_hdr) - 2;
+ for (; temp < temp1; temp++) {
+ if (*temp == '\0') *temp = ' ';
+ }
+ strncpy (hdr->ar_fmag, ARFMAG, 2);
+ ared->parsed_size = status.st_size;
+ ared->arch_header = (char *) hdr;
+
+ return ared;
+}
+
+/* This is magic required by the "ar" program. Since it's
+ undocumented, it's undocumented. You may think that it would
+ take a strong stomach to write this, and it does, but it takes
+ even a stronger stomach to try to code around such a thing!
+*/
+
+struct ar_hdr *
+DEFUN(bfd_special_undocumented_glue, (abfd, filename),
+ bfd *abfd AND
+ char *filename)
+{
+ struct areltdata *ar_elt = bfd_ar_hdr_from_filesystem (abfd, filename);
+ if (ar_elt == NULL)
+ return NULL;
+ return (struct ar_hdr *) ar_elt->arch_header;
+}
+
+
+/* Analogous to stat call */
+int
+bfd_generic_stat_arch_elt (abfd, buf)
+ bfd *abfd;
+ struct stat *buf;
+{
+ struct ar_hdr *hdr;
+ char *aloser;
+
+ if (abfd->arelt_data == NULL) {
+ bfd_error = invalid_operation;
+ return -1;
+ }
+
+ hdr = arch_hdr (abfd);
+
+#define foo(arelt, stelt, size) \
+ buf->stelt = strtol (hdr->arelt, &aloser, size); \
+ if (aloser == hdr->arelt) return -1;
+
+ foo (ar_date, st_mtime, 10);
+ foo (ar_uid, st_uid, 10);
+ foo (ar_gid, st_gid, 10);
+ foo (ar_mode, st_mode, 8);
+
+ buf->st_size = arch_eltdata (abfd)->parsed_size;
+
+ return 0;
+}
+
+void
+bfd_dont_truncate_arname (abfd, pathname, arhdr)
+ bfd *abfd;
+ CONST char *pathname;
+ char *arhdr;
+{
+ /* FIXME: This interacts unpleasantly with ar's quick-append option.
+ Fortunately ic960 users will never use that option. Fixing this
+ is very hard; fortunately I know how to do it and will do so once
+ intel's release is out the door. */
+
+ struct ar_hdr *hdr = (struct ar_hdr *) arhdr;
+ int length;
+ CONST char *filename = normalize(pathname);
+ int maxlen = ar_maxnamelen (abfd);
+
+ length = strlen (filename);
+
+ if (length <= maxlen)
+ memcpy (hdr->ar_name, filename, length);
+
+ if (length < maxlen) (hdr->ar_name)[length] = ar_padchar (abfd);
+ return;
+
+}
+
+void
+bfd_bsd_truncate_arname (abfd, pathname, arhdr)
+ bfd *abfd;
+ CONST char *pathname;
+ char *arhdr;
+{
+ struct ar_hdr *hdr = (struct ar_hdr *) arhdr;
+ int length;
+ CONST char *filename = strrchr (pathname, '/');
+ int maxlen = ar_maxnamelen (abfd);
+
+
+ if (filename == NULL)
+ filename = pathname;
+ else
+ ++filename;
+
+ length = strlen (filename);
+
+ if (length <= maxlen)
+ memcpy (hdr->ar_name, filename, length);
+ else {
+ /* pathname: meet procrustes */
+ memcpy (hdr->ar_name, filename, maxlen);
+ length = maxlen;
+ }
+
+ if (length < maxlen) (hdr->ar_name)[length] = ar_padchar (abfd);
+}
+
+/* Store name into ar header. Truncates the name to fit.
+ 1> strip pathname to be just the basename.
+ 2> if it's short enuf to fit, stuff it in.
+ 3> If it doesn't end with .o, truncate it to fit
+ 4> truncate it before the .o, append .o, stuff THAT in.
+*/
+
+/* This is what gnu ar does. It's better but incompatible with the bsd ar. */
+void
+bfd_gnu_truncate_arname (abfd, pathname, arhdr)
+ bfd *abfd;
+ CONST char *pathname;
+ char *arhdr;
+{
+ struct ar_hdr *hdr = (struct ar_hdr *) arhdr;
+ int length;
+ CONST char *filename = strrchr (pathname, '/');
+ int maxlen = ar_maxnamelen (abfd);
+
+ if (filename == NULL)
+ filename = pathname;
+ else
+ ++filename;
+
+ length = strlen (filename);
+
+ if (length <= maxlen)
+ memcpy (hdr->ar_name, filename, length);
+ else { /* pathname: meet procrustes */
+ memcpy (hdr->ar_name, filename, maxlen);
+ if ((filename[length - 2] == '.') && (filename[length - 1] == 'o')) {
+ hdr->ar_name[maxlen - 2] = '.';
+ hdr->ar_name[maxlen - 1] = 'o';
+ }
+ length = maxlen;
+ }
+
+ if (length < 16) (hdr->ar_name)[length] = ar_padchar (abfd);
+}
+
+
+/* The BFD is open for write and has its format set to bfd_archive */
+boolean
+_bfd_write_archive_contents (arch)
+ bfd *arch;
+{
+ bfd *current;
+ char *etable = NULL;
+ unsigned int elength = 0;
+ boolean makemap = bfd_has_map (arch);
+ boolean hasobjects = false; /* if no .o's, don't bother to make a map */
+ unsigned int i;
+ int tries;
+
+ /* Verify the viability of all entries; if any of them live in the
+ filesystem (as opposed to living in an archive open for input)
+ then construct a fresh ar_hdr for them.
+ */
+ for (current = arch->archive_head; current; current = current->next) {
+ if (bfd_write_p (current)) {
+ bfd_error = invalid_operation;
+ return false;
+ }
+ if (!current->arelt_data) {
+ current->arelt_data =
+ (PTR) bfd_ar_hdr_from_filesystem (arch, current->filename);
+ if (!current->arelt_data) return false;
+
+ /* Put in the file name */
+
+ BFD_SEND (arch, _bfd_truncate_arname,(arch,
+ current->filename,
+ (char *) arch_hdr(current)));
+
+
+ }
+
+ if (makemap) { /* don't bother if we won't make a map! */
+ if ((bfd_check_format (current, bfd_object))
+#if 0 /* FIXME -- these are not set correctly */
+ && ((bfd_get_file_flags (current) & HAS_SYMS))
+#endif
+ )
+ hasobjects = true;
+ }
+ }
+
+ if (!bfd_construct_extended_name_table (arch, &etable, &elength))
+ return false;
+
+ bfd_seek (arch, (file_ptr) 0, SEEK_SET);
+#ifdef GNU960
+ bfd_write (BFD_GNU960_ARMAG(arch), 1, SARMAG, arch);
+#else
+ bfd_write (ARMAG, 1, SARMAG, arch);
+#endif
+
+ if (makemap && hasobjects) {
+
+ if (compute_and_write_armap (arch, elength) != true) {
+ return false;
+ }
+ }
+
+ if (elength != 0) {
+ struct ar_hdr hdr;
+
+ memset ((char *)(&hdr), 0, sizeof (struct ar_hdr));
+ sprintf (&(hdr.ar_name[0]), "ARFILENAMES/");
+ sprintf (&(hdr.ar_size[0]), "%-10d", (int) elength);
+ hdr.ar_fmag[0] = '`'; hdr.ar_fmag[1] = '\n';
+ for (i = 0; i < sizeof (struct ar_hdr); i++)
+ if (((char *)(&hdr))[i] == '\0') (((char *)(&hdr))[i]) = ' ';
+ bfd_write ((char *)&hdr, 1, sizeof (struct ar_hdr), arch);
+ bfd_write (etable, 1, elength, arch);
+ if ((elength % 2) == 1) bfd_write ("\n", 1, 1, arch);
+
+ }
+
+ for (current = arch->archive_head; current; current = current->next) {
+ char buffer[DEFAULT_BUFFERSIZE];
+ unsigned int remaining = arelt_size (current);
+ struct ar_hdr *hdr = arch_hdr(current);
+ /* write ar header */
+
+ if (bfd_write ((char *)hdr, 1, sizeof(*hdr), arch) != sizeof(*hdr)) {
+ syserr:
+ bfd_error = system_call_error;
+ return false;
+ }
+ if (bfd_seek (current, (file_ptr) 0, SEEK_SET) != 0) goto syserr;
+ while (remaining)
+ {
+ unsigned int amt = DEFAULT_BUFFERSIZE;
+ if (amt > remaining) {
+ amt = remaining;
+ }
+ errno = 0;
+ if (bfd_read (buffer, amt, 1, current) != amt) {
+ if (errno) goto syserr;
+ /* Looks like a truncated archive. */
+ bfd_error = malformed_archive;
+ return false;
+ }
+ if (bfd_write (buffer, amt, 1, arch) != amt) goto syserr;
+ remaining -= amt;
+ }
+ if ((arelt_size (current) % 2) == 1) bfd_write ("\n", 1, 1, arch);
+ }
+
+ /* Verify the timestamp in the archive file. If it would
+ not be accepted by the linker, rewrite it until it would be.
+ If anything odd happens, break out and just return.
+ (The Berkeley linker checks the timestamp and refuses to read the
+ table-of-contents if it is >60 seconds less than the file's
+ modified-time. That painful hack requires this painful hack. */
+
+ tries = 1;
+ do {
+ /* FIXME! This kludge is to avoid adding a member to the xvec,
+ while generating a small patch for Adobe. FIXME! The
+ update_armap_timestamp function call should be in the xvec,
+ thus:
+
+ if (bfd_update_armap_timestamp (arch) == true) break;
+ ^
+
+ Instead, we check whether in a BSD archive, and call directly. */
+
+ if (arch->xvec->write_armap != bsd_write_armap)
+ break;
+ if (bsd_update_armap_timestamp(arch) == true) /* FIXME!!! Vector it */
+ break;
+ if (tries > 0)
+ fprintf (stderr,
+ "Warning: writing archive was slow: rewriting timestamp\n");
+ } while (++tries < 6 );
+
+ return true;
+}
+
+/* Note that the namidx for the first symbol is 0 */
+
+boolean
+compute_and_write_armap (arch, elength)
+ bfd *arch;
+ unsigned int elength;
+{
+ bfd *current;
+ file_ptr elt_no = 0;
+ struct orl *map;
+ int orl_max = 15000; /* fine initial default */
+ int orl_count = 0;
+ int stridx = 0; /* string index */
+
+ /* Dunno if this is the best place for this info... */
+ if (elength != 0) elength += sizeof (struct ar_hdr);
+ elength += elength %2 ;
+
+ map = (struct orl *) bfd_zalloc (arch,orl_max * sizeof (struct orl));
+ if (map == NULL) {
+ bfd_error = no_memory;
+ return false;
+ }
+
+ /* Drop all the files called __.SYMDEF, we're going to make our
+ own */
+ while (arch->archive_head &&
+ strcmp(arch->archive_head->filename,"__.SYMDEF") == 0)
+ {
+ arch->archive_head = arch->archive_head->next;
+ }
+ /* Map over each element */
+ for (current = arch->archive_head;
+ current != (bfd *)NULL;
+ current = current->next, elt_no++)
+ {
+ if ((bfd_check_format (current, bfd_object) == true)
+ && ((bfd_get_file_flags (current) & HAS_SYMS))) {
+ asymbol **syms;
+ unsigned int storage;
+ unsigned int symcount;
+ unsigned int src_count;
+
+ storage = get_symtab_upper_bound (current);
+ if (storage != 0) {
+
+ syms = (asymbol **) bfd_zalloc (arch,storage);
+ if (syms == NULL) {
+ bfd_error = no_memory; /* FIXME -- memory leak */
+ return false;
+ }
+ symcount = bfd_canonicalize_symtab (current, syms);
+
+
+ /* Now map over all the symbols, picking out the ones we want */
+ for (src_count = 0; src_count <symcount; src_count++) {
+ flagword flags =
+ (syms[src_count])->flags;
+ asection *sec =
+ syms[src_count]->section;
+
+ if ((flags & BSF_GLOBAL ||
+ flags & BSF_WEAK ||
+ flags & BSF_INDIRECT ||
+ bfd_is_com_section (sec))
+ && (sec != &bfd_und_section)) {
+
+ /* This symbol will go into the archive header */
+ if (orl_count == orl_max)
+ {
+ orl_max *= 2;
+ map = (struct orl *) bfd_realloc (arch, (char *) map,
+ orl_max * sizeof (struct orl));
+ }
+
+ (map[orl_count]).name = (char **) &((syms[src_count])->name);
+ (map[orl_count]).pos = (file_ptr) current;
+ (map[orl_count]).namidx = stridx;
+
+ stridx += strlen ((syms[src_count])->name) + 1;
+ ++orl_count;
+ }
+ }
+ }
+ }
+ }
+ /* OK, now we have collected all the data, let's write them out */
+ if (!BFD_SEND (arch, write_armap,
+ (arch, elength, map, orl_count, stridx))) {
+
+ return false;
+ }
+
+
+ return true;
+}
+
+boolean
+bsd_write_armap (arch, elength, map, orl_count, stridx)
+ bfd *arch;
+ unsigned int elength;
+ struct orl *map;
+ unsigned int orl_count;
+ int stridx;
+{
+ int padit = stridx & 1;
+ unsigned int ranlibsize = orl_count * sizeof (struct ranlib);
+ unsigned int stringsize = stridx + padit;
+ /* Include 8 bytes to store ranlibsize and stringsize in output. */
+ unsigned int mapsize = ranlibsize + stringsize + 8;
+ file_ptr firstreal;
+ bfd *current = arch->archive_head;
+ bfd *last_elt = current; /* last element arch seen */
+ int temp;
+ int count;
+ struct ar_hdr hdr;
+ struct stat statbuf;
+ unsigned int i;
+
+ firstreal = mapsize + elength + sizeof (struct ar_hdr) + SARMAG;
+
+ stat (arch->filename, &statbuf);
+ memset ((char *)(&hdr), 0, sizeof (struct ar_hdr));
+ sprintf (hdr.ar_name, RANLIBMAG);
+ /* Remember the timestamp, to keep it holy. But fudge it a little. */
+ bfd_ardata(arch)->armap_timestamp = statbuf.st_mtime + ARMAP_TIME_OFFSET;
+ bfd_ardata(arch)->armap_datepos = SARMAG +
+ offsetof(struct ar_hdr, ar_date[0]);
+ sprintf (hdr.ar_date, "%ld", bfd_ardata(arch)->armap_timestamp);
+ sprintf (hdr.ar_uid, "%d", getuid());
+ sprintf (hdr.ar_gid, "%d", getgid());
+ sprintf (hdr.ar_size, "%-10d", (int) mapsize);
+ hdr.ar_fmag[0] = '`'; hdr.ar_fmag[1] = '\n';
+ for (i = 0; i < sizeof (struct ar_hdr); i++)
+ if (((char *)(&hdr))[i] == '\0') (((char *)(&hdr))[i]) = ' ';
+ bfd_write ((char *)&hdr, 1, sizeof (struct ar_hdr), arch);
+ bfd_h_put_32(arch, (bfd_vma) ranlibsize, (PTR)&temp);
+ bfd_write (&temp, 1, sizeof (temp), arch);
+
+ for (count = 0; count < orl_count; count++) {
+ struct symdef outs;
+ struct symdef *outp = &outs;
+
+ if (((bfd *)(map[count]).pos) != last_elt) {
+ do {
+ firstreal += arelt_size (current) + sizeof (struct ar_hdr);
+ firstreal += firstreal % 2;
+ current = current->next;
+ } while (current != (bfd *)(map[count]).pos);
+ } /* if new archive element */
+
+ last_elt = current;
+ bfd_h_put_32(arch, ((map[count]).namidx),(PTR) &outs.s.string_offset);
+ bfd_h_put_32(arch, firstreal,(PTR) &outs.file_offset);
+ bfd_write ((char *)outp, 1, sizeof (outs), arch);
+ }
+
+ /* now write the strings themselves */
+ bfd_h_put_32(arch, stringsize, (PTR)&temp);
+ bfd_write ((PTR)&temp, 1, sizeof (temp), arch);
+ for (count = 0; count < orl_count; count++)
+ bfd_write (*((map[count]).name), 1, strlen (*((map[count]).name))+1, arch);
+
+ /* The spec sez this should be a newline. But in order to be
+ bug-compatible for sun's ar we use a null. */
+ if (padit)
+ bfd_write("\0",1,1,arch);
+
+ return true;
+}
+
+
+/* At the end of archive file handling, update the timestamp in the
+ file, so the linker will accept it.
+
+ Return true if the timestamp was OK, or an unusual problem happened.
+ Return false if we updated the timestamp. */
+
+static boolean
+bsd_update_armap_timestamp (arch)
+ bfd *arch;
+{
+ struct stat archstat;
+ struct ar_hdr hdr;
+ int i;
+
+ /* Flush writes, get last-write timestamp from file, and compare it
+ to the timestamp IN the file. */
+ bfd_flush (arch);
+ if (bfd_stat (arch, &archstat) == -1) {
+ perror ("Reading archive file mod timestamp");
+ return true; /* Can't read mod time for some reason */
+ }
+ if (archstat.st_mtime <= bfd_ardata(arch)->armap_timestamp)
+ return true; /* OK by the linker's rules */
+
+ /* Update the timestamp. */
+ bfd_ardata(arch)->armap_timestamp = archstat.st_mtime + ARMAP_TIME_OFFSET;
+
+ /* Prepare an ASCII version suitable for writing. */
+ memset (hdr.ar_date, 0, sizeof (hdr.ar_date));
+ sprintf (hdr.ar_date, "%ld", bfd_ardata(arch)->armap_timestamp);
+ for (i = 0; i < sizeof (hdr.ar_date); i++)
+ if (hdr.ar_date[i] == '\0')
+ (hdr.ar_date)[i] = ' ';
+
+ /* Write it into the file. */
+ bfd_seek (arch, bfd_ardata(arch)->armap_datepos, SEEK_SET);
+ if (bfd_write (hdr.ar_date, sizeof(hdr.ar_date), 1, arch)
+ != sizeof(hdr.ar_date)) {
+ perror ("Writing updated armap timestamp");
+ return true; /* Some error while writing */
+ }
+
+ return false; /* We updated the timestamp successfully. */
+}
+
+
+/* A coff armap looks like :
+ lARMAG
+ struct ar_hdr with name = '/'
+ number of symbols
+ offset of file for symbol 0
+ offset of file for symbol 1
+
+ offset of file for symbol n-1
+ symbol name 0
+ symbol name 1
+
+ symbol name n-1
+
+*/
+
+boolean
+coff_write_armap (arch, elength, map, symbol_count, stridx)
+ bfd *arch;
+ unsigned int elength;
+ struct orl *map;
+ unsigned int symbol_count;
+ int stridx;
+{
+ /* The size of the ranlib is the number of exported symbols in the
+ archive * the number of bytes in a int, + an int for the count */
+
+ unsigned int ranlibsize = (symbol_count * 4) + 4;
+ unsigned int stringsize = stridx;
+ unsigned int mapsize = stringsize + ranlibsize;
+ file_ptr archive_member_file_ptr;
+ bfd *current = arch->archive_head;
+ int count;
+ struct ar_hdr hdr;
+ unsigned int i;
+ int padit = mapsize & 1;
+
+ if (padit) mapsize ++;
+
+ /* work out where the first object file will go in the archive */
+ archive_member_file_ptr = mapsize + elength + sizeof (struct ar_hdr) + SARMAG;
+
+ memset ((char *)(&hdr), 0, sizeof (struct ar_hdr));
+ hdr.ar_name[0] = '/';
+ sprintf (hdr.ar_size, "%-10d", (int) mapsize);
+ sprintf (hdr.ar_date, "%ld", (long)time (NULL));
+ /* This, at least, is what Intel coff sets the values to.: */
+ sprintf ((hdr.ar_uid), "%d", 0);
+ sprintf ((hdr.ar_gid), "%d", 0);
+ sprintf ((hdr.ar_mode), "%-7o",(unsigned ) 0);
+ hdr.ar_fmag[0] = '`'; hdr.ar_fmag[1] = '\n';
+
+ for (i = 0; i < sizeof (struct ar_hdr); i++)
+ if (((char *)(&hdr))[i] == '\0') (((char *)(&hdr))[i]) = ' ';
+
+ /* Write the ar header for this item and the number of symbols */
+
+
+ bfd_write ((PTR)&hdr, 1, sizeof (struct ar_hdr), arch);
+
+ bfd_write_bigendian_4byte_int(arch, symbol_count);
+
+ /* Two passes, first write the file offsets for each symbol -
+ remembering that each offset is on a two byte boundary. */
+
+ /* Write out the file offset for the file associated with each
+ symbol, and remember to keep the offsets padded out. */
+
+ current = arch->archive_head;
+ count = 0;
+ while (current != (bfd *)NULL && count < symbol_count) {
+ /* For each symbol which is used defined in this object, write out
+ the object file's address in the archive */
+
+ while (((bfd *)(map[count]).pos) == current) {
+ bfd_write_bigendian_4byte_int(arch, archive_member_file_ptr);
+ count++;
+ }
+ /* Add size of this archive entry */
+ archive_member_file_ptr += arelt_size (current) + sizeof (struct
+ ar_hdr);
+ /* remember aboout the even alignment */
+ archive_member_file_ptr += archive_member_file_ptr % 2;
+ current = current->next;
+ }
+
+
+
+ /* now write the strings themselves */
+ for (count = 0; count < symbol_count; count++) {
+ bfd_write ((PTR)*((map[count]).name),
+ 1,
+ strlen (*((map[count]).name))+1, arch);
+
+ }
+ /* The spec sez this should be a newline. But in order to be
+ bug-compatible for arc960 we use a null. */
+ if (padit)
+ bfd_write("\0",1,1,arch);
+
+ return true;
+}
diff --git a/gnu/usr.bin/gdb/bfd/archures.c b/gnu/usr.bin/gdb/bfd/archures.c
new file mode 100644
index 0000000..9697904
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/archures.c
@@ -0,0 +1,731 @@
+/* BFD library support routines for architectures.
+ Copyright (C) 1990-1991 Free Software Foundation, Inc.
+ Hacked by John Gilmore and Steve Chamberlain of Cygnus Support.
+
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+
+SECTION
+ Architectures
+
+ BFD's idea of an architecture is implimented in
+ <<archures.c>>. BFD keeps one atom in a BFD describing the
+ architecture of the data attached to the BFD; a pointer to a
+ <<bfd_arch_info_type>>.
+
+ Pointers to structures can be requested independently of a bfd
+ so that an architecture's information can be interrogated
+ without access to an open bfd.
+
+ The arch information is provided by each architecture package.
+ The set of default architectures is selected by the #define
+ <<SELECT_ARCHITECTURES>>. This is normally set up in the
+ <<config/target.mt>> file of your choice. If the name is not
+ defined, then all the architectures supported are included.
+
+ When BFD starts up, all the architectures are called with an
+ initialize method. It is up to the architecture back end to
+ insert as many items into the list of architectures as it wants to;
+ generally this would be one for each machine and one for the
+ default case (an item with a machine field of 0).
+*/
+
+/*
+
+SUBSECTION
+ bfd_architecture
+
+DESCRIPTION
+ This enum gives the object file's CPU architecture, in a
+ global sense --- i.e., what processor family does it belong to?
+ There is another field, which indicates what processor within
+ the family is in use. The machine gives a number which
+ distingushes different versions of the architecture,
+ containing for example 2 and 3 for Intel i960 KA and i960 KB,
+ and 68020 and 68030 for Motorola 68020 and 68030.
+
+.enum bfd_architecture
+.{
+. bfd_arch_unknown, {* File arch not known *}
+. bfd_arch_obscure, {* Arch known, not one of these *}
+. bfd_arch_m68k, {* Motorola 68xxx *}
+. bfd_arch_vax, {* DEC Vax *}
+. bfd_arch_i960, {* Intel 960 *}
+. {* The order of the following is important.
+. lower number indicates a machine type that
+. only accepts a subset of the instructions
+. available to machines with higher numbers.
+. The exception is the "ca", which is
+. incompatible with all other machines except
+. "core". *}
+.
+.#define bfd_mach_i960_core 1
+.#define bfd_mach_i960_ka_sa 2
+.#define bfd_mach_i960_kb_sb 3
+.#define bfd_mach_i960_mc 4
+.#define bfd_mach_i960_xa 5
+.#define bfd_mach_i960_ca 6
+.
+. bfd_arch_a29k, {* AMD 29000 *}
+. bfd_arch_sparc, {* SPARC *}
+. bfd_arch_mips, {* MIPS Rxxxx *}
+. bfd_arch_i386, {* Intel 386 *}
+. bfd_arch_we32k, {* AT&T WE32xxx *}
+. bfd_arch_tahoe, {* CCI/Harris Tahoe *}
+. bfd_arch_i860, {* Intel 860 *}
+. bfd_arch_romp, {* IBM ROMP PC/RT *}
+. bfd_arch_alliant, {* Alliant *}
+. bfd_arch_convex, {* Convex *}
+. bfd_arch_m88k, {* Motorola 88xxx *}
+. bfd_arch_pyramid, {* Pyramid Technology *}
+. bfd_arch_h8300, {* Hitachi H8/300 *}
+.#define bfd_mach_h8300 1
+.#define bfd_mach_h8300h 2
+. bfd_arch_rs6000, {* IBM RS/6000 *}
+. bfd_arch_hppa, {* HP PA RISC *}
+. bfd_arch_z8k, {* Zilog Z8000 *}
+.#define bfd_mach_z8001 1
+.#define bfd_mach_z8002 2
+. bfd_arch_h8500, {* Hitachi H8/500 *}
+. bfd_arch_sh, {* Hitachi SH *}
+. bfd_arch_alpha, {* Dec Alpha *}
+. bfd_arch_last
+. };
+
+
+*/
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+
+/*
+
+SUBSECTION
+ bfd_arch_info
+
+DESCRIPTION
+ This structure contains information on architectures for use
+ within BFD.
+
+.
+.typedef struct bfd_arch_info
+.{
+. int bits_per_word;
+. int bits_per_address;
+. int bits_per_byte;
+. enum bfd_architecture arch;
+. long mach;
+. char *arch_name;
+. CONST char *printable_name;
+. unsigned int section_align_power;
+. {* true if this is the default machine for the architecture *}
+. boolean the_default;
+. CONST struct bfd_arch_info * (*compatible)
+. PARAMS ((CONST struct bfd_arch_info *a,
+. CONST struct bfd_arch_info *b));
+.
+. boolean (*scan) PARAMS ((CONST struct bfd_arch_info *, CONST char *));
+. {* How to disassemble an instruction, producing a printable
+. representation on a specified stdio stream. This isn't
+. defined for most processors at present, because of the size
+. of the additional tables it would drag in, and because gdb
+. wants to use a different interface. *}
+. unsigned int (*disassemble) PARAMS ((bfd_vma addr, CONST char *data,
+. PTR stream));
+.
+. struct bfd_arch_info *next;
+.} bfd_arch_info_type;
+*/
+
+bfd_arch_info_type *bfd_arch_info_list;
+
+
+/*
+FUNCTION
+ bfd_printable_name
+
+SYNOPSIS
+ CONST char *bfd_printable_name(bfd *abfd);
+
+DESCRIPTION
+ Return a printable string representing the architecture and machine
+ from the pointer to the arch info structure
+
+*/
+
+CONST char *
+DEFUN(bfd_printable_name, (abfd),
+ bfd *abfd)
+{
+ return abfd->arch_info->printable_name;
+}
+
+
+
+/*
+FUNCTION
+ bfd_scan_arch
+
+SYNOPSIS
+ bfd_arch_info_type *bfd_scan_arch(CONST char *);
+
+DESCRIPTION
+ This routine is provided with a string and tries to work out
+ if bfd supports any cpu which could be described with the name
+ provided. The routine returns a pointer to an arch_info
+ structure if a machine is found, otherwise NULL.
+
+*/
+
+bfd_arch_info_type *
+DEFUN(bfd_scan_arch,(string),
+ CONST char *string)
+{
+ struct bfd_arch_info *ap;
+
+ /* Look through all the installed architectures */
+ for (ap = bfd_arch_info_list;
+ ap != (bfd_arch_info_type *)NULL;
+ ap = ap->next) {
+
+ if (ap->scan(ap, string))
+ return ap;
+ }
+ return (bfd_arch_info_type *)NULL;
+}
+
+
+
+/*
+FUNCTION
+ bfd_arch_get_compatible
+
+SYNOPSIS
+ CONST bfd_arch_info_type *bfd_arch_get_compatible(
+ CONST bfd *abfd,
+ CONST bfd *bbfd);
+
+DESCRIPTION
+ This routine is used to determine whether two BFDs'
+ architectures and achine types are compatible. It calculates
+ the lowest common denominator between the two architectures
+ and machine types implied by the BFDs and returns a pointer to
+ an arch_info structure describing the compatible machine.
+*/
+
+CONST bfd_arch_info_type *
+DEFUN(bfd_arch_get_compatible,(abfd, bbfd),
+CONST bfd *abfd AND
+CONST bfd *bbfd)
+
+{
+ return abfd->arch_info->compatible(abfd->arch_info,bbfd->arch_info);
+}
+
+
+/*
+INTERNAL_DEFINITION
+ bfd_default_arch_struct
+
+DESCRIPTION
+ The <<bfd_default_arch_struct>> is an item of
+ <<bfd_arch_info_type>> which has been initialized to a fairly
+ generic state. A BFD starts life by pointing to this
+ structure, until the correct back end has determined the real
+ architecture of the file.
+
+.extern bfd_arch_info_type bfd_default_arch_struct;
+
+*/
+
+bfd_arch_info_type bfd_default_arch_struct =
+{
+ 32,32,8,bfd_arch_unknown,0,"unknown","unknown",2,true,
+ bfd_default_compatible,
+ bfd_default_scan,
+ 0,
+};
+
+/*
+FUNCTION
+ bfd_set_arch_info
+
+SYNOPSIS
+ void bfd_set_arch_info(bfd *, bfd_arch_info_type *);
+
+*/
+
+void DEFUN(bfd_set_arch_info,(abfd, arg),
+bfd *abfd AND
+bfd_arch_info_type *arg)
+{
+ abfd->arch_info = arg;
+}
+
+/*
+INTERNAL_FUNCTION
+ bfd_default_set_arch_mach
+
+SYNOPSIS
+ boolean bfd_default_set_arch_mach(bfd *abfd,
+ enum bfd_architecture arch,
+ unsigned long mach);
+
+DESCRIPTION
+ Set the architecture and machine type in a bfd. This finds the
+ correct pointer to structure and inserts it into the arch_info
+ pointer.
+*/
+
+boolean DEFUN(bfd_default_set_arch_mach,(abfd, arch, mach),
+ bfd *abfd AND
+ enum bfd_architecture arch AND
+ unsigned long mach)
+{
+ static struct bfd_arch_info *old_ptr = &bfd_default_arch_struct;
+ boolean found = false;
+ /* run through the table to find the one we want, we keep a little
+ cache to speed things up */
+ if (old_ptr == 0 || arch != old_ptr->arch || mach != old_ptr->mach) {
+ bfd_arch_info_type *ptr;
+ old_ptr = (bfd_arch_info_type *)NULL;
+ for (ptr = bfd_arch_info_list;
+ ptr != (bfd_arch_info_type *)NULL;
+ ptr= ptr->next) {
+ if (ptr->arch == arch &&
+ ((ptr->mach == mach) || (ptr->the_default && mach == 0))) {
+ old_ptr = ptr;
+ found = true;
+ break;
+ }
+ }
+ if (found==false) {
+ /*looked for it and it wasn't there, so put in the default */
+ old_ptr = &bfd_default_arch_struct;
+ bfd_error = bad_value;
+ }
+ }
+ else {
+ /* it was in the cache */
+ found = true;
+ }
+
+ abfd->arch_info = old_ptr;
+
+ return found;
+}
+
+
+
+
+
+/*
+FUNCTION
+ bfd_get_arch
+
+SYNOPSIS
+ enum bfd_architecture bfd_get_arch(bfd *abfd);
+
+DESCRIPTION
+ Returns the enumerated type which describes the supplied bfd's
+ architecture
+
+*/
+
+enum bfd_architecture DEFUN(bfd_get_arch, (abfd), bfd *abfd)
+{
+ return abfd->arch_info->arch;
+}
+
+/*
+FUNCTION
+ bfd_get_mach
+
+SYNOPSIS
+ unsigned long bfd_get_mach(bfd *abfd);
+
+DESCRIPTION
+ Returns the long type which describes the supplied bfd's
+ machine
+*/
+
+unsigned long
+DEFUN(bfd_get_mach, (abfd), bfd *abfd)
+{
+ return abfd->arch_info->mach;
+}
+
+/*
+FUNCTION
+ bfd_arch_bits_per_byte
+
+SYNOPSIS
+ unsigned int bfd_arch_bits_per_byte(bfd *abfd);
+
+DESCRIPTION
+ Returns the number of bits in one of the architectures bytes
+
+*/
+
+unsigned int DEFUN(bfd_arch_bits_per_byte, (abfd), bfd *abfd)
+ {
+ return abfd->arch_info->bits_per_byte;
+ }
+
+/*
+FUNCTION
+ bfd_arch_bits_per_address
+
+SYNOPSIS
+ unsigned int bfd_arch_bits_per_address(bfd *abfd);
+
+DESCRIPTION
+ Returns the number of bits in one of the architectures addresses
+*/
+
+unsigned int DEFUN(bfd_arch_bits_per_address, (abfd), bfd *abfd)
+ {
+ return abfd->arch_info->bits_per_address;
+ }
+
+
+extern void bfd_a29k_arch PARAMS ((void));
+extern void bfd_alpha_arch PARAMS ((void));
+extern void bfd_h8300_arch PARAMS ((void));
+extern void bfd_h8500_arch PARAMS ((void));
+extern void bfd_hppa_arch PARAMS ((void));
+extern void bfd_i386_arch PARAMS ((void));
+extern void bfd_i960_arch PARAMS ((void));
+extern void bfd_m68k_arch PARAMS ((void));
+extern void bfd_m88k_arch PARAMS ((void));
+extern void bfd_mips_arch PARAMS ((void));
+extern void bfd_rs6000_arch PARAMS ((void));
+extern void bfd_sh_arch PARAMS ((void));
+extern void bfd_sparc_arch PARAMS ((void));
+extern void bfd_vax_arch PARAMS ((void));
+extern void bfd_we32k_arch PARAMS ((void));
+extern void bfd_z8k_arch PARAMS ((void));
+
+static void (*archures_init_table[]) PARAMS ((void)) =
+{
+#ifdef SELECT_ARCHITECTURES
+ SELECT_ARCHITECTURES,
+#else
+ bfd_a29k_arch,
+ bfd_alpha_arch,
+ bfd_h8300_arch,
+ bfd_h8500_arch,
+ bfd_hppa_arch,
+ bfd_i386_arch,
+ bfd_i960_arch,
+ bfd_m68k_arch,
+ bfd_m88k_arch,
+ bfd_mips_arch,
+ bfd_rs6000_arch,
+ bfd_sh_arch,
+ bfd_sparc_arch,
+ bfd_vax_arch,
+ bfd_we32k_arch,
+ bfd_z8k_arch,
+#endif
+ 0
+ };
+
+
+
+/*
+INTERNAL_FUNCTION
+ bfd_arch_init
+
+SYNOPSIS
+ void bfd_arch_init(void);
+
+DESCRIPTION
+ This routine initializes the architecture dispatch table by
+ calling all installed architecture packages and getting them
+ to poke around.
+*/
+
+void
+DEFUN_VOID(bfd_arch_init)
+{
+ void (**ptable) PARAMS ((void));
+ for (ptable = archures_init_table;
+ *ptable ;
+ ptable++)
+ {
+ (*ptable)();
+ }
+}
+
+
+/*
+INTERNAL_FUNCTION
+ bfd_arch_linkin
+
+SYNOPSIS
+ void bfd_arch_linkin(bfd_arch_info_type *);
+
+DESCRIPTION
+ Link the provided arch info structure into the list
+*/
+
+void DEFUN(bfd_arch_linkin,(ptr),
+ bfd_arch_info_type *ptr)
+{
+ ptr->next = bfd_arch_info_list;
+ bfd_arch_info_list = ptr;
+}
+
+
+/*
+INTERNAL_FUNCTION
+ bfd_default_compatible
+
+SYNOPSIS
+ CONST bfd_arch_info_type *bfd_default_compatible
+ (CONST bfd_arch_info_type *a,
+ CONST bfd_arch_info_type *b);
+
+DESCRIPTION
+ The default function for testing for compatibility.
+*/
+
+CONST bfd_arch_info_type *
+DEFUN(bfd_default_compatible,(a,b),
+ CONST bfd_arch_info_type *a AND
+ CONST bfd_arch_info_type *b)
+{
+ if(a->arch != b->arch) return NULL;
+
+ if (a->mach > b->mach) {
+ return a;
+ }
+ if (b->mach > a->mach) {
+ return b;
+ }
+ return a;
+}
+
+
+/*
+INTERNAL_FUNCTION
+ bfd_default_scan
+
+SYNOPSIS
+ boolean bfd_default_scan(CONST struct bfd_arch_info *, CONST char *);
+
+DESCRIPTION
+ The default function for working out whether this is an
+ architecture hit and a machine hit.
+*/
+
+boolean
+DEFUN(bfd_default_scan,(info, string),
+CONST struct bfd_arch_info *info AND
+CONST char *string)
+{
+ CONST char *ptr_src;
+ CONST char *ptr_tst;
+ unsigned long number;
+ enum bfd_architecture arch;
+ /* First test for an exact match */
+ if (strcmp(string, info->printable_name) == 0) return true;
+
+ /* See how much of the supplied string matches with the
+ architecture, eg the string m68k:68020 would match the 68k entry
+ up to the :, then we get left with the machine number */
+
+ for (ptr_src = string,
+ ptr_tst = info->arch_name;
+ *ptr_src && *ptr_tst;
+ ptr_src++,
+ ptr_tst++)
+ {
+ if (*ptr_src != *ptr_tst) break;
+ }
+
+ /* Chewed up as much of the architecture as will match, skip any
+ colons */
+ if (*ptr_src == ':') ptr_src++;
+
+ if (*ptr_src == 0) {
+ /* nothing more, then only keep this one if it is the default
+ machine for this architecture */
+ return info->the_default;
+ }
+ number = 0;
+ while (isdigit(*ptr_src)) {
+ number = number * 10 + *ptr_src - '0';
+ ptr_src++;
+ }
+
+ switch (number)
+ {
+ case 300:
+ arch = bfd_arch_h8300;
+ break;
+
+ case 500:
+ arch = bfd_arch_h8500;
+ break;
+
+ case 68010:
+ case 68020:
+ case 68030:
+ case 68040:
+ case 68332:
+ case 68050:
+ case 68000:
+ arch = bfd_arch_m68k;
+ break;
+ case 386:
+ case 80386:
+ case 486:
+ case 80486:
+ arch = bfd_arch_i386;
+ break;
+ case 29000:
+ arch = bfd_arch_a29k;
+ break;
+
+ case 8000:
+ arch = bfd_arch_z8k;
+ break;
+
+ case 32000:
+ arch = bfd_arch_we32k;
+ break;
+
+ case 860:
+ case 80860:
+ arch = bfd_arch_i860;
+ break;
+ case 960:
+ case 80960:
+ arch = bfd_arch_i960;
+ break;
+
+ case 2000:
+ case 3000:
+ case 4000:
+ case 4400:
+ arch = bfd_arch_mips;
+ break;
+
+ case 6000:
+ arch = bfd_arch_rs6000;
+ break;
+
+ default:
+ return false;
+ }
+ if (arch != info->arch)
+ return false;
+
+ if (number != info->mach)
+ return false;
+
+ return true;
+}
+
+
+
+
+/*
+FUNCTION
+ bfd_get_arch_info
+
+
+SYNOPSIS
+ bfd_arch_info_type * bfd_get_arch_info(bfd *);
+
+*/
+
+bfd_arch_info_type *
+DEFUN(bfd_get_arch_info,(abfd),
+bfd *abfd)
+{
+ return abfd->arch_info;
+}
+
+
+/*
+FUNCTION
+ bfd_lookup_arch
+
+SYNOPSIS
+ bfd_arch_info_type *bfd_lookup_arch
+ (enum bfd_architecture
+ arch,
+ long machine);
+
+DESCRIPTION
+ Look for the architecure info struct which matches the
+ arguments given. A machine of 0 will match the
+ machine/architecture structure which marks itself as the
+ default.
+*/
+
+bfd_arch_info_type *
+DEFUN(bfd_lookup_arch,(arch, machine),
+enum bfd_architecture arch AND
+long machine)
+{
+ bfd_arch_info_type *ap;
+ bfd_check_init();
+ for (ap = bfd_arch_info_list;
+ ap != (bfd_arch_info_type *)NULL;
+ ap = ap->next) {
+ if (ap->arch == arch &&
+ ((ap->mach == machine)
+ || (ap->the_default && machine == 0))) {
+ return ap;
+ }
+ }
+ return (bfd_arch_info_type *)NULL;
+}
+
+
+
+/*
+FUNCTION
+ bfd_printable_arch_mach
+
+SYNOPSIS
+ CONST char * bfd_printable_arch_mach
+ (enum bfd_architecture arch, unsigned long machine);
+
+DESCRIPTION
+ Return a printable string representing the architecture and
+ machine type.
+
+ NB. The use of this routine is depreciated.
+*/
+
+CONST char *
+DEFUN(bfd_printable_arch_mach,(arch, machine),
+ enum bfd_architecture arch AND
+ unsigned long machine)
+{
+ bfd_arch_info_type *ap = bfd_lookup_arch(arch, machine);
+ if(ap) return ap->printable_name;
+ return "UNKNOWN!";
+}
diff --git a/gnu/usr.bin/gdb/bfd/bfd.c b/gnu/usr.bin/gdb/bfd/bfd.c
new file mode 100644
index 0000000..a994cd1
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/bfd.c
@@ -0,0 +1,733 @@
+/* Generic BFD library interface and support routines.
+ Copyright (C) 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+ Written by Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+SECTION
+ <<typedef bfd>>
+
+ A BFD is has type <<bfd>>; objects of this type are the
+ cornerstone of any application using <<libbfd>>. References
+ though the BFD and to data in the BFD give the entire BFD
+ functionality.
+
+ Here is the struct used to define the type <<bfd>>. This
+ contains the major data about the file, and contains pointers
+ to the rest of the data.
+
+CODE_FRAGMENT
+.
+.struct _bfd
+.{
+. {* The filename the application opened the BFD with. *}
+. CONST char *filename;
+.
+. {* A pointer to the target jump table. *}
+. struct bfd_target *xvec;
+.
+. {* To avoid dragging too many header files into every file that
+. includes `<<bfd.h>>', IOSTREAM has been declared as a "char
+. *", and MTIME as a "long". Their correct types, to which they
+. are cast when used, are "FILE *" and "time_t". The iostream
+. is the result of an fopen on the filename. *}
+. char *iostream;
+.
+. {* Is the file being cached *}
+.
+. boolean cacheable;
+.
+. {* Marks whether there was a default target specified when the
+. BFD was opened. This is used to select what matching algorithm
+. to use to chose the back end. *}
+.
+. boolean target_defaulted;
+.
+. {* The caching routines use these to maintain a
+. least-recently-used list of BFDs *}
+.
+. struct _bfd *lru_prev, *lru_next;
+.
+. {* When a file is closed by the caching routines, BFD retains
+. state information on the file here:
+. *}
+.
+. file_ptr where;
+.
+. {* and here:*}
+.
+. boolean opened_once;
+.
+. {* Set if we have a locally maintained mtime value, rather than
+. getting it from the file each time: *}
+.
+. boolean mtime_set;
+.
+. {* File modified time, if mtime_set is true: *}
+.
+. long mtime;
+.
+. {* Reserved for an unimplemented file locking extension.*}
+.
+. int ifd;
+.
+. {* The format which belongs to the BFD.*}
+.
+. bfd_format format;
+.
+. {* The direction the BFD was opened with*}
+.
+. enum bfd_direction {no_direction = 0,
+. read_direction = 1,
+. write_direction = 2,
+. both_direction = 3} direction;
+.
+. {* Format_specific flags*}
+.
+. flagword flags;
+.
+. {* Currently my_archive is tested before adding origin to
+. anything. I believe that this can become always an add of
+. origin, with origin set to 0 for non archive files. *}
+.
+. file_ptr origin;
+.
+. {* Remember when output has begun, to stop strange things
+. happening. *}
+. boolean output_has_begun;
+.
+. {* Pointer to linked list of sections*}
+. struct sec *sections;
+.
+. {* The number of sections *}
+. unsigned int section_count;
+.
+. {* Stuff only useful for object files:
+. The start address. *}
+. bfd_vma start_address;
+.
+. {* Used for input and output*}
+. unsigned int symcount;
+.
+. {* Symbol table for output BFD*}
+. struct symbol_cache_entry **outsymbols;
+.
+. {* Pointer to structure which contains architecture information*}
+. struct bfd_arch_info *arch_info;
+.
+. {* Stuff only useful for archives:*}
+. PTR arelt_data;
+. struct _bfd *my_archive;
+. struct _bfd *next;
+. struct _bfd *archive_head;
+. boolean has_armap;
+.
+. {* Used by the back end to hold private data. *}
+.
+. union
+. {
+. struct aout_data_struct *aout_data;
+. struct artdata *aout_ar_data;
+. struct _oasys_data *oasys_obj_data;
+. struct _oasys_ar_data *oasys_ar_data;
+. struct coff_tdata *coff_obj_data;
+. struct ecoff_tdata *ecoff_obj_data;
+. struct ieee_data_struct *ieee_data;
+. struct ieee_ar_data_struct *ieee_ar_data;
+. struct srec_data_struct *srec_data;
+. struct tekhex_data_struct *tekhex_data;
+. struct elf_obj_tdata *elf_obj_data;
+. struct nlm_obj_tdata *nlm_obj_data;
+. struct bout_data_struct *bout_data;
+. struct sun_core_struct *sun_core_data;
+. struct trad_core_struct *trad_core_data;
+. struct hppa_data_struct *hppa_data;
+. struct hpux_core_struct *hpux_core_data;
+. struct sgi_core_struct *sgi_core_data;
+. struct lynx_core_struct *lynx_core_data;
+. struct osf_core_struct *osf_core_data;
+. PTR any;
+. } tdata;
+.
+. {* Used by the application to hold private data*}
+. PTR usrdata;
+.
+. {* Where all the allocated stuff under this BFD goes *}
+. struct obstack memory;
+.
+. {* Is this really needed in addition to usrdata? *}
+. asymbol **ld_symbols;
+.};
+.
+*/
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "coff/internal.h"
+#include "coff/sym.h"
+#include "libcoff.h"
+#include "libecoff.h"
+#undef obj_symbols
+#include "libelf.h"
+
+#undef strerror
+extern char *strerror();
+
+/** Error handling
+ o - Most functions return nonzero on success (check doc for
+ precise semantics); 0 or NULL on error.
+ o - Internal errors are documented by the value of bfd_error.
+ If that is system_call_error then check errno.
+ o - The easiest way to report this to the user is to use bfd_perror.
+*/
+
+bfd_ec bfd_error = no_error;
+
+CONST char *CONST bfd_errmsgs[] = {
+ "No error",
+ "System call error",
+ "Invalid target",
+ "File in wrong format",
+ "Invalid operation",
+ "Memory exhausted",
+ "No symbols",
+ "No relocation info",
+ "No more archived files",
+ "Malformed archive",
+ "Symbol not found",
+ "File format not recognized",
+ "File format is ambiguous",
+ "Section has no contents",
+ "Nonrepresentable section on output",
+ "Symbol needs debug section which does not exist",
+ "Bad value",
+ "File truncated",
+ "#<Invalid error code>"
+ };
+
+static
+void
+DEFUN(bfd_nonrepresentable_section,(abfd, name),
+ CONST bfd * CONST abfd AND
+ CONST char * CONST name)
+{
+ fprintf(stderr,
+ "bfd error writing file %s, format %s can't represent section %s\n",
+ abfd->filename,
+ abfd->xvec->name,
+ name);
+ exit(1);
+}
+
+/*ARGSUSED*/
+static
+void
+DEFUN(bfd_undefined_symbol,(relent, seclet),
+ CONST arelent *relent AND
+ CONST struct bfd_seclet *seclet)
+{
+ asymbol *symbol = *(relent->sym_ptr_ptr);
+ fprintf(stderr, "bfd error relocating, symbol %s is undefined\n",
+ symbol->name);
+ exit(1);
+}
+/*ARGSUSED*/
+static
+void
+DEFUN(bfd_reloc_value_truncated,(relent, seclet),
+ CONST arelent *relent AND
+ struct bfd_seclet *seclet)
+{
+ fprintf(stderr, "bfd error relocating, value truncated\n");
+ exit(1);
+}
+/*ARGSUSED*/
+static
+void
+DEFUN(bfd_reloc_is_dangerous,(relent, seclet),
+ CONST arelent *relent AND
+ CONST struct bfd_seclet *seclet)
+{
+ fprintf(stderr, "bfd error relocating, dangerous\n");
+ exit(1);
+}
+
+bfd_error_vector_type bfd_error_vector =
+ {
+ bfd_nonrepresentable_section ,
+ bfd_undefined_symbol,
+ bfd_reloc_value_truncated,
+ bfd_reloc_is_dangerous,
+ };
+
+
+CONST char *
+bfd_errmsg (error_tag)
+ bfd_ec error_tag;
+{
+#ifndef errno
+ extern int errno;
+#endif
+ if (error_tag == system_call_error)
+ return strerror (errno);
+
+ if ((((int)error_tag <(int) no_error) ||
+ ((int)error_tag > (int)invalid_error_code)))
+ error_tag = invalid_error_code;/* sanity check */
+
+ return bfd_errmsgs [(int)error_tag];
+}
+
+void
+DEFUN (bfd_default_error_trap, (error_tag),
+ bfd_ec error_tag)
+{
+ fprintf(stderr, "bfd assert fail (%s)\n", bfd_errmsg(error_tag));
+}
+
+void (*bfd_error_trap) PARAMS ((bfd_ec)) = bfd_default_error_trap;
+void (*bfd_error_nonrepresentabltrap) PARAMS ((bfd_ec)) = bfd_default_error_trap;
+
+void
+DEFUN(bfd_perror,(message),
+ CONST char *message)
+{
+ if (bfd_error == system_call_error)
+ perror((char *)message); /* must be system error then... */
+ else {
+ if (message == NULL || *message == '\0')
+ fprintf (stderr, "%s\n", bfd_errmsg (bfd_error));
+ else
+ fprintf (stderr, "%s: %s\n", message, bfd_errmsg (bfd_error));
+ }
+}
+
+
+/** Symbols */
+
+
+/*
+FUNCTION
+ bfd_get_reloc_upper_bound
+
+SYNOPSIS
+ unsigned int bfd_get_reloc_upper_bound(bfd *abfd, asection *sect);
+
+DESCRIPTION
+ This function return the number of bytes required to store the
+ relocation information associated with section <<sect>>
+ attached to bfd <<abfd>>
+
+*/
+
+
+unsigned int
+DEFUN(bfd_get_reloc_upper_bound,(abfd, asect),
+ bfd *abfd AND
+ sec_ptr asect)
+{
+ if (abfd->format != bfd_object) {
+ bfd_error = invalid_operation;
+ return 0;
+ }
+
+ return BFD_SEND (abfd, _get_reloc_upper_bound, (abfd, asect));
+}
+
+/*
+FUNCTION
+ bfd_canonicalize_reloc
+
+SYNOPSIS
+ unsigned int bfd_canonicalize_reloc
+ (bfd *abfd,
+ asection *sec,
+ arelent **loc,
+ asymbol **syms);
+
+DESCRIPTION
+ This function calls the back end associated with the open
+ <<abfd>> and translates the external form of the relocation
+ information attached to <<sec>> into the internal canonical
+ form. The table is placed into memory at <<loc>>, which has
+ been preallocated, usually by a call to
+ <<bfd_get_reloc_upper_bound>>.
+
+ The <<syms>> table is also needed for horrible internal magic
+ reasons.
+
+
+*/
+unsigned int
+DEFUN(bfd_canonicalize_reloc,(abfd, asect, location, symbols),
+ bfd *abfd AND
+ sec_ptr asect AND
+ arelent **location AND
+ asymbol **symbols)
+{
+ if (abfd->format != bfd_object) {
+ bfd_error = invalid_operation;
+ return 0;
+ }
+ return BFD_SEND (abfd, _bfd_canonicalize_reloc,
+ (abfd, asect, location, symbols));
+ }
+
+
+/*
+FUNCTION
+ bfd_set_file_flags
+
+SYNOPSIS
+ boolean bfd_set_file_flags(bfd *abfd, flagword flags);
+
+DESCRIPTION
+ This function attempts to set the flag word in the referenced
+ BFD structure to the value supplied.
+
+ Possible errors are:
+ o wrong_format - The target bfd was not of object format.
+ o invalid_operation - The target bfd was open for reading.
+ o invalid_operation -
+ The flag word contained a bit which was not applicable to the
+ type of file. eg, an attempt was made to set the D_PAGED bit
+ on a bfd format which does not support demand paging
+
+*/
+
+boolean
+bfd_set_file_flags (abfd, flags)
+ bfd *abfd;
+ flagword flags;
+{
+ if (abfd->format != bfd_object) {
+ bfd_error = wrong_format;
+ return false;
+ }
+
+ if (bfd_read_p (abfd)) {
+ bfd_error = invalid_operation;
+ return false;
+ }
+
+ bfd_get_file_flags (abfd) = flags;
+ if ((flags & bfd_applicable_file_flags (abfd)) != flags) {
+ bfd_error = invalid_operation;
+ return false;
+ }
+
+return true;
+}
+
+/*
+FUNCTION
+ bfd_set_reloc
+
+SYNOPSIS
+ void bfd_set_reloc
+ (bfd *abfd, asection *sec, arelent **rel, unsigned int count)
+
+DESCRIPTION
+ This function sets the relocation pointer and count within a
+ section to the supplied values.
+
+*/
+/*ARGSUSED*/
+void
+bfd_set_reloc (ignore_abfd, asect, location, count)
+ bfd *ignore_abfd;
+ sec_ptr asect;
+ arelent **location;
+ unsigned int count;
+{
+ asect->orelocation = location;
+ asect->reloc_count = count;
+}
+
+void
+bfd_assert(file, line)
+char *file;
+int line;
+{
+ fprintf(stderr, "bfd assertion fail %s:%d\n",file,line);
+}
+
+
+/*
+FUNCTION
+ bfd_set_start_address
+
+DESCRIPTION
+ Marks the entry point of an output BFD.
+
+RETURNS
+ Returns <<true>> on success, <<false>> otherwise.
+
+SYNOPSIS
+ boolean bfd_set_start_address(bfd *, bfd_vma);
+*/
+
+boolean
+bfd_set_start_address(abfd, vma)
+bfd *abfd;
+bfd_vma vma;
+{
+ abfd->start_address = vma;
+ return true;
+}
+
+
+/*
+FUNCTION
+ The bfd_get_mtime function
+
+SYNOPSIS
+ long bfd_get_mtime(bfd *);
+
+DESCRIPTION
+ Return file modification time (as read from file system, or
+ from archive header for archive members).
+
+*/
+
+long
+bfd_get_mtime (abfd)
+ bfd *abfd;
+{
+ FILE *fp;
+ struct stat buf;
+
+ if (abfd->mtime_set)
+ return abfd->mtime;
+
+ fp = bfd_cache_lookup (abfd);
+ if (0 != fstat (fileno (fp), &buf))
+ return 0;
+
+ abfd->mtime = buf.st_mtime; /* Save value in case anyone wants it */
+ return buf.st_mtime;
+}
+
+/*
+FUNCTION
+ The bfd_get_size function
+
+SYNOPSIS
+ long bfd_get_size(bfd *);
+
+DESCRIPTION
+ Return file size (as read from file system) for the file
+ associated with a bfd.
+
+ Note that the initial motivation for, and use of, this routine is not
+ so we can get the exact size of the object the bfd applies to, since
+ that might not be generally possible (archive members for example?).
+ Although it would be ideal if someone could eventually modify
+ it so that such results were guaranteed.
+
+ Instead, we want to ask questions like "is this NNN byte sized
+ object I'm about to try read from file offset YYY reasonable?"
+ As as example of where we might want to do this, some object formats
+ use string tables for which the first sizeof(long) bytes of the table
+ contain the size of the table itself, including the size bytes.
+ If an application tries to read what it thinks is one of these
+ string tables, without some way to validate the size, and for
+ some reason the size is wrong (byte swapping error, wrong location
+ for the string table, etc), the only clue is likely to be a read
+ error when it tries to read the table, or a "virtual memory
+ exhausted" error when it tries to allocated 15 bazillon bytes
+ of space for the 15 bazillon byte table it is about to read.
+ This function at least allows us to answer the quesion, "is the
+ size reasonable?".
+*/
+
+long
+bfd_get_size (abfd)
+ bfd *abfd;
+{
+ FILE *fp;
+ struct stat buf;
+
+ fp = bfd_cache_lookup (abfd);
+ if (0 != fstat (fileno (fp), &buf))
+ return 0;
+
+ return buf.st_size;
+}
+
+/*
+FUNCTION
+ The bfd_get_gp_size function
+
+SYNOPSIS
+ int bfd_get_gp_size(bfd *);
+
+DESCRIPTION
+ Get the maximum size of objects to be optimized using the GP
+ register under MIPS ECOFF. This is typically set by the -G
+ argument to the compiler, assembler or linker.
+*/
+
+int
+bfd_get_gp_size (abfd)
+ bfd *abfd;
+{
+ if (abfd->xvec->flavour == bfd_target_ecoff_flavour)
+ return ecoff_data (abfd)->gp_size;
+ return 0;
+}
+
+/*
+FUNCTION
+ The bfd_set_gp_size function
+
+SYNOPSIS
+ void bfd_set_gp_size(bfd *, int);
+
+DESCRIPTION
+ Set the maximum size of objects to be optimized using the GP
+ register under ECOFF or MIPS ELF. This is typically set by
+ the -G argument to the compiler, assembler or linker.
+*/
+
+void
+bfd_set_gp_size (abfd, i)
+ bfd *abfd;
+ int i;
+{
+ if (abfd->xvec->flavour == bfd_target_ecoff_flavour)
+ ecoff_data (abfd)->gp_size = i;
+ else if (abfd->xvec->flavour == bfd_target_elf_flavour)
+ elf_gp_size (abfd) = i;
+}
+
+/*
+FUNCTION
+ bfd_scan_vma
+
+DESCRIPTION
+ Converts, like strtoul, a numerical expression as a
+ string into a bfd_vma integer, and returns that integer.
+ (Though without as many bells and whistles as strtoul.)
+ The expression is assumed to be unsigned (i.e. positive).
+ If given a base, it is used as the base for conversion.
+ A base of 0 causes the function to interpret the string
+ in hex if a leading "0x" or "0X" is found, otherwise
+ in octal if a leading zero is found, otherwise in decimal.
+
+ Overflow is not detected.
+
+SYNOPSIS
+ bfd_vma bfd_scan_vma(CONST char *string, CONST char **end, int base);
+*/
+
+bfd_vma
+DEFUN(bfd_scan_vma,(string, end, base),
+ CONST char *string AND
+ CONST char **end AND
+ int base)
+{
+ bfd_vma value;
+ int digit;
+
+ /* Let the host do it if possible. */
+ if (sizeof(bfd_vma) <= sizeof(unsigned long))
+ return (bfd_vma) strtoul (string, 0, base);
+
+ /* A negative base makes no sense, and we only need to go as high as hex. */
+ if ((base < 0) || (base > 16))
+ return (bfd_vma) 0;
+
+ if (base == 0)
+ {
+ if (string[0] == '0')
+ {
+ if ((string[1] == 'x') || (string[1] == 'X'))
+ base = 16;
+ /* XXX should we also allow "0b" or "0B" to set base to 2? */
+ else
+ base = 8;
+ }
+ else
+ base = 10;
+ }
+ if ((base == 16) &&
+ (string[0] == '0') && ((string[1] == 'x') || (string[1] == 'X')))
+ string += 2;
+ /* XXX should we also skip over "0b" or "0B" if base is 2? */
+
+/* Speed could be improved with a table like hex_value[] in gas. */
+#define HEX_VALUE(c) \
+ (isxdigit(c) ? \
+ (isdigit(c) ? \
+ (c - '0') : \
+ (10 + c - (islower(c) ? 'a' : 'A'))) : \
+ 42)
+
+ for (value = 0; (digit = HEX_VALUE(*string)) < base; string++)
+ {
+ value = value * base + digit;
+ }
+
+ if (end)
+ *end = string;
+
+ return value;
+}
+
+/*
+FUNCTION
+ stuff
+
+DESCRIPTION
+ stuff which should be documented
+
+.#define bfd_sizeof_headers(abfd, reloc) \
+. BFD_SEND (abfd, _bfd_sizeof_headers, (abfd, reloc))
+.
+.#define bfd_find_nearest_line(abfd, sec, syms, off, file, func, line) \
+. BFD_SEND (abfd, _bfd_find_nearest_line, (abfd, sec, syms, off, file, func, line))
+.
+. {* Do these three do anything useful at all, for any back end? *}
+.#define bfd_debug_info_start(abfd) \
+. BFD_SEND (abfd, _bfd_debug_info_start, (abfd))
+.
+.#define bfd_debug_info_end(abfd) \
+. BFD_SEND (abfd, _bfd_debug_info_end, (abfd))
+.
+.#define bfd_debug_info_accumulate(abfd, section) \
+. BFD_SEND (abfd, _bfd_debug_info_accumulate, (abfd, section))
+.
+.
+.#define bfd_stat_arch_elt(abfd, stat) \
+. BFD_SEND (abfd, _bfd_stat_arch_elt,(abfd, stat))
+.
+.#define bfd_set_arch_mach(abfd, arch, mach)\
+. BFD_SEND ( abfd, _bfd_set_arch_mach, (abfd, arch, mach))
+.
+.#define bfd_get_relocated_section_contents(abfd, seclet, data, relocateable) \
+. BFD_SEND (abfd, _bfd_get_relocated_section_contents, (abfd, seclet, data, relocateable))
+.
+.#define bfd_relax_section(abfd, section, symbols) \
+. BFD_SEND (abfd, _bfd_relax_section, (abfd, section, symbols))
+.
+.#define bfd_seclet_link(abfd, data, relocateable) \
+. BFD_SEND (abfd, _bfd_seclet_link, (abfd, data, relocateable))
+
+*/
diff --git a/gnu/usr.bin/gdb/bfd/bfd.h b/gnu/usr.bin/gdb/bfd/bfd.h
new file mode 100644
index 0000000..fbe0f0f
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/bfd.h
@@ -0,0 +1,1803 @@
+/* Main header file for the bfd library -- portable access to object files.
+ Copyright 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+ Contributed by Cygnus Support.
+
+** NOTE: bfd.h and bfd-in2.h are GENERATED files. Don't change them;
+** instead, change bfd-in.h or the other BFD source files processed to
+** generate these files.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* bfd.h -- The only header file required by users of the bfd library
+
+The bfd.h file is generated from bfd-in.h and various .c files; if you
+change it, your changes will probably be lost.
+
+All the prototypes and definitions following the comment "THE FOLLOWING
+IS EXTRACTED FROM THE SOURCE" are extracted from the source files for
+BFD. If you change it, someone oneday will extract it from the source
+again, and your changes will be lost. To save yourself from this bind,
+change the definitions in the source in the bfd directory. Type "make
+docs" and then "make headers" in that directory, and magically this file
+will change to reflect your changes.
+
+If you don't have the tools to perform the extraction, then you are
+safe from someone on your system trampling over your header files.
+You should still maintain the equivalence between the source and this
+file though; every change you make to the .c file should be reflected
+here. */
+
+#ifndef __BFD_H_SEEN__
+#define __BFD_H_SEEN__
+
+#include "ansidecl.h"
+#include "obstack.h"
+
+#define BFD_VERSION "2.2"
+
+#define BFD_ARCH_SIZE 32
+
+#if BFD_ARCH_SIZE >= 64
+#define BFD64
+#endif
+
+#ifndef INLINE
+#if __GNUC__ >= 2
+#define INLINE __inline__
+#else
+#define INLINE
+#endif
+#endif
+
+/* 64-bit type definition (if any) from bfd's sysdep.h goes here */
+
+
+/* forward declaration */
+typedef struct _bfd bfd;
+
+/* To squelch erroneous compiler warnings ("illegal pointer
+ combination") from the SVR3 compiler, we would like to typedef
+ boolean to int (it doesn't like functions which return boolean.
+ Making sure they are never implicitly declared to return int
+ doesn't seem to help). But this file is not configured based on
+ the host. */
+/* General rules: functions which are boolean return true on success
+ and false on failure (unless they're a predicate). -- bfd.doc */
+/* I'm sure this is going to break something and someone is going to
+ force me to change it. */
+/* typedef enum boolean {false, true} boolean; */
+/* Yup, SVR4 has a "typedef enum boolean" in <sys/types.h> -fnf */
+typedef enum bfd_boolean {false, true} boolean;
+
+/* A pointer to a position in a file. */
+/* FIXME: This should be using off_t from <sys/types.h>.
+ For now, try to avoid breaking stuff by not including <sys/types.h> here.
+ This will break on systems with 64-bit file offsets (e.g. 4.4BSD).
+ Probably the best long-term answer is to avoid using file_ptr AND off_t
+ in this header file, and to handle this in the BFD implementation
+ rather than in its interface. */
+/* typedef off_t file_ptr; */
+typedef long int file_ptr;
+
+/* Support for different sizes of target format ints and addresses. If the
+ host implements 64-bit values, it defines HOST_64_BIT to be the appropriate
+ type. Otherwise, this code will fall back on gcc's "long long" type if gcc
+ is being used. HOST_64_BIT must be defined in such a way as to be a valid
+ type name by itself or with "unsigned" prefixed. It should be a signed
+ type by itself.
+
+ If neither is the case, then compilation will fail if 64-bit targets are
+ requested. If you don't request any 64-bit targets, you should be safe. */
+
+#ifdef BFD64
+
+#if defined (__GNUC__) && !defined (HOST_64_BIT)
+#define HOST_64_BIT long long
+typedef HOST_64_BIT int64_type;
+typedef unsigned HOST_64_BIT uint64_type;
+#endif
+
+#if !defined (uint64_type) && defined (__GNUC__)
+#define uint64_type unsigned long long
+#define int64_type long long
+#define uint64_typeLOW(x) (unsigned long)(((x) & 0xffffffff))
+#define uint64_typeHIGH(x) (unsigned long)(((x) >> 32) & 0xffffffff)
+#endif
+
+typedef unsigned HOST_64_BIT bfd_vma;
+typedef HOST_64_BIT bfd_signed_vma;
+typedef unsigned HOST_64_BIT bfd_size_type;
+typedef unsigned HOST_64_BIT symvalue;
+#define fprintf_vma(s,x) \
+ fprintf(s,"%08x%08x", uint64_typeHIGH(x), uint64_typeLOW(x))
+#define sprintf_vma(s,x) \
+ sprintf(s,"%08x%08x", uint64_typeHIGH(x), uint64_typeLOW(x))
+#else /* not BFD64 */
+
+/* Represent a target address. Also used as a generic unsigned type
+ which is guaranteed to be big enough to hold any arithmetic types
+ we need to deal with. */
+typedef unsigned long bfd_vma;
+
+/* A generic signed type which is guaranteed to be big enough to hold any
+ arithmetic types we need to deal with. Can be assumed to be compatible
+ with bfd_vma in the same way that signed and unsigned ints are compatible
+ (as parameters, in assignment, etc). */
+typedef long bfd_signed_vma;
+
+typedef unsigned long symvalue;
+typedef unsigned long bfd_size_type;
+
+/* Print a bfd_vma x on stream s. */
+#define fprintf_vma(s,x) fprintf(s, "%08lx", x)
+#define sprintf_vma(s,x) sprintf(s, "%08lx", x)
+#endif /* not BFD64 */
+#define printf_vma(x) fprintf_vma(stdout,x)
+
+typedef unsigned int flagword; /* 32 bits of flags */
+
+/** File formats */
+
+typedef enum bfd_format {
+ bfd_unknown = 0, /* file format is unknown */
+ bfd_object, /* linker/assember/compiler output */
+ bfd_archive, /* object archive file */
+ bfd_core, /* core dump */
+ bfd_type_end} /* marks the end; don't use it! */
+ bfd_format;
+
+/* Object file flag values */
+#define NO_FLAGS 0x00
+#define HAS_RELOC 0x01
+#define EXEC_P 0x02
+#define HAS_LINENO 0x04
+#define HAS_DEBUG 0x08
+#define HAS_SYMS 0x10
+#define HAS_LOCALS 0x20
+#define DYNAMIC 0x40
+#define WP_TEXT 0x80
+#define D_PAGED 0x100
+#define BFD_IS_RELAXABLE 0x200
+
+/* symbols and relocation */
+
+typedef unsigned long symindex;
+
+#define BFD_NO_MORE_SYMBOLS ((symindex) ~0)
+
+typedef enum bfd_symclass {
+ bfd_symclass_unknown = 0,
+ bfd_symclass_fcommon, /* fortran common symbols */
+ bfd_symclass_global, /* global symbol, what a surprise */
+ bfd_symclass_debugger, /* some debugger symbol */
+ bfd_symclass_undefined /* none known */
+ } symclass;
+
+
+/* general purpose part of a symbol;
+ target specific parts will be found in libcoff.h, liba.out.h etc */
+
+
+#define bfd_get_section(x) ((x)->section)
+#define bfd_get_output_section(x) ((x)->section->output_section)
+#define bfd_set_section(x,y) ((x)->section) = (y)
+#define bfd_asymbol_base(x) ((x)->section->vma)
+#define bfd_asymbol_value(x) (bfd_asymbol_base(x) + (x)->value)
+#define bfd_asymbol_name(x) ((x)->name)
+/*Perhaps future: #define bfd_asymbol_bfd(x) ((x)->section->owner)*/
+#define bfd_asymbol_bfd(x) ((x)->the_bfd)
+#define bfd_asymbol_flavour(x) (bfd_asymbol_bfd(x)->xvec->flavour)
+
+/* This is a type pun with struct ranlib on purpose! */
+typedef struct carsym {
+ char *name;
+ file_ptr file_offset; /* look here to find the file */
+} carsym; /* to make these you call a carsymogen */
+
+
+/* Used in generating armaps. Perhaps just a forward definition would do? */
+struct orl { /* output ranlib */
+ char **name; /* symbol name */
+ file_ptr pos; /* bfd* or file position */
+ int namidx; /* index into string table */
+};
+
+
+
+/* Linenumber stuff */
+typedef struct lineno_cache_entry {
+ unsigned int line_number; /* Linenumber from start of function*/
+ union {
+ struct symbol_cache_entry *sym; /* Function name */
+ unsigned long offset; /* Offset into section */
+ } u;
+} alent;
+
+/* object and core file sections */
+
+
+#define align_power(addr, align) \
+ ( ((addr) + ((1<<(align))-1)) & (-1 << (align)))
+
+typedef struct sec *sec_ptr;
+
+#define bfd_get_section_name(bfd, ptr) ((ptr)->name + 0)
+#define bfd_get_section_vma(bfd, ptr) ((ptr)->vma + 0)
+#define bfd_get_section_alignment(bfd, ptr) ((ptr)->alignment_power + 0)
+#define bfd_section_name(bfd, ptr) ((ptr)->name)
+#define bfd_section_size(bfd, ptr) (bfd_get_section_size_before_reloc(ptr))
+#define bfd_section_vma(bfd, ptr) ((ptr)->vma)
+#define bfd_section_alignment(bfd, ptr) ((ptr)->alignment_power)
+#define bfd_get_section_flags(bfd, ptr) ((ptr)->flags + 0)
+#define bfd_get_section_userdata(bfd, ptr) ((ptr)->userdata)
+
+#define bfd_is_com_section(ptr) (((ptr)->flags & SEC_IS_COMMON) != 0)
+
+#define bfd_set_section_vma(bfd, ptr, val) (((ptr)->vma = (ptr)->lma= (val)), ((ptr)->user_set_vma = true), true)
+#define bfd_set_section_alignment(bfd, ptr, val) (((ptr)->alignment_power = (val)),true)
+#define bfd_set_section_userdata(bfd, ptr, val) (((ptr)->userdata = (val)),true)
+
+typedef struct stat stat_type;
+
+/** Error handling */
+
+typedef enum bfd_error {
+ no_error = 0, system_call_error, invalid_target,
+ wrong_format, invalid_operation, no_memory,
+ no_symbols, no_relocation_info,
+ no_more_archived_files, malformed_archive,
+ symbol_not_found, file_not_recognized,
+ file_ambiguously_recognized, no_contents,
+ bfd_error_nonrepresentable_section,
+ no_debug_section, bad_value,
+
+ /* An input file is shorter than expected. */
+ file_truncated,
+
+ invalid_error_code} bfd_ec;
+
+extern bfd_ec bfd_error;
+struct reloc_cache_entry;
+struct bfd_seclet;
+
+
+typedef struct bfd_error_vector {
+ void (* nonrepresentable_section ) PARAMS ((CONST bfd *CONST abfd,
+ CONST char *CONST name));
+ void (* undefined_symbol) PARAMS ((CONST struct reloc_cache_entry *rel,
+ CONST struct bfd_seclet *sec));
+ void (* reloc_value_truncated) PARAMS ((CONST struct
+ reloc_cache_entry *rel,
+ struct bfd_seclet *sec));
+
+ void (* reloc_dangerous) PARAMS ((CONST struct reloc_cache_entry *rel,
+ CONST struct bfd_seclet *sec));
+
+} bfd_error_vector_type;
+
+CONST char *bfd_errmsg PARAMS ((bfd_ec error_tag));
+void bfd_perror PARAMS ((CONST char *message));
+
+
+typedef enum bfd_print_symbol
+{
+ bfd_print_symbol_name,
+ bfd_print_symbol_more,
+ bfd_print_symbol_all
+} bfd_print_symbol_type;
+
+
+/* Information about a symbol that nm needs. */
+
+typedef struct _symbol_info
+{
+ symvalue value;
+ char type; /* */
+ CONST char *name; /* Symbol name. */
+ char stab_other; /* Unused. */
+ short stab_desc; /* Info for N_TYPE. */
+ CONST char *stab_name;
+} symbol_info;
+
+/* The code that implements targets can initialize a jump table with this
+ macro. It must name all its routines the same way (a prefix plus
+ the standard routine suffix), or it must #define the routines that
+ are not so named, before calling JUMP_TABLE in the initializer. */
+
+/* Semi-portable string concatenation in cpp.
+ The CAT4 hack is to avoid a problem with some strict ANSI C preprocessors.
+ The problem is, "32_" is not a valid preprocessing token, and we don't
+ want extra underscores (e.g., "nlm_32_"). The XCAT2 macro will cause the
+ inner CAT macros to be evaluated first, producing still-valid pp-tokens.
+ Then the final concatenation can be done. (Sigh.) */
+#ifndef CAT
+#ifdef SABER
+#define CAT(a,b) a##b
+#define CAT3(a,b,c) a##b##c
+#define CAT4(a,b,c,d) a##b##c##d
+#else
+#ifdef __STDC__
+#define CAT(a,b) a##b
+#define CAT3(a,b,c) a##b##c
+#define XCAT2(a,b) CAT(a,b)
+#define CAT4(a,b,c,d) XCAT2(CAT(a,b),CAT(c,d))
+#else
+#define CAT(a,b) a/**/b
+#define CAT3(a,b,c) a/**/b/**/c
+#define CAT4(a,b,c,d) a/**/b/**/c/**/d
+#endif
+#endif
+#endif
+
+#define JUMP_TABLE(NAME)\
+CAT(NAME,_core_file_failing_command),\
+CAT(NAME,_core_file_failing_signal),\
+CAT(NAME,_core_file_matches_executable_p),\
+CAT(NAME,_slurp_armap),\
+CAT(NAME,_slurp_extended_name_table),\
+CAT(NAME,_truncate_arname),\
+CAT(NAME,_write_armap),\
+CAT(NAME,_close_and_cleanup),\
+CAT(NAME,_set_section_contents),\
+CAT(NAME,_get_section_contents),\
+CAT(NAME,_new_section_hook),\
+CAT(NAME,_get_symtab_upper_bound),\
+CAT(NAME,_get_symtab),\
+CAT(NAME,_get_reloc_upper_bound),\
+CAT(NAME,_canonicalize_reloc),\
+CAT(NAME,_make_empty_symbol),\
+CAT(NAME,_print_symbol),\
+CAT(NAME,_get_symbol_info),\
+CAT(NAME,_get_lineno),\
+CAT(NAME,_set_arch_mach),\
+CAT(NAME,_openr_next_archived_file),\
+CAT(NAME,_find_nearest_line),\
+CAT(NAME,_generic_stat_arch_elt),\
+CAT(NAME,_sizeof_headers),\
+CAT(NAME,_bfd_debug_info_start),\
+CAT(NAME,_bfd_debug_info_end),\
+CAT(NAME,_bfd_debug_info_accumulate),\
+CAT(NAME,_bfd_get_relocated_section_contents),\
+CAT(NAME,_bfd_relax_section),\
+CAT(NAME,_bfd_seclet_link),\
+CAT(NAME,_bfd_reloc_type_lookup),\
+CAT(NAME,_bfd_make_debug_symbol)
+
+#define COFF_SWAP_TABLE (PTR) &bfd_coff_std_swap_table
+
+
+/* User program access to BFD facilities */
+
+/* Cast from const char * to char * so that caller can assign to
+ a char * without a warning. */
+#define bfd_get_filename(abfd) ((char *) (abfd)->filename)
+#define bfd_get_format(abfd) ((abfd)->format)
+#define bfd_get_target(abfd) ((abfd)->xvec->name)
+#define bfd_get_file_flags(abfd) ((abfd)->flags)
+#define bfd_applicable_file_flags(abfd) ((abfd)->xvec->object_flags)
+#define bfd_applicable_section_flags(abfd) ((abfd)->xvec->section_flags)
+#define bfd_my_archive(abfd) ((abfd)->my_archive)
+#define bfd_has_map(abfd) ((abfd)->has_armap)
+
+#define bfd_valid_reloc_types(abfd) ((abfd)->xvec->valid_reloc_types)
+#define bfd_usrdata(abfd) ((abfd)->usrdata)
+
+#define bfd_get_start_address(abfd) ((abfd)->start_address)
+#define bfd_get_symcount(abfd) ((abfd)->symcount)
+#define bfd_get_outsymbols(abfd) ((abfd)->outsymbols)
+#define bfd_count_sections(abfd) ((abfd)->section_count)
+
+#define bfd_get_symbol_leading_char(abfd) ((abfd)->xvec->symbol_leading_char)
+
+/* Byte swapping routines. */
+
+bfd_vma bfd_getb64 PARAMS ((unsigned char *));
+bfd_vma bfd_getl64 PARAMS ((unsigned char *));
+bfd_signed_vma bfd_getb_signed_64 PARAMS ((unsigned char *));
+bfd_signed_vma bfd_getl_signed_64 PARAMS ((unsigned char *));
+bfd_vma bfd_getb32 PARAMS ((unsigned char *));
+bfd_vma bfd_getl32 PARAMS ((unsigned char *));
+bfd_signed_vma bfd_getb_signed_32 PARAMS ((unsigned char *));
+bfd_signed_vma bfd_getl_signed_32 PARAMS ((unsigned char *));
+bfd_vma bfd_getb16 PARAMS ((unsigned char *));
+bfd_vma bfd_getl16 PARAMS ((unsigned char *));
+bfd_signed_vma bfd_getb_signed_16 PARAMS ((unsigned char *));
+bfd_signed_vma bfd_getl_signed_16 PARAMS ((unsigned char *));
+void bfd_putb64 PARAMS ((bfd_vma, unsigned char *));
+void bfd_putl64 PARAMS ((bfd_vma, unsigned char *));
+void bfd_putb32 PARAMS ((bfd_vma, unsigned char *));
+void bfd_putl32 PARAMS ((bfd_vma, unsigned char *));
+void bfd_putb16 PARAMS ((bfd_vma, unsigned char *));
+void bfd_putl16 PARAMS ((bfd_vma, unsigned char *));
+
+/* And more from the source. */
+void
+bfd_init PARAMS ((void));
+
+bfd *
+bfd_openr PARAMS ((CONST char *filename, CONST char*target));
+
+bfd *
+bfd_fdopenr PARAMS ((CONST char *filename, CONST char *target, int fd));
+
+bfd *
+bfd_openw PARAMS ((CONST char *filename, CONST char *target));
+
+boolean
+bfd_close PARAMS ((bfd *));
+
+boolean
+bfd_close_all_done PARAMS ((bfd *));
+
+bfd_size_type
+bfd_alloc_size PARAMS ((bfd *abfd));
+
+bfd *
+bfd_create PARAMS ((CONST char *filename, bfd *templ));
+
+
+ /* Byte swapping macros for user section data. */
+
+#define bfd_put_8(abfd, val, ptr) \
+ (*((unsigned char *)(ptr)) = (unsigned char)val)
+#define bfd_put_signed_8 \
+ bfd_put_8
+#define bfd_get_8(abfd, ptr) \
+ (*(unsigned char *)(ptr))
+#define bfd_get_signed_8(abfd, ptr) \
+ ((*(unsigned char *)(ptr) ^ 0x80) - 0x80)
+
+#define bfd_put_16(abfd, val, ptr) \
+ BFD_SEND(abfd, bfd_putx16, ((val),(ptr)))
+#define bfd_put_signed_16 \
+ bfd_put_16
+#define bfd_get_16(abfd, ptr) \
+ BFD_SEND(abfd, bfd_getx16, (ptr))
+#define bfd_get_signed_16(abfd, ptr) \
+ BFD_SEND (abfd, bfd_getx_signed_16, (ptr))
+
+#define bfd_put_32(abfd, val, ptr) \
+ BFD_SEND(abfd, bfd_putx32, ((val),(ptr)))
+#define bfd_put_signed_32 \
+ bfd_put_32
+#define bfd_get_32(abfd, ptr) \
+ BFD_SEND(abfd, bfd_getx32, (ptr))
+#define bfd_get_signed_32(abfd, ptr) \
+ BFD_SEND(abfd, bfd_getx_signed_32, (ptr))
+
+#define bfd_put_64(abfd, val, ptr) \
+ BFD_SEND(abfd, bfd_putx64, ((val), (ptr)))
+#define bfd_put_signed_64 \
+ bfd_put_64
+#define bfd_get_64(abfd, ptr) \
+ BFD_SEND(abfd, bfd_getx64, (ptr))
+#define bfd_get_signed_64(abfd, ptr) \
+ BFD_SEND(abfd, bfd_getx_signed_64, (ptr))
+
+
+ /* Byte swapping macros for file header data. */
+
+#define bfd_h_put_8(abfd, val, ptr) \
+ bfd_put_8 (abfd, val, ptr)
+#define bfd_h_put_signed_8(abfd, val, ptr) \
+ bfd_put_8 (abfd, val, ptr)
+#define bfd_h_get_8(abfd, ptr) \
+ bfd_get_8 (abfd, ptr)
+#define bfd_h_get_signed_8(abfd, ptr) \
+ bfd_get_signed_8 (abfd, ptr)
+
+#define bfd_h_put_16(abfd, val, ptr) \
+ BFD_SEND(abfd, bfd_h_putx16,(val,ptr))
+#define bfd_h_put_signed_16 \
+ bfd_h_put_16
+#define bfd_h_get_16(abfd, ptr) \
+ BFD_SEND(abfd, bfd_h_getx16,(ptr))
+#define bfd_h_get_signed_16(abfd, ptr) \
+ BFD_SEND(abfd, bfd_h_getx_signed_16, (ptr))
+
+#define bfd_h_put_32(abfd, val, ptr) \
+ BFD_SEND(abfd, bfd_h_putx32,(val,ptr))
+#define bfd_h_put_signed_32 \
+ bfd_h_put_32
+#define bfd_h_get_32(abfd, ptr) \
+ BFD_SEND(abfd, bfd_h_getx32,(ptr))
+#define bfd_h_get_signed_32(abfd, ptr) \
+ BFD_SEND(abfd, bfd_h_getx_signed_32, (ptr))
+
+#define bfd_h_put_64(abfd, val, ptr) \
+ BFD_SEND(abfd, bfd_h_putx64,(val, ptr))
+#define bfd_h_put_signed_64 \
+ bfd_h_put_64
+#define bfd_h_get_64(abfd, ptr) \
+ BFD_SEND(abfd, bfd_h_getx64,(ptr))
+#define bfd_h_get_signed_64(abfd, ptr) \
+ BFD_SEND(abfd, bfd_h_getx_signed_64, (ptr))
+
+typedef struct sec
+{
+ /* The name of the section, the name isn't a copy, the pointer is
+ the same as that passed to bfd_make_section. */
+
+ CONST char *name;
+
+ /* Which section is it 0.nth */
+
+ int index;
+
+ /* The next section in the list belonging to the BFD, or NULL. */
+
+ struct sec *next;
+
+ /* The field flags contains attributes of the section. Some of
+ flags are read in from the object file, and some are
+ synthesized from other information. */
+
+ flagword flags;
+
+#define SEC_NO_FLAGS 0x000
+
+ /* Tells the OS to allocate space for this section when loaded.
+ This would clear for a section containing debug information
+ only. */
+#define SEC_ALLOC 0x001
+
+ /* Tells the OS to load the section from the file when loading.
+ This would be clear for a .bss section */
+#define SEC_LOAD 0x002
+
+ /* The section contains data still to be relocated, so there will
+ be some relocation information too. */
+#define SEC_RELOC 0x004
+
+#if 0 /* Obsolete ? */
+#define SEC_BALIGN 0x008
+#endif
+
+ /* A signal to the OS that the section contains read only
+ data. */
+#define SEC_READONLY 0x010
+
+ /* The section contains code only. */
+#define SEC_CODE 0x020
+
+ /* The section contains data only. */
+#define SEC_DATA 0x040
+
+ /* The section will reside in ROM. */
+#define SEC_ROM 0x080
+
+ /* The section contains constructor information. This section
+ type is used by the linker to create lists of constructors and
+ destructors used by <<g++>>. When a back end sees a symbol
+ which should be used in a constructor list, it creates a new
+ section for the type of name (eg <<__CTOR_LIST__>>), attaches
+ the symbol to it and builds a relocation. To build the lists
+ of constructors, all the linker has to do is catenate all the
+ sections called <<__CTOR_LIST__>> and relocte the data
+ contained within - exactly the operations it would peform on
+ standard data. */
+#define SEC_CONSTRUCTOR 0x100
+
+ /* The section is a constuctor, and should be placed at the
+ end of the text, data, or bss section(?). */
+#define SEC_CONSTRUCTOR_TEXT 0x1100
+#define SEC_CONSTRUCTOR_DATA 0x2100
+#define SEC_CONSTRUCTOR_BSS 0x3100
+
+ /* The section has contents - a data section could be
+ <<SEC_ALLOC>> | <<SEC_HAS_CONTENTS>>, a debug section could be
+ <<SEC_HAS_CONTENTS>> */
+#define SEC_HAS_CONTENTS 0x200
+
+ /* An instruction to the linker not to output sections
+ containing this flag even if they have information which
+ would normally be written. */
+#define SEC_NEVER_LOAD 0x400
+
+ /* The section is a shared library section. The linker must leave
+ these completely alone, as the vma and size are used when
+ the executable is loaded. */
+#define SEC_SHARED_LIBRARY 0x800
+
+ /* The section is a common section (symbols may be defined
+ multiple times, the value of a symbol is the amount of
+ space it requires, and the largest symbol value is the one
+ used). Most targets have exactly one of these (which we
+ translate to bfd_com_section), but ECOFF has two. */
+#define SEC_IS_COMMON 0x8000
+
+ /* The section contains only debugging information. For
+ example, this is set for ELF .debug and .stab sections.
+ strip tests this flag to see if a section can be
+ discarded. */
+#define SEC_DEBUGGING 0x10000
+
+ /* End of section flags. */
+
+ /* The virtual memory address of the section - where it will be
+ at run time. The symbols are relocated against this. The
+ user_set_vma flag is maintained by bfd; if it's not set, the
+ backend can assign addresses (for example, in <<a.out>>, where
+ the default address for <<.data>> is dependent on the specific
+ target and various flags). */
+
+ bfd_vma vma;
+ boolean user_set_vma;
+
+ /* The load address of the section - where it would be in a
+ rom image, really only used for writing section header
+ information. */
+
+ bfd_vma lma;
+
+ /* The size of the section in bytes, as it will be output.
+ contains a value even if the section has no contents (eg, the
+ size of <<.bss>>). This will be filled in after relocation */
+
+ bfd_size_type _cooked_size;
+
+ /* The size on disk of the section in bytes originally. Normally this
+ value is the same as the size, but if some relaxing has
+ been done, then this value will be bigger. */
+
+ bfd_size_type _raw_size;
+
+ /* If this section is going to be output, then this value is the
+ offset into the output section of the first byte in the input
+ section. Eg, if this was going to start at the 100th byte in
+ the output section, this value would be 100. */
+
+ bfd_vma output_offset;
+
+ /* The output section through which to map on output. */
+
+ struct sec *output_section;
+
+ /* The alignment requirement of the section, as an exponent - eg
+ 3 aligns to 2^3 (or 8) */
+
+ unsigned int alignment_power;
+
+ /* If an input section, a pointer to a vector of relocation
+ records for the data in this section. */
+
+ struct reloc_cache_entry *relocation;
+
+ /* If an output section, a pointer to a vector of pointers to
+ relocation records for the data in this section. */
+
+ struct reloc_cache_entry **orelocation;
+
+ /* The number of relocation records in one of the above */
+
+ unsigned reloc_count;
+
+ /* Information below is back end specific - and not always used
+ or updated. */
+
+ /* File position of section data */
+
+ file_ptr filepos;
+
+ /* File position of relocation info */
+
+ file_ptr rel_filepos;
+
+ /* File position of line data */
+
+ file_ptr line_filepos;
+
+ /* Pointer to data for applications */
+
+ PTR userdata;
+
+ struct lang_output_section *otheruserdata;
+
+ /* Attached line number information */
+
+ alent *lineno;
+
+ /* Number of line number records */
+
+ unsigned int lineno_count;
+
+ /* When a section is being output, this value changes as more
+ linenumbers are written out */
+
+ file_ptr moving_line_filepos;
+
+ /* what the section number is in the target world */
+
+ int target_index;
+
+ PTR used_by_bfd;
+
+ /* If this is a constructor section then here is a list of the
+ relocations created to relocate items within it. */
+
+ struct relent_chain *constructor_chain;
+
+ /* The BFD which owns the section. */
+
+ bfd *owner;
+
+ boolean reloc_done;
+ /* A symbol which points at this section only */
+ struct symbol_cache_entry *symbol;
+ struct symbol_cache_entry **symbol_ptr_ptr;
+
+ struct bfd_seclet *seclets_head;
+ struct bfd_seclet *seclets_tail;
+} asection ;
+
+
+ /* These sections are global, and are managed by BFD. The application
+ and target back end are not permitted to change the values in
+ these sections. */
+#define BFD_ABS_SECTION_NAME "*ABS*"
+#define BFD_UND_SECTION_NAME "*UND*"
+#define BFD_COM_SECTION_NAME "*COM*"
+#define BFD_IND_SECTION_NAME "*IND*"
+
+ /* the absolute section */
+extern asection bfd_abs_section;
+ /* Pointer to the undefined section */
+extern asection bfd_und_section;
+ /* Pointer to the common section */
+extern asection bfd_com_section;
+ /* Pointer to the indirect section */
+extern asection bfd_ind_section;
+
+extern struct symbol_cache_entry *bfd_abs_symbol;
+extern struct symbol_cache_entry *bfd_com_symbol;
+extern struct symbol_cache_entry *bfd_und_symbol;
+extern struct symbol_cache_entry *bfd_ind_symbol;
+#define bfd_get_section_size_before_reloc(section) \
+ (section->reloc_done ? (abort(),1): (section)->_raw_size)
+#define bfd_get_section_size_after_reloc(section) \
+ ((section->reloc_done) ? (section)->_cooked_size: (abort(),1))
+asection *
+bfd_get_section_by_name PARAMS ((bfd *abfd, CONST char *name));
+
+asection *
+bfd_make_section_old_way PARAMS ((bfd *, CONST char *name));
+
+asection *
+bfd_make_section_anyway PARAMS ((bfd *, CONST char *name));
+
+asection *
+bfd_make_section PARAMS ((bfd *, CONST char *name));
+
+boolean
+bfd_set_section_flags PARAMS ((bfd *, asection *, flagword));
+
+void
+bfd_map_over_sections PARAMS ((bfd *abfd,
+ void (*func)(bfd *abfd,
+ asection *sect,
+ PTR obj),
+ PTR obj));
+
+boolean
+bfd_set_section_size PARAMS ((bfd *, asection *, bfd_size_type val));
+
+boolean
+bfd_set_section_contents
+ PARAMS ((bfd *abfd,
+ asection *section,
+ PTR data,
+ file_ptr offset,
+ bfd_size_type count));
+
+boolean
+bfd_get_section_contents
+ PARAMS ((bfd *abfd, asection *section, PTR location,
+ file_ptr offset, bfd_size_type count));
+
+enum bfd_architecture
+{
+ bfd_arch_unknown, /* File arch not known */
+ bfd_arch_obscure, /* Arch known, not one of these */
+ bfd_arch_m68k, /* Motorola 68xxx */
+ bfd_arch_vax, /* DEC Vax */
+ bfd_arch_i960, /* Intel 960 */
+ /* The order of the following is important.
+ lower number indicates a machine type that
+ only accepts a subset of the instructions
+ available to machines with higher numbers.
+ The exception is the "ca", which is
+ incompatible with all other machines except
+ "core". */
+
+#define bfd_mach_i960_core 1
+#define bfd_mach_i960_ka_sa 2
+#define bfd_mach_i960_kb_sb 3
+#define bfd_mach_i960_mc 4
+#define bfd_mach_i960_xa 5
+#define bfd_mach_i960_ca 6
+
+ bfd_arch_a29k, /* AMD 29000 */
+ bfd_arch_sparc, /* SPARC */
+ bfd_arch_mips, /* MIPS Rxxxx */
+ bfd_arch_i386, /* Intel 386 */
+ bfd_arch_we32k, /* AT&T WE32xxx */
+ bfd_arch_tahoe, /* CCI/Harris Tahoe */
+ bfd_arch_i860, /* Intel 860 */
+ bfd_arch_romp, /* IBM ROMP PC/RT */
+ bfd_arch_alliant, /* Alliant */
+ bfd_arch_convex, /* Convex */
+ bfd_arch_m88k, /* Motorola 88xxx */
+ bfd_arch_pyramid, /* Pyramid Technology */
+ bfd_arch_h8300, /* Hitachi H8/300 */
+#define bfd_mach_h8300 1
+#define bfd_mach_h8300h 2
+ bfd_arch_rs6000, /* IBM RS/6000 */
+ bfd_arch_hppa, /* HP PA RISC */
+ bfd_arch_z8k, /* Zilog Z8000 */
+#define bfd_mach_z8001 1
+#define bfd_mach_z8002 2
+ bfd_arch_h8500, /* Hitachi H8/500 */
+ bfd_arch_sh, /* Hitachi SH */
+ bfd_arch_alpha, /* Dec Alpha */
+ bfd_arch_last
+ };
+
+typedef struct bfd_arch_info
+{
+ int bits_per_word;
+ int bits_per_address;
+ int bits_per_byte;
+ enum bfd_architecture arch;
+ long mach;
+ char *arch_name;
+ CONST char *printable_name;
+ unsigned int section_align_power;
+ /* true if this is the default machine for the architecture */
+ boolean the_default;
+ CONST struct bfd_arch_info * (*compatible)
+ PARAMS ((CONST struct bfd_arch_info *a,
+ CONST struct bfd_arch_info *b));
+
+ boolean (*scan) PARAMS ((CONST struct bfd_arch_info *, CONST char *));
+ /* How to disassemble an instruction, producing a printable
+ representation on a specified stdio stream. This isn't
+ defined for most processors at present, because of the size
+ of the additional tables it would drag in, and because gdb
+ wants to use a different interface. */
+ unsigned int (*disassemble) PARAMS ((bfd_vma addr, CONST char *data,
+ PTR stream));
+
+ struct bfd_arch_info *next;
+} bfd_arch_info_type;
+CONST char *
+bfd_printable_name PARAMS ((bfd *abfd));
+
+bfd_arch_info_type *
+bfd_scan_arch PARAMS ((CONST char *));
+
+CONST bfd_arch_info_type *
+bfd_arch_get_compatible PARAMS ((
+ CONST bfd *abfd,
+ CONST bfd *bbfd));
+
+void
+bfd_set_arch_info PARAMS ((bfd *, bfd_arch_info_type *));
+
+enum bfd_architecture
+bfd_get_arch PARAMS ((bfd *abfd));
+
+unsigned long
+bfd_get_mach PARAMS ((bfd *abfd));
+
+unsigned int
+bfd_arch_bits_per_byte PARAMS ((bfd *abfd));
+
+unsigned int
+bfd_arch_bits_per_address PARAMS ((bfd *abfd));
+
+bfd_arch_info_type *
+bfd_get_arch_info PARAMS ((bfd *));
+
+bfd_arch_info_type *
+bfd_lookup_arch
+ PARAMS ((enum bfd_architecture
+ arch,
+ long machine));
+
+CONST char *
+bfd_printable_arch_mach
+ PARAMS ((enum bfd_architecture arch, unsigned long machine));
+
+typedef enum bfd_reloc_status
+{
+ /* No errors detected */
+ bfd_reloc_ok,
+
+ /* The relocation was performed, but there was an overflow. */
+ bfd_reloc_overflow,
+
+ /* The address to relocate was not within the section supplied. */
+ bfd_reloc_outofrange,
+
+ /* Used by special functions */
+ bfd_reloc_continue,
+
+ /* Unused */
+ bfd_reloc_notsupported,
+
+ /* Unsupported relocation size requested. */
+ bfd_reloc_other,
+
+ /* The symbol to relocate against was undefined. */
+ bfd_reloc_undefined,
+
+ /* The relocation was performed, but may not be ok - presently
+ generated only when linking i960 coff files with i960 b.out
+ symbols. */
+ bfd_reloc_dangerous
+ }
+ bfd_reloc_status_type;
+
+
+typedef struct reloc_cache_entry
+{
+ /* A pointer into the canonical table of pointers */
+ struct symbol_cache_entry **sym_ptr_ptr;
+
+ /* offset in section */
+ bfd_size_type address;
+
+ /* addend for relocation value */
+ bfd_vma addend;
+
+ /* Pointer to how to perform the required relocation */
+ CONST struct reloc_howto_struct *howto;
+
+} arelent;
+enum complain_overflow
+{
+ /* Do not complain on overflow. */
+ complain_overflow_dont,
+
+ /* Complain if the bitfield overflows, whether it is considered
+ as signed or unsigned. */
+ complain_overflow_bitfield,
+
+ /* Complain if the value overflows when considered as signed
+ number. */
+ complain_overflow_signed,
+
+ /* Complain if the value overflows when considered as an
+ unsigned number. */
+ complain_overflow_unsigned
+};
+
+typedef CONST struct reloc_howto_struct
+{
+ /* The type field has mainly a documetary use - the back end can
+ to what it wants with it, though the normally the back end's
+ external idea of what a reloc number would be would be stored
+ in this field. For example, the a PC relative word relocation
+ in a coff environment would have the type 023 - because that's
+ what the outside world calls a R_PCRWORD reloc. */
+ unsigned int type;
+
+ /* The value the final relocation is shifted right by. This drops
+ unwanted data from the relocation. */
+ unsigned int rightshift;
+
+ /* The size of the item to be relocated. This is *not* a
+ power-of-two measure.
+ 0 : one byte
+ 1 : two bytes
+ 2 : four bytes
+ 3 : nothing done (unless special_function is nonzero)
+ 4 : eight bytes
+ -2 : two bytes, result should be subtracted from the
+ data instead of added
+ There is currently no trivial way to extract a "number of
+ bytes" from a howto pointer. */
+ int size;
+
+ /* The number of bits in the item to be relocated. This is used
+ when doing overflow checking. */
+ unsigned int bitsize;
+
+ /* Notes that the relocation is relative to the location in the
+ data section of the addend. The relocation function will
+ subtract from the relocation value the address of the location
+ being relocated. */
+ boolean pc_relative;
+
+ /* The bit position of the reloc value in the destination.
+ The relocated value is left shifted by this amount. */
+ unsigned int bitpos;
+
+ /* What type of overflow error should be checked for when
+ relocating. */
+ enum complain_overflow complain_on_overflow;
+
+ /* If this field is non null, then the supplied function is
+ called rather than the normal function. This allows really
+ strange relocation methods to be accomodated (e.g., i960 callj
+ instructions). */
+ bfd_reloc_status_type (*special_function)
+ PARAMS ((bfd *abfd,
+ arelent *reloc_entry,
+ struct symbol_cache_entry *symbol,
+ PTR data,
+ asection *input_section,
+ bfd *output_bfd));
+
+ /* The textual name of the relocation type. */
+ char *name;
+
+ /* When performing a partial link, some formats must modify the
+ relocations rather than the data - this flag signals this.*/
+ boolean partial_inplace;
+
+ /* The src_mask is used to select what parts of the read in data
+ are to be used in the relocation sum. E.g., if this was an 8 bit
+ bit of data which we read and relocated, this would be
+ 0x000000ff. When we have relocs which have an addend, such as
+ sun4 extended relocs, the value in the offset part of a
+ relocating field is garbage so we never use it. In this case
+ the mask would be 0x00000000. */
+ bfd_vma src_mask;
+
+ /* The dst_mask is what parts of the instruction are replaced
+ into the instruction. In most cases src_mask == dst_mask,
+ except in the above special case, where dst_mask would be
+ 0x000000ff, and src_mask would be 0x00000000. */
+ bfd_vma dst_mask;
+
+ /* When some formats create PC relative instructions, they leave
+ the value of the pc of the place being relocated in the offset
+ slot of the instruction, so that a PC relative relocation can
+ be made just by adding in an ordinary offset (e.g., sun3 a.out).
+ Some formats leave the displacement part of an instruction
+ empty (e.g., m88k bcs), this flag signals the fact.*/
+ boolean pcrel_offset;
+
+} reloc_howto_type;
+#define HOWTO(C, R,S,B, P, BI, O, SF, NAME, INPLACE, MASKSRC, MASKDST, PC) \
+ {(unsigned)C,R,S,B, P, BI, O,SF,NAME,INPLACE,MASKSRC,MASKDST,PC}
+#define NEWHOWTO( FUNCTION, NAME,SIZE,REL,IN) HOWTO(0,0,SIZE,0,REL,0,complain_overflow_dont,FUNCTION, NAME,false,0,0,IN)
+
+#define HOWTO_PREPARE(relocation, symbol) \
+ { \
+ if (symbol != (asymbol *)NULL) { \
+ if (bfd_is_com_section (symbol->section)) { \
+ relocation = 0; \
+ } \
+ else { \
+ relocation = symbol->value; \
+ } \
+ } \
+}
+typedef unsigned char bfd_byte;
+
+typedef struct relent_chain {
+ arelent relent;
+ struct relent_chain *next;
+} arelent_chain;
+bfd_reloc_status_type
+
+bfd_perform_relocation
+ PARAMS ((bfd * abfd,
+ arelent *reloc_entry,
+ PTR data,
+ asection *input_section,
+ bfd *output_bfd));
+
+typedef enum bfd_reloc_code_real
+{
+ /* Basic absolute relocations */
+ BFD_RELOC_64,
+ BFD_RELOC_32,
+ BFD_RELOC_16,
+ BFD_RELOC_8,
+
+ /* PC-relative relocations */
+ BFD_RELOC_64_PCREL,
+ BFD_RELOC_32_PCREL,
+ BFD_RELOC_24_PCREL, /* used by i960 */
+ BFD_RELOC_16_PCREL,
+ BFD_RELOC_8_PCREL,
+
+ /* Linkage-table relative */
+ BFD_RELOC_32_BASEREL,
+ BFD_RELOC_16_BASEREL,
+ BFD_RELOC_8_BASEREL,
+
+ /* The type of reloc used to build a contructor table - at the moment
+ probably a 32 bit wide abs address, but the cpu can choose. */
+ BFD_RELOC_CTOR,
+
+ /* 8 bits wide, but used to form an address like 0xffnn */
+ BFD_RELOC_8_FFnn,
+
+ /* 32-bit pc-relative, shifted right 2 bits (i.e., 30-bit
+ word displacement, e.g. for SPARC) */
+ BFD_RELOC_32_PCREL_S2,
+
+ /* High 22 bits of 32-bit value, placed into lower 22 bits of
+ target word; simple reloc. */
+ BFD_RELOC_HI22,
+ /* Low 10 bits. */
+ BFD_RELOC_LO10,
+
+ /* Reloc types used for i960/b.out. */
+ BFD_RELOC_I960_CALLJ,
+
+ /* now for the sparc/elf codes */
+ BFD_RELOC_NONE, /* actually used */
+ BFD_RELOC_SPARC_WDISP22,
+ BFD_RELOC_SPARC22,
+ BFD_RELOC_SPARC13,
+ BFD_RELOC_SPARC_GOT10,
+ BFD_RELOC_SPARC_GOT13,
+ BFD_RELOC_SPARC_GOT22,
+ BFD_RELOC_SPARC_PC10,
+ BFD_RELOC_SPARC_PC22,
+ BFD_RELOC_SPARC_WPLT30,
+ BFD_RELOC_SPARC_COPY,
+ BFD_RELOC_SPARC_GLOB_DAT,
+ BFD_RELOC_SPARC_JMP_SLOT,
+ BFD_RELOC_SPARC_RELATIVE,
+ BFD_RELOC_SPARC_UA32,
+
+ /* these are a.out specific? */
+ BFD_RELOC_SPARC_BASE13,
+ BFD_RELOC_SPARC_BASE22,
+
+
+ /* Bits 27..2 of the relocation address shifted right 2 bits;
+ simple reloc otherwise. */
+ BFD_RELOC_MIPS_JMP,
+
+ /* signed 16-bit pc-relative, shifted right 2 bits (e.g. for MIPS) */
+ BFD_RELOC_16_PCREL_S2,
+
+ /* High 16 bits of 32-bit value; simple reloc. */
+ BFD_RELOC_HI16,
+ /* High 16 bits of 32-bit value but the low 16 bits will be sign
+ extended and added to form the final result. If the low 16
+ bits form a negative number, we need to add one to the high value
+ to compensate for the borrow when the low bits are added. */
+ BFD_RELOC_HI16_S,
+ /* Low 16 bits. */
+ BFD_RELOC_LO16,
+
+ /* 16 bit relocation relative to the global pointer. */
+ BFD_RELOC_MIPS_GPREL,
+
+ /* These are, so far, specific to HPPA processors. I'm not sure that some
+ don't duplicate other reloc types, such as BFD_RELOC_32 and _32_PCREL.
+ Also, many more were in the list I got that don't fit in well in the
+ model BFD uses, so I've omitted them for now. If we do make this reloc
+ type get used for code that really does implement the funky reloc types,
+ they'll have to be added to this list. */
+ BFD_RELOC_HPPA_32,
+ BFD_RELOC_HPPA_11,
+ BFD_RELOC_HPPA_14,
+ BFD_RELOC_HPPA_17,
+
+ BFD_RELOC_HPPA_L21,
+ BFD_RELOC_HPPA_R11,
+ BFD_RELOC_HPPA_R14,
+ BFD_RELOC_HPPA_R17,
+ BFD_RELOC_HPPA_LS21,
+ BFD_RELOC_HPPA_RS11,
+ BFD_RELOC_HPPA_RS14,
+ BFD_RELOC_HPPA_RS17,
+ BFD_RELOC_HPPA_LD21,
+ BFD_RELOC_HPPA_RD11,
+ BFD_RELOC_HPPA_RD14,
+ BFD_RELOC_HPPA_RD17,
+ BFD_RELOC_HPPA_LR21,
+ BFD_RELOC_HPPA_RR14,
+ BFD_RELOC_HPPA_RR17,
+
+ BFD_RELOC_HPPA_GOTOFF_11,
+ BFD_RELOC_HPPA_GOTOFF_14,
+ BFD_RELOC_HPPA_GOTOFF_L21,
+ BFD_RELOC_HPPA_GOTOFF_R11,
+ BFD_RELOC_HPPA_GOTOFF_R14,
+ BFD_RELOC_HPPA_GOTOFF_LS21,
+ BFD_RELOC_HPPA_GOTOFF_RS11,
+ BFD_RELOC_HPPA_GOTOFF_RS14,
+ BFD_RELOC_HPPA_GOTOFF_LD21,
+ BFD_RELOC_HPPA_GOTOFF_RD11,
+ BFD_RELOC_HPPA_GOTOFF_RD14,
+ BFD_RELOC_HPPA_GOTOFF_LR21,
+ BFD_RELOC_HPPA_GOTOFF_RR14,
+
+ BFD_RELOC_HPPA_DLT_32,
+ BFD_RELOC_HPPA_DLT_11,
+ BFD_RELOC_HPPA_DLT_14,
+ BFD_RELOC_HPPA_DLT_L21,
+ BFD_RELOC_HPPA_DLT_R11,
+ BFD_RELOC_HPPA_DLT_R14,
+
+ BFD_RELOC_HPPA_ABS_CALL_11,
+ BFD_RELOC_HPPA_ABS_CALL_14,
+ BFD_RELOC_HPPA_ABS_CALL_17,
+ BFD_RELOC_HPPA_ABS_CALL_L21,
+ BFD_RELOC_HPPA_ABS_CALL_R11,
+ BFD_RELOC_HPPA_ABS_CALL_R14,
+ BFD_RELOC_HPPA_ABS_CALL_R17,
+ BFD_RELOC_HPPA_ABS_CALL_LS21,
+ BFD_RELOC_HPPA_ABS_CALL_RS11,
+ BFD_RELOC_HPPA_ABS_CALL_RS14,
+ BFD_RELOC_HPPA_ABS_CALL_RS17,
+ BFD_RELOC_HPPA_ABS_CALL_LD21,
+ BFD_RELOC_HPPA_ABS_CALL_RD11,
+ BFD_RELOC_HPPA_ABS_CALL_RD14,
+ BFD_RELOC_HPPA_ABS_CALL_RD17,
+ BFD_RELOC_HPPA_ABS_CALL_LR21,
+ BFD_RELOC_HPPA_ABS_CALL_RR14,
+ BFD_RELOC_HPPA_ABS_CALL_RR17,
+
+ BFD_RELOC_HPPA_PCREL_CALL_11,
+ BFD_RELOC_HPPA_PCREL_CALL_12,
+ BFD_RELOC_HPPA_PCREL_CALL_14,
+ BFD_RELOC_HPPA_PCREL_CALL_17,
+ BFD_RELOC_HPPA_PCREL_CALL_L21,
+ BFD_RELOC_HPPA_PCREL_CALL_R11,
+ BFD_RELOC_HPPA_PCREL_CALL_R14,
+ BFD_RELOC_HPPA_PCREL_CALL_R17,
+ BFD_RELOC_HPPA_PCREL_CALL_LS21,
+ BFD_RELOC_HPPA_PCREL_CALL_RS11,
+ BFD_RELOC_HPPA_PCREL_CALL_RS14,
+ BFD_RELOC_HPPA_PCREL_CALL_RS17,
+ BFD_RELOC_HPPA_PCREL_CALL_LD21,
+ BFD_RELOC_HPPA_PCREL_CALL_RD11,
+ BFD_RELOC_HPPA_PCREL_CALL_RD14,
+ BFD_RELOC_HPPA_PCREL_CALL_RD17,
+ BFD_RELOC_HPPA_PCREL_CALL_LR21,
+ BFD_RELOC_HPPA_PCREL_CALL_RR14,
+ BFD_RELOC_HPPA_PCREL_CALL_RR17,
+
+ BFD_RELOC_HPPA_PLABEL_32,
+ BFD_RELOC_HPPA_PLABEL_11,
+ BFD_RELOC_HPPA_PLABEL_14,
+ BFD_RELOC_HPPA_PLABEL_L21,
+ BFD_RELOC_HPPA_PLABEL_R11,
+ BFD_RELOC_HPPA_PLABEL_R14,
+
+ BFD_RELOC_HPPA_UNWIND_ENTRY,
+ BFD_RELOC_HPPA_UNWIND_ENTRIES,
+
+ /* i386/elf relocations */
+ BFD_RELOC_386_GOT32,
+ BFD_RELOC_386_PLT32,
+ BFD_RELOC_386_COPY,
+ BFD_RELOC_386_GLOB_DAT,
+ BFD_RELOC_386_JUMP_SLOT,
+ BFD_RELOC_386_RELATIVE,
+ BFD_RELOC_386_GOTOFF,
+ BFD_RELOC_386_GOTPC,
+
+ /* this must be the highest numeric value */
+ BFD_RELOC_UNUSED
+ } bfd_reloc_code_real_type;
+CONST struct reloc_howto_struct *
+
+bfd_reloc_type_lookup PARAMS ((bfd *abfd, bfd_reloc_code_real_type code));
+
+
+typedef struct symbol_cache_entry
+{
+ /* A pointer to the BFD which owns the symbol. This information
+ is necessary so that a back end can work out what additional
+ information (invisible to the application writer) is carried
+ with the symbol.
+
+ This field is *almost* redundant, since you can use section->owner
+ instead, except that some symbols point to the global sections
+ bfd_{abs,com,und}_section. This could be fixed by making
+ these globals be per-bfd (or per-target-flavor). FIXME. */
+
+ struct _bfd *the_bfd; /* Use bfd_asymbol_bfd(sym) to access this field. */
+
+ /* The text of the symbol. The name is left alone, and not copied - the
+ application may not alter it. */
+ CONST char *name;
+
+ /* The value of the symbol. This really should be a union of a
+ numeric value with a pointer, since some flags indicate that
+ a pointer to another symbol is stored here. */
+ symvalue value;
+
+ /* Attributes of a symbol: */
+
+#define BSF_NO_FLAGS 0x00
+
+ /* The symbol has local scope; <<static>> in <<C>>. The value
+ is the offset into the section of the data. */
+#define BSF_LOCAL 0x01
+
+ /* The symbol has global scope; initialized data in <<C>>. The
+ value is the offset into the section of the data. */
+#define BSF_GLOBAL 0x02
+
+ /* The symbol has global scope, and is exported. The value is
+ the offset into the section of the data. */
+#define BSF_EXPORT BSF_GLOBAL /* no real difference */
+
+ /* A normal C symbol would be one of:
+ <<BSF_LOCAL>>, <<BSF_FORT_COMM>>, <<BSF_UNDEFINED>> or
+ <<BSF_GLOBAL>> */
+
+ /* The symbol is a debugging record. The value has an arbitary
+ meaning. */
+#define BSF_DEBUGGING 0x08
+
+ /* The symbol denotes a function entry point. Used in ELF,
+ perhaps others someday. */
+#define BSF_FUNCTION 0x10
+
+ /* Used by the linker. */
+#define BSF_KEEP 0x20
+#define BSF_KEEP_G 0x40
+
+ /* A weak global symbol, overridable without warnings by
+ a regular global symbol of the same name. */
+#define BSF_WEAK 0x80
+
+ /* This symbol was created to point to a section, e.g. ELF's
+ STT_SECTION symbols. */
+#define BSF_SECTION_SYM 0x100
+
+ /* The symbol used to be a common symbol, but now it is
+ allocated. */
+#define BSF_OLD_COMMON 0x200
+
+ /* The default value for common data. */
+#define BFD_FORT_COMM_DEFAULT_VALUE 0
+
+ /* In some files the type of a symbol sometimes alters its
+ location in an output file - ie in coff a <<ISFCN>> symbol
+ which is also <<C_EXT>> symbol appears where it was
+ declared and not at the end of a section. This bit is set
+ by the target BFD part to convey this information. */
+
+#define BSF_NOT_AT_END 0x400
+
+ /* Signal that the symbol is the label of constructor section. */
+#define BSF_CONSTRUCTOR 0x800
+
+ /* Signal that the symbol is a warning symbol. If the symbol
+ is a warning symbol, then the value field (I know this is
+ tacky) will point to the asymbol which when referenced will
+ cause the warning. */
+#define BSF_WARNING 0x1000
+
+ /* Signal that the symbol is indirect. The value of the symbol
+ is a pointer to an undefined asymbol which contains the
+ name to use instead. */
+#define BSF_INDIRECT 0x2000
+
+ /* BSF_FILE marks symbols that contain a file name. This is used
+ for ELF STT_FILE symbols. */
+#define BSF_FILE 0x4000
+
+ flagword flags;
+
+ /* A pointer to the section to which this symbol is
+ relative. This will always be non NULL, there are special
+ sections for undefined and absolute symbols */
+ struct sec *section;
+
+ /* Back end special data. This is being phased out in favour
+ of making this a union. */
+ PTR udata;
+
+} asymbol;
+#define get_symtab_upper_bound(abfd) \
+ BFD_SEND (abfd, _get_symtab_upper_bound, (abfd))
+#define bfd_canonicalize_symtab(abfd, location) \
+ BFD_SEND (abfd, _bfd_canonicalize_symtab,\
+ (abfd, location))
+boolean
+bfd_set_symtab PARAMS ((bfd *, asymbol **, unsigned int ));
+
+void
+bfd_print_symbol_vandf PARAMS ((PTR file, asymbol *symbol));
+
+#define bfd_make_empty_symbol(abfd) \
+ BFD_SEND (abfd, _bfd_make_empty_symbol, (abfd))
+#define bfd_make_debug_symbol(abfd,ptr,size) \
+ BFD_SEND (abfd, _bfd_make_debug_symbol, (abfd, ptr, size))
+int
+bfd_decode_symclass PARAMS ((asymbol *symbol));
+
+void
+bfd_symbol_info PARAMS ((asymbol *symbol, symbol_info *ret));
+
+struct _bfd
+{
+ /* The filename the application opened the BFD with. */
+ CONST char *filename;
+
+ /* A pointer to the target jump table. */
+ struct bfd_target *xvec;
+
+ /* To avoid dragging too many header files into every file that
+ includes `<<bfd.h>>', IOSTREAM has been declared as a "char
+ *", and MTIME as a "long". Their correct types, to which they
+ are cast when used, are "FILE *" and "time_t". The iostream
+ is the result of an fopen on the filename. */
+ char *iostream;
+
+ /* Is the file being cached */
+
+ boolean cacheable;
+
+ /* Marks whether there was a default target specified when the
+ BFD was opened. This is used to select what matching algorithm
+ to use to chose the back end. */
+
+ boolean target_defaulted;
+
+ /* The caching routines use these to maintain a
+ least-recently-used list of BFDs */
+
+ struct _bfd *lru_prev, *lru_next;
+
+ /* When a file is closed by the caching routines, BFD retains
+ state information on the file here:
+ */
+
+ file_ptr where;
+
+ /* and here:*/
+
+ boolean opened_once;
+
+ /* Set if we have a locally maintained mtime value, rather than
+ getting it from the file each time: */
+
+ boolean mtime_set;
+
+ /* File modified time, if mtime_set is true: */
+
+ long mtime;
+
+ /* Reserved for an unimplemented file locking extension.*/
+
+ int ifd;
+
+ /* The format which belongs to the BFD.*/
+
+ bfd_format format;
+
+ /* The direction the BFD was opened with*/
+
+ enum bfd_direction {no_direction = 0,
+ read_direction = 1,
+ write_direction = 2,
+ both_direction = 3} direction;
+
+ /* Format_specific flags*/
+
+ flagword flags;
+
+ /* Currently my_archive is tested before adding origin to
+ anything. I believe that this can become always an add of
+ origin, with origin set to 0 for non archive files. */
+
+ file_ptr origin;
+
+ /* Remember when output has begun, to stop strange things
+ happening. */
+ boolean output_has_begun;
+
+ /* Pointer to linked list of sections*/
+ struct sec *sections;
+
+ /* The number of sections */
+ unsigned int section_count;
+
+ /* Stuff only useful for object files:
+ The start address. */
+ bfd_vma start_address;
+
+ /* Used for input and output*/
+ unsigned int symcount;
+
+ /* Symbol table for output BFD*/
+ struct symbol_cache_entry **outsymbols;
+
+ /* Pointer to structure which contains architecture information*/
+ struct bfd_arch_info *arch_info;
+
+ /* Stuff only useful for archives:*/
+ PTR arelt_data;
+ struct _bfd *my_archive;
+ struct _bfd *next;
+ struct _bfd *archive_head;
+ boolean has_armap;
+
+ /* Used by the back end to hold private data. */
+
+ union
+ {
+ struct aout_data_struct *aout_data;
+ struct artdata *aout_ar_data;
+ struct _oasys_data *oasys_obj_data;
+ struct _oasys_ar_data *oasys_ar_data;
+ struct coff_tdata *coff_obj_data;
+ struct ecoff_tdata *ecoff_obj_data;
+ struct ieee_data_struct *ieee_data;
+ struct ieee_ar_data_struct *ieee_ar_data;
+ struct srec_data_struct *srec_data;
+ struct tekhex_data_struct *tekhex_data;
+ struct elf_obj_tdata *elf_obj_data;
+ struct nlm_obj_tdata *nlm_obj_data;
+ struct bout_data_struct *bout_data;
+ struct sun_core_struct *sun_core_data;
+ struct trad_core_struct *trad_core_data;
+ struct hppa_data_struct *hppa_data;
+ struct hpux_core_struct *hpux_core_data;
+ struct sgi_core_struct *sgi_core_data;
+ struct lynx_core_struct *lynx_core_data;
+ struct osf_core_struct *osf_core_data;
+ PTR any;
+ } tdata;
+
+ /* Used by the application to hold private data*/
+ PTR usrdata;
+
+ /* Where all the allocated stuff under this BFD goes */
+ struct obstack memory;
+
+ /* Is this really needed in addition to usrdata? */
+ asymbol **ld_symbols;
+};
+
+unsigned int
+bfd_get_reloc_upper_bound PARAMS ((bfd *abfd, asection *sect));
+
+unsigned int
+bfd_canonicalize_reloc
+ PARAMS ((bfd *abfd,
+ asection *sec,
+ arelent **loc,
+ asymbol **syms));
+
+boolean
+bfd_set_file_flags PARAMS ((bfd *abfd, flagword flags));
+
+void
+bfd_set_reloc
+ PARAMS ((bfd *abfd, asection *sec, arelent **rel, unsigned int count)
+
+ );
+
+boolean
+bfd_set_start_address PARAMS ((bfd *, bfd_vma));
+
+long
+bfd_get_mtime PARAMS ((bfd *));
+
+long
+bfd_get_size PARAMS ((bfd *));
+
+int
+bfd_get_gp_size PARAMS ((bfd *));
+
+void
+bfd_set_gp_size PARAMS ((bfd *, int));
+
+bfd_vma
+bfd_scan_vma PARAMS ((CONST char *string, CONST char **end, int base));
+
+#define bfd_sizeof_headers(abfd, reloc) \
+ BFD_SEND (abfd, _bfd_sizeof_headers, (abfd, reloc))
+
+#define bfd_find_nearest_line(abfd, sec, syms, off, file, func, line) \
+ BFD_SEND (abfd, _bfd_find_nearest_line, (abfd, sec, syms, off, file, func, line))
+
+ /* Do these three do anything useful at all, for any back end? */
+#define bfd_debug_info_start(abfd) \
+ BFD_SEND (abfd, _bfd_debug_info_start, (abfd))
+
+#define bfd_debug_info_end(abfd) \
+ BFD_SEND (abfd, _bfd_debug_info_end, (abfd))
+
+#define bfd_debug_info_accumulate(abfd, section) \
+ BFD_SEND (abfd, _bfd_debug_info_accumulate, (abfd, section))
+
+
+#define bfd_stat_arch_elt(abfd, stat) \
+ BFD_SEND (abfd, _bfd_stat_arch_elt,(abfd, stat))
+
+#define bfd_set_arch_mach(abfd, arch, mach)\
+ BFD_SEND ( abfd, _bfd_set_arch_mach, (abfd, arch, mach))
+
+#define bfd_get_relocated_section_contents(abfd, seclet, data, relocateable) \
+ BFD_SEND (abfd, _bfd_get_relocated_section_contents, (abfd, seclet, data, relocateable))
+
+#define bfd_relax_section(abfd, section, symbols) \
+ BFD_SEND (abfd, _bfd_relax_section, (abfd, section, symbols))
+
+#define bfd_seclet_link(abfd, data, relocateable) \
+ BFD_SEND (abfd, _bfd_seclet_link, (abfd, data, relocateable))
+symindex
+bfd_get_next_mapent PARAMS ((bfd *, symindex previous, carsym ** sym));
+
+boolean
+bfd_set_archive_head PARAMS ((bfd *output, bfd *new_head));
+
+bfd *
+bfd_get_elt_at_index PARAMS ((bfd * archive, int index));
+
+bfd*
+bfd_openr_next_archived_file PARAMS ((bfd *archive, bfd *previous));
+
+CONST char *
+bfd_core_file_failing_command PARAMS ((bfd *));
+
+int
+bfd_core_file_failing_signal PARAMS ((bfd *));
+
+boolean
+core_file_matches_executable_p
+ PARAMS ((bfd *core_bfd, bfd *exec_bfd));
+
+#define BFD_SEND(bfd, message, arglist) \
+ ((*((bfd)->xvec->message)) arglist)
+#define BFD_SEND_FMT(bfd, message, arglist) \
+ (((bfd)->xvec->message[(int)((bfd)->format)]) arglist)
+typedef struct bfd_target
+{
+ char *name;
+ enum target_flavour {
+ bfd_target_unknown_flavour,
+ bfd_target_aout_flavour,
+ bfd_target_coff_flavour,
+ bfd_target_ecoff_flavour,
+ bfd_target_elf_flavour,
+ bfd_target_ieee_flavour,
+ bfd_target_nlm_flavour,
+ bfd_target_oasys_flavour,
+ bfd_target_tekhex_flavour,
+ bfd_target_srec_flavour,
+ bfd_target_hppa_flavour} flavour;
+ boolean byteorder_big_p;
+ boolean header_byteorder_big_p;
+ flagword object_flags;
+ flagword section_flags;
+ char symbol_leading_char;
+ char ar_pad_char;
+ unsigned short ar_max_namelen;
+ unsigned int align_power_min;
+ bfd_vma (*bfd_getx64) PARAMS ((bfd_byte *));
+ bfd_signed_vma (*bfd_getx_signed_64) PARAMS ((bfd_byte *));
+ void (*bfd_putx64) PARAMS ((bfd_vma, bfd_byte *));
+ bfd_vma (*bfd_getx32) PARAMS ((bfd_byte *));
+ bfd_signed_vma (*bfd_getx_signed_32) PARAMS ((bfd_byte *));
+ void (*bfd_putx32) PARAMS ((bfd_vma, bfd_byte *));
+ bfd_vma (*bfd_getx16) PARAMS ((bfd_byte *));
+ bfd_signed_vma (*bfd_getx_signed_16) PARAMS ((bfd_byte *));
+ void (*bfd_putx16) PARAMS ((bfd_vma, bfd_byte *));
+ bfd_vma (*bfd_h_getx64) PARAMS ((bfd_byte *));
+ bfd_signed_vma (*bfd_h_getx_signed_64) PARAMS ((bfd_byte *));
+ void (*bfd_h_putx64) PARAMS ((bfd_vma, bfd_byte *));
+ bfd_vma (*bfd_h_getx32) PARAMS ((bfd_byte *));
+ bfd_signed_vma (*bfd_h_getx_signed_32) PARAMS ((bfd_byte *));
+ void (*bfd_h_putx32) PARAMS ((bfd_vma, bfd_byte *));
+ bfd_vma (*bfd_h_getx16) PARAMS ((bfd_byte *));
+ bfd_signed_vma (*bfd_h_getx_signed_16) PARAMS ((bfd_byte *));
+ void (*bfd_h_putx16) PARAMS ((bfd_vma, bfd_byte *));
+ struct bfd_target * (*_bfd_check_format[bfd_type_end]) PARAMS ((bfd *));
+ boolean (*_bfd_set_format[bfd_type_end]) PARAMS ((bfd *));
+ boolean (*_bfd_write_contents[bfd_type_end]) PARAMS ((bfd *));
+ char * (*_core_file_failing_command) PARAMS ((bfd *));
+ int (*_core_file_failing_signal) PARAMS ((bfd *));
+ boolean (*_core_file_matches_executable_p) PARAMS ((bfd *, bfd *));
+ boolean (*_bfd_slurp_armap) PARAMS ((bfd *));
+ boolean (*_bfd_slurp_extended_name_table) PARAMS ((bfd *));
+ void (*_bfd_truncate_arname) PARAMS ((bfd *, CONST char *, char *));
+ boolean (*write_armap) PARAMS ((bfd *arch,
+ unsigned int elength,
+ struct orl *map,
+ unsigned int orl_count,
+ int stridx));
+ boolean (*_close_and_cleanup) PARAMS ((bfd *));
+ boolean (*_bfd_set_section_contents) PARAMS ((bfd *, sec_ptr, PTR,
+ file_ptr, bfd_size_type));
+ boolean (*_bfd_get_section_contents) PARAMS ((bfd *, sec_ptr, PTR,
+ file_ptr, bfd_size_type));
+ boolean (*_new_section_hook) PARAMS ((bfd *, sec_ptr));
+ unsigned int (*_get_symtab_upper_bound) PARAMS ((bfd *));
+ unsigned int (*_bfd_canonicalize_symtab) PARAMS ((bfd *,
+ struct symbol_cache_entry **));
+ unsigned int (*_get_reloc_upper_bound) PARAMS ((bfd *, sec_ptr));
+ unsigned int (*_bfd_canonicalize_reloc) PARAMS ((bfd *, sec_ptr, arelent **,
+ struct symbol_cache_entry **));
+ struct symbol_cache_entry *
+ (*_bfd_make_empty_symbol) PARAMS ((bfd *));
+ void (*_bfd_print_symbol) PARAMS ((bfd *, PTR,
+ struct symbol_cache_entry *,
+ bfd_print_symbol_type));
+#define bfd_print_symbol(b,p,s,e) BFD_SEND(b, _bfd_print_symbol, (b,p,s,e))
+ void (*_bfd_get_symbol_info) PARAMS ((bfd *,
+ struct symbol_cache_entry *,
+ symbol_info *));
+#define bfd_get_symbol_info(b,p,e) BFD_SEND(b, _bfd_get_symbol_info, (b,p,e))
+ alent * (*_get_lineno) PARAMS ((bfd *, struct symbol_cache_entry *));
+
+ boolean (*_bfd_set_arch_mach) PARAMS ((bfd *, enum bfd_architecture,
+ unsigned long));
+
+ bfd * (*openr_next_archived_file) PARAMS ((bfd *arch, bfd *prev));
+
+ boolean (*_bfd_find_nearest_line) PARAMS ((bfd *abfd,
+ struct sec *section, struct symbol_cache_entry **symbols,
+ bfd_vma offset, CONST char **file, CONST char **func,
+ unsigned int *line));
+
+ int (*_bfd_stat_arch_elt) PARAMS ((bfd *, struct stat *));
+
+ int (*_bfd_sizeof_headers) PARAMS ((bfd *, boolean));
+
+ void (*_bfd_debug_info_start) PARAMS ((bfd *));
+ void (*_bfd_debug_info_end) PARAMS ((bfd *));
+ void (*_bfd_debug_info_accumulate) PARAMS ((bfd *, struct sec *));
+
+ bfd_byte * (*_bfd_get_relocated_section_contents) PARAMS ((bfd *,
+ struct bfd_seclet *, bfd_byte *data,
+ boolean relocateable));
+
+ boolean (*_bfd_relax_section) PARAMS ((bfd *, struct sec *,
+ struct symbol_cache_entry **));
+
+ boolean (*_bfd_seclet_link) PARAMS ((bfd *, PTR data,
+ boolean relocateable));
+ /* See documentation on reloc types. */
+ CONST struct reloc_howto_struct *
+ (*reloc_type_lookup) PARAMS ((bfd *abfd,
+ bfd_reloc_code_real_type code));
+
+ /* Back-door to allow format-aware applications to create debug symbols
+ while using BFD for everything else. Currently used by the assembler
+ when creating COFF files. */
+ asymbol * (*_bfd_make_debug_symbol) PARAMS ((
+ bfd *abfd,
+ void *ptr,
+ unsigned long size));
+ PTR backend_data;
+} bfd_target;
+bfd_target *
+bfd_find_target PARAMS ((CONST char *, bfd *));
+
+CONST char **
+bfd_target_list PARAMS ((void));
+
+boolean
+bfd_check_format PARAMS ((bfd *abfd, bfd_format format));
+
+boolean
+bfd_set_format PARAMS ((bfd *, bfd_format));
+
+CONST char *
+bfd_format_string PARAMS ((bfd_format));
+
+#endif
diff --git a/gnu/usr.bin/gdb/bfd/cache.c b/gnu/usr.bin/gdb/bfd/cache.c
new file mode 100644
index 0000000..f8cfd19
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/cache.c
@@ -0,0 +1,311 @@
+/* BFD library -- caching of file descriptors.
+ Copyright 1990, 1991, 1992 Free Software Foundation, Inc.
+ Hacked by Steve Chamberlain of Cygnus Support (steve@cygnus.com).
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+SECTION
+ File Caching
+
+ The file caching mechanism is embedded within BFD and allows
+ the application to open as many BFDs as it wants without
+ regard to the underlying operating system's file descriptor
+ limit (often as low as 20 open files). The module in
+ <<cache.c>> maintains a least recently used list of
+ <<BFD_CACHE_MAX_OPEN>> files, and exports the name
+ <<bfd_cache_lookup>> which runs around and makes sure that
+ the required BFD is open. If not, then it chooses a file to
+ close, closes it and opens the one wanted, returning its file
+ handle.
+
+*/
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+
+/*
+INTERNAL_FUNCTION
+ BFD_CACHE_MAX_OPEN macro
+
+DESCRIPTION
+ The maximum number of files which the cache will keep open at
+ one time.
+
+.#define BFD_CACHE_MAX_OPEN 10
+
+*/
+
+
+static boolean
+bfd_cache_delete PARAMS ((bfd *));
+
+/* Number of bfds on the chain. All such bfds have their file open;
+ if it closed, they get snipd()d from the chain. */
+
+static int open_files;
+
+static bfd *cache_sentinel; /* Chain of BFDs with active fds we've
+ opened */
+
+/*
+INTERNAL_FUNCTION
+ bfd_last_cache
+
+SYNOPSIS
+ extern bfd *bfd_last_cache;
+
+DESCRIPTION
+ Zero, or a pointer to the topmost BFD on the chain. This is
+ used by the <<bfd_cache_lookup>> macro in @file{libbfd.h} to
+ determine when it can avoid a function call.
+*/
+
+bfd *bfd_last_cache;
+
+/*
+ * INTERNAL_FUNCTION
+ * bfd_cache_lookup
+ *
+ * DESCRIPTION
+ * Checks to see if the required BFD is the same as the last one
+ * looked up. If so then it can use the iostream in the BFD with
+ * impunity, since it can't have changed since the last lookup,
+ * otherwise it has to perform the complicated lookup function
+ *
+ * .#define bfd_cache_lookup(x) \
+ * . ((x)==bfd_last_cache? \
+ * . (FILE*)(bfd_last_cache->iostream): \
+ * . bfd_cache_lookup_worker(x))
+ *
+ *
+ */
+
+static void
+DEFUN_VOID(close_one)
+{
+ bfd *kill = cache_sentinel;
+ if (kill == 0) /* Nothing in the cache */
+ return ;
+
+ /* We can only close files that want to play this game. */
+ while (!kill->cacheable) {
+ kill = kill->lru_prev;
+ if (kill == cache_sentinel) /* Nobody wants to play */
+ return ;
+ }
+
+ kill->where = ftell((FILE *)(kill->iostream));
+ (void) bfd_cache_delete(kill);
+}
+
+/* Cuts the BFD abfd out of the chain in the cache */
+static void
+DEFUN(snip,(abfd),
+ bfd *abfd)
+{
+ abfd->lru_prev->lru_next = abfd->lru_next;
+ abfd->lru_next->lru_prev = abfd->lru_prev;
+ if (cache_sentinel == abfd) cache_sentinel = (bfd *)NULL;
+}
+
+static boolean
+DEFUN(bfd_cache_delete,(abfd),
+ bfd *abfd)
+{
+ boolean ret;
+
+ if (fclose ((FILE *)(abfd->iostream)) == 0)
+ ret = true;
+ else
+ {
+ ret = false;
+ bfd_error = system_call_error;
+ }
+ snip (abfd);
+ abfd->iostream = NULL;
+ open_files--;
+ bfd_last_cache = 0;
+ return ret;
+}
+
+static bfd *
+DEFUN(insert,(x,y),
+ bfd *x AND
+ bfd *y)
+{
+ if (y) {
+ x->lru_next = y;
+ x->lru_prev = y->lru_prev;
+ y->lru_prev->lru_next = x;
+ y->lru_prev = x;
+
+ }
+ else {
+ x->lru_prev = x;
+ x->lru_next = x;
+ }
+ return x;
+}
+
+
+/* Initialize a BFD by putting it on the cache LRU. */
+
+void
+DEFUN(bfd_cache_init,(abfd),
+ bfd *abfd)
+{
+ if (open_files >= BFD_CACHE_MAX_OPEN)
+ close_one ();
+ cache_sentinel = insert(abfd, cache_sentinel);
+ ++open_files;
+}
+
+
+/*
+INTERNAL_FUNCTION
+ bfd_cache_close
+
+DESCRIPTION
+ Remove the BFD from the cache. If the attached file is open,
+ then close it too.
+
+SYNOPSIS
+ boolean bfd_cache_close (bfd *);
+
+RETURNS
+ <<false>> is returned if closing the file fails, <<true>> is
+ returned if all is well.
+*/
+boolean
+DEFUN(bfd_cache_close,(abfd),
+ bfd *abfd)
+{
+ /* If this file is open then remove from the chain */
+ if (abfd->iostream)
+ {
+ return bfd_cache_delete(abfd);
+ }
+ else
+ {
+ return true;
+ }
+}
+
+/*
+INTERNAL_FUNCTION
+ bfd_open_file
+
+DESCRIPTION
+ Call the OS to open a file for this BFD. Returns the FILE *
+ (possibly null) that results from this operation. Sets up the
+ BFD so that future accesses know the file is open. If the FILE
+ * returned is null, then there is won't have been put in the
+ cache, so it won't have to be removed from it.
+
+SYNOPSIS
+ FILE* bfd_open_file(bfd *);
+*/
+
+FILE *
+DEFUN(bfd_open_file, (abfd),
+ bfd *abfd)
+{
+ abfd->cacheable = true; /* Allow it to be closed later. */
+
+ if(open_files >= BFD_CACHE_MAX_OPEN) {
+ close_one();
+ }
+
+ switch (abfd->direction) {
+ case read_direction:
+ case no_direction:
+ abfd->iostream = (char *) fopen(abfd->filename, FOPEN_RB);
+ break;
+ case both_direction:
+ case write_direction:
+ if (abfd->opened_once == true) {
+ abfd->iostream = (char *) fopen(abfd->filename, FOPEN_RUB);
+ if (!abfd->iostream) {
+ abfd->iostream = (char *) fopen(abfd->filename, FOPEN_WUB);
+ }
+ } else {
+ /*open for creat */
+ abfd->iostream = (char *) fopen(abfd->filename, FOPEN_WB);
+ abfd->opened_once = true;
+ }
+ break;
+ }
+
+ if (abfd->iostream) {
+ bfd_cache_init (abfd);
+ }
+
+ return (FILE *)(abfd->iostream);
+}
+
+/*
+INTERNAL_FUNCTION
+ bfd_cache_lookup_worker
+
+DESCRIPTION
+ Called when the macro <<bfd_cache_lookup>> fails to find a
+ quick answer. Finds a file descriptor for this BFD. If
+ necessary, it open it. If there are already more than
+ BFD_CACHE_MAX_OPEN files open, it trys to close one first, to
+ avoid running out of file descriptors.
+
+SYNOPSIS
+ FILE *bfd_cache_lookup_worker(bfd *);
+
+*/
+
+FILE *
+DEFUN(bfd_cache_lookup_worker,(abfd),
+ bfd *abfd)
+{
+ if (abfd->my_archive)
+ {
+ abfd = abfd->my_archive;
+ }
+ /* Is this file already open .. if so then quick exit */
+ if (abfd->iostream)
+ {
+ if (abfd != cache_sentinel) {
+ /* Place onto head of lru chain */
+ snip (abfd);
+ cache_sentinel = insert(abfd, cache_sentinel);
+ }
+ }
+ /* This is a BFD without a stream -
+ so it must have been closed or never opened.
+ find an empty cache entry and use it. */
+ else
+ {
+
+ if (open_files >= BFD_CACHE_MAX_OPEN)
+ {
+ close_one();
+ }
+
+ BFD_ASSERT(bfd_open_file (abfd) != (FILE *)NULL) ;
+ fseek((FILE *)(abfd->iostream), abfd->where, false);
+ }
+ bfd_last_cache = abfd;
+ return (FILE *)(abfd->iostream);
+}
diff --git a/gnu/usr.bin/gdb/bfd/coffgen.c b/gnu/usr.bin/gdb/bfd/coffgen.c
new file mode 100644
index 0000000..94cc225d
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/coffgen.c
@@ -0,0 +1,1519 @@
+/* Support for the generic parts of COFF, for BFD.
+ Copyright 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+ Written by Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Most of this hacked by Steve Chamberlain, sac@cygnus.com.
+ Split out of coffcode.h by Ian Taylor, ian@cygnus.com. */
+
+/* This file contains COFF code that is not dependent on any
+ particular COFF target. There is only one version of this file in
+ libbfd.a, so no target specific code may be put in here. Or, to
+ put it another way,
+
+ ********** DO NOT PUT TARGET SPECIFIC CODE IN THIS FILE **********
+
+ If you need to add some target specific behaviour, add a new hook
+ function to bfd_coff_backend_data.
+
+ Some of these functions are also called by the ECOFF routines.
+ Those functions may not use any COFF specific information, such as
+ coff_data (abfd). */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "coff/internal.h"
+#include "seclet.h"
+#include "libcoff.h"
+
+static asection bfd_debug_section = { "*DEBUG*" };
+
+/* Take a section header read from a coff file (in HOST byte order),
+ and make a BFD "section" out of it. This is used by ECOFF. */
+static boolean
+DEFUN(make_a_section_from_file,(abfd, hdr, target_index),
+ bfd *abfd AND
+ struct internal_scnhdr *hdr AND
+ unsigned int target_index)
+{
+ asection *return_section;
+ char *name;
+
+ /* Assorted wastage to null-terminate the name, thanks AT&T! */
+ name = bfd_alloc(abfd, sizeof (hdr->s_name)+1);
+ if (name == NULL) {
+ bfd_error = no_memory;
+ return false;
+ }
+ strncpy(name, (char *) &hdr->s_name[0], sizeof (hdr->s_name));
+ name[sizeof (hdr->s_name)] = 0;
+
+ return_section = bfd_make_section(abfd, name);
+ if (return_section == NULL)
+ return_section = bfd_coff_make_section_hook (abfd, name);
+
+ /* Handle several sections of the same name. For example, if an executable
+ has two .bss sections, GDB better be able to find both of them
+ (PR 3562). */
+ if (return_section == NULL)
+ return_section = bfd_make_section_anyway (abfd, name);
+
+ if (return_section == NULL)
+ return false;
+
+ /* s_paddr is presumed to be = to s_vaddr */
+
+ return_section->vma = hdr->s_vaddr;
+ return_section->_raw_size = hdr->s_size;
+ return_section->filepos = hdr->s_scnptr;
+ return_section->rel_filepos = hdr->s_relptr;
+ return_section->reloc_count = hdr->s_nreloc;
+
+ bfd_coff_set_alignment_hook (abfd, return_section, hdr);
+
+ return_section->line_filepos = hdr->s_lnnoptr;
+
+ return_section->lineno_count = hdr->s_nlnno;
+ return_section->userdata = NULL;
+ return_section->next = (asection *) NULL;
+ return_section->flags = bfd_coff_styp_to_sec_flags_hook (abfd, hdr);
+
+ return_section->target_index = target_index;
+
+ /* At least on i386-coff, the line number count for a shared library
+ section must be ignored. */
+ if ((return_section->flags & SEC_SHARED_LIBRARY) != 0)
+ return_section->lineno_count = 0;
+
+ if (hdr->s_nreloc != 0)
+ return_section->flags |= SEC_RELOC;
+ /* FIXME: should this check 'hdr->s_size > 0' */
+ if (hdr->s_scnptr != 0)
+ return_section->flags |= SEC_HAS_CONTENTS;
+ return true;
+}
+
+/* Read in a COFF object and make it into a BFD. This is used by
+ ECOFF as well. */
+
+static
+bfd_target *
+DEFUN(coff_real_object_p,(abfd, nscns, internal_f, internal_a),
+ bfd *abfd AND
+ unsigned nscns AND
+ struct internal_filehdr *internal_f AND
+ struct internal_aouthdr *internal_a)
+{
+ PTR tdata;
+ size_t readsize; /* length of file_info */
+ unsigned int scnhsz;
+ char *external_sections;
+
+ /* Build a play area */
+ tdata = bfd_coff_mkobject_hook (abfd, (PTR) internal_f, (PTR) internal_a);
+ if (tdata == NULL)
+ return 0;
+
+ scnhsz = bfd_coff_scnhsz (abfd);
+ readsize = nscns * scnhsz;
+ external_sections = (char *)bfd_alloc(abfd, readsize);
+
+ if (bfd_read((PTR)external_sections, 1, readsize, abfd) != readsize) {
+ goto fail;
+ }
+
+ /* Now copy data as required; construct all asections etc */
+ if (nscns != 0) {
+ unsigned int i;
+ for (i = 0; i < nscns; i++) {
+ struct internal_scnhdr tmp;
+ bfd_coff_swap_scnhdr_in(abfd, (PTR) (external_sections + i * scnhsz),
+ (PTR) &tmp);
+ make_a_section_from_file(abfd,&tmp, i+1);
+ }
+ }
+
+/* make_abs_section(abfd);*/
+
+ if (bfd_coff_set_arch_mach_hook (abfd, (PTR) internal_f) == false)
+ goto fail;
+
+ if (!(internal_f->f_flags & F_RELFLG))
+ abfd->flags |= HAS_RELOC;
+ if ((internal_f->f_flags & F_EXEC))
+ abfd->flags |= EXEC_P;
+ if (!(internal_f->f_flags & F_LNNO))
+ abfd->flags |= HAS_LINENO;
+ if (!(internal_f->f_flags & F_LSYMS))
+ abfd->flags |= HAS_LOCALS;
+
+
+ bfd_get_symcount(abfd) = internal_f->f_nsyms;
+ if (internal_f->f_nsyms)
+ abfd->flags |= HAS_SYMS;
+
+ if (internal_a != (struct internal_aouthdr *) NULL)
+ bfd_get_start_address (abfd) = internal_a->entry;
+ else
+ bfd_get_start_address (abfd) = 0;
+
+ return abfd->xvec;
+ fail:
+ bfd_release(abfd, tdata);
+ return (bfd_target *)NULL;
+}
+
+/* Turn a COFF file into a BFD, but fail with wrong_format if it is
+ not a COFF file. This is also used by ECOFF. */
+
+bfd_target *
+DEFUN(coff_object_p,(abfd),
+ bfd *abfd)
+{
+ unsigned int filhsz;
+ unsigned int aoutsz;
+ int nscns;
+ PTR filehdr;
+ struct internal_filehdr internal_f;
+ struct internal_aouthdr internal_a;
+
+ bfd_error = system_call_error;
+
+ /* figure out how much to read */
+ filhsz = bfd_coff_filhsz (abfd);
+ aoutsz = bfd_coff_aoutsz (abfd);
+
+ filehdr = bfd_alloc (abfd, filhsz);
+ if (filehdr == NULL)
+ return 0;
+ if (bfd_read(filehdr, 1, filhsz, abfd) != filhsz)
+ return 0;
+ bfd_coff_swap_filehdr_in(abfd, filehdr, &internal_f);
+ bfd_release (abfd, filehdr);
+
+ if (bfd_coff_bad_format_hook (abfd, &internal_f) == false) {
+ bfd_error = wrong_format;
+ return 0;
+ }
+ nscns =internal_f.f_nscns;
+
+ if (internal_f.f_opthdr) {
+ PTR opthdr;
+
+ opthdr = bfd_alloc (abfd, aoutsz);
+ if (opthdr == NULL)
+ return 0;;
+ if (bfd_read(opthdr, 1,aoutsz, abfd) != aoutsz) {
+ return 0;
+ }
+ bfd_coff_swap_aouthdr_in(abfd, opthdr, (PTR)&internal_a);
+ }
+
+ /* Seek past the opt hdr stuff */
+ bfd_seek(abfd, (file_ptr) (internal_f.f_opthdr + filhsz), SEEK_SET);
+
+ return coff_real_object_p(abfd, nscns, &internal_f,
+ (internal_f.f_opthdr != 0
+ ? &internal_a
+ : (struct internal_aouthdr *) NULL));
+}
+
+/* Get the BFD section from a COFF symbol section number. */
+
+struct sec *
+DEFUN(coff_section_from_bfd_index,(abfd, index),
+ bfd *abfd AND
+ int index)
+{
+ struct sec *answer = abfd->sections;
+
+ if (index == N_ABS)
+ {
+ return &bfd_abs_section;
+ }
+ if (index == N_UNDEF)
+ {
+ return &bfd_und_section;
+ }
+ if(index == N_DEBUG)
+ {
+ return &bfd_debug_section;
+
+ }
+
+ while (answer) {
+ if (answer->target_index == index)
+ return answer;
+ answer = answer->next;
+ }
+ BFD_ASSERT(0);
+ return &bfd_und_section; /* For gcc -W and lint. Never executed. */
+}
+
+/* Get the upper bound of a COFF symbol table. */
+
+unsigned int
+coff_get_symtab_upper_bound(abfd)
+bfd *abfd;
+{
+ if (!bfd_coff_slurp_symbol_table(abfd))
+ return 0;
+
+ return (bfd_get_symcount(abfd) + 1) * (sizeof(coff_symbol_type *));
+}
+
+
+/* Canonicalize a COFF symbol table. */
+
+unsigned int
+DEFUN(coff_get_symtab, (abfd, alocation),
+ bfd *abfd AND
+ asymbol **alocation)
+{
+ unsigned int counter = 0;
+ coff_symbol_type *symbase;
+ coff_symbol_type **location = (coff_symbol_type **) (alocation);
+ if (!bfd_coff_slurp_symbol_table(abfd))
+ return 0;
+
+ symbase = obj_symbols(abfd);
+ while (counter < bfd_get_symcount(abfd))
+ {
+ /* This nasty code looks at the symbol to decide whether or
+ not it is descibes a constructor/destructor entry point. It
+ is structured this way to (hopefully) speed non matches */
+#if 0
+ if (0 && symbase->symbol.name[9] == '$')
+ {
+ bfd_constructor_entry(abfd,
+ (asymbol **)location,
+ symbase->symbol.name[10] == 'I' ?
+ "CTOR" : "DTOR");
+ }
+#endif
+ *(location++) = symbase++;
+ counter++;
+ }
+ *location++ = 0;
+ return bfd_get_symcount(abfd);
+}
+
+/* Set lineno_count for the output sections of a COFF file. */
+
+int
+DEFUN(coff_count_linenumbers,(abfd),
+ bfd *abfd)
+{
+ unsigned int limit = bfd_get_symcount(abfd);
+ unsigned int i;
+ int total = 0;
+ asymbol **p;
+ {
+ asection *s = abfd->sections->output_section;
+ while (s) {
+ BFD_ASSERT(s->lineno_count == 0);
+ s = s->next;
+ }
+ }
+
+
+ for (p = abfd->outsymbols, i = 0; i < limit; i++, p++) {
+ asymbol *q_maybe = *p;
+ if (bfd_asymbol_flavour(q_maybe) == bfd_target_coff_flavour) {
+ coff_symbol_type *q = coffsymbol(q_maybe);
+ if (q->lineno) {
+ /*
+ This symbol has a linenumber, increment the owning
+ section's linenumber count
+ */
+ alent *l = q->lineno;
+ q->symbol.section->output_section->lineno_count++;
+ total ++;
+ l++;
+ while (l->line_number) {
+ total ++;
+ q->symbol.section->output_section->lineno_count++;
+ l++;
+ }
+ }
+ }
+ }
+ return total;
+}
+
+/* Takes a bfd and a symbol, returns a pointer to the coff specific
+ area of the symbol if there is one. */
+
+coff_symbol_type *
+DEFUN(coff_symbol_from,(ignore_abfd, symbol),
+ bfd *ignore_abfd AND
+ asymbol *symbol)
+{
+ if (bfd_asymbol_flavour(symbol) != bfd_target_coff_flavour)
+ return (coff_symbol_type *)NULL;
+
+ if (bfd_asymbol_bfd(symbol)->tdata.coff_obj_data == (coff_data_type*)NULL)
+ return (coff_symbol_type *)NULL;
+
+ return (coff_symbol_type *) symbol;
+}
+
+static void
+DEFUN(fixup_symbol_value,(coff_symbol_ptr, syment),
+coff_symbol_type *coff_symbol_ptr AND
+struct internal_syment *syment)
+{
+
+ /* Normalize the symbol flags */
+ if (bfd_is_com_section (coff_symbol_ptr->symbol.section)) {
+ /* a common symbol is undefined with a value */
+ syment->n_scnum = N_UNDEF;
+ syment->n_value = coff_symbol_ptr->symbol.value;
+ }
+ else if (coff_symbol_ptr->symbol.flags & BSF_DEBUGGING) {
+ syment->n_value = coff_symbol_ptr->symbol.value;
+ }
+ else if (coff_symbol_ptr->symbol.section == & bfd_und_section) {
+ syment->n_scnum = N_UNDEF;
+ syment->n_value = 0;
+ }
+ else {
+ if (coff_symbol_ptr->symbol.section) {
+ syment->n_scnum =
+ coff_symbol_ptr->symbol.section->output_section->target_index;
+
+ syment->n_value =
+ coff_symbol_ptr->symbol.value +
+ coff_symbol_ptr->symbol.section->output_offset +
+ coff_symbol_ptr->symbol.section->output_section->vma;
+ }
+ else {
+ BFD_ASSERT(0);
+ /* This can happen, but I don't know why yet (steve@cygnus.com) */
+ syment->n_scnum = N_ABS;
+ syment->n_value = coff_symbol_ptr->symbol.value;
+ }
+ }
+}
+
+/* run through all the symbols in the symbol table and work out what
+ their indexes into the symbol table will be when output
+
+ Coff requires that each C_FILE symbol points to the next one in the
+ chain, and that the last one points to the first external symbol. We
+ do that here too.
+
+*/
+void
+DEFUN(coff_renumber_symbols,(bfd_ptr),
+ bfd *bfd_ptr)
+{
+ unsigned int symbol_count = bfd_get_symcount(bfd_ptr);
+ asymbol **symbol_ptr_ptr = bfd_ptr->outsymbols;
+ unsigned int native_index = 0;
+ struct internal_syment *last_file = (struct internal_syment *)NULL;
+ unsigned int symbol_index;
+
+ /* COFF demands that undefined symbols come after all other symbols.
+ Since we don't need to impose this extra knowledge on all our client
+ programs, deal with that here. Sort the symbol table; just move the
+ undefined symbols to the end, leaving the rest alone. */
+ /* @@ Do we have some condition we could test for, so we don't always
+ have to do this? I don't think relocatability is quite right, but
+ I'm not certain. [raeburn:19920508.1711EST] */
+ {
+ asymbol **newsyms;
+ int i;
+
+ newsyms = (asymbol **) bfd_alloc_by_size_t (bfd_ptr,
+ sizeof (asymbol *)
+ * (symbol_count + 1));
+ bfd_ptr->outsymbols = newsyms;
+ for (i = 0; i < symbol_count; i++)
+ if (symbol_ptr_ptr[i]->section != &bfd_und_section)
+ *newsyms++ = symbol_ptr_ptr[i];
+ for (i = 0; i < symbol_count; i++)
+ if (symbol_ptr_ptr[i]->section == &bfd_und_section)
+ *newsyms++ = symbol_ptr_ptr[i];
+ *newsyms = (asymbol *) NULL;
+ symbol_ptr_ptr = bfd_ptr->outsymbols;
+ }
+
+ for (symbol_index = 0; symbol_index < symbol_count; symbol_index++)
+ {
+ coff_symbol_type *coff_symbol_ptr = coff_symbol_from(bfd_ptr, symbol_ptr_ptr[symbol_index]);
+ if (coff_symbol_ptr && coff_symbol_ptr->native) {
+ combined_entry_type *s = coff_symbol_ptr->native;
+ int i;
+
+ if (s->u.syment.n_sclass == C_FILE)
+ {
+ if (last_file != (struct internal_syment *)NULL) {
+ last_file->n_value = native_index;
+ }
+ last_file = &(s->u.syment);
+ }
+ else {
+
+ /* Modify the symbol values according to their section and
+ type */
+
+ fixup_symbol_value(coff_symbol_ptr, &(s->u.syment));
+ }
+ for (i = 0; i < s->u.syment.n_numaux + 1; i++) {
+ s[i].offset = native_index ++;
+ }
+ }
+ else {
+ native_index++;
+ }
+ }
+ obj_conv_table_size (bfd_ptr) = native_index;
+}
+
+/*
+ Run thorough the symbol table again, and fix it so that all pointers to
+ entries are changed to the entries' index in the output symbol table.
+
+*/
+void
+DEFUN(coff_mangle_symbols,(bfd_ptr),
+ bfd *bfd_ptr)
+{
+ unsigned int symbol_count = bfd_get_symcount(bfd_ptr);
+ asymbol **symbol_ptr_ptr = bfd_ptr->outsymbols;
+ unsigned int symbol_index;
+
+ for (symbol_index = 0; symbol_index < symbol_count; symbol_index++)
+ {
+ coff_symbol_type *coff_symbol_ptr =
+ coff_symbol_from(bfd_ptr, symbol_ptr_ptr[symbol_index]);
+
+ if (coff_symbol_ptr && coff_symbol_ptr->native) {
+ int i;
+ combined_entry_type *s = coff_symbol_ptr->native;
+
+ for (i = 0; i < s->u.syment.n_numaux ; i++) {
+ combined_entry_type *a = s + i + 1;
+ if (a->fix_tag) {
+ a->u.auxent.x_sym.x_tagndx.l =
+ a->u.auxent.x_sym.x_tagndx.p->offset;
+ a->fix_tag = 0;
+ }
+ if (a->fix_end) {
+ a->u.auxent.x_sym.x_fcnary.x_fcn.x_endndx.l =
+ a->u.auxent.x_sym.x_fcnary.x_fcn.x_endndx.p->offset;
+ a->fix_end = 0;
+
+ }
+
+ }
+ }
+ }
+}
+
+static int string_size;
+
+static void
+DEFUN(coff_fix_symbol_name,(abfd, symbol, native),
+ bfd *abfd AND
+ asymbol *symbol AND
+ combined_entry_type *native)
+{
+ unsigned int name_length;
+ union internal_auxent *auxent;
+ char * name = ( char *)(symbol->name);
+
+ if (name == (char *) NULL) {
+ /* coff symbols always have names, so we'll make one up */
+ symbol->name = "strange";
+ name = (char *)symbol->name;
+ }
+ name_length = strlen(name);
+
+ if (native->u.syment.n_sclass == C_FILE) {
+ strncpy(native->u.syment._n._n_name, ".file", SYMNMLEN);
+ auxent = &(native+1)->u.auxent;
+
+ if (bfd_coff_long_filenames (abfd)) {
+ if (name_length <= FILNMLEN) {
+ strncpy(auxent->x_file.x_fname, name, FILNMLEN);
+ }
+ else {
+ auxent->x_file.x_n.x_offset = string_size + 4;
+ auxent->x_file.x_n.x_zeroes = 0;
+ string_size += name_length + 1;
+ }
+ }
+ else {
+ strncpy(auxent->x_file.x_fname, name, FILNMLEN);
+ if (name_length > FILNMLEN) {
+ name[FILNMLEN] = '\0';
+ }
+ }
+ }
+ else
+ { /* NOT A C_FILE SYMBOL */
+ if (name_length <= SYMNMLEN) {
+ /* This name will fit into the symbol neatly */
+ strncpy(native->u.syment._n._n_name, symbol->name, SYMNMLEN);
+ }
+ else {
+ native->u.syment._n._n_n._n_offset = string_size + 4;
+ native->u.syment._n._n_n._n_zeroes = 0;
+ string_size += name_length + 1;
+ }
+ }
+}
+
+#define set_index(symbol, idx) ((symbol)->udata =(PTR) (idx))
+
+static unsigned int
+DEFUN(coff_write_symbol,(abfd, symbol, native, written),
+bfd *abfd AND
+asymbol *symbol AND
+combined_entry_type *native AND
+unsigned int written)
+{
+ unsigned int numaux = native->u.syment.n_numaux;
+ int type = native->u.syment.n_type;
+ int class = native->u.syment.n_sclass;
+ PTR buf;
+ bfd_size_type symesz;
+
+ /* @@ bfd_debug_section isn't accessible outside this file, but we know
+ that C_FILE symbols belong there. So move them. */
+ if (native->u.syment.n_sclass == C_FILE)
+ symbol->section = &bfd_debug_section;
+
+ if (symbol->section == &bfd_abs_section)
+ {
+ native->u.syment.n_scnum = N_ABS;
+ }
+ else if (symbol->section == &bfd_debug_section)
+ {
+ native->u.syment.n_scnum = N_DEBUG;
+ }
+ else if (symbol->section == &bfd_und_section)
+ {
+ native->u.syment.n_scnum = N_UNDEF;
+ }
+ else
+ {
+ native->u.syment.n_scnum =
+ symbol->section->output_section->target_index;
+ }
+
+
+ coff_fix_symbol_name(abfd, symbol, native);
+
+ symesz = bfd_coff_symesz (abfd);
+ buf = bfd_alloc (abfd, symesz);
+ bfd_coff_swap_sym_out(abfd, &native->u.syment, buf);
+ bfd_write(buf, 1, symesz, abfd);
+ bfd_release (abfd, buf);
+
+ if (native->u.syment.n_numaux > 0)
+ {
+ bfd_size_type auxesz;
+ unsigned int j;
+
+ auxesz = bfd_coff_auxesz (abfd);
+ buf = bfd_alloc (abfd, auxesz);
+ for (j = 0; j < native->u.syment.n_numaux; j++)
+ {
+ bfd_coff_swap_aux_out(abfd,
+ &((native + j + 1)->u.auxent),
+ type,
+ class,
+ buf);
+ bfd_write(buf, 1, auxesz, abfd);
+ }
+ bfd_release (abfd, buf);
+ }
+ /*
+ Reuse somewhere in the symbol to keep the index
+ */
+ set_index(symbol, written);
+ return written + 1 + numaux;
+}
+
+
+static unsigned int
+DEFUN(coff_write_alien_symbol,(abfd, symbol, written),
+ bfd *abfd AND
+ asymbol *symbol AND
+ unsigned int written)
+{
+ /*
+ This symbol has been created by the loader, or come from a non
+ coff format. It has no native element to inherit, make our
+ own
+ */
+ combined_entry_type *native;
+ combined_entry_type dummy;
+ native = &dummy;
+ native->u.syment.n_type = T_NULL;
+ native->u.syment.n_flags = 0;
+ if (symbol->section == &bfd_und_section)
+ {
+ native->u.syment.n_scnum = N_UNDEF;
+ native->u.syment.n_value = symbol->value;
+ }
+ else if (bfd_is_com_section (symbol->section))
+ {
+ native->u.syment.n_scnum = N_UNDEF;
+ native->u.syment.n_value = symbol->value;
+
+ }
+
+ else if (symbol->flags & BSF_DEBUGGING) {
+ /*
+ remove name so it doesn't take up any space
+ */
+ symbol->name = "";
+ }
+ else {
+ native->u.syment.n_scnum = symbol->section->output_section->target_index;
+ native->u.syment.n_value = symbol->value +
+ symbol->section->output_section->vma +
+ symbol->section->output_offset;
+ /* Copy the any flags from the the file hdr into the symbol */
+ {
+ coff_symbol_type *c = coff_symbol_from(abfd, symbol);
+ if (c != (coff_symbol_type *)NULL) {
+ native->u.syment.n_flags = bfd_asymbol_bfd(&c->symbol)->flags;
+ }
+ }
+ }
+
+ native->u.syment.n_type = 0;
+ if (symbol->flags & BSF_LOCAL)
+ native->u.syment.n_sclass = C_STAT;
+ else
+ native->u.syment.n_sclass = C_EXT;
+ native->u.syment.n_numaux = 0;
+
+ return coff_write_symbol(abfd, symbol, native, written);
+}
+
+static unsigned int
+DEFUN(coff_write_native_symbol,(abfd, symbol, written),
+bfd *abfd AND
+coff_symbol_type *symbol AND
+unsigned int written)
+{
+ /*
+ Does this symbol have an ascociated line number - if so then
+ make it remember this symbol index. Also tag the auxent of
+ this symbol to point to the right place in the lineno table
+ */
+ combined_entry_type *native = symbol->native;
+
+ alent *lineno = symbol->lineno;
+
+ if (lineno && !symbol->done_lineno) {
+ unsigned int count = 0;
+ lineno[count].u.offset = written;
+ if (native->u.syment.n_numaux) {
+ union internal_auxent *a = &((native+1)->u.auxent);
+
+ a->x_sym.x_fcnary.x_fcn.x_lnnoptr =
+ symbol->symbol.section->output_section->moving_line_filepos;
+ }
+ /*
+ And count and relocate all other linenumbers
+ */
+
+ count++;
+ while (lineno[count].line_number) {
+#if 0
+/* 13 april 92. sac
+I've been told this, but still need proof:
+> The second bug is also in `bfd/coffcode.h'. This bug causes the linker to screw
+> up the pc-relocations for all the line numbers in COFF code. This bug isn't
+> only specific to A29K implementations, but affects all systems using COFF
+> format binaries. Note that in COFF object files, the line number core offsets
+> output by the assembler are relative to the start of each procedure, not
+> to the start of the .text section. This patch relocates the line numbers
+> relative to the `native->u.syment.n_value' instead of the section virtual
+> address. modular!olson@cs.arizona.edu (Jon Olson)
+*/
+ lineno[count].u.offset += native->u.syment.n_value;
+
+#else
+ lineno[count].u.offset +=
+ symbol->symbol.section->output_section->vma +
+ symbol->symbol.section->output_offset;
+#endif
+ count++;
+ }
+ symbol->done_lineno = true;
+
+ symbol->symbol.section->output_section->moving_line_filepos +=
+ count * bfd_coff_linesz (abfd);
+ }
+ return coff_write_symbol(abfd, &( symbol->symbol), native,written);
+}
+
+void
+DEFUN(coff_write_symbols,(abfd),
+ bfd *abfd)
+{
+ unsigned int i;
+ unsigned int limit = bfd_get_symcount(abfd);
+ unsigned int written = 0;
+
+ asymbol **p;
+
+ string_size = 0;
+
+
+ /* Seek to the right place */
+ bfd_seek(abfd, obj_sym_filepos(abfd), SEEK_SET);
+
+ /* Output all the symbols we have */
+
+ written = 0;
+ for (p = abfd->outsymbols, i = 0; i < limit; i++, p++)
+ {
+ asymbol *symbol = *p;
+ coff_symbol_type *c_symbol = coff_symbol_from(abfd, symbol);
+
+ if (c_symbol == (coff_symbol_type *) NULL ||
+ c_symbol->native == (combined_entry_type *)NULL)
+ {
+ written = coff_write_alien_symbol(abfd, symbol, written);
+ }
+ else
+ {
+ written = coff_write_native_symbol(abfd, c_symbol, written);
+ }
+
+ }
+
+ bfd_get_symcount(abfd) = written;
+
+ /* Now write out strings */
+
+ if (string_size != 0)
+ {
+ unsigned int size = string_size + 4;
+ bfd_byte buffer[4];
+
+ bfd_h_put_32(abfd, size, buffer);
+ bfd_write((PTR) buffer, 1, sizeof(buffer), abfd);
+ for (p = abfd->outsymbols, i = 0;
+ i < limit;
+ i++, p++)
+ {
+ asymbol *q = *p;
+ size_t name_length = strlen(q->name);
+ int maxlen;
+ coff_symbol_type* c_symbol = coff_symbol_from(abfd, q);
+ maxlen = ((c_symbol != NULL && c_symbol->native != NULL) &&
+ (c_symbol->native->u.syment.n_sclass == C_FILE)) ?
+ FILNMLEN : SYMNMLEN;
+
+ if (name_length > maxlen) {
+ bfd_write((PTR) (q->name), 1, name_length + 1, abfd);
+ }
+ }
+ }
+ else {
+ /* We would normally not write anything here, but we'll write
+ out 4 so that any stupid coff reader which tries to read
+ the string table even when there isn't one won't croak. */
+ unsigned int size = 4;
+ bfd_byte buffer[4];
+
+ bfd_h_put_32 (abfd, size, buffer);
+ bfd_write((PTR) buffer, 1, sizeof (buffer), abfd);
+ }
+}
+
+void
+DEFUN(coff_write_linenumbers,(abfd),
+ bfd *abfd)
+{
+ asection *s;
+ bfd_size_type linesz;
+ PTR buff;
+
+ linesz = bfd_coff_linesz (abfd);
+ buff = bfd_alloc (abfd, linesz);
+ for (s = abfd->sections; s != (asection *) NULL; s = s->next) {
+ if (s->lineno_count) {
+ asymbol **q = abfd->outsymbols;
+ bfd_seek(abfd, s->line_filepos, SEEK_SET);
+ /* Find all the linenumbers in this section */
+ while (*q) {
+ asymbol *p = *q;
+ if (p->section->output_section == s) {
+ alent *l =
+ BFD_SEND(bfd_asymbol_bfd(p), _get_lineno, (bfd_asymbol_bfd(p), p));
+ if (l) {
+ /* Found a linenumber entry, output */
+ struct internal_lineno out;
+ memset( (PTR)&out, 0, sizeof(out));
+ out.l_lnno = 0;
+ out.l_addr.l_symndx = l->u.offset;
+ bfd_coff_swap_lineno_out(abfd, &out, buff);
+ bfd_write(buff, 1, linesz, abfd);
+ l++;
+ while (l->line_number) {
+ out.l_lnno = l->line_number;
+ out.l_addr.l_symndx = l->u.offset;
+ bfd_coff_swap_lineno_out(abfd, &out, buff);
+ bfd_write(buff, 1, linesz, abfd);
+ l++;
+ }
+ }
+ }
+ q++;
+ }
+ }
+ }
+ bfd_release (abfd, buff);
+}
+
+alent *
+DEFUN(coff_get_lineno,(ignore_abfd, symbol),
+ bfd *ignore_abfd AND
+ asymbol *symbol)
+{
+ return coffsymbol(symbol)->lineno;
+}
+
+asymbol *
+coff_section_symbol (abfd, name)
+ bfd *abfd;
+ char *name;
+{
+ asection *sec = bfd_make_section_old_way (abfd, name);
+ asymbol *sym;
+ combined_entry_type *csym;
+
+ sym = sec->symbol;
+ csym = coff_symbol_from (abfd, sym)->native;
+ /* Make sure back-end COFF stuff is there. */
+ if (csym == 0)
+ {
+ struct foo {
+ coff_symbol_type sym;
+ /* @@FIXME This shouldn't use a fixed size!! */
+ combined_entry_type e[10];
+ };
+ struct foo *f;
+ f = (struct foo *) bfd_alloc_by_size_t (abfd, sizeof (*f));
+ memset ((char *) f, 0, sizeof (*f));
+ coff_symbol_from (abfd, sym)->native = csym = f->e;
+ }
+ csym[0].u.syment.n_sclass = C_STAT;
+ csym[0].u.syment.n_numaux = 1;
+/* SF_SET_STATICS (sym); @@ ??? */
+ csym[1].u.auxent.x_scn.x_scnlen = sec->_raw_size;
+ csym[1].u.auxent.x_scn.x_nreloc = sec->reloc_count;
+ csym[1].u.auxent.x_scn.x_nlinno = sec->lineno_count;
+
+ if (sec->output_section == NULL)
+ {
+ sec->output_section = sec;
+ sec->output_offset = 0;
+ }
+
+ return sym;
+}
+
+/* This function transforms the offsets into the symbol table into
+ pointers to syments. */
+
+static void
+DEFUN(coff_pointerize_aux,(abfd, table_base, type, class, auxent),
+bfd *abfd AND
+combined_entry_type *table_base AND
+int type AND
+int class AND
+combined_entry_type *auxent)
+{
+ /* Don't bother if this is a file or a section */
+ if (class == C_STAT && type == T_NULL) return;
+ if (class == C_FILE) return;
+
+ /* Otherwise patch up */
+#define N_TMASK coff_data (abfd)->local_n_tmask
+#define N_BTSHFT coff_data (abfd)->local_n_btshft
+ if (ISFCN(type) || ISTAG(class) || class == C_BLOCK) {
+ auxent->u.auxent.x_sym.x_fcnary.x_fcn.x_endndx.p = table_base +
+ auxent->u.auxent.x_sym.x_fcnary.x_fcn.x_endndx.l;
+ auxent->fix_end = 1;
+ }
+ /* A negative tagndx is meaningless, but the SCO 3.2v4 cc can
+ generate one, so we must be careful to ignore it. */
+ if (auxent->u.auxent.x_sym.x_tagndx.l > 0) {
+ auxent->u.auxent.x_sym.x_tagndx.p =
+ table_base + auxent->u.auxent.x_sym.x_tagndx.l;
+ auxent->fix_tag = 1;
+ }
+}
+
+static char *
+DEFUN(build_string_table,(abfd),
+bfd *abfd)
+{
+ char string_table_size_buffer[4];
+ unsigned int string_table_size;
+ char *string_table;
+
+ /* At this point we should be "seek"'d to the end of the
+ symbols === the symbol table size. */
+ if (bfd_read((char *) string_table_size_buffer,
+ sizeof(string_table_size_buffer),
+ 1, abfd) != sizeof(string_table_size)) {
+ bfd_error = system_call_error;
+ return (NULL);
+ } /* on error */
+
+ string_table_size = bfd_h_get_32(abfd, (bfd_byte *) string_table_size_buffer);
+
+ if ((string_table = (PTR) bfd_alloc(abfd, string_table_size -= 4)) == NULL) {
+ bfd_error = no_memory;
+ return (NULL);
+ } /* on mallocation error */
+ if (bfd_read(string_table, string_table_size, 1, abfd) != string_table_size) {
+ bfd_error = system_call_error;
+ return (NULL);
+ }
+ return string_table;
+}
+
+/* Allocate space for the ".debug" section, and read it.
+ We did not read the debug section until now, because
+ we didn't want to go to the trouble until someone needed it. */
+
+static char *
+DEFUN(build_debug_section,(abfd),
+ bfd *abfd)
+{
+ char *debug_section;
+ long position;
+
+ asection *sect = bfd_get_section_by_name (abfd, ".debug");
+
+ if (!sect) {
+ bfd_error = no_debug_section;
+ return NULL;
+ }
+
+ debug_section = (PTR) bfd_alloc (abfd,
+ bfd_get_section_size_before_reloc (sect));
+ if (debug_section == NULL) {
+ bfd_error = no_memory;
+ return NULL;
+ }
+
+ /* Seek to the beginning of the `.debug' section and read it.
+ Save the current position first; it is needed by our caller.
+ Then read debug section and reset the file pointer. */
+
+ position = bfd_tell (abfd);
+ bfd_seek (abfd, sect->filepos, SEEK_SET);
+ if (bfd_read (debug_section,
+ bfd_get_section_size_before_reloc (sect), 1, abfd)
+ != bfd_get_section_size_before_reloc(sect)) {
+ bfd_error = system_call_error;
+ return NULL;
+ }
+ bfd_seek (abfd, position, SEEK_SET);
+ return debug_section;
+}
+
+
+/* Return a pointer to a malloc'd copy of 'name'. 'name' may not be
+ \0-terminated, but will not exceed 'maxlen' characters. The copy *will*
+ be \0-terminated. */
+static char *
+DEFUN(copy_name,(abfd, name, maxlen),
+ bfd *abfd AND
+ char *name AND
+ int maxlen)
+{
+ int len;
+ char *newname;
+
+ for (len = 0; len < maxlen; ++len) {
+ if (name[len] == '\0') {
+ break;
+ }
+ }
+
+ if ((newname = (PTR) bfd_alloc(abfd, len+1)) == NULL) {
+ bfd_error = no_memory;
+ return (NULL);
+ }
+ strncpy(newname, name, len);
+ newname[len] = '\0';
+ return newname;
+}
+
+/* Read a symbol table into freshly bfd_allocated memory, swap it, and
+ knit the symbol names into a normalized form. By normalized here I
+ mean that all symbols have an n_offset pointer that points to a null-
+ terminated string. */
+
+combined_entry_type *
+DEFUN(coff_get_normalized_symtab,(abfd),
+bfd *abfd)
+{
+ combined_entry_type *internal;
+ combined_entry_type *internal_ptr;
+ combined_entry_type *symbol_ptr;
+ combined_entry_type *internal_end;
+ bfd_size_type symesz;
+ PTR raw;
+ char *raw_src;
+ char *raw_end;
+ char *string_table = NULL;
+ char *debug_section = NULL;
+ unsigned long size;
+
+ unsigned int raw_size;
+ if (obj_raw_syments(abfd) != (combined_entry_type *)NULL) {
+ return obj_raw_syments(abfd);
+ }
+ if ((size = bfd_get_symcount(abfd) * sizeof(combined_entry_type)) == 0) {
+ bfd_error = no_symbols;
+ return (NULL);
+ }
+
+ internal = (combined_entry_type *)bfd_alloc(abfd, size);
+ internal_end = internal + bfd_get_symcount(abfd);
+
+ symesz = bfd_coff_symesz (abfd);
+ raw_size = bfd_get_symcount(abfd) * symesz;
+ raw = bfd_alloc(abfd,raw_size);
+
+ if (bfd_seek(abfd, obj_sym_filepos(abfd), SEEK_SET) == -1
+ || bfd_read(raw, raw_size, 1, abfd) != raw_size) {
+ bfd_error = system_call_error;
+ return (NULL);
+ }
+ /* mark the end of the symbols */
+ raw_end = (char *) raw + bfd_get_symcount(abfd) * symesz;
+ /*
+ FIXME SOMEDAY. A string table size of zero is very weird, but
+ probably possible. If one shows up, it will probably kill us.
+ */
+
+ /* Swap all the raw entries */
+ for (raw_src = (char *) raw, internal_ptr = internal;
+ raw_src < raw_end;
+ raw_src += symesz, internal_ptr++) {
+
+ unsigned int i;
+ bfd_coff_swap_sym_in(abfd, (PTR)raw_src, (PTR)&internal_ptr->u.syment);
+ internal_ptr->fix_tag = 0;
+ internal_ptr->fix_end = 0;
+ symbol_ptr = internal_ptr;
+
+ for (i = 0;
+ i < symbol_ptr->u.syment.n_numaux;
+ i++)
+ {
+ internal_ptr++;
+ raw_src += symesz;
+
+ internal_ptr->fix_tag = 0;
+ internal_ptr->fix_end = 0;
+ bfd_coff_swap_aux_in(abfd, (PTR) raw_src,
+ symbol_ptr->u.syment.n_type,
+ symbol_ptr->u.syment.n_sclass,
+ &(internal_ptr->u.auxent));
+ /* Remember that bal entries arn't pointerized */
+ if (i != 1 || symbol_ptr->u.syment.n_sclass != C_LEAFPROC)
+ {
+
+ coff_pointerize_aux(abfd,
+ internal,
+ symbol_ptr->u.syment.n_type,
+ symbol_ptr->u.syment.n_sclass,
+ internal_ptr);
+ }
+
+ }
+ }
+
+ /* Free all the raw stuff */
+ bfd_release(abfd, raw);
+
+ for (internal_ptr = internal; internal_ptr < internal_end;
+ internal_ptr ++)
+ {
+ if (internal_ptr->u.syment.n_sclass == C_FILE) {
+ /* make a file symbol point to the name in the auxent, since
+ the text ".file" is redundant */
+ if ((internal_ptr+1)->u.auxent.x_file.x_n.x_zeroes == 0) {
+ /* the filename is a long one, point into the string table */
+ if (string_table == NULL) {
+ string_table = build_string_table(abfd);
+ }
+
+ internal_ptr->u.syment._n._n_n._n_offset =
+ (long) (string_table - 4 +
+ (internal_ptr+1)->u.auxent.x_file.x_n.x_offset);
+ }
+ else {
+ /* ordinary short filename, put into memory anyway */
+ internal_ptr->u.syment._n._n_n._n_offset = (long)
+ copy_name(abfd, (internal_ptr+1)->u.auxent.x_file.x_fname,
+ FILNMLEN);
+ }
+ }
+ else {
+ if (internal_ptr->u.syment._n._n_n._n_zeroes != 0) {
+ /* This is a "short" name. Make it long. */
+ unsigned long i = 0;
+ char *newstring = NULL;
+
+ /* find the length of this string without walking into memory
+ that isn't ours. */
+ for (i = 0; i < 8; ++i) {
+ if (internal_ptr->u.syment._n._n_name[i] == '\0') {
+ break;
+ } /* if end of string */
+ } /* possible lengths of this string. */
+
+ if ((newstring = (PTR) bfd_alloc(abfd, ++i)) == NULL) {
+ bfd_error = no_memory;
+ return (NULL);
+ } /* on error */
+ memset(newstring, 0, i);
+ strncpy(newstring, internal_ptr->u.syment._n._n_name, i-1);
+ internal_ptr->u.syment._n._n_n._n_offset = (long int) newstring;
+ internal_ptr->u.syment._n._n_n._n_zeroes = 0;
+ }
+ else if (!bfd_coff_symname_in_debug(abfd, &internal_ptr->u.syment)) {
+ /* Long name already. Point symbol at the string in the table. */
+ if (string_table == NULL) {
+ string_table = build_string_table(abfd);
+ }
+ internal_ptr->u.syment._n._n_n._n_offset = (long int)
+ (string_table - 4 + internal_ptr->u.syment._n._n_n._n_offset);
+ }
+ else {
+ /* Long name in debug section. Very similar. */
+ if (debug_section == NULL) {
+ debug_section = build_debug_section(abfd);
+ }
+ internal_ptr->u.syment._n._n_n._n_offset = (long int)
+ (debug_section + internal_ptr->u.syment._n._n_n._n_offset);
+ }
+ }
+ internal_ptr += internal_ptr->u.syment.n_numaux;
+ }
+
+ obj_raw_syments(abfd) = internal;
+ obj_raw_syment_count(abfd) = internal_ptr - internal;
+
+ return (internal);
+} /* coff_get_normalized_symtab() */
+
+unsigned int
+DEFUN (coff_get_reloc_upper_bound, (abfd, asect),
+ bfd *abfd AND
+ sec_ptr asect)
+{
+ if (bfd_get_format(abfd) != bfd_object) {
+ bfd_error = invalid_operation;
+ return 0;
+ }
+ return (asect->reloc_count + 1) * sizeof(arelent *);
+}
+
+asymbol *
+DEFUN (coff_make_empty_symbol, (abfd),
+ bfd *abfd)
+{
+ coff_symbol_type *new = (coff_symbol_type *) bfd_alloc(abfd, sizeof(coff_symbol_type));
+ if (new == NULL) {
+ bfd_error = no_memory;
+ return (NULL);
+ } /* on error */
+ new->symbol.section = 0;
+ new->native = 0;
+ new->lineno = (alent *) NULL;
+ new->done_lineno = false;
+ new->symbol.the_bfd = abfd;
+ return &new->symbol;
+}
+
+/* Make a debugging symbol. */
+
+asymbol *
+coff_bfd_make_debug_symbol (abfd, ptr, sz)
+ bfd *abfd;
+ PTR ptr;
+ unsigned long sz;
+{
+ coff_symbol_type *new = (coff_symbol_type *) bfd_alloc(abfd, sizeof(coff_symbol_type));
+ if (new == NULL) {
+ bfd_error = no_memory;
+ return (NULL);
+ } /* on error */
+ /* @@ This shouldn't be using a constant multiplier. */
+ new->native = (combined_entry_type *) bfd_zalloc (abfd, sizeof (combined_entry_type) * 10);
+ new->symbol.section = &bfd_debug_section;
+ new->lineno = (alent *) NULL;
+ new->done_lineno = false;
+ new->symbol.the_bfd = abfd;
+ return &new->symbol;
+}
+
+void
+coff_get_symbol_info (abfd, symbol, ret)
+ bfd *abfd;
+ asymbol *symbol;
+ symbol_info *ret;
+{
+ bfd_symbol_info (symbol, ret);
+}
+
+/* Print out information about COFF symbol. */
+
+void
+coff_print_symbol (abfd, filep, symbol, how)
+ bfd *abfd;
+ PTR filep;
+ asymbol *symbol;
+ bfd_print_symbol_type how;
+{
+ FILE *file = (FILE *) filep;
+
+ switch (how)
+ {
+ case bfd_print_symbol_name:
+ fprintf (file, "%s", symbol->name);
+ break;
+
+ case bfd_print_symbol_more:
+ fprintf (file, "coff %s %s",
+ coffsymbol(symbol)->native ? "n" : "g",
+ coffsymbol(symbol)->lineno ? "l" : " ");
+ break;
+
+ case bfd_print_symbol_all:
+ if (coffsymbol(symbol)->native)
+ {
+ unsigned int aux;
+ combined_entry_type *combined = coffsymbol (symbol)->native;
+ combined_entry_type *root = obj_raw_syments (abfd);
+ struct lineno_cache_entry *l = coffsymbol(symbol)->lineno;
+
+ fprintf (file,"[%3d]", combined - root);
+
+ fprintf (file,
+ "(sc %2d)(fl 0x%02x)(ty %3x)(sc %3d) (nx %d) 0x%08x %s",
+ combined->u.syment.n_scnum,
+ combined->u.syment.n_flags,
+ combined->u.syment.n_type,
+ combined->u.syment.n_sclass,
+ combined->u.syment.n_numaux,
+ combined->u.syment.n_value,
+ symbol->name);
+
+ for (aux = 0; aux < combined->u.syment.n_numaux; aux++)
+ {
+ combined_entry_type *auxp = combined + aux + 1;
+ long tagndx;
+
+ if (auxp->fix_tag)
+ tagndx = auxp->u.auxent.x_sym.x_tagndx.p - root;
+ else
+ tagndx = auxp->u.auxent.x_sym.x_tagndx.l;
+
+ fprintf (file, "\n");
+ switch (combined->u.syment.n_sclass)
+ {
+ case C_FILE:
+ fprintf (file, "File ");
+ break;
+ default:
+
+ fprintf (file, "AUX lnno %d size 0x%x tagndx %d",
+ auxp->u.auxent.x_sym.x_misc.x_lnsz.x_lnno,
+ auxp->u.auxent.x_sym.x_misc.x_lnsz.x_size,
+ tagndx);
+ break;
+ }
+ }
+
+ if (l)
+ {
+ fprintf (file, "\n%s :", l->u.sym->name);
+ l++;
+ while (l->line_number)
+ {
+ fprintf (file, "\n%4d : 0x%x",
+ l->line_number,
+ l->u.offset + symbol->section->vma);
+ l++;
+ }
+ }
+ }
+ else
+ {
+ bfd_print_symbol_vandf ((PTR) file, symbol);
+ fprintf (file, " %-5s %s %s %s",
+ symbol->section->name,
+ coffsymbol(symbol)->native ? "n" : "g",
+ coffsymbol(symbol)->lineno ? "l" : " ",
+ symbol->name);
+ }
+ }
+}
+
+/* Provided a BFD, a section and an offset into the section, calculate
+ and return the name of the source file and the line nearest to the
+ wanted location. */
+
+boolean
+DEFUN(coff_find_nearest_line,(abfd,
+ section,
+ ignore_symbols,
+ offset,
+ filename_ptr,
+ functionname_ptr,
+ line_ptr),
+ bfd *abfd AND
+ asection *section AND
+ asymbol **ignore_symbols AND
+ bfd_vma offset AND
+ CONST char **filename_ptr AND
+ CONST char **functionname_ptr AND
+ unsigned int *line_ptr)
+{
+ static bfd *cache_abfd;
+ static asection *cache_section;
+ static bfd_vma cache_offset;
+ static unsigned int cache_i;
+ static CONST char *cache_function;
+ static unsigned int line_base = 0;
+
+ unsigned int i = 0;
+ coff_data_type *cof = coff_data(abfd);
+ /* Run through the raw syments if available */
+ combined_entry_type *p;
+ alent *l;
+
+
+ *filename_ptr = 0;
+ *functionname_ptr = 0;
+ *line_ptr = 0;
+
+ /* Don't try and find line numbers in a non coff file */
+ if (abfd->xvec->flavour != bfd_target_coff_flavour)
+ return false;
+
+ if (cof == NULL)
+ return false;
+
+ p = cof->raw_syments;
+
+ for (i = 0; i < cof->raw_syment_count; i++) {
+ if (p->u.syment.n_sclass == C_FILE) {
+ /* File name has been moved into symbol */
+ *filename_ptr = (char *) p->u.syment._n._n_n._n_offset;
+ break;
+ }
+ p += 1 + p->u.syment.n_numaux;
+ }
+ /* Now wander though the raw linenumbers of the section */
+ /*
+ If this is the same BFD as we were previously called with and this is
+ the same section, and the offset we want is further down then we can
+ prime the lookup loop
+ */
+ if (abfd == cache_abfd &&
+ section == cache_section &&
+ offset >= cache_offset) {
+ i = cache_i;
+ *functionname_ptr = cache_function;
+ }
+ else {
+ i = 0;
+ }
+ l = &section->lineno[i];
+
+ for (; i < section->lineno_count; i++) {
+ if (l->line_number == 0) {
+ /* Get the symbol this line number points at */
+ coff_symbol_type *coff = (coff_symbol_type *) (l->u.sym);
+ if (coff->symbol.value > offset)
+ break;
+ *functionname_ptr = coff->symbol.name;
+ if (coff->native) {
+ combined_entry_type *s = coff->native;
+ s = s + 1 + s->u.syment.n_numaux;
+ /*
+ S should now point to the .bf of the function
+ */
+ if (s->u.syment.n_numaux) {
+ /*
+ The linenumber is stored in the auxent
+ */
+ union internal_auxent *a = &((s + 1)->u.auxent);
+ line_base = a->x_sym.x_misc.x_lnsz.x_lnno;
+ *line_ptr = line_base;
+ }
+ }
+ }
+ else {
+ if (l->u.offset > offset)
+ break;
+ *line_ptr = l->line_number + line_base - 1;
+ }
+ l++;
+ }
+
+ cache_abfd = abfd;
+ cache_section = section;
+ cache_offset = offset;
+ cache_i = i;
+ cache_function = *functionname_ptr;
+
+ return true;
+}
+
+int
+DEFUN(coff_sizeof_headers,(abfd, reloc),
+ bfd *abfd AND
+ boolean reloc)
+{
+ size_t size;
+
+ if (reloc == false) {
+ size = bfd_coff_filhsz (abfd) + bfd_coff_aoutsz (abfd);
+ }
+ else {
+ size = bfd_coff_filhsz (abfd);
+ }
+
+ size += abfd->section_count * bfd_coff_scnhsz (abfd);
+ return size;
+}
diff --git a/gnu/usr.bin/gdb/bfd/core.c b/gnu/usr.bin/gdb/bfd/core.c
new file mode 100644
index 0000000..c428775
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/core.c
@@ -0,0 +1,106 @@
+/* Core file generic interface routines for BFD.
+ Copyright (C) 1990-1991 Free Software Foundation, Inc.
+ Written by Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+SECTION
+ Core files
+
+DESCRIPTION
+ Buff output this facinating topic
+*/
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+
+
+/*
+FUNCTION
+ bfd_core_file_failing_command
+
+SYNOPSIS
+ CONST char *bfd_core_file_failing_command(bfd *);
+
+DESCRIPTION
+ Returns a read-only string explaining what program was running
+ when it failed and produced the core file being read
+
+*/
+
+CONST char *
+DEFUN(bfd_core_file_failing_command,(abfd),
+ bfd *abfd)
+{
+ if (abfd->format != bfd_core) {
+ bfd_error = invalid_operation;
+ return NULL;
+ }
+ return BFD_SEND (abfd, _core_file_failing_command, (abfd));
+}
+
+/*
+FUNCTION
+ bfd_core_file_failing_signal
+
+SYNOPSIS
+ int bfd_core_file_failing_signal(bfd *);
+
+DESCRIPTION
+ Returns the signal number which caused the core dump which
+ generated the file the BFD is attached to.
+*/
+
+int
+bfd_core_file_failing_signal (abfd)
+ bfd *abfd;
+{
+ if (abfd->format != bfd_core) {
+ bfd_error = invalid_operation;
+ return 0;
+ }
+ return BFD_SEND (abfd, _core_file_failing_signal, (abfd));
+}
+
+
+/*
+FUNCTION
+ core_file_matches_executable_p
+
+SYNOPSIS
+ boolean core_file_matches_executable_p
+ (bfd *core_bfd, bfd *exec_bfd);
+
+DESCRIPTION
+ Returns <<true>> if the core file attached to @var{core_bfd}
+ was generated by a run of the executable file attached to
+ @var{exec_bfd}, or else <<false>>.
+*/
+boolean
+core_file_matches_executable_p (core_bfd, exec_bfd)
+ bfd *core_bfd, *exec_bfd;
+{
+ if ((core_bfd->format != bfd_core) || (exec_bfd->format != bfd_object)) {
+ bfd_error = wrong_format;
+ return false;
+ }
+
+ return BFD_SEND (core_bfd, _core_file_matches_executable_p,
+ (core_bfd, exec_bfd));
+}
diff --git a/gnu/usr.bin/gdb/bfd/cpu-i386.c b/gnu/usr.bin/gdb/bfd/cpu-i386.c
new file mode 100644
index 0000000..b4afdb2
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/cpu-i386.c
@@ -0,0 +1,43 @@
+/* BFD support for the Intel 386 architecture.
+ Copyright 1992 Free Software Foundation, Inc.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+
+static bfd_arch_info_type arch_info_struct =
+ {
+ 32, /* 32 bits in a word */
+ 32, /* 32 bits in an address */
+ 8, /* 8 bits in a byte */
+ bfd_arch_i386,
+ 0, /* only 1 machine */
+ "i386",
+ "i386",
+ 3,
+ true, /* the one and only */
+ bfd_default_compatible,
+ bfd_default_scan ,
+ 0,
+ };
+
+void DEFUN_VOID(bfd_i386_arch)
+{
+ bfd_arch_linkin(&arch_info_struct);
+}
diff --git a/gnu/usr.bin/gdb/bfd/ctor.c b/gnu/usr.bin/gdb/bfd/ctor.c
new file mode 100644
index 0000000..adc6919
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/ctor.c
@@ -0,0 +1,148 @@
+/* BFD library support routines for constructors
+ Copyright (C) 1990-1991 Free Software Foundation, Inc.
+
+ Hacked by Steve Chamberlain of Cygnus Support. With some help from
+ Judy Chamberlain too.
+
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+SECTION
+ Constructors
+
+ Classes in C++ have `constructors' and `destructors'. These
+ are functions which are called automatically by the language
+ whenever data of a class is created or destroyed. Class data
+ which is static data may also be have a type which requires
+ `construction', the contructor must be called before the data
+ can be referenced, so the contructor must be called before the
+ program begins.
+
+ The common solution to this problem is for the compiler to
+ call a magic function as the first statement <<main>>.
+ This magic function, (often called <<__main>>) runs around
+ calling the constructors for all the things needing it.
+
+ With COFF the compile has a bargain with the linker et al.
+ All constructors are given strange names, for example
+ <<__GLOBAL__$I$foo>> might be the label of a contructor for
+ the class @var{foo}. The solution on unfortunate systems
+ (most system V machines) is to perform a partial link on all
+ the <<.o>> files, do an <<nm>> on the result, run <<awk>> or some
+ such over the result looking for strange <<__GLOBAL__$>>
+ symbols, generate a C program from this, compile it and link
+ with the partially linked input. This process is usually
+ called <<collect>>.
+
+ Some versions of <<a.out>> use something called the
+ <<set_vector>> mechanism. The constructor symbols are output
+ from the compiler with a special stab code saying that they
+ are constructors, and the linker can deal with them directly.
+
+ BFD allows applications (ie the linker) to deal with
+ constructor information independently of their external
+ implimentation by providing a set of entry points for the
+ indiviual object back ends to call which maintains a database
+ of the contructor information. The application can
+ interrogate the database to find out what it wants. The
+ construction data essential for the linker to be able to
+ perform its job are:
+
+ o asymbol -
+ The asymbol of the contructor entry point contains all the
+ information necessary to call the function.
+
+ o table id -
+ The type of symbol, i.e., is it a constructor, a destructor or
+ something else someone dreamed up to make our lives difficult.
+
+ This module takes this information and then builds extra
+ sections attached to the bfds which own the entry points. It
+ creates these sections as if they were tables of pointers to
+ the entry points, and builds relocation entries to go with
+ them so that the tables can be relocated along with the data
+ they reference.
+
+ These sections are marked with a special bit
+ (<<SEC_CONSTRUCTOR>>) which the linker notices and do with
+ what it wants.
+
+*/
+
+#include <bfd.h>
+#include <sysdep.h>
+#include <libbfd.h>
+
+
+
+/*
+INTERNAL_FUNCTION
+ bfd_constructor_entry
+
+SYNOPSIS
+ void bfd_constructor_entry(bfd *abfd,
+ asymbol **symbol_ptr_ptr,
+ CONST char*type);
+
+
+DESCRIPTION
+ This function is called with an a symbol describing the
+ function to be called, an string which descibes the xtor type,
+ e.g., something like "CTOR" or "DTOR" would be fine. And the bfd
+ which owns the function. Its duty is to create a section
+ called "CTOR" or "DTOR" or whatever if the bfd doesn't already
+ have one, and grow a relocation table for the entry points as
+ they accumulate.
+
+*/
+
+
+void DEFUN(bfd_constructor_entry,(abfd, symbol_ptr_ptr, type),
+ bfd *abfd AND
+ asymbol **symbol_ptr_ptr AND
+ CONST char *type)
+
+{
+ /* Look up the section we're using to store the table in */
+ asection *rel_section = bfd_get_section_by_name (abfd, type);
+ if (rel_section == (asection *)NULL) {
+ rel_section = bfd_make_section (abfd, type);
+ rel_section->flags = SEC_CONSTRUCTOR;
+ rel_section->alignment_power = 2;
+ }
+
+ /* Create a relocation into the section which references the entry
+ point */
+ {
+ arelent_chain *reloc = (arelent_chain *)bfd_alloc(abfd,
+ sizeof(arelent_chain));
+
+/* reloc->relent.section = (asection *)NULL;*/
+ reloc->relent.addend = 0;
+
+ reloc->relent.sym_ptr_ptr = symbol_ptr_ptr;
+ reloc->next = rel_section->constructor_chain;
+ rel_section->constructor_chain = reloc;
+ reloc->relent.address = rel_section->_cooked_size;
+ /* ask the cpu which howto to use */
+ reloc->relent.howto = bfd_reloc_type_lookup(abfd, BFD_RELOC_CTOR);
+ rel_section->_cooked_size += sizeof(int *);
+ rel_section->reloc_count++;
+ }
+
+}
diff --git a/gnu/usr.bin/gdb/bfd/ecoff.c b/gnu/usr.bin/gdb/bfd/ecoff.c
new file mode 100644
index 0000000..e3b7c93
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/ecoff.c
@@ -0,0 +1,3994 @@
+/* Generic ECOFF (Extended-COFF) routines.
+ Copyright 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+ Original version by Per Bothner.
+ Full support added by Ian Lance Taylor, ian@cygnus.com.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "seclet.h"
+#include "aout/ar.h"
+#include "aout/ranlib.h"
+
+/* FIXME: We need the definitions of N_SET[ADTB], but aout64.h defines
+ some other stuff which we don't want and which conflicts with stuff
+ we do want. */
+#include "libaout.h"
+#include "aout/aout64.h"
+#undef N_ABS
+#undef exec_hdr
+#undef obj_sym_filepos
+
+#include "coff/internal.h"
+#include "coff/sym.h"
+#include "coff/symconst.h"
+#include "coff/ecoff.h"
+#include "libcoff.h"
+#include "libecoff.h"
+
+/* Prototypes for static functions. */
+
+static int ecoff_get_magic PARAMS ((bfd *abfd));
+static void ecoff_set_symbol_info PARAMS ((bfd *abfd, SYMR *ecoff_sym,
+ asymbol *asym, int ext,
+ asymbol **indirect_ptr_ptr));
+static void ecoff_emit_aggregate PARAMS ((bfd *abfd, char *string,
+ RNDXR *rndx, long isym,
+ CONST char *which));
+static char *ecoff_type_to_string PARAMS ((bfd *abfd, union aux_ext *aux_ptr,
+ unsigned int indx, int bigendian));
+static boolean ecoff_slurp_reloc_table PARAMS ((bfd *abfd, asection *section,
+ asymbol **symbols));
+static void ecoff_clear_output_flags PARAMS ((bfd *abfd));
+static boolean ecoff_rel PARAMS ((bfd *output_bfd, bfd_seclet_type *seclet,
+ asection *output_section, PTR data,
+ boolean relocateable));
+static boolean ecoff_dump_seclet PARAMS ((bfd *abfd, bfd_seclet_type *seclet,
+ asection *section, PTR data,
+ boolean relocateable));
+static long ecoff_add_string PARAMS ((bfd *output_bfd, FDR *fdr,
+ CONST char *string, boolean external));
+static boolean ecoff_get_debug PARAMS ((bfd *output_bfd,
+ bfd_seclet_type *seclet,
+ asection *section,
+ boolean relocateable));
+static void ecoff_compute_section_file_positions PARAMS ((bfd *abfd));
+static unsigned int ecoff_armap_hash PARAMS ((CONST char *s,
+ unsigned int *rehash,
+ unsigned int size,
+ unsigned int hlog));
+
+/* This stuff is somewhat copied from coffcode.h. */
+
+static asection bfd_debug_section = { "*DEBUG*" };
+
+/* Create an ECOFF object. */
+
+boolean
+ecoff_mkobject (abfd)
+ bfd *abfd;
+{
+ abfd->tdata.ecoff_obj_data = ((struct ecoff_tdata *)
+ bfd_zalloc (abfd, sizeof (ecoff_data_type)));
+ if (abfd->tdata.ecoff_obj_data == NULL)
+ {
+ bfd_error = no_memory;
+ return false;
+ }
+
+ /* Always create a .scommon section for every BFD. This is a hack so
+ that the linker has something to attach scSCommon symbols to. */
+ if (bfd_make_section (abfd, SCOMMON) == NULL)
+ return false;
+
+ return true;
+}
+
+/* This is a hook called by coff_real_object_p to create any backend
+ specific information. */
+
+PTR
+ecoff_mkobject_hook (abfd, filehdr, aouthdr)
+ bfd *abfd;
+ PTR filehdr;
+ PTR aouthdr;
+{
+ struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr;
+ struct internal_aouthdr *internal_a = (struct internal_aouthdr *) aouthdr;
+ ecoff_data_type *ecoff;
+ asection *regsec;
+
+ if (ecoff_mkobject (abfd) == false)
+ return NULL;
+
+ ecoff = ecoff_data (abfd);
+ ecoff->gp_size = 8;
+ ecoff->sym_filepos = internal_f->f_symptr;
+
+ /* Create the .reginfo section to give programs outside BFD a way to
+ see the information stored in the a.out header. See the comment
+ in coff/ecoff.h. */
+ regsec = bfd_make_section (abfd, REGINFO);
+ if (regsec == NULL)
+ return NULL;
+
+ if (internal_a != (struct internal_aouthdr *) NULL)
+ {
+ int i;
+
+ ecoff->text_start = internal_a->text_start;
+ ecoff->text_end = internal_a->text_start + internal_a->tsize;
+ ecoff->gp = internal_a->gp_value;
+ ecoff->gprmask = internal_a->gprmask;
+ for (i = 0; i < 4; i++)
+ ecoff->cprmask[i] = internal_a->cprmask[i];
+ ecoff->fprmask = internal_a->fprmask;
+ if (internal_a->magic == ECOFF_AOUT_ZMAGIC)
+ abfd->flags |= D_PAGED;
+ }
+
+ /* It turns out that no special action is required by the MIPS or
+ Alpha ECOFF backends. They have different information in the
+ a.out header, but we just copy it all (e.g., gprmask, cprmask and
+ fprmask) and let the swapping routines ensure that only relevant
+ information is written out. */
+
+ return (PTR) ecoff;
+}
+
+/* This is a hook needed by SCO COFF, but we have nothing to do. */
+
+asection *
+ecoff_make_section_hook (abfd, name)
+ bfd *abfd;
+ char *name;
+{
+ return (asection *) NULL;
+}
+
+/* Initialize a new section. */
+
+boolean
+ecoff_new_section_hook (abfd, section)
+ bfd *abfd;
+ asection *section;
+{
+ section->alignment_power = abfd->xvec->align_power_min;
+
+ if (strcmp (section->name, _TEXT) == 0)
+ section->flags |= SEC_CODE | SEC_LOAD | SEC_ALLOC;
+ else if (strcmp (section->name, _DATA) == 0
+ || strcmp (section->name, _SDATA) == 0)
+ section->flags |= SEC_DATA | SEC_LOAD | SEC_ALLOC;
+ else if (strcmp (section->name, _RDATA) == 0
+ || strcmp (section->name, _LIT8) == 0
+ || strcmp (section->name, _LIT4) == 0)
+ section->flags |= SEC_DATA | SEC_LOAD | SEC_ALLOC | SEC_READONLY;
+ else if (strcmp (section->name, _BSS) == 0
+ || strcmp (section->name, _SBSS) == 0)
+ section->flags |= SEC_ALLOC;
+ else if (strcmp (section->name, REGINFO) == 0)
+ {
+ section->flags |= SEC_HAS_CONTENTS | SEC_NEVER_LOAD;
+ section->_raw_size = sizeof (struct ecoff_reginfo);
+ }
+
+ /* Probably any other section name is SEC_NEVER_LOAD, but I'm
+ uncertain about .init on some systems and I don't know how shared
+ libraries work. */
+
+ return true;
+}
+
+/* Determine the machine architecture and type. This is called from
+ the generic COFF routines. It is the inverse of ecoff_get_magic,
+ below. This could be an ECOFF backend routine, with one version
+ for each target, but there aren't all that many ECOFF targets. */
+
+boolean
+ecoff_set_arch_mach_hook (abfd, filehdr)
+ bfd *abfd;
+ PTR filehdr;
+{
+ struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr;
+ enum bfd_architecture arch;
+ unsigned long mach;
+
+ switch (internal_f->f_magic)
+ {
+ case MIPS_MAGIC_1:
+ case MIPS_MAGIC_LITTLE:
+ case MIPS_MAGIC_BIG:
+ arch = bfd_arch_mips;
+ mach = 3000;
+ break;
+
+ case MIPS_MAGIC_LITTLE2:
+ case MIPS_MAGIC_BIG2:
+ /* MIPS ISA level 2: the r6000 */
+ arch = bfd_arch_mips;
+ mach = 6000;
+ break;
+
+ case MIPS_MAGIC_LITTLE3:
+ case MIPS_MAGIC_BIG3:
+ /* MIPS ISA level 3: the r4000 */
+ arch = bfd_arch_mips;
+ mach = 4000;
+ break;
+
+ case ALPHA_MAGIC:
+ arch = bfd_arch_alpha;
+ mach = 0;
+ break;
+
+ default:
+ arch = bfd_arch_obscure;
+ mach = 0;
+ break;
+ }
+
+ return bfd_default_set_arch_mach (abfd, arch, mach);
+}
+
+/* Get the magic number to use based on the architecture and machine.
+ This is the inverse of ecoff_set_arch_mach_hook, above. */
+
+static int
+ecoff_get_magic (abfd)
+ bfd *abfd;
+{
+ int big, little;
+
+ switch (bfd_get_arch (abfd))
+ {
+ case bfd_arch_mips:
+ switch (bfd_get_mach (abfd))
+ {
+ default:
+ case 0:
+ case 3000:
+ big = MIPS_MAGIC_BIG;
+ little = MIPS_MAGIC_LITTLE;
+ break;
+
+ case 6000:
+ big = MIPS_MAGIC_BIG2;
+ little = MIPS_MAGIC_LITTLE2;
+ break;
+
+ case 4000:
+ big = MIPS_MAGIC_BIG3;
+ little = MIPS_MAGIC_LITTLE3;
+ break;
+ }
+
+ return abfd->xvec->byteorder_big_p ? big : little;
+
+ case bfd_arch_alpha:
+ return ALPHA_MAGIC;
+
+ default:
+ abort ();
+ return 0;
+ }
+}
+
+/* Get the section s_flags to use for a section. */
+
+long
+ecoff_sec_to_styp_flags (name, flags)
+ CONST char *name;
+ flagword flags;
+{
+ long styp;
+
+ styp = 0;
+
+ if (strcmp (name, _TEXT) == 0)
+ styp = STYP_TEXT;
+ else if (strcmp (name, _DATA) == 0)
+ styp = STYP_DATA;
+ else if (strcmp (name, _SDATA) == 0)
+ styp = STYP_SDATA;
+ else if (strcmp (name, _RDATA) == 0)
+ styp = STYP_RDATA;
+ else if (strcmp (name, _LIT8) == 0)
+ styp = STYP_LIT8;
+ else if (strcmp (name, _LIT4) == 0)
+ styp = STYP_LIT4;
+ else if (strcmp (name, _BSS) == 0)
+ styp = STYP_BSS;
+ else if (strcmp (name, _SBSS) == 0)
+ styp = STYP_SBSS;
+ else if (strcmp (name, _INIT) == 0)
+ styp = STYP_ECOFF_INIT;
+ else if (strcmp (name, _FINI) == 0)
+ styp = STYP_ECOFF_FINI;
+ else if (flags & SEC_CODE)
+ styp = STYP_TEXT;
+ else if (flags & SEC_DATA)
+ styp = STYP_DATA;
+ else if (flags & SEC_READONLY)
+ styp = STYP_RDATA;
+ else if (flags & SEC_LOAD)
+ styp = STYP_REG;
+ else
+ styp = STYP_BSS;
+
+ if (flags & SEC_NEVER_LOAD)
+ styp |= STYP_NOLOAD;
+
+ return styp;
+}
+
+/* Get the BFD flags to use for a section. */
+
+flagword
+ecoff_styp_to_sec_flags (abfd, hdr)
+ bfd *abfd;
+ PTR hdr;
+{
+ struct internal_scnhdr *internal_s = (struct internal_scnhdr *) hdr;
+ long styp_flags = internal_s->s_flags;
+ flagword sec_flags=0;
+
+ if (styp_flags & STYP_NOLOAD)
+ sec_flags |= SEC_NEVER_LOAD;
+
+ /* For 386 COFF, at least, an unloadable text or data section is
+ actually a shared library section. */
+ if ((styp_flags & STYP_TEXT)
+ || (styp_flags & STYP_ECOFF_INIT)
+ || (styp_flags & STYP_ECOFF_FINI))
+ {
+ if (sec_flags & SEC_NEVER_LOAD)
+ sec_flags |= SEC_CODE | SEC_SHARED_LIBRARY;
+ else
+ sec_flags |= SEC_CODE | SEC_LOAD | SEC_ALLOC;
+ }
+ else if ((styp_flags & STYP_DATA)
+ || (styp_flags & STYP_RDATA)
+ || (styp_flags & STYP_SDATA))
+ {
+ if (sec_flags & SEC_NEVER_LOAD)
+ sec_flags |= SEC_DATA | SEC_SHARED_LIBRARY;
+ else
+ sec_flags |= SEC_DATA | SEC_LOAD | SEC_ALLOC;
+ if (styp_flags & STYP_RDATA)
+ sec_flags |= SEC_READONLY;
+ }
+ else if ((styp_flags & STYP_BSS)
+ || (styp_flags & STYP_SBSS))
+ {
+ sec_flags |= SEC_ALLOC;
+ }
+ else if (styp_flags & STYP_INFO)
+ {
+ sec_flags |= SEC_NEVER_LOAD;
+ }
+ else if ((styp_flags & STYP_LIT8)
+ || (styp_flags & STYP_LIT4))
+ {
+ sec_flags |= SEC_DATA | SEC_LOAD | SEC_ALLOC | SEC_READONLY;
+ }
+ else
+ {
+ sec_flags |= SEC_ALLOC | SEC_LOAD;
+ }
+
+ return sec_flags;
+}
+
+/* Routines to swap auxiliary information in and out. I am assuming
+ that the auxiliary information format is always going to be target
+ independent. */
+
+/* Swap in a type information record.
+ BIGEND says whether AUX symbols are big-endian or little-endian; this
+ info comes from the file header record (fh-fBigendian). */
+
+void
+ecoff_swap_tir_in (bigend, ext_copy, intern)
+ int bigend;
+ struct tir_ext *ext_copy;
+ TIR *intern;
+{
+ struct tir_ext ext[1];
+
+ *ext = *ext_copy; /* Make it reasonable to do in-place. */
+
+ /* now the fun stuff... */
+ if (bigend) {
+ intern->fBitfield = 0 != (ext->t_bits1[0] & TIR_BITS1_FBITFIELD_BIG);
+ intern->continued = 0 != (ext->t_bits1[0] & TIR_BITS1_CONTINUED_BIG);
+ intern->bt = (ext->t_bits1[0] & TIR_BITS1_BT_BIG)
+ >> TIR_BITS1_BT_SH_BIG;
+ intern->tq4 = (ext->t_tq45[0] & TIR_BITS_TQ4_BIG)
+ >> TIR_BITS_TQ4_SH_BIG;
+ intern->tq5 = (ext->t_tq45[0] & TIR_BITS_TQ5_BIG)
+ >> TIR_BITS_TQ5_SH_BIG;
+ intern->tq0 = (ext->t_tq01[0] & TIR_BITS_TQ0_BIG)
+ >> TIR_BITS_TQ0_SH_BIG;
+ intern->tq1 = (ext->t_tq01[0] & TIR_BITS_TQ1_BIG)
+ >> TIR_BITS_TQ1_SH_BIG;
+ intern->tq2 = (ext->t_tq23[0] & TIR_BITS_TQ2_BIG)
+ >> TIR_BITS_TQ2_SH_BIG;
+ intern->tq3 = (ext->t_tq23[0] & TIR_BITS_TQ3_BIG)
+ >> TIR_BITS_TQ3_SH_BIG;
+ } else {
+ intern->fBitfield = 0 != (ext->t_bits1[0] & TIR_BITS1_FBITFIELD_LITTLE);
+ intern->continued = 0 != (ext->t_bits1[0] & TIR_BITS1_CONTINUED_LITTLE);
+ intern->bt = (ext->t_bits1[0] & TIR_BITS1_BT_LITTLE)
+ >> TIR_BITS1_BT_SH_LITTLE;
+ intern->tq4 = (ext->t_tq45[0] & TIR_BITS_TQ4_LITTLE)
+ >> TIR_BITS_TQ4_SH_LITTLE;
+ intern->tq5 = (ext->t_tq45[0] & TIR_BITS_TQ5_LITTLE)
+ >> TIR_BITS_TQ5_SH_LITTLE;
+ intern->tq0 = (ext->t_tq01[0] & TIR_BITS_TQ0_LITTLE)
+ >> TIR_BITS_TQ0_SH_LITTLE;
+ intern->tq1 = (ext->t_tq01[0] & TIR_BITS_TQ1_LITTLE)
+ >> TIR_BITS_TQ1_SH_LITTLE;
+ intern->tq2 = (ext->t_tq23[0] & TIR_BITS_TQ2_LITTLE)
+ >> TIR_BITS_TQ2_SH_LITTLE;
+ intern->tq3 = (ext->t_tq23[0] & TIR_BITS_TQ3_LITTLE)
+ >> TIR_BITS_TQ3_SH_LITTLE;
+ }
+
+#ifdef TEST
+ if (memcmp ((char *)ext, (char *)intern, sizeof (*intern)) != 0)
+ abort();
+#endif
+}
+
+/* Swap out a type information record.
+ BIGEND says whether AUX symbols are big-endian or little-endian; this
+ info comes from the file header record (fh-fBigendian). */
+
+void
+ecoff_swap_tir_out (bigend, intern_copy, ext)
+ int bigend;
+ TIR *intern_copy;
+ struct tir_ext *ext;
+{
+ TIR intern[1];
+
+ *intern = *intern_copy; /* Make it reasonable to do in-place. */
+
+ /* now the fun stuff... */
+ if (bigend) {
+ ext->t_bits1[0] = ((intern->fBitfield ? TIR_BITS1_FBITFIELD_BIG : 0)
+ | (intern->continued ? TIR_BITS1_CONTINUED_BIG : 0)
+ | ((intern->bt << TIR_BITS1_BT_SH_BIG)
+ & TIR_BITS1_BT_BIG));
+ ext->t_tq45[0] = (((intern->tq4 << TIR_BITS_TQ4_SH_BIG)
+ & TIR_BITS_TQ4_BIG)
+ | ((intern->tq5 << TIR_BITS_TQ5_SH_BIG)
+ & TIR_BITS_TQ5_BIG));
+ ext->t_tq01[0] = (((intern->tq0 << TIR_BITS_TQ0_SH_BIG)
+ & TIR_BITS_TQ0_BIG)
+ | ((intern->tq1 << TIR_BITS_TQ1_SH_BIG)
+ & TIR_BITS_TQ1_BIG));
+ ext->t_tq23[0] = (((intern->tq2 << TIR_BITS_TQ2_SH_BIG)
+ & TIR_BITS_TQ2_BIG)
+ | ((intern->tq3 << TIR_BITS_TQ3_SH_BIG)
+ & TIR_BITS_TQ3_BIG));
+ } else {
+ ext->t_bits1[0] = ((intern->fBitfield ? TIR_BITS1_FBITFIELD_LITTLE : 0)
+ | (intern->continued ? TIR_BITS1_CONTINUED_LITTLE : 0)
+ | ((intern->bt << TIR_BITS1_BT_SH_LITTLE)
+ & TIR_BITS1_BT_LITTLE));
+ ext->t_tq45[0] = (((intern->tq4 << TIR_BITS_TQ4_SH_LITTLE)
+ & TIR_BITS_TQ4_LITTLE)
+ | ((intern->tq5 << TIR_BITS_TQ5_SH_LITTLE)
+ & TIR_BITS_TQ5_LITTLE));
+ ext->t_tq01[0] = (((intern->tq0 << TIR_BITS_TQ0_SH_LITTLE)
+ & TIR_BITS_TQ0_LITTLE)
+ | ((intern->tq1 << TIR_BITS_TQ1_SH_LITTLE)
+ & TIR_BITS_TQ1_LITTLE));
+ ext->t_tq23[0] = (((intern->tq2 << TIR_BITS_TQ2_SH_LITTLE)
+ & TIR_BITS_TQ2_LITTLE)
+ | ((intern->tq3 << TIR_BITS_TQ3_SH_LITTLE)
+ & TIR_BITS_TQ3_LITTLE));
+ }
+
+#ifdef TEST
+ if (memcmp ((char *)ext, (char *)intern, sizeof (*intern)) != 0)
+ abort();
+#endif
+}
+
+/* Swap in a relative symbol record. BIGEND says whether it is in
+ big-endian or little-endian format.*/
+
+void
+ecoff_swap_rndx_in (bigend, ext_copy, intern)
+ int bigend;
+ struct rndx_ext *ext_copy;
+ RNDXR *intern;
+{
+ struct rndx_ext ext[1];
+
+ *ext = *ext_copy; /* Make it reasonable to do in-place. */
+
+ /* now the fun stuff... */
+ if (bigend) {
+ intern->rfd = (ext->r_bits[0] << RNDX_BITS0_RFD_SH_LEFT_BIG)
+ | ((ext->r_bits[1] & RNDX_BITS1_RFD_BIG)
+ >> RNDX_BITS1_RFD_SH_BIG);
+ intern->index = ((ext->r_bits[1] & RNDX_BITS1_INDEX_BIG)
+ << RNDX_BITS1_INDEX_SH_LEFT_BIG)
+ | (ext->r_bits[2] << RNDX_BITS2_INDEX_SH_LEFT_BIG)
+ | (ext->r_bits[3] << RNDX_BITS3_INDEX_SH_LEFT_BIG);
+ } else {
+ intern->rfd = (ext->r_bits[0] << RNDX_BITS0_RFD_SH_LEFT_LITTLE)
+ | ((ext->r_bits[1] & RNDX_BITS1_RFD_LITTLE)
+ << RNDX_BITS1_RFD_SH_LEFT_LITTLE);
+ intern->index = ((ext->r_bits[1] & RNDX_BITS1_INDEX_LITTLE)
+ >> RNDX_BITS1_INDEX_SH_LITTLE)
+ | (ext->r_bits[2] << RNDX_BITS2_INDEX_SH_LEFT_LITTLE)
+ | (ext->r_bits[3] << RNDX_BITS3_INDEX_SH_LEFT_LITTLE);
+ }
+
+#ifdef TEST
+ if (memcmp ((char *)ext, (char *)intern, sizeof (*intern)) != 0)
+ abort();
+#endif
+}
+
+/* Swap out a relative symbol record. BIGEND says whether it is in
+ big-endian or little-endian format.*/
+
+void
+ecoff_swap_rndx_out (bigend, intern_copy, ext)
+ int bigend;
+ RNDXR *intern_copy;
+ struct rndx_ext *ext;
+{
+ RNDXR intern[1];
+
+ *intern = *intern_copy; /* Make it reasonable to do in-place. */
+
+ /* now the fun stuff... */
+ if (bigend) {
+ ext->r_bits[0] = intern->rfd >> RNDX_BITS0_RFD_SH_LEFT_BIG;
+ ext->r_bits[1] = (((intern->rfd << RNDX_BITS1_RFD_SH_BIG)
+ & RNDX_BITS1_RFD_BIG)
+ | ((intern->index >> RNDX_BITS1_INDEX_SH_LEFT_BIG)
+ & RNDX_BITS1_INDEX_BIG));
+ ext->r_bits[2] = intern->index >> RNDX_BITS2_INDEX_SH_LEFT_BIG;
+ ext->r_bits[3] = intern->index >> RNDX_BITS3_INDEX_SH_LEFT_BIG;
+ } else {
+ ext->r_bits[0] = intern->rfd >> RNDX_BITS0_RFD_SH_LEFT_LITTLE;
+ ext->r_bits[1] = (((intern->rfd >> RNDX_BITS1_RFD_SH_LEFT_LITTLE)
+ & RNDX_BITS1_RFD_LITTLE)
+ | ((intern->index << RNDX_BITS1_INDEX_SH_LITTLE)
+ & RNDX_BITS1_INDEX_LITTLE));
+ ext->r_bits[2] = intern->index >> RNDX_BITS2_INDEX_SH_LEFT_LITTLE;
+ ext->r_bits[3] = intern->index >> RNDX_BITS3_INDEX_SH_LEFT_LITTLE;
+ }
+
+#ifdef TEST
+ if (memcmp ((char *)ext, (char *)intern, sizeof (*intern)) != 0)
+ abort();
+#endif
+}
+
+/* Read in and swap the important symbolic information for an ECOFF
+ object file. This is called by gdb. */
+
+boolean
+ecoff_slurp_symbolic_info (abfd)
+ bfd *abfd;
+{
+ const struct ecoff_backend_data * const backend = ecoff_backend (abfd);
+ bfd_size_type external_hdr_size;
+ HDRR *internal_symhdr;
+ bfd_size_type raw_base;
+ bfd_size_type raw_size;
+ PTR raw;
+ bfd_size_type external_fdr_size;
+ char *fraw_src;
+ char *fraw_end;
+ struct fdr *fdr_ptr;
+ bfd_size_type raw_end;
+ bfd_size_type cb_end;
+
+ /* Check whether we've already gotten it, and whether there's any to
+ get. */
+ if (ecoff_data (abfd)->raw_syments != (PTR) NULL)
+ return true;
+ if (ecoff_data (abfd)->sym_filepos == 0)
+ {
+ bfd_get_symcount (abfd) = 0;
+ return true;
+ }
+
+ /* At this point bfd_get_symcount (abfd) holds the number of symbols
+ as read from the file header, but on ECOFF this is always the
+ size of the symbolic information header. It would be cleaner to
+ handle this when we first read the file in coffgen.c. */
+ external_hdr_size = backend->external_hdr_size;
+ if (bfd_get_symcount (abfd) != external_hdr_size)
+ {
+ bfd_error = bad_value;
+ return false;
+ }
+
+ /* Read the symbolic information header. */
+ raw = (PTR) alloca (external_hdr_size);
+ if (bfd_seek (abfd, ecoff_data (abfd)->sym_filepos, SEEK_SET) == -1
+ || (bfd_read (raw, external_hdr_size, 1, abfd)
+ != external_hdr_size))
+ {
+ bfd_error = system_call_error;
+ return false;
+ }
+ internal_symhdr = &ecoff_data (abfd)->symbolic_header;
+ (*backend->swap_hdr_in) (abfd, raw, internal_symhdr);
+
+ if (internal_symhdr->magic != backend->sym_magic)
+ {
+ bfd_error = bad_value;
+ return false;
+ }
+
+ /* Now we can get the correct number of symbols. */
+ bfd_get_symcount (abfd) = (internal_symhdr->isymMax
+ + internal_symhdr->iextMax);
+
+ /* Read all the symbolic information at once. */
+ raw_base = ecoff_data (abfd)->sym_filepos + external_hdr_size;
+
+ /* Alpha ecoff makes the determination of raw_size difficult. It has
+ an undocumented debug data section between the symhdr and the first
+ documented section. And the ordering of the sections varies between
+ statically and dynamically linked executables.
+ If bfd supports SEEK_END someday, this code could be simplified. */
+
+ raw_end = 0;
+
+#define UPDATE_RAW_END(start, count, size) \
+ cb_end = internal_symhdr->start + internal_symhdr->count * (size); \
+ if (cb_end > raw_end) \
+ raw_end = cb_end
+
+ UPDATE_RAW_END (cbLineOffset, cbLine, sizeof (unsigned char));
+ UPDATE_RAW_END (cbDnOffset, idnMax, backend->external_dnr_size);
+ UPDATE_RAW_END (cbPdOffset, ipdMax, backend->external_pdr_size);
+ UPDATE_RAW_END (cbSymOffset, isymMax, backend->external_sym_size);
+ UPDATE_RAW_END (cbOptOffset, ioptMax, backend->external_opt_size);
+ UPDATE_RAW_END (cbAuxOffset, iauxMax, sizeof (union aux_ext));
+ UPDATE_RAW_END (cbSsOffset, issMax, sizeof (char));
+ UPDATE_RAW_END (cbSsExtOffset, issExtMax, sizeof (char));
+ UPDATE_RAW_END (cbFdOffset, ifdMax, backend->external_fdr_size);
+ UPDATE_RAW_END (cbRfdOffset, crfd, backend->external_rfd_size);
+ UPDATE_RAW_END (cbExtOffset, iextMax, backend->external_ext_size);
+
+#undef UPDATE_RAW_END
+
+ raw_size = raw_end - raw_base;
+ if (raw_size == 0)
+ {
+ ecoff_data (abfd)->sym_filepos = 0;
+ return true;
+ }
+ raw = (PTR) bfd_alloc (abfd, raw_size);
+ if (raw == NULL)
+ {
+ bfd_error = no_memory;
+ return false;
+ }
+ if (bfd_read (raw, raw_size, 1, abfd) != raw_size)
+ {
+ bfd_error = system_call_error;
+ bfd_release (abfd, raw);
+ return false;
+ }
+
+ ecoff_data (abfd)->raw_size = raw_size;
+ ecoff_data (abfd)->raw_syments = raw;
+
+ /* Get pointers for the numeric offsets in the HDRR structure. */
+#define FIX(off1, off2, type) \
+ if (internal_symhdr->off1 == 0) \
+ ecoff_data (abfd)->off2 = (type) NULL; \
+ else \
+ ecoff_data (abfd)->off2 = (type) ((char *) raw \
+ + internal_symhdr->off1 \
+ - raw_base)
+ FIX (cbLineOffset, line, unsigned char *);
+ FIX (cbDnOffset, external_dnr, PTR);
+ FIX (cbPdOffset, external_pdr, PTR);
+ FIX (cbSymOffset, external_sym, PTR);
+ FIX (cbOptOffset, external_opt, PTR);
+ FIX (cbAuxOffset, external_aux, union aux_ext *);
+ FIX (cbSsOffset, ss, char *);
+ FIX (cbSsExtOffset, ssext, char *);
+ FIX (cbFdOffset, external_fdr, PTR);
+ FIX (cbRfdOffset, external_rfd, PTR);
+ FIX (cbExtOffset, external_ext, PTR);
+#undef FIX
+
+ /* I don't want to always swap all the data, because it will just
+ waste time and most programs will never look at it. The only
+ time the linker needs most of the debugging information swapped
+ is when linking big-endian and little-endian MIPS object files
+ together, which is not a common occurrence.
+
+ We need to look at the fdr to deal with a lot of information in
+ the symbols, so we swap them here. */
+ ecoff_data (abfd)->fdr =
+ (struct fdr *) bfd_alloc (abfd,
+ (internal_symhdr->ifdMax *
+ sizeof (struct fdr)));
+ if (ecoff_data (abfd)->fdr == NULL)
+ {
+ bfd_error = no_memory;
+ return false;
+ }
+ external_fdr_size = backend->external_fdr_size;
+ fdr_ptr = ecoff_data (abfd)->fdr;
+ fraw_src = (char *) ecoff_data (abfd)->external_fdr;
+ fraw_end = fraw_src + internal_symhdr->ifdMax * external_fdr_size;
+ for (; fraw_src < fraw_end; fraw_src += external_fdr_size, fdr_ptr++)
+ (*backend->swap_fdr_in) (abfd, (PTR) fraw_src, fdr_ptr);
+
+ return true;
+}
+
+/* ECOFF symbol table routines. The ECOFF symbol table is described
+ in gcc/mips-tfile.c. */
+
+/* ECOFF uses two common sections. One is the usual one, and the
+ other is for small objects. All the small objects are kept
+ together, and then referenced via the gp pointer, which yields
+ faster assembler code. This is what we use for the small common
+ section. */
+static asection ecoff_scom_section;
+static asymbol ecoff_scom_symbol;
+static asymbol *ecoff_scom_symbol_ptr;
+
+/* Create an empty symbol. */
+
+asymbol *
+ecoff_make_empty_symbol (abfd)
+ bfd *abfd;
+{
+ ecoff_symbol_type *new;
+
+ new = (ecoff_symbol_type *) bfd_alloc (abfd, sizeof (ecoff_symbol_type));
+ if (new == (ecoff_symbol_type *) NULL)
+ {
+ bfd_error = no_memory;
+ return (asymbol *) NULL;
+ }
+ memset (new, 0, sizeof *new);
+ new->symbol.section = (asection *) NULL;
+ new->fdr = (FDR *) NULL;
+ new->local = false;
+ new->native = NULL;
+ new->symbol.the_bfd = abfd;
+ return &new->symbol;
+}
+
+/* Set the BFD flags and section for an ECOFF symbol. */
+
+static void
+ecoff_set_symbol_info (abfd, ecoff_sym, asym, ext, indirect_ptr_ptr)
+ bfd *abfd;
+ SYMR *ecoff_sym;
+ asymbol *asym;
+ int ext;
+ asymbol **indirect_ptr_ptr;
+{
+ asym->the_bfd = abfd;
+ asym->value = ecoff_sym->value;
+ asym->section = &bfd_debug_section;
+ asym->udata = NULL;
+
+ /* An indirect symbol requires two consecutive stabs symbols. */
+ if (*indirect_ptr_ptr != (asymbol *) NULL)
+ {
+ BFD_ASSERT (ECOFF_IS_STAB (ecoff_sym));
+
+ /* @@ Stuffing pointers into integers is a no-no.
+ We can usually get away with it if the integer is
+ large enough though. */
+ if (sizeof (asym) > sizeof (bfd_vma))
+ abort ();
+ (*indirect_ptr_ptr)->value = (bfd_vma) asym;
+
+ asym->flags = BSF_DEBUGGING;
+ asym->section = &bfd_und_section;
+ *indirect_ptr_ptr = NULL;
+ return;
+ }
+
+ if (ECOFF_IS_STAB (ecoff_sym)
+ && (ECOFF_UNMARK_STAB (ecoff_sym->index) | N_EXT) == (N_INDR | N_EXT))
+ {
+ asym->flags = BSF_DEBUGGING | BSF_INDIRECT;
+ asym->section = &bfd_ind_section;
+ /* Pass this symbol on to the next call to this function. */
+ *indirect_ptr_ptr = asym;
+ return;
+ }
+
+ /* Most symbol types are just for debugging. */
+ switch (ecoff_sym->st)
+ {
+ case stGlobal:
+ case stStatic:
+ case stLabel:
+ case stProc:
+ case stStaticProc:
+ break;
+ case stNil:
+ if (ECOFF_IS_STAB (ecoff_sym))
+ {
+ asym->flags = BSF_DEBUGGING;
+ return;
+ }
+ break;
+ default:
+ asym->flags = BSF_DEBUGGING;
+ return;
+ }
+
+ if (ext)
+ asym->flags = BSF_EXPORT | BSF_GLOBAL;
+ else
+ asym->flags = BSF_LOCAL;
+ switch (ecoff_sym->sc)
+ {
+ case scNil:
+ /* Used for compiler generated labels. Leave them in the
+ debugging section, and mark them as local. If BSF_DEBUGGING
+ is set, then nm does not display them for some reason. If no
+ flags are set then the linker whines about them. */
+ asym->flags = BSF_LOCAL;
+ break;
+ case scText:
+ asym->section = bfd_make_section_old_way (abfd, ".text");
+ asym->value -= asym->section->vma;
+ break;
+ case scData:
+ asym->section = bfd_make_section_old_way (abfd, ".data");
+ asym->value -= asym->section->vma;
+ break;
+ case scBss:
+ asym->section = bfd_make_section_old_way (abfd, ".bss");
+ asym->value -= asym->section->vma;
+ break;
+ case scRegister:
+ asym->flags = BSF_DEBUGGING;
+ break;
+ case scAbs:
+ asym->section = &bfd_abs_section;
+ break;
+ case scUndefined:
+ asym->section = &bfd_und_section;
+ asym->flags = 0;
+ asym->value = 0;
+ break;
+ case scCdbLocal:
+ case scBits:
+ case scCdbSystem:
+ case scRegImage:
+ case scInfo:
+ case scUserStruct:
+ asym->flags = BSF_DEBUGGING;
+ break;
+ case scSData:
+ asym->section = bfd_make_section_old_way (abfd, ".sdata");
+ asym->value -= asym->section->vma;
+ break;
+ case scSBss:
+ asym->section = bfd_make_section_old_way (abfd, ".sbss");
+ asym->value -= asym->section->vma;
+ break;
+ case scRData:
+ asym->section = bfd_make_section_old_way (abfd, ".rdata");
+ asym->value -= asym->section->vma;
+ break;
+ case scVar:
+ asym->flags = BSF_DEBUGGING;
+ break;
+ case scCommon:
+ if (asym->value > ecoff_data (abfd)->gp_size)
+ {
+ asym->section = &bfd_com_section;
+ asym->flags = 0;
+ break;
+ }
+ /* Fall through. */
+ case scSCommon:
+ if (ecoff_scom_section.name == NULL)
+ {
+ /* Initialize the small common section. */
+ ecoff_scom_section.name = SCOMMON;
+ ecoff_scom_section.flags = SEC_IS_COMMON;
+ ecoff_scom_section.output_section = &ecoff_scom_section;
+ ecoff_scom_section.symbol = &ecoff_scom_symbol;
+ ecoff_scom_section.symbol_ptr_ptr = &ecoff_scom_symbol_ptr;
+ ecoff_scom_symbol.name = SCOMMON;
+ ecoff_scom_symbol.flags = BSF_SECTION_SYM;
+ ecoff_scom_symbol.section = &ecoff_scom_section;
+ ecoff_scom_symbol_ptr = &ecoff_scom_symbol;
+ }
+ asym->section = &ecoff_scom_section;
+ asym->flags = 0;
+ break;
+ case scVarRegister:
+ case scVariant:
+ asym->flags = BSF_DEBUGGING;
+ break;
+ case scSUndefined:
+ asym->section = &bfd_und_section;
+ asym->flags = 0;
+ asym->value = 0;
+ break;
+ case scInit:
+ asym->section = bfd_make_section_old_way (abfd, ".init");
+ asym->value -= asym->section->vma;
+ break;
+ case scBasedVar:
+ case scXData:
+ case scPData:
+ asym->flags = BSF_DEBUGGING;
+ break;
+ case scFini:
+ asym->section = bfd_make_section_old_way (abfd, ".fini");
+ asym->value -= asym->section->vma;
+ break;
+ default:
+ break;
+ }
+
+ /* Look for special constructors symbols and make relocation entries
+ in a special construction section. These are produced by the
+ -fgnu-linker argument to g++. */
+ if (ECOFF_IS_STAB (ecoff_sym))
+ {
+ switch (ECOFF_UNMARK_STAB (ecoff_sym->index))
+ {
+ default:
+ break;
+
+ case N_SETA:
+ case N_SETT:
+ case N_SETD:
+ case N_SETB:
+ {
+ const char *name;
+ asection *section;
+ arelent_chain *reloc_chain;
+ unsigned int bitsize;
+
+ /* Get a section with the same name as the symbol (usually
+ __CTOR_LIST__ or __DTOR_LIST__). FIXME: gcc uses the
+ name ___CTOR_LIST (three underscores). We need
+ __CTOR_LIST (two underscores), since ECOFF doesn't use
+ a leading underscore. This should be handled by gcc,
+ but instead we do it here. Actually, this should all
+ be done differently anyhow. */
+ name = bfd_asymbol_name (asym);
+ if (name[0] == '_' && name[1] == '_' && name[2] == '_')
+ {
+ ++name;
+ asym->name = name;
+ }
+ section = bfd_get_section_by_name (abfd, name);
+ if (section == (asection *) NULL)
+ {
+ char *copy;
+
+ copy = (char *) bfd_alloc (abfd, strlen (name) + 1);
+ strcpy (copy, name);
+ section = bfd_make_section (abfd, copy);
+ }
+
+ /* Build a reloc pointing to this constructor. */
+ reloc_chain =
+ (arelent_chain *) bfd_alloc (abfd, sizeof (arelent_chain));
+ reloc_chain->relent.sym_ptr_ptr =
+ bfd_get_section (asym)->symbol_ptr_ptr;
+ reloc_chain->relent.address = section->_raw_size;
+ reloc_chain->relent.addend = asym->value;
+ reloc_chain->relent.howto =
+ ecoff_backend (abfd)->constructor_reloc;
+
+ /* Set up the constructor section to hold the reloc. */
+ section->flags = SEC_CONSTRUCTOR;
+ ++section->reloc_count;
+
+ /* Constructor sections must be rounded to a boundary
+ based on the bitsize. These are not real sections--
+ they are handled specially by the linker--so the ECOFF
+ 16 byte alignment restriction does not apply. */
+ bitsize = ecoff_backend (abfd)->constructor_bitsize;
+ section->alignment_power = 1;
+ while ((1 << section->alignment_power) < bitsize / 8)
+ ++section->alignment_power;
+
+ reloc_chain->next = section->constructor_chain;
+ section->constructor_chain = reloc_chain;
+ section->_raw_size += bitsize / 8;
+
+ /* Mark the symbol as a constructor. */
+ asym->flags |= BSF_CONSTRUCTOR;
+ }
+ break;
+ }
+ }
+}
+
+/* Read an ECOFF symbol table. */
+
+boolean
+ecoff_slurp_symbol_table (abfd)
+ bfd *abfd;
+{
+ const struct ecoff_backend_data * const backend = ecoff_backend (abfd);
+ const bfd_size_type external_ext_size = backend->external_ext_size;
+ const bfd_size_type external_sym_size = backend->external_sym_size;
+ void (* const swap_ext_in) PARAMS ((bfd *, PTR, EXTR *))
+ = backend->swap_ext_in;
+ void (* const swap_sym_in) PARAMS ((bfd *, PTR, SYMR *))
+ = backend->swap_sym_in;
+ bfd_size_type internal_size;
+ ecoff_symbol_type *internal;
+ ecoff_symbol_type *internal_ptr;
+ asymbol *indirect_ptr;
+ char *eraw_src;
+ char *eraw_end;
+ FDR *fdr_ptr;
+ FDR *fdr_end;
+
+ /* If we've already read in the symbol table, do nothing. */
+ if (ecoff_data (abfd)->canonical_symbols != NULL)
+ return true;
+
+ /* Get the symbolic information. */
+ if (ecoff_slurp_symbolic_info (abfd) == false)
+ return false;
+ if (bfd_get_symcount (abfd) == 0)
+ return true;
+
+ internal_size = bfd_get_symcount (abfd) * sizeof (ecoff_symbol_type);
+ internal = (ecoff_symbol_type *) bfd_alloc (abfd, internal_size);
+ if (internal == NULL)
+ {
+ bfd_error = no_memory;
+ return false;
+ }
+
+ internal_ptr = internal;
+ indirect_ptr = NULL;
+ eraw_src = (char *) ecoff_data (abfd)->external_ext;
+ eraw_end = (eraw_src
+ + (ecoff_data (abfd)->symbolic_header.iextMax
+ * external_ext_size));
+ for (; eraw_src < eraw_end; eraw_src += external_ext_size, internal_ptr++)
+ {
+ EXTR internal_esym;
+
+ (*swap_ext_in) (abfd, (PTR) eraw_src, &internal_esym);
+ internal_ptr->symbol.name = (ecoff_data (abfd)->ssext
+ + internal_esym.asym.iss);
+ ecoff_set_symbol_info (abfd, &internal_esym.asym,
+ &internal_ptr->symbol, 1, &indirect_ptr);
+ /* The alpha uses a negative ifd field for section symbols. */
+ if (internal_esym.ifd >= 0)
+ internal_ptr->fdr = ecoff_data (abfd)->fdr + internal_esym.ifd;
+ else
+ internal_ptr->fdr = NULL;
+ internal_ptr->local = false;
+ internal_ptr->native = (PTR) eraw_src;
+ }
+ BFD_ASSERT (indirect_ptr == (asymbol *) NULL);
+
+ /* The local symbols must be accessed via the fdr's, because the
+ string and aux indices are relative to the fdr information. */
+ fdr_ptr = ecoff_data (abfd)->fdr;
+ fdr_end = fdr_ptr + ecoff_data (abfd)->symbolic_header.ifdMax;
+ for (; fdr_ptr < fdr_end; fdr_ptr++)
+ {
+ char *lraw_src;
+ char *lraw_end;
+
+ lraw_src = ((char *) ecoff_data (abfd)->external_sym
+ + fdr_ptr->isymBase * external_sym_size);
+ lraw_end = lraw_src + fdr_ptr->csym * external_sym_size;
+ for (;
+ lraw_src < lraw_end;
+ lraw_src += external_sym_size, internal_ptr++)
+ {
+ SYMR internal_sym;
+
+ (*swap_sym_in) (abfd, (PTR) lraw_src, &internal_sym);
+ internal_ptr->symbol.name = (ecoff_data (abfd)->ss
+ + fdr_ptr->issBase
+ + internal_sym.iss);
+ ecoff_set_symbol_info (abfd, &internal_sym,
+ &internal_ptr->symbol, 0, &indirect_ptr);
+ internal_ptr->fdr = fdr_ptr;
+ internal_ptr->local = true;
+ internal_ptr->native = (PTR) lraw_src;
+ }
+ }
+ BFD_ASSERT (indirect_ptr == (asymbol *) NULL);
+
+ ecoff_data (abfd)->canonical_symbols = internal;
+
+ return true;
+}
+
+/* Return the amount of space needed for the canonical symbols. */
+
+unsigned int
+ecoff_get_symtab_upper_bound (abfd)
+ bfd *abfd;
+{
+ if (ecoff_slurp_symbolic_info (abfd) == false
+ || bfd_get_symcount (abfd) == 0)
+ return 0;
+
+ return (bfd_get_symcount (abfd) + 1) * (sizeof (ecoff_symbol_type *));
+}
+
+/* Get the canonicals symbols. */
+
+unsigned int
+ecoff_get_symtab (abfd, alocation)
+ bfd *abfd;
+ asymbol **alocation;
+{
+ unsigned int counter = 0;
+ ecoff_symbol_type *symbase;
+ ecoff_symbol_type **location = (ecoff_symbol_type **) alocation;
+
+ if (ecoff_slurp_symbol_table (abfd) == false
+ || bfd_get_symcount (abfd) == 0)
+ return 0;
+
+ symbase = ecoff_data (abfd)->canonical_symbols;
+ while (counter < bfd_get_symcount (abfd))
+ {
+ *(location++) = symbase++;
+ counter++;
+ }
+ *location++ = (ecoff_symbol_type *) NULL;
+ return bfd_get_symcount (abfd);
+}
+
+/* Turn ECOFF type information into a printable string.
+ ecoff_emit_aggregate and ecoff_type_to_string are from
+ gcc/mips-tdump.c, with swapping added and used_ptr removed. */
+
+/* Write aggregate information to a string. */
+
+static void
+ecoff_emit_aggregate (abfd, string, rndx, isym, which)
+ bfd *abfd;
+ char *string;
+ RNDXR *rndx;
+ long isym;
+ CONST char *which;
+{
+ int ifd = rndx->rfd;
+ int indx = rndx->index;
+ int sym_base, ss_base;
+ CONST char *name;
+
+ if (ifd == 0xfff)
+ ifd = isym;
+
+ sym_base = ecoff_data (abfd)->fdr[ifd].isymBase;
+ ss_base = ecoff_data (abfd)->fdr[ifd].issBase;
+
+ if (indx == indexNil)
+ name = "/* no name */";
+ else
+ {
+ const struct ecoff_backend_data * const backend = ecoff_backend (abfd);
+ SYMR sym;
+
+ indx += sym_base;
+ (*backend->swap_sym_in) (abfd,
+ ((char *) ecoff_data (abfd)->external_sym
+ + indx * backend->external_sym_size),
+ &sym);
+ name = ecoff_data (abfd)->ss + ss_base + sym.iss;
+ }
+
+ sprintf (string,
+ "%s %s { ifd = %d, index = %d }",
+ which, name, ifd,
+ indx + ecoff_data (abfd)->symbolic_header.iextMax);
+}
+
+/* Convert the type information to string format. */
+
+static char *
+ecoff_type_to_string (abfd, aux_ptr, indx, bigendian)
+ bfd *abfd;
+ union aux_ext *aux_ptr;
+ unsigned int indx;
+ int bigendian;
+{
+ AUXU u;
+ struct qual {
+ unsigned int type;
+ int low_bound;
+ int high_bound;
+ int stride;
+ } qualifiers[7];
+
+ unsigned int basic_type;
+ int i;
+ static char buffer1[1024];
+ static char buffer2[1024];
+ char *p1 = buffer1;
+ char *p2 = buffer2;
+ RNDXR rndx;
+
+ for (i = 0; i < 7; i++)
+ {
+ qualifiers[i].low_bound = 0;
+ qualifiers[i].high_bound = 0;
+ qualifiers[i].stride = 0;
+ }
+
+ if (AUX_GET_ISYM (bigendian, &aux_ptr[indx]) == -1)
+ return "-1 (no type)";
+ ecoff_swap_tir_in (bigendian, &aux_ptr[indx++].a_ti, &u.ti);
+
+ basic_type = u.ti.bt;
+ qualifiers[0].type = u.ti.tq0;
+ qualifiers[1].type = u.ti.tq1;
+ qualifiers[2].type = u.ti.tq2;
+ qualifiers[3].type = u.ti.tq3;
+ qualifiers[4].type = u.ti.tq4;
+ qualifiers[5].type = u.ti.tq5;
+ qualifiers[6].type = tqNil;
+
+ /*
+ * Go get the basic type.
+ */
+ switch (basic_type)
+ {
+ case btNil: /* undefined */
+ strcpy (p1, "nil");
+ break;
+
+ case btAdr: /* address - integer same size as pointer */
+ strcpy (p1, "address");
+ break;
+
+ case btChar: /* character */
+ strcpy (p1, "char");
+ break;
+
+ case btUChar: /* unsigned character */
+ strcpy (p1, "unsigned char");
+ break;
+
+ case btShort: /* short */
+ strcpy (p1, "short");
+ break;
+
+ case btUShort: /* unsigned short */
+ strcpy (p1, "unsigned short");
+ break;
+
+ case btInt: /* int */
+ strcpy (p1, "int");
+ break;
+
+ case btUInt: /* unsigned int */
+ strcpy (p1, "unsigned int");
+ break;
+
+ case btLong: /* long */
+ strcpy (p1, "long");
+ break;
+
+ case btULong: /* unsigned long */
+ strcpy (p1, "unsigned long");
+ break;
+
+ case btFloat: /* float (real) */
+ strcpy (p1, "float");
+ break;
+
+ case btDouble: /* Double (real) */
+ strcpy (p1, "double");
+ break;
+
+ /* Structures add 1-2 aux words:
+ 1st word is [ST_RFDESCAPE, offset] pointer to struct def;
+ 2nd word is file index if 1st word rfd is ST_RFDESCAPE. */
+
+ case btStruct: /* Structure (Record) */
+ ecoff_swap_rndx_in (bigendian, &aux_ptr[indx].a_rndx, &rndx);
+ ecoff_emit_aggregate (abfd, p1, &rndx,
+ AUX_GET_ISYM (bigendian, &aux_ptr[indx+1]),
+ "struct");
+ indx++; /* skip aux words */
+ break;
+
+ /* Unions add 1-2 aux words:
+ 1st word is [ST_RFDESCAPE, offset] pointer to union def;
+ 2nd word is file index if 1st word rfd is ST_RFDESCAPE. */
+
+ case btUnion: /* Union */
+ ecoff_swap_rndx_in (bigendian, &aux_ptr[indx].a_rndx, &rndx);
+ ecoff_emit_aggregate (abfd, p1, &rndx,
+ AUX_GET_ISYM (bigendian, &aux_ptr[indx+1]),
+ "union");
+ indx++; /* skip aux words */
+ break;
+
+ /* Enumerations add 1-2 aux words:
+ 1st word is [ST_RFDESCAPE, offset] pointer to enum def;
+ 2nd word is file index if 1st word rfd is ST_RFDESCAPE. */
+
+ case btEnum: /* Enumeration */
+ ecoff_swap_rndx_in (bigendian, &aux_ptr[indx].a_rndx, &rndx);
+ ecoff_emit_aggregate (abfd, p1, &rndx,
+ AUX_GET_ISYM (bigendian, &aux_ptr[indx+1]),
+ "enum");
+ indx++; /* skip aux words */
+ break;
+
+ case btTypedef: /* defined via a typedef, isymRef points */
+ strcpy (p1, "typedef");
+ break;
+
+ case btRange: /* subrange of int */
+ strcpy (p1, "subrange");
+ break;
+
+ case btSet: /* pascal sets */
+ strcpy (p1, "set");
+ break;
+
+ case btComplex: /* fortran complex */
+ strcpy (p1, "complex");
+ break;
+
+ case btDComplex: /* fortran double complex */
+ strcpy (p1, "double complex");
+ break;
+
+ case btIndirect: /* forward or unnamed typedef */
+ strcpy (p1, "forward/unamed typedef");
+ break;
+
+ case btFixedDec: /* Fixed Decimal */
+ strcpy (p1, "fixed decimal");
+ break;
+
+ case btFloatDec: /* Float Decimal */
+ strcpy (p1, "float decimal");
+ break;
+
+ case btString: /* Varying Length Character String */
+ strcpy (p1, "string");
+ break;
+
+ case btBit: /* Aligned Bit String */
+ strcpy (p1, "bit");
+ break;
+
+ case btPicture: /* Picture */
+ strcpy (p1, "picture");
+ break;
+
+ case btVoid: /* Void */
+ strcpy (p1, "void");
+ break;
+
+ default:
+ sprintf (p1, "Unknown basic type %d", (int) basic_type);
+ break;
+ }
+
+ p1 += strlen (buffer1);
+
+ /*
+ * If this is a bitfield, get the bitsize.
+ */
+ if (u.ti.fBitfield)
+ {
+ int bitsize;
+
+ bitsize = AUX_GET_WIDTH (bigendian, &aux_ptr[indx++]);
+ sprintf (p1, " : %d", bitsize);
+ p1 += strlen (buffer1);
+ }
+
+
+ /*
+ * Deal with any qualifiers.
+ */
+ if (qualifiers[0].type != tqNil)
+ {
+ /*
+ * Snarf up any array bounds in the correct order. Arrays
+ * store 5 successive words in the aux. table:
+ * word 0 RNDXR to type of the bounds (ie, int)
+ * word 1 Current file descriptor index
+ * word 2 low bound
+ * word 3 high bound (or -1 if [])
+ * word 4 stride size in bits
+ */
+ for (i = 0; i < 7; i++)
+ {
+ if (qualifiers[i].type == tqArray)
+ {
+ qualifiers[i].low_bound =
+ AUX_GET_DNLOW (bigendian, &aux_ptr[indx+2]);
+ qualifiers[i].high_bound =
+ AUX_GET_DNHIGH (bigendian, &aux_ptr[indx+3]);
+ qualifiers[i].stride =
+ AUX_GET_WIDTH (bigendian, &aux_ptr[indx+4]);
+ indx += 5;
+ }
+ }
+
+ /*
+ * Now print out the qualifiers.
+ */
+ for (i = 0; i < 6; i++)
+ {
+ switch (qualifiers[i].type)
+ {
+ case tqNil:
+ case tqMax:
+ break;
+
+ case tqPtr:
+ strcpy (p2, "ptr to ");
+ p2 += sizeof ("ptr to ")-1;
+ break;
+
+ case tqVol:
+ strcpy (p2, "volatile ");
+ p2 += sizeof ("volatile ")-1;
+ break;
+
+ case tqFar:
+ strcpy (p2, "far ");
+ p2 += sizeof ("far ")-1;
+ break;
+
+ case tqProc:
+ strcpy (p2, "func. ret. ");
+ p2 += sizeof ("func. ret. ");
+ break;
+
+ case tqArray:
+ {
+ int first_array = i;
+ int j;
+
+ /* Print array bounds reversed (ie, in the order the C
+ programmer writes them). C is such a fun language.... */
+
+ while (i < 5 && qualifiers[i+1].type == tqArray)
+ i++;
+
+ for (j = i; j >= first_array; j--)
+ {
+ strcpy (p2, "array [");
+ p2 += sizeof ("array [")-1;
+ if (qualifiers[j].low_bound != 0)
+ sprintf (p2,
+ "%ld:%ld {%ld bits}",
+ (long) qualifiers[j].low_bound,
+ (long) qualifiers[j].high_bound,
+ (long) qualifiers[j].stride);
+
+ else if (qualifiers[j].high_bound != -1)
+ sprintf (p2,
+ "%ld {%ld bits}",
+ (long) (qualifiers[j].high_bound + 1),
+ (long) (qualifiers[j].stride));
+
+ else
+ sprintf (p2, " {%ld bits}", (long) (qualifiers[j].stride));
+
+ p2 += strlen (p2);
+ strcpy (p2, "] of ");
+ p2 += sizeof ("] of ")-1;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ strcpy (p2, buffer1);
+ return buffer2;
+}
+
+/* Return information about ECOFF symbol SYMBOL in RET. */
+
+void
+ecoff_get_symbol_info (abfd, symbol, ret)
+ bfd *abfd; /* Ignored. */
+ asymbol *symbol;
+ symbol_info *ret;
+{
+ bfd_symbol_info (symbol, ret);
+}
+
+/* Print information about an ECOFF symbol. */
+
+void
+ecoff_print_symbol (abfd, filep, symbol, how)
+ bfd *abfd;
+ PTR filep;
+ asymbol *symbol;
+ bfd_print_symbol_type how;
+{
+ const struct ecoff_backend_data * const backend = ecoff_backend (abfd);
+ FILE *file = (FILE *)filep;
+
+ switch (how)
+ {
+ case bfd_print_symbol_name:
+ fprintf (file, "%s", symbol->name);
+ break;
+ case bfd_print_symbol_more:
+ if (ecoffsymbol (symbol)->local)
+ {
+ SYMR ecoff_sym;
+
+ (*backend->swap_sym_in) (abfd, ecoffsymbol (symbol)->native,
+ &ecoff_sym);
+ fprintf (file, "ecoff local ");
+ fprintf_vma (file, (bfd_vma) ecoff_sym.value);
+ fprintf (file, " %x %x", (unsigned) ecoff_sym.st,
+ (unsigned) ecoff_sym.sc);
+ }
+ else
+ {
+ EXTR ecoff_ext;
+
+ (*backend->swap_ext_in) (abfd, ecoffsymbol (symbol)->native,
+ &ecoff_ext);
+ fprintf (file, "ecoff extern ");
+ fprintf_vma (file, (bfd_vma) ecoff_ext.asym.value);
+ fprintf (file, " %x %x", (unsigned) ecoff_ext.asym.st,
+ (unsigned) ecoff_ext.asym.sc);
+ }
+ break;
+ case bfd_print_symbol_all:
+ /* Print out the symbols in a reasonable way */
+ {
+ char type;
+ int pos;
+ EXTR ecoff_ext;
+ char jmptbl;
+ char cobol_main;
+ char weakext;
+
+ if (ecoffsymbol (symbol)->local)
+ {
+ (*backend->swap_sym_in) (abfd, ecoffsymbol (symbol)->native,
+ &ecoff_ext.asym);
+ type = 'l';
+ pos = ((((char *) ecoffsymbol (symbol)->native
+ - (char *) ecoff_data (abfd)->external_sym)
+ / backend->external_sym_size)
+ + ecoff_data (abfd)->symbolic_header.iextMax);
+ jmptbl = ' ';
+ cobol_main = ' ';
+ weakext = ' ';
+ }
+ else
+ {
+ (*backend->swap_ext_in) (abfd, ecoffsymbol (symbol)->native,
+ &ecoff_ext);
+ type = 'e';
+ pos = (((char *) ecoffsymbol (symbol)->native
+ - (char *) ecoff_data (abfd)->external_ext)
+ / backend->external_ext_size);
+ jmptbl = ecoff_ext.jmptbl ? 'j' : ' ';
+ cobol_main = ecoff_ext.cobol_main ? 'c' : ' ';
+ weakext = ecoff_ext.weakext ? 'w' : ' ';
+ }
+
+ fprintf (file, "[%3d] %c ",
+ pos, type);
+ fprintf_vma (file, (bfd_vma) ecoff_ext.asym.value);
+ fprintf (file, " st %x sc %x indx %x %c%c%c %s",
+ (unsigned) ecoff_ext.asym.st,
+ (unsigned) ecoff_ext.asym.sc,
+ (unsigned) ecoff_ext.asym.index,
+ jmptbl, cobol_main, weakext,
+ symbol->name);
+
+ if (ecoffsymbol (symbol)->fdr != NULL
+ && ecoff_ext.asym.index != indexNil)
+ {
+ unsigned int indx;
+ int bigendian;
+ bfd_size_type sym_base;
+ union aux_ext *aux_base;
+
+ indx = ecoff_ext.asym.index;
+
+ /* sym_base is used to map the fdr relative indices which
+ appear in the file to the position number which we are
+ using. */
+ sym_base = ecoffsymbol (symbol)->fdr->isymBase;
+ if (ecoffsymbol (symbol)->local)
+ sym_base += ecoff_data (abfd)->symbolic_header.iextMax;
+
+ /* aux_base is the start of the aux entries for this file;
+ asym.index is an offset from this. */
+ aux_base = (ecoff_data (abfd)->external_aux
+ + ecoffsymbol (symbol)->fdr->iauxBase);
+
+ /* The aux entries are stored in host byte order; the
+ order is indicated by a bit in the fdr. */
+ bigendian = ecoffsymbol (symbol)->fdr->fBigendian;
+
+ /* This switch is basically from gcc/mips-tdump.c */
+ switch (ecoff_ext.asym.st)
+ {
+ case stNil:
+ case stLabel:
+ break;
+
+ case stFile:
+ case stBlock:
+ fprintf (file, "\n End+1 symbol: %ld",
+ (long) (indx + sym_base));
+ break;
+
+ case stEnd:
+ if (ecoff_ext.asym.sc == scText
+ || ecoff_ext.asym.sc == scInfo)
+ fprintf (file, "\n First symbol: %ld",
+ (long) (indx + sym_base));
+ else
+ fprintf (file, "\n First symbol: %ld",
+ (long) (AUX_GET_ISYM (bigendian,
+ &aux_base[ecoff_ext.asym.index])
+ + sym_base));
+ break;
+
+ case stProc:
+ case stStaticProc:
+ if (ECOFF_IS_STAB (&ecoff_ext.asym))
+ ;
+ else if (ecoffsymbol (symbol)->local)
+ fprintf (file, "\n End+1 symbol: %-7ld Type: %s",
+ (long) (AUX_GET_ISYM (bigendian,
+ &aux_base[ecoff_ext.asym.index])
+ + sym_base),
+ ecoff_type_to_string (abfd, aux_base, indx + 1,
+ bigendian));
+ else
+ fprintf (file, "\n Local symbol: %d",
+ (indx
+ + sym_base
+ + ecoff_data (abfd)->symbolic_header.iextMax));
+ break;
+
+ default:
+ if (! ECOFF_IS_STAB (&ecoff_ext.asym))
+ fprintf (file, "\n Type: %s",
+ ecoff_type_to_string (abfd, aux_base, indx,
+ bigendian));
+ break;
+ }
+ }
+ }
+ break;
+ }
+}
+
+/* Read in the relocs for a section. */
+
+static boolean
+ecoff_slurp_reloc_table (abfd, section, symbols)
+ bfd *abfd;
+ asection *section;
+ asymbol **symbols;
+{
+ const struct ecoff_backend_data * const backend = ecoff_backend (abfd);
+ arelent *internal_relocs;
+ bfd_size_type external_reloc_size;
+ bfd_size_type external_relocs_size;
+ char *external_relocs;
+ arelent *rptr;
+ unsigned int i;
+
+ if (section->relocation != (arelent *) NULL
+ || section->reloc_count == 0
+ || (section->flags & SEC_CONSTRUCTOR) != 0)
+ return true;
+
+ if (ecoff_slurp_symbol_table (abfd) == false)
+ return false;
+
+ internal_relocs = (arelent *) bfd_alloc (abfd,
+ (sizeof (arelent)
+ * section->reloc_count));
+ external_reloc_size = backend->external_reloc_size;
+ external_relocs_size = external_reloc_size * section->reloc_count;
+ external_relocs = (char *) bfd_alloc (abfd, external_relocs_size);
+ if (internal_relocs == (arelent *) NULL
+ || external_relocs == (char *) NULL)
+ {
+ bfd_error = no_memory;
+ return false;
+ }
+ if (bfd_seek (abfd, section->rel_filepos, SEEK_SET) != 0)
+ return false;
+ if (bfd_read (external_relocs, 1, external_relocs_size, abfd)
+ != external_relocs_size)
+ {
+ bfd_error = system_call_error;
+ return false;
+ }
+
+ for (i = 0, rptr = internal_relocs; i < section->reloc_count; i++, rptr++)
+ {
+ struct internal_reloc intern;
+
+ (*backend->swap_reloc_in) (abfd,
+ external_relocs + i * external_reloc_size,
+ &intern);
+
+ if (intern.r_extern)
+ {
+ /* r_symndx is an index into the external symbols. */
+ BFD_ASSERT (intern.r_symndx >= 0
+ && (intern.r_symndx
+ < ecoff_data (abfd)->symbolic_header.iextMax));
+ rptr->sym_ptr_ptr = symbols + intern.r_symndx;
+ rptr->addend = 0;
+ }
+ else if (intern.r_symndx == RELOC_SECTION_NONE
+ || intern.r_symndx == RELOC_SECTION_ABS)
+ {
+ rptr->sym_ptr_ptr = bfd_abs_section.symbol_ptr_ptr;
+ rptr->addend = 0;
+ }
+ else
+ {
+ CONST char *sec_name;
+ asection *sec;
+
+ /* r_symndx is a section key. */
+ switch (intern.r_symndx)
+ {
+ case RELOC_SECTION_TEXT: sec_name = ".text"; break;
+ case RELOC_SECTION_RDATA: sec_name = ".rdata"; break;
+ case RELOC_SECTION_DATA: sec_name = ".data"; break;
+ case RELOC_SECTION_SDATA: sec_name = ".sdata"; break;
+ case RELOC_SECTION_SBSS: sec_name = ".sbss"; break;
+ case RELOC_SECTION_BSS: sec_name = ".bss"; break;
+ case RELOC_SECTION_INIT: sec_name = ".init"; break;
+ case RELOC_SECTION_LIT8: sec_name = ".lit8"; break;
+ case RELOC_SECTION_LIT4: sec_name = ".lit4"; break;
+ case RELOC_SECTION_XDATA: sec_name = ".xdata"; break;
+ case RELOC_SECTION_PDATA: sec_name = ".pdata"; break;
+ case RELOC_SECTION_LITA: sec_name = ".lita"; break;
+ default: abort ();
+ }
+
+ sec = bfd_get_section_by_name (abfd, sec_name);
+ if (sec == (asection *) NULL)
+ abort ();
+ rptr->sym_ptr_ptr = sec->symbol_ptr_ptr;
+
+ rptr->addend = - bfd_get_section_vma (abfd, sec);
+ }
+
+ rptr->address = intern.r_vaddr - bfd_get_section_vma (abfd, section);
+
+ /* Let the backend select the howto field and do any other
+ required processing. */
+ (*backend->finish_reloc) (abfd, &intern, rptr);
+ }
+
+ bfd_release (abfd, external_relocs);
+
+ section->relocation = internal_relocs;
+
+ return true;
+}
+
+/* Get a canonical list of relocs. */
+
+unsigned int
+ecoff_canonicalize_reloc (abfd, section, relptr, symbols)
+ bfd *abfd;
+ asection *section;
+ arelent **relptr;
+ asymbol **symbols;
+{
+ unsigned int count;
+
+ if (section->flags & SEC_CONSTRUCTOR)
+ {
+ arelent_chain *chain;
+
+ /* This section has relocs made up by us, not the file, so take
+ them out of their chain and place them into the data area
+ provided. */
+ for (count = 0, chain = section->constructor_chain;
+ count < section->reloc_count;
+ count++, chain = chain->next)
+ *relptr++ = &chain->relent;
+ }
+ else
+ {
+ arelent *tblptr;
+
+ if (ecoff_slurp_reloc_table (abfd, section, symbols) == false)
+ return 0;
+
+ tblptr = section->relocation;
+ if (tblptr == (arelent *) NULL)
+ return 0;
+
+ for (count = 0; count < section->reloc_count; count++)
+ *relptr++ = tblptr++;
+ }
+
+ *relptr = (arelent *) NULL;
+
+ return section->reloc_count;
+}
+
+/* Provided a BFD, a section and an offset into the section, calculate
+ and return the name of the source file and the line nearest to the
+ wanted location. */
+
+boolean
+ecoff_find_nearest_line (abfd,
+ section,
+ ignore_symbols,
+ offset,
+ filename_ptr,
+ functionname_ptr,
+ retline_ptr)
+ bfd *abfd;
+ asection *section;
+ asymbol **ignore_symbols;
+ bfd_vma offset;
+ CONST char **filename_ptr;
+ CONST char **functionname_ptr;
+ unsigned int *retline_ptr;
+{
+ const struct ecoff_backend_data * const backend = ecoff_backend (abfd);
+ FDR *fdr_ptr;
+ FDR *fdr_start;
+ FDR *fdr_end;
+ FDR *fdr_hold;
+ bfd_size_type external_pdr_size;
+ char *pdr_ptr;
+ char *pdr_end;
+ PDR pdr;
+ unsigned char *line_ptr;
+ unsigned char *line_end;
+ int lineno;
+
+ /* If we're not in the .text section, we don't have any line
+ numbers. */
+ if (strcmp (section->name, _TEXT) != 0
+ || offset < ecoff_data (abfd)->text_start
+ || offset >= ecoff_data (abfd)->text_end)
+ return false;
+
+ /* Make sure we have the FDR's. */
+ if (ecoff_slurp_symbolic_info (abfd) == false
+ || bfd_get_symcount (abfd) == 0)
+ return false;
+
+ /* Each file descriptor (FDR) has a memory address. Here we track
+ down which FDR we want. The FDR's are stored in increasing
+ memory order. If speed is ever important, this can become a
+ binary search. We must ignore FDR's with no PDR entries; they
+ will have the adr of the FDR before or after them. */
+ fdr_start = ecoff_data (abfd)->fdr;
+ fdr_end = fdr_start + ecoff_data (abfd)->symbolic_header.ifdMax;
+ fdr_hold = (FDR *) NULL;
+ for (fdr_ptr = fdr_start; fdr_ptr < fdr_end; fdr_ptr++)
+ {
+ if (fdr_ptr->cpd == 0)
+ continue;
+ if (offset < fdr_ptr->adr)
+ break;
+ fdr_hold = fdr_ptr;
+ }
+ if (fdr_hold == (FDR *) NULL)
+ return false;
+ fdr_ptr = fdr_hold;
+
+ /* Each FDR has a list of procedure descriptors (PDR). PDR's also
+ have an address, which is relative to the FDR address, and are
+ also stored in increasing memory order. */
+ offset -= fdr_ptr->adr;
+ external_pdr_size = backend->external_pdr_size;
+ pdr_ptr = ((char *) ecoff_data (abfd)->external_pdr
+ + fdr_ptr->ipdFirst * external_pdr_size);
+ pdr_end = pdr_ptr + fdr_ptr->cpd * external_pdr_size;
+ (*backend->swap_pdr_in) (abfd, (PTR) pdr_ptr, &pdr);
+
+ /* The address of the first PDR is an offset which applies to the
+ addresses of all the PDR's. */
+ offset += pdr.adr;
+
+ for (pdr_ptr += external_pdr_size;
+ pdr_ptr < pdr_end;
+ pdr_ptr += external_pdr_size)
+ {
+ (*backend->swap_pdr_in) (abfd, (PTR) pdr_ptr, &pdr);
+ if (offset < pdr.adr)
+ break;
+ }
+
+ /* Now we can look for the actual line number. The line numbers are
+ stored in a very funky format, which I won't try to describe.
+ Note that right here pdr_ptr and pdr hold the PDR *after* the one
+ we want; we need this to compute line_end. */
+ line_end = ecoff_data (abfd)->line;
+ if (pdr_ptr == pdr_end)
+ line_end += fdr_ptr->cbLineOffset + fdr_ptr->cbLine;
+ else
+ line_end += fdr_ptr->cbLineOffset + pdr.cbLineOffset;
+
+ /* Now change pdr and pdr_ptr to the one we want. */
+ pdr_ptr -= external_pdr_size;
+ (*backend->swap_pdr_in) (abfd, (PTR) pdr_ptr, &pdr);
+
+ offset -= pdr.adr;
+ lineno = pdr.lnLow;
+ line_ptr = (ecoff_data (abfd)->line
+ + fdr_ptr->cbLineOffset
+ + pdr.cbLineOffset);
+ while (line_ptr < line_end)
+ {
+ int delta;
+ int count;
+
+ delta = *line_ptr >> 4;
+ if (delta >= 0x8)
+ delta -= 0x10;
+ count = (*line_ptr & 0xf) + 1;
+ ++line_ptr;
+ if (delta == -8)
+ {
+ delta = (((line_ptr[0]) & 0xff) << 8) + ((line_ptr[1]) & 0xff);
+ if (delta >= 0x8000)
+ delta -= 0x10000;
+ line_ptr += 2;
+ }
+ lineno += delta;
+ if (offset < count * 4)
+ break;
+ offset -= count * 4;
+ }
+
+ /* If fdr_ptr->rss is -1, then this file does not have full symbols,
+ at least according to gdb/mipsread.c. */
+ if (fdr_ptr->rss == -1)
+ {
+ *filename_ptr = NULL;
+ if (pdr.isym == -1)
+ *functionname_ptr = NULL;
+ else
+ {
+ EXTR proc_ext;
+
+ (*backend->swap_ext_in) (abfd,
+ ((char *) ecoff_data (abfd)->external_ext
+ + pdr.isym * backend->external_ext_size),
+ &proc_ext);
+ *functionname_ptr = ecoff_data (abfd)->ssext + proc_ext.asym.iss;
+ }
+ }
+ else
+ {
+ SYMR proc_sym;
+
+ *filename_ptr = ecoff_data (abfd)->ss + fdr_ptr->issBase + fdr_ptr->rss;
+ (*backend->swap_sym_in) (abfd,
+ ((char *) ecoff_data (abfd)->external_sym
+ + ((fdr_ptr->isymBase + pdr.isym)
+ * backend->external_sym_size)),
+ &proc_sym);
+ *functionname_ptr = (ecoff_data (abfd)->ss
+ + fdr_ptr->issBase
+ + proc_sym.iss);
+ }
+ if (lineno == ilineNil)
+ lineno = 0;
+ *retline_ptr = lineno;
+ return true;
+}
+
+/* We can't use the generic linking routines for ECOFF, because we
+ have to handle all the debugging information. The generic link
+ routine just works out the section contents and attaches a list of
+ symbols.
+
+ We link by looping over all the seclets. We make two passes. On
+ the first we set the actual section contents and determine the size
+ of the debugging information. On the second we accumulate the
+ debugging information and write it out.
+
+ This currently always accumulates the debugging information, which
+ is incorrect, because it ignores the -s and -S options of the
+ linker. The linker needs to be modified to give us that
+ information in a more useful format (currently it just provides a
+ list of symbols which should appear in the output file). */
+
+/* Clear the output_has_begun flag for all the input BFD's. We use it
+ to avoid linking in the debugging information for a BFD more than
+ once. */
+
+static void
+ecoff_clear_output_flags (abfd)
+ bfd *abfd;
+{
+ register asection *o;
+ register bfd_seclet_type *p;
+
+ for (o = abfd->sections; o != (asection *) NULL; o = o->next)
+ for (p = o->seclets_head;
+ p != (bfd_seclet_type *) NULL;
+ p = p->next)
+ if (p->type == bfd_indirect_seclet)
+ p->u.indirect.section->owner->output_has_begun = false;
+}
+
+/* Handle an indirect seclet on the first pass. Set the contents of
+ the output section, and accumulate the debugging information if
+ any. */
+
+static boolean
+ecoff_rel (output_bfd, seclet, output_section, data, relocateable)
+ bfd *output_bfd;
+ bfd_seclet_type *seclet;
+ asection *output_section;
+ PTR data;
+ boolean relocateable;
+{
+ bfd *input_bfd;
+ HDRR *output_symhdr;
+ HDRR *input_symhdr;
+
+ if ((output_section->flags & SEC_HAS_CONTENTS)
+ && !(output_section->flags & SEC_NEVER_LOAD)
+ && (output_section->flags & SEC_LOAD)
+ && seclet->size)
+ {
+ data = (PTR) bfd_get_relocated_section_contents (output_bfd,
+ seclet,
+ data,
+ relocateable);
+ if (bfd_set_section_contents (output_bfd,
+ output_section,
+ data,
+ seclet->offset,
+ seclet->size)
+ == false)
+ {
+ abort();
+ }
+ }
+
+ input_bfd = seclet->u.indirect.section->owner;
+
+ /* We want to figure out how much space will be required to
+ incorporate all the debugging information from input_bfd. We use
+ the output_has_begun field to avoid adding it in more than once.
+ The actual incorporation is done in the second pass, in
+ ecoff_get_debug. The code has to parallel that code in its
+ manipulations of output_symhdr. */
+
+ if (input_bfd->output_has_begun)
+ return true;
+ input_bfd->output_has_begun = true;
+
+ output_symhdr = &ecoff_data (output_bfd)->symbolic_header;
+
+ if (input_bfd->xvec->flavour != bfd_target_ecoff_flavour)
+ {
+ asymbol **symbols;
+ asymbol **sym_ptr;
+ asymbol **sym_end;
+
+ /* We just accumulate local symbols from a non-ECOFF BFD. The
+ external symbols are handled separately. */
+
+ symbols = (asymbol **) bfd_alloc (output_bfd,
+ get_symtab_upper_bound (input_bfd));
+ if (symbols == (asymbol **) NULL)
+ {
+ bfd_error = no_memory;
+ return false;
+ }
+ sym_end = symbols + bfd_canonicalize_symtab (input_bfd, symbols);
+
+ for (sym_ptr = symbols; sym_ptr < sym_end; sym_ptr++)
+ {
+ size_t len;
+
+ len = strlen ((*sym_ptr)->name);
+ if (((*sym_ptr)->flags & BSF_EXPORT) == 0)
+ {
+ ++output_symhdr->isymMax;
+ output_symhdr->issMax += len + 1;
+ }
+ }
+
+ bfd_release (output_bfd, (PTR) symbols);
+
+ ++output_symhdr->ifdMax;
+
+ return true;
+ }
+
+ /* We simply add in the information from another ECOFF BFD. First
+ we make sure we have the symbolic information. */
+ if (ecoff_slurp_symbol_table (input_bfd) == false)
+ return false;
+ if (bfd_get_symcount (input_bfd) == 0)
+ return true;
+
+ input_symhdr = &ecoff_data (input_bfd)->symbolic_header;
+
+ /* Figure out how much information we are going to be putting in.
+ The external symbols are handled separately. */
+ output_symhdr->ilineMax += input_symhdr->ilineMax;
+ output_symhdr->cbLine += input_symhdr->cbLine;
+ output_symhdr->idnMax += input_symhdr->idnMax;
+ output_symhdr->ipdMax += input_symhdr->ipdMax;
+ output_symhdr->isymMax += input_symhdr->isymMax;
+ output_symhdr->ioptMax += input_symhdr->ioptMax;
+ output_symhdr->iauxMax += input_symhdr->iauxMax;
+ output_symhdr->issMax += input_symhdr->issMax;
+ output_symhdr->ifdMax += input_symhdr->ifdMax;
+
+ /* The RFD's are special, since we create them if needed. */
+ if (input_symhdr->crfd > 0)
+ output_symhdr->crfd += input_symhdr->crfd;
+ else
+ output_symhdr->crfd += input_symhdr->ifdMax;
+
+ return true;
+}
+
+/* Handle an arbitrary seclet on the first pass. */
+
+static boolean
+ecoff_dump_seclet (abfd, seclet, section, data, relocateable)
+ bfd *abfd;
+ bfd_seclet_type *seclet;
+ asection *section;
+ PTR data;
+ boolean relocateable;
+{
+ switch (seclet->type)
+ {
+ case bfd_indirect_seclet:
+ /* The contents of this section come from another one somewhere
+ else. */
+ return ecoff_rel (abfd, seclet, section, data, relocateable);
+
+ case bfd_fill_seclet:
+ /* Fill in the section with fill.value. This is used to pad out
+ sections, but we must avoid padding the .bss section. */
+ if ((section->flags & SEC_HAS_CONTENTS) == 0)
+ {
+ if (seclet->u.fill.value != 0)
+ abort ();
+ }
+ else
+ {
+ char *d = (char *) bfd_alloc (abfd, seclet->size);
+ unsigned int i;
+ boolean ret;
+
+ for (i = 0; i < seclet->size; i+=2)
+ d[i] = seclet->u.fill.value >> 8;
+ for (i = 1; i < seclet->size; i+=2)
+ d[i] = seclet->u.fill.value;
+ ret = bfd_set_section_contents (abfd, section, d, seclet->offset,
+ seclet->size);
+ bfd_release (abfd, (PTR) d);
+ return ret;
+ }
+ break;
+
+ default:
+ abort();
+ }
+
+ return true;
+}
+
+/* Add a string to the debugging information we are accumulating for a
+ file. Return the offset from the fdr string base or from the
+ external string base. */
+
+static long
+ecoff_add_string (output_bfd, fdr, string, external)
+ bfd *output_bfd;
+ FDR *fdr;
+ CONST char *string;
+ boolean external;
+{
+ HDRR *symhdr;
+ size_t len;
+ long ret;
+
+ symhdr = &ecoff_data (output_bfd)->symbolic_header;
+ len = strlen (string);
+ if (external)
+ {
+ strcpy (ecoff_data (output_bfd)->ssext + symhdr->issExtMax, string);
+ ret = symhdr->issExtMax;
+ symhdr->issExtMax += len + 1;
+ }
+ else
+ {
+ strcpy (ecoff_data (output_bfd)->ss + symhdr->issMax, string);
+ ret = fdr->cbSs;
+ symhdr->issMax += len + 1;
+ fdr->cbSs += len + 1;
+ }
+ return ret;
+}
+
+/* Accumulate the debugging information from an input section. */
+
+static boolean
+ecoff_get_debug (output_bfd, seclet, section, relocateable)
+ bfd *output_bfd;
+ bfd_seclet_type *seclet;
+ asection *section;
+ boolean relocateable;
+{
+ const struct ecoff_backend_data * const backend = ecoff_backend (output_bfd);
+ const bfd_size_type external_sym_size = backend->external_sym_size;
+ const bfd_size_type external_pdr_size = backend->external_pdr_size;
+ const bfd_size_type external_fdr_size = backend->external_fdr_size;
+ const bfd_size_type external_rfd_size = backend->external_rfd_size;
+ void (* const swap_sym_in) PARAMS ((bfd *, PTR, SYMR *))
+ = backend->swap_sym_in;
+ void (* const swap_sym_out) PARAMS ((bfd *, const SYMR *, PTR))
+ = backend->swap_sym_out;
+ void (* const swap_pdr_in) PARAMS ((bfd *, PTR, PDR *))
+ = backend->swap_pdr_in;
+ void (* const swap_fdr_out) PARAMS ((bfd *, const FDR *, PTR))
+ = backend->swap_fdr_out;
+ void (* const swap_rfd_out) PARAMS ((bfd *, const RFDT *, PTR))
+ = backend->swap_rfd_out;
+ bfd *input_bfd;
+ HDRR *output_symhdr;
+ HDRR *input_symhdr;
+ ecoff_data_type *output_ecoff;
+ ecoff_data_type *input_ecoff;
+ unsigned int count;
+ char *sym_out;
+ ecoff_symbol_type *esym_ptr;
+ ecoff_symbol_type *esym_end;
+ FDR *fdr_ptr;
+ FDR *fdr_end;
+ char *fdr_out;
+
+ input_bfd = seclet->u.indirect.section->owner;
+
+ /* Don't get the information more than once. */
+ if (input_bfd->output_has_begun)
+ return true;
+ input_bfd->output_has_begun = true;
+
+ output_ecoff = ecoff_data (output_bfd);
+ output_symhdr = &output_ecoff->symbolic_header;
+
+ if (input_bfd->xvec->flavour != bfd_target_ecoff_flavour)
+ {
+ FDR fdr;
+ asymbol **symbols;
+ asymbol **sym_ptr;
+ asymbol **sym_end;
+
+ /* This is not an ECOFF BFD. Just gather the symbols. */
+
+ memset (&fdr, 0, sizeof fdr);
+
+ fdr.adr = bfd_get_section_vma (output_bfd, section) + seclet->offset;
+ fdr.issBase = output_symhdr->issMax;
+ fdr.cbSs = 0;
+ fdr.rss = ecoff_add_string (output_bfd,
+ &fdr,
+ bfd_get_filename (input_bfd),
+ false);
+ fdr.isymBase = output_symhdr->isymMax;
+
+ /* Get the local symbols from the input BFD. */
+ symbols = (asymbol **) bfd_alloc (output_bfd,
+ get_symtab_upper_bound (input_bfd));
+ if (symbols == (asymbol **) NULL)
+ {
+ bfd_error = no_memory;
+ return false;
+ }
+ sym_end = symbols + bfd_canonicalize_symtab (input_bfd, symbols);
+
+ /* Handle the local symbols. Any external symbols are handled
+ separately. */
+ fdr.csym = 0;
+ for (sym_ptr = symbols; sym_ptr != sym_end; sym_ptr++)
+ {
+ SYMR internal_sym;
+
+ if (((*sym_ptr)->flags & BSF_EXPORT) != 0)
+ continue;
+ memset (&internal_sym, 0, sizeof internal_sym);
+ internal_sym.iss = ecoff_add_string (output_bfd,
+ &fdr,
+ (*sym_ptr)->name,
+ false);
+
+ if (bfd_is_com_section ((*sym_ptr)->section)
+ || (*sym_ptr)->section == &bfd_und_section)
+ internal_sym.value = (*sym_ptr)->value;
+ else
+ internal_sym.value = ((*sym_ptr)->value
+ + (*sym_ptr)->section->output_offset
+ + (*sym_ptr)->section->output_section->vma);
+ internal_sym.st = stNil;
+ internal_sym.sc = scUndefined;
+ internal_sym.index = indexNil;
+ (*swap_sym_out) (output_bfd, &internal_sym,
+ ((char *) output_ecoff->external_sym
+ + output_symhdr->isymMax * external_sym_size));
+ ++fdr.csym;
+ ++output_symhdr->isymMax;
+ }
+
+ bfd_release (output_bfd, (PTR) symbols);
+
+ /* Leave everything else in the FDR zeroed out. This will cause
+ the lang field to be langC. The fBigendian field will
+ indicate little endian format, but it doesn't matter because
+ it only applies to aux fields and there are none. */
+
+ (*swap_fdr_out) (output_bfd, &fdr,
+ ((char *) output_ecoff->external_fdr
+ + output_symhdr->ifdMax * external_fdr_size));
+ ++output_symhdr->ifdMax;
+ return true;
+ }
+
+ /* This is an ECOFF BFD. We want to grab the information from
+ input_bfd and attach it to output_bfd. */
+ count = bfd_get_symcount (input_bfd);
+ if (count == 0)
+ return true;
+ input_ecoff = ecoff_data (input_bfd);
+ input_symhdr = &input_ecoff->symbolic_header;
+
+ /* I think that it is more efficient to simply copy the debugging
+ information from the input BFD to the output BFD. Because ECOFF
+ uses relative pointers for most of the debugging information,
+ only a little of it has to be changed at all. */
+
+ /* Swap in the local symbols, adjust their values, and swap them out
+ again. The external symbols are handled separately. */
+ sym_out = ((char *) output_ecoff->external_sym
+ + output_symhdr->isymMax * external_sym_size);
+
+ esym_ptr = ecoff_data (input_bfd)->canonical_symbols;
+ esym_end = esym_ptr + count;
+ for (; esym_ptr < esym_end; esym_ptr++)
+ {
+ if (esym_ptr->local)
+ {
+ SYMR sym;
+
+ (*swap_sym_in) (input_bfd, esym_ptr->native, &sym);
+
+ /* If we're producing an executable, move common symbols
+ into bss. */
+ if (relocateable == false)
+ {
+ if (sym.sc == scCommon)
+ sym.sc = scBss;
+ else if (sym.sc == scSCommon)
+ sym.sc = scSBss;
+ }
+
+ if (! bfd_is_com_section (esym_ptr->symbol.section)
+ && (esym_ptr->symbol.flags & BSF_DEBUGGING) == 0
+ && esym_ptr->symbol.section != &bfd_und_section)
+ sym.value = (esym_ptr->symbol.value
+ + esym_ptr->symbol.section->output_offset
+ + esym_ptr->symbol.section->output_section->vma);
+ (*swap_sym_out) (output_bfd, &sym, sym_out);
+ sym_out += external_sym_size;
+ }
+ }
+
+ /* That should have accounted for all the local symbols in
+ input_bfd. */
+
+ /* Copy the information that does not need swapping. */
+ memcpy (output_ecoff->line + output_symhdr->cbLine,
+ input_ecoff->line,
+ input_symhdr->cbLine * sizeof (unsigned char));
+ memcpy (output_ecoff->external_aux + output_symhdr->iauxMax,
+ input_ecoff->external_aux,
+ input_symhdr->iauxMax * sizeof (union aux_ext));
+ memcpy (output_ecoff->ss + output_symhdr->issMax,
+ input_ecoff->ss,
+ input_symhdr->issMax * sizeof (char));
+
+ /* Some of the information may need to be swapped. */
+ if (output_bfd->xvec->header_byteorder_big_p
+ == input_bfd->xvec->header_byteorder_big_p)
+ {
+ /* The two BFD's have the same endianness, so memcpy will
+ suffice. */
+ if (input_symhdr->idnMax > 0)
+ memcpy (((char *) output_ecoff->external_dnr
+ + output_symhdr->idnMax * backend->external_dnr_size),
+ input_ecoff->external_dnr,
+ input_symhdr->idnMax * backend->external_dnr_size);
+ if (input_symhdr->ipdMax > 0)
+ memcpy (((char *) output_ecoff->external_pdr
+ + output_symhdr->ipdMax * external_pdr_size),
+ input_ecoff->external_pdr,
+ input_symhdr->ipdMax * external_pdr_size);
+ if (input_symhdr->ioptMax > 0)
+ memcpy (((char *) output_ecoff->external_opt
+ + output_symhdr->ioptMax * backend->external_opt_size),
+ input_ecoff->external_opt,
+ input_symhdr->ioptMax * backend->external_opt_size);
+ }
+ else
+ {
+ bfd_size_type sz;
+ char *in;
+ char *end;
+ char *out;
+
+ /* The two BFD's have different endianness, so we must swap
+ everything in and out. This code would always work, but it
+ would be slow in the normal case. */
+ sz = backend->external_dnr_size;
+ in = (char *) input_ecoff->external_dnr;
+ end = in + input_symhdr->idnMax * sz;
+ out = (char *) output_ecoff->external_dnr + output_symhdr->idnMax * sz;
+ for (; in < end; in += sz, out += sz)
+ {
+ DNR dnr;
+
+ (*backend->swap_dnr_in) (input_bfd, in, &dnr);
+ (*backend->swap_dnr_out) (output_bfd, &dnr, out);
+ }
+
+ sz = external_pdr_size;
+ in = (char *) input_ecoff->external_pdr;
+ end = in + input_symhdr->ipdMax * sz;
+ out = (char *) output_ecoff->external_pdr + output_symhdr->ipdMax * sz;
+ for (; in < end; in += sz, out += sz)
+ {
+ PDR pdr;
+
+ (*swap_pdr_in) (input_bfd, in, &pdr);
+ (*backend->swap_pdr_out) (output_bfd, &pdr, out);
+ }
+
+ sz = backend->external_opt_size;
+ in = (char *) input_ecoff->external_opt;
+ end = in + input_symhdr->ioptMax * sz;
+ out = (char *) output_ecoff->external_opt + output_symhdr->ioptMax * sz;
+ for (; in < end; in += sz, out += sz)
+ {
+ OPTR opt;
+
+ (*backend->swap_opt_in) (input_bfd, in, &opt);
+ (*backend->swap_opt_out) (output_bfd, &opt, out);
+ }
+ }
+
+ /* Set ifdbase so that the external symbols know how to adjust their
+ ifd values. */
+ input_ecoff->ifdbase = output_symhdr->ifdMax;
+
+ fdr_ptr = input_ecoff->fdr;
+ fdr_end = fdr_ptr + input_symhdr->ifdMax;
+ fdr_out = ((char *) output_ecoff->external_fdr
+ + output_symhdr->ifdMax * external_fdr_size);
+ for (; fdr_ptr < fdr_end; fdr_ptr++, fdr_out += external_fdr_size)
+ {
+ FDR fdr;
+ unsigned long pdr_off;
+
+ fdr = *fdr_ptr;
+
+ /* The memory address for this fdr is the address for the seclet
+ plus the offset to this fdr within input_bfd. For some
+ reason the offset of the first procedure pointer is also
+ added in. */
+ if (fdr.cpd == 0)
+ pdr_off = 0;
+ else
+ {
+ PDR pdr;
+
+ (*swap_pdr_in) (input_bfd,
+ ((char *) input_ecoff->external_pdr
+ + fdr.ipdFirst * external_pdr_size),
+ &pdr);
+ pdr_off = pdr.adr;
+ }
+ fdr.adr = (bfd_get_section_vma (output_bfd, section)
+ + seclet->offset
+ + (fdr_ptr->adr - input_ecoff->fdr->adr)
+ + pdr_off);
+
+ fdr.issBase += output_symhdr->issMax;
+ fdr.isymBase += output_symhdr->isymMax;
+ fdr.ilineBase += output_symhdr->ilineMax;
+ fdr.ioptBase += output_symhdr->ioptMax;
+ fdr.ipdFirst += output_symhdr->ipdMax;
+ fdr.iauxBase += output_symhdr->iauxMax;
+ fdr.rfdBase += output_symhdr->crfd;
+
+ /* If there are no RFD's, we are going to add some. We don't
+ want to adjust irfd for this, so that all the FDR's can share
+ the RFD's. */
+ if (input_symhdr->crfd == 0)
+ fdr.crfd = input_symhdr->ifdMax;
+
+ if (fdr.cbLine != 0)
+ fdr.cbLineOffset += output_symhdr->cbLine;
+
+ (*swap_fdr_out) (output_bfd, &fdr, fdr_out);
+ }
+
+ if (input_symhdr->crfd > 0)
+ {
+ void (* const swap_rfd_in) PARAMS ((bfd *, PTR, RFDT *))
+ = backend->swap_rfd_in;
+ char *rfd_in;
+ char *rfd_end;
+ char *rfd_out;
+
+ /* Swap and adjust the RFD's. RFD's are only created by the
+ linker, so this will only be necessary if one of the input
+ files is the result of a partial link. Presumably all
+ necessary RFD's are present. */
+ rfd_in = (char *) input_ecoff->external_rfd;
+ rfd_end = rfd_in + input_symhdr->crfd * external_rfd_size;
+ rfd_out = ((char *) output_ecoff->external_rfd
+ + output_symhdr->crfd * external_rfd_size);
+ for (;
+ rfd_in < rfd_end;
+ rfd_in += external_rfd_size, rfd_out += external_rfd_size)
+ {
+ RFDT rfd;
+
+ (*swap_rfd_in) (input_bfd, rfd_in, &rfd);
+ rfd += output_symhdr->ifdMax;
+ (*swap_rfd_out) (output_bfd, &rfd, rfd_out);
+ }
+ output_symhdr->crfd += input_symhdr->crfd;
+ }
+ else
+ {
+ char *rfd_out;
+ char *rfd_end;
+ RFDT rfd;
+
+ /* Create RFD's. Some of the debugging information includes
+ relative file indices. These indices are taken as indices to
+ the RFD table if there is one, or to the global table if
+ there is not. If we did not create RFD's, we would have to
+ parse and adjust all the debugging information which contains
+ file indices. */
+ rfd = output_symhdr->ifdMax;
+ rfd_out = ((char *) output_ecoff->external_rfd
+ + output_symhdr->crfd * external_rfd_size);
+ rfd_end = rfd_out + input_symhdr->ifdMax * external_rfd_size;
+ for (; rfd_out < rfd_end; rfd_out += external_rfd_size, rfd++)
+ (*swap_rfd_out) (output_bfd, &rfd, rfd_out);
+ output_symhdr->crfd += input_symhdr->ifdMax;
+ }
+
+ /* Combine the register masks. Not all of these are used on all
+ targets, but that's OK because only the relevant ones will be
+ swapped in and out. */
+ {
+ int i;
+
+ output_ecoff->gprmask |= input_ecoff->gprmask;
+ output_ecoff->fprmask |= input_ecoff->fprmask;
+ for (i = 0; i < 4; i++)
+ output_ecoff->cprmask[i] |= input_ecoff->cprmask[i];
+ }
+
+ /* Update the counts. */
+ output_symhdr->ilineMax += input_symhdr->ilineMax;
+ output_symhdr->cbLine += input_symhdr->cbLine;
+ output_symhdr->idnMax += input_symhdr->idnMax;
+ output_symhdr->ipdMax += input_symhdr->ipdMax;
+ output_symhdr->isymMax += input_symhdr->isymMax;
+ output_symhdr->ioptMax += input_symhdr->ioptMax;
+ output_symhdr->iauxMax += input_symhdr->iauxMax;
+ output_symhdr->issMax += input_symhdr->issMax;
+ output_symhdr->ifdMax += input_symhdr->ifdMax;
+
+ return true;
+}
+
+/* This is the actual link routine. It makes two passes over all the
+ seclets. */
+
+boolean
+ecoff_bfd_seclet_link (abfd, data, relocateable)
+ bfd *abfd;
+ PTR data;
+ boolean relocateable;
+{
+ const struct ecoff_backend_data * const backend = ecoff_backend (abfd);
+ HDRR *symhdr;
+ int ipass;
+ register asection *o;
+ register bfd_seclet_type *p;
+ asymbol **sym_ptr_ptr;
+ bfd_size_type debug_align;
+ bfd_size_type size;
+ char *raw;
+
+ /* We accumulate the debugging information counts in the symbolic
+ header. */
+ symhdr = &ecoff_data (abfd)->symbolic_header;
+ symhdr->magic = backend->sym_magic;
+ /* FIXME: What should the version stamp be? */
+ symhdr->vstamp = 0;
+ symhdr->ilineMax = 0;
+ symhdr->cbLine = 0;
+ symhdr->idnMax = 0;
+ symhdr->ipdMax = 0;
+ symhdr->isymMax = 0;
+ symhdr->ioptMax = 0;
+ symhdr->iauxMax = 0;
+ symhdr->issMax = 0;
+ symhdr->issExtMax = 0;
+ symhdr->ifdMax = 0;
+ symhdr->crfd = 0;
+ symhdr->iextMax = 0;
+
+ /* We need to copy over the debugging symbols from each input BFD.
+ When we do this copying, we have to adjust the text address in
+ the FDR structures, so we have to know the text address used for
+ the input BFD. Since we only want to copy the symbols once per
+ input BFD, but we are going to look at each input BFD multiple
+ times (once for each section it provides), we arrange to always
+ look at the text section first. That means that when we copy the
+ debugging information, we always know the text address. So we
+ actually do each pass in two sub passes; first the text sections,
+ then the non-text sections. We use the output_has_begun flag to
+ determine whether we have copied over the debugging information
+ yet. */
+
+ /* Do the first pass: set the output section contents and count the
+ debugging information. */
+ ecoff_clear_output_flags (abfd);
+ for (ipass = 0; ipass < 2; ipass++)
+ {
+ for (o = abfd->sections; o != (asection *) NULL; o = o->next)
+ {
+ /* If this is a fake section, just forget it. The register
+ information is handled in another way. */
+ if (strcmp (o->name, SCOMMON) == 0
+ || strcmp (o->name, REGINFO) == 0)
+ continue;
+
+ /* For SEC_CODE sections, (flags & SEC_CODE) == 0 is false,
+ so they are done on pass 0. For other sections the
+ expression is true, so they are done on pass 1. */
+ if (((o->flags & SEC_CODE) == 0) != ipass)
+ continue;
+
+ for (p = o->seclets_head;
+ p != (bfd_seclet_type *) NULL;
+ p = p->next)
+ {
+ if (ecoff_dump_seclet (abfd, p, o, data, relocateable)
+ == false)
+ return false;
+ }
+ }
+ }
+
+ /* We handle the external symbols differently. We use the ones
+ attached to the output_bfd. The linker will have already
+ determined which symbols are to be attached. Here we just
+ determine how much space we will need for them. */
+ sym_ptr_ptr = bfd_get_outsymbols (abfd);
+ if (sym_ptr_ptr != NULL)
+ {
+ asymbol **sym_end;
+
+ sym_end = sym_ptr_ptr + bfd_get_symcount (abfd);
+ for (; sym_ptr_ptr < sym_end; sym_ptr_ptr++)
+ {
+ if (((*sym_ptr_ptr)->flags & BSF_DEBUGGING) == 0
+ && ((*sym_ptr_ptr)->flags & BSF_LOCAL) == 0)
+ {
+ ++symhdr->iextMax;
+ symhdr->issExtMax += strlen ((*sym_ptr_ptr)->name) + 1;
+ }
+ }
+ }
+
+ /* Adjust the counts so that structures are longword aligned. */
+ debug_align = backend->debug_align;
+ --debug_align;
+ symhdr->cbLine = (symhdr->cbLine + debug_align) &~ debug_align;
+ symhdr->issMax = (symhdr->issMax + debug_align) &~ debug_align;
+ symhdr->issExtMax = (symhdr->issExtMax + debug_align) &~ debug_align;
+
+ /* Now the counts in symhdr are the correct size for the debugging
+ information. We allocate the right amount of space, and reset
+ the counts so that the second pass can use them as indices. It
+ would be possible to output the debugging information directly to
+ the file in pass 2, rather than to build it in memory and then
+ write it out. Outputting to the file would require a lot of
+ seeks and small writes, though, and I think this approach is
+ faster. */
+ size = (symhdr->cbLine * sizeof (unsigned char)
+ + symhdr->idnMax * backend->external_dnr_size
+ + symhdr->ipdMax * backend->external_pdr_size
+ + symhdr->isymMax * backend->external_sym_size
+ + symhdr->ioptMax * backend->external_opt_size
+ + symhdr->iauxMax * sizeof (union aux_ext)
+ + symhdr->issMax * sizeof (char)
+ + symhdr->issExtMax * sizeof (char)
+ + symhdr->ifdMax * backend->external_fdr_size
+ + symhdr->crfd * backend->external_rfd_size
+ + symhdr->iextMax * backend->external_ext_size);
+ raw = (char *) bfd_alloc (abfd, size);
+ if (raw == (char *) NULL)
+ {
+ bfd_error = no_memory;
+ return false;
+ }
+ ecoff_data (abfd)->raw_size = size;
+ ecoff_data (abfd)->raw_syments = (PTR) raw;
+
+ /* Initialize the raw pointers. */
+#define SET(field, count, type, size) \
+ ecoff_data (abfd)->field = (type) raw; \
+ raw += symhdr->count * size
+
+ SET (line, cbLine, unsigned char *, sizeof (unsigned char));
+ SET (external_dnr, idnMax, PTR, backend->external_dnr_size);
+ SET (external_pdr, ipdMax, PTR, backend->external_pdr_size);
+ SET (external_sym, isymMax, PTR, backend->external_sym_size);
+ SET (external_opt, ioptMax, PTR, backend->external_opt_size);
+ SET (external_aux, iauxMax, union aux_ext *, sizeof (union aux_ext));
+ SET (ss, issMax, char *, sizeof (char));
+ SET (ssext, issExtMax, char *, sizeof (char));
+ SET (external_fdr, ifdMax, PTR, backend->external_fdr_size);
+ SET (external_rfd, crfd, PTR, backend->external_rfd_size);
+ SET (external_ext, iextMax, PTR, backend->external_ext_size);
+#undef SET
+
+ /* Reset the counts so the second pass can use them to know how far
+ it has gotten. */
+ symhdr->ilineMax = 0;
+ symhdr->cbLine = 0;
+ symhdr->idnMax = 0;
+ symhdr->ipdMax = 0;
+ symhdr->isymMax = 0;
+ symhdr->ioptMax = 0;
+ symhdr->iauxMax = 0;
+ symhdr->issMax = 0;
+ symhdr->issExtMax = 0;
+ symhdr->ifdMax = 0;
+ symhdr->crfd = 0;
+ symhdr->iextMax = 0;
+
+ /* Do the second pass: accumulate the debugging information. */
+ ecoff_clear_output_flags (abfd);
+ for (ipass = 0; ipass < 2; ipass++)
+ {
+ for (o = abfd->sections; o != (asection *) NULL; o = o->next)
+ {
+ if (strcmp (o->name, SCOMMON) == 0
+ || strcmp (o->name, REGINFO) == 0)
+ continue;
+ if (((o->flags & SEC_CODE) == 0) != ipass)
+ continue;
+ for (p = o->seclets_head;
+ p != (bfd_seclet_type *) NULL;
+ p = p->next)
+ {
+ if (p->type == bfd_indirect_seclet)
+ {
+ if (ecoff_get_debug (abfd, p, o, relocateable) == false)
+ return false;
+ }
+ }
+ }
+ }
+
+ /* Put in the external symbols. */
+ sym_ptr_ptr = bfd_get_outsymbols (abfd);
+ if (sym_ptr_ptr != NULL)
+ {
+ const bfd_size_type external_ext_size = backend->external_ext_size;
+ void (* const swap_ext_in) PARAMS ((bfd *, PTR, EXTR *))
+ = backend->swap_ext_in;
+ void (* const swap_ext_out) PARAMS ((bfd *, const EXTR *, PTR))
+ = backend->swap_ext_out;
+ char *ssext;
+ char *external_ext;
+
+ ssext = ecoff_data (abfd)->ssext;
+ external_ext = (char *) ecoff_data (abfd)->external_ext;
+ for (; *sym_ptr_ptr != NULL; sym_ptr_ptr++)
+ {
+ asymbol *sym_ptr;
+ EXTR esym;
+
+ sym_ptr = *sym_ptr_ptr;
+
+ if ((sym_ptr->flags & BSF_DEBUGGING) != 0
+ || (sym_ptr->flags & BSF_LOCAL) != 0)
+ continue;
+
+ /* The native pointer can be NULL for a symbol created by
+ the linker via ecoff_make_empty_symbol. */
+ if (bfd_asymbol_flavour (sym_ptr) != bfd_target_ecoff_flavour
+ || ecoffsymbol (sym_ptr)->native == NULL)
+ {
+ esym.jmptbl = 0;
+ esym.cobol_main = 0;
+ esym.weakext = 0;
+ esym.reserved = 0;
+ esym.ifd = ifdNil;
+ /* FIXME: we can do better than this for st and sc. */
+ esym.asym.st = stGlobal;
+ esym.asym.sc = scAbs;
+ esym.asym.reserved = 0;
+ esym.asym.index = indexNil;
+ }
+ else
+ {
+ ecoff_symbol_type *ecoff_sym_ptr;
+
+ ecoff_sym_ptr = ecoffsymbol (sym_ptr);
+ if (ecoff_sym_ptr->local)
+ abort ();
+ (*swap_ext_in) (abfd, ecoff_sym_ptr->native, &esym);
+
+ /* If we're producing an executable, move common symbols
+ into bss. */
+ if (relocateable == false)
+ {
+ if (esym.asym.sc == scCommon)
+ esym.asym.sc = scBss;
+ else if (esym.asym.sc == scSCommon)
+ esym.asym.sc = scSBss;
+ }
+
+ /* Adjust the FDR index for the symbol by that used for
+ the input BFD. */
+ esym.ifd += ecoff_data (bfd_asymbol_bfd (sym_ptr))->ifdbase;
+ }
+
+ esym.asym.iss = symhdr->issExtMax;
+
+ if (bfd_is_com_section (sym_ptr->section)
+ || sym_ptr->section == &bfd_und_section)
+ esym.asym.value = sym_ptr->value;
+ else
+ esym.asym.value = (sym_ptr->value
+ + sym_ptr->section->output_offset
+ + sym_ptr->section->output_section->vma);
+
+ (*swap_ext_out) (abfd, &esym, external_ext);
+
+ ecoff_set_sym_index (sym_ptr, symhdr->iextMax);
+
+ external_ext += external_ext_size;
+ ++symhdr->iextMax;
+
+ strcpy (ssext + symhdr->issExtMax, sym_ptr->name);
+ symhdr->issExtMax += strlen (sym_ptr->name) + 1;
+ }
+ }
+
+ /* Adjust the counts so that structures are longword aligned. */
+ symhdr->cbLine = (symhdr->cbLine + debug_align) &~ debug_align;
+ symhdr->issMax = (symhdr->issMax + debug_align) &~ debug_align;
+ symhdr->issExtMax = (symhdr->issExtMax + debug_align) &~ debug_align;
+
+ return true;
+}
+
+/* Set the architecture. The supported architecture is stored in the
+ backend pointer. We always set the architecture anyhow, since many
+ callers ignore the return value. */
+
+boolean
+ecoff_set_arch_mach (abfd, arch, machine)
+ bfd *abfd;
+ enum bfd_architecture arch;
+ unsigned long machine;
+{
+ bfd_default_set_arch_mach (abfd, arch, machine);
+ return arch == ecoff_backend (abfd)->arch;
+}
+
+/* Get the size of the section headers. We do not output the .scommon
+ section which we created in ecoff_mkobject, nor do we output any
+ .reginfo section. */
+
+int
+ecoff_sizeof_headers (abfd, reloc)
+ bfd *abfd;
+ boolean reloc;
+{
+ asection *current;
+ int c;
+
+ c = 0;
+ for (current = abfd->sections;
+ current != (asection *)NULL;
+ current = current->next)
+ if (strcmp (current->name, SCOMMON) != 0
+ && strcmp (current->name, REGINFO) != 0)
+ ++c;
+
+ return (bfd_coff_filhsz (abfd)
+ + bfd_coff_aoutsz (abfd)
+ + c * bfd_coff_scnhsz (abfd));
+}
+
+
+/* Get the contents of a section. This is where we handle reading the
+ .reginfo section, which implicitly holds the contents of an
+ ecoff_reginfo structure. */
+
+boolean
+ecoff_get_section_contents (abfd, section, location, offset, count)
+ bfd *abfd;
+ asection *section;
+ PTR location;
+ file_ptr offset;
+ bfd_size_type count;
+{
+ ecoff_data_type *tdata = ecoff_data (abfd);
+ struct ecoff_reginfo s;
+ int i;
+
+ if (strcmp (section->name, REGINFO) != 0)
+ return bfd_generic_get_section_contents (abfd, section, location,
+ offset, count);
+
+ s.gp_value = tdata->gp;
+ s.gprmask = tdata->gprmask;
+ for (i = 0; i < 4; i++)
+ s.cprmask[i] = tdata->cprmask[i];
+ s.fprmask = tdata->fprmask;
+
+ /* bfd_get_section_contents has already checked that the offset and
+ size is reasonable. We don't have to worry about swapping or any
+ such thing; the .reginfo section is defined such that the
+ contents are an ecoff_reginfo structure as seen on the host. */
+ memcpy (location, ((char *) &s) + offset, count);
+ return true;
+}
+
+/* Calculate the file position for each section, and set
+ reloc_filepos. */
+
+static void
+ecoff_compute_section_file_positions (abfd)
+ bfd *abfd;
+{
+ asection *current;
+ file_ptr sofar;
+ file_ptr old_sofar;
+ boolean first_data;
+
+ if (bfd_get_start_address (abfd))
+ abfd->flags |= EXEC_P;
+
+ sofar = ecoff_sizeof_headers (abfd, false);
+
+ first_data = true;
+ for (current = abfd->sections;
+ current != (asection *) NULL;
+ current = current->next)
+ {
+ /* Only deal with sections which have contents */
+ if ((current->flags & (SEC_HAS_CONTENTS | SEC_LOAD)) == 0
+ || strcmp (current->name, SCOMMON) == 0
+ || strcmp (current->name, REGINFO) == 0)
+ continue;
+
+ /* On Ultrix, the data sections in an executable file must be
+ aligned to a page boundary within the file. This does not
+ affect the section size, though. FIXME: Does this work for
+ other platforms? */
+ if ((abfd->flags & EXEC_P) != 0
+ && (abfd->flags & D_PAGED) != 0
+ && first_data != false
+ && (current->flags & SEC_CODE) == 0)
+ {
+ const bfd_vma round = ecoff_backend (abfd)->round;
+
+ sofar = (sofar + round - 1) &~ (round - 1);
+ first_data = false;
+ }
+
+ /* Align the sections in the file to the same boundary on
+ which they are aligned in virtual memory. */
+ old_sofar = sofar;
+ sofar = BFD_ALIGN (sofar, 1 << current->alignment_power);
+
+ current->filepos = sofar;
+
+ sofar += current->_raw_size;
+
+ /* make sure that this section is of the right size too */
+ old_sofar = sofar;
+ sofar = BFD_ALIGN (sofar, 1 << current->alignment_power);
+ current->_raw_size += sofar - old_sofar;
+ }
+
+ ecoff_data (abfd)->reloc_filepos = sofar;
+}
+
+/* Set the contents of a section. This is where we handle setting the
+ contents of the .reginfo section, which implicitly holds a
+ ecoff_reginfo structure. */
+
+boolean
+ecoff_set_section_contents (abfd, section, location, offset, count)
+ bfd *abfd;
+ asection *section;
+ PTR location;
+ file_ptr offset;
+ bfd_size_type count;
+{
+ if (abfd->output_has_begun == false)
+ ecoff_compute_section_file_positions (abfd);
+
+ if (strcmp (section->name, REGINFO) == 0)
+ {
+ ecoff_data_type *tdata = ecoff_data (abfd);
+ struct ecoff_reginfo s;
+ int i;
+
+ /* If the caller is only changing part of the structure, we must
+ retrieve the current information before the memcpy. */
+ if (offset != 0 || count != sizeof (struct ecoff_reginfo))
+ {
+ s.gp_value = tdata->gp;
+ s.gprmask = tdata->gprmask;
+ for (i = 0; i < 4; i++)
+ s.cprmask[i] = tdata->cprmask[i];
+ s.fprmask = tdata->fprmask;
+ }
+
+ /* bfd_set_section_contents has already checked that the offset
+ and size is reasonable. We don't have to worry about
+ swapping or any such thing; the .reginfo section is defined
+ such that the contents are an ecoff_reginfo structure as seen
+ on the host. */
+ memcpy (((char *) &s) + offset, location, count);
+
+ tdata->gp = s.gp_value;
+ tdata->gprmask = s.gprmask;
+ for (i = 0; i < 4; i++)
+ tdata->cprmask[i] = s.cprmask[i];
+ tdata->fprmask = s.fprmask;
+
+ return true;
+
+ }
+
+ bfd_seek (abfd, (file_ptr) (section->filepos + offset), SEEK_SET);
+
+ if (count != 0)
+ return (bfd_write (location, 1, count, abfd) == count) ? true : false;
+
+ return true;
+}
+
+/* Write out an ECOFF file. */
+
+boolean
+ecoff_write_object_contents (abfd)
+ bfd *abfd;
+{
+ const struct ecoff_backend_data * const backend = ecoff_backend (abfd);
+ const bfd_vma round = backend->round;
+ const bfd_size_type filhsz = bfd_coff_filhsz (abfd);
+ const bfd_size_type aoutsz = bfd_coff_aoutsz (abfd);
+ const bfd_size_type scnhsz = bfd_coff_scnhsz (abfd);
+ const bfd_size_type external_hdr_size = backend->external_hdr_size;
+ const bfd_size_type external_reloc_size = backend->external_reloc_size;
+ void (* const swap_reloc_out) PARAMS ((bfd *,
+ const struct internal_reloc *,
+ PTR))
+ = backend->swap_reloc_out;
+ asection *current;
+ unsigned int count;
+ file_ptr scn_base;
+ file_ptr reloc_base;
+ file_ptr sym_base;
+ unsigned long reloc_size;
+ unsigned long text_size;
+ unsigned long text_start;
+ unsigned long data_size;
+ unsigned long data_start;
+ unsigned long bss_size;
+ PTR buff;
+ struct internal_filehdr internal_f;
+ struct internal_aouthdr internal_a;
+ int i;
+
+ bfd_error = system_call_error;
+
+ if(abfd->output_has_begun == false)
+ ecoff_compute_section_file_positions(abfd);
+
+ if (abfd->sections != (asection *) NULL)
+ scn_base = abfd->sections->filepos;
+ else
+ scn_base = 0;
+ reloc_base = ecoff_data (abfd)->reloc_filepos;
+
+ count = 1;
+ reloc_size = 0;
+ for (current = abfd->sections;
+ current != (asection *)NULL;
+ current = current->next)
+ {
+ if (strcmp (current->name, SCOMMON) == 0
+ || strcmp (current->name, REGINFO) == 0)
+ continue;
+ current->target_index = count;
+ ++count;
+ if (current->reloc_count != 0)
+ {
+ bfd_size_type relsize;
+
+ current->rel_filepos = reloc_base;
+ relsize = current->reloc_count * external_reloc_size;
+ reloc_size += relsize;
+ reloc_base += relsize;
+ }
+ else
+ current->rel_filepos = 0;
+ }
+
+ sym_base = reloc_base + reloc_size;
+
+ /* At least on Ultrix, the symbol table of an executable file must
+ be aligned to a page boundary. FIXME: Is this true on other
+ platforms? */
+ if ((abfd->flags & EXEC_P) != 0
+ && (abfd->flags & D_PAGED) != 0)
+ sym_base = (sym_base + round - 1) &~ (round - 1);
+
+ ecoff_data (abfd)->sym_filepos = sym_base;
+
+ if ((abfd->flags & D_PAGED) != 0)
+ text_size = ecoff_sizeof_headers (abfd, false);
+ else
+ text_size = 0;
+ text_start = 0;
+ data_size = 0;
+ data_start = 0;
+ bss_size = 0;
+
+ /* Write section headers to the file. */
+
+ buff = (PTR) alloca (scnhsz);
+ internal_f.f_nscns = 0;
+ if (bfd_seek (abfd, (file_ptr) (filhsz + aoutsz), SEEK_SET) != 0)
+ return false;
+ for (current = abfd->sections;
+ current != (asection *) NULL;
+ current = current->next)
+ {
+ struct internal_scnhdr section;
+ bfd_vma vma;
+
+ if (strcmp (current->name, SCOMMON) == 0)
+ {
+ BFD_ASSERT (bfd_get_section_size_before_reloc (current) == 0
+ && current->reloc_count == 0);
+ continue;
+ }
+ if (strcmp (current->name, REGINFO) == 0)
+ {
+ BFD_ASSERT (current->reloc_count == 0);
+ continue;
+ }
+
+ ++internal_f.f_nscns;
+
+ strncpy (section.s_name, current->name, sizeof section.s_name);
+
+ /* FIXME: is this correct for shared libraries? I think it is
+ but I have no platform to check. Ian Lance Taylor. */
+ vma = bfd_get_section_vma (abfd, current);
+ if (strcmp (current->name, _LIB) == 0)
+ section.s_vaddr = 0;
+ else
+ section.s_vaddr = vma;
+
+ section.s_paddr = vma;
+ section.s_size = bfd_get_section_size_before_reloc (current);
+
+ /* If this section is unloadable then the scnptr will be 0. */
+ if ((current->flags & (SEC_LOAD | SEC_HAS_CONTENTS)) == 0)
+ section.s_scnptr = 0;
+ else
+ section.s_scnptr = current->filepos;
+ section.s_relptr = current->rel_filepos;
+
+ /* FIXME: the lnnoptr of the .sbss or .sdata section of an
+ object file produced by the assembler is supposed to point to
+ information about how much room is required by objects of
+ various different sizes. I think this only matters if we
+ want the linker to compute the best size to use, or
+ something. I don't know what happens if the information is
+ not present. */
+ section.s_lnnoptr = 0;
+
+ section.s_nreloc = current->reloc_count;
+ section.s_nlnno = 0;
+ section.s_flags = ecoff_sec_to_styp_flags (current->name,
+ current->flags);
+
+ bfd_coff_swap_scnhdr_out (abfd, (PTR) &section, buff);
+ if (bfd_write (buff, 1, scnhsz, abfd) != scnhsz)
+ return false;
+
+ if ((section.s_flags & STYP_TEXT) != 0)
+ {
+ text_size += bfd_get_section_size_before_reloc (current);
+ if (text_start == 0 || text_start > vma)
+ text_start = vma;
+ }
+ else if ((section.s_flags & STYP_RDATA) != 0
+ || (section.s_flags & STYP_DATA) != 0
+ || (section.s_flags & STYP_LIT8) != 0
+ || (section.s_flags & STYP_LIT4) != 0
+ || (section.s_flags & STYP_SDATA) != 0)
+ {
+ data_size += bfd_get_section_size_before_reloc (current);
+ if (data_start == 0 || data_start > vma)
+ data_start = vma;
+ }
+ else if ((section.s_flags & STYP_BSS) != 0
+ || (section.s_flags & STYP_SBSS) != 0)
+ bss_size += bfd_get_section_size_before_reloc (current);
+ }
+
+ /* Set up the file header. */
+
+ internal_f.f_magic = ecoff_get_magic (abfd);
+
+ /* We will NOT put a fucking timestamp in the header here. Every
+ time you put it back, I will come in and take it out again. I'm
+ sorry. This field does not belong here. We fill it with a 0 so
+ it compares the same but is not a reasonable time. --
+ gnu@cygnus.com. */
+ internal_f.f_timdat = 0;
+
+ if (bfd_get_symcount (abfd) != 0)
+ {
+ /* The ECOFF f_nsyms field is not actually the number of
+ symbols, it's the size of symbolic information header. */
+ internal_f.f_nsyms = external_hdr_size;
+ internal_f.f_symptr = sym_base;
+ }
+ else
+ {
+ internal_f.f_nsyms = 0;
+ internal_f.f_symptr = 0;
+ }
+
+ internal_f.f_opthdr = aoutsz;
+
+ internal_f.f_flags = F_LNNO;
+ if (reloc_size == 0)
+ internal_f.f_flags |= F_RELFLG;
+ if (bfd_get_symcount (abfd) == 0)
+ internal_f.f_flags |= F_LSYMS;
+ if (abfd->flags & EXEC_P)
+ internal_f.f_flags |= F_EXEC;
+
+ if (! abfd->xvec->byteorder_big_p)
+ internal_f.f_flags |= F_AR32WR;
+ else
+ internal_f.f_flags |= F_AR32W;
+
+ /* Set up the ``optional'' header. */
+ if ((abfd->flags & D_PAGED) != 0)
+ internal_a.magic = ECOFF_AOUT_ZMAGIC;
+ else
+ internal_a.magic = ECOFF_AOUT_OMAGIC;
+
+ /* FIXME: This is what Ultrix puts in, and it makes the Ultrix
+ linker happy. But, is it right? */
+ internal_a.vstamp = 0x20a;
+
+ /* At least on Ultrix, these have to be rounded to page boundaries.
+ FIXME: Is this true on other platforms? */
+ if ((abfd->flags & D_PAGED) != 0)
+ {
+ internal_a.tsize = (text_size + round - 1) &~ (round - 1);
+ internal_a.text_start = text_start &~ (round - 1);
+ internal_a.dsize = (data_size + round - 1) &~ (round - 1);
+ internal_a.data_start = data_start &~ (round - 1);
+ }
+ else
+ {
+ internal_a.tsize = text_size;
+ internal_a.text_start = text_start;
+ internal_a.dsize = data_size;
+ internal_a.data_start = data_start;
+ }
+
+ /* On Ultrix, the initial portions of the .sbss and .bss segments
+ are at the end of the data section. The bsize field in the
+ optional header records how many bss bytes are required beyond
+ those in the data section. The value is not rounded to a page
+ boundary. */
+ if (bss_size < internal_a.dsize - data_size)
+ bss_size = 0;
+ else
+ bss_size -= internal_a.dsize - data_size;
+ internal_a.bsize = bss_size;
+ internal_a.bss_start = internal_a.data_start + internal_a.dsize;
+
+ internal_a.entry = bfd_get_start_address (abfd);
+
+ internal_a.gp_value = ecoff_data (abfd)->gp;
+
+ internal_a.gprmask = ecoff_data (abfd)->gprmask;
+ internal_a.fprmask = ecoff_data (abfd)->fprmask;
+ for (i = 0; i < 4; i++)
+ internal_a.cprmask[i] = ecoff_data (abfd)->cprmask[i];
+
+ /* Write out the file header and the optional header. */
+
+ if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0)
+ return false;
+
+ buff = (PTR) alloca (filhsz);
+ bfd_coff_swap_filehdr_out (abfd, (PTR) &internal_f, buff);
+ if (bfd_write (buff, 1, filhsz, abfd) != filhsz)
+ return false;
+
+ buff = (PTR) alloca (aoutsz);
+ bfd_coff_swap_aouthdr_out (abfd, (PTR) &internal_a, buff);
+ if (bfd_write (buff, 1, aoutsz, abfd) != aoutsz)
+ return false;
+
+ /* Write out the relocs. */
+ for (current = abfd->sections;
+ current != (asection *) NULL;
+ current = current->next)
+ {
+ arelent **reloc_ptr_ptr;
+ arelent **reloc_end;
+ char *out_ptr;
+
+ if (current->reloc_count == 0)
+ continue;
+
+ buff = bfd_alloc (abfd, current->reloc_count * external_reloc_size);
+ if (buff == NULL)
+ {
+ bfd_error = no_memory;
+ return false;
+ }
+
+ reloc_ptr_ptr = current->orelocation;
+ reloc_end = reloc_ptr_ptr + current->reloc_count;
+ out_ptr = (char *) buff;
+ for (;
+ reloc_ptr_ptr < reloc_end;
+ reloc_ptr_ptr++, out_ptr += external_reloc_size)
+ {
+ arelent *reloc;
+ asymbol *sym;
+ struct internal_reloc in;
+
+ memset (&in, 0, sizeof in);
+
+ reloc = *reloc_ptr_ptr;
+ sym = *reloc->sym_ptr_ptr;
+
+ in.r_vaddr = reloc->address + bfd_get_section_vma (abfd, current);
+ in.r_type = reloc->howto->type;
+
+ if ((sym->flags & BSF_SECTION_SYM) == 0)
+ {
+ in.r_symndx = ecoff_get_sym_index (*reloc->sym_ptr_ptr);
+ in.r_extern = 1;
+ }
+ else
+ {
+ CONST char *name;
+
+ name = bfd_get_section_name (abfd, bfd_get_section (sym));
+ if (strcmp (name, ".text") == 0)
+ in.r_symndx = RELOC_SECTION_TEXT;
+ else if (strcmp (name, ".rdata") == 0)
+ in.r_symndx = RELOC_SECTION_RDATA;
+ else if (strcmp (name, ".data") == 0)
+ in.r_symndx = RELOC_SECTION_DATA;
+ else if (strcmp (name, ".sdata") == 0)
+ in.r_symndx = RELOC_SECTION_SDATA;
+ else if (strcmp (name, ".sbss") == 0)
+ in.r_symndx = RELOC_SECTION_SBSS;
+ else if (strcmp (name, ".bss") == 0)
+ in.r_symndx = RELOC_SECTION_BSS;
+ else if (strcmp (name, ".init") == 0)
+ in.r_symndx = RELOC_SECTION_INIT;
+ else if (strcmp (name, ".lit8") == 0)
+ in.r_symndx = RELOC_SECTION_LIT8;
+ else if (strcmp (name, ".lit4") == 0)
+ in.r_symndx = RELOC_SECTION_LIT4;
+ else
+ abort ();
+ in.r_extern = 0;
+ }
+
+ (*swap_reloc_out) (abfd, &in, (PTR) out_ptr);
+ }
+
+ if (bfd_seek (abfd, current->rel_filepos, SEEK_SET) != 0)
+ return false;
+ if (bfd_write (buff, external_reloc_size, current->reloc_count, abfd)
+ != external_reloc_size * current->reloc_count)
+ return false;
+ bfd_release (abfd, buff);
+ }
+
+ /* Write out the symbolic debugging information. */
+ if (bfd_get_symcount (abfd) > 0)
+ {
+ HDRR *symhdr;
+ unsigned long sym_offset;
+
+ /* Set up the offsets in the symbolic header. */
+ symhdr = &ecoff_data (abfd)->symbolic_header;
+ sym_offset = ecoff_data (abfd)->sym_filepos + external_hdr_size;
+
+#define SET(offset, size, ptr) \
+ if (symhdr->size == 0) \
+ symhdr->offset = 0; \
+ else \
+ symhdr->offset = (((char *) ecoff_data (abfd)->ptr \
+ - (char *) ecoff_data (abfd)->raw_syments) \
+ + sym_offset);
+
+ SET (cbLineOffset, cbLine, line);
+ SET (cbDnOffset, idnMax, external_dnr);
+ SET (cbPdOffset, ipdMax, external_pdr);
+ SET (cbSymOffset, isymMax, external_sym);
+ SET (cbOptOffset, ioptMax, external_opt);
+ SET (cbAuxOffset, iauxMax, external_aux);
+ SET (cbSsOffset, issMax, ss);
+ SET (cbSsExtOffset, issExtMax, ssext);
+ SET (cbFdOffset, ifdMax, external_fdr);
+ SET (cbRfdOffset, crfd, external_rfd);
+ SET (cbExtOffset, iextMax, external_ext);
+#undef SET
+
+ if (bfd_seek (abfd, (file_ptr) ecoff_data (abfd)->sym_filepos,
+ SEEK_SET) != 0)
+ return false;
+ buff = (PTR) alloca (external_hdr_size);
+ (*backend->swap_hdr_out) (abfd, &ecoff_data (abfd)->symbolic_header,
+ buff);
+ if (bfd_write (buff, 1, external_hdr_size, abfd) != external_hdr_size)
+ return false;
+ if (bfd_write ((PTR) ecoff_data (abfd)->raw_syments, 1,
+ ecoff_data (abfd)->raw_size, abfd)
+ != ecoff_data (abfd)->raw_size)
+ return false;
+ }
+ else if ((abfd->flags & EXEC_P) != 0
+ && (abfd->flags & D_PAGED) != 0)
+ {
+ char c;
+
+ /* A demand paged executable must occupy an even number of
+ pages. */
+ if (bfd_seek (abfd, (file_ptr) ecoff_data (abfd)->sym_filepos - 1,
+ SEEK_SET) != 0)
+ return false;
+ if (bfd_read (&c, 1, 1, abfd) == 0)
+ c = 0;
+ if (bfd_seek (abfd, (file_ptr) ecoff_data (abfd)->sym_filepos - 1,
+ SEEK_SET) != 0)
+ return false;
+ if (bfd_write (&c, 1, 1, abfd) != 1)
+ return false;
+ }
+
+ return true;
+}
+
+/* Archive handling. ECOFF uses what appears to be a unique type of
+ archive header (which I call an armap). The byte ordering of the
+ armap and the contents are encoded in the name of the armap itself.
+ At least for now, we only support archives with the same byte
+ ordering in the armap and the contents.
+
+ The first four bytes in the armap are the number of symbol
+ definitions. This is always a power of two.
+
+ This is followed by the symbol definitions. Each symbol definition
+ occupies 8 bytes. The first four bytes are the offset from the
+ start of the armap strings to the null-terminated string naming
+ this symbol. The second four bytes are the file offset to the
+ archive member which defines this symbol. If the second four bytes
+ are 0, then this is not actually a symbol definition, and it should
+ be ignored.
+
+ The symbols are hashed into the armap with a closed hashing scheme.
+ See the functions below for the details of the algorithm.
+
+ We could use the hash table when looking up symbols in a library.
+ This would require a new BFD target entry point to replace the
+ bfd_get_next_mapent function used by the linker.
+
+ After the symbol definitions comes four bytes holding the size of
+ the string table, followed by the string table itself. */
+
+/* The name of an archive headers looks like this:
+ __________E[BL]E[BL]_ (with a trailing space).
+ The trailing space is changed to an X if the archive is changed to
+ indicate that the armap is out of date.
+
+ The Alpha seems to use ________64E[BL]E[BL]_. */
+
+#define ARMAP_BIG_ENDIAN 'B'
+#define ARMAP_LITTLE_ENDIAN 'L'
+#define ARMAP_MARKER 'E'
+#define ARMAP_START_LENGTH 10
+#define ARMAP_HEADER_MARKER_INDEX 10
+#define ARMAP_HEADER_ENDIAN_INDEX 11
+#define ARMAP_OBJECT_MARKER_INDEX 12
+#define ARMAP_OBJECT_ENDIAN_INDEX 13
+#define ARMAP_END_INDEX 14
+#define ARMAP_END "_ "
+
+/* This is a magic number used in the hashing algorithm. */
+#define ARMAP_HASH_MAGIC 0x9dd68ab5
+
+/* This returns the hash value to use for a string. It also sets
+ *REHASH to the rehash adjustment if the first slot is taken. SIZE
+ is the number of entries in the hash table, and HLOG is the log
+ base 2 of SIZE. */
+
+static unsigned int
+ecoff_armap_hash (s, rehash, size, hlog)
+ CONST char *s;
+ unsigned int *rehash;
+ unsigned int size;
+ unsigned int hlog;
+{
+ unsigned int hash;
+
+ hash = *s++;
+ while (*s != '\0')
+ hash = ((hash >> 27) | (hash << 5)) + *s++;
+ hash *= ARMAP_HASH_MAGIC;
+ *rehash = (hash & (size - 1)) | 1;
+ return hash >> (32 - hlog);
+}
+
+/* Read in the armap. */
+
+boolean
+ecoff_slurp_armap (abfd)
+ bfd *abfd;
+{
+ char nextname[17];
+ unsigned int i;
+ struct areltdata *mapdata;
+ bfd_size_type parsed_size;
+ char *raw_armap;
+ struct artdata *ardata;
+ unsigned int count;
+ char *raw_ptr;
+ struct symdef *symdef_ptr;
+ char *stringbase;
+
+ /* Get the name of the first element. */
+ i = bfd_read ((PTR) nextname, 1, 16, abfd);
+ if (i == 0)
+ return true;
+ if (i != 16)
+ return false;
+
+ bfd_seek (abfd, (file_ptr) -16, SEEK_CUR);
+
+ /* Irix 4.0.5F apparently can use either an ECOFF armap or a
+ standard COFF armap. We could move the ECOFF armap stuff into
+ bfd_slurp_armap, but that seems inappropriate since no other
+ target uses this format. Instead, we check directly for a COFF
+ armap. */
+ if (strncmp (nextname, "/ ", 16) == 0)
+ return bfd_slurp_armap (abfd);
+
+ /* See if the first element is an armap. */
+ if (strncmp (nextname, ecoff_backend (abfd)->armap_start,
+ ARMAP_START_LENGTH) != 0
+ || nextname[ARMAP_HEADER_MARKER_INDEX] != ARMAP_MARKER
+ || (nextname[ARMAP_HEADER_ENDIAN_INDEX] != ARMAP_BIG_ENDIAN
+ && nextname[ARMAP_HEADER_ENDIAN_INDEX] != ARMAP_LITTLE_ENDIAN)
+ || nextname[ARMAP_OBJECT_MARKER_INDEX] != ARMAP_MARKER
+ || (nextname[ARMAP_OBJECT_ENDIAN_INDEX] != ARMAP_BIG_ENDIAN
+ && nextname[ARMAP_OBJECT_ENDIAN_INDEX] != ARMAP_LITTLE_ENDIAN)
+ || strncmp (nextname + ARMAP_END_INDEX,
+ ARMAP_END, sizeof ARMAP_END - 1) != 0)
+ {
+ bfd_has_map (abfd) = false;
+ return true;
+ }
+
+ /* Make sure we have the right byte ordering. */
+ if (((nextname[ARMAP_HEADER_ENDIAN_INDEX] == ARMAP_BIG_ENDIAN)
+ ^ (abfd->xvec->header_byteorder_big_p != false))
+ || ((nextname[ARMAP_OBJECT_ENDIAN_INDEX] == ARMAP_BIG_ENDIAN)
+ ^ (abfd->xvec->byteorder_big_p != false)))
+ {
+ bfd_error = wrong_format;
+ return false;
+ }
+
+ /* Read in the armap. */
+ ardata = bfd_ardata (abfd);
+ mapdata = snarf_ar_hdr (abfd);
+ if (mapdata == (struct areltdata *) NULL)
+ return false;
+ parsed_size = mapdata->parsed_size;
+ bfd_release (abfd, (PTR) mapdata);
+
+ raw_armap = (char *) bfd_alloc (abfd, parsed_size);
+ if (raw_armap == (char *) NULL)
+ {
+ bfd_error = no_memory;
+ return false;
+ }
+
+ if (bfd_read ((PTR) raw_armap, 1, parsed_size, abfd) != parsed_size)
+ {
+ bfd_error = malformed_archive;
+ bfd_release (abfd, (PTR) raw_armap);
+ return false;
+ }
+
+ count = bfd_h_get_32 (abfd, (PTR) raw_armap);
+
+ ardata->symdef_count = 0;
+ ardata->cache = (struct ar_cache *) NULL;
+
+ /* This code used to overlay the symdefs over the raw archive data,
+ but that doesn't work on a 64 bit host. */
+
+ stringbase = raw_armap + count * 8 + 8;
+
+#ifdef CHECK_ARMAP_HASH
+ {
+ unsigned int hlog;
+
+ /* Double check that I have the hashing algorithm right by making
+ sure that every symbol can be looked up successfully. */
+ hlog = 0;
+ for (i = 1; i < count; i <<= 1)
+ hlog++;
+ BFD_ASSERT (i == count);
+
+ raw_ptr = raw_armap + 4;
+ for (i = 0; i < count; i++, raw_ptr += 8)
+ {
+ unsigned int name_offset, file_offset;
+ unsigned int hash, rehash, srch;
+
+ name_offset = bfd_h_get_32 (abfd, (PTR) raw_ptr);
+ file_offset = bfd_h_get_32 (abfd, (PTR) (raw_ptr + 4));
+ if (file_offset == 0)
+ continue;
+ hash = ecoff_armap_hash (stringbase + name_offset, &rehash, count,
+ hlog);
+ if (hash == i)
+ continue;
+
+ /* See if we can rehash to this location. */
+ for (srch = (hash + rehash) & (count - 1);
+ srch != hash && srch != i;
+ srch = (srch + rehash) & (count - 1))
+ BFD_ASSERT (bfd_h_get_32 (abfd, (PTR) (raw_armap + 8 + srch * 8))
+ != 0);
+ BFD_ASSERT (srch == i);
+ }
+ }
+
+#endif /* CHECK_ARMAP_HASH */
+
+ raw_ptr = raw_armap + 4;
+ for (i = 0; i < count; i++, raw_ptr += 8)
+ if (bfd_h_get_32 (abfd, (PTR) (raw_ptr + 4)) != 0)
+ ++ardata->symdef_count;
+
+ symdef_ptr = ((struct symdef *)
+ bfd_alloc (abfd,
+ ardata->symdef_count * sizeof (struct symdef)));
+ ardata->symdefs = (carsym *) symdef_ptr;
+
+ raw_ptr = raw_armap + 4;
+ for (i = 0; i < count; i++, raw_ptr += 8)
+ {
+ unsigned int name_offset, file_offset;
+
+ file_offset = bfd_h_get_32 (abfd, (PTR) (raw_ptr + 4));
+ if (file_offset == 0)
+ continue;
+ name_offset = bfd_h_get_32 (abfd, (PTR) raw_ptr);
+ symdef_ptr->s.name = stringbase + name_offset;
+ symdef_ptr->file_offset = file_offset;
+ ++symdef_ptr;
+ }
+
+ ardata->first_file_filepos = bfd_tell (abfd);
+ /* Pad to an even boundary. */
+ ardata->first_file_filepos += ardata->first_file_filepos % 2;
+
+ bfd_has_map (abfd) = true;
+
+ return true;
+}
+
+/* Write out an armap. */
+
+boolean
+ecoff_write_armap (abfd, elength, map, orl_count, stridx)
+ bfd *abfd;
+ unsigned int elength;
+ struct orl *map;
+ unsigned int orl_count;
+ int stridx;
+{
+ unsigned int hashsize, hashlog;
+ unsigned int symdefsize;
+ int padit;
+ unsigned int stringsize;
+ unsigned int mapsize;
+ file_ptr firstreal;
+ struct ar_hdr hdr;
+ struct stat statbuf;
+ unsigned int i;
+ bfd_byte temp[4];
+ bfd_byte *hashtable;
+ bfd *current;
+ bfd *last_elt;
+
+ /* Ultrix appears to use as a hash table size the least power of two
+ greater than twice the number of entries. */
+ for (hashlog = 0; (1 << hashlog) <= 2 * orl_count; hashlog++)
+ ;
+ hashsize = 1 << hashlog;
+
+ symdefsize = hashsize * 8;
+ padit = stridx % 2;
+ stringsize = stridx + padit;
+
+ /* Include 8 bytes to store symdefsize and stringsize in output. */
+ mapsize = symdefsize + stringsize + 8;
+
+ firstreal = SARMAG + sizeof (struct ar_hdr) + mapsize + elength;
+
+ memset ((PTR) &hdr, 0, sizeof hdr);
+
+ /* Work out the ECOFF armap name. */
+ strcpy (hdr.ar_name, ecoff_backend (abfd)->armap_start);
+ hdr.ar_name[ARMAP_HEADER_MARKER_INDEX] = ARMAP_MARKER;
+ hdr.ar_name[ARMAP_HEADER_ENDIAN_INDEX] =
+ (abfd->xvec->header_byteorder_big_p
+ ? ARMAP_BIG_ENDIAN
+ : ARMAP_LITTLE_ENDIAN);
+ hdr.ar_name[ARMAP_OBJECT_MARKER_INDEX] = ARMAP_MARKER;
+ hdr.ar_name[ARMAP_OBJECT_ENDIAN_INDEX] =
+ abfd->xvec->byteorder_big_p ? ARMAP_BIG_ENDIAN : ARMAP_LITTLE_ENDIAN;
+ memcpy (hdr.ar_name + ARMAP_END_INDEX, ARMAP_END, sizeof ARMAP_END - 1);
+
+ /* Write the timestamp of the archive header to be just a little bit
+ later than the timestamp of the file, otherwise the linker will
+ complain that the index is out of date. Actually, the Ultrix
+ linker just checks the archive name; the GNU linker may check the
+ date. */
+ stat (abfd->filename, &statbuf);
+ sprintf (hdr.ar_date, "%ld", (long) (statbuf.st_mtime + 60));
+
+ /* The DECstation uses zeroes for the uid, gid and mode of the
+ armap. */
+ hdr.ar_uid[0] = '0';
+ hdr.ar_gid[0] = '0';
+ hdr.ar_mode[0] = '0';
+
+ sprintf (hdr.ar_size, "%-10d", (int) mapsize);
+
+ hdr.ar_fmag[0] = '`';
+ hdr.ar_fmag[1] = '\n';
+
+ /* Turn all null bytes in the header into spaces. */
+ for (i = 0; i < sizeof (struct ar_hdr); i++)
+ if (((char *)(&hdr))[i] == '\0')
+ (((char *)(&hdr))[i]) = ' ';
+
+ if (bfd_write ((PTR) &hdr, 1, sizeof (struct ar_hdr), abfd)
+ != sizeof (struct ar_hdr))
+ return false;
+
+ bfd_h_put_32 (abfd, hashsize, temp);
+ if (bfd_write (temp, 1, 4, abfd) != 4)
+ return false;
+
+ hashtable = (bfd_byte *) bfd_zalloc (abfd, symdefsize);
+
+ current = abfd->archive_head;
+ last_elt = current;
+ for (i = 0; i < orl_count; i++)
+ {
+ unsigned int hash, rehash;
+
+ /* Advance firstreal to the file position of this archive
+ element. */
+ if (((bfd *) map[i].pos) != last_elt)
+ {
+ do
+ {
+ firstreal += arelt_size (current) + sizeof (struct ar_hdr);
+ firstreal += firstreal % 2;
+ current = current->next;
+ }
+ while (current != (bfd *) map[i].pos);
+ }
+
+ last_elt = current;
+
+ hash = ecoff_armap_hash (*map[i].name, &rehash, hashsize, hashlog);
+ if (bfd_h_get_32 (abfd, (PTR) (hashtable + (hash * 8) + 4)) != 0)
+ {
+ unsigned int srch;
+
+ /* The desired slot is already taken. */
+ for (srch = (hash + rehash) & (hashsize - 1);
+ srch != hash;
+ srch = (srch + rehash) & (hashsize - 1))
+ if (bfd_h_get_32 (abfd, (PTR) (hashtable + (srch * 8) + 4)) == 0)
+ break;
+
+ BFD_ASSERT (srch != hash);
+
+ hash = srch;
+ }
+
+ bfd_h_put_32 (abfd, map[i].namidx, (PTR) (hashtable + hash * 8));
+ bfd_h_put_32 (abfd, firstreal, (PTR) (hashtable + hash * 8 + 4));
+ }
+
+ if (bfd_write (hashtable, 1, symdefsize, abfd) != symdefsize)
+ return false;
+
+ bfd_release (abfd, hashtable);
+
+ /* Now write the strings. */
+ bfd_h_put_32 (abfd, stringsize, temp);
+ if (bfd_write (temp, 1, 4, abfd) != 4)
+ return false;
+ for (i = 0; i < orl_count; i++)
+ {
+ bfd_size_type len;
+
+ len = strlen (*map[i].name) + 1;
+ if (bfd_write ((PTR) (*map[i].name), 1, len, abfd) != len)
+ return false;
+ }
+
+ /* The spec sez this should be a newline. But in order to be
+ bug-compatible for DECstation ar we use a null. */
+ if (padit)
+ {
+ if (bfd_write ("\0", 1, 1, abfd) != 1)
+ return false;
+ }
+
+ return true;
+}
+
+/* See whether this BFD is an archive. If it is, read in the armap
+ and the extended name table. */
+
+bfd_target *
+ecoff_archive_p (abfd)
+ bfd *abfd;
+{
+ char armag[SARMAG + 1];
+
+ if (bfd_read ((PTR) armag, 1, SARMAG, abfd) != SARMAG
+ || strncmp (armag, ARMAG, SARMAG) != 0)
+ {
+ bfd_error = wrong_format;
+ return (bfd_target *) NULL;
+ }
+
+ /* We are setting bfd_ardata(abfd) here, but since bfd_ardata
+ involves a cast, we can't do it as the left operand of
+ assignment. */
+ abfd->tdata.aout_ar_data =
+ (struct artdata *) bfd_zalloc (abfd, sizeof (struct artdata));
+
+ if (bfd_ardata (abfd) == (struct artdata *) NULL)
+ {
+ bfd_error = no_memory;
+ return (bfd_target *) NULL;
+ }
+
+ bfd_ardata (abfd)->first_file_filepos = SARMAG;
+
+ if (ecoff_slurp_armap (abfd) == false
+ || ecoff_slurp_extended_name_table (abfd) == false)
+ {
+ bfd_release (abfd, bfd_ardata (abfd));
+ abfd->tdata.aout_ar_data = (struct artdata *) NULL;
+ return (bfd_target *) NULL;
+ }
+
+ return abfd->xvec;
+}
diff --git a/gnu/usr.bin/gdb/bfd/elf.c b/gnu/usr.bin/gdb/bfd/elf.c
new file mode 100644
index 0000000..2fcf2f1
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/elf.c
@@ -0,0 +1,248 @@
+/* ELF executable support for BFD.
+ Copyright 1993 Free Software Foundation, Inc.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+
+SECTION
+ ELF backends
+
+ BFD support for ELF formats is being worked on.
+ Currently, the best supported back ends are for sparc and i386
+ (running svr4 or Solaris 2).
+
+ Documentation of the internals of the support code still needs
+ to be written. The code is changing quickly enough that we
+ haven't bothered yet.
+ */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#define ARCH_SIZE 0
+#include "libelf.h"
+
+/* Standard ELF hash function. Do not change this function; you will
+ cause invalid hash tables to be generated. (Well, you would if this
+ were being used yet.) */
+unsigned long
+DEFUN (bfd_elf_hash, (name),
+ CONST unsigned char *name)
+{
+ unsigned long h = 0;
+ unsigned long g;
+ int ch;
+
+ while ((ch = *name++) != '\0')
+ {
+ h = (h << 4) + ch;
+ if ((g = (h & 0xf0000000)) != 0)
+ {
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ }
+ return h;
+}
+
+/* Read a specified number of bytes at a specified offset in an ELF
+ file, into a newly allocated buffer, and return a pointer to the
+ buffer. */
+
+static char *
+DEFUN (elf_read, (abfd, offset, size),
+ bfd * abfd AND
+ long offset AND
+ int size)
+{
+ char *buf;
+
+ if ((buf = bfd_alloc (abfd, size)) == NULL)
+ {
+ bfd_error = no_memory;
+ return NULL;
+ }
+ if (bfd_seek (abfd, offset, SEEK_SET) == -1)
+ {
+ bfd_error = system_call_error;
+ return NULL;
+ }
+ if (bfd_read ((PTR) buf, size, 1, abfd) != size)
+ {
+ bfd_error = system_call_error;
+ return NULL;
+ }
+ return buf;
+}
+
+boolean
+DEFUN (elf_mkobject, (abfd), bfd * abfd)
+{
+ /* this just does initialization */
+ /* coff_mkobject zalloc's space for tdata.coff_obj_data ... */
+ elf_tdata (abfd) = (struct elf_obj_tdata *)
+ bfd_zalloc (abfd, sizeof (struct elf_obj_tdata));
+ if (elf_tdata (abfd) == 0)
+ {
+ bfd_error = no_memory;
+ return false;
+ }
+ /* since everything is done at close time, do we need any
+ initialization? */
+
+ return true;
+}
+
+char *
+DEFUN (elf_get_str_section, (abfd, shindex),
+ bfd * abfd AND
+ unsigned int shindex)
+{
+ Elf_Internal_Shdr **i_shdrp;
+ char *shstrtab = NULL;
+ unsigned int offset;
+ unsigned int shstrtabsize;
+
+ i_shdrp = elf_elfsections (abfd);
+ if (i_shdrp == 0 || i_shdrp[shindex] == 0)
+ return 0;
+
+ shstrtab = i_shdrp[shindex]->rawdata;
+ if (shstrtab == NULL)
+ {
+ /* No cached one, attempt to read, and cache what we read. */
+ offset = i_shdrp[shindex]->sh_offset;
+ shstrtabsize = i_shdrp[shindex]->sh_size;
+ shstrtab = elf_read (abfd, offset, shstrtabsize);
+ i_shdrp[shindex]->rawdata = (void *) shstrtab;
+ }
+ return shstrtab;
+}
+
+char *
+DEFUN (elf_string_from_elf_section, (abfd, shindex, strindex),
+ bfd * abfd AND
+ unsigned int shindex AND
+ unsigned int strindex)
+{
+ Elf_Internal_Shdr *hdr;
+
+ if (strindex == 0)
+ return "";
+
+ hdr = elf_elfsections (abfd)[shindex];
+
+ if (!hdr->rawdata
+ && elf_get_str_section (abfd, shindex) == NULL)
+ return NULL;
+
+ return ((char *) hdr->rawdata) + strindex;
+}
+
+/*
+INTERNAL_FUNCTION
+ bfd_elf_find_section
+
+SYNOPSIS
+ struct elf_internal_shdr *bfd_elf_find_section (bfd *abfd, char *name);
+
+DESCRIPTION
+ Helper functions for GDB to locate the string tables.
+ Since BFD hides string tables from callers, GDB needs to use an
+ internal hook to find them. Sun's .stabstr, in particular,
+ isn't even pointed to by the .stab section, so ordinary
+ mechanisms wouldn't work to find it, even if we had some.
+*/
+
+struct elf_internal_shdr *
+DEFUN (bfd_elf_find_section, (abfd, name),
+ bfd * abfd AND
+ char *name)
+{
+ Elf_Internal_Shdr **i_shdrp;
+ char *shstrtab;
+ unsigned int max;
+ unsigned int i;
+
+ i_shdrp = elf_elfsections (abfd);
+ if (i_shdrp != NULL)
+ {
+ shstrtab = elf_get_str_section (abfd, elf_elfheader (abfd)->e_shstrndx);
+ if (shstrtab != NULL)
+ {
+ max = elf_elfheader (abfd)->e_shnum;
+ for (i = 1; i < max; i++)
+ if (!strcmp (&shstrtab[i_shdrp[i]->sh_name], name))
+ return i_shdrp[i];
+ }
+ }
+ return 0;
+}
+
+const struct bfd_elf_arch_map bfd_elf_arch_map[] = {
+ { bfd_arch_sparc, EM_SPARC },
+ { bfd_arch_i386, EM_386 },
+ { bfd_arch_m68k, EM_68K },
+ { bfd_arch_m88k, EM_88K },
+ { bfd_arch_i860, EM_860 },
+ { bfd_arch_mips, EM_MIPS },
+ { bfd_arch_hppa, EM_HPPA },
+};
+
+const int bfd_elf_arch_map_size = sizeof (bfd_elf_arch_map) / sizeof (bfd_elf_arch_map[0]);
+
+const char *const bfd_elf_section_type_names[] = {
+ "SHT_NULL", "SHT_PROGBITS", "SHT_SYMTAB", "SHT_STRTAB",
+ "SHT_RELA", "SHT_HASH", "SHT_DYNAMIC", "SHT_NOTE",
+ "SHT_NOBITS", "SHT_REL", "SHT_SHLIB", "SHT_DYNSYM",
+};
+
+/* ELF relocs are against symbols. If we are producing relocateable
+ output, and the reloc is against an external symbol, and nothing
+ has given us any additional addend, the resulting reloc will also
+ be against the same symbol. In such a case, we don't want to
+ change anything about the way the reloc is handled, since it will
+ all be done at final link time. Rather than put special case code
+ into bfd_perform_relocation, all the reloc types use this howto
+ function. It just short circuits the reloc if producing
+ relocateable output against an external symbol. */
+
+bfd_reloc_status_type
+bfd_elf_generic_reloc (abfd,
+ reloc_entry,
+ symbol,
+ data,
+ input_section,
+ output_bfd)
+ bfd *abfd;
+ arelent *reloc_entry;
+ asymbol *symbol;
+ PTR data;
+ asection *input_section;
+ bfd *output_bfd;
+{
+ if (output_bfd != (bfd *) NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && reloc_entry->addend == 0)
+ {
+ reloc_entry->address += input_section->output_offset;
+ return bfd_reloc_ok;
+ }
+
+ return bfd_reloc_continue;
+}
diff --git a/gnu/usr.bin/gdb/bfd/format.c b/gnu/usr.bin/gdb/bfd/format.c
new file mode 100644
index 0000000..6a80ed7
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/format.c
@@ -0,0 +1,258 @@
+/* Generic BFD support for file formats.
+ Copyright (C) 1990-1991 Free Software Foundation, Inc.
+ Written by Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+SECTION
+ File Formats
+
+ A format is a BFD concept of high level file contents. The
+ formats supported by BFD are:
+
+ o bfd_object
+
+ The BFD may contain data, symbols, relocations and debug info.
+
+ o bfd_archive
+
+ The BFD contains other BFDs and an optional index.
+
+ o bfd_core
+
+ The BFD contains the result of an executable core dump.
+
+
+*/
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+
+extern bfd_target *target_vector[];
+extern bfd_target *default_vector[];
+
+
+/*
+FUNCTION
+ bfd_check_format
+
+SYNOPSIS
+ boolean bfd_check_format(bfd *abfd, bfd_format format);
+
+DESCRIPTION
+ This routine is supplied a BFD and a format. It attempts to
+ verify if the file attached to the BFD is indeed compatible
+ with the format specified (ie, one of <<bfd_object>>,
+ <<bfd_archive>> or <<bfd_core>>).
+
+ If the BFD has been set to a specific @var{target} before the
+ call, only the named target and format combination will be
+ checked. If the target has not been set, or has been set to
+ <<default>> then all the known target backends will be
+ interrogated to determine a match. If the default target
+ matches, it is used. If not, exactly one target must recognize
+ the file, or an error results.
+
+ The function returns <<true>> on success, otherwise <<false>>
+ with one of the following error codes:
+
+ o invalid_operation -
+ if <<format>> is not one of <<bfd_object>>, <<bfd_archive>> or
+ <<bfd_core>>.
+
+ o system_call_error -
+ if an error occured during a read - even some file mismatches
+ can cause system_call_errors
+
+ o file_not_recognised -
+ none of the backends recognised the file format
+
+ o file_ambiguously_recognized -
+ more than one backend recognised the file format.
+
+*/
+
+boolean
+DEFUN(bfd_check_format,(abfd, format),
+ bfd *abfd AND
+ bfd_format format)
+{
+ bfd_target **target, *save_targ, *right_targ;
+ int match_count;
+
+ if (!bfd_read_p (abfd) ||
+ ((int)(abfd->format) < (int)bfd_unknown) ||
+ ((int)(abfd->format) >= (int)bfd_type_end)) {
+ bfd_error = invalid_operation;
+ return false;
+ }
+
+ if (abfd->format != bfd_unknown)
+ return (abfd->format == format)? true: false;
+
+
+ /* Since the target type was defaulted, check them
+ all in the hope that one will be uniquely recognized. */
+
+ save_targ = abfd->xvec;
+ match_count = 0;
+ right_targ = 0;
+
+
+ /* presume the answer is yes */
+ abfd->format = format;
+
+ /* If the target type was explicitly specified, just check that target. */
+
+ if (!abfd->target_defaulted) {
+ bfd_seek (abfd, (file_ptr)0, SEEK_SET); /* rewind! */
+
+ right_targ = BFD_SEND_FMT (abfd, _bfd_check_format, (abfd));
+ if (right_targ) {
+ abfd->xvec = right_targ; /* Set the target as returned */
+ return true; /* File position has moved, BTW */
+ }
+ }
+
+ for (target = target_vector; *target != NULL; target++) {
+ bfd_target *temp;
+
+ abfd->xvec = *target; /* Change BFD's target temporarily */
+ bfd_seek (abfd, (file_ptr)0, SEEK_SET);
+ /* If _bfd_check_format neglects to set bfd_error, assume wrong_format.
+ We didn't used to even pay any attention to bfd_error, so I suspect
+ that some _bfd_check_format might have this problem. */
+ bfd_error = wrong_format;
+ temp = BFD_SEND_FMT (abfd, _bfd_check_format, (abfd));
+ if (temp) { /* This format checks out as ok! */
+ right_targ = temp;
+ match_count++;
+ /* If this is the default target, accept it, even if other targets
+ might match. People who want those other targets have to set
+ the GNUTARGET variable. */
+ if (temp == default_vector[0])
+ {
+ match_count = 1;
+ break;
+ }
+#ifdef GNU960
+ /* Big- and little-endian b.out archives look the same, but it doesn't
+ * matter: there is no difference in their headers, and member file byte
+ * orders will (I hope) be handled appropriately by bfd. Ditto for big
+ * and little coff archives. And the 4 coff/b.out object formats are
+ * unambiguous. So accept the first match we find.
+ */
+ break;
+#endif
+ } else if (bfd_error != wrong_format) {
+ abfd->xvec = save_targ;
+ abfd->format = bfd_unknown;
+ return false;
+ }
+ }
+
+ if (match_count == 1) {
+ abfd->xvec = right_targ; /* Change BFD's target permanently */
+ return true; /* File position has moved, BTW */
+ }
+
+ abfd->xvec = save_targ; /* Restore original target type */
+ abfd->format = bfd_unknown; /* Restore original format */
+ bfd_error = ((match_count == 0) ? file_not_recognized :
+ file_ambiguously_recognized);
+ return false;
+}
+
+
+/*
+FUNCTION
+ bfd_set_format
+
+SYNOPSIS
+ boolean bfd_set_format(bfd *, bfd_format);
+
+DESCRIPTION
+ This function sets the file format of the supplied BFD to the
+ format requested. If the target set in the BFD does not
+ support the format requested, the format is illegal or the BFD
+ is not open for writing than an error occurs.
+
+*/
+
+boolean
+DEFUN(bfd_set_format,(abfd, format),
+ bfd *abfd AND
+ bfd_format format)
+{
+
+ if (bfd_read_p (abfd) ||
+ ((int)abfd->format < (int)bfd_unknown) ||
+ ((int)abfd->format >= (int)bfd_type_end)) {
+ bfd_error = invalid_operation;
+ return false;
+ }
+
+ if (abfd->format != bfd_unknown)
+ return (abfd->format == format) ? true:false;
+
+ /* presume the answer is yes */
+ abfd->format = format;
+
+ if (!BFD_SEND_FMT (abfd, _bfd_set_format, (abfd))) {
+ abfd->format = bfd_unknown;
+ return false;
+ }
+
+ return true;
+}
+
+
+/*
+FUNCTION
+ bfd_format_string
+
+SYNOPSIS
+ CONST char *bfd_format_string(bfd_format);
+
+DESCRIPTION
+ This function takes one argument, and enumerated type
+ (bfd_format) and returns a pointer to a const string
+ <<invalid>>, <<object>>, <<archive>>, <<core>> or <<unknown>>
+ depending upon the value of the enumeration.
+*/
+
+CONST char *
+DEFUN(bfd_format_string,(format),
+ bfd_format format)
+{
+ if (((int)format <(int) bfd_unknown)
+ || ((int)format >=(int) bfd_type_end))
+ return "invalid";
+
+ switch (format) {
+ case bfd_object:
+ return "object"; /* linker/assember/compiler output */
+ case bfd_archive:
+ return "archive"; /* object archive file */
+ case bfd_core:
+ return "core"; /* core dump */
+ default:
+ return "unknown";
+ }
+}
diff --git a/gnu/usr.bin/gdb/bfd/freebsd386.c b/gnu/usr.bin/gdb/bfd/freebsd386.c
new file mode 100644
index 0000000..22cbd6a
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/freebsd386.c
@@ -0,0 +1,110 @@
+/* BFD back-end for i386 a.out binaries under BSD.
+ Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This data should be correct for the format used under all the various
+ BSD ports for 386 machines. */
+
+#define BYTES_IN_WORD 4
+#define ARCH 32
+
+/* ZMAGIC files never have the header in the text. */
+#define N_HEADER_IN_TEXT(x) 0
+
+/* ZMAGIC files start at address 0. This does not apply to QMAGIC. */
+#define TEXT_START_ADDR 0
+
+#define SEGMENT_SIZE PAGE_SIZE
+
+#define DEFAULT_ARCH bfd_arch_i386
+#define MACHTYPE_OK(mtype) ((mtype) == M_386 || (mtype) == M_386_NETBSD || (mtype) == M_UNKNOWN)
+
+#define MY(OP) CAT(freebsd386_,OP)
+#define TARGETNAME "a.out-freebsd-386"
+
+#define N_MAGIC(ex) \
+ ((ex).a_info & 0xffff)
+#define N_MACHTYPE(ex) \
+ ( (N_GETMAGIC_NET(ex) == ZMAGIC) ? N_GETMID_NET(ex) : \
+ ((ex).a_info >> 16) & 0x03ff )
+#define N_FLAGS(ex) \
+ ( (N_GETMAGIC_NET(ex) == ZMAGIC) ? N_GETFLAG_NET(ex) : \
+ ((ex).a_info >> 26) & 0x3f )
+#define N_SET_INFO(ex,mag,mid,flag) \
+ ( (ex).a_info = (((flag) & 0x3f) <<26) | (((mid) & 0x03ff) << 16) | \
+ ((mag) & 0xffff) )
+
+#define N_GETMAGIC_NET(ex) \
+ (ntohl((ex).a_info) & 0xffff)
+#define N_GETMID_NET(ex) \
+ ((ntohl((ex).a_info) >> 16) & 0x03ff)
+#define N_GETFLAG_NET(ex) \
+ ((ntohl((ex).a_info) >> 26) & 0x3f)
+#define N_SETMAGIC_NET(ex,mag,mid,flag) \
+ ( (ex).a_info = htonl( (((flag)&0x3f)<<26) | (((mid)&0x03ff)<<16) | \
+ (((mag)&0xffff)) ) )
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "libaout.h"
+
+#define N_ALIGN(ex,x) \
+ (N_MAGIC(ex) == ZMAGIC || N_MAGIC(ex) == QMAGIC || \
+ N_GETMAGIC_NET(ex) == ZMAGIC || N_GETMAGIC_NET(ex) == QMAGIC ? \
+ ((x) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1) : (x))
+
+/* Valid magic number check. */
+#define N_BADMAG(ex) \
+ (N_MAGIC(ex) != OMAGIC && N_MAGIC(ex) != NMAGIC && \
+ N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC && \
+ N_GETMAGIC_NET(ex) != OMAGIC && N_GETMAGIC_NET(ex) != NMAGIC && \
+ N_GETMAGIC_NET(ex) != ZMAGIC && N_GETMAGIC_NET(ex) != QMAGIC)
+
+/* Address of the bottom of the text segment. */
+#define N_TXTADDR(ex) \
+ ((N_MAGIC(ex) == OMAGIC || N_MAGIC(ex) == NMAGIC || \
+ N_MAGIC(ex) == ZMAGIC) ? 0 : PAGE_SIZE)
+
+/* Address of the bottom of the data segment. */
+#define N_DATADDR(ex) \
+ N_ALIGN(ex, N_TXTADDR(ex) + (ex).a_text)
+
+/* Text segment offset. */
+#define N_TXTOFF(ex) \
+ (N_MAGIC(ex) == ZMAGIC ? PAGE_SIZE : (N_MAGIC(ex) == QMAGIC || \
+ N_GETMAGIC_NET(ex) == ZMAGIC) ? 0 : EXEC_BYTES_SIZE)
+
+/* Data segment offset. */
+#define N_DATOFF(ex) \
+ N_ALIGN(ex, N_TXTOFF(ex) + (ex).a_text)
+
+/* Relocation table offset. */
+#define N_RELOFF(ex) \
+ N_ALIGN(ex, N_DATOFF(ex) + (ex).a_data)
+
+/* Symbol table offset. */
+#define N_SYMOFF(ex) \
+ (N_RELOFF(ex) + (ex).a_trsize + (ex).a_drsize)
+
+/* String table offset. */
+#define N_STROFF(ex) (N_SYMOFF(ex) + (ex).a_syms)
+
+#define NO_SWAP_MAGIC /* magic number already in correct endian format */
+
+#include "aout-target.h"
diff --git a/gnu/usr.bin/gdb/bfd/init.c b/gnu/usr.bin/gdb/bfd/init.c
new file mode 100644
index 0000000..30a2759
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/init.c
@@ -0,0 +1,78 @@
+/* bfd initialization stuff
+ Copyright (C) 1990-1991 Free Software Foundation, Inc.
+ Written by Steve Chamberlain of Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+
+extern void DEFUN_VOID (bfd_section_init);
+
+static boolean initialized = false;
+
+/*
+SECTION
+ Initialization
+
+ This is the initialization section
+
+*/
+
+/*
+FUNCTION
+ bfd_init
+
+SYNOPSIS
+ void bfd_init(void);
+
+DESCRIPTION
+ This routine must be called before any other bfd function to
+ initialize magical internal data structures.
+*/
+
+void DEFUN_VOID(bfd_init)
+{
+ if (initialized == false) {
+ initialized = true;
+
+ bfd_arch_init();
+ }
+}
+
+
+/*
+INTERNAL_FUNCTION
+ bfd_check_init
+
+DESCRIPTION
+ This routine is called before any other bfd function using
+ initialized data is used to ensure that the structures have
+ been initialized. Soon this function will go away, and the bfd
+ library will assume that bfd_init has been called.
+
+SYNOPSIS
+ void bfd_check_init(void);
+*/
+
+void DEFUN_VOID(bfd_check_init)
+{
+ if (initialized == false) {
+ bfd_init();
+ }
+}
diff --git a/gnu/usr.bin/gdb/bfd/libaout.h b/gnu/usr.bin/gdb/bfd/libaout.h
new file mode 100644
index 0000000..0d99d0c
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/libaout.h
@@ -0,0 +1,393 @@
+/* BFD back-end data structures for a.out (and similar) files.
+ Copyright 1990, 1991, 1992 Free Software Foundation, Inc.
+ Written by Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* We try to encapsulate the differences in the various a.out file
+ variants in a few routines, and otherwise share large masses of code.
+ This means we only have to fix bugs in one place, most of the time. */
+
+/* Parameterize the a.out code based on whether it is being built
+ for a 32-bit architecture or a 64-bit architecture. */
+#if ARCH_SIZE==64
+#define GET_WORD bfd_h_get_64
+#define GET_SWORD bfd_h_get_signed_64
+#define PUT_WORD bfd_h_put_64
+#ifndef NAME
+#define NAME(x,y) CAT3(x,_64_,y)
+#endif
+#define JNAME(x) CAT(x,_64)
+#define BYTES_IN_WORD 8
+#else /* ARCH_SIZE == 32 */
+#define GET_WORD bfd_h_get_32
+#define GET_SWORD bfd_h_get_signed_32
+#define PUT_WORD bfd_h_put_32
+#ifndef NAME
+#define NAME(x,y) CAT3(x,_32_,y)
+#endif
+#define JNAME(x) CAT(x,_32)
+#define BYTES_IN_WORD 4
+#endif /* ARCH_SIZE==32 */
+
+/* Declare these types at file level, since they are used in parameter
+ lists, which have wierd scope. */
+struct external_exec;
+struct internal_exec;
+
+/* Back-end information for various a.out targets. */
+struct aout_backend_data
+{
+ /* Are ZMAGIC files mapped contiguously? If so, the text section may
+ need more padding, if the segment size (granularity for memory access
+ control) is larger than the page size. */
+ unsigned char zmagic_mapped_contiguous;
+ /* If this flag is set, ZMAGIC/NMAGIC file headers get mapped in with the
+ text section, which starts immediately after the file header.
+ If not, the text section starts on the next page. */
+ unsigned char text_includes_header;
+
+ /* If the text section VMA isn't specified, and we need an absolute
+ address, use this as the default. If we're producing a relocatable
+ file, zero is always used. */
+ /* ?? Perhaps a callback would be a better choice? Will this do anything
+ reasonable for a format that handles multiple CPUs with different
+ load addresses for each? */
+ bfd_vma default_text_vma;
+
+ /* Callback for setting the page and segment sizes, if they can't be
+ trivially determined from the architecture. */
+ boolean (*set_sizes) PARAMS ((bfd *));
+
+ /* zmagic files only. For go32, the length of the exec header contributes
+ to the size of the text section in the file for alignment purposes but
+ does *not* get counted in the length of the text section. */
+ unsigned char exec_header_not_counted;
+};
+#define aout_backend_info(abfd) \
+ ((CONST struct aout_backend_data *)((abfd)->xvec->backend_data))
+
+/* This is the layout in memory of a "struct exec" while we process it.
+ All 'lengths' are given as a number of bytes.
+ All 'alignments' are for relinkable files only; an alignment of
+ 'n' indicates the corresponding segment must begin at an
+ address that is a multiple of (2**n). */
+
+struct internal_exec
+{
+ long a_info; /* Magic number and flags, packed */
+ bfd_vma a_text; /* length of text, in bytes */
+ bfd_vma a_data; /* length of data, in bytes */
+ bfd_vma a_bss; /* length of uninitialized data area in mem */
+ bfd_vma a_syms; /* length of symbol table data in file */
+ bfd_vma a_entry; /* start address */
+ bfd_vma a_trsize; /* length of text's relocation info, in bytes */
+ bfd_vma a_drsize; /* length of data's relocation info, in bytes */
+ /* Added for i960 */
+ bfd_vma a_tload; /* Text runtime load address */
+ bfd_vma a_dload; /* Data runtime load address */
+ unsigned char a_talign; /* Alignment of text segment */
+ unsigned char a_dalign; /* Alignment of data segment */
+ unsigned char a_balign; /* Alignment of bss segment */
+ char a_relaxable; /* Enough info for linker relax */
+};
+
+/* Magic number is written
+< MSB >
+3130292827262524232221201918171615141312111009080706050403020100
+< FLAGS >< MACHINE TYPE >< MAGIC NUMBER >
+*/
+/* Magic number for NetBSD is
+<MSB >
+3130292827262524232221201918171615141312111009080706050403020100
+< FLAGS >< >< MAGIC NUMBER >
+*/
+
+enum machine_type {
+ M_UNKNOWN = 0,
+ M_68010 = 1,
+ M_68020 = 2,
+ M_SPARC = 3,
+ /* skip a bunch so we don't run into any of suns numbers */
+ M_386 = 100,
+ M_29K = 101, /* AMD 29000 */
+ M_386_DYNIX = 102, /* Sequent running dynix */
+ M_386_NETBSD = 134, /* NetBSD/386 binary */
+ M_MIPS1 = 151, /* MIPS R2000/R3000 binary */
+ M_MIPS2 = 152, /* MIPS R4000/R6000 binary */
+ M_HP200 = 200, /* HP 200 (68010) BSD binary */
+ M_HP300 = (300 % 256), /* HP 300 (68020+68881) BSD binary */
+ M_HPUX = (0x20c % 256)/* HP 200/300 HPUX binary */
+};
+
+#define N_DYNAMIC(exec) ((exec).a_info & 0x8000000)
+
+#ifndef N_MAGIC
+# define N_MAGIC(exec) ((exec).a_info & 0xffff)
+#endif
+
+#ifndef N_MACHTYPE
+# define N_MACHTYPE(exec) ((enum machine_type)(((exec).a_info >> 16) & 0xff))
+#endif
+
+#ifndef N_FLAGS
+# define N_FLAGS(exec) (((exec).a_info >> 24) & 0xff)
+#endif
+
+#ifndef N_SET_INFO
+# define N_SET_INFO(exec, magic, type, flags) \
+((exec).a_info = ((magic) & 0xffff) \
+ | (((int)(type) & 0xff) << 16) \
+ | (((flags) & 0xff) << 24))
+#endif
+
+#ifndef N_SET_MAGIC
+# define N_SET_MAGIC(exec, magic) \
+((exec).a_info = (((exec).a_info & 0xffff0000) | ((magic) & 0xffff)))
+#endif
+
+#ifndef N_SET_MACHTYPE
+# define N_SET_MACHTYPE(exec, machtype) \
+((exec).a_info = \
+ ((exec).a_info&0xff00ffff) | ((((int)(machtype))&0xff) << 16))
+#endif
+
+#ifndef N_SET_FLAGS
+# define N_SET_FLAGS(exec, flags) \
+((exec).a_info = \
+ ((exec).a_info&0x00ffffff) | (((flags) & 0xff) << 24))
+#endif
+
+typedef struct aout_symbol {
+ asymbol symbol;
+ short desc;
+ char other;
+ unsigned char type;
+} aout_symbol_type;
+
+/* The `tdata' struct for all a.out-like object file formats.
+ Various things depend on this struct being around any time an a.out
+ file is being handled. An example is dbxread.c in GDB. */
+
+struct aoutdata {
+ struct internal_exec *hdr; /* exec file header */
+ aout_symbol_type *symbols; /* symtab for input bfd */
+
+ /* For ease, we do this */
+ asection *textsec;
+ asection *datasec;
+ asection *bsssec;
+
+ /* We remember these offsets so that after check_file_format, we have
+ no dependencies on the particular format of the exec_hdr. */
+ file_ptr sym_filepos;
+ file_ptr str_filepos;
+
+ /* Size of a relocation entry in external form */
+ unsigned reloc_entry_size;
+
+ /* Size of a symbol table entry in external form */
+ unsigned symbol_entry_size;
+
+ /* Page size - needed for alignment of demand paged files. */
+ unsigned long page_size;
+
+ /* Segment size - needed for alignment of demand paged files. */
+ unsigned long segment_size;
+
+ unsigned exec_bytes_size;
+ unsigned vma_adjusted : 1;
+
+ /* used when a bfd supports several highly similar formats */
+ enum {
+ default_format = 0,
+ gnu_encap_format } subformat;
+
+ enum {
+ undecided_magic = 0,
+ z_magic,
+ o_magic,
+ n_magic } magic;
+};
+
+struct aout_data_struct {
+ struct aoutdata a;
+ struct internal_exec e;
+};
+
+#define adata(bfd) ((bfd)->tdata.aout_data->a)
+#define exec_hdr(bfd) (adata(bfd).hdr)
+#define obj_aout_symbols(bfd) (adata(bfd).symbols)
+#define obj_textsec(bfd) (adata(bfd).textsec)
+#define obj_datasec(bfd) (adata(bfd).datasec)
+#define obj_bsssec(bfd) (adata(bfd).bsssec)
+#define obj_sym_filepos(bfd) (adata(bfd).sym_filepos)
+#define obj_str_filepos(bfd) (adata(bfd).str_filepos)
+#define obj_reloc_entry_size(bfd) (adata(bfd).reloc_entry_size)
+#define obj_symbol_entry_size(bfd) (adata(bfd).symbol_entry_size)
+#define obj_aout_subformat(bfd) (adata(bfd).subformat)
+
+/* We take the address of the first element of an asymbol to ensure that the
+ macro is only ever applied to an asymbol */
+#define aout_symbol(asymbol) ((aout_symbol_type *)(&(asymbol)->the_bfd))
+
+/* Prototype declarations for functions defined in aoutx.h */
+
+boolean
+NAME(aout,squirt_out_relocs) PARAMS ((bfd *abfd, asection *section));
+
+bfd_target *
+NAME(aout,some_aout_object_p) PARAMS ((bfd *abfd,
+ struct internal_exec *execp,
+ bfd_target * (*callback)(bfd *)));
+
+boolean
+NAME(aout,mkobject) PARAMS ((bfd *abfd));
+
+enum machine_type
+NAME(aout,machine_type) PARAMS ((enum bfd_architecture arch,
+ unsigned long machine));
+
+boolean
+NAME(aout,set_arch_mach) PARAMS ((bfd *abfd, enum bfd_architecture arch,
+ unsigned long machine));
+
+boolean
+NAME(aout,new_section_hook) PARAMS ((bfd *abfd, asection *newsect));
+
+boolean
+NAME(aout,set_section_contents) PARAMS ((bfd *abfd, sec_ptr section,
+ PTR location, file_ptr offset, bfd_size_type count));
+
+asymbol *
+NAME(aout,make_empty_symbol) PARAMS ((bfd *abfd));
+
+boolean
+NAME(aout,slurp_symbol_table) PARAMS ((bfd *abfd));
+
+void
+NAME(aout,write_syms) PARAMS ((bfd *abfd));
+
+void
+NAME(aout,reclaim_symbol_table) PARAMS ((bfd *abfd));
+
+unsigned int
+NAME(aout,get_symtab_upper_bound) PARAMS ((bfd *abfd));
+
+unsigned int
+NAME(aout,get_symtab) PARAMS ((bfd *abfd, asymbol **location));
+
+boolean
+NAME(aout,slurp_reloc_table) PARAMS ((bfd *abfd, sec_ptr asect,
+ asymbol **symbols));
+
+unsigned int
+NAME(aout,canonicalize_reloc) PARAMS ((bfd *abfd, sec_ptr section,
+ arelent **relptr, asymbol **symbols));
+
+unsigned int
+NAME(aout,get_reloc_upper_bound) PARAMS ((bfd *abfd, sec_ptr asect));
+
+void
+NAME(aout,reclaim_reloc) PARAMS ((bfd *ignore_abfd, sec_ptr ignore));
+
+alent *
+NAME(aout,get_lineno) PARAMS ((bfd *ignore_abfd, asymbol *ignore_symbol));
+
+void
+NAME(aout,print_symbol) PARAMS ((bfd *ignore_abfd, PTR file,
+ asymbol *symbol, bfd_print_symbol_type how));
+
+void
+NAME(aout,get_symbol_info) PARAMS ((bfd *ignore_abfd,
+ asymbol *symbol, symbol_info *ret));
+
+boolean
+NAME(aout,close_and_cleanup) PARAMS ((bfd *abfd));
+
+boolean
+NAME(aout,find_nearest_line) PARAMS ((bfd *abfd, asection *section,
+ asymbol **symbols, bfd_vma offset, CONST char **filename_ptr,
+ CONST char **functionname_ptr, unsigned int *line_ptr));
+
+int
+NAME(aout,sizeof_headers) PARAMS ((bfd *abfd, boolean exec));
+
+boolean
+NAME(aout,adjust_sizes_and_vmas) PARAMS ((bfd *abfd,
+ bfd_size_type *text_size, file_ptr *text_end));
+
+void
+NAME(aout,swap_exec_header_in) PARAMS ((bfd *abfd,
+ struct external_exec *raw_bytes, struct internal_exec *execp));
+
+void
+NAME(aout,swap_exec_header_out) PARAMS ((bfd *abfd,
+ struct internal_exec *execp, struct external_exec *raw_bytes));
+
+/* Prototypes for functions in stab-syms.c. */
+
+CONST char *
+aout_stab_name PARAMS ((int code));
+
+/* A.out uses the generic versions of these routines... */
+
+#define aout_32_get_section_contents bfd_generic_get_section_contents
+#define aout_32_close_and_cleanup bfd_generic_close_and_cleanup
+
+#define aout_64_get_section_contents bfd_generic_get_section_contents
+#define aout_64_close_and_cleanup bfd_generic_close_and_cleanup
+#ifndef NO_WRITE_HEADER_KLUDGE
+#define NO_WRITE_HEADER_KLUDGE 0
+#endif
+
+#ifndef WRITE_HEADERS
+#define WRITE_HEADERS(abfd, execp) \
+ { \
+ bfd_size_type text_size; /* dummy vars */ \
+ file_ptr text_end; \
+ if (adata(abfd).magic == undecided_magic) \
+ NAME(aout,adjust_sizes_and_vmas) (abfd, &text_size, &text_end); \
+ \
+ execp->a_syms = bfd_get_symcount (abfd) * EXTERNAL_NLIST_SIZE; \
+ execp->a_entry = bfd_get_start_address (abfd); \
+ \
+ execp->a_trsize = ((obj_textsec (abfd)->reloc_count) * \
+ obj_reloc_entry_size (abfd)); \
+ execp->a_drsize = ((obj_datasec (abfd)->reloc_count) * \
+ obj_reloc_entry_size (abfd)); \
+ NAME(aout,swap_exec_header_out) (abfd, execp, &exec_bytes); \
+ \
+ bfd_seek (abfd, (file_ptr) 0, SEEK_SET); \
+ bfd_write ((PTR) &exec_bytes, 1, EXEC_BYTES_SIZE, abfd); \
+ /* Now write out reloc info, followed by syms and strings */ \
+ \
+ if (bfd_get_symcount (abfd) != 0) \
+ { \
+ bfd_seek (abfd, (file_ptr)(N_SYMOFF(*execp)), SEEK_SET); \
+ \
+ NAME(aout,write_syms)(abfd); \
+ \
+ bfd_seek (abfd, (file_ptr)(N_TRELOFF(*execp)), SEEK_SET); \
+ \
+ if (!NAME(aout,squirt_out_relocs) (abfd, obj_textsec (abfd))) return false; \
+ bfd_seek (abfd, (file_ptr)(N_DRELOFF(*execp)), SEEK_SET); \
+ \
+ if (!NAME(aout,squirt_out_relocs)(abfd, obj_datasec (abfd))) return false; \
+ } \
+ }
+#endif
diff --git a/gnu/usr.bin/gdb/bfd/libbfd.c b/gnu/usr.bin/gdb/bfd/libbfd.c
new file mode 100644
index 0000000..1bf8f7f
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/libbfd.c
@@ -0,0 +1,850 @@
+/* Assorted BFD support routines, only used internally.
+ Copyright 1990, 1991, 1992 Free Software Foundation, Inc.
+ Written by Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+
+/*
+SECTION
+ libbfd
+
+DESCRIPTION
+ This file contains various routines which are used within BFD.
+ They are not intended for export, but are documented here for
+ completeness.
+*/
+
+boolean
+DEFUN(_bfd_dummy_new_section_hook,(ignore, ignore_newsect),
+ bfd *ignore AND
+ asection *ignore_newsect)
+{
+ return true;
+}
+
+boolean
+DEFUN(bfd_false ,(ignore),
+ bfd *ignore)
+{
+ return false;
+}
+
+boolean
+DEFUN(bfd_true,(ignore),
+ bfd *ignore)
+{
+ return true;
+}
+
+PTR
+DEFUN(bfd_nullvoidptr,(ignore),
+ bfd *ignore)
+{
+ return (PTR)NULL;
+}
+
+int
+DEFUN(bfd_0,(ignore),
+ bfd *ignore)
+{
+ return 0;
+}
+
+unsigned int
+DEFUN(bfd_0u,(ignore),
+ bfd *ignore)
+{
+ return 0;
+}
+
+void
+DEFUN(bfd_void,(ignore),
+ bfd *ignore)
+{
+}
+
+boolean
+DEFUN(_bfd_dummy_core_file_matches_executable_p,(ignore_core_bfd, ignore_exec_bfd),
+ bfd *ignore_core_bfd AND
+ bfd *ignore_exec_bfd)
+{
+ bfd_error = invalid_operation;
+ return false;
+}
+
+/* of course you can't initialize a function to be the same as another, grr */
+
+char *
+DEFUN(_bfd_dummy_core_file_failing_command,(ignore_abfd),
+ bfd *ignore_abfd)
+{
+ return (char *)NULL;
+}
+
+int
+DEFUN(_bfd_dummy_core_file_failing_signal,(ignore_abfd),
+ bfd *ignore_abfd)
+{
+ return 0;
+}
+
+bfd_target *
+DEFUN(_bfd_dummy_target,(ignore_abfd),
+ bfd *ignore_abfd)
+{
+ return 0;
+}
+
+/** zalloc -- allocate and clear storage */
+
+
+#ifndef zalloc
+char *
+DEFUN(zalloc,(size),
+ bfd_size_type size)
+{
+ char *ptr = (char *) malloc ((size_t)size);
+
+ if ((ptr != NULL) && (size != 0))
+ memset(ptr,0, (size_t) size);
+
+ return ptr;
+}
+#endif
+
+/*
+INTERNAL_FUNCTION
+ bfd_xmalloc
+
+SYNOPSIS
+ PTR bfd_xmalloc( bfd_size_type size);
+
+DESCRIPTION
+ Like malloc, but exit if no more memory.
+
+*/
+
+/** There is major inconsistency in how running out of memory is handled.
+ Some routines return a NULL, and set bfd_error to no_memory.
+ However, obstack routines can't do this ... */
+
+
+DEFUN(PTR bfd_xmalloc,(size),
+ bfd_size_type size)
+{
+ static CONST char no_memory_message[] = "Virtual memory exhausted!\n";
+ PTR ptr;
+ if (size == 0) size = 1;
+ ptr = (PTR)malloc((size_t) size);
+ if (!ptr)
+ {
+ write (2, no_memory_message, sizeof(no_memory_message)-1);
+ exit (-1);
+ }
+ return ptr;
+}
+
+/*
+INTERNAL_FUNCTION
+ bfd_xmalloc_by_size_t
+
+SYNOPSIS
+ PTR bfd_xmalloc_by_size_t ( size_t size);
+
+DESCRIPTION
+ Like malloc, but exit if no more memory.
+ Uses size_t, so it's suitable for use as obstack_chunk_alloc.
+ */
+PTR
+DEFUN(bfd_xmalloc_by_size_t, (size),
+ size_t size)
+{
+ return bfd_xmalloc ((bfd_size_type) size);
+}
+
+/* Some IO code */
+
+
+/* Note that archive entries don't have streams; they share their parent's.
+ This allows someone to play with the iostream behind BFD's back.
+
+ Also, note that the origin pointer points to the beginning of a file's
+ contents (0 for non-archive elements). For archive entries this is the
+ first octet in the file, NOT the beginning of the archive header. */
+
+static
+int DEFUN(real_read,(where, a,b, file),
+ PTR where AND
+ int a AND
+ int b AND
+ FILE *file)
+{
+ return fread(where, a,b,file);
+}
+bfd_size_type
+DEFUN(bfd_read,(ptr, size, nitems, abfd),
+ PTR ptr AND
+ bfd_size_type size AND
+ bfd_size_type nitems AND
+ bfd *abfd)
+{
+ int nread;
+ nread = real_read (ptr, 1, (int)(size*nitems), bfd_cache_lookup(abfd));
+#ifdef FILE_OFFSET_IS_CHAR_INDEX
+ if (nread > 0)
+ abfd->where += nread;
+#endif
+ return nread;
+}
+
+bfd_size_type
+DEFUN(bfd_write,(ptr, size, nitems, abfd),
+ CONST PTR ptr AND
+ bfd_size_type size AND
+ bfd_size_type nitems AND
+ bfd *abfd)
+{
+ int nwrote = fwrite (ptr, 1, (int)(size*nitems), bfd_cache_lookup(abfd));
+#ifdef FILE_OFFSET_IS_CHAR_INDEX
+ if (nwrote > 0)
+ abfd->where += nwrote;
+#endif
+ return nwrote;
+}
+
+/*
+INTERNAL_FUNCTION
+ bfd_write_bigendian_4byte_int
+
+SYNOPSIS
+ void bfd_write_bigendian_4byte_int(bfd *abfd, int i);
+
+DESCRIPTION
+ Writes a 4 byte integer to the outputing bfd, in big endian
+ mode regardless of what else is going on. This is useful in
+ archives.
+
+*/
+void
+DEFUN(bfd_write_bigendian_4byte_int,(abfd, i),
+ bfd *abfd AND
+ int i)
+{
+ bfd_byte buffer[4];
+ bfd_putb32(i, buffer);
+ bfd_write((PTR)buffer, 4, 1, abfd);
+}
+
+long
+DEFUN(bfd_tell,(abfd),
+ bfd *abfd)
+{
+ file_ptr ptr;
+
+ ptr = ftell (bfd_cache_lookup(abfd));
+
+ if (abfd->my_archive)
+ ptr -= abfd->origin;
+ abfd->where = ptr;
+ return ptr;
+}
+
+int
+DEFUN(bfd_flush,(abfd),
+ bfd *abfd)
+{
+ return fflush (bfd_cache_lookup(abfd));
+}
+
+int
+DEFUN(bfd_stat,(abfd, statbuf),
+ bfd *abfd AND
+ struct stat *statbuf)
+{
+ return fstat (fileno(bfd_cache_lookup(abfd)), statbuf);
+}
+
+int
+DEFUN(bfd_seek,(abfd, position, direction),
+ bfd * CONST abfd AND
+ CONST file_ptr position AND
+ CONST int direction)
+{
+ int result;
+ FILE *f;
+ file_ptr file_position;
+ /* For the time being, a BFD may not seek to it's end. The problem
+ is that we don't easily have a way to recognize the end of an
+ element in an archive. */
+
+ BFD_ASSERT (direction == SEEK_SET || direction == SEEK_CUR);
+
+ if (direction == SEEK_CUR && position == 0)
+ return 0;
+#ifdef FILE_OFFSET_IS_CHAR_INDEX
+ if (abfd->format != bfd_archive && abfd->my_archive == 0)
+ {
+#ifndef NDEBUG
+ /* Explanation for this code: I'm only about 95+% sure that the above
+ conditions are sufficient and that all i/o calls are properly
+ adjusting the `where' field. So this is sort of an `assert'
+ that the `where' field is correct. If we can go a while without
+ tripping the abort, we can probably safely disable this code,
+ so that the real optimizations happen. */
+ file_ptr where_am_i_now;
+ where_am_i_now = ftell (bfd_cache_lookup (abfd));
+ if (abfd->my_archive)
+ where_am_i_now -= abfd->origin;
+ if (where_am_i_now != abfd->where)
+ abort ();
+#endif
+ if (direction == SEEK_SET && position == abfd->where)
+ return 0;
+ }
+ else
+ {
+ /* We need something smarter to optimize access to archives.
+ Currently, anything inside an archive is read via the file
+ handle for the archive. Which means that a bfd_seek on one
+ component affects the `current position' in the archive, as
+ well as in any other component.
+
+ It might be sufficient to put a spike through the cache
+ abstraction, and look to the archive for the file position,
+ but I think we should try for something cleaner.
+
+ In the meantime, no optimization for archives. */
+ }
+#endif
+
+ f = bfd_cache_lookup (abfd);
+ file_position = position;
+ if (direction == SEEK_SET && abfd->my_archive != NULL)
+ file_position += abfd->origin;
+
+ result = fseek (f, file_position, direction);
+
+ if (result != 0)
+ /* Force redetermination of `where' field. */
+ bfd_tell (abfd);
+ else
+ {
+#ifdef FILE_OFFSET_IS_CHAR_INDEX
+ /* Adjust `where' field. */
+ if (direction == SEEK_SET)
+ abfd->where = position;
+ else
+ abfd->where += position;
+#endif
+ }
+ return result;
+}
+
+/** Make a string table */
+
+/*>bfd.h<
+ Add string to table pointed to by table, at location starting with free_ptr.
+ resizes the table if necessary (if it's NULL, creates it, ignoring
+ table_length). Updates free_ptr, table, table_length */
+
+boolean
+DEFUN(bfd_add_to_string_table,(table, new_string, table_length, free_ptr),
+ char **table AND
+ char *new_string AND
+ unsigned int *table_length AND
+ char **free_ptr)
+{
+ size_t string_length = strlen (new_string) + 1; /* include null here */
+ char *base = *table;
+ size_t space_length = *table_length;
+ unsigned int offset = (base ? *free_ptr - base : 0);
+
+ if (base == NULL) {
+ /* Avoid a useless regrow if we can (but of course we still
+ take it next time */
+ space_length = (string_length < DEFAULT_STRING_SPACE_SIZE ?
+ DEFAULT_STRING_SPACE_SIZE : string_length+1);
+ base = zalloc ((bfd_size_type) space_length);
+
+ if (base == NULL) {
+ bfd_error = no_memory;
+ return false;
+ }
+ }
+
+ if ((size_t)(offset + string_length) >= space_length) {
+ /* Make sure we will have enough space */
+ while ((size_t)(offset + string_length) >= space_length)
+ space_length += space_length/2; /* grow by 50% */
+
+ base = (char *) realloc (base, space_length);
+ if (base == NULL) {
+ bfd_error = no_memory;
+ return false;
+ }
+
+ }
+
+ memcpy (base + offset, new_string, string_length);
+ *table = base;
+ *table_length = space_length;
+ *free_ptr = base + offset + string_length;
+
+ return true;
+}
+
+/** The do-it-yourself (byte) sex-change kit */
+
+/* The middle letter e.g. get<b>short indicates Big or Little endian
+ target machine. It doesn't matter what the byte order of the host
+ machine is; these routines work for either. */
+
+/* FIXME: Should these take a count argument?
+ Answer (gnu@cygnus.com): No, but perhaps they should be inline
+ functions in swap.h #ifdef __GNUC__.
+ Gprof them later and find out. */
+
+/*
+FUNCTION
+ bfd_put_size
+FUNCTION
+ bfd_get_size
+
+DESCRIPTION
+ These macros as used for reading and writing raw data in
+ sections; each access (except for bytes) is vectored through
+ the target format of the BFD and mangled accordingly. The
+ mangling performs any necessary endian translations and
+ removes alignment restrictions. Note that types accepted and
+ returned by these macros are identical so they can be swapped
+ around in macros--for example libaout.h defines GET_WORD to
+ either bfd_get_32 or bfd_get_64.
+
+ In the put routines, val must be a bfd_vma. If we are on a
+ system without prototypes, the caller is responsible for making
+ sure that is true, with a cast if necessary. We don't cast
+ them in the macro definitions because that would prevent lint
+ or gcc -Wall from detecting sins such as passing a pointer.
+ To detect calling these with less than a bfd_vma, use gcc
+ -Wconversion on a host with 64 bit bfd_vma's.
+
+.
+.{* Byte swapping macros for user section data. *}
+.
+.#define bfd_put_8(abfd, val, ptr) \
+. (*((unsigned char *)(ptr)) = (unsigned char)val)
+.#define bfd_put_signed_8 \
+. bfd_put_8
+.#define bfd_get_8(abfd, ptr) \
+. (*(unsigned char *)(ptr))
+.#define bfd_get_signed_8(abfd, ptr) \
+. ((*(unsigned char *)(ptr) ^ 0x80) - 0x80)
+.
+.#define bfd_put_16(abfd, val, ptr) \
+. BFD_SEND(abfd, bfd_putx16, ((val),(ptr)))
+.#define bfd_put_signed_16 \
+. bfd_put_16
+.#define bfd_get_16(abfd, ptr) \
+. BFD_SEND(abfd, bfd_getx16, (ptr))
+.#define bfd_get_signed_16(abfd, ptr) \
+. BFD_SEND (abfd, bfd_getx_signed_16, (ptr))
+.
+.#define bfd_put_32(abfd, val, ptr) \
+. BFD_SEND(abfd, bfd_putx32, ((val),(ptr)))
+.#define bfd_put_signed_32 \
+. bfd_put_32
+.#define bfd_get_32(abfd, ptr) \
+. BFD_SEND(abfd, bfd_getx32, (ptr))
+.#define bfd_get_signed_32(abfd, ptr) \
+. BFD_SEND(abfd, bfd_getx_signed_32, (ptr))
+.
+.#define bfd_put_64(abfd, val, ptr) \
+. BFD_SEND(abfd, bfd_putx64, ((val), (ptr)))
+.#define bfd_put_signed_64 \
+. bfd_put_64
+.#define bfd_get_64(abfd, ptr) \
+. BFD_SEND(abfd, bfd_getx64, (ptr))
+.#define bfd_get_signed_64(abfd, ptr) \
+. BFD_SEND(abfd, bfd_getx_signed_64, (ptr))
+.
+*/
+
+/*
+FUNCTION
+ bfd_h_put_size
+FUNCTION
+ bfd_h_get_size
+
+DESCRIPTION
+ These macros have the same function as their <<bfd_get_x>>
+ bretherin, except that they are used for removing information
+ for the header records of object files. Believe it or not,
+ some object files keep their header records in big endian
+ order, and their data in little endian order.
+.
+.{* Byte swapping macros for file header data. *}
+.
+.#define bfd_h_put_8(abfd, val, ptr) \
+. bfd_put_8 (abfd, val, ptr)
+.#define bfd_h_put_signed_8(abfd, val, ptr) \
+. bfd_put_8 (abfd, val, ptr)
+.#define bfd_h_get_8(abfd, ptr) \
+. bfd_get_8 (abfd, ptr)
+.#define bfd_h_get_signed_8(abfd, ptr) \
+. bfd_get_signed_8 (abfd, ptr)
+.
+.#define bfd_h_put_16(abfd, val, ptr) \
+. BFD_SEND(abfd, bfd_h_putx16,(val,ptr))
+.#define bfd_h_put_signed_16 \
+. bfd_h_put_16
+.#define bfd_h_get_16(abfd, ptr) \
+. BFD_SEND(abfd, bfd_h_getx16,(ptr))
+.#define bfd_h_get_signed_16(abfd, ptr) \
+. BFD_SEND(abfd, bfd_h_getx_signed_16, (ptr))
+.
+.#define bfd_h_put_32(abfd, val, ptr) \
+. BFD_SEND(abfd, bfd_h_putx32,(val,ptr))
+.#define bfd_h_put_signed_32 \
+. bfd_h_put_32
+.#define bfd_h_get_32(abfd, ptr) \
+. BFD_SEND(abfd, bfd_h_getx32,(ptr))
+.#define bfd_h_get_signed_32(abfd, ptr) \
+. BFD_SEND(abfd, bfd_h_getx_signed_32, (ptr))
+.
+.#define bfd_h_put_64(abfd, val, ptr) \
+. BFD_SEND(abfd, bfd_h_putx64,(val, ptr))
+.#define bfd_h_put_signed_64 \
+. bfd_h_put_64
+.#define bfd_h_get_64(abfd, ptr) \
+. BFD_SEND(abfd, bfd_h_getx64,(ptr))
+.#define bfd_h_get_signed_64(abfd, ptr) \
+. BFD_SEND(abfd, bfd_h_getx_signed_64, (ptr))
+.
+*/
+
+/* Sign extension to bfd_signed_vma. */
+#define COERCE16(x) (((bfd_signed_vma) (x) ^ 0x8000) - 0x8000)
+#define COERCE32(x) (((bfd_signed_vma) (x) ^ 0x80000000) - 0x80000000)
+#define EIGHT_GAZILLION (((HOST_64_BIT)0x80000000) << 32)
+#define COERCE64(x) \
+ (((bfd_signed_vma) (x) ^ EIGHT_GAZILLION) - EIGHT_GAZILLION)
+
+bfd_vma
+DEFUN(bfd_getb16,(addr),
+ register bfd_byte *addr)
+{
+ return (addr[0] << 8) | addr[1];
+}
+
+bfd_vma
+DEFUN(bfd_getl16,(addr),
+ register bfd_byte *addr)
+{
+ return (addr[1] << 8) | addr[0];
+}
+
+bfd_signed_vma
+DEFUN(bfd_getb_signed_16,(addr),
+ register bfd_byte *addr)
+{
+ return COERCE16((addr[0] << 8) | addr[1]);
+}
+
+bfd_signed_vma
+DEFUN(bfd_getl_signed_16,(addr),
+ register bfd_byte *addr)
+{
+ return COERCE16((addr[1] << 8) | addr[0]);
+}
+
+void
+DEFUN(bfd_putb16,(data, addr),
+ bfd_vma data AND
+ register bfd_byte *addr)
+{
+ addr[0] = (bfd_byte)(data >> 8);
+ addr[1] = (bfd_byte )data;
+}
+
+void
+DEFUN(bfd_putl16,(data, addr),
+ bfd_vma data AND
+ register bfd_byte *addr)
+{
+ addr[0] = (bfd_byte )data;
+ addr[1] = (bfd_byte)(data >> 8);
+}
+
+bfd_vma
+bfd_getb32 (addr)
+ register bfd_byte *addr;
+{
+ return (((((bfd_vma)addr[0] << 8) | addr[1]) << 8)
+ | addr[2]) << 8 | addr[3];
+}
+
+bfd_vma
+bfd_getl32 (addr)
+ register bfd_byte *addr;
+{
+ return (((((bfd_vma)addr[3] << 8) | addr[2]) << 8)
+ | addr[1]) << 8 | addr[0];
+}
+
+bfd_signed_vma
+bfd_getb_signed_32 (addr)
+ register bfd_byte *addr;
+{
+ return COERCE32((((((bfd_vma)addr[0] << 8) | addr[1]) << 8)
+ | addr[2]) << 8 | addr[3]);
+}
+
+bfd_signed_vma
+bfd_getl_signed_32 (addr)
+ register bfd_byte *addr;
+{
+ return COERCE32((((((bfd_vma)addr[3] << 8) | addr[2]) << 8)
+ | addr[1]) << 8 | addr[0]);
+}
+
+bfd_vma
+DEFUN(bfd_getb64,(addr),
+ register bfd_byte *addr)
+{
+#ifdef BFD64
+ bfd_vma low, high;
+
+ high= ((((((((addr[0]) << 8) |
+ addr[1]) << 8) |
+ addr[2]) << 8) |
+ addr[3]) );
+
+ low = (((((((((bfd_vma)addr[4]) << 8) |
+ addr[5]) << 8) |
+ addr[6]) << 8) |
+ addr[7]));
+
+ return high << 32 | low;
+#else
+ BFD_FAIL();
+ return 0;
+#endif
+
+}
+
+bfd_vma
+DEFUN(bfd_getl64,(addr),
+ register bfd_byte *addr)
+{
+
+#ifdef BFD64
+ bfd_vma low, high;
+ high= (((((((addr[7] << 8) |
+ addr[6]) << 8) |
+ addr[5]) << 8) |
+ addr[4]));
+
+ low = ((((((((bfd_vma)addr[3] << 8) |
+ addr[2]) << 8) |
+ addr[1]) << 8) |
+ addr[0]) );
+
+ return high << 32 | low;
+#else
+ BFD_FAIL();
+ return 0;
+#endif
+
+}
+
+bfd_signed_vma
+DEFUN(bfd_getb_signed_64,(addr),
+ register bfd_byte *addr)
+{
+#ifdef BFD64
+ bfd_vma low, high;
+
+ high= ((((((((addr[0]) << 8) |
+ addr[1]) << 8) |
+ addr[2]) << 8) |
+ addr[3]) );
+
+ low = (((((((((bfd_vma)addr[4]) << 8) |
+ addr[5]) << 8) |
+ addr[6]) << 8) |
+ addr[7]));
+
+ return COERCE64(high << 32 | low);
+#else
+ BFD_FAIL();
+ return 0;
+#endif
+
+}
+
+bfd_signed_vma
+DEFUN(bfd_getl_signed_64,(addr),
+ register bfd_byte *addr)
+{
+
+#ifdef BFD64
+ bfd_vma low, high;
+ high= (((((((addr[7] << 8) |
+ addr[6]) << 8) |
+ addr[5]) << 8) |
+ addr[4]));
+
+ low = ((((((((bfd_vma)addr[3] << 8) |
+ addr[2]) << 8) |
+ addr[1]) << 8) |
+ addr[0]) );
+
+ return COERCE64(high << 32 | low);
+#else
+ BFD_FAIL();
+ return 0;
+#endif
+
+}
+
+void
+DEFUN(bfd_putb32,(data, addr),
+ bfd_vma data AND
+ register bfd_byte *addr)
+{
+ addr[0] = (bfd_byte)(data >> 24);
+ addr[1] = (bfd_byte)(data >> 16);
+ addr[2] = (bfd_byte)(data >> 8);
+ addr[3] = (bfd_byte)data;
+}
+
+void
+DEFUN(bfd_putl32,(data, addr),
+ bfd_vma data AND
+ register bfd_byte *addr)
+{
+ addr[0] = (bfd_byte)data;
+ addr[1] = (bfd_byte)(data >> 8);
+ addr[2] = (bfd_byte)(data >> 16);
+ addr[3] = (bfd_byte)(data >> 24);
+}
+void
+DEFUN(bfd_putb64,(data, addr),
+ bfd_vma data AND
+ register bfd_byte *addr)
+{
+#ifdef BFD64
+ addr[0] = (bfd_byte)(data >> (7*8));
+ addr[1] = (bfd_byte)(data >> (6*8));
+ addr[2] = (bfd_byte)(data >> (5*8));
+ addr[3] = (bfd_byte)(data >> (4*8));
+ addr[4] = (bfd_byte)(data >> (3*8));
+ addr[5] = (bfd_byte)(data >> (2*8));
+ addr[6] = (bfd_byte)(data >> (1*8));
+ addr[7] = (bfd_byte)(data >> (0*8));
+#else
+ BFD_FAIL();
+#endif
+
+}
+
+void
+DEFUN(bfd_putl64,(data, addr),
+ bfd_vma data AND
+ register bfd_byte *addr)
+{
+#ifdef BFD64
+ addr[7] = (bfd_byte)(data >> (7*8));
+ addr[6] = (bfd_byte)(data >> (6*8));
+ addr[5] = (bfd_byte)(data >> (5*8));
+ addr[4] = (bfd_byte)(data >> (4*8));
+ addr[3] = (bfd_byte)(data >> (3*8));
+ addr[2] = (bfd_byte)(data >> (2*8));
+ addr[1] = (bfd_byte)(data >> (1*8));
+ addr[0] = (bfd_byte)(data >> (0*8));
+#else
+ BFD_FAIL();
+#endif
+
+}
+
+
+/* Default implementation */
+
+boolean
+DEFUN(bfd_generic_get_section_contents, (abfd, section, location, offset, count),
+ bfd *abfd AND
+ sec_ptr section AND
+ PTR location AND
+ file_ptr offset AND
+ bfd_size_type count)
+{
+ if (count == 0)
+ return true;
+ if ((bfd_size_type)(offset+count) > section->_raw_size
+ || bfd_seek(abfd, (file_ptr)(section->filepos + offset), SEEK_SET) == -1
+ || bfd_read(location, (bfd_size_type)1, count, abfd) != count)
+ return (false); /* on error */
+ return (true);
+}
+
+/* This generic function can only be used in implementations where creating
+ NEW sections is disallowed. It is useful in patching existing sections
+ in read-write files, though. See other set_section_contents functions
+ to see why it doesn't work for new sections. */
+boolean
+DEFUN(bfd_generic_set_section_contents, (abfd, section, location, offset, count),
+ bfd *abfd AND
+ sec_ptr section AND
+ PTR location AND
+ file_ptr offset AND
+ bfd_size_type count)
+{
+ if (count == 0)
+ return true;
+ if ((bfd_size_type)(offset+count) > bfd_get_section_size_after_reloc(section)
+ || bfd_seek(abfd, (file_ptr)(section->filepos + offset), SEEK_SET) == -1
+ || bfd_write(location, (bfd_size_type)1, count, abfd) != count)
+ return (false); /* on error */
+ return (true);
+}
+
+/*
+INTERNAL_FUNCTION
+ bfd_log2
+
+DESCRIPTION
+ Return the log base 2 of the value supplied, rounded up. eg an
+ arg of 1025 would return 11.
+
+SYNOPSIS
+ unsigned int bfd_log2(bfd_vma x);
+*/
+
+unsigned
+bfd_log2(x)
+ bfd_vma x;
+{
+ unsigned result = 0;
+ while ( (bfd_vma)(1<< result) < x)
+ result++;
+ return result;
+}
diff --git a/gnu/usr.bin/gdb/bfd/libbfd.h b/gnu/usr.bin/gdb/bfd/libbfd.h
new file mode 100644
index 0000000..fcd5bbc
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/libbfd.h
@@ -0,0 +1,273 @@
+/* libbfd.h -- Declarations used by bfd library *implementation*.
+ (This include file is not for users of the library.)
+ Copyright 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+ Written by Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* Align an address upward to a boundary, expressed as a number of bytes.
+ E.g. align to an 8-byte boundary with argument of 8. */
+#define BFD_ALIGN(this, boundary) \
+ ((( (this) + ((boundary) -1)) & (~((boundary)-1))))
+
+/* If you want to read and write large blocks, you might want to do it
+ in quanta of this amount */
+#define DEFAULT_BUFFERSIZE 8192
+
+/* Set a tdata field. Can't use the other macros for this, since they
+ do casts, and casting to the left of assignment isn't portable. */
+#define set_tdata(bfd, v) ((bfd)->tdata.any = (PTR) (v))
+
+/* tdata for an archive. For an input archive, cache
+ needs to be free()'d. For an output archive, symdefs do. */
+
+struct artdata {
+ file_ptr first_file_filepos;
+ /* Speed up searching the armap */
+ struct ar_cache *cache;
+ bfd *archive_head; /* Only interesting in output routines */
+ carsym *symdefs; /* the symdef entries */
+ symindex symdef_count; /* how many there are */
+ char *extended_names; /* clever intel extension */
+ time_t armap_timestamp; /* Timestamp value written into armap.
+ This is used for BSD archives to check
+ that the timestamp is recent enough
+ for the BSD linker to not complain,
+ just before we finish writing an
+ archive. */
+ file_ptr armap_datepos; /* Position within archive to seek to
+ rewrite the date field. */
+};
+
+#define bfd_ardata(bfd) ((bfd)->tdata.aout_ar_data)
+
+/* Goes in bfd's arelt_data slot */
+struct areltdata {
+ char * arch_header; /* it's actually a string */
+ unsigned int parsed_size; /* octets of filesize not including ar_hdr */
+ char *filename; /* null-terminated */
+};
+
+#define arelt_size(bfd) (((struct areltdata *)((bfd)->arelt_data))->parsed_size)
+
+char *zalloc PARAMS ((bfd_size_type size));
+
+/* These routines allocate and free things on the BFD's obstack. Note
+ that realloc can never occur in place. */
+
+PTR bfd_alloc PARAMS ((bfd *abfd, size_t size));
+PTR bfd_zalloc PARAMS ((bfd *abfd, size_t size));
+PTR bfd_realloc PARAMS ((bfd *abfd, PTR orig, size_t new));
+void bfd_alloc_grow PARAMS ((bfd *abfd, PTR thing, size_t size));
+PTR bfd_alloc_finish PARAMS ((bfd *abfd));
+PTR bfd_alloc_by_size_t PARAMS ((bfd *abfd, size_t wanted));
+
+#define bfd_release(x,y) (void) obstack_free(&(x->memory),y)
+
+
+bfd_size_type bfd_read PARAMS ((PTR ptr, bfd_size_type size,
+ bfd_size_type nitems, bfd *abfd));
+bfd_size_type bfd_write PARAMS ((CONST PTR ptr, bfd_size_type size,
+ bfd_size_type nitems, bfd *abfd));
+int bfd_seek PARAMS ((bfd* CONST abfd, CONST file_ptr fp,
+ CONST int direction));
+long bfd_tell PARAMS ((bfd *abfd));
+
+int bfd_flush PARAMS ((bfd *abfd));
+int bfd_stat PARAMS ((bfd *abfd, struct stat *));
+
+bfd * _bfd_create_empty_archive_element_shell PARAMS ((bfd *obfd));
+bfd * look_for_bfd_in_cache PARAMS ((bfd *arch_bfd, file_ptr index));
+boolean _bfd_generic_mkarchive PARAMS ((bfd *abfd));
+struct areltdata * snarf_ar_hdr PARAMS ((bfd *abfd));
+bfd_target * bfd_generic_archive_p PARAMS ((bfd *abfd));
+boolean bfd_slurp_armap PARAMS ((bfd *abfd));
+boolean bfd_slurp_bsd_armap_f2 PARAMS ((bfd *abfd));
+#define bfd_slurp_bsd_armap bfd_slurp_armap
+#define bfd_slurp_coff_armap bfd_slurp_armap
+boolean _bfd_slurp_extended_name_table PARAMS ((bfd *abfd));
+boolean _bfd_write_archive_contents PARAMS ((bfd *abfd));
+bfd * new_bfd PARAMS (());
+
+#define DEFAULT_STRING_SPACE_SIZE 0x2000
+boolean bfd_add_to_string_table PARAMS ((char **table, char *new_string,
+ unsigned int *table_length,
+ char **free_ptr));
+
+boolean bfd_false PARAMS ((bfd *ignore));
+boolean bfd_true PARAMS ((bfd *ignore));
+PTR bfd_nullvoidptr PARAMS ((bfd *ignore));
+int bfd_0 PARAMS ((bfd *ignore));
+unsigned int bfd_0u PARAMS ((bfd *ignore));
+void bfd_void PARAMS ((bfd *ignore));
+
+bfd * new_bfd_contained_in PARAMS ((bfd *));
+boolean _bfd_dummy_new_section_hook PARAMS ((bfd *ignore, asection *newsect));
+char * _bfd_dummy_core_file_failing_command PARAMS ((bfd *abfd));
+int _bfd_dummy_core_file_failing_signal PARAMS ((bfd *abfd));
+boolean _bfd_dummy_core_file_matches_executable_p PARAMS ((bfd *core_bfd,
+ bfd *exec_bfd));
+bfd_target * _bfd_dummy_target PARAMS ((bfd *abfd));
+
+void bfd_dont_truncate_arname PARAMS ((bfd *abfd, CONST char *filename,
+ char *hdr));
+void bfd_bsd_truncate_arname PARAMS ((bfd *abfd, CONST char *filename,
+ char *hdr));
+void bfd_gnu_truncate_arname PARAMS ((bfd *abfd, CONST char *filename,
+ char *hdr));
+
+boolean bsd_write_armap PARAMS ((bfd *arch, unsigned int elength,
+ struct orl *map, unsigned int orl_count, int stridx));
+
+boolean coff_write_armap PARAMS ((bfd *arch, unsigned int elength,
+ struct orl *map, unsigned int orl_count, int stridx));
+
+bfd * bfd_generic_openr_next_archived_file PARAMS ((bfd *archive,
+ bfd *last_file));
+
+int bfd_generic_stat_arch_elt PARAMS ((bfd *, struct stat *));
+
+boolean bfd_generic_get_section_contents PARAMS ((bfd *abfd, sec_ptr section,
+ PTR location, file_ptr offset,
+ bfd_size_type count));
+
+boolean bfd_generic_set_section_contents PARAMS ((bfd *abfd, sec_ptr section,
+ PTR location, file_ptr offset,
+ bfd_size_type count));
+
+/* Macros to tell if bfds are read or write enabled.
+
+ Note that bfds open for read may be scribbled into if the fd passed
+ to bfd_fdopenr is actually open both for read and write
+ simultaneously. However an output bfd will never be open for
+ read. Therefore sometimes you want to check bfd_read_p or
+ !bfd_read_p, and only sometimes bfd_write_p.
+*/
+
+#define bfd_read_p(abfd) ((abfd)->direction == read_direction || (abfd)->direction == both_direction)
+#define bfd_write_p(abfd) ((abfd)->direction == write_direction || (abfd)->direction == both_direction)
+
+void bfd_assert PARAMS ((char*,int));
+
+#define BFD_ASSERT(x) \
+{ if (!(x)) bfd_assert(__FILE__,__LINE__); }
+
+#define BFD_FAIL() \
+{ bfd_assert(__FILE__,__LINE__); }
+
+FILE * bfd_cache_lookup_worker PARAMS ((bfd *));
+
+extern bfd *bfd_last_cache;
+
+/* Now Steve, what's the story here? */
+#ifdef lint
+#define itos(x) "l"
+#define stoi(x) 1
+#else
+#define itos(x) ((char*)(x))
+#define stoi(x) ((int)(x))
+#endif
+
+/* Generic routine for close_and_cleanup is really just bfd_true. */
+#define bfd_generic_close_and_cleanup bfd_true
+
+/* And more follows */
+
+void
+bfd_check_init PARAMS ((void));
+
+PTR
+bfd_xmalloc PARAMS (( bfd_size_type size));
+
+PTR
+bfd_xmalloc_by_size_t PARAMS (( size_t size));
+
+void
+bfd_write_bigendian_4byte_int PARAMS ((bfd *abfd, int i));
+
+unsigned int
+bfd_log2 PARAMS ((bfd_vma x));
+
+#define BFD_CACHE_MAX_OPEN 10
+extern bfd *bfd_last_cache;
+
+#define bfd_cache_lookup(x) \
+ ((x)==bfd_last_cache? \
+ (FILE*)(bfd_last_cache->iostream): \
+ bfd_cache_lookup_worker(x))
+boolean
+bfd_cache_close PARAMS ((bfd *));
+
+FILE*
+bfd_open_file PARAMS ((bfd *));
+
+FILE *
+bfd_cache_lookup_worker PARAMS ((bfd *));
+
+void
+bfd_constructor_entry PARAMS ((bfd *abfd,
+ asymbol **symbol_ptr_ptr,
+ CONST char*type));
+
+CONST struct reloc_howto_struct *
+bfd_default_reloc_type_lookup
+ PARAMS ((bfd *abfd AND
+ bfd_reloc_code_real_type code));
+
+boolean
+bfd_generic_relax_section
+ PARAMS ((bfd *abfd,
+ asection *section,
+ asymbol **symbols));
+
+bfd_byte *
+
+bfd_generic_get_relocated_section_contents PARAMS ((bfd *abfd,
+ struct bfd_seclet *seclet,
+ bfd_byte *data,
+ boolean relocateable));
+
+boolean
+bfd_generic_seclet_link
+ PARAMS ((bfd *abfd,
+ PTR data,
+ boolean relocateable));
+
+extern bfd_arch_info_type bfd_default_arch_struct;
+boolean
+bfd_default_set_arch_mach PARAMS ((bfd *abfd,
+ enum bfd_architecture arch,
+ unsigned long mach));
+
+void
+bfd_arch_init PARAMS ((void));
+
+void
+bfd_arch_linkin PARAMS ((bfd_arch_info_type *));
+
+CONST bfd_arch_info_type *
+bfd_default_compatible
+ PARAMS ((CONST bfd_arch_info_type *a,
+ CONST bfd_arch_info_type *b));
+
+boolean
+bfd_default_scan PARAMS ((CONST struct bfd_arch_info *, CONST char *));
+
+struct elf_internal_shdr *
+bfd_elf_find_section PARAMS ((bfd *abfd, char *name));
+
diff --git a/gnu/usr.bin/gdb/bfd/libcoff.h b/gnu/usr.bin/gdb/bfd/libcoff.h
new file mode 100644
index 0000000..2aa6ad4
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/libcoff.h
@@ -0,0 +1,352 @@
+/* BFD COFF object file private structure.
+ Copyright (C) 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+ Written by Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* Object file tdata; access macros */
+
+#define coff_data(bfd) ((bfd)->tdata.coff_obj_data)
+#define exec_hdr(bfd) (coff_data(bfd)->hdr)
+#define obj_symbols(bfd) (coff_data(bfd)->symbols)
+#define obj_sym_filepos(bfd) (coff_data(bfd)->sym_filepos)
+
+#define obj_relocbase(bfd) (coff_data(bfd)->relocbase)
+#define obj_raw_syments(bfd) (coff_data(bfd)->raw_syments)
+#define obj_raw_syment_count(bfd) (coff_data(bfd)->raw_syment_count)
+#define obj_convert(bfd) (coff_data(bfd)->conversion_table)
+#define obj_conv_table_size(bfd) (coff_data(bfd)->conv_table_size)
+#if CFILE_STUFF
+#define obj_symbol_slew(bfd) (coff_data(bfd)->symbol_index_slew)
+#else
+#define obj_symbol_slew(bfd) 0
+#endif
+
+
+/* `Tdata' information kept for COFF files. */
+
+typedef struct coff_tdata
+{
+ struct coff_symbol_struct *symbols; /* symtab for input bfd */
+ unsigned int *conversion_table;
+ int conv_table_size;
+ file_ptr sym_filepos;
+
+ long symbol_index_slew; /* used during read to mark whether a
+ C_FILE symbol as been added. */
+
+ struct coff_ptr_struct *raw_syments;
+ struct lineno *raw_linenos;
+ unsigned int raw_syment_count;
+ unsigned short flags;
+
+ /* These are only valid once writing has begun */
+ long int relocbase;
+
+ /* These members communicate important constants about the symbol table
+ to GDB's symbol-reading code. These `constants' unfortunately vary
+ from coff implementation to implementation... */
+ unsigned local_n_btmask;
+ unsigned local_n_btshft;
+ unsigned local_n_tmask;
+ unsigned local_n_tshift;
+ unsigned local_symesz;
+ unsigned local_auxesz;
+ unsigned local_linesz;
+} coff_data_type;
+
+/* We take the address of the first element of a asymbol to ensure that the
+ * macro is only ever applied to an asymbol. */
+#define coffsymbol(asymbol) ((coff_symbol_type *)(&((asymbol)->the_bfd)))
+
+/* Functions in coffgen.c. */
+extern bfd_target *coff_object_p PARAMS ((bfd *));
+extern struct sec *coff_section_from_bfd_index PARAMS ((bfd *, int));
+extern unsigned int coff_get_symtab_upper_bound PARAMS ((bfd *));
+extern unsigned int coff_get_symtab PARAMS ((bfd *, asymbol **));
+extern int coff_count_linenumbers PARAMS ((bfd *));
+extern struct coff_symbol_struct *coff_symbol_from PARAMS ((bfd *, asymbol *));
+extern void coff_renumber_symbols PARAMS ((bfd *));
+extern void coff_mangle_symbols PARAMS ((bfd *));
+extern void coff_write_symbols PARAMS ((bfd *));
+extern void coff_write_linenumbers PARAMS ((bfd *));
+extern alent *coff_get_lineno PARAMS ((bfd *, asymbol *));
+extern asymbol *coff_section_symbol PARAMS ((bfd *, char *));
+extern struct coff_ptr_struct *coff_get_normalized_symtab PARAMS ((bfd *));
+extern unsigned int coff_get_reloc_upper_bound PARAMS ((bfd *, sec_ptr));
+extern asymbol *coff_make_empty_symbol PARAMS ((bfd *));
+extern void coff_print_symbol PARAMS ((bfd *, PTR filep, asymbol *,
+ bfd_print_symbol_type how));
+extern void coff_get_symbol_info PARAMS ((bfd *, asymbol *,
+ symbol_info *ret));
+extern asymbol *coff_bfd_make_debug_symbol PARAMS ((bfd *, PTR,
+ unsigned long));
+extern boolean coff_find_nearest_line PARAMS ((bfd *,
+ asection *,
+ asymbol **,
+ bfd_vma offset,
+ CONST char **filename_ptr,
+ CONST char **functionname_ptr,
+ unsigned int *line_ptr));
+extern int coff_sizeof_headers PARAMS ((bfd *, boolean reloc));
+extern boolean bfd_coff_reloc16_relax_section PARAMS ((bfd *,
+ asection *,
+ asymbol **));
+extern bfd_byte *bfd_coff_reloc16_get_relocated_section_contents
+ PARAMS ((bfd *, struct bfd_seclet *, bfd_byte *, boolean relocateable));
+extern bfd_vma bfd_coff_reloc16_get_value PARAMS ((arelent *,
+ struct bfd_seclet *));
+
+/* And more taken from the source .. */
+
+typedef struct coff_ptr_struct
+{
+
+ /* Remembers the offset from the first symbol in the file for
+ this symbol. Generated by coff_renumber_symbols. */
+unsigned int offset;
+
+ /* Should the tag field of this symbol be renumbered.
+ Created by coff_pointerize_aux. */
+char fix_tag;
+
+ /* Should the endidx field of this symbol be renumbered.
+ Created by coff_pointerize_aux. */
+char fix_end;
+
+ /* The container for the symbol structure as read and translated
+ from the file. */
+
+union {
+ union internal_auxent auxent;
+ struct internal_syment syment;
+ } u;
+} combined_entry_type;
+
+
+ /* Each canonical asymbol really looks like this: */
+
+typedef struct coff_symbol_struct
+{
+ /* The actual symbol which the rest of BFD works with */
+asymbol symbol;
+
+ /* A pointer to the hidden information for this symbol */
+combined_entry_type *native;
+
+ /* A pointer to the linenumber information for this symbol */
+struct lineno_cache_entry *lineno;
+
+ /* Have the line numbers been relocated yet ? */
+boolean done_lineno;
+} coff_symbol_type;
+typedef struct
+{
+ void (*_bfd_coff_swap_aux_in) PARAMS ((
+ bfd *abfd ,
+ PTR ext,
+ int type,
+ int class ,
+ PTR in));
+
+ void (*_bfd_coff_swap_sym_in) PARAMS ((
+ bfd *abfd ,
+ PTR ext,
+ PTR in));
+
+ void (*_bfd_coff_swap_lineno_in) PARAMS ((
+ bfd *abfd,
+ PTR ext,
+ PTR in));
+
+ unsigned int (*_bfd_coff_swap_aux_out) PARAMS ((
+ bfd *abfd,
+ PTR in,
+ int type,
+ int class,
+ PTR ext));
+
+ unsigned int (*_bfd_coff_swap_sym_out) PARAMS ((
+ bfd *abfd,
+ PTR in,
+ PTR ext));
+
+ unsigned int (*_bfd_coff_swap_lineno_out) PARAMS ((
+ bfd *abfd,
+ PTR in,
+ PTR ext));
+
+ unsigned int (*_bfd_coff_swap_reloc_out) PARAMS ((
+ bfd *abfd,
+ PTR src,
+ PTR dst));
+
+ unsigned int (*_bfd_coff_swap_filehdr_out) PARAMS ((
+ bfd *abfd,
+ PTR in,
+ PTR out));
+
+ unsigned int (*_bfd_coff_swap_aouthdr_out) PARAMS ((
+ bfd *abfd,
+ PTR in,
+ PTR out));
+
+ unsigned int (*_bfd_coff_swap_scnhdr_out) PARAMS ((
+ bfd *abfd,
+ PTR in,
+ PTR out));
+
+ unsigned int _bfd_filhsz;
+ unsigned int _bfd_aoutsz;
+ unsigned int _bfd_scnhsz;
+ unsigned int _bfd_symesz;
+ unsigned int _bfd_auxesz;
+ unsigned int _bfd_linesz;
+ boolean _bfd_coff_long_filenames;
+ void (*_bfd_coff_swap_filehdr_in) PARAMS ((
+ bfd *abfd,
+ PTR ext,
+ PTR in));
+ void (*_bfd_coff_swap_aouthdr_in) PARAMS ((
+ bfd *abfd,
+ PTR ext,
+ PTR in));
+ void (*_bfd_coff_swap_scnhdr_in) PARAMS ((
+ bfd *abfd,
+ PTR ext,
+ PTR in));
+ boolean (*_bfd_coff_bad_format_hook) PARAMS ((
+ bfd *abfd,
+ PTR internal_filehdr));
+ boolean (*_bfd_coff_set_arch_mach_hook) PARAMS ((
+ bfd *abfd,
+ PTR internal_filehdr));
+ PTR (*_bfd_coff_mkobject_hook) PARAMS ((
+ bfd *abfd,
+ PTR internal_filehdr,
+ PTR internal_aouthdr));
+ flagword (*_bfd_styp_to_sec_flags_hook) PARAMS ((
+ bfd *abfd,
+ PTR internal_scnhdr));
+ asection *(*_bfd_make_section_hook) PARAMS ((
+ bfd *abfd,
+ char *name));
+ void (*_bfd_set_alignment_hook) PARAMS ((
+ bfd *abfd,
+ asection *sec,
+ PTR internal_scnhdr));
+ boolean (*_bfd_coff_slurp_symbol_table) PARAMS ((
+ bfd *abfd));
+ boolean (*_bfd_coff_symname_in_debug) PARAMS ((
+ bfd *abfd,
+ struct internal_syment *sym));
+ void (*_bfd_coff_reloc16_extra_cases) PARAMS ((
+ bfd *abfd,
+ struct bfd_seclet *seclet,
+ arelent *reloc,
+ bfd_byte *data,
+ unsigned int *src_ptr,
+ unsigned int *dst_ptr));
+ int (*_bfd_coff_reloc16_estimate) PARAMS ((
+ asection *input_section,
+ asymbol **symbols,
+ arelent *r,
+ unsigned int shrink));
+
+} bfd_coff_backend_data;
+
+#define coff_backend_info(abfd) ((bfd_coff_backend_data *) (abfd)->xvec->backend_data)
+
+#define bfd_coff_swap_aux_in(a,e,t,c,i) \
+ ((coff_backend_info (a)->_bfd_coff_swap_aux_in) (a,e,t,c,i))
+
+#define bfd_coff_swap_sym_in(a,e,i) \
+ ((coff_backend_info (a)->_bfd_coff_swap_sym_in) (a,e,i))
+
+#define bfd_coff_swap_lineno_in(a,e,i) \
+ ((coff_backend_info ( a)->_bfd_coff_swap_lineno_in) (a,e,i))
+
+#define bfd_coff_swap_reloc_out(abfd, i, o) \
+ ((coff_backend_info (abfd)->_bfd_coff_swap_reloc_out) (abfd, i, o))
+
+#define bfd_coff_swap_lineno_out(abfd, i, o) \
+ ((coff_backend_info (abfd)->_bfd_coff_swap_lineno_out) (abfd, i, o))
+
+#define bfd_coff_swap_aux_out(abfd, i, t,c,o) \
+ ((coff_backend_info (abfd)->_bfd_coff_swap_aux_out) (abfd, i,t,c, o))
+
+#define bfd_coff_swap_sym_out(abfd, i,o) \
+ ((coff_backend_info (abfd)->_bfd_coff_swap_sym_out) (abfd, i, o))
+
+#define bfd_coff_swap_scnhdr_out(abfd, i,o) \
+ ((coff_backend_info (abfd)->_bfd_coff_swap_scnhdr_out) (abfd, i, o))
+
+#define bfd_coff_swap_filehdr_out(abfd, i,o) \
+ ((coff_backend_info (abfd)->_bfd_coff_swap_filehdr_out) (abfd, i, o))
+
+#define bfd_coff_swap_aouthdr_out(abfd, i,o) \
+ ((coff_backend_info (abfd)->_bfd_coff_swap_aouthdr_out) (abfd, i, o))
+
+#define bfd_coff_filhsz(abfd) (coff_backend_info (abfd)->_bfd_filhsz)
+#define bfd_coff_aoutsz(abfd) (coff_backend_info (abfd)->_bfd_aoutsz)
+#define bfd_coff_scnhsz(abfd) (coff_backend_info (abfd)->_bfd_scnhsz)
+#define bfd_coff_symesz(abfd) (coff_backend_info (abfd)->_bfd_symesz)
+#define bfd_coff_auxesz(abfd) (coff_backend_info (abfd)->_bfd_auxesz)
+#define bfd_coff_linesz(abfd) (coff_backend_info (abfd)->_bfd_linesz)
+#define bfd_coff_long_filenames(abfd) (coff_backend_info (abfd)->_bfd_coff_long_filenames)
+#define bfd_coff_swap_filehdr_in(abfd, i,o) \
+ ((coff_backend_info (abfd)->_bfd_coff_swap_filehdr_in) (abfd, i, o))
+
+#define bfd_coff_swap_aouthdr_in(abfd, i,o) \
+ ((coff_backend_info (abfd)->_bfd_coff_swap_aouthdr_in) (abfd, i, o))
+
+#define bfd_coff_swap_scnhdr_in(abfd, i,o) \
+ ((coff_backend_info (abfd)->_bfd_coff_swap_scnhdr_in) (abfd, i, o))
+
+#define bfd_coff_bad_format_hook(abfd, filehdr) \
+ ((coff_backend_info (abfd)->_bfd_coff_bad_format_hook) (abfd, filehdr))
+
+#define bfd_coff_set_arch_mach_hook(abfd, filehdr)\
+ ((coff_backend_info (abfd)->_bfd_coff_set_arch_mach_hook) (abfd, filehdr))
+#define bfd_coff_mkobject_hook(abfd, filehdr, aouthdr)\
+ ((coff_backend_info (abfd)->_bfd_coff_mkobject_hook) (abfd, filehdr, aouthdr))
+
+#define bfd_coff_styp_to_sec_flags_hook(abfd, scnhdr)\
+ ((coff_backend_info (abfd)->_bfd_styp_to_sec_flags_hook) (abfd, scnhdr))
+
+#define bfd_coff_make_section_hook(abfd, name)\
+ ((coff_backend_info (abfd)->_bfd_make_section_hook) (abfd, name))
+
+#define bfd_coff_set_alignment_hook(abfd, sec, scnhdr)\
+ ((coff_backend_info (abfd)->_bfd_set_alignment_hook) (abfd, sec, scnhdr))
+
+#define bfd_coff_slurp_symbol_table(abfd)\
+ ((coff_backend_info (abfd)->_bfd_coff_slurp_symbol_table) (abfd))
+
+#define bfd_coff_symname_in_debug(abfd, sym)\
+ ((coff_backend_info (abfd)->_bfd_coff_symname_in_debug) (abfd, sym))
+
+#define bfd_coff_reloc16_extra_cases(abfd, seclet, reloc, data, src_ptr, dst_ptr)\
+ ((coff_backend_info (abfd)->_bfd_coff_reloc16_extra_cases)\
+ (abfd, seclet, reloc, data, src_ptr, dst_ptr))
+
+#define bfd_coff_reloc16_estimate(abfd, section, symbols, reloc, shrink)\
+ ((coff_backend_info (abfd)->_bfd_coff_reloc16_estimate)\
+ (section, symbols, reloc, shrink))
+
+
diff --git a/gnu/usr.bin/gdb/bfd/libecoff.h b/gnu/usr.bin/gdb/bfd/libecoff.h
new file mode 100644
index 0000000..ad3e8f2
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/libecoff.h
@@ -0,0 +1,265 @@
+/* BFD ECOFF object file private structure.
+ Copyright (C) 1993 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This is the backend information kept for ECOFF files. This
+ structure is constant for a particular backend. The first element
+ is the COFF backend data structure, so that ECOFF targets can use
+ the generic COFF code. */
+
+#define ecoff_backend(abfd) \
+ ((struct ecoff_backend_data *) (abfd)->xvec->backend_data)
+
+struct ecoff_backend_data
+{
+ /* COFF backend information. This must be the first field. */
+ bfd_coff_backend_data coff;
+ /* Supported architecture. */
+ enum bfd_architecture arch;
+ /* Symbol table magic number. */
+ int sym_magic;
+ /* Initial portion of armap string. */
+ const char *armap_start;
+ /* Alignment of debugging information. E.g., 4. */
+ bfd_size_type debug_align;
+ /* The page boundary used to align sections in a demand-paged
+ executable file. E.g., 0x1000. */
+ bfd_vma round;
+ /* Bitsize of constructor entries. */
+ unsigned int constructor_bitsize;
+ /* Reloc to use for constructor entries. */
+ CONST struct reloc_howto_struct *constructor_reloc;
+ /* Sizes of external symbolic information. */
+ bfd_size_type external_hdr_size;
+ bfd_size_type external_dnr_size;
+ bfd_size_type external_pdr_size;
+ bfd_size_type external_sym_size;
+ bfd_size_type external_opt_size;
+ bfd_size_type external_fdr_size;
+ bfd_size_type external_rfd_size;
+ bfd_size_type external_ext_size;
+ /* Functions to swap in external symbolic data. */
+ void (*swap_hdr_in) PARAMS ((bfd *, PTR, HDRR *));
+ void (*swap_dnr_in) PARAMS ((bfd *, PTR, DNR *));
+ void (*swap_pdr_in) PARAMS ((bfd *, PTR, PDR *));
+ void (*swap_sym_in) PARAMS ((bfd *, PTR, SYMR *));
+ void (*swap_opt_in) PARAMS ((bfd *, PTR, OPTR *));
+ void (*swap_fdr_in) PARAMS ((bfd *, PTR, FDR *));
+ void (*swap_rfd_in) PARAMS ((bfd *, PTR, RFDT *));
+ void (*swap_ext_in) PARAMS ((bfd *, PTR, EXTR *));
+ /* Functions to swap out external symbolic data. */
+ void (*swap_hdr_out) PARAMS ((bfd *, const HDRR *, PTR));
+ void (*swap_dnr_out) PARAMS ((bfd *, const DNR *, PTR));
+ void (*swap_pdr_out) PARAMS ((bfd *, const PDR *, PTR));
+ void (*swap_sym_out) PARAMS ((bfd *, const SYMR *, PTR));
+ void (*swap_opt_out) PARAMS ((bfd *, const OPTR *, PTR));
+ void (*swap_fdr_out) PARAMS ((bfd *, const FDR *, PTR));
+ void (*swap_rfd_out) PARAMS ((bfd *, const RFDT *, PTR));
+ void (*swap_ext_out) PARAMS ((bfd *, const EXTR *, PTR));
+ /* It so happens that the auxiliary type information has the same
+ type and format for all known ECOFF targets. I don't see any
+ reason that that should change, so at least for now the auxiliary
+ swapping information is not in this table. */
+ /* External reloc size. */
+ bfd_size_type external_reloc_size;
+ /* Reloc swapping functions. */
+ void (*swap_reloc_in) PARAMS ((bfd *, PTR, struct internal_reloc *));
+ void (*swap_reloc_out) PARAMS ((bfd *, const struct internal_reloc *, PTR));
+ /* Backend reloc tweaking. */
+ void (*finish_reloc) PARAMS ((bfd *, struct internal_reloc *, arelent *));
+};
+
+/* This is the target specific information kept for ECOFF files. */
+
+#define ecoff_data(abfd) ((abfd)->tdata.ecoff_obj_data)
+
+typedef struct ecoff_tdata
+{
+ /* The reloc file position, set by
+ ecoff_compute_section_file_positions. */
+ file_ptr reloc_filepos;
+
+ /* The symbol table file position, set by ecoff_mkobject_hook. */
+ file_ptr sym_filepos;
+
+ /* The start and end of the text segment. Only valid for an
+ existing file, not for one we are creating. */
+ unsigned long text_start;
+ unsigned long text_end;
+
+ /* The cached gp value. This is used when relocating. */
+ bfd_vma gp;
+
+ /* The maximum size of objects to optimize using gp. This is
+ typically set by the -G option to the compiler, assembler or
+ linker. */
+ int gp_size;
+
+ /* The register masks. When linking, all the masks found in the
+ input files are combined into the masks of the output file.
+ These are not all used for all targets, but that's OK, because
+ the relevant ones are the only ones swapped in and out. */
+ unsigned long gprmask;
+ unsigned long fprmask;
+ unsigned long cprmask[4];
+
+ /* The size of the unswapped ECOFF symbolic information. */
+ bfd_size_type raw_size;
+
+ /* The unswapped ECOFF symbolic information. */
+ PTR raw_syments;
+
+ /* The swapped ECOFF symbolic header. */
+ HDRR symbolic_header;
+
+ /* Pointers to the unswapped symbolic information. */
+ unsigned char *line;
+ PTR external_dnr; /* struct dnr_ext */
+ PTR external_pdr; /* struct pdr_ext */
+ PTR external_sym; /* struct sym_ext */
+ PTR external_opt; /* struct opt_ext */
+ union aux_ext *external_aux;
+ char *ss;
+ char *ssext;
+ PTR external_fdr; /* struct fdr_ext */
+ PTR external_rfd; /* struct rfd_ext */
+ PTR external_ext; /* struct ext_ext */
+
+ /* The swapped FDR information. */
+ FDR *fdr;
+
+ /* The FDR index. This is set for an input BFD to a link so that
+ the external symbols can set their FDR index correctly. */
+ unsigned int ifdbase;
+
+ /* The canonical BFD symbols. */
+ struct ecoff_symbol_struct *canonical_symbols;
+
+} ecoff_data_type;
+
+/* Each canonical asymbol really looks like this. */
+
+typedef struct ecoff_symbol_struct
+{
+ /* The actual symbol which the rest of BFD works with */
+ asymbol symbol;
+
+ /* The fdr for this symbol. */
+ FDR *fdr;
+
+ /* true if this is a local symbol rather than an external one. */
+ boolean local;
+
+ /* A pointer to the unswapped hidden information for this symbol.
+ This is either a struct sym_ext or a struct ext_ext, depending on
+ the value of the local field above. */
+ PTR native;
+} ecoff_symbol_type;
+
+/* We take the address of the first element of a asymbol to ensure that the
+ macro is only ever applied to an asymbol. */
+#define ecoffsymbol(asymbol) ((ecoff_symbol_type *) (&((asymbol)->the_bfd)))
+
+/* This is a hack borrowed from coffcode.h; we need to save the index
+ of an external symbol when we write it out so that can set the
+ symbol index correctly when we write out the relocs. */
+#define ecoff_get_sym_index(symbol) ((unsigned long) (symbol)->udata)
+#define ecoff_set_sym_index(symbol, idx) ((symbol)->udata = (PTR) (idx))
+
+/* Make an ECOFF object. */
+extern boolean ecoff_mkobject PARAMS ((bfd *));
+
+/* Read in the ECOFF symbolic debugging information. */
+extern boolean ecoff_slurp_symbolic_info PARAMS ((bfd *));
+
+/* Generic ECOFF BFD backend vectors. */
+extern asymbol *ecoff_make_empty_symbol PARAMS ((bfd *abfd));
+extern unsigned int ecoff_get_symtab_upper_bound PARAMS ((bfd *abfd));
+extern unsigned int ecoff_get_symtab PARAMS ((bfd *abfd,
+ asymbol **alocation));
+extern void ecoff_get_symbol_info PARAMS ((bfd *abfd,
+ asymbol *symbol,
+ symbol_info *ret));
+extern void ecoff_print_symbol PARAMS ((bfd *abfd, PTR filep,
+ asymbol *symbol,
+ bfd_print_symbol_type how));
+extern unsigned int ecoff_canonicalize_reloc PARAMS ((bfd *abfd,
+ asection *section,
+ arelent **relptr,
+ asymbol **symbols));
+extern boolean ecoff_find_nearest_line PARAMS ((bfd *abfd,
+ asection *section,
+ asymbol **symbols,
+ bfd_vma offset,
+ CONST char **filename_ptr,
+ CONST char **fnname_ptr,
+ unsigned int *retline_ptr));
+extern boolean ecoff_bfd_seclet_link PARAMS ((bfd *abfd, PTR data,
+ boolean relocateable));
+extern boolean ecoff_set_arch_mach PARAMS ((bfd *abfd,
+ enum bfd_architecture arch,
+ unsigned long machine));
+extern int ecoff_sizeof_headers PARAMS ((bfd *abfd, boolean reloc));
+extern boolean ecoff_set_section_contents PARAMS ((bfd *abfd,
+ asection *section,
+ PTR location,
+ file_ptr offset,
+ bfd_size_type count));
+extern boolean ecoff_get_section_contents PARAMS ((bfd *abfd,
+ asection *section,
+ PTR location,
+ file_ptr offset,
+ bfd_size_type count));
+extern boolean ecoff_write_object_contents PARAMS ((bfd *abfd));
+extern boolean ecoff_slurp_armap PARAMS ((bfd *abfd));
+extern boolean ecoff_write_armap PARAMS ((bfd *abfd, unsigned int elength,
+ struct orl *map,
+ unsigned int orl_count,
+ int stridx));
+#define ecoff_slurp_extended_name_table _bfd_slurp_extended_name_table
+extern bfd_target *ecoff_archive_p PARAMS ((bfd *abfd));
+#define ecoff_get_lineno \
+ ((alent *(*) PARAMS ((bfd *, asymbol *))) bfd_nullvoidptr)
+#define ecoff_truncate_arname bfd_dont_truncate_arname
+#define ecoff_openr_next_archived_file bfd_generic_openr_next_archived_file
+#define ecoff_generic_stat_arch_elt bfd_generic_stat_arch_elt
+#define ecoff_get_reloc_upper_bound coff_get_reloc_upper_bound
+#define ecoff_close_and_cleanup bfd_generic_close_and_cleanup
+#define ecoff_bfd_debug_info_start bfd_void
+#define ecoff_bfd_debug_info_end bfd_void
+#define ecoff_bfd_debug_info_accumulate \
+ ((void (*) PARAMS ((bfd *, struct sec *))) bfd_void)
+#define ecoff_bfd_get_relocated_section_contents \
+ bfd_generic_get_relocated_section_contents
+#define ecoff_bfd_relax_section bfd_generic_relax_section
+#define ecoff_bfd_make_debug_symbol \
+ ((asymbol *(*) PARAMS ((bfd *, void *, unsigned long))) bfd_nullvoidptr)
+
+/* Hook functions for the generic COFF section reading code. */
+extern PTR ecoff_mkobject_hook PARAMS ((bfd *, PTR filehdr, PTR aouthdr));
+extern asection *ecoff_make_section_hook PARAMS ((bfd *abfd, char *name));
+extern boolean ecoff_new_section_hook PARAMS ((bfd *abfd,
+ asection *section));
+#define ecoff_set_alignment_hook \
+ ((void (*) PARAMS ((bfd *, asection *, PTR))) bfd_void)
+extern boolean ecoff_set_arch_mach_hook PARAMS ((bfd *abfd, PTR filehdr));
+extern long ecoff_sec_to_styp_flags PARAMS ((CONST char *name,
+ flagword flags));
+extern flagword ecoff_styp_to_sec_flags PARAMS ((bfd *abfd, PTR hdr));
+extern boolean ecoff_slurp_symbol_table PARAMS ((bfd *abfd));
diff --git a/gnu/usr.bin/gdb/bfd/libelf.h b/gnu/usr.bin/gdb/bfd/libelf.h
new file mode 100644
index 0000000..187c51a
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/libelf.h
@@ -0,0 +1,249 @@
+/* BFD back-end data structures for ELF files.
+ Copyright (C) 1992, 1993 Free Software Foundation, Inc.
+ Written by Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _LIBELF_H_
+#define _LIBELF_H_ 1
+
+#include "elf/common.h"
+#include "elf/internal.h"
+#include "elf/external.h"
+
+/* If size isn't specified as 64 or 32, NAME macro should fail. */
+#ifndef NAME
+#if ARCH_SIZE==64
+#define NAME(x,y) CAT4(x,64,_,y)
+#endif
+#if ARCH_SIZE==32
+#define NAME(x,y) CAT4(x,32,_,y)
+#endif
+#endif
+
+#ifndef NAME
+#define NAME(x,y) CAT4(x,NOSIZE,_,y)
+#endif
+
+#define ElfNAME(X) NAME(Elf,X)
+#define elfNAME(X) NAME(elf,X)
+
+typedef struct
+{
+ asymbol symbol;
+ Elf_Internal_Sym internal_elf_sym;
+ union
+ {
+ unsigned int hppa_arg_reloc;
+ PTR any;
+ }
+ tc_data;
+} elf_symbol_type;
+
+struct elf_backend_data
+{
+ int use_rela_p;
+ int elf_64_p;
+ enum bfd_architecture arch;
+ void (*elf_info_to_howto) PARAMS ((bfd *, arelent *,
+ Elf_Internal_Rela *));
+ void (*elf_info_to_howto_rel) PARAMS ((bfd *, arelent *,
+ Elf_Internal_Rel *));
+ bfd_vma maxpagesize;
+ void (*write_relocs) PARAMS ((bfd *, asection *, PTR));
+
+ void (*elf_backend_symbol_processing) PARAMS ((bfd *, asymbol *));
+ boolean (*elf_backend_symbol_table_processing) PARAMS ((bfd *, elf_symbol_type *, int));
+ boolean (*elf_backend_section_processing) PARAMS ((bfd *, Elf32_Internal_Shdr *));
+ boolean (*elf_backend_section_from_shdr) PARAMS ((bfd *, Elf32_Internal_Shdr *, char *));
+ boolean (*elf_backend_fake_sections) PARAMS ((bfd *, Elf32_Internal_Shdr *, asection *));
+ boolean (*elf_backend_section_from_bfd_section) PARAMS ((bfd *, Elf32_Internal_Shdr *, asection *, int *));
+};
+
+struct elf_sym_extra
+{
+ int elf_sym_num; /* sym# after locals/globals are reordered */
+};
+
+typedef struct elf_sym_extra Elf_Sym_Extra;
+
+struct bfd_elf_arch_map {
+ enum bfd_architecture bfd_arch;
+ int elf_arch;
+};
+
+extern const struct bfd_elf_arch_map bfd_elf_arch_map[];
+extern const int bfd_elf_arch_map_size;
+
+struct bfd_elf_section_data {
+ Elf_Internal_Shdr this_hdr;
+ Elf_Internal_Shdr rel_hdr;
+ int this_idx, rel_idx;
+};
+#define elf_section_data(sec) ((struct bfd_elf_section_data*)sec->used_by_bfd)
+#define shdr_name(abfd,shdr) (elf_shstrtab (abfd)->tab + (shdr)->sh_name)
+
+#define get_elf_backend_data(abfd) \
+ ((struct elf_backend_data *) (abfd)->xvec->backend_data)
+
+struct strtab
+{
+ char *tab;
+ int nentries;
+ int length;
+};
+
+/* Some private data is stashed away for future use using the tdata pointer
+ in the bfd structure. */
+
+struct elf_obj_tdata
+{
+ Elf_Internal_Ehdr elf_header[1]; /* Actual data, but ref like ptr */
+ Elf_Internal_Shdr **elf_sect_ptr;
+ Elf_Internal_Phdr *phdr;
+ struct strtab *strtab_ptr;
+ int num_locals;
+ int num_globals;
+ Elf_Internal_Sym *internal_syms;
+ elf_symbol_type *symbols; /* elf_symbol_type */
+ Elf_Sym_Extra *sym_extra;
+ asymbol **section_syms; /* STT_SECTION symbols for each section */
+ int num_section_syms; /* number of section_syms allocated */
+ Elf_Internal_Shdr symtab_hdr;
+ Elf_Internal_Shdr shstrtab_hdr;
+ Elf_Internal_Shdr strtab_hdr;
+ int symtab_section, shstrtab_section, strtab_section;
+ file_ptr next_file_pos;
+ void *prstatus; /* The raw /proc prstatus structure */
+ void *prpsinfo; /* The raw /proc prpsinfo structure */
+ bfd_vma gp; /* The gp value (MIPS only, for now) */
+ int gp_size; /* The gp size (MIPS only, for now) */
+};
+
+#define elf_tdata(bfd) ((bfd) -> tdata.elf_obj_data)
+#define elf_elfheader(bfd) (elf_tdata(bfd) -> elf_header)
+#define elf_elfsections(bfd) (elf_tdata(bfd) -> elf_sect_ptr)
+#define elf_shstrtab(bfd) (elf_tdata(bfd) -> strtab_ptr)
+#define elf_onesymtab(bfd) (elf_tdata(bfd) -> symtab_section)
+#define elf_num_locals(bfd) (elf_tdata(bfd) -> num_locals)
+#define elf_num_globals(bfd) (elf_tdata(bfd) -> num_globals)
+#define elf_sym_extra(bfd) (elf_tdata(bfd) -> sym_extra)
+#define elf_section_syms(bfd) (elf_tdata(bfd) -> section_syms)
+#define elf_num_section_syms(bfd) (elf_tdata(bfd) -> num_section_syms)
+#define core_prpsinfo(bfd) (elf_tdata(bfd) -> prpsinfo)
+#define core_prstatus(bfd) (elf_tdata(bfd) -> prstatus)
+#define obj_symbols(bfd) (elf_tdata(bfd) -> symbols)
+#define obj_internal_syms(bfd) (elf_tdata(bfd) -> internal_syms)
+#define elf_gp(bfd) (elf_tdata(bfd) -> gp)
+#define elf_gp_size(bfd) (elf_tdata(bfd) -> gp_size)
+
+extern char * elf_string_from_elf_section PARAMS ((bfd *, unsigned, unsigned));
+extern char * elf_get_str_section PARAMS ((bfd *, unsigned));
+
+#define bfd_elf32_mkobject bfd_elf_mkobject
+#define bfd_elf64_mkobject bfd_elf_mkobject
+#define elf_mkobject bfd_elf_mkobject
+
+extern unsigned long bfd_elf_hash PARAMS ((CONST unsigned char *));
+
+extern bfd_reloc_status_type bfd_elf_generic_reloc PARAMS ((bfd *,
+ arelent *,
+ asymbol *,
+ PTR,
+ asection *,
+ bfd *));
+extern boolean bfd_elf_mkobject PARAMS ((bfd *));
+extern Elf_Internal_Shdr *bfd_elf_find_section PARAMS ((bfd *, char *));
+
+extern boolean bfd_elf32_write_object_contents PARAMS ((bfd *));
+extern boolean bfd_elf64_write_object_contents PARAMS ((bfd *));
+
+extern bfd_target *bfd_elf32_object_p PARAMS ((bfd *));
+extern bfd_target *bfd_elf32_core_file_p PARAMS ((bfd *));
+extern char *bfd_elf32_core_file_failing_command PARAMS ((bfd *));
+extern int bfd_elf32_core_file_failing_signal PARAMS ((bfd *));
+extern boolean bfd_elf32_core_file_matches_executable_p PARAMS ((bfd *,
+ bfd *));
+extern boolean bfd_elf32_set_section_contents PARAMS ((bfd *, sec_ptr, PTR,
+ file_ptr,
+ bfd_size_type));
+
+extern unsigned int bfd_elf32_get_symtab_upper_bound PARAMS ((bfd *));
+extern unsigned int bfd_elf32_get_symtab PARAMS ((bfd *, asymbol **));
+extern unsigned int bfd_elf32_get_reloc_upper_bound PARAMS ((bfd *, sec_ptr));
+extern unsigned int bfd_elf32_canonicalize_reloc PARAMS ((bfd *, sec_ptr,
+ arelent **,
+ asymbol **));
+extern asymbol *bfd_elf32_make_empty_symbol PARAMS ((bfd *));
+extern void bfd_elf32_print_symbol PARAMS ((bfd *, PTR, asymbol *,
+ bfd_print_symbol_type));
+extern void bfd_elf32_get_symbol_info PARAMS ((bfd *, asymbol *,
+ symbol_info *));
+extern alent *bfd_elf32_get_lineno PARAMS ((bfd *, asymbol *));
+extern boolean bfd_elf32_set_arch_mach PARAMS ((bfd *, enum bfd_architecture,
+ unsigned long));
+extern boolean bfd_elf32_find_nearest_line PARAMS ((bfd *, asection *,
+ asymbol **,
+ bfd_vma, CONST char **,
+ CONST char **,
+ unsigned int *));
+extern int bfd_elf32_sizeof_headers PARAMS ((bfd *, boolean));
+extern void bfd_elf32__write_relocs PARAMS ((bfd *, asection *, PTR));
+extern boolean bfd_elf32_new_section_hook PARAMS ((bfd *, asection *));
+
+/* If the target doesn't have reloc handling written yet: */
+extern void bfd_elf32_no_info_to_howto PARAMS ((bfd *, arelent *,
+ Elf32_Internal_Rela *));
+
+extern bfd_target *bfd_elf64_object_p PARAMS ((bfd *));
+extern bfd_target *bfd_elf64_core_file_p PARAMS ((bfd *));
+extern char *bfd_elf64_core_file_failing_command PARAMS ((bfd *));
+extern int bfd_elf64_core_file_failing_signal PARAMS ((bfd *));
+extern boolean bfd_elf64_core_file_matches_executable_p PARAMS ((bfd *,
+ bfd *));
+extern boolean bfd_elf64_set_section_contents PARAMS ((bfd *, sec_ptr, PTR,
+ file_ptr,
+ bfd_size_type));
+
+extern unsigned int bfd_elf64_get_symtab_upper_bound PARAMS ((bfd *));
+extern unsigned int bfd_elf64_get_symtab PARAMS ((bfd *, asymbol **));
+extern unsigned int bfd_elf64_get_reloc_upper_bound PARAMS ((bfd *, sec_ptr));
+extern unsigned int bfd_elf64_canonicalize_reloc PARAMS ((bfd *, sec_ptr,
+ arelent **,
+ asymbol **));
+extern asymbol *bfd_elf64_make_empty_symbol PARAMS ((bfd *));
+extern void bfd_elf64_print_symbol PARAMS ((bfd *, PTR, asymbol *,
+ bfd_print_symbol_type));
+extern void bfd_elf64_get_symbol_info PARAMS ((bfd *, asymbol *,
+ symbol_info *));
+extern alent *bfd_elf64_get_lineno PARAMS ((bfd *, asymbol *));
+extern boolean bfd_elf64_set_arch_mach PARAMS ((bfd *, enum bfd_architecture,
+ unsigned long));
+extern boolean bfd_elf64_find_nearest_line PARAMS ((bfd *, asection *,
+ asymbol **,
+ bfd_vma, CONST char **,
+ CONST char **,
+ unsigned int *));
+extern int bfd_elf64_sizeof_headers PARAMS ((bfd *, boolean));
+extern void bfd_elf64__write_relocs PARAMS ((bfd *, asection *, PTR));
+extern boolean bfd_elf64_new_section_hook PARAMS ((bfd *, asection *));
+
+/* If the target doesn't have reloc handling written yet: */
+extern void bfd_elf64_no_info_to_howto PARAMS ((bfd *, arelent *,
+ Elf64_Internal_Rela *));
+
+#endif /* _LIBELF_H_ */
diff --git a/gnu/usr.bin/gdb/bfd/opncls.c b/gnu/usr.bin/gdb/bfd/opncls.c
new file mode 100644
index 0000000..42858c2
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/opncls.c
@@ -0,0 +1,534 @@
+/* opncls.c -- open and close a BFD.
+ Copyright (C) 1990-1991 Free Software Foundation, Inc.
+ Written by Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "obstack.h"
+extern void bfd_cache_init PARAMS ((bfd *));
+FILE *bfd_open_file PARAMS ((bfd *));
+
+/* fdopen is a loser -- we should use stdio exclusively. Unfortunately
+ if we do that we can't use fcntl. */
+
+
+#define obstack_chunk_alloc bfd_xmalloc_by_size_t
+#define obstack_chunk_free free
+
+/* Return a new BFD. All BFD's are allocated through this routine. */
+
+bfd *
+new_bfd PARAMS ((void))
+{
+ bfd *nbfd;
+
+ nbfd = (bfd *)zalloc (sizeof (bfd));
+ if (!nbfd)
+ return 0;
+
+ bfd_check_init();
+ obstack_begin((PTR)&nbfd->memory, 128);
+
+ nbfd->arch_info = &bfd_default_arch_struct;
+
+ nbfd->direction = no_direction;
+ nbfd->iostream = NULL;
+ nbfd->where = 0;
+ nbfd->sections = (asection *)NULL;
+ nbfd->format = bfd_unknown;
+ nbfd->my_archive = (bfd *)NULL;
+ nbfd->origin = 0;
+ nbfd->opened_once = false;
+ nbfd->output_has_begun = false;
+ nbfd->section_count = 0;
+ nbfd->usrdata = (PTR)NULL;
+ nbfd->cacheable = false;
+ nbfd->flags = NO_FLAGS;
+ nbfd->mtime_set = false;
+
+ return nbfd;
+}
+
+/* Allocate a new BFD as a member of archive OBFD. */
+
+bfd *
+new_bfd_contained_in (obfd)
+ bfd *obfd;
+{
+ bfd *nbfd;
+
+ nbfd = new_bfd();
+ nbfd->xvec = obfd->xvec;
+ nbfd->my_archive = obfd;
+ nbfd->direction = read_direction;
+ nbfd->target_defaulted = obfd->target_defaulted;
+ return nbfd;
+}
+
+/*
+SECTION
+ Opening and Closing BFDs
+
+*/
+
+/*
+FUNCTION
+ bfd_openr
+
+SYNOPSIS
+ bfd *bfd_openr(CONST char *filename, CONST char*target);
+
+DESCRIPTION
+ This function opens the file supplied (using <<fopen>>) with the target
+ supplied, it returns a pointer to the created BFD.
+
+ If NULL is returned then an error has occured. Possible errors
+ are <<no_memory>>, <<invalid_target>> or <<system_call>> error.
+*/
+
+bfd *
+DEFUN(bfd_openr, (filename, target),
+ CONST char *filename AND
+ CONST char *target)
+{
+ bfd *nbfd;
+ bfd_target *target_vec;
+
+ nbfd = new_bfd();
+ if (nbfd == NULL) {
+ bfd_error = no_memory;
+ return NULL;
+ }
+
+ target_vec = bfd_find_target (target, nbfd);
+ if (target_vec == NULL) {
+ bfd_error = invalid_target;
+ return NULL;
+ }
+
+ nbfd->filename = filename;
+ nbfd->direction = read_direction;
+
+ if (bfd_open_file (nbfd) == NULL) {
+ bfd_error = system_call_error; /* File didn't exist, or some such */
+ bfd_release(nbfd,0);
+ return NULL;
+ }
+ return nbfd;
+}
+
+
+/* Don't try to `optimize' this function:
+
+ o - We lock using stack space so that interrupting the locking
+ won't cause a storage leak.
+ o - We open the file stream last, since we don't want to have to
+ close it if anything goes wrong. Closing the stream means closing
+ the file descriptor too, even though we didn't open it.
+ */
+/*
+FUNCTION
+ bfd_fdopenr
+
+SYNOPSIS
+ bfd *bfd_fdopenr(CONST char *filename, CONST char *target, int fd);
+
+DESCRIPTION
+ bfd_fdopenr is to bfd_fopenr much like fdopen is to fopen.
+ It opens a BFD on a file already described by the @var{fd}
+ supplied.
+
+ When the file is later bfd_closed, the file descriptor will be closed.
+
+ If the caller desires that this file descriptor be cached by BFD
+ (opened as needed, closed as needed to free descriptors for
+ other opens), with the supplied @var{fd} used as an initial
+ file descriptor (but subject to closure at any time), set
+ bfd->cacheable nonzero in the returned BFD. The default is to
+ assume no cacheing; the file descriptor will remain open until
+ bfd_close, and will not be affected by BFD operations on other
+ files.
+
+ Possible errors are no_memory, invalid_target and system_call
+ error.
+*/
+
+bfd *
+DEFUN(bfd_fdopenr,(filename, target, fd),
+ CONST char *filename AND
+ CONST char *target AND
+ int fd)
+{
+ bfd *nbfd;
+ bfd_target *target_vec;
+ int fdflags;
+
+ bfd_error = system_call_error;
+
+#ifdef NO_FCNTL
+ fdflags = O_RDWR; /* Assume full access */
+#else
+ fdflags = fcntl (fd, F_GETFL, NULL);
+#endif
+ if (fdflags == -1) return NULL;
+
+ nbfd = new_bfd();
+
+ if (nbfd == NULL) {
+ bfd_error = no_memory;
+ return NULL;
+ }
+
+ target_vec = bfd_find_target (target, nbfd);
+ if (target_vec == NULL) {
+ bfd_error = invalid_target;
+ return NULL;
+ }
+#if defined(VMS) || defined(__GO32__)
+ nbfd->iostream = (char *)fopen(filename, FOPEN_RB);
+#else
+ /* (O_ACCMODE) parens are to avoid Ultrix header file bug */
+ switch (fdflags & (O_ACCMODE)) {
+ case O_RDONLY: nbfd->iostream = (char *) fdopen (fd, FOPEN_RB); break;
+ case O_WRONLY: nbfd->iostream = (char *) fdopen (fd, FOPEN_RUB); break;
+ case O_RDWR: nbfd->iostream = (char *) fdopen (fd, FOPEN_RUB); break;
+ default: abort ();
+ }
+#endif
+ if (nbfd->iostream == NULL) {
+ (void) obstack_free (&nbfd->memory, (PTR)0);
+ return NULL;
+ }
+
+ /* OK, put everything where it belongs */
+
+ nbfd->filename = filename;
+
+ /* As a special case we allow a FD open for read/write to
+ be written through, although doing so requires that we end
+ the previous clause with a preposition. */
+ /* (O_ACCMODE) parens are to avoid Ultrix header file bug */
+ switch (fdflags & (O_ACCMODE)) {
+ case O_RDONLY: nbfd->direction = read_direction; break;
+ case O_WRONLY: nbfd->direction = write_direction; break;
+ case O_RDWR: nbfd->direction = both_direction; break;
+ default: abort ();
+ }
+
+ bfd_cache_init (nbfd);
+
+ return nbfd;
+}
+
+/** bfd_openw -- open for writing.
+ Returns a pointer to a freshly-allocated BFD on success, or NULL.
+
+ See comment by bfd_fdopenr before you try to modify this function. */
+
+/*
+FUNCTION
+ bfd_openw
+
+SYNOPSIS
+ bfd *bfd_openw(CONST char *filename, CONST char *target);
+
+DESCRIPTION
+ Creates a BFD, associated with file @var{filename}, using the
+ file format @var{target}, and returns a pointer to it.
+
+ Possible errors are system_call_error, no_memory,
+ invalid_target.
+*/
+
+bfd *
+DEFUN(bfd_openw,(filename, target),
+ CONST char *filename AND
+ CONST char *target)
+{
+ bfd *nbfd;
+ bfd_target *target_vec;
+
+ bfd_error = system_call_error;
+
+ /* nbfd has to point to head of malloc'ed block so that bfd_close may
+ reclaim it correctly. */
+
+ nbfd = new_bfd();
+ if (nbfd == NULL) {
+ bfd_error = no_memory;
+ return NULL;
+ }
+
+ target_vec = bfd_find_target (target, nbfd);
+ if (target_vec == NULL) return NULL;
+
+ nbfd->filename = filename;
+ nbfd->direction = write_direction;
+
+ if (bfd_open_file (nbfd) == NULL) {
+ bfd_error = system_call_error; /* File not writeable, etc */
+ (void) obstack_free (&nbfd->memory, (PTR)0);
+ return NULL;
+ }
+ return nbfd;
+}
+
+/*
+
+FUNCTION
+ bfd_close
+
+SYNOPSIS
+ boolean bfd_close(bfd *);
+
+DESCRIPTION
+
+ This function closes a BFD. If the BFD was open for writing,
+ then pending operations are completed and the file written out
+ and closed. If the created file is executable, then
+ <<chmod>> is called to mark it as such.
+
+ All memory attached to the BFD's obstacks is released.
+
+ The file descriptor associated with the BFD is closed (even
+ if it was passed in to BFD by bfd_fdopenr).
+
+RETURNS
+ <<true>> is returned if all is ok, otherwise <<false>>.
+*/
+
+
+boolean
+DEFUN(bfd_close,(abfd),
+ bfd *abfd)
+{
+ boolean ret;
+
+ if (!bfd_read_p(abfd))
+ if (BFD_SEND_FMT (abfd, _bfd_write_contents, (abfd)) != true)
+ return false;
+
+ if (BFD_SEND (abfd, _close_and_cleanup, (abfd)) != true) return false;
+
+ ret = bfd_cache_close(abfd);
+
+ /* If the file was open for writing and is now executable,
+ make it so */
+ if (ret == true
+ && abfd->direction == write_direction
+ && abfd->flags & EXEC_P) {
+ struct stat buf;
+ stat(abfd->filename, &buf);
+#ifndef S_IXUSR
+#define S_IXUSR 0100 /* Execute by owner. */
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP 0010 /* Execute by group. */
+#endif
+#ifndef S_IXOTH
+#define S_IXOTH 0001 /* Execute by others. */
+#endif
+
+ chmod(abfd->filename, 0777 & (buf.st_mode | S_IXUSR | S_IXGRP | S_IXOTH));
+ }
+ (void) obstack_free (&abfd->memory, (PTR)0);
+ (void) free(abfd);
+ return ret;
+}
+
+/*
+FUNCTION
+ bfd_close_all_done
+
+SYNOPSIS
+ boolean bfd_close_all_done(bfd *);
+
+DESCRIPTION
+ This function closes a BFD. It differs from <<bfd_close>>
+ since it does not complete any pending operations. This
+ routine would be used if the application had just used BFD for
+ swapping and didn't want to use any of the writing code.
+
+ If the created file is executable, then <<chmod>> is called
+ to mark it as such.
+
+ All memory attached to the BFD's obstacks is released.
+
+RETURNS
+ <<true>> is returned if all is ok, otherwise <<false>>.
+
+*/
+
+boolean
+DEFUN(bfd_close_all_done,(abfd),
+ bfd *abfd)
+{
+ boolean ret;
+
+ ret = bfd_cache_close(abfd);
+
+ /* If the file was open for writing and is now executable,
+ make it so */
+ if (ret == true
+ && abfd->direction == write_direction
+ && abfd->flags & EXEC_P) {
+ struct stat buf;
+ stat(abfd->filename, &buf);
+#ifndef S_IXUSR
+#define S_IXUSR 0100 /* Execute by owner. */
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP 0010 /* Execute by group. */
+#endif
+#ifndef S_IXOTH
+#define S_IXOTH 0001 /* Execute by others. */
+#endif
+
+ chmod(abfd->filename, 0x777 &(buf.st_mode | S_IXUSR | S_IXGRP | S_IXOTH));
+ }
+ (void) obstack_free (&abfd->memory, (PTR)0);
+ (void) free(abfd);
+ return ret;
+}
+
+
+/*
+FUNCTION
+ bfd_alloc_size
+
+SYNOPSIS
+ bfd_size_type bfd_alloc_size(bfd *abfd);
+
+DESCRIPTION
+ Return the number of bytes in the obstacks connected to the
+ supplied BFD.
+
+*/
+
+bfd_size_type
+DEFUN(bfd_alloc_size,(abfd),
+ bfd *abfd)
+{
+ struct _obstack_chunk *chunk = abfd->memory.chunk;
+ size_t size = 0;
+ while (chunk) {
+ size += chunk->limit - &(chunk->contents[0]);
+ chunk = chunk->prev;
+ }
+ return size;
+}
+
+
+
+/*
+FUNCTION
+ bfd_create
+
+SYNOPSIS
+ bfd *bfd_create(CONST char *filename, bfd *templ);
+
+DESCRIPTION
+ This routine creates a new BFD in the manner of
+ <<bfd_openw>>, but without opening a file. The new BFD
+ takes the target from the target used by @var{template}. The
+ format is always set to <<bfd_object>>.
+
+*/
+
+bfd *
+DEFUN(bfd_create,(filename, templ),
+ CONST char *filename AND
+ bfd *templ)
+{
+ bfd *nbfd = new_bfd();
+ if (nbfd == (bfd *)NULL) {
+ bfd_error = no_memory;
+ return (bfd *)NULL;
+ }
+ nbfd->filename = filename;
+ if(templ) {
+ nbfd->xvec = templ->xvec;
+ }
+ nbfd->direction = no_direction;
+ bfd_set_format(nbfd, bfd_object);
+ return nbfd;
+}
+
+/*
+INTERNAL_FUNCTION
+ bfd_alloc_by_size_t
+
+SYNOPSIS
+ PTR bfd_alloc_by_size_t(bfd *abfd, size_t wanted);
+
+DESCRIPTION
+ This function allocates a block of memory in the obstack
+ attatched to <<abfd>> and returns a pointer to it.
+*/
+
+
+PTR
+DEFUN(bfd_alloc_by_size_t,(abfd, size),
+ bfd *abfd AND
+ size_t size)
+{
+ PTR res = obstack_alloc(&(abfd->memory), size);
+ return res;
+}
+
+DEFUN(void bfd_alloc_grow,(abfd, ptr, size),
+ bfd *abfd AND
+ PTR ptr AND
+ size_t size)
+{
+ (void) obstack_grow(&(abfd->memory), ptr, size);
+}
+DEFUN(PTR bfd_alloc_finish,(abfd),
+ bfd *abfd)
+{
+ return obstack_finish(&(abfd->memory));
+}
+
+DEFUN(PTR bfd_alloc, (abfd, size),
+ bfd *abfd AND
+ size_t size)
+{
+ return bfd_alloc_by_size_t(abfd, (size_t)size);
+}
+
+DEFUN(PTR bfd_zalloc,(abfd, size),
+ bfd *abfd AND
+ size_t size)
+{
+ PTR res;
+ res = bfd_alloc(abfd, size);
+ memset(res, 0, (size_t)size);
+ return res;
+}
+
+DEFUN(PTR bfd_realloc,(abfd, old, size),
+ bfd *abfd AND
+ PTR old AND
+ size_t size)
+{
+ PTR res = bfd_alloc(abfd, size);
+ memcpy(res, old, (size_t)size);
+ return res;
+}
diff --git a/gnu/usr.bin/gdb/bfd/reloc.c b/gnu/usr.bin/gdb/bfd/reloc.c
new file mode 100644
index 0000000..f2dd520
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/reloc.c
@@ -0,0 +1,1225 @@
+/* BFD support for handling relocation entries.
+ Copyright (C) 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+ Written by Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+SECTION
+ Relocations
+
+ BFD maintains relocations in much the same was as it maintains
+ symbols; they are left alone until required, then read in
+ en-mass and traslated into an internal form. There is a common
+ routine <<bfd_perform_relocation>> which acts upon the
+ canonical form to do the actual fixup.
+
+ Note that relocations are maintained on a per section basis,
+ whilst symbols are maintained on a per BFD basis.
+
+ All a back end has to do to fit the BFD interface is to create
+ as many <<struct reloc_cache_entry>> as there are relocations
+ in a particular section, and fill in the right bits:
+
+@menu
+@* typedef arelent::
+@* howto manager::
+@end menu
+
+*/
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "seclet.h"
+/*
+DOCDD
+INODE
+ typedef arelent, howto manager, Relocations, Relocations
+
+SUBSECTION
+ typedef arelent
+
+ This is the structure of a relocation entry:
+
+CODE_FRAGMENT
+.
+.typedef enum bfd_reloc_status
+.{
+. {* No errors detected *}
+. bfd_reloc_ok,
+.
+. {* The relocation was performed, but there was an overflow. *}
+. bfd_reloc_overflow,
+.
+. {* The address to relocate was not within the section supplied. *}
+. bfd_reloc_outofrange,
+.
+. {* Used by special functions *}
+. bfd_reloc_continue,
+.
+. {* Unused *}
+. bfd_reloc_notsupported,
+.
+. {* Unsupported relocation size requested. *}
+. bfd_reloc_other,
+.
+. {* The symbol to relocate against was undefined. *}
+. bfd_reloc_undefined,
+.
+. {* The relocation was performed, but may not be ok - presently
+. generated only when linking i960 coff files with i960 b.out
+. symbols. *}
+. bfd_reloc_dangerous
+. }
+. bfd_reloc_status_type;
+.
+.
+.typedef struct reloc_cache_entry
+.{
+. {* A pointer into the canonical table of pointers *}
+. struct symbol_cache_entry **sym_ptr_ptr;
+.
+. {* offset in section *}
+. bfd_size_type address;
+.
+. {* addend for relocation value *}
+. bfd_vma addend;
+.
+. {* Pointer to how to perform the required relocation *}
+. CONST struct reloc_howto_struct *howto;
+.
+.} arelent;
+
+*/
+
+/*
+DESCRIPTION
+
+ Here is a description of each of the fields within a relent:
+
+ o sym_ptr_ptr
+
+ The symbol table pointer points to a pointer to the symbol
+ associated with the relocation request. This would naturally
+ be the pointer into the table returned by the back end's
+ get_symtab action. @xref{Symbols}. The symbol is referenced
+ through a pointer to a pointer so that tools like the linker
+ can fix up all the symbols of the same name by modifying only
+ one pointer. The relocation routine looks in the symbol and
+ uses the base of the section the symbol is attached to and the
+ value of the symbol as the initial relocation offset. If the
+ symbol pointer is zero, then the section provided is looked up.
+
+ o address
+
+ The address field gives the offset in bytes from the base of
+ the section data which owns the relocation record to the first
+ byte of relocatable information. The actual data relocated
+ will be relative to this point - for example, a relocation
+ type which modifies the bottom two bytes of a four byte word
+ would not touch the first byte pointed to in a big endian
+ world.
+
+ o addend
+
+ The addend is a value provided by the back end to be added (!)
+ to the relocation offset. Its interpretation is dependent upon
+ the howto. For example, on the 68k the code:
+
+
+| char foo[];
+| main()
+| {
+| return foo[0x12345678];
+| }
+
+ Could be compiled into:
+
+| linkw fp,#-4
+| moveb @@#12345678,d0
+| extbl d0
+| unlk fp
+| rts
+
+
+ This could create a reloc pointing to foo, but leave the
+ offset in the data (something like)
+
+
+|RELOCATION RECORDS FOR [.text]:
+|offset type value
+|00000006 32 _foo
+|
+|00000000 4e56 fffc ; linkw fp,#-4
+|00000004 1039 1234 5678 ; moveb @@#12345678,d0
+|0000000a 49c0 ; extbl d0
+|0000000c 4e5e ; unlk fp
+|0000000e 4e75 ; rts
+
+
+ Using coff and an 88k, some instructions don't have enough
+ space in them to represent the full address range, and
+ pointers have to be loaded in two parts. So you'd get something like:
+
+
+| or.u r13,r0,hi16(_foo+0x12345678)
+| ld.b r2,r13,lo16(_foo+0x12345678)
+| jmp r1
+
+
+ This should create two relocs, both pointing to _foo, and with
+ 0x12340000 in their addend field. The data would consist of:
+
+
+|RELOCATION RECORDS FOR [.text]:
+|offset type value
+|00000002 HVRT16 _foo+0x12340000
+|00000006 LVRT16 _foo+0x12340000
+
+|00000000 5da05678 ; or.u r13,r0,0x5678
+|00000004 1c4d5678 ; ld.b r2,r13,0x5678
+|00000008 f400c001 ; jmp r1
+
+
+ The relocation routine digs out the value from the data, adds
+ it to the addend to get the original offset and then adds the
+ value of _foo. Note that all 32 bits have to be kept around
+ somewhere, to cope with carry from bit 15 to bit 16.
+
+ One further example is the sparc and the a.out format. The
+ sparc has a similar problem to the 88k, in that some
+ instructions don't have room for an entire offset, but on the
+ sparc the parts are created odd sized lumps. The designers of
+ the a.out format chose not to use the data within the section
+ for storing part of the offset; all the offset is kept within
+ the reloc. Any thing in the data should be ignored.
+
+| save %sp,-112,%sp
+| sethi %hi(_foo+0x12345678),%g2
+| ldsb [%g2+%lo(_foo+0x12345678)],%i0
+| ret
+| restore
+
+ Both relocs contains a pointer to foo, and the offsets would
+ contain junk.
+
+
+|RELOCATION RECORDS FOR [.text]:
+|offset type value
+|00000004 HI22 _foo+0x12345678
+|00000008 LO10 _foo+0x12345678
+
+|00000000 9de3bf90 ; save %sp,-112,%sp
+|00000004 05000000 ; sethi %hi(_foo+0),%g2
+|00000008 f048a000 ; ldsb [%g2+%lo(_foo+0)],%i0
+|0000000c 81c7e008 ; ret
+|00000010 81e80000 ; restore
+
+
+ o howto
+
+ The howto field can be imagined as a
+ relocation instruction. It is a pointer to a struct which
+ contains information on what to do with all the other
+ information in the reloc record and data section. A back end
+ would normally have a relocation instruction set and turn
+ relocations into pointers to the correct structure on input -
+ but it would be possible to create each howto field on demand.
+
+*/
+
+/*
+SUBSUBSECTION
+ <<enum complain_overflow>>
+
+ Indicates what sort of overflow checking should be done when
+ performing a relocation.
+
+CODE_FRAGMENT
+.
+.enum complain_overflow
+.{
+. {* Do not complain on overflow. *}
+. complain_overflow_dont,
+.
+. {* Complain if the bitfield overflows, whether it is considered
+. as signed or unsigned. *}
+. complain_overflow_bitfield,
+.
+. {* Complain if the value overflows when considered as signed
+. number. *}
+. complain_overflow_signed,
+.
+. {* Complain if the value overflows when considered as an
+. unsigned number. *}
+. complain_overflow_unsigned
+.};
+
+*/
+
+/*
+SUBSUBSECTION
+ <<reloc_howto_type>>
+
+ The <<reloc_howto_type>> is a structure which contains all the
+ information that BFD needs to know to tie up a back end's data.
+
+CODE_FRAGMENT
+.struct symbol_cache_entry; {* Forward declaration *}
+.
+.typedef CONST struct reloc_howto_struct
+.{
+. {* The type field has mainly a documetary use - the back end can
+. to what it wants with it, though the normally the back end's
+. external idea of what a reloc number would be would be stored
+. in this field. For example, the a PC relative word relocation
+. in a coff environment would have the type 023 - because that's
+. what the outside world calls a R_PCRWORD reloc. *}
+. unsigned int type;
+.
+. {* The value the final relocation is shifted right by. This drops
+. unwanted data from the relocation. *}
+. unsigned int rightshift;
+.
+. {* The size of the item to be relocated. This is *not* a
+. power-of-two measure.
+. 0 : one byte
+. 1 : two bytes
+. 2 : four bytes
+. 3 : nothing done (unless special_function is nonzero)
+. 4 : eight bytes
+. -2 : two bytes, result should be subtracted from the
+. data instead of added
+. There is currently no trivial way to extract a "number of
+. bytes" from a howto pointer. *}
+. int size;
+.
+. {* The number of bits in the item to be relocated. This is used
+. when doing overflow checking. *}
+. unsigned int bitsize;
+.
+. {* Notes that the relocation is relative to the location in the
+. data section of the addend. The relocation function will
+. subtract from the relocation value the address of the location
+. being relocated. *}
+. boolean pc_relative;
+.
+. {* The bit position of the reloc value in the destination.
+. The relocated value is left shifted by this amount. *}
+. unsigned int bitpos;
+.
+. {* What type of overflow error should be checked for when
+. relocating. *}
+. enum complain_overflow complain_on_overflow;
+.
+. {* If this field is non null, then the supplied function is
+. called rather than the normal function. This allows really
+. strange relocation methods to be accomodated (e.g., i960 callj
+. instructions). *}
+. bfd_reloc_status_type (*special_function)
+. PARAMS ((bfd *abfd,
+. arelent *reloc_entry,
+. struct symbol_cache_entry *symbol,
+. PTR data,
+. asection *input_section,
+. bfd *output_bfd));
+.
+. {* The textual name of the relocation type. *}
+. char *name;
+.
+. {* When performing a partial link, some formats must modify the
+. relocations rather than the data - this flag signals this.*}
+. boolean partial_inplace;
+.
+. {* The src_mask is used to select what parts of the read in data
+. are to be used in the relocation sum. E.g., if this was an 8 bit
+. bit of data which we read and relocated, this would be
+. 0x000000ff. When we have relocs which have an addend, such as
+. sun4 extended relocs, the value in the offset part of a
+. relocating field is garbage so we never use it. In this case
+. the mask would be 0x00000000. *}
+. bfd_vma src_mask;
+.
+. {* The dst_mask is what parts of the instruction are replaced
+. into the instruction. In most cases src_mask == dst_mask,
+. except in the above special case, where dst_mask would be
+. 0x000000ff, and src_mask would be 0x00000000. *}
+. bfd_vma dst_mask;
+.
+. {* When some formats create PC relative instructions, they leave
+. the value of the pc of the place being relocated in the offset
+. slot of the instruction, so that a PC relative relocation can
+. be made just by adding in an ordinary offset (e.g., sun3 a.out).
+. Some formats leave the displacement part of an instruction
+. empty (e.g., m88k bcs), this flag signals the fact.*}
+. boolean pcrel_offset;
+.
+.} reloc_howto_type;
+
+*/
+
+/*
+FUNCTION
+ the HOWTO macro
+
+DESCRIPTION
+ The HOWTO define is horrible and will go away.
+
+
+.#define HOWTO(C, R,S,B, P, BI, O, SF, NAME, INPLACE, MASKSRC, MASKDST, PC) \
+. {(unsigned)C,R,S,B, P, BI, O,SF,NAME,INPLACE,MASKSRC,MASKDST,PC}
+
+DESCRIPTION
+ And will be replaced with the totally magic way. But for the
+ moment, we are compatible, so do it this way..
+
+
+.#define NEWHOWTO( FUNCTION, NAME,SIZE,REL,IN) HOWTO(0,0,SIZE,0,REL,0,complain_overflow_dont,FUNCTION, NAME,false,0,0,IN)
+.
+DESCRIPTION
+ Helper routine to turn a symbol into a relocation value.
+
+.#define HOWTO_PREPARE(relocation, symbol) \
+. { \
+. if (symbol != (asymbol *)NULL) { \
+. if (bfd_is_com_section (symbol->section)) { \
+. relocation = 0; \
+. } \
+. else { \
+. relocation = symbol->value; \
+. } \
+. } \
+.}
+
+*/
+
+/*
+TYPEDEF
+ reloc_chain
+
+DESCRIPTION
+
+ How relocs are tied together
+
+.typedef unsigned char bfd_byte;
+.
+.typedef struct relent_chain {
+. arelent relent;
+. struct relent_chain *next;
+.} arelent_chain;
+
+*/
+
+
+
+/*
+FUNCTION
+ bfd_perform_relocation
+
+SYNOPSIS
+ bfd_reloc_status_type
+ bfd_perform_relocation
+ (bfd * abfd,
+ arelent *reloc_entry,
+ PTR data,
+ asection *input_section,
+ bfd *output_bfd);
+
+DESCRIPTION
+ If an output_bfd is supplied to this function the generated
+ image will be relocatable, the relocations are copied to the
+ output file after they have been changed to reflect the new
+ state of the world. There are two ways of reflecting the
+ results of partial linkage in an output file; by modifying the
+ output data in place, and by modifying the relocation record.
+ Some native formats (e.g., basic a.out and basic coff) have no
+ way of specifying an addend in the relocation type, so the
+ addend has to go in the output data. This is no big deal
+ since in these formats the output data slot will always be big
+ enough for the addend. Complex reloc types with addends were
+ invented to solve just this problem.
+
+*/
+
+
+bfd_reloc_status_type
+DEFUN(bfd_perform_relocation,(abfd,
+ reloc_entry,
+ data,
+ input_section,
+ output_bfd),
+ bfd *abfd AND
+ arelent *reloc_entry AND
+ PTR data AND
+ asection *input_section AND
+ bfd *output_bfd)
+{
+ bfd_vma relocation;
+ bfd_reloc_status_type flag = bfd_reloc_ok;
+ bfd_size_type addr = reloc_entry->address ;
+ bfd_vma output_base = 0;
+ reloc_howto_type *howto = reloc_entry->howto;
+ asection *reloc_target_output_section ;
+
+ asymbol *symbol;
+
+ symbol = *( reloc_entry->sym_ptr_ptr);
+ if ((symbol->section == &bfd_abs_section)
+ && output_bfd != (bfd *)NULL)
+ {
+ reloc_entry->address += input_section->output_offset;
+ return bfd_reloc_ok;
+ }
+
+ /* If we are not producing relocateable output, return an error if
+ the symbol is not defined. An undefined weak symbol is
+ considered to have a value of zero (SVR4 ABI, p. 4-27). */
+ if (symbol->section == &bfd_und_section
+ && (symbol->flags & BSF_WEAK) == 0
+ && output_bfd == (bfd *) NULL)
+ flag = bfd_reloc_undefined;
+
+ /* If there is a function supplied to handle this relocation type,
+ call it. It'll return `bfd_reloc_continue' if further processing
+ can be done. */
+ if (howto->special_function)
+ {
+ bfd_reloc_status_type cont;
+ cont = howto->special_function (abfd, reloc_entry, symbol, data,
+ input_section, output_bfd);
+ if (cont != bfd_reloc_continue)
+ return cont;
+ }
+
+ /* Is the address of the relocation really within the section? */
+ if (reloc_entry->address > input_section->_cooked_size)
+ return bfd_reloc_outofrange;
+
+ /* Work out which section the relocation is targetted at and the
+ initial relocation command value. */
+
+ /* Get symbol value. (Common symbols are special.) */
+ if (bfd_is_com_section (symbol->section))
+ relocation = 0;
+ else
+ relocation = symbol->value;
+
+
+ reloc_target_output_section = symbol->section->output_section;
+
+ /* Convert input-section-relative symbol value to absolute. */
+ if (output_bfd && howto->partial_inplace==false)
+ output_base = 0;
+ else
+ output_base = reloc_target_output_section->vma;
+
+ relocation += output_base + symbol->section->output_offset;
+
+ /* Add in supplied addend. */
+ relocation += reloc_entry->addend;
+
+ /* Here the variable relocation holds the final address of the
+ symbol we are relocating against, plus any addend. */
+
+ if (howto->pc_relative == true)
+ {
+ /* This is a PC relative relocation. We want to set RELOCATION
+ to the distance between the address of the symbol and the
+ location. RELOCATION is already the address of the symbol.
+
+ We start by subtracting the address of the section containing
+ the location.
+
+ If pcrel_offset is set, we must further subtract the position
+ of the location within the section. Some targets arrange for
+ the addend to be the negative of the position of the location
+ within the section; for example, i386-aout does this. For
+ i386-aout, pcrel_offset is false. Some other targets do not
+ include the position of the location; for example, m88kbcs,
+ or ELF. For those targets, pcrel_offset is true.
+
+ If we are producing relocateable output, then we must ensure
+ that this reloc will be correctly computed when the final
+ relocation is done. If pcrel_offset is false we want to wind
+ up with the negative of the location within the section,
+ which means we must adjust the existing addend by the change
+ in the location within the section. If pcrel_offset is true
+ we do not want to adjust the existing addend at all.
+
+ FIXME: This seems logical to me, but for the case of
+ producing relocateable output it is not what the code
+ actually does. I don't want to change it, because it seems
+ far too likely that something will break. */
+
+ relocation -=
+ input_section->output_section->vma + input_section->output_offset;
+
+ if (howto->pcrel_offset == true)
+ relocation -= reloc_entry->address;
+ }
+
+ if (output_bfd!= (bfd *)NULL)
+ {
+ if ( howto->partial_inplace == false)
+ {
+ /* This is a partial relocation, and we want to apply the relocation
+ to the reloc entry rather than the raw data. Modify the reloc
+ inplace to reflect what we now know. */
+ reloc_entry->addend = relocation;
+ reloc_entry->address += input_section->output_offset;
+ return flag;
+ }
+ else
+ {
+ /* This is a partial relocation, but inplace, so modify the
+ reloc record a bit.
+
+ If we've relocated with a symbol with a section, change
+ into a ref to the section belonging to the symbol. */
+
+ reloc_entry->address += input_section->output_offset;
+
+ /* WTF?? */
+ if (abfd->xvec->flavour == bfd_target_coff_flavour)
+ {
+ relocation -= reloc_entry->addend;
+ reloc_entry->addend = 0;
+ }
+ else
+ {
+ reloc_entry->addend = relocation;
+ }
+ }
+ }
+ else
+ {
+ reloc_entry->addend = 0;
+ }
+
+ /* FIXME: This overflow checking is incomplete, because the value
+ might have overflowed before we get here. For a correct check we
+ need to compute the value in a size larger than bitsize, but we
+ can't reasonably do that for a reloc the same size as a host
+ machine word.
+ FIXME: We should also do overflow checking on the result after
+ adding in the value contained in the object file. */
+ if (howto->complain_on_overflow != complain_overflow_dont)
+ {
+ bfd_vma check;
+
+ /* Get the value that will be used for the relocation, but
+ starting at bit position zero. */
+ if (howto->rightshift > howto->bitpos)
+ check = relocation >> (howto->rightshift - howto->bitpos);
+ else
+ check = relocation << (howto->bitpos - howto->rightshift);
+ switch (howto->complain_on_overflow)
+ {
+ case complain_overflow_signed:
+ {
+ /* Assumes two's complement. */
+ bfd_signed_vma reloc_signed_max = (1 << (howto->bitsize - 1)) - 1;
+ bfd_signed_vma reloc_signed_min = ~ reloc_signed_max;
+
+ /* The above right shift is incorrect for a signed value.
+ Fix it up by forcing on the upper bits. */
+ if (howto->rightshift > howto->bitpos
+ && (bfd_signed_vma) relocation < 0)
+ check |= ((bfd_vma) -1
+ &~ ((bfd_vma) -1
+ >> (howto->rightshift - howto->bitpos)));
+ if ((bfd_signed_vma) check > reloc_signed_max
+ || (bfd_signed_vma) check < reloc_signed_min)
+ flag = bfd_reloc_overflow;
+ }
+ break;
+ case complain_overflow_unsigned:
+ {
+ /* Assumes two's complement. This expression avoids
+ overflow if howto->bitsize is the number of bits in
+ bfd_vma. */
+ bfd_vma reloc_unsigned_max =
+ (((1 << (howto->bitsize - 1)) - 1) << 1) | 1;
+
+ if ((bfd_vma) check > reloc_unsigned_max)
+ flag = bfd_reloc_overflow;
+ }
+ break;
+ case complain_overflow_bitfield:
+ {
+ /* Assumes two's complement. This expression avoids
+ overflow if howto->bitsize is the number of bits in
+ bfd_vma. */
+ bfd_vma reloc_bits = (((1 << (howto->bitsize - 1)) - 1) << 1) | 1;
+
+ if (((bfd_vma) check &~ reloc_bits) != 0
+ && ((bfd_vma) check &~ reloc_bits) != (-1 &~ reloc_bits))
+ {
+ /* The above right shift is incorrect for a signed
+ value. See if turning on the upper bits fixes the
+ overflow. */
+ if (howto->rightshift > howto->bitpos
+ && (bfd_signed_vma) relocation < 0)
+ {
+ check |= ((bfd_vma) -1
+ &~ ((bfd_vma) -1
+ >> (howto->rightshift - howto->bitpos)));
+ if (((bfd_vma) check &~ reloc_bits) != (-1 &~ reloc_bits))
+ flag = bfd_reloc_overflow;
+ }
+ else
+ flag = bfd_reloc_overflow;
+ }
+ }
+ break;
+ default:
+ abort ();
+ }
+ }
+
+ /*
+ Either we are relocating all the way, or we don't want to apply
+ the relocation to the reloc entry (probably because there isn't
+ any room in the output format to describe addends to relocs)
+ */
+ relocation >>= howto->rightshift;
+
+ /* Shift everything up to where it's going to be used */
+
+ relocation <<= howto->bitpos;
+
+ /* Wait for the day when all have the mask in them */
+
+ /* What we do:
+ i instruction to be left alone
+ o offset within instruction
+ r relocation offset to apply
+ S src mask
+ D dst mask
+ N ~dst mask
+ A part 1
+ B part 2
+ R result
+
+ Do this:
+ i i i i i o o o o o from bfd_get<size>
+ and S S S S S to get the size offset we want
+ + r r r r r r r r r r to get the final value to place
+ and D D D D D to chop to right size
+ -----------------------
+ A A A A A
+ And this:
+ ... i i i i i o o o o o from bfd_get<size>
+ and N N N N N get instruction
+ -----------------------
+ ... B B B B B
+
+ And then:
+ B B B B B
+ or A A A A A
+ -----------------------
+ R R R R R R R R R R put into bfd_put<size>
+ */
+
+#define DOIT(x) \
+ x = ( (x & ~howto->dst_mask) | (((x & howto->src_mask) + relocation) & howto->dst_mask))
+
+ switch (howto->size)
+ {
+ case 0:
+ {
+ char x = bfd_get_8(abfd, (char *)data + addr);
+ DOIT(x);
+ bfd_put_8(abfd,x, (unsigned char *) data + addr);
+ }
+ break;
+
+ case 1:
+ if (relocation)
+ {
+ short x = bfd_get_16(abfd, (bfd_byte *)data + addr);
+ DOIT(x);
+ bfd_put_16(abfd, x, (unsigned char *)data + addr);
+ }
+ break;
+ case 2:
+ if (relocation)
+ {
+ long x = bfd_get_32 (abfd, (bfd_byte *) data + addr);
+ DOIT (x);
+ bfd_put_32 (abfd, x, (bfd_byte *)data + addr);
+ }
+ break;
+ case -2:
+ {
+ long x = bfd_get_32(abfd, (bfd_byte *) data + addr);
+ relocation = -relocation;
+ DOIT(x);
+ bfd_put_32(abfd,x, (bfd_byte *)data + addr);
+ }
+ break;
+
+ case 3:
+ /* Do nothing */
+ break;
+
+ case 4:
+#ifdef BFD64
+ if (relocation)
+ {
+ bfd_vma x = bfd_get_64 (abfd, (bfd_byte *) data + addr);
+ DOIT (x);
+ bfd_put_64 (abfd, x, (bfd_byte *) data + addr);
+ }
+#else
+ abort ();
+#endif
+ break;
+ default:
+ return bfd_reloc_other;
+ }
+
+ return flag;
+}
+
+
+
+/*
+DOCDD
+INODE
+ howto manager, , typedef arelent, Relocations
+
+SECTION
+ The howto manager
+
+ When an application wants to create a relocation, but doesn't
+ know what the target machine might call it, it can find out by
+ using this bit of code.
+
+*/
+
+/*
+TYPEDEF
+ bfd_reloc_code_type
+
+DESCRIPTION
+ The insides of a reloc code. The idea is that, eventually, there
+ will be one enumerator for every type of relocation we ever do.
+ Pass one of these values to <<bfd_reloc_type_lookup>>, and it'll
+ return a howto pointer.
+
+ This does mean that the application must determine the correct
+ enumerator value; you can't get a howto pointer from a random set
+ of attributes.
+
+CODE_FRAGMENT
+.
+.typedef enum bfd_reloc_code_real
+.{
+. {* Basic absolute relocations *}
+. BFD_RELOC_64,
+. BFD_RELOC_32,
+. BFD_RELOC_16,
+. BFD_RELOC_8,
+.
+. {* PC-relative relocations *}
+. BFD_RELOC_64_PCREL,
+. BFD_RELOC_32_PCREL,
+. BFD_RELOC_24_PCREL, {* used by i960 *}
+. BFD_RELOC_16_PCREL,
+. BFD_RELOC_8_PCREL,
+.
+. {* Linkage-table relative *}
+. BFD_RELOC_32_BASEREL,
+. BFD_RELOC_16_BASEREL,
+. BFD_RELOC_8_BASEREL,
+.
+. {* The type of reloc used to build a contructor table - at the moment
+. probably a 32 bit wide abs address, but the cpu can choose. *}
+. BFD_RELOC_CTOR,
+.
+. {* 8 bits wide, but used to form an address like 0xffnn *}
+. BFD_RELOC_8_FFnn,
+.
+. {* 32-bit pc-relative, shifted right 2 bits (i.e., 30-bit
+. word displacement, e.g. for SPARC) *}
+. BFD_RELOC_32_PCREL_S2,
+.
+. {* High 22 bits of 32-bit value, placed into lower 22 bits of
+. target word; simple reloc. *}
+. BFD_RELOC_HI22,
+. {* Low 10 bits. *}
+. BFD_RELOC_LO10,
+.
+. {* Reloc types used for i960/b.out. *}
+. BFD_RELOC_I960_CALLJ,
+.
+. {* now for the sparc/elf codes *}
+. BFD_RELOC_NONE, {* actually used *}
+. BFD_RELOC_SPARC_WDISP22,
+. BFD_RELOC_SPARC22,
+. BFD_RELOC_SPARC13,
+. BFD_RELOC_SPARC_GOT10,
+. BFD_RELOC_SPARC_GOT13,
+. BFD_RELOC_SPARC_GOT22,
+. BFD_RELOC_SPARC_PC10,
+. BFD_RELOC_SPARC_PC22,
+. BFD_RELOC_SPARC_WPLT30,
+. BFD_RELOC_SPARC_COPY,
+. BFD_RELOC_SPARC_GLOB_DAT,
+. BFD_RELOC_SPARC_JMP_SLOT,
+. BFD_RELOC_SPARC_RELATIVE,
+. BFD_RELOC_SPARC_UA32,
+.
+. {* these are a.out specific? *}
+. BFD_RELOC_SPARC_BASE13,
+. BFD_RELOC_SPARC_BASE22,
+.
+.
+. {* Bits 27..2 of the relocation address shifted right 2 bits;
+. simple reloc otherwise. *}
+. BFD_RELOC_MIPS_JMP,
+.
+. {* signed 16-bit pc-relative, shifted right 2 bits (e.g. for MIPS) *}
+. BFD_RELOC_16_PCREL_S2,
+.
+. {* High 16 bits of 32-bit value; simple reloc. *}
+. BFD_RELOC_HI16,
+. {* High 16 bits of 32-bit value but the low 16 bits will be sign
+. extended and added to form the final result. If the low 16
+. bits form a negative number, we need to add one to the high value
+. to compensate for the borrow when the low bits are added. *}
+. BFD_RELOC_HI16_S,
+. {* Low 16 bits. *}
+. BFD_RELOC_LO16,
+.
+. {* 16 bit relocation relative to the global pointer. *}
+. BFD_RELOC_MIPS_GPREL,
+.
+. {* These are, so far, specific to HPPA processors. I'm not sure that some
+. don't duplicate other reloc types, such as BFD_RELOC_32 and _32_PCREL.
+. Also, many more were in the list I got that don't fit in well in the
+. model BFD uses, so I've omitted them for now. If we do make this reloc
+. type get used for code that really does implement the funky reloc types,
+. they'll have to be added to this list. *}
+. BFD_RELOC_HPPA_32,
+. BFD_RELOC_HPPA_11,
+. BFD_RELOC_HPPA_14,
+. BFD_RELOC_HPPA_17,
+.
+. BFD_RELOC_HPPA_L21,
+. BFD_RELOC_HPPA_R11,
+. BFD_RELOC_HPPA_R14,
+. BFD_RELOC_HPPA_R17,
+. BFD_RELOC_HPPA_LS21,
+. BFD_RELOC_HPPA_RS11,
+. BFD_RELOC_HPPA_RS14,
+. BFD_RELOC_HPPA_RS17,
+. BFD_RELOC_HPPA_LD21,
+. BFD_RELOC_HPPA_RD11,
+. BFD_RELOC_HPPA_RD14,
+. BFD_RELOC_HPPA_RD17,
+. BFD_RELOC_HPPA_LR21,
+. BFD_RELOC_HPPA_RR14,
+. BFD_RELOC_HPPA_RR17,
+.
+. BFD_RELOC_HPPA_GOTOFF_11,
+. BFD_RELOC_HPPA_GOTOFF_14,
+. BFD_RELOC_HPPA_GOTOFF_L21,
+. BFD_RELOC_HPPA_GOTOFF_R11,
+. BFD_RELOC_HPPA_GOTOFF_R14,
+. BFD_RELOC_HPPA_GOTOFF_LS21,
+. BFD_RELOC_HPPA_GOTOFF_RS11,
+. BFD_RELOC_HPPA_GOTOFF_RS14,
+. BFD_RELOC_HPPA_GOTOFF_LD21,
+. BFD_RELOC_HPPA_GOTOFF_RD11,
+. BFD_RELOC_HPPA_GOTOFF_RD14,
+. BFD_RELOC_HPPA_GOTOFF_LR21,
+. BFD_RELOC_HPPA_GOTOFF_RR14,
+.
+. BFD_RELOC_HPPA_DLT_32,
+. BFD_RELOC_HPPA_DLT_11,
+. BFD_RELOC_HPPA_DLT_14,
+. BFD_RELOC_HPPA_DLT_L21,
+. BFD_RELOC_HPPA_DLT_R11,
+. BFD_RELOC_HPPA_DLT_R14,
+.
+. BFD_RELOC_HPPA_ABS_CALL_11,
+. BFD_RELOC_HPPA_ABS_CALL_14,
+. BFD_RELOC_HPPA_ABS_CALL_17,
+. BFD_RELOC_HPPA_ABS_CALL_L21,
+. BFD_RELOC_HPPA_ABS_CALL_R11,
+. BFD_RELOC_HPPA_ABS_CALL_R14,
+. BFD_RELOC_HPPA_ABS_CALL_R17,
+. BFD_RELOC_HPPA_ABS_CALL_LS21,
+. BFD_RELOC_HPPA_ABS_CALL_RS11,
+. BFD_RELOC_HPPA_ABS_CALL_RS14,
+. BFD_RELOC_HPPA_ABS_CALL_RS17,
+. BFD_RELOC_HPPA_ABS_CALL_LD21,
+. BFD_RELOC_HPPA_ABS_CALL_RD11,
+. BFD_RELOC_HPPA_ABS_CALL_RD14,
+. BFD_RELOC_HPPA_ABS_CALL_RD17,
+. BFD_RELOC_HPPA_ABS_CALL_LR21,
+. BFD_RELOC_HPPA_ABS_CALL_RR14,
+. BFD_RELOC_HPPA_ABS_CALL_RR17,
+.
+. BFD_RELOC_HPPA_PCREL_CALL_11,
+. BFD_RELOC_HPPA_PCREL_CALL_12,
+. BFD_RELOC_HPPA_PCREL_CALL_14,
+. BFD_RELOC_HPPA_PCREL_CALL_17,
+. BFD_RELOC_HPPA_PCREL_CALL_L21,
+. BFD_RELOC_HPPA_PCREL_CALL_R11,
+. BFD_RELOC_HPPA_PCREL_CALL_R14,
+. BFD_RELOC_HPPA_PCREL_CALL_R17,
+. BFD_RELOC_HPPA_PCREL_CALL_LS21,
+. BFD_RELOC_HPPA_PCREL_CALL_RS11,
+. BFD_RELOC_HPPA_PCREL_CALL_RS14,
+. BFD_RELOC_HPPA_PCREL_CALL_RS17,
+. BFD_RELOC_HPPA_PCREL_CALL_LD21,
+. BFD_RELOC_HPPA_PCREL_CALL_RD11,
+. BFD_RELOC_HPPA_PCREL_CALL_RD14,
+. BFD_RELOC_HPPA_PCREL_CALL_RD17,
+. BFD_RELOC_HPPA_PCREL_CALL_LR21,
+. BFD_RELOC_HPPA_PCREL_CALL_RR14,
+. BFD_RELOC_HPPA_PCREL_CALL_RR17,
+.
+. BFD_RELOC_HPPA_PLABEL_32,
+. BFD_RELOC_HPPA_PLABEL_11,
+. BFD_RELOC_HPPA_PLABEL_14,
+. BFD_RELOC_HPPA_PLABEL_L21,
+. BFD_RELOC_HPPA_PLABEL_R11,
+. BFD_RELOC_HPPA_PLABEL_R14,
+.
+. BFD_RELOC_HPPA_UNWIND_ENTRY,
+. BFD_RELOC_HPPA_UNWIND_ENTRIES,
+.
+. {* i386/elf relocations *}
+. BFD_RELOC_386_GOT32,
+. BFD_RELOC_386_PLT32,
+. BFD_RELOC_386_COPY,
+. BFD_RELOC_386_GLOB_DAT,
+. BFD_RELOC_386_JUMP_SLOT,
+. BFD_RELOC_386_RELATIVE,
+. BFD_RELOC_386_GOTOFF,
+. BFD_RELOC_386_GOTPC,
+.
+. {* this must be the highest numeric value *}
+. BFD_RELOC_UNUSED
+. } bfd_reloc_code_real_type;
+*/
+
+
+/*
+SECTION
+ bfd_reloc_type_lookup
+
+SYNOPSIS
+ CONST struct reloc_howto_struct *
+ bfd_reloc_type_lookup (bfd *abfd, bfd_reloc_code_real_type code);
+
+DESCRIPTION
+ This routine returns a pointer to a howto struct which when
+ invoked, will perform the supplied relocation on data from the
+ architecture noted.
+
+*/
+
+
+CONST struct reloc_howto_struct *
+DEFUN(bfd_reloc_type_lookup,(abfd, code),
+ bfd *abfd AND
+ bfd_reloc_code_real_type code)
+{
+ return BFD_SEND (abfd, reloc_type_lookup, (abfd, code));
+}
+
+static reloc_howto_type bfd_howto_32 =
+ HOWTO(0, 00,2,32,false,0,complain_overflow_bitfield,0,"VRT32", false,0xffffffff,0xffffffff,true);
+
+
+/*
+INTERNAL_FUNCTION
+ bfd_default_reloc_type_lookup
+
+SYNOPSIS
+ CONST struct reloc_howto_struct *bfd_default_reloc_type_lookup
+ (bfd *abfd AND
+ bfd_reloc_code_real_type code);
+
+DESCRIPTION
+ Provides a default relocation lookup routine for any architecture.
+
+
+*/
+
+CONST struct reloc_howto_struct *
+DEFUN(bfd_default_reloc_type_lookup, (abfd, code),
+ bfd *abfd AND
+ bfd_reloc_code_real_type code)
+{
+ switch (code)
+ {
+ case BFD_RELOC_CTOR:
+ /* The type of reloc used in a ctor, which will be as wide as the
+ address - so either a 64, 32, or 16 bitter. */
+ switch (bfd_get_arch_info (abfd)->bits_per_address) {
+ case 64:
+ BFD_FAIL();
+ case 32:
+ return &bfd_howto_32;
+ case 16:
+ BFD_FAIL();
+ default:
+ BFD_FAIL();
+ }
+ default:
+ BFD_FAIL();
+ }
+ return (CONST struct reloc_howto_struct *)NULL;
+}
+
+
+/*
+INTERNAL_FUNCTION
+ bfd_generic_relax_section
+
+SYNOPSIS
+ boolean bfd_generic_relax_section
+ (bfd *abfd,
+ asection *section,
+ asymbol **symbols);
+
+DESCRIPTION
+ Provides default handling for relaxing for back ends which
+ don't do relaxing -- i.e., does nothing.
+*/
+
+boolean
+DEFUN(bfd_generic_relax_section,(abfd, section, symbols),
+ bfd *abfd AND
+ asection *section AND
+ asymbol **symbols)
+{
+
+ return false;
+
+}
+
+
+/*
+INTERNAL_FUNCTION
+ bfd_generic_get_relocated_section_contents
+
+SYNOPSIS
+ bfd_byte *
+ bfd_generic_get_relocated_section_contents (bfd *abfd,
+ struct bfd_seclet *seclet,
+ bfd_byte *data,
+ boolean relocateable);
+
+DESCRIPTION
+ Provides default handling of relocation effort for back ends
+ which can't be bothered to do it efficiently.
+
+*/
+
+bfd_byte *
+DEFUN(bfd_generic_get_relocated_section_contents,(abfd,
+ seclet,
+ data,
+ relocateable),
+ bfd *abfd AND
+ struct bfd_seclet *seclet AND
+ bfd_byte *data AND
+ boolean relocateable)
+{
+ extern bfd_error_vector_type bfd_error_vector;
+
+ /* Get enough memory to hold the stuff */
+ bfd *input_bfd = seclet->u.indirect.section->owner;
+ asection *input_section = seclet->u.indirect.section;
+
+
+
+ size_t reloc_size = bfd_get_reloc_upper_bound(input_bfd, input_section);
+ arelent **reloc_vector = (arelent **) alloca(reloc_size);
+
+ /* read in the section */
+ bfd_get_section_contents(input_bfd,
+ input_section,
+ data,
+ 0,
+ input_section->_raw_size);
+
+/* We're not relaxing the section, so just copy the size info */
+ input_section->_cooked_size = input_section->_raw_size;
+ input_section->reloc_done = true;
+
+
+ if (bfd_canonicalize_reloc(input_bfd,
+ input_section,
+ reloc_vector,
+ seclet->u.indirect.symbols) )
+ {
+ arelent **parent;
+ for (parent = reloc_vector; * parent != (arelent *)NULL;
+ parent++)
+ {
+ bfd_reloc_status_type r=
+ bfd_perform_relocation(input_bfd,
+ *parent,
+ data,
+ input_section,
+ relocateable ? abfd : (bfd *) NULL);
+
+ if (relocateable)
+ {
+ asection *os = input_section->output_section;
+
+ /* A partial link, so keep the relocs */
+ os->orelocation[os->reloc_count] = *parent;
+ os->reloc_count++;
+ }
+
+ if (r != bfd_reloc_ok)
+ {
+ switch (r)
+ {
+ case bfd_reloc_undefined:
+ bfd_error_vector.undefined_symbol(*parent, seclet);
+ break;
+ case bfd_reloc_dangerous:
+ bfd_error_vector.reloc_dangerous(*parent, seclet);
+ break;
+ case bfd_reloc_outofrange:
+ case bfd_reloc_overflow:
+ bfd_error_vector.reloc_value_truncated(*parent, seclet);
+ break;
+ default:
+ abort();
+ break;
+ }
+
+ }
+ }
+ }
+
+
+ return data;
+
+
+}
diff --git a/gnu/usr.bin/gdb/bfd/seclet.c b/gnu/usr.bin/gdb/bfd/seclet.c
new file mode 100644
index 0000000..5dcc59a
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/seclet.c
@@ -0,0 +1,185 @@
+/* seclet.c
+ Copyright (C) 1992, 1993 Free Software Foundation, Inc.
+ Written by Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This module is part of BFD */
+
+
+/* The intention is that one day, all the code which uses sections
+ will change and use seclets instead - maybe seglet would have been
+ a better name..
+
+ Anyway, a seclet contains enough info to be able to describe an
+ area of output memory in one go.
+
+ The only description so far catered for is that of the
+ <<bfd_indirect_seclet>>, which is a select which points to a
+ <<section>> and the <<asymbols>> associated with the section, so
+ that relocation can be done when needed.
+
+ One day there will be more types - they will at least migrate from
+ the linker's data structures - also there could be extra stuff,
+ like a bss seclet, which descibes a lump of memory as containing
+ zeros compactly, without the horrible SEC_* flag cruft.
+
+
+*/
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "seclet.h"
+#include "coff/internal.h"
+
+/* Create a new seclet and attach it to a section. */
+
+bfd_seclet_type *
+DEFUN(bfd_new_seclet,(abfd, section),
+ bfd *abfd AND
+ asection *section)
+{
+ bfd_seclet_type *n = (bfd_seclet_type *)bfd_alloc(abfd, sizeof(bfd_seclet_type));
+ if (section->seclets_tail != (bfd_seclet_type *)NULL) {
+ section->seclets_tail->next = n;
+ }
+ else
+ {
+ section->seclets_head = n;
+ }
+ section->seclets_tail = n;
+
+ return n;
+}
+
+/* Given an indirect seclet which points to an input section, relocate
+ the contents of the seclet and put the data in its final
+ destination. */
+
+static boolean
+DEFUN(rel,(abfd, seclet, output_section, data, relocateable),
+ bfd *abfd AND
+ bfd_seclet_type *seclet AND
+ asection *output_section AND
+ PTR data AND
+ boolean relocateable)
+{
+ if ((output_section->flags & SEC_HAS_CONTENTS) != 0
+ && seclet->size)
+ {
+ data = (PTR) bfd_get_relocated_section_contents(abfd, seclet, data,
+ relocateable);
+ if(bfd_set_section_contents(abfd,
+ output_section,
+ data,
+ seclet->offset,
+ seclet->size) == false)
+ {
+ abort();
+ }
+ }
+ return true;
+}
+
+/* Put the contents of a seclet in its final destination. */
+
+static boolean
+DEFUN(seclet_dump_seclet,(abfd, seclet, section, data, relocateable),
+ bfd *abfd AND
+ bfd_seclet_type *seclet AND
+ asection *section AND
+ PTR data AND
+ boolean relocateable)
+{
+ switch (seclet->type)
+ {
+ case bfd_indirect_seclet:
+ /* The contents of this section come from another one somewhere
+ else */
+ return rel(abfd, seclet, section, data, relocateable);
+
+ case bfd_fill_seclet:
+ /* Fill in the section with us */
+ {
+ char *d = bfd_xmalloc(seclet->size);
+ unsigned int i;
+ for (i =0; i < seclet->size; i+=2) {
+ d[i] = seclet->u.fill.value >> 8;
+ }
+ for (i = 1; i < seclet->size; i+=2) {
+ d[i] = seclet->u.fill.value ;
+ }
+ /* Don't bother to fill in empty sections */
+ if (!(bfd_get_section_flags(abfd, section) & SEC_HAS_CONTENTS))
+ {
+ return true;
+ }
+ return bfd_set_section_contents(abfd, section, d, seclet->offset,
+ seclet->size);
+ }
+
+ default:
+ abort();
+ }
+
+ return true;
+}
+
+/*
+INTERNAL_FUNCTION
+ bfd_generic_seclet_link
+
+SYNOPSIS
+ boolean bfd_generic_seclet_link
+ (bfd *abfd,
+ PTR data,
+ boolean relocateable);
+
+DESCRIPTION
+
+ The generic seclet linking routine. The caller should have
+ set up seclets for all the output sections. The DATA argument
+ should point to a memory area large enough to hold the largest
+ section. This function looks through the seclets and moves
+ the contents into the output sections. If RELOCATEABLE is
+ true, the orelocation fields of the output sections must
+ already be initialized.
+
+*/
+
+boolean
+DEFUN(bfd_generic_seclet_link,(abfd, data, relocateable),
+ bfd *abfd AND
+ PTR data AND
+ boolean relocateable)
+{
+ asection *o = abfd->sections;
+ while (o != (asection *)NULL)
+ {
+ bfd_seclet_type *p = o->seclets_head;
+ while (p != (bfd_seclet_type *)NULL)
+ {
+ if (seclet_dump_seclet(abfd, p, o, data, relocateable) == false)
+ return false;
+ p = p ->next;
+ }
+ o = o->next;
+ }
+
+ return true;
+}
diff --git a/gnu/usr.bin/gdb/bfd/seclet.h b/gnu/usr.bin/gdb/bfd/seclet.h
new file mode 100644
index 0000000..de5fdff
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/seclet.h
@@ -0,0 +1,55 @@
+/* Definitions of little sections (seclets) for BFD.
+ Copyright 1992 Free Software Foundation, Inc.
+ Hacked by Steve Chamberlain of Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _SECLET_H
+#define _SECLET_H
+
+enum bfd_seclet_enum
+{
+ bfd_indirect_seclet,
+ bfd_fill_seclet
+};
+
+struct bfd_seclet
+{
+ struct bfd_seclet *next;
+ enum bfd_seclet_enum type;
+ unsigned int offset;
+ unsigned int size;
+ union
+ {
+ struct
+ {
+ asection *section;
+ asymbol **symbols;
+ } indirect;
+ struct {
+ int value;
+ } fill;
+ }
+ u;
+};
+
+typedef struct bfd_seclet bfd_seclet_type;
+
+bfd_seclet_type *
+bfd_new_seclet PARAMS ((bfd *, asection *));
+
+#endif
diff --git a/gnu/usr.bin/gdb/bfd/section.c b/gnu/usr.bin/gdb/bfd/section.c
new file mode 100644
index 0000000..547c69e
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/section.c
@@ -0,0 +1,899 @@
+/* Object file "section" support for the BFD library.
+ Copyright (C) 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+ Written by Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+SECTION
+ Sections
+
+ Sections are supported in BFD in <<section.c>>.
+
+ The raw data contained within a BFD is maintained through the
+ section abstraction. A single BFD may have any number of
+ sections, and keeps hold of them by pointing to the first,
+ each one points to the next in the list.
+
+@menu
+@* Section Input::
+@* Section Output::
+@* typedef asection::
+@* section prototypes::
+@end menu
+
+INODE
+Section Input, Section Output, Sections, Sections
+SUBSECTION
+ Section Input
+
+ When a BFD is opened for reading, the section structures are
+ created and attached to the BFD.
+
+ Each section has a name which describes the section in the
+ outside world - for example, <<a.out>> would contain at least
+ three sections, called <<.text>>, <<.data>> and <<.bss>>.
+
+ Names need not be unique; for example a COFF file may have several
+ sections named .data.
+
+ Sometimes a BFD will contain more than the 'natural' number of
+ sections. A back end may attach other sections containing
+ constructor data, or an application may add a section (using
+ bfd_make_section) to the sections attached to an already open
+ BFD. For example, the linker creates a supernumary section
+ <<COMMON>> for each input file's BFD to hold information about
+ common storage.
+
+ The raw data is not necessarily read in at the same time as
+ the section descriptor is created. Some targets may leave the
+ data in place until a <<bfd_get_section_contents>> call is
+ made. Other back ends may read in all the data at once - For
+ example; an S-record file has to be read once to determine the
+ size of the data. An IEEE-695 file doesn't contain raw data in
+ sections, but data and relocation expressions intermixed, so
+ the data area has to be parsed to get out the data and
+ relocations.
+
+INODE
+Section Output, typedef asection, Section Input, Sections
+
+SUBSECTION
+ Section Output
+
+ To write a new object style BFD, the various sections to be
+ written have to be created. They are attached to the BFD in
+ the same way as input sections, data is written to the
+ sections using <<bfd_set_section_contents>>.
+
+ Any program that creates or combines sections (e.g., the assembler
+ and linker) must use the fields <<output_section>> and
+ <<output_offset>> to indicate the file sections to which each
+ section must be written. (If the section is being created from
+ scratch, <<output_section>> should probably point to the section
+ itself, and <<output_offset>> should probably be zero.)
+
+ The data to be written comes from input sections attached to
+ the output sections. The output section structure can be
+ considered a filter for the input section, the output section
+ determines the vma of the output data and the name, but the
+ input section determines the offset into the output section of
+ the data to be written.
+
+ E.g., to create a section "O", starting at 0x100, 0x123 long,
+ containing two subsections, "A" at offset 0x0 (ie at vma
+ 0x100) and "B" at offset 0x20 (ie at vma 0x120) the structures
+ would look like:
+
+| section name "A"
+| output_offset 0x00
+| size 0x20
+| output_section -----------> section name "O"
+| | vma 0x100
+| section name "B" | size 0x123
+| output_offset 0x20 |
+| size 0x103 |
+| output_section --------|
+
+
+SUBSECTION
+ Seglets
+
+ The data within a section is stored in a <<seglet>>. These
+ are much like the fixups in <<gas>>. The seglet abstraction
+ allows the a section to grow and shrink within itself.
+
+ A seglet knows how big it is, and which is the next seglet and
+ where the raw data for it is, and also points to a list of
+ relocations which apply to it.
+
+ The seglet is used by the linker to perform relaxing on final
+ code. The application creates code which is as big as
+ necessary to make it work without relaxing, and the user can
+ select whether to relax. Sometimes relaxing takes a lot of
+ time. The linker runs around the relocations to see if any
+ are attached to data which can be shrunk, if so it does it on
+ a seglet by seglet basis.
+
+*/
+
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+
+
+/*
+DOCDD
+INODE
+typedef asection, section prototypes, Section Output, Sections
+SUBSECTION
+ typedef asection
+
+ The shape of a section struct:
+
+CODE_FRAGMENT
+.
+.typedef struct sec
+.{
+. {* The name of the section, the name isn't a copy, the pointer is
+. the same as that passed to bfd_make_section. *}
+.
+. CONST char *name;
+.
+. {* Which section is it 0.nth *}
+.
+. int index;
+.
+. {* The next section in the list belonging to the BFD, or NULL. *}
+.
+. struct sec *next;
+.
+. {* The field flags contains attributes of the section. Some of
+. flags are read in from the object file, and some are
+. synthesized from other information. *}
+.
+. flagword flags;
+.
+.#define SEC_NO_FLAGS 0x000
+.
+. {* Tells the OS to allocate space for this section when loaded.
+. This would clear for a section containing debug information
+. only. *}
+.#define SEC_ALLOC 0x001
+.
+. {* Tells the OS to load the section from the file when loading.
+. This would be clear for a .bss section *}
+.#define SEC_LOAD 0x002
+.
+. {* The section contains data still to be relocated, so there will
+. be some relocation information too. *}
+.#define SEC_RELOC 0x004
+.
+.#if 0 {* Obsolete ? *}
+.#define SEC_BALIGN 0x008
+.#endif
+.
+. {* A signal to the OS that the section contains read only
+. data. *}
+.#define SEC_READONLY 0x010
+.
+. {* The section contains code only. *}
+.#define SEC_CODE 0x020
+.
+. {* The section contains data only. *}
+.#define SEC_DATA 0x040
+.
+. {* The section will reside in ROM. *}
+.#define SEC_ROM 0x080
+.
+. {* The section contains constructor information. This section
+. type is used by the linker to create lists of constructors and
+. destructors used by <<g++>>. When a back end sees a symbol
+. which should be used in a constructor list, it creates a new
+. section for the type of name (eg <<__CTOR_LIST__>>), attaches
+. the symbol to it and builds a relocation. To build the lists
+. of constructors, all the linker has to do is catenate all the
+. sections called <<__CTOR_LIST__>> and relocte the data
+. contained within - exactly the operations it would peform on
+. standard data. *}
+.#define SEC_CONSTRUCTOR 0x100
+.
+. {* The section is a constuctor, and should be placed at the
+. end of the text, data, or bss section(?). *}
+.#define SEC_CONSTRUCTOR_TEXT 0x1100
+.#define SEC_CONSTRUCTOR_DATA 0x2100
+.#define SEC_CONSTRUCTOR_BSS 0x3100
+.
+. {* The section has contents - a data section could be
+. <<SEC_ALLOC>> | <<SEC_HAS_CONTENTS>>, a debug section could be
+. <<SEC_HAS_CONTENTS>> *}
+.#define SEC_HAS_CONTENTS 0x200
+.
+. {* An instruction to the linker not to output sections
+. containing this flag even if they have information which
+. would normally be written. *}
+.#define SEC_NEVER_LOAD 0x400
+.
+. {* The section is a shared library section. The linker must leave
+. these completely alone, as the vma and size are used when
+. the executable is loaded. *}
+.#define SEC_SHARED_LIBRARY 0x800
+.
+. {* The section is a common section (symbols may be defined
+. multiple times, the value of a symbol is the amount of
+. space it requires, and the largest symbol value is the one
+. used). Most targets have exactly one of these (which we
+. translate to bfd_com_section), but ECOFF has two. *}
+.#define SEC_IS_COMMON 0x8000
+.
+. {* The section contains only debugging information. For
+. example, this is set for ELF .debug and .stab sections.
+. strip tests this flag to see if a section can be
+. discarded. *}
+.#define SEC_DEBUGGING 0x10000
+.
+. {* End of section flags. *}
+.
+. {* The virtual memory address of the section - where it will be
+. at run time. The symbols are relocated against this. The
+. user_set_vma flag is maintained by bfd; if it's not set, the
+. backend can assign addresses (for example, in <<a.out>>, where
+. the default address for <<.data>> is dependent on the specific
+. target and various flags). *}
+.
+. bfd_vma vma;
+. boolean user_set_vma;
+.
+. {* The load address of the section - where it would be in a
+. rom image, really only used for writing section header
+. information. *}
+.
+. bfd_vma lma;
+.
+. {* The size of the section in bytes, as it will be output.
+. contains a value even if the section has no contents (eg, the
+. size of <<.bss>>). This will be filled in after relocation *}
+.
+. bfd_size_type _cooked_size;
+.
+. {* The size on disk of the section in bytes originally. Normally this
+. value is the same as the size, but if some relaxing has
+. been done, then this value will be bigger. *}
+.
+. bfd_size_type _raw_size;
+.
+. {* If this section is going to be output, then this value is the
+. offset into the output section of the first byte in the input
+. section. Eg, if this was going to start at the 100th byte in
+. the output section, this value would be 100. *}
+.
+. bfd_vma output_offset;
+.
+. {* The output section through which to map on output. *}
+.
+. struct sec *output_section;
+.
+. {* The alignment requirement of the section, as an exponent - eg
+. 3 aligns to 2^3 (or 8) *}
+.
+. unsigned int alignment_power;
+.
+. {* If an input section, a pointer to a vector of relocation
+. records for the data in this section. *}
+.
+. struct reloc_cache_entry *relocation;
+.
+. {* If an output section, a pointer to a vector of pointers to
+. relocation records for the data in this section. *}
+.
+. struct reloc_cache_entry **orelocation;
+.
+. {* The number of relocation records in one of the above *}
+.
+. unsigned reloc_count;
+.
+. {* Information below is back end specific - and not always used
+. or updated. *}
+.
+. {* File position of section data *}
+.
+. file_ptr filepos;
+.
+. {* File position of relocation info *}
+.
+. file_ptr rel_filepos;
+.
+. {* File position of line data *}
+.
+. file_ptr line_filepos;
+.
+. {* Pointer to data for applications *}
+.
+. PTR userdata;
+.
+. struct lang_output_section *otheruserdata;
+.
+. {* Attached line number information *}
+.
+. alent *lineno;
+.
+. {* Number of line number records *}
+.
+. unsigned int lineno_count;
+.
+. {* When a section is being output, this value changes as more
+. linenumbers are written out *}
+.
+. file_ptr moving_line_filepos;
+.
+. {* what the section number is in the target world *}
+.
+. int target_index;
+.
+. PTR used_by_bfd;
+.
+. {* If this is a constructor section then here is a list of the
+. relocations created to relocate items within it. *}
+.
+. struct relent_chain *constructor_chain;
+.
+. {* The BFD which owns the section. *}
+.
+. bfd *owner;
+.
+. boolean reloc_done;
+. {* A symbol which points at this section only *}
+. struct symbol_cache_entry *symbol;
+. struct symbol_cache_entry **symbol_ptr_ptr;
+.
+. struct bfd_seclet *seclets_head;
+. struct bfd_seclet *seclets_tail;
+.} asection ;
+.
+.
+. {* These sections are global, and are managed by BFD. The application
+. and target back end are not permitted to change the values in
+. these sections. *}
+.#define BFD_ABS_SECTION_NAME "*ABS*"
+.#define BFD_UND_SECTION_NAME "*UND*"
+.#define BFD_COM_SECTION_NAME "*COM*"
+.#define BFD_IND_SECTION_NAME "*IND*"
+.
+. {* the absolute section *}
+.extern asection bfd_abs_section;
+. {* Pointer to the undefined section *}
+.extern asection bfd_und_section;
+. {* Pointer to the common section *}
+.extern asection bfd_com_section;
+. {* Pointer to the indirect section *}
+.extern asection bfd_ind_section;
+.
+.extern struct symbol_cache_entry *bfd_abs_symbol;
+.extern struct symbol_cache_entry *bfd_com_symbol;
+.extern struct symbol_cache_entry *bfd_und_symbol;
+.extern struct symbol_cache_entry *bfd_ind_symbol;
+.#define bfd_get_section_size_before_reloc(section) \
+. (section->reloc_done ? (abort(),1): (section)->_raw_size)
+.#define bfd_get_section_size_after_reloc(section) \
+. ((section->reloc_done) ? (section)->_cooked_size: (abort(),1))
+*/
+
+/* These symbols are global, not specific to any BFD. Therefore, anything
+ that tries to change them is broken, and should be repaired. */
+static CONST asymbol global_syms[] = {
+ /* the_bfd, name, value, attr, section [, udata] */
+ { 0, BFD_COM_SECTION_NAME, 0, BSF_SECTION_SYM, &bfd_com_section },
+ { 0, BFD_UND_SECTION_NAME, 0, BSF_SECTION_SYM, &bfd_und_section },
+ { 0, BFD_ABS_SECTION_NAME, 0, BSF_SECTION_SYM, &bfd_abs_section },
+ { 0, BFD_IND_SECTION_NAME, 0, BSF_SECTION_SYM, &bfd_ind_section },
+};
+
+#define STD_SECTION(SEC, FLAGS, SYM, NAME, IDX) \
+ asymbol *SYM = (asymbol *) &global_syms[IDX]; \
+ asection SEC = { NAME, 0, 0, FLAGS, 0, 0, (boolean) 0, 0, 0, 0, &SEC,\
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (boolean) 0, \
+ (asymbol *) &global_syms[IDX], &SYM, }
+
+STD_SECTION (bfd_com_section, SEC_IS_COMMON, bfd_com_symbol, BFD_COM_SECTION_NAME, 0);
+STD_SECTION (bfd_und_section, 0, bfd_und_symbol, BFD_UND_SECTION_NAME, 1);
+STD_SECTION (bfd_abs_section, 0, bfd_abs_symbol, BFD_ABS_SECTION_NAME, 2);
+STD_SECTION (bfd_ind_section, 0, bfd_ind_symbol, BFD_IND_SECTION_NAME, 3);
+#undef STD_SECTION
+
+/*
+DOCDD
+INODE
+section prototypes, , typedef asection, Sections
+SUBSECTION
+ section prototypes
+
+These are the functions exported by the section handling part of
+<<libbfd>.
+*/
+
+/*
+FUNCTION
+ bfd_get_section_by_name
+
+SYNOPSIS
+ asection *bfd_get_section_by_name(bfd *abfd, CONST char *name);
+
+DESCRIPTION
+ Runs through the provided @var{abfd} and returns the one of the
+ <<asection>>s who's name matches that provided, otherwise NULL.
+ @xref{Sections}, for more information.
+
+ This should only be used in special cases; the normal way to process
+ all sections of a given name is to use bfd_map_over_sections and
+ strcmp on the name (or better yet, base it on the section flags
+ or something else) for each section.
+*/
+
+asection *
+DEFUN(bfd_get_section_by_name,(abfd, name),
+ bfd *abfd AND
+ CONST char *name)
+{
+ asection *sect;
+
+ for (sect = abfd->sections; sect != NULL; sect = sect->next)
+ if (!strcmp (sect->name, name)) return sect;
+ return NULL;
+}
+
+
+/*
+FUNCTION
+ bfd_make_section_old_way
+
+SYNOPSIS
+ asection *bfd_make_section_old_way(bfd *, CONST char *name);
+
+DESCRIPTION
+ This function creates a new empty section called @var{name}
+ and attaches it to the end of the chain of sections for the
+ BFD supplied. An attempt to create a section with a name which
+ is already in use, returns its pointer without changing the
+ section chain.
+
+ It has the funny name since this is the way it used to be
+ before is was rewritten...
+
+ Possible errors are:
+ o invalid_operation -
+ If output has already started for this BFD.
+ o no_memory -
+ If obstack alloc fails.
+
+*/
+
+
+asection *
+DEFUN(bfd_make_section_old_way,(abfd, name),
+ bfd *abfd AND
+ CONST char * name)
+{
+ asection *sec = bfd_get_section_by_name(abfd, name);
+ if (sec == (asection *)NULL)
+ {
+ sec = bfd_make_section(abfd, name);
+ }
+ return sec;
+}
+
+/*
+FUNCTION
+ bfd_make_section_anyway
+
+SYNOPSIS
+ asection *bfd_make_section_anyway(bfd *, CONST char *name);
+
+DESCRIPTION
+ Create a new empty section called @var{name} and attach it to the end of
+ the chain of sections for @var{abfd}. Create a new section even if there
+ is already a section with that name.
+
+ Returns NULL and sets bfd_error on error; possible errors are:
+ o invalid_operation - If output has already started for @var{abfd}.
+ o no_memory - If obstack alloc fails.
+*/
+
+sec_ptr
+bfd_make_section_anyway (abfd, name)
+ bfd *abfd;
+ CONST char *name;
+{
+ asection *newsect;
+ asection **prev = &abfd->sections;
+ asection * sect = abfd->sections;
+
+ if (abfd->output_has_begun)
+ {
+ bfd_error = invalid_operation;
+ return NULL;
+ }
+
+ while (sect) {
+ prev = &sect->next;
+ sect = sect->next;
+ }
+
+ newsect = (asection *) bfd_zalloc(abfd, sizeof (asection));
+ if (newsect == NULL) {
+ bfd_error = no_memory;
+ return NULL;
+ }
+
+ newsect->name = name;
+ newsect->index = abfd->section_count++;
+ newsect->flags = SEC_NO_FLAGS;
+
+ newsect->userdata = 0;
+ newsect->next = (asection *)NULL;
+ newsect->relocation = (arelent *)NULL;
+ newsect->reloc_count = 0;
+ newsect->line_filepos =0;
+ newsect->owner = abfd;
+
+ /* Create a symbol whos only job is to point to this section. This is
+ useful for things like relocs which are relative to the base of a
+ section. */
+ newsect->symbol = bfd_make_empty_symbol(abfd);
+ newsect->symbol->name = name;
+ newsect->symbol->value = 0;
+ newsect->symbol->section = newsect;
+ newsect->symbol->flags = BSF_SECTION_SYM;
+
+ newsect->symbol_ptr_ptr = &newsect->symbol;
+
+ if (BFD_SEND (abfd, _new_section_hook, (abfd, newsect)) != true) {
+ free (newsect);
+ return NULL;
+ }
+
+ *prev = newsect;
+ return newsect;
+}
+
+/*
+FUNCTION
+ bfd_make_section
+
+SYNOPSIS
+ asection *bfd_make_section(bfd *, CONST char *name);
+
+DESCRIPTION
+ Like bfd_make_section_anyway, but return NULL (without setting
+ bfd_error) without changing the section chain if there is already a
+ section named @var{name}. If there is an error, return NULL and set
+ bfd_error.
+*/
+
+sec_ptr
+DEFUN(bfd_make_section,(abfd, name),
+ bfd *abfd AND
+ CONST char * name)
+{
+ asection * sect = abfd->sections;
+
+ if (strcmp(name, BFD_ABS_SECTION_NAME) == 0)
+ {
+ return &bfd_abs_section;
+ }
+ if (strcmp(name, BFD_COM_SECTION_NAME) == 0)
+ {
+ return &bfd_com_section;
+ }
+ if (strcmp(name, BFD_UND_SECTION_NAME) == 0)
+ {
+ return &bfd_und_section;
+ }
+
+ if (strcmp(name, BFD_IND_SECTION_NAME) == 0)
+ {
+ return &bfd_ind_section;
+ }
+
+ while (sect) {
+ if (!strcmp(sect->name, name)) return NULL;
+ sect = sect->next;
+ }
+
+ /* The name is not already used; go ahead and make a new section. */
+ return bfd_make_section_anyway (abfd, name);
+}
+
+
+/*
+FUNCTION
+ bfd_set_section_flags
+
+SYNOPSIS
+ boolean bfd_set_section_flags(bfd *, asection *, flagword);
+
+DESCRIPTION
+ Attempts to set the attributes of the section named in the BFD
+ supplied to the value. Returns true on success, false on
+ error. Possible error returns are:
+
+ o invalid operation -
+ The section cannot have one or more of the attributes
+ requested. For example, a .bss section in <<a.out>> may not
+ have the <<SEC_HAS_CONTENTS>> field set.
+
+*/
+
+boolean
+DEFUN(bfd_set_section_flags,(abfd, section, flags),
+ bfd *abfd AND
+ sec_ptr section AND
+ flagword flags)
+{
+#if 0
+ /* If you try to copy a text section from an input file (where it
+ has the SEC_CODE flag set) to an output file, this loses big if
+ the bfd_applicable_section_flags (abfd) doesn't have the SEC_CODE
+ set - which it doesn't, at least not for a.out. FIXME */
+
+ if ((flags & bfd_applicable_section_flags (abfd)) != flags) {
+ bfd_error = invalid_operation;
+ return false;
+ }
+#endif
+
+ section->flags = flags;
+ return true;
+}
+
+
+/*
+FUNCTION
+ bfd_map_over_sections
+
+SYNOPSIS
+ void bfd_map_over_sections(bfd *abfd,
+ void (*func)(bfd *abfd,
+ asection *sect,
+ PTR obj),
+ PTR obj);
+
+DESCRIPTION
+ Calls the provided function @var{func} for each section
+ attached to the BFD @var{abfd}, passing @var{obj} as an
+ argument. The function will be called as if by
+
+| func(abfd, the_section, obj);
+
+ This is the prefered method for iterating over sections, an
+ alternative would be to use a loop:
+
+| section *p;
+| for (p = abfd->sections; p != NULL; p = p->next)
+| func(abfd, p, ...)
+
+
+*/
+
+/*VARARGS2*/
+void
+DEFUN(bfd_map_over_sections,(abfd, operation, user_storage),
+ bfd *abfd AND
+ void (*operation) PARAMS ((bfd *abfd, asection *sect, PTR obj)) AND
+ PTR user_storage)
+{
+ asection *sect;
+ int i = 0;
+
+ for (sect = abfd->sections; sect != NULL; i++, sect = sect->next)
+ (*operation) (abfd, sect, user_storage);
+
+ if (i != abfd->section_count) /* Debugging */
+ abort();
+}
+
+
+/*
+FUNCTION
+ bfd_set_section_size
+
+SYNOPSIS
+ boolean bfd_set_section_size(bfd *, asection *, bfd_size_type val);
+
+DESCRIPTION
+ Sets @var{section} to the size @var{val}. If the operation is
+ ok, then <<true>> is returned, else <<false>>.
+
+ Possible error returns:
+ o invalid_operation -
+ Writing has started to the BFD, so setting the size is invalid
+
+*/
+
+boolean
+DEFUN(bfd_set_section_size,(abfd, ptr, val),
+ bfd *abfd AND
+ sec_ptr ptr AND
+ bfd_size_type val)
+{
+ /* Once you've started writing to any section you cannot create or change
+ the size of any others. */
+
+ if (abfd->output_has_begun) {
+ bfd_error = invalid_operation;
+ return false;
+ }
+
+ ptr->_cooked_size = val;
+ ptr->_raw_size = val;
+
+ return true;
+}
+
+/*
+FUNCTION
+ bfd_set_section_contents
+
+SYNOPSIS
+ boolean bfd_set_section_contents
+ (bfd *abfd,
+ asection *section,
+ PTR data,
+ file_ptr offset,
+ bfd_size_type count);
+
+
+DESCRIPTION
+ Sets the contents of the section @var{section} in BFD
+ @var{abfd} to the data starting in memory at @var{data}. The
+ data is written to the output section starting at offset
+ @var{offset} for @var{count} bytes.
+
+
+
+ Normally <<true>> is returned, else <<false>>. Possible error
+ returns are:
+ o no_contents -
+ The output section does not have the <<SEC_HAS_CONTENTS>>
+ attribute, so nothing can be written to it.
+ o and some more too
+
+ This routine is front end to the back end function
+ <<_bfd_set_section_contents>>.
+
+
+*/
+
+#define bfd_get_section_size_now(abfd,sec) \
+(sec->reloc_done \
+ ? bfd_get_section_size_after_reloc (sec) \
+ : bfd_get_section_size_before_reloc (sec))
+
+boolean
+DEFUN(bfd_set_section_contents,(abfd, section, location, offset, count),
+ bfd *abfd AND
+ sec_ptr section AND
+ PTR location AND
+ file_ptr offset AND
+ bfd_size_type count)
+{
+ bfd_size_type sz;
+
+ if (!bfd_get_section_flags(abfd, section) & SEC_HAS_CONTENTS)
+ {
+ bfd_error = no_contents;
+ return(false);
+ }
+
+ if (offset < 0)
+ {
+ bad_val:
+ bfd_error = bad_value;
+ return false;
+ }
+ sz = bfd_get_section_size_now (abfd, section);
+ if (offset > sz
+ || count > sz
+ || offset + count > sz)
+ goto bad_val;
+
+ switch (abfd->direction)
+ {
+ case read_direction:
+ case no_direction:
+ bfd_error = invalid_operation;
+ return false;
+
+ case write_direction:
+ break;
+
+ case both_direction:
+ /* File is opened for update. `output_has_begun' some time ago when
+ the file was created. Do not recompute sections sizes or alignments
+ in _bfd_set_section_content. */
+ abfd->output_has_begun = true;
+ break;
+ }
+
+ if (BFD_SEND (abfd, _bfd_set_section_contents,
+ (abfd, section, location, offset, count)))
+ {
+ abfd->output_has_begun = true;
+ return true;
+ }
+
+ return false;
+}
+
+/*
+FUNCTION
+ bfd_get_section_contents
+
+SYNOPSIS
+ boolean bfd_get_section_contents
+ (bfd *abfd, asection *section, PTR location,
+ file_ptr offset, bfd_size_type count);
+
+DESCRIPTION
+ This function reads data from @var{section} in BFD @var{abfd}
+ into memory starting at @var{location}. The data is read at an
+ offset of @var{offset} from the start of the input section,
+ and is read for @var{count} bytes.
+
+ If the contents of a constuctor with the <<SEC_CONSTUCTOR>>
+ flag set are requested, then the @var{location} is filled with
+ zeroes. If no errors occur, <<true>> is returned, else
+ <<false>>.
+
+
+
+*/
+boolean
+DEFUN(bfd_get_section_contents,(abfd, section, location, offset, count),
+ bfd *abfd AND
+ sec_ptr section AND
+ PTR location AND
+ file_ptr offset AND
+ bfd_size_type count)
+{
+ bfd_size_type sz;
+
+ if (section->flags & SEC_CONSTRUCTOR)
+ {
+ memset(location, 0, (unsigned)count);
+ return true;
+ }
+
+ if (offset < 0)
+ {
+ bad_val:
+ bfd_error = bad_value;
+ return false;
+ }
+ sz = bfd_get_section_size_now (abfd, section);
+ if (offset > sz
+ || count > sz
+ || offset + count > sz)
+ goto bad_val;
+
+ if (count == 0)
+ /* Don't bother. */
+ return true;
+
+ return BFD_SEND (abfd, _bfd_get_section_contents,
+ (abfd, section, location, offset, count));
+}
diff --git a/gnu/usr.bin/gdb/bfd/srec.c b/gnu/usr.bin/gdb/bfd/srec.c
new file mode 100644
index 0000000..88ba957
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/srec.c
@@ -0,0 +1,1001 @@
+/* BFD back-end for s-record objects.
+ Copyright 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+ Written by Steve Chamberlain of Cygnus Support <sac@cygnus.com>.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+SUBSECTION
+ S-Record handling
+
+DESCRIPTION
+
+ Ordinary S-Records cannot hold anything but addresses and
+ data, so that's all that we implement.
+
+ The only interesting thing is that S-Records may come out of
+ order and there is no header, so an initial scan is required
+ to discover the minimum and maximum addresses used to create
+ the vma and size of the only section we create. We
+ arbitrarily call this section ".text".
+
+ When bfd_get_section_contents is called the file is read
+ again, and this time the data is placed into a bfd_alloc'd
+ area.
+
+ Any number of sections may be created for output, we save them
+ up and output them when it's time to close the bfd.
+
+ An s record looks like:
+
+EXAMPLE
+ S<type><length><address><data><checksum>
+
+DESCRIPTION
+ Where
+ o length
+ is the number of bytes following upto the checksum. Note that
+ this is not the number of chars following, since it takes two
+ chars to represent a byte.
+ o type
+ is one of:
+ 0) header record
+ 1) two byte address data record
+ 2) three byte address data record
+ 3) four byte address data record
+ 7) four byte address termination record
+ 8) three byte address termination record
+ 9) two byte address termination record
+
+ o address
+ is the start address of the data following, or in the case of
+ a termination record, the start address of the image
+ o data
+ is the data.
+ o checksum
+ is the sum of all the raw byte data in the record, from the length
+ upwards, modulo 256 and subtracted from 255.
+
+
+SUBSECTION
+ Symbol S-Record handling
+
+DESCRIPTION
+ Some ICE equipment understands an addition to the standard
+ S-Record format; symbols and their addresses can be sent
+ before the data.
+
+ The format of this is:
+ ($$ <modulename>
+ (<space> <symbol> <address>)*)
+ $$
+
+ so a short symbol table could look like:
+
+EXAMPLE
+ $$ flash.x
+ $$ flash.c
+ _port6 $0
+ _delay $4
+ _start $14
+ _etext $8036
+ _edata $8036
+ _end $8036
+ $$
+
+DESCRIPTION
+ We allow symbols to be anywhere in the data stream - the module names
+ are always ignored.
+
+*/
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+
+/* Macros for converting between hex and binary */
+
+static CONST char digs[] = "0123456789ABCDEF";
+
+static char hex_value[1 + (unsigned char)~0];
+
+#define NOT_HEX 20
+#define NIBBLE(x) hex_value[(unsigned char)(x)]
+#define HEX(buffer) ((NIBBLE((buffer)[0])<<4) + NIBBLE((buffer)[1]))
+#define TOHEX(d, x, ch) \
+ d[1] = digs[(x) & 0xf]; \
+ d[0] = digs[((x)>>4)&0xf]; \
+ ch += ((x) & 0xff);
+#define ISHEX(x) (hex_value[(unsigned char)(x)] != NOT_HEX)
+
+
+
+static void
+DEFUN_VOID(srec_init)
+{
+ unsigned int i;
+ static boolean inited = false;
+
+ if (inited == false)
+ {
+
+ inited = true;
+
+ for (i = 0; i < sizeof (hex_value); i++)
+ {
+ hex_value[i] = NOT_HEX;
+ }
+
+ for (i = 0; i < 10; i++)
+ {
+ hex_value[i + '0'] = i;
+
+ }
+ for (i = 0; i < 6; i++)
+ {
+ hex_value[i + 'a'] = i+10;
+ hex_value[i + 'A'] = i+10;
+ }
+ }
+}
+
+
+/* The maximum number of bytes on a line is FF */
+#define MAXCHUNK 0xff
+/* The number of bytes we fit onto a line on output */
+#define CHUNK 21
+
+/* We cannot output our srecords as we see them, we have to glue them
+ together, this is done in this structure : */
+
+struct srec_data_list_struct
+{
+ unsigned char *data;
+ bfd_vma where;
+ bfd_size_type size;
+ struct srec_data_list_struct *next;
+
+
+} ;
+typedef struct srec_data_list_struct srec_data_list_type;
+
+
+typedef struct srec_data_struct
+{
+ srec_data_list_type *head;
+ unsigned int type;
+
+ int done_symbol_read;
+ int count;
+ asymbol *symbols;
+ char *strings;
+ int symbol_idx;
+ int string_size;
+ int string_idx;
+} tdata_type;
+
+
+/*
+ called once per input S-Record, used to work out vma and size of data.
+ */
+
+static bfd_vma low,high;
+
+static void
+size_symbols(abfd, buf, len, val)
+bfd *abfd;
+char *buf;
+int len;
+int val;
+{
+ abfd->symcount ++;
+ abfd->tdata.srec_data->string_size += len + 1;
+}
+
+static void
+fillup_symbols(abfd, buf, len, val)
+bfd *abfd;
+char *buf;
+int len;
+int val;
+{
+ if (!abfd->tdata.srec_data->done_symbol_read)
+ {
+ asymbol *p;
+ if (abfd->tdata.srec_data->symbols == 0)
+ {
+ abfd->tdata.srec_data->symbols = (asymbol *)bfd_alloc(abfd, abfd->symcount * sizeof(asymbol));
+ abfd->tdata.srec_data->strings = (char*)bfd_alloc(abfd, abfd->tdata.srec_data->string_size);
+ abfd->tdata.srec_data->symbol_idx = 0;
+ abfd->tdata.srec_data->string_idx = 0;
+ }
+
+ p = abfd->tdata.srec_data->symbols + abfd->tdata.srec_data->symbol_idx++;
+ p->the_bfd = abfd;
+ p->name = abfd->tdata.srec_data->strings + abfd->tdata.srec_data->string_idx;
+ memcpy((char *)(p->name), buf, len+1);
+ abfd->tdata.srec_data->string_idx += len + 1;
+ p->value = val;
+ p->flags = BSF_EXPORT | BSF_GLOBAL;
+ p->section = &bfd_abs_section;
+ p->udata = 0;
+ }
+}
+static void
+DEFUN(size_srec,(abfd, section, address, raw, length),
+ bfd *abfd AND
+ asection *section AND
+ bfd_vma address AND
+ bfd_byte *raw AND
+ unsigned int length)
+{
+ if (address < low)
+ low = address;
+ if (address + length > high)
+ high = address + length -1;
+}
+
+
+/*
+ called once per input S-Record, copies data from input into bfd_alloc'd area
+ */
+
+static void
+DEFUN(fillup,(abfd, section, address, raw, length),
+bfd *abfd AND
+asection *section AND
+bfd_vma address AND
+bfd_byte *raw AND
+unsigned int length)
+{
+ unsigned int i;
+ bfd_byte *dst =
+ (bfd_byte *)(section->used_by_bfd) + address - section->vma;
+ /* length -1 because we don't read in the checksum */
+ for (i = 0; i < length -1 ; i++) {
+ *dst = HEX(raw);
+ dst++;
+ raw+=2;
+ }
+}
+
+/* Pass over an S-Record file, calling one of the above functions on each
+ record. */
+
+static int white(x)
+char x;
+{
+return (x== ' ' || x == '\t' || x == '\n' || x == '\r');
+}
+static int
+skipwhite(src,abfd)
+char *src;
+bfd *abfd;
+{
+ int eof = 0;
+ while (white(*src) && !eof)
+ {
+ eof = (boolean)(bfd_read(src, 1, 1, abfd) != 1);
+ }
+ return eof;
+}
+
+static boolean
+DEFUN(srec_mkobject, (abfd),
+ bfd *abfd)
+{
+ if (abfd->tdata.srec_data == 0)
+ {
+ tdata_type *tdata = (tdata_type *)bfd_alloc(abfd, sizeof(tdata_type));
+ abfd->tdata.srec_data = tdata;
+ tdata->type = 1;
+ tdata->head = (srec_data_list_type *)NULL;
+ }
+ return true;
+
+}
+
+static void
+DEFUN(pass_over,(abfd, func, symbolfunc, section),
+ bfd *abfd AND
+ void (*func)() AND
+ void (*symbolfunc)() AND
+ asection *section)
+{
+ unsigned int bytes_on_line;
+ boolean eof = false;
+
+ srec_mkobject(abfd);
+ /* To the front of the file */
+ bfd_seek(abfd, (file_ptr)0, SEEK_SET);
+ while (eof == false)
+ {
+ char buffer[MAXCHUNK];
+ char *src = buffer;
+ char type;
+ bfd_vma address = 0;
+
+ /* Find first 'S' or $ */
+ eof = (boolean)(bfd_read(src, 1, 1, abfd) != 1);
+ switch (*src)
+ {
+ default:
+ eof = (boolean)(bfd_read(src, 1, 1, abfd) != 1);
+ if (eof) return;
+ break;
+
+ case '$':
+ /* Inside a symbol definition - just ignore the module name */
+ while (*src != '\n' && !eof)
+ {
+ eof = (boolean)(bfd_read(src, 1, 1, abfd) != 1);
+ }
+ break;
+
+ case ' ':
+ /* spaces - maybe just before a symbol */
+ while (*src != '\n' && white(*src)) {
+ eof = skipwhite(src, abfd);
+
+{
+ int val = 0;
+ int slen = 0;
+ char symbol[MAXCHUNK];
+
+ /* get the symbol part */
+ while (!eof && !white(*src) && slen < MAXCHUNK)
+ {
+ symbol[slen++] = *src;
+ eof = (boolean)(bfd_read(src, 1, 1, abfd) != 1);
+ }
+ symbol[slen] = 0;
+ eof = skipwhite(src, abfd);
+ /* skip the $ for the hex value */
+ if (*src == '$')
+ {
+ eof = (boolean)(bfd_read(src, 1, 1, abfd) != 1);
+ }
+
+ /* Scan off the hex number */
+ while (isxdigit(*src ))
+ {
+ val *= 16;
+ if (isdigit(*src))
+ val += *src - '0';
+ else if (isupper(*src)) {
+ val += *src - 'A' + 10;
+ }
+ else {
+ val += *src - 'a' + 10;
+ }
+ eof = (boolean)(bfd_read(src, 1, 1, abfd) != 1);
+ }
+ symbolfunc(abfd, symbol, slen, val);
+ }
+}
+ break;
+ case 'S':
+ src++;
+
+ /* Fetch the type and the length */
+ bfd_read(src, 1, 3, abfd);
+
+ type = *src++;
+
+ if (!ISHEX (src[0]) || !ISHEX (src[1]))
+ break;
+
+ bytes_on_line = HEX(src);
+
+ if (bytes_on_line > MAXCHUNK/2)
+ break;
+ src+=2 ;
+
+ bfd_read(src, 1 , bytes_on_line * 2, abfd);
+
+ switch (type) {
+ case '0':
+ case '5':
+ /* Prologue - ignore */
+ break;
+ case '3':
+ address = HEX(src);
+ src+=2;
+ bytes_on_line--;
+
+ case '2':
+ address = HEX(src) | (address<<8) ;
+ src+=2;
+ bytes_on_line--;
+ case '1':
+ address = HEX(src) | (address<<8) ;
+ src+=2;
+ address = HEX(src) | (address<<8) ;
+ src+=2;
+ bytes_on_line-=2;
+ func(abfd,section, address, src, bytes_on_line);
+ break;
+ default:
+ return;
+ }
+ }
+ }
+
+}
+
+static bfd_target *
+object_p(abfd)
+bfd *abfd;
+{
+ asection *section;
+ /* We create one section called .text for all the contents,
+ and allocate enough room for the entire file. */
+
+ section = bfd_make_section(abfd, ".text");
+ section->_raw_size = 0;
+ section->vma = 0xffffffff;
+ low = 0xffffffff;
+ high = 0;
+ pass_over(abfd, size_srec, size_symbols, section);
+ section->_raw_size = high - low;
+ section->vma = low;
+ section->flags = SEC_HAS_CONTENTS | SEC_LOAD | SEC_ALLOC;
+
+ if (abfd->symcount)
+ abfd->flags |= HAS_SYMS;
+ return abfd->xvec;
+}
+
+static bfd_target *
+DEFUN(srec_object_p, (abfd),
+ bfd *abfd)
+{
+ char b[4];
+
+ srec_init();
+
+ bfd_seek(abfd, (file_ptr)0, SEEK_SET);
+ bfd_read(b, 1, 4, abfd);
+
+ if (b[0] != 'S' || !ISHEX(b[1]) || !ISHEX(b[2]) || !ISHEX(b[3]))
+ return (bfd_target*) NULL;
+
+ /* We create one section called .text for all the contents,
+ and allocate enough room for the entire file. */
+
+ return object_p(abfd);
+}
+
+
+static bfd_target *
+DEFUN(symbolsrec_object_p, (abfd),
+ bfd *abfd)
+{
+ char b[4];
+
+ srec_init();
+
+ bfd_seek(abfd, (file_ptr)0, SEEK_SET);
+ bfd_read(b, 1, 4, abfd);
+
+ if (b[0] != '$' || b[1] != '$')
+ return (bfd_target*) NULL;
+
+ return object_p(abfd);
+}
+
+
+static boolean
+DEFUN(srec_get_section_contents,(abfd, section, location, offset, count),
+ bfd *abfd AND
+ asection *section AND
+ PTR location AND
+ file_ptr offset AND
+ bfd_size_type count)
+{
+ if (section->used_by_bfd == (PTR)NULL)
+ {
+ section->used_by_bfd = (PTR)bfd_alloc (abfd, section->_raw_size);
+
+ pass_over(abfd, fillup, fillup_symbols, section);
+ }
+ (void) memcpy((PTR)location,
+ (PTR)((char *)(section->used_by_bfd) + offset),
+ count);
+ return true;
+}
+
+
+
+boolean
+DEFUN(srec_set_arch_mach,(abfd, arch, machine),
+ bfd *abfd AND
+ enum bfd_architecture arch AND
+ unsigned long machine)
+{
+ return bfd_default_set_arch_mach(abfd, arch, machine);
+}
+
+
+/* we have to save up all the Srecords for a splurge before output,
+ also remember */
+
+static boolean
+DEFUN(srec_set_section_contents,(abfd, section, location, offset, bytes_to_do),
+ bfd *abfd AND
+ sec_ptr section AND
+ PTR location AND
+ file_ptr offset AND
+ bfd_size_type bytes_to_do)
+{
+ tdata_type *tdata = abfd->tdata.srec_data;
+ srec_data_list_type *entry = (srec_data_list_type *)
+ bfd_alloc(abfd, sizeof(srec_data_list_type));
+
+ if ((section->flags & SEC_ALLOC)
+ && (section->flags & SEC_LOAD))
+ {
+ unsigned char *data = (unsigned char *) bfd_alloc(abfd, bytes_to_do);
+ memcpy(data, location, bytes_to_do);
+
+ if ((section->lma + offset + bytes_to_do) <= 0xffff)
+ {
+
+ }
+ else if ((section->lma + offset + bytes_to_do) <= 0xffffff
+ && tdata->type < 2)
+ {
+ tdata->type = 2;
+ }
+ else
+ {
+ tdata->type = 3;
+ }
+
+ entry->data = data;
+ entry->where = section->lma + offset;
+ entry->size = bytes_to_do;
+ entry->next = tdata->head;
+ tdata->head = entry;
+ }
+ return true;
+}
+
+/* Write a record of type, of the supplied number of bytes. The
+ supplied bytes and length don't have a checksum. That's worked out
+ here
+*/
+static
+void DEFUN(srec_write_record,(abfd, type, address, data, end),
+ bfd *abfd AND
+ char type AND
+ bfd_vma address AND
+ CONST unsigned char *data AND
+ CONST unsigned char *end)
+
+{
+ char buffer[MAXCHUNK];
+
+ unsigned int check_sum = 0;
+ unsigned CONST char *src = data;
+ char *dst =buffer;
+ char *length;
+
+
+ *dst++ = 'S';
+ *dst++ = '0' + type;
+
+ length = dst;
+ dst+=2; /* leave room for dst*/
+
+ switch (type)
+ {
+ case 3:
+ case 7:
+ TOHEX(dst, (address >> 24), check_sum);
+ dst+=2;
+ case 8:
+ case 2:
+ TOHEX(dst, (address >> 16), check_sum);
+ dst+=2;
+ case 9:
+ case 1:
+ case 0:
+ TOHEX(dst, (address >> 8), check_sum);
+ dst+=2;
+ TOHEX(dst, (address), check_sum);
+ dst+=2;
+ break;
+
+ }
+ for (src = data; src < end; src++)
+ {
+ TOHEX(dst, *src, check_sum);
+ dst+=2;
+ }
+
+ /* Fill in the length */
+ TOHEX(length, (dst - length)/2, check_sum);
+ check_sum &= 0xff;
+ check_sum = 255 - check_sum;
+ TOHEX(dst, check_sum, check_sum);
+ dst+=2;
+
+ *dst ++ = '\r';
+ *dst ++ = '\n';
+ bfd_write((PTR)buffer, 1, dst - buffer , abfd);
+}
+
+
+
+static void
+DEFUN(srec_write_header,(abfd),
+ bfd *abfd)
+{
+ unsigned char buffer[MAXCHUNK];
+ unsigned char *dst = buffer;
+ unsigned int i;
+
+ /* I'll put an arbitary 40 char limit on header size */
+ for (i = 0; i < 40 && abfd->filename[i]; i++)
+ {
+ *dst++ = abfd->filename[i];
+ }
+ srec_write_record(abfd,0, 0, buffer, dst);
+}
+
+static void
+DEFUN(srec_write_section,(abfd, tdata, list),
+ bfd *abfd AND
+ tdata_type *tdata AND
+ srec_data_list_type *list)
+{
+ unsigned int bytes_written = 0;
+ unsigned char *location = list->data;
+
+ while (bytes_written < list->size)
+ {
+ bfd_vma address;
+
+ unsigned int bytes_this_chunk = list->size - bytes_written;
+
+ if (bytes_this_chunk > CHUNK)
+ {
+ bytes_this_chunk = CHUNK;
+ }
+
+ address = list->where + bytes_written;
+
+ srec_write_record(abfd,
+ tdata->type,
+ address,
+ location,
+ location + bytes_this_chunk);
+
+ bytes_written += bytes_this_chunk;
+ location += bytes_this_chunk;
+ }
+
+}
+
+static void
+DEFUN(srec_write_terminator,(abfd, tdata),
+ bfd *abfd AND
+ tdata_type *tdata)
+{
+ unsigned char buffer[2];
+
+ srec_write_record(abfd, 10 - tdata->type,
+ abfd->start_address, buffer, buffer);
+}
+
+
+
+static void
+srec_write_symbols(abfd)
+ bfd *abfd;
+{
+ char buffer[MAXCHUNK];
+ /* Dump out the symbols of a bfd */
+ int i;
+ int len = bfd_get_symcount(abfd);
+
+ if (len)
+ {
+ asymbol **table = bfd_get_outsymbols(abfd);
+ sprintf(buffer, "$$ %s\r\n", abfd->filename);
+
+ bfd_write(buffer, strlen(buffer), 1, abfd);
+
+ for (i = 0; i < len; i++)
+ {
+ asymbol *s = table[i];
+#if 0
+ int len = strlen(s->name);
+
+ /* If this symbol has a .[ocs] in it, it's probably a file name
+ and we'll output that as the module name */
+
+ if (len > 3 && s->name[len-2] == '.')
+ {
+ int l;
+ sprintf(buffer, "$$ %s\r\n", s->name);
+ l = strlen(buffer);
+ bfd_write(buffer, l, 1, abfd);
+ }
+ else
+#endif
+ if (s->flags & (BSF_GLOBAL | BSF_LOCAL)
+ && (s->flags & BSF_DEBUGGING) == 0
+ && s->name[0] != '.'
+ && s->name[0] != 't')
+ {
+ /* Just dump out non debug symbols */
+
+ int l;
+ char buf2[40], *p;
+
+ sprintf_vma (buf2, s->value + s->section->lma);
+ p = buf2;
+ while (p[0] == '0' && p[1] != 0)
+ p++;
+ sprintf (buffer, " %s $%s\r\n", s->name, p);
+ l = strlen(buffer);
+ bfd_write(buffer, l, 1,abfd);
+ }
+ }
+ sprintf(buffer, "$$ \r\n");
+ bfd_write(buffer, strlen(buffer), 1, abfd);
+ }
+}
+
+static boolean
+internal_srec_write_object_contents(abfd, symbols)
+ bfd *abfd;
+ int symbols;
+{
+ int bytes_written;
+ tdata_type *tdata = abfd->tdata.srec_data;
+ srec_data_list_type *list;
+
+ bytes_written = 0;
+
+
+ if (symbols)
+ srec_write_symbols(abfd);
+
+ srec_write_header(abfd);
+
+ /* Now wander though all the sections provided and output them */
+ list = tdata->head;
+
+ while (list != (srec_data_list_type*)NULL)
+ {
+ srec_write_section(abfd, tdata, list);
+ list = list->next;
+ }
+ srec_write_terminator(abfd, tdata);
+ return true;
+}
+
+static boolean
+srec_write_object_contents(abfd)
+ bfd *abfd;
+{
+ return internal_srec_write_object_contents(abfd, 0);
+}
+
+static boolean
+symbolsrec_write_object_contents(abfd)
+ bfd *abfd;
+{
+ return internal_srec_write_object_contents(abfd, 1);
+}
+
+static int
+DEFUN(srec_sizeof_headers,(abfd, exec),
+ bfd *abfd AND
+ boolean exec)
+{
+return 0;
+}
+
+static asymbol *
+DEFUN(srec_make_empty_symbol, (abfd),
+ bfd*abfd)
+{
+ asymbol *new= (asymbol *)bfd_zalloc (abfd, sizeof (asymbol));
+ new->the_bfd = abfd;
+ return new;
+}
+
+static unsigned int
+srec_get_symtab_upper_bound(abfd)
+bfd *abfd;
+{
+ /* Read in all the info */
+ srec_get_section_contents(abfd,abfd->sections,0,0,0);
+ return (bfd_get_symcount(abfd) + 1) * (sizeof(asymbol *));
+}
+
+static unsigned int
+DEFUN(srec_get_symtab, (abfd, alocation),
+ bfd *abfd AND
+ asymbol **alocation)
+{
+ int lim = abfd->symcount;
+ int i;
+ for (i = 0; i < lim; i++) {
+ alocation[i] = abfd->tdata.srec_data->symbols + i;
+ }
+ alocation[i] = 0;
+ return lim;
+}
+
+void
+DEFUN(srec_get_symbol_info,(ignore_abfd, symbol, ret),
+ bfd *ignore_abfd AND
+ asymbol *symbol AND
+ symbol_info *ret)
+{
+ bfd_symbol_info (symbol, ret);
+}
+
+void
+DEFUN(srec_print_symbol,(ignore_abfd, afile, symbol, how),
+ bfd *ignore_abfd AND
+ PTR afile AND
+ asymbol *symbol AND
+ bfd_print_symbol_type how)
+{
+ FILE *file = (FILE *)afile;
+ switch (how)
+ {
+ case bfd_print_symbol_name:
+ fprintf (file, "%s", symbol->name);
+ break;
+ default:
+ bfd_print_symbol_vandf ((PTR) file, symbol);
+ fprintf (file, " %-5s %s",
+ symbol->section->name,
+ symbol->name);
+
+ }
+}
+
+#define FOO PROTO
+#define srec_new_section_hook (FOO(boolean, (*), (bfd *, asection *)))bfd_true
+
+#define srec_get_reloc_upper_bound (FOO(unsigned int, (*),(bfd*, asection *)))bfd_false
+#define srec_canonicalize_reloc (FOO(unsigned int, (*),(bfd*,asection *, arelent **, asymbol **))) bfd_0
+
+
+
+#define srec_openr_next_archived_file (FOO(bfd *, (*), (bfd*,bfd*))) bfd_nullvoidptr
+#define srec_find_nearest_line (FOO(boolean, (*),(bfd*,asection*,asymbol**,bfd_vma, CONST char**, CONST char**, unsigned int *))) bfd_false
+#define srec_generic_stat_arch_elt (FOO(int, (*), (bfd *,struct stat *))) bfd_0
+
+
+#define srec_core_file_failing_command (char *(*)())(bfd_nullvoidptr)
+#define srec_core_file_failing_signal (int (*)())bfd_0
+#define srec_core_file_matches_executable_p (FOO(boolean, (*),(bfd*, bfd*)))bfd_false
+#define srec_slurp_armap bfd_true
+#define srec_slurp_extended_name_table bfd_true
+#define srec_truncate_arname (void (*)())bfd_nullvoidptr
+#define srec_write_armap (FOO( boolean, (*),(bfd *, unsigned int, struct orl *, unsigned int, int))) bfd_nullvoidptr
+#define srec_get_lineno (struct lineno_cache_entry *(*)())bfd_nullvoidptr
+#define srec_close_and_cleanup bfd_generic_close_and_cleanup
+#define srec_bfd_debug_info_start bfd_void
+#define srec_bfd_debug_info_end bfd_void
+#define srec_bfd_debug_info_accumulate (FOO(void, (*), (bfd *, asection *))) bfd_void
+#define srec_bfd_get_relocated_section_contents bfd_generic_get_relocated_section_contents
+#define srec_bfd_relax_section bfd_generic_relax_section
+#define srec_bfd_seclet_link bfd_generic_seclet_link
+#define srec_bfd_reloc_type_lookup \
+ ((CONST struct reloc_howto_struct *(*) PARAMS ((bfd *, bfd_reloc_code_real_type))) bfd_nullvoidptr)
+#define srec_bfd_make_debug_symbol \
+ ((asymbol *(*) PARAMS ((bfd *, void *, unsigned long))) bfd_nullvoidptr)
+
+bfd_target srec_vec =
+{
+ "srec", /* name */
+ bfd_target_srec_flavour,
+ true, /* target byte order */
+ true, /* target headers byte order */
+ (HAS_RELOC | EXEC_P | /* object flags */
+ HAS_LINENO | HAS_DEBUG |
+ HAS_SYMS | HAS_LOCALS | DYNAMIC | WP_TEXT | D_PAGED),
+ (SEC_CODE|SEC_DATA|SEC_ROM|SEC_HAS_CONTENTS
+ |SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* section flags */
+ 0, /* leading underscore */
+ ' ', /* ar_pad_char */
+ 16, /* ar_max_namelen */
+ 1, /* minimum alignment */
+ bfd_getb64, bfd_getb_signed_64, bfd_putb64,
+ bfd_getb32, bfd_getb_signed_32, bfd_putb32,
+ bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* data */
+ bfd_getb64, bfd_getb_signed_64, bfd_putb64,
+ bfd_getb32, bfd_getb_signed_32, bfd_putb32,
+ bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* hdrs */
+
+ {
+ _bfd_dummy_target,
+ srec_object_p, /* bfd_check_format */
+ (struct bfd_target *(*)()) bfd_nullvoidptr,
+ (struct bfd_target *(*)()) bfd_nullvoidptr,
+ },
+ {
+ bfd_false,
+ srec_mkobject,
+ _bfd_generic_mkarchive,
+ bfd_false,
+ },
+ { /* bfd_write_contents */
+ bfd_false,
+ srec_write_object_contents,
+ _bfd_write_archive_contents,
+ bfd_false,
+ },
+ JUMP_TABLE(srec)
+ };
+
+
+
+bfd_target symbolsrec_vec =
+{
+ "symbolsrec", /* name */
+ bfd_target_srec_flavour,
+ true, /* target byte order */
+ true, /* target headers byte order */
+ (HAS_RELOC | EXEC_P | /* object flags */
+ HAS_LINENO | HAS_DEBUG |
+ HAS_SYMS | HAS_LOCALS | DYNAMIC | WP_TEXT | D_PAGED),
+ (SEC_CODE|SEC_DATA|SEC_ROM|SEC_HAS_CONTENTS
+ |SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* section flags */
+ 0, /* leading underscore */
+ ' ', /* ar_pad_char */
+ 16, /* ar_max_namelen */
+ 1, /* minimum alignment */
+ bfd_getb64, bfd_getb_signed_64, bfd_putb64,
+ bfd_getb32, bfd_getb_signed_32, bfd_putb32,
+ bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* data */
+ bfd_getb64, bfd_getb_signed_64, bfd_putb64,
+ bfd_getb32, bfd_getb_signed_32, bfd_putb32,
+ bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* hdrs */
+
+ {
+ _bfd_dummy_target,
+ symbolsrec_object_p, /* bfd_check_format */
+ (struct bfd_target *(*)()) bfd_nullvoidptr,
+ (struct bfd_target *(*)()) bfd_nullvoidptr,
+ },
+ {
+ bfd_false,
+ srec_mkobject,
+ _bfd_generic_mkarchive,
+ bfd_false,
+ },
+ { /* bfd_write_contents */
+ bfd_false,
+ symbolsrec_write_object_contents,
+ _bfd_write_archive_contents,
+ bfd_false,
+ },
+ JUMP_TABLE(srec),
+ (PTR) 0
+ };
+
diff --git a/gnu/usr.bin/gdb/bfd/stab-syms.c b/gnu/usr.bin/gdb/bfd/stab-syms.c
new file mode 100644
index 0000000..88cf850
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/stab-syms.c
@@ -0,0 +1,59 @@
+/* Table of stab names for the BFD library.
+ Copyright (C) 1990-1991 Free Software Foundation, Inc.
+ Written by Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "bfd.h"
+
+#define ARCH_SIZE 32 /* Value doesn't matter. */
+#include "libaout.h"
+#include "aout/aout64.h"
+
+/* Create a table of debugging stab-codes and corresponding names. */
+
+#define __define_name(CODE, STRING) {(int)CODE, STRING},
+#define __define_stab(NAME, CODE, STRING) __define_name(CODE, STRING)
+CONST struct {short code; char string[10];} aout_stab_names[]
+ = {
+#include "aout/stab.def"
+
+/* These are not really stab symbols, but it is
+ convenient to have them here for the sake of nm.
+ For completeness, we could also add N_TEXT etc, but those
+ are never needed, since nm treats those specially. */
+__define_name (N_SETA, "SETA") /* Absolute set element symbol */
+__define_name (N_SETT, "SETT") /* Text set element symbol */
+__define_name (N_SETD, "SETD") /* Data set element symbol */
+__define_name (N_SETB, "SETB") /* Bss set element symbol */
+__define_name (N_SETV, "SETV") /* Pointer to set vector in data area. */
+__define_name (N_INDR, "INDR")
+__define_name (N_WARNING, "WARNING")
+ };
+#undef __define_stab
+#undef GNU_EXTRA_STABS
+
+CONST char *
+DEFUN(aout_stab_name,(code),
+int code)
+{
+ register int i = sizeof(aout_stab_names) / sizeof(aout_stab_names[0]);
+ while (--i >= 0)
+ if (aout_stab_names[i].code == code)
+ return aout_stab_names[i].string;
+ return 0;
+}
diff --git a/gnu/usr.bin/gdb/bfd/syms.c b/gnu/usr.bin/gdb/bfd/syms.c
new file mode 100644
index 0000000..89ac001
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/syms.c
@@ -0,0 +1,527 @@
+/* Generic symbol-table support for the BFD library.
+ Copyright (C) 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+ Written by Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+SECTION
+ Symbols
+
+ BFD trys to maintain as much symbol information as it can when
+ it moves information from file to file. BFD passes information
+ to applications though the <<asymbol>> structure. When the
+ application requests the symbol table, BFD reads the table in
+ the native form and translates parts of it into the internal
+ format. To maintain more than the infomation passed to
+ applications some targets keep some information `behind the
+ scenes', in a structure only the particular back end knows
+ about. For example, the coff back end keeps the original
+ symbol table structure as well as the canonical structure when
+ a BFD is read in. On output, the coff back end can reconstruct
+ the output symbol table so that no information is lost, even
+ information unique to coff which BFD doesn't know or
+ understand. If a coff symbol table was read, but was written
+ through an a.out back end, all the coff specific information
+ would be lost. The symbol table of a BFD
+ is not necessarily read in until a canonicalize request is
+ made. Then the BFD back end fills in a table provided by the
+ application with pointers to the canonical information. To
+ output symbols, the application provides BFD with a table of
+ pointers to pointers to <<asymbol>>s. This allows applications
+ like the linker to output a symbol as read, since the `behind
+ the scenes' information will be still available.
+@menu
+@* Reading Symbols::
+@* Writing Symbols::
+@* typedef asymbol::
+@* symbol handling functions::
+@end menu
+
+INODE
+Reading Symbols, Writing Symbols, Symbols, Symbols
+SUBSECTION
+ Reading Symbols
+
+ There are two stages to reading a symbol table from a BFD;
+ allocating storage, and the actual reading process. This is an
+ excerpt from an appliction which reads the symbol table:
+
+| unsigned int storage_needed;
+| asymbol **symbol_table;
+| unsigned int number_of_symbols;
+| unsigned int i;
+|
+| storage_needed = get_symtab_upper_bound (abfd);
+|
+| if (storage_needed == 0) {
+| return ;
+| }
+| symbol_table = (asymbol **) bfd_xmalloc (storage_needed);
+| ...
+| number_of_symbols =
+| bfd_canonicalize_symtab (abfd, symbol_table);
+|
+| for (i = 0; i < number_of_symbols; i++) {
+| process_symbol (symbol_table[i]);
+| }
+
+ All storage for the symbols themselves is in an obstack
+ connected to the BFD, and is freed when the BFD is closed.
+
+
+INODE
+Writing Symbols, typedef asymbol, Reading Symbols, Symbols
+SUBSECTION
+ Writing Symbols
+
+ Writing of a symbol table is automatic when a BFD open for
+ writing is closed. The application attaches a vector of
+ pointers to pointers to symbols to the BFD being written, and
+ fills in the symbol count. The close and cleanup code reads
+ through the table provided and performs all the necessary
+ operations. The outputing code must always be provided with an
+ 'owned' symbol; one which has come from another BFD, or one
+ which has been created using <<bfd_make_empty_symbol>>. An
+ example showing the creation of a symbol table with only one element:
+
+| #include "bfd.h"
+| main()
+| {
+| bfd *abfd;
+| asymbol *ptrs[2];
+| asymbol *new;
+|
+| abfd = bfd_openw("foo","a.out-sunos-big");
+| bfd_set_format(abfd, bfd_object);
+| new = bfd_make_empty_symbol(abfd);
+| new->name = "dummy_symbol";
+| new->section = bfd_make_section_old_way(abfd, ".text");
+| new->flags = BSF_GLOBAL;
+| new->value = 0x12345;
+|
+| ptrs[0] = new;
+| ptrs[1] = (asymbol *)0;
+|
+| bfd_set_symtab(abfd, ptrs, 1);
+| bfd_close(abfd);
+| }
+|
+| ./makesym
+| nm foo
+| 00012345 A dummy_symbol
+
+ Many formats cannot represent arbitary symbol information; for
+ instance the <<a.out>> object format does not allow an
+ arbitary number of sections. A symbol pointing to a section
+ which is not one of <<.text>>, <<.data>> or <<.bss>> cannot
+ be described.
+
+*/
+
+
+
+/*
+DOCDD
+INODE
+typedef asymbol, symbol handling functions, Writing Symbols, Symbols
+
+*/
+/*
+SUBSECTION
+ typedef asymbol
+
+ An <<asymbol>> has the form:
+
+*/
+
+/*
+CODE_FRAGMENT
+
+.
+.typedef struct symbol_cache_entry
+.{
+. {* A pointer to the BFD which owns the symbol. This information
+. is necessary so that a back end can work out what additional
+. information (invisible to the application writer) is carried
+. with the symbol.
+.
+. This field is *almost* redundant, since you can use section->owner
+. instead, except that some symbols point to the global sections
+. bfd_{abs,com,und}_section. This could be fixed by making
+. these globals be per-bfd (or per-target-flavor). FIXME. *}
+.
+. struct _bfd *the_bfd; {* Use bfd_asymbol_bfd(sym) to access this field. *}
+.
+. {* The text of the symbol. The name is left alone, and not copied - the
+. application may not alter it. *}
+. CONST char *name;
+.
+. {* The value of the symbol. This really should be a union of a
+. numeric value with a pointer, since some flags indicate that
+. a pointer to another symbol is stored here. *}
+. symvalue value;
+.
+. {* Attributes of a symbol: *}
+.
+.#define BSF_NO_FLAGS 0x00
+.
+. {* The symbol has local scope; <<static>> in <<C>>. The value
+. is the offset into the section of the data. *}
+.#define BSF_LOCAL 0x01
+.
+. {* The symbol has global scope; initialized data in <<C>>. The
+. value is the offset into the section of the data. *}
+.#define BSF_GLOBAL 0x02
+.
+. {* The symbol has global scope, and is exported. The value is
+. the offset into the section of the data. *}
+.#define BSF_EXPORT BSF_GLOBAL {* no real difference *}
+.
+. {* A normal C symbol would be one of:
+. <<BSF_LOCAL>>, <<BSF_FORT_COMM>>, <<BSF_UNDEFINED>> or
+. <<BSF_GLOBAL>> *}
+.
+. {* The symbol is a debugging record. The value has an arbitary
+. meaning. *}
+.#define BSF_DEBUGGING 0x08
+.
+. {* The symbol denotes a function entry point. Used in ELF,
+. perhaps others someday. *}
+.#define BSF_FUNCTION 0x10
+.
+. {* Used by the linker. *}
+.#define BSF_KEEP 0x20
+.#define BSF_KEEP_G 0x40
+.
+. {* A weak global symbol, overridable without warnings by
+. a regular global symbol of the same name. *}
+.#define BSF_WEAK 0x80
+.
+. {* This symbol was created to point to a section, e.g. ELF's
+. STT_SECTION symbols. *}
+.#define BSF_SECTION_SYM 0x100
+.
+. {* The symbol used to be a common symbol, but now it is
+. allocated. *}
+.#define BSF_OLD_COMMON 0x200
+.
+. {* The default value for common data. *}
+.#define BFD_FORT_COMM_DEFAULT_VALUE 0
+.
+. {* In some files the type of a symbol sometimes alters its
+. location in an output file - ie in coff a <<ISFCN>> symbol
+. which is also <<C_EXT>> symbol appears where it was
+. declared and not at the end of a section. This bit is set
+. by the target BFD part to convey this information. *}
+.
+.#define BSF_NOT_AT_END 0x400
+.
+. {* Signal that the symbol is the label of constructor section. *}
+.#define BSF_CONSTRUCTOR 0x800
+.
+. {* Signal that the symbol is a warning symbol. If the symbol
+. is a warning symbol, then the value field (I know this is
+. tacky) will point to the asymbol which when referenced will
+. cause the warning. *}
+.#define BSF_WARNING 0x1000
+.
+. {* Signal that the symbol is indirect. The value of the symbol
+. is a pointer to an undefined asymbol which contains the
+. name to use instead. *}
+.#define BSF_INDIRECT 0x2000
+.
+. {* BSF_FILE marks symbols that contain a file name. This is used
+. for ELF STT_FILE symbols. *}
+.#define BSF_FILE 0x4000
+.
+. flagword flags;
+.
+. {* A pointer to the section to which this symbol is
+. relative. This will always be non NULL, there are special
+. sections for undefined and absolute symbols *}
+. struct sec *section;
+.
+. {* Back end special data. This is being phased out in favour
+. of making this a union. *}
+. PTR udata;
+.
+.} asymbol;
+*/
+
+#include "bfd.h"
+#include "sysdep.h"
+
+#include "libbfd.h"
+#include "aout/stab_gnu.h"
+
+/*
+DOCDD
+INODE
+symbol handling functions, , typedef asymbol, Symbols
+SUBSECTION
+ Symbol Handling Functions
+*/
+
+/*
+FUNCTION
+ get_symtab_upper_bound
+
+DESCRIPTION
+ Returns the number of bytes required in a vector of pointers
+ to <<asymbols>> for all the symbols in the supplied BFD,
+ including a terminal NULL pointer. If there are no symbols in
+ the BFD, then 0 is returned.
+
+.#define get_symtab_upper_bound(abfd) \
+. BFD_SEND (abfd, _get_symtab_upper_bound, (abfd))
+
+*/
+
+/*
+FUNCTION
+ bfd_canonicalize_symtab
+
+DESCRIPTION
+ Supplied a BFD and a pointer to an uninitialized vector of
+ pointers. This reads in the symbols from the BFD, and fills in
+ the table with pointers to the symbols, and a trailing NULL.
+ The routine returns the actual number of symbol pointers not
+ including the NULL.
+
+
+.#define bfd_canonicalize_symtab(abfd, location) \
+. BFD_SEND (abfd, _bfd_canonicalize_symtab,\
+. (abfd, location))
+
+*/
+
+
+/*
+FUNCTION
+ bfd_set_symtab
+
+DESCRIPTION
+ Provided a table of pointers to symbols and a count, writes to
+ the output BFD the symbols when closed.
+
+SYNOPSIS
+ boolean bfd_set_symtab (bfd *, asymbol **, unsigned int );
+*/
+
+boolean
+bfd_set_symtab (abfd, location, symcount)
+ bfd *abfd;
+ asymbol **location;
+ unsigned int symcount;
+{
+ if ((abfd->format != bfd_object) || (bfd_read_p (abfd))) {
+ bfd_error = invalid_operation;
+ return false;
+ }
+
+ bfd_get_outsymbols (abfd) = location;
+ bfd_get_symcount (abfd) = symcount;
+ return true;
+}
+
+/*
+FUNCTION
+ bfd_print_symbol_vandf
+
+DESCRIPTION
+ Prints the value and flags of the symbol supplied to the stream file.
+
+SYNOPSIS
+ void bfd_print_symbol_vandf(PTR file, asymbol *symbol);
+*/
+void
+DEFUN(bfd_print_symbol_vandf,(file, symbol),
+PTR file AND
+asymbol *symbol)
+{
+ flagword type = symbol->flags;
+ if (symbol->section != (asection *)NULL)
+ {
+ fprintf_vma(file, symbol->value+symbol->section->vma);
+ }
+ else
+ {
+ fprintf_vma(file, symbol->value);
+ }
+ fprintf(file," %c%c%c%c%c%c%c",
+ (type & BSF_LOCAL) ? 'l':' ',
+ (type & BSF_GLOBAL) ? 'g' : ' ',
+ (type & BSF_WEAK) ? 'w' : ' ',
+ (type & BSF_CONSTRUCTOR) ? 'C' : ' ',
+ (type & BSF_WARNING) ? 'W' : ' ',
+ (type & BSF_INDIRECT) ? 'I' : ' ',
+ (type & BSF_DEBUGGING) ? 'd' :' ');
+
+}
+
+
+/*
+FUNCTION
+ bfd_make_empty_symbol
+
+DESCRIPTION
+ This function creates a new <<asymbol>> structure for the BFD,
+ and returns a pointer to it.
+
+ This routine is necessary, since each back end has private
+ information surrounding the <<asymbol>>. Building your own
+ <<asymbol>> and pointing to it will not create the private
+ information, and will cause problems later on.
+
+.#define bfd_make_empty_symbol(abfd) \
+. BFD_SEND (abfd, _bfd_make_empty_symbol, (abfd))
+*/
+
+/*
+FUNCTION
+ bfd_make_debug_symbol
+
+DESCRIPTION
+ This function creates a new <<asymbol>> structure for the BFD,
+ to be used as a debugging symbol. Further details of its use have
+ yet to be worked out.
+
+.#define bfd_make_debug_symbol(abfd,ptr,size) \
+. BFD_SEND (abfd, _bfd_make_debug_symbol, (abfd, ptr, size))
+*/
+
+struct section_to_type
+{
+ CONST char *section;
+ char type;
+};
+
+/* Map COFF section names to POSIX/BSD single-character symbol types.
+ This table is probably incomplete. It is sorted for convenience of
+ adding entries. Since it is so short, a linear search is used. */
+static CONST struct section_to_type stt[] = {
+ {"*DEBUG*", 'N'},
+ {".bss", 'b'},
+ {".data", 'd'},
+ {".sbss", 's'}, /* Small BSS (uninitialized data) */
+ {".scommon", 'c'}, /* Small common */
+ {".sdata", 'g'}, /* Small initialized data */
+ {".text", 't'},
+ {0, 0}
+};
+
+/* Return the single-character symbol type corresponding to
+ COFF section S, or '?' for an unknown COFF section. */
+
+static char
+coff_section_type (s)
+ char *s;
+{
+ CONST struct section_to_type *t;
+
+ for (t = &stt[0]; t->section; t++)
+ if (!strcmp (s, t->section))
+ return t->type;
+ return '?';
+}
+
+#ifndef islower
+#define islower(c) ((c) >= 'a' && (c) <= 'z')
+#endif
+#ifndef toupper
+#define toupper(c) (islower(c) ? ((c) & ~0x20) : (c))
+#endif
+
+/*
+FUNCTION
+ bfd_decode_symclass
+
+DESCRIPTION
+ Return a character corresponding to the symbol
+ class of symbol, or '?' for an unknown class.
+
+SYNOPSIS
+ int bfd_decode_symclass(asymbol *symbol);
+*/
+int
+DEFUN(bfd_decode_symclass,(symbol),
+asymbol *symbol)
+{
+ char c;
+
+ if (bfd_is_com_section (symbol->section))
+ return 'C';
+ if (symbol->section == &bfd_und_section)
+ return 'U';
+ if (symbol->section == &bfd_ind_section)
+ return 'I';
+ if (!(symbol->flags & (BSF_GLOBAL|BSF_LOCAL)))
+ return '?';
+
+ if (symbol->section == &bfd_abs_section)
+ c = 'a';
+ else if (symbol->section)
+ c = coff_section_type (symbol->section->name);
+ else
+ return '?';
+ if (symbol->flags & BSF_GLOBAL)
+ c = toupper (c);
+ return c;
+
+ /* We don't have to handle these cases just yet, but we will soon:
+ N_SETV: 'v';
+ N_SETA: 'l';
+ N_SETT: 'x';
+ N_SETD: 'z';
+ N_SETB: 's';
+ N_INDR: 'i';
+ */
+}
+
+/*
+FUNCTION
+ bfd_symbol_info
+
+DESCRIPTION
+ Fill in the basic info about symbol that nm needs.
+ Additional info may be added by the back-ends after
+ calling this function.
+
+SYNOPSIS
+ void bfd_symbol_info(asymbol *symbol, symbol_info *ret);
+*/
+
+void
+DEFUN(bfd_symbol_info,(symbol, ret),
+ asymbol *symbol AND
+ symbol_info *ret)
+{
+ ret->type = bfd_decode_symclass (symbol);
+ if (ret->type != 'U')
+ ret->value = symbol->value+symbol->section->vma;
+ else
+ ret->value = 0;
+ ret->name = symbol->name;
+}
+
+void
+bfd_symbol_is_absolute()
+{
+ abort();
+}
+
diff --git a/gnu/usr.bin/gdb/bfd/sysdep.h b/gnu/usr.bin/gdb/bfd/sysdep.h
new file mode 100644
index 0000000..dd7328b
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/sysdep.h
@@ -0,0 +1,47 @@
+#ifndef hosts_i386bsd_H
+/* Intel 386 running any BSD Unix */
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/file.h>
+#include <machine/param.h>
+#include <machine/vmparam.h>
+
+#ifndef O_ACCMODE
+#define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
+#endif
+
+#define SEEK_SET 0
+#define SEEK_CUR 1
+
+#define HOST_PAGE_SIZE NBPG
+#define HOST_MACHINE_ARCH bfd_arch_i386
+#define HOST_TEXT_START_ADDR USRTEXT
+
+/* Jolitz suggested defining HOST_STACK_END_ADDR to
+ (u.u_kproc.kp_eproc.e_vm.vm_maxsaddr + MAXSSIZ), which should work on
+ both BSDI and 386BSD, but that is believed not to work for BSD 4.4. */
+
+#ifdef __bsdi__
+/* This seems to be the right thing for BSDI. */
+#define HOST_STACK_END_ADDR USRSTACK
+#else
+/* This seems to be the right thing for 386BSD release 0.1. */
+#define HOST_STACK_END_ADDR (USRSTACK - MAXSSIZ)
+#endif
+
+#define HOST_DATA_START_ADDR ((bfd_vma)u.u_kproc.kp_eproc.e_vm.vm_daddr)
+
+#define TRAD_UNIX_CORE_FILE_FAILING_SIGNAL(core_bfd) \
+ ((core_bfd)->tdata.trad_core_data->u.u_sig)
+#define u_comm u_kproc.kp_proc.p_comm
+
+#include "fopen-same.h"
+
+#define hosts_i386bsd_H
+#endif
diff --git a/gnu/usr.bin/gdb/bfd/targets.c b/gnu/usr.bin/gdb/bfd/targets.c
new file mode 100644
index 0000000..d6a9665
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/targets.c
@@ -0,0 +1,635 @@
+/* Generic target-file-type support for the BFD library.
+ Copyright 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+ Written by Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+
+/*
+SECTION
+ Targets
+
+DESCRIPTION
+ Each port of BFD to a different machine requries the creation
+ of a target back end. All the back end provides to the root
+ part of BFD is a structure containing pointers to functions
+ which perform certain low level operations on files. BFD
+ translates the applications's requests through a pointer into
+ calls to the back end routines.
+
+ When a file is opened with <<bfd_openr>>, its format and
+ target are unknown. BFD uses various mechanisms to determine
+ how to interpret the file. The operations performed are:
+
+ o First a BFD is created by calling the internal routine
+ <<new_bfd>>, then <<bfd_find_target>> is called with the
+ target string supplied to <<bfd_openr>> and the new BFD pointer.
+
+ o If a null target string was provided to <<bfd_find_target>>,
+ it looks up the environment variable <<GNUTARGET>> and uses
+ that as the target string.
+
+ o If the target string is still NULL, or the target string is
+ <<default>>, then the first item in the target vector is used
+ as the target type, and <<target_defaulted>> is set to
+ cause <<bfd_check_format>> to loop through all the targets.
+ @xref{bfd_target}. @xref{Formats}.
+
+ o Otherwise, the elements in the target vector are inspected
+ one by one, until a match on target name is found. When found,
+ that is used.
+
+ o Otherwise the error <<invalid_target>> is returned to
+ <<bfd_openr>>.
+
+ o <<bfd_openr>> attempts to open the file using
+ <<bfd_open_file>>, and returns the BFD.
+
+ Once the BFD has been opened and the target selected, the file
+ format may be determined. This is done by calling
+ <<bfd_check_format>> on the BFD with a suggested format.
+ If <<target_defaulted>> has been set, each possible target
+ type is tried to see if it recognizes the specified format. The
+ routine returns <<true>> when the application guesses right.
+@menu
+@* bfd_target::
+@end menu
+*/
+
+
+/*
+
+INODE
+ bfd_target, , Targets, Targets
+DOCDD
+SUBSECTION
+ bfd_target
+
+DESCRIPTION
+ This structure contains everything that BFD knows about a
+ target. It includes things like its byte order, name, what
+ routines to call to do various operations, etc.
+
+ Every BFD points to a target structure with its <<xvec>>
+ member.
+
+ These macros are used to dispatch to functions through the
+ bfd_target vector. They are used in a number of macros further
+ down in @file{bfd.h}, and are also used when calling various
+ routines by hand inside the BFD implementation. The "arglist"
+ argument must be parenthesized; it contains all the arguments
+ to the called function.
+
+ They make the documentation (more) unpleasant to read, so if
+ someone wants to fix this and not break the above, please do.
+
+.#define BFD_SEND(bfd, message, arglist) \
+. ((*((bfd)->xvec->message)) arglist)
+
+ For operations which index on the BFD format
+
+.#define BFD_SEND_FMT(bfd, message, arglist) \
+. (((bfd)->xvec->message[(int)((bfd)->format)]) arglist)
+
+ This is the struct which defines the type of BFD this is. The
+ <<xvec>> member of the struct <<bfd>> itself points here. Each
+ module that implements access to a different target under BFD,
+ defines one of these.
+
+
+ FIXME, these names should be rationalised with the names of
+ the entry points which call them. Too bad we can't have one
+ macro to define them both!
+
+.typedef struct bfd_target
+.{
+
+Identifies the kind of target, eg SunOS4, Ultrix, etc.
+
+. char *name;
+
+The "flavour" of a back end is a general indication about the contents
+of a file.
+
+. enum target_flavour {
+. bfd_target_unknown_flavour,
+. bfd_target_aout_flavour,
+. bfd_target_coff_flavour,
+. bfd_target_ecoff_flavour,
+. bfd_target_elf_flavour,
+. bfd_target_ieee_flavour,
+. bfd_target_nlm_flavour,
+. bfd_target_oasys_flavour,
+. bfd_target_tekhex_flavour,
+. bfd_target_srec_flavour,
+. bfd_target_hppa_flavour} flavour;
+
+The order of bytes within the data area of a file.
+
+. boolean byteorder_big_p;
+
+The order of bytes within the header parts of a file.
+
+. boolean header_byteorder_big_p;
+
+This is a mask of all the flags which an executable may have set -
+from the set <<NO_FLAGS>>, <<HAS_RELOC>>, ...<<D_PAGED>>.
+
+. flagword object_flags;
+
+This is a mask of all the flags which a section may have set - from
+the set <<SEC_NO_FLAGS>>, <<SEC_ALLOC>>, ...<<SET_NEVER_LOAD>>.
+
+. flagword section_flags;
+
+The character normally found at the front of a symbol
+(if any), perhaps _.
+
+. char symbol_leading_char;
+
+The pad character for filenames within an archive header.
+
+. char ar_pad_char;
+
+The maximum number of characters in an archive header.
+
+. unsigned short ar_max_namelen;
+
+The minimum alignment restriction for any section.
+
+. unsigned int align_power_min;
+
+Entries for byte swapping for data. These are different to the other
+entry points, since they don't take BFD as first arg. Certain other handlers
+could do the same.
+
+. bfd_vma (*bfd_getx64) PARAMS ((bfd_byte *));
+. bfd_signed_vma (*bfd_getx_signed_64) PARAMS ((bfd_byte *));
+. void (*bfd_putx64) PARAMS ((bfd_vma, bfd_byte *));
+. bfd_vma (*bfd_getx32) PARAMS ((bfd_byte *));
+. bfd_signed_vma (*bfd_getx_signed_32) PARAMS ((bfd_byte *));
+. void (*bfd_putx32) PARAMS ((bfd_vma, bfd_byte *));
+. bfd_vma (*bfd_getx16) PARAMS ((bfd_byte *));
+. bfd_signed_vma (*bfd_getx_signed_16) PARAMS ((bfd_byte *));
+. void (*bfd_putx16) PARAMS ((bfd_vma, bfd_byte *));
+
+Byte swapping for the headers
+
+. bfd_vma (*bfd_h_getx64) PARAMS ((bfd_byte *));
+. bfd_signed_vma (*bfd_h_getx_signed_64) PARAMS ((bfd_byte *));
+. void (*bfd_h_putx64) PARAMS ((bfd_vma, bfd_byte *));
+. bfd_vma (*bfd_h_getx32) PARAMS ((bfd_byte *));
+. bfd_signed_vma (*bfd_h_getx_signed_32) PARAMS ((bfd_byte *));
+. void (*bfd_h_putx32) PARAMS ((bfd_vma, bfd_byte *));
+. bfd_vma (*bfd_h_getx16) PARAMS ((bfd_byte *));
+. bfd_signed_vma (*bfd_h_getx_signed_16) PARAMS ((bfd_byte *));
+. void (*bfd_h_putx16) PARAMS ((bfd_vma, bfd_byte *));
+
+Format dependent routines: these are vectors of entry points
+within the target vector structure, one for each format to check.
+
+Check the format of a file being read. Return bfd_target * or zero.
+
+. struct bfd_target * (*_bfd_check_format[bfd_type_end]) PARAMS ((bfd *));
+
+Set the format of a file being written.
+
+. boolean (*_bfd_set_format[bfd_type_end]) PARAMS ((bfd *));
+
+Write cached information into a file being written, at bfd_close.
+
+. boolean (*_bfd_write_contents[bfd_type_end]) PARAMS ((bfd *));
+
+The following functions are defined in <<JUMP_TABLE>>. The idea is
+that the back end writer of <<foo>> names all the routines
+<<foo_>>@var{entry_point}, <<JUMP_TABLE>> will built the entries
+in this structure in the right order.
+
+Core file entry points
+
+. char * (*_core_file_failing_command) PARAMS ((bfd *));
+. int (*_core_file_failing_signal) PARAMS ((bfd *));
+. boolean (*_core_file_matches_executable_p) PARAMS ((bfd *, bfd *));
+
+Archive entry points
+
+. boolean (*_bfd_slurp_armap) PARAMS ((bfd *));
+. boolean (*_bfd_slurp_extended_name_table) PARAMS ((bfd *));
+. void (*_bfd_truncate_arname) PARAMS ((bfd *, CONST char *, char *));
+. boolean (*write_armap) PARAMS ((bfd *arch,
+. unsigned int elength,
+. struct orl *map,
+. unsigned int orl_count,
+. int stridx));
+
+Standard stuff.
+
+. boolean (*_close_and_cleanup) PARAMS ((bfd *));
+. boolean (*_bfd_set_section_contents) PARAMS ((bfd *, sec_ptr, PTR,
+. file_ptr, bfd_size_type));
+. boolean (*_bfd_get_section_contents) PARAMS ((bfd *, sec_ptr, PTR,
+. file_ptr, bfd_size_type));
+. boolean (*_new_section_hook) PARAMS ((bfd *, sec_ptr));
+
+Symbols and relocations
+
+. unsigned int (*_get_symtab_upper_bound) PARAMS ((bfd *));
+. unsigned int (*_bfd_canonicalize_symtab) PARAMS ((bfd *,
+. struct symbol_cache_entry **));
+. unsigned int (*_get_reloc_upper_bound) PARAMS ((bfd *, sec_ptr));
+. unsigned int (*_bfd_canonicalize_reloc) PARAMS ((bfd *, sec_ptr, arelent **,
+. struct symbol_cache_entry **));
+. struct symbol_cache_entry *
+. (*_bfd_make_empty_symbol) PARAMS ((bfd *));
+. void (*_bfd_print_symbol) PARAMS ((bfd *, PTR,
+. struct symbol_cache_entry *,
+. bfd_print_symbol_type));
+.#define bfd_print_symbol(b,p,s,e) BFD_SEND(b, _bfd_print_symbol, (b,p,s,e))
+. void (*_bfd_get_symbol_info) PARAMS ((bfd *,
+. struct symbol_cache_entry *,
+. symbol_info *));
+.#define bfd_get_symbol_info(b,p,e) BFD_SEND(b, _bfd_get_symbol_info, (b,p,e))
+
+. alent * (*_get_lineno) PARAMS ((bfd *, struct symbol_cache_entry *));
+.
+. boolean (*_bfd_set_arch_mach) PARAMS ((bfd *, enum bfd_architecture,
+. unsigned long));
+.
+. bfd * (*openr_next_archived_file) PARAMS ((bfd *arch, bfd *prev));
+.
+. boolean (*_bfd_find_nearest_line) PARAMS ((bfd *abfd,
+. struct sec *section, struct symbol_cache_entry **symbols,
+. bfd_vma offset, CONST char **file, CONST char **func,
+. unsigned int *line));
+.
+. int (*_bfd_stat_arch_elt) PARAMS ((bfd *, struct stat *));
+.
+. int (*_bfd_sizeof_headers) PARAMS ((bfd *, boolean));
+.
+. void (*_bfd_debug_info_start) PARAMS ((bfd *));
+. void (*_bfd_debug_info_end) PARAMS ((bfd *));
+. void (*_bfd_debug_info_accumulate) PARAMS ((bfd *, struct sec *));
+.
+. bfd_byte * (*_bfd_get_relocated_section_contents) PARAMS ((bfd *,
+. struct bfd_seclet *, bfd_byte *data,
+. boolean relocateable));
+.
+. boolean (*_bfd_relax_section) PARAMS ((bfd *, struct sec *,
+. struct symbol_cache_entry **));
+.
+. boolean (*_bfd_seclet_link) PARAMS ((bfd *, PTR data,
+. boolean relocateable));
+
+. {* See documentation on reloc types. *}
+. CONST struct reloc_howto_struct *
+. (*reloc_type_lookup) PARAMS ((bfd *abfd,
+. bfd_reloc_code_real_type code));
+.
+. {* Back-door to allow format-aware applications to create debug symbols
+. while using BFD for everything else. Currently used by the assembler
+. when creating COFF files. *}
+. asymbol * (*_bfd_make_debug_symbol) PARAMS ((
+. bfd *abfd,
+. void *ptr,
+. unsigned long size));
+
+Data for use by back-end routines, which isn't generic enough to belong
+in this structure.
+
+. PTR backend_data;
+.} bfd_target;
+
+*/
+
+/* All known xvecs (even those that don't compile on all systems).
+ Alphabetized for easy reference.
+ They are listed a second time below, since
+ we can't intermix extern's and initializers. */
+extern bfd_target a29kcoff_big_vec;
+extern bfd_target a_out_adobe_vec;
+extern bfd_target aout_mips_big_vec;
+extern bfd_target aout_mips_little_vec;
+extern bfd_target apollocoff_vec;
+extern bfd_target b_out_vec_big_host;
+extern bfd_target b_out_vec_little_host;
+extern bfd_target bfd_elf32_big_generic_vec;
+extern bfd_target bfd_elf32_bigmips_vec;
+extern bfd_target bfd_elf32_hppa_vec;
+extern bfd_target bfd_elf32_i386_vec;
+extern bfd_target bfd_elf32_i860_vec;
+extern bfd_target bfd_elf32_little_generic_vec;
+extern bfd_target bfd_elf32_littlemips_vec;
+extern bfd_target bfd_elf32_m68k_vec;
+extern bfd_target bfd_elf32_m88k_vec;
+extern bfd_target bfd_elf32_sparc_vec;
+extern bfd_target bfd_elf64_big_generic_vec;
+extern bfd_target bfd_elf64_little_generic_vec;
+extern bfd_target demo_64_vec;
+extern bfd_target ecoff_big_vec;
+extern bfd_target ecoff_little_vec;
+extern bfd_target ecoffalpha_little_vec;
+extern bfd_target h8300coff_vec;
+extern bfd_target h8500coff_vec;
+extern bfd_target host_aout_vec;
+extern bfd_target hp300bsd_vec;
+extern bfd_target hp300hpux_vec;
+extern bfd_target hppa_vec;
+extern bfd_target i386aout_vec;
+extern bfd_target i386bsd_vec;
+extern bfd_target netbsd386_vec;
+extern bfd_target freebsd386_vec;
+extern bfd_target i386coff_vec;
+extern bfd_target i386linux_vec;
+extern bfd_target i386lynx_aout_vec;
+extern bfd_target i386lynx_coff_vec;
+extern bfd_target icoff_big_vec;
+extern bfd_target icoff_little_vec;
+extern bfd_target ieee_vec;
+extern bfd_target m68kcoff_vec;
+extern bfd_target m68kcoffun_vec;
+extern bfd_target m68klynx_aout_vec;
+extern bfd_target m68klynx_coff_vec;
+extern bfd_target m88kbcs_vec;
+extern bfd_target newsos3_vec;
+extern bfd_target nlm32_big_generic_vec;
+extern bfd_target nlm32_i386_vec;
+extern bfd_target nlm32_little_generic_vec;
+extern bfd_target nlm64_big_generic_vec;
+extern bfd_target nlm64_little_generic_vec;
+extern bfd_target oasys_vec;
+extern bfd_target rs6000coff_vec;
+extern bfd_target shcoff_vec;
+extern bfd_target sunos_big_vec;
+extern bfd_target tekhex_vec;
+extern bfd_target we32kcoff_vec;
+extern bfd_target z8kcoff_vec;
+
+/* srec is always included. */
+extern bfd_target srec_vec;
+extern bfd_target symbolsrec_vec;
+
+/* All of the xvecs for core files. */
+extern bfd_target aix386_core_vec;
+extern bfd_target hpux_core_vec;
+extern bfd_target osf_core_vec;
+extern bfd_target sco_core_vec;
+extern bfd_target trad_core_vec;
+
+bfd_target *target_vector[] = {
+
+#ifdef SELECT_VECS
+
+ SELECT_VECS,
+
+#else /* not SELECT_VECS */
+
+#ifdef DEFAULT_VECTOR
+ &DEFAULT_VECTOR,
+#endif
+ /* This list is alphabetized to make it easy to compare
+ with other vector lists -- the decls above and
+ the case statement in configure.in.
+ Vectors that don't compile on all systems, or aren't finished,
+ should have an entry here with #if 0 around it, to show that
+ it wasn't omitted by mistake. */
+ &a29kcoff_big_vec,
+ &a_out_adobe_vec,
+#if 0 /* No one seems to use this. */
+ &aout_mips_big_vec,
+#endif
+ &aout_mips_little_vec,
+ &b_out_vec_big_host,
+ &b_out_vec_little_host,
+#if 0 /* No one seems to use this. */
+ &bfd_elf32_big_generic_vec,
+ &bfd_elf32_bigmips_vec,
+#endif
+#if 0
+ &bfd_elf32_hppa_vec,
+#endif
+ &bfd_elf32_i386_vec,
+ &bfd_elf32_i860_vec,
+#if 0 /* No one seems to use this. */
+ &bfd_elf32_little_generic_vec,
+ &bfd_elf32_littlemips_vec,
+#endif
+ &bfd_elf32_m68k_vec,
+ &bfd_elf32_m88k_vec,
+ &bfd_elf32_sparc_vec,
+#ifdef BFD64 /* No one seems to use this. */
+ &bfd_elf64_big_generic_vec,
+ &bfd_elf64_little_generic_vec,
+#endif
+#ifdef BFD64
+ &demo_64_vec, /* Only compiled if host has long-long support */
+#endif
+ &ecoff_big_vec,
+ &ecoff_little_vec,
+#if 0
+ &ecoffalpha_little_vec,
+#endif
+ &h8300coff_vec,
+ &h8500coff_vec,
+#if 0
+ &host_aout_vec,
+#endif
+#if 0 /* Clashes with sunos_big_vec magic no. */
+ &hp300bsd_vec,
+#endif
+ &hp300hpux_vec,
+#if defined (HOST_HPPAHPUX) || defined (HOST_HPPABSD)
+ &hppa_vec,
+#endif
+ &i386aout_vec,
+ &i386bsd_vec,
+ &netbsd386_vec,
+ &freebsd386_vec,
+ &i386coff_vec,
+#if 0
+ &i386linux_vec,
+#endif
+ &i386lynx_aout_vec,
+ &i386lynx_coff_vec,
+ &icoff_big_vec,
+ &icoff_little_vec,
+ &ieee_vec,
+ &m68kcoff_vec,
+ &m68kcoffun_vec,
+ &m68klynx_aout_vec,
+ &m68klynx_coff_vec,
+ &m88kbcs_vec,
+ &newsos3_vec,
+#if 0 /* No one seems to use this. */
+ &nlm32_big_generic_vec,
+#endif
+ &nlm32_i386_vec,
+#if 0 /* No one seems to use this. */
+ &nlm32_little_generic_vec,
+#endif
+#ifdef BFD64
+ &nlm64_big_generic_vec,
+ &nlm64_little_generic_vec,
+#endif
+#if 0
+ /* We have no oasys tools anymore, so we can't test any of this
+ anymore. If you want to test the stuff yourself, go ahead...
+ steve@cygnus.com
+ Worse, since there is no magic number for archives, there
+ can be annoying target mis-matches. */
+ &oasys_vec,
+#endif
+ &rs6000coff_vec,
+ &shcoff_vec,
+ &sunos_big_vec,
+#if 0
+ &tekhex_vec,
+#endif
+ &we32kcoff_vec,
+ &z8kcoff_vec,
+
+#endif /* not SELECT_VECS */
+
+/* Always support S-records, for convenience. */
+ &srec_vec,
+ &symbolsrec_vec,
+
+/* Add any required traditional-core-file-handler. */
+
+#ifdef AIX386_CORE
+ &aix386_core_vec,
+#endif
+#ifdef HPUX_CORE
+ &hpux_core_vec,
+#endif
+#ifdef OSF_CORE
+ &osf_core_vec,
+#endif
+#ifdef SCO_CORE
+ &sco_core_vec,
+#endif
+#ifdef TRAD_CORE
+ &trad_core_vec,
+#endif
+
+ NULL /* end of list marker */
+};
+
+/* default_vector[0] contains either the address of the default vector,
+ if there is one, or zero if there isn't. */
+
+bfd_target *default_vector[] = {
+#ifdef DEFAULT_VECTOR
+ &DEFAULT_VECTOR,
+#endif
+ NULL
+};
+
+
+
+
+/*
+FUNCTION
+ bfd_find_target
+
+DESCRIPTION
+ Returns a pointer to the transfer vector for the object target
+ named target_name. If target_name is NULL, chooses the one in
+ the environment variable GNUTARGET; if that is null or not
+ defined thenthe first entry in the target list is chosen.
+ Passing in the string "default" or setting the environment
+ variable to "default" will cause the first entry in the target
+ list to be returned, and "target_defaulted" will be set in the
+ BFD. This causes <<bfd_check_format>> to loop over all the
+ targets to find the one that matches the file being read.
+
+SYNOPSIS
+ bfd_target *bfd_find_target(CONST char *, bfd *);
+*/
+
+bfd_target *
+DEFUN(bfd_find_target,(target_name, abfd),
+ CONST char *target_name AND
+ bfd *abfd)
+{
+ bfd_target **target;
+ extern char *getenv ();
+ CONST char *targname = (target_name ? target_name :
+ (CONST char *) getenv ("GNUTARGET"));
+
+ /* This is safe; the vector cannot be null */
+ if (targname == NULL || !strcmp (targname, "default")) {
+ abfd->target_defaulted = true;
+ return abfd->xvec = target_vector[0];
+ }
+
+ abfd->target_defaulted = false;
+
+ for (target = &target_vector[0]; *target != NULL; target++) {
+ if (!strcmp (targname, (*target)->name))
+ return abfd->xvec = *target;
+ }
+
+ bfd_error = invalid_target;
+ return NULL;
+}
+
+
+/*
+FUNCTION
+ bfd_target_list
+
+DESCRIPTION
+ This function returns a freshly malloced NULL-terminated
+ vector of the names of all the valid BFD targets. Do not
+ modify the names
+
+SYNOPSIS
+ CONST char **bfd_target_list(void);
+
+*/
+
+CONST char **
+DEFUN_VOID(bfd_target_list)
+{
+ int vec_length= 0;
+#ifdef NATIVE_HPPAHPUX_COMPILER
+ /* The native compiler on the HP9000/700 has a bug which causes it
+ to loop endlessly when compiling this file. This avoids it. */
+ volatile
+#endif
+ bfd_target **target;
+ CONST char **name_list, **name_ptr;
+
+ for (target = &target_vector[0]; *target != NULL; target++)
+ vec_length++;
+
+ name_ptr =
+ name_list = (CONST char **) zalloc ((vec_length + 1) * sizeof (char **));
+
+ if (name_list == NULL) {
+ bfd_error = no_memory;
+ return NULL;
+ }
+
+ for (target = &target_vector[0]; *target != NULL; target++)
+ *(name_ptr++) = (*target)->name;
+
+ return name_list;
+}
diff --git a/gnu/usr.bin/gdb/bfd/trad-core.c b/gnu/usr.bin/gdb/bfd/trad-core.c
new file mode 100644
index 0000000..203c80e
--- /dev/null
+++ b/gnu/usr.bin/gdb/bfd/trad-core.c
@@ -0,0 +1,384 @@
+/* BFD back end for traditional Unix core files (U-area and raw sections)
+ Copyright 1988, 1989, 1991, 1992, 1993 Free Software Foundation, Inc.
+ Written by John Gilmore of Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* To use this file on a particular host, configure the host with these
+ parameters in the config/h-HOST file:
+
+ HDEFINES=-DTRAD_CORE
+ HDEPFILES=trad-core.o
+
+ */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "libaout.h" /* BFD a.out internal data structures */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/dir.h>
+#include <signal.h>
+
+#include <sys/user.h> /* After a.out.h */
+#if 0
+/* file.h is included by std-host.h. So we better not try to include it
+ twice; on some systems (dpx2) it is not protected against multiple
+ inclusion. I have checked that all the hosts which use this file
+ include sys/file.h in the hosts file. */
+#include <sys/file.h>
+#endif
+
+#include <errno.h>
+
+ struct trad_core_struct
+ {
+ asection *data_section;
+ asection *stack_section;
+ asection *reg_section;
+ struct user u;
+ } *rawptr;
+
+#define core_upage(bfd) (&((bfd)->tdata.trad_core_data->u))
+#define core_datasec(bfd) ((bfd)->tdata.trad_core_data->data_section)
+#define core_stacksec(bfd) ((bfd)->tdata.trad_core_data->stack_section)
+#define core_regsec(bfd) ((bfd)->tdata.trad_core_data->reg_section)
+
+/* forward declarations */
+
+bfd_target * trad_unix_core_file_p PARAMS ((bfd *abfd));
+char * trad_unix_core_file_failing_command PARAMS ((bfd *abfd));
+int trad_unix_core_file_failing_signal PARAMS ((bfd *abfd));
+boolean trad_unix_core_file_matches_executable_p
+ PARAMS ((bfd *core_bfd, bfd *exec_bfd));
+
+/* Handle 4.2-style (and perhaps also sysV-style) core dump file. */
+
+/* ARGSUSED */
+bfd_target *
+trad_unix_core_file_p (abfd)
+ bfd *abfd;
+
+{
+ int val;
+ struct user u;
+
+#ifdef TRAD_CORE_USER_OFFSET
+ /* If defined, this macro is the file position of the user struct. */
+ if (bfd_seek (abfd, TRAD_CORE_USER_OFFSET, SEEK_SET) == 0)
+ return 0;
+#endif
+
+ val = bfd_read ((void *)&u, 1, sizeof u, abfd);
+ if (val != sizeof u)
+ {
+ /* Too small to be a core file */
+ bfd_error = wrong_format;
+ return 0;
+ }
+
+ /* Sanity check perhaps??? */
+ if (u.u_dsize > 0x1000000) /* Remember, it's in pages... */
+ {
+ bfd_error = wrong_format;
+ return 0;
+ }
+ if (u.u_ssize > 0x1000000)
+ {
+ bfd_error = wrong_format;
+ return 0;
+ }
+
+ /* Check that the size claimed is no greater than the file size. */
+ {
+ FILE *stream = bfd_cache_lookup (abfd);
+ struct stat statbuf;
+ if (stream == NULL)
+ return 0;
+ if (fstat (fileno (stream), &statbuf) < 0)
+ {
+ bfd_error = system_call_error;
+ return 0;
+ }
+ if (NBPG * (UPAGES + u.u_dsize + u.u_ssize) > statbuf.st_size)
+ {
+ bfd_error = file_truncated;
+ return 0;
+ }
+ if (NBPG * (UPAGES + u.u_dsize + u.u_ssize)
+#ifdef TRAD_CORE_EXTRA_SIZE_ALLOWED
+ /* Some systems write the file too big. */
+ + TRAD_CORE_EXTRA_SIZE_ALLOWED
+#endif
+ < statbuf.st_size)
+ {
+ /* The file is too big. Maybe it's not a core file
+ or we otherwise have bad values for u_dsize and u_ssize). */
+ bfd_error = wrong_format;
+ return 0;
+ }
+ }
+
+ /* OK, we believe you. You're a core file (sure, sure). */
+
+ /* Allocate both the upage and the struct core_data at once, so
+ a single free() will free them both. */
+ rawptr = (struct trad_core_struct *)
+ bfd_zalloc (abfd, sizeof (struct trad_core_struct));
+ if (rawptr == NULL) {
+ bfd_error = no_memory;
+ return 0;
+ }
+
+ abfd->tdata.trad_core_data = rawptr;
+
+ rawptr->u = u; /*Copy the uarea into the tdata part of the bfd */
+
+ /* Create the sections. This is raunchy, but bfd_close wants to free
+ them separately. */
+
+ core_stacksec(abfd) = (asection *) zalloc (sizeof (asection));
+ if (core_stacksec (abfd) == NULL) {
+ loser:
+ bfd_error = no_memory;
+ free ((void *)rawptr);
+ return 0;
+ }
+ core_datasec (abfd) = (asection *) zalloc (sizeof (asection));
+ if (core_datasec (abfd) == NULL) {
+ loser1:
+ free ((void *)core_stacksec (abfd));
+ goto loser;
+ }
+ core_regsec (abfd) = (asection *) zalloc (sizeof (asection));
+ if (core_regsec (abfd) == NULL) {
+ free ((void *)core_datasec (abfd));
+ goto loser1;
+ }
+
+ core_stacksec (abfd)->name = ".stack";
+ core_datasec (abfd)->name = ".data";
+ core_regsec (abfd)->name = ".reg";
+
+ core_stacksec (abfd)->flags = SEC_ALLOC + SEC_LOAD + SEC_HAS_CONTENTS;
+ core_datasec (abfd)->flags = SEC_ALLOC + SEC_LOAD + SEC_HAS_CONTENTS;
+ core_regsec (abfd)->flags = SEC_ALLOC + SEC_HAS_CONTENTS;
+
+ core_datasec (abfd)->_raw_size = NBPG * u.u_dsize;
+ core_stacksec (abfd)->_raw_size = NBPG * u.u_ssize;
+ core_regsec (abfd)->_raw_size = NBPG * UPAGES; /* Larger than sizeof struct u */
+
+ /* What a hack... we'd like to steal it from the exec file,
+ since the upage does not seem to provide it. FIXME. */
+#ifdef HOST_DATA_START_ADDR
+ core_datasec (abfd)->vma = HOST_DATA_START_ADDR;
+#else
+ core_datasec (abfd)->vma = HOST_TEXT_START_ADDR + (NBPG * u.u_tsize);
+#endif
+/* a hack, but it works for FreeBSD !! */
+#include <vm/vm_param.h>
+/* this should really be in <vm/vm_param.h>, but somebody forgot it */
+#ifndef vm_page_size
+#define vm_page_size 4096
+#endif
+#define HOST_STACK_START_ADDR trunc_page(u.u_kproc.kp_eproc.e_vm.vm_maxsaddr \
++ MAXSSIZ - ctob(u.u_ssize))
+#ifdef HOST_STACK_START_ADDR
+ core_stacksec (abfd)->vma = HOST_STACK_START_ADDR;
+#else
+ core_stacksec (abfd)->vma = HOST_STACK_END_ADDR - (NBPG * u.u_ssize);
+#endif
+
+ /* This is tricky. As the "register section", we give them the entire
+ upage and stack. u.u_ar0 points to where "register 0" is stored.
+ There are two tricks with this, though. One is that the rest of the
+ registers might be at positive or negative (or both) displacements
+ from *u_ar0. The other is that u_ar0 is sometimes an absolute address
+ in kernel memory, and on other systems it is an offset from the beginning
+ of the `struct user'.
+
+ As a practical matter, we don't know where the registers actually are,
+ so we have to pass the whole area to GDB. We encode the value of u_ar0
+ by setting the .regs section up so that its virtual memory address
+ 0 is at the place pointed to by u_ar0 (by setting the vma of the start
+ of the section to -u_ar0). GDB uses this info to locate the regs,
+ using minor trickery to get around the offset-or-absolute-addr problem. */
+ core_regsec (abfd)->vma = 0 - (int) u.u_ar0;
+
+ core_datasec (abfd)->filepos = NBPG * UPAGES;
+#ifdef TRAD_CORE_STACK_FILEPOS
+ core_stacksec (abfd)->filepos = TRAD_CORE_STACK_FILEPOS;
+#else
+ core_stacksec (abfd)->filepos = (NBPG * UPAGES) + NBPG * u.u_dsize;
+#endif
+ core_regsec (abfd)->filepos = 0; /* Register segment is the upage */
+
+ /* Align to word at least */
+ core_stacksec (abfd)->alignment_power = 2;
+ core_datasec (abfd)->alignment_power = 2;
+ core_regsec (abfd)->alignment_power = 2;
+
+ abfd->sections = core_stacksec (abfd);
+ core_stacksec (abfd)->next = core_datasec (abfd);
+ core_datasec (abfd)->next = core_regsec (abfd);
+ abfd->section_count = 3;
+
+ return abfd->xvec;
+}
+
+char *
+trad_unix_core_file_failing_command (abfd)
+ bfd *abfd;
+{
+#ifndef NO_CORE_COMMAND
+ char *com = abfd->tdata.trad_core_data->u.u_comm;
+ if (*com)
+ return com;
+ else
+#endif
+ return 0;
+}
+
+/* ARGSUSED */
+int
+trad_unix_core_file_failing_signal (ignore_abfd)
+ bfd *ignore_abfd;
+{
+#ifdef TRAD_UNIX_CORE_FILE_FAILING_SIGNAL
+ return TRAD_UNIX_CORE_FILE_FAILING_SIGNAL(ignore_abfd);
+#else
+ return -1; /* FIXME, where is it? */
+#endif
+}
+
+/* ARGSUSED */
+boolean
+trad_unix_core_file_matches_executable_p (core_bfd, exec_bfd)
+ bfd *core_bfd, *exec_bfd;
+{
+ return true; /* FIXME, We have no way of telling at this point */
+}
+
+/* No archive file support via this BFD */
+#define trad_unix_openr_next_archived_file bfd_generic_openr_next_archived_file
+#define trad_unix_generic_stat_arch_elt bfd_generic_stat_arch_elt
+#define trad_unix_slurp_armap bfd_false
+#define trad_unix_slurp_extended_name_table bfd_true
+#define trad_unix_write_armap (boolean (*) PARAMS \
+ ((bfd *arch, unsigned int elength, struct orl *map, \
+ unsigned int orl_count, int stridx))) bfd_false
+#define trad_unix_truncate_arname bfd_dont_truncate_arname
+#define aout_32_openr_next_archived_file bfd_generic_openr_next_archived_file
+
+#define trad_unix_close_and_cleanup bfd_generic_close_and_cleanup
+#define trad_unix_set_section_contents (boolean (*) PARAMS \
+ ((bfd *abfd, asection *section, PTR data, file_ptr offset, \
+ bfd_size_type count))) bfd_false
+#define trad_unix_get_section_contents bfd_generic_get_section_contents
+#define trad_unix_new_section_hook (boolean (*) PARAMS \
+ ((bfd *, sec_ptr))) bfd_true
+#define trad_unix_get_symtab_upper_bound bfd_0u
+#define trad_unix_get_symtab (unsigned int (*) PARAMS \
+ ((bfd *, struct symbol_cache_entry **))) bfd_0u
+#define trad_unix_get_reloc_upper_bound (unsigned int (*) PARAMS \
+ ((bfd *, sec_ptr))) bfd_0u
+#define trad_unix_canonicalize_reloc (unsigned int (*) PARAMS \
+ ((bfd *, sec_ptr, arelent **, struct symbol_cache_entry**))) bfd_0u
+#define trad_unix_make_empty_symbol (struct symbol_cache_entry * \
+ (*) PARAMS ((bfd *))) bfd_false
+#define trad_unix_print_symbol (void (*) PARAMS \
+ ((bfd *, PTR, struct symbol_cache_entry *, \
+ bfd_print_symbol_type))) bfd_false
+#define trad_unix_get_symbol_info (void (*) PARAMS \
+ ((bfd *, struct symbol_cache_entry *, \
+ symbol_info *))) bfd_false
+#define trad_unix_get_lineno (alent * (*) PARAMS \
+ ((bfd *, struct symbol_cache_entry *))) bfd_nullvoidptr
+#define trad_unix_set_arch_mach (boolean (*) PARAMS \
+ ((bfd *, enum bfd_architecture, unsigned long))) bfd_false
+#define trad_unix_find_nearest_line (boolean (*) PARAMS \
+ ((bfd *abfd, struct sec *section, \
+ struct symbol_cache_entry **symbols,bfd_vma offset, \
+ CONST char **file, CONST char **func, unsigned int *line))) bfd_false
+#define trad_unix_sizeof_headers (int (*) PARAMS \
+ ((bfd *, boolean))) bfd_0
+
+#define trad_unix_bfd_debug_info_start bfd_void
+#define trad_unix_bfd_debug_info_end bfd_void
+#define trad_unix_bfd_debug_info_accumulate (void (*) PARAMS \
+ ((bfd *, struct sec *))) bfd_void
+#define trad_unix_bfd_get_relocated_section_contents bfd_generic_get_relocated_section_contents
+#define trad_unix_bfd_relax_section bfd_generic_relax_section
+#define trad_unix_bfd_seclet_link \
+ ((boolean (*) PARAMS ((bfd *, PTR, boolean))) bfd_false)
+#define trad_unix_bfd_reloc_type_lookup \
+ ((CONST struct reloc_howto_struct *(*) PARAMS ((bfd *, bfd_reloc_code_real_type))) bfd_nullvoidptr)
+#define trad_unix_bfd_make_debug_symbol \
+ ((asymbol *(*) PARAMS ((bfd *, void *, unsigned long))) bfd_nullvoidptr)
+
+/* If somebody calls any byte-swapping routines, shoot them. */
+void
+swap_abort()
+{
+ abort(); /* This way doesn't require any declaration for ANSI to fuck up */
+}
+#define NO_GET ((bfd_vma (*) PARAMS (( bfd_byte *))) swap_abort )
+#define NO_PUT ((void (*) PARAMS ((bfd_vma, bfd_byte *))) swap_abort )
+#define NO_SIGNED_GET ((bfd_signed_vma (*) PARAMS ((bfd_byte *))) swap_abort )
+
+bfd_target trad_core_vec =
+ {
+ "trad-core",
+ bfd_target_unknown_flavour,
+ true, /* target byte order */
+ true, /* target headers byte order */
+ (HAS_RELOC | EXEC_P | /* object flags */
+ HAS_LINENO | HAS_DEBUG |
+ HAS_SYMS | HAS_LOCALS | DYNAMIC | WP_TEXT | D_PAGED),
+ (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* section flags */
+ 0, /* symbol prefix */
+ ' ', /* ar_pad_char */
+ 16, /* ar_max_namelen */
+ 3, /* minimum alignment power */
+ NO_GET, NO_SIGNED_GET, NO_PUT, /* 64 bit data */
+ NO_GET, NO_SIGNED_GET, NO_PUT, /* 32 bit data */
+ NO_GET, NO_SIGNED_GET, NO_PUT, /* 16 bit data */
+ NO_GET, NO_SIGNED_GET, NO_PUT, /* 64 bit hdrs */
+ NO_GET, NO_SIGNED_GET, NO_PUT, /* 32 bit hdrs */
+ NO_GET, NO_SIGNED_GET, NO_PUT, /* 16 bit hdrs */
+
+ { /* bfd_check_format */
+ _bfd_dummy_target, /* unknown format */
+ _bfd_dummy_target, /* object file */
+ _bfd_dummy_target, /* archive */
+ trad_unix_core_file_p /* a core file */
+ },
+ { /* bfd_set_format */
+ bfd_false, bfd_false,
+ bfd_false, bfd_false
+ },
+ { /* bfd_write_contents */
+ bfd_false, bfd_false,
+ bfd_false, bfd_false
+ },
+
+ JUMP_TABLE(trad_unix),
+ (PTR) 0 /* backend_data */
+};
diff --git a/gnu/usr.bin/gdb/blockframe.c b/gnu/usr.bin/gdb/blockframe.c
new file mode 100644
index 0000000..236d1cd
--- /dev/null
+++ b/gnu/usr.bin/gdb/blockframe.c
@@ -0,0 +1,622 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)blockframe.c 6.4 (Berkeley) 5/11/91";
+#endif /* not lint */
+
+/* Get info from stack frames;
+ convert between frames, blocks, functions and pc values.
+ Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "frame.h"
+
+#include <obstack.h>
+
+#if defined(NEWVM) && defined(KERNELDEBUG)
+#include <sys/param.h> /* XXX for FRAME_CHAIN_VALID */
+#endif
+
+/* Start and end of object file containing the entry point.
+ STARTUP_FILE_END is the first address of the next file.
+ This file is assumed to be a startup file
+ and frames with pc's inside it
+ are treated as nonexistent.
+
+ Setting these variables is necessary so that backtraces do not fly off
+ the bottom of the stack. */
+CORE_ADDR startup_file_start;
+CORE_ADDR startup_file_end;
+
+/* Is ADDR outside the startup file? */
+int
+outside_startup_file (addr)
+ CORE_ADDR addr;
+{
+ return !(addr >= startup_file_start && addr < startup_file_end);
+}
+
+/* Address of innermost stack frame (contents of FP register) */
+
+static FRAME current_frame;
+
+struct block *block_for_pc ();
+CORE_ADDR get_pc_function_start ();
+
+/*
+ * Cache for frame addresses already read by gdb. Valid only while
+ * inferior is stopped. Control variables for the frame cache should
+ * be local to this module.
+ */
+struct obstack frame_cache_obstack;
+
+/* Return the innermost (currently executing) stack frame. */
+
+FRAME
+get_current_frame ()
+{
+ /* We assume its address is kept in a general register;
+ param.h says which register. */
+
+ return current_frame;
+}
+
+void
+set_current_frame (frame)
+ FRAME frame;
+{
+ current_frame = frame;
+}
+
+FRAME
+create_new_frame (addr, pc)
+ FRAME_ADDR addr;
+ CORE_ADDR pc;
+{
+ struct frame_info *fci; /* Same type as FRAME */
+
+ fci = (struct frame_info *)
+ obstack_alloc (&frame_cache_obstack,
+ sizeof (struct frame_info));
+
+ /* Arbitrary frame */
+ fci->next = (struct frame_info *) 0;
+ fci->prev = (struct frame_info *) 0;
+ fci->frame = addr;
+ fci->next_frame = 0; /* Since arbitrary */
+ fci->pc = pc;
+
+#ifdef INIT_EXTRA_FRAME_INFO
+ INIT_EXTRA_FRAME_INFO (fci);
+#endif
+
+ return fci;
+}
+
+/* Return the frame that called FRAME.
+ If FRAME is the original frame (it has no caller), return 0. */
+
+FRAME
+get_prev_frame (frame)
+ FRAME frame;
+{
+ /* We're allowed to know that FRAME and "struct frame_info *" are
+ the same */
+ return get_prev_frame_info (frame);
+}
+
+/* Return the frame that FRAME calls (0 if FRAME is the innermost
+ frame). */
+
+FRAME
+get_next_frame (frame)
+ FRAME frame;
+{
+ /* We're allowed to know that FRAME and "struct frame_info *" are
+ the same */
+ return frame->next;
+}
+
+/*
+ * Flush the entire frame cache.
+ */
+void
+flush_cached_frames ()
+{
+ /* Since we can't really be sure what the first object allocated was */
+ obstack_free (&frame_cache_obstack, 0);
+ obstack_init (&frame_cache_obstack);
+
+ current_frame = (struct frame_info *) 0; /* Invalidate cache */
+}
+
+/* Return a structure containing various interesting information
+ about a specified stack frame. */
+/* How do I justify including this function? Well, the FRAME
+ identifier format has gone through several changes recently, and
+ it's not completely inconceivable that it could happen again. If
+ it does, have this routine around will help */
+
+struct frame_info *
+get_frame_info (frame)
+ FRAME frame;
+{
+ return frame;
+}
+
+/* If a machine allows frameless functions, it should define a macro
+ FRAMELESS_FUNCTION_INVOCATION(FI, FRAMELESS) in param.h. FI is the struct
+ frame_info for the frame, and FRAMELESS should be set to nonzero
+ if it represents a frameless function invocation. */
+
+/* Many machines which allow frameless functions can detect them using
+ this macro. Such machines should define FRAMELESS_FUNCTION_INVOCATION
+ to just call this macro. */
+#define FRAMELESS_LOOK_FOR_PROLOGUE(FI, FRAMELESS) \
+{ \
+ CORE_ADDR func_start, after_prologue; \
+ func_start = (get_pc_function_start ((FI)->pc) + \
+ FUNCTION_START_OFFSET); \
+ if (func_start) \
+ { \
+ after_prologue = func_start; \
+ SKIP_PROLOGUE (after_prologue); \
+ (FRAMELESS) = (after_prologue == func_start); \
+ } \
+ else \
+ /* If we can't find the start of the function, we don't really */ \
+ /* know whether the function is frameless, but we should be */ \
+ /* able to get a reasonable (i.e. best we can do under the */ \
+ /* circumstances) backtrace by saying that it isn't. */ \
+ (FRAMELESS) = 0; \
+}
+
+/* Return a structure containing various interesting information
+ about the frame that called NEXT_FRAME. Returns NULL
+ if there is no such frame. */
+
+struct frame_info *
+get_prev_frame_info (next_frame)
+ FRAME next_frame;
+{
+ FRAME_ADDR address;
+ struct frame_info *prev;
+ int fromleaf = 0;
+
+ /* If the requested entry is in the cache, return it.
+ Otherwise, figure out what the address should be for the entry
+ we're about to add to the cache. */
+
+ if (!next_frame)
+ {
+ if (!current_frame)
+ {
+ if (!have_inferior_p () && !have_core_file_p ())
+ fatal ("get_prev_frame_info: Called before cache primed. \"Shouldn't happen.\"");
+ else
+ error ("No inferior or core file.");
+ }
+
+ return current_frame;
+ }
+
+ /* If we have the prev one, return it */
+ if (next_frame->prev)
+ return next_frame->prev;
+
+ /* On some machines it is possible to call a function without
+ setting up a stack frame for it. On these machines, we
+ define this macro to take two args; a frameinfo pointer
+ identifying a frame and a variable to set or clear if it is
+ or isn't leafless. */
+#ifdef FRAMELESS_FUNCTION_INVOCATION
+ /* Still don't want to worry about this except on the innermost
+ frame. This macro will set FROMLEAF if NEXT_FRAME is a
+ frameless function invocation. */
+ if (!(next_frame->next))
+ {
+ FRAMELESS_FUNCTION_INVOCATION (next_frame, fromleaf);
+ if (fromleaf)
+ address = next_frame->frame;
+ }
+#endif
+
+ if (!fromleaf)
+ {
+ /* Two macros defined in param.h specify the machine-dependent
+ actions to be performed here.
+ First, get the frame's chain-pointer.
+ If that is zero, the frame is the outermost frame or a leaf
+ called by the outermost frame. This means that if start
+ calls main without a frame, we'll return 0 (which is fine
+ anyway).
+
+ Nope; there's a problem. This also returns when the current
+ routine is a leaf of main. This is unacceptable. We move
+ this to after the ffi test; I'd rather have backtraces from
+ start go curfluy than have an abort called from main not show
+ main. */
+ address = FRAME_CHAIN (next_frame);
+ if (!FRAME_CHAIN_VALID (address, next_frame))
+ return 0;
+ /* If this frame is a leaf, this will be superceeded by the
+ code below. */
+ address = FRAME_CHAIN_COMBINE (address, next_frame);
+ }
+ if (address == 0)
+ return 0;
+
+ prev = (struct frame_info *)
+ obstack_alloc (&frame_cache_obstack,
+ sizeof (struct frame_info));
+
+ if (next_frame)
+ next_frame->prev = prev;
+ prev->next = next_frame;
+ prev->prev = (struct frame_info *) 0;
+ prev->frame = address;
+ prev->next_frame = prev->next ? prev->next->frame : 0;
+
+#ifdef INIT_EXTRA_FRAME_INFO
+ INIT_EXTRA_FRAME_INFO(prev);
+#endif
+
+ /* This entry is in the frame queue now, which is good since
+ FRAME_SAVED_PC may use that queue to figure out it's value
+ (see m-sparc.h). We want the pc saved in the inferior frame. */
+ prev->pc = (fromleaf ? SAVED_PC_AFTER_CALL (next_frame) :
+ next_frame ? FRAME_SAVED_PC (next_frame) : read_pc ());
+
+ return prev;
+}
+
+CORE_ADDR
+get_frame_pc (frame)
+ FRAME frame;
+{
+ struct frame_info *fi;
+ fi = get_frame_info (frame);
+ return fi->pc;
+}
+
+/* Find the addresses in which registers are saved in FRAME. */
+
+void
+get_frame_saved_regs (frame_info_addr, saved_regs_addr)
+ struct frame_info *frame_info_addr;
+ struct frame_saved_regs *saved_regs_addr;
+{
+ FRAME_FIND_SAVED_REGS (frame_info_addr, *saved_regs_addr);
+}
+
+/* Return the innermost lexical block in execution
+ in a specified stack frame. The frame address is assumed valid. */
+
+struct block *
+get_frame_block (frame)
+ FRAME frame;
+{
+ struct frame_info *fi;
+ CORE_ADDR pc;
+
+ fi = get_frame_info (frame);
+
+ pc = fi->pc;
+ if (fi->next_frame != 0)
+ /* We are not in the innermost frame. We need to subtract one to
+ get the correct block, in case the call instruction was the
+ last instruction of the block. If there are any machines on
+ which the saved pc does not point to after the call insn, we
+ probably want to make fi->pc point after the call insn anyway. */
+ --pc;
+ return block_for_pc (pc);
+}
+
+struct block *
+get_current_block ()
+{
+ return block_for_pc (read_pc ());
+}
+
+CORE_ADDR
+get_pc_function_start (pc)
+ CORE_ADDR pc;
+{
+ register struct block *bl = block_for_pc (pc);
+ register struct symbol *symbol;
+ if (bl == 0 || (symbol = block_function (bl)) == 0)
+ {
+ register int misc_index = find_pc_misc_function (pc);
+ if (misc_index >= 0)
+ return misc_function_vector[misc_index].address;
+ return 0;
+ }
+ bl = SYMBOL_BLOCK_VALUE (symbol);
+ return BLOCK_START (bl);
+}
+
+/* Return the symbol for the function executing in frame FRAME. */
+
+struct symbol *
+get_frame_function (frame)
+ FRAME frame;
+{
+ register struct block *bl = get_frame_block (frame);
+ if (bl == 0)
+ return 0;
+ return block_function (bl);
+}
+
+/* Return the innermost lexical block containing the specified pc value,
+ or 0 if there is none. */
+
+extern struct symtab *psymtab_to_symtab ();
+
+struct block *
+block_for_pc (pc)
+ register CORE_ADDR pc;
+{
+ register struct block *b;
+ register int bot, top, half;
+ register struct symtab *s;
+ register struct partial_symtab *ps;
+ struct blockvector *bl;
+
+ /* First search all symtabs for one whose file contains our pc */
+
+ for (s = symtab_list; s; s = s->next)
+ {
+ bl = BLOCKVECTOR (s);
+ b = BLOCKVECTOR_BLOCK (bl, 0);
+ if (BLOCK_START (b) <= pc
+ && BLOCK_END (b) > pc)
+ break;
+ }
+
+ if (s == 0)
+ for (ps = partial_symtab_list; ps; ps = ps->next)
+ {
+ if (ps->textlow <= pc
+ && ps->texthigh > pc)
+ {
+ if (ps->readin)
+ fatal ("Internal error: pc found in readin psymtab and not in any symtab.");
+ s = psymtab_to_symtab (ps);
+ bl = BLOCKVECTOR (s);
+ b = BLOCKVECTOR_BLOCK (bl, 0);
+ break;
+ }
+ }
+
+ if (s == 0)
+ return 0;
+
+ /* Then search that symtab for the smallest block that wins. */
+ /* Use binary search to find the last block that starts before PC. */
+
+ bot = 0;
+ top = BLOCKVECTOR_NBLOCKS (bl);
+
+ while (top - bot > 1)
+ {
+ half = (top - bot + 1) >> 1;
+ b = BLOCKVECTOR_BLOCK (bl, bot + half);
+ if (BLOCK_START (b) <= pc)
+ bot += half;
+ else
+ top = bot + half;
+ }
+
+ /* Now search backward for a block that ends after PC. */
+
+ while (bot >= 0)
+ {
+ b = BLOCKVECTOR_BLOCK (bl, bot);
+ if (BLOCK_END (b) > pc)
+ return b;
+ bot--;
+ }
+
+ return 0;
+}
+
+/* Return the function containing pc value PC.
+ Returns 0 if function is not known. */
+
+struct symbol *
+find_pc_function (pc)
+ CORE_ADDR pc;
+{
+ register struct block *b = block_for_pc (pc);
+ if (b == 0)
+ return 0;
+ return block_function (b);
+}
+
+/* Finds the "function" (text symbol) that is smaller than PC
+ but greatest of all of the potential text symbols. Sets
+ *NAME and/or *ADDRESS conditionally if that pointer is non-zero.
+ Returns 0 if it couldn't find anything, 1 if it did. On a zero
+ return, *NAME and *ADDRESS are always set to zero. On a 1 return,
+ *NAME and *ADDRESS contain real information. */
+
+int
+find_pc_partial_function (pc, name, address)
+ CORE_ADDR pc;
+ char **name;
+ CORE_ADDR *address;
+{
+ struct partial_symtab *pst = find_pc_psymtab (pc);
+ struct symbol *f;
+ int miscfunc;
+ struct partial_symbol *psb;
+
+ if (pst)
+ {
+ if (pst->readin)
+ {
+ /* The information we want has already been read in.
+ We can go to the already readin symbols and we'll get
+ the best possible answer. */
+ f = find_pc_function (pc);
+ if (!f)
+ {
+ return_error:
+ /* No availible symbol. */
+ if (name != 0)
+ *name = 0;
+ if (address != 0)
+ *address = 0;
+ return 0;
+ }
+
+ if (name)
+ *name = SYMBOL_NAME (f);
+ if (address)
+ *address = BLOCK_START (SYMBOL_BLOCK_VALUE (f));
+ return 1;
+ }
+
+ /* Get the information from a combination of the pst
+ (static symbols), and the misc function vector (extern
+ symbols). */
+ miscfunc = find_pc_misc_function (pc);
+ psb = find_pc_psymbol (pst, pc);
+
+ if (!psb && miscfunc == -1)
+ {
+ goto return_error;
+ }
+ if (!psb
+ || (miscfunc != -1
+ && (SYMBOL_VALUE(psb)
+ < misc_function_vector[miscfunc].address)))
+ {
+ if (address)
+ *address = misc_function_vector[miscfunc].address;
+ if (name)
+ *name = misc_function_vector[miscfunc].name;
+ return 1;
+ }
+ else
+ {
+ if (address)
+ *address = SYMBOL_VALUE (psb);
+ if (name)
+ *name = SYMBOL_NAME (psb);
+ return 1;
+ }
+ }
+ else
+ /* Must be in the misc function stuff. */
+ {
+ miscfunc = find_pc_misc_function (pc);
+ if (miscfunc == -1)
+ goto return_error;
+ if (address)
+ *address = misc_function_vector[miscfunc].address;
+ if (name)
+ *name = misc_function_vector[miscfunc].name;
+ return 1;
+ }
+}
+
+/* Find the misc function whose address is the largest
+ while being less than PC. Return its index in misc_function_vector.
+ Returns -1 if PC is not in suitable range. */
+
+int
+find_pc_misc_function (pc)
+ register CORE_ADDR pc;
+{
+ register int lo = 0;
+ register int hi = misc_function_count-1;
+ register int new;
+ register int distance;
+
+ /* Note that the last thing in the vector is always _etext. */
+ /* Actually, "end", now that non-functions
+ go on the misc_function_vector. */
+
+ /* Above statement is not *always* true - fix for case where there are */
+ /* no misc functions at all (ie no symbol table has been read). */
+ if (hi < 0) return -1; /* no misc functions recorded */
+
+ /* trivial reject range test */
+ if (pc < misc_function_vector[0].address ||
+ pc > misc_function_vector[hi].address)
+ return -1;
+
+ /* Note that the following search will not return hi if
+ pc == misc_function_vector[hi].address. If "end" points to the
+ first unused location, this is correct and the above test
+ simply needs to be changed to
+ "pc >= misc_function_vector[hi].address". */
+ do {
+ new = (lo + hi) >> 1;
+ distance = misc_function_vector[new].address - pc;
+ if (distance == 0)
+ return new; /* an exact match */
+ else if (distance > 0)
+ hi = new;
+ else
+ lo = new;
+ } while (hi-lo != 1);
+
+ /* if here, we had no exact match, so return the lower choice */
+ return lo;
+}
+
+/* Return the innermost stack frame executing inside of the specified block,
+ or zero if there is no such frame. */
+
+FRAME
+block_innermost_frame (block)
+ struct block *block;
+{
+ struct frame_info *fi;
+ register FRAME frame;
+ register CORE_ADDR start = BLOCK_START (block);
+ register CORE_ADDR end = BLOCK_END (block);
+
+ frame = 0;
+ while (1)
+ {
+ frame = get_prev_frame (frame);
+ if (frame == 0)
+ return 0;
+ fi = get_frame_info (frame);
+ if (fi->pc >= start && fi->pc < end)
+ return frame;
+ }
+}
+
+void
+_initialize_blockframe ()
+{
+ obstack_init (&frame_cache_obstack);
+}
diff --git a/gnu/usr.bin/gdb/breakpoint.c b/gnu/usr.bin/gdb/breakpoint.c
new file mode 100644
index 0000000..b515ed3
--- /dev/null
+++ b/gnu/usr.bin/gdb/breakpoint.c
@@ -0,0 +1,1383 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)breakpoint.c 6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Everything about breakpoints, for GDB.
+ Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "frame.h"
+
+/* This is the sequence of bytes we insert for a breakpoint. */
+
+static char break_insn[] = BREAKPOINT;
+
+/* States of enablement of breakpoint.
+ `temporary' means disable when hit.
+ `delete' means delete when hit. */
+
+enum enable { disabled, enabled, temporary, delete};
+
+/* Not that the ->silent field is not currently used by any commands
+ (though the code is in there if it was to be and set_raw_breakpoint
+ does set it to 0). I implemented it because I thought it would be
+ useful for a hack I had to put in; I'm going to leave it in because
+ I can see how there might be times when it would indeed be useful */
+
+struct breakpoint
+{
+ struct breakpoint *next;
+ /* Number assigned to distinguish breakpoints. */
+ int number;
+ /* Address to break at. */
+ CORE_ADDR address;
+ /* Line number of this address. Redundant. */
+ int line_number;
+ /* Symtab of file of this address. Redundant. */
+ struct symtab *symtab;
+ /* Zero means disabled; remember the info but don't break here. */
+ enum enable enable;
+ /* Non-zero means a silent breakpoint (don't print frame info
+ if we stop here). */
+ unsigned char silent;
+ /* Number of stops at this breakpoint that should
+ be continued automatically before really stopping. */
+ int ignore_count;
+ /* "Real" contents of byte where breakpoint has been inserted.
+ Valid only when breakpoints are in the program. */
+ char shadow_contents[sizeof break_insn];
+ /* Nonzero if this breakpoint is now inserted. */
+ char inserted;
+ /* Nonzero if this is not the first breakpoint in the list
+ for the given address. */
+ char duplicate;
+ /* Chain of command lines to execute when this breakpoint is hit. */
+ struct command_line *commands;
+ /* Stack depth (address of frame). If nonzero, break only if fp
+ equals this. */
+ FRAME_ADDR frame;
+ /* Conditional. Break only if this expression's value is nonzero. */
+ struct expression *cond;
+};
+
+#define ALL_BREAKPOINTS(b) for (b = breakpoint_chain; b; b = b->next)
+
+/* Chain of all breakpoints defined. */
+
+struct breakpoint *breakpoint_chain;
+
+/* Number of last breakpoint made. */
+
+static int breakpoint_count;
+
+/* Default address, symtab and line to put a breakpoint at
+ for "break" command with no arg.
+ if default_breakpoint_valid is zero, the other three are
+ not valid, and "break" with no arg is an error.
+
+ This set by print_stack_frame, which calls set_default_breakpoint. */
+
+int default_breakpoint_valid;
+CORE_ADDR default_breakpoint_address;
+struct symtab *default_breakpoint_symtab;
+int default_breakpoint_line;
+
+/* Remaining commands (not yet executed)
+ of last breakpoint hit. */
+
+struct command_line *breakpoint_commands;
+
+static void delete_breakpoint ();
+void clear_momentary_breakpoints ();
+void breakpoint_auto_delete ();
+
+/* Flag indicating extra verbosity for xgdb. */
+extern int xgdb_verbose;
+
+/* condition N EXP -- set break condition of breakpoint N to EXP. */
+
+static void
+condition_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ register struct breakpoint *b;
+ register char *p;
+ register int bnum;
+ register struct expression *expr;
+
+ if (arg == 0)
+ error_no_arg ("breakpoint number");
+
+ p = arg;
+ while (*p >= '0' && *p <= '9') p++;
+ if (p == arg)
+ /* There is no number here. (e.g. "cond a == b"). */
+ error_no_arg ("breakpoint number");
+ bnum = atoi (arg);
+
+ ALL_BREAKPOINTS (b)
+ if (b->number == bnum)
+ {
+ if (b->cond)
+ {
+ free (b->cond);
+ b->cond = 0; /* parse_c_1 can leave this unchanged. */
+ }
+ if (*p == 0)
+ {
+ b->cond = 0;
+ if (from_tty)
+ printf ("Breakpoint %d now unconditional.\n", bnum);
+ }
+ else
+ {
+ if (*p != ' ' && *p != '\t')
+ error ("Arguments must be an integer (breakpoint number) and an expression.");
+
+ /* Find start of expression */
+ while (*p == ' ' || *p == '\t') p++;
+
+ arg = p;
+ b->cond = (struct expression *) parse_c_1 (&arg, block_for_pc (b->address), 0);
+ if (*arg)
+ error ("Junk at end of expression");
+ }
+ return;
+ }
+
+ error ("No breakpoint number %d.", bnum);
+}
+
+static void
+commands_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ register struct breakpoint *b;
+ register char *p, *p1;
+ register int bnum;
+ struct command_line *l;
+
+ /* If we allowed this, we would have problems with when to
+ free the storage, if we change the commands currently
+ being read from. */
+
+ if (breakpoint_commands)
+ error ("Can't use the \"commands\" command among a breakpoint's commands.");
+
+ /* Allow commands by itself to refer to the last breakpoint. */
+ if (arg == 0)
+ bnum = breakpoint_count;
+ else
+ {
+ p = arg;
+ if (! (*p >= '0' && *p <= '9'))
+ error ("Argument must be integer (a breakpoint number).");
+
+ while (*p >= '0' && *p <= '9') p++;
+ if (*p)
+ error ("Unexpected extra arguments following breakpoint number.");
+
+ bnum = atoi (arg);
+ }
+
+ ALL_BREAKPOINTS (b)
+ if (b->number == bnum)
+ {
+ if (from_tty && input_from_terminal_p ())
+ {
+ printf ("Type commands for when breakpoint %d is hit, one per line.\n\
+End with a line saying just \"end\".\n", bnum);
+ fflush (stdout);
+ }
+ l = read_command_lines (from_tty);
+ free_command_lines (b->commands);
+ b->commands = l;
+ return;
+ }
+ error ("No breakpoint number %d.", bnum);
+}
+
+/* Called from command loop to execute the commands
+ associated with the breakpoint we just stopped at. */
+
+void
+do_breakpoint_commands ()
+{
+ struct command_line *cmd;
+
+ while (cmd = breakpoint_commands)
+ {
+ breakpoint_commands = 0;
+ execute_command_lines(cmd);
+ /* If command was "cont", breakpoint_commands is now 0,
+ of if we stopped at yet another breakpoint which has commands,
+ it is now the commands for the new breakpoint. */
+ }
+ clear_momentary_breakpoints ();
+}
+
+/* Used when the program is proceeded, to eliminate any remaining
+ commands attached to the previous breakpoint we stopped at. */
+
+void
+clear_breakpoint_commands ()
+{
+ breakpoint_commands = 0;
+ breakpoint_auto_delete (0);
+}
+
+/* Functions to get and set the current list of pending
+ breakpoint commands. These are used by run_stack_dummy
+ to preserve the commands around a function call. */
+
+struct command_line *
+get_breakpoint_commands ()
+{
+ return breakpoint_commands;
+}
+
+void
+set_breakpoint_commands (cmds)
+ struct command_line *cmds;
+{
+ breakpoint_commands = cmds;
+}
+
+/* insert_breakpoints is used when starting or continuing the program.
+ remove_breakpoints is used when the program stops.
+ Both return zero if successful,
+ or an `errno' value if could not write the inferior. */
+
+int
+insert_breakpoints ()
+{
+ register struct breakpoint *b;
+ int val;
+
+#ifdef BREAKPOINT_DEBUG
+ printf ("Inserting breakpoints.\n");
+#endif /* BREAKPOINT_DEBUG */
+
+ ALL_BREAKPOINTS (b)
+ if (b->enable != disabled && ! b->inserted && ! b->duplicate)
+ {
+ read_memory (b->address, b->shadow_contents, sizeof break_insn);
+ val = write_memory (b->address, break_insn, sizeof break_insn);
+ if (val)
+ return val;
+#ifdef BREAKPOINT_DEBUG
+ printf ("Inserted breakpoint at 0x%x, shadow 0x%x, 0x%x.\n",
+ b->address, b->shadow_contents[0], b->shadow_contents[1]);
+#endif /* BREAKPOINT_DEBUG */
+ b->inserted = 1;
+ }
+ return 0;
+}
+
+int
+remove_breakpoints ()
+{
+ register struct breakpoint *b;
+ int val;
+
+#ifdef BREAKPOINT_DEBUG
+ printf ("Removing breakpoints.\n");
+#endif /* BREAKPOINT_DEBUG */
+
+ ALL_BREAKPOINTS (b)
+ if (b->inserted)
+ {
+ val = write_memory (b->address, b->shadow_contents, sizeof break_insn);
+ if (val)
+ return val;
+ b->inserted = 0;
+#ifdef BREAKPOINT_DEBUG
+ printf ("Removed breakpoint at 0x%x, shadow 0x%x, 0x%x.\n",
+ b->address, b->shadow_contents[0], b->shadow_contents[1]);
+#endif /* BREAKPOINT_DEBUG */
+ }
+
+ return 0;
+}
+
+/* Clear the "inserted" flag in all breakpoints.
+ This is done when the inferior is loaded. */
+
+void
+mark_breakpoints_out ()
+{
+ register struct breakpoint *b;
+
+ ALL_BREAKPOINTS (b)
+ b->inserted = 0;
+}
+
+/* breakpoint_here_p (PC) returns 1 if an enabled breakpoint exists at PC.
+ When continuing from a location with a breakpoint,
+ we actually single step once before calling insert_breakpoints. */
+
+int
+breakpoint_here_p (pc)
+ CORE_ADDR pc;
+{
+ register struct breakpoint *b;
+
+ ALL_BREAKPOINTS (b)
+ if (b->enable != disabled && b->address == pc)
+ return 1;
+
+ return 0;
+}
+
+/* Evaluate the expression EXP and return 1 if value is zero.
+ This is used inside a catch_errors to evaluate the breakpoint condition. */
+
+int
+breakpoint_cond_eval (exp)
+ struct expression *exp;
+{
+ return value_zerop (evaluate_expression (exp));
+}
+
+/* Return 0 if PC is not the address just after a breakpoint,
+ or -1 if breakpoint says do not stop now,
+ or -2 if breakpoint says it has deleted itself and don't stop,
+ or -3 if hit a breakpoint number -3 (delete when program stops),
+ or else the number of the breakpoint,
+ with 0x1000000 added (or subtracted, for a negative return value) for
+ a silent breakpoint. */
+
+int
+breakpoint_stop_status (pc, frame_address)
+ CORE_ADDR pc;
+ FRAME_ADDR frame_address;
+{
+ register struct breakpoint *b;
+ register int cont = 0;
+
+ /* Get the address where the breakpoint would have been. */
+ pc -= DECR_PC_AFTER_BREAK;
+
+ ALL_BREAKPOINTS (b)
+ if (b->enable != disabled && b->address == pc)
+ {
+ if (b->frame && b->frame != frame_address)
+ cont = -1;
+ else
+ {
+ int value_zero;
+ if (b->cond)
+ {
+ /* Need to select the frame, with all that implies
+ so that the conditions will have the right context. */
+ select_frame (get_current_frame (), 0);
+ value_zero
+ = catch_errors (breakpoint_cond_eval, b->cond,
+ "Error occurred in testing breakpoint condition.");
+ free_all_values ();
+ }
+ if (b->cond && value_zero)
+ {
+ cont = -1;
+ }
+ else if (b->ignore_count > 0)
+ {
+ b->ignore_count--;
+ cont = -1;
+ }
+ else
+ {
+ if (b->enable == temporary)
+ b->enable = disabled;
+ breakpoint_commands = b->commands;
+ if (b->silent
+ || (breakpoint_commands
+ && !strcmp ("silent", breakpoint_commands->line)))
+ {
+ if (breakpoint_commands)
+ breakpoint_commands = breakpoint_commands->next;
+ return (b->number > 0 ?
+ 0x1000000 + b->number :
+ b->number - 0x1000000);
+ }
+ return b->number;
+ }
+ }
+ }
+
+ return cont;
+}
+
+static void
+breakpoint_1 (bnum)
+ int bnum;
+{
+ register struct breakpoint *b;
+ register struct command_line *l;
+ register struct symbol *sym;
+ CORE_ADDR last_addr = (CORE_ADDR)-1;
+
+ ALL_BREAKPOINTS (b)
+ if (bnum == -1 || bnum == b->number)
+ {
+ printf_filtered ("#%-3d %c 0x%08x", b->number,
+ "nyod"[(int) b->enable],
+ b->address);
+ last_addr = b->address;
+ if (b->symtab)
+ {
+ sym = find_pc_function (b->address);
+ if (sym)
+ {
+ fputs_filtered (" in ", stdout);
+ fputs_demangled (SYMBOL_NAME (sym), stdout, 1);
+ fputs_filtered (" (", stdout);
+ }
+ fputs_filtered (b->symtab->filename, stdout);
+ printf_filtered (" line %d", b->line_number);
+ if (sym) fputs_filtered(")", stdout);
+ }
+ else
+ print_address_symbolic (b->address, stdout);
+
+ printf_filtered ("\n");
+
+ if (b->ignore_count)
+ printf_filtered ("\tignore next %d hits\n", b->ignore_count);
+ if (b->frame)
+ printf_filtered ("\tstop only in stack frame at 0x%x\n", b->frame);
+ if (b->cond)
+ {
+ printf_filtered ("\tbreak only if ");
+ print_expression (b->cond, stdout);
+ printf_filtered ("\n");
+ }
+ if (l = b->commands)
+ while (l)
+ {
+ printf_filtered ("\t%s\n", l->line);
+ l = l->next;
+ }
+ }
+
+ /* Compare against (CORE_ADDR)-1 in case some compiler decides
+ that a comparison of an unsigned with -1 is always false. */
+ if (last_addr != (CORE_ADDR)-1)
+ set_next_address (last_addr);
+}
+
+static void
+breakpoints_info (bnum_exp)
+ char *bnum_exp;
+{
+ int bnum = -1;
+
+ if (bnum_exp)
+ bnum = parse_and_eval_address (bnum_exp);
+ else if (breakpoint_chain == 0)
+ printf_filtered ("No breakpoints.\n");
+ else
+ printf_filtered ("Breakpoints:\n\
+Num Enb Address Where\n");
+
+ breakpoint_1 (bnum);
+}
+
+/* Print a message describing any breakpoints set at PC. */
+
+static void
+describe_other_breakpoints (pc)
+ register CORE_ADDR pc;
+{
+ register int others = 0;
+ register struct breakpoint *b;
+
+ ALL_BREAKPOINTS (b)
+ if (b->address == pc)
+ others++;
+ if (others > 0)
+ {
+ printf ("Note: breakpoint%s ", (others > 1) ? "s" : "");
+ ALL_BREAKPOINTS (b)
+ if (b->address == pc)
+ {
+ others--;
+ printf ("%d%s%s ",
+ b->number,
+ (b->enable == disabled) ? " (disabled)" : "",
+ (others > 1) ? "," : ((others == 1) ? " and" : ""));
+ }
+ printf ("also set at pc 0x%x.\n", pc);
+ }
+}
+
+/* Set the default place to put a breakpoint
+ for the `break' command with no arguments. */
+
+void
+set_default_breakpoint (valid, addr, symtab, line)
+ int valid;
+ CORE_ADDR addr;
+ struct symtab *symtab;
+ int line;
+{
+ default_breakpoint_valid = valid;
+ default_breakpoint_address = addr;
+ default_breakpoint_symtab = symtab;
+ default_breakpoint_line = line;
+}
+
+/* Rescan breakpoints at address ADDRESS,
+ marking the first one as "first" and any others as "duplicates".
+ This is so that the bpt instruction is only inserted once. */
+
+static void
+check_duplicates (address)
+ CORE_ADDR address;
+{
+ register struct breakpoint *b;
+ register int count = 0;
+
+ ALL_BREAKPOINTS (b)
+ if (b->enable != disabled && b->address == address)
+ {
+ count++;
+ b->duplicate = count > 1;
+ }
+}
+
+/* Low level routine to set a breakpoint.
+ Takes as args the three things that every breakpoint must have.
+ Returns the breakpoint object so caller can set other things.
+ Does not set the breakpoint number!
+ Does not print anything. */
+
+static struct breakpoint *
+set_raw_breakpoint (sal)
+ struct symtab_and_line sal;
+{
+ register struct breakpoint *b, *b1;
+
+ b = (struct breakpoint *) xmalloc (sizeof (struct breakpoint));
+ bzero (b, sizeof *b);
+ b->address = sal.pc;
+ b->symtab = sal.symtab;
+ b->line_number = sal.line;
+ b->enable = enabled;
+ b->next = 0;
+ b->silent = 0;
+
+ /* Add this breakpoint to the end of the chain
+ so that a list of breakpoints will come out in order
+ of increasing numbers. */
+
+ b1 = breakpoint_chain;
+ if (b1 == 0)
+ breakpoint_chain = b;
+ else
+ {
+ while (b1->next)
+ b1 = b1->next;
+ b1->next = b;
+ }
+
+ check_duplicates (sal.pc);
+
+ return b;
+}
+
+/* Set a breakpoint that will evaporate an end of command
+ at address specified by SAL.
+ Restrict it to frame FRAME if FRAME is nonzero. */
+
+void
+set_momentary_breakpoint (sal, frame)
+ struct symtab_and_line sal;
+ FRAME frame;
+{
+ register struct breakpoint *b;
+ b = set_raw_breakpoint (sal);
+ b->number = -3;
+ b->enable = delete;
+ b->frame = (frame ? FRAME_FP (frame) : 0);
+}
+
+void
+clear_momentary_breakpoints ()
+{
+ register struct breakpoint *b;
+ ALL_BREAKPOINTS (b)
+ if (b->number == -3)
+ {
+ delete_breakpoint (b);
+ break;
+ }
+}
+
+/* Set a breakpoint from a symtab and line.
+ If TEMPFLAG is nonzero, it is a temporary breakpoint.
+ Print the same confirmation messages that the breakpoint command prints. */
+
+void
+set_breakpoint (s, line, tempflag)
+ struct symtab *s;
+ int line;
+ int tempflag;
+{
+ register struct breakpoint *b;
+ struct symtab_and_line sal;
+
+ sal.symtab = s;
+ sal.line = line;
+ sal.pc = find_line_pc (sal.symtab, sal.line);
+ if (sal.pc == 0)
+ error ("No line %d in file \"%s\".\n", sal.line, sal.symtab->filename);
+ else
+ {
+ describe_other_breakpoints (sal.pc);
+
+ b = set_raw_breakpoint (sal);
+ b->number = ++breakpoint_count;
+ b->cond = 0;
+ if (tempflag)
+ b->enable = temporary;
+
+ printf ("Breakpoint %d at 0x%x", b->number, b->address);
+ if (b->symtab)
+ printf (": file %s, line %d.", b->symtab->filename, b->line_number);
+ printf ("\n");
+ }
+}
+
+/* Set a breakpoint according to ARG (function, linenum or *address)
+ and make it temporary if TEMPFLAG is nonzero. */
+
+static void
+break_command_1 (arg, tempflag, from_tty)
+ char *arg;
+ int tempflag, from_tty;
+{
+ struct symtabs_and_lines sals;
+ struct symtab_and_line sal;
+ register struct expression *cond = 0;
+ register struct breakpoint *b;
+ char *save_arg;
+ int i;
+ CORE_ADDR pc;
+
+ sals.sals = NULL;
+ sals.nelts = 0;
+
+ sal.line = sal.pc = sal.end = 0;
+ sal.symtab = 0;
+
+ /* If no arg given, or if first arg is 'if ', use the default breakpoint. */
+
+ if (!arg || (arg[0] == 'i' && arg[1] == 'f'
+ && (arg[2] == ' ' || arg[2] == '\t')))
+ {
+ if (default_breakpoint_valid)
+ {
+ sals.sals = (struct symtab_and_line *)
+ malloc (sizeof (struct symtab_and_line));
+ sal.pc = default_breakpoint_address;
+ sal.line = default_breakpoint_line;
+ sal.symtab = default_breakpoint_symtab;
+ sals.sals[0] = sal;
+ sals.nelts = 1;
+ }
+ else
+ error ("No default breakpoint address now.");
+ }
+ else
+ /* Force almost all breakpoints to be in terms of the
+ current_source_symtab (which is decode_line_1's default). This
+ should produce the results we want almost all of the time while
+ leaving default_breakpoint_* alone. */
+ if (default_breakpoint_valid
+ && (!current_source_symtab
+ || (arg && (*arg == '+' || *arg == '-'))))
+ sals = decode_line_1 (&arg, 1, default_breakpoint_symtab,
+ default_breakpoint_line);
+ else
+ sals = decode_line_1 (&arg, 1, 0, 0);
+
+ if (! sals.nelts)
+ return;
+
+ save_arg = arg;
+ for (i = 0; i < sals.nelts; i++)
+ {
+ sal = sals.sals[i];
+ if (sal.pc == 0 && sal.symtab != 0)
+ {
+ pc = find_line_pc (sal.symtab, sal.line);
+ if (pc == 0)
+ error ("No line %d in file \"%s\".",
+ sal.line, sal.symtab->filename);
+ }
+ else
+ pc = sal.pc;
+
+ while (arg && *arg)
+ {
+ if (arg[0] == 'i' && arg[1] == 'f'
+ && (arg[2] == ' ' || arg[2] == '\t'))
+ cond = (struct expression *) parse_c_1 ((arg += 2, &arg),
+ block_for_pc (pc), 0);
+ else
+ error ("Junk at end of arguments.");
+ }
+ arg = save_arg;
+ sals.sals[i].pc = pc;
+ }
+
+ for (i = 0; i < sals.nelts; i++)
+ {
+ sal = sals.sals[i];
+
+ if (from_tty)
+ describe_other_breakpoints (sal.pc);
+
+ b = set_raw_breakpoint (sal);
+ b->number = ++breakpoint_count;
+ b->cond = cond;
+ if (tempflag)
+ b->enable = temporary;
+
+ printf ("Breakpoint %d at 0x%x", b->number, b->address);
+ if (b->symtab)
+ printf (": file %s, line %d.", b->symtab->filename, b->line_number);
+ printf ("\n");
+ }
+
+ if (sals.nelts > 1)
+ {
+ printf ("Multiple breakpoints were set.\n");
+ printf ("Use the \"delete\" command to delete unwanted breakpoints.\n");
+ }
+ free (sals.sals);
+}
+
+static void
+break_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ break_command_1 (arg, 0, from_tty);
+}
+
+static void
+tbreak_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ break_command_1 (arg, 1, from_tty);
+}
+
+/*
+ * Helper routine for the until_command routine in infcmd.c. Here
+ * because it uses the mechanisms of breakpoints.
+ */
+void
+until_break_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ struct symtabs_and_lines sals;
+ struct symtab_and_line sal;
+ FRAME prev_frame = get_prev_frame (selected_frame);
+
+ clear_proceed_status ();
+
+ /* Set a breakpoint where the user wants it and at return from
+ this function */
+
+ if (default_breakpoint_valid)
+ sals = decode_line_1 (&arg, 1, default_breakpoint_symtab,
+ default_breakpoint_line);
+ else
+ sals = decode_line_1 (&arg, 1, 0, 0);
+
+ if (sals.nelts != 1)
+ error ("Couldn't get information on specified line.");
+
+ sal = sals.sals[0];
+ free (sals.sals); /* malloc'd, so freed */
+
+ if (*arg)
+ error ("Junk at end of arguments.");
+
+ if (sal.pc == 0 && sal.symtab != 0)
+ sal.pc = find_line_pc (sal.symtab, sal.line);
+
+ if (sal.pc == 0)
+ error ("No line %d in file \"%s\".", sal.line, sal.symtab->filename);
+
+ set_momentary_breakpoint (sal, selected_frame);
+
+ /* Keep within the current frame */
+
+ if (prev_frame)
+ {
+ struct frame_info *fi;
+
+ fi = get_frame_info (prev_frame);
+ sal = find_pc_line (fi->pc, 0);
+ sal.pc = fi->pc;
+ set_momentary_breakpoint (sal, prev_frame);
+ }
+
+ proceed (-1, -1, 0);
+}
+
+static void
+clear_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ register struct breakpoint *b, *b1;
+ struct symtabs_and_lines sals;
+ struct symtab_and_line sal;
+ register struct breakpoint *found;
+ int i;
+
+ if (arg)
+ {
+ sals = decode_line_spec (arg, 1);
+ }
+ else
+ {
+ sals.sals = (struct symtab_and_line *) malloc (sizeof (struct symtab_and_line));
+ sal.line = default_breakpoint_line;
+ sal.symtab = default_breakpoint_symtab;
+ sal.pc = 0;
+ if (sal.symtab == 0)
+ error ("No source file specified.");
+
+ sals.sals[0] = sal;
+ sals.nelts = 1;
+ }
+
+ for (i = 0; i < sals.nelts; i++)
+ {
+ /* If exact pc given, clear bpts at that pc.
+ But if sal.pc is zero, clear all bpts on specified line. */
+ sal = sals.sals[i];
+ found = (struct breakpoint *) 0;
+ while (breakpoint_chain
+ && (sal.pc ? breakpoint_chain->address == sal.pc
+ : (breakpoint_chain->symtab == sal.symtab
+ && breakpoint_chain->line_number == sal.line)))
+ {
+ b1 = breakpoint_chain;
+ breakpoint_chain = b1->next;
+ b1->next = found;
+ found = b1;
+ }
+
+ ALL_BREAKPOINTS (b)
+ while (b->next
+ && (sal.pc ? b->next->address == sal.pc
+ : (b->next->symtab == sal.symtab
+ && b->next->line_number == sal.line)))
+ {
+ b1 = b->next;
+ b->next = b1->next;
+ b1->next = found;
+ found = b1;
+ }
+
+ if (found == 0)
+ error ("No breakpoint at %s.", arg);
+
+ if (found->next) from_tty = 1; /* Always report if deleted more than one */
+ if (from_tty) printf ("Deleted breakpoint%s ", found->next ? "s" : "");
+ while (found)
+ {
+ if (from_tty) printf ("%d ", found->number);
+ b1 = found->next;
+ delete_breakpoint (found);
+ found = b1;
+ }
+ if (from_tty) putchar ('\n');
+ }
+ free (sals.sals);
+}
+
+/* Delete breakpoint number BNUM if it is a `delete' breakpoint.
+ This is called after breakpoint BNUM has been hit.
+ Also delete any breakpoint numbered -3 unless there are breakpoint
+ commands to be executed. */
+
+void
+breakpoint_auto_delete (bnum)
+ int bnum;
+{
+ register struct breakpoint *b;
+ if (bnum != 0)
+ ALL_BREAKPOINTS (b)
+ if (b->number == bnum)
+ {
+ if (b->enable == delete)
+ delete_breakpoint (b);
+ break;
+ }
+ if (breakpoint_commands == 0)
+ clear_momentary_breakpoints ();
+}
+
+static void
+delete_breakpoint (bpt)
+ struct breakpoint *bpt;
+{
+ register struct breakpoint *b;
+
+ if (bpt->inserted)
+ write_memory (bpt->address, bpt->shadow_contents, sizeof break_insn);
+
+ if (breakpoint_chain == bpt)
+ breakpoint_chain = bpt->next;
+
+ ALL_BREAKPOINTS (b)
+ if (b->next == bpt)
+ {
+ b->next = bpt->next;
+ break;
+ }
+
+ check_duplicates (bpt->address);
+
+ free_command_lines (bpt->commands);
+ if (bpt->cond)
+ free (bpt->cond);
+
+ if (xgdb_verbose && bpt->number >=0)
+ printf ("breakpoint #%d deleted\n", bpt->number);
+
+ free (bpt);
+}
+
+static void map_breakpoint_numbers ();
+
+static void
+delete_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ register struct breakpoint *b, *b1;
+
+ if (arg == 0)
+ {
+ /* Ask user only if there are some breakpoints to delete. */
+ if (!from_tty
+ || breakpoint_chain && query ("Delete all breakpoints? "))
+ {
+ /* No arg; clear all breakpoints. */
+ while (breakpoint_chain)
+ delete_breakpoint (breakpoint_chain);
+ }
+ }
+ else
+ map_breakpoint_numbers (arg, delete_breakpoint);
+}
+
+/* Delete all breakpoints.
+ Done when new symtabs are loaded, since the break condition expressions
+ may become invalid, and the breakpoints are probably wrong anyway. */
+
+void
+clear_breakpoints ()
+{
+ delete_command (0, 0);
+}
+
+/* Set ignore-count of breakpoint number BPTNUM to COUNT.
+ If from_tty is nonzero, it prints a message to that effect,
+ which ends with a period (no newline). */
+
+void
+set_ignore_count (bptnum, count, from_tty)
+ int bptnum, count, from_tty;
+{
+ register struct breakpoint *b;
+
+ if (count < 0)
+ count = 0;
+
+ ALL_BREAKPOINTS (b)
+ if (b->number == bptnum)
+ {
+ b->ignore_count = count;
+ if (!from_tty)
+ return;
+ else if (count == 0)
+ printf ("Will stop next time breakpoint %d is reached.", bptnum);
+ else if (count == 1)
+ printf ("Will ignore next crossing of breakpoint %d.", bptnum);
+ else
+ printf ("Will ignore next %d crossings of breakpoint %d.",
+ count, bptnum);
+ return;
+ }
+
+ error ("No breakpoint number %d.", bptnum);
+}
+
+/* Clear the ignore counts of all breakpoints. */
+void
+breakpoint_clear_ignore_counts ()
+{
+ struct breakpoint *b;
+
+ ALL_BREAKPOINTS (b)
+ b->ignore_count = 0;
+}
+
+/* Command to set ignore-count of breakpoint N to COUNT. */
+
+static void
+ignore_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ register char *p = args;
+ register int num;
+
+ if (p == 0)
+ error_no_arg ("a breakpoint number");
+
+ while (*p >= '0' && *p <= '9') p++;
+ if (*p && *p != ' ' && *p != '\t')
+ error ("First argument must be a breakpoint number.");
+
+ num = atoi (args);
+
+ if (*p == 0)
+ error ("Second argument (specified ignore-count) is missing.");
+
+ set_ignore_count (num, parse_and_eval_address (p), from_tty);
+ printf ("\n");
+}
+
+/* Call FUNCTION on each of the breakpoints
+ whose numbers are given in ARGS. */
+
+static void
+map_breakpoint_numbers (args, function)
+ char *args;
+ void (*function) ();
+{
+ register char *p = args;
+ register char *p1;
+ register int num;
+ register struct breakpoint *b;
+
+ if (p == 0)
+ error_no_arg ("one or more breakpoint numbers");
+
+ while (*p)
+ {
+ p1 = p;
+ while (*p1 >= '0' && *p1 <= '9') p1++;
+ if (*p1 && *p1 != ' ' && *p1 != '\t')
+ error ("Arguments must be breakpoint numbers.");
+
+ num = atoi (p);
+
+ ALL_BREAKPOINTS (b)
+ if (b->number == num)
+ {
+ function (b);
+ goto win;
+ }
+ printf ("No breakpoint number %d.\n", num);
+ win:
+ p = p1;
+ while (*p == ' ' || *p == '\t') p++;
+ }
+}
+
+static void
+enable_breakpoint (bpt)
+ struct breakpoint *bpt;
+{
+ bpt->enable = enabled;
+
+ if (xgdb_verbose && bpt->number >= 0)
+ printf ("breakpoint #%d enabled\n", bpt->number);
+
+ check_duplicates (bpt->address);
+}
+
+static void
+enable_command (args)
+ char *args;
+{
+ struct breakpoint *bpt;
+ if (args == 0)
+ ALL_BREAKPOINTS (bpt)
+ enable_breakpoint (bpt);
+ else
+ map_breakpoint_numbers (args, enable_breakpoint);
+}
+
+static void
+disable_breakpoint (bpt)
+ struct breakpoint *bpt;
+{
+ bpt->enable = disabled;
+
+ if (xgdb_verbose && bpt->number >= 0)
+ printf ("breakpoint #%d disabled\n", bpt->number);
+
+ check_duplicates (bpt->address);
+}
+
+static void
+disable_command (args)
+ char *args;
+{
+ register struct breakpoint *bpt;
+ if (args == 0)
+ ALL_BREAKPOINTS (bpt)
+ disable_breakpoint (bpt);
+ else
+ map_breakpoint_numbers (args, disable_breakpoint);
+}
+
+static void
+enable_once_breakpoint (bpt)
+ struct breakpoint *bpt;
+{
+ bpt->enable = temporary;
+
+ check_duplicates (bpt->address);
+}
+
+static void
+enable_once_command (args)
+ char *args;
+{
+ map_breakpoint_numbers (args, enable_once_breakpoint);
+}
+
+static void
+enable_delete_breakpoint (bpt)
+ struct breakpoint *bpt;
+{
+ bpt->enable = delete;
+
+ check_duplicates (bpt->address);
+}
+
+static void
+enable_delete_command (args)
+ char *args;
+{
+ map_breakpoint_numbers (args, enable_delete_breakpoint);
+}
+
+/*
+ * Use default_breakpoint_'s, or nothing if they aren't valid.
+ */
+struct symtabs_and_lines
+decode_line_spec_1 (string, funfirstline)
+ char *string;
+ int funfirstline;
+{
+ struct symtabs_and_lines sals;
+ if (string == 0)
+ error ("Empty line specification.");
+ if (default_breakpoint_valid)
+ sals = decode_line_1 (&string, funfirstline,
+ default_breakpoint_symtab, default_breakpoint_line);
+ else
+ sals = decode_line_1 (&string, funfirstline, 0, 0);
+ if (*string)
+ error ("Junk at end of line specification: %s", string);
+ return sals;
+}
+
+
+/* Chain containing all defined enable commands. */
+
+extern struct cmd_list_element
+ *enablelist, *disablelist,
+ *deletelist, *enablebreaklist;
+
+extern struct cmd_list_element *cmdlist;
+
+void
+_initialize_breakpoint ()
+{
+ breakpoint_chain = 0;
+ breakpoint_count = 0;
+
+ add_com ("ignore", class_breakpoint, ignore_command,
+ "Set ignore-count of breakpoint number N to COUNT.");
+
+ add_com ("commands", class_breakpoint, commands_command,
+ "Set commands to be executed when a breakpoint is hit.\n\
+Give breakpoint number as argument after \"commands\".\n\
+With no argument, the targeted breakpoint is the last one set.\n\
+The commands themselves follow starting on the next line.\n\
+Type a line containing \"end\" to indicate the end of them.\n\
+Give \"silent\" as the first line to make the breakpoint silent;\n\
+then no output is printed when it is hit, except what the commands print.");
+
+ add_com ("condition", class_breakpoint, condition_command,
+ "Specify breakpoint number N to break only if COND is true.\n\
+N is an integer; COND is a C expression to be evaluated whenever\n\
+breakpoint N is reached. Actually break only when COND is nonzero.");
+
+ add_com ("tbreak", class_breakpoint, tbreak_command,
+ "Set a temporary breakpoint. Args like \"break\" command.\n\
+Like \"break\" except the breakpoint is only enabled temporarily,\n\
+so it will be disabled when hit. Equivalent to \"break\" followed\n\
+by using \"enable once\" on the breakpoint number.");
+
+ add_prefix_cmd ("enable", class_breakpoint, enable_command,
+ "Enable some breakpoints or auto-display expressions.\n\
+Give breakpoint numbers (separated by spaces) as arguments.\n\
+With no subcommand, breakpoints are enabled until you command otherwise.\n\
+This is used to cancel the effect of the \"disable\" command.\n\
+With a subcommand you can enable temporarily.\n\
+\n\
+The \"display\" subcommand applies to auto-displays instead of breakpoints.",
+ &enablelist, "enable ", 1, &cmdlist);
+
+ add_abbrev_prefix_cmd ("breakpoints", class_breakpoint, enable_command,
+ "Enable some breakpoints or auto-display expressions.\n\
+Give breakpoint numbers (separated by spaces) as arguments.\n\
+With no subcommand, breakpoints are enabled until you command otherwise.\n\
+This is used to cancel the effect of the \"disable\" command.\n\
+May be abbreviates to simply \"enable\".\n\
+With a subcommand you can enable temporarily.",
+ &enablebreaklist, "enable breakpoints ", 1, &enablelist);
+
+ add_cmd ("once", no_class, enable_once_command,
+ "Enable breakpoints for one hit. Give breakpoint numbers.\n\
+If a breakpoint is hit while enabled in this fashion, it becomes disabled.\n\
+See the \"tbreak\" command which sets a breakpoint and enables it once.",
+ &enablebreaklist);
+
+ add_cmd ("delete", no_class, enable_delete_command,
+ "Enable breakpoints and delete when hit. Give breakpoint numbers.\n\
+If a breakpoint is hit while enabled in this fashion, it is deleted.",
+ &enablebreaklist);
+
+ add_cmd ("delete", no_class, enable_delete_command,
+ "Enable breakpoints and delete when hit. Give breakpoint numbers.\n\
+If a breakpoint is hit while enabled in this fashion, it is deleted.",
+ &enablelist);
+
+ add_cmd ("once", no_class, enable_once_command,
+ "Enable breakpoints for one hit. Give breakpoint numbers.\n\
+If a breakpoint is hit while enabled in this fashion, it becomes disabled.\n\
+See the \"tbreak\" command which sets a breakpoint and enables it once.",
+ &enablelist);
+
+ add_prefix_cmd ("disable", class_breakpoint, disable_command,
+ "Disable some breakpoints or auto-display expressions.\n\
+Arguments are breakpoint numbers with spaces in between.\n\
+To disable all breakpoints, give no argument.\n\
+A disabled breakpoint is not forgotten, but has no effect until reenabled.\n\
+\n\
+The \"display\" subcommand applies to auto-displays instead of breakpoints.",
+ &disablelist, "disable ", 1, &cmdlist);
+ add_com_alias ("dis", "disable", class_breakpoint, 1);
+ add_com_alias ("disa", "disable", class_breakpoint, 1);
+
+ add_abbrev_cmd ("breakpoints", class_breakpoint, disable_command,
+ "Disable some breakpoints or auto-display expressions.\n\
+Arguments are breakpoint numbers with spaces in between.\n\
+To disable all breakpoints, give no argument.\n\
+A disabled breakpoint is not forgotten, but has no effect until reenabled.\n\
+This command may be abbreviated \"disable\".",
+ &disablelist);
+
+ add_prefix_cmd ("delete", class_breakpoint, delete_command,
+ "Delete some breakpoints or auto-display expressions.\n\
+Arguments are breakpoint numbers with spaces in between.\n\
+To delete all breakpoints, give no argument.\n\
+\n\
+Also a prefix command for deletion of other GDB objects.\n\
+The \"unset\" command is also an alias for \"delete\".",
+ &deletelist, "delete ", 1, &cmdlist);
+ add_com_alias ("d", "delete", class_breakpoint, 1);
+ add_com_alias ("unset", "delete", class_alias, 1);
+
+ add_cmd ("breakpoints", class_alias, delete_command,
+ "Delete some breakpoints or auto-display expressions.\n\
+Arguments are breakpoint numbers with spaces in between.\n\
+To delete all breakpoints, give no argument.\n\
+This command may be abbreviated \"delete\".",
+ &deletelist);
+
+ add_com ("clear", class_breakpoint, clear_command,
+ "Clear breakpoint at specified line or function.\n\
+Argument may be line number, function name, or \"*\" and an address.\n\
+If line number is specified, all breakpoints in that line are cleared.\n\
+If function is specified, breakpoints at beginning of function are cleared.\n\
+If an address is specified, breakpoints at that address are cleared.\n\n\
+With no argument, clears all breakpoints in the line that the selected frame\n\
+is executing in.\n\
+\n\
+See also the \"delete\" command which clears breakpoints by number.");
+
+ add_com ("break", class_breakpoint, break_command,
+ "Set breakpoint at specified line or function.\n\
+Argument may be line number, function name, or \"*\" and an address.\n\
+If line number is specified, break at start of code for that line.\n\
+If function is specified, break at start of code for that function.\n\
+If an address is specified, break at that exact address.\n\
+With no arg, uses current execution address of selected stack frame.\n\
+This is useful for breaking on return to a stack frame.\n\
+\n\
+Multiple breakpoints at one place are permitted, and useful if conditional.\n\
+\n\
+Do \"help breakpoints\" for info on other commands dealing with breakpoints.");
+ add_com_alias ("b", "break", class_run, 1);
+ add_com_alias ("br", "break", class_run, 1);
+ add_com_alias ("bre", "break", class_run, 1);
+ add_com_alias ("brea", "break", class_run, 1);
+
+ add_info ("breakpoints", breakpoints_info,
+ "Status of all breakpoints, or breakpoint number NUMBER.\n\
+Second column is \"y\" for enabled breakpoint, \"n\" for disabled,\n\
+\"o\" for enabled once (disable when hit), \"d\" for enable but delete when hit.\n\
+Then come the address and the file/line number.\n\n\
+Convenience variable \"$_\" and default examine address for \"x\"\n\
+are set to the address of the last breakpoint listed.");
+}
+
diff --git a/gnu/usr.bin/gdb/command.c b/gnu/usr.bin/gdb/command.c
new file mode 100644
index 0000000..79daea4
--- /dev/null
+++ b/gnu/usr.bin/gdb/command.c
@@ -0,0 +1,856 @@
+/* Library for reading command lines and decoding commands.
+ Copyright (C) 1986, 1989 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "command.h"
+#include "defs.h"
+#include <stdio.h>
+#include <ctype.h>
+
+extern char *xmalloc ();
+
+/* Add element named NAME to command list *LIST.
+ FUN should be the function to execute the command;
+ it will get a character string as argument, with leading
+ and trailing blanks already eliminated.
+
+ DOC is a documentation string for the command.
+ Its first line should be a complete sentence.
+ It should start with ? for a command that is an abbreviation
+ or with * for a command that most users don't need to know about. */
+
+struct cmd_list_element *
+add_cmd (name, class, fun, doc, list)
+ char *name;
+ int class;
+ void (*fun) ();
+ char *doc;
+ struct cmd_list_element **list;
+{
+ register struct cmd_list_element *c
+ = (struct cmd_list_element *) xmalloc (sizeof (struct cmd_list_element));
+
+ delete_cmd (name, list);
+ c->next = *list;
+ c->name = savestring (name, strlen (name));
+ c->class = class;
+ c->function = fun;
+ c->doc = doc;
+ c->prefixlist = 0;
+ c->allow_unknown = 0;
+ c->abbrev_flag = 0;
+ c->aux = 0;
+ *list = c;
+ return c;
+}
+
+/* Same as above, except that the abbrev_flag is set. */
+
+struct cmd_list_element *
+add_abbrev_cmd (name, class, fun, doc, list)
+ char *name;
+ int class;
+ void (*fun) ();
+ char *doc;
+ struct cmd_list_element **list;
+{
+ register struct cmd_list_element *c
+ = (struct cmd_list_element *) xmalloc (sizeof (struct cmd_list_element));
+
+ delete_cmd (name, list);
+ c->next = *list;
+ c->name = savestring (name, strlen (name));
+ c->class = class;
+ c->function = fun;
+ c->doc = doc;
+ c->prefixlist = 0;
+ c->allow_unknown = 0;
+ c->abbrev_flag = 1;
+ c->aux = 0;
+ *list = c;
+ return c;
+}
+
+struct cmd_list_element *
+add_alias_cmd (name, oldname, class, abbrev_flag, list)
+ char *name;
+ char *oldname;
+ int class;
+ int abbrev_flag;
+ struct cmd_list_element **list;
+{
+ /* Must do this since lookup_cmd tries to side-effect its first arg */
+ char *copied_name;
+ register struct cmd_list_element *old;
+ register struct cmd_list_element *c;
+ copied_name = (char *) alloca (strlen (oldname) + 1);
+ strcpy (copied_name, oldname);
+ old = lookup_cmd (&copied_name, *list, 0, 1, 1);
+
+ if (old == 0)
+ {
+ delete_cmd (name, list);
+ return 0;
+ }
+
+ c = add_cmd (name, class, old->function, old->doc, list);
+ c->prefixlist = old->prefixlist;
+ c->prefixname = old->prefixname;
+ c->allow_unknown = old->allow_unknown;
+ c->abbrev_flag = abbrev_flag;
+ c->aux = old->aux;
+ return c;
+}
+
+/* Like add_cmd but adds an element for a command prefix:
+ a name that should be followed by a subcommand to be looked up
+ in another command list. PREFIXLIST should be the address
+ of the variable containing that list. */
+
+struct cmd_list_element *
+add_prefix_cmd (name, class, fun, doc, prefixlist, prefixname,
+ allow_unknown, list)
+ char *name;
+ int class;
+ void (*fun) ();
+ char *doc;
+ struct cmd_list_element **prefixlist;
+ char *prefixname;
+ int allow_unknown;
+ struct cmd_list_element **list;
+{
+ register struct cmd_list_element *c = add_cmd (name, class, fun, doc, list);
+ c->prefixlist = prefixlist;
+ c->prefixname = prefixname;
+ c->allow_unknown = allow_unknown;
+ return c;
+}
+
+/* Like add_prefix_cmd butsets the abbrev_flag on the new command. */
+
+struct cmd_list_element *
+add_abbrev_prefix_cmd (name, class, fun, doc, prefixlist, prefixname,
+ allow_unknown, list)
+ char *name;
+ int class;
+ void (*fun) ();
+ char *doc;
+ struct cmd_list_element **prefixlist;
+ char *prefixname;
+ int allow_unknown;
+ struct cmd_list_element **list;
+{
+ register struct cmd_list_element *c = add_cmd (name, class, fun, doc, list);
+ c->prefixlist = prefixlist;
+ c->prefixname = prefixname;
+ c->allow_unknown = allow_unknown;
+ c->abbrev_flag = 1;
+ return c;
+}
+
+/* Remove the command named NAME from the command list. */
+
+void
+delete_cmd (name, list)
+ char *name;
+ struct cmd_list_element **list;
+{
+ register struct cmd_list_element *c;
+
+ while (*list && !strcmp ((*list)->name, name))
+ {
+ *list = (*list)->next;
+ }
+
+ if (*list)
+ for (c = *list; c->next;)
+ {
+ if (!strcmp (c->next->name, name))
+ c->next = c->next->next;
+ else
+ c = c->next;
+ }
+}
+
+void help_cmd (), help_list (), help_cmd_list ();
+
+/* This command really has to deal with two things:
+ * 1) I want documentation on *this string* (usually called by
+ * "help commandname").
+ * 2) I want documentation on *this list* (usually called by
+ * giving a command that requires subcommands. Also called by saying
+ * just "help".)
+ *
+ * I am going to split this into two seperate comamnds, help_cmd and
+ * help_list.
+ */
+
+void
+help_cmd (command, stream)
+ char *command;
+ FILE *stream;
+{
+ struct cmd_list_element *c;
+ extern struct cmd_list_element *cmdlist;
+
+ if (!command)
+ {
+ help_list (cmdlist, "", -2, stream);
+ return;
+ }
+
+ c = lookup_cmd (&command, cmdlist, "", 0, 0);
+
+ if (c == 0)
+ return;
+
+ /* There are three cases here.
+ If c->prefixlist is nonzer, we have a prefix command.
+ Print its documentation, then list its subcommands.
+
+ If c->function is nonzero, we really have a command.
+ Print its documentation and return.
+
+ If c->function is zero, we have a class name.
+ Print its documentation (as if it were a command)
+ and then set class to he number of this class
+ so that the commands in the class will be listed. */
+
+ fputs_filtered (c->doc, stream);
+ fputs_filtered ("\n", stream);
+
+ if (c->prefixlist == 0 && c->function != 0)
+ return;
+ fprintf_filtered (stream, "\n");
+
+ /* If this is a prefix command, print it's subcommands */
+ if (c->prefixlist)
+ help_list (*c->prefixlist, c->prefixname, -1, stream);
+
+ /* If this is a class name, print all of the commands in the class */
+ if (c->function == 0)
+ help_list (cmdlist, "", c->class, stream);
+}
+
+/*
+ * Get a specific kind of help on a command list.
+ *
+ * LIST is the list.
+ * CMDTYPE is the prefix to use in the title string.
+ * CLASS is the class with which to list the nodes of this list (see
+ * documentation for help_cmd_list below), As usual, -1 for
+ * everything, -2 for just classes, and non-negative for only things
+ * in a specific class.
+ * and STREAM is the output stream on which to print things.
+ * If you call this routine with a class >= 0, it recurses.
+ */
+void
+help_list (list, cmdtype, class, stream)
+ struct cmd_list_element *list;
+ char *cmdtype;
+ int class;
+ FILE *stream;
+{
+ int len;
+ char *cmdtype1, *cmdtype2;
+
+ /* If CMDTYPE is "foo ", CMDTYPE1 gets " foo" and CMDTYPE2 gets "foo sub" */
+ len = strlen (cmdtype);
+ cmdtype1 = (char *) alloca (len + 1);
+ cmdtype1[0] = 0;
+ cmdtype2 = (char *) alloca (len + 4);
+ cmdtype2[0] = 0;
+ if (len)
+ {
+ cmdtype1[0] = ' ';
+ strncpy (cmdtype1 + 1, cmdtype, len - 1);
+ cmdtype1[len] = 0;
+ strncpy (cmdtype2, cmdtype, len - 1);
+ strcpy (cmdtype2 + len - 1, " sub");
+ }
+
+ if (class == -2)
+ fprintf_filtered (stream, "List of classes of %scommands:\n\n", cmdtype2);
+ else
+ fprintf_filtered (stream, "List of %scommands:\n\n", cmdtype2);
+
+ help_cmd_list (list, class, cmdtype, (class >= 0), stream);
+
+ if (class == -2)
+ fprintf_filtered (stream, "\n\
+Type \"help%s\" followed by a class name for a list of commands in that class.",
+ cmdtype1);
+
+ fprintf_filtered (stream, "\n\
+Type \"help%s\" followed by %scommand name for full documentation.\n\
+Command name abbreviations are allowed if unambiguous.\n",
+ cmdtype1, cmdtype2);
+}
+
+
+/*
+ * Implement a help command on command list LIST.
+ * RECURSE should be non-zero if this should be done recursively on
+ * all sublists of LIST.
+ * PREFIX is the prefix to print before each command name.
+ * STREAM is the stream upon which the output should be written.
+ * CLASS should be:
+ * A non-negative class number to list only commands in that
+ * class.
+ * -1 to list all commands in list.
+ * -2 to list all classes in list.
+ *
+ * Note that RECURSE will be active on *all* sublists, not just the
+ * ones seclected by the criteria above (ie. the selection mechanism
+ * is at the low level, not the high-level).
+ */
+void
+help_cmd_list (list, class, prefix, recurse, stream)
+ struct cmd_list_element *list;
+ int class;
+ char *prefix;
+ int recurse;
+ FILE *stream;
+{
+ register struct cmd_list_element *c;
+ register char *p;
+ static char *line_buffer = 0;
+ static int line_size;
+
+ if (!line_buffer)
+ {
+ line_size = 80;
+ line_buffer = (char *) xmalloc (line_size);
+ }
+
+ for (c = list; c; c = c->next)
+ {
+ if (c->abbrev_flag == 0 &&
+ (class == -1
+ || (class == -2 && c->function == 0)
+ || (class == c->class && c->function != 0)))
+ {
+ fprintf_filtered (stream, "%s%s -- ", prefix, c->name);
+ /* Print just the first line */
+ p = c->doc;
+ while (*p && *p != '\n') p++;
+ if (p - c->doc > line_size - 1)
+ {
+ line_size = p - c->doc + 1;
+ free (line_buffer);
+ line_buffer = (char *) xmalloc (line_size);
+ }
+ strncpy (line_buffer, c->doc, p - c->doc);
+ line_buffer[p - c->doc] = '\0';
+ fputs_filtered (line_buffer, stream);
+ fputs_filtered ("\n", stream);
+ }
+ if (recurse
+ && c->prefixlist != 0
+ && c->abbrev_flag == 0)
+ help_cmd_list (*c->prefixlist, class, c->prefixname, 1, stream);
+ }
+}
+
+/* This routine takes a line of TEXT and a CLIST in which to
+ start the lookup. When it returns it will have incremented the text
+ pointer past the section of text it matched, set *RESULT_LIST to
+ the list in which the last word was matched, and will return the
+ cmd list element which the text matches. It will return 0 if no
+ match at all was possible. It will return -1 if ambigous matches are
+ possible; in this case *RESULT_LIST will be set to the list in which
+ there are ambiguous choices (and text will be set to the ambiguous
+ text string).
+
+ It does no error reporting whatsoever; control will always return
+ to the superior routine.
+
+ In the case of an ambiguous return (-1), *RESULT_LIST will be set to
+ point at the prefix_command (ie. the best match) *or* (special
+ case) will be 0 if no prefix command was ever found. For example,
+ in the case of "info a", "info" matches without ambiguity, but "a"
+ could be "args" or "address", so *RESULT_LIST is set to
+ the cmd_list_element for "info". So in this case
+ result list should not be interpeted as a pointer to the beginning
+ of a list; it simply points to a specific command.
+
+ This routine does *not* modify the text pointed to by TEXT.
+
+ If INGNORE_HELP_CLASSES is nonzero, ignore any command list
+ elements which are actually help classes rather than commands (i.e.
+ the function field of the struct cmd_list_element is 0). */
+
+struct cmd_list_element *
+lookup_cmd_1 (text, clist, result_list, ignore_help_classes)
+ char **text;
+ struct cmd_list_element *clist, **result_list;
+ int ignore_help_classes;
+{
+ char *p, *command;
+ int len, tmp, nfound;
+ struct cmd_list_element *found, *c;
+
+ while (**text == ' ' || **text == '\t')
+ (*text)++;
+
+ /* Treating underscores as part of command words is important
+ so that "set args_foo()" doesn't get interpreted as
+ "set args _foo()". */
+ for (p = *text;
+ *p && (isalnum(*p) || *p == '-' || *p == '_');
+ p++)
+ ;
+
+ /* If nothing but whitespace, return 0. */
+ if (p == *text)
+ return 0;
+
+ len = p - *text;
+
+ /* *text and p now bracket the first command word to lookup (and
+ it's length is len). We copy this into a local temporary,
+ converting to lower case as we go. */
+
+ command = (char *) alloca (len + 1);
+ for (tmp = 0; tmp < len; tmp++)
+ {
+ char x = (*text)[tmp];
+ command[tmp] = (x >= 'A' && x <= 'Z') ? x - 'A' + 'a' : x;
+ }
+ command[len] = '\0';
+
+ /* Look it up. */
+ found = 0;
+ nfound = 0;
+ for (c = clist; c; c = c->next)
+ if (!strncmp (command, c->name, len)
+ && (!ignore_help_classes || c->function))
+ {
+ found = c;
+ nfound++;
+ if (c->name[len] == '\0')
+ {
+ nfound = 1;
+ break;
+ }
+ }
+
+ /* If nothing matches, we have a simple failure. */
+ if (nfound == 0)
+ return 0;
+
+ if (nfound > 1)
+ {
+ *result_list = 0; /* Will be modified in calling routine
+ if we know what the prefix command is.
+ */
+ return (struct cmd_list_element *) -1; /* Ambiguous. */
+ }
+
+ /* We've matched something on this list. Move text pointer forward. */
+
+ *text = p;
+ if (found->prefixlist)
+ {
+ c = lookup_cmd_1 (text, *found->prefixlist, result_list,
+ ignore_help_classes);
+ if (!c)
+ {
+ /* Didn't find anything; this is as far as we got. */
+ *result_list = clist;
+ return found;
+ }
+ else if (c == (struct cmd_list_element *) -1)
+ {
+ /* We've gotten this far properley, but the next step
+ is ambiguous. We need to set the result list to the best
+ we've found (if an inferior hasn't already set it). */
+ if (!*result_list)
+ /* This used to say *result_list = *found->prefixlist
+ If that was correct, need to modify the documentation
+ at the top of this function to clarify what is supposed
+ to be going on. */
+ *result_list = found;
+ return c;
+ }
+ else
+ {
+ /* We matched! */
+ return c;
+ }
+ }
+ else
+ {
+ *result_list = clist;
+ return found;
+ }
+}
+
+/* Look up the contents of *LINE as a command in the command list LIST.
+ LIST is a chain of struct cmd_list_element's.
+ If it is found, return the struct cmd_list_element for that command
+ and update *LINE to point after the command name, at the first argument.
+ If not found, call error if ALLOW_UNKNOWN is zero
+ otherwise (or if error returns) return zero.
+ Call error if specified command is ambiguous,
+ unless ALLOW_UNKNOWN is negative.
+ CMDTYPE precedes the word "command" in the error message.
+
+ If INGNORE_HELP_CLASSES is nonzero, ignore any command list
+ elements which are actually help classes rather than commands (i.e.
+ the function field of the struct cmd_list_element is 0). */
+
+struct cmd_list_element *
+lookup_cmd (line, list, cmdtype, allow_unknown, ignore_help_classes)
+ char **line;
+ struct cmd_list_element *list;
+ char *cmdtype;
+ int allow_unknown;
+ int ignore_help_classes;
+{
+ struct cmd_list_element *last_list = 0;
+ struct cmd_list_element *c =
+ lookup_cmd_1 (line, list, &last_list, ignore_help_classes);
+ char *ptr = (*line) + strlen (*line) - 1;
+
+ /* Clear off trailing whitespace. */
+ while (ptr >= *line && (*ptr == ' ' || *ptr == '\t'))
+ ptr--;
+ *(ptr + 1) = '\0';
+
+ if (!c)
+ {
+ if (!allow_unknown)
+ {
+ if (!*line)
+ error ("Lack of needed %scommand", cmdtype);
+ else
+ {
+ char *p = *line, *q;
+
+ while (isalnum(*p) || *p == '-')
+ p++;
+
+ q = (char *) alloca (p - *line + 1);
+ strncpy (q, *line, p - *line);
+ q[p-*line] = '\0';
+
+ error ("Undefined %scommand: \"%s\".", cmdtype, q);
+ }
+ }
+ else
+ return 0;
+ }
+ else if (c == (struct cmd_list_element *) -1)
+ {
+ /* Ambigous. Local values should be off prefixlist or called
+ values. */
+ int local_allow_unknown = (last_list ? last_list->allow_unknown :
+ allow_unknown);
+ char *local_cmdtype = last_list ? last_list->prefixname : cmdtype;
+ struct cmd_list_element *local_list =
+ (last_list ? *(last_list->prefixlist) : list);
+
+ if (local_allow_unknown < 0)
+ {
+ if (last_list)
+ return last_list; /* Found something. */
+ else
+ return 0; /* Found nothing. */
+ }
+ else
+ {
+ /* Report as error. */
+ int amb_len;
+ char ambbuf[100];
+
+ for (amb_len = 0;
+ ((*line)[amb_len] && (*line)[amb_len] != ' '
+ && (*line)[amb_len] != '\t');
+ amb_len++)
+ ;
+
+ ambbuf[0] = 0;
+ for (c = local_list; c; c = c->next)
+ if (!strncmp (*line, c->name, amb_len))
+ {
+ if (strlen (ambbuf) + strlen (c->name) + 6 < sizeof ambbuf)
+ {
+ if (strlen (ambbuf))
+ strcat (ambbuf, ", ");
+ strcat (ambbuf, c->name);
+ }
+ else
+ {
+ strcat (ambbuf, "..");
+ break;
+ }
+ }
+ error ("Ambiguous %scommand \"%s\": %s.", local_cmdtype,
+ *line, ambbuf);
+ }
+ }
+ else
+ {
+ /* We've got something. It may still not be what the caller
+ wants (if this command *needs* a subcommand). */
+ while (**line == ' ' || **line == '\t')
+ (*line)++;
+
+ if (c->prefixlist && **line && !c->allow_unknown)
+ error ("Undefined %scommand: \"%s\".", c->prefixname, *line);
+
+ /* Seems to be what he wants. Return it. */
+ return c;
+ }
+}
+
+#if 0
+/* Look up the contents of *LINE as a command in the command list LIST.
+ LIST is a chain of struct cmd_list_element's.
+ If it is found, return the struct cmd_list_element for that command
+ and update *LINE to point after the command name, at the first argument.
+ If not found, call error if ALLOW_UNKNOWN is zero
+ otherwise (or if error returns) return zero.
+ Call error if specified command is ambiguous,
+ unless ALLOW_UNKNOWN is negative.
+ CMDTYPE precedes the word "command" in the error message. */
+
+struct cmd_list_element *
+lookup_cmd (line, list, cmdtype, allow_unknown)
+ char **line;
+ struct cmd_list_element *list;
+ char *cmdtype;
+ int allow_unknown;
+{
+ register char *p;
+ register struct cmd_list_element *c, *found;
+ int nfound;
+ char ambbuf[100];
+ char *processed_cmd;
+ int i, cmd_len;
+
+ /* Skip leading whitespace. */
+
+ while (**line == ' ' || **line == '\t')
+ (*line)++;
+
+ /* Clear out trailing whitespace. */
+
+ p = *line + strlen (*line);
+ while (p != *line && (p[-1] == ' ' || p[-1] == '\t'))
+ p--;
+ *p = 0;
+
+ /* Find end of command name. */
+
+ p = *line;
+ while (*p == '-'
+ || (*p >= 'a' && *p <= 'z')
+ || (*p >= 'A' && *p <= 'Z')
+ || (*p >= '0' && *p <= '9'))
+ p++;
+
+ /* Look up the command name.
+ If exact match, keep that.
+ Otherwise, take command abbreviated, if unique. Note that (in my
+ opinion) a null string does *not* indicate ambiguity; simply the
+ end of the argument. */
+
+ if (p == *line)
+ {
+ if (!allow_unknown)
+ error ("Lack of needed %scommand", cmdtype);
+ return 0;
+ }
+
+ /* Copy over to a local buffer, converting to lowercase on the way.
+ This is in case the command being parsed is a subcommand which
+ doesn't match anything, and that's ok. We want the original
+ untouched for the routine of the original command. */
+
+ processed_cmd = (char *) alloca (p - *line + 1);
+ for (cmd_len = 0; cmd_len < p - *line; cmd_len++)
+ {
+ char x = (*line)[cmd_len];
+ if (x >= 'A' && x <= 'Z')
+ processed_cmd[cmd_len] = x - 'A' + 'a';
+ else
+ processed_cmd[cmd_len] = x;
+ }
+ processed_cmd[cmd_len] = '\0';
+
+ /* Check all possibilities in the current command list. */
+ found = 0;
+ nfound = 0;
+ for (c = list; c; c = c->next)
+ {
+ if (!strncmp (processed_cmd, c->name, cmd_len))
+ {
+ found = c;
+ nfound++;
+ if (c->name[cmd_len] == 0)
+ {
+ nfound = 1;
+ break;
+ }
+ }
+ }
+
+ /* Report error for undefined command name. */
+
+ if (nfound != 1)
+ {
+ if (nfound > 1 && allow_unknown >= 0)
+ {
+ ambbuf[0] = 0;
+ for (c = list; c; c = c->next)
+ if (!strncmp (processed_cmd, c->name, cmd_len))
+ {
+ if (strlen (ambbuf) + strlen (c->name) + 6 < sizeof ambbuf)
+ {
+ if (strlen (ambbuf))
+ strcat (ambbuf, ", ");
+ strcat (ambbuf, c->name);
+ }
+ else
+ {
+ strcat (ambbuf, "..");
+ break;
+ }
+ }
+ error ("Ambiguous %scommand \"%s\": %s.", cmdtype,
+ processed_cmd, ambbuf);
+ }
+ else if (!allow_unknown)
+ error ("Undefined %scommand: \"%s\".", cmdtype, processed_cmd);
+ return 0;
+ }
+
+ /* Skip whitespace before the argument. */
+
+ while (*p == ' ' || *p == '\t') p++;
+ *line = p;
+
+ if (found->prefixlist && *p)
+ {
+ c = lookup_cmd (line, *found->prefixlist, found->prefixname,
+ found->allow_unknown);
+ if (c)
+ return c;
+ }
+
+ return found;
+}
+#endif
+
+/* Helper function for SYMBOL_COMPLETION_FUNCTION. */
+
+/* Return a vector of char pointers which point to the different
+ possible completions in LIST of TEXT. */
+
+char **
+complete_on_cmdlist (list, text)
+ struct cmd_list_element *list;
+ char *text;
+{
+ struct cmd_list_element *ptr;
+ char **matchlist;
+ int sizeof_matchlist;
+ int matches;
+ int textlen = strlen (text);
+
+ sizeof_matchlist = 10;
+ matchlist = (char **) xmalloc (sizeof_matchlist * sizeof (char *));
+ matches = 0;
+
+ for (ptr = list; ptr; ptr = ptr->next)
+ if (!strncmp (ptr->name, text, textlen)
+ && !ptr->abbrev_flag
+ && (ptr->function
+ || ptr->prefixlist))
+ {
+ if (matches == sizeof_matchlist)
+ {
+ sizeof_matchlist *= 2;
+ matchlist = (char **) xrealloc (matchlist,
+ (sizeof_matchlist
+ * sizeof (char *)));
+ }
+
+ matchlist[matches] = (char *)
+ xmalloc (strlen (ptr->name) + 1);
+ strcpy (matchlist[matches++], ptr->name);
+ }
+
+ if (matches == 0)
+ {
+ free (matchlist);
+ matchlist = 0;
+ }
+ else
+ {
+ matchlist = (char **) xrealloc (matchlist, ((matches + 1)
+ * sizeof (char *)));
+ matchlist[matches] = (char *) 0;
+ }
+
+ return matchlist;
+}
+
+static void
+shell_escape (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ int rc, status, pid;
+ char *p, *user_shell;
+ extern char *rindex ();
+
+ if ((user_shell = (char *) getenv ("SHELL")) == NULL)
+ user_shell = "/bin/sh";
+
+ /* Get the name of the shell for arg0 */
+ if ((p = rindex (user_shell, '/')) == NULL)
+ p = user_shell;
+ else
+ p++; /* Get past '/' */
+
+ if ((pid = fork()) == 0)
+ {
+ if (!arg)
+ execl (user_shell, p, 0);
+ else
+ execl (user_shell, p, "-c", arg, 0);
+
+ fprintf (stderr, "Exec of shell failed\n");
+ exit (0);
+ }
+
+ if (pid != -1)
+ while ((rc = wait (&status)) != pid && rc != -1)
+ ;
+ else
+ error ("Fork failed");
+}
+
+void
+_initialize_command ()
+{
+ add_com ("shell", class_support, shell_escape,
+ "Execute the rest of the line as a shell command. \n\
+With no arguments, run an inferior shell.");
+}
diff --git a/gnu/usr.bin/gdb/command.h b/gnu/usr.bin/gdb/command.h
new file mode 100644
index 0000000..fe28aef
--- /dev/null
+++ b/gnu/usr.bin/gdb/command.h
@@ -0,0 +1,77 @@
+/* Header file for command-reading library command.c.
+ Copyright (C) 1986, 1989 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This structure records one command'd definition. */
+
+struct cmd_list_element
+ {
+ /* Points to next command in this list. */
+ struct cmd_list_element *next;
+
+ /* Name of this command. */
+ char *name;
+
+ /* Command class; class values are chosen by application program. */
+ int class;
+
+ /* Function definition of this command.
+ Zero for command class names and for help topics that
+ are not really commands. */
+ void (*function) ();
+
+ /* Documentation of this command (or help topic).
+ First line is brief documentation; remaining lines form, with it,
+ the full documentation. First line should end with a period.
+ Entire string should also end with a period, not a newline. */
+ char *doc;
+
+ /* Auxiliary information.
+ It is up to the calling program to decide what this means. */
+ char *aux;
+
+ /* Nonzero identifies a prefix command. For them, the address
+ of the variable containing the list of subcommands. */
+ struct cmd_list_element **prefixlist;
+
+ /* For prefix commands only:
+ String containing prefix commands to get here: this one
+ plus any others needed to get to it. Should end in a space.
+ It is used before the word "command" in describing the
+ commands reached through this prefix. */
+ char *prefixname;
+
+ /* For prefix commands only:
+ nonzero means do not get an error if subcommand is not
+ recognized; call the prefix's own function in that case. */
+ char allow_unknown;
+
+ /* Nonzero says this is an abbreviation, and should not
+ be mentioned in lists of commands.
+ This allows "br<tab>" to complete to "break", which it
+ otherwise wouldn't. */
+ char abbrev_flag;
+ };
+
+/* Forward-declarations of the entry-points of command.c. */
+
+extern struct cmd_list_element *add_cmd ();
+extern struct cmd_list_element *add_alias_cmd ();
+extern struct cmd_list_element *add_prefix_cmd ();
+extern struct cmd_list_element *lookup_cmd (), *lookup_cmd_1 ();
+extern char **complete_on_cmdlist ();
+extern void delete_cmd ();
+extern void help_cmd ();
diff --git a/gnu/usr.bin/gdb/config/Makefile.i386 b/gnu/usr.bin/gdb/config/Makefile.i386
new file mode 100644
index 0000000..cc52aa3
--- /dev/null
+++ b/gnu/usr.bin/gdb/config/Makefile.i386
@@ -0,0 +1,6 @@
+# @(#)Makefile.i386 6.2 (Berkeley) 3/21/91
+
+CONFIGSRCS= i386bsd-dep.c i386-pinsn.c
+
+param.h:
+ ln -s $(.CURDIR)/config/m-i386bsd.h param.h
diff --git a/gnu/usr.bin/gdb/config/default-dep.c b/gnu/usr.bin/gdb/config/default-dep.c
new file mode 100644
index 0000000..13fe7b9
--- /dev/null
+++ b/gnu/usr.bin/gdb/config/default-dep.c
@@ -0,0 +1,585 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)default-dep.c 6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Low level interface to ptrace, for GDB when running under Unix.
+ Copyright (C) 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "frame.h"
+#include "inferior.h"
+
+#ifdef USG
+#include <sys/types.h>
+#endif
+
+#include <sys/param.h>
+#include <sys/dir.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+/* #include <fcntl.h> Can we live without this? */
+
+#ifdef COFF_ENCAPSULATE
+#include "a.out.encap.h"
+#else
+#include <a.out.h>
+#endif
+#ifndef N_SET_MAGIC
+#define N_SET_MAGIC(exec, val) ((exec).a_magic = (val))
+#endif
+
+#include <sys/user.h> /* After a.out.h */
+#include <sys/file.h>
+#include <sys/stat.h>
+
+extern int errno;
+
+/* This function simply calls ptrace with the given arguments.
+ It exists so that all calls to ptrace are isolated in this
+ machine-dependent file. */
+int
+call_ptrace (request, pid, arg3, arg4)
+ int request, pid, arg3, arg4;
+{
+ return ptrace (request, pid, arg3, arg4);
+}
+
+kill_inferior ()
+{
+ if (remote_debugging)
+ return;
+ if (inferior_pid == 0)
+ return;
+ ptrace (8, inferior_pid, 0, 0);
+ wait (0);
+ inferior_died ();
+}
+
+/* This is used when GDB is exiting. It gives less chance of error.*/
+
+kill_inferior_fast ()
+{
+ if (remote_debugging)
+ return;
+ if (inferior_pid == 0)
+ return;
+ ptrace (8, inferior_pid, 0, 0);
+ wait (0);
+}
+
+/* Resume execution of the inferior process.
+ If STEP is nonzero, single-step it.
+ If SIGNAL is nonzero, give it that signal. */
+
+void
+resume (step, signal)
+ int step;
+ int signal;
+{
+ errno = 0;
+ if (remote_debugging)
+ remote_resume (step, signal);
+ else
+ {
+ ptrace (step ? 9 : 7, inferior_pid, 1, signal);
+ if (errno)
+ perror_with_name ("ptrace");
+ }
+}
+
+void
+fetch_inferior_registers ()
+{
+ register int regno;
+ register unsigned int regaddr;
+ char buf[MAX_REGISTER_RAW_SIZE];
+ register int i;
+
+ struct user u;
+ unsigned int offset = (char *) &u.u_ar0 - (char *) &u;
+ offset = ptrace (3, inferior_pid, offset, 0) - KERNEL_U_ADDR;
+
+ for (regno = 0; regno < NUM_REGS; regno++)
+ {
+ regaddr = register_addr (regno, offset);
+ for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (int))
+ {
+ *(int *) &buf[i] = ptrace (3, inferior_pid, regaddr, 0);
+ regaddr += sizeof (int);
+ }
+ supply_register (regno, buf);
+ }
+}
+
+/* Store our register values back into the inferior.
+ If REGNO is -1, do this for all registers.
+ Otherwise, REGNO specifies which register (so we can save time). */
+
+store_inferior_registers (regno)
+ int regno;
+{
+ register unsigned int regaddr;
+ char buf[80];
+
+ struct user u;
+ unsigned int offset = (char *) &u.u_ar0 - (char *) &u;
+ offset = ptrace (3, inferior_pid, offset, 0) - KERNEL_U_ADDR;
+
+ if (regno >= 0)
+ {
+ regaddr = register_addr (regno, offset);
+ errno = 0;
+ ptrace (6, inferior_pid, regaddr, read_register (regno));
+ if (errno != 0)
+ {
+ sprintf (buf, "writing register number %d", regno);
+ perror_with_name (buf);
+ }
+ }
+ else for (regno = 0; regno < NUM_REGS; regno++)
+ {
+ regaddr = register_addr (regno, offset);
+ errno = 0;
+ ptrace (6, inferior_pid, regaddr, read_register (regno));
+ if (errno != 0)
+ {
+ sprintf (buf, "writing all regs, number %d", regno);
+ perror_with_name (buf);
+ }
+ }
+}
+
+/* Copy LEN bytes from inferior's memory starting at MEMADDR
+ to debugger memory starting at MYADDR.
+ On failure (cannot read from inferior, usually because address is out
+ of bounds) returns the value of errno. */
+
+int
+read_inferior_memory (memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+{
+ register int i;
+ /* Round starting address down to longword boundary. */
+ register CORE_ADDR addr = memaddr & - sizeof (int);
+ /* Round ending address up; get number of longwords that makes. */
+ register int count
+ = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int);
+ /* Allocate buffer of that many longwords. */
+ register int *buffer = (int *) alloca (count * sizeof (int));
+ extern int errno;
+
+ /* Read all the longwords */
+ for (i = 0; i < count; i++, addr += sizeof (int))
+ {
+ errno = 0;
+#if 0
+ /* This is now done by read_memory, because when this function did it,
+ reading a byte or short int hardware port read whole longs, causing
+ serious side effects
+ such as bus errors and unexpected hardware operation. This would
+ also be a problem with ptrace if the inferior process could read
+ or write hardware registers, but that's not usually the case. */
+ if (remote_debugging)
+ buffer[i] = remote_fetch_word (addr);
+ else
+#endif
+ buffer[i] = ptrace (1, inferior_pid, addr, 0);
+ if (errno)
+ return errno;
+ }
+
+ /* Copy appropriate bytes out of the buffer. */
+ bcopy ((char *) buffer + (memaddr & (sizeof (int) - 1)), myaddr, len);
+ return 0;
+}
+
+/* Copy LEN bytes of data from debugger memory at MYADDR
+ to inferior's memory at MEMADDR.
+ On failure (cannot write the inferior)
+ returns the value of errno. */
+
+int
+write_inferior_memory (memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+{
+ register int i;
+ /* Round starting address down to longword boundary. */
+ register CORE_ADDR addr = memaddr & - sizeof (int);
+ /* Round ending address up; get number of longwords that makes. */
+ register int count
+ = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int);
+ /* Allocate buffer of that many longwords. */
+ register int *buffer = (int *) alloca (count * sizeof (int));
+ extern int errno;
+
+ /* Fill start and end extra bytes of buffer with existing memory data. */
+
+ if (remote_debugging)
+ return (remote_write_inferior_memory(memaddr, myaddr, len));
+
+ buffer[0] = ptrace (1, inferior_pid, addr, 0);
+
+ if (count > 1)
+ buffer[count - 1] = ptrace (1, inferior_pid,
+ addr + (count - 1) * sizeof (int), 0);
+
+ /* Copy data to be written over corresponding part of buffer */
+
+ bcopy (myaddr, (char *) buffer + (memaddr & (sizeof (int) - 1)), len);
+
+ /* Write the entire buffer. */
+
+ for (i = 0; i < count; i++, addr += sizeof (int))
+ {
+ errno = 0;
+ ptrace (4, inferior_pid, addr, buffer[i]);
+ if (errno)
+ return errno;
+ }
+
+ return 0;
+}
+
+/* Work with core dump and executable files, for GDB.
+ This code would be in core.c if it weren't machine-dependent. */
+
+#ifndef N_TXTADDR
+#define N_TXTADDR(hdr) 0
+#endif /* no N_TXTADDR */
+
+#ifndef N_DATADDR
+#define N_DATADDR(hdr) hdr.a_text
+#endif /* no N_DATADDR */
+
+/* Make COFF and non-COFF names for things a little more compatible
+ to reduce conditionals later. */
+
+#ifdef COFF_FORMAT
+#define a_magic magic
+#endif
+
+#ifndef COFF_FORMAT
+#ifndef AOUTHDR
+#define AOUTHDR struct exec
+#endif
+#endif
+
+extern char *sys_siglist[];
+
+
+/* Hook for `exec_file_command' command to call. */
+
+extern void (*exec_file_display_hook) ();
+
+/* File names of core file and executable file. */
+
+extern char *corefile;
+extern char *execfile;
+
+/* Descriptors on which core file and executable file are open.
+ Note that the execchan is closed when an inferior is created
+ and reopened if the inferior dies or is killed. */
+
+extern int corechan;
+extern int execchan;
+
+/* Last modification time of executable file.
+ Also used in source.c to compare against mtime of a source file. */
+
+extern int exec_mtime;
+
+/* Virtual addresses of bounds of the two areas of memory in the core file. */
+
+extern CORE_ADDR data_start;
+extern CORE_ADDR data_end;
+extern CORE_ADDR stack_start;
+extern CORE_ADDR stack_end;
+
+/* Virtual addresses of bounds of two areas of memory in the exec file.
+ Note that the data area in the exec file is used only when there is no core file. */
+
+extern CORE_ADDR text_start;
+extern CORE_ADDR text_end;
+
+extern CORE_ADDR exec_data_start;
+extern CORE_ADDR exec_data_end;
+
+/* Address in executable file of start of text area data. */
+
+extern int text_offset;
+
+/* Address in executable file of start of data area data. */
+
+extern int exec_data_offset;
+
+/* Address in core file of start of data area data. */
+
+extern int data_offset;
+
+/* Address in core file of start of stack area data. */
+
+extern int stack_offset;
+
+#ifdef COFF_FORMAT
+/* various coff data structures */
+
+extern FILHDR file_hdr;
+extern SCNHDR text_hdr;
+extern SCNHDR data_hdr;
+
+#endif /* not COFF_FORMAT */
+
+/* a.out header saved in core file. */
+
+extern AOUTHDR core_aouthdr;
+
+/* a.out header of exec file. */
+
+extern AOUTHDR exec_aouthdr;
+
+extern void validate_files ();
+
+core_file_command (filename, from_tty)
+ char *filename;
+ int from_tty;
+{
+ int val;
+ extern char registers[];
+
+ /* Discard all vestiges of any previous core file
+ and mark data and stack spaces as empty. */
+
+ if (corefile)
+ free (corefile);
+ corefile = 0;
+
+ if (corechan >= 0)
+ close (corechan);
+ corechan = -1;
+
+ data_start = 0;
+ data_end = 0;
+ stack_start = STACK_END_ADDR;
+ stack_end = STACK_END_ADDR;
+
+ /* Now, if a new core file was specified, open it and digest it. */
+
+ if (filename)
+ {
+ filename = tilde_expand (filename);
+ make_cleanup (free, filename);
+
+ if (have_inferior_p ())
+ error ("To look at a core file, you must kill the inferior with \"kill\".");
+ corechan = open (filename, O_RDONLY, 0);
+ if (corechan < 0)
+ perror_with_name (filename);
+ /* 4.2-style (and perhaps also sysV-style) core dump file. */
+ {
+ struct user u;
+
+ unsigned int reg_offset;
+
+ val = myread (corechan, &u, sizeof u);
+ if (val < 0)
+ perror_with_name ("Not a core file: reading upage");
+ if (val != sizeof u)
+ error ("Not a core file: could only read %d bytes", val);
+
+ /* We are depending on exec_file_command having been called
+ previously to set exec_data_start. Since the executable
+ and the core file share the same text segment, the address
+ of the data segment will be the same in both. */
+ data_start = exec_data_start;
+
+ data_end = data_start + NBPG * u.u_dsize;
+ stack_start = stack_end - NBPG * u.u_ssize;
+ data_offset = NBPG * UPAGES;
+ stack_offset = NBPG * (UPAGES + u.u_dsize);
+
+ /* Some machines put an absolute address in here and some put
+ the offset in the upage of the regs. */
+ reg_offset = (int) u.u_ar0;
+ if (reg_offset > NBPG * UPAGES)
+ reg_offset -= KERNEL_U_ADDR;
+
+ /* I don't know where to find this info.
+ So, for now, mark it as not available. */
+ N_SET_MAGIC (core_aouthdr, 0);
+
+ /* Read the register values out of the core file and store
+ them where `read_register' will find them. */
+
+ {
+ register int regno;
+
+ for (regno = 0; regno < NUM_REGS; regno++)
+ {
+ char buf[MAX_REGISTER_RAW_SIZE];
+
+ val = lseek (corechan, register_addr (regno, reg_offset), 0);
+ if (val < 0
+ || (val = myread (corechan, buf, sizeof buf)) < 0)
+ {
+ char * buffer = (char *) alloca (strlen (reg_names[regno])
+ + 30);
+ strcpy (buffer, "Reading register ");
+ strcat (buffer, reg_names[regno]);
+
+ perror_with_name (buffer);
+ }
+
+ supply_register (regno, buf);
+ }
+ }
+ }
+ if (filename[0] == '/')
+ corefile = savestring (filename, strlen (filename));
+ else
+ {
+ corefile = concat (current_directory, "/", filename);
+ }
+
+ set_current_frame ( create_new_frame (read_register (FP_REGNUM),
+ read_pc ()));
+ select_frame (get_current_frame (), 0);
+ validate_files ();
+ }
+ else if (from_tty)
+ printf ("No core file now.\n");
+}
+
+exec_file_command (filename, from_tty)
+ char *filename;
+ int from_tty;
+{
+ int val;
+
+ /* Eliminate all traces of old exec file.
+ Mark text segment as empty. */
+
+ if (execfile)
+ free (execfile);
+ execfile = 0;
+ data_start = 0;
+ data_end -= exec_data_start;
+ text_start = 0;
+ text_end = 0;
+ exec_data_start = 0;
+ exec_data_end = 0;
+ if (execchan >= 0)
+ close (execchan);
+ execchan = -1;
+
+ /* Now open and digest the file the user requested, if any. */
+
+ if (filename)
+ {
+ filename = tilde_expand (filename);
+ make_cleanup (free, filename);
+
+ execchan = openp (getenv ("PATH"), 1, filename, O_RDONLY, 0,
+ &execfile);
+ if (execchan < 0)
+ perror_with_name (filename);
+
+#ifdef COFF_FORMAT
+ {
+ int aout_hdrsize;
+ int num_sections;
+
+ if (read_file_hdr (execchan, &file_hdr) < 0)
+ error ("\"%s\": not in executable format.", execfile);
+
+ aout_hdrsize = file_hdr.f_opthdr;
+ num_sections = file_hdr.f_nscns;
+
+ if (read_aout_hdr (execchan, &exec_aouthdr, aout_hdrsize) < 0)
+ error ("\"%s\": can't read optional aouthdr", execfile);
+
+ if (read_section_hdr (execchan, _TEXT, &text_hdr, num_sections,
+ aout_hdrsize) < 0)
+ error ("\"%s\": can't read text section header", execfile);
+
+ if (read_section_hdr (execchan, _DATA, &data_hdr, num_sections,
+ aout_hdrsize) < 0)
+ error ("\"%s\": can't read data section header", execfile);
+
+ text_start = exec_aouthdr.text_start;
+ text_end = text_start + exec_aouthdr.tsize;
+ text_offset = text_hdr.s_scnptr;
+ exec_data_start = exec_aouthdr.data_start;
+ exec_data_end = exec_data_start + exec_aouthdr.dsize;
+ exec_data_offset = data_hdr.s_scnptr;
+ data_start = exec_data_start;
+ data_end += exec_data_start;
+ exec_mtime = file_hdr.f_timdat;
+ }
+#else /* not COFF_FORMAT */
+ {
+ struct stat st_exec;
+
+#ifdef HEADER_SEEK_FD
+ HEADER_SEEK_FD (execchan);
+#endif
+
+ val = myread (execchan, &exec_aouthdr, sizeof (AOUTHDR));
+
+ if (val < 0)
+ perror_with_name (filename);
+
+ text_start = N_TXTADDR (exec_aouthdr);
+ exec_data_start = N_DATADDR (exec_aouthdr);
+
+ text_offset = N_TXTOFF (exec_aouthdr);
+ exec_data_offset = N_TXTOFF (exec_aouthdr) + exec_aouthdr.a_text;
+
+ text_end = text_start + exec_aouthdr.a_text;
+ exec_data_end = exec_data_start + exec_aouthdr.a_data;
+ data_start = exec_data_start;
+ data_end += exec_data_start;
+
+ if (fstat (execchan, &st_exec) < 0)
+ perror_with_name (filename);
+ exec_mtime = st_exec.st_mtime;
+ }
+#endif /* not COFF_FORMAT */
+
+ validate_files ();
+ }
+ else if (from_tty)
+ printf ("No exec file now.\n");
+
+ /* Tell display code (if any) about the changed file name. */
+ if (exec_file_display_hook)
+ (*exec_file_display_hook) (filename);
+}
diff --git a/gnu/usr.bin/gdb/config/i386-dep.c b/gnu/usr.bin/gdb/config/i386-dep.c
new file mode 100644
index 0000000..c4630d0
--- /dev/null
+++ b/gnu/usr.bin/gdb/config/i386-dep.c
@@ -0,0 +1,1275 @@
+/* Low level interface to ptrace, for GDB when running on the Intel 386.
+ Copyright (C) 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "frame.h"
+#include "inferior.h"
+
+#ifdef USG
+#include <sys/types.h>
+#endif
+
+#include <sys/param.h>
+#include <sys/dir.h>
+#include <signal.h>
+#include <sys/user.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#ifdef COFF_ENCAPSULATE
+#include "a.out.encap.h"
+#else
+#include <a.out.h>
+#endif
+
+#ifndef N_SET_MAGIC
+#ifdef COFF_FORMAT
+#define N_SET_MAGIC(exec, val) ((exec).magic = (val))
+#else
+#define N_SET_MAGIC(exec, val) ((exec).a_magic = (val))
+#endif
+#endif
+
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <sys/reg.h>
+
+extern int errno;
+
+/* This function simply calls ptrace with the given arguments.
+ It exists so that all calls to ptrace are isolated in this
+ machine-dependent file. */
+int
+call_ptrace (request, pid, arg3, arg4)
+ int request, pid, arg3, arg4;
+{
+ return ptrace (request, pid, arg3, arg4);
+}
+
+kill_inferior ()
+{
+ if (remote_debugging)
+ return;
+ if (inferior_pid == 0)
+ return;
+ ptrace (8, inferior_pid, 0, 0);
+ wait (0);
+ inferior_died ();
+}
+
+/* This is used when GDB is exiting. It gives less chance of error.*/
+
+kill_inferior_fast ()
+{
+ if (remote_debugging)
+ return;
+ if (inferior_pid == 0)
+ return;
+ ptrace (8, inferior_pid, 0, 0);
+ wait (0);
+}
+
+/* Resume execution of the inferior process.
+ If STEP is nonzero, single-step it.
+ If SIGNAL is nonzero, give it that signal. */
+
+void
+resume (step, signal)
+ int step;
+ int signal;
+{
+ errno = 0;
+ if (remote_debugging)
+ remote_resume (step, signal);
+ else
+ {
+ ptrace (step ? 9 : 7, inferior_pid, 1, signal);
+ if (errno)
+ perror_with_name ("ptrace");
+ }
+}
+
+void
+fetch_inferior_registers ()
+{
+ register int regno;
+ register unsigned int regaddr;
+ char buf[MAX_REGISTER_RAW_SIZE];
+ register int i;
+
+ struct user u;
+ unsigned int offset = (char *) &u.u_ar0 - (char *) &u;
+ offset = ptrace (3, inferior_pid, offset, 0) - KERNEL_U_ADDR;
+
+ for (regno = 0; regno < NUM_REGS; regno++)
+ {
+ regaddr = register_addr (regno, offset);
+ for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (int))
+ {
+ *(int *) &buf[i] = ptrace (3, inferior_pid, regaddr, 0);
+ regaddr += sizeof (int);
+ }
+ supply_register (regno, buf);
+ }
+}
+
+/* Store our register values back into the inferior.
+ If REGNO is -1, do this for all registers.
+ Otherwise, REGNO specifies which register (so we can save time). */
+
+store_inferior_registers (regno)
+ int regno;
+{
+ register unsigned int regaddr;
+ char buf[80];
+
+ struct user u;
+ unsigned int offset = (char *) &u.u_ar0 - (char *) &u;
+ offset = ptrace (3, inferior_pid, offset, 0) - KERNEL_U_ADDR;
+
+ if (regno >= 0)
+ {
+ regaddr = register_addr (regno, offset);
+ errno = 0;
+ ptrace (6, inferior_pid, regaddr, read_register (regno));
+ if (errno != 0)
+ {
+ sprintf (buf, "writing register number %d", regno);
+ perror_with_name (buf);
+ }
+ }
+ else for (regno = 0; regno < NUM_REGS; regno++)
+ {
+ regaddr = register_addr (regno, offset);
+ errno = 0;
+ ptrace (6, inferior_pid, regaddr, read_register (regno));
+ if (errno != 0)
+ {
+ sprintf (buf, "writing register number %d", regno);
+ perror_with_name (buf);
+ }
+ }
+}
+
+/* Copy LEN bytes from inferior's memory starting at MEMADDR
+ to debugger memory starting at MYADDR.
+ On failure (cannot read from inferior, usually because address is out
+ of bounds) returns the value of errno. */
+
+int
+read_inferior_memory (memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+{
+ register int i;
+ /* Round starting address down to longword boundary. */
+ register CORE_ADDR addr = memaddr & - sizeof (int);
+ /* Round ending address up; get number of longwords that makes. */
+ register int count
+ = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int);
+ /* Allocate buffer of that many longwords. */
+ register int *buffer = (int *) alloca (count * sizeof (int));
+ extern int errno;
+
+ /* Read all the longwords */
+ for (i = 0; i < count; i++, addr += sizeof (int))
+ {
+ errno = 0;
+ if (remote_debugging)
+ buffer[i] = remote_fetch_word (addr);
+ else
+ buffer[i] = ptrace (1, inferior_pid, addr, 0);
+ if (errno)
+ return errno;
+ }
+
+ /* Copy appropriate bytes out of the buffer. */
+ bcopy ((char *) buffer + (memaddr & (sizeof (int) - 1)), myaddr, len);
+ return 0;
+}
+
+/* Copy LEN bytes of data from debugger memory at MYADDR
+ to inferior's memory at MEMADDR.
+ On failure (cannot write the inferior)
+ returns the value of errno. */
+
+int
+write_inferior_memory (memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+{
+ register int i;
+ /* Round starting address down to longword boundary. */
+ register CORE_ADDR addr = memaddr & - sizeof (int);
+ /* Round ending address up; get number of longwords that makes. */
+ register int count
+ = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int);
+ /* Allocate buffer of that many longwords. */
+ register int *buffer = (int *) alloca (count * sizeof (int));
+ extern int errno;
+
+ /* Fill start and end extra bytes of buffer with existing memory data. */
+
+ if (remote_debugging)
+ buffer[0] = remote_fetch_word (addr);
+ else
+ buffer[0] = ptrace (1, inferior_pid, addr, 0);
+
+ if (count > 1)
+ {
+ if (remote_debugging)
+ buffer[count - 1]
+ = remote_fetch_word (addr + (count - 1) * sizeof (int));
+ else
+ buffer[count - 1]
+ = ptrace (1, inferior_pid,
+ addr + (count - 1) * sizeof (int), 0);
+ }
+
+ /* Copy data to be written over corresponding part of buffer */
+
+ bcopy (myaddr, (char *) buffer + (memaddr & (sizeof (int) - 1)), len);
+
+ /* Write the entire buffer. */
+
+ for (i = 0; i < count; i++, addr += sizeof (int))
+ {
+ errno = 0;
+ if (remote_debugging)
+ remote_store_word (addr, buffer[i]);
+ else
+ ptrace (4, inferior_pid, addr, buffer[i]);
+ if (errno)
+ return errno;
+ }
+
+ return 0;
+}
+
+/* Work with core dump and executable files, for GDB.
+ This code would be in core.c if it weren't machine-dependent. */
+
+#ifndef N_TXTADDR
+#define N_TXTADDR(hdr) 0
+#endif /* no N_TXTADDR */
+
+#ifndef N_DATADDR
+#define N_DATADDR(hdr) hdr.a_text
+#endif /* no N_DATADDR */
+
+/* Make COFF and non-COFF names for things a little more compatible
+ to reduce conditionals later. */
+
+#ifndef COFF_FORMAT
+#ifndef AOUTHDR
+#define AOUTHDR struct exec
+#endif
+#endif
+
+extern char *sys_siglist[];
+
+
+/* Hook for `exec_file_command' command to call. */
+
+extern void (*exec_file_display_hook) ();
+
+/* File names of core file and executable file. */
+
+extern char *corefile;
+extern char *execfile;
+
+/* Descriptors on which core file and executable file are open.
+ Note that the execchan is closed when an inferior is created
+ and reopened if the inferior dies or is killed. */
+
+extern int corechan;
+extern int execchan;
+
+/* Last modification time of executable file.
+ Also used in source.c to compare against mtime of a source file. */
+
+extern int exec_mtime;
+
+/* Virtual addresses of bounds of the two areas of memory in the core file. */
+
+extern CORE_ADDR data_start;
+extern CORE_ADDR data_end;
+extern CORE_ADDR stack_start;
+extern CORE_ADDR stack_end;
+
+/* Virtual addresses of bounds of two areas of memory in the exec file.
+ Note that the data area in the exec file is used only when there is no core file. */
+
+extern CORE_ADDR text_start;
+extern CORE_ADDR text_end;
+
+extern CORE_ADDR exec_data_start;
+extern CORE_ADDR exec_data_end;
+
+/* Address in executable file of start of text area data. */
+
+extern int text_offset;
+
+/* Address in executable file of start of data area data. */
+
+extern int exec_data_offset;
+
+/* Address in core file of start of data area data. */
+
+extern int data_offset;
+
+/* Address in core file of start of stack area data. */
+
+extern int stack_offset;
+
+#ifdef COFF_FORMAT
+/* various coff data structures */
+
+extern FILHDR file_hdr;
+extern SCNHDR text_hdr;
+extern SCNHDR data_hdr;
+
+#endif /* not COFF_FORMAT */
+
+/* a.out header saved in core file. */
+
+extern AOUTHDR core_aouthdr;
+
+/* a.out header of exec file. */
+
+extern AOUTHDR exec_aouthdr;
+
+extern void validate_files ();
+
+core_file_command (filename, from_tty)
+ char *filename;
+ int from_tty;
+{
+ int val;
+ extern char registers[];
+
+ /* Discard all vestiges of any previous core file
+ and mark data and stack spaces as empty. */
+
+ if (corefile)
+ free (corefile);
+ corefile = 0;
+
+ if (corechan >= 0)
+ close (corechan);
+ corechan = -1;
+
+ data_start = 0;
+ data_end = 0;
+ stack_start = STACK_END_ADDR;
+ stack_end = STACK_END_ADDR;
+
+ /* Now, if a new core file was specified, open it and digest it. */
+
+ if (filename)
+ {
+ filename = tilde_expand (filename);
+ make_cleanup (free, filename);
+
+ if (have_inferior_p ())
+ error ("To look at a core file, you must kill the inferior with \"kill\".");
+ corechan = open (filename, O_RDONLY, 0);
+ if (corechan < 0)
+ perror_with_name (filename);
+ /* 4.2-style (and perhaps also sysV-style) core dump file. */
+ {
+ struct user u;
+
+ int reg_offset;
+
+ val = myread (corechan, &u, sizeof u);
+ if (val < 0)
+ perror_with_name (filename);
+ data_start = exec_data_start;
+
+ data_end = data_start + NBPG * u.u_dsize;
+ stack_start = stack_end - NBPG * u.u_ssize;
+ data_offset = NBPG * UPAGES;
+ stack_offset = NBPG * (UPAGES + u.u_dsize);
+ reg_offset = (int) u.u_ar0 - KERNEL_U_ADDR;
+
+ /* I don't know where to find this info.
+ So, for now, mark it as not available. */
+/* N_SET_MAGIC (core_aouthdr, 0); */
+ bzero ((char *) &core_aouthdr, sizeof core_aouthdr);
+
+ /* Read the register values out of the core file and store
+ them where `read_register' will find them. */
+
+ {
+ register int regno;
+
+ for (regno = 0; regno < NUM_REGS; regno++)
+ {
+ char buf[MAX_REGISTER_RAW_SIZE];
+
+ val = lseek (corechan, register_addr (regno, reg_offset), 0);
+ if (val < 0)
+ perror_with_name (filename);
+
+ val = myread (corechan, buf, sizeof buf);
+ if (val < 0)
+ perror_with_name (filename);
+ supply_register (regno, buf);
+ }
+ }
+ }
+ if (filename[0] == '/')
+ corefile = savestring (filename, strlen (filename));
+ else
+ {
+ corefile = concat (current_directory, "/", filename);
+ }
+
+ set_current_frame ( create_new_frame (read_register (FP_REGNUM),
+ read_pc ()));
+ select_frame (get_current_frame (), 0);
+ validate_files ();
+ }
+ else if (from_tty)
+ printf ("No core file now.\n");
+}
+
+exec_file_command (filename, from_tty)
+ char *filename;
+ int from_tty;
+{
+ int val;
+
+ /* Eliminate all traces of old exec file.
+ Mark text segment as empty. */
+
+ if (execfile)
+ free (execfile);
+ execfile = 0;
+ data_start = 0;
+ data_end -= exec_data_start;
+ text_start = 0;
+ text_end = 0;
+ exec_data_start = 0;
+ exec_data_end = 0;
+ if (execchan >= 0)
+ close (execchan);
+ execchan = -1;
+
+ /* Now open and digest the file the user requested, if any. */
+
+ if (filename)
+ {
+ filename = tilde_expand (filename);
+ make_cleanup (free, filename);
+
+ execchan = openp (getenv ("PATH"), 1, filename, O_RDONLY, 0,
+ &execfile);
+ if (execchan < 0)
+ perror_with_name (filename);
+
+#ifdef COFF_FORMAT
+ {
+ int aout_hdrsize;
+ int num_sections;
+
+ if (read_file_hdr (execchan, &file_hdr) < 0)
+ error ("\"%s\": not in executable format.", execfile);
+
+ aout_hdrsize = file_hdr.f_opthdr;
+ num_sections = file_hdr.f_nscns;
+
+ if (read_aout_hdr (execchan, &exec_aouthdr, aout_hdrsize) < 0)
+ error ("\"%s\": can't read optional aouthdr", execfile);
+
+ if (read_section_hdr (execchan, _TEXT, &text_hdr, num_sections,
+ aout_hdrsize) < 0)
+ error ("\"%s\": can't read text section header", execfile);
+
+ if (read_section_hdr (execchan, _DATA, &data_hdr, num_sections,
+ aout_hdrsize) < 0)
+ error ("\"%s\": can't read data section header", execfile);
+
+ text_start = exec_aouthdr.text_start;
+ text_end = text_start + exec_aouthdr.tsize;
+ text_offset = text_hdr.s_scnptr;
+ exec_data_start = exec_aouthdr.data_start;
+ exec_data_end = exec_data_start + exec_aouthdr.dsize;
+ exec_data_offset = data_hdr.s_scnptr;
+ data_start = exec_data_start;
+ data_end += exec_data_start;
+ exec_mtime = file_hdr.f_timdat;
+ }
+#else /* not COFF_FORMAT */
+ {
+ struct stat st_exec;
+
+#ifdef HEADER_SEEK_FD
+ HEADER_SEEK_FD (execchan);
+#endif
+
+ val = myread (execchan, &exec_aouthdr, sizeof (AOUTHDR));
+
+ if (val < 0)
+ perror_with_name (filename);
+
+ text_start = N_TXTADDR (exec_aouthdr);
+ exec_data_start = N_DATADDR (exec_aouthdr);
+
+ text_offset = N_TXTOFF (exec_aouthdr);
+ exec_data_offset = N_TXTOFF (exec_aouthdr) + exec_aouthdr.a_text;
+
+ text_end = text_start + exec_aouthdr.a_text;
+ exec_data_end = exec_data_start + exec_aouthdr.a_data;
+ data_start = exec_data_start;
+ data_end += exec_data_start;
+
+ fstat (execchan, &st_exec);
+ exec_mtime = st_exec.st_mtime;
+ }
+#endif /* not COFF_FORMAT */
+
+ validate_files ();
+ }
+ else if (from_tty)
+ printf ("No exec file now.\n");
+
+ /* Tell display code (if any) about the changed file name. */
+ if (exec_file_display_hook)
+ (*exec_file_display_hook) (filename);
+}
+
+/* helper functions for m-i386.h */
+
+/* stdio style buffering to minimize calls to ptrace */
+static CORE_ADDR codestream_next_addr;
+static CORE_ADDR codestream_addr;
+static unsigned char codestream_buf[sizeof (int)];
+static int codestream_off;
+static int codestream_cnt;
+
+#define codestream_tell() (codestream_addr + codestream_off)
+#define codestream_peek() (codestream_cnt == 0 ? \
+ codestream_fill(1): codestream_buf[codestream_off])
+#define codestream_get() (codestream_cnt-- == 0 ? \
+ codestream_fill(0) : codestream_buf[codestream_off++])
+
+static unsigned char
+codestream_fill (peek_flag)
+{
+ codestream_addr = codestream_next_addr;
+ codestream_next_addr += sizeof (int);
+ codestream_off = 0;
+ codestream_cnt = sizeof (int);
+ read_memory (codestream_addr,
+ (unsigned char *)codestream_buf,
+ sizeof (int));
+
+ if (peek_flag)
+ return (codestream_peek());
+ else
+ return (codestream_get());
+}
+
+static void
+codestream_seek (place)
+{
+ codestream_next_addr = place & -sizeof (int);
+ codestream_cnt = 0;
+ codestream_fill (1);
+ while (codestream_tell() != place)
+ codestream_get ();
+}
+
+static void
+codestream_read (buf, count)
+ unsigned char *buf;
+{
+ unsigned char *p;
+ int i;
+ p = buf;
+ for (i = 0; i < count; i++)
+ *p++ = codestream_get ();
+}
+
+/* next instruction is a jump, move to target */
+static
+i386_follow_jump ()
+{
+ int long_delta;
+ short short_delta;
+ char byte_delta;
+ int data16;
+ int pos;
+
+ pos = codestream_tell ();
+
+ data16 = 0;
+ if (codestream_peek () == 0x66)
+ {
+ codestream_get ();
+ data16 = 1;
+ }
+
+ switch (codestream_get ())
+ {
+ case 0xe9:
+ /* relative jump: if data16 == 0, disp32, else disp16 */
+ if (data16)
+ {
+ codestream_read ((unsigned char *)&short_delta, 2);
+ pos += short_delta + 3; /* include size of jmp inst */
+ }
+ else
+ {
+ codestream_read ((unsigned char *)&long_delta, 4);
+ pos += long_delta + 5;
+ }
+ break;
+ case 0xeb:
+ /* relative jump, disp8 (ignore data16) */
+ codestream_read ((unsigned char *)&byte_delta, 1);
+ pos += byte_delta + 2;
+ break;
+ }
+ codestream_seek (pos + data16);
+}
+
+/*
+ * find & return amound a local space allocated, and advance codestream to
+ * first register push (if any)
+ *
+ * if entry sequence doesn't make sense, return -1, and leave
+ * codestream pointer random
+ */
+static long
+i386_get_frame_setup (pc)
+{
+ unsigned char op;
+
+ codestream_seek (pc);
+
+ i386_follow_jump ();
+
+ op = codestream_get ();
+
+ if (op == 0x58) /* popl %eax */
+ {
+ /*
+ * this function must start with
+ *
+ * popl %eax 0x58
+ * xchgl %eax, (%esp) 0x87 0x04 0x24
+ * or xchgl %eax, 0(%esp) 0x87 0x44 0x24 0x00
+ *
+ * (the system 5 compiler puts out the second xchg
+ * inst, and the assembler doesn't try to optimize it,
+ * so the 'sib' form gets generated)
+ *
+ * this sequence is used to get the address of the return
+ * buffer for a function that returns a structure
+ */
+ int pos;
+ unsigned char buf[4];
+ static unsigned char proto1[3] = { 0x87,0x04,0x24 };
+ static unsigned char proto2[4] = { 0x87,0x44,0x24,0x00 };
+ pos = codestream_tell ();
+ codestream_read (buf, 4);
+ if (bcmp (buf, proto1, 3) == 0)
+ pos += 3;
+ else if (bcmp (buf, proto2, 4) == 0)
+ pos += 4;
+
+ codestream_seek (pos);
+ op = codestream_get (); /* update next opcode */
+ }
+
+ if (op == 0x55) /* pushl %esp */
+ {
+ /* check for movl %esp, %ebp - can be written two ways */
+ switch (codestream_get ())
+ {
+ case 0x8b:
+ if (codestream_get () != 0xec)
+ return (-1);
+ break;
+ case 0x89:
+ if (codestream_get () != 0xe5)
+ return (-1);
+ break;
+ default:
+ return (-1);
+ }
+ /* check for stack adjustment
+ *
+ * subl $XXX, %esp
+ *
+ * note: you can't subtract a 16 bit immediate
+ * from a 32 bit reg, so we don't have to worry
+ * about a data16 prefix
+ */
+ op = codestream_peek ();
+ if (op == 0x83)
+ {
+ /* subl with 8 bit immed */
+ codestream_get ();
+ if (codestream_get () != 0xec)
+ return (-1);
+ /* subl with signed byte immediate
+ * (though it wouldn't make sense to be negative)
+ */
+ return (codestream_get());
+ }
+ else if (op == 0x81)
+ {
+ /* subl with 32 bit immed */
+ int locals;
+ codestream_get();
+ if (codestream_get () != 0xec)
+ return (-1);
+ /* subl with 32 bit immediate */
+ codestream_read ((unsigned char *)&locals, 4);
+ return (locals);
+ }
+ else
+ {
+ return (0);
+ }
+ }
+ else if (op == 0xc8)
+ {
+ /* enter instruction: arg is 16 bit unsigned immed */
+ unsigned short slocals;
+ codestream_read ((unsigned char *)&slocals, 2);
+ codestream_get (); /* flush final byte of enter instruction */
+ return (slocals);
+ }
+ return (-1);
+}
+
+/* Return number of args passed to a frame.
+ Can return -1, meaning no way to tell. */
+
+/* on the 386, the instruction following the call could be:
+ * popl %ecx - one arg
+ * addl $imm, %esp - imm/4 args; imm may be 8 or 32 bits
+ * anything else - zero args
+ */
+
+int
+i386_frame_num_args (fi)
+ struct frame_info fi;
+{
+ int retpc;
+ unsigned char op;
+ struct frame_info *pfi;
+
+ pfi = get_prev_frame_info ((fi));
+ if (pfi == 0)
+ {
+ /* Note: this can happen if we are looking at the frame for
+ main, because FRAME_CHAIN_VALID won't let us go into
+ start. If we have debugging symbols, that's not really
+ a big deal; it just means it will only show as many arguments
+ to main as are declared. */
+ return -1;
+ }
+ else
+ {
+ retpc = pfi->pc;
+ op = read_memory_integer (retpc, 1);
+ if (op == 0x59)
+ /* pop %ecx */
+ return 1;
+ else if (op == 0x83)
+ {
+ op = read_memory_integer (retpc+1, 1);
+ if (op == 0xc4)
+ /* addl $<signed imm 8 bits>, %esp */
+ return (read_memory_integer (retpc+2,1)&0xff)/4;
+ else
+ return 0;
+ }
+ else if (op == 0x81)
+ { /* add with 32 bit immediate */
+ op = read_memory_integer (retpc+1, 1);
+ if (op == 0xc4)
+ /* addl $<imm 32>, %esp */
+ return read_memory_integer (retpc+2, 4) / 4;
+ else
+ return 0;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+}
+
+/*
+ * parse the first few instructions of the function to see
+ * what registers were stored.
+ *
+ * We handle these cases:
+ *
+ * The startup sequence can be at the start of the function,
+ * or the function can start with a branch to startup code at the end.
+ *
+ * %ebp can be set up with either the 'enter' instruction, or
+ * 'pushl %ebp, movl %esp, %ebp' (enter is too slow to be useful,
+ * but was once used in the sys5 compiler)
+ *
+ * Local space is allocated just below the saved %ebp by either the
+ * 'enter' instruction, or by 'subl $<size>, %esp'. 'enter' has
+ * a 16 bit unsigned argument for space to allocate, and the
+ * 'addl' instruction could have either a signed byte, or
+ * 32 bit immediate.
+ *
+ * Next, the registers used by this function are pushed. In
+ * the sys5 compiler they will always be in the order: %edi, %esi, %ebx
+ * (and sometimes a harmless bug causes it to also save but not restore %eax);
+ * however, the code below is willing to see the pushes in any order,
+ * and will handle up to 8 of them.
+ *
+ * If the setup sequence is at the end of the function, then the
+ * next instruction will be a branch back to the start.
+ */
+
+i386_frame_find_saved_regs (fip, fsrp)
+ struct frame_info *fip;
+ struct frame_saved_regs *fsrp;
+{
+ unsigned long locals;
+ unsigned char *p;
+ unsigned char op;
+ CORE_ADDR dummy_bottom;
+ CORE_ADDR adr;
+ int i;
+
+ bzero (fsrp, sizeof *fsrp);
+
+ /* if frame is the end of a dummy, compute where the
+ * beginning would be
+ */
+ dummy_bottom = fip->frame - 4 - NUM_REGS*4 - CALL_DUMMY_LENGTH;
+
+ /* check if the PC is in the stack, in a dummy frame */
+ if (dummy_bottom <= fip->pc && fip->pc <= fip->frame)
+ {
+ /* all regs were saved by push_call_dummy () */
+ adr = fip->frame - 4;
+ for (i = 0; i < NUM_REGS; i++)
+ {
+ fsrp->regs[i] = adr;
+ adr -= 4;
+ }
+ return;
+ }
+
+ locals = i386_get_frame_setup (get_pc_function_start (fip->pc));
+
+ if (locals >= 0)
+ {
+ adr = fip->frame - 4 - locals;
+ for (i = 0; i < 8; i++)
+ {
+ op = codestream_get ();
+ if (op < 0x50 || op > 0x57)
+ break;
+ fsrp->regs[op - 0x50] = adr;
+ adr -= 4;
+ }
+ }
+
+ fsrp->regs[PC_REGNUM] = fip->frame + 4;
+ fsrp->regs[FP_REGNUM] = fip->frame;
+}
+
+/* return pc of first real instruction */
+i386_skip_prologue (pc)
+{
+ unsigned char op;
+ int i;
+
+ if (i386_get_frame_setup (pc) < 0)
+ return (pc);
+
+ /* found valid frame setup - codestream now points to
+ * start of push instructions for saving registers
+ */
+
+ /* skip over register saves */
+ for (i = 0; i < 8; i++)
+ {
+ op = codestream_peek ();
+ /* break if not pushl inst */
+ if (op < 0x50 || op > 0x57)
+ break;
+ codestream_get ();
+ }
+
+ i386_follow_jump ();
+
+ return (codestream_tell ());
+}
+
+i386_push_dummy_frame ()
+{
+ CORE_ADDR sp = read_register (SP_REGNUM);
+ int regnum;
+
+ sp = push_word (sp, read_register (PC_REGNUM));
+ sp = push_word (sp, read_register (FP_REGNUM));
+ write_register (FP_REGNUM, sp);
+ for (regnum = 0; regnum < NUM_REGS; regnum++)
+ sp = push_word (sp, read_register (regnum));
+ write_register (SP_REGNUM, sp);
+}
+
+i386_pop_frame ()
+{
+ FRAME frame = get_current_frame ();
+ CORE_ADDR fp;
+ int regnum;
+ struct frame_saved_regs fsr;
+ struct frame_info *fi;
+
+ fi = get_frame_info (frame);
+ fp = fi->frame;
+ get_frame_saved_regs (fi, &fsr);
+ for (regnum = 0; regnum < NUM_REGS; regnum++)
+ {
+ CORE_ADDR adr;
+ adr = fsr.regs[regnum];
+ if (adr)
+ write_register (regnum, read_memory_integer (adr, 4));
+ }
+ write_register (FP_REGNUM, read_memory_integer (fp, 4));
+ write_register (PC_REGNUM, read_memory_integer (fp + 4, 4));
+ write_register (SP_REGNUM, fp + 8);
+ flush_cached_frames ();
+ set_current_frame ( create_new_frame (read_register (FP_REGNUM),
+ read_pc ()));
+}
+
+/* this table must line up with REGISTER_NAMES in m-i386.h */
+/* symbols like 'EAX' come from <sys/reg.h> */
+static int regmap[] =
+{
+ EAX, ECX, EDX, EBX,
+ UESP, EBP, ESI, EDI,
+ EIP, EFL, CS, SS,
+ DS, ES, FS, GS,
+};
+
+/* blockend is the value of u.u_ar0, and points to the
+ * place where GS is stored
+ */
+i386_register_u_addr (blockend, regnum)
+{
+#if 0
+ /* this will be needed if fp registers are reinstated */
+ /* for now, you can look at them with 'info float'
+ * sys5 wont let you change them with ptrace anyway
+ */
+ if (regnum >= FP0_REGNUM && regnum <= FP7_REGNUM)
+ {
+ int ubase, fpstate;
+ struct user u;
+ ubase = blockend + 4 * (SS + 1) - KSTKSZ;
+ fpstate = ubase + ((char *)&u.u_fpstate - (char *)&u);
+ return (fpstate + 0x1c + 10 * (regnum - FP0_REGNUM));
+ }
+ else
+#endif
+ return (blockend + 4 * regmap[regnum]);
+
+}
+
+i387_to_double (from, to)
+ char *from;
+ char *to;
+{
+ long *lp;
+ /* push extended mode on 387 stack, then pop in double mode
+ *
+ * first, set exception masks so no error is generated -
+ * number will be rounded to inf or 0, if necessary
+ */
+ asm ("pushl %eax"); /* grab a stack slot */
+ asm ("fstcw (%esp)"); /* get 387 control word */
+ asm ("movl (%esp),%eax"); /* save old value */
+ asm ("orl $0x3f,%eax"); /* mask all exceptions */
+ asm ("pushl %eax");
+ asm ("fldcw (%esp)"); /* load new value into 387 */
+
+ asm ("movl 8(%ebp),%eax");
+ asm ("fldt (%eax)"); /* push extended number on 387 stack */
+ asm ("fwait");
+ asm ("movl 12(%ebp),%eax");
+ asm ("fstpl (%eax)"); /* pop double */
+ asm ("fwait");
+
+ asm ("popl %eax"); /* flush modified control word */
+ asm ("fnclex"); /* clear exceptions */
+ asm ("fldcw (%esp)"); /* restore original control word */
+ asm ("popl %eax"); /* flush saved copy */
+}
+
+double_to_i387 (from, to)
+ char *from;
+ char *to;
+{
+ /* push double mode on 387 stack, then pop in extended mode
+ * no errors are possible because every 64-bit pattern
+ * can be converted to an extended
+ */
+ asm ("movl 8(%ebp),%eax");
+ asm ("fldl (%eax)");
+ asm ("fwait");
+ asm ("movl 12(%ebp),%eax");
+ asm ("fstpt (%eax)");
+ asm ("fwait");
+}
+
+struct env387
+{
+ unsigned short control;
+ unsigned short r0;
+ unsigned short status;
+ unsigned short r1;
+ unsigned short tag;
+ unsigned short r2;
+ unsigned long eip;
+ unsigned short code_seg;
+ unsigned short opcode;
+ unsigned long operand;
+ unsigned short operand_seg;
+ unsigned short r3;
+ unsigned char regs[8][10];
+};
+
+static
+print_387_control_word (control)
+unsigned short control;
+{
+ printf ("control 0x%04x: ", control);
+ printf ("compute to ");
+ switch ((control >> 8) & 3)
+ {
+ case 0: printf ("24 bits; "); break;
+ case 1: printf ("(bad); "); break;
+ case 2: printf ("53 bits; "); break;
+ case 3: printf ("64 bits; "); break;
+ }
+ printf ("round ");
+ switch ((control >> 10) & 3)
+ {
+ case 0: printf ("NEAREST; "); break;
+ case 1: printf ("DOWN; "); break;
+ case 2: printf ("UP; "); break;
+ case 3: printf ("CHOP; "); break;
+ }
+ if (control & 0x3f)
+ {
+ printf ("mask:");
+ if (control & 0x0001) printf (" INVALID");
+ if (control & 0x0002) printf (" DENORM");
+ if (control & 0x0004) printf (" DIVZ");
+ if (control & 0x0008) printf (" OVERF");
+ if (control & 0x0010) printf (" UNDERF");
+ if (control & 0x0020) printf (" LOS");
+ printf (";");
+ }
+ printf ("\n");
+ if (control & 0xe080) printf ("warning: reserved bits on 0x%x\n",
+ control & 0xe080);
+}
+
+static
+print_387_status_word (status)
+ unsigned short status;
+{
+ printf ("status 0x%04x: ", status);
+ if (status & 0xff)
+ {
+ printf ("exceptions:");
+ if (status & 0x0001) printf (" INVALID");
+ if (status & 0x0002) printf (" DENORM");
+ if (status & 0x0004) printf (" DIVZ");
+ if (status & 0x0008) printf (" OVERF");
+ if (status & 0x0010) printf (" UNDERF");
+ if (status & 0x0020) printf (" LOS");
+ if (status & 0x0040) printf (" FPSTACK");
+ printf ("; ");
+ }
+ printf ("flags: %d%d%d%d; ",
+ (status & 0x4000) != 0,
+ (status & 0x0400) != 0,
+ (status & 0x0200) != 0,
+ (status & 0x0100) != 0);
+
+ printf ("top %d\n", (status >> 11) & 7);
+}
+
+static
+print_387_status (status, ep)
+ unsigned short status;
+ struct env387 *ep;
+{
+ int i;
+ int bothstatus;
+ int top;
+ int fpreg;
+ unsigned char *p;
+
+ bothstatus = ((status != 0) && (ep->status != 0));
+ if (status != 0)
+ {
+ if (bothstatus)
+ printf ("u: ");
+ print_387_status_word (status);
+ }
+
+ if (ep->status != 0)
+ {
+ if (bothstatus)
+ printf ("e: ");
+ print_387_status_word (ep->status);
+ }
+
+ print_387_control_word (ep->control);
+ printf ("last exception: ");
+ printf ("opcode 0x%x; ", ep->opcode);
+ printf ("pc 0x%x:0x%x; ", ep->code_seg, ep->eip);
+ printf ("operand 0x%x:0x%x\n", ep->operand_seg, ep->operand);
+
+ top = (ep->status >> 11) & 7;
+
+ printf ("regno tag msb lsb value\n");
+ for (fpreg = 7; fpreg >= 0; fpreg--)
+ {
+ double val;
+
+ printf ("%s %d: ", fpreg == top ? "=>" : " ", fpreg);
+
+ switch ((ep->tag >> (fpreg * 2)) & 3)
+ {
+ case 0: printf ("valid "); break;
+ case 1: printf ("zero "); break;
+ case 2: printf ("trap "); break;
+ case 3: printf ("empty "); break;
+ }
+ for (i = 9; i >= 0; i--)
+ printf ("%02x", ep->regs[fpreg][i]);
+
+ i387_to_double (ep->regs[fpreg], (char *)&val);
+ printf (" %g\n", val);
+ }
+ if (ep->r0)
+ printf ("warning: reserved0 is 0x%x\n", ep->r0);
+ if (ep->r1)
+ printf ("warning: reserved1 is 0x%x\n", ep->r1);
+ if (ep->r2)
+ printf ("warning: reserved2 is 0x%x\n", ep->r2);
+ if (ep->r3)
+ printf ("warning: reserved3 is 0x%x\n", ep->r3);
+}
+
+#ifndef U_FPSTATE
+#define U_FPSTATE(u) u.u_fpstate
+#endif
+
+i386_float_info ()
+{
+ struct user u; /* just for address computations */
+ int i;
+ /* fpstate defined in <sys/user.h> */
+ struct fpstate *fpstatep;
+ char buf[sizeof (struct fpstate) + 2 * sizeof (int)];
+ unsigned int uaddr;
+ char fpvalid;
+ unsigned int rounded_addr;
+ unsigned int rounded_size;
+ extern int corechan;
+ int skip;
+
+ uaddr = (char *)&u.u_fpvalid - (char *)&u;
+ if (have_inferior_p())
+ {
+ unsigned int data;
+ unsigned int mask;
+
+ rounded_addr = uaddr & -sizeof (int);
+ data = ptrace (3, inferior_pid, rounded_addr, 0);
+ mask = 0xff << ((uaddr - rounded_addr) * 8);
+
+ fpvalid = ((data & mask) != 0);
+ }
+ else
+ {
+ if (lseek (corechan, uaddr, 0) < 0)
+ perror ("seek on core file");
+ if (myread (corechan, &fpvalid, 1) < 0)
+ perror ("read on core file");
+
+ }
+
+ if (fpvalid == 0)
+ {
+ printf ("no floating point status saved\n");
+ return;
+ }
+
+ uaddr = (char *)&U_FPSTATE(u) - (char *)&u;
+ if (have_inferior_p ())
+ {
+ int *ip;
+
+ rounded_addr = uaddr & -sizeof (int);
+ rounded_size = (((uaddr + sizeof (struct fpstate)) - uaddr) +
+ sizeof (int) - 1) / sizeof (int);
+ skip = uaddr - rounded_addr;
+
+ ip = (int *)buf;
+ for (i = 0; i < rounded_size; i++)
+ {
+ *ip++ = ptrace (3, inferior_pid, rounded_addr, 0);
+ rounded_addr += sizeof (int);
+ }
+ }
+ else
+ {
+ if (lseek (corechan, uaddr, 0) < 0)
+ perror_with_name ("seek on core file");
+ if (myread (corechan, buf, sizeof (struct fpstate)) < 0)
+ perror_with_name ("read from core file");
+ skip = 0;
+ }
+
+ fpstatep = (struct fpstate *)(buf + skip);
+ print_387_status (fpstatep->status, (struct env387 *)fpstatep->state);
+}
+
diff --git a/gnu/usr.bin/gdb/config/i386-pinsn.c b/gnu/usr.bin/gdb/config/i386-pinsn.c
new file mode 100644
index 0000000..649baaf
--- /dev/null
+++ b/gnu/usr.bin/gdb/config/i386-pinsn.c
@@ -0,0 +1,1812 @@
+/* Print i386 instructions for GDB, the GNU debugger.
+ Copyright (C) 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * 80386 instruction printer by Pace Willisson (pace@prep.ai.mit.edu)
+ * July 1988
+ */
+
+/*
+ * The main tables describing the instructions is essentially a copy
+ * of the "Opcode Map" chapter (Appendix A) of the Intel 80386
+ * Programmers Manual. Usually, there is a capital letter, followed
+ * by a small letter. The capital letter tell the addressing mode,
+ * and the small letter tells about the operand size. Refer to
+ * the Intel manual for details.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#define Eb OP_E, b_mode
+#define indirEb OP_indirE, b_mode
+#define Gb OP_G, b_mode
+#define Ev OP_E, v_mode
+#define indirEv OP_indirE, v_mode
+#define Ew OP_E, w_mode
+#define Ma OP_E, v_mode
+#define M OP_E, 0
+#define Mp OP_E, 0 /* ? */
+#define Gv OP_G, v_mode
+#define Gw OP_G, w_mode
+#define Rw OP_rm, w_mode
+#define Rd OP_rm, d_mode
+#define Ib OP_I, b_mode
+#define sIb OP_sI, b_mode /* sign extened byte */
+#define Iv OP_I, v_mode
+#define Iw OP_I, w_mode
+#define Jb OP_J, b_mode
+#define Jv OP_J, v_mode
+#define ONE OP_ONE, 0
+#define Cd OP_C, d_mode
+#define Dd OP_D, d_mode
+#define Td OP_T, d_mode
+
+#define eAX OP_REG, eAX_reg
+#define eBX OP_REG, eBX_reg
+#define eCX OP_REG, eCX_reg
+#define eDX OP_REG, eDX_reg
+#define eSP OP_REG, eSP_reg
+#define eBP OP_REG, eBP_reg
+#define eSI OP_REG, eSI_reg
+#define eDI OP_REG, eDI_reg
+#define AL OP_REG, al_reg
+#define CL OP_REG, cl_reg
+#define DL OP_REG, dl_reg
+#define BL OP_REG, bl_reg
+#define AH OP_REG, ah_reg
+#define CH OP_REG, ch_reg
+#define DH OP_REG, dh_reg
+#define BH OP_REG, bh_reg
+#define AX OP_REG, ax_reg
+#define DX OP_REG, dx_reg
+#define indirDX OP_REG, indir_dx_reg
+
+#define Sw OP_SEG, w_mode
+#define Ap OP_DIR, lptr
+#define Av OP_DIR, v_mode
+#define Ob OP_OFF, b_mode
+#define Ov OP_OFF, v_mode
+#define Xb OP_DSSI, b_mode
+#define Xv OP_DSSI, v_mode
+#define Yb OP_ESDI, b_mode
+#define Yv OP_ESDI, v_mode
+
+#define es OP_REG, es_reg
+#define ss OP_REG, ss_reg
+#define cs OP_REG, cs_reg
+#define ds OP_REG, ds_reg
+#define fs OP_REG, fs_reg
+#define gs OP_REG, gs_reg
+
+int OP_E(), OP_indirE(), OP_G(), OP_I(), OP_sI(), OP_REG();
+int OP_J(), OP_SEG();
+int OP_DIR(), OP_OFF(), OP_DSSI(), OP_ESDI(), OP_ONE(), OP_C();
+int OP_D(), OP_T(), OP_rm();
+
+
+#define b_mode 1
+#define v_mode 2
+#define w_mode 3
+#define d_mode 4
+
+#define es_reg 100
+#define cs_reg 101
+#define ss_reg 102
+#define ds_reg 103
+#define fs_reg 104
+#define gs_reg 105
+#define eAX_reg 107
+#define eCX_reg 108
+#define eDX_reg 109
+#define eBX_reg 110
+#define eSP_reg 111
+#define eBP_reg 112
+#define eSI_reg 113
+#define eDI_reg 114
+
+#define lptr 115
+
+#define al_reg 116
+#define cl_reg 117
+#define dl_reg 118
+#define bl_reg 119
+#define ah_reg 120
+#define ch_reg 121
+#define dh_reg 122
+#define bh_reg 123
+
+#define ax_reg 124
+#define cx_reg 125
+#define dx_reg 126
+#define bx_reg 127
+#define sp_reg 128
+#define bp_reg 129
+#define si_reg 130
+#define di_reg 131
+
+#define indir_dx_reg 150
+
+#define GRP1b NULL, NULL, 0
+#define GRP1S NULL, NULL, 1
+#define GRP1Ss NULL, NULL, 2
+#define GRP2b NULL, NULL, 3
+#define GRP2S NULL, NULL, 4
+#define GRP2b_one NULL, NULL, 5
+#define GRP2S_one NULL, NULL, 6
+#define GRP2b_cl NULL, NULL, 7
+#define GRP2S_cl NULL, NULL, 8
+#define GRP3b NULL, NULL, 9
+#define GRP3S NULL, NULL, 10
+#define GRP4 NULL, NULL, 11
+#define GRP5 NULL, NULL, 12
+#define GRP6 NULL, NULL, 13
+#define GRP7 NULL, NULL, 14
+#define GRP8 NULL, NULL, 15
+
+#define FLOATCODE 50
+#define FLOAT NULL, NULL, FLOATCODE
+
+struct dis386 {
+ char *name;
+ int (*op1)();
+ int bytemode1;
+ int (*op2)();
+ int bytemode2;
+ int (*op3)();
+ int bytemode3;
+};
+
+struct dis386 dis386[] = {
+ /* 00 */
+ { "addb", Eb, Gb },
+ { "addS", Ev, Gv },
+ { "addb", Gb, Eb },
+ { "addS", Gv, Ev },
+ { "addb", AL, Ib },
+ { "addS", eAX, Iv },
+ { "pushl", es },
+ { "popl", es },
+ /* 08 */
+ { "orb", Eb, Gb },
+ { "orS", Ev, Gv },
+ { "orb", Gb, Eb },
+ { "orS", Gv, Ev },
+ { "orb", AL, Ib },
+ { "orS", eAX, Iv },
+ { "pushl", cs },
+ { "(bad)" }, /* 0x0f extended opcode escape */
+ /* 10 */
+ { "adcb", Eb, Gb },
+ { "adcS", Ev, Gv },
+ { "adcb", Gb, Eb },
+ { "adcS", Gv, Ev },
+ { "adcb", AL, Ib },
+ { "adcS", eAX, Iv },
+ { "pushl", ss },
+ { "popl", ss },
+ /* 18 */
+ { "sbbb", Eb, Gb },
+ { "sbbS", Ev, Gv },
+ { "sbbb", Gb, Eb },
+ { "sbbS", Gv, Ev },
+ { "sbbb", AL, Ib },
+ { "sbbS", eAX, Iv },
+ { "pushl", ds },
+ { "popl", ds },
+ /* 20 */
+ { "andb", Eb, Gb },
+ { "andS", Ev, Gv },
+ { "andb", Gb, Eb },
+ { "andS", Gv, Ev },
+ { "andb", AL, Ib },
+ { "andS", eAX, Iv },
+ { "(bad)" }, /* SEG ES prefix */
+ { "daa" },
+ /* 28 */
+ { "subb", Eb, Gb },
+ { "subS", Ev, Gv },
+ { "subb", Gb, Eb },
+ { "subS", Gv, Ev },
+ { "subb", AL, Ib },
+ { "subS", eAX, Iv },
+ { "(bad)" }, /* SEG CS prefix */
+ { "das" },
+ /* 30 */
+ { "xorb", Eb, Gb },
+ { "xorS", Ev, Gv },
+ { "xorb", Gb, Eb },
+ { "xorS", Gv, Ev },
+ { "xorb", AL, Ib },
+ { "xorS", eAX, Iv },
+ { "(bad)" }, /* SEG SS prefix */
+ { "aaa" },
+ /* 38 */
+ { "cmpb", Eb, Gb },
+ { "cmpS", Ev, Gv },
+ { "cmpb", Gb, Eb },
+ { "cmpS", Gv, Ev },
+ { "cmpb", AL, Ib },
+ { "cmpS", eAX, Iv },
+ { "(bad)" }, /* SEG DS prefix */
+ { "aas" },
+ /* 40 */
+ { "incS", eAX },
+ { "incS", eCX },
+ { "incS", eDX },
+ { "incS", eBX },
+ { "incS", eSP },
+ { "incS", eBP },
+ { "incS", eSI },
+ { "incS", eDI },
+ /* 48 */
+ { "decS", eAX },
+ { "decS", eCX },
+ { "decS", eDX },
+ { "decS", eBX },
+ { "decS", eSP },
+ { "decS", eBP },
+ { "decS", eSI },
+ { "decS", eDI },
+ /* 50 */
+ { "pushS", eAX },
+ { "pushS", eCX },
+ { "pushS", eDX },
+ { "pushS", eBX },
+ { "pushS", eSP },
+ { "pushS", eBP },
+ { "pushS", eSI },
+ { "pushS", eDI },
+ /* 58 */
+ { "popS", eAX },
+ { "popS", eCX },
+ { "popS", eDX },
+ { "popS", eBX },
+ { "popS", eSP },
+ { "popS", eBP },
+ { "popS", eSI },
+ { "popS", eDI },
+ /* 60 */
+ { "pusha" },
+ { "popa" },
+ { "boundS", Gv, Ma },
+ { "arpl", Ew, Gw },
+ { "(bad)" }, /* seg fs */
+ { "(bad)" }, /* seg gs */
+ { "(bad)" }, /* op size prefix */
+ { "(bad)" }, /* adr size prefix */
+ /* 68 */
+ { "pushS", Iv }, /* 386 book wrong */
+ { "imulS", Gv, Ev, Iv },
+ { "pushl", sIb }, /* push of byte really pushes 4 bytes */
+ { "imulS", Gv, Ev, Ib },
+ { "insb", Yb, indirDX },
+ { "insS", Yv, indirDX },
+ { "outsb", indirDX, Xb },
+ { "outsS", indirDX, Xv },
+ /* 70 */
+ { "jo", Jb },
+ { "jno", Jb },
+ { "jb", Jb },
+ { "jae", Jb },
+ { "je", Jb },
+ { "jne", Jb },
+ { "jbe", Jb },
+ { "ja", Jb },
+ /* 78 */
+ { "js", Jb },
+ { "jns", Jb },
+ { "jp", Jb },
+ { "jnp", Jb },
+ { "jl", Jb },
+ { "jnl", Jb },
+ { "jle", Jb },
+ { "jg", Jb },
+ /* 80 */
+ { GRP1b },
+ { GRP1S },
+ { "(bad)" },
+ { GRP1Ss },
+ { "testb", Eb, Gb },
+ { "testS", Ev, Gv },
+ { "xchgb", Eb, Gb },
+ { "xchgS", Ev, Gv },
+ /* 88 */
+ { "movb", Eb, Gb },
+ { "movS", Ev, Gv },
+ { "movb", Gb, Eb },
+ { "movS", Gv, Ev },
+ { "movw", Ew, Sw },
+ { "leaS", Gv, M },
+ { "movw", Sw, Ew },
+ { "popS", Ev },
+ /* 90 */
+ { "nop" },
+ { "xchgS", eCX, eAX },
+ { "xchgS", eDX, eAX },
+ { "xchgS", eBX, eAX },
+ { "xchgS", eSP, eAX },
+ { "xchgS", eBP, eAX },
+ { "xchgS", eSI, eAX },
+ { "xchgS", eDI, eAX },
+ /* 98 */
+ { "cwtl" },
+ { "cltd" },
+ { "lcall", Ap },
+ { "(bad)" }, /* fwait */
+ { "pushf" },
+ { "popf" },
+ { "sahf" },
+ { "lahf" },
+ /* a0 */
+ { "movb", AL, Ob },
+ { "movS", eAX, Ov },
+ { "movb", Ob, AL },
+ { "movS", Ov, eAX },
+ { "movsb", Yb, Xb },
+ { "movsS", Yv, Xv },
+ { "cmpsb", Yb, Xb },
+ { "cmpsS", Yv, Xv },
+ /* a8 */
+ { "testb", AL, Ib },
+ { "testS", eAX, Iv },
+ { "stosb", Yb, AL },
+ { "stosS", Yv, eAX },
+ { "lodsb", AL, Xb },
+ { "lodsS", eAX, Xv },
+ { "scasb", AL, Xb },
+ { "scasS", eAX, Xv },
+ /* b0 */
+ { "movb", AL, Ib },
+ { "movb", CL, Ib },
+ { "movb", DL, Ib },
+ { "movb", BL, Ib },
+ { "movb", AH, Ib },
+ { "movb", CH, Ib },
+ { "movb", DH, Ib },
+ { "movb", BH, Ib },
+ /* b8 */
+ { "movS", eAX, Iv },
+ { "movS", eCX, Iv },
+ { "movS", eDX, Iv },
+ { "movS", eBX, Iv },
+ { "movS", eSP, Iv },
+ { "movS", eBP, Iv },
+ { "movS", eSI, Iv },
+ { "movS", eDI, Iv },
+ /* c0 */
+ { GRP2b },
+ { GRP2S },
+ { "ret", Iw },
+ { "ret" },
+ { "lesS", Gv, Mp },
+ { "ldsS", Gv, Mp },
+ { "movb", Eb, Ib },
+ { "movS", Ev, Iv },
+ /* c8 */
+ { "enter", Iw, Ib },
+ { "leave" },
+ { "lret", Iw },
+ { "lret" },
+ { "int3" },
+ { "int", Ib },
+ { "into" },
+ { "iret" },
+ /* d0 */
+ { GRP2b_one },
+ { GRP2S_one },
+ { GRP2b_cl },
+ { GRP2S_cl },
+ { "aam", Ib },
+ { "aad", Ib },
+ { "(bad)" },
+ { "xlat" },
+ /* d8 */
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ /* e0 */
+ { "loopne", Jb },
+ { "loope", Jb },
+ { "loop", Jb },
+ { "jCcxz", Jb },
+ { "inb", AL, Ib },
+ { "inS", eAX, Ib },
+ { "outb", Ib, AL },
+ { "outS", Ib, eAX },
+ /* e8 */
+ { "call", Av },
+ { "jmp", Jv },
+ { "ljmp", Ap },
+ { "jmp", Jb },
+ { "inb", AL, indirDX },
+ { "inS", eAX, indirDX },
+ { "outb", indirDX, AL },
+ { "outS", indirDX, eAX },
+ /* f0 */
+ { "(bad)" }, /* lock prefix */
+ { "(bad)" },
+ { "(bad)" }, /* repne */
+ { "(bad)" }, /* repz */
+ { "hlt" },
+ { "cmc" },
+ { GRP3b },
+ { GRP3S },
+ /* f8 */
+ { "clc" },
+ { "stc" },
+ { "cli" },
+ { "sti" },
+ { "cld" },
+ { "std" },
+ { GRP4 },
+ { GRP5 },
+};
+
+struct dis386 dis386_twobyte[] = {
+ /* 00 */
+ { GRP6 },
+ { GRP7 },
+ { "larS", Gv, Ew },
+ { "lslS", Gv, Ew },
+ { "(bad)" },
+ { "(bad)" },
+ { "clts" },
+ { "(bad)" },
+ /* 08 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 10 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 18 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 20 */
+ /* these are all backward in appendix A of the intel book */
+ { "movl", Rd, Cd },
+ { "movl", Rd, Dd },
+ { "movl", Cd, Rd },
+ { "movl", Dd, Rd },
+ { "movl", Rd, Td },
+ { "(bad)" },
+ { "movl", Td, Rd },
+ { "(bad)" },
+ /* 28 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 30 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 38 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 40 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 48 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 50 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 58 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 60 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 68 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 70 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 78 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 80 */
+ { "jo", Jv },
+ { "jno", Jv },
+ { "jb", Jv },
+ { "jae", Jv },
+ { "je", Jv },
+ { "jne", Jv },
+ { "jbe", Jv },
+ { "ja", Jv },
+ /* 88 */
+ { "js", Jv },
+ { "jns", Jv },
+ { "jp", Jv },
+ { "jnp", Jv },
+ { "jl", Jv },
+ { "jge", Jv },
+ { "jle", Jv },
+ { "jg", Jv },
+ /* 90 */
+ { "seto", Eb },
+ { "setno", Eb },
+ { "setb", Eb },
+ { "setae", Eb },
+ { "sete", Eb },
+ { "setne", Eb },
+ { "setbe", Eb },
+ { "seta", Eb },
+ /* 98 */
+ { "sets", Eb },
+ { "setns", Eb },
+ { "setp", Eb },
+ { "setnp", Eb },
+ { "setl", Eb },
+ { "setge", Eb },
+ { "setle", Eb },
+ { "setg", Eb },
+ /* a0 */
+ { "pushl", fs },
+ { "popl", fs },
+ { "(bad)" },
+ { "btS", Ev, Gv },
+ { "shldS", Ev, Gv, Ib },
+ { "shldS", Ev, Gv, CL },
+ { "(bad)" },
+ { "(bad)" },
+ /* a8 */
+ { "pushl", gs },
+ { "popl", gs },
+ { "(bad)" },
+ { "btsS", Ev, Gv },
+ { "shrdS", Ev, Gv, Ib },
+ { "shrdS", Ev, Gv, CL },
+ { "(bad)" },
+ { "imulS", Gv, Ev },
+ /* b0 */
+ { "(bad)" },
+ { "(bad)" },
+ { "lssS", Gv, Mp }, /* 386 lists only Mp */
+ { "btrS", Ev, Gv },
+ { "lfsS", Gv, Mp }, /* 386 lists only Mp */
+ { "lgsS", Gv, Mp }, /* 386 lists only Mp */
+ { "movzbS", Gv, Eb },
+ { "movzwS", Gv, Ew },
+ /* b8 */
+ { "(bad)" },
+ { "(bad)" },
+ { GRP8 },
+ { "btcS", Ev, Gv },
+ { "bsfS", Gv, Ev },
+ { "bsrS", Gv, Ev },
+ { "movsbS", Gv, Eb },
+ { "movswS", Gv, Ew },
+ /* c0 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* c8 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* d0 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* d8 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* e0 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* e8 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* f0 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* f8 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+};
+
+static char obuf[100];
+static char *obufp;
+static char scratchbuf[100];
+static unsigned char *start_codep;
+static unsigned char *codep;
+static int mod;
+static int rm;
+static int reg;
+
+static char *names32[]={
+ "%eax","%ecx","%edx","%ebx", "%esp","%ebp","%esi","%edi",
+};
+static char *names16[] = {
+ "%ax","%cx","%dx","%bx","%sp","%bp","%si","%di",
+};
+static char *names8[] = {
+ "%al","%cl","%dl","%bl","%ah","%ch","%dh","%bh",
+};
+static char *names_seg[] = {
+ "%es","%cs","%ss","%ds","%fs","%gs","%?","%?",
+};
+
+struct dis386 grps[][8] = {
+ /* GRP1b */
+ {
+ { "addb", Eb, Ib },
+ { "orb", Eb, Ib },
+ { "adcb", Eb, Ib },
+ { "sbbb", Eb, Ib },
+ { "andb", Eb, Ib },
+ { "subb", Eb, Ib },
+ { "xorb", Eb, Ib },
+ { "cmpb", Eb, Ib }
+ },
+ /* GRP1S */
+ {
+ { "addS", Ev, Iv },
+ { "orS", Ev, Iv },
+ { "adcS", Ev, Iv },
+ { "sbbS", Ev, Iv },
+ { "andS", Ev, Iv },
+ { "subS", Ev, Iv },
+ { "xorS", Ev, Iv },
+ { "cmpS", Ev, Iv }
+ },
+ /* GRP1Ss */
+ {
+ { "addS", Ev, sIb },
+ { "orS", Ev, sIb },
+ { "adcS", Ev, sIb },
+ { "sbbS", Ev, sIb },
+ { "andS", Ev, sIb },
+ { "subS", Ev, sIb },
+ { "xorS", Ev, sIb },
+ { "cmpS", Ev, sIb }
+ },
+ /* GRP2b */
+ {
+ { "rolb", Eb, Ib },
+ { "rorb", Eb, Ib },
+ { "rclb", Eb, Ib },
+ { "rcrb", Eb, Ib },
+ { "shlb", Eb, Ib },
+ { "shrb", Eb, Ib },
+ { "(bad)" },
+ { "sarb", Eb, Ib },
+ },
+ /* GRP2S */
+ {
+ { "rolS", Ev, Ib },
+ { "rorS", Ev, Ib },
+ { "rclS", Ev, Ib },
+ { "rcrS", Ev, Ib },
+ { "shlS", Ev, Ib },
+ { "shrS", Ev, Ib },
+ { "(bad)" },
+ { "sarS", Ev, Ib },
+ },
+ /* GRP2b_one */
+ {
+ { "rolb", Eb },
+ { "rorb", Eb },
+ { "rclb", Eb },
+ { "rcrb", Eb },
+ { "shlb", Eb },
+ { "shrb", Eb },
+ { "(bad)" },
+ { "sarb", Eb },
+ },
+ /* GRP2S_one */
+ {
+ { "rolS", Ev },
+ { "rorS", Ev },
+ { "rclS", Ev },
+ { "rcrS", Ev },
+ { "shlS", Ev },
+ { "shrS", Ev },
+ { "(bad)" },
+ { "sarS", Ev },
+ },
+ /* GRP2b_cl */
+ {
+ { "rolb", Eb, CL },
+ { "rorb", Eb, CL },
+ { "rclb", Eb, CL },
+ { "rcrb", Eb, CL },
+ { "shlb", Eb, CL },
+ { "shrb", Eb, CL },
+ { "(bad)" },
+ { "sarb", Eb, CL },
+ },
+ /* GRP2S_cl */
+ {
+ { "rolS", Ev, CL },
+ { "rorS", Ev, CL },
+ { "rclS", Ev, CL },
+ { "rcrS", Ev, CL },
+ { "shlS", Ev, CL },
+ { "shrS", Ev, CL },
+ { "(bad)" },
+ { "sarS", Ev, CL }
+ },
+ /* GRP3b */
+ {
+ { "testb", Eb, Ib },
+ { "(bad)", Eb },
+ { "notb", Eb },
+ { "negb", Eb },
+ { "mulb", AL, Eb },
+ { "imulb", AL, Eb },
+ { "divb", AL, Eb },
+ { "idivb", AL, Eb }
+ },
+ /* GRP3S */
+ {
+ { "testS", Ev, Iv },
+ { "(bad)" },
+ { "notS", Ev },
+ { "negS", Ev },
+ { "mulS", eAX, Ev },
+ { "imulS", eAX, Ev },
+ { "divS", eAX, Ev },
+ { "idivS", eAX, Ev },
+ },
+ /* GRP4 */
+ {
+ { "incb", Eb },
+ { "decb", Eb },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ },
+ /* GRP5 */
+ {
+ { "incS", Ev },
+ { "decS", Ev },
+ { "call", indirEv },
+ { "lcall", indirEv },
+ { "jmp", indirEv },
+ { "ljmp", indirEv },
+ { "pushS", Ev },
+ { "(bad)" },
+ },
+ /* GRP6 */
+ {
+ { "sldt", Ew },
+ { "str", Ew },
+ { "lldt", Ew },
+ { "ltr", Ew },
+ { "verr", Ew },
+ { "verw", Ew },
+ { "(bad)" },
+ { "(bad)" }
+ },
+ /* GRP7 */
+ {
+ { "sgdt", Ew },
+ { "sidt", Ew },
+ { "lgdt", Ew },
+ { "lidt", Ew },
+ { "smsw", Ew },
+ { "(bad)" },
+ { "lmsw", Ew },
+ { "(bad)" },
+ },
+ /* GRP8 */
+ {
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { "btS", Ev, Ib },
+ { "btsS", Ev, Ib },
+ { "btrS", Ev, Ib },
+ { "btcS", Ev, Ib },
+ }
+};
+
+#define PREFIX_REPZ 1
+#define PREFIX_REPNZ 2
+#define PREFIX_LOCK 4
+#define PREFIX_CS 8
+#define PREFIX_SS 0x10
+#define PREFIX_DS 0x20
+#define PREFIX_ES 0x40
+#define PREFIX_FS 0x80
+#define PREFIX_GS 0x100
+#define PREFIX_DATA 0x200
+#define PREFIX_ADR 0x400
+#define PREFIX_FWAIT 0x800
+
+static int prefixes;
+
+ckprefix ()
+{
+ prefixes = 0;
+ while (1)
+ {
+ switch (*codep)
+ {
+ case 0xf3:
+ prefixes |= PREFIX_REPZ;
+ break;
+ case 0xf2:
+ prefixes |= PREFIX_REPNZ;
+ break;
+ case 0xf0:
+ prefixes |= PREFIX_LOCK;
+ break;
+ case 0x2e:
+ prefixes |= PREFIX_CS;
+ break;
+ case 0x36:
+ prefixes |= PREFIX_SS;
+ break;
+ case 0x3e:
+ prefixes |= PREFIX_DS;
+ break;
+ case 0x26:
+ prefixes |= PREFIX_ES;
+ break;
+ case 0x64:
+ prefixes |= PREFIX_FS;
+ break;
+ case 0x65:
+ prefixes |= PREFIX_GS;
+ break;
+ case 0x66:
+ prefixes |= PREFIX_DATA;
+ break;
+ case 0x67:
+ prefixes |= PREFIX_ADR;
+ break;
+ case 0x9b:
+ prefixes |= PREFIX_FWAIT;
+ break;
+ default:
+ return;
+ }
+ codep++;
+ }
+}
+
+static int dflag;
+static int aflag;
+
+static char op1out[100], op2out[100], op3out[100];
+static int start_pc;
+
+/*
+ * disassemble the first instruction in 'inbuf'. You have to make
+ * sure all of the bytes of the instruction are filled in.
+ * On the 386's of 1988, the maximum length of an instruction is 15 bytes.
+ * (see topic "Redundant prefixes" in the "Differences from 8086"
+ * section of the "Virtual 8086 Mode" chapter.)
+ * 'pc' should be the address of this instruction, it will
+ * be used to print the target address if this is a relative jump or call
+ * 'outbuf' gets filled in with the disassembled instruction. it should
+ * be long enough to hold the longest disassembled instruction.
+ * 100 bytes is certainly enough, unless symbol printing is added later
+ * The function returns the length of this instruction in bytes.
+ */
+i386dis (pc, inbuf, outbuf)
+ int pc;
+ unsigned char *inbuf;
+ char *outbuf;
+{
+ struct dis386 *dp;
+ char *p;
+ int i;
+ int enter_instruction;
+ char *first, *second, *third;
+ int needcomma;
+
+ obuf[0] = 0;
+ op1out[0] = 0;
+ op2out[0] = 0;
+ op3out[0] = 0;
+
+ start_pc = pc;
+ start_codep = inbuf;
+ codep = inbuf;
+
+ ckprefix ();
+
+ if (*codep == 0xc8)
+ enter_instruction = 1;
+ else
+ enter_instruction = 0;
+
+ obufp = obuf;
+
+ if (prefixes & PREFIX_REPZ)
+ oappend ("repz ");
+ if (prefixes & PREFIX_REPNZ)
+ oappend ("repnz ");
+ if (prefixes & PREFIX_LOCK)
+ oappend ("lock ");
+
+ if ((prefixes & PREFIX_FWAIT)
+ && ((*codep < 0xd8) || (*codep > 0xdf)))
+ {
+ /* fwait not followed by floating point instruction */
+ oappend ("fwait");
+ strcpy (outbuf, obuf);
+ return (1);
+ }
+
+ /* these would be initialized to 0 if disassembling for 8086 or 286 */
+ dflag = 1;
+ aflag = 1;
+
+ if (prefixes & PREFIX_DATA)
+ dflag ^= 1;
+
+ if (prefixes & PREFIX_ADR)
+ {
+ aflag ^= 1;
+ oappend ("addr16 ");
+ }
+
+ if (*codep == 0x0f)
+ dp = &dis386_twobyte[*++codep];
+ else
+ dp = &dis386[*codep];
+ codep++;
+ mod = (*codep >> 6) & 3;
+ reg = (*codep >> 3) & 7;
+ rm = *codep & 7;
+
+ if (dp->name == NULL && dp->bytemode1 == FLOATCODE)
+ {
+ dofloat ();
+ }
+ else
+ {
+ if (dp->name == NULL)
+ dp = &grps[dp->bytemode1][reg];
+
+ putop (dp->name);
+
+ obufp = op1out;
+ if (dp->op1)
+ (*dp->op1)(dp->bytemode1);
+
+ obufp = op2out;
+ if (dp->op2)
+ (*dp->op2)(dp->bytemode2);
+
+ obufp = op3out;
+ if (dp->op3)
+ (*dp->op3)(dp->bytemode3);
+ }
+
+ obufp = obuf + strlen (obuf);
+ for (i = strlen (obuf); i < 6; i++)
+ oappend (" ");
+ oappend (" ");
+
+ /* enter instruction is printed with operands in the
+ * same order as the intel book; everything else
+ * is printed in reverse order
+ */
+ if (enter_instruction)
+ {
+ first = op1out;
+ second = op2out;
+ third = op3out;
+ }
+ else
+ {
+ first = op3out;
+ second = op2out;
+ third = op1out;
+ }
+ needcomma = 0;
+ if (*first)
+ {
+ oappend (first);
+ needcomma = 1;
+ }
+ if (*second)
+ {
+ if (needcomma)
+ oappend (",");
+ oappend (second);
+ needcomma = 1;
+ }
+ if (*third)
+ {
+ if (needcomma)
+ oappend (",");
+ oappend (third);
+ }
+ strcpy (outbuf, obuf);
+ return (codep - inbuf);
+}
+
+char *float_mem[] = {
+ /* d8 */
+ "fadds",
+ "fmuls",
+ "fcoms",
+ "fcomps",
+ "fsubs",
+ "fsubrs",
+ "fdivs",
+ "fdivrs",
+ /* d9 */
+ "flds",
+ "(bad)",
+ "fsts",
+ "fstps",
+ "fldenv",
+ "fldcw",
+ "fNstenv",
+ "fNstcw",
+ /* da */
+ "fiaddl",
+ "fimull",
+ "ficoml",
+ "ficompl",
+ "fisubl",
+ "fisubrl",
+ "fidivl",
+ "fidivrl",
+ /* db */
+ "fildl",
+ "(bad)",
+ "fistl",
+ "fistpl",
+ "(bad)",
+ "fldt",
+ "(bad)",
+ "fstpt",
+ /* dc */
+ "faddl",
+ "fmull",
+ "fcoml",
+ "fcompl",
+ "fsubl",
+ "fsubrl",
+ "fdivl",
+ "fdivrl",
+ /* dd */
+ "fldl",
+ "(bad)",
+ "fstl",
+ "fstpl",
+ "frstor",
+ "(bad)",
+ "fNsave",
+ "fNstsw",
+ /* de */
+ "fiadd",
+ "fimul",
+ "ficom",
+ "ficomp",
+ "fisub",
+ "fisubr",
+ "fidiv",
+ "fidivr",
+ /* df */
+ "fild",
+ "(bad)",
+ "fist",
+ "fistp",
+ "fbld",
+ "fildll",
+ "fbstp",
+ "fistpll",
+};
+
+#define ST OP_ST, 0
+#define STi OP_STi, 0
+int OP_ST(), OP_STi();
+
+#define FGRPd9_2 NULL, NULL, 0
+#define FGRPd9_4 NULL, NULL, 1
+#define FGRPd9_5 NULL, NULL, 2
+#define FGRPd9_6 NULL, NULL, 3
+#define FGRPd9_7 NULL, NULL, 4
+#define FGRPda_5 NULL, NULL, 5
+#define FGRPdb_4 NULL, NULL, 6
+#define FGRPde_3 NULL, NULL, 7
+#define FGRPdf_4 NULL, NULL, 8
+
+struct dis386 float_reg[][8] = {
+ /* d8 */
+ {
+ { "fadd", ST, STi },
+ { "fmul", ST, STi },
+ { "fcom", STi },
+ { "fcomp", STi },
+ { "fsub", ST, STi },
+ { "fsubr", ST, STi },
+ { "fdiv", ST, STi },
+ { "fdivr", ST, STi },
+ },
+ /* d9 */
+ {
+ { "fld", STi },
+ { "fxch", STi },
+ { FGRPd9_2 },
+ { "(bad)" },
+ { FGRPd9_4 },
+ { FGRPd9_5 },
+ { FGRPd9_6 },
+ { FGRPd9_7 },
+ },
+ /* da */
+ {
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { FGRPda_5 },
+ { "(bad)" },
+ { "(bad)" },
+ },
+ /* db */
+ {
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { FGRPdb_4 },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ },
+ /* dc */
+ {
+ { "fadd", STi, ST },
+ { "fmul", STi, ST },
+ { "(bad)" },
+ { "(bad)" },
+ { "fsub", STi, ST },
+ { "fsubr", STi, ST },
+ { "fdiv", STi, ST },
+ { "fdivr", STi, ST },
+ },
+ /* dd */
+ {
+ { "ffree", STi },
+ { "(bad)" },
+ { "fst", STi },
+ { "fstp", STi },
+ { "fucom", STi },
+ { "fucomp", STi },
+ { "(bad)" },
+ { "(bad)" },
+ },
+ /* de */
+ {
+ { "faddp", STi, ST },
+ { "fmulp", STi, ST },
+ { "(bad)" },
+ { FGRPde_3 },
+ { "fsubp", STi, ST },
+ { "fsubrp", STi, ST },
+ { "fdivp", STi, ST },
+ { "fdivrp", STi, ST },
+ },
+ /* df */
+ {
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { FGRPdf_4 },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ },
+};
+
+
+char *fgrps[][8] = {
+ /* d9_2 0 */
+ {
+ "fnop","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+ },
+
+ /* d9_4 1 */
+ {
+ "fchs","fabs","(bad)","(bad)","ftst","fxam","(bad)","(bad)",
+ },
+
+ /* d9_5 2 */
+ {
+ "fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz","(bad)",
+ },
+
+ /* d9_6 3 */
+ {
+ "f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp",
+ },
+
+ /* d9_7 4 */
+ {
+ "fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos",
+ },
+
+ /* da_5 5 */
+ {
+ "(bad)","fucompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+ },
+
+ /* db_4 6 */
+ {
+ "feni(287 only)","fdisi(287 only)","fNclex","fNinit",
+ "fNsetpm(287 only)","(bad)","(bad)","(bad)",
+ },
+
+ /* de_3 7 */
+ {
+ "(bad)","fcompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+ },
+
+ /* df_4 8 */
+ {
+ "fNstsw","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+ },
+};
+
+
+dofloat ()
+{
+ struct dis386 *dp;
+ unsigned char floatop;
+
+ floatop = codep[-1];
+
+ if (mod != 3)
+ {
+ putop (float_mem[(floatop - 0xd8) * 8 + reg]);
+ obufp = op1out;
+ OP_E (v_mode);
+ return;
+ }
+ codep++;
+
+ dp = &float_reg[floatop - 0xd8][reg];
+ if (dp->name == NULL)
+ {
+ putop (fgrps[dp->bytemode1][rm]);
+ /* instruction fnstsw is only one with strange arg */
+ if (floatop == 0xdf && *codep == 0xe0)
+ strcpy (op1out, "%eax");
+ }
+ else
+ {
+ putop (dp->name);
+ obufp = op1out;
+ if (dp->op1)
+ (*dp->op1)(dp->bytemode1);
+ obufp = op2out;
+ if (dp->op2)
+ (*dp->op2)(dp->bytemode2);
+ }
+}
+
+/* ARGSUSED */
+OP_ST (ignore)
+{
+ oappend ("%st");
+}
+
+/* ARGSUSED */
+OP_STi (ignore)
+{
+ sprintf (scratchbuf, "%%st(%d)", rm);
+ oappend (scratchbuf);
+}
+
+
+/* capital letters in template are macros */
+putop (template)
+ char *template;
+{
+ char *p;
+
+ for (p = template; *p; p++)
+ {
+ switch (*p)
+ {
+ default:
+ *obufp++ = *p;
+ break;
+ case 'C': /* For jcxz/jecxz */
+ if (aflag == 0)
+ *obufp++ = 'e';
+ break;
+ case 'N':
+ if ((prefixes & PREFIX_FWAIT) == 0)
+ *obufp++ = 'n';
+ break;
+ case 'S':
+ /* operand size flag */
+ if (dflag)
+ *obufp++ = 'l';
+ else
+ *obufp++ = 'w';
+ break;
+ }
+ }
+ *obufp = 0;
+}
+
+oappend (s)
+char *s;
+{
+ strcpy (obufp, s);
+ obufp += strlen (s);
+ *obufp = 0;
+}
+
+append_prefix ()
+{
+ if (prefixes & PREFIX_CS)
+ oappend ("%cs:");
+ if (prefixes & PREFIX_DS)
+ oappend ("%ds:");
+ if (prefixes & PREFIX_SS)
+ oappend ("%ss:");
+ if (prefixes & PREFIX_ES)
+ oappend ("%es:");
+ if (prefixes & PREFIX_FS)
+ oappend ("%fs:");
+ if (prefixes & PREFIX_GS)
+ oappend ("%gs:");
+}
+
+OP_indirE (bytemode)
+{
+ oappend ("*");
+ OP_E (bytemode);
+}
+
+OP_E (bytemode)
+{
+ int disp;
+ int havesib;
+ int didoutput = 0;
+ int base;
+ int index;
+ int scale;
+ int havebase;
+
+ /* skip mod/rm byte */
+ codep++;
+
+ havesib = 0;
+ havebase = 0;
+ disp = 0;
+
+ if (mod == 3)
+ {
+ switch (bytemode)
+ {
+ case b_mode:
+ oappend (names8[rm]);
+ break;
+ case w_mode:
+ oappend (names16[rm]);
+ break;
+ case v_mode:
+ if (dflag)
+ oappend (names32[rm]);
+ else
+ oappend (names16[rm]);
+ break;
+ default:
+ oappend ("<bad dis table>");
+ break;
+ }
+ return;
+ }
+
+ append_prefix ();
+ if (rm == 4)
+ {
+ havesib = 1;
+ havebase = 1;
+ scale = (*codep >> 6) & 3;
+ index = (*codep >> 3) & 7;
+ base = *codep & 7;
+ codep++;
+ }
+
+ switch (mod)
+ {
+ case 0:
+ switch (rm)
+ {
+ case 4:
+ /* implies havesib and havebase */
+ if (base == 5) {
+ havebase = 0;
+ disp = get32 ();
+ }
+ break;
+ case 5:
+ disp = get32 ();
+ break;
+ default:
+ havebase = 1;
+ base = rm;
+ break;
+ }
+ break;
+ case 1:
+ disp = *(char *)codep++;
+ if (rm != 4)
+ {
+ havebase = 1;
+ base = rm;
+ }
+ break;
+ case 2:
+ disp = get32 ();
+ if (rm != 4)
+ {
+ havebase = 1;
+ base = rm;
+ }
+ break;
+ }
+
+ if (mod != 0 || rm == 5 || (havesib && base == 5))
+ {
+ sprintf (scratchbuf, "%d", disp);
+ oappend (scratchbuf);
+ }
+
+ if (havebase || havesib)
+ {
+ oappend ("(");
+ if (havebase)
+ oappend (names32[base]);
+ if (havesib)
+ {
+ if (index != 4)
+ {
+ sprintf (scratchbuf, ",%s", names32[index]);
+ oappend (scratchbuf);
+ }
+ sprintf (scratchbuf, ",%d", 1 << scale);
+ oappend (scratchbuf);
+ }
+ oappend (")");
+ }
+}
+
+OP_G (bytemode)
+{
+ switch (bytemode)
+ {
+ case b_mode:
+ oappend (names8[reg]);
+ break;
+ case w_mode:
+ oappend (names16[reg]);
+ break;
+ case d_mode:
+ oappend (names32[reg]);
+ break;
+ case v_mode:
+ if (dflag)
+ oappend (names32[reg]);
+ else
+ oappend (names16[reg]);
+ break;
+ default:
+ oappend ("<internal disassembler error>");
+ break;
+ }
+}
+
+get32 ()
+{
+ int x = 0;
+
+ x = *codep++ & 0xff;
+ x |= (*codep++ & 0xff) << 8;
+ x |= (*codep++ & 0xff) << 16;
+ x |= (*codep++ & 0xff) << 24;
+ return (x);
+}
+
+get16 ()
+{
+ int x = 0;
+
+ x = *codep++ & 0xff;
+ x |= (*codep++ & 0xff) << 8;
+ return (x);
+}
+
+OP_REG (code)
+{
+ char *s;
+
+ switch (code)
+ {
+ case indir_dx_reg: s = "(%dx)"; break;
+ case ax_reg: case cx_reg: case dx_reg: case bx_reg:
+ case sp_reg: case bp_reg: case si_reg: case di_reg:
+ s = names16[code - ax_reg];
+ break;
+ case es_reg: case ss_reg: case cs_reg:
+ case ds_reg: case fs_reg: case gs_reg:
+ s = names_seg[code - es_reg];
+ break;
+ case al_reg: case ah_reg: case cl_reg: case ch_reg:
+ case dl_reg: case dh_reg: case bl_reg: case bh_reg:
+ s = names8[code - al_reg];
+ break;
+ case eAX_reg: case eCX_reg: case eDX_reg: case eBX_reg:
+ case eSP_reg: case eBP_reg: case eSI_reg: case eDI_reg:
+ if (dflag)
+ s = names32[code - eAX_reg];
+ else
+ s = names16[code - eAX_reg];
+ break;
+ default:
+ s = "<internal disassembler error>";
+ break;
+ }
+ oappend (s);
+}
+
+OP_I (bytemode)
+{
+ int op;
+
+ switch (bytemode)
+ {
+ case b_mode:
+ op = *codep++ & 0xff;
+ break;
+ case v_mode:
+ if (dflag)
+ op = get32 ();
+ else
+ op = get16 ();
+ break;
+ case w_mode:
+ op = get16 ();
+ break;
+ default:
+ oappend ("<internal disassembler error>");
+ return;
+ }
+ sprintf (scratchbuf, "$0x%x", op);
+ oappend (scratchbuf);
+}
+
+OP_sI (bytemode)
+{
+ int op;
+
+ switch (bytemode)
+ {
+ case b_mode:
+ op = *(char *)codep++;
+ break;
+ case v_mode:
+ if (dflag)
+ op = get32 ();
+ else
+ op = (short)get16();
+ break;
+ case w_mode:
+ op = (short)get16 ();
+ break;
+ default:
+ oappend ("<internal disassembler error>");
+ return;
+ }
+ sprintf (scratchbuf, "$0x%x", op);
+ oappend (scratchbuf);
+}
+
+OP_J (bytemode)
+{
+ int disp;
+ int mask = -1;
+
+ switch (bytemode)
+ {
+ case b_mode:
+ disp = *(char *)codep++;
+ break;
+ case v_mode:
+ if (dflag)
+ disp = get32 ();
+ else
+ {
+ disp = (short)get16 ();
+ /* for some reason, a data16 prefix on a jump instruction
+ means that the pc is masked to 16 bits after the
+ displacement is added! */
+ mask = 0xffff;
+ }
+ break;
+ default:
+ oappend ("<internal disassembelr error>");
+ return;
+ }
+
+ sprintf (scratchbuf, "0x%x",
+ (start_pc + codep - start_codep + disp) & mask);
+ oappend (scratchbuf);
+}
+
+/* ARGSUSED */
+OP_SEG (dummy)
+{
+ static char *sreg[] = {
+ "%es","%cs","%ss","%ds","%fs","%gs","%?","%?",
+ };
+
+ oappend (sreg[reg]);
+}
+
+OP_DIR (size)
+{
+ int seg, offset;
+
+ switch (size)
+ {
+ case lptr:
+ if (aflag)
+ {
+ offset = get32 ();
+ seg = get16 ();
+ }
+ else
+ {
+ offset = get16 ();
+ seg = get16 ();
+ }
+ sprintf (scratchbuf, "0x%x,0x%x", seg, offset);
+ oappend (scratchbuf);
+ break;
+ case v_mode:
+ if (aflag)
+ offset = get32 ();
+ else
+ offset = (short)get16 ();
+
+ sprintf (scratchbuf, "0x%x",
+ start_pc + codep - start_codep + offset);
+ oappend (scratchbuf);
+ break;
+ default:
+ oappend ("<internal disassembler error>");
+ break;
+ }
+}
+
+/* ARGSUSED */
+OP_OFF (bytemode)
+{
+ int off;
+
+ if (aflag)
+ off = get32 ();
+ else
+ off = get16 ();
+
+ sprintf (scratchbuf, "0x%x", off);
+ oappend (scratchbuf);
+}
+
+/* ARGSUSED */
+OP_ESDI (dummy)
+{
+ oappend ("%es:(");
+ oappend (aflag ? "%edi" : "%di");
+ oappend (")");
+}
+
+/* ARGSUSED */
+OP_DSSI (dummy)
+{
+ oappend ("%ds:(");
+ oappend (aflag ? "%esi" : "%si");
+ oappend (")");
+}
+
+/* ARGSUSED */
+OP_ONE (dummy)
+{
+ oappend ("1");
+}
+
+/* ARGSUSED */
+OP_C (dummy)
+{
+ codep++; /* skip mod/rm */
+ sprintf (scratchbuf, "%%cr%d", reg);
+ oappend (scratchbuf);
+}
+
+/* ARGSUSED */
+OP_D (dummy)
+{
+ codep++; /* skip mod/rm */
+ sprintf (scratchbuf, "%%db%d", reg);
+ oappend (scratchbuf);
+}
+
+/* ARGSUSED */
+OP_T (dummy)
+{
+ codep++; /* skip mod/rm */
+ sprintf (scratchbuf, "%%tr%d", reg);
+ oappend (scratchbuf);
+}
+
+OP_rm (bytemode)
+{
+ switch (bytemode)
+ {
+ case d_mode:
+ oappend (names32[rm]);
+ break;
+ case w_mode:
+ oappend (names16[rm]);
+ break;
+ }
+}
+
+/* GDB interface */
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "frame.h"
+#include "inferior.h"
+
+#define MAXLEN 20
+print_insn (memaddr, stream)
+ CORE_ADDR memaddr;
+ FILE *stream;
+{
+ unsigned char buffer[MAXLEN];
+ /* should be expanded if disassembler prints symbol names */
+ char outbuf[100];
+ int n;
+
+ read_memory (memaddr, buffer, MAXLEN);
+
+ n = i386dis ((int)memaddr, buffer, outbuf);
+
+ fputs (outbuf, stream);
+
+ return (n);
+}
+
diff --git a/gnu/usr.bin/gdb/config/i386bsd-dep.c b/gnu/usr.bin/gdb/config/i386bsd-dep.c
new file mode 100644
index 0000000..16286ee
--- /dev/null
+++ b/gnu/usr.bin/gdb/config/i386bsd-dep.c
@@ -0,0 +1,1889 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)i386bsd-dep.c 6.10 (Berkeley) 6/26/91";
+#endif /* not lint */
+
+/* Low level interface to ptrace, for GDB when running on the Intel 386.
+ Copyright (C) 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "frame.h"
+#include "inferior.h"
+#include "value.h"
+
+#include <sys/param.h>
+#include <sys/dir.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#include <a.out.h>
+
+#ifndef N_SET_MAGIC
+#define N_SET_MAGIC(exec, val) ((exec).a_magic = (val))
+#endif
+
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/uio.h>
+#define curpcb Xcurpcb /* XXX avoid leaking declaration from pcb.h */
+#include <sys/user.h>
+#undef curpcb
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/ptrace.h>
+
+#include <machine/reg.h>
+
+#ifdef KERNELDEBUG
+#ifndef NEWVM
+#include <sys/vmmac.h>
+#include <machine/pte.h>
+#else
+#include <sys/proc.h> /* for curproc */
+#endif
+#include <machine/vmparam.h>
+#include <machine/cpu.h>
+#include <ctype.h>
+#include "symtab.h" /* XXX */
+
+#undef vtophys /* XXX */
+
+extern int kernel_debugging;
+
+#define KERNOFF ((unsigned)KERNBASE)
+#ifndef NEWVM
+#define INKERNEL(x) ((x) >= KERNOFF && (x) < KERNOFF + ctob(slr))
+#define INUPAGE(x) \
+ ((x) >= KERNEL_U_ADDR && (x) < KERNEL_U_ADDR + NBPG)
+#else
+#define INKERNEL(x) ((x) >= KERNOFF)
+#endif
+
+#define PT_ADDR_ANY ((caddr_t) 1)
+
+/*
+ * Convert from sysmap pte index to system virtual address & vice-versa.
+ * (why aren't these in one of the system vm macro files???)
+ */
+#define smxtob(a) (sbr + (a) * sizeof(pte))
+#define btosmx(b) (((b) - sbr) / sizeof(pte))
+
+static int ok_to_cache();
+static int found_pcb;
+#ifdef NEWVM
+static CORE_ADDR curpcb;
+static CORE_ADDR kstack;
+#endif
+
+static void setregmap();
+
+extern int errno;
+
+/*
+ * This function simply calls ptrace with the given arguments. It exists so
+ * that all calls to ptrace are isolated in this machine-dependent file.
+ */
+int
+call_ptrace(request, pid, arg3, arg4)
+ int request;
+ pid_t pid;
+ caddr_t arg3;
+ int arg4;
+{
+ return(ptrace(request, pid, arg3, arg4));
+}
+
+kill_inferior()
+{
+ if (remote_debugging) {
+#ifdef KERNELDEBUG
+ if (kernel_debugging)
+ /*
+ * It's a very, very bad idea to go away leaving
+ * breakpoints in a remote kernel or to leave it
+ * stopped at a breakpoint.
+ */
+ clear_breakpoints();
+#endif
+ remote_close(0);
+ inferior_died();
+ } else if (inferior_pid != 0) {
+ ptrace(PT_KILL, inferior_pid, 0, 0);
+ wait(0);
+ inferior_died();
+ }
+}
+
+/*
+ * This is used when GDB is exiting. It gives less chance of error.
+ */
+kill_inferior_fast()
+{
+ if (remote_debugging) {
+#ifdef KERNELDEBUG
+ if (kernel_debugging)
+ clear_breakpoints();
+#endif
+ remote_close(0);
+ return;
+ }
+ if (inferior_pid == 0)
+ return;
+
+ ptrace(PT_KILL, inferior_pid, 0, 0);
+ wait(0);
+}
+
+/*
+ * Resume execution of the inferior process. If STEP is nonzero, single-step
+ * it. If SIGNAL is nonzero, give it that signal.
+ */
+void
+resume(step, signal)
+ int step;
+ int signal;
+{
+ errno = 0;
+ if (remote_debugging)
+ remote_resume(step, signal);
+ else {
+ ptrace(step ? PT_STEP : PT_CONTINUE, inferior_pid,
+ PT_ADDR_ANY, signal);
+ if (errno)
+ perror_with_name("ptrace");
+ }
+}
+
+#ifdef ATTACH_DETACH
+extern int attach_flag;
+
+/*
+ * Start debugging the process whose number is PID.
+ */
+attach(pid)
+ int pid;
+{
+ errno = 0;
+ ptrace(PT_ATTACH, pid, 0, 0);
+ if (errno)
+ perror_with_name("ptrace");
+ attach_flag = 1;
+ return pid;
+}
+
+/*
+ * Stop debugging the process whose number is PID and continue it
+ * with signal number SIGNAL. SIGNAL = 0 means just continue it.
+ */
+void
+detach(signal)
+ int signal;
+{
+ errno = 0;
+ ptrace(PT_DETACH, inferior_pid, PT_ADDR_ANY, signal);
+ if (errno)
+ perror_with_name("ptrace");
+ attach_flag = 0;
+}
+#endif /* ATTACH_DETACH */
+
+static unsigned int
+get_register_offset()
+{
+ unsigned int offset;
+ struct user u; /* XXX */
+ unsigned int flags = (char *) &u.u_pcb.pcb_flags - (char *) &u;
+
+ setregmap(ptrace(PT_READ_U, inferior_pid, (caddr_t)flags, 0));
+
+#ifdef NEWVM
+ offset = (char *) &u.u_kproc.kp_proc.p_regs - (char *) &u;
+ offset = ptrace(PT_READ_U, inferior_pid, (caddr_t)offset, 0) -
+ USRSTACK;
+#else
+ offset = (char *) &u.u_ar0 - (char *) &u;
+ offset = ptrace(PT_READ_U, inferior_pid, (caddr_t)offset, 0) -
+ KERNEL_U_ADDR;
+#endif
+
+ return offset;
+}
+
+void
+fetch_inferior_registers()
+{
+ register int regno;
+ register unsigned int regaddr;
+ char buf[MAX_REGISTER_RAW_SIZE];
+ register int i;
+ unsigned int offset;
+
+ if (remote_debugging) {
+ extern char registers[];
+
+ remote_fetch_registers(registers);
+ return;
+ }
+
+ offset = get_register_offset();
+
+ for (regno = 0; regno < NUM_REGS; regno++) {
+ regaddr = register_addr(regno, offset);
+ for (i = 0; i < REGISTER_RAW_SIZE(regno); i += sizeof(int)) {
+ *(int *)&buf[i] = ptrace(PT_READ_U, inferior_pid,
+ (caddr_t)regaddr, 0);
+ regaddr += sizeof(int);
+ }
+ supply_register(regno, buf);
+ }
+}
+
+/*
+ * Store our register values back into the inferior. If REGNO is -1, do this
+ * for all registers. Otherwise, REGNO specifies which register (so we can
+ * save time).
+ */
+store_inferior_registers(regno)
+ int regno;
+{
+ register unsigned int regaddr;
+ char buf[80];
+ extern char registers[];
+ register int i;
+ unsigned int offset;
+
+ if (remote_debugging) {
+ extern char registers[];
+
+ remote_store_registers(registers);
+ return;
+ }
+
+ offset = get_register_offset();
+
+ if (regno >= 0) {
+ regaddr = register_addr(regno, offset);
+ for (i = 0; i < REGISTER_RAW_SIZE(regno); i += sizeof(int)) {
+ errno = 0;
+ ptrace(PT_WRITE_U, inferior_pid, (caddr_t)regaddr,
+ *(int *) &registers[REGISTER_BYTE(regno) + i]);
+ if (errno != 0) {
+ sprintf(buf, "writing register number %d(%d)",
+ regno, i);
+ perror_with_name(buf);
+ }
+ regaddr += sizeof(int);
+ }
+ } else
+ for (regno = 0; regno < NUM_REGS; regno++) {
+ regaddr = register_addr(regno, offset);
+ for (i = 0; i < REGISTER_RAW_SIZE(regno);
+ i += sizeof(int)) {
+ errno = 0;
+ ptrace(PT_WRITE_U, inferior_pid,
+ (caddr_t)regaddr,
+ *(int *) &registers[REGISTER_BYTE(regno) + i]);
+ if (errno != 0) {
+ sprintf(buf,
+ "writing register number %d(%d)",
+ regno, i);
+ perror_with_name(buf);
+ }
+ regaddr += sizeof(int);
+ }
+ }
+}
+
+/*
+ * Copy LEN bytes from inferior's memory starting at MEMADDR to debugger
+ * memory starting at MYADDR. On failure (cannot read from inferior, usually
+ * because address is out of bounds) returns the value of errno.
+ */
+int
+read_inferior_memory(memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+{
+ register int i;
+ /* Round starting address down to longword boundary. */
+ register CORE_ADDR addr = memaddr & -sizeof(int);
+ /* Round ending address up; get number of longwords that makes. */
+ register int count = (((memaddr + len) - addr) + sizeof(int) - 1) /
+ sizeof(int);
+ /* Allocate buffer of that many longwords. */
+ register int *buffer = (int *) alloca(count * sizeof(int));
+ extern int errno;
+
+ if (remote_debugging)
+ return (remote_read_inferior_memory(memaddr, myaddr, len));
+
+ /* Read all the longwords */
+ errno = 0;
+ for (i = 0; i < count && errno == 0; i++, addr += sizeof(int))
+ buffer[i] = ptrace(PT_READ_I, inferior_pid, (caddr_t)addr, 0);
+
+ /* Copy appropriate bytes out of the buffer. */
+ bcopy((char *) buffer + (memaddr & (sizeof(int) - 1)), myaddr, len);
+ return(errno);
+}
+
+/*
+ * Copy LEN bytes of data from debugger memory at MYADDR to inferior's memory
+ * at MEMADDR. On failure (cannot write the inferior) returns the value of
+ * errno.
+ */
+
+int
+write_inferior_memory(memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+{
+ register int i;
+ /* Round starting address down to longword boundary. */
+ register CORE_ADDR addr = memaddr & -sizeof(int);
+ /* Round ending address up; get number of longwords that makes. */
+ register int count = (((memaddr + len) - addr) + sizeof(int) - 1) /
+ sizeof(int);
+ /* Allocate buffer of that many longwords. */
+ register int *buffer = (int *) alloca(count * sizeof(int));
+ extern int errno;
+
+ /*
+ * Fill start and end extra bytes of buffer with existing memory
+ * data.
+ */
+ if (remote_debugging)
+ return (remote_write_inferior_memory(memaddr, myaddr, len));
+
+ /*
+ * Fill start and end extra bytes of buffer with existing memory
+ * data.
+ */
+ buffer[0] = ptrace(PT_READ_I, inferior_pid, (caddr_t)addr, 0);
+
+ if (count > 1)
+ buffer[count - 1] = ptrace(PT_READ_I, inferior_pid,
+ (caddr_t)addr + (count - 1) * sizeof(int), 0);
+
+ /* Copy data to be written over corresponding part of buffer */
+
+ bcopy(myaddr, (char *) buffer + (memaddr & (sizeof(int) - 1)), len);
+
+ /* Write the entire buffer. */
+
+ errno = 0;
+ for (i = 0; i < count && errno == 0; i++, addr += sizeof(int))
+ ptrace(PT_WRITE_I, inferior_pid, (caddr_t)addr, buffer[i]);
+
+ return(errno);
+}
+
+
+/*
+ * Work with core dump and executable files, for GDB.
+ * This code would be in core.c if it weren't machine-dependent.
+ */
+
+#ifndef N_TXTADDR
+#define N_TXTADDR(hdr) 0
+#endif /* no N_TXTADDR */
+
+#ifndef N_DATADDR
+#define N_DATADDR(hdr) hdr.a_text
+#endif /* no N_DATADDR */
+
+/*
+ * Make COFF and non-COFF names for things a little more compatible to reduce
+ * conditionals later.
+ */
+
+#ifndef AOUTHDR
+#define AOUTHDR struct exec
+#endif
+
+extern char *sys_siglist[];
+
+
+/* Hook for `exec_file_command' command to call. */
+
+extern void (*exec_file_display_hook) ();
+
+/* File names of core file and executable file. */
+
+extern char *corefile;
+extern char *execfile;
+
+/* Descriptors on which core file and executable file are open.
+ Note that the execchan is closed when an inferior is created
+ and reopened if the inferior dies or is killed. */
+
+extern int corechan;
+extern int execchan;
+
+/* Last modification time of executable file.
+ Also used in source.c to compare against mtime of a source file. */
+
+extern int exec_mtime;
+
+/* Virtual addresses of bounds of the two areas of memory in the core file. */
+
+extern CORE_ADDR data_start;
+extern CORE_ADDR data_end;
+extern CORE_ADDR stack_start;
+extern CORE_ADDR stack_end;
+
+/* Virtual addresses of bounds of two areas of memory in the exec file.
+ Note that the data area in the exec file is used only when there is no core file. */
+
+extern CORE_ADDR text_start;
+extern CORE_ADDR text_end;
+
+extern CORE_ADDR exec_data_start;
+extern CORE_ADDR exec_data_end;
+
+/* Address in executable file of start of text area data. */
+
+extern int text_offset;
+
+/* Address in executable file of start of data area data. */
+
+extern int exec_data_offset;
+
+/* Address in core file of start of data area data. */
+
+extern int data_offset;
+
+/* Address in core file of start of stack area data. */
+
+extern int stack_offset;
+
+/* a.out header saved in core file. */
+
+extern AOUTHDR core_aouthdr;
+
+/* a.out header of exec file. */
+
+extern AOUTHDR exec_aouthdr;
+
+extern void validate_files ();
+
+extern int (*core_file_hook)();
+
+#ifdef KERNELDEBUG
+/*
+ * Kernel debugging routines.
+ */
+
+#define IOTOP 0x100000 /* XXX should get this from include file */
+#define IOBASE 0xa0000 /* XXX should get this from include file */
+
+static CORE_ADDR file_offset;
+static CORE_ADDR lowram;
+static CORE_ADDR sbr;
+static CORE_ADDR slr;
+static struct pcb pcb;
+
+static CORE_ADDR
+ksym_lookup(name)
+ char *name;
+{
+ struct symbol *sym;
+ int i;
+
+ if ((i = lookup_misc_func(name)) < 0)
+ error("kernel symbol `%s' not found.", name);
+
+ return (misc_function_vector[i].address);
+}
+
+/*
+ * return true if 'len' bytes starting at 'addr' can be read out as
+ * longwords and/or locally cached (this is mostly for memory mapped
+ * i/o register access when debugging remote kernels).
+ *
+ * XXX the HP code does this differently with NEWVM
+ */
+static int
+ok_to_cache(addr, len)
+{
+ static CORE_ADDR atdevbase;
+
+ if (! atdevbase)
+ atdevbase = ksym_lookup("atdevbase");
+
+ if (addr >= atdevbase && addr < atdevbase + (IOTOP - IOBASE))
+ return (0);
+
+ return (1);
+}
+
+static
+physrd(addr, dat, len)
+ u_int addr;
+ char *dat;
+{
+ if (lseek(corechan, addr - file_offset, L_SET) == -1)
+ return (-1);
+ if (read(corechan, dat, len) != len)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * When looking at kernel data space through /dev/mem or with a core file, do
+ * virtual memory mapping.
+ */
+#ifdef NEWVM
+static CORE_ADDR
+vtophys(addr)
+ CORE_ADDR addr;
+{
+ CORE_ADDR v;
+ struct pte pte;
+ static CORE_ADDR PTD = -1;
+ CORE_ADDR current_ptd;
+
+ /*
+ * If we're looking at the kernel stack,
+ * munge the address to refer to the user space mapping instead;
+ * that way we get the requested process's kstack, not the running one.
+ */
+ if (addr >= kstack && addr < kstack + ctob(UPAGES))
+ addr = (addr - kstack) + curpcb;
+
+ /*
+ * We may no longer have a linear system page table...
+ *
+ * Here's the scoop. IdlePTD contains the physical address
+ * of a page table directory that always maps the kernel.
+ * IdlePTD is in memory that is mapped 1-to-1, so we can
+ * find it easily given its 'virtual' address from ksym_lookup().
+ * For hysterical reasons, the value of IdlePTD is stored in sbr.
+ *
+ * To look up a kernel address, we first convert it to a 1st-level
+ * address and look it up in IdlePTD. This gives us the physical
+ * address of a page table page; we extract the 2nd-level part of
+ * VA and read the 2nd-level pte. Finally, we add the offset part
+ * of the VA into the physical address from the pte and return it.
+ *
+ * User addresses are a little more complicated. If we don't have
+ * a current PCB from read_pcb(), we use PTD, which is the (fixed)
+ * virtual address of the current ptd. Since it's NOT in 1-to-1
+ * kernel space, we must look it up using IdlePTD. If we do have
+ * a pcb, we get the ptd from pcb_ptd.
+ */
+
+ if (INKERNEL(addr))
+ current_ptd = sbr;
+ else if (found_pcb == 0) {
+ if (PTD == -1)
+ PTD = vtophys(ksym_lookup("PTD"));
+ current_ptd = PTD;
+ } else
+ current_ptd = pcb.pcb_ptd;
+
+ /*
+ * Read the first-level page table (ptd).
+ */
+ v = current_ptd + ((unsigned)addr >> PD_SHIFT) * sizeof pte;
+ if (physrd(v, (char *)&pte, sizeof pte) || pte.pg_v == 0)
+ return (~0);
+
+ /*
+ * Read the second-level page table.
+ */
+ v = i386_ptob(pte.pg_pfnum) + ((addr&PT_MASK) >> PG_SHIFT) * sizeof pte;
+ if (physrd(v, (char *) &pte, sizeof(pte)) || pte.pg_v == 0)
+ return (~0);
+
+ addr = i386_ptob(pte.pg_pfnum) + (addr & PGOFSET);
+#if 0
+ printf("vtophys(%x) -> %x\n", oldaddr, addr);
+#endif
+ return (addr);
+}
+#else
+static CORE_ADDR
+vtophys(addr)
+ CORE_ADDR addr;
+{
+ CORE_ADDR v;
+ struct pte pte;
+ CORE_ADDR oldaddr = addr;
+
+ if (found_pcb == 0 && INUPAGE(addr)) {
+ static CORE_ADDR pSwtchmap;
+
+ if (pSwtchmap == 0)
+ pSwtchmap = vtophys(ksym_lookup("Swtchmap"));
+ addr = pSwtchmap;
+ } else if (INKERNEL(addr)) {
+ /*
+ * In system space get system pte. If valid or reclaimable
+ * then physical address is combination of its page number
+ * and the page offset of the original address.
+ */
+ addr = smxtob(btop(addr - KERNOFF)) - KERNOFF;
+ } else {
+ v = btop(addr);
+ if (v < pcb.pcb_p0lr)
+ addr = (CORE_ADDR) pcb.pcb_p0br +
+ v * sizeof (struct pte);
+ else if (v >= pcb.pcb_p1lr && v < P1PAGES)
+ addr = (CORE_ADDR) pcb.pcb_p0br +
+ ((pcb.pcb_szpt * NPTEPG - HIGHPAGES) -
+ (BTOPUSRSTACK - v)) * sizeof (struct pte);
+ else
+ return (~0);
+
+ /*
+ * For p0/p1 address, user-level page table should be in
+ * kernel vm. Do second-level indirect by recursing.
+ */
+ if (!INKERNEL(addr))
+ return (~0);
+
+ addr = vtophys(addr);
+ }
+ /*
+ * Addr is now address of the pte of the page we are interested in;
+ * get the pte and paste up the physical address.
+ */
+ if (physrd(addr, (char *) &pte, sizeof(pte)))
+ return (~0);
+
+ if (pte.pg_v == 0 && (pte.pg_fod || pte.pg_pfnum == 0))
+ return (~0);
+
+ addr = (CORE_ADDR)ptob(pte.pg_pfnum) + (oldaddr & PGOFSET);
+#if 0
+ printf("vtophys(%x) -> %x\n", oldaddr, addr);
+#endif
+ return (addr);
+}
+
+#endif
+
+static
+kvread(addr)
+ CORE_ADDR addr;
+{
+ CORE_ADDR paddr = vtophys(addr);
+
+ if (paddr != ~0)
+ if (physrd(paddr, (char *)&addr, sizeof(addr)) == 0);
+ return (addr);
+
+ return (~0);
+}
+
+static void
+read_pcb(uaddr)
+ u_int uaddr;
+{
+ int i;
+ int *pcb_regs = (int *)&pcb;
+
+#ifdef NEWVM
+ if (physrd(uaddr, (char *)&pcb, sizeof pcb))
+ error("cannot read pcb at %x\n", uaddr);
+ printf("current pcb at %x\n", uaddr);
+#else
+ if (physrd(uaddr, (char *)&pcb, sizeof pcb))
+ error("cannot read pcb at %x\n", uaddr);
+ printf("p0br %x p0lr %x p1br %x p1lr %x\n",
+ pcb.pcb_p0br, pcb.pcb_p0lr, pcb.pcb_p1br, pcb.pcb_p1lr);
+#endif
+
+ /*
+ * get the register values out of the sys pcb and
+ * store them where `read_register' will find them.
+ */
+ for (i = 0; i < 8; ++i)
+ supply_register(i, &pcb_regs[i+10]);
+ supply_register(8, &pcb_regs[8]); /* eip */
+ supply_register(9, &pcb_regs[9]); /* eflags */
+ for (i = 10; i < 13; ++i) /* cs, ss, ds */
+ supply_register(i, &pcb_regs[i+9]);
+ supply_register(13, &pcb_regs[18]); /* es */
+ for (i = 14; i < 16; ++i) /* fs, gs */
+ supply_register(i, &pcb_regs[i+8]);
+
+ /* XXX 80387 registers? */
+}
+
+static void
+setup_kernel_debugging()
+{
+ struct stat stb;
+ int devmem = 0;
+ CORE_ADDR addr;
+
+ fstat(corechan, &stb);
+ if ((stb.st_mode & S_IFMT) == S_IFCHR && stb.st_rdev == makedev(2, 0))
+ devmem = 1;
+
+#ifdef NEWVM
+ physrd(ksym_lookup("IdlePTD") - KERNOFF, &sbr, sizeof sbr);
+ slr = 2 * NPTEPG; /* XXX temporary */
+ printf("IdlePTD %x\n", sbr);
+ curpcb = ksym_lookup("curpcb") - KERNOFF;
+ physrd(curpcb, &curpcb, sizeof curpcb);
+ kstack = ksym_lookup("kstack");
+#else
+ sbr = ksym_lookup("Sysmap");
+ slr = ksym_lookup("Syssize");
+ printf("sbr %x slr %x\n", sbr, slr);
+#endif
+
+ /*
+ * pcb where "panic" saved registers in first thing in current
+ * u area.
+ */
+#ifndef NEWVM
+ read_pcb(vtophys(ksym_lookup("u")));
+#endif
+ found_pcb = 1;
+ if (!devmem) {
+ /* find stack frame */
+ CORE_ADDR panicstr;
+ char buf[256];
+ register char *cp;
+
+ panicstr = kvread(ksym_lookup("panicstr"));
+ if (panicstr == ~0)
+ return;
+ (void) kernel_core_file_hook(panicstr, buf, sizeof(buf));
+ for (cp = buf; cp < &buf[sizeof(buf)] && *cp; cp++)
+ if (!isascii(*cp) || (!isprint(*cp) && !isspace(*cp)))
+ *cp = '?';
+ if (*cp)
+ *cp = '\0';
+ printf("panic: %s\n", buf);
+ read_pcb(ksym_lookup("dumppcb") - KERNOFF);
+ }
+#ifdef NEWVM
+ else
+ read_pcb(vtophys(kstack));
+#endif
+
+ stack_start = USRSTACK;
+ stack_end = USRSTACK + ctob(UPAGES);
+}
+
+set_paddr_command(arg)
+ char *arg;
+{
+ u_int uaddr;
+
+ if (!arg)
+ error_no_arg("ps-style address for new current process");
+ if (!kernel_debugging)
+ error("not debugging kernel");
+ uaddr = (u_int) parse_and_eval_address(arg);
+#ifndef NEWVM
+ read_pcb(ctob(uaddr));
+#else
+ /* p_addr is now a pcb virtual address */
+ read_pcb(vtophys(uaddr));
+ curpcb = uaddr;
+#endif
+
+ flush_cached_frames();
+ set_current_frame(create_new_frame(read_register(FP_REGNUM), read_pc()));
+ select_frame(get_current_frame(), 0);
+}
+
+/*
+ * read len bytes from kernel virtual address 'addr' into local
+ * buffer 'buf'. Return 0 if read ok, 1 otherwise. On read
+ * errors, portion of buffer not read is zeroed.
+ */
+kernel_core_file_hook(addr, buf, len)
+ CORE_ADDR addr;
+ char *buf;
+ int len;
+{
+ int i;
+ CORE_ADDR paddr;
+
+ while (len > 0) {
+ paddr = vtophys(addr);
+ if (paddr == ~0) {
+ bzero(buf, len);
+ return (1);
+ }
+ /* we can't read across a page boundary */
+ i = min(len, NBPG - (addr & PGOFSET));
+ if (physrd(paddr, buf, i)) {
+ bzero(buf, len);
+ return (1);
+ }
+ buf += i;
+ addr += i;
+ len -= i;
+ }
+ return (0);
+}
+#endif
+
+core_file_command(filename, from_tty)
+ char *filename;
+ int from_tty;
+{
+ int val;
+ extern char registers[];
+#ifdef KERNELDEBUG
+ struct stat stb;
+#endif
+
+ /*
+ * Discard all vestiges of any previous core file and mark data and
+ * stack spaces as empty.
+ */
+ if (corefile)
+ free(corefile);
+ corefile = 0;
+ core_file_hook = 0;
+
+ if (corechan >= 0)
+ close(corechan);
+ corechan = -1;
+
+ /* Now, if a new core file was specified, open it and digest it. */
+
+ if (filename == 0) {
+ if (from_tty)
+ printf("No core file now.\n");
+ return;
+ }
+ filename = tilde_expand(filename);
+ make_cleanup(free, filename);
+ if (have_inferior_p())
+ error("To look at a core file, you must kill the inferior with \"kill\".");
+ corechan = open(filename, O_RDONLY, 0);
+ if (corechan < 0)
+ perror_with_name(filename);
+
+#ifdef KERNELDEBUG
+ fstat(corechan, &stb);
+
+ if (kernel_debugging) {
+ setup_kernel_debugging();
+ core_file_hook = kernel_core_file_hook;
+ } else if ((stb.st_mode & S_IFMT) == S_IFCHR &&
+ stb.st_rdev == makedev(2, 1)) {
+ /* looking at /dev/kmem */
+ data_offset = data_start = KERNOFF;
+ data_end = ~0; /* XXX */
+ stack_end = stack_start = data_end;
+ } else
+#endif
+ {
+ /*
+ * 4.2-style core dump file.
+ */
+ struct user u;
+ unsigned int reg_offset;
+
+ val = myread(corechan, &u, sizeof u);
+ if (val < 0)
+ perror_with_name("Not a core file: reading upage");
+ if (val != sizeof u)
+ error("Not a core file: could only read %d bytes", val);
+
+ /*
+ * We are depending on exec_file_command having been
+ * called previously to set exec_data_start. Since
+ * the executable and the core file share the same
+ * text segment, the address of the data segment will
+ * be the same in both.
+ */
+ data_start = exec_data_start;
+
+#ifndef NEWVM
+ data_end = data_start + NBPG * u.u_dsize;
+ stack_start = stack_end - NBPG * u.u_ssize;
+ data_offset = NBPG * UPAGES;
+ stack_offset = NBPG * (UPAGES + u.u_dsize);
+
+ /*
+ * Some machines put an absolute address in here and
+ * some put the offset in the upage of the regs.
+ */
+ reg_offset = (int) u.u_ar0 - KERNEL_U_ADDR;
+#else
+ stack_end = (CORE_ADDR) u.u_kproc.kp_eproc.e_vm.vm_maxsaddr
+ + MAXSSIZ;
+
+ data_end = data_start +
+ NBPG * u.u_kproc.kp_eproc.e_vm.vm_dsize;
+ stack_start = stack_end -
+ NBPG * u.u_kproc.kp_eproc.e_vm.vm_ssize;
+ data_offset = NBPG * UPAGES;
+ stack_offset = NBPG *
+ (UPAGES + u.u_kproc.kp_eproc.e_vm.vm_dsize);
+
+ reg_offset = (int) u.u_kproc.kp_proc.p_regs - USRSTACK;
+#endif
+
+ setregmap(u.u_pcb.pcb_flags);
+
+ /*
+ * I don't know where to find this info. So, for now,
+ * mark it as not available.
+ */
+ /* N_SET_MAGIC (core_aouthdr, 0); */
+ bzero ((char *) &core_aouthdr, sizeof core_aouthdr);
+
+ /*
+ * Read the register values out of the core file and
+ * store them where `read_register' will find them.
+ */
+ {
+ register int regno;
+
+ for (regno = 0; regno < NUM_REGS; regno++) {
+ char buf[MAX_REGISTER_RAW_SIZE];
+
+ val = lseek(corechan, register_addr(regno, reg_offset), 0);
+ if (val < 0
+ || (val = myread(corechan, buf, sizeof buf)) < 0) {
+ char *buffer = (char *) alloca(strlen(reg_names[regno]) + 30);
+ strcpy(buffer, "Reading register ");
+ strcat(buffer, reg_names[regno]);
+ perror_with_name(buffer);
+ }
+ supply_register(regno, buf);
+ }
+ }
+ }
+#endif
+ if (filename[0] == '/')
+ corefile = savestring(filename, strlen(filename));
+ else
+ corefile = concat(current_directory, "/", filename);
+
+ set_current_frame(create_new_frame(read_register(FP_REGNUM),
+ read_pc()));
+ select_frame(get_current_frame(), 0);
+ validate_files();
+}
+
+exec_file_command(filename, from_tty)
+ char *filename;
+ int from_tty;
+{
+ int val;
+
+ /*
+ * Eliminate all traces of old exec file. Mark text segment as empty.
+ */
+
+ if (execfile)
+ free(execfile);
+ execfile = 0;
+ data_start = 0;
+ data_end = 0;
+ stack_start = 0;
+ stack_end = 0;
+ text_start = 0;
+ text_end = 0;
+ exec_data_start = 0;
+ exec_data_end = 0;
+ if (execchan >= 0)
+ close(execchan);
+ execchan = -1;
+
+ /* Now open and digest the file the user requested, if any. */
+
+ if (filename) {
+ filename = tilde_expand(filename);
+ make_cleanup(free, filename);
+
+ execchan = openp(getenv("PATH"), 1, filename, O_RDONLY, 0,
+ &execfile);
+ if (execchan < 0)
+ perror_with_name(filename);
+
+ {
+ struct stat st_exec;
+
+#ifdef HEADER_SEEK_FD
+ HEADER_SEEK_FD(execchan);
+#endif
+
+ val = myread(execchan, &exec_aouthdr, sizeof(AOUTHDR));
+
+ if (val < 0)
+ perror_with_name(filename);
+
+#ifdef KERNELDEBUG
+ if (kernel_debugging) {
+ /* Gross and disgusting XXX */
+ text_start = KERNTEXT_BASE;
+ exec_data_start = KERNTEXT_BASE +
+ (exec_aouthdr.a_text + 4095) & ~ 4095;
+ } else {
+#endif
+ text_start = N_TXTADDR(exec_aouthdr);
+ exec_data_start = N_DATADDR(exec_aouthdr);
+#ifdef KERNELDEBUG
+ }
+#endif
+
+ text_offset = N_TXTOFF(exec_aouthdr);
+ exec_data_offset = N_TXTOFF(exec_aouthdr) + exec_aouthdr.a_text;
+
+ text_end = text_start + exec_aouthdr.a_text;
+ exec_data_end = exec_data_start + exec_aouthdr.a_data;
+
+ fstat(execchan, &st_exec);
+ exec_mtime = st_exec.st_mtime;
+ }
+
+ validate_files();
+ } else if (from_tty)
+ printf("No exec file now.\n");
+
+ /* Tell display code (if any) about the changed file name. */
+ if (exec_file_display_hook)
+ (*exec_file_display_hook) (filename);
+}
+
+int dummy_code[] = {
+ 0xb8909090, /* nop; nop; nop; movl $0x32323232,%eax */
+ 0x32323232,
+#define DUMMY_CALL_INDEX 1
+ 0x90ccd0ff, /* call %eax; int3; nop */
+};
+
+/*
+ * Build `dummy' call instructions on inferior's stack to cause
+ * it to call a subroutine.
+ *
+ * N.B. - code in wait_for_inferior requires that sp < pc < fp when
+ * we take the trap 2 above so it will recognize that we stopped
+ * at a `dummy' call. So, after the call sp is *not* decremented
+ * to clean the arguments, code & other stuff we lay on the stack.
+ * Since the regs are restored to saved values at the breakpoint,
+ * sp will get reset correctly. Also, this restore means we don't
+ * have to construct frame linkage info to save pc & fp. The lack
+ * of frame linkage means we can't do a backtrace, etc., if the
+ * called function gets a fault or hits a breakpoint but code in
+ * run_stack_dummy makes this impossible anyway.
+ */
+CORE_ADDR
+setup_dummy(sp, funaddr, nargs, args, struct_return_bytes, pushfn)
+ CORE_ADDR sp;
+ CORE_ADDR funaddr;
+ int nargs;
+ value *args;
+ int struct_return_bytes;
+ CORE_ADDR (*pushfn)();
+{
+ int padding, i;
+ CORE_ADDR top = sp, struct_addr, pc;
+
+ i = arg_stacklen(nargs, args) + struct_return_bytes
+ + sizeof(dummy_code);
+ if (i & 3)
+ padding = 4 - (i & 3);
+ else
+ padding = 0;
+ pc = sp - sizeof(dummy_code);
+ sp = pc - padding - struct_return_bytes;
+ struct_addr = sp;
+ while (--nargs >= 0)
+ sp = (*pushfn)(sp, *args++);
+ if (struct_return_bytes)
+ STORE_STRUCT_RETURN(struct_addr, sp);
+ write_register(SP_REGNUM, sp);
+
+ dummy_code[DUMMY_CALL_INDEX] = (int)funaddr;
+ write_memory(pc, (char *)dummy_code, sizeof(dummy_code));
+
+ return pc;
+}
+
+/* helper functions for m-i386.h */
+
+/* stdio style buffering to minimize calls to ptrace */
+static CORE_ADDR codestream_next_addr;
+static CORE_ADDR codestream_addr;
+static unsigned char codestream_buf[sizeof (int)];
+static int codestream_off;
+static int codestream_cnt;
+
+#define codestream_tell() (codestream_addr + codestream_off)
+#define codestream_peek() (codestream_cnt == 0 ? \
+ codestream_fill(1): codestream_buf[codestream_off])
+#define codestream_get() (codestream_cnt-- == 0 ? \
+ codestream_fill(0) : codestream_buf[codestream_off++])
+
+static unsigned char
+codestream_fill (peek_flag)
+{
+ codestream_addr = codestream_next_addr;
+ codestream_next_addr += sizeof (int);
+ codestream_off = 0;
+ codestream_cnt = sizeof (int);
+ read_memory (codestream_addr,
+ (unsigned char *)codestream_buf,
+ sizeof (int));
+
+ if (peek_flag)
+ return (codestream_peek());
+ else
+ return (codestream_get());
+}
+
+static void
+codestream_seek (place)
+{
+ codestream_next_addr = place & -sizeof (int);
+ codestream_cnt = 0;
+ codestream_fill (1);
+ while (codestream_tell() != place)
+ codestream_get ();
+}
+
+static void
+codestream_read (buf, count)
+ unsigned char *buf;
+{
+ unsigned char *p;
+ int i;
+ p = buf;
+ for (i = 0; i < count; i++)
+ *p++ = codestream_get ();
+}
+
+/* next instruction is a jump, move to target */
+static
+i386_follow_jump ()
+{
+ int long_delta;
+ short short_delta;
+ char byte_delta;
+ int data16;
+ int pos;
+
+ pos = codestream_tell ();
+
+ data16 = 0;
+ if (codestream_peek () == 0x66)
+ {
+ codestream_get ();
+ data16 = 1;
+ }
+
+ switch (codestream_get ())
+ {
+ case 0xe9:
+ /* relative jump: if data16 == 0, disp32, else disp16 */
+ if (data16)
+ {
+ codestream_read ((unsigned char *)&short_delta, 2);
+ pos += short_delta + 3; /* include size of jmp inst */
+ }
+ else
+ {
+ codestream_read ((unsigned char *)&long_delta, 4);
+ pos += long_delta + 5;
+ }
+ break;
+ case 0xeb:
+ /* relative jump, disp8 (ignore data16) */
+ codestream_read ((unsigned char *)&byte_delta, 1);
+ pos += byte_delta + 2;
+ break;
+ }
+ codestream_seek (pos + data16);
+}
+
+/*
+ * find & return amound a local space allocated, and advance codestream to
+ * first register push (if any)
+ *
+ * if entry sequence doesn't make sense, return -1, and leave
+ * codestream pointer random
+ */
+static long
+i386_get_frame_setup (pc)
+{
+ unsigned char op;
+
+ codestream_seek (pc);
+
+ i386_follow_jump ();
+
+ op = codestream_get ();
+
+ if (op == 0x58) /* popl %eax */
+ {
+ /*
+ * this function must start with
+ *
+ * popl %eax 0x58
+ * xchgl %eax, (%esp) 0x87 0x04 0x24
+ * or xchgl %eax, 0(%esp) 0x87 0x44 0x24 0x00
+ *
+ * (the system 5 compiler puts out the second xchg
+ * inst, and the assembler doesn't try to optimize it,
+ * so the 'sib' form gets generated)
+ *
+ * this sequence is used to get the address of the return
+ * buffer for a function that returns a structure
+ */
+ int pos;
+ unsigned char buf[4];
+ static unsigned char proto1[3] = { 0x87,0x04,0x24 };
+ static unsigned char proto2[4] = { 0x87,0x44,0x24,0x00 };
+ pos = codestream_tell ();
+ codestream_read (buf, 4);
+ if (bcmp (buf, proto1, 3) == 0)
+ pos += 3;
+ else if (bcmp (buf, proto2, 4) == 0)
+ pos += 4;
+
+ codestream_seek (pos);
+ op = codestream_get (); /* update next opcode */
+ }
+
+ if (op == 0x55) /* pushl %esp */
+ {
+ /* check for movl %esp, %ebp - can be written two ways */
+ switch (codestream_get ())
+ {
+ case 0x8b:
+ if (codestream_get () != 0xec)
+ return (-1);
+ break;
+ case 0x89:
+ if (codestream_get () != 0xe5)
+ return (-1);
+ break;
+ default:
+ return (-1);
+ }
+ /* check for stack adjustment
+ *
+ * subl $XXX, %esp
+ *
+ * note: you can't subtract a 16 bit immediate
+ * from a 32 bit reg, so we don't have to worry
+ * about a data16 prefix
+ */
+ op = codestream_peek ();
+ if (op == 0x83)
+ {
+ /* subl with 8 bit immed */
+ codestream_get ();
+ if (codestream_get () != 0xec)
+ return (-1);
+ /* subl with signed byte immediate
+ * (though it wouldn't make sense to be negative)
+ */
+ return (codestream_get());
+ }
+ else if (op == 0x81)
+ {
+ /* subl with 32 bit immed */
+ int locals;
+ codestream_get();
+ if (codestream_get () != 0xec)
+ return (-1);
+ /* subl with 32 bit immediate */
+ codestream_read ((unsigned char *)&locals, 4);
+ return (locals);
+ }
+ else
+ {
+ return (0);
+ }
+ }
+ else if (op == 0xc8)
+ {
+ /* enter instruction: arg is 16 bit unsigned immed */
+ unsigned short slocals;
+ codestream_read ((unsigned char *)&slocals, 2);
+ codestream_get (); /* flush final byte of enter instruction */
+ return (slocals);
+ }
+ return (-1);
+}
+
+/* Return number of args passed to a frame.
+ Can return -1, meaning no way to tell. */
+
+/* on the 386, the instruction following the call could be:
+ * popl %ecx - one arg
+ * addl $imm, %esp - imm/4 args; imm may be 8 or 32 bits
+ * anything else - zero args
+ */
+
+int
+i386_frame_num_args (fi)
+ struct frame_info fi;
+{
+ int retpc;
+ unsigned char op;
+ struct frame_info *pfi;
+
+ pfi = get_prev_frame_info ((fi));
+ if (pfi == 0)
+ {
+ /* Note: this can happen if we are looking at the frame for
+ main, because FRAME_CHAIN_VALID won't let us go into
+ start. If we have debugging symbols, that's not really
+ a big deal; it just means it will only show as many arguments
+ to main as are declared. */
+ return -1;
+ }
+ else
+ {
+ retpc = pfi->pc;
+ op = read_memory_integer (retpc, 1);
+ if (op == 0x59)
+ /* pop %ecx */
+ return 1;
+ else if (op == 0x83)
+ {
+ op = read_memory_integer (retpc+1, 1);
+ if (op == 0xc4)
+ /* addl $<signed imm 8 bits>, %esp */
+ return (read_memory_integer (retpc+2,1)&0xff)/4;
+ else
+ return 0;
+ }
+ else if (op == 0x81)
+ { /* add with 32 bit immediate */
+ op = read_memory_integer (retpc+1, 1);
+ if (op == 0xc4)
+ /* addl $<imm 32>, %esp */
+ return read_memory_integer (retpc+2, 4) / 4;
+ else
+ return 0;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+}
+
+/*
+ * parse the first few instructions of the function to see
+ * what registers were stored.
+ *
+ * We handle these cases:
+ *
+ * The startup sequence can be at the start of the function,
+ * or the function can start with a branch to startup code at the end.
+ *
+ * %ebp can be set up with either the 'enter' instruction, or
+ * 'pushl %ebp, movl %esp, %ebp' (enter is too slow to be useful,
+ * but was once used in the sys5 compiler)
+ *
+ * Local space is allocated just below the saved %ebp by either the
+ * 'enter' instruction, or by 'subl $<size>, %esp'. 'enter' has
+ * a 16 bit unsigned argument for space to allocate, and the
+ * 'addl' instruction could have either a signed byte, or
+ * 32 bit immediate.
+ *
+ * Next, the registers used by this function are pushed. In
+ * the sys5 compiler they will always be in the order: %edi, %esi, %ebx
+ * (and sometimes a harmless bug causes it to also save but not restore %eax);
+ * however, the code below is willing to see the pushes in any order,
+ * and will handle up to 8 of them.
+ *
+ * If the setup sequence is at the end of the function, then the
+ * next instruction will be a branch back to the start.
+ */
+
+i386_frame_find_saved_regs (fip, fsrp)
+ struct frame_info *fip;
+ struct frame_saved_regs *fsrp;
+{
+ unsigned long locals;
+ unsigned char *p;
+ unsigned char op;
+ CORE_ADDR dummy_bottom;
+ CORE_ADDR adr;
+ int i;
+
+ bzero (fsrp, sizeof *fsrp);
+
+#if 0
+ /* if frame is the end of a dummy, compute where the
+ * beginning would be
+ */
+ dummy_bottom = fip->frame - 4 - NUM_REGS*4 - CALL_DUMMY_LENGTH;
+
+ /* check if the PC is in the stack, in a dummy frame */
+ if (dummy_bottom <= fip->pc && fip->pc <= fip->frame)
+ {
+ /* all regs were saved by push_call_dummy () */
+ adr = fip->frame - 4;
+ for (i = 0; i < NUM_REGS; i++)
+ {
+ fsrp->regs[i] = adr;
+ adr -= 4;
+ }
+ return;
+ }
+#endif
+
+ locals = i386_get_frame_setup (get_pc_function_start (fip->pc));
+
+ if (locals >= 0)
+ {
+ adr = fip->frame - 4 - locals;
+ for (i = 0; i < 8; i++)
+ {
+ op = codestream_get ();
+ if (op < 0x50 || op > 0x57)
+ break;
+ fsrp->regs[op - 0x50] = adr;
+ adr -= 4;
+ }
+ }
+
+ fsrp->regs[PC_REGNUM] = fip->frame + 4;
+ fsrp->regs[FP_REGNUM] = fip->frame;
+}
+
+/* return pc of first real instruction */
+i386_skip_prologue (pc)
+{
+ unsigned char op;
+ int i;
+
+ if (i386_get_frame_setup (pc) < 0)
+ return (pc);
+
+ /* found valid frame setup - codestream now points to
+ * start of push instructions for saving registers
+ */
+
+ /* skip over register saves */
+ for (i = 0; i < 8; i++)
+ {
+ op = codestream_peek ();
+ /* break if not pushl inst */
+ if (op < 0x50 || op > 0x57)
+ break;
+ codestream_get ();
+ }
+
+ i386_follow_jump ();
+
+ return (codestream_tell ());
+}
+
+i386_pop_frame ()
+{
+ FRAME frame = get_current_frame ();
+ CORE_ADDR fp;
+ int regnum;
+ struct frame_saved_regs fsr;
+ struct frame_info *fi;
+
+ fi = get_frame_info (frame);
+ fp = fi->frame;
+ get_frame_saved_regs (fi, &fsr);
+ for (regnum = 0; regnum < NUM_REGS; regnum++)
+ {
+ CORE_ADDR adr;
+ adr = fsr.regs[regnum];
+ if (adr)
+ write_register (regnum, read_memory_integer (adr, 4));
+ }
+ write_register (FP_REGNUM, read_memory_integer (fp, 4));
+ write_register (PC_REGNUM, read_memory_integer (fp + 4, 4));
+ write_register (SP_REGNUM, fp + 8);
+ flush_cached_frames ();
+ set_current_frame ( create_new_frame (read_register (FP_REGNUM),
+ read_pc ()));
+}
+
+/* this table must line up with REGISTER_NAMES in m-i386.h */
+/* symbols like 'EAX' come from <sys/reg.h> */
+static int trapmap[] =
+{
+ tEAX, tECX, tEDX, tEBX,
+ tESP, tEBP, tESI, tEDI,
+ tEIP, tEFLAGS, tCS, tSS,
+ tDS, tES, tES, tES /* lies: no fs or gs */
+};
+#if defined(FM_TRAP) || defined(EX_TRAPSTK)
+static int syscallmap[] =
+{
+ sEAX, sECX, sEDX, sEBX,
+ sESP, sEBP, sESI, sEDI,
+ sEIP, sEFLAGS, sCS, sSS,
+ sCS, sCS, sCS, sCS /* lies: no ds, es, fs or gs */
+};
+#endif
+static int *regmap;
+
+static void
+setregmap(flags)
+ int flags;
+{
+#ifdef FM_TRAP
+ regmap = flags & FM_TRAP ? trapmap: syscallmap;
+#elif EX_TRAPSTK
+ regmap = flags & EX_TRAPSTK ? trapmap : syscallmap;
+#else
+ regmap = trapmap; /* the lesser evil */
+#endif
+}
+
+/* blockend is the value of u.u_ar0, and points to the
+ * place where GS is stored
+ */
+i386_register_u_addr (blockend, regnum)
+{
+#if 0
+ /* this will be needed if fp registers are reinstated */
+ /* for now, you can look at them with 'info float'
+ * sys5 wont let you change them with ptrace anyway
+ */
+ if (regnum >= FP0_REGNUM && regnum <= FP7_REGNUM)
+ {
+ int ubase, fpstate;
+ struct user u;
+ ubase = blockend + 4 * (SS + 1) - KSTKSZ;
+ fpstate = ubase + ((char *)&u.u_fpstate - (char *)&u);
+ return (fpstate + 0x1c + 10 * (regnum - FP0_REGNUM));
+ }
+ else
+#endif
+ return (blockend + 4 * regmap[regnum]);
+}
+
+i387_to_double (from, to)
+ char *from;
+ char *to;
+{
+ long *lp;
+ /* push extended mode on 387 stack, then pop in double mode
+ *
+ * first, set exception masks so no error is generated -
+ * number will be rounded to inf or 0, if necessary
+ */
+ asm ("pushl %eax"); /* grab a stack slot */
+ asm ("fstcw (%esp)"); /* get 387 control word */
+ asm ("movl (%esp),%eax"); /* save old value */
+ asm ("orl $0x3f,%eax"); /* mask all exceptions */
+ asm ("pushl %eax");
+ asm ("fldcw (%esp)"); /* load new value into 387 */
+
+ asm ("movl 8(%ebp),%eax");
+ asm ("fldt (%eax)"); /* push extended number on 387 stack */
+ asm ("fwait");
+ asm ("movl 12(%ebp),%eax");
+ asm ("fstpl (%eax)"); /* pop double */
+ asm ("fwait");
+
+ asm ("popl %eax"); /* flush modified control word */
+ asm ("fnclex"); /* clear exceptions */
+ asm ("fldcw (%esp)"); /* restore original control word */
+ asm ("popl %eax"); /* flush saved copy */
+}
+
+double_to_i387 (from, to)
+ char *from;
+ char *to;
+{
+ /* push double mode on 387 stack, then pop in extended mode
+ * no errors are possible because every 64-bit pattern
+ * can be converted to an extended
+ */
+ asm ("movl 8(%ebp),%eax");
+ asm ("fldl (%eax)");
+ asm ("fwait");
+ asm ("movl 12(%ebp),%eax");
+ asm ("fstpt (%eax)");
+ asm ("fwait");
+}
+
+struct env387
+{
+ unsigned short control;
+ unsigned short r0;
+ unsigned short status;
+ unsigned short r1;
+ unsigned short tag;
+ unsigned short r2;
+ unsigned long eip;
+ unsigned short code_seg;
+ unsigned short opcode;
+ unsigned long operand;
+ unsigned short operand_seg;
+ unsigned short r3;
+ unsigned char regs[8][10];
+};
+
+static
+print_387_control_word (control)
+unsigned short control;
+{
+ printf ("control 0x%04x: ", control);
+ printf ("compute to ");
+ switch ((control >> 8) & 3)
+ {
+ case 0: printf ("24 bits; "); break;
+ case 1: printf ("(bad); "); break;
+ case 2: printf ("53 bits; "); break;
+ case 3: printf ("64 bits; "); break;
+ }
+ printf ("round ");
+ switch ((control >> 10) & 3)
+ {
+ case 0: printf ("NEAREST; "); break;
+ case 1: printf ("DOWN; "); break;
+ case 2: printf ("UP; "); break;
+ case 3: printf ("CHOP; "); break;
+ }
+ if (control & 0x3f)
+ {
+ printf ("mask:");
+ if (control & 0x0001) printf (" INVALID");
+ if (control & 0x0002) printf (" DENORM");
+ if (control & 0x0004) printf (" DIVZ");
+ if (control & 0x0008) printf (" OVERF");
+ if (control & 0x0010) printf (" UNDERF");
+ if (control & 0x0020) printf (" LOS");
+ printf (";");
+ }
+ printf ("\n");
+ if (control & 0xe080) printf ("warning: reserved bits on 0x%x\n",
+ control & 0xe080);
+}
+
+static
+print_387_status_word (status)
+ unsigned short status;
+{
+ printf ("status 0x%04x: ", status);
+ if (status & 0xff)
+ {
+ printf ("exceptions:");
+ if (status & 0x0001) printf (" INVALID");
+ if (status & 0x0002) printf (" DENORM");
+ if (status & 0x0004) printf (" DIVZ");
+ if (status & 0x0008) printf (" OVERF");
+ if (status & 0x0010) printf (" UNDERF");
+ if (status & 0x0020) printf (" LOS");
+ if (status & 0x0040) printf (" FPSTACK");
+ printf ("; ");
+ }
+ printf ("flags: %d%d%d%d; ",
+ (status & 0x4000) != 0,
+ (status & 0x0400) != 0,
+ (status & 0x0200) != 0,
+ (status & 0x0100) != 0);
+
+ printf ("top %d\n", (status >> 11) & 7);
+}
+
+static
+print_387_status (status, ep)
+ unsigned short status;
+ struct env387 *ep;
+{
+ int i;
+ int bothstatus;
+ int top;
+ int fpreg;
+ unsigned char *p;
+
+ bothstatus = ((status != 0) && (ep->status != 0));
+ if (status != 0)
+ {
+ if (bothstatus)
+ printf ("u: ");
+ print_387_status_word (status);
+ }
+
+ if (ep->status != 0)
+ {
+ if (bothstatus)
+ printf ("e: ");
+ print_387_status_word (ep->status);
+ }
+
+ print_387_control_word (ep->control);
+ printf ("last exception: ");
+ printf ("opcode 0x%x; ", ep->opcode);
+ printf ("pc 0x%x:0x%x; ", ep->code_seg, ep->eip);
+ printf ("operand 0x%x:0x%x\n", ep->operand_seg, ep->operand);
+
+ top = (ep->status >> 11) & 7;
+
+ printf (" regno tag msb lsb value\n");
+ for (fpreg = 7; fpreg >= 0; fpreg--)
+ {
+ int st_regno;
+ double val;
+
+ /* The physical regno `fpreg' is only relevant as an index into the
+ * tag word. Logical `%st' numbers are required for indexing `p->regs.
+ */
+ st_regno = (fpreg + 8 - top) & 0x7;
+
+ printf ("%%st(%d) %s ", st_regno, fpreg == top ? "=>" : " ");
+
+ switch ((ep->tag >> (fpreg * 2)) & 3)
+ {
+ case 0: printf ("valid "); break;
+ case 1: printf ("zero "); break;
+ case 2: printf ("trap "); break;
+ case 3: printf ("empty "); break;
+ }
+ for (i = 9; i >= 0; i--)
+ printf ("%02x", ep->regs[st_regno][i]);
+
+ i387_to_double (ep->regs[st_regno], (char *)&val);
+ printf (" %g\n", val);
+ }
+#if 0 /* reserved fields are always 0xffff on 486's */
+ if (ep->r0)
+ printf ("warning: reserved0 is 0x%x\n", ep->r0);
+ if (ep->r1)
+ printf ("warning: reserved1 is 0x%x\n", ep->r1);
+ if (ep->r2)
+ printf ("warning: reserved2 is 0x%x\n", ep->r2);
+ if (ep->r3)
+ printf ("warning: reserved3 is 0x%x\n", ep->r3);
+#endif
+}
+
+#ifdef __386BSD__
+#define fpstate save87
+#define U_FPSTATE(u) u.u_pcb.pcb_savefpu
+#endif
+
+#ifndef U_FPSTATE
+#define U_FPSTATE(u) u.u_fpstate
+#endif
+
+i386_float_info ()
+{
+ struct user u; /* just for address computations */
+ int i;
+ /* fpstate defined in <sys/user.h> */
+ struct fpstate *fpstatep;
+ char buf[sizeof (struct fpstate) + 2 * sizeof (int)];
+ unsigned int uaddr;
+ char fpvalid;
+ unsigned int rounded_addr;
+ unsigned int rounded_size;
+ extern int corechan;
+ int skip;
+
+#ifndef __386BSD__ /* XXX - look at pcb flags */
+ uaddr = (char *)&u.u_fpvalid - (char *)&u;
+ if (have_inferior_p())
+ {
+ unsigned int data;
+ unsigned int mask;
+
+ rounded_addr = uaddr & -sizeof (int);
+ data = ptrace (PT_READ_U, inferior_pid, (caddr_t)rounded_addr, 0);
+ mask = 0xff << ((uaddr - rounded_addr) * 8);
+
+ fpvalid = ((data & mask) != 0);
+ }
+ else
+ {
+ if (lseek (corechan, uaddr, 0) < 0)
+ perror ("seek on core file");
+ if (myread (corechan, &fpvalid, 1) < 0)
+ perror ("read on core file");
+
+ }
+
+ if (fpvalid == 0)
+ {
+ printf ("no floating point status saved\n");
+ return;
+ }
+#endif /* not __386BSD__ */
+
+ uaddr = (char *)&U_FPSTATE(u) - (char *)&u;
+ if (have_inferior_p ())
+ {
+ int *ip;
+
+ rounded_addr = uaddr & -sizeof (int);
+ rounded_size = (((uaddr + sizeof (struct fpstate)) - uaddr) +
+ sizeof (int) - 1) / sizeof (int);
+ skip = uaddr - rounded_addr;
+
+ ip = (int *)buf;
+ for (i = 0; i < rounded_size; i++)
+ {
+ *ip++ = ptrace (PT_READ_U, inferior_pid, (caddr_t)rounded_addr, 0);
+ rounded_addr += sizeof (int);
+ }
+ }
+ else
+ {
+ if (lseek (corechan, uaddr, 0) < 0)
+ perror_with_name ("seek on core file");
+ if (myread (corechan, buf, sizeof (struct fpstate)) < 0)
+ perror_with_name ("read from core file");
+ skip = 0;
+ }
+
+#ifdef __386BSD__
+ print_387_status (0, (struct env387 *)buf);
+#else
+ fpstatep = (struct fpstate *)(buf + skip);
+ print_387_status (fpstatep->status, (struct env387 *)fpstatep->state);
+#endif
+}
+
+void
+_initialize_i386bsd_dep()
+{
+#ifdef KERNELDEBUG
+ add_com ("process-address", class_obscure, set_paddr_command,
+ "The process identified by (ps-style) ADDR becomes the\n\
+\"current\" process context for kernel debugging.");
+ add_com_alias ("paddr", "process-address", class_obscure, 0);
+#endif
+}
diff --git a/gnu/usr.bin/gdb/config/m-i386-sv32.h b/gnu/usr.bin/gdb/config/m-i386-sv32.h
new file mode 100644
index 0000000..38fb4eb
--- /dev/null
+++ b/gnu/usr.bin/gdb/config/m-i386-sv32.h
@@ -0,0 +1,28 @@
+/* Macro defintions for i386, running System V 3.2.
+ Copyright (C) 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "m-i386.h"
+
+/* Apparently there is inconsistency among various System V's about what
+ the name of this field is. */
+#define U_FPSTATE(u) u.u_fps.u_fpstate
+
+/* TIOCGETC is defined in System V 3.2 termio.h, but struct tchars
+ is not. This makes problems for inflow.c. */
+#define TIOCGETC_BROKEN
diff --git a/gnu/usr.bin/gdb/config/m-i386.h b/gnu/usr.bin/gdb/config/m-i386.h
new file mode 100644
index 0000000..5449ec4
--- /dev/null
+++ b/gnu/usr.bin/gdb/config/m-i386.h
@@ -0,0 +1,394 @@
+/* Macro defintions for i386.
+ Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Define the bit, byte, and word ordering of the machine. */
+/* #define BITS_BIG_ENDIAN */
+/* #define BYTES_BIG_ENDIAN */
+/* #define WORDS_BIG_ENDIAN */
+
+/*
+ * Changes for 80386 by Pace Willisson (pace@prep.ai.mit.edu)
+ * July 1988
+ */
+
+
+#ifndef i386
+#define i386
+#endif
+
+/* I'm running gdb 3.4 under 386/ix 2.0.2, which is a derivative of AT&T's
+Sys V/386 3.2.
+
+On some machines, gdb crashes when it's starting up while calling the
+vendor's termio tgetent() routine. It always works when run under
+itself (actually, under 3.2, it's not an infinitely recursive bug.)
+After some poking around, it appears that depending on the environment
+size, or whether you're running YP, or the phase of the moon or something,
+the stack is not always long-aligned when main() is called, and tgetent()
+takes strong offense at that. On some machines this bug never appears, but
+on those where it does, it occurs quite reliably. */
+#define ALIGN_STACK_ON_STARTUP
+
+/* define USG if you are using sys5 /usr/include's */
+#define USG
+
+/* USG systems need these */
+#define vfork() fork()
+#define MAXPATHLEN 500
+
+/* define this if you don't have the extension to coff that allows
+ * file names to appear in the string table
+ * (aux.x_file.x_foff)
+ */
+#define COFF_NO_LONG_FILE_NAMES
+
+/* turn this on when rest of gdb is ready */
+/* #define IEEE_FLOAT */
+
+#define NBPG NBPC
+#define UPAGES USIZE
+
+#define HAVE_TERMIO
+
+/* Get rid of any system-imposed stack limit if possible. */
+
+/* #define SET_STACK_LIMIT_HUGE not in sys5 */
+
+/* Define this if the C compiler puts an underscore at the front
+ of external names before giving them to the linker. */
+
+/* #define NAMES_HAVE_UNDERSCORE */
+
+/* Specify debugger information format. */
+
+/* #define READ_DBX_FORMAT */
+#define COFF_FORMAT
+
+/* number of traps that happen between exec'ing the shell
+ * to run an inferior, and when we finally get to
+ * the inferior code. This is 2 on most implementations.
+ */
+#define START_INFERIOR_TRAPS_EXPECTED 4
+
+/* Offset from address of function to start of its code.
+ Zero on most machines. */
+
+#define FUNCTION_START_OFFSET 0
+
+/* Advance PC across any function entry prologue instructions
+ to reach some "real" code. */
+
+#define SKIP_PROLOGUE(frompc) {(frompc) = i386_skip_prologue((frompc));}
+
+/* Immediately after a function call, return the saved pc.
+ Can't always go through the frames for this because on some machines
+ the new frame is not set up until the new function executes
+ some instructions. */
+
+#define SAVED_PC_AFTER_CALL(frame) \
+ (read_memory_integer (read_register (SP_REGNUM), 4))
+
+/* This is the amount to subtract from u.u_ar0
+ to get the offset in the core file of the register values. */
+
+#define KERNEL_U_ADDR 0xe0000000
+
+/* Address of end of stack space. */
+
+#define STACK_END_ADDR 0x80000000
+
+/* Stack grows downward. */
+
+#define INNER_THAN <
+
+/* Sequence of bytes for breakpoint instruction. */
+
+#define BREAKPOINT {0xcc}
+
+/* Amount PC must be decremented by after a breakpoint.
+ This is often the number of bytes in BREAKPOINT
+ but not always. */
+
+#define DECR_PC_AFTER_BREAK 1
+
+/* Nonzero if instruction at PC is a return instruction. */
+
+#define ABOUT_TO_RETURN(pc) (read_memory_integer (pc, 1) == 0xc3)
+
+/* Return 1 if P points to an invalid floating point value.
+ LEN is the length in bytes -- not relevant on the 386. */
+
+#define INVALID_FLOAT(p, len) (0)
+
+/* code to execute to print interesting information about the
+ * floating point processor (if any)
+ * No need to define if there is nothing to do.
+ */
+#define FLOAT_INFO { i386_float_info (); }
+
+
+/* Largest integer type */
+#define LONGEST long
+
+/* Name of the builtin type for the LONGEST type above. */
+#define BUILTIN_TYPE_LONGEST builtin_type_long
+
+/* Say how long (ordinary) registers are. */
+
+#define REGISTER_TYPE long
+
+/* Number of machine registers */
+
+#define NUM_REGS 16
+
+/* Initializer for an array of names of registers.
+ There should be NUM_REGS strings in this initializer. */
+
+/* the order of the first 8 registers must match the compiler's
+ * numbering scheme (which is the same as the 386 scheme)
+ * also, this table must match regmap in i386-pinsn.c.
+ */
+#define REGISTER_NAMES { "eax", "ecx", "edx", "ebx", \
+ "esp", "ebp", "esi", "edi", \
+ "eip", "ps", "cs", "ss", \
+ "ds", "es", "fs", "gs", \
+ }
+
+/* Register numbers of various important registers.
+ Note that some of these values are "real" register numbers,
+ and correspond to the general registers of the machine,
+ and some are "phony" register numbers which are too large
+ to be actual register numbers as far as the user is concerned
+ but do serve to get the desired values when passed to read_register. */
+
+#define FP_REGNUM 5 /* Contains address of executing stack frame */
+#define SP_REGNUM 4 /* Contains address of top of stack */
+
+#define PC_REGNUM 8
+#define PS_REGNUM 9
+
+#define REGISTER_U_ADDR(addr, blockend, regno) \
+ (addr) = i386_register_u_addr ((blockend),(regno));
+
+/* Total amount of space needed to store our copies of the machine's
+ register state, the array `registers'. */
+#define REGISTER_BYTES (NUM_REGS * 4)
+
+/* Index within `registers' of the first byte of the space for
+ register N. */
+
+#define REGISTER_BYTE(N) ((N)*4)
+
+/* Number of bytes of storage in the actual machine representation
+ for register N. */
+
+#define REGISTER_RAW_SIZE(N) (4)
+
+/* Number of bytes of storage in the program's representation
+ for register N. */
+
+#define REGISTER_VIRTUAL_SIZE(N) (4)
+
+/* Largest value REGISTER_RAW_SIZE can have. */
+
+#define MAX_REGISTER_RAW_SIZE 4
+
+/* Largest value REGISTER_VIRTUAL_SIZE can have. */
+
+#define MAX_REGISTER_VIRTUAL_SIZE 4
+
+/* Nonzero if register N requires conversion
+ from raw format to virtual format. */
+
+#define REGISTER_CONVERTIBLE(N) (0)
+
+/* Convert data from raw format for register REGNUM
+ to virtual format for register REGNUM. */
+
+#define REGISTER_CONVERT_TO_VIRTUAL(REGNUM,FROM,TO) {bcopy ((FROM), (TO), 4);}
+
+/* Convert data from virtual format for register REGNUM
+ to raw format for register REGNUM. */
+
+#define REGISTER_CONVERT_TO_RAW(REGNUM,FROM,TO) {bcopy ((FROM), (TO), 4);}
+
+/* Return the GDB type object for the "standard" data type
+ of data in register N. */
+
+#define REGISTER_VIRTUAL_TYPE(N) (builtin_type_int)
+
+/* Store the address of the place in which to copy the structure the
+ subroutine will return. This is called from call_function. */
+
+#define STORE_STRUCT_RETURN(ADDR, SP) \
+ { (SP) -= sizeof (ADDR); \
+ write_memory ((SP), &(ADDR), sizeof (ADDR)); }
+
+/* Extract from an array REGBUF containing the (raw) register state
+ a function return value of type TYPE, and copy that, in virtual format,
+ into VALBUF. */
+
+#define EXTRACT_RETURN_VALUE(TYPE,REGBUF,VALBUF) \
+ bcopy (REGBUF, VALBUF, TYPE_LENGTH (TYPE))
+
+/* Write into appropriate registers a function return value
+ of type TYPE, given in virtual format. */
+
+#define STORE_RETURN_VALUE(TYPE,VALBUF) \
+ write_register_bytes (0, VALBUF, TYPE_LENGTH (TYPE))
+
+/* Extract from an array REGBUF containing the (raw) register state
+ the address in which a function should return its structure value,
+ as a CORE_ADDR (or an expression that can be used as one). */
+
+#define EXTRACT_STRUCT_VALUE_ADDRESS(REGBUF) (*(int *)(REGBUF))
+
+
+/* Describe the pointer in each stack frame to the previous stack frame
+ (its caller). */
+
+/* FRAME_CHAIN takes a frame's nominal address
+ and produces the frame's chain-pointer.
+
+ FRAME_CHAIN_COMBINE takes the chain pointer and the frame's nominal address
+ and produces the nominal address of the caller frame.
+
+ However, if FRAME_CHAIN_VALID returns zero,
+ it means the given frame is the outermost one and has no caller.
+ In that case, FRAME_CHAIN_COMBINE is not used. */
+
+#define FRAME_CHAIN(thisframe) \
+ (outside_startup_file ((thisframe)->pc) ? \
+ read_memory_integer ((thisframe)->frame, 4) :\
+ 0)
+
+#define FRAME_CHAIN_VALID(chain, thisframe) \
+ (chain != 0 && (outside_startup_file (FRAME_SAVED_PC (thisframe))))
+
+#define FRAME_CHAIN_COMBINE(chain, thisframe) (chain)
+
+/* Define other aspects of the stack frame. */
+
+/* A macro that tells us whether the function invocation represented
+ by FI does not have a frame on the stack associated with it. If it
+ does not, FRAMELESS is set to 1, else 0. */
+#define FRAMELESS_FUNCTION_INVOCATION(FI, FRAMELESS) \
+ FRAMELESS_LOOK_FOR_PROLOGUE(FI, FRAMELESS)
+
+#define FRAME_SAVED_PC(FRAME) (read_memory_integer ((FRAME)->frame + 4, 4))
+
+#define FRAME_ARGS_ADDRESS(fi) ((fi)->frame)
+
+#define FRAME_LOCALS_ADDRESS(fi) ((fi)->frame)
+
+/* Return number of args passed to a frame.
+ Can return -1, meaning no way to tell. */
+
+#define FRAME_NUM_ARGS(numargs, fi) (numargs) = i386_frame_num_args(fi)
+
+/* Return number of bytes at start of arglist that are not really args. */
+
+#define FRAME_ARGS_SKIP 8
+
+/* Put here the code to store, into a struct frame_saved_regs,
+ the addresses of the saved registers of frame described by FRAME_INFO.
+ This includes special registers such as pc and fp saved in special
+ ways in the stack frame. sp is even more special:
+ the address we return for it IS the sp for the next frame. */
+
+#define FRAME_FIND_SAVED_REGS(frame_info, frame_saved_regs) \
+{ i386_frame_find_saved_regs ((frame_info), &(frame_saved_regs)); }
+
+
+/* Things needed for making the inferior call functions. */
+
+/* Push an empty stack frame, to record the current PC, etc. */
+
+#define PUSH_DUMMY_FRAME { i386_push_dummy_frame (); }
+
+/* Discard from the stack the innermost frame, restoring all registers. */
+
+#define POP_FRAME { i386_pop_frame (); }
+
+/* this is
+ * call 11223344 (32 bit relative)
+ * int3
+ */
+
+#define CALL_DUMMY { 0x223344e8, 0xcc11 }
+
+#define CALL_DUMMY_LENGTH 8
+
+#define CALL_DUMMY_START_OFFSET 0 /* Start execution at beginning of dummy */
+
+/* Insert the specified number of args and function address
+ into a call sequence of the above form stored at DUMMYNAME. */
+
+#define FIX_CALL_DUMMY(dummyname, pc, fun, nargs, type) \
+{ \
+ int from, to, delta, loc; \
+ loc = (int)(read_register (SP_REGNUM) - CALL_DUMMY_LENGTH); \
+ from = loc + 5; \
+ to = (int)(fun); \
+ delta = to - from; \
+ *(int *)((char *)(dummyname) + 1) = delta; \
+}
+
+
+#if 0
+/* Interface definitions for kernel debugger KDB. */
+
+/* Map machine fault codes into signal numbers.
+ First subtract 0, divide by 4, then index in a table.
+ Faults for which the entry in this table is 0
+ are not handled by KDB; the program's own trap handler
+ gets to handle then. */
+
+#define FAULT_CODE_ORIGIN 0
+#define FAULT_CODE_UNITS 4
+#define FAULT_TABLE \
+{ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0}
+
+/* Start running with a stack stretching from BEG to END.
+ BEG and END should be symbols meaningful to the assembler.
+ This is used only for kdb. */
+
+#define INIT_STACK(beg, end) {}
+
+/* Push the frame pointer register on the stack. */
+#define PUSH_FRAME_PTR {}
+
+/* Copy the top-of-stack to the frame pointer register. */
+#define POP_FRAME_PTR {}
+
+/* After KDB is entered by a fault, push all registers
+ that GDB thinks about (all NUM_REGS of them),
+ so that they appear in order of ascending GDB register number.
+ The fault code will be on the stack beyond the last register. */
+
+#define PUSH_REGISTERS {}
+
+/* Assuming the registers (including processor status) have been
+ pushed on the stack in order of ascending GDB register number,
+ restore them and return to the address in the saved PC register. */
+
+#define POP_REGISTERS {}
+#endif
diff --git a/gnu/usr.bin/gdb/config/m-i386bsd.h b/gnu/usr.bin/gdb/config/m-i386bsd.h
new file mode 100644
index 0000000..15d97b2
--- /dev/null
+++ b/gnu/usr.bin/gdb/config/m-i386bsd.h
@@ -0,0 +1,375 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1991 by William Jolitz at UUNET Technologies, Inc.
+ *
+ * @(#)m-i386bsd.h 6.7 (Berkeley) 5/8/91
+ */
+
+/* Macro definitions for i386.
+ Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Define the bit, byte, and word ordering of the machine. */
+/* #define BITS_BIG_ENDIAN */
+/* #define BYTES_BIG_ENDIAN */
+/* #define WORDS_BIG_ENDIAN */
+
+/*
+ * Changes for 80386 by Pace Willisson (pace@prep.ai.mit.edu)
+ * July 1988
+ * [ MODIFIED FOR 386BSD W. Jolitz ]
+ */
+
+#ifndef i386
+#define i386 1
+#define i386b 1
+#endif
+
+#define IEEE_FLOAT
+#define LONG_LONG
+
+/* Library stuff: POSIX tty (not supported yet), V7 tty (sigh), vprintf. */
+
+#define HAVE_TERMIOS 1
+#define USE_OLD_TTY 1
+#define HAVE_VPRINTF 1
+
+/* We support local and remote kernel debugging. */
+
+#define KERNELDEBUG 1
+
+/* Get rid of any system-imposed stack limit if possible. */
+
+#define SET_STACK_LIMIT_HUGE
+
+/* Define this if the C compiler puts an underscore at the front
+ of external names before giving them to the linker. */
+
+#define NAMES_HAVE_UNDERSCORE
+
+/* Specify debugger information format. */
+
+#define READ_DBX_FORMAT
+
+/* number of traps that happen between exec'ing the shell
+ * to run an inferior, and when we finally get to
+ * the inferior code. This is 2 on most implementations.
+ */
+#define START_INFERIOR_TRAPS_EXPECTED 2
+
+/* Offset from address of function to start of its code.
+ Zero on most machines. */
+
+#define FUNCTION_START_OFFSET 0
+
+/* Advance PC across any function entry prologue instructions
+ to reach some "real" code. */
+
+#define SKIP_PROLOGUE(frompc) {(frompc) = i386_skip_prologue((frompc));}
+
+/* Immediately after a function call, return the saved pc.
+ Can't always go through the frames for this because on some machines
+ the new frame is not set up until the new function executes
+ some instructions. */
+
+#define SAVED_PC_AFTER_CALL(frame) \
+ (read_memory_integer (read_register (SP_REGNUM), 4))
+
+/* This is the amount to subtract from u.u_ar0
+ to get the offset in the core file of the register values. */
+
+#ifdef NEWVM
+#include <machine/vmparam.h>
+#define KERNEL_U_ADDR USRSTACK
+#else
+#define KERNEL_U_ADDR 0xfdffd000
+#endif
+
+/* Address of end of stack space. */
+
+#define STACK_END_ADDR KERNEL_U_ADDR
+
+/* Stack grows downward. */
+
+#define INNER_THAN <
+
+/* Sequence of bytes for breakpoint instruction. */
+
+#define BREAKPOINT {0xcc}
+
+/* Amount PC must be decremented by after a breakpoint.
+ This is often the number of bytes in BREAKPOINT
+ but not always. */
+
+#define DECR_PC_AFTER_BREAK 1
+
+/* Nonzero if instruction at PC is a return instruction. */
+
+#define ABOUT_TO_RETURN(pc) \
+ strchr("\302\303\312\313\317", read_memory_integer(pc, 1))
+
+/* Return 1 if P points to an invalid floating point value.
+ LEN is the length in bytes -- not relevant on the 386. */
+
+#define INVALID_FLOAT(p, len) (0)
+
+/* code to execute to print interesting information about the
+ * floating point processor (if any)
+ * No need to define if there is nothing to do.
+ */
+#define FLOAT_INFO { i386_float_info (); }
+
+
+/* Largest integer type */
+#define LONGEST long long
+
+/* Name of the builtin type for the LONGEST type above. */
+#define BUILTIN_TYPE_LONGEST builtin_type_long_long
+
+/* Say how long (ordinary) registers are. */
+
+#define REGISTER_TYPE long
+
+/* Number of machine registers */
+
+#define NUM_REGS 16
+
+/* Initializer for an array of names of registers.
+ There should be NUM_REGS strings in this initializer. */
+
+/* the order of the first 8 registers must match the compiler's
+ * numbering scheme (which is the same as the 386 scheme)
+ * also, this table must match regmap in i386-pinsn.c.
+ */
+#define REGISTER_NAMES { "eax", "ecx", "edx", "ebx", \
+ "esp", "ebp", "esi", "edi", \
+ "eip", "ps", "cs", "ss", \
+ "ds", "es", "fs", "gs", \
+ }
+
+/* Register numbers of various important registers.
+ Note that some of these values are "real" register numbers,
+ and correspond to the general registers of the machine,
+ and some are "phony" register numbers which are too large
+ to be actual register numbers as far as the user is concerned
+ but do serve to get the desired values when passed to read_register. */
+
+#define FP_REGNUM 5 /* Contains address of executing stack frame */
+#define SP_REGNUM 4 /* Contains address of top of stack */
+
+#define PC_REGNUM 8
+#define PS_REGNUM 9
+
+#define REGISTER_U_ADDR(addr, blockend, regno) \
+ (addr) = i386_register_u_addr ((blockend),(regno));
+
+/* Total amount of space needed to store our copies of the machine's
+ register state, the array `registers'. */
+#define REGISTER_BYTES (NUM_REGS * 4)
+
+/* Index within `registers' of the first byte of the space for
+ register N. */
+
+#define REGISTER_BYTE(N) ((N)*4)
+
+/* Number of bytes of storage in the actual machine representation
+ for register N. */
+
+#define REGISTER_RAW_SIZE(N) (4)
+
+/* Number of bytes of storage in the program's representation
+ for register N. */
+
+#define REGISTER_VIRTUAL_SIZE(N) (4)
+
+/* Largest value REGISTER_RAW_SIZE can have. */
+
+#define MAX_REGISTER_RAW_SIZE 4
+
+/* Largest value REGISTER_VIRTUAL_SIZE can have. */
+
+#define MAX_REGISTER_VIRTUAL_SIZE 4
+
+/* Nonzero if register N requires conversion
+ from raw format to virtual format. */
+
+#define REGISTER_CONVERTIBLE(N) (0)
+
+/* Convert data from raw format for register REGNUM
+ to virtual format for register REGNUM. */
+
+#define REGISTER_CONVERT_TO_VIRTUAL(REGNUM,FROM,TO) {bcopy ((FROM), (TO), 4);}
+
+/* Convert data from virtual format for register REGNUM
+ to raw format for register REGNUM. */
+
+#define REGISTER_CONVERT_TO_RAW(REGNUM,FROM,TO) {bcopy ((FROM), (TO), 4);}
+
+/* Return the GDB type object for the "standard" data type
+ of data in register N. */
+
+#define REGISTER_VIRTUAL_TYPE(N) (builtin_type_int)
+
+/* Store the address of the place in which to copy the structure the
+ subroutine will return. This is called from call_function. */
+
+#define STORE_STRUCT_RETURN(ADDR, SP) \
+ { (SP) -= sizeof (ADDR); \
+ write_memory ((SP), &(ADDR), sizeof (ADDR)); }
+
+/* Extract from an array REGBUF containing the (raw) register state
+ a function return value of type TYPE, and copy that, in virtual format,
+ into VALBUF. */
+
+#define EXTRACT_RETURN_VALUE(TYPE,REGBUF,VALBUF) \
+ bcopy (REGBUF, VALBUF, TYPE_LENGTH (TYPE))
+
+/* Write into appropriate registers a function return value
+ of type TYPE, given in virtual format. */
+
+#define STORE_RETURN_VALUE(TYPE,VALBUF) \
+ write_register_bytes (0, VALBUF, TYPE_LENGTH (TYPE))
+
+/* Extract from an array REGBUF containing the (raw) register state
+ the address in which a function should return its structure value,
+ as a CORE_ADDR (or an expression that can be used as one). */
+
+#define EXTRACT_STRUCT_VALUE_ADDRESS(REGBUF) (*(int *)(REGBUF))
+
+
+/* Describe the pointer in each stack frame to the previous stack frame
+ (its caller). */
+
+/* FRAME_CHAIN takes a frame's nominal address
+ and produces the frame's chain-pointer.
+
+ FRAME_CHAIN_COMBINE takes the chain pointer and the frame's nominal address
+ and produces the nominal address of the caller frame.
+
+ However, if FRAME_CHAIN_VALID returns zero,
+ it means the given frame is the outermost one and has no caller.
+ In that case, FRAME_CHAIN_COMBINE is not used. */
+
+#define FRAME_CHAIN(thisframe) \
+ (outside_startup_file ((thisframe)->pc) ? \
+ read_memory_integer ((thisframe)->frame, 4) :\
+ 0)
+
+#ifdef KERNELDEBUG
+#define KERNTEXT_BASE 0xfe000000
+#ifdef NEWVM
+#define KERNSTACK_TOP (read_register(SP_REGNUM) + 0x2000) /* approximate */
+#else
+/* #define KERNSTACK_TOP (P1PAGES << PGSHIFT) */
+#define KERNSTACK_TOP 0xfe000000
+#endif
+extern int kernel_debugging;
+#define FRAME_CHAIN_VALID(chain, thisframe) \
+ (chain != 0 && \
+ !kernel_debugging ? outside_startup_file(FRAME_SAVED_PC(thisframe)) :\
+ (chain >= read_register(SP_REGNUM) && chain < KERNSTACK_TOP))
+#else
+#define FRAME_CHAIN_VALID(chain, thisframe) \
+ (chain != 0 && (outside_startup_file (FRAME_SAVED_PC (thisframe))))
+#endif
+
+#define FRAME_CHAIN_COMBINE(chain, thisframe) (chain)
+
+/* Define other aspects of the stack frame. */
+
+/* A macro that tells us whether the function invocation represented
+ by FI does not have a frame on the stack associated with it. If it
+ does not, FRAMELESS is set to 1, else 0. */
+#define FRAMELESS_FUNCTION_INVOCATION(FI, FRAMELESS) \
+ FRAMELESS_LOOK_FOR_PROLOGUE(FI, FRAMELESS)
+
+#define FRAME_SAVED_PC(FRAME) (read_memory_integer ((FRAME)->frame + 4, 4))
+
+#define FRAME_ARGS_ADDRESS(fi) ((fi)->frame)
+
+#define FRAME_LOCALS_ADDRESS(fi) ((fi)->frame)
+
+/* Return number of args passed to a frame.
+ Can return -1, meaning no way to tell. */
+
+#define FRAME_NUM_ARGS(numargs, fi) (numargs) = i386_frame_num_args(fi)
+
+/* Return number of bytes at start of arglist that are not really args. */
+
+#define FRAME_ARGS_SKIP 8
+
+/* Put here the code to store, into a struct frame_saved_regs,
+ the addresses of the saved registers of frame described by FRAME_INFO.
+ This includes special registers such as pc and fp saved in special
+ ways in the stack frame. sp is even more special:
+ the address we return for it IS the sp for the next frame. */
+
+#define FRAME_FIND_SAVED_REGS(frame_info, frame_saved_regs) \
+{ i386_frame_find_saved_regs ((frame_info), &(frame_saved_regs)); }
+
+
+/* Discard from the stack the innermost frame, restoring all registers. */
+
+#define POP_FRAME { i386_pop_frame (); }
+
+#define NEW_CALL_FUNCTION
+
+#if 0
+/* Interface definitions for kernel debugger KDB. */
+
+/* Map machine fault codes into signal numbers.
+ First subtract 0, divide by 4, then index in a table.
+ Faults for which the entry in this table is 0
+ are not handled by KDB; the program's own trap handler
+ gets to handle then. */
+
+#define FAULT_CODE_ORIGIN 0
+#define FAULT_CODE_UNITS 4
+#define FAULT_TABLE \
+{ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0}
+
+/* Start running with a stack stretching from BEG to END.
+ BEG and END should be symbols meaningful to the assembler.
+ This is used only for kdb. */
+
+#define INIT_STACK(beg, end) {}
+
+/* Push the frame pointer register on the stack. */
+#define PUSH_FRAME_PTR {}
+
+/* Copy the top-of-stack to the frame pointer register. */
+#define POP_FRAME_PTR {}
+
+/* After KDB is entered by a fault, push all registers
+ that GDB thinks about (all NUM_REGS of them),
+ so that they appear in order of ascending GDB register number.
+ The fault code will be on the stack beyond the last register. */
+
+#define PUSH_REGISTERS {}
+
+/* Assuming the registers (including processor status) have been
+ pushed on the stack in order of ascending GDB register number,
+ restore them and return to the address in the saved PC register. */
+
+#define POP_REGISTERS {}
+#endif
diff --git a/gnu/usr.bin/gdb/config/m-i386g-sv32.h b/gnu/usr.bin/gdb/config/m-i386g-sv32.h
new file mode 100644
index 0000000..3d69eea
--- /dev/null
+++ b/gnu/usr.bin/gdb/config/m-i386g-sv32.h
@@ -0,0 +1,28 @@
+/* Macro defintions for i386, running System V 3.2.
+ Copyright (C) 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "m-i386gas.h"
+
+/* Apparently there is inconsistency among various System V's about what
+ the name of this field is. */
+#define U_FPSTATE(u) u.u_fps.u_fpstate
+
+/* TIOCGETC is defined in System V 3.2 termio.h, but struct tchars
+ is not. This makes problems for inflow.c. */
+#define TIOCGETC_BROKEN
diff --git a/gnu/usr.bin/gdb/config/m-i386gas.h b/gnu/usr.bin/gdb/config/m-i386gas.h
new file mode 100644
index 0000000..fbd2138
--- /dev/null
+++ b/gnu/usr.bin/gdb/config/m-i386gas.h
@@ -0,0 +1,37 @@
+/* Macro definitions for i386 using the GNU object file format.
+ Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * Changes for 80386 by Pace Willisson (pace@prep.ai.mit.edu)
+ * July 1988
+ *
+ * i386gnu: COFF_ENCAPSULATE
+ */
+
+
+#define COFF_ENCAPSULATE
+
+#include "m-i386.h"
+
+
+#define NAMES_HAVE_UNDERSCORE
+
+#undef COFF_FORMAT
+#define READ_DBX_FORMAT
+
diff --git a/gnu/usr.bin/gdb/copying.c b/gnu/usr.bin/gdb/copying.c
new file mode 100644
index 0000000..b3d7519
--- /dev/null
+++ b/gnu/usr.bin/gdb/copying.c
@@ -0,0 +1,215 @@
+/* Do not modify this file; it is created automatically
+ by copying.awk. */
+extern int immediate_quit;
+static void
+copying_info ()
+{
+ immediate_quit++;
+ printf_filtered ("\n");
+ printf_filtered (" GNU GENERAL PUBLIC LICENSE\n");
+ printf_filtered (" Version 1, February 1989\n");
+ printf_filtered ("\n");
+ printf_filtered (" Copyright (C) 1989 Free Software Foundation, Inc.\n");
+ printf_filtered (" 675 Mass Ave, Cambridge, MA 02139, USA\n");
+ printf_filtered (" Everyone is permitted to copy and distribute verbatim copies\n");
+ printf_filtered (" of this license document, but changing it is not allowed.\n");
+ printf_filtered ("\n");
+ printf_filtered (" Preamble\n");
+ printf_filtered ("\n");
+ printf_filtered (" The license agreements of most software companies try to keep users\n");
+ printf_filtered ("at the mercy of those companies. By contrast, our General Public\n");
+ printf_filtered ("License is intended to guarantee your freedom to share and change free\n");
+ printf_filtered ("software--to make sure the software is free for all its users. The\n");
+ printf_filtered ("General Public License applies to the Free Software Foundation's\n");
+ printf_filtered ("software and to any other program whose authors commit to using it.\n");
+ printf_filtered ("You can use it for your programs, too.\n");
+ printf_filtered ("\n");
+ printf_filtered (" When we speak of free software, we are referring to freedom, not\n");
+ printf_filtered ("price. Specifically, the General Public License is designed to make\n");
+ printf_filtered ("sure that you have the freedom to give away or sell copies of free\n");
+ printf_filtered ("software, that you receive source code or can get it if you want it,\n");
+ printf_filtered ("that you can change the software or use pieces of it in new free\n");
+ printf_filtered ("programs; and that you know you can do these things.\n");
+ printf_filtered ("\n");
+ printf_filtered (" To protect your rights, we need to make restrictions that forbid\n");
+ printf_filtered ("anyone to deny you these rights or to ask you to surrender the rights.\n");
+ printf_filtered ("These restrictions translate to certain responsibilities for you if you\n");
+ printf_filtered ("distribute copies of the software, or if you modify it.\n");
+ printf_filtered ("\n");
+ printf_filtered (" For example, if you distribute copies of a such a program, whether\n");
+ printf_filtered ("gratis or for a fee, you must give the recipients all the rights that\n");
+ printf_filtered ("you have. You must make sure that they, too, receive or can get the\n");
+ printf_filtered ("source code. And you must tell them their rights.\n");
+ printf_filtered ("\n");
+ printf_filtered (" We protect your rights with two steps: (1) copyright the software, and\n");
+ printf_filtered ("(2) offer you this license which gives you legal permission to copy,\n");
+ printf_filtered ("distribute and/or modify the software.\n");
+ printf_filtered ("\n");
+ printf_filtered (" Also, for each author's protection and ours, we want to make certain\n");
+ printf_filtered ("that everyone understands that there is no warranty for this free\n");
+ printf_filtered ("software. If the software is modified by someone else and passed on, we\n");
+ printf_filtered ("want its recipients to know that what they have is not the original, so\n");
+ printf_filtered ("that any problems introduced by others will not reflect on the original\n");
+ printf_filtered ("authors' reputations.\n");
+ printf_filtered ("\n");
+ printf_filtered (" The precise terms and conditions for copying, distribution and\n");
+ printf_filtered ("modification follow.\n");
+ printf_filtered (" \n");
+ printf_filtered (" GNU GENERAL PUBLIC LICENSE\n");
+ printf_filtered (" TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n");
+ printf_filtered ("\n");
+ printf_filtered (" 0. This License Agreement applies to any program or other work which\n");
+ printf_filtered ("contains a notice placed by the copyright holder saying it may be\n");
+ printf_filtered ("distributed under the terms of this General Public License. The\n");
+ printf_filtered ("\"Program\", below, refers to any such program or work, and a \"work based\n");
+ printf_filtered ("on the Program\" means either the Program or any work containing the\n");
+ printf_filtered ("Program or a portion of it, either verbatim or with modifications. Each\n");
+ printf_filtered ("licensee is addressed as \"you\".\n");
+ printf_filtered ("\n");
+ printf_filtered (" 1. You may copy and distribute verbatim copies of the Program's source\n");
+ printf_filtered ("code as you receive it, in any medium, provided that you conspicuously and\n");
+ printf_filtered ("appropriately publish on each copy an appropriate copyright notice and\n");
+ printf_filtered ("disclaimer of warranty; keep intact all the notices that refer to this\n");
+ printf_filtered ("General Public License and to the absence of any warranty; and give any\n");
+ printf_filtered ("other recipients of the Program a copy of this General Public License\n");
+ printf_filtered ("along with the Program. You may charge a fee for the physical act of\n");
+ printf_filtered ("transferring a copy.\n");
+ printf_filtered ("\n");
+ printf_filtered (" 2. You may modify your copy or copies of the Program or any portion of\n");
+ printf_filtered ("it, and copy and distribute such modifications under the terms of Paragraph\n");
+ printf_filtered ("1 above, provided that you also do the following:\n");
+ printf_filtered ("\n");
+ printf_filtered (" a) cause the modified files to carry prominent notices stating that\n");
+ printf_filtered (" you changed the files and the date of any change; and\n");
+ printf_filtered ("\n");
+ printf_filtered (" b) cause the whole of any work that you distribute or publish, that\n");
+ printf_filtered (" in whole or in part contains the Program or any part thereof, either\n");
+ printf_filtered (" with or without modifications, to be licensed at no charge to all\n");
+ printf_filtered (" third parties under the terms of this General Public License (except\n");
+ printf_filtered (" that you may choose to grant warranty protection to some or all\n");
+ printf_filtered (" third parties, at your option).\n");
+ printf_filtered ("\n");
+ printf_filtered (" c) If the modified program normally reads commands interactively when\n");
+ printf_filtered (" run, you must cause it, when started running for such interactive use\n");
+ printf_filtered (" in the simplest and most usual way, to print or display an\n");
+ printf_filtered (" announcement including an appropriate copyright notice and a notice\n");
+ printf_filtered (" that there is no warranty (or else, saying that you provide a\n");
+ printf_filtered (" warranty) and that users may redistribute the program under these\n");
+ printf_filtered (" conditions, and telling the user how to view a copy of this General\n");
+ printf_filtered (" Public License.\n");
+ printf_filtered ("\n");
+ printf_filtered (" d) You may charge a fee for the physical act of transferring a\n");
+ printf_filtered (" copy, and you may at your option offer warranty protection in\n");
+ printf_filtered (" exchange for a fee.\n");
+ printf_filtered ("\n");
+ printf_filtered ("Mere aggregation of another independent work with the Program (or its\n");
+ printf_filtered ("derivative) on a volume of a storage or distribution medium does not bring\n");
+ printf_filtered ("the other work under the scope of these terms.\n");
+ printf_filtered (" \n");
+ printf_filtered (" 3. You may copy and distribute the Program (or a portion or derivative of\n");
+ printf_filtered ("it, under Paragraph 2) in object code or executable form under the terms of\n");
+ printf_filtered ("Paragraphs 1 and 2 above provided that you also do one of the following:\n");
+ printf_filtered ("\n");
+ printf_filtered (" a) accompany it with the complete corresponding machine-readable\n");
+ printf_filtered (" source code, which must be distributed under the terms of\n");
+ printf_filtered (" Paragraphs 1 and 2 above; or,\n");
+ printf_filtered ("\n");
+ printf_filtered (" b) accompany it with a written offer, valid for at least three\n");
+ printf_filtered (" years, to give any third party free (except for a nominal charge\n");
+ printf_filtered (" for the cost of distribution) a complete machine-readable copy of the\n");
+ printf_filtered (" corresponding source code, to be distributed under the terms of\n");
+ printf_filtered (" Paragraphs 1 and 2 above; or,\n");
+ printf_filtered ("\n");
+ printf_filtered (" c) accompany it with the information you received as to where the\n");
+ printf_filtered (" corresponding source code may be obtained. (This alternative is\n");
+ printf_filtered (" allowed only for noncommercial distribution and only if you\n");
+ printf_filtered (" received the program in object code or executable form alone.)\n");
+ printf_filtered ("\n");
+ printf_filtered ("Source code for a work means the preferred form of the work for making\n");
+ printf_filtered ("modifications to it. For an executable file, complete source code means\n");
+ printf_filtered ("all the source code for all modules it contains; but, as a special\n");
+ printf_filtered ("exception, it need not include source code for modules which are standard\n");
+ printf_filtered ("libraries that accompany the operating system on which the executable\n");
+ printf_filtered ("file runs, or for standard header files or definitions files that\n");
+ printf_filtered ("accompany that operating system.\n");
+ printf_filtered ("\n");
+ printf_filtered (" 4. You may not copy, modify, sublicense, distribute or transfer the\n");
+ printf_filtered ("Program except as expressly provided under this General Public License.\n");
+ printf_filtered ("Any attempt otherwise to copy, modify, sublicense, distribute or transfer\n");
+ printf_filtered ("the Program is void, and will automatically terminate your rights to use\n");
+ printf_filtered ("the Program under this License. However, parties who have received\n");
+ printf_filtered ("copies, or rights to use copies, from you under this General Public\n");
+ printf_filtered ("License will not have their licenses terminated so long as such parties\n");
+ printf_filtered ("remain in full compliance.\n");
+ printf_filtered ("\n");
+ printf_filtered (" 5. By copying, distributing or modifying the Program (or any work based\n");
+ printf_filtered ("on the Program) you indicate your acceptance of this license to do so,\n");
+ printf_filtered ("and all its terms and conditions.\n");
+ printf_filtered ("\n");
+ printf_filtered (" 6. Each time you redistribute the Program (or any work based on the\n");
+ printf_filtered ("Program), the recipient automatically receives a license from the original\n");
+ printf_filtered ("licensor to copy, distribute or modify the Program subject to these\n");
+ printf_filtered ("terms and conditions. You may not impose any further restrictions on the\n");
+ printf_filtered ("recipients' exercise of the rights granted herein.\n");
+ printf_filtered (" \n");
+ printf_filtered (" 7. The Free Software Foundation may publish revised and/or new versions\n");
+ printf_filtered ("of the General Public License from time to time. Such new versions will\n");
+ printf_filtered ("be similar in spirit to the present version, but may differ in detail to\n");
+ printf_filtered ("address new problems or concerns.\n");
+ printf_filtered ("\n");
+ printf_filtered ("Each version is given a distinguishing version number. If the Program\n");
+ printf_filtered ("specifies a version number of the license which applies to it and \"any\n");
+ printf_filtered ("later version\", you have the option of following the terms and conditions\n");
+ printf_filtered ("either of that version or of any later version published by the Free\n");
+ printf_filtered ("Software Foundation. If the Program does not specify a version number of\n");
+ printf_filtered ("the license, you may choose any version ever published by the Free Software\n");
+ printf_filtered ("Foundation.\n");
+ printf_filtered ("\n");
+ printf_filtered (" 8. If you wish to incorporate parts of the Program into other free\n");
+ printf_filtered ("programs whose distribution conditions are different, write to the author\n");
+ printf_filtered ("to ask for permission. For software which is copyrighted by the Free\n");
+ printf_filtered ("Software Foundation, write to the Free Software Foundation; we sometimes\n");
+ printf_filtered ("make exceptions for this. Our decision will be guided by the two goals\n");
+ printf_filtered ("of preserving the free status of all derivatives of our free software and\n");
+ printf_filtered ("of promoting the sharing and reuse of software generally.\n");
+ printf_filtered ("\n");
+ immediate_quit--;
+}
+
+static void
+warranty_info ()
+{
+ immediate_quit++;
+ printf_filtered (" NO WARRANTY\n");
+ printf_filtered ("\n");
+ printf_filtered (" 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\n");
+ printf_filtered ("FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN\n");
+ printf_filtered ("OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\n");
+ printf_filtered ("PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\n");
+ printf_filtered ("OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n");
+ printf_filtered ("MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS\n");
+ printf_filtered ("TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE\n");
+ printf_filtered ("PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\n");
+ printf_filtered ("REPAIR OR CORRECTION.\n");
+ printf_filtered ("\n");
+ printf_filtered (" 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\n");
+ printf_filtered ("WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\n");
+ printf_filtered ("REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\n");
+ printf_filtered ("INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\n");
+ printf_filtered ("OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\n");
+ printf_filtered ("TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\n");
+ printf_filtered ("YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\n");
+ printf_filtered ("PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\n");
+ printf_filtered ("POSSIBILITY OF SUCH DAMAGES.\n");
+ printf_filtered ("\n");
+ immediate_quit--;
+}
+
+void
+_initialize_copying ()
+{
+ add_info ("copying", copying_info,
+ "Conditions for redistributing copies of GDB.");
+ add_info ("warranty", warranty_info,
+ "Various kinds of warranty you do not have.");
+}
diff --git a/gnu/usr.bin/gdb/core.c b/gnu/usr.bin/gdb/core.c
new file mode 100644
index 0000000..307addb
--- /dev/null
+++ b/gnu/usr.bin/gdb/core.c
@@ -0,0 +1,581 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)core.c 6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Work with core dump and executable files, for GDB.
+ Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "frame.h" /* required by inferior.h */
+#include "inferior.h"
+
+#ifdef USG
+#include <sys/types.h>
+#include <fcntl.h>
+#endif
+
+#ifdef COFF_ENCAPSULATE
+#include "a.out.encap.h"
+#else
+#include <a.out.h>
+#endif
+#ifndef N_MAGIC
+#ifdef COFF_FORMAT
+#define N_MAGIC(exec) ((exec).magic)
+#else
+#define N_MAGIC(exec) ((exec).a_magic)
+#endif
+#endif
+#include <signal.h>
+#include <sys/param.h>
+#include <sys/dir.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#ifdef UMAX_CORE
+#include <sys/ptrace.h>
+#else
+#include <sys/user.h>
+#endif
+
+#ifndef N_TXTADDR
+#define N_TXTADDR(hdr) 0
+#endif /* no N_TXTADDR */
+
+#ifndef N_DATADDR
+#define N_DATADDR(hdr) hdr.a_text
+#endif /* no N_DATADDR */
+
+#ifndef COFF_FORMAT
+#ifndef AOUTHDR
+#define AOUTHDR struct exec
+#endif
+#endif
+
+extern char *sys_siglist[];
+
+extern core_file_command (), exec_file_command ();
+
+/* Hook for `exec_file_command' command to call. */
+
+void (*exec_file_display_hook) ();
+
+/* File names of core file and executable file. */
+
+char *corefile;
+char *execfile;
+
+/* Descriptors on which core file and executable file are open.
+ Note that the execchan is closed when an inferior is created
+ and reopened if the inferior dies or is killed. */
+
+int corechan;
+int execchan;
+
+/* Last modification time of executable file.
+ Also used in source.c to compare against mtime of a source file. */
+
+int exec_mtime;
+
+/* Virtual addresses of bounds of the two areas of memory in the core file. */
+
+CORE_ADDR data_start;
+CORE_ADDR data_end;
+CORE_ADDR stack_start;
+CORE_ADDR stack_end;
+
+#if defined (REG_STACK_SEGMENT)
+/* Start and end of the register stack segment. */
+CORE_ADDR reg_stack_start;
+CORE_ADDR reg_stack_end;
+#endif /* REG_STACK_SEGMENT */
+
+/* Virtual addresses of bounds of two areas of memory in the exec file.
+ Note that the data area in the exec file is used only when there is no core file. */
+
+CORE_ADDR text_start;
+CORE_ADDR text_end;
+
+CORE_ADDR exec_data_start;
+CORE_ADDR exec_data_end;
+
+/* Offset within executable file of start of text area data. */
+
+int text_offset;
+
+/* Offset within executable file of start of data area data. */
+
+int exec_data_offset;
+
+/* Offset within core file of start of data area data. */
+
+int data_offset;
+
+/* Offset within core file of start of stack area data. */
+
+int stack_offset;
+
+#ifdef COFF_FORMAT
+/* various coff data structures */
+
+FILHDR file_hdr;
+SCNHDR text_hdr;
+SCNHDR data_hdr;
+
+#endif /* not COFF_FORMAT */
+
+/* a.out header saved in core file. */
+
+AOUTHDR core_aouthdr;
+
+/* a.out header of exec file. */
+
+AOUTHDR exec_aouthdr;
+
+void validate_files ();
+unsigned int register_addr ();
+
+/* Call this to specify the hook for exec_file_command to call back.
+ This is called from the x-window display code. */
+
+void
+specify_exec_file_hook (hook)
+ void (*hook) ();
+{
+ exec_file_display_hook = hook;
+}
+
+/* The exec file must be closed before running an inferior.
+ If it is needed again after the inferior dies, it must
+ be reopened. */
+
+void
+close_exec_file ()
+{
+ if (execchan >= 0)
+ close (execchan);
+ execchan = -1;
+}
+
+void
+reopen_exec_file ()
+{
+ if (execchan < 0 && execfile != 0)
+ {
+ char *filename = concat (execfile, "", "");
+ exec_file_command (filename, 0);
+ free (filename);
+ }
+}
+
+/* If we have both a core file and an exec file,
+ print a warning if they don't go together.
+ This should really check that the core file came
+ from that exec file, but I don't know how to do it. */
+
+void
+validate_files ()
+{
+ if (execfile != 0 && corefile != 0)
+ {
+ struct stat st_core;
+
+ if (fstat (corechan, &st_core) < 0)
+ /* It might be a good idea to print an error message.
+ On the other hand, if the user tries to *do* anything with
+ the core file, (s)he'll find out soon enough. */
+ return;
+
+ if (N_MAGIC (core_aouthdr) != 0
+ && bcmp (&core_aouthdr, &exec_aouthdr, sizeof core_aouthdr))
+ printf ("Warning: core file does not match specified executable file.\n");
+ else if (exec_mtime > st_core.st_mtime) {
+#ifdef KERNELDEBUG
+ extern int kernel_debugging;
+ if (!kernel_debugging)
+#endif
+ printf ("Warning: exec file is newer than core file.\n");
+ }
+ }
+}
+
+/* Return the name of the executable file as a string.
+ ERR nonzero means get error if there is none specified;
+ otherwise return 0 in that case. */
+
+char *
+get_exec_file (err)
+ int err;
+{
+ if (err && execfile == 0)
+ error ("No executable file specified.\n\
+Use the \"exec-file\" and \"symbol-file\" commands.");
+ return execfile;
+}
+
+int
+have_core_file_p ()
+{
+ return corefile != 0;
+}
+
+static void
+files_info ()
+{
+ char *symfile;
+ extern char *get_sym_file ();
+
+ if (execfile)
+ printf ("Executable file \"%s\".\n", execfile);
+ else
+ printf ("No executable file\n");
+ if (corefile == 0)
+ printf ("No core dump file\n");
+ else
+ printf ("Core dump file \"%s\".\n", corefile);
+
+ if (have_inferior_p ())
+ printf ("Using the running image of the program, rather than these files.\n");
+
+ symfile = get_sym_file ();
+ if (symfile != 0)
+ printf ("Symbols from \"%s\".\n", symfile);
+
+#ifdef FILES_INFO_HOOK
+ if (FILES_INFO_HOOK ())
+ return;
+#endif
+
+ if (! have_inferior_p ())
+ {
+ if (execfile)
+ {
+ printf ("Text segment in executable from 0x%x to 0x%x.\n",
+ text_start, text_end);
+ printf ("Data segment in executable from 0x%x to 0x%x.\n",
+ exec_data_start, exec_data_end);
+ if (corefile)
+ printf ("(But since we have a core file, we're using...)\n");
+ }
+ if (corefile)
+ {
+ printf ("Data segment in core file from 0x%x to 0x%x.\n",
+ data_start, data_end);
+ printf ("Stack segment in core file from 0x%x to 0x%x.\n",
+ stack_start, stack_end);
+ }
+ }
+}
+
+/* Read "memory data" from core file and/or executable file.
+ Returns zero if successful, 1 if xfer_core_file failed, errno value if
+ ptrace failed. */
+
+int
+read_memory (memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+{
+ if (len == 0)
+ return 0;
+
+ if (have_inferior_p ())
+ {
+ if (remote_debugging)
+ return remote_read_inferior_memory (memaddr, myaddr, len);
+ else
+ return read_inferior_memory (memaddr, myaddr, len);
+ }
+ else
+ return xfer_core_file (memaddr, myaddr, len);
+}
+
+/* Write LEN bytes of data starting at address MYADDR
+ into debugged program memory at address MEMADDR.
+ Returns zero if successful, or an errno value if ptrace failed. */
+
+int
+write_memory (memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+{
+ if (have_inferior_p ())
+ {
+ if (remote_debugging)
+ return remote_write_inferior_memory (memaddr, myaddr, len);
+ else
+ return write_inferior_memory (memaddr, myaddr, len);
+ }
+ else
+ error ("Can write memory only when program being debugged is running.");
+}
+
+#ifndef XFER_CORE_FILE
+int (*core_file_hook)(); /* hook to handle special core files like
+ like /dev/mem and crash dumps */
+
+/* Read from the program's memory (except for inferior processes).
+ This function is misnamed, since it only reads, never writes; and
+ since it will use the core file and/or executable file as necessary.
+
+ It should be extended to write as well as read, FIXME, for patching files.
+
+ Return 0 if address could be read, 1 if not. */
+
+int
+xfer_core_file (memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+{
+ register int i;
+ register int val;
+ int xferchan;
+ char **xferfile;
+ int fileptr;
+ int returnval = 0;
+
+ if (core_file_hook)
+ return ((*core_file_hook)(memaddr, myaddr, len));
+
+ while (len > 0)
+ {
+ xferfile = 0;
+ xferchan = 0;
+
+ /* Determine which file the next bunch of addresses reside in,
+ and where in the file. Set the file's read/write pointer
+ to point at the proper place for the desired address
+ and set xferfile and xferchan for the correct file.
+
+ If desired address is nonexistent, leave them zero.
+
+ i is set to the number of bytes that can be handled
+ along with the next address.
+
+ We put the most likely tests first for efficiency. */
+
+ /* Note that if there is no core file
+ data_start and data_end are equal. */
+ if (memaddr >= data_start && memaddr < data_end)
+ {
+ i = min (len, data_end - memaddr);
+ fileptr = memaddr - data_start + data_offset;
+ xferfile = &corefile;
+ xferchan = corechan;
+ }
+ /* Note that if there is no core file
+ stack_start and stack_end are equal. */
+ else if (memaddr >= stack_start && memaddr < stack_end)
+ {
+ i = min (len, stack_end - memaddr);
+ fileptr = memaddr - stack_start + stack_offset;
+ xferfile = &corefile;
+ xferchan = corechan;
+ }
+#ifdef REG_STACK_SEGMENT
+ /* Pyramids have an extra segment in the virtual address space
+ for the (control) stack of register-window frames */
+ else if (memaddr >= reg_stack_start && memaddr < reg_stack_end)
+ {
+ i = min (len, reg_stack_end - memaddr);
+ fileptr = memaddr - reg_stack_start + reg_stack_offset;
+ xferfile = &corefile;
+ xferchan = corechan;
+ }
+#endif /* REG_STACK_SEGMENT */
+
+ else if (corechan < 0
+ && memaddr >= exec_data_start && memaddr < exec_data_end)
+ {
+ i = min (len, exec_data_end - memaddr);
+ fileptr = memaddr - exec_data_start + exec_data_offset;
+ xferfile = &execfile;
+ xferchan = execchan;
+ }
+ else if (memaddr >= text_start && memaddr < text_end)
+ {
+ i = min (len, text_end - memaddr);
+ fileptr = memaddr - text_start + text_offset;
+ xferfile = &execfile;
+ xferchan = execchan;
+ }
+ else if (memaddr < text_start)
+ {
+ i = min (len, text_start - memaddr);
+ }
+ else if (memaddr >= text_end
+ && memaddr < (corechan >= 0? data_start : exec_data_start))
+ {
+ i = min (len, data_start - memaddr);
+ }
+ else if (corechan >= 0
+ && memaddr >= data_end && memaddr < stack_start)
+ {
+ i = min (len, stack_start - memaddr);
+ }
+ else if (corechan < 0 && memaddr >= exec_data_end)
+ {
+ /* Since there is nothing at higher addresses than data
+ (without a core file or an inferior, there is no
+ stack, set i to do the rest of the operation now. */
+ i = len;
+ }
+#ifdef REG_STACK_SEGMENT
+ else if (memaddr >= reg_stack_end && reg_stack_end != 0)
+ {
+ i = min (len, reg_stack_start - memaddr);
+ }
+ else if (memaddr >= stack_end && memaddr < reg_stack_start)
+#else /* no REG_STACK_SEGMENT. */
+ else if (memaddr >= stack_end && stack_end != 0)
+#endif /* no REG_STACK_SEGMENT. */
+ {
+ /* Since there is nothing at higher addresses than
+ the stack, set i to do the rest of the operation now. */
+ i = len;
+ }
+ else
+ {
+ /* Address did not classify into one of the known ranges.
+ This shouldn't happen; we catch the endpoints. */
+ fatal ("Internal: Bad case logic in xfer_core_file.");
+ }
+
+ /* Now we know which file to use.
+ Set up its pointer and transfer the data. */
+ if (xferfile)
+ {
+ if (*xferfile == 0)
+ if (xferfile == &execfile)
+ error ("No program file to examine.");
+ else
+ error ("No core dump file or running program to examine.");
+ val = lseek (xferchan, fileptr, 0);
+ if (val == -1)
+ perror_with_name (*xferfile);
+ val = myread (xferchan, myaddr, i);
+ if (val < 0)
+ perror_with_name (*xferfile);
+ }
+ /* If this address is for nonexistent memory,
+ read zeros if reading, or do nothing if writing.
+ Actually, we never right. */
+ else
+ {
+ bzero (myaddr, i);
+ returnval = 1;
+ }
+
+ memaddr += i;
+ myaddr += i;
+ len -= i;
+ }
+ return returnval;
+}
+#endif /* XFER_CORE_FILE */
+
+/* My replacement for the read system call.
+ Used like `read' but keeps going if `read' returns too soon. */
+
+int
+myread (desc, addr, len)
+ int desc;
+ char *addr;
+ int len;
+{
+ register int val;
+ int orglen = len;
+
+ while (len > 0)
+ {
+ val = read (desc, addr, len);
+ if (val < 0)
+ return val;
+ if (val == 0)
+ return orglen - len;
+ len -= val;
+ addr += val;
+ }
+ return orglen;
+}
+
+#ifdef REGISTER_U_ADDR
+
+/* Return the address in the core dump or inferior of register REGNO.
+ BLOCKEND is the address of the end of the user structure. */
+
+unsigned int
+register_addr (regno, blockend)
+ int regno;
+ int blockend;
+{
+ int addr;
+
+ if (regno < 0 || regno >= NUM_REGS)
+ error ("Invalid register number %d.", regno);
+
+ REGISTER_U_ADDR (addr, blockend, regno);
+
+ return addr;
+}
+
+#endif /* REGISTER_U_ADDR */
+
+void
+_initialize_core()
+{
+ corechan = -1;
+ execchan = -1;
+ corefile = 0;
+ execfile = 0;
+ exec_file_display_hook = 0;
+
+ text_start = 0;
+ text_end = 0;
+ data_start = 0;
+ data_end = 0;
+ exec_data_start = 0;
+ exec_data_end = 0;
+ stack_start = STACK_END_ADDR;
+ stack_end = STACK_END_ADDR;
+
+ add_com ("core-file", class_files, core_file_command,
+ "Use FILE as core dump for examining memory and registers.\n\
+No arg means have no core file.");
+ add_com ("exec-file", class_files, exec_file_command,
+ "Use FILE as program for getting contents of pure memory.\n\
+If FILE cannot be found as specified, your execution directory path\n\
+is searched for a command of that name.\n\
+No arg means have no executable file.");
+ add_info ("files", files_info, "Names of files being debugged.");
+}
+
diff --git a/gnu/usr.bin/gdb/cplus-dem.c b/gnu/usr.bin/gdb/cplus-dem.c
new file mode 100644
index 0000000..8ea9c8b
--- /dev/null
+++ b/gnu/usr.bin/gdb/cplus-dem.c
@@ -0,0 +1,996 @@
+/* Demangler for GNU C++
+ Copyright (C) 1989 Free Software Foundation, Inc.
+ written by James Clark (jjc@jclark.uucp)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This is for g++ 1.36.1 (November 6 version). It will probably
+ require changes for any other version.
+
+ Modified for g++ 1.36.2 (November 18 version). */
+
+/* This file exports one function
+
+ char *cplus_demangle (const char *name, int mode)
+
+ If NAME is a mangled function name produced by GNU C++, then
+ a pointer to a malloced string giving a C++ representation
+ of the name will be returned; otherwise NULL will be returned.
+ It is the caller's responsibility to free the string which
+ is returned.
+
+ If MODE > 0, then ANSI qualifiers such as `const' and `void' are output.
+ Otherwise they are not.
+ If MODE >= 0, parameters are emitted; otherwise not.
+
+ For example,
+
+ cplus_demangle ("foo__1Ai", 0) => "A::foo(int)"
+ cplus_demangle ("foo__1Ai", 1) => "A::foo(int)"
+ cplus_demangle ("foo__1Ai", -1) => "A::foo"
+
+ cplus_demangle ("foo__1Afe", 0) => "A::foo(float,...)"
+ cplus_demangle ("foo__1Afe", 1) => "A::foo(float,...)"
+ cplus_demangle ("foo__1Afe", -1) => "A::foo"
+
+ This file imports xmalloc and xrealloc, which are like malloc and
+ realloc except that they generate a fatal error if there is no
+ available memory. */
+
+/* #define nounderscore 1 /* define this is names don't start with _ */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef USG
+#include <memory.h>
+#include <string.h>
+#else
+#include <strings.h>
+#define memcpy(s1, s2, n) bcopy ((s2), (s1), (n))
+#define memcmp(s1, s2, n) bcmp ((s2), (s1), (n))
+#define strchr index
+#define strrchr rindex
+#endif
+
+#ifndef __STDC__
+#define const
+#endif
+
+#ifdef __STDC__
+extern char *cplus_demangle (const char *type, int mode);
+#else
+extern char *cplus_demangle ();
+#endif
+
+#ifdef __STDC__
+extern char *xmalloc (int);
+extern char *xrealloc (char *, int);
+#else
+extern char *xmalloc ();
+extern char *xrealloc ();
+#endif
+
+static char **typevec = 0;
+static int ntypes = 0;
+static int typevec_size = 0;
+
+static struct {
+ const char *in;
+ const char *out;
+} optable[] = {
+ "new", " new",
+ "delete", " delete",
+ "ne", "!=",
+ "eq", "==",
+ "ge", ">=",
+ "gt", ">",
+ "le", "<=",
+ "lt", "<",
+ "plus", "+",
+ "minus", "-",
+ "mult", "*",
+ "convert", "+", /* unary + */
+ "negate", "-", /* unary - */
+ "trunc_mod", "%",
+ "trunc_div", "/",
+ "truth_andif", "&&",
+ "truth_orif", "||",
+ "truth_not", "!",
+ "postincrement", "++",
+ "postdecrement", "--",
+ "bit_ior", "|",
+ "bit_xor", "^",
+ "bit_and", "&",
+ "bit_not", "~",
+ "call", "()",
+ "cond", "?:",
+ "alshift", "<<",
+ "arshift", ">>",
+ "component", "->",
+ "indirect", "*",
+ "method_call", "->()",
+ "addr", "&", /* unary & */
+ "array", "[]",
+ "nop", "", /* for operator= */
+};
+
+/* Beware: these aren't '\0' terminated. */
+
+typedef struct {
+ char *b; /* pointer to start of string */
+ char *p; /* pointer after last character */
+ char *e; /* pointer after end of allocated space */
+} string;
+
+#ifdef __STDC__
+static void string_need (string *s, int n);
+static void string_delete (string *s);
+static void string_init (string *s);
+static void string_clear (string *s);
+static int string_empty (string *s);
+static void string_append (string *p, const char *s);
+static void string_appends (string *p, string *s);
+static void string_appendn (string *p, const char *s, int n);
+static void string_prepend (string *p, const char *s);
+#if 0
+static void string_prepends (string *p, string *s);
+#endif
+static void string_prependn (string *p, const char *s, int n);
+static int get_count (const char **type, int *count);
+static int do_args (const char **type, string *decl, int arg_mode);
+static int do_type (const char **type, string *result, int arg_mode);
+static int do_arg (const char **type, string *result, int arg_mode);
+static void munge_function_name (string *name, int arg_mode);
+static void remember_type (const char *type, int len);
+#else
+static void string_need ();
+static void string_delete ();
+static void string_init ();
+static void string_clear ();
+static int string_empty ();
+static void string_append ();
+static void string_appends ();
+static void string_appendn ();
+static void string_prepend ();
+static void string_prepends ();
+static void string_prependn ();
+static int get_count ();
+static int do_args ();
+static int do_type ();
+static int do_arg ();
+static int do_args ();
+static void munge_function_name ();
+static void remember_type ();
+#endif
+
+char *
+cplus_demangle (type, arg_mode)
+ const char *type;
+ int arg_mode;
+{
+ string decl;
+ int n;
+ int success = 0;
+ int constructor = 0;
+ int const_flag = 0;
+ int i;
+ const char *p;
+#ifndef LONGERNAMES
+ const char *premangle;
+#endif
+
+# define print_ansi_qualifiers (arg_mode > 0)
+# define print_arg_types (arg_mode >= 0)
+
+ if (type == NULL || *type == '\0')
+ return NULL;
+#ifndef nounderscore
+ if (*type++ != '_')
+ return NULL;
+#endif
+ p = type;
+ while (*p != '\0' && !(*p == '_' && p[1] == '_'))
+ p++;
+ if (*p == '\0')
+ {
+ /* destructor */
+ if (type[0] == '_' && type[1] == '$' && type[2] == '_')
+ {
+ int n = (strlen (type) - 3)*2 + 3 + 2 + 1;
+ char *tem = (char *) xmalloc (n);
+ strcpy (tem, type + 3);
+ strcat (tem, "::~");
+ strcat (tem, type + 3);
+ strcat (tem, "()");
+ return tem;
+ }
+ /* static data member */
+ if (*type != '_' && (p = strchr (type, '$')) != NULL)
+ {
+ int n = strlen (type) + 2;
+ char *tem = (char *) xmalloc (n);
+ memcpy (tem, type, p - type);
+ strcpy (tem + (p - type), "::");
+ strcpy (tem + (p - type) + 2, p + 1);
+ return tem;
+ }
+ /* virtual table "_vt$" */
+ if (type[0] == '_' && type[1] == 'v' && type[2] == 't' && type[3] == '$')
+ {
+ int n = strlen (type + 4) + 14 + 1;
+ char *tem = (char *) xmalloc (n);
+ strcpy (tem, type + 4);
+ strcat (tem, " virtual table");
+ return tem;
+ }
+ return NULL;
+ }
+
+ string_init (&decl);
+
+ if (p == type)
+ {
+ if (!isdigit (p[2]))
+ {
+ string_delete (&decl);
+ return NULL;
+ }
+ constructor = 1;
+ }
+ else
+ {
+ string_appendn (&decl, type, p - type);
+ munge_function_name (&decl, arg_mode);
+ }
+ p += 2;
+
+#ifndef LONGERNAMES
+ premangle = p;
+#endif
+ switch (*p)
+ {
+ case 'C':
+ /* a const member function */
+ if (!isdigit (p[1]))
+ {
+ string_delete (&decl);
+ return NULL;
+ }
+ p += 1;
+ const_flag = 1;
+ /* fall through */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ n = 0;
+ do
+ {
+ n *= 10;
+ n += *p - '0';
+ p += 1;
+ }
+ while (isdigit (*p));
+ if (strlen (p) < n)
+ {
+ string_delete (&decl);
+ return NULL;
+ }
+ if (constructor)
+ {
+ string_appendn (&decl, p, n);
+ string_append (&decl, "::");
+ string_appendn (&decl, p, n);
+ }
+ else
+ {
+ string_prepend (&decl, "::");
+ string_prependn (&decl, p, n);
+ }
+ p += n;
+#ifndef LONGERNAMES
+ remember_type (premangle, p - premangle);
+#endif
+ success = do_args (&p, &decl, arg_mode);
+ if (const_flag && print_arg_types)
+ string_append (&decl, " const");
+ break;
+ case 'F':
+ p += 1;
+ success = do_args (&p, &decl, arg_mode);
+ break;
+ }
+
+ for (i = 0; i < ntypes; i++)
+ if (typevec[i] != NULL)
+ free (typevec[i]);
+ ntypes = 0;
+ if (typevec != NULL)
+ {
+ free ((char *)typevec);
+ typevec = NULL;
+ typevec_size = 0;
+ }
+
+ if (success)
+ {
+ string_appendn (&decl, "", 1);
+ return decl.b;
+ }
+ else
+ {
+ string_delete (&decl);
+ return NULL;
+ }
+}
+
+static int
+get_count (type, count)
+ const char **type;
+ int *count;
+{
+ if (!isdigit (**type))
+ return 0;
+ *count = **type - '0';
+ *type += 1;
+ /* see flush_repeats in cplus-method.c */
+ if (isdigit (**type))
+ {
+ const char *p = *type;
+ int n = *count;
+ do
+ {
+ n *= 10;
+ n += *p - '0';
+ p += 1;
+ }
+ while (isdigit (*p));
+ if (*p == '_')
+ {
+ *type = p + 1;
+ *count = n;
+ }
+ }
+ return 1;
+}
+
+/* result will be initialised here; it will be freed on failure */
+
+static int
+do_type (type, result, arg_mode)
+ const char **type;
+ string *result;
+ int arg_mode;
+{
+ int n;
+ int done;
+ int non_empty = 0;
+ int success;
+ string decl;
+ const char *remembered_type;
+
+ string_init (&decl);
+ string_init (result);
+
+ done = 0;
+ success = 1;
+ while (success && !done)
+ {
+ int member;
+ switch (**type)
+ {
+ case 'P':
+ *type += 1;
+ string_prepend (&decl, "*");
+ break;
+
+ case 'R':
+ *type += 1;
+ string_prepend (&decl, "&");
+ break;
+
+ case 'T':
+ *type += 1;
+ if (!get_count (type, &n) || n >= ntypes)
+ success = 0;
+ else
+ {
+ remembered_type = typevec[n];
+ type = &remembered_type;
+ }
+ break;
+
+ case 'F':
+ *type += 1;
+ if (!string_empty (&decl) && decl.b[0] == '*')
+ {
+ string_prepend (&decl, "(");
+ string_append (&decl, ")");
+ }
+ if (!do_args (type, &decl, arg_mode) || **type != '_')
+ success = 0;
+ else
+ *type += 1;
+ break;
+
+ case 'M':
+ case 'O':
+ {
+ int constp = 0;
+ int volatilep = 0;
+
+ member = **type == 'M';
+ *type += 1;
+ if (!isdigit (**type))
+ {
+ success = 0;
+ break;
+ }
+ n = 0;
+ do
+ {
+ n *= 10;
+ n += **type - '0';
+ *type += 1;
+ }
+ while (isdigit (**type));
+ if (strlen (*type) < n)
+ {
+ success = 0;
+ break;
+ }
+ string_append (&decl, ")");
+ string_prepend (&decl, "::");
+ string_prependn (&decl, *type, n);
+ string_prepend (&decl, "(");
+ *type += n;
+ if (member)
+ {
+ if (**type == 'C')
+ {
+ *type += 1;
+ constp = 1;
+ }
+ if (**type == 'V')
+ {
+ *type += 1;
+ volatilep = 1;
+ }
+ if (*(*type)++ != 'F')
+ {
+ success = 0;
+ break;
+ }
+ }
+ if ((member && !do_args (type, &decl, arg_mode)) || **type != '_')
+ {
+ success = 0;
+ break;
+ }
+ *type += 1;
+ if (! print_ansi_qualifiers)
+ break;
+ if (constp)
+ {
+ if (non_empty)
+ string_append (&decl, " ");
+ else
+ non_empty = 1;
+ string_append (&decl, "const");
+ }
+ if (volatilep)
+ {
+ if (non_empty)
+ string_append (&decl, " ");
+ else
+ non_empty = 1;
+ string_append (&decl, "volatile");
+ }
+ break;
+ }
+
+ case 'C':
+ if ((*type)[1] == 'P')
+ {
+ *type += 1;
+ if (print_ansi_qualifiers)
+ {
+ if (!string_empty (&decl))
+ string_prepend (&decl, " ");
+ string_prepend (&decl, "const");
+ }
+ break;
+ }
+
+ /* fall through */
+ default:
+ done = 1;
+ break;
+ }
+ }
+
+ done = 0;
+ non_empty = 0;
+ while (success && !done)
+ {
+ switch (**type)
+ {
+ case 'C':
+ *type += 1;
+ if (print_ansi_qualifiers)
+ {
+ if (non_empty)
+ string_append (result, " ");
+ else
+ non_empty = 1;
+ string_append (result, "const");
+ }
+ break;
+ case 'U':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ else
+ non_empty = 1;
+ string_append (result, "unsigned");
+ break;
+ case 'V':
+ *type += 1;
+ if (print_ansi_qualifiers)
+ {
+ if (non_empty)
+ string_append (result, " ");
+ else
+ non_empty = 1;
+ string_append (result, "volatile");
+ }
+ break;
+ default:
+ done = 1;
+ break;
+ }
+ }
+
+ if (success)
+ switch (**type)
+ {
+ case '\0':
+ case '_':
+ break;
+ case 'v':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "void");
+ break;
+ case 'x':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "long long");
+ break;
+ case 'l':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "long");
+ break;
+ case 'i':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "int");
+ break;
+ case 's':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "short");
+ break;
+ case 'c':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "char");
+ break;
+ case 'r':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "long double");
+ break;
+ case 'd':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "double");
+ break;
+ case 'f':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "float");
+ break;
+ case 'G':
+ *type += 1;
+ if (!isdigit (**type))
+ {
+ success = 0;
+ break;
+ }
+ /* fall through */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ n = 0;
+ do
+ {
+ n *= 10;
+ n += **type - '0';
+ *type += 1;
+ }
+ while (isdigit (**type));
+ if (strlen (*type) < n)
+ {
+ success = 0;
+ break;
+ }
+ if (non_empty)
+ string_append (result, " ");
+ string_appendn (result, *type, n);
+ *type += n;
+ break;
+ default:
+ success = 0;
+ break;
+ }
+
+ if (success)
+ {
+ if (!string_empty (&decl))
+ {
+ string_append (result, " ");
+ string_appends (result, &decl);
+ }
+ string_delete (&decl);
+ return 1;
+ }
+ else
+ {
+ string_delete (&decl);
+ string_delete (result);
+ return 0;
+ }
+}
+
+/* `result' will be initialised in do_type; it will be freed on failure */
+
+static int
+do_arg (type, result, arg_mode)
+ const char **type;
+ string *result;
+ int arg_mode;
+{
+ const char *start = *type;
+
+ if (!do_type (type, result, arg_mode))
+ return 0;
+ remember_type (start, *type - start);
+ return 1;
+}
+
+static void
+remember_type (start, len)
+ const char *start;
+ int len;
+{
+ char *tem;
+
+ if (ntypes >= typevec_size)
+ {
+ if (typevec_size == 0)
+ {
+ typevec_size = 3;
+ typevec = (char **) xmalloc (sizeof (char*)*typevec_size);
+ }
+ else
+ {
+ typevec_size *= 2;
+ typevec = (char **) xrealloc ((char *)typevec, sizeof (char*)*typevec_size);
+ }
+ }
+ tem = (char *) xmalloc (len + 1);
+ memcpy (tem, start, len);
+ tem[len] = '\0';
+ typevec[ntypes++] = tem;
+}
+
+/* `decl' must be already initialised, usually non-empty;
+ it won't be freed on failure */
+
+static int
+do_args (type, decl, arg_mode)
+ const char **type;
+ string *decl;
+ int arg_mode;
+{
+ string arg;
+ int need_comma = 0;
+
+ if (print_arg_types)
+ string_append (decl, "(");
+
+ while (**type != '_' && **type != '\0' && **type != 'e' && **type != 'v')
+ {
+ if (**type == 'N')
+ {
+ int r;
+ int t;
+ *type += 1;
+ if (!get_count (type, &r) || !get_count (type, &t) || t >= ntypes)
+ return 0;
+ while (--r >= 0)
+ {
+ const char *tem = typevec[t];
+ if (need_comma && print_arg_types)
+ string_append (decl, ", ");
+ if (!do_arg (&tem, &arg, arg_mode))
+ return 0;
+ if (print_arg_types)
+ string_appends (decl, &arg);
+ string_delete (&arg);
+ need_comma = 1;
+ }
+ }
+ else
+ {
+ if (need_comma & print_arg_types)
+ string_append (decl, ", ");
+ if (!do_arg (type, &arg, arg_mode))
+ return 0;
+ if (print_arg_types)
+ string_appends (decl, &arg);
+ string_delete (&arg);
+ need_comma = 1;
+ }
+ }
+
+ if (**type == 'v')
+ *type += 1;
+ else if (**type == 'e')
+ {
+ *type += 1;
+ if (print_arg_types)
+ {
+ if (need_comma)
+ string_append (decl, ",");
+ string_append (decl, "...");
+ }
+ }
+
+ if (print_arg_types)
+ string_append (decl, ")");
+ return 1;
+}
+
+static void
+munge_function_name (name, arg_mode)
+ string *name;
+ int arg_mode;
+{
+ if (!string_empty (name) && name->p - name->b >= 3
+ && name->b[0] == 'o' && name->b[1] == 'p' && name->b[2] == '$')
+ {
+ int i;
+ /* see if it's an assignment expression */
+ if (name->p - name->b >= 10 /* op$assign_ */
+ && memcmp (name->b + 3, "assign_", 7) == 0)
+ {
+ for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++)
+ {
+ int len = name->p - name->b - 10;
+ if (strlen (optable[i].in) == len
+ && memcmp (optable[i].in, name->b + 10, len) == 0)
+ {
+ string_clear (name);
+ string_append (name, "operator");
+ string_append (name, optable[i].out);
+ string_append (name, "=");
+ return;
+ }
+ }
+ }
+ else
+ {
+ for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++)
+ {
+ int len = name->p - name->b - 3;
+ if (strlen (optable[i].in) == len
+ && memcmp (optable[i].in, name->b + 3, len) == 0)
+ {
+ string_clear (name);
+ string_append (name, "operator");
+ string_append (name, optable[i].out);
+ return;
+ }
+ }
+ }
+ return;
+ }
+ else if (!string_empty (name) && name->p - name->b >= 5
+ && memcmp (name->b, "type$", 5) == 0)
+ {
+ /* type conversion operator */
+ string type;
+ const char *tem = name->b + 5;
+ if (do_type (&tem, &type, arg_mode))
+ {
+ string_clear (name);
+ string_append (name, "operator ");
+ string_appends (name, &type);
+ string_delete (&type);
+ return;
+ }
+ }
+}
+
+/* a mini string-handling package */
+
+static void
+string_need (s, n)
+ string *s;
+ int n;
+{
+ if (s->b == NULL)
+ {
+ if (n < 32)
+ n = 32;
+ s->p = s->b = (char *) xmalloc (n);
+ s->e = s->b + n;
+ }
+ else if (s->e - s->p < n)
+ {
+ int tem = s->p - s->b;
+ n += tem;
+ n *= 2;
+ s->b = (char *) xrealloc (s->b, n);
+ s->p = s->b + tem;
+ s->e = s->b + n;
+ }
+}
+
+static void
+string_delete (s)
+ string *s;
+{
+ if (s->b != NULL)
+ {
+ free (s->b);
+ s->b = s->e = s->p = NULL;
+ }
+}
+
+static void
+string_init (s)
+ string *s;
+{
+ s->b = s->p = s->e = NULL;
+}
+
+static void
+string_clear (s)
+ string *s;
+{
+ s->p = s->b;
+}
+
+static int
+string_empty (s)
+ string *s;
+{
+ return s->b == s->p;
+}
+
+static void
+string_append (p, s)
+ string *p;
+ const char *s;
+{
+ int n;
+ if (s == NULL || *s == '\0')
+ return;
+ n = strlen (s);
+ string_need (p, n);
+ memcpy (p->p, s, n);
+ p->p += n;
+}
+
+static void
+string_appends (p, s)
+ string *p, *s;
+{
+ int n;
+ if (s->b == s->p)
+ return;
+ n = s->p - s->b;
+ string_need (p, n);
+ memcpy (p->p, s->b, n);
+ p->p += n;
+}
+
+static void
+string_appendn (p, s, n)
+ string *p;
+ const char *s;
+ int n;
+{
+ if (n == 0)
+ return;
+ string_need (p, n);
+ memcpy (p->p, s, n);
+ p->p += n;
+}
+
+static void
+string_prepend (p, s)
+ string *p;
+ const char *s;
+{
+ if (s == NULL || *s == '\0')
+ return;
+ string_prependn (p, s, strlen (s));
+}
+
+#if 0
+static void
+string_prepends (p, s)
+ string *p, *s;
+{
+ if (s->b == s->p)
+ return;
+ string_prependn (p, s->b, s->p - s->b);
+}
+#endif
+
+static void
+string_prependn (p, s, n)
+ string *p;
+ const char *s;
+ int n;
+{
+ char *q;
+
+ if (n == 0)
+ return;
+ string_need (p, n);
+ for (q = p->p - 1; q >= p->b; q--)
+ q[n] = q[0];
+ memcpy (p->b, s, n);
+ p->p += n;
+}
diff --git a/gnu/usr.bin/gdb/dbxread.c b/gnu/usr.bin/gdb/dbxread.c
new file mode 100644
index 0000000..7a25665
--- /dev/null
+++ b/gnu/usr.bin/gdb/dbxread.c
@@ -0,0 +1,5727 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dbxread.c 6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Read dbx symbol tables and convert to internal format, for GDB.
+ Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Symbol read-in occurs in two phases:
+ 1. A scan (read_dbx_symtab()) of the entire executable, whose sole
+ purpose is to make a list of symbols (partial symbol table)
+ which will cause symbols
+ to be read in if referenced. This scan happens when the
+ "symbol-file" command is given (symbol_file_command()).
+ 2. Full read-in of symbols. (psymtab_to_symtab()). This happens
+ when a symbol in a file for which symbols have not yet been
+ read in is referenced.
+ 2a. The "add-file" command. Similar to #2. */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+
+#ifdef READ_DBX_FORMAT
+
+#ifdef USG
+#include <sys/types.h>
+#include <fcntl.h>
+#define L_SET 0
+#define L_INCR 1
+#endif
+
+#ifdef COFF_ENCAPSULATE
+#include "a.out.encap.h"
+#include "stab.gnu.h"
+#else
+#include <a.out.h>
+#include <stab.h>
+#endif
+#include <ctype.h>
+
+#ifndef NO_GNU_STABS
+/*
+ * Define specifically gnu symbols here.
+ */
+
+/* The following type indicates the definition of a symbol as being
+ an indirect reference to another symbol. The other symbol
+ appears as an undefined reference, immediately following this symbol.
+
+ Indirection is asymmetrical. The other symbol's value will be used
+ to satisfy requests for the indirect symbol, but not vice versa.
+ If the other symbol does not have a definition, libraries will
+ be searched to find a definition. */
+#ifndef N_INDR
+#define N_INDR 0xa
+#endif
+
+/* The following symbols refer to set elements.
+ All the N_SET[ATDB] symbols with the same name form one set.
+ Space is allocated for the set in the text section, and each set
+ element's value is stored into one word of the space.
+ The first word of the space is the length of the set (number of elements).
+
+ The address of the set is made into an N_SETV symbol
+ whose name is the same as the name of the set.
+ This symbol acts like a N_DATA global symbol
+ in that it can satisfy undefined external references. */
+
+#ifndef N_SETA
+#define N_SETA 0x14 /* Absolute set element symbol */
+#endif /* This is input to LD, in a .o file. */
+
+#ifndef N_SETT
+#define N_SETT 0x16 /* Text set element symbol */
+#endif /* This is input to LD, in a .o file. */
+
+#ifndef N_SETD
+#define N_SETD 0x18 /* Data set element symbol */
+#endif /* This is input to LD, in a .o file. */
+
+#ifndef N_SETB
+#define N_SETB 0x1A /* Bss set element symbol */
+#endif /* This is input to LD, in a .o file. */
+
+/* Macros dealing with the set element symbols defined in a.out.h */
+#define SET_ELEMENT_P(x) ((x)>=N_SETA&&(x)<=(N_SETB|N_EXT))
+#define TYPE_OF_SET_ELEMENT(x) ((x)-N_SETA+N_ABS)
+
+#ifndef N_SETV
+#define N_SETV 0x1C /* Pointer to set vector in data area. */
+#endif /* This is output from LD. */
+
+#ifndef N_WARNING
+#define N_WARNING 0x1E /* Warning message to print if file included */
+#endif /* This is input to ld */
+
+#ifndef __GNU_STAB__
+
+/* Line number for the data section. This is to be used to describe
+ the source location of a variable declaration. */
+#ifndef N_DSLINE
+#define N_DSLINE (N_SLINE+N_DATA-N_TEXT)
+#endif
+
+/* Line number for the bss section. This is to be used to describe
+ the source location of a variable declaration. */
+#ifndef N_BSLINE
+#define N_BSLINE (N_SLINE+N_BSS-N_TEXT)
+#endif
+
+#endif /* not __GNU_STAB__ */
+#endif /* NO_GNU_STABS */
+
+#include <obstack.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include "symtab.h"
+
+#ifndef COFF_FORMAT
+#ifndef AOUTHDR
+#define AOUTHDR struct exec
+#endif
+#endif
+
+static void add_symbol_to_list ();
+static void read_dbx_symtab ();
+static void process_one_symbol ();
+static void free_all_psymbols ();
+static struct type *read_type ();
+static struct type *read_range_type ();
+static struct type *read_enum_type ();
+static struct type *read_struct_type ();
+static struct type *read_array_type ();
+static long read_number ();
+static void read_huge_number ();
+static void finish_block ();
+static struct blockvector *make_blockvector ();
+static struct symbol *define_symbol ();
+static void start_subfile ();
+static int hashname ();
+static void hash_symsegs ();
+static struct pending *copy_pending ();
+static void fix_common_block ();
+
+static void add_undefined_type ();
+static void cleanup_undefined_types ();
+
+extern char *index();
+
+extern struct symtab *read_symsegs ();
+extern void free_all_symtabs ();
+extern void free_all_psymtabs ();
+extern void free_inclink_symtabs ();
+
+/* C++ */
+static struct type **read_args ();
+
+/* Macro to determine which symbols to ignore when reading the first symbol
+ of a file. Some machines override this definition. */
+#ifdef N_NSYMS
+#ifndef IGNORE_SYMBOL
+/* This code is used on Ultrix systems. Ignore it */
+#define IGNORE_SYMBOL(type) (type == N_NSYMS)
+#endif
+#else
+#ifndef IGNORE_SYMBOL
+/* Don't ignore any symbols. */
+#define IGNORE_SYMBOL(type) (0)
+#endif
+#endif /* not N_NSYMS */
+
+/* Macro for number of symbol table entries (in usual a.out format).
+ Some machines override this definition. */
+#ifndef NUMBER_OF_SYMBOLS
+#ifdef COFF_HEADER
+#define NUMBER_OF_SYMBOLS \
+ ((COFF_HEADER(hdr) ? hdr.coffhdr.filehdr.f_nsyms : hdr.a_syms) / \
+ sizeof (struct nlist))
+#else
+#define NUMBER_OF_SYMBOLS (hdr.a_syms / sizeof (struct nlist))
+#endif
+#endif
+
+/* Macro for file-offset of symbol table (in usual a.out format). */
+#ifndef SYMBOL_TABLE_OFFSET
+#define SYMBOL_TABLE_OFFSET N_SYMOFF (hdr)
+#endif
+
+/* Macro for file-offset of string table (in usual a.out format). */
+#ifndef STRING_TABLE_OFFSET
+#define STRING_TABLE_OFFSET (N_SYMOFF (hdr) + hdr.a_syms)
+#endif
+
+/* Macro to store the length of the string table data in INTO. */
+#ifndef READ_STRING_TABLE_SIZE
+#define READ_STRING_TABLE_SIZE(INTO) \
+{ val = myread (desc, &INTO, sizeof INTO); \
+ if (val < 0) perror_with_name (name); }
+#endif
+
+/* Macro to declare variables to hold the file's header data. */
+#ifndef DECLARE_FILE_HEADERS
+#define DECLARE_FILE_HEADERS AOUTHDR hdr
+#endif
+
+/* Macro to read the header data from descriptor DESC and validate it.
+ NAME is the file name, for error messages. */
+#ifndef READ_FILE_HEADERS
+#ifdef HEADER_SEEK_FD
+#define READ_FILE_HEADERS(DESC, NAME) \
+{ HEADER_SEEK_FD (DESC); \
+ val = myread (DESC, &hdr, sizeof hdr); \
+ if (val < 0) perror_with_name (NAME); \
+ if (N_BADMAG (hdr)) \
+ error ("File \"%s\" not in executable format.", NAME); }
+#else
+#define READ_FILE_HEADERS(DESC, NAME) \
+{ val = myread (DESC, &hdr, sizeof hdr); \
+ if (val < 0) perror_with_name (NAME); \
+ if (N_BADMAG (hdr)) \
+ error ("File \"%s\" not in executable format.", NAME); }
+#endif
+#endif
+
+/* Non-zero if this is an object (.o) file, rather than an executable.
+ Distinguishing between the two is rarely necessary (and seems like
+ a hack, but there is no other way to do ADDR_OF_TEXT_SEGMENT
+ right for SunOS). */
+#if !defined (IS_OBJECT_FILE)
+/* This will not work
+ if someone decides to make ld preserve relocation info. */
+#define IS_OBJECT_FILE (hdr.a_trsize != 0)
+#endif
+
+/* Macro for size of text segment */
+#ifndef SIZE_OF_TEXT_SEGMENT
+#define SIZE_OF_TEXT_SEGMENT hdr.a_text
+#endif
+
+/* Get the address in debugged memory of the start
+ of the text segment. */
+#if !defined (ADDR_OF_TEXT_SEGMENT)
+#if defined (N_TXTADDR)
+#define ADDR_OF_TEXT_SEGMENT (IS_OBJECT_FILE ? 0 : N_TXTADDR (hdr))
+#else /* no N_TXTADDR */
+#define ADDR_OF_TEXT_SEGMENT 0
+#endif /* no N_TXTADDR */
+#endif /* no ADDR_OF_TEXT_SEGMENT */
+
+/* Macro to get entry point from headers. */
+#ifndef ENTRY_POINT
+#define ENTRY_POINT hdr.a_entry
+#endif
+
+/* Macro for name of symbol to indicate a file compiled with gcc. */
+#ifndef GCC_COMPILED_FLAG_SYMBOL
+#define GCC_COMPILED_FLAG_SYMBOL "gcc_compiled."
+#endif
+
+/* Convert stab register number (from `r' declaration) to a gdb REGNUM. */
+
+#ifndef STAB_REG_TO_REGNUM
+#define STAB_REG_TO_REGNUM(VALUE) (VALUE)
+#endif
+
+/* Define this as 1 if a pcc declaration of a char or short argument
+ gives the correct address. Otherwise assume pcc gives the
+ address of the corresponding int, which is not the same on a
+ big-endian machine. */
+
+#ifndef BELIEVE_PCC_PROMOTION
+#define BELIEVE_PCC_PROMOTION 0
+#endif
+
+/* Nonzero means give verbose info on gdb action. From main.c. */
+extern int info_verbose;
+
+/* Chain of symtabs made from reading the file's symsegs.
+ These symtabs do not go into symtab_list themselves,
+ but the information is copied from them when appropriate
+ to make the symtabs that will exist permanently. */
+
+static struct symtab *symseg_chain;
+
+/* Symseg symbol table for the file whose data we are now processing.
+ It is one of those in symseg_chain. Or 0, for a compilation that
+ has no symseg. */
+
+static struct symtab *current_symseg;
+
+/* Name of source file whose symbol data we are now processing.
+ This comes from a symbol of type N_SO. */
+
+static char *last_source_file;
+
+/* Core address of start of text of current source file.
+ This too comes from the N_SO symbol. */
+
+static CORE_ADDR last_source_start_addr;
+
+/* End of the text segment of the executable file,
+ as found in the symbol _etext. */
+
+static CORE_ADDR end_of_text_addr;
+
+/* The list of sub-source-files within the current individual compilation.
+ Each file gets its own symtab with its own linetable and associated info,
+ but they all share one blockvector. */
+
+struct subfile
+{
+ struct subfile *next;
+ char *name;
+ struct linetable *line_vector;
+ int line_vector_length;
+ int line_vector_index;
+ int prev_line_number;
+};
+
+static struct subfile *subfiles;
+
+static struct subfile *current_subfile;
+
+/* Count symbols as they are processed, for error messages. */
+
+static int symnum;
+
+/* Vector of types defined so far, indexed by their dbx type numbers.
+ (In newer sun systems, dbx uses a pair of numbers in parens,
+ as in "(SUBFILENUM,NUMWITHINSUBFILE)". Then these numbers must be
+ translated through the type_translations hash table to get
+ the index into the type vector.) */
+
+static struct typevector *type_vector;
+
+/* Number of elements allocated for type_vector currently. */
+
+static int type_vector_length;
+
+/* Vector of line number information. */
+
+static struct linetable *line_vector;
+
+/* Index of next entry to go in line_vector_index. */
+
+static int line_vector_index;
+
+/* Last line number recorded in the line vector. */
+
+static int prev_line_number;
+
+/* Number of elements allocated for line_vector currently. */
+
+static int line_vector_length;
+
+/* Hash table of global symbols whose values are not known yet.
+ They are chained thru the SYMBOL_VALUE, since we don't
+ have the correct data for that slot yet. */
+/* The use of the LOC_BLOCK code in this chain is nonstandard--
+ it refers to a FORTRAN common block rather than the usual meaning. */
+
+#define HASHSIZE 127
+static struct symbol *global_sym_chain[HASHSIZE];
+
+/* Record the symbols defined for each context in a list.
+ We don't create a struct block for the context until we
+ know how long to make it. */
+
+#define PENDINGSIZE 100
+
+struct pending
+{
+ struct pending *next;
+ int nsyms;
+ struct symbol *symbol[PENDINGSIZE];
+};
+
+/* List of free `struct pending' structures for reuse. */
+struct pending *free_pendings;
+
+/* Here are the three lists that symbols are put on. */
+
+struct pending *file_symbols; /* static at top level, and types */
+
+struct pending *global_symbols; /* global functions and variables */
+
+struct pending *local_symbols; /* everything local to lexical context */
+
+/* List of symbols declared since the last BCOMM. This list is a tail
+ of local_symbols. When ECOMM is seen, the symbols on the list
+ are noted so their proper addresses can be filled in later,
+ using the common block base address gotten from the assembler
+ stabs. */
+
+struct pending *common_block;
+int common_block_i;
+
+/* Stack representing unclosed lexical contexts
+ (that will become blocks, eventually). */
+
+struct context_stack
+{
+ struct pending *locals;
+ struct pending_block *old_blocks;
+ struct symbol *name;
+ CORE_ADDR start_addr;
+ int depth;
+};
+
+struct context_stack *context_stack;
+
+/* Index of first unused entry in context stack. */
+int context_stack_depth;
+
+/* Currently allocated size of context stack. */
+
+int context_stack_size;
+
+/* Nonzero if within a function (so symbols should be local,
+ if nothing says specifically). */
+
+int within_function;
+
+/* List of blocks already made (lexical contexts already closed).
+ This is used at the end to make the blockvector. */
+
+struct pending_block
+{
+ struct pending_block *next;
+ struct block *block;
+};
+
+struct pending_block *pending_blocks;
+
+extern CORE_ADDR startup_file_start; /* From blockframe.c */
+extern CORE_ADDR startup_file_end; /* From blockframe.c */
+
+/* File name symbols were loaded from. */
+
+static char *symfile;
+
+/* Low and high symbol values (inclusive) for the global variable
+ entries in the symbol file. */
+
+static int first_global_sym, last_global_sym;
+
+/* Structures with which to manage partial symbol allocation. */
+
+struct psymbol_allocation_list global_psymbols, static_psymbols;
+
+/* Global variable which, when set, indicates that we are processing a
+ .o file compiled with gcc */
+
+static unsigned char processing_gcc_compilation;
+
+/* Make a list of forward references which haven't been defined. */
+static struct type **undef_types;
+static int undef_types_allocated, undef_types_length;
+
+ /* Setup a define to deal cleanly with the underscore problem */
+
+#ifdef NAMES_HAVE_UNDERSCORE
+#define HASH_OFFSET 1
+#else
+#define HASH_OFFSET 0
+#endif
+
+#if 0
+/* I'm not sure why this is here. To debug bugs which cause
+ an infinite loop of allocations, I suppose. In any event,
+ dumping core when out of memory isn't usually right. */
+static int
+xxmalloc (n)
+{
+ int v = malloc (n);
+ if (v == 0)
+ {
+ fprintf (stderr, "Virtual memory exhausted.\n");
+ abort ();
+ }
+ return v;
+}
+#else /* not 0 */
+#define xxmalloc xmalloc
+#endif /* not 0 */
+
+/* Make a copy of the string at PTR with SIZE characters in the symbol obstack
+ (and add a null character at the end in the copy).
+ Returns the address of the copy. */
+
+static char *
+obsavestring (ptr, size)
+ char *ptr;
+ int size;
+{
+ register char *p = (char *) obstack_alloc (symbol_obstack, size + 1);
+ /* Open-coded bcopy--saves function call time.
+ These strings are usually short. */
+ {
+ register char *p1 = ptr;
+ register char *p2 = p;
+ char *end = ptr + size;
+ while (p1 != end)
+ *p2++ = *p1++;
+ }
+ p[size] = 0;
+ return p;
+}
+
+/* Concatenate strings S1, S2 and S3; return the new string.
+ Space is found in the symbol_obstack. */
+
+static char *
+obconcat (s1, s2, s3)
+ char *s1, *s2, *s3;
+{
+ register int len = strlen (s1) + strlen (s2) + strlen (s3) + 1;
+ register char *val = (char *) obstack_alloc (symbol_obstack, len);
+ strcpy (val, s1);
+ strcat (val, s2);
+ strcat (val, s3);
+ return val;
+}
+
+/* Support for Sun changes to dbx symbol format */
+
+/* For each identified header file, we have a table of types defined
+ in that header file.
+
+ header_files maps header file names to their type tables.
+ It is a vector of n_header_files elements.
+ Each element describes one header file.
+ It contains a vector of types.
+
+ Sometimes it can happen that the same header file produces
+ different results when included in different places.
+ This can result from conditionals or from different
+ things done before including the file.
+ When this happens, there are multiple entries for the file in this table,
+ one entry for each distinct set of results.
+ The entries are distinguished by the INSTANCE field.
+ The INSTANCE field appears in the N_BINCL and N_EXCL symbol table and is
+ used to match header-file references to their corresponding data. */
+
+struct header_file
+{
+ char *name; /* Name of header file */
+ int instance; /* Numeric code distinguishing instances
+ of one header file that produced
+ different results when included.
+ It comes from the N_BINCL or N_EXCL. */
+ struct type **vector; /* Pointer to vector of types */
+ int length; /* Allocated length (# elts) of that vector */
+};
+
+static struct header_file *header_files;
+
+static int n_header_files;
+
+static int n_allocated_header_files;
+
+/* During initial symbol readin, we need to have a structure to keep
+ track of which psymtabs have which bincls in them. This structure
+ is used during readin to setup the list of dependencies within each
+ partial symbol table. */
+
+struct header_file_location
+{
+ char *name; /* Name of header file */
+ int instance; /* See above */
+ struct partial_symtab *pst; /* Partial symtab that has the
+ BINCL/EINCL defs for this file */
+};
+
+/* The actual list and controling variables */
+static struct header_file_location *bincl_list, *next_bincl;
+static int bincls_allocated;
+
+/* Within each object file, various header files are assigned numbers.
+ A type is defined or referred to with a pair of numbers
+ (FILENUM,TYPENUM) where FILENUM is the number of the header file
+ and TYPENUM is the number within that header file.
+ TYPENUM is the index within the vector of types for that header file.
+
+ FILENUM == 1 is special; it refers to the main source of the object file,
+ and not to any header file. FILENUM != 1 is interpreted by looking it up
+ in the following table, which contains indices in header_files. */
+
+static int *this_object_header_files;
+
+static int n_this_object_header_files;
+
+static int n_allocated_this_object_header_files;
+
+/* When a header file is getting special overriding definitions
+ for one source file, record here the header_files index
+ of its normal definition vector.
+ At other times, this is -1. */
+
+static int header_file_prev_index;
+
+/* At the start of reading dbx symbols, allocate our tables. */
+
+static void
+init_header_files ()
+{
+ n_allocated_header_files = 10;
+ header_files = (struct header_file *) xxmalloc (10 * sizeof (struct header_file));
+ n_header_files = 0;
+
+ n_allocated_this_object_header_files = 10;
+ this_object_header_files = (int *) xxmalloc (10 * sizeof (int));
+}
+
+/* At the end of reading dbx symbols, free our tables. */
+
+static void
+free_header_files ()
+{
+ register int i;
+ for (i = 0; i < n_header_files; i++)
+ free (header_files[i].name);
+ if (header_files) free (header_files);
+ if (this_object_header_files)
+ free (this_object_header_files);
+}
+
+/* Called at the start of each object file's symbols.
+ Clear out the mapping of header file numbers to header files. */
+
+static void
+new_object_header_files ()
+{
+ /* Leave FILENUM of 0 free for builtin types and this file's types. */
+ n_this_object_header_files = 1;
+ header_file_prev_index = -1;
+}
+
+/* Add header file number I for this object file
+ at the next successive FILENUM. */
+
+static void
+add_this_object_header_file (i)
+ int i;
+{
+ if (n_this_object_header_files == n_allocated_this_object_header_files)
+ {
+ n_allocated_this_object_header_files *= 2;
+ this_object_header_files
+ = (int *) xrealloc (this_object_header_files,
+ n_allocated_this_object_header_files * sizeof (int));
+ }
+
+ this_object_header_files[n_this_object_header_files++] = i;
+}
+
+/* Add to this file an "old" header file, one already seen in
+ a previous object file. NAME is the header file's name.
+ INSTANCE is its instance code, to select among multiple
+ symbol tables for the same header file. */
+
+static void
+add_old_header_file (name, instance)
+ char *name;
+ int instance;
+{
+ register struct header_file *p = header_files;
+ register int i;
+
+ for (i = 0; i < n_header_files; i++)
+ if (!strcmp (p[i].name, name) && instance == p[i].instance)
+ {
+ add_this_object_header_file (i);
+ return;
+ }
+ error ("Invalid symbol data: \"repeated\" header file that hasn't been seen before, at symtab pos %d.",
+ symnum);
+}
+
+/* Add to this file a "new" header file: definitions for its types follow.
+ NAME is the header file's name.
+ Most often this happens only once for each distinct header file,
+ but not necessarily. If it happens more than once, INSTANCE has
+ a different value each time, and references to the header file
+ use INSTANCE values to select among them.
+
+ dbx output contains "begin" and "end" markers for each new header file,
+ but at this level we just need to know which files there have been;
+ so we record the file when its "begin" is seen and ignore the "end". */
+
+static void
+add_new_header_file (name, instance)
+ char *name;
+ int instance;
+{
+ register int i;
+ register struct header_file *p = header_files;
+ header_file_prev_index = -1;
+
+#if 0
+ /* This code was used before I knew about the instance codes.
+ My first hypothesis is that it is not necessary now
+ that instance codes are handled. */
+
+ /* Has this header file a previous definition?
+ If so, make a new entry anyway so that this use in this source file
+ gets a separate entry. Later source files get the old entry.
+ Record here the index of the old entry, so that any type indices
+ not previously defined can get defined in the old entry as
+ well as in the new one. */
+
+ for (i = 0; i < n_header_files; i++)
+ if (!strcmp (p[i].name, name))
+ {
+ header_file_prev_index = i;
+ }
+
+#endif
+
+ /* Make sure there is room for one more header file. */
+
+ if (n_header_files == n_allocated_header_files)
+ {
+ n_allocated_header_files *= 2;
+ header_files = (struct header_file *)
+ xrealloc (header_files,
+ (n_allocated_header_files
+ * sizeof (struct header_file)));
+ }
+
+ /* Create an entry for this header file. */
+
+ i = n_header_files++;
+ header_files[i].name = savestring (name, strlen(name));
+ header_files[i].instance = instance;
+ header_files[i].length = 10;
+ header_files[i].vector
+ = (struct type **) xxmalloc (10 * sizeof (struct type *));
+ bzero (header_files[i].vector, 10 * sizeof (struct type *));
+
+ add_this_object_header_file (i);
+}
+
+/* Look up a dbx type-number pair. Return the address of the slot
+ where the type for that number-pair is stored.
+ The number-pair is in TYPENUMS.
+
+ This can be used for finding the type associated with that pair
+ or for associating a new type with the pair. */
+
+static struct type **
+dbx_lookup_type (typenums)
+ int typenums[2];
+{
+ register int filenum = typenums[0], index = typenums[1];
+
+ if (filenum < 0 || filenum >= n_this_object_header_files)
+ error ("Invalid symbol data: type number (%d,%d) out of range at symtab pos %d.",
+ filenum, index, symnum);
+
+ if (filenum == 0)
+ {
+ /* Type is defined outside of header files.
+ Find it in this object file's type vector. */
+ if (index >= type_vector_length)
+ {
+ type_vector_length *= 2;
+ type_vector = (struct typevector *)
+ xrealloc (type_vector,
+ (sizeof (struct typevector)
+ + type_vector_length * sizeof (struct type *)));
+ bzero (&type_vector->type[type_vector_length / 2],
+ type_vector_length * sizeof (struct type *) / 2);
+ }
+ return &type_vector->type[index];
+ }
+ else
+ {
+ register int real_filenum = this_object_header_files[filenum];
+ register struct header_file *f;
+
+ if (real_filenum >= n_header_files)
+ abort ();
+
+ f = &header_files[real_filenum];
+
+ if (index >= f->length)
+ {
+ f->length *= 2;
+ f->vector = (struct type **)
+ xrealloc (f->vector, f->length * sizeof (struct type *));
+ bzero (&f->vector[f->length / 2],
+ f->length * sizeof (struct type *) / 2);
+ }
+ return &f->vector[index];
+ }
+}
+
+/* Create a type object. Occaisionally used when you need a type
+ which isn't going to be given a type number. */
+
+static struct type *
+dbx_create_type ()
+{
+ register struct type *type =
+ (struct type *) obstack_alloc (symbol_obstack, sizeof (struct type));
+
+ bzero (type, sizeof (struct type));
+ TYPE_VPTR_FIELDNO (type) = -1;
+ return type;
+}
+
+/* Make sure there is a type allocated for type numbers TYPENUMS
+ and return the type object.
+ This can create an empty (zeroed) type object.
+ TYPENUMS may be (-1, -1) to return a new type object that is not
+ put into the type vector, and so may not be referred to by number. */
+
+static struct type *
+dbx_alloc_type (typenums)
+ int typenums[2];
+{
+ register struct type **type_addr;
+ register struct type *type;
+
+ if (typenums[1] != -1)
+ {
+ type_addr = dbx_lookup_type (typenums);
+ type = *type_addr;
+ }
+ else
+ {
+ type_addr = 0;
+ type = 0;
+ }
+
+ /* If we are referring to a type not known at all yet,
+ allocate an empty type for it.
+ We will fill it in later if we find out how. */
+ if (type == 0)
+ {
+ type = dbx_create_type ();
+ if (type_addr)
+ *type_addr = type;
+ }
+
+ return type;
+}
+
+#if 0
+static struct type **
+explicit_lookup_type (real_filenum, index)
+ int real_filenum, index;
+{
+ register struct header_file *f = &header_files[real_filenum];
+
+ if (index >= f->length)
+ {
+ f->length *= 2;
+ f->vector = (struct type **)
+ xrealloc (f->vector, f->length * sizeof (struct type *));
+ bzero (&f->vector[f->length / 2],
+ f->length * sizeof (struct type *) / 2);
+ }
+ return &f->vector[index];
+}
+#endif
+
+/* maintain the lists of symbols and blocks */
+
+/* Add a symbol to one of the lists of symbols. */
+static void
+add_symbol_to_list (symbol, listhead)
+ struct symbol *symbol;
+ struct pending **listhead;
+{
+ /* We keep PENDINGSIZE symbols in each link of the list.
+ If we don't have a link with room in it, add a new link. */
+ if (*listhead == 0 || (*listhead)->nsyms == PENDINGSIZE)
+ {
+ register struct pending *link;
+ if (free_pendings)
+ {
+ link = free_pendings;
+ free_pendings = link->next;
+ }
+ else
+ link = (struct pending *) xxmalloc (sizeof (struct pending));
+
+ link->next = *listhead;
+ *listhead = link;
+ link->nsyms = 0;
+ }
+
+ (*listhead)->symbol[(*listhead)->nsyms++] = symbol;
+}
+
+/* At end of reading syms, or in case of quit,
+ really free as many `struct pending's as we can easily find. */
+
+static void
+really_free_pendings ()
+{
+ struct pending *next, *next1;
+ struct pending_block *bnext, *bnext1;
+
+ for (next = free_pendings; next; next = next1)
+ {
+ next1 = next->next;
+ free (next);
+ }
+ free_pendings = 0;
+
+ for (bnext = pending_blocks; bnext; bnext = bnext1)
+ {
+ bnext1 = bnext->next;
+ free (bnext);
+ }
+ pending_blocks = 0;
+
+ for (next = file_symbols; next; next = next1)
+ {
+ next1 = next->next;
+ free (next);
+ }
+ for (next = global_symbols; next; next = next1)
+ {
+ next1 = next->next;
+ free (next);
+ }
+}
+
+/* Take one of the lists of symbols and make a block from it.
+ Keep the order the symbols have in the list (reversed from the input file).
+ Put the block on the list of pending blocks. */
+
+static void
+finish_block (symbol, listhead, old_blocks, start, end)
+ struct symbol *symbol;
+ struct pending **listhead;
+ struct pending_block *old_blocks;
+ CORE_ADDR start, end;
+{
+ register struct pending *next, *next1;
+ register struct block *block;
+ register struct pending_block *pblock;
+ struct pending_block *opblock;
+ register int i;
+
+ /* Count the length of the list of symbols. */
+
+ for (next = *listhead, i = 0; next; i += next->nsyms, next = next->next);
+
+ block = (struct block *) obstack_alloc (symbol_obstack,
+ (sizeof (struct block)
+ + ((i - 1)
+ * sizeof (struct symbol *))));
+
+ /* Copy the symbols into the block. */
+
+ BLOCK_NSYMS (block) = i;
+ for (next = *listhead; next; next = next->next)
+ {
+ register int j;
+ for (j = next->nsyms - 1; j >= 0; j--)
+ BLOCK_SYM (block, --i) = next->symbol[j];
+ }
+
+ BLOCK_START (block) = start;
+ BLOCK_END (block) = end;
+ BLOCK_SUPERBLOCK (block) = 0; /* Filled in when containing block is made */
+ BLOCK_GCC_COMPILED (block) = processing_gcc_compilation;
+
+ /* Put the block in as the value of the symbol that names it. */
+
+ if (symbol)
+ {
+ SYMBOL_BLOCK_VALUE (symbol) = block;
+ BLOCK_FUNCTION (block) = symbol;
+ }
+ else
+ BLOCK_FUNCTION (block) = 0;
+
+ /* Now "free" the links of the list, and empty the list. */
+
+ for (next = *listhead; next; next = next1)
+ {
+ next1 = next->next;
+ next->next = free_pendings;
+ free_pendings = next;
+ }
+ *listhead = 0;
+
+ /* Install this block as the superblock
+ of all blocks made since the start of this scope
+ that don't have superblocks yet. */
+
+ opblock = 0;
+ for (pblock = pending_blocks; pblock != old_blocks; pblock = pblock->next)
+ {
+ if (BLOCK_SUPERBLOCK (pblock->block) == 0)
+ BLOCK_SUPERBLOCK (pblock->block) = block;
+ opblock = pblock;
+ }
+
+ /* Record this block on the list of all blocks in the file.
+ Put it after opblock, or at the beginning if opblock is 0.
+ This puts the block in the list after all its subblocks. */
+
+ /* Allocate in the symbol_obstack to save time.
+ It wastes a little space. */
+ pblock = (struct pending_block *)
+ obstack_alloc (symbol_obstack,
+ sizeof (struct pending_block));
+ pblock->block = block;
+ if (opblock)
+ {
+ pblock->next = opblock->next;
+ opblock->next = pblock;
+ }
+ else
+ {
+ pblock->next = pending_blocks;
+ pending_blocks = pblock;
+ }
+}
+
+static struct blockvector *
+make_blockvector ()
+{
+ register struct pending_block *next, *next1;
+ register struct blockvector *blockvector;
+ register int i;
+
+ /* Count the length of the list of blocks. */
+
+ for (next = pending_blocks, i = 0; next; next = next->next, i++);
+
+ blockvector = (struct blockvector *)
+ obstack_alloc (symbol_obstack,
+ (sizeof (struct blockvector)
+ + (i - 1) * sizeof (struct block *)));
+
+ /* Copy the blocks into the blockvector.
+ This is done in reverse order, which happens to put
+ the blocks into the proper order (ascending starting address).
+ finish_block has hair to insert each block into the list
+ after its subblocks in order to make sure this is true. */
+
+ BLOCKVECTOR_NBLOCKS (blockvector) = i;
+ for (next = pending_blocks; next; next = next->next)
+ BLOCKVECTOR_BLOCK (blockvector, --i) = next->block;
+
+#if 0 /* Now we make the links in the obstack, so don't free them. */
+ /* Now free the links of the list, and empty the list. */
+
+ for (next = pending_blocks; next; next = next1)
+ {
+ next1 = next->next;
+ free (next);
+ }
+#endif
+ pending_blocks = 0;
+
+ return blockvector;
+}
+
+/* Manage the vector of line numbers. */
+
+static void
+record_line (line, pc)
+ int line;
+ CORE_ADDR pc;
+{
+ struct linetable_entry *e;
+ /* Ignore the dummy line number in libg.o */
+
+ if (line == 0xffff)
+ return;
+
+ /* Make sure line vector is big enough. */
+
+ if (line_vector_index + 1 >= line_vector_length)
+ {
+ line_vector_length *= 2;
+ line_vector = (struct linetable *)
+ xrealloc (line_vector,
+ (sizeof (struct linetable)
+ + line_vector_length * sizeof (struct linetable_entry)));
+ current_subfile->line_vector = line_vector;
+ }
+
+ e = line_vector->item + line_vector_index++;
+ e->line = line; e->pc = pc;
+}
+
+/* Start a new symtab for a new source file.
+ This is called when a dbx symbol of type N_SO is seen;
+ it indicates the start of data for one original source file. */
+
+static void
+start_symtab (name, start_addr)
+ char *name;
+ CORE_ADDR start_addr;
+{
+ register struct symtab *s;
+
+ last_source_file = name;
+ last_source_start_addr = start_addr;
+ file_symbols = 0;
+ global_symbols = 0;
+ within_function = 0;
+
+ /* Context stack is initially empty, with room for 10 levels. */
+ context_stack
+ = (struct context_stack *) xxmalloc (10 * sizeof (struct context_stack));
+ context_stack_size = 10;
+ context_stack_depth = 0;
+
+ new_object_header_files ();
+
+ for (s = symseg_chain; s; s = s->next)
+ if (s->ldsymoff == symnum * sizeof (struct nlist))
+ break;
+ current_symseg = s;
+ if (s != 0)
+ return;
+
+ type_vector_length = 160;
+ type_vector = (struct typevector *)
+ xxmalloc (sizeof (struct typevector)
+ + type_vector_length * sizeof (struct type *));
+ bzero (type_vector->type, type_vector_length * sizeof (struct type *));
+
+ /* Initialize the list of sub source files with one entry
+ for this file (the top-level source file). */
+
+ subfiles = 0;
+ current_subfile = 0;
+ start_subfile (name);
+
+#if 0 /* This is now set at the beginning of read_ofile_symtab */
+ /* Set default for compiler to pcc; assume that we aren't processing
+ a gcc compiled file until proved otherwise. */
+
+ processing_gcc_compilation = 0;
+#endif
+}
+
+/* Handle an N_SOL symbol, which indicates the start of
+ code that came from an included (or otherwise merged-in)
+ source file with a different name. */
+
+static void
+start_subfile (name)
+ char *name;
+{
+ register struct subfile *subfile;
+
+ /* Save the current subfile's line vector data. */
+
+ if (current_subfile)
+ {
+ current_subfile->line_vector_index = line_vector_index;
+ current_subfile->line_vector_length = line_vector_length;
+ current_subfile->prev_line_number = prev_line_number;
+ }
+
+ /* See if this subfile is already known as a subfile of the
+ current main source file. */
+
+ for (subfile = subfiles; subfile; subfile = subfile->next)
+ {
+ if (!strcmp (subfile->name, name))
+ {
+ line_vector = subfile->line_vector;
+ line_vector_index = subfile->line_vector_index;
+ line_vector_length = subfile->line_vector_length;
+ prev_line_number = subfile->prev_line_number;
+ current_subfile = subfile;
+ return;
+ }
+ }
+
+ /* This subfile is not known. Add an entry for it. */
+
+ line_vector_index = 0;
+ line_vector_length = 1000;
+ prev_line_number = -2; /* Force first line number to be explicit */
+ line_vector = (struct linetable *)
+ xxmalloc (sizeof (struct linetable)
+ + line_vector_length * sizeof (struct linetable_entry));
+
+ /* Make an entry for this subfile in the list of all subfiles
+ of the current main source file. */
+
+ subfile = (struct subfile *) xxmalloc (sizeof (struct subfile));
+ subfile->next = subfiles;
+ subfile->name = savestring (name, strlen (name));
+ subfile->line_vector = line_vector;
+ subfiles = subfile;
+ current_subfile = subfile;
+}
+
+/* Finish the symbol definitions for one main source file,
+ close off all the lexical contexts for that file
+ (creating struct block's for them), then make the struct symtab
+ for that file and put it in the list of all such.
+
+ END_ADDR is the address of the end of the file's text. */
+
+static void
+end_symtab (end_addr)
+ CORE_ADDR end_addr;
+{
+ register struct symtab *symtab;
+ register struct blockvector *blockvector;
+ register struct subfile *subfile;
+ register struct linetable *lv;
+ struct subfile *nextsub;
+
+ if (current_symseg != 0)
+ {
+ last_source_file = 0;
+ current_symseg = 0;
+ return;
+ }
+
+ /* Finish the lexical context of the last function in the file;
+ pop the context stack. */
+
+ if (context_stack_depth > 0)
+ {
+ register struct context_stack *cstk;
+ context_stack_depth--;
+ cstk = &context_stack[context_stack_depth];
+ /* Make a block for the local symbols within. */
+ finish_block (cstk->name, &local_symbols, cstk->old_blocks,
+ cstk->start_addr, end_addr);
+ }
+
+ /* Cleanup any undefined types that have been left hanging around
+ (this needs to be done before the finish_blocks so that
+ file_symbols is still good). */
+ cleanup_undefined_types ();
+
+ /* Finish defining all the blocks of this symtab. */
+ finish_block (0, &file_symbols, 0, last_source_start_addr, end_addr);
+ finish_block (0, &global_symbols, 0, last_source_start_addr, end_addr);
+ blockvector = make_blockvector ();
+
+ current_subfile->line_vector_index = line_vector_index;
+
+ /* Now create the symtab objects proper, one for each subfile. */
+ /* (The main file is one of them.) */
+
+ for (subfile = subfiles; subfile; subfile = nextsub)
+ {
+ symtab = (struct symtab *) xxmalloc (sizeof (struct symtab));
+ symtab->free_ptr = 0;
+
+ /* Fill in its components. */
+ symtab->blockvector = blockvector;
+ type_vector->length = type_vector_length;
+ symtab->typevector = type_vector;
+ symtab->free_code = free_linetable;
+ if (subfile->next == 0)
+ symtab->free_ptr = (char *) type_vector;
+
+ symtab->filename = subfile->name;
+ lv = subfile->line_vector;
+ lv->nitems = subfile->line_vector_index;
+ symtab->linetable = (struct linetable *)
+ xrealloc (lv, (sizeof (struct linetable)
+ + lv->nitems * sizeof (struct linetable_entry)));
+ symtab->nlines = 0;
+ symtab->line_charpos = 0;
+
+ /* Link the new symtab into the list of such. */
+ symtab->next = symtab_list;
+ symtab_list = symtab;
+
+ nextsub = subfile->next;
+ free (subfile);
+ }
+
+ type_vector = 0;
+ type_vector_length = -1;
+ line_vector = 0;
+ line_vector_length = -1;
+ last_source_file = 0;
+}
+
+#ifdef N_BINCL
+
+/* Handle the N_BINCL and N_EINCL symbol types
+ that act like N_SOL for switching source files
+ (different subfiles, as we call them) within one object file,
+ but using a stack rather than in an arbitrary order. */
+
+struct subfile_stack
+{
+ struct subfile_stack *next;
+ char *name;
+ int prev_index;
+};
+
+struct subfile_stack *subfile_stack;
+
+static void
+push_subfile ()
+{
+ register struct subfile_stack *tem
+ = (struct subfile_stack *) xxmalloc (sizeof (struct subfile_stack));
+
+ tem->next = subfile_stack;
+ subfile_stack = tem;
+ if (current_subfile == 0 || current_subfile->name == 0)
+ abort ();
+ tem->name = current_subfile->name;
+ tem->prev_index = header_file_prev_index;
+}
+
+static char *
+pop_subfile ()
+{
+ register char *name;
+ register struct subfile_stack *link = subfile_stack;
+
+ if (link == 0)
+ abort ();
+
+ name = link->name;
+ subfile_stack = link->next;
+ header_file_prev_index = link->prev_index;
+ free (link);
+
+ return name;
+}
+#endif /* Have N_BINCL */
+
+/* Accumulate the misc functions in bunches of 127.
+ At the end, copy them all into one newly allocated structure. */
+
+#define MISC_BUNCH_SIZE 127
+
+struct misc_bunch
+{
+ struct misc_bunch *next;
+ struct misc_function contents[MISC_BUNCH_SIZE];
+};
+
+/* Bunch currently being filled up.
+ The next field points to chain of filled bunches. */
+
+static struct misc_bunch *misc_bunch;
+
+/* Number of slots filled in current bunch. */
+
+static int misc_bunch_index;
+
+/* Total number of misc functions recorded so far. */
+
+static int misc_count;
+
+static void
+init_misc_functions ()
+{
+ misc_count = 0;
+ misc_bunch = 0;
+ misc_bunch_index = MISC_BUNCH_SIZE;
+}
+
+static void
+record_misc_function (name, address, type)
+ char *name;
+ CORE_ADDR address;
+ int type;
+{
+ register struct misc_bunch *new;
+ register unsigned char mtype;
+
+ if (misc_bunch_index == MISC_BUNCH_SIZE)
+ {
+ new = (struct misc_bunch *) xxmalloc (sizeof (struct misc_bunch));
+ misc_bunch_index = 0;
+ new->next = misc_bunch;
+ misc_bunch = new;
+ }
+ misc_bunch->contents[misc_bunch_index].name = name;
+ misc_bunch->contents[misc_bunch_index].address = address;
+ switch (type &~ N_EXT)
+ {
+ case N_TEXT: mtype = mf_text; break;
+ case N_DATA: mtype = mf_data; break;
+ case N_BSS: mtype = mf_bss; break;
+ case N_ABS: mtype = mf_abs; break;
+#ifdef N_SETV
+ case N_SETV: mtype = mf_data; break;
+#endif
+ default: mtype = mf_unknown; break;
+ }
+ misc_bunch->contents[misc_bunch_index].type = mtype;
+ misc_bunch_index++;
+ misc_count++;
+}
+
+static int
+compare_misc_functions (fn1, fn2)
+ struct misc_function *fn1, *fn2;
+{
+ /* Return a signed result based on unsigned comparisons
+ so that we sort into unsigned numeric order. */
+ if (fn1->address < fn2->address)
+ return -1;
+ if (fn1->address > fn2->address)
+ return 1;
+ return 0;
+}
+
+static void
+discard_misc_bunches ()
+{
+ register struct misc_bunch *next;
+
+ while (misc_bunch)
+ {
+ next = misc_bunch->next;
+ free (misc_bunch);
+ misc_bunch = next;
+ }
+}
+
+/* INCLINK nonzero means bunches are from an incrementally-linked file.
+ Add them to the existing bunches.
+ Otherwise INCLINK is zero, and we start from scratch. */
+static void
+condense_misc_bunches (inclink)
+ int inclink;
+{
+ register int i, j;
+ register struct misc_bunch *bunch;
+#ifdef NAMES_HAVE_UNDERSCORE
+ int offset = 1;
+#else
+ int offset = 0;
+#endif
+
+ if (inclink)
+ {
+ misc_function_vector
+ = (struct misc_function *)
+ xrealloc (misc_function_vector, (misc_count + misc_function_count)
+ * sizeof (struct misc_function));
+ j = misc_function_count;
+ }
+ else
+ {
+ misc_function_vector
+ = (struct misc_function *)
+ xxmalloc (misc_count * sizeof (struct misc_function));
+ j = 0;
+ }
+
+ bunch = misc_bunch;
+ while (bunch)
+ {
+ for (i = 0; i < misc_bunch_index; i++)
+ {
+ misc_function_vector[j] = bunch->contents[i];
+ misc_function_vector[j].name
+ = obconcat (misc_function_vector[j].name
+ + (misc_function_vector[j].name[0] == '_' ? offset : 0),
+ "", "");
+ j++;
+ }
+ bunch = bunch->next;
+ misc_bunch_index = MISC_BUNCH_SIZE;
+ }
+
+ if (inclink)
+ misc_function_count += misc_count;
+ else
+ misc_function_count = j;
+
+ /* Sort the misc functions by address. */
+
+ qsort (misc_function_vector, misc_function_count,
+ sizeof (struct misc_function),
+ compare_misc_functions);
+
+ /* (re)build the hash table (positions changed during the sort) */
+
+ for (i = 0; i < MISC_FUNC_HASH_SIZE; ++i)
+ misc_function_hash_tab[i] = -1;
+ for (i = 0; i < misc_function_count; ++i)
+ {
+ j = hash_symbol(misc_function_vector[i].name) & (MISC_FUNC_HASH_SIZE - 1);
+ misc_function_vector[i].next = misc_function_hash_tab[j];
+ misc_function_hash_tab[j] = i;
+ }
+}
+
+/* Call sort_syms to sort alphabetically
+ the symbols of each block of each symtab. */
+
+static int
+compare_symbols (s1, s2)
+ struct symbol **s1, **s2;
+{
+ register int namediff;
+
+ /* Compare the initial characters. */
+ namediff = SYMBOL_NAME (*s1)[0] - SYMBOL_NAME (*s2)[0];
+ if (namediff != 0) return namediff;
+
+ /* If they match, compare the rest of the names. */
+ namediff = strcmp (SYMBOL_NAME (*s1), SYMBOL_NAME (*s2));
+ if (namediff != 0) return namediff;
+
+ /* For symbols of the same name, registers should come first. */
+ return ((SYMBOL_CLASS (*s2) == LOC_REGISTER)
+ - (SYMBOL_CLASS (*s1) == LOC_REGISTER));
+}
+
+static void sort_symtab_syms ();
+
+static void
+sort_syms ()
+{
+ register struct symtab *s;
+
+ for (s = symtab_list; s; s = s->next)
+ sort_symtab_syms (s);
+}
+
+static void
+sort_symtab_syms (s)
+ register struct symtab *s;
+{
+ register struct blockvector *bv = BLOCKVECTOR (s);
+ int nbl = BLOCKVECTOR_NBLOCKS (bv);
+ int i;
+ register struct block *b;
+
+ /* Note that in the following sort, we always make sure that
+ register debug symbol declarations always come before regular
+ debug symbol declarations (as might happen when parameters are
+ then put into registers by the compiler). We do this by a
+ correct compare in compare_symbols, and by the reversal of the
+ symbols if we don't sort. This works as long as a register debug
+ symbol always comes after a parameter debug symbol. */
+
+ /* This is no longer necessary; lookup_block_symbol now always
+ prefers some other declaration over a parameter declaration. We
+ still sort the thing (that is necessary), but we don't reverse it
+ if we shouldn't sort it. */
+
+ for (i = 0; i < nbl; i++)
+ {
+ b = BLOCKVECTOR_BLOCK (bv, i);
+ if (BLOCK_SHOULD_SORT (b))
+ qsort (&BLOCK_SYM (b, 0), BLOCK_NSYMS (b),
+ sizeof (struct symbol *), compare_symbols);
+ }
+}
+
+
+extern struct symtab *psymtab_to_symtab ();
+
+/* The entry point. */
+static CORE_ADDR entry_point;
+
+static char *symfile_string_table;
+static int symfile_string_table_size;
+
+/* This is the symbol-file command. Read the file, analyze its symbols,
+ and add a struct symtab to symtab_list. */
+
+void
+symbol_file_command (name, from_tty)
+ char *name;
+ int from_tty;
+{
+ register int desc;
+ DECLARE_FILE_HEADERS;
+ struct nlist *nlist;
+
+ /* The string table. */
+ char *stringtab;
+
+ /* The size of the string table (buffer is a bizarre name...). */
+ long buffer;
+
+ register int val;
+ extern void close ();
+ struct cleanup *old_chain;
+ struct symtab *symseg;
+ struct stat statbuf;
+
+ dont_repeat ();
+
+ if (name == 0)
+ {
+ if ((symtab_list || partial_symtab_list)
+ && from_tty
+ && !query ("Discard symbol table? ", 0))
+ error ("Not confirmed.");
+ if (symfile)
+ free (symfile);
+ symfile = 0;
+ free_all_symtabs ();
+ free_all_psymtabs ();
+ return;
+ }
+
+ name = tilde_expand (name);
+ make_cleanup (free, name);
+
+ if ((symtab_list || partial_symtab_list)
+ && !query ("Load new symbol table from \"%s\"? ", name))
+ error ("Not confirmed.");
+
+ {
+ char *absolute_name;
+ desc = openp (getenv ("PATH"), 1, name, O_RDONLY, 0, &absolute_name);
+ if (desc < 0)
+ perror_with_name (name);
+ else
+ name = absolute_name;
+ }
+
+ old_chain = make_cleanup (close, desc);
+ make_cleanup (free_current_contents, &name);
+
+ READ_FILE_HEADERS (desc, name);
+
+ entry_point = ENTRY_POINT;
+
+ if (NUMBER_OF_SYMBOLS == 0)
+ {
+ if (symfile)
+ free (symfile);
+ symfile = 0;
+ free_all_symtabs ();
+ free_all_psymtabs ();
+ printf ("%s has no symbol-table; symbols discarded.\n", name);
+ fflush (stdout);
+ do_cleanups (old_chain);
+ return;
+ }
+
+ printf ("Reading symbol data from %s...", name);
+ fflush (stdout);
+
+ /* Now read the string table, all at once. */
+ val = lseek (desc, STRING_TABLE_OFFSET, 0);
+ if (val < 0)
+ perror_with_name (name);
+ if (stat (name, &statbuf) == -1)
+ perror_with_name (name);
+ READ_STRING_TABLE_SIZE (buffer);
+ if (buffer >= 0 && buffer < statbuf.st_size)
+ {
+ /* This should speed things up without consuming much
+ extra memory (because probably little of the space is going
+ to be reused anyway, whether in data or stack space).
+
+ A quick test (running GDB on itself and setting 9 breakpoints
+ in different files) showed that memory usage was almost
+ identical for the two cases. */
+#if 0
+#ifdef BROKEN_LARGE_ALLOCA
+ stringtab = (char *) xmalloc (buffer);
+ make_cleanup (free, stringtab);
+#else
+ stringtab = (char *) alloca (buffer);
+#endif
+#endif
+ stringtab = (char *) xmalloc (buffer);
+ symfile_string_table = stringtab;
+ symfile_string_table_size = buffer;
+ }
+ else
+ stringtab = NULL;
+ if (stringtab == NULL)
+ error ("ridiculous string table size: %d bytes", buffer);
+
+ /* Usually READ_STRING_TABLE_SIZE will have shifted the file pointer.
+ Occaisionally, it won't. */
+ val = lseek (desc, STRING_TABLE_OFFSET, L_SET);
+ if (val < 0)
+ perror_with_name (name);
+ val = myread (desc, stringtab, buffer);
+ if (val < 0)
+ perror_with_name (name);
+
+ /* Throw away the old symbol table. */
+
+ if (symfile)
+ free (symfile);
+ symfile = 0;
+ free_all_symtabs ();
+ free_all_psymtabs ();
+
+ /* Empty the hash table of global syms looking for values. */
+ bzero (global_sym_chain, sizeof global_sym_chain);
+
+ /* Symsegs are no longer supported by GDB. Setting symseg_chain to
+ 0 is easier than finding all the symseg code and eliminating it. */
+ symseg_chain = 0;
+
+ /* Position to read the symbol table. Do not read it all at once. */
+ val = lseek (desc, SYMBOL_TABLE_OFFSET, 0);
+ if (val < 0)
+ perror_with_name (name);
+
+ /* Don't put these on the cleanup chain; they need to stick around
+ until the next call to symbol_file_command. *Then* we'll free
+ them. */
+ free_header_files ();
+ init_header_files ();
+
+ init_misc_functions ();
+ make_cleanup (discard_misc_bunches, 0);
+
+ free_pendings = 0;
+ pending_blocks = 0;
+ file_symbols = 0;
+ global_symbols = 0;
+ make_cleanup (really_free_pendings, 0);
+
+ /* Now that the symbol table data of the executable file are all in core,
+ process them and define symbols accordingly. Closes desc. */
+
+ read_dbx_symtab (desc, stringtab, buffer, NUMBER_OF_SYMBOLS, 0,
+ ADDR_OF_TEXT_SEGMENT, SIZE_OF_TEXT_SEGMENT);
+
+ /* Go over the misc functions and install them in vector. */
+
+ condense_misc_bunches (0);
+
+ /* Don't allow char * to have a typename (else would get caddr_t.) */
+
+ TYPE_NAME (lookup_pointer_type (builtin_type_char)) = 0;
+
+ /* Make a default for file to list. */
+
+ symfile = savestring (name, strlen (name));
+
+ /* Call to select_source_symtab used to be here; it was using too
+ much time. I'll make sure that list_sources can handle the lack
+ of current_source_symtab */
+
+ do_cleanups (old_chain); /* Descriptor closed here */
+
+ /* Free the symtabs made by read_symsegs, but not their contents,
+ which have been copied into symtabs on symtab_list. */
+ while (symseg_chain)
+ {
+ register struct symtab *s = symseg_chain->next;
+ free (symseg_chain);
+ symseg_chain = s;
+ }
+
+ if (!partial_symtab_list)
+ printf ("\n(no debugging symbols found)...");
+
+ printf ("done.\n");
+ fflush (stdout);
+}
+
+/* Return name of file symbols were loaded from, or 0 if none.. */
+
+char *
+get_sym_file ()
+{
+ return symfile;
+}
+
+/* Buffer for reading the symbol table entries. */
+static struct nlist symbuf[4096];
+static int symbuf_idx;
+static int symbuf_end;
+
+/* I/O descriptor for reading the symbol table. */
+static int symtab_input_desc;
+
+/* The address of the string table
+ of the object file we are reading (as copied into core). */
+static char *stringtab_global;
+
+/* Refill the symbol table input buffer
+ and set the variables that control fetching entries from it.
+ Reports an error if no data available.
+ This function can read past the end of the symbol table
+ (into the string table) but this does no harm. */
+
+static int
+fill_symbuf ()
+{
+ int nbytes = myread (symtab_input_desc, symbuf, sizeof (symbuf));
+ if (nbytes <= 0)
+ error ("error or end of file reading symbol table");
+ symbuf_end = nbytes / sizeof (struct nlist);
+ symbuf_idx = 0;
+ return 1;
+}
+
+/* dbx allows the text of a symbol name to be continued into the
+ next symbol name! When such a continuation is encountered
+ (a \ at the end of the text of a name)
+ call this function to get the continuation. */
+
+static char *
+next_symbol_text ()
+{
+ if (symbuf_idx == symbuf_end)
+ fill_symbuf ();
+ symnum++;
+ return symbuf[symbuf_idx++].n_un.n_strx + stringtab_global;
+}
+
+/*
+ * Initializes storage for all of the partial symbols that will be
+ * created by read_dbx_symtab and subsidiaries.
+ */
+void
+init_psymbol_list (total_symbols)
+ int total_symbols;
+{
+ /* Current best guess is that there are approximately a twentieth
+ of the total symbols (in a debugging file) are global or static
+ oriented symbols */
+ global_psymbols.size = total_symbols / 10;
+ static_psymbols.size = total_symbols / 10;
+ global_psymbols.next = global_psymbols.list = (struct partial_symbol *)
+ xmalloc (global_psymbols.size * sizeof (struct partial_symbol));
+ static_psymbols.next = static_psymbols.list = (struct partial_symbol *)
+ xmalloc (static_psymbols.size * sizeof (struct partial_symbol));
+}
+
+/*
+ * Initialize the list of bincls to contain none and have some
+ * allocated.
+ */
+static void
+init_bincl_list (number)
+ int number;
+{
+ bincls_allocated = number;
+ next_bincl = bincl_list = (struct header_file_location *)
+ xmalloc (bincls_allocated * sizeof(struct header_file_location));
+}
+
+/*
+ * Add a bincl to the list.
+ */
+static void
+add_bincl_to_list (pst, name, instance)
+ struct partial_symtab *pst;
+ char *name;
+ int instance;
+{
+ if (next_bincl >= bincl_list + bincls_allocated)
+ {
+ int offset = next_bincl - bincl_list;
+ bincls_allocated *= 2;
+ bincl_list = (struct header_file_location *)
+ xrealloc (bincl_list,
+ bincls_allocated * sizeof (struct header_file_location));
+ next_bincl = bincl_list + offset;
+ }
+ next_bincl->pst = pst;
+ next_bincl->instance = instance;
+ next_bincl++->name = name;
+}
+
+/*
+ * Given a name, value pair, find the corresponding
+ * bincl in the list. Return the partial symtab associated
+ * with that header_file_location.
+ */
+struct partial_symtab *
+find_corresponding_bincl_psymtab (name, instance)
+ char *name;
+ int instance;
+{
+ struct header_file_location *bincl;
+
+ for (bincl = bincl_list; bincl < next_bincl; bincl++)
+ if (bincl->instance == instance
+ && !strcmp (name, bincl->name))
+ return bincl->pst;
+
+ return (struct partial_symtab *) 0;
+}
+
+/*
+ * Free the storage allocated for the bincl list.
+ */
+static void
+free_bincl_list ()
+{
+ free (bincl_list);
+ bincls_allocated = 0;
+}
+
+static struct partial_symtab *start_psymtab ();
+static void add_psymtab_dependency ();
+static void end_psymtab();
+
+static int
+compare_psymbols (s1, s2)
+ register struct partial_symbol *s1, *s2;
+{
+ register char
+ *st1 = SYMBOL_NAME (s1),
+ *st2 = SYMBOL_NAME (s2);
+ register int i;
+
+ if (st1[0] - st2[0])
+ return (st1[0] - st2[0]);
+ if (st1[1] - st2[1])
+ return (st1[1] - st2[1]);
+ if (i = strcmp(st1, st2))
+ return (i);
+ /* Next comparison implements policy that used to be in lookup_symbol:
+ * it would search psymtabs in psymtab_list order (reverse order of
+ * declaration) & take first occurance of symbol it found. So, we
+ * collate duplicate names in reverse psymtab order. */
+ return (s2->pst - s1->pst);
+}
+
+/* Given pointers to an a.out symbol table in core containing dbx
+ style data, setup partial_symtab's describing each source file for
+ which debugging information is available. NLISTLEN is the number
+ of symbols in the symbol table. All symbol names are given as
+ offsets relative to STRINGTAB. STRINGTAB_SIZE is the size of
+ STRINGTAB.
+
+ I have no idea whether or not this routine should be setup to deal
+ with inclinks. It seems reasonable to me that they be dealt with
+ standardly, so I am not going to make a strong effort to deal with
+ them here.
+ */
+
+static void
+read_dbx_symtab (desc, stringtab, stringtab_size, nlistlen, inclink,
+ text_addr, text_size)
+ int desc;
+ register char *stringtab;
+ register long stringtab_size;
+ register int nlistlen;
+ int inclink;
+ unsigned text_addr;
+ int text_size;
+{
+ register struct nlist *bufp;
+ register char *namestring;
+ register struct partial_symbol *psym;
+ register struct psymbol_allocation_list *psymbol_struct;
+
+ int nsl;
+ int past_first_source_file = 0;
+ CORE_ADDR last_o_file_start = 0;
+ char *last_o_file_name = "*bogus*";
+ struct cleanup *old_chain;
+ char *p;
+ enum namespace ns;
+ enum address_class class;
+
+#ifdef PROFILE_TYPES
+ int i;
+ int profile_types [256];
+ int strcmp_called = 0;
+ int autovars = 0;
+ int global_funs = 0;
+#endif
+
+ /* Current partial symtab */
+ struct partial_symtab *pst;
+
+ /* List of current psymtab's include files */
+ char **psymtab_include_list;
+ int includes_allocated;
+ int includes_used;
+
+ /* Index within current psymtab dependency list */
+ struct partial_symtab **dependency_list;
+ int dependencies_used, dependencies_allocated;
+
+#ifdef PROFILE_TYPES
+ for (i = 0; i < 256; i++)
+ profile_types[i] = 0;
+#endif
+
+ stringtab_global = stringtab;
+
+ pst = (struct partial_symtab *) 0;
+
+ includes_allocated = 30;
+ includes_used = 0;
+ psymtab_include_list = (char **) alloca (includes_allocated *
+ sizeof (char *));
+
+ dependencies_allocated = 30;
+ dependencies_used = 0;
+ dependency_list =
+ (struct partial_symtab **) alloca (dependencies_allocated *
+ sizeof (struct partial_symtab *));
+
+ old_chain = make_cleanup (free_all_psymtabs, 0);
+
+ /* Init bincl list */
+ init_bincl_list (20);
+ make_cleanup (free_bincl_list, 0);
+
+ /* Setup global partial symbol list */
+ init_psymbol_list (nlistlen);
+
+ last_source_file = 0;
+
+#ifdef END_OF_TEXT_DEFAULT
+ end_of_text_addr = END_OF_TEXT_DEFAULT;
+#else
+ end_of_text_addr = text_addr + text_size;
+#endif
+
+ symtab_input_desc = desc; /* This is needed for fill_symbuf below */
+ symbuf_end = symbuf_idx = 0;
+
+ for (symnum = 0; symnum < nlistlen; symnum++)
+ {
+ /* Get the symbol for this run and pull out some info */
+ QUIT; /* allow this to be interruptable */
+ if (symbuf_idx == symbuf_end)
+ fill_symbuf ();
+ bufp = &symbuf[symbuf_idx++];
+
+#ifdef PROFILE_TYPES
+ profile_types[bufp->n_type]++;
+#endif
+
+ /*
+ * Special case to speed up readin.
+ */
+ if (bufp->n_type == N_SLINE) continue;
+
+ /* Ok. There is a lot of code duplicated in the rest of this
+ switch statiement (for efficiency reasons). Since I don't
+ like duplicating code, I will do my penance here, and
+ describe the code which is duplicated:
+
+ *) The assignment to namestring.
+ *) The call to index.
+ *) The addition of a partial symbol the the two partial
+ symbol lists. This last is a large section of code, so
+ I've imbedded it in the following macro.
+ */
+
+/* Set namestring based on bufp. */
+#define SET_NAMESTRING()\
+ if (bufp->n_un.n_strx < 0 || bufp->n_un.n_strx >= stringtab_size) \
+ error ("Invalid symbol data: bad string table offset: %d", \
+ bufp->n_un.n_strx); \
+ namestring = bufp->n_un.n_strx + stringtab
+
+#define ADD_PSYMBOL_TO_LIST(NAME, NAMELENGTH, NAMESPACE, CLASS, LIST, VALUE)\
+ do { \
+ if ((LIST).next >= \
+ (LIST).list + (LIST).size) \
+ { \
+ (LIST).list = (struct partial_symbol *) \
+ xrealloc ((LIST).list, \
+ ((LIST).size * 2 \
+ * sizeof (struct partial_symbol))); \
+ /* Next assumes we only went one over. Should be good if \
+ program works correctly */ \
+ (LIST).next = \
+ (LIST).list + (LIST).size; \
+ (LIST).size *= 2; \
+ } \
+ psym = (LIST).next++; \
+ \
+ SYMBOL_NAME (psym) = (char *) obstack_alloc (psymbol_obstack, \
+ (NAMELENGTH) + 1); \
+ strncpy (SYMBOL_NAME (psym), (NAME), (NAMELENGTH)); \
+ SYMBOL_NAME (psym)[(NAMELENGTH)] = '\0'; \
+ SYMBOL_NAMESPACE (psym) = (NAMESPACE); \
+ SYMBOL_CLASS (psym) = (CLASS); \
+ SYMBOL_VALUE (psym) = (VALUE); \
+ } while (0);
+
+
+ switch (bufp->n_type)
+ {
+ /*
+ * Standard, non-debugger, symbols
+ */
+
+ case N_TEXT | N_EXT:
+ /* Catch etext */
+
+ SET_NAMESTRING();
+
+ if (namestring[6] == '\0' && namestring[5] == 't'
+ && namestring[4] == 'x' && namestring[3] == 'e'
+ && namestring[2] == 't' && namestring[1] == 'e'
+ && namestring[0] == '_')
+ end_of_text_addr = bufp->n_value;
+
+ /* Figure out beginning and end of global linker symbol
+ section and put non-debugger specified symbols on
+ tmp_symchain */
+
+ last_global_sym = symnum;
+ if (!first_global_sym) first_global_sym = symnum;
+
+ record_misc_function (namestring, bufp->n_value,
+ bufp->n_type); /* Always */
+
+ continue;
+
+#ifdef N_NBTEXT
+ case N_NBTEXT | N_EXT:
+#endif
+#ifdef N_NBDATA
+ case N_NBDATA | N_EXT:
+#endif
+#ifdef N_NBBSS
+ case N_NBBSS | N_EXT:
+#endif
+#ifdef N_SETV
+ case N_SETV | N_EXT:
+#endif
+ case N_ABS | N_EXT:
+ case N_DATA | N_EXT:
+ case N_BSS | N_EXT:
+ /* Figure out beginning and end of global linker symbol
+ section and put non-debugger specified symbols on
+ tmp_symchain */
+
+ SET_NAMESTRING();
+
+ last_global_sym = symnum;
+ if (!first_global_sym) first_global_sym = symnum;
+
+ /* Not really a function here, but... */
+ record_misc_function (namestring, bufp->n_value,
+ bufp->n_type); /* Always */
+
+ continue;
+
+#ifdef N_NBTEXT
+ case N_NBTEXT:
+#endif
+
+ /* We need to be able to deal with both N_FN or N_TEXT,
+ because we have no way of knowing whether the sys-supplied ld
+ or GNU ld was used to make the executable. */
+#if ! (N_FN & N_EXT)
+ case N_FN:
+#endif
+ case N_FN | N_EXT:
+ case N_TEXT:
+ SET_NAMESTRING();
+ if ((namestring[0] == '-' && namestring[1] == 'l')
+ || (namestring [(nsl = strlen (namestring)) - 1] == 'o'
+ && namestring [nsl - 2] == '.'))
+ {
+ if (entry_point < bufp->n_value
+ && entry_point >= last_o_file_start)
+ {
+ startup_file_start = last_o_file_start;
+ startup_file_end = bufp->n_value;
+ }
+ if (past_first_source_file && pst)
+ {
+ end_psymtab (pst, psymtab_include_list, includes_used,
+ symnum * sizeof (struct nlist), bufp->n_value,
+ dependency_list, dependencies_used,
+ global_psymbols.next, static_psymbols.next);
+ pst = (struct partial_symtab *) 0;
+ includes_used = 0;
+ dependencies_used = 0;
+ }
+ else
+ past_first_source_file = 1;
+ last_o_file_start = bufp->n_value;
+ last_o_file_name = namestring;
+ nsl = strlen(namestring);
+ if (namestring[nsl-2] == '.' && namestring[nsl-1] == 'o')
+ namestring[nsl-2] = 0;
+ }
+ else if (strcmp(namestring, "gcc_compiled."))
+ {
+ if (*namestring == '_')
+ ++namestring;
+ namestring = obconcat(last_o_file_name, ":", namestring);
+ last_global_sym = symnum;
+ if (!first_global_sym)
+ first_global_sym = symnum;
+ record_misc_function(namestring, bufp->n_value, bufp->n_type);
+ }
+ continue;
+
+ case N_ABS:
+ case N_DATA:
+ case N_BSS:
+ SET_NAMESTRING();
+ if (*namestring == '_')
+ ++namestring;
+ namestring = obconcat(last_o_file_name, ":", namestring);
+ last_global_sym = symnum;
+ if (!first_global_sym)
+ first_global_sym = symnum;
+ record_misc_function(namestring, bufp->n_value, bufp->n_type);
+ continue;
+
+ case N_UNDF:
+ case N_UNDF | N_EXT:
+#ifdef N_NBDATA
+ case N_NBDATA:
+#endif
+#ifdef N_NBBSS
+ case N_NBBSS:
+#endif
+
+ /* Keep going . . .*/
+
+ /*
+ * Special symbol types for GNU
+ */
+#ifdef N_INDR
+ case N_INDR:
+ case N_INDR | N_EXT:
+#endif
+#ifdef N_SETA
+ case N_SETA:
+ case N_SETA | N_EXT:
+ case N_SETT:
+ case N_SETT | N_EXT:
+ case N_SETD:
+ case N_SETD | N_EXT:
+ case N_SETB:
+ case N_SETB | N_EXT:
+ case N_SETV:
+#endif
+ continue;
+
+ /*
+ * Debugger symbols
+ */
+
+ case N_SO:
+ /* End the current partial symtab and start a new one */
+
+ SET_NAMESTRING();
+
+ if (pst && past_first_source_file)
+ {
+ end_psymtab (pst, psymtab_include_list, includes_used,
+ symnum * sizeof (struct nlist), bufp->n_value,
+ dependency_list, dependencies_used,
+ global_psymbols.next, static_psymbols.next);
+ pst = (struct partial_symtab *) 0;
+ includes_used = 0;
+ dependencies_used = 0;
+ }
+ else
+ past_first_source_file = 1;
+
+ pst = start_psymtab (namestring, bufp->n_value,
+ symnum * sizeof (struct nlist),
+ global_psymbols.next, static_psymbols.next);
+
+ continue;
+
+#ifdef N_BINCL
+ case N_BINCL:
+ /* Add this bincl to the bincl_list for future EXCLs. No
+ need to save the string; it'll be around until
+ read_dbx_symtab function return */
+
+ SET_NAMESTRING();
+
+ add_bincl_to_list (pst, namestring, bufp->n_value);
+
+ /* Mark down an include file in the current psymtab */
+
+ psymtab_include_list[includes_used++] = namestring;
+ if (includes_used >= includes_allocated)
+ {
+ char **orig = psymtab_include_list;
+
+ psymtab_include_list = (char **)
+ alloca ((includes_allocated *= 2) *
+ sizeof (char *));
+ bcopy (orig, psymtab_include_list,
+ includes_used * sizeof (char *));
+ }
+
+ continue;
+#endif
+
+ case N_SOL:
+ /* Mark down an include file in the current psymtab */
+
+ SET_NAMESTRING();
+
+ /* In C++, one may expect the same filename to come round many
+ times, when code is coming alternately from the main file
+ and from inline functions in other files. So I check to see
+ if this is a file we've seen before.
+
+ This seems to be a lot of time to be spending on N_SOL, but
+ things like "break expread.y:435" need to work (I
+ suppose the psymtab_include_list could be hashed or put
+ in a binary tree, if profiling shows this is a major hog). */
+ {
+ register int i;
+ for (i = 0; i < includes_used; i++)
+ if (!strcmp (namestring, psymtab_include_list[i]))
+ {
+ i = -1;
+ break;
+ }
+ if (i == -1)
+ continue;
+ }
+
+ psymtab_include_list[includes_used++] = namestring;
+ if (includes_used >= includes_allocated)
+ {
+ char **orig = psymtab_include_list;
+
+ psymtab_include_list = (char **)
+ alloca ((includes_allocated *= 2) *
+ sizeof (char *));
+ bcopy (orig, psymtab_include_list,
+ includes_used * sizeof (char *));
+ }
+ continue;
+
+ case N_LSYM: /* Typedef or automatic variable. */
+ SET_NAMESTRING();
+
+ p = (char *) index (namestring, ':');
+
+ /* Skip if there is no :. */
+ if (!p) continue;
+
+ switch (p[1])
+ {
+ case 'T':
+ ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+ STRUCT_NAMESPACE, LOC_TYPEDEF,
+ static_psymbols, bufp->n_value);
+ goto check_enum;
+ case 't':
+ ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+ VAR_NAMESPACE, LOC_TYPEDEF,
+ static_psymbols, bufp->n_value);
+ check_enum:
+ /* If this is an enumerated type, we need to
+ add all the enum constants to the partial symbol
+ table. This does not cover enums without names, e.g.
+ "enum {a, b} c;" in C, but fortunately those are
+ rare. There is no way for GDB to find those from the
+ enum type without spending too much time on it. Thus
+ to solve this problem, the compiler needs to put out separate
+ constant symbols ('c' N_LSYMS) for enum constants in
+ enums without names. */
+
+ /* We are looking for something of the form
+ <name> ":" ("t" | "T") [<number> "="] "e"
+ {<constant> ":" <value> ","} ";". */
+
+ /* Skip over the colon and the 't' or 'T'. */
+ p += 2;
+ /* This type may be given a number. Skip over it. */
+ while ((*p >= '0' && *p <= '9')
+ || *p == '=')
+ p++;
+
+ if (*p++ == 'e')
+ {
+ /* We have found an enumerated type. */
+ /* According to comments in read_enum_type
+ a comma could end it instead of a semicolon.
+ I don't know where that happens.
+ Accept either. */
+ while (*p && *p != ';' && *p != ',')
+ {
+ char *q;
+
+ /* Check for and handle cretinous dbx symbol name
+ continuation! */
+ if (*p == '\\')
+ p = next_symbol_text ();
+
+ /* Point to the character after the name
+ of the enum constant. */
+ for (q = p; *q && *q != ':'; q++)
+ ;
+ /* Note that the value doesn't matter for
+ enum constants in psymtabs, just in symtabs. */
+ ADD_PSYMBOL_TO_LIST (p, q - p,
+ VAR_NAMESPACE, LOC_CONST,
+ static_psymbols, 0);
+ /* Point past the name. */
+ p = q;
+ /* Skip over the value. */
+ while (*p && *p != ',')
+ p++;
+ /* Advance past the comma. */
+ if (*p)
+ p++;
+ }
+ }
+
+ continue;
+ case 'c':
+ /* Constant, e.g. from "const" in Pascal. */
+ ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+ VAR_NAMESPACE, LOC_CONST,
+ static_psymbols, bufp->n_value);
+ continue;
+ default:
+#ifdef PROFILE_TYPES
+ if (isalpha(p[1]))
+ printf ("Funny...LSYM with a letter that isn't a type\n");
+ autovars++;
+#endif
+ /* Skip if the thing following the : is
+ not a letter (which indicates declaration of a local
+ variable, which we aren't interested in). */
+ continue;
+ }
+
+ case N_FUN:
+#if 0
+ /* This special-casing of N_FUN is just wrong; N_FUN
+ does not mean "function"; it means "text segment".
+ So N_FUN can go with 'V', etc. as well as 'f' or 'F'. */
+
+ SET_NAMESTRING();
+
+ p = (char *) index (namestring, ':');
+
+ if (!p || p[1] == 'F') continue;
+
+#ifdef PROFILE_TYPES
+ if (p[1] != 'f')
+ printf ("Funny...FUN with a letter that isn't 'F' or 'f'.\n");
+ global_funs++;
+#endif
+
+ ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+ VAR_NAMESPACE, LOC_BLOCK,
+ static_psymbols, bufp->n_value);
+
+ continue;
+#endif /* 0 */
+ case N_GSYM: /* Global (extern) variable; can be
+ data or bss (sigh). */
+ case N_STSYM: /* Data seg var -- static */
+ case N_LCSYM: /* BSS " */
+
+ /* Following may probably be ignored; I'll leave them here
+ for now (until I do Pascal and Modula 2 extensions). */
+
+ case N_PC: /* I may or may not need this; I
+ suspect not. */
+#ifdef N_M2C
+ case N_M2C: /* I suspect that I can ignore this here. */
+ case N_SCOPE: /* Same. */
+#endif
+
+ SET_NAMESTRING();
+
+ p = (char *) index (namestring, ':');
+ if (!p)
+ continue; /* Not a debugging symbol. */
+
+ process_symbol_for_psymtab:
+
+ /* Main processing section for debugging symbols which
+ the initial read through the symbol tables needs to worry
+ about. If we reach this point, the symbol which we are
+ considering is definitely one we are interested in.
+ p must also contain the (valid) index into the namestring
+ which indicates the debugging type symbol. */
+
+ switch (p[1])
+ {
+ case 'c':
+ ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+ VAR_NAMESPACE, LOC_CONST,
+ static_psymbols, bufp->n_value);
+ continue;
+ case 'S':
+ ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+ VAR_NAMESPACE, LOC_STATIC,
+ static_psymbols, bufp->n_value);
+ continue;
+ case 'G':
+ ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+ VAR_NAMESPACE, LOC_EXTERNAL,
+ global_psymbols, bufp->n_value);
+ continue;
+
+ case 't':
+ ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+ VAR_NAMESPACE, LOC_TYPEDEF,
+ global_psymbols, bufp->n_value);
+ continue;
+
+ case 'f':
+ ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+ VAR_NAMESPACE, LOC_BLOCK,
+ static_psymbols, bufp->n_value);
+ continue;
+
+ /* Two things show up here (hopefully); static symbols of
+ local scope (static used inside braces) or extensions
+ of structure symbols. We can ignore both. */
+ case 'V':
+ case '(':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ /* Global functions are ignored here. I'm not
+ sure what psymtab they go into (or just the misc
+ function vector). */
+ case 'F':
+ continue;
+
+ default:
+ fatal ("Internal error: Unexpected debugging symbol type '%c' at symnum %d.\n",
+ p[1], symnum);
+ }
+
+#ifdef N_BINCL
+ case N_EXCL:
+
+ SET_NAMESTRING();
+
+ /* Find the corresponding bincl and mark that psymtab on the
+ psymtab dependency list */
+ {
+ struct partial_symtab *needed_pst =
+ find_corresponding_bincl_psymtab (namestring, bufp->n_value);
+
+ /* If this include file was defined earlier in this file,
+ leave it alone. */
+ if (needed_pst == pst) continue;
+
+ if (needed_pst)
+ {
+ int i;
+ int found = 0;
+
+ for (i = 0; i < dependencies_used; i++)
+ if (dependency_list[i] == needed_pst)
+ {
+ found = 1;
+ break;
+ }
+
+ /* If it's already in the list, skip the rest. */
+ if (found) continue;
+
+ dependency_list[dependencies_used++] = needed_pst;
+ if (dependencies_used >= dependencies_allocated)
+ {
+ struct partial_symtab **orig = dependency_list;
+ dependency_list =
+ (struct partial_symtab **)
+ alloca ((dependencies_allocated *= 2)
+ * sizeof (struct partial_symtab *));
+ bcopy (orig, dependency_list,
+ (dependencies_used
+ * sizeof (struct partial_symtab *)));
+#ifdef DEBUG_INFO
+ fprintf (stderr, "Had to reallocate dependency list.\n");
+ fprintf (stderr, "New dependencies allocated: %d\n",
+ dependencies_allocated);
+#endif
+ }
+ }
+ else
+ error ("Invalid symbol data: \"repeated\" header file not previously seen, at symtab pos %d.",
+ symnum);
+ }
+ continue;
+
+ case N_EINCL:
+#endif
+#ifdef N_DSLINE
+ case N_DSLINE:
+#endif
+#ifdef N_BSLINE
+ case N_BSLINE:
+#endif
+ case N_SSYM: /* Claim: Structure or union element.
+ Hopefully, I can ignore this. */
+ case N_ENTRY: /* Alternate entry point; can ignore. */
+#ifdef N_MAIN
+ case N_MAIN: /* Can definitely ignore this. */
+#endif
+ case N_LENG:
+ case N_BCOMM:
+ case N_ECOMM:
+ case N_ECOML:
+ case N_FNAME:
+ case N_SLINE:
+ case N_RSYM:
+ case N_PSYM:
+ case N_LBRAC:
+ case N_RBRAC:
+ /* These symbols aren't interesting; don't worry about them */
+
+ continue;
+
+ default:
+ /* If we haven't found it yet, we've got problems */
+
+ if (IGNORE_SYMBOL (bufp->n_type))
+ continue;
+
+ fatal ("Bad symbol type 0x%x encountered in gdb scan", bufp->n_type);
+ }
+ }
+
+ /* If there's stuff to be cleaned up, clean it up. */
+ if (entry_point < bufp->n_value
+ && entry_point >= last_o_file_start)
+ {
+ startup_file_start = last_o_file_start;
+ startup_file_end = bufp->n_value;
+ }
+
+ if (pst)
+ {
+ end_psymtab (pst, psymtab_include_list, includes_used,
+ symnum * sizeof (struct nlist), end_of_text_addr,
+ dependency_list, dependencies_used,
+ global_psymbols.next, static_psymbols.next);
+ includes_used = 0;
+ dependencies_used = 0;
+ pst = (struct partial_symtab *) 0;
+ }
+
+ /* sort the global & static symtab list so we can binary search them */
+ qsort (global_psymbols.list, global_psymbols.next - global_psymbols.list,
+ sizeof (struct partial_symbol), compare_psymbols);
+ qsort (static_psymbols.list, static_psymbols.next - static_psymbols.list,
+ sizeof (struct partial_symbol), compare_psymbols);
+ free_bincl_list ();
+ discard_cleanups (old_chain);
+#ifdef PROFILE_TYPES
+ {
+ int i, j;
+#define __define_stab(SYM, NUMBER, NAME) {NUMBER, NAME},
+ static struct xyzzy {
+ unsigned char symnum;
+ char *name;
+ } tmp_list[] = {
+#include "stab.def"
+ {0x1, "eREF"},
+ {0x2, "ABS"},
+ {0x3, "eABS"},
+ {0x4, "TEXT"},
+ {0x5, "eTEXT"},
+ {0x6, "DATA"},
+ {0x7, "eDATA"},
+ {0x8, "BSS"},
+ {0x9, "eBSS"},
+ {0x12, "COMM"},
+ {0x13, "eCOMM"},
+ {0x1f, "FN"},
+ {0, "Unknown"},
+};
+ for (i = 0; i < 256; i++)
+ {
+ for (j = 0; j < (sizeof (tmp_list) / sizeof (struct xyzzy)) - 1; j++)
+ if (tmp_list[j].symnum == i)
+ break;
+ printf ("Symbol \"%s\" (0x%x) occured %d times.\n",
+ tmp_list[j].name, i, profile_types[i]);
+ }
+ printf ("Auto vars (under LSYM): %d\n", autovars);
+ printf ("Global funs (under FUN): %d\n", global_funs);
+ }
+#endif
+}
+
+/*
+ * Allocate and partially fill a partial symtab. It will be
+ * completely filled at the end of the symbol list.
+ */
+static struct partial_symtab *
+start_psymtab (filename, textlow, ldsymoff, global_syms, static_syms)
+ char *filename;
+ int textlow;
+ int ldsymoff;
+ struct partial_symbol *global_syms;
+ struct partial_symbol *static_syms;
+{
+ struct partial_symtab *result =
+ (struct partial_symtab *) obstack_alloc (psymbol_obstack,
+ sizeof (struct partial_symtab));
+
+ result->filename =
+ (char *) obstack_alloc (psymbol_obstack,
+ strlen (filename) + 1);
+ strcpy (result->filename, filename);
+
+ result->textlow = textlow;
+ result->ldsymoff = ldsymoff;
+
+ result->readin = 0;
+
+ result->globals_offset = global_syms - global_psymbols.list;
+ result->statics_offset = static_syms - static_psymbols.list;
+
+ result->n_global_syms = 0;
+ result->n_static_syms = 0;
+
+ return result;
+}
+
+
+/* Close off the current usage of a partial_symbol table entry. This
+ involves setting the correct number of includes (with a realloc),
+ setting the high text mark, setting the symbol length in the
+ executable, and setting the length of the global and static lists
+ of psymbols.
+
+ The global symbols and static symbols are then seperately sorted.
+
+ Then the partial symtab is put on the global list.
+ *** List variables and peculiarities of same. ***
+ */
+static void
+end_psymtab (pst, include_list, num_includes, capping_symbol_offset,
+ capping_text, dependency_list, number_dependencies,
+ capping_global, capping_static)
+ struct partial_symtab *pst;
+ char **include_list;
+ int num_includes;
+ int capping_symbol_offset;
+ int capping_text;
+ struct partial_symtab **dependency_list;
+ int number_dependencies;
+ struct partial_symbol *capping_global, *capping_static;
+{
+ int i;
+ register struct partial_symbol *ps;
+
+ pst->ldsymlen = capping_symbol_offset - pst->ldsymoff;
+ pst->texthigh = capping_text;
+
+ pst->n_global_syms =
+ capping_global - (global_psymbols.list + pst->globals_offset);
+ pst->n_static_syms =
+ capping_static - (static_psymbols.list + pst->statics_offset);
+
+ pst->dependencies = (struct partial_symtab **)
+ obstack_alloc (psymbol_obstack,
+ number_dependencies * sizeof (struct partial_symtab *));
+ bcopy (dependency_list, pst->dependencies,
+ number_dependencies * sizeof (struct partial_symtab *));
+ pst->number_of_dependencies = number_dependencies;
+
+ for (i = 0; i < num_includes; i++)
+ {
+ /* Eventually, put this on obstack */
+ struct partial_symtab *subpst =
+ (struct partial_symtab *)
+ obstack_alloc (psymbol_obstack,
+ sizeof (struct partial_symtab));
+
+ subpst->filename =
+ (char *) obstack_alloc (psymbol_obstack,
+ strlen (include_list[i]) + 1);
+ strcpy (subpst->filename, include_list[i]);
+
+ subpst->ldsymoff =
+ subpst->ldsymlen =
+ subpst->textlow =
+ subpst->texthigh = 0;
+ subpst->readin = 0;
+
+ subpst->dependencies = (struct partial_symtab **)
+ obstack_alloc (psymbol_obstack,
+ sizeof (struct partial_symtab *));
+ subpst->dependencies[0] = pst;
+ subpst->number_of_dependencies = 1;
+
+ subpst->globals_offset =
+ subpst->n_global_syms =
+ subpst->statics_offset =
+ subpst->n_static_syms = 0;
+
+ subpst->next = partial_symtab_list;
+ partial_symtab_list = subpst;
+ }
+
+ for (ps = global_psymbols.list + pst->globals_offset;
+ ps < capping_global; ++ps)
+ ps->pst = pst;
+ for (ps = static_psymbols.list + pst->statics_offset;
+ ps < capping_static; ++ps)
+ ps->pst = pst;
+
+ /* Put the psymtab on the psymtab list */
+ pst->next = partial_symtab_list;
+ partial_symtab_list = pst;
+}
+
+
+/* Helper routines for psymtab_to_symtab. */
+static void scan_file_globals ();
+static void read_ofile_symtab ();
+
+static void
+psymtab_to_symtab_1 (pst, desc, stringtab, stringtab_size, sym_offset)
+ struct partial_symtab *pst;
+ int desc;
+ char *stringtab;
+ int stringtab_size;
+ int sym_offset;
+{
+ struct cleanup *old_chain;
+ int i;
+
+ if (!pst)
+ return;
+
+ if (pst->readin)
+ {
+ fprintf (stderr, "Psymtab for %s already read in. Shouldn't happen.\n",
+ pst->filename);
+ return;
+ }
+
+ /* Read in all partial symbtabs on which this one is dependent */
+ for (i = 0; i < pst->number_of_dependencies; i++)
+ if (!pst->dependencies[i]->readin)
+ {
+ /* Inform about additional files that need to be read in. */
+ if (info_verbose)
+ {
+ printf_filtered (" and %s...", pst->dependencies[i]->filename);
+ fflush (stdout);
+ }
+ psymtab_to_symtab_1 (pst->dependencies[i], desc,
+ stringtab, stringtab_size, sym_offset);
+ }
+
+ if (pst->ldsymlen) /* Otherwise it's a dummy */
+ {
+ /* Init stuff necessary for reading in symbols */
+ free_pendings = 0;
+ pending_blocks = 0;
+ file_symbols = 0;
+ global_symbols = 0;
+ old_chain = make_cleanup (really_free_pendings, 0);
+
+ /* Read in this files symbols */
+ lseek (desc, sym_offset, L_SET);
+ read_ofile_symtab (desc, stringtab, stringtab_size,
+ pst->ldsymoff,
+ pst->ldsymlen, pst->textlow,
+ pst->texthigh - pst->textlow, 0);
+ sort_symtab_syms (symtab_list); /* At beginning since just added */
+
+ do_cleanups (old_chain);
+ }
+
+ pst->readin = 1;
+}
+
+/*
+ * Read in all of the symbols for a given psymtab for real. Return
+ * the value of the symtab you create. Do not free the storage
+ * allocated to the psymtab; it may have pointers to it.
+ */
+struct symtab *
+psymtab_to_symtab(pst)
+ struct partial_symtab *pst;
+{
+ int desc;
+ DECLARE_FILE_HEADERS;
+ char *stringtab;
+ struct partial_symtab **list_patch;
+ int stsize, val;
+ struct stat statbuf;
+ struct cleanup *old_chain;
+ extern void close ();
+ int i;
+ struct symtab *result;
+ char *name = symfile; /* Some of the macros require the */
+ /* variable "name" to be defined in */
+ /* the context in which they execute */
+ /* (Yech!) */
+
+ if (!pst)
+ return 0;
+
+ if (pst->readin)
+ {
+ fprintf (stderr, "Psymtab for %s already read in. Shouldn't happen.\n",
+ pst->filename);
+ return 0;
+ }
+
+ if (!name)
+ error("No symbol file currently specified; use command symbol-file");
+
+ if (pst->ldsymlen || pst->number_of_dependencies)
+ {
+ /* Print the message now, before reading the string table,
+ to avoid disconcerting pauses. */
+ if (info_verbose)
+ {
+ printf_filtered ("Reading in symbols for %s...", pst->filename);
+ fflush (stdout);
+ }
+
+ /* Open symbol file and read in string table */
+ if (stat (name, &statbuf) < 0)
+ perror_with_name (name);
+ desc = open(name, O_RDONLY, 0); /* symbol_file_command
+ guarrantees that the symbol file name
+ will be absolute, so there is no
+ need for openp */
+
+ old_chain = make_cleanup (close, desc);
+
+ if (desc < 0)
+ error("Symbol file not readable");
+
+ READ_FILE_HEADERS (desc, name);
+
+#if 0
+ /* Read in the string table */
+ lseek (desc, STRING_TABLE_OFFSET, L_SET);
+ READ_STRING_TABLE_SIZE (stsize);
+ if (stsize >= 0 && stsize < statbuf.st_size)
+ {
+#ifdef BROKEN_LARGE_ALLOCA
+ stringtab = (char *) xmalloc (stsize);
+ make_cleanup (free, stringtab);
+#else
+ stringtab = (char *) alloca (stsize);
+#endif
+ }
+ else
+ stringtab = NULL;
+ if (stringtab == NULL)
+ error ("ridiculous string table size: %d bytes", stsize);
+
+ /* Usually READ_STRING_TABLE_SIZE will have shifted the file pointer.
+ Occaisionally, it won't. */
+ val = lseek (desc, STRING_TABLE_OFFSET, L_SET);
+ if (val < 0)
+ perror_with_name (name);
+ val = myread (desc, stringtab, stsize);
+ if (val < 0)
+ perror_with_name (name);
+#endif /* 0 */
+ stringtab = symfile_string_table;
+ stsize = symfile_string_table_size;
+
+ psymtab_to_symtab_1 (pst, desc, stringtab, stsize,
+ SYMBOL_TABLE_OFFSET);
+
+ /* Match with global symbols. This only needs to be done once,
+ after all of the symtabs and dependencies have been read in. */
+ scan_file_globals ();
+
+ do_cleanups (old_chain);
+
+ /* Finish up the debug error message. */
+ if (info_verbose)
+ printf_filtered ("done.\n");
+ }
+
+ /* Search through list for correct name. */
+ for (result = symtab_list; result; result = result->next)
+ if (!strcmp (result->filename, pst->filename))
+ return result;
+
+ return 0;
+}
+
+/*
+ * Scan through all of the global symbols defined in the object file,
+ * assigning values to the debugging symbols that need to be assigned
+ * to. Get these symbols from the misc function list.
+ */
+static void
+scan_file_globals ()
+{
+ int hash;
+ int mf;
+
+ for (mf = 0; mf < misc_function_count; mf++)
+ {
+ char *namestring = misc_function_vector[mf].name;
+ struct symbol *sym, *prev;
+
+ QUIT;
+
+ prev = (struct symbol *) 0;
+
+ /* Get the hash index and check all the symbols
+ under that hash index. */
+
+ hash = hashname (namestring);
+
+ for (sym = global_sym_chain[hash]; sym;)
+ {
+ if (*namestring == SYMBOL_NAME (sym)[0]
+ && !strcmp(namestring + 1, SYMBOL_NAME (sym) + 1))
+ {
+ /* Splice this symbol out of the hash chain and
+ assign the value we have to it. */
+ if (prev)
+ SYMBOL_VALUE (prev) = SYMBOL_VALUE (sym);
+ else
+ global_sym_chain[hash]
+ = (struct symbol *) SYMBOL_VALUE (sym);
+
+ /* Check to see whether we need to fix up a common block. */
+ /* Note: this code might be executed several times for
+ the same symbol if there are multiple references. */
+ if (SYMBOL_CLASS (sym) == LOC_BLOCK)
+ fix_common_block (sym, misc_function_vector[mf].address);
+ else
+ SYMBOL_VALUE (sym) = misc_function_vector[mf].address;
+
+ if (prev)
+ sym = (struct symbol *) SYMBOL_VALUE (prev);
+ else
+ sym = global_sym_chain[hash];
+ }
+ else
+ {
+ prev = sym;
+ sym = (struct symbol *) SYMBOL_VALUE (sym);
+ }
+ }
+ }
+}
+
+/*
+ * Read in a defined section of a specific object file's symbols.
+ *
+ * DESC is the file descriptor for the file, positioned at the
+ * beginning of the symtab
+ * STRINGTAB is a pointer to the files string
+ * table, already read in
+ * SYM_OFFSET is the offset within the file of
+ * the beginning of the symbols we want to read, NUM_SUMBOLS is the
+ * number of symbols to read
+ * TEXT_OFFSET is the offset to be added to
+ * all values of symbols coming in and
+ * TEXT_SIZE is the size of the text segment read in.
+ * OFFSET is a flag which indicates that the value of all of the
+ * symbols should be offset by TEXT_OFFSET (for the purposes of
+ * incremental linking).
+ */
+
+static void
+read_ofile_symtab (desc, stringtab, stringtab_size, sym_offset,
+ sym_size, text_offset, text_size, offset)
+ int desc;
+ register char *stringtab;
+ int sym_offset;
+ int sym_size;
+ int text_offset;
+ int text_size;
+ int offset;
+{
+ register char *namestring;
+ register struct symbol *sym, *prev;
+ int hash;
+ struct cleanup *old_chain;
+ struct nlist *bufp;
+ unsigned char type;
+#ifdef N_BINCL
+ subfile_stack = 0;
+#endif
+
+ stringtab_global = stringtab;
+ last_source_file = 0;
+
+ symtab_input_desc = desc;
+ symbuf_end = symbuf_idx = 0;
+
+ /* It is necessary to actually read one symbol *before* the start
+ of this symtab's symbols, because the GCC_COMPILED_FLAG_SYMBOL
+ occurs before the N_SO symbol.
+
+ Detecting this in read_dbx_symtab
+ would slow down initial readin, so we look for it here instead. */
+ if (sym_offset >= sizeof (struct nlist))
+ {
+ lseek (desc, sym_offset - sizeof (struct nlist), L_INCR);
+ fill_symbuf ();
+ bufp = &symbuf[symbuf_idx++];
+
+ if (bufp->n_un.n_strx < 0 || bufp->n_un.n_strx >= stringtab_size)
+ error ("Invalid symbol data: bad string table offset: %d",
+ bufp->n_un.n_strx);
+ namestring = bufp->n_un.n_strx + stringtab;
+
+ processing_gcc_compilation =
+ (bufp->n_type == N_TEXT
+ && !strcmp (namestring, GCC_COMPILED_FLAG_SYMBOL));
+ }
+ else
+ {
+ /* The N_SO starting this symtab is the first symbol, so we
+ better not check the symbol before it. I'm not this can
+ happen, but it doesn't hurt to check for it. */
+ lseek(desc, sym_offset, L_INCR);
+ processing_gcc_compilation = 0;
+ }
+
+ if (symbuf_idx == symbuf_end)
+ fill_symbuf();
+ bufp = &symbuf[symbuf_idx];
+ if ((unsigned char) bufp->n_type != N_SO)
+ fatal("First symbol in segment of executable not a source symbol");
+
+ for (symnum = 0;
+ symnum < sym_size / sizeof(struct nlist);
+ symnum++)
+ {
+ QUIT; /* Allow this to be interruptable */
+ if (symbuf_idx == symbuf_end)
+ fill_symbuf();
+ bufp = &symbuf[symbuf_idx++];
+ type = bufp->n_type;
+
+ if (offset &&
+ (type == N_TEXT || type == N_DATA || type == N_BSS))
+ bufp->n_value += text_offset;
+
+ if (bufp->n_un.n_strx < 0 || bufp->n_un.n_strx >= stringtab_size)
+ error ("Invalid symbol data: bad string table offset: %d",
+ bufp->n_un.n_strx);
+ namestring = bufp->n_un.n_strx + stringtab;
+
+ if (type & N_STAB)
+ process_one_symbol(type, bufp->n_desc,
+ bufp->n_value, namestring);
+ /* We skip checking for a new .o or -l file; that should never
+ happen in this routine. */
+ else if (type == N_TEXT
+ && !strcmp (namestring, GCC_COMPILED_FLAG_SYMBOL))
+ /* I don't think this code will ever be executed, because
+ the GCC_COMPILED_FLAG_SYMBOL usually is right before
+ the N_SO symbol which starts this source file.
+ However, there is no reason not to accept
+ the GCC_COMPILED_FLAG_SYMBOL anywhere. */
+ processing_gcc_compilation = 1;
+ else if (type & N_EXT || type == N_TEXT
+#ifdef N_NBTEXT
+ || type == N_NBTEXT
+#endif
+ )
+ /* Global symbol: see if we came across a dbx defintion for
+ a corresponding symbol. If so, store the value. Remove
+ syms from the chain when their values are stored, but
+ search the whole chain, as there may be several syms from
+ different files with the same name. */
+ /* This is probably not true. Since the files will be read
+ in one at a time, each reference to a global symbol will
+ be satisfied in each file as it appears. So we skip this
+ section. */
+ &stringtab_global; /* For debugger; am I right? */
+ }
+ end_symtab (text_offset + text_size);
+}
+
+static int
+hashname (name)
+ char *name;
+{
+ register char *p = name;
+ register int total = p[0];
+ register int c;
+
+ c = p[1];
+ total += c << 2;
+ if (c)
+ {
+ c = p[2];
+ total += c << 4;
+ if (c)
+ total += p[3] << 6;
+ }
+
+ /* Ensure result is positive. */
+ if (total < 0) total += (1000 << 6);
+ return total % HASHSIZE;
+}
+
+/* Put all appropriate global symbols in the symseg data
+ onto the hash chains so that their addresses will be stored
+ when seen later in loader global symbols. */
+
+static void
+hash_symsegs ()
+{
+ /* Look at each symbol in each block in each symseg symtab. */
+ struct symtab *s;
+ for (s = symseg_chain; s; s = s->next)
+ {
+ register int n;
+ for (n = BLOCKVECTOR_NBLOCKS (BLOCKVECTOR (s)) - 1; n >= 0; n--)
+ {
+ register struct block *b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), n);
+ register int i;
+ for (i = BLOCK_NSYMS (b) - 1; i >= 0; i--)
+ {
+ register struct symbol *sym = BLOCK_SYM (b, i);
+
+ /* Put the symbol on a chain if its value is an address
+ that is figured out by the loader. */
+
+ if (SYMBOL_CLASS (sym) == LOC_EXTERNAL)
+ {
+ register int hash = hashname (SYMBOL_NAME (sym));
+ SYMBOL_VALUE (sym) = (int) global_sym_chain[hash];
+ global_sym_chain[hash] = sym;
+ SYMBOL_CLASS (sym) = LOC_STATIC;
+ }
+ }
+ }
+ }
+}
+
+static void
+process_one_symbol (type, desc, value, name)
+ int type, desc;
+ CORE_ADDR value;
+ char *name;
+{
+ register struct context_stack *new;
+ char *colon_pos;
+
+ /* Something is wrong if we see real data before
+ seeing a source file name. */
+
+ if (last_source_file == 0 && type != N_SO)
+ {
+ /* Currently this ignores N_ENTRY on Gould machines, N_NSYM on machines
+ where that code is defined. */
+ if (IGNORE_SYMBOL (type))
+ return;
+
+ error ("Invalid symbol data: does not start by identifying a source file.");
+ }
+
+ switch (type)
+ {
+ case N_FUN:
+ case N_FNAME:
+ /* Either of these types of symbols indicates the start of
+ a new function. We must process its "name" normally for dbx,
+ but also record the start of a new lexical context, and possibly
+ also the end of the lexical context for the previous function. */
+ /* This is not always true. This type of symbol may indicate a
+ text segment variable. */
+
+ colon_pos = index (name, ':');
+ if (!colon_pos++
+ || (*colon_pos != 'f' && *colon_pos != 'F'))
+ {
+ define_symbol (value, name, desc);
+ break;
+ }
+
+ within_function = 1;
+ if (context_stack_depth > 0)
+ {
+ new = &context_stack[--context_stack_depth];
+ /* Make a block for the local symbols within. */
+ finish_block (new->name, &local_symbols, new->old_blocks,
+ new->start_addr, value);
+ }
+ /* Stack must be empty now. */
+ if (context_stack_depth != 0)
+ error ("Invalid symbol data: unmatched N_LBRAC before symtab pos %d.",
+ symnum);
+
+ new = &context_stack[context_stack_depth++];
+ new->old_blocks = pending_blocks;
+ new->start_addr = value;
+ new->name = define_symbol (value, name, desc);
+ local_symbols = 0;
+ break;
+
+ case N_LBRAC:
+ /* This "symbol" just indicates the start of an inner lexical
+ context within a function. */
+
+ if (context_stack_depth == context_stack_size)
+ {
+ context_stack_size *= 2;
+ context_stack = (struct context_stack *)
+ xrealloc (context_stack,
+ (context_stack_size
+ * sizeof (struct context_stack)));
+ }
+
+ new = &context_stack[context_stack_depth++];
+ new->depth = desc;
+ new->locals = local_symbols;
+ new->old_blocks = pending_blocks;
+ new->start_addr = value;
+ new->name = 0;
+ local_symbols = 0;
+ break;
+
+ case N_RBRAC:
+ /* This "symbol" just indicates the end of an inner lexical
+ context that was started with N_LBRAC. */
+ new = &context_stack[--context_stack_depth];
+ if (desc != new->depth)
+ error ("Invalid symbol data: N_LBRAC/N_RBRAC symbol mismatch, symtab pos %d.", symnum);
+
+ /* Some native compilers put the variable decls inside of an
+ LBRAC/RBRAC block. This macro should be nonzero if this
+ is true. DESC is N_DESC from the N_RBRAC symbol. */
+#if !defined (VARIABLES_INSIDE_BLOCK)
+#define VARIABLES_INSIDE_BLOCK(desc) 0
+#endif
+
+ /* Can only use new->locals as local symbols here if we're in
+ gcc or on a machine that puts them before the lbrack. */
+ if (!VARIABLES_INSIDE_BLOCK(desc))
+ local_symbols = new->locals;
+
+ /* If this is not the outermost LBRAC...RBRAC pair in the
+ function, its local symbols preceded it, and are the ones
+ just recovered from the context stack. Defined the block for them.
+
+ If this is the outermost LBRAC...RBRAC pair, there is no
+ need to do anything; leave the symbols that preceded it
+ to be attached to the function's own block. However, if
+ it is so, we need to indicate that we just moved outside
+ of the function. */
+ if (local_symbols
+ && context_stack_depth > !VARIABLES_INSIDE_BLOCK(desc))
+ {
+ /* Muzzle a compiler bug that makes end < start. */
+ if (new->start_addr > value)
+ new->start_addr = value;
+ /* Make a block for the local symbols within. */
+ finish_block (0, &local_symbols, new->old_blocks,
+ new->start_addr + last_source_start_addr,
+ value + last_source_start_addr);
+ }
+ else
+ {
+ within_function = 0;
+ }
+ if (VARIABLES_INSIDE_BLOCK(desc))
+ /* Now pop locals of block just finished. */
+ local_symbols = new->locals;
+ break;
+
+ case N_FN | N_EXT:
+ /* This kind of symbol supposedly indicates the start
+ of an object file. In fact this type does not appear. */
+ break;
+
+ case N_SO:
+ /* This type of symbol indicates the start of data
+ for one source file.
+ Finish the symbol table of the previous source file
+ (if any) and start accumulating a new symbol table. */
+#ifdef PCC_SOL_BROKEN
+ /* pcc bug, occasionally puts out SO for SOL. */
+ if (context_stack_depth > 0)
+ {
+ start_subfile (name);
+ break;
+ }
+#endif
+ if (last_source_file)
+ end_symtab (value);
+ start_symtab (name, value);
+ break;
+
+ case N_SOL:
+ /* This type of symbol indicates the start of data for
+ a sub-source-file, one whose contents were copied or
+ included in the compilation of the main source file
+ (whose name was given in the N_SO symbol.) */
+ start_subfile (name);
+ break;
+
+#ifdef N_BINCL
+ case N_BINCL:
+ push_subfile ();
+ add_new_header_file (name, value);
+ start_subfile (name);
+ break;
+
+ case N_EINCL:
+ start_subfile (pop_subfile ());
+ break;
+
+ case N_EXCL:
+ add_old_header_file (name, value);
+ break;
+#endif /* have N_BINCL */
+
+ case N_SLINE:
+ /* This type of "symbol" really just records
+ one line-number -- core-address correspondence.
+ Enter it in the line list for this symbol table. */
+ record_line (desc, value);
+ break;
+
+ case N_BCOMM:
+ if (common_block)
+ error ("Invalid symbol data: common within common at symtab pos %d",
+ symnum);
+ common_block = local_symbols;
+ common_block_i = local_symbols ? local_symbols->nsyms : 0;
+ break;
+
+ case N_ECOMM:
+ /* Symbols declared since the BCOMM are to have the common block
+ start address added in when we know it. common_block points to
+ the first symbol after the BCOMM in the local_symbols list;
+ copy the list and hang it off the symbol for the common block name
+ for later fixup. */
+ {
+ int i;
+ struct pending *link = local_symbols;
+ struct symbol *sym =
+ (struct symbol *) xmalloc (sizeof (struct symbol));
+ bzero (sym, sizeof *sym);
+ SYMBOL_NAME (sym) = savestring (name, strlen (name));
+ SYMBOL_CLASS (sym) = LOC_BLOCK;
+ SYMBOL_NAMESPACE (sym) = (enum namespace)((long)
+ copy_pending (local_symbols, common_block_i, common_block));
+ i = hashname (SYMBOL_NAME (sym));
+ SYMBOL_VALUE (sym) = (int) global_sym_chain[i];
+ global_sym_chain[i] = sym;
+ common_block = 0;
+ break;
+ }
+
+ case N_ECOML:
+ case N_LENG:
+ break;
+
+ default:
+ if (name)
+ define_symbol (value, name, desc);
+ }
+}
+
+/* This function was added for C++ functionality. I presume that it
+ condenses the bunches formed by reading in an additional .o file
+ (incremental linking). */
+
+static void
+condense_addl_misc_bunches ()
+{
+ register int i, j;
+ register struct misc_bunch *bunch;
+#ifdef NAMES_HAVE_UNDERSCORE
+ int offset = 1;
+#else
+ int offset = 0;
+#endif
+
+ misc_function_vector
+ = (struct misc_function *) xrealloc (misc_function_vector,
+ (misc_count + misc_function_count) * sizeof (struct misc_function));
+
+ j = misc_function_count;
+ bunch = misc_bunch;
+ while (bunch)
+ {
+ for (i = 0; i < misc_bunch_index; i++)
+ {
+ misc_function_vector[j] = bunch->contents[i];
+ misc_function_vector[j].name
+ = concat (misc_function_vector[j].name
+ + (misc_function_vector[j].name[0] == '_' ? offset : 0),
+ "", "");
+ j++;
+ }
+ bunch = bunch->next;
+ misc_bunch_index = MISC_BUNCH_SIZE;
+ }
+
+ misc_function_count += misc_count;
+
+ /* Sort the misc functions by address. */
+
+ qsort (misc_function_vector, misc_function_count,
+ sizeof (struct misc_function), compare_misc_functions);
+}
+
+
+/* Read in another .o file and create a symtab entry for it.*/
+
+static void
+read_addl_syms (desc, stringtab, nlistlen, text_addr, text_size)
+ int desc;
+ register char *stringtab;
+ register int nlistlen;
+ unsigned text_addr;
+ int text_size;
+{
+ FILE *stream = fdopen (desc, "r");
+ register char *namestring;
+ register struct symbol *sym, *prev;
+ int hash;
+
+#ifdef N_BINCL
+ subfile_stack = 0;
+#endif
+
+ last_source_file = 0;
+ bzero (global_sym_chain, sizeof global_sym_chain);
+ symtab_input_desc = desc;
+ stringtab_global = stringtab;
+ fill_symbuf ();
+
+ for (symnum = 0; symnum < nlistlen; symnum++)
+ {
+ struct nlist *bufp;
+ unsigned char type;
+
+ QUIT; /* allow this to be interruptable */
+ if (symbuf_idx == symbuf_end)
+ fill_symbuf ();
+ bufp = &symbuf[symbuf_idx++];
+ type = bufp->n_type & N_TYPE;
+ namestring = bufp->n_un.n_strx + stringtab;
+
+ if( (type == N_TEXT) || (type == N_DATA) || (type == N_BSS) )
+ {
+ /* Relocate this file's symbol table information
+ to the address it has been loaded into. */
+ bufp->n_value += text_addr;
+ }
+
+ type = bufp->n_type;
+
+ if (type & N_STAB)
+ process_one_symbol (type, bufp->n_desc,
+ bufp->n_value, namestring);
+ /* A static text symbol whose name ends in ".o"
+ can only mean the start of another object file.
+ So end the symtab of the source file we have been processing.
+ This is how we avoid counting the libraries as part
+ or the last source file.
+ Also this way we find end of first object file (crt0). */
+ else if ((type == N_TEXT
+#ifdef N_NBTEXT
+ || type == N_NBTEXT
+#endif
+ )
+ && (!strcmp (namestring + strlen (namestring) - 2, ".o"))
+ || ! strncmp (namestring, "-l", 2))
+ {
+ if (last_source_file)
+ end_symtab (bufp->n_value);
+ }
+ else if (type & N_EXT || type == N_TEXT
+#ifdef N_NBTEXT
+ || type == N_NBTEXT
+#endif
+ )
+ {
+ int used_up = 0;
+
+ /* Record the location of _etext. */
+ if (type == (N_TEXT | N_EXT)
+ && !strcmp (namestring, "_etext"))
+ end_of_text_addr = bufp->n_value;
+
+#if 0
+ /* 25 Sep 89: The following seems to be stolen from
+ read_ofile_symtab, and is wrong here (i.e. there was no
+ first pass for add-file symbols). */
+ /* This shouldn't be necessary, as we now do all of this work
+ in scan_global syms and all misc functions should have been
+ recorded on the first pass. */
+ /* Global symbol: see if we came across a dbx definition
+ for a corresponding symbol. If so, store the value.
+ Remove syms from the chain when their values are stored,
+ but search the whole chain, as there may be several syms
+ from different files with the same name. */
+ if (type & N_EXT)
+ {
+ prev = 0;
+#ifdef NAMES_HAVE_UNDERSCORE
+ hash = hashname (namestring + 1);
+#else /* not NAMES_HAVE_UNDERSCORE */
+ hash = hashname (namestring);
+#endif /* not NAMES_HAVE_UNDERSCORE */
+ for (sym = global_sym_chain[hash];
+ sym;)
+ {
+ if (
+#ifdef NAMES_HAVE_UNDERSCORE
+ *namestring == '_'
+ && namestring[1] == SYMBOL_NAME (sym)[0]
+ &&
+ !strcmp (namestring + 2, SYMBOL_NAME (sym) + 1)
+#else /* NAMES_HAVE_UNDERSCORE */
+ namestring[0] == SYMBOL_NAME (sym)[0]
+ &&
+ !strcmp (namestring + 1, SYMBOL_NAME (sym) + 1)
+#endif /* NAMES_HAVE_UNDERSCORE */
+ )
+ {
+ if (prev)
+ SYMBOL_VALUE (prev) = SYMBOL_VALUE (sym);
+ else
+ global_sym_chain[hash]
+ = (struct symbol *) SYMBOL_VALUE (sym);
+ if (SYMBOL_CLASS (sym) == LOC_BLOCK)
+ fix_common_block (sym, bufp->n_value);
+ else
+ SYMBOL_VALUE (sym) = bufp->n_value;
+ if (prev)
+ sym = (struct symbol *) SYMBOL_VALUE (prev);
+ else
+ sym = global_sym_chain[hash];
+
+ used_up = 1;
+ }
+ else
+ {
+ prev = sym;
+ sym = (struct symbol *) SYMBOL_VALUE (sym);
+ }
+ }
+ }
+
+ /* Defined global or text symbol: record as a misc function
+ if it didn't give its address to a debugger symbol above. */
+ if (type <= (N_TYPE | N_EXT)
+ && type != N_EXT
+ && ! used_up)
+ record_misc_function (namestring, bufp->n_value,
+ bufp->n_type);
+#endif /* 0 */
+ }
+ }
+
+ if (last_source_file)
+ end_symtab (text_addr + text_size);
+
+ fclose (stream);
+}
+
+/* C++:
+ This function allows the addition of incrementally linked object files.
+ Since this has a fair amount of code in common with symbol_file_command,
+ it might be worthwhile to consolidate things, as was done with
+ read_dbx_symtab and condense_misc_bunches. */
+
+void
+add_file_command (arg_string)
+ char* arg_string;
+{
+ register int desc;
+ DECLARE_FILE_HEADERS;
+ struct nlist *nlist;
+ char *stringtab;
+ long buffer;
+ register int val;
+ extern void close ();
+ struct cleanup *old_chain;
+ struct symtab *symseg;
+ struct stat statbuf;
+ char *name;
+ unsigned text_addr;
+
+ if (arg_string == 0)
+ error ("add-file takes a file name and an address");
+
+ arg_string = tilde_expand (arg_string);
+ make_cleanup (free, arg_string);
+
+ for( ; *arg_string == ' '; arg_string++ );
+ name = arg_string;
+ for( ; *arg_string && *arg_string != ' ' ; arg_string++ );
+ *arg_string++ = (char) 0;
+
+ if (name[0] == 0)
+ error ("add-file takes a file name and an address");
+
+ text_addr = parse_and_eval_address (arg_string);
+
+ dont_repeat ();
+
+ if (!query ("add symbol table from filename \"%s\" at text_addr = 0x%x\n",
+ name, text_addr))
+ error ("Not confirmed.");
+
+ desc = open (name, O_RDONLY);
+ if (desc < 0)
+ perror_with_name (name);
+
+ old_chain = make_cleanup (close, desc);
+
+ READ_FILE_HEADERS (desc, name);
+
+ if (NUMBER_OF_SYMBOLS == 0)
+ {
+ printf ("%s does not have a symbol-table.\n", name);
+ fflush (stdout);
+ return;
+ }
+
+ printf ("Reading symbol data from %s...", name);
+ fflush (stdout);
+
+ /* Now read the string table, all at once. */
+ val = lseek (desc, STRING_TABLE_OFFSET, 0);
+ if (val < 0)
+ perror_with_name (name);
+ if (stat (name, &statbuf) < 0)
+ perror_with_name (name);
+ READ_STRING_TABLE_SIZE (buffer);
+ if (buffer >= 0 && buffer < statbuf.st_size)
+ {
+#ifdef BROKEN_LARGE_ALLOCA
+ stringtab = (char *) xmalloc (buffer);
+ make_cleanup (free, stringtab);
+#else
+ stringtab = (char *) alloca (buffer);
+#endif
+ }
+ else
+ stringtab = NULL;
+ if (stringtab == NULL)
+ error ("ridiculous string table size: %d bytes", buffer);
+
+ /* Usually READ_STRING_TABLE_SIZE will have shifted the file pointer.
+ Occaisionally, it won't. */
+ val = lseek (desc, STRING_TABLE_OFFSET, 0);
+ if (val < 0)
+ perror_with_name (name);
+ val = myread (desc, stringtab, buffer);
+ if (val < 0)
+ perror_with_name (name);
+
+ /* Symsegs are no longer supported by GDB. Setting symseg_chain to
+ 0 is easier than finding all the symseg code and eliminating it. */
+ symseg_chain = 0;
+
+ /* Position to read the symbol table. Do not read it all at once. */
+ val = lseek (desc, SYMBOL_TABLE_OFFSET, 0);
+ if (val < 0)
+ perror_with_name (name);
+
+ init_misc_functions ();
+ make_cleanup (discard_misc_bunches, 0);
+ init_header_files ();
+ make_cleanup (free_header_files, 0);
+ free_pendings = 0;
+ pending_blocks = 0;
+ file_symbols = 0;
+ global_symbols = 0;
+ make_cleanup (really_free_pendings, 0);
+
+ read_addl_syms (desc, stringtab, NUMBER_OF_SYMBOLS, text_addr,
+ SIZE_OF_TEXT_SEGMENT);
+
+
+ /* Sort symbols alphabetically within each block. */
+
+ sort_syms ();
+
+ /* Go over the misc functions and install them in vector. */
+
+ condense_addl_misc_bunches (1);
+
+ /* Don't allow char * to have a typename (else would get caddr_t.) */
+
+ TYPE_NAME (lookup_pointer_type (builtin_type_char)) = 0;
+
+ do_cleanups (old_chain);
+
+ /* Free the symtabs made by read_symsegs, but not their contents,
+ which have been copied into symtabs on symtab_list. */
+ while (symseg_chain)
+ {
+ register struct symtab *s = symseg_chain->next;
+ free (symseg_chain);
+ symseg_chain = s;
+ }
+
+ printf ("done.\n");
+ fflush (stdout);
+}
+
+/* Read a number by which a type is referred to in dbx data,
+ or perhaps read a pair (FILENUM, TYPENUM) in parentheses.
+ Just a single number N is equivalent to (0,N).
+ Return the two numbers by storing them in the vector TYPENUMS.
+ TYPENUMS will then be used as an argument to dbx_lookup_type. */
+
+static void
+read_type_number (pp, typenums)
+ register char **pp;
+ register int *typenums;
+{
+ if (**pp == '(')
+ {
+ (*pp)++;
+ typenums[0] = read_number (pp, ',');
+ typenums[1] = read_number (pp, ')');
+ }
+ else
+ {
+ typenums[0] = 0;
+ typenums[1] = read_number (pp, 0);
+ }
+}
+
+
+
+static struct symbol *
+define_symbol (value, string, desc)
+ int value;
+ char *string;
+ int desc;
+{
+ register struct symbol *sym
+ = (struct symbol *) obstack_alloc (symbol_obstack, sizeof (struct symbol));
+ char *p = (char *) index (string, ':');
+ int deftype;
+ register int i;
+
+ /* Ignore syms with empty names. */
+ if (string[0] == 0)
+ return 0;
+
+ /* Ignore old-style symbols from cc -go */
+ if (p == 0)
+ return 0;
+
+ SYMBOL_NAME (sym)
+ = (char *) obstack_alloc (symbol_obstack, ((p - string) + 1));
+ /* Open-coded bcopy--saves function call time. */
+ {
+ register char *p1 = string;
+ register char *p2 = SYMBOL_NAME (sym);
+ while (p1 != p)
+ *p2++ = *p1++;
+ *p2++ = '\0';
+ }
+ p++;
+ /* Determine the type of name being defined. */
+ if ((*p >= '0' && *p <= '9') || *p == '(')
+ deftype = 'l';
+ else
+ deftype = *p++;
+
+ /* c is a special case, not followed by a type-number.
+ SYMBOL:c=iVALUE for an integer constant symbol.
+ SYMBOL:c=rVALUE for a floating constant symbol.
+ SYMBOL:c=eTYPE,INTVALUE for an enum constant symbol.
+ e.g. "b:c=e6,0" for "const b = blob1"
+ (where type 6 is defined by "blobs:t6=eblob1:0,blob2:1,;"). */
+ if (deftype == 'c')
+ {
+ if (*p++ != '=')
+ error ("Invalid symbol data at symtab pos %d.", symnum);
+ switch (*p++)
+ {
+ case 'r':
+ {
+ double d = atof (p);
+ char *value;
+
+ SYMBOL_TYPE (sym) = builtin_type_double;
+ value = (char *) obstack_alloc (symbol_obstack, sizeof (double));
+ bcopy (&d, value, sizeof (double));
+ SYMBOL_VALUE_BYTES (sym) = value;
+ SYMBOL_CLASS (sym) = LOC_CONST_BYTES;
+ }
+ break;
+ case 'i':
+ {
+ SYMBOL_TYPE (sym) = builtin_type_int;
+ SYMBOL_VALUE (sym) = atoi (p);
+ SYMBOL_CLASS (sym) = LOC_CONST;
+ }
+ break;
+ case 'e':
+ /* SYMBOL:c=eTYPE,INTVALUE for an enum constant symbol.
+ e.g. "b:c=e6,0" for "const b = blob1"
+ (where type 6 is defined by "blobs:t6=eblob1:0,blob2:1,;"). */
+ {
+ int typenums[2];
+
+ read_type_number (&p, typenums);
+ if (*p++ != ',')
+ error ("Invalid symbol data: no comma in enum const symbol");
+
+ SYMBOL_TYPE (sym) = *dbx_lookup_type (typenums);
+ SYMBOL_VALUE (sym) = atoi (p);
+ SYMBOL_CLASS (sym) = LOC_CONST;
+ }
+ break;
+ default:
+ error ("Invalid symbol data at symtab pos %d.", symnum);
+ }
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &file_symbols);
+ return sym;
+ }
+
+ /* Now usually comes a number that says which data type,
+ and possibly more stuff to define the type
+ (all of which is handled by read_type) */
+
+ if (deftype == 'p' && *p == 'F')
+ /* pF is a two-letter code that means a function parameter in Fortran.
+ The type-number specifies the type of the return value.
+ Translate it into a pointer-to-function type. */
+ {
+ p++;
+ SYMBOL_TYPE (sym)
+ = lookup_pointer_type (lookup_function_type (read_type (&p)));
+ }
+ else
+ {
+ struct type *type = read_type (&p);
+
+ if ((deftype == 'F' || deftype == 'f')
+ && TYPE_CODE (type) != TYPE_CODE_FUNC)
+ SYMBOL_TYPE (sym) = lookup_function_type (type);
+ else
+ SYMBOL_TYPE (sym) = type;
+ }
+
+ switch (deftype)
+ {
+ case 'f':
+ SYMBOL_CLASS (sym) = LOC_BLOCK;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &file_symbols);
+ break;
+
+ case 'F':
+ SYMBOL_CLASS (sym) = LOC_BLOCK;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &global_symbols);
+ break;
+
+ case 'G':
+ /* For a class G (global) symbol, it appears that the
+ value is not correct. It is necessary to search for the
+ corresponding linker definition to find the value.
+ These definitions appear at the end of the namelist. */
+ i = hashname (SYMBOL_NAME (sym));
+ SYMBOL_VALUE (sym) = (int) global_sym_chain[i];
+ global_sym_chain[i] = sym;
+ SYMBOL_CLASS (sym) = LOC_STATIC;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &global_symbols);
+ break;
+
+ /* This case is faked by a conditional above,
+ when there is no code letter in the dbx data.
+ Dbx data never actually contains 'l'. */
+ case 'l':
+ SYMBOL_CLASS (sym) = LOC_LOCAL;
+ SYMBOL_VALUE (sym) = value;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &local_symbols);
+ break;
+
+ case 'p':
+ SYMBOL_CLASS (sym) = LOC_ARG;
+ SYMBOL_VALUE (sym) = value;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &local_symbols);
+
+ /* If it's gcc compiled, if it says `short', believe it. */
+ if (processing_gcc_compilation || BELIEVE_PCC_PROMOTION)
+ break;
+
+#if defined(BELIEVE_PCC_PROMOTION_TYPE)
+ /* This macro is defined on machines (e.g. sparc) where
+ we should believe the type of a PCC 'short' argument,
+ but shouldn't believe the address (the address is
+ the address of the corresponding int). Note that
+ this is only different from the BELIEVE_PCC_PROMOTION
+ case on big-endian machines.
+
+ My guess is that this correction, as opposed to changing
+ the parameter to an 'int' (as done below, for PCC
+ on most machines), is the right thing to do
+ on all machines, but I don't want to risk breaking
+ something that already works. On most PCC machines,
+ the sparc problem doesn't come up because the calling
+ function has to zero the top bytes (not knowing whether
+ the called function wants an int or a short), so there
+ is no practical difference between an int and a short
+ (except perhaps what happens when the GDB user types
+ "print short_arg = 0x10000;").
+ Hacked for SunOS 4.1 by gnu@cygnus.com. In 4.1, the compiler
+ actually produces the correct address (we don't need to fix it
+ up). I made this code adapt so that it will offset the symbol
+ if it was pointing at an int-aligned location and not
+ otherwise. This way you can use the same gdb for 4.0.x and
+ 4.1 systems. */
+
+ if (0 == SYMBOL_VALUE (sym) % sizeof (int))
+ {
+ if (SYMBOL_TYPE (sym) == builtin_type_char
+ || SYMBOL_TYPE (sym) == builtin_type_unsigned_char)
+ SYMBOL_VALUE (sym) += 3;
+ else if (SYMBOL_TYPE (sym) == builtin_type_short
+ || SYMBOL_TYPE (sym) == builtin_type_unsigned_short)
+ SYMBOL_VALUE (sym) += 2;
+ }
+ break;
+
+#else /* no BELIEVE_PCC_PROMOTION_TYPE. */
+
+ /* If PCC says a parameter is a short or a char,
+ it is really an int. */
+ if (SYMBOL_TYPE (sym) == builtin_type_char
+ || SYMBOL_TYPE (sym) == builtin_type_short)
+ SYMBOL_TYPE (sym) = builtin_type_int;
+ else if (SYMBOL_TYPE (sym) == builtin_type_unsigned_char
+ || SYMBOL_TYPE (sym) == builtin_type_unsigned_short)
+ SYMBOL_TYPE (sym) = builtin_type_unsigned_int;
+ break;
+
+#endif /* no BELIEVE_PCC_PROMOTION_TYPE. */
+
+ case 'P':
+ SYMBOL_CLASS (sym) = LOC_REGPARM;
+ SYMBOL_VALUE (sym) = STAB_REG_TO_REGNUM (value);
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &local_symbols);
+ break;
+
+ case 'r':
+/* XXX */
+#ifdef sparc
+{
+ struct symbol *s0;
+
+ /*
+ * If we see a parm decl immediately followed by a reg decl of
+ * the same name (and in the same block), we change it to a single
+ * instance of a reg parm. Sun's cc will generate these.
+ */
+ if (local_symbols &&
+ (s0 = local_symbols->symbol[local_symbols->nsyms - 1]) &&
+ SYMBOL_CLASS(s0) == LOC_ARG &&
+ strcmp(SYMBOL_NAME(s0), SYMBOL_NAME(sym)) == 0) {
+ SYMBOL_CLASS (s0) = LOC_REGPARM;
+ SYMBOL_VALUE (s0) = STAB_REG_TO_REGNUM (value);
+ SYMBOL_NAMESPACE (s0) = VAR_NAMESPACE;
+ return s0;
+ }
+}
+#endif
+ SYMBOL_CLASS (sym) = LOC_REGISTER;
+ SYMBOL_VALUE (sym) = STAB_REG_TO_REGNUM (value);
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &local_symbols);
+ break;
+
+ case 'S':
+ /* Static symbol at top level of file */
+ SYMBOL_CLASS (sym) = LOC_STATIC;
+ SYMBOL_VALUE (sym) = value;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &file_symbols);
+ break;
+
+ case 't':
+ SYMBOL_CLASS (sym) = LOC_TYPEDEF;
+ SYMBOL_VALUE (sym) = value;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ if (TYPE_NAME (SYMBOL_TYPE (sym)) == 0
+ && (TYPE_FLAGS (SYMBOL_TYPE (sym)) & TYPE_FLAG_PERM) == 0)
+ TYPE_NAME (SYMBOL_TYPE (sym)) =
+ obsavestring (SYMBOL_NAME (sym),
+ strlen (SYMBOL_NAME (sym)));
+ /* C++ vagaries: we may have a type which is derived from
+ a base type which did not have its name defined when the
+ derived class was output. We fill in the derived class's
+ base part member's name here in that case. */
+ else if ((TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_STRUCT
+ || TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_UNION)
+ && TYPE_N_BASECLASSES (SYMBOL_TYPE (sym)))
+ {
+ int i;
+ for (i = TYPE_N_BASECLASSES (SYMBOL_TYPE (sym)); i > 0; i--)
+ if (TYPE_FIELD_NAME (SYMBOL_TYPE (sym), i - 1) == 0)
+ TYPE_FIELD_NAME (SYMBOL_TYPE (sym), i - 1) =
+ TYPE_NAME (TYPE_BASECLASS (SYMBOL_TYPE (sym), i));
+ }
+
+ add_symbol_to_list (sym, &file_symbols);
+ break;
+
+ case 'T':
+ SYMBOL_CLASS (sym) = LOC_TYPEDEF;
+ SYMBOL_VALUE (sym) = value;
+ SYMBOL_NAMESPACE (sym) = STRUCT_NAMESPACE;
+ if (TYPE_NAME (SYMBOL_TYPE (sym)) == 0
+ && (TYPE_FLAGS (SYMBOL_TYPE (sym)) & TYPE_FLAG_PERM) == 0)
+ TYPE_NAME (SYMBOL_TYPE (sym))
+ = obconcat ("",
+ (TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_ENUM
+ ? "enum "
+ : (TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_STRUCT
+ ? "struct " : "union ")),
+ SYMBOL_NAME (sym));
+ add_symbol_to_list (sym, &file_symbols);
+ break;
+
+ case 'V':
+ /* Static symbol of local scope */
+ SYMBOL_CLASS (sym) = LOC_STATIC;
+ SYMBOL_VALUE (sym) = value;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &local_symbols);
+ break;
+
+ case 'v':
+ /* Reference parameter */
+ SYMBOL_CLASS (sym) = LOC_REF_ARG;
+ SYMBOL_VALUE (sym) = value;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &local_symbols);
+ break;
+
+ case 'X':
+ /* This is used by Sun FORTRAN for "function result value".
+ Sun claims ("dbx and dbxtool interfaces", 2nd ed)
+ that Pascal uses it too, but when I tried it Pascal used
+ "x:3" (local symbol) instead. */
+ SYMBOL_CLASS (sym) = LOC_LOCAL;
+ SYMBOL_VALUE (sym) = value;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &local_symbols);
+ break;
+
+ default:
+ error ("Invalid symbol data: unknown symbol-type code `%c' at symtab pos %d.", deftype, symnum);
+ }
+ return sym;
+}
+
+/* What about types defined as forward references inside of a small lexical
+ scope? */
+/* Add a type to the list of undefined types to be checked through
+ once this file has been read in. */
+static void
+add_undefined_type (type)
+ struct type *type;
+{
+ if (undef_types_length == undef_types_allocated)
+ {
+ undef_types_allocated *= 2;
+ undef_types = (struct type **)
+ xrealloc (undef_types,
+ undef_types_allocated * sizeof (struct type *));
+ }
+ undef_types[undef_types_length++] = type;
+}
+
+/* Add here something to go through each undefined type, see if it's
+ still undefined, and do a full lookup if so. */
+static void
+cleanup_undefined_types ()
+{
+ struct type **type, *ntype;
+ struct symbol *sym;
+
+ for (type = undef_types; type < undef_types + undef_types_length; type++)
+ {
+ struct type *ntype = 0;
+ /* Reasonable test to see if it's been defined since. */
+ if (TYPE_NFIELDS (*type) == 0)
+ {
+ struct pending *ppt;
+ int i;
+ /* Name of the type, without "struct" or "union" */
+ char *typename = TYPE_NAME (*type);
+
+ if (!strncmp (typename, "struct ", 7))
+ typename += 7;
+ if (!strncmp (typename, "union ", 6))
+ typename += 6;
+
+ for (ppt = file_symbols; ppt; ppt = ppt->next)
+ for (i = 0; i < ppt->nsyms; i++)
+ {
+ struct symbol *sym = ppt->symbol[i];
+
+ if (SYMBOL_CLASS (sym) == LOC_TYPEDEF
+ && SYMBOL_NAMESPACE (sym) == STRUCT_NAMESPACE
+ && (TYPE_CODE (SYMBOL_TYPE (sym)) ==
+ TYPE_CODE (*type))
+ && !strcmp (SYMBOL_NAME (sym), typename))
+ bcopy (SYMBOL_TYPE (sym), *type, sizeof (struct type));
+ }
+ }
+ else
+ /* It has been defined; don't mark it as a stub. */
+ TYPE_FLAGS (*type) &= ~TYPE_FLAG_STUB;
+ }
+ undef_types_length = 0;
+}
+
+
+
+/* Read a dbx type reference or definition;
+ return the type that is meant.
+ This can be just a number, in which case it references
+ a type already defined and placed in type_vector.
+ Or the number can be followed by an =, in which case
+ it means to define a new type according to the text that
+ follows the =. */
+
+static
+struct type *
+read_type (pp)
+ register char **pp;
+{
+ register struct type *type = 0;
+ register int n;
+ struct type *type1;
+ int typenums[2];
+ int xtypenums[2];
+ char *tmpc;
+
+ /* Read type number if present. The type number may be omitted.
+ for instance in a two-dimensional array declared with type
+ "ar1;1;10;ar1;1;10;4". */
+ if ((**pp >= '0' && **pp <= '9')
+ || **pp == '(')
+ {
+ read_type_number (pp, typenums);
+
+ /* Detect random reference to type not yet defined.
+ Allocate a type object but leave it zeroed. */
+ if (**pp != '=')
+ return dbx_alloc_type (typenums);
+
+ *pp += 2;
+ }
+ else
+ {
+ /* 'typenums=' not present, type is anonymous. Read and return
+ the definition, but don't put it in the type vector. */
+ typenums[0] = typenums[1] = -1;
+ *pp += 1;
+ }
+
+ switch ((*pp)[-1])
+ {
+ case 'x':
+ {
+ enum type_code code;
+
+ /* Used to index through file_symbols. */
+ struct pending *ppt;
+ int i;
+
+ /* Name including "struct", etc. */
+ char *type_name;
+
+ /* Name without "struct", etc. */
+ char *type_name_only;
+
+ {
+ char *prefix;
+ char *from, *to;
+
+ /* Set the type code according to the following letter. */
+ switch ((*pp)[0])
+ {
+ case 's':
+ code = TYPE_CODE_STRUCT;
+ prefix = "struct ";
+ break;
+ case 'u':
+ code = TYPE_CODE_UNION;
+ prefix = "union ";
+ break;
+ case 'e':
+ code = TYPE_CODE_ENUM;
+ prefix = "enum ";
+ break;
+ default:
+ error ("Bad type cross reference at symnum: %d.", symnum);
+ }
+
+ to = type_name = (char *)
+ obstack_alloc (symbol_obstack,
+ (strlen (prefix) +
+ ((char *) index (*pp, ':') - (*pp)) + 1));
+
+ /* Copy the prefix. */
+ from = prefix;
+ while (*to++ = *from++)
+ ;
+ to--;
+
+ type_name_only = to;
+
+ /* Copy the name. */
+ from = *pp + 1;
+ while ((*to++ = *from++) != ':')
+ ;
+ *--to = '\0';
+
+ /* Set the pointer ahead of the name which we just read. */
+ *pp = from;
+
+#if 0
+ /* The following hack is clearly wrong, because it doesn't
+ check whether we are in a baseclass. I tried to reproduce
+ the case that it is trying to fix, but I couldn't get
+ g++ to put out a cross reference to a basetype. Perhaps
+ it doesn't do it anymore. */
+ /* Note: for C++, the cross reference may be to a base type which
+ has not yet been seen. In this case, we skip to the comma,
+ which will mark the end of the base class name. (The ':'
+ at the end of the base class name will be skipped as well.)
+ But sometimes (ie. when the cross ref is the last thing on
+ the line) there will be no ','. */
+ from = (char *) index (*pp, ',');
+ if (from)
+ *pp = from;
+#endif /* 0 */
+ }
+
+ /* Now check to see whether the type has already been declared. */
+ /* This is necessary at least in the case where the
+ program says something like
+ struct foo bar[5];
+ The compiler puts out a cross-reference; we better find
+ set the length of the structure correctly so we can
+ set the length of the array. */
+ for (ppt = file_symbols; ppt; ppt = ppt->next)
+ for (i = 0; i < ppt->nsyms; i++)
+ {
+ struct symbol *sym = ppt->symbol[i];
+
+ if (SYMBOL_CLASS (sym) == LOC_TYPEDEF
+ && SYMBOL_NAMESPACE (sym) == STRUCT_NAMESPACE
+ && (TYPE_CODE (SYMBOL_TYPE (sym)) == code)
+ && !strcmp (SYMBOL_NAME (sym), type_name_only))
+ {
+ obstack_free (symbol_obstack, type_name);
+ type = SYMBOL_TYPE (sym);
+ return type;
+ }
+ }
+
+ /* Didn't find the type to which this refers, so we must
+ be dealing with a forward reference. Allocate a type
+ structure for it, and keep track of it so we can
+ fill in the rest of the fields when we get the full
+ type. */
+ type = dbx_alloc_type (typenums);
+ TYPE_CODE (type) = code;
+ TYPE_NAME (type) = type_name;
+
+ TYPE_FLAGS (type) |= TYPE_FLAG_STUB;
+
+ add_undefined_type (type);
+ return type;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '(':
+ (*pp)--;
+ read_type_number (pp, xtypenums);
+ type = *dbx_lookup_type (xtypenums);
+ if (type == 0)
+ type = builtin_type_void;
+ if (typenums[0] != -1)
+ *dbx_lookup_type (typenums) = type;
+ break;
+
+ case '*':
+ type1 = read_type (pp);
+ if (TYPE_POINTER_TYPE (type1))
+ {
+ type = TYPE_POINTER_TYPE (type1);
+ if (typenums[0] != -1)
+ *dbx_lookup_type (typenums) = type;
+ }
+ else
+ {
+ type = dbx_alloc_type (typenums);
+ smash_to_pointer_type (type, type1);
+ }
+ break;
+
+ case '@':
+ {
+ struct type *domain = read_type (pp);
+ char c;
+ struct type *memtype;
+
+ if (*(*pp)++ != ',')
+ error ("invalid member type data format, at symtab pos %d.",
+ symnum);
+
+ memtype = read_type (pp);
+ type = dbx_alloc_type (typenums);
+ smash_to_member_type (type, domain, memtype);
+ }
+ break;
+
+ case '#':
+ {
+ struct type *domain = read_type (pp);
+ char c;
+ struct type *return_type;
+ struct type **args;
+
+ if (*(*pp)++ != ',')
+ error ("invalid member type data format, at symtab pos %d.",
+ symnum);
+
+ return_type = read_type (pp);
+ args = read_args (pp, ';');
+ type = dbx_alloc_type (typenums);
+ smash_to_method_type (type, domain, return_type, args);
+ }
+ break;
+
+ case '&':
+ type1 = read_type (pp);
+ if (TYPE_REFERENCE_TYPE (type1))
+ {
+ type = TYPE_REFERENCE_TYPE (type1);
+ if (typenums[0] != -1)
+ *dbx_lookup_type (typenums) = type;
+ }
+ else
+ {
+ type = dbx_alloc_type (typenums);
+ smash_to_reference_type (type, type1);
+ }
+ break;
+
+ case 'f':
+ type1 = read_type (pp);
+ if (TYPE_FUNCTION_TYPE (type1))
+ {
+ type = TYPE_FUNCTION_TYPE (type1);
+ if (typenums[0] != -1)
+ *dbx_lookup_type (typenums) = type;
+ }
+ else
+ {
+ type = dbx_alloc_type (typenums);
+ smash_to_function_type (type, type1);
+ }
+ break;
+
+ case 'r':
+ type = read_range_type (pp, typenums);
+ if (typenums[0] != -1)
+ *dbx_lookup_type (typenums) = type;
+ break;
+
+ case 'e':
+ type = dbx_alloc_type (typenums);
+ type = read_enum_type (pp, type);
+ *dbx_lookup_type (typenums) = type;
+ break;
+
+ case 's':
+ type = dbx_alloc_type (typenums);
+ type = read_struct_type (pp, type);
+ break;
+
+ case 'u':
+ type = dbx_alloc_type (typenums);
+ type = read_struct_type (pp, type);
+ TYPE_CODE (type) = TYPE_CODE_UNION;
+ break;
+
+ case 'a':
+ if (*(*pp)++ != 'r')
+ error ("Invalid symbol data: unrecognized type-code `a%c' %s %d.",
+ (*pp)[-1], "at symtab position", symnum);
+
+ type = dbx_alloc_type (typenums);
+ type = read_array_type (pp, type);
+ break;
+
+ default:
+ error ("Invalid symbol data: unrecognized type-code `%c' at symtab pos %d.",
+ (*pp)[-1], symnum);
+ }
+
+ if (type == 0)
+ abort ();
+
+#if 0
+ /* If this is an overriding temporary alteration for a header file's
+ contents, and this type number is unknown in the global definition,
+ put this type into the global definition at this type number. */
+ if (header_file_prev_index >= 0)
+ {
+ register struct type **tp
+ = explicit_lookup_type (header_file_prev_index, typenums[1]);
+ if (*tp == 0)
+ *tp = type;
+ }
+#endif
+ return type;
+}
+
+/* This page contains subroutines of read_type. */
+
+/* Read the description of a structure (or union type)
+ and return an object describing the type. */
+
+static struct type *
+read_struct_type (pp, type)
+ char **pp;
+ register struct type *type;
+{
+ struct nextfield
+ {
+ struct nextfield *next;
+ int visibility;
+ struct field field;
+ };
+
+ struct next_fnfield
+ {
+ struct next_fnfield *next;
+ int visibility;
+ struct fn_field fn_field;
+ };
+
+ struct next_fnfieldlist
+ {
+ struct next_fnfieldlist *next;
+ struct fn_fieldlist fn_fieldlist;
+ };
+
+ register struct nextfield *list = 0;
+ struct nextfield *new;
+ int totalsize;
+ char *name;
+ register char *p;
+ int nfields = 0;
+ register int n;
+
+ register struct next_fnfieldlist *mainlist = 0;
+ int nfn_fields = 0;
+ int read_possible_virtual_info = 0;
+
+ if (TYPE_MAIN_VARIANT (type) == 0)
+ {
+ TYPE_MAIN_VARIANT (type) = type;
+ }
+
+ TYPE_CODE (type) = TYPE_CODE_STRUCT;
+
+ /* First comes the total size in bytes. */
+
+ TYPE_LENGTH (type) = read_number (pp, 0);
+
+ /* C++: Now, if the class is a derived class, then the next character
+ will be a '!', followed by the number of base classes derived from.
+ Each element in the list contains visibility information,
+ the offset of this base class in the derived structure,
+ and then the base type. */
+ if (**pp == '!')
+ {
+ int i, n_baseclasses, offset;
+ struct type **baseclass_vec;
+ struct type *baseclass;
+ int via_public;
+
+ /* Nonzero if it is a virtual baseclass, i.e.,
+
+ struct A{};
+ struct B{};
+ struct C : public B, public virtual A {};
+
+ B is a baseclass of C; A is a virtual baseclass for C. This is a C++
+ 2.0 language feature. */
+ int via_virtual;
+
+ *pp += 1;
+
+ n_baseclasses = read_number (pp, ',');
+ baseclass_vec = (struct type **)
+ obstack_alloc (symbol_obstack,
+ (n_baseclasses) * sizeof (struct type **)) - 1;
+
+ for (i = 1; i <= n_baseclasses; i++)
+ {
+ if (**pp == '\\')
+ *pp = next_symbol_text ();
+
+ switch (*(*pp)++)
+ {
+ case '0':
+ via_virtual = 0;
+ break;
+ case '1':
+ via_virtual = 1;
+ break;
+ default:
+ error ("Invalid symbol data: bad visibility format at symtab pos %d",
+ symnum);
+ }
+
+ switch (*(*pp)++)
+ {
+ case '0':
+ via_public = 0;
+ break;
+ case '2':
+ via_public = 1;
+ break;
+ default:
+ error ("Invalid symbol data: bad visibility format at symtab pos %d.",
+ symnum);
+ }
+
+ /* Offset of the portion of the object corresponding to
+ this baseclass. Always zero in the absence of
+ multiple inheritance. */
+ offset = read_number (pp, ',');
+ baseclass = read_type (pp);
+ *pp += 1; /* skip trailing ';' */
+
+ if (offset != 0)
+ {
+ static int error_printed = 0;
+
+ if (!error_printed)
+ {
+ fprintf (stderr,
+"\nWarning: GDB has limited understanding of multiple inheritance...");
+ error_printed = 1;
+ }
+ offset = 0;
+ }
+
+ baseclass_vec[i] = lookup_basetype_type (baseclass, offset, via_virtual, via_public);
+
+ /* Since lookup_basetype_type can copy the type,
+ it might copy a stub type (complete with stub flag).
+ If so, we need to add it to the list of undefined types
+ to clean up later. Even if lookup_basetype_type
+ didn't copy the type, adding it to the undefined list
+ will not do any harm. */
+ if (TYPE_FLAGS(baseclass_vec[i]) & TYPE_FLAG_STUB)
+ add_undefined_type (baseclass_vec[i]);
+
+ /* Make this baseclass visible for structure-printing purposes. */
+ new = (struct nextfield *) alloca (sizeof (struct nextfield));
+ new->next = list;
+ list = new;
+ list->field.type = baseclass_vec[i];
+ list->field.name = TYPE_NAME (baseclass_vec[i]);
+ list->field.bitpos = offset;
+ list->field.bitsize = 0; /* this should be an unpacked field! */
+ nfields++;
+ }
+ TYPE_N_BASECLASSES (type) = n_baseclasses;
+ TYPE_BASECLASSES (type) = baseclass_vec;
+ }
+
+ /* Now come the fields, as NAME:?TYPENUM,BITPOS,BITSIZE; for each one.
+ At the end, we see a semicolon instead of a field.
+
+ In C++, this may wind up being NAME:?TYPENUM:PHYSNAME; for
+ a static field.
+
+ The `?' is a placeholder for one of '+' (public visibility),
+ '0' (protected visibility), and '-' (private visibility). */
+
+ /* We better set p right now, in case there are no fields at all... */
+ p = *pp;
+
+ while (**pp != ';')
+ {
+ int visibility;
+
+ /* Check for and handle cretinous dbx symbol name continuation! */
+ if (**pp == '\\') *pp = next_symbol_text ();
+
+ /* Get space to record the next field's data. */
+ new = (struct nextfield *) alloca (sizeof (struct nextfield));
+ new->next = list;
+ list = new;
+
+ /* Get the field name. */
+ p = *pp;
+ while (*p != ':') p++;
+ list->field.name = obsavestring (*pp, p - *pp);
+
+ /* C++: Check to see if we have hit the methods yet. */
+ if (p[1] == ':')
+ break;
+
+ *pp = p + 1;
+
+ /* This means we have a visibility for a field coming. */
+ if (**pp == '/')
+ {
+ switch (*++*pp)
+ {
+ case '0':
+ visibility = 0;
+ *pp += 1;
+ break;
+
+ case '1':
+ visibility = 1;
+ *pp += 1;
+ break;
+
+ case '2':
+ visibility = 2;
+ *pp += 1;
+ break;
+ }
+ }
+ /* else normal dbx-style format. */
+
+ list->field.type = read_type (pp);
+ if (**pp == ':')
+ {
+ list->field.bitpos = (long)-1;
+ p = ++(*pp);
+ while (*p != ';') p++;
+ list->field.bitsize = (long) savestring (*pp, p - *pp);
+ *pp = p + 1;
+ nfields++;
+ continue;
+ }
+ else if (**pp != ',')
+ error ("Invalid symbol data: bad structure-type format at symtab pos %d.",
+ symnum);
+ (*pp)++; /* Skip the comma. */
+ list->field.bitpos = read_number (pp, ',');
+ list->field.bitsize = read_number (pp, ';');
+
+#if 0
+ /* This is wrong because this is identical to the symbols
+ produced for GCC 0-size arrays. For example:
+ typedef union {
+ int num;
+ char str[0];
+ } foo;
+ The code which dumped core in such circumstances should be
+ fixed not to dump core. */
+
+ /* g++ -g0 can put out bitpos & bitsize zero for a static
+ field. This does not give us any way of getting its
+ class, so we can't know its name. But we can just
+ ignore the field so we don't dump core and other nasty
+ stuff. */
+ if (list->field.bitpos == 0
+ && list->field.bitsize == 0)
+ {
+ /* Have we given the warning yet? */
+ static int warning_given = 0;
+
+ /* Only give the warning once, no matter how many class
+ variables there are. */
+ if (!warning_given)
+ {
+ warning_given = 1;
+ fprintf_filtered (stderr, "\n\
+Warning: DBX-style class variable debugging information encountered.\n\
+You seem to have compiled your program with \
+\"g++ -g0\" instead of \"g++ -g\".\n\
+Therefore GDB will not know about your class variables.\n\
+");
+ }
+
+ /* Ignore this field. */
+ list = list->next;
+ }
+ else
+#endif /* 0 */
+ {
+ /* Detect an unpacked field and mark it as such.
+ dbx gives a bit size for all fields.
+ Note that forward refs cannot be packed,
+ and treat enums as if they had the width of ints. */
+ if (TYPE_CODE (list->field.type) != TYPE_CODE_INT
+ && TYPE_CODE (list->field.type) != TYPE_CODE_ENUM)
+ list->field.bitsize = 0;
+ if ((list->field.bitsize == 8 * TYPE_LENGTH (list->field.type)
+ || (TYPE_CODE (list->field.type) == TYPE_CODE_ENUM
+ && (list->field.bitsize
+ == 8 * TYPE_LENGTH (builtin_type_int))
+ )
+ )
+ &&
+ list->field.bitpos % 8 == 0)
+ list->field.bitsize = 0;
+ nfields++;
+ }
+ }
+
+ /* Now come the method fields, as NAME::methods
+ where each method is of the form TYPENUM,ARGS,...:PHYSNAME;
+ At the end, we see a semicolon instead of a field.
+
+ For the case of overloaded operators, the format is
+ OPERATOR::*.methods, where OPERATOR is the string "operator",
+ `*' holds the place for an operator name (such as `+=')
+ and `.' marks the end of the operator name. */
+ if (p[1] == ':')
+ {
+ /* Now, read in the methods. To simplify matters, we
+ "unread" the name that has been read, so that we can
+ start from the top. */
+
+ p = *pp;
+
+ /* chill the list of fields: the last entry (at the head)
+ is a partially constructed entry which we now scrub. */
+ list = list->next;
+
+ /* For each list of method lists... */
+ do
+ {
+ int i;
+ struct next_fnfield *sublist = 0;
+ struct fn_field *fn_fields = 0;
+ int length = 0;
+ struct next_fnfieldlist *new_mainlist =
+ (struct next_fnfieldlist *)alloca (sizeof (struct next_fnfieldlist));
+
+ /* read in the name. */
+ while (*p != ':') p++;
+ if ((*pp)[0] == 'o' && (*pp)[1] == 'p' && (*pp)[2] == '$')
+ {
+ static char opname[] = "operator";
+ char *o = opname + strlen(opname);
+
+ /* Skip past '::'. */
+ p += 2;
+ while (*p != '.')
+ *o++ = *p++;
+ new_mainlist->fn_fieldlist.name = savestring (opname, o - opname);
+ /* Skip past '.' */
+ *pp = p + 1;
+ }
+ else
+ {
+ i = 0;
+ new_mainlist->fn_fieldlist.name = savestring (*pp, p - *pp);
+ /* Skip past '::'. */
+ *pp = p + 2;
+ }
+
+ do
+ {
+ struct next_fnfield *new_sublist =
+ (struct next_fnfield *)alloca (sizeof (struct next_fnfield));
+
+ /* Check for and handle cretinous dbx symbol name continuation! */
+ if (**pp == '\\') *pp = next_symbol_text ();
+
+ new_sublist->fn_field.type = read_type (pp);
+ if (**pp != ':')
+ error ("invalid symtab info for method at symbol number %d.",
+ symnum);
+ *pp += 1;
+ new_sublist->fn_field.args =
+ TYPE_ARG_TYPES (new_sublist->fn_field.type);
+ p = *pp;
+ while (*p != ';') p++;
+ new_sublist->fn_field.physname = savestring (*pp, p - *pp);
+ *pp = p + 1;
+ new_sublist->visibility = *(*pp)++ - '0';
+ if (**pp == '\\') *pp = next_symbol_text ();
+
+ switch (*(*pp)++)
+ {
+ case '*':
+ /* virtual member function, followed by index. */
+ new_sublist->fn_field.voffset = read_number (pp, ';') + 1;
+ break;
+ case '?':
+ /* static member function. */
+ new_sublist->fn_field.voffset = 1;
+ break;
+ default:
+ /* **pp == '.'. */
+ /* normal member function. */
+ new_sublist->fn_field.voffset = 0;
+ break;
+ }
+
+ new_sublist->next = sublist;
+ sublist = new_sublist;
+ length++;
+ }
+ while (**pp != ';');
+
+ *pp += 1;
+
+ new_mainlist->fn_fieldlist.fn_fields =
+ (struct fn_field *) obstack_alloc (symbol_obstack,
+ sizeof (struct fn_field) * length);
+ TYPE_FN_PRIVATE_BITS (new_mainlist->fn_fieldlist) =
+ (int *) obstack_alloc (symbol_obstack,
+ sizeof (int) * (1 + (length >> 5)));
+
+ TYPE_FN_PROTECTED_BITS (new_mainlist->fn_fieldlist) =
+ (int *) obstack_alloc (symbol_obstack,
+ sizeof (int) * (1 + (length >> 5)));
+
+ for (i = length; sublist; sublist = sublist->next)
+ {
+ new_mainlist->fn_fieldlist.fn_fields[--i] = sublist->fn_field;
+ if (sublist->visibility == 0)
+ B_SET (new_mainlist->fn_fieldlist.private_fn_field_bits, i);
+ else if (sublist->visibility == 1)
+ B_SET (new_mainlist->fn_fieldlist.protected_fn_field_bits, i);
+ }
+
+ new_mainlist->fn_fieldlist.length = length;
+ new_mainlist->next = mainlist;
+ mainlist = new_mainlist;
+ nfn_fields++;
+ }
+ while (**pp != ';');
+ }
+
+ *pp += 1;
+
+ /* Now create the vector of fields, and record how big it is. */
+
+ TYPE_NFIELDS (type) = nfields;
+ TYPE_FIELDS (type) = (struct field *) obstack_alloc (symbol_obstack,
+ sizeof (struct field) * nfields);
+ TYPE_FIELD_PRIVATE_BITS (type) =
+ (int *) obstack_alloc (symbol_obstack,
+ sizeof (int) * (1 + (nfields >> 5)));
+ TYPE_FIELD_PROTECTED_BITS (type) =
+ (int *) obstack_alloc (symbol_obstack,
+ sizeof (int) * (1 + (nfields >> 5)));
+
+ TYPE_NFN_FIELDS (type) = nfn_fields;
+ TYPE_NFN_FIELDS_TOTAL (type) = nfn_fields;
+
+ {
+ int i;
+ for (i = 1; i <= TYPE_N_BASECLASSES (type); ++i)
+ TYPE_NFN_FIELDS_TOTAL (type) +=
+ TYPE_NFN_FIELDS_TOTAL (TYPE_BASECLASS (type, i));
+ }
+
+ TYPE_FN_FIELDLISTS (type) =
+ (struct fn_fieldlist *) obstack_alloc (symbol_obstack,
+ sizeof (struct fn_fieldlist) * nfn_fields);
+
+ /* Copy the saved-up fields into the field vector. */
+
+ for (n = nfields; list; list = list->next)
+ {
+ TYPE_FIELD (type, --n) = list->field;
+ if (list->visibility == 0)
+ SET_TYPE_FIELD_PRIVATE (type, n);
+ else if (list->visibility == 1)
+ SET_TYPE_FIELD_PROTECTED (type, n);
+ }
+
+ for (n = nfn_fields; mainlist; mainlist = mainlist->next)
+ TYPE_FN_FIELDLISTS (type)[--n] = mainlist->fn_fieldlist;
+
+ if (**pp == '~')
+ {
+ *pp += 1;
+
+ if (**pp == '=')
+ {
+ TYPE_FLAGS (type)
+ |= TYPE_FLAG_HAS_CONSTRUCTOR | TYPE_FLAG_HAS_DESTRUCTOR;
+ *pp += 1;
+ }
+ else if (**pp == '+')
+ {
+ TYPE_FLAGS (type) |= TYPE_FLAG_HAS_CONSTRUCTOR;
+ *pp += 1;
+ }
+ else if (**pp == '-')
+ {
+ TYPE_FLAGS (type) |= TYPE_FLAG_HAS_DESTRUCTOR;
+ *pp += 1;
+ }
+
+ /* Read either a '%' or the final ';'. */
+ if (*(*pp)++ == '%')
+ {
+ /* Now we must record the virtual function table pointer's
+ field information. */
+
+ struct type *t;
+ int i;
+
+ t = read_type (pp);
+ p = (*pp)++;
+ while (*p != ';') p++;
+ TYPE_VPTR_BASETYPE (type) = t;
+ if (type == t)
+ {
+ if (TYPE_FIELD_NAME (t, 0) == 0)
+ TYPE_VPTR_FIELDNO (type) = i = 0;
+ else for (i = TYPE_NFIELDS (t) - 1; i >= 0; --i)
+ if (! strncmp (TYPE_FIELD_NAME (t, i), *pp,
+ strlen (TYPE_FIELD_NAME (t, i))))
+ {
+ TYPE_VPTR_FIELDNO (type) = i;
+ break;
+ }
+ if (i < 0)
+ error ("virtual function table field not found");
+ }
+ else
+ TYPE_VPTR_FIELDNO (type) = TYPE_VPTR_FIELDNO (TYPE_BASECLASS (type, 1));
+ *pp = p + 1;
+ }
+ else
+ {
+ TYPE_VPTR_BASETYPE (type) = 0;
+ TYPE_VPTR_FIELDNO (type) = -1;
+ }
+ }
+ else
+ {
+ TYPE_VPTR_BASETYPE (type) = 0;
+ TYPE_VPTR_FIELDNO (type) = -1;
+ }
+
+ return type;
+}
+
+/* Read a definition of an array type,
+ and create and return a suitable type object.
+ Also creates a range type which represents the bounds of that
+ array. */
+static struct type *
+read_array_type (pp, type)
+ register char **pp;
+ register struct type *type;
+{
+ struct type *index_type, *element_type, *range_type;
+ int lower, upper;
+ int adjustable = 0;
+
+ /* Format of an array type:
+ "ar<index type>;lower;upper;<array_contents_type>". Put code in
+ to handle this.
+
+ Fortran adjustable arrays use Adigits or Tdigits for lower or upper;
+ for these, produce a type like float[][]. */
+
+ index_type = read_type (pp);
+ if (*(*pp)++ != ';')
+ error ("Invalid symbol data; improper format of array type decl.");
+
+ if (!(**pp >= '0' && **pp <= '9'))
+ {
+ *pp += 1;
+ adjustable = 1;
+ }
+ lower = read_number (pp, ';');
+
+ if (!(**pp >= '0' && **pp <= '9'))
+ {
+ *pp += 1;
+ adjustable = 1;
+ }
+ upper = read_number (pp, ';');
+
+ element_type = read_type (pp);
+
+ if (adjustable)
+ {
+ lower = 0;
+ upper = -1;
+ }
+
+ {
+ /* Create range type. */
+ range_type = (struct type *) obstack_alloc (symbol_obstack,
+ sizeof (struct type));
+ TYPE_CODE (range_type) = TYPE_CODE_RANGE;
+ TYPE_TARGET_TYPE (range_type) = index_type;
+
+ /* This should never be needed. */
+ TYPE_LENGTH (range_type) = sizeof (int);
+
+ TYPE_NFIELDS (range_type) = 2;
+ TYPE_FIELDS (range_type) =
+ (struct field *) obstack_alloc (symbol_obstack,
+ 2 * sizeof (struct field));
+ TYPE_FIELD_BITPOS (range_type, 0) = lower;
+ TYPE_FIELD_BITPOS (range_type, 1) = upper;
+ }
+
+ TYPE_CODE (type) = TYPE_CODE_ARRAY;
+ TYPE_TARGET_TYPE (type) = element_type;
+ TYPE_LENGTH (type) = (upper - lower + 1) * TYPE_LENGTH (element_type);
+ TYPE_NFIELDS (type) = 1;
+ TYPE_FIELDS (type) =
+ (struct field *) obstack_alloc (symbol_obstack,
+ sizeof (struct field));
+ TYPE_FIELD_TYPE (type, 0) = range_type;
+
+ return type;
+}
+
+
+/* Read a definition of an enumeration type,
+ and create and return a suitable type object.
+ Also defines the symbols that represent the values of the type. */
+
+static struct type *
+read_enum_type (pp, type)
+ register char **pp;
+ register struct type *type;
+{
+ register char *p;
+ char *name;
+ register long n;
+ register struct symbol *sym;
+ int nsyms = 0;
+ struct pending **symlist;
+ struct pending *osyms, *syms;
+ int o_nsyms;
+
+ if (within_function)
+ symlist = &local_symbols;
+ else
+ symlist = &file_symbols;
+ osyms = *symlist;
+ o_nsyms = osyms ? osyms->nsyms : 0;
+
+ /* Read the value-names and their values.
+ The input syntax is NAME:VALUE,NAME:VALUE, and so on.
+ A semicolon or comman instead of a NAME means the end. */
+ while (**pp && **pp != ';' && **pp != ',')
+ {
+ /* Check for and handle cretinous dbx symbol name continuation! */
+ if (**pp == '\\') *pp = next_symbol_text ();
+
+ p = *pp;
+ while (*p != ':') p++;
+ name = obsavestring (*pp, p - *pp);
+ *pp = p + 1;
+ n = read_number (pp, ',');
+
+ sym = (struct symbol *) obstack_alloc (symbol_obstack, sizeof (struct symbol));
+ bzero (sym, sizeof (struct symbol));
+ SYMBOL_NAME (sym) = name;
+ SYMBOL_CLASS (sym) = LOC_CONST;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ SYMBOL_VALUE (sym) = n;
+ add_symbol_to_list (sym, symlist);
+ nsyms++;
+ }
+
+ if (**pp == ';')
+ (*pp)++; /* Skip the semicolon. */
+
+ /* Now fill in the fields of the type-structure. */
+
+ TYPE_LENGTH (type) = sizeof (int);
+ TYPE_CODE (type) = TYPE_CODE_ENUM;
+ TYPE_NFIELDS (type) = nsyms;
+ TYPE_FIELDS (type) = (struct field *) obstack_alloc (symbol_obstack, sizeof (struct field) * nsyms);
+
+ /* Find the symbols for the values and put them into the type.
+ The symbols can be found in the symlist that we put them on
+ to cause them to be defined. osyms contains the old value
+ of that symlist; everything up to there was defined by us. */
+ /* Note that we preserve the order of the enum constants, so
+ that in something like "enum {FOO, LAST_THING=FOO}" we print
+ FOO, not LAST_THING. */
+
+ for (syms = *symlist, n = 0; syms; syms = syms->next)
+ {
+ int j = 0;
+ if (syms == osyms)
+ j = o_nsyms;
+ for (; j < syms->nsyms; j++)
+ {
+ struct symbol *sym = syms->symbol[j];
+ SYMBOL_TYPE (sym) = type;
+ TYPE_FIELD_NAME (type, n) = SYMBOL_NAME (sym);
+ TYPE_FIELD_VALUE (type, n) = 0;
+ TYPE_FIELD_BITPOS (type, n) = SYMBOL_VALUE (sym);
+ TYPE_FIELD_BITSIZE (type, n++) = 0;
+ }
+ if (syms == osyms)
+ break;
+ }
+
+ return type;
+}
+
+#define MAX_OF_TYPE(t) ((1 << (sizeof (t) - 1)) - 1)
+#define MIN_OF_TYPE(t) (-(1 << (sizeof (t) - 1)))
+
+static struct type *
+read_range_type (pp, typenums)
+ char **pp;
+ int typenums[2];
+{
+ int rangenums[2];
+ long n2, n3;
+ int n2bits, n3bits;
+ int self_subrange;
+ struct type *result_type;
+ struct type *index_type;
+
+ /* First comes a type we are a subrange of.
+ In C it is usually 0, 1 or the type being defined. */
+ read_type_number (pp, rangenums);
+ self_subrange = (rangenums[0] == typenums[0] &&
+ rangenums[1] == typenums[1]);
+
+ /* A semicolon should now follow; skip it. */
+ if (**pp == ';')
+ (*pp)++;
+
+ /* The remaining two operands are usually lower and upper bounds
+ of the range. But in some special cases they mean something else. */
+ read_huge_number (pp, ';', &n2, &n2bits);
+ read_huge_number (pp, ';', &n3, &n3bits);
+
+ if (n2bits == -1 || n3bits == -1)
+ error ("Unrecognized type range %s.", pp);
+
+ if (n2bits != 0 || n3bits != 0)
+#ifdef LONG_LONG
+ {
+ char got_signed = 0;
+ char got_unsigned = 0;
+ /* Number of bits in the type. */
+ int nbits;
+
+ /* Range from 0 to <large number> is an unsigned large integral type. */
+ if ((n2bits == 0 && n2 == 0) && n3bits != 0)
+ {
+ got_unsigned = 1;
+ nbits = n3bits;
+ }
+ /* Range fro <large number> to <large number>-1 is a large signed
+ integral type. */
+ else if (n2bits != 0 && n3bits != 0 && n2bits == n3bits + 1)
+ {
+ got_signed = 1;
+ nbits = n2bits;
+ }
+
+ /* Check for "long long". */
+ if (got_signed && nbits == CHAR_BIT * sizeof (long long))
+ return builtin_type_long_long;
+ if (got_unsigned && nbits == CHAR_BIT * sizeof (long long))
+ return builtin_type_unsigned_long_long;
+
+ error ("Large type isn't a long long.");
+ }
+#else /* LONG_LONG */
+ error ("Type long long not supported on this machine.");
+#endif
+
+ /* A type defined as a subrange of itself, with bounds both 0, is void. */
+ if (self_subrange && n2 == 0 && n3 == 0)
+ return builtin_type_void;
+
+ /* If n3 is zero and n2 is not, we want a floating type,
+ and n2 is the width in bytes.
+
+ Fortran programs appear to use this for complex types also,
+ and they give no way to distinguish between double and single-complex!
+ We don't have complex types, so we would lose on all fortran files!
+ So return type `double' for all of those. It won't work right
+ for the complex values, but at least it makes the file loadable. */
+
+ if (n3 == 0 && n2 > 0)
+ {
+ if (n2 == sizeof (float))
+ return builtin_type_float;
+ return builtin_type_double;
+ }
+
+ /* If the upper bound is -1, it must really be an unsigned int. */
+
+ else if (n2 == 0 && n3 == -1)
+ {
+ if (sizeof (int) == sizeof (long))
+ return builtin_type_unsigned_int;
+ else
+ return builtin_type_unsigned_long;
+ }
+
+ /* Special case: char is defined (Who knows why) as a subrange of
+ itself with range 0-127. */
+ else if (self_subrange && n2 == 0 && n3 == 127)
+ return builtin_type_char;
+
+ /* Assumptions made here: Subrange of self is equivalent to subrange
+ of int. */
+ else if (n2 == 0
+ && (self_subrange ||
+ *dbx_lookup_type (rangenums) == builtin_type_int))
+ {
+ /* an unsigned type */
+ if (n3 == UINT_MAX)
+ return builtin_type_unsigned_int;
+ if (n3 == ULONG_MAX)
+ return builtin_type_unsigned_long;
+ if (n3 == USHRT_MAX)
+ return builtin_type_unsigned_short;
+ if (n3 == UCHAR_MAX)
+ return builtin_type_unsigned_char;
+ }
+#ifdef LONG_LONG
+ else if (n3 == 0 && n2 == -sizeof (long long))
+ return builtin_type_long_long;
+#endif
+ else if (n2 == -n3 -1)
+ {
+ /* a signed type */
+ if (n3 == INT_MAX)
+ return builtin_type_int;
+ if (n3 == LONG_MAX)
+ return builtin_type_long;
+ if (n3 == SHRT_MAX)
+ return builtin_type_short;
+ if (n3 == CHAR_MAX)
+ return builtin_type_char;
+ }
+
+ /* We have a real range type on our hands. Allocate space and
+ return a real pointer. */
+
+ /* At this point I don't have the faintest idea how to deal with
+ a self_subrange type; I'm going to assume that this is used
+ as an idiom, and that all of them are special cases. So . . . */
+ if (self_subrange)
+ error ("Type defined as subrange of itself: %s.", pp);
+
+ result_type = (struct type *) obstack_alloc (symbol_obstack,
+ sizeof (struct type));
+ bzero (result_type, sizeof (struct type));
+
+ TYPE_TARGET_TYPE (result_type) = (self_subrange ?
+ builtin_type_int :
+ *dbx_lookup_type(rangenums));
+
+ /* We have to figure out how many bytes it takes to hold this
+ range type. I'm going to assume that anything that is pushing
+ the bounds of a long was taken care of above. */
+ if (n2 >= MIN_OF_TYPE(char) && n3 <= MAX_OF_TYPE(char))
+ TYPE_LENGTH (result_type) = 1;
+ else if (n2 >= MIN_OF_TYPE(short) && n3 <= MAX_OF_TYPE(short))
+ TYPE_LENGTH (result_type) = sizeof (short);
+ else if (n2 >= MIN_OF_TYPE(int) && n3 <= MAX_OF_TYPE(int))
+ TYPE_LENGTH (result_type) = sizeof (int);
+ else if (n2 >= MIN_OF_TYPE(long) && n3 <= MAX_OF_TYPE(long))
+ TYPE_LENGTH (result_type) = sizeof (long);
+ else
+ error ("Ranged type doesn't fit within known sizes.");
+
+ TYPE_LENGTH (result_type) = TYPE_LENGTH (TYPE_TARGET_TYPE (result_type));
+ TYPE_CODE (result_type) = TYPE_CODE_RANGE;
+ TYPE_NFIELDS (result_type) = 2;
+ TYPE_FIELDS (result_type) =
+ (struct field *) obstack_alloc (symbol_obstack,
+ 2 * sizeof (struct field));
+ bzero (TYPE_FIELDS (result_type), 2 * sizeof (struct field));
+ TYPE_FIELD_BITPOS (result_type, 0) = n2;
+ TYPE_FIELD_BITPOS (result_type, 1) = n3;
+
+ return result_type;
+}
+
+/* Read a number from the string pointed to by *PP.
+ The value of *PP is advanced over the number.
+ If END is nonzero, the character that ends the
+ number must match END, or an error happens;
+ and that character is skipped if it does match.
+ If END is zero, *PP is left pointing to that character. */
+
+static long
+read_number (pp, end)
+ char **pp;
+ int end;
+{
+ register char *p = *pp;
+ register long n = 0;
+ register int c;
+ int sign = 1;
+
+ /* Handle an optional leading minus sign. */
+
+ if (*p == '-')
+ {
+ sign = -1;
+ p++;
+ }
+
+ /* Read the digits, as far as they go. */
+
+ while ((c = *p++) >= '0' && c <= '9')
+ {
+ n *= 10;
+ n += c - '0';
+ }
+ if (end)
+ {
+ if (c && c != end)
+ error ("Invalid symbol data: invalid character \\%03o at symbol pos %d.", c, symnum);
+ }
+ else
+ --p;
+
+ *pp = p;
+ return n * sign;
+}
+
+static void
+read_huge_number (pp, end, valu, bits)
+ char **pp;
+ int end;
+ long *valu;
+ int *bits;
+{
+ char *p = *pp;
+ int sign = 1;
+ long n = 0;
+ int radix = 10;
+ char overflow = 0;
+ int nbits = 0;
+ int c;
+ long upper_limit;
+
+ /* Handle an optional leading minus sign. */
+
+ if (*p == '-')
+ {
+ sign = -1;
+ p++;
+ }
+
+ /* Leading zero means octal. GCC uses this to output values larger
+ than an int (because that would be hard in decimal). */
+ if (*p == '0')
+ {
+ radix = 8;
+ p++;
+ }
+
+ upper_limit = LONG_MAX / radix;
+ while ((c = *p++) >= '0' && c <= '9')
+ {
+ if (n <= upper_limit)
+ {
+ n *= radix;
+ n += c - '0';
+ }
+ else
+ overflow = 1;
+
+ /* This depends on large values being output in octal, which is
+ what GCC does. */
+ if (radix == 8)
+ {
+ if (nbits == 0)
+ {
+ if (c == '0')
+ /* Ignore leading zeroes. */
+ ;
+ else if (c == '1')
+ nbits = 1;
+ else if (c == '2' || c == '3')
+ nbits = 2;
+ else
+ nbits = 3;
+ }
+ else
+ nbits += 3;
+ }
+ }
+ if (end)
+ {
+ if (c && c != end)
+ {
+ if (bits != NULL)
+ *bits = -1;
+ return;
+ }
+ }
+ else
+ --p;
+
+ *pp = p;
+ if (overflow)
+ {
+ if (nbits == 0)
+ {
+ /* Large decimal constants are an error (because it is hard to
+ count how many bits are in them). */
+ if (bits != NULL)
+ *bits = -1;
+ return;
+ }
+
+ /* -0x7f is the same as 0x80. So deal with it by adding one to
+ the number of bits. */
+ if (sign == -1)
+ ++nbits;
+ if (bits)
+ *bits = nbits;
+ }
+ else
+ {
+ if (valu)
+ *valu = n * sign;
+ if (bits)
+ *bits = 0;
+ }
+}
+
+/* Read in an argument list. This is a list of types. It is terminated with
+ a ':', FYI. Return the list of types read in. */
+static struct type **
+read_args (pp, end)
+ char **pp;
+ int end;
+{
+ struct type *types[1024], **rval; /* allow for fns of 1023 parameters */
+ int n = 0;
+
+ while (**pp != end)
+ {
+ if (**pp != ',')
+ error ("Invalid argument list: no ',', at symtab pos %d", symnum);
+ *pp += 1;
+
+ /* Check for and handle cretinous dbx symbol name continuation! */
+ if (**pp == '\\')
+ *pp = next_symbol_text ();
+
+ types[n++] = read_type (pp);
+ }
+ *pp += 1; /* get past `end' (the ':' character) */
+
+ if (n == 1)
+ {
+ rval = (struct type **) xmalloc (2 * sizeof (struct type *));
+ }
+ else if (TYPE_CODE (types[n-1]) != TYPE_CODE_VOID)
+ {
+ rval = (struct type **) xmalloc ((n + 1) * sizeof (struct type *));
+ bzero (rval + n, sizeof (struct type *));
+ }
+ else
+ {
+ rval = (struct type **) xmalloc (n * sizeof (struct type *));
+ }
+ bcopy (types, rval, n * sizeof (struct type *));
+ return rval;
+}
+
+/* This function is really horrible, but to avoid it, there would need
+ to be more filling in of forward references. THIS SHOULD BE MOVED OUT
+ OF COFFREAD.C AND DBXREAD.C TO SOME PLACE WHERE IT CAN BE SHARED */
+int
+fill_in_vptr_fieldno (type)
+ struct type *type;
+{
+ if (TYPE_VPTR_FIELDNO (type) < 0)
+ TYPE_VPTR_FIELDNO (type) =
+ fill_in_vptr_fieldno (TYPE_BASECLASS (type, 1));
+ return TYPE_VPTR_FIELDNO (type);
+}
+
+/* Copy a pending list, used to record the contents of a common
+ block for later fixup. BUG FIX by rde@topexpress.co.uk */
+static struct pending *
+copy_pending (beg, begi, end)
+ struct pending *beg, *end;
+ int begi;
+{
+ struct pending *new = 0;
+ struct pending *next;
+
+ /* rde note: `begi' is an offset in block `end', NOT `beg' */
+ for (next = beg; next != 0; next = next->next)
+ {
+ register int j;
+ for (j = next == end ? begi : 0; j < next->nsyms; j++)
+ add_symbol_to_list (next->symbol[j], &new);
+
+ if (next == end)
+ break;
+ }
+ return new;
+}
+
+/* Add a common block's start address to the offset of each symbol
+ declared to be in it (by being between a BCOMM/ECOMM pair that uses
+ the common block name). */
+
+static void
+fix_common_block (sym, value)
+ struct symbol *sym;
+ int value;
+{
+ struct pending *next = (struct pending *) SYMBOL_NAMESPACE (sym);
+ for ( ; next; next = next->next)
+ {
+ register int j;
+ for (j = next->nsyms - 1; j >= 0; j--)
+ SYMBOL_VALUE (next->symbol[j]) += value;
+ }
+}
+
+void
+_initialize_dbxread ()
+{
+ symfile = 0;
+ header_files = (struct header_file *) 0;
+ this_object_header_files = (int *) 0;
+
+ undef_types_allocated = 20;
+ undef_types_length = 0;
+ undef_types = (struct type **) xmalloc (undef_types_allocated *
+ sizeof (struct type *));
+
+ add_com ("symbol-file", class_files, symbol_file_command,
+ "Load symbol table (in dbx format) from executable file FILE.");
+
+ add_com ("add-file", class_files, add_file_command,
+ "Load the symbols from FILE, assuming its code is at TEXT_START.") ;
+}
+
+#endif /* READ_DBX_FORMAT */
diff --git a/gnu/usr.bin/gdb/defs.h b/gnu/usr.bin/gdb/defs.h
new file mode 100644
index 0000000..de744fc
--- /dev/null
+++ b/gnu/usr.bin/gdb/defs.h
@@ -0,0 +1,122 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ *
+ * @(#)defs.h 6.3 (Berkeley) 5/8/91
+ */
+
+/* Basic definitions for GDB, the GNU debugger.
+ Copyright (C) 1986, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define CORE_ADDR unsigned int
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#define max(a, b) ((a) > (b) ? (a) : (b))
+
+extern char *savestring ();
+extern char *concat ();
+extern char *xmalloc (), *xrealloc ();
+extern int parse_escape ();
+extern char *reg_names[];
+
+/* Various possibilities for alloca. */
+#ifdef sparc
+#include <alloca.h>
+#else
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else
+extern char *alloca ();
+#endif
+#endif
+
+extern int quit_flag;
+
+extern int immediate_quit;
+
+#define QUIT { if (quit_flag) quit (); }
+
+/* Notes on classes: class_alias is for alias commands which are not
+ abbreviations of the original command. */
+
+enum command_class
+{
+ no_class = -1, class_run = 0, class_vars, class_stack,
+ class_files, class_support, class_info, class_breakpoint,
+ class_alias, class_obscure, class_user,
+};
+
+/* the cleanup list records things that have to be undone
+ if an error happens (descriptors to be closed, memory to be freed, etc.)
+ Each link in the chain records a function to call and an
+ argument to give it.
+
+ Use make_cleanup to add an element to the cleanup chain.
+ Use do_cleanups to do all cleanup actions back to a given
+ point in the chain. Use discard_cleanups to remove cleanups
+ from the chain back to a given point, not doing them. */
+
+struct cleanup
+{
+ struct cleanup *next;
+ void (*function) ();
+ int arg;
+};
+
+extern void do_cleanups ();
+extern void discard_cleanups ();
+extern struct cleanup *make_cleanup ();
+extern struct cleanup *save_cleanups ();
+extern void restore_cleanups ();
+extern void free_current_contents ();
+extern void reinitialize_more_filter ();
+extern void fputs_filtered ();
+extern void fprintf_filtered ();
+extern void printf_filtered ();
+extern void print_spaces_filtered ();
+extern char *tilde_expand ();
+
+/* Structure for saved commands lines
+ (for breakpoints, defined commands, etc). */
+
+struct command_line
+{
+ struct command_line *next;
+ char *line;
+ int type; /* statement type */
+#define CL_END 0
+#define CL_NORMAL 1
+#define CL_WHILE 2
+#define CL_IF 3
+#define CL_EXITLOOP 4
+#define CL_NOP 5
+ struct command_line *body; /* body of loop for while, body of if */
+ struct command_line *elsebody; /* body of else part of if */
+};
+
+extern struct command_line *read_command_lines ();
+extern void do_command_lines();
+
+/* String containing the current directory (what getwd would return). */
+
+char *current_directory;
+
diff --git a/gnu/usr.bin/gdb/doc/ChangeLog b/gnu/usr.bin/gdb/doc/ChangeLog
new file mode 100644
index 0000000..fb86719
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/ChangeLog
@@ -0,0 +1,783 @@
+Tue Oct 19 14:21:18 1993 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * gdb.texinfo (Sourc Path): index entries for $cwd, $pdir
+
+ * a4rc.sed: update to work with Andreas Vogel papersize params
+
+ * refcard.tex: use Andreas Vogel simplifications of papersize
+ params; remove useless version info; update copyright date.
+
+Tue Oct 19 10:46:22 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * gdb.texinfo (Symbols): Add class NAME to doc for ptype.
+
+Tue Oct 12 09:11:45 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * gdb.texinfo (Files): Say what address the load command loads it at.
+
+ * stabs.texinfo (Common Blocks): Minor cleanups.
+
+ * stabs.texinfo: Update ld stabs in elf relocation to reflect the fact
+ that Sun has backed away from the linker kludge and thus the relevant
+ issue is changes to the SunPRO tools, not the Solaris linker.
+
+ * stabs.texinfo (Traditional Integer Types): Clean up description
+ of octal bounds a little bit. Document extra leading zeroes.
+
+Thu Oct 7 16:15:37 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * gdb.texinfo (Signaling): Update for symbolic symbol names
+ and add a section explaining the difference between the GDB
+ signal command and the shell kill utility.
+
+Wed Oct 6 13:23:01 1993 Tom Lord (lord@rtl.cygnus.com)
+
+ * libgdb.texinfo: added `@' to braces that were unescaped.
+
+Mon Oct 4 10:42:18 1993 Tom Lord (lord@rtl.cygnus.com)
+
+ * libgdb.texinfo: new file. Spec for the gdb library.
+
+Sun Oct 3 15:26:56 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo (Include Files): Fix typo (start -> end).
+
+Thu Sep 30 18:24:56 1993 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * gdb.texinfo, remote.texi: assorted small improvements, mostly
+ from Melissa at FSF's editing pass.
+
+Thu Sep 30 11:54:38 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * gdb.texinfo: Remove stuff about ar and 14 character filenames.
+ I believe this was fixed by the 13 Sep 89 change to print_frame_info.
+ Also, modern versions of ar like BSD 4.4 or SVR4 don't have this bug.
+
+Wed Sep 22 21:22:11 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * remote.texi (Bootstrapping): Discuss 386 call gates.
+
+Sat Sep 18 17:10:44 1993 Jim Kingdon (kingdon@poseidon.cygnus.com)
+
+ * stabs.texinfo (Based Variables): New node.
+
+Thu Sep 16 17:48:55 1993 Jim Kingdon (kingdon@cirdan.cygnus.com)
+
+ * stabs.texinfo (Negative Type Numbers): Re-write discussions of
+ names, sizes, and formats to suggest how not to lose.
+
+Sat Sep 11 09:35:11 1993 Jim Kingdon (kingdon@poseidon.cygnus.com)
+
+ * stabs.texinfo (Methods): Fix typo.
+
+Fri Sep 10 06:34:20 1993 David J. Mackenzie (djm@thepub.cygnus.com)
+
+ * gdb.texinfo: Fix a few typos.
+
+Wed Sep 8 09:11:52 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * gdb.texinfo: Clarify how well it works with Fortran.
+
+ * stabs.texinfo (Stabs In ELF, Statics, ELF Transformations):
+ More on relocating stabs in ELF files.
+
+Tue Sep 7 13:45:02 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo (Stabs In ELF): Talk about N_FUN value.
+
+Mon Sep 6 19:23:18 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo (Local Variable Parameters): Talk about nameless
+ parameters on VAX.
+
+Fri Sep 3 17:06:08 1993 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * gdb.texinfo: @up/@down -> @raisesections/@lowersections
+
+Fri Sep 3 12:04:15 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo: Make info author notice match the TeX author notice.
+
+Tue Aug 31 13:21:06 1993 David J. Mackenzie (djm@thepub.cygnus.com)
+
+ * stabs.texinfo: Initial-caps all words in node names and
+ non-trivial words in section names.
+
+Mon Aug 30 11:13:16 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo: Many minor cleanups.
+
+ * stabs.texinfo: Remove @deffn except from Expanded Reference node.
+
+Sat Aug 28 12:08:09 1993 David J. MacKenzie (djm@edison.eng.umd.edu)
+
+ * stabs.texinfo: Remove full description of big example.
+ It's not really helpful; just use pieces of it where appropriate.
+ Add more Texinfo formatting directives (@samp, etc.).
+ Use @deffn to define stab types.
+ Eliminate some wordiness. Break up some nodes.
+ Add an (alphabetized) index of symbol types.
+ Use consistent capitalization style in node and section names.
+
+Thu Aug 26 06:36:31 1993 Fred Fish (fnf@deneb.cygnus.com)
+
+ * gdb.texinfo: Change typo "Two two" to "The two".
+
+Sun Aug 22 12:15:18 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo (XCOFF-differences): Remove references to
+ non-existent types N_DECL and N_RPSYM.
+
+ * stabs.texinfo (String Field): Say that type attributes bug is
+ fixed in GDB 4.10, since it is.
+
+ * stabs.texinfo: Clean up djm cleanups, and more cleanups of my own.
+
+Sat Aug 21 04:32:28 1993 David MacKenzie (djm@cygnus.com)
+
+ * stabs.texinfo: Formatting cleanups.
+
+Fri Aug 20 20:49:53 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo: When explaining the n_type of a stab, standardize
+ how we do it ('#' as a comment indicator, "36 is N_FUN" as text,
+ no tabs, use @r).
+ (Global Variables): Clean up.
+
+Tue Aug 17 15:57:27 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo (Stack Variables): Re-write.
+
+Mon Aug 16 21:20:08 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo (Stabs-in-elf): Talk about getting the start
+ addresses of a source file. Also revise formatting.
+ Change "object module" or "object file" to "source file".
+ Various: Miscellaneous cleanups.
+
+Thu Aug 12 15:11:51 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo: Point to mangling info in gcc's gpcompare.texi.
+
+Tue Aug 10 16:57:49 1993 Stan Shebs (shebs@rtl.cygnus.com)
+
+ * gdbint.texinfo: Removed many nonsensical machine-collected
+ host and target conditionals, described some of the remainder.
+
+Tue Aug 10 13:28:30 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * gdbint.texinfo (Getting Started): Use @itemize, not @table.
+
+ * gdbint.texinfo (Top): Add name to @top line, and re-write the
+ paragraph which follows.
+
+ * gdbint.texinfo (Host): Use @code not @samp for Makefile
+ variables. Looks better and avoids overful hbox.
+
+Fri Jul 30 18:26:21 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo (Procedures): Improve stuff on nested functions.
+
+Thu Jul 29 15:10:58 1993 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * remote.texi: (MIPS Remote) clearer doc for set/show timeout,
+ retransmit-timeout
+
+Thu Jul 29 13:16:09 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * gdbint.texinfo: Update statement about `some ancient Unix
+ systems, like Ultrix 4.0' to Ultrix 4.2.
+
+Wed Jul 28 15:26:53 1993 Roland H. Pesch (pesch@el_bosque.cygnus.com)
+
+ * h8-cfg.texi, all-cfg.texi: new flag GDBSERVER
+
+ * Makefile.in: depend on remote.texi rather than gdbinv-s.texi
+
+ * remote.texi: (Server) New node on gdbserver. (Remote Serial,
+ ST2000 Remote, MIPS Remote): mention `host:port' syntax for TCP.
+
+ * remote.texi: new name for former gdbinv-s.texi
+
+ * gdb.texinfo: use remote.texi rather than gdbinv-s.texi
+
+Wed Jul 28 08:26:24 1993 Ian Lance Taylor (ian@cygnus.com)
+
+ * gdbinv-s.texi: Documented timeout and retransmit-timeout
+ variables for MIPS remote debugging protocol.
+
+Mon Jul 26 13:00:09 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo (Negative Type Numbers): FORTRAN LOGICAL fix.
+
+Tue Jul 20 16:30:41 1993 Jim Kingdon (kingdon@deneb.cygnus.com)
+
+ * Makefile.in (refcard.dvi): Use srcdir where necessary.
+
+Mon Jul 19 12:02:50 1993 Roland H. Pesch (pesch@cygnus.com)
+
+ * gdb.texinfo: repair conditional bugs in text markup
+
+Fri Jul 16 18:57:50 1993 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * gdb.texinfo, all-cfg.texi, h8-cfg.texi: introduce MOD2 switch
+ to select Modula-2 material.
+
+Thu Jul 15 13:15:01 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo: Cleanups regarding statics.
+
+ * gdbinv-s.texi (Bootstrapping): Document exceptionHandler.
+ (Debug Session): Mention exceptionHandler. Add xref to Bootstrapping.
+
+Mon Jul 12 13:37:02 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo: N_MAIN is sometimes used for C.
+
+Fri Jul 9 09:47:02 1993 Peter Schauer (pes@regent.e-technik.tu-muenchen.de)
+
+ * gdbint.texinfo (Host, Target Conditionals): Remove TM_FILE_OVERRIDE.
+
+Tue Jul 6 12:41:28 1993 John Gilmore (gnu@cygnus.com)
+
+ * gdbint.texinfo (Target Conditionals): Remove NO_TYPEDEFS,
+ removed from the code by Kingdon.
+
+Tue Jul 6 12:24:34 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * gdb.texinfo (Break Commands): Remove stuff about flushing terminal
+ input when evaluating breakpoint conditions; the bug has been fixed.
+
+ * gdb.texinfo (Continuing and Stepping): Argument to "continue"
+ sets the ignore count to N-1, not to N.
+
+Thu Jul 1 14:57:42 1993 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * refcard.tex (\hoffset): correct longstanding error to match
+ intended offset; avoids cutting off edge on some printers
+
+Wed Jun 30 18:23:06 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo (Parameters): Say that order of stabs is significant.
+
+Fri Jun 25 21:34:52 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo (Common Blocks): Say what Sun FORTRAN does.
+
+Fri Jun 25 16:15:10 1993 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * Makefile.in: (REFEDITS) new var to control whether PS or CM
+ fonts and whether US or A4 paper for GDB refcard; (refcard.dvi)
+ collect sed edits if any, apply to refcard before formatting;
+ (refcard.ps) stop implying PS fonts if PS output requested;
+ (lrefcard.ps) delete extra target for variant PS fonts
+
+ * refcard.tex: parametrize papersize dependent info, collect
+ in easily replaced spot
+
+ * a4rc.sed: new file, edits to refcard for A4 paper
+
+Fri Jun 25 14:21:46 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo (Negative Type Numbers): Type -16 is 4 bytes.
+
+Wed Jun 23 15:02:50 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo (Negative Type Numbers): Minor character cleanups.
+
+Tue Jun 22 16:31:52 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo: Express disapproval of 'D' symbol descriptor
+ politely rather than rudely.
+
+Fri Jun 18 19:42:09 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo: Document common blocks.
+
+Fri Jun 18 12:12:57 1993 Fred Fish (fnf@cygnus.com)
+
+ * stabs.texinfo: Add some basic info about stabs-in-elf.
+
+Fri Jun 18 13:57:09 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo (Top): Minor cleanup.
+
+Mon Jun 14 16:16:51 1993 david d `zoo' zuhn (zoo at rtl.cygnus.com)
+
+ * Makefile.in (install-info): remove parentdir support
+
+Tue Jun 15 18:11:39 1993 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * gdb.texinfo (Copying): delete this node and references to it;
+ RMS says this manual need not carry GPL. (passim): Improvements
+ from last round at FSF, largely due to Ian Taylor review, and
+ minor formatting improvements.
+
+ * gdbinv-s.texi (passim): Improvements from last round at FSF,
+ largely due to Ian Taylor review. (Debug Session): minor edits to
+ new text.
+
+Sun Jun 13 12:52:39 1993 Jim Kingdon (kingdon@cygnus.com)
+
+ * Makefile.in (realclean): Remove info and dvi files too.
+
+Sat Jun 12 16:09:22 1993 Jim Kingdon (kingdon@cygnus.com)
+
+ * {all,h8}-config.texi: Rename to *-cfg.texi for 14 char filenames.
+ * Makefile.in: Change accordingly. gdb-config.texi -> gdb-cfg.texi.
+ * gdb.texinfo: Change accordingly.
+
+ * stabs.texinfo: Clean up N_{L,R}BRAC. Discuss what addresses of
+ N_{L,R}BRAC,N_SLINE are relative to.
+
+Fri Jun 11 15:15:55 1993 Jim Kingdon (kingdon@cygnus.com)
+
+ * Makefile.in (GDBvn.texi): Update atomically.
+
+Wed Jun 9 10:58:16 1993 Jim Kingdon (kingdon@cygnus.com)
+
+ * gdbinv-s.texi (Debug Session): Document exceptionHook.
+
+Tue Jun 8 13:42:04 1993 Jim Kingdon (kingdon@cygnus.com)
+
+ * gdb.texinfo (Print Settings): Move all stuff relating to symbolic
+ addresses together. Also motivate the set print symbol-filename
+ command and suggest other solutions.
+
+Tue Jun 1 22:46:43 1993 Fred Fish (fnf@cygnus.com)
+
+ * gdb.texinfo (set print elements): Note that the number of
+ elements is set to unlimited by "set print elements 0".
+
+Mon May 31 08:06:55 1993 Jim Kingdon (kingdon@cygnus.com)
+
+ * stabs.texinfo (Builtin Type Descriptors): Try to clarify what
+ NF_LDOUBLE means.
+ (Stab Types): Include Solaris stab types.
+ (Procedures): Document Solaris extensions.
+
+Thu May 27 06:20:42 1993 Peter Schauer (pes@regent.e-technik.tu-muenchen.de)
+
+ * gdb.texinfo: Add `set print symbol-filename' doc.
+
+Wed May 26 00:26:42 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo (Arrays): Talk about type definition vs. type
+ information.
+
+ * stabs.texinfo (Builtin Type Descriptors): Talk about omitting
+ the trailing semicolon.
+
+Tue May 25 14:49:42 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo (Line Numbers, Source Files): Re-write these two nodes
+ and merge in other parts of the document addressing these subjects.
+ gdbint.texinfo (XCOFF): Remove info which is now in stabs.texinfo.
+
+ * stabs.texinfo (Subranges, Arrays): Try to explain about the semicolon
+ at the end of a range type.
+
+ * stabs.texinfo (Subranges): "A offset" and "T offset" are not
+ AIX extensions.
+
+Mon May 24 09:00:33 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo (Stabs Format): Misc fixes.
+
+Sat May 22 10:40:56 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo (Constants): Allow an `e' constant to be non-enum.
+ (Traditional builtin types): Document convex convention for long long.
+ (Negative builtin types): Discuss type names, and misc fixes.
+
+Fri May 21 11:20:31 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo (Builtin Type Descriptors): Document the floating
+ point types used with @samp{R} type descriptor.
+ (Symbol Descriptors): Describe how to handle conflict between
+ different meanings of @samp{P} symbol descriptor.
+
+Thu May 20 13:35:10 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo: Remove node Quick Reference and put its children
+ directly under the main menu.
+
+ * stabs.texinfo: Many more changes to bring it into line with
+ AIX documentation and reality. I think it now has all the
+ information from the AIX documentation, except that I burned
+ out when I got to variant records (Pascal and Modula-2) and
+ all the COBOL types. Oh well, we can add them later when we're
+ worrying more about those languages.
+
+ * stabs.texinfo (Automatic variables): Talk about what it means
+ to omit the symbol descriptor.
+
+Tue May 18 17:59:18 1993 Jim Kingdon (kingdon@lioth.cygnus.com)
+
+ * stabs.texinfo (Parameters): Add "(sometimes)" when describing
+ gcc2 behavior with promoted args.
+
+Fri May 14 21:35:29 1993 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * gdb.texinfo: include readline appendices in info version of manual
+
+Fri May 7 11:56:18 1993 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * gdbinv-s.texi (Remote Serial): describe new ^C behavior in
+ target remote.
+
+ * gdb.texinfo (Machine Code): more index entries for disassemble
+
+Fri May 7 10:12:30 1993 Fred Fish (fnf@cygnus.com)
+
+ * Clarify the intended use of the gdb-testers and gdb-patches
+ mailing lists, and shrink gzip comment.
+
+Thu May 6 16:39:50 1993 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * gdb.texinfo (Shell Commands): do not mention SHELL env var in
+ DOSHOST configuration of manual.
+
+ * gdb.texinfo (MIPS Stack): new node.
+
+ * all-config.texi (MIPS) new switch.
+
+ * gdbinv-s.texi (Nindy Options) Remove two instances of future
+ tense; (MIPS Remote) new node.
+
+ * gdb.texinfo (passim) rephrases to work around makeinfo @value
+ bug; (Environment) less passive, other small cleanups in text about
+ .cshrc/.bashrc; (Invoking GDB) new MIPS Remote menu entry;
+ (Remote) new MIPS Remote menu entry.
+
+Thu Apr 29 09:36:25 1993 Jim Kingdon (kingdon@cygnus.com)
+
+ * stabs.texinfo: Many changes to include information from the
+ AIX documentation.
+
+ * gdb.texinfo (Environment): Mention pitfall with .cshrc.
+
+Tue Apr 27 14:02:57 1993 Jim Kingdon (kingdon@cygnus.com)
+
+ * gdbint.texinfo (new node Debugging GDB, elsewhere):
+ Move a bunch of information from ../README.
+ (Getting Started): New node.
+
+Fri Apr 23 17:21:13 1993 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * gdbinv-s.texi, gdb.texinfo: include Hitachi SH target
+
+ * gdb.texinfo: advance manual revision dates to present
+
+ * gdbinv-s.texi, gdb.texinfo, all-config.texi, h8-config.texi:
+ stop using silly Roman numerals in @set variable names
+
+Fri Apr 23 07:30:01 1993 Jim Kingdon (kingdon@cygnus.com)
+
+ * stabs.texinfo (Parameters): Keep trying to get this right.
+
+Wed Apr 21 15:18:47 1993 Jim Kingdon (kingdon@cygnus.com)
+
+ * stabs.texinfo (Parameters): More on "local parameters".
+
+Mon Apr 19 08:00:51 1993 Jim Kingdon (kingdon@cygnus.com)
+
+ * stabs.texinfo (Parameters): Re-do "local parameters" section.
+
+Sun Apr 18 09:47:45 1993 Jim Kingdon (kingdon@cygnus.com)
+
+ * stabs.texinfo (Symbol descriptors): Re-do using @table and @xref.
+ (Parameters): Rewrite.
+ (xcoff-differences, Sun-differences): Minor changes.
+
+Thu Apr 15 02:35:24 1993 John Gilmore (gnu@cacophony.cygnus.com)
+
+ * stabs.texinfo: Minor cleanup.
+
+Wed Apr 14 17:31:00 1993 Jim Kingdon (kingdon@cygnus.com)
+
+ * gdbint.texinfo: Minor xcoff stuff.
+
+Wed Apr 7 14:11:07 1993 Fred Fish (fnf@cygnus.com)
+
+ * gdbint.texinfo: Update for new config directory structure.
+ Add info about internal type data structures.
+
+Mon Apr 5 09:06:30 1993 Ian Lance Taylor (ian@cygnus.com)
+
+ * Makefile.in (SFILES_INCLUDED): gdb-config.texi is no longer in
+ $(srcdir).
+ (gdb-config.texi): Depend on file in $(srcdir).
+
+Fri Apr 2 16:55:13 1993 Jim Kingdon (kingdon@cygnus.com)
+
+ * stabs.texinfo: Fixes about N_SO.
+
+Fri Mar 26 18:00:35 1993 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * gdb.texinfo: include list of nonstandard init file names
+
+ * *-config.texi: new switch GENERIC for text that applies *only*
+ to (usual) multiple-target version of manual
+
+ * gdb.texinfo, gdbinv-s.texi: Update conditional markup to correct
+ h8 config
+
+ * gdb.texinfo: depend on latest fixed makeinfo, use conditionals
+ in menus (rather than conditionally selected multiple alternative
+ menus).
+
+ * Makefile.in: define and use DOC_CONFIG var to select
+ configuration for GDB user manual.
+
+ * gdb-config.texi: delete from repository, generate from Makefile.
+
+ * all-config.texi: normal `generic' configuration file, formerly
+ stored as gdb-config.texi
+
+Wed Mar 24 14:03:19 1993 david d `zoo' zuhn (zoo at poseidon.cygnus.com)
+
+ * Makefile.in: add dvi target to build all .dvi files
+
+Tue Mar 23 16:03:24 1993 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * gdb.texinfo, gdvinv-s.texinfo: formatting improvements.
+
+Fri Mar 19 21:46:50 1993 John Gilmore (gnu@cygnus.com)
+
+ * gdbint.texinfo: Doc NO_MMALLOC and NO_MMALLOC_CHECK as
+ host conditionals.
+ * stabs.texinfo: More array fixes inspired by Jim's.
+
+Fri Mar 19 10:23:34 1993 Jim Kingdon (kingdon@cygnus.com)
+
+ * stabs.texinfo: Fixes re arrays and continuations.
+
+ * gdbint.texinfo: Add XCOFF node.
+
+Mon Mar 8 15:52:18 1993 John Gilmore (gnu@cygnus.com)
+
+ * gdb.texinfo: Add `set print max-symbolic-offset' doc.
+
+Sun Feb 21 17:09:38 1993 Per Bothner (bothner@rtl.cygnus.com)
+
+ * stabs.texinfo: Fix for array types to mention lower bounds.
+
+Thu Feb 18 01:19:49 1993 John Gilmore (gnu@cygnus.com)
+
+ * gdbint.texinfo: Update PTRACE_ARG3_TYPE doc, pull PT_*.
+
+Wed Feb 17 08:15:24 1993 John Gilmore (gnu@cygnus.com)
+
+ * gdbint.texinfo: Remove SET_STACK_LIMIT_HUGE from target defines.
+
+Thu Feb 11 10:38:40 1993 John Gilmore (gnu@cygnus.com)
+
+ * gdbint.texinfo: Fix thinko (NM_FILE => NAT_FILE). Found
+ by Michael Ben-Gershon <mybg@CS.HUJI.AC.IL>.
+
+Wed Feb 10 23:59:19 1993 John Gilmore (gnu@cygnus.com)
+
+ * gdbint.texinfo: Eliminate IBM6000_HOST, document IBM6000_TARGET.
+
+Tue Feb 9 18:26:21 1993 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * gdb.texinfo, gdbinv-s.texi: misc updates
+
+Sat Feb 6 10:25:47 1993 John Gilmore (gnu@cygnus.com)
+
+ * gdbint.texinfo: Brief documentation for longjmp support,
+ from an email msg by Stu.
+
+Fri Feb 5 14:10:15 1993 John Gilmore (gnu@cygnus.com)
+
+ * stabs.texinfo: Fix description of floating point "range"
+ types (which really define basic types). Reported by Jim Meehan,
+ <meehan@src.dec.com>.
+
+ * gdbint.texinfo: Remove COFF_NO_LONG_FILE_NAMES define, now gone.
+
+Thu Feb 4 13:56:46 1993 Ian Lance Taylor (ian@cygnus.com)
+
+ * gdbint.texinfo: Slightly expand section on supporting a new
+ object file format.
+
+Thu Feb 4 01:49:04 1993 John Gilmore (gnu@cygnus.com)
+
+ * Makefile.in (refcard.ps, lrefcard.ps): Remove psref.tex
+ intermediate file.
+
+Tue Feb 2 12:18:06 1993 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * gdb.texinfo, gdbinv-s.texi: miscellaneous stylistic cleanups
+
+Mon Feb 1 15:35:47 1993 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * gdbinv-s.texi: z8000 simulator target name is just "sim"
+
+ * gdbinv-s.texi: Mention that Z8000 simulator can simulate Z8001
+ as well as Z8002.
+
+Sat Nov 28 06:51:35 1992 John Gilmore (gnu@cygnus.com)
+
+ * gdbint.texinfo: Add sections on clean design and on how to send
+ in changes.
+
+Mon Nov 9 23:57:02 1992 John Gilmore (gnu@cygnus.com)
+
+ * gdbint.texinfo: Add how to declare the result of make_cleanup.
+
+Mon Oct 26 11:09:47 1992 John Gilmore (gnu@cygnus.com)
+
+ * gdb.texinfo: Fix typo, reported by Karl Berry.
+
+Fri Oct 23 00:41:21 1992 John Gilmore (gnu@cygnus.com)
+
+ * gdb.texinfo: Add opcodes dir to GDB distribution description.
+
+Sat Oct 10 18:04:58 1992 david d `zoo' zuhn (zoo at cirdan.cygnus.com)
+
+ * gdbint.texinfo: fixed a stray email address (needs @@),
+ added @table @code to node "Native Conditionals"
+
+Tue Sep 22 00:34:15 1992 John Gilmore (gnu@cygnus.com)
+
+ * gdbint.texinfo: Describe coding style of GDB.
+
+Mon Sep 21 19:32:16 1992 John Gilmore (gnu@cygnus.com)
+
+ * stabs.texinfo: Minor wording changes.
+
+Tue Sep 15 02:57:09 1992 John Gilmore (gnu@cygnus.com)
+
+ * gdbint.texinfo: Improve release doc slightly.
+
+Fri Sep 11 01:34:25 1992 John Gilmore (gnu@sphagnum.cygnus.com)
+
+ * gdbint.texinfo: Improve doc of GDB config macros.
+
+Wed Sep 9 16:52:06 1992 John Gilmore (gnu@cygnus.com)
+
+ * stabs.texinfo: Remove Bothner's changes for C++ nested types.
+ These will be reinserted when examined.
+
+Mon Aug 24 01:17:55 1992 John Gilmore (gnu@cygnus.com)
+
+ * gdbint.texinfo: Make a start at documenting all the #if macros
+ in GDB. At least list them all, and start separating them into
+ host-specific and target-specific.
+
+Tue Aug 18 15:59:13 1992 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * gdbinv-s.m4.in: refrain from using @cartouche for just a few
+ examples (not consistent w others).
+ gdb.texinfo: issue disclaimer paragraph on cmdline options only
+ for generic vn of doc
+
+Tue Aug 18 14:53:27 1992 Ian Lance Taylor (ian@cygnus.com)
+
+ * Makefile.in: always create installation directories.
+
+Tue Aug 18 14:11:50 1992 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * gdb.texinfo: in h8 config, do not describe searching commands.
+
+Mon Aug 17 18:07:59 1992 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * gdb.texinfo, none.m4, h8.m4, gdbinv-s.m4.in: improve H8/300
+ conditionals; introduce a few generic switches that may be
+ useful for other cross-dev or dos-hosted configs.
+
+ * gdb.texinfo: fix typo in "info reg" description
+
+Sun Aug 16 01:16:18 1992 John Gilmore (gnu@cygnus.com)
+
+ * stabs.texinfo: Minor updates from running TeX over it.
+ * Makefile.in (stabs.dvi, stabs.ps): Add.
+
+Sat Aug 15 20:52:24 1992 Per Bothner (bothner@rtl.cygnus.com)
+
+ * stabs.texinfo: Stabs documentation, written by Julia Menapace.
+ First pass at converting it to texinfo.
+
+Sat Aug 15 03:14:59 1992 John Gilmore (gnu@cygnus.com)
+
+ * gdb.texinfo, refcard.tex: Document mult args on `info reg'.
+ * Makefile.in (refcard.ps, lrefcard.ps): Add missing $(srdir).
+
+Fri Aug 14 21:08:47 1992 John Gilmore (gnu@cygnus.com)
+
+ * gdbint.texinfo: Add section on partial symbol tables.
+
+Sat Jun 20 16:31:10 1992 John Gilmore (gnu at cygnus.com)
+
+ * gdb.texinfo: document `set remotedebug' and `set
+ rstack_high_address'.
+
+Thu May 14 17:09:48 1992 Roland H. Pesch (pesch@fowanton.cygnus.com)
+
+ * gdb.texinfo: slight expansion of new text on reading info files
+ * gdbinv-s.m4.in: correct and expand info on cross-debugging
+ H8/300 from DOS.
+
+Tue May 12 12:22:47 1992 John Gilmore (gnu at cygnus.com)
+
+ * gdb.texinfo: `info user' => `show user'. Noticed by David Taylor.
+
+Mon May 11 19:06:27 1992 John Gilmore (gnu at cygnus.com)
+
+ * gdb.texinfo: Say how to read the `info' files.
+
+Tue May 5 12:11:38 1992 K. Richard Pixley (rich@cygnus.com)
+
+ * Makefile.in: gm4 -> m4.
+
+Fri Apr 10 17:50:43 1992 John Gilmore (gnu at rtl.cygnus.com)
+
+ * gdb.texinfo: Update for GDB-4.5. Move `Formatting
+ Documentation' ahead of `Installing GDB' to match README.
+ Update shared library doc, -readnow and -mapped, and directory
+ structure (add glob and mmalloc). Update configure doc.
+
+Tue Mar 24 23:28:38 1992 K. Richard Pixley (rich@cygnus.com)
+
+ * Makefile.in: remove $(srcdir) from gdb.info rule.
+
+Sat Mar 7 18:44:50 1992 K. Richard Pixley (rich@rtl.cygnus.com)
+
+ * Makefile.in: commented out gdb-all.texinfo rule. This is
+ temporary.
+
+Wed Feb 26 18:04:40 1992 K. Richard Pixley (rich@cygnus.com)
+
+ * Makefile.in, configure.in: removed traces of namesubdir,
+ -subdirs, $(subdir), $(unsubdir), some rcs triggers. Forced
+ copyrights to '92, changed some from Cygnus to FSF.
+
+Fri Dec 13 09:47:31 1991 John Gilmore (gnu at cygnus.com)
+
+ * gdb.texinfo: Improve how we ask for bug reports.
+
+Tue Dec 10 04:07:21 1991 K. Richard Pixley (rich at rtl.cygnus.com)
+
+ * Makefile.in: infodir belongs in datadir.
+
+Fri Dec 6 23:57:34 1991 K. Richard Pixley (rich at rtl.cygnus.com)
+
+ * Makefile.in: remove spaces following hyphens, bsd make can't
+ cope. install using INSTALL_DATA. added clean-info. added
+ standards.text support.
+
+Thu Dec 5 22:46:12 1991 K. Richard Pixley (rich at rtl.cygnus.com)
+
+ * Makefile.in: idestdir and ddestdir go away. Added copyrights
+ and shift gpl to v2. Added ChangeLog if it didn't exist. docdir
+ and mandir now keyed off datadir by default.
+
+
+Local Variables:
+mode: indented-text
+left-margin: 8
+fill-column: 74
+version-control: never
+End:
diff --git a/gnu/usr.bin/gdb/doc/Makefile b/gnu/usr.bin/gdb/doc/Makefile
new file mode 100644
index 0000000..8cde5d4
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/Makefile
@@ -0,0 +1,340 @@
+# This file was generated automatically by configure. Do not edit.
+VPATH = .
+links =
+host_alias = i386-unknown-freebsd
+host_cpu = i386
+host_vendor = unknown
+host_os = freebsd
+host_canonical = i386-unknown-freebsd
+target_alias = i386-unknown-freebsd
+target_cpu = i386
+target_vendor = unknown
+target_os = freebsd
+target_canonical = i386-unknown-freebsd
+##Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+# Makefile for GDB documentation.
+# This file is part of GDB.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+srcdir = .
+
+prefix = /usr/gnu
+
+infodir = $(prefix)/info
+
+SHELL = /bin/sh
+
+INSTALL = install -c
+INSTALL_PROGRAM = $(INSTALL)
+INSTALL_DATA = $(INSTALL)
+
+# main GDB source directory
+gdbdir = $(srcdir)/..
+
+# where to find texinfo; GDB dist should include a recent one
+TEXIDIR=${gdbdir}/../texinfo
+
+# where to find makeinfo, preferably one designed for texinfo-2
+MAKEINFO=makeinfo
+
+# where to find texi2roff, ditto
+TEXI2ROFF=texi2roff
+
+# Where is the source dir for the READLINE library doc?
+# Traditionally readline is in .. or .
+READLINE_DIR = ${gdbdir}/../readline/doc
+
+SET_TEXINPUTS = TEXINPUTS=${TEXIDIR}:.:$(srcdir):$(READLINE_DIR):$$TEXINPUTS
+
+# There may be alternate predefined collections of switches to configure
+# the GDB manual. Normally this is not done in synch with the software
+# config system, since this choice tends to be independent; most people
+# want a doc config of `all' for a generic manual, regardless of sw config.
+DOC_CONFIG = all
+
+# This list of sed edits will edit the GDB reference card
+# for what fonts and what papersize to use.
+# By default (NO edits applied), the refcard uses:
+# - Computer Modern (CM) fonts
+# - US letter paper (8.5x11in)
+# List some of the following files for alternative fonts and paper:
+# a4rc.sed use A4 paper (297 x 210 mm)
+# psrc.sed use PostScript fonts (Karl Berry short TeX names)
+# lpsrc.sed use PostScript fonts (full PostScript names in TeX)
+# e.g. for A4, Postscript: REFEDITS = a4rc.sed psrc.sed
+# for A4, CM fonts: REFEDITS = a4rc.sed
+# for US, PS fonts: REFEDITS = psrc.sed
+# for default:
+REFEDITS =
+
+# Don Knuth's TeX formatter
+TEX = tex
+
+# auxiliary program for sorting Texinfo indices
+TEXINDEX = texindex
+
+# Main GDB manual's source files
+SFILES_INCLUDED = gdb-cfg.texi $(srcdir)/remote.texi
+
+SFILES_LOCAL = $(srcdir)/gdb.texinfo GDBvn.texi $(SFILES_INCLUDED)
+
+SFILES_DOC = $(SFILES_LOCAL) \
+ $(READLINE_DIR)/rluser.texinfo $(READLINE_DIR)/inc-hist.texi
+
+#### Host, target, and site specific Makefile fragments come in here.
+###
+
+all install:
+
+info: gdb.info gdbint.info stabs.info
+dvi: gdb.dvi refcard.dvi gdbint.dvi
+all-doc: gdb.info gdb.dvi refcard.dvi gdb-internals gdbint.dvi
+
+install-info: info
+ for i in *.info* ; do \
+ $(INSTALL_DATA) $$i $(infodir)/$$i ; \
+ done
+
+STAGESTUFF = *.info* gdb-all.texi GDBvn.texi
+
+# Copy the object files from a particular stage into a subdirectory.
+stage1: force
+ -mkdir stage1
+ -mv $(STAGESTUFF) stage1
+
+stage2: force
+ -mkdir stage2
+ -mv $(STAGESTUFF) stage2
+
+stage3: force
+ -mkdir stage3
+ -mv $(STAGESTUFF) stage3
+
+against=stage2
+
+comparison: force
+ for i in $(STAGESTUFF) ; do cmp $$i $(against)/$$i ; done
+
+de-stage1: force
+ -(cd stage1 ; mv -f * ..)
+ -rmdir stage1
+
+de-stage2: force
+ -(cd stage2 ; mv -f * ..)
+ -rmdir stage2
+
+de-stage3: force
+ -(cd stage3 ; mv -f * ..)
+ -rmdir stage3
+
+clean-info:
+ rm -f gdb.info* gdbint.info* stabs.info*
+
+clean-dvi:
+ rm -f gdb.dvi refcard.dvi gdbint.dvi stabs.dvi sedref.dvi
+
+mostlyclean: clean-info clean-dvi
+ rm -f gdb.?? gdb.??? gdb.mm gdb.ms gdb.me
+ rm -f links2roff
+ rm -f refcard.ps lrefcard.ps refcard.log sedref.* *~
+ rm -f gdbint.?? gdbint.??? stabs.?? stabs.???
+
+clean: mostlyclean
+ rm -f GDBvn.texi rluser.texinfo inc-hist.texi
+
+distclean: clean
+ rm -f Makefile config.status
+
+realclean: distclean clean-dvi clean-info
+
+# GDB QUICK REFERENCE (dvi output)
+refcard.dvi : refcard.tex $(REFEDITS)
+ if [ -z "$(REFEDITS)" ]; then \
+ cp $(srcdir)/refcard.tex sedref.tex ; \
+ else \
+ echo > tmp.sed ; \
+ for f in "$(REFEDITS)" ; do \
+ cat $(srcdir)/$$f >>tmp.sed ; done ; \
+ sed -f tmp.sed $(srcdir)/refcard.tex >sedref.tex ; \
+ fi
+ $(SET_TEXINPUTS) $(TEX) sedref.tex
+ mv sedref.dvi refcard.dvi
+ rm -f sedref.log sedref.tex tmp.sed
+
+refcard.ps : refcard.dvi
+ dvips -t landscape refcard.dvi -o
+
+# File to record current GDB version number (copied from main dir Makefile.in)
+GDBvn.texi : ${gdbdir}/Makefile.in
+ echo "@set GDBVN `sed <$(srcdir)/../Makefile.in -n 's/VERSION = //p'`" > ./GDBvn.new
+ mv GDBvn.new GDBvn.texi
+
+# Updated atomically
+.PRECIOUS: GDBvn.texi
+
+# Choose configuration for GDB manual (normally `all'; normally not tied into
+# `configure' script because most users prefer generic version of manual,
+# not one for their binary config---which may not be specifically
+# defined anyways).
+gdb-cfg.texi: ${srcdir}/${DOC_CONFIG}-cfg.texi
+ ln -s ${srcdir}/${DOC_CONFIG}-cfg.texi gdb-cfg.texi || \
+ ln ${srcdir}/${DOC_CONFIG}-cfg.texi gdb-cfg.texi || \
+ cp ${srcdir}/${DOC_CONFIG}-cfg.texi gdb-cfg.texi
+
+# GDB MANUAL: texinfo source, using @set/@clear/@value/@ifset/@ifclear
+# If your texinfo or makeinfo don't support these, get a new texinfo release
+#
+# The nonsense with GDBvn.texi gets this to run with both Sun and GNU make.
+# Note that we can *generate* GDBvn.texi, but since we distribute one in the
+# source directory for the benefit of people who *don't* use this makefile,
+# VPATH will often tell make not to bother building it, because the one
+# in the srcdir is up to date. (if not, then make should build one here).
+
+# GDB MANUAL: TeX dvi file
+gdb.dvi: ${SFILES_DOC}
+ if [ ! -f ./GDBvn.texi ]; then \
+ ln -s $(srcdir)/GDBvn.texi . || \
+ ln $(srcdir)/GDBvn.texi . || \
+ cp $(srcdir)/GDBvn.texi . ; else true; fi
+ $(SET_TEXINPUTS) $(TEX) gdb.texinfo
+ $(SET_TEXINPUTS) $(TEX) gdb.texinfo
+ $(TEXINDEX) gdb.??
+ $(SET_TEXINPUTS) $(TEX) gdb.texinfo
+ rm -f gdb.?? gdb.log gdb.aux gdb.toc gdb.??s
+
+# GDB MANUAL: info file
+# We're using texinfo2, and older makeinfo's may not be able to
+# cope with all the markup.
+gdb.info: ${SFILES_DOC}
+ $(MAKEINFO) -I ${READLINE_DIR} -I $(srcdir) -o ./gdb.info gdb.texinfo
+
+# GDB MANUAL: roff translations
+# Try to use a recent texi2roff. v2 was put on prep in jan91.
+# If you want an index, see texi2roff doc for postprocessing
+# and add -i to texi2roff invocations below.
+# Workarounds for texi2roff-2 (probably fixed in later texi2roff's, delete
+# corresponding -e lines when later texi2roff's are current)
+# + @ifinfo's deleted explicitly due to texi2roff-2 bug w nested constructs.
+# + @c's deleted explicitly because texi2roff sees texinfo commands in them
+# + @ (that's at-BLANK) not recognized by texi2roff, turned into blank
+# + @alphaenumerate is ridiculously new, turned into @enumerate
+
+# texi2roff doesn't have a notion of include dirs, so we have to fake
+# it out for gdb manual's include files---but only if not configured
+# in main sourcedir.
+links2roff: $(SFILES_INCLUDED)
+ if [ ! -f gdb.texinfo ]; then \
+ ln -s $(SFILES_INCLUDED) . || \
+ ln $(SFILES_INCLUDED) . || \
+ cp $(SFILES_INCLUDED) . ; \
+ fi
+ touch links2roff
+
+# "Readline" appendices. Get them also due to lack of includes,
+# regardless of whether or not configuring in main sourcedir.
+# @ftable removed due to bug in texi2roff-2; if your texi2roff
+# is newer, try just ln or cp
+rluser.texinfo: ${READLINE_DIR}/rluser.texinfo
+ sed -e 's/^@ftable/@table/g' \
+ -e 's/^@end ftable/@end table/g' \
+ ${READLINE_DIR}/rluser.texinfo > ./rluser.texinfo
+
+inc-hist.texi: ${READLINE_DIR}/inc-hist.texi
+ ln -s ${READLINE_DIR}/inc-hist.texi . || \
+ ln ${READLINE_DIR}/inc-hist.texi . || \
+ cp ${READLINE_DIR}/inc-hist.texi .
+
+# gdb manual suitable for [gtn]roff -me
+gdb.me: $(SFILES_LOCAL) links2roff rluser.texinfo inc-hist.texi
+ sed -e '/\\input texinfo/d' \
+ -e '/@c TEXI2ROFF-KILL/,/@c END TEXI2ROFF-KILL/d' \
+ -e '/^@ifinfo/,/^@end ifinfo/d' \
+ -e '/^@c /d' \
+ -e 's/{.*,,/{/' \
+ -e 's/@ / /g' \
+ -e 's/^@alphaenumerate/@enumerate/g' \
+ -e 's/^@end alphaenumerate/@end enumerate/g' \
+ $(srcdir)/gdb.texinfo | \
+ $(TEXI2ROFF) -me | \
+ sed -e 's/---/\\(em/g' \
+ >gdb.me
+
+# gdb manual suitable for [gtn]roff -ms
+gdb.ms: $(SFILES_LOCAL) links2roff rluser.texinfo inc-hist.texi
+ sed -e '/\\input texinfo/d' \
+ -e '/@c TEXI2ROFF-KILL/,/@c END TEXI2ROFF-KILL/d' \
+ -e '/^@ifinfo/,/^@end ifinfo/d' \
+ -e '/^@c /d' \
+ -e 's/{.*,,/{/' \
+ -e 's/@ / /g' \
+ -e 's/^@alphaenumerate/@enumerate/g' \
+ -e 's/^@end alphaenumerate/@end enumerate/g' \
+ $(srcdir)/gdb.texinfo | \
+ $(TEXI2ROFF) -ms | \
+ sed -e 's/---/\\(em/g' \
+ >gdb.ms
+
+# gdb manual suitable for [tn]roff -mm
+# '@noindent's removed due to texi2roff-2 mm bug; if yours is newer,
+# try leaving them in
+gdb.mm: $(SFILES_LOCAL) links2roff rluser.texinfo inc-hist.texi
+ sed -e '/\\input texinfo/d' \
+ -e '/@c TEXI2ROFF-KILL/,/@c END TEXI2ROFF-KILL/d' \
+ -e '/^@ifinfo/,/^@end ifinfo/d' \
+ -e '/^@c /d' \
+ -e 's/{.*,,/{/' \
+ -e '/@noindent/d' \
+ -e 's/@ / /g' \
+ -e 's/^@alphaenumerate/@enumerate/g' \
+ -e 's/^@end alphaenumerate/@end enumerate/g' \
+ $(srcdir)/gdb.texinfo | \
+ $(TEXI2ROFF) -mm | \
+ sed -e 's/---/\\(em/g' \
+ >gdb.mm
+
+# GDB INTERNALS MANUAL: TeX dvi file
+gdbint.dvi : gdbint.texinfo
+ $(SET_TEXINPUTS) $(TEX) gdbint.texinfo
+ $(TEXINDEX) gdbint.??
+ $(SET_TEXINPUTS) $(TEX) gdbint.texinfo
+ rm -f gdbint.?? gdbint.aux gdbint.cps gdbint.fns gdbint.kys \
+ gdbint.log gdbint.pgs gdbint.toc gdbint.tps gdbint.vrs
+
+# GDB INTERNALS MANUAL: info file
+gdb-internals: gdbint.info
+
+gdbint.info: gdbint.texinfo
+ $(MAKEINFO) -o gdbint.info $(srcdir)/gdbint.texinfo
+
+stabs.info: stabs.texinfo
+ $(MAKEINFO) -o stabs.info $(srcdir)/stabs.texinfo
+
+# STABS DOCUMENTATION: TeX dvi file
+stabs.dvi : stabs.texinfo
+ $(SET_TEXINPUTS) $(TEX) stabs.texinfo
+ $(TEXINDEX) stabs.??
+ $(SET_TEXINPUTS) $(TEX) stabs.texinfo
+ rm -f stabs.?? stabs.aux stabs.cps stabs.fns stabs.kys \
+ stabs.log stabs.pgs stabs.toc stabs.tps stabs.vrs
+
+stabs.ps: stabs.dvi
+ dvips -o stabs.ps stabs
+
+force:
+
+Makefile: $(srcdir)/Makefile.in $(host_makefile_frag) $(target_makefile_frag)
+ $(SHELL) ./config.status
diff --git a/gnu/usr.bin/gdb/doc/Makefile.in b/gnu/usr.bin/gdb/doc/Makefile.in
new file mode 100644
index 0000000..d5ae290
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/Makefile.in
@@ -0,0 +1,327 @@
+##Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+# Makefile for GDB documentation.
+# This file is part of GDB.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+srcdir = .
+
+prefix = /usr/local
+
+infodir = $(prefix)/info
+
+SHELL = /bin/sh
+
+INSTALL = install -c
+INSTALL_PROGRAM = $(INSTALL)
+INSTALL_DATA = $(INSTALL)
+
+# main GDB source directory
+gdbdir = $(srcdir)/..
+
+# where to find texinfo; GDB dist should include a recent one
+TEXIDIR=${gdbdir}/../texinfo
+
+# where to find makeinfo, preferably one designed for texinfo-2
+MAKEINFO=makeinfo
+
+# where to find texi2roff, ditto
+TEXI2ROFF=texi2roff
+
+# Where is the source dir for the READLINE library doc?
+# Traditionally readline is in .. or .
+READLINE_DIR = ${gdbdir}/../readline/doc
+
+SET_TEXINPUTS = TEXINPUTS=${TEXIDIR}:.:$(srcdir):$(READLINE_DIR):$$TEXINPUTS
+
+# There may be alternate predefined collections of switches to configure
+# the GDB manual. Normally this is not done in synch with the software
+# config system, since this choice tends to be independent; most people
+# want a doc config of `all' for a generic manual, regardless of sw config.
+DOC_CONFIG = all
+
+# This list of sed edits will edit the GDB reference card
+# for what fonts and what papersize to use.
+# By default (NO edits applied), the refcard uses:
+# - Computer Modern (CM) fonts
+# - US letter paper (8.5x11in)
+# List some of the following files for alternative fonts and paper:
+# a4rc.sed use A4 paper (297 x 210 mm)
+# psrc.sed use PostScript fonts (Karl Berry short TeX names)
+# lpsrc.sed use PostScript fonts (full PostScript names in TeX)
+# e.g. for A4, Postscript: REFEDITS = a4rc.sed psrc.sed
+# for A4, CM fonts: REFEDITS = a4rc.sed
+# for US, PS fonts: REFEDITS = psrc.sed
+# for default:
+REFEDITS =
+
+# Don Knuth's TeX formatter
+TEX = tex
+
+# auxiliary program for sorting Texinfo indices
+TEXINDEX = texindex
+
+# Main GDB manual's source files
+SFILES_INCLUDED = gdb-cfg.texi $(srcdir)/remote.texi
+
+SFILES_LOCAL = $(srcdir)/gdb.texinfo GDBvn.texi $(SFILES_INCLUDED)
+
+SFILES_DOC = $(SFILES_LOCAL) \
+ $(READLINE_DIR)/rluser.texinfo $(READLINE_DIR)/inc-hist.texi
+
+#### Host, target, and site specific Makefile fragments come in here.
+###
+
+all install:
+
+info: gdb.info gdbint.info stabs.info
+dvi: gdb.dvi refcard.dvi gdbint.dvi
+all-doc: gdb.info gdb.dvi refcard.dvi gdb-internals gdbint.dvi
+
+install-info: info
+ for i in *.info* ; do \
+ $(INSTALL_DATA) $$i $(infodir)/$$i ; \
+ done
+
+STAGESTUFF = *.info* gdb-all.texi GDBvn.texi
+
+# Copy the object files from a particular stage into a subdirectory.
+stage1: force
+ -mkdir stage1
+ -mv $(STAGESTUFF) stage1
+
+stage2: force
+ -mkdir stage2
+ -mv $(STAGESTUFF) stage2
+
+stage3: force
+ -mkdir stage3
+ -mv $(STAGESTUFF) stage3
+
+against=stage2
+
+comparison: force
+ for i in $(STAGESTUFF) ; do cmp $$i $(against)/$$i ; done
+
+de-stage1: force
+ -(cd stage1 ; mv -f * ..)
+ -rmdir stage1
+
+de-stage2: force
+ -(cd stage2 ; mv -f * ..)
+ -rmdir stage2
+
+de-stage3: force
+ -(cd stage3 ; mv -f * ..)
+ -rmdir stage3
+
+clean-info:
+ rm -f gdb.info* gdbint.info* stabs.info*
+
+clean-dvi:
+ rm -f gdb.dvi refcard.dvi gdbint.dvi stabs.dvi sedref.dvi
+
+mostlyclean: clean-info clean-dvi
+ rm -f gdb.?? gdb.??? gdb.mm gdb.ms gdb.me
+ rm -f links2roff
+ rm -f refcard.ps lrefcard.ps refcard.log sedref.* *~
+ rm -f gdbint.?? gdbint.??? stabs.?? stabs.???
+
+clean: mostlyclean
+ rm -f GDBvn.texi rluser.texinfo inc-hist.texi
+
+distclean: clean
+ rm -f Makefile config.status
+
+realclean: distclean clean-dvi clean-info
+
+# GDB QUICK REFERENCE (dvi output)
+refcard.dvi : refcard.tex $(REFEDITS)
+ if [ -z "$(REFEDITS)" ]; then \
+ cp $(srcdir)/refcard.tex sedref.tex ; \
+ else \
+ echo > tmp.sed ; \
+ for f in "$(REFEDITS)" ; do \
+ cat $(srcdir)/$$f >>tmp.sed ; done ; \
+ sed -f tmp.sed $(srcdir)/refcard.tex >sedref.tex ; \
+ fi
+ $(SET_TEXINPUTS) $(TEX) sedref.tex
+ mv sedref.dvi refcard.dvi
+ rm -f sedref.log sedref.tex tmp.sed
+
+refcard.ps : refcard.dvi
+ dvips -t landscape refcard.dvi -o
+
+# File to record current GDB version number (copied from main dir Makefile.in)
+GDBvn.texi : ${gdbdir}/Makefile.in
+ echo "@set GDBVN `sed <$(srcdir)/../Makefile.in -n 's/VERSION = //p'`" > ./GDBvn.new
+ mv GDBvn.new GDBvn.texi
+
+# Updated atomically
+.PRECIOUS: GDBvn.texi
+
+# Choose configuration for GDB manual (normally `all'; normally not tied into
+# `configure' script because most users prefer generic version of manual,
+# not one for their binary config---which may not be specifically
+# defined anyways).
+gdb-cfg.texi: ${srcdir}/${DOC_CONFIG}-cfg.texi
+ ln -s ${srcdir}/${DOC_CONFIG}-cfg.texi gdb-cfg.texi || \
+ ln ${srcdir}/${DOC_CONFIG}-cfg.texi gdb-cfg.texi || \
+ cp ${srcdir}/${DOC_CONFIG}-cfg.texi gdb-cfg.texi
+
+# GDB MANUAL: texinfo source, using @set/@clear/@value/@ifset/@ifclear
+# If your texinfo or makeinfo don't support these, get a new texinfo release
+#
+# The nonsense with GDBvn.texi gets this to run with both Sun and GNU make.
+# Note that we can *generate* GDBvn.texi, but since we distribute one in the
+# source directory for the benefit of people who *don't* use this makefile,
+# VPATH will often tell make not to bother building it, because the one
+# in the srcdir is up to date. (if not, then make should build one here).
+
+# GDB MANUAL: TeX dvi file
+gdb.dvi: ${SFILES_DOC}
+ if [ ! -f ./GDBvn.texi ]; then \
+ ln -s $(srcdir)/GDBvn.texi . || \
+ ln $(srcdir)/GDBvn.texi . || \
+ cp $(srcdir)/GDBvn.texi . ; else true; fi
+ $(SET_TEXINPUTS) $(TEX) gdb.texinfo
+ $(SET_TEXINPUTS) $(TEX) gdb.texinfo
+ $(TEXINDEX) gdb.??
+ $(SET_TEXINPUTS) $(TEX) gdb.texinfo
+ rm -f gdb.?? gdb.log gdb.aux gdb.toc gdb.??s
+
+# GDB MANUAL: info file
+# We're using texinfo2, and older makeinfo's may not be able to
+# cope with all the markup.
+gdb.info: ${SFILES_DOC}
+ $(MAKEINFO) -I ${READLINE_DIR} -I $(srcdir) -o ./gdb.info gdb.texinfo
+
+# GDB MANUAL: roff translations
+# Try to use a recent texi2roff. v2 was put on prep in jan91.
+# If you want an index, see texi2roff doc for postprocessing
+# and add -i to texi2roff invocations below.
+# Workarounds for texi2roff-2 (probably fixed in later texi2roff's, delete
+# corresponding -e lines when later texi2roff's are current)
+# + @ifinfo's deleted explicitly due to texi2roff-2 bug w nested constructs.
+# + @c's deleted explicitly because texi2roff sees texinfo commands in them
+# + @ (that's at-BLANK) not recognized by texi2roff, turned into blank
+# + @alphaenumerate is ridiculously new, turned into @enumerate
+
+# texi2roff doesn't have a notion of include dirs, so we have to fake
+# it out for gdb manual's include files---but only if not configured
+# in main sourcedir.
+links2roff: $(SFILES_INCLUDED)
+ if [ ! -f gdb.texinfo ]; then \
+ ln -s $(SFILES_INCLUDED) . || \
+ ln $(SFILES_INCLUDED) . || \
+ cp $(SFILES_INCLUDED) . ; \
+ fi
+ touch links2roff
+
+# "Readline" appendices. Get them also due to lack of includes,
+# regardless of whether or not configuring in main sourcedir.
+# @ftable removed due to bug in texi2roff-2; if your texi2roff
+# is newer, try just ln or cp
+rluser.texinfo: ${READLINE_DIR}/rluser.texinfo
+ sed -e 's/^@ftable/@table/g' \
+ -e 's/^@end ftable/@end table/g' \
+ ${READLINE_DIR}/rluser.texinfo > ./rluser.texinfo
+
+inc-hist.texi: ${READLINE_DIR}/inc-hist.texi
+ ln -s ${READLINE_DIR}/inc-hist.texi . || \
+ ln ${READLINE_DIR}/inc-hist.texi . || \
+ cp ${READLINE_DIR}/inc-hist.texi .
+
+# gdb manual suitable for [gtn]roff -me
+gdb.me: $(SFILES_LOCAL) links2roff rluser.texinfo inc-hist.texi
+ sed -e '/\\input texinfo/d' \
+ -e '/@c TEXI2ROFF-KILL/,/@c END TEXI2ROFF-KILL/d' \
+ -e '/^@ifinfo/,/^@end ifinfo/d' \
+ -e '/^@c /d' \
+ -e 's/{.*,,/{/' \
+ -e 's/@ / /g' \
+ -e 's/^@alphaenumerate/@enumerate/g' \
+ -e 's/^@end alphaenumerate/@end enumerate/g' \
+ $(srcdir)/gdb.texinfo | \
+ $(TEXI2ROFF) -me | \
+ sed -e 's/---/\\(em/g' \
+ >gdb.me
+
+# gdb manual suitable for [gtn]roff -ms
+gdb.ms: $(SFILES_LOCAL) links2roff rluser.texinfo inc-hist.texi
+ sed -e '/\\input texinfo/d' \
+ -e '/@c TEXI2ROFF-KILL/,/@c END TEXI2ROFF-KILL/d' \
+ -e '/^@ifinfo/,/^@end ifinfo/d' \
+ -e '/^@c /d' \
+ -e 's/{.*,,/{/' \
+ -e 's/@ / /g' \
+ -e 's/^@alphaenumerate/@enumerate/g' \
+ -e 's/^@end alphaenumerate/@end enumerate/g' \
+ $(srcdir)/gdb.texinfo | \
+ $(TEXI2ROFF) -ms | \
+ sed -e 's/---/\\(em/g' \
+ >gdb.ms
+
+# gdb manual suitable for [tn]roff -mm
+# '@noindent's removed due to texi2roff-2 mm bug; if yours is newer,
+# try leaving them in
+gdb.mm: $(SFILES_LOCAL) links2roff rluser.texinfo inc-hist.texi
+ sed -e '/\\input texinfo/d' \
+ -e '/@c TEXI2ROFF-KILL/,/@c END TEXI2ROFF-KILL/d' \
+ -e '/^@ifinfo/,/^@end ifinfo/d' \
+ -e '/^@c /d' \
+ -e 's/{.*,,/{/' \
+ -e '/@noindent/d' \
+ -e 's/@ / /g' \
+ -e 's/^@alphaenumerate/@enumerate/g' \
+ -e 's/^@end alphaenumerate/@end enumerate/g' \
+ $(srcdir)/gdb.texinfo | \
+ $(TEXI2ROFF) -mm | \
+ sed -e 's/---/\\(em/g' \
+ >gdb.mm
+
+# GDB INTERNALS MANUAL: TeX dvi file
+gdbint.dvi : gdbint.texinfo
+ $(SET_TEXINPUTS) $(TEX) gdbint.texinfo
+ $(TEXINDEX) gdbint.??
+ $(SET_TEXINPUTS) $(TEX) gdbint.texinfo
+ rm -f gdbint.?? gdbint.aux gdbint.cps gdbint.fns gdbint.kys \
+ gdbint.log gdbint.pgs gdbint.toc gdbint.tps gdbint.vrs
+
+# GDB INTERNALS MANUAL: info file
+gdb-internals: gdbint.info
+
+gdbint.info: gdbint.texinfo
+ $(MAKEINFO) -o gdbint.info $(srcdir)/gdbint.texinfo
+
+stabs.info: stabs.texinfo
+ $(MAKEINFO) -o stabs.info $(srcdir)/stabs.texinfo
+
+# STABS DOCUMENTATION: TeX dvi file
+stabs.dvi : stabs.texinfo
+ $(SET_TEXINPUTS) $(TEX) stabs.texinfo
+ $(TEXINDEX) stabs.??
+ $(SET_TEXINPUTS) $(TEX) stabs.texinfo
+ rm -f stabs.?? stabs.aux stabs.cps stabs.fns stabs.kys \
+ stabs.log stabs.pgs stabs.toc stabs.tps stabs.vrs
+
+stabs.ps: stabs.dvi
+ dvips -o stabs.ps stabs
+
+force:
+
+Makefile: $(srcdir)/Makefile.in $(host_makefile_frag) $(target_makefile_frag)
+ $(SHELL) ./config.status
diff --git a/gnu/usr.bin/gdb/doc/a4rc.sed b/gnu/usr.bin/gdb/doc/a4rc.sed
new file mode 100644
index 0000000..2292290
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/a4rc.sed
@@ -0,0 +1,11 @@
+/--- Papersize params:/,/--- end papersize params/c\
+%------- Papersize params:\
+%% A4 paper (297x210mm)\
+%%\
+\\totalwidth=297mm % total width of paper\
+\\totalheight=210mm % total height of paper\
+\\hmargin=5mm % horizontal margin width\
+\\vmargin=10mm % vertical margin width\
+\\secskip=.6pc % space between refcard secs\
+\\lskip=1pt % extra skip between \\sec entries\
+%------- end papersize params
diff --git a/gnu/usr.bin/gdb/doc/all-cfg.texi b/gnu/usr.bin/gdb/doc/all-cfg.texi
new file mode 100644
index 0000000..ec64da1
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/all-cfg.texi
@@ -0,0 +1,117 @@
+@c GDB MANUAL configuration file.
+@c Copyright (c) 1993 Free Software Foundation, Inc.
+@c
+@c NOTE: While the GDB manual is configurable (by changing these
+@c switches), its configuration is ***NOT*** automatically tied in to
+@c source configuration---because the authors expect that, save in
+@c unusual cases, the most inclusive form of the manual is appropriate
+@c no matter how the program itself is configured.
+@c
+@c The only automatically-varying variable is the GDB version number,
+@c which the Makefile rewrites based on the VERSION variable from
+@c `../Makefile.in'.
+@c
+@c GDB version number is recorded in the variable GDBVN
+@include GDBvn.texi
+@c
+@c ----------------------------------------------------------------------
+@c PLATFORM FLAGS:
+@set GENERIC
+@c
+@c Hitachi H8/300 target:
+@set H8
+@c Hitachi H8/300 target ONLY:
+@clear H8EXCLUSIVE
+@c
+@c remote MIPS target:
+@set MIPS
+@c
+@c SPARC target:
+@set SPARC
+@c
+@c AMD 29000 target:
+@set AMD29K
+@c
+@c Intel 960 target:
+@set I960
+@c
+@c Tandem ST2000 (phone switch) target:
+@set ST2000
+@c
+@c Zilog 8000 target:
+@set Z8K
+@c
+@c Lucid "Energize" environment:
+@clear LUCID
+@c
+@c Wind River Systems VxWorks environment:
+@set VXWORKS
+@c
+@c ----------------------------------------------------------------------
+@c DOC FEATURE FLAGS:
+@c
+@c Include change-from-old?
+@set NOVEL
+@c
+@c Bare-board target?
+@clear BARETARGET
+@c
+@c Restrict languages discussed to C?
+@c This is backward. As time permits, change this to language-specific
+@c switches for what to include.
+@clear CONLY
+@c Discuss Fortran?
+@set FORTRAN
+@c
+@c Discuss Modula 2?
+@set MOD2
+@c
+@c Specifically for host machine running DOS?
+@clear DOSHOST
+@c
+@c Talk about CPU simulator targets?
+@set SIMS
+@c
+@c Is manual stand-alone, or part of an agglomeration, with overall GPL?
+@clear AGGLOMERATION
+@c
+@c Remote serial line settings of interest?
+@set SERIAL
+@c
+@c Discuss features requiring Posix or similar OS environment?
+@set POSIX
+@c
+@c Discuss remote serial debugging stub?
+@set REMOTESTUB
+@c
+@c Discuss gdbserver?
+@set GDBSERVER
+@c
+@c Refrain from discussing how to configure sw and format doc?
+@clear PRECONFIGURED
+@c
+@c Refrain from referring to unfree publications?
+@set FSFDOC
+@c
+@c ----------------------------------------------------------------------
+@c STRINGS:
+@c
+@c Name of GDB program. Used also for (gdb) prompt string.
+@set GDBP gdb
+@c
+@c Name of GDB product. Used in running text.
+@set GDBN GDB
+@c
+@c Name of GDB initialization file.
+@set GDBINIT .gdbinit
+@c
+@c Name of host. Should not be used in generic configs, but generic
+@c value may catch some flubs.
+@set HOST machine specific
+@c
+@c Name of GCC product
+@set NGCC GCC
+@c
+@c Name of GCC program
+@set GCC gcc
+
diff --git a/gnu/usr.bin/gdb/doc/config.status b/gnu/usr.bin/gdb/doc/config.status
new file mode 100755
index 0000000..5d2c6dd
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/config.status
@@ -0,0 +1,5 @@
+#!/bin/sh
+# This file was generated automatically by configure. Do not edit.
+# This directory was configured as follows:
+../../configure --host=i386-unknown-freebsd --target=i386-unknown-freebsd -norecursion
+#
diff --git a/gnu/usr.bin/gdb/doc/configure.in b/gnu/usr.bin/gdb/doc/configure.in
new file mode 100644
index 0000000..1d2b47e
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/configure.in
@@ -0,0 +1,7 @@
+srcname="GDB doc"
+srctrigger=gdb.texinfo
+# per-host:
+# per-target:
+
+files=""
+links=""
diff --git a/gnu/usr.bin/gdb/doc/gdb-cfg.texi b/gnu/usr.bin/gdb/doc/gdb-cfg.texi
new file mode 100644
index 0000000..ec64da1
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/gdb-cfg.texi
@@ -0,0 +1,117 @@
+@c GDB MANUAL configuration file.
+@c Copyright (c) 1993 Free Software Foundation, Inc.
+@c
+@c NOTE: While the GDB manual is configurable (by changing these
+@c switches), its configuration is ***NOT*** automatically tied in to
+@c source configuration---because the authors expect that, save in
+@c unusual cases, the most inclusive form of the manual is appropriate
+@c no matter how the program itself is configured.
+@c
+@c The only automatically-varying variable is the GDB version number,
+@c which the Makefile rewrites based on the VERSION variable from
+@c `../Makefile.in'.
+@c
+@c GDB version number is recorded in the variable GDBVN
+@include GDBvn.texi
+@c
+@c ----------------------------------------------------------------------
+@c PLATFORM FLAGS:
+@set GENERIC
+@c
+@c Hitachi H8/300 target:
+@set H8
+@c Hitachi H8/300 target ONLY:
+@clear H8EXCLUSIVE
+@c
+@c remote MIPS target:
+@set MIPS
+@c
+@c SPARC target:
+@set SPARC
+@c
+@c AMD 29000 target:
+@set AMD29K
+@c
+@c Intel 960 target:
+@set I960
+@c
+@c Tandem ST2000 (phone switch) target:
+@set ST2000
+@c
+@c Zilog 8000 target:
+@set Z8K
+@c
+@c Lucid "Energize" environment:
+@clear LUCID
+@c
+@c Wind River Systems VxWorks environment:
+@set VXWORKS
+@c
+@c ----------------------------------------------------------------------
+@c DOC FEATURE FLAGS:
+@c
+@c Include change-from-old?
+@set NOVEL
+@c
+@c Bare-board target?
+@clear BARETARGET
+@c
+@c Restrict languages discussed to C?
+@c This is backward. As time permits, change this to language-specific
+@c switches for what to include.
+@clear CONLY
+@c Discuss Fortran?
+@set FORTRAN
+@c
+@c Discuss Modula 2?
+@set MOD2
+@c
+@c Specifically for host machine running DOS?
+@clear DOSHOST
+@c
+@c Talk about CPU simulator targets?
+@set SIMS
+@c
+@c Is manual stand-alone, or part of an agglomeration, with overall GPL?
+@clear AGGLOMERATION
+@c
+@c Remote serial line settings of interest?
+@set SERIAL
+@c
+@c Discuss features requiring Posix or similar OS environment?
+@set POSIX
+@c
+@c Discuss remote serial debugging stub?
+@set REMOTESTUB
+@c
+@c Discuss gdbserver?
+@set GDBSERVER
+@c
+@c Refrain from discussing how to configure sw and format doc?
+@clear PRECONFIGURED
+@c
+@c Refrain from referring to unfree publications?
+@set FSFDOC
+@c
+@c ----------------------------------------------------------------------
+@c STRINGS:
+@c
+@c Name of GDB program. Used also for (gdb) prompt string.
+@set GDBP gdb
+@c
+@c Name of GDB product. Used in running text.
+@set GDBN GDB
+@c
+@c Name of GDB initialization file.
+@set GDBINIT .gdbinit
+@c
+@c Name of host. Should not be used in generic configs, but generic
+@c value may catch some flubs.
+@set HOST machine specific
+@c
+@c Name of GCC product
+@set NGCC GCC
+@c
+@c Name of GCC program
+@set GCC gcc
+
diff --git a/gnu/usr.bin/gdb/doc/gdb.info b/gnu/usr.bin/gdb/doc/gdb.info
new file mode 100644
index 0000000..c326469
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/gdb.info
@@ -0,0 +1,213 @@
+This is Info file ./gdb.info, produced by Makeinfo-1.52 from the input
+file gdb.texinfo.
+
+START-INFO-DIR-ENTRY
+* Gdb:: The GNU debugger.
+END-INFO-DIR-ENTRY
+ This file documents the GNU debugger GDB.
+
+ This is Edition 4.09, August 1993, of `Debugging with GDB: the GNU
+Source-Level Debugger' for GDB Version 4.11.
+
+ Copyright (C) 1988, '89, '90, '91, '92, '93 Free Software
+Foundation, Inc.
+
+ Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of
+this manual under the conditions for verbatim copying, provided also
+that the entire resulting derived work is distributed under the terms
+of a permission notice identical to this one.
+
+ Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions.
+
+
+Indirect:
+gdb.info-1: 992
+gdb.info-2: 50863
+gdb.info-3: 98423
+gdb.info-4: 145674
+gdb.info-5: 194815
+gdb.info-6: 244253
+gdb.info-7: 290141
+gdb.info-8: 335234
+
+Tag Table:
+(Indirect)
+Node: Top992
+Node: Summary2561
+Node: Free Software3754
+Node: Contributors4492
+Node: New Features8199
+Node: Sample Session12215
+Node: Invocation19094
+Node: Invoking GDB19559
+Node: File Options21298
+Node: Mode Options24476
+Node: Quitting GDB26641
+Node: Shell Commands27359
+Node: Commands28106
+Node: Command Syntax28739
+Node: Completion30598
+Node: Help34666
+Node: Running38442
+Node: Compilation39426
+Node: Starting41224
+Node: Arguments44411
+Node: Environment45412
+Node: Working Directory48518
+Node: Input/Output49258
+Node: Attach50863
+Node: Kill Process53122
+Node: Process Information54097
+Node: Stopping55350
+Node: Breakpoints56423
+Node: Set Breaks58622
+Node: Set Watchpoints65221
+Node: Exception Handling66051
+Node: Delete Breaks68610
+Node: Disabling70238
+Node: Conditions72881
+Node: Break Commands77378
+Node: Breakpoint Menus80225
+Node: Error in Breakpoints81935
+Node: Continuing and Stepping82839
+Node: Signals89318
+Node: Stack92940
+Node: Frames94414
+Node: Backtrace96691
+Node: Selection98423
+Node: Frame Info100917
+Node: MIPS Stack102984
+Node: Source103857
+Node: List104806
+Node: Search108286
+Node: Source Path109085
+Node: Machine Code111763
+Node: Data114236
+Node: Expressions116111
+Node: Variables117793
+Node: Arrays120314
+Node: Output Formats122397
+Node: Memory124456
+Node: Auto Display128727
+Node: Print Settings132474
+Node: Value History140630
+Node: Convenience Vars143017
+Node: Registers145674
+Node: Floating Point Hardware150276
+Node: Languages150781
+Node: Setting151949
+Node: Manually152483
+Node: Automatically153663
+Node: Show154980
+Node: Checks155888
+Node: Type Checking157244
+Node: Range Checking159924
+Node: Support162265
+Node: C163185
+Node: C Operators164016
+Node: C Constants168071
+Node: Cplus expressions169974
+Node: C Defaults172597
+Node: C Checks173215
+Node: Debugging C173926
+Node: Debugging C plus plus174404
+Node: Modula-2176416
+Node: M2 Operators177308
+Node: Built-In Func/Proc180308
+Node: M2 Constants183051
+Node: M2 Defaults184640
+Node: Deviations185239
+Node: M2 Checks186330
+Node: M2 Scope187130
+Node: GDB/M2188142
+Node: Symbols189081
+Node: Altering194815
+Node: Assignment195797
+Node: Jumping197907
+Node: Signaling199914
+Node: Returning201034
+Node: Calling202226
+Node: Patching202700
+Node: GDB Files203782
+Node: Files204247
+Node: Symbol Errors214466
+Node: Targets218064
+Node: Active Targets218954
+Node: Target Commands220530
+Node: Remote223904
+Node: Remote Serial225315
+Node: Stub Contents227768
+Node: Bootstrapping229877
+Node: Debug Session233057
+Node: Protocol236218
+Node: Server239069
+Node: i960-Nindy Remote242748
+Node: Nindy Startup243568
+Node: Nindy Options244253
+Node: Nindy Reset245867
+Node: UDI29K Remote246251
+Node: EB29K Remote247172
+Node: Comms (EB29K)248006
+Node: gdb-EB29K251189
+Node: Remote Log252555
+Node: ST2000 Remote253030
+Node: VxWorks Remote254499
+Node: VxWorks Connection256224
+Node: VxWorks Download257150
+Node: VxWorks Attach258886
+Node: Hitachi Remote259281
+Node: MIPS Remote260790
+Node: Simulator262861
+Node: Controlling GDB264351
+Node: Prompt264962
+Node: Editing265571
+Node: History266338
+Node: Screen Size269024
+Node: Numbers270420
+Node: Messages/Warnings271538
+Node: Sequences274587
+Node: Define275147
+Node: Hooks277144
+Node: Command Files278547
+Node: Output280302
+Node: Emacs282714
+Node: GDB Bugs288669
+Node: Bug Criteria289387
+Node: Bug Reporting290141
+Node: Command Line Editing297342
+Node: Introduction and Notation297763
+Node: Readline Interaction298780
+Node: Readline Bare Essentials299914
+Node: Readline Movement Commands301417
+Node: Readline Killing Commands302303
+Node: Readline Arguments303941
+Node: Readline Init File304887
+Node: Readline Init Syntax305708
+Node: Commands For Moving309640
+Node: Commands For History310260
+Node: Commands For Text311330
+Node: Commands For Killing313046
+Node: Numeric Arguments314168
+Node: Commands For Completion314606
+Node: Miscellaneous Commands315325
+Node: Readline Vi Mode316077
+Node: Using History Interactively316784
+Node: History Interaction317141
+Node: Event Designators318189
+Node: Word Designators318828
+Node: Modifiers319724
+Node: Renamed Commands320469
+Node: Formatting Documentation322131
+Node: Installing GDB325465
+Node: Separate Objdir328945
+Node: Config Names331490
+Node: configure Options332918
+Node: Index335234
+
+End Tag Table
diff --git a/gnu/usr.bin/gdb/doc/gdb.info-1 b/gnu/usr.bin/gdb/doc/gdb.info-1
new file mode 100644
index 0000000..a1d7120
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/gdb.info-1
@@ -0,0 +1,1304 @@
+This is Info file ./gdb.info, produced by Makeinfo-1.52 from the input
+file gdb.texinfo.
+
+START-INFO-DIR-ENTRY
+* Gdb:: The GNU debugger.
+END-INFO-DIR-ENTRY
+ This file documents the GNU debugger GDB.
+
+ This is Edition 4.09, August 1993, of `Debugging with GDB: the GNU
+Source-Level Debugger' for GDB Version 4.11.
+
+ Copyright (C) 1988, '89, '90, '91, '92, '93 Free Software
+Foundation, Inc.
+
+ Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of
+this manual under the conditions for verbatim copying, provided also
+that the entire resulting derived work is distributed under the terms
+of a permission notice identical to this one.
+
+ Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions.
+
+
+File: gdb.info, Node: Top, Next: Summary, Prev: (DIR), Up: (DIR)
+
+Debugging with GDB
+******************
+
+ This file describes GDB, the GNU symbolic debugger.
+
+ This is Edition 4.09, August 1993, for GDB Version 4.11.
+
+* Menu:
+
+* Summary:: Summary of GDB
+
+* New Features:: New features since GDB version 3.5
+
+* Sample Session:: A sample GDB session
+
+* Invocation:: Getting in and out of GDB
+* Commands:: GDB commands
+* Running:: Running programs under GDB
+* Stopping:: Stopping and continuing
+* Stack:: Examining the stack
+* Source:: Examining source files
+* Data:: Examining data
+
+* Languages:: Using GDB with different languages
+
+
+* Symbols:: Examining the symbol table
+* Altering:: Altering execution
+* GDB Files:: GDB files
+* Targets:: Specifying a debugging target
+* Controlling GDB:: Controlling GDB
+* Sequences:: Canned sequences of commands
+
+* Emacs:: Using GDB under GNU Emacs
+
+* GDB Bugs:: Reporting bugs in GDB
+* Command Line Editing:: Facilities of the readline library
+* Using History Interactively::
+
+* Renamed Commands::
+
+* Formatting Documentation:: How to format and print GDB documentation
+* Installing GDB:: Installing GDB
+
+* Index:: Index
+
+
+File: gdb.info, Node: Summary, Next: New Features, Prev: Top, Up: Top
+
+Summary of GDB
+**************
+
+ The purpose of a debugger such as GDB is to allow you to see what is
+going on "inside" another program while it executes--or what another
+program was doing at the moment it crashed.
+
+ GDB can do four main kinds of things (plus other things in support of
+these) to help you catch bugs in the act:
+
+ * Start your program, specifying anything that might affect its
+ behavior.
+
+ * Make your program stop on specified conditions.
+
+ * Examine what has happened, when your program has stopped.
+
+ * Change things in your program, so you can experiment with
+ correcting the effects of one bug and go on to learn about another.
+
+ You can use GDB to debug programs written in C, C++, and Modula-2.
+G{No Value For "DBN"} can be used to debug programs written in Fortran,
+although it does not yet support entering expressions, printing values,
+etc. using Fortran syntax. It may be necessary to refer to some
+variables with a trailing underscore.
+
+* Menu:
+
+* Free Software:: Freely redistributable software
+* Contributors:: Contributors to GDB
+
+
+File: gdb.info, Node: Free Software, Next: Contributors, Up: Summary
+
+Free software
+=============
+
+ GDB is "free software", protected by the GNU General Public License
+(GPL). The GPL gives you the freedom to copy or adapt a licensed
+program--but every person getting a copy also gets with it the freedom
+to modify that copy (which means that they must get access to the
+source code), and the freedom to distribute further copies. Typical
+software companies use copyrights to limit your freedoms; the Free
+Software Foundation uses the GPL to preserve these freedoms.
+
+ Fundamentally, the General Public License is a license which says
+that you have these freedoms and that you cannot take these freedoms
+away from anyone else.
+
+
+File: gdb.info, Node: Contributors, Prev: Free Software, Up: Summary
+
+Contributors to GDB
+===================
+
+ Richard Stallman was the original author of GDB, and of many other
+GNU programs. Many others have contributed to its development. This
+section attempts to credit major contributors. One of the virtues of
+free software is that everyone is free to contribute to it; with
+regret, we cannot actually acknowledge everyone here. The file
+`ChangeLog' in the GDB distribution approximates a blow-by-blow account.
+
+ Changes much prior to version 2.0 are lost in the mists of time.
+
+ *Plea:* Additions to this section are particularly welcome. If you
+ or your friends (or enemies, to be evenhanded) have been unfairly
+ omitted from this list, we would like to add your names!
+
+ So that they may not regard their long labor as thankless, we
+particularly thank those who shepherded GDB through major releases: Fred
+Fish (releases 4.11, 4.10, 4.9), Stu Grossman and John Gilmore (releases
+4.8, 4.7, 4.6, 4.5, 4.4), John Gilmore (releases 4.3, 4.2, 4.1, 4.0, and
+3.9); Jim Kingdon (releases 3.5, 3.4, 3.3); and Randy Smith (releases
+3.2, 3.1, 3.0). As major maintainer of GDB for some period, each
+contributed significantly to the structure, stability, and capabilities
+of the entire debugger.
+
+ Richard Stallman, assisted at various times by Peter TerMaat, Chris
+Hanson, and Richard Mlynarik, handled releases through 2.8.
+
+ Michael Tiemann is the author of most of the GNU C++ support in GDB,
+with significant additional contributions from Per Bothner. James
+Clark wrote the GNU C++ demangler. Early work on C++ was by Peter
+TerMaat (who also did much general update work leading to release 3.0).
+
+ GDB 4 uses the BFD subroutine library to examine multiple
+object-file formats; BFD was a joint project of David V.
+Henkel-Wallace, Rich Pixley, Steve Chamberlain, and John Gilmore.
+
+ David Johnson wrote the original COFF support; Pace Willison did the
+original support for encapsulated COFF.
+
+ Adam de Boor and Bradley Davis contributed the ISI Optimum V support.
+Per Bothner, Noboyuki Hikichi, and Alessandro Forin contributed MIPS
+support. Jean-Daniel Fekete contributed Sun 386i support. Chris
+Hanson improved the HP9000 support. Noboyuki Hikichi and Tomoyuki
+Hasei contributed Sony/News OS 3 support. David Johnson contributed
+Encore Umax support. Jyrki Kuoppala contributed Altos 3068 support.
+Keith Packard contributed NS32K support. Doug Rabson contributed Acorn
+Risc Machine support. Chris Smith contributed Convex support (and
+Fortran debugging). Jonathan Stone contributed Pyramid support.
+Michael Tiemann contributed SPARC support. Tim Tucker contributed
+support for the Gould NP1 and Gould Powernode. Pace Willison
+contributed Intel 386 support. Jay Vosburgh contributed Symmetry
+support.
+
+ Rich Schaefer and Peter Schauer helped with support of SunOS shared
+libraries.
+
+ Jay Fenlason and Roland McGrath ensured that GDB and GAS agree about
+several machine instruction sets.
+
+ Patrick Duval, Ted Goldstein, Vikram Koka and Glenn Engel helped
+develop remote debugging. Intel Corporation and Wind River Systems
+contributed remote debugging modules for their products.
+
+ Brian Fox is the author of the readline libraries providing
+command-line editing and command history.
+
+ Andrew Beers of SUNY Buffalo wrote the language-switching code, the
+Modula-2 support, and contributed the Languages chapter of this manual.
+
+ Fred Fish wrote most of the support for Unix System Vr4. He also
+enhanced the command-completion support to cover C++ overloaded symbols.
+
+ Hitachi America, Ltd. sponsored the support for Hitachi
+microprocessors.
+
+
+File: gdb.info, Node: New Features, Next: Sample Session, Prev: Summary, Up: Top
+
+New Features since GDB Version 3.5
+**********************************
+
+*Targets*
+ Using the new command `target', you can select at runtime whether
+ you are debugging local files, local processes, standalone systems
+ over a serial port, realtime systems over a TCP/IP connection,
+ etc. The command `load' can download programs into a remote
+ system. Serial stubs are available for Motorola 680x0, Intel
+ 80386, and Sparc remote systems; GDB also supports debugging
+ realtime processes running under VxWorks, using SunRPC Remote
+ Procedure Calls over TCP/IP to talk to a debugger stub on the
+ target system. Internally, GDB now uses a function vector to
+ mediate access to different targets; if you need to add your own
+ support for a remote protocol, this makes it much easier.
+
+*Watchpoints*
+ GDB now sports watchpoints as well as breakpoints. You can use a
+ watchpoint to stop execution whenever the value of an expression
+ changes, without having to predict a particular place in your
+ program where this may happen.
+
+*Wide Output*
+ Commands that issue wide output now insert newlines at places
+ designed to make the output more readable.
+
+*Object Code Formats*
+ GDB uses a new library called the Binary File Descriptor (BFD)
+ Library to permit it to switch dynamically, without
+ reconfiguration or recompilation, between different object-file
+ formats. Formats currently supported are COFF, ELF, a.out, Intel
+ 960 b.out, MIPS ECOFF, HPPA SOM (with stabs debugging), and
+ S-records; files may be read as .o files, archive libraries, or
+ core dumps. BFD is available as a subroutine library so that
+ other programs may take advantage of it, and the other GNU binary
+ utilities are being converted to use it.
+
+*Configuration and Ports*
+ Compile-time configuration (to select a particular architecture and
+ operating system) is much easier. The script `configure' now
+ allows you to configure GDB as either a native debugger or a
+ cross-debugger. *Note Installing GDB::, for details on how to
+ configure.
+
+*Interaction*
+ The user interface to the GDB control variables is simpler, and is
+ consolidated in two commands, `set' and `show'. Output lines are
+ now broken at readable places, rather than overflowing onto the
+ next line. You can suppress output of machine-level addresses,
+ displaying only source language information.
+
+*C++*
+ GDB now supports C++ multiple inheritance (if used with a GCC
+ version 2 compiler), and also has limited support for C++ exception
+ handling, with the commands `catch' and `info catch': GDB can
+ break when an exception is raised, before the stack is peeled back
+ to the exception handler's context.
+
+*Modula-2*
+ GDB now has preliminary support for the GNU Modula-2 compiler,
+ currently under development at the State University of New York at
+ Buffalo. Coordinated development of both GDB and the GNU Modula-2
+ compiler will continue. Other Modula-2 compilers are currently
+ not supported, and attempting to debug programs compiled with them
+ will likely result in an error as the symbol table of the
+ executable is read in.
+
+*Command Rationalization*
+ Many GDB commands have been renamed to make them easier to remember
+ and use. In particular, the subcommands of `info' and
+ `show'/`set' are grouped to make the former refer to the state of
+ your program, and the latter refer to the state of GDB itself.
+ *Note Renamed Commands::, for details on what commands were
+ renamed.
+
+*Shared Libraries*
+ GDB 4 can debug programs and core files that use SunOS, SVR4, or
+ IBM RS/6000 shared libraries.
+
+*Reference Card*
+ GDB 4 has a reference card. *Note Formatting the Documentation:
+ Formatting Documentation, for instructions about how to print it.
+
+
+File: gdb.info, Node: Sample Session, Next: Invocation, Prev: New Features, Up: Top
+
+A Sample GDB Session
+********************
+
+ You can use this manual at your leisure to read all about GDB.
+However, a handful of commands are enough to get started using the
+debugger. This chapter illustrates those commands.
+
+ One of the preliminary versions of GNU `m4' (a generic macro
+processor) exhibits the following bug: sometimes, when we change its
+quote strings from the default, the commands used to capture one macro
+definition within another stop working. In the following short `m4'
+session, we define a macro `foo' which expands to `0000'; we then use
+the `m4' built-in `defn' to define `bar' as the same thing. However,
+when we change the open quote string to `<QUOTE>' and the close quote
+string to `<UNQUOTE>', the same procedure fails to define a new synonym
+`baz':
+
+ $ cd gnu/m4
+ $ ./m4
+ define(foo,0000)
+
+ foo
+ 0000
+ define(bar,defn(`foo'))
+
+ bar
+ 0000
+ changequote(<QUOTE>,<UNQUOTE>)
+
+ define(baz,defn(<QUOTE>foo<UNQUOTE>))
+ baz
+ C-d
+ m4: End of input: 0: fatal error: EOF in string
+
+Let us use GDB to try to see what is going on.
+
+ $ gdb m4
+ GDB is free software and you are welcome to distribute copies
+ of it under certain conditions; type "show copying" to see
+ the conditions.
+ There is absolutely no warranty for GDB; type "show warranty"
+ for details.
+ GDB 4.11, Copyright 1993 Free Software Foundation, Inc...
+ (gdb)
+
+GDB reads only enough symbol data to know where to find the rest when
+needed; as a result, the first prompt comes up very quickly. We now
+tell GDB to use a narrower display width than usual, so that examples
+will fit in this manual.
+
+ (gdb) set width 70
+
+We need to see how the `m4' built-in `changequote' works. Having
+looked at the source, we know the relevant subroutine is
+`m4_changequote', so we set a breakpoint there with the GDB `break'
+command.
+
+ (gdb) break m4_changequote
+ Breakpoint 1 at 0x62f4: file builtin.c, line 879.
+
+Using the `run' command, we start `m4' running under GDB control; as
+long as control does not reach the `m4_changequote' subroutine, the
+program runs as usual:
+
+ (gdb) run
+ Starting program: /work/Editorial/gdb/gnu/m4/m4
+ define(foo,0000)
+
+ foo
+ 0000
+
+To trigger the breakpoint, we call `changequote'. GDB suspends
+execution of `m4', displaying information about the context where it
+stops.
+
+ changequote(<QUOTE>,<UNQUOTE>)
+
+ Breakpoint 1, m4_changequote (argc=3, argv=0x33c70)
+ at builtin.c:879
+ 879 if (bad_argc(TOKEN_DATA_TEXT(argv[0]),argc,1,3))
+
+Now we use the command `n' (`next') to advance execution to the next
+line of the current function.
+
+ (gdb) n
+ 882 set_quotes((argc >= 2) ? TOKEN_DATA_TEXT(argv[1])\
+ : nil,
+
+`set_quotes' looks like a promising subroutine. We can go into it by
+using the command `s' (`step') instead of `next'. `step' goes to the
+next line to be executed in *any* subroutine, so it steps into
+`set_quotes'.
+
+ (gdb) s
+ set_quotes (lq=0x34c78 "<QUOTE>", rq=0x34c88 "<UNQUOTE>")
+ at input.c:530
+ 530 if (lquote != def_lquote)
+
+The display that shows the subroutine where `m4' is now suspended (and
+its arguments) is called a stack frame display. It shows a summary of
+the stack. We can use the `backtrace' command (which can also be
+spelled `bt'), to see where we are in the stack as a whole: the
+`backtrace' command displays a stack frame for each active subroutine.
+
+ (gdb) bt
+ #0 set_quotes (lq=0x34c78 "<QUOTE>", rq=0x34c88 "<UNQUOTE>")
+ at input.c:530
+ #1 0x6344 in m4_changequote (argc=3, argv=0x33c70)
+ at builtin.c:882
+ #2 0x8174 in expand_macro (sym=0x33320) at macro.c:242
+ #3 0x7a88 in expand_token (obs=0x0, t=209696, td=0xf7fffa30)
+ at macro.c:71
+ #4 0x79dc in expand_input () at macro.c:40
+ #5 0x2930 in main (argc=0, argv=0xf7fffb20) at m4.c:195
+
+We will step through a few more lines to see what happens. The first
+two times, we can use `s'; the next two times we use `n' to avoid
+falling into the `xstrdup' subroutine.
+
+ (gdb) s
+ 0x3b5c 532 if (rquote != def_rquote)
+ (gdb) s
+ 0x3b80 535 lquote = (lq == nil || *lq == '\0') ? \
+ def_lquote : xstrdup(lq);
+ (gdb) n
+ 536 rquote = (rq == nil || *rq == '\0') ? def_rquote\
+ : xstrdup(rq);
+ (gdb) n
+ 538 len_lquote = strlen(rquote);
+
+The last line displayed looks a little odd; we can examine the variables
+`lquote' and `rquote' to see if they are in fact the new left and right
+quotes we specified. We use the command `p' (`print') to see their
+values.
+
+ (gdb) p lquote
+ $1 = 0x35d40 "<QUOTE>"
+ (gdb) p rquote
+ $2 = 0x35d50 "<UNQUOTE>"
+
+`lquote' and `rquote' are indeed the new left and right quotes. To
+look at some context, we can display ten lines of source surrounding
+the current line with the `l' (`list') command.
+
+ (gdb) l
+ 533 xfree(rquote);
+ 534
+ 535 lquote = (lq == nil || *lq == '\0') ? def_lquote\
+ : xstrdup (lq);
+ 536 rquote = (rq == nil || *rq == '\0') ? def_rquote\
+ : xstrdup (rq);
+ 537
+ 538 len_lquote = strlen(rquote);
+ 539 len_rquote = strlen(lquote);
+ 540 }
+ 541
+ 542 void
+
+Let us step past the two lines that set `len_lquote' and `len_rquote',
+and then examine the values of those variables.
+
+ (gdb) n
+ 539 len_rquote = strlen(lquote);
+ (gdb) n
+ 540 }
+ (gdb) p len_lquote
+ $3 = 9
+ (gdb) p len_rquote
+ $4 = 7
+
+That certainly looks wrong, assuming `len_lquote' and `len_rquote' are
+meant to be the lengths of `lquote' and `rquote' respectively. We can
+set them to better values using the `p' command, since it can print the
+value of any expression--and that expression can include subroutine
+calls and assignments.
+
+ (gdb) p len_lquote=strlen(lquote)
+ $5 = 7
+ (gdb) p len_rquote=strlen(rquote)
+ $6 = 9
+
+Is that enough to fix the problem of using the new quotes with the `m4'
+built-in `defn'? We can allow `m4' to continue executing with the `c'
+(`continue') command, and then try the example that caused trouble
+initially:
+
+ (gdb) c
+ Continuing.
+
+ define(baz,defn(<QUOTE>foo<UNQUOTE>))
+
+ baz
+ 0000
+
+Success! The new quotes now work just as well as the default ones. The
+problem seems to have been just the two typos defining the wrong
+lengths. We allow `m4' exit by giving it an EOF as input:
+
+ C-d
+ Program exited normally.
+
+The message `Program exited normally.' is from GDB; it indicates `m4'
+has finished executing. We can end our GDB session with the GDB `quit'
+command.
+
+ (gdb) quit
+
+
+File: gdb.info, Node: Invocation, Next: Commands, Prev: Sample Session, Up: Top
+
+Getting In and Out of GDB
+*************************
+
+ This chapter discusses how to start GDB, and how to get out of it.
+(The essentials: type `gdb' to start GDB, and type `quit' or `C-d' to
+exit.)
+
+* Menu:
+
+* Invoking GDB:: How to start GDB
+* Quitting GDB:: How to quit GDB
+* Shell Commands:: How to use shell commands inside GDB
+
+
+File: gdb.info, Node: Invoking GDB, Next: Quitting GDB, Up: Invocation
+
+Invoking GDB
+============
+
+ Invoke GDB by running the program `gdb'. Once started, GDB reads
+commands from the terminal until you tell it to exit.
+
+ You can also run `gdb' with a variety of arguments and options, to
+specify more of your debugging environment at the outset.
+
+ The command-line options described here are designed to cover a
+variety of situations; in some environments, some of these options may
+effectively be unavailable.
+
+ The most usual way to start GDB is with one argument, specifying an
+executable program:
+
+ gdb PROGRAM
+
+You can also start with both an executable program and a core file
+specified:
+
+ gdb PROGRAM CORE
+
+ You can, instead, specify a process ID as a second argument, if you
+want to debug a running process:
+
+ gdb PROGRAM 1234
+
+would attach GDB to process `1234' (unless you also have a file named
+`1234'; GDB does check for a core file first).
+
+ Taking advantage of the second command-line argument requires a
+fairly complete operating system; when you use GDB as a remote debugger
+attached to a bare board, there may not be any notion of "process", and
+there is often no way to get a core dump.
+
+You can further control how GDB starts up by using command-line
+options. GDB itself can remind you of the options available.
+
+Type
+
+ gdb -help
+
+to display all available options and briefly describe their use (`gdb
+-h' is a shorter equivalent).
+
+ All options and command line arguments you give are processed in
+sequential order. The order makes a difference when the `-x' option is
+used.
+
+* Menu:
+
+
+
+* File Options:: Choosing files
+* Mode Options:: Choosing modes
+
+
+File: gdb.info, Node: File Options, Next: Mode Options, Up: Invoking GDB
+
+Choosing files
+--------------
+
+ When GDB starts, it reads any arguments other than options as
+specifying an executable file and core file (or process ID). This is
+the same as if the arguments were specified by the `-se' and `-c'
+options respectively. (GDB reads the first argument that does not have
+an associated option flag as equivalent to the `-se' option followed by
+that argument; and the second argument that does not have an associated
+option flag, if any, as equivalent to the `-c' option followed by that
+argument.)
+
+ Many options have both long and short forms; both are shown in the
+following list. GDB also recognizes the long forms if you truncate
+them, so long as enough of the option is present to be unambiguous.
+(If you prefer, you can flag option arguments with `--' rather than
+`-', though we illustrate the more usual convention.)
+
+`-symbols FILE'
+`-s FILE'
+ Read symbol table from file FILE.
+
+`-exec FILE'
+`-e FILE'
+ Use file FILE as the executable file to execute when appropriate,
+ and for examining pure data in conjunction with a core dump.
+
+`-se FILE'
+ Read symbol table from file FILE and use it as the executable file.
+
+`-core FILE'
+`-c FILE'
+ Use file FILE as a core dump to examine.
+
+`-c NUMBER'
+ Connect to process ID NUMBER, as with the `attach' command (unless
+ there is a file in core-dump format named NUMBER, in which case
+ `-c' specifies that file as a core dump to read).
+
+`-command FILE'
+`-x FILE'
+ Execute GDB commands from file FILE. *Note Command files: Command
+ Files.
+
+`-directory DIRECTORY'
+`-d DIRECTORY'
+ Add DIRECTORY to the path to search for source files.
+
+`-m'
+`-mapped'
+ *Warning: this option depends on operating system facilities that
+ are not supported on all systems.*
+ If memory-mapped files are available on your system through the
+ `mmap' system call, you can use this option to have GDB write the
+ symbols from your program into a reusable file in the current
+ directory. If the program you are debugging is called
+ `/tmp/fred', the mapped symbol file will be `./fred.syms'. Future
+ GDB debugging sessions will notice the presence of this file, and
+ will quickly map in symbol information from it, rather than reading
+ the symbol table from the executable program.
+
+ The `.syms' file is specific to the host machine where GDB is run.
+ It holds an exact image of the internal GDB symbol table. It
+ cannot be shared across multiple host platforms.
+
+`-r'
+`-readnow'
+ Read each symbol file's entire symbol table immediately, rather
+ than the default, which is to read it incrementally as it is
+ needed. This makes startup slower, but makes future operations
+ faster.
+
+ The `-mapped' and `-readnow' options are typically combined in order
+to build a `.syms' file that contains complete symbol information.
+(*Note Commands to specify files: Files, for information on `.syms'
+files.) A simple GDB invocation to do nothing but build a `.syms' file
+for future use is:
+
+ gdb -batch -nx -mapped -readnow programname
+
+
+File: gdb.info, Node: Mode Options, Prev: File Options, Up: Invoking GDB
+
+Choosing modes
+--------------
+
+ You can run GDB in various alternative modes--for example, in batch
+mode or quiet mode.
+
+`-nx'
+`-n'
+ Do not execute commands from any initialization files (normally
+ called `.gdbinit'). Normally, the commands in these files are
+ executed after all the command options and arguments have been
+ processed. *Note Command files: Command Files.
+
+`-quiet'
+`-q'
+ "Quiet". Do not print the introductory and copyright messages.
+ These messages are also suppressed in batch mode.
+
+`-batch'
+ Run in batch mode. Exit with status `0' after processing all the
+ command files specified with `-x' (and all commands from
+ initialization files, if not inhibited with `-n'). Exit with
+ nonzero status if an error occurs in executing the GDB commands in
+ the command files.
+
+ Batch mode may be useful for running GDB as a filter, for example
+ to download and run a program on another computer; in order to
+ make this more useful, the message
+
+ Program exited normally.
+
+ (which is ordinarily issued whenever a program running under GDB
+ control terminates) is not issued when running in batch mode.
+
+`-cd DIRECTORY'
+ Run GDB using DIRECTORY as its working directory, instead of the
+ current directory.
+
+`-fullname'
+`-f'
+ Emacs sets this option when it runs GDB as a subprocess. It tells
+ GDB to output the full file name and line number in a standard,
+ recognizable fashion each time a stack frame is displayed (which
+ includes each time your program stops). This recognizable format
+ looks like two `\032' characters, followed by the file name, line
+ number and character position separated by colons, and a newline.
+ The Emacs-to-GDB interface program uses the two `\032' characters
+ as a signal to display the source code for the frame.
+
+`-b BPS'
+ Set the line speed (baud rate or bits per second) of any serial
+ interface used by GDB for remote debugging.
+
+`-tty DEVICE'
+ Run using DEVICE for your program's standard input and output.
+
+
+File: gdb.info, Node: Quitting GDB, Next: Shell Commands, Prev: Invoking GDB, Up: Invocation
+
+Quitting GDB
+============
+
+`quit'
+ To exit GDB, use the `quit' command (abbreviated `q'), or type an
+ end-of-file character (usually `C-d').
+
+ An interrupt (often `C-c') will not exit from GDB, but rather will
+terminate the action of any GDB command that is in progress and return
+to GDB command level. It is safe to type the interrupt character at
+any time because GDB does not allow it to take effect until a time when
+it is safe.
+
+ If you have been using GDB to control an attached process or device,
+you can release it with the `detach' command (*note Debugging an
+already-running process: Attach.).
+
+
+File: gdb.info, Node: Shell Commands, Prev: Quitting GDB, Up: Invocation
+
+Shell commands
+==============
+
+ If you need to execute occasional shell commands during your
+debugging session, there is no need to leave or suspend GDB; you can
+just use the `shell' command.
+
+`shell COMMAND STRING'
+ Invoke a the standard shell to execute COMMAND STRING. If it
+ exists, the environment variable `SHELL' determines which shell to
+ run. Otherwise GDB uses `/bin/sh'.
+
+ The utility `make' is often needed in development environments. You
+do not have to use the `shell' command for this purpose in GDB:
+
+`make MAKE-ARGS'
+ Execute the `make' program with the specified arguments. This is
+ equivalent to `shell make MAKE-ARGS'.
+
+
+File: gdb.info, Node: Commands, Next: Running, Prev: Invocation, Up: Top
+
+GDB Commands
+************
+
+ You can abbreviate a GDB command to the first few letters of the
+command name, if that abbreviation is unambiguous; and you can repeat
+certain GDB commands by typing just RET. You can also use the TAB key
+to get GDB to fill out the rest of a word in a command (or to show you
+the alternatives available, if there is more than one possibility).
+
+* Menu:
+
+* Command Syntax:: How to give commands to GDB
+* Completion:: Command completion
+* Help:: How to ask GDB for help
+
+
+File: gdb.info, Node: Command Syntax, Next: Completion, Up: Commands
+
+Command syntax
+==============
+
+ A GDB command is a single line of input. There is no limit on how
+long it can be. It starts with a command name, which is followed by
+arguments whose meaning depends on the command name. For example, the
+command `step' accepts an argument which is the number of times to
+step, as in `step 5'. You can also use the `step' command with no
+arguments. Some command names do not allow any arguments.
+
+ GDB command names may always be truncated if that abbreviation is
+unambiguous. Other possible command abbreviations are listed in the
+documentation for individual commands. In some cases, even ambiguous
+abbreviations are allowed; for example, `s' is specially defined as
+equivalent to `step' even though there are other commands whose names
+start with `s'. You can test abbreviations by using them as arguments
+to the `help' command.
+
+ A blank line as input to GDB (typing just RET) means to repeat the
+previous command. Certain commands (for example, `run') will not repeat
+this way; these are commands for which unintentional repetition might
+cause trouble and which you are unlikely to want to repeat.
+
+ The `list' and `x' commands, when you repeat them with RET,
+construct new arguments rather than repeating exactly as typed. This
+permits easy scanning of source or memory.
+
+ GDB can also use RET in another way: to partition lengthy output, in
+a way similar to the common utility `more' (*note Screen size: Screen
+Size.). Since it is easy to press one RET too many in this situation,
+GDB disables command repetition after any command that generates this
+sort of display.
+
+ Any text from a `#' to the end of the line is a comment; it does
+nothing. This is useful mainly in command files (*note Command files:
+Command Files.).
+
+
+File: gdb.info, Node: Completion, Next: Help, Prev: Command Syntax, Up: Commands
+
+Command completion
+==================
+
+ GDB can fill in the rest of a word in a command for you, if there is
+only one possibility; it can also show you what the valid possibilities
+are for the next word in a command, at any time. This works for GDB
+commands, GDB subcommands, and the names of symbols in your program.
+
+ Press the TAB key whenever you want GDB to fill out the rest of a
+word. If there is only one possibility, GDB will fill in the word, and
+wait for you to finish the command (or press RET to enter it). For
+example, if you type
+
+ (gdb) info bre TAB
+
+GDB fills in the rest of the word `breakpoints', since that is the only
+`info' subcommand beginning with `bre':
+
+ (gdb) info breakpoints
+
+You can either press RET at this point, to run the `info breakpoints'
+command, or backspace and enter something else, if `breakpoints' does
+not look like the command you expected. (If you were sure you wanted
+`info breakpoints' in the first place, you might as well just type RET
+immediately after `info bre', to exploit command abbreviations rather
+than command completion).
+
+ If there is more than one possibility for the next word when you
+press TAB, GDB will sound a bell. You can either supply more
+characters and try again, or just press TAB a second time, and GDB will
+display all the possible completions for that word. For example, you
+might want to set a breakpoint on a subroutine whose name begins with
+`make_', but when you type `b make_TAB' GDB just sounds the bell.
+Typing TAB again will display all the function names in your program
+that begin with those characters, for example:
+
+ (gdb) b make_ TAB
+GDB sounds bell; press TAB again, to see:
+ make_a_section_from_file make_environ
+ make_abs_section make_function_type
+ make_blockvector make_pointer_type
+ make_cleanup make_reference_type
+ make_command make_symbol_completion_list
+ (gdb) b make_
+
+After displaying the available possibilities, GDB copies your partial
+input (`b make_' in the example) so you can finish the command.
+
+ If you just want to see the list of alternatives in the first place,
+you can press `M-?' rather than pressing TAB twice. `M-?' means `META
+?'. You can type this either by holding down a key designated as the
+META shift on your keyboard (if there is one) while typing `?', or as
+ESC followed by `?'.
+
+ Sometimes the string you need, while logically a "word", may contain
+parentheses or other characters that GDB normally excludes from its
+notion of a word. To permit word completion to work in this situation,
+you may enclose words in `'' (single quote marks) in GDB commands.
+
+ The most likely situation where you might need this is in typing the
+name of a C++ function. This is because C++ allows function overloading
+(multiple definitions of the same function, distinguished by argument
+type). For example, when you want to set a breakpoint you may need to
+distinguish whether you mean the version of `name' that takes an `int'
+parameter, `name(int)', or the version that takes a `float' parameter,
+`name(float)'. To use the word-completion facilities in this
+situation, type a single quote `'' at the beginning of the function
+name. This alerts GDB that it may need to consider more information
+than usual when you press TAB or `M-?' to request word completion:
+
+ (gdb) b 'bubble( M-?
+ bubble(double,double) bubble(int,int)
+ (gdb) b 'bubble(
+
+ In some cases, GDB can tell that completing a name will require
+quotes. When this happens, GDB will insert the quote for you (while
+completing as much as it can) if you do not type the quote in the first
+place:
+
+ (gdb) b bub TAB
+GDB alters your input line to the following, and rings a bell:
+ (gdb) b 'bubble(
+
+In general, GDB can tell that a quote is needed (and inserts it) if you
+have not yet started typing the argument list when you ask for
+completion on an overloaded symbol.
+
+
+File: gdb.info, Node: Help, Prev: Completion, Up: Commands
+
+Getting help
+============
+
+ You can always ask GDB itself for information on its commands, using
+the command `help'.
+
+`help'
+`h'
+ You can use `help' (abbreviated `h') with no arguments to display
+ a short list of named classes of commands:
+
+ (gdb) help
+ List of classes of commands:
+
+ running -- Running the program
+ stack -- Examining the stack
+ data -- Examining data
+ breakpoints -- Making program stop at certain points
+ files -- Specifying and examining files
+ status -- Status inquiries
+ support -- Support facilities
+ user-defined -- User-defined commands
+ aliases -- Aliases of other commands
+ obscure -- Obscure features
+
+ Type "help" followed by a class name for a list of
+ commands in that class.
+ Type "help" followed by command name for full
+ documentation.
+ Command name abbreviations are allowed if unambiguous.
+ (gdb)
+
+`help CLASS'
+ Using one of the general help classes as an argument, you can get a
+ list of the individual commands in that class. For example, here
+ is the help display for the class `status':
+
+ (gdb) help status
+ Status inquiries.
+
+ List of commands:
+
+ show -- Generic command for showing things set
+ with "set"
+ info -- Generic command for printing status
+
+ Type "help" followed by command name for full
+ documentation.
+ Command name abbreviations are allowed if unambiguous.
+ (gdb)
+
+`help COMMAND'
+ With a command name as `help' argument, GDB will display a short
+ paragraph on how to use that command.
+
+ In addition to `help', you can use the GDB commands `info' and
+`show' to inquire about the state of your program, or the state of GDB
+itself. Each command supports many topics of inquiry; this manual
+introduces each of them in the appropriate context. The listings under
+`info' and under `show' in the Index point to all the sub-commands.
+*Note Index::.
+
+`info'
+ This command (abbreviated `i') is for describing the state of your
+ program. For example, you can list the arguments given to your
+ program with `info args', list the registers currently in use with
+ `info registers', or list the breakpoints you have set with `info
+ breakpoints'. You can get a complete list of the `info'
+ sub-commands with `help info'.
+
+`show'
+ In contrast, `show' is for describing the state of GDB itself.
+ You can change most of the things you can `show', by using the
+ related command `set'; for example, you can control what number
+ system is used for displays with `set radix', or simply inquire
+ which is currently in use with `show radix'.
+
+ To display all the settable parameters and their current values,
+ you can use `show' with no arguments; you may also use `info set'.
+ Both commands produce the same display.
+
+ Here are three miscellaneous `show' subcommands, all of which are
+exceptional in lacking corresponding `set' commands:
+
+`show version'
+ Show what version of GDB is running. You should include this
+ information in GDB bug-reports. If multiple versions of GDB are in
+ use at your site, you may occasionally want to determine which
+ version of GDB you are running; as GDB evolves, new commands are
+ introduced, and old ones may wither away. The version number is
+ also announced when you start GDB.
+
+`show copying'
+ Display information about permission for copying GDB.
+
+`show warranty'
+ Display the GNU "NO WARRANTY" statement.
+
+
+File: gdb.info, Node: Running, Next: Stopping, Prev: Commands, Up: Top
+
+Running Programs Under GDB
+**************************
+
+ When you run a program under GDB, you must first generate debugging
+information when you compile it. You may start it with its arguments,
+if any, in an environment of your choice. You may redirect your
+program's input and output, debug an already running process, or kill a
+child process.
+
+* Menu:
+
+* Compilation:: Compiling for debugging
+* Starting:: Starting your program
+
+* Arguments:: Your program's arguments
+* Environment:: Your program's environment
+* Working Directory:: Your program's working directory
+* Input/Output:: Your program's input and output
+* Attach:: Debugging an already-running process
+* Kill Process:: Killing the child process
+* Process Information:: Additional process information
+
+
+File: gdb.info, Node: Compilation, Next: Starting, Up: Running
+
+Compiling for debugging
+=======================
+
+ In order to debug a program effectively, you need to generate
+debugging information when you compile it. This debugging information
+is stored in the object file; it describes the data type of each
+variable or function and the correspondence between source line numbers
+and addresses in the executable code.
+
+ To request debugging information, specify the `-g' option when you
+run the compiler.
+
+ Many C compilers are unable to handle the `-g' and `-O' options
+together. Using those compilers, you cannot generate optimized
+executables containing debugging information.
+
+ GCC, the GNU C compiler, supports `-g' with or without `-O', making
+it possible to debug optimized code. We recommend that you *always*
+use `-g' whenever you compile a program. You may think your program is
+correct, but there is no sense in pushing your luck.
+
+ When you debug a program compiled with `-g -O', remember that the
+optimizer is rearranging your code; the debugger will show you what is
+really there. Do not be too surprised when the execution path does not
+exactly match your source file! An extreme example: if you define a
+variable, but never use it, GDB will never see that variable--because
+the compiler optimizes it out of existence.
+
+ Some things do not work as well with `-g -O' as with just `-g',
+particularly on machines with instruction scheduling. If in doubt,
+recompile with `-g' alone, and if this fixes the problem, please report
+it as a bug (including a test case!).
+
+ Older versions of the GNU C compiler permitted a variant option
+`-gg' for debugging information. GDB no longer supports this format;
+if your GNU C compiler has this option, do not use it.
+
+
+File: gdb.info, Node: Starting, Next: Arguments, Prev: Compilation, Up: Running
+
+Starting your program
+=====================
+
+`run'
+`r'
+ Use the `run' command to start your program under GDB. You must
+ first specify the program name (except on VxWorks) with an
+ argument to GDB (*note Getting In and Out of GDB: Invocation.), or
+ by using the `file' or `exec-file' command (*note Commands to
+ specify files: Files.).
+
+ If you are running your program in an execution environment that
+supports processes, `run' creates an inferior process and makes that
+process run your program. (In environments without processes, `run'
+jumps to the start of your program.)
+
+ The execution of a program is affected by certain information it
+receives from its superior. GDB provides ways to specify this
+information, which you must do *before* starting your program. (You
+can change it after starting your program, but such changes will only
+affect your program the next time you start it.) This information may
+be divided into four categories:
+
+The *arguments.*
+ Specify the arguments to give your program as the arguments of the
+ `run' command. If a shell is available on your target, the shell
+ is used to pass the arguments, so that you may use normal
+ conventions (such as wildcard expansion or variable substitution)
+ in describing the arguments. In Unix systems, you can control
+ which shell is used with the `SHELL' environment variable. *Note
+ Your program's arguments: Arguments.
+
+The *environment.*
+ Your program normally inherits its environment from GDB, but you
+ can use the GDB commands `set environment' and `unset environment'
+ to change parts of the environment that will be given to your
+ program. *Note Your program's environment: Environment.
+
+The *working directory.*
+ Your program inherits its working directory from GDB. You can set
+ the GDB working directory with the `cd' command in GDB. *Note
+ Your program's working directory: Working Directory.
+
+The *standard input and output.*
+ Your program normally uses the same device for standard input and
+ standard output as GDB is using. You can redirect input and output
+ in the `run' command line, or you can use the `tty' command to set
+ a different device for your program. *Note Your program's input
+ and output: Input/Output.
+
+ *Warning:* While input and output redirection work, you cannot use
+ pipes to pass the output of the program you are debugging to
+ another program; if you attempt this, GDB is likely to wind up
+ debugging the wrong program.
+
+ When you issue the `run' command, your program begins to execute
+immediately. *Note Stopping and continuing: Stopping, for discussion
+of how to arrange for your program to stop. Once your program has
+stopped, you may call functions in your program, using the `print' or
+`call' commands. *Note Examining Data: Data.
+
+ If the modification time of your symbol file has changed since the
+last time GDB read its symbols, GDB will discard its symbol table and
+re-read it. When it does this, GDB tries to retain your current
+breakpoints.
+
+
+File: gdb.info, Node: Arguments, Next: Environment, Prev: Starting, Up: Running
+
+Your program's arguments
+========================
+
+ The arguments to your program can be specified by the arguments of
+the `run' command. They are passed to a shell, which expands wildcard
+characters and performs redirection of I/O, and thence to your program.
+Your `SHELL' environment variable (if it exists) specifies what shell
+GDB if you do not define `SHELL', GDB uses `/bin/sh'.
+
+ `run' with no arguments uses the same arguments used by the previous
+`run', or those set by the `set args' command.
+
+`set args'
+ Specify the arguments to be used the next time your program is
+ run. If `set args' has no arguments, `run' will execute your
+ program with no arguments. Once you have run your program with
+ arguments, using `set args' before the next `run' is the only way
+ to run it again without arguments.
+
+`show args'
+ Show the arguments to give your program when it is started.
+
+
+File: gdb.info, Node: Environment, Next: Working Directory, Prev: Arguments, Up: Running
+
+Your program's environment
+==========================
+
+ The "environment" consists of a set of environment variables and
+their values. Environment variables conventionally record such things
+as your user name, your home directory, your terminal type, and your
+search path for programs to run. Usually you set up environment
+variables with the shell and they are inherited by all the other
+programs you run. When debugging, it can be useful to try running your
+program with a modified environment without having to start GDB over
+again.
+
+`path DIRECTORY'
+ Add DIRECTORY to the front of the `PATH' environment variable (the
+ search path for executables), for both GDB and your program. You
+ may specify several directory names, separated by `:' or
+ whitespace. If DIRECTORY is already in the path, it is moved to
+ the front, so it will be searched sooner.
+
+ You can use the string `$cwd' to refer to whatever is the current
+ working directory at the time GDB searches the path. If you use
+ `.' instead, it refers to the directory where you executed the
+ `path' command. GDB replaces `.' in the DIRECTORY argument (with
+ the current path) before adding DIRECTORY to the search path.
+
+`show paths'
+ Display the list of search paths for executables (the `PATH'
+ environment variable).
+
+`show environment [VARNAME]'
+ Print the value of environment variable VARNAME to be given to
+ your program when it starts. If you do not supply VARNAME, print
+ the names and values of all environment variables to be given to
+ your program. You can abbreviate `environment' as `env'.
+
+`set environment VARNAME [=] VALUE'
+ Set environment variable VARNAME to VALUE. The value changes for
+ your program only, not for GDB itself. VALUE may be any string;
+ the values of environment variables are just strings, and any
+ interpretation is supplied by your program itself. The VALUE
+ parameter is optional; if it is eliminated, the variable is set to
+ a null value.
+
+ For example, this command:
+
+ set env USER = foo
+
+ tells a Unix program, when subsequently run, that its user is named
+ `foo'. (The spaces around `=' are used for clarity here; they are
+ not actually required.)
+
+`unset environment VARNAME'
+ Remove variable VARNAME from the environment to be passed to your
+ program. This is different from `set env VARNAME ='; `unset
+ environment' removes the variable from the environment, rather
+ than assigning it an empty value.
+
+ *Warning:* GDB runs your program using the shell indicated by your
+`SHELL' environment variable if it exists (or `/bin/sh' if not). If
+your `SHELL' variable names a shell that runs an initialization
+file--such as `.cshrc' for C-shell, or `.bashrc' for BASH--any
+variables you set in that file will affect your program. You may wish
+to move setting of environment variables to files that are only run
+when you sign on, such as `.login' or `.profile'.
+
+
+File: gdb.info, Node: Working Directory, Next: Input/Output, Prev: Environment, Up: Running
+
+Your program's working directory
+================================
+
+ Each time you start your program with `run', it inherits its working
+directory from the current working directory of GDB. The GDB working
+directory is initially whatever it inherited from its parent process
+(typically the shell), but you can specify a new working directory in
+GDB with the `cd' command.
+
+ The GDB working directory also serves as a default for the commands
+that specify files for GDB to operate on. *Note Commands to specify
+files: Files.
+
+`cd DIRECTORY'
+ Set the GDB working directory to DIRECTORY.
+
+`pwd'
+ Print the GDB working directory.
+
+
+File: gdb.info, Node: Input/Output, Next: Attach, Prev: Working Directory, Up: Running
+
+Your program's input and output
+===============================
+
+ By default, the program you run under GDB does input and output to
+the same terminal that GDB uses. GDB switches the terminal to its own
+terminal modes to interact with you, but it records the terminal modes
+your program was using and switches back to them when you continue
+running your program.
+
+`info terminal'
+ Displays information recorded by GDB about the terminal modes your
+ program is using.
+
+ You can redirect your program's input and/or output using shell
+redirection with the `run' command. For example,
+
+ run > outfile
+
+starts your program, diverting its output to the file `outfile'.
+
+ Another way to specify where your program should do input and output
+is with the `tty' command. This command accepts a file name as
+argument, and causes this file to be the default for future `run'
+commands. It also resets the controlling terminal for the child
+process, for future `run' commands. For example,
+
+ tty /dev/ttyb
+
+directs that processes started with subsequent `run' commands default
+to do input and output on the terminal `/dev/ttyb' and have that as
+their controlling terminal.
+
+ An explicit redirection in `run' overrides the `tty' command's
+effect on the input/output device, but not its effect on the controlling
+terminal.
+
+ When you use the `tty' command or redirect input in the `run'
+command, only the input *for your program* is affected. The input for
+GDB still comes from your terminal.
+
diff --git a/gnu/usr.bin/gdb/doc/gdb.info-2 b/gnu/usr.bin/gdb/doc/gdb.info-2
new file mode 100644
index 0000000..e8be2fa
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/gdb.info-2
@@ -0,0 +1,1165 @@
+This is Info file ./gdb.info, produced by Makeinfo-1.52 from the input
+file gdb.texinfo.
+
+START-INFO-DIR-ENTRY
+* Gdb:: The GNU debugger.
+END-INFO-DIR-ENTRY
+ This file documents the GNU debugger GDB.
+
+ This is Edition 4.09, August 1993, of `Debugging with GDB: the GNU
+Source-Level Debugger' for GDB Version 4.11.
+
+ Copyright (C) 1988, '89, '90, '91, '92, '93 Free Software
+Foundation, Inc.
+
+ Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of
+this manual under the conditions for verbatim copying, provided also
+that the entire resulting derived work is distributed under the terms
+of a permission notice identical to this one.
+
+ Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions.
+
+
+File: gdb.info, Node: Attach, Next: Kill Process, Prev: Input/Output, Up: Running
+
+Debugging an already-running process
+====================================
+
+`attach PROCESS-ID'
+ This command attaches to a running process--one that was started
+ outside GDB. (`info files' will show your active targets.) The
+ command takes as argument a process ID. The usual way to find out
+ the process-id of a Unix process is with the `ps' utility, or with
+ the `jobs -l' shell command.
+
+ `attach' will not repeat if you press RET a second time after
+ executing the command.
+
+ To use `attach', your program must be running in an environment
+which supports processes; for example, `attach' does not work for
+programs on bare-board targets that lack an operating system. You must
+also have permission to send the process a signal.
+
+ When using `attach', you should first use the `file' command to
+specify the program running in the process and load its symbol table.
+*Note Commands to Specify Files: Files.
+
+ The first thing GDB does after arranging to debug the specified
+process is to stop it. You can examine and modify an attached process
+with all the GDB commands that are ordinarily available when you start
+processes with `run'. You can insert breakpoints; you can step and
+continue; you can modify storage. If you would rather the process
+continue running, you may use the `continue' command after attaching
+GDB to the process.
+
+`detach'
+ When you have finished debugging the attached process, you can use
+ the `detach' command to release it from GDB control. Detaching
+ the process continues its execution. After the `detach' command,
+ that process and GDB become completely independent once more, and
+ you are ready to `attach' another process or start one with `run'.
+ `detach' will not repeat if you press RET again after executing
+ the command.
+
+ If you exit GDB or use the `run' command while you have an attached
+process, you kill that process. By default, you will be asked for
+confirmation if you try to do either of these things; you can control
+whether or not you need to confirm by using the `set confirm' command
+(*note Optional warnings and messages: Messages/Warnings.).
+
+
+File: gdb.info, Node: Kill Process, Next: Process Information, Prev: Attach, Up: Running
+
+Killing the child process
+=========================
+
+`kill'
+ Kill the child process in which your program is running under GDB.
+
+ This command is useful if you wish to debug a core dump instead of a
+running process. GDB ignores any core dump file while your program is
+running.
+
+ On some operating systems, a program cannot be executed outside GDB
+while you have breakpoints set on it inside GDB. You can use the
+`kill' command in this situation to permit running your program outside
+the debugger.
+
+ The `kill' command is also useful if you wish to recompile and
+relink your program, since on many systems it is impossible to modify an
+executable file while it is running in a process. In this case, when
+you next type `run', GDB will notice that the file has changed, and
+will re-read the symbol table (while trying to preserve your current
+breakpoint settings).
+
+
+File: gdb.info, Node: Process Information, Prev: Kill Process, Up: Running
+
+Additional process information
+==============================
+
+ Some operating systems provide a facility called `/proc' that can be
+used to examine the image of a running process using file-system
+subroutines. If GDB is configured for an operating system with this
+facility, the command `info proc' is available to report on several
+kinds of information about the process running your program.
+
+`info proc'
+ Summarize available information about the process.
+
+`info proc mappings'
+ Report on the address ranges accessible in the program, with
+ information on whether your program may read, write, or execute
+ each range.
+
+`info proc times'
+ Starting time, user CPU time, and system CPU time for your program
+ and its children.
+
+`info proc id'
+ Report on the process IDs related to your program: its own process
+ ID, the ID of its parent, the process group ID, and the session ID.
+
+`info proc status'
+ General information on the state of the process. If the process is
+ stopped, this report includes the reason for stopping, and any
+ signal received.
+
+`info proc all'
+ Show all the above information about the process.
+
+
+File: gdb.info, Node: Stopping, Next: Stack, Prev: Running, Up: Top
+
+Stopping and Continuing
+***********************
+
+ The principal purposes of using a debugger are so that you can stop
+your program before it terminates; or so that, if your program runs into
+trouble, you can investigate and find out why.
+
+ Inside GDB, your program may stop for any of several reasons, such as
+a signal, a breakpoint, or reaching a new line after a GDB command such
+as `step'. You may then examine and change variables, set new
+breakpoints or remove old ones, and then continue execution. Usually,
+the messages shown by GDB provide ample explanation of the status of
+your program--but you can also explicitly request this information at
+any time.
+
+`info program'
+ Display information about the status of your program: whether it is
+ running or not, what process it is, and why it stopped.
+
+* Menu:
+
+
+* Breakpoints:: Breakpoints, watchpoints, and exceptions
+
+
+* Continuing and Stepping:: Resuming execution
+
+* Signals:: Signals
+
+
+File: gdb.info, Node: Breakpoints, Next: Continuing and Stepping, Up: Stopping
+
+Breakpoints, watchpoints, and exceptions
+========================================
+
+ A "breakpoint" makes your program stop whenever a certain point in
+the program is reached. For each breakpoint, you can add various
+conditions to control in finer detail whether your program will stop.
+You can set breakpoints with the `break' command and its variants
+(*note Setting breakpoints: Set Breaks.), to specify the place where
+your program should stop by line number, function name or exact address
+in the program. In languages with exception handling (such as GNU
+C++), you can also set breakpoints where an exception is raised (*note
+Breakpoints and exceptions: Exception Handling.).
+
+ A "watchpoint" is a special breakpoint that stops your program when
+the value of an expression changes. You must use a different command
+to set watchpoints (*note Setting watchpoints: Set Watchpoints.), but
+aside from that, you can manage a watchpoint like any other breakpoint:
+you enable, disable, and delete both breakpoints and watchpoints using
+the same commands.
+
+ You can arrange to have values from your program displayed
+automatically whenever GDB stops at a breakpoint. *Note Automatic
+display: Auto Display.
+
+ GDB assigns a number to each breakpoint or watchpoint when you
+create it; these numbers are successive integers starting with one. In
+many of the commands for controlling various features of breakpoints you
+use the breakpoint number to say which breakpoint you want to change.
+Each breakpoint may be "enabled" or "disabled"; if disabled, it has no
+effect on your program until you enable it again.
+
+* Menu:
+
+* Set Breaks:: Setting breakpoints
+* Set Watchpoints:: Setting watchpoints
+
+* Exception Handling:: Breakpoints and exceptions
+
+* Delete Breaks:: Deleting breakpoints
+* Disabling:: Disabling breakpoints
+* Conditions:: Break conditions
+* Break Commands:: Breakpoint command lists
+
+* Breakpoint Menus:: Breakpoint menus
+
+* Error in Breakpoints:: "Cannot insert breakpoints"
+
+
+File: gdb.info, Node: Set Breaks, Next: Set Watchpoints, Up: Breakpoints
+
+Setting breakpoints
+-------------------
+
+ Breakpoints are set with the `break' command (abbreviated `b'). The
+debugger convenience variable `$bpnum' records the number of the
+beakpoint you've set most recently; see *Note Convenience variables:
+Convenience Vars, for a discussion of what you can do with convenience
+variables.
+
+ You have several ways to say where the breakpoint should go.
+
+`break FUNCTION'
+ Set a breakpoint at entry to function FUNCTION. When using source
+ languages that permit overloading of symbols, such as C++,
+ FUNCTION may refer to more than one possible place to break.
+ *Note Breakpoint menus: Breakpoint Menus, for a discussion of that
+ situation.
+
+`break +OFFSET'
+`break -OFFSET'
+ Set a breakpoint some number of lines forward or back from the
+ position at which execution stopped in the currently selected
+ frame.
+
+`break LINENUM'
+ Set a breakpoint at line LINENUM in the current source file. That
+ file is the last file whose source text was printed. This
+ breakpoint will stop your program just before it executes any of
+ the code on that line.
+
+`break FILENAME:LINENUM'
+ Set a breakpoint at line LINENUM in source file FILENAME.
+
+`break FILENAME:FUNCTION'
+ Set a breakpoint at entry to function FUNCTION found in file
+ FILENAME. Specifying a file name as well as a function name is
+ superfluous except when multiple files contain similarly named
+ functions.
+
+`break *ADDRESS'
+ Set a breakpoint at address ADDRESS. You can use this to set
+ breakpoints in parts of your program which do not have debugging
+ information or source files.
+
+`break'
+ When called without any arguments, `break' sets a breakpoint at
+ the next instruction to be executed in the selected stack frame
+ (*note Examining the Stack: Stack.). In any selected frame but the
+ innermost, this will cause your program to stop as soon as control
+ returns to that frame. This is similar to the effect of a
+ `finish' command in the frame inside the selected frame--except
+ that `finish' does not leave an active breakpoint. If you use
+ `break' without an argument in the innermost frame, GDB will stop
+ the next time it reaches the current location; this may be useful
+ inside loops.
+
+ GDB normally ignores breakpoints when it resumes execution, until
+ at least one instruction has been executed. If it did not do
+ this, you would be unable to proceed past a breakpoint without
+ first disabling the breakpoint. This rule applies whether or not
+ the breakpoint already existed when your program stopped.
+
+`break ... if COND'
+ Set a breakpoint with condition COND; evaluate the expression COND
+ each time the breakpoint is reached, and stop only if the value is
+ nonzero--that is, if COND evaluates as true. `...' stands for one
+ of the possible arguments described above (or no argument)
+ specifying where to break. *Note Break conditions: Conditions,
+ for more information on breakpoint conditions.
+
+`tbreak ARGS'
+ Set a breakpoint enabled only for one stop. ARGS are the same as
+ for the `break' command, and the breakpoint is set in the same
+ way, but the breakpoint is automatically disabled after the first
+ time your program stops there. *Note Disabling breakpoints:
+ Disabling.
+
+`rbreak REGEX'
+ Set breakpoints on all functions matching the regular expression
+ REGEX. This command sets an unconditional breakpoint on all
+ matches, printing a list of all breakpoints it set. Once these
+ breakpoints are set, they are treated just like the breakpoints
+ set with the `break' command. They can be deleted, disabled, made
+ conditional, etc., in the standard ways.
+
+ When debugging C++ programs, `rbreak' is useful for setting
+ breakpoints on overloaded functions that are not members of any
+ special classes.
+
+`info breakpoints [N]'
+`info break [N]'
+`info watchpoints [N]'
+ Print a table of all breakpoints and watchpoints set and not
+ deleted, with the following columns for each breakpoint:
+
+ *Breakpoint Numbers*
+ *Type*
+ Breakpoint or watchpoint.
+
+ *Disposition*
+ Whether the breakpoint is marked to be disabled or deleted
+ when hit.
+
+ *Enabled or Disabled*
+ Enabled breakpoints are marked with `y'. `n' marks
+ breakpoints that are not enabled.
+
+ *Address*
+ Where the breakpoint is in your program, as a memory address
+
+ *What*
+ Where the breakpoint is in the source for your program, as a
+ file and line number.
+
+ If a breakpoint is conditional, `info break' shows the condition on
+ the line following the affected breakpoint; breakpoint commands,
+ if any, are listed after that.
+
+ `info break' with a breakpoint number N as argument lists only
+ that breakpoint. The convenience variable `$_' and the default
+ examining-address for the `x' command are set to the address of
+ the last breakpoint listed (*note Examining memory: Memory.).
+
+ GDB allows you to set any number of breakpoints at the same place in
+your program. There is nothing silly or meaningless about this. When
+the breakpoints are conditional, this is even useful (*note Break
+conditions: Conditions.).
+
+ GDB itself sometimes sets breakpoints in your program for special
+purposes, such as proper handling of `longjmp' (in C programs). These
+internal breakpoints are assigned negative numbers, starting with `-1';
+`info breakpoints' does not display them.
+
+ You can see these breakpoints with the GDB maintenance command
+`maint info breakpoints'.
+
+`maint info breakpoints'
+ Using the same format as `info breakpoints', display both the
+ breakpoints you've set explicitly, and those GDB is using for
+ internal purposes. Internal breakpoints are shown with negative
+ breakpoint numbers. The type column identifies what kind of
+ breakpoint is shown:
+
+ `breakpoint'
+ Normal, explicitly set breakpoint.
+
+ `watchpoint'
+ Normal, explicitly set watchpoint.
+
+ `longjmp'
+ Internal breakpoint, used to handle correctly stepping through
+ `longjmp' calls.
+
+ `longjmp resume'
+ Internal breakpoint at the target of a `longjmp'.
+
+ `until'
+ Temporary internal breakpoint used by the GDB `until' command.
+
+ `finish'
+ Temporary internal breakpoint used by the GDB `finish'
+ command.
+
+
+File: gdb.info, Node: Set Watchpoints, Next: Exception Handling, Prev: Set Breaks, Up: Breakpoints
+
+Setting watchpoints
+-------------------
+
+ You can use a watchpoint to stop execution whenever the value of an
+expression changes, without having to predict a particular place where
+this may happen.
+
+ Watchpoints currently execute two orders of magnitude more slowly
+than other breakpoints, but this can be well worth it to catch errors
+where you have no clue what part of your program is the culprit. Some
+processors provide special hardware to support watchpoint evaluation;
+future releases of GDB will use such hardware if it is available.
+
+`watch EXPR'
+ Set a watchpoint for an expression.
+
+`info watchpoints'
+ This command prints a list of watchpoints and breakpoints; it is
+ the same as `info break'.
+
+
+File: gdb.info, Node: Exception Handling, Next: Delete Breaks, Prev: Set Watchpoints, Up: Breakpoints
+
+Breakpoints and exceptions
+--------------------------
+
+ Some languages, such as GNU C++, implement exception handling. You
+can use GDB to examine what caused your program to raise an exception,
+and to list the exceptions your program is prepared to handle at a
+given point in time.
+
+`catch EXCEPTIONS'
+ You can set breakpoints at active exception handlers by using the
+ `catch' command. EXCEPTIONS is a list of names of exceptions to
+ catch.
+
+ You can use `info catch' to list active exception handlers. *Note
+Information about a frame: Frame Info.
+
+ There are currently some limitations to exception handling in GDB.
+These will be corrected in a future release.
+
+ * If you call a function interactively, GDB normally returns control
+ to you when the function has finished executing. If the call
+ raises an exception, however, the call may bypass the mechanism
+ that returns control to you and cause your program to simply
+ continue running until it hits a breakpoint, catches a signal that
+ GDB is listening for, or exits.
+
+ * You cannot raise an exception interactively.
+
+ * You cannot interactively install an exception handler.
+
+ Sometimes `catch' is not the best way to debug exception handling:
+if you need to know exactly where an exception is raised, it is better
+to stop *before* the exception handler is called, since that way you
+can see the stack before any unwinding takes place. If you set a
+breakpoint in an exception handler instead, it may not be easy to find
+out where the exception was raised.
+
+ To stop just before an exception handler is called, you need some
+knowledge of the implementation. In the case of GNU C++, exceptions are
+raised by calling a library function named `__raise_exception' which
+has the following ANSI C interface:
+
+ /* ADDR is where the exception identifier is stored.
+ ID is the exception identifier. */
+ void __raise_exception (void **ADDR, void *ID);
+
+To make the debugger catch all exceptions before any stack unwinding
+takes place, set a breakpoint on `__raise_exception' (*note
+Breakpoints; watchpoints; and exceptions: Breakpoints.).
+
+ With a conditional breakpoint (*note Break conditions: Conditions.)
+that depends on the value of ID, you can stop your program when a
+specific exception is raised. You can use multiple conditional
+breakpoints to stop your program when any of a number of exceptions are
+raised.
+
+
+File: gdb.info, Node: Delete Breaks, Next: Disabling, Prev: Exception Handling, Up: Breakpoints
+
+Deleting breakpoints
+--------------------
+
+ It is often necessary to eliminate a breakpoint or watchpoint once it
+has done its job and you no longer want your program to stop there.
+This is called "deleting" the breakpoint. A breakpoint that has been
+deleted no longer exists; it is forgotten.
+
+ With the `clear' command you can delete breakpoints according to
+where they are in your program. With the `delete' command you can
+delete individual breakpoints or watchpoints by specifying their
+breakpoint numbers.
+
+ It is not necessary to delete a breakpoint to proceed past it. GDB
+automatically ignores breakpoints on the first instruction to be
+executed when you continue execution without changing the execution
+address.
+
+`clear'
+ Delete any breakpoints at the next instruction to be executed in
+ the selected stack frame (*note Selecting a frame: Selection.).
+ When the innermost frame is selected, this is a good way to delete
+ a breakpoint where your program just stopped.
+
+`clear FUNCTION'
+`clear FILENAME:FUNCTION'
+ Delete any breakpoints set at entry to the function FUNCTION.
+
+`clear LINENUM'
+`clear FILENAME:LINENUM'
+ Delete any breakpoints set at or within the code of the specified
+ line.
+
+`delete [breakpoints] [BNUMS...]'
+ Delete the breakpoints or watchpoints of the numbers specified as
+ arguments. If no argument is specified, delete all breakpoints
+ (GDB asks confirmation, unless you have `set confirm off'). You
+ can abbreviate this command as `d'.
+
+
+File: gdb.info, Node: Disabling, Next: Conditions, Prev: Delete Breaks, Up: Breakpoints
+
+Disabling breakpoints
+---------------------
+
+ Rather than deleting a breakpoint or watchpoint, you might prefer to
+"disable" it. This makes the breakpoint inoperative as if it had been
+deleted, but remembers the information on the breakpoint so that you
+can "enable" it again later.
+
+ You disable and enable breakpoints and watchpoints with the `enable'
+and `disable' commands, optionally specifying one or more breakpoint
+numbers as arguments. Use `info break' or `info watch' to print a list
+of breakpoints or watchpoints if you do not know which numbers to use.
+
+ A breakpoint or watchpoint can have any of four different states of
+enablement:
+
+ * Enabled. The breakpoint will stop your program. A breakpoint set
+ with the `break' command starts out in this state.
+
+ * Disabled. The breakpoint has no effect on your program.
+
+ * Enabled once. The breakpoint will stop your program, but when it
+ does so it will become disabled. A breakpoint set with the
+ `tbreak' command starts out in this state.
+
+ * Enabled for deletion. The breakpoint will stop your program, but
+ immediately after it does so it will be deleted permanently.
+
+ You can use the following commands to enable or disable breakpoints
+and watchpoints:
+
+`disable [breakpoints] [BNUMS...]'
+ Disable the specified breakpoints--or all breakpoints, if none are
+ listed. A disabled breakpoint has no effect but is not forgotten.
+ All options such as ignore-counts, conditions and commands are
+ remembered in case the breakpoint is enabled again later. You may
+ abbreviate `disable' as `dis'.
+
+`enable [breakpoints] [BNUMS...]'
+ Enable the specified breakpoints (or all defined breakpoints).
+ They become effective once again in stopping your program.
+
+`enable [breakpoints] once BNUMS...'
+ Enable the specified breakpoints temporarily. Each will be
+ disabled again the next time it stops your program.
+
+`enable [breakpoints] delete BNUMS...'
+ Enable the specified breakpoints to work once and then die. Each
+ of the breakpoints will be deleted the next time it stops your
+ program.
+
+ Save for a breakpoint set with `tbreak' (*note Setting breakpoints:
+Set Breaks.), breakpoints that you set are initially enabled;
+subsequently, they become disabled or enabled only when you use one of
+the commands above. (The command `until' can set and delete a
+breakpoint of its own, but it will not change the state of your other
+breakpoints; see *Note Continuing and stepping: Continuing and
+Stepping.)
+
+
+File: gdb.info, Node: Conditions, Next: Break Commands, Prev: Disabling, Up: Breakpoints
+
+Break conditions
+----------------
+
+ The simplest sort of breakpoint breaks every time your program
+reaches a specified place. You can also specify a "condition" for a
+breakpoint. A condition is just a Boolean expression in your
+programming language (*note Expressions: Expressions.). A breakpoint
+with a condition evaluates the expression each time your program
+reaches it, and your program stops only if the condition is *true*.
+
+ This is the converse of using assertions for program validation; in
+that situation, you want to stop when the assertion is violated--that
+is, when the condition is false. In C, if you want to test an
+assertion expressed by the condition ASSERT, you should set the
+condition `! ASSERT' on the appropriate breakpoint.
+
+ Conditions are also accepted for watchpoints; you may not need them,
+since a watchpoint is inspecting the value of an expression anyhow--but
+it might be simpler, say, to just set a watchpoint on a variable name,
+and specify a condition that tests whether the new value is an
+interesting one.
+
+ Break conditions can have side effects, and may even call functions
+in your program. This can be useful, for example, to activate functions
+that log program progress, or to use your own print functions to format
+special data structures. The effects are completely predictable unless
+there is another enabled breakpoint at the same address. (In that
+case, GDB might see the other breakpoint first and stop your program
+without checking the condition of this one.) Note that breakpoint
+commands are usually more convenient and flexible for the purpose of
+performing side effects when a breakpoint is reached (*note Breakpoint
+command lists: Break Commands.).
+
+ Break conditions can be specified when a breakpoint is set, by using
+`if' in the arguments to the `break' command. *Note Setting
+breakpoints: Set Breaks. They can also be changed at any time with the
+`condition' command. The `watch' command does not recognize the `if'
+keyword; `condition' is the only way to impose a further condition on a
+watchpoint.
+
+`condition BNUM EXPRESSION'
+ Specify EXPRESSION as the break condition for breakpoint or
+ watchpoint number BNUM. From now on, this breakpoint will stop
+ your program only if the value of EXPRESSION is true (nonzero, in
+ C). When you use `condition', GDB checks EXPRESSION immediately
+ for syntactic correctness, and to determine whether symbols in it
+ have referents in the context of your breakpoint. GDB does not
+ actually evaluate EXPRESSION at the time the `condition' command
+ is given, however. *Note Expressions: Expressions.
+
+`condition BNUM'
+ Remove the condition from breakpoint number BNUM. It becomes an
+ ordinary unconditional breakpoint.
+
+ A special case of a breakpoint condition is to stop only when the
+breakpoint has been reached a certain number of times. This is so
+useful that there is a special way to do it, using the "ignore count"
+of the breakpoint. Every breakpoint has an ignore count, which is an
+integer. Most of the time, the ignore count is zero, and therefore has
+no effect. But if your program reaches a breakpoint whose ignore count
+is positive, then instead of stopping, it just decrements the ignore
+count by one and continues. As a result, if the ignore count value is
+N, the breakpoint will not stop the next N times it is reached.
+
+`ignore BNUM COUNT'
+ Set the ignore count of breakpoint number BNUM to COUNT. The next
+ COUNT times the breakpoint is reached, your program's execution
+ will not stop; other than to decrement the ignore count, GDB takes
+ no action.
+
+ To make the breakpoint stop the next time it is reached, specify a
+ count of zero.
+
+ When you use `continue' to resume execution of your program from a
+ breakpoint, you can specify an ignore count directly as an
+ argument to `continue', rather than using `ignore'. *Note
+ Continuing and stepping: Continuing and Stepping.
+
+ If a breakpoint has a positive ignore count and a condition, the
+ condition is not checked. Once the ignore count reaches zero, the
+ condition will be checked.
+
+ You could achieve the effect of the ignore count with a condition
+ such as `$foo-- <= 0' using a debugger convenience variable that
+ is decremented each time. *Note Convenience variables:
+ Convenience Vars.
+
+
+File: gdb.info, Node: Break Commands, Next: Breakpoint Menus, Prev: Conditions, Up: Breakpoints
+
+Breakpoint command lists
+------------------------
+
+ You can give any breakpoint (or watchpoint) a series of commands to
+execute when your program stops due to that breakpoint. For example,
+you might want to print the values of certain expressions, or enable
+other breakpoints.
+
+`commands [BNUM]'
+`... COMMAND-LIST ...'
+`end'
+ Specify a list of commands for breakpoint number BNUM. The
+ commands themselves appear on the following lines. Type a line
+ containing just `end' to terminate the commands.
+
+ To remove all commands from a breakpoint, type `commands' and
+ follow it immediately with `end'; that is, give no commands.
+
+ With no BNUM argument, `commands' refers to the last breakpoint or
+ watchpoint set (not to the breakpoint most recently encountered).
+
+ Pressing RET as a means of repeating the last GDB command is
+disabled within a COMMAND-LIST.
+
+ You can use breakpoint commands to start your program up again.
+Simply use the `continue' command, or `step', or any other command that
+resumes execution.
+
+ Any other commands in the command list, after a command that resumes
+execution, are ignored. This is because any time you resume execution
+(even with a simple `next' or `step'), you may encounter another
+breakpoint--which could have its own command list, leading to
+ambiguities about which list to execute.
+
+ If the first command you specify in a command list is `silent', the
+usual message about stopping at a breakpoint is not printed. This may
+be desirable for breakpoints that are to print a specific message and
+then continue. If none of the remaining commands print anything, you
+will see no sign that the breakpoint was reached. `silent' is
+meaningful only at the beginning of a breakpoint command list.
+
+ The commands `echo', `output', and `printf' allow you to print
+precisely controlled output, and are often useful in silent
+breakpoints. *Note Commands for controlled output: Output.
+
+ For example, here is how you could use breakpoint commands to print
+the value of `x' at entry to `foo' whenever `x' is positive.
+
+ break foo if x>0
+ commands
+ silent
+ printf "x is %d\n",x
+ cont
+ end
+
+ One application for breakpoint commands is to compensate for one bug
+so you can test for another. Put a breakpoint just after the erroneous
+line of code, give it a condition to detect the case in which something
+erroneous has been done, and give it commands to assign correct values
+to any variables that need them. End with the `continue' command so
+that your program does not stop, and start with the `silent' command so
+that no output is produced. Here is an example:
+
+ break 403
+ commands
+ silent
+ set x = y + 4
+ cont
+ end
+
+
+File: gdb.info, Node: Breakpoint Menus, Next: Error in Breakpoints, Prev: Break Commands, Up: Breakpoints
+
+Breakpoint menus
+----------------
+
+ Some programming languages (notably C++) permit a single function
+name to be defined several times, for application in different contexts.
+This is called "overloading". When a function name is overloaded,
+`break FUNCTION' is not enough to tell GDB where you want a breakpoint.
+If you realize this will be a problem, you can use something like
+`break FUNCTION(TYPES)' to specify which particular version of the
+function you want. Otherwise, GDB offers you a menu of numbered
+choices for different possible breakpoints, and waits for your
+selection with the prompt `>'. The first two options are always `[0]
+cancel' and `[1] all'. Typing `1' sets a breakpoint at each definition
+of FUNCTION, and typing `0' aborts the `break' command without setting
+any new breakpoints.
+
+ For example, the following session excerpt shows an attempt to set a
+breakpoint at the overloaded symbol `String::after'. We choose three
+particular definitions of that function name:
+
+ (gdb) b String::after
+ [0] cancel
+ [1] all
+ [2] file:String.cc; line number:867
+ [3] file:String.cc; line number:860
+ [4] file:String.cc; line number:875
+ [5] file:String.cc; line number:853
+ [6] file:String.cc; line number:846
+ [7] file:String.cc; line number:735
+ > 2 4 6
+ Breakpoint 1 at 0xb26c: file String.cc, line 867.
+ Breakpoint 2 at 0xb344: file String.cc, line 875.
+ Breakpoint 3 at 0xafcc: file String.cc, line 846.
+ Multiple breakpoints were set.
+ Use the "delete" command to delete unwanted
+ breakpoints.
+ (gdb)
+
+
+File: gdb.info, Node: Error in Breakpoints, Prev: Breakpoint Menus, Up: Breakpoints
+
+"Cannot insert breakpoints"
+---------------------------
+
+ Under some operating systems, breakpoints cannot be used in a
+program if any other process is running that program. In this
+situation, attempting to run or continue a program with a breakpoint
+causes GDB to stop the other process.
+
+ When this happens, you have three ways to proceed:
+
+ 1. Remove or disable the breakpoints, then continue.
+
+ 2. Suspend GDB, and copy the file containing your program to a new
+ name. Resume GDB and use the `exec-file' command to specify that
+ GDB should run your program under that name. Then start your
+ program again.
+
+ 3. Relink your program so that the text segment is nonsharable, using
+ the linker option `-N'. The operating system limitation may not
+ apply to nonsharable executables.
+
+
+File: gdb.info, Node: Continuing and Stepping, Next: Signals, Prev: Breakpoints, Up: Stopping
+
+Continuing and stepping
+=======================
+
+ "Continuing" means resuming program execution until your program
+completes normally. In contrast, "stepping" means executing just one
+more "step" of your program, where "step" may mean either one line of
+source code, or one machine instruction (depending on what particular
+command you use). Either when continuing or when stepping, your
+program may stop even sooner, due to a breakpoint or a signal. (If due
+to a signal, you may want to use `handle', or use `signal 0' to resume
+execution. *Note Signals: Signals.)
+
+`continue [IGNORE-COUNT]'
+`c [IGNORE-COUNT]'
+`fg [IGNORE-COUNT]'
+ Resume program execution, at the address where your program last
+ stopped; any breakpoints set at that address are bypassed. The
+ optional argument IGNORE-COUNT allows you to specify a further
+ number of times to ignore a breakpoint at this location; its
+ effect is like that of `ignore' (*note Break conditions:
+ Conditions.).
+
+ The argument IGNORE-COUNT is meaningful only when your program
+ stopped due to a breakpoint. At other times, the argument to
+ `continue' is ignored.
+
+ The synonyms `c' and `fg' are provided purely for convenience, and
+ have exactly the same behavior as `continue'.
+
+ To resume execution at a different place, you can use `return'
+(*note Returning from a function: Returning.) to go back to the calling
+function; or `jump' (*note Continuing at a different address: Jumping.)
+to go to an arbitrary location in your program.
+
+ A typical technique for using stepping is to set a breakpoint (*note
+Breakpoints; watchpoints; and exceptions: Breakpoints.) at the
+beginning of the function or the section of your program where a
+problem is believed to lie, run your program until it stops at that
+breakpoint, and then step through the suspect area, examining the
+variables that are interesting, until you see the problem happen.
+
+`step'
+ Continue running your program until control reaches a different
+ source line, then stop it and return control to GDB. This command
+ is abbreviated `s'.
+
+ *Warning:* If you use the `step' command while control is
+ within a function that was compiled without debugging
+ information, execution proceeds until control reaches a
+ function that does have debugging information.
+
+`step COUNT'
+ Continue running as in `step', but do so COUNT times. If a
+ breakpoint is reached, or a signal not related to stepping occurs
+ before COUNT steps, stepping stops right away.
+
+`next [COUNT]'
+ Continue to the next source line in the current (innermost) stack
+ frame. Similar to `step', but any function calls appearing within
+ the line of code are executed without stopping. Execution stops
+ when control reaches a different line of code at the stack level
+ which was executing when the `next' command was given. This
+ command is abbreviated `n'.
+
+ An argument COUNT is a repeat count, as for `step'.
+
+ `next' within a function that lacks debugging information acts like
+ `step', but any function calls appearing within the code of the
+ function are executed without stopping.
+
+`finish'
+ Continue running until just after function in the selected stack
+ frame returns. Print the returned value (if any).
+
+ Contrast this with the `return' command (*note Returning from a
+ function: Returning.).
+
+`until'
+`u'
+ Continue running until a source line past the current line, in the
+ current stack frame, is reached. This command is used to avoid
+ single stepping through a loop more than once. It is like the
+ `next' command, except that when `until' encounters a jump, it
+ automatically continues execution until the program counter is
+ greater than the address of the jump.
+
+ This means that when you reach the end of a loop after single
+ stepping though it, `until' will cause your program to continue
+ execution until the loop is exited. In contrast, a `next' command
+ at the end of a loop will simply step back to the beginning of the
+ loop, which would force you to step through the next iteration.
+
+ `until' always stops your program if it attempts to exit the
+ current stack frame.
+
+ `until' may produce somewhat counterintuitive results if the order
+ of machine code does not match the order of the source lines. For
+ example, in the following excerpt from a debugging session, the `f'
+ (`frame') command shows that execution is stopped at line `206';
+ yet when we use `until', we get to line `195':
+
+ (gdb) f
+ #0 main (argc=4, argv=0xf7fffae8) at m4.c:206
+ 206 expand_input();
+ (gdb) until
+ 195 for ( ; argc > 0; NEXTARG) {
+
+ This happened because, for execution efficiency, the compiler had
+ generated code for the loop closure test at the end, rather than
+ the start, of the loop--even though the test in a C `for'-loop is
+ written before the body of the loop. The `until' command appeared
+ to step back to the beginning of the loop when it advanced to this
+ expression; however, it has not really gone to an earlier
+ statement--not in terms of the actual machine code.
+
+ `until' with no argument works by means of single instruction
+ stepping, and hence is slower than `until' with an argument.
+
+`until LOCATION'
+`u LOCATION'
+ Continue running your program until either the specified location
+ is reached, or the current stack frame returns. LOCATION is any of
+ the forms of argument acceptable to `break' (*note Setting
+ breakpoints: Set Breaks.). This form of the command uses
+ breakpoints, and hence is quicker than `until' without an argument.
+
+`stepi'
+`si'
+ Execute one machine instruction, then stop and return to the
+ debugger.
+
+ It is often useful to do `display/i $pc' when stepping by machine
+ instructions. This will cause the next instruction to be executed
+ to be displayed automatically at each stop. *Note Automatic
+ display: Auto Display.
+
+ An argument is a repeat count, as in `step'.
+
+`nexti'
+`ni'
+ Execute one machine instruction, but if it is a function call,
+ proceed until the function returns.
+
+ An argument is a repeat count, as in `next'.
+
+
+File: gdb.info, Node: Signals, Prev: Continuing and Stepping, Up: Stopping
+
+Signals
+=======
+
+ A signal is an asynchronous event that can happen in a program. The
+operating system defines the possible kinds of signals, and gives each
+kind a name and a number. For example, in Unix `SIGINT' is the signal
+a program gets when you type an interrupt (often `C-c'); `SIGSEGV' is
+the signal a program gets from referencing a place in memory far away
+from all the areas in use; `SIGALRM' occurs when the alarm clock timer
+goes off (which happens only if your program has requested an alarm).
+
+ Some signals, including `SIGALRM', are a normal part of the
+functioning of your program. Others, such as `SIGSEGV', indicate
+errors; these signals are "fatal" (kill your program immediately) if the
+program has not specified in advance some other way to handle the
+signal. `SIGINT' does not indicate an error in your program, but it is
+normally fatal so it can carry out the purpose of the interrupt: to
+kill the program.
+
+ GDB has the ability to detect any occurrence of a signal in your
+program. You can tell GDB in advance what to do for each kind of
+signal.
+
+ Normally, GDB is set up to ignore non-erroneous signals like
+`SIGALRM' (so as not to interfere with their role in the functioning of
+your program) but to stop your program immediately whenever an error
+signal happens. You can change these settings with the `handle'
+command.
+
+`info signals'
+ Print a table of all the kinds of signals and how GDB has been
+ told to handle each one. You can use this to see the signal
+ numbers of all the defined types of signals.
+
+`handle SIGNAL KEYWORDS...'
+ Change the way GDB handles signal SIGNAL. SIGNAL can be the
+ number of a signal or its name (with or without the `SIG' at the
+ beginning). The KEYWORDS say what change to make.
+
+ The keywords allowed by the `handle' command can be abbreviated.
+Their full names are:
+
+`nostop'
+ GDB should not stop your program when this signal happens. It may
+ still print a message telling you that the signal has come in.
+
+`stop'
+ GDB should stop your program when this signal happens. This
+ implies the `print' keyword as well.
+
+`print'
+ GDB should print a message when this signal happens.
+
+`noprint'
+ GDB should not mention the occurrence of the signal at all. This
+ implies the `nostop' keyword as well.
+
+`pass'
+ GDB should allow your program to see this signal; your program
+ will be able to handle the signal, or may be terminated if the
+ signal is fatal and not handled.
+
+`nopass'
+ GDB should not allow your program to see this signal.
+
+ When a signal stops your program, the signal is not visible until you
+continue. Your program will see the signal then, if `pass' is in
+effect for the signal in question *at that time*. In other words,
+after GDB reports a signal, you can use the `handle' command with
+`pass' or `nopass' to control whether that signal will be seen by your
+program when you later continue it.
+
+ You can also use the `signal' command to prevent your program from
+seeing a signal, or cause it to see a signal it normally would not see,
+or to give it any signal at any time. For example, if your program
+stopped due to some sort of memory reference error, you might store
+correct values into the erroneous variables and continue, hoping to see
+more execution; but your program would probably terminate immediately as
+a result of the fatal signal once it saw the signal. To prevent this,
+you can continue with `signal 0'. *Note Giving your program a signal:
+Signaling.
+
+
+File: gdb.info, Node: Stack, Next: Source, Prev: Stopping, Up: Top
+
+Examining the Stack
+*******************
+
+ When your program has stopped, the first thing you need to know is
+where it stopped and how it got there.
+
+ Each time your program performs a function call, the information
+about where in your program the call was made from is saved in a block
+of data called a "stack frame". The frame also contains the arguments
+of the call and the local variables of the function that was called.
+All the stack frames are allocated in a region of memory called the
+"call stack".
+
+ When your program stops, the GDB commands for examining the stack
+allow you to see all of this information.
+
+ One of the stack frames is "selected" by GDB and many GDB commands
+refer implicitly to the selected frame. In particular, whenever you
+ask GDB for the value of a variable in your program, the value is found
+in the selected frame. There are special GDB commands to select
+whichever frame you are interested in.
+
+ When your program stops, GDB automatically selects the currently
+executing frame and describes it briefly as the `frame' command does
+(*note Information about a frame: Frame Info.).
+
+* Menu:
+
+* Frames:: Stack frames
+* Backtrace:: Backtraces
+* Selection:: Selecting a frame
+* Frame Info:: Information on a frame
+
+* MIPS Stack:: MIPS machines and the function stack
+
+
+File: gdb.info, Node: Frames, Next: Backtrace, Up: Stack
+
+Stack frames
+============
+
+ The call stack is divided up into contiguous pieces called "stack
+frames", or "frames" for short; each frame is the data associated with
+one call to one function. The frame contains the arguments given to
+the function, the function's local variables, and the address at which
+the function is executing.
+
+ When your program is started, the stack has only one frame, that of
+the function `main'. This is called the "initial" frame or the
+"outermost" frame. Each time a function is called, a new frame is
+made. Each time a function returns, the frame for that function
+invocation is eliminated. If a function is recursive, there can be
+many frames for the same function. The frame for the function in which
+execution is actually occurring is called the "innermost" frame. This
+is the most recently created of all the stack frames that still exist.
+
+ Inside your program, stack frames are identified by their addresses.
+A stack frame consists of many bytes, each of which has its own
+address; each kind of computer has a convention for choosing one of
+those bytes whose address serves as the address of the frame. Usually
+this address is kept in a register called the "frame pointer register"
+while execution is going on in that frame.
+
+ GDB assigns numbers to all existing stack frames, starting with zero
+for the innermost frame, one for the frame that called it, and so on
+upward. These numbers do not really exist in your program; they are
+assigned by GDB to give you a way of designating stack frames in GDB
+commands.
+
+ Some compilers provide a way to compile functions so that they
+operate without stack frames. (For example, the `gcc' option
+`-fomit-frame-pointer' will generate functions without a frame.) This
+is occasionally done with heavily used library functions to save the
+frame setup time. GDB has limited facilities for dealing with these
+function invocations. If the innermost function invocation has no
+stack frame, GDB will nevertheless regard it as though it had a
+separate frame, which is numbered zero as usual, allowing correct
+tracing of the function call chain. However, GDB has no provision for
+frameless functions elsewhere in the stack.
+
+
+File: gdb.info, Node: Backtrace, Next: Selection, Prev: Frames, Up: Stack
+
+Backtraces
+==========
+
+ A backtrace is a summary of how your program got where it is. It
+shows one line per frame, for many frames, starting with the currently
+executing frame (frame zero), followed by its caller (frame one), and
+on up the stack.
+
+`backtrace'
+`bt'
+ Print a backtrace of the entire stack: one line per frame for all
+ frames in the stack.
+
+ You can stop the backtrace at any time by typing the system
+ interrupt character, normally `C-c'.
+
+`backtrace N'
+`bt N'
+ Similar, but print only the innermost N frames.
+
+`backtrace -N'
+`bt -N'
+ Similar, but print only the outermost N frames.
+
+ The names `where' and `info stack' (abbreviated `info s') are
+additional aliases for `backtrace'.
+
+ Each line in the backtrace shows the frame number and the function
+name. The program counter value is also shown--unless you use `set
+print address off'. The backtrace also shows the source file name and
+line number, as well as the arguments to the function. The program
+counter value is omitted if it is at the beginning of the code for that
+line number.
+
+ Here is an example of a backtrace. It was made with the command `bt
+3', so it shows the innermost three frames.
+
+ #0 m4_traceon (obs=0x24eb0, argc=1, argv=0x2b8c8)
+ at builtin.c:993
+ #1 0x6e38 in expand_macro (sym=0x2b600) at macro.c:242
+ #2 0x6840 in expand_token (obs=0x0, t=177664, td=0xf7fffb08)
+ at macro.c:71
+ (More stack frames follow...)
+
+The display for frame zero does not begin with a program counter value,
+indicating that your program has stopped at the beginning of the code
+for line `993' of `builtin.c'.
+
diff --git a/gnu/usr.bin/gdb/doc/gdb.info-3 b/gnu/usr.bin/gdb/doc/gdb.info-3
new file mode 100644
index 0000000..aea5862
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/gdb.info-3
@@ -0,0 +1,1264 @@
+This is Info file ./gdb.info, produced by Makeinfo-1.52 from the input
+file gdb.texinfo.
+
+START-INFO-DIR-ENTRY
+* Gdb:: The GNU debugger.
+END-INFO-DIR-ENTRY
+ This file documents the GNU debugger GDB.
+
+ This is Edition 4.09, August 1993, of `Debugging with GDB: the GNU
+Source-Level Debugger' for GDB Version 4.11.
+
+ Copyright (C) 1988, '89, '90, '91, '92, '93 Free Software
+Foundation, Inc.
+
+ Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of
+this manual under the conditions for verbatim copying, provided also
+that the entire resulting derived work is distributed under the terms
+of a permission notice identical to this one.
+
+ Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions.
+
+
+File: gdb.info, Node: Selection, Next: Frame Info, Prev: Backtrace, Up: Stack
+
+Selecting a frame
+=================
+
+ Most commands for examining the stack and other data in your program
+work on whichever stack frame is selected at the moment. Here are the
+commands for selecting a stack frame; all of them finish by printing a
+brief description of the stack frame just selected.
+
+`frame N'
+`f N'
+ Select frame number N. Recall that frame zero is the innermost
+ (currently executing) frame, frame one is the frame that called the
+ innermost one, and so on. The highest-numbered frame is the one
+ for `main'.
+
+`frame ADDR'
+`f ADDR'
+ Select the frame at address ADDR. This is useful mainly if the
+ chaining of stack frames has been damaged by a bug, making it
+ impossible for GDB to assign numbers properly to all frames. In
+ addition, this can be useful when your program has multiple stacks
+ and switches between them.
+
+ On the SPARC architecture, `frame' needs two addresses to select
+ an arbitrary frame: a frame pointer and a stack pointer.
+
+`up N'
+ Move N frames up the stack. For positive numbers N, this advances
+ toward the outermost frame, to higher frame numbers, to frames
+ that have existed longer. N defaults to one.
+
+`down N'
+ Move N frames down the stack. For positive numbers N, this
+ advances toward the innermost frame, to lower frame numbers, to
+ frames that were created more recently. N defaults to one. You
+ may abbreviate `down' as `do'.
+
+ All of these commands end by printing two lines of output describing
+the frame. The first line shows the frame number, the function name,
+the arguments, and the source file and line number of execution in that
+frame. The second line shows the text of that source line.
+
+ For example:
+ (gdb) up
+ #1 0x22f0 in main (argc=1, argv=0xf7fffbf4, env=0xf7fffbfc)
+ at env.c:10
+ 10 read_input_file (argv[i]);
+
+ After such a printout, the `list' command with no arguments will
+print ten lines centered on the point of execution in the frame. *Note
+Printing source lines: List.
+
+`up-silently N'
+`down-silently N'
+ These two commands are variants of `up' and `down', respectively;
+ they differ in that they do their work silently, without causing
+ display of the new frame. They are intended primarily for use in
+ GDB command scripts, where the output might be unnecessary and
+ distracting.
+
+
+File: gdb.info, Node: Frame Info, Next: MIPS Stack, Prev: Selection, Up: Stack
+
+Information about a frame
+=========================
+
+ There are several other commands to print information about the
+selected stack frame.
+
+`frame'
+`f'
+ When used without any argument, this command does not change which
+ frame is selected, but prints a brief description of the currently
+ selected stack frame. It can be abbreviated `f'. With an
+ argument, this command is used to select a stack frame. *Note
+ Selecting a frame: Selection.
+
+`info frame'
+`info f'
+ This command prints a verbose description of the selected stack
+ frame, including the address of the frame, the addresses of the
+ next frame down (called by this frame) and the next frame up
+ (caller of this frame), the language that the source code
+ corresponding to this frame was written in, the address of the
+ frame's arguments, the program counter saved in it (the address of
+ execution in the caller frame), and which registers were saved in
+ the frame. The verbose description is useful when something has
+ gone wrong that has made the stack format fail to fit the usual
+ conventions.
+
+`info frame ADDR'
+`info f ADDR'
+ Print a verbose description of the frame at address ADDR, without
+ selecting that frame. The selected frame remains unchanged by
+ this command.
+
+`info args'
+ Print the arguments of the selected frame, each on a separate line.
+
+`info locals'
+ Print the local variables of the selected frame, each on a separate
+ line. These are all variables (declared either static or
+ automatic) accessible at the point of execution of the selected
+ frame.
+
+`info catch'
+ Print a list of all the exception handlers that are active in the
+ current stack frame at the current point of execution. To see
+ other exception handlers, visit the associated frame (using the
+ `up', `down', or `frame' commands); then type `info catch'. *Note
+ Breakpoints and exceptions: Exception Handling.
+
+
+File: gdb.info, Node: MIPS Stack, Prev: Frame Info, Up: Stack
+
+MIPS machines and the function stack
+====================================
+
+ MIPS based computers use an unusual stack frame, which sometimes
+requires GDB to search backward in the object code to find the
+beginning of a function.
+
+ To improve response time (especially for embedded applications, where
+GDB may be restricted to a slow serial line for this search) you may
+want to limit the size of this search, using one of these commands:
+
+`set heuristic-fence-post LIMIT'
+ Restrict GDBN to examining at most LIMIT bytes in its search for
+ the beginning of a function. A value of `0' (the default) means
+ there is no limit.
+
+`show heuristic-fence-post'
+ Display the current limit.
+
+These commands are available *only* when GDB is configured for
+debugging programs on MIPS processors.
+
+
+File: gdb.info, Node: Source, Next: Data, Prev: Stack, Up: Top
+
+Examining Source Files
+**********************
+
+ GDB can print parts of your program's source, since the debugging
+information recorded in the program tells GDB what source files were
+used to build it. When your program stops, GDB spontaneously prints
+the line where it stopped. Likewise, when you select a stack frame
+(*note Selecting a frame: Selection.), GDB prints the line where
+execution in that frame has stopped. You can print other portions of
+source files by explicit command.
+
+ If you use GDB through its GNU Emacs interface, you may prefer to use
+Emacs facilities to view source; *note Using GDB under GNU Emacs:
+Emacs..
+
+* Menu:
+
+* List:: Printing source lines
+
+* Search:: Searching source files
+
+* Source Path:: Specifying source directories
+* Machine Code:: Source and machine code
+
+
+File: gdb.info, Node: List, Next: Search, Up: Source
+
+Printing source lines
+=====================
+
+ To print lines from a source file, use the `list' command
+(abbreviated `l'). There are several ways to specify what part of the
+file you want to print.
+
+ Here are the forms of the `list' command most commonly used:
+
+`list LINENUM'
+ Print lines centered around line number LINENUM in the current
+ source file.
+
+`list FUNCTION'
+ Print lines centered around the beginning of function FUNCTION.
+
+`list'
+ Print more lines. If the last lines printed were printed with a
+ `list' command, this prints lines following the last lines
+ printed; however, if the last line printed was a solitary line
+ printed as part of displaying a stack frame (*note Examining the
+ Stack: Stack.), this prints lines centered around that line.
+
+`list -'
+ Print lines just before the lines last printed.
+
+ By default, GDB prints ten source lines with any of these forms of
+the `list' command. You can change this using `set listsize':
+
+`set listsize COUNT'
+ Make the `list' command display COUNT source lines (unless the
+ `list' argument explicitly specifies some other number).
+
+`show listsize'
+ Display the number of lines that `list' will currently display by
+ default.
+
+ Repeating a `list' command with RET discards the argument, so it is
+equivalent to typing just `list'. This is more useful than listing the
+same lines again. An exception is made for an argument of `-'; that
+argument is preserved in repetition so that each repetition moves up in
+the source file.
+
+ In general, the `list' command expects you to supply zero, one or two
+"linespecs". Linespecs specify source lines; there are several ways of
+writing them but the effect is always to specify some source line.
+Here is a complete description of the possible arguments for `list':
+
+`list LINESPEC'
+ Print lines centered around the line specified by LINESPEC.
+
+`list FIRST,LAST'
+ Print lines from FIRST to LAST. Both arguments are linespecs.
+
+`list ,LAST'
+ Print lines ending with LAST.
+
+`list FIRST,'
+ Print lines starting with FIRST.
+
+`list +'
+ Print lines just after the lines last printed.
+
+`list -'
+ Print lines just before the lines last printed.
+
+`list'
+ As described in the preceding table.
+
+ Here are the ways of specifying a single source line--all the kinds
+of linespec.
+
+`NUMBER'
+ Specifies line NUMBER of the current source file. When a `list'
+ command has two linespecs, this refers to the same source file as
+ the first linespec.
+
+`+OFFSET'
+ Specifies the line OFFSET lines after the last line printed. When
+ used as the second linespec in a `list' command that has two, this
+ specifies the line OFFSET lines down from the first linespec.
+
+`-OFFSET'
+ Specifies the line OFFSET lines before the last line printed.
+
+`FILENAME:NUMBER'
+ Specifies line NUMBER in the source file FILENAME.
+
+`FUNCTION'
+ Specifies the line of the open-brace that begins the body of the
+ function FUNCTION.
+
+`FILENAME:FUNCTION'
+ Specifies the line of the open-brace that begins the body of the
+ function FUNCTION in the file FILENAME. You only need the file
+ name with a function name to avoid ambiguity when there are
+ identically named functions in different source files.
+
+`*ADDRESS'
+ Specifies the line containing the program address ADDRESS.
+ ADDRESS may be any expression.
+
+
+File: gdb.info, Node: Search, Next: Source Path, Prev: List, Up: Source
+
+Searching source files
+======================
+
+ There are two commands for searching through the current source file
+for a regular expression.
+
+`forward-search REGEXP'
+`search REGEXP'
+ The command `forward-search REGEXP' checks each line, starting
+ with the one following the last line listed, for a match for
+ REGEXP. It lists the line that is found. You can use synonym
+ `search REGEXP' or abbreviate the command name as `fo'.
+
+`reverse-search REGEXP'
+ The command `reverse-search REGEXP' checks each line, starting
+ with the one before the last line listed and going backward, for a
+ match for REGEXP. It lists the line that is found. You can
+ abbreviate this command as `rev'.
+
+
+File: gdb.info, Node: Source Path, Next: Machine Code, Prev: Search, Up: Source
+
+Specifying source directories
+=============================
+
+ Executable programs sometimes do not record the directories of the
+source files from which they were compiled, just the names. Even when
+they do, the directories could be moved between the compilation and
+your debugging session. GDB has a list of directories to search for
+source files; this is called the "source path". Each time GDB wants a
+source file, it tries all the directories in the list, in the order
+they are present in the list, until it finds a file with the desired
+name. Note that the executable search path is *not* used for this
+purpose. Neither is the current working directory, unless it happens
+to be in the source path.
+
+ If GDB cannot find a source file in the source path, and the object
+program records a directory, GDB tries that directory too. If the
+source path is empty, and there is no record of the compilation
+directory, GDB will, as a last resort, look in the current directory.
+
+ Whenever you reset or rearrange the source path, GDB will clear out
+any information it has cached about where source files are found, where
+each line is in the file, etc.
+
+ When you start GDB, its source path is empty. To add other
+directories, use the `directory' command.
+
+`directory DIRNAME ...'
+ Add directory DIRNAME to the front of the source path. Several
+ directory names may be given to this command, separated by `:' or
+ whitespace. You may specify a directory that is already in the
+ source path; this moves it forward, so it will be searched sooner.
+
+ You can use the string `$cdir' to refer to the compilation
+ directory (if one is recorded), and `$cwd' to refer to the current
+ working directory. `$cwd' is not the same as `.'--the former
+ tracks the current working directory as it changes during your GDB
+ session, while the latter is immediately expanded to the current
+ directory at the time you add an entry to the source path.
+
+`directory'
+ Reset the source path to empty again. This requires confirmation.
+
+`show directories'
+ Print the source path: show which directories it contains.
+
+ If your source path is cluttered with directories that are no longer
+of interest, GDB may sometimes cause confusion by finding the wrong
+versions of source. You can correct the situation as follows:
+
+ 1. Use `directory' with no argument to reset the source path to empty.
+
+ 2. Use `directory' with suitable arguments to reinstall the
+ directories you want in the source path. You can add all the
+ directories in one command.
+
+
+File: gdb.info, Node: Machine Code, Prev: Source Path, Up: Source
+
+Source and machine code
+=======================
+
+ You can use the command `info line' to map source lines to program
+addresses (and vice versa), and the command `disassemble' to display a
+range of addresses as machine instructions.
+
+`info line LINESPEC'
+ Print the starting and ending addresses of the compiled code for
+ source line LINESPEC. You can specify source lines in any of the
+ ways understood by the `list' command (*note Printing source
+ lines: List.).
+
+ For example, we can use `info line' to discover the location of the
+object code for the first line of function `m4_changequote':
+
+ (gdb) info line m4_changecom
+ Line 895 of "builtin.c" starts at pc 0x634c and ends at 0x6350.
+
+We can also inquire (using `*ADDR' as the form for LINESPEC) what
+source line covers a particular address:
+ (gdb) info line *0x63ff
+ Line 926 of "builtin.c" starts at pc 0x63e4 and ends at 0x6404.
+
+ After `info line', the default address for the `x' command is
+changed to the starting address of the line, so that `x/i' is
+sufficient to begin examining the machine code (*note Examining memory:
+Memory.). Also, this address is saved as the value of the convenience
+variable `$_' (*note Convenience variables: Convenience Vars.).
+
+`disassemble'
+ This specialized command dumps a range of memory as machine
+ instructions. The default memory range is the function
+ surrounding the program counter of the selected frame. A single
+ argument to this command is a program counter value; the function
+ surrounding this value will be dumped. Two arguments specify a
+ range of addresses (first inclusive, second exclusive) to dump.
+
+ We can use `disassemble' to inspect the object code range shown in
+the last `info line' example (the example shows SPARC machine
+instructions):
+
+ (gdb) disas 0x63e4 0x6404
+ Dump of assembler code from 0x63e4 to 0x6404:
+ 0x63e4 <builtin_init+5340>: ble 0x63f8 <builtin_init+5360>
+ 0x63e8 <builtin_init+5344>: sethi %hi(0x4c00), %o0
+ 0x63ec <builtin_init+5348>: ld [%i1+4], %o0
+ 0x63f0 <builtin_init+5352>: b 0x63fc <builtin_init+5364>
+ 0x63f4 <builtin_init+5356>: ld [%o0+4], %o0
+ 0x63f8 <builtin_init+5360>: or %o0, 0x1a4, %o0
+ 0x63fc <builtin_init+5364>: call 0x9288 <path_search>
+ 0x6400 <builtin_init+5368>: nop
+ End of assembler dump.
+
+
+File: gdb.info, Node: Data, Next: Languages, Prev: Source, Up: Top
+
+Examining Data
+**************
+
+ The usual way to examine data in your program is with the `print'
+command (abbreviated `p'), or its synonym `inspect'. It evaluates and
+prints the value of an expression of the language your program is
+written in (*note Using GDB with Different Languages: Languages.).
+
+`print EXP'
+`print /F EXP'
+ EXP is an expression (in the source language). By default the
+ value of EXP is printed in a format appropriate to its data type;
+ you can choose a different format by specifying `/F', where F is a
+ letter specifying the format; *note Output formats: Output
+ Formats..
+
+`print'
+`print /F'
+ If you omit EXP, GDB displays the last value again (from the
+ "value history"; *note Value history: Value History.). This
+ allows you to conveniently inspect the same value in an
+ alternative format.
+
+ A more low-level way of examining data is with the `x' command. It
+examines data in memory at a specified address and prints it in a
+specified format. *Note Examining memory: Memory.
+
+ If you are interested in information about types, or about how the
+fields of a struct or class are declared, use the `ptype EXP' command
+rather than `print'. *Note Examining the Symbol Table: Symbols.
+
+* Menu:
+
+* Expressions:: Expressions
+* Variables:: Program variables
+* Arrays:: Artificial arrays
+* Output Formats:: Output formats
+* Memory:: Examining memory
+* Auto Display:: Automatic display
+* Print Settings:: Print settings
+* Value History:: Value history
+* Convenience Vars:: Convenience variables
+* Registers:: Registers
+
+* Floating Point Hardware:: Floating point hardware
+
+
+File: gdb.info, Node: Expressions, Next: Variables, Up: Data
+
+Expressions
+===========
+
+ `print' and many other GDB commands accept an expression and compute
+its value. Any kind of constant, variable or operator defined by the
+programming language you are using is valid in an expression in GDB.
+This includes conditional expressions, function calls, casts and string
+constants. It unfortunately does not include symbols defined by
+preprocessor `#define' commands.
+
+ Because C is so widespread, most of the expressions shown in
+examples in this manual are in C. *Note Using GDB with Different
+Languages: Languages, for information on how to use expressions in other
+languages.
+
+ In this section, we discuss operators that you can use in GDB
+expressions regardless of your programming language.
+
+ Casts are supported in all languages, not just in C, because it is so
+useful to cast a number into a pointer so as to examine a structure at
+that address in memory.
+
+ GDB supports these operators in addition to those of programming
+languages:
+
+`@'
+ `@' is a binary operator for treating parts of memory as arrays.
+ *Note Artificial arrays: Arrays, for more information.
+
+`::'
+ `::' allows you to specify a variable in terms of the file or
+ function where it is defined. *Note Program variables: Variables.
+
+`{TYPE} ADDR'
+ Refers to an object of type TYPE stored at address ADDR in memory.
+ ADDR may be any expression whose value is an integer or pointer
+ (but parentheses are required around binary operators, just as in
+ a cast). This construct is allowed regardless of what kind of
+ data is normally supposed to reside at ADDR.
+
+
+File: gdb.info, Node: Variables, Next: Arrays, Prev: Expressions, Up: Data
+
+Program variables
+=================
+
+ The most common kind of expression to use is the name of a variable
+in your program.
+
+ Variables in expressions are understood in the selected stack frame
+(*note Selecting a frame: Selection.); they must either be global (or
+static) or be visible according to the scope rules of the programming
+language from the point of execution in that frame. This means that in
+the function
+
+ foo (a)
+ int a;
+ {
+ bar (a);
+ {
+ int b = test ();
+ bar (b);
+ }
+ }
+
+you can examine and use the variable `a' whenever your program is
+executing within the function `foo', but you can only use or examine
+the variable `b' while your program is executing inside the block where
+`b' is declared.
+
+ There is an exception: you can refer to a variable or function whose
+scope is a single source file even if the current execution point is not
+in this file. But it is possible to have more than one such variable or
+function with the same name (in different source files). If that
+happens, referring to that name has unpredictable effects. If you wish,
+you can specify a static variable in a particular function or file,
+using the colon-colon notation:
+
+ FILE::VARIABLE
+ FUNCTION::VARIABLE
+
+Here FILE or FUNCTION is the name of the context for the static
+VARIABLE. In the case of file names, you can use quotes to make sure
+GDB parses the file name as a single word--for example, to print a
+global value of `x' defined in `f2.c':
+
+ (gdb) p 'f2.c'::x
+
+ This use of `::' is very rarely in conflict with the very similar
+use of the same notation in C++. GDB also supports use of the C++
+scope resolution operator in GDB expressions.
+
+ *Warning:* Occasionally, a local variable may appear to have the
+ wrong value at certain points in a function--just after entry to a
+ new scope, and just before exit.
+ You may see this problem when you are stepping by machine
+instructions. This is because on most machines, it takes more than one
+instruction to set up a stack frame (including local variable
+definitions); if you are stepping by machine instructions, variables
+may appear to have the wrong values until the stack frame is completely
+built. On exit, it usually also takes more than one machine
+instruction to destroy a stack frame; after you begin stepping through
+that group of instructions, local variable definitions may be gone.
+
+
+File: gdb.info, Node: Arrays, Next: Output Formats, Prev: Variables, Up: Data
+
+Artificial arrays
+=================
+
+ It is often useful to print out several successive objects of the
+same type in memory; a section of an array, or an array of dynamically
+determined size for which only a pointer exists in the program.
+
+ You can do this by referring to a contiguous span of memory as an
+"artificial array", using the binary operator `@'. The left operand of
+`@' should be the first element of the desired array, as an individual
+object. The right operand should be the desired length of the array.
+The result is an array value whose elements are all of the type of the
+left argument. The first element is actually the left argument; the
+second element comes from bytes of memory immediately following those
+that hold the first element, and so on. Here is an example. If a
+program says
+
+ int *array = (int *) malloc (len * sizeof (int));
+
+you can print the contents of `array' with
+
+ p *array@len
+
+ The left operand of `@' must reside in memory. Array values made
+with `@' in this way behave just like other arrays in terms of
+subscripting, and are coerced to pointers when used in expressions.
+Artificial arrays most often appear in expressions via the value history
+(*note Value history: Value History.), after printing one out.
+
+ Sometimes the artificial array mechanism is not quite enough; in
+moderately complex data structures, the elements of interest may not
+actually be adjacent--for example, if you are interested in the values
+of pointers in an array. One useful work-around in this situation is
+to use a convenience variable (*note Convenience variables: Convenience
+Vars.) as a counter in an expression that prints the first interesting
+value, and then repeat that expression via RET. For instance, suppose
+you have an array `dtab' of pointers to structures, and you are
+interested in the values of a field `fv' in each structure. Here is an
+example of what you might type:
+
+ set $i = 0
+ p dtab[$i++]->fv
+ RET
+ RET
+ ...
+
+
+File: gdb.info, Node: Output Formats, Next: Memory, Prev: Arrays, Up: Data
+
+Output formats
+==============
+
+ By default, GDB prints a value according to its data type. Sometimes
+this is not what you want. For example, you might want to print a
+number in hex, or a pointer in decimal. Or you might want to view data
+in memory at a certain address as a character string or as an
+instruction. To do these things, specify an "output format" when you
+print a value.
+
+ The simplest use of output formats is to say how to print a value
+already computed. This is done by starting the arguments of the
+`print' command with a slash and a format letter. The format letters
+supported are:
+
+`x'
+ Regard the bits of the value as an integer, and print the integer
+ in hexadecimal.
+
+`d'
+ Print as integer in signed decimal.
+
+`u'
+ Print as integer in unsigned decimal.
+
+`o'
+ Print as integer in octal.
+
+`t'
+ Print as integer in binary. The letter `t' stands for "two". (1)
+
+`a'
+ Print as an address, both absolute in hex and as an offset from the
+ nearest preceding symbol. This format can be used to discover
+ where (in what function) an unknown address is located:
+
+ (gdb) p/a 0x54320
+ $3 = 0x54320 <_initialize_vx+396>
+
+`c'
+ Regard as an integer and print it as a character constant.
+
+`f'
+ Regard the bits of the value as a floating point number and print
+ using typical floating point syntax.
+
+ For example, to print the program counter in hex (*note
+Registers::.), type
+
+ p/x $pc
+
+Note that no space is required before the slash; this is because command
+names in GDB cannot contain a slash.
+
+ To reprint the last value in the value history with a different
+format, you can use the `print' command with just a format and no
+expression. For example, `p/x' reprints the last value in hex.
+
+ ---------- Footnotes ----------
+
+ (1) `b' cannot be used because these format letters are also used
+with the `x' command, where `b' stands for "byte"; *note Examining
+memory: Memory..
+
+
+File: gdb.info, Node: Memory, Next: Auto Display, Prev: Output Formats, Up: Data
+
+Examining memory
+================
+
+ You can use the command `x' (for "examine") to examine memory in any
+of several formats, independently of your program's data types.
+
+`x/NFU ADDR'
+`x ADDR'
+`x'
+ Use the `x' command to examine memory.
+
+ N, F, and U are all optional parameters that specify how much memory
+to display and how to format it; ADDR is an expression giving the
+address where you want to start displaying memory. If you use defaults
+for NFU, you need not type the slash `/'. Several commands set
+convenient defaults for ADDR.
+
+N, the repeat count
+ The repeat count is a decimal integer; the default is 1. It
+ specifies how much memory (counting by units U) to display.
+
+F, the display format
+ The display format is one of the formats used by `print', or `s'
+ (null-terminated string) or `i' (machine instruction). The
+ default is `x' (hexadecimal) initially, or the format from the
+ last time you used either `x' or `print'.
+
+U, the unit size
+ The unit size is any of
+
+ `b'
+ Bytes.
+
+ `h'
+ Halfwords (two bytes).
+
+ `w'
+ Words (four bytes). This is the initial default.
+
+ `g'
+ Giant words (eight bytes).
+
+ Each time you specify a unit size with `x', that size becomes the
+ default unit the next time you use `x'. (For the `s' and `i'
+ formats, the unit size is ignored and is normally not written.)
+
+ADDR, starting display address
+ ADDR is the address where you want GDB to begin displaying memory.
+ The expression need not have a pointer value (though it may); it
+ is always interpreted as an integer address of a byte of memory.
+ *Note Expressions: Expressions, for more information on
+ expressions. The default for ADDR is usually just after the last
+ address examined--but several other commands also set the default
+ address: `info breakpoints' (to the address of the last breakpoint
+ listed), `info line' (to the starting address of a line), and
+ `print' (if you use it to display a value from memory).
+
+ For example, `x/3uh 0x54320' is a request to display three halfwords
+(`h') of memory, formatted as unsigned decimal integers (`u'), starting
+at address `0x54320'. `x/4xw $sp' prints the four words (`w') of
+memory above the stack pointer (here, `$sp'; *note Registers::.) in
+hexadecimal (`x').
+
+ Since the letters indicating unit sizes are all distinct from the
+letters specifying output formats, you do not have to remember whether
+unit size or format comes first; either order will work. The output
+specifications `4xw' and `4wx' mean exactly the same thing. (However,
+the count N must come first; `wx4' will not work.)
+
+ Even though the unit size U is ignored for the formats `s' and `i',
+you might still want to use a count N; for example, `3i' specifies that
+you want to see three machine instructions, including any operands.
+The command `disassemble' gives an alternative way of inspecting
+machine instructions; *note Source and machine code: Machine Code..
+
+ All the defaults for the arguments to `x' are designed to make it
+easy to continue scanning memory with minimal specifications each time
+you use `x'. For example, after you have inspected three machine
+instructions with `x/3i ADDR', you can inspect the next seven with just
+`x/7'. If you use RET to repeat the `x' command, the repeat count N is
+used again; the other arguments default as for successive uses of `x'.
+
+ The addresses and contents printed by the `x' command are not saved
+in the value history because there is often too much of them and they
+would get in the way. Instead, GDB makes these values available for
+subsequent use in expressions as values of the convenience variables
+`$_' and `$__'. After an `x' command, the last address examined is
+available for use in expressions in the convenience variable `$_'. The
+contents of that address, as examined, are available in the convenience
+variable `$__'.
+
+ If the `x' command has a repeat count, the address and contents saved
+are from the last memory unit printed; this is not the same as the last
+address printed if several units were printed on the last line of
+output.
+
+
+File: gdb.info, Node: Auto Display, Next: Print Settings, Prev: Memory, Up: Data
+
+Automatic display
+=================
+
+ If you find that you want to print the value of an expression
+frequently (to see how it changes), you might want to add it to the
+"automatic display list" so that GDB will print its value each time
+your program stops. Each expression added to the list is given a
+number to identify it; to remove an expression from the list, you
+specify that number. The automatic display looks like this:
+
+ 2: foo = 38
+ 3: bar[5] = (struct hack *) 0x3804
+
+This display shows item numbers, expressions and their current values.
+As with displays you request manually using `x' or `print', you can
+specify the output format you prefer; in fact, `display' decides
+whether to use `print' or `x' depending on how elaborate your format
+specification is--it uses `x' if you specify a unit size, or one of the
+two formats (`i' and `s') that are only supported by `x'; otherwise it
+uses `print'.
+
+`display EXP'
+ Add the expression EXP to the list of expressions to display each
+ time your program stops. *Note Expressions: Expressions.
+
+ `display' will not repeat if you press RET again after using it.
+
+`display/FMT EXP'
+ For FMT specifying only a display format and not a size or count,
+ add the expression EXP to the auto-display list but arrange to
+ display it each time in the specified format FMT. *Note Output
+ formats: Output Formats.
+
+`display/FMT ADDR'
+ For FMT `i' or `s', or including a unit-size or a number of units,
+ add the expression ADDR as a memory address to be examined each
+ time your program stops. Examining means in effect doing `x/FMT
+ ADDR'. *Note Examining memory: Memory.
+
+ For example, `display/i $pc' can be helpful, to see the machine
+instruction about to be executed each time execution stops (`$pc' is a
+common name for the program counter; *note Registers::.).
+
+`undisplay DNUMS...'
+`delete display DNUMS...'
+ Remove item numbers DNUMS from the list of expressions to display.
+
+ `undisplay' will not repeat if you press RET after using it.
+ (Otherwise you would just get the error `No display number ...'.)
+
+`disable display DNUMS...'
+ Disable the display of item numbers DNUMS. A disabled display
+ item is not printed automatically, but is not forgotten. It may be
+ enabled again later.
+
+`enable display DNUMS...'
+ Enable display of item numbers DNUMS. It becomes effective once
+ again in auto display of its expression, until you specify
+ otherwise.
+
+`display'
+ Display the current values of the expressions on the list, just as
+ is done when your program stops.
+
+`info display'
+ Print the list of expressions previously set up to display
+ automatically, each one with its item number, but without showing
+ the values. This includes disabled expressions, which are marked
+ as such. It also includes expressions which would not be
+ displayed right now because they refer to automatic variables not
+ currently available.
+
+ If a display expression refers to local variables, then it does not
+make sense outside the lexical context for which it was set up. Such an
+expression is disabled when execution enters a context where one of its
+variables is not defined. For example, if you give the command
+`display last_char' while inside a function with an argument
+`last_char', then this argument will be displayed while your program
+continues to stop inside that function. When it stops elsewhere--where
+there is no variable `last_char'--display is disabled. The next time
+your program stops where `last_char' is meaningful, you can enable the
+display expression once again.
+
+
+File: gdb.info, Node: Print Settings, Next: Value History, Prev: Auto Display, Up: Data
+
+Print settings
+==============
+
+ GDB provides the following ways to control how arrays, structures,
+and symbols are printed.
+
+These settings are useful for debugging programs in any language:
+
+`set print address'
+`set print address on'
+ GDB will print memory addresses showing the location of stack
+ traces, structure values, pointer values, breakpoints, and so
+ forth, even when it also displays the contents of those addresses.
+ The default is on. For example, this is what a stack frame
+ display looks like, with `set print address on':
+
+ (gdb) f
+ #0 set_quotes (lq=0x34c78 "<<", rq=0x34c88 ">>")
+ at input.c:530
+ 530 if (lquote != def_lquote)
+
+`set print address off'
+ Do not print addresses when displaying their contents. For
+ example, this is the same stack frame displayed with `set print
+ address off':
+
+ (gdb) set print addr off
+ (gdb) f
+ #0 set_quotes (lq="<<", rq=">>") at input.c:530
+ 530 if (lquote != def_lquote)
+
+ You can use `set print address off' to eliminate all machine
+ dependent displays from the GDB interface. For example, with
+ `print address off', you should get the same text for backtraces on
+ all machines--whether or not they involve pointer arguments.
+
+`show print address'
+ Show whether or not addresses are to be printed.
+
+ When GDB prints a symbolic address, it normally prints the closest
+earlier symbol plus an offset. If that symbol does not uniquely
+identify the address (for example, it is a name whose scope is a single
+source file), you may need to disambiguate. One way to do this is with
+`info line', for example `info line *0x4537'. Alternately, you can set
+GDB to print the source file and line number when it prints a symbolic
+address:
+
+`set print symbol-filename on'
+ Tell GDB to print the source file name and line number of a symbol
+ in the symbolic form of an address.
+
+`set print symbol-filename off'
+ Do not print source file name and line number of a symbol. This
+ is the default.
+
+`show print symbol-filename'
+ Show whether or not GDB will print the source file name and line
+ number of a symbol in the symbolic form of an address.
+
+ Also, you may wish to see the symbolic form only if the address being
+printed is reasonably close to the closest earlier symbol:
+
+`set print max-symbolic-offset MAX-OFFSET'
+ Tell GDB to only display the symbolic form of an address if the
+ offset between the closest earlier symbol and the address is less
+ than MAX-OFFSET. The default is 0, which means to always print the
+ symbolic form of an address, if any symbol precedes it.
+
+`show print max-symbolic-offset'
+ Ask how large the maximum offset is that GDB will print in a
+ symbolic address.
+
+`set print array'
+`set print array on'
+ GDB will pretty-print arrays. This format is more convenient to
+ read, but uses more space. The default is off.
+
+`set print array off'
+ Return to compressed format for arrays.
+
+`show print array'
+ Show whether compressed or pretty format is selected for displaying
+ arrays.
+
+`set print elements NUMBER-OF-ELEMENTS'
+ If GDB is printing a large array, it will stop printing after it
+ has printed the number of elements set by the `set print elements'
+ command. This limit also applies to the display of strings.
+ Setting the number of elements to zero means that the printing is
+ unlimited.
+
+`show print elements'
+ Display the number of elements of a large array that GDB will print
+ before losing patience.
+
+`set print pretty on'
+ Cause GDB to print structures in an indented format with one
+ member per line, like this:
+
+ $1 = {
+ next = 0x0,
+ flags = {
+ sweet = 1,
+ sour = 1
+ },
+ meat = 0x54 "Pork"
+ }
+
+`set print pretty off'
+ Cause GDB to print structures in a compact format, like this:
+
+ $1 = {next = 0x0, flags = {sweet = 1, sour = 1}, \
+ meat = 0x54 "Pork"}
+
+ This is the default format.
+
+`show print pretty'
+ Show which format GDB will use to print structures.
+
+`set print sevenbit-strings on'
+ Print using only seven-bit characters; if this option is set, GDB
+ will display any eight-bit characters (in strings or character
+ values) using the notation `\'NNN. For example, `M-a' is
+ displayed as `\341'.
+
+`set print sevenbit-strings off'
+ Print using either seven-bit or eight-bit characters, as required.
+ This is the default.
+
+`show print sevenbit-strings'
+ Show whether or not GDB will print only seven-bit characters.
+
+`set print union on'
+ Tell GDB to print unions which are contained in structures. This
+ is the default setting.
+
+`set print union off'
+ Tell GDB not to print unions which are contained in structures.
+
+`show print union'
+ Ask GDB whether or not it will print unions which are contained in
+ structures.
+
+ For example, given the declarations
+
+ typedef enum {Tree, Bug} Species;
+ typedef enum {Big_tree, Acorn, Seedling} Tree_forms;
+ typedef enum {Caterpillar, Cocoon, Butterfly}
+ Bug_forms;
+
+ struct thing {
+ Species it;
+ union {
+ Tree_forms tree;
+ Bug_forms bug;
+ } form;
+ };
+
+ struct thing foo = {Tree, {Acorn}};
+
+ with `set print union on' in effect `p foo' would print
+
+ $1 = {it = Tree, form = {tree = Acorn, bug = Cocoon}}
+
+ and with `set print union off' in effect it would print
+
+ $1 = {it = Tree, form = {...}}
+
+These settings are of interest when debugging C++ programs:
+
+`set print demangle'
+`set print demangle on'
+ Print C++ names in their source form rather than in the encoded
+ ("mangled") form passed to the assembler and linker for type-safe
+ linkage. The default is `on'.
+
+`show print demangle'
+ Show whether C++ names will be printed in mangled or demangled
+ form.
+
+`set print asm-demangle'
+`set print asm-demangle on'
+ Print C++ names in their source form rather than their mangled
+ form, even in assembler code printouts such as instruction
+ disassemblies. The default is off.
+
+`show print asm-demangle'
+ Show whether C++ names in assembly listings will be printed in
+ mangled or demangled form.
+
+`set demangle-style STYLE'
+ Choose among several encoding schemes used by different compilers
+ to represent C++ names. The choices for STYLE are currently:
+
+ `auto'
+ Allow GDB to choose a decoding style by inspecting your
+ program.
+
+ `gnu'
+ Decode based on the GNU C++ compiler (`g++') encoding
+ algorithm.
+
+ `lucid'
+ Decode based on the Lucid C++ compiler (`lcc') encoding
+ algorithm.
+
+ `arm'
+ Decode using the algorithm in the `C++ Annotated Reference
+ Manual'. *Warning:* this setting alone is not sufficient to
+ allow debugging `cfront'-generated executables. GDB would
+ require further enhancement to permit that.
+
+`show demangle-style'
+ Display the encoding style currently in use for decoding C++
+ symbols.
+
+`set print object'
+`set print object on'
+ When displaying a pointer to an object, identify the *actual*
+ (derived) type of the object rather than the *declared* type, using
+ the virtual function table.
+
+`set print object off'
+ Display only the declared type of objects, without reference to the
+ virtual function table. This is the default setting.
+
+`show print object'
+ Show whether actual, or declared, object types will be displayed.
+
+`set print vtbl'
+`set print vtbl on'
+ Pretty print C++ virtual function tables. The default is off.
+
+`set print vtbl off'
+ Do not pretty print C++ virtual function tables.
+
+`show print vtbl'
+ Show whether C++ virtual function tables are pretty printed, or
+ not.
+
+
+File: gdb.info, Node: Value History, Next: Convenience Vars, Prev: Print Settings, Up: Data
+
+Value history
+=============
+
+ Values printed by the `print' command are saved in the GDB "value
+history" so that you can refer to them in other expressions. Values are
+kept until the symbol table is re-read or discarded (for example with
+the `file' or `symbol-file' commands). When the symbol table changes,
+the value history is discarded, since the values may contain pointers
+back to the types defined in the symbol table.
+
+ The values printed are given "history numbers" by which you can
+refer to them. These are successive integers starting with one.
+`print' shows you the history number assigned to a value by printing
+`$NUM = ' before the value; here NUM is the history number.
+
+ To refer to any previous value, use `$' followed by the value's
+history number. The way `print' labels its output is designed to
+remind you of this. Just `$' refers to the most recent value in the
+history, and `$$' refers to the value before that. `$$N' refers to the
+Nth value from the end; `$$2' is the value just prior to `$$', `$$1' is
+equivalent to `$$', and `$$0' is equivalent to `$'.
+
+ For example, suppose you have just printed a pointer to a structure
+and want to see the contents of the structure. It suffices to type
+
+ p *$
+
+ If you have a chain of structures where the component `next' points
+to the next one, you can print the contents of the next one with this:
+
+ p *$.next
+
+You can print successive links in the chain by repeating this
+command--which you can do by just typing RET.
+
+ Note that the history records values, not expressions. If the value
+of `x' is 4 and you type these commands:
+
+ print x
+ set x=5
+
+then the value recorded in the value history by the `print' command
+remains 4 even though the value of `x' has changed.
+
+`show values'
+ Print the last ten values in the value history, with their item
+ numbers. This is like `p $$9' repeated ten times, except that
+ `show values' does not change the history.
+
+`show values N'
+ Print ten history values centered on history item number N.
+
+`show values +'
+ Print ten history values just after the values last printed. If
+ no more values are available, produces no display.
+
+ Pressing RET to repeat `show values N' has exactly the same effect
+as `show values +'.
+
+
+File: gdb.info, Node: Convenience Vars, Next: Registers, Prev: Value History, Up: Data
+
+Convenience variables
+=====================
+
+ GDB provides "convenience variables" that you can use within GDB to
+hold on to a value and refer to it later. These variables exist
+entirely within GDB; they are not part of your program, and setting a
+convenience variable has no direct effect on further execution of your
+program. That is why you can use them freely.
+
+ Convenience variables are prefixed with `$'. Any name preceded by
+`$' can be used for a convenience variable, unless it is one of the
+predefined machine-specific register names (*note Registers::.).
+(Value history references, in contrast, are *numbers* preceded by `$'.
+*Note Value history: Value History.)
+
+ You can save a value in a convenience variable with an assignment
+expression, just as you would set a variable in your program. For
+example:
+
+ set $foo = *object_ptr
+
+would save in `$foo' the value contained in the object pointed to by
+`object_ptr'.
+
+ Using a convenience variable for the first time creates it, but its
+value is `void' until you assign a new value. You can alter the value
+with another assignment at any time.
+
+ Convenience variables have no fixed types. You can assign a
+convenience variable any type of value, including structures and
+arrays, even if that variable already has a value of a different type.
+The convenience variable, when used as an expression, has the type of
+its current value.
+
+`show convenience'
+ Print a list of convenience variables used so far, and their
+ values. Abbreviated `show con'.
+
+ One of the ways to use a convenience variable is as a counter to be
+incremented or a pointer to be advanced. For example, to print a field
+from successive elements of an array of structures:
+
+ set $i = 0
+ print bar[$i++]->contents
+ ... repeat that command by typing RET.
+
+ Some convenience variables are created automatically by GDB and given
+values likely to be useful.
+
+`$_'
+ The variable `$_' is automatically set by the `x' command to the
+ last address examined (*note Examining memory: Memory.). Other
+ commands which provide a default address for `x' to examine also
+ set `$_' to that address; these commands include `info line' and
+ `info breakpoint'. The type of `$_' is `void *' except when set
+ by the `x' command, in which case it is a pointer to the type of
+ `$__'.
+
+`$__'
+ The variable `$__' is automatically set by the `x' command to the
+ value found in the last address examined. Its type is chosen to
+ match the format in which the data was printed.
+
diff --git a/gnu/usr.bin/gdb/doc/gdb.info-4 b/gnu/usr.bin/gdb/doc/gdb.info-4
new file mode 100644
index 0000000..b0758fa
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/gdb.info-4
@@ -0,0 +1,1349 @@
+This is Info file ./gdb.info, produced by Makeinfo-1.52 from the input
+file gdb.texinfo.
+
+START-INFO-DIR-ENTRY
+* Gdb:: The GNU debugger.
+END-INFO-DIR-ENTRY
+ This file documents the GNU debugger GDB.
+
+ This is Edition 4.09, August 1993, of `Debugging with GDB: the GNU
+Source-Level Debugger' for GDB Version 4.11.
+
+ Copyright (C) 1988, '89, '90, '91, '92, '93 Free Software
+Foundation, Inc.
+
+ Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of
+this manual under the conditions for verbatim copying, provided also
+that the entire resulting derived work is distributed under the terms
+of a permission notice identical to this one.
+
+ Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions.
+
+
+File: gdb.info, Node: Registers, Next: Floating Point Hardware, Prev: Convenience Vars, Up: Data
+
+Registers
+=========
+
+ You can refer to machine register contents, in expressions, as
+variables with names starting with `$'. The names of registers are
+different for each machine; use `info registers' to see the names used
+on your machine.
+
+`info registers'
+ Print the names and values of all registers except floating-point
+ registers (in the selected stack frame).
+
+`info all-registers'
+ Print the names and values of all registers, including
+ floating-point registers.
+
+`info registers REGNAME ...'
+ Print the relativized value of each specified register REGNAME.
+ rEGNAME may be any register name valid on the machine you are
+ using, with or without the initial `$'.
+
+ GDB has four "standard" register names that are available (in
+expressions) on most machines--whenever they do not conflict with an
+architecture's canonical mnemonics for registers. The register names
+`$pc' and `$sp' are used for the program counter register and the stack
+pointer. `$fp' is used for a register that contains a pointer to the
+current stack frame, and `$ps' is used for a register that contains the
+processor status. For example, you could print the program counter in
+hex with
+
+ p/x $pc
+
+or print the instruction to be executed next with
+
+ x/i $pc
+
+or add four to the stack pointer(1) with
+
+ set $sp += 4
+
+ Whenever possible, these four standard register names are available
+on your machine even though the machine has different canonical
+mnemonics, so long as there is no conflict. The `info registers'
+command shows the canonical names. For example, on the SPARC, `info
+registers' displays the processor status register as `$psr' but you can
+also refer to it as `$ps'.
+
+ GDB always considers the contents of an ordinary register as an
+integer when the register is examined in this way. Some machines have
+special registers which can hold nothing but floating point; these
+registers are considered to have floating point values. There is no way
+to refer to the contents of an ordinary register as floating point value
+(although you can *print* it as a floating point value with `print/f
+$REGNAME').
+
+ Some registers have distinct "raw" and "virtual" data formats. This
+means that the data format in which the register contents are saved by
+the operating system is not the same one that your program normally
+sees. For example, the registers of the 68881 floating point
+coprocessor are always saved in "extended" (raw) format, but all C
+programs expect to work with "double" (virtual) format. In such cases,
+GDB normally works with the virtual format only (the format that makes
+sense for your program), but the `info registers' command prints the
+data in both formats.
+
+ Normally, register values are relative to the selected stack frame
+(*note Selecting a frame: Selection.). This means that you get the
+value that the register would contain if all stack frames farther in
+were exited and their saved registers restored. In order to see the
+true contents of hardware registers, you must select the innermost
+frame (with `frame 0').
+
+ However, GDB must deduce where registers are saved, from the machine
+code generated by your compiler. If some registers are not saved, or if
+GDB is unable to locate the saved registers, the selected stack frame
+will make no difference.
+
+`set rstack_high_address ADDRESS'
+ On AMD 29000 family processors, registers are saved in a separate
+ "register stack". There is no way for GDB to determine the extent
+ of this stack. Normally, GDB just assumes that the stack is "large
+ enough". This may result in GDB referencing memory locations that
+ do not exist. If necessary, you can get around this problem by
+ specifying the ending address of the register stack with the `set
+ rstack_high_address' command. The argument should be an address,
+ which you will probably want to precede with `0x' to specify in
+ hexadecimal.
+
+`show rstack_high_address'
+ Display the current limit of the register stack, on AMD 29000
+ family processors.
+
+ ---------- Footnotes ----------
+
+ (1) This is a way of removing one word from the stack, on machines
+where stacks grow downward in memory (most machines, nowadays). This
+assumes that the innermost stack frame is selected; setting `$sp' is
+not allowed when other stack frames are selected. To pop entire frames
+off the stack, regardless of machine architecture, use `return'; *note
+Returning from a function: Returning..
+
+
+File: gdb.info, Node: Floating Point Hardware, Prev: Registers, Up: Data
+
+Floating point hardware
+=======================
+
+ Depending on the host machine architecture, GDB may be able to give
+you more information about the status of the floating point hardware.
+
+`info float'
+ Display hardware-dependent information about the floating point
+ unit. The exact contents and layout vary depending on the
+ floating point chip; on some platforms, `info float' is not
+ available at all.
+
+
+File: gdb.info, Node: Languages, Next: Symbols, Prev: Data, Up: Top
+
+Using GDB with Different Languages
+**********************************
+
+ Although programming languages generally have common aspects, they
+are rarely expressed in the same manner. For instance, in ANSI C,
+dereferencing a pointer `p' is accomplished by `*p', but in Modula-2,
+it is accomplished by `p^'. Values can also be represented (and
+displayed) differently. Hex numbers in C are written like `0x1ae',
+while in Modula-2 they appear as `1AEH'.
+
+ Language-specific information is built into GDB for some languages,
+allowing you to express operations like the above in your program's
+native language, and allowing GDB to output values in a manner
+consistent with the syntax of your program's native language. The
+language you use to build expressions, called the "working language",
+can be selected manually, or GDB can set it automatically.
+
+* Menu:
+
+* Setting:: Switching between source languages
+* Show:: Displaying the language
+
+* Checks:: Type and range checks
+
+* Support:: Supported languages
+
+
+File: gdb.info, Node: Setting, Next: Show, Up: Languages
+
+Switching between source languages
+==================================
+
+ There are two ways to control the working language--either have GDB
+set it automatically, or select it manually yourself. You can use the
+`set language' command for either purpose. On startup, GDB defaults to
+setting the language automatically.
+
+* Menu:
+
+* Manually:: Setting the working language manually
+* Automatically:: Having GDB infer the source language
+
+
+File: gdb.info, Node: Manually, Next: Automatically, Up: Setting
+
+Setting the working language
+----------------------------
+
+ If you allow GDB to set the language automatically, expressions are
+interpreted the same way in your debugging session and your program.
+
+ If you wish, you may set the language manually. To do this, issue
+the command `set language LANG', where LANG is the name of a language,
+such as `c' or `modula-2'. For a list of the supported languages, type
+`set language'.
+
+ Setting the language manually prevents GDB from updating the working
+language automatically. This can lead to confusion if you try to debug
+a program when the working language is not the same as the source
+language, when an expression is acceptable to both languages--but means
+different things. For instance, if the current source file were
+written in C, and GDB was parsing Modula-2, a command such as:
+
+ print a = b + c
+
+might not have the effect you intended. In C, this means to add `b'
+and `c' and place the result in `a'. The result printed would be the
+value of `a'. In Modula-2, this means to compare `a' to the result of
+`b+c', yielding a `BOOLEAN' value.
+
+
+File: gdb.info, Node: Automatically, Prev: Manually, Up: Setting
+
+Having GDB infer the source language
+------------------------------------
+
+ To have GDB set the working language automatically, use `set
+language local' or `set language auto'. GDB then infers the language
+that a program was written in by looking at the name of its source
+files, and examining their extensions:
+
+`*.mod'
+ Modula-2 source file
+
+`*.c'
+ C source file
+
+`*.C'
+`*.cc'
+ C++ source file
+
+ This information is recorded for each function or procedure in a
+source file. When your program stops in a frame (usually by
+encountering a breakpoint), GDB sets the working language to the
+language recorded for the function in that frame. If the language for
+a frame is unknown (that is, if the function or block corresponding to
+the frame was defined in a source file that does not have a recognized
+extension), the current working language is not changed, and GDB issues
+a warning.
+
+ This may not seem necessary for most programs, which are written
+entirely in one source language. However, program modules and libraries
+written in one source language can be used by a main program written in
+a different source language. Using `set language auto' in this case
+frees you from having to set the working language manually.
+
+
+File: gdb.info, Node: Show, Next: Checks, Prev: Setting, Up: Languages
+
+Displaying the language
+=======================
+
+ The following commands will help you find out which language is the
+working language, and also what language source files were written in.
+
+`show language'
+ Display the current working language. This is the language you
+ can use with commands such as `print' to build and compute
+ expressions that may involve variables in your program.
+
+`info frame'
+ Among the other information listed here (*note Information about a
+ frame: Frame Info.) is the source language for this frame. This
+ is the language that will become the working language if you ever
+ use an identifier that is in this frame.
+
+`info source'
+ Among the other information listed here (*note Examining the
+ Symbol Table: Symbols.) is the source language of this source file.
+
+
+File: gdb.info, Node: Checks, Next: Support, Prev: Show, Up: Languages
+
+Type and range checking
+=======================
+
+ *Warning:* In this release, the GDB commands for type and range
+ checking are included, but they do not yet have any effect. This
+ section documents the intended facilities.
+
+ Some languages are designed to guard you against making seemingly
+common errors through a series of compile- and run-time checks. These
+include checking the type of arguments to functions and operators, and
+making sure mathematical overflows are caught at run time. Checks such
+as these help to ensure a program's correctness once it has been
+compiled by eliminating type mismatches, and providing active checks
+for range errors when your program is running.
+
+ GDB can check for conditions like the above if you wish. Although
+GDB will not check the statements in your program, it can check
+expressions entered directly into GDB for evaluation via the `print'
+command, for example. As with the working language, GDB can also
+decide whether or not to check automatically based on your program's
+source language. *Note Supported languages: Support, for the default
+settings of supported languages.
+
+* Menu:
+
+* Type Checking:: An overview of type checking
+* Range Checking:: An overview of range checking
+
+
+File: gdb.info, Node: Type Checking, Next: Range Checking, Up: Checks
+
+An overview of type checking
+----------------------------
+
+ Some languages, such as Modula-2, are strongly typed, meaning that
+the arguments to operators and functions have to be of the correct type,
+otherwise an error occurs. These checks prevent type mismatch errors
+from ever causing any run-time problems. For example,
+
+ 1 + 2 => 3
+but
+ error--> 1 + 2.3
+
+ The second example fails because the `CARDINAL' 1 is not
+type-compatible with the `REAL' 2.3.
+
+ For expressions you use in GDB commands, you can tell the GDB type
+checker to skip checking; to treat any mismatches as errors and abandon
+the expression; or only issue warnings when type mismatches occur, but
+evaluate the expression anyway. When you choose the last of these, GDB
+evaluates expressions like the second example above, but also issues a
+warning.
+
+ Even though you may turn type checking off, other type-based reasons
+may prevent GDB from evaluating an expression. For instance, GDB does
+not know how to add an `int' and a `struct foo'. These particular type
+errors have nothing to do with the language in use, and usually arise
+from expressions, such as the one described above, which make little
+sense to evaluate anyway.
+
+ Each language defines to what degree it is strict about type. For
+instance, both Modula-2 and C require the arguments to arithmetical
+operators to be numbers. In C, enumerated types and pointers can be
+represented as numbers, so that they are valid arguments to mathematical
+operators. *Note Supported languages: Support, for further details on
+specific languages.
+
+ GDB provides some additional commands for controlling the type
+checker:
+
+`set check type auto'
+ Set type checking on or off based on the current working language.
+ *Note Supported languages: Support, for the default settings for
+ each language.
+
+`set check type on'
+`set check type off'
+ Set type checking on or off, overriding the default setting for the
+ current working language. Issue a warning if the setting does not
+ match the language default. If any type mismatches occur in
+ evaluating an expression while typechecking is on, GDB prints a
+ message and aborts evaluation of the expression.
+
+`set check type warn'
+ Cause the type checker to issue warnings, but to always attempt to
+ evaluate the expression. Evaluating the expression may still be
+ impossible for other reasons. For example, GDB cannot add numbers
+ and structures.
+
+`show type'
+ Show the current setting of the type checker, and whether or not
+ GDB is setting it automatically.
+
+
+File: gdb.info, Node: Range Checking, Prev: Type Checking, Up: Checks
+
+An overview of range checking
+-----------------------------
+
+ In some languages (such as Modula-2), it is an error to exceed the
+bounds of a type; this is enforced with run-time checks. Such range
+checking is meant to ensure program correctness by making sure
+computations do not overflow, or indices on an array element access do
+not exceed the bounds of the array.
+
+ For expressions you use in GDB commands, you can tell GDB to treat
+range errors in one of three ways: ignore them, always treat them as
+errors and abandon the expression, or issue warnings but evaluate the
+expression anyway.
+
+ A range error can result from numerical overflow, from exceeding an
+array index bound, or when you type a constant that is not a member of
+any type. Some languages, however, do not treat overflows as an error.
+In many implementations of C, mathematical overflow causes the result
+to "wrap around" to lower values--for example, if M is the largest
+integer value, and S is the smallest, then
+
+ M + 1 => S
+
+ This, too, is specific to individual languages, and in some cases
+specific to individual compilers or machines. *Note Supported
+languages: Support, for further details on specific languages.
+
+ GDB provides some additional commands for controlling the range
+checker:
+
+`set check range auto'
+ Set range checking on or off based on the current working language.
+ *Note Supported languages: Support, for the default settings for
+ each language.
+
+`set check range on'
+`set check range off'
+ Set range checking on or off, overriding the default setting for
+ the current working language. A warning is issued if the setting
+ does not match the language default. If a range error occurs,
+ then a message is printed and evaluation of the expression is
+ aborted.
+
+`set check range warn'
+ Output messages when the GDB range checker detects a range error,
+ but attempt to evaluate the expression anyway. Evaluating the
+ expression may still be impossible for other reasons, such as
+ accessing memory that the process does not own (a typical example
+ from many Unix systems).
+
+`show range'
+ Show the current setting of the range checker, and whether or not
+ it is being set automatically by GDB.
+
+
+File: gdb.info, Node: Support, Prev: Checks, Up: Languages
+
+Supported languages
+===================
+
+ GDB 4 supports C, C++, and Modula-2. Some GDB features may be used
+in expressions regardless of the language you use: the GDB `@' and `::'
+operators, and the `{type}addr' construct (*note Expressions:
+Expressions.) can be used with the constructs of any supported language.
+
+ The following sections detail to what degree each source language is
+supported by GDB. These sections are not meant to be language
+tutorials or references, but serve only as a reference guide to what the
+GDB expression parser will accept, and what input and output formats
+should look like for different languages. There are many good books
+written on each of these languages; please look to these for a language
+reference or tutorial.
+
+* Menu:
+
+* C:: C and C++
+* Modula-2:: Modula-2
+
+
+File: gdb.info, Node: C, Next: Modula-2, Up: Support
+
+C and C++
+---------
+
+ Since C and C++ are so closely related, many features of GDB apply
+to both languages. Whenever this is the case, we discuss both languages
+together.
+
+ The C++ debugging facilities are jointly implemented by the GNU C++
+compiler and GDB. Therefore, to debug your C++ code effectively, you
+must compile your C++ programs with the GNU C++ compiler, `g++'.
+
+* Menu:
+
+* C Operators:: C and C++ operators
+* C Constants:: C and C++ constants
+* Cplus expressions:: C++ expressions
+* C Defaults:: Default settings for C and C++
+
+* C Checks:: C and C++ type and range checks
+
+* Debugging C:: GDB and C
+* Debugging C plus plus:: Special features for C++
+
+
+File: gdb.info, Node: C Operators, Next: C Constants, Up: C
+
+C and C++ operators
+-------------------
+
+ Operators must be defined on values of specific types. For instance,
+`+' is defined on numbers, but not on structures. Operators are often
+defined on groups of types.
+
+ For the purposes of C and C++, the following definitions hold:
+
+ * *Integral types* include `int' with any of its storage-class
+ specifiers; `char'; and `enum'.
+
+ * *Floating-point types* include `float' and `double'.
+
+ * *Pointer types* include all types defined as `(TYPE *)'.
+
+ * *Scalar types* include all of the above.
+
+The following operators are supported. They are listed here in order
+of increasing precedence:
+
+`,'
+ The comma or sequencing operator. Expressions in a
+ comma-separated list are evaluated from left to right, with the
+ result of the entire expression being the last expression
+ evaluated.
+
+`='
+ Assignment. The value of an assignment expression is the value
+ assigned. Defined on scalar types.
+
+`OP='
+ Used in an expression of the form `A OP= B', and translated to
+ `A = A OP B'. `OP=' and `=' have the same precendence. OP is any
+ one of the operators `|', `^', `&', `<<', `>>', `+', `-', `*',
+ `/', `%'.
+
+`?:'
+ The ternary operator. `A ? B : C' can be thought of as: if A
+ then B else C. A should be of an integral type.
+
+`||'
+ Logical OR. Defined on integral types.
+
+`&&'
+ Logical AND. Defined on integral types.
+
+`|'
+ Bitwise OR. Defined on integral types.
+
+`^'
+ Bitwise exclusive-OR. Defined on integral types.
+
+`&'
+ Bitwise AND. Defined on integral types.
+
+`==, !='
+ Equality and inequality. Defined on scalar types. The value of
+ these expressions is 0 for false and non-zero for true.
+
+`<, >, <=, >='
+ Less than, greater than, less than or equal, greater than or equal.
+ Defined on scalar types. The value of these expressions is 0 for
+ false and non-zero for true.
+
+`<<, >>'
+ left shift, and right shift. Defined on integral types.
+
+`@'
+ The GDB "artificial array" operator (*note Expressions:
+ Expressions.).
+
+`+, -'
+ Addition and subtraction. Defined on integral types,
+ floating-point types and pointer types.
+
+`*, /, %'
+ Multiplication, division, and modulus. Multiplication and
+ division are defined on integral and floating-point types.
+ Modulus is defined on integral types.
+
+`++, --'
+ Increment and decrement. When appearing before a variable, the
+ operation is performed before the variable is used in an
+ expression; when appearing after it, the variable's value is used
+ before the operation takes place.
+
+`*'
+ Pointer dereferencing. Defined on pointer types. Same precedence
+ as `++'.
+
+`&'
+ Address operator. Defined on variables. Same precedence as `++'.
+
+ For debugging C++, GDB implements a use of `&' beyond what is
+ allowed in the C++ language itself: you can use `&(&REF)' (or, if
+ you prefer, simply `&&REF') to examine the address where a C++
+ reference variable (declared with `&REF') is stored.
+
+`-'
+ Negative. Defined on integral and floating-point types. Same
+ precedence as `++'.
+
+`!'
+ Logical negation. Defined on integral types. Same precedence as
+ `++'.
+
+`~'
+ Bitwise complement operator. Defined on integral types. Same
+ precedence as `++'.
+
+`., ->'
+ Structure member, and pointer-to-structure member. For
+ convenience, GDB regards the two as equivalent, choosing whether
+ to dereference a pointer based on the stored type information.
+ Defined on `struct' and `union' data.
+
+`[]'
+ Array indexing. `A[I]' is defined as `*(A+I)'. Same precedence
+ as `->'.
+
+`()'
+ Function parameter list. Same precedence as `->'.
+
+`::'
+ C++ scope resolution operator. Defined on `struct', `union', and
+ `class' types.
+
+`::'
+ Doubled colons also represent the GDB scope operator (*note
+ Expressions: Expressions.). Same precedence as `::', above.
+
+
+File: gdb.info, Node: C Constants, Next: Cplus expressions, Prev: C Operators, Up: C
+
+C and C++ constants
+-------------------
+
+ GDB allows you to express the constants of C and C++ in the
+following ways:
+
+ * Integer constants are a sequence of digits. Octal constants are
+ specified by a leading `0' (ie. zero), and hexadecimal constants by
+ a leading `0x' or `0X'. Constants may also end with a letter `l',
+ specifying that the constant should be treated as a `long' value.
+
+ * Floating point constants are a sequence of digits, followed by a
+ decimal point, followed by a sequence of digits, and optionally
+ followed by an exponent. An exponent is of the form:
+ `e[[+]|-]NNN', where NNN is another sequence of digits. The `+'
+ is optional for positive exponents.
+
+ * Enumerated constants consist of enumerated identifiers, or their
+ integral equivalents.
+
+ * Character constants are a single character surrounded by single
+ quotes (`''), or a number--the ordinal value of the corresponding
+ character (usually its ASCII value). Within quotes, the single
+ character may be represented by a letter or by "escape sequences",
+ which are of the form `\NNN', where NNN is the octal representation
+ of the character's ordinal value; or of the form `\X', where `X'
+ is a predefined special character--for example, `\n' for newline.
+
+ * String constants are a sequence of character constants surrounded
+ by double quotes (`"').
+
+ * Pointer constants are an integral value. You can also write
+ pointers to constants using the C operator `&'.
+
+ * Array constants are comma-separated lists surrounded by braces `{'
+ and `}'; for example, `{1,2,3}' is a three-element array of
+ integers, `{{1,2}, {3,4}, {5,6}}' is a three-by-two array, and
+ `{&"hi", &"there", &"fred"}' is a three-element array of pointers.
+
+
+File: gdb.info, Node: Cplus expressions, Next: C Defaults, Prev: C Constants, Up: C
+
+C++ expressions
+---------------
+
+ GDB expression handling has a number of extensions to interpret a
+significant subset of C++ expressions.
+
+ *Warning:* Most of these extensions depend on the use of additional
+ debugging information in the symbol table, and thus require a rich,
+ extendable object code format. In particular, if your system uses
+ a.out, MIPS ECOFF, RS/6000 XCOFF, or Sun ELF with stabs extensions
+ to the symbol table, these facilities are all available. Where
+ the object code format is standard COFF, on the other hand, most
+ of the C++ support in GDB will *not* work, nor can it. For the
+ standard SVr4 debugging format, DWARF in ELF, the standard is
+ still evolving, so the C++ support in GDB is still fragile; when
+ this debugging format stabilizes, however, C++ support will also
+ be available on systems that use it.
+
+ 1. Member function calls are allowed; you can use expressions like
+
+ count = aml->GetOriginal(x, y)
+
+ 2. While a member function is active (in the selected stack frame),
+ your expressions have the same namespace available as the member
+ function; that is, GDB allows implicit references to the class
+ instance pointer `this' following the same rules as C++.
+
+ 3. You can call overloaded functions; GDB will resolve the function
+ call to the right definition, with one restriction--you must use
+ arguments of the type required by the function that you want to
+ call. GDB will not perform conversions requiring constructors or
+ user-defined type operators.
+
+ 4. GDB understands variables declared as C++ references; you can use
+ them in expressions just as you do in C++ source--they are
+ automatically dereferenced.
+
+ In the parameter list shown when GDB displays a frame, the values
+ of reference variables are not displayed (unlike other variables);
+ this avoids clutter, since references are often used for large
+ structures. The *address* of a reference variable is always
+ shown, unless you have specified `set print address off'.
+
+ 5. GDB supports the C++ name resolution operator `::'--your
+ expressions can use it just as expressions in your program do.
+ Since one scope may be defined in another, you can use `::'
+ repeatedly if necessary, for example in an expression like
+ `SCOPE1::SCOPE2::NAME'. GDB also allows resolving name scope by
+ reference to source files, in both C and C++ debugging (*note
+ Program variables: Variables.).
+
+
+File: gdb.info, Node: C Defaults, Next: C Checks, Prev: Cplus expressions, Up: C
+
+C and C++ defaults
+------------------
+
+ If you allow GDB to set type and range checking automatically, they
+both default to `off' whenever the working language changes to C or
+C++. This happens regardless of whether you, or GDB, selected the
+working language.
+
+ If you allow GDB to set the language automatically, it sets the
+working language to C or C++ on entering code compiled from a source
+file whose name ends with `.c', `.C', or `.cc'. *Note Having GDB infer
+the source language: Automatically, for further details.
+
+
+File: gdb.info, Node: C Checks, Next: Debugging C, Prev: C Defaults, Up: C
+
+C and C++ type and range checks
+-------------------------------
+
+ By default, when GDB parses C or C++ expressions, type checking is
+not used. However, if you turn type checking on, GDB will consider two
+variables type equivalent if:
+
+ * The two variables are structured and have the same structure,
+ union, or enumerated tag.
+
+ * Two two variables have the same type name, or types that have been
+ declared equivalent through `typedef'.
+
+ Range checking, if turned on, is done on mathematical operations.
+Array indices are not checked, since they are often used to index a
+pointer that is not itself an array.
+
+
+File: gdb.info, Node: Debugging C, Next: Debugging C plus plus, Prev: C Checks, Up: C
+
+GDB and C
+---------
+
+ The `set print union' and `show print union' commands apply to the
+`union' type. When set to `on', any `union' that is inside a `struct'
+or `class' will also be printed. Otherwise, it will appear as `{...}'.
+
+ The `@' operator aids in the debugging of dynamic arrays, formed
+with pointers and a memory allocation function. *Note Expressions:
+Expressions.
+
+
+File: gdb.info, Node: Debugging C plus plus, Prev: Debugging C, Up: C
+
+GDB features for C++
+--------------------
+
+ Some GDB commands are particularly useful with C++, and some are
+designed specifically for use with C++. Here is a summary:
+
+`breakpoint menus'
+ When you want a breakpoint in a function whose name is overloaded,
+ GDB breakpoint menus help you specify which function definition
+ you want. *Note Breakpoint menus: Breakpoint Menus.
+
+`rbreak REGEX'
+ Setting breakpoints using regular expressions is helpful for
+ setting breakpoints on overloaded functions that are not members
+ of any special classes. *Note Setting breakpoints: Set Breaks.
+
+`catch EXCEPTIONS'
+`info catch'
+ Debug C++ exception handling using these commands. *Note
+ Breakpoints and exceptions: Exception Handling.
+
+`ptype TYPENAME'
+ Print inheritance relationships as well as other information for
+ type TYPENAME. *Note Examining the Symbol Table: Symbols.
+
+`set print demangle'
+`show print demangle'
+`set print asm-demangle'
+`show print asm-demangle'
+ Control whether C++ symbols display in their source form, both when
+ displaying code as C++ source and when displaying disassemblies.
+ *Note Print settings: Print Settings.
+
+`set print object'
+`show print object'
+ Choose whether to print derived (actual) or declared types of
+ objects. *Note Print settings: Print Settings.
+
+`set print vtbl'
+`show print vtbl'
+ Control the format for printing virtual function tables. *Note
+ Print settings: Print Settings.
+
+`Overloaded symbol names'
+ You can specify a particular definition of an overloaded symbol,
+ using the same notation that is used to declare such symbols in
+ C++: type `SYMBOL(TYPES)' rather than just SYMBOL. You can also
+ use the GDB command-line word completion facilities to list the
+ available choices, or to finish the type list for you. *Note
+ Command completion: Completion, for details on how to do this.
+
+
+File: gdb.info, Node: Modula-2, Prev: C, Up: Support
+
+Modula-2
+--------
+
+ The extensions made to GDB to support Modula-2 only support output
+from the GNU Modula-2 compiler (which is currently being developed).
+Other Modula-2 compilers are not currently supported, and attempting to
+debug executables produced by them will most likely result in an error
+as GDB reads in the executable's symbol table.
+
+* Menu:
+
+* M2 Operators:: Built-in operators
+* Built-In Func/Proc:: Built-in functions and procedures
+* M2 Constants:: Modula-2 constants
+* M2 Defaults:: Default settings for Modula-2
+* Deviations:: Deviations from standard Modula-2
+* M2 Checks:: Modula-2 type and range checks
+* M2 Scope:: The scope operators `::' and `.'
+* GDB/M2:: GDB and Modula-2
+
+
+File: gdb.info, Node: M2 Operators, Next: Built-In Func/Proc, Up: Modula-2
+
+Operators
+---------
+
+ Operators must be defined on values of specific types. For instance,
+`+' is defined on numbers, but not on structures. Operators are often
+defined on groups of types. For the purposes of Modula-2, the
+following definitions hold:
+
+ * *Integral types* consist of `INTEGER', `CARDINAL', and their
+ subranges.
+
+ * *Character types* consist of `CHAR' and its subranges.
+
+ * *Floating-point types* consist of `REAL'.
+
+ * *Pointer types* consist of anything declared as `POINTER TO TYPE'.
+
+ * *Scalar types* consist of all of the above.
+
+ * *Set types* consist of `SET' and `BITSET' types.
+
+ * *Boolean types* consist of `BOOLEAN'.
+
+The following operators are supported, and appear in order of
+increasing precedence:
+
+`,'
+ Function argument or array index separator.
+
+`:='
+ Assignment. The value of VAR `:=' VALUE is VALUE.
+
+`<, >'
+ Less than, greater than on integral, floating-point, or enumerated
+ types.
+
+`<=, >='
+ Less than, greater than, less than or equal to, greater than or
+ equal to on integral, floating-point and enumerated types, or set
+ inclusion on set types. Same precedence as `<'.
+
+`=, <>, #'
+ Equality and two ways of expressing inequality, valid on scalar
+ types. Same precedence as `<'. In GDB scripts, only `<>' is
+ available for inequality, since `#' conflicts with the script
+ comment character.
+
+`IN'
+ Set membership. Defined on set types and the types of their
+ members. Same precedence as `<'.
+
+`OR'
+ Boolean disjunction. Defined on boolean types.
+
+`AND, &'
+ Boolean conjuction. Defined on boolean types.
+
+`@'
+ The GDB "artificial array" operator (*note Expressions:
+ Expressions.).
+
+`+, -'
+ Addition and subtraction on integral and floating-point types, or
+ union and difference on set types.
+
+`*'
+ Multiplication on integral and floating-point types, or set
+ intersection on set types.
+
+`/'
+ Division on floating-point types, or symmetric set difference on
+ set types. Same precedence as `*'.
+
+`DIV, MOD'
+ Integer division and remainder. Defined on integral types. Same
+ precedence as `*'.
+
+`-'
+ Negative. Defined on `INTEGER' and `REAL' data.
+
+`^'
+ Pointer dereferencing. Defined on pointer types.
+
+`NOT'
+ Boolean negation. Defined on boolean types. Same precedence as
+ `^'.
+
+`.'
+ `RECORD' field selector. Defined on `RECORD' data. Same
+ precedence as `^'.
+
+`[]'
+ Array indexing. Defined on `ARRAY' data. Same precedence as `^'.
+
+`()'
+ Procedure argument list. Defined on `PROCEDURE' objects. Same
+ precedence as `^'.
+
+`::, .'
+ GDB and Modula-2 scope operators.
+
+ *Warning:* Sets and their operations are not yet supported, so GDB
+ will treat the use of the operator `IN', or the use of operators
+ `+', `-', `*', `/', `=', , `<>', `#', `<=', and `>=' on sets as an
+ error.
+
+
+File: gdb.info, Node: Built-In Func/Proc, Next: M2 Constants, Prev: M2 Operators, Up: Modula-2
+
+Built-in functions and procedures
+---------------------------------
+
+ Modula-2 also makes available several built-in procedures and
+functions. In describing these, the following metavariables are used:
+
+A
+ represents an `ARRAY' variable.
+
+C
+ represents a `CHAR' constant or variable.
+
+I
+ represents a variable or constant of integral type.
+
+M
+ represents an identifier that belongs to a set. Generally used in
+ the same function with the metavariable S. The type of S should
+ be `SET OF MTYPE' (where MTYPE is the type of M).
+
+N
+ represents a variable or constant of integral or floating-point
+ type.
+
+R
+ represents a variable or constant of floating-point type.
+
+T
+ represents a type.
+
+V
+ represents a variable.
+
+X
+ represents a variable or constant of one of many types. See the
+ explanation of the function for details.
+
+ All Modula-2 built-in procedures also return a result, described
+below.
+
+`ABS(N)'
+ Returns the absolute value of N.
+
+`CAP(C)'
+ If C is a lower case letter, it returns its upper case equivalent,
+ otherwise it returns its argument
+
+`CHR(I)'
+ Returns the character whose ordinal value is I.
+
+`DEC(V)'
+ Decrements the value in the variable V. Returns the new value.
+
+`DEC(V,I)'
+ Decrements the value in the variable V by I. Returns the new
+ value.
+
+`EXCL(M,S)'
+ Removes the element M from the set S. Returns the new set.
+
+`FLOAT(I)'
+ Returns the floating point equivalent of the integer I.
+
+`HIGH(A)'
+ Returns the index of the last member of A.
+
+`INC(V)'
+ Increments the value in the variable V. Returns the new value.
+
+`INC(V,I)'
+ Increments the value in the variable V by I. Returns the new
+ value.
+
+`INCL(M,S)'
+ Adds the element M to the set S if it is not already there.
+ Returns the new set.
+
+`MAX(T)'
+ Returns the maximum value of the type T.
+
+`MIN(T)'
+ Returns the minimum value of the type T.
+
+`ODD(I)'
+ Returns boolean TRUE if I is an odd number.
+
+`ORD(X)'
+ Returns the ordinal value of its argument. For example, the
+ ordinal value of a character is its ASCII value (on machines
+ supporting the ASCII character set). X must be of an ordered
+ type, which include integral, character and enumerated types.
+
+`SIZE(X)'
+ Returns the size of its argument. X can be a variable or a type.
+
+`TRUNC(R)'
+ Returns the integral part of R.
+
+`VAL(T,I)'
+ Returns the member of the type T whose ordinal value is I.
+
+ *Warning:* Sets and their operations are not yet supported, so
+ GDB will treat the use of procedures `INCL' and `EXCL' as an error.
+
+
+File: gdb.info, Node: M2 Constants, Next: M2 Defaults, Prev: Built-In Func/Proc, Up: Modula-2
+
+Constants
+---------
+
+ GDB allows you to express the constants of Modula-2 in the following
+ways:
+
+ * Integer constants are simply a sequence of digits. When used in an
+ expression, a constant is interpreted to be type-compatible with
+ the rest of the expression. Hexadecimal integers are specified by
+ a trailing `H', and octal integers by a trailing `B'.
+
+ * Floating point constants appear as a sequence of digits, followed
+ by a decimal point and another sequence of digits. An optional
+ exponent can then be specified, in the form `E[+|-]NNN', where
+ `[+|-]NNN' is the desired exponent. All of the digits of the
+ floating point constant must be valid decimal (base 10) digits.
+
+ * Character constants consist of a single character enclosed by a
+ pair of like quotes, either single (`'') or double (`"'). They may
+ also be expressed by their ordinal value (their ASCII value,
+ usually) followed by a `C'.
+
+ * String constants consist of a sequence of characters enclosed by a
+ pair of like quotes, either single (`'') or double (`"'). Escape
+ sequences in the style of C are also allowed. *Note C and C++
+ constants: C Constants, for a brief explanation of escape
+ sequences.
+
+ * Enumerated constants consist of an enumerated identifier.
+
+ * Boolean constants consist of the identifiers `TRUE' and `FALSE'.
+
+ * Pointer constants consist of integral values only.
+
+ * Set constants are not yet supported.
+
+
+File: gdb.info, Node: M2 Defaults, Next: Deviations, Prev: M2 Constants, Up: Modula-2
+
+Modula-2 defaults
+-----------------
+
+ If type and range checking are set automatically by GDB, they both
+default to `on' whenever the working language changes to Modula-2.
+This happens regardless of whether you, or GDB, selected the working
+language.
+
+ If you allow GDB to set the language automatically, then entering
+code compiled from a file whose name ends with `.mod' will set the
+working language to Modula-2. *Note Having GDB set the language
+automatically: Automatically, for further details.
+
+
+File: gdb.info, Node: Deviations, Next: M2 Checks, Prev: M2 Defaults, Up: Modula-2
+
+Deviations from standard Modula-2
+---------------------------------
+
+ A few changes have been made to make Modula-2 programs easier to
+debug. This is done primarily via loosening its type strictness:
+
+ * Unlike in standard Modula-2, pointer constants can be formed by
+ integers. This allows you to modify pointer variables during
+ debugging. (In standard Modula-2, the actual address contained in
+ a pointer variable is hidden from you; it can only be modified
+ through direct assignment to another pointer variable or
+ expression that returned a pointer.)
+
+ * C escape sequences can be used in strings and characters to
+ represent non-printable characters. GDB will print out strings
+ with these escape sequences embedded. Single non-printable
+ characters are printed using the `CHR(NNN)' format.
+
+ * The assignment operator (`:=') returns the value of its right-hand
+ argument.
+
+ * All built-in procedures both modify *and* return their argument.
+
+
+File: gdb.info, Node: M2 Checks, Next: M2 Scope, Prev: Deviations, Up: Modula-2
+
+Modula-2 type and range checks
+------------------------------
+
+ *Warning:* in this release, GDB does not yet perform type or range
+ checking.
+
+ GDB considers two Modula-2 variables type equivalent if:
+
+ * They are of types that have been declared equivalent via a `TYPE
+ T1 = T2' statement
+
+ * They have been declared on the same line. (Note: This is true of
+ the GNU Modula-2 compiler, but it may not be true of other
+ compilers.)
+
+ As long as type checking is enabled, any attempt to combine variables
+whose types are not equivalent is an error.
+
+ Range checking is done on all mathematical operations, assignment,
+array index bounds, and all built-in functions and procedures.
+
+
+File: gdb.info, Node: M2 Scope, Next: GDB/M2, Prev: M2 Checks, Up: Modula-2
+
+The scope operators `::' and `.'
+--------------------------------
+
+ There are a few subtle differences between the Modula-2 scope
+operator (`.') and the GDB scope operator (`::'). The two have similar
+syntax:
+
+
+ MODULE . ID
+ SCOPE :: ID
+
+where SCOPE is the name of a module or a procedure, MODULE the name of
+a module, and ID is any declared identifier within your program, except
+another module.
+
+ Using the `::' operator makes GDB search the scope specified by
+SCOPE for the identifier ID. If it is not found in the specified
+scope, then GDB will search all scopes enclosing the one specified by
+SCOPE.
+
+ Using the `.' operator makes GDB search the current scope for the
+identifier specified by ID that was imported from the definition module
+specified by MODULE. With this operator, it is an error if the
+identifier ID was not imported from definition module MODULE, or if ID
+is not an identifier in MODULE.
+
+
+File: gdb.info, Node: GDB/M2, Prev: M2 Scope, Up: Modula-2
+
+GDB and Modula-2
+----------------
+
+ Some GDB commands have little use when debugging Modula-2 programs.
+Five subcommands of `set print' and `show print' apply specifically to
+C and C++: `vtbl', `demangle', `asm-demangle', `object', and `union'.
+The first four apply to C++, and the last to the C `union' type, which
+has no direct analogue in Modula-2.
+
+ The `@' operator (*note Expressions: Expressions.), while available
+while using any language, is not useful with Modula-2. Its intent is
+to aid the debugging of "dynamic arrays", which cannot be created in
+Modula-2 as they can in C or C++. However, because an address can be
+specified by an integral constant, the construct `{TYPE}ADREXP' is
+still useful. (*note Expressions: Expressions.)
+
+ In GDB scripts, the Modula-2 inequality operator `#' is interpreted
+as the beginning of a comment. Use `<>' instead.
+
+
+File: gdb.info, Node: Symbols, Next: Altering, Prev: Languages, Up: Top
+
+Examining the Symbol Table
+**************************
+
+ The commands described in this section allow you to inquire about the
+symbols (names of variables, functions and types) defined in your
+program. This information is inherent in the text of your program and
+does not change as your program executes. GDB finds it in your
+program's symbol table, in the file indicated when you started GDB
+(*note Choosing files: File Options.), or by one of the file-management
+commands (*note Commands to specify files: Files.).
+
+ Occasionally, you may need to refer to symbols that contain unusual
+characters, which GDB ordinarily treats as word delimiters. The most
+frequent case is in referring to static variables in other source files
+(*note Program variables: Variables.). File names are recorded in
+object files as debugging symbols, but GDB would ordinarily parse a
+typical file name, like `foo.c', as the three words `foo' `.' `c'. To
+allow GDB to recognize `foo.c' as a single symbol, enclose it in single
+quotes; for example,
+
+ p 'foo.c'::x
+
+looks up the value of `x' in the scope of the file `foo.c'.
+
+`info address SYMBOL'
+ Describe where the data for SYMBOL is stored. For a register
+ variable, this says which register it is kept in. For a
+ non-register local variable, this prints the stack-frame offset at
+ which the variable is always stored.
+
+ Note the contrast with `print &SYMBOL', which does not work at all
+ for a register variable, and for a stack local variable prints the
+ exact address of the current instantiation of the variable.
+
+`whatis EXP'
+ Print the data type of expression EXP. EXP is not actually
+ evaluated, and any side-effecting operations (such as assignments
+ or function calls) inside it do not take place. *Note
+ Expressions: Expressions.
+
+`whatis'
+ Print the data type of `$', the last value in the value history.
+
+`ptype TYPENAME'
+ Print a description of data type TYPENAME. TYPENAME may be the
+ name of a type, or for C code it may have the form `class
+ CLASS-NAME', `struct STRUCT-TAG', `union UNION-TAG' or `enum
+ ENUM-TAG'.
+
+`ptype EXP'
+`ptype'
+ Print a description of the type of expression EXP. `ptype'
+ differs from `whatis' by printing a detailed description, instead
+ of just the name of the type.
+
+ For example, for this variable declaration:
+
+ struct complex {double real; double imag;} v;
+
+ the two commands give this output:
+
+ (gdb) whatis v
+ type = struct complex
+ (gdb) ptype v
+ type = struct complex {
+ double real;
+ double imag;
+ }
+
+ As with `whatis', using `ptype' without an argument refers to the
+ type of `$', the last value in the value history.
+
+`info types REGEXP'
+`info types'
+ Print a brief description of all types whose name matches REGEXP
+ (or all types in your program, if you supply no argument). Each
+ complete typename is matched as though it were a complete line;
+ thus, `i type value' gives information on all types in your
+ program whose name includes the string `value', but `i type
+ ^value$' gives information only on types whose complete name is
+ `value'.
+
+ This command differs from `ptype' in two ways: first, like
+ `whatis', it does not print a detailed description; second, it
+ lists all source files where a type is defined.
+
+`info source'
+ Show the name of the current source file--that is, the source file
+ for the function containing the current point of execution--and
+ the language it was written in.
+
+`info sources'
+ Print the names of all source files in your program for which
+ there is debugging information, organized into two lists: files
+ whose symbols have already been read, and files whose symbols will
+ be read when needed.
+
+`info functions'
+ Print the names and data types of all defined functions.
+
+`info functions REGEXP'
+ Print the names and data types of all defined functions whose
+ names contain a match for regular expression REGEXP. Thus, `info
+ fun step' finds all functions whose names include `step'; `info
+ fun ^step' finds those whose names start with `step'.
+
+`info variables'
+ Print the names and data types of all variables that are declared
+ outside of functions (i.e., excluding local variables).
+
+`info variables REGEXP'
+ Print the names and data types of all variables (except for local
+ variables) whose names contain a match for regular expression
+ REGEXP.
+
+`maint print symbols FILENAME'
+`maint print psymbols FILENAME'
+`maint print msymbols FILENAME'
+ Write a dump of debugging symbol data into the file FILENAME.
+ These commands are used to debug the GDB symbol-reading code. Only
+ symbols with debugging data are included. If you use `maint print
+ symbols', GDB includes all the symbols for which it has already
+ collected full details: that is, FILENAME reflects symbols for
+ only those files whose symbols GDB has read. You can use the
+ command `info sources' to find out which files these are. If you
+ use `maint print psymbols' instead, the dump shows information
+ about symbols that GDB only knows partially--that is, symbols
+ defined in files that GDB has skimmed, but not yet read
+ completely. Finally, `maint print msymbols' dumps just the
+ minimal symbol information required for each object file from
+ which GDB has read some symbols. *Note Commands to specify files:
+ Files, for a discussion of how GDB reads symbols (in the
+ description of `symbol-file').
+
diff --git a/gnu/usr.bin/gdb/doc/gdb.info-5 b/gnu/usr.bin/gdb/doc/gdb.info-5
new file mode 100644
index 0000000..ecf3d18
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/gdb.info-5
@@ -0,0 +1,1215 @@
+This is Info file ./gdb.info, produced by Makeinfo-1.52 from the input
+file gdb.texinfo.
+
+START-INFO-DIR-ENTRY
+* Gdb:: The GNU debugger.
+END-INFO-DIR-ENTRY
+ This file documents the GNU debugger GDB.
+
+ This is Edition 4.09, August 1993, of `Debugging with GDB: the GNU
+Source-Level Debugger' for GDB Version 4.11.
+
+ Copyright (C) 1988, '89, '90, '91, '92, '93 Free Software
+Foundation, Inc.
+
+ Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of
+this manual under the conditions for verbatim copying, provided also
+that the entire resulting derived work is distributed under the terms
+of a permission notice identical to this one.
+
+ Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions.
+
+
+File: gdb.info, Node: Altering, Next: GDB Files, Prev: Symbols, Up: Top
+
+Altering Execution
+******************
+
+ Once you think you have found an error in your program, you might
+want to find out for certain whether correcting the apparent error
+would lead to correct results in the rest of the run. You can find the
+answer by experiment, using the GDB features for altering execution of
+the program.
+
+ For example, you can store new values into variables or memory
+locations, give your program a signal, restart it at a different
+address, or even return prematurely from a function to its caller.
+
+* Menu:
+
+* Assignment:: Assignment to variables
+* Jumping:: Continuing at a different address
+
+* Signaling:: Giving your program a signal
+
+* Returning:: Returning from a function
+* Calling:: Calling your program's functions
+* Patching:: Patching your program
+
+
+File: gdb.info, Node: Assignment, Next: Jumping, Up: Altering
+
+Assignment to variables
+=======================
+
+ To alter the value of a variable, evaluate an assignment expression.
+*Note Expressions: Expressions. For example,
+
+ print x=4
+
+stores the value 4 into the variable `x', and then prints the value of
+the assignment expression (which is 4). *Note Using GDB with Different
+Languages: Languages, for more information on operators in supported
+languages.
+
+ If you are not interested in seeing the value of the assignment, use
+the `set' command instead of the `print' command. `set' is really the
+same as `print' except that the expression's value is not printed and
+is not put in the value history (*note Value history: Value History.).
+The expression is evaluated only for its effects.
+
+ If the beginning of the argument string of the `set' command appears
+identical to a `set' subcommand, use the `set variable' command instead
+of just `set'. This command is identical to `set' except for its lack
+of subcommands. For example, if your program has a variable `width',
+you get an error if you try to set a new value with just `set width=13',
+because GDB has the command `set width':
+
+ (gdb) whatis width
+ type = double
+ (gdb) p width
+ $4 = 13
+ (gdb) set width=47
+ Invalid syntax in expression.
+
+The invalid expression, of course, is `=47'. In order to actually set
+the program's variable `width', use
+
+ (gdb) set var width=47
+
+ GDB allows more implicit conversions in assignments than C; you can
+freely store an integer value into a pointer variable or vice versa,
+and you can convert any structure to any other structure that is the
+same length or shorter.
+
+ To store values into arbitrary places in memory, use the `{...}'
+construct to generate a value of specified type at a specified address
+(*note Expressions: Expressions.). For example, `{int}0x83040' refers
+to memory location `0x83040' as an integer (which implies a certain size
+and representation in memory), and
+
+ set {int}0x83040 = 4
+
+stores the value 4 into that memory location.
+
+
+File: gdb.info, Node: Jumping, Next: Signaling, Prev: Assignment, Up: Altering
+
+Continuing at a different address
+=================================
+
+ Ordinarily, when you continue your program, you do so at the place
+where it stopped, with the `continue' command. You can instead
+continue at an address of your own choosing, with the following
+commands:
+
+`jump LINESPEC'
+ Resume execution at line LINESPEC. Execution will stop
+ immediately if there is a breakpoint there. *Note Printing source
+ lines: List, for a description of the different forms of LINESPEC.
+
+ The `jump' command does not change the current stack frame, or the
+ stack pointer, or the contents of any memory location or any
+ register other than the program counter. If line LINESPEC is in a
+ different function from the one currently executing, the results
+ may be bizarre if the two functions expect different patterns of
+ arguments or of local variables. For this reason, the `jump'
+ command requests confirmation if the specified line is not in the
+ function currently executing. However, even bizarre results are
+ predictable if you are well acquainted with the machine-language
+ code of your program.
+
+`jump *ADDRESS'
+ Resume execution at the instruction at address ADDRESS.
+
+ You can get much the same effect as the `jump' command by storing a
+new value into the register `$pc'. The difference is that this does
+not start your program running; it only changes the address where it
+*will* run when it is continued. For example,
+
+ set $pc = 0x485
+
+causes the next `continue' command or stepping command to execute at
+address `0x485', rather than at the address where your program stopped.
+*Note Continuing and stepping: Continuing and Stepping.
+
+ The most common occasion to use the `jump' command is to back up,
+perhaps with more breakpoints set, over a portion of a program that has
+already executed, in order to examine its execution in more detail.
+
+
+File: gdb.info, Node: Signaling, Next: Returning, Prev: Jumping, Up: Altering
+
+Giving your program a signal
+============================
+
+`signal SIGNAL'
+ Resume execution where your program stopped, but immediately give
+ it the signal SIGNAL. SIGNAL can be the name or the number of a
+ signal. For example, on many systems `signal 2' and `signal
+ SIGINT' are both ways of sending an interrupt signal.
+
+ Alternatively, if SIGNAL is zero, continue execution without
+ giving a signal. This is useful when your program stopped on
+ account of a signal and would ordinary see the signal when resumed
+ with the `continue' command; `signal 0' causes it to resume
+ without a signal.
+
+ `signal' does not repeat when you press RET a second time after
+ executing the command.
+
+ Invoking the `signal' command is not the same as invoking the `kill'
+utility from the shell. Sending a signal with `kill' causes GDB to
+decide what to do with the signal depending on the signal handling
+tables (*note Signals::.). The `signal' command passes the signal
+directly to your program.
+
+
+File: gdb.info, Node: Returning, Next: Calling, Prev: Signaling, Up: Altering
+
+Returning from a function
+=========================
+
+`return'
+`return EXPRESSION'
+ You can cancel execution of a function call with the `return'
+ command. If you give an EXPRESSION argument, its value is used as
+ the function's return value.
+
+ When you use `return', GDB discards the selected stack frame (and
+all frames within it). You can think of this as making the discarded
+frame return prematurely. If you wish to specify a value to be
+returned, give that value as the argument to `return'.
+
+ This pops the selected stack frame (*note Selecting a frame:
+Selection.), and any other frames inside of it, leaving its caller as
+the innermost remaining frame. That frame becomes selected. The
+specified value is stored in the registers used for returning values of
+functions.
+
+ The `return' command does not resume execution; it leaves the
+program stopped in the state that would exist if the function had just
+returned. In contrast, the `finish' command (*note Continuing and
+stepping: Continuing and Stepping.) resumes execution until the
+selected stack frame returns naturally.
+
+
+File: gdb.info, Node: Calling, Next: Patching, Prev: Returning, Up: Altering
+
+Calling program functions
+=========================
+
+`call EXPR'
+ Evaluate the expression EXPR without displaying `void' returned
+ values.
+
+ You can use this variant of the `print' command if you want to
+execute a function from your program, but without cluttering the output
+with `void' returned values. The result is printed and saved in the
+value history, if it is not void.
+
+
+File: gdb.info, Node: Patching, Prev: Calling, Up: Altering
+
+Patching programs
+=================
+
+ By default, GDB opens the file containing your program's executable
+code (or the corefile) read-only. This prevents accidental alterations
+to machine code; but it also prevents you from intentionally patching
+your program's binary.
+
+ If you'd like to be able to patch the binary, you can specify that
+explicitly with the `set write' command. For example, you might want
+to turn on internal debugging flags, or even to make emergency repairs.
+
+`set write on'
+`set write off'
+ If you specify `set write on', GDB will open executable and core
+ files for both reading and writing; if you specify `set write off'
+ (the default), GDB will open them read-only.
+
+ If you have already loaded a file, you must load it again (using
+ the `exec-file' or `core-file' command) after changing `set
+ write', for your new setting to take effect.
+
+`show write'
+ Display whether executable files and core files will be opened for
+ writing as well as reading.
+
+
+File: gdb.info, Node: GDB Files, Next: Targets, Prev: Altering, Up: Top
+
+GDB Files
+*********
+
+ GDB needs to know the file name of the program to be debugged, both
+in order to read its symbol table and in order to start your program.
+To debug a core dump of a previous run, you must also tell GDB the name
+of the core dump file.
+
+* Menu:
+
+* Files:: Commands to specify files
+* Symbol Errors:: Errors reading symbol files
+
+
+File: gdb.info, Node: Files, Next: Symbol Errors, Up: GDB Files
+
+Commands to specify files
+=========================
+
+ The usual way to specify executable and core dump file names is with
+the command arguments given when you start GDB (*note Getting In and
+Out of GDB: Invocation..
+
+ Occasionally it is necessary to change to a different file during a
+GDB session. Or you may run GDB and forget to specify a file you want
+to use. In these situations the GDB commands to specify new files are
+useful.
+
+`file FILENAME'
+ Use FILENAME as the program to be debugged. It is read for its
+ symbols and for the contents of pure memory. It is also the
+ program executed when you use the `run' command. If you do not
+ specify a directory and the file is not found in the GDB working
+ directory, GDB uses the environment variable `PATH' as a list of
+ directories to search, just as the shell does when looking for a
+ program to run. You can change the value of this variable, for
+ both GDB and your program, using the `path' command.
+
+ On systems with memory-mapped files, an auxiliary symbol table file
+ `FILENAME.syms' may be available for FILENAME. If it is, GDB will
+ map in the symbol table from `FILENAME.syms', starting up more
+ quickly. See the descriptions of the options `-mapped' and
+ `-readnow' (available on the command line, and with the commands
+ `file', `symbol-file', or `add-symbol-file'), for more information.
+
+`file'
+ `file' with no argument makes GDB discard any information it has
+ on both executable file and the symbol table.
+
+`exec-file [ FILENAME ]'
+ Specify that the program to be run (but not the symbol table) is
+ found in FILENAME. GDB will search the environment variable `PATH'
+ if necessary to locate your program. Omitting FILENAME means to
+ discard information on the executable file.
+
+`symbol-file [ FILENAME ]'
+ Read symbol table information from file FILENAME. `PATH' is
+ searched when necessary. Use the `file' command to get both symbol
+ table and program to run from the same file.
+
+ `symbol-file' with no argument clears out GDB information on your
+ program's symbol table.
+
+ The `symbol-file' command causes GDB to forget the contents of its
+ convenience variables, the value history, and all breakpoints and
+ auto-display expressions. This is because they may contain
+ pointers to the internal data recording symbols and data types,
+ which are part of the old symbol table data being discarded inside
+ GDB.
+
+ `symbol-file' will not repeat if you press RET again after
+ executing it once.
+
+ When GDB is configured for a particular environment, it will
+ understand debugging information in whatever format is the standard
+ generated for that environment; you may use either a GNU compiler,
+ or other compilers that adhere to the local conventions. Best
+ results are usually obtained from GNU compilers; for example,
+ using `gcc' you can generate debugging information for optimized
+ code.
+
+ On some kinds of object files, the `symbol-file' command does not
+ normally read the symbol table in full right away. Instead, it
+ scans the symbol table quickly to find which source files and
+ which symbols are present. The details are read later, one source
+ file at a time, as they are needed.
+
+ The purpose of this two-stage reading strategy is to make GDB
+ start up faster. For the most part, it is invisible except for
+ occasional pauses while the symbol table details for a particular
+ source file are being read. (The `set verbose' command can turn
+ these pauses into messages if desired. *Note Optional warnings
+ and messages: Messages/Warnings.)
+
+ We have not implemented the two-stage strategy for COFF yet. When
+ the symbol table is stored in COFF format, `symbol-file' reads the
+ symbol table data in full right away.
+
+`symbol-file FILENAME [ -readnow ] [ -mapped ]'
+`file FILENAME [ -readnow ] [ -mapped ]'
+ You can override the GDB two-stage strategy for reading symbol
+ tables by using the `-readnow' option with any of the commands that
+ load symbol table information, if you want to be sure GDB has the
+ entire symbol table available.
+
+ If memory-mapped files are available on your system through the
+ `mmap' system call, you can use another option, `-mapped', to
+ cause GDB to write the symbols for your program into a reusable
+ file. Future GDB debugging sessions will map in symbol information
+ from this auxiliary symbol file (if the program has not changed),
+ rather than spending time reading the symbol table from the
+ executable program. Using the `-mapped' option has the same
+ effect as starting GDB with the `-mapped' command-line option.
+
+ You can use both options together, to make sure the auxiliary
+ symbol file has all the symbol information for your program.
+
+ The auxiliary symbol file for a program called MYPROG is called
+ `MYPROG.syms'. Once this file exists (so long as it is newer than
+ the corresponding executable), GDB will always attempt to use it
+ when you debug MYPROG; no special options or commands are needed.
+
+ The `.syms' file is specific to the host machine where you run
+ GDB. It holds an exact image of the internal GDB symbol table.
+ It cannot be shared across multiple host platforms.
+
+`core-file [ FILENAME ]'
+ Specify the whereabouts of a core dump file to be used as the
+ "contents of memory". Traditionally, core files contain only some
+ parts of the address space of the process that generated them; GDB
+ can access the executable file itself for other parts.
+
+ `core-file' with no argument specifies that no core file is to be
+ used.
+
+ Note that the core file is ignored when your program is actually
+ running under GDB. So, if you have been running your program and
+ you wish to debug a core file instead, you must kill the
+ subprocess in which the program is running. To do this, use the
+ `kill' command (*note Killing the child process: Kill Process.).
+
+`load FILENAME'
+ Depending on what remote debugging facilities are configured into
+ GDB, the `load' command may be available. Where it exists, it is
+ meant to make FILENAME (an executable) available for debugging on
+ the remote system--by downloading, or dynamic linking, for example.
+ `load' also records the FILENAME symbol table in GDB, like the
+ `add-symbol-file' command.
+
+ If your GDB does not have a `load' command, attempting to execute
+ it gets the error message "`You can't do that when your target is
+ ...'"
+
+ The file is loaded at whatever address is specified in the
+ executable. For some object file formats, like a.out, the object
+ file format fixes the address and so it won't necessarily match
+ the address you gave to the linker.
+
+ On VxWorks, `load' will dynamically link FILENAME on the current
+ target system as well as adding its symbols in GDB.
+
+ With the Nindy interface to an Intel 960 board, `load' will
+ download FILENAME to the 960 as well as adding its symbols in GDB.
+
+ When you select remote debugging to a Hitachi SH, H8/300, or
+ H8/500 board (*note GDB and Hitachi Microprocessors: Hitachi
+ Remote.), the `load' command downloads your program to the Hitachi
+ board and also opens it as the current executable target for GDB
+ on your host (like the `file' command).
+
+ `load' will not repeat if you press RET again after using it.
+
+`add-symbol-file FILENAME ADDRESS'
+`add-symbol-file FILENAME ADDRESS [ -readnow ] [ -mapped ]'
+ The `add-symbol-file' command reads additional symbol table
+ information from the file FILENAME. You would use this command
+ when FILENAME has been dynamically loaded (by some other means)
+ into the program that is running. ADDRESS should be the memory
+ address at which the file has been loaded; GDB cannot figure this
+ out for itself. You can specify ADDRESS as an expression.
+
+ The symbol table of the file FILENAME is added to the symbol table
+ originally read with the `symbol-file' command. You can use the
+ `add-symbol-file' command any number of times; the new symbol data
+ thus read keeps adding to the old. To discard all old symbol data
+ instead, use the `symbol-file' command.
+
+ `add-symbol-file' will not repeat if you press RET after using it.
+
+ You can use the `-mapped' and `-readnow' options just as with the
+ `symbol-file' command, to change how GDB manages the symbol table
+ information for FILENAME.
+
+`info files'
+`info target'
+ `info files' and `info target' are synonymous; both print the
+ current target (*note Specifying a Debugging Target: Targets.),
+ including the names of the executable and core dump files
+ currently in use by GDB, and the files from which symbols were
+ loaded. The command `help targets' lists all possible targets
+ rather than current ones.
+
+ All file-specifying commands allow both absolute and relative file
+names as arguments. GDB always converts the file name to an absolute
+path name and remembers it that way.
+
+ GDB supports SunOS, SVR4, and IBM RS/6000 shared libraries. GDB
+automatically loads symbol definitions from shared libraries when you
+use the `run' command, or when you examine a core file. (Before you
+issue the `run' command, GDB will not understand references to a
+function in a shared library, however--unless you are debugging a core
+file).
+
+`info share'
+`info sharedlibrary'
+ Print the names of the shared libraries which are currently loaded.
+
+`sharedlibrary REGEX'
+`share REGEX'
+ This is an obsolescent command; you can use it to explicitly load
+ shared object library symbols for files matching a Unix regular
+ expression, but as with files loaded automatically, it will only
+ load shared libraries required by your program for a core file or
+ after typing `run'. If REGEX is omitted all shared libraries
+ required by your program are loaded.
+
+
+File: gdb.info, Node: Symbol Errors, Prev: Files, Up: GDB Files
+
+Errors reading symbol files
+===========================
+
+ While reading a symbol file, GDB will occasionally encounter
+problems, such as symbol types it does not recognize, or known bugs in
+compiler output. By default, GDB does not notify you of such problems,
+since they are relatively common and primarily of interest to people
+debugging compilers. If you are interested in seeing information about
+ill-constructed symbol tables, you can either ask GDB to print only one
+message about each such type of problem, no matter how many times the
+problem occurs; or you can ask GDB to print more messages, to see how
+many times the problems occur, with the `set complaints' command (*note
+Optional warnings and messages: Messages/Warnings.).
+
+ The messages currently printed, and their meanings, include:
+
+`inner block not inside outer block in SYMBOL'
+ The symbol information shows where symbol scopes begin and end
+ (such as at the start of a function or a block of statements).
+ This error indicates that an inner scope block is not fully
+ contained in its outer scope blocks.
+
+ GDB circumvents the problem by treating the inner block as if it
+ had the same scope as the outer block. In the error message,
+ SYMBOL may be shown as "`(don't know)'" if the outer block is not a
+ function.
+
+`block at ADDRESS out of order'
+ The symbol information for symbol scope blocks should occur in
+ order of increasing addresses. This error indicates that it does
+ not do so.
+
+ GDB does not circumvent this problem, and will have trouble
+ locating symbols in the source file whose symbols it is reading.
+ (You can often determine what source file is affected by specifying
+ `set verbose on'. *Note Optional warnings and messages:
+ Messages/Warnings.)
+
+`bad block start address patched'
+ The symbol information for a symbol scope block has a start address
+ smaller than the address of the preceding source line. This is
+ known to occur in the SunOS 4.1.1 (and earlier) C compiler.
+
+ GDB circumvents the problem by treating the symbol scope block as
+ starting on the previous source line.
+
+`bad string table offset in symbol N'
+ Symbol number N contains a pointer into the string table which is
+ larger than the size of the string table.
+
+ GDB circumvents the problem by considering the symbol to have the
+ name `foo', which may cause other problems if many symbols end up
+ with this name.
+
+`unknown symbol type `0xNN''
+ The symbol information contains new data types that GDB does not
+ yet know how to read. `0xNN' is the symbol type of the
+ misunderstood information, in hexadecimal.
+
+ GDB circumvents the error by ignoring this symbol information.
+ This will usually allow your program to be debugged, though
+ certain symbols will not be accessible. If you encounter such a
+ problem and feel like debugging it, you can debug `gdb' with
+ itself, breakpoint on `complain', then go up to the function
+ `read_dbx_symtab' and examine `*bufp' to see the symbol.
+
+`stub type has NULL name'
+ GDB could not find the full definition for a struct or class.
+
+`const/volatile indicator missing (ok if using g++ v1.x), got...'
+ The symbol information for a C++ member function is missing some
+ information that recent versions of the compiler should have output
+ for it.
+
+`info mismatch between compiler and debugger'
+ GDB could not parse a type specification output by the compiler.
+
+
+File: gdb.info, Node: Targets, Next: Controlling GDB, Prev: GDB Files, Up: Top
+
+Specifying a Debugging Target
+*****************************
+
+ A "target" is the execution environment occupied by your program.
+Often, GDB runs in the same host environment as your program; in that
+case, the debugging target is specified as a side effect when you use
+the `file' or `core' commands. When you need more flexibility--for
+example, running GDB on a physically separate host, or controlling a
+standalone system over a serial port or a realtime system over a TCP/IP
+connection--you can use the `target' command to specify one of the
+target types configured for GDB (*note Commands for managing targets:
+Target Commands.).
+
+* Menu:
+
+* Active Targets:: Active targets
+* Target Commands:: Commands for managing targets
+* Remote:: Remote debugging
+
+
+File: gdb.info, Node: Active Targets, Next: Target Commands, Up: Targets
+
+Active targets
+==============
+
+ There are three classes of targets: processes, core files, and
+executable files. GDB can work concurrently on up to three active
+targets, one in each class. This allows you to (for example) start a
+process and inspect its activity without abandoning your work on a core
+file.
+
+ For example, if you execute `gdb a.out', then the executable file
+`a.out' is the only active target. If you designate a core file as
+well--presumably from a prior run that crashed and coredumped--then GDB
+has two active targets and will use them in tandem, looking first in
+the corefile target, then in the executable file, to satisfy requests
+for memory addresses. (Typically, these two classes of target are
+complementary, since core files contain only a program's read-write
+memory--variables and so on--plus machine status, while executable
+files contain only the program text and initialized data.)
+
+ When you type `run', your executable file becomes an active process
+target as well. When a process target is active, all GDB commands
+requesting memory addresses refer to that target; addresses in an
+active core file or executable file target are obscured while the
+process target is active.
+
+ Use the `core-file' and `exec-file' commands to select a new core
+file or executable target (*note Commands to specify files: Files.).
+To specify as a target a process that is already running, use the
+`attach' command (*note Debugging an already-running process: Attach.).
+
+
+File: gdb.info, Node: Target Commands, Next: Remote, Prev: Active Targets, Up: Targets
+
+Commands for managing targets
+=============================
+
+`target TYPE PARAMETERS'
+ Connects the GDB host environment to a target machine or process.
+ A target is typically a protocol for talking to debugging
+ facilities. You use the argument TYPE to specify the type or
+ protocol of the target machine.
+
+ Further PARAMETERS are interpreted by the target protocol, but
+ typically include things like device names or host names to connect
+ with, process numbers, and baud rates.
+
+ The `target' command will not repeat if you press RET again after
+ executing the command.
+
+`help target'
+ Displays the names of all targets available. To display targets
+ currently selected, use either `info target' or `info files'
+ (*note Commands to specify files: Files.).
+
+`help target NAME'
+ Describe a particular target, including any parameters necessary to
+ select it.
+
+ Here are some common targets (available, or not, depending on the GDB
+configuration):
+
+`target exec PROGRAM'
+ An executable file. `target exec PROGRAM' is the same as
+ `exec-file PROGRAM'.
+
+`target core FILENAME'
+ A core dump file. `target core FILENAME' is the same as
+ `core-file FILENAME'.
+
+`target remote DEV'
+ Remote serial target in GDB-specific protocol. The argument DEV
+ specifies what serial device to use for the connection (e.g.
+ `/dev/ttya'). *Note Remote debugging: Remote.
+
+`target sim'
+ CPU simulator. *Note Simulated CPU Target: Simulator.
+
+`target udi KEYWORD'
+ Remote AMD29K target, using the AMD UDI protocol. The KEYWORD
+ argument specifies which 29K board or simulator to use. *Note GDB
+ and the UDI protocol for AMD29K: UDI29K Remote.
+
+`target amd-eb DEV SPEED PROG'
+ Remote PC-resident AMD EB29K board, attached over serial lines.
+ dEV is the serial device, as for `target remote'; SPEED allows you
+ to specify the linespeed; and PROG is the name of the program to
+ be debugged, as it appears to DOS on the PC. *Note GDB with a
+ remote EB29K: EB29K Remote.
+
+`target hms'
+ A Hitachi SH, H8/300, or H8/500 board, attached via serial line to
+ your host. Use special commands `device' and `speed' to control
+ the serial line and the communications speed used. *Note GDB and
+ Hitachi Microprocessors: Hitachi Remote.
+
+`target nindy DEVICENAME'
+ An Intel 960 board controlled by a Nindy Monitor. DEVICENAME is
+ the name of the serial device to use for the connection, e.g.
+ `/dev/ttya'. *Note GDB with a remote i960 (Nindy): i960-Nindy
+ Remote.
+
+`target st2000 DEV SPEED'
+ A Tandem ST2000 phone switch, running Tandem's STDBUG protocol.
+ dEV is the name of the device attached to the ST2000 serial line;
+ SPEED is the communication line speed. The arguments are not used
+ if GDB is configured to connect to the ST2000 using TCP or Telnet.
+ *Note GDB with a Tandem ST2000: ST2000 Remote.
+
+`target vxworks MACHINENAME'
+ A VxWorks system, attached via TCP/IP. The argument MACHINENAME
+ is the target system's machine name or IP address. *Note GDB and
+ VxWorks: VxWorks Remote.
+
+ Different targets are available on different configurations of GDB;
+your configuration may have more or fewer targets.
+
+
+File: gdb.info, Node: Remote, Prev: Target Commands, Up: Targets
+
+Remote debugging
+================
+
+ If you are trying to debug a program running on a machine that
+cannot run GDB in the usual way, it is often useful to use remote
+debugging. For example, you might use remote debugging on an operating
+system kernel, or on a small system which does not have a general
+purpose operating system powerful enough to run a full-featured
+debugger.
+
+ Some configurations of GDB have special serial or TCP/IP interfaces
+to make this work with particular debugging targets. In addition, GDB
+comes with a generic serial protocol (specific to GDB, but not specific
+to any particular target system) which you can use if you write the
+remote stubs--the code that will run on the remote system to
+communicate with GDB.
+
+ Other remote targets may be available in your configuration of GDB;
+use `help targets' to list them.
+
+* Menu:
+
+
+* Remote Serial:: GDB remote serial protocol
+
+* i960-Nindy Remote:: GDB with a remote i960 (Nindy)
+
+* UDI29K Remote:: GDB and the UDI protocol for AMD29K
+* EB29K Remote:: GDB with a remote EB29K
+
+* VxWorks Remote:: GDB and VxWorks
+
+* ST2000 Remote:: GDB with a Tandem ST2000
+
+* Hitachi Remote:: GDB and Hitachi Microprocessors
+
+* MIPS Remote:: GDB and MIPS boards
+
+* Simulator:: Simulated CPU target
+
+
+File: gdb.info, Node: Remote Serial, Next: i960-Nindy Remote, Up: Remote
+
+The GDB remote serial protocol
+------------------------------
+
+ To debug a program running on another machine (the debugging
+"target" machine), you must first arrange for all the usual
+prerequisites for the program to run by itself. For example, for a C
+program, you need
+
+ 1. A startup routine to set up the C runtime environment; these
+ usually have a name like `crt0'. The startup routine may be
+ supplied by your hardware supplier, or you may have to write your
+ own.
+
+ 2. You probably need a C subroutine library to support your program's
+ subroutine calls, notably managing input and output.
+
+ 3. A way of getting your program to the other machine--for example, a
+ download program. These are often supplied by the hardware
+ manufacturer, but you may have to write your own from hardware
+ documentation.
+
+ The next step is to arrange for your program to use a serial port to
+communicate with the machine where GDB is running (the "host" machine).
+In general terms, the scheme looks like this:
+
+*On the host,*
+ GDB already understands how to use this protocol; when everything
+ else is set up, you can simply use the `target remote' command
+ (*note Specifying a Debugging Target: Targets.).
+
+*On the target,*
+ you must link with your program a few special-purpose subroutines
+ that implement the GDB remote serial protocol. The file
+ containing these subroutines is called a "debugging stub".
+
+ On certain remote targets, you can use an auxiliary program
+ `gdbserver' instead of linking a stub into your program. *Note
+ Using the `gdbserver' program: Server, for details.
+
+ The debugging stub is specific to the architecture of the remote
+machine; for example, use `sparc-stub.c' to debug programs on SPARC
+boards.
+
+ These working remote stubs are distributed with GDB:
+
+`sparc-stub.c'
+ For SPARC architectures.
+
+`m68k-stub.c'
+ For Motorola 680x0 architectures.
+
+`i386-stub.c'
+ For Intel 386 and compatible architectures.
+
+ The `README' file in the GDB distribution may list other recently
+added stubs.
+
+* Menu:
+
+* Stub Contents:: What the stub can do for you
+* Bootstrapping:: What you must do for the stub
+* Debug Session:: Putting it all together
+* Protocol:: Outline of the communication protocol
+
+* Server:: Using the `gdbserver' program
+
+
+File: gdb.info, Node: Stub Contents, Next: Bootstrapping, Up: Remote Serial
+
+What the stub can do for you
+----------------------------
+
+ The debugging stub for your architecture supplies these three
+subroutines:
+
+`set_debug_traps'
+ This routine arranges for `handle_exception' to run when your
+ program stops. You must call this subroutine explicitly near the
+ beginning of your program.
+
+`handle_exception'
+ This is the central workhorse, but your program never calls it
+ explicitly--the setup code arranges for `handle_exception' to run
+ when a trap is triggered.
+
+ `handle_exception' takes control when your program stops during
+ execution (for example, on a breakpoint), and mediates
+ communications with GDB on the host machine. This is where the
+ communications protocol is implemented; `handle_exception' acts as
+ the GDB representative on the target machine; it begins by sending
+ summary information on the state of your program, then continues
+ to execute, retrieving and transmitting any information GDB needs,
+ until you execute a GDB command that makes your program resume; at
+ that point, `handle_exception' returns control to your own code on
+ the target machine.
+
+`breakpoint'
+ Use this auxiliary subroutine to make your program contain a
+ breakpoint. Depending on the particular situation, this may be
+ the only way for GDB to get control. For instance, if your target
+ machine has some sort of interrupt button, you won't need to call
+ this; pressing the interrupt button will transfer control to
+ `handle_exception'--in effect, to GDB. On some machines, simply
+ receiving characters on the serial port may also trigger a trap;
+ again, in that situation, you don't need to call `breakpoint' from
+ your own program--simply running `target remote' from the host GDB
+ session will get control.
+
+ Call `breakpoint' if none of these is true, or if you simply want
+ to make certain your program stops at a predetermined point for the
+ start of your debugging session.
+
+
+File: gdb.info, Node: Bootstrapping, Next: Debug Session, Prev: Stub Contents, Up: Remote Serial
+
+What you must do for the stub
+-----------------------------
+
+ The debugging stubs that come with GDB are set up for a particular
+chip architecture, but they have no information about the rest of your
+debugging target machine. To allow the stub to work, you must supply
+these special low-level subroutines:
+
+`int getDebugChar()'
+ Write this subroutine to read a single character from the serial
+ port. It may be identical to `getchar' for your target system; a
+ different name is used to allow you to distinguish the two if you
+ wish.
+
+`void putDebugChar(int)'
+ Write this subroutine to write a single character to the serial
+ port. It may be identical to `putchar' for your target system; a
+ different name is used to allow you to distinguish the two if you
+ wish.
+
+`void exceptionHandler (int EXCEPTION_NUMBER, void *EXCEPTION_ADDRESS)'
+ Write this function to install EXCEPTION_ADDRESS in the exception
+ handling tables. You need to do this because the stub does not
+ have any way of knowing what the exception handling tables on your
+ target system are like (for example, the processor's table might
+ be in ROM, containing entries which point to a table in RAM).
+ eXCEPTION_NUMBER is the exception number which should be changed;
+ its meaning is architecture-dependent (for example, different
+ numbers might represent divide by zero, misaligned access, etc).
+ When this exception occurs, control should be transferred directly
+ to EXCEPTION_ADDRESS, and the processor state (stack, registers,
+ etc.) should be just as it is when a processor exception occurs.
+ So if you want to use a jump instruction to reach
+ EXCEPTION_ADDRESS, it should be a simple jump, not a jump to
+ subroutine.
+
+ For the 386, EXCEPTION_ADDRESS should be installed as an interrupt
+ gate so that interrupts are masked while the handler runs. The
+ gate should be at privilege level 0 (the most privileged level).
+ The SPARC and 68k stubs are able to mask interrupts themself
+ without help from `exceptionHandler'.
+
+`void flush_i_cache()'
+ Write this subroutine to flush the instruction cache, if any, on
+ your target machine. If there is no instruction cache, this
+ subroutine may be a no-op.
+
+ On target machines that have instruction caches, GDB requires this
+ function to make certain that the state of your program is stable.
+
+You must also make sure this library routine is available:
+
+`void *memset(void *, int, int)'
+ This is the standard library function `memset' that sets an area of
+ memory to a known value. If you have one of the free versions of
+ `libc.a', `memset' can be found there; otherwise, you must either
+ obtain it from your hardware manufacturer, or write your own.
+
+ If you do not use the GNU C compiler, you may need other standard
+library subroutines as well; this will vary from one stub to another,
+but in general the stubs are likely to use any of the common library
+subroutines which `gcc' generates as inline code.
+
+
+File: gdb.info, Node: Debug Session, Next: Protocol, Prev: Bootstrapping, Up: Remote Serial
+
+Putting it all together
+-----------------------
+
+ In summary, when your program is ready to debug, you must follow
+these steps.
+
+ 1. Make sure you have the supporting low-level routines (*note What
+ you must do for the stub: Bootstrapping.):
+ `getDebugChar', `putDebugChar',
+ `flush_i_cache', `memset', `exceptionHandler'.
+
+ 2. Insert these lines near the top of your program:
+
+ set_debug_traps();
+ breakpoint();
+
+ 3. For the 680x0 stub only, you need to provide a variable called
+ `exceptionHook'. Normally you just use
+
+ void (*exceptionHook)() = 0;
+
+ but if before calling `set_debug_traps', you set it to point to a
+ function in your program, that function is called when `GDB'
+ continues after stopping on a trap (for example, bus error). The
+ function indicated by `exceptionHook' is called with one
+ parameter: an `int' which is the exception number.
+
+ 4. Compile and link together: your program, the GDB debugging stub for
+ your target architecture, and the supporting subroutines.
+
+ 5. Make sure you have a serial connection between your target machine
+ and the GDB host, and identify the serial port used for this on
+ the host.
+
+ 6. Download your program to your target machine (or get it there by
+ whatever means the manufacturer provides), and start it.
+
+ 7. To start remote debugging, run GDB on the host machine, and specify
+ as an executable file the program that is running in the remote
+ machine. This tells GDB how to find your program's symbols and
+ the contents of its pure text.
+
+ Then establish communication using the `target remote' command.
+ Its argument specifies how to communicate with the target
+ machine--either via a devicename attached to a direct serial line,
+ or a TCP port (usually to a terminal server which in turn has a
+ serial line to the target). For example, to use a serial line
+ connected to the device named `/dev/ttyb':
+
+ target remote /dev/ttyb
+
+ To use a TCP connection, use an argument of the form `HOST:port'.
+ For example, to connect to port 2828 on a terminal server named
+ `manyfarms':
+
+ target remote manyfarms:2828
+
+ Now you can use all the usual commands to examine and change data
+and to step and continue the remote program.
+
+ To resume the remote program and stop debugging it, use the `detach'
+command.
+
+ Whenever GDB is waiting for the remote program, if you type the
+interrupt character (often C-C), GDB attempts to stop the program.
+This may or may not succeed, depending in part on the hardware and the
+serial drivers the remote system uses. If you type the interrupt
+character once again, GDB displays this prompt:
+
+ Interrupted while waiting for the program.
+ Give up (and stop debugging it)? (y or n)
+
+ If you type `y', GDB abandons the remote debugging session. (If you
+decide you want to try again later, you can use `target remote' again
+to connect once more.) If you type `n', GDB goes back to waiting.
+
+
+File: gdb.info, Node: Protocol, Next: Server, Prev: Debug Session, Up: Remote Serial
+
+Outline of the communication protocol
+-------------------------------------
+
+ The stub files provided with GDB implement the target side of the
+communication protocol, and the GDB side is implemented in the GDB
+source file `remote.c'. Normally, you can simply allow these
+subroutines to communicate, and ignore the details. (If you're
+implementing your own stub file, you can still ignore the details: start
+with one of the existing stub files. `sparc-stub.c' is the best
+organized, and therefore the easiest to read.)
+
+ However, there may be occasions when you need to know something about
+the protocol--for example, if there is only one serial port to your
+target machine, you might want your program to do something special if
+it recognizes a packet meant for GDB.
+
+ All GDB commands and responses (other than acknowledgements, which
+are single characters) are sent as a packet which includes a checksum.
+A packet is introduced with the character `$', and ends with the
+character `#' followed by a two-digit checksum:
+
+ $PACKET INFO#CHECKSUM
+
+CHECKSUM is computed as the modulo 256 sum of the PACKET INFO
+characters.
+
+ When either the host or the target machine receives a packet, the
+first response expected is an acknowledgement: a single character,
+either `+' (to indicate the package was received correctly) or `-' (to
+request retransmission).
+
+ The host (GDB) sends commands, and the target (the debugging stub
+incorporated in your program) sends data in response. The target also
+sends data when your program stops.
+
+ Command packets are distinguished by their first character, which
+identifies the kind of command.
+
+ These are the commands currently supported:
+
+`g'
+ Requests the values of CPU registers.
+
+`G'
+ Sets the values of CPU registers.
+
+`mADDR,COUNT'
+ Read COUNT bytes at location ADDR.
+
+`MADDR,COUNT:...'
+ Write COUNT bytes at location ADDR.
+
+`c'
+`cADDR'
+ Resume execution at the current address (or at ADDR if supplied).
+
+`s'
+`sADDR'
+ Step the target program for one instruction, from either the
+ current program counter or from ADDR if supplied.
+
+`k'
+ Kill the target program.
+
+`?'
+ Report the most recent signal. To allow you to take advantage of
+ the GDB signal handling commands, one of the functions of the
+ debugging stub is to report CPU traps as the corresponding POSIX
+ signal values.
+
+ If you have trouble with the serial connection, you can use the
+command `set remotedebug'. This makes GDB report on all packets sent
+back and forth across the serial line to the remote machine. The
+packet-debugging information is printed on the GDB standard output
+stream. `set remotedebug off' turns it off, and `show remotedebug'
+will show you its current state.
+
+
+File: gdb.info, Node: Server, Prev: Protocol, Up: Remote Serial
+
+Using the `gdbserver' program
+-----------------------------
+
+ `gdbserver' is a control program for Unix-like systems, which allows
+you to connect your program with a remote GDB via `target remote'--but
+without linking in the usual debugging stub.
+
+ `gdbserver' is not a complete replacement for the debugging stubs,
+because it requires essentially the same operating-system facilities
+that GDB itself does. In fact, a system that can run `gdbserver' to
+connect to a remote GDB could also run GDBN locally! `gdbserver' is
+sometimes useful nevertheless, because it is a much smaller program
+than GDB itself. It is also easier to port than all of GDBN, so you
+may be able to get started more quickly on a new system by using
+`gdbserver'.
+
+ GDB and `gdbserver' communicate via either a serial line or a TCP
+connection, using the standard GDB remote serial protocol.
+
+*On the target,*
+ you need to have a copy of the program you want to debug.
+ `gdbserver' does not need your program's symbol table, so you can
+ strip the program if necessary to save space. GDB on the host
+ system does all the symbol handling.
+
+ To use the server, you must tell it how to communicate with {No
+ Value For "GDB"}; the name of your program; and the arguments for
+ your program. The syntax is:
+
+ target> gdbserver COMM PROGRAM [ ARGS ... ]
+
+ COMM is either a device name (to use a serial line) or a TCP
+ hostname and portnumber. For example, to debug emacs with the
+ argument `foo.txt' and communicate with GDB over the serial port
+ `/dev/com1':
+
+ target> gdbserver /dev/com1 emacs foo.txt
+
+ `gdbserver' waits passively for the host GDB to communicate with
+ it.
+
+ To use a TCP connection instead of a serial line:
+
+ target> gdbserver host:2345 emacs foo.txt
+
+ The only difference from the previous example is the first
+ argument, specifying that you are communicating with the host GDB
+ via TCP. The `host:2345' argument means that `gdbserver' is to
+ expect a TCP connection from machine `host' to local TCP port 2345.
+ (Currently, the `host' part is ignored.) You can choose any number
+ you want for the port number as long as it does not conflict with
+ any TCP ports already in use on the target system.(1) You must use
+ the same port number with the host GDB `target remote' command.
+
+*On the host,*
+ you need an unstripped copy of your program, since GDB needs
+ symbols and debugging information. Start up GDB as usual, using
+ the name of the local copy of your program as the first argument.
+ (You may also need the `--baud' option if the serial line is
+ running at anything other than 9600 bps.) After that, use `target
+ remote' to establish communications with `gdbserver'. Its
+ argument is either a device name (usually a serial device, like
+ `/dev/ttyb'), or a TCP port descriptof in the form `HOST:PORT'.
+ For example:
+
+ (gdb) target remote /dev/ttyb
+
+ communicates with the server via serial line `/dev/ttyb', and
+
+ (gdb) target remote the-target:2345
+
+ communicates via a TCP connection to port 2345 on host
+ `the-target'. For TCP connections, you must start up `gdbserver'
+ prior to using the `target remote' command. Otherwise you may get
+ an error whose text depends on the host system, but which usually
+ looks something like `Connection refused'.
+
+ ---------- Footnotes ----------
+
+ (1) If you choose a port number that conflicts with another
+service, `gdbserver' prints an error message and exits.
+
+
+File: gdb.info, Node: i960-Nindy Remote, Next: UDI29K Remote, Prev: Remote Serial, Up: Remote
+
+GDB with a remote i960 (Nindy)
+------------------------------
+
+ "Nindy" is a ROM Monitor program for Intel 960 target systems. When
+GDB is configured to control a remote Intel 960 using Nindy, you can
+tell GDB how to connect to the 960 in several ways:
+
+ * Through command line options specifying serial port, version of the
+ Nindy protocol, and communications speed;
+
+ * By responding to a prompt on startup;
+
+ * By using the `target' command at any point during your GDB
+ session. *Note Commands for managing targets: Target Commands.
+
+* Menu:
+
+* Nindy Startup:: Startup with Nindy
+* Nindy Options:: Options for Nindy
+* Nindy Reset:: Nindy reset command
+
+
+File: gdb.info, Node: Nindy Startup, Next: Nindy Options, Up: i960-Nindy Remote
+
+Startup with Nindy
+------------------
+
+ If you simply start `gdb' without using any command-line options,
+you are prompted for what serial port to use, *before* you reach the
+ordinary GDB prompt:
+
+ Attach /dev/ttyNN -- specify NN, or "quit" to quit:
+
+Respond to the prompt with whatever suffix (after `/dev/tty')
+identifies the serial port you want to use. You can, if you choose,
+simply start up with no Nindy connection by responding to the prompt
+with an empty line. If you do this and later wish to attach to Nindy,
+use `target' (*note Commands for managing targets: Target Commands.).
+
diff --git a/gnu/usr.bin/gdb/doc/gdb.info-6 b/gnu/usr.bin/gdb/doc/gdb.info-6
new file mode 100644
index 0000000..8a746fd
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/gdb.info-6
@@ -0,0 +1,1220 @@
+This is Info file ./gdb.info, produced by Makeinfo-1.52 from the input
+file gdb.texinfo.
+
+START-INFO-DIR-ENTRY
+* Gdb:: The GNU debugger.
+END-INFO-DIR-ENTRY
+ This file documents the GNU debugger GDB.
+
+ This is Edition 4.09, August 1993, of `Debugging with GDB: the GNU
+Source-Level Debugger' for GDB Version 4.11.
+
+ Copyright (C) 1988, '89, '90, '91, '92, '93 Free Software
+Foundation, Inc.
+
+ Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of
+this manual under the conditions for verbatim copying, provided also
+that the entire resulting derived work is distributed under the terms
+of a permission notice identical to this one.
+
+ Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions.
+
+
+File: gdb.info, Node: Nindy Options, Next: Nindy Reset, Prev: Nindy Startup, Up: i960-Nindy Remote
+
+Options for Nindy
+-----------------
+
+ These are the startup options for beginning your GDB session with a
+Nindy-960 board attached:
+
+`-r PORT'
+ Specify the serial port name of a serial interface to be used to
+ connect to the target system. This option is only available when
+ GDB is configured for the Intel 960 target architecture. You may
+ specify PORT as any of: a full pathname (e.g. `-r /dev/ttya'), a
+ device name in `/dev' (e.g. `-r ttya'), or simply the unique
+ suffix for a specific `tty' (e.g. `-r a').
+
+`-O'
+ (An uppercase letter "O", not a zero.) Specify that GDB should use
+ the "old" Nindy monitor protocol to connect to the target system.
+ This option is only available when GDB is configured for the Intel
+ 960 target architecture.
+
+ *Warning:* if you specify `-O', but are actually trying to
+ connect to a target system that expects the newer protocol,
+ the connection fails, appearing to be a speed mismatch. GDB
+ repeatedly attempts to reconnect at several different line
+ speeds. You can abort this process with an interrupt.
+
+`-brk'
+ Specify that GDB should first send a `BREAK' signal to the target
+ system, in an attempt to reset it, before connecting to a Nindy
+ target.
+
+ *Warning:* Many target systems do not have the hardware that
+ this requires; it only works with a few boards.
+
+ The standard `-b' option controls the line speed used on the serial
+port.
+
+
+File: gdb.info, Node: Nindy Reset, Prev: Nindy Options, Up: i960-Nindy Remote
+
+Nindy reset command
+-------------------
+
+`reset'
+ For a Nindy target, this command sends a "break" to the remote
+ target system; this is only useful if the target has been equipped
+ with a circuit to perform a hard reset (or some other interesting
+ action) when a break is detected.
+
+
+File: gdb.info, Node: UDI29K Remote, Next: EB29K Remote, Prev: i960-Nindy Remote, Up: Remote
+
+GDB and the UDI protocol for AMD29K
+-----------------------------------
+
+ GDB supports AMD's UDI ("Universal Debugger Interface") protocol for
+debugging the a29k processor family. To use this configuration with
+AMD targets running the MiniMON monitor, you need the program `MONTIP',
+available from AMD at no charge. You can also use GDB with the UDI
+conformant a29k simulator program `ISSTIP', also available from AMD.
+
+`target udi KEYWORD'
+ Select the UDI interface to a remote a29k board or simulator, where
+ KEYWORD is an entry in the AMD configuration file `udi_soc'. This
+ file contains keyword entries which specify parameters used to
+ connect to a29k targets. If the `udi_soc' file is not in your
+ working directory, you must set the environment variable `UDICONF'
+ to its pathname.
+
+
+File: gdb.info, Node: EB29K Remote, Next: VxWorks Remote, Prev: UDI29K Remote, Up: Remote
+
+GDB and the EBMON protocol for AMD29K
+-------------------------------------
+
+ AMD distributes a 29K development board meant to fit in a PC,
+together with a DOS-hosted monitor program called `EBMON'. As a
+shorthand term, this development system is called the "EB29K". To use
+GDB from a Unix system to run programs on the EB29K board, you must
+first connect a serial cable between the PC (which hosts the EB29K
+board) and a serial port on the Unix system. In the following, we
+assume you've hooked the cable between the PC's `COM1' port and
+`/dev/ttya' on the Unix system.
+
+* Menu:
+
+* Comms (EB29K):: Communications setup
+* gdb-EB29K:: EB29K cross-debugging
+* Remote Log:: Remote log
+
+
+File: gdb.info, Node: Comms (EB29K), Next: gdb-EB29K, Up: EB29K Remote
+
+Communications setup
+--------------------
+
+ The next step is to set up the PC's port, by doing something like
+this in DOS on the PC:
+
+ C:\> MODE com1:9600,n,8,1,none
+
+This example--run on an MS DOS 4.0 system--sets the PC port to 9600
+bps, no parity, eight data bits, one stop bit, and no "retry" action;
+you must match the communications parameters when establishing the Unix
+end of the connection as well.
+
+ To give control of the PC to the Unix side of the serial line, type
+the following at the DOS console:
+
+ C:\> CTTY com1
+
+(Later, if you wish to return control to the DOS console, you can use
+the command `CTTY con'--but you must send it over the device that had
+control, in our example over the `COM1' serial line).
+
+ From the Unix host, use a communications program such as `tip' or
+`cu' to communicate with the PC; for example,
+
+ cu -s 9600 -l /dev/ttya
+
+The `cu' options shown specify, respectively, the linespeed and the
+serial port to use. If you use `tip' instead, your command line may
+look something like the following:
+
+ tip -9600 /dev/ttya
+
+Your system may require a different name where we show `/dev/ttya' as
+the argument to `tip'. The communications parameters, including which
+port to use, are associated with the `tip' argument in the "remote"
+descriptions file--normally the system table `/etc/remote'.
+
+ Using the `tip' or `cu' connection, change the DOS working directory
+to the directory containing a copy of your 29K program, then start the
+PC program `EBMON' (an EB29K control program supplied with your board
+by AMD). You should see an initial display from `EBMON' similar to the
+one that follows, ending with the `EBMON' prompt `#'--
+
+ C:\> G:
+
+ G:\> CD \usr\joe\work29k
+
+ G:\USR\JOE\WORK29K> EBMON
+ Am29000 PC Coprocessor Board Monitor, version 3.0-18
+ Copyright 1990 Advanced Micro Devices, Inc.
+ Written by Gibbons and Associates, Inc.
+
+ Enter '?' or 'H' for help
+
+ PC Coprocessor Type = EB29K
+ I/O Base = 0x208
+ Memory Base = 0xd0000
+
+ Data Memory Size = 2048KB
+ Available I-RAM Range = 0x8000 to 0x1fffff
+ Available D-RAM Range = 0x80002000 to 0x801fffff
+
+ PageSize = 0x400
+ Register Stack Size = 0x800
+ Memory Stack Size = 0x1800
+
+ CPU PRL = 0x3
+ Am29027 Available = No
+ Byte Write Available = Yes
+
+ # ~.
+
+ Then exit the `cu' or `tip' program (done in the example by typing
+`~.' at the `EBMON' prompt). `EBMON' will keep running, ready for GDB
+to take over.
+
+ For this example, we've assumed what is probably the most convenient
+way to make sure the same 29K program is on both the PC and the Unix
+system: a PC/NFS connection that establishes "drive `G:'" on the PC as
+a file system on the Unix host. If you do not have PC/NFS or something
+similar connecting the two systems, you must arrange some other
+way--perhaps floppy-disk transfer--of getting the 29K program from the
+Unix system to the PC; GDB will *not* download it over the serial line.
+
+
+File: gdb.info, Node: gdb-EB29K, Next: Remote Log, Prev: Comms (EB29K), Up: EB29K Remote
+
+EB29K cross-debugging
+---------------------
+
+ Finally, `cd' to the directory containing an image of your 29K
+program on the Unix system, and start GDB--specifying as argument the
+name of your 29K program:
+
+ cd /usr/joe/work29k
+ gdb myfoo
+
+ Now you can use the `target' command:
+
+ target amd-eb /dev/ttya 9600 MYFOO
+
+In this example, we've assumed your program is in a file called
+`myfoo'. Note that the filename given as the last argument to `target
+amd-eb' should be the name of the program as it appears to DOS. In our
+example this is simply `MYFOO', but in general it can include a DOS
+path, and depending on your transfer mechanism may not resemble the
+name on the Unix side.
+
+ At this point, you can set any breakpoints you wish; when you are
+ready to see your program run on the 29K board, use the GDB command
+`run'.
+
+ To stop debugging the remote program, use the GDB `detach' command.
+
+ To return control of the PC to its console, use `tip' or `cu' once
+again, after your GDB session has concluded, to attach to `EBMON'. You
+can then type the command `q' to shut down `EBMON', returning control
+to the DOS command-line interpreter. Type `CTTY con' to return command
+input to the main DOS console, and type `~.' to leave `tip' or `cu'.
+
+
+File: gdb.info, Node: Remote Log, Prev: gdb-EB29K, Up: EB29K Remote
+
+Remote log
+----------
+
+ The `target amd-eb' command creates a file `eb.log' in the current
+working directory, to help debug problems with the connection.
+`eb.log' records all the output from `EBMON', including echoes of the
+commands sent to it. Running `tail -f' on this file in another window
+often helps to understand trouble with `EBMON', or unexpected events on
+the PC side of the connection.
+
+
+File: gdb.info, Node: ST2000 Remote, Next: Hitachi Remote, Prev: VxWorks Remote, Up: Remote
+
+GDB with a Tandem ST2000
+------------------------
+
+ To connect your ST2000 to the host system, see the manufacturer's
+manual. Once the ST2000 is physically attached, you can run
+
+ target st2000 DEV SPEED
+
+to establish it as your debugging environment. DEV is normally the
+name of a serial device, such as `/dev/ttya', connected to the ST2000
+via a serial line. You can instead specify DEV as a TCP connection
+(for example, to a serial line attached via a terminal concentrator)
+using the syntax `HOSTNAME:PORTNUMBER'.
+
+ The `load' and `attach' commands are *not* defined for this target;
+you must load your program into the ST2000 as you normally would for
+standalone operation. GDB will read debugging information (such as
+symbols) from a separate, debugging version of the program available on
+your host computer.
+
+ These auxiliary GDB commands are available to help you with the
+ST2000 environment:
+
+`st2000 COMMAND'
+ Send a COMMAND to the STDBUG monitor. See the manufacturer's
+ manual for available commands.
+
+`connect'
+ Connect the controlling terminal to the STDBUG command monitor.
+ When you are done interacting with STDBUG, typing either of two
+ character sequences will get you back to the GDB command prompt:
+ `RET~.' (Return, followed by tilde and period) or `RET~C-d'
+ (Return, followed by tilde and control-D).
+
+
+File: gdb.info, Node: VxWorks Remote, Next: ST2000 Remote, Prev: EB29K Remote, Up: Remote
+
+GDB and VxWorks
+---------------
+
+ GDB enables developers to spawn and debug tasks running on networked
+VxWorks targets from a Unix host. Already-running tasks spawned from
+the VxWorks shell can also be debugged. GDB uses code that runs on
+both the Unix host and on the VxWorks target. The program `gdb' is
+installed and executed on the Unix host. (It may be installed with the
+name `vxgdb', to distinguish it from a GDB for debugging programs on
+the host itself.)
+
+ The following information on connecting to VxWorks was current when
+this manual was produced; newer releases of VxWorks may use revised
+procedures.
+
+ The remote debugging interface (RDB) routines are installed and
+executed on the VxWorks target. These routines are included in the
+VxWorks library `rdb.a' and are incorporated into the system image when
+source-level debugging is enabled in the VxWorks configuration.
+
+ If you wish, you can define `INCLUDE_RDB' in the VxWorks
+configuration file `configAll.h' to include the RDB interface routines
+and spawn the source debugging task `tRdbTask' when VxWorks is booted.
+For more information on configuring and remaking VxWorks, see the
+manufacturer's manual.
+
+ Once you have included the RDB interface in your VxWorks system image
+and set your Unix execution search path to find GDB, you are ready to
+run GDB. From your Unix host, run `gdb' (or `vxgdb', depending on your
+installation).
+
+ GDB comes up showing the prompt:
+
+ (vxgdb)
+
+* Menu:
+
+* VxWorks Connection:: Connecting to VxWorks
+* VxWorks Download:: VxWorks download
+* VxWorks Attach:: Running tasks
+
+
+File: gdb.info, Node: VxWorks Connection, Next: VxWorks Download, Up: VxWorks Remote
+
+Connecting to VxWorks
+---------------------
+
+ The GDB command `target' lets you connect to a VxWorks target on the
+network. To connect to a target whose host name is "`tt'", type:
+
+ (vxgdb) target vxworks tt
+
+ GDB displays messages like these:
+
+ Attaching remote machine across net...
+ Connected to tt.
+
+ GDB then attempts to read the symbol tables of any object modules
+loaded into the VxWorks target since it was last booted. GDB locates
+these files by searching the directories listed in the command search
+path (*note Your program's environment: Environment.); if it fails to
+find an object file, it displays a message such as:
+
+ prog.o: No such file or directory.
+
+ When this happens, add the appropriate directory to the search path
+with the GDB command `path', and execute the `target' command again.
+
+
+File: gdb.info, Node: VxWorks Download, Next: VxWorks Attach, Prev: VxWorks Connection, Up: VxWorks Remote
+
+VxWorks download
+----------------
+
+ If you have connected to the VxWorks target and you want to debug an
+object that has not yet been loaded, you can use the GDB `load' command
+to download a file from Unix to VxWorks incrementally. The object file
+given as an argument to the `load' command is actually opened twice:
+first by the VxWorks target in order to download the code, then by GDB
+in order to read the symbol table. This can lead to problems if the
+current working directories on the two systems differ. If both systems
+have NFS mounted the same filesystems, you can avoid these problems by
+using absolute paths. Otherwise, it is simplest to set the working
+directory on both systems to the directory in which the object file
+resides, and then to reference the file by its name, without any path.
+For instance, a program `prog.o' may reside in `VXPATH/vw/demo/rdb' in
+VxWorks and in `HOSTPATH/vw/demo/rdb' on the host. To load this
+program, type this on VxWorks:
+
+ -> cd "VXPATH/vw/demo/rdb"
+
+ Then, in GDB, type:
+
+ (vxgdb) cd HOSTPATH/vw/demo/rdb
+ (vxgdb) load prog.o
+
+ GDB displays a response similar to this:
+
+ Reading symbol data from wherever/vw/demo/rdb/prog.o... done.
+
+ You can also use the `load' command to reload an object module after
+editing and recompiling the corresponding source file. Note that this
+will cause GDB to delete all currently-defined breakpoints,
+auto-displays, and convenience variables, and to clear the value
+history. (This is necessary in order to preserve the integrity of
+debugger data structures that reference the target system's symbol
+table.)
+
+
+File: gdb.info, Node: VxWorks Attach, Prev: VxWorks Download, Up: VxWorks Remote
+
+Running tasks
+-------------
+
+ You can also attach to an existing task using the `attach' command as
+follows:
+
+ (vxgdb) attach TASK
+
+where TASK is the VxWorks hexadecimal task ID. The task can be running
+or suspended when you attach to it. If running, it will be suspended at
+the time of attachment.
+
+
+File: gdb.info, Node: Hitachi Remote, Next: MIPS Remote, Prev: ST2000 Remote, Up: Remote
+
+GDB and Hitachi Microprocessors
+-------------------------------
+
+ GDB needs to know these things to talk to your Hitachi SH, H8/300,
+or H8/500:
+
+ 1. that you want to use `target hms', the remote debugging interface
+ for Hitachi microprocessors (this is the default when GDB is
+ configured specifically for the Hitachi SH, H8/300, or H8/500);
+
+ 2. what serial device connects your host to your Hitachi board (the
+ first serial device available on your host is the default);
+
+
+ Use the special `gdb' command `device PORT' if you need to
+explicitly set the serial device. The default PORT is the first
+available port on your host. This is only necessary on Unix hosts,
+where it is typically something like `/dev/ttya'.
+
+ `gdb' has another special command to set the communications speed:
+`speed BPS'. This command also is only used from Unix hosts; on DOS
+hosts, set the line speed as usual from outside GDB with the DOS `mode'
+command (for instance, `mode com2:9600,n,8,1,p' for a 9600 bps
+connection).
+
+ The `device' and `speed' commands are available only when you use a
+Unix host to debug your Hitachi microprocessor programs. If you use a
+DOS host, GDB depends on an auxiliary terminate-and-stay-resident
+program called `asynctsr' to communicate with the development board
+through a PC serial port. You must also use the DOS `mode' command to
+set up the serial port on the DOS side.
+
+
+File: gdb.info, Node: MIPS Remote, Next: Simulator, Prev: Hitachi Remote, Up: Remote
+
+GDB and remote MIPS boards
+--------------------------
+
+ GDB can use the MIPS remote debugging protocol to talk to a MIPS
+board attached to a serial line. This is available when you configure
+GDB with `--target=mips-idt-ecoff'.
+
+ To run a program on the board, start up `gdb' with the name of your
+program as the argument. To connect to the board, use the command
+`target mips PORT', where PORT is the name of the serial port connected
+to the board. If the program has not already been downloaded to the
+board, you may use the `load' command to download it. You can then use
+all the usual GDB commands.
+
+ You can also specify PORT as a TCP connection (for instance, to a
+serial line managed by a terminal concentrator), using the syntax
+`HOSTNAME:PORTNUMBER'.
+
+ You can see some debugging information about communications with the
+board by setting the `remotedebug' variable. If you set it to 1 using
+`set remotedebug 1' every packet will be displayed. If you set it to 2
+every character will be displayed. You can check the current value at
+any time with the command `show remotedebug'.
+
+ You can control the timeout used while waiting for a packet, in the
+MIPS remote protocol, with the `set timeout SECONDS' command. The
+default is 5 seconds. Similarly, you can control the timeout used while
+waiting for an acknowledgement of a packet with the `set
+retransmit-timeout SECONDS' command. The default is 3 seconds. You
+can inspect both values with `show timeout' and `show
+retransmit-timeout'. (These commands are *only* available when GDB is
+configured for `--target=mips-idt-ecoff'.)
+
+ If your target board does not support the MIPS floating point
+coprocessor, you should use the command `set mipsfpu off' (you may wish
+to put this in your .gdbinit file). This tells GDB how to find the
+return value of functions which return floating point values. It also
+allows GDB to avoid saving the floating point registers when calling
+functions on the board.
+
+
+File: gdb.info, Node: Simulator, Prev: MIPS Remote, Up: Remote
+
+Simulated CPU target
+--------------------
+
+ For some configurations, GDB includes a CPU simulator that you can
+use instead of a hardware CPU to debug your programs. Currently, a
+simulator is available when GDB is configured to debug Zilog Z8000 or
+Hitachi microprocessor targets.
+
+ For the Z8000 family, `target sim' simulates either the Z8002 (the
+unsegmented variant of the Z8000 architecture) or the Z8001 (the
+segmented variant). The simulator recognizes which architecture is
+appropriate by inspecting the object code.
+
+`target sim'
+ Debug programs on a simulated CPU (which CPU depends on the GDB
+ configuration)
+
+After specifying this target, you can debug programs for the simulated
+CPU in the same style as programs for your host computer; use the
+`file' command to load a new program image, the `run' command to run
+your program, and so on.
+
+ As well as making available all the usual machine registers (see
+`info reg'), this debugging target provides three additional items of
+information as specially named registers:
+
+`cycles'
+ Counts clock-ticks in the simulator.
+
+`insts'
+ Counts instructions run in the simulator.
+
+`time'
+ Execution time in 60ths of a second.
+
+ You can refer to these values in GDB expressions with the usual
+conventions; for example, `b fputc if $cycles>5000' sets a conditional
+breakpoint that will suspend only after at least 5000 simulated clock
+ticks.
+
+
+File: gdb.info, Node: Controlling GDB, Next: Sequences, Prev: Targets, Up: Top
+
+Controlling GDB
+***************
+
+ You can alter the way GDB interacts with you by using the `set'
+command. For commands controlling how GDB displays data, *note Print
+settings: Print Settings.; other settings are described here.
+
+* Menu:
+
+* Prompt:: Prompt
+* Editing:: Command editing
+* History:: Command history
+* Screen Size:: Screen size
+* Numbers:: Numbers
+* Messages/Warnings:: Optional warnings and messages
+
+
+File: gdb.info, Node: Prompt, Next: Editing, Up: Controlling GDB
+
+Prompt
+======
+
+ GDB indicates its readiness to read a command by printing a string
+called the "prompt". This string is normally `(gdb)'. You can change
+the prompt string with the `set prompt' command. For instance, when
+debugging GDB with GDB, it is useful to change the prompt in one of the
+GDB sessions so that you can always tell which one you are talking to.
+
+`set prompt NEWPROMPT'
+ Directs GDB to use NEWPROMPT as its prompt string henceforth.
+
+`show prompt'
+ Prints a line of the form: `Gdb's prompt is: YOUR-PROMPT'
+
+
+File: gdb.info, Node: Editing, Next: History, Prev: Prompt, Up: Controlling GDB
+
+Command editing
+===============
+
+ GDB reads its input commands via the "readline" interface. This GNU
+library provides consistent behavior for programs which provide a
+command line interface to the user. Advantages are `emacs'-style or
+`vi'-style inline editing of commands, `csh'-like history substitution,
+and a storage and recall of command history across debugging sessions.
+
+ You may control the behavior of command line editing in GDB with the
+command `set'.
+
+`set editing'
+`set editing on'
+ Enable command line editing (enabled by default).
+
+`set editing off'
+ Disable command line editing.
+
+`show editing'
+ Show whether command line editing is enabled.
+
+
+File: gdb.info, Node: History, Next: Screen Size, Prev: Editing, Up: Controlling GDB
+
+Command history
+===============
+
+ GDB can keep track of the commands you type during your debugging
+sessions, so that you can be certain of precisely what happened. Use
+these commands to manage the GDB command history facility.
+
+`set history filename FNAME'
+ Set the name of the GDB command history file to FNAME. This is
+ the file from which GDB will read an initial command history list
+ or to which it will write this list when it exits. This list is
+ accessed through history expansion or through the history command
+ editing characters listed below. This file defaults to the value
+ of the environment variable `GDBHISTFILE', or to `./.gdb_history'
+ if this variable is not set.
+
+`set history save'
+`set history save on'
+ Record command history in a file, whose name may be specified with
+ the `set history filename' command. By default, this option is
+ disabled.
+
+`set history save off'
+ Stop recording command history in a file.
+
+`set history size SIZE'
+ Set the number of commands which GDB will keep in its history list.
+ This defaults to the value of the environment variable `HISTSIZE',
+ or to 256 if this variable is not set.
+
+ History expansion assigns special meaning to the character `!'.
+
+ Since `!' is also the logical not operator in C, history expansion
+is off by default. If you decide to enable history expansion with the
+`set history expansion on' command, you may sometimes need to follow
+`!' (when it is used as logical not, in an expression) with a space or
+a tab to prevent it from being expanded. The readline history
+facilities will not attempt substitution on the strings `!=' and `!(',
+even when history expansion is enabled.
+
+ The commands to control history expansion are:
+
+`set history expansion on'
+`set history expansion'
+ Enable history expansion. History expansion is off by default.
+
+`set history expansion off'
+ Disable history expansion.
+
+ The readline code comes with more complete documentation of
+ editing and history expansion features. Users unfamiliar with
+ `emacs' or `vi' may wish to read it.
+
+`show history'
+`show history filename'
+`show history save'
+`show history size'
+`show history expansion'
+ These commands display the state of the GDB history parameters.
+ `show history' by itself displays all four states.
+
+`show commands'
+ Display the last ten commands in the command history.
+
+`show commands N'
+ Print ten commands centered on command number N.
+
+`show commands +'
+ Print ten commands just after the commands last printed.
+
+
+File: gdb.info, Node: Screen Size, Next: Numbers, Prev: History, Up: Controlling GDB
+
+Screen size
+===========
+
+ Certain commands to GDB may produce large amounts of information
+output to the screen. To help you read all of it, GDB pauses and asks
+you for input at the end of each page of output. Type RET when you
+want to continue the output, or `q' to discard the remaining output.
+Also, the screen width setting determines when to wrap lines of output.
+Depending on what is being printed, GDB tries to break the line at a
+readable place, rather than simply letting it overflow onto the
+following line.
+
+ Normally GDB knows the size of the screen from the termcap data base
+together with the value of the `TERM' environment variable and the
+`stty rows' and `stty cols' settings. If this is not correct, you can
+override it with the `set height' and `set width' commands:
+
+`set height LPP'
+`show height'
+`set width CPL'
+`show width'
+ These `set' commands specify a screen height of LPP lines and a
+ screen width of CPL characters. The associated `show' commands
+ display the current settings.
+
+ If you specify a height of zero lines, GDB will not pause during
+ output no matter how long the output is. This is useful if output
+ is to a file or to an editor buffer.
+
+ Likewise, you can specify `set width 0' to prevent GDB from
+ wrapping its output.
+
+
+File: gdb.info, Node: Numbers, Next: Messages/Warnings, Prev: Screen Size, Up: Controlling GDB
+
+Numbers
+=======
+
+ You can always enter numbers in octal, decimal, or hexadecimal in
+GDB by the usual conventions: octal numbers begin with `0', decimal
+numbers end with `.', and hexadecimal numbers begin with `0x'. Numbers
+that begin with none of these are, by default, entered in base 10;
+likewise, the default display for numbers--when no particular format is
+specified--is base 10. You can change the default base for both input
+and output with the `set radix' command.
+
+`set radix BASE'
+ Set the default base for numeric input and display. Supported
+ choices for BASE are decimal 8, 10, or 16. BASE must itself be
+ specified either unambiguously or using the current default radix;
+ for example, any of
+
+ set radix 012
+ set radix 10.
+ set radix 0xa
+
+ will set the base to decimal. On the other hand, `set radix 10'
+ will leave the radix unchanged no matter what it was.
+
+`show radix'
+ Display the current default base for numeric input and display.
+
+
+File: gdb.info, Node: Messages/Warnings, Prev: Numbers, Up: Controlling GDB
+
+Optional warnings and messages
+==============================
+
+ By default, GDB is silent about its inner workings. If you are
+running on a slow machine, you may want to use the `set verbose'
+command. It will make GDB tell you when it does a lengthy internal
+operation, so you will not think it has crashed.
+
+ Currently, the messages controlled by `set verbose' are those which
+announce that the symbol table for a source file is being read; see
+`symbol-file' in *Note Commands to specify files: Files.
+
+`set verbose on'
+ Enables GDB output of certain informational messages.
+
+`set verbose off'
+ Disables GDB output of certain informational messages.
+
+`show verbose'
+ Displays whether `set verbose' is on or off.
+
+ By default, if GDB encounters bugs in the symbol table of an object
+file, it is silent; but if you are debugging a compiler, you may find
+this information useful (*note Errors reading symbol files: Symbol
+Errors.).
+
+`set complaints LIMIT'
+ Permits GDB to output LIMIT complaints about each type of unusual
+ symbols before becoming silent about the problem. Set LIMIT to
+ zero to suppress all complaints; set it to a large number to
+ prevent complaints from being suppressed.
+
+`show complaints'
+ Displays how many symbol complaints GDB is permitted to produce.
+
+ By default, GDB is cautious, and asks what sometimes seems to be a
+lot of stupid questions to confirm certain commands. For example, if
+you try to run a program which is already running:
+
+ (gdb) run
+ The program being debugged has been started already.
+ Start it from the beginning? (y or n)
+
+ If you are willing to unflinchingly face the consequences of your own
+commands, you can disable this "feature":
+
+`set confirm off'
+ Disables confirmation requests.
+
+`set confirm on'
+ Enables confirmation requests (the default).
+
+`show confirm'
+ Displays state of confirmation requests.
+
+ Some systems allow individual object files that make up your program
+to be replaced without stopping and restarting your program. For
+example, in VxWorks you can simply recompile a defective object file
+and keep on running. If you are running on one of these systems, you
+can allow GDB to reload the symbols for automatically relinked modules:
+
+`set symbol-reloading on'
+ Replace symbol definitions for the corresponding source file when
+ an object file with a particular name is seen again.
+
+`set symbol-reloading off'
+ Do not replace symbol definitions when re-encountering object
+ files of the same name. This is the default state; if you are not
+ running on a system that permits automatically relinking modules,
+ you should leave `symbol-reloading' off, since otherwise GDB may
+ discard symbols when linking large programs, that may contain
+ several modules (from different directories or libraries) with the
+ same name.
+
+`show symbol-reloading'
+ Show the current `on' or `off' setting.
+
+
+File: gdb.info, Node: Sequences, Next: Emacs, Prev: Controlling GDB, Up: Top
+
+Canned Sequences of Commands
+****************************
+
+ Aside from breakpoint commands (*note Breakpoint command lists:
+Break Commands.), GDB provides two ways to store sequences of commands
+for execution as a unit: user-defined commands and command files.
+
+* Menu:
+
+* Define:: User-defined commands
+* Hooks:: User-defined command hooks
+* Command Files:: Command files
+* Output:: Commands for controlled output
+
+
+File: gdb.info, Node: Define, Next: Hooks, Up: Sequences
+
+User-defined commands
+=====================
+
+ A "user-defined command" is a sequence of GDB commands to which you
+assign a new name as a command. This is done with the `define' command.
+
+`define COMMANDNAME'
+ Define a command named COMMANDNAME. If there is already a command
+ by that name, you are asked to confirm that you want to redefine
+ it.
+
+ The definition of the command is made up of other GDB command
+ lines, which are given following the `define' command. The end of
+ these commands is marked by a line containing `end'.
+
+`document COMMANDNAME'
+ Give documentation to the user-defined command COMMANDNAME. The
+ command COMMANDNAME must already be defined. This command reads
+ lines of documentation just as `define' reads the lines of the
+ command definition, ending with `end'. After the `document'
+ command is finished, `help' on command COMMANDNAME will print the
+ documentation you have specified.
+
+ You may use the `document' command again to change the
+ documentation of a command. Redefining the command with `define'
+ does not change the documentation.
+
+`help user-defined'
+ List all user-defined commands, with the first line of the
+ documentation (if any) for each.
+
+`show user'
+`show user COMMANDNAME'
+ Display the GDB commands used to define COMMANDNAME (but not its
+ documentation). If no COMMANDNAME is given, display the
+ definitions for all user-defined commands.
+
+ User-defined commands do not take arguments. When they are
+executed, the commands of the definition are not printed. An error in
+any command stops execution of the user-defined command.
+
+ Commands that would ask for confirmation if used interactively
+proceed without asking when used inside a user-defined command. Many
+GDB commands that normally print messages to say what they are doing
+omit the messages when used in a user-defined command.
+
+
+File: gdb.info, Node: Hooks, Next: Command Files, Prev: Define, Up: Sequences
+
+User-defined command hooks
+==========================
+
+ You may define *hooks*, which are a special kind of user-defined
+command. Whenever you run the command `foo', if the user-defined
+command `hook-foo' exists, it is executed (with no arguments) before
+that command.
+
+ In addition, a pseudo-command, `stop' exists. Defining
+(`hook-stop') makes the associated commands execute every time
+execution stops in your program: before breakpoint commands are run,
+displays are printed, or the stack frame is printed.
+
+ For example, to ignore `SIGALRM' signals while single-stepping, but
+treat them normally during normal execution, you could define:
+
+ define hook-stop
+ handle SIGALRM nopass
+ end
+
+ define hook-run
+ handle SIGALRM pass
+ end
+
+ define hook-continue
+ handle SIGLARM pass
+ end
+
+ You can define a hook for any single-word command in GDB, but not
+for command aliases; you should define a hook for the basic command
+name, e.g. `backtrace' rather than `bt'. If an error occurs during
+the execution of your hook, execution of GDB commands stops and GDB
+issues a prompt (before the command that you actually typed had a
+chance to run).
+
+ If you try to define a hook which does not match any known command,
+you will get a warning from the `define' command.
+
+
+File: gdb.info, Node: Command Files, Next: Output, Prev: Hooks, Up: Sequences
+
+Command files
+=============
+
+ A command file for GDB is a file of lines that are GDB commands.
+Comments (lines starting with `#') may also be included. An empty line
+in a command file does nothing; it does not mean to repeat the last
+command, as it would from the terminal.
+
+ When you start GDB, it automatically executes commands from its
+"init files". These are files named `.gdbinit'. GDB reads the init
+file (if any) in your home directory and then the init file (if any) in
+the current working directory. (The init files are not executed if you
+use the `-nx' option; *note Choosing modes: Mode Options..)
+
+ On some configurations of GDB, the init file is known by a different
+name (these are typically environments where a specialized form of GDB
+may need to coexist with other forms, hence a different name for the
+specialized version's init file). These are the environments with
+special init file names:
+
+ * VxWorks (Wind River Systems real-time OS): `.vxgdbinit'
+
+ * OS68K (Enea Data Systems real-time OS): `.os68gdbinit'
+
+ * ES-1800 (Ericsson Telecom AB M68000 emulator): `.esgdbinit'
+
+ You can also request the execution of a command file with the
+`source' command:
+
+`source FILENAME'
+ Execute the command file FILENAME.
+
+ The lines in a command file are executed sequentially. They are not
+printed as they are executed. An error in any command terminates
+execution of the command file.
+
+ Commands that would ask for confirmation if used interactively
+proceed without asking when used in a command file. Many GDB commands
+that normally print messages to say what they are doing omit the
+messages when called from command files.
+
+
+File: gdb.info, Node: Output, Prev: Command Files, Up: Sequences
+
+Commands for controlled output
+==============================
+
+ During the execution of a command file or a user-defined command,
+normal GDB output is suppressed; the only output that appears is what is
+explicitly printed by the commands in the definition. This section
+describes three commands useful for generating exactly the output you
+want.
+
+`echo TEXT'
+ Print TEXT. Nonprinting characters can be included in TEXT using
+ C escape sequences, such as `\n' to print a newline. *No newline
+ will be printed unless you specify one.* In addition to the
+ standard C escape sequences, a backslash followed by a space
+ stands for a space. This is useful for displaying a string with
+ spaces at the beginning or the end, since leading and trailing
+ spaces are otherwise trimmed from all arguments. To print ` and
+ foo = ', use the command `echo \ and foo = \ '.
+
+ A backslash at the end of TEXT can be used, as in C, to continue
+ the command onto subsequent lines. For example,
+
+ echo This is some text\n\
+ which is continued\n\
+ onto several lines.\n
+
+ produces the same output as
+
+ echo This is some text\n
+ echo which is continued\n
+ echo onto several lines.\n
+
+`output EXPRESSION'
+ Print the value of EXPRESSION and nothing but that value: no
+ newlines, no `$NN = '. The value is not entered in the value
+ history either. *Note Expressions: Expressions, for more
+ information on expressions.
+
+`output/FMT EXPRESSION'
+ Print the value of EXPRESSION in format FMT. You can use the same
+ formats as for `print'. *Note Output formats: Output Formats, for
+ more information.
+
+`printf STRING, EXPRESSIONS...'
+ Print the values of the EXPRESSIONS under the control of STRING.
+ The EXPRESSIONS are separated by commas and may be either numbers
+ or pointers. Their values are printed as specified by STRING,
+ exactly as if your program were to execute the C subroutine
+
+ printf (STRING, EXPRESSIONS...);
+
+ For example, you can print two values in hex like this:
+
+ printf "foo, bar-foo = 0x%x, 0x%x\n", foo, bar-foo
+
+ The only backslash-escape sequences that you can use in the format
+ string are the simple ones that consist of backslash followed by a
+ letter.
+
+
+File: gdb.info, Node: Emacs, Next: GDB Bugs, Prev: Sequences, Up: Top
+
+Using GDB under GNU Emacs
+*************************
+
+ A special interface allows you to use GNU Emacs to view (and edit)
+the source files for the program you are debugging with GDB.
+
+ To use this interface, use the command `M-x gdb' in Emacs. Give the
+executable file you want to debug as an argument. This command starts
+GDB as a subprocess of Emacs, with input and output through a newly
+created Emacs buffer.
+
+ Using GDB under Emacs is just like using GDB normally except for two
+things:
+
+ * All "terminal" input and output goes through the Emacs buffer.
+
+ This applies both to GDB commands and their output, and to the input
+and output done by the program you are debugging.
+
+ This is useful because it means that you can copy the text of
+previous commands and input them again; you can even use parts of the
+output in this way.
+
+ All the facilities of Emacs' Shell mode are available for interacting
+with your program. In particular, you can send signals the usual
+way--for example, `C-c C-c' for an interrupt, `C-c C-z' for a stop.
+
+ * GDB displays source code through Emacs.
+
+ Each time GDB displays a stack frame, Emacs automatically finds the
+source file for that frame and puts an arrow (`=>') at the left margin
+of the current line. Emacs uses a separate buffer for source display,
+and splits the screen to show both your GDB session and the source.
+
+ Explicit GDB `list' or search commands still produce output as
+usual, but you probably will have no reason to use them.
+
+ *Warning:* If the directory where your program resides is not your
+ current directory, it can be easy to confuse Emacs about the
+ location of the source files, in which case the auxiliary display
+ buffer will not appear to show your source. GDB can find programs
+ by searching your environment's `PATH' variable, so the GDB input
+ and output session will proceed normally; but Emacs does not get
+ enough information back from GDB to locate the source files in
+ this situation. To avoid this problem, either start GDB mode from
+ the directory where your program resides, or specify a full path
+ name when prompted for the `M-x gdb' argument.
+
+ A similar confusion can result if you use the GDB `file' command to
+ switch to debugging a program in some other location, from an
+ existing GDB buffer in Emacs.
+
+ By default, `M-x gdb' calls the program called `gdb'. If you need
+to call GDB by a different name (for example, if you keep several
+configurations around, with different names) you can set the Emacs
+variable `gdb-command-name'; for example,
+
+ (setq gdb-command-name "mygdb")
+
+(preceded by `ESC ESC', or typed in the `*scratch*' buffer, or in your
+`.emacs' file) will make Emacs call the program named "`mygdb'" instead.
+
+ In the GDB I/O buffer, you can use these special Emacs commands in
+addition to the standard Shell mode commands:
+
+`C-h m'
+ Describe the features of Emacs' GDB Mode.
+
+`M-s'
+ Execute to another source line, like the GDB `step' command; also
+ update the display window to show the current file and location.
+
+`M-n'
+ Execute to next source line in this function, skipping all function
+ calls, like the GDB `next' command. Then update the display window
+ to show the current file and location.
+
+`M-i'
+ Execute one instruction, like the GDB `stepi' command; update
+ display window accordingly.
+
+`M-x gdb-nexti'
+ Execute to next instruction, using the GDB `nexti' command; update
+ display window accordingly.
+
+`C-c C-f'
+ Execute until exit from the selected stack frame, like the GDB
+ `finish' command.
+
+`M-c'
+ Continue execution of your program, like the GDB `continue'
+ command.
+
+ *Warning:* In Emacs v19, this command is `C-c C-p'.
+
+`M-u'
+ Go up the number of frames indicated by the numeric argument
+ (*note Numeric Arguments: (emacs)Arguments.), like the GDB `up'
+ command.
+
+ *Warning:* In Emacs v19, this command is `C-c C-u'.
+
+`M-d'
+ Go down the number of frames indicated by the numeric argument,
+ like the GDB `down' command.
+
+ *Warning:* In Emacs v19, this command is `C-c C-d'.
+
+`C-x &'
+ Read the number where the cursor is positioned, and insert it at
+ the end of the GDB I/O buffer. For example, if you wish to
+ disassemble code around an address that was displayed earlier,
+ type `disassemble'; then move the cursor to the address display,
+ and pick up the argument for `disassemble' by typing `C-x &'.
+
+ You can customize this further by defining elements of the list
+ `gdb-print-command'; once it is defined, you can format or
+ otherwise process numbers picked up by `C-x &' before they are
+ inserted. A numeric argument to `C-x &' will both indicate that
+ you wish special formatting, and act as an index to pick an
+ element of the list. If the list element is a string, the number
+ to be inserted is formatted using the Emacs function `format';
+ otherwise the number is passed as an argument to the corresponding
+ list element.
+
+ In any source file, the Emacs command `C-x SPC' (`gdb-break') tells
+GDB to set a breakpoint on the source line point is on.
+
+ If you accidentally delete the source-display buffer, an easy way to
+get it back is to type the command `f' in the GDB buffer, to request a
+frame display; when you run under Emacs, this will recreate the source
+buffer if necessary to show you the context of the current frame.
+
+ The source files displayed in Emacs are in ordinary Emacs buffers
+which are visiting the source files in the usual way. You can edit the
+files with these buffers if you wish; but keep in mind that GDB
+communicates with Emacs in terms of line numbers. If you add or delete
+lines from the text, the line numbers that GDB knows will cease to
+correspond properly with the code.
+
+
+File: gdb.info, Node: GDB Bugs, Next: Command Line Editing, Prev: Emacs, Up: Top
+
+Reporting Bugs in GDB
+*********************
+
+ Your bug reports play an essential role in making GDB reliable.
+
+ Reporting a bug may help you by bringing a solution to your problem,
+or it may not. But in any case the principal function of a bug report
+is to help the entire community by making the next version of GDB work
+better. Bug reports are your contribution to the maintenance of GDB.
+
+ In order for a bug report to serve its purpose, you must include the
+information that enables us to fix the bug.
+
+* Menu:
+
+* Bug Criteria:: Have you found a bug?
+* Bug Reporting:: How to report bugs
+
+
+File: gdb.info, Node: Bug Criteria, Next: Bug Reporting, Up: GDB Bugs
+
+Have you found a bug?
+=====================
+
+ If you are not sure whether you have found a bug, here are some
+guidelines:
+
+ * If the debugger gets a fatal signal, for any input whatever, that
+ is a GDB bug. Reliable debuggers never crash.
+
+ * If GDB produces an error message for valid input, that is a bug.
+
+ * If GDB does not produce an error message for invalid input, that
+ is a bug. However, you should note that your idea of "invalid
+ input" might be our idea of "an extension" or "support for
+ traditional practice".
+
+ * If you are an experienced user of debugging tools, your suggestions
+ for improvement of GDB are welcome in any case.
+
diff --git a/gnu/usr.bin/gdb/doc/gdb.info-7 b/gnu/usr.bin/gdb/doc/gdb.info-7
new file mode 100644
index 0000000..963527e
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/gdb.info-7
@@ -0,0 +1,1233 @@
+This is Info file ./gdb.info, produced by Makeinfo-1.52 from the input
+file gdb.texinfo.
+
+START-INFO-DIR-ENTRY
+* Gdb:: The GNU debugger.
+END-INFO-DIR-ENTRY
+ This file documents the GNU debugger GDB.
+
+ This is Edition 4.09, August 1993, of `Debugging with GDB: the GNU
+Source-Level Debugger' for GDB Version 4.11.
+
+ Copyright (C) 1988, '89, '90, '91, '92, '93 Free Software
+Foundation, Inc.
+
+ Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of
+this manual under the conditions for verbatim copying, provided also
+that the entire resulting derived work is distributed under the terms
+of a permission notice identical to this one.
+
+ Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions.
+
+
+File: gdb.info, Node: Bug Reporting, Prev: Bug Criteria, Up: GDB Bugs
+
+How to report bugs
+==================
+
+ A number of companies and individuals offer support for GNU products.
+If you obtained GDB from a support organization, we recommend you
+contact that organization first.
+
+ You can find contact information for many support companies and
+individuals in the file `etc/SERVICE' in the GNU Emacs distribution.
+
+ In any event, we also recommend that you send bug reports for GDB to
+one of these addresses:
+
+ bug-gdb@prep.ai.mit.edu
+ {ucbvax|mit-eddie|uunet}!prep.ai.mit.edu!bug-gdb
+
+ *Do not send bug reports to `info-gdb', or to `help-gdb', or to any
+newsgroups.* Most users of GDB do not want to receive bug reports.
+Those that do, have arranged to receive `bug-gdb'.
+
+ The mailing list `bug-gdb' has a newsgroup `gnu.gdb.bug' which
+serves as a repeater. The mailing list and the newsgroup carry exactly
+the same messages. Often people think of posting bug reports to the
+newsgroup instead of mailing them. This appears to work, but it has one
+problem which can be crucial: a newsgroup posting often lacks a mail
+path back to the sender. Thus, if we need to ask for more information,
+we may be unable to reach you. For this reason, it is better to send
+bug reports to the mailing list.
+
+ As a last resort, send bug reports on paper to:
+
+ GNU Debugger Bugs
+ Free Software Foundation
+ 545 Tech Square
+ Cambridge, MA 02139
+
+ The fundamental principle of reporting bugs usefully is this:
+*report all the facts*. If you are not sure whether to state a fact or
+leave it out, state it!
+
+ Often people omit facts because they think they know what causes the
+problem and assume that some details do not matter. Thus, you might
+assume that the name of the variable you use in an example does not
+matter. Well, probably it does not, but one cannot be sure. Perhaps
+the bug is a stray memory reference which happens to fetch from the
+location where that name is stored in memory; perhaps, if the name were
+different, the contents of that location would fool the debugger into
+doing the right thing despite the bug. Play it safe and give a
+specific, complete example. That is the easiest thing for you to do,
+and the most helpful.
+
+ Keep in mind that the purpose of a bug report is to enable us to fix
+the bug if it is new to us. It is not as important as what happens if
+the bug is already known. Therefore, always write your bug reports on
+the assumption that the bug has not been reported previously.
+
+ Sometimes people give a few sketchy facts and ask, "Does this ring a
+bell?" Those bug reports are useless, and we urge everyone to *refuse
+to respond to them* except to chide the sender to report bugs properly.
+
+ To enable us to fix the bug, you should include all these things:
+
+ * The version of GDB. GDB announces it if you start with no
+ arguments; you can also print it at any time using `show version'.
+
+ Without this, we will not know whether there is any point in
+ looking for the bug in the current version of GDB.
+
+ * The type of machine you are using, and the operating system name
+ and version number.
+
+ * What compiler (and its version) was used to compile GDB--e.g.
+ "gcc-2.0".
+
+ * What compiler (and its version) was used to compile the program you
+ are debugging--e.g. "gcc-2.0".
+
+ * The command arguments you gave the compiler to compile your
+ example and observe the bug. For example, did you use `-O'? To
+ guarantee you will not omit something important, list them all. A
+ copy of the Makefile (or the output from make) is sufficient.
+
+ If we were to try to guess the arguments, we would probably guess
+ wrong and then we might not encounter the bug.
+
+ * A complete input script, and all necessary source files, that will
+ reproduce the bug.
+
+ * A description of what behavior you observe that you believe is
+ incorrect. For example, "It gets a fatal signal."
+
+ Of course, if the bug is that GDB gets a fatal signal, then we will
+ certainly notice it. But if the bug is incorrect output, we might
+ not notice unless it is glaringly wrong. We are human, after all.
+ You might as well not give us a chance to make a mistake.
+
+ Even if the problem you experience is a fatal signal, you should
+ still say so explicitly. Suppose something strange is going on,
+ such as, your copy of GDB is out of synch, or you have encountered
+ a bug in the C library on your system. (This has happened!) Your
+ copy might crash and ours would not. If you told us to expect a
+ crash, then when ours fails to crash, we would know that the bug
+ was not happening for us. If you had not told us to expect a
+ crash, then we would not be able to draw any conclusion from our
+ observations.
+
+ * If you wish to suggest changes to the GDB source, send us context
+ diffs. If you even discuss something in the GDB source, refer to
+ it by context, not by line number.
+
+ The line numbers in our development sources will not match those
+ in your sources. Your line numbers would convey no useful
+ information to us.
+
+ Here are some things that are not necessary:
+
+ * A description of the envelope of the bug.
+
+ Often people who encounter a bug spend a lot of time investigating
+ which changes to the input file will make the bug go away and which
+ changes will not affect it.
+
+ This is often time consuming and not very useful, because the way
+ we will find the bug is by running a single example under the
+ debugger with breakpoints, not by pure deduction from a series of
+ examples. We recommend that you save your time for something else.
+
+ Of course, if you can find a simpler example to report *instead*
+ of the original one, that is a convenience for us. Errors in the
+ output will be easier to spot, running under the debugger will take
+ less time, etc.
+
+ However, simplification is not vital; if you do not want to do
+ this, report the bug anyway and send us the entire test case you
+ used.
+
+ * A patch for the bug.
+
+ A patch for the bug does help us if it is a good one. But do not
+ omit the necessary information, such as the test case, on the
+ assumption that a patch is all we need. We might see problems
+ with your patch and decide to fix the problem another way, or we
+ might not understand it at all.
+
+ Sometimes with a program as complicated as GDB it is very hard to
+ construct an example that will make the program follow a certain
+ path through the code. If you do not send us the example, we will
+ not be able to construct one, so we will not be able to verify
+ that the bug is fixed.
+
+ And if we cannot understand what bug you are trying to fix, or why
+ your patch should be an improvement, we will not install it. A
+ test case will help us to understand.
+
+ * A guess about what the bug is or what it depends on.
+
+ Such guesses are usually wrong. Even we cannot guess right about
+ such things without first using the debugger to find the facts.
+
+
+File: gdb.info, Node: Command Line Editing, Next: Using History Interactively, Prev: GDB Bugs, Up: Top
+
+Command Line Editing
+********************
+
+ This text describes GNU's command line editing interface.
+
+* Menu:
+
+* Introduction and Notation:: Notation used in this text.
+* Readline Interaction:: The minimum set of commands for editing a line.
+* Readline Init File:: Customizing Readline from a user's view.
+
+
+File: gdb.info, Node: Introduction and Notation, Next: Readline Interaction, Up: Command Line Editing
+
+Introduction to Line Editing
+============================
+
+ The following paragraphs describe the notation we use to represent
+keystrokes.
+
+ The text C-k is read as `Control-K' and describes the character
+produced when the Control key is depressed and the k key is struck.
+
+ The text M-k is read as `Meta-K' and describes the character
+produced when the meta key (if you have one) is depressed, and the k
+key is struck. If you do not have a meta key, the identical keystroke
+can be generated by typing ESC first, and then typing k. Either
+process is known as "metafying" the k key.
+
+ The text M-C-k is read as `Meta-Control-k' and describes the
+character produced by "metafying" C-k.
+
+ In addition, several keys have their own names. Specifically, DEL,
+ESC, LFD, SPC, RET, and TAB all stand for themselves when seen in this
+text, or in an init file (*note Readline Init File::., for more info).
+
+
+File: gdb.info, Node: Readline Interaction, Next: Readline Init File, Prev: Introduction and Notation, Up: Command Line Editing
+
+Readline Interaction
+====================
+
+ Often during an interactive session you type in a long line of text,
+only to notice that the first word on the line is misspelled. The
+Readline library gives you a set of commands for manipulating the text
+as you type it in, allowing you to just fix your typo, and not forcing
+you to retype the majority of the line. Using these editing commands,
+you move the cursor to the place that needs correction, and delete or
+insert the text of the corrections. Then, when you are satisfied with
+the line, you simply press RETURN. You do not have to be at the end of
+the line to press RETURN; the entire line is accepted regardless of the
+location of the cursor within the line.
+
+* Menu:
+
+* Readline Bare Essentials:: The least you need to know about Readline.
+* Readline Movement Commands:: Moving about the input line.
+* Readline Killing Commands:: How to delete text, and how to get it back!
+* Readline Arguments:: Giving numeric arguments to commands.
+
+
+File: gdb.info, Node: Readline Bare Essentials, Next: Readline Movement Commands, Up: Readline Interaction
+
+Readline Bare Essentials
+------------------------
+
+ In order to enter characters into the line, simply type them. The
+typed character appears where the cursor was, and then the cursor moves
+one space to the right. If you mistype a character, you can use DEL to
+back up, and delete the mistyped character.
+
+ Sometimes you may miss typing a character that you wanted to type,
+and not notice your error until you have typed several other
+characters. In that case, you can type C-b to move the cursor to the
+left, and then correct your mistake. Aftwerwards, you can move the
+cursor to the right with C-f.
+
+ When you add text in the middle of a line, you will notice that
+characters to the right of the cursor get `pushed over' to make room
+for the text that you have inserted. Likewise, when you delete text
+behind the cursor, characters to the right of the cursor get `pulled
+back' to fill in the blank space created by the removal of the text. A
+list of the basic bare essentials for editing the text of an input line
+follows.
+
+C-b
+ Move back one character.
+
+C-f
+ Move forward one character.
+
+DEL
+ Delete the character to the left of the cursor.
+
+C-d
+ Delete the character underneath the cursor.
+
+Printing characters
+ Insert itself into the line at the cursor.
+
+C-_
+ Undo the last thing that you did. You can undo all the way back
+ to an empty line.
+
+
+File: gdb.info, Node: Readline Movement Commands, Next: Readline Killing Commands, Prev: Readline Bare Essentials, Up: Readline Interaction
+
+Readline Movement Commands
+--------------------------
+
+ The above table describes the most basic possible keystrokes that
+you need in order to do editing of the input line. For your
+convenience, many other commands have been added in addition to C-b,
+C-f, C-d, and DEL. Here are some commands for moving more rapidly
+about the line.
+
+C-a
+ Move to the start of the line.
+
+C-e
+ Move to the end of the line.
+
+M-f
+ Move forward a word.
+
+M-b
+ Move backward a word.
+
+C-l
+ Clear the screen, reprinting the current line at the top.
+
+ Notice how C-f moves forward a character, while M-f moves forward a
+word. It is a loose convention that control keystrokes operate on
+characters while meta keystrokes operate on words.
+
+
+File: gdb.info, Node: Readline Killing Commands, Next: Readline Arguments, Prev: Readline Movement Commands, Up: Readline Interaction
+
+Readline Killing Commands
+-------------------------
+
+ "Killing" text means to delete the text from the line, but to save
+it away for later use, usually by "yanking" it back into the line. If
+the description for a command says that it `kills' text, then you can
+be sure that you can get the text back in a different (or the same)
+place later.
+
+ Here is the list of commands for killing text.
+
+C-k
+ Kill the text from the current cursor position to the end of the
+ line.
+
+M-d
+ Kill from the cursor to the end of the current word, or if between
+ words, to the end of the next word.
+
+M-DEL
+ Kill from the cursor to the start of the previous word, or if
+ between words, to the start of the previous word.
+
+C-w
+ Kill from the cursor to the previous whitespace. This is
+ different than M-DEL because the word boundaries differ.
+
+ And, here is how to "yank" the text back into the line. Yanking is
+
+C-y
+ Yank the most recently killed text back into the buffer at the
+ cursor.
+
+M-y
+ Rotate the kill-ring, and yank the new top. You can only do this
+ if the prior command is C-y or M-y.
+
+ When you use a kill command, the text is saved in a "kill-ring".
+Any number of consecutive kills save all of the killed text together, so
+that when you yank it back, you get it in one clean sweep. The kill
+ring is not line specific; the text that you killed on a previously
+typed line is available to be yanked back later, when you are typing
+another line.
+
+
+File: gdb.info, Node: Readline Arguments, Prev: Readline Killing Commands, Up: Readline Interaction
+
+Readline Arguments
+------------------
+
+ You can pass numeric arguments to Readline commands. Sometimes the
+argument acts as a repeat count, other times it is the sign of the
+argument that is significant. If you pass a negative argument to a
+command which normally acts in a forward direction, that command will
+act in a backward direction. For example, to kill text back to the
+start of the line, you might type M- C-k.
+
+ The general way to pass numeric arguments to a command is to type
+meta digits before the command. If the first `digit' you type is a
+minus sign (-), then the sign of the argument will be negative. Once
+you have typed one meta digit to get the argument started, you can type
+the remainder of the digits, and then the command. For example, to give
+the C-d command an argument of 10, you could type M-1 0 C-d.
+
+
+File: gdb.info, Node: Readline Init File, Prev: Readline Interaction, Up: Command Line Editing
+
+Readline Init File
+==================
+
+ Although the Readline library comes with a set of Emacs-like
+keybindings, it is possible that you would like to use a different set
+of keybindings. You can customize programs that use Readline by putting
+commands in an "init" file in your home directory. The name of this
+file is `~/.inputrc'.
+
+ When a program which uses the Readline library starts up, the
+`~/.inputrc' file is read, and the keybindings are set.
+
+ In addition, the C-x C-r command re-reads this init file, thus
+incorporating any changes that you might have made to it.
+
+* Menu:
+
+* Readline Init Syntax:: Syntax for the commands in `~/.inputrc'.
+* Readline Vi Mode:: Switching to `vi' mode in Readline.
+
+
+File: gdb.info, Node: Readline Init Syntax, Next: Readline Vi Mode, Up: Readline Init File
+
+Readline Init Syntax
+--------------------
+
+ There are only four constructs allowed in the `~/.inputrc' file:
+
+Variable Settings
+ You can change the state of a few variables in Readline. You do
+ this by using the `set' command within the init file. Here is how
+ you would specify that you wish to use Vi line editing commands:
+
+ set editing-mode vi
+
+ Right now, there are only a few variables which can be set; so few
+ in fact, that we just iterate them here:
+
+ `editing-mode'
+ The `editing-mode' variable controls which editing mode you
+ are using. By default, GNU Readline starts up in Emacs
+ editing mode, where the keystrokes are most similar to Emacs.
+ This variable can either be set to `emacs' or `vi'.
+
+ `horizontal-scroll-mode'
+ This variable can either be set to `On' or `Off'. Setting it
+ to `On' means that the text of the lines that you edit will
+ scroll horizontally on a single screen line when they are
+ larger than the width of the screen, instead of wrapping onto
+ a new screen line. By default, this variable is set to `Off'.
+
+ `mark-modified-lines'
+ This variable when set to `On', says to display an asterisk
+ (`*') at the starts of history lines which have been modified.
+ This variable is off by default.
+
+ `prefer-visible-bell'
+ If this variable is set to `On' it means to use a visible
+ bell if one is available, rather than simply ringing the
+ terminal bell. By default, the value is `Off'.
+
+Key Bindings
+ The syntax for controlling keybindings in the `~/.inputrc' file is
+ simple. First you have to know the name of the command that you
+ want to change. The following pages contain tables of the command
+ name, the default keybinding, and a short description of what the
+ command does.
+
+ Once you know the name of the command, simply place the name of
+ the key you wish to bind the command to, a colon, and then the
+ name of the command on a line in the `~/.inputrc' file. The name
+ of the key can be expressed in different ways, depending on which
+ is most comfortable for you.
+
+ KEYNAME: FUNCTION-NAME or MACRO
+ KEYNAME is the name of a key spelled out in English. For
+ example:
+ Control-u: universal-argument
+ Meta-Rubout: backward-kill-word
+ Control-o: ">&output"
+
+ In the above example, C-u is bound to the function
+ `universal-argument', and C-o is bound to run the macro
+ expressed on the right hand side (that is, to insert the text
+ `>&output' into the line).
+
+ "KEYSEQ": FUNCTION-NAME or MACRO
+ KEYSEQ differs from KEYNAME above in that strings denoting an
+ entire key sequence can be specified. Simply place the key
+ sequence in double quotes. GNU Emacs style key escapes can
+ be used, as in the following example:
+
+ "\C-u": universal-argument
+ "\C-x\C-r": re-read-init-file
+ "\e[11~": "Function Key 1"
+
+ In the above example, C-u is bound to the function
+ `universal-argument' (just as it was in the first example),
+ C-x C-r is bound to the function `re-read-init-file', and ESC
+ [ 1 1 ~ is bound to insert the text `Function Key 1'.
+
+* Menu:
+
+* Commands For Moving:: Moving about the line.
+* Commands For History:: Getting at previous lines.
+* Commands For Text:: Commands for changing text.
+* Commands For Killing:: Commands for killing and yanking.
+* Numeric Arguments:: Specifying numeric arguments, repeat counts.
+* Commands For Completion:: Getting Readline to do the typing for you.
+* Miscellaneous Commands:: Other miscillaneous commands.
+
+
+File: gdb.info, Node: Commands For Moving, Next: Commands For History, Up: Readline Init Syntax
+
+Commands For Moving
+-------------------
+
+`beginning-of-line (C-a)'
+ Move to the start of the current line.
+
+`end-of-line (C-e)'
+ Move to the end of the line.
+
+`forward-char (C-f)'
+ Move forward a character.
+
+`backward-char (C-b)'
+ Move back a character.
+
+`forward-word (M-f)'
+ Move forward to the end of the next word.
+
+`backward-word (M-b)'
+ Move back to the start of this, or the previous, word.
+
+`clear-screen (C-l)'
+ Clear the screen leaving the current line at the top of the screen.
+
+
+File: gdb.info, Node: Commands For History, Next: Commands For Text, Prev: Commands For Moving, Up: Readline Init Syntax
+
+Commands For Manipulating The History
+-------------------------------------
+
+`accept-line (Newline, Return)'
+ Accept the line regardless of where the cursor is. If this line is
+ non-empty, add it to the history list. If this line was a history
+ line, then restore the history line to its original state.
+
+`previous-history (C-p)'
+ Move `up' through the history list.
+
+`next-history (C-n)'
+ Move `down' through the history list.
+
+`beginning-of-history (M-<)'
+ Move to the first line in the history.
+
+`end-of-history (M->)'
+ Move to the end of the input history, i.e., the line you are
+ entering!
+
+`reverse-search-history (C-r)'
+ Search backward starting at the current line and moving `up'
+ through the history as necessary. This is an incremental search.
+
+`forward-search-history (C-s)'
+ Search forward starting at the current line and moving `down'
+ through the the history as neccessary.
+
+
+File: gdb.info, Node: Commands For Text, Next: Commands For Killing, Prev: Commands For History, Up: Readline Init Syntax
+
+Commands For Changing Text
+--------------------------
+
+`delete-char (C-d)'
+ Delete the character under the cursor. If the cursor is at the
+ beginning of the line, and there are no characters in the line, and
+ the last character typed was not C-d, then return EOF.
+
+`backward-delete-char (Rubout)'
+ Delete the character behind the cursor. A numeric arg says to kill
+ the characters instead of deleting them.
+
+`quoted-insert (C-q, C-v)'
+ Add the next character that you type to the line verbatim. This is
+ how to insert things like C-q for example.
+
+`tab-insert (M-TAB)'
+ Insert a tab character.
+
+`self-insert (a, b, A, 1, !, ...)'
+ Insert yourself.
+
+`transpose-chars (C-t)'
+ Drag the character before point forward over the character at
+ point. Point moves forward as well. If point is at the end of
+ the line, then transpose the two characters before point.
+ Negative args don't work.
+
+`transpose-words (M-t)'
+ Drag the word behind the cursor past the word in front of the
+ cursor moving the cursor over that word as well.
+
+`upcase-word (M-u)'
+ Uppercase all letters in the current (or following) word. With a
+ negative argument, do the previous word, but do not move point.
+
+`downcase-word (M-l)'
+ Lowercase all letters in the current (or following) word. With a
+ negative argument, do the previous word, but do not move point.
+
+`capitalize-word (M-c)'
+ Uppercase the first letter in the current (or following) word.
+ With a negative argument, do the previous word, but do not move
+ point.
+
+
+File: gdb.info, Node: Commands For Killing, Next: Numeric Arguments, Prev: Commands For Text, Up: Readline Init Syntax
+
+Killing And Yanking
+-------------------
+
+`kill-line (C-k)'
+ Kill the text from the current cursor position to the end of the
+ line.
+
+`backward-kill-line ()'
+ Kill backward to the beginning of the line. This is normally
+ unbound.
+
+`kill-word (M-d)'
+ Kill from the cursor to the end of the current word, or if between
+ words, to the end of the next word.
+
+`backward-kill-word (M-DEL)'
+ Kill the word behind the cursor.
+
+`unix-line-discard (C-u)'
+ Do what C-u used to do in Unix line input. We save the killed
+ text on the kill-ring, though.
+
+`unix-word-rubout (C-w)'
+ Do what C-w used to do in Unix line input. The killed text is
+ saved on the kill-ring. This is different than backward-kill-word
+ because the word boundaries differ.
+
+`yank (C-y)'
+ Yank the top of the kill ring into the buffer at point.
+
+`yank-pop (M-y)'
+ Rotate the kill-ring, and yank the new top. You can only do this
+ if the prior command is yank or yank-pop.
+
+
+File: gdb.info, Node: Numeric Arguments, Next: Commands For Completion, Prev: Commands For Killing, Up: Readline Init Syntax
+
+Specifying Numeric Arguments
+----------------------------
+
+`digit-argument (M-0, M-1, ... M--)'
+ Add this digit to the argument already accumulating, or start a new
+ argument. M- starts a negative argument.
+
+`universal-argument ()'
+ Do what C-u does in emacs. By default, this is not bound.
+
+
+File: gdb.info, Node: Commands For Completion, Next: Miscellaneous Commands, Prev: Numeric Arguments, Up: Readline Init Syntax
+
+Letting Readline Type For You
+-----------------------------
+
+`complete (TAB)'
+ Attempt to do completion on the text before point. This is
+ implementation defined. Generally, if you are typing a filename
+ argument, you can do filename completion; if you are typing a
+ command, you can do command completion, if you are typing in a
+ symbol to GDB, you can do symbol name completion, if you are
+ typing in a variable to Bash, you can do variable name
+ completion...
+
+`possible-completions (M-?)'
+ List the possible completions of the text before point.
+
+
+File: gdb.info, Node: Miscellaneous Commands, Prev: Commands For Completion, Up: Readline Init Syntax
+
+Some Miscellaneous Commands
+---------------------------
+
+`re-read-init-file (C-x C-r)'
+ Read in the contents of your `~/.inputrc' file, and incorporate
+ any bindings found there.
+
+`abort (C-g)'
+ Stop running the current editing command.
+
+`prefix-meta (ESC)'
+ Make the next character that you type be metafied. This is for
+ people without a meta key. Typing ESC f is equivalent to typing
+ M-f.
+
+`undo (C-_)'
+ Incremental undo, separately remembered for each line.
+
+`revert-line (M-r)'
+ Undo all changes made to this line. This is like typing the `undo'
+ command enough times to get back to the beginning.
+
+
+File: gdb.info, Node: Readline Vi Mode, Prev: Readline Init Syntax, Up: Readline Init File
+
+Readline Vi Mode
+----------------
+
+ While the Readline library does not have a full set of Vi editing
+functions, it does contain enough to allow simple editing of the line.
+
+ In order to switch interactively between Emacs and Vi editing modes,
+use the command M-C-j (toggle-editing-mode).
+
+ When you enter a line in Vi mode, you are already placed in
+`insertion' mode, as if you had typed an `i'. Pressing ESC switches
+you into `edit' mode, where you can edit the text of the line with the
+standard Vi movement keys, move to previous history lines with `k', and
+following lines with `j', and so forth.
+
+
+File: gdb.info, Node: Using History Interactively, Next: Renamed Commands, Prev: Command Line Editing, Up: Top
+
+Using History Interactively
+***************************
+
+ This chapter describes how to use the GNU History Library
+interactively, from a user's standpoint.
+
+* Menu:
+
+* History Interaction:: What it feels like using History as a user.
+
+
+File: gdb.info, Node: History Interaction, Up: Using History Interactively
+
+History Interaction
+===================
+
+ The History library provides a history expansion feature that is
+similar to the history expansion in Csh. The following text describes
+the sytax that you use to manipulate the history information.
+
+ History expansion takes place in two parts. The first is to
+determine which line from the previous history should be used during
+substitution. The second is to select portions of that line for
+inclusion into the current one. The line selected from the previous
+history is called the "event", and the portions of that line that are
+acted upon are called "words". The line is broken into words in the
+same fashion that the Bash shell does, so that several English (or
+Unix) words surrounded by quotes are considered as one word.
+
+* Menu:
+
+* Event Designators:: How to specify which history line to use.
+* Word Designators:: Specifying which words are of interest.
+* Modifiers:: Modifying the results of susbstitution.
+
+
+File: gdb.info, Node: Event Designators, Next: Word Designators, Up: History Interaction
+
+Event Designators
+-----------------
+
+ An event designator is a reference to a command line entry in the
+history list.
+
+`!'
+ Start a history subsititution, except when followed by a space,
+ tab, or the end of the line... = or (.
+
+`!!'
+ Refer to the previous command. This is a synonym for `!-1'.
+
+`!n'
+ Refer to command line N.
+
+`!-n'
+ Refer to the command line N lines back.
+
+`!string'
+ Refer to the most recent command starting with STRING.
+
+`!?string'[`?']
+ Refer to the most recent command containing STRING.
+
+
+File: gdb.info, Node: Word Designators, Next: Modifiers, Prev: Event Designators, Up: History Interaction
+
+Word Designators
+----------------
+
+ A : separates the event specification from the word designator. It
+can be omitted if the word designator begins with a ^, $, * or %.
+Words are numbered from the beginning of the line, with the first word
+being denoted by a 0 (zero).
+
+`0 (zero)'
+ The zero'th word. For many applications, this is the command word.
+
+`n'
+ The N'th word.
+
+`^'
+ The first argument. that is, word 1.
+
+`$'
+ The last argument.
+
+`%'
+ The word matched by the most recent `?string?' search.
+
+`x-y'
+ A range of words; `-Y' Abbreviates `0-Y'.
+
+`*'
+ All of the words, excepting the zero'th. This is a synonym for
+ `1-$'. It is not an error to use * if there is just one word in
+ the event. The empty string is returned in that case.
+
+
+File: gdb.info, Node: Modifiers, Prev: Word Designators, Up: History Interaction
+
+Modifiers
+---------
+
+ After the optional word designator, you can add a sequence of one or
+more of the following modifiers, each preceded by a :.
+
+`#'
+ The entire command line typed so far. This means the current
+ command, not the previous command, so it really isn't a word
+ designator, and doesn't belong in this section.
+
+`h'
+ Remove a trailing pathname component, leaving only the head.
+
+`r'
+ Remove a trailing suffix of the form `.'SUFFIX, leaving the
+ basename.
+
+`e'
+ Remove all but the suffix.
+
+`t'
+ Remove all leading pathname components, leaving the tail.
+
+`p'
+ Print the new command but do not execute it.
+
+
+File: gdb.info, Node: Renamed Commands, Next: Formatting Documentation, Prev: Using History Interactively, Up: Top
+
+Renamed Commands
+****************
+
+ The following commands were renamed in GDB 4, in order to make the
+command set as a whole more consistent and easier to use and remember:
+
+ OLD COMMAND NEW COMMAND
+ --------------- -------------------------------
+ add-syms add-symbol-file
+ delete environment unset environment
+ info convenience show convenience
+ info copying show copying
+ info directories show directories
+ info editing show commands
+ info history show values
+ info targets help target
+ info values show values
+ info version show version
+ info warranty show warranty
+ set/show addressprint set/show print address
+ set/show array-max set/show print elements
+ set/show arrayprint set/show print array
+ set/show asm-demangle set/show print asm-demangle
+ set/show caution set/show confirm
+ set/show demangle set/show print demangle
+ set/show history write set/show history save
+ set/show prettyprint set/show print pretty
+ set/show screen-height set/show height
+ set/show screen-width set/show width
+ set/show sevenbit-strings set/show print sevenbit-strings
+ set/show unionprint set/show print union
+ set/show vtblprint set/show print vtbl
+
+ unset [No longer an alias for delete]
+
+
+File: gdb.info, Node: Formatting Documentation, Next: Installing GDB, Prev: Renamed Commands, Up: Top
+
+Formatting Documentation
+************************
+
+ The GDB 4 release includes an already-formatted reference card, ready
+for printing with PostScript or GhostScript, in the `gdb' subdirectory
+of the main source directory(1). If you can use PostScript or
+GhostScript with your printer, you can print the reference card
+immediately with `refcard.ps'.
+
+ The release also includes the source for the reference card. You
+can format it, using TeX, by typing:
+
+ make refcard.dvi
+
+ The GDB reference card is designed to print in landscape mode on US
+"letter" size paper; that is, on a sheet 11 inches wide by 8.5 inches
+high. You will need to specify this form of printing as an option to
+your DVI output program.
+
+ All the documentation for GDB comes as part of the machine-readable
+distribution. The documentation is written in Texinfo format, which is
+a documentation system that uses a single source file to produce both
+on-line information and a printed manual. You can use one of the Info
+formatting commands to create the on-line version of the documentation
+and TeX (or `texi2roff') to typeset the printed version.
+
+ GDB includes an already formatted copy of the on-line Info version of
+this manual in the `gdb' subdirectory. The main Info file is
+`gdb-VERSION-NUMBER/gdb/gdb.info', and it refers to subordinate files
+matching `gdb.info*' in the same directory. If necessary, you can
+print out these files, or read them with any editor; but they are
+easier to read using the `info' subsystem in GNU Emacs or the
+standalone `info' program, available as part of the GNU Texinfo
+distribution.
+
+ If you want to format these Info files yourself, you need one of the
+Info formatting programs, such as `texinfo-format-buffer' or `makeinfo'.
+
+ If you have `makeinfo' installed, and are in the top level GDB
+source directory (`gdb-4.11', in the case of version 4.11), you can
+make the Info file by typing:
+
+ cd gdb
+ make gdb.info
+
+ If you want to typeset and print copies of this manual, you need TeX,
+a program to print its DVI output files, and `texinfo.tex', the Texinfo
+definitions file.
+
+ TeX is a typesetting program; it does not print files directly, but
+produces output files called DVI files. To print a typeset document,
+you need a program to print DVI files. If your system has TeX
+installed, chances are it has such a program. The precise command to
+use depends on your system; `lpr -d' is common; another (for PostScript
+devices) is `dvips'. The DVI print command may require a file name
+without any extension or a `.dvi' extension.
+
+ TeX also requires a macro definitions file called `texinfo.tex'.
+This file tells TeX how to typeset a document written in Texinfo
+format. On its own, TeX cannot read, much less typeset a Texinfo file.
+`texinfo.tex' is distributed with GDB and is located in the
+`gdb-VERSION-NUMBER/texinfo' directory.
+
+ If you have TeX and a DVI printer program installed, you can typeset
+and print this manual. First switch to the the `gdb' subdirectory of
+the main source directory (for example, to `gdb-4.11/gdb') and then
+type:
+
+ make gdb.dvi
+
+ ---------- Footnotes ----------
+
+ (1) In `gdb-4.11/gdb/refcard.ps' of the version 4.11 release.
+
+
+File: gdb.info, Node: Installing GDB, Next: Index, Prev: Formatting Documentation, Up: Top
+
+Installing GDB
+**************
+
+ GDB comes with a `configure' script that automates the process of
+preparing GDB for installation; you can then use `make' to build the
+`gdb' program.
+
+ The GDB distribution includes all the source code you need for GDB in
+a single directory, whose name is usually composed by appending the
+version number to `gdb'.
+
+ For example, the GDB version 4.11 distribution is in the `gdb-4.11'
+directory. That directory contains:
+
+`gdb-4.11/configure (and supporting files)'
+ script for configuring GDB and all its supporting libraries.
+
+`gdb-4.11/gdb'
+ the source specific to GDB itself
+
+`gdb-4.11/bfd'
+ source for the Binary File Descriptor library
+
+`gdb-4.11/include'
+ GNU include files
+
+`gdb-4.11/libiberty'
+ source for the `-liberty' free software library
+
+`gdb-4.11/opcodes'
+ source for the library of opcode tables and disassemblers
+
+`gdb-4.11/readline'
+ source for the GNU command-line interface
+
+`gdb-4.11/glob'
+ source for the GNU filename pattern-matching subroutine
+
+`gdb-4.11/mmalloc'
+ source for the GNU memory-mapped malloc package
+
+ The simplest way to configure and build GDB is to run `configure'
+from the `gdb-VERSION-NUMBER' source directory, which in this example
+is the `gdb-4.11' directory.
+
+ First switch to the `gdb-VERSION-NUMBER' source directory if you are
+not already in it; then run `configure'. Pass the identifier for the
+platform on which GDB will run as an argument.
+
+ For example:
+
+ cd gdb-4.11
+ ./configure HOST
+ make
+
+where HOST is an identifier such as `sun4' or `decstation', that
+identifies the platform where GDB will run. (You can often leave off
+HOST; `configure' tries to guess the correct value by examining your
+system.)
+
+ Running `configure HOST' and then running `make' builds the `bfd',
+`readline', `mmalloc', and `libiberty' libraries, then `gdb' itself.
+The configured source files, and the binaries, are left in the
+corresponding source directories.
+
+ `configure' is a Bourne-shell (`/bin/sh') script; if your system
+does not recognize this automatically when you run a different shell,
+you may need to run `sh' on it explicitly:
+
+ sh configure HOST
+
+ If you run `configure' from a directory that contains source
+directories for multiple libraries or programs, such as the `gdb-4.11'
+source directory for version 4.11, `configure' creates configuration
+files for every directory level underneath (unless you tell it not to,
+with the `--norecursion' option).
+
+ You can run the `configure' script from any of the subordinate
+directories in the GDB distribution if you only want to configure that
+subdirectory, but be sure to specify a path to it.
+
+ For example, with version 4.11, type the following to configure only
+the `bfd' subdirectory:
+
+ cd gdb-4.11/bfd
+ ../configure HOST
+
+ You can install `gdb' anywhere; it has no hardwired paths. However,
+you should make sure that the shell on your path (named by the `SHELL'
+environment variable) is publicly readable. Remember that GDB uses the
+shell to start your program--some systems refuse to let GDB debug child
+processes whose programs are not readable.
+
+* Menu:
+
+* Separate Objdir:: Compiling GDB in another directory
+* Config Names:: Specifying names for hosts and targets
+* configure Options:: Summary of options for configure
+
+
+File: gdb.info, Node: Separate Objdir, Next: Config Names, Up: Installing GDB
+
+Compiling GDB in another directory
+==================================
+
+ If you want to run GDB versions for several host or target machines,
+you need a different `gdb' compiled for each combination of host and
+target. `configure' is designed to make this easy by allowing you to
+generate each configuration in a separate subdirectory, rather than in
+the source directory. If your `make' program handles the `VPATH'
+feature (GNU `make' does), running `make' in each of these directories
+builds the `gdb' program specified there.
+
+ To build `gdb' in a separate directory, run `configure' with the
+`--srcdir' option to specify where to find the source. (You also need
+to specify a path to find `configure' itself from your working
+directory. If the path to `configure' would be the same as the
+argument to `--srcdir', you can leave out the `--srcdir' option; it
+will be assumed.)
+
+ For example, with version 4.11, you can build GDB in a separate
+directory for a Sun 4 like this:
+
+ cd gdb-4.11
+ mkdir ../gdb-sun4
+ cd ../gdb-sun4
+ ../gdb-4.11/configure sun4
+ make
+
+ When `configure' builds a configuration using a remote source
+directory, it creates a tree for the binaries with the same structure
+(and using the same names) as the tree under the source directory. In
+the example, you'd find the Sun 4 library `libiberty.a' in the
+directory `gdb-sun4/libiberty', and GDB itself in `gdb-sun4/gdb'.
+
+ One popular reason to build several GDB configurations in separate
+directories is to configure GDB for cross-compiling (where GDB runs on
+one machine--the host--while debugging programs that run on another
+machine--the target). You specify a cross-debugging target by giving
+the `--target=TARGET' option to `configure'.
+
+ When you run `make' to build a program or library, you must run it
+in a configured directory--whatever directory you were in when you
+called `configure' (or one of its subdirectories).
+
+ The `Makefile' that `configure' generates in each source directory
+also runs recursively. If you type `make' in a source directory such
+as `gdb-4.11' (or in a separate configured directory configured with
+`--srcdir=PATH/gdb-4.11'), you will build all the required libraries,
+and then build GDB.
+
+ When you have multiple hosts or targets configured in separate
+directories, you can run `make' on them in parallel (for example, if
+they are NFS-mounted on each of the hosts); they will not interfere
+with each other.
+
+
+File: gdb.info, Node: Config Names, Next: configure Options, Prev: Separate Objdir, Up: Installing GDB
+
+Specifying names for hosts and targets
+======================================
+
+ The specifications used for hosts and targets in the `configure'
+script are based on a three-part naming scheme, but some short
+predefined aliases are also supported. The full naming scheme encodes
+three pieces of information in the following pattern:
+
+ ARCHITECTURE-VENDOR-OS
+
+ For example, you can use the alias `sun4' as a HOST argument, or as
+the value for TARGET in a `--target=TARGET' option. The equivalent
+full name is `sparc-sun-sunos4'.
+
+ The `configure' script accompanying GDB does not provide any query
+facility to list all supported host and target names or aliases.
+`configure' calls the Bourne shell script `config.sub' to map
+abbreviations to full names; you can read the script, if you wish, or
+you can use it to test your guesses on abbreviations--for example:
+
+ % sh config.sub sun4
+ sparc-sun-sunos4.1.1
+ % sh config.sub sun3
+ m68k-sun-sunos4.1.1
+ % sh config.sub decstation
+ mips-dec-ultrix4.2
+ % sh config.sub hp300bsd
+ m68k-hp-bsd
+ % sh config.sub i386v
+ i386-unknown-sysv
+ % sh config.sub i786v
+ Invalid configuration `i786v': machine `i786v' not recognized
+
+`config.sub' is also distributed in the GDB source directory
+(`gdb-4.11', for version 4.11).
+
+
+File: gdb.info, Node: configure Options, Prev: Config Names, Up: Installing GDB
+
+`configure' options
+===================
+
+ Here is a summary of the `configure' options and arguments that are
+most often useful for building GDB. `configure' also has several other
+options not listed here. *note : (configure.info)What Configure Does,
+for a full explanation of `configure'.
+
+ configure [--help]
+ [--prefix=DIR]
+ [--srcdir=PATH]
+ [--norecursion] [--rm]
+ [--target=TARGET] HOST
+
+You may introduce options with a single `-' rather than `--' if you
+prefer; but you may abbreviate option names if you use `--'.
+
+`--help'
+ Display a quick summary of how to invoke `configure'.
+
+`-prefix=DIR'
+ Configure the source to install programs and files under directory
+ `DIR'.
+
+`--srcdir=PATH'
+ *Warning: using this option requires GNU `make', or another `make'
+ that implements the `VPATH' feature.*
+ Use this option to make configurations in directories separate
+ from the GDB source directories. Among other things, you can use
+ this to build (or maintain) several configurations simultaneously,
+ in separate directories. `configure' writes configuration
+ specific files in the current directory, but arranges for them to
+ use the source in the directory PATH. `configure' will create
+ directories under the working directory in parallel to the source
+ directories below PATH.
+
+`--norecursion'
+ Configure only the directory level where `configure' is executed;
+ do not propagate configuration to subdirectories.
+
+`--rm'
+ *Remove* files otherwise built during configuration.
+
+`--target=TARGET'
+ Configure GDB for cross-debugging programs running on the specified
+ TARGET. Without this option, GDB is configured to debug programs
+ that run on the same machine (HOST) as GDB itself.
+
+ There is no convenient way to generate a list of all available
+ targets.
+
+`HOST ...'
+ Configure GDB to run on the specified HOST.
+
+ There is no convenient way to generate a list of all available
+ hosts.
+
+`configure' accepts other options, for compatibility with configuring
+other GNU tools recursively; but these are the only options that affect
+GDB or its supporting libraries.
+
diff --git a/gnu/usr.bin/gdb/doc/gdb.info-8 b/gnu/usr.bin/gdb/doc/gdb.info-8
new file mode 100644
index 0000000..1d259e0
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/gdb.info-8
@@ -0,0 +1,657 @@
+This is Info file ./gdb.info, produced by Makeinfo-1.52 from the input
+file gdb.texinfo.
+
+START-INFO-DIR-ENTRY
+* Gdb:: The GNU debugger.
+END-INFO-DIR-ENTRY
+ This file documents the GNU debugger GDB.
+
+ This is Edition 4.09, August 1993, of `Debugging with GDB: the GNU
+Source-Level Debugger' for GDB Version 4.11.
+
+ Copyright (C) 1988, '89, '90, '91, '92, '93 Free Software
+Foundation, Inc.
+
+ Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of
+this manual under the conditions for verbatim copying, provided also
+that the entire resulting derived work is distributed under the terms
+of a permission notice identical to this one.
+
+ Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions.
+
+
+File: gdb.info, Node: Index, Prev: Installing GDB, Up: Top
+
+Index
+*****
+
+* Menu:
+
+* #: Command Syntax.
+* $bpnum: Set Breaks.
+* $cdir: Source Path.
+* $cwd: Source Path.
+* $_: Convenience Vars.
+* $__: Convenience Vars.
+* .: M2 Scope.
+* .esgdbinit: Command Files.
+* .os68gdbinit: Command Files.
+* .vxgdbinit: Command Files.
+* /proc: Process Information.
+* 386: Remote Serial.
+* 680x0: Remote Serial.
+* @: Arrays.
+* # in Modula-2: GDB/M2.
+* $$: Value History.
+* $_ and info breakpoints: Set Breaks.
+* $_ and info line: Machine Code.
+* $_, $__, and value history: Memory.
+* $: Value History.
+* breakpoint subroutine, remote: Stub Contents.
+* heuristic-fence-post (MIPS): MIPS Stack.
+* remotedebug, MIPS protocol: MIPS Remote.
+* retransmit-timeout, MIPS protocol: MIPS Remote.
+* timeout, MIPS protocol: MIPS Remote.
+* vi style command editing: Readline Vi Mode.
+* .gdbinit: Command Files.
+* COFF versus C++: Cplus expressions.
+* ECOFF and C++: Cplus expressions.
+* ELF/DWARF and C++: Cplus expressions.
+* ELF/stabs and C++: Cplus expressions.
+* XCOFF and C++: Cplus expressions.
+* GDB bugs, reporting: Bug Reporting.
+* {TYPE}: Expressions.
+* a.out and C++: Cplus expressions.
+* abbreviation: Command Syntax.
+* active targets: Active Targets.
+* add-symbol-file: Files.
+* add-syms: Renamed Commands.
+* AMD 29K register stack: Registers.
+* AMD EB29K: Target Commands.
+* AMD29K via UDI: UDI29K Remote.
+* arguments (to your program): Arguments.
+* artificial array: Arrays.
+* assembly instructions: Machine Code.
+* assignment: Assignment.
+* attach: Attach.
+* attach: Attach.
+* automatic display: Auto Display.
+* b: Set Breaks.
+* backtrace: Backtrace.
+* break: Set Breaks.
+* break in overloaded functions: Debugging C plus plus.
+* breakpoint commands: Break Commands.
+* breakpoint conditions: Conditions.
+* breakpoint numbers: Breakpoints.
+* breakpoint on memory address: Breakpoints.
+* breakpoint on variable modification: Breakpoints.
+* breakpoints: Breakpoints.
+* bt: Backtrace.
+* bug criteria: Bug Criteria.
+* bug reports: Bug Reporting.
+* bugs in GDB: GDB Bugs.
+* c: Continuing and Stepping.
+* C and C++: C.
+* C and C++ checks: C Checks.
+* C and C++ constants: C Operators.
+* C and C++ defaults: C Defaults.
+* C and C++ operators: C.
+* C++: C.
+* C++ and object formats: Cplus expressions.
+* C++ exception handling: Debugging C plus plus.
+* C++ scope resolution: Variables.
+* C++ support, not in COFF: Cplus expressions.
+* C++ symbol decoding style: Print Settings.
+* C++ symbol display: Debugging C plus plus.
+* call: Calling.
+* call overloaded functions: Cplus expressions.
+* call stack: Stack.
+* calling functions: Calling.
+* calling make: Shell Commands.
+* casts, to view memory: Expressions.
+* catch: Exception Handling.
+* catch exceptions: Frame Info.
+* cd: Working Directory.
+* cdir: Source Path.
+* checks, range: Type Checking.
+* checks, type: Checks.
+* checksum, for GDB remote: Protocol.
+* clear: Delete Breaks.
+* clearing breakpoints, watchpoints: Delete Breaks.
+* colon, doubled as scope operator: M2 Scope.
+* colon-colon: M2 Scope.
+* colon-colon: Variables.
+* command files: Hooks.
+* command files: Command Files.
+* command line editing: Editing.
+* commands: Break Commands.
+* commands for C++: Debugging C plus plus.
+* commands to STDBUG (ST2000): ST2000 Remote.
+* comment: Command Syntax.
+* compilation directory: Source Path.
+* completion: Completion.
+* completion of quoted strings: Completion.
+* condition: Conditions.
+* conditional breakpoints: Conditions.
+* configuring GDB: Installing GDB.
+* confirmation: Messages/Warnings.
+* connect (to STDBUG): ST2000 Remote.
+* continue: Continuing and Stepping.
+* continuing: Continuing and Stepping.
+* controlling terminal: Input/Output.
+* convenience variables: Convenience Vars.
+* core: Files.
+* core dump file: Files.
+* core-file: Files.
+* CPU simulator: Simulator.
+* crash of debugger: Bug Criteria.
+* current directory: Source Path.
+* cwd: Source Path.
+* d: Delete Breaks.
+* debugger crash: Bug Criteria.
+* debugging optimized code: Compilation.
+* debugging stub, example: Protocol.
+* debugging target: Targets.
+* define: Define.
+* delete: Delete Breaks.
+* delete breakpoints: Delete Breaks.
+* delete display: Auto Display.
+* delete environment: Renamed Commands.
+* deleting breakpoints, watchpoints: Delete Breaks.
+* detach: Attach.
+* device: Hitachi Remote.
+* directories for source files: Source Path.
+* directory: Source Path.
+* directory, compilation: Source Path.
+* directory, current: Source Path.
+* dis: Disabling.
+* disable: Disabling.
+* disable breakpoints: Disabling.
+* disable display: Auto Display.
+* disabled breakpoints: Disabling.
+* disassemble: Machine Code.
+* display: Auto Display.
+* display of expressions: Auto Display.
+* do: Selection.
+* document: Define.
+* documentation: Formatting Documentation.
+* down: Selection.
+* down-silently: Selection.
+* download to H8/300 or H8/500: Files.
+* download to Hitachi SH: Files.
+* download to Nindy-960: Files.
+* download to VxWorks: VxWorks Download.
+* dynamic linking: Files.
+* eb.log: Remote Log.
+* EB29K board: EB29K Remote.
+* EBMON: Comms (EB29K).
+* echo: Output.
+* editing: Editing.
+* editing-mode: Readline Init Syntax.
+* emacs: Emacs.
+* enable: Disabling.
+* enable breakpoints: Disabling.
+* enable display: Auto Display.
+* enabled breakpoints: Disabling.
+* end: Break Commands.
+* entering numbers: Numbers.
+* environment (of your program): Environment.
+* error on valid input: Bug Criteria.
+* event designators: Event Designators.
+* examining data: Data.
+* examining memory: Memory.
+* exception handlers: Exception Handling.
+* exception handlers: Frame Info.
+* exceptionHandler: Bootstrapping.
+* exec-file: Files.
+* executable file: Files.
+* exiting GDB: Quitting GDB.
+* expansion: History Interaction.
+* expressions: Expressions.
+* expressions in C or C++: C.
+* expressions in C++: Cplus expressions.
+* expressions in Modula-2: Modula-2.
+* f: Selection.
+* fatal signal: Bug Criteria.
+* fatal signals: Signals.
+* fg: Continuing and Stepping.
+* file: Files.
+* finish: Continuing and Stepping.
+* flinching: Messages/Warnings.
+* floating point: Floating Point Hardware.
+* floating point registers: Registers.
+* floating point, MIPS remote: MIPS Remote.
+* flush_i_cache: Bootstrapping.
+* foo: Symbol Errors.
+* format options: Print Settings.
+* formatted output: Output Formats.
+* Fortran: Summary.
+* forward-search: Search.
+* frame: Selection.
+* frame: Frames.
+* frame number: Frames.
+* frame pointer: Frames.
+* frameless execution: Frames.
+* g++: C.
+* GDB reference card: Formatting Documentation.
+* gdbserver: Server.
+* getDebugChar: Bootstrapping.
+* GNU C++: C.
+* h: Help.
+* H8/300 or H8/500 download: Files.
+* H8/300 or H8/500 simulator: Simulator.
+* handle: Signals.
+* handle_exception: Stub Contents.
+* handling signals: Signals.
+* help: Help.
+* help target: Target Commands.
+* help user-defined: Define.
+* history expansion: History.
+* history file: History.
+* history number: Value History.
+* history save: History.
+* history size: History.
+* history substitution: History.
+* Hitachi SH download: Files.
+* Hitachi SH simulator: Simulator.
+* horizontal-scroll-mode: Readline Init Syntax.
+* i: Help.
+* i/o: Input/Output.
+* i386-stub.c: Remote Serial.
+* i960: i960-Nindy Remote.
+* ignore: Conditions.
+* ignore count (of breakpoint): Conditions.
+* INCLUDE_RDB: VxWorks Remote.
+* info: Help.
+* info address: Symbols.
+* info all-registers: Registers.
+* info args: Frame Info.
+* info breakpoints: Set Breaks.
+* info catch: Frame Info.
+* info convenience: Renamed Commands.
+* info copying: Renamed Commands.
+* info directories: Renamed Commands.
+* info display: Auto Display.
+* info editing: Renamed Commands.
+* info f: Frame Info.
+* info files: Files.
+* info float: Floating Point Hardware.
+* info frame: Frame Info.
+* info frame: Show.
+* info functions: Symbols.
+* info history: Renamed Commands.
+* info line: Machine Code.
+* info locals: Frame Info.
+* info proc: Process Information.
+* info proc id: Process Information.
+* info proc mappings: Process Information.
+* info proc status: Process Information.
+* info proc times: Process Information.
+* info program: Stopping.
+* info registers: Registers.
+* info s: Backtrace.
+* info set: Help.
+* info share: Files.
+* info sharedlibrary: Files.
+* info signals: Signals.
+* info source: Symbols.
+* info source: Show.
+* info sources: Symbols.
+* info stack: Backtrace.
+* info target: Files.
+* info targets: Renamed Commands.
+* info terminal: Input/Output.
+* info types: Symbols.
+* info values: Renamed Commands.
+* info variables: Symbols.
+* info version: Renamed Commands.
+* info warranty: Renamed Commands.
+* info watchpoints: Set Watchpoints.
+* inheritance: Debugging C plus plus.
+* init file: Command Files.
+* init file name: Command Files.
+* initial frame: Frames.
+* innermost frame: Frames.
+* inspect: Data.
+* installation: Installing GDB.
+* instructions, assembly: Machine Code.
+* Intel: Remote Serial.
+* interaction, readline: Readline Interaction.
+* internal GDB breakpoints: Set Breaks.
+* interrupt: Quitting GDB.
+* interrupting remote programs: Debug Session.
+* invalid input: Bug Criteria.
+* jump: Jumping.
+* kill: Kill Process.
+* l: List.
+* languages: Languages.
+* latest breakpoint: Set Breaks.
+* leaving GDB: Quitting GDB.
+* linespec: List.
+* list: List.
+* listing machine instructions: Machine Code.
+* load: Files.
+* log file for EB29K: Remote Log.
+* m68k-stub.c: Remote Serial.
+* machine instructions: Machine Code.
+* maint info breakpoints: Set Breaks.
+* maint print psymbols: Symbols.
+* maint print symbols: Symbols.
+* make: Shell Commands.
+* mapped: Files.
+* mark-modified-lines: Readline Init Syntax.
+* member functions: Cplus expressions.
+* memory tracing: Breakpoints.
+* memory, viewing as typed object: Expressions.
+* memory-mapped symbol file: Files.
+* memset: Bootstrapping.
+* MIPS boards: MIPS Remote.
+* MIPS remote floating point: MIPS Remote.
+* MIPS stack: MIPS Stack.
+* Modula-2: Modula-2.
+* Modula-2 built-ins: M2 Operators.
+* Modula-2 checks: M2 Checks.
+* Modula-2 constants: Built-In Func/Proc.
+* Modula-2 defaults: M2 Defaults.
+* Modula-2 operators: M2 Operators.
+* Modula-2, deviations from: Deviations.
+* Motorola 680x0: Remote Serial.
+* multiple targets: Active Targets.
+* n: Continuing and Stepping.
+* names of symbols: Symbols.
+* namespace in C++: Cplus expressions.
+* negative breakpoint numbers: Set Breaks.
+* next: Continuing and Stepping.
+* nexti: Continuing and Stepping.
+* ni: Continuing and Stepping.
+* Nindy: i960-Nindy Remote.
+* number representation: Numbers.
+* numbers for breakpoints: Breakpoints.
+* object formats and C++: Cplus expressions.
+* online documentation: Help.
+* optimized code, debugging: Compilation.
+* outermost frame: Frames.
+* output: Output.
+* output formats: Output Formats.
+* overloading: Breakpoint Menus.
+* overloading in C++: Debugging C plus plus.
+* packets, reporting on stdout: Protocol.
+* partial symbol dump: Symbols.
+* patching binaries: Patching.
+* path: Environment.
+* pauses in output: Screen Size.
+* pipes: Starting.
+* prefer-visible-bell: Readline Init Syntax.
+* print: Data.
+* print settings: Print Settings.
+* printf: Output.
+* printing data: Data.
+* process image: Process Information.
+* prompt: Prompt.
+* protocol, GDB remote serial: Protocol.
+* ptype: Symbols.
+* putDebugChar: Bootstrapping.
+* pwd: Working Directory.
+* q: Quitting GDB.
+* quit: Quitting GDB.
+* quotes in commands: Completion.
+* quoting names: Symbols.
+* raise exceptions: Exception Handling.
+* range checking: Type Checking.
+* rbreak: Set Breaks.
+* reading symbols immediately: Files.
+* readline: Editing.
+* readnow: Files.
+* redirection: Input/Output.
+* reference card: Formatting Documentation.
+* reference declarations: Cplus expressions.
+* register stack, AMD29K: Registers.
+* registers: Registers.
+* regular expression: Set Breaks.
+* reloading symbols: Messages/Warnings.
+* remote connection without stubs: Server.
+* remote debugging: Remote.
+* remote programs, interrupting: Debug Session.
+* remote serial debugging summary: Debug Session.
+* remote serial debugging, overview: Remote Serial.
+* remote serial protocol: Protocol.
+* remote serial stub: Stub Contents.
+* remote serial stub list: Remote Serial.
+* remote serial stub, initialization: Stub Contents.
+* remote serial stub, main routine: Stub Contents.
+* remote stub, example: Protocol.
+* remote stub, support routines: Bootstrapping.
+* repeating commands: Command Syntax.
+* reporting bugs in GDB: GDB Bugs.
+* reset: Nindy Reset.
+* response time, MIPS debugging: MIPS Stack.
+* resuming execution: Continuing and Stepping.
+* RET: Command Syntax.
+* return: Returning.
+* returning from a function: Returning.
+* reverse-search: Search.
+* run: Starting.
+* running: Starting.
+* running 29K programs: EB29K Remote.
+* running VxWorks tasks: VxWorks Attach.
+* s: Continuing and Stepping.
+* saving symbol table: Files.
+* scope: M2 Scope.
+* search: Search.
+* searching: Search.
+* selected frame: Stack.
+* serial connections, debugging: Protocol.
+* serial device, Hitachi micros: Hitachi Remote.
+* serial line speed, Hitachi micros: Hitachi Remote.
+* serial line, target remote: Debug Session.
+* serial protocol, GDB remote: Protocol.
+* set addressprint: Renamed Commands.
+* set args: Arguments.
+* set array-max: Renamed Commands.
+* set arrayprint: Renamed Commands.
+* set asm-demangle: Renamed Commands.
+* set caution: Renamed Commands.
+* set check: Range Checking.
+* set check: Type Checking.
+* set check range: Range Checking.
+* set check type: Type Checking.
+* set complaints: Messages/Warnings.
+* set confirm: Messages/Warnings.
+* set demangle: Renamed Commands.
+* set demangle-style: Print Settings.
+* set editing: Editing.
+* set environment: Environment.
+* set height: Screen Size.
+* set history expansion: History.
+* set history filename: History.
+* set history save: History.
+* set history size: History.
+* set history write: Renamed Commands.
+* set language: Manually.
+* set listsize: List.
+* set mipsfpu off: MIPS Remote.
+* set prettyprint: Renamed Commands.
+* set print address: Print Settings.
+* set print array: Print Settings.
+* set print asm-demangle: Print Settings.
+* set print demangle: Print Settings.
+* set print elements: Print Settings.
+* set print max-symbolic-offset: Print Settings.
+* set print object: Print Settings.
+* set print pretty: Print Settings.
+* set print sevenbit-strings: Print Settings.
+* set print symbol-filename: Print Settings.
+* set print union: Print Settings.
+* set print vtbl: Print Settings.
+* set prompt: Prompt.
+* set radix: Numbers.
+* set remotedebug: Protocol.
+* set retransmit-timeout: MIPS Remote.
+* set rstack_high_address: Registers.
+* set screen-height: Renamed Commands.
+* set screen-width: Renamed Commands.
+* set sevenbit-strings: Renamed Commands.
+* set symbol-reloading: Messages/Warnings.
+* set timeout: MIPS Remote.
+* set unionprint: Renamed Commands.
+* set variable: Assignment.
+* set verbose: Messages/Warnings.
+* set vtblprint: Renamed Commands.
+* set width: Screen Size.
+* set write: Patching.
+* setting variables: Assignment.
+* setting watchpoints: Set Watchpoints.
+* set_debug_traps: Stub Contents.
+* share: Files.
+* shared libraries: Files.
+* sharedlibrary: Files.
+* shell: Shell Commands.
+* shell escape: Shell Commands.
+* show: Help.
+* show addressprint: Renamed Commands.
+* show args: Arguments.
+* show array-max: Renamed Commands.
+* show arrayprint: Renamed Commands.
+* show asm-demangle: Renamed Commands.
+* show caution: Renamed Commands.
+* show check range: Range Checking.
+* show check type: Type Checking.
+* show commands: History.
+* show complaints: Messages/Warnings.
+* show confirm: Messages/Warnings.
+* show convenience: Convenience Vars.
+* show copying: Help.
+* show demangle: Renamed Commands.
+* show demangle-style: Print Settings.
+* show directories: Source Path.
+* show editing: Editing.
+* show environment: Environment.
+* show height: Screen Size.
+* show history: History.
+* show history write: Renamed Commands.
+* show language: Show.
+* show listsize: List.
+* show paths: Environment.
+* show prettyprint: Renamed Commands.
+* show print address: Print Settings.
+* show print array: Print Settings.
+* show print asm-demangle: Print Settings.
+* show print demangle: Print Settings.
+* show print elements: Print Settings.
+* show print max-symbolic-offset: Print Settings.
+* show print object: Print Settings.
+* show print pretty: Print Settings.
+* show print sevenbit-strings: Print Settings.
+* show print symbol-filename: Print Settings.
+* show print union: Print Settings.
+* show print vtbl: Print Settings.
+* show prompt: Prompt.
+* show radix: Numbers.
+* show remotedebug: Protocol.
+* show retransmit-timeout: MIPS Remote.
+* show rstack_high_address: Registers.
+* show screen-height: Renamed Commands.
+* show screen-width: Renamed Commands.
+* show sevenbit-strings: Renamed Commands.
+* show timeout: MIPS Remote.
+* show unionprint: Renamed Commands.
+* show user: Define.
+* show values: Value History.
+* show verbose: Messages/Warnings.
+* show version: Help.
+* show vtblprint: Renamed Commands.
+* show warranty: Help.
+* show width: Screen Size.
+* show write: Patching.
+* si: Continuing and Stepping.
+* signal: Signaling.
+* signals: Signals.
+* silent: Break Commands.
+* sim: Simulator.
+* simulator: Simulator.
+* simulator, H8/300 or H8/500: Simulator.
+* simulator, Hitachi SH: Simulator.
+* simulator, Z8000: Simulator.
+* size of screen: Screen Size.
+* source: Command Files.
+* source path: Source Path.
+* sparc-stub.c: Remote Serial.
+* speed: Hitachi Remote.
+* st2000 CMD: ST2000 Remote.
+* ST2000 auxiliary commands: ST2000 Remote.
+* stack frame: Frames.
+* stack on MIPS: MIPS Stack.
+* stacking targets: Active Targets.
+* starting: Starting.
+* STDBUG commands (ST2000): ST2000 Remote.
+* step: Continuing and Stepping.
+* stepi: Continuing and Stepping.
+* stepping: Continuing and Stepping.
+* stub example, remote debugging: Protocol.
+* stupid questions: Messages/Warnings.
+* symbol decoding style, C++: Print Settings.
+* symbol dump: Symbols.
+* symbol names: Symbols.
+* symbol overloading: Breakpoint Menus.
+* symbol table: Files.
+* symbol-file: Files.
+* symbols, reading immediately: Files.
+* target: Targets.
+* target amd-eb: Target Commands.
+* target core: Target Commands.
+* target exec: Target Commands.
+* target hms: Target Commands.
+* target mips PORT: MIPS Remote.
+* target nindy: Target Commands.
+* target remote: Target Commands.
+* target sim: Target Commands.
+* target sim: Simulator.
+* target st2000: Target Commands.
+* target udi: Target Commands.
+* target vxworks: Target Commands.
+* tbreak: Set Breaks.
+* TCP port, target remote: Debug Session.
+* terminal: Input/Output.
+* this: Cplus expressions.
+* toggle-editing-mode: Readline Vi Mode.
+* tty: Input/Output.
+* type casting memory: Expressions.
+* type checking: Checks.
+* type conversions in C++: Cplus expressions.
+* u: Continuing and Stepping.
+* udi: UDI29K Remote.
+* UDI: UDI29K Remote.
+* undisplay: Auto Display.
+* unset: Renamed Commands.
+* unset environment: Environment.
+* until: Continuing and Stepping.
+* up: Selection.
+* up-silently: Selection.
+* user-defined command: Define.
+* value history: Value History.
+* variable name conflict: Variables.
+* variable values, wrong: Variables.
+* variables, setting: Assignment.
+* version number: Help.
+* VxWorks: VxWorks Remote.
+* watch: Set Watchpoints.
+* watchpoints: Breakpoints.
+* whatis: Symbols.
+* where: Backtrace.
+* word completion: Completion.
+* working directory: Source Path.
+* working directory (of your program): Working Directory.
+* working language: Languages.
+* writing into corefiles: Patching.
+* writing into executables: Patching.
+* wrong values: Variables.
+* x: Memory.
+* Z8000 simulator: Simulator.
+
+
diff --git a/gnu/usr.bin/gdb/doc/gdb.texinfo b/gnu/usr.bin/gdb/doc/gdb.texinfo
new file mode 100644
index 0000000..a2f293d
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/gdb.texinfo
@@ -0,0 +1,8591 @@
+\input texinfo @c -*-texinfo-*-
+@c Copyright (c) 1988 1989 1990 1991 1992 1993 Free Software Foundation, Inc.
+@c
+@c %**start of header
+@c makeinfo ignores cmds prev to setfilename, so its arg cannot make use
+@c of @set vars. However, you can override filename with makeinfo -o.
+@setfilename gdb.info
+@c
+@include gdb-cfg.texi
+@c
+@ifset GENERIC
+@settitle Debugging with @value{GDBN}
+@end ifset
+@ifclear GENERIC
+@settitle Debugging with @value{GDBN} (@value{TARGET})
+@end ifclear
+@setchapternewpage odd
+@c %**end of header
+
+@iftex
+@c @smallbook
+@c @cropmarks
+@end iftex
+
+@finalout
+@syncodeindex ky cp
+
+@c readline appendices use @vindex
+@syncodeindex vr cp
+
+@c ===> NOTE! <==
+@c Determine the edition number in *three* places by hand:
+@c 1. First ifinfo section 2. title page 3. top node
+@c To find the locations, search for !!set
+
+@c GDB CHANGELOG CONSULTED BETWEEN:
+@c Fri Oct 11 23:27:06 1991 John Gilmore (gnu at cygnus.com)
+@c Sat Dec 22 02:51:40 1990 John Gilmore (gnu at cygint)
+
+@c THIS MANUAL REQUIRES TEXINFO-2 macros and info-makers to format properly.
+
+@ifinfo
+@c This is a dir.info fragment to support semi-automated addition of
+@c manuals to an info tree. zoo@cygnus.com is developing this facility.
+@format
+START-INFO-DIR-ENTRY
+* Gdb:: The GNU debugger.
+END-INFO-DIR-ENTRY
+@end format
+@end ifinfo
+@c
+@c
+@ifinfo
+This file documents the GNU debugger @value{GDBN}.
+
+@c !!set edition, date, version
+This is Edition 4.09, August 1993,
+of @cite{Debugging with @value{GDBN}: the GNU Source-Level Debugger}
+for GDB Version @value{GDBVN}.
+
+Copyright (C) 1988, '89, '90, '91, '92, '93 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+@ignore
+Permission is granted to process this file through TeX and print the
+results, provided the printed document carries copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+@end ignore
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided also that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions.
+@end ifinfo
+
+@titlepage
+@title Debugging with @value{GDBN}
+@subtitle The GNU Source-Level Debugger
+@ifclear GENERIC
+@subtitle (@value{TARGET})
+@end ifclear
+@sp 1
+@c !!set edition, date, version
+@subtitle Edition 4.09, for @value{GDBN} version @value{GDBVN}
+@subtitle August 1993
+@author Richard M. Stallman and Roland H. Pesch
+@page
+@tex
+{\parskip=0pt
+\hfill (Send bugs and comments on @value{GDBN} to bug-gdb\@prep.ai.mit.edu.)\par
+\hfill {\it Debugging with @value{GDBN}}\par
+\hfill \TeX{}info \texinfoversion\par
+\hfill pesch\@cygnus.com\par
+}
+@end tex
+
+@vskip 0pt plus 1filll
+Copyright @copyright{} 1988, '89, '90, '91, '92, '93 Free Software
+Foundation, Inc.
+@sp 2
+Published by the Free Software Foundation @*
+675 Massachusetts Avenue, @*
+Cambridge, MA 02139 USA @*
+Printed copies are available for $20 each. @*
+ISBN 1-882114-11-6 @*
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided also that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions.
+@end titlepage
+@page
+
+@ifinfo
+@node Top
+@top Debugging with @value{GDBN}
+
+This file describes @value{GDBN}, the GNU symbolic debugger.
+
+@c !!set edition, date, version
+This is Edition 4.09, August 1993, for GDB Version @value{GDBVN}.
+
+@menu
+* Summary:: Summary of @value{GDBN}
+@ifset NOVEL
+* New Features:: New features since GDB version 3.5
+@end ifset
+@ifclear BARETARGET
+* Sample Session:: A sample @value{GDBN} session
+@end ifclear
+
+* Invocation:: Getting in and out of @value{GDBN}
+* Commands:: @value{GDBN} commands
+* Running:: Running programs under @value{GDBN}
+* Stopping:: Stopping and continuing
+* Stack:: Examining the stack
+* Source:: Examining source files
+* Data:: Examining data
+@ifclear CONLY
+* Languages:: Using @value{GDBN} with different languages
+@end ifclear
+@ifset CONLY
+* C:: C language support
+@end ifset
+@c remnant makeinfo bug, blank line needed after two end-ifs?
+
+* Symbols:: Examining the symbol table
+* Altering:: Altering execution
+* GDB Files:: @value{GDBN} files
+* Targets:: Specifying a debugging target
+* Controlling GDB:: Controlling @value{GDBN}
+* Sequences:: Canned sequences of commands
+@ifclear DOSHOST
+* Emacs:: Using @value{GDBN} under GNU Emacs
+@end ifclear
+
+* GDB Bugs:: Reporting bugs in @value{GDBN}
+* Command Line Editing:: Facilities of the readline library
+* Using History Interactively::
+@ifset NOVEL
+* Renamed Commands::
+@end ifset
+@ifclear PRECONFIGURED
+* Formatting Documentation:: How to format and print GDB documentation
+* Installing GDB:: Installing GDB
+@end ifclear
+
+* Index:: Index
+@end menu
+@end ifinfo
+
+@node Summary
+@unnumbered Summary of @value{GDBN}
+
+The purpose of a debugger such as @value{GDBN} is to allow you to see what is
+going on ``inside'' another program while it executes---or what another
+program was doing at the moment it crashed.
+
+@value{GDBN} can do four main kinds of things (plus other things in support of
+these) to help you catch bugs in the act:
+
+@itemize @bullet
+@item
+Start your program, specifying anything that might affect its behavior.
+
+@item
+Make your program stop on specified conditions.
+
+@item
+Examine what has happened, when your program has stopped.
+
+@item
+Change things in your program, so you can experiment with correcting the
+effects of one bug and go on to learn about another.
+@end itemize
+
+@ifclear CONLY
+@ifclear MOD2
+You can use @value{GDBN} to debug programs written in C or C++.
+@end ifclear
+@ifset MOD2
+You can use @value{GDBN} to debug programs written in C, C++, and
+Modula-2.
+@end ifset
+@ifset FORTRAN
+@cindex Fortran
+@value{GDBN} can be used to debug programs written in Fortran, although
+it does not yet support entering expressions, printing values, etc.
+using Fortran syntax. It may be necessary to refer to some variables
+with a trailing underscore.
+@end ifset
+@end ifclear
+
+@menu
+* Free Software:: Freely redistributable software
+* Contributors:: Contributors to GDB
+@end menu
+
+@node Free Software
+@unnumberedsec Free software
+
+@value{GDBN} is @dfn{free software}, protected by the GNU General Public License
+(GPL). The GPL gives you the freedom to copy or adapt a licensed
+program---but every person getting a copy also gets with it the
+freedom to modify that copy (which means that they must get access to
+the source code), and the freedom to distribute further copies.
+Typical software companies use copyrights to limit your freedoms; the
+Free Software Foundation uses the GPL to preserve these freedoms.
+
+Fundamentally, the General Public License is a license which says that
+you have these freedoms and that you cannot take these freedoms away
+from anyone else.
+
+@node Contributors
+@unnumberedsec Contributors to GDB
+
+Richard Stallman was the original author of GDB, and of many other GNU
+programs. Many others have contributed to its development. This
+section attempts to credit major contributors. One of the virtues of
+free software is that everyone is free to contribute to it; with
+regret, we cannot actually acknowledge everyone here. The file
+@file{ChangeLog} in the GDB distribution approximates a blow-by-blow
+account.
+
+Changes much prior to version 2.0 are lost in the mists of time.
+
+@quotation
+@emph{Plea:} Additions to this section are particularly welcome. If you
+or your friends (or enemies, to be evenhanded) have been unfairly
+omitted from this list, we would like to add your names!
+@end quotation
+
+So that they may not regard their long labor as thankless, we
+particularly thank those who shepherded GDB through major releases: Fred
+Fish (releases 4.11, 4.10, 4.9), Stu Grossman and John Gilmore (releases
+4.8, 4.7, 4.6, 4.5, 4.4), John Gilmore (releases 4.3, 4.2, 4.1, 4.0, and
+3.9); Jim Kingdon (releases 3.5, 3.4, 3.3); and Randy Smith (releases
+3.2, 3.1, 3.0). As major maintainer of GDB for some period, each
+contributed significantly to the structure, stability, and capabilities
+of the entire debugger.
+
+Richard Stallman, assisted at various times by Peter TerMaat, Chris
+Hanson, and Richard Mlynarik, handled releases through 2.8.
+
+@ifclear CONLY
+Michael Tiemann is the author of most of the GNU C++ support in GDB,
+with significant additional contributions from Per Bothner. James
+Clark wrote the GNU C++ demangler. Early work on C++ was by Peter
+TerMaat (who also did much general update work leading to release 3.0).
+@end ifclear
+
+GDB 4 uses the BFD subroutine library to examine multiple
+object-file formats; BFD was a joint project of David V.
+Henkel-Wallace, Rich Pixley, Steve Chamberlain, and John Gilmore.
+
+David Johnson wrote the original COFF support; Pace Willison did
+the original support for encapsulated COFF.
+
+Adam de Boor and Bradley Davis contributed the ISI Optimum V support.
+Per Bothner, Noboyuki Hikichi, and Alessandro Forin contributed MIPS
+support. Jean-Daniel Fekete contributed Sun 386i support. Chris
+Hanson improved the HP9000 support. Noboyuki Hikichi and Tomoyuki
+Hasei contributed Sony/News OS 3 support. David Johnson contributed
+Encore Umax support. Jyrki Kuoppala contributed Altos 3068 support.
+Keith Packard contributed NS32K support. Doug Rabson contributed
+Acorn Risc Machine support. Chris Smith contributed Convex support
+(and Fortran debugging). Jonathan Stone contributed Pyramid support.
+Michael Tiemann contributed SPARC support. Tim Tucker contributed
+support for the Gould NP1 and Gould Powernode. Pace Willison
+contributed Intel 386 support. Jay Vosburgh contributed Symmetry
+support.
+
+Rich Schaefer and Peter Schauer helped with support of SunOS shared
+libraries.
+
+Jay Fenlason and Roland McGrath ensured that GDB and GAS agree about
+several machine instruction sets.
+
+Patrick Duval, Ted Goldstein, Vikram Koka and Glenn Engel helped
+develop remote debugging. Intel Corporation and Wind River Systems
+contributed remote debugging modules for their products.
+
+Brian Fox is the author of the readline libraries providing
+command-line editing and command history.
+
+Andrew Beers of SUNY Buffalo wrote the language-switching code,
+@ifset MOD2
+the Modula-2 support,
+@end ifset
+and contributed the Languages chapter of this manual.
+
+Fred Fish wrote most of the support for Unix System Vr4.
+@ifclear CONLY
+He also enhanced the command-completion support to cover C++ overloaded
+symbols.
+@end ifclear
+
+Hitachi America, Ltd. sponsored the support for Hitachi microprocessors.
+
+@ifset NOVEL
+@node New Features
+@unnumbered New Features since GDB Version 3.5
+
+@table @emph
+@item Targets
+Using the new command @code{target}, you can select at runtime whether
+you are debugging local files, local processes, standalone systems over
+a serial port, realtime systems over a TCP/IP connection, etc. The
+command @code{load} can download programs into a remote system. Serial
+stubs are available for Motorola 680x0, Intel 80386, and Sparc remote
+systems; GDB also supports debugging realtime processes running under
+VxWorks, using SunRPC Remote Procedure Calls over TCP/IP to talk to a
+debugger stub on the target system. Internally, GDB now uses a function
+vector to mediate access to different targets; if you need to add your
+own support for a remote protocol, this makes it much easier.
+
+@item Watchpoints
+GDB now sports watchpoints as well as breakpoints. You can use a
+watchpoint to stop execution whenever the value of an expression
+changes, without having to predict a particular place in your program
+where this may happen.
+
+@item Wide Output
+Commands that issue wide output now insert newlines at places designed
+to make the output more readable.
+
+@item Object Code Formats
+GDB uses a new library called the Binary File Descriptor (BFD) Library
+to permit it to switch dynamically, without reconfiguration or
+recompilation, between different object-file formats. Formats currently
+supported are COFF, ELF, a.out, Intel 960 b.out, MIPS ECOFF, HPPA SOM
+(with stabs debugging), and S-records; files may be read as .o files,
+archive libraries, or core dumps. BFD is available as a subroutine
+library so that other programs may take advantage of it, and the other
+GNU binary utilities are being converted to use it.
+
+@item Configuration and Ports
+Compile-time configuration (to select a particular architecture and
+operating system) is much easier. The script @code{configure} now
+allows you to configure GDB as either a native debugger or a
+cross-debugger. @xref{Installing GDB}, for details on how to
+configure.
+
+@item Interaction
+The user interface to the GDB control variables is simpler,
+and is consolidated in two commands, @code{set} and @code{show}. Output
+lines are now broken at readable places, rather than overflowing onto
+the next line. You can suppress output of machine-level addresses,
+displaying only source language information.
+
+@item C++
+GDB now supports C++ multiple inheritance (if used with a GCC
+version 2 compiler), and also has limited support for C++ exception
+handling, with the commands @code{catch} and @code{info catch}: GDB
+can break when an exception is raised, before the stack is peeled back
+to the exception handler's context.
+
+@ifset MOD2
+@item Modula-2
+GDB now has preliminary support for the GNU Modula-2 compiler, currently
+under development at the State University of New York at Buffalo.
+Coordinated development of both GDB and the GNU Modula-2 compiler will
+continue. Other Modula-2 compilers are currently not supported, and
+attempting to debug programs compiled with them will likely result in an
+error as the symbol table of the executable is read in.
+@end ifset
+
+@item Command Rationalization
+Many GDB commands have been renamed to make them easier to remember
+and use. In particular, the subcommands of @code{info} and
+@code{show}/@code{set} are grouped to make the former refer to the state
+of your program, and the latter refer to the state of GDB itself.
+@xref{Renamed Commands}, for details on what commands were renamed.
+
+@item Shared Libraries
+GDB 4 can debug programs and core files that use SunOS, SVR4, or IBM RS/6000
+shared libraries.
+
+@item Reference Card
+GDB 4 has a reference card. @xref{Formatting Documentation,,Formatting
+the Documentation}, for instructions about how to print it.
+@end table
+@end ifset
+
+@ifclear BARETARGET
+@node Sample Session
+@chapter A Sample @value{GDBN} Session
+
+You can use this manual at your leisure to read all about @value{GDBN}.
+However, a handful of commands are enough to get started using the
+debugger. This chapter illustrates those commands.
+
+@iftex
+In this sample session, we emphasize user input like this: @b{input},
+to make it easier to pick out from the surrounding output.
+@end iftex
+
+@c FIXME: this example may not be appropriate for some configs, where
+@c FIXME...primary interest is in remote use.
+
+One of the preliminary versions of GNU @code{m4} (a generic macro
+processor) exhibits the following bug: sometimes, when we change its
+quote strings from the default, the commands used to capture one macro
+definition within another stop working. In the following short @code{m4}
+session, we define a macro @code{foo} which expands to @code{0000}; we
+then use the @code{m4} built-in @code{defn} to define @code{bar} as the
+same thing. However, when we change the open quote string to
+@code{<QUOTE>} and the close quote string to @code{<UNQUOTE>}, the same
+procedure fails to define a new synonym @code{baz}:
+
+@smallexample
+$ @b{cd gnu/m4}
+$ @b{./m4}
+@b{define(foo,0000)}
+
+@b{foo}
+0000
+@b{define(bar,defn(`foo'))}
+
+@b{bar}
+0000
+@b{changequote(<QUOTE>,<UNQUOTE>)}
+
+@b{define(baz,defn(<QUOTE>foo<UNQUOTE>))}
+@b{baz}
+@b{C-d}
+m4: End of input: 0: fatal error: EOF in string
+@end smallexample
+
+@noindent
+Let us use @value{GDBN} to try to see what is going on.
+
+@smallexample
+$ @b{@value{GDBP} m4}
+@c FIXME: this falsifies the exact text played out, to permit smallbook
+@c FIXME... format to come out better.
+GDB is free software and you are welcome to distribute copies
+ of it under certain conditions; type "show copying" to see
+ the conditions.
+There is absolutely no warranty for GDB; type "show warranty"
+ for details.
+GDB @value{GDBVN}, Copyright 1993 Free Software Foundation, Inc...
+(@value{GDBP})
+@end smallexample
+
+@noindent
+@value{GDBN} reads only enough symbol data to know where to find the rest when
+needed; as a result, the first prompt comes up very quickly. We now
+tell @value{GDBN} to use a narrower display width than usual, so that examples
+will fit in this manual.
+
+@smallexample
+(@value{GDBP}) @b{set width 70}
+@end smallexample
+
+@noindent
+We need to see how the @code{m4} built-in @code{changequote} works.
+Having looked at the source, we know the relevant subroutine is
+@code{m4_changequote}, so we set a breakpoint there with the @value{GDBN}
+@code{break} command.
+
+@smallexample
+(@value{GDBP}) @b{break m4_changequote}
+Breakpoint 1 at 0x62f4: file builtin.c, line 879.
+@end smallexample
+
+@noindent
+Using the @code{run} command, we start @code{m4} running under @value{GDBN}
+control; as long as control does not reach the @code{m4_changequote}
+subroutine, the program runs as usual:
+
+@smallexample
+(@value{GDBP}) @b{run}
+Starting program: /work/Editorial/gdb/gnu/m4/m4
+@b{define(foo,0000)}
+
+@b{foo}
+0000
+@end smallexample
+
+@noindent
+To trigger the breakpoint, we call @code{changequote}. @value{GDBN}
+suspends execution of @code{m4}, displaying information about the
+context where it stops.
+
+@smallexample
+@b{changequote(<QUOTE>,<UNQUOTE>)}
+
+Breakpoint 1, m4_changequote (argc=3, argv=0x33c70)
+ at builtin.c:879
+879 if (bad_argc(TOKEN_DATA_TEXT(argv[0]),argc,1,3))
+@end smallexample
+
+@noindent
+Now we use the command @code{n} (@code{next}) to advance execution to
+the next line of the current function.
+
+@smallexample
+(@value{GDBP}) @b{n}
+882 set_quotes((argc >= 2) ? TOKEN_DATA_TEXT(argv[1])\
+ : nil,
+@end smallexample
+
+@noindent
+@code{set_quotes} looks like a promising subroutine. We can go into it
+by using the command @code{s} (@code{step}) instead of @code{next}.
+@code{step} goes to the next line to be executed in @emph{any}
+subroutine, so it steps into @code{set_quotes}.
+
+@smallexample
+(@value{GDBP}) @b{s}
+set_quotes (lq=0x34c78 "<QUOTE>", rq=0x34c88 "<UNQUOTE>")
+ at input.c:530
+530 if (lquote != def_lquote)
+@end smallexample
+
+@noindent
+The display that shows the subroutine where @code{m4} is now
+suspended (and its arguments) is called a stack frame display. It
+shows a summary of the stack. We can use the @code{backtrace}
+command (which can also be spelled @code{bt}), to see where we are
+in the stack as a whole: the @code{backtrace} command displays a
+stack frame for each active subroutine.
+
+@smallexample
+(@value{GDBP}) @b{bt}
+#0 set_quotes (lq=0x34c78 "<QUOTE>", rq=0x34c88 "<UNQUOTE>")
+ at input.c:530
+#1 0x6344 in m4_changequote (argc=3, argv=0x33c70)
+ at builtin.c:882
+#2 0x8174 in expand_macro (sym=0x33320) at macro.c:242
+#3 0x7a88 in expand_token (obs=0x0, t=209696, td=0xf7fffa30)
+ at macro.c:71
+#4 0x79dc in expand_input () at macro.c:40
+#5 0x2930 in main (argc=0, argv=0xf7fffb20) at m4.c:195
+@end smallexample
+
+@noindent
+We will step through a few more lines to see what happens. The first two
+times, we can use @samp{s}; the next two times we use @code{n} to avoid
+falling into the @code{xstrdup} subroutine.
+
+@smallexample
+(@value{GDBP}) @b{s}
+0x3b5c 532 if (rquote != def_rquote)
+(@value{GDBP}) @b{s}
+0x3b80 535 lquote = (lq == nil || *lq == '\0') ? \
+def_lquote : xstrdup(lq);
+(@value{GDBP}) @b{n}
+536 rquote = (rq == nil || *rq == '\0') ? def_rquote\
+ : xstrdup(rq);
+(@value{GDBP}) @b{n}
+538 len_lquote = strlen(rquote);
+@end smallexample
+
+@noindent
+The last line displayed looks a little odd; we can examine the variables
+@code{lquote} and @code{rquote} to see if they are in fact the new left
+and right quotes we specified. We use the command @code{p}
+(@code{print}) to see their values.
+
+@smallexample
+(@value{GDBP}) @b{p lquote}
+$1 = 0x35d40 "<QUOTE>"
+(@value{GDBP}) @b{p rquote}
+$2 = 0x35d50 "<UNQUOTE>"
+@end smallexample
+
+@noindent
+@code{lquote} and @code{rquote} are indeed the new left and right quotes.
+To look at some context, we can display ten lines of source
+surrounding the current line with the @code{l} (@code{list}) command.
+
+@smallexample
+(@value{GDBP}) @b{l}
+533 xfree(rquote);
+534
+535 lquote = (lq == nil || *lq == '\0') ? def_lquote\
+ : xstrdup (lq);
+536 rquote = (rq == nil || *rq == '\0') ? def_rquote\
+ : xstrdup (rq);
+537
+538 len_lquote = strlen(rquote);
+539 len_rquote = strlen(lquote);
+540 @}
+541
+542 void
+@end smallexample
+
+@noindent
+Let us step past the two lines that set @code{len_lquote} and
+@code{len_rquote}, and then examine the values of those variables.
+
+@smallexample
+(@value{GDBP}) @b{n}
+539 len_rquote = strlen(lquote);
+(@value{GDBP}) @b{n}
+540 @}
+(@value{GDBP}) @b{p len_lquote}
+$3 = 9
+(@value{GDBP}) @b{p len_rquote}
+$4 = 7
+@end smallexample
+
+@noindent
+That certainly looks wrong, assuming @code{len_lquote} and
+@code{len_rquote} are meant to be the lengths of @code{lquote} and
+@code{rquote} respectively. We can set them to better values using
+the @code{p} command, since it can print the value of
+any expression---and that expression can include subroutine calls and
+assignments.
+
+@smallexample
+(@value{GDBP}) @b{p len_lquote=strlen(lquote)}
+$5 = 7
+(@value{GDBP}) @b{p len_rquote=strlen(rquote)}
+$6 = 9
+@end smallexample
+
+@noindent
+Is that enough to fix the problem of using the new quotes with the
+@code{m4} built-in @code{defn}? We can allow @code{m4} to continue
+executing with the @code{c} (@code{continue}) command, and then try the
+example that caused trouble initially:
+
+@smallexample
+(@value{GDBP}) @b{c}
+Continuing.
+
+@b{define(baz,defn(<QUOTE>foo<UNQUOTE>))}
+
+baz
+0000
+@end smallexample
+
+@noindent
+Success! The new quotes now work just as well as the default ones. The
+problem seems to have been just the two typos defining the wrong
+lengths. We allow @code{m4} exit by giving it an EOF as input:
+
+@smallexample
+@b{C-d}
+Program exited normally.
+@end smallexample
+
+@noindent
+The message @samp{Program exited normally.} is from @value{GDBN}; it
+indicates @code{m4} has finished executing. We can end our @value{GDBN}
+session with the @value{GDBN} @code{quit} command.
+
+@smallexample
+(@value{GDBP}) @b{quit}
+@end smallexample
+@end ifclear
+
+@node Invocation
+@chapter Getting In and Out of @value{GDBN}
+
+This chapter discusses how to start @value{GDBN}, and how to get out of it.
+(The essentials: type @samp{@value{GDBP}} to start GDB, and type @kbd{quit}
+or @kbd{C-d} to exit.)
+
+@menu
+* Invoking GDB:: How to start @value{GDBN}
+* Quitting GDB:: How to quit @value{GDBN}
+* Shell Commands:: How to use shell commands inside @value{GDBN}
+@end menu
+
+@node Invoking GDB
+@section Invoking @value{GDBN}
+
+@ifset H8EXCLUSIVE
+For details on starting up @value{GDBP} as a
+remote debugger attached to a Hitachi microprocessor, see @ref{Hitachi
+Remote,,@value{GDBN} and Hitachi Microprocessors}.
+@end ifset
+
+Invoke @value{GDBN} by running the program @code{@value{GDBP}}. Once started,
+@value{GDBN} reads commands from the terminal until you tell it to exit.
+
+You can also run @code{@value{GDBP}} with a variety of arguments and options,
+to specify more of your debugging environment at the outset.
+
+@ifset GENERIC
+The command-line options described here are designed
+to cover a variety of situations; in some environments, some of these
+options may effectively be unavailable.
+@end ifset
+
+The most usual way to start @value{GDBN} is with one argument,
+specifying an executable program:
+
+@example
+@value{GDBP} @var{program}
+@end example
+
+@ifclear BARETARGET
+@noindent
+You can also start with both an executable program and a core file
+specified:
+
+@example
+@value{GDBP} @var{program} @var{core}
+@end example
+
+You can, instead, specify a process ID as a second argument, if you want
+to debug a running process:
+
+@example
+@value{GDBP} @var{program} 1234
+@end example
+
+@noindent
+would attach @value{GDBN} to process @code{1234} (unless you also have a file
+named @file{1234}; @value{GDBN} does check for a core file first).
+
+Taking advantage of the second command-line argument requires a fairly
+complete operating system; when you use @value{GDBN} as a remote debugger
+attached to a bare board, there may not be any notion of ``process'',
+and there is often no way to get a core dump.
+@end ifclear
+
+@noindent
+You can further control how @value{GDBN} starts up by using command-line
+options. @value{GDBN} itself can remind you of the options available.
+
+@noindent
+Type
+
+@example
+@value{GDBP} -help
+@end example
+
+@noindent
+to display all available options and briefly describe their use
+(@samp{@value{GDBP} -h} is a shorter equivalent).
+
+All options and command line arguments you give are processed
+in sequential order. The order makes a difference when the
+@samp{-x} option is used.
+
+
+@menu
+@ifclear GENERIC
+@ifset REMOTESTUB
+* Remote Serial:: @value{GDBN} remote serial protocol
+@end ifset
+@ifset I960
+* i960-Nindy Remote:: @value{GDBN} with a remote i960 (Nindy)
+@end ifset
+@ifset AMD29K
+* UDI29K Remote:: @value{GDBN} and the UDI protocol for AMD29K
+* EB29K Remote:: @value{GDBN} with a remote EB29K
+@end ifset
+@ifset VXWORKS
+* VxWorks Remote:: @value{GDBN} and VxWorks
+@end ifset
+@ifset ST2000
+* ST2000 Remote:: @value{GDBN} with a Tandem ST2000
+@end ifset
+@ifset H8
+* Hitachi Remote:: @value{GDBN} and Hitachi Microprocessors
+@end ifset
+@ifset MIPS
+* MIPS Remote:: @value{GDBN} and MIPS boards
+@end ifset
+@ifset SIMS
+* Simulator:: Simulated CPU target
+@end ifset
+@end ifclear
+@c remnant makeinfo bug requires this blank line after *two* end-ifblahs:
+
+* File Options:: Choosing files
+* Mode Options:: Choosing modes
+@end menu
+
+@ifclear GENERIC
+@include remote.texi
+@end ifclear
+
+@node File Options
+@subsection Choosing files
+
+@ifclear BARETARGET
+When @value{GDBN} starts, it reads any arguments other than options as
+specifying an executable file and core file (or process ID). This is
+the same as if the arguments were specified by the @samp{-se} and
+@samp{-c} options respectively. (@value{GDBN} reads the first argument
+that does not have an associated option flag as equivalent to the
+@samp{-se} option followed by that argument; and the second argument
+that does not have an associated option flag, if any, as equivalent to
+the @samp{-c} option followed by that argument.)
+@end ifclear
+@ifset BARETARGET
+When @value{GDBN} starts, it reads any argument other than options as
+specifying an executable file. This is the same as if the argument was
+specified by the @samp{-se} option.
+@end ifset
+
+Many options have both long and short forms; both are shown in the
+following list. @value{GDBN} also recognizes the long forms if you truncate
+them, so long as enough of the option is present to be unambiguous.
+(If you prefer, you can flag option arguments with @samp{--} rather
+than @samp{-}, though we illustrate the more usual convention.)
+
+@table @code
+@item -symbols @var{file}
+@itemx -s @var{file}
+Read symbol table from file @var{file}.
+
+@item -exec @var{file}
+@itemx -e @var{file}
+Use file @var{file} as the executable file to execute when
+@ifset BARETARGET
+appropriate.
+@end ifset
+@ifclear BARETARGET
+appropriate, and for examining pure data in conjunction with a core
+dump.
+@end ifclear
+
+@item -se @var{file}
+Read symbol table from file @var{file} and use it as the executable
+file.
+
+@ifclear BARETARGET
+@item -core @var{file}
+@itemx -c @var{file}
+Use file @var{file} as a core dump to examine.
+
+@item -c @var{number}
+Connect to process ID @var{number}, as with the @code{attach} command
+(unless there is a file in core-dump format named @var{number}, in which
+case @samp{-c} specifies that file as a core dump to read).
+@end ifclear
+
+@item -command @var{file}
+@itemx -x @var{file}
+Execute @value{GDBN} commands from file @var{file}. @xref{Command
+Files,, Command files}.
+
+@item -directory @var{directory}
+@itemx -d @var{directory}
+Add @var{directory} to the path to search for source files.
+
+@ifclear BARETARGET
+@item -m
+@itemx -mapped
+@emph{Warning: this option depends on operating system facilities that are not
+supported on all systems.}@*
+If memory-mapped files are available on your system through the @code{mmap}
+system call, you can use this option
+to have @value{GDBN} write the symbols from your
+program into a reusable file in the current directory. If the program you are debugging is
+called @file{/tmp/fred}, the mapped symbol file will be @file{./fred.syms}.
+Future @value{GDBN} debugging sessions will notice the presence of this file,
+and will quickly map in symbol information from it, rather than reading
+the symbol table from the executable program.
+
+@c FIXME! Really host, not target?
+The @file{.syms} file is specific to the host machine where @value{GDBN}
+is run. It holds an exact image of the internal @value{GDBN} symbol
+table. It cannot be shared across multiple host platforms.
+@end ifclear
+
+@item -r
+@itemx -readnow
+Read each symbol file's entire symbol table immediately, rather than
+the default, which is to read it incrementally as it is needed.
+This makes startup slower, but makes future operations faster.
+@end table
+
+@ifclear BARETARGET
+The @code{-mapped} and @code{-readnow} options are typically combined in
+order to build a @file{.syms} file that contains complete symbol
+information. (@xref{Files,,Commands to specify files}, for information
+on @file{.syms} files.) A simple GDB invocation to do nothing but build
+a @file{.syms} file for future use is:
+
+@example
+ gdb -batch -nx -mapped -readnow programname
+@end example
+@end ifclear
+
+@node Mode Options
+@subsection Choosing modes
+
+You can run @value{GDBN} in various alternative modes---for example, in
+batch mode or quiet mode.
+
+@table @code
+@item -nx
+@itemx -n
+Do not execute commands from any initialization files (normally called
+@file{@value{GDBINIT}}). Normally, the commands in these files are
+executed after all the command options and arguments have been
+processed. @xref{Command Files,,Command files}.
+
+@item -quiet
+@itemx -q
+``Quiet''. Do not print the introductory and copyright messages. These
+messages are also suppressed in batch mode.
+
+@item -batch
+Run in batch mode. Exit with status @code{0} after processing all the
+command files specified with @samp{-x} (and all commands from
+initialization files, if not inhibited with @samp{-n}). Exit with
+nonzero status if an error occurs in executing the @value{GDBN} commands
+in the command files.
+
+Batch mode may be useful for running @value{GDBN} as a filter, for example to
+download and run a program on another computer; in order to make this
+more useful, the message
+
+@example
+Program exited normally.
+@end example
+
+@noindent
+(which is ordinarily issued whenever a program running under @value{GDBN} control
+terminates) is not issued when running in batch mode.
+
+@item -cd @var{directory}
+Run @value{GDBN} using @var{directory} as its working directory,
+instead of the current directory.
+
+@ifset LUCID
+@item -context @var{authentication}
+When the Energize programming system starts up @value{GDBN}, it uses this
+option to trigger an alternate mode of interaction.
+@var{authentication} is a pair of numeric codes that identify @value{GDBN}
+as a client in the Energize environment. Avoid this option when you run
+@value{GDBN} directly from the command line. See @ref{Energize,,Using
+@value{GDBN} with Energize} for more discussion of using @value{GDBN} with Energize.
+@end ifset
+
+@ifclear DOSHOST
+@item -fullname
+@itemx -f
+Emacs sets this option when it runs @value{GDBN} as a subprocess. It tells @value{GDBN}
+to output the full file name and line number in a standard,
+recognizable fashion each time a stack frame is displayed (which
+includes each time your program stops). This recognizable format looks
+like two @samp{\032} characters, followed by the file name, line number
+and character position separated by colons, and a newline. The
+Emacs-to-@value{GDBN} interface program uses the two @samp{\032} characters as
+a signal to display the source code for the frame.
+@end ifclear
+
+@ifset SERIAL
+@item -b @var{bps}
+Set the line speed (baud rate or bits per second) of any serial
+interface used by @value{GDBN} for remote debugging.
+
+@item -tty @var{device}
+Run using @var{device} for your program's standard input and output.
+@c FIXME: kingdon thinks there is more to -tty. Investigate.
+@end ifset
+@end table
+
+@node Quitting GDB
+@section Quitting @value{GDBN}
+@cindex exiting @value{GDBN}
+@cindex leaving @value{GDBN}
+
+@table @code
+@item quit
+@kindex quit
+@kindex q
+To exit @value{GDBN}, use the @code{quit} command (abbreviated @code{q}), or type
+an end-of-file character (usually @kbd{C-d}).
+@end table
+
+@cindex interrupt
+An interrupt (often @kbd{C-c}) will not exit from @value{GDBN}, but rather
+will terminate the action of any @value{GDBN} command that is in progress and
+return to @value{GDBN} command level. It is safe to type the interrupt
+character at any time because @value{GDBN} does not allow it to take effect
+until a time when it is safe.
+
+@ifclear BARETARGET
+If you have been using @value{GDBN} to control an attached process or
+device, you can release it with the @code{detach} command
+(@pxref{Attach, ,Debugging an already-running process}).
+@end ifclear
+
+@node Shell Commands
+@section Shell commands
+
+If you need to execute occasional shell commands during your
+debugging session, there is no need to leave or suspend @value{GDBN}; you can
+just use the @code{shell} command.
+
+@table @code
+@item shell @var{command string}
+@kindex shell
+@cindex shell escape
+Invoke a the standard shell to execute @var{command string}.
+@ifclear DOSHOST
+If it exists, the environment variable @code{SHELL} determines which
+shell to run. Otherwise @value{GDBN} uses @code{/bin/sh}.
+@end ifclear
+@end table
+
+The utility @code{make} is often needed in development environments.
+You do not have to use the @code{shell} command for this purpose in
+@value{GDBN}:
+
+@table @code
+@item make @var{make-args}
+@kindex make
+@cindex calling make
+Execute the @code{make} program with the specified
+arguments. This is equivalent to @samp{shell make @var{make-args}}.
+@end table
+
+@node Commands
+@chapter @value{GDBN} Commands
+
+You can abbreviate a @value{GDBN} command to the first few letters of the command
+name, if that abbreviation is unambiguous; and you can repeat certain
+@value{GDBN} commands by typing just @key{RET}. You can also use the @key{TAB}
+key to get @value{GDBN} to fill out the rest of a word in a command (or to
+show you the alternatives available, if there is more than one possibility).
+
+@menu
+* Command Syntax:: How to give commands to @value{GDBN}
+* Completion:: Command completion
+* Help:: How to ask @value{GDBN} for help
+@end menu
+
+@node Command Syntax
+@section Command syntax
+
+A @value{GDBN} command is a single line of input. There is no limit on
+how long it can be. It starts with a command name, which is followed by
+arguments whose meaning depends on the command name. For example, the
+command @code{step} accepts an argument which is the number of times to
+step, as in @samp{step 5}. You can also use the @code{step} command
+with no arguments. Some command names do not allow any arguments.
+
+@cindex abbreviation
+@value{GDBN} command names may always be truncated if that abbreviation is
+unambiguous. Other possible command abbreviations are listed in the
+documentation for individual commands. In some cases, even ambiguous
+abbreviations are allowed; for example, @code{s} is specially defined as
+equivalent to @code{step} even though there are other commands whose
+names start with @code{s}. You can test abbreviations by using them as
+arguments to the @code{help} command.
+
+@cindex repeating commands
+@kindex RET
+A blank line as input to @value{GDBN} (typing just @key{RET}) means to
+repeat the previous command. Certain commands (for example, @code{run})
+will not repeat this way; these are commands for which unintentional
+repetition might cause trouble and which you are unlikely to want to
+repeat.
+
+The @code{list} and @code{x} commands, when you repeat them with
+@key{RET}, construct new arguments rather than repeating
+exactly as typed. This permits easy scanning of source or memory.
+
+@value{GDBN} can also use @key{RET} in another way: to partition lengthy
+output, in a way similar to the common utility @code{more}
+(@pxref{Screen Size,,Screen size}). Since it is easy to press one
+@key{RET} too many in this situation, @value{GDBN} disables command
+repetition after any command that generates this sort of display.
+
+@kindex #
+@cindex comment
+Any text from a @kbd{#} to the end of the line is a comment; it does
+nothing. This is useful mainly in command files (@pxref{Command
+Files,,Command files}).
+
+@node Completion
+@section Command completion
+
+@cindex completion
+@cindex word completion
+@value{GDBN} can fill in the rest of a word in a command for you, if there is
+only one possibility; it can also show you what the valid possibilities
+are for the next word in a command, at any time. This works for @value{GDBN}
+commands, @value{GDBN} subcommands, and the names of symbols in your program.
+
+Press the @key{TAB} key whenever you want @value{GDBN} to fill out the rest
+of a word. If there is only one possibility, @value{GDBN} will fill in the
+word, and wait for you to finish the command (or press @key{RET} to
+enter it). For example, if you type
+
+@c FIXME "@key" does not distinguish its argument sufficiently to permit
+@c complete accuracy in these examples; space introduced for clarity.
+@c If texinfo enhancements make it unnecessary, it would be nice to
+@c replace " @key" by "@key" in the following...
+@example
+(@value{GDBP}) info bre @key{TAB}
+@end example
+
+@noindent
+@value{GDBN} fills in the rest of the word @samp{breakpoints}, since that is
+the only @code{info} subcommand beginning with @samp{bre}:
+
+@example
+(@value{GDBP}) info breakpoints
+@end example
+
+@noindent
+You can either press @key{RET} at this point, to run the @code{info
+breakpoints} command, or backspace and enter something else, if
+@samp{breakpoints} does not look like the command you expected. (If you
+were sure you wanted @code{info breakpoints} in the first place, you
+might as well just type @key{RET} immediately after @samp{info bre},
+to exploit command abbreviations rather than command completion).
+
+If there is more than one possibility for the next word when you press
+@key{TAB}, @value{GDBN} will sound a bell. You can either supply more
+characters and try again, or just press @key{TAB} a second time, and
+@value{GDBN} will display all the possible completions for that word. For
+example, you might want to set a breakpoint on a subroutine whose name
+begins with @samp{make_}, but when you type @kbd{b make_@key{TAB}} @value{GDBN}
+just sounds the bell. Typing @key{TAB} again will display all the
+function names in your program that begin with those characters, for
+example:
+
+@example
+(@value{GDBP}) b make_ @key{TAB}
+@exdent @value{GDBN} sounds bell; press @key{TAB} again, to see:
+make_a_section_from_file make_environ
+make_abs_section make_function_type
+make_blockvector make_pointer_type
+make_cleanup make_reference_type
+make_command make_symbol_completion_list
+(@value{GDBP}) b make_
+@end example
+
+@noindent
+After displaying the available possibilities, @value{GDBN} copies your
+partial input (@samp{b make_} in the example) so you can finish the
+command.
+
+If you just want to see the list of alternatives in the first place, you
+can press @kbd{M-?} rather than pressing @key{TAB} twice. @kbd{M-?}
+means @kbd{@key{META} ?}. You can type this
+@ifclear DOSHOST
+either by holding down a
+key designated as the @key{META} shift on your keyboard (if there is
+one) while typing @kbd{?}, or
+@end ifclear
+as @key{ESC} followed by @kbd{?}.
+
+@cindex quotes in commands
+@cindex completion of quoted strings
+Sometimes the string you need, while logically a ``word'', may contain
+parentheses or other characters that @value{GDBN} normally excludes from its
+notion of a word. To permit word completion to work in this situation,
+you may enclose words in @code{'} (single quote marks) in @value{GDBN} commands.
+
+@ifclear CONLY
+The most likely situation where you might need this is in typing the
+name of a C++ function. This is because C++ allows function overloading
+(multiple definitions of the same function, distinguished by argument
+type). For example, when you want to set a breakpoint you may need to
+distinguish whether you mean the version of @code{name} that takes an
+@code{int} parameter, @code{name(int)}, or the version that takes a
+@code{float} parameter, @code{name(float)}. To use the word-completion
+facilities in this situation, type a single quote @code{'} at the
+beginning of the function name. This alerts @value{GDBN} that it may need to
+consider more information than usual when you press @key{TAB} or
+@kbd{M-?} to request word completion:
+
+@example
+(@value{GDBP}) b 'bubble( @key{M-?}
+bubble(double,double) bubble(int,int)
+(@value{GDBP}) b 'bubble(
+@end example
+
+In some cases, @value{GDBN} can tell that completing a name will require
+quotes. When this happens, @value{GDBN} will insert the quote for you (while
+completing as much as it can) if you do not type the quote in the first
+place:
+
+@example
+(@value{GDBP}) b bub @key{TAB}
+@exdent @value{GDBN} alters your input line to the following, and rings a bell:
+(@value{GDBP}) b 'bubble(
+@end example
+
+@noindent
+In general, @value{GDBN} can tell that a quote is needed (and inserts it) if
+you have not yet started typing the argument list when you ask for
+completion on an overloaded symbol.
+@end ifclear
+
+
+@node Help
+@section Getting help
+@cindex online documentation
+@kindex help
+
+You can always ask @value{GDBN} itself for information on its commands, using the
+command @code{help}.
+
+@table @code
+@item help
+@itemx h
+@kindex h
+You can use @code{help} (abbreviated @code{h}) with no arguments to
+display a short list of named classes of commands:
+
+@smallexample
+(@value{GDBP}) help
+List of classes of commands:
+
+running -- Running the program
+stack -- Examining the stack
+data -- Examining data
+breakpoints -- Making program stop at certain points
+files -- Specifying and examining files
+status -- Status inquiries
+support -- Support facilities
+user-defined -- User-defined commands
+aliases -- Aliases of other commands
+obscure -- Obscure features
+
+Type "help" followed by a class name for a list of
+commands in that class.
+Type "help" followed by command name for full
+documentation.
+Command name abbreviations are allowed if unambiguous.
+(@value{GDBP})
+@end smallexample
+
+@item help @var{class}
+Using one of the general help classes as an argument, you can get a
+list of the individual commands in that class. For example, here is the
+help display for the class @code{status}:
+
+@smallexample
+(@value{GDBP}) help status
+Status inquiries.
+
+List of commands:
+
+@c Line break in "show" line falsifies real output, but needed
+@c to fit in smallbook page size.
+show -- Generic command for showing things set
+ with "set"
+info -- Generic command for printing status
+
+Type "help" followed by command name for full
+documentation.
+Command name abbreviations are allowed if unambiguous.
+(@value{GDBP})
+@end smallexample
+
+@item help @var{command}
+With a command name as @code{help} argument, @value{GDBN} will display a
+short paragraph on how to use that command.
+@end table
+
+In addition to @code{help}, you can use the @value{GDBN} commands @code{info}
+and @code{show} to inquire about the state of your program, or the state
+of @value{GDBN} itself. Each command supports many topics of inquiry; this
+manual introduces each of them in the appropriate context. The listings
+under @code{info} and under @code{show} in the Index point to
+all the sub-commands. @xref{Index}.
+
+@c @group
+@table @code
+@item info
+@kindex info
+@kindex i
+This command (abbreviated @code{i}) is for describing the state of your
+program. For example, you can list the arguments given to your program
+with @code{info args}, list the registers currently in use with @code{info
+registers}, or list the breakpoints you have set with @code{info breakpoints}.
+You can get a complete list of the @code{info} sub-commands with
+@w{@code{help info}}.
+
+@kindex show
+@item show
+In contrast, @code{show} is for describing the state of @value{GDBN} itself.
+You can change most of the things you can @code{show}, by using the
+related command @code{set}; for example, you can control what number
+system is used for displays with @code{set radix}, or simply inquire
+which is currently in use with @code{show radix}.
+
+@kindex info set
+To display all the settable parameters and their current
+values, you can use @code{show} with no arguments; you may also use
+@code{info set}. Both commands produce the same display.
+@c FIXME: "info set" violates the rule that "info" is for state of
+@c FIXME...program. Ck w/ GNU: "info set" to be called something else,
+@c FIXME...or change desc of rule---eg "state of prog and debugging session"?
+@end table
+@c @end group
+
+Here are three miscellaneous @code{show} subcommands, all of which are
+exceptional in lacking corresponding @code{set} commands:
+
+@table @code
+@kindex show version
+@cindex version number
+@item show version
+Show what version of @value{GDBN} is running. You should include this
+information in @value{GDBN} bug-reports. If multiple versions of @value{GDBN} are in
+use at your site, you may occasionally want to determine which version
+of @value{GDBN} you are running; as @value{GDBN} evolves, new commands are introduced,
+and old ones may wither away. The version number is also announced
+when you start @value{GDBN}.
+
+@kindex show copying
+@item show copying
+Display information about permission for copying @value{GDBN}.
+
+@kindex show warranty
+@item show warranty
+Display the GNU ``NO WARRANTY'' statement.
+@end table
+
+@node Running
+@chapter Running Programs Under @value{GDBN}
+
+When you run a program under @value{GDBN}, you must first generate
+debugging information when you compile it.
+@ifclear BARETARGET
+You may start it with its arguments, if any, in an environment of your
+choice. You may redirect your program's input and output, debug an
+already running process, or kill a child process.
+@end ifclear
+
+@menu
+* Compilation:: Compiling for debugging
+* Starting:: Starting your program
+@ifclear BARETARGET
+* Arguments:: Your program's arguments
+* Environment:: Your program's environment
+* Working Directory:: Your program's working directory
+* Input/Output:: Your program's input and output
+* Attach:: Debugging an already-running process
+* Kill Process:: Killing the child process
+* Process Information:: Additional process information
+@end ifclear
+@end menu
+
+@node Compilation
+@section Compiling for debugging
+
+In order to debug a program effectively, you need to generate
+debugging information when you compile it. This debugging information
+is stored in the object file; it describes the data type of each
+variable or function and the correspondence between source line numbers
+and addresses in the executable code.
+
+To request debugging information, specify the @samp{-g} option when you run
+the compiler.
+
+Many C compilers are unable to handle the @samp{-g} and @samp{-O}
+options together. Using those compilers, you cannot generate optimized
+executables containing debugging information.
+
+@value{NGCC}, the GNU C compiler, supports @samp{-g} with or without
+@samp{-O}, making it possible to debug optimized code. We recommend
+that you @emph{always} use @samp{-g} whenever you compile a program.
+You may think your program is correct, but there is no sense in pushing
+your luck.
+
+@cindex optimized code, debugging
+@cindex debugging optimized code
+When you debug a program compiled with @samp{-g -O}, remember that the
+optimizer is rearranging your code; the debugger will show you what is
+really there. Do not be too surprised when the execution path does not
+exactly match your source file! An extreme example: if you define a
+variable, but never use it, @value{GDBN} will never see that
+variable---because the compiler optimizes it out of existence.
+
+Some things do not work as well with @samp{-g -O} as with just
+@samp{-g}, particularly on machines with instruction scheduling. If in
+doubt, recompile with @samp{-g} alone, and if this fixes the problem,
+please report it as a bug (including a test case!).
+
+Older versions of the GNU C compiler permitted a variant option
+@w{@samp{-gg}} for debugging information. @value{GDBN} no longer supports this
+format; if your GNU C compiler has this option, do not use it.
+
+@need 2000
+@node Starting
+@section Starting your program
+@cindex starting
+@cindex running
+
+@table @code
+@item run
+@itemx r
+@kindex run
+Use the @code{run} command to start your program under @value{GDBN}. You must
+first specify the program name
+@ifset VXWORKS
+(except on VxWorks)
+@end ifset
+with an argument to @value{GDBN} (@pxref{Invocation, ,Getting In and
+Out of @value{GDBN}}), or by using the @code{file} or @code{exec-file}
+command (@pxref{Files, ,Commands to specify files}).
+
+@end table
+
+@ifclear BARETARGET
+If you are running your program in an execution environment that
+supports processes, @code{run} creates an inferior process and makes
+that process run your program. (In environments without processes,
+@code{run} jumps to the start of your program.)
+
+The execution of a program is affected by certain information it
+receives from its superior. @value{GDBN} provides ways to specify this
+information, which you must do @emph{before} starting your program. (You
+can change it after starting your program, but such changes will only affect
+your program the next time you start it.) This information may be
+divided into four categories:
+
+@table @asis
+@item The @emph{arguments.}
+Specify the arguments to give your program as the arguments of the
+@code{run} command. If a shell is available on your target, the shell
+is used to pass the arguments, so that you may use normal conventions
+(such as wildcard expansion or variable substitution) in describing
+the arguments. In Unix systems, you can control which shell is used
+with the @code{SHELL} environment variable. @xref{Arguments, ,Your
+program's arguments}.
+
+@item The @emph{environment.}
+Your program normally inherits its environment from @value{GDBN}, but you can
+use the @value{GDBN} commands @code{set environment} and @code{unset
+environment} to change parts of the environment that will be given to
+your program. @xref{Environment, ,Your program's environment}.
+
+@item The @emph{working directory.}
+Your program inherits its working directory from @value{GDBN}. You can set
+the @value{GDBN} working directory with the @code{cd} command in @value{GDBN}.
+@xref{Working Directory, ,Your program's working directory}.
+
+@item The @emph{standard input and output.}
+Your program normally uses the same device for standard input and
+standard output as @value{GDBN} is using. You can redirect input and output
+in the @code{run} command line, or you can use the @code{tty} command to
+set a different device for your program.
+@xref{Input/Output, ,Your program's input and output}.
+
+@cindex pipes
+@emph{Warning:} While input and output redirection work, you cannot use
+pipes to pass the output of the program you are debugging to another
+program; if you attempt this, @value{GDBN} is likely to wind up debugging the
+wrong program.
+@end table
+@end ifclear
+
+When you issue the @code{run} command, your program begins to execute
+immediately. @xref{Stopping, ,Stopping and continuing}, for discussion
+of how to arrange for your program to stop. Once your program has
+stopped, you may call functions in your program, using the @code{print}
+or @code{call} commands. @xref{Data, ,Examining Data}.
+
+If the modification time of your symbol file has changed since the
+last time @value{GDBN} read its symbols, @value{GDBN} will discard its symbol table and
+re-read it. When it does this, @value{GDBN} tries to retain your current
+breakpoints.
+
+@ifclear BARETARGET
+@node Arguments
+@section Your program's arguments
+
+@cindex arguments (to your program)
+The arguments to your program can be specified by the arguments of the
+@code{run} command. They are passed to a shell, which expands wildcard
+characters and performs redirection of I/O, and thence to your program.
+Your @code{SHELL} environment variable (if it exists) specifies what
+shell @value{GDBN} if you do not define @code{SHELL}, @value{GDBN} uses
+@code{/bin/sh}.
+
+@code{run} with no arguments uses the same arguments used by the previous
+@code{run}, or those set by the @code{set args} command.
+
+@kindex set args
+@table @code
+@item set args
+Specify the arguments to be used the next time your program is run. If
+@code{set args} has no arguments, @code{run} will execute your program
+with no arguments. Once you have run your program with arguments,
+using @code{set args} before the next @code{run} is the only way to run
+it again without arguments.
+
+@item show args
+@kindex show args
+Show the arguments to give your program when it is started.
+@end table
+
+@node Environment
+@section Your program's environment
+
+@cindex environment (of your program)
+The @dfn{environment} consists of a set of environment variables and
+their values. Environment variables conventionally record such things as
+your user name, your home directory, your terminal type, and your search
+path for programs to run. Usually you set up environment variables with
+the shell and they are inherited by all the other programs you run. When
+debugging, it can be useful to try running your program with a modified
+environment without having to start @value{GDBN} over again.
+
+@table @code
+@item path @var{directory}
+@kindex path
+Add @var{directory} to the front of the @code{PATH} environment variable
+(the search path for executables), for both @value{GDBN} and your program.
+You may specify several directory names, separated by @samp{:} or
+whitespace. If @var{directory} is already in the path, it is moved to
+the front, so it will be searched sooner.
+
+You can use the string @samp{$cwd} to refer to whatever is the current
+working directory at the time @value{GDBN} searches the path. If you
+use @samp{.} instead, it refers to the directory where you executed the
+@code{path} command. @value{GDBN} replaces @samp{.} in the
+@var{directory} argument (with the current path) before adding
+@var{directory} to the search path.
+@c 'path' is explicitly nonrepeatable, but RMS points out it is silly to
+@c document that, since repeating it would be a no-op.
+
+@item show paths
+@kindex show paths
+Display the list of search paths for executables (the @code{PATH}
+environment variable).
+
+@item show environment @r{[}@var{varname}@r{]}
+@kindex show environment
+Print the value of environment variable @var{varname} to be given to
+your program when it starts. If you do not supply @var{varname},
+print the names and values of all environment variables to be given to
+your program. You can abbreviate @code{environment} as @code{env}.
+
+@item set environment @var{varname} @r{[}=@r{]} @var{value}
+@kindex set environment
+Set environment variable @var{varname} to @var{value}. The value
+changes for your program only, not for @value{GDBN} itself. @var{value} may
+be any string; the values of environment variables are just strings, and
+any interpretation is supplied by your program itself. The @var{value}
+parameter is optional; if it is eliminated, the variable is set to a
+null value.
+@c "any string" here does not include leading, trailing
+@c blanks. Gnu asks: does anyone care?
+
+For example, this command:
+
+@example
+set env USER = foo
+@end example
+
+@noindent
+tells a Unix program, when subsequently run, that its user is named
+@samp{foo}. (The spaces around @samp{=} are used for clarity here; they
+are not actually required.)
+
+@item unset environment @var{varname}
+@kindex unset environment
+Remove variable @var{varname} from the environment to be passed to your
+program. This is different from @samp{set env @var{varname} =};
+@code{unset environment} removes the variable from the environment,
+rather than assigning it an empty value.
+@end table
+
+@emph{Warning:} @value{GDBN} runs your program using the shell indicated
+by your @code{SHELL} environment variable if it exists (or
+@code{/bin/sh} if not). If your @code{SHELL} variable names a shell
+that runs an initialization file---such as @file{.cshrc} for C-shell, or
+@file{.bashrc} for BASH---any variables you set in that file will affect
+your program. You may wish to move setting of environment variables to
+files that are only run when you sign on, such as @file{.login} or
+@file{.profile}.
+
+@node Working Directory
+@section Your program's working directory
+
+@cindex working directory (of your program)
+Each time you start your program with @code{run}, it inherits its
+working directory from the current working directory of @value{GDBN}.
+The @value{GDBN} working directory is initially whatever it inherited
+from its parent process (typically the shell), but you can specify a new
+working directory in @value{GDBN} with the @code{cd} command.
+
+The @value{GDBN} working directory also serves as a default for the commands
+that specify files for @value{GDBN} to operate on. @xref{Files, ,Commands to
+specify files}.
+
+@table @code
+@item cd @var{directory}
+@kindex cd
+Set the @value{GDBN} working directory to @var{directory}.
+
+@item pwd
+@kindex pwd
+Print the @value{GDBN} working directory.
+@end table
+
+@node Input/Output
+@section Your program's input and output
+
+@cindex redirection
+@cindex i/o
+@cindex terminal
+By default, the program you run under @value{GDBN} does input and output to
+the same terminal that @value{GDBN} uses. @value{GDBN} switches the terminal to
+its own terminal modes to interact with you, but it records the terminal
+modes your program was using and switches back to them when you continue
+running your program.
+
+@table @code
+@item info terminal
+@kindex info terminal
+Displays information recorded by @value{GDBN} about the terminal modes your
+program is using.
+@end table
+
+You can redirect your program's input and/or output using shell
+redirection with the @code{run} command. For example,
+
+@example
+run > outfile
+@end example
+
+@noindent
+starts your program, diverting its output to the file @file{outfile}.
+
+@kindex tty
+@cindex controlling terminal
+Another way to specify where your program should do input and output is
+with the @code{tty} command. This command accepts a file name as
+argument, and causes this file to be the default for future @code{run}
+commands. It also resets the controlling terminal for the child
+process, for future @code{run} commands. For example,
+
+@example
+tty /dev/ttyb
+@end example
+
+@noindent
+directs that processes started with subsequent @code{run} commands
+default to do input and output on the terminal @file{/dev/ttyb} and have
+that as their controlling terminal.
+
+An explicit redirection in @code{run} overrides the @code{tty} command's
+effect on the input/output device, but not its effect on the controlling
+terminal.
+
+When you use the @code{tty} command or redirect input in the @code{run}
+command, only the input @emph{for your program} is affected. The input
+for @value{GDBN} still comes from your terminal.
+
+@node Attach
+@section Debugging an already-running process
+@kindex attach
+@cindex attach
+
+@table @code
+@item attach @var{process-id}
+This command attaches to a running process---one that was started
+outside @value{GDBN}. (@code{info files} will show your active
+targets.) The command takes as argument a process ID. The usual way to
+find out the process-id of a Unix process is with the @code{ps} utility,
+or with the @samp{jobs -l} shell command.
+
+@code{attach} will not repeat if you press @key{RET} a second time after
+executing the command.
+@end table
+
+To use @code{attach}, your program must be running in an environment
+which supports processes; for example, @code{attach} does not work for
+programs on bare-board targets that lack an operating system. You must
+also have permission to send the process a signal.
+
+When using @code{attach}, you should first use the @code{file} command
+to specify the program running in the process and load its symbol table.
+@xref{Files, ,Commands to Specify Files}.
+
+The first thing @value{GDBN} does after arranging to debug the specified
+process is to stop it. You can examine and modify an attached process
+with all the @value{GDBN} commands that are ordinarily available when you start
+processes with @code{run}. You can insert breakpoints; you can step and
+continue; you can modify storage. If you would rather the process
+continue running, you may use the @code{continue} command after
+attaching @value{GDBN} to the process.
+
+@table @code
+@item detach
+@kindex detach
+When you have finished debugging the attached process, you can use the
+@code{detach} command to release it from @value{GDBN} control. Detaching
+the process continues its execution. After the @code{detach} command,
+that process and @value{GDBN} become completely independent once more, and you
+are ready to @code{attach} another process or start one with @code{run}.
+@code{detach} will not repeat if you press @key{RET} again after
+executing the command.
+@end table
+
+If you exit @value{GDBN} or use the @code{run} command while you have an attached
+process, you kill that process. By default, you will be asked for
+confirmation if you try to do either of these things; you can control
+whether or not you need to confirm by using the @code{set confirm} command
+(@pxref{Messages/Warnings, ,Optional warnings and messages}).
+
+@node Kill Process
+@c @group
+@section Killing the child process
+
+@table @code
+@item kill
+@kindex kill
+Kill the child process in which your program is running under @value{GDBN}.
+@end table
+
+This command is useful if you wish to debug a core dump instead of a
+running process. @value{GDBN} ignores any core dump file while your program
+is running.
+@c @end group
+
+On some operating systems, a program cannot be executed outside @value{GDBN}
+while you have breakpoints set on it inside @value{GDBN}. You can use the
+@code{kill} command in this situation to permit running your program
+outside the debugger.
+
+The @code{kill} command is also useful if you wish to recompile and
+relink your program, since on many systems it is impossible to modify an
+executable file while it is running in a process. In this case, when you
+next type @code{run}, @value{GDBN} will notice that the file has changed, and
+will re-read the symbol table (while trying to preserve your current
+breakpoint settings).
+
+@node Process Information
+@section Additional process information
+
+@kindex /proc
+@cindex process image
+Some operating systems provide a facility called @samp{/proc} that can
+be used to examine the image of a running process using file-system
+subroutines. If @value{GDBN} is configured for an operating system with this
+facility, the command @code{info proc} is available to report on several
+kinds of information about the process running your program.
+
+@table @code
+@item info proc
+@kindex info proc
+Summarize available information about the process.
+
+@item info proc mappings
+@kindex info proc mappings
+Report on the address ranges accessible in the program, with information
+on whether your program may read, write, or execute each range.
+
+@item info proc times
+@kindex info proc times
+Starting time, user CPU time, and system CPU time for your program and
+its children.
+
+@item info proc id
+@kindex info proc id
+Report on the process IDs related to your program: its own process ID,
+the ID of its parent, the process group ID, and the session ID.
+
+@item info proc status
+@kindex info proc status
+General information on the state of the process. If the process is
+stopped, this report includes the reason for stopping, and any signal
+received.
+
+@item info proc all
+Show all the above information about the process.
+@end table
+@end ifclear
+
+@node Stopping
+@chapter Stopping and Continuing
+
+The principal purposes of using a debugger are so that you can stop your
+program before it terminates; or so that, if your program runs into
+trouble, you can investigate and find out why.
+
+Inside @value{GDBN}, your program may stop for any of several reasons, such
+as
+@ifclear BARETARGET
+a signal,
+@end ifclear
+a breakpoint, or reaching a new line after a @value{GDBN}
+command such as @code{step}. You may then examine and change
+variables, set new breakpoints or remove old ones, and then continue
+execution. Usually, the messages shown by @value{GDBN} provide ample
+explanation of the status of your program---but you can also explicitly
+request this information at any time.
+
+@table @code
+@item info program
+@kindex info program
+Display information about the status of your program: whether it is
+running or not,
+@ifclear BARETARGET
+what process it is,
+@end ifclear
+and why it stopped.
+@end table
+
+@menu
+@ifclear CONLY
+* Breakpoints:: Breakpoints, watchpoints, and exceptions
+@end ifclear
+@ifset CONLY
+* Breakpoints:: Breakpoints and watchpoints
+@end ifset
+@c Remnant makeinfo bug requires blank line after *successful* end-if in menu:
+
+* Continuing and Stepping:: Resuming execution
+@ifset POSIX
+* Signals:: Signals
+@end ifset
+@end menu
+
+@c makeinfo node-defaulting requires adjacency of @node and sectioning cmds
+@c ...hence distribute @node Breakpoints over two possible @if expansions.
+@c
+@ifclear CONLY
+@node Breakpoints
+@section Breakpoints, watchpoints, and exceptions
+@end ifclear
+@ifset CONLY
+@node Breakpoints
+@section Breakpoints and watchpoints
+@end ifset
+
+@cindex breakpoints
+A @dfn{breakpoint} makes your program stop whenever a certain point in
+the program is reached. For each breakpoint, you can add various
+conditions to control in finer detail whether your program will stop.
+You can set breakpoints with the @code{break} command and its variants
+(@pxref{Set Breaks, ,Setting breakpoints}), to specify the place where
+your program should stop by line number, function name or exact address
+in the program.
+@ifclear CONLY
+In languages with exception handling (such as GNU C++), you can also set
+breakpoints where an exception is raised (@pxref{Exception Handling,
+,Breakpoints and exceptions}).
+@end ifclear
+
+@cindex watchpoints
+@cindex memory tracing
+@cindex breakpoint on memory address
+@cindex breakpoint on variable modification
+A @dfn{watchpoint} is a special breakpoint that stops your program
+when the value of an expression changes. You must use a different
+command to set watchpoints (@pxref{Set Watchpoints, ,Setting
+watchpoints}), but aside from that, you can manage a watchpoint like
+any other breakpoint: you enable, disable, and delete both breakpoints
+and watchpoints using the same commands.
+
+You can arrange to have values from your program displayed automatically
+whenever @value{GDBN} stops at a breakpoint. @xref{Auto Display,
+,Automatic display}.
+
+@cindex breakpoint numbers
+@cindex numbers for breakpoints
+@value{GDBN} assigns a number to each breakpoint or watchpoint when you
+create it; these numbers are successive integers starting with one. In
+many of the commands for controlling various features of breakpoints you
+use the breakpoint number to say which breakpoint you want to change.
+Each breakpoint may be @dfn{enabled} or @dfn{disabled}; if disabled, it has
+no effect on your program until you enable it again.
+
+@menu
+* Set Breaks:: Setting breakpoints
+* Set Watchpoints:: Setting watchpoints
+@ifclear CONLY
+* Exception Handling:: Breakpoints and exceptions
+@end ifclear
+
+* Delete Breaks:: Deleting breakpoints
+* Disabling:: Disabling breakpoints
+* Conditions:: Break conditions
+* Break Commands:: Breakpoint command lists
+@ifclear CONLY
+* Breakpoint Menus:: Breakpoint menus
+@end ifclear
+@ifclear BARETARGET
+* Error in Breakpoints:: ``Cannot insert breakpoints''
+@end ifclear
+@end menu
+
+@node Set Breaks
+@subsection Setting breakpoints
+
+@c FIXME LMB what does GDB do if no code on line of breakpt?
+@c consider in particular declaration with/without initialization.
+@c
+@c FIXME 2 is there stuff on this already? break at fun start, already init?
+
+@kindex break
+@kindex b
+@kindex $bpnum
+@cindex latest breakpoint
+Breakpoints are set with the @code{break} command (abbreviated
+@code{b}). The debugger convenience variable @samp{$bpnum} records the
+number of the beakpoint you've set most recently; see @ref{Convenience
+Vars,, Convenience variables}, for a discussion of what you can do with
+convenience variables.
+
+You have several ways to say where the breakpoint should go.
+
+@table @code
+@item break @var{function}
+Set a breakpoint at entry to function @var{function}.
+@ifclear CONLY
+When using source languages that permit overloading of symbols, such as
+C++, @var{function} may refer to more than one possible place to break.
+@xref{Breakpoint Menus,,Breakpoint menus}, for a discussion of that situation.
+@end ifclear
+
+@item break +@var{offset}
+@itemx break -@var{offset}
+Set a breakpoint some number of lines forward or back from the position
+at which execution stopped in the currently selected frame.
+
+@item break @var{linenum}
+Set a breakpoint at line @var{linenum} in the current source file.
+That file is the last file whose source text was printed. This
+breakpoint will stop your program just before it executes any of the
+code on that line.
+
+@item break @var{filename}:@var{linenum}
+Set a breakpoint at line @var{linenum} in source file @var{filename}.
+
+@item break @var{filename}:@var{function}
+Set a breakpoint at entry to function @var{function} found in file
+@var{filename}. Specifying a file name as well as a function name is
+superfluous except when multiple files contain similarly named
+functions.
+
+@item break *@var{address}
+Set a breakpoint at address @var{address}. You can use this to set
+breakpoints in parts of your program which do not have debugging
+information or source files.
+
+@item break
+When called without any arguments, @code{break} sets a breakpoint at
+the next instruction to be executed in the selected stack frame
+(@pxref{Stack, ,Examining the Stack}). In any selected frame but the
+innermost, this will cause your program to stop as soon as control
+returns to that frame. This is similar to the effect of a
+@code{finish} command in the frame inside the selected frame---except
+that @code{finish} does not leave an active breakpoint. If you use
+@code{break} without an argument in the innermost frame, @value{GDBN} will stop
+the next time it reaches the current location; this may be useful
+inside loops.
+
+@value{GDBN} normally ignores breakpoints when it resumes execution, until at
+least one instruction has been executed. If it did not do this, you
+would be unable to proceed past a breakpoint without first disabling the
+breakpoint. This rule applies whether or not the breakpoint already
+existed when your program stopped.
+
+@item break @dots{} if @var{cond}
+Set a breakpoint with condition @var{cond}; evaluate the expression
+@var{cond} each time the breakpoint is reached, and stop only if the
+value is nonzero---that is, if @var{cond} evaluates as true.
+@samp{@dots{}} stands for one of the possible arguments described
+above (or no argument) specifying where to break. @xref{Conditions,
+,Break conditions}, for more information on breakpoint conditions.
+
+@item tbreak @var{args}
+@kindex tbreak
+Set a breakpoint enabled only for one stop. @var{args} are the
+same as for the @code{break} command, and the breakpoint is set in the same
+way, but the breakpoint is automatically disabled after the first time your
+program stops there. @xref{Disabling, ,Disabling breakpoints}.
+
+@item rbreak @var{regex}
+@kindex rbreak
+@cindex regular expression
+@c FIXME what kind of regexp?
+Set breakpoints on all functions matching the regular expression
+@var{regex}. This command
+sets an unconditional breakpoint on all matches, printing a list of all
+breakpoints it set. Once these breakpoints are set, they are treated
+just like the breakpoints set with the @code{break} command. They can
+be deleted, disabled, made conditional, etc., in the standard ways.
+
+@ifclear CONLY
+When debugging C++ programs, @code{rbreak} is useful for setting
+breakpoints on overloaded functions that are not members of any special
+classes.
+@end ifclear
+
+@kindex info breakpoints
+@cindex @code{$_} and @code{info breakpoints}
+@item info breakpoints @r{[}@var{n}@r{]}
+@itemx info break @r{[}@var{n}@r{]}
+@itemx info watchpoints @r{[}@var{n}@r{]}
+Print a table of all breakpoints and watchpoints set and not
+deleted, with the following columns for each breakpoint:
+
+@table @emph
+@item Breakpoint Numbers
+@item Type
+Breakpoint or watchpoint.
+@item Disposition
+Whether the breakpoint is marked to be disabled or deleted when hit.
+@item Enabled or Disabled
+Enabled breakpoints are marked with @samp{y}. @samp{n} marks breakpoints
+that are not enabled.
+@item Address
+Where the breakpoint is in your program, as a memory address
+@item What
+Where the breakpoint is in the source for your program, as a file and
+line number.
+@end table
+
+@noindent
+If a breakpoint is conditional, @code{info break} shows the condition on
+the line following the affected breakpoint; breakpoint commands, if any,
+are listed after that.
+
+@noindent
+@code{info break} with a breakpoint
+number @var{n} as argument lists only that breakpoint. The
+convenience variable @code{$_} and the default examining-address for
+the @code{x} command are set to the address of the last breakpoint
+listed (@pxref{Memory, ,Examining memory}).
+@end table
+
+@value{GDBN} allows you to set any number of breakpoints at the same place in
+your program. There is nothing silly or meaningless about this. When
+the breakpoints are conditional, this is even useful
+(@pxref{Conditions, ,Break conditions}).
+
+@cindex negative breakpoint numbers
+@cindex internal @value{GDBN} breakpoints
+@value{GDBN} itself sometimes sets breakpoints in your program for special
+purposes, such as proper handling of @code{longjmp} (in C programs).
+These internal breakpoints are assigned negative numbers, starting with
+@code{-1}; @samp{info breakpoints} does not display them.
+
+You can see these breakpoints with the @value{GDBN} maintenance command
+@samp{maint info breakpoints}.
+
+@table @code
+@kindex maint info breakpoints
+@item maint info breakpoints
+Using the same format as @samp{info breakpoints}, display both the
+breakpoints you've set explicitly, and those @value{GDBN} is using for
+internal purposes. Internal breakpoints are shown with negative
+breakpoint numbers. The type column identifies what kind of breakpoint
+is shown:
+
+@table @code
+@item breakpoint
+Normal, explicitly set breakpoint.
+
+@item watchpoint
+Normal, explicitly set watchpoint.
+
+@item longjmp
+Internal breakpoint, used to handle correctly stepping through
+@code{longjmp} calls.
+
+@item longjmp resume
+Internal breakpoint at the target of a @code{longjmp}.
+
+@item until
+Temporary internal breakpoint used by the @value{GDBN} @code{until} command.
+
+@item finish
+Temporary internal breakpoint used by the @value{GDBN} @code{finish} command.
+@end table
+
+@end table
+
+
+@node Set Watchpoints
+@subsection Setting watchpoints
+@cindex setting watchpoints
+
+You can use a watchpoint to stop execution whenever the value of an
+expression changes, without having to predict a particular place
+where this may happen.
+
+Watchpoints currently execute two orders of magnitude more slowly than
+other breakpoints, but this can be well worth it to catch errors where
+you have no clue what part of your program is the culprit. Some
+processors provide special hardware to support watchpoint evaluation; future
+releases of @value{GDBN} will use such hardware if it is available.
+
+@table @code
+@kindex watch
+@item watch @var{expr}
+Set a watchpoint for an expression.
+
+@kindex info watchpoints
+@item info watchpoints
+This command prints a list of watchpoints and breakpoints; it is the
+same as @code{info break}.
+@end table
+
+@ifclear CONLY
+@node Exception Handling
+@subsection Breakpoints and exceptions
+@cindex exception handlers
+
+Some languages, such as GNU C++, implement exception handling. You can
+use @value{GDBN} to examine what caused your program to raise an exception,
+and to list the exceptions your program is prepared to handle at a
+given point in time.
+
+@table @code
+@item catch @var{exceptions}
+@kindex catch
+You can set breakpoints at active exception handlers by using the
+@code{catch} command. @var{exceptions} is a list of names of exceptions
+to catch.
+@end table
+
+You can use @code{info catch} to list active exception handlers.
+@xref{Frame Info, ,Information about a frame}.
+
+There are currently some limitations to exception handling in @value{GDBN}.
+These will be corrected in a future release.
+
+@itemize @bullet
+@item
+If you call a function interactively, @value{GDBN} normally returns
+control to you when the function has finished executing. If the call
+raises an exception, however, the call may bypass the mechanism that
+returns control to you and cause your program to simply continue
+running until it hits a breakpoint, catches a signal that @value{GDBN} is
+listening for, or exits.
+@item
+You cannot raise an exception interactively.
+@item
+You cannot interactively install an exception handler.
+@end itemize
+
+@cindex raise exceptions
+Sometimes @code{catch} is not the best way to debug exception handling:
+if you need to know exactly where an exception is raised, it is better to
+stop @emph{before} the exception handler is called, since that way you
+can see the stack before any unwinding takes place. If you set a
+breakpoint in an exception handler instead, it may not be easy to find
+out where the exception was raised.
+
+To stop just before an exception handler is called, you need some
+knowledge of the implementation. In the case of GNU C++, exceptions are
+raised by calling a library function named @code{__raise_exception}
+which has the following ANSI C interface:
+
+@example
+ /* @var{addr} is where the exception identifier is stored.
+ ID is the exception identifier. */
+ void __raise_exception (void **@var{addr}, void *@var{id});
+@end example
+
+@noindent
+To make the debugger catch all exceptions before any stack
+unwinding takes place, set a breakpoint on @code{__raise_exception}
+(@pxref{Breakpoints, ,Breakpoints; watchpoints; and exceptions}).
+
+With a conditional breakpoint (@pxref{Conditions, ,Break conditions})
+that depends on the value of @var{id}, you can stop your program when
+a specific exception is raised. You can use multiple conditional
+breakpoints to stop your program when any of a number of exceptions are
+raised.
+@end ifclear
+
+@node Delete Breaks
+@subsection Deleting breakpoints
+
+@cindex clearing breakpoints, watchpoints
+@cindex deleting breakpoints, watchpoints
+It is often necessary to eliminate a breakpoint or watchpoint once it
+has done its job and you no longer want your program to stop there. This
+is called @dfn{deleting} the breakpoint. A breakpoint that has been
+deleted no longer exists; it is forgotten.
+
+With the @code{clear} command you can delete breakpoints according to
+where they are in your program. With the @code{delete} command you can
+delete individual breakpoints or watchpoints by specifying their
+breakpoint numbers.
+
+It is not necessary to delete a breakpoint to proceed past it. @value{GDBN}
+automatically ignores breakpoints on the first instruction to be executed
+when you continue execution without changing the execution address.
+
+@table @code
+@item clear
+@kindex clear
+Delete any breakpoints at the next instruction to be executed in the
+selected stack frame (@pxref{Selection, ,Selecting a frame}). When
+the innermost frame is selected, this is a good way to delete a
+breakpoint where your program just stopped.
+
+@item clear @var{function}
+@itemx clear @var{filename}:@var{function}
+Delete any breakpoints set at entry to the function @var{function}.
+
+@item clear @var{linenum}
+@itemx clear @var{filename}:@var{linenum}
+Delete any breakpoints set at or within the code of the specified line.
+
+@item delete @r{[}breakpoints@r{]} @r{[}@var{bnums}@dots{}@r{]}
+@cindex delete breakpoints
+@kindex delete
+@kindex d
+Delete the breakpoints or watchpoints of the numbers specified as
+arguments. If no argument is specified, delete all breakpoints (@value{GDBN}
+asks confirmation, unless you have @code{set confirm off}). You
+can abbreviate this command as @code{d}.
+@end table
+
+@node Disabling
+@subsection Disabling breakpoints
+
+@cindex disabled breakpoints
+@cindex enabled breakpoints
+Rather than deleting a breakpoint or watchpoint, you might prefer to
+@dfn{disable} it. This makes the breakpoint inoperative as if it had
+been deleted, but remembers the information on the breakpoint so that
+you can @dfn{enable} it again later.
+
+You disable and enable breakpoints and watchpoints with the
+@code{enable} and @code{disable} commands, optionally specifying one or
+more breakpoint numbers as arguments. Use @code{info break} or
+@code{info watch} to print a list of breakpoints or watchpoints if you
+do not know which numbers to use.
+
+A breakpoint or watchpoint can have any of four different states of
+enablement:
+
+@itemize @bullet
+@item
+Enabled. The breakpoint will stop your program. A breakpoint set
+with the @code{break} command starts out in this state.
+@item
+Disabled. The breakpoint has no effect on your program.
+@item
+Enabled once. The breakpoint will stop your program, but
+when it does so it will become disabled. A breakpoint set
+with the @code{tbreak} command starts out in this state.
+@item
+Enabled for deletion. The breakpoint will stop your program, but
+immediately after it does so it will be deleted permanently.
+@end itemize
+
+You can use the following commands to enable or disable breakpoints and
+watchpoints:
+
+@table @code
+@item disable @r{[}breakpoints@r{]} @r{[}@var{bnums}@dots{}@r{]}
+@kindex disable breakpoints
+@kindex disable
+@kindex dis
+Disable the specified breakpoints---or all breakpoints, if none are
+listed. A disabled breakpoint has no effect but is not forgotten. All
+options such as ignore-counts, conditions and commands are remembered in
+case the breakpoint is enabled again later. You may abbreviate
+@code{disable} as @code{dis}.
+
+@item enable @r{[}breakpoints@r{]} @r{[}@var{bnums}@dots{}@r{]}
+@kindex enable breakpoints
+@kindex enable
+Enable the specified breakpoints (or all defined breakpoints). They
+become effective once again in stopping your program.
+
+@item enable @r{[}breakpoints@r{]} once @var{bnums}@dots{}
+Enable the specified breakpoints temporarily. Each will be disabled
+again the next time it stops your program.
+
+@item enable @r{[}breakpoints@r{]} delete @var{bnums}@dots{}
+Enable the specified breakpoints to work once and then die. Each of
+the breakpoints will be deleted the next time it stops your program.
+@end table
+
+Save for a breakpoint set with @code{tbreak} (@pxref{Set Breaks,
+,Setting breakpoints}), breakpoints that you set are initially enabled;
+subsequently, they become disabled or enabled only when you use one of
+the commands above. (The command @code{until} can set and delete a
+breakpoint of its own, but it will not change the state of your other
+breakpoints; see @ref{Continuing and Stepping, ,Continuing and
+stepping}.)
+
+@node Conditions
+@subsection Break conditions
+@cindex conditional breakpoints
+@cindex breakpoint conditions
+
+@c FIXME what is scope of break condition expr? Context where wanted?
+@c in particular for a watchpoint?
+The simplest sort of breakpoint breaks every time your program reaches a
+specified place. You can also specify a @dfn{condition} for a
+breakpoint. A condition is just a Boolean expression in your
+programming language (@pxref{Expressions, ,Expressions}). A breakpoint with
+a condition evaluates the expression each time your program reaches it,
+and your program stops only if the condition is @emph{true}.
+
+This is the converse of using assertions for program validation; in that
+situation, you want to stop when the assertion is violated---that is,
+when the condition is false. In C, if you want to test an assertion expressed
+by the condition @var{assert}, you should set the condition
+@samp{! @var{assert}} on the appropriate breakpoint.
+
+Conditions are also accepted for watchpoints; you may not need them,
+since a watchpoint is inspecting the value of an expression anyhow---but
+it might be simpler, say, to just set a watchpoint on a variable name,
+and specify a condition that tests whether the new value is an interesting
+one.
+
+Break conditions can have side effects, and may even call functions in
+your program. This can be useful, for example, to activate functions
+that log program progress, or to use your own print functions to
+format special data structures. The effects are completely predictable
+unless there is another enabled breakpoint at the same address. (In
+that case, @value{GDBN} might see the other breakpoint first and stop your
+program without checking the condition of this one.) Note that
+breakpoint commands are usually more convenient and flexible for the
+purpose of performing side effects when a breakpoint is reached
+(@pxref{Break Commands, ,Breakpoint command lists}).
+
+Break conditions can be specified when a breakpoint is set, by using
+@samp{if} in the arguments to the @code{break} command. @xref{Set
+Breaks, ,Setting breakpoints}. They can also be changed at any time
+with the @code{condition} command. The @code{watch} command does not
+recognize the @code{if} keyword; @code{condition} is the only way to
+impose a further condition on a watchpoint.
+
+@table @code
+@item condition @var{bnum} @var{expression}
+@kindex condition
+Specify @var{expression} as the break condition for breakpoint or
+watchpoint number @var{bnum}. From now on, this breakpoint will stop
+your program only if the value of @var{expression} is true (nonzero, in
+C). When you use @code{condition}, @value{GDBN} checks @var{expression}
+immediately for syntactic correctness, and to determine whether symbols
+in it have referents in the context of your breakpoint.
+@c FIXME so what does GDB do if there is no referent? Moreover, what
+@c about watchpoints?
+@value{GDBN} does
+not actually evaluate @var{expression} at the time the @code{condition}
+command is given, however. @xref{Expressions, ,Expressions}.
+
+@item condition @var{bnum}
+Remove the condition from breakpoint number @var{bnum}. It becomes
+an ordinary unconditional breakpoint.
+@end table
+
+@cindex ignore count (of breakpoint)
+A special case of a breakpoint condition is to stop only when the
+breakpoint has been reached a certain number of times. This is so
+useful that there is a special way to do it, using the @dfn{ignore
+count} of the breakpoint. Every breakpoint has an ignore count, which
+is an integer. Most of the time, the ignore count is zero, and
+therefore has no effect. But if your program reaches a breakpoint whose
+ignore count is positive, then instead of stopping, it just decrements
+the ignore count by one and continues. As a result, if the ignore count
+value is @var{n}, the breakpoint will not stop the next @var{n} times it
+is reached.
+
+@table @code
+@item ignore @var{bnum} @var{count}
+@kindex ignore
+Set the ignore count of breakpoint number @var{bnum} to @var{count}.
+The next @var{count} times the breakpoint is reached, your program's
+execution will not stop; other than to decrement the ignore count, @value{GDBN}
+takes no action.
+
+To make the breakpoint stop the next time it is reached, specify
+a count of zero.
+
+When you use @code{continue} to resume execution of your program from a
+breakpoint, you can specify an ignore count directly as an argument to
+@code{continue}, rather than using @code{ignore}. @xref{Continuing and
+Stepping,,Continuing and stepping}.
+
+If a breakpoint has a positive ignore count and a condition, the condition
+is not checked. Once the ignore count reaches zero, the condition will
+be checked.
+
+You could achieve the effect of the ignore count with a condition such
+as @w{@samp{$foo-- <= 0}} using a debugger convenience variable that
+is decremented each time. @xref{Convenience Vars, ,Convenience
+variables}.
+@end table
+
+@node Break Commands
+@subsection Breakpoint command lists
+
+@cindex breakpoint commands
+You can give any breakpoint (or watchpoint) a series of commands to
+execute when your program stops due to that breakpoint. For example, you
+might want to print the values of certain expressions, or enable other
+breakpoints.
+
+@table @code
+@item commands @r{[}@var{bnum}@r{]}
+@itemx @dots{} @var{command-list} @dots{}
+@itemx end
+@kindex commands
+@kindex end
+Specify a list of commands for breakpoint number @var{bnum}. The commands
+themselves appear on the following lines. Type a line containing just
+@code{end} to terminate the commands.
+
+To remove all commands from a breakpoint, type @code{commands} and
+follow it immediately with @code{end}; that is, give no commands.
+
+With no @var{bnum} argument, @code{commands} refers to the last
+breakpoint or watchpoint set (not to the breakpoint most recently
+encountered).
+@end table
+
+Pressing @key{RET} as a means of repeating the last @value{GDBN} command is
+disabled within a @var{command-list}.
+
+You can use breakpoint commands to start your program up again. Simply
+use the @code{continue} command, or @code{step}, or any other command
+that resumes execution.
+
+Any other commands in the command list, after a command that resumes
+execution, are ignored. This is because any time you resume execution
+(even with a simple @code{next} or @code{step}), you may encounter
+another breakpoint---which could have its own command list, leading to
+ambiguities about which list to execute.
+
+@kindex silent
+If the first command you specify in a command list is @code{silent}, the
+usual message about stopping at a breakpoint is not printed. This may
+be desirable for breakpoints that are to print a specific message and
+then continue. If none of the remaining commands print anything, you
+will see no sign that the breakpoint was reached. @code{silent} is
+meaningful only at the beginning of a breakpoint command list.
+
+The commands @code{echo}, @code{output}, and @code{printf} allow you to
+print precisely controlled output, and are often useful in silent
+breakpoints. @xref{Output, ,Commands for controlled output}.
+
+For example, here is how you could use breakpoint commands to print the
+value of @code{x} at entry to @code{foo} whenever @code{x} is positive.
+
+@example
+break foo if x>0
+commands
+silent
+printf "x is %d\n",x
+cont
+end
+@end example
+
+One application for breakpoint commands is to compensate for one bug so
+you can test for another. Put a breakpoint just after the erroneous line
+of code, give it a condition to detect the case in which something
+erroneous has been done, and give it commands to assign correct values
+to any variables that need them. End with the @code{continue} command
+so that your program does not stop, and start with the @code{silent}
+command so that no output is produced. Here is an example:
+
+@example
+break 403
+commands
+silent
+set x = y + 4
+cont
+end
+@end example
+
+@ifclear CONLY
+@node Breakpoint Menus
+@subsection Breakpoint menus
+@cindex overloading
+@cindex symbol overloading
+
+Some programming languages (notably C++) permit a single function name
+to be defined several times, for application in different contexts.
+This is called @dfn{overloading}. When a function name is overloaded,
+@samp{break @var{function}} is not enough to tell @value{GDBN} where you want
+a breakpoint. If you realize this will be a problem, you can use
+something like @samp{break @var{function}(@var{types})} to specify which
+particular version of the function you want. Otherwise, @value{GDBN} offers
+you a menu of numbered choices for different possible breakpoints, and
+waits for your selection with the prompt @samp{>}. The first two
+options are always @samp{[0] cancel} and @samp{[1] all}. Typing @kbd{1}
+sets a breakpoint at each definition of @var{function}, and typing
+@kbd{0} aborts the @code{break} command without setting any new
+breakpoints.
+
+For example, the following session excerpt shows an attempt to set a
+breakpoint at the overloaded symbol @code{String::after}.
+We choose three particular definitions of that function name:
+
+@c FIXME! This is likely to change to show arg type lists, at least
+@smallexample
+(@value{GDBP}) b String::after
+[0] cancel
+[1] all
+[2] file:String.cc; line number:867
+[3] file:String.cc; line number:860
+[4] file:String.cc; line number:875
+[5] file:String.cc; line number:853
+[6] file:String.cc; line number:846
+[7] file:String.cc; line number:735
+> 2 4 6
+Breakpoint 1 at 0xb26c: file String.cc, line 867.
+Breakpoint 2 at 0xb344: file String.cc, line 875.
+Breakpoint 3 at 0xafcc: file String.cc, line 846.
+Multiple breakpoints were set.
+Use the "delete" command to delete unwanted
+ breakpoints.
+(@value{GDBP})
+@end smallexample
+@end ifclear
+
+@ifclear BARETARGET
+@node Error in Breakpoints
+@subsection ``Cannot insert breakpoints''
+
+@c FIXME: "cannot insert breakpoints" error, v unclear.
+@c Q in pending mail to Gilmore. ---pesch@cygnus.com, 26mar91
+@c some light may be shed by looking at instances of
+@c ONE_PROCESS_WRITETEXT. But error message seems possible otherwise
+@c too. pesch, 20sep91
+Under some operating systems, breakpoints cannot be used in a program if
+any other process is running that program. In this situation,
+attempting to run or continue a program with a breakpoint causes @value{GDBN}
+to stop the other process.
+
+When this happens, you have three ways to proceed:
+
+@enumerate
+@item
+Remove or disable the breakpoints, then continue.
+
+@item
+Suspend @value{GDBN}, and copy the file containing your program to a new name.
+Resume @value{GDBN} and use the @code{exec-file} command to specify that @value{GDBN}
+should run your program under that name. Then start your program again.
+
+@c FIXME: RMS commented here "Show example". Maybe when someone
+@c explains the first FIXME: in this section...
+
+@item
+Relink your program so that the text segment is nonsharable, using the
+linker option @samp{-N}. The operating system limitation may not apply
+to nonsharable executables.
+@end enumerate
+@end ifclear
+
+@node Continuing and Stepping
+@section Continuing and stepping
+
+@cindex stepping
+@cindex continuing
+@cindex resuming execution
+@dfn{Continuing} means resuming program execution until your program
+completes normally. In contrast, @dfn{stepping} means executing just
+one more ``step'' of your program, where ``step'' may mean either one
+line of source code, or one machine instruction (depending on what
+particular command you use). Either when continuing
+or when stepping, your program may stop even sooner, due to
+@ifset BARETARGET
+a breakpoint.
+@end ifset
+@ifclear BARETARGET
+a breakpoint or a signal. (If due to a signal, you may want to use
+@code{handle}, or use @samp{signal 0} to resume execution.
+@xref{Signals, ,Signals}.)
+@end ifclear
+
+@table @code
+@item continue @r{[}@var{ignore-count}@r{]}
+@itemx c @r{[}@var{ignore-count}@r{]}
+@itemx fg @r{[}@var{ignore-count}@r{]}
+@kindex continue
+@kindex c
+@kindex fg
+Resume program execution, at the address where your program last stopped;
+any breakpoints set at that address are bypassed. The optional argument
+@var{ignore-count} allows you to specify a further number of times to
+ignore a breakpoint at this location; its effect is like that of
+@code{ignore} (@pxref{Conditions, ,Break conditions}).
+
+The argument @var{ignore-count} is meaningful only when your program
+stopped due to a breakpoint. At other times, the argument to
+@code{continue} is ignored.
+
+The synonyms @code{c} and @code{fg} are provided purely for convenience,
+and have exactly the same behavior as @code{continue}.
+@end table
+
+To resume execution at a different place, you can use @code{return}
+(@pxref{Returning, ,Returning from a function}) to go back to the
+calling function; or @code{jump} (@pxref{Jumping, ,Continuing at a
+different address}) to go to an arbitrary location in your program.
+
+A typical technique for using stepping is to set a breakpoint
+@ifclear CONLY
+(@pxref{Breakpoints, ,Breakpoints; watchpoints; and exceptions})
+@end ifclear
+@ifset CONLY
+(@pxref{Breakpoints, ,Breakpoints and watchpoints})
+@end ifset
+at the
+beginning of the function or the section of your program where a
+problem is believed to lie, run your program until it stops at that
+breakpoint, and then step through the suspect area, examining the
+variables that are interesting, until you see the problem happen.
+
+@table @code
+@item step
+@kindex step
+@kindex s
+Continue running your program until control reaches a different source
+line, then stop it and return control to @value{GDBN}. This command is
+abbreviated @code{s}.
+
+@quotation
+@emph{Warning:} If you use the @code{step} command while control is
+within a function that was compiled without debugging information,
+execution proceeds until control reaches a function that does have
+debugging information.
+@end quotation
+
+@item step @var{count}
+Continue running as in @code{step}, but do so @var{count} times. If a
+breakpoint is reached,
+@ifclear BARETARGET
+or a signal not related to stepping occurs before @var{count} steps,
+@end ifclear
+stepping stops right away.
+
+@item next @r{[}@var{count}@r{]}
+@kindex next
+@kindex n
+Continue to the next source line in the current (innermost) stack frame.
+Similar to @code{step}, but any function calls appearing within the line
+of code are executed without stopping. Execution stops when control
+reaches a different line of code at the stack level which was executing
+when the @code{next} command was given. This command is abbreviated
+@code{n}.
+
+An argument @var{count} is a repeat count, as for @code{step}.
+
+@code{next} within a function that lacks debugging information acts like
+@code{step}, but any function calls appearing within the code of the
+function are executed without stopping.
+
+@item finish
+@kindex finish
+Continue running until just after function in the selected stack frame
+returns. Print the returned value (if any).
+
+Contrast this with the @code{return} command (@pxref{Returning,
+,Returning from a function}).
+
+@item until
+@kindex until
+@itemx u
+@kindex u
+Continue running until a source line past the current line, in the
+current stack frame, is reached. This command is used to avoid single
+stepping through a loop more than once. It is like the @code{next}
+command, except that when @code{until} encounters a jump, it
+automatically continues execution until the program counter is greater
+than the address of the jump.
+
+This means that when you reach the end of a loop after single stepping
+though it, @code{until} will cause your program to continue execution
+until the loop is exited. In contrast, a @code{next} command at the end
+of a loop will simply step back to the beginning of the loop, which
+would force you to step through the next iteration.
+
+@code{until} always stops your program if it attempts to exit the current
+stack frame.
+
+@code{until} may produce somewhat counterintuitive results if the order
+of machine code does not match the order of the source lines. For
+example, in the following excerpt from a debugging session, the @code{f}
+(@code{frame}) command shows that execution is stopped at line
+@code{206}; yet when we use @code{until}, we get to line @code{195}:
+
+@example
+(@value{GDBP}) f
+#0 main (argc=4, argv=0xf7fffae8) at m4.c:206
+206 expand_input();
+(@value{GDBP}) until
+195 for ( ; argc > 0; NEXTARG) @{
+@end example
+
+This happened because, for execution efficiency, the compiler had
+generated code for the loop closure test at the end, rather than the
+start, of the loop---even though the test in a C @code{for}-loop is
+written before the body of the loop. The @code{until} command appeared
+to step back to the beginning of the loop when it advanced to this
+expression; however, it has not really gone to an earlier
+statement---not in terms of the actual machine code.
+
+@code{until} with no argument works by means of single
+instruction stepping, and hence is slower than @code{until} with an
+argument.
+
+@item until @var{location}
+@itemx u @var{location}
+Continue running your program until either the specified location is
+reached, or the current stack frame returns. @var{location} is any of
+the forms of argument acceptable to @code{break} (@pxref{Set Breaks,
+,Setting breakpoints}). This form of the command uses breakpoints,
+and hence is quicker than @code{until} without an argument.
+
+@item stepi
+@itemx si
+@kindex stepi
+@kindex si
+Execute one machine instruction, then stop and return to the debugger.
+
+It is often useful to do @samp{display/i $pc} when stepping by machine
+instructions. This will cause the next instruction to be executed to
+be displayed automatically at each stop. @xref{Auto Display,
+,Automatic display}.
+
+An argument is a repeat count, as in @code{step}.
+
+@need 750
+@item nexti
+@itemx ni
+@kindex nexti
+@kindex ni
+Execute one machine instruction, but if it is a function call,
+proceed until the function returns.
+
+An argument is a repeat count, as in @code{next}.
+@end table
+
+@ifset POSIX
+@node Signals
+@section Signals
+@cindex signals
+
+A signal is an asynchronous event that can happen in a program. The
+operating system defines the possible kinds of signals, and gives each
+kind a name and a number. For example, in Unix @code{SIGINT} is the
+signal a program gets when you type an interrupt (often @kbd{C-c});
+@code{SIGSEGV} is the signal a program gets from referencing a place in
+memory far away from all the areas in use; @code{SIGALRM} occurs when
+the alarm clock timer goes off (which happens only if your program has
+requested an alarm).
+
+@cindex fatal signals
+Some signals, including @code{SIGALRM}, are a normal part of the
+functioning of your program. Others, such as @code{SIGSEGV}, indicate
+errors; these signals are @dfn{fatal} (kill your program immediately) if the
+program has not specified in advance some other way to handle the signal.
+@code{SIGINT} does not indicate an error in your program, but it is normally
+fatal so it can carry out the purpose of the interrupt: to kill the program.
+
+@value{GDBN} has the ability to detect any occurrence of a signal in your
+program. You can tell @value{GDBN} in advance what to do for each kind of
+signal.
+
+@cindex handling signals
+Normally, @value{GDBN} is set up to ignore non-erroneous signals like @code{SIGALRM}
+(so as not to interfere with their role in the functioning of your program)
+but to stop your program immediately whenever an error signal happens.
+You can change these settings with the @code{handle} command.
+
+@table @code
+@item info signals
+@kindex info signals
+Print a table of all the kinds of signals and how @value{GDBN} has been told to
+handle each one. You can use this to see the signal numbers of all
+the defined types of signals.
+
+@item handle @var{signal} @var{keywords}@dots{}
+@kindex handle
+Change the way @value{GDBN} handles signal @var{signal}. @var{signal} can be the
+number of a signal or its name (with or without the @samp{SIG} at the
+beginning). The @var{keywords} say what change to make.
+@end table
+
+@c @group
+The keywords allowed by the @code{handle} command can be abbreviated.
+Their full names are:
+
+@table @code
+@item nostop
+@value{GDBN} should not stop your program when this signal happens. It may
+still print a message telling you that the signal has come in.
+
+@item stop
+@value{GDBN} should stop your program when this signal happens. This implies
+the @code{print} keyword as well.
+
+@item print
+@value{GDBN} should print a message when this signal happens.
+
+@item noprint
+@value{GDBN} should not mention the occurrence of the signal at all. This
+implies the @code{nostop} keyword as well.
+
+@item pass
+@value{GDBN} should allow your program to see this signal; your program will be
+able to handle the signal, or may be terminated if the signal is fatal
+and not handled.
+
+@item nopass
+@value{GDBN} should not allow your program to see this signal.
+@end table
+@c @end group
+
+When a signal stops your program, the signal is not visible until you
+continue. Your program will see the signal then, if @code{pass} is in
+effect for the signal in question @emph{at that time}. In other words,
+after @value{GDBN} reports a signal, you can use the @code{handle}
+command with @code{pass} or @code{nopass} to control whether that
+signal will be seen by your program when you later continue it.
+
+You can also use the @code{signal} command to prevent your program from
+seeing a signal, or cause it to see a signal it normally would not see,
+or to give it any signal at any time. For example, if your program stopped
+due to some sort of memory reference error, you might store correct
+values into the erroneous variables and continue, hoping to see more
+execution; but your program would probably terminate immediately as
+a result of the fatal signal once it saw the signal. To prevent this,
+you can continue with @samp{signal 0}. @xref{Signaling, ,Giving your
+program a signal}.
+@end ifset
+
+@node Stack
+@chapter Examining the Stack
+
+When your program has stopped, the first thing you need to know is where it
+stopped and how it got there.
+
+@cindex call stack
+Each time your program performs a function call, the information about
+where in your program the call was made from is saved in a block of data
+called a @dfn{stack frame}. The frame also contains the arguments of the
+call and the local variables of the function that was called. All the
+stack frames are allocated in a region of memory called the @dfn{call
+stack}.
+
+When your program stops, the @value{GDBN} commands for examining the
+stack allow you to see all of this information.
+
+@cindex selected frame
+One of the stack frames is @dfn{selected} by @value{GDBN} and many
+@value{GDBN} commands refer implicitly to the selected frame. In
+particular, whenever you ask @value{GDBN} for the value of a variable in
+your program, the value is found in the selected frame. There are
+special @value{GDBN} commands to select whichever frame you are
+interested in.
+
+When your program stops, @value{GDBN} automatically selects the
+currently executing frame and describes it briefly as the @code{frame}
+command does (@pxref{Frame Info, ,Information about a frame}).
+
+@menu
+* Frames:: Stack frames
+* Backtrace:: Backtraces
+* Selection:: Selecting a frame
+* Frame Info:: Information on a frame
+@ifset MIPS
+* MIPS Stack:: MIPS machines and the function stack
+@end ifset
+@end menu
+
+@node Frames
+@section Stack frames
+
+@cindex frame
+@cindex stack frame
+The call stack is divided up into contiguous pieces called @dfn{stack
+frames}, or @dfn{frames} for short; each frame is the data associated
+with one call to one function. The frame contains the arguments given
+to the function, the function's local variables, and the address at
+which the function is executing.
+
+@cindex initial frame
+@cindex outermost frame
+@cindex innermost frame
+When your program is started, the stack has only one frame, that of the
+function @code{main}. This is called the @dfn{initial} frame or the
+@dfn{outermost} frame. Each time a function is called, a new frame is
+made. Each time a function returns, the frame for that function invocation
+is eliminated. If a function is recursive, there can be many frames for
+the same function. The frame for the function in which execution is
+actually occurring is called the @dfn{innermost} frame. This is the most
+recently created of all the stack frames that still exist.
+
+@cindex frame pointer
+Inside your program, stack frames are identified by their addresses. A
+stack frame consists of many bytes, each of which has its own address; each
+kind of computer has a convention for choosing one of those bytes whose
+address serves as the address of the frame. Usually this address is kept
+in a register called the @dfn{frame pointer register} while execution is
+going on in that frame.
+
+@cindex frame number
+@value{GDBN} assigns numbers to all existing stack frames, starting with
+zero for the innermost frame, one for the frame that called it,
+and so on upward. These numbers do not really exist in your program;
+they are assigned by @value{GDBN} to give you a way of designating stack
+frames in @value{GDBN} commands.
+
+@c below produces an acceptable overful hbox. --mew 13aug1993
+@cindex frameless execution
+Some compilers provide a way to compile functions so that they operate
+without stack frames. (For example, the @code{@value{GCC}} option
+@samp{-fomit-frame-pointer} will generate functions without a frame.)
+This is occasionally done with heavily used library functions to save
+the frame setup time. @value{GDBN} has limited facilities for dealing
+with these function invocations. If the innermost function invocation
+has no stack frame, @value{GDBN} will nevertheless regard it as though
+it had a separate frame, which is numbered zero as usual, allowing
+correct tracing of the function call chain. However, @value{GDBN} has
+no provision for frameless functions elsewhere in the stack.
+
+@node Backtrace
+@section Backtraces
+
+A backtrace is a summary of how your program got where it is. It shows one
+line per frame, for many frames, starting with the currently executing
+frame (frame zero), followed by its caller (frame one), and on up the
+stack.
+
+@table @code
+@item backtrace
+@itemx bt
+@kindex backtrace
+@kindex bt
+Print a backtrace of the entire stack: one line per frame for all
+frames in the stack.
+
+You can stop the backtrace at any time by typing the system interrupt
+character, normally @kbd{C-c}.
+
+@item backtrace @var{n}
+@itemx bt @var{n}
+Similar, but print only the innermost @var{n} frames.
+
+@item backtrace -@var{n}
+@itemx bt -@var{n}
+Similar, but print only the outermost @var{n} frames.
+@end table
+
+@kindex where
+@kindex info stack
+@kindex info s
+The names @code{where} and @code{info stack} (abbreviated @code{info s})
+are additional aliases for @code{backtrace}.
+
+Each line in the backtrace shows the frame number and the function name.
+The program counter value is also shown---unless you use @code{set
+print address off}. The backtrace also shows the source file name and
+line number, as well as the arguments to the function. The program
+counter value is omitted if it is at the beginning of the code for that
+line number.
+
+Here is an example of a backtrace. It was made with the command
+@samp{bt 3}, so it shows the innermost three frames.
+
+@smallexample
+@group
+#0 m4_traceon (obs=0x24eb0, argc=1, argv=0x2b8c8)
+ at builtin.c:993
+#1 0x6e38 in expand_macro (sym=0x2b600) at macro.c:242
+#2 0x6840 in expand_token (obs=0x0, t=177664, td=0xf7fffb08)
+ at macro.c:71
+(More stack frames follow...)
+@end group
+@end smallexample
+
+@noindent
+The display for frame zero does not begin with a program counter
+value, indicating that your program has stopped at the beginning of the
+code for line @code{993} of @code{builtin.c}.
+
+@node Selection
+@section Selecting a frame
+
+Most commands for examining the stack and other data in your program work on
+whichever stack frame is selected at the moment. Here are the commands for
+selecting a stack frame; all of them finish by printing a brief description
+of the stack frame just selected.
+
+@table @code
+@item frame @var{n}
+@itemx f @var{n}
+@kindex frame
+@kindex f
+Select frame number @var{n}. Recall that frame zero is the innermost
+(currently executing) frame, frame one is the frame that called the
+innermost one, and so on. The highest-numbered frame is the one for
+@code{main}.
+
+@item frame @var{addr}
+@itemx f @var{addr}
+Select the frame at address @var{addr}. This is useful mainly if the
+chaining of stack frames has been damaged by a bug, making it
+impossible for @value{GDBN} to assign numbers properly to all frames. In
+addition, this can be useful when your program has multiple stacks and
+switches between them.
+
+@ifset SPARC
+On the SPARC architecture, @code{frame} needs two addresses to
+select an arbitrary frame: a frame pointer and a stack pointer.
+@c note to future updaters: this is conditioned on a flag
+@c FRAME_SPECIFICATION_DYADIC in the tm-*.h files, currently only used
+@c by SPARC, hence the specific attribution. Generalize or list all
+@c possibilities if more supported machines start doing this.
+@end ifset
+
+@item up @var{n}
+@kindex up
+Move @var{n} frames up the stack. For positive numbers @var{n}, this
+advances toward the outermost frame, to higher frame numbers, to frames
+that have existed longer. @var{n} defaults to one.
+
+@item down @var{n}
+@kindex down
+@kindex do
+Move @var{n} frames down the stack. For positive numbers @var{n}, this
+advances toward the innermost frame, to lower frame numbers, to frames
+that were created more recently. @var{n} defaults to one. You may
+abbreviate @code{down} as @code{do}.
+@end table
+
+All of these commands end by printing two lines of output describing the
+frame. The first line shows the frame number, the function name, the
+arguments, and the source file and line number of execution in that
+frame. The second line shows the text of that source line.
+
+For example:
+@smallexample
+@group
+(@value{GDBP}) up
+#1 0x22f0 in main (argc=1, argv=0xf7fffbf4, env=0xf7fffbfc)
+ at env.c:10
+10 read_input_file (argv[i]);
+@end group
+@end smallexample
+
+After such a printout, the @code{list} command with no arguments will
+print ten lines centered on the point of execution in the frame.
+@xref{List, ,Printing source lines}.
+
+@table @code
+@item up-silently @var{n}
+@itemx down-silently @var{n}
+@kindex down-silently
+@kindex up-silently
+These two commands are variants of @code{up} and @code{down},
+respectively; they differ in that they do their work silently, without
+causing display of the new frame. They are intended primarily for use
+in @value{GDBN} command scripts, where the output might be unnecessary and
+distracting.
+@end table
+
+@node Frame Info
+@section Information about a frame
+
+There are several other commands to print information about the selected
+stack frame.
+
+@table @code
+@item frame
+@itemx f
+When used without any argument, this command does not change which
+frame is selected, but prints a brief description of the currently
+selected stack frame. It can be abbreviated @code{f}. With an
+argument, this command is used to select a stack frame.
+@xref{Selection, ,Selecting a frame}.
+
+@item info frame
+@itemx info f
+@kindex info frame
+@kindex info f
+This command prints a verbose description of the selected stack frame,
+including the address of the frame, the addresses of the next frame down
+(called by this frame) and the next frame up (caller of this frame), the
+language that the source code corresponding to this frame was written in,
+the address of the frame's arguments, the program counter saved in it
+(the address of execution in the caller frame), and which registers
+were saved in the frame. The verbose description is useful when
+something has gone wrong that has made the stack format fail to fit
+the usual conventions.
+
+@item info frame @var{addr}
+@itemx info f @var{addr}
+Print a verbose description of the frame at address @var{addr},
+without selecting that frame. The selected frame remains unchanged by
+this command.
+
+@item info args
+@kindex info args
+Print the arguments of the selected frame, each on a separate line.
+
+@item info locals
+@kindex info locals
+Print the local variables of the selected frame, each on a separate
+line. These are all variables (declared either static or automatic)
+accessible at the point of execution of the selected frame.
+
+@ifclear CONLY
+@item info catch
+@kindex info catch
+@cindex catch exceptions
+@cindex exception handlers
+Print a list of all the exception handlers that are active in the
+current stack frame at the current point of execution. To see other
+exception handlers, visit the associated frame (using the @code{up},
+@code{down}, or @code{frame} commands); then type @code{info catch}.
+@xref{Exception Handling, ,Breakpoints and exceptions}.
+@end ifclear
+@end table
+
+@ifset MIPS
+@node MIPS Stack
+@section MIPS machines and the function stack
+
+@cindex stack on MIPS
+@cindex MIPS stack
+MIPS based computers use an unusual stack frame, which sometimes
+requires @value{GDBN} to search backward in the object code to find the
+beginning of a function.
+
+@cindex response time, MIPS debugging
+To improve response time (especially for embedded applications, where
+@value{GDBN} may be restricted to a slow serial line for this search)
+you may want to limit the size of this search, using one of these
+commands:
+@c FIXME! So what happens when GDB does *not* find the beginning of a
+@c function?
+
+@cindex @code{heuristic-fence-post} (MIPS)
+@table @code
+@item set heuristic-fence-post @var{limit}
+Restrict @var{GDBN} to examining at most @var{limit} bytes in its search
+for the beginning of a function. A value of @code{0} (the default)
+means there is no limit.
+
+@item show heuristic-fence-post
+Display the current limit.
+@end table
+
+@noindent
+These commands are available @emph{only} when @value{GDBN} is configured
+for debugging programs on MIPS processors.
+@end ifset
+
+@node Source
+@chapter Examining Source Files
+
+@value{GDBN} can print parts of your program's source, since the debugging
+information recorded in the program tells @value{GDBN} what source files were
+used to build it. When your program stops, @value{GDBN} spontaneously prints
+the line where it stopped. Likewise, when you select a stack frame
+(@pxref{Selection, ,Selecting a frame}), @value{GDBN} prints the line where
+execution in that frame has stopped. You can print other portions of
+source files by explicit command.
+
+@ifclear DOSHOST
+If you use @value{GDBN} through its GNU Emacs interface, you may prefer to use
+Emacs facilities to view source; @pxref{Emacs, ,Using @value{GDBN} under GNU
+Emacs}.
+@end ifclear
+
+@menu
+* List:: Printing source lines
+@ifclear DOSHOST
+* Search:: Searching source files
+@end ifclear
+
+* Source Path:: Specifying source directories
+* Machine Code:: Source and machine code
+@end menu
+
+@node List
+@section Printing source lines
+
+@kindex list
+@kindex l
+To print lines from a source file, use the @code{list} command
+(abbreviated @code{l}). There are several ways to specify what part
+of the file you want to print.
+
+Here are the forms of the @code{list} command most commonly used:
+
+@table @code
+@item list @var{linenum}
+Print lines centered around line number @var{linenum} in the
+current source file.
+
+@item list @var{function}
+Print lines centered around the beginning of function
+@var{function}.
+
+@item list
+Print more lines. If the last lines printed were printed with a
+@code{list} command, this prints lines following the last lines
+printed; however, if the last line printed was a solitary line printed
+as part of displaying a stack frame (@pxref{Stack, ,Examining the
+Stack}), this prints lines centered around that line.
+
+@item list -
+Print lines just before the lines last printed.
+@end table
+
+By default, @value{GDBN} prints ten source lines with any of these forms of
+the @code{list} command. You can change this using @code{set listsize}:
+
+@table @code
+@item set listsize @var{count}
+@kindex set listsize
+Make the @code{list} command display @var{count} source lines (unless
+the @code{list} argument explicitly specifies some other number).
+
+@item show listsize
+@kindex show listsize
+Display the number of lines that @code{list} will currently display by
+default.
+@end table
+
+Repeating a @code{list} command with @key{RET} discards the argument,
+so it is equivalent to typing just @code{list}. This is more useful
+than listing the same lines again. An exception is made for an
+argument of @samp{-}; that argument is preserved in repetition so that
+each repetition moves up in the source file.
+
+@cindex linespec
+In general, the @code{list} command expects you to supply zero, one or two
+@dfn{linespecs}. Linespecs specify source lines; there are several ways
+of writing them but the effect is always to specify some source line.
+Here is a complete description of the possible arguments for @code{list}:
+
+@table @code
+@item list @var{linespec}
+Print lines centered around the line specified by @var{linespec}.
+
+@item list @var{first},@var{last}
+Print lines from @var{first} to @var{last}. Both arguments are
+linespecs.
+
+@item list ,@var{last}
+Print lines ending with @var{last}.
+
+@item list @var{first},
+Print lines starting with @var{first}.
+
+@item list +
+Print lines just after the lines last printed.
+
+@item list -
+Print lines just before the lines last printed.
+
+@item list
+As described in the preceding table.
+@end table
+
+Here are the ways of specifying a single source line---all the
+kinds of linespec.
+
+@table @code
+@item @var{number}
+Specifies line @var{number} of the current source file.
+When a @code{list} command has two linespecs, this refers to
+the same source file as the first linespec.
+
+@item +@var{offset}
+Specifies the line @var{offset} lines after the last line printed.
+When used as the second linespec in a @code{list} command that has
+two, this specifies the line @var{offset} lines down from the
+first linespec.
+
+@item -@var{offset}
+Specifies the line @var{offset} lines before the last line printed.
+
+@item @var{filename}:@var{number}
+Specifies line @var{number} in the source file @var{filename}.
+
+@item @var{function}
+@c FIXME: "of the open-brace" is C-centric. When we add other langs...
+Specifies the line of the open-brace that begins the body of the
+function @var{function}.
+
+@item @var{filename}:@var{function}
+Specifies the line of the open-brace that begins the body of the
+function @var{function} in the file @var{filename}. You only need the
+file name with a function name to avoid ambiguity when there are
+identically named functions in different source files.
+
+@item *@var{address}
+Specifies the line containing the program address @var{address}.
+@var{address} may be any expression.
+@end table
+
+@ifclear DOSHOST
+@node Search
+@section Searching source files
+@cindex searching
+@kindex reverse-search
+
+There are two commands for searching through the current source file for a
+regular expression.
+
+@table @code
+@item forward-search @var{regexp}
+@itemx search @var{regexp}
+@kindex search
+@kindex forward-search
+The command @samp{forward-search @var{regexp}} checks each line,
+starting with the one following the last line listed, for a match for
+@var{regexp}. It lists the line that is found. You can use
+synonym @samp{search @var{regexp}} or abbreviate the command name as
+@code{fo}.
+
+@item reverse-search @var{regexp}
+The command @samp{reverse-search @var{regexp}} checks each line, starting
+with the one before the last line listed and going backward, for a match
+for @var{regexp}. It lists the line that is found. You can abbreviate
+this command as @code{rev}.
+@end table
+@end ifclear
+
+@node Source Path
+@section Specifying source directories
+
+@cindex source path
+@cindex directories for source files
+Executable programs sometimes do not record the directories of the source
+files from which they were compiled, just the names. Even when they do,
+the directories could be moved between the compilation and your debugging
+session. @value{GDBN} has a list of directories to search for source files;
+this is called the @dfn{source path}. Each time @value{GDBN} wants a source file,
+it tries all the directories in the list, in the order they are present
+in the list, until it finds a file with the desired name. Note that
+the executable search path is @emph{not} used for this purpose. Neither is
+the current working directory, unless it happens to be in the source
+path.
+
+If @value{GDBN} cannot find a source file in the source path, and the object
+program records a directory, @value{GDBN} tries that directory too. If the
+source path is empty, and there is no record of the compilation
+directory, @value{GDBN} will, as a last resort, look in the current
+directory.
+
+Whenever you reset or rearrange the source path, @value{GDBN} will clear out
+any information it has cached about where source files are found, where
+each line is in the file, etc.
+
+@kindex directory
+When you start @value{GDBN}, its source path is empty.
+To add other directories, use the @code{directory} command.
+
+@table @code
+@item directory @var{dirname} @dots{}
+Add directory @var{dirname} to the front of the source path. Several
+directory names may be given to this command, separated by @samp{:} or
+whitespace. You may specify a directory that is already in the source
+path; this moves it forward, so it will be searched sooner.
+
+@kindex cdir
+@kindex cwd
+@kindex $cdir
+@kindex $cwd
+@cindex compilation directory
+@cindex current directory
+@cindex working directory
+@cindex directory, current
+@cindex directory, compilation
+You can use the string @samp{$cdir} to refer to the compilation
+directory (if one is recorded), and @samp{$cwd} to refer to the current
+working directory. @samp{$cwd} is not the same as @samp{.}---the former
+tracks the current working directory as it changes during your @value{GDBN}
+session, while the latter is immediately expanded to the current
+directory at the time you add an entry to the source path.
+
+@item directory
+Reset the source path to empty again. This requires confirmation.
+
+@c RET-repeat for @code{directory} is explicitly disabled, but since
+@c repeating it would be a no-op we do not say that. (thanks to RMS)
+
+@item show directories
+@kindex show directories
+Print the source path: show which directories it contains.
+@end table
+
+If your source path is cluttered with directories that are no longer of
+interest, @value{GDBN} may sometimes cause confusion by finding the wrong
+versions of source. You can correct the situation as follows:
+
+@enumerate
+@item
+Use @code{directory} with no argument to reset the source path to empty.
+
+@item
+Use @code{directory} with suitable arguments to reinstall the
+directories you want in the source path. You can add all the
+directories in one command.
+@end enumerate
+
+@node Machine Code
+@section Source and machine code
+
+You can use the command @code{info line} to map source lines to program
+addresses (and vice versa), and the command @code{disassemble} to display
+a range of addresses as machine instructions.
+
+@table @code
+@item info line @var{linespec}
+@kindex info line
+Print the starting and ending addresses of the compiled code for
+source line @var{linespec}. You can specify source lines in any of
+the ways understood by the @code{list} command (@pxref{List, ,Printing
+source lines}).
+@end table
+
+For example, we can use @code{info line} to discover the location of
+the object code for the first line of function
+@code{m4_changequote}:
+
+@smallexample
+(@value{GDBP}) info line m4_changecom
+Line 895 of "builtin.c" starts at pc 0x634c and ends at 0x6350.
+@end smallexample
+
+@noindent
+We can also inquire (using @code{*@var{addr}} as the form for
+@var{linespec}) what source line covers a particular address:
+@smallexample
+(@value{GDBP}) info line *0x63ff
+Line 926 of "builtin.c" starts at pc 0x63e4 and ends at 0x6404.
+@end smallexample
+
+@cindex @code{$_} and @code{info line}
+After @code{info line}, the default address for the @code{x} command
+is changed to the starting address of the line, so that @samp{x/i} is
+sufficient to begin examining the machine code (@pxref{Memory,
+,Examining memory}). Also, this address is saved as the value of the
+convenience variable @code{$_} (@pxref{Convenience Vars, ,Convenience
+variables}).
+
+@table @code
+@kindex disassemble
+@item disassemble
+@cindex assembly instructions
+@cindex instructions, assembly
+@cindex machine instructions
+@cindex listing machine instructions
+This specialized command dumps a range of memory as machine
+instructions. The default memory range is the function surrounding the
+program counter of the selected frame. A single argument to this
+command is a program counter value; the function surrounding this value
+will be dumped. Two arguments specify a range of addresses (first
+inclusive, second exclusive) to dump.
+@end table
+
+@ifclear H8EXCLUSIVE
+We can use @code{disassemble} to inspect the object code
+range shown in the last @code{info line} example (the example
+shows SPARC machine instructions):
+
+
+@smallexample
+(@value{GDBP}) disas 0x63e4 0x6404
+Dump of assembler code from 0x63e4 to 0x6404:
+0x63e4 <builtin_init+5340>: ble 0x63f8 <builtin_init+5360>
+0x63e8 <builtin_init+5344>: sethi %hi(0x4c00), %o0
+0x63ec <builtin_init+5348>: ld [%i1+4], %o0
+0x63f0 <builtin_init+5352>: b 0x63fc <builtin_init+5364>
+0x63f4 <builtin_init+5356>: ld [%o0+4], %o0
+0x63f8 <builtin_init+5360>: or %o0, 0x1a4, %o0
+0x63fc <builtin_init+5364>: call 0x9288 <path_search>
+0x6400 <builtin_init+5368>: nop
+End of assembler dump.
+@end smallexample
+@end ifclear
+
+@ifset H8EXCLUSIVE
+For example, here is the beginning of the output for the
+disassembly of a function @code{fact}:
+
+
+@smallexample
+(@value{GDBP}) disas fact
+Dump of assembler code for function fact:
+to 0x808c:
+0x802c <fact>: 6d f2 mov.w r2,@@-r7
+0x802e <fact+2>: 6d f3 mov.w r3,@@-r7
+0x8030 <fact+4>: 6d f6 mov.w r6,@@-r7
+0x8032 <fact+6>: 0d 76 mov.w r7,r6
+0x8034 <fact+8>: 6f 70 00 08 mov.w @@(0x8,r7),r0
+0x8038 <fact+12> 19 11 sub.w r1,r1
+ .
+ .
+ .
+@end smallexample
+@end ifset
+
+@node Data
+@chapter Examining Data
+
+@cindex printing data
+@cindex examining data
+@kindex print
+@kindex inspect
+@c "inspect" is not quite a synonym if you are using Epoch, which we do not
+@c document because it is nonstandard... Under Epoch it displays in a
+@c different window or something like that.
+The usual way to examine data in your program is with the @code{print}
+command (abbreviated @code{p}), or its synonym @code{inspect}.
+@ifclear CONLY
+It evaluates and prints the value of an expression of the language your
+program is written in (@pxref{Languages, ,Using @value{GDBN} with Different
+Languages}).
+@end ifclear
+
+@table @code
+@item print @var{exp}
+@itemx print /@var{f} @var{exp}
+@var{exp} is an expression (in the source language). By default the
+value of @var{exp} is printed in a format appropriate to its data type;
+you can choose a different format by specifying @samp{/@var{f}}, where
+@var{f} is a letter specifying the format; @pxref{Output Formats,,Output
+formats}.
+
+@item print
+@itemx print /@var{f}
+If you omit @var{exp}, @value{GDBN} displays the last value again (from the
+@dfn{value history}; @pxref{Value History, ,Value history}). This allows you to
+conveniently inspect the same value in an alternative format.
+@end table
+
+A more low-level way of examining data is with the @code{x} command.
+It examines data in memory at a specified address and prints it in a
+specified format. @xref{Memory, ,Examining memory}.
+
+If you are interested in information about types, or about how the fields
+of a struct
+@ifclear CONLY
+or class
+@end ifclear
+are declared, use the @code{ptype @var{exp}}
+command rather than @code{print}. @xref{Symbols, ,Examining the Symbol Table}.
+
+@menu
+* Expressions:: Expressions
+* Variables:: Program variables
+* Arrays:: Artificial arrays
+* Output Formats:: Output formats
+* Memory:: Examining memory
+* Auto Display:: Automatic display
+* Print Settings:: Print settings
+* Value History:: Value history
+* Convenience Vars:: Convenience variables
+* Registers:: Registers
+@ifclear HAVE-FLOAT
+* Floating Point Hardware:: Floating point hardware
+@end ifclear
+@end menu
+
+@node Expressions
+@section Expressions
+
+@cindex expressions
+@code{print} and many other @value{GDBN} commands accept an expression and
+compute its value. Any kind of constant, variable or operator defined
+by the programming language you are using is valid in an expression in
+@value{GDBN}. This includes conditional expressions, function calls, casts
+and string constants. It unfortunately does not include symbols defined
+by preprocessor @code{#define} commands.
+
+@ifclear CONLY
+Because C is so widespread, most of the expressions shown in examples in
+this manual are in C. @xref{Languages, , Using @value{GDBN} with Different
+Languages}, for information on how to use expressions in other
+languages.
+
+In this section, we discuss operators that you can use in @value{GDBN}
+expressions regardless of your programming language.
+
+Casts are supported in all languages, not just in C, because it is so
+useful to cast a number into a pointer so as to examine a structure
+at that address in memory.
+@c FIXME: casts supported---Mod2 true?
+@end ifclear
+
+@value{GDBN} supports these operators in addition to those of programming
+languages:
+
+@table @code
+@item @@
+@samp{@@} is a binary operator for treating parts of memory as arrays.
+@xref{Arrays, ,Artificial arrays}, for more information.
+
+@item ::
+@samp{::} allows you to specify a variable in terms of the file or
+function where it is defined. @xref{Variables, ,Program variables}.
+
+@item @{@var{type}@} @var{addr}
+@cindex @{@var{type}@}
+@cindex type casting memory
+@cindex memory, viewing as typed object
+@cindex casts, to view memory
+Refers to an object of type @var{type} stored at address @var{addr} in
+memory. @var{addr} may be any expression whose value is an integer or
+pointer (but parentheses are required around binary operators, just as in
+a cast). This construct is allowed regardless of what kind of data is
+normally supposed to reside at @var{addr}.
+@end table
+
+@node Variables
+@section Program variables
+
+The most common kind of expression to use is the name of a variable
+in your program.
+
+Variables in expressions are understood in the selected stack frame
+(@pxref{Selection, ,Selecting a frame}); they must either be global
+(or static) or be visible according to the scope rules of the
+programming language from the point of execution in that frame. This
+means that in the function
+
+@example
+foo (a)
+ int a;
+@{
+ bar (a);
+ @{
+ int b = test ();
+ bar (b);
+ @}
+@}
+@end example
+
+@noindent
+you can examine and use the variable @code{a} whenever your program is
+executing within the function @code{foo}, but you can only use or
+examine the variable @code{b} while your program is executing inside
+the block where @code{b} is declared.
+
+@cindex variable name conflict
+There is an exception: you can refer to a variable or function whose
+scope is a single source file even if the current execution point is not
+in this file. But it is possible to have more than one such variable or
+function with the same name (in different source files). If that
+happens, referring to that name has unpredictable effects. If you wish,
+you can specify a static variable in a particular function or file,
+using the colon-colon notation:
+
+@cindex colon-colon
+@iftex
+@c info cannot cope with a :: index entry, but why deprive hard copy readers?
+@kindex ::
+@end iftex
+@example
+@var{file}::@var{variable}
+@var{function}::@var{variable}
+@end example
+
+@noindent
+Here @var{file} or @var{function} is the name of the context for the
+static @var{variable}. In the case of file names, you can use quotes to
+make sure @value{GDBN} parses the file name as a single word---for example,
+to print a global value of @code{x} defined in @file{f2.c}:
+
+@example
+(@value{GDBP}) p 'f2.c'::x
+@end example
+
+@ifclear CONLY
+@cindex C++ scope resolution
+This use of @samp{::} is very rarely in conflict with the very similar
+use of the same notation in C++. @value{GDBN} also supports use of the C++
+scope resolution operator in @value{GDBN} expressions.
+@c FIXME: Um, so what happens in one of those rare cases where it's in
+@c conflict?? --mew
+@end ifclear
+
+@cindex wrong values
+@cindex variable values, wrong
+@quotation
+@emph{Warning:} Occasionally, a local variable may appear to have the
+wrong value at certain points in a function---just after entry to a new
+scope, and just before exit.
+@end quotation
+You may see this problem when you are stepping by machine instructions.
+This is because on most machines, it takes more than one instruction to
+set up a stack frame (including local variable definitions); if you are
+stepping by machine instructions, variables may appear to have the wrong
+values until the stack frame is completely built. On exit, it usually
+also takes more than one machine instruction to destroy a stack frame;
+after you begin stepping through that group of instructions, local
+variable definitions may be gone.
+
+@node Arrays
+@section Artificial arrays
+
+@cindex artificial array
+@kindex @@
+It is often useful to print out several successive objects of the
+same type in memory; a section of an array, or an array of
+dynamically determined size for which only a pointer exists in the
+program.
+
+You can do this by referring to a contiguous span of memory as an
+@dfn{artificial array}, using the binary operator @samp{@@}. The left
+operand of @samp{@@} should be the first element of the desired array,
+as an individual object. The right operand should be the desired length
+of the array. The result is an array value whose elements are all of
+the type of the left argument. The first element is actually the left
+argument; the second element comes from bytes of memory immediately
+following those that hold the first element, and so on. Here is an
+example. If a program says
+
+@example
+int *array = (int *) malloc (len * sizeof (int));
+@end example
+
+@noindent
+you can print the contents of @code{array} with
+
+@example
+p *array@@len
+@end example
+
+The left operand of @samp{@@} must reside in memory. Array values made
+with @samp{@@} in this way behave just like other arrays in terms of
+subscripting, and are coerced to pointers when used in expressions.
+Artificial arrays most often appear in expressions via the value history
+(@pxref{Value History, ,Value history}), after printing one out.
+
+Sometimes the artificial array mechanism is not quite enough; in
+moderately complex data structures, the elements of interest may not
+actually be adjacent---for example, if you are interested in the values
+of pointers in an array. One useful work-around in this situation is
+to use a convenience variable (@pxref{Convenience Vars, ,Convenience
+variables}) as a counter in an expression that prints the first
+interesting value, and then repeat that expression via @key{RET}. For
+instance, suppose you have an array @code{dtab} of pointers to
+structures, and you are interested in the values of a field @code{fv}
+in each structure. Here is an example of what you might type:
+
+@example
+set $i = 0
+p dtab[$i++]->fv
+@key{RET}
+@key{RET}
+@dots{}
+@end example
+
+@node Output Formats
+@section Output formats
+
+@cindex formatted output
+@cindex output formats
+By default, @value{GDBN} prints a value according to its data type. Sometimes
+this is not what you want. For example, you might want to print a number
+in hex, or a pointer in decimal. Or you might want to view data in memory
+at a certain address as a character string or as an instruction. To do
+these things, specify an @dfn{output format} when you print a value.
+
+The simplest use of output formats is to say how to print a value
+already computed. This is done by starting the arguments of the
+@code{print} command with a slash and a format letter. The format
+letters supported are:
+
+@table @code
+@item x
+Regard the bits of the value as an integer, and print the integer in
+hexadecimal.
+
+@item d
+Print as integer in signed decimal.
+
+@item u
+Print as integer in unsigned decimal.
+
+@item o
+Print as integer in octal.
+
+@item t
+Print as integer in binary. The letter @samp{t} stands for ``two''.
+@footnote{@samp{b} cannot be used because these format letters are also
+used with the @code{x} command, where @samp{b} stands for ``byte'';
+@pxref{Memory,,Examining memory}.}
+
+@item a
+Print as an address, both absolute in hex and as an offset from the
+nearest preceding symbol. This format can be used to discover where (in
+what function) an unknown address is located:
+
+@example
+(@value{GDBP}) p/a 0x54320
+$3 = 0x54320 <_initialize_vx+396>
+@end example
+
+@item c
+Regard as an integer and print it as a character constant.
+
+@item f
+Regard the bits of the value as a floating point number and print
+using typical floating point syntax.
+@end table
+
+For example, to print the program counter in hex (@pxref{Registers}), type
+
+@example
+p/x $pc
+@end example
+
+@noindent
+Note that no space is required before the slash; this is because command
+names in @value{GDBN} cannot contain a slash.
+
+To reprint the last value in the value history with a different format,
+you can use the @code{print} command with just a format and no
+expression. For example, @samp{p/x} reprints the last value in hex.
+
+@node Memory
+@section Examining memory
+
+You can use the command @code{x} (for ``examine'') to examine memory in
+any of several formats, independently of your program's data types.
+
+@cindex examining memory
+@table @code
+@kindex x
+@item x/@var{nfu} @var{addr}
+@itemx x @var{addr}
+@itemx x
+Use the @code{x} command to examine memory.
+@end table
+
+@var{n}, @var{f}, and @var{u} are all optional parameters that specify how
+much memory to display and how to format it; @var{addr} is an
+expression giving the address where you want to start displaying memory.
+If you use defaults for @var{nfu}, you need not type the slash @samp{/}.
+Several commands set convenient defaults for @var{addr}.
+
+@table @r
+@item @var{n}, the repeat count
+The repeat count is a decimal integer; the default is 1. It specifies
+how much memory (counting by units @var{u}) to display.
+@c This really is **decimal**; unaffected by 'set radix' as of GDB
+@c 4.1.2.
+
+@item @var{f}, the display format
+The display format is one of the formats used by @code{print},
+or @samp{s} (null-terminated string) or @samp{i} (machine instruction).
+The default is @samp{x} (hexadecimal) initially, or the format from the
+last time you used either @code{x} or @code{print}.
+
+@item @var{u}, the unit size
+The unit size is any of
+
+@table @code
+@item b
+Bytes.
+@item h
+Halfwords (two bytes).
+@item w
+Words (four bytes). This is the initial default.
+@item g
+Giant words (eight bytes).
+@end table
+
+Each time you specify a unit size with @code{x}, that size becomes the
+default unit the next time you use @code{x}. (For the @samp{s} and
+@samp{i} formats, the unit size is ignored and is normally not written.)
+
+@item @var{addr}, starting display address
+@var{addr} is the address where you want @value{GDBN} to begin displaying
+memory. The expression need not have a pointer value (though it may);
+it is always interpreted as an integer address of a byte of memory.
+@xref{Expressions, ,Expressions}, for more information on expressions. The default for
+@var{addr} is usually just after the last address examined---but several
+other commands also set the default address: @code{info breakpoints} (to
+the address of the last breakpoint listed), @code{info line} (to the
+starting address of a line), and @code{print} (if you use it to display
+a value from memory).
+@end table
+
+For example, @samp{x/3uh 0x54320} is a request to display three halfwords
+(@code{h}) of memory, formatted as unsigned decimal integers (@samp{u}),
+starting at address @code{0x54320}. @samp{x/4xw $sp} prints the four
+words (@samp{w}) of memory above the stack pointer (here, @samp{$sp};
+@pxref{Registers}) in hexadecimal (@samp{x}).
+
+Since the letters indicating unit sizes are all distinct from the
+letters specifying output formats, you do not have to remember whether
+unit size or format comes first; either order will work. The output
+specifications @samp{4xw} and @samp{4wx} mean exactly the same thing.
+(However, the count @var{n} must come first; @samp{wx4} will not work.)
+
+Even though the unit size @var{u} is ignored for the formats @samp{s}
+and @samp{i}, you might still want to use a count @var{n}; for example,
+@samp{3i} specifies that you want to see three machine instructions,
+including any operands. The command @code{disassemble} gives an
+alternative way of inspecting machine instructions; @pxref{Machine
+Code,,Source and machine code}.
+
+All the defaults for the arguments to @code{x} are designed to make it
+easy to continue scanning memory with minimal specifications each time
+you use @code{x}. For example, after you have inspected three machine
+instructions with @samp{x/3i @var{addr}}, you can inspect the next seven
+with just @samp{x/7}. If you use @key{RET} to repeat the @code{x} command,
+the repeat count @var{n} is used again; the other arguments default as
+for successive uses of @code{x}.
+
+@cindex @code{$_}, @code{$__}, and value history
+The addresses and contents printed by the @code{x} command are not saved
+in the value history because there is often too much of them and they
+would get in the way. Instead, @value{GDBN} makes these values available for
+subsequent use in expressions as values of the convenience variables
+@code{$_} and @code{$__}. After an @code{x} command, the last address
+examined is available for use in expressions in the convenience variable
+@code{$_}. The contents of that address, as examined, are available in
+the convenience variable @code{$__}.
+
+If the @code{x} command has a repeat count, the address and contents saved
+are from the last memory unit printed; this is not the same as the last
+address printed if several units were printed on the last line of output.
+
+@node Auto Display
+@section Automatic display
+@cindex automatic display
+@cindex display of expressions
+
+If you find that you want to print the value of an expression frequently
+(to see how it changes), you might want to add it to the @dfn{automatic
+display list} so that @value{GDBN} will print its value each time your program stops.
+Each expression added to the list is given a number to identify it;
+to remove an expression from the list, you specify that number.
+The automatic display looks like this:
+
+@example
+2: foo = 38
+3: bar[5] = (struct hack *) 0x3804
+@end example
+
+@noindent
+This display shows item numbers, expressions and their current values. As with
+displays you request manually using @code{x} or @code{print}, you can
+specify the output format you prefer; in fact, @code{display} decides
+whether to use @code{print} or @code{x} depending on how elaborate your
+format specification is---it uses @code{x} if you specify a unit size,
+or one of the two formats (@samp{i} and @samp{s}) that are only
+supported by @code{x}; otherwise it uses @code{print}.
+
+@table @code
+@item display @var{exp}
+@kindex display
+Add the expression @var{exp} to the list of expressions to display
+each time your program stops. @xref{Expressions, ,Expressions}.
+
+@code{display} will not repeat if you press @key{RET} again after using it.
+
+@item display/@var{fmt} @var{exp}
+For @var{fmt} specifying only a display format and not a size or
+count, add the expression @var{exp} to the auto-display list but
+arrange to display it each time in the specified format @var{fmt}.
+@xref{Output Formats,,Output formats}.
+
+@item display/@var{fmt} @var{addr}
+For @var{fmt} @samp{i} or @samp{s}, or including a unit-size or a
+number of units, add the expression @var{addr} as a memory address to
+be examined each time your program stops. Examining means in effect
+doing @samp{x/@var{fmt} @var{addr}}. @xref{Memory, ,Examining memory}.
+@end table
+
+For example, @samp{display/i $pc} can be helpful, to see the machine
+instruction about to be executed each time execution stops (@samp{$pc}
+is a common name for the program counter; @pxref{Registers}).
+
+@table @code
+@item undisplay @var{dnums}@dots{}
+@itemx delete display @var{dnums}@dots{}
+@kindex delete display
+@kindex undisplay
+Remove item numbers @var{dnums} from the list of expressions to display.
+
+@code{undisplay} will not repeat if you press @key{RET} after using it.
+(Otherwise you would just get the error @samp{No display number @dots{}}.)
+
+@item disable display @var{dnums}@dots{}
+@kindex disable display
+Disable the display of item numbers @var{dnums}. A disabled display
+item is not printed automatically, but is not forgotten. It may be
+enabled again later.
+
+@item enable display @var{dnums}@dots{}
+@kindex enable display
+Enable display of item numbers @var{dnums}. It becomes effective once
+again in auto display of its expression, until you specify otherwise.
+
+@item display
+Display the current values of the expressions on the list, just as is
+done when your program stops.
+
+@item info display
+@kindex info display
+Print the list of expressions previously set up to display
+automatically, each one with its item number, but without showing the
+values. This includes disabled expressions, which are marked as such.
+It also includes expressions which would not be displayed right now
+because they refer to automatic variables not currently available.
+@end table
+
+If a display expression refers to local variables, then it does not make
+sense outside the lexical context for which it was set up. Such an
+expression is disabled when execution enters a context where one of its
+variables is not defined. For example, if you give the command
+@code{display last_char} while inside a function with an argument
+@code{last_char}, then this argument will be displayed while your program
+continues to stop inside that function. When it stops elsewhere---where
+there is no variable @code{last_char}---display is disabled. The next time
+your program stops where @code{last_char} is meaningful, you can enable the
+display expression once again.
+
+@node Print Settings
+@section Print settings
+
+@cindex format options
+@cindex print settings
+@value{GDBN} provides the following ways to control how arrays, structures,
+and symbols are printed.
+
+@noindent
+These settings are useful for debugging programs in any language:
+
+@table @code
+@item set print address
+@itemx set print address on
+@kindex set print address
+@value{GDBN} will print memory addresses showing the location of stack
+traces, structure values, pointer values, breakpoints, and so forth,
+even when it also displays the contents of those addresses. The default
+is on. For example, this is what a stack frame display looks like, with
+@code{set print address on}:
+
+@smallexample
+@group
+(@value{GDBP}) f
+#0 set_quotes (lq=0x34c78 "<<", rq=0x34c88 ">>")
+ at input.c:530
+530 if (lquote != def_lquote)
+@end group
+@end smallexample
+
+@item set print address off
+Do not print addresses when displaying their contents. For example,
+this is the same stack frame displayed with @code{set print address off}:
+
+@smallexample
+@group
+(@value{GDBP}) set print addr off
+(@value{GDBP}) f
+#0 set_quotes (lq="<<", rq=">>") at input.c:530
+530 if (lquote != def_lquote)
+@end group
+@end smallexample
+
+You can use @samp{set print address off} to eliminate all machine
+dependent displays from the @value{GDBN} interface. For example, with
+@code{print address off}, you should get the same text for backtraces on
+all machines---whether or not they involve pointer arguments.
+
+@item show print address
+@kindex show print address
+Show whether or not addresses are to be printed.
+@end table
+
+When @value{GDBN} prints a symbolic address, it normally prints the
+closest earlier symbol plus an offset. If that symbol does not uniquely
+identify the address (for example, it is a name whose scope is a single
+source file), you may need to disambiguate. One way to do this is with
+@code{info line}, for example @code{info line *0x4537}. Alternately,
+you can set @value{GDBN} to print the source file and line number when
+it prints a symbolic address:
+
+@table @code
+@item set print symbol-filename on
+@kindex set print symbol-filename
+Tell @value{GDBN} to print the source file name and line number of a
+symbol in the symbolic form of an address.
+
+@item set print symbol-filename off
+Do not print source file name and line number of a symbol. This is the
+default.
+
+@item show print symbol-filename
+@kindex show print symbol-filename
+Show whether or not @value{GDBN} will print the source file name and
+line number of a symbol in the symbolic form of an address.
+@end table
+
+Also, you may wish to see the symbolic form only if the address being
+printed is reasonably close to the closest earlier symbol:
+
+@table @code
+@item set print max-symbolic-offset @var{max-offset}
+@kindex set print max-symbolic-offset
+Tell @value{GDBN} to only display the symbolic form of an address if the
+offset between the closest earlier symbol and the address is less than
+@var{max-offset}. The default is 0, which means to always print the
+symbolic form of an address, if any symbol precedes it.
+
+@item show print max-symbolic-offset
+@kindex show print max-symbolic-offset
+Ask how large the maximum offset is that @value{GDBN} will print in a
+symbolic address.
+@end table
+
+@table @code
+@item set print array
+@itemx set print array on
+@kindex set print array
+@value{GDBN} will pretty-print arrays. This format is more convenient to read,
+but uses more space. The default is off.
+
+@item set print array off
+Return to compressed format for arrays.
+
+@item show print array
+@kindex show print array
+Show whether compressed or pretty format is selected for displaying
+arrays.
+
+@item set print elements @var{number-of-elements}
+@kindex set print elements
+If @value{GDBN} is printing a large array, it will stop printing after it has
+printed the number of elements set by the @code{set print elements} command.
+This limit also applies to the display of strings.
+Setting the number of elements to zero means that the printing is unlimited.
+
+@item show print elements
+@kindex show print elements
+Display the number of elements of a large array that @value{GDBN} will print
+before losing patience.
+
+@item set print pretty on
+@kindex set print pretty
+Cause @value{GDBN} to print structures in an indented format with one member per
+line, like this:
+
+@smallexample
+@group
+$1 = @{
+ next = 0x0,
+ flags = @{
+ sweet = 1,
+ sour = 1
+ @},
+ meat = 0x54 "Pork"
+@}
+@end group
+@end smallexample
+
+@item set print pretty off
+Cause @value{GDBN} to print structures in a compact format, like this:
+
+@smallexample
+@group
+$1 = @{next = 0x0, flags = @{sweet = 1, sour = 1@}, \
+meat = 0x54 "Pork"@}
+@end group
+@end smallexample
+
+@noindent
+This is the default format.
+
+@item show print pretty
+@kindex show print pretty
+Show which format @value{GDBN} will use to print structures.
+
+@item set print sevenbit-strings on
+@kindex set print sevenbit-strings
+Print using only seven-bit characters; if this option is set,
+@value{GDBN} will display any eight-bit characters (in strings or character
+values) using the notation @code{\}@var{nnn}. For example, @kbd{M-a} is
+displayed as @code{\341}.
+
+@item set print sevenbit-strings off
+Print using either seven-bit or eight-bit characters, as required. This
+is the default.
+
+@item show print sevenbit-strings
+@kindex show print sevenbit-strings
+Show whether or not @value{GDBN} will print only seven-bit characters.
+
+@item set print union on
+@kindex set print union
+Tell @value{GDBN} to print unions which are contained in structures. This is the
+default setting.
+
+@item set print union off
+Tell @value{GDBN} not to print unions which are contained in structures.
+
+@item show print union
+@kindex show print union
+Ask @value{GDBN} whether or not it will print unions which are contained in
+structures.
+
+For example, given the declarations
+
+@smallexample
+typedef enum @{Tree, Bug@} Species;
+typedef enum @{Big_tree, Acorn, Seedling@} Tree_forms;
+typedef enum @{Caterpillar, Cocoon, Butterfly@}
+ Bug_forms;
+
+struct thing @{
+ Species it;
+ union @{
+ Tree_forms tree;
+ Bug_forms bug;
+ @} form;
+@};
+
+struct thing foo = @{Tree, @{Acorn@}@};
+@end smallexample
+
+@noindent
+with @code{set print union on} in effect @samp{p foo} would print
+
+@smallexample
+$1 = @{it = Tree, form = @{tree = Acorn, bug = Cocoon@}@}
+@end smallexample
+
+@noindent
+and with @code{set print union off} in effect it would print
+
+@smallexample
+$1 = @{it = Tree, form = @{...@}@}
+@end smallexample
+@end table
+
+@ifclear CONLY
+@need 1000
+@noindent
+These settings are of interest when debugging C++ programs:
+
+@table @code
+@item set print demangle
+@itemx set print demangle on
+@kindex set print demangle
+Print C++ names in their source form rather than in the encoded
+(``mangled'') form passed to the assembler and linker for type-safe
+linkage. The default is @samp{on}.
+
+@item show print demangle
+@kindex show print demangle
+Show whether C++ names will be printed in mangled or demangled form.
+
+@item set print asm-demangle
+@itemx set print asm-demangle on
+@kindex set print asm-demangle
+Print C++ names in their source form rather than their mangled form, even
+in assembler code printouts such as instruction disassemblies.
+The default is off.
+
+@item show print asm-demangle
+@kindex show print asm-demangle
+Show whether C++ names in assembly listings will be printed in mangled
+or demangled form.
+
+@item set demangle-style @var{style}
+@kindex set demangle-style
+@cindex C++ symbol decoding style
+@cindex symbol decoding style, C++
+Choose among several encoding schemes used by different compilers to
+represent C++ names. The choices for @var{style} are currently:
+
+@table @code
+@item auto
+Allow @value{GDBN} to choose a decoding style by inspecting your program.
+
+@item gnu
+Decode based on the GNU C++ compiler (@code{g++}) encoding algorithm.
+
+@item lucid
+Decode based on the Lucid C++ compiler (@code{lcc}) encoding algorithm.
+
+@item arm
+Decode using the algorithm in the @cite{C++ Annotated Reference Manual}.
+@strong{Warning:} this setting alone is not sufficient to allow
+debugging @code{cfront}-generated executables. @value{GDBN} would
+require further enhancement to permit that.
+@end table
+
+@item show demangle-style
+@kindex show demangle-style
+Display the encoding style currently in use for decoding C++ symbols.
+
+@item set print object
+@itemx set print object on
+@kindex set print object
+When displaying a pointer to an object, identify the @emph{actual}
+(derived) type of the object rather than the @emph{declared} type, using
+the virtual function table.
+
+@item set print object off
+Display only the declared type of objects, without reference to the
+virtual function table. This is the default setting.
+
+@item show print object
+@kindex show print object
+Show whether actual, or declared, object types will be displayed.
+
+@item set print vtbl
+@itemx set print vtbl on
+@kindex set print vtbl
+Pretty print C++ virtual function tables. The default is off.
+
+@item set print vtbl off
+Do not pretty print C++ virtual function tables.
+
+@item show print vtbl
+@kindex show print vtbl
+Show whether C++ virtual function tables are pretty printed, or not.
+@end table
+@end ifclear
+
+@node Value History
+@section Value history
+
+@cindex value history
+Values printed by the @code{print} command are saved in the @value{GDBN} @dfn{value
+history} so that you can refer to them in other expressions. Values are
+kept until the symbol table is re-read or discarded (for example with
+the @code{file} or @code{symbol-file} commands). When the symbol table
+changes, the value history is discarded, since the values may contain
+pointers back to the types defined in the symbol table.
+
+@cindex @code{$}
+@cindex @code{$$}
+@cindex history number
+The values printed are given @dfn{history numbers} by which you can
+refer to them. These are successive integers starting with one.
+@code{print} shows you the history number assigned to a value by
+printing @samp{$@var{num} = } before the value; here @var{num} is the
+history number.
+
+To refer to any previous value, use @samp{$} followed by the value's
+history number. The way @code{print} labels its output is designed to
+remind you of this. Just @code{$} refers to the most recent value in
+the history, and @code{$$} refers to the value before that.
+@code{$$@var{n}} refers to the @var{n}th value from the end; @code{$$2}
+is the value just prior to @code{$$}, @code{$$1} is equivalent to
+@code{$$}, and @code{$$0} is equivalent to @code{$}.
+
+For example, suppose you have just printed a pointer to a structure and
+want to see the contents of the structure. It suffices to type
+
+@example
+p *$
+@end example
+
+If you have a chain of structures where the component @code{next} points
+to the next one, you can print the contents of the next one with this:
+
+@example
+p *$.next
+@end example
+
+@noindent
+You can print successive links in the chain by repeating this
+command---which you can do by just typing @key{RET}.
+
+Note that the history records values, not expressions. If the value of
+@code{x} is 4 and you type these commands:
+
+@example
+print x
+set x=5
+@end example
+
+@noindent
+then the value recorded in the value history by the @code{print} command
+remains 4 even though the value of @code{x} has changed.
+
+@table @code
+@kindex show values
+@item show values
+Print the last ten values in the value history, with their item numbers.
+This is like @samp{p@ $$9} repeated ten times, except that @code{show
+values} does not change the history.
+
+@item show values @var{n}
+Print ten history values centered on history item number @var{n}.
+
+@item show values +
+Print ten history values just after the values last printed. If no more
+values are available, produces no display.
+@end table
+
+Pressing @key{RET} to repeat @code{show values @var{n}} has exactly the
+same effect as @samp{show values +}.
+
+@node Convenience Vars
+@section Convenience variables
+
+@cindex convenience variables
+@value{GDBN} provides @dfn{convenience variables} that you can use within
+@value{GDBN} to hold on to a value and refer to it later. These variables
+exist entirely within @value{GDBN}; they are not part of your program, and
+setting a convenience variable has no direct effect on further execution
+of your program. That is why you can use them freely.
+
+Convenience variables are prefixed with @samp{$}. Any name preceded by
+@samp{$} can be used for a convenience variable, unless it is one of
+the predefined machine-specific register names (@pxref{Registers}).
+(Value history references, in contrast, are @emph{numbers} preceded
+by @samp{$}. @xref{Value History, ,Value history}.)
+
+You can save a value in a convenience variable with an assignment
+expression, just as you would set a variable in your program.
+For example:
+
+@example
+set $foo = *object_ptr
+@end example
+
+@noindent
+would save in @code{$foo} the value contained in the object pointed to by
+@code{object_ptr}.
+
+Using a convenience variable for the first time creates it, but its
+value is @code{void} until you assign a new value. You can alter the
+value with another assignment at any time.
+
+Convenience variables have no fixed types. You can assign a convenience
+variable any type of value, including structures and arrays, even if
+that variable already has a value of a different type. The convenience
+variable, when used as an expression, has the type of its current value.
+
+@table @code
+@item show convenience
+@kindex show convenience
+Print a list of convenience variables used so far, and their values.
+Abbreviated @code{show con}.
+@end table
+
+One of the ways to use a convenience variable is as a counter to be
+incremented or a pointer to be advanced. For example, to print
+a field from successive elements of an array of structures:
+
+@example
+set $i = 0
+print bar[$i++]->contents
+@i{@dots{} repeat that command by typing @key{RET}.}
+@end example
+
+Some convenience variables are created automatically by @value{GDBN} and given
+values likely to be useful.
+
+@table @code
+@item $_
+@kindex $_
+The variable @code{$_} is automatically set by the @code{x} command to
+the last address examined (@pxref{Memory, ,Examining memory}). Other
+commands which provide a default address for @code{x} to examine also
+set @code{$_} to that address; these commands include @code{info line}
+and @code{info breakpoint}. The type of @code{$_} is @code{void *}
+except when set by the @code{x} command, in which case it is a pointer
+to the type of @code{$__}.
+
+@item $__
+@kindex $__
+The variable @code{$__} is automatically set by the @code{x} command
+to the value found in the last address examined. Its type is chosen
+to match the format in which the data was printed.
+@end table
+
+@node Registers
+@section Registers
+
+@cindex registers
+You can refer to machine register contents, in expressions, as variables
+with names starting with @samp{$}. The names of registers are different
+for each machine; use @code{info registers} to see the names used on
+your machine.
+
+@table @code
+@item info registers
+@kindex info registers
+Print the names and values of all registers except floating-point
+registers (in the selected stack frame).
+
+@item info all-registers
+@kindex info all-registers
+@cindex floating point registers
+Print the names and values of all registers, including floating-point
+registers.
+
+@item info registers @var{regname} @dots{}
+Print the relativized value of each specified register @var{regname}.
+@var{regname} may be any register name valid on the machine you are using, with
+or without the initial @samp{$}.
+@end table
+
+@value{GDBN} has four ``standard'' register names that are available (in
+expressions) on most machines---whenever they do not conflict with an
+architecture's canonical mnemonics for registers. The register names
+@code{$pc} and @code{$sp} are used for the program counter register and
+the stack pointer. @code{$fp} is used for a register that contains a
+pointer to the current stack frame, and @code{$ps} is used for a
+register that contains the processor status. For example,
+you could print the program counter in hex with
+
+@example
+p/x $pc
+@end example
+
+@noindent
+or print the instruction to be executed next with
+
+@example
+x/i $pc
+@end example
+
+@noindent
+or add four to the stack pointer@footnote{This is a way of removing
+one word from the stack, on machines where stacks grow downward in
+memory (most machines, nowadays). This assumes that the innermost
+stack frame is selected; setting @code{$sp} is not allowed when other
+stack frames are selected. To pop entire frames off the stack,
+regardless of machine architecture, use @code{return};
+@pxref{Returning, ,Returning from a function}.} with
+
+@example
+set $sp += 4
+@end example
+
+Whenever possible, these four standard register names are available on
+your machine even though the machine has different canonical mnemonics,
+so long as there is no conflict. The @code{info registers} command
+shows the canonical names. For example, on the SPARC, @code{info
+registers} displays the processor status register as @code{$psr} but you
+can also refer to it as @code{$ps}.
+
+@value{GDBN} always considers the contents of an ordinary register as an
+integer when the register is examined in this way. Some machines have
+special registers which can hold nothing but floating point; these
+registers are considered to have floating point values. There is no way
+to refer to the contents of an ordinary register as floating point value
+(although you can @emph{print} it as a floating point value with
+@samp{print/f $@var{regname}}).
+
+Some registers have distinct ``raw'' and ``virtual'' data formats. This
+means that the data format in which the register contents are saved by
+the operating system is not the same one that your program normally
+sees. For example, the registers of the 68881 floating point
+coprocessor are always saved in ``extended'' (raw) format, but all C
+programs expect to work with ``double'' (virtual) format. In such
+cases, @value{GDBN} normally works with the virtual format only (the format that
+makes sense for your program), but the @code{info registers} command
+prints the data in both formats.
+
+Normally, register values are relative to the selected stack frame
+(@pxref{Selection, ,Selecting a frame}). This means that you get the
+value that the register would contain if all stack frames farther in
+were exited and their saved registers restored. In order to see the
+true contents of hardware registers, you must select the innermost
+frame (with @samp{frame 0}).
+
+However, @value{GDBN} must deduce where registers are saved, from the machine
+code generated by your compiler. If some registers are not saved, or if
+@value{GDBN} is unable to locate the saved registers, the selected stack
+frame will make no difference.
+
+@ifset AMD29K
+@table @code
+@item set rstack_high_address @var{address}
+@kindex set rstack_high_address
+@cindex AMD 29K register stack
+@cindex register stack, AMD29K
+On AMD 29000 family processors, registers are saved in a separate
+``register stack''. There is no way for @value{GDBN} to determine the extent
+of this stack. Normally, @value{GDBN} just assumes that the stack is ``large
+enough''. This may result in @value{GDBN} referencing memory locations that
+do not exist. If necessary, you can get around this problem by
+specifying the ending address of the register stack with the @code{set
+rstack_high_address} command. The argument should be an address, which
+you will probably want to precede with @samp{0x} to specify in
+hexadecimal.
+
+@item show rstack_high_address
+@kindex show rstack_high_address
+Display the current limit of the register stack, on AMD 29000 family
+processors.
+@end table
+@end ifset
+
+@ifclear HAVE-FLOAT
+@node Floating Point Hardware
+@section Floating point hardware
+@cindex floating point
+
+@c FIXME! Really host, not target?
+Depending on the host machine architecture, @value{GDBN} may be able to give
+you more information about the status of the floating point hardware.
+
+@table @code
+@item info float
+@kindex info float
+Display hardware-dependent information about the floating
+point unit. The exact contents and layout vary depending on the
+floating point chip; on some platforms, @samp{info float} is not
+available at all.
+@end table
+@c FIXME: this is a cop-out. Try to get examples, explanations. Only
+@c FIXME...supported currently on arm's and 386's. Mark properly with
+@c FIXME... m4 macros to isolate general statements from hardware-dep,
+@c FIXME... at that point.
+@end ifclear
+
+@ifclear CONLY
+@node Languages
+@chapter Using @value{GDBN} with Different Languages
+@cindex languages
+
+@ifset MOD2
+Although programming languages generally have common aspects, they are
+rarely expressed in the same manner. For instance, in ANSI C,
+dereferencing a pointer @code{p} is accomplished by @code{*p}, but in
+Modula-2, it is accomplished by @code{p^}. Values can also be
+represented (and displayed) differently. Hex numbers in C are written
+like @samp{0x1ae}, while in Modula-2 they appear as @samp{1AEH}.
+@end ifset
+
+@cindex working language
+Language-specific information is built into @value{GDBN} for some languages,
+allowing you to express operations like the above in your program's
+native language, and allowing @value{GDBN} to output values in a manner
+consistent with the syntax of your program's native language. The
+language you use to build expressions, called the @dfn{working
+language}, can be selected manually, or @value{GDBN} can set it
+automatically.
+
+@menu
+* Setting:: Switching between source languages
+* Show:: Displaying the language
+@ifset MOD2
+* Checks:: Type and range checks
+@end ifset
+
+* Support:: Supported languages
+@end menu
+
+@node Setting
+@section Switching between source languages
+
+There are two ways to control the working language---either have @value{GDBN}
+set it automatically, or select it manually yourself. You can use the
+@code{set language} command for either purpose. On startup, @value{GDBN}
+defaults to setting the language automatically.
+
+@menu
+* Manually:: Setting the working language manually
+* Automatically:: Having @value{GDBN} infer the source language
+@end menu
+
+@node Manually
+@subsection Setting the working language
+
+If you allow @value{GDBN} to set the language automatically,
+expressions are interpreted the same way in your debugging session and
+your program.
+
+@kindex set language
+If you wish, you may set the language manually. To do this, issue the
+command @samp{set language @var{lang}}, where @var{lang} is the name of
+a language, such as
+@ifclear MOD2
+@code{c}.
+@end ifclear
+@ifset MOD2
+@code{c} or @code{modula-2}.
+@end ifset
+For a list of the supported languages, type @samp{set language}.
+@c FIXME: rms: eventually this command should be "help set language".
+
+@ifset MOD2
+Setting the language manually prevents @value{GDBN} from updating the working
+language automatically. This can lead to confusion if you try
+to debug a program when the working language is not the same as the
+source language, when an expression is acceptable to both
+languages---but means different things. For instance, if the current
+source file were written in C, and @value{GDBN} was parsing Modula-2, a
+command such as:
+
+@example
+print a = b + c
+@end example
+
+@noindent
+might not have the effect you intended. In C, this means to add
+@code{b} and @code{c} and place the result in @code{a}. The result
+printed would be the value of @code{a}. In Modula-2, this means to compare
+@code{a} to the result of @code{b+c}, yielding a @code{BOOLEAN} value.
+@end ifset
+
+@node Automatically
+@subsection Having @value{GDBN} infer the source language
+
+To have @value{GDBN} set the working language automatically, use @samp{set
+language local} or @samp{set language auto}. @value{GDBN} then infers the
+language that a program was written in by looking at the name of its
+source files, and examining their extensions:
+
+@table @file
+@ifset MOD2
+@item *.mod
+Modula-2 source file
+@end ifset
+
+@item *.c
+C source file
+
+@item *.C
+@itemx *.cc
+C++ source file
+@end table
+
+This information is recorded for each function or procedure in a source
+file. When your program stops in a frame (usually by encountering a
+breakpoint), @value{GDBN} sets the working language to the language recorded
+for the function in that frame. If the language for a frame is unknown
+(that is, if the function or block corresponding to the frame was
+defined in a source file that does not have a recognized extension), the
+current working language is not changed, and @value{GDBN} issues a warning.
+
+This may not seem necessary for most programs, which are written
+entirely in one source language. However, program modules and libraries
+written in one source language can be used by a main program written in
+a different source language. Using @samp{set language auto} in this
+case frees you from having to set the working language manually.
+
+@node Show
+@section Displaying the language
+
+The following commands will help you find out which language is the
+working language, and also what language source files were written in.
+
+@kindex show language
+@kindex info frame
+@kindex info source
+@table @code
+@item show language
+Display the current working language. This is the
+language you can use with commands such as @code{print} to
+build and compute expressions that may involve variables in your program.
+
+@item info frame
+Among the other information listed here (@pxref{Frame Info, ,Information
+about a frame}) is the source language for this frame. This is the
+language that will become the working language if you ever use an
+identifier that is in this frame.
+
+@item info source
+Among the other information listed here (@pxref{Symbols, ,Examining the
+Symbol Table}) is the source language of this source file.
+@end table
+
+@ifset MOD2
+@node Checks
+@section Type and range checking
+
+@quotation
+@emph{Warning:} In this release, the @value{GDBN} commands for type and range
+checking are included, but they do not yet have any effect. This
+section documents the intended facilities.
+@end quotation
+@c FIXME remove warning when type/range code added
+
+Some languages are designed to guard you against making seemingly common
+errors through a series of compile- and run-time checks. These include
+checking the type of arguments to functions and operators, and making
+sure mathematical overflows are caught at run time. Checks such as
+these help to ensure a program's correctness once it has been compiled
+by eliminating type mismatches, and providing active checks for range
+errors when your program is running.
+
+@value{GDBN} can check for conditions like the above if you wish.
+Although @value{GDBN} will not check the statements in your program, it
+can check expressions entered directly into @value{GDBN} for evaluation via
+the @code{print} command, for example. As with the working language,
+@value{GDBN} can also decide whether or not to check automatically based on
+your program's source language. @xref{Support, ,Supported languages},
+for the default settings of supported languages.
+
+@menu
+* Type Checking:: An overview of type checking
+* Range Checking:: An overview of range checking
+@end menu
+
+@cindex type checking
+@cindex checks, type
+@node Type Checking
+@subsection An overview of type checking
+
+Some languages, such as Modula-2, are strongly typed, meaning that the
+arguments to operators and functions have to be of the correct type,
+otherwise an error occurs. These checks prevent type mismatch
+errors from ever causing any run-time problems. For example,
+
+@example
+1 + 2 @result{} 3
+@exdent but
+@error{} 1 + 2.3
+@end example
+
+The second example fails because the @code{CARDINAL} 1 is not
+type-compatible with the @code{REAL} 2.3.
+
+For expressions you use in @value{GDBN} commands, you can tell the @value{GDBN}
+type checker to skip checking; to treat any mismatches as errors and
+abandon the expression; or only issue warnings when type mismatches
+occur, but evaluate the expression anyway. When you choose the last of
+these, @value{GDBN} evaluates expressions like the second example above, but
+also issues a warning.
+
+Even though you may turn type checking off, other type-based reasons may
+prevent @value{GDBN} from evaluating an expression. For instance, @value{GDBN} does not
+know how to add an @code{int} and a @code{struct foo}. These particular
+type errors have nothing to do with the language in use, and usually
+arise from expressions, such as the one described above, which make
+little sense to evaluate anyway.
+
+Each language defines to what degree it is strict about type. For
+instance, both Modula-2 and C require the arguments to arithmetical
+operators to be numbers. In C, enumerated types and pointers can be
+represented as numbers, so that they are valid arguments to mathematical
+operators. @xref{Support, ,Supported languages}, for further
+details on specific languages.
+
+@value{GDBN} provides some additional commands for controlling the type checker:
+
+@kindex set check
+@kindex set check type
+@kindex show check type
+@table @code
+@item set check type auto
+Set type checking on or off based on the current working language.
+@xref{Support, ,Supported languages}, for the default settings for
+each language.
+
+@item set check type on
+@itemx set check type off
+Set type checking on or off, overriding the default setting for the
+current working language. Issue a warning if the setting does not
+match the language default. If any type mismatches occur in
+evaluating an expression while typechecking is on, @value{GDBN} prints a
+message and aborts evaluation of the expression.
+
+@item set check type warn
+Cause the type checker to issue warnings, but to always attempt to
+evaluate the expression. Evaluating the expression may still
+be impossible for other reasons. For example, @value{GDBN} cannot add
+numbers and structures.
+
+@item show type
+Show the current setting of the type checker, and whether or not @value{GDBN} is
+setting it automatically.
+@end table
+
+@cindex range checking
+@cindex checks, range
+@node Range Checking
+@subsection An overview of range checking
+
+In some languages (such as Modula-2), it is an error to exceed the
+bounds of a type; this is enforced with run-time checks. Such range
+checking is meant to ensure program correctness by making sure
+computations do not overflow, or indices on an array element access do
+not exceed the bounds of the array.
+
+For expressions you use in @value{GDBN} commands, you can tell
+@value{GDBN} to treat range errors in one of three ways: ignore them,
+always treat them as errors and abandon the expression, or issue
+warnings but evaluate the expression anyway.
+
+A range error can result from numerical overflow, from exceeding an
+array index bound, or when you type a constant that is not a member
+of any type. Some languages, however, do not treat overflows as an
+error. In many implementations of C, mathematical overflow causes the
+result to ``wrap around'' to lower values---for example, if @var{m} is
+the largest integer value, and @var{s} is the smallest, then
+
+@example
+@var{m} + 1 @result{} @var{s}
+@end example
+
+This, too, is specific to individual languages, and in some cases
+specific to individual compilers or machines. @xref{Support, ,
+Supported languages}, for further details on specific languages.
+
+@value{GDBN} provides some additional commands for controlling the range checker:
+
+@kindex set check
+@kindex set check range
+@kindex show check range
+@table @code
+@item set check range auto
+Set range checking on or off based on the current working language.
+@xref{Support, ,Supported languages}, for the default settings for
+each language.
+
+@item set check range on
+@itemx set check range off
+Set range checking on or off, overriding the default setting for the
+current working language. A warning is issued if the setting does not
+match the language default. If a range error occurs, then a message
+is printed and evaluation of the expression is aborted.
+
+@item set check range warn
+Output messages when the @value{GDBN} range checker detects a range error,
+but attempt to evaluate the expression anyway. Evaluating the
+expression may still be impossible for other reasons, such as accessing
+memory that the process does not own (a typical example from many Unix
+systems).
+
+@item show range
+Show the current setting of the range checker, and whether or not it is
+being set automatically by @value{GDBN}.
+@end table
+@end ifset
+
+@node Support
+@section Supported languages
+
+@ifset MOD2
+@value{GDBN} 4 supports C, C++, and Modula-2.
+@end ifset
+@ifclear MOD2
+@value{GDBN} 4 supports C, and C++.
+@end ifclear
+Some @value{GDBN} features may be used in expressions regardless of the
+language you use: the @value{GDBN} @code{@@} and @code{::} operators,
+and the @samp{@{type@}addr} construct (@pxref{Expressions,
+,Expressions}) can be used with the constructs of any supported
+language.
+
+The following sections detail to what degree each source language is
+supported by @value{GDBN}. These sections are not meant to be language
+tutorials or references, but serve only as a reference guide to what the
+@value{GDBN} expression parser will accept, and what input and output
+formats should look like for different languages. There are many good
+books written on each of these languages; please look to these for a
+language reference or tutorial.
+
+@ifset MOD2
+@menu
+* C:: C and C++
+* Modula-2:: Modula-2
+@end menu
+
+@node C
+@subsection C and C++
+@cindex C and C++
+@cindex expressions in C or C++
+
+Since C and C++ are so closely related, many features of @value{GDBN} apply
+to both languages. Whenever this is the case, we discuss both languages
+together.
+@end ifset
+@ifclear MOD2
+@c Cancel this below, under same condition, at end of this chapter!
+@raisesections
+@end ifclear
+
+@cindex C++
+@kindex g++
+@cindex GNU C++
+The C++ debugging facilities are jointly implemented by the GNU C++
+compiler and @value{GDBN}. Therefore, to debug your C++ code effectively,
+you must compile your C++ programs with the GNU C++ compiler,
+@code{g++}.
+@end ifclear
+@ifset CONLY
+@node C
+@chapter C Language Support
+@cindex C language
+@cindex expressions in C
+
+Information specific to the C language is built into @value{GDBN} so that you
+can use C expressions while degugging. This also permits @value{GDBN} to
+output values in a manner consistent with C conventions.
+
+@menu
+* C Operators:: C operators
+* C Constants:: C constants
+* Debugging C:: @value{GDBN} and C
+@end menu
+@end ifset
+@ifclear CONLY
+@menu
+* C Operators:: C and C++ operators
+* C Constants:: C and C++ constants
+* Cplus expressions:: C++ expressions
+* C Defaults:: Default settings for C and C++
+@ifset MOD2
+* C Checks:: C and C++ type and range checks
+@end ifset
+
+* Debugging C:: @value{GDBN} and C
+* Debugging C plus plus:: Special features for C++
+@end menu
+@end ifclear
+
+@ifclear CONLY
+@cindex C and C++ operators
+@node C Operators
+@subsubsection C and C++ operators
+@end ifclear
+@ifset CONLY
+@cindex C operators
+@node C Operators
+@section C operators
+@end ifset
+
+Operators must be defined on values of specific types. For instance,
+@code{+} is defined on numbers, but not on structures. Operators are
+often defined on groups of types.
+
+@ifclear CONLY
+For the purposes of C and C++, the following definitions hold:
+@end ifclear
+
+@itemize @bullet
+@item
+@emph{Integral types} include @code{int} with any of its storage-class
+specifiers; @code{char}; and @code{enum}.
+
+@item
+@emph{Floating-point types} include @code{float} and @code{double}.
+
+@item
+@emph{Pointer types} include all types defined as @code{(@var{type}
+*)}.
+
+@item
+@emph{Scalar types} include all of the above.
+@end itemize
+
+@noindent
+The following operators are supported. They are listed here
+in order of increasing precedence:
+
+@table @code
+@item ,
+The comma or sequencing operator. Expressions in a comma-separated list
+are evaluated from left to right, with the result of the entire
+expression being the last expression evaluated.
+
+@item =
+Assignment. The value of an assignment expression is the value
+assigned. Defined on scalar types.
+
+@item @var{op}=
+Used in an expression of the form @w{@code{@var{a} @var{op}= @var{b}}},
+and translated to @w{@code{@var{a} = @var{a op b}}}.
+@w{@code{@var{op}=}} and @code{=} have the same precendence.
+@var{op} is any one of the operators @code{|}, @code{^}, @code{&},
+@code{<<}, @code{>>}, @code{+}, @code{-}, @code{*}, @code{/}, @code{%}.
+
+@item ?:
+The ternary operator. @code{@var{a} ? @var{b} : @var{c}} can be thought
+of as: if @var{a} then @var{b} else @var{c}. @var{a} should be of an
+integral type.
+
+@item ||
+Logical @sc{or}. Defined on integral types.
+
+@item &&
+Logical @sc{and}. Defined on integral types.
+
+@item |
+Bitwise @sc{or}. Defined on integral types.
+
+@item ^
+Bitwise exclusive-@sc{or}. Defined on integral types.
+
+@item &
+Bitwise @sc{and}. Defined on integral types.
+
+@item ==@r{, }!=
+Equality and inequality. Defined on scalar types. The value of these
+expressions is 0 for false and non-zero for true.
+
+@item <@r{, }>@r{, }<=@r{, }>=
+Less than, greater than, less than or equal, greater than or equal.
+Defined on scalar types. The value of these expressions is 0 for false
+and non-zero for true.
+
+@item <<@r{, }>>
+left shift, and right shift. Defined on integral types.
+
+@item @@
+The @value{GDBN} ``artificial array'' operator (@pxref{Expressions, ,Expressions}).
+
+@item +@r{, }-
+Addition and subtraction. Defined on integral types, floating-point types and
+pointer types.
+
+@item *@r{, }/@r{, }%
+Multiplication, division, and modulus. Multiplication and division are
+defined on integral and floating-point types. Modulus is defined on
+integral types.
+
+@item ++@r{, }--
+Increment and decrement. When appearing before a variable, the
+operation is performed before the variable is used in an expression;
+when appearing after it, the variable's value is used before the
+operation takes place.
+
+@item *
+Pointer dereferencing. Defined on pointer types. Same precedence as
+@code{++}.
+
+@item &
+Address operator. Defined on variables. Same precedence as @code{++}.
+
+@ifclear CONLY
+For debugging C++, @value{GDBN} implements a use of @samp{&} beyond what is
+allowed in the C++ language itself: you can use @samp{&(&@var{ref})}
+(or, if you prefer, simply @samp{&&@var{ref}}) to examine the address
+where a C++ reference variable (declared with @samp{&@var{ref}}) is
+stored.
+@end ifclear
+
+@item -
+Negative. Defined on integral and floating-point types. Same
+precedence as @code{++}.
+
+@item !
+Logical negation. Defined on integral types. Same precedence as
+@code{++}.
+
+@item ~
+Bitwise complement operator. Defined on integral types. Same precedence as
+@code{++}.
+
+
+@item .@r{, }->
+Structure member, and pointer-to-structure member. For convenience,
+@value{GDBN} regards the two as equivalent, choosing whether to dereference a
+pointer based on the stored type information.
+Defined on @code{struct} and @code{union} data.
+
+@item []
+Array indexing. @code{@var{a}[@var{i}]} is defined as
+@code{*(@var{a}+@var{i})}. Same precedence as @code{->}.
+
+@item ()
+Function parameter list. Same precedence as @code{->}.
+
+@ifclear CONLY
+@item ::
+C++ scope resolution operator. Defined on
+@code{struct}, @code{union}, and @code{class} types.
+@end ifclear
+
+@item ::
+Doubled colons
+@ifclear CONLY
+also
+@end ifclear
+represent the @value{GDBN} scope operator (@pxref{Expressions,
+,Expressions}).
+@ifclear CONLY
+Same precedence as @code{::}, above.
+@end ifclear
+@end table
+
+@ifclear CONLY
+@cindex C and C++ constants
+@node C Constants
+@subsubsection C and C++ constants
+
+@value{GDBN} allows you to express the constants of C and C++ in the
+following ways:
+@end ifclear
+@ifset CONLY
+@cindex C constants
+@node C Constants
+@section C constants
+
+@value{GDBN} allows you to express the constants of C in the
+following ways:
+@end ifset
+
+@itemize @bullet
+@item
+Integer constants are a sequence of digits. Octal constants are
+specified by a leading @samp{0} (ie. zero), and hexadecimal constants by
+a leading @samp{0x} or @samp{0X}. Constants may also end with a letter
+@samp{l}, specifying that the constant should be treated as a
+@code{long} value.
+
+@item
+Floating point constants are a sequence of digits, followed by a decimal
+point, followed by a sequence of digits, and optionally followed by an
+exponent. An exponent is of the form:
+@samp{@w{e@r{[[}+@r{]|}-@r{]}@var{nnn}}}, where @var{nnn} is another
+sequence of digits. The @samp{+} is optional for positive exponents.
+
+@item
+Enumerated constants consist of enumerated identifiers, or their
+integral equivalents.
+
+@item
+Character constants are a single character surrounded by single quotes
+(@code{'}), or a number---the ordinal value of the corresponding character
+(usually its @sc{ASCII} value). Within quotes, the single character may
+be represented by a letter or by @dfn{escape sequences}, which are of
+the form @samp{\@var{nnn}}, where @var{nnn} is the octal representation
+of the character's ordinal value; or of the form @samp{\@var{x}}, where
+@samp{@var{x}} is a predefined special character---for example,
+@samp{\n} for newline.
+
+@item
+String constants are a sequence of character constants surrounded
+by double quotes (@code{"}).
+
+@item
+Pointer constants are an integral value. You can also write pointers
+to constants using the C operator @samp{&}.
+
+@item
+Array constants are comma-separated lists surrounded by braces @samp{@{}
+and @samp{@}}; for example, @samp{@{1,2,3@}} is a three-element array of
+integers, @samp{@{@{1,2@}, @{3,4@}, @{5,6@}@}} is a three-by-two array,
+and @samp{@{&"hi", &"there", &"fred"@}} is a three-element array of pointers.
+@end itemize
+
+@ifclear CONLY
+@node Cplus expressions
+@subsubsection C++ expressions
+
+@cindex expressions in C++
+@value{GDBN} expression handling has a number of extensions to
+interpret a significant subset of C++ expressions.
+
+@cindex C++ support, not in @sc{coff}
+@cindex @sc{coff} versus C++
+@cindex C++ and object formats
+@cindex object formats and C++
+@cindex a.out and C++
+@cindex @sc{ecoff} and C++
+@cindex @sc{xcoff} and C++
+@cindex @sc{elf}/stabs and C++
+@cindex @sc{elf}/@sc{dwarf} and C++
+@quotation
+@emph{Warning:} Most of these extensions depend on the use of additional
+debugging information in the symbol table, and thus require a rich,
+extendable object code format. In particular, if your system uses
+a.out, MIPS @sc{ecoff}, RS/6000 @sc{xcoff}, or Sun @sc{elf} with stabs
+extensions to the symbol table, these facilities are all available.
+Where the object code format is standard @sc{coff}, on the other hand,
+most of the C++ support in @value{GDBN} will @emph{not} work, nor can it.
+For the standard SVr4 debugging format, @sc{dwarf} in @sc{elf}, the
+standard is still evolving, so the C++ support in @value{GDBN} is still
+fragile; when this debugging format stabilizes, however, C++ support
+will also be available on systems that use it.
+@end quotation
+
+@enumerate
+
+@cindex member functions
+@item
+Member function calls are allowed; you can use expressions like
+
+@example
+count = aml->GetOriginal(x, y)
+@end example
+
+@kindex this
+@cindex namespace in C++
+@item
+While a member function is active (in the selected stack frame), your
+expressions have the same namespace available as the member function;
+that is, @value{GDBN} allows implicit references to the class instance
+pointer @code{this} following the same rules as C++.
+
+@cindex call overloaded functions
+@cindex type conversions in C++
+@item
+You can call overloaded functions; @value{GDBN} will resolve the function
+call to the right definition, with one restriction---you must use
+arguments of the type required by the function that you want to call.
+@value{GDBN} will not perform conversions requiring constructors or
+user-defined type operators.
+
+@cindex reference declarations
+@item
+@value{GDBN} understands variables declared as C++ references; you can use them in
+expressions just as you do in C++ source---they are automatically
+dereferenced.
+
+In the parameter list shown when @value{GDBN} displays a frame, the values of
+reference variables are not displayed (unlike other variables); this
+avoids clutter, since references are often used for large structures.
+The @emph{address} of a reference variable is always shown, unless
+you have specified @samp{set print address off}.
+
+@item
+@value{GDBN} supports the C++ name resolution operator @code{::}---your
+expressions can use it just as expressions in your program do. Since
+one scope may be defined in another, you can use @code{::} repeatedly if
+necessary, for example in an expression like
+@samp{@var{scope1}::@var{scope2}::@var{name}}. @value{GDBN} also allows
+resolving name scope by reference to source files, in both C and C++
+debugging (@pxref{Variables, ,Program variables}).
+@end enumerate
+
+@node C Defaults
+@subsubsection C and C++ defaults
+@cindex C and C++ defaults
+
+If you allow @value{GDBN} to set type and range checking automatically, they
+both default to @code{off} whenever the working language changes to
+C or C++. This happens regardless of whether you, or @value{GDBN},
+selected the working language.
+
+If you allow @value{GDBN} to set the language automatically, it sets the
+working language to C or C++ on entering code compiled from a source file
+whose name ends with @file{.c}, @file{.C}, or @file{.cc}.
+@xref{Automatically, ,Having @value{GDBN} infer the source language}, for
+further details.
+
+@ifset MOD2
+@c Type checking is (a) primarily motivated by Modula-2, and (b)
+@c unimplemented. If (b) changes, it might make sense to let this node
+@c appear even if Mod-2 does not, but meanwhile ignore it. pesch 16jul93.
+@node C Checks
+@subsubsection C and C++ type and range checks
+@cindex C and C++ checks
+
+By default, when @value{GDBN} parses C or C++ expressions, type checking
+is not used. However, if you turn type checking on, @value{GDBN} will
+consider two variables type equivalent if:
+
+@itemize @bullet
+@item
+The two variables are structured and have the same structure, union, or
+enumerated tag.
+
+@item
+Two two variables have the same type name, or types that have been
+declared equivalent through @code{typedef}.
+
+@ignore
+@c leaving this out because neither J Gilmore nor R Pesch understand it.
+@c FIXME--beers?
+@item
+The two @code{struct}, @code{union}, or @code{enum} variables are
+declared in the same declaration. (Note: this may not be true for all C
+compilers.)
+@end ignore
+@end itemize
+
+Range checking, if turned on, is done on mathematical operations. Array
+indices are not checked, since they are often used to index a pointer
+that is not itself an array.
+@end ifset
+@end ifclear
+
+@ifclear CONLY
+@node Debugging C
+@subsubsection @value{GDBN} and C
+@end ifclear
+@ifset CONLY
+@node Debugging C
+@section @value{GDBN} and C
+@end ifset
+
+The @code{set print union} and @code{show print union} commands apply to
+the @code{union} type. When set to @samp{on}, any @code{union} that is
+inside a @code{struct}
+@ifclear CONLY
+or @code{class}
+@end ifclear
+will also be printed.
+Otherwise, it will appear as @samp{@{...@}}.
+
+The @code{@@} operator aids in the debugging of dynamic arrays, formed
+with pointers and a memory allocation function. @xref{Expressions,
+,Expressions}.
+
+@ifclear CONLY
+@node Debugging C plus plus
+@subsubsection @value{GDBN} features for C++
+
+@cindex commands for C++
+Some @value{GDBN} commands are particularly useful with C++, and some are
+designed specifically for use with C++. Here is a summary:
+
+@table @code
+@cindex break in overloaded functions
+@item @r{breakpoint menus}
+When you want a breakpoint in a function whose name is overloaded,
+@value{GDBN} breakpoint menus help you specify which function definition
+you want. @xref{Breakpoint Menus,,Breakpoint menus}.
+
+@cindex overloading in C++
+@item rbreak @var{regex}
+Setting breakpoints using regular expressions is helpful for setting
+breakpoints on overloaded functions that are not members of any special
+classes.
+@xref{Set Breaks, ,Setting breakpoints}.
+
+@cindex C++ exception handling
+@item catch @var{exceptions}
+@itemx info catch
+Debug C++ exception handling using these commands. @xref{Exception
+Handling, ,Breakpoints and exceptions}.
+
+@cindex inheritance
+@item ptype @var{typename}
+Print inheritance relationships as well as other information for type
+@var{typename}.
+@xref{Symbols, ,Examining the Symbol Table}.
+
+@cindex C++ symbol display
+@item set print demangle
+@itemx show print demangle
+@itemx set print asm-demangle
+@itemx show print asm-demangle
+Control whether C++ symbols display in their source form, both when
+displaying code as C++ source and when displaying disassemblies.
+@xref{Print Settings, ,Print settings}.
+
+@item set print object
+@itemx show print object
+Choose whether to print derived (actual) or declared types of objects.
+@xref{Print Settings, ,Print settings}.
+
+@item set print vtbl
+@itemx show print vtbl
+Control the format for printing virtual function tables.
+@xref{Print Settings, ,Print settings}.
+
+@item @r{Overloaded symbol names}
+You can specify a particular definition of an overloaded symbol, using
+the same notation that is used to declare such symbols in C++: type
+@code{@var{symbol}(@var{types})} rather than just @var{symbol}. You can
+also use the @value{GDBN} command-line word completion facilities to list the
+available choices, or to finish the type list for you.
+@xref{Completion,, Command completion}, for details on how to do this.
+@end table
+@ifclear MOD2
+@c cancels "raisesections" under same conditions near bgn of chapter
+@lowersections
+@end ifclear
+
+@ifset MOD2
+@node Modula-2
+@subsection Modula-2
+@cindex Modula-2
+
+The extensions made to @value{GDBN} to support Modula-2 only support
+output from the GNU Modula-2 compiler (which is currently being
+developed). Other Modula-2 compilers are not currently supported, and
+attempting to debug executables produced by them will most likely
+result in an error as @value{GDBN} reads in the executable's symbol
+table.
+
+@cindex expressions in Modula-2
+@menu
+* M2 Operators:: Built-in operators
+* Built-In Func/Proc:: Built-in functions and procedures
+* M2 Constants:: Modula-2 constants
+* M2 Defaults:: Default settings for Modula-2
+* Deviations:: Deviations from standard Modula-2
+* M2 Checks:: Modula-2 type and range checks
+* M2 Scope:: The scope operators @code{::} and @code{.}
+* GDB/M2:: @value{GDBN} and Modula-2
+@end menu
+
+@node M2 Operators
+@subsubsection Operators
+@cindex Modula-2 operators
+
+Operators must be defined on values of specific types. For instance,
+@code{+} is defined on numbers, but not on structures. Operators are
+often defined on groups of types. For the purposes of Modula-2, the
+following definitions hold:
+
+@itemize @bullet
+
+@item
+@emph{Integral types} consist of @code{INTEGER}, @code{CARDINAL}, and
+their subranges.
+
+@item
+@emph{Character types} consist of @code{CHAR} and its subranges.
+
+@item
+@emph{Floating-point types} consist of @code{REAL}.
+
+@item
+@emph{Pointer types} consist of anything declared as @code{POINTER TO
+@var{type}}.
+
+@item
+@emph{Scalar types} consist of all of the above.
+
+@item
+@emph{Set types} consist of @code{SET} and @code{BITSET} types.
+
+@item
+@emph{Boolean types} consist of @code{BOOLEAN}.
+@end itemize
+
+@noindent
+The following operators are supported, and appear in order of
+increasing precedence:
+
+@table @code
+@item ,
+Function argument or array index separator.
+
+@item :=
+Assignment. The value of @var{var} @code{:=} @var{value} is
+@var{value}.
+
+@item <@r{, }>
+Less than, greater than on integral, floating-point, or enumerated
+types.
+
+@item <=@r{, }>=
+Less than, greater than, less than or equal to, greater than or equal to
+on integral, floating-point and enumerated types, or set inclusion on
+set types. Same precedence as @code{<}.
+
+@item =@r{, }<>@r{, }#
+Equality and two ways of expressing inequality, valid on scalar types.
+Same precedence as @code{<}. In @value{GDBN} scripts, only @code{<>} is
+available for inequality, since @code{#} conflicts with the script
+comment character.
+
+@item IN
+Set membership. Defined on set types and the types of their members.
+Same precedence as @code{<}.
+
+@item OR
+Boolean disjunction. Defined on boolean types.
+
+@item AND@r{, }&
+Boolean conjuction. Defined on boolean types.
+
+@item @@
+The @value{GDBN} ``artificial array'' operator (@pxref{Expressions, ,Expressions}).
+
+@item +@r{, }-
+Addition and subtraction on integral and floating-point types, or union
+and difference on set types.
+
+@item *
+Multiplication on integral and floating-point types, or set intersection
+on set types.
+
+@item /
+Division on floating-point types, or symmetric set difference on set
+types. Same precedence as @code{*}.
+
+@item DIV@r{, }MOD
+Integer division and remainder. Defined on integral types. Same
+precedence as @code{*}.
+
+@item -
+Negative. Defined on @code{INTEGER} and @code{REAL} data.
+
+@item ^
+Pointer dereferencing. Defined on pointer types.
+
+@item NOT
+Boolean negation. Defined on boolean types. Same precedence as
+@code{^}.
+
+@item .
+@code{RECORD} field selector. Defined on @code{RECORD} data. Same
+precedence as @code{^}.
+
+@item []
+Array indexing. Defined on @code{ARRAY} data. Same precedence as @code{^}.
+
+@item ()
+Procedure argument list. Defined on @code{PROCEDURE} objects. Same precedence
+as @code{^}.
+
+@item ::@r{, }.
+@value{GDBN} and Modula-2 scope operators.
+@end table
+
+@quotation
+@emph{Warning:} Sets and their operations are not yet supported, so @value{GDBN}
+will treat the use of the operator @code{IN}, or the use of operators
+@code{+}, @code{-}, @code{*}, @code{/}, @code{=}, , @code{<>}, @code{#},
+@code{<=}, and @code{>=} on sets as an error.
+@end quotation
+
+@cindex Modula-2 built-ins
+@node Built-In Func/Proc
+@subsubsection Built-in functions and procedures
+
+Modula-2 also makes available several built-in procedures and functions.
+In describing these, the following metavariables are used:
+
+@table @var
+
+@item a
+represents an @code{ARRAY} variable.
+
+@item c
+represents a @code{CHAR} constant or variable.
+
+@item i
+represents a variable or constant of integral type.
+
+@item m
+represents an identifier that belongs to a set. Generally used in the
+same function with the metavariable @var{s}. The type of @var{s} should
+be @code{SET OF @var{mtype}} (where @var{mtype} is the type of @var{m}).
+
+@item n
+represents a variable or constant of integral or floating-point type.
+
+@item r
+represents a variable or constant of floating-point type.
+
+@item t
+represents a type.
+
+@item v
+represents a variable.
+
+@item x
+represents a variable or constant of one of many types. See the
+explanation of the function for details.
+@end table
+
+All Modula-2 built-in procedures also return a result, described below.
+
+@table @code
+@item ABS(@var{n})
+Returns the absolute value of @var{n}.
+
+@item CAP(@var{c})
+If @var{c} is a lower case letter, it returns its upper case
+equivalent, otherwise it returns its argument
+
+@item CHR(@var{i})
+Returns the character whose ordinal value is @var{i}.
+
+@item DEC(@var{v})
+Decrements the value in the variable @var{v}. Returns the new value.
+
+@item DEC(@var{v},@var{i})
+Decrements the value in the variable @var{v} by @var{i}. Returns the
+new value.
+
+@item EXCL(@var{m},@var{s})
+Removes the element @var{m} from the set @var{s}. Returns the new
+set.
+
+@item FLOAT(@var{i})
+Returns the floating point equivalent of the integer @var{i}.
+
+@item HIGH(@var{a})
+Returns the index of the last member of @var{a}.
+
+@item INC(@var{v})
+Increments the value in the variable @var{v}. Returns the new value.
+
+@item INC(@var{v},@var{i})
+Increments the value in the variable @var{v} by @var{i}. Returns the
+new value.
+
+@item INCL(@var{m},@var{s})
+Adds the element @var{m} to the set @var{s} if it is not already
+there. Returns the new set.
+
+@item MAX(@var{t})
+Returns the maximum value of the type @var{t}.
+
+@item MIN(@var{t})
+Returns the minimum value of the type @var{t}.
+
+@item ODD(@var{i})
+Returns boolean TRUE if @var{i} is an odd number.
+
+@item ORD(@var{x})
+Returns the ordinal value of its argument. For example, the ordinal
+value of a character is its ASCII value (on machines supporting the
+ASCII character set). @var{x} must be of an ordered type, which include
+integral, character and enumerated types.
+
+@item SIZE(@var{x})
+Returns the size of its argument. @var{x} can be a variable or a type.
+
+@item TRUNC(@var{r})
+Returns the integral part of @var{r}.
+
+@item VAL(@var{t},@var{i})
+Returns the member of the type @var{t} whose ordinal value is @var{i}.
+@end table
+
+@quotation
+@emph{Warning:} Sets and their operations are not yet supported, so
+@value{GDBN} will treat the use of procedures @code{INCL} and @code{EXCL} as
+an error.
+@end quotation
+
+@cindex Modula-2 constants
+@node M2 Constants
+@subsubsection Constants
+
+@value{GDBN} allows you to express the constants of Modula-2 in the following
+ways:
+
+@itemize @bullet
+
+@item
+Integer constants are simply a sequence of digits. When used in an
+expression, a constant is interpreted to be type-compatible with the
+rest of the expression. Hexadecimal integers are specified by a
+trailing @samp{H}, and octal integers by a trailing @samp{B}.
+
+@item
+Floating point constants appear as a sequence of digits, followed by a
+decimal point and another sequence of digits. An optional exponent can
+then be specified, in the form @samp{E@r{[}+@r{|}-@r{]}@var{nnn}}, where
+@samp{@r{[}+@r{|}-@r{]}@var{nnn}} is the desired exponent. All of the
+digits of the floating point constant must be valid decimal (base 10)
+digits.
+
+@item
+Character constants consist of a single character enclosed by a pair of
+like quotes, either single (@code{'}) or double (@code{"}). They may
+also be expressed by their ordinal value (their ASCII value, usually)
+followed by a @samp{C}.
+
+@item
+String constants consist of a sequence of characters enclosed by a
+pair of like quotes, either single (@code{'}) or double (@code{"}).
+Escape sequences in the style of C are also allowed. @xref{C
+Constants, ,C and C++ constants}, for a brief explanation of escape
+sequences.
+
+@item
+Enumerated constants consist of an enumerated identifier.
+
+@item
+Boolean constants consist of the identifiers @code{TRUE} and
+@code{FALSE}.
+
+@item
+Pointer constants consist of integral values only.
+
+@item
+Set constants are not yet supported.
+@end itemize
+
+@node M2 Defaults
+@subsubsection Modula-2 defaults
+@cindex Modula-2 defaults
+
+If type and range checking are set automatically by @value{GDBN}, they
+both default to @code{on} whenever the working language changes to
+Modula-2. This happens regardless of whether you, or @value{GDBN},
+selected the working language.
+
+If you allow @value{GDBN} to set the language automatically, then entering
+code compiled from a file whose name ends with @file{.mod} will set the
+working language to Modula-2. @xref{Automatically, ,Having @value{GDBN} set
+the language automatically}, for further details.
+
+@node Deviations
+@subsubsection Deviations from standard Modula-2
+@cindex Modula-2, deviations from
+
+A few changes have been made to make Modula-2 programs easier to debug.
+This is done primarily via loosening its type strictness:
+
+@itemize @bullet
+@item
+Unlike in standard Modula-2, pointer constants can be formed by
+integers. This allows you to modify pointer variables during
+debugging. (In standard Modula-2, the actual address contained in a
+pointer variable is hidden from you; it can only be modified
+through direct assignment to another pointer variable or expression that
+returned a pointer.)
+
+@item
+C escape sequences can be used in strings and characters to represent
+non-printable characters. @value{GDBN} will print out strings with these
+escape sequences embedded. Single non-printable characters are
+printed using the @samp{CHR(@var{nnn})} format.
+
+@item
+The assignment operator (@code{:=}) returns the value of its right-hand
+argument.
+
+@item
+All built-in procedures both modify @emph{and} return their argument.
+@end itemize
+
+@node M2 Checks
+@subsubsection Modula-2 type and range checks
+@cindex Modula-2 checks
+
+@quotation
+@emph{Warning:} in this release, @value{GDBN} does not yet perform type or
+range checking.
+@end quotation
+@c FIXME remove warning when type/range checks added
+
+@value{GDBN} considers two Modula-2 variables type equivalent if:
+
+@itemize @bullet
+@item
+They are of types that have been declared equivalent via a @code{TYPE
+@var{t1} = @var{t2}} statement
+
+@item
+They have been declared on the same line. (Note: This is true of the
+GNU Modula-2 compiler, but it may not be true of other compilers.)
+@end itemize
+
+As long as type checking is enabled, any attempt to combine variables
+whose types are not equivalent is an error.
+
+Range checking is done on all mathematical operations, assignment, array
+index bounds, and all built-in functions and procedures.
+
+@node M2 Scope
+@subsubsection The scope operators @code{::} and @code{.}
+@cindex scope
+@kindex .
+@cindex colon, doubled as scope operator
+@ifinfo
+@kindex colon-colon
+@c Info cannot handle :: but TeX can.
+@end ifinfo
+@iftex
+@kindex ::
+@end iftex
+
+There are a few subtle differences between the Modula-2 scope operator
+(@code{.}) and the @value{GDBN} scope operator (@code{::}). The two have
+similar syntax:
+
+@example
+
+@var{module} . @var{id}
+@var{scope} :: @var{id}
+@end example
+
+@noindent
+where @var{scope} is the name of a module or a procedure,
+@var{module} the name of a module, and @var{id} is any declared
+identifier within your program, except another module.
+
+Using the @code{::} operator makes @value{GDBN} search the scope
+specified by @var{scope} for the identifier @var{id}. If it is not
+found in the specified scope, then @value{GDBN} will search all scopes
+enclosing the one specified by @var{scope}.
+
+Using the @code{.} operator makes @value{GDBN} search the current scope for
+the identifier specified by @var{id} that was imported from the
+definition module specified by @var{module}. With this operator, it is
+an error if the identifier @var{id} was not imported from definition
+module @var{module}, or if @var{id} is not an identifier in
+@var{module}.
+
+@node GDB/M2
+@subsubsection @value{GDBN} and Modula-2
+
+Some @value{GDBN} commands have little use when debugging Modula-2 programs.
+Five subcommands of @code{set print} and @code{show print} apply
+specifically to C and C++: @samp{vtbl}, @samp{demangle},
+@samp{asm-demangle}, @samp{object}, and @samp{union}. The first four
+apply to C++, and the last to the C @code{union} type, which has no direct
+analogue in Modula-2.
+
+The @code{@@} operator (@pxref{Expressions, ,Expressions}), while available
+while using any language, is not useful with Modula-2. Its
+intent is to aid the debugging of @dfn{dynamic arrays}, which cannot be
+created in Modula-2 as they can in C or C++. However, because an
+address can be specified by an integral constant, the construct
+@samp{@{@var{type}@}@var{adrexp}} is still useful. (@pxref{Expressions, ,Expressions})
+
+@cindex @code{#} in Modula-2
+In @value{GDBN} scripts, the Modula-2 inequality operator @code{#} is
+interpreted as the beginning of a comment. Use @code{<>} instead.
+
+@end ifset
+@end ifclear
+
+@node Symbols
+@chapter Examining the Symbol Table
+
+The commands described in this section allow you to inquire about the
+symbols (names of variables, functions and types) defined in your
+program. This information is inherent in the text of your program and
+does not change as your program executes. @value{GDBN} finds it in your
+program's symbol table, in the file indicated when you started @value{GDBN}
+(@pxref{File Options, ,Choosing files}), or by one of the
+file-management commands (@pxref{Files, ,Commands to specify files}).
+
+@c FIXME! This might be intentionally specific to C and C++; if so, move
+@c to someplace in C section of lang chapter.
+@cindex symbol names
+@cindex names of symbols
+@cindex quoting names
+Occasionally, you may need to refer to symbols that contain unusual
+characters, which @value{GDBN} ordinarily treats as word delimiters. The
+most frequent case is in referring to static variables in other
+source files (@pxref{Variables,,Program variables}). File names
+are recorded in object files as debugging symbols, but @value{GDBN} would
+ordinarily parse a typical file name, like @file{foo.c}, as the three words
+@samp{foo} @samp{.} @samp{c}. To allow @value{GDBN} to recognize
+@samp{foo.c} as a single symbol, enclose it in single quotes; for example,
+
+@example
+p 'foo.c'::x
+@end example
+
+@noindent
+looks up the value of @code{x} in the scope of the file @file{foo.c}.
+
+@table @code
+@item info address @var{symbol}
+@kindex info address
+Describe where the data for @var{symbol} is stored. For a register
+variable, this says which register it is kept in. For a non-register
+local variable, this prints the stack-frame offset at which the variable
+is always stored.
+
+Note the contrast with @samp{print &@var{symbol}}, which does not work
+at all for a register variable, and for a stack local variable prints
+the exact address of the current instantiation of the variable.
+
+@item whatis @var{exp}
+@kindex whatis
+Print the data type of expression @var{exp}. @var{exp} is not
+actually evaluated, and any side-effecting operations (such as
+assignments or function calls) inside it do not take place.
+@xref{Expressions, ,Expressions}.
+
+@item whatis
+Print the data type of @code{$}, the last value in the value history.
+
+@item ptype @var{typename}
+@kindex ptype
+Print a description of data type @var{typename}. @var{typename} may be
+the name of a type, or for C code it may have the form
+@ifclear CONLY
+@samp{class @var{class-name}},
+@end ifclear
+@samp{struct @var{struct-tag}}, @samp{union @var{union-tag}} or
+@samp{enum @var{enum-tag}}.
+
+@item ptype @var{exp}
+@itemx ptype
+Print a description of the type of expression @var{exp}. @code{ptype}
+differs from @code{whatis} by printing a detailed description, instead
+of just the name of the type.
+
+For example, for this variable declaration:
+
+@example
+struct complex @{double real; double imag;@} v;
+@end example
+
+@noindent
+the two commands give this output:
+
+@example
+@group
+(@value{GDBP}) whatis v
+type = struct complex
+(@value{GDBP}) ptype v
+type = struct complex @{
+ double real;
+ double imag;
+@}
+@end group
+@end example
+
+@noindent
+As with @code{whatis}, using @code{ptype} without an argument refers to
+the type of @code{$}, the last value in the value history.
+
+@item info types @var{regexp}
+@itemx info types
+@kindex info types
+Print a brief description of all types whose name matches @var{regexp}
+(or all types in your program, if you supply no argument). Each
+complete typename is matched as though it were a complete line; thus,
+@samp{i type value} gives information on all types in your program whose
+name includes the string @code{value}, but @samp{i type ^value$} gives
+information only on types whose complete name is @code{value}.
+
+This command differs from @code{ptype} in two ways: first, like
+@code{whatis}, it does not print a detailed description; second, it
+lists all source files where a type is defined.
+
+@item info source
+@kindex info source
+Show the name of the current source file---that is, the source file for
+the function containing the current point of execution---and the language
+it was written in.
+
+@item info sources
+@kindex info sources
+Print the names of all source files in your program for which there is
+debugging information, organized into two lists: files whose symbols
+have already been read, and files whose symbols will be read when needed.
+
+@item info functions
+@kindex info functions
+Print the names and data types of all defined functions.
+
+@item info functions @var{regexp}
+Print the names and data types of all defined functions
+whose names contain a match for regular expression @var{regexp}.
+Thus, @samp{info fun step} finds all functions whose names
+include @code{step}; @samp{info fun ^step} finds those whose names
+start with @code{step}.
+
+@item info variables
+@kindex info variables
+Print the names and data types of all variables that are declared
+outside of functions (i.e., excluding local variables).
+
+@item info variables @var{regexp}
+Print the names and data types of all variables (except for local
+variables) whose names contain a match for regular expression
+@var{regexp}.
+
+@ignore
+This was never implemented.
+@item info methods
+@itemx info methods @var{regexp}
+@kindex info methods
+The @code{info methods} command permits the user to examine all defined
+methods within C++ program, or (with the @var{regexp} argument) a
+specific set of methods found in the various C++ classes. Many
+C++ classes provide a large number of methods. Thus, the output
+from the @code{ptype} command can be overwhelming and hard to use. The
+@code{info-methods} command filters the methods, printing only those
+which match the regular-expression @var{regexp}.
+@end ignore
+
+@item maint print symbols @var{filename}
+@itemx maint print psymbols @var{filename}
+@itemx maint print msymbols @var{filename}
+@kindex maint print symbols
+@cindex symbol dump
+@kindex maint print psymbols
+@cindex partial symbol dump
+Write a dump of debugging symbol data into the file @var{filename}.
+These commands are used to debug the @value{GDBN} symbol-reading code. Only
+symbols with debugging data are included. If you use @samp{maint print
+symbols}, @value{GDBN} includes all the symbols for which it has already
+collected full details: that is, @var{filename} reflects symbols for
+only those files whose symbols @value{GDBN} has read. You can use the
+command @code{info sources} to find out which files these are. If you
+use @samp{maint print psymbols} instead, the dump shows information about
+symbols that @value{GDBN} only knows partially---that is, symbols defined in
+files that @value{GDBN} has skimmed, but not yet read completely. Finally,
+@samp{maint print msymbols} dumps just the minimal symbol information
+required for each object file from which @value{GDBN} has read some symbols.
+@xref{Files, ,Commands to specify files}, for a discussion of how
+@value{GDBN} reads symbols (in the description of @code{symbol-file}).
+@end table
+
+@node Altering
+@chapter Altering Execution
+
+Once you think you have found an error in your program, you might want to
+find out for certain whether correcting the apparent error would lead to
+correct results in the rest of the run. You can find the answer by
+experiment, using the @value{GDBN} features for altering execution of the
+program.
+
+For example, you can store new values into variables or memory
+locations,
+@ifclear BARETARGET
+give your program a signal, restart it
+@end ifclear
+@ifset BARETARGET
+restart your program
+@end ifset
+at a different address, or even return prematurely from a function to
+its caller.
+
+@menu
+* Assignment:: Assignment to variables
+* Jumping:: Continuing at a different address
+@ifclear BARETARGET
+* Signaling:: Giving your program a signal
+@end ifclear
+
+* Returning:: Returning from a function
+* Calling:: Calling your program's functions
+* Patching:: Patching your program
+@end menu
+
+@node Assignment
+@section Assignment to variables
+
+@cindex assignment
+@cindex setting variables
+To alter the value of a variable, evaluate an assignment expression.
+@xref{Expressions, ,Expressions}. For example,
+
+@example
+print x=4
+@end example
+
+@noindent
+stores the value 4 into the variable @code{x}, and then prints the
+value of the assignment expression (which is 4).
+@ifclear CONLY
+@xref{Languages, ,Using @value{GDBN} with Different Languages}, for more
+information on operators in supported languages.
+@end ifclear
+
+@kindex set variable
+@cindex variables, setting
+If you are not interested in seeing the value of the assignment, use the
+@code{set} command instead of the @code{print} command. @code{set} is
+really the same as @code{print} except that the expression's value is
+not printed and is not put in the value history (@pxref{Value History,
+,Value history}). The expression is evaluated only for its effects.
+
+If the beginning of the argument string of the @code{set} command
+appears identical to a @code{set} subcommand, use the @code{set
+variable} command instead of just @code{set}. This command is identical
+to @code{set} except for its lack of subcommands. For example, if
+your program has a variable @code{width}, you get
+an error if you try to set a new value with just @samp{set width=13},
+because @value{GDBN} has the command @code{set width}:
+
+@example
+(@value{GDBP}) whatis width
+type = double
+(@value{GDBP}) p width
+$4 = 13
+(@value{GDBP}) set width=47
+Invalid syntax in expression.
+@end example
+
+@noindent
+The invalid expression, of course, is @samp{=47}. In
+order to actually set the program's variable @code{width}, use
+
+@example
+(@value{GDBP}) set var width=47
+@end example
+
+@value{GDBN} allows more implicit conversions in assignments than C; you can
+freely store an integer value into a pointer variable or vice versa,
+and you can convert any structure to any other structure that is the
+same length or shorter.
+@comment FIXME: how do structs align/pad in these conversions?
+@comment /pesch@cygnus.com 18dec1990
+
+To store values into arbitrary places in memory, use the @samp{@{@dots{}@}}
+construct to generate a value of specified type at a specified address
+(@pxref{Expressions, ,Expressions}). For example, @code{@{int@}0x83040} refers
+to memory location @code{0x83040} as an integer (which implies a certain size
+and representation in memory), and
+
+@example
+set @{int@}0x83040 = 4
+@end example
+
+@noindent
+stores the value 4 into that memory location.
+
+@node Jumping
+@section Continuing at a different address
+
+Ordinarily, when you continue your program, you do so at the place where
+it stopped, with the @code{continue} command. You can instead continue at
+an address of your own choosing, with the following commands:
+
+@table @code
+@item jump @var{linespec}
+@kindex jump
+Resume execution at line @var{linespec}. Execution will stop
+immediately if there is a breakpoint there. @xref{List, ,Printing
+source lines}, for a description of the different forms of
+@var{linespec}.
+
+The @code{jump} command does not change the current stack frame, or
+the stack pointer, or the contents of any memory location or any
+register other than the program counter. If line @var{linespec} is in
+a different function from the one currently executing, the results may
+be bizarre if the two functions expect different patterns of arguments or
+of local variables. For this reason, the @code{jump} command requests
+confirmation if the specified line is not in the function currently
+executing. However, even bizarre results are predictable if you are
+well acquainted with the machine-language code of your program.
+
+@item jump *@var{address}
+Resume execution at the instruction at address @var{address}.
+@end table
+
+You can get much the same effect as the @code{jump} command by storing a
+new value into the register @code{$pc}. The difference is that this
+does not start your program running; it only changes the address where it
+@emph{will} run when it is continued. For example,
+
+@example
+set $pc = 0x485
+@end example
+
+@noindent
+causes the next @code{continue} command or stepping command to execute at
+address @code{0x485}, rather than at the address where your program stopped.
+@xref{Continuing and Stepping, ,Continuing and stepping}.
+
+The most common occasion to use the @code{jump} command is to back up,
+perhaps with more breakpoints set, over a portion of a program that has
+already executed, in order to examine its execution in more detail.
+
+@ifclear BARETARGET
+@c @group
+@node Signaling
+@section Giving your program a signal
+
+@table @code
+@item signal @var{signal}
+@kindex signal
+Resume execution where your program stopped, but immediately give it the
+signal @var{signal}. @var{signal} can be the name or the number of a
+signal. For example, on many systems @code{signal 2} and @code{signal
+SIGINT} are both ways of sending an interrupt signal.
+
+Alternatively, if @var{signal} is zero, continue execution without
+giving a signal. This is useful when your program stopped on account of
+a signal and would ordinary see the signal when resumed with the
+@code{continue} command; @samp{signal 0} causes it to resume without a
+signal.
+
+@code{signal} does not repeat when you press @key{RET} a second time
+after executing the command.
+@end table
+@c @end group
+
+Invoking the @code{signal} command is not the same as invoking the
+@code{kill} utility from the shell. Sending a signal with @code{kill}
+causes @value{GDBN} to decide what to do with the signal depending on
+the signal handling tables (@pxref{Signals}). The @code{signal} command
+passes the signal directly to your program.
+
+@end ifclear
+
+@node Returning
+@section Returning from a function
+
+@table @code
+@item return
+@itemx return @var{expression}
+@cindex returning from a function
+@kindex return
+You can cancel execution of a function call with the @code{return}
+command. If you give an
+@var{expression} argument, its value is used as the function's return
+value.
+@end table
+
+When you use @code{return}, @value{GDBN} discards the selected stack frame
+(and all frames within it). You can think of this as making the
+discarded frame return prematurely. If you wish to specify a value to
+be returned, give that value as the argument to @code{return}.
+
+This pops the selected stack frame (@pxref{Selection, ,Selecting a
+frame}), and any other frames inside of it, leaving its caller as the
+innermost remaining frame. That frame becomes selected. The
+specified value is stored in the registers used for returning values
+of functions.
+
+The @code{return} command does not resume execution; it leaves the
+program stopped in the state that would exist if the function had just
+returned. In contrast, the @code{finish} command (@pxref{Continuing
+and Stepping, ,Continuing and stepping}) resumes execution until the
+selected stack frame returns naturally.
+
+@node Calling
+@section Calling program functions
+
+@cindex calling functions
+@kindex call
+@table @code
+@item call @var{expr}
+Evaluate the expression @var{expr} without displaying @code{void}
+returned values.
+@end table
+
+You can use this variant of the @code{print} command if you want to
+execute a function from your program, but without cluttering the output
+with @code{void} returned values. The result is printed and saved in
+the value history, if it is not void.
+
+@node Patching
+@section Patching programs
+@cindex patching binaries
+@cindex writing into executables
+@ifclear BARETARGET
+@cindex writing into corefiles
+@end ifclear
+
+By default, @value{GDBN} opens the file containing your program's executable
+code
+@ifclear BARETARGET
+(or the corefile)
+@end ifclear
+read-only. This prevents accidental alterations
+to machine code; but it also prevents you from intentionally patching
+your program's binary.
+
+If you'd like to be able to patch the binary, you can specify that
+explicitly with the @code{set write} command. For example, you might
+want to turn on internal debugging flags, or even to make emergency
+repairs.
+
+@table @code
+@item set write on
+@itemx set write off
+@kindex set write
+If you specify @samp{set write on}, @value{GDBN} will open executable
+@ifclear BARETARGET
+and core
+@end ifclear
+files for both reading and writing; if you specify @samp{set write
+off} (the default), @value{GDBN} will open them read-only.
+
+If you have already loaded a file, you must load it again (using the
+@code{exec-file}
+@ifclear BARETARGET
+or @code{core-file}
+@end ifclear
+command) after changing @code{set write}, for your new setting to take
+effect.
+
+@item show write
+@kindex show write
+Display whether executable files
+@ifclear BARETARGET
+and core files
+@end ifclear
+will be opened for writing as well as reading.
+@end table
+
+@node GDB Files
+@chapter @value{GDBN} Files
+
+@value{GDBN} needs to know the file name of the program to be debugged, both in
+order to read its symbol table and in order to start your program.
+@ifclear BARETARGET
+To debug a core dump of a previous run, you must also tell @value{GDBN}
+the name of the core dump file.
+@end ifclear
+
+@menu
+* Files:: Commands to specify files
+* Symbol Errors:: Errors reading symbol files
+@end menu
+
+@node Files
+@section Commands to specify files
+@cindex symbol table
+
+@ifclear BARETARGET
+@cindex core dump file
+The usual way to specify executable and core dump file names is with
+the command arguments given when you start @value{GDBN} (@pxref{Invocation,
+,Getting In and Out of @value{GDBN}}.
+@end ifclear
+@ifset BARETARGET
+The usual way to specify an executable file name is with
+the command argument given when you start @value{GDBN}, (@pxref{Invocation,
+,Getting In and Out of @value{GDBN}}.
+@end ifset
+
+Occasionally it is necessary to change to a different file during a
+@value{GDBN} session. Or you may run @value{GDBN} and forget to specify
+a file you want to use. In these situations the @value{GDBN} commands
+to specify new files are useful.
+
+@table @code
+@item file @var{filename}
+@cindex executable file
+@kindex file
+Use @var{filename} as the program to be debugged. It is read for its
+symbols and for the contents of pure memory. It is also the program
+executed when you use the @code{run} command. If you do not specify a
+directory and the file is not found in the @value{GDBN} working directory, @value{GDBN}
+uses the environment variable @code{PATH} as a list of directories to
+search, just as the shell does when looking for a program to run. You
+can change the value of this variable, for both @value{GDBN} and your program,
+using the @code{path} command.
+
+On systems with memory-mapped files, an auxiliary symbol table file
+@file{@var{filename}.syms} may be available for @var{filename}. If it
+is, @value{GDBN} will map in the symbol table from
+@file{@var{filename}.syms}, starting up more quickly. See the
+descriptions of the options @samp{-mapped} and @samp{-readnow} (available
+on the command line, and with the commands @code{file}, @code{symbol-file},
+or @code{add-symbol-file}), for more information.
+
+@item file
+@code{file} with no argument makes @value{GDBN} discard any information it
+has on both executable file and the symbol table.
+
+@item exec-file @r{[} @var{filename} @r{]}
+@kindex exec-file
+Specify that the program to be run (but not the symbol table) is found
+in @var{filename}. @value{GDBN} will search the environment variable @code{PATH}
+if necessary to locate your program. Omitting @var{filename} means to
+discard information on the executable file.
+
+@item symbol-file @r{[} @var{filename} @r{]}
+@kindex symbol-file
+Read symbol table information from file @var{filename}. @code{PATH} is
+searched when necessary. Use the @code{file} command to get both symbol
+table and program to run from the same file.
+
+@code{symbol-file} with no argument clears out @value{GDBN} information on your
+program's symbol table.
+
+The @code{symbol-file} command causes @value{GDBN} to forget the contents of its
+convenience variables, the value history, and all breakpoints and
+auto-display expressions. This is because they may contain pointers to
+the internal data recording symbols and data types, which are part of
+the old symbol table data being discarded inside @value{GDBN}.
+
+@code{symbol-file} will not repeat if you press @key{RET} again after
+executing it once.
+
+When @value{GDBN} is configured for a particular environment, it will
+understand debugging information in whatever format is the standard
+generated for that environment; you may use either a GNU compiler, or
+other compilers that adhere to the local conventions. Best results are
+usually obtained from GNU compilers; for example, using @code{@value{GCC}}
+you can generate debugging information for optimized code.
+
+On some kinds of object files, the @code{symbol-file} command does not
+normally read the symbol table in full right away. Instead, it scans
+the symbol table quickly to find which source files and which symbols
+are present. The details are read later, one source file at a time,
+as they are needed.
+
+The purpose of this two-stage reading strategy is to make @value{GDBN} start up
+faster. For the most part, it is invisible except for occasional
+pauses while the symbol table details for a particular source file are
+being read. (The @code{set verbose} command can turn these pauses
+into messages if desired. @xref{Messages/Warnings, ,Optional warnings
+and messages}.)
+
+We have not implemented the two-stage strategy for COFF yet. When the
+symbol table is stored in COFF format, @code{symbol-file} reads the
+symbol table data in full right away.
+
+@item symbol-file @var{filename} @r{[} -readnow @r{]} @r{[} -mapped @r{]}
+@itemx file @var{filename} @r{[} -readnow @r{]} @r{[} -mapped @r{]}
+@kindex readnow
+@cindex reading symbols immediately
+@cindex symbols, reading immediately
+@kindex mapped
+@cindex memory-mapped symbol file
+@cindex saving symbol table
+You can override the @value{GDBN} two-stage strategy for reading symbol
+tables by using the @samp{-readnow} option with any of the commands that
+load symbol table information, if you want to be sure @value{GDBN} has the
+entire symbol table available.
+
+@ifclear BARETARGET
+If memory-mapped files are available on your system through the
+@code{mmap} system call, you can use another option, @samp{-mapped}, to
+cause @value{GDBN} to write the symbols for your program into a reusable
+file. Future @value{GDBN} debugging sessions will map in symbol information
+from this auxiliary symbol file (if the program has not changed), rather
+than spending time reading the symbol table from the executable
+program. Using the @samp{-mapped} option has the same effect as
+starting @value{GDBN} with the @samp{-mapped} command-line option.
+
+You can use both options together, to make sure the auxiliary symbol
+file has all the symbol information for your program.
+
+The auxiliary symbol file for a program called @var{myprog} is called
+@samp{@var{myprog}.syms}. Once this file exists (so long as it is newer
+than the corresponding executable), @value{GDBN} will always attempt to use
+it when you debug @var{myprog}; no special options or commands are
+needed.
+
+The @file{.syms} file is specific to the host machine where you run
+@value{GDBN}. It holds an exact image of the internal @value{GDBN}
+symbol table. It cannot be shared across multiple host platforms.
+
+@c FIXME: for now no mention of directories, since this seems to be in
+@c flux. 13mar1992 status is that in theory GDB would look either in
+@c current dir or in same dir as myprog; but issues like competing
+@c GDB's, or clutter in system dirs, mean that in practice right now
+@c only current dir is used. FFish says maybe a special GDB hierarchy
+@c (eg rooted in val of env var GDBSYMS) could exist for mappable symbol
+@c files.
+
+@item core-file @r{[} @var{filename} @r{]}
+@kindex core
+@kindex core-file
+Specify the whereabouts of a core dump file to be used as the ``contents
+of memory''. Traditionally, core files contain only some parts of the
+address space of the process that generated them; @value{GDBN} can access the
+executable file itself for other parts.
+
+@code{core-file} with no argument specifies that no core file is
+to be used.
+
+Note that the core file is ignored when your program is actually running
+under @value{GDBN}. So, if you have been running your program and you wish to
+debug a core file instead, you must kill the subprocess in which the
+program is running. To do this, use the @code{kill} command
+(@pxref{Kill Process, ,Killing the child process}).
+@end ifclear
+
+@item load @var{filename}
+@kindex load
+@ifset GENERIC
+Depending on what remote debugging facilities are configured into
+@value{GDBN}, the @code{load} command may be available. Where it exists, it
+is meant to make @var{filename} (an executable) available for debugging
+on the remote system---by downloading, or dynamic linking, for example.
+@code{load} also records the @var{filename} symbol table in @value{GDBN}, like
+the @code{add-symbol-file} command.
+
+If your @value{GDBN} does not have a @code{load} command, attempting to
+execute it gets the error message ``@code{You can't do that when your
+target is @dots{}}''
+@end ifset
+
+The file is loaded at whatever address is specified in the executable.
+For some object file formats, like a.out, the object file format fixes
+the address and so it won't necessarily match the address you gave to
+the linker.
+
+@ifset VXWORKS
+On VxWorks, @code{load} will dynamically link @var{filename} on the
+current target system as well as adding its symbols in @value{GDBN}.
+@end ifset
+
+@ifset I960
+@cindex download to Nindy-960
+With the Nindy interface to an Intel 960 board, @code{load} will
+download @var{filename} to the 960 as well as adding its symbols in
+@value{GDBN}.
+@end ifset
+
+@ifset H8
+@cindex download to H8/300 or H8/500
+@cindex H8/300 or H8/500 download
+@cindex download to Hitachi SH
+@cindex Hitachi SH download
+When you select remote debugging to a Hitachi SH, H8/300, or H8/500 board
+(@pxref{Hitachi Remote,,@value{GDBN} and Hitachi Microprocessors}),
+the @code{load} command downloads your program to the Hitachi board and also
+opens it as the current executable target for @value{GDBN} on your host
+(like the @code{file} command).
+@end ifset
+
+@code{load} will not repeat if you press @key{RET} again after using it.
+
+@ifclear BARETARGET
+@item add-symbol-file @var{filename} @var{address}
+@itemx add-symbol-file @var{filename} @var{address} @r{[} -readnow @r{]} @r{[} -mapped @r{]}
+@kindex add-symbol-file
+@cindex dynamic linking
+The @code{add-symbol-file} command reads additional symbol table information
+from the file @var{filename}. You would use this command when @var{filename}
+has been dynamically loaded (by some other means) into the program that
+is running. @var{address} should be the memory address at which the
+file has been loaded; @value{GDBN} cannot figure this out for itself.
+You can specify @var{address} as an expression.
+
+The symbol table of the file @var{filename} is added to the symbol table
+originally read with the @code{symbol-file} command. You can use the
+@code{add-symbol-file} command any number of times; the new symbol data thus
+read keeps adding to the old. To discard all old symbol data instead,
+use the @code{symbol-file} command.
+
+@code{add-symbol-file} will not repeat if you press @key{RET} after using it.
+
+You can use the @samp{-mapped} and @samp{-readnow} options just as with
+the @code{symbol-file} command, to change how @value{GDBN} manages the symbol
+table information for @var{filename}.
+@end ifclear
+
+@item info files
+@itemx info target
+@kindex info files
+@kindex info target
+@code{info files} and @code{info target} are synonymous; both print
+the current target (@pxref{Targets, ,Specifying a Debugging Target}),
+including the
+@ifclear BARETARGET
+names of the executable and core dump files
+@end ifclear
+@ifset BARETARGET
+name of the executable file
+@end ifset
+currently in use by @value{GDBN}, and the files from which symbols were
+loaded. The command @code{help targets} lists all possible targets
+rather than current ones.
+@end table
+
+All file-specifying commands allow both absolute and relative file names
+as arguments. @value{GDBN} always converts the file name to an absolute path
+name and remembers it that way.
+
+@ifclear BARETARGET
+@cindex shared libraries
+@value{GDBN} supports SunOS, SVR4, and IBM RS/6000 shared libraries.
+@value{GDBN} automatically loads symbol definitions from shared libraries
+when you use the @code{run} command, or when you examine a core file.
+(Before you issue the @code{run} command, @value{GDBN} will not understand
+references to a function in a shared library, however---unless you are
+debugging a core file).
+@c FIXME: next @value{GDBN} release should permit some refs to undef
+@c FIXME...symbols---eg in a break cmd---assuming they are from a shared lib
+
+@table @code
+@item info share
+@itemx info sharedlibrary
+@kindex info sharedlibrary
+@kindex info share
+Print the names of the shared libraries which are currently loaded.
+
+@item sharedlibrary @var{regex}
+@itemx share @var{regex}
+@kindex sharedlibrary
+@kindex share
+This is an obsolescent command; you can use it to explicitly load shared
+object library symbols for files matching a Unix regular expression, but
+as with files loaded automatically, it will only load shared libraries
+required by your program for a core file or after typing @code{run}. If
+@var{regex} is omitted all shared libraries required by your program are
+loaded.
+@end table
+@end ifclear
+
+@node Symbol Errors
+@section Errors reading symbol files
+
+While reading a symbol file, @value{GDBN} will occasionally encounter problems,
+such as symbol types it does not recognize, or known bugs in compiler
+output. By default, @value{GDBN} does not notify you of such problems, since
+they are relatively common and primarily of interest to people
+debugging compilers. If you are interested in seeing information
+about ill-constructed symbol tables, you can either ask @value{GDBN} to print
+only one message about each such type of problem, no matter how many
+times the problem occurs; or you can ask @value{GDBN} to print more messages,
+to see how many times the problems occur, with the @code{set
+complaints} command (@pxref{Messages/Warnings, ,Optional warnings and
+messages}).
+
+The messages currently printed, and their meanings, include:
+
+@table @code
+@item inner block not inside outer block in @var{symbol}
+
+The symbol information shows where symbol scopes begin and end
+(such as at the start of a function or a block of statements). This
+error indicates that an inner scope block is not fully contained
+in its outer scope blocks.
+
+@value{GDBN} circumvents the problem by treating the inner block as if it had
+the same scope as the outer block. In the error message, @var{symbol}
+may be shown as ``@code{(don't know)}'' if the outer block is not a
+function.
+
+@item block at @var{address} out of order
+
+The symbol information for symbol scope blocks should occur in
+order of increasing addresses. This error indicates that it does not
+do so.
+
+@value{GDBN} does not circumvent this problem, and will have trouble
+locating symbols in the source file whose symbols it is reading. (You
+can often determine what source file is affected by specifying
+@code{set verbose on}. @xref{Messages/Warnings, ,Optional warnings and
+messages}.)
+
+@item bad block start address patched
+
+The symbol information for a symbol scope block has a start address
+smaller than the address of the preceding source line. This is known
+to occur in the SunOS 4.1.1 (and earlier) C compiler.
+
+@value{GDBN} circumvents the problem by treating the symbol scope block as
+starting on the previous source line.
+
+@item bad string table offset in symbol @var{n}
+
+@cindex foo
+Symbol number @var{n} contains a pointer into the string table which is
+larger than the size of the string table.
+
+@value{GDBN} circumvents the problem by considering the symbol to have the
+name @code{foo}, which may cause other problems if many symbols end up
+with this name.
+
+@item unknown symbol type @code{0x@var{nn}}
+
+The symbol information contains new data types that @value{GDBN} does not yet
+know how to read. @code{0x@var{nn}} is the symbol type of the misunderstood
+information, in hexadecimal.
+
+@value{GDBN} circumvents the error by ignoring this symbol information. This
+will usually allow your program to be debugged, though certain symbols
+will not be accessible. If you encounter such a problem and feel like
+debugging it, you can debug @code{@value{GDBP}} with itself, breakpoint on
+@code{complain}, then go up to the function @code{read_dbx_symtab} and
+examine @code{*bufp} to see the symbol.
+
+@item stub type has NULL name
+@value{GDBN} could not find the full definition for
+@ifclear CONLY
+a struct or class.
+@end ifclear
+@ifset CONLY
+a struct.
+@end ifset
+
+@ifclear CONLY
+@item const/volatile indicator missing (ok if using g++ v1.x), got@dots{}
+
+The symbol information for a C++ member function is missing some
+information that recent versions of the compiler should have output
+for it.
+@end ifclear
+
+@item info mismatch between compiler and debugger
+
+@value{GDBN} could not parse a type specification output by the compiler.
+@end table
+
+@node Targets
+@chapter Specifying a Debugging Target
+@cindex debugging target
+@kindex target
+
+A @dfn{target} is the execution environment occupied by your program.
+@ifclear BARETARGET
+Often, @value{GDBN} runs in the same host environment as your program; in
+that case, the debugging target is specified as a side effect when you
+use the @code{file} or @code{core} commands. When you need more
+flexibility---for example, running @value{GDBN} on a physically separate
+host, or controlling a standalone system over a serial port or a
+realtime system over a TCP/IP connection---you
+@end ifclear
+@ifset BARETARGET
+You
+@end ifset
+can use the @code{target} command to specify one of the target types
+configured for @value{GDBN} (@pxref{Target Commands, ,Commands for managing
+targets}).
+
+@menu
+* Active Targets:: Active targets
+* Target Commands:: Commands for managing targets
+* Remote:: Remote debugging
+@end menu
+
+@node Active Targets
+@section Active targets
+@cindex stacking targets
+@cindex active targets
+@cindex multiple targets
+
+@ifclear BARETARGET
+There are three classes of targets: processes, core files, and
+executable files. @value{GDBN} can work concurrently on up to three active
+targets, one in each class. This allows you to (for example) start a
+process and inspect its activity without abandoning your work on a core
+file.
+
+For example, if you execute @samp{gdb a.out}, then the executable file
+@code{a.out} is the only active target. If you designate a core file as
+well---presumably from a prior run that crashed and coredumped---then
+@value{GDBN} has two active targets and will use them in tandem, looking
+first in the corefile target, then in the executable file, to satisfy
+requests for memory addresses. (Typically, these two classes of target
+are complementary, since core files contain only a program's
+read-write memory---variables and so on---plus machine status, while
+executable files contain only the program text and initialized data.)
+@end ifclear
+
+When you type @code{run}, your executable file becomes an active process
+target as well. When a process target is active, all @value{GDBN} commands
+requesting memory addresses refer to that target; addresses in an
+@ifclear BARETARGET
+active core file or
+@end ifclear
+executable file target are obscured while the process
+target is active.
+
+@ifset BARETARGET
+Use the @code{exec-file} command to select a
+new executable target (@pxref{Files, ,Commands to specify
+files}).
+@end ifset
+@ifclear BARETARGET
+Use the @code{core-file} and @code{exec-file} commands to select a
+new core file or executable target (@pxref{Files, ,Commands to specify
+files}). To specify as a target a process that is already running, use
+the @code{attach} command (@pxref{Attach, ,Debugging an
+already-running process}).
+@end ifclear
+
+@node Target Commands
+@section Commands for managing targets
+
+@table @code
+@item target @var{type} @var{parameters}
+Connects the @value{GDBN} host environment to a target
+@ifset BARETARGET
+machine.
+@end ifset
+@ifclear BARETARGET
+machine or process. A target is typically a protocol for talking to
+debugging facilities. You use the argument @var{type} to specify the
+type or protocol of the target machine.
+
+Further @var{parameters} are interpreted by the target protocol, but
+typically include things like device names or host names to connect
+with, process numbers, and baud rates.
+@end ifclear
+
+The @code{target} command will not repeat if you press @key{RET} again
+after executing the command.
+
+@item help target
+@kindex help target
+Displays the names of all targets available. To display targets
+currently selected, use either @code{info target} or @code{info files}
+(@pxref{Files, ,Commands to specify files}).
+
+@item help target @var{name}
+Describe a particular target, including any parameters necessary to
+select it.
+@end table
+
+Here are some common targets (available, or not, depending on the GDB
+configuration):
+
+@table @code
+@item target exec @var{program}
+@kindex target exec
+An executable file. @samp{target exec @var{program}} is the same as
+@samp{exec-file @var{program}}.
+
+@ifclear BARETARGET
+@item target core @var{filename}
+@kindex target core
+A core dump file. @samp{target core @var{filename}} is the same as
+@samp{core-file @var{filename}}.
+@end ifclear
+
+@ifset REMOTESTUB
+@item target remote @var{dev}
+@kindex target remote
+Remote serial target in GDB-specific protocol. The argument @var{dev}
+specifies what serial device to use for the connection (e.g.
+@file{/dev/ttya}). @xref{Remote, ,Remote debugging}.
+@end ifset
+
+@ifset SIMS
+@item target sim
+@kindex target sim
+CPU simulator. @xref{Simulator,,Simulated CPU Target}.
+@end ifset
+
+@ifset AMD29K
+@item target udi @var{keyword}
+@kindex target udi
+Remote AMD29K target, using the AMD UDI protocol. The @var{keyword}
+argument specifies which 29K board or simulator to use. @xref{UDI29K
+Remote,,@value{GDBN} and the UDI protocol for AMD29K}.
+
+@item target amd-eb @var{dev} @var{speed} @var{PROG}
+@kindex target amd-eb
+@cindex AMD EB29K
+Remote PC-resident AMD EB29K board, attached over serial lines.
+@var{dev} is the serial device, as for @code{target remote};
+@var{speed} allows you to specify the linespeed; and @var{PROG} is the
+name of the program to be debugged, as it appears to DOS on the PC.
+@xref{EB29K Remote, ,@value{GDBN} with a remote EB29K}.
+
+@end ifset
+@ifset H8
+@item target hms
+@kindex target hms
+A Hitachi SH, H8/300, or H8/500 board, attached via serial line to your host.
+@ifclear H8EXCLUSIVE
+@c Unix only, not currently of interest for H8-only manual
+Use special commands @code{device} and @code{speed} to control the serial
+line and the communications speed used.
+@end ifclear
+@xref{Hitachi Remote,,@value{GDBN} and Hitachi Microprocessors}.
+
+@end ifset
+@ifset I960
+@item target nindy @var{devicename}
+@kindex target nindy
+An Intel 960 board controlled by a Nindy Monitor. @var{devicename} is
+the name of the serial device to use for the connection, e.g.
+@file{/dev/ttya}. @xref{i960-Nindy Remote, ,@value{GDBN} with a remote i960 (Nindy)}.
+
+@end ifset
+@ifset ST2000
+@item target st2000 @var{dev} @var{speed}
+@kindex target st2000
+A Tandem ST2000 phone switch, running Tandem's STDBUG protocol. @var{dev}
+is the name of the device attached to the ST2000 serial line;
+@var{speed} is the communication line speed. The arguments are not used
+if @value{GDBN} is configured to connect to the ST2000 using TCP or Telnet.
+@xref{ST2000 Remote,,@value{GDBN} with a Tandem ST2000}.
+
+@end ifset
+@ifset VXWORKS
+@item target vxworks @var{machinename}
+@kindex target vxworks
+A VxWorks system, attached via TCP/IP. The argument @var{machinename}
+is the target system's machine name or IP address.
+@xref{VxWorks Remote, ,@value{GDBN} and VxWorks}.
+@end ifset
+@end table
+
+@ifset GENERIC
+Different targets are available on different configurations of @value{GDBN}; your
+configuration may have more or fewer targets.
+@end ifset
+
+@node Remote
+@section Remote debugging
+@cindex remote debugging
+
+If you are trying to debug a program running on a machine that cannot run
+GDB in the usual way, it is often useful to use remote debugging. For
+example, you might use remote debugging on an operating system kernel, or on
+a small system which does not have a general purpose operating system
+powerful enough to run a full-featured debugger.
+
+Some configurations of GDB have special serial or TCP/IP interfaces
+to make this work with particular debugging targets. In addition,
+GDB comes with a generic serial protocol (specific to GDB, but
+not specific to any particular target system) which you can use if you
+write the remote stubs---the code that will run on the remote system to
+communicate with GDB.
+
+Other remote targets may be available in your
+configuration of GDB; use @code{help targets} to list them.
+
+@ifset GENERIC
+@c Text on starting up GDB in various specific cases; it goes up front
+@c in manuals configured for any of those particular situations, here
+@c otherwise.
+@menu
+@ifset REMOTESTUB
+* Remote Serial:: @value{GDBN} remote serial protocol
+@end ifset
+@ifset I960
+* i960-Nindy Remote:: @value{GDBN} with a remote i960 (Nindy)
+@end ifset
+@ifset AMD29K
+* UDI29K Remote:: @value{GDBN} and the UDI protocol for AMD29K
+* EB29K Remote:: @value{GDBN} with a remote EB29K
+@end ifset
+@ifset VXWORKS
+* VxWorks Remote:: @value{GDBN} and VxWorks
+@end ifset
+@ifset ST2000
+* ST2000 Remote:: @value{GDBN} with a Tandem ST2000
+@end ifset
+@ifset H8
+* Hitachi Remote:: @value{GDBN} and Hitachi Microprocessors
+@end ifset
+@ifset MIPS
+* MIPS Remote:: @value{GDBN} and MIPS boards
+@end ifset
+@ifset SIMS
+* Simulator:: Simulated CPU target
+@end ifset
+@end menu
+
+@include remote.texi
+@end ifset
+
+@node Controlling GDB
+@chapter Controlling @value{GDBN}
+
+You can alter the way @value{GDBN} interacts with you by using
+the @code{set} command. For commands controlling how @value{GDBN} displays
+data, @pxref{Print Settings, ,Print settings}; other settings are described here.
+
+@menu
+* Prompt:: Prompt
+* Editing:: Command editing
+* History:: Command history
+* Screen Size:: Screen size
+* Numbers:: Numbers
+* Messages/Warnings:: Optional warnings and messages
+@end menu
+
+@node Prompt
+@section Prompt
+@cindex prompt
+
+@value{GDBN} indicates its readiness to read a command by printing a string
+called the @dfn{prompt}. This string is normally @samp{(@value{GDBP})}. You
+can change the prompt string with the @code{set prompt} command. For
+instance, when debugging @value{GDBN} with @value{GDBN}, it is useful to change
+the prompt in one of the @value{GDBN} sessions so that you can always tell which
+one you are talking to.
+
+@table @code
+@item set prompt @var{newprompt}
+@kindex set prompt
+Directs @value{GDBN} to use @var{newprompt} as its prompt string henceforth.
+@kindex show prompt
+@item show prompt
+Prints a line of the form: @samp{Gdb's prompt is: @var{your-prompt}}
+@end table
+
+@node Editing
+@section Command editing
+@cindex readline
+@cindex command line editing
+
+@value{GDBN} reads its input commands via the @dfn{readline} interface. This
+GNU library provides consistent behavior for programs which provide a
+command line interface to the user. Advantages are @code{emacs}-style
+or @code{vi}-style inline editing of commands, @code{csh}-like history
+substitution, and a storage and recall of command history across
+debugging sessions.
+
+You may control the behavior of command line editing in @value{GDBN} with the
+command @code{set}.
+
+@table @code
+@kindex set editing
+@cindex editing
+@item set editing
+@itemx set editing on
+Enable command line editing (enabled by default).
+
+@item set editing off
+Disable command line editing.
+
+@kindex show editing
+@item show editing
+Show whether command line editing is enabled.
+@end table
+
+@node History
+@section Command history
+
+@value{GDBN} can keep track of the commands you type during your
+debugging sessions, so that you can be certain of precisely what
+happened. Use these commands to manage the @value{GDBN} command
+history facility.
+
+@table @code
+@cindex history substitution
+@cindex history file
+@kindex set history filename
+@item set history filename @var{fname}
+Set the name of the @value{GDBN} command history file to @var{fname}. This is
+the file from which @value{GDBN} will read an initial command history
+list or to which it will write this list when it exits. This list is
+accessed through history expansion or through the history
+command editing characters listed below. This file defaults to the
+value of the environment variable @code{GDBHISTFILE}, or to
+@file{./.gdb_history} if this variable is not set.
+
+@cindex history save
+@kindex set history save
+@item set history save
+@itemx set history save on
+Record command history in a file, whose name may be specified with the
+@code{set history filename} command. By default, this option is disabled.
+
+@item set history save off
+Stop recording command history in a file.
+
+@cindex history size
+@kindex set history size
+@item set history size @var{size}
+Set the number of commands which @value{GDBN} will keep in its history list.
+This defaults to the value of the environment variable
+@code{HISTSIZE}, or to 256 if this variable is not set.
+@end table
+
+@cindex history expansion
+History expansion assigns special meaning to the character @kbd{!}.
+@ifset have-readline-appendices
+@xref{Event Designators}.
+@end ifset
+
+Since @kbd{!} is also the logical not operator in C, history expansion
+is off by default. If you decide to enable history expansion with the
+@code{set history expansion on} command, you may sometimes need to
+follow @kbd{!} (when it is used as logical not, in an expression) with
+a space or a tab to prevent it from being expanded. The readline
+history facilities will not attempt substitution on the strings
+@kbd{!=} and @kbd{!(}, even when history expansion is enabled.
+
+The commands to control history expansion are:
+
+@table @code
+
+@kindex set history expansion
+@item set history expansion on
+@itemx set history expansion
+Enable history expansion. History expansion is off by default.
+
+@item set history expansion off
+Disable history expansion.
+
+The readline code comes with more complete documentation of
+editing and history expansion features. Users unfamiliar with @code{emacs}
+or @code{vi} may wish to read it.
+@ifset have-readline-appendices
+@xref{Command Line Editing}.
+@end ifset
+
+@c @group
+@kindex show history
+@item show history
+@itemx show history filename
+@itemx show history save
+@itemx show history size
+@itemx show history expansion
+These commands display the state of the @value{GDBN} history parameters.
+@code{show history} by itself displays all four states.
+@c @end group
+@end table
+
+@table @code
+@kindex show commands
+@item show commands
+Display the last ten commands in the command history.
+
+@item show commands @var{n}
+Print ten commands centered on command number @var{n}.
+
+@item show commands +
+Print ten commands just after the commands last printed.
+@end table
+
+@node Screen Size
+@section Screen size
+@cindex size of screen
+@cindex pauses in output
+
+Certain commands to @value{GDBN} may produce large amounts of
+information output to the screen. To help you read all of it,
+@value{GDBN} pauses and asks you for input at the end of each page of
+output. Type @key{RET} when you want to continue the output, or @kbd{q}
+to discard the remaining output. Also, the screen width setting
+determines when to wrap lines of output. Depending on what is being
+printed, @value{GDBN} tries to break the line at a readable place,
+rather than simply letting it overflow onto the following line.
+
+Normally @value{GDBN} knows the size of the screen from the termcap data base
+together with the value of the @code{TERM} environment variable and the
+@code{stty rows} and @code{stty cols} settings. If this is not correct,
+you can override it with the @code{set height} and @code{set
+width} commands:
+
+@table @code
+@item set height @var{lpp}
+@itemx show height
+@itemx set width @var{cpl}
+@itemx show width
+@kindex set height
+@kindex set width
+@kindex show width
+@kindex show height
+These @code{set} commands specify a screen height of @var{lpp} lines and
+a screen width of @var{cpl} characters. The associated @code{show}
+commands display the current settings.
+
+If you specify a height of zero lines, @value{GDBN} will not pause during output
+no matter how long the output is. This is useful if output is to a file
+or to an editor buffer.
+
+Likewise, you can specify @samp{set width 0} to prevent @value{GDBN}
+from wrapping its output.
+@end table
+
+@node Numbers
+@section Numbers
+@cindex number representation
+@cindex entering numbers
+
+You can always enter numbers in octal, decimal, or hexadecimal in @value{GDBN} by
+the usual conventions: octal numbers begin with @samp{0}, decimal
+numbers end with @samp{.}, and hexadecimal numbers begin with @samp{0x}.
+Numbers that begin with none of these are, by default, entered in base
+10; likewise, the default display for numbers---when no particular
+format is specified---is base 10. You can change the default base for
+both input and output with the @code{set radix} command.
+
+@table @code
+@kindex set radix
+@item set radix @var{base}
+Set the default base for numeric input and display. Supported choices
+for @var{base} are decimal 8, 10, or 16. @var{base} must itself be
+specified either unambiguously or using the current default radix; for
+example, any of
+
+@example
+set radix 012
+set radix 10.
+set radix 0xa
+@end example
+
+@noindent
+will set the base to decimal. On the other hand, @samp{set radix 10}
+will leave the radix unchanged no matter what it was.
+
+@kindex show radix
+@item show radix
+Display the current default base for numeric input and display.
+@end table
+
+@node Messages/Warnings
+@section Optional warnings and messages
+
+By default, @value{GDBN} is silent about its inner workings. If you are running
+on a slow machine, you may want to use the @code{set verbose} command.
+It will make @value{GDBN} tell you when it does a lengthy internal operation, so
+you will not think it has crashed.
+
+Currently, the messages controlled by @code{set verbose} are those
+which announce that the symbol table for a source file is being read;
+see @code{symbol-file} in @ref{Files, ,Commands to specify files}.
+
+@table @code
+@kindex set verbose
+@item set verbose on
+Enables @value{GDBN} output of certain informational messages.
+
+@item set verbose off
+Disables @value{GDBN} output of certain informational messages.
+
+@kindex show verbose
+@item show verbose
+Displays whether @code{set verbose} is on or off.
+@end table
+
+By default, if @value{GDBN} encounters bugs in the symbol table of an object
+file, it is silent; but if you are debugging a compiler, you may find
+this information useful (@pxref{Symbol Errors, ,Errors reading symbol files}).
+
+@table @code
+@kindex set complaints
+@item set complaints @var{limit}
+Permits @value{GDBN} to output @var{limit} complaints about each type of unusual
+symbols before becoming silent about the problem. Set @var{limit} to
+zero to suppress all complaints; set it to a large number to prevent
+complaints from being suppressed.
+
+@kindex show complaints
+@item show complaints
+Displays how many symbol complaints @value{GDBN} is permitted to produce.
+@end table
+
+By default, @value{GDBN} is cautious, and asks what sometimes seems to be a
+lot of stupid questions to confirm certain commands. For example, if
+you try to run a program which is already running:
+
+@example
+(@value{GDBP}) run
+The program being debugged has been started already.
+Start it from the beginning? (y or n)
+@end example
+
+If you are willing to unflinchingly face the consequences of your own
+commands, you can disable this ``feature'':
+
+@table @code
+@kindex set confirm
+@cindex flinching
+@cindex confirmation
+@cindex stupid questions
+@item set confirm off
+Disables confirmation requests.
+
+@item set confirm on
+Enables confirmation requests (the default).
+
+@item show confirm
+@kindex show confirm
+Displays state of confirmation requests.
+@end table
+
+@c FIXME this does not really belong here. But where *does* it belong?
+@cindex reloading symbols
+Some systems allow individual object files that make up your program to
+be replaced without stopping and restarting your program.
+@ifset VXWORKS
+For example, in VxWorks you can simply recompile a defective object file
+and keep on running.
+@end ifset
+If you are running on one of these systems, you can allow @value{GDBN} to
+reload the symbols for automatically relinked modules:
+
+@table @code
+@kindex set symbol-reloading
+@item set symbol-reloading on
+Replace symbol definitions for the corresponding source file when an
+object file with a particular name is seen again.
+
+@item set symbol-reloading off
+Do not replace symbol definitions when re-encountering object files of
+the same name. This is the default state; if you are not running on a
+system that permits automatically relinking modules, you should leave
+@code{symbol-reloading} off, since otherwise @value{GDBN} may discard symbols
+when linking large programs, that may contain several modules (from
+different directories or libraries) with the same name.
+
+@item show symbol-reloading
+Show the current @code{on} or @code{off} setting.
+@end table
+
+@node Sequences
+@chapter Canned Sequences of Commands
+
+Aside from breakpoint commands (@pxref{Break Commands, ,Breakpoint
+command lists}), @value{GDBN} provides two ways to store sequences of commands
+for execution as a unit: user-defined commands and command files.
+
+@menu
+* Define:: User-defined commands
+* Hooks:: User-defined command hooks
+* Command Files:: Command files
+* Output:: Commands for controlled output
+@end menu
+
+@node Define
+@section User-defined commands
+
+@cindex user-defined command
+A @dfn{user-defined command} is a sequence of @value{GDBN} commands to which you
+assign a new name as a command. This is done with the @code{define}
+command.
+
+@table @code
+@item define @var{commandname}
+@kindex define
+Define a command named @var{commandname}. If there is already a command
+by that name, you are asked to confirm that you want to redefine it.
+
+The definition of the command is made up of other @value{GDBN} command lines,
+which are given following the @code{define} command. The end of these
+commands is marked by a line containing @code{end}.
+
+@item document @var{commandname}
+@kindex document
+Give documentation to the user-defined command @var{commandname}. The
+command @var{commandname} must already be defined. This command reads
+lines of documentation just as @code{define} reads the lines of the
+command definition, ending with @code{end}. After the @code{document}
+command is finished, @code{help} on command @var{commandname} will print
+the documentation you have specified.
+
+You may use the @code{document} command again to change the
+documentation of a command. Redefining the command with @code{define}
+does not change the documentation.
+
+@item help user-defined
+@kindex help user-defined
+List all user-defined commands, with the first line of the documentation
+(if any) for each.
+
+@item show user
+@itemx show user @var{commandname}
+@kindex show user
+Display the @value{GDBN} commands used to define @var{commandname} (but not its
+documentation). If no @var{commandname} is given, display the
+definitions for all user-defined commands.
+@end table
+
+User-defined commands do not take arguments. When they are executed, the
+commands of the definition are not printed. An error in any command
+stops execution of the user-defined command.
+
+Commands that would ask for confirmation if used interactively proceed
+without asking when used inside a user-defined command. Many @value{GDBN} commands
+that normally print messages to say what they are doing omit the messages
+when used in a user-defined command.
+
+@node Hooks
+@section User-defined command hooks
+@cindex command files
+
+You may define @emph{hooks}, which are a special kind of user-defined
+command. Whenever you run the command @samp{foo}, if the user-defined
+command @samp{hook-foo} exists, it is executed (with no arguments)
+before that command.
+
+In addition, a pseudo-command, @samp{stop} exists. Defining
+(@samp{hook-stop}) makes the associated commands execute every time
+execution stops in your program: before breakpoint commands are run,
+displays are printed, or the stack frame is printed.
+
+@ifclear BARETARGET
+For example, to ignore @code{SIGALRM} signals while
+single-stepping, but treat them normally during normal execution,
+you could define:
+
+@example
+define hook-stop
+handle SIGALRM nopass
+end
+
+define hook-run
+handle SIGALRM pass
+end
+
+define hook-continue
+handle SIGLARM pass
+end
+@end example
+@end ifclear
+
+You can define a hook for any single-word command in @value{GDBN}, but
+not for command aliases; you should define a hook for the basic command
+name, e.g. @code{backtrace} rather than @code{bt}.
+@c FIXME! So how does Joe User discover whether a command is an alias
+@c or not?
+If an error occurs during the execution of your hook, execution of
+@value{GDBN} commands stops and @value{GDBN} issues a prompt
+(before the command that you actually typed had a chance to run).
+
+If you try to define a hook which does not match any known command, you
+will get a warning from the @code{define} command.
+
+@node Command Files
+@section Command files
+
+@cindex command files
+A command file for @value{GDBN} is a file of lines that are @value{GDBN} commands. Comments
+(lines starting with @kbd{#}) may also be included. An empty line in a
+command file does nothing; it does not mean to repeat the last command, as
+it would from the terminal.
+
+@cindex init file
+@cindex @file{@value{GDBINIT}}
+When you start @value{GDBN}, it automatically executes commands from its
+@dfn{init files}. These are files named @file{@value{GDBINIT}}. @value{GDBN} reads
+the init file (if any) in your home directory and then the init file
+(if any) in the current working directory. (The init files are not
+executed if you use the @samp{-nx} option; @pxref{Mode Options,
+,Choosing modes}.)
+
+@ifset GENERIC
+@cindex init file name
+On some configurations of @value{GDBN}, the init file is known by a
+different name (these are typically environments where a specialized
+form of GDB may need to coexist with other forms, hence a different name
+for the specialized version's init file). These are the environments
+with special init file names:
+
+@itemize @bullet
+@kindex .vxgdbinit
+@item
+VxWorks (Wind River Systems real-time OS): @samp{.vxgdbinit}
+
+@kindex .os68gdbinit
+@item
+OS68K (Enea Data Systems real-time OS): @samp{.os68gdbinit}
+
+@kindex .esgdbinit
+@item
+ES-1800 (Ericsson Telecom AB M68000 emulator): @samp{.esgdbinit}
+@end itemize
+@end ifset
+
+You can also request the execution of a command file with the
+@code{source} command:
+
+@table @code
+@item source @var{filename}
+@kindex source
+Execute the command file @var{filename}.
+@end table
+
+The lines in a command file are executed sequentially. They are not
+printed as they are executed. An error in any command terminates execution
+of the command file.
+
+Commands that would ask for confirmation if used interactively proceed
+without asking when used in a command file. Many @value{GDBN} commands that
+normally print messages to say what they are doing omit the messages
+when called from command files.
+
+@node Output
+@section Commands for controlled output
+
+During the execution of a command file or a user-defined command, normal
+@value{GDBN} output is suppressed; the only output that appears is what is
+explicitly printed by the commands in the definition. This section
+describes three commands useful for generating exactly the output you
+want.
+
+@table @code
+@item echo @var{text}
+@kindex echo
+@c I do not consider backslash-space a standard C escape sequence
+@c because it is not in ANSI.
+Print @var{text}. Nonprinting characters can be included in
+@var{text} using C escape sequences, such as @samp{\n} to print a
+newline. @strong{No newline will be printed unless you specify one.}
+In addition to the standard C escape sequences, a backslash followed
+by a space stands for a space. This is useful for displaying a
+string with spaces at the beginning or the end, since leading and
+trailing spaces are otherwise trimmed from all arguments.
+To print @samp{@w{ }and foo =@w{ }}, use the command
+@samp{echo \@w{ }and foo = \@w{ }}.
+
+A backslash at the end of @var{text} can be used, as in C, to continue
+the command onto subsequent lines. For example,
+
+@example
+echo This is some text\n\
+which is continued\n\
+onto several lines.\n
+@end example
+
+produces the same output as
+
+@example
+echo This is some text\n
+echo which is continued\n
+echo onto several lines.\n
+@end example
+
+@item output @var{expression}
+@kindex output
+Print the value of @var{expression} and nothing but that value: no
+newlines, no @samp{$@var{nn} = }. The value is not entered in the
+value history either. @xref{Expressions, ,Expressions}, for more information on
+expressions.
+
+@item output/@var{fmt} @var{expression}
+Print the value of @var{expression} in format @var{fmt}. You can use
+the same formats as for @code{print}. @xref{Output Formats,,Output
+formats}, for more information.
+
+@item printf @var{string}, @var{expressions}@dots{}
+@kindex printf
+Print the values of the @var{expressions} under the control of
+@var{string}. The @var{expressions} are separated by commas and may be
+either numbers or pointers. Their values are printed as specified by
+@var{string}, exactly as if your program were to execute the C
+subroutine
+
+@example
+printf (@var{string}, @var{expressions}@dots{});
+@end example
+
+For example, you can print two values in hex like this:
+
+@smallexample
+printf "foo, bar-foo = 0x%x, 0x%x\n", foo, bar-foo
+@end smallexample
+
+The only backslash-escape sequences that you can use in the format
+string are the simple ones that consist of backslash followed by a
+letter.
+@end table
+
+@ifclear DOSHOST
+@node Emacs
+@chapter Using @value{GDBN} under GNU Emacs
+
+@cindex emacs
+A special interface allows you to use GNU Emacs to view (and
+edit) the source files for the program you are debugging with
+@value{GDBN}.
+
+To use this interface, use the command @kbd{M-x gdb} in Emacs. Give the
+executable file you want to debug as an argument. This command starts
+@value{GDBN} as a subprocess of Emacs, with input and output through a newly
+created Emacs buffer.
+
+Using @value{GDBN} under Emacs is just like using @value{GDBN} normally except for two
+things:
+
+@itemize @bullet
+@item
+All ``terminal'' input and output goes through the Emacs buffer.
+@end itemize
+
+This applies both to @value{GDBN} commands and their output, and to the input
+and output done by the program you are debugging.
+
+This is useful because it means that you can copy the text of previous
+commands and input them again; you can even use parts of the output
+in this way.
+
+All the facilities of Emacs' Shell mode are available for interacting
+with your program. In particular, you can send signals the usual
+way---for example, @kbd{C-c C-c} for an interrupt, @kbd{C-c C-z} for a
+stop.
+
+@itemize @bullet
+@item
+@value{GDBN} displays source code through Emacs.
+@end itemize
+
+Each time @value{GDBN} displays a stack frame, Emacs automatically finds the
+source file for that frame and puts an arrow (@samp{=>}) at the
+left margin of the current line. Emacs uses a separate buffer for
+source display, and splits the screen to show both your @value{GDBN} session
+and the source.
+
+Explicit @value{GDBN} @code{list} or search commands still produce output as
+usual, but you probably will have no reason to use them.
+
+@quotation
+@emph{Warning:} If the directory where your program resides is not your
+current directory, it can be easy to confuse Emacs about the location of
+the source files, in which case the auxiliary display buffer will not
+appear to show your source. @value{GDBN} can find programs by searching your
+environment's @code{PATH} variable, so the @value{GDBN} input and output
+session will proceed normally; but Emacs does not get enough information
+back from @value{GDBN} to locate the source files in this situation. To
+avoid this problem, either start @value{GDBN} mode from the directory where
+your program resides, or specify a full path name when prompted for the
+@kbd{M-x gdb} argument.
+
+A similar confusion can result if you use the @value{GDBN} @code{file} command to
+switch to debugging a program in some other location, from an existing
+@value{GDBN} buffer in Emacs.
+@end quotation
+
+By default, @kbd{M-x gdb} calls the program called @file{gdb}. If
+you need to call @value{GDBN} by a different name (for example, if you keep
+several configurations around, with different names) you can set the
+Emacs variable @code{gdb-command-name}; for example,
+
+@example
+(setq gdb-command-name "mygdb")
+@end example
+
+@noindent
+(preceded by @kbd{ESC ESC}, or typed in the @code{*scratch*} buffer, or
+in your @file{.emacs} file) will make Emacs call the program named
+``@code{mygdb}'' instead.
+
+In the @value{GDBN} I/O buffer, you can use these special Emacs commands in
+addition to the standard Shell mode commands:
+
+@table @kbd
+@item C-h m
+Describe the features of Emacs' @value{GDBN} Mode.
+
+@item M-s
+Execute to another source line, like the @value{GDBN} @code{step} command; also
+update the display window to show the current file and location.
+
+@item M-n
+Execute to next source line in this function, skipping all function
+calls, like the @value{GDBN} @code{next} command. Then update the display window
+to show the current file and location.
+
+@item M-i
+Execute one instruction, like the @value{GDBN} @code{stepi} command; update
+display window accordingly.
+
+@item M-x gdb-nexti
+Execute to next instruction, using the @value{GDBN} @code{nexti} command; update
+display window accordingly.
+
+@item C-c C-f
+Execute until exit from the selected stack frame, like the @value{GDBN}
+@code{finish} command.
+
+@item M-c
+Continue execution of your program, like the @value{GDBN} @code{continue}
+command.
+
+@emph{Warning:} In Emacs v19, this command is @kbd{C-c C-p}.
+
+@item M-u
+Go up the number of frames indicated by the numeric argument
+(@pxref{Arguments, , Numeric Arguments, emacs, The GNU Emacs Manual}),
+like the @value{GDBN} @code{up} command.
+
+@emph{Warning:} In Emacs v19, this command is @kbd{C-c C-u}.
+
+@item M-d
+Go down the number of frames indicated by the numeric argument, like the
+@value{GDBN} @code{down} command.
+
+@emph{Warning:} In Emacs v19, this command is @kbd{C-c C-d}.
+
+@item C-x &
+Read the number where the cursor is positioned, and insert it at the end
+of the @value{GDBN} I/O buffer. For example, if you wish to disassemble code
+around an address that was displayed earlier, type @kbd{disassemble};
+then move the cursor to the address display, and pick up the
+argument for @code{disassemble} by typing @kbd{C-x &}.
+
+You can customize this further by defining elements of the list
+@code{gdb-print-command}; once it is defined, you can format or
+otherwise process numbers picked up by @kbd{C-x &} before they are
+inserted. A numeric argument to @kbd{C-x &} will both indicate that you
+wish special formatting, and act as an index to pick an element of the
+list. If the list element is a string, the number to be inserted is
+formatted using the Emacs function @code{format}; otherwise the number
+is passed as an argument to the corresponding list element.
+@end table
+
+In any source file, the Emacs command @kbd{C-x SPC} (@code{gdb-break})
+tells @value{GDBN} to set a breakpoint on the source line point is on.
+
+If you accidentally delete the source-display buffer, an easy way to get
+it back is to type the command @code{f} in the @value{GDBN} buffer, to
+request a frame display; when you run under Emacs, this will recreate
+the source buffer if necessary to show you the context of the current
+frame.
+
+The source files displayed in Emacs are in ordinary Emacs buffers
+which are visiting the source files in the usual way. You can edit
+the files with these buffers if you wish; but keep in mind that @value{GDBN}
+communicates with Emacs in terms of line numbers. If you add or
+delete lines from the text, the line numbers that @value{GDBN} knows will cease
+to correspond properly with the code.
+
+@c The following dropped because Epoch is nonstandard. Reactivate
+@c if/when v19 does something similar. ---pesch@cygnus.com 19dec1990
+@ignore
+@kindex emacs epoch environment
+@kindex epoch
+@kindex inspect
+
+Version 18 of Emacs has a built-in window system called the @code{epoch}
+environment. Users of this environment can use a new command,
+@code{inspect} which performs identically to @code{print} except that
+each value is printed in its own window.
+@end ignore
+@end ifclear
+
+@ifset LUCID
+@node Energize
+@chapter Using @value{GDBN} with Energize
+
+@cindex Energize
+The Energize Programming System is an integrated development environment
+that includes a point-and-click interface to many programming tools.
+When you use @value{GDBN} in this environment, you can use the standard
+Energize graphical interface to drive @value{GDBN}; you can also, if you
+choose, type @value{GDBN} commands as usual in a debugging window. Even if
+you use the graphical interface, the debugging window (which uses Emacs,
+and resembles the standard Emacs interface to @value{GDBN}) displays the
+equivalent commands, so that the history of your debugging session is
+properly reflected.
+
+When Energize starts up a @value{GDBN} session, it uses one of the
+command-line options @samp{-energize} or @samp{-cadillac} (``cadillac''
+is the name of the communications protocol used by the Energize system).
+This option makes @value{GDBN} run as one of the tools in the Energize Tool
+Set: it sends all output to the Energize kernel, and accept input from
+it as well.
+
+See the user manual for the Energize Programming System for
+information on how to use the Energize graphical interface and the other
+development tools that Energize integrates with @value{GDBN}.
+
+@end ifset
+
+@node GDB Bugs
+@chapter Reporting Bugs in @value{GDBN}
+@cindex bugs in @value{GDBN}
+@cindex reporting bugs in @value{GDBN}
+
+Your bug reports play an essential role in making @value{GDBN} reliable.
+
+Reporting a bug may help you by bringing a solution to your problem, or it
+may not. But in any case the principal function of a bug report is to help
+the entire community by making the next version of @value{GDBN} work better. Bug
+reports are your contribution to the maintenance of @value{GDBN}.
+
+In order for a bug report to serve its purpose, you must include the
+information that enables us to fix the bug.
+
+@menu
+* Bug Criteria:: Have you found a bug?
+* Bug Reporting:: How to report bugs
+@end menu
+
+@node Bug Criteria
+@section Have you found a bug?
+@cindex bug criteria
+
+If you are not sure whether you have found a bug, here are some guidelines:
+
+@itemize @bullet
+@item
+@cindex fatal signal
+@cindex debugger crash
+@cindex crash of debugger
+If the debugger gets a fatal signal, for any input whatever, that is a
+@value{GDBN} bug. Reliable debuggers never crash.
+
+@item
+@cindex error on valid input
+If @value{GDBN} produces an error message for valid input, that is a bug.
+
+@item
+@cindex invalid input
+If @value{GDBN} does not produce an error message for invalid input,
+that is a bug. However, you should note that your idea of
+``invalid input'' might be our idea of ``an extension'' or ``support
+for traditional practice''.
+
+@item
+If you are an experienced user of debugging tools, your suggestions
+for improvement of @value{GDBN} are welcome in any case.
+@end itemize
+
+@node Bug Reporting
+@section How to report bugs
+@cindex bug reports
+@cindex @value{GDBN} bugs, reporting
+
+A number of companies and individuals offer support for GNU products.
+If you obtained @value{GDBN} from a support organization, we recommend you
+contact that organization first.
+
+You can find contact information for many support companies and
+individuals in the file @file{etc/SERVICE} in the GNU Emacs
+distribution.
+
+In any event, we also recommend that you send bug reports for @value{GDBN} to one
+of these addresses:
+
+@example
+bug-gdb@@prep.ai.mit.edu
+@{ucbvax|mit-eddie|uunet@}!prep.ai.mit.edu!bug-gdb
+@end example
+
+@strong{Do not send bug reports to @samp{info-gdb}, or to
+@samp{help-gdb}, or to any newsgroups.} Most users of @value{GDBN} do not want to
+receive bug reports. Those that do, have arranged to receive @samp{bug-gdb}.
+
+The mailing list @samp{bug-gdb} has a newsgroup @samp{gnu.gdb.bug} which
+serves as a repeater. The mailing list and the newsgroup carry exactly
+the same messages. Often people think of posting bug reports to the
+newsgroup instead of mailing them. This appears to work, but it has one
+problem which can be crucial: a newsgroup posting often lacks a mail
+path back to the sender. Thus, if we need to ask for more information,
+we may be unable to reach you. For this reason, it is better to send
+bug reports to the mailing list.
+
+As a last resort, send bug reports on paper to:
+
+@example
+GNU Debugger Bugs
+Free Software Foundation
+545 Tech Square
+Cambridge, MA 02139
+@end example
+
+The fundamental principle of reporting bugs usefully is this:
+@strong{report all the facts}. If you are not sure whether to state a
+fact or leave it out, state it!
+
+Often people omit facts because they think they know what causes the
+problem and assume that some details do not matter. Thus, you might
+assume that the name of the variable you use in an example does not matter.
+Well, probably it does not, but one cannot be sure. Perhaps the bug is a
+stray memory reference which happens to fetch from the location where that
+name is stored in memory; perhaps, if the name were different, the contents
+of that location would fool the debugger into doing the right thing despite
+the bug. Play it safe and give a specific, complete example. That is the
+easiest thing for you to do, and the most helpful.
+
+Keep in mind that the purpose of a bug report is to enable us to fix
+the bug if it is new to us. It is not as important as what happens if
+the bug is already known. Therefore, always write your bug reports on
+the assumption that the bug has not been reported previously.
+
+Sometimes people give a few sketchy facts and ask, ``Does this ring a
+bell?'' Those bug reports are useless, and we urge everyone to
+@emph{refuse to respond to them} except to chide the sender to report
+bugs properly.
+
+To enable us to fix the bug, you should include all these things:
+
+@itemize @bullet
+@item
+The version of @value{GDBN}. @value{GDBN} announces it if you start with no
+arguments; you can also print it at any time using @code{show version}.
+
+Without this, we will not know whether there is any point in looking for
+the bug in the current version of @value{GDBN}.
+
+@item
+The type of machine you are using, and the operating system name and
+version number.
+
+@item
+What compiler (and its version) was used to compile @value{GDBN}---e.g.
+``@value{GCC}--2.0''.
+
+@item
+What compiler (and its version) was used to compile the program you
+are debugging---e.g. ``@value{GCC}--2.0''.
+
+@item
+The command arguments you gave the compiler to compile your example and
+observe the bug. For example, did you use @samp{-O}? To guarantee
+you will not omit something important, list them all. A copy of the
+Makefile (or the output from make) is sufficient.
+
+If we were to try to guess the arguments, we would probably guess wrong
+and then we might not encounter the bug.
+
+@item
+A complete input script, and all necessary source files, that will
+reproduce the bug.
+
+@item
+A description of what behavior you observe that you believe is
+incorrect. For example, ``It gets a fatal signal.''
+
+Of course, if the bug is that @value{GDBN} gets a fatal signal, then we will
+certainly notice it. But if the bug is incorrect output, we might not
+notice unless it is glaringly wrong. We are human, after all. You
+might as well not give us a chance to make a mistake.
+
+Even if the problem you experience is a fatal signal, you should still
+say so explicitly. Suppose something strange is going on, such as,
+your copy of @value{GDBN} is out of synch, or you have encountered a
+bug in the C library on your system. (This has happened!) Your copy
+might crash and ours would not. If you told us to expect a crash,
+then when ours fails to crash, we would know that the bug was not
+happening for us. If you had not told us to expect a crash, then we
+would not be able to draw any conclusion from our observations.
+
+@item
+If you wish to suggest changes to the @value{GDBN} source, send us context
+diffs. If you even discuss something in the @value{GDBN} source, refer to
+it by context, not by line number.
+
+The line numbers in our development sources will not match those in your
+sources. Your line numbers would convey no useful information to us.
+@end itemize
+
+Here are some things that are not necessary:
+
+@itemize @bullet
+@item
+A description of the envelope of the bug.
+
+Often people who encounter a bug spend a lot of time investigating
+which changes to the input file will make the bug go away and which
+changes will not affect it.
+
+This is often time consuming and not very useful, because the way we
+will find the bug is by running a single example under the debugger
+with breakpoints, not by pure deduction from a series of examples.
+We recommend that you save your time for something else.
+
+Of course, if you can find a simpler example to report @emph{instead}
+of the original one, that is a convenience for us. Errors in the
+output will be easier to spot, running under the debugger will take
+less time, etc.
+
+However, simplification is not vital; if you do not want to do this,
+report the bug anyway and send us the entire test case you used.
+
+@item
+A patch for the bug.
+
+A patch for the bug does help us if it is a good one. But do not omit
+the necessary information, such as the test case, on the assumption that
+a patch is all we need. We might see problems with your patch and decide
+to fix the problem another way, or we might not understand it at all.
+
+Sometimes with a program as complicated as @value{GDBN} it is very hard to
+construct an example that will make the program follow a certain path
+through the code. If you do not send us the example, we will not be able
+to construct one, so we will not be able to verify that the bug is fixed.
+
+And if we cannot understand what bug you are trying to fix, or why your
+patch should be an improvement, we will not install it. A test case will
+help us to understand.
+
+@item
+A guess about what the bug is or what it depends on.
+
+Such guesses are usually wrong. Even we cannot guess right about such
+things without first using the debugger to find the facts.
+@end itemize
+
+@c The readline documentation is distributed with the readline code
+@c and consists of the two following files:
+@c rluser.texinfo
+@c inc-hist.texi
+@c Use -I with makeinfo to point to the appropriate directory,
+@c environment var TEXINPUTS with TeX.
+@include rluser.texinfo
+@include inc-hist.texi
+
+@ifset NOVEL
+@node Renamed Commands
+@appendix Renamed Commands
+
+The following commands were renamed in GDB 4, in order to make the
+command set as a whole more consistent and easier to use and remember:
+
+@kindex add-syms
+@kindex delete environment
+@kindex info copying
+@kindex info convenience
+@kindex info directories
+@kindex info editing
+@kindex info history
+@kindex info targets
+@kindex info values
+@kindex info version
+@kindex info warranty
+@kindex set addressprint
+@kindex set arrayprint
+@kindex set prettyprint
+@kindex set screen-height
+@kindex set screen-width
+@kindex set unionprint
+@kindex set vtblprint
+@kindex set demangle
+@kindex set asm-demangle
+@kindex set sevenbit-strings
+@kindex set array-max
+@kindex set caution
+@kindex set history write
+@kindex show addressprint
+@kindex show arrayprint
+@kindex show prettyprint
+@kindex show screen-height
+@kindex show screen-width
+@kindex show unionprint
+@kindex show vtblprint
+@kindex show demangle
+@kindex show asm-demangle
+@kindex show sevenbit-strings
+@kindex show array-max
+@kindex show caution
+@kindex show history write
+@kindex unset
+
+@c TEXI2ROFF-KILL
+@ifinfo
+@c END TEXI2ROFF-KILL
+@example
+OLD COMMAND NEW COMMAND
+@c TEXI2ROFF-KILL
+--------------- -------------------------------
+@c END TEXI2ROFF-KILL
+add-syms add-symbol-file
+delete environment unset environment
+info convenience show convenience
+info copying show copying
+info directories show directories
+info editing show commands
+info history show values
+info targets help target
+info values show values
+info version show version
+info warranty show warranty
+set/show addressprint set/show print address
+set/show array-max set/show print elements
+set/show arrayprint set/show print array
+set/show asm-demangle set/show print asm-demangle
+set/show caution set/show confirm
+set/show demangle set/show print demangle
+set/show history write set/show history save
+set/show prettyprint set/show print pretty
+set/show screen-height set/show height
+set/show screen-width set/show width
+set/show sevenbit-strings set/show print sevenbit-strings
+set/show unionprint set/show print union
+set/show vtblprint set/show print vtbl
+
+unset [No longer an alias for delete]
+@end example
+@c TEXI2ROFF-KILL
+@end ifinfo
+
+@tex
+\vskip \parskip\vskip \baselineskip
+\halign{\tt #\hfil &\qquad#&\tt #\hfil\cr
+{\bf Old Command} &&{\bf New Command}\cr
+add-syms &&add-symbol-file\cr
+delete environment &&unset environment\cr
+info convenience &&show convenience\cr
+info copying &&show copying\cr
+info directories &&show directories \cr
+info editing &&show commands\cr
+info history &&show values\cr
+info targets &&help target\cr
+info values &&show values\cr
+info version &&show version\cr
+info warranty &&show warranty\cr
+set{\rm / }show addressprint &&set{\rm / }show print address\cr
+set{\rm / }show array-max &&set{\rm / }show print elements\cr
+set{\rm / }show arrayprint &&set{\rm / }show print array\cr
+set{\rm / }show asm-demangle &&set{\rm / }show print asm-demangle\cr
+set{\rm / }show caution &&set{\rm / }show confirm\cr
+set{\rm / }show demangle &&set{\rm / }show print demangle\cr
+set{\rm / }show history write &&set{\rm / }show history save\cr
+set{\rm / }show prettyprint &&set{\rm / }show print pretty\cr
+set{\rm / }show screen-height &&set{\rm / }show height\cr
+set{\rm / }show screen-width &&set{\rm / }show width\cr
+set{\rm / }show sevenbit-strings &&set{\rm / }show print sevenbit-strings\cr
+set{\rm / }show unionprint &&set{\rm / }show print union\cr
+set{\rm / }show vtblprint &&set{\rm / }show print vtbl\cr
+\cr
+unset &&\rm(No longer an alias for delete)\cr
+}
+@end tex
+@c END TEXI2ROFF-KILL
+@end ifset
+
+@ifclear PRECONFIGURED
+@node Formatting Documentation
+@appendix Formatting Documentation
+
+@cindex GDB reference card
+@cindex reference card
+The GDB 4 release includes an already-formatted reference card, ready
+for printing with PostScript or GhostScript, in the @file{gdb}
+subdirectory of the main source directory@footnote{In
+@file{gdb-@value{GDBVN}/gdb/refcard.ps} of the version @value{GDBVN}
+release.}. If you can use PostScript or GhostScript with your printer,
+you can print the reference card immediately with @file{refcard.ps}.
+
+The release also includes the source for the reference card. You
+can format it, using @TeX{}, by typing:
+
+@example
+make refcard.dvi
+@end example
+
+The GDB reference card is designed to print in landscape mode on US
+``letter'' size paper; that is, on a sheet 11 inches wide by 8.5 inches
+high. You will need to specify this form of printing as an option to
+your @sc{dvi} output program.
+
+@cindex documentation
+
+All the documentation for GDB comes as part of the machine-readable
+distribution. The documentation is written in Texinfo format, which is
+a documentation system that uses a single source file to produce both
+on-line information and a printed manual. You can use one of the Info
+formatting commands to create the on-line version of the documentation
+and @TeX{} (or @code{texi2roff}) to typeset the printed version.
+
+GDB includes an already formatted copy of the on-line Info version of
+this manual in the @file{gdb} subdirectory. The main Info file is
+@file{gdb-@var{version-number}/gdb/gdb.info}, and it refers to
+subordinate files matching @samp{gdb.info*} in the same directory. If
+necessary, you can print out these files, or read them with any editor;
+but they are easier to read using the @code{info} subsystem in GNU Emacs
+or the standalone @code{info} program, available as part of the GNU
+Texinfo distribution.
+
+If you want to format these Info files yourself, you need one of the
+Info formatting programs, such as @code{texinfo-format-buffer} or
+@code{makeinfo}.
+
+If you have @code{makeinfo} installed, and are in the top level GDB
+source directory (@file{gdb-@value{GDBVN}}, in the case of version @value{GDBVN}), you can
+make the Info file by typing:
+
+@example
+cd gdb
+make gdb.info
+@end example
+
+If you want to typeset and print copies of this manual, you need @TeX{},
+a program to print its @sc{dvi} output files, and @file{texinfo.tex}, the
+Texinfo definitions file.
+
+@TeX{} is a typesetting program; it does not print files directly, but
+produces output files called @sc{dvi} files. To print a typeset
+document, you need a program to print @sc{dvi} files. If your system
+has @TeX{} installed, chances are it has such a program. The precise
+command to use depends on your system; @kbd{lpr -d} is common; another
+(for PostScript devices) is @kbd{dvips}. The @sc{dvi} print command may
+require a file name without any extension or a @samp{.dvi} extension.
+
+@TeX{} also requires a macro definitions file called
+@file{texinfo.tex}. This file tells @TeX{} how to typeset a document
+written in Texinfo format. On its own, @TeX{} cannot read, much less
+typeset a Texinfo file. @file{texinfo.tex} is distributed with GDB
+and is located in the @file{gdb-@var{version-number}/texinfo}
+directory.
+
+If you have @TeX{} and a @sc{dvi} printer program installed, you can
+typeset and print this manual. First switch to the the @file{gdb}
+subdirectory of the main source directory (for example, to
+@file{gdb-@value{GDBVN}/gdb}) and then type:
+
+@example
+make gdb.dvi
+@end example
+
+@node Installing GDB
+@appendix Installing GDB
+@cindex configuring GDB
+@cindex installation
+
+GDB comes with a @code{configure} script that automates the process
+of preparing GDB for installation; you can then use @code{make} to
+build the @code{gdb} program.
+@iftex
+@c irrelevant in info file; it's as current as the code it lives with.
+@footnote{If you have a more recent version of GDB than @value{GDBVN},
+look at the @file{README} file in the sources; we may have improved the
+installation procedures since publishing this manual.}
+@end iftex
+
+The GDB distribution includes all the source code you need for GDB in
+a single directory, whose name is usually composed by appending the
+version number to @samp{gdb}.
+
+For example, the GDB version @value{GDBVN} distribution is in the
+@file{gdb-@value{GDBVN}} directory. That directory contains:
+
+@table @code
+@item gdb-@value{GDBVN}/configure @r{(and supporting files)}
+script for configuring GDB and all its supporting libraries.
+
+@item gdb-@value{GDBVN}/gdb
+the source specific to GDB itself
+
+@item gdb-@value{GDBVN}/bfd
+source for the Binary File Descriptor library
+
+@item gdb-@value{GDBVN}/include
+GNU include files
+
+@item gdb-@value{GDBVN}/libiberty
+source for the @samp{-liberty} free software library
+
+@item gdb-@value{GDBVN}/opcodes
+source for the library of opcode tables and disassemblers
+
+@item gdb-@value{GDBVN}/readline
+source for the GNU command-line interface
+
+@item gdb-@value{GDBVN}/glob
+source for the GNU filename pattern-matching subroutine
+
+@item gdb-@value{GDBVN}/mmalloc
+source for the GNU memory-mapped malloc package
+@end table
+
+The simplest way to configure and build GDB is to run @code{configure}
+from the @file{gdb-@var{version-number}} source directory, which in
+this example is the @file{gdb-@value{GDBVN}} directory.
+
+First switch to the @file{gdb-@var{version-number}} source directory
+if you are not already in it; then run @code{configure}. Pass the
+identifier for the platform on which GDB will run as an
+argument.
+
+For example:
+
+@example
+cd gdb-@value{GDBVN}
+./configure @var{host}
+make
+@end example
+
+@noindent
+where @var{host} is an identifier such as @samp{sun4} or
+@samp{decstation}, that identifies the platform where GDB will run.
+(You can often leave off @var{host}; @code{configure} tries to guess the
+correct value by examining your system.)
+
+Running @samp{configure @var{host}} and then running @code{make} builds the
+@file{bfd}, @file{readline}, @file{mmalloc}, and @file{libiberty}
+libraries, then @code{gdb} itself. The configured source files, and the
+binaries, are left in the corresponding source directories.
+
+@code{configure} is a Bourne-shell (@code{/bin/sh}) script; if your
+system does not recognize this automatically when you run a different
+shell, you may need to run @code{sh} on it explicitly:
+
+@example
+sh configure @var{host}
+@end example
+
+If you run @code{configure} from a directory that contains source
+directories for multiple libraries or programs, such as the
+@file{gdb-@value{GDBVN}} source directory for version @value{GDBVN}, @code{configure}
+creates configuration files for every directory level underneath (unless
+you tell it not to, with the @samp{--norecursion} option).
+
+You can run the @code{configure} script from any of the
+subordinate directories in the GDB distribution if you only want to
+configure that subdirectory, but be sure to specify a path to it.
+
+For example, with version @value{GDBVN}, type the following to configure only
+the @code{bfd} subdirectory:
+
+@example
+@group
+cd gdb-@value{GDBVN}/bfd
+../configure @var{host}
+@end group
+@end example
+
+You can install @code{@value{GDBP}} anywhere; it has no hardwired paths.
+However, you should make sure that the shell on your path (named by
+the @samp{SHELL} environment variable) is publicly readable. Remember
+that GDB uses the shell to start your program---some systems refuse to
+let GDB debug child processes whose programs are not readable.
+
+@menu
+* Separate Objdir:: Compiling GDB in another directory
+* Config Names:: Specifying names for hosts and targets
+* configure Options:: Summary of options for configure
+@end menu
+
+@node Separate Objdir
+@section Compiling GDB in another directory
+
+If you want to run GDB versions for several host or target machines,
+you need a different @code{gdb} compiled for each combination of
+host and target. @code{configure} is designed to make this easy by
+allowing you to generate each configuration in a separate subdirectory,
+rather than in the source directory. If your @code{make} program
+handles the @samp{VPATH} feature (GNU @code{make} does), running
+@code{make} in each of these directories builds the @code{gdb}
+program specified there.
+
+To build @code{gdb} in a separate directory, run @code{configure}
+with the @samp{--srcdir} option to specify where to find the source.
+(You also need to specify a path to find @code{configure}
+itself from your working directory. If the path to @code{configure}
+would be the same as the argument to @samp{--srcdir}, you can leave out
+the @samp{--srcdir} option; it will be assumed.)
+
+For example, with version @value{GDBVN}, you can build GDB in a separate
+directory for a Sun 4 like this:
+
+@example
+@group
+cd gdb-@value{GDBVN}
+mkdir ../gdb-sun4
+cd ../gdb-sun4
+../gdb-@value{GDBVN}/configure sun4
+make
+@end group
+@end example
+
+When @code{configure} builds a configuration using a remote source
+directory, it creates a tree for the binaries with the same structure
+(and using the same names) as the tree under the source directory. In
+the example, you'd find the Sun 4 library @file{libiberty.a} in the
+directory @file{gdb-sun4/libiberty}, and GDB itself in
+@file{gdb-sun4/gdb}.
+
+One popular reason to build several GDB configurations in separate
+directories is to configure GDB for cross-compiling (where GDB
+runs on one machine---the host---while debugging programs that run on
+another machine---the target). You specify a cross-debugging target by
+giving the @samp{--target=@var{target}} option to @code{configure}.
+
+When you run @code{make} to build a program or library, you must run
+it in a configured directory---whatever directory you were in when you
+called @code{configure} (or one of its subdirectories).
+
+The @code{Makefile} that @code{configure} generates in each source
+directory also runs recursively. If you type @code{make} in a source
+directory such as @file{gdb-@value{GDBVN}} (or in a separate configured
+directory configured with @samp{--srcdir=@var{path}/gdb-@value{GDBVN}}), you
+will build all the required libraries, and then build GDB.
+
+When you have multiple hosts or targets configured in separate
+directories, you can run @code{make} on them in parallel (for example,
+if they are NFS-mounted on each of the hosts); they will not interfere
+with each other.
+
+@node Config Names
+@section Specifying names for hosts and targets
+
+The specifications used for hosts and targets in the @code{configure}
+script are based on a three-part naming scheme, but some short predefined
+aliases are also supported. The full naming scheme encodes three pieces
+of information in the following pattern:
+
+@example
+@var{architecture}-@var{vendor}-@var{os}
+@end example
+
+For example, you can use the alias @code{sun4} as a @var{host} argument,
+or as the value for @var{target} in a @code{--target=@var{target}}
+option. The equivalent full name is @samp{sparc-sun-sunos4}.
+
+The @code{configure} script accompanying GDB does not provide
+any query facility to list all supported host and target names or
+aliases. @code{configure} calls the Bourne shell script
+@code{config.sub} to map abbreviations to full names; you can read the
+script, if you wish, or you can use it to test your guesses on
+abbreviations---for example:
+
+@smallexample
+% sh config.sub sun4
+sparc-sun-sunos4.1.1
+% sh config.sub sun3
+m68k-sun-sunos4.1.1
+% sh config.sub decstation
+mips-dec-ultrix4.2
+% sh config.sub hp300bsd
+m68k-hp-bsd
+% sh config.sub i386v
+i386-unknown-sysv
+% sh config.sub i786v
+Invalid configuration `i786v': machine `i786v' not recognized
+@end smallexample
+
+@noindent
+@code{config.sub} is also distributed in the GDB source
+directory (@file{gdb-@value{GDBVN}}, for version @value{GDBVN}).
+
+@node configure Options
+@section @code{configure} options
+
+Here is a summary of the @code{configure} options and arguments that
+are most often useful for building @value{GDBN}. @code{configure} also has
+several other options not listed here. @inforef{What Configure
+Does,,configure.info}, for a full explanation of @code{configure}.
+@c FIXME: Would this be more, or less, useful as an xref (ref to printed
+@c manual in the printed manual, ref to info file only from the info file)?
+
+@example
+configure @r{[}--help@r{]}
+ @r{[}--prefix=@var{dir}@r{]}
+ @r{[}--srcdir=@var{path}@r{]}
+ @r{[}--norecursion@r{]} @r{[}--rm@r{]}
+ @r{[}--target=@var{target}@r{]} @var{host}
+@end example
+
+@noindent
+You may introduce options with a single @samp{-} rather than
+@samp{--} if you prefer; but you may abbreviate option names if you use
+@samp{--}.
+
+@table @code
+@item --help
+Display a quick summary of how to invoke @code{configure}.
+
+@item -prefix=@var{dir}
+Configure the source to install programs and files under directory
+@file{@var{dir}}.
+
+@item --srcdir=@var{path}
+@strong{Warning: using this option requires GNU @code{make}, or another
+@code{make} that implements the @code{VPATH} feature.}@*
+Use this option to make configurations in directories separate from the
+GDB source directories. Among other things, you can use this to
+build (or maintain) several configurations simultaneously, in separate
+directories. @code{configure} writes configuration specific files in
+the current directory, but arranges for them to use the source in the
+directory @var{path}. @code{configure} will create directories under
+the working directory in parallel to the source directories below
+@var{path}.
+
+@item --norecursion
+Configure only the directory level where @code{configure} is executed; do not
+propagate configuration to subdirectories.
+
+@item --rm
+@emph{Remove} files otherwise built during configuration.
+
+@c This does not work (yet if ever). FIXME.
+@c @item --parse=@var{lang} @dots{}
+@c Configure the GDB expression parser to parse the listed languages.
+@c @samp{all} configures GDB for all supported languages. To get a
+@c list of all supported languages, omit the argument. Without this
+@c option, GDB is configured to parse all supported languages.
+
+@item --target=@var{target}
+Configure GDB for cross-debugging programs running on the specified
+@var{target}. Without this option, GDB is configured to debug
+programs that run on the same machine (@var{host}) as GDB itself.
+
+There is no convenient way to generate a list of all available targets.
+
+@item @var{host} @dots{}
+Configure GDB to run on the specified @var{host}.
+
+There is no convenient way to generate a list of all available hosts.
+@end table
+
+@noindent
+@code{configure} accepts other options, for compatibility with
+configuring other GNU tools recursively; but these are the only
+options that affect GDB or its supporting libraries.
+@end ifclear
+
+@node Index
+@unnumbered Index
+
+@printindex cp
+
+@tex
+% I think something like @colophon should be in texinfo. In the
+% meantime:
+\long\def\colophon{\hbox to0pt{}\vfill
+\centerline{The body of this manual is set in}
+\centerline{\fontname\tenrm,}
+\centerline{with headings in {\bf\fontname\tenbf}}
+\centerline{and examples in {\tt\fontname\tentt}.}
+\centerline{{\it\fontname\tenit\/},}
+\centerline{{\bf\fontname\tenbf}, and}
+\centerline{{\sl\fontname\tensl\/}}
+\centerline{are used for emphasis.}\vfill}
+\page\colophon
+% Blame: pesch@cygnus.com, 1991.
+@end tex
+
+@contents
+@bye
diff --git a/gnu/usr.bin/gdb/doc/gdbint.texinfo b/gnu/usr.bin/gdb/doc/gdbint.texinfo
new file mode 100644
index 0000000..a2f6257
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/gdbint.texinfo
@@ -0,0 +1,2658 @@
+\input texinfo
+@setfilename gdbint.info
+@c $Id: gdbint.texinfo,v 1.1.1.1 1993/10/30 21:59:41 jkh Exp $
+
+@ifinfo
+@format
+START-INFO-DIR-ENTRY
+* Gdb-Internals: (gdbint). The GNU debugger's internals.
+END-INFO-DIR-ENTRY
+@end format
+@end ifinfo
+
+@ifinfo
+This file documents the internals of the GNU debugger GDB.
+
+Copyright 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+Contributed by Cygnus Support. Written by John Gilmore.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+@ignore
+Permission is granted to process this file through Tex and print the
+results, provided the printed document carries copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+@end ignore
+Permission is granted to copy or distribute modified versions of this
+manual under the terms of the GPL (for which purpose this text may be
+regarded as a program in the language TeX).
+@end ifinfo
+
+@setchapternewpage off
+@settitle GDB Internals
+@titlepage
+@title{Working in GDB}
+@subtitle{A guide to the internals of the GNU debugger}
+@author John Gilmore
+@author Cygnus Support
+@page
+@tex
+\def\$#1${{#1}} % Kluge: collect RCS revision info without $...$
+\xdef\manvers{\$Revision: 1.1.1.1 $} % For use in headers, footers too
+{\parskip=0pt
+\hfill Cygnus Support\par
+\hfill \manvers\par
+\hfill \TeX{}info \texinfoversion\par
+}
+@end tex
+
+@vskip 0pt plus 1filll
+Copyright @copyright{} 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+@end titlepage
+
+@node Top
+@c Perhaps this should be the title of the document (but only for info,
+@c not for TeX). Existing GNU manuals seem inconsistent on this point.
+@top Scope of this Document
+
+This document documents the internals of the GNU debugger, GDB. It is
+intended to document aspects of GDB which apply across many different
+parts of GDB (for example, @pxref{Coding Style}), or which are global
+aspects of design (for example, what are the major modules and which
+files document them in detail?). Information which pertains to specific
+data structures, functions, variables, etc., should be put in comments
+in the source code, not here. It is more likely to get noticed and kept
+up to date there. Some of the information in this document should
+probably be moved into comments.
+
+@menu
+* README:: The README File
+* Getting Started:: Getting started working on GDB
+* Debugging GDB:: Debugging GDB with itself
+* New Architectures:: Defining a New Host or Target Architecture
+* Config:: Adding a New Configuration
+* Host:: Adding a New Host
+* Native:: Adding a New Native Configuration
+* Target:: Adding a New Target
+* Languages:: Defining New Source Languages
+* Releases:: Configuring GDB for Release
+* Partial Symbol Tables:: How GDB reads symbols quickly at startup
+* Types:: How GDB keeps track of types
+* BFD support for GDB:: How BFD and GDB interface
+* Symbol Reading:: Defining New Symbol Readers
+* Cleanups:: Cleanups
+* Wrapping:: Wrapping Output Lines
+* Frames:: Keeping track of function calls
+* Remote Stubs:: Code that runs in targets and talks to GDB
+* Longjmp Support:: Stepping through longjmp's in the target
+* Coding Style:: Strunk and White for GDB maintainers
+* Clean Design:: Frank Lloyd Wright for GDB maintainers
+* Submitting Patches:: How to get your changes into GDB releases
+* Host Conditionals:: What features exist in the host
+* Target Conditionals:: What features exist in the target
+* Native Conditionals:: Conditionals for when host and target are same
+* Obsolete Conditionals:: Conditionals that don't exist any more
+* XCOFF:: The Object file format used on IBM's RS/6000
+@end menu
+
+@node README
+@chapter The @file{README} File
+
+Check the @file{README} file, it often has useful information that does not
+appear anywhere else in the directory.
+
+@node Getting Started
+@chapter Getting Started Working on GDB
+
+GDB is a large and complicated program, and if you first starting to
+work on it, it can be hard to know where to start. Fortunately, if you
+know how to go about it, there are ways to figure out what is going on:
+
+@itemize @bullet
+@item
+This manual, the GDB Internals manual, has information which applies
+generally to many parts of GDB.
+
+@item
+Information about particular functions or data structures are located in
+comments with those functions or data structures. If you run across a
+function or a global variable which does not have a comment correctly
+explaining what is does, this can be thought of as a bug in GDB; feel
+free to submit a bug report, with a suggested comment if you can figure
+out what the comment should say (@pxref{Submitting Patches}). If you
+find a comment which is actually wrong, be especially sure to report that.
+
+Comments explaining the function of macros defined in host, target, or
+native dependent files can be in several places. Sometimes they are
+repeated every place the macro is defined. Sometimes they are where the
+macro is used. Sometimes there is a header file which supplies a
+default definition of the macro, and the comment is there. This manual
+also has a list of macros (@pxref{Host Conditionals}, @pxref{Target
+Conditionals}, @pxref{Native Conditionals}, and @pxref{Obsolete
+Conditionals}) with some documentation.
+
+@item
+Start with the header files. Once you some idea of how GDB's internal
+symbol tables are stored (see @file{symtab.h}, @file{gdbtypes.h}), you
+will find it much easier to understand the code which uses and creates
+those symbol tables.
+
+@item
+You may wish to process the information you are getting somehow, to
+enhance your understanding of it. Summarize it, translate it to another
+language, add some (perhaps trivial or non-useful) feature to GDB, use
+the code to predict what a test case would do and write the test case
+and verify your prediction, etc. If you are reading code and your eyes
+are starting to glaze over, this is a sign you need to use a more active
+approach.
+
+@item
+Once you have a part of GDB to start with, you can find more
+specifically the part you are looking for by stepping through each
+function with the @code{next} command. Do not use @code{step} or you
+will quickly get distracted; when the function you are stepping through
+calls another function try only to get a big-picture understanding
+(perhaps using the comment at the beginning of the function being
+called) of what it does. This way you can identify which of the
+functions being called by the function you are stepping through is the
+one which you are interested in. You may need to examine the data
+structures generated at each stage, with reference to the comments in
+the header files explaining what the data structures are supposed to
+look like.
+
+Of course, this same technique can be used if you are just reading the
+code, rather than actually stepping through it. The same general
+principle applies---when the code you are looking at calls something
+else, just try to understand generally what the code being called does,
+rather than worrying about all its details.
+
+@item
+A good place to start when tracking down some particular area is with a
+command which invokes that feature. Suppose you want to know how
+single-stepping works. As a GDB user, you know that the @code{step}
+command invokes single-stepping. The command is invoked via command
+tables (see @file{command.h}); by convention the function which actually
+performs the command is formed by taking the name of the command and
+adding @samp{_command}, or in the case of an @code{info} subcommand,
+@samp{_info}. For example, the @code{step} command invokes the
+@code{step_command} function and the @code{info display} command invokes
+@code{display_info}. When this convention is not followed, you might
+have to use @code{grep} or @kbd{M-x tags-search} in emacs, or run GDB on
+itself and set a breakpoint in @code{execute_command}.
+
+@item
+If all of the above fail, it may be appropriate to ask for information
+on @code{bug-gdb}. But @emph{never} post a generic question like ``I was
+wondering if anyone could give me some tips about understanding
+GDB''---if we had some magic secret we would put it in this manual.
+Suggestions for improving the manual are always welcome, of course.
+@end itemize
+
+Good luck!
+
+@node Debugging GDB
+@chapter Debugging GDB with itself
+If gdb is limping on your machine, this is the preferred way to get it
+fully functional. Be warned that in some ancient Unix systems, like
+Ultrix 4.2, a program can't be running in one process while it is being
+debugged in another. Rather than typing the command @code{@w{./gdb
+./gdb}}, which works on Suns and such, you can copy @file{gdb} to
+@file{gdb2} and then type @code{@w{./gdb ./gdb2}}.
+
+When you run gdb in the gdb source directory, it will read a
+@file{.gdbinit} file that sets up some simple things to make debugging
+gdb easier. The @code{info} command, when executed without a subcommand
+in a gdb being debugged by gdb, will pop you back up to the top level
+gdb. See @file{.gdbinit} for details.
+
+If you use emacs, you will probably want to do a @code{make TAGS} after
+you configure your distribution; this will put the machine dependent
+routines for your local machine where they will be accessed first by
+@kbd{M-.}
+
+Also, make sure that you've either compiled gdb with your local cc, or
+have run @code{fixincludes} if you are compiling with gcc.
+
+@node New Architectures
+@chapter Defining a New Host or Target Architecture
+
+When building support for a new host and/or target, much of the work you
+need to do is handled by specifying configuration files;
+@pxref{Config,,Adding a New Configuration}. Further work can be
+divided into ``host-dependent'' (@pxref{Host,,Adding a New Host}) and
+``target-dependent'' (@pxref{Target,,Adding a New Target}). The
+following discussion is meant to explain the difference between hosts
+and targets.
+
+@heading What is considered ``host-dependent'' versus ``target-dependent''?
+
+@dfn{Host} refers to attributes of the system where GDB runs.
+@dfn{Target} refers to the system where the program being debugged
+executes. In most cases they are the same machine, in which case
+a third type of @dfn{Native} attributes come into play.
+
+Defines and include files needed to build on the host are host support.
+Examples are tty support, system defined types, host byte order, host
+float format.
+
+Defines and information needed to handle the target format are target
+dependent. Examples are the stack frame format, instruction set,
+breakpoint instruction, registers, and how to set up and tear down the stack
+to call a function.
+
+Information that is only needed when the host and target are the same,
+is native dependent. One example is Unix child process support; if the
+host and target are not the same, doing a fork to start the target
+process is a bad idea. The various macros needed for finding the
+registers in the @code{upage}, running @code{ptrace}, and such are all in the
+native-dependent files.
+
+Another example of native-dependent code is support for features
+that are really part of the target environment, but which require
+@code{#include} files that are only available on the host system.
+Core file handling and @code{setjmp} handling are two common cases.
+
+When you want to make GDB work ``native'' on a particular
+machine, you have to include all three kinds of information.
+
+The dependent information in GDB is organized into files by naming
+conventions.
+
+Host-Dependent Files
+@table @file
+@item config/*/*.mh
+Sets Makefile parameters
+@item config/*/xm-*.h
+Global #include's and #define's and definitions
+@item *-xdep.c
+Global variables and functions
+@end table
+
+Native-Dependent Files
+@table @file
+@item config/*/*.mh
+Sets Makefile parameters (for @emph{both} host and native)
+@item config/*/nm-*.h
+#include's and #define's and definitions. This file
+is only included by the small number of modules that need it,
+so beware of doing feature-test #define's from its macros.
+@item *-nat.c
+global variables and functions
+@end table
+
+Target-Dependent Files
+@table @file
+@item config/*/*.mt
+Sets Makefile parameters
+@item config/*/tm-*.h
+Global #include's and #define's and definitions
+@item *-tdep.c
+Global variables and functions
+@end table
+
+At this writing, most supported hosts have had their host and native
+dependencies sorted out properly. There are a few stragglers, which
+can be recognized by the absence of NATDEPFILES lines in their
+@file{config/*/*.mh}.
+
+@node Config
+@chapter Adding a New Configuration
+
+Most of the work in making GDB compile on a new machine is in specifying
+the configuration of the machine. This is done in a dizzying variety of
+header files and configuration scripts, which we hope to make more
+sensible soon. Let's say your new host is called an @var{xxx} (e.g.
+@samp{sun4}), and its full three-part configuration name is
+@code{@var{xarch}-@var{xvend}-@var{xos}} (e.g. @samp{sparc-sun-sunos4}). In
+particular:
+
+In the top level directory, edit @file{config.sub} and add @var{xarch},
+@var{xvend}, and @var{xos} to the lists of supported architectures,
+vendors, and operating systems near the bottom of the file. Also, add
+@var{xxx} as an alias that maps to
+@code{@var{xarch}-@var{xvend}-@var{xos}}. You can test your changes by
+running
+
+@example
+./config.sub @var{xxx}
+@end example
+@noindent
+and
+@example
+./config.sub @code{@var{xarch}-@var{xvend}-@var{xos}}
+@end example
+@noindent
+which should both respond with @code{@var{xarch}-@var{xvend}-@var{xos}}
+and no error messages.
+
+Now, go to the @file{bfd} directory and
+create a new file @file{bfd/hosts/h-@var{xxx}.h}. Examine the
+other @file{h-*.h} files as templates, and create one that brings in the
+right include files for your system, and defines any host-specific
+macros needed by BFD, the Binutils, GNU LD, or the Opcodes directories.
+(They all share the bfd @file{hosts} directory and the @file{configure.host}
+file.)
+
+Then edit @file{bfd/configure.host}. Add a line to recognize your
+@code{@var{xarch}-@var{xvend}-@var{xos}} configuration, and set
+@code{my_host} to @var{xxx} when you recognize it. This will cause your
+file @file{h-@var{xxx}.h} to be linked to @file{sysdep.h} at configuration
+time. When creating the line that recognizes your configuration,
+only match the fields that you really need to match; e.g. don't match
+match the architecture or manufacturer if the OS is sufficient
+to distinguish the configuration that your @file{h-@var{xxx}.h} file supports.
+Don't match the manufacturer name unless you really need to.
+This should make future ports easier.
+
+Also, if this host requires any changes to the Makefile, create a file
+@file{bfd/config/@var{xxx}.mh}, which includes the required lines.
+
+It's possible that the @file{libiberty} and @file{readline} directories
+won't need any changes for your configuration, but if they do, you can
+change the @file{configure.in} file there to recognize your system and
+map to an @file{mh-@var{xxx}} file. Then add @file{mh-@var{xxx}}
+to the @file{config/} subdirectory, to set any makefile variables you
+need. The only current options in there are things like @samp{-DSYSV}.
+(This @file{mh-@var{xxx}} naming convention differs from elsewhere
+in GDB, by historical accident. It should be cleaned up so that all
+such files are called @file{@var{xxx}.mh}.)
+
+Aha! Now to configure GDB itself! Edit
+@file{gdb/configure.in} to recognize your system and set @code{gdb_host}
+to @var{xxx}, and (unless your desired target is already available) also
+set @code{gdb_target} to something appropriate (for instance,
+@var{xxx}). To handle new hosts, modify the segment after the comment
+@samp{# per-host}; to handle new targets, modify after @samp{#
+per-target}.
+@c Would it be simpler to just use different per-host and per-target
+@c *scripts*, and call them from {configure} ?
+
+Finally, you'll need to specify and define GDB's host-, native-, and
+target-dependent @file{.h} and @file{.c} files used for your
+configuration; the next two chapters discuss those.
+
+
+@node Host
+@chapter Adding a New Host
+
+Once you have specified a new configuration for your host
+(@pxref{Config,,Adding a New Configuration}), there are three remaining
+pieces to making GDB work on a new machine. First, you have to make it
+host on the new machine (compile there, handle that machine's terminals
+properly, etc). If you will be cross-debugging to some other kind of
+system that's already supported, you are done.
+
+If you want to use GDB to debug programs that run on the new machine,
+you have to get it to understand the machine's object files, symbol
+files, and interfaces to processes; @pxref{Target,,Adding a New Target}
+and @pxref{Native,,Adding a New Native Configuration}
+
+Several files control GDB's configuration for host systems:
+
+@table @file
+@item gdb/config/@var{arch}/@var{xxx}.mh
+Specifies Makefile fragments needed when hosting on machine @var{xxx}.
+In particular, this lists the required machine-dependent object files,
+by defining @samp{XDEPFILES=@dots{}}. Also
+specifies the header file which describes host @var{xxx}, by defining
+@code{XM_FILE= xm-@var{xxx}.h}. You can also define @code{CC},
+@code{REGEX} and @code{REGEX1}, @code{SYSV_DEFINE}, @code{XM_CFLAGS},
+@code{XM_ADD_FILES}, @code{XM_CLIBS}, @code{XM_CDEPS},
+etc.; see @file{Makefile.in}.
+
+@item gdb/config/@var{arch}/xm-@var{xxx}.h
+(@file{xm.h} is a link to this file, created by configure).
+Contains C macro definitions describing the host system environment,
+such as byte order, host C compiler and library, ptrace support,
+and core file structure. Crib from existing @file{xm-*.h} files
+to create a new one.
+
+@item gdb/@var{xxx}-xdep.c
+Contains any miscellaneous C code required for this machine
+as a host. On many machines it doesn't exist at all. If it does
+exist, put @file{@var{xxx}-xdep.o} into the @code{XDEPFILES} line
+in @file{gdb/config/mh-@var{xxx}}.
+@end table
+
+@subheading Generic Host Support Files
+
+There are some ``generic'' versions of routines that can be used by
+various systems. These can be customized in various ways by macros
+defined in your @file{xm-@var{xxx}.h} file. If these routines work for
+the @var{xxx} host, you can just include the generic file's name (with
+@samp{.o}, not @samp{.c}) in @code{XDEPFILES}.
+
+Otherwise, if your machine needs custom support routines, you will need
+to write routines that perform the same functions as the generic file.
+Put them into @code{@var{xxx}-xdep.c}, and put @code{@var{xxx}-xdep.o}
+into @code{XDEPFILES}.
+
+@table @file
+@item ser-bsd.c
+This contains serial line support for Berkeley-derived Unix systems.
+
+@item ser-go32.c
+This contains serial line support for 32-bit programs running under DOS
+using the GO32 execution environment.
+
+@item ser-termios.c
+This contains serial line support for System V-derived Unix systems.
+@end table
+
+Now, you are now ready to try configuring GDB to compile using your system
+as its host. From the top level (above @file{bfd}, @file{gdb}, etc), do:
+
+@example
+./configure @var{xxx} +target=vxworks960
+@end example
+
+This will configure your system to cross-compile for VxWorks on
+the Intel 960, which is probably not what you really want, but it's
+a test case that works at this stage. (You haven't set up to be
+able to debug programs that run @emph{on} @var{xxx} yet.)
+
+If this succeeds, you can try building it all with:
+
+@example
+make
+@end example
+
+Repeat until the program configures, compiles, links, and runs.
+When run, it won't be able to do much (unless you have a VxWorks/960
+board on your network) but you will know that the host support is
+pretty well done.
+
+Good luck! Comments and suggestions about this section are particularly
+welcome; send them to @samp{bug-gdb@@prep.ai.mit.edu}.
+
+@node Native
+@chapter Adding a New Native Configuration
+
+If you are making GDB run native on the @var{xxx} machine, you have
+plenty more work to do. Several files control GDB's configuration for
+native support:
+
+@table @file
+@item gdb/config/@var{xarch}/@var{xxx}.mh
+Specifies Makefile fragments needed when hosting @emph{or native}
+on machine @var{xxx}.
+In particular, this lists the required native-dependent object files,
+by defining @samp{NATDEPFILES=@dots{}}. Also
+specifies the header file which describes native support on @var{xxx},
+by defining @samp{NAT_FILE= nm-@var{xxx}.h}.
+You can also define @samp{NAT_CFLAGS},
+@samp{NAT_ADD_FILES}, @samp{NAT_CLIBS}, @samp{NAT_CDEPS},
+etc.; see @file{Makefile.in}.
+
+@item gdb/config/@var{arch}/nm-@var{xxx}.h
+(@file{nm.h} is a link to this file, created by configure).
+Contains C macro definitions describing the native system environment,
+such as child process control and core file support.
+Crib from existing @file{nm-*.h} files to create a new one.
+
+@item gdb/@var{xxx}-nat.c
+Contains any miscellaneous C code required for this native support
+of this machine. On some machines it doesn't exist at all.
+@end table
+
+@subheading Generic Native Support Files
+
+There are some ``generic'' versions of routines that can be used by
+various systems. These can be customized in various ways by macros
+defined in your @file{nm-@var{xxx}.h} file. If these routines work for
+the @var{xxx} host, you can just include the generic file's name (with
+@samp{.o}, not @samp{.c}) in @code{NATDEPFILES}.
+
+Otherwise, if your machine needs custom support routines, you will need
+to write routines that perform the same functions as the generic file.
+Put them into @code{@var{xxx}-nat.c}, and put @code{@var{xxx}-nat.o}
+into @code{NATDEPFILES}.
+
+@table @file
+
+@item inftarg.c
+This contains the @emph{target_ops vector} that supports Unix child
+processes on systems which use ptrace and wait to control the child.
+
+@item procfs.c
+This contains the @emph{target_ops vector} that supports Unix child
+processes on systems which use /proc to control the child.
+
+@item fork-child.c
+This does the low-level grunge that uses Unix system calls
+to do a "fork and exec" to start up a child process.
+
+@item infptrace.c
+This is the low level interface to inferior processes for systems
+using the Unix @code{ptrace} call in a vanilla way.
+
+@item coredep.c::fetch_core_registers()
+Support for reading registers out of a core file. This routine calls
+@code{register_addr()}, see below.
+Now that BFD is used to read core files, virtually all machines should
+use @code{coredep.c}, and should just provide @code{fetch_core_registers} in
+@code{@var{xxx}-nat.c} (or @code{REGISTER_U_ADDR} in @code{nm-@var{xxx}.h}).
+
+@item coredep.c::register_addr()
+If your @code{nm-@var{xxx}.h} file defines the macro
+@code{REGISTER_U_ADDR(addr, blockend, regno)}, it should be defined to
+set @code{addr} to the offset within the @samp{user}
+struct of GDB register number @code{regno}. @code{blockend} is the
+offset within the ``upage'' of @code{u.u_ar0}.
+If @code{REGISTER_U_ADDR} is defined,
+@file{coredep.c} will define the @code{register_addr()} function and use
+the macro in it. If you do not define @code{REGISTER_U_ADDR}, but you
+are using the standard @code{fetch_core_registers()}, you will need to
+define your own version of @code{register_addr()}, put it into your
+@code{@var{xxx}-nat.c} file, and be sure @code{@var{xxx}-nat.o} is in
+the @code{NATDEPFILES} list. If you have your own
+@code{fetch_core_registers()}, you may not need a separate
+@code{register_addr()}. Many custom @code{fetch_core_registers()}
+implementations simply locate the registers themselves.@refill
+@end table
+
+When making GDB run native on a new operating system,
+to make it possible to debug
+core files, you will need to either write specific code for parsing your
+OS's core files, or customize @file{bfd/trad-core.c}. First, use
+whatever @code{#include} files your machine uses to define the struct of
+registers that is accessible (possibly in the u-area) in a core file
+(rather than @file{machine/reg.h}), and an include file that defines whatever
+header exists on a core file (e.g. the u-area or a @samp{struct core}). Then
+modify @code{trad_unix_core_file_p()} to use these values to set up the
+section information for the data segment, stack segment, any other
+segments in the core file (perhaps shared library contents or control
+information), ``registers'' segment, and if there are two discontiguous
+sets of registers (e.g. integer and float), the ``reg2'' segment. This
+section information basically delimits areas in the core file in a
+standard way, which the section-reading routines in BFD know how to seek
+around in.
+
+Then back in GDB, you need a matching routine called
+@code{fetch_core_registers()}. If you can use the generic one, it's in
+@file{coredep.c}; if not, it's in your @file{@var{xxx}-nat.c} file.
+It will be passed a char pointer to the entire ``registers'' segment,
+its length, and a zero; or a char pointer to the entire ``regs2''
+segment, its length, and a 2. The routine should suck out the supplied
+register values and install them into GDB's ``registers'' array.
+(@xref{New Architectures,,Defining a New Host or Target Architecture},
+for more info about this.)
+
+If your system uses @file{/proc} to control processes, and uses ELF
+format core files, then you may be able to use the same routines
+for reading the registers out of processes and out of core files.
+
+@node Target
+@chapter Adding a New Target
+
+For a new target called @var{ttt}, first specify the configuration as
+described in @ref{Config,,Adding a New Configuration}. If your new
+target is the same as your new host, you've probably already done that.
+
+A variety of files specify attributes of the GDB target environment:
+
+@table @file
+@item gdb/config/@var{arch}/@var{ttt}.mt
+Contains a Makefile fragment specific to this target.
+Specifies what object files are needed for target @var{ttt}, by
+defining @samp{TDEPFILES=@dots{}}.
+Also specifies the header file which describes @var{ttt}, by defining
+@samp{TM_FILE= tm-@var{ttt}.h}. You can also define @samp{TM_CFLAGS},
+@samp{TM_CLIBS}, @samp{TM_CDEPS},
+and other Makefile variables here; see @file{Makefile.in}.
+
+@item gdb/config/@var{arch}/tm-@var{ttt}.h
+(@file{tm.h} is a link to this file, created by configure).
+Contains macro definitions about the target machine's
+registers, stack frame format and instructions.
+Crib from existing @file{tm-*.h} files when building a new one.
+
+@item gdb/@var{ttt}-tdep.c
+Contains any miscellaneous code required for this target machine.
+On some machines it doesn't exist at all. Sometimes the macros
+in @file{tm-@var{ttt}.h} become very complicated, so they are
+implemented as functions here instead, and the macro is simply
+defined to call the function.
+
+@item gdb/exec.c
+Defines functions for accessing files that are
+executable on the target system. These functions open and examine an
+exec file, extract data from one, write data to one, print information
+about one, etc. Now that executable files are handled with BFD, every
+target should be able to use the generic exec.c rather than its
+own custom code.
+
+@item gdb/@var{arch}-pinsn.c
+Prints (disassembles) the target machine's instructions.
+This file is usually shared with other target machines which use the
+same processor, which is why it is @file{@var{arch}-pinsn.c} rather
+than @file{@var{ttt}-pinsn.c}.
+
+@item gdb/@var{arch}-opcode.h
+Contains some large initialized
+data structures describing the target machine's instructions.
+This is a bit strange for a @file{.h} file, but it's OK since
+it is only included in one place. @file{@var{arch}-opcode.h} is shared
+between the debugger and the assembler, if the GNU assembler has been
+ported to the target machine.
+
+@item gdb/config/@var{arch}/tm-@var{arch}.h
+This often exists to describe the basic layout of the target machine's
+processor chip (registers, stack, etc).
+If used, it is included by @file{tm-@var{xxx}.h}. It can
+be shared among many targets that use the same processor.
+
+@item gdb/@var{arch}-tdep.c
+Similarly, there are often common subroutines that are shared by all
+target machines that use this particular architecture.
+@end table
+
+When adding support for a new target machine, there are various areas
+of support that might need change, or might be OK.
+
+If you are using an existing object file format (a.out or COFF),
+there is probably little to be done. See @file{bfd/doc/bfd.texinfo}
+for more information on writing new a.out or COFF versions.
+
+If you need to add a new object file format, you must first add it to
+BFD. This is beyond the scope of this document right now. Basically
+you must build a transfer vector (of type @code{bfd_target}), which will
+mean writing all the required routines, and add it to the list in
+@file{bfd/targets.c}.
+
+You must then arrange for the BFD code to provide access to the
+debugging symbols. Generally GDB will have to call swapping routines
+from BFD and a few other BFD internal routines to locate the debugging
+information. As much as possible, GDB should not depend on the BFD
+internal data structures.
+
+For some targets (e.g., COFF), there is a special transfer vector used
+to call swapping routines, since the external data structures on various
+platforms have different sizes and layouts. Specialized routines that
+will only ever be implemented by one object file format may be called
+directly. This interface should be described in a file
+@file{bfd/libxxx.h}, which is included by GDB.
+
+If you are adding a new operating system for an existing CPU chip, add a
+@file{tm-@var{xos}.h} file that describes the operating system
+facilities that are unusual (extra symbol table info; the breakpoint
+instruction needed; etc). Then write a
+@file{tm-@var{xarch}-@var{xos}.h} that just @code{#include}s
+@file{tm-@var{xarch}.h} and @file{tm-@var{xos}.h}. (Now that we have
+three-part configuration names, this will probably get revised to
+separate the @var{xos} configuration from the @var{xarch}
+configuration.)
+
+
+@node Languages
+@chapter Adding a Source Language to GDB
+
+To add other languages to GDB's expression parser, follow the following steps:
+
+@table @emph
+@item Create the expression parser.
+
+This should reside in a file @file{@var{lang}-exp.y}. Routines for building
+parsed expressions into a @samp{union exp_element} list are in @file{parse.c}.
+
+Since we can't depend upon everyone having Bison, and YACC produces
+parsers that define a bunch of global names, the following lines
+@emph{must} be included at the top of the YACC parser, to prevent
+the various parsers from defining the same global names:
+
+@example
+#define yyparse @var{lang}_parse
+#define yylex @var{lang}_lex
+#define yyerror @var{lang}_error
+#define yylval @var{lang}_lval
+#define yychar @var{lang}_char
+#define yydebug @var{lang}_debug
+#define yypact @var{lang}_pact
+#define yyr1 @var{lang}_r1
+#define yyr2 @var{lang}_r2
+#define yydef @var{lang}_def
+#define yychk @var{lang}_chk
+#define yypgo @var{lang}_pgo
+#define yyact @var{lang}_act
+#define yyexca @var{lang}_exca
+#define yyerrflag @var{lang}_errflag
+#define yynerrs @var{lang}_nerrs
+@end example
+
+At the bottom of your parser, define a @code{struct language_defn} and
+initialize it with the right values for your language. Define an
+@code{initialize_@var{lang}} routine and have it call
+@samp{add_language(@var{lang}_language_defn)} to tell the rest of GDB
+that your language exists. You'll need some other supporting variables
+and functions, which will be used via pointers from your
+@code{@var{lang}_language_defn}. See the declaration of @code{struct
+language_defn} in @file{language.h}, and the other @file{*-exp.y} files,
+for more information.
+
+@item Add any evaluation routines, if necessary
+
+If you need new opcodes (that represent the operations of the language),
+add them to the enumerated type in @file{expression.h}. Add support
+code for these operations in @code{eval.c:evaluate_subexp()}. Add cases
+for new opcodes in two functions from @file{parse.c}:
+@code{prefixify_subexp()} and @code{length_of_subexp()}. These compute
+the number of @code{exp_element}s that a given operation takes up.
+
+@item Update some existing code
+
+Add an enumerated identifier for your language to the enumerated type
+@code{enum language} in @file{defs.h}.
+
+Update the routines in @file{language.c} so your language is included. These
+routines include type predicates and such, which (in some cases) are
+language dependent. If your language does not appear in the switch
+statement, an error is reported.
+
+Also included in @file{language.c} is the code that updates the variable
+@code{current_language}, and the routines that translate the
+@code{language_@var{lang}} enumerated identifier into a printable
+string.
+
+Update the function @code{_initialize_language} to include your language. This
+function picks the default language upon startup, so is dependent upon
+which languages that GDB is built for.
+
+Update @code{allocate_symtab} in @file{symfile.c} and/or symbol-reading
+code so that the language of each symtab (source file) is set properly.
+This is used to determine the language to use at each stack frame level.
+Currently, the language is set based upon the extension of the source
+file. If the language can be better inferred from the symbol
+information, please set the language of the symtab in the symbol-reading
+code.
+
+Add helper code to @code{expprint.c:print_subexp()} to handle any new
+expression opcodes you have added to @file{expression.h}. Also, add the
+printed representations of your operators to @code{op_print_tab}.
+
+@item Add a place of call
+
+Add a call to @code{@var{lang}_parse()} and @code{@var{lang}_error} in
+@code{parse.c:parse_exp_1()}.
+
+@item Use macros to trim code
+
+The user has the option of building GDB for some or all of the
+languages. If the user decides to build GDB for the language
+@var{lang}, then every file dependent on @file{language.h} will have the
+macro @code{_LANG_@var{lang}} defined in it. Use @code{#ifdef}s to
+leave out large routines that the user won't need if he or she is not
+using your language.
+
+Note that you do not need to do this in your YACC parser, since if GDB
+is not build for @var{lang}, then @file{@var{lang}-exp.tab.o} (the
+compiled form of your parser) is not linked into GDB at all.
+
+See the file @file{configure.in} for how GDB is configured for different
+languages.
+
+@item Edit @file{Makefile.in}
+
+Add dependencies in @file{Makefile.in}. Make sure you update the macro
+variables such as @code{HFILES} and @code{OBJS}, otherwise your code may
+not get linked in, or, worse yet, it may not get @code{tar}red into the
+distribution!
+@end table
+
+
+@node Releases
+@chapter Configuring GDB for Release
+
+From the top level directory (containing @file{gdb}, @file{bfd},
+@file{libiberty}, and so on):
+@example
+make -f Makefile.in gdb.tar.Z
+@end example
+
+This will properly configure, clean, rebuild any files that are
+distributed pre-built (e.g. @file{c-exp.tab.c} or @file{refcard.ps}),
+and will then make a tarfile. (If the top level directory has already
+beenn configured, you can just do @code{make gdb.tar.Z} instead.)
+
+This procedure requires:
+@itemize @bullet
+@item symbolic links
+@item @code{makeinfo} (texinfo2 level)
+@item @TeX{}
+@item @code{dvips}
+@item @code{yacc} or @code{bison}
+@end itemize
+@noindent
+@dots{} and the usual slew of utilities (@code{sed}, @code{tar}, etc.).
+
+@subheading TEMPORARY RELEASE PROCEDURE FOR DOCUMENTATION
+
+@file{gdb.texinfo} is currently marked up using the texinfo-2 macros,
+which are not yet a default for anything (but we have to start using
+them sometime).
+
+For making paper, the only thing this implies is the right generation of
+@file{texinfo.tex} needs to be included in the distribution.
+
+For making info files, however, rather than duplicating the texinfo2
+distribution, generate @file{gdb-all.texinfo} locally, and include the files
+@file{gdb.info*} in the distribution. Note the plural; @code{makeinfo} will
+split the document into one overall file and five or so included files.
+
+
+@node Partial Symbol Tables
+@chapter Partial Symbol Tables
+
+GDB has three types of symbol tables.
+
+@itemize @bullet
+@item full symbol tables (symtabs). These contain the main
+information about symbols and addresses.
+@item partial symbol tables (psymtabs). These contain enough
+information to know when to read the corresponding
+part of the full symbol table.
+@item minimal symbol tables (msymtabs). These contain information
+gleaned from non-debugging symbols.
+@end itemize
+
+This section describes partial symbol tables.
+
+A psymtab is constructed by doing a very quick pass over an executable
+file's debugging information. Small amounts of information are
+extracted -- enough to identify which parts of the symbol table will
+need to be re-read and fully digested later, when the user needs the
+information. The speed of this pass causes GDB to start up very
+quickly. Later, as the detailed rereading occurs, it occurs in small
+pieces, at various times, and the delay therefrom is mostly invisible to
+the user. (@xref{Symbol Reading}.)
+
+The symbols that show up in a file's psymtab should be, roughly, those
+visible to the debugger's user when the program is not running code from
+that file. These include external symbols and types, static
+symbols and types, and enum values declared at file scope.
+
+The psymtab also contains the range of instruction addresses that the
+full symbol table would represent.
+
+The idea is that there are only two ways for the user (or much of
+the code in the debugger) to reference a symbol:
+
+@itemize @bullet
+
+@item by its address
+(e.g. execution stops at some address which is inside a function
+in this file). The address will be noticed to be in the
+range of this psymtab, and the full symtab will be read in.
+@code{find_pc_function}, @code{find_pc_line}, and other @code{find_pc_@dots{}}
+functions handle this.
+
+@item by its name
+(e.g. the user asks to print a variable, or set a breakpoint on a
+function). Global names and file-scope names will be found in the
+psymtab, which will cause the symtab to be pulled in. Local names will
+have to be qualified by a global name, or a file-scope name, in which
+case we will have already read in the symtab as we evaluated the
+qualifier. Or, a local symbol can be referenced when
+we are "in" a local scope, in which case the first case applies.
+@code{lookup_symbol} does most of the work here.
+
+@end itemize
+
+The only reason that psymtabs exist is to cause a symtab to be read in
+at the right moment. Any symbol that can be elided from a psymtab,
+while still causing that to happen, should not appear in it. Since
+psymtabs don't have the idea of scope, you can't put local symbols in
+them anyway. Psymtabs don't have the idea of the type of a symbol,
+either, so types need not appear, unless they will be referenced by
+name.
+
+It is a bug for GDB to behave one way when only a psymtab has been read,
+and another way if the corresponding symtab has been read in. Such
+bugs are typically caused by a psymtab that does not contain all the
+visible symbols, or which has the wrong instruction address ranges.
+
+The psymtab for a particular section of a symbol-file (objfile)
+could be thrown away after the symtab has been read in. The symtab
+should always be searched before the psymtab, so the psymtab will
+never be used (in a bug-free environment). Currently,
+psymtabs are allocated on an obstack, and all the psymbols themselves
+are allocated in a pair of large arrays on an obstack, so there is
+little to be gained by trying to free them unless you want to do a lot
+more work.
+
+@node Types
+@chapter Types
+
+Fundamental Types (e.g., FT_VOID, FT_BOOLEAN).
+
+These are the fundamental types that gdb uses internally. Fundamental
+types from the various debugging formats (stabs, ELF, etc) are mapped into
+one of these. They are basically a union of all fundamental types that
+gdb knows about for all the languages that gdb knows about.
+
+Type Codes (e.g., TYPE_CODE_PTR, TYPE_CODE_ARRAY).
+
+Each time gdb builds an internal type, it marks it with one of these
+types. The type may be a fundamental type, such as TYPE_CODE_INT, or
+a derived type, such as TYPE_CODE_PTR which is a pointer to another
+type. Typically, several FT_* types map to one TYPE_CODE_* type, and
+are distinguished by other members of the type struct, such as whether
+the type is signed or unsigned, and how many bits it uses.
+
+Builtin Types (e.g., builtin_type_void, builtin_type_char).
+
+These are instances of type structs that roughly correspond to fundamental
+types and are created as global types for gdb to use for various ugly
+historical reasons. We eventually want to eliminate these. Note for
+example that builtin_type_int initialized in gdbtypes.c is basically the
+same as a TYPE_CODE_INT type that is initialized in c-lang.c for an
+FT_INTEGER fundamental type. The difference is that the builtin_type is
+not associated with any particular objfile, and only one instance exists,
+while c-lang.c builds as many TYPE_CODE_INT types as needed, with each
+one associated with some particular objfile.
+
+@node BFD support for GDB
+@chapter Binary File Descriptor Library Support for GDB
+
+BFD provides support for GDB in several ways:
+
+@table @emph
+@item identifying executable and core files
+BFD will identify a variety of file types, including a.out, coff, and
+several variants thereof, as well as several kinds of core files.
+
+@item access to sections of files
+BFD parses the file headers to determine the names, virtual addresses,
+sizes, and file locations of all the various named sections in files
+(such as the text section or the data section). GDB simply calls
+BFD to read or write section X at byte offset Y for length Z.
+
+@item specialized core file support
+BFD provides routines to determine the failing command name stored
+in a core file, the signal with which the program failed, and whether
+a core file matches (i.e. could be a core dump of) a particular executable
+file.
+
+@item locating the symbol information
+GDB uses an internal interface of BFD to determine where to find the
+symbol information in an executable file or symbol-file. GDB itself
+handles the reading of symbols, since BFD does not ``understand'' debug
+symbols, but GDB uses BFD's cached information to find the symbols,
+string table, etc.
+@end table
+
+@c The interface for symbol reading is described in @ref{Symbol
+@c Reading,,Symbol Reading}.
+
+
+@node Symbol Reading
+@chapter Symbol Reading
+
+GDB reads symbols from "symbol files". The usual symbol file is the
+file containing the program which gdb is debugging. GDB can be directed
+to use a different file for symbols (with the ``symbol-file''
+command), and it can also read more symbols via the ``add-file'' and ``load''
+commands, or while reading symbols from shared libraries.
+
+Symbol files are initially opened by @file{symfile.c} using the BFD
+library. BFD identifies the type of the file by examining its header.
+@code{symfile_init} then uses this identification to locate a
+set of symbol-reading functions.
+
+Symbol reading modules identify themselves to GDB by calling
+@code{add_symtab_fns} during their module initialization. The argument
+to @code{add_symtab_fns} is a @code{struct sym_fns} which contains
+the name (or name prefix) of the symbol format, the length of the prefix,
+and pointers to four functions. These functions are called at various
+times to process symbol-files whose identification matches the specified
+prefix.
+
+The functions supplied by each module are:
+
+@table @code
+@item @var{xxx}_symfile_init(struct sym_fns *sf)
+
+Called from @code{symbol_file_add} when we are about to read a new
+symbol file. This function should clean up any internal state
+(possibly resulting from half-read previous files, for example)
+and prepare to read a new symbol file. Note that the symbol file
+which we are reading might be a new "main" symbol file, or might
+be a secondary symbol file whose symbols are being added to the
+existing symbol table.
+
+The argument to @code{@var{xxx}_symfile_init} is a newly allocated
+@code{struct sym_fns} whose @code{bfd} field contains the BFD
+for the new symbol file being read. Its @code{private} field
+has been zeroed, and can be modified as desired. Typically,
+a struct of private information will be @code{malloc}'d, and
+a pointer to it will be placed in the @code{private} field.
+
+There is no result from @code{@var{xxx}_symfile_init}, but it can call
+@code{error} if it detects an unavoidable problem.
+
+@item @var{xxx}_new_init()
+
+Called from @code{symbol_file_add} when discarding existing symbols.
+This function need only handle
+the symbol-reading module's internal state; the symbol table data
+structures visible to the rest of GDB will be discarded by
+@code{symbol_file_add}. It has no arguments and no result.
+It may be called after @code{@var{xxx}_symfile_init}, if a new symbol
+table is being read, or may be called alone if all symbols are
+simply being discarded.
+
+@item @var{xxx}_symfile_read(struct sym_fns *sf, CORE_ADDR addr, int mainline)
+
+Called from @code{symbol_file_add} to actually read the symbols from a
+symbol-file into a set of psymtabs or symtabs.
+
+@code{sf} points to the struct sym_fns originally passed to
+@code{@var{xxx}_sym_init} for possible initialization. @code{addr} is the
+offset between the file's specified start address and its true address
+in memory. @code{mainline} is 1 if this is the main symbol table being
+read, and 0 if a secondary symbol file (e.g. shared library or
+dynamically loaded file) is being read.@refill
+@end table
+
+In addition, if a symbol-reading module creates psymtabs when
+@var{xxx}_symfile_read is called, these psymtabs will contain a pointer to
+a function @code{@var{xxx}_psymtab_to_symtab}, which can be called from
+any point in the GDB symbol-handling code.
+
+@table @code
+@item @var{xxx}_psymtab_to_symtab (struct partial_symtab *pst)
+
+Called from @code{psymtab_to_symtab} (or the PSYMTAB_TO_SYMTAB
+macro) if the psymtab has not already been read in and had its
+@code{pst->symtab} pointer set. The argument is the psymtab
+to be fleshed-out into a symtab. Upon return, pst->readin
+should have been set to 1, and pst->symtab should contain a
+pointer to the new corresponding symtab, or zero if there
+were no symbols in that part of the symbol file.
+@end table
+
+
+@node Cleanups
+@chapter Cleanups
+
+Cleanups are a structured way to deal with things that need to be done
+later. When your code does something (like @code{malloc} some memory, or open
+a file) that needs to be undone later (e.g. free the memory or close
+the file), it can make a cleanup. The cleanup will be done at some
+future point: when the command is finished, when an error occurs, or
+when your code decides it's time to do cleanups.
+
+You can also discard cleanups, that is, throw them away without doing
+what they say. This is only done if you ask that it be done.
+
+Syntax:
+
+@table @code
+@item struct cleanup *@var{old_chain};
+Declare a variable which will hold a cleanup chain handle.
+
+@item @var{old_chain} = make_cleanup (@var{function}, @var{arg});
+Make a cleanup which will cause @var{function} to be called with @var{arg}
+(a @code{char *}) later. The result, @var{old_chain}, is a handle that can be
+passed to @code{do_cleanups} or @code{discard_cleanups} later. Unless you are
+going to call @code{do_cleanups} or @code{discard_cleanups} yourself,
+you can ignore the result from @code{make_cleanup}.
+
+
+@item do_cleanups (@var{old_chain});
+Perform all cleanups done since @code{make_cleanup} returned @var{old_chain}.
+E.g.:
+@example
+make_cleanup (a, 0);
+old = make_cleanup (b, 0);
+do_cleanups (old);
+@end example
+@noindent
+will call @code{b()} but will not call @code{a()}. The cleanup that calls @code{a()} will remain
+in the cleanup chain, and will be done later unless otherwise discarded.@refill
+
+@item discard_cleanups (@var{old_chain});
+Same as @code{do_cleanups} except that it just removes the cleanups from the
+chain and does not call the specified functions.
+
+@end table
+
+Some functions, e.g. @code{fputs_filtered()} or @code{error()}, specify that they
+``should not be called when cleanups are not in place''. This means
+that any actions you need to reverse in the case of an error or
+interruption must be on the cleanup chain before you call these functions,
+since they might never return to your code (they @samp{longjmp} instead).
+
+
+@node Wrapping
+@chapter Wrapping Output Lines
+
+Output that goes through @code{printf_filtered} or @code{fputs_filtered} or
+@code{fputs_demangled} needs only to have calls to @code{wrap_here} added
+in places that would be good breaking points. The utility routines
+will take care of actually wrapping if the line width is exceeded.
+
+The argument to @code{wrap_here} is an indentation string which is printed
+@emph{only} if the line breaks there. This argument is saved away and used
+later. It must remain valid until the next call to @code{wrap_here} or
+until a newline has been printed through the @code{*_filtered} functions.
+Don't pass in a local variable and then return!
+
+It is usually best to call @code{wrap_here()} after printing a comma or space.
+If you call it before printing a space, make sure that your indentation
+properly accounts for the leading space that will print if the line wraps
+there.
+
+Any function or set of functions that produce filtered output must finish
+by printing a newline, to flush the wrap buffer, before switching to
+unfiltered (``@code{printf}'') output. Symbol reading routines that print
+warnings are a good example.
+
+
+@node Frames
+@chapter Frames
+
+A frame is a construct that GDB uses to keep track of calling and called
+functions.
+
+@table @code
+@item FRAME_FP
+in the machine description has no meaning to the machine-independent
+part of GDB, except that it is used when setting up a new frame from
+scratch, as follows:
+
+@example
+ create_new_frame (read_register (FP_REGNUM), read_pc ()));
+@end example
+
+Other than that, all the meaning imparted to @code{FP_REGNUM} is imparted by
+the machine-dependent code. So, @code{FP_REGNUM} can have any value that
+is convenient for the code that creates new frames. (@code{create_new_frame}
+calls @code{INIT_EXTRA_FRAME_INFO} if it is defined; that is where you should
+use the @code{FP_REGNUM} value, if your frames are nonstandard.)
+
+@item FRAME_CHAIN
+Given a GDB frame, determine the address of the calling function's
+frame. This will be used to create a new GDB frame struct, and then
+@code{INIT_EXTRA_FRAME_INFO} and @code{INIT_FRAME_PC} will be called for
+the new frame.
+@end table
+
+@node Remote Stubs
+@chapter Remote Stubs
+
+GDB's file @file{remote.c} talks a serial protocol to code that runs
+in the target system. GDB provides several sample ``stubs'' that can
+be integrated into target programs or operating systems for this purpose;
+they are named @file{*-stub.c}.
+
+The GDB user's manual describes how to put such a stub into your target
+code. What follows is a discussion of integrating the SPARC stub
+into a complicated operating system (rather than a simple program),
+by Stu Grossman, the author of this stub.
+
+The trap handling code in the stub assumes the following upon entry to
+trap_low:
+
+@enumerate
+@item %l1 and %l2 contain pc and npc respectively at the time of the trap
+@item traps are disabled
+@item you are in the correct trap window
+@end enumerate
+
+As long as your trap handler can guarantee those conditions, then there is no
+reason why you shouldn't be able to `share' traps with the stub. The stub has
+no requirement that it be jumped to directly from the hardware trap vector.
+That is why it calls @code{exceptionHandler()}, which is provided by the external
+environment. For instance, this could setup the hardware traps to actually
+execute code which calls the stub first, and then transfers to its own trap
+handler.
+
+For the most point, there probably won't be much of an issue with `sharing'
+traps, as the traps we use are usually not used by the kernel, and often
+indicate unrecoverable error conditions. Anyway, this is all controlled by a
+table, and is trivial to modify.
+The most important trap for us is for @code{ta 1}. Without that, we
+can't single step or do breakpoints. Everything else is unnecessary
+for the proper operation of the debugger/stub.
+
+From reading the stub, it's probably not obvious how breakpoints work. They
+are simply done by deposit/examine operations from GDB.
+
+@node Longjmp Support
+@chapter Longjmp Support
+
+GDB has support for figuring out that the target is doing a
+@code{longjmp} and for stopping at the target of the jump, if we are
+stepping. This is done with a few specialized internal breakpoints,
+which are visible in the @code{maint info breakpoint} command.
+
+To make this work, you need to define a macro called
+@code{GET_LONGJMP_TARGET}, which will examine the @code{jmp_buf}
+structure and extract the longjmp target address. Since @code{jmp_buf}
+is target specific, you will need to define it in the appropriate
+@file{tm-xxx.h} file. Look in @file{tm-sun4os4.h} and
+@file{sparc-tdep.c} for examples of how to do this.
+
+@node Coding Style
+@chapter Coding Style
+
+GDB is generally written using the GNU coding standards, as described in
+@file{standards.texi}, which is available for anonymous FTP from GNU
+archive sites. There are some additional considerations for GDB
+maintainers that reflect the unique environment and style of GDB
+maintenance. If you follow these guidelines, GDB will be more
+consistent and easier to maintain.
+
+GDB's policy on the use of prototypes is that prototypes are used
+to @emph{declare} functions but never to @emph{define} them. Simple
+macros are used in the declarations, so that a non-ANSI compiler can
+compile GDB without trouble. The simple macro calls are used like
+this:
+
+@example @code
+extern int
+memory_remove_breakpoint PARAMS ((CORE_ADDR, char *));
+@end example
+
+Note the double parentheses around the parameter types. This allows
+an arbitrary number of parameters to be described, without freaking
+out the C preprocessor. When the function has no parameters, it
+should be described like:
+
+@example @code
+void
+noprocess PARAMS ((void));
+@end example
+
+The @code{PARAMS} macro expands to its argument in ANSI C, or to a simple
+@code{()} in traditional C.
+
+All external functions should have a @code{PARAMS} declaration in a
+header file that callers include. All static functions should have such
+a declaration near the top of their source file.
+
+We don't have a gcc option that will properly check that these rules
+have been followed, but it's GDB policy, and we periodically check it
+using the tools available (plus manual labor), and clean up any remnants.
+
+@node Clean Design
+@chapter Clean Design
+
+In addition to getting the syntax right, there's the little question of
+semantics. Some things are done in certain ways in GDB because long
+experience has shown that the more obvious ways caused various kinds of
+trouble. In particular:
+
+@table @bullet
+@item
+You can't assume the byte order of anything that comes from a
+target (including @var{value}s, object files, and instructions). Such
+things must be byte-swapped using @code{SWAP_TARGET_AND_HOST} in GDB,
+or one of the swap routines defined in @file{bfd.h}, such as @code{bfd_get_32}.
+
+@item
+You can't assume that you know what interface is being used to talk to
+the target system. All references to the target must go through the
+current @code{target_ops} vector.
+
+@item
+You can't assume that the host and target machines are the same machine
+(except in the ``native'' support modules).
+In particular, you can't assume that the target machine's header files
+will be available on the host machine. Target code must bring along its
+own header files -- written from scratch or explicitly donated by their
+owner, to avoid copyright problems.
+
+@item
+Insertion of new @code{#ifdef}'s will be frowned upon. It's much better
+to write the code portably than to conditionalize it for various systems.
+
+@item
+New @code{#ifdef}'s which test for specific compilers or manufacturers
+or operating systems are unacceptable. All @code{#ifdef}'s should test
+for features. The information about which configurations contain which
+features should be segregated into the configuration files. Experience
+has proven far too often that a feature unique to one particular system
+often creeps into other systems; and that a conditional based on
+some predefined macro for your current system will become worthless
+over time, as new versions of your system come out that behave differently
+with regard to this feature.
+
+@item
+Adding code that handles specific architectures, operating systems, target
+interfaces, or hosts, is not acceptable in generic code. If a hook
+is needed at that point, invent a generic hook and define it for your
+configuration, with something like:
+
+@example
+#ifdef WRANGLE_SIGNALS
+ WRANGLE_SIGNALS (signo);
+#endif
+@end example
+
+In your host, target, or native configuration file, as appropriate,
+define @code{WRANGLE_SIGNALS} to do the machine-dependent thing. Take
+a bit of care in defining the hook, so that it can be used by other
+ports in the future, if they need a hook in the same place.
+
+If the hook is not defined, the code should do whatever "most" machines
+want. Using @code{#ifdef}, as above, is the preferred way to do this,
+but sometimes that gets convoluted, in which case use
+
+@example
+#ifndef SPECIAL_FOO_HANDLING
+#define SPECIAL_FOO_HANDLING(pc, sp) (0)
+#endif
+@end example
+
+where the macro is used or in an appropriate header file.
+
+Whether to include a @dfn{small} hook, a hook around the exact pieces of
+code which are system-dependent, or whether to replace a whole function
+with a hook depends on the case. A good example of this dilemma can be
+found in @code{get_saved_register}. All machines that GDB 2.8 ran on
+just needed the @code{FRAME_FIND_SAVED_REGS} hook to find the saved
+registers. Then the SPARC and Pyramid came along, and
+@code{HAVE_REGISTER_WINDOWS} and @code{REGISTER_IN_WINDOW_P} were
+introduced. Then the 29k and 88k required the @code{GET_SAVED_REGISTER}
+hook. The first three are examples of small hooks; the latter replaces
+a whole function. In this specific case, it is useful to have both
+kinds; it would be a bad idea to replace all the uses of the small hooks
+with @code{GET_SAVED_REGISTER}, since that would result in much
+duplicated code. Other times, duplicating a few lines of code here or
+there is much cleaner than introducing a large number of small hooks.
+
+Another way to generalize GDB along a particular interface is with an
+attribute struct. For example, GDB has been generalized to handle
+multiple kinds of remote interfaces -- not by #ifdef's everywhere, but
+by defining the "target_ops" structure and having a current target (as
+well as a stack of targets below it, for memory references). Whenever
+something needs to be done that depends on which remote interface we are
+using, a flag in the current target_ops structure is tested (e.g.
+`target_has_stack'), or a function is called through a pointer in the
+current target_ops structure. In this way, when a new remote interface
+is added, only one module needs to be touched -- the one that actually
+implements the new remote interface. Other examples of
+attribute-structs are BFD access to multiple kinds of object file
+formats, or GDB's access to multiple source languages.
+
+Please avoid duplicating code. For example, in GDB 3.x all the code
+interfacing between @code{ptrace} and the rest of GDB was duplicated in
+@file{*-dep.c}, and so changing something was very painful. In GDB 4.x,
+these have all been consolidated into @file{infptrace.c}.
+@file{infptrace.c} can deal with variations between systems the same way
+any system-independent file would (hooks, #if defined, etc.), and
+machines which are radically different don't need to use infptrace.c at
+all.
+
+@item
+@emph{Do} write code that doesn't depend on the sizes of C data types,
+the format of the host's floating point numbers, the alignment of anything,
+or the order of evaluation of expressions. In short, follow good
+programming practices for writing portable C code.
+
+@end table
+
+@node Submitting Patches
+@chapter Submitting Patches
+
+Thanks for thinking of offering your changes back to the community of
+GDB users. In general we like to get well designed enhancements.
+Thanks also for checking in advance about the best way to transfer the
+changes.
+
+The two main problems with getting your patches in are,
+
+@table @bullet
+@item
+The GDB maintainers will only install ``cleanly designed'' patches.
+You may not always agree on what is clean design.
+@pxref{Coding Style}, @pxref{Clean Design}.
+
+@item
+If the maintainers don't have time to put the patch in when it
+arrives, or if there is any question about a patch, it
+goes into a large queue with everyone else's patches and
+bug reports.
+@end table
+
+I don't know how to get past these problems except by continuing to try.
+
+There are two issues here -- technical and legal.
+
+The legal issue is that to incorporate substantial changes requires a
+copyright assignment from you and/or your employer, granting ownership
+of the changes to the Free Software Foundation. You can get the
+standard document for doing this by sending mail to
+@code{gnu@@prep.ai.mit.edu} and asking for it. I recommend that people
+write in "All programs owned by the Free Software Foundation" as "NAME
+OF PROGRAM", so that changes in many programs (not just GDB, but GAS,
+Emacs, GCC, etc) can be contributed with only one piece of legalese
+pushed through the bureacracy and filed with the FSF. I can't start
+merging changes until this paperwork is received by the FSF (their
+rules, which I follow since I maintain it for them).
+
+Technically, the easiest way to receive changes is to receive each
+feature as a small context diff or unidiff, suitable for "patch".
+Each message sent to me should include the changes to C code and
+header files for a single feature, plus ChangeLog entries for each
+directory where files were modified, and diffs for any changes needed
+to the manuals (gdb/doc/gdb.texi or gdb/doc/gdbint.texi). If there
+are a lot of changes for a single feature, they can be split down
+into multiple messages.
+
+In this way, if I read and like the feature, I can add it to the
+sources with a single patch command, do some testing, and check it in.
+If you leave out the ChangeLog, I have to write one. If you leave
+out the doc, I have to puzzle out what needs documenting. Etc.
+
+The reason to send each change in a separate message is that I will
+not install some of the changes. They'll be returned to you with
+questions or comments. If I'm doing my job, my message back to you
+will say what you have to fix in order to make the change acceptable.
+The reason to have separate messages for separate features is so
+that other changes (which I @emph{am} willing to accept) can be installed
+while one or more changes are being reworked. If multiple features
+are sent in a single message, I tend to not put in the effort to sort
+out the acceptable changes from the unacceptable, so none of the
+features get installed until all are acceptable.
+
+If this sounds painful or authoritarian, well, it is. But I get a lot
+of bug reports and a lot of patches, and most of them don't get
+installed because I don't have the time to finish the job that the bug
+reporter or the contributor could have done. Patches that arrive
+complete, working, and well designed, tend to get installed on the day
+they arrive. The others go into a queue and get installed if and when
+I scan back over the queue -- which can literally take months
+sometimes. It's in both our interests to make patch installation easy
+-- you get your changes installed, and I make some forward progress on
+GDB in a normal 12-hour day (instead of them having to wait until I
+have a 14-hour or 16-hour day to spend cleaning up patches before I
+can install them).
+
+Please send patches to @code{bug-gdb@@prep.ai.mit.edu}, if they are less
+than about 25,000 characters. If longer than that, either make them
+available somehow (e.g. anonymous FTP), and announce it on
+@code{bug-gdb}, or send them directly to the GDB maintainers at
+@code{gdb-patches@@cygnus.com}.
+
+@node Host Conditionals
+@chapter Host Conditionals
+
+When GDB is configured and compiled, various macros are defined or left
+undefined, to control compilation based on the attributes of the host
+system. These macros and their meanings are:
+
+@emph{NOTE: For now, both host and target conditionals are here.
+Eliminate target conditionals from this list as they are identified.}
+
+@table @code
+@item BLOCK_ADDRESS_FUNCTION_RELATIVE
+dbxread.c
+@item GDBINIT_FILENAME
+The default name of GDB's initialization file (normally @file{.gdbinit}).
+@item KERNELDEBUG
+tm-hppa.h
+@item MEM_FNS_DECLARED
+Your host config file defines this if it includes
+declarations of @code{memcpy} and @code{memset}. Define this
+to avoid conflicts between the native include
+files and the declarations in @file{defs.h}.
+@item NO_SYS_FILE
+dbxread.c
+@item PYRAMID_CONTROL_FRAME_DEBUGGING
+pyr-xdep.c
+@item SIGWINCH_HANDLER_BODY
+utils.c
+@item ADDITIONAL_OPTIONS
+main.c
+@item ADDITIONAL_OPTION_CASES
+main.c
+@item ADDITIONAL_OPTION_HANDLER
+main.c
+@item ADDITIONAL_OPTION_HELP
+main.c
+@item ADDR_BITS_REMOVE
+defs.h
+@item AIX_BUGGY_PTRACE_CONTINUE
+infptrace.c
+@item ALIGN_STACK_ON_STARTUP
+main.c
+@item ALTOS
+altos-xdep.c
+@item ALTOS_AS
+xm-altos.h
+@item ASCII_COFF
+remote-adapt.c
+@item BADMAG
+coffread.c
+@item BCS
+tm-delta88.h
+@item BEFORE_MAIN_LOOP_HOOK
+main.c
+@item BELIEVE_PCC_PROMOTION
+coffread.c
+@item BELIEVE_PCC_PROMOTION_TYPE
+stabsread.c
+@item BITS_BIG_ENDIAN
+defs.h
+@item BKPT_AT_MAIN
+solib.c
+@item BLOCK_ADDRESS_ABSOLUTE
+dbxread.c
+@item BPT_VECTOR
+tm-m68k.h
+@item BREAKPOINT
+tm-m68k.h
+@item BREAKPOINT_DEBUG
+breakpoint.c
+@item BROKEN_LARGE_ALLOCA
+Avoid large @code{alloca}'s. For example, on sun's, Large alloca's fail
+because the attempt to increase the stack limit in main() fails because
+shared libraries are allocated just below the initial stack limit. The
+SunOS kernel will not allow the stack to grow into the area occupied by
+the shared libraries.
+@item BSTRING
+regex.c
+@item CALL_DUMMY
+valops.c
+@item CALL_DUMMY_LOCATION
+inferior.h
+@item CALL_DUMMY_STACK_ADJUST
+valops.c
+@item CANNOT_FETCH_REGISTER
+hppabsd-xdep.c
+@item CANNOT_STORE_REGISTER
+findvar.c
+@item CFRONT_PRODUCER
+dwarfread.c
+@item CHILD_PREPARE_TO_STORE
+inftarg.c
+@item CLEAR_DEFERRED_STORES
+inflow.c
+@item CLEAR_SOLIB
+objfiles.c
+@item COFF_ENCAPSULATE
+hppabsd-tdep.c
+@item COFF_FORMAT
+symm-tdep.c
+@item CORE_NEEDS_RELOCATION
+stack.c
+@item CPLUS_MARKER
+cplus-dem.c
+@item CREATE_INFERIOR_HOOK
+infrun.c
+@item C_ALLOCA
+regex.c
+@item C_GLBLREG
+coffread.c
+@item DBXREAD_ONLY
+partial-stab.h
+@item DBX_PARM_SYMBOL_CLASS
+stabsread.c
+@item DEBUG
+remote-adapt.c
+@item DEBUG_INFO
+partial-stab.h
+@item DEBUG_PTRACE
+hppabsd-xdep.c
+@item DECR_PC_AFTER_BREAK
+breakpoint.c
+@item DEFAULT_PROMPT
+The default value of the prompt string (normally @code{"(gdb) "}).
+@item DELTA88
+m88k-xdep.c
+@item DEV_TTY
+symmisc.c
+@item DGUX
+m88k-xdep.c
+@item DISABLE_UNSETTABLE_BREAK
+breakpoint.c
+@item DONT_USE_REMOTE
+remote.c
+@item DO_DEFERRED_STORES
+infrun.c
+@item DO_REGISTERS_INFO
+infcmd.c
+@item EXTRACT_RETURN_VALUE
+tm-m68k.h
+@item EXTRACT_STRUCT_VALUE_ADDRESS
+values.c
+@item EXTRA_FRAME_INFO
+frame.h
+@item EXTRA_SYMTAB_INFO
+symtab.h
+@item FILES_INFO_HOOK
+target.c
+@item FLOAT_INFO
+infcmd.c
+@item FOPEN_RB
+defs.h
+@item FRAMELESS_FUNCTION_INVOCATION
+blockframe.c
+@item FRAME_ARGS_ADDRESS_CORRECT
+stack.c
+@item FRAME_CHAIN_COMBINE
+blockframe.c
+@item FRAME_CHAIN_VALID
+frame.h
+@item FRAME_CHAIN_VALID_ALTERNATE
+frame.h
+@item FRAME_FIND_SAVED_REGS
+stack.c
+@item FRAME_GET_BASEREG_VALUE
+frame.h
+@item FRAME_NUM_ARGS
+tm-m68k.h
+@item FRAME_SPECIFICATION_DYADIC
+stack.c
+@item FUNCTION_EPILOGUE_SIZE
+coffread.c
+@item F_OK
+xm-ultra3.h
+@item GCC2_COMPILED_FLAG_SYMBOL
+dbxread.c
+@item GCC_COMPILED_FLAG_SYMBOL
+dbxread.c
+@item GCC_MANGLE_BUG
+symtab.c
+@item GCC_PRODUCER
+dwarfread.c
+@item GET_SAVED_REGISTER
+findvar.c
+@item GPLUS_PRODUCER
+dwarfread.c
+@item HANDLE_RBRAC
+partial-stab.h
+@item HAVE_MMAP
+In some cases, use the system call @code{mmap} for reading symbol
+tables. For some machines this allows for sharing and quick updates.
+@item HAVE_REGISTER_WINDOWS
+findvar.c
+@item HAVE_SIGSETMASK
+main.c
+@item HAVE_TERMIO
+inflow.c
+@item HEADER_SEEK_FD
+arm-tdep.c
+@item HOSTING_ONLY
+xm-rtbsd.h
+@item HOST_BYTE_ORDER
+ieee-float.c
+@item HPUX_ASM
+xm-hp300hpux.h
+@item HPUX_VERSION_5
+hp300ux-xdep.c
+@item HP_OS_BUG
+infrun.c
+@item I80960
+remote-vx.c
+@item IEEE_DEBUG
+ieee-float.c
+@item IEEE_FLOAT
+valprint.c
+@item IGNORE_SYMBOL
+dbxread.c
+@item INIT_EXTRA_FRAME_INFO
+blockframe.c
+@item INIT_EXTRA_SYMTAB_INFO
+symfile.c
+@item INIT_FRAME_PC
+blockframe.c
+@item INNER_THAN
+valops.c
+@item INT_MAX
+defs.h
+@item INT_MIN
+defs.h
+@item IN_GDB
+i960-pinsn.c
+@item IN_SIGTRAMP
+infrun.c
+@item IN_SOLIB_TRAMPOLINE
+infrun.c
+@item ISATTY
+main.c
+@item IS_TRAPPED_INTERNALVAR
+values.c
+@item KERNELDEBUG
+dbxread.c
+@item KERNEL_DEBUGGING
+tm-ultra3.h
+@item KERNEL_U_ADDR
+Define this to the address of the @code{u} structure (the ``user struct'',
+also known as the ``u-page'') in kernel virtual memory. GDB needs to know
+this so that it can subtract this address from absolute addresses in
+the upage, that are obtained via ptrace or from core files. On systems
+that don't need this value, set it to zero.
+@item KERNEL_U_ADDR_BSD
+Define this to cause GDB to determine the address of @code{u} at runtime,
+by using Berkeley-style @code{nlist} on the kernel's image in the root
+directory.
+@item KERNEL_U_ADDR_HPUX
+Define this to cause GDB to determine the address of @code{u} at runtime,
+by using HP-style @code{nlist} on the kernel's image in the root
+directory.
+@item LCC_PRODUCER
+dwarfread.c
+@item LOG_FILE
+remote-adapt.c
+@item LONGERNAMES
+cplus-dem.c
+@item LONGEST
+defs.h
+@item CC_HAS_LONG_LONG
+defs.h
+@item PRINTF_HAS_LONG_LONG
+defs.h
+@item LONG_MAX
+defs.h
+@item LSEEK_NOT_LINEAR
+source.c
+@item L_LNNO32
+coffread.c
+@item L_SET
+This macro is used as the argument to lseek (or, most commonly, bfd_seek).
+FIXME, it should be replaced by SEEK_SET instead, which is the POSIX equivalent.
+@item MACHKERNELDEBUG
+hppabsd-tdep.c
+@item MAINTENANCE
+dwarfread.c
+@item MAINTENANCE_CMDS
+If the value of this is 1, then a number of optional maintenance commands
+are compiled in.
+@item MALLOC_INCOMPATIBLE
+Define this if the system's prototype for @code{malloc} differs from the
+@sc{ANSI} definition.
+@item MIPSEL
+mips-tdep.c
+@item MMAP_BASE_ADDRESS
+When using HAVE_MMAP, the first mapping should go at this address.
+@item MMAP_INCREMENT
+when using HAVE_MMAP, this is the increment between mappings.
+@item MONO
+ser-go32.c
+@item MOTOROLA
+xm-altos.h
+@item NBPG
+altos-xdep.c
+@item NEED_POSIX_SETPGID
+infrun.c
+@item NEED_TEXT_START_END
+exec.c
+@item NFAILURES
+regex.c
+@item NORETURN
+(in defs.h - is this really useful to define/undefine?)
+@item NOTDEF
+regex.c
+@item NOTDEF
+remote-adapt.c
+@item NOTDEF
+remote-mm.c
+@item NOTICE_SIGNAL_HANDLING_CHANGE
+infrun.c
+@item NO_HIF_SUPPORT
+remote-mm.c
+@item NO_JOB_CONTROL
+signals.h
+@item NO_MMALLOC
+GDB will use the @code{mmalloc} library for memory allocation for symbol
+reading, unless this symbol is defined. Define it on systems
+on which @code{mmalloc} does not
+work for some reason. One example is the DECstation, where its RPC
+library can't cope with our redefinition of @code{malloc} to call
+@code{mmalloc}. When defining @code{NO_MMALLOC}, you will also have
+to override the setting of @code{MMALLOC_LIB} to empty, in the Makefile.
+Therefore, this define is usually set on the command line by overriding
+@code{MMALLOC_DISABLE} in @file{config/*/*.mh}, rather than by defining
+it in @file{xm-*.h}.
+@item NO_MMALLOC_CHECK
+Define this if you are using @code{mmalloc}, but don't want the overhead
+of checking the heap with @code{mmcheck}.
+@item NO_SIGINTERRUPT
+remote-adapt.c
+@item NO_SINGLE_STEP
+infptrace.c
+@item NS32K_SVC_IMMED_OPERANDS
+ns32k-opcode.h
+@item NUMERIC_REG_NAMES
+mips-tdep.c
+@item N_SETV
+dbxread.c
+@item N_SET_MAGIC
+hppabsd-tdep.c
+@item NaN
+tm-umax.h
+@item ONE_PROCESS_WRITETEXT
+breakpoint.c
+@item O_BINARY
+exec.c
+@item O_RDONLY
+xm-ultra3.h
+@item PC
+convx-opcode.h
+@item PCC_SOL_BROKEN
+dbxread.c
+@item PC_IN_CALL_DUMMY
+inferior.h
+@item PC_LOAD_SEGMENT
+stack.c
+@item PRINT_RANDOM_SIGNAL
+infcmd.c
+@item PRINT_REGISTER_HOOK
+infcmd.c
+@item PRINT_TYPELESS_INTEGER
+valprint.c
+@item PROCESS_LINENUMBER_HOOK
+buildsym.c
+@item PROLOGUE_FIRSTLINE_OVERLAP
+infrun.c
+@item PSIGNAL_IN_SIGNAL_H
+defs.h
+@item PUSH_ARGUMENTS
+valops.c
+@item PYRAMID_CONTROL_FRAME_DEBUGGING
+pyr-xdep.c
+@item PYRAMID_CORE
+pyr-xdep.c
+@item PYRAMID_PTRACE
+pyr-xdep.c
+@item REGISTER_BYTES
+remote.c
+@item REGISTER_NAMES
+tm-a29k.h
+@item REG_STACK_SEGMENT
+exec.c
+@item REG_STRUCT_HAS_ADDR
+findvar.c
+@item RE_NREGS
+regex.h
+@item R_FP
+dwarfread.c
+@item R_OK
+xm-altos.h
+@item SEEK_END
+state.c
+@item SEEK_SET
+state.c
+@item SEM
+coffread.c
+@item SET_STACK_LIMIT_HUGE
+When defined, stack limits will be raised to their maximum. Use this
+if your host supports @code{setrlimit} and you have trouble with
+@code{stringtab} in @file{dbxread.c}.
+
+Also used in @file{fork-child.c} to return stack limits before child
+processes are forked.
+@item SHELL_COMMAND_CONCAT
+infrun.c
+@item SHELL_FILE
+infrun.c
+@item SHIFT_INST_REGS
+breakpoint.c
+@item SIGN_EXTEND_CHAR
+regex.c
+@item SIGTRAP_STOP_AFTER_LOAD
+infrun.c
+@item SKIP_PROLOGUE
+tm-m68k.h
+@item SKIP_PROLOGUE_FRAMELESS_P
+blockframe.c
+@item SKIP_TRAMPOLINE_CODE
+infrun.c
+@item SOLIB_ADD
+core.c
+@item SOLIB_CREATE_INFERIOR_HOOK
+infrun.c
+@item STACK_ALIGN
+valops.c
+@item START_INFERIOR_TRAPS_EXPECTED
+infrun.c
+@item STOP_SIGNAL
+main.c
+@item STORE_RETURN_VALUE
+tm-m68k.h
+@item SUN4_COMPILER_FEATURE
+infrun.c
+@item SUN_FIXED_LBRAC_BUG
+dbxread.c
+@item SVR4_SHARED_LIBS
+solib.c
+@item SWITCH_ENUM_BUG
+regex.c
+@item SYM1
+tm-ultra3.h
+@item SYMBOL_RELOADING_DEFAULT
+symfile.c
+@item SYNTAX_TABLE
+regex.c
+@item Sword
+regex.c
+@item TDESC
+infrun.c
+@item TIOCGETC
+inflow.c
+@item TIOCGLTC
+inflow.c
+@item TIOCGPGRP
+inflow.c
+@item TIOCLGET
+inflow.c
+@item TIOCLSET
+inflow.c
+@item TIOCNOTTY
+inflow.c
+@item T_ARG
+coffread.c
+@item T_VOID
+coffread.c
+@item UINT_MAX
+defs.h
+@item UPAGES
+altos-xdep.c
+@item USER
+m88k-tdep.c
+@item USE_GAS
+xm-news.h
+@item USE_O_NOCTTY
+inflow.c
+@item USE_STRUCT_CONVENTION
+values.c
+@item USG
+Means that System V (prior to SVR4) include files are in use.
+(FIXME: This symbol is abused in @file{infrun.c}, @file{regex.c},
+@file{remote-nindy.c}, and @file{utils.c} for other things, at the moment.)
+@item USIZE
+xm-m88k.h
+@item U_FPSTATE
+i386-xdep.c
+@item VARIABLES_INSIDE_BLOCK
+dbxread.c
+@item WRS_ORIG
+remote-vx.c
+@item _LANG_c
+language.c
+@item _LANG_m2
+language.c
+@item __GNUC__
+news-xdep.c
+@item __GO32__
+inflow.c
+@item __HPUX_ASM__
+xm-hp300hpux.h
+@item __INT_VARARGS_H
+printcmd.c
+@item __not_on_pyr_yet
+pyr-xdep.c
+@item alloca
+defs.h
+@item const
+defs.h
+@item GOULD_PN
+gould-pinsn.c
+@item hp800
+xm-hppabsd.h
+@item hpux
+hppabsd-core.c
+@item lint
+valarith.c
+@item longest_to_int
+defs.h
+@item mc68020
+m68k-stub.c
+@item notdef
+gould-pinsn.c
+@item ns32k_opcodeT
+ns32k-opcode.h
+@item sgi
+mips-tdep.c
+@item sparc
+regex.c
+@item sun
+m68k-tdep.c
+@item sun386
+tm-sun386.h
+@item test
+regex.c
+@item ultrix
+xm-mips.h
+@item volatile
+defs.h
+@end table
+
+@node Target Conditionals
+@chapter Target Conditionals
+
+When GDB is configured and compiled, various macros are defined or left
+undefined, to control compilation based on the attributes of the target
+system. These macros and their meanings are:
+
+@emph{NOTE: For now, both host and target conditionals are here.
+Eliminate host conditionals from this list as they are identified.}
+
+@table @code
+@item PUSH_DUMMY_FRAME
+Used in @samp{call_function_by_hand} to create an artificial stack frame.
+@item POP_FRAME
+Used in @samp{call_function_by_hand} to remove an artificial stack frame.
+@item BLOCK_ADDRESS_FUNCTION_RELATIVE
+dbxread.c
+@item KERNELDEBUG
+tm-hppa.h
+@item NO_SYS_FILE
+dbxread.c
+@item PYRAMID_CONTROL_FRAME_DEBUGGING
+pyr-xdep.c
+@item SIGWINCH_HANDLER_BODY
+utils.c
+@item ADDITIONAL_OPTIONS
+main.c
+@item ADDITIONAL_OPTION_CASES
+main.c
+@item ADDITIONAL_OPTION_HANDLER
+main.c
+@item ADDITIONAL_OPTION_HELP
+main.c
+@item ADDR_BITS_REMOVE
+defs.h
+@item ALIGN_STACK_ON_STARTUP
+main.c
+@item ALTOS
+altos-xdep.c
+@item ALTOS_AS
+xm-altos.h
+@item ASCII_COFF
+remote-adapt.c
+@item BADMAG
+coffread.c
+@item BCS
+tm-delta88.h
+@item BEFORE_MAIN_LOOP_HOOK
+main.c
+@item BELIEVE_PCC_PROMOTION
+coffread.c
+@item BELIEVE_PCC_PROMOTION_TYPE
+stabsread.c
+@item BITS_BIG_ENDIAN
+defs.h
+@item BKPT_AT_MAIN
+solib.c
+@item BLOCK_ADDRESS_ABSOLUTE
+dbxread.c
+@item BPT_VECTOR
+tm-m68k.h
+@item BREAKPOINT
+tm-m68k.h
+@item BREAKPOINT_DEBUG
+breakpoint.c
+@item BSTRING
+regex.c
+@item CALL_DUMMY
+valops.c
+@item CALL_DUMMY_LOCATION
+inferior.h
+@item CALL_DUMMY_STACK_ADJUST
+valops.c
+@item CANNOT_FETCH_REGISTER
+hppabsd-xdep.c
+@item CANNOT_STORE_REGISTER
+findvar.c
+@item CFRONT_PRODUCER
+dwarfread.c
+@item CHILD_PREPARE_TO_STORE
+inftarg.c
+@item CLEAR_DEFERRED_STORES
+inflow.c
+@item CLEAR_SOLIB
+objfiles.c
+@item COFF_ENCAPSULATE
+hppabsd-tdep.c
+@item COFF_FORMAT
+symm-tdep.c
+@item CORE_NEEDS_RELOCATION
+stack.c
+@item CPLUS_MARKER
+cplus-dem.c
+@item CREATE_INFERIOR_HOOK
+infrun.c
+@item C_ALLOCA
+regex.c
+@item C_GLBLREG
+coffread.c
+@item DBXREAD_ONLY
+partial-stab.h
+@item DBX_PARM_SYMBOL_CLASS
+stabsread.c
+@item DEBUG
+remote-adapt.c
+@item DEBUG_INFO
+partial-stab.h
+@item DEBUG_PTRACE
+hppabsd-xdep.c
+@item DECR_PC_AFTER_BREAK
+breakpoint.c
+@item DELTA88
+m88k-xdep.c
+@item DEV_TTY
+symmisc.c
+@item DGUX
+m88k-xdep.c
+@item DISABLE_UNSETTABLE_BREAK
+breakpoint.c
+@item DONT_USE_REMOTE
+remote.c
+@item DO_DEFERRED_STORES
+infrun.c
+@item DO_REGISTERS_INFO
+infcmd.c
+@item END_OF_TEXT_DEFAULT
+This is an expression that should designate the end of the text section
+(? FIXME ?)
+@item EXTRACT_RETURN_VALUE
+tm-m68k.h
+@item EXTRACT_STRUCT_VALUE_ADDRESS
+values.c
+@item EXTRA_FRAME_INFO
+frame.h
+@item EXTRA_SYMTAB_INFO
+symtab.h
+@item FILES_INFO_HOOK
+target.c
+@item FLOAT_INFO
+infcmd.c
+@item FOPEN_RB
+defs.h
+@item FP0_REGNUM
+a68v-xdep.c
+@item FPC_REGNUM
+mach386-xdep.c
+@item FP_REGNUM
+parse.c
+@item FPU
+Unused? 6-oct-92 rich@@cygnus.com. FIXME.
+@item FRAMELESS_FUNCTION_INVOCATION
+blockframe.c
+@item FRAME_ARGS_ADDRESS_CORRECT
+stack.c
+@item FRAME_CHAIN
+Given FRAME, return a pointer to the calling frame.
+@item FRAME_CHAIN_COMBINE
+blockframe.c
+@item FRAME_CHAIN_VALID
+frame.h
+@item FRAME_CHAIN_VALID_ALTERNATE
+frame.h
+@item FRAME_FIND_SAVED_REGS
+stack.c
+@item FRAME_GET_BASEREG_VALUE
+frame.h
+@item FRAME_NUM_ARGS
+tm-m68k.h
+@item FRAME_SPECIFICATION_DYADIC
+stack.c
+@item FRAME_SAVED_PC
+Given FRAME, return the pc saved there. That is, the return address.
+@item FUNCTION_EPILOGUE_SIZE
+coffread.c
+@item F_OK
+xm-ultra3.h
+@item GCC2_COMPILED_FLAG_SYMBOL
+dbxread.c
+@item GCC_COMPILED_FLAG_SYMBOL
+dbxread.c
+@item GCC_MANGLE_BUG
+symtab.c
+@item GCC_PRODUCER
+dwarfread.c
+@item GDB_TARGET_IS_HPPA
+This determines whether horrible kludge code in dbxread.c and partial-stab.h
+is used to mangle multiple-symbol-table files from HPPA's. This should all
+be ripped out, and a scheme like elfread.c used.
+@item GDB_TARGET_IS_MACH386
+mach386-xdep.c
+@item GDB_TARGET_IS_SUN3
+a68v-xdep.c
+@item GDB_TARGET_IS_SUN386
+sun386-xdep.c
+@item GET_LONGJMP_TARGET
+For most machines, this is a target-dependent parameter. On the DECstation
+and the Iris, this is a native-dependent parameter, since <setjmp.h> is
+needed to define it.
+
+This macro determines the target PC address that longjmp() will jump
+to, assuming that we have just stopped at a longjmp breakpoint. It
+takes a CORE_ADDR * as argument, and stores the target PC value through
+this pointer. It examines the current state of the machine as needed.
+@item GET_SAVED_REGISTER
+findvar.c
+@item GPLUS_PRODUCER
+dwarfread.c
+@item GR64_REGNUM
+remote-adapt.c
+@item GR64_REGNUM
+remote-mm.c
+@item HANDLE_RBRAC
+partial-stab.h
+@item HAVE_68881
+m68k-tdep.c
+@item HAVE_REGISTER_WINDOWS
+findvar.c
+@item HAVE_SIGSETMASK
+main.c
+@item HAVE_TERMIO
+inflow.c
+@item HEADER_SEEK_FD
+arm-tdep.c
+@item HOSTING_ONLY
+xm-rtbsd.h
+@item HOST_BYTE_ORDER
+ieee-float.c
+@item HPUX_ASM
+xm-hp300hpux.h
+@item HPUX_VERSION_5
+hp300ux-xdep.c
+@item HP_OS_BUG
+infrun.c
+@item I80960
+remote-vx.c
+@item IBM6000_TARGET
+Shows that we are configured for an IBM RS/6000 target. This conditional
+should be eliminated (FIXME) and replaced by feature-specific macros.
+It was introduced in haste and we are repenting at leisure.
+@item IEEE_DEBUG
+ieee-float.c
+@item IEEE_FLOAT
+valprint.c
+@item IGNORE_SYMBOL
+dbxread.c
+@item INIT_EXTRA_FRAME_INFO
+blockframe.c
+@item INIT_EXTRA_SYMTAB_INFO
+symfile.c
+@item INIT_FRAME_PC
+blockframe.c
+@item INNER_THAN
+valops.c
+@item INT_MAX
+defs.h
+@item INT_MIN
+defs.h
+@item IN_GDB
+i960-pinsn.c
+@item IN_SIGTRAMP
+infrun.c
+@item IN_SOLIB_TRAMPOLINE
+infrun.c
+@item ISATTY
+main.c
+@item IS_TRAPPED_INTERNALVAR
+values.c
+@item KERNELDEBUG
+dbxread.c
+@item KERNEL_DEBUGGING
+tm-ultra3.h
+@item LCC_PRODUCER
+dwarfread.c
+@item LOG_FILE
+remote-adapt.c
+@item LONGERNAMES
+cplus-dem.c
+@item LONGEST
+defs.h
+@item CC_HAS_LONG_LONG
+defs.h
+@item PRINTF_HAS_LONG_LONG
+defs.h
+@item LONG_MAX
+defs.h
+@item L_LNNO32
+coffread.c
+@item MACHKERNELDEBUG
+hppabsd-tdep.c
+@item MAINTENANCE
+dwarfread.c
+@item MIPSEL
+mips-tdep.c
+@item MOTOROLA
+xm-altos.h
+@item NBPG
+altos-xdep.c
+@item NEED_POSIX_SETPGID
+infrun.c
+@item NEED_TEXT_START_END
+exec.c
+@item NFAILURES
+regex.c
+@item NNPC_REGNUM
+infrun.c
+@item NOTDEF
+regex.c
+@item NOTDEF
+remote-adapt.c
+@item NOTDEF
+remote-mm.c
+@item NOTICE_SIGNAL_HANDLING_CHANGE
+infrun.c
+@item NO_HIF_SUPPORT
+remote-mm.c
+@item NO_SIGINTERRUPT
+remote-adapt.c
+@item NO_SINGLE_STEP
+infptrace.c
+@item NPC_REGNUM
+infcmd.c
+@item NS32K_SVC_IMMED_OPERANDS
+ns32k-opcode.h
+@item NUMERIC_REG_NAMES
+mips-tdep.c
+@item N_SETV
+dbxread.c
+@item N_SET_MAGIC
+hppabsd-tdep.c
+@item NaN
+tm-umax.h
+@item ONE_PROCESS_WRITETEXT
+breakpoint.c
+@item PC
+convx-opcode.h
+@item PCC_SOL_BROKEN
+dbxread.c
+@item PC_IN_CALL_DUMMY
+inferior.h
+@item PC_LOAD_SEGMENT
+stack.c
+@item PC_REGNUM
+parse.c
+@item PRINT_RANDOM_SIGNAL
+infcmd.c
+@item PRINT_REGISTER_HOOK
+infcmd.c
+@item PRINT_TYPELESS_INTEGER
+valprint.c
+@item PROCESS_LINENUMBER_HOOK
+buildsym.c
+@item PROLOGUE_FIRSTLINE_OVERLAP
+infrun.c
+@item PSIGNAL_IN_SIGNAL_H
+defs.h
+@item PS_REGNUM
+parse.c
+@item PUSH_ARGUMENTS
+valops.c
+@item REGISTER_BYTES
+remote.c
+@item REGISTER_NAMES
+tm-a29k.h
+@item REG_STACK_SEGMENT
+exec.c
+@item REG_STRUCT_HAS_ADDR
+findvar.c
+@item RE_NREGS
+regex.h
+@item R_FP
+dwarfread.c
+@item R_OK
+xm-altos.h
+@item SDB_REG_TO_REGNUM
+Define this to convert sdb register numbers
+into gdb regnums. If not defined, no conversion will be done.
+@item SEEK_END
+state.c
+@item SEEK_SET
+state.c
+@item SEM
+coffread.c
+@item SHELL_COMMAND_CONCAT
+infrun.c
+@item SHELL_FILE
+infrun.c
+@item SHIFT_INST_REGS
+breakpoint.c
+@item SIGN_EXTEND_CHAR
+regex.c
+@item SIGTRAP_STOP_AFTER_LOAD
+infrun.c
+@item SKIP_PROLOGUE
+tm-m68k.h
+@item SKIP_PROLOGUE_FRAMELESS_P
+blockframe.c
+@item SKIP_TRAMPOLINE_CODE
+infrun.c
+@item SOLIB_ADD
+core.c
+@item SOLIB_CREATE_INFERIOR_HOOK
+infrun.c
+@item SP_REGNUM
+parse.c
+@item STAB_REG_TO_REGNUM
+Define this to convert stab register numbers (as gotten from `r' declarations)
+into gdb regnums. If not defined, no conversion will be done.
+@item STACK_ALIGN
+valops.c
+@item START_INFERIOR_TRAPS_EXPECTED
+infrun.c
+@item STOP_SIGNAL
+main.c
+@item STORE_RETURN_VALUE
+tm-m68k.h
+@item SUN4_COMPILER_FEATURE
+infrun.c
+@item SUN_FIXED_LBRAC_BUG
+dbxread.c
+@item SVR4_SHARED_LIBS
+solib.c
+@item SWITCH_ENUM_BUG
+regex.c
+@item SYM1
+tm-ultra3.h
+@item SYMBOL_RELOADING_DEFAULT
+symfile.c
+@item SYNTAX_TABLE
+regex.c
+@item Sword
+regex.c
+@item TARGET_BYTE_ORDER
+defs.h
+@item TARGET_CHAR_BIT
+defs.h
+@item TARGET_COMPLEX_BIT
+defs.h
+@item TARGET_DOUBLE_BIT
+defs.h
+@item TARGET_DOUBLE_COMPLEX_BIT
+defs.h
+@item TARGET_FLOAT_BIT
+defs.h
+@item TARGET_INT_BIT
+defs.h
+@item TARGET_LONG_BIT
+defs.h
+@item TARGET_LONG_DOUBLE_BIT
+defs.h
+@item TARGET_LONG_LONG_BIT
+defs.h
+@item TARGET_PTR_BIT
+defs.h
+@item TARGET_READ_PC
+@item TARGET_WRITE_PC
+@item TARGET_READ_SP
+@item TARGET_WRITE_SP
+@item TARGET_READ_FP
+@item TARGET_WRITE_FP
+These change the behavior of @code{read_pc}, @code{write_pc},
+@code{read_sp}, @code{write_sp}, @code{read_fp} and @code{write_fp}.
+For most targets, these may be left undefined. GDB will call the
+read and write register functions with the relevant @code{_REGNUM} argument.
+
+These macros are useful when a target keeps one of these registers in a
+hard to get at place; for example, part in a segment register and part
+in an ordinary register.
+
+@item TARGET_SHORT_BIT
+defs.h
+@item TDESC
+infrun.c
+@item T_ARG
+coffread.c
+@item T_VOID
+coffread.c
+@item UINT_MAX
+defs.h
+@item USER
+m88k-tdep.c
+@item USE_GAS
+xm-news.h
+@item USE_STRUCT_CONVENTION
+values.c
+@item USIZE
+xm-m88k.h
+@item U_FPSTATE
+i386-xdep.c
+@item VARIABLES_INSIDE_BLOCK
+dbxread.c
+@item WRS_ORIG
+remote-vx.c
+@item _LANG_c
+language.c
+@item _LANG_m2
+language.c
+@item __GO32__
+inflow.c
+@item __HPUX_ASM__
+xm-hp300hpux.h
+@item __INT_VARARGS_H
+printcmd.c
+@item __not_on_pyr_yet
+pyr-xdep.c
+@item GOULD_PN
+gould-pinsn.c
+@item hp800
+xm-hppabsd.h
+@item hpux
+hppabsd-core.c
+@item longest_to_int
+defs.h
+@item mc68020
+m68k-stub.c
+@item ns32k_opcodeT
+ns32k-opcode.h
+@item sgi
+mips-tdep.c
+@item sparc
+regex.c
+@item sun
+m68k-tdep.c
+@item sun386
+tm-sun386.h
+@item test
+(Define this to enable testing code in regex.c.)
+@end table
+
+@node Native Conditionals
+@chapter Native Conditionals
+
+When GDB is configured and compiled, various macros are defined or left
+undefined, to control compilation when the host and target systems
+are the same. These macros should be defined (or left undefined)
+in @file{nm-@var{system}.h}.
+
+@table @code
+@item ATTACH_DETACH
+If defined, then gdb will include support for the @code{attach} and
+@code{detach} commands.
+@item FETCH_INFERIOR_REGISTERS
+Define this if the native-dependent code will provide its
+own routines
+@code{fetch_inferior_registers} and @code{store_inferior_registers} in
+@file{@var{HOST}-nat.c}.
+If this symbol is @emph{not} defined, and @file{infptrace.c}
+is included in this configuration, the default routines in
+@file{infptrace.c} are used for these functions.
+@item GET_LONGJMP_TARGET
+For most machines, this is a target-dependent parameter. On the DECstation
+and the Iris, this is a native-dependent parameter, since <setjmp.h> is
+needed to define it.
+
+This macro determines the target PC address that longjmp() will jump
+to, assuming that we have just stopped at a longjmp breakpoint. It
+takes a CORE_ADDR * as argument, and stores the target PC value through
+this pointer. It examines the current state of the machine as needed.
+@item PROC_NAME_FMT
+Defines the format for the name of a @file{/proc} device. Should be
+defined in @file{nm.h} @emph{only} in order to override the default
+definition in @file{procfs.c}.
+@item PTRACE_FP_BUG
+mach386-xdep.c
+@item PTRACE_ARG3_TYPE
+The type of the third argument to the @code{ptrace} system call, if it exists
+and is different from @code{int}.
+@item REGISTER_U_ADDR
+Defines the offset of the registers in the ``u area''; @pxref{Host}.
+@item USE_PROC_FS
+This determines whether small routines in @file{*-tdep.c}, which
+translate register values
+between GDB's internal representation and the /proc representation,
+are compiled.
+@item U_REGS_OFFSET
+This is the offset of the registers in the upage. It need only be
+defined if the generic ptrace register access routines in
+@file{infptrace.c} are being used (that is,
+@file{infptrace.c} is configured in, and
+@code{FETCH_INFERIOR_REGISTERS} is not defined). If the default value
+from @file{infptrace.c} is good enough, leave it undefined.
+
+The default value means that u.u_ar0 @emph{points to} the location of the
+registers. I'm guessing that @code{#define U_REGS_OFFSET 0} means that
+u.u_ar0 @emph{is} the location of the registers.
+@end table
+
+@node Obsolete Conditionals
+@chapter Obsolete Conditionals
+
+Fragments of old code in GDB sometimes reference or set the following
+configuration macros. They should not be used by new code, and
+old uses should be removed as those parts of the debugger are
+otherwise touched.
+
+@table @code
+@item STACK_END_ADDR
+This macro used to define where the end of the stack appeared, for use
+in interpreting core file formats that don't record this address in the
+core file itself. This information is now configured in BFD, and GDB
+gets the info portably from there. The values in GDB's configuration
+files should be moved into BFD configuration files (if needed there),
+and deleted from all of GDB's config files.
+
+Any @file{@var{foo}-xdep.c} file that references STACK_END_ADDR
+is so old that it has never been converted to use BFD. Now that's old!
+@end table
+
+@node XCOFF
+@chapter The XCOFF Object File Format
+
+The IBM RS/6000 running AIX uses an object file format called xcoff.
+The COFF sections, symbols, and line numbers are used, but debugging
+symbols are dbx-style stabs whose strings are located in the
+@samp{.debug} section (rather than the string table). For more
+information, @xref{Top,,,stabs,The Stabs Debugging Format}, and search
+for XCOFF.
+
+The shared library scheme has a nice clean interface for figuring out
+what shared libraries are in use, but the catch is that everything which
+refers to addresses (symbol tables and breakpoints at least) needs to be
+relocated for both shared libraries and the main executable. At least
+using the standard mechanism this can only be done once the program has
+been run (or the core file has been read).
+
+@contents
+@bye
diff --git a/gnu/usr.bin/gdb/doc/h8-cfg.texi b/gnu/usr.bin/gdb/doc/h8-cfg.texi
new file mode 100644
index 0000000..823c7c2
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/h8-cfg.texi
@@ -0,0 +1,47 @@
+@c GDB version number is recorded in the variable GDBVN
+@include GDBvn.texi
+@c
+@set AGGLOMERATION
+@clear AMD29K
+@set BARETARGET
+@clear CONLY
+@set DOSHOST
+@clear FORTRAN
+@clear FSFDOC
+@clear GDBSERVER
+@clear GENERIC
+@set H8
+@set H8EXCLUSIVE
+@clear HAVE-FLOAT
+@clear I960
+@clear MOD2
+@clear NOVEL
+@clear POSIX
+@set PRECONFIGURED
+@clear REMOTESTUB
+@set SIMS
+@clear SERIAL
+@clear SPARC
+@clear ST2000
+@clear VXWORKS
+@clear Z8K
+@c ----------------------------------------------------------------------
+@c STRINGS:
+@c
+@c Name of GDB program. Used also for (gdb) prompt string.
+@set GDBP gdb
+@c
+@c Name of GDB product. Used in running text.
+@set GDBN GDB
+@c
+@c Name of GDB initialization file.
+@set GDBINIT .gdbinit
+@c
+@c Name of target.
+@set TARGET Hitachi Microprocessors
+@c
+@c Name of GCC product
+@set NGCC GCC
+@c
+@c Name of GCC program
+@set GCC gcc
diff --git a/gnu/usr.bin/gdb/doc/libgdb.texinfo b/gnu/usr.bin/gdb/doc/libgdb.texinfo
new file mode 100644
index 0000000..c67c3a8
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/libgdb.texinfo
@@ -0,0 +1,1471 @@
+\input texinfo @c -*-texinfo-*-
+@c %**start of header
+@setfilename libgdb.info
+@settitle Libgdb
+@setchapternewpage odd
+@c %**end of header
+
+@ifinfo
+This file documents libgdb, the GNU library for symbolic debuggers.
+
+Copyright 1993 Cygnus Support
+
+Permission is granted to ...
+@end ifinfo
+
+@c This title page illustrates only one of the
+@c two methods of forming a title page.
+
+@titlepage
+@title Libgdb
+@subtitle Version 0.1
+@subtitle 27 Sep 1993
+@author Thomas Lord
+
+@c The following two commands
+@c start the copyright page.
+@page
+@vskip 0pt plus 1filll
+Copyright @copyright{} 1993 COPYRIGHT-OWNER
+
+Published by ...
+
+Permission is granted to ...
+@end titlepage
+
+@node Top, Overview, (dir), (dir)
+
+@ifinfo
+
+Libgdb is a library which provides the core functionality of a symbolic
+debugger. It is derived from GNU GDB and depends on the BFD library.
+
+This is an early draft of this document. Subsequent versions will likely
+contain revisions, deletions and additions.
+
+This document applies to version 0.0.
+
+Text marked `[[[' indicates areas which require expansion.
+
+Many nodes describe library entry points by giving a prototype and brief
+description:
+
+@deftypefun {const char **} gdb_warranty ()
+(warranty_info)
+Return a pointer to the text of the GDB disclaimer.
+@end deftypefun
+
+The parenthesized symbols (e.g. `(warranty_info)') refer to the
+existing GDB source and generally indicate where to find code with
+which to implement the library function.
+@end ifinfo
+
+@menu
+* Copying:: Your rights and freedoms.
+* Overview:: The basics of libgdb and this document.
+* Conventions:: Programming conventions for users of libgdb.
+* Targets:: Selecting debugging targets and symbol tables.
+* Symtabs:: Accessing symbol tables and debugging information.
+* Source:: Relating inferiors to source files.
+* Running:: Creating, continuing, and stepping through an
+ inferior process.
+* Stopping:: Using breakpoints, signaling an inferior.
+* Stack:: Accessing an inferior's execution stack.
+* Expressions:: How to parse and evaluate expressions in the
+ context of an inferior.
+* Values:: Data from the inferior, the values of expressions.
+* Examining:: Formatting values as strings.
+* Types:: Examining the types of an inferiors data.
+@end menu
+
+
+@node Copying, Overview, top, top
+@comment node-name, next, previous, up
+@chapter Copying
+@cindex copying
+
+blah blah
+
+@node Overview, Conventions, Copying, top
+@comment node-name, next, previous, up
+@chapter Overview
+@cindex overview
+@cindex definitions
+
+
+Libgdb is a library which provides the core functionality of a symbolic
+debugger. It is derived from GNU GDB and depends on the BFD library.
+
+target
+inferior
+
+
+
+@node Conventions, Targets, Overview, top
+@comment node-name, next, previous, up
+@chapter Programming Conventions for Libgdb Clients
+@cindex Conventions
+
+@heading Naming Conventions
+
+Names intentionally exported from libgdb all begin @code{gdb_}
+as in @code{gdb_use_file}.
+
+
+@heading Error Returns
+
+Libgdb functions that might not succeed generally have a return
+type of @code{gdb_error_t}.
+
+@deftypefun {const char *} gdb_error_msg (gdb_error_t @var{error})
+returns a reasonable error message for @var{error}.
+@end deftypefun
+
+
+@heading Blocking I/O
+
+[[[....]]]
+
+
+@heading Global Parameters
+@subheading the current directory
+@deftypefun gdb_error_t gdb_cd (char * @var{dir})
+Specify gdb's default directory as well as the working
+directory for the inferior (when first started).@*
+(cd_command)
+@end deftypefun
+
+@deftypefun {char *} gdb_copy_pwd ()
+Make a copy of the name of gdb's default directory.@*
+(pwd_command)
+@end deftypefun
+
+
+@subheading controlling the input/output radix
+@deftypefun gdb_error_t gdb_set_base (int)
+Change the default output radix to 10 or 16, or set it to 0
+(heuristic). This command is mostly obsolete now that the print
+command allows formats to apply to aggregates, but is still handy
+occasionally.@*
+(set_base_command)
+@end deftypefun
+
+@deftypefun gdb_error_t gdb_set_input_radix (int)
+@deftypefunx gdb_error_t gdb_set_output_radix (int)
+@deftypefunx gdb_error_t gdb_set_radix (int)
+Valid output radixes are only 0 (heuristic), 10, and 16.@*
+(set_radix)
+@end deftypefun
+
+
+@subheading manipulating environments
+@deftp Type {struct environ}
+@example
+struct environ
+@{
+ int allocated;
+ char ** vector;
+@}
+@end example
+A `struct environ' holds a description of environment
+variable bindings.
+@end deftp
+
+@deftypefun {struct environ *} gdb_make_environ ()
+Create a new (empty) environment.@*
+(make_environ)
+@end deftypefun
+
+@deftypefun {void} gdb_free_environ (struct environ *)
+Free an environment allocated by `gdb_make_environ'.@*
+(free_environ)
+@end deftypefun
+
+@deftypefun {void} gdb_init_environ (struct environ * env)
+Copy the processes environment into ENV.@*
+(init_environ)
+@end deftypefun
+
+@deftypefun {char **} gdb_get_in_environ (const struct environ * @var{env}, const char * @var{var})
+Look up the binding of @var{var} in @var{env}.@*
+(get_in_environ)
+@end deftypefun
+
+
+@deftypefun {void} gdb_set_in_environ (struct environ * @var{env}, const char * @var{var}, const char * @var{value})
+Lookup/bind variables within an environment.
+(set_in_environ)
+@end deftypefun
+
+
+@subheading legal notices
+@deftypefun {char **} gdb_copying ()
+@deftypefunx {char **} gdb_warranty ()
+These return pointers to NULL terminated arrays of strings.
+They contain text which describes the conditions under which
+libgdb is distributed (`gdb_copying') and which explains to
+users that there is no warranty for libgdb (`gdb_warranty').@*
+(show_warranty_command, show_copying_command)
+@end deftypefun
+
+
+@subheading the inferior's terminal
+@deftypefun void gdb_inferiors_io (int @var{std_in}, int @var{std_out}, int @var{std_err})
+Assert that the given descriptors should be copied into
+descriptors 0, 1, and 2 of the inferior when it
+is next run.
+@end deftypefun
+
+
+@heading callbacks
+
+One idiom used in several places deserves mention.
+At times, it makes sense for libgdb functions to
+invoke functions provided by the libgdb client.
+Where this is the case, callback structures are used
+to refer to client functions. For example, here
+are the declarations for a callback to which libgdb
+will pass an integer and a character pointer.
+
+@example
+struct a_gdb_cback;
+typedef void (*a_gdb_cback_fn) (struct a_gdb_cback *,
+ int, char *);
+@end example
+
+Suppose the client wants the callback to be implemented
+by @code{foo} which we will assume takes not only the integer
+and character pointer, but also a floating point number.
+The client could use these declarations:
+
+@example
+struct my_cback
+@{
+ struct a_gdb_cback gdb_cback; /* must be first */
+ float magic_number;
+@};
+
+void
+foo_helper (struct a_gdb_cback * callback, int i, char * cp)
+@{
+ foo ( ((struct my_cback *)callback)->magic_number, i, c);
+@}
+
+struct my_cback
+@{
+ foo_helper,
+ 1079252848.8
+@} the_cback;
+@end example
+
+
+@subheading stream callbacks
+
+A common kind of callback takes just a character pointer,
+presumed to point to part or all of an informational
+message.
+
+@example
+struct gdb_stream_cback;
+typedef void (*gdb_stream_cback_fn) (struct gdb_stream_cback *,
+ char *);
+@end example
+
+
+@subheading integer callbacks
+
+Another common kind of callback takes just an integer.
+
+@example
+struct gdb_int_cback;
+typedef void (*gdb_int_cback_fn) (struct gdb_int_cback *, int);
+@end example
+
+@node Targets, Symtabs, Conventions, top
+@comment node-name, next, previous, up
+@chapter Selecting Targets and Symbol Tables for Debugging
+@cindex targets
+
+@deftypefun gdb_error_t gdb_use_file (char * @var{filename})
+Arrange to read both executable code and symbol table information
+from FILENAME.
+
+This is exactly equivalent to a sequence of two calls:
+@example
+ gdb_use_exec_file (filename);
+ gdb_use_symbol_file (filename);
+@end example
+(file_command)
+@end deftypefun
+
+
+@deftypefun gdb_error_t gdb_use_exec_file (char * @var{filename})
+Read the code to debug from `filename'.@*
+(exec_file_command)
+@end deftypefun
+
+
+@deftypefun {char *} gdb_get_exec_file ()
+Return the name of the executable file as a string or 0
+if there is none.
+@end deftypefun
+
+
+@deftypefun gdb_error_t gdb_use_core (char * @var{filename})
+Specify the whereabouts of a core dump file to be used as the
+"contents of memory". Traditionally, core files contain only some
+parts of the address space of the process that generated them; GDB
+can access the executable file itself for other parts.
+
+If @var{filename} is @code{NULL}, no core file is used.@*
+(core_file_command)
+@end deftypefun
+
+
+@deftypefun gdb_error_t gdb_use_symbol_file (char * @var{filename})
+Arrange to read symbol table information from `filename'.
+
+This is the same as:
+
+ gdb_symbol_file_add (filename, 1, (CORE_ADDR)0, 1, 0, 0);
+
+See @code{gdb_symbol_file_add} for finer control over the symbol
+table.@*
+(symbol_file_command)
+@end deftypefun
+
+
+@deftypefun gdb_error_t gdb_symbol_file_add (@var{name}, @var{verbose}, @var{text_addr}, @var{replace}, @var{eager})
+Arrange to read additional symbol table information from
+the file `name'.
+
+The arguments are:
+@itemize @minus
+@item struct gdb_stream_cback * @var{info_out}
+
+Callback to handle informational output.
+
+@item char * @var{name}
+
+If not 0, verbose output will occur.
+
+@item int @var{be_verbose}
+
+Regulates the amount of informational output produced.
+
+@item CORE_ADDR @var{text_addr}
+
+is the address at which the named file is presumed to have
+been loaded.
+
+@item int @var{replace}@*
+
+If not 0, this will become the only file
+in the symbol table -- all previously loaded
+symbol table information will be discarded.
+
+@item int @var{readnow}
+
+If not 0, eagerly read symbols from this file,otherwise
+symbols will only be read lazily (as needed).
+@end itemize
+@end deftypefun
+
+
+@deftypefun {char *} gdb_copy_exec_path ()
+Make a copy of the execution path.@*
+[[[implement: strsave(get_in_environ (inferior_environ, "PATH"));]]]@*
+(path_info)
+@end deftypefun
+
+
+@deftypefun void gdb_mod_exec_path (char * @var{dirnames})
+Add zero or more directories to the front of the execution path.
+@var{dirnames} should be a colon separated list of directory names.@*
+(path_command)
+@end deftypefun
+
+
+@deftypefun gdb_error_t gdb_target_device (char * @var{name})
+Connects the libgdb host environment to a target machine
+or process.@*
+(target foo)
+@end deftypefun
+
+
+@deftypefun gdb_error_t gdb_set_baud (int @var{rate})
+If using a remote target connected by a serial port,
+use RATE as the communication speed.
+@end deftypefun
+
+
+@deftypefun gdb_error_t gdb_set_target_debugging (int @var{level})
+Choose the level of verboseness of with which a remote
+target produces debugging output.
+@end deftypefun
+
+@node Symtabs, Source, Targets, top
+@comment node-name, next, previous, up
+@chapter Accessing symbol tables and debugging information.
+@cindex Symtabs
+@cindex {Symbol Tables}
+
+@deftp Type {struct symtab}
+Each source file is represented by a struct symtab.
+In many contexts, @code{struct symtab *} is used in preference
+to a {char *} filename to refer to the source.
+@end deftp
+
+
+@deftypefun {char *} gdb_symtab_to_filename (struct symtab *)
+@deftypefunx {char *} gdb_symtab_to_dirname (struct symtab *)
+Return the location of the file corresponding to this symtab.
+@code{gdb_symtab_to_dirname} might return @code{NULL} if no directory
+is known. @code{gdb_symtab_to_line_count} might return -1 if line
+number information is unavailable.
+@end deftypefun
+
+@deftypefun int gdb_symtab_to_line_count (struct symtab *)
+(See also `Source')
+@end deftypefun
+
+
+@deftypefun {struct symtab *} gdb_filename_to_symtab (char * @var{filename})
+Lookup the symbol table of a source file named NAME.@*
+(lookup_symtab)
+@end deftypefun
+
+
+@deftp Type {struct symtab_and_line}
+@example
+struct symtab_and_line
+@{
+ struct symtab *symtab;
+ int line;
+ CORE_ADDR pc;
+ CORE_ADDR end;
+@}
+@end example
+
+@code{struct symtab_and_line} is used to refer to a particular line
+of source code. It is used to locate breakpoints in the source
+code and the executable.
+
+@code{line} starts at 1 and proceeds through symtab->nlines.
+0 is never a valid line number; it is used to indicate
+that line number information is not available.
+@end deftp
+
+
+@deftypefun {struct symtab_and_line} gdb_find_pc_line (CORE_ADDR @var{pc}, int @var{notcurrent})
+Find the source file and line number for a given @var{pc} value.
+Return a structure containing a symtab pointer, a line number,
+and a pc range for the entire source line.
+The value's @code{.pc} field is NOT the specified @var{pc}.
+@var{notcurrent} nonzero means, if specified pc is on a line boundary,
+use the line that ends there. Otherwise, in that case, the line
+that begins there is used.@*
+(find_pc_line)
+@end deftypefun
+
+
+@deftypefun gdb_error_t gdb_find_line (struct symtab_and_line * @var{out}, struct symtab *, int)
+Create a symtab_and_line for a given symtab and line number.
+In other words, if you know the source file and line,
+this returns a location for the breakpoint.@*
+(resolve_sal_pc)
+@end deftypefun
+
+
+@deftypefun {struct symtabs_and_lines} gdb_decode_line (@var{argptr}, @var{firstln}, @var{default_symtab}, @var{default_line}, @var{canonical})
+@example
+ char ** argptr;
+ int funfirstline;
+ struct symtab * default_symtab;
+ int default_line;
+ char *** canonical;
+@end example
+ Parse a string that specifies a line number in GDB syntax.
+ @var{argptr} will be advanced over the characters actually parsed.
+
+ The string can be:
+
+ LINENUM -- that line number in current file. PC returned is 0.
+ FILE:LINENUM -- that line in that file. PC returned is 0.
+ FUNCTION -- line number of openbrace of that function.
+ PC returned is the start of the function.
+ VARIABLE -- line number of definition of that variable.
+ PC returned is 0.
+ FILE:FUNCTION -- likewise, but prefer functions in that file.
+ *EXPR -- line in which address EXPR appears.
+
+ FUNCTION may be an undebuggable function found in minimal symbol
+ table.
+
+ If the argument FUNFIRSTLINE is nonzero, we want the first line
+ of real code inside a function when a function is specified.
+
+ DEFAULT_SYMTAB specifies the file to use if none is specified.
+ It defaults to current_source_symtab.
+
+ DEFAULT_LINE specifies the line number to use for relative line
+ numbers (that start with signs). Defaults to current_source_line.
+ If CANONICAL is non-NULL, store an array of strings containing the
+ canonical line specs there if necessary. Currently overloaded
+ member functions and line numbers or static functions without a
+ filename yield a canonical line spec. The array and the line spec
+ strings are allocated on the heap, it is the callers responsibility
+ to free them.
+
+ Note that it is possible to return zero for the symtab
+ if no file is validly specified. Callers must check that.
+ Also, the line number returned may be invalid.
+
+ The return value of this function includes allocated memory
+ which the caller is responsible for freeing:
+
+ struct symtabs_and_lines sals;
+ sals = decode_line_spec (arg, 1);
+ ....
+ free (sals.sals);@*
+(decode_line_1)
+@end deftypefun
+
+
+@deftp Type {struct block *}
+Lexical environments in the program are represented by struct block.
+These are useful as arguements to expression parsing functions (see
+`Expressions').
+@end deftp
+
+
+@deftypefun {struct block *} gdb_block_for_pc (CORE_ADDR)
+Return the innermost lexical block containing the
+specified pc value, or 0 if there is none.@*
+(block_for_pc)
+@end deftypefun
+
+
+@deftypefun {struct block *} gdb_get_frame_block (FRAME @var{frame})
+This returns the block being executed by a given
+stack frame (see `Stack')@*
+(get_frame_block)
+@end deftypefun
+
+
+@deftypefun int gdb_find_line_pc_range (@var{syms}, @var{line}, @var{start_out}, @var{end_out})
+@example
+struct symtab * @var{start_out};
+int @var{line};
+CORE_ADDR * @var{start_out};
+CORE_ADDR * @var{end_out};
+@end example
+Find the range of pc values in a line.@*
+Store the starting pc of the line into @code{*@var{startptr}}.
+and the ending pc (start of next line) into @code{*@var{endptr}}.
+
+Returns 1 to indicate success.@*
+Returns 0 if could not find the specified line.@*
+(find_line_pc_range)
+@end deftypefun
+
+
+@deftypefun int gdb_find_pc_partial_function (@var{pc}, @var{name}, @var{address}, @var{endaddr})
+@example
+CORE_ADDR @var{pc};
+char **@var{name};
+CORE_ADDR *@var{address};
+CORE_ADDR *@var{endaddr};
+@end example
+Finds the "function" (text symbol) that is smaller than @var{pc} but
+greatest of all of the potential text symbols. Sets @code{*@var{name}}
+and/or @code{*@var{address}} conditionally if that pointer is non-null. If
+@var{endaddr} is non-null, then set @code{*@var{endaddr}} to be the end of
+the function (exclusive), but passing @var{endaddr} as non-null means that
+the function might cause symbols to be read. This function either succeeds
+or fails (not halfway succeeds). If it succeeds, it sets
+@code{*@var{name}}, @code{*@var{address}}, and @code{*@var{endaddr}} to
+real information and returns 1. If it fails, it sets @code{*@var{name}},
+@code{*@var{address}}, and @code{*@var{endaddr}} to zero and returns 0.
+
+@example
+ pc = get_frame_pc (selected_frame);
+ if (find_pc_partial_function (pc, &name, &low, &high) == 0)
+ error ("No function contains program counter for selected frame.\n");
+@end example
+(find_pc_partial_function)
+@end deftypefun
+
+
+@deftypefun void gdb_list_symbols (@var{info_out}, @var{regexp}, @var{class}, @var{bpt})
+@example
+struct gdb_stream_cback * @var{info_out};
+char * @var{regexp};
+int @var{class};
+int @var{bpt};
+@end example
+List all symbols (if @var{regexp} is NULL) or all symbols matching @var{regexp}.
+
+
+If @var{class} is ...
+@itemize @bullet
+@item
+0, list all symbols except functions, type names, and
+constants (enums).
+@item
+1, list only functions.
+@item
+2, list only type names.
+@item
+3, list only method names.
+@end itemize
+BPT is non-zero if set a breakpoint at the functions we find.@*
+(variables_info, functions_info, types_info, list_symbols)
+@end deftypefun
+
+
+@deftypefun int gdb_locals_info (struct gdb_stream_cback * @var{info_out}, FRAME @var{frame})
+Print all the local variables in the given frame.
+including all the blocks active in that frame
+at its current pc.
+
+Returns 1 if the job was done,
+or 0 if nothing was printed because we have no info
+on the function running in @var{frame}.@*
+(locals_info)
+@end deftypefun
+
+
+@deftypefun int print_frame_arg_vars (struct gdb_stream_cback *, FRAME)
+Similar to `gdb_locals_info'.@*
+(args_info)
+@end deftypefun
+
+@node Source, Running, Symtabs, top
+@comment node-name, next, previous, up
+@chapter Relating Inferiors to Source Files
+@cindex source
+@cindex {source files}
+
+How to find the source that corresponds to executable code and the
+executable code that corresponds to a line of source.
+
+@deftypefun {char *} gdb_copy_source_fullname (struct symtab *@var{s})
+Return a copy of the full path name to a source file.
+(See `Symtabs' for more information about filenames
+and symbol tables.).
+@end deftypefun
+
+
+@deftypefun int gdb_open_source_file (struct symtab *@var{s})
+Open a source file corresponding to @var{s}. Returns a file descriptor
+or negative number for error.
+[[[We may decide not to provide this function.]]]@*
+(open_source_file)
+@end deftypefun
+
+
+@deftypefun int gdb_source_line_pos (struct symtab * @var{s}, int @var{lineno})
+Return the byte offset of a given line of source
+or a negative number if @var{lineno} is out of range.@*
+(find_source_lines)
+@end deftypefun
+
+
+ -- IDIOM: The gdb command `show directories'.
+@example
+ puts_filtered ("Source directories searched: ");
+ puts_filtered (source_path);
+ puts_filtered ("\n");
+@end example
+(show_directories)
+
+
+@deftypefun {char *} gdb_source_path ()
+Return the path in which source files are sought.@*
+(source_path)
+@end deftypefun
+
+
+@deftypefun void gdb_modify_source_path (char * @var{dirnames})
+Change the source path according to dirnames.@*
+(directory_command)
+@end deftypefun
+
+
+See `Symtabs' for functions relating symbol tables to files.
+(source_info)
+
+
+See `Symtabs' for functions relating source lines to PC values.
+(line_info)
+
+
+[[[Try to expose sources_info without having to introduce struct object *?]]]
+(sources_info)
+
+
+@node Running, Stopping, Source, top
+@comment node-name, next, previous, up
+@chapter Creating, Continuing, and Stepping Through an Inferior Process
+@cindex running
+
+
+@deftypefun gdb_error_t gdb_target_create_inferior (@var{exec}, @var{args}, @var{environ})
+@example
+char * @var{exec_file};
+char * @var{inferior_args};
+char ** @var{inferior_environment_vector};
+@end example
+Create a running inferior.
+[[[I think the exec_file parameter is redundant. Perhaps this will take
+only two arguments.]]]@*
+(run_command, target_create_inferior)
+@end deftypefun
+
+
+@deftypefun int gdb_target_has_execution ()
+Return non-0 if an inferior is running.@*
+(target_has_execution)
+@end deftypefun
+
+
+@deftypefun void gdb_target_kill ()
+Kill the inferior process. Make it go away.
+The inferior may become a core file.
+If so, gdb_target_has_stack() will return non-0.@*
+(target_kill)
+@end deftypefun
+
+
+@deftypefun gdb_error_t gdb_step_1 (@var{skip_subs}, @var{single_inst}, @var{repeat_count})
+@example
+int skip_subs;
+int single_inst;
+int repeat_count;
+@end example
+Continue a program a little bit. Roughly:
+@example
+ for (; count > 0; --count)
+ gdb_clear_proceed_status ();
+ gdb_proceed (...);
+@end example
+(next_command, nexti_command, step_command, stepi_command)
+@end deftypefun
+
+
+ -- IDIOM: Continuing a program where it stopped.
+@example
+ gdb_clear_proceed_status ();
+ gdb_proceed ((CORE_ADDR) -1, -1, 0);
+@end example
+(continue_command)
+
+
+ -- IDIOM: Continuing a program giving it a specified signal.
+@example
+ gdb_clear_proceed_status ();
+ gdb_proceed ((CORE_ADDR) -1, signum, 0);
+@end example
+(signal_command)
+
+
+@deftypefun {char *} strtosigno (char * @var{str})
+(Typical use:)
+@example
+ signum = strtosigno (signum_exp);
+
+ if (signum == 0)
+ /* Not found as a name, try it as an expression. */
+ signum = parse_and_eval_address (signum_exp);
+
+ gdb_clear_proceed_status ();
+ gdb_proceed ();
+@end example
+@end deftypefun
+
+
+ -- IDIOM: Continuing a program at a specified address.
+@example
+ gdb_clear_proceed_status ();
+ gdb_proceed (addr, 0, 0);
+@end example
+(jump_command)
+
+
+@deftypefun gdb_error_t gdb_finish ()
+"finish": Set a temporary breakpoint at the place
+the selected frame will return to, then continue.
+This is a convenience function but it summarizes a lot
+of other stuff.@*
+(finish_command)
+@end deftypefun
+
+
+@deftypefun void gdb_clear_proceed_status ()
+Clear out all variables saying what to do when inferior is continued.
+First do this, then set the ones you want, then call @code{gdb_proceed}.
+
+ [[[Some of these should be documented, others hidden.]]]
+@example
+ The variables are:
+ trap_expected = 0;
+ step_range_start = 0;
+ step_range_end = 0;
+ step_frame_address = 0;
+ step_over_calls = -1;
+ stop_after_trap = 0;
+ stop_soon_quietly = 0;
+ proceed_to_finish = 0;
+ breakpoint_proceeded = 1; /* We're about to proceed... */
+
+ /* Discard any remaining commands or status from previous stop. */
+ bpstat_clear (&stop_bpstat);
+@end example
+(clear_proceed_status)
+@end deftypefun
+
+
+@deftypefun void gdb_proceed (CORE_ADDR @var{addr}, int @var{signal}, int @var{step})
+Basic routine for continuing the program in various fashions.
+
+@var{addr} is the address to resume at, or -1 for resume where stopped.@*
+@var{signal} is the signal to give it, or 0 for none,
+or -1 for act according to how it stopped.@*
+@var{step} is nonzero if should trap after one instruction.
+-1 means return after that and print nothing.@*
+You should probably set various step_... variables
+before calling here, if you are stepping.
+
+You should call @code{gdb_clear_proceed_status} before calling proceed.
+(See the documentation for @code{gdb_clear_proceed_status} for more
+parameters to @code{gdb_proceed}).@*
+(proceed)
+@end deftypefun
+
+
+@deftypefun gdb_error_t gdb_return (value @var{return_value}, FRAME @var{frame})
+Make @var{frame} return to @var{value} to it's caller.
+Unlike the other functions in this section, this doesn't
+call proceed.
+(return_command)
+@end deftypefun
+
+
+@deftypefun int gdb_inferior_pid ()
+0 or the valid pid of an inferior.
+@end deftypefun
+
+
+@deftypefun gdb_error_t gdb_attach (int @var{pid})
+takes a program started up outside of gdb and
+`attaches'' to it. This stops it cold in its tracks and allows us
+to start debugging it. and wait for the trace-trap that results
+from attaching.@*
+(attach_command)
+@end deftypefun
+
+
+@deftypefun gdb_error_t gdb_detach (int @var{signal_num})
+Takes a program previously attached to and detaches it.
+The program resumes execution and will no longer stop
+on signals, etc. We better not have left any breakpoints
+in the program or it'll die when it hits one. For this
+to work, it may be necessary for the process to have been
+previously attached. It *might* work if the program was
+started via the normal ptrace (PTRACE_TRACEME).@*
+(detach_command)
+@end deftypefun
+
+@node Stopping, Stack, Running, top
+@comment node-name, next, previous, up
+@chapter Using Breakpoints, Signaling an Inferior
+@cindex stopping
+@cindex breakpoints
+
+
+@deftp Type {struct breakpoint}
+Breakpoints are typically represented @code{struct breakpoint *}.
+@end deftp
+
+
+@deftypefun {struct breakpoint *} gdb_find_breakpoint (int)
+Find a breakpoint given it's number (return 0 if it doesn't exist).
+@end deftypefun
+
+@deftypefun gdb_error_t gdb_set_break (struct breakpoint * @var{brk_out}, struct symtab_and_line)
+@deftypefunx gdb_error_t gdb_set_tbreak (struct breakpoint *, struct symtab_and_line)
+@deftypefunx gdb_error_t gdb_set_until (struct breakpoint *, struct symtab_and_line)
+These three are like their command language counterparts.
+They are front ends to `gdb_set_raw_breakpoint'.
+See `Symtabs' for sources of `struct symtab_and_line'.@*
+(break_command, break_command_1, until_command, tbreak_command)
+@end deftypefun
+
+
+@deftypefun gdb_error_t gdb_set_watchpt (@var{brk_out}, @var{exp_string}, @var{exp}, @var{exp_valid_block})
+@example
+struct breakpoint * @var{brk_out};
+char * @var{exp_string};
+struct expression * @var{exp};
+struct block * @var{expression_valid_block};
+@end example
+Set a watchpoint for the given expression.@*
+(watch_command)
+@end deftypefun
+
+
+@deftypefun void gdb_set_ignore_count (int @var{bptnum}, int @var{count})
+Set ignore-count of breakpoint number BPTNUM to COUNT.@*
+(set_ignore_count)
+@end deftypefun
+
+
+@deftypefun {struct gdb_bp_condition *} gdb_set_condition (@var{bp}, @var{exp_str}, @var{cond})
+@example
+int @var{pbtnum};
+char * @var{exp_str};
+struct gdb_bp_condition * @var{cond};
+
+typedef int (*gdb_bp_fn) (struct gdb_bp_condition *, int bp_num);
+struct gdb_bp_condition
+@{
+ gdb_bp_fn fn;
+@};
+@end example
+Add a condition to a breakpoint.
+The condition is a callback which should return
+0 to skip the breakpoint, and 1 to break at it.
+It is called at times when the break might occur.
+
+A useful application of these callbacks to attach
+an expression to breakpoints like the gdb `condition'
+command. See `Expressions' for the parsing and
+evaluation of expressions.@*
+(condition_command)
+@end deftypefun
+
+
+@deftypefun gdb_error_t gdb_enable_breakpoint (struct breakpoint * @var{bpt}, int @var{once})
+@deftypefunx gdb_error_t gdb_disable_breakpoint (struct breakpoint * @var{bpt})
+Enable/disable a breakpoint. If `once' is not 0, the
+breakpoint is only temporarily enabled.@*
+(enable_breakpoint, disable_breakpoint, enable_command)
+@end deftypefun
+
+
+@deftypefun gdb_error_t gdb_delete_breakpoint (struct breakpoint * @var{bpt})
+Delete a breakpoint and clean up all traces of it in the
+data structures.@*
+(delete_breakpoint)
+@end deftypefun
+
+
+@deftypefun void gdb_clear_breakpoints (struct symtabs_and_lines * @var{sals})
+Clear breakpoints from a list of program locations as
+might be returned by `gdb_decode_line' (see `Symtabs').@*
+(clear_command)
+@end deftypefun
+
+
+@deftypefun {static struct symtabs_and_lines} get_catch_sals (int @var{this_level_only})
+Return the line numbers of all exception handlers currently
+active (or `this_level_only'?? [[[?]]]).
+[[[The implementation should remember to resolve_sal_pc]]]
+@end deftypefun
+
+
+@deftp Type {struct breakpoint_cback}
+@example
+typedef void (*breakpoint_cback_fn) (struct breakpoint_cback *, int bp_num);
+struct breakpoint_cback
+@{
+ breakpoint_cback_fn fn;
+@};
+@end example
+
+Breakpoints can have an associated function which is called
+when the program is stopped by that breakpoint.@*
+(commands_command)
+@end deftp
+
+
+@deftypefun {struct breakpoint_cback *} gdb_set_breakpoint_cback (int @var{bp_num}, struct breakpoint_cback *)
+This sets a breakpoint callback and returns the previous callback value
+for that breakpoint.
+[[[In the long run, the command interpreter should be available
+ for the use of hooks like this one.]]]
+@end deftypefun
+
+
+@deftypefun {struct breakpoint_cback *} gdb_get_breakpoint_cback (int @var{bp_num})
+@end deftypefun
+
+
+@deftypefun void gdb_breakpoints_info (struct gdb_stream_cback, int @var{bp_num}, int @var{watches})
+Print information on breakpoint number @var{bnum}, or -1 if all.
+If @var{watches} is zero, process only breakpoints; if @var{watches}
+is nonzero, process only watchpoints.
+[[[In the long run, expose the information read off by this function.]]]@*
+(info breakpoints, info watchpoints, breakpoints_info, breakpoint_1)
+@end deftypefun
+
+
+@deftypefun void gdb_catch_info (struct gdb_stream_cback *)
+Print a list of all the exception handlers that are active in the
+current stack frame at the current point of execution.@*
+(catch_info)
+@end deftypefun
+
+
+@deftypefun void gdb_handle_command (char * @var{args})
+Takes arguments like the gdb command `handle' and has
+the same effect.@*
+(handle_command)
+@end deftypefun
+
+
+@deftypefun void gdb_signals_info (struct gdb_stream_cback *)
+Show how signals are handled.@*
+(signals_info)
+@end deftypefun
+
+
+@node Stack, Expressions, Stopping, top
+@comment node-name, next, previous, up
+@chapter Accessing An Inferior's Execution Stack
+@cindex stack
+@cindex FRAME
+@cindex {stack frames}
+
+
+
+@deftp Type FRAME
+This type representing active stack frames in the inferior.
+Consider this type opaque.
+@end deftp
+
+
+@deftypefun FRAME gdb_get_innermost_frame ()
+Returns the innermost frame or the frame most recently designated
+as current by a call to gdb_set_current_frame.@*
+(get_current_frame)
+@end deftypefun
+
+
+@deftypefun FRAME gdb_get_caller_frame (FRAME @var{frame})
+Return the frame that called @var{frame}.@*
+If @var{frame} is the original frame (it has no caller), return 0.@*
+(get_prev_frame)
+@end deftypefun
+
+
+@deftypefun FRAME gdb_get_called_frame (FRAME @var{frame})
+Return the frame that @var{frame} calls (0 if @var{frame} is the innermost
+frame).@*
+(get_next_frame)
+@end deftypefun
+
+
+@deftypefun FRAME gdb_parse_frame_specification (char * @var{frame_exp})
+Read a frame specification in whatever the appropriate format is.
+Call @code{error}() If the specification is in any way invalid (i.e.
+this function never returns NULL).@*
+(parse_frame_specification)
+@end deftypefun
+
+
+@deftypefun CORE_ADDR get_frame_pc (FRAME @var{frame})@*
+(Example use: Implementing @code{disassemble_command})@*
+(get_frame_pc)
+@end deftypefun
+
+
+@deftypefun FRAME gdb_selected_frame ()
+The "selected" stack frame is used by default for local and
+arg access. May be @code{NULL}, for no selected frame.@*
+(variable selected_frame)
+@end deftypefun
+
+
+@deftypefun int gdb_selected_frame_level ()
+Level of the selected frame:@*
+0 for innermost,@*
+1 for its caller,@*
+or -1 for frame specified by address with no defined level.@*
+(variable selected_frame_level)
+@end deftypefun
+
+
+@deftypefun void gdb_select_frame (FRAME @var{frame}, int @var{level})
+Select frame @var{frame}, and note that its stack level is @var{level}.
+@var{level} may be -1 if an actual level number is not known.
+Calls @code{set_language} to establish the correct language for the
+selected frame.
+@end deftypefun
+
+
+ -- IDIOM: Computing Frame Levels@*
+@example
+/* Try to figure out what level this frame is as before a
+ call to gdb_select_frame. But if there is
+ no current stack, don't error out, just pass -1
+ instead. */
+frame1 = 0;
+level = -1;
+if (get_current_frame()) @{
+ for (frame1 = get_prev_frame (0);
+ frame1 && frame1 != frame;
+ frame1 = get_prev_frame (frame1))
+ level++;
+@}
+@end example
+
+
+@deftypefun void gdb_print_stack_frame (@var{cback}, @var{frame}, @var{level}, @var{source})
+@example
+struct gdb_stream_cback * @var{cback};
+FRAME @var{frame};
+int @var{level};
+int @var{source};
+@end example
+Print a stack frame briefly. @var{frame} should be the frame id
+and @var{level} should be its level in the stack (or -1 for level not defined).
+This prints the level, the function executing, the arguments,
+and the file name and line number.@*
+If the pc is not at the beginning of the source line,
+the actual pc is printed at the beginning.@*
+If @var{source} is 1, print the source line as well.@*
+If @var{source} is -1, print ONLY the source line.@*
+(print_stack_frame)
+@end deftypefun
+
+
+@deftypefun void gdb_print_backtrace (cback, @var{count}, @var{from_tty})
+@example
+struct gdb_stream_cback * @var{cback};
+int @var{count};
+int @var{from_tty};
+@end example
+Print briefly all stack frames or just the innermost @var{count} frames.@*
+(backtrace_command)
+@end deftypefun
+
+
+@deftypefun FRAME gdb_find_relative_frame (FRAME @var{frame}, int * @var{level_offset_ptr})
+Find a frame a certain number of levels away from @var{frame}.
+@var{level_offset_ptr} points to an int containing the number of levels.
+Positive means go to earlier frames (up); negative, the reverse.
+The int that contains the number of levels is counted toward
+zero as the frames for those levels are found.
+If the top or bottom frame is reached, that frame is returned,
+but the final value of @var{*level_offset_ptr} is nonzero and indicates
+how much farther the original request asked to go.
+@end deftypefun
+
+
+@deftypefun FRAME gdb_select_frame_downward (int @var{count})
+@deftypefunx FRAME gdb_select_frame_upward (int @var{count})
+Simply a combination of find_relative_frame and select_frame.
+Returns the newly selected frame.@*
+(down_silently_command, up_silently_command)
+@end deftypefun
+
+
+@deftypefun void gdb_frame_info (struct gdb_stream_cback * @var{cback}, FRAME @var{frame})
+Print verbosely the selected the argument @var{frame}.
+This means absolutely all information in the frame is printed.@*
+(frame_info)
+@end deftypefun
+
+
+@node Expressions, Values, Stack, top
+@comment node-name, next, previous, up
+@chapter How to Parse and Evaluate Expressions
+@cindex parsing
+@cindex expressions
+@cindex {expression evaluation}
+@cindex evaluation
+
+
+@deftp Type {struct expression *}
+This represents a parsed expression as might be used for a
+breakpoint condition.
+@end deftp
+
+
+@deftp Type {struct block}
+Describes a lexical environment.
+@end deftp
+
+See also `Values'
+See also `Examining'
+
+
+@deftypefun struct expression * parse_exp_1 (char ** @var{stringptr}, struct block * @var{block} int @var{comma})
+Read an expression from the string @code{*@var{stringptr}} points to,
+parse it, and return a pointer to a struct expression that we malloc.
+Use @var{block} as the lexical context for variable names;
+if @var{block} is zero, use the block of the selected stack frame.
+Meanwhile, advance @code{*@var{stringptr}} to point after the expression,
+at the first nonwhite character that is not part of the expression
+(possibly a null character).
+
+If @var{comma} is nonzero, stop if a comma is reached.
+(See `Stack' for information about the selected frame)
+@end deftypefun
+
+
+@deftypefun gdb_error_t gdb_evaluate_expression (value * @var{value_out}, struct expression * @var{exp})
+Evaluate an expression. See `values' for more information about
+the return type.@*
+(evaluate_expression)
+@end deftypefun
+
+
+@deftypefun value gdb_evaluate_type (struct expression @var{*exp})
+Evaluate an expression, avoiding all memory references
+and getting a value whose type alone is correct.@*
+(evaluate_type)
+@end deftypefun
+
+
+
+@node Values, Examining, Expressions, top
+@comment node-name, next, previous, up
+@chapter Data from the Inferior, the Values of Expressions
+@cindex values
+@cindex {expression values}
+
+Values are allocated by functions such as @code{gdb_evaluate_expression}.
+All currently allocated values are on the list @code{all_values} and can be
+freed by calling @code{gdb_free_all_values}.
+
+To preserve a value across calls to @code{gdb_free_all_values}, use
+@code{gdb_release_value}. Values added to the history list are automaticly
+released. To free a released value use @code{gdb_free_value}.
+
+
+@deftypefun void gdb_free_value (value)
+Free the memory associated with a released value.
+Do not call this function except on values that have been
+passed to @code{gdb_release_value}.@*
+(gdb_value_free)
+@end deftypefun
+
+
+@deftypefun void gdb_free_all_values (void)
+Free all allocated values which haven't been released.
+This should be called periodically from outside the dynamic
+scope of libgdb functions.@*
+(free_all_values)
+@end deftypefun
+
+
+@deftypefun void gdb_release_value (value @var{val})
+Remove a value from the list @code{all_values} in order to
+protect it from @code{gdb_free_all_values}.@*
+(release_value)
+@end deftypefun
+
+
+There is a `history list' -- a numbered list of values for
+future reference. These can be referred to in expressions,
+for example.
+
+@deftypefun int gdb_record_latest_value (value @var{val})
+Add a value to the history list.@*
+(record_latest_value)
+@end deftypefun
+
+
+@deftypefun value gdb_access_value_history (int @var{index})
+Retrieve a value from the history list.@*
+(access_value_history)
+@end deftypefun
+
+
+[[[At the moment, the only libgdb use for values is
+ string formatting (see `Examining'). So, they are treated
+ as opaque. It'd be useful to expose more of them in the long run.]]]
+
+
+@node Examining, Types, Values, top
+@comment node-name, next, previous, up
+@chapter Formatting Values as Strings
+@cindex examining
+@cindex printing
+@cindex formatting
+@cindex {pretty printing}
+
+
+Many functions in this section use @code{struct gdb_stream_cback}.
+That structure is explained in `Basics'.
+
+
+@deftypefun void gdb_print_formatted (struct gdb_stream_cback * @var{cback}, value @var{val}, int @var{format}, int @var{size})
+Print value @var{val} on a stream according to @var{format}, a letter or 0.
+Do not end with a newline.
+0 means print @var{val} according to its own type.
+@var{size} is the letter for the size of datum being printed.
+This is used to pad hex numbers so they line up.@*
+(print_formatted)
+@end deftypefun
+
+
+@deftypefun static void gdb_printf_command (struct gdb_stream_cback * @var{cback}, char * @var{format}, value * @var{values}, int @var{n_values})@*
+(printf_command)
+@end deftypefun
+
+
+@deftypefun int gdb_value_print (struct gdb_stream_cback * @var{cback}, @var{value}, int @var{format}, enum @var{val_prettyprint})
+Print the value @var{val} in C-ish syntax on @var{stream}.
+@var{format} is a format-letter, or 0 for print in natural format of data type.
+If the object printed is a string pointer, returns
+the number of string bytes printed.
+[[[implementation: watch the change in argument order]]]@*
+(value_print)
+@end deftypefun
+
+
+ -- IDIOM: This prints the values of all convenience variables:
+@example
+for (var = internalvars; var; var = var->next)
+@{
+printf_filtered ("$%s = ", var->name);
+value_print (var->value, stdout, 0, Val_pretty_default);
+printf_filtered ("\n");
+@}
+@end example
+
+
+@deftypefun int gdb_print_insn (struct gdb_stream_cback * @var{cback}, CORE_ADDR @var{memaddr})
+Print the instruction at @var{memaddr} and return the
+length of the instruction in bytes.@*
+(print_insn)
+@end deftypefun
+
+
+@deftypefun void gdb_print_address (struct gdb_stream_cback * @var{cback}, CORE_ADDR @var{addr})
+Print address @var{addr} symbolically on @var{stream}.
+First print it as a number. Then perhaps print
+@code{<SYMBOL + OFFSET>} after the number.@*
+(print_address)
+@end deftypefun
+
+
+ -- IDIOM: This is the core of a dissasemble command:
+@example
+for (pc = low; pc < high; )
+@{
+ print_address (pc, stdout);
+ printf_filtered (":\t");
+ pc += print_insn (pc, stdout);
+ printf_filtered ("\n");
+@}
+@end example
+Advice for computing pc extents like @code{low} and @code{high}
+can be found in `Symtabs' -- for example, @code{gdb_find_line_pc_range}.@*
+(disassemble_command)
+
+
+@deftypefun void gdb_print_registers (struct gdb_stream_cback * @var{cback}, int @var{regnum}, int @var{fpregs}, int @var{fancy})
+Print the values of registers.
+@var{regnum} can be -1 (print all the registers) or a specific register number.
+If @var{regnum} is -1, @var{fpregs} determines whether floating point registers are
+shown.@*
+(info registers, info all-registers, nofp_registers_info, all_registers_info)
+@end deftypefun
+
+
+@deftypefun char * gdb_register_name (int @var{i})
+Look up a register name by number.
+@end deftypefun
+
+
+@deftypefun int gdb_parse_register_name (char ** @var{name})
+Parse a register name and advance a text pointer.
+Return -1 for bogus names.
+@end deftypefun
+
+
+@deftypefun CORE_ADDR gdb_read_pc ()
+Return the contents of the inferior's program counter.
+@end deftypefun
+
+
+@deftypefun int gdb_is_stepping ()
+If true, the inferior is stopped after being stepped.
+@end deftypefun
+
+
+@deftypefun void gdb_current_breakpoints (gdb_int_cback)
+Call a callback for each of the current breakpoints.@*
+(program_info)
+@end deftypefun
+
+
+@deftypefun int gdb_stop_signal ()
+Return the signal that stopped the inferior.
+@end deftypefun
+
+
+@deftypefun char * strsigno (int)
+Return a symbolic name for a signal.
+@end deftypefun
+
+
+@deftypefun void gdb_target_info (struct gdb_stream_cback *)
+Print status information about target we're accessing.@*
+(target_files_info, e.g. child_files_info)
+@end deftypefun
+
+
+float_info
+[[[what is appropriate?]]]
+
+
+@deftypefun void gdb_address_info (struct gdb_stream_cback * @var{cback}, char * @var{symbol});
+Like the `info address' command -- show where @var{symbol}
+is located.@*
+(address_info)
+@end deftypefun
+
+
+@node Types, top, Examining, top
+@comment node-name, next, previous, up
+@chapter Examining the Types of an Inferior's Data
+@cindex types
+
+
+@deftp Type {struct type}
+@code{struct type *} is used to represent a type. For example, that is
+the type returned by the macro @code{VALUE_TYPE(val)} which yields the
+type of inferior data recorded in @code{val}. (see `evaluate_type' in
+`Expressions').
+@end deftp
+
+
+@deftypefun void type_print (@var{type}, @var{varstring}, @var{stream_cback}, @var{show})
+@example
+struct type @var{*type};
+char @var{*varstring};
+struct gdb_stream_cback * @var{stream_cback};
+FILE @var{*stream};
+int @var{show};
+@end example
+Print a description of a type @var{type} in the form of a declaration of a
+variable named @var{varstring}. (@var{varstring} is demangled if necessary.)
+Output goes to @var{stream_cback}.
+
+If @var{show} is positive, we show the contents of the outermost level
+of structure even if there is a type name that could be used instead.
+If @var{show} is negative, we never show the details of elements' types.
+(See `Basics' for an explanation of `struct gdb_stream_cback').
+@end deftypefun
+
+
+[[[In the long run, we need something to programmaticly read off type
+ structures in a machine/language independent way.]]]
+
+@bye
diff --git a/gnu/usr.bin/gdb/doc/lpsrc.sed b/gnu/usr.bin/gdb/doc/lpsrc.sed
new file mode 100644
index 0000000..1c7af4a
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/lpsrc.sed
@@ -0,0 +1,13 @@
+/font defs: ---/,/end font defs ---/c\
+%-------------------- PostScript (long names) font defs: -----------------\
+\\font\\bbf=Times-Bold at 10pt\
+\\font\\vbbf=Times-Bold at 12pt\
+\\font\\smrm=Times-Roman at 6pt\
+\\font\\brm=Times-Roman at 10pt\
+\\font\\rm=Times-Roman at 8pt\
+\\font\\it=Times-Italic at 8pt\
+\\font\\tt=Courier at 8pt\
+% Used only for \copyright, replacing plain TeX macro.\
+\\font\\sym=Symbol at 7pt\
+\\def\\copyright{{\\sym\\char'323}}\
+%-------------------- end font defs ---------------------------------
diff --git a/gnu/usr.bin/gdb/doc/psrc.sed b/gnu/usr.bin/gdb/doc/psrc.sed
new file mode 100644
index 0000000..9bb557e
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/psrc.sed
@@ -0,0 +1,13 @@
+/font defs: ---/,/end font defs ---/c\
+%-------------------- PostScript (K Berry names) font defs: --------------\
+\\font\\bbf=ptmb at 10pt\
+\\font\\vbbf=ptmb at 12pt\
+\\font\\smrm=ptmr at 6pt\
+\\font\\brm=ptmr at 10pt\
+\\font\\rm=ptmr at 8pt\
+\\font\\it=ptmri at 8pt\
+\\font\\tt=pcrr at 8pt\
+% Used only for \copyright, replacing plain TeX macro.\
+\\font\\sym=psyr at 7pt\
+\\def\\copyright{{\\sym\\char'323}}\
+%-------------------- end font defs ---------------------------------
diff --git a/gnu/usr.bin/gdb/doc/refcard.ps b/gnu/usr.bin/gdb/doc/refcard.ps
new file mode 100644
index 0000000..0046b79
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/refcard.ps
@@ -0,0 +1,798 @@
+%!PS-Adobe-2.0
+%%Creator: dvips 5.47 Copyright 1986-91 Radical Eye Software
+%%Title: refcard.dvi
+%%Pages: 2 1
+%%BoundingBox: 0 0 612 792
+%%EndComments
+%%BeginProcSet: tex.pro
+/TeXDict 200 dict def TeXDict begin /N /def load def /B{bind def}N /S /exch
+load def /X{S N}B /TR /translate load N /isls false N /vsize 10 N /@rigin{
+isls{[0 1 -1 0 0 0]concat}if 72 Resolution div 72 VResolution div neg scale
+Resolution VResolution vsize neg mul TR matrix currentmatrix dup dup 4 get
+round 4 exch put dup dup 5 get round 5 exch put setmatrix}N /@letter{/vsize 10
+N}B /@landscape{/isls true N /vsize -1 N}B /@a4{/vsize 10.6929133858 N}B /@a3{
+/vsize 15.5531 N}B /@ledger{/vsize 16 N}B /@legal{/vsize 13 N}B /@manualfeed{
+statusdict /manualfeed true put}B /@copies{/#copies X}B /FMat[1 0 0 -1 0 0]N
+/FBB[0 0 0 0]N /nn 0 N /IE 0 N /ctr 0 N /df-tail{/nn 8 dict N nn begin
+/FontType 3 N /FontMatrix fntrx N /FontBBox FBB N string /base X array
+/BitMaps X /BuildChar{CharBuilder}N /Encoding IE N end dup{/foo setfont}2
+array copy cvx N load 0 nn put /ctr 0 N[}B /df{/sf 1 N /fntrx FMat N df-tail}
+B /dfs{div /sf X /fntrx[sf 0 0 sf neg 0 0]N df-tail}B /E{pop nn dup definefont
+setfont}B /ch-width{ch-data dup length 5 sub get}B /ch-height{ch-data dup
+length 4 sub get}B /ch-xoff{128 ch-data dup length 3 sub get sub}B /ch-yoff{
+ch-data dup length 2 sub get 127 sub}B /ch-dx{ch-data dup length 1 sub get}B
+/ch-image{ch-data dup type /stringtype ne{ctr get /ctr ctr 1 add N}if}B /id 0
+N /rw 0 N /rc 0 N /gp 0 N /cp 0 N /G 0 N /sf 0 N /CharBuilder{save 3 1 roll S
+dup /base get 2 index get S /BitMaps get S get /ch-data X pop /ctr 0 N ch-dx 0
+ch-xoff ch-yoff ch-height sub ch-xoff ch-width add ch-yoff setcachedevice
+ch-width ch-height true[1 0 0 -1 -.1 ch-xoff sub ch-yoff .1 add]{ch-image}
+imagemask restore}B /D{/cc X dup type /stringtype ne{]}if nn /base get cc ctr
+put nn /BitMaps get S ctr S sf 1 ne{dup dup length 1 sub dup 2 index S get sf
+div put}if put /ctr ctr 1 add N}B /I{cc 1 add D}B /bop{userdict /bop-hook
+known{bop-hook}if /SI save N @rigin 0 0 moveto}N /eop{clear SI restore
+showpage userdict /eop-hook known{eop-hook}if}N /@start{userdict /start-hook
+known{start-hook}if /VResolution X /Resolution X 1000 div /DVImag X /IE 256
+array N 0 1 255{IE S 1 string dup 0 3 index put cvn put}for}N /p /show load N
+/RMat[1 0 0 -1 0 0]N /BDot 260 string N /rulex 0 N /ruley 0 N /v{/ruley X
+/rulex X V}B /V statusdict begin /product where{pop product dup length 7 ge{0
+7 getinterval(Display)eq}{pop false}ifelse}{false}ifelse end{{gsave TR -.1 -.1
+TR 1 1 scale rulex ruley false RMat{BDot}imagemask grestore}}{{gsave TR -.1
+-.1 TR rulex ruley scale 1 1 false RMat{BDot}imagemask grestore}}ifelse B /a{
+moveto}B /delta 0 N /tail{dup /delta X 0 rmoveto}B /M{S p delta add tail}B /b{
+S p tail}B /c{-4 M}B /d{-3 M}B /e{-2 M}B /f{-1 M}B /g{0 M}B /h{1 M}B /i{2 M}B
+/j{3 M}B /k{4 M}B /w{0 rmoveto}B /l{p -4 w}B /m{p -3 w}B /n{p -2 w}B /o{p -1 w
+}B /q{p 1 w}B /r{p 2 w}B /s{p 3 w}B /t{p 4 w}B /x{0 S rmoveto}B /y{3 2 roll p
+a}B /bos{/SS save N}B /eos{clear SS restore}B end
+%%EndProcSet
+TeXDict begin 1000 300 300 @start /Fa 3 104 df<0003FE0000000FFF8000003C01E000
+00F000780001C0001C00030000060006000003000C0000018018000000C018000000C030000000
+603000000060600000003060000000306000000030C000000018C000000018C000000018C00000
+0018C000000018C000000018C000000018C000000018C000000018600000003060000000306000
+0000303000000060300000006018000000C018000000C00C000001800600000300030000060001
+C0001C0000F0007800003C01E000000FFF80000003FE000025277E9D2A>13
+D<003C00E001C00180038003800380038003800380038003800380038003800380038003000700
+1C00F0001C00070003000380038003800380038003800380038003800380038003800380018001
+C000E0003C0E297D9E15>102 D<F0001C00070003000380038003800380038003800380038003
+800380038003800380018001C000E0003C00E001C0018003800380038003800380038003800380
+03800380038003800380030007001C00F0000E297D9E15>I E /Fb 1 59
+df<60F0F06004047C830C>58 D E /Fc 61 125 df<01F8000604000C0E00180E001800001800
+00180000FFFE001806001806001806001806001806001806001806001806001806001806001806
+007E1F801114809313>12 D<01FE00060E000C0E00180600180600180600180600FFFE00180600
+1806001806001806001806001806001806001806001806001806001806007E1F801114809313>
+I<4100E380618020802080208041004100820009097F9311>34 D<020002000F8032406220C210
+C270C270E220F2007F003FC00FE002E002704230E230C2308220426022C01F00020002000C187E
+9511>36 D<40E06020202040408003097D9309>39 D<01020408103020606040C0C0C0C0C0C0C0
+C0C0C040606020301008040201081E7E950D>I<80402010080C04060602030303030303030303
+03020606040C0810204080081E7E950D>I<006000006000006000006000006000006000006000
+006000006000006000FFFFF0FFFFF0006000006000006000006000006000006000006000006000
+00600000600014167E9119>43 D<40E06020202040408003097D8209>I<FFFF080280860B>I<40
+E04003037D8209>I<0010003000600060006000C000C000C00180018003000300030006000600
+06000C000C000C0018001800300030003000600060006000C000C0000C1D7E9511>I<0F0030C0
+606060604020C030C030C030C030C030C030C030C030C03040206060606030C00F000C137E9211
+>I<0C001C00EC000C000C000C000C000C000C000C000C000C000C000C000C000C000C000C00FF
+C00A137D9211>I<1F0060C06060F070F030603000700070006000C001C0018002000400081010
+1020207FE0FFE00C137E9211>I<40E0400000000000000040E040030D7D8C09>58
+D<40E0400000000000000040E06020202040408003137D8C09>I<003000003000007800007800
+007800009C00009C00011E00010E00010E0002070002070004038007FF800403800801C00801C0
+1000E03800E0FE07FC16147F9319>65 D<FFFC001C07001C03801C01C01C01C01C01C01C01C01C
+03801C07001FFE001C03801C01C01C00E01C00E01C00E01C00E01C00E01C01C01C0380FFFE0013
+147F9317>I<00FC200703600C00E0180060300060700020600020E00000E00000E00000E00000
+E00000E000006000207000203000201800400C008007030000FC0013147E9318>I<FFFC001C07
+001C01C01C00E01C00601C00701C00301C00381C00381C00381C00381C00381C00381C00301C00
+701C00601C00E01C01C01C0380FFFC0015147F9319>I<FFFF801C03801C00801C00801C00401C
+00401C08401C08001C18001FF8001C18001C08001C08201C00201C00201C00601C00401C00C01C
+01C0FFFFC013147F9316>I<00FC200703600C00E0180060300060700020600020E00000E00000
+E00000E00000E00FF8E000E06000E07000E03000E01800E00C00E007036000FC2015147E931A>
+71 D<FFC0001C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C0000
+1C00401C00401C00401C00C01C00801C01801C0380FFFF8012147F9315>76
+D<FE000FE01E000F00170017001700170017001700138027001380270011C0470011C0470010E0
+870010E0870010E087001071070010710700103A0700103A0700101C0700101C0700381C0700FE
+083FE01B147F931E>I<FC01FC1E007017002017802013802011C02010E0201070201070201038
+20101C20100E20100F201007201003A01001E01000E01000E0380060FE002016147F9319>I<01
+F800070E001C03803801C03000C07000E0600060E00070E00070E00070E00070E00070E0007070
+00E07000E03000C03801C01C0380070E0001F80014147E9319>I<FFFC001C07001C03801C01C0
+1C01C01C01C01C01C01C01C01C03801C07001FFC001C00001C00001C00001C00001C00001C0000
+1C00001C0000FF800012147F9316>I<FFF8001C07001C03801C01C01C01C01C01C01C01C01C03
+801C07001FF8001C0E001C07001C03801C03801C03801C03801C03841C03841C01CCFF80F81614
+7F9318>82 D<7FFFF0607030407010407010807008807008807008007000007000007000007000
+00700000700000700000700000700000700000700000700007FF0015147F9318>84
+D<FF81FC1C00701C00201C00201C00201C00201C00201C00201C00201C00201C00201C00201C00
+201C00201C00200C00200E004006008003830000FC0016147F9319>I<FF1FF0FC380380303803
+80301C03C0201C03C0201E03C0600E04E0400E04E0400704E08007087080070870800388710003
+9039000390390001F03E0001E01E0001E01E0000C00C0000C00C0000C00C001E147F9321>87
+D<FF00FC3C00301E00200E004007004007808003C10001C10001E20000F6000074000038000038
+0000380000380000380000380000380000380001FF0016147F9319>89 D<208041004100820082
+008200C300E380410009097A9311>92 D<7F00E1C0E0404060006007E038606060C060C064C064
+61E43E380E0D7E8C11>97 D<F00030003000300030003000300033E034103808300C3006300630
+0630063006300C3808343023E00F147F9312>I<0FE0187020706020C000C000C000C000C00060
+00201018200FC00C0D7F8C0F>I<00780018001800180018001800180F98187820386018C018C0
+18C018C018C0186018203810580F9E0F147F9312>I<0F80104020206030C010FFF0C000C000C0
+006000201018200FC00C0D7F8C0F>I<03C00CE018E01840180018001800FF0018001800180018
+0018001800180018001800180018007F000B1480930A>I<0F3C30E62040606060606060204030
+C02F00600060003FE03FF06018C00CC00CC00C601830300FC00F147F8C11>I<F0003000300030
+0030003000300033E034303818301830183018301830183018301830183018FC7E0F147F9312>
+I<2070200000000000F03030303030303030303030FC06157F9409>I<02070200000000000F03
+0303030303030303030303030343E2E67C081B82940A>I<F00030003000300030003000300030
+F8306030403080330037003B80318030C0306030703030FC7C0E147F9311>I<F0303030303030
+303030303030303030303030FC06147F9309>I<F3E1F0343218381C0C30180C30180C30180C30
+180C30180C30180C30180C30180C30180CFC7E3F180D7F8C1B>I<F3E034303818301830183018
+301830183018301830183018FC7E0F0D7F8C12>I<0FC0186020106018C00CC00CC00CC00CC00C
+6018601838700FC00E0D7F8C11>I<F3E034303808300C30063006300630063006300C38083430
+33E030003000300030003000FC000F137F8C12>I<0F88184820386018C018C018C018C018C018
+6018203818580F9800180018001800180018007E0F137F8C11>I<F3C034E038E0304030003000
+300030003000300030003000FE000B0D7F8C0D>I<3E806180C080C080E0007E003F8003C080C0
+80C0C0C0E1809F000A0D7F8C0D>I<10001000100030007000FF80300030003000300030003000
+300030803080308011000E0009127F910D>I<F078301830183018301830183018301830183018
+303818580F9E0F0D7F8C12>I<F87C301830101820182018200C400C4006800680078003000300
+0E0D7F8C11>I<F87CF8707030305820305820188840188C40188C400D04800D06800D06800603
+00060300060300150D7F8C18>I<F87C303018600C400C800700030007800CC008E010603030F8
+7C0E0D7F8C11>I<F87C301830101820182018200C400C400680068007800300030002000200E6
+00E400E80070000E137F8C11>I<FFF0C06080C081C08180030006000C101810383030206060FF
+E00C0D7F8C0F>I<FFFFFFFF2001808821>124 D E /Fd 2 94 df<FEFEC0C0C0C0C0C0C0C0C0C0
+C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0FEFE07297C9E0C>91
+D<FEFE060606060606060606060606060606060606060606060606060606060606060606060606
+06FEFE0729809E0C>93 D E /Fe 27 123 df<001F8F000020D9800060990000C0300000C03000
+00C0300000C0300007FFFE000180300001806000018060000180600001806000030060000300C0
+000300C0000300C0000300C0000600C00006018000060180000601800004010000CCC30000C8C6
+000070780000191A819315>11 D<000FF000301800601000600000C00000C00000C00007FFE000
+C0600180600180C00180C00180C00180C00181800301900301900301900301A00300E006000006
+0000060000C40000C80000700000151A819314>I<FEFC07027D860C>45
+D<000080000180000300000300000600000600000C000018000018000030000030000060000060
+0000C0000180000180000300000300000600000C00000C00001800001800003000003000006000
+00C00000C00000800000111D7E9512>47 D<07C03F8000C00C0001600800016008000120080001
+300800023010000218100002181000020C1000040C200004042000040620000402200008034000
+080340000801C0000801C00018008000FE00800019147E9319>78 D<07B00C7010703060606060
+606060C0C0C0C8C0C841C862D03C700D0D7C8C12>97 D<7C000C00180018001800180030003700
+388030C060C060C060C060C0C180C180C1004300660038000A147C9310>I<07800C4010603040
+600060006000C000C0004020404021801E000B0D7C8C10>I<007C000C00180018001800180030
+07B00C7010703060606060606060C0C0C0C8C0C841C862D03C700E147C9312>I<07800C401020
+304060407F8060004000C0004020604021801E000B0D7C8C10>I<001C00660064006000C000C0
+00C007F800C001800180018001800180030003000300030003000200060006000600C400C80070
+000F1A81930B>I<01D8023804380C3018301830183030603060306010E019C00EC000C000C001
+80C180C3007C000D137E8C10>I<3E0006000C000C000C000C00180019E01E3018303830303030
+3030306060606460C460C4C0C8C0700E147D9312>I<02060000000000384C4C8C981818303262
+62643807147D930B>I<7C0C181818183030303060606060C0D0D0D0D06006147C9309>108
+D<30F878590D8C4E0E0C9C0E0C980C0C180C0C180C0C3018183018193018313018316030326030
+1C180D7D8C1C>I<30F05B184C189C189818181818183030303230623062606460380F0D7D8C13>
+I<03800C6018203030603060306030C060C06040C0608023001E000C0D7C8C12>I<0C78168C13
+0426062606060606060C0C0C0C0C080C101A2019C018001800300030003000FC000F137F8C12>
+I<31F05A184C109C009800180018003000300030003000600060000D0D7D8C0F>114
+D<0700188018C0308038001E001F0003800180C180810082007C000A0D7D8C0E>I<04000C000C
+000C001800FF8018001800300030003000300060006100610062006400380009127D910C>I<38
+184C184C188C3098301830183030603064306430E411E80E380E0D7D8C12>I<38104C384C108C
+10981018101810302030203040304018800F000D0D7D8C10>I<071E09E311C221802180018001
+800300030403044308C51078E0100D7F8C10>120 D<38184C184C188C30983018301830306030
+60306030E011C00EC000C000802180630046003C000D137D8C11>I<06100F2010E00040008001
+0002000C00102020203840478083000C0D7E8C0E>I E /Ff 55 123 df<0100030003000F803F
+E073704338C338C338C31073007F003FC00FE003F003384318E318E318E33073603FC00F800300
+030001000D1A7E9612>36 D<07001F8019C039C039C039C039BE3B3E3E701C701C701CE03EE06F
+E0E7C0E3C4E38E63CE7EFC3C380F147F9312>38 D<070007000700E738FFF87FF01FC01FC07FF0
+FFF8E7380700070007000D0E7E9012>42 D<038003800380038003800380FFFEFFFEFFFE038003
+8003800380038003800F0F7F9112>I<60F0F878183030E0C00509798312>I<FFF8FFF8FFF80D03
+7E8B12>I<60F0F0600404798312>I<0018003800380070007000E000E001C001C001C003800380
+070007000E000E001C001C001C003800380070007000E000E000C0000D1A7E9612>I<07C00FE0
+1C703838701C701CE00EE00EE00EE00EE00EE00EE00EE01E701C701C38381C700FE007C00F147F
+9312>I<0F803FC070E0E070E038E038403800380030007000E000C00180030006000C00183830
+387FF87FF80D147E9312>50 D<0FE03FF07838701C201C001C0038007807E007F00038001C000E
+000E400EE00EE01C78383FF00FC00F147F9312>I<E000FFFEFFFEE018E038007000E000C001C0
+0380038007000700070007000E000E000E000E000E0004000F157F9412>55
+D<60F0F06000000000000060F0F060040E798D12>58 D<0038007801F003E00F801F003C00F800
+F000F8003C001F000F8003E001F0007800380D117E9212>60 D<FFFEFFFE7FFE0000000000007F
+FEFFFEFFFE0F097F8E12>I<4000E000F0007C003E000F8007C001E000F8007800F801E007C00F
+803E007C00F000E00040000D137E9312>I<03E007F01E18381C30FC71FE739EE30EE70EE70EE7
+0EE70EE30C739C71F830F038001E0E07FE03F80F147F9312>64 D<03E60FFE1C3E381E700E700E
+600EE000E000E000E000E000E000600E700E700E381C1C380FF003E00F147F9312>67
+D<FFFEFFFE380E380E380E3800380038E038E03FE03FE038E038E03800380E380E380E380EFFFE
+FFFE0F147F9312>69 D<FFFEFFFE380E380E380E3800380038E038E03FE03FE038E038E0380038
+00380038003800FF00FF000F147F9312>I<FFE0FFE00E000E000E000E000E000E000E000E000E
+000E000E000E000E000E000E000E00FFE0FFE00B147D9312>73 D<FC7EFC7E7C7C745C76DC76DC
+76DC76DC76DC76DC77DC739C739C701C701C701C701C701CF83EF83E0F147F9312>77
+D<FEFEFEFE3E383A383B383B383B383B383B383B3839B839B839B839B839B839B838B838F8FEF8
+FEF80F147F9312>I<3FE07FF07070E038E038E038E038E038E038E038E038E038E038E038E038
+E038E03870707FF03FE00D147E9312>I<FFE0FFF8383C381C380E380E380E380E381C383C3FF8
+3FE0380038003800380038003800FE00FE000F147F9312>I<FF80FFE038F03878383838383838
+387838F03FE03FC038E0387038703870387038773877FE3EFE1C10147F9312>82
+D<1F303FF070F0E070E070E070E00070007F003FC00FE000F0003800386038E038E030F070FFE0
+CF800D147E9312>I<7FFEFFFEE38EE38EE38E0380038003800380038003800380038003800380
+0380038003801FF01FF00F147F9312>I<FE3F80FE3F80380E00380E00380E00380E00380E0038
+0E00380E00380E00380E00380E00380E00380E00380E00380E001C1C000E380007F00003E00011
+14809312>I<3F807FC070E0207000700FF03FF07870E070E070E07070F03FFE1F3E0F0E7E8D12>
+97 D<F800F80038003800380038003BE03FF03C38381C380C380E380E380E380E380C381C3C38
+3FF01BC00F147F9312>I<07F01FF8383870106000E000E000E000E0006000703838381FF007E0
+0D0E7E8D12>I<00F800F8003800380038003807B81FF8387870386038E038E038E038E0386038
+707838781FFE0FBE0F147F9312>I<07801FE0387070706038E038FFF8FFF8E000600070383838
+1FF007C00D0E7E8D12>I<007E00FF01C70382038003807FFEFFFE038003800380038003800380
+03800380038003803FF83FF81014809312>I<0F9E1FFF38E7707070707070707038E03FC03F80
+70003FE03FF83FFC701EE00EE00EE00E600C783C1FF00FE010167F8D12>I<F800F80038003800
+3800380039E03FF03E383C3838383838383838383838383838383838FE3EFE3E0F147F9312>I<
+06000F000F000600000000000000FF00FF000700070007000700070007000700070007000700FF
+F0FFF00C157D9412>I<00C001E001E000C00000000000001FE01FE000E000E000E000E000E000
+E000E000E000E000E000E000E000E000E000E040C0E1C0FF807E000B1C7E9412>I<F800F80038
+003800380038003BFC3BFC38F039E03BC03F803F803FC03DE038E038703838FC7EFC7E0F147F93
+12>I<FF00FF000700070007000700070007000700070007000700070007000700070007000700
+FFF8FFF80D147E9312>I<F71C00FFBE0079E70079E70071C70071C70071C70071C70071C70071
+C70071C70071C700F9E780F8E380110E808D12>I<F9E0FFF03E383C3838383838383838383838
+383838383838FE3EFE3E0F0E7F8D12>I<0F803FE038E07070E038E038E038E038E038F0787070
+38E03FE00F800D0E7E8D12>I<FBE0FFF03C38381C380C380E380E380E380E380C381C3C383FF0
+3BC038003800380038003800FE00FE000F157F8D12>I<079C1FFC387C703C601CE01CE01CE01C
+E01C601C703C387C1FFC079C001C001C001C001C001C007F007F10157F8D12>I<FCF8FDFC1F1C
+1E081E001C001C001C001C001C001C001C00FFC0FFC00E0E7E8D12>I<1FF03FF06070C070E000
+7F003FE00FF000786018E018F030FFE0DFC00D0E7E8D12>I<06000E000E000E007FF8FFF80E00
+0E000E000E000E000E000E000E380E380E3807F003C00D127F9112>I<F8F8F8F8383838383838
+38383838383838383838383838781FFE0FBE0F0E7F8D12>I<FC7EFC7E38383C781C701C701C70
+0EE00EE00EE006C007C007C003800F0E7F8D12>I<FEFEFEFE701C701C301838383BB83FF83FF8
+3AB838B81CF01CF01CF00F0E7F8D12>I<7C7C7C7C1CF00EE00FC007C00380078007C00EE01EF0
+1C70FC7EFC7E0F0E7F8D12>I<FC7EFC7E3C381C381C701C700E700E600E6006E006E003C003C0
+03C0038003800380778077007E003C000F157F8D12>I<3FFC7FFC7038707000E001C003800700
+0E001C1C381C701CFFFCFFFC0E0E7F8D12>I E /Fg 35 122 df<00038000000380000007C000
+0007C0000007C000000FE000000FE000001FF000001BF000001BF0000031F8000031F8000061FC
+000060FC0000E0FE0000C07E0000C07E0001803F0001FFFF0003FFFF8003001F8003001F800600
+0FC006000FC00E000FE00C0007E0FFC07FFEFFC07FFE1F1C7E9B24>65 D<FFFFF800FFFFFF000F
+C01F800FC00FC00FC007C00FC007E00FC007E00FC007E00FC007E00FC007E00FC007C00FC00F80
+0FC03F000FFFFE000FC00F800FC007C00FC007E00FC003E00FC003F00FC003F00FC003F00FC003
+F00FC003F00FC007E00FC007E00FC01FC0FFFFFF00FFFFFC001C1C7E9B22>I<001FE02000FFF8
+E003F80FE007C003E00F8001E01F0000E03E0000E03E0000607E0000607C000060FC000000FC00
+0000FC000000FC000000FC000000FC000000FC000000FC0000007C0000607E0000603E0000603E
+0000C01F0000C00F80018007C0030003F80E0000FFFC00001FE0001B1C7D9B22>I<FFFFF800FF
+FFFF000FC01FC00FC007E00FC001F00FC001F80FC000F80FC000FC0FC0007C0FC0007C0FC0007E
+0FC0007E0FC0007E0FC0007E0FC0007E0FC0007E0FC0007E0FC0007E0FC0007C0FC0007C0FC000
+7C0FC000F80FC000F80FC001F00FC007E00FC01FC0FFFFFF00FFFFF8001F1C7E9B25>I<FFFFFF
+00FFFFFF000FC01F000FC007000FC003000FC003800FC003800FC181800FC181800FC181800FC1
+80000FC380000FFF80000FFF80000FC380000FC180000FC180000FC180600FC180600FC000E00F
+C000C00FC000C00FC001C00FC001C00FC003C00FC00F80FFFFFF80FFFFFF801B1C7E9B1F>I<FF
+FFFF00FFFFFF000FC01F000FC007000FC003000FC003800FC003800FC001800FC181800FC18180
+0FC180000FC180000FC380000FFF80000FFF80000FC380000FC180000FC180000FC180000FC180
+000FC000000FC000000FC000000FC000000FC000000FC00000FFFF0000FFFF0000191C7E9B1E>
+I<000FF008007FFE3801FC07F807E001F80F8000781F0000783F0000383E0000387E0000187C00
+0018FC000000FC000000FC000000FC000000FC000000FC000000FC007FFFFC007FFF7C0001F87E
+0001F83E0001F83F0001F81F0001F80F8001F807E001F801FC07F8007FFE78000FF818201C7D9B
+26>I<FFFC3FFFFFFC3FFF0FC003F00FC003F00FC003F00FC003F00FC003F00FC003F00FC003F0
+0FC003F00FC003F00FC003F00FFFFFF00FFFFFF00FC003F00FC003F00FC003F00FC003F00FC003
+F00FC003F00FC003F00FC003F00FC003F00FC003F00FC003F00FC003F0FFFC3FFFFFFC3FFF201C
+7E9B25>I<FFFF00FFFF000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000F
+C0000FC0000FC0000FC0000FC0000FC0000FC0030FC0030FC0030FC0070FC0070FC0060FC00E0F
+C01E0FC07EFFFFFEFFFFFE181C7E9B1D>76 D<FFE003FFFFE003FF0FF000300FF800300DFC0030
+0CFE00300C7E00300C3F00300C1F80300C1FC0300C0FE0300C07F0300C03F0300C01F8300C01FC
+300C00FE300C007F300C003F300C001FB00C001FF00C000FF00C0007F00C0003F00C0001F00C00
+00F00C0000F0FFC00070FFC00030201C7E9B25>78 D<FFFFF800FFFFFE000FC03F800FC00F800F
+C007C00FC007E00FC007E00FC007E00FC007E00FC007E00FC007C00FC007C00FC00F800FC03F00
+0FFFFC000FC000000FC000000FC000000FC000000FC000000FC000000FC000000FC000000FC000
+000FC000000FC00000FFFC0000FFFC00001B1C7E9B21>80 D<07F8201FFEE03C07E07801E07000
+E0F000E0F00060F00060F80000FE0000FFE0007FFE003FFF003FFF800FFFC007FFE0007FE00003
+F00001F00000F0C000F0C000F0C000E0E000E0F001C0FC03C0EFFF0083FC00141C7D9B1B>83
+D<7FFFFFE07FFFFFE0781F81E0701F80E0601F8060E01F8070C01F8030C01F8030C01F8030C01F
+8030001F8000001F8000001F8000001F8000001F8000001F8000001F8000001F8000001F800000
+1F8000001F8000001F8000001F8000001F8000001F8000001F800007FFFE0007FFFE001C1C7E9B
+21>I<FFFC03FFFFFC03FF0FC000300FC000300FC000300FC000300FC000300FC000300FC00030
+0FC000300FC000300FC000300FC000300FC000300FC000300FC000300FC000300FC000300FC000
+300FC000300FC0003007C0003007C0006003E000E001F001C000FC0780007FFE00000FF800201C
+7E9B25>I<FFFC7FFE0FFCFFFC7FFE0FFC0FC007E000C00FC007F000C00FE003F001C007E003F0
+018007E007F8018003F007F8030003F007F8030003F80CFC070001F80CFC060001F81CFE060001
+FC187E0E0000FC187E0C0000FC387F0C00007E303F1800007E303F1800007F601FB800003F601F
+B000003FE01FF000003FC00FF000001FC00FE000001FC00FE000000F8007C000000F8007C00000
+0F0003C0000007000380000007000380002E1C7F9B31>87 D<0FF8001C1E003E0F803E07803E07
+C01C07C00007C0007FC007E7C01F07C03C07C07C07C0F807C0F807C0F807C0780BC03E13F80FE1
+F815127F9117>97 D<FF0000FF00001F00001F00001F00001F00001F00001F00001F00001F0000
+1F00001F3F801FE1E01F80701F00781F003C1F003C1F003E1F003E1F003E1F003E1F003E1F003E
+1F003C1F003C1F00781F80701EC1E01C3F00171D7F9C1B>I<03FC000E0E001C1F003C1F00781F
+00780E00F80000F80000F80000F80000F80000F800007800007801803C01801C03000E0E0003F8
+0011127E9115>I<000FF0000FF00001F00001F00001F00001F00001F00001F00001F00001F000
+01F001F9F00F07F01C03F03C01F07801F07801F0F801F0F801F0F801F0F801F0F801F0F801F078
+01F07801F03C01F01C03F00F0FFE03F9FE171D7E9C1B>I<01FC000F07001C03803C01C07801C0
+7801E0F801E0F801E0FFFFE0F80000F80000F800007800007C00603C00601E00C00F038001FC00
+13127F9116>I<03F8F00E0F381E0F381C07303C07803C07803C07803C07801C07001E0F000E0E
+001BF8001000001800001800001FFF001FFFC00FFFE01FFFF07801F8F00078F00078F000787000
+707800F01E03C007FF00151B7F9118>103 D<FF0000FF00001F00001F00001F00001F00001F00
+001F00001F00001F00001F00001F0FC01F31E01F40F01F80F81F80F81F00F81F00F81F00F81F00
+F81F00F81F00F81F00F81F00F81F00F81F00F81F00F8FFE7FFFFE7FF181D7F9C1B>I<1E003F00
+3F003F003F001E00000000000000000000000000FF00FF001F001F001F001F001F001F001F001F
+001F001F001F001F001F001F00FFE0FFE00B1E7F9D0E>I<FF0000FF00001F00001F00001F0000
+1F00001F00001F00001F00001F00001F00001F0FF81F0FF81F03801F07001F0C001F18001F7000
+1FF8001FFC001FBC001F3E001F1F001F0F001F0F801F07C01F03E0FFC7FCFFC7FC161D7F9C19>
+107 D<FF00FF001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F00
+1F001F001F001F001F001F001F001F001F00FFE0FFE00B1D7F9C0E>I<FF0FC07E00FF31E18F00
+1F40F207801F80FC07C01F80FC07C01F00F807C01F00F807C01F00F807C01F00F807C01F00F807
+C01F00F807C01F00F807C01F00F807C01F00F807C01F00F807C01F00F807C0FFE7FF3FF8FFE7FF
+3FF825127F9128>I<FF0FC0FF31E01F40F01F80F81F80F81F00F81F00F81F00F81F00F81F00F8
+1F00F81F00F81F00F81F00F81F00F81F00F8FFE7FFFFE7FF18127F911B>I<01FC000F07801C01
+C03C01E07800F07800F0F800F8F800F8F800F8F800F8F800F8F800F87800F07800F03C01E01E03
+C00F078001FC0015127F9118>I<FF3F80FFE1E01F80F01F00781F007C1F003C1F003E1F003E1F
+003E1F003E1F003E1F003E1F003C1F007C1F00781F80F01FC1E01F3F001F00001F00001F00001F
+00001F00001F0000FFE000FFE000171A7F911B>I<FE3E00FE47001E8F801E8F801E8F801F0700
+1F00001F00001F00001F00001F00001F00001F00001F00001F00001F0000FFF000FFF00011127F
+9114>114 D<1FD830786018E018E018F000FF807FE07FF01FF807FC007CC01CC01CE01CE018F8
+30CFC00E127E9113>I<0300030003000300070007000F000F003FFCFFFC1F001F001F001F001F
+001F001F001F001F001F0C1F0C1F0C1F0C0F08079803F00E1A7F9913>I<FF07F8FF07F81F00F8
+1F00F81F00F81F00F81F00F81F00F81F00F81F00F81F00F81F00F81F00F81F00F81F01F80F01F8
+0786FF01F8FF18127F911B>I<FFC7FCFFC7FC1F81800F838007C70003EE0001FC0001F80000F8
+00007C0000FE0001DF00039F00070F800607C00C03E0FF07FCFF07FC16127F9119>120
+D<FFC1FCFFC1FC1F00601F80E00F80C00FC0C007C18007C18003E30003E30001F70001F60000FE
+0000FC0000FC00007800007800003000003000007000706000F86000F8C000F980007300003E00
+00161A7F9119>I E /Fh 47 122 df<020408103020604040C0C0C0C0C0C0C0C0404060203010
+080402071A7F920C>40 D<8040201018080C0404060606060606060604040C081810204080071A
+7E920C>I<40E060202040408003087E8209>44 D<40E04003037E8209>46
+D<0C003C00CC000C000C000C000C000C000C000C000C000C000C000C000C00FF8009107E8F0F>
+49 D<1F00618040C08060C0600060006000C00180030006000C00102020207FC0FFC00B107F8F
+0F>I<1F00218060C060C000C0008001800F00008000400060C060C060804060801F000B107F8F
+0F>I<0300030007000F000B001300330023004300C300FFE003000300030003001FE00B107F8F
+0F>I<1F00318060C0C040C060C060C06040E021E01E600060004060C0608043003E000B107F8F
+0F>57 D<40E040000000000040E060202040408003107E8A09>59 D<03E0000C180010040023C2
+004631004C0D00980C80980C80980C80980C80980C804C0C80463C8023C7001000000C038003FC
+0011117E9017>64 D<FFF8180C18061803180318031806181C1FFC180618031803180318031806
+180EFFF810117F9015>66 D<03F10C0B1807300360014001C000C000C000C000C0004001600130
+0218020C0C03F010117E9016>I<FFF800180C001803001801001801801800801800C01800C018
+00C01800C01800C01800C0180180180100180300180E00FFF80012117F9017>I<FFFE18061802
+180318011821182018601FE01860182018201800180018001800FF0010117F9014>70
+D<03F1000C0B00180700300300600100400100C00000C00000C00000C03FC0C003004003006003
+003003001803000C070003F90012117E9017>I<FF181818181818181818181818181818FF0811
+7F900B>73 D<FF0018001800180018001800180018001800180018021802180218061804181CFF
+FC0F117F9013>76 D<F81FC01C07001C020016020013020013020011820010C20010C200106200
+103200101200101A00100E00100600380600FE020012117F9016>78 D<FFF0181C180418061806
+18061804181C1FF01800180018001800180018001800FF000F117F9014>80
+D<FFE000181800180C00180600180600180600180C001818001FE000181800180C00180C00180C
+00180C00180C20180420FF03C013117F9016>82 D<1F2060E0006080208020800060003E001F80
+00C00060002080208020C040E0C09F000B117E9011>I<7FFF8060C18040C080C0C0C080C04080
+C04000C00000C00000C00000C00000C00000C00000C00000C00000C00000C0000FFC0012117F90
+16>I<FF1FC0180700180200180200180200180200180200180200180200180200180200180200
+1802000802000C040006080001F00012117F9016>I<FC07C03003003003001802001802000C04
+000C040006080006080006080003100003100001A00001A00001E00000C00000C00012117F9016
+>I<3E006300018001800F8031804180C190C19063903DE00C0B7F8A0F>97
+D<F0003000300030003000300037C038603030301830183018301830183030386027800D117F90
+11>I<1F8030C06000C000C000C000C000C000604030801F000A0B7F8A0E>I<01E0006000600060
+006000600F6030E06060C060C060C060C060C060606030E01F780D117F9011>I<1F00318060C0
+C0C0FFC0C000C000C000604030801F000A0B7F8A0E>I<07000D801800180018001800FE001800
+180018001800180018001800180018007E00091180900A>I<1EF0331061806180618033003E00
+400060003F803FC060E0C060C060C06060C01F000C117F8A0F>I<F00030003000300030003000
+33C03C6030603060306030603060306030603060FCF80D117F9011>I<30703000000000F03030
+30303030303030FC0612809108>I<F0003000300030003000300031F030C03080330036003F00
+3300318031C030C0F9F00C117F9010>107 D<F0303030303030303030303030303030FC061180
+9008>I<F3C3C03C2C20383830303030303030303030303030303030303030303030FCFCFC160B
+7F8A1A>I<F3C03C6030603060306030603060306030603060FCF80D0B7F8A11>I<1F00318060C0
+C060C060C060C060C06060C031801F000B0B7F8A0F>I<F7C03860303030183018301830183018
+3030386037803000300030003000FC000D107F8A11>I<F7003980300030003000300030003000
+30003000FC00090B7F8A0C>114 D<3E028280783E038181C2BC080B7F8A0C>I<10103030FE3030
+303030323232321C070F7F8E0C>I<F1E03060306030603060306030603060306030E00F780D0B
+7F8A11>I<F8F0706030403040188018800D000D000D00060006000C0B7F8A10>I<F9F3C060E180
+30E10030E1003131001932001A12000E1C000E1C000C0C00040800120B7F8A16>I<F8F0306030
+403040188018800D000D000F000600060004000400CC00C80070000C107F8A10>121
+D E /Fi 12 86 df<FFFFFF8000FFFFFFE00007F001F80007F000FC0007F0007E0007F0007E00
+07F0007F0007F0007F0007F0007F0007F0007F0007F0007F0007F0007E0007F000FE0007F000FC
+0007F003F80007FFFFF00007FFFFF00007F001FC0007F0007E0007F0003F0007F0003F8007F000
+1F8007F0001FC007F0001FC007F0001FC007F0001FC007F0001FC007F0001FC007F0003F8007F0
+003F8007F0007F0007F001FE00FFFFFFF800FFFFFFC00022227EA128>66
+D<0003FE0080001FFF818000FF01E38001F8003F8003E0001F8007C0000F800F800007801F8000
+07803F000003803F000003807F000001807E000001807E00000180FE00000000FE00000000FE00
+000000FE00000000FE00000000FE00000000FE00000000FE000000007E000000007E000001807F
+000001803F000001803F000003801F800003000F8000030007C000060003F0000C0001F8003800
+00FF00F000001FFFC0000003FE000021227DA128>I<FFFFFF8000FFFFFFF00007F003FC0007F0
+007E0007F0003F0007F0001F8007F0000FC007F00007E007F00007E007F00007F007F00003F007
+F00003F007F00003F007F00003F807F00003F807F00003F807F00003F807F00003F807F00003F8
+07F00003F807F00003F807F00003F807F00003F007F00003F007F00003F007F00007E007F00007
+E007F0000FC007F0001F8007F0003F0007F0007E0007F003FC00FFFFFFF000FFFFFF800025227E
+A12B>I<FFFFFFFCFFFFFFFC07F000FC07F0003C07F0001C07F0000C07F0000E07F0000E07F000
+0607F0180607F0180607F0180607F0180007F0380007F0780007FFF80007FFF80007F0780007F0
+380007F0180007F0180007F0180307F0180307F0000307F0000607F0000607F0000607F0000E07
+F0000E07F0001E07F0003E07F001FCFFFFFFFCFFFFFFFC20227EA125>I<FFFFFFF8FFFFFFF807
+F001F807F0007807F0003807F0001807F0001C07F0001C07F0000C07F0000C07F0180C07F0180C
+07F0180007F0180007F0380007F0780007FFF80007FFF80007F0780007F0380007F0180007F018
+0007F0180007F0180007F0000007F0000007F0000007F0000007F0000007F0000007F0000007F0
+0000FFFFE000FFFFE0001E227EA123>I<0003FE0040001FFFC0C0007F00F1C001F8003FC003F0
+000FC007C00007C00FC00003C01F800003C03F000001C03F000001C07F000000C07E000000C07E
+000000C0FE00000000FE00000000FE00000000FE00000000FE00000000FE00000000FE00000000
+FE000FFFFC7E000FFFFC7F00001FC07F00001FC03F00001FC03F00001FC01F80001FC00FC0001F
+C007E0001FC003F0001FC001FC003FC0007F80E7C0001FFFC3C00003FF00C026227DA12C>I<FF
+FFE0FFFFE003F80003F80003F80003F80003F80003F80003F80003F80003F80003F80003F80003
+F80003F80003F80003F80003F80003F80003F80003F80003F80003F80003F80003F80003F80003
+F80003F80003F80003F80003F80003F800FFFFE0FFFFE013227FA115>73
+D<FFFF803FFCFFFF803FFC07F000038007F000070007F0000E0007F000180007F000300007F000
+E00007F001C00007F003800007F007000007F00E000007F018000007F038000007F0FC000007F1
+FE000007F3FE000007F77F000007FE7F800007F83F800007F01FC00007F01FE00007F00FE00007
+F007F00007F007F80007F003F80007F001FC0007F001FE0007F000FF0007F0007F0007F0007F80
+07F0003FC0FFFF83FFFCFFFF83FFFC26227EA12C>75 D<FFF8001FFEFFFC001FFE07FC0000C007
+FE0000C006FF0000C0067F8000C0063FC000C0061FE000C0060FE000C0060FF000C00607F800C0
+0603FC00C00601FE00C00600FE00C00600FF00C006007F80C006003FC0C006001FE0C006000FF0
+C0060007F0C0060007F8C0060003FCC0060001FEC0060000FFC00600007FC00600007FC0060000
+3FC00600001FC00600000FC006000007C006000003C006000003C0FFF00001C0FFF00000C02722
+7EA12C>78 D<0007FC0000003FFF800000FC07E00003F001F80007E000FC000FC0007E001F8000
+3F001F80003F003F00001F803F00001F807F00001FC07E00000FC07E00000FC0FE00000FE0FE00
+000FE0FE00000FE0FE00000FE0FE00000FE0FE00000FE0FE00000FE0FE00000FE0FE00000FE07E
+00000FC07F00001FC07F00001FC03F00001F803F81F03F801F83F83F000FC70C7E0007E606FC00
+03F607F80000FF07E000003FFF80000007FF80200000038020000001C020000001E0E0000001FF
+E0000001FFC0000000FFC0000000FFC00000007F800000007F000000001E00232C7DA12A>81
+D<FFFFFE0000FFFFFFC00007F007F00007F001F80007F000FC0007F0007E0007F0007F0007F000
+7F0007F0007F0007F0007F0007F0007F0007F0007F0007F0007E0007F000FC0007F001F80007F0
+07F00007FFFFC00007FFFF800007F00FE00007F007F00007F003F80007F001FC0007F001FC0007
+F001FC0007F001FC0007F001FC0007F001FC0007F001FC0007F001FC0007F001FC0607F000FE06
+07F000FF0CFFFF803FF8FFFF800FF027227EA12A>I<FFFF803FFCFFFF803FFC07F000018007F0
+00018007F000018007F000018007F000018007F000018007F000018007F000018007F000018007
+F000018007F000018007F000018007F000018007F000018007F000018007F000018007F0000180
+07F000018007F000018007F000018007F000018007F000018007F000018007F000018003F00003
+0003F800030001F800060000FC000E00007E001C00003F80F800000FFFE0000001FF000026227E
+A12B>85 D E end
+%%EndProlog
+%%BeginSetup
+%%Feature: *Resolution 300
+TeXDict begin @landscape
+%%EndSetup
+%%Page: 1 1
+bop -225 -183 a Fi(GDB)14 b(QUICK)g(REFERENCE)22 b Fh(GDB)14
+b(V)n(ersion)h(4)-225 -91 y Fg(Essen)o(tial)c(Commands)-225
+-45 y Ff(gdb)i Fe(pr)n(o)n(gr)n(am)f Fd([)p Fe(c)n(or)n(e)p
+Fd(])24 b Fc(debug)14 b Fe(pr)n(o)n(gr)n(am)e Fd([)p Fc(using)i(coredump)g
+Fe(c)n(or)n(e)p Fd(])-225 9 y Ff(b)f Fd([)p Fe(\014le)p Ff(:)p
+Fd(])p Fe(function)68 b Fc(set)13 b(breakp)q(oin)o(t)i(at)e
+Fe(function)g Fd([)p Fc(in)h Fe(\014le)p Fd(])-225 63 y Ff(run)f
+Fd([)p Fe(ar)n(glist)p Fd(])126 b Fc(start)13 b(y)o(our)h(program)f
+Fd([)p Fc(with)h Fe(ar)n(glist)p Fd(])-225 106 y Ff(bt)274
+b Fc(bac)o(ktrace:)20 b(displa)o(y)c(program)d(stac)o(k)-225
+143 y Ff(p)g Fe(expr)214 b Fc(displa)o(y)15 b(the)f(v)n(alue)h(of)e(an)h
+(expression)-225 180 y Ff(c)292 b Fc(con)o(tin)o(ue)14 b(running)h(y)o(our)f
+(program)-225 218 y Ff(n)292 b Fc(next)14 b(line,)h(stepping)g(o)o(v)o(er)f
+(function)g(calls)-225 255 y Ff(s)292 b Fc(next)14 b(line,)h(stepping)g(in)o
+(to)f(function)h(calls)-225 352 y Fg(Starting)c(GDB)-225 398
+y Ff(gdb)256 b Fc(start)13 b(GDB,)h(with)g(no)g(debugging)g(\014les)-225
+435 y Ff(gdb)f Fe(pr)n(o)n(gr)n(am)121 b Fc(b)q(egin)14 b(debugging)g
+Fe(pr)n(o)n(gr)n(am)-225 472 y Ff(gdb)f Fe(pr)n(o)n(gr)n(am)f(c)n(or)n(e)48
+b Fc(debug)14 b(coredump)g Fe(c)n(or)n(e)f Fc(pro)q(duced)h(b)o(y)105
+510 y Fe(pr)n(o)n(gr)n(am)-225 548 y Ff(gdb)f(--help)135 b
+Fc(describ)q(e)14 b(command)g(line)g(options)-225 647 y Fg(Stopping)c(GDB)
+-225 692 y Ff(quit)238 b Fc(exit)14 b(GDB;)g(also)g Ff(q)f
+Fc(or)g Ff(EOF)g Fc(\(eg)g Ff(C-d)p Fc(\))-225 734 y Ff(INTERRUPT)148
+b Fc(\(eg)13 b Ff(C-c)p Fc(\))g(terminate)g(curren)o(t)h(command,)h(or)105
+771 y(send)g(to)e(running)i(pro)q(cess)-225 868 y Fg(Getting)c(Help)-225
+914 y Ff(help)238 b Fc(list)14 b(classes)g(of)g(commands)-225
+951 y Ff(help)e Fe(class)155 b Fc(one-line)14 b(descriptions)h(for)f
+(commands)g(in)105 988 y Fe(class)-225 1021 y Ff(help)e Fe(c)n(ommand)83
+b Fc(describ)q(e)14 b Fe(c)n(ommand)-225 1119 y Fg(Executing)e(y)o(our)h
+(Program)-225 1165 y Ff(run)g Fe(ar)n(glist)150 b Fc(start)13
+b(y)o(our)h(program)f(with)i Fe(ar)n(glist)-225 1203 y Ff(run)256
+b Fc(start)13 b(y)o(our)h(program)f(with)i(curren)o(t)f(argumen)o(t)105
+1240 y(list)-225 1273 y Ff(run)f Fb(:)7 b(:)g(:)12 b Ff(<)p
+Fe(inf)h Ff(>)p Fe(outf)32 b Fc(start)13 b(y)o(our)h(program)f(with)i(input,)
+g(output)105 1310 y(redirected)-225 1360 y Ff(kill)238 b Fc(kill)15
+b(running)g(program)-225 1434 y Ff(tty)e Fe(dev)193 b Fc(use)14
+b Fe(dev)f Fc(as)g(stdin)i(and)f(stdout)g(for)g(next)g Ff(run)-225
+1472 y(set)f(args)f Fe(ar)n(glist)66 b Fc(sp)q(ecify)15 b Fe(ar)n(glist)d
+Fc(for)i(next)g Ff(run)-225 1509 y(set)f(args)171 b Fc(sp)q(ecify)15
+b(empt)o(y)f(argumen)o(t)g(list)-225 1546 y Ff(show)e(args)154
+b Fc(displa)o(y)15 b(argumen)o(t)f(list)-225 1621 y Ff(show)e(environment)28
+b Fc(sho)o(w)13 b(all)i(en)o(vironmen)o(t)g(v)n(ariables)-225
+1659 y Ff(show)d(env)h Fe(var)110 b Fc(sho)o(w)13 b(v)n(alue)i(of)f(en)o
+(vironmen)o(t)h(v)n(ariable)f Fe(var)-225 1696 y Ff(set)f(env)g
+Fe(var)f(string)28 b Fc(set)13 b(en)o(vironmen)o(t)j(v)n(ariable)e
+Fe(var)-225 1733 y Ff(unset)e(env)h Fe(var)92 b Fc(remo)o(v)o(e)13
+b Fe(var)g Fc(from)h(en)o(vironmen)o(t)-225 1820 y Fg(Shell)d(Commands)-225
+1866 y Ff(cd)i Fe(dir)217 b Fc(c)o(hange)13 b(w)o(orking)h(directory)g(to)f
+Fe(dir)-225 1903 y Ff(pwd)256 b Fc(Prin)o(t)15 b(w)o(orking)e(directory)-225
+1941 y Ff(make)f Fb(:)7 b(:)g(:)176 b Fc(call)14 b(\\)p Ff(make)p
+Fc(")-225 1978 y Ff(shell)e Fe(cmd)146 b Fc(execute)13 b(arbitrary)h(shell)h
+(command)f(string)-225 2100 y Fd([)f(])h Fh(surround)f(optional)j(argumen)o
+(ts)45 b Fb(:)7 b(:)g(:)13 b Fh(sho)o(w)h(one)f(or)h(more)f(argumen)o(ts)-216
+2174 y(c)-230 2175 y Fa(\015)p Fh(1991,)h(1992)h(F)n(ree)d(Soft)o(w)o(are)h
+(F)n(oundation,)h(Inc.)60 b(P)o(ermissions)15 b(on)f(bac)o(k)p
+800 -217 1 9 v 800 2175 V 875 -183 a Fg(Breakp)q(oin)o(ts)d(and)j(W)l(atc)o
+(hp)q(oin)o(ts)875 -132 y Ff(break)e Fd([)p Fe(\014le)p Ff(:)p
+Fd(])p Fe(line)875 -86 y Ff(b)i Fd([)p Fe(\014le)p Ff(:)p Fd(])p
+Fe(line)1185 -132 y Fc(set)g(breakp)q(oin)o(t)g(at)f Fe(line)h
+Fc(n)o(um)o(b)q(er)h Fd([)p Fc(in)f Fe(\014le)p Fd(])1185 -94
+y Fc(eg:)33 b Ff(break)12 b(main.c:37)875 -32 y(break)g Fd([)p
+Fe(\014le)p Ff(:)p Fd(])p Fe(func)57 b Fc(set)14 b(breakp)q(oin)o(t)g(at)f
+Fe(func)h Fd([)p Fc(in)g Fe(\014le)p Fd(])875 11 y Ff(break)e(+)p
+Fe(o\013set)875 48 y Ff(break)g(-)p Fe(o\013set)1185 11 y Fc(set)i(break)f
+(at)h Fe(o\013set)f Fc(lines)i(from)f(curren)o(t)g(stop)875
+87 y Ff(break)e(*)p Fe(addr)121 b Fc(set)14 b(breakp)q(oin)o(t)g(at)f
+(address)h Fe(addr)875 124 y Ff(break)220 b Fc(set)14 b(breakp)q(oin)o(t)g
+(at)f(next)i(instruction)875 161 y Ff(break)d Fb(:)7 b(:)g(:)12
+b Ff(if)i Fe(expr)31 b Fc(break)14 b(conditionally)h(on)f(nonzero)f
+Fe(expr)875 211 y Ff(cond)g Fe(n)h Fd([)p Fe(expr)p Fd(])103
+b Fc(new)13 b(conditional)i(expression)g(on)e(breakp)q(oin)o(t)1206
+248 y Fe(n)p Fc(;)h(mak)o(e)g(unconditional)h(if)g(no)e Fe(expr)875
+286 y Ff(tbreak)f Fb(:)7 b(:)g(:)140 b Fc(temp)q(orary)13 b(break;)i(disable)
+f(when)g(reac)o(hed)875 324 y Ff(rbreak)e Fe(r)n(e)n(gex)115
+b Fc(break)14 b(on)f(all)i(functions)g(matc)o(hing)f Fe(r)n(e)n(gex)875
+361 y Ff(watch)e Fe(expr)143 b Fc(set)14 b(a)f(w)o(atc)o(hp)q(oin)o(t)h(for)f
+(expression)i Fe(expr)875 398 y Ff(catch)d Fe(x)192 b Fc(break)14
+b(at)f(C++)i(handler)f(for)g(exception)g Fe(x)875 473 y Ff(info)f(break)135
+b Fc(sho)o(w)13 b(de\014ned)i(breakp)q(oin)o(ts)875 511 y Ff(info)e(watch)135
+b Fc(sho)o(w)13 b(de\014ned)i(w)o(atc)o(hp)q(oin)o(ts)875 585
+y Ff(clear)220 b Fc(delete)14 b(breakp)q(oin)o(ts)g(at)g(next)g(instruction)
+875 635 y Ff(clear)e Fd([)p Fe(\014le)p Ff(:)p Fd(])p Fe(fun)73
+b Fc(delete)14 b(breakp)q(oin)o(ts)g(at)g(en)o(try)g(to)f Fe(fun)p
+Fc(\(\))875 688 y Ff(clear)f Fd([)p Fe(\014le)p Ff(:)p Fd(])p
+Fe(line)66 b Fc(delete)14 b(breakp)q(oin)o(ts)g(on)g(source)f(line)875
+742 y Ff(delete)f Fd([)p Fe(n)p Fd(])147 b Fc(delete)14 b(breakp)q(oin)o(ts)g
+Fd([)p Fc(or)f(breakp)q(oin)o(t)i Fe(n)p Fd(])875 823 y Ff(disable)c
+Fd([)p Fe(n)p Fd(])130 b Fc(disable)15 b(breakp)q(oin)o(ts)f
+Fd([)p Fc(or)f(breakp)q(oin)o(t)i Fe(n)p Fd(])875 877 y Ff(enable)d
+Fd([)p Fe(n)p Fd(])147 b Fc(enable)14 b(breakp)q(oin)o(ts)g
+Fd([)p Fc(or)f(breakp)q(oin)o(t)i Fe(n)p Fd(])875 931 y Ff(enable)d(once)g
+Fd([)p Fe(n)p Fd(])63 b Fc(enable)14 b(breakp)q(oin)o(ts)g
+Fd([)p Fc(or)f(breakp)q(oin)o(t)i Fe(n)p Fd(])p Fc(;)1206 969
+y(disable)f(again)g(when)f(reac)o(hed)875 1018 y Ff(enable)f(del)h
+Fd([)p Fe(n)p Fd(])80 b Fc(enable)14 b(breakp)q(oin)o(ts)g
+Fd([)p Fc(or)f(breakp)q(oin)o(t)i Fe(n)p Fd(])p Fc(;)1206 1055
+y(delete)e(when)h(reac)o(hed)875 1105 y Ff(ignore)e Fe(n)i(c)n(ount)76
+b Fc(ignore)13 b(breakp)q(oin)o(t)i Fe(n)p Fc(,)f Fe(c)n(ount)g
+Fc(times)875 1180 y Ff(commands)d Fe(n)946 1217 y Fd([)p Ff(silent)p
+Fd(])946 1255 y Fe(c)n(ommand-list)1185 1180 y Fc(execute)i(GDB)h
+Fe(c)n(ommand-list)e Fc(ev)o(ery)j(time)1206 1217 y(breakp)q(oin)o(t)f
+Fe(n)g Fc(is)g(reac)o(hed.)21 b Fd([)p Ff(silent)1206 1263
+y Fc(suppresses)14 b(default)h(displa)o(y)p Fd(])875 1306 y
+Ff(end)256 b Fc(end)14 b(of)g Fe(c)n(ommand-list)875 1393 y
+Fg(Program)f(Stac)o(k)875 1445 y Ff(backtrace)e Fd([)p Fe(n)p
+Fd(])875 1490 y Ff(bt)i Fd([)p Fe(n)p Fd(])1185 1445 y Fc(prin)o(t)i(trace)e
+(of)g(all)h(frames)g(in)h(stac)o(k;)f(or)f(of)h Fe(n)1206 1482
+y Fc(frames|innermost)g(if)h Fe(n)p Ff(>0)p Fc(,)e(outermost)h(if)1206
+1519 y Fe(n)p Ff(<0)875 1563 y(frame)e Fd([)p Fe(n)p Fd(])165
+b Fc(select)13 b(frame)h(n)o(um)o(b)q(er)h Fe(n)f Fc(or)f(frame)h(at)g
+(address)1206 1600 y Fe(n)p Fc(;)g(if)h(no)e Fe(n)p Fc(,)i(displa)o(y)g
+(curren)o(t)f(frame)875 1639 y Ff(up)f Fe(n)242 b Fc(select)13
+b(frame)h Fe(n)g Fc(frames)g(up)875 1676 y Ff(down)f Fe(n)206
+b Fc(select)13 b(frame)h Fe(n)g Fc(frames)g(do)o(wn)875 1720
+y Ff(info)f(frame)f Fd([)p Fe(addr)p Fd(])30 b Fc(describ)q(e)14
+b(selected)g(frame,)g(or)f(frame)h(at)f Fe(addr)875 1763 y
+Ff(info)g(args)153 b Fc(argumen)o(ts)14 b(of)f(selected)h(frame)875
+1800 y Ff(info)f(locals)117 b Fc(lo)q(cal)13 b(v)n(ariables)i(of)e(selected)h
+(frame)875 1844 y Ff(info)f(reg)f Fd([)p Fe(rn)p Fd(])p Fb(:)7
+b(:)g(:)875 1889 y Ff(info)13 b(all-reg)e Fd([)p Fe(rn)p Fd(])1185
+1844 y Fc(register)i(v)n(alues)i Fd([)p Fc(for)e(regs)g Fe(rn)s
+Fd(])g Fc(in)h(selected)1206 1881 y(frame;)g Ff(all-reg)d Fc(includes)k
+(\015oating)e(p)q(oin)o(t)875 1933 y Ff(info)g(catch)135 b
+Fc(exception)14 b(handlers)h(activ)o(e)e(in)i(selected)f(frame)p
+1900 -217 V 1900 2175 V 1975 -183 a Fg(Execution)f(Con)o(trol)1975
+-138 y Ff(continue)e Fd([)p Fe(c)n(ount)p Fd(])1975 -92 y Ff(c)j
+Fd([)p Fe(c)n(ount)p Fd(])2285 -138 y Fc(con)o(tin)o(ue)g(running;)i(if)e
+Fe(c)n(ount)g Fc(sp)q(eci\014ed,)g(ignore)2306 -100 y(this)g(breakp)q(oin)o
+(t)h(next)f Fe(c)n(ount)g Fc(times)1975 -26 y Ff(step)f Fd([)p
+Fe(c)n(ount)p Fd(])1975 20 y Ff(s)h Fd([)p Fe(c)n(ount)p Fd(])2285
+-26 y Fc(execute)g(un)o(til)h(another)e(line)i(reac)o(hed;)f(rep)q(eat)2306
+12 y Fe(c)n(ount)f Fc(times)i(if)f(sp)q(eci\014ed)1975 74 y
+Ff(stepi)e Fd([)p Fe(c)n(ount)p Fd(])1975 120 y Ff(si)h Fd([)p
+Fe(c)n(ount)p Fd(])2285 74 y Fc(step)h(b)o(y)h(mac)o(hine)f(instructions)h
+(rather)f(than)2306 111 y(source)f(lines)1975 186 y Ff(next)g
+Fd([)p Fe(c)n(ount)p Fd(])1975 232 y Ff(n)h Fd([)p Fe(c)n(ount)p
+Fd(])2285 186 y Fc(execute)g(next)g(line,)h(including)h(an)o(y)e(function)
+2306 223 y(calls)1975 286 y Ff(nexti)e Fd([)p Fe(c)n(ount)p
+Fd(])1975 331 y Ff(ni)h Fd([)p Fe(c)n(ount)p Fd(])2285 286
+y Fc(next)h(mac)o(hine)h(instruction)g(rather)e(than)2306 323
+y(source)g(line)1975 398 y Ff(until)f Fd([)p Fe(lo)n(c)n(ation)p
+Fd(])67 b Fc(run)14 b(un)o(til)i(next)e(instruction)h(\(or)e
+Fe(lo)n(c)n(ation)p Fc(\))1975 441 y Ff(finish)202 b Fc(run)14
+b(un)o(til)i(selected)e(stac)o(k)f(frame)h(returns)1975 484
+y Ff(return)e Fd([)p Fe(expr)p Fd(])101 b Fc(p)q(op)14 b(selected)g(stac)o(k)
+f(frame)h(without)2306 522 y(executing)g Fd([)p Fc(setting)f(return)i(v)n
+(alue)p Fd(])1975 566 y Ff(signal)d Fe(num)125 b Fc(resume)14
+b(execution)g(with)g(signal)h Fe(s)f Fc(\(none)f(if)i Ff(0)p
+Fc(\))1975 604 y Ff(jump)e Fe(line)1975 641 y Ff(jump)g(*)p
+Fe(addr)n(ess)2285 604 y Fc(resume)h(execution)g(at)g(sp)q(eci\014ed)g
+Fe(line)f Fc(n)o(um)o(b)q(er)2306 641 y(or)g Fe(addr)n(ess)1975
+681 y Ff(set)g(var=)p Fe(expr)106 b Fc(ev)n(aluate)14 b Fe(expr)e
+Fc(without)i(displa)o(ying)i(it;)f(use)2306 718 y(for)e(altering)h(program)f
+(v)n(ariables)1975 815 y Fg(Displa)o(y)1975 867 y Ff(print)f
+Fd([)p Ff(/)p Fe(f)6 b Fd(])13 b([)p Fe(expr)p Fd(])1975 913
+y Ff(p)h Fd([)p Ff(/)p Fe(f)5 b Fd(])13 b([)p Fe(expr)p Fd(])2285
+867 y Fc(sho)o(w)h(v)n(alue)g(of)g Fe(expr)e Fd([)p Fc(or)h(last)h(v)n(alue)h
+Ff($)p Fd(])2306 904 y Fc(according)e(to)g(format)h Fe(f)p
+Fc(:)2046 956 y Ff(x)221 b Fc(hexadecimal)2046 993 y Ff(d)g
+Fc(signed)14 b(decimal)2046 1030 y Ff(u)221 b Fc(unsigned)15
+b(decimal)2046 1068 y Ff(o)221 b Fc(o)q(ctal)2046 1105 y Ff(t)g
+Fc(binary)2046 1142 y Ff(a)g Fc(address,)14 b(absolute)g(and)g(relativ)o(e)
+2046 1180 y Ff(c)221 b Fc(c)o(haracter)2046 1217 y Ff(f)g Fc(\015oating)13
+b(p)q(oin)o(t)1975 1266 y Ff(call)g Fd([)p Ff(/)p Fe(f)5 b
+Fd(])13 b Fe(expr)89 b Fc(lik)o(e)15 b Ff(print)d Fc(but)i(do)q(es)g(not)g
+(displa)o(y)h Ff(void)1975 1320 y(x)f Fd([)p Ff(/)p Fe(Nuf)5
+b Fd(])14 b Fe(expr)98 b Fc(examine)14 b(memory)g(at)g(address)g
+Fe(expr)p Fc(;)f(optional)2306 1358 y(format)g(sp)q(ec)h(follo)o(ws)g(slash)
+2011 1396 y Fe(N)249 b Fc(coun)o(t)14 b(of)f(ho)o(w)h(man)o(y)h(units)g(to)e
+(displa)o(y)2011 1433 y Fe(u)256 b Fc(unit)15 b(size;)f(one)g(of)2356
+1471 y Ff(b)f Fc(individual)k(b)o(ytes)2356 1508 y Ff(h)c Fc(halfw)o(ords)h
+(\(t)o(w)o(o)f(b)o(ytes\))2356 1545 y Ff(w)g Fc(w)o(ords)h(\(four)g(b)o
+(ytes\))2356 1583 y Ff(g)f Fc(gian)o(t)h(w)o(ords)f(\(eigh)o(t)h(b)o(ytes\))
+2011 1620 y Fe(f)263 b Fc(prin)o(ting)15 b(format.)21 b(An)o(y)14
+b Ff(print)e Fc(format,)i(or)2356 1657 y Ff(s)f Fc(n)o(ull-terminated)j
+(string)2356 1695 y Ff(i)d Fc(mac)o(hine)h(instructions)1975
+1738 y Ff(disassem)d Fd([)p Fe(addr)p Fd(])62 b Fc(displa)o(y)15
+b(memory)g(as)e(mac)o(hine)h(instructions)1975 1840 y Fg(Automatic)e(Displa)o
+(y)1975 1891 y Ff(display)g Fd([)p Ff(/)p Fe(f)5 b Fd(])13
+b Fe(expr)36 b Fc(sho)o(w)14 b(v)n(alue)g(of)g Fe(expr)e Fc(eac)o(h)i(time)g
+(program)2306 1929 y(stops)g Fd([)p Fc(according)e(to)i(format)f
+Fe(f)6 b Fd(])1975 1972 y Ff(display)184 b Fc(displa)o(y)15
+b(all)g(enabled)f(expressions)h(on)f(list)1975 2014 y Ff(undisplay)d
+Fe(n)118 b Fc(remo)o(v)o(e)14 b(n)o(um)o(b)q(er\(s\))h Fe(n)f
+Fc(from)g(list)h(of)2306 2051 y(automatically)f(displa)o(y)o(ed)h
+(expressions)1975 2091 y Ff(disable)d(disp)g Fe(n)69 b Fc(disable)15
+b(displa)o(y)g(for)f(expression\(s\))h(n)o(um)o(b)q(er)g Fe(n)1975
+2132 y Ff(enable)d(disp)g Fe(n)87 b Fc(enable)14 b(displa)o(y)h(for)f
+(expression\(s\))h(n)o(um)o(b)q(er)g Fe(n)1975 2170 y Ff(info)e(display)99
+b Fc(n)o(um)o(b)q(ered)15 b(list)g(of)e(displa)o(y)j(expressions)p
+eop
+%%Page: 2 2
+bop -225 -183 a Fg(Expressions)-225 -138 y Fe(expr)245 b Fc(an)13
+b(expression)i(in)g(C,)f(C++,)g(or)g(Mo)q(dula-2)105 -100 y(\(including)i
+(function)f(calls\),)f(or:)-225 -60 y Fe(addr)s Ff(@)p Fe(len)176
+b Fc(an)13 b(arra)o(y)h(of)f Fe(len)h Fc(elemen)o(ts)h(b)q(eginning)f(at)105
+-23 y Fe(addr)-225 10 y(\014le)p Ff(::)p Fe(nm)182 b Fc(a)13
+b(v)n(ariable)h(or)g(function)h Fe(nm)e Fc(de\014ned)i(in)f
+Fe(\014le)-225 59 y Fa(f)p Fe(typ)n(e)p Fa(g)p Fe(addr)138
+b Fc(read)13 b(memory)h(at)g Fe(addr)e Fc(as)h(sp)q(eci\014ed)h
+Fe(typ)n(e)-225 105 y Ff($)292 b Fc(most)13 b(recen)o(t)h(displa)o(y)o(ed)i
+(v)n(alue)-225 142 y Ff($)p Fe(n)273 b(n)p Fc(th)14 b(displa)o(y)o(ed)i(v)n
+(alue)-225 179 y Ff($$)274 b Fc(displa)o(y)o(ed)15 b(v)n(alue)g(previous)g
+(to)e($)-225 217 y Ff($$)p Fe(n)255 b(n)p Fc(th)14 b(displa)o(y)o(ed)i(v)n
+(alue)e(bac)o(k)g(from)g($)-225 254 y Ff($)p -205 254 11 2
+v 292 w Fc(last)g(address)g(examined)g(with)h Ff(x)-225 291
+y($)p -205 291 V -193 291 V 292 w Fc(v)n(alue)f(at)g(address)g($)p
+357 291 10 2 v -225 329 a Ff($)p Fe(var)243 b Fc(con)o(v)o(enience)14
+b(v)n(ariable;)h(assign)f(an)o(y)g(v)n(alue)-225 410 y Ff(show)e(values)g
+Fd([)p Fe(n)p Fd(])63 b Fc(sho)o(w)13 b(last)h(10)f(v)n(alues)i
+Fd([)p Fc(or)e(surrounding)i($)p Fe(n)p Fd(])-225 453 y Ff(show)d
+(convenience)28 b Fc(displa)o(y)15 b(all)f(con)o(v)o(enience)h(v)n(ariables)
+-225 550 y Fg(Sym)o(b)q(ol)d(T)l(able)-225 595 y Ff(info)g(address)g
+Fe(s)74 b Fc(sho)o(w)13 b(where)h(sym)o(b)q(ol)h Fe(s)f Fc(is)g(stored)-225
+645 y Ff(info)e(func)h Fd([)p Fe(r)n(e)n(gex)p Fd(])42 b Fc(sho)o(w)13
+b(names,)i(t)o(yp)q(es)f(of)g(de\014ned)g(functions)105 682
+y(\(all,)h(or)e(matc)o(hing)h Fe(r)n(e)n(gex)p Fc(\))-225 733
+y Ff(info)e(var)h Fd([)p Fe(r)n(e)n(gex)p Fd(])60 b Fc(sho)o(w)13
+b(names,)i(t)o(yp)q(es)f(of)g(global)f(v)n(ariables)i(\(all,)105
+770 y(or)f(matc)o(hing)g Fe(r)n(e)n(gex)p Fc(\))-225 821 y
+Ff(whatis)e Fd([)p Fe(expr)p Fd(])-225 867 y Ff(ptype)g Fd([)p
+Fe(expr)p Fd(])85 821 y Fc(sho)o(w)h(data)h(t)o(yp)q(e)g(of)g
+Fe(expr)e Fd([)p Fc(or)h Ff($)p Fd(])g Fc(without)105 858 y(ev)n(aluating;)i
+Ff(ptype)d Fc(giv)o(es)i(more)g(detail)-225 910 y Ff(ptype)e
+Fe(typ)n(e)147 b Fc(describ)q(e)14 b(t)o(yp)q(e,)h(struct,)f(union,)h(or)f
+(en)o(um)-225 1008 y Fg(GDB)f(Scripts)-225 1054 y Ff(source)f
+Fe(script)104 b Fc(read,)14 b(execute)f(GDB)h(commands)g(from)g(\014le)105
+1091 y Fe(script)-225 1147 y Ff(define)e Fe(cmd)-154 1184 y(c)n(ommand-list)
+85 1147 y Fc(create)g(new)i(GDB)g(command)g Fe(cmd)p Fc(;)f(execute)105
+1184 y(script)i(de\014ned)f(b)o(y)h Fe(c)n(ommand-list)-225
+1222 y Ff(end)256 b Fc(end)14 b(of)g Fe(c)n(ommand-list)-225
+1260 y Ff(document)d Fe(cmd)-154 1297 y(help-text)85 1260 y
+Fc(create)h(online)j(do)q(cumen)o(tation)f(for)f(new)h(GDB)105
+1297 y(command)g Fe(cmd)-225 1335 y Ff(end)256 b Fc(end)14
+b(of)g Fe(help-text)-225 1432 y Fg(Signals)-225 1478 y Ff(handle)e
+Fe(signal)h(act)44 b Fc(sp)q(ecify)15 b(GDB)e(actions)h(for)f
+Fe(signal)p Fc(:)-190 1515 y Ff(print)185 b Fc(announce)13
+b(signal)-190 1553 y Ff(noprint)149 b Fc(b)q(e)13 b(silen)o(t)i(for)f(signal)
+-190 1590 y Ff(stop)203 b Fc(halt)14 b(execution)g(on)g(signal)-190
+1627 y Ff(nostop)167 b Fc(do)13 b(not)h(halt)g(execution)-190
+1665 y Ff(pass)203 b Fc(allo)o(w)13 b(y)o(our)h(program)f(to)h(handle)g
+(signal)-190 1702 y Ff(nopass)167 b Fc(do)13 b(not)h(allo)o(w)g(y)o(our)g
+(program)f(to)g(see)h(signal)-225 1739 y Ff(info)e(signals)100
+b Fc(sho)o(w)13 b(table)h(of)g(signals,)h(GDB)e(action)h(for)f(eac)o(h)-225
+1838 y Fg(Debugging)e(T)l(argets)-225 1884 y Ff(target)h Fe(typ)n(e)g(p)n(ar)
+n(am)24 b Fc(connect)13 b(to)g(target)g(mac)o(hine,)i(pro)q(cess,)e(or)h
+(\014le)-225 1921 y Ff(help)e(target)118 b Fc(displa)o(y)15
+b(a)o(v)n(ailable)g(targets)-225 1958 y Ff(attach)d Fe(p)n(ar)n(am)97
+b Fc(connect)13 b(to)g(another)h(pro)q(cess)-225 1996 y Ff(detach)202
+b Fc(release)13 b(target)f(from)i(GDB)g(con)o(trol)p 800 -217
+1 9 v 800 2175 V 875 -183 a Fg(Con)o(trollin)o(g)d(GDB)875
+-138 y Ff(set)i Fe(p)n(ar)n(am)f(value)61 b Fc(set)14 b(one)f(of)h(GDB's)g
+(in)o(ternal)h(parameters)875 -100 y Ff(show)e Fe(p)n(ar)n(am)132
+b Fc(displa)o(y)15 b(curren)o(t)f(setting)g(of)g(parameter)875
+-51 y(P)o(arameters)f(understo)q(o)q(d)h(b)o(y)h Ff(set)e Fc(and)h
+Ff(show)p Fc(:)910 -13 y Ff(complaints)d Fe(limit)i Fc(n)o(um)o(b)q(er)i(of)e
+(messages)h(on)f(un)o(usual)j(sym)o(b)q(ols)910 28 y Ff(confirm)c
+Fe(on/o\013)43 b Fc(enable)14 b(or)f(disable)i(cautionary)f(queries)910
+66 y Ff(editing)e Fe(on/o\013)43 b Fc(con)o(trol)13 b Ff(readline)e
+Fc(command-line)k(editing)910 103 y Ff(height)d Fe(lpp)110
+b Fc(n)o(um)o(b)q(er)15 b(of)e(lines)i(b)q(efore)f(pause)g(in)g(displa)o(y)
+910 145 y Ff(language)d Fe(lang)58 b Fc(Language)12 b(for)h(GDB)h
+(expressions)h(\()p Ff(auto)p Fc(,)e Ff(c)g Fc(or)1206 182
+y Ff(modula-2)p Fc(\))910 222 y Ff(listsize)e Fe(n)101 b Fc(n)o(um)o(b)q(er)
+15 b(of)e(lines)i(sho)o(wn)f(b)o(y)h Ff(list)910 259 y(prompt)d
+Fe(str)114 b Fc(use)14 b Fe(str)f Fc(as)g(GDB)h(prompt)910
+297 y Ff(radix)e Fe(b)n(ase)111 b Fc(o)q(ctal,)13 b(decimal,)i(or)e(hex)i(n)o
+(um)o(b)q(er)1206 334 y(represen)o(tation)910 374 y Ff(verbose)d
+Fe(on/o\013)43 b Fc(con)o(trol)13 b(messages)g(when)h(loading)g(sym)o(b)q
+(ols)910 411 y Ff(width)e Fe(cpl)130 b Fc(n)o(um)o(b)q(er)15
+b(of)e(c)o(haracters)g(b)q(efore)h(line)g(folded)910 449 y
+Ff(write)e Fe(on/o\013)79 b Fc(Allo)o(w)13 b(or)h(forbid)g(patc)o(hing)h
+(binary)m(,)g(core)e(\014les)1206 486 y(\(when)g(reop)q(ened)h(with)g
+Ff(exec)f Fc(or)g Ff(core)p Fc(\))910 526 y Ff(history)f Fb(:)7
+b(:)g(:)910 563 y Ff(h)14 b Fb(:)7 b(:)g(:)1185 526 y Fc(groups)14
+b(with)g(the)g(follo)o(wing)g(options:)910 598 y Ff(h)g(exp)f
+Fe(o\013/on)82 b Fc(disable/enable)14 b Ff(readline)d Fc(history)k(expansion)
+910 635 y Ff(h)f(file)e Fe(\014lename)33 b Fc(\014le)14 b(for)f(recording)h
+(GDB)f(command)h(history)910 672 y Ff(h)g(size)e Fe(size)104
+b Fc(n)o(um)o(b)q(er)15 b(of)e(commands)h(k)o(ept)h(in)f(history)h(list)910
+710 y Ff(h)f(save)e Fe(o\013/on)65 b Fc(con)o(trol)13 b(use)h(of)g(external)g
+(\014le)g(for)f(command)1206 747 y(history)910 803 y Ff(print)f
+Fb(:)7 b(:)g(:)910 840 y Ff(p)14 b Fb(:)7 b(:)g(:)1185 803
+y Fc(groups)14 b(with)g(the)g(follo)o(wing)g(options:)910 882
+y Ff(p)g(address)d Fe(on/o\013)h Fc(prin)o(t)j(memory)f(addresses)g(in)g
+(stac)o(ks,)h(v)n(alues)910 923 y Ff(p)f(array)e Fe(o\013/on)47
+b Fc(compact)13 b(or)g(attractiv)o(e)g(format)h(for)g(arra)o(ys)910
+965 y Ff(p)g(demangl)d Fe(on/o\013)h Fc(source)h(\(demangled\))h(or)g(in)o
+(ternal)g(form)g(for)1206 1002 y(C++)g(sym)o(b)q(ols)910 1042
+y Ff(p)g(asm-dem)d Fe(on/o\013)h Fc(demangle)i(C++)g(sym)o(b)q(ols)h(in)g
+(mac)o(hine-)1206 1079 y(instruction)g(output)910 1118 y Ff(p)f(elements)d
+Fe(limit)17 b Fc(n)o(um)o(b)q(er)e(of)e(arra)o(y)h(elemen)o(ts)g(to)g(displa)
+o(y)910 1160 y Ff(p)g(object)e Fe(on/o\013)29 b Fc(prin)o(t)15
+b(C++)f(deriv)o(ed)h(t)o(yp)q(es)g(for)e(ob)r(jects)910 1201
+y Ff(p)h(pretty)e Fe(o\013/on)29 b Fc(struct)14 b(displa)o(y:)23
+b(compact)13 b(or)g(inden)o(ted)910 1243 y Ff(p)h(union)e Fe(on/o\013)47
+b Fc(displa)o(y)15 b(of)f(union)h(mem)o(b)q(ers)910 1284 y
+Ff(p)f(vtbl)e Fe(o\013/on)65 b Fc(displa)o(y)15 b(of)f(C++)h(virtual)f
+(function)h(tables)875 1359 y Ff(show)e(commands)81 b Fc(sho)o(w)13
+b(last)h(10)f(commands)875 1396 y Ff(show)g(commands)e Fe(n)51
+b Fc(sho)o(w)13 b(10)g(commands)h(around)g(n)o(um)o(b)q(er)h
+Fe(n)875 1434 y Ff(show)e(commands)e(+)52 b Fc(sho)o(w)13 b(next)i(10)e
+(commands)875 1521 y Fg(W)l(orking)g(Files)875 1573 y Ff(file)g
+Fd([)p Fe(\014le)p Fd(])156 b Fc(use)14 b Fe(\014le)f Fc(for)h(b)q(oth)g(sym)
+o(b)q(ols)h(and)f(executable;)1206 1610 y(with)g(no)g(arg,)f(discard)h(b)q
+(oth)875 1659 y Ff(core)f Fd([)p Fe(\014le)p Fd(])156 b Fc(read)13
+b Fe(\014le)h Fc(as)f(coredump;)i(or)e(discard)875 1713 y Ff(exec)g
+Fd([)p Fe(\014le)p Fd(])156 b Fc(use)14 b Fe(\014le)f Fc(as)h(executable)g
+(only;)h(or)e(discard)875 1767 y Ff(symbol)f Fd([)p Fe(\014le)p
+Fd(])121 b Fc(use)14 b(sym)o(b)q(ol)h(table)f(from)g Fe(\014le)p
+Fc(;)f(or)h(discard)875 1810 y Ff(load)f Fe(\014le)180 b Fc(dynamically)15
+b(link)h Fe(\014le)f Fc(and)f(add)g(its)h(sym)o(b)q(ols)875
+1848 y Ff(add-sym)c Fe(\014le)j(addr)45 b Fc(read)13 b(additional)i(sym)o(b)q
+(ols)g(from)f Fe(\014le)p Fc(,)1206 1885 y(dynamically)h(loaded)f(at)f
+Fe(addr)875 1923 y Ff(info)g(files)135 b Fc(displa)o(y)15 b(w)o(orking)f
+(\014les)g(and)g(targets)f(in)i(use)875 1961 y Ff(path)e Fe(dirs)167
+b Fc(add)14 b Fe(dirs)f Fc(to)g(fron)o(t)h(of)g(path)g(searc)o(hed)f(for)1206
+1998 y(executable)g(and)h(sym)o(b)q(ol)h(\014les)875 2037 y
+Ff(show)e(path)153 b Fc(displa)o(y)15 b(executable)f(and)g(sym)o(b)q(ol)h
+(\014le)f(path)875 2074 y Ff(info)f(share)135 b Fc(list)15
+b(names)e(of)h(shared)g(libraries)h(curren)o(tly)1206 2111
+y(loaded)p 1900 -217 V 1900 2175 V 1975 -183 a Fg(Source)e(Files)1975
+-138 y Ff(dir)g Fe(names)148 b Fc(add)14 b(directory)g Fe(names)f
+Fc(to)h(fron)o(t)g(of)f(source)2306 -100 y(path)1975 -62 y
+Ff(dir)256 b Fc(clear)13 b(source)h(path)1975 -25 y Ff(show)f(dir)171
+b Fc(sho)o(w)14 b(curren)o(t)g(source)f(path)1975 50 y Ff(list)238
+b Fc(sho)o(w)14 b(next)g(ten)g(lines)h(of)e(source)1975 87
+y Ff(list)g(-)207 b Fc(sho)o(w)14 b(previous)g(ten)g(lines)1975
+125 y Ff(list)f Fe(lines)156 b Fc(displa)o(y)15 b(source)f(surrounding)h
+Fe(lines)p Fc(,)f(sp)q(eci\014ed)2306 162 y(as:)2011 206 y
+Fd([)p Fe(\014le)p Ff(:)p Fd(])p Fe(num)122 b Fc(line)15 b(n)o(um)o(b)q(er)g
+Fd([)p Fc(in)f(named)g(\014le)p Fd(])2011 260 y([)p Fe(\014le)p
+Ff(:)p Fd(])p Fe(function)63 b Fc(b)q(eginning)15 b(of)e(function)i
+Fd([)p Fc(in)f(named)g(\014le)p Fd(])2011 303 y Ff(+)p Fe(o\013)217
+b(o\013)14 b Fc(lines)h(after)e(last)h(prin)o(ted)2011 340
+y Ff(-)p Fe(o\013)217 b(o\013)14 b Fc(lines)h(previous)f(to)g(last)g(prin)o
+(ted)2011 377 y Ff(*)p Fe(addr)n(ess)145 b Fc(line)15 b(con)o(taining)f
+Fe(addr)n(ess)1975 415 y Ff(list)f Fe(f)p Ff(,)p Fe(l)187 b
+Fc(from)14 b(line)h Fe(f)e Fc(to)g(line)i Fe(l)1975 452 y Ff(info)e(line)f
+Fe(num)76 b Fc(sho)o(w)14 b(starting,)g(ending)g(addresses)g(of)2306
+489 y(compiled)g(co)q(de)f(for)h(source)f(line)i Fe(num)1975
+528 y Ff(info)e(source)117 b Fc(sho)o(w)14 b(name)f(of)h(curren)o(t)g(source)
+f(\014le)1975 565 y Ff(info)g(sources)99 b Fc(list)15 b(all)f(source)f
+(\014les)h(in)h(use)1975 603 y Ff(forw)e Fe(r)n(e)n(gex)150
+b Fc(searc)o(h)13 b(follo)o(wing)h(source)g(lines)h(for)e Fe(r)n(e)n(gex)1975
+640 y Ff(rev)g Fe(r)n(e)n(gex)168 b Fc(searc)o(h)13 b(preceding)h(source)g
+(lines)h(for)e Fe(r)n(e)n(gex)1975 737 y Fg(GDB)g(under)f(GNU)i(Emacs)1975
+782 y Ff(M-x)f(gdb)189 b Fc(run)14 b(GDB)g(under)h(Emacs)1975
+820 y Ff(C-h)e(m)225 b Fc(describ)q(e)14 b(GDB)g(mo)q(de)1975
+857 y Ff(M-s)256 b Fc(step)14 b(one)f(line)i(\()p Ff(step)p
+Fc(\))1975 899 y Ff(M-n)256 b Fc(next)14 b(line)h(\()p Ff(next)p
+Fc(\))1975 936 y Ff(M-i)256 b Fc(step)14 b(one)f(instruction)i(\()p
+Ff(stepi)p Fc(\))1975 978 y Ff(C-c)e(C-f)189 b Fc(\014nish)15
+b(curren)o(t)f(stac)o(k)g(frame)f(\()p Ff(finish)p Fc(\))1975
+1015 y Ff(M-c)256 b Fc(con)o(tin)o(ue)14 b(\()p Ff(cont)p Fc(\))1975
+1052 y Ff(M-u)256 b Fc(up)14 b Fe(ar)n(g)f Fc(frames)h(\()p
+Ff(up)p Fc(\))1975 1094 y Ff(M-d)256 b Fc(do)o(wn)14 b Fe(ar)n(g)f
+Fc(frames)h(\()p Ff(down)p Fc(\))1975 1131 y Ff(C-x)f(&)225
+b Fc(cop)o(y)14 b(n)o(um)o(b)q(er)h(from)f(p)q(oin)o(t,)h(insert)f(at)f(end)
+1975 1169 y Ff(C-x)g(SPC)189 b Fc(\(in)14 b(source)g(\014le\))g(set)f(break)h
+(at)g(p)q(oin)o(t)1975 1267 y Fg(GDB)f(License)1975 1313 y
+Ff(show)g(copying)99 b Fc(Displa)o(y)15 b(GNU)e(General)g(Public)i(License)
+1975 1350 y Ff(show)e(warranty)81 b Fc(There)13 b(is)i(NO)e(W)l(ARRANTY)g
+(for)h(GDB.)2306 1387 y(Displa)o(y)h(full)g(no-w)o(arran)o(t)o(y)f(statemen)o
+(t.)2024 1622 y Fh(Cop)o(yrigh)o(t)2185 1621 y(c)2171 1622
+y Fa(\015)o Fh(1991,)h(1992,)f(1993)h(F)n(ree)d(Soft)o(w)o(are)i(F)n
+(oundation,)f(Inc.)2215 1660 y(Roland)i(P)o(esc)o(h)f(\(p)q(esc)o(h@cygn)o
+(us.com\))1997 1697 y(The)f(author)h(assumes)f(no)h(resp)q(onsibilit)o(y)j
+(for)c(an)o(y)h(errors)g(on)g(this)h(card.)1975 1759 y(This)g(card)e(ma)o(y)g
+(b)q(e)g(freely)h(distributed)g(under)f(the)g(terms)g(of)g(the)h(GNU)1975
+1797 y(General)h(Public)g(License.)2012 1834 y(Please)f(con)o(tribute)g(to)g
+(dev)o(elopmen)o(t)e(of)i(this)h(card)e(b)o(y)h(annotating)h(it.)1975
+1896 y(GDB)g(itself)f(is)h(free)e(soft)o(w)o(are;)g(y)o(ou)h(are)g(w)o
+(elcome)e(to)i(distribute)h(copies)f(of)1975 1934 y(it)h(under)e(the)g(terms)
+g(of)g(the)g(GNU)i(General)g(Public)g(License.)20 b(There)12
+b(is)1975 1971 y(absolutely)k(no)d(w)o(arran)o(t)o(y)i(for)e(GDB.)p
+eop
+%%Trailer
+end
+userdict /end-hook known{end-hook}if
+%%EOF
diff --git a/gnu/usr.bin/gdb/doc/refcard.tex b/gnu/usr.bin/gdb/doc/refcard.tex
new file mode 100644
index 0000000..5899608
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/refcard.tex
@@ -0,0 +1,646 @@
+%%%%%%%%%%%%%%%% gdb-refcard.tex %%%%%%%%%%%%%%%%
+
+%This file is TeX source for a reference card describing GDB, the GNU debugger.
+%$Id: refcard.tex,v 1.1.1.1 1993/10/30 21:59:42 jkh Exp $
+%Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+%Permission is granted to make and distribute verbatim copies of
+%this reference provided the copyright notices and permission notices
+%are preserved on all copies.
+%
+%TeX markup is a programming language; accordingly this file is source
+%for a program to generate a reference.
+%
+%This program is free software; you can redistribute it and/or modify
+%it under the terms of the GNU General Public License as published by
+%the Free Software Foundation; either version 1, or (at your option)
+%any later version.
+%
+%This program is distributed in the hope that it will be useful, but
+%WITHOUT ANY WARRANTY; without even the implied warranty of
+%MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%General Public License for more details.
+%
+%You can find a copy of the GNU General Public License in the GDB
+%manual; or write to the Free Software Foundation, Inc.,
+%675 Mass Ave, Cambridge, MA 02139, USA.
+%
+%You can contact the author as: pesch@cygnus.com
+%
+% Roland Pesch
+% Cygnus Support
+% 1937 Landings Drive
+% Mountain View, CA 94043 USA
+%
+% +1 415 903 1400
+%
+%
+%
+% 22-AUG-1993 Andreas Vogel
+%
+% Modifications made in order to handle different papersizes correctly.
+% You only have to set the total width and height of the paper, the
+% horizontal and vertical margin space measured from *paper edge*
+% and the interline and interspec spacing.
+% In order to support a new papersize, you have to fiddle with the
+% latter four dimensions. Just try out a few values.
+% All other values will be computed at process time so it should be
+% quite easy to support different paper sizes - only four values to
+% guess :-)
+%
+% To find the configuration places, just search for the string
+% "CONFIGURATION".
+%
+% Andreas Vogel (av@ssw.de)
+%
+%
+%
+% Uncomment the following `magnification' command if you want to print
+% out in a larger font. Caution! You may need larger paper. You had
+% best avoid using 3-column output if you try this. See the ``Three
+% column format'' section below if you want to print in three column
+% format.
+%
+%\magnification=\magstep 1
+%
+% NOTE ON INTENTIONAL OMISSIONS: This reference card includes most GDB
+% commands, but due to space constraints there are some things I chose
+% to omit. In general, not all synonyms for commands are covered, nor
+% all variations of a command.
+% The GDB-under-Emacs section omits gdb-mode functions without default
+% keybindings. GDB startup options are not described.
+% set print sevenbit-strings, set symbol-reloading omitted.
+% printsyms, printpsyms, omitted since they're for GDB maintenance primarily
+% share omitted due to obsolescence
+% set check range/type omitted at least til code is in GDB.
+%
+%-------------------- Three column format -----------------------
+
+%%%% --- To disable three column format, comment out this entire section
+
+% Three-column format for landscape printing
+
+%-------- Papersize defs:
+
+\newdimen\totalwidth \newdimen\totalheight
+\newdimen\hmargin \newdimen\vmargin
+\newdimen\secskip \newdimen\lskip
+\newdimen\barwidth \newdimen\barheight
+\newdimen\intersecwidth
+
+%%
+%% START CONFIGURATION - PAPERSIZE DEFINITIONS
+%------- Papersize params:
+%% US letter paper (8.5x11in)
+%%
+\totalwidth=11in % total width of paper
+\totalheight=8.5in % total height of paper
+\hmargin=.25in % horizontal margin width
+\vmargin=.25in % vertical margin width
+\secskip=1pc % space between refcard secs
+\lskip=2pt % extra skip between \sec entries
+%------- end papersize params
+%%
+%% change according to personal taste, not papersize dependent
+%%
+\barwidth=.1pt % width of the cropmark bar
+\barheight=2pt % height of the cropmark bar
+\intersecwidth=0.5em % width between \itmwid and \dfnwid
+%%
+%% END CONFIGURATION - PAPERSIZE DEFINITIONS
+%%
+
+%%
+%% values to be computed - nothing to configure
+%%
+\newdimen\fullhsize % width of area without margins
+\newdimen\itmwid % width of item column
+\newdimen\dfnwid % width of definition column
+\newdimen\temp % only for temporary use
+
+%%
+%% adjust the offsets so the margins are measured *from paper edge*
+%%
+\hoffset=-1in \advance \hoffset by \hmargin
+\voffset=-1in \advance \voffset by \vmargin
+
+%%
+%% fullhsize = totalwidth - (2 * hmargin)
+%%
+\fullhsize=\totalwidth
+\temp=\hmargin \multiply \temp by 2 \advance \fullhsize by -\temp
+
+%%
+%% hsize = (fullhsize - (4 * hmargin) - (2 * barwidth)) / 3
+%%
+\hsize=\fullhsize
+\temp=\hmargin \multiply \temp by 4 \advance \hsize by -\temp
+\temp=\barwidth \multiply \temp by 2 \advance \hsize by -\temp
+\divide \hsize by 3
+
+%%
+%% vsize = totalheight - (2 * vmargin)
+%%
+\vsize=\totalheight
+\temp=\vmargin \multiply \temp by 2 \advance \vsize by -\temp
+
+%%
+%% itmwid = (hsize - intersecwidth) * 1/3
+%% dfnwid = (hsize - intersecwidth) * 2/3
+%%
+\temp=\hsize \advance \temp by -\intersecwidth \divide \temp by 3
+\itmwid=\temp
+\dfnwid=\hsize \advance \dfnwid by -\itmwid
+
+%-------- end papersize defs
+
+
+\def\fulline{\hbox to \fullhsize}
+\let\lcr=L \newbox\leftcolumn\newbox\centercolumn
+\output={\if L\lcr
+ \global\setbox\leftcolumn=\columnbox \global\let\lcr=C
+ \else
+ \if C\lcr
+ \global\setbox\centercolumn=\columnbox \global\let\lcr=R
+ \else \tripleformat \global\let\lcr=L
+ \fi
+ \fi
+% \ifnum\outputpenalty>-20000 \else\dosupereject\fi
+ }
+
+%%
+%% START CONFIGURATION - ALTERNATIVE FOLDING GUIDES
+%%
+%% For NO printed folding guide,
+%% comment out other \def\vdecor's and uncomment:
+
+%\def\vdecor{\hskip\hmargin plus1fil\hskip\barwidth plus1fil\hskip\hmargin plus1fil}
+
+%% For SOLID LINE folding guide,
+%% comment out other \def\vdecor's and uncomment:
+
+%\def\vdecor{\hskip\hmargin plus1fil \vrule width \barwidth \hskip\hmargin plus1fil}
+
+%% For SMALL MARKS NEAR TOP AND BOTTOM as folding guide,
+%% comment out other \def\vdecor's and uncomment:
+
+\def\vdecor{\hskip\hmargin plus1fil
+\vbox to \vsize{\hbox to \barwidth{\vrule height\barheight width\barwidth}\vfill
+\hbox to \barwidth{\vrule height\barheight width\barwidth}}%THIS PERCENT SIGN IS ESSENTIAL
+\hskip\hmargin plus1fil}
+
+%%
+%% END CONFIGURATION - ALTERNATIVES FOR FOLDING GUIDES
+%%
+
+\def\tripleformat{\shipout\vbox{\fulline{\box\leftcolumn\vdecor
+ \box\centercolumn\vdecor
+ \columnbox}
+ }
+ \advancepageno}
+\def\columnbox{\leftline{\pagebody}}
+\def\bye{\par\vfill
+ \supereject
+ \if R\lcr \null\vfill\eject\fi
+ \end}
+
+%-------------------- end three column format -----------------------
+
+%-------------------- Computer Modern font defs: --------------------
+\font\bbf=cmbx10
+\font\vbbf=cmbx12
+\font\smrm=cmr6
+\font\brm=cmr10
+\font\rm=cmr7
+\font\it=cmti7
+\font\tt=cmtt8
+%-------------------- end font defs ---------------------------------
+
+%
+\hyphenpenalty=5000\tolerance=2000\raggedright\raggedbottom
+\normalbaselineskip=9pt\baselineskip=9pt
+%
+\parindent=0pt
+\parskip=0pt
+\footline={\vbox to0pt{\hss}}
+%
+\def\ctl#1{{\tt C-#1}}
+\def\opt#1{{\brm[{\rm #1}]}}
+\def\xtra#1{\noalign{\smallskip{\tt#1}}}
+%
+\long\def\sec#1;#2\endsec{\vskip \secskip
+\halign{%
+%COL 1 (of halign):
+\vtop{\hsize=\itmwid\tt
+##\par\vskip \lskip }\hfil
+%COL 2 (of halign):
+&\vtop{\hsize=\dfnwid\hangafter=1\hangindent=\intersecwidth
+\rm ##\par\vskip \lskip}\cr
+%Tail of \long\def fills in halign body with \sec args:
+\noalign{{\bbf #1}\vskip \lskip}
+#2
+}
+}
+
+{\vbbf GDB QUICK REFERENCE}\hfil{\smrm GDB Version 4}\qquad
+
+\sec Essential Commands;
+gdb {\it program} \opt{{\it core}}&debug {\it program} \opt{using
+coredump {\it core}}\cr
+b \opt{\it file\tt:}{\it function}&set breakpoint at {\it function} \opt{in \it file}\cr
+run \opt{{\it arglist}}&start your program \opt{with {\it arglist}}\cr
+bt& backtrace: display program stack\cr
+p {\it expr}&display the value of an expression\cr
+c &continue running your program\cr
+n &next line, stepping over function calls\cr
+s &next line, stepping into function calls\cr
+\endsec
+
+\sec Starting GDB;
+gdb&start GDB, with no debugging files\cr
+gdb {\it program}&begin debugging {\it program}\cr
+gdb {\it program core}&debug coredump {\it core} produced by {\it
+program}\cr
+gdb --help&describe command line options\cr
+\endsec
+
+\sec Stopping GDB;
+quit&exit GDB; also {\tt q} or {\tt EOF} (eg \ctl{d})\cr
+INTERRUPT&(eg \ctl{c}) terminate current command, or send to running process\cr
+\endsec
+
+\sec Getting Help;
+help&list classes of commands\cr
+help {\it class}&one-line descriptions for commands in {\it class}\cr
+help {\it command}&describe {\it command}\cr
+\endsec
+
+\sec Executing your Program;
+run {\it arglist}&start your program with {\it arglist}\cr
+run&start your program with current argument list\cr
+run $\ldots$ <{\it inf} >{\it outf}&start your program with input, output
+redirected\cr
+\cr
+kill&kill running program\cr
+\cr
+tty {\it dev}&use {\it dev} as stdin and stdout for next {\tt run}\cr
+set args {\it arglist}&specify {\it arglist} for next
+{\tt run}\cr
+set args&specify empty argument list\cr
+show args&display argument list\cr
+\cr
+show environment&show all environment variables\cr
+show env {\it var}&show value of environment variable {\it var}\cr
+set env {\it var} {\it string}&set environment variable {\it var}\cr
+unset env {\it var}&remove {\it var} from environment\cr
+\endsec
+
+\sec Shell Commands;
+cd {\it dir}&change working directory to {\it dir}\cr
+pwd&Print working directory\cr
+make $\ldots$&call ``{\tt make}''\cr
+shell {\it cmd}&execute arbitrary shell command string\cr
+\endsec
+
+\vfill
+\line{\smrm \opt{ } surround optional arguments \hfill $\ldots$ show
+one or more arguments}
+\vskip\baselineskip
+\centerline{\smrm \copyright 1991, 1992 Free Software Foundation, Inc.\qquad Permissions on back}
+\eject
+\sec Breakpoints and Watchpoints;
+break \opt{\it file\tt:}{\it line}\par
+b \opt{\it file\tt:}{\it line}&set breakpoint at {\it line} number \opt{in \it file}\par
+eg:\quad{\tt break main.c:37}\quad\cr
+break \opt{\it file\tt:}{\it func}&set breakpoint at {\it
+func} \opt{in \it file}\cr
+break +{\it offset}\par
+break -{\it offset}&set break at {\it offset} lines from current stop\cr
+break *{\it addr}&set breakpoint at address {\it addr}\cr
+break&set breakpoint at next instruction\cr
+break $\ldots$ if {\it expr}&break conditionally on nonzero {\it expr}\cr
+cond {\it n} \opt{\it expr}&new conditional expression on breakpoint
+{\it n}; make unconditional if no {\it expr}\cr
+tbreak $\ldots$&temporary break; disable when reached\cr
+rbreak {\it regex}&break on all functions matching {\it regex}\cr
+watch {\it expr}&set a watchpoint for expression {\it expr}\cr
+catch {\it x}&break at C++ handler for exception {\it x}\cr
+\cr
+info break&show defined breakpoints\cr
+info watch&show defined watchpoints\cr
+\cr
+clear&delete breakpoints at next instruction\cr
+clear \opt{\it file\tt:}{\it fun}&delete breakpoints at entry to {\it fun}()\cr
+clear \opt{\it file\tt:}{\it line}&delete breakpoints on source line \cr
+delete \opt{{\it n}}&delete breakpoints
+\opt{or breakpoint {\it n}}\cr
+\cr
+disable \opt{{\it n}}&disable breakpoints
+\opt{or breakpoint {\it n}}
+\cr
+enable \opt{{\it n}}&enable breakpoints
+\opt{or breakpoint {\it n}}
+\cr
+enable once \opt{{\it n}}&enable breakpoints \opt{or breakpoint {\it n}};
+disable again when reached
+\cr
+enable del \opt{{\it n}}&enable breakpoints \opt{or breakpoint {\it n}};
+delete when reached
+\cr
+\cr
+ignore {\it n} {\it count}&ignore breakpoint {\it n}, {\it count}
+times\cr
+\cr
+commands {\it n}\par
+\qquad \opt{\tt silent}\par
+\qquad {\it command-list}&execute GDB {\it command-list} every time breakpoint {\it n} is reached. \opt{{\tt silent} suppresses default
+display}\cr
+end&end of {\it command-list}\cr
+\endsec
+
+\sec Program Stack;
+backtrace \opt{\it n}\par
+bt \opt{\it n}&print trace of all frames in stack; or of {\it n}
+frames---innermost if {\it n}{\tt >0}, outermost if {\it n}{\tt <0}\cr
+frame \opt{\it n}&select frame number {\it n} or frame at address {\it
+n}; if no {\it n}, display current frame\cr
+up {\it n}&select frame {\it n} frames up\cr
+down {\it n}&select frame {\it n} frames down\cr
+info frame \opt{\it addr}&describe selected frame, or frame at
+{\it addr}\cr
+info args&arguments of selected frame\cr
+info locals&local variables of selected frame\cr
+info reg \opt{\it rn}$\ldots$\par
+info all-reg \opt{\it rn}&register values \opt{for regs {\it rn\/}} in
+selected frame; {\tt all-reg} includes floating point\cr
+info catch&exception handlers active in selected frame\cr
+\endsec
+
+\vfill\eject
+\sec Execution Control;
+continue \opt{\it count}\par
+c \opt{\it count}&continue running; if {\it count} specified, ignore
+this breakpoint next {\it count} times\cr
+\cr
+step \opt{\it count}\par
+s \opt{\it count}&execute until another line reached; repeat {\it count} times if
+specified\cr
+stepi \opt{\it count}\par
+si \opt{\it count}&step by machine instructions rather than source
+lines\cr
+\cr
+next \opt{\it count}\par
+n \opt{\it count}&execute next line, including any function calls\cr
+nexti \opt{\it count}\par
+ni \opt{\it count}&next machine instruction rather than source
+line\cr
+\cr
+until \opt{\it location}&run until next instruction (or {\it
+location})\cr
+finish&run until selected stack frame returns\cr
+return \opt{\it expr}&pop selected stack frame without executing
+\opt{setting return value}\cr
+signal {\it num}&resume execution with signal {\it s} (none if {\tt 0})\cr
+jump {\it line}\par
+jump *{\it address}&resume execution at specified {\it line} number or
+{\it address}\cr
+set var={\it expr}&evaluate {\it expr} without displaying it; use for
+altering program variables\cr
+\endsec
+
+\sec Display;
+print \opt{\tt/{\it f}\/} \opt{\it expr}\par
+p \opt{\tt/{\it f}\/} \opt{\it expr}&show value of {\it expr} \opt{or
+last value \tt \$} according to format {\it f}:\cr
+\qquad x&hexadecimal\cr
+\qquad d&signed decimal\cr
+\qquad u&unsigned decimal\cr
+\qquad o&octal\cr
+\qquad t&binary\cr
+\qquad a&address, absolute and relative\cr
+\qquad c&character\cr
+\qquad f&floating point\cr
+call \opt{\tt /{\it f}\/} {\it expr}&like {\tt print} but does not display
+{\tt void}\cr
+x \opt{\tt/{\it Nuf}\/} {\it expr}&examine memory at address {\it expr};
+optional format spec follows slash\cr
+\quad {\it N}&count of how many units to display\cr
+\quad {\it u}&unit size; one of\cr
+&{\tt\qquad b}\ individual bytes\cr
+&{\tt\qquad h}\ halfwords (two bytes)\cr
+&{\tt\qquad w}\ words (four bytes)\cr
+&{\tt\qquad g}\ giant words (eight bytes)\cr
+\quad {\it f}&printing format. Any {\tt print} format, or\cr
+&{\tt\qquad s}\ null-terminated string\cr
+&{\tt\qquad i}\ machine instructions\cr
+disassem \opt{\it addr}&display memory as machine instructions\cr
+\endsec
+
+\sec Automatic Display;
+display \opt{\tt/\it f\/} {\it expr}&show value of {\it expr} each time
+program stops \opt{according to format {\it f}\/}\cr
+display&display all enabled expressions on list\cr
+undisplay {\it n}&remove number(s) {\it n} from list of
+automatically displayed expressions\cr
+disable disp {\it n}&disable display for expression(s) number {\it
+n}\cr
+enable disp {\it n}&enable display for expression(s) number {\it
+n}\cr
+info display&numbered list of display expressions\cr
+\endsec
+
+\vfill\eject
+
+\sec Expressions;
+{\it expr}&an expression in C, C++, or Modula-2 (including function calls), or:\cr
+{\it addr\/}@{\it len}&an array of {\it len} elements beginning at {\it
+addr}\cr
+{\it file}::{\it nm}&a variable or function {\it nm} defined in {\it
+file}\cr
+$\tt\{${\it type}$\tt\}${\it addr}&read memory at {\it addr} as specified
+{\it type}\cr
+\$&most recent displayed value\cr
+\${\it n}&{\it n}th displayed value\cr
+\$\$&displayed value previous to \$\cr
+\$\${\it n}&{\it n}th displayed value back from \$\cr
+\$\_&last address examined with {\tt x}\cr
+\$\_\_&value at address \$\_\cr
+\${\it var}&convenience variable; assign any value\cr
+\cr
+show values \opt{{\it n}}&show last 10 values \opt{or surrounding
+\${\it n}}\cr
+show convenience&display all convenience variables\cr
+\endsec
+
+\sec Symbol Table;
+info address {\it s}&show where symbol {\it s} is stored\cr
+info func \opt{\it regex}&show names, types of defined functions
+(all, or matching {\it regex})\cr
+info var \opt{\it regex}&show names, types of global variables (all,
+or matching {\it regex})\cr
+whatis \opt{\it expr}\par
+ptype \opt{\it expr}&show data type of {\it expr} \opt{or \tt \$}
+without evaluating; {\tt ptype} gives more detail\cr
+ptype {\it type}&describe type, struct, union, or enum\cr
+\endsec
+
+\sec GDB Scripts;
+source {\it script}&read, execute GDB commands from file {\it
+script}\cr
+\cr
+define {\it cmd}\par
+\qquad {\it command-list}&create new GDB command {\it cmd};
+execute script defined by {\it command-list}\cr
+end&end of {\it command-list}\cr
+document {\it cmd}\par
+\qquad {\it help-text}&create online documentation
+for new GDB command {\it cmd}\cr
+end&end of {\it help-text}\cr
+\endsec
+
+\sec Signals;
+handle {\it signal} {\it act}&specify GDB actions for {\it signal}:\cr
+\quad print&announce signal\cr
+\quad noprint&be silent for signal\cr
+\quad stop&halt execution on signal\cr
+\quad nostop&do not halt execution\cr
+\quad pass&allow your program to handle signal\cr
+\quad nopass&do not allow your program to see signal\cr
+info signals&show table of signals, GDB action for each\cr
+\endsec
+
+\sec Debugging Targets;
+target {\it type} {\it param}&connect to target machine, process, or file\cr
+help target&display available targets\cr
+attach {\it param}&connect to another process\cr
+detach&release target from GDB control\cr
+\endsec
+
+\vfill\eject
+\sec Controlling GDB;
+set {\it param} {\it value}&set one of GDB's internal parameters\cr
+show {\it param}&display current setting of parameter\cr
+\xtra{\rm Parameters understood by {\tt set} and {\tt show}:}
+\quad complaints {\it limit}&number of messages on unusual symbols\cr
+\quad confirm {\it on/off}&enable or disable cautionary queries\cr
+\quad editing {\it on/off}&control {\tt readline} command-line editing\cr
+\quad height {\it lpp}&number of lines before pause in display\cr
+\quad language {\it lang}&Language for GDB expressions ({\tt auto}, {\tt c} or
+{\tt modula-2})\cr
+\quad listsize {\it n}&number of lines shown by {\tt list}\cr
+\quad prompt {\it str}&use {\it str} as GDB prompt\cr
+\quad radix {\it base}&octal, decimal, or hex number representation\cr
+\quad verbose {\it on/off}&control messages when loading
+symbols\cr
+\quad width {\it cpl}&number of characters before line folded\cr
+\quad write {\it on/off}&Allow or forbid patching binary, core files
+(when reopened with {\tt exec} or {\tt core})
+\cr
+\quad history $\ldots$\par
+\quad h $\ldots$&groups with the following options:\cr
+\quad h exp {\it off/on}&disable/enable {\tt readline} history expansion\cr
+\quad h file {\it filename}&file for recording GDB command history\cr
+\quad h size {\it size}&number of commands kept in history list\cr
+\quad h save {\it off/on}&control use of external file for
+command history\cr
+\cr
+\quad print $\ldots$\par
+\quad p $\ldots$&groups with the following options:\cr
+\quad p address {\it on/off}&print memory addresses in stacks,
+values\cr
+\quad p array {\it off/on}&compact or attractive format for
+arrays\cr
+\quad p demangl {\it on/off}&source (demangled) or internal form for C++
+symbols\cr
+\quad p asm-dem {\it on/off}&demangle C++ symbols in
+machine-instruction output\cr
+\quad p elements {\it limit}&number of array elements to display\cr
+\quad p object {\it on/off}&print C++ derived types for objects\cr
+\quad p pretty {\it off/on}&struct display: compact or indented\cr
+\quad p union {\it on/off}&display of union members\cr
+\quad p vtbl {\it off/on}&display of C++ virtual function
+tables\cr
+\cr
+show commands&show last 10 commands\cr
+show commands {\it n}&show 10 commands around number {\it n}\cr
+show commands +&show next 10 commands\cr
+\endsec
+
+\sec Working Files;
+file \opt{\it file}&use {\it file} for both symbols and executable;
+with no arg, discard both\cr
+core \opt{\it file}&read {\it file} as coredump; or discard\cr
+exec \opt{\it file}&use {\it file} as executable only; or discard\cr
+symbol \opt{\it file}&use symbol table from {\it file}; or discard\cr
+load {\it file}&dynamically link {\it file\/} and add its symbols\cr
+add-sym {\it file} {\it addr}&read additional symbols from {\it file},
+dynamically loaded at {\it addr}\cr
+info files&display working files and targets in use\cr
+path {\it dirs}&add {\it dirs} to front of path searched for
+executable and symbol files\cr
+show path&display executable and symbol file path\cr
+info share&list names of shared libraries currently loaded\cr
+\endsec
+
+\vfill\eject
+\sec Source Files;
+dir {\it names}&add directory {\it names} to front of source path\cr
+dir&clear source path\cr
+show dir&show current source path\cr
+\cr
+list&show next ten lines of source\cr
+list -&show previous ten lines\cr
+list {\it lines}&display source surrounding {\it lines},
+specified as:\cr
+\quad{\opt{\it file\tt:}\it num}&line number \opt{in named file}\cr
+\quad{\opt{\it file\tt:}\it function}&beginning of function \opt{in
+named file}\cr
+\quad{\tt +\it off}&{\it off} lines after last printed\cr
+\quad{\tt -\it off}&{\it off} lines previous to last printed\cr
+\quad{\tt*\it address}&line containing {\it address}\cr
+list {\it f},{\it l}&from line {\it f} to line {\it l}\cr
+info line {\it num}&show starting, ending addresses of compiled code for
+source line {\it num}\cr
+info source&show name of current source file\cr
+info sources&list all source files in use\cr
+forw {\it regex}&search following source lines for {\it regex}\cr
+rev {\it regex}&search preceding source lines for {\it regex}\cr
+\endsec
+
+\sec GDB under GNU Emacs;
+M-x gdb&run GDB under Emacs\cr
+\ctl{h} m&describe GDB mode\cr
+M-s&step one line ({\tt step})\cr
+M-n&next line ({\tt next})\cr
+M-i&step one instruction ({\tt stepi})\cr
+\ctl{c} \ctl{f}&finish current stack frame ({\tt finish})\cr
+M-c&continue ({\tt cont})\cr
+M-u&up {\it arg} frames ({\tt up})\cr
+M-d&down {\it arg} frames ({\tt down})\cr
+\ctl{x} \&&copy number from point, insert at end\cr
+\ctl{x} SPC&(in source file) set break at point\cr
+\endsec
+
+\sec GDB License;
+show copying&Display GNU General Public License\cr
+show warranty&There is NO WARRANTY for GDB. Display full no-warranty
+statement.\cr
+\endsec
+
+
+\vfill
+{\smrm\parskip=6pt
+\centerline{Copyright \copyright 1991, 1992, 1993 Free Software Foundation, Inc.}
+\centerline{Roland Pesch (pesch@cygnus.com)}
+\centerline{The author assumes no responsibility for any errors on this card.}
+
+This card may be freely distributed under the terms of the GNU
+General Public License.
+
+\centerline{Please contribute to development of this card by
+annotating it.}
+
+GDB itself is free software; you are welcome to distribute copies of
+it under the terms of the GNU General Public License. There is
+absolutely no warranty for GDB.
+}
+\end
diff --git a/gnu/usr.bin/gdb/doc/remote.texi b/gnu/usr.bin/gdb/doc/remote.texi
new file mode 100644
index 0000000..5b7ec90
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/remote.texi
@@ -0,0 +1,1294 @@
+@c -*- Texinfo -*-
+@c Copyright (c) 1990 1991 1992 1993 Free Software Foundation, Inc.
+@c This file is part of the source for the GDB manual.
+@c This text diverted to "Remote Debugging" section in general case;
+@c however, if we're doing a manual specifically for one of these, it
+@c belongs up front (in "Getting In and Out" chapter).
+
+@ifset REMOTESTUB
+@node Remote Serial
+@subsection The @value{GDBN} remote serial protocol
+
+@cindex remote serial debugging, overview
+To debug a program running on another machine (the debugging
+@dfn{target} machine), you must first arrange for all the usual
+prerequisites for the program to run by itself. For example, for a C
+program, you need
+
+@enumerate
+@item
+A startup routine to set up the C runtime environment; these usually
+have a name like @file{crt0}. The startup routine may be supplied by
+your hardware supplier, or you may have to write your own.
+
+@item
+You probably need a C subroutine library to support your program's
+subroutine calls, notably managing input and output.
+
+@item
+A way of getting your program to the other machine---for example, a
+download program. These are often supplied by the hardware
+manufacturer, but you may have to write your own from hardware
+documentation.
+@end enumerate
+
+The next step is to arrange for your program to use a serial port to
+communicate with the machine where @value{GDBN} is running (the @dfn{host}
+machine). In general terms, the scheme looks like this:
+
+@table @emph
+@item On the host,
+@value{GDBN} already understands how to use this protocol; when everything
+else is set up, you can simply use the @samp{target remote} command
+(@pxref{Targets,,Specifying a Debugging Target}).
+
+@item On the target,
+you must link with your program a few special-purpose subroutines that
+implement the @value{GDBN} remote serial protocol. The file containing these
+subroutines is called a @dfn{debugging stub}.
+
+@ifset GDBSERVER
+On certain remote targets, you can use an auxiliary program
+@code{gdbserver} instead of linking a stub into your program.
+@xref{Server,,Using the @code{gdbserver} program}, for details.
+@end ifset
+@end table
+
+The debugging stub is specific to the architecture of the remote
+machine; for example, use @file{sparc-stub.c} to debug programs on
+@sc{sparc} boards.
+
+@cindex remote serial stub list
+These working remote stubs are distributed with @value{GDBN}:
+
+@table @code
+@item sparc-stub.c
+@kindex sparc-stub.c
+For @sc{sparc} architectures.
+
+@item m68k-stub.c
+@kindex m68k-stub.c
+@cindex Motorola 680x0
+@cindex 680x0
+For Motorola 680x0 architectures.
+
+@item i386-stub.c
+@kindex i386-stub.c
+@cindex Intel
+@cindex 386
+For Intel 386 and compatible architectures.
+@end table
+
+The @file{README} file in the @value{GDBN} distribution may list other
+recently added stubs.
+
+@menu
+* Stub Contents:: What the stub can do for you
+* Bootstrapping:: What you must do for the stub
+* Debug Session:: Putting it all together
+* Protocol:: Outline of the communication protocol
+@ifset GDBSERVER
+* Server:: Using the `gdbserver' program
+@end ifset
+@end menu
+
+@node Stub Contents
+@subsubsection What the stub can do for you
+
+@cindex remote serial stub
+The debugging stub for your architecture supplies these three
+subroutines:
+
+@table @code
+@item set_debug_traps
+@kindex set_debug_traps
+@cindex remote serial stub, initialization
+This routine arranges for @code{handle_exception} to run when your
+program stops. You must call this subroutine explicitly near the
+beginning of your program.
+
+@item handle_exception
+@kindex handle_exception
+@cindex remote serial stub, main routine
+This is the central workhorse, but your program never calls it
+explicitly---the setup code arranges for @code{handle_exception} to
+run when a trap is triggered.
+
+@code{handle_exception} takes control when your program stops during
+execution (for example, on a breakpoint), and mediates communications
+with @value{GDBN} on the host machine. This is where the communications
+protocol is implemented; @code{handle_exception} acts as the @value{GDBN}
+representative on the target machine; it begins by sending summary
+information on the state of your program, then continues to execute,
+retrieving and transmitting any information @value{GDBN} needs, until you
+execute a @value{GDBN} command that makes your program resume; at that point,
+@code{handle_exception} returns control to your own code on the target
+machine.
+
+@item breakpoint
+@cindex @code{breakpoint} subroutine, remote
+Use this auxiliary subroutine to make your program contain a
+breakpoint. Depending on the particular situation, this may be the only
+way for @value{GDBN} to get control. For instance, if your target
+machine has some sort of interrupt button, you won't need to call this;
+pressing the interrupt button will transfer control to
+@code{handle_exception}---in effect, to @value{GDBN}. On some machines,
+simply receiving characters on the serial port may also trigger a trap;
+again, in that situation, you don't need to call @code{breakpoint} from
+your own program---simply running @samp{target remote} from the host
+@value{GDBN} session will get control.
+
+Call @code{breakpoint} if none of these is true, or if you simply want
+to make certain your program stops at a predetermined point for the
+start of your debugging session.
+@end table
+
+@node Bootstrapping
+@subsubsection What you must do for the stub
+
+@cindex remote stub, support routines
+The debugging stubs that come with @value{GDBN} are set up for a particular
+chip architecture, but they have no information about the rest of your
+debugging target machine. To allow the stub to work, you must supply
+these special low-level subroutines:
+
+@table @code
+@item int getDebugChar()
+@kindex getDebugChar
+Write this subroutine to read a single character from the serial port.
+It may be identical to @code{getchar} for your target system; a
+different name is used to allow you to distinguish the two if you wish.
+
+@item void putDebugChar(int)
+@kindex putDebugChar
+Write this subroutine to write a single character to the serial port.
+It may be identical to @code{putchar} for your target system; a
+different name is used to allow you to distinguish the two if you wish.
+
+@item void exceptionHandler (int @var{exception_number}, void *@var{exception_address})
+@kindex exceptionHandler
+Write this function to install @var{exception_address} in the exception
+handling tables. You need to do this because the stub does not have any
+way of knowing what the exception handling tables on your target system
+are like (for example, the processor's table might be in @sc{rom},
+containing entries which point to a table in @sc{ram}).
+@var{exception_number} is the exception number which should be changed;
+its meaning is architecture-dependent (for example, different numbers
+might represent divide by zero, misaligned access, etc). When this
+exception occurs, control should be transferred directly to
+@var{exception_address}, and the processor state (stack, registers,
+etc.) should be just as it is when a processor exception occurs. So if
+you want to use a jump instruction to reach @var{exception_address}, it
+should be a simple jump, not a jump to subroutine.
+
+For the 386, @var{exception_address} should be installed as an interrupt
+gate so that interrupts are masked while the handler runs. The gate
+should be at privilege level 0 (the most privileged level). The
+@sc{sparc} and 68k stubs are able to mask interrupts themself without
+help from @code{exceptionHandler}.
+
+@item void flush_i_cache()
+@kindex flush_i_cache
+Write this subroutine to flush the instruction cache, if any, on your
+target machine. If there is no instruction cache, this subroutine may
+be a no-op.
+
+On target machines that have instruction caches, @value{GDBN} requires this
+function to make certain that the state of your program is stable.
+@end table
+
+@noindent
+You must also make sure this library routine is available:
+
+@table @code
+@item void *memset(void *, int, int)
+@kindex memset
+This is the standard library function @code{memset} that sets an area of
+memory to a known value. If you have one of the free versions of
+@code{libc.a}, @code{memset} can be found there; otherwise, you must
+either obtain it from your hardware manufacturer, or write your own.
+@end table
+
+If you do not use the GNU C compiler, you may need other standard
+library subroutines as well; this will vary from one stub to another,
+but in general the stubs are likely to use any of the common library
+subroutines which @code{gcc} generates as inline code.
+
+
+@node Debug Session
+@subsubsection Putting it all together
+
+@cindex remote serial debugging summary
+In summary, when your program is ready to debug, you must follow these
+steps.
+
+@enumerate
+@item
+Make sure you have the supporting low-level routines
+(@pxref{Bootstrapping,,What you must do for the stub}):
+@display
+@code{getDebugChar}, @code{putDebugChar},
+@code{flush_i_cache}, @code{memset}, @code{exceptionHandler}.
+@end display
+
+@item
+Insert these lines near the top of your program:
+
+@example
+set_debug_traps();
+breakpoint();
+@end example
+
+@item
+For the 680x0 stub only, you need to provide a variable called
+@code{exceptionHook}. Normally you just use
+
+@example
+void (*exceptionHook)() = 0;
+@end example
+
+but if before calling @code{set_debug_traps}, you set it to point to a
+function in your program, that function is called when
+@code{@value{GDBN}} continues after stopping on a trap (for example, bus
+error). The function indicated by @code{exceptionHook} is called with
+one parameter: an @code{int} which is the exception number.
+
+@item
+Compile and link together: your program, the @value{GDBN} debugging stub for
+your target architecture, and the supporting subroutines.
+
+@item
+Make sure you have a serial connection between your target machine and
+the @value{GDBN} host, and identify the serial port used for this on the host.
+
+@item
+@c The "remote" target now provides a `load' command, so we should
+@c document that. FIXME.
+Download your program to your target machine (or get it there by
+whatever means the manufacturer provides), and start it.
+
+@item
+To start remote debugging, run @value{GDBN} on the host machine, and specify
+as an executable file the program that is running in the remote machine.
+This tells @value{GDBN} how to find your program's symbols and the contents
+of its pure text.
+
+@cindex serial line, @code{target remote}
+Then establish communication using the @code{target remote} command.
+Its argument specifies how to communicate with the target
+machine---either via a devicename attached to a direct serial line, or a
+TCP port (usually to a terminal server which in turn has a serial line
+to the target). For example, to use a serial line connected to the
+device named @file{/dev/ttyb}:
+
+@example
+target remote /dev/ttyb
+@end example
+
+@cindex TCP port, @code{target remote}
+To use a TCP connection, use an argument of the form
+@code{@var{host}:port}. For example, to connect to port 2828 on a
+terminal server named @code{manyfarms}:
+
+@example
+target remote manyfarms:2828
+@end example
+@end enumerate
+
+Now you can use all the usual commands to examine and change data and to
+step and continue the remote program.
+
+To resume the remote program and stop debugging it, use the @code{detach}
+command.
+
+@cindex interrupting remote programs
+@cindex remote programs, interrupting
+Whenever @value{GDBN} is waiting for the remote program, if you type the
+interrupt character (often @key{C-C}), @value{GDBN} attempts to stop the
+program. This may or may not succeed, depending in part on the hardware
+and the serial drivers the remote system uses. If you type the
+interrupt character once again, @value{GDBN} displays this prompt:
+
+@example
+Interrupted while waiting for the program.
+Give up (and stop debugging it)? (y or n)
+@end example
+
+If you type @kbd{y}, @value{GDBN} abandons the remote debugging session.
+(If you decide you want to try again later, you can use @samp{target
+remote} again to connect once more.) If you type @kbd{n}, @value{GDBN}
+goes back to waiting.
+
+@node Protocol
+@subsubsection Outline of the communication protocol
+
+@cindex debugging stub, example
+@cindex remote stub, example
+@cindex stub example, remote debugging
+The stub files provided with @value{GDBN} implement the target side of the
+communication protocol, and the @value{GDBN} side is implemented in the
+@value{GDBN} source file @file{remote.c}. Normally, you can simply allow
+these subroutines to communicate, and ignore the details. (If you're
+implementing your own stub file, you can still ignore the details: start
+with one of the existing stub files. @file{sparc-stub.c} is the best
+organized, and therefore the easiest to read.)
+
+However, there may be occasions when you need to know something about
+the protocol---for example, if there is only one serial port to your
+target machine, you might want your program to do something special if
+it recognizes a packet meant for @value{GDBN}.
+
+@cindex protocol, @value{GDBN} remote serial
+@cindex serial protocol, @value{GDBN} remote
+@cindex remote serial protocol
+All @value{GDBN} commands and responses (other than acknowledgements, which
+are single characters) are sent as a packet which includes a
+checksum. A packet is introduced with the character @samp{$}, and ends
+with the character @samp{#} followed by a two-digit checksum:
+
+@example
+$@var{packet info}#@var{checksum}
+@end example
+
+@cindex checksum, for @value{GDBN} remote
+@noindent
+@var{checksum} is computed as the modulo 256 sum of the @var{packet
+info} characters.
+
+When either the host or the target machine receives a packet, the first
+response expected is an acknowledgement: a single character, either
+@samp{+} (to indicate the package was received correctly) or @samp{-}
+(to request retransmission).
+
+The host (@value{GDBN}) sends commands, and the target (the debugging stub
+incorporated in your program) sends data in response. The target also
+sends data when your program stops.
+
+Command packets are distinguished by their first character, which
+identifies the kind of command.
+
+These are the commands currently supported:
+
+@table @code
+@item g
+Requests the values of CPU registers.
+
+@item G
+Sets the values of CPU registers.
+
+@item m@var{addr},@var{count}
+Read @var{count} bytes at location @var{addr}.
+
+@item M@var{addr},@var{count}:@dots{}
+Write @var{count} bytes at location @var{addr}.
+
+@item c
+@itemx c@var{addr}
+Resume execution at the current address (or at @var{addr} if supplied).
+
+@item s
+@itemx s@var{addr}
+Step the target program for one instruction, from either the current
+program counter or from @var{addr} if supplied.
+
+@item k
+Kill the target program.
+
+@item ?
+Report the most recent signal. To allow you to take advantage of the
+@value{GDBN} signal handling commands, one of the functions of the debugging
+stub is to report CPU traps as the corresponding POSIX signal values.
+@end table
+
+@kindex set remotedebug
+@kindex show remotedebug
+@cindex packets, reporting on stdout
+@cindex serial connections, debugging
+If you have trouble with the serial connection, you can use the command
+@code{set remotedebug}. This makes @value{GDBN} report on all packets sent
+back and forth across the serial line to the remote machine. The
+packet-debugging information is printed on the @value{GDBN} standard output
+stream. @code{set remotedebug off} turns it off, and @code{show
+remotedebug} will show you its current state.
+
+@ifset GDBSERVER
+@node Server
+@subsubsection Using the @code{gdbserver} program
+
+@kindex gdbserver
+@cindex remote connection without stubs
+@code{gdbserver} is a control program for Unix-like systems, which
+allows you to connect your program with a remote @value{GDBN} via
+@code{target remote}---but without linking in the usual debugging stub.
+
+@code{gdbserver} is not a complete replacement for the debugging stubs,
+because it requires essentially the same operating-system facilities
+that @value{GDBN} itself does. In fact, a system that can run
+@code{gdbserver} to connect to a remote @value{GDBN} could also run
+@var{GDBN} locally! @code{gdbserver} is sometimes useful nevertheless,
+because it is a much smaller program than @value{GDBN} itself. It is
+also easier to port than all of @var{GDBN}, so you may be able to get
+started more quickly on a new system by using @code{gdbserver}.
+
+@value{GDBN} and @code{gdbserver} communicate via either a serial line
+or a TCP connection, using the standard @value{GDBN} remote serial
+protocol.
+
+@table @emph
+@item On the target,
+you need to have a copy of the program you want to debug.
+@code{gdbserver} does not need your program's symbol table, so you can
+strip the program if necessary to save space. @value{GDBN} on the host
+system does all the symbol handling.
+
+To use the server, you must tell it how to communicate with @value{GDB};
+the name of your program; and the arguments for your program. The
+syntax is:
+
+@smallexample
+target> gdbserver @var{comm} @var{program} [ @var{args} @dots{} ]
+@end smallexample
+
+@var{comm} is either a device name (to use a serial line) or a TCP
+hostname and portnumber. For example, to debug emacs with the argument
+@samp{foo.txt} and communicate with @value{GDBN} over the serial port
+@file{/dev/com1}:
+
+@smallexample
+target> gdbserver /dev/com1 emacs foo.txt
+@end smallexample
+
+@code{gdbserver} waits passively for the host @value{GDBN} to communicate
+with it.
+
+To use a TCP connection instead of a serial line:
+
+@smallexample
+target> gdbserver host:2345 emacs foo.txt
+@end smallexample
+
+The only difference from the previous example is the first argument,
+specifying that you are communicating with the host @value{GDBN} via
+TCP. The @samp{host:2345} argument means that @code{gdbserver} is to
+expect a TCP connection from machine @samp{host} to local TCP port 2345.
+(Currently, the @samp{host} part is ignored.) You can choose any number
+you want for the port number as long as it does not conflict with any
+TCP ports already in use on the target system.@footnote{If you choose a
+port number that conflicts with another service, @code{gdbserver} prints
+an error message and exits.} You must use the same port number with the
+host @value{GDBN} @code{target remote} command.
+
+@item On the host,
+you need an unstripped copy of your program, since
+@value{GDBN} needs symbols and debugging information. Start up
+@value{GDBN} as usual, using the name of the local copy of your program
+as the first argument. (You may also need the
+@samp{--baud} option if the serial line is running at anything other than 9600 bps.)
+After that, use @code{target remote} to establish communications with @code{gdbserver}. Its argument is either
+a device name (usually a serial device, like @file{/dev/ttyb}), or a TCP
+port descriptof in the form @code{@var{host}:@var{PORT}}. For example:
+
+@smallexample
+(@value{GDBP}) target remote /dev/ttyb
+@end smallexample
+
+@noindent
+communicates with the server via serial line @file{/dev/ttyb}, and
+
+@smallexample
+(@value{GDBP}) target remote the-target:2345
+@end smallexample
+
+@noindent
+communicates via a TCP connection to port 2345 on host @file{the-target}.
+For TCP connections, you must start up @code{gdbserver} prior to using
+the @code{target remote} command. Otherwise you may get an error whose
+text depends on the host system, but which usually looks something like
+@samp{Connection refused}.
+@end table
+@end ifset
+
+@end ifset
+
+@ifset I960
+@node i960-Nindy Remote
+@subsection @value{GDBN} with a remote i960 (Nindy)
+
+@cindex Nindy
+@cindex i960
+@dfn{Nindy} is a ROM Monitor program for Intel 960 target systems. When
+@value{GDBN} is configured to control a remote Intel 960 using Nindy, you can
+tell @value{GDBN} how to connect to the 960 in several ways:
+
+@itemize @bullet
+@item
+Through command line options specifying serial port, version of the
+Nindy protocol, and communications speed;
+
+@item
+By responding to a prompt on startup;
+
+@item
+By using the @code{target} command at any point during your @value{GDBN}
+session. @xref{Target Commands, ,Commands for managing targets}.
+
+@end itemize
+
+@menu
+* Nindy Startup:: Startup with Nindy
+* Nindy Options:: Options for Nindy
+* Nindy Reset:: Nindy reset command
+@end menu
+
+@node Nindy Startup
+@subsubsection Startup with Nindy
+
+If you simply start @code{@value{GDBP}} without using any command-line
+options, you are prompted for what serial port to use, @emph{before} you
+reach the ordinary @value{GDBN} prompt:
+
+@example
+Attach /dev/ttyNN -- specify NN, or "quit" to quit:
+@end example
+
+@noindent
+Respond to the prompt with whatever suffix (after @samp{/dev/tty})
+identifies the serial port you want to use. You can, if you choose,
+simply start up with no Nindy connection by responding to the prompt
+with an empty line. If you do this and later wish to attach to Nindy,
+use @code{target} (@pxref{Target Commands, ,Commands for managing targets}).
+
+@node Nindy Options
+@subsubsection Options for Nindy
+
+These are the startup options for beginning your @value{GDBN} session with a
+Nindy-960 board attached:
+
+@table @code
+@item -r @var{port}
+Specify the serial port name of a serial interface to be used to connect
+to the target system. This option is only available when @value{GDBN} is
+configured for the Intel 960 target architecture. You may specify
+@var{port} as any of: a full pathname (e.g. @samp{-r /dev/ttya}), a
+device name in @file{/dev} (e.g. @samp{-r ttya}), or simply the unique
+suffix for a specific @code{tty} (e.g. @samp{-r a}).
+
+@item -O
+(An uppercase letter ``O'', not a zero.) Specify that @value{GDBN} should use
+the ``old'' Nindy monitor protocol to connect to the target system.
+This option is only available when @value{GDBN} is configured for the Intel 960
+target architecture.
+
+@quotation
+@emph{Warning:} if you specify @samp{-O}, but are actually trying to
+connect to a target system that expects the newer protocol, the connection
+fails, appearing to be a speed mismatch. @value{GDBN} repeatedly
+attempts to reconnect at several different line speeds. You can abort
+this process with an interrupt.
+@end quotation
+
+@item -brk
+Specify that @value{GDBN} should first send a @code{BREAK} signal to the target
+system, in an attempt to reset it, before connecting to a Nindy target.
+
+@quotation
+@emph{Warning:} Many target systems do not have the hardware that this
+requires; it only works with a few boards.
+@end quotation
+@end table
+
+The standard @samp{-b} option controls the line speed used on the serial
+port.
+
+@c @group
+@node Nindy Reset
+@subsubsection Nindy reset command
+
+@table @code
+@item reset
+@kindex reset
+For a Nindy target, this command sends a ``break'' to the remote target
+system; this is only useful if the target has been equipped with a
+circuit to perform a hard reset (or some other interesting action) when
+a break is detected.
+@end table
+@c @end group
+@end ifset
+
+@ifset AMD29K
+@node UDI29K Remote
+@subsection @value{GDBN} and the UDI protocol for AMD29K
+
+@cindex UDI
+@cindex AMD29K via UDI
+@value{GDBN} supports AMD's UDI (``Universal Debugger Interface'')
+protocol for debugging the a29k processor family. To use this
+configuration with AMD targets running the MiniMON monitor, you need the
+program @code{MONTIP}, available from AMD at no charge. You can also
+use @value{GDBN} with the UDI conformant a29k simulator program
+@code{ISSTIP}, also available from AMD.
+
+@table @code
+@item target udi @var{keyword}
+@kindex udi
+Select the UDI interface to a remote a29k board or simulator, where
+@var{keyword} is an entry in the AMD configuration file @file{udi_soc}.
+This file contains keyword entries which specify parameters used to
+connect to a29k targets. If the @file{udi_soc} file is not in your
+working directory, you must set the environment variable @samp{UDICONF}
+to its pathname.
+@end table
+
+@node EB29K Remote
+@subsection @value{GDBN} and the EBMON protocol for AMD29K
+
+@cindex EB29K board
+@cindex running 29K programs
+
+AMD distributes a 29K development board meant to fit in a PC, together
+with a DOS-hosted monitor program called @code{EBMON}. As a shorthand
+term, this development system is called the ``EB29K''. To use
+@value{GDBN} from a Unix system to run programs on the EB29K board, you
+must first connect a serial cable between the PC (which hosts the EB29K
+board) and a serial port on the Unix system. In the following, we
+assume you've hooked the cable between the PC's @file{COM1} port and
+@file{/dev/ttya} on the Unix system.
+
+@menu
+* Comms (EB29K):: Communications setup
+* gdb-EB29K:: EB29K cross-debugging
+* Remote Log:: Remote log
+@end menu
+
+@node Comms (EB29K)
+@subsubsection Communications setup
+
+The next step is to set up the PC's port, by doing something like this
+in DOS on the PC:
+
+@example
+C:\> MODE com1:9600,n,8,1,none
+@end example
+
+@noindent
+This example---run on an MS DOS 4.0 system---sets the PC port to 9600
+bps, no parity, eight data bits, one stop bit, and no ``retry'' action;
+you must match the communications parameters when establishing the Unix
+end of the connection as well.
+@c FIXME: Who knows what this "no retry action" crud from the DOS manual may
+@c mean? It's optional; leave it out? ---pesch@cygnus.com, 25feb91
+
+To give control of the PC to the Unix side of the serial line, type
+the following at the DOS console:
+
+@example
+C:\> CTTY com1
+@end example
+
+@noindent
+(Later, if you wish to return control to the DOS console, you can use
+the command @code{CTTY con}---but you must send it over the device that
+had control, in our example over the @file{COM1} serial line).
+
+From the Unix host, use a communications program such as @code{tip} or
+@code{cu} to communicate with the PC; for example,
+
+@example
+cu -s 9600 -l /dev/ttya
+@end example
+
+@noindent
+The @code{cu} options shown specify, respectively, the linespeed and the
+serial port to use. If you use @code{tip} instead, your command line
+may look something like the following:
+
+@example
+tip -9600 /dev/ttya
+@end example
+
+@noindent
+Your system may require a different name where we show
+@file{/dev/ttya} as the argument to @code{tip}. The communications
+parameters, including which port to use, are associated with the
+@code{tip} argument in the ``remote'' descriptions file---normally the
+system table @file{/etc/remote}.
+@c FIXME: What if anything needs doing to match the "n,8,1,none" part of
+@c the DOS side's comms setup? cu can support -o (odd
+@c parity), -e (even parity)---apparently no settings for no parity or
+@c for character size. Taken from stty maybe...? John points out tip
+@c can set these as internal variables, eg ~s parity=none; man stty
+@c suggests that it *might* work to stty these options with stdin or
+@c stdout redirected... ---pesch@cygnus.com, 25feb91
+
+@kindex EBMON
+Using the @code{tip} or @code{cu} connection, change the DOS working
+directory to the directory containing a copy of your 29K program, then
+start the PC program @code{EBMON} (an EB29K control program supplied
+with your board by AMD). You should see an initial display from
+@code{EBMON} similar to the one that follows, ending with the
+@code{EBMON} prompt @samp{#}---
+
+@example
+C:\> G:
+
+G:\> CD \usr\joe\work29k
+
+G:\USR\JOE\WORK29K> EBMON
+Am29000 PC Coprocessor Board Monitor, version 3.0-18
+Copyright 1990 Advanced Micro Devices, Inc.
+Written by Gibbons and Associates, Inc.
+
+Enter '?' or 'H' for help
+
+PC Coprocessor Type = EB29K
+I/O Base = 0x208
+Memory Base = 0xd0000
+
+Data Memory Size = 2048KB
+Available I-RAM Range = 0x8000 to 0x1fffff
+Available D-RAM Range = 0x80002000 to 0x801fffff
+
+PageSize = 0x400
+Register Stack Size = 0x800
+Memory Stack Size = 0x1800
+
+CPU PRL = 0x3
+Am29027 Available = No
+Byte Write Available = Yes
+
+# ~.
+@end example
+
+Then exit the @code{cu} or @code{tip} program (done in the example by
+typing @code{~.} at the @code{EBMON} prompt). @code{EBMON} will keep
+running, ready for @value{GDBN} to take over.
+
+For this example, we've assumed what is probably the most convenient
+way to make sure the same 29K program is on both the PC and the Unix
+system: a PC/NFS connection that establishes ``drive @code{G:}'' on the
+PC as a file system on the Unix host. If you do not have PC/NFS or
+something similar connecting the two systems, you must arrange some
+other way---perhaps floppy-disk transfer---of getting the 29K program
+from the Unix system to the PC; @value{GDBN} will @emph{not} download it over the
+serial line.
+
+@node gdb-EB29K
+@subsubsection EB29K cross-debugging
+
+Finally, @code{cd} to the directory containing an image of your 29K
+program on the Unix system, and start @value{GDBN}---specifying as argument the
+name of your 29K program:
+
+@example
+cd /usr/joe/work29k
+@value{GDBP} myfoo
+@end example
+
+Now you can use the @code{target} command:
+
+@example
+target amd-eb /dev/ttya 9600 MYFOO
+@c FIXME: test above 'target amd-eb' as spelled, with caps! caps are meant to
+@c emphasize that this is the name as seen by DOS (since I think DOS is
+@c single-minded about case of letters). ---pesch@cygnus.com, 25feb91
+@end example
+
+@noindent
+In this example, we've assumed your program is in a file called
+@file{myfoo}. Note that the filename given as the last argument to
+@code{target amd-eb} should be the name of the program as it appears to DOS.
+In our example this is simply @code{MYFOO}, but in general it can include
+a DOS path, and depending on your transfer mechanism may not resemble
+the name on the Unix side.
+
+At this point, you can set any breakpoints you wish; when you are ready
+to see your program run on the 29K board, use the @value{GDBN} command
+@code{run}.
+
+To stop debugging the remote program, use the @value{GDBN} @code{detach}
+command.
+
+To return control of the PC to its console, use @code{tip} or @code{cu}
+once again, after your @value{GDBN} session has concluded, to attach to
+@code{EBMON}. You can then type the command @code{q} to shut down
+@code{EBMON}, returning control to the DOS command-line interpreter.
+Type @code{CTTY con} to return command input to the main DOS console,
+and type @kbd{~.} to leave @code{tip} or @code{cu}.
+
+@node Remote Log
+@subsubsection Remote log
+@kindex eb.log
+@cindex log file for EB29K
+
+The @code{target amd-eb} command creates a file @file{eb.log} in the
+current working directory, to help debug problems with the connection.
+@file{eb.log} records all the output from @code{EBMON}, including echoes
+of the commands sent to it. Running @samp{tail -f} on this file in
+another window often helps to understand trouble with @code{EBMON}, or
+unexpected events on the PC side of the connection.
+
+@end ifset
+
+@ifset ST2000
+@node ST2000 Remote
+@subsection @value{GDBN} with a Tandem ST2000
+
+To connect your ST2000 to the host system, see the manufacturer's
+manual. Once the ST2000 is physically attached, you can run
+
+@example
+target st2000 @var{dev} @var{speed}
+@end example
+
+@noindent
+to establish it as your debugging environment. @var{dev} is normally
+the name of a serial device, such as @file{/dev/ttya}, connected to the
+ST2000 via a serial line. You can instead specify @var{dev} as a TCP
+connection (for example, to a serial line attached via a terminal
+concentrator) using the syntax @code{@var{hostname}:@var{portnumber}}.
+
+The @code{load} and @code{attach} commands are @emph{not} defined for
+this target; you must load your program into the ST2000 as you normally
+would for standalone operation. @value{GDBN} will read debugging information
+(such as symbols) from a separate, debugging version of the program
+available on your host computer.
+@c FIXME!! This is terribly vague; what little content is here is
+@c basically hearsay.
+
+@cindex ST2000 auxiliary commands
+These auxiliary @value{GDBN} commands are available to help you with the ST2000
+environment:
+
+@table @code
+@item st2000 @var{command}
+@kindex st2000 @var{cmd}
+@cindex STDBUG commands (ST2000)
+@cindex commands to STDBUG (ST2000)
+Send a @var{command} to the STDBUG monitor. See the manufacturer's
+manual for available commands.
+
+@item connect
+@cindex connect (to STDBUG)
+Connect the controlling terminal to the STDBUG command monitor. When
+you are done interacting with STDBUG, typing either of two character
+sequences will get you back to the @value{GDBN} command prompt:
+@kbd{@key{RET}~.} (Return, followed by tilde and period) or
+@kbd{@key{RET}~@key{C-d}} (Return, followed by tilde and control-D).
+@end table
+@end ifset
+
+@ifset VXWORKS
+@node VxWorks Remote
+@subsection @value{GDBN} and VxWorks
+@cindex VxWorks
+
+@value{GDBN} enables developers to spawn and debug tasks running on networked
+VxWorks targets from a Unix host. Already-running tasks spawned from
+the VxWorks shell can also be debugged. @value{GDBN} uses code that runs on
+both the Unix host and on the VxWorks target. The program
+@code{gdb} is installed and executed on the Unix host. (It may be
+installed with the name @code{vxgdb}, to distinguish it from a
+@value{GDBN} for debugging programs on the host itself.)
+
+The following information on connecting to VxWorks was current when
+this manual was produced; newer releases of VxWorks may use revised
+procedures.
+
+The remote debugging interface (RDB) routines are installed and executed
+on the VxWorks target. These routines are included in the VxWorks library
+@file{rdb.a} and are incorporated into the system image when source-level
+debugging is enabled in the VxWorks configuration.
+
+@kindex INCLUDE_RDB
+If you wish, you can define @code{INCLUDE_RDB} in the VxWorks
+configuration file @file{configAll.h} to include the RDB interface
+routines and spawn the source debugging task @code{tRdbTask} when
+VxWorks is booted. For more information on configuring and remaking
+VxWorks, see the manufacturer's manual.
+@c VxWorks, see the @cite{VxWorks Programmer's Guide}.
+
+Once you have included the RDB interface in your VxWorks system image
+and set your Unix execution search path to find @value{GDBN}, you are ready
+to run @value{GDBN}. From your Unix host, run @code{gdb} (or
+@code{vxgdb}, depending on your installation).
+
+@value{GDBN} comes up showing the prompt:
+
+@example
+(vxgdb)
+@end example
+
+@menu
+* VxWorks Connection:: Connecting to VxWorks
+* VxWorks Download:: VxWorks download
+* VxWorks Attach:: Running tasks
+@end menu
+
+@node VxWorks Connection
+@subsubsection Connecting to VxWorks
+
+The @value{GDBN} command @code{target} lets you connect to a VxWorks target on the
+network. To connect to a target whose host name is ``@code{tt}'', type:
+
+@example
+(vxgdb) target vxworks tt
+@end example
+
+@value{GDBN} displays messages like these:
+
+@smallexample
+Attaching remote machine across net...
+Connected to tt.
+@end smallexample
+
+@value{GDBN} then attempts to read the symbol tables of any object modules
+loaded into the VxWorks target since it was last booted. @value{GDBN} locates
+these files by searching the directories listed in the command search
+path (@pxref{Environment, ,Your program's environment}); if it fails
+to find an object file, it displays a message such as:
+
+@example
+prog.o: No such file or directory.
+@end example
+
+When this happens, add the appropriate directory to the search path with
+the @value{GDBN} command @code{path}, and execute the @code{target}
+command again.
+
+@node VxWorks Download
+@subsubsection VxWorks download
+
+@cindex download to VxWorks
+If you have connected to the VxWorks target and you want to debug an
+object that has not yet been loaded, you can use the @value{GDBN}
+@code{load} command to download a file from Unix to VxWorks
+incrementally. The object file given as an argument to the @code{load}
+command is actually opened twice: first by the VxWorks target in order
+to download the code, then by @value{GDBN} in order to read the symbol
+table. This can lead to problems if the current working directories on
+the two systems differ. If both systems have NFS mounted the same
+filesystems, you can avoid these problems by using absolute paths.
+Otherwise, it is simplest to set the working directory on both systems
+to the directory in which the object file resides, and then to reference
+the file by its name, without any path. For instance, a program
+@file{prog.o} may reside in @file{@var{vxpath}/vw/demo/rdb} in VxWorks
+and in @file{@var{hostpath}/vw/demo/rdb} on the host. To load this
+program, type this on VxWorks:
+
+@example
+-> cd "@var{vxpath}/vw/demo/rdb"
+@end example
+
+Then, in @value{GDBN}, type:
+
+@example
+(vxgdb) cd @var{hostpath}/vw/demo/rdb
+(vxgdb) load prog.o
+@end example
+
+@value{GDBN} displays a response similar to this:
+
+@smallexample
+Reading symbol data from wherever/vw/demo/rdb/prog.o... done.
+@end smallexample
+
+You can also use the @code{load} command to reload an object module
+after editing and recompiling the corresponding source file. Note that
+this will cause @value{GDBN} to delete all currently-defined breakpoints,
+auto-displays, and convenience variables, and to clear the value
+history. (This is necessary in order to preserve the integrity of
+debugger data structures that reference the target system's symbol
+table.)
+
+@node VxWorks Attach
+@subsubsection Running tasks
+
+@cindex running VxWorks tasks
+You can also attach to an existing task using the @code{attach} command as
+follows:
+
+@example
+(vxgdb) attach @var{task}
+@end example
+
+@noindent
+where @var{task} is the VxWorks hexadecimal task ID. The task can be running
+or suspended when you attach to it. If running, it will be suspended at
+the time of attachment.
+@end ifset
+
+@ifset H8
+@node Hitachi Remote
+@subsection @value{GDBN} and Hitachi Microprocessors
+@value{GDBN} needs to know these things to talk to your
+Hitachi SH, H8/300, or H8/500:
+
+@enumerate
+@item
+that you want to use @samp{target hms}, the remote debugging interface
+for Hitachi microprocessors (this is the default when GDB is configured
+specifically for the Hitachi SH, H8/300, or H8/500);
+
+@item
+what serial device connects your host to your Hitachi board (the first
+serial device available on your host is the default);
+
+@ignore
+@c this is only for Unix hosts, not currently of interest.
+@item
+what speed to use over the serial device.
+@end ignore
+@end enumerate
+
+@ifclear H8EXCLUSIVE
+@c only for Unix hosts
+@kindex device
+@cindex serial device, Hitachi micros
+Use the special @code{@value{GDBP}} command @samp{device @var{port}} if you
+need to explicitly set the serial device. The default @var{port} is the
+first available port on your host. This is only necessary on Unix
+hosts, where it is typically something like @file{/dev/ttya}.
+
+@kindex speed
+@cindex serial line speed, Hitachi micros
+@code{@value{GDBP}} has another special command to set the communications
+speed: @samp{speed @var{bps}}. This command also is only used from Unix
+hosts; on DOS hosts, set the line speed as usual from outside GDB with
+the DOS @kbd{mode} command (for instance, @w{@samp{mode
+com2:9600,n,8,1,p}} for a 9600 bps connection).
+
+The @samp{device} and @samp{speed} commands are available only when you
+use a Unix host to debug your Hitachi microprocessor programs. If you
+use a DOS host,
+@end ifclear
+@value{GDBN} depends on an auxiliary terminate-and-stay-resident program
+called @code{asynctsr} to communicate with the development board
+through a PC serial port. You must also use the DOS @code{mode} command
+to set up the serial port on the DOS side.
+
+@ifset DOSHOST
+The following sample session illustrates the steps needed to start a
+program under @value{GDBN} control on an H8/300. The example uses a
+sample H8/300 program called @file{t.x}. The procedure is the same for
+the Hitachi SH and the H8/500.
+
+First hook up your development board. In this example, we use a
+board attached to serial port @code{COM2}; if you use a different serial
+port, substitute its name in the argument of the @code{mode} command.
+When you call @code{asynctsr}, the auxiliary comms program used by the
+degugger, you give it just the numeric part of the serial port's name;
+for example, @samp{asyncstr 2} below runs @code{asyncstr} on
+@code{COM2}.
+
+@example
+(eg-C:\H8300\TEST) mode com2:9600,n,8,1,p
+
+Resident portion of MODE loaded
+
+COM2: 9600, n, 8, 1, p
+
+(eg-C:\H8300\TEST) asynctsr 2
+@end example
+
+@quotation
+@emph{Warning:} We have noticed a bug in PC-NFS that conflicts with
+@code{asynctsr}. If you also run PC-NFS on your DOS host, you may need to
+disable it, or even boot without it, to use @code{asynctsr} to control
+your development board.
+@end quotation
+
+@kindex target hms
+Now that serial communications are set up, and the development board is
+connected, you can start up @value{GDBN}. Call @code{@value{GDBP}} with
+the name of your program as the argument. @code{@value{GDBP}} prompts
+you, as usual, with the prompt @samp{(@value{GDBP})}. Use two special
+commands to begin your debugging session: @samp{target hms} to specify
+cross-debugging to the Hitachi board, and the @code{load} command to
+download your program to the board. @code{load} displays the names of
+the program's sections, and a @samp{*} for each 2K of data downloaded.
+(If you want to refresh @value{GDBN} data on symbols or on the
+executable file without downloading, use the @value{GDBN} commands
+@code{file} or @code{symbol-file}. These commands, and @code{load}
+itself, are described in @ref{Files,,Commands to specify files}.)
+
+@smallexample
+(eg-C:\H8300\TEST) @value{GDBP} t.x
+GDB is free software and you are welcome to distribute copies
+ of it under certain conditions; type "show copying" to see
+ the conditions.
+There is absolutely no warranty for GDB; type "show warranty"
+for details.
+GDB @value{GDBVN}, Copyright 1992 Free Software Foundation, Inc...
+(gdb) target hms
+Connected to remote H8/300 HMS system.
+(gdb) load t.x
+.text : 0x8000 .. 0xabde ***********
+.data : 0xabde .. 0xad30 *
+.stack : 0xf000 .. 0xf014 *
+@end smallexample
+
+At this point, you're ready to run or debug your program. From here on,
+you can use all the usual @value{GDBN} commands. The @code{break} command
+sets breakpoints; the @code{run} command starts your program;
+@code{print} or @code{x} display data; the @code{continue} command
+resumes execution after stopping at a breakpoint. You can use the
+@code{help} command at any time to find out more about @value{GDBN} commands.
+
+Remember, however, that @emph{operating system} facilities aren't
+available on your development board; for example, if your program hangs,
+you can't send an interrupt---but you can press the @sc{reset} switch!
+
+Use the @sc{reset} button on the development board
+@itemize @bullet
+@item
+to interrupt your program (don't use @kbd{ctl-C} on the DOS host---it has
+no way to pass an interrupt signal to the development board); and
+
+@item
+to return to the @value{GDBN} command prompt after your program finishes
+normally. The communications protocol provides no other way for @value{GDBN}
+to detect program completion.
+@end itemize
+
+In either case, @value{GDBN} will see the effect of a @sc{reset} on the
+development board as a ``normal exit'' of your program.
+@end ifset
+@end ifset
+
+@ifset MIPS
+@node MIPS Remote
+@subsection @value{GDBN} and remote MIPS boards
+
+@cindex MIPS boards
+@value{GDBN} can use the MIPS remote debugging protocol to talk to a
+MIPS board attached to a serial line. This is available when
+you configure @value{GDBN} with @samp{--target=mips-idt-ecoff}.
+
+@kindex target mips @var{port}
+To run a program on the board, start up @code{@value{GDBP}} with the
+name of your program as the argument. To connect to the board, use the
+command @samp{target mips @var{port}}, where @var{port} is the name of
+the serial port connected to the board. If the program has not already
+been downloaded to the board, you may use the @code{load} command to
+download it. You can then use all the usual @value{GDBN} commands.
+
+You can also specify @var{port} as a TCP connection (for instance, to a
+serial line managed by a terminal concentrator), using the syntax
+@code{@var{hostname}:@var{portnumber}}.
+
+@cindex @code{remotedebug}, MIPS protocol
+@c FIXME! For this to be useful, you must know something about the MIPS
+@c FIXME...protocol. Where is it described?
+You can see some debugging information about communications with the board
+by setting the @code{remotedebug} variable. If you set it to 1 using
+@samp{set remotedebug 1} every packet will be displayed. If you set it
+to 2 every character will be displayed. You can check the current value
+at any time with the command @samp{show remotedebug}.
+
+@cindex @code{timeout}, MIPS protocol
+@cindex @code{retransmit-timeout}, MIPS protocol
+@kindex set timeout
+@kindex show timeout
+@kindex set retransmit-timeout
+@kindex show retransmit-timeout
+You can control the timeout used while waiting for a packet, in the MIPS
+remote protocol, with the @code{set timeout @var{seconds}} command. The
+default is 5 seconds. Similarly, you can control the timeout used while
+waiting for an acknowledgement of a packet with the @code{set
+retransmit-timeout @var{seconds}} command. The default is 3 seconds.
+You can inspect both values with @code{show timeout} and @code{show
+retransmit-timeout}. (These commands are @emph{only} available when
+@value{GDBN} is configured for @samp{--target=mips-idt-ecoff}.)
+
+@kindex set mipsfpu off
+@cindex MIPS remote floating point
+@cindex floating point, MIPS remote
+If your target board does not support the MIPS floating point
+coprocessor, you should use the command @samp{set mipsfpu off} (you may
+wish to put this in your @value{GDBINIT} file). This tells @value{GDBN}
+how to find the return value of functions which return floating point
+values. It also allows @value{GDBN} to avoid saving the floating point
+registers when calling functions on the board.
+@end ifset
+
+@ifset SIMS
+@node Simulator
+@subsection Simulated CPU target
+
+@ifset GENERIC
+@cindex simulator
+@cindex simulator, Z8000
+@cindex Z8000 simulator
+@cindex simulator, H8/300 or H8/500
+@cindex H8/300 or H8/500 simulator
+@cindex simulator, Hitachi SH
+@cindex Hitachi SH simulator
+@cindex CPU simulator
+For some configurations, @value{GDBN} includes a CPU simulator that you
+can use instead of a hardware CPU to debug your programs. Currently,
+a simulator is available when @value{GDBN} is configured to debug Zilog
+Z8000 or Hitachi microprocessor targets.
+@end ifset
+
+@ifclear GENERIC
+@ifset H8
+@cindex simulator, H8/300 or H8/500
+@cindex Hitachi H8/300 or H8/500 simulator
+@cindex simulator, Hitachi SH
+@cindex Hitachi SH simulator
+When configured for debugging Hitachi microprocessor targets,
+@value{GDBN} includes a CPU simulator for the target chip (a Hitachi SH,
+H8/300, or H8/500).
+@end ifset
+
+@ifset Z8K
+@cindex simulator, Z8000
+@cindex Zilog Z8000 simulator
+When configured for debugging Zilog Z8000 targets, @value{GDBN} includes
+a Z8000 simulator.
+@end ifset
+@end ifclear
+
+@ifset Z8K
+For the Z8000 family, @samp{target sim} simulates either the Z8002 (the
+unsegmented variant of the Z8000 architecture) or the Z8001 (the
+segmented variant). The simulator recognizes which architecture is
+appropriate by inspecting the object code.
+@end ifset
+
+@table @code
+@item target sim
+@kindex sim
+@kindex target sim
+Debug programs on a simulated CPU
+@ifset GENERIC
+(which CPU depends on the @value{GDBN} configuration)
+@end ifset
+@end table
+
+@noindent
+After specifying this target, you can debug programs for the simulated
+CPU in the same style as programs for your host computer; use the
+@code{file} command to load a new program image, the @code{run} command
+to run your program, and so on.
+
+As well as making available all the usual machine registers (see
+@code{info reg}), this debugging target provides three additional items
+of information as specially named registers:
+
+@table @code
+@item cycles
+Counts clock-ticks in the simulator.
+
+@item insts
+Counts instructions run in the simulator.
+
+@item time
+Execution time in 60ths of a second.
+@end table
+
+You can refer to these values in @value{GDBN} expressions with the usual
+conventions; for example, @w{@samp{b fputc if $cycles>5000}} sets a
+conditional breakpoint that will suspend only after at least 5000
+simulated clock ticks.
+@end ifset
diff --git a/gnu/usr.bin/gdb/doc/stabs.texinfo b/gnu/usr.bin/gdb/doc/stabs.texinfo
new file mode 100644
index 0000000..23d8e7b
--- /dev/null
+++ b/gnu/usr.bin/gdb/doc/stabs.texinfo
@@ -0,0 +1,3795 @@
+\input texinfo
+@setfilename stabs.info
+
+@c @finalout
+
+@ifinfo
+@format
+START-INFO-DIR-ENTRY
+* Stabs:: The "stabs" debugging information format.
+END-INFO-DIR-ENTRY
+@end format
+@end ifinfo
+
+@ifinfo
+This document describes the stabs debugging symbol tables.
+
+Copyright 1992, 1993 Free Software Foundation, Inc.
+Contributed by Cygnus Support. Written by Julia Menapace, Jim Kingdon,
+and David MacKenzie.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+@ignore
+Permission is granted to process this file through Tex and print the
+results, provided the printed document carries copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+@end ignore
+Permission is granted to copy or distribute modified versions of this
+manual under the terms of the GPL (for which purpose this text may be
+regarded as a program in the language TeX).
+@end ifinfo
+
+@setchapternewpage odd
+@settitle STABS
+@titlepage
+@title The ``stabs'' debug format
+@author Julia Menapace, Jim Kingdon, David MacKenzie
+@author Cygnus Support
+@page
+@tex
+\def\$#1${{#1}} % Kluge: collect RCS revision info without $...$
+\xdef\manvers{\$Revision: 1.1.1.1 $} % For use in headers, footers too
+{\parskip=0pt
+\hfill Cygnus Support\par
+\hfill \manvers\par
+\hfill \TeX{}info \texinfoversion\par
+}
+@end tex
+
+@vskip 0pt plus 1filll
+Copyright @copyright{} 1992, 1993 Free Software Foundation, Inc.
+Contributed by Cygnus Support.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+@end titlepage
+
+@ifinfo
+@node Top
+@top The "stabs" representation of debugging information
+
+This document describes the stabs debugging format.
+
+@menu
+* Overview:: Overview of stabs
+* Program Structure:: Encoding of the structure of the program
+* Constants:: Constants
+* Variables::
+* Types:: Type definitions
+* Symbol Tables:: Symbol information in symbol tables
+* Cplusplus:: Appendixes:
+* Stab Types:: Symbol types in a.out files
+* Symbol Descriptors:: Table of symbol descriptors
+* Type Descriptors:: Table of type descriptors
+* Expanded Reference:: Reference information by stab type
+* Questions:: Questions and anomolies
+* XCOFF Differences:: Differences between GNU stabs in a.out
+ and GNU stabs in XCOFF
+* Sun Differences:: Differences between GNU stabs and Sun
+ native stabs
+* Stabs In ELF:: Stabs in an ELF file.
+* Symbol Types Index:: Index of symbolic stab symbol type names.
+@end menu
+@end ifinfo
+
+
+@node Overview
+@chapter Overview of Stabs
+
+@dfn{Stabs} refers to a format for information that describes a program
+to a debugger. This format was apparently invented by
+@c FIXME! <<name of inventor>> at
+the University of California at Berkeley, for the @code{pdx} Pascal
+debugger; the format has spread widely since then.
+
+This document is one of the few published sources of documentation on
+stabs. It is believed to be comprehensive for stabs used by C. The
+lists of symbol descriptors (@pxref{Symbol Descriptors}) and type
+descriptors (@pxref{Type Descriptors}) are believed to be completely
+comprehensive. Stabs for COBOL-specific features and for variant
+records (used by Pascal and Modula-2) are poorly documented here.
+
+Other sources of information on stabs are @cite{Dbx and Dbxtool
+Interfaces}, 2nd edition, by Sun, 1988, and @cite{AIX Version 3.2 Files
+Reference}, Fourth Edition, September 1992, "dbx Stabstring Grammar" in
+the a.out section, page 2-31. This document is believed to incorporate
+the information from those two sources except where it explictly directs
+you to them for more information.
+
+@menu
+* Flow:: Overview of debugging information flow
+* Stabs Format:: Overview of stab format
+* String Field:: The string field
+* C Example:: A simple example in C source
+* Assembly Code:: The simple example at the assembly level
+@end menu
+
+@node Flow
+@section Overview of Debugging Information Flow
+
+The GNU C compiler compiles C source in a @file{.c} file into assembly
+language in a @file{.s} file, which the assembler translates into
+a @file{.o} file, which the linker combines with other @file{.o} files and
+libraries to produce an executable file.
+
+With the @samp{-g} option, GCC puts in the @file{.s} file additional
+debugging information, which is slightly transformed by the assembler
+and linker, and carried through into the final executable. This
+debugging information describes features of the source file like line
+numbers, the types and scopes of variables, and function names,
+parameters, and scopes.
+
+For some object file formats, the debugging information is encapsulated
+in assembler directives known collectively as @dfn{stab} (symbol table)
+directives, which are interspersed with the generated code. Stabs are
+the native format for debugging information in the a.out and XCOFF
+object file formats. The GNU tools can also emit stabs in the COFF and
+ECOFF object file formats.
+
+The assembler adds the information from stabs to the symbol information
+it places by default in the symbol table and the string table of the
+@file{.o} file it is building. The linker consolidates the @file{.o}
+files into one executable file, with one symbol table and one string
+table. Debuggers use the symbol and string tables in the executable as
+a source of debugging information about the program.
+
+@node Stabs Format
+@section Overview of Stab Format
+
+There are three overall formats for stab assembler directives,
+differentiated by the first word of the stab. The name of the directive
+describes which combination of four possible data fields follows. It is
+either @code{.stabs} (string), @code{.stabn} (number), or @code{.stabd}
+(dot). IBM's XCOFF assembler uses @code{.stabx} (and some other
+directives such as @code{.file} and @code{.bi}) instead of
+@code{.stabs}, @code{.stabn} or @code{.stabd}.
+
+The overall format of each class of stab is:
+
+@example
+.stabs "@var{string}",@var{type},@var{other},@var{desc},@var{value}
+.stabn @var{type},@var{other},@var{desc},@var{value}
+.stabd @var{type},@var{other},@var{desc}
+.stabx "@var{string}",@var{value},@var{type},@var{sdb-type}
+@end example
+
+@c what is the correct term for "current file location"? My AIX
+@c assembler manual calls it "the value of the current location counter".
+For @code{.stabn} and @code{.stabd}, there is no @var{string} (the
+@code{n_strx} field is zero; see @ref{Symbol Tables}). For
+@code{.stabd}, the @var{value} field is implicit and has the value of
+the current file location. For @code{.stabx}, the @var{sdb-type} field
+is unused for stabs and can always be set to zero. The @var{other}
+field is almost always unused and can be set to zero.
+
+The number in the @var{type} field gives some basic information about
+which type of stab this is (or whether it @emph{is} a stab, as opposed
+to an ordinary symbol). Each valid type number defines a different stab
+type; further, the stab type defines the exact interpretation of, and
+possible values for, any remaining @var{string}, @var{desc}, or
+@var{value} fields present in the stab. @xref{Stab Types}, for a list
+in numeric order of the valid @var{type} field values for stab directives.
+
+@node String Field
+@section The String Field
+
+For most stabs the string field holds the meat of the
+debugging information. The flexible nature of this field
+is what makes stabs extensible. For some stab types the string field
+contains only a name. For other stab types the contents can be a great
+deal more complex.
+
+The overall format of the string field for most stab types is:
+
+@example
+"@var{name}:@var{symbol-descriptor} @var{type-information}"
+@end example
+
+@var{name} is the name of the symbol represented by the stab.
+@var{name} can be omitted, which means the stab represents an unnamed
+object. For example, @samp{:t10=*2} defines type 10 as a pointer to
+type 2, but does not give the type a name. Omitting the @var{name}
+field is supported by AIX dbx and GDB after about version 4.8, but not
+other debuggers. GCC sometimes uses a single space as the name instead
+of omitting the name altogether; apparently that is supported by most
+debuggers.
+
+The @var{symbol-descriptor} following the @samp{:} is an alphabetic
+character that tells more specifically what kind of symbol the stab
+represents. If the @var{symbol-descriptor} is omitted, but type
+information follows, then the stab represents a local variable. For a
+list of symbol descriptors, see @ref{Symbol Descriptors}. The @samp{c}
+symbol descriptor is an exception in that it is not followed by type
+information. @xref{Constants}.
+
+@var{type-information} is either a @var{type-number}, or
+@samp{@var{type-number}=}. A @var{type-number} alone is a type
+reference, referring directly to a type that has already been defined.
+
+The @samp{@var{type-number}=} form is a type definition, where the
+number represents a new type which is about to be defined. The type
+definition may refer to other types by number, and those type numbers
+may be followed by @samp{=} and nested definitions.
+
+In a type definition, if the character that follows the equals sign is
+non-numeric then it is a @var{type-descriptor}, and tells what kind of
+type is about to be defined. Any other values following the
+@var{type-descriptor} vary, depending on the @var{type-descriptor}.
+@xref{Type Descriptors}, for a list of @var{type-descriptor} values. If
+a number follows the @samp{=} then the number is a @var{type-reference}.
+For a full description of types, @ref{Types}.
+
+There is an AIX extension for type attributes. Following the @samp{=}
+are any number of type attributes. Each one starts with @samp{@@} and
+ends with @samp{;}. Debuggers, including AIX's dbx and GDB 4.10, skip
+any type attributes they do not recognize. GDB 4.9 and other versions
+of dbx may not do this. Because of a conflict with C++
+(@pxref{Cplusplus}), new attributes should not be defined which begin
+with a digit, @samp{(}, or @samp{-}; GDB may be unable to distinguish
+those from the C++ type descriptor @samp{@@}. The attributes are:
+
+@table @code
+@item a@var{boundary}
+@var{boundary} is an integer specifying the alignment. I assume it
+applies to all variables of this type.
+
+@item s@var{size}
+Size in bits of a variable of this type.
+
+@item p@var{integer}
+Pointer class (for checking). Not sure what this means, or how
+@var{integer} is interpreted.
+
+@item P
+Indicate this is a packed type, meaning that structure fields or array
+elements are placed more closely in memory, to save memory at the
+expense of speed.
+@end table
+
+All of this can make the string field quite long. All
+versions of GDB, and some versions of dbx, can handle arbitrarily long
+strings. But many versions of dbx cretinously limit the strings to
+about 80 characters, so compilers which must work with such dbx's need
+to split the @code{.stabs} directive into several @code{.stabs}
+directives. Each stab duplicates exactly all but the
+string field. The string field of
+every stab except the last is marked as continued with a
+double-backslash at the end. Removing the backslashes and concatenating
+the string fields of each stab produces the original,
+long string.
+
+@node C Example
+@section A Simple Example in C Source
+
+To get the flavor of how stabs describe source information for a C
+program, let's look at the simple program:
+
+@example
+main()
+@{
+ printf("Hello world");
+@}
+@end example
+
+When compiled with @samp{-g}, the program above yields the following
+@file{.s} file. Line numbers have been added to make it easier to refer
+to parts of the @file{.s} file in the description of the stabs that
+follows.
+
+@node Assembly Code
+@section The Simple Example at the Assembly Level
+
+This simple ``hello world'' example demonstrates several of the stab
+types used to describe C language source files.
+
+@example
+1 gcc2_compiled.:
+2 .stabs "/cygint/s1/users/jcm/play/",100,0,0,Ltext0
+3 .stabs "hello.c",100,0,0,Ltext0
+4 .text
+5 Ltext0:
+6 .stabs "int:t1=r1;-2147483648;2147483647;",128,0,0,0
+7 .stabs "char:t2=r2;0;127;",128,0,0,0
+8 .stabs "long int:t3=r1;-2147483648;2147483647;",128,0,0,0
+9 .stabs "unsigned int:t4=r1;0;-1;",128,0,0,0
+10 .stabs "long unsigned int:t5=r1;0;-1;",128,0,0,0
+11 .stabs "short int:t6=r1;-32768;32767;",128,0,0,0
+12 .stabs "long long int:t7=r1;0;-1;",128,0,0,0
+13 .stabs "short unsigned int:t8=r1;0;65535;",128,0,0,0
+14 .stabs "long long unsigned int:t9=r1;0;-1;",128,0,0,0
+15 .stabs "signed char:t10=r1;-128;127;",128,0,0,0
+16 .stabs "unsigned char:t11=r1;0;255;",128,0,0,0
+17 .stabs "float:t12=r1;4;0;",128,0,0,0
+18 .stabs "double:t13=r1;8;0;",128,0,0,0
+19 .stabs "long double:t14=r1;8;0;",128,0,0,0
+20 .stabs "void:t15=15",128,0,0,0
+21 .align 4
+22 LC0:
+23 .ascii "Hello, world!\12\0"
+24 .align 4
+25 .global _main
+26 .proc 1
+27 _main:
+28 .stabn 68,0,4,LM1
+29 LM1:
+30 !#PROLOGUE# 0
+31 save %sp,-136,%sp
+32 !#PROLOGUE# 1
+33 call ___main,0
+34 nop
+35 .stabn 68,0,5,LM2
+36 LM2:
+37 LBB2:
+38 sethi %hi(LC0),%o1
+39 or %o1,%lo(LC0),%o0
+40 call _printf,0
+41 nop
+42 .stabn 68,0,6,LM3
+43 LM3:
+44 LBE2:
+45 .stabn 68,0,6,LM4
+46 LM4:
+47 L1:
+48 ret
+49 restore
+50 .stabs "main:F1",36,0,0,_main
+51 .stabn 192,0,0,LBB2
+52 .stabn 224,0,0,LBE2
+@end example
+
+@node Program Structure
+@chapter Encoding the Structure of the Program
+
+The elements of the program structure that stabs encode include the name
+of the main function, the names of the source and include files, the
+line numbers, procedure names and types, and the beginnings and ends of
+blocks of code.
+
+@menu
+* Main Program:: Indicate what the main program is
+* Source Files:: The path and name of the source file
+* Include Files:: Names of include files
+* Line Numbers::
+* Procedures::
+* Nested Procedures::
+* Block Structure::
+@end menu
+
+@node Main Program
+@section Main Program
+
+@findex N_MAIN
+Most languages allow the main program to have any name. The
+@code{N_MAIN} stab type tells the debugger the name that is used in this
+program. Only the string field is significant; it is the name of
+a function which is the main program. Most C compilers do not use this
+stab (they expect the debugger to assume that the name is @code{main}),
+but some C compilers emit an @code{N_MAIN} stab for the @code{main}
+function.
+
+@node Source Files
+@section Paths and Names of the Source Files
+
+@findex N_SO
+Before any other stabs occur, there must be a stab specifying the source
+file. This information is contained in a symbol of stab type
+@code{N_SO}; the string field contains the name of the file. The
+value of the symbol is the start address of the portion of the
+text section corresponding to that file.
+
+With the Sun Solaris2 compiler, the desc field contains a
+source-language code.
+@c Do the debuggers use it? What are the codes? -djm
+
+Some compilers (for example, GCC2 and SunOS4 @file{/bin/cc}) also
+include the directory in which the source was compiled, in a second
+@code{N_SO} symbol preceding the one containing the file name. This
+symbol can be distinguished by the fact that it ends in a slash. Code
+from the @code{cfront} C++ compiler can have additional @code{N_SO} symbols for
+nonexistent source files after the @code{N_SO} for the real source file;
+these are believed to contain no useful information.
+
+For example:
+
+@example
+.stabs "/cygint/s1/users/jcm/play/",100,0,0,Ltext0 # @r{100 is N_SO}
+.stabs "hello.c",100,0,0,Ltext0
+ .text
+Ltext0:
+@end example
+
+Instead of @code{N_SO} symbols, XCOFF uses a @code{.file} assembler
+directive which assembles to a standard COFF @code{.file} symbol;
+explaining this in detail is outside the scope of this document.
+
+@node Include Files
+@section Names of Include Files
+
+There are several schemes for dealing with include files: the
+traditional @code{N_SOL} approach, Sun's @code{N_BINCL} approach, and the
+XCOFF @code{C_BINCL} approach (which despite the similar name has little in
+common with @code{N_BINCL}).
+
+@findex N_SOL
+An @code{N_SOL} symbol specifies which include file subsequent symbols
+refer to. The string field is the name of the file and the value is the
+text address corresponding to the end of the previous include file and
+the start of this one. To specify the main source file again, use an
+@code{N_SOL} symbol with the name of the main source file.
+
+@findex N_BINCL
+@findex N_EINCL
+@findex N_EXCL
+The @code{N_BINCL} approach works as follows. An @code{N_BINCL} symbol
+specifies the start of an include file. In an object file, only the
+string is significant; the Sun linker puts data into some of the
+other fields. The end of the include file is marked by an
+@code{N_EINCL} symbol (which has no string field). In an object
+file, there is no significant data in the @code{N_EINCL} symbol; the Sun
+linker puts data into some of the fields. @code{N_BINCL} and
+@code{N_EINCL} can be nested.
+
+If the linker detects that two source files have identical stabs between
+an @code{N_BINCL} and @code{N_EINCL} pair (as will generally be the case
+for a header file), then it only puts out the stabs once. Each
+additional occurance is replaced by an @code{N_EXCL} symbol. I believe
+the Sun (SunOS4, not sure about Solaris) linker is the only one which
+supports this feature.
+@c What do the fields of N_EXCL contain? -djm
+
+@findex C_BINCL
+@findex C_EINCL
+For the start of an include file in XCOFF, use the @file{.bi} assembler
+directive, which generates a @code{C_BINCL} symbol. A @file{.ei}
+directive, which generates a @code{C_EINCL} symbol, denotes the end of
+the include file. Both directives are followed by the name of the
+source file in quotes, which becomes the string for the symbol.
+The value of each symbol, produced automatically by the assembler
+and linker, is the offset into the executable of the beginning
+(inclusive, as you'd expect) or end (inclusive, as you would not expect)
+of the portion of the COFF line table that corresponds to this include
+file. @code{C_BINCL} and @code{C_EINCL} do not nest.
+
+@node Line Numbers
+@section Line Numbers
+
+@findex N_SLINE
+An @code{N_SLINE} symbol represents the start of a source line. The
+desc field contains the line number and the value
+contains the code address for the start of that source line. On most
+machines the address is absolute; for Sun's stabs-in-ELF, it is relative
+to the function in which the @code{N_SLINE} symbol occurs.
+
+@findex N_DSLINE
+@findex N_BSLINE
+GNU documents @code{N_DSLINE} and @code{N_BSLINE} symbols for line
+numbers in the data or bss segments, respectively. They are identical
+to @code{N_SLINE} but are relocated differently by the linker. They
+were intended to be used to describe the source location of a variable
+declaration, but I believe that GCC2 actually puts the line number in
+the desc field of the stab for the variable itself. GDB has been
+ignoring these symbols (unless they contain a string field) since
+at least GDB 3.5.
+
+For single source lines that generate discontiguous code, such as flow
+of control statements, there may be more than one line number entry for
+the same source line. In this case there is a line number entry at the
+start of each code range, each with the same line number.
+
+XCOFF does not use stabs for line numbers. Instead, it uses COFF line
+numbers (which are outside the scope of this document). Standard COFF
+line numbers cannot deal with include files, but in XCOFF this is fixed
+with the @code{C_BINCL} method of marking include files (@pxref{Include
+Files}).
+
+@node Procedures
+@section Procedures
+
+@findex N_FUN, for functions
+@findex N_FNAME
+@findex N_STSYM, for functions (Sun acc)
+@findex N_GSYM, for functions (Sun acc)
+All of the following stabs normally use the @code{N_FUN} symbol type.
+However, Sun's @code{acc} compiler on SunOS4 uses @code{N_GSYM} and
+@code{N_STSYM}, which means that the value of the stab for the function
+is useless and the debugger must get the address of the function from
+the non-stab symbols instead. BSD Fortran is said to use @code{N_FNAME}
+with the same restriction; the value of the symbol is not useful (I'm
+not sure it really does use this, because GDB doesn't handle this and no
+one has complained).
+
+A function is represented by an @samp{F} symbol descriptor for a global
+(extern) function, and @samp{f} for a static (local) function. The
+value is the address of the start of the function. For @code{a.out}, it
+is already relocated. For stabs in ELF, the SunPRO compiler version
+2.0.1 and GCC put out an address which gets relocated by the linker. In
+a future release SunPRO is planning to put out zero, in which case the
+address can be found from the ELF (non-stab) symbol. Because looking
+things up in the ELF symbols would probably be slow, I'm not sure how to
+find which symbol of that name is the right one, and this doesn't
+provide any way to deal with nested functions, it would probably be
+better to make the value of the stab an address relative to the start of
+the file. See @ref{Stabs In ELF} for more information on linker
+relocation of stabs in ELF files.
+
+The type information of the stab represents the return type of the
+function; thus @samp{foo:f5} means that foo is a function returning type
+5. There is no need to try to get the line number of the start of the
+function from the stab for the function; it is in the next
+@code{N_SLINE} symbol.
+
+@c FIXME: verify whether the "I suspect" below is true or not.
+Some compilers (such as Sun's Solaris compiler) support an extension for
+specifying the types of the arguments. I suspect this extension is not
+used for old (non-prototyped) function definitions in C. If the
+extension is in use, the type information of the stab for the function
+is followed by type information for each argument, with each argument
+preceded by @samp{;}. An argument type of 0 means that additional
+arguments are being passed, whose types and number may vary (@samp{...}
+in ANSI C). GDB has tolerated this extension (parsed the syntax, if not
+necessarily used the information) since at least version 4.8; I don't
+know whether all versions of dbx tolerate it. The argument types given
+here are not redundant with the symbols for the formal parameters
+(@pxref{Parameters}); they are the types of the arguments as they are
+passed, before any conversions might take place. For example, if a C
+function which is declared without a prototype takes a @code{float}
+argument, the value is passed as a @code{double} but then converted to a
+@code{float}. Debuggers need to use the types given in the arguments
+when printing values, but when calling the function they need to use the
+types given in the symbol defining the function.
+
+If the return type and types of arguments of a function which is defined
+in another source file are specified (i.e., a function prototype in ANSI
+C), traditionally compilers emit no stab; the only way for the debugger
+to find the information is if the source file where the function is
+defined was also compiled with debugging symbols. As an extension the
+Solaris compiler uses symbol descriptor @samp{P} followed by the return
+type of the function, followed by the arguments, each preceded by
+@samp{;}, as in a stab with symbol descriptor @samp{f} or @samp{F}.
+This use of symbol descriptor @samp{P} can be distinguished from its use
+for register parameters (@pxref{Register Parameters}) by the fact that it has
+symbol type @code{N_FUN}.
+
+The AIX documentation also defines symbol descriptor @samp{J} as an
+internal function. I assume this means a function nested within another
+function. It also says symbol descriptor @samp{m} is a module in
+Modula-2 or extended Pascal.
+
+Procedures (functions which do not return values) are represented as
+functions returning the @code{void} type in C. I don't see why this couldn't
+be used for all languages (inventing a @code{void} type for this purpose if
+necessary), but the AIX documentation defines @samp{I}, @samp{P}, and
+@samp{Q} for internal, global, and static procedures, respectively.
+These symbol descriptors are unusual in that they are not followed by
+type information.
+
+The following example shows a stab for a function @code{main} which
+returns type number @code{1}. The @code{_main} specified for the value
+is a reference to an assembler label which is used to fill in the start
+address of the function.
+
+@example
+.stabs "main:F1",36,0,0,_main # @r{36 is N_FUN}
+@end example
+
+The stab representing a procedure is located immediately following the
+code of the procedure. This stab is in turn directly followed by a
+group of other stabs describing elements of the procedure. These other
+stabs describe the procedure's parameters, its block local variables, and
+its block structure.
+
+@node Nested Procedures
+@section Nested Procedures
+
+For any of the symbol descriptors representing procedures, after the
+symbol descriptor and the type information is optionally a scope
+specifier. This consists of a comma, the name of the procedure, another
+comma, and the name of the enclosing procedure. The first name is local
+to the scope specified, and seems to be redundant with the name of the
+symbol (before the @samp{:}). This feature is used by GCC, and
+presumably Pascal, Modula-2, etc., compilers, for nested functions.
+
+If procedures are nested more than one level deep, only the immediately
+containing scope is specified. For example, this code:
+
+@example
+int
+foo (int x)
+@{
+ int bar (int y)
+ @{
+ int baz (int z)
+ @{
+ return x + y + z;
+ @}
+ return baz (x + 2 * y);
+ @}
+ return x + bar (3 * x);
+@}
+@end example
+
+@noindent
+produces the stabs:
+
+@example
+.stabs "baz:f1,baz,bar",36,0,0,_baz.15 # @r{36 is N_FUN}
+.stabs "bar:f1,bar,foo",36,0,0,_bar.12
+.stabs "foo:F1",36,0,0,_foo
+@end example
+
+@node Block Structure
+@section Block Structure
+
+@findex N_LBRAC
+@findex N_RBRAC
+The program's block structure is represented by the @code{N_LBRAC} (left
+brace) and the @code{N_RBRAC} (right brace) stab types. The variables
+defined inside a block precede the @code{N_LBRAC} symbol for most
+compilers, including GCC. Other compilers, such as the Convex, Acorn
+RISC machine, and Sun @code{acc} compilers, put the variables after the
+@code{N_LBRAC} symbol. The values of the @code{N_LBRAC} and
+@code{N_RBRAC} symbols are the start and end addresses of the code of
+the block, respectively. For most machines, they are relative to the
+starting address of this source file. For the Gould NP1, they are
+absolute. For Sun's stabs-in-ELF, they are relative to the function in
+which they occur.
+
+The @code{N_LBRAC} and @code{N_RBRAC} stabs that describe the block
+scope of a procedure are located after the @code{N_FUN} stab that
+represents the procedure itself.
+
+Sun documents the desc field of @code{N_LBRAC} and
+@code{N_RBRAC} symbols as containing the nesting level of the block.
+However, dbx seems to not care, and GCC always sets desc to
+zero.
+
+@node Constants
+@chapter Constants
+
+The @samp{c} symbol descriptor indicates that this stab represents a
+constant. This symbol descriptor is an exception to the general rule
+that symbol descriptors are followed by type information. Instead, it
+is followed by @samp{=} and one of the following:
+
+@table @code
+@item b @var{value}
+Boolean constant. @var{value} is a numeric value; I assume it is 0 for
+false or 1 for true.
+
+@item c @var{value}
+Character constant. @var{value} is the numeric value of the constant.
+
+@item e @var{type-information} , @var{value}
+Constant whose value can be represented as integral.
+@var{type-information} is the type of the constant, as it would appear
+after a symbol descriptor (@pxref{String Field}). @var{value} is the
+numeric value of the constant. GDB 4.9 does not actually get the right
+value if @var{value} does not fit in a host @code{int}, but it does not
+do anything violent, and future debuggers could be extended to accept
+integers of any size (whether unsigned or not). This constant type is
+usually documented as being only for enumeration constants, but GDB has
+never imposed that restriction; I don't know about other debuggers.
+
+@item i @var{value}
+Integer constant. @var{value} is the numeric value. The type is some
+sort of generic integer type (for GDB, a host @code{int}); to specify
+the type explicitly, use @samp{e} instead.
+
+@item r @var{value}
+Real constant. @var{value} is the real value, which can be @samp{INF}
+(optionally preceded by a sign) for infinity, @samp{QNAN} for a quiet
+NaN (not-a-number), or @samp{SNAN} for a signalling NaN. If it is a
+normal number the format is that accepted by the C library function
+@code{atof}.
+
+@item s @var{string}
+String constant. @var{string} is a string enclosed in either @samp{'}
+(in which case @samp{'} characters within the string are represented as
+@samp{\'} or @samp{"} (in which case @samp{"} characters within the
+string are represented as @samp{\"}).
+
+@item S @var{type-information} , @var{elements} , @var{bits} , @var{pattern}
+Set constant. @var{type-information} is the type of the constant, as it
+would appear after a symbol descriptor (@pxref{String Field}).
+@var{elements} is the number of elements in the set (does this means
+how many bits of @var{pattern} are actually used, which would be
+redundant with the type, or perhaps the number of bits set in
+@var{pattern}? I don't get it), @var{bits} is the number of bits in the
+constant (meaning it specifies the length of @var{pattern}, I think),
+and @var{pattern} is a hexadecimal representation of the set. AIX
+documentation refers to a limit of 32 bytes, but I see no reason why
+this limit should exist. This form could probably be used for arbitrary
+constants, not just sets; the only catch is that @var{pattern} should be
+understood to be target, not host, byte order and format.
+@end table
+
+The boolean, character, string, and set constants are not supported by
+GDB 4.9, but it ignores them. GDB 4.8 and earlier gave an error
+message and refused to read symbols from the file containing the
+constants.
+
+The above information is followed by @samp{;}.
+
+@node Variables
+@chapter Variables
+
+Different types of stabs describe the various ways that variables can be
+allocated: on the stack, globally, in registers, in common blocks,
+statically, or as arguments to a function.
+
+@menu
+* Stack Variables:: Variables allocated on the stack.
+* Global Variables:: Variables used by more than one source file.
+* Register Variables:: Variables in registers.
+* Common Blocks:: Variables statically allocated together.
+* Statics:: Variables local to one source file.
+* Based Variables:: Fortran pointer based variables.
+* Parameters:: Variables for arguments to functions.
+@end menu
+
+@node Stack Variables
+@section Automatic Variables Allocated on the Stack
+
+If a variable's scope is local to a function and its lifetime is only as
+long as that function executes (C calls such variables
+@dfn{automatic}), it can be allocated in a register (@pxref{Register
+Variables}) or on the stack.
+
+@findex N_LSYM
+Each variable allocated on the stack has a stab with the symbol
+descriptor omitted. Since type information should begin with a digit,
+@samp{-}, or @samp{(}, only those characters precluded from being used
+for symbol descriptors. However, the Acorn RISC machine (ARM) is said
+to get this wrong: it puts out a mere type definition here, without the
+preceding @samp{@var{type-number}=}. This is a bad idea; there is no
+guarantee that type descriptors are distinct from symbol descriptors.
+Stabs for stack variables use the @code{N_LSYM} stab type.
+
+The value of the stab is the offset of the variable within the
+local variables. On most machines this is an offset from the frame
+pointer and is negative. The location of the stab specifies which block
+it is defined in; see @ref{Block Structure}.
+
+For example, the following C code:
+
+@example
+int
+main ()
+@{
+ int x;
+@}
+@end example
+
+produces the following stabs:
+
+@example
+.stabs "main:F1",36,0,0,_main # @r{36 is N_FUN}
+.stabs "x:1",128,0,0,-12 # @r{128 is N_LSYM}
+.stabn 192,0,0,LBB2 # @r{192 is N_LBRAC}
+.stabn 224,0,0,LBE2 # @r{224 is N_RBRAC}
+@end example
+
+@xref{Procedures} for more information on the @code{N_FUN} stab, and
+@ref{Block Structure} for more information on the @code{N_LBRAC} and
+@code{N_RBRAC} stabs.
+
+@node Global Variables
+@section Global Variables
+
+@findex N_GSYM
+A variable whose scope is not specific to just one source file is
+represented by the @samp{G} symbol descriptor. These stabs use the
+@code{N_GSYM} stab type. The type information for the stab
+(@pxref{String Field}) gives the type of the variable.
+
+For example, the following source code:
+
+@example
+char g_foo = 'c';
+@end example
+
+@noindent
+yields the following assembly code:
+
+@example
+.stabs "g_foo:G2",32,0,0,0 # @r{32 is N_GSYM}
+ .global _g_foo
+ .data
+_g_foo:
+ .byte 99
+@end example
+
+The address of the variable represented by the @code{N_GSYM} is not
+contained in the @code{N_GSYM} stab. The debugger gets this information
+from the external symbol for the global variable. In the example above,
+the @code{.global _g_foo} and @code{_g_foo:} lines tell the assembler to
+produce an external symbol.
+
+@node Register Variables
+@section Register Variables
+
+@findex N_RSYM
+@c According to an old version of this manual, AIX uses C_RPSYM instead
+@c of C_RSYM. I am skeptical; this should be verified.
+Register variables have their own stab type, @code{N_RSYM}, and their
+own symbol descriptor, @samp{r}. The stab's value is the
+number of the register where the variable data will be stored.
+@c .stabs "name:type",N_RSYM,0,RegSize,RegNumber (Sun doc)
+
+AIX defines a separate symbol descriptor @samp{d} for floating point
+registers. This seems unnecessary; why not just just give floating
+point registers different register numbers? I have not verified whether
+the compiler actually uses @samp{d}.
+
+If the register is explicitly allocated to a global variable, but not
+initialized, as in:
+
+@example
+register int g_bar asm ("%g5");
+@end example
+
+@noindent
+then the stab may be emitted at the end of the object file, with
+the other bss symbols.
+
+@node Common Blocks
+@section Common Blocks
+
+A common block is a statically allocated section of memory which can be
+referred to by several source files. It may contain several variables.
+I believe Fortran is the only language with this feature.
+
+@findex N_BCOMM
+@findex N_ECOMM
+@findex C_BCOMM
+@findex C_ECOMM
+A @code{N_BCOMM} stab begins a common block and an @code{N_ECOMM} stab
+ends it. The only field that is significant in these two stabs is the
+string, which names a normal (non-debugging) symbol that gives the
+address of the common block. According to IBM documentation, only the
+@code{N_BCOMM} has the name of the common block (even though their
+compiler actually puts it both places).
+
+@findex N_ECOML
+@findex C_ECOML
+The stabs for the members of the common block are between the
+@code{N_BCOMM} and the @code{N_ECOMM}; the value of each stab is the
+offset within the common block of that variable. IBM uses the
+@code{C_ECOML} stab type, and there is a corresponding @code{N_ECOML}
+stab type, but Sun's Fortran compiler uses @code{N_GSYM} instead. The
+variables within a common block use the @samp{V} symbol descriptor (I
+believe this is true of all Fortran variables). Other stabs (at least
+type declarations using @code{C_DECL}) can also be between the
+@code{N_BCOMM} and the @code{N_ECOMM}.
+
+@node Statics
+@section Static Variables
+
+Initialized static variables are represented by the @samp{S} and
+@samp{V} symbol descriptors. @samp{S} means file scope static, and
+@samp{V} means procedure scope static.
+
+@c This is probably not worth mentioning; it is only true on the sparc
+@c for `double' variables which although declared const are actually in
+@c the data segment (the text segment can't guarantee 8 byte alignment).
+@c (although GCC
+@c 2.4.5 has a bug in that it uses @code{N_FUN}, so neither dbx nor GDB can
+@c find the variables)
+@findex N_STSYM
+@findex N_LCSYM
+@findex N_FUN, for variables
+@findex N_ROSYM
+In a.out files, @code{N_STSYM} means the data section, @code{N_FUN}
+means the text section, and @code{N_LCSYM} means the bss section. For
+those systems with a read-only data section separate from the text
+section (Solaris), @code{N_ROSYM} means the read-only data section.
+
+For example, the source lines:
+
+@example
+static const int var_const = 5;
+static int var_init = 2;
+static int var_noinit;
+@end example
+
+@noindent
+yield the following stabs:
+
+@example
+.stabs "var_const:S1",36,0,0,_var_const # @r{36 is N_FUN}
+@dots{}
+.stabs "var_init:S1",38,0,0,_var_init # @r{38 is N_STSYM}
+@dots{}
+.stabs "var_noinit:S1",40,0,0,_var_noinit # @r{40 is N_LCSYM}
+@end example
+
+In XCOFF files, each symbol has a section number, so the stab type
+need not indicate the section.
+
+In ECOFF files, the storage class is used to specify the section, so the
+stab type need not indicate the section.
+
+In ELF files, for the SunPRO compiler version 2.0.1, symbol descriptor
+@samp{S} means that the address is absolute (the linker relocates it)
+and symbol descriptor @samp{V} means that the address is relative to the
+start of the relevant section for that compilation unit. SunPRO has
+plans to have the linker stop relocating stabs; I suspect that their the
+debugger gets the address from the corresponding ELF (not stab) symbol.
+I'm not sure how to find which symbol of that name is the right one.
+The clean way to do all this would be to have a the value of a symbol
+descriptor @samp{S} symbol be an offset relative to the start of the
+file, just like everything else, but that introduces obvious
+compatibility problems. For more information on linker stab relocation,
+@xref{Stabs In ELF}.
+
+@node Based Variables
+@section Fortran Based Variables
+
+Fortran (at least, the Sun and SGI dialects of FORTRAN-77) has a feature
+which allows allocating arrays with @code{malloc}, but which avoids
+blurring the line between arrays and pointers the way that C does. In
+stabs such a variable uses the @samp{b} symbol descriptor.
+
+For example, the Fortran declarations
+
+@example
+real foo, foo10(10), foo10_5(10,5)
+pointer (foop, foo)
+pointer (foo10p, foo10)
+pointer (foo105p, foo10_5)
+@end example
+
+produce the stabs
+
+@example
+foo:b6
+foo10:bar3;1;10;6
+foo10_5:bar3;1;5;ar3;1;10;6
+@end example
+
+In this example, @code{real} is type 6 and type 3 is an integral type
+which is the type of the subscripts of the array (probably
+@code{integer}).
+
+The @samp{b} symbol descriptor is like @samp{V} in that it denotes a
+statically allocated symbol whose scope is local to a function; see
+@xref{Statics}. The value of the symbol, instead of being the address
+of the variable itself, is the address of a pointer to that variable.
+So in the above example, the value of the @code{foo} stab is the address
+of a pointer to a real, the value of the @code{foo10} stab is the
+address of a pointer to a 10-element array of reals, and the value of
+the @code{foo10_5} stab is the address of a pointer to a 5-element array
+of 10-element arrays of reals.
+
+@node Parameters
+@section Parameters
+
+Formal parameters to a function are represented by a stab (or sometimes
+two; see below) for each parameter. The stabs are in the order in which
+the debugger should print the parameters (i.e., the order in which the
+parameters are declared in the source file). The exact form of the stab
+depends on how the parameter is being passed.
+
+@findex N_PSYM
+Parameters passed on the stack use the symbol descriptor @samp{p} and
+the @code{N_PSYM} symbol type. The value of the symbol is an offset
+used to locate the parameter on the stack; its exact meaning is
+machine-dependent, but on most machines it is an offset from the frame
+pointer.
+
+As a simple example, the code:
+
+@example
+main (argc, argv)
+ int argc;
+ char **argv;
+@end example
+
+produces the stabs:
+
+@example
+.stabs "main:F1",36,0,0,_main # @r{36 is N_FUN}
+.stabs "argc:p1",160,0,0,68 # @r{160 is N_PSYM}
+.stabs "argv:p20=*21=*2",160,0,0,72
+@end example
+
+The type definition of @code{argv} is interesting because it contains
+several type definitions. Type 21 is pointer to type 2 (char) and
+@code{argv} (type 20) is pointer to type 21.
+
+@c FIXME: figure out what these mean and describe them coherently.
+The following symbol descriptors are also said to go with @code{N_PSYM}.
+The value of the symbol is said to be an offset from the argument
+pointer (I'm not sure whether this is true or not).
+
+@example
+pP (<<??>>)
+pF Fortran function parameter
+X (function result variable)
+@end example
+
+@menu
+* Register Parameters::
+* Local Variable Parameters::
+* Reference Parameters::
+* Conformant Arrays::
+@end menu
+
+@node Register Parameters
+@subsection Passing Parameters in Registers
+
+If the parameter is passed in a register, then traditionally there are
+two symbols for each argument:
+
+@example
+.stabs "arg:p1" . . . ; N_PSYM
+.stabs "arg:r1" . . . ; N_RSYM
+@end example
+
+Debuggers use the second one to find the value, and the first one to
+know that it is an argument.
+
+@findex C_RPSYM
+@findex N_RSYM, for parameters
+Because that approach is kind of ugly, some compilers use symbol
+descriptor @samp{P} or @samp{R} to indicate an argument which is in a
+register. Symbol type @code{C_RPSYM} is used with @samp{R} and
+@code{N_RSYM} is used with @samp{P}. The symbol's value is
+the register number. @samp{P} and @samp{R} mean the same thing; the
+difference is that @samp{P} is a GNU invention and @samp{R} is an IBM
+(XCOFF) invention. As of version 4.9, GDB should handle either one.
+
+There is at least one case where GCC uses a @samp{p} and @samp{r} pair
+rather than @samp{P}; this is where the argument is passed in the
+argument list and then loaded into a register.
+
+According to the AIX documentation, symbol descriptor @samp{D} is for a
+parameter passed in a floating point register. This seems
+unnecessary---why not just use @samp{R} with a register number which
+indicates that it's a floating point register? I haven't verified
+whether the system actually does what the documentation indicates.
+
+@c FIXME: On the hppa this is for any type > 8 bytes, I think, and not
+@c for small structures (investigate).
+On the sparc and hppa, for a @samp{P} symbol whose type is a structure
+or union, the register contains the address of the structure. On the
+sparc, this is also true of a @samp{p} and @samp{r} pair (using Sun
+@code{cc}) or a @samp{p} symbol. However, if a (small) structure is
+really in a register, @samp{r} is used. And, to top it all off, on the
+hppa it might be a structure which was passed on the stack and loaded
+into a register and for which there is a @samp{p} and @samp{r} pair! I
+believe that symbol descriptor @samp{i} is supposed to deal with this
+case (it is said to mean "value parameter by reference, indirect
+access"; I don't know the source for this information), but I don't know
+details or what compilers or debuggers use it, if any (not GDB or GCC).
+It is not clear to me whether this case needs to be dealt with
+differently than parameters passed by reference (@pxref{Reference Parameters}).
+
+@node Local Variable Parameters
+@subsection Storing Parameters as Local Variables
+
+There is a case similar to an argument in a register, which is an
+argument that is actually stored as a local variable. Sometimes this
+happens when the argument was passed in a register and then the compiler
+stores it as a local variable. If possible, the compiler should claim
+that it's in a register, but this isn't always done.
+
+@findex N_LSYM, for parameter
+Some compilers use the pair of symbols approach described above
+(@samp{@var{arg}:p} followed by @samp{@var{arg}:}); this includes GCC1
+(not GCC2) on the sparc when passing a small structure and GCC2
+(sometimes) when the argument type is @code{float} and it is passed as a
+@code{double} and converted to @code{float} by the prologue (in the
+latter case the type of the @samp{@var{arg}:p} symbol is @code{double}
+and the type of the @samp{@var{arg}:} symbol is @code{float}).
+
+GCC, at least on the 960, has another solution to the same problem. It
+uses a single @samp{p} symbol descriptor for an argument which is stored
+as a local variable but uses @code{N_LSYM} instead of @code{N_PSYM}. In
+this case, the value of the symbol is an offset relative to the local
+variables for that function, not relative to the arguments; on some
+machines those are the same thing, but not on all.
+
+@c This is mostly just background info; the part that logically belongs
+@c here is the last sentence.
+On the VAX or on other machines in which the calling convention includes
+the number of words of arguments actually passed, the debugger (GDB at
+least) uses the parameter symbols to keep track of whether it needs to
+print nameless arguments in addition to the formal parameters which it
+has printed because each one has a stab. For example, in
+
+@example
+extern int fprintf (FILE *stream, char *format, @dots{});
+@dots{}
+fprintf (stdout, "%d\n", x);
+@end example
+
+there are stabs for @code{stream} and @code{format}. On most machines,
+the debugger can only print those two arguments (because it has no way
+of knowing that additional arguments were passed), but on the VAX or
+other machines with a calling convention which indicates the number of
+words of arguments, the debugger can print all three arguments. To do
+so, the parameter symbol (symbol descriptor @samp{p}) (not necessarily
+@samp{r} or symbol descriptor omitted symbols) needs to contain the
+actual type as passed (for example, @code{double} not @code{float} if it
+is passed as a double and converted to a float).
+
+@node Reference Parameters
+@subsection Passing Parameters by Reference
+
+If the parameter is passed by reference (e.g., Pascal @code{VAR}
+parameters), then the symbol descriptor is @samp{v} if it is in the
+argument list, or @samp{a} if it in a register. Other than the fact
+that these contain the address of the parameter rather than the
+parameter itself, they are identical to @samp{p} and @samp{R},
+respectively. I believe @samp{a} is an AIX invention; @samp{v} is
+supported by all stabs-using systems as far as I know.
+
+@node Conformant Arrays
+@subsection Passing Conformant Array Parameters
+
+@c Is this paragraph correct? It is based on piecing together patchy
+@c information and some guesswork
+Conformant arrays are a feature of Modula-2, and perhaps other
+languages, in which the size of an array parameter is not known to the
+called function until run-time. Such parameters have two stabs: a
+@samp{x} for the array itself, and a @samp{C}, which represents the size
+of the array. The value of the @samp{x} stab is the offset in the
+argument list where the address of the array is stored (it this right?
+it is a guess); the value of the @samp{C} stab is the offset in the
+argument list where the size of the array (in elements? in bytes?) is
+stored.
+
+@node Types
+@chapter Defining Types
+
+The examples so far have described types as references to previously
+defined types, or defined in terms of subranges of or pointers to
+previously defined types. This chapter describes the other type
+descriptors that may follow the @samp{=} in a type definition.
+
+@menu
+* Builtin Types:: Integers, floating point, void, etc.
+* Miscellaneous Types:: Pointers, sets, files, etc.
+* Cross-References:: Referring to a type not yet defined.
+* Subranges:: A type with a specific range.
+* Arrays:: An aggregate type of same-typed elements.
+* Strings:: Like an array but also has a length.
+* Enumerations:: Like an integer but the values have names.
+* Structures:: An aggregate type of different-typed elements.
+* Typedefs:: Giving a type a name.
+* Unions:: Different types sharing storage.
+* Function Types::
+@end menu
+
+@node Builtin Types
+@section Builtin Types
+
+Certain types are built in (@code{int}, @code{short}, @code{void},
+@code{float}, etc.); the debugger recognizes these types and knows how
+to handle them. Thus, don't be surprised if some of the following ways
+of specifying builtin types do not specify everything that a debugger
+would need to know about the type---in some cases they merely specify
+enough information to distinguish the type from other types.
+
+The traditional way to define builtin types is convolunted, so new ways
+have been invented to describe them. Sun's @code{acc} uses special
+builtin type descriptors (@samp{b} and @samp{R}), and IBM uses negative
+type numbers. GDB accepts all three ways, as of version 4.8; dbx just
+accepts the traditional builtin types and perhaps one of the other two
+formats. The following sections describe each of these formats.
+
+@menu
+* Traditional Builtin Types:: Put on your seatbelts and prepare for kludgery
+* Builtin Type Descriptors:: Builtin types with special type descriptors
+* Negative Type Numbers:: Builtin types using negative type numbers
+@end menu
+
+@node Traditional Builtin Types
+@subsection Traditional Builtin Types
+
+This is the traditional, convoluted method for defining builtin types.
+There are several classes of such type definitions: integer, floating
+point, and @code{void}.
+
+@menu
+* Traditional Integer Types::
+* Traditional Other Types::
+@end menu
+
+@node Traditional Integer Types
+@subsubsection Traditional Integer Types
+
+Often types are defined as subranges of themselves. If the bounding values
+fit within an @code{int}, then they are given normally. For example:
+
+@example
+.stabs "int:t1=r1;-2147483648;2147483647;",128,0,0,0 # @r{128 is N_LSYM}
+.stabs "char:t2=r2;0;127;",128,0,0,0
+@end example
+
+Builtin types can also be described as subranges of @code{int}:
+
+@example
+.stabs "unsigned short:t6=r1;0;65535;",128,0,0,0
+@end example
+
+If the lower bound of a subrange is 0 and the upper bound is -1,
+the type is an unsigned integral type whose bounds are too
+big to describe in an @code{int}. Traditionally this is only used for
+@code{unsigned int} and @code{unsigned long}:
+
+@example
+.stabs "unsigned int:t4=r1;0;-1;",128,0,0,0
+@end example
+
+For larger types, GCC 2.4.5 puts out bounds in octal, with one or more
+leading zeroes. In this case a negative bound consists of a number
+which is a 1 bit (for the sign bit) followed by a 0 bit for each bit in
+the number (except the sign bit), and a positive bound is one which is a
+1 bit for each bit in the number (except possibly the sign bit). All
+known versions of dbx and GDB version 4 accept this (at least in the
+sense of not refusing to process the file), but GDB 3.5 refuses to read
+the whole file containing such symbols. So GCC 2.3.3 did not output the
+proper size for these types. As an example of octal bounds, the string
+fields of the stabs for 64 bit integer types look like:
+
+@c .stabs directives, etc., omitted to make it fit on the page.
+@example
+long int:t3=r1;001000000000000000000000;000777777777777777777777;
+long unsigned int:t5=r1;000000000000000000000000;001777777777777777777777;
+@end example
+
+If the lower bound of a subrange is 0 and the upper bound is negative,
+the type is an unsigned integral type whose size in bytes is the
+absolute value of the upper bound. I believe this is a Convex
+convention for @code{unsigned long long}.
+
+If the lower bound of a subrange is negative and the upper bound is 0,
+the type is a signed integral type whose size in bytes is
+the absolute value of the lower bound. I believe this is a Convex
+convention for @code{long long}. To distinguish this from a legitimate
+subrange, the type should be a subrange of itself. I'm not sure whether
+this is the case for Convex.
+
+@node Traditional Other Types
+@subsubsection Traditional Other Types
+
+If the upper bound of a subrange is 0 and the lower bound is positive,
+the type is a floating point type, and the lower bound of the subrange
+indicates the number of bytes in the type:
+
+@example
+.stabs "float:t12=r1;4;0;",128,0,0,0
+.stabs "double:t13=r1;8;0;",128,0,0,0
+@end example
+
+However, GCC writes @code{long double} the same way it writes
+@code{double}, so there is no way to distinguish.
+
+@example
+.stabs "long double:t14=r1;8;0;",128,0,0,0
+@end example
+
+Complex types are defined the same way as floating-point types; there is
+no way to distinguish a single-precision complex from a double-precision
+floating-point type.
+
+The C @code{void} type is defined as itself:
+
+@example
+.stabs "void:t15=15",128,0,0,0
+@end example
+
+I'm not sure how a boolean type is represented.
+
+@node Builtin Type Descriptors
+@subsection Defining Builtin Types Using Builtin Type Descriptors
+
+This is the method used by Sun's @code{acc} for defining builtin types.
+These are the type descriptors to define builtin types:
+
+@table @code
+@c FIXME: clean up description of width and offset, once we figure out
+@c what they mean
+@item b @var{signed} @var{char-flag} @var{width} ; @var{offset} ; @var{nbits} ;
+Define an integral type. @var{signed} is @samp{u} for unsigned or
+@samp{s} for signed. @var{char-flag} is @samp{c} which indicates this
+is a character type, or is omitted. I assume this is to distinguish an
+integral type from a character type of the same size, for example it
+might make sense to set it for the C type @code{wchar_t} so the debugger
+can print such variables differently (Solaris does not do this). Sun
+sets it on the C types @code{signed char} and @code{unsigned char} which
+arguably is wrong. @var{width} and @var{offset} appear to be for small
+objects stored in larger ones, for example a @code{short} in an
+@code{int} register. @var{width} is normally the number of bytes in the
+type. @var{offset} seems to always be zero. @var{nbits} is the number
+of bits in the type.
+
+Note that type descriptor @samp{b} used for builtin types conflicts with
+its use for Pascal space types (@pxref{Miscellaneous Types}); they can
+be distinguished because the character following the type descriptor
+will be a digit, @samp{(}, or @samp{-} for a Pascal space type, or
+@samp{u} or @samp{s} for a builtin type.
+
+@item w
+Documented by AIX to define a wide character type, but their compiler
+actually uses negative type numbers (@pxref{Negative Type Numbers}).
+
+@item R @var{fp-type} ; @var{bytes} ;
+Define a floating point type. @var{fp-type} has one of the following values:
+
+@table @code
+@item 1 (NF_SINGLE)
+IEEE 32-bit (single precision) floating point format.
+
+@item 2 (NF_DOUBLE)
+IEEE 64-bit (double precision) floating point format.
+
+@item 3 (NF_COMPLEX)
+@item 4 (NF_COMPLEX16)
+@item 5 (NF_COMPLEX32)
+@c "GDB source" really means @file{include/aout/stab_gnu.h}, but trying
+@c to put that here got an overfull hbox.
+These are for complex numbers. A comment in the GDB source describes
+them as Fortran @code{complex}, @code{double complex}, and
+@code{complex*16}, respectively, but what does that mean? (i.e., Single
+precision? Double precison?).
+
+@item 6 (NF_LDOUBLE)
+Long double. This should probably only be used for Sun format
+@code{long double}, and new codes should be used for other floating
+point formats (@code{NF_DOUBLE} can be used if a @code{long double} is
+really just an IEEE double, of course).
+@end table
+
+@var{bytes} is the number of bytes occupied by the type. This allows a
+debugger to perform some operations with the type even if it doesn't
+understand @var{fp-type}.
+
+@item g @var{type-information} ; @var{nbits}
+Documented by AIX to define a floating type, but their compiler actually
+uses negative type numbers (@pxref{Negative Type Numbers}).
+
+@item c @var{type-information} ; @var{nbits}
+Documented by AIX to define a complex type, but their compiler actually
+uses negative type numbers (@pxref{Negative Type Numbers}).
+@end table
+
+The C @code{void} type is defined as a signed integral type 0 bits long:
+@example
+.stabs "void:t19=bs0;0;0",128,0,0,0
+@end example
+The Solaris compiler seems to omit the trailing semicolon in this case.
+Getting sloppy in this way is not a swift move because if a type is
+embedded in a more complex expression it is necessary to be able to tell
+where it ends.
+
+I'm not sure how a boolean type is represented.
+
+@node Negative Type Numbers
+@subsection Negative Type Numbers
+
+This is the method used in XCOFF for defining builtin types.
+Since the debugger knows about the builtin types anyway, the idea of
+negative type numbers is simply to give a special type number which
+indicates the builtin type. There is no stab defining these types.
+
+There are several subtle issues with negative type numbers.
+
+One is the size of the type. A builtin type (for example the C types
+@code{int} or @code{long}) might have different sizes depending on
+compiler options, the target architecture, the ABI, etc. This issue
+doesn't come up for IBM tools since (so far) they just target the
+RS/6000; the sizes indicated below for each size are what the IBM
+RS/6000 tools use. To deal with differing sizes, either define separate
+negative type numbers for each size (which works but requires changing
+the debugger, and, unless you get both AIX dbx and GDB to accept the
+change, introduces an incompatibility), or use a type attribute
+(@pxref{String Field}) to define a new type with the appropriate size
+(which merely requires a debugger which understands type attributes,
+like AIX dbx). For example,
+
+@example
+.stabs "boolean:t10=@@s8;-16",128,0,0,0
+@end example
+
+defines an 8-bit boolean type, and
+
+@example
+.stabs "boolean:t10=@@s64;-16",128,0,0,0
+@end example
+
+defines a 64-bit boolean type.
+
+A similar issue is the format of the type. This comes up most often for
+floating-point types, which could have various formats (particularly
+extended doubles, which vary quite a bit even among IEEE systems).
+Again, it is best to define a new negative type number for each
+different format; changing the format based on the target system has
+various problems. One such problem is that the Alpha has both VAX and
+IEEE floating types. One can easily imagine one library using the VAX
+types and another library in the same executable using the IEEE types.
+Another example is that the interpretation of whether a boolean is true
+or false can be based on the least significant bit, most significant
+bit, whether it is zero, etc., and different compilers (or different
+options to the same compiler) might provide different kinds of boolean.
+
+The last major issue is the names of the types. The name of a given
+type depends @emph{only} on the negative type number given; these do not
+vary depending on the language, the target system, or anything else.
+One can always define separate type numbers---in the following list you
+will see for example separate @code{int} and @code{integer*4} types
+which are identical except for the name. But compatibility can be
+maintained by not inventing new negative type numbers and instead just
+defining a new type with a new name. For example:
+
+@example
+.stabs "CARDINAL:t10=-8",128,0,0,0
+@end example
+
+Here is the list of negative type numbers. The phrase @dfn{integral
+type} is used to mean twos-complement (I strongly suspect that all
+machines which use stabs use twos-complement; most machines use
+twos-complement these days).
+
+@table @code
+@item -1
+@code{int}, 32 bit signed integral type.
+
+@item -2
+@code{char}, 8 bit type holding a character. Both GDB and dbx on AIX
+treat this as signed. GCC uses this type whether @code{char} is signed
+or not, which seems like a bad idea. The AIX compiler (@code{xlc}) seems to
+avoid this type; it uses -5 instead for @code{char}.
+
+@item -3
+@code{short}, 16 bit signed integral type.
+
+@item -4
+@code{long}, 32 bit signed integral type.
+
+@item -5
+@code{unsigned char}, 8 bit unsigned integral type.
+
+@item -6
+@code{signed char}, 8 bit signed integral type.
+
+@item -7
+@code{unsigned short}, 16 bit unsigned integral type.
+
+@item -8
+@code{unsigned int}, 32 bit unsigned integral type.
+
+@item -9
+@code{unsigned}, 32 bit unsigned integral type.
+
+@item -10
+@code{unsigned long}, 32 bit unsigned integral type.
+
+@item -11
+@code{void}, type indicating the lack of a value.
+
+@item -12
+@code{float}, IEEE single precision.
+
+@item -13
+@code{double}, IEEE double precision.
+
+@item -14
+@code{long double}, IEEE double precision. The compiler claims the size
+will increase in a future release, and for binary compatibility you have
+to avoid using @code{long double}. I hope when they increase it they
+use a new negative type number.
+
+@item -15
+@code{integer}. 32 bit signed integral type.
+
+@item -16
+@code{boolean}. 32 bit type. How is the truth value encoded? Is it
+the least significant bit or is it a question of whether the whole value
+is zero or non-zero?
+
+@item -17
+@code{short real}. IEEE single precision.
+
+@item -18
+@code{real}. IEEE double precision.
+
+@item -19
+@code{stringptr}. @xref{Strings}.
+
+@item -20
+@code{character}, 8 bit unsigned character type.
+
+@item -21
+@code{logical*1}, 8 bit type. This Fortran type has a split
+personality in that it is used for boolean variables, but can also be
+used for unsigned integers. 0 is false, 1 is true, and other values are
+non-boolean.
+
+@item -22
+@code{logical*2}, 16 bit type. This Fortran type has a split
+personality in that it is used for boolean variables, but can also be
+used for unsigned integers. 0 is false, 1 is true, and other values are
+non-boolean.
+
+@item -23
+@code{logical*4}, 32 bit type. This Fortran type has a split
+personality in that it is used for boolean variables, but can also be
+used for unsigned integers. 0 is false, 1 is true, and other values are
+non-boolean.
+
+@item -24
+@code{logical}, 32 bit type. This Fortran type has a split
+personality in that it is used for boolean variables, but can also be
+used for unsigned integers. 0 is false, 1 is true, and other values are
+non-boolean.
+
+@item -25
+@code{complex}. A complex type consisting of two IEEE single-precision
+floating point values.
+
+@item -26
+@code{complex}. A complex type consisting of two IEEE double-precision
+floating point values.
+
+@item -27
+@code{integer*1}, 8 bit signed integral type.
+
+@item -28
+@code{integer*2}, 16 bit signed integral type.
+
+@item -29
+@code{integer*4}, 32 bit signed integral type.
+
+@item -30
+@code{wchar}. Wide character, 16 bits wide, unsigned (what format?
+Unicode?).
+@end table
+
+@node Miscellaneous Types
+@section Miscellaneous Types
+
+@table @code
+@item b @var{type-information} ; @var{bytes}
+Pascal space type. This is documented by IBM; what does it mean?
+
+This use of the @samp{b} type descriptor can be distinguished
+from its use for builtin integral types (@pxref{Builtin Type
+Descriptors}) because the character following the type descriptor is
+always a digit, @samp{(}, or @samp{-}.
+
+@item B @var{type-information}
+A volatile-qualified version of @var{type-information}. This is
+a Sun extension. References and stores to a variable with a
+volatile-qualified type must not be optimized or cached; they
+must occur as the user specifies them.
+
+@item d @var{type-information}
+File of type @var{type-information}. As far as I know this is only used
+by Pascal.
+
+@item k @var{type-information}
+A const-qualified version of @var{type-information}. This is a Sun
+extension. A variable with a const-qualified type cannot be modified.
+
+@item M @var{type-information} ; @var{length}
+Multiple instance type. The type seems to composed of @var{length}
+repetitions of @var{type-information}, for example @code{character*3} is
+represented by @samp{M-2;3}, where @samp{-2} is a reference to a
+character type (@pxref{Negative Type Numbers}). I'm not sure how this
+differs from an array. This appears to be a Fortran feature.
+@var{length} is a bound, like those in range types; see @ref{Subranges}.
+
+@item S @var{type-information}
+Pascal set type. @var{type-information} must be a small type such as an
+enumeration or a subrange, and the type is a bitmask whose length is
+specified by the number of elements in @var{type-information}.
+
+@item * @var{type-information}
+Pointer to @var{type-information}.
+@end table
+
+@node Cross-References
+@section Cross-References to Other Types
+
+A type can be used before it is defined; one common way to deal with
+that situation is just to use a type reference to a type which has not
+yet been defined.
+
+Another way is with the @samp{x} type descriptor, which is followed by
+@samp{s} for a structure tag, @samp{u} for a union tag, or @samp{e} for
+a enumerator tag, followed by the name of the tag, followed by @samp{:}.
+For example, the following C declarations:
+
+@example
+struct foo;
+struct foo *bar;
+@end example
+
+@noindent
+produce:
+
+@example
+.stabs "bar:G16=*17=xsfoo:",32,0,0,0
+@end example
+
+Not all debuggers support the @samp{x} type descriptor, so on some
+machines GCC does not use it. I believe that for the above example it
+would just emit a reference to type 17 and never define it, but I
+haven't verified that.
+
+Modula-2 imported types, at least on AIX, use the @samp{i} type
+descriptor, which is followed by the name of the module from which the
+type is imported, followed by @samp{:}, followed by the name of the
+type. There is then optionally a comma followed by type information for
+the type. This differs from merely naming the type (@pxref{Typedefs}) in
+that it identifies the module; I don't understand whether the name of
+the type given here is always just the same as the name we are giving
+it, or whether this type descriptor is used with a nameless stab
+(@pxref{String Field}), or what. The symbol ends with @samp{;}.
+
+@node Subranges
+@section Subrange Types
+
+The @samp{r} type descriptor defines a type as a subrange of another
+type. It is followed by type information for the type of which it is a
+subrange, a semicolon, an integral lower bound, a semicolon, an
+integral upper bound, and a semicolon. The AIX documentation does not
+specify the trailing semicolon, in an effort to specify array indexes
+more cleanly, but a subrange which is not an array index has always
+included a trailing semicolon (@pxref{Arrays}).
+
+Instead of an integer, either bound can be one of the following:
+
+@table @code
+@item A @var{offset}
+The bound is passed by reference on the stack at offset @var{offset}
+from the argument list. @xref{Parameters}, for more information on such
+offsets.
+
+@item T @var{offset}
+The bound is passed by value on the stack at offset @var{offset} from
+the argument list.
+
+@item a @var{register-number}
+The bound is pased by reference in register number
+@var{register-number}.
+
+@item t @var{register-number}
+The bound is passed by value in register number @var{register-number}.
+
+@item J
+There is no bound.
+@end table
+
+Subranges are also used for builtin types; see @ref{Traditional Builtin Types}.
+
+@node Arrays
+@section Array Types
+
+Arrays use the @samp{a} type descriptor. Following the type descriptor
+is the type of the index and the type of the array elements. If the
+index type is a range type, it ends in a semicolon; otherwise
+(for example, if it is a type reference), there does not
+appear to be any way to tell where the types are separated. In an
+effort to clean up this mess, IBM documents the two types as being
+separated by a semicolon, and a range type as not ending in a semicolon
+(but this is not right for range types which are not array indexes,
+@pxref{Subranges}). I think probably the best solution is to specify
+that a semicolon ends a range type, and that the index type and element
+type of an array are separated by a semicolon, but that if the index
+type is a range type, the extra semicolon can be omitted. GDB (at least
+through version 4.9) doesn't support any kind of index type other than a
+range anyway; I'm not sure about dbx.
+
+It is well established, and widely used, that the type of the index,
+unlike most types found in the stabs, is merely a type definition, not
+type information (@pxref{String Field}) (that is, it need not start with
+@samp{@var{type-number}=} if it is defining a new type). According to a
+comment in GDB, this is also true of the type of the array elements; it
+gives @samp{ar1;1;10;ar1;1;10;4} as a legitimate way to express a two
+dimensional array. According to AIX documentation, the element type
+must be type information. GDB accepts either.
+
+The type of the index is often a range type, expressed as the type
+descriptor @samp{r} and some parameters. It defines the size of the
+array. In the example below, the range @samp{r1;0;2;} defines an index
+type which is a subrange of type 1 (integer), with a lower bound of 0
+and an upper bound of 2. This defines the valid range of subscripts of
+a three-element C array.
+
+For example, the definition:
+
+@example
+char char_vec[3] = @{'a','b','c'@};
+@end example
+
+@noindent
+produces the output:
+
+@example
+.stabs "char_vec:G19=ar1;0;2;2",32,0,0,0
+ .global _char_vec
+ .align 4
+_char_vec:
+ .byte 97
+ .byte 98
+ .byte 99
+@end example
+
+If an array is @dfn{packed}, the elements are spaced more
+closely than normal, saving memory at the expense of speed. For
+example, an array of 3-byte objects might, if unpacked, have each
+element aligned on a 4-byte boundary, but if packed, have no padding.
+One way to specify that something is packed is with type attributes
+(@pxref{String Field}). In the case of arrays, another is to use the
+@samp{P} type descriptor instead of @samp{a}. Other than specifying a
+packed array, @samp{P} is identical to @samp{a}.
+
+@c FIXME-what is it? A pointer?
+An open array is represented by the @samp{A} type descriptor followed by
+type information specifying the type of the array elements.
+
+@c FIXME: what is the format of this type? A pointer to a vector of pointers?
+An N-dimensional dynamic array is represented by
+
+@example
+D @var{dimensions} ; @var{type-information}
+@end example
+
+@c Does dimensions really have this meaning? The AIX documentation
+@c doesn't say.
+@var{dimensions} is the number of dimensions; @var{type-information}
+specifies the type of the array elements.
+
+@c FIXME: what is the format of this type? A pointer to some offsets in
+@c another array?
+A subarray of an N-dimensional array is represented by
+
+@example
+E @var{dimensions} ; @var{type-information}
+@end example
+
+@c Does dimensions really have this meaning? The AIX documentation
+@c doesn't say.
+@var{dimensions} is the number of dimensions; @var{type-information}
+specifies the type of the array elements.
+
+@node Strings
+@section Strings
+
+Some languages, like C or the original Pascal, do not have string types,
+they just have related things like arrays of characters. But most
+Pascals and various other languages have string types, which are
+indicated as follows:
+
+@table @code
+@item n @var{type-information} ; @var{bytes}
+@var{bytes} is the maximum length. I'm not sure what
+@var{type-information} is; I suspect that it means that this is a string
+of @var{type-information} (thus allowing a string of integers, a string
+of wide characters, etc., as well as a string of characters). Not sure
+what the format of this type is. This is an AIX feature.
+
+@item z @var{type-information} ; @var{bytes}
+Just like @samp{n} except that this is a gstring, not an ordinary
+string. I don't know the difference.
+
+@item N
+Pascal Stringptr. What is this? This is an AIX feature.
+@end table
+
+@node Enumerations
+@section Enumerations
+
+Enumerations are defined with the @samp{e} type descriptor.
+
+@c FIXME: Where does this information properly go? Perhaps it is
+@c redundant with something we already explain.
+The source line below declares an enumeration type at file scope.
+The type definition is located after the @code{N_RBRAC} that marks the end of
+the previous procedure's block scope, and before the @code{N_FUN} that marks
+the beginning of the next procedure's block scope. Therefore it does not
+describe a block local symbol, but a file local one.
+
+The source line:
+
+@example
+enum e_places @{first,second=3,last@};
+@end example
+
+@noindent
+generates the following stab:
+
+@example
+.stabs "e_places:T22=efirst:0,second:3,last:4,;",128,0,0,0
+@end example
+
+The symbol descriptor (@samp{T}) says that the stab describes a
+structure, enumeration, or union tag. The type descriptor @samp{e},
+following the @samp{22=} of the type definition narrows it down to an
+enumeration type. Following the @samp{e} is a list of the elements of
+the enumeration. The format is @samp{@var{name}:@var{value},}. The
+list of elements ends with @samp{;}.
+
+There is no standard way to specify the size of an enumeration type; it
+is determined by the architecture (normally all enumerations types are
+32 bits). There should be a way to specify an enumeration type of
+another size; type attributes would be one way to do this. @xref{Stabs
+Format}.
+
+@node Structures
+@section Structures
+
+The encoding of structures in stabs can be shown with an example.
+
+The following source code declares a structure tag and defines an
+instance of the structure in global scope. Then a @code{typedef} equates the
+structure tag with a new type. Seperate stabs are generated for the
+structure tag, the structure @code{typedef}, and the structure instance. The
+stabs for the tag and the @code{typedef} are emited when the definitions are
+encountered. Since the structure elements are not initialized, the
+stab and code for the structure variable itself is located at the end
+of the program in the bss section.
+
+@example
+struct s_tag @{
+ int s_int;
+ float s_float;
+ char s_char_vec[8];
+ struct s_tag* s_next;
+@} g_an_s;
+
+typedef struct s_tag s_typedef;
+@end example
+
+The structure tag has an @code{N_LSYM} stab type because, like the
+enumeration, the symbol has file scope. Like the enumeration, the
+symbol descriptor is @samp{T}, for enumeration, structure, or tag type.
+The type descriptor @samp{s} following the @samp{16=} of the type
+definition narrows the symbol type to structure.
+
+Following the @samp{s} type descriptor is the number of bytes the
+structure occupies, followed by a description of each structure element.
+The structure element descriptions are of the form @var{name:type, bit
+offset from the start of the struct, number of bits in the element}.
+
+@c FIXME: phony line break. Can probably be fixed by using an example
+@c with fewer fields.
+@example
+# @r{128 is N_LSYM}
+.stabs "s_tag:T16=s20s_int:1,0,32;s_float:12,32,32;
+ s_char_vec:17=ar1;0;7;2,64,64;s_next:18=*16,128,32;;",128,0,0,0
+@end example
+
+In this example, the first two structure elements are previously defined
+types. For these, the type following the @samp{@var{name}:} part of the
+element description is a simple type reference. The other two structure
+elements are new types. In this case there is a type definition
+embedded after the @samp{@var{name}:}. The type definition for the
+array element looks just like a type definition for a standalone array.
+The @code{s_next} field is a pointer to the same kind of structure that
+the field is an element of. So the definition of structure type 16
+contains a type definition for an element which is a pointer to type 16.
+
+@node Typedefs
+@section Giving a Type a Name
+
+To give a type a name, use the @samp{t} symbol descriptor. The type
+is specified by the type information (@pxref{String Field}) for the stab.
+For example,
+
+@example
+.stabs "s_typedef:t16",128,0,0,0 # @r{128 is N_LSYM}
+@end example
+
+specifies that @code{s_typedef} refers to type number 16. Such stabs
+have symbol type @code{N_LSYM} (or @code{C_DECL} for XCOFF).
+
+If you are specifying the tag name for a structure, union, or
+enumeration, use the @samp{T} symbol descriptor instead. I believe C is
+the only language with this feature.
+
+If the type is an opaque type (I believe this is a Modula-2 feature),
+AIX provides a type descriptor to specify it. The type descriptor is
+@samp{o} and is followed by a name. I don't know what the name
+means---is it always the same as the name of the type, or is this type
+descriptor used with a nameless stab (@pxref{String Field})? There
+optionally follows a comma followed by type information which defines
+the type of this type. If omitted, a semicolon is used in place of the
+comma and the type information, and the type is much like a generic
+pointer type---it has a known size but little else about it is
+specified.
+
+@node Unions
+@section Unions
+
+@example
+union u_tag @{
+ int u_int;
+ float u_float;
+ char* u_char;
+@} an_u;
+@end example
+
+This code generates a stab for a union tag and a stab for a union
+variable. Both use the @code{N_LSYM} stab type. If a union variable is
+scoped locally to the procedure in which it is defined, its stab is
+located immediately preceding the @code{N_LBRAC} for the procedure's block
+start.
+
+The stab for the union tag, however, is located preceding the code for
+the procedure in which it is defined. The stab type is @code{N_LSYM}. This
+would seem to imply that the union type is file scope, like the struct
+type @code{s_tag}. This is not true. The contents and position of the stab
+for @code{u_type} do not convey any infomation about its procedure local
+scope.
+
+@c FIXME: phony line break. Can probably be fixed by using an example
+@c with fewer fields.
+@smallexample
+# @r{128 is N_LSYM}
+.stabs "u_tag:T23=u4u_int:1,0,32;u_float:12,0,32;u_char:21,0,32;;",
+ 128,0,0,0
+@end smallexample
+
+The symbol descriptor @samp{T}, following the @samp{name:} means that
+the stab describes an enumeration, structure, or union tag. The type
+descriptor @samp{u}, following the @samp{23=} of the type definition,
+narrows it down to a union type definition. Following the @samp{u} is
+the number of bytes in the union. After that is a list of union element
+descriptions. Their format is @var{name:type, bit offset into the
+union, number of bytes for the element;}.
+
+The stab for the union variable is:
+
+@example
+.stabs "an_u:23",128,0,0,-20 # @r{128 is N_LSYM}
+@end example
+
+@samp{-20} specifies where the variable is stored (@pxref{Stack
+Variables}).
+
+@node Function Types
+@section Function Types
+
+Various types can be defined for function variables. These types are
+not used in defining functions (@pxref{Procedures}); they are used for
+things like pointers to functions.
+
+The simple, traditional, type is type descriptor @samp{f} is followed by
+type information for the return type of the function, followed by a
+semicolon.
+
+This does not deal with functions for which the number and types of the
+parameters are part of the type, as in Modula-2 or ANSI C. AIX provides
+extensions to specify these, using the @samp{f}, @samp{F}, @samp{p}, and
+@samp{R} type descriptors.
+
+First comes the type descriptor. If it is @samp{f} or @samp{F}, this
+type involves a function rather than a procedure, and the type
+information for the return type of the function follows, followed by a
+comma. Then comes the number of parameters to the function and a
+semicolon. Then, for each parameter, there is the name of the parameter
+followed by a colon (this is only present for type descriptors @samp{R}
+and @samp{F} which represent Pascal function or procedure parameters),
+type information for the parameter, a comma, 0 if passed by reference or
+1 if passed by value, and a semicolon. The type definition ends with a
+semicolon.
+
+For example, this variable definition:
+
+@example
+int (*g_pf)();
+@end example
+
+@noindent
+generates the following code:
+
+@example
+.stabs "g_pf:G24=*25=f1",32,0,0,0
+ .common _g_pf,4,"bss"
+@end example
+
+The variable defines a new type, 24, which is a pointer to another new
+type, 25, which is a function returning @code{int}.
+
+@node Symbol Tables
+@chapter Symbol Information in Symbol Tables
+
+This chapter describes the format of symbol table entries
+and how stab assembler directives map to them. It also describes the
+transformations that the assembler and linker make on data from stabs.
+
+@menu
+* Symbol Table Format::
+* Transformations On Symbol Tables::
+@end menu
+
+@node Symbol Table Format
+@section Symbol Table Format
+
+Each time the assembler encounters a stab directive, it puts
+each field of the stab into a corresponding field in a symbol table
+entry of its output file. If the stab contains a string field, the
+symbol table entry for that stab points to a string table entry
+containing the string data from the stab. Assembler labels become
+relocatable addresses. Symbol table entries in a.out have the format:
+
+@c FIXME: should refer to external, not internal.
+@example
+struct internal_nlist @{
+ unsigned long n_strx; /* index into string table of name */
+ unsigned char n_type; /* type of symbol */
+ unsigned char n_other; /* misc info (usually empty) */
+ unsigned short n_desc; /* description field */
+ bfd_vma n_value; /* value of symbol */
+@};
+@end example
+
+If the stab has a string, the @code{n_strx} field holds the offset in
+bytes of the string within the string table. The string is terminated
+by a NUL character. If the stab lacks a string (for example, it was
+produced by a @code{.stabn} or @code{.stabd} directive), the
+@code{n_strx} field is zero.
+
+Symbol table entries with @code{n_type} field values greater than 0x1f
+originated as stabs generated by the compiler (with one random
+exception). The other entries were placed in the symbol table of the
+executable by the assembler or the linker.
+
+@node Transformations On Symbol Tables
+@section Transformations on Symbol Tables
+
+The linker concatenates object files and does fixups of externally
+defined symbols.
+
+You can see the transformations made on stab data by the assembler and
+linker by examining the symbol table after each pass of the build. To
+do this, use @samp{nm -ap}, which dumps the symbol table, including
+debugging information, unsorted. For stab entries the columns are:
+@var{value}, @var{other}, @var{desc}, @var{type}, @var{string}. For
+assembler and linker symbols, the columns are: @var{value}, @var{type},
+@var{string}.
+
+The low 5 bits of the stab type tell the linker how to relocate the
+value of the stab. Thus for stab types like @code{N_RSYM} and
+@code{N_LSYM}, where the value is an offset or a register number, the
+low 5 bits are @code{N_ABS}, which tells the linker not to relocate the
+value.
+
+Where the value of a stab contains an assembly language label,
+it is transformed by each build step. The assembler turns it into a
+relocatable address and the linker turns it into an absolute address.
+
+@menu
+* Transformations On Static Variables::
+* Transformations On Global Variables::
+* ELF Transformations:: In ELF, things are a bit different.
+@end menu
+
+@node Transformations On Static Variables
+@subsection Transformations on Static Variables
+
+This source line defines a static variable at file scope:
+
+@example
+static int s_g_repeat
+@end example
+
+@noindent
+The following stab describes the symbol:
+
+@example
+.stabs "s_g_repeat:S1",38,0,0,_s_g_repeat
+@end example
+
+@noindent
+The assembler transforms the stab into this symbol table entry in the
+@file{.o} file. The location is expressed as a data segment offset.
+
+@example
+00000084 - 00 0000 STSYM s_g_repeat:S1
+@end example
+
+@noindent
+In the symbol table entry from the executable, the linker has made the
+relocatable address absolute.
+
+@example
+0000e00c - 00 0000 STSYM s_g_repeat:S1
+@end example
+
+@node Transformations On Global Variables
+@subsection Transformations on Global Variables
+
+Stabs for global variables do not contain location information. In
+this case, the debugger finds location information in the assembler or
+linker symbol table entry describing the variable. The source line:
+
+@example
+char g_foo = 'c';
+@end example
+
+@noindent
+generates the stab:
+
+@example
+.stabs "g_foo:G2",32,0,0,0
+@end example
+
+The variable is represented by two symbol table entries in the object
+file (see below). The first one originated as a stab. The second one
+is an external symbol. The upper case @samp{D} signifies that the
+@code{n_type} field of the symbol table contains 7, @code{N_DATA} with
+local linkage. The stab's value is zero since the value is not used for
+@code{N_GSYM} stabs. The value of the linker symbol is the relocatable
+address corresponding to the variable.
+
+@example
+00000000 - 00 0000 GSYM g_foo:G2
+00000080 D _g_foo
+@end example
+
+@noindent
+These entries as transformed by the linker. The linker symbol table
+entry now holds an absolute address:
+
+@example
+00000000 - 00 0000 GSYM g_foo:G2
+@dots{}
+0000e008 D _g_foo
+@end example
+
+@node ELF Transformations
+@subsection Transformations of Stabs in ELF Files
+
+For ELF files, use @code{objdump --stabs} instead of @code{nm} to show
+the stabs in an object or executable file. @code{objdump} is a GNU
+utility; Sun does not provide any equivalent.
+
+The following example is for a stab whose value is an address is
+relative to the compilation unit (@pxref{Stabs In ELF}). For example,
+if the source line
+
+@example
+static int ld = 5;
+@end example
+
+appears within a function, then the assembly language output from the
+compiler contains:
+
+@example
+.Ddata.data:
+@dots{}
+ .stabs "ld:V(0,3)",0x26,0,4,.L18-Ddata.data # @r{0x26 is N_STSYM}
+@dots{}
+.L18:
+ .align 4
+ .word 0x5
+@end example
+
+Because the value is formed by subtracting one symbol from another, the
+value is absolute, not relocatable, and so the object file contains
+
+@example
+Symnum n_type n_othr n_desc n_value n_strx String
+31 STSYM 0 4 00000004 680 ld:V(0,3)
+@end example
+
+without any relocations, and the executable file also contains
+
+@example
+Symnum n_type n_othr n_desc n_value n_strx String
+31 STSYM 0 4 00000004 680 ld:V(0,3)
+@end example
+
+@node Cplusplus
+@chapter GNU C++ Stabs
+
+@menu
+* Basic Cplusplus Types::
+* Simple Classes::
+* Class Instance::
+* Methods:: Method definition
+* Protections::
+* Method Modifiers::
+* Virtual Methods::
+* Inheritence::
+* Virtual Base Classes::
+* Static Members::
+@end menu
+
+Type descriptors added for C++ descriptions:
+
+@table @code
+@item #
+method type (@code{##} if minimal debug)
+
+@item @@
+Member (class and variable) type. It is followed by type information
+for the offset basetype, a comma, and type information for the type of
+the field being pointed to. (FIXME: this is acknowledged to be
+gibberish. Can anyone say what really goes here?).
+
+Note that there is a conflict between this and type attributes
+(@pxref{String Field}); both use type descriptor @samp{@@}.
+Fortunately, the @samp{@@} type descriptor used in this C++ sense always
+will be followed by a digit, @samp{(}, or @samp{-}, and type attributes
+never start with those things.
+@end table
+
+@node Basic Cplusplus Types
+@section Basic Types For C++
+
+<< the examples that follow are based on a01.C >>
+
+
+C++ adds two more builtin types to the set defined for C. These are
+the unknown type and the vtable record type. The unknown type, type
+16, is defined in terms of itself like the void type.
+
+The vtable record type, type 17, is defined as a structure type and
+then as a structure tag. The structure has four fields: delta, index,
+pfn, and delta2. pfn is the function pointer.
+
+<< In boilerplate $vtbl_ptr_type, what are the fields delta,
+index, and delta2 used for? >>
+
+This basic type is present in all C++ programs even if there are no
+virtual methods defined.
+
+@display
+.stabs "struct_name:sym_desc(type)type_def(17)=type_desc(struct)struct_bytes(8)
+ elem_name(delta):type_ref(short int),bit_offset(0),field_bits(16);
+ elem_name(index):type_ref(short int),bit_offset(16),field_bits(16);
+ elem_name(pfn):type_def(18)=type_desc(ptr to)type_ref(void),
+ bit_offset(32),field_bits(32);
+ elem_name(delta2):type_def(short int);bit_offset(32),field_bits(16);;"
+ N_LSYM, NIL, NIL
+@end display
+
+@smallexample
+.stabs "$vtbl_ptr_type:t17=s8
+ delta:6,0,16;index:6,16,16;pfn:18=*15,32,32;delta2:6,32,16;;"
+ ,128,0,0,0
+@end smallexample
+
+@display
+.stabs "name:sym_dec(struct tag)type_ref($vtbl_ptr_type)",N_LSYM,NIL,NIL,NIL
+@end display
+
+@example
+.stabs "$vtbl_ptr_type:T17",128,0,0,0
+@end example
+
+@node Simple Classes
+@section Simple Class Definition
+
+The stabs describing C++ language features are an extension of the
+stabs describing C. Stabs representing C++ class types elaborate
+extensively on the stab format used to describe structure types in C.
+Stabs representing class type variables look just like stabs
+representing C language variables.
+
+Consider the following very simple class definition.
+
+@example
+class baseA @{
+public:
+ int Adat;
+ int Ameth(int in, char other);
+@};
+@end example
+
+The class @code{baseA} is represented by two stabs. The first stab describes
+the class as a structure type. The second stab describes a structure
+tag of the class type. Both stabs are of stab type @code{N_LSYM}. Since the
+stab is not located between an @code{N_FUN} and an @code{N_LBRAC} stab this indicates
+that the class is defined at file scope. If it were, then the @code{N_LSYM}
+would signify a local variable.
+
+A stab describing a C++ class type is similar in format to a stab
+describing a C struct, with each class member shown as a field in the
+structure. The part of the struct format describing fields is
+expanded to include extra information relevent to C++ class members.
+In addition, if the class has multiple base classes or virtual
+functions the struct format outside of the field parts is also
+augmented.
+
+In this simple example the field part of the C++ class stab
+representing member data looks just like the field part of a C struct
+stab. The section on protections describes how its format is
+sometimes extended for member data.
+
+The field part of a C++ class stab representing a member function
+differs substantially from the field part of a C struct stab. It
+still begins with @samp{name:} but then goes on to define a new type number
+for the member function, describe its return type, its argument types,
+its protection level, any qualifiers applied to the method definition,
+and whether the method is virtual or not. If the method is virtual
+then the method description goes on to give the vtable index of the
+method, and the type number of the first base class defining the
+method.
+
+When the field name is a method name it is followed by two colons rather
+than one. This is followed by a new type definition for the method.
+This is a number followed by an equal sign and the type descriptor
+@samp{#}, indicating a method type, and a second @samp{#}, indicating
+that this is the @dfn{minimal} type of method definition used by GCC2,
+not larger method definitions used by earlier versions of GCC. This is
+followed by a type reference showing the return type of the method and a
+semi-colon.
+
+The format of an overloaded operator method name differs from that of
+other methods. It is @samp{op$::@var{operator-name}.} where
+@var{operator-name} is the operator name such as @samp{+} or @samp{+=}.
+The name ends with a period, and any characters except the period can
+occur in the @var{operator-name} string.
+
+The next part of the method description represents the arguments to the
+method, preceeded by a colon and ending with a semi-colon. The types of
+the arguments are expressed in the same way argument types are expressed
+in C++ name mangling. In this example an @code{int} and a @code{char}
+map to @samp{ic}.
+
+This is followed by a number, a letter, and an asterisk or period,
+followed by another semicolon. The number indicates the protections
+that apply to the member function. Here the 2 means public. The
+letter encodes any qualifier applied to the method definition. In
+this case, @samp{A} means that it is a normal function definition. The dot
+shows that the method is not virtual. The sections that follow
+elaborate further on these fields and describe the additional
+information present for virtual methods.
+
+
+@display
+.stabs "class_name:sym_desc(type)type_def(20)=type_desc(struct)struct_bytes(4)
+ field_name(Adat):type(int),bit_offset(0),field_bits(32);
+
+ method_name(Ameth)::type_def(21)=type_desc(method)return_type(int);
+ :arg_types(int char);
+ protection(public)qualifier(normal)virtual(no);;"
+ N_LSYM,NIL,NIL,NIL
+@end display
+
+@smallexample
+.stabs "baseA:t20=s4Adat:1,0,32;Ameth::21=##1;:ic;2A.;;",128,0,0,0
+
+.stabs "class_name:sym_desc(struct tag)",N_LSYM,NIL,NIL,NIL
+
+.stabs "baseA:T20",128,0,0,0
+@end smallexample
+
+@node Class Instance
+@section Class Instance
+
+As shown above, describing even a simple C++ class definition is
+accomplished by massively extending the stab format used in C to
+describe structure types. However, once the class is defined, C stabs
+with no modifications can be used to describe class instances. The
+following source:
+
+@example
+main () @{
+ baseA AbaseA;
+@}
+@end example
+
+@noindent
+yields the following stab describing the class instance. It looks no
+different from a standard C stab describing a local variable.
+
+@display
+.stabs "name:type_ref(baseA)", N_LSYM, NIL, NIL, frame_ptr_offset
+@end display
+
+@example
+.stabs "AbaseA:20",128,0,0,-20
+@end example
+
+@node Methods
+@section Method Definition
+
+The class definition shown above declares Ameth. The C++ source below
+defines Ameth:
+
+@example
+int
+baseA::Ameth(int in, char other)
+@{
+ return in;
+@};
+@end example
+
+
+This method definition yields three stabs following the code of the
+method. One stab describes the method itself and following two describe
+its parameters. Although there is only one formal argument all methods
+have an implicit argument which is the @code{this} pointer. The @code{this}
+pointer is a pointer to the object on which the method was called. Note
+that the method name is mangled to encode the class name and argument
+types. Name mangling is described in the @sc{arm} (@cite{The Annotated
+C++ Reference Manual}, by Ellis and Stroustrup, @sc{isbn}
+0-201-51459-1); @file{gpcompare.texi} in Cygnus GCC distributions
+describes the differences between GNU mangling and @sc{arm}
+mangling.
+@c FIXME: Use @xref, especially if this is generally installed in the
+@c info tree.
+@c FIXME: This information should be in a net release, either of GCC or
+@c GDB. But gpcompare.texi doesn't seem to be in the FSF GCC.
+
+@example
+.stabs "name:symbol_desriptor(global function)return_type(int)",
+ N_FUN, NIL, NIL, code_addr_of_method_start
+
+.stabs "Ameth__5baseAic:F1",36,0,0,_Ameth__5baseAic
+@end example
+
+Here is the stab for the @code{this} pointer implicit argument. The
+name of the @code{this} pointer is always @code{this}. Type 19, the
+@code{this} pointer is defined as a pointer to type 20, @code{baseA},
+but a stab defining @code{baseA} has not yet been emited. Since the
+compiler knows it will be emited shortly, here it just outputs a cross
+reference to the undefined symbol, by prefixing the symbol name with
+@samp{xs}.
+
+@example
+.stabs "name:sym_desc(register param)type_def(19)=
+ type_desc(ptr to)type_ref(baseA)=
+ type_desc(cross-reference to)baseA:",N_RSYM,NIL,NIL,register_number
+
+.stabs "this:P19=*20=xsbaseA:",64,0,0,8
+@end example
+
+The stab for the explicit integer argument looks just like a parameter
+to a C function. The last field of the stab is the offset from the
+argument pointer, which in most systems is the same as the frame
+pointer.
+
+@example
+.stabs "name:sym_desc(value parameter)type_ref(int)",
+ N_PSYM,NIL,NIL,offset_from_arg_ptr
+
+.stabs "in:p1",160,0,0,72
+@end example
+
+<< The examples that follow are based on A1.C >>
+
+@node Protections
+@section Protections
+
+
+In the simple class definition shown above all member data and
+functions were publicly accessable. The example that follows
+contrasts public, protected and privately accessable fields and shows
+how these protections are encoded in C++ stabs.
+
+@c FIXME: What does "part of the string" mean?
+Protections for class member data are signified by two characters
+embedded in the stab defining the class type. These characters are
+located after the name: part of the string. @samp{/0} means private,
+@samp{/1} means protected, and @samp{/2} means public. If these
+characters are omited this means that the member is public. The
+following C++ source:
+
+@example
+class all_data @{
+private:
+ int priv_dat;
+protected:
+ char prot_dat;
+public:
+ float pub_dat;
+@};
+@end example
+
+@noindent
+generates the following stab to describe the class type all_data.
+
+@display
+.stabs "class_name:sym_desc(type)type_def(19)=type_desc(struct)struct_bytes
+ data_name:/protection(private)type_ref(int),bit_offset,num_bits;
+ data_name:/protection(protected)type_ref(char),bit_offset,num_bits;
+ data_name:(/num omited, private)type_ref(float),bit_offset,num_bits;;"
+ N_LSYM,NIL,NIL,NIL
+@end display
+
+@smallexample
+.stabs "all_data:t19=s12
+ priv_dat:/01,0,32;prot_dat:/12,32,8;pub_dat:12,64,32;;",128,0,0,0
+@end smallexample
+
+Protections for member functions are signified by one digit embeded in
+the field part of the stab describing the method. The digit is 0 if
+private, 1 if protected and 2 if public. Consider the C++ class
+definition below:
+
+@example
+class all_methods @{
+private:
+ int priv_meth(int in)@{return in;@};
+protected:
+ char protMeth(char in)@{return in;@};
+public:
+ float pubMeth(float in)@{return in;@};
+@};
+@end example
+
+It generates the following stab. The digit in question is to the left
+of an @samp{A} in each case. Notice also that in this case two symbol
+descriptors apply to the class name struct tag and struct type.
+
+@display
+.stabs "class_name:sym_desc(struct tag&type)type_def(21)=
+ sym_desc(struct)struct_bytes(1)
+ meth_name::type_def(22)=sym_desc(method)returning(int);
+ :args(int);protection(private)modifier(normal)virtual(no);
+ meth_name::type_def(23)=sym_desc(method)returning(char);
+ :args(char);protection(protected)modifier(normal)virual(no);
+ meth_name::type_def(24)=sym_desc(method)returning(float);
+ :args(float);protection(public)modifier(normal)virtual(no);;",
+ N_LSYM,NIL,NIL,NIL
+@end display
+
+@smallexample
+.stabs "all_methods:Tt21=s1priv_meth::22=##1;:i;0A.;protMeth::23=##2;:c;1A.;
+ pubMeth::24=##12;:f;2A.;;",128,0,0,0
+@end smallexample
+
+@node Method Modifiers
+@section Method Modifiers (@code{const}, @code{volatile}, @code{const volatile})
+
+<< based on a6.C >>
+
+In the class example described above all the methods have the normal
+modifier. This method modifier information is located just after the
+protection information for the method. This field has four possible
+character values. Normal methods use @samp{A}, const methods use
+@samp{B}, volatile methods use @samp{C}, and const volatile methods use
+@samp{D}. Consider the class definition below:
+
+@example
+class A @{
+public:
+ int ConstMeth (int arg) const @{ return arg; @};
+ char VolatileMeth (char arg) volatile @{ return arg; @};
+ float ConstVolMeth (float arg) const volatile @{return arg; @};
+@};
+@end example
+
+This class is described by the following stab:
+
+@display
+.stabs "class(A):sym_desc(struct)type_def(20)=type_desc(struct)struct_bytes(1)
+ meth_name(ConstMeth)::type_def(21)sym_desc(method)
+ returning(int);:arg(int);protection(public)modifier(const)virtual(no);
+ meth_name(VolatileMeth)::type_def(22)=sym_desc(method)
+ returning(char);:arg(char);protection(public)modifier(volatile)virt(no)
+ meth_name(ConstVolMeth)::type_def(23)=sym_desc(method)
+ returning(float);:arg(float);protection(public)modifer(const volatile)
+ virtual(no);;", @dots{}
+@end display
+
+@example
+.stabs "A:T20=s1ConstMeth::21=##1;:i;2B.;VolatileMeth::22=##2;:c;2C.;
+ ConstVolMeth::23=##12;:f;2D.;;",128,0,0,0
+@end example
+
+@node Virtual Methods
+@section Virtual Methods
+
+<< The following examples are based on a4.C >>
+
+The presence of virtual methods in a class definition adds additional
+data to the class description. The extra data is appended to the
+description of the virtual method and to the end of the class
+description. Consider the class definition below:
+
+@example
+class A @{
+public:
+ int Adat;
+ virtual int A_virt (int arg) @{ return arg; @};
+@};
+@end example
+
+This results in the stab below describing class A. It defines a new
+type (20) which is an 8 byte structure. The first field of the class
+struct is @samp{Adat}, an integer, starting at structure offset 0 and
+occupying 32 bits.
+
+The second field in the class struct is not explicitly defined by the
+C++ class definition but is implied by the fact that the class
+contains a virtual method. This field is the vtable pointer. The
+name of the vtable pointer field starts with @samp{$vf} and continues with a
+type reference to the class it is part of. In this example the type
+reference for class A is 20 so the name of its vtable pointer field is
+@samp{$vf20}, followed by the usual colon.
+
+Next there is a type definition for the vtable pointer type (21).
+This is in turn defined as a pointer to another new type (22).
+
+Type 22 is the vtable itself, which is defined as an array, indexed by
+a range of integers between 0 and 1, and whose elements are of type
+17. Type 17 was the vtable record type defined by the boilerplate C++
+type definitions, as shown earlier.
+
+The bit offset of the vtable pointer field is 32. The number of bits
+in the field are not specified when the field is a vtable pointer.
+
+Next is the method definition for the virtual member function @code{A_virt}.
+Its description starts out using the same format as the non-virtual
+member functions described above, except instead of a dot after the
+@samp{A} there is an asterisk, indicating that the function is virtual.
+Since is is virtual some addition information is appended to the end
+of the method description.
+
+The first number represents the vtable index of the method. This is a
+32 bit unsigned number with the high bit set, followed by a
+semi-colon.
+
+The second number is a type reference to the first base class in the
+inheritence hierarchy defining the virtual member function. In this
+case the class stab describes a base class so the virtual function is
+not overriding any other definition of the method. Therefore the
+reference is to the type number of the class that the stab is
+describing (20).
+
+This is followed by three semi-colons. One marks the end of the
+current sub-section, one marks the end of the method field, and the
+third marks the end of the struct definition.
+
+For classes containing virtual functions the very last section of the
+string part of the stab holds a type reference to the first base
+class. This is preceeded by @samp{~%} and followed by a final semi-colon.
+
+@display
+.stabs "class_name(A):type_def(20)=sym_desc(struct)struct_bytes(8)
+ field_name(Adat):type_ref(int),bit_offset(0),field_bits(32);
+ field_name(A virt func ptr):type_def(21)=type_desc(ptr to)type_def(22)=
+ sym_desc(array)index_type_ref(range of int from 0 to 1);
+ elem_type_ref(vtbl elem type),
+ bit_offset(32);
+ meth_name(A_virt)::typedef(23)=sym_desc(method)returning(int);
+ :arg_type(int),protection(public)normal(yes)virtual(yes)
+ vtable_index(1);class_first_defining(A);;;~%first_base(A);",
+ N_LSYM,NIL,NIL,NIL
+@end display
+
+@c FIXME: bogus line break.
+@example
+.stabs "A:t20=s8Adat:1,0,32;$vf20:21=*22=ar1;0;1;17,32;
+ A_virt::23=##1;:i;2A*-2147483647;20;;;~%20;",128,0,0,0
+@end example
+
+@node Inheritence
+@section Inheritence
+
+Stabs describing C++ derived classes include additional sections that
+describe the inheritence hierarchy of the class. A derived class stab
+also encodes the number of base classes. For each base class it tells
+if the base class is virtual or not, and if the inheritence is private
+or public. It also gives the offset into the object of the portion of
+the object corresponding to each base class.
+
+This additional information is embeded in the class stab following the
+number of bytes in the struct. First the number of base classes
+appears bracketed by an exclamation point and a comma.
+
+Then for each base type there repeats a series: two digits, a number,
+a comma, another number, and a semi-colon.
+
+The first of the two digits is 1 if the base class is virtual and 0 if
+not. The second digit is 2 if the derivation is public and 0 if not.
+
+The number following the first two digits is the offset from the start
+of the object to the part of the object pertaining to the base class.
+
+After the comma, the second number is a type_descriptor for the base
+type. Finally a semi-colon ends the series, which repeats for each
+base class.
+
+The source below defines three base classes @code{A}, @code{B}, and
+@code{C} and the derived class @code{D}.
+
+
+@example
+class A @{
+public:
+ int Adat;
+ virtual int A_virt (int arg) @{ return arg; @};
+@};
+
+class B @{
+public:
+ int B_dat;
+ virtual int B_virt (int arg) @{return arg; @};
+@};
+
+class C @{
+public:
+ int Cdat;
+ virtual int C_virt (int arg) @{return arg; @};
+@};
+
+class D : A, virtual B, public C @{
+public:
+ int Ddat;
+ virtual int A_virt (int arg ) @{ return arg+1; @};
+ virtual int B_virt (int arg) @{ return arg+2; @};
+ virtual int C_virt (int arg) @{ return arg+3; @};
+ virtual int D_virt (int arg) @{ return arg; @};
+@};
+@end example
+
+Class stabs similar to the ones described earlier are generated for
+each base class.
+
+@c FIXME!!! the linebreaks in the following example probably make the
+@c examples literally unusable, but I don't know any other way to get
+@c them on the page.
+@c One solution would be to put some of the type definitions into
+@c separate stabs, even if that's not exactly what the compiler actually
+@c emits.
+@smallexample
+.stabs "A:T20=s8Adat:1,0,32;$vf20:21=*22=ar1;0;1;17,32;
+ A_virt::23=##1;:i;2A*-2147483647;20;;;~%20;",128,0,0,0
+
+.stabs "B:Tt25=s8Bdat:1,0,32;$vf25:21,32;B_virt::26=##1;
+ :i;2A*-2147483647;25;;;~%25;",128,0,0,0
+
+.stabs "C:Tt28=s8Cdat:1,0,32;$vf28:21,32;C_virt::29=##1;
+ :i;2A*-2147483647;28;;;~%28;",128,0,0,0
+@end smallexample
+
+In the stab describing derived class @code{D} below, the information about
+the derivation of this class is encoded as follows.
+
+@display
+.stabs "derived_class_name:symbol_descriptors(struct tag&type)=
+ type_descriptor(struct)struct_bytes(32)!num_bases(3),
+ base_virtual(no)inheritence_public(no)base_offset(0),
+ base_class_type_ref(A);
+ base_virtual(yes)inheritence_public(no)base_offset(NIL),
+ base_class_type_ref(B);
+ base_virtual(no)inheritence_public(yes)base_offset(64),
+ base_class_type_ref(C); @dots{}
+@end display
+
+@c FIXME! fake linebreaks.
+@smallexample
+.stabs "D:Tt31=s32!3,000,20;100,25;0264,28;$vb25:24,128;Ddat:
+ 1,160,32;A_virt::32=##1;:i;2A*-2147483647;20;;B_virt:
+ :32:i;2A*-2147483647;25;;C_virt::32:i;2A*-2147483647;
+ 28;;D_virt::32:i;2A*-2147483646;31;;;~%20;",128,0,0,0
+@end smallexample
+
+@node Virtual Base Classes
+@section Virtual Base Classes
+
+A derived class object consists of a concatination in memory of the data
+areas defined by each base class, starting with the leftmost and ending
+with the rightmost in the list of base classes. The exception to this
+rule is for virtual inheritence. In the example above, class @code{D}
+inherits virtually from base class @code{B}. This means that an
+instance of a @code{D} object will not contain its own @code{B} part but
+merely a pointer to a @code{B} part, known as a virtual base pointer.
+
+In a derived class stab, the base offset part of the derivation
+information, described above, shows how the base class parts are
+ordered. The base offset for a virtual base class is always given as 0.
+Notice that the base offset for @code{B} is given as 0 even though
+@code{B} is not the first base class. The first base class @code{A}
+starts at offset 0.
+
+The field information part of the stab for class @code{D} describes the field
+which is the pointer to the virtual base class @code{B}. The vbase pointer
+name is @samp{$vb} followed by a type reference to the virtual base class.
+Since the type id for @code{B} in this example is 25, the vbase pointer name
+is @samp{$vb25}.
+
+@c FIXME!! fake linebreaks below
+@smallexample
+.stabs "D:Tt31=s32!3,000,20;100,25;0264,28;$vb25:24,128;Ddat:1,
+ 160,32;A_virt::32=##1;:i;2A*-2147483647;20;;B_virt::32:i;
+ 2A*-2147483647;25;;C_virt::32:i;2A*-2147483647;28;;D_virt:
+ :32:i;2A*-2147483646;31;;;~%20;",128,0,0,0
+@end smallexample
+
+Following the name and a semicolon is a type reference describing the
+type of the virtual base class pointer, in this case 24. Type 24 was
+defined earlier as the type of the @code{B} class @code{this} pointer. The
+@code{this} pointer for a class is a pointer to the class type.
+
+@example
+.stabs "this:P24=*25=xsB:",64,0,0,8
+@end example
+
+Finally the field offset part of the vbase pointer field description
+shows that the vbase pointer is the first field in the @code{D} object,
+before any data fields defined by the class. The layout of a @code{D}
+class object is a follows, @code{Adat} at 0, the vtable pointer for
+@code{A} at 32, @code{Cdat} at 64, the vtable pointer for C at 96, the
+virtual base pointer for @code{B} at 128, and @code{Ddat} at 160.
+
+
+@node Static Members
+@section Static Members
+
+The data area for a class is a concatenation of the space used by the
+data members of the class. If the class has virtual methods, a vtable
+pointer follows the class data. The field offset part of each field
+description in the class stab shows this ordering.
+
+<< How is this reflected in stabs? See Cygnus bug #677 for some info. >>
+
+@node Stab Types
+@appendix Table of Stab Types
+
+The following are all the possible values for the stab type field, for
+@code{a.out} files, in numeric order. This does not apply to XCOFF, but
+it does apply to stabs in ELF. Stabs in ECOFF use these values but add
+0x8f300 to distinguish them from non-stab symbols.
+
+The symbolic names are defined in the file @file{include/aout/stabs.def}.
+
+@menu
+* Non-Stab Symbol Types:: Types from 0 to 0x1f
+* Stab Symbol Types:: Types from 0x20 to 0xff
+@end menu
+
+@node Non-Stab Symbol Types
+@appendixsec Non-Stab Symbol Types
+
+The following types are used by the linker and assembler, not by stab
+directives. Since this document does not attempt to describe aspects of
+object file format other than the debugging format, no details are
+given.
+
+@c Try to get most of these to fit on a single line.
+@iftex
+@tableindent=1.5in
+@end iftex
+
+@table @code
+@item 0x0 N_UNDF
+Undefined symbol
+
+@item 0x2 N_ABS
+File scope absolute symbol
+
+@item 0x3 N_ABS | N_EXT
+External absolute symbol
+
+@item 0x4 N_TEXT
+File scope text symbol
+
+@item 0x5 N_TEXT | N_EXT
+External text symbol
+
+@item 0x6 N_DATA
+File scope data symbol
+
+@item 0x7 N_DATA | N_EXT
+External data symbol
+
+@item 0x8 N_BSS
+File scope BSS symbol
+
+@item 0x9 N_BSS | N_EXT
+External BSS symbol
+
+@item 0x0c N_FN_SEQ
+Same as @code{N_FN}, for Sequent compilers
+
+@item 0x0a N_INDR
+Symbol is indirected to another symbol
+
+@item 0x12 N_COMM
+Common---visible after shared library dynamic link
+
+@item 0x14 N_SETA
+Absolute set element
+
+@item 0x16 N_SETT
+Text segment set element
+
+@item 0x18 N_SETD
+Data segment set element
+
+@item 0x1a N_SETB
+BSS segment set element
+
+@item 0x1c N_SETV
+Pointer to set vector
+
+@item 0x1e N_WARNING
+Print a warning message during linking
+
+@item 0x1f N_FN
+File name of a @file{.o} file
+@end table
+
+@node Stab Symbol Types
+@appendixsec Stab Symbol Types
+
+The following symbol types indicate that this is a stab. This is the
+full list of stab numbers, including stab types that are used in
+languages other than C.
+
+@table @code
+@item 0x20 N_GSYM
+Global symbol; see @ref{Global Variables}.
+
+@item 0x22 N_FNAME
+Function name (for BSD Fortran); see @ref{Procedures}.
+
+@item 0x24 N_FUN
+Function name (@pxref{Procedures}) or text segment variable
+(@pxref{Statics}).
+
+@item 0x26 N_STSYM
+Data segment file-scope variable; see @ref{Statics}.
+
+@item 0x28 N_LCSYM
+BSS segment file-scope variable; see @ref{Statics}.
+
+@item 0x2a N_MAIN
+Name of main routine; see @ref{Main Program}.
+
+@item 0x2c N_ROSYM
+Variable in @code{.rodata} section; see @ref{Statics}.
+
+@item 0x30 N_PC
+Global symbol (for Pascal); see @ref{N_PC}.
+
+@item 0x32 N_NSYMS
+Number of symbols (according to Ultrix V4.0); see @ref{N_NSYMS}.
+
+@item 0x34 N_NOMAP
+No DST map; see @ref{N_NOMAP}.
+
+@c FIXME: describe this solaris feature in the body of the text (see
+@c comments in include/aout/stab.def).
+@item 0x38 N_OBJ
+Object file (Solaris2).
+
+@c See include/aout/stab.def for (a little) more info.
+@item 0x3c N_OPT
+Debugger options (Solaris2).
+
+@item 0x40 N_RSYM
+Register variable; see @ref{Register Variables}.
+
+@item 0x42 N_M2C
+Modula-2 compilation unit; see @ref{N_M2C}.
+
+@item 0x44 N_SLINE
+Line number in text segment; see @ref{Line Numbers}.
+
+@item 0x46 N_DSLINE
+Line number in data segment; see @ref{Line Numbers}.
+
+@item 0x48 N_BSLINE
+Line number in bss segment; see @ref{Line Numbers}.
+
+@item 0x48 N_BROWS
+Sun source code browser, path to @file{.cb} file; see @ref{N_BROWS}.
+
+@item 0x4a N_DEFD
+GNU Modula2 definition module dependency; see @ref{N_DEFD}.
+
+@item 0x4c N_FLINE
+Function start/body/end line numbers (Solaris2).
+
+@item 0x50 N_EHDECL
+GNU C++ exception variable; see @ref{N_EHDECL}.
+
+@item 0x50 N_MOD2
+Modula2 info "for imc" (according to Ultrix V4.0); see @ref{N_MOD2}.
+
+@item 0x54 N_CATCH
+GNU C++ @code{catch} clause; see @ref{N_CATCH}.
+
+@item 0x60 N_SSYM
+Structure of union element; see @ref{N_SSYM}.
+
+@item 0x62 N_ENDM
+Last stab for module (Solaris2).
+
+@item 0x64 N_SO
+Path and name of source file; see @ref{Source Files}.
+
+@item 0x80 N_LSYM
+Stack variable (@pxref{Stack Variables}) or type (@pxref{Typedefs}).
+
+@item 0x82 N_BINCL
+Beginning of an include file (Sun only); see @ref{Include Files}.
+
+@item 0x84 N_SOL
+Name of include file; see @ref{Include Files}.
+
+@item 0xa0 N_PSYM
+Parameter variable; see @ref{Parameters}.
+
+@item 0xa2 N_EINCL
+End of an include file; see @ref{Include Files}.
+
+@item 0xa4 N_ENTRY
+Alternate entry point; see @ref{N_ENTRY}.
+
+@item 0xc0 N_LBRAC
+Beginning of a lexical block; see @ref{Block Structure}.
+
+@item 0xc2 N_EXCL
+Place holder for a deleted include file; see @ref{Include Files}.
+
+@item 0xc4 N_SCOPE
+Modula2 scope information (Sun linker); see @ref{N_SCOPE}.
+
+@item 0xe0 N_RBRAC
+End of a lexical block; see @ref{Block Structure}.
+
+@item 0xe2 N_BCOMM
+Begin named common block; see @ref{Common Blocks}.
+
+@item 0xe4 N_ECOMM
+End named common block; see @ref{Common Blocks}.
+
+@item 0xe8 N_ECOML
+Member of a common block; see @ref{Common Blocks}.
+
+@c FIXME: How does this really work? Move it to main body of document.
+@item 0xea N_WITH
+Pascal @code{with} statement: type,,0,0,offset (Solaris2).
+
+@item 0xf0 N_NBTEXT
+Gould non-base registers; see @ref{Gould}.
+
+@item 0xf2 N_NBDATA
+Gould non-base registers; see @ref{Gould}.
+
+@item 0xf4 N_NBBSS
+Gould non-base registers; see @ref{Gould}.
+
+@item 0xf6 N_NBSTS
+Gould non-base registers; see @ref{Gould}.
+
+@item 0xf8 N_NBLCS
+Gould non-base registers; see @ref{Gould}.
+@end table
+
+@c Restore the default table indent
+@iftex
+@tableindent=.8in
+@end iftex
+
+@node Symbol Descriptors
+@appendix Table of Symbol Descriptors
+
+The symbol descriptor is the character which follows the colon in many
+stabs, and which tells what kind of stab it is. @xref{String Field},
+for more information about their use.
+
+@c Please keep this alphabetical
+@table @code
+@c In TeX, this looks great, digit is in italics. But makeinfo insists
+@c on putting it in `', not realizing that @var should override @code.
+@c I don't know of any way to make makeinfo do the right thing. Seems
+@c like a makeinfo bug to me.
+@item @var{digit}
+@itemx (
+@itemx -
+Variable on the stack; see @ref{Stack Variables}.
+
+@item a
+Parameter passed by reference in register; see @ref{Reference Parameters}.
+
+@item b
+Based variable; see @ref{Based Variables}.
+
+@item c
+Constant; see @ref{Constants}.
+
+@item C
+Conformant array bound (Pascal, maybe other languages); @ref{Conformant
+Arrays}. Name of a caught exception (GNU C++). These can be
+distinguished because the latter uses @code{N_CATCH} and the former uses
+another symbol type.
+
+@item d
+Floating point register variable; see @ref{Register Variables}.
+
+@item D
+Parameter in floating point register; see @ref{Register Parameters}.
+
+@item f
+File scope function; see @ref{Procedures}.
+
+@item F
+Global function; see @ref{Procedures}.
+
+@item G
+Global variable; see @ref{Global Variables}.
+
+@item i
+@xref{Register Parameters}.
+
+@item I
+Internal (nested) procedure; see @ref{Nested Procedures}.
+
+@item J
+Internal (nested) function; see @ref{Nested Procedures}.
+
+@item L
+Label name (documented by AIX, no further information known).
+
+@item m
+Module; see @ref{Procedures}.
+
+@item p
+Argument list parameter; see @ref{Parameters}.
+
+@item pP
+@xref{Parameters}.
+
+@item pF
+Fortran Function parameter; see @ref{Parameters}.
+
+@item P
+Unfortunately, three separate meanings have been independently invented
+for this symbol descriptor. At least the GNU and Sun uses can be
+distinguished by the symbol type. Global Procedure (AIX) (symbol type
+used unknown); see @ref{Procedures}. Register parameter (GNU) (symbol
+type @code{N_PSYM}); see @ref{Parameters}. Prototype of function
+referenced by this file (Sun @code{acc}) (symbol type @code{N_FUN}).
+
+@item Q
+Static Procedure; see @ref{Procedures}.
+
+@item R
+Register parameter; see @ref{Register Parameters}.
+
+@item r
+Register variable; see @ref{Register Variables}.
+
+@item S
+File scope variable; see @ref{Statics}.
+
+@item t
+Type name; see @ref{Typedefs}.
+
+@item T
+Enumeration, structure, or union tag; see @ref{Typedefs}.
+
+@item v
+Parameter passed by reference; see @ref{Reference Parameters}.
+
+@item V
+Procedure scope static variable; see @ref{Statics}.
+
+@item x
+Conformant array; see @ref{Conformant Arrays}.
+
+@item X
+Function return variable; see @ref{Parameters}.
+@end table
+
+@node Type Descriptors
+@appendix Table of Type Descriptors
+
+The type descriptor is the character which follows the type number and
+an equals sign. It specifies what kind of type is being defined.
+@xref{String Field}, for more information about their use.
+
+@table @code
+@item @var{digit}
+@itemx (
+Type reference; see @ref{String Field}.
+
+@item -
+Reference to builtin type; see @ref{Negative Type Numbers}.
+
+@item #
+Method (C++); see @ref{Cplusplus}.
+
+@item *
+Pointer; see @ref{Miscellaneous Types}.
+
+@item &
+Reference (C++).
+
+@item @@
+Type Attributes (AIX); see @ref{String Field}. Member (class and variable)
+type (GNU C++); see @ref{Cplusplus}.
+
+@item a
+Array; see @ref{Arrays}.
+
+@item A
+Open array; see @ref{Arrays}.
+
+@item b
+Pascal space type (AIX); see @ref{Miscellaneous Types}. Builtin integer
+type (Sun); see @ref{Builtin Type Descriptors}.
+
+@item B
+Volatile-qualified type; see @ref{Miscellaneous Types}.
+
+@item c
+Complex builtin type; see @ref{Builtin Type Descriptors}.
+
+@item C
+COBOL Picture type. See AIX documentation for details.
+
+@item d
+File type; see @ref{Miscellaneous Types}.
+
+@item D
+N-dimensional dynamic array; see @ref{Arrays}.
+
+@item e
+Enumeration type; see @ref{Enumerations}.
+
+@item E
+N-dimensional subarray; see @ref{Arrays}.
+
+@item f
+Function type; see @ref{Function Types}.
+
+@item F
+Pascal function parameter; see @ref{Function Types}
+
+@item g
+Builtin floating point type; see @ref{Builtin Type Descriptors}.
+
+@item G
+COBOL Group. See AIX documentation for details.
+
+@item i
+Imported type; see @ref{Cross-References}.
+
+@item k
+Const-qualified type; see @ref{Miscellaneous Types}.
+
+@item K
+COBOL File Descriptor. See AIX documentation for details.
+
+@item M
+Multiple instance type; see @ref{Miscellaneous Types}.
+
+@item n
+String type; see @ref{Strings}.
+
+@item N
+Stringptr; see @ref{Strings}.
+
+@item o
+Opaque type; see @ref{Typedefs}.
+
+@item p
+Procedure; see @ref{Function Types}.
+
+@item P
+Packed array; see @ref{Arrays}.
+
+@item r
+Range type; see @ref{Subranges}.
+
+@item R
+Builtin floating type; see @ref{Builtin Type Descriptors} (Sun). Pascal
+subroutine parameter; see @ref{Function Types} (AIX). Detecting this
+conflict is possible with careful parsing (hint: a Pascal subroutine
+parameter type will always contain a comma, and a builtin type
+descriptor never will).
+
+@item s
+Structure type; see @ref{Structures}.
+
+@item S
+Set type; see @ref{Miscellaneous Types}.
+
+@item u
+Union; see @ref{Unions}.
+
+@item v
+Variant record. This is a Pascal and Modula-2 feature which is like a
+union within a struct in C. See AIX documentation for details.
+
+@item w
+Wide character; see @ref{Builtin Type Descriptors}.
+
+@item x
+Cross-reference; see @ref{Cross-References}.
+
+@item z
+gstring; see @ref{Strings}.
+@end table
+
+@node Expanded Reference
+@appendix Expanded Reference by Stab Type
+
+@c FIXME: This appendix should go away; see N_PSYM or N_SO for an example.
+
+For a full list of stab types, and cross-references to where they are
+described, see @ref{Stab Types}. This appendix just duplicates certain
+information from the main body of this document; eventually the
+information will all be in one place.
+
+Format of an entry:
+
+The first line is the symbol type (see @file{include/aout/stab.def}).
+
+The second line describes the language constructs the symbol type
+represents.
+
+The third line is the stab format with the significant stab fields
+named and the rest NIL.
+
+Subsequent lines expand upon the meaning and possible values for each
+significant stab field. @samp{#} stands in for the type descriptor.
+
+Finally, any further information.
+
+@menu
+* N_PC:: Pascal global symbol
+* N_NSYMS:: Number of symbols
+* N_NOMAP:: No DST map
+* N_M2C:: Modula-2 compilation unit
+* N_BROWS:: Path to .cb file for Sun source code browser
+* N_DEFD:: GNU Modula2 definition module dependency
+* N_EHDECL:: GNU C++ exception variable
+* N_MOD2:: Modula2 information "for imc"
+* N_CATCH:: GNU C++ "catch" clause
+* N_SSYM:: Structure or union element
+* N_ENTRY:: Alternate entry point
+* N_SCOPE:: Modula2 scope information (Sun only)
+* Gould:: non-base register symbols used on Gould systems
+* N_LENG:: Length of preceding entry
+@end menu
+
+@node N_PC
+@section N_PC
+
+@deffn @code{.stabs} N_PC
+@findex N_PC
+Global symbol (for Pascal).
+
+@example
+"name" -> "symbol_name" <<?>>
+value -> supposedly the line number (stab.def is skeptical)
+@end example
+
+@display
+@file{stabdump.c} says:
+
+global pascal symbol: name,,0,subtype,line
+<< subtype? >>
+@end display
+@end deffn
+
+@node N_NSYMS
+@section N_NSYMS
+
+@deffn @code{.stabn} N_NSYMS
+@findex N_NSYMS
+Number of symbols (according to Ultrix V4.0).
+
+@display
+ 0, files,,funcs,lines (stab.def)
+@end display
+@end deffn
+
+@node N_NOMAP
+@section N_NOMAP
+
+@deffn @code{.stabs} N_NOMAP
+@findex N_NOMAP
+No DST map for symbol (according to Ultrix V4.0). I think this means a
+variable has been optimized out.
+
+@display
+ name, ,0,type,ignored (stab.def)
+@end display
+@end deffn
+
+@node N_M2C
+@section N_M2C
+
+@deffn @code{.stabs} N_M2C
+@findex N_M2C
+Modula-2 compilation unit.
+
+@example
+"string" -> "unit_name,unit_time_stamp[,code_time_stamp]"
+desc -> unit_number
+value -> 0 (main unit)
+ 1 (any other unit)
+@end example
+@end deffn
+
+@node N_BROWS
+@section N_BROWS
+
+@deffn @code{.stabs} N_BROWS
+@findex N_BROWS
+Sun source code browser, path to @file{.cb} file
+
+<<?>>
+"path to associated @file{.cb} file"
+
+Note: N_BROWS has the same value as N_BSLINE.
+@end deffn
+
+@node N_DEFD
+@section N_DEFD
+
+@deffn @code{.stabn} N_DEFD
+@findex N_DEFD
+GNU Modula2 definition module dependency.
+
+GNU Modula-2 definition module dependency. The value is the
+modification time of the definition file. The other field is non-zero
+if it is imported with the GNU M2 keyword @code{%INITIALIZE}. Perhaps
+@code{N_M2C} can be used if there are enough empty fields?
+@end deffn
+
+@node N_EHDECL
+@section N_EHDECL
+
+@deffn @code{.stabs} N_EHDECL
+@findex N_EHDECL
+GNU C++ exception variable <<?>>.
+
+"@var{string} is variable name"
+
+Note: conflicts with @code{N_MOD2}.
+@end deffn
+
+@node N_MOD2
+@section N_MOD2
+
+@deffn @code{.stab?} N_MOD2
+@findex N_MOD2
+Modula2 info "for imc" (according to Ultrix V4.0)
+
+Note: conflicts with @code{N_EHDECL} <<?>>
+@end deffn
+
+@node N_CATCH
+@section N_CATCH
+
+@deffn @code{.stabn} N_CATCH
+@findex N_CATCH
+GNU C++ @code{catch} clause
+
+GNU C++ @code{catch} clause. The value is its address. The desc field
+is nonzero if this entry is immediately followed by a @code{CAUGHT} stab
+saying what exception was caught. Multiple @code{CAUGHT} stabs means
+that multiple exceptions can be caught here. If desc is 0, it means all
+exceptions are caught here.
+@end deffn
+
+@node N_SSYM
+@section N_SSYM
+
+@deffn @code{.stabn} N_SSYM
+@findex N_SSYM
+Structure or union element.
+
+The value is the offset in the structure.
+
+<<?looking at structs and unions in C I didn't see these>>
+@end deffn
+
+@node N_ENTRY
+@section N_ENTRY
+
+@deffn @code{.stabn} N_ENTRY
+@findex N_ENTRY
+Alternate entry point.
+The value is its address.
+<<?>>
+@end deffn
+
+@node N_SCOPE
+@section N_SCOPE
+
+@deffn @code{.stab?} N_SCOPE
+@findex N_SCOPE
+Modula2 scope information (Sun linker)
+<<?>>
+@end deffn
+
+@node Gould
+@section Non-base registers on Gould systems
+
+@deffn @code{.stab?} N_NBTEXT
+@deffnx @code{.stab?} N_NBDATA
+@deffnx @code{.stab?} N_NBBSS
+@deffnx @code{.stab?} N_NBSTS
+@deffnx @code{.stab?} N_NBLCS
+@findex N_NBTEXT
+@findex N_NBDATA
+@findex N_NBBSS
+@findex N_NBSTS
+@findex N_NBLCS
+These are used on Gould systems for non-base registers syms.
+
+However, the following values are not the values used by Gould; they are
+the values which GNU has been documenting for these values for a long
+time, without actually checking what Gould uses. I include these values
+only because perhaps some someone actually did something with the GNU
+information (I hope not, why GNU knowingly assigned wrong values to
+these in the header file is a complete mystery to me).
+
+@example
+240 0xf0 N_NBTEXT ??
+242 0xf2 N_NBDATA ??
+244 0xf4 N_NBBSS ??
+246 0xf6 N_NBSTS ??
+248 0xf8 N_NBLCS ??
+@end example
+@end deffn
+
+@node N_LENG
+@section N_LENG
+
+@deffn @code{.stabn} N_LENG
+@findex N_LENG
+Second symbol entry containing a length-value for the preceding entry.
+The value is the length.
+@end deffn
+
+@node Questions
+@appendix Questions and Anomalies
+
+@itemize @bullet
+@item
+@c I think this is changed in GCC 2.4.5 to put the line number there.
+For GNU C stabs defining local and global variables (@code{N_LSYM} and
+@code{N_GSYM}), the desc field is supposed to contain the source
+line number on which the variable is defined. In reality the desc
+field is always 0. (This behavior is defined in @file{dbxout.c} and
+putting a line number in desc is controlled by @samp{#ifdef
+WINNING_GDB}, which defaults to false). GDB supposedly uses this
+information if you say @samp{list @var{var}}. In reality, @var{var} can
+be a variable defined in the program and GDB says @samp{function
+@var{var} not defined}.
+
+@item
+In GNU C stabs, there seems to be no way to differentiate tag types:
+structures, unions, and enums (symbol descriptor @samp{T}) and typedefs
+(symbol descriptor @samp{t}) defined at file scope from types defined locally
+to a procedure or other more local scope. They all use the @code{N_LSYM}
+stab type. Types defined at procedure scope are emited after the
+@code{N_RBRAC} of the preceding function and before the code of the
+procedure in which they are defined. This is exactly the same as
+types defined in the source file between the two procedure bodies.
+GDB overcompensates by placing all types in block #1, the block for
+symbols of file scope. This is true for default, @samp{-ansi} and
+@samp{-traditional} compiler options. (Bugs gcc/1063, gdb/1066.)
+
+@item
+What ends the procedure scope? Is it the proc block's @code{N_RBRAC} or the
+next @code{N_FUN}? (I believe its the first.)
+
+@item
+@c FIXME: This should go with the other stuff about global variables.
+Global variable stabs don't have location information. This comes
+from the external symbol for the same variable. The external symbol
+has a leading underbar on the _name of the variable and the stab does
+not. How do we know these two symbol table entries are talking about
+the same symbol when their names are different? (Answer: the debugger
+knows that external symbols have leading underbars).
+
+@c FIXME: This is absurdly vague; there all kinds of differences, some
+@c of which are the same between gnu & sun, and some of which aren't.
+@c In particular, I'm pretty sure GCC works with Sun dbx by default.
+@c @item
+@c Can GCC be configured to output stabs the way the Sun compiler
+@c does, so that their native debugging tools work? <NO?> It doesn't by
+@c default. GDB reads either format of stab. (GCC or SunC). How about
+@c dbx?
+@end itemize
+
+@node XCOFF Differences
+@appendix Differences Between GNU Stabs in a.out and GNU Stabs in XCOFF
+
+@c FIXME: Merge *all* these into the main body of the document.
+The AIX/RS6000 native object file format is XCOFF with stabs. This
+appendix only covers those differences which are not covered in the main
+body of this document.
+
+@itemize @bullet
+@item
+BSD a.out stab types correspond to AIX XCOFF storage classes. In general
+the mapping is @code{N_@var{stabtype}} becomes @code{C_@var{stabtype}}.
+Some stab types in a.out are not supported in XCOFF; most of these use
+@code{C_DECL}.
+
+@c FIXME: Get C_* types for the block, figure out whether it is always
+@c used (I suspect not), explain clearly, and move to node Statics.
+Exception: initialised static @code{N_STSYM} and un-initialized static
+@code{N_LCSYM} both map to the @code{C_STSYM} storage class. But the
+distinction is preserved because in XCOFF @code{N_STSYM} and
+@code{N_LCSYM} must be emited in a named static block. Begin the block
+with @samp{.bs s[RW] data_section_name} for @code{N_STSYM} or @samp{.bs
+s bss_section_name} for @code{N_LCSYM}. End the block with @samp{.es}.
+
+@c FIXME: I think they are trying to say something about whether the
+@c assembler defaults the value to the location counter.
+@item
+If the XCOFF stab is an @code{N_FUN} (@code{C_FUN}) then follow the
+string field with @samp{,.} instead of just @samp{,}.
+@end itemize
+
+I think that's it for @file{.s} file differences. They could stand to be
+better presented. This is just a list of what I have noticed so far.
+There are a @emph{lot} of differences in the information in the symbol
+tables of the executable and object files.
+
+Mapping of a.out stab types to XCOFF storage classes:
+
+@example
+stab type storage class
+-------------------------------
+N_GSYM C_GSYM
+N_FNAME unused
+N_FUN C_FUN
+N_STSYM C_STSYM
+N_LCSYM C_STSYM
+N_MAIN unknown
+N_PC unknown
+N_RSYM C_RSYM
+unknown C_RPSYM
+N_M2C unknown
+N_SLINE unknown
+N_DSLINE unknown
+N_BSLINE unknown
+N_BROWSE unchanged
+N_CATCH unknown
+N_SSYM unknown
+N_SO unknown
+N_LSYM C_LSYM
+various C_DECL
+N_BINCL unknown
+N_SOL unknown
+N_PSYM C_PSYM
+N_EINCL unknown
+N_ENTRY C_ENTRY
+N_LBRAC unknown
+N_EXCL unknown
+N_SCOPE unknown
+N_RBRAC unknown
+N_BCOMM C_BCOMM
+N_ECOMM C_ECOMM
+N_ECOML C_ECOML
+
+N_LENG unknown
+@end example
+
+@node Sun Differences
+@appendix Differences Between GNU Stabs and Sun Native Stabs
+
+@c FIXME: Merge all this stuff into the main body of the document.
+
+@itemize @bullet
+@item
+GNU C stabs define @emph{all} types, file or procedure scope, as
+@code{N_LSYM}. Sun doc talks about using @code{N_GSYM} too.
+
+@item
+Sun C stabs use type number pairs in the format
+(@var{file-number},@var{type-number}) where @var{file-number} is a
+number starting with 1 and incremented for each sub-source file in the
+compilation. @var{type-number} is a number starting with 1 and
+incremented for each new type defined in the compilation. GNU C stabs
+use the type number alone, with no source file number.
+@end itemize
+
+@node Stabs In ELF
+@appendix Using Stabs With The ELF Object File Format
+
+The ELF object file format allows tools to create object files with
+custom sections containing any arbitrary data. To use stabs in ELF
+object files, the tools create two custom sections, a section named
+@code{.stab} which contains an array of fixed length structures, one
+struct per stab, and a section named @code{.stabstr} containing all the
+variable length strings that are referenced by stabs in the @code{.stab}
+section. The byte order of the stabs binary data matches the byte order
+of the ELF file itself, as determined from the @code{EI_DATA} field in
+the @code{e_ident} member of the ELF header.
+
+The first stab in the @code{.stab} section for each compilation unit is
+synthetic, generated entirely by the assembler, with no corresponding
+@code{.stab} directive as input to the assembler. This stab contains
+the following fields:
+
+@table @code
+@item n_strx
+Offset in the @code{.stabstr} section to the source filename.
+
+@item n_type
+@code{N_UNDF}.
+
+@item n_other
+Unused field, always zero.
+
+@item n_desc
+Count of upcoming symbols, i.e., the number of remaining stabs for this
+source file.
+
+@item n_value
+Size of the string table fragment associated with this source file, in
+bytes.
+@end table
+
+The @code{.stabstr} section always starts with a null byte (so that string
+offsets of zero reference a null string), followed by random length strings,
+each of which is null byte terminated.
+
+The ELF section header for the @code{.stab} section has its
+@code{sh_link} member set to the section number of the @code{.stabstr}
+section, and the @code{.stabstr} section has its ELF section
+header @code{sh_type} member set to @code{SHT_STRTAB} to mark it as a
+string table.
+
+To keep linking fast, it is a bad idea to have the linker relocating
+stabs, so (except for a few cases, see below) none of the addresses in
+the @code{n_value} field of the stabs are relocated by the linker.
+Instead they are relative to the source file (or some entity smaller
+than a source file, like a function). To find the address of each
+section corresponding to a given source file, the compiler puts out
+symbols giving the address of each section for a given source file.
+Since these are ELF (not stab) symbols, the linker relocates them
+correctly without having to touch the stabs section. They are named
+@code{Bbss.bss} for the bss section, @code{Ddata.data} for the data
+section, and @code{Drodata.rodata} for the rodata section. For the text
+section, there is no such symbol (but there should be, see below). For
+an example of how these symbols work, @xref{ELF Transformations}. GCC
+does not provide these symbols; it instead relies on the stabs getting
+relocated, which slows down linking. Thus addresses which would
+normally be relative to @code{Bbss.bss}, etc., are already relocated.
+The Sun linker provided with Solaris 2.2 and earlier relocates stabs
+using normal ELF relocation information, as it would do for any section.
+Sun has been threatening to kludge their linker to not do this (to speed
+up linking), even though the correct way to avoid having the linker do
+these relocations is to have the compiler no longer output relocatable
+values. Last I heard they had been talked out of the linker kludge.
+See Sun point patch 101052-01 and Sun bug 1142109. This affects
+@samp{S} symbol descriptor stabs (@pxref{Statics}) and functions
+(@pxref{Procedures}). In the latter case, to adopt the clean solution
+(making the value of the stab relative to the start of the compilation
+unit), it would be necessary to invent a @code{Ttext.text} symbol,
+analogous to the @code{Bbss.bss}, etc., symbols. I recommend this
+rather than using a zero value and getting the address from the ELF
+symbols.
+
+@node Symbol Types Index
+@unnumbered Symbol Types Index
+
+@printindex fn
+
+@contents
+@bye
diff --git a/gnu/usr.bin/gdb/environ.c b/gnu/usr.bin/gdb/environ.c
new file mode 100644
index 0000000..0220166
--- /dev/null
+++ b/gnu/usr.bin/gdb/environ.c
@@ -0,0 +1,185 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)environ.c 6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* environ.c -- library for manipulating environments for GNU.
+ Copyright (C) 1986, 1989 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#define max(a, b) ((a) > (b) ? (a) : (b))
+
+#include "environ.h"
+
+/* Return a new environment object. */
+
+struct environ *
+make_environ ()
+{
+ register struct environ *e;
+
+ e = (struct environ *) xmalloc (sizeof (struct environ));
+
+ e->allocated = 10;
+ e->vector = (char **) xmalloc ((e->allocated + 1) * sizeof (char *));
+ e->vector[0] = 0;
+ return e;
+}
+
+/* Free an environment and all the strings in it. */
+
+void
+free_environ (e)
+ register struct environ *e;
+{
+ register char **vector = e->vector;
+
+ while (*vector)
+ free (*vector++);
+
+ free (e);
+}
+
+/* Copy the environment given to this process into E.
+ Also copies all the strings in it, so we can be sure
+ that all strings in these environments are safe to free. */
+
+void
+init_environ (e)
+ register struct environ *e;
+{
+ extern char **environ;
+ register int i;
+
+ for (i = 0; environ[i]; i++);
+
+ if (e->allocated < i)
+ {
+ e->allocated = max (i, e->allocated + 10);
+ e->vector = (char **) xrealloc (e->vector,
+ (e->allocated + 1) * sizeof (char *));
+ }
+
+ bcopy (environ, e->vector, (i + 1) * sizeof (char *));
+
+ while (--i >= 0)
+ {
+ register int len = strlen (e->vector[i]) + 1;
+ register char *new = (char *) xmalloc (len);
+ bcopy (e->vector[i], new, len);
+ e->vector[i] = new;
+ }
+}
+
+/* Return the vector of environment E.
+ This is used to get something to pass to execve. */
+
+char **
+environ_vector (e)
+ struct environ *e;
+{
+ return e->vector;
+}
+
+/* Return the value in environment E of variable VAR. */
+
+char *
+get_in_environ (e, var)
+ struct environ *e;
+ char *var;
+{
+ register int len = strlen (var);
+ register char **vector = e->vector;
+ register char *s;
+
+ for (; s = *vector; vector++)
+ if (!strncmp (s, var, len)
+ && s[len] == '=')
+ return &s[len + 1];
+
+ return 0;
+}
+
+/* Store the value in E of VAR as VALUE. */
+
+void
+set_in_environ (e, var, value)
+ struct environ *e;
+ char *var;
+ char *value;
+{
+ register int i;
+ register int len = strlen (var);
+ register char **vector = e->vector;
+ register char *s;
+
+ for (i = 0; s = vector[i]; i++)
+ if (!strncmp (s, var, len)
+ && s[len] == '=')
+ break;
+
+ if (s == 0)
+ {
+ if (i == e->allocated)
+ {
+ e->allocated += 10;
+ vector = (char **) xrealloc (vector,
+ (e->allocated + 1) * sizeof (char *));
+ e->vector = vector;
+ }
+ vector[i + 1] = 0;
+ }
+ else
+ free (s);
+
+ s = (char *) xmalloc (len + strlen (value) + 2);
+ strcpy (s, var);
+ strcat (s, "=");
+ strcat (s, value);
+ vector[i] = s;
+ return;
+}
+
+/* Remove the setting for variable VAR from environment E. */
+
+void
+unset_in_environ (e, var)
+ struct environ *e;
+ char *var;
+{
+ register int len = strlen (var);
+ register char **vector = e->vector;
+ register char *s;
+
+ for (; s = *vector; vector++)
+ if (!strncmp (s, var, len)
+ && s[len] == '=')
+ {
+ free (s);
+ bcopy (vector + 1, vector,
+ (e->allocated - (vector - e->vector)) * sizeof (char *));
+ e->vector[e->allocated - 1] = 0;
+ return;
+ }
+}
diff --git a/gnu/usr.bin/gdb/environ.h b/gnu/usr.bin/gdb/environ.h
new file mode 100644
index 0000000..13f31f4
--- /dev/null
+++ b/gnu/usr.bin/gdb/environ.h
@@ -0,0 +1,39 @@
+/* Header for environment manipulation library.
+ Copyright (C) 1989, Free Software Foundation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* We manipulate environments represented as these structures. */
+
+struct environ
+{
+ /* Number of usable slots allocated in VECTOR.
+ VECTOR always has one slot not counted here,
+ to hold the terminating zero. */
+ int allocated;
+ /* A vector of slots, ALLOCATED + 1 of them.
+ The first few slots contain strings "VAR=VALUE"
+ and the next one contains zero.
+ Then come some unused slots. */
+ char **vector;
+};
+
+struct environ *make_environ ();
+void free_environ ();
+void init_environ ();
+char *get_in_environ ();
+void set_in_environ ();
+void unset_in_environ ();
+char **environ_vector ();
diff --git a/gnu/usr.bin/gdb/eval.c b/gnu/usr.bin/gdb/eval.c
new file mode 100644
index 0000000..60779e6
--- /dev/null
+++ b/gnu/usr.bin/gdb/eval.c
@@ -0,0 +1,1065 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)eval.c 6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Evaluate expressions for GDB.
+ Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "value.h"
+#include "expression.h"
+
+
+/* Parse the string EXP as a C expression, evaluate it,
+ and return the result as a number. */
+
+CORE_ADDR
+parse_and_eval_address (exp)
+ char *exp;
+{
+ struct expression *expr = parse_c_expression (exp);
+ register CORE_ADDR addr;
+ register struct cleanup *old_chain
+ = make_cleanup (free_current_contents, &expr);
+
+ addr = (CORE_ADDR) value_as_long (evaluate_expression (expr));
+ do_cleanups (old_chain);
+ return addr;
+}
+
+/* Like parse_and_eval_address but takes a pointer to a char * variable
+ and advanced that variable across the characters parsed. */
+
+CORE_ADDR
+parse_and_eval_address_1 (expptr)
+ char **expptr;
+{
+ struct expression *expr = parse_c_1 (expptr, 0, 0);
+ register CORE_ADDR addr;
+ register struct cleanup *old_chain
+ = make_cleanup (free_current_contents, &expr);
+
+ addr = value_as_long (evaluate_expression (expr));
+ do_cleanups (old_chain);
+ return addr;
+}
+
+value
+parse_and_eval (exp)
+ char *exp;
+{
+ struct expression *expr = parse_c_expression (exp);
+ register value val;
+ register struct cleanup *old_chain
+ = make_cleanup (free_current_contents, &expr);
+
+ val = evaluate_expression (expr);
+ do_cleanups (old_chain);
+ return val;
+}
+
+/* Parse up to a comma (or to a closeparen)
+ in the string EXPP as an expression, evaluate it, and return the value.
+ EXPP is advanced to point to the comma. */
+
+value
+parse_to_comma_and_eval (expp)
+ char **expp;
+{
+ struct expression *expr = parse_c_1 (expp, 0, 1);
+ register value val;
+ register struct cleanup *old_chain
+ = make_cleanup (free_current_contents, &expr);
+
+ val = evaluate_expression (expr);
+ do_cleanups (old_chain);
+ return val;
+}
+
+/* Evaluate an expression in internal prefix form
+ such as is constructed by expread.y.
+
+ See expression.h for info on the format of an expression. */
+
+static value evaluate_subexp ();
+static value evaluate_subexp_for_address ();
+static value evaluate_subexp_for_sizeof ();
+static value evaluate_subexp_with_coercion ();
+
+/* return true if 'var' has an address in inferior's memory. */
+static int
+value_has_lval(var)
+ register struct symbol *var;
+{
+ switch (SYMBOL_CLASS(var))
+ {
+ case LOC_STATIC:
+ case LOC_LABEL:
+ case LOC_ARG:
+ case LOC_REF_ARG:
+ case LOC_LOCAL:
+ case LOC_BLOCK:
+ return (1);
+ }
+ return (0);
+}
+
+/* Values of NOSIDE argument to eval_subexp. */
+enum noside
+{ EVAL_NORMAL,
+ EVAL_SKIP, /* Only effect is to increment pos. */
+ EVAL_AVOID_SIDE_EFFECTS, /* Don't modify any variables or
+ call any functions. The value
+ returned will have the correct
+ type, and will have an
+ approximately correct lvalue
+ type (inaccuracy: anything that is
+ listed as being in a register in
+ the function in which it was
+ declared will be lval_register). */
+};
+
+value
+evaluate_expression (exp)
+ struct expression *exp;
+{
+ int pc = 0;
+ return evaluate_subexp (0, exp, &pc, EVAL_NORMAL);
+}
+
+/* Evaluate an expression, avoiding all memory references
+ and getting a value whose type alone is correct. */
+
+value
+evaluate_type (exp)
+ struct expression *exp;
+{
+ int pc = 0;
+ return evaluate_subexp (0, exp, &pc, EVAL_AVOID_SIDE_EFFECTS);
+}
+
+static value
+evaluate_subexp (expect_type, exp, pos, noside)
+ struct type *expect_type;
+ register struct expression *exp;
+ register int *pos;
+ enum noside noside;
+{
+ enum exp_opcode op;
+ int tem;
+ register int pc, pc2, oldpos;
+ register value arg1, arg2, arg3;
+ int nargs;
+ value *argvec;
+
+ pc = (*pos)++;
+ op = exp->elts[pc].opcode;
+
+ switch (op)
+ {
+ case OP_SCOPE:
+ tem = strlen (&exp->elts[pc + 2].string);
+ (*pos) += 3 + ((tem + sizeof (union exp_element))
+ / sizeof (union exp_element));
+ return value_static_field (exp->elts[pc + 1].type,
+ &exp->elts[pc + 2].string, -1);
+
+ case OP_LONG:
+ (*pos) += 3;
+ return value_from_long (exp->elts[pc + 1].type,
+ exp->elts[pc + 2].longconst);
+
+ case OP_DOUBLE:
+ (*pos) += 3;
+ return value_from_double (exp->elts[pc + 1].type,
+ exp->elts[pc + 2].doubleconst);
+
+ case OP_VAR_VALUE:
+ (*pos) += 2;
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ {
+ struct symbol * sym = exp->elts[pc + 1].symbol;
+ enum lval_type lv;
+
+ switch (SYMBOL_CLASS (sym))
+ {
+ case LOC_CONST:
+ case LOC_LABEL:
+ case LOC_CONST_BYTES:
+ lv = not_lval;
+ case LOC_REGISTER:
+ case LOC_REGPARM:
+ lv = lval_register;
+ default:
+ lv = lval_memory;
+ }
+
+ return value_zero (SYMBOL_TYPE (sym), lv);
+ }
+ else
+ return value_of_variable (exp->elts[pc + 1].symbol);
+
+ case OP_LAST:
+ (*pos) += 2;
+ return access_value_history ((int) exp->elts[pc + 1].longconst);
+
+ case OP_REGISTER:
+ (*pos) += 2;
+ return value_of_register ((int) exp->elts[pc + 1].longconst);
+
+ case OP_INTERNALVAR:
+ (*pos) += 2;
+ return value_of_internalvar (exp->elts[pc + 1].internalvar);
+
+ case OP_STRING:
+ tem = strlen (&exp->elts[pc + 1].string);
+ (*pos) += 2 + ((tem + sizeof (union exp_element))
+ / sizeof (union exp_element));
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ return value_string (&exp->elts[pc + 1].string, tem);
+
+ case TERNOP_COND:
+ /* Skip third and second args to evaluate the first one. */
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+ if (value_zerop (arg1))
+ {
+ evaluate_subexp (0, exp, pos, EVAL_SKIP);
+ return evaluate_subexp (0, exp, pos, noside);
+ }
+ else
+ {
+ arg2 = evaluate_subexp (0, exp, pos, noside);
+ evaluate_subexp (0, exp, pos, EVAL_SKIP);
+ return arg2;
+ }
+
+ case OP_FUNCALL:
+ (*pos) += 2;
+ op = exp->elts[*pos].opcode;
+ if (op == STRUCTOP_MEMBER || op == STRUCTOP_MPTR)
+ {
+ int fnptr;
+ int tem2;
+
+ nargs = (int) exp->elts[pc + 1].longconst + 1;
+ /* First, evaluate the structure into arg2 */
+ pc2 = (*pos)++;
+
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+
+ if (op == STRUCTOP_MEMBER)
+ {
+ arg2 = evaluate_subexp_for_address (exp, pos, noside);
+ }
+ else
+ {
+ arg2 = evaluate_subexp (0, exp, pos, noside);
+ }
+
+ /* If the function is a virtual function, then the
+ aggregate value (providing the structure) plays
+ its part by providing the vtable. Otherwise,
+ it is just along for the ride: call the function
+ directly. */
+
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+
+ fnptr = (int) value_as_long (arg1);
+ if (fnptr < 128)
+ {
+ struct type *basetype;
+ int i, j;
+ basetype = TYPE_TARGET_TYPE (VALUE_TYPE (arg2));
+ basetype = TYPE_VPTR_BASETYPE (basetype);
+ for (i = TYPE_NFN_FIELDS (basetype) - 1; i >= 0; i--)
+ {
+ struct fn_field *f = TYPE_FN_FIELDLIST1 (basetype, i);
+ /* If one is virtual, then all are virtual. */
+ if (TYPE_FN_FIELD_VIRTUAL_P (f, 0))
+ for (j = TYPE_FN_FIELDLIST_LENGTH (basetype, i) - 1; j >= 0; --j)
+ if (TYPE_FN_FIELD_VOFFSET (f, j) == fnptr)
+ {
+ value vtbl;
+ value base = value_ind (arg2);
+ struct type *fntype = lookup_pointer_type (TYPE_FN_FIELD_TYPE (f, j));
+
+ if (TYPE_VPTR_FIELDNO (basetype) < 0)
+ TYPE_VPTR_FIELDNO (basetype)
+ = fill_in_vptr_fieldno (basetype);
+
+ VALUE_TYPE (base) = basetype;
+ vtbl = value_field (base, TYPE_VPTR_FIELDNO (basetype));
+ VALUE_TYPE (vtbl) = lookup_pointer_type (fntype);
+ VALUE_TYPE (arg1) = builtin_type_int;
+ arg1 = value_subscript (vtbl, arg1);
+ VALUE_TYPE (arg1) = fntype;
+ goto got_it;
+ }
+ }
+ if (i < 0)
+ error ("virtual function at index %d not found", fnptr);
+ }
+ else
+ {
+ VALUE_TYPE (arg1) = lookup_pointer_type (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)));
+ }
+ got_it:
+
+ /* Now, say which argument to start evaluating from */
+ tem = 2;
+ }
+ else if (op == STRUCTOP_STRUCT || op == STRUCTOP_PTR)
+ {
+ /* Hair for method invocations */
+ int tem2;
+
+ nargs = (int) exp->elts[pc + 1].longconst + 1;
+ /* First, evaluate the structure into arg2 */
+ pc2 = (*pos)++;
+ tem2 = strlen (&exp->elts[pc2 + 1].string);
+ *pos += 2 + (tem2 + sizeof (union exp_element)) / sizeof (union exp_element);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+
+ if (op == STRUCTOP_STRUCT)
+ {
+ arg2 = evaluate_subexp_for_address (exp, pos, noside);
+ }
+ else
+ {
+ arg2 = evaluate_subexp (0, exp, pos, noside);
+ }
+ /* Now, say which argument to start evaluating from */
+ tem = 2;
+ }
+ else
+ {
+ nargs = (int) exp->elts[pc + 1].longconst;
+ tem = 0;
+ }
+ argvec = (value *) alloca (sizeof (value) * (nargs + 2));
+ for (; tem <= nargs; tem++)
+ /* Ensure that array expressions are coerced into pointer objects. */
+ argvec[tem] = evaluate_subexp_with_coercion (exp, pos, noside);
+
+ /* signal end of arglist */
+ argvec[tem] = 0;
+
+ if (op == STRUCTOP_STRUCT || op == STRUCTOP_PTR)
+ {
+ int static_memfuncp;
+
+ argvec[1] = arg2;
+ argvec[0] =
+ value_struct_elt (arg2, argvec+1, &exp->elts[pc2 + 1].string,
+ &static_memfuncp,
+ op == STRUCTOP_STRUCT
+ ? "structure" : "structure pointer");
+ if (static_memfuncp)
+ {
+ argvec[1] = argvec[0];
+ nargs--;
+ argvec++;
+ }
+ }
+ else if (op == STRUCTOP_MEMBER || op == STRUCTOP_MPTR)
+ {
+ argvec[1] = arg2;
+ argvec[0] = arg1;
+ }
+
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ {
+ /* If the return type doesn't look like a function type, call an
+ error. This can happen if somebody tries to turn a variable into
+ a function call. This is here because people often want to
+ call, eg, strcmp, which gdb doesn't know is a function. If
+ gdb isn't asked for it's opinion (ie. through "whatis"),
+ it won't offer it. */
+
+ struct type *ftype =
+ TYPE_TARGET_TYPE (VALUE_TYPE (argvec[0]));
+
+ if (ftype)
+ return allocate_value (TYPE_TARGET_TYPE (VALUE_TYPE (argvec[0])));
+ else
+ error ("Expression of type other than \"Function returning ...\" used as function");
+ }
+ return call_function (argvec[0], nargs, argvec + 1);
+
+ case STRUCTOP_STRUCT:
+ tem = strlen (&exp->elts[pc + 1].string);
+ (*pos) += 2 + ((tem + sizeof (union exp_element))
+ / sizeof (union exp_element));
+
+ /* Try to convert "foo.bar" into "(&foo)->bar" so we won't copy
+ * the entire contents of a large struct just to extract one
+ * value from it. */
+ if (noside == EVAL_NORMAL && exp->elts[*pos].opcode == OP_VAR_VALUE
+ && value_has_lval(exp->elts[*pos + 1].symbol))
+ arg1 = evaluate_subexp_for_address(exp, pos, noside);
+ else
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ {
+ register struct type *type = VALUE_TYPE (arg1);
+ if (TYPE_CODE (type) == TYPE_CODE_PTR)
+ type = TYPE_TARGET_TYPE (type);
+ return value_zero (lookup_struct_elt_type (type,
+ &exp->elts[pc + 1].string),
+ lval_memory);
+ }
+ else
+ return value_struct_elt (arg1, 0, &exp->elts[pc + 1].string, 0,
+ "structure");
+
+ case STRUCTOP_PTR:
+ tem = strlen (&exp->elts[pc + 1].string);
+ (*pos) += 2 + (tem + sizeof (union exp_element)) / sizeof (union exp_element);
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ return value_zero (lookup_struct_elt_type (TYPE_TARGET_TYPE
+ (VALUE_TYPE (arg1)),
+ &exp->elts[pc + 1].string),
+ lval_memory);
+ else
+ return value_struct_elt (arg1, 0, &exp->elts[pc + 1].string, 0,
+ "structure pointer");
+
+ case STRUCTOP_MEMBER:
+ arg1 = evaluate_subexp_for_address (exp, pos, noside);
+ arg2 = evaluate_subexp (0, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ /* Now, convert these values to an address. */
+ if (TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_PTR
+ || ((TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2)))
+ != TYPE_CODE_MEMBER)
+ && (TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2)))
+ != TYPE_CODE_METHOD)))
+ error ("non-pointer-to-member value used in pointer-to-member construct");
+ arg3 = value_from_long (builtin_type_long,
+ value_as_long (arg1) + value_as_long (arg2));
+ VALUE_TYPE (arg3) =
+ lookup_pointer_type (TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2))));
+ return value_ind (arg3);
+
+ case STRUCTOP_MPTR:
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+ arg2 = evaluate_subexp (0, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ /* Now, convert these values to an address. */
+ if (TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_PTR
+ || (TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2))) != TYPE_CODE_MEMBER
+ && TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2))) != TYPE_CODE_METHOD))
+ error ("non-pointer-to-member value used in pointer-to-member construct");
+ arg3 = value_from_long (builtin_type_long,
+ value_as_long (arg1) + value_as_long (arg2));
+ VALUE_TYPE (arg3) =
+ lookup_pointer_type (TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2))));
+ return value_ind (arg3);
+
+ case BINOP_ASSIGN:
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+ arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+ if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+ return arg1;
+ if (binop_user_defined_p (op, arg1, arg2))
+ return value_x_binop (arg1, arg2, op, 0);
+ else
+ return value_assign (arg1, arg2);
+
+ case BINOP_ASSIGN_MODIFY:
+ (*pos) += 2;
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+ arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+ if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+ return arg1;
+ op = exp->elts[pc + 1].opcode;
+ if (binop_user_defined_p (op, arg1, arg2))
+ return value_x_binop (arg1, arg2, BINOP_ASSIGN_MODIFY, op);
+ else if (op == BINOP_ADD)
+ arg2 = value_add (arg1, arg2);
+ else if (op == BINOP_SUB)
+ arg2 = value_sub (arg1, arg2);
+ else
+ arg2 = value_binop (arg1, arg2, op);
+ return value_assign (arg1, arg2);
+
+ case BINOP_ADD:
+ arg1 = evaluate_subexp_with_coercion (exp, pos, noside);
+ arg2 = evaluate_subexp_with_coercion (exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (binop_user_defined_p (op, arg1, arg2))
+ return value_x_binop (arg1, arg2, op, 0);
+ else
+ return value_add (arg1, arg2);
+
+ case BINOP_SUB:
+ arg1 = evaluate_subexp_with_coercion (exp, pos, noside);
+ arg2 = evaluate_subexp_with_coercion (exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (binop_user_defined_p (op, arg1, arg2))
+ return value_x_binop (arg1, arg2, op, 0);
+ else
+ return value_sub (arg1, arg2);
+
+ case BINOP_MUL:
+ case BINOP_DIV:
+ case BINOP_REM:
+ case BINOP_LSH:
+ case BINOP_RSH:
+ case BINOP_LOGAND:
+ case BINOP_LOGIOR:
+ case BINOP_LOGXOR:
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+ arg2 = evaluate_subexp (0, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (binop_user_defined_p (op, arg1, arg2))
+ return value_x_binop (arg1, arg2, op, 0);
+ else
+ if (noside == EVAL_AVOID_SIDE_EFFECTS
+ && op == BINOP_DIV)
+ return value_zero (VALUE_TYPE (arg1), not_lval);
+ else
+ return value_binop (arg1, arg2, op);
+
+ case BINOP_SUBSCRIPT:
+ arg1 = evaluate_subexp_with_coercion (exp, pos, noside);
+ arg2 = evaluate_subexp_with_coercion (exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ return value_zero (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)),
+ VALUE_LVAL (arg1));
+
+ if (binop_user_defined_p (op, arg1, arg2))
+ return value_x_binop (arg1, arg2, op, 0);
+ else
+ return value_subscript (arg1, arg2);
+
+ case BINOP_AND:
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ {
+ arg2 = evaluate_subexp (0, exp, pos, noside);
+ goto nosideret;
+ }
+
+ oldpos = *pos;
+ arg2 = evaluate_subexp (0, exp, pos, EVAL_AVOID_SIDE_EFFECTS);
+ *pos = oldpos;
+
+ if (binop_user_defined_p (op, arg1, arg2))
+ {
+ arg2 = evaluate_subexp (0, exp, pos, noside);
+ return value_x_binop (arg1, arg2, op, 0);
+ }
+ else
+ {
+ tem = value_zerop (arg1);
+ arg2 = evaluate_subexp (0, exp, pos,
+ (tem ? EVAL_SKIP : noside));
+ return value_from_long (builtin_type_int,
+ (LONGEST) (!tem && !value_zerop (arg2)));
+ }
+
+ case BINOP_OR:
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ {
+ arg2 = evaluate_subexp (0, exp, pos, noside);
+ goto nosideret;
+ }
+
+ oldpos = *pos;
+ arg2 = evaluate_subexp (0, exp, pos, EVAL_AVOID_SIDE_EFFECTS);
+ *pos = oldpos;
+
+ if (binop_user_defined_p (op, arg1, arg2))
+ {
+ arg2 = evaluate_subexp (0, exp, pos, noside);
+ return value_x_binop (arg1, arg2, op, 0);
+ }
+ else
+ {
+ tem = value_zerop (arg1);
+ arg2 = evaluate_subexp (0, exp, pos,
+ (!tem ? EVAL_SKIP : noside));
+ return value_from_long (builtin_type_int,
+ (LONGEST) (!tem || !value_zerop (arg2)));
+ }
+
+ case BINOP_EQUAL:
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+ arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (binop_user_defined_p (op, arg1, arg2))
+ {
+ return value_x_binop (arg1, arg2, op, 0);
+ }
+ else
+ {
+ tem = value_equal (arg1, arg2);
+ return value_from_long (builtin_type_int, (LONGEST) tem);
+ }
+
+ case BINOP_NOTEQUAL:
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+ arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (binop_user_defined_p (op, arg1, arg2))
+ {
+ return value_x_binop (arg1, arg2, op, 0);
+ }
+ else
+ {
+ tem = value_equal (arg1, arg2);
+ return value_from_long (builtin_type_int, (LONGEST) ! tem);
+ }
+
+ case BINOP_LESS:
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+ arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (binop_user_defined_p (op, arg1, arg2))
+ {
+ return value_x_binop (arg1, arg2, op, 0);
+ }
+ else
+ {
+ tem = value_less (arg1, arg2);
+ return value_from_long (builtin_type_int, (LONGEST) tem);
+ }
+
+ case BINOP_GTR:
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+ arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (binop_user_defined_p (op, arg1, arg2))
+ {
+ return value_x_binop (arg1, arg2, op, 0);
+ }
+ else
+ {
+ tem = value_less (arg2, arg1);
+ return value_from_long (builtin_type_int, (LONGEST) tem);
+ }
+
+ case BINOP_GEQ:
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+ arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (binop_user_defined_p (op, arg1, arg2))
+ {
+ return value_x_binop (arg1, arg2, op, 0);
+ }
+ else
+ {
+ tem = value_less (arg1, arg2);
+ return value_from_long (builtin_type_int, (LONGEST) ! tem);
+ }
+
+ case BINOP_LEQ:
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+ arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (binop_user_defined_p (op, arg1, arg2))
+ {
+ return value_x_binop (arg1, arg2, op, 0);
+ }
+ else
+ {
+ tem = value_less (arg2, arg1);
+ return value_from_long (builtin_type_int, (LONGEST) ! tem);
+ }
+
+ case BINOP_REPEAT:
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+ arg2 = evaluate_subexp (0, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_INT)
+ error ("Non-integral right operand for \"@\" operator.");
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ return allocate_repeat_value (VALUE_TYPE (arg1),
+ (int) value_as_long (arg2));
+ else
+ return value_repeat (arg1, (int) value_as_long (arg2));
+
+ case BINOP_COMMA:
+ evaluate_subexp (0, exp, pos, noside);
+ return evaluate_subexp (0, exp, pos, noside);
+
+ case UNOP_NEG:
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (unop_user_defined_p (op, arg1))
+ return value_x_unop (arg1, op);
+ else
+ return value_neg (arg1);
+
+ case UNOP_LOGNOT:
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (unop_user_defined_p (op, arg1))
+ return value_x_unop (arg1, op);
+ else
+ return value_lognot (arg1);
+
+ case UNOP_ZEROP:
+ arg1 = evaluate_subexp (0, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (unop_user_defined_p (op, arg1))
+ return value_x_unop (arg1, op);
+ else
+ return value_from_long (builtin_type_int,
+ (LONGEST) value_zerop (arg1));
+
+ case UNOP_IND:
+ if (expect_type && TYPE_CODE (expect_type) == TYPE_CODE_PTR)
+ expect_type = TYPE_TARGET_TYPE (expect_type);
+ arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ {
+ if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR
+ || TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_REF
+ /* In C you can dereference an array to get the 1st elt. */
+ || TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_ARRAY
+ )
+ return value_zero (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)),
+ lval_memory);
+ else if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_INT)
+ /* GDB allows dereferencing an int. */
+ return value_zero (builtin_type_int, lval_memory);
+ else
+ error ("Attempt to take contents of a non-pointer value.");
+ }
+ return value_ind (arg1);
+
+ case UNOP_ADDR:
+ /* C++: check for and handle pointer to members. */
+
+ op = exp->elts[*pos].opcode;
+
+ if (noside == EVAL_SKIP)
+ {
+ if (op == OP_SCOPE)
+ {
+ char *name = &exp->elts[pc+3].string;
+ int tem = strlen (name);
+ (*pos) += 2 + (tem + sizeof (union exp_element)) / sizeof (union exp_element);
+ }
+ else
+ evaluate_subexp (expect_type, exp, pos, EVAL_SKIP);
+ goto nosideret;
+ }
+
+ if (op == OP_SCOPE)
+ {
+ char *name = &exp->elts[pc+3].string;
+ int tem = strlen (name);
+ struct type *domain = exp->elts[pc+2].type;
+ (*pos) += 2 + (tem + sizeof (union exp_element)) / sizeof (union exp_element);
+ arg1 = value_struct_elt_for_address (domain, expect_type, name);
+ if (arg1)
+ return arg1;
+ error ("no field `%s' in structure", name);
+ }
+ else
+ return evaluate_subexp_for_address (exp, pos, noside);
+
+ case UNOP_SIZEOF:
+ if (noside == EVAL_SKIP)
+ {
+ evaluate_subexp (0, exp, pos, EVAL_SKIP);
+ goto nosideret;
+ }
+ return evaluate_subexp_for_sizeof (exp, pos);
+
+ case UNOP_CAST:
+ (*pos) += 2;
+ arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ return value_cast (exp->elts[pc + 1].type, arg1);
+
+ case UNOP_MEMVAL:
+ (*pos) += 2;
+ arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ return value_zero (exp->elts[pc + 1].type, lval_memory);
+ else
+ return value_at (exp->elts[pc + 1].type,
+ (CORE_ADDR) value_as_long (arg1));
+
+ case UNOP_PREINCREMENT:
+ arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+ if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+ return arg1;
+ else if (unop_user_defined_p (op, arg1))
+ {
+ return value_x_unop (arg1, op);
+ }
+ else
+ {
+ arg2 = value_add (arg1, value_from_long (builtin_type_char,
+ (LONGEST) 1));
+ return value_assign (arg1, arg2);
+ }
+
+ case UNOP_PREDECREMENT:
+ arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+ if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+ return arg1;
+ else if (unop_user_defined_p (op, arg1))
+ {
+ return value_x_unop (arg1, op);
+ }
+ else
+ {
+ arg2 = value_sub (arg1, value_from_long (builtin_type_char,
+ (LONGEST) 1));
+ return value_assign (arg1, arg2);
+ }
+
+ case UNOP_POSTINCREMENT:
+ arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+ if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+ return arg1;
+ else if (unop_user_defined_p (op, arg1))
+ {
+ return value_x_unop (arg1, op);
+ }
+ else
+ {
+ arg2 = value_add (arg1, value_from_long (builtin_type_char,
+ (LONGEST) 1));
+ value_assign (arg1, arg2);
+ return arg1;
+ }
+
+ case UNOP_POSTDECREMENT:
+ arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+ if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+ return arg1;
+ else if (unop_user_defined_p (op, arg1))
+ {
+ return value_x_unop (arg1, op);
+ }
+ else
+ {
+ arg2 = value_sub (arg1, value_from_long (builtin_type_char,
+ (LONGEST) 1));
+ value_assign (arg1, arg2);
+ return arg1;
+ }
+
+ case OP_THIS:
+ (*pos) += 1;
+ return value_of_this (1);
+
+ default:
+ error ("internal error: I do not know how to evaluate what you gave me");
+ }
+
+ nosideret:
+ return value_from_long (builtin_type_long, (LONGEST) 1);
+}
+
+/* Evaluate a subexpression of EXP, at index *POS,
+ and return the address of that subexpression.
+ Advance *POS over the subexpression.
+ If the subexpression isn't an lvalue, get an error.
+ NOSIDE may be EVAL_AVOID_SIDE_EFFECTS;
+ then only the type of the result need be correct. */
+
+static value
+evaluate_subexp_for_address (exp, pos, noside)
+ register struct expression *exp;
+ register int *pos;
+ enum noside noside;
+{
+ enum exp_opcode op;
+ register int pc;
+
+ pc = (*pos);
+ op = exp->elts[pc].opcode;
+
+ switch (op)
+ {
+ case UNOP_IND:
+ (*pos)++;
+ return evaluate_subexp (0, exp, pos, noside);
+
+ case UNOP_MEMVAL:
+ (*pos) += 3;
+ return value_cast (lookup_pointer_type (exp->elts[pc + 1].type),
+ evaluate_subexp (0, exp, pos, noside));
+
+ case OP_VAR_VALUE:
+ (*pos) += 3;
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ {
+ struct type *type =
+ lookup_pointer_type (SYMBOL_TYPE (exp->elts[pc + 1].symbol));
+ enum address_class sym_class =
+ SYMBOL_CLASS (exp->elts[pc + 1].symbol);
+
+ if (sym_class == LOC_CONST
+ || sym_class == LOC_CONST_BYTES
+ || sym_class == LOC_REGISTER
+ || sym_class == LOC_REGPARM)
+ error ("Attempt to take address of register or constant.");
+
+ return
+ value_zero (type, not_lval);
+ }
+ else
+ return locate_var_value (exp->elts[pc + 1].symbol, (CORE_ADDR) 0);
+
+ default:
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ {
+ value x = evaluate_subexp (0, exp, pos, noside);
+ if (VALUE_LVAL (x) == lval_memory)
+ return value_zero (TYPE_POINTER_TYPE (VALUE_TYPE (x)),
+ not_lval);
+ else
+ error ("Attempt to take address of non-lval");
+ }
+ return value_addr (evaluate_subexp (0, exp, pos, noside));
+ }
+}
+
+/* Evaluate like `evaluate_subexp' except coercing arrays to pointers.
+ When used in contexts where arrays will be coerced anyway,
+ this is equivalent to `evaluate_subexp'
+ but much faster because it avoids actually fetching array contents. */
+
+static value
+evaluate_subexp_with_coercion (exp, pos, noside)
+ register struct expression *exp;
+ register int *pos;
+ enum noside noside;
+{
+ register enum exp_opcode op;
+ register int pc;
+ register value val;
+
+ pc = (*pos);
+ op = exp->elts[pc].opcode;
+
+ switch (op)
+ {
+ case OP_VAR_VALUE:
+ if (TYPE_CODE (SYMBOL_TYPE (exp->elts[pc + 1].symbol)) == TYPE_CODE_ARRAY)
+ {
+ (*pos) += 3;
+ val = locate_var_value (exp->elts[pc + 1].symbol, (CORE_ADDR) 0);
+ return value_cast (lookup_pointer_type (TYPE_TARGET_TYPE (SYMBOL_TYPE (exp->elts[pc + 1].symbol))),
+ val);
+ }
+ }
+
+ return evaluate_subexp (0, exp, pos, noside);
+}
+
+/* Evaluate a subexpression of EXP, at index *POS,
+ and return a value for the size of that subexpression.
+ Advance *POS over the subexpression. */
+
+static value
+evaluate_subexp_for_sizeof (exp, pos)
+ register struct expression *exp;
+ register int *pos;
+{
+ enum exp_opcode op;
+ register int pc;
+ value val;
+
+ pc = (*pos);
+ op = exp->elts[pc].opcode;
+
+ switch (op)
+ {
+ /* This case is handled specially
+ so that we avoid creating a value for the result type.
+ If the result type is very big, it's desirable not to
+ create a value unnecessarily. */
+ case UNOP_IND:
+ (*pos)++;
+ val = evaluate_subexp (0, exp, pos, EVAL_AVOID_SIDE_EFFECTS);
+ return value_from_long (builtin_type_int, (LONGEST)
+ TYPE_LENGTH (TYPE_TARGET_TYPE (VALUE_TYPE (val))));
+
+ case UNOP_MEMVAL:
+ (*pos) += 3;
+ return value_from_long (builtin_type_int,
+ (LONGEST) TYPE_LENGTH (exp->elts[pc + 1].type));
+
+ case OP_VAR_VALUE:
+ (*pos) += 3;
+ return value_from_long (builtin_type_int,
+ (LONGEST) TYPE_LENGTH (SYMBOL_TYPE (exp->elts[pc + 1].symbol)));
+
+ default:
+ val = evaluate_subexp (0, exp, pos, EVAL_AVOID_SIDE_EFFECTS);
+ return value_from_long (builtin_type_int,
+ (LONGEST) TYPE_LENGTH (VALUE_TYPE (val)));
+ }
+}
diff --git a/gnu/usr.bin/gdb/expprint.c b/gnu/usr.bin/gdb/expprint.c
new file mode 100644
index 0000000..2c63cf8
--- /dev/null
+++ b/gnu/usr.bin/gdb/expprint.c
@@ -0,0 +1,324 @@
+/* Print in infix form a struct expression.
+ Copyright (C) 1986, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "expression.h"
+#include "value.h"
+
+
+/* These codes indicate operator precedences, least tightly binding first. */
+/* Adding 1 to a precedence value is done for binary operators,
+ on the operand which is more tightly bound, so that operators
+ of equal precedence within that operand will get parentheses. */
+/* PREC_HYPER and PREC_ABOVE_COMMA are not the precedence of any operator;
+ they are used as the "surrounding precedence" to force
+ various kinds of things to be parenthesized. */
+enum precedence
+{ PREC_NULL, PREC_COMMA, PREC_ABOVE_COMMA, PREC_ASSIGN, PREC_OR, PREC_AND,
+ PREC_LOGIOR, PREC_LOGAND, PREC_LOGXOR, PREC_EQUAL, PREC_ORDER,
+ PREC_SHIFT, PREC_ADD, PREC_MUL, PREC_REPEAT,
+ PREC_HYPER, PREC_PREFIX, PREC_SUFFIX };
+
+/* Table mapping opcodes into strings for printing operators
+ and precedences of the operators. */
+
+struct op_print
+{
+ char *string;
+ enum exp_opcode opcode;
+ /* Precedence of operator. These values are used only by comparisons. */
+ enum precedence precedence;
+ int right_assoc;
+};
+
+static struct op_print op_print_tab[] =
+ {
+ {",", BINOP_COMMA, PREC_COMMA, 0},
+ {"=", BINOP_ASSIGN, PREC_ASSIGN, 1},
+ {"||", BINOP_OR, PREC_OR, 0},
+ {"&&", BINOP_AND, PREC_AND, 0},
+ {"|", BINOP_LOGIOR, PREC_LOGIOR, 0},
+ {"&", BINOP_LOGAND, PREC_LOGAND, 0},
+ {"^", BINOP_LOGXOR, PREC_LOGXOR, 0},
+ {"==", BINOP_EQUAL, PREC_EQUAL, 0},
+ {"!=", BINOP_NOTEQUAL, PREC_EQUAL, 0},
+ {"<=", BINOP_LEQ, PREC_ORDER, 0},
+ {">=", BINOP_GEQ, PREC_ORDER, 0},
+ {">", BINOP_GTR, PREC_ORDER, 0},
+ {"<", BINOP_LESS, PREC_ORDER, 0},
+ {">>", BINOP_RSH, PREC_SHIFT, 0},
+ {"<<", BINOP_LSH, PREC_SHIFT, 0},
+ {"+", BINOP_ADD, PREC_ADD, 0},
+ {"-", BINOP_SUB, PREC_ADD, 0},
+ {"*", BINOP_MUL, PREC_MUL, 0},
+ {"/", BINOP_DIV, PREC_MUL, 0},
+ {"%", BINOP_REM, PREC_MUL, 0},
+ {"@", BINOP_REPEAT, PREC_REPEAT, 0},
+ {"-", UNOP_NEG, PREC_PREFIX, 0},
+ {"!", UNOP_ZEROP, PREC_PREFIX, 0},
+ {"~", UNOP_LOGNOT, PREC_PREFIX, 0},
+ {"*", UNOP_IND, PREC_PREFIX, 0},
+ {"&", UNOP_ADDR, PREC_PREFIX, 0},
+ {"sizeof ", UNOP_SIZEOF, PREC_PREFIX, 0},
+ {"++", UNOP_PREINCREMENT, PREC_PREFIX, 0},
+ {"--", UNOP_PREDECREMENT, PREC_PREFIX, 0},
+ /* C++ */
+ {"::", BINOP_SCOPE, PREC_PREFIX, 0},
+ };
+
+static void print_subexp ();
+
+void
+print_expression (exp, stream)
+ struct expression *exp;
+ FILE *stream;
+{
+ int pc = 0;
+ print_subexp (exp, &pc, stream, PREC_NULL);
+}
+
+/* Print the subexpression of EXP that starts in position POS, on STREAM.
+ PREC is the precedence of the surrounding operator;
+ if the precedence of the main operator of this subexpression is less,
+ parentheses are needed here. */
+
+static void
+print_subexp (exp, pos, stream, prec)
+ register struct expression *exp;
+ register int *pos;
+ FILE *stream;
+ enum precedence prec;
+{
+ register int tem;
+ register int pc;
+ int nargs;
+ register char *op_str;
+ int assign_modify = 0;
+ enum exp_opcode opcode;
+ enum precedence myprec;
+ /* Set to 1 for a right-associative operator. */
+ int assoc;
+
+ pc = (*pos)++;
+ opcode = exp->elts[pc].opcode;
+ switch (opcode)
+ {
+ case OP_SCOPE:
+ myprec = PREC_PREFIX;
+ assoc = 0;
+ (*pos) += 2;
+ print_subexp (exp, pos, stream, (int) myprec + assoc);
+ fprintf (stream, " :: ");
+ nargs = strlen (&exp->elts[pc + 2].string);
+ (*pos) += 1 + (nargs + sizeof (union exp_element)) / sizeof (union exp_element);
+
+ fprintf (stream, &exp->elts[pc + 2].string);
+ return;
+
+ case OP_LONG:
+ (*pos) += 3;
+ value_print (value_from_long (exp->elts[pc + 1].type,
+ exp->elts[pc + 2].longconst),
+ stream, 0, Val_no_prettyprint);
+ return;
+
+ case OP_DOUBLE:
+ (*pos) += 3;
+ value_print (value_from_double (exp->elts[pc + 1].type,
+ exp->elts[pc + 2].doubleconst),
+ stream, 0, Val_no_prettyprint);
+ return;
+
+ case OP_VAR_VALUE:
+ (*pos) += 2;
+ fprintf (stream, "%s", SYMBOL_NAME (exp->elts[pc + 1].symbol));
+ return;
+
+ case OP_LAST:
+ (*pos) += 2;
+ fprintf (stream, "$%d", (int) exp->elts[pc + 1].longconst);
+ return;
+
+ case OP_REGISTER:
+ (*pos) += 2;
+ fprintf (stream, "$%s", reg_names[exp->elts[pc + 1].longconst]);
+ return;
+
+ case OP_INTERNALVAR:
+ (*pos) += 2;
+ fprintf (stream, "$%s",
+ internalvar_name (exp->elts[pc + 1].internalvar));
+ return;
+
+ case OP_FUNCALL:
+ (*pos) += 2;
+ nargs = exp->elts[pc + 1].longconst;
+ print_subexp (exp, pos, stream, PREC_SUFFIX);
+ fprintf (stream, " (");
+ for (tem = 0; tem < nargs; tem++)
+ {
+ if (tem > 0)
+ fprintf (stream, ", ");
+ print_subexp (exp, pos, stream, PREC_ABOVE_COMMA);
+ }
+ fprintf (stream, ")");
+ return;
+
+ case OP_STRING:
+ nargs = strlen (&exp->elts[pc + 1].string);
+ (*pos) += 2 + (nargs + sizeof (union exp_element)) / sizeof (union exp_element);
+ fprintf (stream, "\"");
+ for (tem = 0; tem < nargs; tem++)
+ printchar ((&exp->elts[pc + 1].string)[tem], stream, '"');
+ fprintf (stream, "\"");
+ return;
+
+ case TERNOP_COND:
+ if ((int) prec > (int) PREC_COMMA)
+ fprintf (stream, "(");
+ /* Print the subexpressions, forcing parentheses
+ around any binary operations within them.
+ This is more parentheses than are strictly necessary,
+ but it looks clearer. */
+ print_subexp (exp, pos, stream, PREC_HYPER);
+ fprintf (stream, " ? ");
+ print_subexp (exp, pos, stream, PREC_HYPER);
+ fprintf (stream, " : ");
+ print_subexp (exp, pos, stream, PREC_HYPER);
+ if ((int) prec > (int) PREC_COMMA)
+ fprintf (stream, ")");
+ return;
+
+ case STRUCTOP_STRUCT:
+ tem = strlen (&exp->elts[pc + 1].string);
+ (*pos) += 2 + (tem + sizeof (union exp_element)) / sizeof (union exp_element);
+ print_subexp (exp, pos, stream, PREC_SUFFIX);
+ fprintf (stream, ".%s", &exp->elts[pc + 1].string);
+ return;
+
+ case STRUCTOP_PTR:
+ tem = strlen (&exp->elts[pc + 1].string);
+ (*pos) += 2 + (tem + sizeof (union exp_element)) / sizeof (union exp_element);
+ print_subexp (exp, pos, stream, PREC_SUFFIX);
+ fprintf (stream, "->%s", &exp->elts[pc + 1].string);
+ return;
+
+ case BINOP_SUBSCRIPT:
+ print_subexp (exp, pos, stream, PREC_SUFFIX);
+ fprintf (stream, "[");
+ print_subexp (exp, pos, stream, PREC_ABOVE_COMMA);
+ fprintf (stream, "]");
+ return;
+
+ case UNOP_POSTINCREMENT:
+ print_subexp (exp, pos, stream, PREC_SUFFIX);
+ fprintf (stream, "++");
+ return;
+
+ case UNOP_POSTDECREMENT:
+ print_subexp (exp, pos, stream, PREC_SUFFIX);
+ fprintf (stream, "--");
+ return;
+
+ case UNOP_CAST:
+ (*pos) += 2;
+ if ((int) prec > (int) PREC_PREFIX)
+ fprintf (stream, "(");
+ fprintf (stream, "(");
+ type_print (exp->elts[pc + 1].type, "", stream, 0);
+ fprintf (stream, ") ");
+ print_subexp (exp, pos, stream, PREC_PREFIX);
+ if ((int) prec > (int) PREC_PREFIX)
+ fprintf (stream, ")");
+ return;
+
+ case UNOP_MEMVAL:
+ (*pos) += 2;
+ if ((int) prec > (int) PREC_PREFIX)
+ fprintf (stream, "(");
+ fprintf (stream, "{");
+ type_print (exp->elts[pc + 1].type, "", stream, 0);
+ fprintf (stream, "} ");
+ print_subexp (exp, pos, stream, PREC_PREFIX);
+ if ((int) prec > (int) PREC_PREFIX)
+ fprintf (stream, ")");
+ return;
+
+ case BINOP_ASSIGN_MODIFY:
+ opcode = exp->elts[pc + 1].opcode;
+ (*pos) += 2;
+ myprec = PREC_ASSIGN;
+ assoc = 1;
+ assign_modify = 1;
+ for (tem = 0; tem < sizeof op_print_tab / sizeof op_print_tab[0]; tem++)
+ if (op_print_tab[tem].opcode == opcode)
+ {
+ op_str = op_print_tab[tem].string;
+ break;
+ }
+
+ case OP_THIS:
+ ++(*pos);
+ fprintf (stream, "this");
+ return;
+
+ default:
+ for (tem = 0; tem < sizeof op_print_tab / sizeof op_print_tab[0]; tem++)
+ if (op_print_tab[tem].opcode == opcode)
+ {
+ op_str = op_print_tab[tem].string;
+ myprec = op_print_tab[tem].precedence;
+ assoc = op_print_tab[tem].right_assoc;
+ break;
+ }
+ }
+
+ if ((int) myprec < (int) prec)
+ fprintf (stream, "(");
+ if ((int) opcode > (int) BINOP_END)
+ {
+ /* Unary prefix operator. */
+ fprintf (stream, "%s", op_str);
+ print_subexp (exp, pos, stream, PREC_PREFIX);
+ }
+ else
+ {
+ /* Binary operator. */
+ /* Print left operand.
+ If operator is right-associative,
+ increment precedence for this operand. */
+ print_subexp (exp, pos, stream, (int) myprec + assoc);
+ /* Print the operator itself. */
+ if (assign_modify)
+ fprintf (stream, " %s= ", op_str);
+ else if (op_str[0] == ',')
+ fprintf (stream, "%s ", op_str);
+ else
+ fprintf (stream, " %s ", op_str);
+ /* Print right operand.
+ If operator is left-associative,
+ increment precedence for this operand. */
+ print_subexp (exp, pos, stream, (int) myprec + !assoc);
+ }
+ if ((int) myprec < (int) prec)
+ fprintf (stream, ")");
+}
diff --git a/gnu/usr.bin/gdb/expread.y b/gnu/usr.bin/gdb/expread.y
new file mode 100644
index 0000000..96a12c4
--- /dev/null
+++ b/gnu/usr.bin/gdb/expread.y
@@ -0,0 +1,1782 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+/* Parse C expressions for GDB.
+ Copyright (C) 1986, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Parse a C expression from text in a string,
+ and return the result as a struct expression pointer.
+ That structure contains arithmetic operations in reverse polish,
+ with constants represented by operations that are followed by special data.
+ See expression.h for the details of the format.
+ What is important here is that it can be built up sequentially
+ during the process of parsing; the lower levels of the tree always
+ come first in the result. */
+
+%{
+#ifndef lint
+static char sccsid[] = "@(#)expread.y 6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "frame.h"
+#include "expression.h"
+
+#include <a.out.h>
+
+static struct expression *expout;
+static int expout_size;
+static int expout_ptr;
+
+static int yylex ();
+static void yyerror ();
+static void write_exp_elt ();
+static void write_exp_elt_opcode ();
+static void write_exp_elt_sym ();
+static void write_exp_elt_longcst ();
+static void write_exp_elt_dblcst ();
+static void write_exp_elt_type ();
+static void write_exp_elt_intern ();
+static void write_exp_string ();
+static void start_arglist ();
+static int end_arglist ();
+static void free_funcalls ();
+static char *copy_name ();
+
+/* If this is nonzero, this block is used as the lexical context
+ for symbol names. */
+
+static struct block *expression_context_block;
+
+/* The innermost context required by the stack and register variables
+ we've encountered so far. */
+struct block *innermost_block;
+
+/* The block in which the most recently discovered symbol was found. */
+struct block *block_found;
+
+/* Number of arguments seen so far in innermost function call. */
+static int arglist_len;
+
+/* Data structure for saving values of arglist_len
+ for function calls whose arguments contain other function calls. */
+
+struct funcall
+ {
+ struct funcall *next;
+ int arglist_len;
+ };
+
+struct funcall *funcall_chain;
+
+/* This kind of datum is used to represent the name
+ of a symbol token. */
+
+struct stoken
+ {
+ char *ptr;
+ int length;
+ };
+
+/* For parsing of complicated types.
+ An array should be preceded in the list by the size of the array. */
+enum type_pieces
+ {tp_end = -1, tp_pointer, tp_reference, tp_array, tp_function};
+static enum type_pieces *type_stack;
+static int type_stack_depth, type_stack_size;
+
+static void push_type ();
+static enum type_pieces pop_type ();
+
+/* Allow debugging of parsing. */
+#define YYDEBUG 1
+%}
+
+/* Although the yacc "value" of an expression is not used,
+ since the result is stored in the structure being created,
+ other node types do have values. */
+
+%union
+ {
+ LONGEST lval;
+ unsigned LONGEST ulval;
+ double dval;
+ struct symbol *sym;
+ struct type *tval;
+ struct stoken sval;
+ int voidval;
+ struct block *bval;
+ enum exp_opcode opcode;
+ struct internalvar *ivar;
+
+ struct type **tvec;
+ int *ivec;
+ }
+
+%type <voidval> exp exp1 start variable
+%type <tval> type typebase
+%type <tvec> nonempty_typelist
+%type <bval> block
+
+/* Fancy type parsing. */
+%type <voidval> func_mod direct_abs_decl abs_decl
+%type <tval> ptype
+%type <lval> array_mod
+
+%token <lval> INT CHAR
+%token <ulval> UINT
+%token <dval> FLOAT
+
+/* Both NAME and TYPENAME tokens represent symbols in the input,
+ and both convey their data as strings.
+ But a TYPENAME is a string that happens to be defined as a typedef
+ or builtin type name (such as int or char)
+ and a NAME is any other symbol.
+
+ Contexts where this distinction is not important can use the
+ nonterminal "name", which matches either NAME or TYPENAME. */
+
+%token <sval> NAME TYPENAME BLOCKNAME STRING
+%type <sval> name name_not_typename typename
+
+%token STRUCT UNION ENUM SIZEOF UNSIGNED COLONCOLON
+
+/* Special type cases, put in to allow the parser to distinguish different
+ legal basetypes. */
+%token SIGNED LONG SHORT INT_KEYWORD
+
+%token <lval> LAST REGNAME
+
+%token <ivar> VARIABLE
+
+%token <opcode> ASSIGN_MODIFY
+
+/* C++ */
+%token THIS
+
+%left ','
+%left ABOVE_COMMA
+%right '=' ASSIGN_MODIFY
+%right '?'
+%left OR
+%left AND
+%left '|'
+%left '^'
+%left '&'
+%left EQUAL NOTEQUAL
+%left '<' '>' LEQ GEQ
+%left LSH RSH
+%left '@'
+%left '+' '-'
+%left '*' '/' '%'
+%right UNARY INCREMENT DECREMENT
+%right ARROW '.' '[' '('
+%left COLONCOLON
+
+%%
+
+start : exp1
+ ;
+
+/* Expressions, including the comma operator. */
+exp1 : exp
+ | exp1 ',' exp
+ { write_exp_elt_opcode (BINOP_COMMA); }
+ ;
+
+/* Expressions, not including the comma operator. */
+exp : '*' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_IND); }
+
+exp : '&' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_ADDR); }
+
+exp : '-' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_NEG); }
+ ;
+
+exp : '!' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_ZEROP); }
+ ;
+
+exp : '~' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_LOGNOT); }
+ ;
+
+exp : INCREMENT exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_PREINCREMENT); }
+ ;
+
+exp : DECREMENT exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_PREDECREMENT); }
+ ;
+
+exp : exp INCREMENT %prec UNARY
+ { write_exp_elt_opcode (UNOP_POSTINCREMENT); }
+ ;
+
+exp : exp DECREMENT %prec UNARY
+ { write_exp_elt_opcode (UNOP_POSTDECREMENT); }
+ ;
+
+exp : SIZEOF exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_SIZEOF); }
+ ;
+
+exp : exp ARROW name
+ { write_exp_elt_opcode (STRUCTOP_PTR);
+ write_exp_string ($3);
+ write_exp_elt_opcode (STRUCTOP_PTR); }
+ ;
+
+exp : exp ARROW '*' exp
+ { write_exp_elt_opcode (STRUCTOP_MPTR); }
+ ;
+
+exp : exp '.' name
+ { write_exp_elt_opcode (STRUCTOP_STRUCT);
+ write_exp_string ($3);
+ write_exp_elt_opcode (STRUCTOP_STRUCT); }
+ ;
+
+exp : exp '.' '*' exp
+ { write_exp_elt_opcode (STRUCTOP_MEMBER); }
+ ;
+
+exp : exp '[' exp1 ']'
+ { write_exp_elt_opcode (BINOP_SUBSCRIPT); }
+ ;
+
+exp : exp '('
+ /* This is to save the value of arglist_len
+ being accumulated by an outer function call. */
+ { start_arglist (); }
+ arglist ')' %prec ARROW
+ { write_exp_elt_opcode (OP_FUNCALL);
+ write_exp_elt_longcst ((LONGEST) end_arglist ());
+ write_exp_elt_opcode (OP_FUNCALL); }
+ ;
+
+arglist :
+ ;
+
+arglist : exp
+ { arglist_len = 1; }
+ ;
+
+arglist : arglist ',' exp %prec ABOVE_COMMA
+ { arglist_len++; }
+ ;
+
+exp : '{' type '}' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_MEMVAL);
+ write_exp_elt_type ($2);
+ write_exp_elt_opcode (UNOP_MEMVAL); }
+ ;
+
+exp : '(' type ')' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_CAST);
+ write_exp_elt_type ($2);
+ write_exp_elt_opcode (UNOP_CAST); }
+ ;
+
+exp : '(' exp1 ')'
+ { }
+ ;
+
+/* Binary operators in order of decreasing precedence. */
+
+exp : exp '@' exp
+ { write_exp_elt_opcode (BINOP_REPEAT); }
+ ;
+
+exp : exp '*' exp
+ { write_exp_elt_opcode (BINOP_MUL); }
+ ;
+
+exp : exp '/' exp
+ { write_exp_elt_opcode (BINOP_DIV); }
+ ;
+
+exp : exp '%' exp
+ { write_exp_elt_opcode (BINOP_REM); }
+ ;
+
+exp : exp '+' exp
+ { write_exp_elt_opcode (BINOP_ADD); }
+ ;
+
+exp : exp '-' exp
+ { write_exp_elt_opcode (BINOP_SUB); }
+ ;
+
+exp : exp LSH exp
+ { write_exp_elt_opcode (BINOP_LSH); }
+ ;
+
+exp : exp RSH exp
+ { write_exp_elt_opcode (BINOP_RSH); }
+ ;
+
+exp : exp EQUAL exp
+ { write_exp_elt_opcode (BINOP_EQUAL); }
+ ;
+
+exp : exp NOTEQUAL exp
+ { write_exp_elt_opcode (BINOP_NOTEQUAL); }
+ ;
+
+exp : exp LEQ exp
+ { write_exp_elt_opcode (BINOP_LEQ); }
+ ;
+
+exp : exp GEQ exp
+ { write_exp_elt_opcode (BINOP_GEQ); }
+ ;
+
+exp : exp '<' exp
+ { write_exp_elt_opcode (BINOP_LESS); }
+ ;
+
+exp : exp '>' exp
+ { write_exp_elt_opcode (BINOP_GTR); }
+ ;
+
+exp : exp '&' exp
+ { write_exp_elt_opcode (BINOP_LOGAND); }
+ ;
+
+exp : exp '^' exp
+ { write_exp_elt_opcode (BINOP_LOGXOR); }
+ ;
+
+exp : exp '|' exp
+ { write_exp_elt_opcode (BINOP_LOGIOR); }
+ ;
+
+exp : exp AND exp
+ { write_exp_elt_opcode (BINOP_AND); }
+ ;
+
+exp : exp OR exp
+ { write_exp_elt_opcode (BINOP_OR); }
+ ;
+
+exp : exp '?' exp ':' exp %prec '?'
+ { write_exp_elt_opcode (TERNOP_COND); }
+ ;
+
+exp : exp '=' exp
+ { write_exp_elt_opcode (BINOP_ASSIGN); }
+ ;
+
+exp : exp ASSIGN_MODIFY exp
+ { write_exp_elt_opcode (BINOP_ASSIGN_MODIFY);
+ write_exp_elt_opcode ($2);
+ write_exp_elt_opcode (BINOP_ASSIGN_MODIFY); }
+ ;
+
+exp : INT
+ { write_exp_elt_opcode (OP_LONG);
+ if ($1 == (int) $1 || $1 == (unsigned int) $1)
+ write_exp_elt_type (builtin_type_int);
+ else
+ write_exp_elt_type (BUILTIN_TYPE_LONGEST);
+ write_exp_elt_longcst ((LONGEST) $1);
+ write_exp_elt_opcode (OP_LONG); }
+ ;
+
+exp : UINT
+ {
+ write_exp_elt_opcode (OP_LONG);
+ if ($1 == (unsigned int) $1)
+ write_exp_elt_type (builtin_type_unsigned_int);
+ else
+ write_exp_elt_type (BUILTIN_TYPE_UNSIGNED_LONGEST);
+ write_exp_elt_longcst ((LONGEST) $1);
+ write_exp_elt_opcode (OP_LONG);
+ }
+ ;
+
+exp : CHAR
+ { write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_char);
+ write_exp_elt_longcst ((LONGEST) $1);
+ write_exp_elt_opcode (OP_LONG); }
+ ;
+
+exp : FLOAT
+ { write_exp_elt_opcode (OP_DOUBLE);
+ write_exp_elt_type (builtin_type_double);
+ write_exp_elt_dblcst ($1);
+ write_exp_elt_opcode (OP_DOUBLE); }
+ ;
+
+exp : variable
+ ;
+
+exp : LAST
+ { write_exp_elt_opcode (OP_LAST);
+ write_exp_elt_longcst ((LONGEST) $1);
+ write_exp_elt_opcode (OP_LAST); }
+ ;
+
+exp : REGNAME
+ { write_exp_elt_opcode (OP_REGISTER);
+ write_exp_elt_longcst ((LONGEST) $1);
+ write_exp_elt_opcode (OP_REGISTER); }
+ ;
+
+exp : VARIABLE
+ { write_exp_elt_opcode (OP_INTERNALVAR);
+ write_exp_elt_intern ($1);
+ write_exp_elt_opcode (OP_INTERNALVAR); }
+ ;
+
+exp : SIZEOF '(' type ')' %prec UNARY
+ { write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_int);
+ write_exp_elt_longcst ((LONGEST) TYPE_LENGTH ($3));
+ write_exp_elt_opcode (OP_LONG); }
+ ;
+
+exp : STRING
+ { write_exp_elt_opcode (OP_STRING);
+ write_exp_string ($1);
+ write_exp_elt_opcode (OP_STRING); }
+ ;
+
+/* C++. */
+exp : THIS
+ { write_exp_elt_opcode (OP_THIS);
+ write_exp_elt_opcode (OP_THIS); }
+ ;
+
+/* end of C++. */
+
+block : BLOCKNAME
+ {
+ struct symtab *tem = lookup_symtab (copy_name ($1));
+ struct symbol *sym;
+
+ if (tem)
+ $$ = BLOCKVECTOR_BLOCK (BLOCKVECTOR (tem), 1);
+ else
+ {
+ sym = lookup_symbol (copy_name ($1),
+ expression_context_block,
+ VAR_NAMESPACE, 0);
+ if (sym && SYMBOL_CLASS (sym) == LOC_BLOCK)
+ $$ = SYMBOL_BLOCK_VALUE (sym);
+ else
+ error ("No file or function \"%s\".",
+ copy_name ($1));
+ }
+ }
+ ;
+
+block : block COLONCOLON name
+ { struct symbol *tem
+ = lookup_symbol (copy_name ($3), $1, VAR_NAMESPACE, 0);
+ if (!tem || SYMBOL_CLASS (tem) != LOC_BLOCK)
+ error ("No function \"%s\" in specified context.",
+ copy_name ($3));
+ $$ = SYMBOL_BLOCK_VALUE (tem); }
+ ;
+
+variable: block COLONCOLON name
+ { struct symbol *sym;
+ sym = lookup_symbol (copy_name ($3), $1, VAR_NAMESPACE, 0);
+ if (sym == 0)
+ error ("No symbol \"%s\" in specified context.",
+ copy_name ($3));
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ write_exp_elt_sym (sym);
+ write_exp_elt_opcode (OP_VAR_VALUE); }
+ ;
+
+variable: typebase COLONCOLON name
+ {
+ struct type *type = $1;
+ if (TYPE_CODE (type) != TYPE_CODE_STRUCT
+ && TYPE_CODE (type) != TYPE_CODE_UNION)
+ error ("`%s' is not defined as an aggregate type.",
+ TYPE_NAME (type));
+
+ write_exp_elt_opcode (OP_SCOPE);
+ write_exp_elt_type (type);
+ write_exp_string ($3);
+ write_exp_elt_opcode (OP_SCOPE);
+ }
+ | COLONCOLON name
+ {
+ char *name = copy_name ($2);
+ struct symbol *sym;
+ int i;
+
+ sym = lookup_symbol (name, 0, VAR_NAMESPACE, 0);
+ if (sym)
+ {
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ write_exp_elt_sym (sym);
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ break;
+ }
+ for (i = 0; i < misc_function_count; i++)
+ if (!strcmp (misc_function_vector[i].name, name))
+ break;
+
+ if (i < misc_function_count)
+ {
+ enum misc_function_type mft =
+ (enum misc_function_type)
+ misc_function_vector[i].type;
+
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_int);
+ write_exp_elt_longcst ((LONGEST) misc_function_vector[i].address);
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_opcode (UNOP_MEMVAL);
+ if (mft == mf_data || mft == mf_bss)
+ write_exp_elt_type (builtin_type_int);
+ else if (mft == mf_text)
+ write_exp_elt_type (lookup_function_type (builtin_type_int));
+ else
+ write_exp_elt_type (builtin_type_char);
+ write_exp_elt_opcode (UNOP_MEMVAL);
+ }
+ else
+ if (symtab_list == 0
+ && partial_symtab_list == 0)
+ error ("No symbol table is loaded. Use the \"symbol-file\" command.");
+ else
+ error ("No symbol \"%s\" in current context.", name);
+ }
+ ;
+
+variable: name_not_typename
+ { struct symbol *sym;
+ int is_a_field_of_this;
+
+ sym = lookup_symbol (copy_name ($1),
+ expression_context_block,
+ VAR_NAMESPACE,
+ &is_a_field_of_this);
+ if (sym)
+ {
+ switch (sym->class)
+ {
+ case LOC_REGISTER:
+ case LOC_ARG:
+ case LOC_LOCAL:
+ if (innermost_block == 0 ||
+ contained_in (block_found,
+ innermost_block))
+ innermost_block = block_found;
+ }
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ write_exp_elt_sym (sym);
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ }
+ else if (is_a_field_of_this)
+ {
+ /* C++: it hangs off of `this'. Must
+ not inadvertently convert from a method call
+ to data ref. */
+ if (innermost_block == 0 ||
+ contained_in (block_found, innermost_block))
+ innermost_block = block_found;
+ write_exp_elt_opcode (OP_THIS);
+ write_exp_elt_opcode (OP_THIS);
+ write_exp_elt_opcode (STRUCTOP_PTR);
+ write_exp_string ($1);
+ write_exp_elt_opcode (STRUCTOP_PTR);
+ }
+ else
+ {
+ register int i;
+ register char *arg = copy_name ($1);
+
+ for (i = 0; i < misc_function_count; i++)
+ if (!strcmp (misc_function_vector[i].name, arg))
+ break;
+
+ if (i < misc_function_count)
+ {
+ enum misc_function_type mft =
+ (enum misc_function_type)
+ misc_function_vector[i].type;
+
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_int);
+ write_exp_elt_longcst ((LONGEST) misc_function_vector[i].address);
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_opcode (UNOP_MEMVAL);
+ if (mft == mf_data || mft == mf_bss)
+ write_exp_elt_type (builtin_type_int);
+ else if (mft == mf_text)
+ write_exp_elt_type (lookup_function_type (builtin_type_int));
+ else
+ write_exp_elt_type (builtin_type_char);
+ write_exp_elt_opcode (UNOP_MEMVAL);
+ }
+ else if (symtab_list == 0
+ && partial_symtab_list == 0)
+ error ("No symbol table is loaded. Use the \"symbol-file\" command.");
+ else
+ error ("No symbol \"%s\" in current context.",
+ copy_name ($1));
+ }
+ }
+ ;
+
+
+ptype : typebase
+ | typebase abs_decl
+ {
+ /* This is where the interesting stuff happens. */
+ int done = 0;
+ int array_size;
+ struct type *follow_type = $1;
+
+ while (!done)
+ switch (pop_type ())
+ {
+ case tp_end:
+ done = 1;
+ break;
+ case tp_pointer:
+ follow_type = lookup_pointer_type (follow_type);
+ break;
+ case tp_reference:
+ follow_type = lookup_reference_type (follow_type);
+ break;
+ case tp_array:
+ array_size = (int) pop_type ();
+ if (array_size != -1)
+ follow_type = create_array_type (follow_type,
+ array_size);
+ else
+ follow_type = lookup_pointer_type (follow_type);
+ break;
+ case tp_function:
+ follow_type = lookup_function_type (follow_type);
+ break;
+ }
+ $$ = follow_type;
+ }
+ ;
+
+abs_decl: '*'
+ { push_type (tp_pointer); $$ = 0; }
+ | '*' abs_decl
+ { push_type (tp_pointer); $$ = $2; }
+ | direct_abs_decl
+ ;
+
+direct_abs_decl: '(' abs_decl ')'
+ { $$ = $2; }
+ | direct_abs_decl array_mod
+ {
+ push_type ((enum type_pieces) $2);
+ push_type (tp_array);
+ }
+ | array_mod
+ {
+ push_type ((enum type_pieces) $1);
+ push_type (tp_array);
+ $$ = 0;
+ }
+ | direct_abs_decl func_mod
+ { push_type (tp_function); }
+ | func_mod
+ { push_type (tp_function); }
+ ;
+
+array_mod: '[' ']'
+ { $$ = -1; }
+ | '[' INT ']'
+ { $$ = $2; }
+ ;
+
+func_mod: '(' ')'
+ { $$ = 0; }
+ ;
+
+type : ptype
+ | typebase COLONCOLON '*'
+ { $$ = lookup_member_type (builtin_type_int, $1); }
+ | type '(' typebase COLONCOLON '*' ')'
+ { $$ = lookup_member_type ($1, $3); }
+ | type '(' typebase COLONCOLON '*' ')' '(' ')'
+ { $$ = lookup_member_type
+ (lookup_function_type ($1), $3); }
+ | type '(' typebase COLONCOLON '*' ')' '(' nonempty_typelist ')'
+ { $$ = lookup_member_type
+ (lookup_function_type ($1), $3);
+ free ($8); }
+ ;
+
+typebase
+ : TYPENAME
+ { $$ = lookup_typename (copy_name ($1),
+ expression_context_block, 0); }
+ | INT_KEYWORD
+ { $$ = builtin_type_int; }
+ | LONG
+ { $$ = builtin_type_long; }
+ | SHORT
+ { $$ = builtin_type_short; }
+ | LONG INT_KEYWORD
+ { $$ = builtin_type_long; }
+ | UNSIGNED LONG INT_KEYWORD
+ { $$ = builtin_type_unsigned_long; }
+ | SHORT INT_KEYWORD
+ { $$ = builtin_type_short; }
+ | UNSIGNED SHORT INT_KEYWORD
+ { $$ = builtin_type_unsigned_short; }
+ | STRUCT name
+ { $$ = lookup_struct (copy_name ($2),
+ expression_context_block); }
+ | UNION name
+ { $$ = lookup_union (copy_name ($2),
+ expression_context_block); }
+ | ENUM name
+ { $$ = lookup_enum (copy_name ($2),
+ expression_context_block); }
+ | UNSIGNED typename
+ { $$ = lookup_unsigned_typename (copy_name ($2)); }
+ | UNSIGNED
+ { $$ = builtin_type_unsigned_int; }
+ | SIGNED typename
+ { $$ = lookup_typename (copy_name ($2),
+ expression_context_block, 0); }
+ | SIGNED
+ { $$ = builtin_type_int; }
+ ;
+
+typename: TYPENAME
+ | INT_KEYWORD
+ {
+ $$.ptr = "int";
+ $$.length = 3;
+ }
+ | LONG
+ {
+ $$.ptr = "long";
+ $$.length = 4;
+ }
+ | SHORT
+ {
+ $$.ptr = "short";
+ $$.length = 5;
+ }
+ ;
+
+nonempty_typelist
+ : type
+ { $$ = (struct type **)xmalloc (sizeof (struct type *) * 2);
+ $$[0] = (struct type *)0;
+ $$[1] = $1;
+ }
+ | nonempty_typelist ',' type
+ { int len = sizeof (struct type *) * ++($<ivec>1[0]);
+ $$ = (struct type **)xrealloc ($1, len);
+ $$[$<ivec>$[0]] = $3;
+ }
+ ;
+
+name : NAME
+ | BLOCKNAME
+ | TYPENAME
+ ;
+
+name_not_typename : NAME
+ | BLOCKNAME
+ ;
+
+%%
+
+/* Begin counting arguments for a function call,
+ saving the data about any containing call. */
+
+static void
+start_arglist ()
+{
+ register struct funcall *new = (struct funcall *) xmalloc (sizeof (struct funcall));
+
+ new->next = funcall_chain;
+ new->arglist_len = arglist_len;
+ arglist_len = 0;
+ funcall_chain = new;
+}
+
+/* Return the number of arguments in a function call just terminated,
+ and restore the data for the containing function call. */
+
+static int
+end_arglist ()
+{
+ register int val = arglist_len;
+ register struct funcall *call = funcall_chain;
+ funcall_chain = call->next;
+ arglist_len = call->arglist_len;
+ free (call);
+ return val;
+}
+
+/* Free everything in the funcall chain.
+ Used when there is an error inside parsing. */
+
+static void
+free_funcalls ()
+{
+ register struct funcall *call, *next;
+
+ for (call = funcall_chain; call; call = next)
+ {
+ next = call->next;
+ free (call);
+ }
+}
+
+/* This page contains the functions for adding data to the struct expression
+ being constructed. */
+
+/* Add one element to the end of the expression. */
+
+/* To avoid a bug in the Sun 4 compiler, we pass things that can fit into
+ a register through here */
+
+static void
+write_exp_elt (expelt)
+ union exp_element expelt;
+{
+ if (expout_ptr >= expout_size)
+ {
+ expout_size *= 2;
+ expout = (struct expression *) xrealloc (expout,
+ sizeof (struct expression)
+ + expout_size * sizeof (union exp_element));
+ }
+ expout->elts[expout_ptr++] = expelt;
+}
+
+static void
+write_exp_elt_opcode (expelt)
+ enum exp_opcode expelt;
+{
+ union exp_element tmp;
+
+ tmp.opcode = expelt;
+
+ write_exp_elt (tmp);
+}
+
+static void
+write_exp_elt_sym (expelt)
+ struct symbol *expelt;
+{
+ union exp_element tmp;
+
+ tmp.symbol = expelt;
+
+ write_exp_elt (tmp);
+}
+
+static void
+write_exp_elt_longcst (expelt)
+ LONGEST expelt;
+{
+ union exp_element tmp;
+
+ tmp.longconst = expelt;
+
+ write_exp_elt (tmp);
+}
+
+static void
+write_exp_elt_dblcst (expelt)
+ double expelt;
+{
+ union exp_element tmp;
+
+ tmp.doubleconst = expelt;
+
+ write_exp_elt (tmp);
+}
+
+static void
+write_exp_elt_type (expelt)
+ struct type *expelt;
+{
+ union exp_element tmp;
+
+ tmp.type = expelt;
+
+ write_exp_elt (tmp);
+}
+
+static void
+write_exp_elt_intern (expelt)
+ struct internalvar *expelt;
+{
+ union exp_element tmp;
+
+ tmp.internalvar = expelt;
+
+ write_exp_elt (tmp);
+}
+
+/* Add a string constant to the end of the expression.
+ Follow it by its length in bytes, as a separate exp_element. */
+
+static void
+write_exp_string (str)
+ struct stoken str;
+{
+ register int len = str.length;
+ register int lenelt
+ = (len + sizeof (union exp_element)) / sizeof (union exp_element);
+
+ expout_ptr += lenelt;
+
+ if (expout_ptr >= expout_size)
+ {
+ expout_size = max (expout_size * 2, expout_ptr + 10);
+ expout = (struct expression *)
+ xrealloc (expout, (sizeof (struct expression)
+ + (expout_size * sizeof (union exp_element))));
+ }
+ bcopy (str.ptr, (char *) &expout->elts[expout_ptr - lenelt], len);
+ ((char *) &expout->elts[expout_ptr - lenelt])[len] = 0;
+ write_exp_elt_longcst ((LONGEST) len);
+}
+
+/* During parsing of a C expression, the pointer to the next character
+ is in this variable. */
+
+static char *lexptr;
+
+/* Tokens that refer to names do so with explicit pointer and length,
+ so they can share the storage that lexptr is parsing.
+
+ When it is necessary to pass a name to a function that expects
+ a null-terminated string, the substring is copied out
+ into a block of storage that namecopy points to.
+
+ namecopy is allocated once, guaranteed big enough, for each parsing. */
+
+static char *namecopy;
+
+/* Current depth in parentheses within the expression. */
+
+static int paren_depth;
+
+/* Nonzero means stop parsing on first comma (if not within parentheses). */
+
+static int comma_terminates;
+
+/* Take care of parsing a number (anything that starts with a digit).
+ Set yylval and return the token type; update lexptr.
+ LEN is the number of characters in it. */
+
+/*** Needs some error checking for the float case ***/
+
+static int
+parse_number (olen)
+ int olen;
+{
+ register char *p = lexptr;
+ register LONGEST n = 0;
+ register int c;
+ register int base = 10;
+ register int len = olen;
+ char *err_copy;
+ int unsigned_p = 0;
+
+ extern double atof ();
+
+ for (c = 0; c < len; c++)
+ if (p[c] == '.')
+ {
+ /* It's a float since it contains a point. */
+ yylval.dval = atof (p);
+ lexptr += len;
+ return FLOAT;
+ }
+
+ if (len >= 3 && (!strncmp (p, "0x", 2) || !strncmp (p, "0X", 2)))
+ {
+ p += 2;
+ base = 16;
+ len -= 2;
+ }
+ else if (*p == '0')
+ base = 8;
+
+ while (len-- > 0)
+ {
+ c = *p++;
+ if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
+ if (c != 'l' && c != 'u')
+ n *= base;
+ if (c >= '0' && c <= '9')
+ n += c - '0';
+ else
+ {
+ if (base == 16 && c >= 'a' && c <= 'f')
+ n += c - 'a' + 10;
+ else if (len == 0 && c == 'l')
+ ;
+ else if (len == 0 && c == 'u')
+ unsigned_p = 1;
+ else if (base == 10 && len != 0 && (c == 'e' || c == 'E'))
+ {
+ /* Scientific notation, where we are unlucky enough not
+ to have a '.' in the string. */
+ yylval.dval = atof (lexptr);
+ lexptr += olen;
+ return FLOAT;
+ }
+ else
+ {
+ err_copy = (char *) alloca (olen + 1);
+ bcopy (lexptr, err_copy, olen);
+ err_copy[olen] = 0;
+ error ("Invalid number \"%s\".", err_copy);
+ }
+ }
+ }
+
+ lexptr = p;
+ if (unsigned_p)
+ {
+ yylval.ulval = n;
+ return UINT;
+ }
+ else
+ {
+ yylval.lval = n;
+ return INT;
+ }
+}
+
+struct token
+{
+ char *operator;
+ int token;
+ enum exp_opcode opcode;
+};
+
+static struct token tokentab3[] =
+ {
+ {">>=", ASSIGN_MODIFY, BINOP_RSH},
+ {"<<=", ASSIGN_MODIFY, BINOP_LSH}
+ };
+
+static struct token tokentab2[] =
+ {
+ {"+=", ASSIGN_MODIFY, BINOP_ADD},
+ {"-=", ASSIGN_MODIFY, BINOP_SUB},
+ {"*=", ASSIGN_MODIFY, BINOP_MUL},
+ {"/=", ASSIGN_MODIFY, BINOP_DIV},
+ {"%=", ASSIGN_MODIFY, BINOP_REM},
+ {"|=", ASSIGN_MODIFY, BINOP_LOGIOR},
+ {"&=", ASSIGN_MODIFY, BINOP_LOGAND},
+ {"^=", ASSIGN_MODIFY, BINOP_LOGXOR},
+ {"++", INCREMENT, BINOP_END},
+ {"--", DECREMENT, BINOP_END},
+ {"->", ARROW, BINOP_END},
+ {"&&", AND, BINOP_END},
+ {"||", OR, BINOP_END},
+ {"::", COLONCOLON, BINOP_END},
+ {"<<", LSH, BINOP_END},
+ {">>", RSH, BINOP_END},
+ {"==", EQUAL, BINOP_END},
+ {"!=", NOTEQUAL, BINOP_END},
+ {"<=", LEQ, BINOP_END},
+ {">=", GEQ, BINOP_END}
+ };
+
+/* assign machine-independent names to certain registers
+ * (unless overridden by the REGISTER_NAMES table)
+ */
+struct std_regs {
+ char *name;
+ int regnum;
+} std_regs[] = {
+#ifdef PC_REGNUM
+ { "pc", PC_REGNUM },
+#endif
+#ifdef FP_REGNUM
+ { "fp", FP_REGNUM },
+#endif
+#ifdef SP_REGNUM
+ { "sp", SP_REGNUM },
+#endif
+#ifdef PS_REGNUM
+ { "ps", PS_REGNUM },
+#endif
+};
+
+#define NUM_STD_REGS (sizeof std_regs / sizeof std_regs[0])
+
+/* Read one token, getting characters through lexptr. */
+
+static int
+yylex ()
+{
+ register int c;
+ register int namelen;
+ register int i;
+ register char *tokstart;
+
+ retry:
+
+ tokstart = lexptr;
+ /* See if it is a special token of length 3. */
+ for (i = 0; i < sizeof tokentab3 / sizeof tokentab3[0]; i++)
+ if (!strncmp (tokstart, tokentab3[i].operator, 3))
+ {
+ lexptr += 3;
+ yylval.opcode = tokentab3[i].opcode;
+ return tokentab3[i].token;
+ }
+
+ /* See if it is a special token of length 2. */
+ for (i = 0; i < sizeof tokentab2 / sizeof tokentab2[0]; i++)
+ if (!strncmp (tokstart, tokentab2[i].operator, 2))
+ {
+ lexptr += 2;
+ yylval.opcode = tokentab2[i].opcode;
+ return tokentab2[i].token;
+ }
+
+ switch (c = *tokstart)
+ {
+ case 0:
+ return 0;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ lexptr++;
+ goto retry;
+
+ case '\'':
+ lexptr++;
+ c = *lexptr++;
+ if (c == '\\')
+ c = parse_escape (&lexptr);
+ yylval.lval = c;
+ c = *lexptr++;
+ if (c != '\'')
+ error ("Invalid character constant.");
+ return CHAR;
+
+ case '(':
+ paren_depth++;
+ lexptr++;
+ return c;
+
+ case ')':
+ if (paren_depth == 0)
+ return 0;
+ paren_depth--;
+ lexptr++;
+ return c;
+
+ case ',':
+ if (comma_terminates && paren_depth == 0)
+ return 0;
+ lexptr++;
+ return c;
+
+ case '.':
+ /* Might be a floating point number. */
+ if (lexptr[1] >= '0' && lexptr[1] <= '9')
+ break; /* Falls into number code. */
+
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '%':
+ case '|':
+ case '&':
+ case '^':
+ case '~':
+ case '!':
+ case '@':
+ case '<':
+ case '>':
+ case '[':
+ case ']':
+ case '?':
+ case ':':
+ case '=':
+ case '{':
+ case '}':
+ lexptr++;
+ return c;
+
+ case '"':
+ for (namelen = 1; (c = tokstart[namelen]) != '"'; namelen++)
+ if (c == '\\')
+ {
+ c = tokstart[++namelen];
+ if (c >= '0' && c <= '9')
+ {
+ c = tokstart[++namelen];
+ if (c >= '0' && c <= '9')
+ c = tokstart[++namelen];
+ }
+ }
+ yylval.sval.ptr = tokstart + 1;
+ yylval.sval.length = namelen - 1;
+ lexptr += namelen + 1;
+ return STRING;
+ }
+
+ /* Is it a number? */
+ /* Note: We have already dealt with the case of the token '.'.
+ See case '.' above. */
+ if ((c >= '0' && c <= '9') || c == '.')
+ {
+ /* It's a number. */
+ int got_dot = 0, got_e = 0;
+ register char *p = tokstart;
+ int hex = c == '0' && (p[1] == 'x' || p[1] == 'X');
+ if (hex)
+ p += 2;
+ for (;; ++p)
+ {
+ if (!hex && !got_e && (*p == 'e' || *p == 'E'))
+ got_dot = got_e = 1;
+ else if (!hex && !got_dot && *p == '.')
+ got_dot = 1;
+ else if (got_e && (p[-1] == 'e' || p[-1] == 'E')
+ && (*p == '-' || *p == '+'))
+ /* This is the sign of the exponent, not the end of the
+ number. */
+ continue;
+ else if (!got_dot && !got_e && (*p=='l'||*p=='L')){
+ ++p; break;
+ }
+ else if (!got_dot && !got_e && !hex && (*p=='u'||*p=='U')){
+ ++p; break;
+ }
+ else if (*p < '0' || *p > '9'
+ && (!hex || ((*p < 'a' || *p > 'f')
+ && (*p < 'A' || *p > 'F'))))
+ break;
+ }
+ return parse_number (p - tokstart);
+ }
+
+ if (!(c == '_' || c == '$'
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
+ /* We must have come across a bad character (e.g. ';'). */
+ error ("Invalid character '%c' in expression.", c);
+
+ /* It's a name. See how long it is. */
+ namelen = 0;
+ for (c = tokstart[namelen];
+ (c == '_' || c == '$' || (c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
+ c = tokstart[++namelen])
+ ;
+
+ /* The token "if" terminates the expression and is NOT
+ removed from the input stream. */
+ if (namelen == 2 && tokstart[0] == 'i' && tokstart[1] == 'f')
+ {
+ return 0;
+ }
+
+ lexptr += namelen;
+
+ /* Handle the tokens $digits; also $ (short for $0) and $$ (short for $$1)
+ and $$digits (equivalent to $<-digits> if you could type that).
+ Make token type LAST, and put the number (the digits) in yylval. */
+
+ if (*tokstart == '$')
+ {
+ register int negate = 0;
+ c = 1;
+ /* Double dollar means negate the number and add -1 as well.
+ Thus $$ alone means -1. */
+ if (namelen >= 2 && tokstart[1] == '$')
+ {
+ negate = 1;
+ c = 2;
+ }
+ if (c == namelen)
+ {
+ /* Just dollars (one or two) */
+ yylval.lval = - negate;
+ return LAST;
+ }
+ /* Is the rest of the token digits? */
+ for (; c < namelen; c++)
+ if (!(tokstart[c] >= '0' && tokstart[c] <= '9'))
+ break;
+ if (c == namelen)
+ {
+ yylval.lval = atoi (tokstart + 1 + negate);
+ if (negate)
+ yylval.lval = - yylval.lval;
+ return LAST;
+ }
+ }
+
+ /* Handle tokens that refer to machine registers:
+ $ followed by a register name. */
+
+ if (*tokstart == '$') {
+ for (c = 0; c < NUM_REGS; c++)
+ if (namelen - 1 == strlen (reg_names[c])
+ && !strncmp (tokstart + 1, reg_names[c], namelen - 1))
+ {
+ yylval.lval = c;
+ return REGNAME;
+ }
+ for (c = 0; c < NUM_STD_REGS; c++)
+ if (namelen - 1 == strlen (std_regs[c].name)
+ && !strncmp (tokstart + 1, std_regs[c].name, namelen - 1))
+ {
+ yylval.lval = std_regs[c].regnum;
+ return REGNAME;
+ }
+ }
+ /* Catch specific keywords. Should be done with a data structure. */
+ switch (namelen)
+ {
+ case 8:
+ if (!strncmp (tokstart, "unsigned", 8))
+ return UNSIGNED;
+ break;
+ case 6:
+ if (!strncmp (tokstart, "struct", 6))
+ return STRUCT;
+ if (!strncmp (tokstart, "signed", 6))
+ return SIGNED;
+ if (!strncmp (tokstart, "sizeof", 6))
+ return SIZEOF;
+ break;
+ case 5:
+ if (!strncmp (tokstart, "union", 5))
+ return UNION;
+ if (!strncmp (tokstart, "short", 5))
+ return SHORT;
+ break;
+ case 4:
+ if (!strncmp (tokstart, "enum", 4))
+ return ENUM;
+ if (!strncmp (tokstart, "long", 4))
+ return LONG;
+ if (!strncmp (tokstart, "this", 4)
+ && lookup_symbol ("$this", expression_context_block,
+ VAR_NAMESPACE, 0))
+ return THIS;
+ break;
+ case 3:
+ if (!strncmp (tokstart, "int", 3))
+ return INT_KEYWORD;
+ break;
+ default:
+ break;
+ }
+
+ yylval.sval.ptr = tokstart;
+ yylval.sval.length = namelen;
+
+ /* Any other names starting in $ are debugger internal variables. */
+
+ if (*tokstart == '$')
+ {
+ yylval.ivar = (struct internalvar *) lookup_internalvar (copy_name (yylval.sval) + 1);
+ return VARIABLE;
+ }
+
+ /* Use token-type BLOCKNAME for symbols that happen to be defined as
+ functions or symtabs. If this is not so, then ...
+ Use token-type TYPENAME for symbols that happen to be defined
+ currently as names of types; NAME for other symbols.
+ The caller is not constrained to care about the distinction. */
+ {
+ char *tmp = copy_name (yylval.sval);
+ struct symbol *sym;
+
+ if (lookup_partial_symtab (tmp))
+ return BLOCKNAME;
+ sym = lookup_symbol (tmp, expression_context_block,
+ VAR_NAMESPACE, 0);
+ if (sym && SYMBOL_CLASS (sym) == LOC_BLOCK)
+ return BLOCKNAME;
+ if (lookup_typename (copy_name (yylval.sval), expression_context_block, 1))
+ return TYPENAME;
+ return NAME;
+ }
+}
+
+static void
+yyerror ()
+{
+ error ("Invalid syntax in expression.");
+}
+
+/* Return a null-terminated temporary copy of the name
+ of a string token. */
+
+static char *
+copy_name (token)
+ struct stoken token;
+{
+ bcopy (token.ptr, namecopy, token.length);
+ namecopy[token.length] = 0;
+ return namecopy;
+}
+
+/* Reverse an expression from suffix form (in which it is constructed)
+ to prefix form (in which we can conveniently print or execute it). */
+
+static void prefixify_subexp ();
+
+static void
+prefixify_expression (expr)
+ register struct expression *expr;
+{
+ register int len = sizeof (struct expression) +
+ expr->nelts * sizeof (union exp_element);
+ register struct expression *temp;
+ register int inpos = expr->nelts, outpos = 0;
+
+ temp = (struct expression *) alloca (len);
+
+ /* Copy the original expression into temp. */
+ bcopy (expr, temp, len);
+
+ prefixify_subexp (temp, expr, inpos, outpos);
+}
+
+/* Return the number of exp_elements in the subexpression of EXPR
+ whose last exp_element is at index ENDPOS - 1 in EXPR. */
+
+static int
+length_of_subexp (expr, endpos)
+ register struct expression *expr;
+ register int endpos;
+{
+ register int oplen = 1;
+ register int args = 0;
+ register int i;
+
+ if (endpos < 0)
+ error ("?error in length_of_subexp");
+
+ i = (int) expr->elts[endpos - 1].opcode;
+
+ switch (i)
+ {
+ /* C++ */
+ case OP_SCOPE:
+ oplen = 4 + ((expr->elts[endpos - 2].longconst
+ + sizeof (union exp_element))
+ / sizeof (union exp_element));
+ break;
+
+ case OP_LONG:
+ case OP_DOUBLE:
+ oplen = 4;
+ break;
+
+ case OP_VAR_VALUE:
+ case OP_LAST:
+ case OP_REGISTER:
+ case OP_INTERNALVAR:
+ oplen = 3;
+ break;
+
+ case OP_FUNCALL:
+ oplen = 3;
+ args = 1 + expr->elts[endpos - 2].longconst;
+ break;
+
+ case UNOP_CAST:
+ case UNOP_MEMVAL:
+ oplen = 3;
+ args = 1;
+ break;
+
+ case STRUCTOP_STRUCT:
+ case STRUCTOP_PTR:
+ args = 1;
+ case OP_STRING:
+ oplen = 3 + ((expr->elts[endpos - 2].longconst
+ + sizeof (union exp_element))
+ / sizeof (union exp_element));
+ break;
+
+ case TERNOP_COND:
+ args = 3;
+ break;
+
+ case BINOP_ASSIGN_MODIFY:
+ oplen = 3;
+ args = 2;
+ break;
+
+ /* C++ */
+ case OP_THIS:
+ oplen = 2;
+ break;
+
+ default:
+ args = 1 + (i < (int) BINOP_END);
+ }
+
+ while (args > 0)
+ {
+ oplen += length_of_subexp (expr, endpos - oplen);
+ args--;
+ }
+
+ return oplen;
+}
+
+/* Copy the subexpression ending just before index INEND in INEXPR
+ into OUTEXPR, starting at index OUTBEG.
+ In the process, convert it from suffix to prefix form. */
+
+static void
+prefixify_subexp (inexpr, outexpr, inend, outbeg)
+ register struct expression *inexpr;
+ struct expression *outexpr;
+ register int inend;
+ int outbeg;
+{
+ register int oplen = 1;
+ register int args = 0;
+ register int i;
+ int *arglens;
+ enum exp_opcode opcode;
+
+ /* Compute how long the last operation is (in OPLEN),
+ and also how many preceding subexpressions serve as
+ arguments for it (in ARGS). */
+
+ opcode = inexpr->elts[inend - 1].opcode;
+ switch (opcode)
+ {
+ /* C++ */
+ case OP_SCOPE:
+ oplen = 4 + ((inexpr->elts[inend - 2].longconst
+ + sizeof (union exp_element))
+ / sizeof (union exp_element));
+ break;
+
+ case OP_LONG:
+ case OP_DOUBLE:
+ oplen = 4;
+ break;
+
+ case OP_VAR_VALUE:
+ case OP_LAST:
+ case OP_REGISTER:
+ case OP_INTERNALVAR:
+ oplen = 3;
+ break;
+
+ case OP_FUNCALL:
+ oplen = 3;
+ args = 1 + inexpr->elts[inend - 2].longconst;
+ break;
+
+ case UNOP_CAST:
+ case UNOP_MEMVAL:
+ oplen = 3;
+ args = 1;
+ break;
+
+ case STRUCTOP_STRUCT:
+ case STRUCTOP_PTR:
+ args = 1;
+ case OP_STRING:
+ oplen = 3 + ((inexpr->elts[inend - 2].longconst
+ + sizeof (union exp_element))
+ / sizeof (union exp_element));
+
+ break;
+
+ case TERNOP_COND:
+ args = 3;
+ break;
+
+ case BINOP_ASSIGN_MODIFY:
+ oplen = 3;
+ args = 2;
+ break;
+
+ /* C++ */
+ case OP_THIS:
+ oplen = 2;
+ break;
+
+ default:
+ args = 1 + ((int) opcode < (int) BINOP_END);
+ }
+
+ /* Copy the final operator itself, from the end of the input
+ to the beginning of the output. */
+ inend -= oplen;
+ bcopy (&inexpr->elts[inend], &outexpr->elts[outbeg],
+ oplen * sizeof (union exp_element));
+ outbeg += oplen;
+
+ /* Find the lengths of the arg subexpressions. */
+ arglens = (int *) alloca (args * sizeof (int));
+ for (i = args - 1; i >= 0; i--)
+ {
+ oplen = length_of_subexp (inexpr, inend);
+ arglens[i] = oplen;
+ inend -= oplen;
+ }
+
+ /* Now copy each subexpression, preserving the order of
+ the subexpressions, but prefixifying each one.
+ In this loop, inend starts at the beginning of
+ the expression this level is working on
+ and marches forward over the arguments.
+ outbeg does similarly in the output. */
+ for (i = 0; i < args; i++)
+ {
+ oplen = arglens[i];
+ inend += oplen;
+ prefixify_subexp (inexpr, outexpr, inend, outbeg);
+ outbeg += oplen;
+ }
+}
+
+/* This page contains the two entry points to this file. */
+
+/* Read a C expression from the string *STRINGPTR points to,
+ parse it, and return a pointer to a struct expression that we malloc.
+ Use block BLOCK as the lexical context for variable names;
+ if BLOCK is zero, use the block of the selected stack frame.
+ Meanwhile, advance *STRINGPTR to point after the expression,
+ at the first nonwhite character that is not part of the expression
+ (possibly a null character).
+
+ If COMMA is nonzero, stop if a comma is reached. */
+
+struct expression *
+parse_c_1 (stringptr, block, comma)
+ char **stringptr;
+ struct block *block;
+{
+ struct cleanup *old_chain;
+
+ lexptr = *stringptr;
+
+ paren_depth = 0;
+ type_stack_depth = 0;
+
+ comma_terminates = comma;
+
+ if (lexptr == 0 || *lexptr == 0)
+ error_no_arg ("expression to compute");
+
+ old_chain = make_cleanup (free_funcalls, 0);
+ funcall_chain = 0;
+
+ expression_context_block = block ? block : get_selected_block ();
+
+ namecopy = (char *) alloca (strlen (lexptr) + 1);
+ expout_size = 10;
+ expout_ptr = 0;
+ expout = (struct expression *)
+ xmalloc (sizeof (struct expression)
+ + expout_size * sizeof (union exp_element));
+ make_cleanup (free_current_contents, &expout);
+ if (yyparse ())
+ yyerror ();
+ discard_cleanups (old_chain);
+ expout->nelts = expout_ptr;
+ expout = (struct expression *)
+ xrealloc (expout,
+ sizeof (struct expression)
+ + expout_ptr * sizeof (union exp_element));
+ prefixify_expression (expout);
+ *stringptr = lexptr;
+ return expout;
+}
+
+/* Parse STRING as an expression, and complain if this fails
+ to use up all of the contents of STRING. */
+
+struct expression *
+parse_c_expression (string)
+ char *string;
+{
+ register struct expression *exp;
+ exp = parse_c_1 (&string, 0, 0);
+ if (*string)
+ error ("Junk after end of expression.");
+ return exp;
+}
+
+static void
+push_type (tp)
+ enum type_pieces tp;
+{
+ if (type_stack_depth == type_stack_size)
+ {
+ type_stack_size *= 2;
+ type_stack = (enum type_pieces *)
+ xrealloc (type_stack, type_stack_size * sizeof (enum type_pieces));
+ }
+ type_stack[type_stack_depth++] = tp;
+}
+
+static enum type_pieces
+pop_type ()
+{
+ if (type_stack_depth)
+ return type_stack[--type_stack_depth];
+ return tp_end;
+}
+
+void
+_initialize_expread ()
+{
+ type_stack_size = 80;
+ type_stack_depth = 0;
+ type_stack = (enum type_pieces *)
+ xmalloc (type_stack_size * sizeof (enum type_pieces));
+}
diff --git a/gnu/usr.bin/gdb/expression.h b/gnu/usr.bin/gdb/expression.h
new file mode 100644
index 0000000..5a5e20e
--- /dev/null
+++ b/gnu/usr.bin/gdb/expression.h
@@ -0,0 +1,191 @@
+/* Definitions for expressions stored in reversed prefix form, for GDB.
+ Copyright (C) 1986, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Definitions for saved C expressions. */
+
+/* An expression is represented as a vector of union exp_element's.
+ Each exp_element is an opcode, except that some opcodes cause
+ the following exp_element to be treated as a long or double constant
+ or as a variable. The opcodes are obeyed, using a stack for temporaries.
+ The value is left on the temporary stack at the end. */
+
+/* When it is necessary to include a string,
+ it can occupy as many exp_elements as it needs.
+ We find the length of the string using strlen,
+ divide to find out how many exp_elements are used up,
+ and skip that many. Strings, like numbers, are indicated
+ by the preceding opcode. */
+
+enum exp_opcode
+{
+/* BINOP_... operate on two values computed by following subexpressions,
+ replacing them by one result value. They take no immediate arguments. */
+ BINOP_ADD, /* + */
+ BINOP_SUB, /* - */
+ BINOP_MUL, /* * */
+ BINOP_DIV, /* / */
+ BINOP_REM, /* % */
+ BINOP_LSH, /* << */
+ BINOP_RSH, /* >> */
+ BINOP_AND, /* && */
+ BINOP_OR, /* || */
+ BINOP_LOGAND, /* & */
+ BINOP_LOGIOR, /* | */
+ BINOP_LOGXOR, /* ^ */
+ BINOP_EQUAL, /* == */
+ BINOP_NOTEQUAL, /* != */
+ BINOP_LESS, /* < */
+ BINOP_GTR, /* > */
+ BINOP_LEQ, /* <= */
+ BINOP_GEQ, /* >= */
+ BINOP_REPEAT, /* @ */
+ BINOP_ASSIGN, /* = */
+ BINOP_COMMA, /* , */
+ BINOP_SUBSCRIPT, /* x[y] */
+ BINOP_EXP, /* Exponentiation */
+
+/* C++. */
+ BINOP_MIN, /* <? */
+ BINOP_MAX, /* >? */
+ BINOP_SCOPE, /* :: */
+
+ /* STRUCTOP_MEMBER is used for pointer-to-member constructs.
+ X . * Y translates into X STRUCTOP_MEMBER Y. */
+ STRUCTOP_MEMBER,
+ /* STRUCTOP_MPTR is used for pointer-to-member constructs
+ when X is a pointer instead of an aggregate. */
+ STRUCTOP_MPTR,
+/* end of C++. */
+
+ BINOP_END,
+
+ BINOP_ASSIGN_MODIFY, /* +=, -=, *=, and so on.
+ The following exp_element is another opcode,
+ a BINOP_, saying how to modify.
+ Then comes another BINOP_ASSIGN_MODIFY,
+ making three exp_elements in total. */
+
+/* Operates on three values computed by following subexpressions. */
+ TERNOP_COND, /* ?: */
+
+/* The OP_... series take immediate following arguments.
+ After the arguments come another OP_... (the same one)
+ so that the grouping can be recognized from the end. */
+
+/* OP_LONG is followed by a type pointer in the next exp_element
+ and the long constant value in the following exp_element.
+ Then comes another OP_LONG.
+ Thus, the operation occupies four exp_elements. */
+
+ OP_LONG,
+/* OP_DOUBLE is similar but takes a double constant instead of a long one. */
+ OP_DOUBLE,
+/* OP_VAR_VALUE takes one struct symbol * in the following exp_element,
+ followed by another OP_VAR_VALUE, making three exp_elements. */
+ OP_VAR_VALUE,
+/* OP_LAST is followed by an integer in the next exp_element.
+ The integer is zero for the last value printed,
+ or it is the absolute number of a history element.
+ With another OP_LAST at the end, this makes three exp_elements. */
+ OP_LAST,
+/* OP_REGISTER is followed by an integer in the next exp_element.
+ This is the number of a register to fetch (as an int).
+ With another OP_REGISTER at the end, this makes three exp_elements. */
+ OP_REGISTER,
+/* OP_INTERNALVAR is followed by an internalvar ptr in the next exp_element.
+ With another OP_INTERNALVAR at the end, this makes three exp_elements. */
+ OP_INTERNALVAR,
+/* OP_FUNCALL is followed by an integer in the next exp_element.
+ The integer is the number of args to the function call.
+ That many plus one values from following subexpressions
+ are used, the first one being the function.
+ The integer is followed by a repeat of OP_FUNCALL,
+ making three exp_elements. */
+ OP_FUNCALL,
+/* OP_STRING represents a string constant.
+ Its format is the same as that of a STRUCTOP, but the string
+ data is just made into a string constant when the operation
+ is executed. */
+ OP_STRING,
+
+/* UNOP_CAST is followed by a type pointer in the next exp_element.
+ With another UNOP_CAST at the end, this makes three exp_elements.
+ It casts the value of the following subexpression. */
+ UNOP_CAST,
+/* UNOP_MEMVAL is followed by a type pointer in the next exp_element
+ With another UNOP_MEMVAL at the end, this makes three exp_elements.
+ It casts the contents of the word addressed by the value of the
+ following subexpression. */
+ UNOP_MEMVAL,
+/* UNOP_... operate on one value from a following subexpression
+ and replace it with a result. They take no immediate arguments. */
+ UNOP_NEG, /* Unary - */
+ UNOP_ZEROP, /* Unary ! */
+ UNOP_LOGNOT, /* Unary ~ */
+ UNOP_IND, /* Unary * */
+ UNOP_ADDR, /* Unary & */
+ UNOP_PREINCREMENT, /* ++ before an expression */
+ UNOP_POSTINCREMENT, /* ++ after an expression */
+ UNOP_PREDECREMENT, /* -- before an expression */
+ UNOP_POSTDECREMENT, /* -- after an expression */
+ UNOP_SIZEOF, /* Unary sizeof (followed by expression) */
+
+/* STRUCTOP_... operate on a value from a following subexpression
+ by extracting a structure component specified by a string
+ that appears in the following exp_elements (as many as needed).
+ STRUCTOP_STRUCT is used for "." and STRUCTOP_PTR for "->".
+ They differ only in the error message given in case the value is
+ not suitable or the structure component specified is not found.
+
+ The length of the string follows in the next exp_element,
+ (after the string), followed by another STRUCTOP_... code. */
+ STRUCTOP_STRUCT,
+ STRUCTOP_PTR,
+
+/* C++ */
+ /* OP_THIS is just a placeholder for the class instance variable.
+ It just comes in a tight (OP_THIS, OP_THIS) pair. */
+ OP_THIS,
+
+ /* OP_SCOPE surrounds a type name and a field name. The type
+ name is encoded as one element, but the field name stays as
+ a string, which, of course, is variable length. */
+ OP_SCOPE,
+
+};
+
+union exp_element
+{
+ enum exp_opcode opcode;
+ struct symbol *symbol;
+ LONGEST longconst;
+ double doubleconst;
+ char string;
+ struct type *type;
+ struct internalvar *internalvar;
+};
+
+struct expression
+{
+ int nelts;
+ union exp_element elts[1];
+};
+
+struct expression *parse_c_expression ();
+struct expression *parse_c_1 ();
diff --git a/gnu/usr.bin/gdb/findvar.c b/gnu/usr.bin/gdb/findvar.c
new file mode 100644
index 0000000..0157d10
--- /dev/null
+++ b/gnu/usr.bin/gdb/findvar.c
@@ -0,0 +1,579 @@
+/* Find a variable's value in memory, for GDB, the GNU debugger.
+ Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "frame.h"
+#include "value.h"
+
+CORE_ADDR read_register ();
+
+/* Return the address in which frame FRAME's value of register REGNUM
+ has been saved in memory. Or return zero if it has not been saved.
+ If REGNUM specifies the SP, the value we return is actually
+ the SP value, not an address where it was saved. */
+
+CORE_ADDR
+find_saved_register (frame, regnum)
+ FRAME frame;
+ int regnum;
+{
+ struct frame_info *fi;
+ struct frame_saved_regs saved_regs;
+
+ register FRAME frame1 = 0;
+ register CORE_ADDR addr = 0;
+
+#ifdef HAVE_REGISTER_WINDOWS
+ /* We assume that a register in a register window will only be saved
+ in one place (since the name changes and disappears as you go
+ towards inner frames), so we only call get_frame_saved_regs on
+ the current frame. This is directly in contradiction to the
+ usage below, which assumes that registers used in a frame must be
+ saved in a lower (more interior) frame. This change is a result
+ of working on a register window machine; get_frame_saved_regs
+ always returns the registers saved within a frame, within the
+ context (register namespace) of that frame. */
+
+ /* However, note that we don't want this to return anything if
+ nothing is saved (if there's a frame inside of this one). Also,
+ callers to this routine asking for the stack pointer want the
+ stack pointer saved for *this* frame; this is returned from the
+ next frame. */
+
+
+ if (REGISTER_IN_WINDOW_P(regnum))
+ {
+ frame1 = get_next_frame (frame);
+ if (!frame1) return 0; /* Registers of this frame are
+ active. */
+
+ /* Get the SP from the next frame in; it will be this
+ current frame. */
+ if (regnum != SP_REGNUM)
+ frame1 = frame;
+
+ fi = get_frame_info (frame1);
+ get_frame_saved_regs (fi, &saved_regs);
+ return (saved_regs.regs[regnum] ?
+ saved_regs.regs[regnum] : 0);
+ }
+#endif /* HAVE_REGISTER_WINDOWS */
+
+ /* Note that this next routine assumes that registers used in
+ frame x will be saved only in the frame that x calls and
+ frames interior to it. This is not true on the sparc, but the
+ above macro takes care of it, so we should be all right. */
+ while (1)
+ {
+ QUIT;
+ frame1 = get_prev_frame (frame1);
+ if (frame1 == 0 || frame1 == frame)
+ break;
+ fi = get_frame_info (frame1);
+ get_frame_saved_regs (fi, &saved_regs);
+ if (saved_regs.regs[regnum])
+ addr = saved_regs.regs[regnum];
+ }
+
+ return addr;
+}
+
+/* Copy the bytes of register REGNUM, relative to the current stack frame,
+ into our memory at MYADDR.
+ The number of bytes copied is REGISTER_RAW_SIZE (REGNUM). */
+
+void
+read_relative_register_raw_bytes (regnum, myaddr)
+ int regnum;
+ char *myaddr;
+{
+ register CORE_ADDR addr;
+
+ if (regnum == FP_REGNUM)
+ {
+ bcopy (&FRAME_FP(selected_frame), myaddr, sizeof (CORE_ADDR));
+ return;
+ }
+
+ addr = find_saved_register (selected_frame, regnum);
+
+ if (addr)
+ {
+ if (regnum == SP_REGNUM)
+ {
+ CORE_ADDR buffer = addr;
+ bcopy (&buffer, myaddr, sizeof (CORE_ADDR));
+ }
+ else
+ read_memory (addr, myaddr, REGISTER_RAW_SIZE (regnum));
+ return;
+ }
+ read_register_bytes (REGISTER_BYTE (regnum),
+ myaddr, REGISTER_RAW_SIZE (regnum));
+}
+
+/* Return a `value' with the contents of register REGNUM
+ in its virtual format, with the type specified by
+ REGISTER_VIRTUAL_TYPE. */
+
+value
+value_of_register (regnum)
+ int regnum;
+{
+ register CORE_ADDR addr;
+ register value val;
+ char raw_buffer[MAX_REGISTER_RAW_SIZE];
+ char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE];
+
+ if (! (have_inferior_p () || have_core_file_p ()))
+ error ("Can't get value of register without inferior or core file");
+
+ addr = find_saved_register (selected_frame, regnum);
+ if (addr)
+ {
+ if (regnum == SP_REGNUM)
+ return value_from_long (builtin_type_int, (LONGEST) addr);
+ read_memory (addr, raw_buffer, REGISTER_RAW_SIZE (regnum));
+ }
+ else
+ read_register_bytes (REGISTER_BYTE (regnum), raw_buffer,
+ REGISTER_RAW_SIZE (regnum));
+
+ REGISTER_CONVERT_TO_VIRTUAL (regnum, raw_buffer, virtual_buffer);
+ val = allocate_value (REGISTER_VIRTUAL_TYPE (regnum));
+ bcopy (virtual_buffer, VALUE_CONTENTS (val), REGISTER_VIRTUAL_SIZE (regnum));
+ VALUE_LVAL (val) = addr ? lval_memory : lval_register;
+ VALUE_ADDRESS (val) = addr ? addr : REGISTER_BYTE (regnum);
+ VALUE_REGNO (val) = regnum;
+ return val;
+}
+
+/* Low level examining and depositing of registers.
+
+ Note that you must call `fetch_registers' once
+ before examining or depositing any registers. */
+
+char registers[REGISTER_BYTES];
+
+/* Copy LEN bytes of consecutive data from registers
+ starting with the REGBYTE'th byte of register data
+ into memory at MYADDR. */
+
+void
+read_register_bytes (regbyte, myaddr, len)
+ int regbyte;
+ char *myaddr;
+ int len;
+{
+ bcopy (&registers[regbyte], myaddr, len);
+}
+
+/* Copy LEN bytes of consecutive data from memory at MYADDR
+ into registers starting with the REGBYTE'th byte of register data. */
+
+void
+write_register_bytes (regbyte, myaddr, len)
+ int regbyte;
+ char *myaddr;
+ int len;
+{
+ bcopy (myaddr, &registers[regbyte], len);
+ if (have_inferior_p ())
+ store_inferior_registers (-1);
+}
+
+/* Return the contents of register REGNO,
+ regarding it as an integer. */
+
+CORE_ADDR
+read_register (regno)
+ int regno;
+{
+ /* This loses when REGISTER_RAW_SIZE (regno) != sizeof (int) */
+ return *(int *) &registers[REGISTER_BYTE (regno)];
+}
+
+/* Store VALUE in the register number REGNO, regarded as an integer. */
+
+void
+write_register (regno, val)
+ int regno, val;
+{
+ /* This loses when REGISTER_RAW_SIZE (regno) != sizeof (int) */
+#if defined(sun4)
+ /* This is a no-op on a Sun 4. */
+ if (regno == 0)
+ return;
+#endif
+
+ *(int *) &registers[REGISTER_BYTE (regno)] = val;
+
+ if (have_inferior_p ())
+ store_inferior_registers (regno);
+}
+
+/* Record that register REGNO contains VAL.
+ This is used when the value is obtained from the inferior or core dump,
+ so there is no need to store the value there. */
+
+void
+supply_register (regno, val)
+ int regno;
+ char *val;
+{
+ bcopy (val, &registers[REGISTER_BYTE (regno)], REGISTER_RAW_SIZE (regno));
+}
+
+/* Given a struct symbol for a variable,
+ and a stack frame id, read the value of the variable
+ and return a (pointer to a) struct value containing the value. */
+
+value
+read_var_value (var, frame)
+ register struct symbol *var;
+ FRAME frame;
+{
+ register value v;
+
+ struct frame_info *fi;
+
+ struct type *type = SYMBOL_TYPE (var);
+ register CORE_ADDR addr = 0;
+ int val = SYMBOL_VALUE (var);
+ register int len;
+
+ v = allocate_value (type);
+ VALUE_LVAL (v) = lval_memory; /* The most likely possibility. */
+ len = TYPE_LENGTH (type);
+
+ if (frame == 0) frame = selected_frame;
+
+ switch (SYMBOL_CLASS (var))
+ {
+ case LOC_CONST:
+ case LOC_LABEL:
+ bcopy (&val, VALUE_CONTENTS (v), len);
+ VALUE_LVAL (v) = not_lval;
+ return v;
+
+ case LOC_CONST_BYTES:
+ bcopy (val, VALUE_CONTENTS (v), len);
+ VALUE_LVAL (v) = not_lval;
+ return v;
+
+ case LOC_STATIC:
+ addr = val;
+ break;
+
+/* Nonzero if a struct which is located in a register or a LOC_ARG
+ really contains
+ the address of the struct, not the struct itself. GCC_P is nonzero
+ if the function was compiled with GCC. */
+#if !defined (REG_STRUCT_HAS_ADDR)
+#define REG_STRUCT_HAS_ADDR(gcc_p) 0
+#endif
+
+ case LOC_ARG:
+ fi = get_frame_info (frame);
+ addr = val + FRAME_ARGS_ADDRESS (fi);
+ break;
+
+ case LOC_REF_ARG:
+ fi = get_frame_info (frame);
+ addr = val + FRAME_ARGS_ADDRESS (fi);
+ addr = read_memory_integer (addr, sizeof (CORE_ADDR));
+ break;
+
+ case LOC_LOCAL:
+ fi = get_frame_info (frame);
+ addr = val + FRAME_LOCALS_ADDRESS (fi);
+ break;
+
+ case LOC_TYPEDEF:
+ error ("Cannot look up value of a typedef");
+
+ case LOC_BLOCK:
+ VALUE_ADDRESS (v) = BLOCK_START (SYMBOL_BLOCK_VALUE (var));
+ return v;
+
+ case LOC_REGISTER:
+ case LOC_REGPARM:
+ {
+ struct block *b = get_frame_block (frame);
+
+ v = value_from_register (type, val, frame);
+
+ if (REG_STRUCT_HAS_ADDR(b->gcc_compile_flag)
+ && TYPE_CODE (type) == TYPE_CODE_STRUCT)
+ addr = *(CORE_ADDR *)VALUE_CONTENTS (v);
+ else
+ return v;
+ }
+ }
+
+ read_memory (addr, VALUE_CONTENTS (v), len);
+ VALUE_ADDRESS (v) = addr;
+ return v;
+}
+
+/* Return a value of type TYPE, stored in register REGNUM, in frame
+ FRAME. */
+
+value
+value_from_register (type, regnum, frame)
+ struct type *type;
+ int regnum;
+ FRAME frame;
+{
+ char raw_buffer [MAX_REGISTER_RAW_SIZE];
+ char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE];
+ CORE_ADDR addr;
+ value v = allocate_value (type);
+ int len = TYPE_LENGTH (type);
+ char *value_bytes = 0;
+ int value_bytes_copied = 0;
+ int num_storage_locs;
+
+ VALUE_REGNO (v) = regnum;
+
+ num_storage_locs = (len > REGISTER_VIRTUAL_SIZE (regnum) ?
+ ((len - 1) / REGISTER_RAW_SIZE (regnum)) + 1 :
+ 1);
+
+ if (num_storage_locs > 1)
+ {
+ /* Value spread across multiple storage locations. */
+
+ int local_regnum;
+ int mem_stor = 0, reg_stor = 0;
+ int mem_tracking = 1;
+ CORE_ADDR last_addr = 0;
+
+ value_bytes = (char *) alloca (len + MAX_REGISTER_RAW_SIZE);
+
+ /* Copy all of the data out, whereever it may be. */
+
+ for (local_regnum = regnum;
+ value_bytes_copied < len;
+ (value_bytes_copied += REGISTER_RAW_SIZE (local_regnum),
+ ++local_regnum))
+ {
+ int register_index = local_regnum - regnum;
+ addr = find_saved_register (frame, local_regnum);
+ if (addr == 0)
+ {
+ read_register_bytes (REGISTER_BYTE (local_regnum),
+ value_bytes + value_bytes_copied,
+ REGISTER_RAW_SIZE (local_regnum));
+ reg_stor++;
+ }
+ else
+ {
+ read_memory (addr, value_bytes + value_bytes_copied,
+ REGISTER_RAW_SIZE (local_regnum));
+ mem_stor++;
+ mem_tracking =
+ (mem_tracking
+ && (regnum == local_regnum
+ || addr == last_addr));
+ }
+ last_addr = addr;
+ }
+
+ if ((reg_stor && mem_stor)
+ || (mem_stor && !mem_tracking))
+ /* Mixed storage; all of the hassle we just went through was
+ for some good purpose. */
+ {
+ VALUE_LVAL (v) = lval_reg_frame_relative;
+ VALUE_FRAME (v) = FRAME_FP (frame);
+ VALUE_FRAME_REGNUM (v) = regnum;
+ }
+ else if (mem_stor)
+ {
+ VALUE_LVAL (v) = lval_memory;
+ VALUE_ADDRESS (v) = find_saved_register (frame, regnum);
+ }
+ else if (reg_stor)
+ {
+ VALUE_LVAL (v) = lval_register;
+ VALUE_ADDRESS (v) = REGISTER_BYTE (regnum);
+ }
+ else
+ fatal ("value_from_register: Value not stored anywhere!");
+
+ /* Any structure stored in more than one register will always be
+ an inegral number of registers. Otherwise, you'd need to do
+ some fiddling with the last register copied here for little
+ endian machines. */
+
+ /* Copy into the contents section of the value. */
+ bcopy (value_bytes, VALUE_CONTENTS (v), len);
+
+ return v;
+ }
+
+ /* Data is completely contained within a single register. Locate the
+ register's contents in a real register or in core;
+ read the data in raw format. */
+
+ addr = find_saved_register (frame, regnum);
+ if (addr == 0)
+ {
+ /* Value is really in a register. */
+
+ VALUE_LVAL (v) = lval_register;
+ VALUE_ADDRESS (v) = REGISTER_BYTE (regnum);
+
+ read_register_bytes (REGISTER_BYTE (regnum),
+ raw_buffer, REGISTER_RAW_SIZE (regnum));
+ }
+ else
+ {
+ /* Value was in a register that has been saved in memory. */
+
+ read_memory (addr, raw_buffer, REGISTER_RAW_SIZE (regnum));
+ VALUE_LVAL (v) = lval_memory;
+ VALUE_ADDRESS (v) = addr;
+ }
+
+ /* Convert the raw contents to virtual contents.
+ (Just copy them if the formats are the same.) */
+
+ REGISTER_CONVERT_TO_VIRTUAL (regnum, raw_buffer, virtual_buffer);
+
+ if (REGISTER_CONVERTIBLE (regnum))
+ {
+ /* When the raw and virtual formats differ, the virtual format
+ corresponds to a specific data type. If we want that type,
+ copy the data into the value.
+ Otherwise, do a type-conversion. */
+
+ if (type != REGISTER_VIRTUAL_TYPE (regnum))
+ {
+ /* eg a variable of type `float' in a 68881 register
+ with raw type `extended' and virtual type `double'.
+ Fetch it as a `double' and then convert to `float'. */
+ v = allocate_value (REGISTER_VIRTUAL_TYPE (regnum));
+ bcopy (virtual_buffer, VALUE_CONTENTS (v), len);
+ v = value_cast (type, v);
+ }
+ else
+ bcopy (virtual_buffer, VALUE_CONTENTS (v), len);
+ }
+ else
+ {
+ /* Raw and virtual formats are the same for this register. */
+
+#ifdef BYTES_BIG_ENDIAN
+ if (len < REGISTER_RAW_SIZE (regnum))
+ {
+ /* Big-endian, and we want less than full size. */
+ VALUE_OFFSET (v) = REGISTER_RAW_SIZE (regnum) - len;
+ }
+#endif
+
+ bcopy (virtual_buffer + VALUE_OFFSET (v),
+ VALUE_CONTENTS (v), len);
+ }
+
+ return v;
+}
+
+/* Given a struct symbol for a variable,
+ and a stack frame id,
+ return a (pointer to a) struct value containing the variable's address. */
+
+value
+locate_var_value (var, frame)
+ register struct symbol *var;
+ FRAME frame;
+{
+ register CORE_ADDR addr = 0;
+ int val = SYMBOL_VALUE (var);
+ struct frame_info *fi;
+ struct type *type = SYMBOL_TYPE (var);
+ struct type *result_type;
+
+ if (frame == 0) frame = selected_frame;
+
+ switch (SYMBOL_CLASS (var))
+ {
+ case LOC_CONST:
+ case LOC_CONST_BYTES:
+ error ("Address requested for identifier \"%s\" which is a constant.",
+ SYMBOL_NAME (var));
+
+ case LOC_REGISTER:
+ case LOC_REGPARM:
+ addr = find_saved_register (frame, val);
+ if (addr != 0)
+ {
+ int len = TYPE_LENGTH (type);
+#ifdef BYTES_BIG_ENDIAN
+ if (len < REGISTER_RAW_SIZE (val))
+ /* Big-endian, and we want less than full size. */
+ addr += REGISTER_RAW_SIZE (val) - len;
+#endif
+ break;
+ }
+ error ("Address requested for identifier \"%s\" which is in a register.",
+ SYMBOL_NAME (var));
+
+ case LOC_STATIC:
+ case LOC_LABEL:
+ addr = val;
+ break;
+
+ case LOC_ARG:
+ fi = get_frame_info (frame);
+ addr = val + FRAME_ARGS_ADDRESS (fi);
+ break;
+
+ case LOC_REF_ARG:
+ fi = get_frame_info (frame);
+ addr = val + FRAME_ARGS_ADDRESS (fi);
+ addr = read_memory_integer (addr, sizeof (CORE_ADDR));
+ break;
+
+ case LOC_LOCAL:
+ fi = get_frame_info (frame);
+ addr = val + FRAME_LOCALS_ADDRESS (fi);
+ break;
+
+ case LOC_TYPEDEF:
+ error ("Address requested for identifier \"%s\" which is a typedef.",
+ SYMBOL_NAME (var));
+
+ case LOC_BLOCK:
+ addr = BLOCK_START (SYMBOL_BLOCK_VALUE (var));
+ break;
+ }
+
+ /* Address of an array is of the type of address of it's elements. */
+ result_type =
+ lookup_pointer_type (TYPE_CODE (type) == TYPE_CODE_ARRAY ?
+ TYPE_TARGET_TYPE (type) : type);
+
+ return value_cast (result_type,
+ value_from_long (builtin_type_long, (LONGEST) addr));
+}
+
diff --git a/gnu/usr.bin/gdb/frame.h b/gnu/usr.bin/gdb/frame.h
new file mode 100644
index 0000000..322ddba
--- /dev/null
+++ b/gnu/usr.bin/gdb/frame.h
@@ -0,0 +1,115 @@
+/* Definitions for dealing with stack frames, for GDB, the GNU debugger.
+ Copyright (C) 1986, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Note that frame.h requires param.h! */
+
+/*
+ * FRAME is the type of the identifier of a specific stack frame. It
+ * is a pointer to the frame cache item corresponding to this frame.
+ * Please note that frame id's are *not* constant over calls to the
+ * inferior. Use frame addresses, which are.
+ *
+ * FRAME_ADDR is the type of the address of a specific frame. I
+ * cannot imagine a case in which this would not be CORE_ADDR, so
+ * maybe it's silly to give it it's own type. Life's rough.
+ *
+ * FRAME_FP is a macro which converts from a frame identifier into a
+ * frame_address.
+ *
+ * FRAME_INFO_ID is a macro which "converts" from a frame info pointer
+ * to a frame id. This is here in case I or someone else decides to
+ * change the FRAME type again.
+ *
+ * This file and blockframe.c are the only places which are allowed to
+ * use the equivalence between FRAME and struct frame_info *. EXCEPTION:
+ * value.h uses CORE_ADDR instead of FRAME_ADDR because the compiler
+ * will accept that in the absense of this file.
+ */
+typedef struct frame_info *FRAME;
+typedef CORE_ADDR FRAME_ADDR;
+#define FRAME_FP(fr) ((fr)->frame)
+#define FRAME_INFO_ID(f) (f)
+
+/*
+ * Caching structure for stack frames. This is also the structure
+ * used for extended info about stack frames. May add more to this
+ * structure as it becomes necessary.
+ *
+ * Note that the first entry in the cache will always refer to the
+ * innermost executing frame. This value should be set (is it?
+ * Check) in something like normal_stop.
+ */
+struct frame_info
+ {
+ /* Nominal address of the frame described. */
+ FRAME_ADDR frame;
+ /* Address at which execution is occurring in this frame.
+ For the innermost frame, it's the current pc.
+ For other frames, it is a pc saved in the next frame. */
+ CORE_ADDR pc;
+ /* The frame called by the frame we are describing, or 0.
+ This may be set even if there isn't a frame called by the one
+ we are describing (.->next == 0); in that case it is simply the
+ bottom of this frame */
+ FRAME_ADDR next_frame;
+ /* Anything extra for this structure that may have been defined
+ in the machine depedent files. */
+#ifdef EXTRA_FRAME_INFO
+ EXTRA_FRAME_INFO
+#endif
+ /* Pointers to the next and previous frame_info's in this stack. */
+ FRAME next, prev;
+ };
+
+/* Describe the saved registers of a frame. */
+
+struct frame_saved_regs
+ {
+ /* For each register, address of where it was saved on entry to the frame,
+ or zero if it was not saved on entry to this frame. */
+ CORE_ADDR regs[NUM_REGS];
+ };
+
+/* The stack frame that the user has specified for commands to act on.
+ Note that one cannot assume this is the address of valid data. */
+
+extern FRAME selected_frame;
+
+extern struct frame_info *get_frame_info ();
+extern struct frame_info *get_prev_frame_info ();
+
+extern FRAME create_new_frame ();
+
+extern void get_frame_saved_regs ();
+
+extern FRAME get_prev_frame ();
+extern FRAME get_current_frame ();
+extern FRAME get_next_frame ();
+
+extern struct block *get_frame_block ();
+extern struct block *get_current_block ();
+extern struct block *get_selected_block ();
+extern struct symbol *get_frame_function ();
+extern struct symbol *get_pc_function ();
+
+/* In stack.c */
+extern FRAME find_relative_frame ();
+
+/* Generic pointer value indicating "I don't know." */
+#define Frame_unknown (CORE_ADDR)-1
diff --git a/gnu/usr.bin/gdb/gdb.1 b/gnu/usr.bin/gdb/gdb.1
new file mode 100644
index 0000000..57d744b
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb.1
@@ -0,0 +1,3 @@
+.\" %W% (Berkeley) %G%
+.\"
+.\" placeholder, until we can produce the manual page
diff --git a/gnu/usr.bin/gdb/gdb/COPYING b/gnu/usr.bin/gdb/gdb/COPYING
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/gnu/usr.bin/gdb/gdb/Makefile b/gnu/usr.bin/gdb/gdb/Makefile
new file mode 100644
index 0000000..5f134e2
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/Makefile
@@ -0,0 +1,72 @@
+PROG = gdb
+BINDIR= /usr/bin
+SRCS = main.c blockframe.c breakpoint.c findvar.c stack.c thread.c \
+ source.c values.c eval.c valops.c valarith.c valprint.c printcmd.c \
+ symtab.c symfile.c symmisc.c infcmd.c infrun.c command.c utils.c \
+ expprint.c environ.c gdbtypes.c copying.c i386-tdep.c i386-pinsn.c \
+ freebsd-solib.c ser-unix.c exec.c fork-child.c infptrace.c inftarg.c \
+ corelow.c coredep.c freebsd-nat.c remote.c dcache.c remote-utils.c \
+ mem-break.c target.c putenv.c parse.c language.c buildsym.c \
+ objfiles.c minsyms.c maint.c demangle.c dbxread.c coffread.c \
+ elfread.c dwarfread.c mipsread.c stabsread.c core.c c-lang.c \
+ ch-lang.c m2-lang.c complaints.c typeprint.c c-typeprint.c \
+ ch-typeprint.c m2-typeprint.c c-valprint.c cp-valprint.c ch-valprint.c \
+ m2-valprint.c nlmread.c serial.c inflow.c regex.c init.c \
+ c-exp.tab.c ch-exp.tab.c m2-exp.tab.c version.c i386-dis.c dis-buf.c
+
+c-exp.tab.c: $(.CURDIR)/c-exp.y
+ yacc -d -p c_ $(.CURDIR)/c-exp.y
+ sed -e '/extern.*malloc/d' -e '/extern.*realloc/d' -e '/extern.*free/d' \
+ -e '/include.*malloc.h/d' -e 's/malloc/xmalloc/g' \
+ -e 's/realloc/xrealloc/g' < y.tab.c > c-exp.new
+ rm y.tab.c
+ mv c-exp.new ./c-exp.tab.c
+
+ch-exp.tab.c: $(.CURDIR)/ch-exp.y
+ yacc -d -p ch_ $(.CURDIR)/ch-exp.y
+ sed -e '/extern.*malloc/d' -e '/extern.*realloc/d' -e '/extern.*free/d' \
+ -e '/include.*malloc.h/d' -e 's/malloc/xmalloc/g' \
+ -e 's/realloc/xrealloc/g' < y.tab.c > ch-exp.new
+ rm y.tab.c
+ mv ch-exp.new ./ch-exp.tab.c
+
+m2-exp.tab.c: $(.CURDIR)/m2-exp.y
+ yacc -d -p m2_ $(.CURDIR)/m2-exp.y
+ sed -e '/extern.*malloc/d' -e '/extern.*realloc/d' -e '/extern.*free/d' \
+ -e '/include.*malloc.h/d' -e 's/malloc/xmalloc/g' \
+ -e 's/realloc/xrealloc/g' < y.tab.c > m2-exp.new
+ rm y.tab.c
+ mv m2-exp.new ./m2-exp.tab.c
+
+
+
+CFLAGS+= -I$(.CURDIR)/. -I/usr/include/readline -I$(.CURDIR)/../bfd
+DPADD+= ${LIBREADLINE} ${LIBTERMCAP}
+LDADD+= -lreadline -ltermcap
+
+.if exists(${.CURDIR}/../libiberty/obj)
+LDADD+= -L${.CURDIR}/../libiberty/obj -liberty
+DPADD+= ${.CURDIR}/../libiberty/obj/libiberty.a
+.else
+LDADD+= -L${.CURDIR}/../libiberty/ -liberty
+DPADD+= ${.CURDIR}/../libiberty/libiberty.a
+.endif
+
+.if exists(${.CURDIR}/../bfd/obj)
+LDADD+= -L${.CURDIR}/../bfd/obj -lbfd
+DPADD+= ${.CURDIR}/../bfd/obj/libbfd.a
+.else
+LDADD+= -L${.CURDIR}/../bfd/ -lbfd
+DPADD+= ${.CURDIR}/../bfd/libbfd.a
+.endif
+
+.if exists(${.CURDIR}/../mmalloc/obj)
+LDADD+= -L${.CURDIR}/../mmalloc/obj -lmmalloc
+DPADD+= ${.CURDIR}/../mmalloc/obj/libmmalloc.a
+.else
+LDADD+= -L${.CURDIR}/../mmalloc/ -lmmalloc
+DPADD+= ${.CURDIR}/../mmalloc/libmmalloc.a
+.endif
+
+LDADD+= -lcompat
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/gdb/gdb/ansidecl.h b/gnu/usr.bin/gdb/gdb/ansidecl.h
new file mode 100644
index 0000000..fdc4072
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/ansidecl.h
@@ -0,0 +1,139 @@
+/* ANSI and traditional C compatability macros
+ Copyright 1991, 1992 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ANSI and traditional C compatibility macros
+
+ ANSI C is assumed if __STDC__ is #defined.
+
+ Macro ANSI C definition Traditional C definition
+ ----- ---- - ---------- ----------- - ----------
+ PTR `void *' `char *'
+ LONG_DOUBLE `long double' `double'
+ VOLATILE `volatile' `'
+ SIGNED `signed' `'
+ PTRCONST `void *const' `char *'
+
+ CONST is also defined, but is obsolete. Just use const.
+
+ DEFUN (name, arglist, args)
+
+ Defines function NAME.
+
+ ARGLIST lists the arguments, separated by commas and enclosed in
+ parentheses. ARGLIST becomes the argument list in traditional C.
+
+ ARGS list the arguments with their types. It becomes a prototype in
+ ANSI C, and the type declarations in traditional C. Arguments should
+ be separated with `AND'. For functions with a variable number of
+ arguments, the last thing listed should be `DOTS'.
+
+ DEFUN_VOID (name)
+
+ Defines a function NAME, which takes no arguments.
+
+ obsolete -- EXFUN (name, (prototype)) -- obsolete.
+
+ Replaced by PARAMS. Do not use; will disappear someday soon.
+ Was used in external function declarations.
+ In ANSI C it is `NAME PROTOTYPE' (so PROTOTYPE should be enclosed in
+ parentheses). In traditional C it is `NAME()'.
+ For a function that takes no arguments, PROTOTYPE should be `(void)'.
+
+ PARAMS ((args))
+
+ We could use the EXFUN macro to handle prototype declarations, but
+ the name is misleading and the result is ugly. So we just define a
+ simple macro to handle the parameter lists, as in:
+
+ static int foo PARAMS ((int, char));
+
+ This produces: `static int foo();' or `static int foo (int, char);'
+
+ EXFUN would have done it like this:
+
+ static int EXFUN (foo, (int, char));
+
+ but the function is not external...and it's hard to visually parse
+ the function name out of the mess. EXFUN should be considered
+ obsolete; new code should be written to use PARAMS.
+
+ For example:
+ extern int printf PARAMS ((CONST char *format DOTS));
+ int DEFUN(fprintf, (stream, format),
+ FILE *stream AND CONST char *format DOTS) { ... }
+ void DEFUN_VOID(abort) { ... }
+*/
+
+#ifndef _ANSIDECL_H
+
+#define _ANSIDECL_H 1
+
+
+/* Every source file includes this file,
+ so they will all get the switch for lint. */
+/* LINTLIBRARY */
+
+
+#if defined (__STDC__) || defined (_AIX) || (defined (__mips) && defined (_SYSTYPE_SVR4))
+/* All known AIX compilers implement these things (but don't always
+ define __STDC__). The RISC/OS MIPS compiler defines these things
+ in SVR4 mode, but does not define __STDC__. */
+
+#define PTR void *
+#define PTRCONST void *CONST
+#define LONG_DOUBLE long double
+
+#define AND ,
+#define NOARGS void
+#define CONST const
+#define VOLATILE volatile
+#define SIGNED signed
+#define DOTS , ...
+
+#define EXFUN(name, proto) name proto
+#define DEFUN(name, arglist, args) name(args)
+#define DEFUN_VOID(name) name(void)
+
+#define PROTO(type, name, arglist) type name arglist
+#define PARAMS(paramlist) paramlist
+
+#else /* Not ANSI C. */
+
+#define PTR char *
+#define PTRCONST PTR
+#define LONG_DOUBLE double
+
+#define AND ;
+#define NOARGS
+#define CONST
+#ifndef const /* some systems define it in header files for non-ansi mode */
+#define const
+#endif
+#define VOLATILE
+#define SIGNED
+#define DOTS
+
+#define EXFUN(name, proto) name()
+#define DEFUN(name, arglist, args) name arglist args;
+#define DEFUN_VOID(name) name()
+#define PROTO(type, name, arglist) type name ()
+#define PARAMS(paramlist) ()
+
+#endif /* ANSI C. */
+
+#endif /* ansidecl.h */
diff --git a/gnu/usr.bin/gdb/gdb/aout/aout64.h b/gnu/usr.bin/gdb/gdb/aout/aout64.h
new file mode 100644
index 0000000..018e6dd
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/aout/aout64.h
@@ -0,0 +1,431 @@
+/* `a.out' object-file definitions, including extensions to 64-bit fields */
+
+#ifndef __A_OUT_64_H__
+#define __A_OUT_64_H__
+
+/* This is the layout on disk of the 32-bit or 64-bit exec header. */
+
+#ifndef external_exec
+struct external_exec
+{
+ bfd_byte e_info[4]; /* magic number and stuff */
+ bfd_byte e_text[BYTES_IN_WORD]; /* length of text section in bytes */
+ bfd_byte e_data[BYTES_IN_WORD]; /* length of data section in bytes */
+ bfd_byte e_bss[BYTES_IN_WORD]; /* length of bss area in bytes */
+ bfd_byte e_syms[BYTES_IN_WORD]; /* length of symbol table in bytes */
+ bfd_byte e_entry[BYTES_IN_WORD]; /* start address */
+ bfd_byte e_trsize[BYTES_IN_WORD]; /* length of text relocation info */
+ bfd_byte e_drsize[BYTES_IN_WORD]; /* length of data relocation info */
+};
+
+#define EXEC_BYTES_SIZE (4 + BYTES_IN_WORD * 7)
+
+/* Magic numbers for a.out files */
+
+#if ARCH_SIZE==64
+#define OMAGIC 0x1001 /* Code indicating object file */
+#define ZMAGIC 0x1002 /* Code indicating demand-paged executable. */
+#define NMAGIC 0x1003 /* Code indicating pure executable. */
+
+/* There is no 64-bit QMAGIC as far as I know. */
+
+#define N_BADMAG(x) (N_MAGIC(x) != OMAGIC \
+ && N_MAGIC(x) != NMAGIC \
+ && N_MAGIC(x) != ZMAGIC)
+#else
+#define OMAGIC 0407 /* ...object file or impure executable. */
+#define NMAGIC 0410 /* Code indicating pure executable. */
+#define ZMAGIC 0413 /* Code indicating demand-paged executable. */
+
+/* This indicates a demand-paged executable with the header in the text.
+ As far as I know it is only used by 386BSD and/or BSDI. */
+#define QMAGIC 0314
+# ifndef N_BADMAG
+# define N_BADMAG(x) (N_MAGIC(x) != OMAGIC \
+ && N_MAGIC(x) != NMAGIC \
+ && N_MAGIC(x) != ZMAGIC \
+ && N_MAGIC(x) != QMAGIC)
+# endif /* N_BADMAG */
+#endif
+
+#endif
+
+#ifdef QMAGIC
+#define N_IS_QMAGIC(x) (N_MAGIC (x) == QMAGIC)
+#else
+#define N_IS_QMAGIC(x) (0)
+#endif
+
+/* The difference between PAGE_SIZE and N_SEGSIZE is that PAGE_SIZE is
+ the the finest granularity at which you can page something, thus it
+ controls the padding (if any) before the text segment of a ZMAGIC
+ file. N_SEGSIZE is the resolution at which things can be marked as
+ read-only versus read/write, so it controls the padding between the
+ text segment and the data segment (in memory; on disk the padding
+ between them is PAGE_SIZE). PAGE_SIZE and N_SEGSIZE are the same
+ for most machines, but different for sun3. */
+
+/* By default, segment size is constant. But some machines override this
+ to be a function of the a.out header (e.g. machine type). */
+
+#ifndef N_SEGSIZE
+#define N_SEGSIZE(x) SEGMENT_SIZE
+#endif
+
+/* Virtual memory address of the text section.
+ This is getting very complicated. A good reason to discard a.out format
+ for something that specifies these fields explicitly. But til then...
+
+ * OMAGIC and NMAGIC files:
+ (object files: text for "relocatable addr 0" right after the header)
+ start at 0, offset is EXEC_BYTES_SIZE, size as stated.
+ * The text address, offset, and size of ZMAGIC files depend
+ on the entry point of the file:
+ * entry point below TEXT_START_ADDR:
+ (hack for SunOS shared libraries)
+ start at 0, offset is 0, size as stated.
+ * If N_HEADER_IN_TEXT(x) is true (which defaults to being the
+ case when the entry point is EXEC_BYTES_SIZE or further into a page):
+ no padding is needed; text can start after exec header. Sun
+ considers the text segment of such files to include the exec header;
+ for BFD's purposes, we don't, which makes more work for us.
+ start at TEXT_START_ADDR + EXEC_BYTES_SIZE, offset is EXEC_BYTES_SIZE,
+ size as stated minus EXEC_BYTES_SIZE.
+ * If N_HEADER_IN_TEXT(x) is false (which defaults to being the case when
+ the entry point is less than EXEC_BYTES_SIZE into a page (e.g. page
+ aligned)): (padding is needed so that text can start at a page boundary)
+ start at TEXT_START_ADDR, offset PAGE_SIZE, size as stated.
+
+ Specific configurations may want to hardwire N_HEADER_IN_TEXT,
+ for efficiency or to allow people to play games with the entry point.
+ In that case, you would #define N_HEADER_IN_TEXT(x) as 1 for sunos,
+ and as 0 for most other hosts (Sony News, Vax Ultrix, etc).
+ (Do this in the appropriate bfd target file.)
+ (The default is a heuristic that will break if people try changing
+ the entry point, perhaps with the ld -e flag.)
+
+ * QMAGIC is always like a ZMAGIC for which N_HEADER_IN_TEXT is true,
+ and for which the starting address is PAGE_SIZE (or should this be
+ SEGMENT_SIZE?) (TEXT_START_ADDR only applies to ZMAGIC, not to QMAGIC).
+ */
+
+/* This macro is only relevant for ZMAGIC files; QMAGIC always has the header
+ in the text. */
+#ifndef N_HEADER_IN_TEXT
+#define N_HEADER_IN_TEXT(x) (((x).a_entry & (PAGE_SIZE-1)) >= EXEC_BYTES_SIZE)
+#endif
+
+/* Sun shared libraries, not linux. This macro is only relevant for ZMAGIC
+ files. */
+#ifndef N_SHARED_LIB
+#define N_SHARED_LIB(x) ((x).a_entry < TEXT_START_ADDR)
+#endif
+
+#ifndef N_TXTADDR
+#define N_TXTADDR(x) \
+ (/* The address of a QMAGIC file is always one page in, */ \
+ /* with the header in the text. */ \
+ N_IS_QMAGIC (x) ? PAGE_SIZE + EXEC_BYTES_SIZE : \
+ N_MAGIC(x) != ZMAGIC ? 0 : /* object file or NMAGIC */\
+ N_SHARED_LIB(x) ? 0 : \
+ N_HEADER_IN_TEXT(x) ? \
+ TEXT_START_ADDR + EXEC_BYTES_SIZE : /* no padding */\
+ TEXT_START_ADDR /* a page of padding */\
+ )
+#endif
+
+/* Offset in an a.out of the start of the text section. */
+#ifndef N_TXTOFF
+#define N_TXTOFF(x) \
+ (/* For {O,N,Q}MAGIC, no padding. */ \
+ N_MAGIC(x) != ZMAGIC ? EXEC_BYTES_SIZE : \
+ N_SHARED_LIB(x) ? 0 : \
+ N_HEADER_IN_TEXT(x) ? \
+ EXEC_BYTES_SIZE : /* no padding */\
+ PAGE_SIZE /* a page of padding */\
+ )
+#endif
+/* Size of the text section. It's always as stated, except that we
+ offset it to `undo' the adjustment to N_TXTADDR and N_TXTOFF
+ for ZMAGIC files that nominally include the exec header
+ as part of the first page of text. (BFD doesn't consider the
+ exec header to be part of the text segment.) */
+#ifndef N_TXTSIZE
+#define N_TXTSIZE(x) \
+ (/* For QMAGIC, we don't consider the header part of the text section. */\
+ N_IS_QMAGIC (x) ? (x).a_text - EXEC_BYTES_SIZE : \
+ (N_MAGIC(x) != ZMAGIC || N_SHARED_LIB(x)) ? (x).a_text : \
+ N_HEADER_IN_TEXT(x) ? \
+ (x).a_text - EXEC_BYTES_SIZE: /* no padding */\
+ (x).a_text /* a page of padding */\
+ )
+#endif
+/* The address of the data segment in virtual memory.
+ It is the text segment address, plus text segment size, rounded
+ up to a N_SEGSIZE boundary for pure or pageable files. */
+#ifndef N_DATADDR
+#define N_DATADDR(x) \
+ (N_MAGIC(x)==OMAGIC? (N_TXTADDR(x)+N_TXTSIZE(x)) \
+ : (N_SEGSIZE(x) + ((N_TXTADDR(x)+N_TXTSIZE(x)-1) & ~(N_SEGSIZE(x)-1))))
+#endif
+/* The address of the BSS segment -- immediately after the data segment. */
+
+#define N_BSSADDR(x) (N_DATADDR(x) + (x).a_data)
+
+/* Offsets of the various portions of the file after the text segment. */
+
+/* For {N,Q,Z}MAGIC, there is padding to make the data segment start
+ on a page boundary. Most of the time the a_text field (and thus
+ N_TXTSIZE) already contains this padding. But if it doesn't (I
+ think maybe this happens on BSDI and/or 386BSD), then add it. */
+
+#ifndef N_DATOFF
+#define N_DATOFF(x) \
+ (N_MAGIC(x) == OMAGIC ? N_TXTOFF(x) + N_TXTSIZE(x) : \
+ PAGE_SIZE + ((N_TXTOFF(x) + N_TXTSIZE(x) - 1) & ~(PAGE_SIZE - 1)))
+#endif
+
+#ifndef N_TRELOFF
+#define N_TRELOFF(x) ( N_DATOFF(x) + (x).a_data )
+#endif
+#ifndef N_DRELOFF
+#define N_DRELOFF(x) ( N_TRELOFF(x) + (x).a_trsize )
+#endif
+#ifndef N_SYMOFF
+#define N_SYMOFF(x) ( N_DRELOFF(x) + (x).a_drsize )
+#endif
+#ifndef N_STROFF
+#define N_STROFF(x) ( N_SYMOFF(x) + (x).a_syms )
+#endif
+
+/* Symbols */
+#ifndef external_nlist
+struct external_nlist {
+ bfd_byte e_strx[BYTES_IN_WORD]; /* index into string table of name */
+ bfd_byte e_type[1]; /* type of symbol */
+ bfd_byte e_other[1]; /* misc info (usually empty) */
+ bfd_byte e_desc[2]; /* description field */
+ bfd_byte e_value[BYTES_IN_WORD]; /* value of symbol */
+};
+#define EXTERNAL_NLIST_SIZE (BYTES_IN_WORD+4+BYTES_IN_WORD)
+#endif
+
+struct internal_nlist {
+ unsigned long n_strx; /* index into string table of name */
+ unsigned char n_type; /* type of symbol */
+ unsigned char n_other; /* misc info (usually empty) */
+ unsigned short n_desc; /* description field */
+ bfd_vma n_value; /* value of symbol */
+};
+
+/* The n_type field is the symbol type, containing: */
+
+#define N_UNDF 0 /* Undefined symbol */
+#define N_ABS 2 /* Absolute symbol -- defined at particular addr */
+#define N_TEXT 4 /* Text sym -- defined at offset in text seg */
+#define N_DATA 6 /* Data sym -- defined at offset in data seg */
+#define N_BSS 8 /* BSS sym -- defined at offset in zero'd seg */
+#define N_COMM 0x12 /* Common symbol (visible after shared lib dynlink) */
+#define N_FN 0x1f /* File name of .o file */
+#define N_FN_SEQ 0x0C /* N_FN from Sequent compilers (sigh) */
+/* Note: N_EXT can only be usefully OR-ed with N_UNDF, N_ABS, N_TEXT,
+ N_DATA, or N_BSS. When the low-order bit of other types is set,
+ (e.g. N_WARNING versus N_FN), they are two different types. */
+#define N_EXT 1 /* External symbol (as opposed to local-to-this-file) */
+#define N_TYPE 0x1e
+#define N_STAB 0xe0 /* If any of these bits are on, it's a debug symbol */
+
+#define N_INDR 0x0a
+
+/* The following symbols refer to set elements.
+ All the N_SET[ATDB] symbols with the same name form one set.
+ Space is allocated for the set in the text section, and each set
+ elements value is stored into one word of the space.
+ The first word of the space is the length of the set (number of elements).
+
+ The address of the set is made into an N_SETV symbol
+ whose name is the same as the name of the set.
+ This symbol acts like a N_DATA global symbol
+ in that it can satisfy undefined external references. */
+
+/* These appear as input to LD, in a .o file. */
+#define N_SETA 0x14 /* Absolute set element symbol */
+#define N_SETT 0x16 /* Text set element symbol */
+#define N_SETD 0x18 /* Data set element symbol */
+#define N_SETB 0x1A /* Bss set element symbol */
+
+/* This is output from LD. */
+#define N_SETV 0x1C /* Pointer to set vector in data area. */
+
+/* Warning symbol. The text gives a warning message, the next symbol
+ in the table will be undefined. When the symbol is referenced, the
+ message is printed. */
+
+#define N_WARNING 0x1e
+
+/* Relocations
+
+ There are two types of relocation flavours for a.out systems,
+ standard and extended. The standard form is used on systems where the
+ instruction has room for all the bits of an offset to the operand, whilst
+ the extended form is used when an address operand has to be split over n
+ instructions. Eg, on the 68k, each move instruction can reference
+ the target with a displacement of 16 or 32 bits. On the sparc, move
+ instructions use an offset of 14 bits, so the offset is stored in
+ the reloc field, and the data in the section is ignored.
+*/
+
+/* This structure describes a single relocation to be performed.
+ The text-relocation section of the file is a vector of these structures,
+ all of which apply to the text section.
+ Likewise, the data-relocation section applies to the data section. */
+
+struct reloc_std_external {
+ bfd_byte r_address[BYTES_IN_WORD]; /* offset of of data to relocate */
+ bfd_byte r_index[3]; /* symbol table index of symbol */
+ bfd_byte r_type[1]; /* relocation type */
+};
+
+#define RELOC_STD_BITS_PCREL_BIG 0x80
+#define RELOC_STD_BITS_PCREL_LITTLE 0x01
+
+#define RELOC_STD_BITS_LENGTH_BIG 0x60
+#define RELOC_STD_BITS_LENGTH_SH_BIG 5 /* To shift to units place */
+#define RELOC_STD_BITS_LENGTH_LITTLE 0x06
+#define RELOC_STD_BITS_LENGTH_SH_LITTLE 1
+
+#define RELOC_STD_BITS_EXTERN_BIG 0x10
+#define RELOC_STD_BITS_EXTERN_LITTLE 0x08
+
+#define RELOC_STD_BITS_BASEREL_BIG 0x08
+#define RELOC_STD_BITS_BASEREL_LITTLE 0x08
+
+#define RELOC_STD_BITS_JMPTABLE_BIG 0x04
+#define RELOC_STD_BITS_JMPTABLE_LITTLE 0x04
+
+#define RELOC_STD_BITS_RELATIVE_BIG 0x02
+#define RELOC_STD_BITS_RELATIVE_LITTLE 0x02
+
+#define RELOC_STD_SIZE (BYTES_IN_WORD + 3 + 1) /* Bytes per relocation entry */
+
+struct reloc_std_internal
+{
+ bfd_vma r_address; /* Address (within segment) to be relocated. */
+ /* The meaning of r_symbolnum depends on r_extern. */
+ unsigned int r_symbolnum:24;
+ /* Nonzero means value is a pc-relative offset
+ and it should be relocated for changes in its own address
+ as well as for changes in the symbol or section specified. */
+ unsigned int r_pcrel:1;
+ /* Length (as exponent of 2) of the field to be relocated.
+ Thus, a value of 2 indicates 1<<2 bytes. */
+ unsigned int r_length:2;
+ /* 1 => relocate with value of symbol.
+ r_symbolnum is the index of the symbol
+ in files the symbol table.
+ 0 => relocate with the address of a segment.
+ r_symbolnum is N_TEXT, N_DATA, N_BSS or N_ABS
+ (the N_EXT bit may be set also, but signifies nothing). */
+ unsigned int r_extern:1;
+ /* The next three bits are for SunOS shared libraries, and seem to
+ be undocumented. */
+ unsigned int r_baserel:1; /* Linkage table relative */
+ unsigned int r_jmptable:1; /* pc-relative to jump table */
+ unsigned int r_relative:1; /* "relative relocation" */
+ /* unused */
+ unsigned int r_pad:1; /* Padding -- set to zero */
+};
+
+
+/* EXTENDED RELOCS */
+
+struct reloc_ext_external {
+ bfd_byte r_address[BYTES_IN_WORD]; /* offset of of data to relocate */
+ bfd_byte r_index[3]; /* symbol table index of symbol */
+ bfd_byte r_type[1]; /* relocation type */
+ bfd_byte r_addend[BYTES_IN_WORD]; /* datum addend */
+};
+
+#define RELOC_EXT_BITS_EXTERN_BIG 0x80
+#define RELOC_EXT_BITS_EXTERN_LITTLE 0x01
+
+#define RELOC_EXT_BITS_TYPE_BIG 0x1F
+#define RELOC_EXT_BITS_TYPE_SH_BIG 0
+#define RELOC_EXT_BITS_TYPE_LITTLE 0xF8
+#define RELOC_EXT_BITS_TYPE_SH_LITTLE 3
+
+/* Bytes per relocation entry */
+#define RELOC_EXT_SIZE (BYTES_IN_WORD + 3 + 1 + BYTES_IN_WORD)
+
+enum reloc_type
+{
+ /* simple relocations */
+ RELOC_8, /* data[0:7] = addend + sv */
+ RELOC_16, /* data[0:15] = addend + sv */
+ RELOC_32, /* data[0:31] = addend + sv */
+ /* pc-rel displacement */
+ RELOC_DISP8, /* data[0:7] = addend - pc + sv */
+ RELOC_DISP16, /* data[0:15] = addend - pc + sv */
+ RELOC_DISP32, /* data[0:31] = addend - pc + sv */
+ /* Special */
+ RELOC_WDISP30, /* data[0:29] = (addend + sv - pc)>>2 */
+ RELOC_WDISP22, /* data[0:21] = (addend + sv - pc)>>2 */
+ RELOC_HI22, /* data[0:21] = (addend + sv)>>10 */
+ RELOC_22, /* data[0:21] = (addend + sv) */
+ RELOC_13, /* data[0:12] = (addend + sv) */
+ RELOC_LO10, /* data[0:9] = (addend + sv) */
+ RELOC_SFA_BASE,
+ RELOC_SFA_OFF13,
+ /* P.I.C. (base-relative) */
+ RELOC_BASE10, /* Not sure - maybe we can do this the */
+ RELOC_BASE13, /* right way now */
+ RELOC_BASE22,
+ /* for some sort of pc-rel P.I.C. (?) */
+ RELOC_PC10,
+ RELOC_PC22,
+ /* P.I.C. jump table */
+ RELOC_JMP_TBL,
+ /* reputedly for shared libraries somehow */
+ RELOC_SEGOFF16,
+ RELOC_GLOB_DAT,
+ RELOC_JMP_SLOT,
+ RELOC_RELATIVE,
+
+ RELOC_11,
+ RELOC_WDISP2_14,
+ RELOC_WDISP19,
+ RELOC_HHI22, /* data[0:21] = (addend + sv) >> 42 */
+ RELOC_HLO10, /* data[0:9] = (addend + sv) >> 32 */
+
+ /* 29K relocation types */
+ RELOC_JUMPTARG,
+ RELOC_CONST,
+ RELOC_CONSTH,
+
+
+ /* Q .
+ What are the other ones,
+ Since this is a clean slate, can we throw away the ones we dont
+ understand ? Should we sort the values ? What about using a
+ microcode format like the 68k ?
+ */
+ NO_RELOC
+ };
+
+
+struct reloc_internal {
+ bfd_vma r_address; /* offset of of data to relocate */
+ long r_index; /* symbol table index of symbol */
+ enum reloc_type r_type; /* relocation type */
+ bfd_vma r_addend; /* datum addend */
+};
+
+/* Q.
+ Should the length of the string table be 4 bytes or 8 bytes ?
+
+ Q.
+ What about archive indexes ?
+
+ */
+
+#endif /* __A_OUT_64_H__ */
diff --git a/gnu/usr.bin/gdb/gdb/aout/ar.h b/gnu/usr.bin/gdb/gdb/aout/ar.h
new file mode 100644
index 0000000..cca636d
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/aout/ar.h
@@ -0,0 +1,32 @@
+/* archive file definition for GNU software */
+
+/* So far this is correct for BSDish archives. Don't forget that
+ files must begin on an even byte boundary. */
+
+#ifndef __GNU_AR_H__
+#define __GNU_AR_H__
+
+#define ARMAG "!<arch>\n" /* For COFF and a.out archives */
+#define ARMAGB "!<bout>\n" /* For b.out archives */
+#define SARMAG 8
+#define ARFMAG "`\n"
+
+/* The ar_date field of the armap (__.SYMDEF) member of an archive
+ must be greater than the modified date of the entire file, or
+ BSD-derived linkers complain. We originally write the ar_date with
+ this offset from the real file's mod-time. After finishing the
+ file, we rewrite ar_date if it's not still greater than the mod date. */
+
+#define ARMAP_TIME_OFFSET 60
+
+struct ar_hdr {
+ char ar_name[16]; /* name of this member */
+ char ar_date[12]; /* file mtime */
+ char ar_uid[6]; /* owner uid; printed as decimal */
+ char ar_gid[6]; /* owner gid; printed as decimal */
+ char ar_mode[8]; /* file mode, printed as octal */
+ char ar_size[10]; /* file size, printed as decimal */
+ char ar_fmag[2]; /* should contain ARFMAG */
+};
+
+#endif /* __GNU_AR_H__ */
diff --git a/gnu/usr.bin/gdb/gdb/aout/ranlib.h b/gnu/usr.bin/gdb/gdb/aout/ranlib.h
new file mode 100644
index 0000000..53e35ce
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/aout/ranlib.h
@@ -0,0 +1,62 @@
+/* ranlib.h -- archive library index member definition for GNU.
+ Copyright 1990-1991 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* The Symdef member of an archive contains two things:
+ a table that maps symbol-string offsets to file offsets,
+ and a symbol-string table. All the symbol names are
+ run together (each with trailing null) in the symbol-string
+ table. There is a single longword bytecount on the front
+ of each of these tables. Thus if we have two symbols,
+ "foo" and "_bar", that are in archive members at offsets
+ 200 and 900, it would look like this:
+ 16 ; byte count of index table
+ 0 ; offset of "foo" in string table
+ 200 ; offset of foo-module in file
+ 4 ; offset of "bar" in string table
+ 900 ; offset of bar-module in file
+ 9 ; byte count of string table
+ "foo\0_bar\0" ; string table */
+
+#define RANLIBMAG "__.SYMDEF" /* Archive file name containing index */
+#define RANLIBSKEW 3 /* Creation time offset */
+
+/* Format of __.SYMDEF:
+ First, a longword containing the size of the 'symdef' data that follows.
+ Second, zero or more 'symdef' structures.
+ Third, a longword containing the length of symbol name strings.
+ Fourth, zero or more symbol name strings (each followed by a null). */
+
+struct symdef
+ {
+ union
+ {
+ unsigned long string_offset; /* In the file */
+ char *name; /* In memory, sometimes */
+ } s;
+ /* this points to the front of the file header (AKA member header --
+ a struct ar_hdr), not to the front of the file or into the file).
+ in other words it only tells you which file to read */
+ unsigned long file_offset;
+ };
+
+/* Compatability with BSD code */
+
+#define ranlib symdef
+#define ran_un s
+#define ran_strx string_offset
+#define ran_name name
+#define ran_off file_offset
diff --git a/gnu/usr.bin/gdb/gdb/aout/stab.def b/gnu/usr.bin/gdb/gdb/aout/stab.def
new file mode 100644
index 0000000..9d1da7d
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/aout/stab.def
@@ -0,0 +1,264 @@
+/* Table of DBX symbol codes for the GNU system.
+ Copyright (C) 1988, 1991 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* New stab from Solaris 2. This uses an n_type of 0, which in a.out files
+ overlaps the N_UNDF used for ordinary symbols. In ELF files, the
+ debug information is in a different file section, so there is no conflict.
+ This symbol's n_value gives the size of the string section associated
+ with this file. The symbol's n_strx (relative to the just-updated
+ string section start address) gives the name of the source file,
+ e.g. "foo.c", without any path information. The symbol's n_desc gives
+ the count of upcoming symbols associated with this file (not including
+ this one). */
+/* __define_stab (N_UNDF, 0x00, "UNDF") */
+
+/* Global variable. Only the name is significant.
+ To find the address, look in the corresponding external symbol. */
+__define_stab (N_GSYM, 0x20, "GSYM")
+
+/* Function name for BSD Fortran. Only the name is significant.
+ To find the address, look in the corresponding external symbol. */
+__define_stab (N_FNAME, 0x22, "FNAME")
+
+/* Function name or text-segment variable for C. Value is its address.
+ Desc is supposedly starting line number, but GCC doesn't set it
+ and DBX seems not to miss it. */
+__define_stab (N_FUN, 0x24, "FUN")
+
+/* Data-segment variable with internal linkage. Value is its address.
+ "Static Sym". */
+__define_stab (N_STSYM, 0x26, "STSYM")
+
+/* BSS-segment variable with internal linkage. Value is its address. */
+__define_stab (N_LCSYM, 0x28, "LCSYM")
+
+/* Name of main routine. Only the name is significant. */
+__define_stab (N_MAIN, 0x2a, "MAIN")
+
+/* Solaris2: Read-only data symbols. */
+__define_stab (N_ROSYM, 0x2c, "ROSYM")
+
+/* Global symbol in Pascal.
+ Supposedly the value is its line number; I'm skeptical. */
+__define_stab (N_PC, 0x30, "PC")
+
+/* Number of symbols: 0, files,,funcs,lines according to Ultrix V4.0. */
+__define_stab (N_NSYMS, 0x32, "NSYMS")
+
+/* "No DST map for sym: name, ,0,type,ignored" according to Ultrix V4.0. */
+__define_stab (N_NOMAP, 0x34, "NOMAP")
+
+/* New stab from Solaris 2. Like N_SO, but for the object file. Two in
+ a row provide the build directory and the relative path of the .o from it.
+ Solaris2 uses this to avoid putting the stabs info into the linked
+ executable; this stab goes into the ".stab.index" section, and the debugger
+ reads the real stabs directly from the .o files instead. */
+__define_stab (N_OBJ, 0x38, "OBJ")
+
+/* New stab from Solaris 2. Options for the debugger, related to the
+ source language for this module. E.g. whether to use ANSI
+ integral promotions or traditional integral promotions. */
+__define_stab (N_OPT, 0x3c, "OPT")
+
+/* Register variable. Value is number of register. */
+__define_stab (N_RSYM, 0x40, "RSYM")
+
+/* Modula-2 compilation unit. Can someone say what info it contains? */
+__define_stab (N_M2C, 0x42, "M2C")
+
+/* Line number in text segment. Desc is the line number;
+ value is corresponding address. On Solaris2, the line number is
+ relative to the start of the current function. */
+__define_stab (N_SLINE, 0x44, "SLINE")
+
+/* Similar, for data segment. */
+__define_stab (N_DSLINE, 0x46, "DSLINE")
+
+/* Similar, for bss segment. */
+__define_stab (N_BSLINE, 0x48, "BSLINE")
+
+/* Sun's source-code browser stabs. ?? Don't know what the fields are.
+ Supposedly the field is "path to associated .cb file". THIS VALUE
+ OVERLAPS WITH N_BSLINE! */
+__define_stab (N_BROWS, 0x48, "BROWS")
+
+/* GNU Modula-2 definition module dependency. Value is the modification time
+ of the definition file. Other is non-zero if it is imported with the
+ GNU M2 keyword %INITIALIZE. Perhaps N_M2C can be used if there
+ are enough empty fields? */
+__define_stab(N_DEFD, 0x4a, "DEFD")
+
+/* New in Solaris2. Function start/body/end line numbers. */
+__define_stab(N_FLINE, 0x4C, "FLINE")
+
+/* THE FOLLOWING TWO STAB VALUES CONFLICT. Happily, one is for Modula-2
+ and one is for C++. Still,... */
+/* GNU C++ exception variable. Name is variable name. */
+__define_stab (N_EHDECL, 0x50, "EHDECL")
+/* Modula2 info "for imc": name,,0,0,0 according to Ultrix V4.0. */
+__define_stab (N_MOD2, 0x50, "MOD2")
+
+/* GNU C++ `catch' clause. Value is its address. Desc is nonzero if
+ this entry is immediately followed by a CAUGHT stab saying what exception
+ was caught. Multiple CAUGHT stabs means that multiple exceptions
+ can be caught here. If Desc is 0, it means all exceptions are caught
+ here. */
+__define_stab (N_CATCH, 0x54, "CATCH")
+
+/* Structure or union element. Value is offset in the structure. */
+__define_stab (N_SSYM, 0x60, "SSYM")
+
+/* Solaris2: Last stab emitted for module. */
+__define_stab (N_ENDM, 0x62, "ENDM")
+
+/* Name of main source file.
+ Value is starting text address of the compilation.
+ If multiple N_SO's appear, the first to contain a trailing / is the
+ compilation directory. The first to not contain a trailing / is the
+ source file name, relative to the compilation directory. Others (perhaps
+ resulting from cfront) are ignored.
+ On Solaris2, value is undefined, but desc is a source-language code. */
+
+__define_stab (N_SO, 0x64, "SO")
+
+/* Automatic variable in the stack. Value is offset from frame pointer.
+ Also used for type descriptions. */
+__define_stab (N_LSYM, 0x80, "LSYM")
+
+/* Beginning of an include file. Only Sun uses this.
+ In an object file, only the name is significant.
+ The Sun linker puts data into some of the other fields. */
+__define_stab (N_BINCL, 0x82, "BINCL")
+
+/* Name of sub-source file (#include file).
+ Value is starting text address of the compilation. */
+__define_stab (N_SOL, 0x84, "SOL")
+
+/* Parameter variable. Value is offset from argument pointer.
+ (On most machines the argument pointer is the same as the frame pointer. */
+__define_stab (N_PSYM, 0xa0, "PSYM")
+
+/* End of an include file. No name.
+ This and N_BINCL act as brackets around the file's output.
+ In an object file, there is no significant data in this entry.
+ The Sun linker puts data into some of the fields. */
+__define_stab (N_EINCL, 0xa2, "EINCL")
+
+/* Alternate entry point. Value is its address. */
+__define_stab (N_ENTRY, 0xa4, "ENTRY")
+
+/* Beginning of lexical block.
+ The desc is the nesting level in lexical blocks.
+ The value is the address of the start of the text for the block.
+ The variables declared inside the block *precede* the N_LBRAC symbol.
+ On Solaris2, the value is relative to the start of the current function. */
+__define_stab (N_LBRAC, 0xc0, "LBRAC")
+
+/* Place holder for deleted include file. Replaces a N_BINCL and everything
+ up to the corresponding N_EINCL. The Sun linker generates these when
+ it finds multiple identical copies of the symbols from an include file.
+ This appears only in output from the Sun linker. */
+__define_stab (N_EXCL, 0xc2, "EXCL")
+
+/* Modula-2 scope information. Can someone say what info it contains? */
+__define_stab (N_SCOPE, 0xc4, "SCOPE")
+
+/* End of a lexical block. Desc matches the N_LBRAC's desc.
+ The value is the address of the end of the text for the block.
+ On Solaris2, the value is relative to the start of the current function. */
+__define_stab (N_RBRAC, 0xe0, "RBRAC")
+
+/* Begin named common block. Only the name is significant. */
+__define_stab (N_BCOMM, 0xe2, "BCOMM")
+
+/* End named common block. Only the name is significant
+ (and it should match the N_BCOMM). */
+__define_stab (N_ECOMM, 0xe4, "ECOMM")
+
+/* Member of a common block; value is offset within the common block.
+ This should occur within a BCOMM/ECOMM pair. */
+__define_stab (N_ECOML, 0xe8, "ECOML")
+
+/* Solaris2: Pascal "with" statement: type,,0,0,offset */
+__define_stab (N_WITH, 0xea, "WITH")
+
+/* These STAB's are used on Gould systems for Non-Base register symbols
+ or something like that. FIXME. I have assigned the values at random
+ since I don't have a Gould here. Fixups from Gould folk welcome... */
+__define_stab (N_NBTEXT, 0xF0, "NBTEXT")
+__define_stab (N_NBDATA, 0xF2, "NBDATA")
+__define_stab (N_NBBSS, 0xF4, "NBBSS")
+__define_stab (N_NBSTS, 0xF6, "NBSTS")
+__define_stab (N_NBLCS, 0xF8, "NBLCS")
+
+/* Second symbol entry containing a length-value for the preceding entry.
+ The value is the length. */
+__define_stab (N_LENG, 0xfe, "LENG")
+
+/* The above information, in matrix format.
+
+ STAB MATRIX
+ _________________________________________________
+ | 00 - 1F are not dbx stab symbols |
+ | In most cases, the low bit is the EXTernal bit|
+
+ | 00 UNDEF | 02 ABS | 04 TEXT | 06 DATA |
+ | 01 |EXT | 03 |EXT | 05 |EXT | 07 |EXT |
+
+ | 08 BSS | 0A INDR | 0C FN_SEQ | 0E |
+ | 09 |EXT | 0B | 0D | 0F |
+
+ | 10 | 12 COMM | 14 SETA | 16 SETT |
+ | 11 | 13 | 15 | 17 |
+
+ | 18 SETD | 1A SETB | 1C SETV | 1E WARNING|
+ | 19 | 1B | 1D | 1F FN |
+
+ |_______________________________________________|
+ | Debug entries with bit 01 set are unused. |
+ | 20 GSYM | 22 FNAME | 24 FUN | 26 STSYM |
+ | 28 LCSYM | 2A MAIN | 2C ROSYM | 2E |
+ | 30 PC | 32 NSYMS | 34 NOMAP | 36 |
+ | 38 OBJ | 3A | 3C OPT | 3E |
+ | 40 RSYM | 42 M2C | 44 SLINE | 46 DSLINE |
+ | 48 BSLINE*| 4A DEFD | 4C FLINE | 4E |
+ | 50 EHDECL*| 52 | 54 CATCH | 56 |
+ | 58 | 5A | 5C | 5E |
+ | 60 SSYM | 62 ENDM | 64 SO | 66 |
+ | 68 | 6A | 6C | 6E |
+ | 70 | 72 | 74 | 76 |
+ | 78 | 7A | 7C | 7E |
+ | 80 LSYM | 82 BINCL | 84 SOL | 86 |
+ | 88 | 8A | 8C | 8E |
+ | 90 | 92 | 94 | 96 |
+ | 98 | 9A | 9C | 9E |
+ | A0 PSYM | A2 EINCL | A4 ENTRY | A6 |
+ | A8 | AA | AC | AE |
+ | B0 | B2 | B4 | B6 |
+ | B8 | BA | BC | BE |
+ | C0 LBRAC | C2 EXCL | C4 SCOPE | C6 |
+ | C8 | CA | CC | CE |
+ | D0 | D2 | D4 | D6 |
+ | D8 | DA | DC | DE |
+ | E0 RBRAC | E2 BCOMM | E4 ECOMM | E6 |
+ | E8 ECOML | EA WITH | EC | EE |
+ | F0 | F2 | F4 | F6 |
+ | F8 | FA | FC | FE LENG |
+ +-----------------------------------------------+
+ * 50 EHDECL is also MOD2.
+ * 48 BSLINE is also BROWS.
+ */
diff --git a/gnu/usr.bin/gdb/gdb/aout/stab_gnu.h b/gnu/usr.bin/gdb/gdb/aout/stab_gnu.h
new file mode 100644
index 0000000..477b87d
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/aout/stab_gnu.h
@@ -0,0 +1,36 @@
+#ifndef __GNU_STAB__
+
+/* Indicate the GNU stab.h is in use. */
+
+#define __GNU_STAB__
+
+#define __define_stab(NAME, CODE, STRING) NAME=CODE,
+
+enum __stab_debug_code
+{
+#include "aout/stab.def"
+LAST_UNUSED_STAB_CODE
+};
+
+#undef __define_stab
+
+/* Definitions of "desc" field for N_SO stabs in Solaris2. */
+
+#define N_SO_AS 1
+#define N_SO_C 2
+#define N_SO_ANSI_C 3
+#define N_SO_CC 4 /* C++ */
+#define N_SO_FORTRAN 5
+#define N_SO_PASCAL 6
+
+/* Solaris2: Floating point type values in basic types. */
+
+#define NF_NONE 0
+#define NF_SINGLE 1 /* IEEE 32-bit */
+#define NF_DOUBLE 2 /* IEEE 64-bit */
+#define NF_COMPLEX 3 /* Fortran complex */
+#define NF_COMPLEX16 4 /* Fortran double complex */
+#define NF_COMPLEX32 5 /* Fortran complex*16 */
+#define NF_LDOUBLE 6 /* Long double (whatever that is) */
+
+#endif /* __GNU_STAB_ */
diff --git a/gnu/usr.bin/gdb/gdb/blockframe.c b/gnu/usr.bin/gdb/gdb/blockframe.c
new file mode 100644
index 0000000..c7b3fdc
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/blockframe.c
@@ -0,0 +1,821 @@
+/* Get info from stack frames;
+ convert between frames, blocks, functions and pc values.
+ Copyright 1986, 1987, 1988, 1989, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "symtab.h"
+#include "bfd.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "frame.h"
+#include "gdbcore.h"
+#include "value.h" /* for read_register */
+#include "target.h" /* for target_has_stack */
+#include "inferior.h" /* for read_pc */
+
+/* Is ADDR inside the startup file? Note that if your machine
+ has a way to detect the bottom of the stack, there is no need
+ to call this function from FRAME_CHAIN_VALID; the reason for
+ doing so is that some machines have no way of detecting bottom
+ of stack.
+
+ A PC of zero is always considered to be the bottom of the stack. */
+
+int
+inside_entry_file (addr)
+ CORE_ADDR addr;
+{
+ if (addr == 0)
+ return 1;
+ if (symfile_objfile == 0)
+ return 0;
+#if CALL_DUMMY_LOCATION == AT_ENTRY_POINT
+ /* Do not stop backtracing if the pc is in the call dummy
+ at the entry point. */
+ if (PC_IN_CALL_DUMMY (addr, 0, 0))
+ return 0;
+#endif
+ return (addr >= symfile_objfile -> ei.entry_file_lowpc &&
+ addr < symfile_objfile -> ei.entry_file_highpc);
+}
+
+/* Test a specified PC value to see if it is in the range of addresses
+ that correspond to the main() function. See comments above for why
+ we might want to do this.
+
+ Typically called from FRAME_CHAIN_VALID.
+
+ A PC of zero is always considered to be the bottom of the stack. */
+
+int
+inside_main_func (pc)
+CORE_ADDR pc;
+{
+ if (pc == 0)
+ return 1;
+ if (symfile_objfile == 0)
+ return 0;
+ return (symfile_objfile -> ei.main_func_lowpc <= pc &&
+ symfile_objfile -> ei.main_func_highpc > pc);
+}
+
+/* Test a specified PC value to see if it is in the range of addresses
+ that correspond to the process entry point function. See comments
+ in objfiles.h for why we might want to do this.
+
+ Typically called from FRAME_CHAIN_VALID.
+
+ A PC of zero is always considered to be the bottom of the stack. */
+
+int
+inside_entry_func (pc)
+CORE_ADDR pc;
+{
+ if (pc == 0)
+ return 1;
+ if (symfile_objfile == 0)
+ return 0;
+#if CALL_DUMMY_LOCATION == AT_ENTRY_POINT
+ /* Do not stop backtracing if the pc is in the call dummy
+ at the entry point. */
+ if (PC_IN_CALL_DUMMY (pc, 0, 0))
+ return 0;
+#endif
+ return (symfile_objfile -> ei.entry_func_lowpc <= pc &&
+ symfile_objfile -> ei.entry_func_highpc > pc);
+}
+
+/* Address of innermost stack frame (contents of FP register) */
+
+static FRAME current_frame;
+
+/*
+ * Cache for frame addresses already read by gdb. Valid only while
+ * inferior is stopped. Control variables for the frame cache should
+ * be local to this module.
+ */
+struct obstack frame_cache_obstack;
+
+/* Return the innermost (currently executing) stack frame. */
+
+FRAME
+get_current_frame ()
+{
+ /* We assume its address is kept in a general register;
+ param.h says which register. */
+
+ return current_frame;
+}
+
+void
+set_current_frame (frame)
+ FRAME frame;
+{
+ current_frame = frame;
+}
+
+FRAME
+create_new_frame (addr, pc)
+ FRAME_ADDR addr;
+ CORE_ADDR pc;
+{
+ struct frame_info *fci; /* Same type as FRAME */
+ char *name;
+
+ fci = (struct frame_info *)
+ obstack_alloc (&frame_cache_obstack,
+ sizeof (struct frame_info));
+
+ /* Arbitrary frame */
+ fci->next = (struct frame_info *) 0;
+ fci->prev = (struct frame_info *) 0;
+ fci->frame = addr;
+ fci->pc = pc;
+ find_pc_partial_function (pc, &name, (CORE_ADDR *)NULL,(CORE_ADDR *)NULL);
+ fci->signal_handler_caller = IN_SIGTRAMP (fci->pc, name);
+
+#ifdef INIT_EXTRA_FRAME_INFO
+ INIT_EXTRA_FRAME_INFO (0, fci);
+#endif
+
+ return fci;
+}
+
+/* Return the frame that called FRAME.
+ If FRAME is the original frame (it has no caller), return 0. */
+
+FRAME
+get_prev_frame (frame)
+ FRAME frame;
+{
+ /* We're allowed to know that FRAME and "struct frame_info *" are
+ the same */
+ return get_prev_frame_info (frame);
+}
+
+/* Return the frame that FRAME calls (0 if FRAME is the innermost
+ frame). */
+
+FRAME
+get_next_frame (frame)
+ FRAME frame;
+{
+ /* We're allowed to know that FRAME and "struct frame_info *" are
+ the same */
+ return frame->next;
+}
+
+/*
+ * Flush the entire frame cache.
+ */
+void
+flush_cached_frames ()
+{
+ /* Since we can't really be sure what the first object allocated was */
+ obstack_free (&frame_cache_obstack, 0);
+ obstack_init (&frame_cache_obstack);
+
+ current_frame = (struct frame_info *) 0; /* Invalidate cache */
+}
+
+/* Flush the frame cache, and start a new one if necessary. */
+void
+reinit_frame_cache ()
+{
+ flush_cached_frames ();
+ if (target_has_stack)
+ {
+ set_current_frame (create_new_frame (read_fp (), read_pc ()));
+ select_frame (get_current_frame (), 0);
+ }
+ else
+ {
+ set_current_frame (0);
+ select_frame ((FRAME) 0, -1);
+ }
+}
+
+/* Return a structure containing various interesting information
+ about a specified stack frame. */
+/* How do I justify including this function? Well, the FRAME
+ identifier format has gone through several changes recently, and
+ it's not completely inconceivable that it could happen again. If
+ it does, have this routine around will help */
+
+struct frame_info *
+get_frame_info (frame)
+ FRAME frame;
+{
+ return frame;
+}
+
+/* If a machine allows frameless functions, it should define a macro
+ FRAMELESS_FUNCTION_INVOCATION(FI, FRAMELESS) in param.h. FI is the struct
+ frame_info for the frame, and FRAMELESS should be set to nonzero
+ if it represents a frameless function invocation. */
+
+/* Return nonzero if the function for this frame lacks a prologue. Many
+ machines can define FRAMELESS_FUNCTION_INVOCATION to just call this
+ function. */
+
+int
+frameless_look_for_prologue (frame)
+ FRAME frame;
+{
+ CORE_ADDR func_start, after_prologue;
+ func_start = (get_pc_function_start (frame->pc) +
+ FUNCTION_START_OFFSET);
+ if (func_start)
+ {
+ after_prologue = func_start;
+#ifdef SKIP_PROLOGUE_FRAMELESS_P
+ /* This is faster, since only care whether there *is* a prologue,
+ not how long it is. */
+ SKIP_PROLOGUE_FRAMELESS_P (after_prologue);
+#else
+ SKIP_PROLOGUE (after_prologue);
+#endif
+ return after_prologue == func_start;
+ }
+ else
+ /* If we can't find the start of the function, we don't really
+ know whether the function is frameless, but we should be able
+ to get a reasonable (i.e. best we can do under the
+ circumstances) backtrace by saying that it isn't. */
+ return 0;
+}
+
+/* Default a few macros that people seldom redefine. */
+
+#if !defined (INIT_FRAME_PC)
+#define INIT_FRAME_PC(fromleaf, prev) \
+ prev->pc = (fromleaf ? SAVED_PC_AFTER_CALL (prev->next) : \
+ prev->next ? FRAME_SAVED_PC (prev->next) : read_pc ());
+#endif
+
+#ifndef FRAME_CHAIN_COMBINE
+#define FRAME_CHAIN_COMBINE(chain, thisframe) (chain)
+#endif
+
+/* Return a structure containing various interesting information
+ about the frame that called NEXT_FRAME. Returns NULL
+ if there is no such frame. */
+
+struct frame_info *
+get_prev_frame_info (next_frame)
+ FRAME next_frame;
+{
+ FRAME_ADDR address = 0;
+ struct frame_info *prev;
+ int fromleaf = 0;
+ char *name;
+
+ /* If the requested entry is in the cache, return it.
+ Otherwise, figure out what the address should be for the entry
+ we're about to add to the cache. */
+
+ if (!next_frame)
+ {
+#if 0
+ /* This screws value_of_variable, which just wants a nice clean
+ NULL return from block_innermost_frame if there are no frames.
+ I don't think I've ever seen this message happen otherwise.
+ And returning NULL here is a perfectly legitimate thing to do. */
+ if (!current_frame)
+ {
+ error ("You haven't set up a process's stack to examine.");
+ }
+#endif
+
+ return current_frame;
+ }
+
+ /* If we have the prev one, return it */
+ if (next_frame->prev)
+ return next_frame->prev;
+
+ /* On some machines it is possible to call a function without
+ setting up a stack frame for it. On these machines, we
+ define this macro to take two args; a frameinfo pointer
+ identifying a frame and a variable to set or clear if it is
+ or isn't leafless. */
+#ifdef FRAMELESS_FUNCTION_INVOCATION
+ /* Still don't want to worry about this except on the innermost
+ frame. This macro will set FROMLEAF if NEXT_FRAME is a
+ frameless function invocation. */
+ if (!(next_frame->next))
+ {
+ FRAMELESS_FUNCTION_INVOCATION (next_frame, fromleaf);
+ if (fromleaf)
+ address = next_frame->frame;
+ }
+#endif
+
+ if (!fromleaf)
+ {
+ /* Two macros defined in tm.h specify the machine-dependent
+ actions to be performed here.
+ First, get the frame's chain-pointer.
+ If that is zero, the frame is the outermost frame or a leaf
+ called by the outermost frame. This means that if start
+ calls main without a frame, we'll return 0 (which is fine
+ anyway).
+
+ Nope; there's a problem. This also returns when the current
+ routine is a leaf of main. This is unacceptable. We move
+ this to after the ffi test; I'd rather have backtraces from
+ start go curfluy than have an abort called from main not show
+ main. */
+ address = FRAME_CHAIN (next_frame);
+ if (!FRAME_CHAIN_VALID (address, next_frame))
+ return 0;
+ address = FRAME_CHAIN_COMBINE (address, next_frame);
+ }
+ if (address == 0)
+ return 0;
+
+ prev = (struct frame_info *)
+ obstack_alloc (&frame_cache_obstack,
+ sizeof (struct frame_info));
+
+ if (next_frame)
+ next_frame->prev = prev;
+ prev->next = next_frame;
+ prev->prev = (struct frame_info *) 0;
+ prev->frame = address;
+ prev->signal_handler_caller = 0;
+
+/* This change should not be needed, FIXME! We should
+ determine whether any targets *need* INIT_FRAME_PC to happen
+ after INIT_EXTRA_FRAME_INFO and come up with a simple way to
+ express what goes on here.
+
+ INIT_EXTRA_FRAME_INFO is called from two places: create_new_frame
+ (where the PC is already set up) and here (where it isn't).
+ INIT_FRAME_PC is only called from here, always after
+ INIT_EXTRA_FRAME_INFO.
+
+ The catch is the MIPS, where INIT_EXTRA_FRAME_INFO requires the PC
+ value (which hasn't been set yet). Some other machines appear to
+ require INIT_EXTRA_FRAME_INFO before they can do INIT_FRAME_PC. Phoo.
+
+ We shouldn't need INIT_FRAME_PC_FIRST to add more complication to
+ an already overcomplicated part of GDB. gnu@cygnus.com, 15Sep92.
+
+ To answer the question, yes the sparc needs INIT_FRAME_PC after
+ INIT_EXTRA_FRAME_INFO. Suggested scheme:
+
+ SETUP_INNERMOST_FRAME()
+ Default version is just create_new_frame (read_fp ()),
+ read_pc ()). Machines with extra frame info would do that (or the
+ local equivalent) and then set the extra fields.
+ SETUP_ARBITRARY_FRAME(argc, argv)
+ Only change here is that create_new_frame would no longer init extra
+ frame info; SETUP_ARBITRARY_FRAME would have to do that.
+ INIT_PREV_FRAME(fromleaf, prev)
+ Replace INIT_EXTRA_FRAME_INFO and INIT_FRAME_PC.
+ std_frame_pc(fromleaf, prev)
+ This is the default setting for INIT_PREV_FRAME. It just does what
+ the default INIT_FRAME_PC does. Some machines will call it from
+ INIT_PREV_FRAME (either at the beginning, the end, or in the middle).
+ Some machines won't use it.
+ kingdon@cygnus.com, 13Apr93. */
+
+#ifdef INIT_FRAME_PC_FIRST
+ INIT_FRAME_PC_FIRST (fromleaf, prev);
+#endif
+
+#ifdef INIT_EXTRA_FRAME_INFO
+ INIT_EXTRA_FRAME_INFO(fromleaf, prev);
+#endif
+
+ /* This entry is in the frame queue now, which is good since
+ FRAME_SAVED_PC may use that queue to figure out it's value
+ (see tm-sparc.h). We want the pc saved in the inferior frame. */
+ INIT_FRAME_PC(fromleaf, prev);
+
+ find_pc_partial_function (prev->pc, &name,
+ (CORE_ADDR *)NULL,(CORE_ADDR *)NULL);
+ if (IN_SIGTRAMP (prev->pc, name))
+ prev->signal_handler_caller = 1;
+
+ return prev;
+}
+
+CORE_ADDR
+get_frame_pc (frame)
+ FRAME frame;
+{
+ struct frame_info *fi;
+ fi = get_frame_info (frame);
+ return fi->pc;
+}
+
+#if defined (FRAME_FIND_SAVED_REGS)
+/* Find the addresses in which registers are saved in FRAME. */
+
+void
+get_frame_saved_regs (frame_info_addr, saved_regs_addr)
+ struct frame_info *frame_info_addr;
+ struct frame_saved_regs *saved_regs_addr;
+{
+ FRAME_FIND_SAVED_REGS (frame_info_addr, *saved_regs_addr);
+}
+#endif
+
+/* Return the innermost lexical block in execution
+ in a specified stack frame. The frame address is assumed valid. */
+
+struct block *
+get_frame_block (frame)
+ FRAME frame;
+{
+ struct frame_info *fi;
+ CORE_ADDR pc;
+
+ fi = get_frame_info (frame);
+
+ pc = fi->pc;
+ if (fi->next != 0 && fi->next->signal_handler_caller == 0)
+ /* We are not in the innermost frame and we were not interrupted
+ by a signal. We need to subtract one to get the correct block,
+ in case the call instruction was the last instruction of the block.
+ If there are any machines on which the saved pc does not point to
+ after the call insn, we probably want to make fi->pc point after
+ the call insn anyway. */
+ --pc;
+ return block_for_pc (pc);
+}
+
+struct block *
+get_current_block ()
+{
+ return block_for_pc (read_pc ());
+}
+
+CORE_ADDR
+get_pc_function_start (pc)
+ CORE_ADDR pc;
+{
+ register struct block *bl;
+ register struct symbol *symbol;
+ register struct minimal_symbol *msymbol;
+ CORE_ADDR fstart;
+
+ if ((bl = block_for_pc (pc)) != NULL &&
+ (symbol = block_function (bl)) != NULL)
+ {
+ bl = SYMBOL_BLOCK_VALUE (symbol);
+ fstart = BLOCK_START (bl);
+ }
+ else if ((msymbol = lookup_minimal_symbol_by_pc (pc)) != NULL)
+ {
+ fstart = SYMBOL_VALUE_ADDRESS (msymbol);
+ }
+ else
+ {
+ fstart = 0;
+ }
+ return (fstart);
+}
+
+/* Return the symbol for the function executing in frame FRAME. */
+
+struct symbol *
+get_frame_function (frame)
+ FRAME frame;
+{
+ register struct block *bl = get_frame_block (frame);
+ if (bl == 0)
+ return 0;
+ return block_function (bl);
+}
+
+/* Return the blockvector immediately containing the innermost lexical block
+ containing the specified pc value, or 0 if there is none.
+ PINDEX is a pointer to the index value of the block. If PINDEX
+ is NULL, we don't pass this information back to the caller. */
+
+struct blockvector *
+blockvector_for_pc (pc, pindex)
+ register CORE_ADDR pc;
+ int *pindex;
+{
+ register struct block *b;
+ register int bot, top, half;
+ register struct symtab *s;
+ struct blockvector *bl;
+
+ /* First search all symtabs for one whose file contains our pc */
+ s = find_pc_symtab (pc);
+ if (s == 0)
+ return 0;
+
+ bl = BLOCKVECTOR (s);
+ b = BLOCKVECTOR_BLOCK (bl, 0);
+
+ /* Then search that symtab for the smallest block that wins. */
+ /* Use binary search to find the last block that starts before PC. */
+
+ bot = 0;
+ top = BLOCKVECTOR_NBLOCKS (bl);
+
+ while (top - bot > 1)
+ {
+ half = (top - bot + 1) >> 1;
+ b = BLOCKVECTOR_BLOCK (bl, bot + half);
+ if (BLOCK_START (b) <= pc)
+ bot += half;
+ else
+ top = bot + half;
+ }
+
+ /* Now search backward for a block that ends after PC. */
+
+ while (bot >= 0)
+ {
+ b = BLOCKVECTOR_BLOCK (bl, bot);
+ if (BLOCK_END (b) > pc)
+ {
+ if (pindex)
+ *pindex = bot;
+ return bl;
+ }
+ bot--;
+ }
+
+ return 0;
+}
+
+/* Return the innermost lexical block containing the specified pc value,
+ or 0 if there is none. */
+
+struct block *
+block_for_pc (pc)
+ register CORE_ADDR pc;
+{
+ register struct blockvector *bl;
+ int index;
+
+ bl = blockvector_for_pc (pc, &index);
+ if (bl)
+ return BLOCKVECTOR_BLOCK (bl, index);
+ return 0;
+}
+
+/* Return the function containing pc value PC.
+ Returns 0 if function is not known. */
+
+struct symbol *
+find_pc_function (pc)
+ CORE_ADDR pc;
+{
+ register struct block *b = block_for_pc (pc);
+ if (b == 0)
+ return 0;
+ return block_function (b);
+}
+
+/* These variables are used to cache the most recent result
+ * of find_pc_partial_function. */
+
+static CORE_ADDR cache_pc_function_low = 0;
+static CORE_ADDR cache_pc_function_high = 0;
+static char *cache_pc_function_name = 0;
+
+/* Clear cache, e.g. when symbol table is discarded. */
+
+void
+clear_pc_function_cache()
+{
+ cache_pc_function_low = 0;
+ cache_pc_function_high = 0;
+ cache_pc_function_name = (char *)0;
+}
+
+/* Finds the "function" (text symbol) that is smaller than PC but
+ greatest of all of the potential text symbols. Sets *NAME and/or
+ *ADDRESS conditionally if that pointer is non-null. If ENDADDR is
+ non-null, then set *ENDADDR to be the end of the function
+ (exclusive), but passing ENDADDR as non-null means that the
+ function might cause symbols to be read. This function either
+ succeeds or fails (not halfway succeeds). If it succeeds, it sets
+ *NAME, *ADDRESS, and *ENDADDR to real information and returns 1.
+ If it fails, it sets *NAME, *ADDRESS, and *ENDADDR to zero
+ and returns 0. */
+
+int
+find_pc_partial_function (pc, name, address, endaddr)
+ CORE_ADDR pc;
+ char **name;
+ CORE_ADDR *address;
+ CORE_ADDR *endaddr;
+{
+ struct partial_symtab *pst;
+ struct symbol *f;
+ struct minimal_symbol *msymbol;
+ struct partial_symbol *psb;
+ struct obj_section *sec;
+
+ if (pc >= cache_pc_function_low && pc < cache_pc_function_high)
+ goto return_cached_value;
+
+ /* If sigtramp is in the u area, it counts as a function (especially
+ important for step_1). */
+#if defined SIGTRAMP_START
+ if (IN_SIGTRAMP (pc, (char *)NULL))
+ {
+ cache_pc_function_low = SIGTRAMP_START;
+ cache_pc_function_high = SIGTRAMP_END;
+ cache_pc_function_name = "<sigtramp>";
+
+ goto return_cached_value;
+ }
+#endif
+
+ msymbol = lookup_minimal_symbol_by_pc (pc);
+ pst = find_pc_psymtab (pc);
+ if (pst)
+ {
+ /* Need to read the symbols to get a good value for the end address. */
+ if (endaddr != NULL && !pst->readin)
+ {
+ /* Need to get the terminal in case symbol-reading produces
+ output. */
+ target_terminal_ours_for_output ();
+ PSYMTAB_TO_SYMTAB (pst);
+ }
+
+ if (pst->readin)
+ {
+ /* Checking whether the msymbol has a larger value is for the
+ "pathological" case mentioned in print_frame_info. */
+ f = find_pc_function (pc);
+ if (f != NULL
+ && (msymbol == NULL
+ || (BLOCK_START (SYMBOL_BLOCK_VALUE (f))
+ >= SYMBOL_VALUE_ADDRESS (msymbol))))
+ {
+ cache_pc_function_low = BLOCK_START (SYMBOL_BLOCK_VALUE (f));
+ cache_pc_function_high = BLOCK_END (SYMBOL_BLOCK_VALUE (f));
+ cache_pc_function_name = SYMBOL_NAME (f);
+ goto return_cached_value;
+ }
+ }
+ else
+ {
+ /* Now that static symbols go in the minimal symbol table, perhaps
+ we could just ignore the partial symbols. But at least for now
+ we use the partial or minimal symbol, whichever is larger. */
+ psb = find_pc_psymbol (pst, pc);
+
+ if (psb
+ && (msymbol == NULL ||
+ (SYMBOL_VALUE_ADDRESS (psb)
+ >= SYMBOL_VALUE_ADDRESS (msymbol))))
+ {
+ /* This case isn't being cached currently. */
+ if (address)
+ *address = SYMBOL_VALUE_ADDRESS (psb);
+ if (name)
+ *name = SYMBOL_NAME (psb);
+ /* endaddr non-NULL can't happen here. */
+ return 1;
+ }
+ }
+ }
+
+ /* Not in the normal symbol tables, see if the pc is in a known section.
+ If it's not, then give up. This ensures that anything beyond the end
+ of the text seg doesn't appear to be part of the last function in the
+ text segment. */
+
+ sec = find_pc_section (pc);
+
+ if (!sec)
+ msymbol = NULL;
+
+ /* Must be in the minimal symbol table. */
+ if (msymbol == NULL)
+ {
+ /* No available symbol. */
+ if (name != NULL)
+ *name = 0;
+ if (address != NULL)
+ *address = 0;
+ if (endaddr != NULL)
+ *endaddr = 0;
+ return 0;
+ }
+
+ /* See if we're in a transfer table for Sun shared libs. */
+
+ if (msymbol -> type == mst_text)
+ cache_pc_function_low = SYMBOL_VALUE_ADDRESS (msymbol);
+ else
+ /* It is a transfer table for Sun shared libraries. */
+ cache_pc_function_low = pc - FUNCTION_START_OFFSET;
+
+ cache_pc_function_name = SYMBOL_NAME (msymbol);
+
+ /* Use the lesser of the next minimal symbol, or the end of the section, as
+ the end of the function. */
+
+ if (SYMBOL_NAME (msymbol + 1) != NULL
+ && SYMBOL_VALUE_ADDRESS (msymbol + 1) < sec->endaddr)
+ cache_pc_function_high = SYMBOL_VALUE_ADDRESS (msymbol + 1);
+ else
+ /* We got the start address from the last msymbol in the objfile.
+ So the end address is the end of the section. */
+ cache_pc_function_high = sec->endaddr;
+
+ return_cached_value:
+ if (address)
+ *address = cache_pc_function_low;
+ if (name)
+ *name = cache_pc_function_name;
+ if (endaddr)
+ *endaddr = cache_pc_function_high;
+ return 1;
+}
+
+/* Return the innermost stack frame executing inside of BLOCK,
+ or NULL if there is no such frame. If BLOCK is NULL, just return NULL. */
+
+FRAME
+block_innermost_frame (block)
+ struct block *block;
+{
+ struct frame_info *fi;
+ register FRAME frame;
+ register CORE_ADDR start;
+ register CORE_ADDR end;
+
+ if (block == NULL)
+ return NULL;
+
+ start = BLOCK_START (block);
+ end = BLOCK_END (block);
+
+ frame = 0;
+ while (1)
+ {
+ frame = get_prev_frame (frame);
+ if (frame == 0)
+ return 0;
+ fi = get_frame_info (frame);
+ if (fi->pc >= start && fi->pc < end)
+ return frame;
+ }
+}
+
+#ifdef SIGCONTEXT_PC_OFFSET
+/* Get saved user PC for sigtramp from sigcontext for BSD style sigtramp. */
+
+CORE_ADDR
+sigtramp_saved_pc (frame)
+ FRAME frame;
+{
+ CORE_ADDR sigcontext_addr;
+ char buf[TARGET_PTR_BIT / TARGET_CHAR_BIT];
+ int ptrbytes = TARGET_PTR_BIT / TARGET_CHAR_BIT;
+ int sigcontext_offs = (2 * TARGET_INT_BIT) / TARGET_CHAR_BIT;
+
+ /* Get sigcontext address, it is the third parameter on the stack. */
+ if (frame->next)
+ sigcontext_addr = read_memory_integer (FRAME_ARGS_ADDRESS (frame->next)
+ + FRAME_ARGS_SKIP + sigcontext_offs,
+ ptrbytes);
+ else
+ sigcontext_addr = read_memory_integer (read_register (SP_REGNUM)
+ + sigcontext_offs,
+ ptrbytes);
+
+ /* Don't cause a memory_error when accessing sigcontext in case the stack
+ layout has changed or the stack is corrupt. */
+ target_read_memory (sigcontext_addr + SIGCONTEXT_PC_OFFSET, buf, ptrbytes);
+ return extract_unsigned_integer (buf, ptrbytes);
+}
+#endif /* SIGCONTEXT_PC_OFFSET */
+
+void
+_initialize_blockframe ()
+{
+ obstack_init (&frame_cache_obstack);
+}
diff --git a/gnu/usr.bin/gdb/gdb/breakpoint.c b/gnu/usr.bin/gdb/gdb/breakpoint.c
new file mode 100644
index 0000000..69694c0
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/breakpoint.c
@@ -0,0 +1,3301 @@
+/* Everything about breakpoints, for GDB.
+ Copyright 1986, 1987, 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include <ctype.h>
+#include "symtab.h"
+#include "frame.h"
+#include "breakpoint.h"
+#include "gdbtypes.h"
+#include "expression.h"
+#include "gdbcore.h"
+#include "gdbcmd.h"
+#include "value.h"
+#include "ctype.h"
+#include "command.h"
+#include "inferior.h"
+#include "target.h"
+#include "language.h"
+#include <string.h>
+#include "demangle.h"
+
+/* local function prototypes */
+
+static void
+catch_command_1 PARAMS ((char *, int, int));
+
+static void
+enable_delete_command PARAMS ((char *, int));
+
+static void
+enable_delete_breakpoint PARAMS ((struct breakpoint *));
+
+static void
+enable_once_command PARAMS ((char *, int));
+
+static void
+enable_once_breakpoint PARAMS ((struct breakpoint *));
+
+static void
+disable_command PARAMS ((char *, int));
+
+static void
+disable_breakpoint PARAMS ((struct breakpoint *));
+
+static void
+enable_command PARAMS ((char *, int));
+
+static void
+enable_breakpoint PARAMS ((struct breakpoint *));
+
+static void
+map_breakpoint_numbers PARAMS ((char *, void (*)(struct breakpoint *)));
+
+static void
+ignore_command PARAMS ((char *, int));
+
+static int
+breakpoint_re_set_one PARAMS ((char *));
+
+static void
+delete_command PARAMS ((char *, int));
+
+static void
+clear_command PARAMS ((char *, int));
+
+static void
+catch_command PARAMS ((char *, int));
+
+static struct symtabs_and_lines
+get_catch_sals PARAMS ((int));
+
+static void
+watch_command PARAMS ((char *, int));
+
+static void
+tbreak_command PARAMS ((char *, int));
+
+static void
+break_command_1 PARAMS ((char *, int, int));
+
+static void
+mention PARAMS ((struct breakpoint *));
+
+static struct breakpoint *
+set_raw_breakpoint PARAMS ((struct symtab_and_line));
+
+static void
+check_duplicates PARAMS ((CORE_ADDR));
+
+static void
+describe_other_breakpoints PARAMS ((CORE_ADDR));
+
+static void
+breakpoints_info PARAMS ((char *, int));
+
+static void
+breakpoint_1 PARAMS ((int, int));
+
+static bpstat
+bpstat_alloc PARAMS ((struct breakpoint *, bpstat));
+
+static int
+breakpoint_cond_eval PARAMS ((char *));
+
+static void
+cleanup_executing_breakpoints PARAMS ((int));
+
+static void
+commands_command PARAMS ((char *, int));
+
+static void
+condition_command PARAMS ((char *, int));
+
+static int
+get_number PARAMS ((char **));
+
+static void
+set_breakpoint_count PARAMS ((int));
+
+
+extern int addressprint; /* Print machine addresses? */
+extern int demangle; /* Print de-mangled symbol names? */
+
+/* Are we executing breakpoint commands? */
+static int executing_breakpoint_commands;
+
+/* Walk the following statement or block through all breakpoints.
+ ALL_BREAKPOINTS_SAFE does so even if the statment deletes the current
+ breakpoint. */
+
+#define ALL_BREAKPOINTS(b) for (b = breakpoint_chain; b; b = b->next)
+
+#define ALL_BREAKPOINTS_SAFE(b,tmp) \
+ for (b = breakpoint_chain; \
+ b? (tmp=b->next, 1): 0; \
+ b = tmp)
+
+/* Chain of all breakpoints defined. */
+
+struct breakpoint *breakpoint_chain;
+
+/* Number of last breakpoint made. */
+
+static int breakpoint_count;
+
+/* Set breakpoint count to NUM. */
+static void
+set_breakpoint_count (num)
+ int num;
+{
+ breakpoint_count = num;
+ set_internalvar (lookup_internalvar ("bpnum"),
+ value_from_longest (builtin_type_int, (LONGEST) num));
+}
+
+/* Default address, symtab and line to put a breakpoint at
+ for "break" command with no arg.
+ if default_breakpoint_valid is zero, the other three are
+ not valid, and "break" with no arg is an error.
+
+ This set by print_stack_frame, which calls set_default_breakpoint. */
+
+int default_breakpoint_valid;
+CORE_ADDR default_breakpoint_address;
+struct symtab *default_breakpoint_symtab;
+int default_breakpoint_line;
+
+/* Flag indicating extra verbosity for xgdb. */
+extern int xgdb_verbose;
+
+/* *PP is a string denoting a breakpoint. Get the number of the breakpoint.
+ Advance *PP after the string and any trailing whitespace.
+
+ Currently the string can either be a number or "$" followed by the name
+ of a convenience variable. Making it an expression wouldn't work well
+ for map_breakpoint_numbers (e.g. "4 + 5 + 6"). */
+static int
+get_number (pp)
+ char **pp;
+{
+ int retval;
+ char *p = *pp;
+
+ if (p == NULL)
+ /* Empty line means refer to the last breakpoint. */
+ return breakpoint_count;
+ else if (*p == '$')
+ {
+ /* Make a copy of the name, so we can null-terminate it
+ to pass to lookup_internalvar(). */
+ char *varname;
+ char *start = ++p;
+ value val;
+
+ while (isalnum (*p) || *p == '_')
+ p++;
+ varname = (char *) alloca (p - start + 1);
+ strncpy (varname, start, p - start);
+ varname[p - start] = '\0';
+ val = value_of_internalvar (lookup_internalvar (varname));
+ if (TYPE_CODE (VALUE_TYPE (val)) != TYPE_CODE_INT)
+ error (
+"Convenience variables used to specify breakpoints must have integer values."
+ );
+ retval = (int) value_as_long (val);
+ }
+ else
+ {
+ if (*p == '-')
+ ++p;
+ while (*p >= '0' && *p <= '9')
+ ++p;
+ if (p == *pp)
+ /* There is no number here. (e.g. "cond a == b"). */
+ error_no_arg ("breakpoint number");
+ retval = atoi (*pp);
+ }
+ if (!(isspace (*p) || *p == '\0'))
+ error ("breakpoint number expected");
+ while (isspace (*p))
+ p++;
+ *pp = p;
+ return retval;
+}
+
+/* condition N EXP -- set break condition of breakpoint N to EXP. */
+
+static void
+condition_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ register struct breakpoint *b;
+ char *p;
+ register int bnum;
+
+ if (arg == 0)
+ error_no_arg ("breakpoint number");
+
+ p = arg;
+ bnum = get_number (&p);
+
+ ALL_BREAKPOINTS (b)
+ if (b->number == bnum)
+ {
+ if (b->cond)
+ {
+ free ((PTR)b->cond);
+ b->cond = 0;
+ }
+ if (b->cond_string != NULL)
+ free ((PTR)b->cond_string);
+
+ if (*p == 0)
+ {
+ b->cond = 0;
+ b->cond_string = NULL;
+ if (from_tty)
+ printf_filtered ("Breakpoint %d now unconditional.\n", bnum);
+ }
+ else
+ {
+ arg = p;
+ /* I don't know if it matters whether this is the string the user
+ typed in or the decompiled expression. */
+ b->cond_string = savestring (arg, strlen (arg));
+ b->cond = parse_exp_1 (&arg, block_for_pc (b->address), 0);
+ if (*arg)
+ error ("Junk at end of expression");
+ }
+ return;
+ }
+
+ error ("No breakpoint number %d.", bnum);
+}
+
+/* ARGSUSED */
+static void
+commands_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ register struct breakpoint *b;
+ char *p;
+ register int bnum;
+ struct command_line *l;
+
+ /* If we allowed this, we would have problems with when to
+ free the storage, if we change the commands currently
+ being read from. */
+
+ if (executing_breakpoint_commands)
+ error ("Can't use the \"commands\" command among a breakpoint's commands.");
+
+ p = arg;
+ bnum = get_number (&p);
+ if (p && *p)
+ error ("Unexpected extra arguments following breakpoint number.");
+
+ ALL_BREAKPOINTS (b)
+ if (b->number == bnum)
+ {
+ if (from_tty && input_from_terminal_p ())
+ printf_filtered ("Type commands for when breakpoint %d is hit, one per line.\n\
+End with a line saying just \"end\".\n", bnum);
+ l = read_command_lines ();
+ free_command_lines (&b->commands);
+ b->commands = l;
+ return;
+ }
+ error ("No breakpoint number %d.", bnum);
+}
+
+extern int memory_breakpoint_size; /* from mem-break.c */
+
+/* Like target_read_memory() but if breakpoints are inserted, return
+ the shadow contents instead of the breakpoints themselves.
+
+ Read "memory data" from whatever target or inferior we have.
+ Returns zero if successful, errno value if not. EIO is used
+ for address out of bounds. If breakpoints are inserted, returns
+ shadow contents, not the breakpoints themselves. From breakpoint.c. */
+
+int
+read_memory_nobpt (memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ unsigned len;
+{
+ int status;
+ struct breakpoint *b;
+
+ if (memory_breakpoint_size < 0)
+ /* No breakpoints on this machine. FIXME: This should be
+ dependent on the debugging target. Probably want
+ target_insert_breakpoint to return a size, saying how many
+ bytes of the shadow contents are used, or perhaps have
+ something like target_xfer_shadow. */
+ return target_read_memory (memaddr, myaddr, len);
+
+ ALL_BREAKPOINTS (b)
+ {
+ if (b->type == bp_watchpoint || !b->inserted)
+ continue;
+ else if (b->address + memory_breakpoint_size <= memaddr)
+ /* The breakpoint is entirely before the chunk of memory
+ we are reading. */
+ continue;
+ else if (b->address >= memaddr + len)
+ /* The breakpoint is entirely after the chunk of memory we
+ are reading. */
+ continue;
+ else
+ {
+ /* Copy the breakpoint from the shadow contents, and recurse
+ for the things before and after. */
+
+ /* Addresses and length of the part of the breakpoint that
+ we need to copy. */
+ CORE_ADDR membpt = b->address;
+ unsigned int bptlen = memory_breakpoint_size;
+ /* Offset within shadow_contents. */
+ int bptoffset = 0;
+
+ if (membpt < memaddr)
+ {
+ /* Only copy the second part of the breakpoint. */
+ bptlen -= memaddr - membpt;
+ bptoffset = memaddr - membpt;
+ membpt = memaddr;
+ }
+
+ if (membpt + bptlen > memaddr + len)
+ {
+ /* Only copy the first part of the breakpoint. */
+ bptlen -= (membpt + bptlen) - (memaddr + len);
+ }
+
+ memcpy (myaddr + membpt - memaddr,
+ b->shadow_contents + bptoffset, bptlen);
+
+ if (membpt > memaddr)
+ {
+ /* Copy the section of memory before the breakpoint. */
+ status = read_memory_nobpt (memaddr, myaddr, membpt - memaddr);
+ if (status != 0)
+ return status;
+ }
+
+ if (membpt + bptlen < memaddr + len)
+ {
+ /* Copy the section of memory after the breakpoint. */
+ status = read_memory_nobpt
+ (membpt + bptlen,
+ myaddr + membpt + bptlen - memaddr,
+ memaddr + len - (membpt + bptlen));
+ if (status != 0)
+ return status;
+ }
+ return 0;
+ }
+ }
+ /* Nothing overlaps. Just call read_memory_noerr. */
+ return target_read_memory (memaddr, myaddr, len);
+}
+
+/* insert_breakpoints is used when starting or continuing the program.
+ remove_breakpoints is used when the program stops.
+ Both return zero if successful,
+ or an `errno' value if could not write the inferior. */
+
+int
+insert_breakpoints ()
+{
+ register struct breakpoint *b;
+ int val = 0;
+ int disabled_breaks = 0;
+
+ ALL_BREAKPOINTS (b)
+ if (b->type != bp_watchpoint
+ && b->enable != disabled
+ && ! b->inserted
+ && ! b->duplicate)
+ {
+ val = target_insert_breakpoint(b->address, b->shadow_contents);
+ if (val)
+ {
+ /* Can't set the breakpoint. */
+#if defined (DISABLE_UNSETTABLE_BREAK)
+ if (DISABLE_UNSETTABLE_BREAK (b->address))
+ {
+ val = 0;
+ b->enable = disabled;
+ if (!disabled_breaks)
+ {
+ fprintf (stderr,
+ "Cannot insert breakpoint %d:\n", b->number);
+ printf_filtered ("Disabling shared library breakpoints:\n");
+ }
+ disabled_breaks = 1;
+ printf_filtered ("%d ", b->number);
+ }
+ else
+#endif
+ {
+ fprintf (stderr, "Cannot insert breakpoint %d:\n", b->number);
+#ifdef ONE_PROCESS_WRITETEXT
+ fprintf (stderr,
+ "The same program may be running in another process.\n");
+#endif
+ memory_error (val, b->address); /* which bombs us out */
+ }
+ }
+ else
+ b->inserted = 1;
+ }
+ if (disabled_breaks)
+ printf_filtered ("\n");
+ return val;
+}
+
+int
+remove_breakpoints ()
+{
+ register struct breakpoint *b;
+ int val;
+
+#ifdef BREAKPOINT_DEBUG
+ printf ("Removing breakpoints.\n");
+#endif /* BREAKPOINT_DEBUG */
+
+ ALL_BREAKPOINTS (b)
+ if (b->type != bp_watchpoint && b->inserted)
+ {
+ val = target_remove_breakpoint(b->address, b->shadow_contents);
+ if (val)
+ return val;
+ b->inserted = 0;
+#ifdef BREAKPOINT_DEBUG
+ printf ("Removed breakpoint at %s",
+ local_hex_string((unsigned long) b->address));
+ printf (", shadow %s",
+ local_hex_string((unsigned long) b->shadow_contents[0]));
+ printf (", %s.\n",
+ local_hex_string((unsigned long) b->shadow_contents[1]));
+#endif /* BREAKPOINT_DEBUG */
+ }
+
+ return 0;
+}
+
+/* Clear the "inserted" flag in all breakpoints. */
+
+void
+mark_breakpoints_out ()
+{
+ register struct breakpoint *b;
+
+ ALL_BREAKPOINTS (b)
+ b->inserted = 0;
+}
+
+/* Clear the "inserted" flag in all breakpoints and delete any breakpoints
+ which should go away between runs of the program. */
+
+void
+breakpoint_init_inferior ()
+{
+ register struct breakpoint *b, *temp;
+
+ ALL_BREAKPOINTS_SAFE (b, temp)
+ {
+ b->inserted = 0;
+
+ /* If the call dummy breakpoint is at the entry point it will
+ cause problems when the inferior is rerun, so we better
+ get rid of it. */
+ if (b->type == bp_call_dummy)
+ delete_breakpoint (b);
+ }
+}
+
+/* breakpoint_here_p (PC) returns 1 if an enabled breakpoint exists at PC.
+ When continuing from a location with a breakpoint,
+ we actually single step once before calling insert_breakpoints. */
+
+int
+breakpoint_here_p (pc)
+ CORE_ADDR pc;
+{
+ register struct breakpoint *b;
+
+ ALL_BREAKPOINTS (b)
+ if (b->enable != disabled && b->address == pc)
+ return 1;
+
+ return 0;
+}
+
+/* breakpoint_match_thread (PC, PID) returns true if the breakpoint at PC
+ is valid for process/thread PID. */
+
+int
+breakpoint_thread_match (pc, pid)
+ CORE_ADDR pc;
+ int pid;
+{
+ struct breakpoint *b;
+ int thread;
+
+ thread = pid_to_thread_id (pid);
+
+ ALL_BREAKPOINTS (b)
+ if (b->enable != disabled
+ && b->address == pc
+ && (b->thread == -1 || b->thread == thread))
+ return 1;
+
+ return 0;
+}
+
+
+/* bpstat stuff. External routines' interfaces are documented
+ in breakpoint.h. */
+
+/* Clear a bpstat so that it says we are not at any breakpoint.
+ Also free any storage that is part of a bpstat. */
+
+void
+bpstat_clear (bsp)
+ bpstat *bsp;
+{
+ bpstat p;
+ bpstat q;
+
+ if (bsp == 0)
+ return;
+ p = *bsp;
+ while (p != NULL)
+ {
+ q = p->next;
+ if (p->old_val != NULL)
+ value_free (p->old_val);
+ free ((PTR)p);
+ p = q;
+ }
+ *bsp = NULL;
+}
+
+/* Return a copy of a bpstat. Like "bs1 = bs2" but all storage that
+ is part of the bpstat is copied as well. */
+
+bpstat
+bpstat_copy (bs)
+ bpstat bs;
+{
+ bpstat p = NULL;
+ bpstat tmp;
+ bpstat retval = NULL;
+
+ if (bs == NULL)
+ return bs;
+
+ for (; bs != NULL; bs = bs->next)
+ {
+ tmp = (bpstat) xmalloc (sizeof (*tmp));
+ memcpy (tmp, bs, sizeof (*tmp));
+ if (p == NULL)
+ /* This is the first thing in the chain. */
+ retval = tmp;
+ else
+ p->next = tmp;
+ p = tmp;
+ }
+ p->next = NULL;
+ return retval;
+}
+
+/* Find the bpstat associated with this breakpoint */
+
+bpstat
+bpstat_find_breakpoint(bsp, breakpoint)
+ bpstat bsp;
+ struct breakpoint *breakpoint;
+{
+ if (bsp == NULL) return NULL;
+
+ for (;bsp != NULL; bsp = bsp->next) {
+ if (bsp->breakpoint_at == breakpoint) return bsp;
+ }
+ return NULL;
+}
+
+/* Return the breakpoint number of the first breakpoint we are stopped
+ at. *BSP upon return is a bpstat which points to the remaining
+ breakpoints stopped at (but which is not guaranteed to be good for
+ anything but further calls to bpstat_num).
+ Return 0 if passed a bpstat which does not indicate any breakpoints. */
+
+int
+bpstat_num (bsp)
+ bpstat *bsp;
+{
+ struct breakpoint *b;
+
+ if ((*bsp) == NULL)
+ return 0; /* No more breakpoint values */
+ else
+ {
+ b = (*bsp)->breakpoint_at;
+ *bsp = (*bsp)->next;
+ if (b == NULL)
+ return -1; /* breakpoint that's been deleted since */
+ else
+ return b->number; /* We have its number */
+ }
+}
+
+/* Modify BS so that the actions will not be performed. */
+
+void
+bpstat_clear_actions (bs)
+ bpstat bs;
+{
+ for (; bs != NULL; bs = bs->next)
+ {
+ bs->commands = NULL;
+ if (bs->old_val != NULL)
+ {
+ value_free (bs->old_val);
+ bs->old_val = NULL;
+ }
+ }
+}
+
+/* Stub for cleaning up our state if we error-out of a breakpoint command */
+/* ARGSUSED */
+static void
+cleanup_executing_breakpoints (ignore)
+ int ignore;
+{
+ executing_breakpoint_commands = 0;
+}
+
+/* Execute all the commands associated with all the breakpoints at this
+ location. Any of these commands could cause the process to proceed
+ beyond this point, etc. We look out for such changes by checking
+ the global "breakpoint_proceeded" after each command. */
+
+void
+bpstat_do_actions (bsp)
+ bpstat *bsp;
+{
+ bpstat bs;
+ struct cleanup *old_chain;
+
+ executing_breakpoint_commands = 1;
+ old_chain = make_cleanup (cleanup_executing_breakpoints, 0);
+
+top:
+ bs = *bsp;
+
+ breakpoint_proceeded = 0;
+ for (; bs != NULL; bs = bs->next)
+ {
+ while (bs->commands)
+ {
+ char *line = bs->commands->line;
+ bs->commands = bs->commands->next;
+ execute_command (line, 0);
+ /* If the inferior is proceeded by the command, bomb out now.
+ The bpstat chain has been blown away by wait_for_inferior.
+ But since execution has stopped again, there is a new bpstat
+ to look at, so start over. */
+ if (breakpoint_proceeded)
+ goto top;
+ }
+ }
+
+ executing_breakpoint_commands = 0;
+ discard_cleanups (old_chain);
+}
+
+/* This is the normal print_it function for a bpstat. In the future,
+ much of this logic could (should?) be moved to bpstat_stop_status,
+ by having it set different print_it functions. */
+
+static int
+print_it_normal (bs)
+ bpstat bs;
+{
+ /* bs->breakpoint_at can be NULL if it was a momentary breakpoint
+ which has since been deleted. */
+ if (bs->breakpoint_at == NULL
+ || (bs->breakpoint_at->type != bp_breakpoint
+ && bs->breakpoint_at->type != bp_watchpoint))
+ return 0;
+
+ if (bs->breakpoint_at->type == bp_breakpoint)
+ {
+ /* I think the user probably only wants to see one breakpoint
+ number, not all of them. */
+ printf_filtered ("\nBreakpoint %d, ", bs->breakpoint_at->number);
+ return 0;
+ }
+
+ if (bs->old_val != NULL)
+ {
+ printf_filtered ("\nWatchpoint %d, ", bs->breakpoint_at->number);
+ print_expression (bs->breakpoint_at->exp, stdout);
+ printf_filtered ("\nOld value = ");
+ value_print (bs->old_val, stdout, 0, Val_pretty_default);
+ printf_filtered ("\nNew value = ");
+ value_print (bs->breakpoint_at->val, stdout, 0,
+ Val_pretty_default);
+ printf_filtered ("\n");
+ value_free (bs->old_val);
+ bs->old_val = NULL;
+ return 0;
+ }
+ /* We can't deal with it. Maybe another member of the bpstat chain can. */
+ return -1;
+}
+
+/* Print a message indicating what happened. Returns nonzero to
+ say that only the source line should be printed after this (zero
+ return means print the frame as well as the source line). */
+/* Currently we always return zero. */
+int
+bpstat_print (bs)
+ bpstat bs;
+{
+ int val;
+
+ if (bs == NULL)
+ return 0;
+
+ val = (*bs->print_it) (bs);
+ if (val >= 0)
+ return val;
+
+ /* Maybe another breakpoint in the chain caused us to stop.
+ (Currently all watchpoints go on the bpstat whether hit or
+ not. That probably could (should) be changed, provided care is taken
+ with respect to bpstat_explains_signal). */
+ if (bs->next)
+ return bpstat_print (bs->next);
+
+ /* We reached the end of the chain without printing anything. */
+ return 0;
+}
+
+/* Evaluate the expression EXP and return 1 if value is zero.
+ This is used inside a catch_errors to evaluate the breakpoint condition.
+ The argument is a "struct expression *" that has been cast to char * to
+ make it pass through catch_errors. */
+
+static int
+breakpoint_cond_eval (exp)
+ char *exp;
+{
+ return !value_true (evaluate_expression ((struct expression *)exp));
+}
+
+/* Allocate a new bpstat and chain it to the current one. */
+
+static bpstat
+bpstat_alloc (b, cbs)
+ register struct breakpoint *b;
+ bpstat cbs; /* Current "bs" value */
+{
+ bpstat bs;
+
+ bs = (bpstat) xmalloc (sizeof (*bs));
+ cbs->next = bs;
+ bs->breakpoint_at = b;
+ /* If the condition is false, etc., don't do the commands. */
+ bs->commands = NULL;
+ bs->old_val = NULL;
+ bs->print_it = print_it_normal;
+ return bs;
+}
+
+/* Return the frame which we can use to evaluate the expression
+ whose valid block is valid_block, or NULL if not in scope.
+
+ This whole concept is probably not the way to do things (it is incredibly
+ slow being the main reason, not to mention fragile (e.g. the sparc
+ frame pointer being fetched as 0 bug causes it to stop)). Instead,
+ introduce a version of "struct frame" which survives over calls to the
+ inferior, but which is better than FRAME_ADDR in the sense that it lets
+ us evaluate expressions relative to that frame (on some machines, it
+ can just be a FRAME_ADDR). Save one of those instead of (or in addition
+ to) the exp_valid_block, and then use it to evaluate the watchpoint
+ expression, with no need to do all this backtracing every time.
+
+ Or better yet, what if it just copied the struct frame and its next
+ frame? Off the top of my head, I would think that would work
+ because things like (a29k) rsize and msize, or (sparc) bottom just
+ depend on the frame, and aren't going to be different just because
+ the inferior has done something. Trying to recalculate them
+ strikes me as a lot of work, possibly even impossible. Saving the
+ next frame is needed at least on a29k, where get_saved_register
+ uses fi->next->saved_msp. For figuring out whether that frame is
+ still on the stack, I guess this needs to be machine-specific (e.g.
+ a29k) but I think
+
+ read_fp () INNER_THAN watchpoint_frame->frame
+
+ would generally work.
+
+ Of course the scope of the expression could be less than a whole
+ function; perhaps if the innermost frame is the one which the
+ watchpoint is relative to (another machine-specific thing, usually
+
+ FRAMELESS_FUNCTION_INVOCATION (get_current_frame(), fromleaf)
+ read_fp () == wp_frame->frame
+ && !fromleaf
+
+ ), *then* it could do a
+
+ contained_in (get_current_block (), wp->exp_valid_block).
+
+ */
+
+FRAME
+within_scope (valid_block)
+ struct block *valid_block;
+{
+ FRAME fr = get_current_frame ();
+ struct frame_info *fi = get_frame_info (fr);
+ CORE_ADDR func_start;
+
+ /* If caller_pc_valid is true, we are stepping through
+ a function prologue, which is bounded by callee_func_start
+ (inclusive) and callee_prologue_end (exclusive).
+ caller_pc is the pc of the caller.
+
+ Yes, this is hairy. */
+ static int caller_pc_valid = 0;
+ static CORE_ADDR caller_pc;
+ static CORE_ADDR callee_func_start;
+ static CORE_ADDR callee_prologue_end;
+
+ find_pc_partial_function (fi->pc, (PTR)NULL, &func_start, (CORE_ADDR *)NULL);
+ func_start += FUNCTION_START_OFFSET;
+ if (fi->pc == func_start)
+ {
+ /* We just called a function. The only other case I
+ can think of where the pc would equal the pc of the
+ start of a function is a frameless function (i.e.
+ no prologue) where we branch back to the start
+ of the function. In that case, SKIP_PROLOGUE won't
+ find one, and we'll clear caller_pc_valid a few lines
+ down. */
+ caller_pc_valid = 1;
+ caller_pc = SAVED_PC_AFTER_CALL (fr);
+ callee_func_start = func_start;
+ SKIP_PROLOGUE (func_start);
+ callee_prologue_end = func_start;
+ }
+ if (caller_pc_valid)
+ {
+ if (fi->pc < callee_func_start
+ || fi->pc >= callee_prologue_end)
+ caller_pc_valid = 0;
+ }
+
+ if (contained_in (block_for_pc (caller_pc_valid
+ ? caller_pc
+ : fi->pc),
+ valid_block))
+ {
+ return fr;
+ }
+ fr = get_prev_frame (fr);
+
+ /* If any active frame is in the exp_valid_block, then it's
+ OK. Note that this might not be the same invocation of
+ the exp_valid_block that we were watching a little while
+ ago, or the same one as when the watchpoint was set (e.g.
+ we are watching a local variable in a recursive function.
+ When we return from a recursive invocation, then we are
+ suddenly watching a different instance of the variable).
+
+ At least for now I am going to consider this a feature. */
+ for (; fr != NULL; fr = get_prev_frame (fr))
+ {
+ fi = get_frame_info (fr);
+ if (contained_in (block_for_pc (fi->pc),
+ valid_block))
+ {
+ return fr;
+ }
+ }
+ return NULL;
+}
+
+/* Possible return values for watchpoint_check (this can't be an enum
+ because of check_errors). */
+/* The watchpoint has been disabled. */
+#define WP_DISABLED 1
+/* The value has changed. */
+#define WP_VALUE_CHANGED 2
+/* The value has not changed. */
+#define WP_VALUE_NOT_CHANGED 3
+
+/* Check watchpoint condition. */
+static int
+watchpoint_check (p)
+ char *p;
+{
+ bpstat bs = (bpstat) p;
+ FRAME fr;
+
+ int within_current_scope;
+ if (bs->breakpoint_at->exp_valid_block == NULL)
+ within_current_scope = 1;
+ else
+ {
+ fr = within_scope (bs->breakpoint_at->exp_valid_block);
+ within_current_scope = fr != NULL;
+ if (within_current_scope)
+ /* If we end up stopping, the current frame will get selected
+ in normal_stop. So this call to select_frame won't affect
+ the user. */
+ select_frame (fr, -1);
+ }
+
+ if (within_current_scope)
+ {
+ /* We use value_{,free_to_}mark because it could be a
+ *long* time before we return to the command level and
+ call free_all_values. We can't call free_all_values because
+ we might be in the middle of evaluating a function call. */
+
+ value mark = value_mark ();
+ value new_val = evaluate_expression (bs->breakpoint_at->exp);
+ if (!value_equal (bs->breakpoint_at->val, new_val))
+ {
+ release_value (new_val);
+ value_free_to_mark (mark);
+ bs->old_val = bs->breakpoint_at->val;
+ bs->breakpoint_at->val = new_val;
+ /* We will stop here */
+ return WP_VALUE_CHANGED;
+ }
+ else
+ {
+ /* Nothing changed, don't do anything. */
+ value_free_to_mark (mark);
+ /* We won't stop here */
+ return WP_VALUE_NOT_CHANGED;
+ }
+ }
+ else
+ {
+ /* This seems like the only logical thing to do because
+ if we temporarily ignored the watchpoint, then when
+ we reenter the block in which it is valid it contains
+ garbage (in the case of a function, it may have two
+ garbage values, one before and one after the prologue).
+ So we can't even detect the first assignment to it and
+ watch after that (since the garbage may or may not equal
+ the first value assigned). */
+ bs->breakpoint_at->enable = disabled;
+ printf_filtered ("\
+Watchpoint %d disabled because the program has left the block in\n\
+which its expression is valid.\n", bs->breakpoint_at->number);
+ return WP_DISABLED;
+ }
+}
+
+/* This is used when everything which needs to be printed has
+ already been printed. But we still want to print the frame. */
+static int
+print_it_done (bs)
+ bpstat bs;
+{
+ return 0;
+}
+
+/* This is used when nothing should be printed for this bpstat entry. */
+
+static int
+print_it_noop (bs)
+ bpstat bs;
+{
+ return -1;
+}
+
+/* Get a bpstat associated with having just stopped at address *PC
+ and frame address FRAME_ADDRESS. Update *PC to point at the
+ breakpoint (if we hit a breakpoint). NOT_A_BREAKPOINT is nonzero
+ if this is known to not be a real breakpoint (it could still be a
+ watchpoint, though). */
+
+/* Determine whether we stopped at a breakpoint, etc, or whether we
+ don't understand this stop. Result is a chain of bpstat's such that:
+
+ if we don't understand the stop, the result is a null pointer.
+
+ if we understand why we stopped, the result is not null.
+
+ Each element of the chain refers to a particular breakpoint or
+ watchpoint at which we have stopped. (We may have stopped for
+ several reasons concurrently.)
+
+ Each element of the chain has valid next, breakpoint_at,
+ commands, FIXME??? fields.
+
+ */
+
+bpstat
+bpstat_stop_status (pc, frame_address, not_a_breakpoint)
+ CORE_ADDR *pc;
+ FRAME_ADDR frame_address;
+ int not_a_breakpoint;
+{
+ register struct breakpoint *b;
+ CORE_ADDR bp_addr;
+#if DECR_PC_AFTER_BREAK != 0 || defined (SHIFT_INST_REGS)
+ /* True if we've hit a breakpoint (as opposed to a watchpoint). */
+ int real_breakpoint = 0;
+#endif
+ /* Root of the chain of bpstat's */
+ struct bpstat root_bs[1];
+ /* Pointer to the last thing in the chain currently. */
+ bpstat bs = root_bs;
+
+ /* Get the address where the breakpoint would have been. */
+ bp_addr = *pc - DECR_PC_AFTER_BREAK;
+
+ ALL_BREAKPOINTS (b)
+ {
+ if (b->enable == disabled)
+ continue;
+
+ if (b->type != bp_watchpoint && b->address != bp_addr)
+ continue;
+
+ if (b->type != bp_watchpoint && not_a_breakpoint)
+ continue;
+
+ /* Come here if it's a watchpoint, or if the break address matches */
+
+ bs = bpstat_alloc (b, bs); /* Alloc a bpstat to explain stop */
+
+ bs->stop = 1;
+ bs->print = 1;
+
+ if (b->type == bp_watchpoint)
+ {
+ static char message1[] =
+ "Error evaluating expression for watchpoint %d\n";
+ char message[sizeof (message1) + 30 /* slop */];
+ sprintf (message, message1, b->number);
+ switch (catch_errors (watchpoint_check, (char *) bs, message,
+ RETURN_MASK_ALL))
+ {
+ case WP_DISABLED:
+ /* We've already printed what needs to be printed. */
+ bs->print_it = print_it_done;
+ /* Stop. */
+ break;
+ case WP_VALUE_CHANGED:
+ /* Stop. */
+ break;
+ case WP_VALUE_NOT_CHANGED:
+ /* Don't stop. */
+ bs->print_it = print_it_noop;
+ bs->stop = 0;
+ continue;
+ default:
+ /* Can't happen. */
+ /* FALLTHROUGH */
+ case 0:
+ /* Error from catch_errors. */
+ b->enable = disabled;
+ printf_filtered ("Watchpoint %d disabled.\n", b->number);
+ /* We've already printed what needs to be printed. */
+ bs->print_it = print_it_done;
+ /* Stop. */
+ break;
+ }
+ }
+#if DECR_PC_AFTER_BREAK != 0 || defined (SHIFT_INST_REGS)
+ else
+ real_breakpoint = 1;
+#endif
+
+ if (b->frame && b->frame != frame_address)
+ bs->stop = 0;
+ else
+ {
+ int value_is_zero = 0;
+
+ if (b->cond)
+ {
+ /* Need to select the frame, with all that implies
+ so that the conditions will have the right context. */
+ select_frame (get_current_frame (), 0);
+ value_is_zero
+ = catch_errors (breakpoint_cond_eval, (char *)(b->cond),
+ "Error in testing breakpoint condition:\n",
+ RETURN_MASK_ALL);
+ /* FIXME-someday, should give breakpoint # */
+ free_all_values ();
+ }
+ if (b->cond && value_is_zero)
+ {
+ bs->stop = 0;
+ }
+ else if (b->ignore_count > 0)
+ {
+ b->ignore_count--;
+ bs->stop = 0;
+ }
+ else
+ {
+ /* We will stop here */
+ if (b->disposition == disable)
+ b->enable = disabled;
+ bs->commands = b->commands;
+ if (b->silent)
+ bs->print = 0;
+ if (bs->commands && STREQ ("silent", bs->commands->line))
+ {
+ bs->commands = bs->commands->next;
+ bs->print = 0;
+ }
+ }
+ }
+ /* Print nothing for this entry if we dont stop or if we dont print. */
+ if (bs->stop == 0 || bs->print == 0)
+ bs->print_it = print_it_noop;
+ }
+
+ bs->next = NULL; /* Terminate the chain */
+ bs = root_bs->next; /* Re-grab the head of the chain */
+#if DECR_PC_AFTER_BREAK != 0 || defined (SHIFT_INST_REGS)
+ if (bs)
+ {
+ if (real_breakpoint)
+ {
+ *pc = bp_addr;
+#if defined (SHIFT_INST_REGS)
+ SHIFT_INST_REGS();
+#else /* No SHIFT_INST_REGS. */
+ write_pc (bp_addr);
+#endif /* No SHIFT_INST_REGS. */
+ }
+ }
+#endif /* DECR_PC_AFTER_BREAK != 0. */
+ return bs;
+}
+
+/* Tell what to do about this bpstat. */
+struct bpstat_what
+bpstat_what (bs)
+ bpstat bs;
+{
+ /* Classify each bpstat as one of the following. */
+ enum class {
+ /* This bpstat element has no effect on the main_action. */
+ no_effect = 0,
+
+ /* There was a watchpoint, stop but don't print. */
+ wp_silent,
+
+ /* There was a watchpoint, stop and print. */
+ wp_noisy,
+
+ /* There was a breakpoint but we're not stopping. */
+ bp_nostop,
+
+ /* There was a breakpoint, stop but don't print. */
+ bp_silent,
+
+ /* There was a breakpoint, stop and print. */
+ bp_noisy,
+
+ /* We hit the longjmp breakpoint. */
+ long_jump,
+
+ /* We hit the longjmp_resume breakpoint. */
+ long_resume,
+
+ /* This is just used to count how many enums there are. */
+ class_last
+ };
+
+ /* Here is the table which drives this routine. So that we can
+ format it pretty, we define some abbreviations for the
+ enum bpstat_what codes. */
+#define keep_c BPSTAT_WHAT_KEEP_CHECKING
+#define stop_s BPSTAT_WHAT_STOP_SILENT
+#define stop_n BPSTAT_WHAT_STOP_NOISY
+#define single BPSTAT_WHAT_SINGLE
+#define setlr BPSTAT_WHAT_SET_LONGJMP_RESUME
+#define clrlr BPSTAT_WHAT_CLEAR_LONGJMP_RESUME
+#define clrlrs BPSTAT_WHAT_CLEAR_LONGJMP_RESUME_SINGLE
+/* "Can't happen." Might want to print an error message.
+ abort() is not out of the question, but chances are GDB is just
+ a bit confused, not unusable. */
+#define err BPSTAT_WHAT_STOP_NOISY
+
+ /* Given an old action and a class, come up with a new action. */
+ /* One interesting property of this table is that wp_silent is the same
+ as bp_silent and wp_noisy is the same as bp_noisy. That is because
+ after stopping, the check for whether to step over a breakpoint
+ (BPSTAT_WHAT_SINGLE type stuff) is handled in proceed() without
+ reference to how we stopped. We retain separate wp_silent and bp_silent
+ codes in case we want to change that someday. */
+ static const enum bpstat_what_main_action
+ table[(int)class_last][(int)BPSTAT_WHAT_LAST] =
+ {
+ /* old action */
+ /* keep_c stop_s stop_n single setlr clrlr clrlrs */
+
+/*no_effect*/ {keep_c, stop_s, stop_n, single, setlr , clrlr , clrlrs},
+/*wp_silent*/ {stop_s, stop_s, stop_n, stop_s, stop_s, stop_s, stop_s},
+/*wp_noisy*/ {stop_n, stop_n, stop_n, stop_n, stop_n, stop_n, stop_n},
+/*bp_nostop*/ {single, stop_s, stop_n, single, setlr , clrlrs, clrlrs},
+/*bp_silent*/ {stop_s, stop_s, stop_n, stop_s, stop_s, stop_s, stop_s},
+/*bp_noisy*/ {stop_n, stop_n, stop_n, stop_n, stop_n, stop_n, stop_n},
+/*long_jump*/ {setlr , stop_s, stop_n, setlr , err , err , err },
+/*long_resume*/ {clrlr , stop_s, stop_n, clrlrs, err , err , err }
+ };
+#undef keep_c
+#undef stop_s
+#undef stop_n
+#undef single
+#undef setlr
+#undef clrlr
+#undef clrlrs
+#undef err
+ enum bpstat_what_main_action current_action = BPSTAT_WHAT_KEEP_CHECKING;
+ struct bpstat_what retval;
+
+ retval.call_dummy = 0;
+ retval.step_resume = 0;
+ for (; bs != NULL; bs = bs->next)
+ {
+ enum class bs_class = no_effect;
+ if (bs->breakpoint_at == NULL)
+ /* I suspect this can happen if it was a momentary breakpoint
+ which has since been deleted. */
+ continue;
+ switch (bs->breakpoint_at->type)
+ {
+ case bp_breakpoint:
+ case bp_until:
+ case bp_finish:
+ if (bs->stop)
+ {
+ if (bs->print)
+ bs_class = bp_noisy;
+ else
+ bs_class = bp_silent;
+ }
+ else
+ bs_class = bp_nostop;
+ break;
+ case bp_watchpoint:
+ if (bs->stop)
+ {
+ if (bs->print)
+ bs_class = wp_noisy;
+ else
+ bs_class = wp_silent;
+ }
+ else
+ /* There was a watchpoint, but we're not stopping. This requires
+ no further action. */
+ bs_class = no_effect;
+ break;
+ case bp_longjmp:
+ bs_class = long_jump;
+ break;
+ case bp_longjmp_resume:
+ bs_class = long_resume;
+ break;
+ case bp_step_resume:
+#if 0
+ /* Need to temporarily disable this until we can fix the bug
+ with nexting over a breakpoint with ->stop clear causing
+ an infinite loop. For now, treat the breakpoint as having
+ been hit even if the frame is wrong. */
+ if (bs->stop)
+ {
+#endif
+ retval.step_resume = 1;
+ /* We don't handle this via the main_action. */
+ bs_class = no_effect;
+#if 0
+ }
+ else
+ /* It is for the wrong frame. */
+ bs_class = bp_nostop;
+#endif
+ break;
+ case bp_call_dummy:
+ /* Make sure the action is stop (silent or noisy), so infrun.c
+ pops the dummy frame. */
+ bs_class = bp_silent;
+ retval.call_dummy = 1;
+ break;
+ }
+ current_action = table[(int)bs_class][(int)current_action];
+ }
+ retval.main_action = current_action;
+ return retval;
+}
+
+/* Nonzero if we should step constantly (e.g. watchpoints on machines
+ without hardware support). This isn't related to a specific bpstat,
+ just to things like whether watchpoints are set. */
+
+int
+bpstat_should_step ()
+{
+ struct breakpoint *b;
+ ALL_BREAKPOINTS (b)
+ if (b->enable == enabled && b->type == bp_watchpoint)
+ return 1;
+ return 0;
+}
+
+/* Print information on breakpoint number BNUM, or -1 if all.
+ If WATCHPOINTS is zero, process only breakpoints; if WATCHPOINTS
+ is nonzero, process only watchpoints. */
+
+static void
+breakpoint_1 (bnum, allflag)
+ int bnum;
+ int allflag;
+{
+ register struct breakpoint *b;
+ register struct command_line *l;
+ register struct symbol *sym;
+ CORE_ADDR last_addr = (CORE_ADDR)-1;
+ int found_a_breakpoint = 0;
+ static char *bptypes[] = {"breakpoint", "until", "finish", "watchpoint",
+ "longjmp", "longjmp resume", "step resume",
+ "call dummy" };
+ static char *bpdisps[] = {"del", "dis", "keep"};
+ static char bpenables[] = "ny";
+ char wrap_indent[80];
+
+ ALL_BREAKPOINTS (b)
+ if (bnum == -1
+ || bnum == b->number)
+ {
+/* We only print out user settable breakpoints unless the allflag is set. */
+ if (!allflag
+ && b->type != bp_breakpoint
+ && b->type != bp_watchpoint)
+ continue;
+
+ if (!found_a_breakpoint++)
+ printf_filtered ("Num Type Disp Enb %sWhat\n",
+ addressprint ? "Address " : "");
+
+ printf_filtered ("%-3d %-14s %-4s %-3c ",
+ b->number,
+ bptypes[(int)b->type],
+ bpdisps[(int)b->disposition],
+ bpenables[(int)b->enable]);
+ strcpy (wrap_indent, " ");
+ if (addressprint)
+ strcat (wrap_indent, " ");
+ switch (b->type)
+ {
+ case bp_watchpoint:
+ print_expression (b->exp, stdout);
+ break;
+
+ case bp_breakpoint:
+ case bp_until:
+ case bp_finish:
+ case bp_longjmp:
+ case bp_longjmp_resume:
+ case bp_step_resume:
+ case bp_call_dummy:
+ if (addressprint)
+ printf_filtered ("%s ", local_hex_string_custom ((unsigned long) b->address, "08l"));
+
+ last_addr = b->address;
+ if (b->source_file)
+ {
+ sym = find_pc_function (b->address);
+ if (sym)
+ {
+ fputs_filtered ("in ", stdout);
+ fputs_filtered (SYMBOL_SOURCE_NAME (sym), stdout);
+ wrap_here (wrap_indent);
+ fputs_filtered (" at ", stdout);
+ }
+ fputs_filtered (b->source_file, stdout);
+ printf_filtered (":%d", b->line_number);
+ }
+ else
+ print_address_symbolic (b->address, stdout, demangle, " ");
+ break;
+ }
+
+ printf_filtered ("\n");
+
+ if (b->frame)
+ printf_filtered ("\tstop only in stack frame at %s\n",
+ local_hex_string((unsigned long) b->frame));
+ if (b->cond)
+ {
+ printf_filtered ("\tstop only if ");
+ print_expression (b->cond, stdout);
+ printf_filtered ("\n");
+ }
+ if (b->ignore_count)
+ printf_filtered ("\tignore next %d hits\n", b->ignore_count);
+ if ((l = b->commands))
+ while (l)
+ {
+ fputs_filtered ("\t", stdout);
+ fputs_filtered (l->line, stdout);
+ fputs_filtered ("\n", stdout);
+ l = l->next;
+ }
+ }
+
+ if (!found_a_breakpoint)
+ {
+ if (bnum == -1)
+ printf_filtered ("No breakpoints or watchpoints.\n");
+ else
+ printf_filtered ("No breakpoint or watchpoint number %d.\n", bnum);
+ }
+ else
+ /* Compare against (CORE_ADDR)-1 in case some compiler decides
+ that a comparison of an unsigned with -1 is always false. */
+ if (last_addr != (CORE_ADDR)-1)
+ set_next_address (last_addr);
+}
+
+/* ARGSUSED */
+static void
+breakpoints_info (bnum_exp, from_tty)
+ char *bnum_exp;
+ int from_tty;
+{
+ int bnum = -1;
+
+ if (bnum_exp)
+ bnum = parse_and_eval_address (bnum_exp);
+
+ breakpoint_1 (bnum, 0);
+}
+
+#if MAINTENANCE_CMDS
+
+/* ARGSUSED */
+static void
+maintenance_info_breakpoints (bnum_exp, from_tty)
+ char *bnum_exp;
+ int from_tty;
+{
+ int bnum = -1;
+
+ if (bnum_exp)
+ bnum = parse_and_eval_address (bnum_exp);
+
+ breakpoint_1 (bnum, 1);
+}
+
+#endif
+
+/* Print a message describing any breakpoints set at PC. */
+
+static void
+describe_other_breakpoints (pc)
+ register CORE_ADDR pc;
+{
+ register int others = 0;
+ register struct breakpoint *b;
+
+ ALL_BREAKPOINTS (b)
+ if (b->address == pc)
+ others++;
+ if (others > 0)
+ {
+ printf ("Note: breakpoint%s ", (others > 1) ? "s" : "");
+ ALL_BREAKPOINTS (b)
+ if (b->address == pc)
+ {
+ others--;
+ printf ("%d%s%s ",
+ b->number,
+ (b->enable == disabled) ? " (disabled)" : "",
+ (others > 1) ? "," : ((others == 1) ? " and" : ""));
+ }
+ printf ("also set at pc %s.\n", local_hex_string((unsigned long) pc));
+ }
+}
+
+/* Set the default place to put a breakpoint
+ for the `break' command with no arguments. */
+
+void
+set_default_breakpoint (valid, addr, symtab, line)
+ int valid;
+ CORE_ADDR addr;
+ struct symtab *symtab;
+ int line;
+{
+ default_breakpoint_valid = valid;
+ default_breakpoint_address = addr;
+ default_breakpoint_symtab = symtab;
+ default_breakpoint_line = line;
+}
+
+/* Rescan breakpoints at address ADDRESS,
+ marking the first one as "first" and any others as "duplicates".
+ This is so that the bpt instruction is only inserted once. */
+
+static void
+check_duplicates (address)
+ CORE_ADDR address;
+{
+ register struct breakpoint *b;
+ register int count = 0;
+
+ if (address == 0) /* Watchpoints are uninteresting */
+ return;
+
+ ALL_BREAKPOINTS (b)
+ if (b->enable != disabled && b->address == address)
+ {
+ count++;
+ b->duplicate = count > 1;
+ }
+}
+
+/* Low level routine to set a breakpoint.
+ Takes as args the three things that every breakpoint must have.
+ Returns the breakpoint object so caller can set other things.
+ Does not set the breakpoint number!
+ Does not print anything.
+
+ ==> This routine should not be called if there is a chance of later
+ error(); otherwise it leaves a bogus breakpoint on the chain. Validate
+ your arguments BEFORE calling this routine! */
+
+static struct breakpoint *
+set_raw_breakpoint (sal)
+ struct symtab_and_line sal;
+{
+ register struct breakpoint *b, *b1;
+
+ b = (struct breakpoint *) xmalloc (sizeof (struct breakpoint));
+ memset (b, 0, sizeof (*b));
+ b->address = sal.pc;
+ if (sal.symtab == NULL)
+ b->source_file = NULL;
+ else
+ b->source_file = savestring (sal.symtab->filename,
+ strlen (sal.symtab->filename));
+ b->thread = -1;
+ b->line_number = sal.line;
+ b->enable = enabled;
+ b->next = 0;
+ b->silent = 0;
+ b->ignore_count = 0;
+ b->commands = NULL;
+ b->frame = 0;
+
+ /* Add this breakpoint to the end of the chain
+ so that a list of breakpoints will come out in order
+ of increasing numbers. */
+
+ b1 = breakpoint_chain;
+ if (b1 == 0)
+ breakpoint_chain = b;
+ else
+ {
+ while (b1->next)
+ b1 = b1->next;
+ b1->next = b;
+ }
+
+ check_duplicates (sal.pc);
+
+ return b;
+}
+
+static void
+create_longjmp_breakpoint(func_name)
+ char *func_name;
+{
+ struct symtab_and_line sal;
+ struct breakpoint *b;
+ static int internal_breakpoint_number = -1;
+
+ if (func_name != NULL)
+ {
+ struct minimal_symbol *m;
+
+ m = lookup_minimal_symbol(func_name, (struct objfile *)NULL);
+ if (m)
+ sal.pc = SYMBOL_VALUE_ADDRESS (m);
+ else
+ return;
+ }
+ else
+ sal.pc = 0;
+
+ sal.symtab = NULL;
+ sal.line = 0;
+
+ b = set_raw_breakpoint(sal);
+ if (!b) return;
+
+ b->type = func_name != NULL ? bp_longjmp : bp_longjmp_resume;
+ b->disposition = donttouch;
+ b->enable = disabled;
+ b->silent = 1;
+ if (func_name)
+ b->addr_string = strsave(func_name);
+ b->number = internal_breakpoint_number--;
+}
+
+/* Call this routine when stepping and nexting to enable a breakpoint if we do
+ a longjmp(). When we hit that breakpoint, call
+ set_longjmp_resume_breakpoint() to figure out where we are going. */
+
+void
+enable_longjmp_breakpoint()
+{
+ register struct breakpoint *b;
+
+ ALL_BREAKPOINTS (b)
+ if (b->type == bp_longjmp)
+ {
+ b->enable = enabled;
+ check_duplicates (b->address);
+ }
+}
+
+void
+disable_longjmp_breakpoint()
+{
+ register struct breakpoint *b;
+
+ ALL_BREAKPOINTS (b)
+ if ( b->type == bp_longjmp
+ || b->type == bp_longjmp_resume)
+ {
+ b->enable = disabled;
+ check_duplicates (b->address);
+ }
+}
+
+/* Call this after hitting the longjmp() breakpoint. Use this to set a new
+ breakpoint at the target of the jmp_buf.
+
+ FIXME - This ought to be done by setting a temporary breakpoint that gets
+ deleted automatically...
+*/
+
+void
+set_longjmp_resume_breakpoint(pc, frame)
+ CORE_ADDR pc;
+ FRAME frame;
+{
+ register struct breakpoint *b;
+
+ ALL_BREAKPOINTS (b)
+ if (b->type == bp_longjmp_resume)
+ {
+ b->address = pc;
+ b->enable = enabled;
+ if (frame != NULL)
+ b->frame = FRAME_FP(frame);
+ else
+ b->frame = 0;
+ check_duplicates (b->address);
+ return;
+ }
+}
+
+/* Set a breakpoint that will evaporate an end of command
+ at address specified by SAL.
+ Restrict it to frame FRAME if FRAME is nonzero. */
+
+struct breakpoint *
+set_momentary_breakpoint (sal, frame, type)
+ struct symtab_and_line sal;
+ FRAME frame;
+ enum bptype type;
+{
+ register struct breakpoint *b;
+ b = set_raw_breakpoint (sal);
+ b->type = type;
+ b->enable = enabled;
+ b->disposition = donttouch;
+ b->frame = (frame ? FRAME_FP (frame) : 0);
+ return b;
+}
+
+#if 0
+void
+clear_momentary_breakpoints ()
+{
+ register struct breakpoint *b;
+ ALL_BREAKPOINTS (b)
+ if (b->disposition == delete)
+ {
+ delete_breakpoint (b);
+ break;
+ }
+}
+#endif
+
+/* Tell the user we have just set a breakpoint B. */
+static void
+mention (b)
+ struct breakpoint *b;
+{
+ switch (b->type)
+ {
+ case bp_watchpoint:
+ printf_filtered ("Watchpoint %d: ", b->number);
+ print_expression (b->exp, stdout);
+ break;
+ case bp_breakpoint:
+ printf_filtered ("Breakpoint %d at %s", b->number,
+ local_hex_string((unsigned long) b->address));
+ if (b->source_file)
+ printf_filtered (": file %s, line %d.",
+ b->source_file, b->line_number);
+ break;
+ case bp_until:
+ case bp_finish:
+ case bp_longjmp:
+ case bp_longjmp_resume:
+ case bp_step_resume:
+ break;
+ }
+ printf_filtered ("\n");
+}
+
+#if 0
+/* Nobody calls this currently. */
+/* Set a breakpoint from a symtab and line.
+ If TEMPFLAG is nonzero, it is a temporary breakpoint.
+ ADDR_STRING is a malloc'd string holding the name of where we are
+ setting the breakpoint. This is used later to re-set it after the
+ program is relinked and symbols are reloaded.
+ Print the same confirmation messages that the breakpoint command prints. */
+
+void
+set_breakpoint (s, line, tempflag, addr_string)
+ struct symtab *s;
+ int line;
+ int tempflag;
+ char *addr_string;
+{
+ register struct breakpoint *b;
+ struct symtab_and_line sal;
+
+ sal.symtab = s;
+ sal.line = line;
+ sal.pc = 0;
+ resolve_sal_pc (&sal); /* Might error out */
+ describe_other_breakpoints (sal.pc);
+
+ b = set_raw_breakpoint (sal);
+ set_breakpoint_count (breakpoint_count + 1);
+ b->number = breakpoint_count;
+ b->type = bp_breakpoint;
+ b->cond = 0;
+ b->addr_string = addr_string;
+ b->enable = enabled;
+ b->disposition = tempflag ? delete : donttouch;
+
+ mention (b);
+}
+#endif /* 0 */
+
+/* Set a breakpoint according to ARG (function, linenum or *address)
+ and make it temporary if TEMPFLAG is nonzero. */
+
+static void
+break_command_1 (arg, tempflag, from_tty)
+ char *arg;
+ int tempflag, from_tty;
+{
+ struct symtabs_and_lines sals;
+ struct symtab_and_line sal;
+ register struct expression *cond = 0;
+ register struct breakpoint *b;
+
+ /* Pointers in arg to the start, and one past the end, of the condition. */
+ char *cond_start = NULL;
+ char *cond_end = NULL;
+ /* Pointers in arg to the start, and one past the end,
+ of the address part. */
+ char *addr_start = NULL;
+ char *addr_end = NULL;
+ struct cleanup *old_chain;
+ struct cleanup *canonical_strings_chain = NULL;
+ char **canonical = (char **)NULL;
+ int i;
+ int thread;
+
+ sals.sals = NULL;
+ sals.nelts = 0;
+
+ sal.line = sal.pc = sal.end = 0;
+ sal.symtab = 0;
+
+ /* If no arg given, or if first arg is 'if ', use the default breakpoint. */
+
+ if (!arg || (arg[0] == 'i' && arg[1] == 'f'
+ && (arg[2] == ' ' || arg[2] == '\t')))
+ {
+ if (default_breakpoint_valid)
+ {
+ sals.sals = (struct symtab_and_line *)
+ xmalloc (sizeof (struct symtab_and_line));
+ sal.pc = default_breakpoint_address;
+ sal.line = default_breakpoint_line;
+ sal.symtab = default_breakpoint_symtab;
+ sals.sals[0] = sal;
+ sals.nelts = 1;
+ }
+ else
+ error ("No default breakpoint address now.");
+ }
+ else
+ {
+ addr_start = arg;
+
+ /* Force almost all breakpoints to be in terms of the
+ current_source_symtab (which is decode_line_1's default). This
+ should produce the results we want almost all of the time while
+ leaving default_breakpoint_* alone. */
+ if (default_breakpoint_valid
+ && (!current_source_symtab
+ || (arg && (*arg == '+' || *arg == '-'))))
+ sals = decode_line_1 (&arg, 1, default_breakpoint_symtab,
+ default_breakpoint_line, &canonical);
+ else
+ sals = decode_line_1 (&arg, 1, (struct symtab *)NULL, 0, &canonical);
+
+ addr_end = arg;
+ }
+
+ if (! sals.nelts)
+ return;
+
+ /* Make sure that all storage allocated in decode_line_1 gets freed in case
+ the following `for' loop errors out. */
+ old_chain = make_cleanup (free, sals.sals);
+ if (canonical != (char **)NULL)
+ {
+ make_cleanup (free, canonical);
+ canonical_strings_chain = make_cleanup (null_cleanup, 0);
+ for (i = 0; i < sals.nelts; i++)
+ {
+ if (canonical[i] != NULL)
+ make_cleanup (free, canonical[i]);
+ }
+ }
+
+ thread = -1; /* No specific thread yet */
+
+ /* Resolve all line numbers to PC's, and verify that conditions
+ can be parsed, before setting any breakpoints. */
+ for (i = 0; i < sals.nelts; i++)
+ {
+ char *tok, *end_tok;
+ int toklen;
+
+ resolve_sal_pc (&sals.sals[i]);
+
+ tok = arg;
+
+ while (tok && *tok)
+ {
+ while (*tok == ' ' || *tok == '\t')
+ tok++;
+
+ end_tok = tok;
+
+ while (*end_tok != ' ' && *end_tok != '\t' && *end_tok != '\000')
+ end_tok++;
+
+ toklen = end_tok - tok;
+
+ if (toklen >= 1 && strncmp (tok, "if", toklen) == 0)
+ {
+ tok = cond_start = end_tok + 1;
+ cond = parse_exp_1 (&tok, block_for_pc (sals.sals[i].pc), 0);
+ cond_end = tok;
+ }
+ else if (toklen >= 1 && strncmp (tok, "thread", toklen) == 0)
+ {
+ char *tmptok;
+
+ tok = end_tok + 1;
+ tmptok = tok;
+ thread = strtol (tok, &tok, 0);
+ if (tok == tmptok)
+ error ("Junk after thread keyword.");
+ if (!valid_thread_id (thread))
+ error ("Unknown thread %d\n", thread);
+ }
+ else
+ error ("Junk at end of arguments.");
+ }
+ }
+
+ /* Remove the canonical strings from the cleanup, they are needed below. */
+ if (canonical != (char **)NULL)
+ discard_cleanups (canonical_strings_chain);
+
+ /* Now set all the breakpoints. */
+ for (i = 0; i < sals.nelts; i++)
+ {
+ sal = sals.sals[i];
+
+ if (from_tty)
+ describe_other_breakpoints (sal.pc);
+
+ b = set_raw_breakpoint (sal);
+ set_breakpoint_count (breakpoint_count + 1);
+ b->number = breakpoint_count;
+ b->type = bp_breakpoint;
+ b->cond = cond;
+ b->thread = thread;
+
+ /* If a canonical line spec is needed use that instead of the
+ command string. */
+ if (canonical != (char **)NULL && canonical[i] != NULL)
+ b->addr_string = canonical[i];
+ else if (addr_start)
+ b->addr_string = savestring (addr_start, addr_end - addr_start);
+ if (cond_start)
+ b->cond_string = savestring (cond_start, cond_end - cond_start);
+
+ b->enable = enabled;
+ b->disposition = tempflag ? delete : donttouch;
+
+ mention (b);
+ }
+
+ if (sals.nelts > 1)
+ {
+ printf ("Multiple breakpoints were set.\n");
+ printf ("Use the \"delete\" command to delete unwanted breakpoints.\n");
+ }
+ do_cleanups (old_chain);
+}
+
+/* Helper function for break_command_1 and disassemble_command. */
+
+void
+resolve_sal_pc (sal)
+ struct symtab_and_line *sal;
+{
+ CORE_ADDR pc;
+
+ if (sal->pc == 0 && sal->symtab != 0)
+ {
+ pc = find_line_pc (sal->symtab, sal->line);
+ if (pc == 0)
+ error ("No line %d in file \"%s\".",
+ sal->line, sal->symtab->filename);
+ sal->pc = pc;
+ }
+}
+
+void
+break_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ break_command_1 (arg, 0, from_tty);
+}
+
+static void
+tbreak_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ break_command_1 (arg, 1, from_tty);
+}
+
+/* ARGSUSED */
+static void
+watch_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ struct breakpoint *b;
+ struct symtab_and_line sal;
+ struct expression *exp;
+ struct block *exp_valid_block;
+ struct value *val;
+
+ sal.pc = 0;
+ sal.symtab = NULL;
+ sal.line = 0;
+
+ /* Parse arguments. */
+ innermost_block = NULL;
+ exp = parse_expression (arg);
+ exp_valid_block = innermost_block;
+ val = evaluate_expression (exp);
+ release_value (val);
+ if (VALUE_LAZY (val))
+ value_fetch_lazy (val);
+
+ /* Now set up the breakpoint. */
+ b = set_raw_breakpoint (sal);
+ set_breakpoint_count (breakpoint_count + 1);
+ b->number = breakpoint_count;
+ b->type = bp_watchpoint;
+ b->disposition = donttouch;
+ b->exp = exp;
+ b->exp_valid_block = exp_valid_block;
+ b->val = val;
+ b->cond = 0;
+ b->cond_string = NULL;
+ b->exp_string = savestring (arg, strlen (arg));
+ mention (b);
+}
+
+/*
+ * Helper routine for the until_command routine in infcmd.c. Here
+ * because it uses the mechanisms of breakpoints.
+ */
+/* ARGSUSED */
+void
+until_break_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ struct symtabs_and_lines sals;
+ struct symtab_and_line sal;
+ FRAME prev_frame = get_prev_frame (selected_frame);
+ struct breakpoint *breakpoint;
+ struct cleanup *old_chain;
+
+ clear_proceed_status ();
+
+ /* Set a breakpoint where the user wants it and at return from
+ this function */
+
+ if (default_breakpoint_valid)
+ sals = decode_line_1 (&arg, 1, default_breakpoint_symtab,
+ default_breakpoint_line, (char ***)NULL);
+ else
+ sals = decode_line_1 (&arg, 1, (struct symtab *)NULL, 0, (char ***)NULL);
+
+ if (sals.nelts != 1)
+ error ("Couldn't get information on specified line.");
+
+ sal = sals.sals[0];
+ free ((PTR)sals.sals); /* malloc'd, so freed */
+
+ if (*arg)
+ error ("Junk at end of arguments.");
+
+ resolve_sal_pc (&sal);
+
+ breakpoint = set_momentary_breakpoint (sal, selected_frame, bp_until);
+
+ old_chain = make_cleanup(delete_breakpoint, breakpoint);
+
+ /* Keep within the current frame */
+
+ if (prev_frame)
+ {
+ struct frame_info *fi;
+
+ fi = get_frame_info (prev_frame);
+ sal = find_pc_line (fi->pc, 0);
+ sal.pc = fi->pc;
+ breakpoint = set_momentary_breakpoint (sal, prev_frame, bp_until);
+ make_cleanup(delete_breakpoint, breakpoint);
+ }
+
+ proceed (-1, -1, 0);
+ do_cleanups(old_chain);
+}
+
+#if 0
+/* These aren't used; I don't konw what they were for. */
+/* Set a breakpoint at the catch clause for NAME. */
+static int
+catch_breakpoint (name)
+ char *name;
+{
+}
+
+static int
+disable_catch_breakpoint ()
+{
+}
+
+static int
+delete_catch_breakpoint ()
+{
+}
+
+static int
+enable_catch_breakpoint ()
+{
+}
+#endif /* 0 */
+
+struct sal_chain
+{
+ struct sal_chain *next;
+ struct symtab_and_line sal;
+};
+
+#if 0
+/* This isn't used; I don't know what it was for. */
+/* For each catch clause identified in ARGS, run FUNCTION
+ with that clause as an argument. */
+static struct symtabs_and_lines
+map_catch_names (args, function)
+ char *args;
+ int (*function)();
+{
+ register char *p = args;
+ register char *p1;
+ struct symtabs_and_lines sals;
+#if 0
+ struct sal_chain *sal_chain = 0;
+#endif
+
+ if (p == 0)
+ error_no_arg ("one or more catch names");
+
+ sals.nelts = 0;
+ sals.sals = NULL;
+
+ while (*p)
+ {
+ p1 = p;
+ /* Don't swallow conditional part. */
+ if (p1[0] == 'i' && p1[1] == 'f'
+ && (p1[2] == ' ' || p1[2] == '\t'))
+ break;
+
+ if (isalpha (*p1))
+ {
+ p1++;
+ while (isalnum (*p1) || *p1 == '_' || *p1 == '$')
+ p1++;
+ }
+
+ if (*p1 && *p1 != ' ' && *p1 != '\t')
+ error ("Arguments must be catch names.");
+
+ *p1 = 0;
+#if 0
+ if (function (p))
+ {
+ struct sal_chain *next
+ = (struct sal_chain *)alloca (sizeof (struct sal_chain));
+ next->next = sal_chain;
+ next->sal = get_catch_sal (p);
+ sal_chain = next;
+ goto win;
+ }
+#endif
+ printf ("No catch clause for exception %s.\n", p);
+#if 0
+ win:
+#endif
+ p = p1;
+ while (*p == ' ' || *p == '\t') p++;
+ }
+}
+#endif /* 0 */
+
+/* This shares a lot of code with `print_frame_label_vars' from stack.c. */
+
+static struct symtabs_and_lines
+get_catch_sals (this_level_only)
+ int this_level_only;
+{
+ register struct blockvector *bl;
+ register struct block *block;
+ int index, have_default = 0;
+ struct frame_info *fi;
+ CORE_ADDR pc;
+ struct symtabs_and_lines sals;
+ struct sal_chain *sal_chain = 0;
+ char *blocks_searched;
+
+ /* Not sure whether an error message is always the correct response,
+ but it's better than a core dump. */
+ if (selected_frame == NULL)
+ error ("No selected frame.");
+ block = get_frame_block (selected_frame);
+ fi = get_frame_info (selected_frame);
+ pc = fi->pc;
+
+ sals.nelts = 0;
+ sals.sals = NULL;
+
+ if (block == 0)
+ error ("No symbol table info available.\n");
+
+ bl = blockvector_for_pc (BLOCK_END (block) - 4, &index);
+ blocks_searched = (char *) alloca (BLOCKVECTOR_NBLOCKS (bl) * sizeof (char));
+ memset (blocks_searched, 0, BLOCKVECTOR_NBLOCKS (bl) * sizeof (char));
+
+ while (block != 0)
+ {
+ CORE_ADDR end = BLOCK_END (block) - 4;
+ int last_index;
+
+ if (bl != blockvector_for_pc (end, &index))
+ error ("blockvector blotch");
+ if (BLOCKVECTOR_BLOCK (bl, index) != block)
+ error ("blockvector botch");
+ last_index = BLOCKVECTOR_NBLOCKS (bl);
+ index += 1;
+
+ /* Don't print out blocks that have gone by. */
+ while (index < last_index
+ && BLOCK_END (BLOCKVECTOR_BLOCK (bl, index)) < pc)
+ index++;
+
+ while (index < last_index
+ && BLOCK_END (BLOCKVECTOR_BLOCK (bl, index)) < end)
+ {
+ if (blocks_searched[index] == 0)
+ {
+ struct block *b = BLOCKVECTOR_BLOCK (bl, index);
+ int nsyms;
+ register int i;
+ register struct symbol *sym;
+
+ nsyms = BLOCK_NSYMS (b);
+
+ for (i = 0; i < nsyms; i++)
+ {
+ sym = BLOCK_SYM (b, i);
+ if (STREQ (SYMBOL_NAME (sym), "default"))
+ {
+ if (have_default)
+ continue;
+ have_default = 1;
+ }
+ if (SYMBOL_CLASS (sym) == LOC_LABEL)
+ {
+ struct sal_chain *next = (struct sal_chain *)
+ alloca (sizeof (struct sal_chain));
+ next->next = sal_chain;
+ next->sal = find_pc_line (SYMBOL_VALUE_ADDRESS (sym), 0);
+ sal_chain = next;
+ }
+ }
+ blocks_searched[index] = 1;
+ }
+ index++;
+ }
+ if (have_default)
+ break;
+ if (sal_chain && this_level_only)
+ break;
+
+ /* After handling the function's top-level block, stop.
+ Don't continue to its superblock, the block of
+ per-file symbols. */
+ if (BLOCK_FUNCTION (block))
+ break;
+ block = BLOCK_SUPERBLOCK (block);
+ }
+
+ if (sal_chain)
+ {
+ struct sal_chain *tmp_chain;
+
+ /* Count the number of entries. */
+ for (index = 0, tmp_chain = sal_chain; tmp_chain;
+ tmp_chain = tmp_chain->next)
+ index++;
+
+ sals.nelts = index;
+ sals.sals = (struct symtab_and_line *)
+ xmalloc (index * sizeof (struct symtab_and_line));
+ for (index = 0; sal_chain; sal_chain = sal_chain->next, index++)
+ sals.sals[index] = sal_chain->sal;
+ }
+
+ return sals;
+}
+
+/* Commands to deal with catching exceptions. */
+
+static void
+catch_command_1 (arg, tempflag, from_tty)
+ char *arg;
+ int tempflag;
+ int from_tty;
+{
+ /* First, translate ARG into something we can deal with in terms
+ of breakpoints. */
+
+ struct symtabs_and_lines sals;
+ struct symtab_and_line sal;
+ register struct expression *cond = 0;
+ register struct breakpoint *b;
+ char *save_arg;
+ int i;
+
+ sal.line = sal.pc = sal.end = 0;
+ sal.symtab = 0;
+
+ /* If no arg given, or if first arg is 'if ', all active catch clauses
+ are breakpointed. */
+
+ if (!arg || (arg[0] == 'i' && arg[1] == 'f'
+ && (arg[2] == ' ' || arg[2] == '\t')))
+ {
+ /* Grab all active catch clauses. */
+ sals = get_catch_sals (0);
+ }
+ else
+ {
+ /* Grab selected catch clauses. */
+ error ("catch NAME not implemented");
+#if 0
+ /* This isn't used; I don't know what it was for. */
+ sals = map_catch_names (arg, catch_breakpoint);
+#endif
+ }
+
+ if (! sals.nelts)
+ return;
+
+ save_arg = arg;
+ for (i = 0; i < sals.nelts; i++)
+ {
+ resolve_sal_pc (&sals.sals[i]);
+
+ while (arg && *arg)
+ {
+ if (arg[0] == 'i' && arg[1] == 'f'
+ && (arg[2] == ' ' || arg[2] == '\t'))
+ cond = parse_exp_1 ((arg += 2, &arg),
+ block_for_pc (sals.sals[i].pc), 0);
+ else
+ error ("Junk at end of arguments.");
+ }
+ arg = save_arg;
+ }
+
+ for (i = 0; i < sals.nelts; i++)
+ {
+ sal = sals.sals[i];
+
+ if (from_tty)
+ describe_other_breakpoints (sal.pc);
+
+ b = set_raw_breakpoint (sal);
+ set_breakpoint_count (breakpoint_count + 1);
+ b->number = breakpoint_count;
+ b->type = bp_breakpoint;
+ b->cond = cond;
+ b->enable = enabled;
+ b->disposition = tempflag ? delete : donttouch;
+
+ mention (b);
+ }
+
+ if (sals.nelts > 1)
+ {
+ printf ("Multiple breakpoints were set.\n");
+ printf ("Use the \"delete\" command to delete unwanted breakpoints.\n");
+ }
+ free ((PTR)sals.sals);
+}
+
+#if 0
+/* These aren't used; I don't know what they were for. */
+/* Disable breakpoints on all catch clauses described in ARGS. */
+static void
+disable_catch (args)
+ char *args;
+{
+ /* Map the disable command to catch clauses described in ARGS. */
+}
+
+/* Enable breakpoints on all catch clauses described in ARGS. */
+static void
+enable_catch (args)
+ char *args;
+{
+ /* Map the disable command to catch clauses described in ARGS. */
+}
+
+/* Delete breakpoints on all catch clauses in the active scope. */
+static void
+delete_catch (args)
+ char *args;
+{
+ /* Map the delete command to catch clauses described in ARGS. */
+}
+#endif /* 0 */
+
+static void
+catch_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ catch_command_1 (arg, 0, from_tty);
+}
+
+static void
+clear_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ register struct breakpoint *b, *b1;
+ struct symtabs_and_lines sals;
+ struct symtab_and_line sal;
+ register struct breakpoint *found;
+ int i;
+
+ if (arg)
+ {
+ sals = decode_line_spec (arg, 1);
+ }
+ else
+ {
+ sals.sals = (struct symtab_and_line *) xmalloc (sizeof (struct symtab_and_line));
+ sal.line = default_breakpoint_line;
+ sal.symtab = default_breakpoint_symtab;
+ sal.pc = 0;
+ if (sal.symtab == 0)
+ error ("No source file specified.");
+
+ sals.sals[0] = sal;
+ sals.nelts = 1;
+ }
+
+ for (i = 0; i < sals.nelts; i++)
+ {
+ /* If exact pc given, clear bpts at that pc.
+ But if sal.pc is zero, clear all bpts on specified line. */
+ sal = sals.sals[i];
+ found = (struct breakpoint *) 0;
+ while (breakpoint_chain
+ && (sal.pc
+ ? breakpoint_chain->address == sal.pc
+ : (breakpoint_chain->source_file != NULL
+ && sal.symtab != NULL
+ && STREQ (breakpoint_chain->source_file,
+ sal.symtab->filename)
+ && breakpoint_chain->line_number == sal.line)))
+ {
+ b1 = breakpoint_chain;
+ breakpoint_chain = b1->next;
+ b1->next = found;
+ found = b1;
+ }
+
+ ALL_BREAKPOINTS (b)
+ while (b->next
+ && b->next->type != bp_watchpoint
+ && (sal.pc
+ ? b->next->address == sal.pc
+ : (b->next->source_file != NULL
+ && sal.symtab != NULL
+ && STREQ (b->next->source_file, sal.symtab->filename)
+ && b->next->line_number == sal.line)))
+ {
+ b1 = b->next;
+ b->next = b1->next;
+ b1->next = found;
+ found = b1;
+ }
+
+ if (found == 0)
+ {
+ if (arg)
+ error ("No breakpoint at %s.", arg);
+ else
+ error ("No breakpoint at this line.");
+ }
+
+ if (found->next) from_tty = 1; /* Always report if deleted more than one */
+ if (from_tty) printf ("Deleted breakpoint%s ", found->next ? "s" : "");
+ while (found)
+ {
+ if (from_tty) printf ("%d ", found->number);
+ b1 = found->next;
+ delete_breakpoint (found);
+ found = b1;
+ }
+ if (from_tty) putchar ('\n');
+ }
+ free ((PTR)sals.sals);
+}
+
+/* Delete breakpoint in BS if they are `delete' breakpoints.
+ This is called after any breakpoint is hit, or after errors. */
+
+void
+breakpoint_auto_delete (bs)
+ bpstat bs;
+{
+ for (; bs; bs = bs->next)
+ if (bs->breakpoint_at && bs->breakpoint_at->disposition == delete
+ && bs->stop)
+ delete_breakpoint (bs->breakpoint_at);
+}
+
+/* Delete a breakpoint and clean up all traces of it in the data structures. */
+
+void
+delete_breakpoint (bpt)
+ struct breakpoint *bpt;
+{
+ register struct breakpoint *b;
+ register bpstat bs;
+
+ if (bpt->inserted)
+ target_remove_breakpoint(bpt->address, bpt->shadow_contents);
+
+ if (breakpoint_chain == bpt)
+ breakpoint_chain = bpt->next;
+
+ ALL_BREAKPOINTS (b)
+ if (b->next == bpt)
+ {
+ b->next = bpt->next;
+ break;
+ }
+
+ check_duplicates (bpt->address);
+ /* If this breakpoint was inserted, and there is another breakpoint
+ at the same address, we need to insert the other breakpoint. */
+ if (bpt->inserted)
+ {
+ ALL_BREAKPOINTS (b)
+ if (b->address == bpt->address
+ && !b->duplicate
+ && b->enable != disabled)
+ {
+ int val;
+ val = target_insert_breakpoint (b->address, b->shadow_contents);
+ if (val != 0)
+ {
+ fprintf (stderr, "Cannot insert breakpoint %d:\n", b->number);
+ memory_error (val, b->address); /* which bombs us out */
+ }
+ else
+ b->inserted = 1;
+ }
+ }
+
+ free_command_lines (&bpt->commands);
+ if (bpt->cond)
+ free (bpt->cond);
+ if (bpt->cond_string != NULL)
+ free (bpt->cond_string);
+ if (bpt->addr_string != NULL)
+ free (bpt->addr_string);
+ if (bpt->exp_string != NULL)
+ free (bpt->exp_string);
+ if (bpt->source_file != NULL)
+ free (bpt->source_file);
+
+ if (xgdb_verbose && bpt->type == bp_breakpoint)
+ printf ("breakpoint #%d deleted\n", bpt->number);
+
+ /* Be sure no bpstat's are pointing at it after it's been freed. */
+ /* FIXME, how can we find all bpstat's? We just check stop_bpstat for now. */
+ for (bs = stop_bpstat; bs; bs = bs->next)
+ if (bs->breakpoint_at == bpt)
+ bs->breakpoint_at = NULL;
+ free ((PTR)bpt);
+}
+
+static void
+delete_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+
+ if (arg == 0)
+ {
+ /* Ask user only if there are some breakpoints to delete. */
+ if (!from_tty
+ || (breakpoint_chain && query ("Delete all breakpoints? ", 0, 0)))
+ {
+ /* No arg; clear all breakpoints. */
+ while (breakpoint_chain)
+ delete_breakpoint (breakpoint_chain);
+ }
+ }
+ else
+ map_breakpoint_numbers (arg, delete_breakpoint);
+}
+
+/* Reset a breakpoint given it's struct breakpoint * BINT.
+ The value we return ends up being the return value from catch_errors.
+ Unused in this case. */
+
+static int
+breakpoint_re_set_one (bint)
+ char *bint;
+{
+ struct breakpoint *b = (struct breakpoint *)bint; /* get past catch_errs */
+ int i;
+ struct symtabs_and_lines sals;
+ char *s;
+ enum enable save_enable;
+
+ switch (b->type)
+ {
+ case bp_breakpoint:
+ if (b->addr_string == NULL)
+ {
+ /* Anything without a string can't be re-set. */
+ delete_breakpoint (b);
+ return 0;
+ }
+ /* In case we have a problem, disable this breakpoint. We'll restore
+ its status if we succeed. */
+ save_enable = b->enable;
+ b->enable = disabled;
+
+ s = b->addr_string;
+ sals = decode_line_1 (&s, 1, (struct symtab *)NULL, 0, (char ***)NULL);
+ for (i = 0; i < sals.nelts; i++)
+ {
+ resolve_sal_pc (&sals.sals[i]);
+
+ /* Reparse conditions, they might contain references to the
+ old symtab. */
+ if (b->cond_string != NULL)
+ {
+ s = b->cond_string;
+ if (b->cond)
+ free ((PTR)b->cond);
+ b->cond = parse_exp_1 (&s, block_for_pc (sals.sals[i].pc), 0);
+ }
+
+ /* We need to re-set the breakpoint if the address changes...*/
+ if (b->address != sals.sals[i].pc
+ /* ...or new and old breakpoints both have source files, and
+ the source file name or the line number changes... */
+ || (b->source_file != NULL
+ && sals.sals[i].symtab != NULL
+ && (!STREQ (b->source_file, sals.sals[i].symtab->filename)
+ || b->line_number != sals.sals[i].line)
+ )
+ /* ...or we switch between having a source file and not having
+ one. */
+ || ((b->source_file == NULL) != (sals.sals[i].symtab == NULL))
+ )
+ {
+ if (b->source_file != NULL)
+ free (b->source_file);
+ if (sals.sals[i].symtab == NULL)
+ b->source_file = NULL;
+ else
+ b->source_file =
+ savestring (sals.sals[i].symtab->filename,
+ strlen (sals.sals[i].symtab->filename));
+ b->line_number = sals.sals[i].line;
+ b->address = sals.sals[i].pc;
+
+ check_duplicates (b->address);
+
+ mention (b);
+ }
+ b->enable = save_enable; /* Restore it, this worked. */
+ }
+ free ((PTR)sals.sals);
+ break;
+
+ case bp_watchpoint:
+ innermost_block = NULL;
+ /* The issue arises of what context to evaluate this in. The same
+ one as when it was set, but what does that mean when symbols have
+ been re-read? We could save the filename and functionname, but
+ if the context is more local than that, the best we could do would
+ be something like how many levels deep and which index at that
+ particular level, but that's going to be less stable than filenames
+ or functionnames. */
+ /* So for now, just use a global context. */
+ b->exp = parse_expression (b->exp_string);
+ b->exp_valid_block = innermost_block;
+ b->val = evaluate_expression (b->exp);
+ release_value (b->val);
+ if (VALUE_LAZY (b->val))
+ value_fetch_lazy (b->val);
+
+ if (b->cond_string != NULL)
+ {
+ s = b->cond_string;
+ b->cond = parse_exp_1 (&s, (struct block *)0, 0);
+ }
+ if (b->enable == enabled)
+ mention (b);
+ break;
+
+ default:
+ printf_filtered ("Deleting unknown breakpoint type %d\n", b->type);
+ /* fall through */
+ case bp_until:
+ case bp_finish:
+ case bp_longjmp:
+ case bp_longjmp_resume:
+ case bp_call_dummy:
+ delete_breakpoint (b);
+ break;
+ }
+
+ return 0;
+}
+
+/* Re-set all breakpoints after symbols have been re-loaded. */
+void
+breakpoint_re_set ()
+{
+ struct breakpoint *b, *temp;
+ static char message1[] = "Error in re-setting breakpoint %d:\n";
+ char message[sizeof (message1) + 30 /* slop */];
+
+ ALL_BREAKPOINTS_SAFE (b, temp)
+ {
+ sprintf (message, message1, b->number); /* Format possible error msg */
+ catch_errors (breakpoint_re_set_one, (char *) b, message,
+ RETURN_MASK_ALL);
+ }
+
+ create_longjmp_breakpoint("longjmp");
+ create_longjmp_breakpoint("_longjmp");
+ create_longjmp_breakpoint("siglongjmp");
+ create_longjmp_breakpoint(NULL);
+
+#if 0
+ /* Took this out (temporaliy at least), since it produces an extra
+ blank line at startup. This messes up the gdbtests. -PB */
+ /* Blank line to finish off all those mention() messages we just printed. */
+ printf_filtered ("\n");
+#endif
+}
+
+/* Set ignore-count of breakpoint number BPTNUM to COUNT.
+ If from_tty is nonzero, it prints a message to that effect,
+ which ends with a period (no newline). */
+
+void
+set_ignore_count (bptnum, count, from_tty)
+ int bptnum, count, from_tty;
+{
+ register struct breakpoint *b;
+
+ if (count < 0)
+ count = 0;
+
+ ALL_BREAKPOINTS (b)
+ if (b->number == bptnum)
+ {
+ b->ignore_count = count;
+ if (!from_tty)
+ return;
+ else if (count == 0)
+ printf_filtered ("Will stop next time breakpoint %d is reached.",
+ bptnum);
+ else if (count == 1)
+ printf_filtered ("Will ignore next crossing of breakpoint %d.",
+ bptnum);
+ else
+ printf_filtered ("Will ignore next %d crossings of breakpoint %d.",
+ count, bptnum);
+ return;
+ }
+
+ error ("No breakpoint number %d.", bptnum);
+}
+
+/* Clear the ignore counts of all breakpoints. */
+void
+breakpoint_clear_ignore_counts ()
+{
+ struct breakpoint *b;
+
+ ALL_BREAKPOINTS (b)
+ b->ignore_count = 0;
+}
+
+/* Command to set ignore-count of breakpoint N to COUNT. */
+
+static void
+ignore_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ char *p = args;
+ register int num;
+
+ if (p == 0)
+ error_no_arg ("a breakpoint number");
+
+ num = get_number (&p);
+
+ if (*p == 0)
+ error ("Second argument (specified ignore-count) is missing.");
+
+ set_ignore_count (num,
+ longest_to_int (value_as_long (parse_and_eval (p))),
+ from_tty);
+ printf_filtered ("\n");
+}
+
+/* Call FUNCTION on each of the breakpoints
+ whose numbers are given in ARGS. */
+
+static void
+map_breakpoint_numbers (args, function)
+ char *args;
+ void (*function) PARAMS ((struct breakpoint *));
+{
+ register char *p = args;
+ char *p1;
+ register int num;
+ register struct breakpoint *b;
+
+ if (p == 0)
+ error_no_arg ("one or more breakpoint numbers");
+
+ while (*p)
+ {
+ p1 = p;
+
+ num = get_number (&p1);
+
+ ALL_BREAKPOINTS (b)
+ if (b->number == num)
+ {
+ function (b);
+ goto win;
+ }
+ printf ("No breakpoint number %d.\n", num);
+ win:
+ p = p1;
+ }
+}
+
+static void
+enable_breakpoint (bpt)
+ struct breakpoint *bpt;
+{
+ FRAME save_selected_frame = NULL;
+ int save_selected_frame_level = -1;
+
+ bpt->enable = enabled;
+
+ if (xgdb_verbose && bpt->type == bp_breakpoint)
+ printf ("breakpoint #%d enabled\n", bpt->number);
+
+ check_duplicates (bpt->address);
+ if (bpt->type == bp_watchpoint)
+ {
+ if (bpt->exp_valid_block != NULL)
+ {
+ FRAME fr = within_scope (bpt->exp_valid_block);
+ if (fr == NULL)
+ {
+ printf_filtered ("\
+Cannot enable watchpoint %d because the block in which its expression\n\
+is valid is not currently in scope.\n", bpt->number);
+ bpt->enable = disabled;
+ return;
+ }
+ save_selected_frame = selected_frame;
+ save_selected_frame_level = selected_frame_level;
+ select_frame (fr, -1);
+ }
+
+ value_free (bpt->val);
+
+ bpt->val = evaluate_expression (bpt->exp);
+ release_value (bpt->val);
+ if (VALUE_LAZY (bpt->val))
+ value_fetch_lazy (bpt->val);
+
+ if (save_selected_frame_level >= 0)
+ select_frame (save_selected_frame, save_selected_frame_level);
+ }
+}
+
+/* ARGSUSED */
+static void
+enable_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ struct breakpoint *bpt;
+ if (args == 0)
+ ALL_BREAKPOINTS (bpt)
+ switch (bpt->type)
+ {
+ case bp_breakpoint:
+ case bp_watchpoint:
+ enable_breakpoint (bpt);
+ default:
+ continue;
+ }
+ else
+ map_breakpoint_numbers (args, enable_breakpoint);
+}
+
+static void
+disable_breakpoint (bpt)
+ struct breakpoint *bpt;
+{
+ bpt->enable = disabled;
+
+ if (xgdb_verbose && bpt->type == bp_breakpoint)
+ printf_filtered ("breakpoint #%d disabled\n", bpt->number);
+
+ check_duplicates (bpt->address);
+}
+
+/* ARGSUSED */
+static void
+disable_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ register struct breakpoint *bpt;
+ if (args == 0)
+ ALL_BREAKPOINTS (bpt)
+ switch (bpt->type)
+ {
+ case bp_breakpoint:
+ case bp_watchpoint:
+ disable_breakpoint (bpt);
+ default:
+ continue;
+ }
+ else
+ map_breakpoint_numbers (args, disable_breakpoint);
+}
+
+static void
+enable_once_breakpoint (bpt)
+ struct breakpoint *bpt;
+{
+ bpt->enable = enabled;
+ bpt->disposition = disable;
+
+ check_duplicates (bpt->address);
+}
+
+/* ARGSUSED */
+static void
+enable_once_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ map_breakpoint_numbers (args, enable_once_breakpoint);
+}
+
+static void
+enable_delete_breakpoint (bpt)
+ struct breakpoint *bpt;
+{
+ bpt->enable = enabled;
+ bpt->disposition = delete;
+
+ check_duplicates (bpt->address);
+}
+
+/* ARGSUSED */
+static void
+enable_delete_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ map_breakpoint_numbers (args, enable_delete_breakpoint);
+}
+
+/*
+ * Use default_breakpoint_'s, or nothing if they aren't valid.
+ */
+struct symtabs_and_lines
+decode_line_spec_1 (string, funfirstline)
+ char *string;
+ int funfirstline;
+{
+ struct symtabs_and_lines sals;
+ if (string == 0)
+ error ("Empty line specification.");
+ if (default_breakpoint_valid)
+ sals = decode_line_1 (&string, funfirstline,
+ default_breakpoint_symtab, default_breakpoint_line,
+ (char ***)NULL);
+ else
+ sals = decode_line_1 (&string, funfirstline,
+ (struct symtab *)NULL, 0, (char ***)NULL);
+ if (*string)
+ error ("Junk at end of line specification: %s", string);
+ return sals;
+}
+
+void
+_initialize_breakpoint ()
+{
+ breakpoint_chain = 0;
+ /* Don't bother to call set_breakpoint_count. $bpnum isn't useful
+ before a breakpoint is set. */
+ breakpoint_count = 0;
+
+ add_com ("ignore", class_breakpoint, ignore_command,
+ "Set ignore-count of breakpoint number N to COUNT.");
+
+ add_com ("commands", class_breakpoint, commands_command,
+ "Set commands to be executed when a breakpoint is hit.\n\
+Give breakpoint number as argument after \"commands\".\n\
+With no argument, the targeted breakpoint is the last one set.\n\
+The commands themselves follow starting on the next line.\n\
+Type a line containing \"end\" to indicate the end of them.\n\
+Give \"silent\" as the first line to make the breakpoint silent;\n\
+then no output is printed when it is hit, except what the commands print.");
+
+ add_com ("condition", class_breakpoint, condition_command,
+ "Specify breakpoint number N to break only if COND is true.\n\
+N is an integer; COND is an expression to be evaluated whenever\n\
+breakpoint N is reached. ");
+
+ add_com ("tbreak", class_breakpoint, tbreak_command,
+ "Set a temporary breakpoint. Args like \"break\" command.\n\
+Like \"break\" except the breakpoint is only enabled temporarily,\n\
+so it will be disabled when hit. Equivalent to \"break\" followed\n\
+by using \"enable once\" on the breakpoint number.");
+
+ add_prefix_cmd ("enable", class_breakpoint, enable_command,
+ "Enable some breakpoints.\n\
+Give breakpoint numbers (separated by spaces) as arguments.\n\
+With no subcommand, breakpoints are enabled until you command otherwise.\n\
+This is used to cancel the effect of the \"disable\" command.\n\
+With a subcommand you can enable temporarily.",
+ &enablelist, "enable ", 1, &cmdlist);
+
+ add_abbrev_prefix_cmd ("breakpoints", class_breakpoint, enable_command,
+ "Enable some breakpoints.\n\
+Give breakpoint numbers (separated by spaces) as arguments.\n\
+This is used to cancel the effect of the \"disable\" command.\n\
+May be abbreviated to simply \"enable\".\n",
+ &enablebreaklist, "enable breakpoints ", 1, &enablelist);
+
+ add_cmd ("once", no_class, enable_once_command,
+ "Enable breakpoints for one hit. Give breakpoint numbers.\n\
+If a breakpoint is hit while enabled in this fashion, it becomes disabled.\n\
+See the \"tbreak\" command which sets a breakpoint and enables it once.",
+ &enablebreaklist);
+
+ add_cmd ("delete", no_class, enable_delete_command,
+ "Enable breakpoints and delete when hit. Give breakpoint numbers.\n\
+If a breakpoint is hit while enabled in this fashion, it is deleted.",
+ &enablebreaklist);
+
+ add_cmd ("delete", no_class, enable_delete_command,
+ "Enable breakpoints and delete when hit. Give breakpoint numbers.\n\
+If a breakpoint is hit while enabled in this fashion, it is deleted.",
+ &enablelist);
+
+ add_cmd ("once", no_class, enable_once_command,
+ "Enable breakpoints for one hit. Give breakpoint numbers.\n\
+If a breakpoint is hit while enabled in this fashion, it becomes disabled.\n\
+See the \"tbreak\" command which sets a breakpoint and enables it once.",
+ &enablelist);
+
+ add_prefix_cmd ("disable", class_breakpoint, disable_command,
+ "Disable some breakpoints.\n\
+Arguments are breakpoint numbers with spaces in between.\n\
+To disable all breakpoints, give no argument.\n\
+A disabled breakpoint is not forgotten, but has no effect until reenabled.",
+ &disablelist, "disable ", 1, &cmdlist);
+ add_com_alias ("dis", "disable", class_breakpoint, 1);
+ add_com_alias ("disa", "disable", class_breakpoint, 1);
+
+ add_cmd ("breakpoints", class_alias, disable_command,
+ "Disable some breakpoints.\n\
+Arguments are breakpoint numbers with spaces in between.\n\
+To disable all breakpoints, give no argument.\n\
+A disabled breakpoint is not forgotten, but has no effect until reenabled.\n\
+This command may be abbreviated \"disable\".",
+ &disablelist);
+
+ add_prefix_cmd ("delete", class_breakpoint, delete_command,
+ "Delete some breakpoints or auto-display expressions.\n\
+Arguments are breakpoint numbers with spaces in between.\n\
+To delete all breakpoints, give no argument.\n\
+\n\
+Also a prefix command for deletion of other GDB objects.\n\
+The \"unset\" command is also an alias for \"delete\".",
+ &deletelist, "delete ", 1, &cmdlist);
+ add_com_alias ("d", "delete", class_breakpoint, 1);
+
+ add_cmd ("breakpoints", class_alias, delete_command,
+ "Delete some breakpoints or auto-display expressions.\n\
+Arguments are breakpoint numbers with spaces in between.\n\
+To delete all breakpoints, give no argument.\n\
+This command may be abbreviated \"delete\".",
+ &deletelist);
+
+ add_com ("clear", class_breakpoint, clear_command,
+ "Clear breakpoint at specified line or function.\n\
+Argument may be line number, function name, or \"*\" and an address.\n\
+If line number is specified, all breakpoints in that line are cleared.\n\
+If function is specified, breakpoints at beginning of function are cleared.\n\
+If an address is specified, breakpoints at that address are cleared.\n\n\
+With no argument, clears all breakpoints in the line that the selected frame\n\
+is executing in.\n\
+\n\
+See also the \"delete\" command which clears breakpoints by number.");
+
+ add_com ("break", class_breakpoint, break_command,
+ "Set breakpoint at specified line or function.\n\
+Argument may be line number, function name, or \"*\" and an address.\n\
+If line number is specified, break at start of code for that line.\n\
+If function is specified, break at start of code for that function.\n\
+If an address is specified, break at that exact address.\n\
+With no arg, uses current execution address of selected stack frame.\n\
+This is useful for breaking on return to a stack frame.\n\
+\n\
+Multiple breakpoints at one place are permitted, and useful if conditional.\n\
+\n\
+Do \"help breakpoints\" for info on other commands dealing with breakpoints.");
+ add_com_alias ("b", "break", class_run, 1);
+ add_com_alias ("br", "break", class_run, 1);
+ add_com_alias ("bre", "break", class_run, 1);
+ add_com_alias ("brea", "break", class_run, 1);
+
+ add_info ("breakpoints", breakpoints_info,
+ "Status of user-settable breakpoints, or breakpoint number NUMBER.\n\
+The \"Type\" column indicates one of:\n\
+\tbreakpoint - normal breakpoint\n\
+\twatchpoint - watchpoint\n\
+The \"Disp\" column contains one of \"keep\", \"del\", or \"dis\" to indicate\n\
+the disposition of the breakpoint after it gets hit. \"dis\" means that the\n\
+breakpoint will be disabled. The \"Address\" and \"What\" columns indicate the\n\
+address and file/line number respectively.\n\n\
+Convenience variable \"$_\" and default examine address for \"x\"\n\
+are set to the address of the last breakpoint listed.\n\n\
+Convenience variable \"$bpnum\" contains the number of the last\n\
+breakpoint set.");
+
+#if MAINTENANCE_CMDS
+
+ add_cmd ("breakpoints", class_maintenance, maintenance_info_breakpoints,
+ "Status of all breakpoints, or breakpoint number NUMBER.\n\
+The \"Type\" column indicates one of:\n\
+\tbreakpoint - normal breakpoint\n\
+\twatchpoint - watchpoint\n\
+\tlongjmp - internal breakpoint used to step through longjmp()\n\
+\tlongjmp resume - internal breakpoint at the target of longjmp()\n\
+\tuntil - internal breakpoint used by the \"until\" command\n\
+\tfinish - internal breakpoint used by the \"finish\" command\n\
+The \"Disp\" column contains one of \"keep\", \"del\", or \"dis\" to indicate\n\
+the disposition of the breakpoint after it gets hit. \"dis\" means that the\n\
+breakpoint will be disabled. The \"Address\" and \"What\" columns indicate the\n\
+address and file/line number respectively.\n\n\
+Convenience variable \"$_\" and default examine address for \"x\"\n\
+are set to the address of the last breakpoint listed.\n\n\
+Convenience variable \"$bpnum\" contains the number of the last\n\
+breakpoint set.",
+ &maintenanceinfolist);
+
+#endif /* MAINTENANCE_CMDS */
+
+ add_com ("catch", class_breakpoint, catch_command,
+ "Set breakpoints to catch exceptions that are raised.\n\
+Argument may be a single exception to catch, multiple exceptions\n\
+to catch, or the default exception \"default\". If no arguments\n\
+are given, breakpoints are set at all exception handlers catch clauses\n\
+within the current scope.\n\
+\n\
+A condition specified for the catch applies to all breakpoints set\n\
+with this command\n\
+\n\
+Do \"help breakpoints\" for info on other commands dealing with breakpoints.");
+
+ add_com ("watch", class_breakpoint, watch_command,
+ "Set a watchpoint for an expression.\n\
+A watchpoint stops execution of your program whenever the value of\n\
+an expression changes.");
+
+ add_info ("watchpoints", breakpoints_info,
+ "Synonym for ``info breakpoints''.");
+}
+
+/* OK, when we call objfile_relocate, we need to relocate breakpoints
+ too. breakpoint_re_set is not a good choice--for example, if
+ addr_string contains just a line number without a file name the
+ breakpoint might get set in a different file. In general, there is
+ no need to go all the way back to the user's string (though this might
+ work if some effort were made to canonicalize it), since symtabs and
+ everything except addresses are still valid.
+
+ Probably the best way to solve this is to have each breakpoint save
+ the objfile and the section number that was used to set it (if set
+ by "*addr", probably it is best to use find_pc_line to get a symtab
+ and use the objfile and block_line_section for that symtab). Then
+ objfile_relocate can call fixup_breakpoints with the objfile and
+ the new_offsets, and it can relocate only the appropriate breakpoints. */
+
+#ifdef IBM6000_TARGET
+/* But for now, just kludge it based on the concept that before an
+ objfile is relocated the breakpoint is below 0x10000000, and afterwards
+ it is higher, so that way we only relocate each breakpoint once. */
+
+void
+fixup_breakpoints (low, high, delta)
+ CORE_ADDR low;
+ CORE_ADDR high;
+ CORE_ADDR delta;
+{
+ struct breakpoint *b;
+
+ ALL_BREAKPOINTS (b)
+ {
+ if (b->address >= low && b->address <= high)
+ b->address += delta;
+ }
+}
+#endif
diff --git a/gnu/usr.bin/gdb/gdb/breakpoint.h b/gnu/usr.bin/gdb/gdb/breakpoint.h
new file mode 100644
index 0000000..5005450
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/breakpoint.h
@@ -0,0 +1,372 @@
+/* Data structures associated with breakpoints in GDB.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (BREAKPOINT_H)
+#define BREAKPOINT_H 1
+
+#include "frame.h"
+#include "value.h"
+
+/* This is the maximum number of bytes a breakpoint instruction can take.
+ Feel free to increase it. It's just used in a few places to size
+ arrays that should be independent of the target architecture. */
+
+#define BREAKPOINT_MAX 16
+
+/* Type of breakpoint. */
+/* FIXME In the future, we should fold all other breakpoint-like things into
+ here. This includes:
+
+ * single-step (for machines where we have to simulate single stepping)
+ (probably, though perhaps it is better for it to look as much as
+ possible like a single-step to wait_for_inferior). */
+
+enum bptype {
+ bp_breakpoint, /* Normal breakpoint */
+ bp_until, /* used by until command */
+ bp_finish, /* used by finish command */
+ bp_watchpoint, /* Watchpoint */
+ bp_longjmp, /* secret breakpoint to find longjmp() */
+ bp_longjmp_resume, /* secret breakpoint to escape longjmp() */
+
+ /* Used by wait_for_inferior for stepping over subroutine calls, for
+ stepping over signal handlers, and for skipping prologues. */
+ bp_step_resume,
+
+ /* The breakpoint at the end of a call dummy. */
+ /* FIXME: What if the function we are calling longjmp()s out of the
+ call, or the user gets out with the "return" command? We currently
+ have no way of cleaning up the breakpoint in these (obscure) situations.
+ (Probably can solve this by noticing longjmp, "return", etc., it's
+ similar to noticing when a watchpoint on a local variable goes out
+ of scope (with hardware support for watchpoints)). */
+ bp_call_dummy
+};
+
+/* States of enablement of breakpoint. */
+
+enum enable { disabled, enabled};
+
+/* Disposition of breakpoint. Ie: what to do after hitting it. */
+
+enum bpdisp {
+ delete, /* Delete it */
+ disable, /* Disable it */
+ donttouch /* Leave it alone */
+};
+
+/* Note that the ->silent field is not currently used by any commands
+ (though the code is in there if it was to be, and set_raw_breakpoint
+ does set it to 0). I implemented it because I thought it would be
+ useful for a hack I had to put in; I'm going to leave it in because
+ I can see how there might be times when it would indeed be useful */
+
+/* This is for a breakpoint or a watchpoint. */
+
+struct breakpoint
+{
+ struct breakpoint *next;
+ /* Type of breakpoint. */
+ enum bptype type;
+ /* Zero means disabled; remember the info but don't break here. */
+ enum enable enable;
+ /* What to do with this breakpoint after we hit it. */
+ enum bpdisp disposition;
+ /* Number assigned to distinguish breakpoints. */
+ int number;
+
+ /* Address to break at, or NULL if not a breakpoint. */
+ CORE_ADDR address;
+
+ /* Line number of this address. Only matters if address is
+ non-NULL. */
+
+ int line_number;
+
+ /* Source file name of this address. Only matters if address is
+ non-NULL. */
+
+ char *source_file;
+
+ /* Non-zero means a silent breakpoint (don't print frame info
+ if we stop here). */
+ unsigned char silent;
+ /* Number of stops at this breakpoint that should
+ be continued automatically before really stopping. */
+ int ignore_count;
+ /* "Real" contents of byte where breakpoint has been inserted.
+ Valid only when breakpoints are in the program. Under the complete
+ control of the target insert_breakpoint and remove_breakpoint routines.
+ No other code should assume anything about the value(s) here. */
+ char shadow_contents[BREAKPOINT_MAX];
+ /* Nonzero if this breakpoint is now inserted. Only matters if address
+ is non-NULL. */
+ char inserted;
+ /* Nonzero if this is not the first breakpoint in the list
+ for the given address. Only matters if address is non-NULL. */
+ char duplicate;
+ /* Chain of command lines to execute when this breakpoint is hit. */
+ struct command_line *commands;
+ /* Stack depth (address of frame). If nonzero, break only if fp
+ equals this. */
+ FRAME_ADDR frame;
+ /* Conditional. Break only if this expression's value is nonzero. */
+ struct expression *cond;
+
+ /* String we used to set the breakpoint (malloc'd). Only matters if
+ address is non-NULL. */
+ char *addr_string;
+ /* String form of the breakpoint condition (malloc'd), or NULL if there
+ is no condition. */
+ char *cond_string;
+ /* String form of exp (malloc'd), or NULL if none. */
+ char *exp_string;
+
+ /* The expression we are watching, or NULL if not a watchpoint. */
+ struct expression *exp;
+ /* The largest block within which it is valid, or NULL if it is
+ valid anywhere (e.g. consists just of global symbols). */
+ struct block *exp_valid_block;
+ /* Value of the watchpoint the last time we checked it. */
+ value val;
+ /* Thread number for thread-specific breakpoint, or -1 if don't care */
+ int thread;
+};
+
+/* The following stuff is an abstract data type "bpstat" ("breakpoint status").
+ This provides the ability to determine whether we have stopped at a
+ breakpoint, and what we should do about it. */
+
+typedef struct bpstat *bpstat;
+
+/* Interface: */
+/* Clear a bpstat so that it says we are not at any breakpoint.
+ Also free any storage that is part of a bpstat. */
+extern void bpstat_clear PARAMS ((bpstat *));
+
+/* Return a copy of a bpstat. Like "bs1 = bs2" but all storage that
+ is part of the bpstat is copied as well. */
+extern bpstat bpstat_copy PARAMS ((bpstat));
+
+/* FIXME: prototypes uses equivalence between FRAME_ADDR and CORE_ADDR */
+extern bpstat bpstat_stop_status PARAMS ((CORE_ADDR *, CORE_ADDR, int));
+
+/* This bpstat_what stuff tells wait_for_inferior what to do with a
+ breakpoint (a challenging task). */
+
+enum bpstat_what_main_action {
+ /* Perform various other tests; that is, this bpstat does not
+ say to perform any action (e.g. failed watchpoint and nothing
+ else). */
+ BPSTAT_WHAT_KEEP_CHECKING,
+
+ /* Rather than distinguish between noisy and silent stops here, it
+ might be cleaner to have bpstat_print make that decision (also
+ taking into account stop_print_frame and source_only). But the
+ implications are a bit scary (interaction with auto-displays, etc.),
+ so I won't try it. */
+
+ /* Stop silently. */
+ BPSTAT_WHAT_STOP_SILENT,
+
+ /* Stop and print. */
+ BPSTAT_WHAT_STOP_NOISY,
+
+ /* Remove breakpoints, single step once, then put them back in and
+ go back to what we were doing. It's possible that this should be
+ removed from the main_action and put into a separate field, to more
+ cleanly handle BPSTAT_WHAT_CLEAR_LONGJMP_RESUME_SINGLE. */
+ BPSTAT_WHAT_SINGLE,
+
+ /* Set longjmp_resume breakpoint, remove all other breakpoints,
+ and continue. The "remove all other breakpoints" part is required
+ if we are also stepping over another breakpoint as well as doing
+ the longjmp handling. */
+ BPSTAT_WHAT_SET_LONGJMP_RESUME,
+
+ /* Clear longjmp_resume breakpoint, then handle as
+ BPSTAT_WHAT_KEEP_CHECKING. */
+ BPSTAT_WHAT_CLEAR_LONGJMP_RESUME,
+
+ /* Clear longjmp_resume breakpoint, then handle as BPSTAT_WHAT_SINGLE. */
+ BPSTAT_WHAT_CLEAR_LONGJMP_RESUME_SINGLE,
+
+ /* This is just used to keep track of how many enums there are. */
+ BPSTAT_WHAT_LAST
+};
+
+struct bpstat_what {
+ enum bpstat_what_main_action main_action;
+
+ /* Did we hit the step resume breakpoint? This is separate from the
+ main_action to allow for it to be combined with any of the main
+ actions. */
+ int step_resume;
+
+ /* Did we hit a call dummy breakpoint? This only goes with a main_action
+ of BPSTAT_WHAT_STOP_SILENT or BPSTAT_WHAT_STOP_NOISY (the concept of
+ continuing from a call dummy without popping the frame is not a
+ useful one). */
+ int call_dummy;
+};
+
+/* Tell what to do about this bpstat. */
+struct bpstat_what bpstat_what PARAMS ((bpstat));
+
+/* Find the bpstat associated with a breakpoint. NULL otherwise. */
+bpstat bpstat_find_breakpoint PARAMS ((bpstat, struct breakpoint *));
+
+/* Nonzero if a signal that we got in wait() was due to circumstances
+ explained by the BS. */
+/* Currently that is true if we have hit a breakpoint, or if there is
+ a watchpoint enabled. */
+#define bpstat_explains_signal(bs) ((bs) != NULL)
+
+/* Nonzero if we should step constantly (e.g. watchpoints on machines
+ without hardware support). This isn't related to a specific bpstat,
+ just to things like whether watchpoints are set. */
+extern int bpstat_should_step PARAMS ((void));
+
+/* Print a message indicating what happened. Returns nonzero to
+ say that only the source line should be printed after this (zero
+ return means print the frame as well as the source line). */
+extern int bpstat_print PARAMS ((bpstat));
+
+/* Return the breakpoint number of the first breakpoint we are stopped
+ at. *BSP upon return is a bpstat which points to the remaining
+ breakpoints stopped at (but which is not guaranteed to be good for
+ anything but further calls to bpstat_num).
+ Return 0 if passed a bpstat which does not indicate any breakpoints. */
+extern int bpstat_num PARAMS ((bpstat *));
+
+/* Perform actions associated with having stopped at *BSP. Actually, we just
+ use this for breakpoint commands. Perhaps other actions will go here
+ later, but this is executed at a late time (from the command loop). */
+extern void bpstat_do_actions PARAMS ((bpstat *));
+
+/* Modify BS so that the actions will not be performed. */
+extern void bpstat_clear_actions PARAMS ((bpstat));
+
+/* Implementation: */
+struct bpstat
+{
+ /* Linked list because there can be two breakpoints at the
+ same place, and a bpstat reflects the fact that both have been hit. */
+ bpstat next;
+ /* Breakpoint that we are at. */
+ struct breakpoint *breakpoint_at;
+ /* Commands left to be done. */
+ struct command_line *commands;
+ /* Old value associated with a watchpoint. */
+ value old_val;
+
+ /* Nonzero if this breakpoint tells us to print the frame. */
+ char print;
+
+ /* Nonzero if this breakpoint tells us to stop. */
+ char stop;
+
+ /* Function called by bpstat_print to print stuff associated with
+ this element of the bpstat chain. Returns 0 or 1 just like
+ bpstat_print, or -1 if it can't deal with it. */
+ int (*print_it) PARAMS((bpstat bs));
+};
+
+/* Prototypes for breakpoint-related functions. */
+
+#ifdef __STDC__ /* Forward declarations for prototypes */
+struct frame_info;
+#endif
+
+extern int
+breakpoint_here_p PARAMS ((CORE_ADDR));
+
+extern int
+breakpoint_thread_match PARAMS ((CORE_ADDR, int));
+
+extern void
+until_break_command PARAMS ((char *, int));
+
+extern void
+breakpoint_re_set PARAMS ((void));
+
+extern void
+clear_momentary_breakpoints PARAMS ((void));
+
+/* FIXME: Prototype uses equivalence of "struct frame_info *" and FRAME */
+extern struct breakpoint *
+set_momentary_breakpoint PARAMS ((struct symtab_and_line,
+ struct frame_info *,
+ enum bptype));
+
+extern void
+set_ignore_count PARAMS ((int, int, int));
+
+extern void
+set_default_breakpoint PARAMS ((int, CORE_ADDR, struct symtab *, int));
+
+extern void
+mark_breakpoints_out PARAMS ((void));
+
+extern void
+breakpoint_init_inferior PARAMS ((void));
+
+extern void
+delete_breakpoint PARAMS ((struct breakpoint *));
+
+extern void
+breakpoint_auto_delete PARAMS ((bpstat));
+
+extern void
+breakpoint_clear_ignore_counts PARAMS ((void));
+
+extern void
+break_command PARAMS ((char *, int));
+
+extern int
+insert_breakpoints PARAMS ((void));
+
+extern int
+remove_breakpoints PARAMS ((void));
+
+extern void
+enable_longjmp_breakpoint PARAMS ((void));
+
+extern void
+disable_longjmp_breakpoint PARAMS ((void));
+
+extern void
+set_longjmp_resume_breakpoint PARAMS ((CORE_ADDR, FRAME));
+
+/* The following are for displays, which aren't really breakpoints, but
+ here is as good a place as any for them. */
+
+extern void
+disable_current_display PARAMS ((void));
+
+extern void
+do_displays PARAMS ((void));
+
+extern void
+disable_display PARAMS ((int));
+
+extern void
+clear_displays PARAMS ((void));
+
+#endif /* !defined (BREAKPOINT_H) */
diff --git a/gnu/usr.bin/gdb/gdb/buildsym.c b/gnu/usr.bin/gdb/gdb/buildsym.c
new file mode 100644
index 0000000..dfa9be1
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/buildsym.c
@@ -0,0 +1,950 @@
+/* Support routines for building symbol tables in GDB's internal format.
+ Copyright 1986, 1987, 1988, 1989, 1990, 1991, 1992
+ Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This module provides subroutines used for creating and adding to
+ the symbol table. These routines are called from various symbol-
+ file-reading routines.
+
+ Routines to support specific debugging information formats (stabs,
+ DWARF, etc) belong somewhere else. */
+
+#include "defs.h"
+#include "bfd.h"
+#include "obstack.h"
+#include "symtab.h"
+#include "symfile.h" /* Needed for "struct complaint" */
+#include "objfiles.h"
+#include "complaints.h"
+#include <string.h>
+
+/* Ask buildsym.h to define the vars it normally declares `extern'. */
+#define EXTERN /**/
+#include "buildsym.h" /* Our own declarations */
+#undef EXTERN
+
+static int
+compare_line_numbers PARAMS ((const void *, const void *));
+
+static struct blockvector *
+make_blockvector PARAMS ((struct objfile *));
+
+
+/* Initial sizes of data structures. These are realloc'd larger if needed,
+ and realloc'd down to the size actually used, when completed. */
+
+#define INITIAL_CONTEXT_STACK_SIZE 10
+#define INITIAL_LINE_VECTOR_LENGTH 1000
+
+
+/* Complaints about the symbols we have encountered. */
+
+struct complaint innerblock_complaint =
+ {"inner block not inside outer block in %s", 0, 0};
+
+struct complaint innerblock_anon_complaint =
+ {"inner block not inside outer block", 0, 0};
+
+struct complaint blockvector_complaint =
+ {"block at 0x%lx out of order", 0, 0};
+
+
+/* maintain the lists of symbols and blocks */
+
+/* Add a symbol to one of the lists of symbols. */
+
+void
+add_symbol_to_list (symbol, listhead)
+ struct symbol *symbol;
+ struct pending **listhead;
+{
+ register struct pending *link;
+
+ /* We keep PENDINGSIZE symbols in each link of the list.
+ If we don't have a link with room in it, add a new link. */
+ if (*listhead == NULL || (*listhead)->nsyms == PENDINGSIZE)
+ {
+ if (free_pendings)
+ {
+ link = free_pendings;
+ free_pendings = link->next;
+ }
+ else
+ {
+ link = (struct pending *) xmalloc (sizeof (struct pending));
+ }
+
+ link->next = *listhead;
+ *listhead = link;
+ link->nsyms = 0;
+ }
+
+ (*listhead)->symbol[(*listhead)->nsyms++] = symbol;
+}
+
+/* Find a symbol named NAME on a LIST. NAME need not be '\0'-terminated;
+ LENGTH is the length of the name. */
+
+struct symbol *
+find_symbol_in_list (list, name, length)
+ struct pending *list;
+ char *name;
+ int length;
+{
+ int j;
+ char *pp;
+
+ while (list != NULL)
+ {
+ for (j = list->nsyms; --j >= 0; )
+ {
+ pp = SYMBOL_NAME (list->symbol[j]);
+ if (*pp == *name && strncmp (pp, name, length) == 0 &&
+ pp[length] == '\0')
+ {
+ return (list->symbol[j]);
+ }
+ }
+ list = list->next;
+ }
+ return (NULL);
+}
+
+/* At end of reading syms, or in case of quit,
+ really free as many `struct pending's as we can easily find. */
+
+/* ARGSUSED */
+void
+really_free_pendings (foo)
+ int foo;
+{
+ struct pending *next, *next1;
+#if 0
+ struct pending_block *bnext, *bnext1;
+#endif
+
+ for (next = free_pendings; next; next = next1)
+ {
+ next1 = next->next;
+ free ((PTR)next);
+ }
+ free_pendings = NULL;
+
+#if 0 /* Now we make the links in the symbol_obstack, so don't free them. */
+ for (bnext = pending_blocks; bnext; bnext = bnext1)
+ {
+ bnext1 = bnext->next;
+ free ((PTR)bnext);
+ }
+#endif
+ pending_blocks = NULL;
+
+ for (next = file_symbols; next != NULL; next = next1)
+ {
+ next1 = next->next;
+ free ((PTR)next);
+ }
+ file_symbols = NULL;
+
+ for (next = global_symbols; next != NULL; next = next1)
+ {
+ next1 = next->next;
+ free ((PTR)next);
+ }
+ global_symbols = NULL;
+}
+
+/* Take one of the lists of symbols and make a block from it.
+ Keep the order the symbols have in the list (reversed from the input file).
+ Put the block on the list of pending blocks. */
+
+void
+finish_block (symbol, listhead, old_blocks, start, end, objfile)
+ struct symbol *symbol;
+ struct pending **listhead;
+ struct pending_block *old_blocks;
+ CORE_ADDR start, end;
+ struct objfile *objfile;
+{
+ register struct pending *next, *next1;
+ register struct block *block;
+ register struct pending_block *pblock;
+ struct pending_block *opblock;
+ register int i;
+ register int j;
+
+ /* Count the length of the list of symbols. */
+
+ for (next = *listhead, i = 0;
+ next;
+ i += next->nsyms, next = next->next)
+ {
+ /*EMPTY*/;
+ }
+
+ block = (struct block *) obstack_alloc (&objfile -> symbol_obstack,
+ (sizeof (struct block) + ((i - 1) * sizeof (struct symbol *))));
+
+ /* Copy the symbols into the block. */
+
+ BLOCK_NSYMS (block) = i;
+ for (next = *listhead; next; next = next->next)
+ {
+ for (j = next->nsyms - 1; j >= 0; j--)
+ {
+ BLOCK_SYM (block, --i) = next->symbol[j];
+ }
+ }
+
+ BLOCK_START (block) = start;
+ BLOCK_END (block) = end;
+ /* Superblock filled in when containing block is made */
+ BLOCK_SUPERBLOCK (block) = NULL;
+ BLOCK_GCC_COMPILED (block) = processing_gcc_compilation;
+
+ /* Put the block in as the value of the symbol that names it. */
+
+ if (symbol)
+ {
+ SYMBOL_BLOCK_VALUE (symbol) = block;
+ BLOCK_FUNCTION (block) = symbol;
+ }
+ else
+ {
+ BLOCK_FUNCTION (block) = NULL;
+ }
+
+ /* Now "free" the links of the list, and empty the list. */
+
+ for (next = *listhead; next; next = next1)
+ {
+ next1 = next->next;
+ next->next = free_pendings;
+ free_pendings = next;
+ }
+ *listhead = NULL;
+
+ /* Install this block as the superblock
+ of all blocks made since the start of this scope
+ that don't have superblocks yet. */
+
+ opblock = NULL;
+ for (pblock = pending_blocks; pblock != old_blocks; pblock = pblock->next)
+ {
+ if (BLOCK_SUPERBLOCK (pblock->block) == NULL)
+ {
+#if 1
+ /* Check to be sure the blocks are nested as we receive them.
+ If the compiler/assembler/linker work, this just burns a small
+ amount of time. */
+ if (BLOCK_START (pblock->block) < BLOCK_START (block) ||
+ BLOCK_END (pblock->block) > BLOCK_END (block))
+ {
+ if (symbol)
+ {
+ complain (&innerblock_complaint,
+ SYMBOL_SOURCE_NAME (symbol));
+ }
+ else
+ {
+ complain (&innerblock_anon_complaint);
+ }
+ BLOCK_START (pblock->block) = BLOCK_START (block);
+ BLOCK_END (pblock->block) = BLOCK_END (block);
+ }
+#endif
+ BLOCK_SUPERBLOCK (pblock->block) = block;
+ }
+ opblock = pblock;
+ }
+
+ /* Record this block on the list of all blocks in the file.
+ Put it after opblock, or at the beginning if opblock is 0.
+ This puts the block in the list after all its subblocks. */
+
+ /* Allocate in the symbol_obstack to save time.
+ It wastes a little space. */
+ pblock = (struct pending_block *)
+ obstack_alloc (&objfile -> symbol_obstack,
+ sizeof (struct pending_block));
+ pblock->block = block;
+ if (opblock)
+ {
+ pblock->next = opblock->next;
+ opblock->next = pblock;
+ }
+ else
+ {
+ pblock->next = pending_blocks;
+ pending_blocks = pblock;
+ }
+}
+
+static struct blockvector *
+make_blockvector (objfile)
+ struct objfile *objfile;
+{
+ register struct pending_block *next;
+ register struct blockvector *blockvector;
+ register int i;
+
+ /* Count the length of the list of blocks. */
+
+ for (next = pending_blocks, i = 0; next; next = next->next, i++) {;}
+
+ blockvector = (struct blockvector *)
+ obstack_alloc (&objfile -> symbol_obstack,
+ (sizeof (struct blockvector)
+ + (i - 1) * sizeof (struct block *)));
+
+ /* Copy the blocks into the blockvector.
+ This is done in reverse order, which happens to put
+ the blocks into the proper order (ascending starting address).
+ finish_block has hair to insert each block into the list
+ after its subblocks in order to make sure this is true. */
+
+ BLOCKVECTOR_NBLOCKS (blockvector) = i;
+ for (next = pending_blocks; next; next = next->next)
+ {
+ BLOCKVECTOR_BLOCK (blockvector, --i) = next->block;
+ }
+
+#if 0 /* Now we make the links in the obstack, so don't free them. */
+ /* Now free the links of the list, and empty the list. */
+
+ for (next = pending_blocks; next; next = next1)
+ {
+ next1 = next->next;
+ free (next);
+ }
+#endif
+ pending_blocks = NULL;
+
+#if 1 /* FIXME, shut this off after a while to speed up symbol reading. */
+ /* Some compilers output blocks in the wrong order, but we depend
+ on their being in the right order so we can binary search.
+ Check the order and moan about it. FIXME. */
+ if (BLOCKVECTOR_NBLOCKS (blockvector) > 1)
+ {
+ for (i = 1; i < BLOCKVECTOR_NBLOCKS (blockvector); i++)
+ {
+ if (BLOCK_START(BLOCKVECTOR_BLOCK (blockvector, i-1))
+ > BLOCK_START(BLOCKVECTOR_BLOCK (blockvector, i)))
+ {
+ complain (&blockvector_complaint,
+ (unsigned long) BLOCK_START(BLOCKVECTOR_BLOCK (blockvector, i)));
+ }
+ }
+ }
+#endif
+
+ return (blockvector);
+}
+
+
+/* Start recording information about source code that came from an included
+ (or otherwise merged-in) source file with a different name. NAME is
+ the name of the file (cannot be NULL), DIRNAME is the directory in which
+ it resides (or NULL if not known). */
+
+void
+start_subfile (name, dirname)
+ char *name;
+ char *dirname;
+{
+ register struct subfile *subfile;
+
+ /* See if this subfile is already known as a subfile of the
+ current main source file. */
+
+ for (subfile = subfiles; subfile; subfile = subfile->next)
+ {
+ if (STREQ (subfile->name, name))
+ {
+ current_subfile = subfile;
+ return;
+ }
+ }
+
+ /* This subfile is not known. Add an entry for it.
+ Make an entry for this subfile in the list of all subfiles
+ of the current main source file. */
+
+ subfile = (struct subfile *) xmalloc (sizeof (struct subfile));
+ subfile->next = subfiles;
+ subfiles = subfile;
+ current_subfile = subfile;
+
+ /* Save its name and compilation directory name */
+ subfile->name = (name == NULL)? NULL : strdup (name);
+ subfile->dirname = (dirname == NULL) ? NULL : strdup (dirname);
+
+ /* Initialize line-number recording for this subfile. */
+ subfile->line_vector = NULL;
+
+ /* Default the source language to whatever can be deduced from
+ the filename. If nothing can be deduced (such as for a C/C++
+ include file with a ".h" extension), then inherit whatever
+ language the previous subfile had. This kludgery is necessary
+ because there is no standard way in some object formats to
+ record the source language. Also, when symtabs are allocated
+ we try to deduce a language then as well, but it is too late
+ for us to use that information while reading symbols, since
+ symtabs aren't allocated until after all the symbols have
+ been processed for a given source file. */
+
+ subfile->language = deduce_language_from_filename (subfile->name);
+ if (subfile->language == language_unknown &&
+ subfile->next != NULL)
+ {
+ subfile->language = subfile->next->language;
+ }
+
+ /* cfront output is a C program, so in most ways it looks like a C
+ program. But to demangle we need to set the language to C++. We
+ can distinguish cfront code by the fact that it has #line
+ directives which specify a file name ending in .C.
+
+ So if the filename of this subfile ends in .C, then change the language
+ of any pending subfiles from C to C++. We also accept any other C++
+ suffixes accepted by deduce_language_from_filename (in particular,
+ some people use .cxx with cfront). */
+
+ if (subfile->name)
+ {
+ struct subfile *s;
+
+ if (deduce_language_from_filename (subfile->name) == language_cplus)
+ for (s = subfiles; s != NULL; s = s->next)
+ if (s->language == language_c)
+ s->language = language_cplus;
+ }
+
+ /* And patch up this file if necessary. */
+ if (subfile->language == language_c
+ && subfile->next != NULL
+ && subfile->next->language == language_cplus)
+ {
+ subfile->language = language_cplus;
+ }
+}
+
+/* For stabs readers, the first N_SO symbol is assumed to be the source
+ file name, and the subfile struct is initialized using that assumption.
+ If another N_SO symbol is later seen, immediately following the first
+ one, then the first one is assumed to be the directory name and the
+ second one is really the source file name.
+
+ So we have to patch up the subfile struct by moving the old name value to
+ dirname and remembering the new name. Some sanity checking is performed
+ to ensure that the state of the subfile struct is reasonable and that the
+ old name we are assuming to be a directory name actually is (by checking
+ for a trailing '/'). */
+
+void
+patch_subfile_names (subfile, name)
+ struct subfile *subfile;
+ char *name;
+{
+ if (subfile != NULL && subfile->dirname == NULL && subfile->name != NULL
+ && subfile->name[strlen(subfile->name)-1] == '/')
+ {
+ subfile->dirname = subfile->name;
+ subfile->name = strdup (name);
+
+ /* Default the source language to whatever can be deduced from
+ the filename. If nothing can be deduced (such as for a C/C++
+ include file with a ".h" extension), then inherit whatever
+ language the previous subfile had. This kludgery is necessary
+ because there is no standard way in some object formats to
+ record the source language. Also, when symtabs are allocated
+ we try to deduce a language then as well, but it is too late
+ for us to use that information while reading symbols, since
+ symtabs aren't allocated until after all the symbols have
+ been processed for a given source file. */
+
+ subfile->language = deduce_language_from_filename (subfile->name);
+ if (subfile->language == language_unknown &&
+ subfile->next != NULL)
+ {
+ subfile->language = subfile->next->language;
+ }
+ }
+}
+
+
+/* Handle the N_BINCL and N_EINCL symbol types
+ that act like N_SOL for switching source files
+ (different subfiles, as we call them) within one object file,
+ but using a stack rather than in an arbitrary order. */
+
+void
+push_subfile ()
+{
+ register struct subfile_stack *tem
+ = (struct subfile_stack *) xmalloc (sizeof (struct subfile_stack));
+
+ tem->next = subfile_stack;
+ subfile_stack = tem;
+ if (current_subfile == NULL || current_subfile->name == NULL)
+ {
+ abort ();
+ }
+ tem->name = current_subfile->name;
+}
+
+char *
+pop_subfile ()
+{
+ register char *name;
+ register struct subfile_stack *link = subfile_stack;
+
+ if (link == NULL)
+ {
+ abort ();
+ }
+ name = link->name;
+ subfile_stack = link->next;
+ free ((PTR)link);
+ return (name);
+}
+
+
+/* Add a linetable entry for line number LINE and address PC to the line
+ vector for SUBFILE. */
+
+void
+record_line (subfile, line, pc)
+ register struct subfile *subfile;
+ int line;
+ CORE_ADDR pc;
+{
+ struct linetable_entry *e;
+ /* Ignore the dummy line number in libg.o */
+
+ if (line == 0xffff)
+ {
+ return;
+ }
+
+ /* Make sure line vector exists and is big enough. */
+ if (!subfile->line_vector)
+ {
+ subfile->line_vector_length = INITIAL_LINE_VECTOR_LENGTH;
+ subfile->line_vector = (struct linetable *)
+ xmalloc (sizeof (struct linetable)
+ + subfile->line_vector_length * sizeof (struct linetable_entry));
+ subfile->line_vector->nitems = 0;
+ }
+
+ if (subfile->line_vector->nitems + 1 >= subfile->line_vector_length)
+ {
+ subfile->line_vector_length *= 2;
+ subfile->line_vector = (struct linetable *)
+ xrealloc ((char *) subfile->line_vector, (sizeof (struct linetable)
+ + subfile->line_vector_length * sizeof (struct linetable_entry)));
+ }
+
+ e = subfile->line_vector->item + subfile->line_vector->nitems++;
+ e->line = line; e->pc = pc;
+}
+
+
+/* Needed in order to sort line tables from IBM xcoff files. Sigh! */
+
+static int
+compare_line_numbers (ln1p, ln2p)
+ const PTR ln1p;
+ const PTR ln2p;
+{
+ struct linetable_entry *ln1 = (struct linetable_entry *) ln1p;
+ struct linetable_entry *ln2 = (struct linetable_entry *) ln2p;
+
+ /* Note: this code does not assume that CORE_ADDRs can fit in ints.
+ Please keep it that way. */
+ if (ln1->pc < ln2->pc)
+ return -1;
+
+ if (ln1->pc > ln2->pc)
+ return 1;
+
+ /* If pc equal, sort by line. I'm not sure whether this is optimum
+ behavior (see comment at struct linetable in symtab.h). */
+ return ln1->line - ln2->line;
+}
+
+
+/* Start a new symtab for a new source file.
+ Called, for example, when a stabs symbol of type N_SO is seen, or when
+ a DWARF TAG_compile_unit DIE is seen.
+ It indicates the start of data for one original source file. */
+
+void
+start_symtab (name, dirname, start_addr)
+ char *name;
+ char *dirname;
+ CORE_ADDR start_addr;
+{
+
+ last_source_file = name;
+ last_source_start_addr = start_addr;
+ file_symbols = NULL;
+ global_symbols = NULL;
+ within_function = 0;
+
+ /* Context stack is initially empty. Allocate first one with room for
+ 10 levels; reuse it forever afterward. */
+ if (context_stack == NULL)
+ {
+ context_stack_size = INITIAL_CONTEXT_STACK_SIZE;
+ context_stack = (struct context_stack *)
+ xmalloc (context_stack_size * sizeof (struct context_stack));
+ }
+ context_stack_depth = 0;
+
+ /* Initialize the list of sub source files with one entry
+ for this file (the top-level source file). */
+
+ subfiles = NULL;
+ current_subfile = NULL;
+ start_subfile (name, dirname);
+}
+
+/* Finish the symbol definitions for one main source file,
+ close off all the lexical contexts for that file
+ (creating struct block's for them), then make the struct symtab
+ for that file and put it in the list of all such.
+
+ END_ADDR is the address of the end of the file's text.
+ SECTION is the section number (in objfile->section_offsets) of
+ the blockvector and linetable.
+
+ Note that it is possible for end_symtab() to return NULL. In particular,
+ for the DWARF case at least, it will return NULL when it finds a
+ compilation unit that has exactly one DIE, a TAG_compile_unit DIE. This
+ can happen when we link in an object file that was compiled from an empty
+ source file. Returning NULL is probably not the correct thing to do,
+ because then gdb will never know about this empty file (FIXME). */
+
+struct symtab *
+end_symtab (end_addr, sort_pending, sort_linevec, objfile, section)
+ CORE_ADDR end_addr;
+ int sort_pending;
+ int sort_linevec;
+ struct objfile *objfile;
+ int section;
+{
+ register struct symtab *symtab = NULL;
+ register struct blockvector *blockvector;
+ register struct subfile *subfile;
+ register struct context_stack *cstk;
+ struct subfile *nextsub;
+
+ /* Finish the lexical context of the last function in the file;
+ pop the context stack. */
+
+ if (context_stack_depth > 0)
+ {
+ context_stack_depth--;
+ cstk = &context_stack[context_stack_depth];
+ /* Make a block for the local symbols within. */
+ finish_block (cstk->name, &local_symbols, cstk->old_blocks,
+ cstk->start_addr, end_addr, objfile);
+
+ if (context_stack_depth > 0)
+ {
+ /* This is said to happen with SCO. The old coffread.c code
+ simply emptied the context stack, so we do the same. FIXME:
+ Find out why it is happening. This is not believed to happen
+ in most cases (even for coffread.c); it used to be an abort(). */
+ static struct complaint msg =
+ {"Context stack not empty in end_symtab", 0, 0};
+ complain (&msg);
+ context_stack_depth = 0;
+ }
+ }
+
+ /* It is unfortunate that in xcoff, pending blocks might not be ordered
+ in this stage. Especially, blocks for static functions will show up at
+ the end. We need to sort them, so tools like `find_pc_function' and
+ `find_pc_block' can work reliably. */
+
+ if (sort_pending && pending_blocks)
+ {
+ /* FIXME! Remove this horrid bubble sort and use qsort!!! */
+ int swapped;
+ do
+ {
+ struct pending_block *pb, *pbnext;
+
+ pb = pending_blocks;
+ pbnext = pb->next;
+ swapped = 0;
+
+ while (pbnext)
+ {
+ /* swap blocks if unordered! */
+
+ if (BLOCK_START(pb->block) < BLOCK_START(pbnext->block))
+ {
+ struct block *tmp = pb->block;
+ pb->block = pbnext->block;
+ pbnext->block = tmp;
+ swapped = 1;
+ }
+ pb = pbnext;
+ pbnext = pbnext->next;
+ }
+ } while (swapped);
+ }
+
+ /* Cleanup any undefined types that have been left hanging around
+ (this needs to be done before the finish_blocks so that
+ file_symbols is still good).
+
+ Both cleanup_undefined_types and finish_global_stabs are stabs
+ specific, but harmless for other symbol readers, since on gdb
+ startup or when finished reading stabs, the state is set so these
+ are no-ops. FIXME: Is this handled right in case of QUIT? Can
+ we make this cleaner? */
+
+ cleanup_undefined_types ();
+ finish_global_stabs (objfile);
+
+ if (pending_blocks == NULL
+ && file_symbols == NULL
+ && global_symbols == NULL)
+ {
+ /* Ignore symtabs that have no functions with real debugging info */
+ blockvector = NULL;
+ }
+ else
+ {
+ /* Define the STATIC_BLOCK & GLOBAL_BLOCK, and build the blockvector. */
+ finish_block (0, &file_symbols, 0, last_source_start_addr, end_addr,
+ objfile);
+ finish_block (0, &global_symbols, 0, last_source_start_addr, end_addr,
+ objfile);
+ blockvector = make_blockvector (objfile);
+ }
+
+#ifdef PROCESS_LINENUMBER_HOOK
+ PROCESS_LINENUMBER_HOOK (); /* Needed for xcoff. */
+#endif
+
+ /* Now create the symtab objects proper, one for each subfile. */
+ /* (The main file is the last one on the chain.) */
+
+ for (subfile = subfiles; subfile; subfile = nextsub)
+ {
+ int linetablesize = 0;
+ /* If we have blocks of symbols, make a symtab.
+ Otherwise, just ignore this file and any line number info in it. */
+ symtab = NULL;
+ if (blockvector)
+ {
+ if (subfile->line_vector)
+ {
+ linetablesize = sizeof (struct linetable) +
+ subfile->line_vector->nitems * sizeof (struct linetable_entry);
+#if 0
+ /* I think this is artifact from before it went on the obstack.
+ I doubt we'll need the memory between now and when we
+ free it later in this function. */
+ /* First, shrink the linetable to make more memory. */
+ subfile->line_vector = (struct linetable *)
+ xrealloc ((char *) subfile->line_vector, linetablesize);
+#endif
+ /* If sort_linevec is false, we might want just check to make
+ sure they are sorted and complain() if not, as a way of
+ tracking down compilers/symbol readers which don't get
+ them sorted right. */
+
+ if (sort_linevec)
+ qsort (subfile->line_vector->item,
+ subfile->line_vector->nitems,
+ sizeof (struct linetable_entry), compare_line_numbers);
+ }
+
+ /* Now, allocate a symbol table. */
+ symtab = allocate_symtab (subfile->name, objfile);
+
+ /* Fill in its components. */
+ symtab->blockvector = blockvector;
+ if (subfile->line_vector)
+ {
+ /* Reallocate the line table on the symbol obstack */
+ symtab->linetable = (struct linetable *)
+ obstack_alloc (&objfile -> symbol_obstack, linetablesize);
+ memcpy (symtab->linetable, subfile->line_vector, linetablesize);
+ }
+ else
+ {
+ symtab->linetable = NULL;
+ }
+ symtab->block_line_section = section;
+ if (subfile->dirname)
+ {
+ /* Reallocate the dirname on the symbol obstack */
+ symtab->dirname = (char *)
+ obstack_alloc (&objfile -> symbol_obstack,
+ strlen (subfile -> dirname) + 1);
+ strcpy (symtab->dirname, subfile->dirname);
+ }
+ else
+ {
+ symtab->dirname = NULL;
+ }
+ symtab->free_code = free_linetable;
+ symtab->free_ptr = NULL;
+
+ /* Use whatever language we have been using for this subfile,
+ not the one that was deduced in allocate_symtab from the
+ filename. We already did our own deducing when we created
+ the subfile, and we may have altered our opinion of what
+ language it is from things we found in the symbols. */
+ symtab->language = subfile->language;
+
+ /* All symtabs for the main file and the subfiles share a
+ blockvector, so we need to clear primary for everything but
+ the main file. */
+
+ symtab->primary = 0;
+ }
+ if (subfile->name != NULL)
+ {
+ free ((PTR) subfile->name);
+ }
+ if (subfile->dirname != NULL)
+ {
+ free ((PTR) subfile->dirname);
+ }
+ if (subfile->line_vector != NULL)
+ {
+ free ((PTR) subfile->line_vector);
+ }
+
+ nextsub = subfile->next;
+ free ((PTR)subfile);
+ }
+
+ /* Set this for the main source file. */
+ if (symtab)
+ {
+ symtab->primary = 1;
+ }
+
+ last_source_file = NULL;
+ current_subfile = NULL;
+
+ return (symtab);
+}
+
+
+/* Push a context block. Args are an identifying nesting level (checkable
+ when you pop it), and the starting PC address of this context. */
+
+struct context_stack *
+push_context (desc, valu)
+ int desc;
+ CORE_ADDR valu;
+{
+ register struct context_stack *new;
+
+ if (context_stack_depth == context_stack_size)
+ {
+ context_stack_size *= 2;
+ context_stack = (struct context_stack *)
+ xrealloc ((char *) context_stack,
+ (context_stack_size * sizeof (struct context_stack)));
+ }
+
+ new = &context_stack[context_stack_depth++];
+ new->depth = desc;
+ new->locals = local_symbols;
+ new->old_blocks = pending_blocks;
+ new->start_addr = valu;
+ new->name = NULL;
+
+ local_symbols = NULL;
+
+ return (new);
+}
+
+
+/* Compute a small integer hash code for the given name. */
+
+int
+hashname (name)
+ char *name;
+{
+ register char *p = name;
+ register int total = p[0];
+ register int c;
+
+ c = p[1];
+ total += c << 2;
+ if (c)
+ {
+ c = p[2];
+ total += c << 4;
+ if (c)
+ {
+ total += p[3] << 6;
+ }
+ }
+
+ /* Ensure result is positive. */
+ if (total < 0)
+ {
+ total += (1000 << 6);
+ }
+ return (total % HASHSIZE);
+}
+
+
+/* Initialize anything that needs initializing when starting to read
+ a fresh piece of a symbol file, e.g. reading in the stuff corresponding
+ to a psymtab. */
+
+void
+buildsym_init ()
+{
+ free_pendings = NULL;
+ file_symbols = NULL;
+ global_symbols = NULL;
+ pending_blocks = NULL;
+}
+
+/* Initialize anything that needs initializing when a completely new
+ symbol file is specified (not just adding some symbols from another
+ file, e.g. a shared library). */
+
+void
+buildsym_new_init ()
+{
+ buildsym_init ();
+}
+
+/* Initializer for this module */
+
+void
+_initialize_buildsym ()
+{
+}
diff --git a/gnu/usr.bin/gdb/gdb/buildsym.h b/gnu/usr.bin/gdb/gdb/buildsym.h
new file mode 100644
index 0000000..e034863
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/buildsym.h
@@ -0,0 +1,259 @@
+/* Build symbol tables in GDB's internal format.
+ Copyright (C) 1986-1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (BUILDSYM_H)
+#define BUILDSYM_H 1
+
+/* This module provides definitions used for creating and adding to
+ the symbol table. These routines are called from various symbol-
+ file-reading routines.
+
+ They originated in dbxread.c of gdb-4.2, and were split out to
+ make xcoffread.c more maintainable by sharing code.
+
+ Variables declared in this file can be defined by #define-ing
+ the name EXTERN to null. It is used to declare variables that
+ are normally extern, but which get defined in a single module
+ using this technique. */
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+#define HASHSIZE 127 /* Size of things hashed via hashname() */
+
+/* Name of source file whose symbol data we are now processing.
+ This comes from a symbol of type N_SO. */
+
+EXTERN char *last_source_file;
+
+/* Core address of start of text of current source file.
+ This too comes from the N_SO symbol. */
+
+EXTERN CORE_ADDR last_source_start_addr;
+
+/* The list of sub-source-files within the current individual compilation.
+ Each file gets its own symtab with its own linetable and associated info,
+ but they all share one blockvector. */
+
+struct subfile
+{
+ struct subfile *next;
+ char *name;
+ char *dirname;
+ struct linetable *line_vector;
+ int line_vector_length;
+ enum language language;
+};
+
+EXTERN struct subfile *subfiles;
+
+EXTERN struct subfile *current_subfile;
+
+/* Global variable which, when set, indicates that we are processing a
+ .o file compiled with gcc */
+
+EXTERN unsigned char processing_gcc_compilation;
+
+/* When set, we are processing a .o file compiled by sun acc. This is
+ misnamed; it refers to all stabs-in-elf implementations which use
+ N_UNDF the way Sun does, including Solaris gcc. Hopefully all
+ stabs-in-elf implementations ever invented will choose to be
+ compatible. */
+
+EXTERN unsigned char processing_acc_compilation;
+
+/* Count symbols as they are processed, for error messages. */
+
+EXTERN unsigned int symnum;
+
+/* Record the symbols defined for each context in a list.
+ We don't create a struct block for the context until we
+ know how long to make it. */
+
+#define PENDINGSIZE 100
+
+struct pending
+{
+ struct pending *next;
+ int nsyms;
+ struct symbol *symbol[PENDINGSIZE];
+};
+
+/* List of free `struct pending' structures for reuse. */
+
+EXTERN struct pending *free_pendings;
+
+/* Here are the three lists that symbols are put on. */
+
+EXTERN struct pending *file_symbols; /* static at top level, and types */
+
+EXTERN struct pending *global_symbols; /* global functions and variables */
+
+EXTERN struct pending *local_symbols; /* everything local to lexic context */
+
+/* Stack representing unclosed lexical contexts
+ (that will become blocks, eventually). */
+
+struct context_stack
+{
+ /* Outer locals at the time we entered */
+
+ struct pending *locals;
+
+ /* Pointer into blocklist as of entry */
+
+ struct pending_block *old_blocks;
+
+ /* Name of function, if any, defining context*/
+
+ struct symbol *name;
+
+ /* PC where this context starts */
+
+ CORE_ADDR start_addr;
+
+ /* Temp slot for exception handling. */
+
+ CORE_ADDR end_addr;
+
+ /* For error-checking matching push/pop */
+
+ int depth;
+
+};
+
+EXTERN struct context_stack *context_stack;
+
+/* Index of first unused entry in context stack. */
+
+EXTERN int context_stack_depth;
+
+/* Currently allocated size of context stack. */
+
+EXTERN int context_stack_size;
+
+/* Macro "function" for popping contexts from the stack. Pushing is done
+ by a real function, push_context. This returns a pointer to a struct
+ context_stack. */
+
+#define pop_context() (&context_stack[--context_stack_depth]);
+
+/* Nonzero if within a function (so symbols should be local,
+ if nothing says specifically). */
+
+EXTERN int within_function;
+
+/* List of blocks already made (lexical contexts already closed).
+ This is used at the end to make the blockvector. */
+
+struct pending_block
+{
+ struct pending_block *next;
+ struct block *block;
+};
+
+EXTERN struct pending_block *pending_blocks;
+
+
+struct subfile_stack
+{
+ struct subfile_stack *next;
+ char *name;
+};
+
+EXTERN struct subfile_stack *subfile_stack;
+
+#define next_symbol_text() (*next_symbol_text_func)()
+
+/* Function to invoke get the next symbol. Return the symbol name. */
+
+EXTERN char *(*next_symbol_text_func) PARAMS ((void));
+
+/* Vector of types defined so far, indexed by their type numbers.
+ Used for both stabs and coff.
+ (In newer sun systems, dbx uses a pair of numbers in parens,
+ as in "(SUBFILENUM,NUMWITHINSUBFILE)". Then these numbers must be
+ translated through the type_translations hash table to get
+ the index into the type vector.) */
+
+EXTERN struct type **type_vector;
+
+/* Number of elements allocated for type_vector currently. */
+
+EXTERN int type_vector_length;
+
+/* Initial size of type vector. Is realloc'd larger if needed,
+ and realloc'd down to the size actually used, when completed. */
+
+#define INITIAL_TYPE_VECTOR_LENGTH 160
+
+extern void
+add_symbol_to_list PARAMS ((struct symbol *, struct pending **));
+
+extern struct symbol *
+find_symbol_in_list PARAMS ((struct pending *, char *, int));
+
+extern void
+finish_block PARAMS ((struct symbol *, struct pending **,
+ struct pending_block *, CORE_ADDR, CORE_ADDR,
+ struct objfile *));
+
+extern void
+really_free_pendings PARAMS ((int foo));
+
+extern void
+start_subfile PARAMS ((char *, char *));
+
+extern void
+patch_subfile_names PARAMS ((struct subfile *subfile, char *name));
+
+extern void
+push_subfile PARAMS ((void));
+
+extern char *
+pop_subfile PARAMS ((void));
+
+extern struct symtab *
+end_symtab PARAMS ((CORE_ADDR, int, int, struct objfile *, int));
+
+extern void
+scan_file_globals PARAMS ((struct objfile *));
+
+extern void
+buildsym_new_init PARAMS ((void));
+
+extern void
+buildsym_init PARAMS ((void));
+
+extern struct context_stack *
+push_context PARAMS ((int, CORE_ADDR));
+
+extern void
+record_line PARAMS ((struct subfile *, int, CORE_ADDR));
+
+extern void
+start_symtab PARAMS ((char *, char *, CORE_ADDR));
+
+extern int
+hashname PARAMS ((char *));
+
+#undef EXTERN
+
+#endif /* defined (BUILDSYM_H) */
diff --git a/gnu/usr.bin/gdb/gdb/c-exp.tab.c b/gnu/usr.bin/gdb/gdb/c-exp.tab.c
new file mode 100644
index 0000000..924dfc6
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/c-exp.tab.c
@@ -0,0 +1,2648 @@
+#ifndef lint
+static char yysccsid[] = "@(#)yaccpar 1.9 (Berkeley) 02/21/93";
+#endif
+#define YYBYACC 1
+#define YYMAJOR 1
+#define YYMINOR 9
+#define yyclearin (yychar=(-1))
+#define yyerrok (yyerrflag=0)
+#define YYRECOVERING (yyerrflag!=0)
+#define yyparse c_parse
+#define yylex c_lex
+#define yyerror c_error
+#define yychar c_char
+#define yyval c_val
+#define yylval c_lval
+#define yydebug c_debug
+#define yynerrs c_nerrs
+#define yyerrflag c_errflag
+#define yyss c_ss
+#define yyssp c_ssp
+#define yyvs c_vs
+#define yyvsp c_vsp
+#define yylhs c_lhs
+#define yylen c_len
+#define yydefred c_defred
+#define yydgoto c_dgoto
+#define yysindex c_sindex
+#define yyrindex c_rindex
+#define yygindex c_gindex
+#define yytable c_table
+#define yycheck c_check
+#define yyname c_name
+#define yyrule c_rule
+#define YYPREFIX "c_"
+#line 38 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+
+#include "defs.h"
+#include "expression.h"
+#include "parser-defs.h"
+#include "value.h"
+#include "language.h"
+#include "c-lang.h"
+
+/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
+ as well as gratuitiously global symbol names, so we can have multiple
+ yacc generated parsers in gdb. Note that these are only the variables
+ produced by yacc. If other parser generators (bison, byacc, etc) produce
+ additional global names that conflict at link time, then those parser
+ generators need to be fixed instead of adding those names to this list. */
+
+#define yymaxdepth c_maxdepth
+#define yyparse c_parse
+#define yylex c_lex
+#define yyerror c_error
+#define yylval c_lval
+#define yychar c_char
+#define yydebug c_debug
+#define yypact c_pact
+#define yyr1 c_r1
+#define yyr2 c_r2
+#define yydef c_def
+#define yychk c_chk
+#define yypgo c_pgo
+#define yyact c_act
+#define yyexca c_exca
+#define yyerrflag c_errflag
+#define yynerrs c_nerrs
+#define yyps c_ps
+#define yypv c_pv
+#define yys c_s
+#define yy_yys c_yys
+#define yystate c_state
+#define yytmp c_tmp
+#define yyv c_v
+#define yy_yyv c_yyv
+#define yyval c_val
+#define yylloc c_lloc
+#define yyreds c_reds /* With YYDEBUG defined */
+#define yytoks c_toks /* With YYDEBUG defined */
+
+#ifndef YYDEBUG
+#define YYDEBUG 0 /* Default to no yydebug support */
+#endif
+
+int
+yyparse PARAMS ((void));
+
+static int
+yylex PARAMS ((void));
+
+void
+yyerror PARAMS ((char *));
+
+#line 102 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+typedef union
+ {
+ LONGEST lval;
+ struct {
+ LONGEST val;
+ struct type *type;
+ } typed_val;
+ double dval;
+ struct symbol *sym;
+ struct type *tval;
+ struct stoken sval;
+ struct ttype tsym;
+ struct symtoken ssym;
+ int voidval;
+ struct block *bval;
+ enum exp_opcode opcode;
+ struct internalvar *ivar;
+
+ struct type **tvec;
+ int *ivec;
+ } YYSTYPE;
+#line 125 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+/* YYSTYPE gets defined by %union */
+static int
+parse_number PARAMS ((char *, int, int, YYSTYPE *));
+#line 121 "y.tab.c"
+#define INT 257
+#define FLOAT 258
+#define STRING 259
+#define NAME 260
+#define TYPENAME 261
+#define NAME_OR_INT 262
+#define STRUCT 263
+#define CLASS 264
+#define UNION 265
+#define ENUM 266
+#define SIZEOF 267
+#define UNSIGNED 268
+#define COLONCOLON 269
+#define TEMPLATE 270
+#define ERROR 271
+#define SIGNED_KEYWORD 272
+#define LONG 273
+#define SHORT 274
+#define INT_KEYWORD 275
+#define CONST_KEYWORD 276
+#define VOLATILE_KEYWORD 277
+#define LAST 278
+#define REGNAME 279
+#define VARIABLE 280
+#define ASSIGN_MODIFY 281
+#define THIS 282
+#define ABOVE_COMMA 283
+#define OROR 284
+#define ANDAND 285
+#define EQUAL 286
+#define NOTEQUAL 287
+#define LEQ 288
+#define GEQ 289
+#define LSH 290
+#define RSH 291
+#define UNARY 292
+#define INCREMENT 293
+#define DECREMENT 294
+#define ARROW 295
+#define BLOCKNAME 296
+#define YYERRCODE 256
+short c_lhs[] = { -1,
+ 0, 0, 3, 2, 2, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 21, 1, 6, 20, 20, 20, 7, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 19, 19, 4, 5, 5,
+ 4, 4, 4, 14, 14, 14, 14, 14, 14, 13,
+ 13, 13, 13, 13, 12, 12, 12, 12, 12, 15,
+ 15, 11, 11, 8, 8, 8, 8, 8, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 18, 18, 18, 18, 10, 10, 16, 16, 16,
+ 16, 17, 17,
+};
+short c_len[] = { 2,
+ 1, 1, 1, 1, 3, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 3, 3, 4, 3, 3,
+ 4, 4, 0, 5, 1, 0, 1, 3, 1, 3,
+ 4, 4, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 5, 3, 3, 1, 1, 1, 1, 1,
+ 1, 1, 4, 1, 1, 1, 3, 3, 3, 4,
+ 1, 2, 1, 1, 2, 2, 2, 3, 3, 1,
+ 2, 1, 2, 1, 3, 2, 1, 2, 1, 2,
+ 3, 2, 3, 1, 3, 6, 8, 9, 1, 1,
+ 1, 1, 2, 3, 2, 3, 3, 4, 2, 3,
+ 2, 2, 2, 2, 2, 1, 2, 1, 5, 2,
+ 2, 1, 1, 1, 1, 1, 3, 1, 1, 1,
+ 1, 1, 1,
+};
+short c_defred[] = { 0,
+ 56, 58, 64, 132, 99, 57, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 100, 0, 0,
+ 60, 61, 62, 65, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 25, 0, 0, 0, 2, 59, 71,
+ 0, 0, 0, 94, 73, 0, 128, 130, 131, 129,
+ 111, 112, 113, 114, 0, 0, 0, 122, 0, 0,
+ 123, 115, 72, 0, 124, 125, 117, 0, 103, 109,
+ 120, 121, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 13, 14, 0, 0, 0, 23, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 89, 0, 77, 87, 0, 0, 0, 0, 104,
+ 110, 0, 106, 33, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 17,
+ 16, 0, 20, 19, 0, 0, 0, 29, 0, 0,
+ 30, 0, 95, 0, 69, 78, 79, 83, 81, 0,
+ 90, 92, 0, 0, 0, 0, 0, 88, 86, 0,
+ 0, 108, 0, 0, 0, 0, 0, 22, 0, 0,
+ 0, 0, 70, 91, 0, 0, 93, 85, 119, 0,
+ 24, 0, 0, 0, 0, 97, 0, 98,
+};
+short c_dgoto[] = { 35,
+ 36, 78, 38, 39, 40, 41, 169, 183, 57, 185,
+ 122, 123, 124, 44, 125, 175, 45, 62, 46, 113,
+ 166,
+};
+short c_sindex[] = { 1773,
+ 0, 0, 0, 0, 0, 0, -243, -243, -243, -243,
+ 1839, -240, -243, -243, -56, -260, -266, 0, 1303, 1303,
+ 0, 0, 0, 0, 1773, 1773, 1773, 1773, 1773, 1773,
+ 0, 1773, 1773, 0, 0, 2134, -24, 0, 0, 0,
+ 1773, -16, -36, 0, 0, -233, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1773, 83, -219, 0, -217, -208,
+ 0, 0, 0, 57, 0, 0, 0, -199, 0, 0,
+ 0, 0, 83, 83, 83, 83, 83, 74, -12, 83,
+ 83, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773,
+ 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773,
+ 1773, 1773, 1773, 0, 0, 2136, 2175, 1773, 0, 1773,
+ 2134, -28, -17, 1303, -35, 31, 31, 31, 31, -90,
+ 1807, 0, -3, 0, 0, -243, 49, -52, -148, 0,
+ 0, 1303, 0, 0, 1773, 2134, 2134, 2099, 2197, 2208,
+ 2236, 2269, 2304, 2474, 2474, 743, 743, 743, 743, 615,
+ 615, 273, 320, 320, 83, 83, 83, 0, 1773, 0,
+ 0, 1773, 0, 0, -44, 1773, 2134, 0, 1773, 1773,
+ 0, -137, 0, -243, 0, 0, 0, 0, 0, 63,
+ 0, 0, -16, 28, 80, 117, 477, 0, 0, 0,
+ 1653, 0, -32, 83, 1773, 83, 83, 0, 106, 83,
+ 2134, 136, 0, 0, 145, 1303, 0, 0, 0, 2169,
+ 0, 129, -16, 155, 2276, 0, 171, 0,
+};
+short c_rindex[] = { 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1146, 0, 0, 1403, 1413, 1691, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 16, 188, 0, 0, 0,
+ -13, 206, 79, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 213, 0, 0, 1716, 1721,
+ 0, 0, 0, 0, 0, 0, 0, 1730, 0, 0,
+ 0, 0, 311, 402, 414, 487, 515, 0, 0, 583,
+ 673, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ -19, 0, 0, 0, 0, 10, 52, 111, 359, 0,
+ 0, 0, 492, 0, 0, 0, 0, 0, 1746, 0,
+ 0, 0, 0, 0, 0, 668, 892, 0, 881, 153,
+ 479, 1135, 128, 1583, 1620, 1366, 1438, 1546, 1572, 1264,
+ 1312, 1236, 1182, 1224, 782, 794, 853, 40, 0, 0,
+ 0, 0, 0, 0, 0, 193, 239, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 201, -30, 0, 0, 0, 0, 0, 99,
+ 138, 0, 0, 928, 0, 967, 1056, 0, 0, 1110,
+ -18, 0, 0, 0, 0, 0, 0, 0, 0, 110,
+ 0, 0, 257, 544, 0, 0, 0, 0,
+};
+short c_gindex[] = { 0,
+ 2543, 5, 0, 0, 42, 0, 107, 75, 450, 13,
+ 115, 0, 148, 0, 120, 989, 0, 215, 0, 81,
+ 0,
+};
+#define YYTABLESIZE 2769
+short c_table[] = { 110,
+ 133, 118, 181, 121, 37, 119, 173, 114, 70, 75,
+ 74, 114, 68, 74, 69, 4, 47, 48, 49, 110,
+ 58, 27, 28, 114, 27, 28, 170, 114, 135, 209,
+ 26, 74, 59, 60, 61, 126, 187, 133, 133, 130,
+ 133, 133, 133, 133, 133, 133, 133, 133, 198, 128,
+ 75, 76, 50, 75, 120, 129, 4, 130, 133, 4,
+ 133, 133, 133, 133, 133, 118, 131, 121, 118, 119,
+ 121, 75, 119, 174, 42, 133, 130, 130, 74, 130,
+ 130, 130, 130, 130, 130, 130, 130, 120, 114, 191,
+ 174, 133, 76, 133, 133, 76, 168, 130, 68, 130,
+ 130, 130, 130, 130, 79, 27, 28, 168, 4, 53,
+ 82, 26, 165, 76, 134, 112, 132, 110, 120, 74,
+ 207, 120, 109, 206, 133, 133, 192, 48, 107, 127,
+ 130, 202, 130, 130, 75, 68, 68, 63, 68, 68,
+ 68, 68, 68, 68, 68, 68, 211, 160, 163, 170,
+ 53, 82, 51, 53, 82, 204, 68, 208, 68, 68,
+ 68, 68, 68, 130, 130, 48, 180, 53, 48, 214,
+ 53, 48, 82, 108, 63, 63, 76, 212, 63, 63,
+ 63, 63, 63, 63, 63, 48, 173, 1, 48, 68,
+ 48, 68, 68, 51, 215, 63, 51, 63, 63, 63,
+ 63, 63, 53, 74, 58, 3, 193, 47, 48, 49,
+ 51, 218, 15, 51, 206, 51, 65, 66, 61, 171,
+ 48, 48, 68, 68, 47, 48, 49, 217, 63, 67,
+ 63, 63, 115, 26, 53, 82, 26, 188, 5, 116,
+ 117, 126, 189, 50, 126, 51, 199, 0, 0, 15,
+ 15, 48, 48, 15, 15, 15, 15, 15, 0, 15,
+ 50, 63, 63, 176, 177, 178, 179, 0, 186, 66,
+ 15, 0, 15, 15, 15, 15, 15, 51, 0, 5,
+ 213, 133, 5, 0, 133, 133, 133, 133, 133, 133,
+ 133, 133, 0, 133, 133, 133, 205, 127, 0, 0,
+ 127, 0, 0, 116, 117, 15, 15, 0, 99, 103,
+ 7, 0, 109, 0, 101, 99, 0, 100, 107, 102,
+ 130, 0, 0, 130, 130, 130, 130, 130, 130, 130,
+ 130, 5, 130, 130, 130, 0, 15, 15, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 7, 7, 0,
+ 0, 7, 7, 7, 7, 7, 103, 7, 80, 109,
+ 0, 101, 0, 108, 0, 107, 102, 67, 7, 0,
+ 7, 7, 7, 7, 7, 104, 105, 106, 0, 68,
+ 0, 0, 68, 68, 68, 68, 68, 68, 68, 68,
+ 53, 68, 68, 68, 0, 0, 0, 0, 0, 80,
+ 0, 8, 80, 7, 7, 0, 0, 0, 48, 0,
+ 108, 48, 48, 6, 0, 0, 0, 0, 63, 0,
+ 80, 63, 63, 63, 63, 63, 63, 63, 63, 0,
+ 0, 0, 63, 51, 7, 7, 51, 51, 8, 8,
+ 0, 0, 8, 8, 8, 8, 8, 0, 8, 43,
+ 6, 6, 0, 0, 6, 6, 6, 6, 6, 8,
+ 6, 8, 8, 8, 8, 8, 0, 0, 71, 72,
+ 0, 6, 0, 6, 6, 6, 6, 6, 50, 43,
+ 0, 0, 0, 80, 0, 0, 11, 0, 0, 0,
+ 43, 84, 0, 15, 8, 8, 15, 15, 15, 15,
+ 15, 15, 15, 15, 43, 0, 6, 6, 0, 0,
+ 0, 0, 0, 0, 12, 0, 0, 182, 0, 50,
+ 0, 0, 50, 11, 11, 8, 8, 11, 11, 11,
+ 11, 11, 84, 11, 0, 84, 50, 6, 6, 50,
+ 0, 50, 0, 96, 11, 0, 11, 11, 11, 11,
+ 11, 12, 12, 84, 0, 12, 12, 12, 12, 12,
+ 0, 12, 0, 172, 0, 104, 105, 106, 0, 0,
+ 184, 50, 12, 0, 12, 12, 12, 12, 12, 11,
+ 11, 184, 9, 0, 96, 0, 0, 96, 0, 0,
+ 0, 7, 0, 0, 7, 7, 7, 7, 7, 7,
+ 7, 7, 50, 50, 0, 96, 0, 12, 12, 0,
+ 11, 11, 104, 105, 106, 0, 84, 0, 0, 9,
+ 9, 0, 0, 9, 9, 9, 9, 9, 0, 9,
+ 0, 0, 0, 0, 0, 0, 184, 0, 12, 12,
+ 9, 0, 9, 9, 9, 9, 9, 0, 0, 0,
+ 0, 103, 0, 0, 109, 184, 101, 99, 0, 100,
+ 107, 102, 0, 0, 184, 0, 0, 55, 96, 0,
+ 0, 0, 10, 0, 0, 9, 9, 0, 98, 0,
+ 0, 0, 8, 0, 0, 8, 8, 8, 8, 8,
+ 8, 8, 8, 0, 6, 0, 0, 6, 6, 6,
+ 6, 6, 6, 6, 6, 108, 9, 9, 55, 10,
+ 10, 55, 0, 10, 10, 10, 10, 10, 0, 10,
+ 0, 0, 0, 0, 0, 55, 0, 0, 0, 0,
+ 10, 0, 10, 10, 10, 10, 10, 5, 0, 7,
+ 8, 9, 10, 0, 12, 0, 14, 0, 15, 16,
+ 17, 18, 19, 20, 0, 0, 0, 0, 0, 50,
+ 55, 0, 50, 50, 0, 10, 10, 11, 0, 0,
+ 11, 11, 11, 11, 11, 11, 11, 11, 0, 103,
+ 0, 35, 109, 0, 101, 99, 0, 100, 107, 102,
+ 0, 0, 55, 36, 0, 12, 10, 10, 12, 12,
+ 12, 12, 12, 12, 12, 12, 98, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 35, 35,
+ 0, 0, 35, 35, 35, 35, 35, 0, 35, 0,
+ 36, 36, 0, 108, 36, 36, 36, 36, 36, 35,
+ 36, 35, 35, 35, 35, 35, 0, 0, 0, 0,
+ 0, 36, 37, 36, 36, 36, 36, 36, 0, 0,
+ 0, 0, 0, 9, 0, 0, 9, 9, 9, 9,
+ 9, 9, 9, 9, 35, 35, 0, 0, 0, 0,
+ 52, 0, 0, 0, 0, 0, 36, 36, 0, 37,
+ 37, 54, 0, 37, 37, 37, 37, 37, 0, 37,
+ 0, 0, 0, 0, 0, 35, 35, 104, 105, 106,
+ 37, 0, 37, 37, 37, 37, 37, 36, 36, 0,
+ 0, 52, 0, 0, 52, 0, 0, 32, 0, 0,
+ 0, 0, 54, 0, 0, 54, 0, 0, 52, 0,
+ 0, 52, 0, 52, 0, 37, 37, 0, 0, 54,
+ 0, 0, 0, 10, 0, 0, 10, 10, 10, 10,
+ 10, 10, 10, 10, 32, 32, 18, 0, 32, 32,
+ 32, 32, 32, 52, 32, 0, 37, 37, 0, 0,
+ 0, 0, 0, 0, 54, 32, 0, 32, 32, 32,
+ 32, 32, 0, 0, 0, 51, 52, 53, 54, 0,
+ 0, 63, 64, 18, 18, 52, 0, 18, 18, 18,
+ 18, 18, 0, 18, 0, 0, 54, 0, 0, 0,
+ 32, 32, 0, 0, 18, 0, 18, 18, 18, 18,
+ 18, 0, 96, 97, 0, 104, 105, 106, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 32, 32, 0, 0, 21, 0, 0, 0, 18,
+ 18, 0, 35, 0, 0, 35, 35, 35, 35, 35,
+ 35, 35, 35, 0, 36, 0, 0, 36, 36, 36,
+ 36, 36, 36, 36, 36, 0, 0, 0, 0, 0,
+ 18, 18, 21, 21, 161, 164, 21, 21, 21, 21,
+ 21, 0, 21, 0, 0, 0, 0, 0, 0, 31,
+ 0, 0, 0, 21, 190, 21, 21, 21, 21, 21,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 37, 49, 0, 37, 37, 37, 37,
+ 37, 37, 37, 37, 0, 116, 31, 31, 21, 21,
+ 31, 31, 31, 31, 31, 0, 31, 0, 0, 0,
+ 0, 52, 203, 0, 52, 0, 0, 31, 0, 31,
+ 31, 31, 31, 31, 0, 49, 0, 0, 49, 21,
+ 21, 38, 0, 116, 0, 116, 116, 116, 0, 116,
+ 0, 0, 49, 0, 0, 49, 0, 49, 0, 0,
+ 0, 0, 31, 31, 0, 0, 0, 116, 32, 0,
+ 0, 32, 32, 32, 32, 32, 32, 32, 32, 38,
+ 0, 0, 38, 39, 38, 38, 38, 49, 49, 0,
+ 0, 0, 0, 31, 31, 34, 116, 0, 0, 38,
+ 0, 38, 38, 38, 38, 38, 0, 18, 0, 0,
+ 18, 18, 18, 18, 18, 18, 18, 18, 49, 49,
+ 0, 39, 0, 40, 39, 0, 39, 39, 39, 0,
+ 116, 0, 0, 34, 38, 38, 34, 0, 0, 34,
+ 0, 39, 0, 39, 39, 39, 39, 39, 0, 0,
+ 0, 0, 0, 34, 0, 34, 34, 34, 34, 34,
+ 0, 40, 0, 0, 40, 38, 38, 40, 0, 0,
+ 0, 41, 0, 0, 0, 0, 39, 39, 0, 0,
+ 0, 40, 0, 40, 40, 40, 40, 0, 34, 34,
+ 0, 0, 0, 0, 0, 0, 21, 0, 0, 21,
+ 21, 21, 21, 21, 21, 21, 21, 39, 39, 41,
+ 0, 0, 41, 0, 0, 41, 40, 40, 0, 34,
+ 34, 0, 0, 0, 0, 46, 0, 0, 0, 41,
+ 0, 41, 41, 41, 41, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 40, 40, 0,
+ 31, 0, 0, 31, 31, 31, 31, 31, 31, 31,
+ 31, 0, 118, 46, 41, 41, 46, 0, 0, 46,
+ 0, 0, 101, 0, 116, 49, 0, 0, 49, 49,
+ 0, 116, 116, 46, 0, 46, 46, 46, 46, 0,
+ 0, 0, 0, 0, 0, 41, 41, 47, 0, 0,
+ 118, 0, 118, 118, 118, 0, 118, 0, 0, 0,
+ 101, 0, 101, 101, 101, 0, 101, 0, 46, 46,
+ 0, 0, 38, 0, 118, 38, 38, 38, 38, 38,
+ 38, 38, 38, 0, 101, 47, 0, 0, 47, 0,
+ 0, 47, 0, 0, 0, 0, 0, 0, 0, 46,
+ 46, 0, 0, 118, 0, 47, 0, 47, 47, 47,
+ 47, 0, 0, 101, 39, 0, 0, 39, 39, 39,
+ 39, 39, 39, 39, 39, 0, 34, 0, 0, 34,
+ 34, 34, 34, 34, 34, 34, 34, 118, 0, 0,
+ 47, 47, 0, 0, 0, 0, 0, 101, 0, 0,
+ 0, 0, 0, 0, 40, 44, 0, 40, 40, 40,
+ 40, 40, 40, 40, 40, 0, 0, 0, 0, 0,
+ 0, 47, 47, 5, 0, 7, 8, 9, 10, 0,
+ 12, 45, 14, 0, 15, 16, 17, 18, 19, 20,
+ 0, 0, 42, 44, 0, 0, 44, 0, 0, 44,
+ 0, 0, 41, 0, 0, 41, 41, 41, 41, 41,
+ 41, 41, 41, 44, 0, 44, 44, 44, 44, 45,
+ 0, 0, 45, 0, 0, 45, 0, 0, 0, 43,
+ 42, 0, 0, 42, 0, 0, 42, 0, 0, 45,
+ 0, 45, 45, 45, 45, 0, 0, 0, 44, 44,
+ 42, 0, 0, 42, 0, 42, 46, 0, 0, 46,
+ 46, 46, 46, 46, 46, 0, 0, 43, 0, 0,
+ 43, 0, 0, 43, 45, 45, 0, 0, 0, 44,
+ 44, 118, 0, 0, 0, 42, 42, 43, 118, 118,
+ 43, 101, 43, 0, 0, 32, 0, 0, 101, 101,
+ 102, 0, 30, 0, 0, 45, 45, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 42, 42, 0, 0,
+ 0, 0, 43, 43, 0, 124, 0, 0, 47, 0,
+ 125, 47, 47, 47, 47, 47, 47, 0, 102, 105,
+ 102, 102, 102, 0, 102, 0, 0, 0, 0, 0,
+ 0, 0, 0, 43, 43, 107, 0, 0, 0, 0,
+ 0, 0, 102, 124, 0, 124, 124, 124, 125, 124,
+ 125, 125, 125, 0, 125, 0, 0, 105, 0, 105,
+ 105, 105, 0, 105, 0, 34, 0, 124, 33, 0,
+ 0, 102, 125, 107, 0, 107, 107, 107, 0, 107,
+ 0, 105, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 32, 124, 107, 0, 0,
+ 25, 125, 30, 0, 27, 102, 0, 26, 0, 0,
+ 105, 0, 0, 0, 0, 0, 44, 0, 0, 44,
+ 44, 44, 44, 44, 44, 0, 107, 0, 0, 0,
+ 124, 0, 0, 0, 118, 125, 121, 182, 119, 0,
+ 0, 0, 45, 0, 105, 45, 45, 45, 45, 45,
+ 45, 0, 0, 42, 0, 0, 42, 42, 42, 42,
+ 107, 32, 0, 0, 0, 0, 25, 0, 55, 0,
+ 27, 0, 0, 26, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 34, 0, 120, 33, 0,
+ 43, 0, 0, 43, 43, 43, 43, 0, 0, 1,
+ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 0, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 0, 24, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 28, 29, 0, 31, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 102,
+ 0, 34, 0, 0, 33, 0, 102, 102, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 124, 0, 0, 0, 0, 125,
+ 0, 124, 124, 0, 0, 0, 125, 125, 105, 0,
+ 0, 0, 0, 0, 0, 105, 105, 0, 0, 0,
+ 0, 0, 0, 0, 107, 0, 0, 0, 0, 0,
+ 0, 107, 107, 0, 0, 0, 0, 0, 0, 1,
+ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 0, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 0, 24, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 28, 29, 5, 31, 7,
+ 8, 9, 10, 0, 12, 0, 14, 0, 15, 16,
+ 17, 18, 19, 20, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 2, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 0,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 0,
+ 24, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 28, 29, 0, 31, 103, 89, 0, 109, 0,
+ 101, 99, 0, 100, 107, 102, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 195, 0, 92, 83,
+ 93, 84, 98, 0, 0, 0, 0, 0, 0, 0,
+ 103, 89, 0, 109, 0, 101, 99, 159, 100, 107,
+ 102, 0, 0, 0, 0, 0, 0, 0, 0, 108,
+ 0, 0, 88, 92, 83, 93, 84, 98, 0, 0,
+ 0, 0, 0, 0, 0, 103, 89, 0, 109, 0,
+ 101, 99, 0, 100, 107, 102, 162, 0, 0, 0,
+ 0, 0, 87, 0, 108, 0, 0, 88, 92, 0,
+ 93, 84, 98, 103, 89, 0, 109, 0, 101, 99,
+ 0, 100, 107, 102, 103, 89, 0, 109, 0, 101,
+ 99, 0, 100, 107, 102, 0, 92, 87, 93, 108,
+ 98, 0, 88, 0, 0, 0, 0, 92, 0, 93,
+ 0, 98, 103, 89, 0, 109, 0, 101, 99, 0,
+ 100, 107, 102, 0, 0, 0, 0, 108, 0, 0,
+ 88, 0, 87, 0, 0, 92, 0, 93, 108, 98,
+ 0, 88, 0, 0, 0, 103, 89, 0, 109, 0,
+ 101, 99, 0, 100, 107, 102, 216, 0, 0, 0,
+ 87, 0, 0, 0, 0, 0, 108, 0, 92, 88,
+ 93, 87, 98, 0, 0, 0, 0, 0, 0, 0,
+ 103, 0, 0, 109, 0, 101, 99, 0, 100, 107,
+ 102, 0, 0, 0, 0, 0, 0, 0, 0, 108,
+ 0, 0, 0, 92, 0, 93, 0, 98, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 82,
+ 0, 0, 85, 86, 90, 91, 94, 95, 96, 97,
+ 0, 104, 105, 106, 108, 47, 158, 49, 7, 8,
+ 9, 10, 0, 12, 0, 14, 0, 15, 16, 17,
+ 18, 19, 20, 0, 82, 0, 0, 85, 86, 90,
+ 91, 94, 95, 96, 97, 0, 104, 105, 106, 0,
+ 0, 50, 0, 0, 47, 158, 49, 7, 8, 9,
+ 10, 0, 12, 0, 14, 0, 15, 16, 17, 18,
+ 19, 20, 85, 86, 90, 91, 94, 95, 96, 97,
+ 0, 104, 105, 106, 0, 0, 0, 0, 0, 0,
+ 50, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 86, 90, 91, 94, 95, 96, 97, 0, 104,
+ 105, 106, 0, 90, 91, 94, 95, 96, 97, 0,
+ 104, 105, 106, 0, 0, 0, 0, 0, 0, 0,
+ 103, 0, 0, 109, 0, 101, 99, 0, 100, 107,
+ 102, 90, 91, 94, 95, 96, 97, 0, 104, 105,
+ 106, 0, 0, 92, 0, 93, 5, 98, 7, 8,
+ 9, 10, 0, 12, 0, 14, 0, 15, 16, 17,
+ 18, 19, 20, 56, 90, 91, 94, 95, 96, 97,
+ 0, 104, 105, 106, 108, 0, 0, 73, 74, 75,
+ 76, 77, 0, 0, 80, 81, 0, 0, 0, 0,
+ 0, 0, 0, 111, 0, 0, 0, 0, 0, 90,
+ 91, 94, 95, 96, 97, 0, 104, 105, 106, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 136, 137, 138, 139, 140, 141,
+ 142, 143, 144, 145, 146, 147, 148, 149, 150, 151,
+ 152, 153, 154, 155, 156, 157, 0, 0, 0, 0,
+ 0, 0, 167, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 194, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 196, 0, 0, 197, 0, 0, 0, 111, 0,
+ 0, 200, 201, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 194, 0, 0, 0, 210, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 94, 95, 96, 97, 0, 104, 105, 106,
+};
+short c_check[] = { 44,
+ 0, 38, 93, 40, 0, 42, 42, 40, 275, 0,
+ 41, 40, 273, 44, 275, 0, 260, 261, 262, 44,
+ 261, 41, 41, 40, 44, 44, 44, 40, 41, 62,
+ 44, 62, 273, 274, 275, 269, 40, 37, 38, 0,
+ 40, 41, 42, 43, 44, 45, 46, 47, 93, 269,
+ 41, 0, 296, 44, 91, 273, 41, 275, 58, 44,
+ 60, 61, 62, 63, 64, 38, 275, 40, 38, 42,
+ 40, 62, 42, 126, 0, 275, 37, 38, 0, 40,
+ 41, 42, 43, 44, 45, 46, 47, 91, 40, 41,
+ 126, 91, 41, 93, 94, 44, 125, 58, 0, 60,
+ 61, 62, 63, 64, 30, 125, 125, 125, 93, 0,
+ 0, 125, 108, 62, 41, 41, 60, 44, 91, 41,
+ 41, 91, 40, 44, 124, 125, 275, 0, 46, 55,
+ 91, 269, 93, 94, 125, 37, 38, 0, 40, 41,
+ 42, 43, 44, 45, 46, 47, 41, 106, 107, 44,
+ 41, 41, 0, 44, 44, 93, 58, 41, 60, 61,
+ 62, 63, 64, 124, 125, 38, 257, 58, 41, 41,
+ 61, 44, 62, 91, 37, 38, 125, 42, 41, 42,
+ 43, 44, 45, 46, 47, 58, 42, 0, 61, 91,
+ 63, 93, 94, 41, 40, 58, 44, 60, 61, 62,
+ 63, 64, 93, 125, 261, 0, 132, 260, 261, 262,
+ 58, 41, 0, 61, 44, 63, 273, 274, 275, 113,
+ 93, 94, 124, 125, 260, 261, 262, 215, 91, 15,
+ 93, 94, 269, 41, 125, 125, 44, 123, 0, 276,
+ 277, 41, 123, 296, 44, 93, 166, -1, -1, 37,
+ 38, 124, 125, 41, 42, 43, 44, 45, -1, 47,
+ 296, 124, 125, 116, 117, 118, 119, -1, 121, 269,
+ 58, -1, 60, 61, 62, 63, 64, 125, -1, 41,
+ 206, 281, 44, -1, 284, 285, 286, 287, 288, 289,
+ 290, 291, -1, 293, 294, 295, 269, 41, -1, -1,
+ 44, -1, -1, 276, 277, 93, 94, -1, 269, 37,
+ 0, -1, 40, -1, 42, 43, -1, 45, 46, 47,
+ 281, -1, -1, 284, 285, 286, 287, 288, 289, 290,
+ 291, 93, 293, 294, 295, -1, 124, 125, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 37, 38, -1,
+ -1, 41, 42, 43, 44, 45, 37, 47, 0, 40,
+ -1, 42, -1, 91, -1, 46, 47, 269, 58, -1,
+ 60, 61, 62, 63, 64, 293, 294, 295, -1, 281,
+ -1, -1, 284, 285, 286, 287, 288, 289, 290, 291,
+ 281, 293, 294, 295, -1, -1, -1, -1, -1, 41,
+ -1, 0, 44, 93, 94, -1, -1, -1, 281, -1,
+ 91, 284, 285, 0, -1, -1, -1, -1, 281, -1,
+ 62, 284, 285, 286, 287, 288, 289, 290, 291, -1,
+ -1, -1, 295, 281, 124, 125, 284, 285, 37, 38,
+ -1, -1, 41, 42, 43, 44, 45, -1, 47, 0,
+ 37, 38, -1, -1, 41, 42, 43, 44, 45, 58,
+ 47, 60, 61, 62, 63, 64, -1, -1, 19, 20,
+ -1, 58, -1, 60, 61, 62, 63, 64, 0, 30,
+ -1, -1, -1, 125, -1, -1, 0, -1, -1, -1,
+ 41, 0, -1, 281, 93, 94, 284, 285, 286, 287,
+ 288, 289, 290, 291, 55, -1, 93, 94, -1, -1,
+ -1, -1, -1, -1, 0, -1, -1, 41, -1, 41,
+ -1, -1, 44, 37, 38, 124, 125, 41, 42, 43,
+ 44, 45, 41, 47, -1, 44, 58, 124, 125, 61,
+ -1, 63, -1, 0, 58, -1, 60, 61, 62, 63,
+ 64, 37, 38, 62, -1, 41, 42, 43, 44, 45,
+ -1, 47, -1, 114, -1, 293, 294, 295, -1, -1,
+ 121, 93, 58, -1, 60, 61, 62, 63, 64, 93,
+ 94, 132, 0, -1, 41, -1, -1, 44, -1, -1,
+ -1, 281, -1, -1, 284, 285, 286, 287, 288, 289,
+ 290, 291, 124, 125, -1, 62, -1, 93, 94, -1,
+ 124, 125, 293, 294, 295, -1, 125, -1, -1, 37,
+ 38, -1, -1, 41, 42, 43, 44, 45, -1, 47,
+ -1, -1, -1, -1, -1, -1, 187, -1, 124, 125,
+ 58, -1, 60, 61, 62, 63, 64, -1, -1, -1,
+ -1, 37, -1, -1, 40, 206, 42, 43, -1, 45,
+ 46, 47, -1, -1, 215, -1, -1, 0, 125, -1,
+ -1, -1, 0, -1, -1, 93, 94, -1, 64, -1,
+ -1, -1, 281, -1, -1, 284, 285, 286, 287, 288,
+ 289, 290, 291, -1, 281, -1, -1, 284, 285, 286,
+ 287, 288, 289, 290, 291, 91, 124, 125, 41, 37,
+ 38, 44, -1, 41, 42, 43, 44, 45, -1, 47,
+ -1, -1, -1, -1, -1, 58, -1, -1, -1, -1,
+ 58, -1, 60, 61, 62, 63, 64, 261, -1, 263,
+ 264, 265, 266, -1, 268, -1, 270, -1, 272, 273,
+ 274, 275, 276, 277, -1, -1, -1, -1, -1, 281,
+ 93, -1, 284, 285, -1, 93, 94, 281, -1, -1,
+ 284, 285, 286, 287, 288, 289, 290, 291, -1, 37,
+ -1, 0, 40, -1, 42, 43, -1, 45, 46, 47,
+ -1, -1, 125, 0, -1, 281, 124, 125, 284, 285,
+ 286, 287, 288, 289, 290, 291, 64, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 37, 38,
+ -1, -1, 41, 42, 43, 44, 45, -1, 47, -1,
+ 37, 38, -1, 91, 41, 42, 43, 44, 45, 58,
+ 47, 60, 61, 62, 63, 64, -1, -1, -1, -1,
+ -1, 58, 0, 60, 61, 62, 63, 64, -1, -1,
+ -1, -1, -1, 281, -1, -1, 284, 285, 286, 287,
+ 288, 289, 290, 291, 93, 94, -1, -1, -1, -1,
+ 0, -1, -1, -1, -1, -1, 93, 94, -1, 37,
+ 38, 0, -1, 41, 42, 43, 44, 45, -1, 47,
+ -1, -1, -1, -1, -1, 124, 125, 293, 294, 295,
+ 58, -1, 60, 61, 62, 63, 64, 124, 125, -1,
+ -1, 41, -1, -1, 44, -1, -1, 0, -1, -1,
+ -1, -1, 41, -1, -1, 44, -1, -1, 58, -1,
+ -1, 61, -1, 63, -1, 93, 94, -1, -1, 58,
+ -1, -1, -1, 281, -1, -1, 284, 285, 286, 287,
+ 288, 289, 290, 291, 37, 38, 0, -1, 41, 42,
+ 43, 44, 45, 93, 47, -1, 124, 125, -1, -1,
+ -1, -1, -1, -1, 93, 58, -1, 60, 61, 62,
+ 63, 64, -1, -1, -1, 7, 8, 9, 10, -1,
+ -1, 13, 14, 37, 38, 125, -1, 41, 42, 43,
+ 44, 45, -1, 47, -1, -1, 125, -1, -1, -1,
+ 93, 94, -1, -1, 58, -1, 60, 61, 62, 63,
+ 64, -1, 290, 291, -1, 293, 294, 295, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 124, 125, -1, -1, 0, -1, -1, -1, 93,
+ 94, -1, 281, -1, -1, 284, 285, 286, 287, 288,
+ 289, 290, 291, -1, 281, -1, -1, 284, 285, 286,
+ 287, 288, 289, 290, 291, -1, -1, -1, -1, -1,
+ 124, 125, 37, 38, 106, 107, 41, 42, 43, 44,
+ 45, -1, 47, -1, -1, -1, -1, -1, -1, 0,
+ -1, -1, -1, 58, 126, 60, 61, 62, 63, 64,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 281, 0, -1, 284, 285, 286, 287,
+ 288, 289, 290, 291, -1, 0, 37, 38, 93, 94,
+ 41, 42, 43, 44, 45, -1, 47, -1, -1, -1,
+ -1, 281, 174, -1, 284, -1, -1, 58, -1, 60,
+ 61, 62, 63, 64, -1, 41, -1, -1, 44, 124,
+ 125, 0, -1, 38, -1, 40, 41, 42, -1, 44,
+ -1, -1, 58, -1, -1, 61, -1, 63, -1, -1,
+ -1, -1, 93, 94, -1, -1, -1, 62, 281, -1,
+ -1, 284, 285, 286, 287, 288, 289, 290, 291, 38,
+ -1, -1, 41, 0, 43, 44, 45, 93, 94, -1,
+ -1, -1, -1, 124, 125, 0, 91, -1, -1, 58,
+ -1, 60, 61, 62, 63, 64, -1, 281, -1, -1,
+ 284, 285, 286, 287, 288, 289, 290, 291, 124, 125,
+ -1, 38, -1, 0, 41, -1, 43, 44, 45, -1,
+ 125, -1, -1, 38, 93, 94, 41, -1, -1, 44,
+ -1, 58, -1, 60, 61, 62, 63, 64, -1, -1,
+ -1, -1, -1, 58, -1, 60, 61, 62, 63, 64,
+ -1, 38, -1, -1, 41, 124, 125, 44, -1, -1,
+ -1, 0, -1, -1, -1, -1, 93, 94, -1, -1,
+ -1, 58, -1, 60, 61, 62, 63, -1, 93, 94,
+ -1, -1, -1, -1, -1, -1, 281, -1, -1, 284,
+ 285, 286, 287, 288, 289, 290, 291, 124, 125, 38,
+ -1, -1, 41, -1, -1, 44, 93, 94, -1, 124,
+ 125, -1, -1, -1, -1, 0, -1, -1, -1, 58,
+ -1, 60, 61, 62, 63, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 124, 125, -1,
+ 281, -1, -1, 284, 285, 286, 287, 288, 289, 290,
+ 291, -1, 0, 38, 93, 94, 41, -1, -1, 44,
+ -1, -1, 0, -1, 269, 281, -1, -1, 284, 285,
+ -1, 276, 277, 58, -1, 60, 61, 62, 63, -1,
+ -1, -1, -1, -1, -1, 124, 125, 0, -1, -1,
+ 38, -1, 40, 41, 42, -1, 44, -1, -1, -1,
+ 38, -1, 40, 41, 42, -1, 44, -1, 93, 94,
+ -1, -1, 281, -1, 62, 284, 285, 286, 287, 288,
+ 289, 290, 291, -1, 62, 38, -1, -1, 41, -1,
+ -1, 44, -1, -1, -1, -1, -1, -1, -1, 124,
+ 125, -1, -1, 91, -1, 58, -1, 60, 61, 62,
+ 63, -1, -1, 91, 281, -1, -1, 284, 285, 286,
+ 287, 288, 289, 290, 291, -1, 281, -1, -1, 284,
+ 285, 286, 287, 288, 289, 290, 291, 125, -1, -1,
+ 93, 94, -1, -1, -1, -1, -1, 125, -1, -1,
+ -1, -1, -1, -1, 281, 0, -1, 284, 285, 286,
+ 287, 288, 289, 290, 291, -1, -1, -1, -1, -1,
+ -1, 124, 125, 261, -1, 263, 264, 265, 266, -1,
+ 268, 0, 270, -1, 272, 273, 274, 275, 276, 277,
+ -1, -1, 0, 38, -1, -1, 41, -1, -1, 44,
+ -1, -1, 281, -1, -1, 284, 285, 286, 287, 288,
+ 289, 290, 291, 58, -1, 60, 61, 62, 63, 38,
+ -1, -1, 41, -1, -1, 44, -1, -1, -1, 0,
+ 38, -1, -1, 41, -1, -1, 44, -1, -1, 58,
+ -1, 60, 61, 62, 63, -1, -1, -1, 93, 94,
+ 58, -1, -1, 61, -1, 63, 281, -1, -1, 284,
+ 285, 286, 287, 288, 289, -1, -1, 38, -1, -1,
+ 41, -1, -1, 44, 93, 94, -1, -1, -1, 124,
+ 125, 269, -1, -1, -1, 93, 94, 58, 276, 277,
+ 61, 269, 63, -1, -1, 33, -1, -1, 276, 277,
+ 0, -1, 40, -1, -1, 124, 125, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 124, 125, -1, -1,
+ -1, -1, 93, 94, -1, 0, -1, -1, 281, -1,
+ 0, 284, 285, 286, 287, 288, 289, -1, 38, 0,
+ 40, 41, 42, -1, 44, -1, -1, -1, -1, -1,
+ -1, -1, -1, 124, 125, 0, -1, -1, -1, -1,
+ -1, -1, 62, 38, -1, 40, 41, 42, 38, 44,
+ 40, 41, 42, -1, 44, -1, -1, 38, -1, 40,
+ 41, 42, -1, 44, -1, 123, -1, 62, 126, -1,
+ -1, 91, 62, 38, -1, 40, 41, 42, -1, 44,
+ -1, 62, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 33, 91, 62, -1, -1,
+ 38, 91, 40, -1, 42, 125, -1, 45, -1, -1,
+ 91, -1, -1, -1, -1, -1, 281, -1, -1, 284,
+ 285, 286, 287, 288, 289, -1, 91, -1, -1, -1,
+ 125, -1, -1, -1, 38, 125, 40, 41, 42, -1,
+ -1, -1, 281, -1, 125, 284, 285, 286, 287, 288,
+ 289, -1, -1, 281, -1, -1, 284, 285, 286, 287,
+ 125, 33, -1, -1, -1, -1, 38, -1, 40, -1,
+ 42, -1, -1, 45, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 123, -1, 91, 126, -1,
+ 281, -1, -1, 284, 285, 286, 287, -1, -1, 257,
+ 258, 259, 260, 261, 262, 263, 264, 265, 266, 267,
+ 268, 269, 270, -1, 272, 273, 274, 275, 276, 277,
+ 278, 279, 280, -1, 282, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 293, 294, -1, 296, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 269,
+ -1, 123, -1, -1, 126, -1, 276, 277, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 269, -1, -1, -1, -1, 269,
+ -1, 276, 277, -1, -1, -1, 276, 277, 269, -1,
+ -1, -1, -1, -1, -1, 276, 277, -1, -1, -1,
+ -1, -1, -1, -1, 269, -1, -1, -1, -1, -1,
+ -1, 276, 277, -1, -1, -1, -1, -1, -1, 257,
+ 258, 259, 260, 261, 262, 263, 264, 265, 266, 267,
+ 268, 269, 270, -1, 272, 273, 274, 275, 276, 277,
+ 278, 279, 280, -1, 282, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 293, 294, 261, 296, 263,
+ 264, 265, 266, -1, 268, -1, 270, -1, 272, 273,
+ 274, 275, 276, 277, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 257, 258, 259, 260, 261,
+ 262, 263, 264, 265, 266, 267, 268, 269, 270, -1,
+ 272, 273, 274, 275, 276, 277, 278, 279, 280, -1,
+ 282, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 293, 294, -1, 296, 37, 38, -1, 40, -1,
+ 42, 43, -1, 45, 46, 47, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 58, -1, 60, 61,
+ 62, 63, 64, -1, -1, -1, -1, -1, -1, -1,
+ 37, 38, -1, 40, -1, 42, 43, 42, 45, 46,
+ 47, -1, -1, -1, -1, -1, -1, -1, -1, 91,
+ -1, -1, 94, 60, 61, 62, 63, 64, -1, -1,
+ -1, -1, -1, -1, -1, 37, 38, -1, 40, -1,
+ 42, 43, -1, 45, 46, 47, 42, -1, -1, -1,
+ -1, -1, 124, -1, 91, -1, -1, 94, 60, -1,
+ 62, 63, 64, 37, 38, -1, 40, -1, 42, 43,
+ -1, 45, 46, 47, 37, 38, -1, 40, -1, 42,
+ 43, -1, 45, 46, 47, -1, 60, 124, 62, 91,
+ 64, -1, 94, -1, -1, -1, -1, 60, -1, 62,
+ -1, 64, 37, 38, -1, 40, -1, 42, 43, -1,
+ 45, 46, 47, -1, -1, -1, -1, 91, -1, -1,
+ 94, -1, 124, -1, -1, 60, -1, 62, 91, 64,
+ -1, 94, -1, -1, -1, 37, 38, -1, 40, -1,
+ 42, 43, -1, 45, 46, 47, 41, -1, -1, -1,
+ 124, -1, -1, -1, -1, -1, 91, -1, 60, 94,
+ 62, 124, 64, -1, -1, -1, -1, -1, -1, -1,
+ 37, -1, -1, 40, -1, 42, 43, -1, 45, 46,
+ 47, -1, -1, -1, -1, -1, -1, -1, -1, 91,
+ -1, -1, -1, 60, -1, 62, -1, 64, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 281,
+ -1, -1, 284, 285, 286, 287, 288, 289, 290, 291,
+ -1, 293, 294, 295, 91, 260, 261, 262, 263, 264,
+ 265, 266, -1, 268, -1, 270, -1, 272, 273, 274,
+ 275, 276, 277, -1, 281, -1, -1, 284, 285, 286,
+ 287, 288, 289, 290, 291, -1, 293, 294, 295, -1,
+ -1, 296, -1, -1, 260, 261, 262, 263, 264, 265,
+ 266, -1, 268, -1, 270, -1, 272, 273, 274, 275,
+ 276, 277, 284, 285, 286, 287, 288, 289, 290, 291,
+ -1, 293, 294, 295, -1, -1, -1, -1, -1, -1,
+ 296, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 285, 286, 287, 288, 289, 290, 291, -1, 293,
+ 294, 295, -1, 286, 287, 288, 289, 290, 291, -1,
+ 293, 294, 295, -1, -1, -1, -1, -1, -1, -1,
+ 37, -1, -1, 40, -1, 42, 43, -1, 45, 46,
+ 47, 286, 287, 288, 289, 290, 291, -1, 293, 294,
+ 295, -1, -1, 60, -1, 62, 261, 64, 263, 264,
+ 265, 266, -1, 268, -1, 270, -1, 272, 273, 274,
+ 275, 276, 277, 11, 286, 287, 288, 289, 290, 291,
+ -1, 293, 294, 295, 91, -1, -1, 25, 26, 27,
+ 28, 29, -1, -1, 32, 33, -1, -1, -1, -1,
+ -1, -1, -1, 41, -1, -1, -1, -1, -1, 286,
+ 287, 288, 289, 290, 291, -1, 293, 294, 295, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
+ 98, 99, 100, 101, 102, 103, -1, -1, -1, -1,
+ -1, -1, 110, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 135, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 159, -1, -1, 162, -1, -1, -1, 166, -1,
+ -1, 169, 170, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 191, -1, -1, -1, 195, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 288, 289, 290, 291, -1, 293, 294, 295,
+};
+#define YYFINAL 35
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYMAXTOKEN 296
+#if YYDEBUG
+char *c_name[] = {
+"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+"'!'",0,0,0,"'%'","'&'",0,"'('","')'","'*'","'+'","','","'-'","'.'","'/'",0,0,0,
+0,0,0,0,0,0,0,"':'",0,"'<'","'='","'>'","'?'","'@'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,"'['",0,"']'","'^'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,"'{'","'|'","'}'","'~'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"INT","FLOAT",
+"STRING","NAME","TYPENAME","NAME_OR_INT","STRUCT","CLASS","UNION","ENUM",
+"SIZEOF","UNSIGNED","COLONCOLON","TEMPLATE","ERROR","SIGNED_KEYWORD","LONG",
+"SHORT","INT_KEYWORD","CONST_KEYWORD","VOLATILE_KEYWORD","LAST","REGNAME",
+"VARIABLE","ASSIGN_MODIFY","THIS","ABOVE_COMMA","OROR","ANDAND","EQUAL",
+"NOTEQUAL","LEQ","GEQ","LSH","RSH","UNARY","INCREMENT","DECREMENT","ARROW",
+"BLOCKNAME",
+};
+char *c_rule[] = {
+"$accept : start",
+"start : exp1",
+"start : type_exp",
+"type_exp : type",
+"exp1 : exp",
+"exp1 : exp1 ',' exp",
+"exp : '*' exp",
+"exp : '&' exp",
+"exp : '-' exp",
+"exp : '!' exp",
+"exp : '~' exp",
+"exp : INCREMENT exp",
+"exp : DECREMENT exp",
+"exp : exp INCREMENT",
+"exp : exp DECREMENT",
+"exp : SIZEOF exp",
+"exp : exp ARROW name",
+"exp : exp ARROW qualified_name",
+"exp : exp ARROW '*' exp",
+"exp : exp '.' name",
+"exp : exp '.' qualified_name",
+"exp : exp '.' '*' exp",
+"exp : exp '[' exp1 ']'",
+"$$1 :",
+"exp : exp '(' $$1 arglist ')'",
+"lcurly : '{'",
+"arglist :",
+"arglist : exp",
+"arglist : arglist ',' exp",
+"rcurly : '}'",
+"exp : lcurly arglist rcurly",
+"exp : lcurly type rcurly exp",
+"exp : '(' type ')' exp",
+"exp : '(' exp1 ')'",
+"exp : exp '@' exp",
+"exp : exp '*' exp",
+"exp : exp '/' exp",
+"exp : exp '%' exp",
+"exp : exp '+' exp",
+"exp : exp '-' exp",
+"exp : exp LSH exp",
+"exp : exp RSH exp",
+"exp : exp EQUAL exp",
+"exp : exp NOTEQUAL exp",
+"exp : exp LEQ exp",
+"exp : exp GEQ exp",
+"exp : exp '<' exp",
+"exp : exp '>' exp",
+"exp : exp '&' exp",
+"exp : exp '^' exp",
+"exp : exp '|' exp",
+"exp : exp ANDAND exp",
+"exp : exp OROR exp",
+"exp : exp '?' exp ':' exp",
+"exp : exp '=' exp",
+"exp : exp ASSIGN_MODIFY exp",
+"exp : INT",
+"exp : NAME_OR_INT",
+"exp : FLOAT",
+"exp : variable",
+"exp : LAST",
+"exp : REGNAME",
+"exp : VARIABLE",
+"exp : SIZEOF '(' type ')'",
+"exp : STRING",
+"exp : THIS",
+"block : BLOCKNAME",
+"block : block COLONCOLON name",
+"variable : block COLONCOLON name",
+"qualified_name : typebase COLONCOLON name",
+"qualified_name : typebase COLONCOLON '~' name",
+"variable : qualified_name",
+"variable : COLONCOLON name",
+"variable : name_not_typename",
+"ptype : typebase",
+"ptype : typebase CONST_KEYWORD",
+"ptype : typebase VOLATILE_KEYWORD",
+"ptype : typebase abs_decl",
+"ptype : typebase CONST_KEYWORD abs_decl",
+"ptype : typebase VOLATILE_KEYWORD abs_decl",
+"abs_decl : '*'",
+"abs_decl : '*' abs_decl",
+"abs_decl : '&'",
+"abs_decl : '&' abs_decl",
+"abs_decl : direct_abs_decl",
+"direct_abs_decl : '(' abs_decl ')'",
+"direct_abs_decl : direct_abs_decl array_mod",
+"direct_abs_decl : array_mod",
+"direct_abs_decl : direct_abs_decl func_mod",
+"direct_abs_decl : func_mod",
+"array_mod : '[' ']'",
+"array_mod : '[' INT ']'",
+"func_mod : '(' ')'",
+"func_mod : '(' nonempty_typelist ')'",
+"type : ptype",
+"type : typebase COLONCOLON '*'",
+"type : type '(' typebase COLONCOLON '*' ')'",
+"type : type '(' typebase COLONCOLON '*' ')' '(' ')'",
+"type : type '(' typebase COLONCOLON '*' ')' '(' nonempty_typelist ')'",
+"typebase : TYPENAME",
+"typebase : INT_KEYWORD",
+"typebase : LONG",
+"typebase : SHORT",
+"typebase : LONG INT_KEYWORD",
+"typebase : UNSIGNED LONG INT_KEYWORD",
+"typebase : LONG LONG",
+"typebase : LONG LONG INT_KEYWORD",
+"typebase : UNSIGNED LONG LONG",
+"typebase : UNSIGNED LONG LONG INT_KEYWORD",
+"typebase : SHORT INT_KEYWORD",
+"typebase : UNSIGNED SHORT INT_KEYWORD",
+"typebase : STRUCT name",
+"typebase : CLASS name",
+"typebase : UNION name",
+"typebase : ENUM name",
+"typebase : UNSIGNED typename",
+"typebase : UNSIGNED",
+"typebase : SIGNED_KEYWORD typename",
+"typebase : SIGNED_KEYWORD",
+"typebase : TEMPLATE name '<' type '>'",
+"typebase : CONST_KEYWORD typebase",
+"typebase : VOLATILE_KEYWORD typebase",
+"typename : TYPENAME",
+"typename : INT_KEYWORD",
+"typename : LONG",
+"typename : SHORT",
+"nonempty_typelist : type",
+"nonempty_typelist : nonempty_typelist ',' type",
+"name : NAME",
+"name : BLOCKNAME",
+"name : TYPENAME",
+"name : NAME_OR_INT",
+"name_not_typename : NAME",
+"name_not_typename : BLOCKNAME",
+};
+#endif
+#ifdef YYSTACKSIZE
+#undef YYMAXDEPTH
+#define YYMAXDEPTH YYSTACKSIZE
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 500
+#define YYMAXDEPTH 500
+#endif
+#endif
+int yydebug;
+int yynerrs;
+int yyerrflag;
+int yychar;
+short *yyssp;
+YYSTYPE *yyvsp;
+YYSTYPE yyval;
+YYSTYPE yylval;
+short yyss[YYSTACKSIZE];
+YYSTYPE yyvs[YYSTACKSIZE];
+#define yystacksize YYSTACKSIZE
+#line 914 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+
+/* Take care of parsing a number (anything that starts with a digit).
+ Set yylval and return the token type; update lexptr.
+ LEN is the number of characters in it. */
+
+/*** Needs some error checking for the float case ***/
+
+static int
+parse_number (p, len, parsed_float, putithere)
+ register char *p;
+ register int len;
+ int parsed_float;
+ YYSTYPE *putithere;
+{
+ register LONGEST n = 0;
+ register LONGEST prevn = 0;
+ register int i = 0;
+ register int c;
+ register int base = input_radix;
+ int unsigned_p = 0;
+ int long_p = 0;
+ unsigned LONGEST high_bit;
+ struct type *signed_type;
+ struct type *unsigned_type;
+
+ if (parsed_float)
+ {
+ /* It's a float since it contains a point or an exponent. */
+ putithere->dval = atof (p);
+ return FLOAT;
+ }
+
+ /* Handle base-switching prefixes 0x, 0t, 0d, 0 */
+ if (p[0] == '0')
+ switch (p[1])
+ {
+ case 'x':
+ case 'X':
+ if (len >= 3)
+ {
+ p += 2;
+ base = 16;
+ len -= 2;
+ }
+ break;
+
+ case 't':
+ case 'T':
+ case 'd':
+ case 'D':
+ if (len >= 3)
+ {
+ p += 2;
+ base = 10;
+ len -= 2;
+ }
+ break;
+
+ default:
+ base = 8;
+ break;
+ }
+
+ while (len-- > 0)
+ {
+ c = *p++;
+ if (c >= 'A' && c <= 'Z')
+ c += 'a' - 'A';
+ if (c != 'l' && c != 'u')
+ n *= base;
+ if (c >= '0' && c <= '9')
+ n += i = c - '0';
+ else
+ {
+ if (base > 10 && c >= 'a' && c <= 'f')
+ n += i = c - 'a' + 10;
+ else if (len == 0 && c == 'l')
+ long_p = 1;
+ else if (len == 0 && c == 'u')
+ unsigned_p = 1;
+ else
+ return ERROR; /* Char not a digit */
+ }
+ if (i >= base)
+ return ERROR; /* Invalid digit in this base */
+
+ /* Portably test for overflow (only works for nonzero values, so make
+ a second check for zero). */
+ if((prevn >= n) && n != 0)
+ unsigned_p=1; /* Try something unsigned */
+ /* If range checking enabled, portably test for unsigned overflow. */
+ if(RANGE_CHECK && n!=0)
+ {
+ if((unsigned_p && (unsigned)prevn >= (unsigned)n))
+ range_error("Overflow on numeric constant.");
+ }
+ prevn=n;
+ }
+
+ /* If the number is too big to be an int, or it's got an l suffix
+ then it's a long. Work out if this has to be a long by
+ shifting right and and seeing if anything remains, and the
+ target int size is different to the target long size.
+
+ In the expression below, we could have tested
+ (n >> TARGET_INT_BIT)
+ to see if it was zero,
+ but too many compilers warn about that, when ints and longs
+ are the same size. So we shift it twice, with fewer bits
+ each time, for the same result. */
+
+ if ( (TARGET_INT_BIT != TARGET_LONG_BIT
+ && ((n >> 2) >> (TARGET_INT_BIT-2))) /* Avoid shift warning */
+ || long_p)
+ {
+ high_bit = ((unsigned LONGEST)1) << (TARGET_LONG_BIT-1);
+ unsigned_type = builtin_type_unsigned_long;
+ signed_type = builtin_type_long;
+ }
+ else
+ {
+ high_bit = ((unsigned LONGEST)1) << (TARGET_INT_BIT-1);
+ unsigned_type = builtin_type_unsigned_int;
+ signed_type = builtin_type_int;
+ }
+
+ putithere->typed_val.val = n;
+
+ /* If the high bit of the worked out type is set then this number
+ has to be unsigned. */
+
+ if (unsigned_p || (n & high_bit))
+ {
+ putithere->typed_val.type = unsigned_type;
+ }
+ else
+ {
+ putithere->typed_val.type = signed_type;
+ }
+
+ return INT;
+}
+
+struct token
+{
+ char *operator;
+ int token;
+ enum exp_opcode opcode;
+};
+
+static const struct token tokentab3[] =
+ {
+ {">>=", ASSIGN_MODIFY, BINOP_RSH},
+ {"<<=", ASSIGN_MODIFY, BINOP_LSH}
+ };
+
+static const struct token tokentab2[] =
+ {
+ {"+=", ASSIGN_MODIFY, BINOP_ADD},
+ {"-=", ASSIGN_MODIFY, BINOP_SUB},
+ {"*=", ASSIGN_MODIFY, BINOP_MUL},
+ {"/=", ASSIGN_MODIFY, BINOP_DIV},
+ {"%=", ASSIGN_MODIFY, BINOP_REM},
+ {"|=", ASSIGN_MODIFY, BINOP_BITWISE_IOR},
+ {"&=", ASSIGN_MODIFY, BINOP_BITWISE_AND},
+ {"^=", ASSIGN_MODIFY, BINOP_BITWISE_XOR},
+ {"++", INCREMENT, BINOP_END},
+ {"--", DECREMENT, BINOP_END},
+ {"->", ARROW, BINOP_END},
+ {"&&", ANDAND, BINOP_END},
+ {"||", OROR, BINOP_END},
+ {"::", COLONCOLON, BINOP_END},
+ {"<<", LSH, BINOP_END},
+ {">>", RSH, BINOP_END},
+ {"==", EQUAL, BINOP_END},
+ {"!=", NOTEQUAL, BINOP_END},
+ {"<=", LEQ, BINOP_END},
+ {">=", GEQ, BINOP_END}
+ };
+
+/* Read one token, getting characters through lexptr. */
+
+static int
+yylex ()
+{
+ int c;
+ int namelen;
+ unsigned int i;
+ char *tokstart;
+ char *tokptr;
+ int tempbufindex;
+ static char *tempbuf;
+ static int tempbufsize;
+
+ retry:
+
+ tokstart = lexptr;
+ /* See if it is a special token of length 3. */
+ for (i = 0; i < sizeof tokentab3 / sizeof tokentab3[0]; i++)
+ if (STREQN (tokstart, tokentab3[i].operator, 3))
+ {
+ lexptr += 3;
+ yylval.opcode = tokentab3[i].opcode;
+ return tokentab3[i].token;
+ }
+
+ /* See if it is a special token of length 2. */
+ for (i = 0; i < sizeof tokentab2 / sizeof tokentab2[0]; i++)
+ if (STREQN (tokstart, tokentab2[i].operator, 2))
+ {
+ lexptr += 2;
+ yylval.opcode = tokentab2[i].opcode;
+ return tokentab2[i].token;
+ }
+
+ switch (c = *tokstart)
+ {
+ case 0:
+ return 0;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ lexptr++;
+ goto retry;
+
+ case '\'':
+ /* We either have a character constant ('0' or '\177' for example)
+ or we have a quoted symbol reference ('foo(int,int)' in C++
+ for example). */
+ lexptr++;
+ c = *lexptr++;
+ if (c == '\\')
+ c = parse_escape (&lexptr);
+
+ yylval.typed_val.val = c;
+ yylval.typed_val.type = builtin_type_char;
+
+ c = *lexptr++;
+ if (c != '\'')
+ {
+ namelen = skip_quoted (tokstart) - tokstart;
+ if (namelen > 2)
+ {
+ lexptr = tokstart + namelen;
+ if (lexptr[-1] != '\'')
+ error ("Unmatched single quote.");
+ namelen -= 2;
+ tokstart++;
+ goto tryname;
+ }
+ error ("Invalid character constant.");
+ }
+ return INT;
+
+ case '(':
+ paren_depth++;
+ lexptr++;
+ return c;
+
+ case ')':
+ if (paren_depth == 0)
+ return 0;
+ paren_depth--;
+ lexptr++;
+ return c;
+
+ case ',':
+ if (comma_terminates && paren_depth == 0)
+ return 0;
+ lexptr++;
+ return c;
+
+ case '.':
+ /* Might be a floating point number. */
+ if (lexptr[1] < '0' || lexptr[1] > '9')
+ goto symbol; /* Nope, must be a symbol. */
+ /* FALL THRU into number case. */
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ /* It's a number. */
+ int got_dot = 0, got_e = 0, toktype;
+ register char *p = tokstart;
+ int hex = input_radix > 10;
+
+ if (c == '0' && (p[1] == 'x' || p[1] == 'X'))
+ {
+ p += 2;
+ hex = 1;
+ }
+ else if (c == '0' && (p[1]=='t' || p[1]=='T' || p[1]=='d' || p[1]=='D'))
+ {
+ p += 2;
+ hex = 0;
+ }
+
+ for (;; ++p)
+ {
+ /* This test includes !hex because 'e' is a valid hex digit
+ and thus does not indicate a floating point number when
+ the radix is hex. */
+ if (!hex && !got_e && (*p == 'e' || *p == 'E'))
+ got_dot = got_e = 1;
+ /* This test does not include !hex, because a '.' always indicates
+ a decimal floating point number regardless of the radix. */
+ else if (!got_dot && *p == '.')
+ got_dot = 1;
+ else if (got_e && (p[-1] == 'e' || p[-1] == 'E')
+ && (*p == '-' || *p == '+'))
+ /* This is the sign of the exponent, not the end of the
+ number. */
+ continue;
+ /* We will take any letters or digits. parse_number will
+ complain if past the radix, or if L or U are not final. */
+ else if ((*p < '0' || *p > '9')
+ && ((*p < 'a' || *p > 'z')
+ && (*p < 'A' || *p > 'Z')))
+ break;
+ }
+ toktype = parse_number (tokstart, p - tokstart, got_dot|got_e, &yylval);
+ if (toktype == ERROR)
+ {
+ char *err_copy = (char *) alloca (p - tokstart + 1);
+
+ memcpy (err_copy, tokstart, p - tokstart);
+ err_copy[p - tokstart] = 0;
+ error ("Invalid number \"%s\".", err_copy);
+ }
+ lexptr = p;
+ return toktype;
+ }
+
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '%':
+ case '|':
+ case '&':
+ case '^':
+ case '~':
+ case '!':
+ case '@':
+ case '<':
+ case '>':
+ case '[':
+ case ']':
+ case '?':
+ case ':':
+ case '=':
+ case '{':
+ case '}':
+ symbol:
+ lexptr++;
+ return c;
+
+ case '"':
+
+ /* Build the gdb internal form of the input string in tempbuf,
+ translating any standard C escape forms seen. Note that the
+ buffer is null byte terminated *only* for the convenience of
+ debugging gdb itself and printing the buffer contents when
+ the buffer contains no embedded nulls. Gdb does not depend
+ upon the buffer being null byte terminated, it uses the length
+ string instead. This allows gdb to handle C strings (as well
+ as strings in other languages) with embedded null bytes */
+
+ tokptr = ++tokstart;
+ tempbufindex = 0;
+
+ do {
+ /* Grow the static temp buffer if necessary, including allocating
+ the first one on demand. */
+ if (tempbufindex + 1 >= tempbufsize)
+ {
+ tempbuf = (char *) xrealloc (tempbuf, tempbufsize += 64);
+ }
+ switch (*tokptr)
+ {
+ case '\0':
+ case '"':
+ /* Do nothing, loop will terminate. */
+ break;
+ case '\\':
+ tokptr++;
+ c = parse_escape (&tokptr);
+ if (c == -1)
+ {
+ continue;
+ }
+ tempbuf[tempbufindex++] = c;
+ break;
+ default:
+ tempbuf[tempbufindex++] = *tokptr++;
+ break;
+ }
+ } while ((*tokptr != '"') && (*tokptr != '\0'));
+ if (*tokptr++ != '"')
+ {
+ error ("Unterminated string in expression.");
+ }
+ tempbuf[tempbufindex] = '\0'; /* See note above */
+ yylval.sval.ptr = tempbuf;
+ yylval.sval.length = tempbufindex;
+ lexptr = tokptr;
+ return (STRING);
+ }
+
+ if (!(c == '_' || c == '$'
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
+ /* We must have come across a bad character (e.g. ';'). */
+ error ("Invalid character '%c' in expression.", c);
+
+ /* It's a name. See how long it is. */
+ namelen = 0;
+ for (c = tokstart[namelen];
+ (c == '_' || c == '$' || (c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
+ c = tokstart[++namelen])
+ ;
+
+ /* The token "if" terminates the expression and is NOT
+ removed from the input stream. */
+ if (namelen == 2 && tokstart[0] == 'i' && tokstart[1] == 'f')
+ {
+ return 0;
+ }
+
+ lexptr += namelen;
+
+ /* Handle the tokens $digits; also $ (short for $0) and $$ (short for $$1)
+ and $$digits (equivalent to $<-digits> if you could type that).
+ Make token type LAST, and put the number (the digits) in yylval. */
+
+ tryname:
+ if (*tokstart == '$')
+ {
+ register int negate = 0;
+ c = 1;
+ /* Double dollar means negate the number and add -1 as well.
+ Thus $$ alone means -1. */
+ if (namelen >= 2 && tokstart[1] == '$')
+ {
+ negate = 1;
+ c = 2;
+ }
+ if (c == namelen)
+ {
+ /* Just dollars (one or two) */
+ yylval.lval = - negate;
+ return LAST;
+ }
+ /* Is the rest of the token digits? */
+ for (; c < namelen; c++)
+ if (!(tokstart[c] >= '0' && tokstart[c] <= '9'))
+ break;
+ if (c == namelen)
+ {
+ yylval.lval = atoi (tokstart + 1 + negate);
+ if (negate)
+ yylval.lval = - yylval.lval;
+ return LAST;
+ }
+ }
+
+ /* Handle tokens that refer to machine registers:
+ $ followed by a register name. */
+
+ if (*tokstart == '$') {
+ for (c = 0; c < NUM_REGS; c++)
+ if (namelen - 1 == strlen (reg_names[c])
+ && STREQN (tokstart + 1, reg_names[c], namelen - 1))
+ {
+ yylval.lval = c;
+ return REGNAME;
+ }
+ for (c = 0; c < num_std_regs; c++)
+ if (namelen - 1 == strlen (std_regs[c].name)
+ && STREQN (tokstart + 1, std_regs[c].name, namelen - 1))
+ {
+ yylval.lval = std_regs[c].regnum;
+ return REGNAME;
+ }
+ }
+ /* Catch specific keywords. Should be done with a data structure. */
+ switch (namelen)
+ {
+ case 8:
+ if (STREQN (tokstart, "unsigned", 8))
+ return UNSIGNED;
+ if (current_language->la_language == language_cplus
+ && STREQN (tokstart, "template", 8))
+ return TEMPLATE;
+ if (STREQN (tokstart, "volatile", 8))
+ return VOLATILE_KEYWORD;
+ break;
+ case 6:
+ if (STREQN (tokstart, "struct", 6))
+ return STRUCT;
+ if (STREQN (tokstart, "signed", 6))
+ return SIGNED_KEYWORD;
+ if (STREQN (tokstart, "sizeof", 6))
+ return SIZEOF;
+ break;
+ case 5:
+ if (current_language->la_language == language_cplus
+ && STREQN (tokstart, "class", 5))
+ return CLASS;
+ if (STREQN (tokstart, "union", 5))
+ return UNION;
+ if (STREQN (tokstart, "short", 5))
+ return SHORT;
+ if (STREQN (tokstart, "const", 5))
+ return CONST_KEYWORD;
+ break;
+ case 4:
+ if (STREQN (tokstart, "enum", 4))
+ return ENUM;
+ if (STREQN (tokstart, "long", 4))
+ return LONG;
+ if (current_language->la_language == language_cplus
+ && STREQN (tokstart, "this", 4))
+ {
+ static const char this_name[] =
+ { CPLUS_MARKER, 't', 'h', 'i', 's', '\0' };
+
+ if (lookup_symbol (this_name, expression_context_block,
+ VAR_NAMESPACE, (int *) NULL,
+ (struct symtab **) NULL))
+ return THIS;
+ }
+ break;
+ case 3:
+ if (STREQN (tokstart, "int", 3))
+ return INT_KEYWORD;
+ break;
+ default:
+ break;
+ }
+
+ yylval.sval.ptr = tokstart;
+ yylval.sval.length = namelen;
+
+ /* Any other names starting in $ are debugger internal variables. */
+
+ if (*tokstart == '$')
+ {
+ yylval.ivar = lookup_internalvar (copy_name (yylval.sval) + 1);
+ return VARIABLE;
+ }
+
+ /* Use token-type BLOCKNAME for symbols that happen to be defined as
+ functions or symtabs. If this is not so, then ...
+ Use token-type TYPENAME for symbols that happen to be defined
+ currently as names of types; NAME for other symbols.
+ The caller is not constrained to care about the distinction. */
+ {
+ char *tmp = copy_name (yylval.sval);
+ struct symbol *sym;
+ int is_a_field_of_this = 0;
+ int hextype;
+
+ sym = lookup_symbol (tmp, expression_context_block,
+ VAR_NAMESPACE,
+ current_language->la_language == language_cplus
+ ? &is_a_field_of_this : (int *) NULL,
+ (struct symtab **) NULL);
+ if ((sym && SYMBOL_CLASS (sym) == LOC_BLOCK) ||
+ lookup_partial_symtab (tmp))
+ {
+ yylval.ssym.sym = sym;
+ yylval.ssym.is_a_field_of_this = is_a_field_of_this;
+ return BLOCKNAME;
+ }
+ if (sym && SYMBOL_CLASS (sym) == LOC_TYPEDEF)
+ {
+ char *p;
+ char *namestart;
+ struct symbol *best_sym;
+
+ /* Look ahead to detect nested types. This probably should be
+ done in the grammar, but trying seemed to introduce a lot
+ of shift/reduce and reduce/reduce conflicts. It's possible
+ that it could be done, though. Or perhaps a non-grammar, but
+ less ad hoc, approach would work well. */
+
+ /* Since we do not currently have any way of distinguishing
+ a nested type from a non-nested one (the stabs don't tell
+ us whether a type is nested), we just ignore the
+ containing type. */
+
+ p = lexptr;
+ best_sym = sym;
+ while (1)
+ {
+ /* Skip whitespace. */
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ ++p;
+ if (*p == ':' && p[1] == ':')
+ {
+ /* Skip the `::'. */
+ p += 2;
+ /* Skip whitespace. */
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ ++p;
+ namestart = p;
+ while (*p == '_' || *p == '$' || (*p >= '0' && *p <= '9')
+ || (*p >= 'a' && *p <= 'z')
+ || (*p >= 'A' && *p <= 'Z'))
+ ++p;
+ if (p != namestart)
+ {
+ struct symbol *cur_sym;
+ /* As big as the whole rest of the expression, which is
+ at least big enough. */
+ char *tmp = alloca (strlen (namestart));
+
+ memcpy (tmp, namestart, p - namestart);
+ tmp[p - namestart] = '\0';
+ cur_sym = lookup_symbol (tmp, expression_context_block,
+ VAR_NAMESPACE, (int *) NULL,
+ (struct symtab **) NULL);
+ if (cur_sym)
+ {
+ if (SYMBOL_CLASS (cur_sym) == LOC_TYPEDEF)
+ {
+ best_sym = cur_sym;
+ lexptr = p;
+ }
+ else
+ break;
+ }
+ else
+ break;
+ }
+ else
+ break;
+ }
+ else
+ break;
+ }
+
+ yylval.tsym.type = SYMBOL_TYPE (best_sym);
+ return TYPENAME;
+ }
+ if ((yylval.tsym.type = lookup_primitive_typename (tmp)) != 0)
+ return TYPENAME;
+
+ /* Input names that aren't symbols but ARE valid hex numbers,
+ when the input radix permits them, can be names or numbers
+ depending on the parse. Note we support radixes > 16 here. */
+ if (!sym &&
+ ((tokstart[0] >= 'a' && tokstart[0] < 'a' + input_radix - 10) ||
+ (tokstart[0] >= 'A' && tokstart[0] < 'A' + input_radix - 10)))
+ {
+ YYSTYPE newlval; /* Its value is ignored. */
+ hextype = parse_number (tokstart, namelen, 0, &newlval);
+ if (hextype == INT)
+ {
+ yylval.ssym.sym = sym;
+ yylval.ssym.is_a_field_of_this = is_a_field_of_this;
+ return NAME_OR_INT;
+ }
+ }
+
+ /* Any other kind of symbol */
+ yylval.ssym.sym = sym;
+ yylval.ssym.is_a_field_of_this = is_a_field_of_this;
+ return NAME;
+ }
+}
+
+void
+yyerror (msg)
+ char *msg;
+{
+ error (msg ? msg : "Invalid syntax in expression.");
+}
+#line 1706 "y.tab.c"
+#define YYABORT goto yyabort
+#define YYREJECT goto yyabort
+#define YYACCEPT goto yyaccept
+#define YYERROR goto yyerrlab
+int
+yyparse()
+{
+ register int yym, yyn, yystate;
+#if YYDEBUG
+ register char *yys;
+ extern char *getenv();
+
+ if (yys = getenv("YYDEBUG"))
+ {
+ yyn = *yys;
+ if (yyn >= '0' && yyn <= '9')
+ yydebug = yyn - '0';
+ }
+#endif
+
+ yynerrs = 0;
+ yyerrflag = 0;
+ yychar = (-1);
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+ *yyssp = yystate = 0;
+
+yyloop:
+ if (yyn = yydefred[yystate]) goto yyreduce;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ }
+ if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, shifting to state %d\n",
+ YYPREFIX, yystate, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ yychar = (-1);
+ if (yyerrflag > 0) --yyerrflag;
+ goto yyloop;
+ }
+ if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+ yyn = yytable[yyn];
+ goto yyreduce;
+ }
+ if (yyerrflag) goto yyinrecovery;
+#ifdef lint
+ goto yynewerror;
+#endif
+yynewerror:
+ yyerror("syntax error");
+#ifdef lint
+ goto yyerrlab;
+#endif
+yyerrlab:
+ ++yynerrs;
+yyinrecovery:
+ if (yyerrflag < 3)
+ {
+ yyerrflag = 3;
+ for (;;)
+ {
+ if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, error recovery shifting\
+ to state %d\n", YYPREFIX, *yyssp, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ goto yyloop;
+ }
+ else
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: error recovery discarding state %d\n",
+ YYPREFIX, *yyssp);
+#endif
+ if (yyssp <= yyss) goto yyabort;
+ --yyssp;
+ --yyvsp;
+ }
+ }
+ }
+ else
+ {
+ if (yychar == 0) goto yyabort;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, error recovery discards token %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ yychar = (-1);
+ goto yyloop;
+ }
+yyreduce:
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, reducing by rule %d (%s)\n",
+ YYPREFIX, yystate, yyn, yyrule[yyn]);
+#endif
+ yym = yylen[yyn];
+ yyval = yyvsp[1-yym];
+ switch (yyn)
+ {
+case 3:
+#line 211 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode(OP_TYPE);
+ write_exp_elt_type(yyvsp[0].tval);
+ write_exp_elt_opcode(OP_TYPE);}
+break;
+case 5:
+#line 219 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_COMMA); }
+break;
+case 6:
+#line 224 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (UNOP_IND); }
+break;
+case 7:
+#line 227 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (UNOP_ADDR); }
+break;
+case 8:
+#line 230 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (UNOP_NEG); }
+break;
+case 9:
+#line 234 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (UNOP_LOGICAL_NOT); }
+break;
+case 10:
+#line 238 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (UNOP_COMPLEMENT); }
+break;
+case 11:
+#line 242 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (UNOP_PREINCREMENT); }
+break;
+case 12:
+#line 246 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (UNOP_PREDECREMENT); }
+break;
+case 13:
+#line 250 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (UNOP_POSTINCREMENT); }
+break;
+case 14:
+#line 254 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (UNOP_POSTDECREMENT); }
+break;
+case 15:
+#line 258 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (UNOP_SIZEOF); }
+break;
+case 16:
+#line 262 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (STRUCTOP_PTR);
+ write_exp_string (yyvsp[0].sval);
+ write_exp_elt_opcode (STRUCTOP_PTR); }
+break;
+case 17:
+#line 268 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ /* exp->type::name becomes exp->*(&type::name) */
+ /* Note: this doesn't work if name is a
+ static member! FIXME */
+ write_exp_elt_opcode (UNOP_ADDR);
+ write_exp_elt_opcode (STRUCTOP_MPTR); }
+break;
+case 18:
+#line 275 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (STRUCTOP_MPTR); }
+break;
+case 19:
+#line 279 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (STRUCTOP_STRUCT);
+ write_exp_string (yyvsp[0].sval);
+ write_exp_elt_opcode (STRUCTOP_STRUCT); }
+break;
+case 20:
+#line 285 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ /* exp.type::name becomes exp.*(&type::name) */
+ /* Note: this doesn't work if name is a
+ static member! FIXME */
+ write_exp_elt_opcode (UNOP_ADDR);
+ write_exp_elt_opcode (STRUCTOP_MEMBER); }
+break;
+case 21:
+#line 293 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (STRUCTOP_MEMBER); }
+break;
+case 22:
+#line 297 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_SUBSCRIPT); }
+break;
+case 23:
+#line 303 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ start_arglist (); }
+break;
+case 24:
+#line 305 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (OP_FUNCALL);
+ write_exp_elt_longcst ((LONGEST) end_arglist ());
+ write_exp_elt_opcode (OP_FUNCALL); }
+break;
+case 25:
+#line 311 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ start_arglist (); }
+break;
+case 27:
+#line 318 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ arglist_len = 1; }
+break;
+case 28:
+#line 322 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ arglist_len++; }
+break;
+case 29:
+#line 326 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.lval = end_arglist () - 1; }
+break;
+case 30:
+#line 329 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (OP_ARRAY);
+ write_exp_elt_longcst ((LONGEST) 0);
+ write_exp_elt_longcst ((LONGEST) yyvsp[0].lval);
+ write_exp_elt_opcode (OP_ARRAY); }
+break;
+case 31:
+#line 336 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (UNOP_MEMVAL);
+ write_exp_elt_type (yyvsp[-2].tval);
+ write_exp_elt_opcode (UNOP_MEMVAL); }
+break;
+case 32:
+#line 342 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (UNOP_CAST);
+ write_exp_elt_type (yyvsp[-2].tval);
+ write_exp_elt_opcode (UNOP_CAST); }
+break;
+case 33:
+#line 348 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ }
+break;
+case 34:
+#line 354 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_REPEAT); }
+break;
+case 35:
+#line 358 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_MUL); }
+break;
+case 36:
+#line 362 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_DIV); }
+break;
+case 37:
+#line 366 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_REM); }
+break;
+case 38:
+#line 370 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_ADD); }
+break;
+case 39:
+#line 374 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_SUB); }
+break;
+case 40:
+#line 378 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_LSH); }
+break;
+case 41:
+#line 382 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_RSH); }
+break;
+case 42:
+#line 386 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_EQUAL); }
+break;
+case 43:
+#line 390 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_NOTEQUAL); }
+break;
+case 44:
+#line 394 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_LEQ); }
+break;
+case 45:
+#line 398 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_GEQ); }
+break;
+case 46:
+#line 402 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_LESS); }
+break;
+case 47:
+#line 406 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_GTR); }
+break;
+case 48:
+#line 410 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_BITWISE_AND); }
+break;
+case 49:
+#line 414 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_BITWISE_XOR); }
+break;
+case 50:
+#line 418 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_BITWISE_IOR); }
+break;
+case 51:
+#line 422 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_LOGICAL_AND); }
+break;
+case 52:
+#line 426 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_LOGICAL_OR); }
+break;
+case 53:
+#line 430 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (TERNOP_COND); }
+break;
+case 54:
+#line 434 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_ASSIGN); }
+break;
+case 55:
+#line 438 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (BINOP_ASSIGN_MODIFY);
+ write_exp_elt_opcode (yyvsp[-1].opcode);
+ write_exp_elt_opcode (BINOP_ASSIGN_MODIFY); }
+break;
+case 56:
+#line 444 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (yyvsp[0].typed_val.type);
+ write_exp_elt_longcst ((LONGEST)(yyvsp[0].typed_val.val));
+ write_exp_elt_opcode (OP_LONG); }
+break;
+case 57:
+#line 451 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ YYSTYPE val;
+ parse_number (yyvsp[0].ssym.stoken.ptr, yyvsp[0].ssym.stoken.length, 0, &val);
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (val.typed_val.type);
+ write_exp_elt_longcst ((LONGEST)val.typed_val.val);
+ write_exp_elt_opcode (OP_LONG);
+ }
+break;
+case 58:
+#line 462 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (OP_DOUBLE);
+ write_exp_elt_type (builtin_type_double);
+ write_exp_elt_dblcst (yyvsp[0].dval);
+ write_exp_elt_opcode (OP_DOUBLE); }
+break;
+case 60:
+#line 472 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (OP_LAST);
+ write_exp_elt_longcst ((LONGEST) yyvsp[0].lval);
+ write_exp_elt_opcode (OP_LAST); }
+break;
+case 61:
+#line 478 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (OP_REGISTER);
+ write_exp_elt_longcst ((LONGEST) yyvsp[0].lval);
+ write_exp_elt_opcode (OP_REGISTER); }
+break;
+case 62:
+#line 484 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (OP_INTERNALVAR);
+ write_exp_elt_intern (yyvsp[0].ivar);
+ write_exp_elt_opcode (OP_INTERNALVAR); }
+break;
+case 63:
+#line 490 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_int);
+ write_exp_elt_longcst ((LONGEST) TYPE_LENGTH (yyvsp[-1].tval));
+ write_exp_elt_opcode (OP_LONG); }
+break;
+case 64:
+#line 497 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ /* C strings are converted into array constants with
+ an explicit null byte added at the end. Thus
+ the array upper bound is the string length.
+ There is no such thing in C as a completely empty
+ string. */
+ char *sp = yyvsp[0].sval.ptr; int count = yyvsp[0].sval.length;
+ while (count-- > 0)
+ {
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_char);
+ write_exp_elt_longcst ((LONGEST)(*sp++));
+ write_exp_elt_opcode (OP_LONG);
+ }
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_char);
+ write_exp_elt_longcst ((LONGEST)'\0');
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_opcode (OP_ARRAY);
+ write_exp_elt_longcst ((LONGEST) 0);
+ write_exp_elt_longcst ((LONGEST) (yyvsp[0].sval.length));
+ write_exp_elt_opcode (OP_ARRAY); }
+break;
+case 65:
+#line 522 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ write_exp_elt_opcode (OP_THIS);
+ write_exp_elt_opcode (OP_THIS); }
+break;
+case 66:
+#line 529 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{
+ if (yyvsp[0].ssym.sym != 0)
+ yyval.bval = SYMBOL_BLOCK_VALUE (yyvsp[0].ssym.sym);
+ else
+ {
+ struct symtab *tem =
+ lookup_symtab (copy_name (yyvsp[0].ssym.stoken));
+ if (tem)
+ yyval.bval = BLOCKVECTOR_BLOCK
+ (BLOCKVECTOR (tem), STATIC_BLOCK);
+ else
+ error ("No file or function \"%s\".",
+ copy_name (yyvsp[0].ssym.stoken));
+ }
+ }
+break;
+case 67:
+#line 547 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ struct symbol *tem
+ = lookup_symbol (copy_name (yyvsp[0].sval), yyvsp[-2].bval,
+ VAR_NAMESPACE, (int *) NULL,
+ (struct symtab **) NULL);
+ if (!tem || SYMBOL_CLASS (tem) != LOC_BLOCK)
+ error ("No function \"%s\" in specified context.",
+ copy_name (yyvsp[0].sval));
+ yyval.bval = SYMBOL_BLOCK_VALUE (tem); }
+break;
+case 68:
+#line 558 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ struct symbol *sym;
+ sym = lookup_symbol (copy_name (yyvsp[0].sval), yyvsp[-2].bval,
+ VAR_NAMESPACE, (int *) NULL,
+ (struct symtab **) NULL);
+ if (sym == 0)
+ error ("No symbol \"%s\" in specified context.",
+ copy_name (yyvsp[0].sval));
+
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ /* block_found is set by lookup_symbol. */
+ write_exp_elt_block (block_found);
+ write_exp_elt_sym (sym);
+ write_exp_elt_opcode (OP_VAR_VALUE); }
+break;
+case 69:
+#line 574 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{
+ struct type *type = yyvsp[-2].tval;
+ if (TYPE_CODE (type) != TYPE_CODE_STRUCT
+ && TYPE_CODE (type) != TYPE_CODE_UNION)
+ error ("`%s' is not defined as an aggregate type.",
+ TYPE_NAME (type));
+
+ write_exp_elt_opcode (OP_SCOPE);
+ write_exp_elt_type (type);
+ write_exp_string (yyvsp[0].sval);
+ write_exp_elt_opcode (OP_SCOPE);
+ }
+break;
+case 70:
+#line 587 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{
+ struct type *type = yyvsp[-3].tval;
+ struct stoken tmp_token;
+ if (TYPE_CODE (type) != TYPE_CODE_STRUCT
+ && TYPE_CODE (type) != TYPE_CODE_UNION)
+ error ("`%s' is not defined as an aggregate type.",
+ TYPE_NAME (type));
+
+ if (!STREQ (type_name_no_tag (type), yyvsp[0].sval.ptr))
+ error ("invalid destructor `%s::~%s'",
+ type_name_no_tag (type), yyvsp[0].sval.ptr);
+
+ tmp_token.ptr = (char*) alloca (yyvsp[0].sval.length + 2);
+ tmp_token.length = yyvsp[0].sval.length + 1;
+ tmp_token.ptr[0] = '~';
+ memcpy (tmp_token.ptr+1, yyvsp[0].sval.ptr, yyvsp[0].sval.length);
+ tmp_token.ptr[tmp_token.length] = 0;
+ write_exp_elt_opcode (OP_SCOPE);
+ write_exp_elt_type (type);
+ write_exp_string (tmp_token);
+ write_exp_elt_opcode (OP_SCOPE);
+ }
+break;
+case 72:
+#line 613 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{
+ char *name = copy_name (yyvsp[0].sval);
+ struct symbol *sym;
+ struct minimal_symbol *msymbol;
+
+ sym =
+ lookup_symbol (name, (const struct block *) NULL,
+ VAR_NAMESPACE, (int *) NULL,
+ (struct symtab **) NULL);
+ if (sym)
+ {
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ write_exp_elt_block (NULL);
+ write_exp_elt_sym (sym);
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ break;
+ }
+
+ msymbol = lookup_minimal_symbol (name,
+ (struct objfile *) NULL);
+ if (msymbol != NULL)
+ {
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_long);
+ write_exp_elt_longcst ((LONGEST) SYMBOL_VALUE_ADDRESS (msymbol));
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_opcode (UNOP_MEMVAL);
+ if (msymbol -> type == mst_data ||
+ msymbol -> type == mst_bss)
+ write_exp_elt_type (builtin_type_int);
+ else if (msymbol -> type == mst_text)
+ write_exp_elt_type (lookup_function_type (builtin_type_int));
+ else
+ write_exp_elt_type (builtin_type_char);
+ write_exp_elt_opcode (UNOP_MEMVAL);
+ }
+ else
+ if (!have_full_symbols () && !have_partial_symbols ())
+ error ("No symbol table is loaded. Use the \"file\" command.");
+ else
+ error ("No symbol \"%s\" in current context.", name);
+ }
+break;
+case 73:
+#line 658 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ struct symbol *sym = yyvsp[0].ssym.sym;
+
+ if (sym)
+ {
+ if (symbol_read_needs_frame (sym))
+ {
+ if (innermost_block == 0 ||
+ contained_in (block_found,
+ innermost_block))
+ innermost_block = block_found;
+ }
+
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ /* We want to use the selected frame, not
+ another more inner frame which happens to
+ be in the same block. */
+ write_exp_elt_block (NULL);
+ write_exp_elt_sym (sym);
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ }
+ else if (yyvsp[0].ssym.is_a_field_of_this)
+ {
+ /* C++: it hangs off of `this'. Must
+ not inadvertently convert from a method call
+ to data ref. */
+ if (innermost_block == 0 ||
+ contained_in (block_found, innermost_block))
+ innermost_block = block_found;
+ write_exp_elt_opcode (OP_THIS);
+ write_exp_elt_opcode (OP_THIS);
+ write_exp_elt_opcode (STRUCTOP_PTR);
+ write_exp_string (yyvsp[0].ssym.stoken);
+ write_exp_elt_opcode (STRUCTOP_PTR);
+ }
+ else
+ {
+ struct minimal_symbol *msymbol;
+ register char *arg = copy_name (yyvsp[0].ssym.stoken);
+
+ msymbol = lookup_minimal_symbol (arg,
+ (struct objfile *) NULL);
+ if (msymbol != NULL)
+ {
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_long);
+ write_exp_elt_longcst ((LONGEST) SYMBOL_VALUE_ADDRESS (msymbol));
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_opcode (UNOP_MEMVAL);
+ if (msymbol -> type == mst_data ||
+ msymbol -> type == mst_bss)
+ write_exp_elt_type (builtin_type_int);
+ else if (msymbol -> type == mst_text)
+ write_exp_elt_type (lookup_function_type (builtin_type_int));
+ else
+ write_exp_elt_type (builtin_type_char);
+ write_exp_elt_opcode (UNOP_MEMVAL);
+ }
+ else if (!have_full_symbols () && !have_partial_symbols ())
+ error ("No symbol table is loaded. Use the \"file\" command.");
+ else
+ error ("No symbol \"%s\" in current context.",
+ copy_name (yyvsp[0].ssym.stoken));
+ }
+ }
+break;
+case 77:
+#line 737 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = follow_types (yyvsp[-1].tval); }
+break;
+case 78:
+#line 739 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = follow_types (yyvsp[-2].tval); }
+break;
+case 79:
+#line 741 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = follow_types (yyvsp[-2].tval); }
+break;
+case 80:
+#line 745 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ push_type (tp_pointer); yyval.voidval = 0; }
+break;
+case 81:
+#line 747 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ push_type (tp_pointer); yyval.voidval = yyvsp[0].voidval; }
+break;
+case 82:
+#line 749 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ push_type (tp_reference); yyval.voidval = 0; }
+break;
+case 83:
+#line 751 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ push_type (tp_reference); yyval.voidval = yyvsp[0].voidval; }
+break;
+case 85:
+#line 756 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.voidval = yyvsp[-1].voidval; }
+break;
+case 86:
+#line 758 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{
+ push_type_int (yyvsp[0].lval);
+ push_type (tp_array);
+ }
+break;
+case 87:
+#line 763 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{
+ push_type_int (yyvsp[0].lval);
+ push_type (tp_array);
+ yyval.voidval = 0;
+ }
+break;
+case 88:
+#line 773 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ push_type (tp_function); }
+break;
+case 89:
+#line 775 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ push_type (tp_function); }
+break;
+case 90:
+#line 779 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.lval = -1; }
+break;
+case 91:
+#line 781 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.lval = yyvsp[-1].typed_val.val; }
+break;
+case 92:
+#line 785 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 93:
+#line 787 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ free ((PTR)yyvsp[-1].tvec); yyval.voidval = 0; }
+break;
+case 95:
+#line 794 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = lookup_member_type (builtin_type_int, yyvsp[-2].tval); }
+break;
+case 96:
+#line 796 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = lookup_member_type (yyvsp[-5].tval, yyvsp[-3].tval); }
+break;
+case 97:
+#line 798 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = lookup_member_type
+ (lookup_function_type (yyvsp[-7].tval), yyvsp[-5].tval); }
+break;
+case 98:
+#line 801 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = lookup_member_type
+ (lookup_function_type (yyvsp[-8].tval), yyvsp[-6].tval);
+ free ((PTR)yyvsp[-1].tvec); }
+break;
+case 99:
+#line 808 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = yyvsp[0].tsym.type; }
+break;
+case 100:
+#line 810 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = builtin_type_int; }
+break;
+case 101:
+#line 812 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = builtin_type_long; }
+break;
+case 102:
+#line 814 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = builtin_type_short; }
+break;
+case 103:
+#line 816 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = builtin_type_long; }
+break;
+case 104:
+#line 818 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = builtin_type_unsigned_long; }
+break;
+case 105:
+#line 820 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = builtin_type_long_long; }
+break;
+case 106:
+#line 822 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = builtin_type_long_long; }
+break;
+case 107:
+#line 824 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = builtin_type_unsigned_long_long; }
+break;
+case 108:
+#line 826 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = builtin_type_unsigned_long_long; }
+break;
+case 109:
+#line 828 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = builtin_type_short; }
+break;
+case 110:
+#line 830 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = builtin_type_unsigned_short; }
+break;
+case 111:
+#line 832 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = lookup_struct (copy_name (yyvsp[0].sval),
+ expression_context_block); }
+break;
+case 112:
+#line 835 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = lookup_struct (copy_name (yyvsp[0].sval),
+ expression_context_block); }
+break;
+case 113:
+#line 838 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = lookup_union (copy_name (yyvsp[0].sval),
+ expression_context_block); }
+break;
+case 114:
+#line 841 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = lookup_enum (copy_name (yyvsp[0].sval),
+ expression_context_block); }
+break;
+case 115:
+#line 844 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = lookup_unsigned_typename (TYPE_NAME(yyvsp[0].tsym.type)); }
+break;
+case 116:
+#line 846 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = builtin_type_unsigned_int; }
+break;
+case 117:
+#line 848 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = lookup_signed_typename (TYPE_NAME(yyvsp[0].tsym.type)); }
+break;
+case 118:
+#line 850 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = builtin_type_int; }
+break;
+case 119:
+#line 852 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = lookup_template_type(copy_name(yyvsp[-3].sval), yyvsp[-1].tval,
+ expression_context_block);
+ }
+break;
+case 120:
+#line 858 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = yyvsp[0].tval; }
+break;
+case 121:
+#line 859 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tval = yyvsp[0].tval; }
+break;
+case 123:
+#line 864 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{
+ yyval.tsym.stoken.ptr = "int";
+ yyval.tsym.stoken.length = 3;
+ yyval.tsym.type = builtin_type_int;
+ }
+break;
+case 124:
+#line 870 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{
+ yyval.tsym.stoken.ptr = "long";
+ yyval.tsym.stoken.length = 4;
+ yyval.tsym.type = builtin_type_long;
+ }
+break;
+case 125:
+#line 876 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{
+ yyval.tsym.stoken.ptr = "short";
+ yyval.tsym.stoken.length = 5;
+ yyval.tsym.type = builtin_type_short;
+ }
+break;
+case 126:
+#line 885 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.tvec = (struct type **) xmalloc (sizeof (struct type *) * 2);
+ yyval.ivec[0] = 1; /* Number of types in vector */
+ yyval.tvec[1] = yyvsp[0].tval;
+ }
+break;
+case 127:
+#line 890 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ int len = sizeof (struct type *) * (++(yyvsp[-2].ivec[0]) + 1);
+ yyval.tvec = (struct type **) xrealloc ((char *) yyvsp[-2].tvec, len);
+ yyval.tvec[yyval.ivec[0]] = yyvsp[0].tval;
+ }
+break;
+case 128:
+#line 896 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.sval = yyvsp[0].ssym.stoken; }
+break;
+case 129:
+#line 897 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.sval = yyvsp[0].ssym.stoken; }
+break;
+case 130:
+#line 898 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.sval = yyvsp[0].tsym.stoken; }
+break;
+case 131:
+#line 899 "/usr/src/gnu/usr.bin/gdb/gdb/c-exp.y"
+{ yyval.sval = yyvsp[0].ssym.stoken; }
+break;
+#line 2593 "y.tab.c"
+ }
+ yyssp -= yym;
+ yystate = *yyssp;
+ yyvsp -= yym;
+ yym = yylhs[yyn];
+ if (yystate == 0 && yym == 0)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state 0 to\
+ state %d\n", YYPREFIX, YYFINAL);
+#endif
+ yystate = YYFINAL;
+ *++yyssp = YYFINAL;
+ *++yyvsp = yyval;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, YYFINAL, yychar, yys);
+ }
+#endif
+ }
+ if (yychar == 0) goto yyaccept;
+ goto yyloop;
+ }
+ if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yystate)
+ yystate = yytable[yyn];
+ else
+ yystate = yydgoto[yym];
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state %d \
+to state %d\n", YYPREFIX, *yyssp, yystate);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate;
+ *++yyvsp = yyval;
+ goto yyloop;
+yyoverflow:
+ yyerror("yacc stack overflow");
+yyabort:
+ return (1);
+yyaccept:
+ return (0);
+}
diff --git a/gnu/usr.bin/gdb/gdb/c-exp.y b/gnu/usr.bin/gdb/gdb/c-exp.y
new file mode 100644
index 0000000..0e7d39a
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/c-exp.y
@@ -0,0 +1,1601 @@
+/* YACC parser for C expressions, for GDB.
+ Copyright (C) 1986, 1989, 1990, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Parse a C expression from text in a string,
+ and return the result as a struct expression pointer.
+ That structure contains arithmetic operations in reverse polish,
+ with constants represented by operations that are followed by special data.
+ See expression.h for the details of the format.
+ What is important here is that it can be built up sequentially
+ during the process of parsing; the lower levels of the tree always
+ come first in the result.
+
+ Note that malloc's and realloc's in this file are transformed to
+ xmalloc and xrealloc respectively by the same sed command in the
+ makefile that remaps any other malloc/realloc inserted by the parser
+ generator. Doing this with #defines and trying to control the interaction
+ with include files (<malloc.h> and <stdlib.h> for example) just became
+ too messy, particularly when such includes can be inserted at random
+ times by the parser generator. */
+
+%{
+
+#include "defs.h"
+#include "expression.h"
+#include "parser-defs.h"
+#include "value.h"
+#include "language.h"
+#include "c-lang.h"
+
+/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
+ as well as gratuitiously global symbol names, so we can have multiple
+ yacc generated parsers in gdb. Note that these are only the variables
+ produced by yacc. If other parser generators (bison, byacc, etc) produce
+ additional global names that conflict at link time, then those parser
+ generators need to be fixed instead of adding those names to this list. */
+
+#define yymaxdepth c_maxdepth
+#define yyparse c_parse
+#define yylex c_lex
+#define yyerror c_error
+#define yylval c_lval
+#define yychar c_char
+#define yydebug c_debug
+#define yypact c_pact
+#define yyr1 c_r1
+#define yyr2 c_r2
+#define yydef c_def
+#define yychk c_chk
+#define yypgo c_pgo
+#define yyact c_act
+#define yyexca c_exca
+#define yyerrflag c_errflag
+#define yynerrs c_nerrs
+#define yyps c_ps
+#define yypv c_pv
+#define yys c_s
+#define yy_yys c_yys
+#define yystate c_state
+#define yytmp c_tmp
+#define yyv c_v
+#define yy_yyv c_yyv
+#define yyval c_val
+#define yylloc c_lloc
+#define yyreds c_reds /* With YYDEBUG defined */
+#define yytoks c_toks /* With YYDEBUG defined */
+
+#ifndef YYDEBUG
+#define YYDEBUG 0 /* Default to no yydebug support */
+#endif
+
+int
+yyparse PARAMS ((void));
+
+static int
+yylex PARAMS ((void));
+
+void
+yyerror PARAMS ((char *));
+
+%}
+
+/* Although the yacc "value" of an expression is not used,
+ since the result is stored in the structure being created,
+ other node types do have values. */
+
+%union
+ {
+ LONGEST lval;
+ struct {
+ LONGEST val;
+ struct type *type;
+ } typed_val;
+ double dval;
+ struct symbol *sym;
+ struct type *tval;
+ struct stoken sval;
+ struct ttype tsym;
+ struct symtoken ssym;
+ int voidval;
+ struct block *bval;
+ enum exp_opcode opcode;
+ struct internalvar *ivar;
+
+ struct type **tvec;
+ int *ivec;
+ }
+
+%{
+/* YYSTYPE gets defined by %union */
+static int
+parse_number PARAMS ((char *, int, int, YYSTYPE *));
+%}
+
+%type <voidval> exp exp1 type_exp start variable qualified_name lcurly
+%type <lval> rcurly
+%type <tval> type typebase
+%type <tvec> nonempty_typelist
+/* %type <bval> block */
+
+/* Fancy type parsing. */
+%type <voidval> func_mod direct_abs_decl abs_decl
+%type <tval> ptype
+%type <lval> array_mod
+
+%token <typed_val> INT
+%token <dval> FLOAT
+
+/* Both NAME and TYPENAME tokens represent symbols in the input,
+ and both convey their data as strings.
+ But a TYPENAME is a string that happens to be defined as a typedef
+ or builtin type name (such as int or char)
+ and a NAME is any other symbol.
+ Contexts where this distinction is not important can use the
+ nonterminal "name", which matches either NAME or TYPENAME. */
+
+%token <sval> STRING
+%token <ssym> NAME /* BLOCKNAME defined below to give it higher precedence. */
+%token <tsym> TYPENAME
+%type <sval> name
+%type <ssym> name_not_typename
+%type <tsym> typename
+
+/* A NAME_OR_INT is a symbol which is not known in the symbol table,
+ but which would parse as a valid number in the current input radix.
+ E.g. "c" when input_radix==16. Depending on the parse, it will be
+ turned into a name or into a number. */
+
+%token <ssym> NAME_OR_INT
+
+%token STRUCT CLASS UNION ENUM SIZEOF UNSIGNED COLONCOLON
+%token TEMPLATE
+%token ERROR
+
+/* Special type cases, put in to allow the parser to distinguish different
+ legal basetypes. */
+%token SIGNED_KEYWORD LONG SHORT INT_KEYWORD CONST_KEYWORD VOLATILE_KEYWORD
+%token <lval> LAST REGNAME
+
+%token <ivar> VARIABLE
+
+%token <opcode> ASSIGN_MODIFY
+
+/* C++ */
+%token THIS
+
+%left ','
+%left ABOVE_COMMA
+%right '=' ASSIGN_MODIFY
+%right '?'
+%left OROR
+%left ANDAND
+%left '|'
+%left '^'
+%left '&'
+%left EQUAL NOTEQUAL
+%left '<' '>' LEQ GEQ
+%left LSH RSH
+%left '@'
+%left '+' '-'
+%left '*' '/' '%'
+%right UNARY INCREMENT DECREMENT
+%right ARROW '.' '[' '('
+%token <ssym> BLOCKNAME
+%type <bval> block
+%left COLONCOLON
+
+
+%%
+
+start : exp1
+ | type_exp
+ ;
+
+type_exp: type
+ { write_exp_elt_opcode(OP_TYPE);
+ write_exp_elt_type($1);
+ write_exp_elt_opcode(OP_TYPE);}
+ ;
+
+/* Expressions, including the comma operator. */
+exp1 : exp
+ | exp1 ',' exp
+ { write_exp_elt_opcode (BINOP_COMMA); }
+ ;
+
+/* Expressions, not including the comma operator. */
+exp : '*' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_IND); }
+
+exp : '&' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_ADDR); }
+
+exp : '-' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_NEG); }
+ ;
+
+exp : '!' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_LOGICAL_NOT); }
+ ;
+
+exp : '~' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_COMPLEMENT); }
+ ;
+
+exp : INCREMENT exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_PREINCREMENT); }
+ ;
+
+exp : DECREMENT exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_PREDECREMENT); }
+ ;
+
+exp : exp INCREMENT %prec UNARY
+ { write_exp_elt_opcode (UNOP_POSTINCREMENT); }
+ ;
+
+exp : exp DECREMENT %prec UNARY
+ { write_exp_elt_opcode (UNOP_POSTDECREMENT); }
+ ;
+
+exp : SIZEOF exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_SIZEOF); }
+ ;
+
+exp : exp ARROW name
+ { write_exp_elt_opcode (STRUCTOP_PTR);
+ write_exp_string ($3);
+ write_exp_elt_opcode (STRUCTOP_PTR); }
+ ;
+
+exp : exp ARROW qualified_name
+ { /* exp->type::name becomes exp->*(&type::name) */
+ /* Note: this doesn't work if name is a
+ static member! FIXME */
+ write_exp_elt_opcode (UNOP_ADDR);
+ write_exp_elt_opcode (STRUCTOP_MPTR); }
+ ;
+exp : exp ARROW '*' exp
+ { write_exp_elt_opcode (STRUCTOP_MPTR); }
+ ;
+
+exp : exp '.' name
+ { write_exp_elt_opcode (STRUCTOP_STRUCT);
+ write_exp_string ($3);
+ write_exp_elt_opcode (STRUCTOP_STRUCT); }
+ ;
+
+exp : exp '.' qualified_name
+ { /* exp.type::name becomes exp.*(&type::name) */
+ /* Note: this doesn't work if name is a
+ static member! FIXME */
+ write_exp_elt_opcode (UNOP_ADDR);
+ write_exp_elt_opcode (STRUCTOP_MEMBER); }
+ ;
+
+exp : exp '.' '*' exp
+ { write_exp_elt_opcode (STRUCTOP_MEMBER); }
+ ;
+
+exp : exp '[' exp1 ']'
+ { write_exp_elt_opcode (BINOP_SUBSCRIPT); }
+ ;
+
+exp : exp '('
+ /* This is to save the value of arglist_len
+ being accumulated by an outer function call. */
+ { start_arglist (); }
+ arglist ')' %prec ARROW
+ { write_exp_elt_opcode (OP_FUNCALL);
+ write_exp_elt_longcst ((LONGEST) end_arglist ());
+ write_exp_elt_opcode (OP_FUNCALL); }
+ ;
+
+lcurly : '{'
+ { start_arglist (); }
+ ;
+
+arglist :
+ ;
+
+arglist : exp
+ { arglist_len = 1; }
+ ;
+
+arglist : arglist ',' exp %prec ABOVE_COMMA
+ { arglist_len++; }
+ ;
+
+rcurly : '}'
+ { $$ = end_arglist () - 1; }
+ ;
+exp : lcurly arglist rcurly %prec ARROW
+ { write_exp_elt_opcode (OP_ARRAY);
+ write_exp_elt_longcst ((LONGEST) 0);
+ write_exp_elt_longcst ((LONGEST) $3);
+ write_exp_elt_opcode (OP_ARRAY); }
+ ;
+
+exp : lcurly type rcurly exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_MEMVAL);
+ write_exp_elt_type ($2);
+ write_exp_elt_opcode (UNOP_MEMVAL); }
+ ;
+
+exp : '(' type ')' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_CAST);
+ write_exp_elt_type ($2);
+ write_exp_elt_opcode (UNOP_CAST); }
+ ;
+
+exp : '(' exp1 ')'
+ { }
+ ;
+
+/* Binary operators in order of decreasing precedence. */
+
+exp : exp '@' exp
+ { write_exp_elt_opcode (BINOP_REPEAT); }
+ ;
+
+exp : exp '*' exp
+ { write_exp_elt_opcode (BINOP_MUL); }
+ ;
+
+exp : exp '/' exp
+ { write_exp_elt_opcode (BINOP_DIV); }
+ ;
+
+exp : exp '%' exp
+ { write_exp_elt_opcode (BINOP_REM); }
+ ;
+
+exp : exp '+' exp
+ { write_exp_elt_opcode (BINOP_ADD); }
+ ;
+
+exp : exp '-' exp
+ { write_exp_elt_opcode (BINOP_SUB); }
+ ;
+
+exp : exp LSH exp
+ { write_exp_elt_opcode (BINOP_LSH); }
+ ;
+
+exp : exp RSH exp
+ { write_exp_elt_opcode (BINOP_RSH); }
+ ;
+
+exp : exp EQUAL exp
+ { write_exp_elt_opcode (BINOP_EQUAL); }
+ ;
+
+exp : exp NOTEQUAL exp
+ { write_exp_elt_opcode (BINOP_NOTEQUAL); }
+ ;
+
+exp : exp LEQ exp
+ { write_exp_elt_opcode (BINOP_LEQ); }
+ ;
+
+exp : exp GEQ exp
+ { write_exp_elt_opcode (BINOP_GEQ); }
+ ;
+
+exp : exp '<' exp
+ { write_exp_elt_opcode (BINOP_LESS); }
+ ;
+
+exp : exp '>' exp
+ { write_exp_elt_opcode (BINOP_GTR); }
+ ;
+
+exp : exp '&' exp
+ { write_exp_elt_opcode (BINOP_BITWISE_AND); }
+ ;
+
+exp : exp '^' exp
+ { write_exp_elt_opcode (BINOP_BITWISE_XOR); }
+ ;
+
+exp : exp '|' exp
+ { write_exp_elt_opcode (BINOP_BITWISE_IOR); }
+ ;
+
+exp : exp ANDAND exp
+ { write_exp_elt_opcode (BINOP_LOGICAL_AND); }
+ ;
+
+exp : exp OROR exp
+ { write_exp_elt_opcode (BINOP_LOGICAL_OR); }
+ ;
+
+exp : exp '?' exp ':' exp %prec '?'
+ { write_exp_elt_opcode (TERNOP_COND); }
+ ;
+
+exp : exp '=' exp
+ { write_exp_elt_opcode (BINOP_ASSIGN); }
+ ;
+
+exp : exp ASSIGN_MODIFY exp
+ { write_exp_elt_opcode (BINOP_ASSIGN_MODIFY);
+ write_exp_elt_opcode ($2);
+ write_exp_elt_opcode (BINOP_ASSIGN_MODIFY); }
+ ;
+
+exp : INT
+ { write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type ($1.type);
+ write_exp_elt_longcst ((LONGEST)($1.val));
+ write_exp_elt_opcode (OP_LONG); }
+ ;
+
+exp : NAME_OR_INT
+ { YYSTYPE val;
+ parse_number ($1.stoken.ptr, $1.stoken.length, 0, &val);
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (val.typed_val.type);
+ write_exp_elt_longcst ((LONGEST)val.typed_val.val);
+ write_exp_elt_opcode (OP_LONG);
+ }
+ ;
+
+
+exp : FLOAT
+ { write_exp_elt_opcode (OP_DOUBLE);
+ write_exp_elt_type (builtin_type_double);
+ write_exp_elt_dblcst ($1);
+ write_exp_elt_opcode (OP_DOUBLE); }
+ ;
+
+exp : variable
+ ;
+
+exp : LAST
+ { write_exp_elt_opcode (OP_LAST);
+ write_exp_elt_longcst ((LONGEST) $1);
+ write_exp_elt_opcode (OP_LAST); }
+ ;
+
+exp : REGNAME
+ { write_exp_elt_opcode (OP_REGISTER);
+ write_exp_elt_longcst ((LONGEST) $1);
+ write_exp_elt_opcode (OP_REGISTER); }
+ ;
+
+exp : VARIABLE
+ { write_exp_elt_opcode (OP_INTERNALVAR);
+ write_exp_elt_intern ($1);
+ write_exp_elt_opcode (OP_INTERNALVAR); }
+ ;
+
+exp : SIZEOF '(' type ')' %prec UNARY
+ { write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_int);
+ write_exp_elt_longcst ((LONGEST) TYPE_LENGTH ($3));
+ write_exp_elt_opcode (OP_LONG); }
+ ;
+
+exp : STRING
+ { /* C strings are converted into array constants with
+ an explicit null byte added at the end. Thus
+ the array upper bound is the string length.
+ There is no such thing in C as a completely empty
+ string. */
+ char *sp = $1.ptr; int count = $1.length;
+ while (count-- > 0)
+ {
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_char);
+ write_exp_elt_longcst ((LONGEST)(*sp++));
+ write_exp_elt_opcode (OP_LONG);
+ }
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_char);
+ write_exp_elt_longcst ((LONGEST)'\0');
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_opcode (OP_ARRAY);
+ write_exp_elt_longcst ((LONGEST) 0);
+ write_exp_elt_longcst ((LONGEST) ($1.length));
+ write_exp_elt_opcode (OP_ARRAY); }
+ ;
+
+/* C++. */
+exp : THIS
+ { write_exp_elt_opcode (OP_THIS);
+ write_exp_elt_opcode (OP_THIS); }
+ ;
+
+/* end of C++. */
+
+block : BLOCKNAME
+ {
+ if ($1.sym != 0)
+ $$ = SYMBOL_BLOCK_VALUE ($1.sym);
+ else
+ {
+ struct symtab *tem =
+ lookup_symtab (copy_name ($1.stoken));
+ if (tem)
+ $$ = BLOCKVECTOR_BLOCK
+ (BLOCKVECTOR (tem), STATIC_BLOCK);
+ else
+ error ("No file or function \"%s\".",
+ copy_name ($1.stoken));
+ }
+ }
+ ;
+
+block : block COLONCOLON name
+ { struct symbol *tem
+ = lookup_symbol (copy_name ($3), $1,
+ VAR_NAMESPACE, (int *) NULL,
+ (struct symtab **) NULL);
+ if (!tem || SYMBOL_CLASS (tem) != LOC_BLOCK)
+ error ("No function \"%s\" in specified context.",
+ copy_name ($3));
+ $$ = SYMBOL_BLOCK_VALUE (tem); }
+ ;
+
+variable: block COLONCOLON name
+ { struct symbol *sym;
+ sym = lookup_symbol (copy_name ($3), $1,
+ VAR_NAMESPACE, (int *) NULL,
+ (struct symtab **) NULL);
+ if (sym == 0)
+ error ("No symbol \"%s\" in specified context.",
+ copy_name ($3));
+
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ /* block_found is set by lookup_symbol. */
+ write_exp_elt_block (block_found);
+ write_exp_elt_sym (sym);
+ write_exp_elt_opcode (OP_VAR_VALUE); }
+ ;
+
+qualified_name: typebase COLONCOLON name
+ {
+ struct type *type = $1;
+ if (TYPE_CODE (type) != TYPE_CODE_STRUCT
+ && TYPE_CODE (type) != TYPE_CODE_UNION)
+ error ("`%s' is not defined as an aggregate type.",
+ TYPE_NAME (type));
+
+ write_exp_elt_opcode (OP_SCOPE);
+ write_exp_elt_type (type);
+ write_exp_string ($3);
+ write_exp_elt_opcode (OP_SCOPE);
+ }
+ | typebase COLONCOLON '~' name
+ {
+ struct type *type = $1;
+ struct stoken tmp_token;
+ if (TYPE_CODE (type) != TYPE_CODE_STRUCT
+ && TYPE_CODE (type) != TYPE_CODE_UNION)
+ error ("`%s' is not defined as an aggregate type.",
+ TYPE_NAME (type));
+
+ if (!STREQ (type_name_no_tag (type), $4.ptr))
+ error ("invalid destructor `%s::~%s'",
+ type_name_no_tag (type), $4.ptr);
+
+ tmp_token.ptr = (char*) alloca ($4.length + 2);
+ tmp_token.length = $4.length + 1;
+ tmp_token.ptr[0] = '~';
+ memcpy (tmp_token.ptr+1, $4.ptr, $4.length);
+ tmp_token.ptr[tmp_token.length] = 0;
+ write_exp_elt_opcode (OP_SCOPE);
+ write_exp_elt_type (type);
+ write_exp_string (tmp_token);
+ write_exp_elt_opcode (OP_SCOPE);
+ }
+ ;
+
+variable: qualified_name
+ | COLONCOLON name
+ {
+ char *name = copy_name ($2);
+ struct symbol *sym;
+ struct minimal_symbol *msymbol;
+
+ sym =
+ lookup_symbol (name, (const struct block *) NULL,
+ VAR_NAMESPACE, (int *) NULL,
+ (struct symtab **) NULL);
+ if (sym)
+ {
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ write_exp_elt_block (NULL);
+ write_exp_elt_sym (sym);
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ break;
+ }
+
+ msymbol = lookup_minimal_symbol (name,
+ (struct objfile *) NULL);
+ if (msymbol != NULL)
+ {
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_long);
+ write_exp_elt_longcst ((LONGEST) SYMBOL_VALUE_ADDRESS (msymbol));
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_opcode (UNOP_MEMVAL);
+ if (msymbol -> type == mst_data ||
+ msymbol -> type == mst_bss)
+ write_exp_elt_type (builtin_type_int);
+ else if (msymbol -> type == mst_text)
+ write_exp_elt_type (lookup_function_type (builtin_type_int));
+ else
+ write_exp_elt_type (builtin_type_char);
+ write_exp_elt_opcode (UNOP_MEMVAL);
+ }
+ else
+ if (!have_full_symbols () && !have_partial_symbols ())
+ error ("No symbol table is loaded. Use the \"file\" command.");
+ else
+ error ("No symbol \"%s\" in current context.", name);
+ }
+ ;
+
+variable: name_not_typename
+ { struct symbol *sym = $1.sym;
+
+ if (sym)
+ {
+ if (symbol_read_needs_frame (sym))
+ {
+ if (innermost_block == 0 ||
+ contained_in (block_found,
+ innermost_block))
+ innermost_block = block_found;
+ }
+
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ /* We want to use the selected frame, not
+ another more inner frame which happens to
+ be in the same block. */
+ write_exp_elt_block (NULL);
+ write_exp_elt_sym (sym);
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ }
+ else if ($1.is_a_field_of_this)
+ {
+ /* C++: it hangs off of `this'. Must
+ not inadvertently convert from a method call
+ to data ref. */
+ if (innermost_block == 0 ||
+ contained_in (block_found, innermost_block))
+ innermost_block = block_found;
+ write_exp_elt_opcode (OP_THIS);
+ write_exp_elt_opcode (OP_THIS);
+ write_exp_elt_opcode (STRUCTOP_PTR);
+ write_exp_string ($1.stoken);
+ write_exp_elt_opcode (STRUCTOP_PTR);
+ }
+ else
+ {
+ struct minimal_symbol *msymbol;
+ register char *arg = copy_name ($1.stoken);
+
+ msymbol = lookup_minimal_symbol (arg,
+ (struct objfile *) NULL);
+ if (msymbol != NULL)
+ {
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_long);
+ write_exp_elt_longcst ((LONGEST) SYMBOL_VALUE_ADDRESS (msymbol));
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_opcode (UNOP_MEMVAL);
+ if (msymbol -> type == mst_data ||
+ msymbol -> type == mst_bss)
+ write_exp_elt_type (builtin_type_int);
+ else if (msymbol -> type == mst_text)
+ write_exp_elt_type (lookup_function_type (builtin_type_int));
+ else
+ write_exp_elt_type (builtin_type_char);
+ write_exp_elt_opcode (UNOP_MEMVAL);
+ }
+ else if (!have_full_symbols () && !have_partial_symbols ())
+ error ("No symbol table is loaded. Use the \"file\" command.");
+ else
+ error ("No symbol \"%s\" in current context.",
+ copy_name ($1.stoken));
+ }
+ }
+ ;
+
+
+/* shift/reduce conflict: "typebase ." and the token is '('. (Shows up
+ twice, once where qualified_name is a possibility and once where
+ it is not). */
+/* shift/reduce conflict: "typebase CONST_KEYWORD ." and the token is '('. */
+/* shift/reduce conflict: "typebase VOLATILE_KEYWORD ." and the token is
+ '('. */
+ptype : typebase
+ /* "const" and "volatile" are curently ignored. A type qualifier
+ before the type is currently handled in the typebase rule. */
+ | typebase CONST_KEYWORD
+ | typebase VOLATILE_KEYWORD
+ | typebase abs_decl
+ { $$ = follow_types ($1); }
+ | typebase CONST_KEYWORD abs_decl
+ { $$ = follow_types ($1); }
+ | typebase VOLATILE_KEYWORD abs_decl
+ { $$ = follow_types ($1); }
+ ;
+
+abs_decl: '*'
+ { push_type (tp_pointer); $$ = 0; }
+ | '*' abs_decl
+ { push_type (tp_pointer); $$ = $2; }
+ | '&'
+ { push_type (tp_reference); $$ = 0; }
+ | '&' abs_decl
+ { push_type (tp_reference); $$ = $2; }
+ | direct_abs_decl
+ ;
+
+direct_abs_decl: '(' abs_decl ')'
+ { $$ = $2; }
+ | direct_abs_decl array_mod
+ {
+ push_type_int ($2);
+ push_type (tp_array);
+ }
+ | array_mod
+ {
+ push_type_int ($1);
+ push_type (tp_array);
+ $$ = 0;
+ }
+
+ /* shift/reduce conflict. "direct_abs_decl . func_mod", and the token
+ is '('. */
+
+ | direct_abs_decl func_mod
+ { push_type (tp_function); }
+ | func_mod
+ { push_type (tp_function); }
+ ;
+
+array_mod: '[' ']'
+ { $$ = -1; }
+ | '[' INT ']'
+ { $$ = $2.val; }
+ ;
+
+func_mod: '(' ')'
+ { $$ = 0; }
+ | '(' nonempty_typelist ')'
+ { free ((PTR)$2); $$ = 0; }
+ ;
+
+/* shift/reduce conflict: "type '(' typebase COLONCOLON '*' ')' ." and the
+ token is '('. */
+type : ptype
+ | typebase COLONCOLON '*'
+ { $$ = lookup_member_type (builtin_type_int, $1); }
+ | type '(' typebase COLONCOLON '*' ')'
+ { $$ = lookup_member_type ($1, $3); }
+ | type '(' typebase COLONCOLON '*' ')' '(' ')'
+ { $$ = lookup_member_type
+ (lookup_function_type ($1), $3); }
+ | type '(' typebase COLONCOLON '*' ')' '(' nonempty_typelist ')'
+ { $$ = lookup_member_type
+ (lookup_function_type ($1), $3);
+ free ((PTR)$8); }
+ ;
+
+typebase /* Implements (approximately): (type-qualifier)* type-specifier */
+ : TYPENAME
+ { $$ = $1.type; }
+ | INT_KEYWORD
+ { $$ = builtin_type_int; }
+ | LONG
+ { $$ = builtin_type_long; }
+ | SHORT
+ { $$ = builtin_type_short; }
+ | LONG INT_KEYWORD
+ { $$ = builtin_type_long; }
+ | UNSIGNED LONG INT_KEYWORD
+ { $$ = builtin_type_unsigned_long; }
+ | LONG LONG
+ { $$ = builtin_type_long_long; }
+ | LONG LONG INT_KEYWORD
+ { $$ = builtin_type_long_long; }
+ | UNSIGNED LONG LONG
+ { $$ = builtin_type_unsigned_long_long; }
+ | UNSIGNED LONG LONG INT_KEYWORD
+ { $$ = builtin_type_unsigned_long_long; }
+ | SHORT INT_KEYWORD
+ { $$ = builtin_type_short; }
+ | UNSIGNED SHORT INT_KEYWORD
+ { $$ = builtin_type_unsigned_short; }
+ | STRUCT name
+ { $$ = lookup_struct (copy_name ($2),
+ expression_context_block); }
+ | CLASS name
+ { $$ = lookup_struct (copy_name ($2),
+ expression_context_block); }
+ | UNION name
+ { $$ = lookup_union (copy_name ($2),
+ expression_context_block); }
+ | ENUM name
+ { $$ = lookup_enum (copy_name ($2),
+ expression_context_block); }
+ | UNSIGNED typename
+ { $$ = lookup_unsigned_typename (TYPE_NAME($2.type)); }
+ | UNSIGNED
+ { $$ = builtin_type_unsigned_int; }
+ | SIGNED_KEYWORD typename
+ { $$ = lookup_signed_typename (TYPE_NAME($2.type)); }
+ | SIGNED_KEYWORD
+ { $$ = builtin_type_int; }
+ | TEMPLATE name '<' type '>'
+ { $$ = lookup_template_type(copy_name($2), $4,
+ expression_context_block);
+ }
+ /* "const" and "volatile" are curently ignored. A type qualifier
+ after the type is handled in the ptype rule. I think these could
+ be too. */
+ | CONST_KEYWORD typebase { $$ = $2; }
+ | VOLATILE_KEYWORD typebase { $$ = $2; }
+ ;
+
+typename: TYPENAME
+ | INT_KEYWORD
+ {
+ $$.stoken.ptr = "int";
+ $$.stoken.length = 3;
+ $$.type = builtin_type_int;
+ }
+ | LONG
+ {
+ $$.stoken.ptr = "long";
+ $$.stoken.length = 4;
+ $$.type = builtin_type_long;
+ }
+ | SHORT
+ {
+ $$.stoken.ptr = "short";
+ $$.stoken.length = 5;
+ $$.type = builtin_type_short;
+ }
+ ;
+
+nonempty_typelist
+ : type
+ { $$ = (struct type **) malloc (sizeof (struct type *) * 2);
+ $<ivec>$[0] = 1; /* Number of types in vector */
+ $$[1] = $1;
+ }
+ | nonempty_typelist ',' type
+ { int len = sizeof (struct type *) * (++($<ivec>1[0]) + 1);
+ $$ = (struct type **) realloc ((char *) $1, len);
+ $$[$<ivec>$[0]] = $3;
+ }
+ ;
+
+name : NAME { $$ = $1.stoken; }
+ | BLOCKNAME { $$ = $1.stoken; }
+ | TYPENAME { $$ = $1.stoken; }
+ | NAME_OR_INT { $$ = $1.stoken; }
+ ;
+
+name_not_typename : NAME
+ | BLOCKNAME
+/* These would be useful if name_not_typename was useful, but it is just
+ a fake for "variable", so these cause reduce/reduce conflicts because
+ the parser can't tell whether NAME_OR_INT is a name_not_typename (=variable,
+ =exp) or just an exp. If name_not_typename was ever used in an lvalue
+ context where only a name could occur, this might be useful.
+ | NAME_OR_INT
+ */
+ ;
+
+%%
+
+/* Take care of parsing a number (anything that starts with a digit).
+ Set yylval and return the token type; update lexptr.
+ LEN is the number of characters in it. */
+
+/*** Needs some error checking for the float case ***/
+
+static int
+parse_number (p, len, parsed_float, putithere)
+ register char *p;
+ register int len;
+ int parsed_float;
+ YYSTYPE *putithere;
+{
+ register LONGEST n = 0;
+ register LONGEST prevn = 0;
+ register int i = 0;
+ register int c;
+ register int base = input_radix;
+ int unsigned_p = 0;
+ int long_p = 0;
+ unsigned LONGEST high_bit;
+ struct type *signed_type;
+ struct type *unsigned_type;
+
+ if (parsed_float)
+ {
+ /* It's a float since it contains a point or an exponent. */
+ putithere->dval = atof (p);
+ return FLOAT;
+ }
+
+ /* Handle base-switching prefixes 0x, 0t, 0d, 0 */
+ if (p[0] == '0')
+ switch (p[1])
+ {
+ case 'x':
+ case 'X':
+ if (len >= 3)
+ {
+ p += 2;
+ base = 16;
+ len -= 2;
+ }
+ break;
+
+ case 't':
+ case 'T':
+ case 'd':
+ case 'D':
+ if (len >= 3)
+ {
+ p += 2;
+ base = 10;
+ len -= 2;
+ }
+ break;
+
+ default:
+ base = 8;
+ break;
+ }
+
+ while (len-- > 0)
+ {
+ c = *p++;
+ if (c >= 'A' && c <= 'Z')
+ c += 'a' - 'A';
+ if (c != 'l' && c != 'u')
+ n *= base;
+ if (c >= '0' && c <= '9')
+ n += i = c - '0';
+ else
+ {
+ if (base > 10 && c >= 'a' && c <= 'f')
+ n += i = c - 'a' + 10;
+ else if (len == 0 && c == 'l')
+ long_p = 1;
+ else if (len == 0 && c == 'u')
+ unsigned_p = 1;
+ else
+ return ERROR; /* Char not a digit */
+ }
+ if (i >= base)
+ return ERROR; /* Invalid digit in this base */
+
+ /* Portably test for overflow (only works for nonzero values, so make
+ a second check for zero). */
+ if((prevn >= n) && n != 0)
+ unsigned_p=1; /* Try something unsigned */
+ /* If range checking enabled, portably test for unsigned overflow. */
+ if(RANGE_CHECK && n!=0)
+ {
+ if((unsigned_p && (unsigned)prevn >= (unsigned)n))
+ range_error("Overflow on numeric constant.");
+ }
+ prevn=n;
+ }
+
+ /* If the number is too big to be an int, or it's got an l suffix
+ then it's a long. Work out if this has to be a long by
+ shifting right and and seeing if anything remains, and the
+ target int size is different to the target long size.
+
+ In the expression below, we could have tested
+ (n >> TARGET_INT_BIT)
+ to see if it was zero,
+ but too many compilers warn about that, when ints and longs
+ are the same size. So we shift it twice, with fewer bits
+ each time, for the same result. */
+
+ if ( (TARGET_INT_BIT != TARGET_LONG_BIT
+ && ((n >> 2) >> (TARGET_INT_BIT-2))) /* Avoid shift warning */
+ || long_p)
+ {
+ high_bit = ((unsigned LONGEST)1) << (TARGET_LONG_BIT-1);
+ unsigned_type = builtin_type_unsigned_long;
+ signed_type = builtin_type_long;
+ }
+ else
+ {
+ high_bit = ((unsigned LONGEST)1) << (TARGET_INT_BIT-1);
+ unsigned_type = builtin_type_unsigned_int;
+ signed_type = builtin_type_int;
+ }
+
+ putithere->typed_val.val = n;
+
+ /* If the high bit of the worked out type is set then this number
+ has to be unsigned. */
+
+ if (unsigned_p || (n & high_bit))
+ {
+ putithere->typed_val.type = unsigned_type;
+ }
+ else
+ {
+ putithere->typed_val.type = signed_type;
+ }
+
+ return INT;
+}
+
+struct token
+{
+ char *operator;
+ int token;
+ enum exp_opcode opcode;
+};
+
+static const struct token tokentab3[] =
+ {
+ {">>=", ASSIGN_MODIFY, BINOP_RSH},
+ {"<<=", ASSIGN_MODIFY, BINOP_LSH}
+ };
+
+static const struct token tokentab2[] =
+ {
+ {"+=", ASSIGN_MODIFY, BINOP_ADD},
+ {"-=", ASSIGN_MODIFY, BINOP_SUB},
+ {"*=", ASSIGN_MODIFY, BINOP_MUL},
+ {"/=", ASSIGN_MODIFY, BINOP_DIV},
+ {"%=", ASSIGN_MODIFY, BINOP_REM},
+ {"|=", ASSIGN_MODIFY, BINOP_BITWISE_IOR},
+ {"&=", ASSIGN_MODIFY, BINOP_BITWISE_AND},
+ {"^=", ASSIGN_MODIFY, BINOP_BITWISE_XOR},
+ {"++", INCREMENT, BINOP_END},
+ {"--", DECREMENT, BINOP_END},
+ {"->", ARROW, BINOP_END},
+ {"&&", ANDAND, BINOP_END},
+ {"||", OROR, BINOP_END},
+ {"::", COLONCOLON, BINOP_END},
+ {"<<", LSH, BINOP_END},
+ {">>", RSH, BINOP_END},
+ {"==", EQUAL, BINOP_END},
+ {"!=", NOTEQUAL, BINOP_END},
+ {"<=", LEQ, BINOP_END},
+ {">=", GEQ, BINOP_END}
+ };
+
+/* Read one token, getting characters through lexptr. */
+
+static int
+yylex ()
+{
+ int c;
+ int namelen;
+ unsigned int i;
+ char *tokstart;
+ char *tokptr;
+ int tempbufindex;
+ static char *tempbuf;
+ static int tempbufsize;
+
+ retry:
+
+ tokstart = lexptr;
+ /* See if it is a special token of length 3. */
+ for (i = 0; i < sizeof tokentab3 / sizeof tokentab3[0]; i++)
+ if (STREQN (tokstart, tokentab3[i].operator, 3))
+ {
+ lexptr += 3;
+ yylval.opcode = tokentab3[i].opcode;
+ return tokentab3[i].token;
+ }
+
+ /* See if it is a special token of length 2. */
+ for (i = 0; i < sizeof tokentab2 / sizeof tokentab2[0]; i++)
+ if (STREQN (tokstart, tokentab2[i].operator, 2))
+ {
+ lexptr += 2;
+ yylval.opcode = tokentab2[i].opcode;
+ return tokentab2[i].token;
+ }
+
+ switch (c = *tokstart)
+ {
+ case 0:
+ return 0;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ lexptr++;
+ goto retry;
+
+ case '\'':
+ /* We either have a character constant ('0' or '\177' for example)
+ or we have a quoted symbol reference ('foo(int,int)' in C++
+ for example). */
+ lexptr++;
+ c = *lexptr++;
+ if (c == '\\')
+ c = parse_escape (&lexptr);
+
+ yylval.typed_val.val = c;
+ yylval.typed_val.type = builtin_type_char;
+
+ c = *lexptr++;
+ if (c != '\'')
+ {
+ namelen = skip_quoted (tokstart) - tokstart;
+ if (namelen > 2)
+ {
+ lexptr = tokstart + namelen;
+ if (lexptr[-1] != '\'')
+ error ("Unmatched single quote.");
+ namelen -= 2;
+ tokstart++;
+ goto tryname;
+ }
+ error ("Invalid character constant.");
+ }
+ return INT;
+
+ case '(':
+ paren_depth++;
+ lexptr++;
+ return c;
+
+ case ')':
+ if (paren_depth == 0)
+ return 0;
+ paren_depth--;
+ lexptr++;
+ return c;
+
+ case ',':
+ if (comma_terminates && paren_depth == 0)
+ return 0;
+ lexptr++;
+ return c;
+
+ case '.':
+ /* Might be a floating point number. */
+ if (lexptr[1] < '0' || lexptr[1] > '9')
+ goto symbol; /* Nope, must be a symbol. */
+ /* FALL THRU into number case. */
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ /* It's a number. */
+ int got_dot = 0, got_e = 0, toktype;
+ register char *p = tokstart;
+ int hex = input_radix > 10;
+
+ if (c == '0' && (p[1] == 'x' || p[1] == 'X'))
+ {
+ p += 2;
+ hex = 1;
+ }
+ else if (c == '0' && (p[1]=='t' || p[1]=='T' || p[1]=='d' || p[1]=='D'))
+ {
+ p += 2;
+ hex = 0;
+ }
+
+ for (;; ++p)
+ {
+ /* This test includes !hex because 'e' is a valid hex digit
+ and thus does not indicate a floating point number when
+ the radix is hex. */
+ if (!hex && !got_e && (*p == 'e' || *p == 'E'))
+ got_dot = got_e = 1;
+ /* This test does not include !hex, because a '.' always indicates
+ a decimal floating point number regardless of the radix. */
+ else if (!got_dot && *p == '.')
+ got_dot = 1;
+ else if (got_e && (p[-1] == 'e' || p[-1] == 'E')
+ && (*p == '-' || *p == '+'))
+ /* This is the sign of the exponent, not the end of the
+ number. */
+ continue;
+ /* We will take any letters or digits. parse_number will
+ complain if past the radix, or if L or U are not final. */
+ else if ((*p < '0' || *p > '9')
+ && ((*p < 'a' || *p > 'z')
+ && (*p < 'A' || *p > 'Z')))
+ break;
+ }
+ toktype = parse_number (tokstart, p - tokstart, got_dot|got_e, &yylval);
+ if (toktype == ERROR)
+ {
+ char *err_copy = (char *) alloca (p - tokstart + 1);
+
+ memcpy (err_copy, tokstart, p - tokstart);
+ err_copy[p - tokstart] = 0;
+ error ("Invalid number \"%s\".", err_copy);
+ }
+ lexptr = p;
+ return toktype;
+ }
+
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '%':
+ case '|':
+ case '&':
+ case '^':
+ case '~':
+ case '!':
+ case '@':
+ case '<':
+ case '>':
+ case '[':
+ case ']':
+ case '?':
+ case ':':
+ case '=':
+ case '{':
+ case '}':
+ symbol:
+ lexptr++;
+ return c;
+
+ case '"':
+
+ /* Build the gdb internal form of the input string in tempbuf,
+ translating any standard C escape forms seen. Note that the
+ buffer is null byte terminated *only* for the convenience of
+ debugging gdb itself and printing the buffer contents when
+ the buffer contains no embedded nulls. Gdb does not depend
+ upon the buffer being null byte terminated, it uses the length
+ string instead. This allows gdb to handle C strings (as well
+ as strings in other languages) with embedded null bytes */
+
+ tokptr = ++tokstart;
+ tempbufindex = 0;
+
+ do {
+ /* Grow the static temp buffer if necessary, including allocating
+ the first one on demand. */
+ if (tempbufindex + 1 >= tempbufsize)
+ {
+ tempbuf = (char *) realloc (tempbuf, tempbufsize += 64);
+ }
+ switch (*tokptr)
+ {
+ case '\0':
+ case '"':
+ /* Do nothing, loop will terminate. */
+ break;
+ case '\\':
+ tokptr++;
+ c = parse_escape (&tokptr);
+ if (c == -1)
+ {
+ continue;
+ }
+ tempbuf[tempbufindex++] = c;
+ break;
+ default:
+ tempbuf[tempbufindex++] = *tokptr++;
+ break;
+ }
+ } while ((*tokptr != '"') && (*tokptr != '\0'));
+ if (*tokptr++ != '"')
+ {
+ error ("Unterminated string in expression.");
+ }
+ tempbuf[tempbufindex] = '\0'; /* See note above */
+ yylval.sval.ptr = tempbuf;
+ yylval.sval.length = tempbufindex;
+ lexptr = tokptr;
+ return (STRING);
+ }
+
+ if (!(c == '_' || c == '$'
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
+ /* We must have come across a bad character (e.g. ';'). */
+ error ("Invalid character '%c' in expression.", c);
+
+ /* It's a name. See how long it is. */
+ namelen = 0;
+ for (c = tokstart[namelen];
+ (c == '_' || c == '$' || (c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
+ c = tokstart[++namelen])
+ ;
+
+ /* The token "if" terminates the expression and is NOT
+ removed from the input stream. */
+ if (namelen == 2 && tokstart[0] == 'i' && tokstart[1] == 'f')
+ {
+ return 0;
+ }
+
+ lexptr += namelen;
+
+ /* Handle the tokens $digits; also $ (short for $0) and $$ (short for $$1)
+ and $$digits (equivalent to $<-digits> if you could type that).
+ Make token type LAST, and put the number (the digits) in yylval. */
+
+ tryname:
+ if (*tokstart == '$')
+ {
+ register int negate = 0;
+ c = 1;
+ /* Double dollar means negate the number and add -1 as well.
+ Thus $$ alone means -1. */
+ if (namelen >= 2 && tokstart[1] == '$')
+ {
+ negate = 1;
+ c = 2;
+ }
+ if (c == namelen)
+ {
+ /* Just dollars (one or two) */
+ yylval.lval = - negate;
+ return LAST;
+ }
+ /* Is the rest of the token digits? */
+ for (; c < namelen; c++)
+ if (!(tokstart[c] >= '0' && tokstart[c] <= '9'))
+ break;
+ if (c == namelen)
+ {
+ yylval.lval = atoi (tokstart + 1 + negate);
+ if (negate)
+ yylval.lval = - yylval.lval;
+ return LAST;
+ }
+ }
+
+ /* Handle tokens that refer to machine registers:
+ $ followed by a register name. */
+
+ if (*tokstart == '$') {
+ for (c = 0; c < NUM_REGS; c++)
+ if (namelen - 1 == strlen (reg_names[c])
+ && STREQN (tokstart + 1, reg_names[c], namelen - 1))
+ {
+ yylval.lval = c;
+ return REGNAME;
+ }
+ for (c = 0; c < num_std_regs; c++)
+ if (namelen - 1 == strlen (std_regs[c].name)
+ && STREQN (tokstart + 1, std_regs[c].name, namelen - 1))
+ {
+ yylval.lval = std_regs[c].regnum;
+ return REGNAME;
+ }
+ }
+ /* Catch specific keywords. Should be done with a data structure. */
+ switch (namelen)
+ {
+ case 8:
+ if (STREQN (tokstart, "unsigned", 8))
+ return UNSIGNED;
+ if (current_language->la_language == language_cplus
+ && STREQN (tokstart, "template", 8))
+ return TEMPLATE;
+ if (STREQN (tokstart, "volatile", 8))
+ return VOLATILE_KEYWORD;
+ break;
+ case 6:
+ if (STREQN (tokstart, "struct", 6))
+ return STRUCT;
+ if (STREQN (tokstart, "signed", 6))
+ return SIGNED_KEYWORD;
+ if (STREQN (tokstart, "sizeof", 6))
+ return SIZEOF;
+ break;
+ case 5:
+ if (current_language->la_language == language_cplus
+ && STREQN (tokstart, "class", 5))
+ return CLASS;
+ if (STREQN (tokstart, "union", 5))
+ return UNION;
+ if (STREQN (tokstart, "short", 5))
+ return SHORT;
+ if (STREQN (tokstart, "const", 5))
+ return CONST_KEYWORD;
+ break;
+ case 4:
+ if (STREQN (tokstart, "enum", 4))
+ return ENUM;
+ if (STREQN (tokstart, "long", 4))
+ return LONG;
+ if (current_language->la_language == language_cplus
+ && STREQN (tokstart, "this", 4))
+ {
+ static const char this_name[] =
+ { CPLUS_MARKER, 't', 'h', 'i', 's', '\0' };
+
+ if (lookup_symbol (this_name, expression_context_block,
+ VAR_NAMESPACE, (int *) NULL,
+ (struct symtab **) NULL))
+ return THIS;
+ }
+ break;
+ case 3:
+ if (STREQN (tokstart, "int", 3))
+ return INT_KEYWORD;
+ break;
+ default:
+ break;
+ }
+
+ yylval.sval.ptr = tokstart;
+ yylval.sval.length = namelen;
+
+ /* Any other names starting in $ are debugger internal variables. */
+
+ if (*tokstart == '$')
+ {
+ yylval.ivar = lookup_internalvar (copy_name (yylval.sval) + 1);
+ return VARIABLE;
+ }
+
+ /* Use token-type BLOCKNAME for symbols that happen to be defined as
+ functions or symtabs. If this is not so, then ...
+ Use token-type TYPENAME for symbols that happen to be defined
+ currently as names of types; NAME for other symbols.
+ The caller is not constrained to care about the distinction. */
+ {
+ char *tmp = copy_name (yylval.sval);
+ struct symbol *sym;
+ int is_a_field_of_this = 0;
+ int hextype;
+
+ sym = lookup_symbol (tmp, expression_context_block,
+ VAR_NAMESPACE,
+ current_language->la_language == language_cplus
+ ? &is_a_field_of_this : (int *) NULL,
+ (struct symtab **) NULL);
+ if ((sym && SYMBOL_CLASS (sym) == LOC_BLOCK) ||
+ lookup_partial_symtab (tmp))
+ {
+ yylval.ssym.sym = sym;
+ yylval.ssym.is_a_field_of_this = is_a_field_of_this;
+ return BLOCKNAME;
+ }
+ if (sym && SYMBOL_CLASS (sym) == LOC_TYPEDEF)
+ {
+ char *p;
+ char *namestart;
+ struct symbol *best_sym;
+
+ /* Look ahead to detect nested types. This probably should be
+ done in the grammar, but trying seemed to introduce a lot
+ of shift/reduce and reduce/reduce conflicts. It's possible
+ that it could be done, though. Or perhaps a non-grammar, but
+ less ad hoc, approach would work well. */
+
+ /* Since we do not currently have any way of distinguishing
+ a nested type from a non-nested one (the stabs don't tell
+ us whether a type is nested), we just ignore the
+ containing type. */
+
+ p = lexptr;
+ best_sym = sym;
+ while (1)
+ {
+ /* Skip whitespace. */
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ ++p;
+ if (*p == ':' && p[1] == ':')
+ {
+ /* Skip the `::'. */
+ p += 2;
+ /* Skip whitespace. */
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ ++p;
+ namestart = p;
+ while (*p == '_' || *p == '$' || (*p >= '0' && *p <= '9')
+ || (*p >= 'a' && *p <= 'z')
+ || (*p >= 'A' && *p <= 'Z'))
+ ++p;
+ if (p != namestart)
+ {
+ struct symbol *cur_sym;
+ /* As big as the whole rest of the expression, which is
+ at least big enough. */
+ char *tmp = alloca (strlen (namestart));
+
+ memcpy (tmp, namestart, p - namestart);
+ tmp[p - namestart] = '\0';
+ cur_sym = lookup_symbol (tmp, expression_context_block,
+ VAR_NAMESPACE, (int *) NULL,
+ (struct symtab **) NULL);
+ if (cur_sym)
+ {
+ if (SYMBOL_CLASS (cur_sym) == LOC_TYPEDEF)
+ {
+ best_sym = cur_sym;
+ lexptr = p;
+ }
+ else
+ break;
+ }
+ else
+ break;
+ }
+ else
+ break;
+ }
+ else
+ break;
+ }
+
+ yylval.tsym.type = SYMBOL_TYPE (best_sym);
+ return TYPENAME;
+ }
+ if ((yylval.tsym.type = lookup_primitive_typename (tmp)) != 0)
+ return TYPENAME;
+
+ /* Input names that aren't symbols but ARE valid hex numbers,
+ when the input radix permits them, can be names or numbers
+ depending on the parse. Note we support radixes > 16 here. */
+ if (!sym &&
+ ((tokstart[0] >= 'a' && tokstart[0] < 'a' + input_radix - 10) ||
+ (tokstart[0] >= 'A' && tokstart[0] < 'A' + input_radix - 10)))
+ {
+ YYSTYPE newlval; /* Its value is ignored. */
+ hextype = parse_number (tokstart, namelen, 0, &newlval);
+ if (hextype == INT)
+ {
+ yylval.ssym.sym = sym;
+ yylval.ssym.is_a_field_of_this = is_a_field_of_this;
+ return NAME_OR_INT;
+ }
+ }
+
+ /* Any other kind of symbol */
+ yylval.ssym.sym = sym;
+ yylval.ssym.is_a_field_of_this = is_a_field_of_this;
+ return NAME;
+ }
+}
+
+void
+yyerror (msg)
+ char *msg;
+{
+ error (msg ? msg : "Invalid syntax in expression.");
+}
diff --git a/gnu/usr.bin/gdb/gdb/c-lang.c b/gnu/usr.bin/gdb/gdb/c-lang.c
new file mode 100644
index 0000000..ea07959
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/c-lang.c
@@ -0,0 +1,447 @@
+/* C language support routines for GDB, the GNU debugger.
+ Copyright 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "expression.h"
+#include "parser-defs.h"
+#include "language.h"
+#include "c-lang.h"
+
+/* Print the character C on STREAM as part of the contents of a literal
+ string whose delimiter is QUOTER. Note that that format for printing
+ characters and strings is language specific. */
+
+static void
+emit_char (c, stream, quoter)
+ register int c;
+ FILE *stream;
+ int quoter;
+{
+
+ c &= 0xFF; /* Avoid sign bit follies */
+
+ if (PRINT_LITERAL_FORM (c))
+ {
+ if (c == '\\' || c == quoter)
+ {
+ fputs_filtered ("\\", stream);
+ }
+ fprintf_filtered (stream, "%c", c);
+ }
+ else
+ {
+ switch (c)
+ {
+ case '\n':
+ fputs_filtered ("\\n", stream);
+ break;
+ case '\b':
+ fputs_filtered ("\\b", stream);
+ break;
+ case '\t':
+ fputs_filtered ("\\t", stream);
+ break;
+ case '\f':
+ fputs_filtered ("\\f", stream);
+ break;
+ case '\r':
+ fputs_filtered ("\\r", stream);
+ break;
+ case '\033':
+ fputs_filtered ("\\e", stream);
+ break;
+ case '\007':
+ fputs_filtered ("\\a", stream);
+ break;
+ default:
+ fprintf_filtered (stream, "\\%.3o", (unsigned int) c);
+ break;
+ }
+ }
+}
+
+static void
+c_printchar (c, stream)
+ int c;
+ FILE *stream;
+{
+ fputs_filtered ("'", stream);
+ emit_char (c, stream, '\'');
+ fputs_filtered ("'", stream);
+}
+
+/* Print the character string STRING, printing at most LENGTH characters.
+ Printing stops early if the number hits print_max; repeat counts
+ are printed as appropriate. Print ellipses at the end if we
+ had to stop before printing LENGTH characters, or if FORCE_ELLIPSES. */
+
+static void
+c_printstr (stream, string, length, force_ellipses)
+ FILE *stream;
+ char *string;
+ unsigned int length;
+ int force_ellipses;
+{
+ register unsigned int i;
+ unsigned int things_printed = 0;
+ int in_quotes = 0;
+ int need_comma = 0;
+ extern int inspect_it;
+ extern int repeat_count_threshold;
+ extern int print_max;
+
+ /* If the string was not truncated due to `set print elements', and
+ the last byte of it is a null, we don't print that, in traditional C
+ style. */
+ if ((!force_ellipses) && length > 0 && string[length-1] == '\0')
+ length--;
+
+ if (length == 0)
+ {
+ fputs_filtered ("\"\"", stdout);
+ return;
+ }
+
+ for (i = 0; i < length && things_printed < print_max; ++i)
+ {
+ /* Position of the character we are examining
+ to see whether it is repeated. */
+ unsigned int rep1;
+ /* Number of repetitions we have detected so far. */
+ unsigned int reps;
+
+ QUIT;
+
+ if (need_comma)
+ {
+ fputs_filtered (", ", stream);
+ need_comma = 0;
+ }
+
+ rep1 = i + 1;
+ reps = 1;
+ while (rep1 < length && string[rep1] == string[i])
+ {
+ ++rep1;
+ ++reps;
+ }
+
+ if (reps > repeat_count_threshold)
+ {
+ if (in_quotes)
+ {
+ if (inspect_it)
+ fputs_filtered ("\\\", ", stream);
+ else
+ fputs_filtered ("\", ", stream);
+ in_quotes = 0;
+ }
+ c_printchar (string[i], stream);
+ fprintf_filtered (stream, " <repeats %u times>", reps);
+ i = rep1 - 1;
+ things_printed += repeat_count_threshold;
+ need_comma = 1;
+ }
+ else
+ {
+ if (!in_quotes)
+ {
+ if (inspect_it)
+ fputs_filtered ("\\\"", stream);
+ else
+ fputs_filtered ("\"", stream);
+ in_quotes = 1;
+ }
+ emit_char (string[i], stream, '"');
+ ++things_printed;
+ }
+ }
+
+ /* Terminate the quotes if necessary. */
+ if (in_quotes)
+ {
+ if (inspect_it)
+ fputs_filtered ("\\\"", stream);
+ else
+ fputs_filtered ("\"", stream);
+ }
+
+ if (force_ellipses || i < length)
+ fputs_filtered ("...", stream);
+}
+
+/* Create a fundamental C type using default reasonable for the current
+ target machine.
+
+ Some object/debugging file formats (DWARF version 1, COFF, etc) do not
+ define fundamental types such as "int" or "double". Others (stabs or
+ DWARF version 2, etc) do define fundamental types. For the formats which
+ don't provide fundamental types, gdb can create such types using this
+ function.
+
+ FIXME: Some compilers distinguish explicitly signed integral types
+ (signed short, signed int, signed long) from "regular" integral types
+ (short, int, long) in the debugging information. There is some dis-
+ agreement as to how useful this feature is. In particular, gcc does
+ not support this. Also, only some debugging formats allow the
+ distinction to be passed on to a debugger. For now, we always just
+ use "short", "int", or "long" as the type name, for both the implicit
+ and explicitly signed types. This also makes life easier for the
+ gdb test suite since we don't have to account for the differences
+ in output depending upon what the compiler and debugging format
+ support. We will probably have to re-examine the issue when gdb
+ starts taking it's fundamental type information directly from the
+ debugging information supplied by the compiler. fnf@cygnus.com */
+
+static struct type *
+c_create_fundamental_type (objfile, typeid)
+ struct objfile *objfile;
+ int typeid;
+{
+ register struct type *type = NULL;
+
+ switch (typeid)
+ {
+ default:
+ /* FIXME: For now, if we are asked to produce a type not in this
+ language, create the equivalent of a C integer type with the
+ name "<?type?>". When all the dust settles from the type
+ reconstruction work, this should probably become an error. */
+ type = init_type (TYPE_CODE_INT,
+ TARGET_INT_BIT / TARGET_CHAR_BIT,
+ 0, "<?type?>", objfile);
+ warning ("internal error: no C/C++ fundamental type %d", typeid);
+ break;
+ case FT_VOID:
+ type = init_type (TYPE_CODE_VOID,
+ TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+ 0, "void", objfile);
+ break;
+ case FT_CHAR:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+ 0, "char", objfile);
+ break;
+ case FT_SIGNED_CHAR:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_SIGNED, "signed char", objfile);
+ break;
+ case FT_UNSIGNED_CHAR:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED, "unsigned char", objfile);
+ break;
+ case FT_SHORT:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_SHORT_BIT / TARGET_CHAR_BIT,
+ 0, "short", objfile);
+ break;
+ case FT_SIGNED_SHORT:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_SHORT_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_SIGNED, "short", objfile); /* FIXME-fnf */
+ break;
+ case FT_UNSIGNED_SHORT:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_SHORT_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED, "unsigned short", objfile);
+ break;
+ case FT_INTEGER:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_INT_BIT / TARGET_CHAR_BIT,
+ 0, "int", objfile);
+ break;
+ case FT_SIGNED_INTEGER:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_INT_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_SIGNED, "int", objfile); /* FIXME -fnf */
+ break;
+ case FT_UNSIGNED_INTEGER:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_INT_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED, "unsigned int", objfile);
+ break;
+ case FT_LONG:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_LONG_BIT / TARGET_CHAR_BIT,
+ 0, "long", objfile);
+ break;
+ case FT_SIGNED_LONG:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_LONG_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_SIGNED, "long", objfile); /* FIXME -fnf */
+ break;
+ case FT_UNSIGNED_LONG:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_LONG_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED, "unsigned long", objfile);
+ break;
+ case FT_LONG_LONG:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_LONG_LONG_BIT / TARGET_CHAR_BIT,
+ 0, "long long", objfile);
+ break;
+ case FT_SIGNED_LONG_LONG:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_LONG_LONG_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_SIGNED, "signed long long", objfile);
+ break;
+ case FT_UNSIGNED_LONG_LONG:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_LONG_LONG_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED, "unsigned long long", objfile);
+ break;
+ case FT_FLOAT:
+ type = init_type (TYPE_CODE_FLT,
+ TARGET_FLOAT_BIT / TARGET_CHAR_BIT,
+ 0, "float", objfile);
+ break;
+ case FT_DBL_PREC_FLOAT:
+ type = init_type (TYPE_CODE_FLT,
+ TARGET_DOUBLE_BIT / TARGET_CHAR_BIT,
+ 0, "double", objfile);
+ break;
+ case FT_EXT_PREC_FLOAT:
+ type = init_type (TYPE_CODE_FLT,
+ TARGET_LONG_DOUBLE_BIT / TARGET_CHAR_BIT,
+ 0, "long double", objfile);
+ break;
+ }
+ return (type);
+}
+
+
+/* Table mapping opcodes into strings for printing operators
+ and precedences of the operators. */
+
+static const struct op_print c_op_print_tab[] =
+ {
+ {",", BINOP_COMMA, PREC_COMMA, 0},
+ {"=", BINOP_ASSIGN, PREC_ASSIGN, 1},
+ {"||", BINOP_LOGICAL_OR, PREC_LOGICAL_OR, 0},
+ {"&&", BINOP_LOGICAL_AND, PREC_LOGICAL_AND, 0},
+ {"|", BINOP_BITWISE_IOR, PREC_BITWISE_IOR, 0},
+ {"^", BINOP_BITWISE_XOR, PREC_BITWISE_XOR, 0},
+ {"&", BINOP_BITWISE_AND, PREC_BITWISE_AND, 0},
+ {"==", BINOP_EQUAL, PREC_EQUAL, 0},
+ {"!=", BINOP_NOTEQUAL, PREC_EQUAL, 0},
+ {"<=", BINOP_LEQ, PREC_ORDER, 0},
+ {">=", BINOP_GEQ, PREC_ORDER, 0},
+ {">", BINOP_GTR, PREC_ORDER, 0},
+ {"<", BINOP_LESS, PREC_ORDER, 0},
+ {">>", BINOP_RSH, PREC_SHIFT, 0},
+ {"<<", BINOP_LSH, PREC_SHIFT, 0},
+ {"+", BINOP_ADD, PREC_ADD, 0},
+ {"-", BINOP_SUB, PREC_ADD, 0},
+ {"*", BINOP_MUL, PREC_MUL, 0},
+ {"/", BINOP_DIV, PREC_MUL, 0},
+ {"%", BINOP_REM, PREC_MUL, 0},
+ {"@", BINOP_REPEAT, PREC_REPEAT, 0},
+ {"-", UNOP_NEG, PREC_PREFIX, 0},
+ {"!", UNOP_LOGICAL_NOT, PREC_PREFIX, 0},
+ {"~", UNOP_COMPLEMENT, PREC_PREFIX, 0},
+ {"*", UNOP_IND, PREC_PREFIX, 0},
+ {"&", UNOP_ADDR, PREC_PREFIX, 0},
+ {"sizeof ", UNOP_SIZEOF, PREC_PREFIX, 0},
+ {"++", UNOP_PREINCREMENT, PREC_PREFIX, 0},
+ {"--", UNOP_PREDECREMENT, PREC_PREFIX, 0},
+ /* C++ */
+ {"::", BINOP_SCOPE, PREC_PREFIX, 0},
+ {NULL, 0, 0, 0}
+};
+
+struct type ** const (c_builtin_types[]) =
+{
+ &builtin_type_int,
+ &builtin_type_long,
+ &builtin_type_short,
+ &builtin_type_char,
+ &builtin_type_float,
+ &builtin_type_double,
+ &builtin_type_void,
+ &builtin_type_long_long,
+ &builtin_type_signed_char,
+ &builtin_type_unsigned_char,
+ &builtin_type_unsigned_short,
+ &builtin_type_unsigned_int,
+ &builtin_type_unsigned_long,
+ &builtin_type_unsigned_long_long,
+ &builtin_type_long_double,
+ &builtin_type_complex,
+ &builtin_type_double_complex,
+ 0
+};
+
+const struct language_defn c_language_defn = {
+ "c", /* Language name */
+ language_c,
+ c_builtin_types,
+ range_check_off,
+ type_check_off,
+ c_parse,
+ c_error,
+ c_printchar, /* Print a character constant */
+ c_printstr, /* Function to print string constant */
+ c_create_fundamental_type, /* Create fundamental type in this language */
+ c_print_type, /* Print a type using appropriate syntax */
+ c_val_print, /* Print a value using appropriate syntax */
+ &BUILTIN_TYPE_LONGEST, /* longest signed integral type */
+ &BUILTIN_TYPE_UNSIGNED_LONGEST,/* longest unsigned integral type */
+ &builtin_type_double, /* longest floating point type */ /*FIXME*/
+ {"", "", "", ""}, /* Binary format info */
+ {"0%lo", "0", "o", ""}, /* Octal format info */
+ {"%ld", "", "d", ""}, /* Decimal format info */
+ {"0x%lx", "0x", "x", ""}, /* Hex format info */
+ c_op_print_tab, /* expression operators for printing */
+ LANG_MAGIC
+};
+
+const struct language_defn cplus_language_defn = {
+ "c++", /* Language name */
+ language_cplus,
+ c_builtin_types,
+ range_check_off,
+ type_check_off,
+ c_parse,
+ c_error,
+ c_printchar, /* Print a character constant */
+ c_printstr, /* Function to print string constant */
+ c_create_fundamental_type, /* Create fundamental type in this language */
+ c_print_type, /* Print a type using appropriate syntax */
+ c_val_print, /* Print a value using appropriate syntax */
+ &BUILTIN_TYPE_LONGEST, /* longest signed integral type */
+ &BUILTIN_TYPE_UNSIGNED_LONGEST,/* longest unsigned integral type */
+ &builtin_type_double, /* longest floating point type */ /*FIXME*/
+ {"", "", "", ""}, /* Binary format info */
+ {"0%lo", "0", "o", ""}, /* Octal format info */
+ {"%ld", "", "d", ""}, /* Decimal format info */
+ {"0x%lx", "0x", "x", ""}, /* Hex format info */
+ c_op_print_tab, /* expression operators for printing */
+ LANG_MAGIC
+};
+
+void
+_initialize_c_language ()
+{
+ add_language (&c_language_defn);
+ add_language (&cplus_language_defn);
+}
diff --git a/gnu/usr.bin/gdb/gdb/c-lang.h b/gnu/usr.bin/gdb/gdb/c-lang.h
new file mode 100644
index 0000000..4c343a9
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/c-lang.h
@@ -0,0 +1,31 @@
+/* C language support definitions for GDB, the GNU debugger.
+ Copyright 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+extern int
+c_parse PARAMS ((void)); /* Defined in c-exp.y */
+
+extern void
+c_error PARAMS ((char *)); /* Defined in c-exp.y */
+
+extern void /* Defined in c-typeprint.c */
+c_print_type PARAMS ((struct type *, char *, FILE *, int, int));
+
+extern int
+c_val_print PARAMS ((struct type *, char *, CORE_ADDR, FILE *, int, int,
+ int, enum val_prettyprint));
diff --git a/gnu/usr.bin/gdb/gdb/c-typeprint.c b/gnu/usr.bin/gdb/gdb/c-typeprint.c
new file mode 100644
index 0000000..fa4035b
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/c-typeprint.c
@@ -0,0 +1,797 @@
+/* Support for printing C and C++ types for GDB, the GNU debugger.
+ Copyright 1986, 1988, 1989, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "obstack.h"
+#include "bfd.h" /* Binary File Description */
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "expression.h"
+#include "value.h"
+#include "gdbcore.h"
+#include "target.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "language.h"
+#include "demangle.h"
+#include "c-lang.h"
+#include "typeprint.h"
+
+#include <string.h>
+#include <errno.h>
+
+extern int demangle; /* whether to print C++ syms raw or source-form */
+
+static void
+c_type_print_args PARAMS ((struct type *, FILE *));
+
+static void
+c_type_print_varspec_suffix PARAMS ((struct type *, FILE *, int, int, int));
+
+static void
+cp_type_print_derivation_info PARAMS ((FILE *, struct type *));
+
+void
+c_type_print_varspec_prefix PARAMS ((struct type *, FILE *, int, int));
+
+void
+c_type_print_base PARAMS ((struct type *, FILE *, int, int));
+
+
+/* Print a description of a type in the format of a
+ typedef for the current language.
+ NEW is the new name for a type TYPE. */
+
+void
+c_typedef_print (type, new, stream)
+ struct type *type;
+ struct symbol *new;
+ FILE *stream;
+{
+ switch (current_language->la_language)
+ {
+#ifdef _LANG_c
+ case language_c:
+ case language_cplus:
+ fprintf_filtered(stream, "typedef ");
+ type_print(type,"",stream,0);
+ if(TYPE_NAME ((SYMBOL_TYPE (new))) == 0
+ || !STREQ (TYPE_NAME ((SYMBOL_TYPE (new))), SYMBOL_NAME (new)))
+ fprintf_filtered(stream, " %s", SYMBOL_SOURCE_NAME(new));
+ break;
+#endif
+#ifdef _LANG_m2
+ case language_m2:
+ fprintf_filtered(stream, "TYPE ");
+ if(!TYPE_NAME(SYMBOL_TYPE(new)) ||
+ !STREQ (TYPE_NAME(SYMBOL_TYPE(new)), SYMBOL_NAME(new)))
+ fprintf_filtered(stream, "%s = ", SYMBOL_SOURCE_NAME(new));
+ else
+ fprintf_filtered(stream, "<builtin> = ");
+ type_print(type,"",stream,0);
+ break;
+#endif
+#ifdef _LANG_chill
+ case language_chill:
+ error ("Missing Chill support in function c_typedef_print."); /*FIXME*/
+#endif
+ default:
+ error("Language not supported.");
+ }
+ fprintf_filtered(stream, ";\n");
+}
+
+
+/* LEVEL is the depth to indent lines by. */
+
+void
+c_print_type (type, varstring, stream, show, level)
+ struct type *type;
+ char *varstring;
+ FILE *stream;
+ int show;
+ int level;
+{
+ register enum type_code code;
+ int demangled_args;
+
+ c_type_print_base (type, stream, show, level);
+ code = TYPE_CODE (type);
+ if ((varstring != NULL && *varstring != '\0')
+ ||
+ /* Need a space if going to print stars or brackets;
+ but not if we will print just a type name. */
+ ((show > 0 || TYPE_NAME (type) == 0)
+ &&
+ (code == TYPE_CODE_PTR || code == TYPE_CODE_FUNC
+ || code == TYPE_CODE_METHOD
+ || code == TYPE_CODE_ARRAY
+ || code == TYPE_CODE_MEMBER
+ || code == TYPE_CODE_REF)))
+ fputs_filtered (" ", stream);
+ c_type_print_varspec_prefix (type, stream, show, 0);
+
+ fputs_filtered (varstring, stream);
+
+ /* For demangled function names, we have the arglist as part of the name,
+ so don't print an additional pair of ()'s */
+
+ demangled_args = varstring[strlen(varstring) - 1] == ')';
+ c_type_print_varspec_suffix (type, stream, show, 0, demangled_args);
+
+}
+
+/* Print the C++ method arguments ARGS to the file STREAM. */
+
+void
+cp_type_print_method_args (args, prefix, varstring, staticp, stream)
+ struct type **args;
+ char *prefix;
+ char *varstring;
+ int staticp;
+ FILE *stream;
+{
+ int i;
+
+ fprintf_symbol_filtered (stream, prefix, language_cplus, DMGL_ANSI);
+ fprintf_symbol_filtered (stream, varstring, language_cplus, DMGL_ANSI);
+ fputs_filtered (" (", stream);
+ if (args && args[!staticp] && args[!staticp]->code != TYPE_CODE_VOID)
+ {
+ i = !staticp; /* skip the class variable */
+ while (1)
+ {
+ type_print (args[i++], "", stream, 0);
+ if (!args[i])
+ {
+ fprintf_filtered (stream, " ...");
+ break;
+ }
+ else if (args[i]->code != TYPE_CODE_VOID)
+ {
+ fprintf_filtered (stream, ", ");
+ }
+ else break;
+ }
+ }
+ fprintf_filtered (stream, ")");
+}
+
+/* If TYPE is a derived type, then print out derivation information.
+ Print only the actual base classes of this type, not the base classes
+ of the base classes. I.E. for the derivation hierarchy:
+
+ class A { int a; };
+ class B : public A {int b; };
+ class C : public B {int c; };
+
+ Print the type of class C as:
+
+ class C : public B {
+ int c;
+ }
+
+ Not as the following (like gdb used to), which is not legal C++ syntax for
+ derived types and may be confused with the multiple inheritance form:
+
+ class C : public B : public A {
+ int c;
+ }
+
+ In general, gdb should try to print the types as closely as possible to
+ the form that they appear in the source code. */
+
+static void
+cp_type_print_derivation_info (stream, type)
+ FILE *stream;
+ struct type *type;
+{
+ char *name;
+ int i;
+
+ for (i = 0; i < TYPE_N_BASECLASSES (type); i++)
+ {
+ fputs_filtered (i == 0 ? ": " : ", ", stream);
+ fprintf_filtered (stream, "%s%s ",
+ BASETYPE_VIA_PUBLIC (type, i) ? "public" : "private",
+ BASETYPE_VIA_VIRTUAL(type, i) ? " virtual" : "");
+ name = type_name_no_tag (TYPE_BASECLASS (type, i));
+ fprintf_filtered (stream, "%s", name ? name : "(null)");
+ }
+ if (i > 0)
+ {
+ fputs_filtered (" ", stream);
+ }
+}
+
+/* Print any asterisks or open-parentheses needed before the
+ variable name (to describe its type).
+
+ On outermost call, pass 0 for PASSED_A_PTR.
+ On outermost call, SHOW > 0 means should ignore
+ any typename for TYPE and show its details.
+ SHOW is always zero on recursive calls. */
+
+void
+c_type_print_varspec_prefix (type, stream, show, passed_a_ptr)
+ struct type *type;
+ FILE *stream;
+ int show;
+ int passed_a_ptr;
+{
+ char *name;
+ if (type == 0)
+ return;
+
+ if (TYPE_NAME (type) && show <= 0)
+ return;
+
+ QUIT;
+
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_PTR:
+ c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, 1);
+ fprintf_filtered (stream, "*");
+ break;
+
+ case TYPE_CODE_MEMBER:
+ if (passed_a_ptr)
+ fprintf_filtered (stream, "(");
+ c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, 0);
+ fprintf_filtered (stream, " ");
+ name = type_name_no_tag (TYPE_DOMAIN_TYPE (type));
+ if (name)
+ fputs_filtered (name, stream);
+ else
+ c_type_print_base (TYPE_DOMAIN_TYPE (type), stream, 0, passed_a_ptr);
+ fprintf_filtered (stream, "::");
+ break;
+
+ case TYPE_CODE_METHOD:
+ if (passed_a_ptr)
+ fprintf (stream, "(");
+ c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, 0);
+ if (passed_a_ptr)
+ {
+ fprintf_filtered (stream, " ");
+ c_type_print_base (TYPE_DOMAIN_TYPE (type), stream, 0, passed_a_ptr);
+ fprintf_filtered (stream, "::");
+ }
+ break;
+
+ case TYPE_CODE_REF:
+ c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, 1);
+ fprintf_filtered (stream, "&");
+ break;
+
+ case TYPE_CODE_FUNC:
+ c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, 0);
+ if (passed_a_ptr)
+ fprintf_filtered (stream, "(");
+ break;
+
+ case TYPE_CODE_ARRAY:
+ c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, 0);
+ if (passed_a_ptr)
+ fprintf_filtered (stream, "(");
+ break;
+
+ case TYPE_CODE_UNDEF:
+ case TYPE_CODE_STRUCT:
+ case TYPE_CODE_UNION:
+ case TYPE_CODE_ENUM:
+ case TYPE_CODE_INT:
+ case TYPE_CODE_FLT:
+ case TYPE_CODE_VOID:
+ case TYPE_CODE_ERROR:
+ case TYPE_CODE_CHAR:
+ case TYPE_CODE_BOOL:
+ case TYPE_CODE_SET:
+ case TYPE_CODE_RANGE:
+ case TYPE_CODE_STRING:
+ case TYPE_CODE_BITSTRING:
+ /* These types need no prefix. They are listed here so that
+ gcc -Wall will reveal any types that haven't been handled. */
+ break;
+ }
+}
+
+static void
+c_type_print_args (type, stream)
+ struct type *type;
+ FILE *stream;
+{
+ int i;
+ struct type **args;
+
+ fprintf_filtered (stream, "(");
+ args = TYPE_ARG_TYPES (type);
+ if (args != NULL)
+ {
+ if (args[1] == NULL)
+ {
+ fprintf_filtered (stream, "...");
+ }
+ else
+ {
+ for (i = 1;
+ args[i] != NULL && args[i]->code != TYPE_CODE_VOID;
+ i++)
+ {
+ c_print_type (args[i], "", stream, -1, 0);
+ if (args[i+1] == NULL)
+ {
+ fprintf_filtered (stream, "...");
+ }
+ else if (args[i+1]->code != TYPE_CODE_VOID)
+ {
+ fprintf_filtered (stream, ",");
+ wrap_here (" ");
+ }
+ }
+ }
+ }
+ fprintf_filtered (stream, ")");
+}
+
+/* Print any array sizes, function arguments or close parentheses
+ needed after the variable name (to describe its type).
+ Args work like c_type_print_varspec_prefix. */
+
+static void
+c_type_print_varspec_suffix (type, stream, show, passed_a_ptr, demangled_args)
+ struct type *type;
+ FILE *stream;
+ int show;
+ int passed_a_ptr;
+ int demangled_args;
+{
+ if (type == 0)
+ return;
+
+ if (TYPE_NAME (type) && show <= 0)
+ return;
+
+ QUIT;
+
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_ARRAY:
+ if (passed_a_ptr)
+ fprintf_filtered (stream, ")");
+
+ fprintf_filtered (stream, "[");
+ if (TYPE_LENGTH (type) > 0 && TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > 0)
+ fprintf_filtered (stream, "%d",
+ (TYPE_LENGTH (type)
+ / TYPE_LENGTH (TYPE_TARGET_TYPE (type))));
+ fprintf_filtered (stream, "]");
+
+ c_type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, 0, 0);
+ break;
+
+ case TYPE_CODE_MEMBER:
+ if (passed_a_ptr)
+ fprintf_filtered (stream, ")");
+ c_type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, 0, 0);
+ break;
+
+ case TYPE_CODE_METHOD:
+ if (passed_a_ptr)
+ fprintf_filtered (stream, ")");
+ c_type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, 0, 0);
+ if (passed_a_ptr)
+ {
+ c_type_print_args (type, stream);
+ }
+ break;
+
+ case TYPE_CODE_PTR:
+ case TYPE_CODE_REF:
+ c_type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, 1, 0);
+ break;
+
+ case TYPE_CODE_FUNC:
+ c_type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0,
+ passed_a_ptr, 0);
+ if (passed_a_ptr)
+ fprintf_filtered (stream, ")");
+ if (!demangled_args)
+ fprintf_filtered (stream, "()");
+ break;
+
+ case TYPE_CODE_UNDEF:
+ case TYPE_CODE_STRUCT:
+ case TYPE_CODE_UNION:
+ case TYPE_CODE_ENUM:
+ case TYPE_CODE_INT:
+ case TYPE_CODE_FLT:
+ case TYPE_CODE_VOID:
+ case TYPE_CODE_ERROR:
+ case TYPE_CODE_CHAR:
+ case TYPE_CODE_BOOL:
+ case TYPE_CODE_SET:
+ case TYPE_CODE_RANGE:
+ case TYPE_CODE_STRING:
+ case TYPE_CODE_BITSTRING:
+ /* These types do not need a suffix. They are listed so that
+ gcc -Wall will report types that may not have been considered. */
+ break;
+ }
+}
+
+/* Print the name of the type (or the ultimate pointer target,
+ function value or array element), or the description of a
+ structure or union.
+
+ SHOW positive means print details about the type (e.g. enum values),
+ and print structure elements passing SHOW - 1 for show.
+ SHOW zero means just print the type name or struct tag if there is one.
+ If there is no name, print something sensible but concise like
+ "struct {...}".
+ SHOW negative means the same things as SHOW zero. The difference is that
+ zero is used for printing structure elements and -1 is used for the
+ "whatis" command. But I don't see any need to distinguish.
+
+ LEVEL is the number of spaces to indent by.
+ We increase it for some recursive calls. */
+
+void
+c_type_print_base (type, stream, show, level)
+ struct type *type;
+ FILE *stream;
+ int show;
+ int level;
+{
+ register int i;
+ register int len;
+ register int lastval;
+ char *mangled_name;
+ char *demangled_name;
+ enum {s_none, s_public, s_private, s_protected} section_type;
+ QUIT;
+
+ wrap_here (" ");
+ if (type == NULL)
+ {
+ fputs_filtered ("<type unknown>", stream);
+ return;
+ }
+
+ /* When SHOW is zero or less, and there is a valid type name, then always
+ just print the type name directly from the type. */
+ /* If we have "typedef struct foo {. . .} bar;" do we want to print it
+ as "struct foo" or as "bar"? Pick the latter, because C++ folk tend
+ to expect things like "class5 *foo" rather than "struct class5 *foo". */
+
+ if (show <= 0
+ && TYPE_NAME (type) != NULL)
+ {
+ fputs_filtered (TYPE_NAME (type), stream);
+ return;
+ }
+
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_ARRAY:
+ case TYPE_CODE_PTR:
+ case TYPE_CODE_MEMBER:
+ case TYPE_CODE_REF:
+ case TYPE_CODE_FUNC:
+ case TYPE_CODE_METHOD:
+ c_type_print_base (TYPE_TARGET_TYPE (type), stream, show, level);
+ break;
+
+ case TYPE_CODE_STRUCT:
+ if (HAVE_CPLUS_STRUCT (type))
+ {
+ fprintf_filtered (stream, "class ");
+ }
+ else
+ {
+ fprintf_filtered (stream, "struct ");
+ }
+ goto struct_union;
+
+ case TYPE_CODE_UNION:
+ fprintf_filtered (stream, "union ");
+
+ struct_union:
+ if (TYPE_TAG_NAME (type) != NULL)
+ {
+ fputs_filtered (TYPE_TAG_NAME (type), stream);
+ if (show > 0)
+ fputs_filtered (" ", stream);
+ }
+ wrap_here (" ");
+ if (show <= 0)
+ {
+ /* If we just printed a tag name, no need to print anything else. */
+ if (TYPE_TAG_NAME (type) == NULL)
+ fprintf_filtered (stream, "{...}");
+ }
+ else if (show > 0)
+ {
+ check_stub_type (type);
+
+ cp_type_print_derivation_info (stream, type);
+
+ fprintf_filtered (stream, "{\n");
+ if ((TYPE_NFIELDS (type) == 0) && (TYPE_NFN_FIELDS (type) == 0))
+ {
+ if (TYPE_FLAGS (type) & TYPE_FLAG_STUB)
+ fprintfi_filtered (level + 4, stream, "<incomplete type>\n");
+ else
+ fprintfi_filtered (level + 4, stream, "<no data fields>\n");
+ }
+
+ /* Start off with no specific section type, so we can print
+ one for the first field we find, and use that section type
+ thereafter until we find another type. */
+
+ section_type = s_none;
+
+ /* If there is a base class for this type,
+ do not print the field that it occupies. */
+
+ len = TYPE_NFIELDS (type);
+ for (i = TYPE_N_BASECLASSES (type); i < len; i++)
+ {
+ QUIT;
+ /* Don't print out virtual function table. */
+ if ((TYPE_FIELD_NAME (type, i))[5] == CPLUS_MARKER &&
+ !strncmp (TYPE_FIELD_NAME (type, i), "_vptr", 5))
+ continue;
+
+ /* If this is a C++ class we can print the various C++ section
+ labels. */
+
+ if (HAVE_CPLUS_STRUCT (type))
+ {
+ if (TYPE_FIELD_PROTECTED (type, i))
+ {
+ if (section_type != s_protected)
+ {
+ section_type = s_protected;
+ fprintfi_filtered (level + 2, stream,
+ "protected:\n");
+ }
+ }
+ else if (TYPE_FIELD_PRIVATE (type, i))
+ {
+ if (section_type != s_private)
+ {
+ section_type = s_private;
+ fprintfi_filtered (level + 2, stream, "private:\n");
+ }
+ }
+ else
+ {
+ if (section_type != s_public)
+ {
+ section_type = s_public;
+ fprintfi_filtered (level + 2, stream, "public:\n");
+ }
+ }
+ }
+
+ print_spaces_filtered (level + 4, stream);
+ if (TYPE_FIELD_STATIC (type, i))
+ {
+ fprintf_filtered (stream, "static ");
+ }
+ c_print_type (TYPE_FIELD_TYPE (type, i),
+ TYPE_FIELD_NAME (type, i),
+ stream, show - 1, level + 4);
+ if (!TYPE_FIELD_STATIC (type, i)
+ && TYPE_FIELD_PACKED (type, i))
+ {
+ /* It is a bitfield. This code does not attempt
+ to look at the bitpos and reconstruct filler,
+ unnamed fields. This would lead to misleading
+ results if the compiler does not put out fields
+ for such things (I don't know what it does). */
+ fprintf_filtered (stream, " : %d",
+ TYPE_FIELD_BITSIZE (type, i));
+ }
+ fprintf_filtered (stream, ";\n");
+ }
+
+ /* If there are both fields and methods, put a space between. */
+ len = TYPE_NFN_FIELDS (type);
+ if (len && section_type != s_none)
+ fprintf_filtered (stream, "\n");
+
+ /* C++: print out the methods */
+
+ for (i = 0; i < len; i++)
+ {
+ struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i);
+ int j, len2 = TYPE_FN_FIELDLIST_LENGTH (type, i);
+ char *method_name = TYPE_FN_FIELDLIST_NAME (type, i);
+ char *name = type_name_no_tag (type);
+ int is_constructor = name && STREQ(method_name, name);
+ for (j = 0; j < len2; j++)
+ {
+ QUIT;
+ if (TYPE_FN_FIELD_PROTECTED (f, j))
+ {
+ if (section_type != s_protected)
+ {
+ section_type = s_protected;
+ fprintfi_filtered (level + 2, stream,
+ "protected:\n");
+ }
+ }
+ else if (TYPE_FN_FIELD_PRIVATE (f, j))
+ {
+ if (section_type != s_private)
+ {
+ section_type = s_private;
+ fprintfi_filtered (level + 2, stream, "private:\n");
+ }
+ }
+ else
+ {
+ if (section_type != s_public)
+ {
+ section_type = s_public;
+ fprintfi_filtered (level + 2, stream, "public:\n");
+ }
+ }
+
+ print_spaces_filtered (level + 4, stream);
+ if (TYPE_FN_FIELD_VIRTUAL_P (f, j))
+ fprintf_filtered (stream, "virtual ");
+ else if (TYPE_FN_FIELD_STATIC_P (f, j))
+ fprintf_filtered (stream, "static ");
+ if (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)) == 0)
+ {
+ /* Keep GDB from crashing here. */
+ fprintf (stream, "<undefined type> %s;\n",
+ TYPE_FN_FIELD_PHYSNAME (f, j));
+ break;
+ }
+ else if (!is_constructor)
+ {
+ type_print (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)),
+ "", stream, 0);
+ fputs_filtered (" ", stream);
+ }
+ if (TYPE_FN_FIELD_STUB (f, j))
+ {
+ /* Build something we can demangle. */
+ mangled_name = gdb_mangle_name (type, i, j);
+ demangled_name =
+ cplus_demangle (mangled_name,
+ DMGL_ANSI | DMGL_PARAMS);
+ if (demangled_name == NULL)
+ fprintf_filtered (stream, "<badly mangled name %s>",
+ mangled_name);
+ else
+ {
+ char *demangled_no_class =
+ strchr (demangled_name, ':');
+
+ if (demangled_no_class == NULL)
+ demangled_no_class = demangled_name;
+ else
+ {
+ if (*++demangled_no_class == ':')
+ ++demangled_no_class;
+ }
+ fputs_filtered (demangled_no_class, stream);
+ free (demangled_name);
+ }
+ free (mangled_name);
+ }
+ else if (TYPE_FN_FIELD_PHYSNAME (f, j)[0] == '_'
+ && TYPE_FN_FIELD_PHYSNAME (f, j)[1] == CPLUS_MARKER)
+ cp_type_print_method_args (TYPE_FN_FIELD_ARGS (f, j) + 1,
+ "~", method_name, 0, stream);
+ else
+ cp_type_print_method_args (TYPE_FN_FIELD_ARGS (f, j), "",
+ method_name,
+ TYPE_FN_FIELD_STATIC_P (f, j),
+ stream);
+
+ fprintf_filtered (stream, ";\n");
+ }
+ }
+
+ fprintfi_filtered (level, stream, "}");
+ }
+ break;
+
+ case TYPE_CODE_ENUM:
+ fprintf_filtered (stream, "enum ");
+ if (TYPE_TAG_NAME (type) != NULL)
+ {
+ fputs_filtered (TYPE_TAG_NAME (type), stream);
+ if (show > 0)
+ fputs_filtered (" ", stream);
+ }
+
+ wrap_here (" ");
+ if (show <= 0)
+ {
+ /* If we just printed a tag name, no need to print anything else. */
+ if (TYPE_TAG_NAME (type) == NULL)
+ fprintf_filtered (stream, "{...}");
+ }
+ else if (show > 0)
+ {
+ fprintf_filtered (stream, "{");
+ len = TYPE_NFIELDS (type);
+ lastval = 0;
+ for (i = 0; i < len; i++)
+ {
+ QUIT;
+ if (i) fprintf_filtered (stream, ", ");
+ wrap_here (" ");
+ fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
+ if (lastval != TYPE_FIELD_BITPOS (type, i))
+ {
+ fprintf_filtered (stream, " = %d", TYPE_FIELD_BITPOS (type, i));
+ lastval = TYPE_FIELD_BITPOS (type, i);
+ }
+ lastval++;
+ }
+ fprintf_filtered (stream, "}");
+ }
+ break;
+
+ case TYPE_CODE_VOID:
+ fprintf_filtered (stream, "void");
+ break;
+
+ case TYPE_CODE_UNDEF:
+ fprintf_filtered (stream, "struct <unknown>");
+ break;
+
+ case TYPE_CODE_ERROR:
+ fprintf_filtered (stream, "<unknown type>");
+ break;
+
+ case TYPE_CODE_RANGE:
+ /* This should not occur */
+ fprintf_filtered (stream, "<range type>");
+ break;
+
+ default:
+ /* Handle types not explicitly handled by the other cases,
+ such as fundamental types. For these, just print whatever
+ the type name is, as recorded in the type itself. If there
+ is no type name, then complain. */
+ if (TYPE_NAME (type) != NULL)
+ {
+ fputs_filtered (TYPE_NAME (type), stream);
+ }
+ else
+ {
+ /* At least for dump_symtab, it is important that this not be
+ an error (). */
+ fprintf_filtered (stream, "<invalid type code %d>",
+ TYPE_CODE (type));
+ }
+ break;
+ }
+}
+
diff --git a/gnu/usr.bin/gdb/gdb/c-valprint.c b/gnu/usr.bin/gdb/gdb/c-valprint.c
new file mode 100644
index 0000000..bcf0a28
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/c-valprint.c
@@ -0,0 +1,416 @@
+/* Support for printing C values for GDB, the GNU debugger.
+ Copyright 1986, 1988, 1989, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "expression.h"
+#include "value.h"
+#include "demangle.h"
+#include "valprint.h"
+#include "language.h"
+
+/* BEGIN-FIXME */
+
+extern int vtblprint; /* Controls printing of vtbl's */
+extern int demangle; /* whether to print C++ syms raw or src-form */
+
+extern void
+cp_print_class_member PARAMS ((char *, struct type *, FILE *, char *));
+
+extern void
+cp_print_class_method PARAMS ((char *, struct type *, FILE *));
+
+extern void
+cp_print_value_fields PARAMS ((struct type *, char *, FILE *, int, int,
+ enum val_prettyprint, struct type **));
+
+extern int
+cp_is_vtbl_ptr_type PARAMS ((struct type *));
+
+extern int
+cp_is_vtbl_member PARAMS ((struct type *));
+
+/* END-FIXME */
+
+
+/* BEGIN-FIXME: Hooks into c-typeprint.c */
+
+extern void
+c_type_print_varspec_prefix PARAMS ((struct type *, FILE *, int, int));
+
+extern void
+cp_type_print_method_args PARAMS ((struct type **, char *, char *, int,
+ FILE *));
+/* END-FIXME */
+
+
+extern struct obstack dont_print_obstack;
+
+
+/* Print data of type TYPE located at VALADDR (within GDB), which came from
+ the inferior at address ADDRESS, onto stdio stream STREAM according to
+ FORMAT (a letter or 0 for natural format). The data at VALADDR is in
+ target byte order.
+
+ If the data are a string pointer, returns the number of string characters
+ printed.
+
+ If DEREF_REF is nonzero, then dereference references, otherwise just print
+ them like pointers.
+
+ The PRETTY parameter controls prettyprinting. */
+
+int
+c_val_print (type, valaddr, address, stream, format, deref_ref, recurse,
+ pretty)
+ struct type *type;
+ char *valaddr;
+ CORE_ADDR address;
+ FILE *stream;
+ int format;
+ int deref_ref;
+ int recurse;
+ enum val_prettyprint pretty;
+{
+ register unsigned int i = 0; /* Number of characters printed */
+ unsigned len;
+ struct type *elttype;
+ unsigned eltlen;
+ LONGEST val;
+ CORE_ADDR addr;
+
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_ARRAY:
+ if (TYPE_LENGTH (type) > 0 && TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > 0)
+ {
+ elttype = TYPE_TARGET_TYPE (type);
+ eltlen = TYPE_LENGTH (elttype);
+ len = TYPE_LENGTH (type) / eltlen;
+ if (prettyprint_arrays)
+ {
+ print_spaces_filtered (2 + 2 * recurse, stream);
+ }
+ /* For an array of chars, print with string syntax. */
+ if (eltlen == 1 && TYPE_CODE (elttype) == TYPE_CODE_INT
+ && (format == 0 || format == 's'))
+ {
+ LA_PRINT_STRING (stream, valaddr, len, 0);
+ i = len;
+ }
+ else
+ {
+ fprintf_filtered (stream, "{");
+ /* If this is a virtual function table, print the 0th
+ entry specially, and the rest of the members normally. */
+ if (cp_is_vtbl_ptr_type (elttype))
+ {
+ i = 1;
+ fprintf_filtered (stream, "%d vtable entries", len - 1);
+ }
+ else
+ {
+ i = 0;
+ }
+ val_print_array_elements (type, valaddr, address, stream,
+ format, deref_ref, recurse, pretty, i);
+ fprintf_filtered (stream, "}");
+ }
+ break;
+ }
+ /* Array of unspecified length: treat like pointer to first elt. */
+ addr = address;
+ goto print_unpacked_pointer;
+
+ case TYPE_CODE_PTR:
+ if (format && format != 's')
+ {
+ print_scalar_formatted (valaddr, type, format, 0, stream);
+ break;
+ }
+ if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_METHOD)
+ {
+ cp_print_class_method (valaddr, type, stream);
+ }
+ else if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_MEMBER)
+ {
+ cp_print_class_member (valaddr,
+ TYPE_DOMAIN_TYPE (TYPE_TARGET_TYPE (type)),
+ stream, "&");
+ }
+ else
+ {
+ addr = unpack_pointer (type, valaddr);
+ print_unpacked_pointer:
+ elttype = TYPE_TARGET_TYPE (type);
+
+ if (TYPE_CODE (elttype) == TYPE_CODE_FUNC)
+ {
+ /* Try to print what function it points to. */
+ print_address_demangle (addr, stream, demangle);
+ /* Return value is irrelevant except for string pointers. */
+ return (0);
+ }
+
+ if (addressprint && format != 's')
+ {
+ fprintf_filtered (stream, "0x%lx", (unsigned long)addr);
+ }
+
+ /* For a pointer to char or unsigned char, also print the string
+ pointed to, unless pointer is null. */
+ if (TYPE_LENGTH (elttype) == 1
+ && TYPE_CODE (elttype) == TYPE_CODE_INT
+ && (format == 0 || format == 's')
+ && addr != 0)
+ {
+ i = val_print_string (addr, 0, stream);
+ }
+ else if (cp_is_vtbl_member(type))
+ {
+ /* print vtbl's nicely */
+ CORE_ADDR vt_address = unpack_pointer (type, valaddr);
+
+ struct minimal_symbol *msymbol =
+ lookup_minimal_symbol_by_pc (vt_address);
+ if ((msymbol != NULL) &&
+ (vt_address == SYMBOL_VALUE_ADDRESS (msymbol)))
+ {
+ fputs_filtered (" <", stream);
+ fputs_filtered (SYMBOL_SOURCE_NAME (msymbol), stream);
+ fputs_filtered (">", stream);
+ }
+ if (vtblprint)
+ {
+ value vt_val;
+ struct symbol *wsym = (struct symbol *)NULL;
+ struct type *wtype;
+ struct symtab *s;
+ struct block *block = (struct block *)NULL;
+ int is_this_fld;
+
+
+ wsym = lookup_symbol (SYMBOL_NAME(msymbol), block,
+ VAR_NAMESPACE, &is_this_fld, &s);
+
+ if (wsym)
+ {
+ wtype = SYMBOL_TYPE(wsym);
+ }
+ else
+ {
+ wtype = TYPE_TARGET_TYPE(type);
+ }
+ vt_val = value_at (wtype, vt_address);
+ val_print (VALUE_TYPE (vt_val), VALUE_CONTENTS (vt_val),
+ VALUE_ADDRESS (vt_val), stream, format,
+ deref_ref, recurse + 1, pretty);
+ if (pretty)
+ {
+ fprintf_filtered (stream, "\n");
+ print_spaces_filtered (2 + 2 * recurse, stream);
+ }
+ }
+ }
+
+ /* Return number of characters printed, plus one for the
+ terminating null if we have "reached the end". */
+ return (i + (print_max && i != print_max));
+ }
+ break;
+
+ case TYPE_CODE_MEMBER:
+ error ("not implemented: member type in c_val_print");
+ break;
+
+ case TYPE_CODE_REF:
+ if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_MEMBER)
+ {
+ cp_print_class_member (valaddr,
+ TYPE_DOMAIN_TYPE (TYPE_TARGET_TYPE (type)),
+ stream, "");
+ break;
+ }
+ if (addressprint)
+ {
+ fprintf_filtered (stream, "@0x%lx",
+ unpack_long (builtin_type_int, valaddr));
+ if (deref_ref)
+ fputs_filtered (": ", stream);
+ }
+ /* De-reference the reference. */
+ if (deref_ref)
+ {
+ if (TYPE_CODE (TYPE_TARGET_TYPE (type)) != TYPE_CODE_UNDEF)
+ {
+ value deref_val =
+ value_at
+ (TYPE_TARGET_TYPE (type),
+ unpack_pointer (lookup_pointer_type (builtin_type_void),
+ valaddr));
+ val_print (VALUE_TYPE (deref_val),
+ VALUE_CONTENTS (deref_val),
+ VALUE_ADDRESS (deref_val), stream, format,
+ deref_ref, recurse + 1, pretty);
+ }
+ else
+ fputs_filtered ("???", stream);
+ }
+ break;
+
+ case TYPE_CODE_UNION:
+ if (recurse && !unionprint)
+ {
+ fprintf_filtered (stream, "{...}");
+ break;
+ }
+ /* Fall through. */
+ case TYPE_CODE_STRUCT:
+ if (vtblprint && cp_is_vtbl_ptr_type(type))
+ {
+ /* Print the unmangled name if desired. */
+ print_address_demangle(*((int *) (valaddr + /* FIXME bytesex */
+ TYPE_FIELD_BITPOS (type, VTBL_FNADDR_OFFSET) / 8)),
+ stream, demangle);
+ break;
+ }
+ cp_print_value_fields (type, valaddr, stream, format, recurse, pretty,
+ 0);
+ break;
+
+ case TYPE_CODE_ENUM:
+ if (format)
+ {
+ print_scalar_formatted (valaddr, type, format, 0, stream);
+ break;
+ }
+ len = TYPE_NFIELDS (type);
+ val = unpack_long (type, valaddr);
+ for (i = 0; i < len; i++)
+ {
+ QUIT;
+ if (val == TYPE_FIELD_BITPOS (type, i))
+ {
+ break;
+ }
+ }
+ if (i < len)
+ {
+ fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
+ }
+ else
+ {
+ print_longest (stream, 'd', 0, val);
+ }
+ break;
+
+ case TYPE_CODE_FUNC:
+ if (format)
+ {
+ print_scalar_formatted (valaddr, type, format, 0, stream);
+ break;
+ }
+ /* FIXME, we should consider, at least for ANSI C language, eliminating
+ the distinction made between FUNCs and POINTERs to FUNCs. */
+ fprintf_filtered (stream, "{");
+ type_print (type, "", stream, -1);
+ fprintf_filtered (stream, "} ");
+ /* Try to print what function it points to, and its address. */
+ print_address_demangle (address, stream, demangle);
+ break;
+
+ case TYPE_CODE_BOOL:
+ /* Do something at least vaguely reasonable, for example if the
+ language is set wrong. */
+
+ case TYPE_CODE_INT:
+ format = format ? format : output_format;
+ if (format)
+ {
+ print_scalar_formatted (valaddr, type, format, 0, stream);
+ }
+ else
+ {
+ val_print_type_code_int (type, valaddr, stream);
+ /* C and C++ has no single byte int type, char is used instead.
+ Since we don't know whether the value is really intended to
+ be used as an integer or a character, print the character
+ equivalent as well. */
+ if (TYPE_LENGTH (type) == 1)
+ {
+ fputs_filtered (" ", stream);
+ LA_PRINT_CHAR ((unsigned char) unpack_long (type, valaddr),
+ stream);
+ }
+ }
+ break;
+
+ case TYPE_CODE_CHAR:
+ format = format ? format : output_format;
+ if (format)
+ {
+ print_scalar_formatted (valaddr, type, format, 0, stream);
+ }
+ else
+ {
+ fprintf_filtered (stream, TYPE_UNSIGNED (type) ? "%u" : "%d",
+ unpack_long (type, valaddr));
+ fputs_filtered (" ", stream);
+ LA_PRINT_CHAR ((unsigned char) unpack_long (type, valaddr), stream);
+ }
+ break;
+
+ case TYPE_CODE_FLT:
+ if (format)
+ {
+ print_scalar_formatted (valaddr, type, format, 0, stream);
+ }
+ else
+ {
+ print_floating (valaddr, type, stream);
+ }
+ break;
+
+ case TYPE_CODE_VOID:
+ fprintf_filtered (stream, "void");
+ break;
+
+ case TYPE_CODE_ERROR:
+ fprintf_filtered (stream, "<error type>");
+ break;
+
+ case TYPE_CODE_RANGE:
+ /* FIXME, we should not ever have to print one of these yet. */
+ fprintf_filtered (stream, "<range type>");
+ break;
+
+ case TYPE_CODE_UNDEF:
+ /* This happens (without TYPE_FLAG_STUB set) on systems which don't use
+ dbx xrefs (NO_DBX_XREFS in gcc) if a file has a "struct foo *bar"
+ and no complete type for struct foo in that file. */
+ fprintf_filtered (stream, "<incomplete type>");
+ break;
+
+ default:
+ error ("Invalid C/C++ type code %d in symbol table.", TYPE_CODE (type));
+ }
+ fflush (stream);
+ return (0);
+}
diff --git a/gnu/usr.bin/gdb/gdb/call-cmds.h b/gnu/usr.bin/gdb/gdb/call-cmds.h
new file mode 100644
index 0000000..9030d84
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/call-cmds.h
@@ -0,0 +1,28 @@
+/* Prototypes for GDB commands that are called internally by other functions.
+ Copyright 1992 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+extern void
+initialize_all_files PARAMS ((void));
+
+extern void
+exec_file_command PARAMS ((char *, int));
+
+extern void
+core_file_command PARAMS ((char *, int));
+
+extern void
+break_command PARAMS ((char *, int));
diff --git a/gnu/usr.bin/gdb/gdb/ch-exp.tab.c b/gnu/usr.bin/gdb/gdb/ch-exp.tab.c
new file mode 100644
index 0000000..7adab65
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/ch-exp.tab.c
@@ -0,0 +1,2854 @@
+#ifndef lint
+static char yysccsid[] = "@(#)yaccpar 1.9 (Berkeley) 02/21/93";
+#endif
+#define YYBYACC 1
+#define YYMAJOR 1
+#define YYMINOR 9
+#define yyclearin (yychar=(-1))
+#define yyerrok (yyerrflag=0)
+#define YYRECOVERING (yyerrflag!=0)
+#define yyparse ch_parse
+#define yylex ch_lex
+#define yyerror ch_error
+#define yychar ch_char
+#define yyval ch_val
+#define yylval ch_lval
+#define yydebug ch_debug
+#define yynerrs ch_nerrs
+#define yyerrflag ch_errflag
+#define yyss ch_ss
+#define yyssp ch_ssp
+#define yyvs ch_vs
+#define yyvsp ch_vsp
+#define yylhs ch_lhs
+#define yylen ch_len
+#define yydefred ch_defred
+#define yydgoto ch_dgoto
+#define yysindex ch_sindex
+#define yyrindex ch_rindex
+#define yygindex ch_gindex
+#define yytable ch_table
+#define yycheck ch_check
+#define yyname ch_name
+#define yyrule ch_rule
+#define YYPREFIX "ch_"
+#line 55 "./ch-exp.y"
+
+#include "defs.h"
+#include <ctype.h>
+#include "expression.h"
+#include "language.h"
+#include "value.h"
+#include "parser-defs.h"
+#include "ch-lang.h"
+
+/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
+ as well as gratuitiously global symbol names, so we can have multiple
+ yacc generated parsers in gdb. Note that these are only the variables
+ produced by yacc. If other parser generators (bison, byacc, etc) produce
+ additional global names that conflict at link time, then those parser
+ generators need to be fixed instead of adding those names to this list. */
+
+#define yymaxdepth chill_maxdepth
+#define yyparse chill_parse
+#define yylex chill_lex
+#define yyerror chill_error
+#define yylval chill_lval
+#define yychar chill_char
+#define yydebug chill_debug
+#define yypact chill_pact
+#define yyr1 chill_r1
+#define yyr2 chill_r2
+#define yydef chill_def
+#define yychk chill_chk
+#define yypgo chill_pgo
+#define yyact chill_act
+#define yyexca chill_exca
+#define yyerrflag chill_errflag
+#define yynerrs chill_nerrs
+#define yyps chill_ps
+#define yypv chill_pv
+#define yys chill_s
+#define yy_yys chill_yys
+#define yystate chill_state
+#define yytmp chill_tmp
+#define yyv chill_v
+#define yy_yyv chill_yyv
+#define yyval chill_val
+#define yylloc chill_lloc
+#define yyreds chill_reds /* With YYDEBUG defined */
+#define yytoks chill_toks /* With YYDEBUG defined */
+
+#ifndef YYDEBUG
+#define YYDEBUG 0 /* Default to no yydebug support */
+#endif
+
+int
+yyparse PARAMS ((void));
+
+static int
+yylex PARAMS ((void));
+
+void
+yyerror PARAMS ((char *));
+
+#line 120 "./ch-exp.y"
+typedef union
+ {
+ LONGEST lval;
+ unsigned LONGEST ulval;
+ struct {
+ LONGEST val;
+ struct type *type;
+ } typed_val;
+ double dval;
+ struct symbol *sym;
+ struct type *tval;
+ struct stoken sval;
+ struct ttype tsym;
+ struct symtoken ssym;
+ int voidval;
+ struct block *bval;
+ enum exp_opcode opcode;
+ struct internalvar *ivar;
+
+ struct type **tvec;
+ int *ivec;
+ } YYSTYPE;
+#line 119 "y.tab.c"
+#define FIXME_01 257
+#define FIXME_02 258
+#define FIXME_03 259
+#define FIXME_04 260
+#define FIXME_05 261
+#define FIXME_06 262
+#define FIXME_07 263
+#define FIXME_08 264
+#define FIXME_09 265
+#define FIXME_10 266
+#define FIXME_11 267
+#define FIXME_12 268
+#define FIXME_13 269
+#define FIXME_14 270
+#define FIXME_15 271
+#define FIXME_16 272
+#define FIXME_17 273
+#define FIXME_18 274
+#define FIXME_19 275
+#define FIXME_20 276
+#define FIXME_21 277
+#define FIXME_22 278
+#define FIXME_24 279
+#define FIXME_25 280
+#define FIXME_26 281
+#define FIXME_27 282
+#define FIXME_28 283
+#define FIXME_29 284
+#define FIXME_30 285
+#define INTEGER_LITERAL 286
+#define BOOLEAN_LITERAL 287
+#define CHARACTER_LITERAL 288
+#define FLOAT_LITERAL 289
+#define GENERAL_PROCEDURE_NAME 290
+#define LOCATION_NAME 291
+#define SET_LITERAL 292
+#define EMPTINESS_LITERAL 293
+#define CHARACTER_STRING_LITERAL 294
+#define BIT_STRING_LITERAL 295
+#define TYPENAME 296
+#define FIELD_NAME 297
+#define CASE 298
+#define OF 299
+#define ESAC 300
+#define LOGIOR 301
+#define ORIF 302
+#define LOGXOR 303
+#define LOGAND 304
+#define ANDIF 305
+#define NOTEQUAL 306
+#define GTR 307
+#define LEQ 308
+#define IN 309
+#define SLASH_SLASH 310
+#define MOD 311
+#define REM 312
+#define NOT 313
+#define POINTER 314
+#define RECEIVE 315
+#define UP 316
+#define IF 317
+#define THEN 318
+#define ELSE 319
+#define FI 320
+#define ELSIF 321
+#define ILLEGAL_TOKEN 322
+#define NUM 323
+#define PRED 324
+#define SUCC 325
+#define ABS 326
+#define CARD 327
+#define MAX_TOKEN 328
+#define MIN_TOKEN 329
+#define SIZE 330
+#define UPPER 331
+#define LOWER 332
+#define LENGTH 333
+#define GDB_REGNAME 334
+#define GDB_LAST 335
+#define GDB_VARIABLE 336
+#define GDB_ASSIGNMENT 337
+#define YYERRCODE 256
+short ch_lhs[] = { -1,
+ 0, 0, 20, 20, 21, 1, 1, 2, 2, 2,
+ 2, 2, 45, 45, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 4,
+ 5, 5, 5, 5, 5, 6, 6, 6, 6, 6,
+ 6, 6, 6, 7, 8, 9, 9, 62, 10, 11,
+ 11, 12, 13, 14, 15, 17, 18, 19, 22, 22,
+ 22, 23, 23, 24, 25, 25, 26, 27, 28, 28,
+ 28, 28, 29, 29, 29, 30, 30, 30, 30, 30,
+ 30, 30, 30, 31, 31, 31, 31, 32, 32, 32,
+ 32, 32, 33, 33, 33, 33, 34, 34, 34, 60,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 49, 49, 49, 49, 61, 50, 50, 51,
+ 44, 52, 53, 54, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 46, 47, 48, 55, 56, 57, 58,
+ 59,
+};
+short ch_len[] = { 2,
+ 1, 1, 1, 1, 1, 1, 2, 1, 1, 1,
+ 1, 1, 1, 3, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 4, 6, 6, 0, 5, 6,
+ 6, 2, 2, 1, 1, 1, 1, 3, 1, 1,
+ 1, 5, 9, 2, 2, 4, 1, 4, 1, 3,
+ 3, 3, 1, 3, 3, 1, 3, 3, 3, 3,
+ 3, 3, 3, 1, 3, 3, 3, 1, 3, 3,
+ 3, 3, 1, 2, 2, 2, 2, 2, 1, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 1, 4, 4, 4, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1,
+};
+short ch_defred[] = { 0,
+ 5, 12, 44, 54, 56, 57, 125, 126, 127, 128,
+ 129, 36, 37, 38, 39, 35, 8, 40, 41, 42,
+ 43, 117, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 10, 9, 11, 0, 0, 6, 0, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 55,
+ 27, 28, 0, 1, 4, 3, 61, 0, 0, 0,
+ 0, 0, 88, 93, 31, 32, 33, 34, 0, 0,
+ 60, 0, 138, 0, 30, 29, 94, 0, 95, 0,
+ 0, 141, 98, 0, 137, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 52, 7,
+ 96, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 53, 0, 58, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 122, 123, 124, 0, 0, 0,
+ 0, 0, 0, 118, 0, 0, 0, 120, 0, 100,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 89, 90, 91, 92, 130,
+ 131, 0, 0, 134, 136, 0, 0, 0, 140, 0,
+ 0, 139, 64, 0, 0, 0, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 0, 0, 0, 110, 111,
+ 112, 45, 0, 0, 0, 0, 13, 0, 0, 0,
+ 65, 0, 62, 0, 0, 0, 133, 0, 132, 0,
+ 135, 0, 0, 49, 0, 0, 67, 0, 0, 114,
+ 115, 116, 47, 46, 50, 51, 14, 0, 68, 66,
+ 0, 63,
+};
+short ch_dgoto[] = { 44,
+ 85, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 67, 137, 196, 238, 190, 68, 69, 70,
+ 71, 72, 73, 74, 75, 76, 77, 78, 79, 182,
+ 183, 230, 228, 80, 218, 186, 232, 187, 149, 155,
+ 159, 150, 151, 152, 96, 84, 193, 191, 93, 81,
+ 88, 188,
+};
+short ch_sindex[] = { 457,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, -262, 879, 879, 893, -215, 622, -258, 50,
+ 51, 52, 53, 56, 57, 58, 67, 72, 73, 75,
+ 0, 0, 0, 0, -221, 0, -293, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, -261, 0, 0, 0, 0, -248, -275, -44,
+ -30, -33, 0, 0, 0, 0, 0, 0, 88, 91,
+ 0, 92, 0, -165, 0, 0, 0, 92, 0, 0,
+ -293, 0, 0, 94, 0, -181, 622, 622, 622, 622,
+ 622, 622, 622, 636, 622, 622, 622, 457, 0, 0,
+ 0, 797, 797, 797, 797, 797, 797, 797, 797, 797,
+ 797, 797, 797, 797, 797, 797, 797, 797, 797, 797,
+ -233, -270, 0, -137, 0, -132, -272, 112, 118, 119,
+ 120, 121, 122, 128, 0, 0, 0, 129, 130, 132,
+ 141, 143, 92, 0, 144, 92, 146, 0, 149, 0,
+ -275, -275, -275, -44, -44, -30, -30, -30, -30, -30,
+ -30, -30, -33, -33, -33, 0, 0, 0, 0, 0,
+ 0, -39, 115, 0, 0, 126, -120, 622, 0, 106,
+ 142, 0, 0, -132, -258, -110, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 622, 622, 622, 0, 0,
+ 0, 0, -64, -61, -63, -64, 0, -34, -103, 622,
+ 0, -181, 0, 174, 181, -22, 0, 182, 0, 184,
+ 0, 192, 193, 0, 622, 622, 0, 177, -272, 0,
+ 0, 0, 0, 0, 0, 0, 0, 150, 0, 0,
+ -55, 0,
+};
+short ch_rindex[] = { 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0, 82, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 24, 0, 0, 0, 0, 506, 255, 191,
+ 145, 95, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 237, 0, 0, 0, 0, 0, 0, 0, 59,
+ 204, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 714, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, -40, 0, 0,
+ 0, 0, 205, 0, 0, 206, 0, 0, 0, 0,
+ 567, 573, 577, 531, 554, 168, 180, 208, 231, 500,
+ 522, 545, 105, 133, 158, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0,
+};
+short ch_gindex[] = { 0,
+ 1230, 0, -23, 0, 0, 185, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 26, 148,
+ 0, 1162, 0, 31, 15, 22, 0, 0, -56, -104,
+ -45, -37, -92, 55, 0, 0, 0, 0, 0, 0,
+ 0, 0, 43, 0, 63, 0, 0, 0, 0, 154,
+ 0, 0, 0, 0, 71, 0, 87, 0, 0, 0,
+ 5, 0,
+};
+#define YYTABLESIZE 1466
+short ch_table[] = { 30,
+ 30, 212, 91, 109, 82, 184, 234, 185, 127, 235,
+ 164, 165, 124, 128, 125, 121, 117, 119, 242, 83,
+ 110, 235, 95, 29, 12, 13, 14, 15, 115, 116,
+ 18, 19, 20, 21, 176, 177, 178, 179, 180, 181,
+ 30, 30, 30, 30, 30, 30, 194, 30, 195, 86,
+ 86, 86, 112, 113, 114, 161, 162, 163, 97, 30,
+ 30, 30, 30, 29, 29, 29, 29, 29, 29, 92,
+ 29, 166, 167, 168, 169, 170, 171, 172, 87, 89,
+ 91, 99, 29, 29, 29, 29, 173, 174, 175, 97,
+ 98, 99, 100, 30, 84, 101, 102, 103, 30, 97,
+ 97, 97, 97, 97, 85, 97, 104, 133, 153, 156,
+ 156, 105, 106, 133, 107, 108, 29, 97, 97, 97,
+ 97, 121, 99, 99, 99, 99, 99, 131, 99, 86,
+ 132, 28, 86, 134, 135, 84, 136, 84, 84, 84,
+ 99, 99, 99, 99, 76, 85, 189, 85, 85, 85,
+ 192, 97, 197, 84, 84, 84, 84, 87, 198, 199,
+ 200, 201, 202, 85, 85, 85, 85, 77, 203, 204,
+ 205, 206, 214, 86, 99, 86, 86, 86, 133, 78,
+ 207, 133, 208, 215, 209, 76, 210, 84, 76, 211,
+ 73, 86, 86, 86, 86, 216, 219, 85, 87, 220,
+ 87, 87, 87, 76, 76, 76, 76, 79, 77, 223,
+ 227, 77, 229, 231, 240, 236, 87, 87, 87, 87,
+ 78, 241, 243, 78, 244, 86, 77, 77, 77, 77,
+ 80, 73, 245, 246, 73, 249, 2, 76, 78, 78,
+ 78, 78, 251, 121, 252, 113, 119, 111, 79, 73,
+ 87, 79, 239, 250, 69, 160, 30, 248, 233, 157,
+ 77, 118, 120, 122, 123, 222, 79, 79, 79, 79,
+ 226, 80, 78, 30, 80, 0, 213, 129, 130, 126,
+ 221, 0, 0, 73, 0, 0, 0, 0, 0, 80,
+ 80, 80, 80, 0, 0, 69, 0, 30, 69, 0,
+ 79, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 69, 30, 0, 0, 0, 0, 0,
+ 29, 0, 0, 80, 29, 29, 29, 29, 29, 29,
+ 29, 29, 29, 29, 29, 29, 0, 29, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 69, 0, 0,
+ 0, 0, 0, 0, 0, 30, 0, 0, 0, 97,
+ 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
+ 97, 0, 30, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 0, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 86, 86, 86, 86, 86, 86, 86,
+ 86, 86, 86, 0, 0, 76, 76, 76, 76, 76,
+ 76, 76, 76, 76, 0, 0, 0, 0, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 0, 0, 0,
+ 78, 78, 78, 78, 78, 78, 78, 78, 78, 0,
+ 0, 73, 73, 73, 73, 73, 28, 0, 0, 81,
+ 0, 24, 0, 0, 0, 59, 0, 0, 79, 79,
+ 79, 79, 79, 79, 79, 79, 79, 0, 0, 0,
+ 0, 82, 0, 0, 0, 0, 0, 0, 0, 0,
+ 74, 80, 80, 80, 80, 80, 80, 80, 80, 80,
+ 81, 0, 0, 81, 83, 0, 59, 0, 0, 59,
+ 0, 0, 0, 75, 0, 69, 69, 69, 81, 81,
+ 81, 81, 82, 0, 59, 82, 70, 0, 0, 0,
+ 0, 74, 71, 0, 74, 0, 72, 0, 0, 0,
+ 82, 82, 82, 82, 0, 83, 0, 0, 83, 74,
+ 0, 0, 81, 0, 75, 0, 0, 75, 59, 0,
+ 0, 0, 0, 83, 83, 83, 83, 70, 0, 0,
+ 70, 0, 75, 71, 82, 0, 71, 72, 0, 0,
+ 72, 0, 0, 74, 0, 70, 0, 0, 0, 0,
+ 0, 71, 0, 0, 0, 72, 0, 83, 0, 0,
+ 0, 0, 0, 0, 0, 0, 75, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 70,
+ 0, 28, 0, 0, 0, 71, 24, 0, 0, 72,
+ 0, 0, 0, 0, 0, 28, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 2, 3, 4, 5, 6,
+ 0, 0, 0, 7, 8, 9, 10, 11, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 48, 23, 0, 0, 0, 48, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 25,
+ 26, 27, 0, 29, 0, 0, 0, 0, 0, 30,
+ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 0, 0, 0, 0, 0, 0, 0,
+ 81, 81, 81, 81, 81, 81, 81, 81, 81, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 82, 82, 82, 82, 82, 82, 82, 82,
+ 82, 74, 74, 74, 74, 74, 28, 0, 0, 0,
+ 0, 24, 0, 0, 0, 83, 83, 83, 83, 83,
+ 83, 83, 83, 83, 75, 75, 75, 75, 75, 0,
+ 0, 0, 0, 0, 0, 0, 0, 70, 70, 70,
+ 0, 0, 0, 71, 71, 71, 0, 72, 72, 72,
+ 2, 3, 4, 5, 6, 0, 0, 0, 7, 8,
+ 9, 10, 11, 0, 2, 3, 4, 5, 6, 145,
+ 146, 147, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 28, 23,
+ 0, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 28, 0, 25, 26, 27, 0, 29, 0,
+ 0, 0, 0, 0, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
+ 42, 43, 48, 48, 48, 48, 48, 0, 0, 0,
+ 48, 48, 48, 48, 48, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 0, 48, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 48, 48, 48, 0,
+ 48, 0, 0, 0, 0, 0, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 0, 0, 0, 0, 0, 2, 3, 4, 5, 6,
+ 0, 0, 0, 7, 8, 9, 10, 11, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 25,
+ 26, 27, 0, 0, 0, 0, 0, 0, 0, 30,
+ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 0, 0, 0, 0, 2, 3, 4,
+ 5, 6, 0, 0, 0, 7, 8, 9, 10, 11,
+ 0, 2, 3, 4, 5, 6, 0, 0, 0, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 0, 0, 0, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 94,
+ 0, 0, 26, 27, 0, 0, 0, 0, 0, 0,
+ 0, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 45,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 90, 0, 45, 138, 139,
+ 140, 141, 142, 143, 144, 0, 154, 154, 158, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 45, 45, 45, 45,
+ 45, 45, 45, 148, 45, 45, 45, 45, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 217,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 224, 225, 217,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 237, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 247, 237, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 45, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 45, 45, 45, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 45,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 45, 45,
+};
+short ch_check[] = { 40,
+ 0, 41, 26, 297, 0, 276, 41, 278, 42, 44,
+ 115, 116, 43, 47, 45, 60, 61, 62, 41, 282,
+ 314, 44, 281, 0, 286, 287, 288, 289, 304, 305,
+ 292, 293, 294, 295, 127, 128, 129, 130, 272, 273,
+ 40, 41, 42, 43, 44, 45, 319, 47, 321, 24,
+ 25, 26, 301, 302, 303, 112, 113, 114, 0, 59,
+ 60, 61, 62, 40, 41, 42, 43, 44, 45, 285,
+ 47, 117, 118, 119, 120, 121, 122, 123, 24, 25,
+ 104, 0, 59, 60, 61, 62, 124, 125, 126, 40,
+ 40, 40, 40, 93, 0, 40, 40, 40, 40, 41,
+ 42, 43, 44, 45, 0, 47, 40, 82, 104, 105,
+ 106, 40, 40, 88, 40, 337, 93, 59, 60, 61,
+ 62, 40, 41, 42, 43, 44, 45, 40, 47, 104,
+ 40, 40, 0, 299, 41, 41, 318, 43, 44, 45,
+ 59, 60, 61, 62, 0, 41, 284, 43, 44, 45,
+ 283, 93, 41, 59, 60, 61, 62, 0, 41, 41,
+ 41, 41, 41, 59, 60, 61, 62, 0, 41, 41,
+ 41, 40, 58, 41, 93, 43, 44, 45, 153, 0,
+ 40, 156, 40, 58, 41, 41, 41, 93, 44, 41,
+ 0, 59, 60, 61, 62, 316, 91, 93, 41, 58,
+ 43, 44, 45, 59, 60, 61, 62, 0, 41, 320,
+ 275, 44, 274, 277, 41, 319, 59, 60, 61, 62,
+ 41, 41, 41, 44, 41, 93, 59, 60, 61, 62,
+ 0, 41, 41, 41, 44, 59, 0, 93, 59, 60,
+ 61, 62, 93, 40, 300, 41, 41, 63, 41, 59,
+ 93, 44, 222, 239, 0, 108, 297, 236, 216, 106,
+ 93, 306, 307, 308, 309, 195, 59, 60, 61, 62,
+ 208, 41, 93, 314, 44, -1, 316, 311, 312, 310,
+ 194, -1, -1, 93, -1, -1, -1, -1, -1, 59,
+ 60, 61, 62, -1, -1, 41, -1, 297, 44, -1,
+ 93, 301, 302, 303, 304, 305, 306, 307, 308, 309,
+ 310, 311, 312, 59, 314, -1, -1, -1, -1, -1,
+ 297, -1, -1, 93, 301, 302, 303, 304, 305, 306,
+ 307, 308, 309, 310, 311, 312, -1, 314, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 93, -1, -1,
+ -1, -1, -1, -1, -1, 297, -1, -1, -1, 301,
+ 302, 303, 304, 305, 306, 307, 308, 309, 310, 311,
+ 312, -1, 314, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 301, 302, 303, 304, 305, 306, 307, 308,
+ 309, 310, 311, 312, -1, 301, 302, 303, 304, 305,
+ 306, 307, 308, 309, 310, 301, 302, 303, 304, 305,
+ 306, 307, 308, 309, 310, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 301, 302, 303, 304, 305, 306, 307,
+ 308, 309, 310, -1, -1, 301, 302, 303, 304, 305,
+ 306, 307, 308, 309, -1, -1, -1, -1, 301, 302,
+ 303, 304, 305, 306, 307, 308, 309, 310, 301, 302,
+ 303, 304, 305, 306, 307, 308, 309, -1, -1, -1,
+ 301, 302, 303, 304, 305, 306, 307, 308, 309, -1,
+ -1, 301, 302, 303, 304, 305, 40, -1, -1, 0,
+ -1, 45, -1, -1, -1, 0, -1, -1, 301, 302,
+ 303, 304, 305, 306, 307, 308, 309, -1, -1, -1,
+ -1, 0, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 301, 302, 303, 304, 305, 306, 307, 308, 309,
+ 41, -1, -1, 44, 0, -1, 41, -1, -1, 44,
+ -1, -1, -1, 0, -1, 301, 302, 303, 59, 60,
+ 61, 62, 41, -1, 59, 44, 0, -1, -1, -1,
+ -1, 41, 0, -1, 44, -1, 0, -1, -1, -1,
+ 59, 60, 61, 62, -1, 41, -1, -1, 44, 59,
+ -1, -1, 93, -1, 41, -1, -1, 44, 93, -1,
+ -1, -1, -1, 59, 60, 61, 62, 41, -1, -1,
+ 44, -1, 59, 41, 93, -1, 44, 41, -1, -1,
+ 44, -1, -1, 93, -1, 59, -1, -1, -1, -1,
+ -1, 59, -1, -1, -1, 59, -1, 93, -1, -1,
+ -1, -1, -1, -1, -1, -1, 93, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 93,
+ -1, 40, -1, -1, -1, 93, 45, -1, -1, 93,
+ -1, -1, -1, -1, -1, 40, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 257, -1, 259, 260, 261, 262, 263,
+ -1, -1, -1, 267, 268, 269, 270, 271, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 286, 287, 288, 289, 290, 291, 292, 293,
+ 294, 295, 296, 40, 298, -1, -1, -1, 45, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 313,
+ 314, 315, -1, 317, -1, -1, -1, -1, -1, 323,
+ 324, 325, 326, 327, 328, 329, 330, 331, 332, 333,
+ 334, 335, 336, -1, -1, -1, -1, -1, -1, -1,
+ 301, 302, 303, 304, 305, 306, 307, 308, 309, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 301, 302, 303, 304, 305, 306, 307, 308,
+ 309, 301, 302, 303, 304, 305, 40, -1, -1, -1,
+ -1, 45, -1, -1, -1, 301, 302, 303, 304, 305,
+ 306, 307, 308, 309, 301, 302, 303, 304, 305, -1,
+ -1, -1, -1, -1, -1, -1, -1, 301, 302, 303,
+ -1, -1, -1, 301, 302, 303, -1, 301, 302, 303,
+ 259, 260, 261, 262, 263, -1, -1, -1, 267, 268,
+ 269, 270, 271, -1, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 286, 287, 288,
+ 289, 290, 291, 292, 293, 294, 295, 296, 40, 298,
+ -1, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 40, -1, 313, 314, 315, -1, 317, -1,
+ -1, -1, -1, -1, 323, 324, 325, 326, 327, 328,
+ 329, 330, 331, 332, 333, 334, 335, 336, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 334,
+ 335, 336, 259, 260, 261, 262, 263, -1, -1, -1,
+ 267, 268, 269, 270, 271, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 286,
+ 287, 288, 289, 290, 291, 292, 293, 294, 295, 296,
+ -1, 298, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 313, 314, 315, -1,
+ 317, -1, -1, -1, -1, -1, 323, 324, 325, 326,
+ 327, 328, 329, 330, 331, 332, 333, 334, 335, 336,
+ -1, -1, -1, -1, -1, 259, 260, 261, 262, 263,
+ -1, -1, -1, 267, 268, 269, 270, 271, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 286, 287, 288, 289, 290, 291, 292, 293,
+ 294, 295, 296, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 313,
+ 314, 315, -1, -1, -1, -1, -1, -1, -1, 323,
+ 324, 325, 326, 327, 328, 329, 330, 331, 332, 333,
+ 334, 335, 336, -1, -1, -1, -1, 259, 260, 261,
+ 262, 263, -1, -1, -1, 267, 268, 269, 270, 271,
+ -1, 259, 260, 261, 262, 263, -1, -1, -1, 267,
+ 268, 269, 270, 271, 286, 287, 288, 289, 290, 291,
+ 292, 293, 294, 295, 296, -1, -1, -1, 286, 287,
+ 288, 289, 290, 291, 292, 293, 294, 295, 296, 28,
+ -1, -1, 314, 315, -1, -1, -1, -1, -1, -1,
+ -1, 323, 324, 325, 326, 327, 328, 329, 330, 331,
+ 332, 333, 334, 335, 336, 323, 324, 325, 326, 327,
+ 328, 329, 330, 331, 332, 333, 334, 335, 336, 0,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 26, -1, 28, 97, 98,
+ 99, 100, 101, 102, 103, -1, 105, 106, 107, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 97, 98, 99, 100,
+ 101, 102, 103, 104, 105, 106, 107, 108, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 188,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 206, 207, 208,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 220, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 235, 236, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 188, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 206, 207, 208, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 220,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 235, 236,
+};
+#define YYFINAL 44
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYMAXTOKEN 337
+#if YYDEBUG
+char *ch_name[] = {
+"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,"'('","')'","'*'","'+'","','","'-'","'.'","'/'",0,0,0,0,0,0,0,0,0,0,
+"':'","';'","'<'","'='","'>'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,"'['",0,"']'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,"FIXME_01","FIXME_02","FIXME_03","FIXME_04",
+"FIXME_05","FIXME_06","FIXME_07","FIXME_08","FIXME_09","FIXME_10","FIXME_11",
+"FIXME_12","FIXME_13","FIXME_14","FIXME_15","FIXME_16","FIXME_17","FIXME_18",
+"FIXME_19","FIXME_20","FIXME_21","FIXME_22","FIXME_24","FIXME_25","FIXME_26",
+"FIXME_27","FIXME_28","FIXME_29","FIXME_30","INTEGER_LITERAL","BOOLEAN_LITERAL",
+"CHARACTER_LITERAL","FLOAT_LITERAL","GENERAL_PROCEDURE_NAME","LOCATION_NAME",
+"SET_LITERAL","EMPTINESS_LITERAL","CHARACTER_STRING_LITERAL",
+"BIT_STRING_LITERAL","TYPENAME","FIELD_NAME","CASE","OF","ESAC","LOGIOR","ORIF",
+"LOGXOR","LOGAND","ANDIF","NOTEQUAL","GTR","LEQ","IN","SLASH_SLASH","MOD","REM",
+"NOT","POINTER","RECEIVE","UP","IF","THEN","ELSE","FI","ELSIF","ILLEGAL_TOKEN",
+"NUM","PRED","SUCC","ABS","CARD","MAX_TOKEN","MIN_TOKEN","SIZE","UPPER","LOWER",
+"LENGTH","GDB_REGNAME","GDB_LAST","GDB_VARIABLE","GDB_ASSIGNMENT",
+};
+char *ch_rule[] = {
+"$accept : start",
+"start : value",
+"start : mode_name",
+"value : expression",
+"value : undefined_value",
+"undefined_value : FIXME_01",
+"location : access_name",
+"location : primitive_value POINTER",
+"access_name : LOCATION_NAME",
+"access_name : GDB_LAST",
+"access_name : GDB_REGNAME",
+"access_name : GDB_VARIABLE",
+"access_name : FIXME_03",
+"expression_list : expression",
+"expression_list : expression_list ',' expression",
+"primitive_value : location_contents",
+"primitive_value : value_name",
+"primitive_value : literal",
+"primitive_value : tuple",
+"primitive_value : value_string_element",
+"primitive_value : value_string_slice",
+"primitive_value : value_array_element",
+"primitive_value : value_array_slice",
+"primitive_value : value_structure_field",
+"primitive_value : expression_conversion",
+"primitive_value : value_procedure_call",
+"primitive_value : value_built_in_routine_call",
+"primitive_value : start_expression",
+"primitive_value : zero_adic_operator",
+"primitive_value : parenthesised_expression",
+"location_contents : location",
+"value_name : synonym_name",
+"value_name : value_enumeration_name",
+"value_name : value_do_with_name",
+"value_name : value_receive_name",
+"value_name : GENERAL_PROCEDURE_NAME",
+"literal : INTEGER_LITERAL",
+"literal : BOOLEAN_LITERAL",
+"literal : CHARACTER_LITERAL",
+"literal : FLOAT_LITERAL",
+"literal : SET_LITERAL",
+"literal : EMPTINESS_LITERAL",
+"literal : CHARACTER_STRING_LITERAL",
+"literal : BIT_STRING_LITERAL",
+"tuple : FIXME_04",
+"value_string_element : string_primitive_value '(' start_element ')'",
+"value_string_slice : string_primitive_value '(' left_element ':' right_element ')'",
+"value_string_slice : string_primitive_value '(' start_element UP slice_size ')'",
+"$$1 :",
+"value_array_element : array_primitive_value '(' $$1 expression_list ')'",
+"value_array_slice : array_primitive_value '(' lower_element ':' upper_element ')'",
+"value_array_slice : array_primitive_value '(' first_element UP slice_size ')'",
+"value_structure_field : primitive_value FIELD_NAME",
+"expression_conversion : mode_name parenthesised_expression",
+"value_procedure_call : FIXME_05",
+"value_built_in_routine_call : chill_value_built_in_routine_call",
+"start_expression : FIXME_06",
+"zero_adic_operator : FIXME_07",
+"parenthesised_expression : '(' expression ')'",
+"expression : operand_0",
+"expression : single_assignment_action",
+"expression : conditional_expression",
+"conditional_expression : IF boolean_expression then_alternative else_alternative FI",
+"conditional_expression : CASE case_selector_list OF value_case_alternative '[' ELSE sub_expression ']' ESAC",
+"then_alternative : THEN subexpression",
+"else_alternative : ELSE subexpression",
+"else_alternative : ELSIF boolean_expression then_alternative else_alternative",
+"sub_expression : expression",
+"value_case_alternative : case_label_specification ':' sub_expression ';'",
+"operand_0 : operand_1",
+"operand_0 : operand_0 LOGIOR operand_1",
+"operand_0 : operand_0 ORIF operand_1",
+"operand_0 : operand_0 LOGXOR operand_1",
+"operand_1 : operand_2",
+"operand_1 : operand_1 LOGAND operand_2",
+"operand_1 : operand_1 ANDIF operand_2",
+"operand_2 : operand_3",
+"operand_2 : operand_2 '=' operand_3",
+"operand_2 : operand_2 NOTEQUAL operand_3",
+"operand_2 : operand_2 '>' operand_3",
+"operand_2 : operand_2 GTR operand_3",
+"operand_2 : operand_2 '<' operand_3",
+"operand_2 : operand_2 LEQ operand_3",
+"operand_2 : operand_2 IN operand_3",
+"operand_3 : operand_4",
+"operand_3 : operand_3 '+' operand_4",
+"operand_3 : operand_3 '-' operand_4",
+"operand_3 : operand_3 SLASH_SLASH operand_4",
+"operand_4 : operand_5",
+"operand_4 : operand_4 '*' operand_5",
+"operand_4 : operand_4 '/' operand_5",
+"operand_4 : operand_4 MOD operand_5",
+"operand_4 : operand_4 REM operand_5",
+"operand_5 : operand_6",
+"operand_5 : '-' operand_6",
+"operand_5 : NOT operand_6",
+"operand_5 : parenthesised_expression literal",
+"operand_6 : POINTER location",
+"operand_6 : RECEIVE buffer_location",
+"operand_6 : primitive_value",
+"single_assignment_action : location GDB_ASSIGNMENT value",
+"chill_value_built_in_routine_call : NUM '(' expression ')'",
+"chill_value_built_in_routine_call : PRED '(' expression ')'",
+"chill_value_built_in_routine_call : SUCC '(' expression ')'",
+"chill_value_built_in_routine_call : ABS '(' expression ')'",
+"chill_value_built_in_routine_call : CARD '(' expression ')'",
+"chill_value_built_in_routine_call : MAX_TOKEN '(' expression ')'",
+"chill_value_built_in_routine_call : MIN_TOKEN '(' expression ')'",
+"chill_value_built_in_routine_call : SIZE '(' location ')'",
+"chill_value_built_in_routine_call : SIZE '(' mode_argument ')'",
+"chill_value_built_in_routine_call : UPPER '(' upper_lower_argument ')'",
+"chill_value_built_in_routine_call : LOWER '(' upper_lower_argument ')'",
+"chill_value_built_in_routine_call : LENGTH '(' length_argument ')'",
+"mode_argument : mode_name",
+"mode_argument : array_mode_name '(' expression ')'",
+"mode_argument : string_mode_name '(' expression ')'",
+"mode_argument : variant_structure_mode_name '(' expression_list ')'",
+"mode_name : TYPENAME",
+"upper_lower_argument : expression",
+"upper_lower_argument : mode_name",
+"length_argument : expression",
+"array_primitive_value : primitive_value",
+"array_mode_name : FIXME_08",
+"string_mode_name : FIXME_09",
+"variant_structure_mode_name : FIXME_10",
+"synonym_name : FIXME_11",
+"value_enumeration_name : FIXME_12",
+"value_do_with_name : FIXME_13",
+"value_receive_name : FIXME_14",
+"string_primitive_value : FIXME_15",
+"start_element : FIXME_16",
+"left_element : FIXME_17",
+"right_element : FIXME_18",
+"slice_size : FIXME_19",
+"lower_element : FIXME_20",
+"upper_element : FIXME_21",
+"first_element : FIXME_22",
+"boolean_expression : FIXME_26",
+"case_selector_list : FIXME_27",
+"subexpression : FIXME_28",
+"case_label_specification : FIXME_29",
+"buffer_location : FIXME_30",
+};
+#endif
+#ifdef YYSTACKSIZE
+#undef YYMAXDEPTH
+#define YYMAXDEPTH YYSTACKSIZE
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 500
+#define YYMAXDEPTH 500
+#endif
+#endif
+int yydebug;
+int yynerrs;
+int yyerrflag;
+int yychar;
+short *yyssp;
+YYSTYPE *yyvsp;
+YYSTYPE yyval;
+YYSTYPE yylval;
+short yyss[YYSTACKSIZE];
+YYSTYPE yyvs[YYSTACKSIZE];
+#define yystacksize YYSTACKSIZE
+#line 994 "./ch-exp.y"
+
+/* Implementation of a dynamically expandable buffer for processing input
+ characters acquired through lexptr and building a value to return in
+ yylval. */
+
+static char *tempbuf; /* Current buffer contents */
+static int tempbufsize; /* Size of allocated buffer */
+static int tempbufindex; /* Current index into buffer */
+
+#define GROWBY_MIN_SIZE 64 /* Minimum amount to grow buffer by */
+
+#define CHECKBUF(size) \
+ do { \
+ if (tempbufindex + (size) >= tempbufsize) \
+ { \
+ growbuf_by_size (size); \
+ } \
+ } while (0);
+
+/* Grow the static temp buffer if necessary, including allocating the first one
+ on demand. */
+
+static void
+growbuf_by_size (count)
+ int count;
+{
+ int growby;
+
+ growby = max (count, GROWBY_MIN_SIZE);
+ tempbufsize += growby;
+ if (tempbuf == NULL)
+ {
+ tempbuf = (char *) xmalloc (tempbufsize);
+ }
+ else
+ {
+ tempbuf = (char *) xrealloc (tempbuf, tempbufsize);
+ }
+}
+
+/* Try to consume a simple name string token. If successful, returns
+ a pointer to a nullbyte terminated copy of the name that can be used
+ in symbol table lookups. If not successful, returns NULL. */
+
+static char *
+match_simple_name_string ()
+{
+ char *tokptr = lexptr;
+
+ if (isalpha (*tokptr))
+ {
+ char *result;
+ do {
+ tokptr++;
+ } while (isalnum (*tokptr) || (*tokptr == '_'));
+ yylval.sval.ptr = lexptr;
+ yylval.sval.length = tokptr - lexptr;
+ lexptr = tokptr;
+ result = copy_name (yylval.sval);
+ for (tokptr = result; *tokptr; tokptr++)
+ if (isupper (*tokptr))
+ *tokptr = tolower(*tokptr);
+ return result;
+ }
+ return (NULL);
+}
+
+/* Start looking for a value composed of valid digits as set by the base
+ in use. Note that '_' characters are valid anywhere, in any quantity,
+ and are simply ignored. Since we must find at least one valid digit,
+ or reject this token as an integer literal, we keep track of how many
+ digits we have encountered. */
+
+static int
+decode_integer_value (base, tokptrptr, ivalptr)
+ int base;
+ char **tokptrptr;
+ int *ivalptr;
+{
+ char *tokptr = *tokptrptr;
+ int temp;
+ int digits = 0;
+
+ while (*tokptr != '\0')
+ {
+ temp = tolower (*tokptr);
+ tokptr++;
+ switch (temp)
+ {
+ case '_':
+ continue;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ temp -= '0';
+ break;
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ temp -= 'a';
+ temp += 10;
+ break;
+ default:
+ temp = base;
+ break;
+ }
+ if (temp < base)
+ {
+ digits++;
+ *ivalptr *= base;
+ *ivalptr += temp;
+ }
+ else
+ {
+ /* Found something not in domain for current base. */
+ tokptr--; /* Unconsume what gave us indigestion. */
+ break;
+ }
+ }
+
+ /* If we didn't find any digits, then we don't have a valid integer
+ value, so reject the entire token. Otherwise, update the lexical
+ scan pointer, and return non-zero for success. */
+
+ if (digits == 0)
+ {
+ return (0);
+ }
+ else
+ {
+ *tokptrptr = tokptr;
+ return (1);
+ }
+}
+
+static int
+decode_integer_literal (valptr, tokptrptr)
+ int *valptr;
+ char **tokptrptr;
+{
+ char *tokptr = *tokptrptr;
+ int base = 0;
+ int ival = 0;
+ int explicit_base = 0;
+
+ /* Look for an explicit base specifier, which is optional. */
+
+ switch (*tokptr)
+ {
+ case 'd':
+ case 'D':
+ explicit_base++;
+ base = 10;
+ tokptr++;
+ break;
+ case 'b':
+ case 'B':
+ explicit_base++;
+ base = 2;
+ tokptr++;
+ break;
+ case 'h':
+ case 'H':
+ explicit_base++;
+ base = 16;
+ tokptr++;
+ break;
+ case 'o':
+ case 'O':
+ explicit_base++;
+ base = 8;
+ tokptr++;
+ break;
+ default:
+ base = 10;
+ break;
+ }
+
+ /* If we found an explicit base ensure that the character after the
+ explicit base is a single quote. */
+
+ if (explicit_base && (*tokptr++ != '\''))
+ {
+ return (0);
+ }
+
+ /* Attempt to decode whatever follows as an integer value in the
+ indicated base, updating the token pointer in the process and
+ computing the value into ival. Also, if we have an explicit
+ base, then the next character must not be a single quote, or we
+ have a bitstring literal, so reject the entire token in this case.
+ Otherwise, update the lexical scan pointer, and return non-zero
+ for success. */
+
+ if (!decode_integer_value (base, &tokptr, &ival))
+ {
+ return (0);
+ }
+ else if (explicit_base && (*tokptr == '\''))
+ {
+ return (0);
+ }
+ else
+ {
+ *valptr = ival;
+ *tokptrptr = tokptr;
+ return (1);
+ }
+}
+
+/* If it wasn't for the fact that floating point values can contain '_'
+ characters, we could just let strtod do all the hard work by letting it
+ try to consume as much of the current token buffer as possible and
+ find a legal conversion. Unfortunately we need to filter out the '_'
+ characters before calling strtod, which we do by copying the other
+ legal chars to a local buffer to be converted. However since we also
+ need to keep track of where the last unconsumed character in the input
+ buffer is, we have transfer only as many characters as may compose a
+ legal floating point value. */
+
+static int
+match_float_literal ()
+{
+ char *tokptr = lexptr;
+ char *buf;
+ char *copy;
+ double dval;
+ extern double strtod ();
+
+ /* Make local buffer in which to build the string to convert. This is
+ required because underscores are valid in chill floating point numbers
+ but not in the string passed to strtod to convert. The string will be
+ no longer than our input string. */
+
+ copy = buf = (char *) alloca (strlen (tokptr) + 1);
+
+ /* Transfer all leading digits to the conversion buffer, discarding any
+ underscores. */
+
+ while (isdigit (*tokptr) || *tokptr == '_')
+ {
+ if (*tokptr != '_')
+ {
+ *copy++ = *tokptr;
+ }
+ tokptr++;
+ }
+
+ /* Now accept either a '.', or one of [eEdD]. Dot is legal regardless
+ of whether we found any leading digits, and we simply accept it and
+ continue on to look for the fractional part and/or exponent. One of
+ [eEdD] is legal only if we have seen digits, and means that there
+ is no fractional part. If we find neither of these, then this is
+ not a floating point number, so return failure. */
+
+ switch (*tokptr++)
+ {
+ case '.':
+ /* Accept and then look for fractional part and/or exponent. */
+ *copy++ = '.';
+ break;
+
+ case 'e':
+ case 'E':
+ case 'd':
+ case 'D':
+ if (copy == buf)
+ {
+ return (0);
+ }
+ *copy++ = 'e';
+ goto collect_exponent;
+ break;
+
+ default:
+ return (0);
+ break;
+ }
+
+ /* We found a '.', copy any fractional digits to the conversion buffer, up
+ to the first nondigit, non-underscore character. */
+
+ while (isdigit (*tokptr) || *tokptr == '_')
+ {
+ if (*tokptr != '_')
+ {
+ *copy++ = *tokptr;
+ }
+ tokptr++;
+ }
+
+ /* Look for an exponent, which must start with one of [eEdD]. If none
+ is found, jump directly to trying to convert what we have collected
+ so far. */
+
+ switch (*tokptr)
+ {
+ case 'e':
+ case 'E':
+ case 'd':
+ case 'D':
+ *copy++ = 'e';
+ tokptr++;
+ break;
+ default:
+ goto convert_float;
+ break;
+ }
+
+ /* Accept an optional '-' or '+' following one of [eEdD]. */
+
+ collect_exponent:
+ if (*tokptr == '+' || *tokptr == '-')
+ {
+ *copy++ = *tokptr++;
+ }
+
+ /* Now copy an exponent into the conversion buffer. Note that at the
+ moment underscores are *not* allowed in exponents. */
+
+ while (isdigit (*tokptr))
+ {
+ *copy++ = *tokptr++;
+ }
+
+ /* If we transfered any chars to the conversion buffer, try to interpret its
+ contents as a floating point value. If any characters remain, then we
+ must not have a valid floating point string. */
+
+ convert_float:
+ *copy = '\0';
+ if (copy != buf)
+ {
+ dval = strtod (buf, &copy);
+ if (*copy == '\0')
+ {
+ yylval.dval = dval;
+ lexptr = tokptr;
+ return (FLOAT_LITERAL);
+ }
+ }
+ return (0);
+}
+
+/* Recognize a string literal. A string literal is a nonzero sequence
+ of characters enclosed in matching single or double quotes, except that
+ a single character inside single quotes is a character literal, which
+ we reject as a string literal. To embed the terminator character inside
+ a string, it is simply doubled (I.E. "this""is""one""string") */
+
+static int
+match_string_literal ()
+{
+ char *tokptr = lexptr;
+
+ for (tempbufindex = 0, tokptr++; *tokptr != '\0'; tokptr++)
+ {
+ CHECKBUF (1);
+ if (*tokptr == *lexptr)
+ {
+ if (*(tokptr + 1) == *lexptr)
+ {
+ tokptr++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ tempbuf[tempbufindex++] = *tokptr;
+ }
+ if (*tokptr == '\0' /* no terminator */
+ || tempbufindex == 0 /* no string */
+ || (tempbufindex == 1 && *tokptr == '\'')) /* char literal */
+ {
+ return (0);
+ }
+ else
+ {
+ tempbuf[tempbufindex] = '\0';
+ yylval.sval.ptr = tempbuf;
+ yylval.sval.length = tempbufindex;
+ lexptr = ++tokptr;
+ return (CHARACTER_STRING_LITERAL);
+ }
+}
+
+/* Recognize a character literal. A character literal is single character
+ or a control sequence, enclosed in single quotes. A control sequence
+ is a comma separated list of one or more integer literals, enclosed
+ in parenthesis and introduced with a circumflex character.
+
+ EX: 'a' '^(7)' '^(7,8)'
+
+ As a GNU chill extension, the syntax C'xx' is also recognized as a
+ character literal, where xx is a hex value for the character.
+
+ Note that more than a single character, enclosed in single quotes, is
+ a string literal.
+
+ Also note that the control sequence form is not in GNU Chill since it
+ is ambiguous with the string literal form using single quotes. I.E.
+ is '^(7)' a character literal or a string literal. In theory it it
+ possible to tell by context, but GNU Chill doesn't accept the control
+ sequence form, so neither do we (for now the code is disabled).
+
+ Returns CHARACTER_LITERAL if a match is found.
+ */
+
+static int
+match_character_literal ()
+{
+ char *tokptr = lexptr;
+ int ival = 0;
+
+ if ((tolower (*tokptr) == 'c') && (*(tokptr + 1) == '\''))
+ {
+ /* We have a GNU chill extension form, so skip the leading "C'",
+ decode the hex value, and then ensure that we have a trailing
+ single quote character. */
+ tokptr += 2;
+ if (!decode_integer_value (16, &tokptr, &ival) || (*tokptr != '\''))
+ {
+ return (0);
+ }
+ tokptr++;
+ }
+ else if (*tokptr == '\'')
+ {
+ tokptr++;
+
+ /* Determine which form we have, either a control sequence or the
+ single character form. */
+
+ if ((*tokptr == '^') && (*(tokptr + 1) == '('))
+ {
+#if 0 /* Disable, see note above. -fnf */
+ /* Match and decode a control sequence. Return zero if we don't
+ find a valid integer literal, or if the next unconsumed character
+ after the integer literal is not the trailing ')'.
+ FIXME: We currently don't handle the multiple integer literal
+ form. */
+ tokptr += 2;
+ if (!decode_integer_literal (&ival, &tokptr) || (*tokptr++ != ')'))
+ {
+ return (0);
+ }
+#else
+ return (0);
+#endif
+ }
+ else
+ {
+ ival = *tokptr++;
+ }
+
+ /* The trailing quote has not yet been consumed. If we don't find
+ it, then we have no match. */
+
+ if (*tokptr++ != '\'')
+ {
+ return (0);
+ }
+ }
+ else
+ {
+ /* Not a character literal. */
+ return (0);
+ }
+ yylval.typed_val.val = ival;
+ yylval.typed_val.type = builtin_type_chill_char;
+ lexptr = tokptr;
+ return (CHARACTER_LITERAL);
+}
+
+/* Recognize an integer literal, as specified in Z.200 sec 5.2.4.2.
+ Note that according to 5.2.4.2, a single "_" is also a valid integer
+ literal, however GNU-chill requires there to be at least one "digit"
+ in any integer literal. */
+
+static int
+match_integer_literal ()
+{
+ char *tokptr = lexptr;
+ int ival;
+
+ if (!decode_integer_literal (&ival, &tokptr))
+ {
+ return (0);
+ }
+ else
+ {
+ yylval.typed_val.val = ival;
+ yylval.typed_val.type = builtin_type_int;
+ lexptr = tokptr;
+ return (INTEGER_LITERAL);
+ }
+}
+
+/* Recognize a bit-string literal, as specified in Z.200 sec 5.2.4.8
+ Note that according to 5.2.4.8, a single "_" is also a valid bit-string
+ literal, however GNU-chill requires there to be at least one "digit"
+ in any bit-string literal. */
+
+static int
+match_bitstring_literal ()
+{
+ char *tokptr = lexptr;
+ int mask;
+ int bitoffset = 0;
+ int bitcount = 0;
+ int base;
+ int digit;
+
+ tempbufindex = 0;
+
+ /* Look for the required explicit base specifier. */
+
+ switch (*tokptr++)
+ {
+ case 'b':
+ case 'B':
+ base = 2;
+ break;
+ case 'o':
+ case 'O':
+ base = 8;
+ break;
+ case 'h':
+ case 'H':
+ base = 16;
+ break;
+ default:
+ return (0);
+ break;
+ }
+
+ /* Ensure that the character after the explicit base is a single quote. */
+
+ if (*tokptr++ != '\'')
+ {
+ return (0);
+ }
+
+ while (*tokptr != '\0' && *tokptr != '\'')
+ {
+ digit = tolower (*tokptr);
+ tokptr++;
+ switch (digit)
+ {
+ case '_':
+ continue;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ digit -= '0';
+ break;
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ digit -= 'a';
+ digit += 10;
+ break;
+ default:
+ return (0);
+ break;
+ }
+ if (digit >= base)
+ {
+ /* Found something not in domain for current base. */
+ return (0);
+ }
+ else
+ {
+ /* Extract bits from digit, starting with the msbit appropriate for
+ the current base, and packing them into the bitstring byte,
+ starting at the lsbit. */
+ for (mask = (base >> 1); mask > 0; mask >>= 1)
+ {
+ bitcount++;
+ CHECKBUF (1);
+ if (digit & mask)
+ {
+ tempbuf[tempbufindex] |= (1 << bitoffset);
+ }
+ bitoffset++;
+ if (bitoffset == HOST_CHAR_BIT)
+ {
+ bitoffset = 0;
+ tempbufindex++;
+ }
+ }
+ }
+ }
+
+ /* Verify that we consumed everything up to the trailing single quote,
+ and that we found some bits (IE not just underbars). */
+
+ if (*tokptr++ != '\'')
+ {
+ return (0);
+ }
+ else
+ {
+ yylval.sval.ptr = tempbuf;
+ yylval.sval.length = bitcount;
+ lexptr = tokptr;
+ return (BIT_STRING_LITERAL);
+ }
+}
+
+/* Recognize tokens that start with '$'. These include:
+
+ $regname A native register name or a "standard
+ register name".
+ Return token GDB_REGNAME.
+
+ $variable A convenience variable with a name chosen
+ by the user.
+ Return token GDB_VARIABLE.
+
+ $digits Value history with index <digits>, starting
+ from the first value which has index 1.
+ Return GDB_LAST.
+
+ $$digits Value history with index <digits> relative
+ to the last value. I.E. $$0 is the last
+ value, $$1 is the one previous to that, $$2
+ is the one previous to $$1, etc.
+ Return token GDB_LAST.
+
+ $ | $0 | $$0 The last value in the value history.
+ Return token GDB_LAST.
+
+ $$ An abbreviation for the second to the last
+ value in the value history, I.E. $$1
+ Return token GDB_LAST.
+
+ Note that we currently assume that register names and convenience
+ variables follow the convention of starting with a letter or '_'.
+
+ */
+
+static int
+match_dollar_tokens ()
+{
+ char *tokptr;
+ int regno;
+ int namelength;
+ int negate;
+ int ival;
+
+ /* We will always have a successful match, even if it is just for
+ a single '$', the abbreviation for $$0. So advance lexptr. */
+
+ tokptr = ++lexptr;
+
+ if (*tokptr == '_' || isalpha (*tokptr))
+ {
+ /* Look for a match with a native register name, usually something
+ like "r0" for example. */
+
+ for (regno = 0; regno < NUM_REGS; regno++)
+ {
+ namelength = strlen (reg_names[regno]);
+ if (STREQN (tokptr, reg_names[regno], namelength)
+ && !isalnum (tokptr[namelength]))
+ {
+ yylval.lval = regno;
+ lexptr += namelength + 1;
+ return (GDB_REGNAME);
+ }
+ }
+
+ /* Look for a match with a standard register name, usually something
+ like "pc", which gdb always recognizes as the program counter
+ regardless of what the native register name is. */
+
+ for (regno = 0; regno < num_std_regs; regno++)
+ {
+ namelength = strlen (std_regs[regno].name);
+ if (STREQN (tokptr, std_regs[regno].name, namelength)
+ && !isalnum (tokptr[namelength]))
+ {
+ yylval.lval = std_regs[regno].regnum;
+ lexptr += namelength;
+ return (GDB_REGNAME);
+ }
+ }
+
+ /* Attempt to match against a convenience variable. Note that
+ this will always succeed, because if no variable of that name
+ already exists, the lookup_internalvar will create one for us.
+ Also note that both lexptr and tokptr currently point to the
+ start of the input string we are trying to match, and that we
+ have already tested the first character for non-numeric, so we
+ don't have to treat it specially. */
+
+ while (*tokptr == '_' || isalnum (*tokptr))
+ {
+ tokptr++;
+ }
+ yylval.sval.ptr = lexptr;
+ yylval.sval.length = tokptr - lexptr;
+ yylval.ivar = lookup_internalvar (copy_name (yylval.sval));
+ lexptr = tokptr;
+ return (GDB_VARIABLE);
+ }
+
+ /* Since we didn't match against a register name or convenience
+ variable, our only choice left is a history value. */
+
+ if (*tokptr == '$')
+ {
+ negate = 1;
+ ival = 1;
+ tokptr++;
+ }
+ else
+ {
+ negate = 0;
+ ival = 0;
+ }
+
+ /* Attempt to decode more characters as an integer value giving
+ the index in the history list. If successful, the value will
+ overwrite ival (currently 0 or 1), and if not, ival will be
+ left alone, which is good since it is currently correct for
+ the '$' or '$$' case. */
+
+ decode_integer_literal (&ival, &tokptr);
+ yylval.lval = negate ? -ival : ival;
+ lexptr = tokptr;
+ return (GDB_LAST);
+}
+
+struct token
+{
+ char *operator;
+ int token;
+};
+
+static const struct token idtokentab[] =
+{
+ { "length", LENGTH },
+ { "lower", LOWER },
+ { "upper", UPPER },
+ { "andif", ANDIF },
+ { "pred", PRED },
+ { "succ", SUCC },
+ { "card", CARD },
+ { "size", SIZE },
+ { "orif", ORIF },
+ { "num", NUM },
+ { "abs", ABS },
+ { "max", MAX_TOKEN },
+ { "min", MIN_TOKEN },
+ { "mod", MOD },
+ { "rem", REM },
+ { "not", NOT },
+ { "xor", LOGXOR },
+ { "and", LOGAND },
+ { "in", IN },
+ { "or", LOGIOR }
+};
+
+static const struct token tokentab2[] =
+{
+ { ":=", GDB_ASSIGNMENT },
+ { "//", SLASH_SLASH },
+ { "->", POINTER },
+ { "/=", NOTEQUAL },
+ { "<=", LEQ },
+ { ">=", GTR }
+};
+
+/* Read one token, getting characters through lexptr. */
+/* This is where we will check to make sure that the language and the
+ operators used are compatible. */
+
+static int
+yylex ()
+{
+ unsigned int i;
+ int token;
+ char *simplename;
+ struct symbol *sym;
+
+ /* Skip over any leading whitespace. */
+ while (isspace (*lexptr))
+ {
+ lexptr++;
+ }
+ /* Look for special single character cases which can't be the first
+ character of some other multicharacter token. */
+ switch (*lexptr)
+ {
+ case '\0':
+ return (0);
+ case ',':
+ case '=':
+ case ';':
+ case '!':
+ case '+':
+ case '*':
+ case '(':
+ case ')':
+ case '[':
+ case ']':
+ return (*lexptr++);
+ }
+ /* Look for characters which start a particular kind of multicharacter
+ token, such as a character literal, register name, convenience
+ variable name, string literal, etc. */
+ switch (*lexptr)
+ {
+ case '\'':
+ case '\"':
+ /* First try to match a string literal, which is any nonzero
+ sequence of characters enclosed in matching single or double
+ quotes, except that a single character inside single quotes
+ is a character literal, so we have to catch that case also. */
+ token = match_string_literal ();
+ if (token != 0)
+ {
+ return (token);
+ }
+ if (*lexptr == '\'')
+ {
+ token = match_character_literal ();
+ if (token != 0)
+ {
+ return (token);
+ }
+ }
+ break;
+ case 'C':
+ case 'c':
+ token = match_character_literal ();
+ if (token != 0)
+ {
+ return (token);
+ }
+ break;
+ case '$':
+ token = match_dollar_tokens ();
+ if (token != 0)
+ {
+ return (token);
+ }
+ break;
+ }
+ /* See if it is a special token of length 2. */
+ for (i = 0; i < sizeof (tokentab2) / sizeof (tokentab2[0]); i++)
+ {
+ if (STREQN (lexptr, tokentab2[i].operator, 2))
+ {
+ lexptr += 2;
+ return (tokentab2[i].token);
+ }
+ }
+ /* Look for single character cases which which could be the first
+ character of some other multicharacter token, but aren't, or we
+ would already have found it. */
+ switch (*lexptr)
+ {
+ case '-':
+ case ':':
+ case '/':
+ case '<':
+ case '>':
+ return (*lexptr++);
+ }
+ /* Look for a float literal before looking for an integer literal, so
+ we match as much of the input stream as possible. */
+ token = match_float_literal ();
+ if (token != 0)
+ {
+ return (token);
+ }
+ token = match_bitstring_literal ();
+ if (token != 0)
+ {
+ return (token);
+ }
+ token = match_integer_literal ();
+ if (token != 0)
+ {
+ return (token);
+ }
+
+ /* Try to match a simple name string, and if a match is found, then
+ further classify what sort of name it is and return an appropriate
+ token. Note that attempting to match a simple name string consumes
+ the token from lexptr, so we can't back out if we later find that
+ we can't classify what sort of name it is. */
+
+ simplename = match_simple_name_string ();
+
+ if (simplename != NULL)
+ {
+ /* See if it is a reserved identifier. */
+ for (i = 0; i < sizeof (idtokentab) / sizeof (idtokentab[0]); i++)
+ {
+ if (STREQ (simplename, idtokentab[i].operator))
+ {
+ return (idtokentab[i].token);
+ }
+ }
+
+ /* Look for other special tokens. */
+ if (STREQ (simplename, "true"))
+ {
+ yylval.ulval = 1;
+ return (BOOLEAN_LITERAL);
+ }
+ if (STREQ (simplename, "false"))
+ {
+ yylval.ulval = 0;
+ return (BOOLEAN_LITERAL);
+ }
+
+ sym = lookup_symbol (simplename, expression_context_block,
+ VAR_NAMESPACE, (int *) NULL,
+ (struct symtab **) NULL);
+ if (sym != NULL)
+ {
+ yylval.ssym.stoken.ptr = NULL;
+ yylval.ssym.stoken.length = 0;
+ yylval.ssym.sym = sym;
+ yylval.ssym.is_a_field_of_this = 0; /* FIXME, C++'ism */
+ switch (SYMBOL_CLASS (sym))
+ {
+ case LOC_BLOCK:
+ /* Found a procedure name. */
+ return (GENERAL_PROCEDURE_NAME);
+ case LOC_STATIC:
+ /* Found a global or local static variable. */
+ return (LOCATION_NAME);
+ case LOC_REGISTER:
+ case LOC_ARG:
+ case LOC_REF_ARG:
+ case LOC_REGPARM:
+ case LOC_REGPARM_ADDR:
+ case LOC_LOCAL:
+ case LOC_LOCAL_ARG:
+ case LOC_BASEREG:
+ case LOC_BASEREG_ARG:
+ if (innermost_block == NULL
+ || contained_in (block_found, innermost_block))
+ {
+ innermost_block = block_found;
+ }
+ return (LOCATION_NAME);
+ break;
+ case LOC_CONST:
+ case LOC_LABEL:
+ return (LOCATION_NAME);
+ break;
+ case LOC_TYPEDEF:
+ yylval.tsym.type = SYMBOL_TYPE (sym);
+ return TYPENAME;
+ case LOC_UNDEF:
+ case LOC_CONST_BYTES:
+ case LOC_OPTIMIZED_OUT:
+ error ("Symbol \"%s\" names no location.", simplename);
+ break;
+ }
+ }
+ else if (!have_full_symbols () && !have_partial_symbols ())
+ {
+ error ("No symbol table is loaded. Use the \"file\" command.");
+ }
+ else
+ {
+ error ("No symbol \"%s\" in current context.", simplename);
+ }
+ }
+
+ /* Catch single character tokens which are not part of some
+ longer token. */
+
+ switch (*lexptr)
+ {
+ case '.': /* Not float for example. */
+ lexptr++;
+ while (isspace (*lexptr)) lexptr++;
+ simplename = match_simple_name_string ();
+ if (!simplename)
+ return '.';
+ return FIELD_NAME;
+ }
+
+ return (ILLEGAL_TOKEN);
+}
+
+void
+yyerror (msg)
+ char *msg; /* unused */
+{
+ printf ("Parsing: %s\n", lexptr);
+ if (yychar < 256)
+ {
+ error ("Invalid syntax in expression near character '%c'.", yychar);
+ }
+ else
+ {
+ error ("Invalid syntax in expression");
+ }
+}
+#line 1836 "y.tab.c"
+#define YYABORT goto yyabort
+#define YYREJECT goto yyabort
+#define YYACCEPT goto yyaccept
+#define YYERROR goto yyerrlab
+int
+yyparse()
+{
+ register int yym, yyn, yystate;
+#if YYDEBUG
+ register char *yys;
+ extern char *getenv();
+
+ if (yys = getenv("YYDEBUG"))
+ {
+ yyn = *yys;
+ if (yyn >= '0' && yyn <= '9')
+ yydebug = yyn - '0';
+ }
+#endif
+
+ yynerrs = 0;
+ yyerrflag = 0;
+ yychar = (-1);
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+ *yyssp = yystate = 0;
+
+yyloop:
+ if (yyn = yydefred[yystate]) goto yyreduce;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ }
+ if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, shifting to state %d\n",
+ YYPREFIX, yystate, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ yychar = (-1);
+ if (yyerrflag > 0) --yyerrflag;
+ goto yyloop;
+ }
+ if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+ yyn = yytable[yyn];
+ goto yyreduce;
+ }
+ if (yyerrflag) goto yyinrecovery;
+#ifdef lint
+ goto yynewerror;
+#endif
+yynewerror:
+ yyerror("syntax error");
+#ifdef lint
+ goto yyerrlab;
+#endif
+yyerrlab:
+ ++yynerrs;
+yyinrecovery:
+ if (yyerrflag < 3)
+ {
+ yyerrflag = 3;
+ for (;;)
+ {
+ if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, error recovery shifting\
+ to state %d\n", YYPREFIX, *yyssp, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ goto yyloop;
+ }
+ else
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: error recovery discarding state %d\n",
+ YYPREFIX, *yyssp);
+#endif
+ if (yyssp <= yyss) goto yyabort;
+ --yyssp;
+ --yyvsp;
+ }
+ }
+ }
+ else
+ {
+ if (yychar == 0) goto yyabort;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, error recovery discards token %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ yychar = (-1);
+ goto yyloop;
+ }
+yyreduce:
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, reducing by rule %d (%s)\n",
+ YYPREFIX, yystate, yyn, yyrule[yyn]);
+#endif
+ yym = yylen[yyn];
+ yyval = yyvsp[1-yym];
+ switch (yyn)
+ {
+case 1:
+#line 312 "./ch-exp.y"
+{ }
+break;
+case 2:
+#line 314 "./ch-exp.y"
+{ write_exp_elt_opcode(OP_TYPE);
+ write_exp_elt_type(yyvsp[0].tsym.type);
+ write_exp_elt_opcode(OP_TYPE);}
+break;
+case 3:
+#line 320 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 4:
+#line 324 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 5:
+#line 330 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 7:
+#line 339 "./ch-exp.y"
+{
+ write_exp_elt_opcode (UNOP_IND);
+ }
+break;
+case 8:
+#line 347 "./ch-exp.y"
+{
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ write_exp_elt_block (NULL);
+ write_exp_elt_sym (yyvsp[0].ssym.sym);
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ }
+break;
+case 9:
+#line 354 "./ch-exp.y"
+{
+ write_exp_elt_opcode (OP_LAST);
+ write_exp_elt_longcst (yyvsp[0].lval);
+ write_exp_elt_opcode (OP_LAST);
+ }
+break;
+case 10:
+#line 360 "./ch-exp.y"
+{
+ write_exp_elt_opcode (OP_REGISTER);
+ write_exp_elt_longcst (yyvsp[0].lval);
+ write_exp_elt_opcode (OP_REGISTER);
+ }
+break;
+case 11:
+#line 366 "./ch-exp.y"
+{
+ write_exp_elt_opcode (OP_INTERNALVAR);
+ write_exp_elt_intern (yyvsp[0].ivar);
+ write_exp_elt_opcode (OP_INTERNALVAR);
+ }
+break;
+case 12:
+#line 372 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 13:
+#line 380 "./ch-exp.y"
+{
+ arglist_len = 1;
+ }
+break;
+case 14:
+#line 384 "./ch-exp.y"
+{
+ arglist_len++;
+ }
+break;
+case 15:
+#line 391 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 16:
+#line 395 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 17:
+#line 399 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 18:
+#line 403 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 19:
+#line 407 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 20:
+#line 411 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 21:
+#line 415 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 22:
+#line 419 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 23:
+#line 423 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 24:
+#line 427 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 25:
+#line 431 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 26:
+#line 435 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 27:
+#line 439 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 28:
+#line 443 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 29:
+#line 447 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 30:
+#line 455 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 31:
+#line 463 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 32:
+#line 467 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 33:
+#line 471 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 34:
+#line 475 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 35:
+#line 479 "./ch-exp.y"
+{
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ write_exp_elt_block (NULL);
+ write_exp_elt_sym (yyvsp[0].ssym.sym);
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ }
+break;
+case 36:
+#line 490 "./ch-exp.y"
+{
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (yyvsp[0].typed_val.type);
+ write_exp_elt_longcst ((LONGEST) (yyvsp[0].typed_val.val));
+ write_exp_elt_opcode (OP_LONG);
+ }
+break;
+case 37:
+#line 497 "./ch-exp.y"
+{
+ write_exp_elt_opcode (OP_BOOL);
+ write_exp_elt_longcst ((LONGEST) yyvsp[0].ulval);
+ write_exp_elt_opcode (OP_BOOL);
+ }
+break;
+case 38:
+#line 503 "./ch-exp.y"
+{
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (yyvsp[0].typed_val.type);
+ write_exp_elt_longcst ((LONGEST) (yyvsp[0].typed_val.val));
+ write_exp_elt_opcode (OP_LONG);
+ }
+break;
+case 39:
+#line 510 "./ch-exp.y"
+{
+ write_exp_elt_opcode (OP_DOUBLE);
+ write_exp_elt_type (builtin_type_double);
+ write_exp_elt_dblcst (yyvsp[0].dval);
+ write_exp_elt_opcode (OP_DOUBLE);
+ }
+break;
+case 40:
+#line 517 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 41:
+#line 521 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 42:
+#line 525 "./ch-exp.y"
+{
+ write_exp_elt_opcode (OP_STRING);
+ write_exp_string (yyvsp[0].sval);
+ write_exp_elt_opcode (OP_STRING);
+ }
+break;
+case 43:
+#line 531 "./ch-exp.y"
+{
+ write_exp_elt_opcode (OP_BITSTRING);
+ write_exp_bitstring (yyvsp[0].sval);
+ write_exp_elt_opcode (OP_BITSTRING);
+ }
+break;
+case 44:
+#line 541 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 45:
+#line 550 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 46:
+#line 558 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 47:
+#line 562 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 48:
+#line 572 "./ch-exp.y"
+{ start_arglist (); }
+break;
+case 49:
+#line 574 "./ch-exp.y"
+{
+ write_exp_elt_opcode (MULTI_SUBSCRIPT);
+ write_exp_elt_longcst ((LONGEST) end_arglist ());
+ write_exp_elt_opcode (MULTI_SUBSCRIPT);
+ }
+break;
+case 50:
+#line 584 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 51:
+#line 588 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 52:
+#line 596 "./ch-exp.y"
+{ write_exp_elt_opcode (STRUCTOP_STRUCT);
+ write_exp_string (yyvsp[0].sval);
+ write_exp_elt_opcode (STRUCTOP_STRUCT);
+ }
+break;
+case 53:
+#line 605 "./ch-exp.y"
+{
+ write_exp_elt_opcode (UNOP_CAST);
+ write_exp_elt_type (yyvsp[-1].tsym.type);
+ write_exp_elt_opcode (UNOP_CAST);
+ }
+break;
+case 54:
+#line 615 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 55:
+#line 623 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 56:
+#line 631 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 57:
+#line 639 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 58:
+#line 647 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 59:
+#line 655 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 60:
+#line 659 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 61:
+#line 663 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 62:
+#line 669 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 63:
+#line 673 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 64:
+#line 679 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 65:
+#line 685 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 66:
+#line 689 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 67:
+#line 695 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 68:
+#line 701 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 69:
+#line 709 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 70:
+#line 713 "./ch-exp.y"
+{
+ write_exp_elt_opcode (BINOP_BITWISE_IOR);
+ }
+break;
+case 71:
+#line 717 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 72:
+#line 721 "./ch-exp.y"
+{
+ write_exp_elt_opcode (BINOP_BITWISE_XOR);
+ }
+break;
+case 73:
+#line 729 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 74:
+#line 733 "./ch-exp.y"
+{
+ write_exp_elt_opcode (BINOP_BITWISE_AND);
+ }
+break;
+case 75:
+#line 737 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 76:
+#line 745 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 77:
+#line 749 "./ch-exp.y"
+{
+ write_exp_elt_opcode (BINOP_EQUAL);
+ }
+break;
+case 78:
+#line 753 "./ch-exp.y"
+{
+ write_exp_elt_opcode (BINOP_NOTEQUAL);
+ }
+break;
+case 79:
+#line 757 "./ch-exp.y"
+{
+ write_exp_elt_opcode (BINOP_GTR);
+ }
+break;
+case 80:
+#line 761 "./ch-exp.y"
+{
+ write_exp_elt_opcode (BINOP_GEQ);
+ }
+break;
+case 81:
+#line 765 "./ch-exp.y"
+{
+ write_exp_elt_opcode (BINOP_LESS);
+ }
+break;
+case 82:
+#line 769 "./ch-exp.y"
+{
+ write_exp_elt_opcode (BINOP_LEQ);
+ }
+break;
+case 83:
+#line 773 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 84:
+#line 782 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 85:
+#line 786 "./ch-exp.y"
+{
+ write_exp_elt_opcode (BINOP_ADD);
+ }
+break;
+case 86:
+#line 790 "./ch-exp.y"
+{
+ write_exp_elt_opcode (BINOP_SUB);
+ }
+break;
+case 87:
+#line 794 "./ch-exp.y"
+{
+ write_exp_elt_opcode (BINOP_CONCAT);
+ }
+break;
+case 88:
+#line 802 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 89:
+#line 806 "./ch-exp.y"
+{
+ write_exp_elt_opcode (BINOP_MUL);
+ }
+break;
+case 90:
+#line 810 "./ch-exp.y"
+{
+ write_exp_elt_opcode (BINOP_DIV);
+ }
+break;
+case 91:
+#line 814 "./ch-exp.y"
+{
+ write_exp_elt_opcode (BINOP_MOD);
+ }
+break;
+case 92:
+#line 818 "./ch-exp.y"
+{
+ write_exp_elt_opcode (BINOP_REM);
+ }
+break;
+case 93:
+#line 826 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 94:
+#line 830 "./ch-exp.y"
+{
+ write_exp_elt_opcode (UNOP_NEG);
+ }
+break;
+case 95:
+#line 834 "./ch-exp.y"
+{
+ write_exp_elt_opcode (UNOP_LOGICAL_NOT);
+ }
+break;
+case 96:
+#line 840 "./ch-exp.y"
+{
+ write_exp_elt_opcode (BINOP_CONCAT);
+ }
+break;
+case 97:
+#line 848 "./ch-exp.y"
+{
+ write_exp_elt_opcode (UNOP_ADDR);
+ }
+break;
+case 98:
+#line 852 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 99:
+#line 856 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 100:
+#line 866 "./ch-exp.y"
+{
+ write_exp_elt_opcode (BINOP_ASSIGN);
+ }
+break;
+case 101:
+#line 875 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 102:
+#line 879 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 103:
+#line 883 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 104:
+#line 887 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 105:
+#line 891 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 106:
+#line 895 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 107:
+#line 899 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 108:
+#line 903 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 109:
+#line 907 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 110:
+#line 911 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 111:
+#line 915 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 112:
+#line 919 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 113:
+#line 925 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 114:
+#line 929 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 115:
+#line 933 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 116:
+#line 937 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 118:
+#line 946 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 119:
+#line 950 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 120:
+#line 956 "./ch-exp.y"
+{
+ yyval.voidval = 0; /* FIXME */
+ }
+break;
+case 121:
+#line 964 "./ch-exp.y"
+{
+ yyval.voidval = 0;
+ }
+break;
+case 122:
+#line 972 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 123:
+#line 973 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 124:
+#line 974 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 125:
+#line 975 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 126:
+#line 976 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 127:
+#line 977 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 128:
+#line 978 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 129:
+#line 979 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 130:
+#line 980 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 131:
+#line 981 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 132:
+#line 982 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 133:
+#line 983 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 134:
+#line 984 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 135:
+#line 985 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 136:
+#line 986 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 137:
+#line 987 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 138:
+#line 988 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 139:
+#line 989 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 140:
+#line 990 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+case 141:
+#line 991 "./ch-exp.y"
+{ yyval.voidval = 0; }
+break;
+#line 2799 "y.tab.c"
+ }
+ yyssp -= yym;
+ yystate = *yyssp;
+ yyvsp -= yym;
+ yym = yylhs[yyn];
+ if (yystate == 0 && yym == 0)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state 0 to\
+ state %d\n", YYPREFIX, YYFINAL);
+#endif
+ yystate = YYFINAL;
+ *++yyssp = YYFINAL;
+ *++yyvsp = yyval;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, YYFINAL, yychar, yys);
+ }
+#endif
+ }
+ if (yychar == 0) goto yyaccept;
+ goto yyloop;
+ }
+ if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yystate)
+ yystate = yytable[yyn];
+ else
+ yystate = yydgoto[yym];
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state %d \
+to state %d\n", YYPREFIX, *yyssp, yystate);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate;
+ *++yyvsp = yyval;
+ goto yyloop;
+yyoverflow:
+ yyerror("yacc stack overflow");
+yyabort:
+ return (1);
+yyaccept:
+ return (0);
+}
diff --git a/gnu/usr.bin/gdb/gdb/ch-exp.y b/gnu/usr.bin/gdb/gdb/ch-exp.y
new file mode 100644
index 0000000..b6370f3
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/ch-exp.y
@@ -0,0 +1,1997 @@
+/* YACC grammar for Chill expressions, for GDB.
+ Copyright 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Parse a Chill expression from text in a string,
+ and return the result as a struct expression pointer.
+ That structure contains arithmetic operations in reverse polish,
+ with constants represented by operations that are followed by special data.
+ See expression.h for the details of the format.
+ What is important here is that it can be built up sequentially
+ during the process of parsing; the lower levels of the tree always
+ come first in the result.
+
+ Note that malloc's and realloc's in this file are transformed to
+ xmalloc and xrealloc respectively by the same sed command in the
+ makefile that remaps any other malloc/realloc inserted by the parser
+ generator. Doing this with #defines and trying to control the interaction
+ with include files (<malloc.h> and <stdlib.h> for example) just became
+ too messy, particularly when such includes can be inserted at random
+ times by the parser generator.
+
+ Also note that the language accepted by this parser is more liberal
+ than the one accepted by an actual Chill compiler. For example, the
+ language rule that a simple name string can not be one of the reserved
+ simple name strings is not enforced (e.g "case" is not treated as a
+ reserved name). Another example is that Chill is a strongly typed
+ language, and certain expressions that violate the type constraints
+ may still be evaluated if gdb can do so in a meaningful manner, while
+ such expressions would be rejected by the compiler. The reason for
+ this more liberal behavior is the philosophy that the debugger
+ is intended to be a tool that is used by the programmer when things
+ go wrong, and as such, it should provide as few artificial barriers
+ to it's use as possible. If it can do something meaningful, even
+ something that violates language contraints that are enforced by the
+ compiler, it should do so without complaint.
+
+ */
+
+%{
+
+#include "defs.h"
+#include <ctype.h>
+#include "expression.h"
+#include "language.h"
+#include "value.h"
+#include "parser-defs.h"
+#include "ch-lang.h"
+
+/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
+ as well as gratuitiously global symbol names, so we can have multiple
+ yacc generated parsers in gdb. Note that these are only the variables
+ produced by yacc. If other parser generators (bison, byacc, etc) produce
+ additional global names that conflict at link time, then those parser
+ generators need to be fixed instead of adding those names to this list. */
+
+#define yymaxdepth chill_maxdepth
+#define yyparse chill_parse
+#define yylex chill_lex
+#define yyerror chill_error
+#define yylval chill_lval
+#define yychar chill_char
+#define yydebug chill_debug
+#define yypact chill_pact
+#define yyr1 chill_r1
+#define yyr2 chill_r2
+#define yydef chill_def
+#define yychk chill_chk
+#define yypgo chill_pgo
+#define yyact chill_act
+#define yyexca chill_exca
+#define yyerrflag chill_errflag
+#define yynerrs chill_nerrs
+#define yyps chill_ps
+#define yypv chill_pv
+#define yys chill_s
+#define yy_yys chill_yys
+#define yystate chill_state
+#define yytmp chill_tmp
+#define yyv chill_v
+#define yy_yyv chill_yyv
+#define yyval chill_val
+#define yylloc chill_lloc
+#define yyreds chill_reds /* With YYDEBUG defined */
+#define yytoks chill_toks /* With YYDEBUG defined */
+
+#ifndef YYDEBUG
+#define YYDEBUG 0 /* Default to no yydebug support */
+#endif
+
+int
+yyparse PARAMS ((void));
+
+static int
+yylex PARAMS ((void));
+
+void
+yyerror PARAMS ((char *));
+
+%}
+
+/* Although the yacc "value" of an expression is not used,
+ since the result is stored in the structure being created,
+ other node types do have values. */
+
+%union
+ {
+ LONGEST lval;
+ unsigned LONGEST ulval;
+ struct {
+ LONGEST val;
+ struct type *type;
+ } typed_val;
+ double dval;
+ struct symbol *sym;
+ struct type *tval;
+ struct stoken sval;
+ struct ttype tsym;
+ struct symtoken ssym;
+ int voidval;
+ struct block *bval;
+ enum exp_opcode opcode;
+ struct internalvar *ivar;
+
+ struct type **tvec;
+ int *ivec;
+ }
+
+%token <voidval> FIXME_01
+%token <voidval> FIXME_02
+%token <voidval> FIXME_03
+%token <voidval> FIXME_04
+%token <voidval> FIXME_05
+%token <voidval> FIXME_06
+%token <voidval> FIXME_07
+%token <voidval> FIXME_08
+%token <voidval> FIXME_09
+%token <voidval> FIXME_10
+%token <voidval> FIXME_11
+%token <voidval> FIXME_12
+%token <voidval> FIXME_13
+%token <voidval> FIXME_14
+%token <voidval> FIXME_15
+%token <voidval> FIXME_16
+%token <voidval> FIXME_17
+%token <voidval> FIXME_18
+%token <voidval> FIXME_19
+%token <voidval> FIXME_20
+%token <voidval> FIXME_21
+%token <voidval> FIXME_22
+%token <voidval> FIXME_24
+%token <voidval> FIXME_25
+%token <voidval> FIXME_26
+%token <voidval> FIXME_27
+%token <voidval> FIXME_28
+%token <voidval> FIXME_29
+%token <voidval> FIXME_30
+
+%token <typed_val> INTEGER_LITERAL
+%token <ulval> BOOLEAN_LITERAL
+%token <typed_val> CHARACTER_LITERAL
+%token <dval> FLOAT_LITERAL
+%token <ssym> GENERAL_PROCEDURE_NAME
+%token <ssym> LOCATION_NAME
+%token <voidval> SET_LITERAL
+%token <voidval> EMPTINESS_LITERAL
+%token <sval> CHARACTER_STRING_LITERAL
+%token <sval> BIT_STRING_LITERAL
+%token <tsym> TYPENAME
+%token <sval> FIELD_NAME
+
+%token <voidval> '.'
+%token <voidval> ';'
+%token <voidval> ':'
+%token <voidval> CASE
+%token <voidval> OF
+%token <voidval> ESAC
+%token <voidval> LOGIOR
+%token <voidval> ORIF
+%token <voidval> LOGXOR
+%token <voidval> LOGAND
+%token <voidval> ANDIF
+%token <voidval> '='
+%token <voidval> NOTEQUAL
+%token <voidval> '>'
+%token <voidval> GTR
+%token <voidval> '<'
+%token <voidval> LEQ
+%token <voidval> IN
+%token <voidval> '+'
+%token <voidval> '-'
+%token <voidval> '*'
+%token <voidval> '/'
+%token <voidval> SLASH_SLASH
+%token <voidval> MOD
+%token <voidval> REM
+%token <voidval> NOT
+%token <voidval> POINTER
+%token <voidval> RECEIVE
+%token <voidval> '['
+%token <voidval> ']'
+%token <voidval> '('
+%token <voidval> ')'
+%token <voidval> UP
+%token <voidval> IF
+%token <voidval> THEN
+%token <voidval> ELSE
+%token <voidval> FI
+%token <voidval> ELSIF
+%token <voidval> ILLEGAL_TOKEN
+%token <voidval> NUM
+%token <voidval> PRED
+%token <voidval> SUCC
+%token <voidval> ABS
+%token <voidval> CARD
+%token <voidval> MAX_TOKEN
+%token <voidval> MIN_TOKEN
+%token <voidval> SIZE
+%token <voidval> UPPER
+%token <voidval> LOWER
+%token <voidval> LENGTH
+
+/* Tokens which are not Chill tokens used in expressions, but rather GDB
+ specific things that we recognize in the same context as Chill tokens
+ (register names for example). */
+
+%token <lval> GDB_REGNAME /* Machine register name */
+%token <lval> GDB_LAST /* Value history */
+%token <ivar> GDB_VARIABLE /* Convenience variable */
+%token <voidval> GDB_ASSIGNMENT /* Assign value to somewhere */
+
+%type <voidval> location
+%type <voidval> access_name
+%type <voidval> primitive_value
+%type <voidval> location_contents
+%type <voidval> value_name
+%type <voidval> literal
+%type <voidval> tuple
+%type <voidval> value_string_element
+%type <voidval> value_string_slice
+%type <voidval> value_array_element
+%type <voidval> value_array_slice
+%type <voidval> value_structure_field
+%type <voidval> expression_conversion
+%type <voidval> value_procedure_call
+%type <voidval> value_built_in_routine_call
+%type <voidval> chill_value_built_in_routine_call
+%type <voidval> start_expression
+%type <voidval> zero_adic_operator
+%type <voidval> parenthesised_expression
+%type <voidval> value
+%type <voidval> undefined_value
+%type <voidval> expression
+%type <voidval> conditional_expression
+%type <voidval> then_alternative
+%type <voidval> else_alternative
+%type <voidval> sub_expression
+%type <voidval> value_case_alternative
+%type <voidval> operand_0
+%type <voidval> operand_1
+%type <voidval> operand_2
+%type <voidval> operand_3
+%type <voidval> operand_4
+%type <voidval> operand_5
+%type <voidval> operand_6
+%type <voidval> synonym_name
+%type <voidval> value_enumeration_name
+%type <voidval> value_do_with_name
+%type <voidval> value_receive_name
+%type <voidval> string_primitive_value
+%type <voidval> start_element
+%type <voidval> left_element
+%type <voidval> right_element
+%type <voidval> slice_size
+%type <voidval> array_primitive_value
+%type <voidval> expression_list
+%type <voidval> lower_element
+%type <voidval> upper_element
+%type <voidval> first_element
+%type <voidval> mode_argument
+%type <voidval> upper_lower_argument
+%type <voidval> length_argument
+%type <voidval> array_mode_name
+%type <voidval> string_mode_name
+%type <voidval> variant_structure_mode_name
+%type <voidval> boolean_expression
+%type <voidval> case_selector_list
+%type <voidval> subexpression
+%type <voidval> case_label_specification
+%type <voidval> buffer_location
+%type <voidval> single_assignment_action
+%type <tsym> mode_name
+
+%%
+
+/* Z.200, 5.3.1 */
+
+start : value { }
+ | mode_name
+ { write_exp_elt_opcode(OP_TYPE);
+ write_exp_elt_type($1.type);
+ write_exp_elt_opcode(OP_TYPE);}
+ ;
+
+value : expression
+ {
+ $$ = 0; /* FIXME */
+ }
+ | undefined_value
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+undefined_value : FIXME_01
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+/* Z.200, 4.2.1 */
+
+location : access_name
+ | primitive_value POINTER
+ {
+ write_exp_elt_opcode (UNOP_IND);
+ }
+ ;
+
+/* Z.200, 4.2.2 */
+
+access_name : LOCATION_NAME
+ {
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ write_exp_elt_block (NULL);
+ write_exp_elt_sym ($1.sym);
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ }
+ | GDB_LAST /* gdb specific */
+ {
+ write_exp_elt_opcode (OP_LAST);
+ write_exp_elt_longcst ($1);
+ write_exp_elt_opcode (OP_LAST);
+ }
+ | GDB_REGNAME /* gdb specific */
+ {
+ write_exp_elt_opcode (OP_REGISTER);
+ write_exp_elt_longcst ($1);
+ write_exp_elt_opcode (OP_REGISTER);
+ }
+ | GDB_VARIABLE /* gdb specific */
+ {
+ write_exp_elt_opcode (OP_INTERNALVAR);
+ write_exp_elt_intern ($1);
+ write_exp_elt_opcode (OP_INTERNALVAR);
+ }
+ | FIXME_03
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+/* Z.200, 4.2.8 */
+
+expression_list : expression
+ {
+ arglist_len = 1;
+ }
+ | expression_list ',' expression
+ {
+ arglist_len++;
+ }
+
+/* Z.200, 5.2.1 */
+
+primitive_value : location_contents
+ {
+ $$ = 0; /* FIXME */
+ }
+ | value_name
+ {
+ $$ = 0; /* FIXME */
+ }
+ | literal
+ {
+ $$ = 0; /* FIXME */
+ }
+ | tuple
+ {
+ $$ = 0; /* FIXME */
+ }
+ | value_string_element
+ {
+ $$ = 0; /* FIXME */
+ }
+ | value_string_slice
+ {
+ $$ = 0; /* FIXME */
+ }
+ | value_array_element
+ {
+ $$ = 0; /* FIXME */
+ }
+ | value_array_slice
+ {
+ $$ = 0; /* FIXME */
+ }
+ | value_structure_field
+ {
+ $$ = 0; /* FIXME */
+ }
+ | expression_conversion
+ {
+ $$ = 0; /* FIXME */
+ }
+ | value_procedure_call
+ {
+ $$ = 0; /* FIXME */
+ }
+ | value_built_in_routine_call
+ {
+ $$ = 0; /* FIXME */
+ }
+ | start_expression
+ {
+ $$ = 0; /* FIXME */
+ }
+ | zero_adic_operator
+ {
+ $$ = 0; /* FIXME */
+ }
+ | parenthesised_expression
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+/* Z.200, 5.2.2 */
+
+location_contents: location
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+/* Z.200, 5.2.3 */
+
+value_name : synonym_name
+ {
+ $$ = 0; /* FIXME */
+ }
+ | value_enumeration_name
+ {
+ $$ = 0; /* FIXME */
+ }
+ | value_do_with_name
+ {
+ $$ = 0; /* FIXME */
+ }
+ | value_receive_name
+ {
+ $$ = 0; /* FIXME */
+ }
+ | GENERAL_PROCEDURE_NAME
+ {
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ write_exp_elt_block (NULL);
+ write_exp_elt_sym ($1.sym);
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ }
+ ;
+
+/* Z.200, 5.2.4.1 */
+
+literal : INTEGER_LITERAL
+ {
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type ($1.type);
+ write_exp_elt_longcst ((LONGEST) ($1.val));
+ write_exp_elt_opcode (OP_LONG);
+ }
+ | BOOLEAN_LITERAL
+ {
+ write_exp_elt_opcode (OP_BOOL);
+ write_exp_elt_longcst ((LONGEST) $1);
+ write_exp_elt_opcode (OP_BOOL);
+ }
+ | CHARACTER_LITERAL
+ {
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type ($1.type);
+ write_exp_elt_longcst ((LONGEST) ($1.val));
+ write_exp_elt_opcode (OP_LONG);
+ }
+ | FLOAT_LITERAL
+ {
+ write_exp_elt_opcode (OP_DOUBLE);
+ write_exp_elt_type (builtin_type_double);
+ write_exp_elt_dblcst ($1);
+ write_exp_elt_opcode (OP_DOUBLE);
+ }
+ | SET_LITERAL
+ {
+ $$ = 0; /* FIXME */
+ }
+ | EMPTINESS_LITERAL
+ {
+ $$ = 0; /* FIXME */
+ }
+ | CHARACTER_STRING_LITERAL
+ {
+ write_exp_elt_opcode (OP_STRING);
+ write_exp_string ($1);
+ write_exp_elt_opcode (OP_STRING);
+ }
+ | BIT_STRING_LITERAL
+ {
+ write_exp_elt_opcode (OP_BITSTRING);
+ write_exp_bitstring ($1);
+ write_exp_elt_opcode (OP_BITSTRING);
+ }
+ ;
+
+/* Z.200, 5.2.5 */
+
+tuple : FIXME_04
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+
+/* Z.200, 5.2.6 */
+
+value_string_element: string_primitive_value '(' start_element ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+/* Z.200, 5.2.7 */
+
+value_string_slice: string_primitive_value '(' left_element ':' right_element ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ | string_primitive_value '(' start_element UP slice_size ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+/* Z.200, 5.2.8 */
+
+value_array_element: array_primitive_value '('
+ /* This is to save the value of arglist_len
+ being accumulated for each dimension. */
+ { start_arglist (); }
+ expression_list ')'
+ {
+ write_exp_elt_opcode (MULTI_SUBSCRIPT);
+ write_exp_elt_longcst ((LONGEST) end_arglist ());
+ write_exp_elt_opcode (MULTI_SUBSCRIPT);
+ }
+ ;
+
+/* Z.200, 5.2.9 */
+
+value_array_slice: array_primitive_value '(' lower_element ':' upper_element ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ | array_primitive_value '(' first_element UP slice_size ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+/* Z.200, 5.2.10 */
+
+value_structure_field: primitive_value FIELD_NAME
+ { write_exp_elt_opcode (STRUCTOP_STRUCT);
+ write_exp_string ($2);
+ write_exp_elt_opcode (STRUCTOP_STRUCT);
+ }
+ ;
+
+/* Z.200, 5.2.11 */
+
+expression_conversion: mode_name parenthesised_expression
+ {
+ write_exp_elt_opcode (UNOP_CAST);
+ write_exp_elt_type ($1.type);
+ write_exp_elt_opcode (UNOP_CAST);
+ }
+ ;
+
+/* Z.200, 5.2.12 */
+
+value_procedure_call: FIXME_05
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+/* Z.200, 5.2.13 */
+
+value_built_in_routine_call: chill_value_built_in_routine_call
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+/* Z.200, 5.2.14 */
+
+start_expression: FIXME_06
+ {
+ $$ = 0; /* FIXME */
+ } /* Not in GNU-Chill */
+ ;
+
+/* Z.200, 5.2.15 */
+
+zero_adic_operator: FIXME_07
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+/* Z.200, 5.2.16 */
+
+parenthesised_expression: '(' expression ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+/* Z.200, 5.3.2 */
+
+expression : operand_0
+ {
+ $$ = 0; /* FIXME */
+ }
+ | single_assignment_action
+ {
+ $$ = 0; /* FIXME */
+ }
+ | conditional_expression
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+conditional_expression : IF boolean_expression then_alternative else_alternative FI
+ {
+ $$ = 0; /* FIXME */
+ }
+ | CASE case_selector_list OF value_case_alternative '[' ELSE sub_expression ']' ESAC
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+then_alternative: THEN subexpression
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+else_alternative: ELSE subexpression
+ {
+ $$ = 0; /* FIXME */
+ }
+ | ELSIF boolean_expression then_alternative else_alternative
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+sub_expression : expression
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+value_case_alternative: case_label_specification ':' sub_expression ';'
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+/* Z.200, 5.3.3 */
+
+operand_0 : operand_1
+ {
+ $$ = 0; /* FIXME */
+ }
+ | operand_0 LOGIOR operand_1
+ {
+ write_exp_elt_opcode (BINOP_BITWISE_IOR);
+ }
+ | operand_0 ORIF operand_1
+ {
+ $$ = 0; /* FIXME */
+ }
+ | operand_0 LOGXOR operand_1
+ {
+ write_exp_elt_opcode (BINOP_BITWISE_XOR);
+ }
+ ;
+
+/* Z.200, 5.3.4 */
+
+operand_1 : operand_2
+ {
+ $$ = 0; /* FIXME */
+ }
+ | operand_1 LOGAND operand_2
+ {
+ write_exp_elt_opcode (BINOP_BITWISE_AND);
+ }
+ | operand_1 ANDIF operand_2
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+/* Z.200, 5.3.5 */
+
+operand_2 : operand_3
+ {
+ $$ = 0; /* FIXME */
+ }
+ | operand_2 '=' operand_3
+ {
+ write_exp_elt_opcode (BINOP_EQUAL);
+ }
+ | operand_2 NOTEQUAL operand_3
+ {
+ write_exp_elt_opcode (BINOP_NOTEQUAL);
+ }
+ | operand_2 '>' operand_3
+ {
+ write_exp_elt_opcode (BINOP_GTR);
+ }
+ | operand_2 GTR operand_3
+ {
+ write_exp_elt_opcode (BINOP_GEQ);
+ }
+ | operand_2 '<' operand_3
+ {
+ write_exp_elt_opcode (BINOP_LESS);
+ }
+ | operand_2 LEQ operand_3
+ {
+ write_exp_elt_opcode (BINOP_LEQ);
+ }
+ | operand_2 IN operand_3
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+
+/* Z.200, 5.3.6 */
+
+operand_3 : operand_4
+ {
+ $$ = 0; /* FIXME */
+ }
+ | operand_3 '+' operand_4
+ {
+ write_exp_elt_opcode (BINOP_ADD);
+ }
+ | operand_3 '-' operand_4
+ {
+ write_exp_elt_opcode (BINOP_SUB);
+ }
+ | operand_3 SLASH_SLASH operand_4
+ {
+ write_exp_elt_opcode (BINOP_CONCAT);
+ }
+ ;
+
+/* Z.200, 5.3.7 */
+
+operand_4 : operand_5
+ {
+ $$ = 0; /* FIXME */
+ }
+ | operand_4 '*' operand_5
+ {
+ write_exp_elt_opcode (BINOP_MUL);
+ }
+ | operand_4 '/' operand_5
+ {
+ write_exp_elt_opcode (BINOP_DIV);
+ }
+ | operand_4 MOD operand_5
+ {
+ write_exp_elt_opcode (BINOP_MOD);
+ }
+ | operand_4 REM operand_5
+ {
+ write_exp_elt_opcode (BINOP_REM);
+ }
+ ;
+
+/* Z.200, 5.3.8 */
+
+operand_5 : operand_6
+ {
+ $$ = 0; /* FIXME */
+ }
+ | '-' operand_6
+ {
+ write_exp_elt_opcode (UNOP_NEG);
+ }
+ | NOT operand_6
+ {
+ write_exp_elt_opcode (UNOP_LOGICAL_NOT);
+ }
+ | parenthesised_expression literal
+/* We require the string operand to be a literal, to avoid some
+ nasty parsing ambiguities. */
+ {
+ write_exp_elt_opcode (BINOP_CONCAT);
+ }
+ ;
+
+/* Z.200, 5.3.9 */
+
+operand_6 : POINTER location
+ {
+ write_exp_elt_opcode (UNOP_ADDR);
+ }
+ | RECEIVE buffer_location
+ {
+ $$ = 0; /* FIXME */
+ }
+ | primitive_value
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+
+/* Z.200, 6.2 */
+
+single_assignment_action :
+ location GDB_ASSIGNMENT value
+ {
+ write_exp_elt_opcode (BINOP_ASSIGN);
+ }
+ ;
+
+/* Z.200, 6.20.3 */
+
+chill_value_built_in_routine_call :
+ NUM '(' expression ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ | PRED '(' expression ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ | SUCC '(' expression ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ | ABS '(' expression ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ | CARD '(' expression ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ | MAX_TOKEN '(' expression ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ | MIN_TOKEN '(' expression ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ | SIZE '(' location ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ | SIZE '(' mode_argument ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ | UPPER '(' upper_lower_argument ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ | LOWER '(' upper_lower_argument ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ | LENGTH '(' length_argument ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+mode_argument : mode_name
+ {
+ $$ = 0; /* FIXME */
+ }
+ | array_mode_name '(' expression ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ | string_mode_name '(' expression ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ | variant_structure_mode_name '(' expression_list ')'
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+mode_name : TYPENAME
+ ;
+
+upper_lower_argument : expression
+ {
+ $$ = 0; /* FIXME */
+ }
+ | mode_name
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+length_argument : expression
+ {
+ $$ = 0; /* FIXME */
+ }
+ ;
+
+/* Z.200, 12.4.3 */
+
+array_primitive_value : primitive_value
+ {
+ $$ = 0;
+ }
+ ;
+
+
+/* Things which still need productions... */
+
+array_mode_name : FIXME_08 { $$ = 0; }
+string_mode_name : FIXME_09 { $$ = 0; }
+variant_structure_mode_name: FIXME_10 { $$ = 0; }
+synonym_name : FIXME_11 { $$ = 0; }
+value_enumeration_name : FIXME_12 { $$ = 0; }
+value_do_with_name : FIXME_13 { $$ = 0; }
+value_receive_name : FIXME_14 { $$ = 0; }
+string_primitive_value : FIXME_15 { $$ = 0; }
+start_element : FIXME_16 { $$ = 0; }
+left_element : FIXME_17 { $$ = 0; }
+right_element : FIXME_18 { $$ = 0; }
+slice_size : FIXME_19 { $$ = 0; }
+lower_element : FIXME_20 { $$ = 0; }
+upper_element : FIXME_21 { $$ = 0; }
+first_element : FIXME_22 { $$ = 0; }
+boolean_expression : FIXME_26 { $$ = 0; }
+case_selector_list : FIXME_27 { $$ = 0; }
+subexpression : FIXME_28 { $$ = 0; }
+case_label_specification: FIXME_29 { $$ = 0; }
+buffer_location : FIXME_30 { $$ = 0; }
+
+%%
+
+/* Implementation of a dynamically expandable buffer for processing input
+ characters acquired through lexptr and building a value to return in
+ yylval. */
+
+static char *tempbuf; /* Current buffer contents */
+static int tempbufsize; /* Size of allocated buffer */
+static int tempbufindex; /* Current index into buffer */
+
+#define GROWBY_MIN_SIZE 64 /* Minimum amount to grow buffer by */
+
+#define CHECKBUF(size) \
+ do { \
+ if (tempbufindex + (size) >= tempbufsize) \
+ { \
+ growbuf_by_size (size); \
+ } \
+ } while (0);
+
+/* Grow the static temp buffer if necessary, including allocating the first one
+ on demand. */
+
+static void
+growbuf_by_size (count)
+ int count;
+{
+ int growby;
+
+ growby = max (count, GROWBY_MIN_SIZE);
+ tempbufsize += growby;
+ if (tempbuf == NULL)
+ {
+ tempbuf = (char *) malloc (tempbufsize);
+ }
+ else
+ {
+ tempbuf = (char *) realloc (tempbuf, tempbufsize);
+ }
+}
+
+/* Try to consume a simple name string token. If successful, returns
+ a pointer to a nullbyte terminated copy of the name that can be used
+ in symbol table lookups. If not successful, returns NULL. */
+
+static char *
+match_simple_name_string ()
+{
+ char *tokptr = lexptr;
+
+ if (isalpha (*tokptr))
+ {
+ char *result;
+ do {
+ tokptr++;
+ } while (isalnum (*tokptr) || (*tokptr == '_'));
+ yylval.sval.ptr = lexptr;
+ yylval.sval.length = tokptr - lexptr;
+ lexptr = tokptr;
+ result = copy_name (yylval.sval);
+ for (tokptr = result; *tokptr; tokptr++)
+ if (isupper (*tokptr))
+ *tokptr = tolower(*tokptr);
+ return result;
+ }
+ return (NULL);
+}
+
+/* Start looking for a value composed of valid digits as set by the base
+ in use. Note that '_' characters are valid anywhere, in any quantity,
+ and are simply ignored. Since we must find at least one valid digit,
+ or reject this token as an integer literal, we keep track of how many
+ digits we have encountered. */
+
+static int
+decode_integer_value (base, tokptrptr, ivalptr)
+ int base;
+ char **tokptrptr;
+ int *ivalptr;
+{
+ char *tokptr = *tokptrptr;
+ int temp;
+ int digits = 0;
+
+ while (*tokptr != '\0')
+ {
+ temp = tolower (*tokptr);
+ tokptr++;
+ switch (temp)
+ {
+ case '_':
+ continue;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ temp -= '0';
+ break;
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ temp -= 'a';
+ temp += 10;
+ break;
+ default:
+ temp = base;
+ break;
+ }
+ if (temp < base)
+ {
+ digits++;
+ *ivalptr *= base;
+ *ivalptr += temp;
+ }
+ else
+ {
+ /* Found something not in domain for current base. */
+ tokptr--; /* Unconsume what gave us indigestion. */
+ break;
+ }
+ }
+
+ /* If we didn't find any digits, then we don't have a valid integer
+ value, so reject the entire token. Otherwise, update the lexical
+ scan pointer, and return non-zero for success. */
+
+ if (digits == 0)
+ {
+ return (0);
+ }
+ else
+ {
+ *tokptrptr = tokptr;
+ return (1);
+ }
+}
+
+static int
+decode_integer_literal (valptr, tokptrptr)
+ int *valptr;
+ char **tokptrptr;
+{
+ char *tokptr = *tokptrptr;
+ int base = 0;
+ int ival = 0;
+ int explicit_base = 0;
+
+ /* Look for an explicit base specifier, which is optional. */
+
+ switch (*tokptr)
+ {
+ case 'd':
+ case 'D':
+ explicit_base++;
+ base = 10;
+ tokptr++;
+ break;
+ case 'b':
+ case 'B':
+ explicit_base++;
+ base = 2;
+ tokptr++;
+ break;
+ case 'h':
+ case 'H':
+ explicit_base++;
+ base = 16;
+ tokptr++;
+ break;
+ case 'o':
+ case 'O':
+ explicit_base++;
+ base = 8;
+ tokptr++;
+ break;
+ default:
+ base = 10;
+ break;
+ }
+
+ /* If we found an explicit base ensure that the character after the
+ explicit base is a single quote. */
+
+ if (explicit_base && (*tokptr++ != '\''))
+ {
+ return (0);
+ }
+
+ /* Attempt to decode whatever follows as an integer value in the
+ indicated base, updating the token pointer in the process and
+ computing the value into ival. Also, if we have an explicit
+ base, then the next character must not be a single quote, or we
+ have a bitstring literal, so reject the entire token in this case.
+ Otherwise, update the lexical scan pointer, and return non-zero
+ for success. */
+
+ if (!decode_integer_value (base, &tokptr, &ival))
+ {
+ return (0);
+ }
+ else if (explicit_base && (*tokptr == '\''))
+ {
+ return (0);
+ }
+ else
+ {
+ *valptr = ival;
+ *tokptrptr = tokptr;
+ return (1);
+ }
+}
+
+/* If it wasn't for the fact that floating point values can contain '_'
+ characters, we could just let strtod do all the hard work by letting it
+ try to consume as much of the current token buffer as possible and
+ find a legal conversion. Unfortunately we need to filter out the '_'
+ characters before calling strtod, which we do by copying the other
+ legal chars to a local buffer to be converted. However since we also
+ need to keep track of where the last unconsumed character in the input
+ buffer is, we have transfer only as many characters as may compose a
+ legal floating point value. */
+
+static int
+match_float_literal ()
+{
+ char *tokptr = lexptr;
+ char *buf;
+ char *copy;
+ double dval;
+ extern double strtod ();
+
+ /* Make local buffer in which to build the string to convert. This is
+ required because underscores are valid in chill floating point numbers
+ but not in the string passed to strtod to convert. The string will be
+ no longer than our input string. */
+
+ copy = buf = (char *) alloca (strlen (tokptr) + 1);
+
+ /* Transfer all leading digits to the conversion buffer, discarding any
+ underscores. */
+
+ while (isdigit (*tokptr) || *tokptr == '_')
+ {
+ if (*tokptr != '_')
+ {
+ *copy++ = *tokptr;
+ }
+ tokptr++;
+ }
+
+ /* Now accept either a '.', or one of [eEdD]. Dot is legal regardless
+ of whether we found any leading digits, and we simply accept it and
+ continue on to look for the fractional part and/or exponent. One of
+ [eEdD] is legal only if we have seen digits, and means that there
+ is no fractional part. If we find neither of these, then this is
+ not a floating point number, so return failure. */
+
+ switch (*tokptr++)
+ {
+ case '.':
+ /* Accept and then look for fractional part and/or exponent. */
+ *copy++ = '.';
+ break;
+
+ case 'e':
+ case 'E':
+ case 'd':
+ case 'D':
+ if (copy == buf)
+ {
+ return (0);
+ }
+ *copy++ = 'e';
+ goto collect_exponent;
+ break;
+
+ default:
+ return (0);
+ break;
+ }
+
+ /* We found a '.', copy any fractional digits to the conversion buffer, up
+ to the first nondigit, non-underscore character. */
+
+ while (isdigit (*tokptr) || *tokptr == '_')
+ {
+ if (*tokptr != '_')
+ {
+ *copy++ = *tokptr;
+ }
+ tokptr++;
+ }
+
+ /* Look for an exponent, which must start with one of [eEdD]. If none
+ is found, jump directly to trying to convert what we have collected
+ so far. */
+
+ switch (*tokptr)
+ {
+ case 'e':
+ case 'E':
+ case 'd':
+ case 'D':
+ *copy++ = 'e';
+ tokptr++;
+ break;
+ default:
+ goto convert_float;
+ break;
+ }
+
+ /* Accept an optional '-' or '+' following one of [eEdD]. */
+
+ collect_exponent:
+ if (*tokptr == '+' || *tokptr == '-')
+ {
+ *copy++ = *tokptr++;
+ }
+
+ /* Now copy an exponent into the conversion buffer. Note that at the
+ moment underscores are *not* allowed in exponents. */
+
+ while (isdigit (*tokptr))
+ {
+ *copy++ = *tokptr++;
+ }
+
+ /* If we transfered any chars to the conversion buffer, try to interpret its
+ contents as a floating point value. If any characters remain, then we
+ must not have a valid floating point string. */
+
+ convert_float:
+ *copy = '\0';
+ if (copy != buf)
+ {
+ dval = strtod (buf, &copy);
+ if (*copy == '\0')
+ {
+ yylval.dval = dval;
+ lexptr = tokptr;
+ return (FLOAT_LITERAL);
+ }
+ }
+ return (0);
+}
+
+/* Recognize a string literal. A string literal is a nonzero sequence
+ of characters enclosed in matching single or double quotes, except that
+ a single character inside single quotes is a character literal, which
+ we reject as a string literal. To embed the terminator character inside
+ a string, it is simply doubled (I.E. "this""is""one""string") */
+
+static int
+match_string_literal ()
+{
+ char *tokptr = lexptr;
+
+ for (tempbufindex = 0, tokptr++; *tokptr != '\0'; tokptr++)
+ {
+ CHECKBUF (1);
+ if (*tokptr == *lexptr)
+ {
+ if (*(tokptr + 1) == *lexptr)
+ {
+ tokptr++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ tempbuf[tempbufindex++] = *tokptr;
+ }
+ if (*tokptr == '\0' /* no terminator */
+ || tempbufindex == 0 /* no string */
+ || (tempbufindex == 1 && *tokptr == '\'')) /* char literal */
+ {
+ return (0);
+ }
+ else
+ {
+ tempbuf[tempbufindex] = '\0';
+ yylval.sval.ptr = tempbuf;
+ yylval.sval.length = tempbufindex;
+ lexptr = ++tokptr;
+ return (CHARACTER_STRING_LITERAL);
+ }
+}
+
+/* Recognize a character literal. A character literal is single character
+ or a control sequence, enclosed in single quotes. A control sequence
+ is a comma separated list of one or more integer literals, enclosed
+ in parenthesis and introduced with a circumflex character.
+
+ EX: 'a' '^(7)' '^(7,8)'
+
+ As a GNU chill extension, the syntax C'xx' is also recognized as a
+ character literal, where xx is a hex value for the character.
+
+ Note that more than a single character, enclosed in single quotes, is
+ a string literal.
+
+ Also note that the control sequence form is not in GNU Chill since it
+ is ambiguous with the string literal form using single quotes. I.E.
+ is '^(7)' a character literal or a string literal. In theory it it
+ possible to tell by context, but GNU Chill doesn't accept the control
+ sequence form, so neither do we (for now the code is disabled).
+
+ Returns CHARACTER_LITERAL if a match is found.
+ */
+
+static int
+match_character_literal ()
+{
+ char *tokptr = lexptr;
+ int ival = 0;
+
+ if ((tolower (*tokptr) == 'c') && (*(tokptr + 1) == '\''))
+ {
+ /* We have a GNU chill extension form, so skip the leading "C'",
+ decode the hex value, and then ensure that we have a trailing
+ single quote character. */
+ tokptr += 2;
+ if (!decode_integer_value (16, &tokptr, &ival) || (*tokptr != '\''))
+ {
+ return (0);
+ }
+ tokptr++;
+ }
+ else if (*tokptr == '\'')
+ {
+ tokptr++;
+
+ /* Determine which form we have, either a control sequence or the
+ single character form. */
+
+ if ((*tokptr == '^') && (*(tokptr + 1) == '('))
+ {
+#if 0 /* Disable, see note above. -fnf */
+ /* Match and decode a control sequence. Return zero if we don't
+ find a valid integer literal, or if the next unconsumed character
+ after the integer literal is not the trailing ')'.
+ FIXME: We currently don't handle the multiple integer literal
+ form. */
+ tokptr += 2;
+ if (!decode_integer_literal (&ival, &tokptr) || (*tokptr++ != ')'))
+ {
+ return (0);
+ }
+#else
+ return (0);
+#endif
+ }
+ else
+ {
+ ival = *tokptr++;
+ }
+
+ /* The trailing quote has not yet been consumed. If we don't find
+ it, then we have no match. */
+
+ if (*tokptr++ != '\'')
+ {
+ return (0);
+ }
+ }
+ else
+ {
+ /* Not a character literal. */
+ return (0);
+ }
+ yylval.typed_val.val = ival;
+ yylval.typed_val.type = builtin_type_chill_char;
+ lexptr = tokptr;
+ return (CHARACTER_LITERAL);
+}
+
+/* Recognize an integer literal, as specified in Z.200 sec 5.2.4.2.
+ Note that according to 5.2.4.2, a single "_" is also a valid integer
+ literal, however GNU-chill requires there to be at least one "digit"
+ in any integer literal. */
+
+static int
+match_integer_literal ()
+{
+ char *tokptr = lexptr;
+ int ival;
+
+ if (!decode_integer_literal (&ival, &tokptr))
+ {
+ return (0);
+ }
+ else
+ {
+ yylval.typed_val.val = ival;
+ yylval.typed_val.type = builtin_type_int;
+ lexptr = tokptr;
+ return (INTEGER_LITERAL);
+ }
+}
+
+/* Recognize a bit-string literal, as specified in Z.200 sec 5.2.4.8
+ Note that according to 5.2.4.8, a single "_" is also a valid bit-string
+ literal, however GNU-chill requires there to be at least one "digit"
+ in any bit-string literal. */
+
+static int
+match_bitstring_literal ()
+{
+ char *tokptr = lexptr;
+ int mask;
+ int bitoffset = 0;
+ int bitcount = 0;
+ int base;
+ int digit;
+
+ tempbufindex = 0;
+
+ /* Look for the required explicit base specifier. */
+
+ switch (*tokptr++)
+ {
+ case 'b':
+ case 'B':
+ base = 2;
+ break;
+ case 'o':
+ case 'O':
+ base = 8;
+ break;
+ case 'h':
+ case 'H':
+ base = 16;
+ break;
+ default:
+ return (0);
+ break;
+ }
+
+ /* Ensure that the character after the explicit base is a single quote. */
+
+ if (*tokptr++ != '\'')
+ {
+ return (0);
+ }
+
+ while (*tokptr != '\0' && *tokptr != '\'')
+ {
+ digit = tolower (*tokptr);
+ tokptr++;
+ switch (digit)
+ {
+ case '_':
+ continue;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ digit -= '0';
+ break;
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ digit -= 'a';
+ digit += 10;
+ break;
+ default:
+ return (0);
+ break;
+ }
+ if (digit >= base)
+ {
+ /* Found something not in domain for current base. */
+ return (0);
+ }
+ else
+ {
+ /* Extract bits from digit, starting with the msbit appropriate for
+ the current base, and packing them into the bitstring byte,
+ starting at the lsbit. */
+ for (mask = (base >> 1); mask > 0; mask >>= 1)
+ {
+ bitcount++;
+ CHECKBUF (1);
+ if (digit & mask)
+ {
+ tempbuf[tempbufindex] |= (1 << bitoffset);
+ }
+ bitoffset++;
+ if (bitoffset == HOST_CHAR_BIT)
+ {
+ bitoffset = 0;
+ tempbufindex++;
+ }
+ }
+ }
+ }
+
+ /* Verify that we consumed everything up to the trailing single quote,
+ and that we found some bits (IE not just underbars). */
+
+ if (*tokptr++ != '\'')
+ {
+ return (0);
+ }
+ else
+ {
+ yylval.sval.ptr = tempbuf;
+ yylval.sval.length = bitcount;
+ lexptr = tokptr;
+ return (BIT_STRING_LITERAL);
+ }
+}
+
+/* Recognize tokens that start with '$'. These include:
+
+ $regname A native register name or a "standard
+ register name".
+ Return token GDB_REGNAME.
+
+ $variable A convenience variable with a name chosen
+ by the user.
+ Return token GDB_VARIABLE.
+
+ $digits Value history with index <digits>, starting
+ from the first value which has index 1.
+ Return GDB_LAST.
+
+ $$digits Value history with index <digits> relative
+ to the last value. I.E. $$0 is the last
+ value, $$1 is the one previous to that, $$2
+ is the one previous to $$1, etc.
+ Return token GDB_LAST.
+
+ $ | $0 | $$0 The last value in the value history.
+ Return token GDB_LAST.
+
+ $$ An abbreviation for the second to the last
+ value in the value history, I.E. $$1
+ Return token GDB_LAST.
+
+ Note that we currently assume that register names and convenience
+ variables follow the convention of starting with a letter or '_'.
+
+ */
+
+static int
+match_dollar_tokens ()
+{
+ char *tokptr;
+ int regno;
+ int namelength;
+ int negate;
+ int ival;
+
+ /* We will always have a successful match, even if it is just for
+ a single '$', the abbreviation for $$0. So advance lexptr. */
+
+ tokptr = ++lexptr;
+
+ if (*tokptr == '_' || isalpha (*tokptr))
+ {
+ /* Look for a match with a native register name, usually something
+ like "r0" for example. */
+
+ for (regno = 0; regno < NUM_REGS; regno++)
+ {
+ namelength = strlen (reg_names[regno]);
+ if (STREQN (tokptr, reg_names[regno], namelength)
+ && !isalnum (tokptr[namelength]))
+ {
+ yylval.lval = regno;
+ lexptr += namelength + 1;
+ return (GDB_REGNAME);
+ }
+ }
+
+ /* Look for a match with a standard register name, usually something
+ like "pc", which gdb always recognizes as the program counter
+ regardless of what the native register name is. */
+
+ for (regno = 0; regno < num_std_regs; regno++)
+ {
+ namelength = strlen (std_regs[regno].name);
+ if (STREQN (tokptr, std_regs[regno].name, namelength)
+ && !isalnum (tokptr[namelength]))
+ {
+ yylval.lval = std_regs[regno].regnum;
+ lexptr += namelength;
+ return (GDB_REGNAME);
+ }
+ }
+
+ /* Attempt to match against a convenience variable. Note that
+ this will always succeed, because if no variable of that name
+ already exists, the lookup_internalvar will create one for us.
+ Also note that both lexptr and tokptr currently point to the
+ start of the input string we are trying to match, and that we
+ have already tested the first character for non-numeric, so we
+ don't have to treat it specially. */
+
+ while (*tokptr == '_' || isalnum (*tokptr))
+ {
+ tokptr++;
+ }
+ yylval.sval.ptr = lexptr;
+ yylval.sval.length = tokptr - lexptr;
+ yylval.ivar = lookup_internalvar (copy_name (yylval.sval));
+ lexptr = tokptr;
+ return (GDB_VARIABLE);
+ }
+
+ /* Since we didn't match against a register name or convenience
+ variable, our only choice left is a history value. */
+
+ if (*tokptr == '$')
+ {
+ negate = 1;
+ ival = 1;
+ tokptr++;
+ }
+ else
+ {
+ negate = 0;
+ ival = 0;
+ }
+
+ /* Attempt to decode more characters as an integer value giving
+ the index in the history list. If successful, the value will
+ overwrite ival (currently 0 or 1), and if not, ival will be
+ left alone, which is good since it is currently correct for
+ the '$' or '$$' case. */
+
+ decode_integer_literal (&ival, &tokptr);
+ yylval.lval = negate ? -ival : ival;
+ lexptr = tokptr;
+ return (GDB_LAST);
+}
+
+struct token
+{
+ char *operator;
+ int token;
+};
+
+static const struct token idtokentab[] =
+{
+ { "length", LENGTH },
+ { "lower", LOWER },
+ { "upper", UPPER },
+ { "andif", ANDIF },
+ { "pred", PRED },
+ { "succ", SUCC },
+ { "card", CARD },
+ { "size", SIZE },
+ { "orif", ORIF },
+ { "num", NUM },
+ { "abs", ABS },
+ { "max", MAX_TOKEN },
+ { "min", MIN_TOKEN },
+ { "mod", MOD },
+ { "rem", REM },
+ { "not", NOT },
+ { "xor", LOGXOR },
+ { "and", LOGAND },
+ { "in", IN },
+ { "or", LOGIOR }
+};
+
+static const struct token tokentab2[] =
+{
+ { ":=", GDB_ASSIGNMENT },
+ { "//", SLASH_SLASH },
+ { "->", POINTER },
+ { "/=", NOTEQUAL },
+ { "<=", LEQ },
+ { ">=", GTR }
+};
+
+/* Read one token, getting characters through lexptr. */
+/* This is where we will check to make sure that the language and the
+ operators used are compatible. */
+
+static int
+yylex ()
+{
+ unsigned int i;
+ int token;
+ char *simplename;
+ struct symbol *sym;
+
+ /* Skip over any leading whitespace. */
+ while (isspace (*lexptr))
+ {
+ lexptr++;
+ }
+ /* Look for special single character cases which can't be the first
+ character of some other multicharacter token. */
+ switch (*lexptr)
+ {
+ case '\0':
+ return (0);
+ case ',':
+ case '=':
+ case ';':
+ case '!':
+ case '+':
+ case '*':
+ case '(':
+ case ')':
+ case '[':
+ case ']':
+ return (*lexptr++);
+ }
+ /* Look for characters which start a particular kind of multicharacter
+ token, such as a character literal, register name, convenience
+ variable name, string literal, etc. */
+ switch (*lexptr)
+ {
+ case '\'':
+ case '\"':
+ /* First try to match a string literal, which is any nonzero
+ sequence of characters enclosed in matching single or double
+ quotes, except that a single character inside single quotes
+ is a character literal, so we have to catch that case also. */
+ token = match_string_literal ();
+ if (token != 0)
+ {
+ return (token);
+ }
+ if (*lexptr == '\'')
+ {
+ token = match_character_literal ();
+ if (token != 0)
+ {
+ return (token);
+ }
+ }
+ break;
+ case 'C':
+ case 'c':
+ token = match_character_literal ();
+ if (token != 0)
+ {
+ return (token);
+ }
+ break;
+ case '$':
+ token = match_dollar_tokens ();
+ if (token != 0)
+ {
+ return (token);
+ }
+ break;
+ }
+ /* See if it is a special token of length 2. */
+ for (i = 0; i < sizeof (tokentab2) / sizeof (tokentab2[0]); i++)
+ {
+ if (STREQN (lexptr, tokentab2[i].operator, 2))
+ {
+ lexptr += 2;
+ return (tokentab2[i].token);
+ }
+ }
+ /* Look for single character cases which which could be the first
+ character of some other multicharacter token, but aren't, or we
+ would already have found it. */
+ switch (*lexptr)
+ {
+ case '-':
+ case ':':
+ case '/':
+ case '<':
+ case '>':
+ return (*lexptr++);
+ }
+ /* Look for a float literal before looking for an integer literal, so
+ we match as much of the input stream as possible. */
+ token = match_float_literal ();
+ if (token != 0)
+ {
+ return (token);
+ }
+ token = match_bitstring_literal ();
+ if (token != 0)
+ {
+ return (token);
+ }
+ token = match_integer_literal ();
+ if (token != 0)
+ {
+ return (token);
+ }
+
+ /* Try to match a simple name string, and if a match is found, then
+ further classify what sort of name it is and return an appropriate
+ token. Note that attempting to match a simple name string consumes
+ the token from lexptr, so we can't back out if we later find that
+ we can't classify what sort of name it is. */
+
+ simplename = match_simple_name_string ();
+
+ if (simplename != NULL)
+ {
+ /* See if it is a reserved identifier. */
+ for (i = 0; i < sizeof (idtokentab) / sizeof (idtokentab[0]); i++)
+ {
+ if (STREQ (simplename, idtokentab[i].operator))
+ {
+ return (idtokentab[i].token);
+ }
+ }
+
+ /* Look for other special tokens. */
+ if (STREQ (simplename, "true"))
+ {
+ yylval.ulval = 1;
+ return (BOOLEAN_LITERAL);
+ }
+ if (STREQ (simplename, "false"))
+ {
+ yylval.ulval = 0;
+ return (BOOLEAN_LITERAL);
+ }
+
+ sym = lookup_symbol (simplename, expression_context_block,
+ VAR_NAMESPACE, (int *) NULL,
+ (struct symtab **) NULL);
+ if (sym != NULL)
+ {
+ yylval.ssym.stoken.ptr = NULL;
+ yylval.ssym.stoken.length = 0;
+ yylval.ssym.sym = sym;
+ yylval.ssym.is_a_field_of_this = 0; /* FIXME, C++'ism */
+ switch (SYMBOL_CLASS (sym))
+ {
+ case LOC_BLOCK:
+ /* Found a procedure name. */
+ return (GENERAL_PROCEDURE_NAME);
+ case LOC_STATIC:
+ /* Found a global or local static variable. */
+ return (LOCATION_NAME);
+ case LOC_REGISTER:
+ case LOC_ARG:
+ case LOC_REF_ARG:
+ case LOC_REGPARM:
+ case LOC_REGPARM_ADDR:
+ case LOC_LOCAL:
+ case LOC_LOCAL_ARG:
+ case LOC_BASEREG:
+ case LOC_BASEREG_ARG:
+ if (innermost_block == NULL
+ || contained_in (block_found, innermost_block))
+ {
+ innermost_block = block_found;
+ }
+ return (LOCATION_NAME);
+ break;
+ case LOC_CONST:
+ case LOC_LABEL:
+ return (LOCATION_NAME);
+ break;
+ case LOC_TYPEDEF:
+ yylval.tsym.type = SYMBOL_TYPE (sym);
+ return TYPENAME;
+ case LOC_UNDEF:
+ case LOC_CONST_BYTES:
+ case LOC_OPTIMIZED_OUT:
+ error ("Symbol \"%s\" names no location.", simplename);
+ break;
+ }
+ }
+ else if (!have_full_symbols () && !have_partial_symbols ())
+ {
+ error ("No symbol table is loaded. Use the \"file\" command.");
+ }
+ else
+ {
+ error ("No symbol \"%s\" in current context.", simplename);
+ }
+ }
+
+ /* Catch single character tokens which are not part of some
+ longer token. */
+
+ switch (*lexptr)
+ {
+ case '.': /* Not float for example. */
+ lexptr++;
+ while (isspace (*lexptr)) lexptr++;
+ simplename = match_simple_name_string ();
+ if (!simplename)
+ return '.';
+ return FIELD_NAME;
+ }
+
+ return (ILLEGAL_TOKEN);
+}
+
+void
+yyerror (msg)
+ char *msg; /* unused */
+{
+ printf ("Parsing: %s\n", lexptr);
+ if (yychar < 256)
+ {
+ error ("Invalid syntax in expression near character '%c'.", yychar);
+ }
+ else
+ {
+ error ("Invalid syntax in expression");
+ }
+}
diff --git a/gnu/usr.bin/gdb/gdb/ch-lang.c b/gnu/usr.bin/gdb/gdb/ch-lang.c
new file mode 100644
index 0000000..2f74061
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/ch-lang.c
@@ -0,0 +1,341 @@
+/* Chill language support routines for GDB, the GNU debugger.
+ Copyright 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "expression.h"
+#include "parser-defs.h"
+#include "language.h"
+#include "ch-lang.h"
+
+
+/* For now, Chill uses a simple mangling algorithm whereby you simply
+ discard everything after the occurance of two successive CPLUS_MARKER
+ characters to derive the demangled form. */
+
+char *
+chill_demangle (mangled)
+ const char *mangled;
+{
+ char *joiner;
+ char *demangled;
+
+ joiner = strchr (mangled, CPLUS_MARKER);
+ if (joiner != NULL && *(joiner + 1) == CPLUS_MARKER)
+ {
+ demangled = savestring (mangled, joiner - mangled);
+ }
+ else
+ {
+ demangled = NULL;
+ }
+ return (demangled);
+}
+
+static void
+chill_printchar (c, stream)
+ register int c;
+ FILE *stream;
+{
+ c &= 0xFF; /* Avoid sign bit follies */
+
+ if (PRINT_LITERAL_FORM (c))
+ {
+ fprintf_filtered (stream, "'%c'", c);
+ }
+ else
+ {
+ fprintf_filtered (stream, "C'%.2x'", (unsigned int) c);
+ }
+}
+
+/* Print the character string STRING, printing at most LENGTH characters.
+ Printing stops early if the number hits print_max; repeat counts
+ are printed as appropriate. Print ellipses at the end if we
+ had to stop before printing LENGTH characters, or if FORCE_ELLIPSES.
+ Note that gdb maintains the length of strings without counting the
+ terminating null byte, while chill strings are typically written with
+ an explicit null byte. So we always assume an implied null byte
+ until gdb is able to maintain non-null terminated strings as well
+ as null terminated strings (FIXME).
+ */
+
+static void
+chill_printstr (stream, string, length, force_ellipses)
+ FILE *stream;
+ char *string;
+ unsigned int length;
+ int force_ellipses;
+{
+ register unsigned int i;
+ unsigned int things_printed = 0;
+ int in_literal_form = 0;
+ int in_control_form = 0;
+ int need_slashslash = 0;
+ unsigned int c;
+ extern int repeat_count_threshold;
+ extern int print_max;
+
+ if (length == 0)
+ {
+ chill_printchar ('\0', stream);
+ return;
+ }
+
+ for (i = 0; i < length && things_printed < print_max; ++i)
+ {
+ /* Position of the character we are examining
+ to see whether it is repeated. */
+ unsigned int rep1;
+ /* Number of repetitions we have detected so far. */
+ unsigned int reps;
+
+ QUIT;
+
+ if (need_slashslash)
+ {
+ fputs_filtered ("//", stream);
+ need_slashslash = 0;
+ }
+
+ rep1 = i + 1;
+ reps = 1;
+ while (rep1 < length && string[rep1] == string[i])
+ {
+ ++rep1;
+ ++reps;
+ }
+
+ c = string[i];
+ if (reps > repeat_count_threshold)
+ {
+ if (in_control_form || in_literal_form)
+ {
+ fputs_filtered ("'//", stream);
+ in_control_form = in_literal_form = 0;
+ }
+ chill_printchar (c, stream);
+ fprintf_filtered (stream, "<repeats %u times>", reps);
+ i = rep1 - 1;
+ things_printed += repeat_count_threshold;
+ need_slashslash = 1;
+ }
+ else
+ {
+ if (PRINT_LITERAL_FORM (c))
+ {
+ if (!in_literal_form)
+ {
+ if (in_control_form)
+ {
+ fputs_filtered ("'//", stream);
+ in_control_form = 0;
+ }
+ fputs_filtered ("'", stream);
+ in_literal_form = 1;
+ }
+ fprintf_filtered (stream, "%c", c);
+ }
+ else
+ {
+ if (!in_control_form)
+ {
+ if (in_literal_form)
+ {
+ fputs_filtered ("'//", stream);
+ in_literal_form = 0;
+ }
+ fputs_filtered ("c'", stream);
+ in_control_form = 1;
+ }
+ fprintf_filtered (stream, "%.2x", c);
+ }
+ ++things_printed;
+ }
+ }
+
+ /* Terminate the quotes if necessary. */
+ if (in_literal_form || in_control_form)
+ {
+ fputs_filtered ("'", stream);
+ }
+ if (force_ellipses || (i < length))
+ {
+ fputs_filtered ("...", stream);
+ }
+}
+
+static struct type *
+chill_create_fundamental_type (objfile, typeid)
+ struct objfile *objfile;
+ int typeid;
+{
+ register struct type *type = NULL;
+
+ switch (typeid)
+ {
+ default:
+ /* FIXME: For now, if we are asked to produce a type not in this
+ language, create the equivalent of a C integer type with the
+ name "<?type?>". When all the dust settles from the type
+ reconstruction work, this should probably become an error. */
+ type = init_type (TYPE_CODE_INT, 2, 0, "<?type?>", objfile);
+ warning ("internal error: no chill fundamental type %d", typeid);
+ break;
+ case FT_VOID:
+ /* FIXME: Currently the GNU Chill compiler emits some DWARF entries for
+ typedefs, unrelated to anything directly in the code being compiled,
+ that have some FT_VOID types. Just fake it for now. */
+ type = init_type (TYPE_CODE_VOID, 0, 0, "<?VOID?>", objfile);
+ break;
+ case FT_BOOLEAN:
+ type = init_type (TYPE_CODE_BOOL, 1, TYPE_FLAG_UNSIGNED, "BOOL", objfile);
+ break;
+ case FT_CHAR:
+ type = init_type (TYPE_CODE_CHAR, 1, TYPE_FLAG_UNSIGNED, "CHAR", objfile);
+ break;
+ case FT_SIGNED_CHAR:
+ type = init_type (TYPE_CODE_INT, 1, TYPE_FLAG_SIGNED, "BYTE", objfile);
+ break;
+ case FT_UNSIGNED_CHAR:
+ type = init_type (TYPE_CODE_INT, 1, TYPE_FLAG_UNSIGNED, "UBYTE", objfile);
+ break;
+ case FT_SHORT: /* Chill ints are 2 bytes */
+ type = init_type (TYPE_CODE_INT, 2, TYPE_FLAG_SIGNED, "INT", objfile);
+ break;
+ case FT_UNSIGNED_SHORT: /* Chill ints are 2 bytes */
+ type = init_type (TYPE_CODE_INT, 2, TYPE_FLAG_UNSIGNED, "UINT", objfile);
+ break;
+ case FT_INTEGER: /* FIXME? */
+ case FT_SIGNED_INTEGER: /* FIXME? */
+ case FT_LONG: /* Chill longs are 4 bytes */
+ case FT_SIGNED_LONG: /* Chill longs are 4 bytes */
+ type = init_type (TYPE_CODE_INT, 4, TYPE_FLAG_SIGNED, "LONG", objfile);
+ break;
+ case FT_UNSIGNED_INTEGER: /* FIXME? */
+ case FT_UNSIGNED_LONG: /* Chill longs are 4 bytes */
+ type = init_type (TYPE_CODE_INT, 4, TYPE_FLAG_UNSIGNED, "ULONG", objfile);
+ break;
+ case FT_FLOAT:
+ type = init_type (TYPE_CODE_FLT, 4, 0, "REAL", objfile);
+ break;
+ case FT_DBL_PREC_FLOAT:
+ type = init_type (TYPE_CODE_FLT, 8, 0, "LONG_REAL", objfile);
+ break;
+ }
+ return (type);
+}
+
+
+/* Table of operators and their precedences for printing expressions. */
+
+static const struct op_print chill_op_print_tab[] = {
+ {"AND", BINOP_LOGICAL_AND, PREC_LOGICAL_AND, 0},
+ {"OR", BINOP_LOGICAL_OR, PREC_LOGICAL_OR, 0},
+ {"NOT", UNOP_LOGICAL_NOT, PREC_PREFIX, 0},
+ {"MOD", BINOP_MOD, PREC_MUL, 0},
+ {"REM", BINOP_REM, PREC_MUL, 0},
+ {":=", BINOP_ASSIGN, PREC_ASSIGN, 1},
+ {"=", BINOP_EQUAL, PREC_EQUAL, 0},
+ {"/=", BINOP_NOTEQUAL, PREC_EQUAL, 0},
+ {"<=", BINOP_LEQ, PREC_ORDER, 0},
+ {">=", BINOP_GEQ, PREC_ORDER, 0},
+ {">", BINOP_GTR, PREC_ORDER, 0},
+ {"<", BINOP_LESS, PREC_ORDER, 0},
+ {"+", BINOP_ADD, PREC_ADD, 0},
+ {"-", BINOP_SUB, PREC_ADD, 0},
+ {"*", BINOP_MUL, PREC_MUL, 0},
+ {"/", BINOP_DIV, PREC_MUL, 0},
+ {"//", BINOP_CONCAT, PREC_PREFIX, 0}, /* FIXME: precedence? */
+ {"-", UNOP_NEG, PREC_PREFIX, 0},
+ {NULL, 0, 0, 0}
+};
+
+
+/* The built-in types of Chill. */
+
+struct type *builtin_type_chill_bool;
+struct type *builtin_type_chill_char;
+struct type *builtin_type_chill_long;
+struct type *builtin_type_chill_ulong;
+struct type *builtin_type_chill_real;
+
+struct type ** const (chill_builtin_types[]) =
+{
+ &builtin_type_chill_bool,
+ &builtin_type_chill_char,
+ &builtin_type_chill_long,
+ &builtin_type_chill_ulong,
+ &builtin_type_chill_real,
+ 0
+};
+
+const struct language_defn chill_language_defn = {
+ "chill",
+ language_chill,
+ chill_builtin_types,
+ range_check_on,
+ type_check_on,
+ chill_parse, /* parser */
+ chill_error, /* parser error function */
+ chill_printchar, /* print a character constant */
+ chill_printstr, /* function to print a string constant */
+ chill_create_fundamental_type,/* Create fundamental type in this language */
+ chill_print_type, /* Print a type using appropriate syntax */
+ chill_val_print, /* Print a value using appropriate syntax */
+ &BUILTIN_TYPE_LONGEST, /* longest signed integral type */
+ &BUILTIN_TYPE_UNSIGNED_LONGEST,/* longest unsigned integral type */
+ &builtin_type_chill_real, /* longest floating point type */
+ {"", "B'", "", ""}, /* Binary format info */
+ {"O'%lo", "O'", "o", ""}, /* Octal format info */
+ {"D'%ld", "D'", "d", ""}, /* Decimal format info */
+ {"H'%lx", "H'", "x", ""}, /* Hex format info */
+ chill_op_print_tab, /* expression operators for printing */
+ LANG_MAGIC
+};
+
+/* Initialization for Chill */
+
+void
+_initialize_chill_language ()
+{
+ builtin_type_chill_bool =
+ init_type (TYPE_CODE_BOOL, TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED,
+ "BOOL", (struct objfile *) NULL);
+ builtin_type_chill_char =
+ init_type (TYPE_CODE_CHAR, TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED,
+ "CHAR", (struct objfile *) NULL);
+ builtin_type_chill_long =
+ init_type (TYPE_CODE_INT, TARGET_LONG_BIT / TARGET_CHAR_BIT,
+ 0,
+ "LONG", (struct objfile *) NULL);
+ builtin_type_chill_ulong =
+ init_type (TYPE_CODE_INT, TARGET_LONG_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED,
+ "ULONG", (struct objfile *) NULL);
+ builtin_type_chill_real =
+ init_type (TYPE_CODE_FLT, TARGET_DOUBLE_BIT / TARGET_CHAR_BIT,
+ 0,
+ "LONG_REAL", (struct objfile *) NULL);
+
+ add_language (&chill_language_defn);
+}
diff --git a/gnu/usr.bin/gdb/gdb/ch-lang.h b/gnu/usr.bin/gdb/gdb/ch-lang.h
new file mode 100644
index 0000000..13579d9
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/ch-lang.h
@@ -0,0 +1,31 @@
+/* Chill language support definitions for GDB, the GNU debugger.
+ Copyright 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+extern int
+chill_parse PARAMS ((void)); /* Defined in ch-exp.y */
+
+extern void
+chill_error PARAMS ((char *)); /* Defined in ch-exp.y */
+
+extern void /* Defined in ch-typeprint.c */
+chill_print_type PARAMS ((struct type *, char *, FILE *, int, int));
+
+extern int
+chill_val_print PARAMS ((struct type *, char *, CORE_ADDR, FILE *, int, int,
+ int, enum val_prettyprint));
diff --git a/gnu/usr.bin/gdb/gdb/ch-typeprint.c b/gnu/usr.bin/gdb/gdb/ch-typeprint.c
new file mode 100644
index 0000000..c3cdcd2
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/ch-typeprint.c
@@ -0,0 +1,225 @@
+/* Support for printing Chill types for GDB, the GNU debugger.
+ Copyright 1986, 1988, 1989, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "obstack.h"
+#include "bfd.h" /* Binary File Description */
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "expression.h"
+#include "value.h"
+#include "gdbcore.h"
+#include "target.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "language.h"
+#include "demangle.h"
+#include "ch-lang.h"
+
+#include <string.h>
+#include <errno.h>
+
+static void
+chill_type_print_base PARAMS ((struct type *, FILE *, int, int));
+
+void
+chill_print_type (type, varstring, stream, show, level)
+ struct type *type;
+ char *varstring;
+ FILE *stream;
+ int show;
+ int level;
+{
+ if (varstring != NULL && *varstring != '\0')
+ {
+ fputs_filtered (varstring, stream);
+ fputs_filtered (" ", stream);
+ }
+ chill_type_print_base (type, stream, show, level);
+}
+
+/* Print the name of the type (or the ultimate pointer target,
+ function value or array element).
+
+ SHOW nonzero means don't print this type as just its name;
+ show its real definition even if it has a name.
+ SHOW zero means print just typename or tag if there is one
+ SHOW negative means abbreviate structure elements.
+ SHOW is decremented for printing of structure elements.
+
+ LEVEL is the depth to indent by.
+ We increase it for some recursive calls. */
+
+static void
+chill_type_print_base (type, stream, show, level)
+ struct type *type;
+ FILE *stream;
+ int show;
+ int level;
+{
+ char *name;
+ register int len;
+ register int i;
+ struct type *index_type;
+ struct type *range_type;
+ LONGEST low_bound;
+ LONGEST high_bound;
+
+ QUIT;
+
+ wrap_here (" ");
+ if (type == NULL)
+ {
+ fputs_filtered ("<type unknown>", stream);
+ return;
+ }
+
+ /* When SHOW is zero or less, and there is a valid type name, then always
+ just print the type name directly from the type. */
+
+ if ((show <= 0) && (TYPE_NAME (type) != NULL))
+ {
+ fputs_filtered (TYPE_NAME (type), stream);
+ return;
+ }
+
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_PTR:
+ if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_VOID)
+ {
+ fprintf_filtered (stream, "PTR");
+ break;
+ }
+ fprintf_filtered (stream, "REF ");
+ chill_type_print_base (TYPE_TARGET_TYPE (type), stream, show, level);
+ break;
+
+ case TYPE_CODE_ARRAY:
+ range_type = TYPE_FIELD_TYPE (type, 0);
+ index_type = TYPE_TARGET_TYPE (range_type);
+ low_bound = TYPE_FIELD_BITPOS (range_type, 0);
+ high_bound = TYPE_FIELD_BITPOS (range_type, 1);
+ fputs_filtered ("ARRAY (", stream);
+ print_type_scalar (index_type, low_bound, stream);
+ fputs_filtered (":", stream);
+ print_type_scalar (index_type, high_bound, stream);
+ fputs_filtered (") ", stream);
+ chill_print_type (TYPE_TARGET_TYPE (type), "", stream, show, level);
+ break;
+
+ case TYPE_CODE_STRING:
+ range_type = TYPE_FIELD_TYPE (type, 0);
+ index_type = TYPE_TARGET_TYPE (range_type);
+ high_bound = TYPE_FIELD_BITPOS (range_type, 1);
+ fputs_filtered ("CHAR (", stream);
+ print_type_scalar (index_type, high_bound + 1, stream);
+ fputs_filtered (")", stream);
+ break;
+
+ case TYPE_CODE_MEMBER:
+ fprintf_filtered (stream, "MEMBER ");
+ chill_type_print_base (TYPE_TARGET_TYPE (type), stream, show, level);
+ break;
+ case TYPE_CODE_REF:
+ fprintf_filtered (stream, "/*LOC*/ ");
+ chill_type_print_base (TYPE_TARGET_TYPE (type), stream, show, level);
+ break;
+ case TYPE_CODE_FUNC:
+ fprintf_filtered (stream, "PROC (?)");
+ chill_type_print_base (TYPE_TARGET_TYPE (type), stream, show, level);
+ break;
+
+ case TYPE_CODE_STRUCT:
+ fprintf_filtered (stream, "STRUCT ");
+ if ((name = type_name_no_tag (type)) != NULL)
+ {
+ fputs_filtered (name, stream);
+ fputs_filtered (" ", stream);
+ wrap_here (" ");
+ }
+ if (show < 0)
+ {
+ fprintf_filtered (stream, "(...)");
+ }
+ else
+ {
+ check_stub_type (type);
+ fprintf_filtered (stream, "(\n");
+ if ((TYPE_NFIELDS (type) == 0) && (TYPE_NFN_FIELDS (type) == 0))
+ {
+ if (TYPE_FLAGS (type) & TYPE_FLAG_STUB)
+ {
+ fprintfi_filtered (level + 4, stream, "<incomplete type>\n");
+ }
+ else
+ {
+ fprintfi_filtered (level + 4, stream, "<no data fields>\n");
+ }
+ }
+ else
+ {
+ len = TYPE_NFIELDS (type);
+ for (i = TYPE_N_BASECLASSES (type); i < len; i++)
+ {
+ QUIT;
+ print_spaces_filtered (level + 4, stream);
+ chill_print_type (TYPE_FIELD_TYPE (type, i),
+ TYPE_FIELD_NAME (type, i),
+ stream, show - 1, level + 4);
+ if (i < (len - 1))
+ {
+ fputs_filtered (",", stream);
+ }
+ fputs_filtered ("\n", stream);
+ }
+ }
+ fprintfi_filtered (level, stream, ")");
+ }
+ break;
+
+ case TYPE_CODE_VOID:
+ case TYPE_CODE_UNDEF:
+ case TYPE_CODE_ERROR:
+ case TYPE_CODE_RANGE:
+ case TYPE_CODE_ENUM:
+ case TYPE_CODE_UNION:
+ case TYPE_CODE_METHOD:
+ error ("missing language support in chill_type_print_base");
+ break;
+
+ default:
+
+ /* Handle types not explicitly handled by the other cases,
+ such as fundamental types. For these, just print whatever
+ the type name is, as recorded in the type itself. If there
+ is no type name, then complain. */
+
+ if (TYPE_NAME (type) != NULL)
+ {
+ fputs_filtered (TYPE_NAME (type), stream);
+ }
+ else
+ {
+ error ("Unrecognized type code (%d) in symbol table.",
+ TYPE_CODE (type));
+ }
+ break;
+ }
+}
diff --git a/gnu/usr.bin/gdb/gdb/ch-valprint.c b/gnu/usr.bin/gdb/gdb/ch-valprint.c
new file mode 100644
index 0000000..261b22e
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/ch-valprint.c
@@ -0,0 +1,332 @@
+/* Support for printing Chill values for GDB, the GNU debugger.
+ Copyright 1986, 1988, 1989, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "obstack.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "valprint.h"
+#include "expression.h"
+#include "value.h"
+#include "language.h"
+#include "demangle.h"
+
+static void
+chill_print_value_fields PARAMS ((struct type *, char *, FILE *, int, int,
+ enum val_prettyprint, struct type **));
+
+
+/* Print data of type TYPE located at VALADDR (within GDB), which came from
+ the inferior at address ADDRESS, onto stdio stream STREAM according to
+ FORMAT (a letter or 0 for natural format). The data at VALADDR is in
+ target byte order.
+
+ If the data are a string pointer, returns the number of string characters
+ printed.
+
+ If DEREF_REF is nonzero, then dereference references, otherwise just print
+ them like pointers.
+
+ The PRETTY parameter controls prettyprinting. */
+
+int
+chill_val_print (type, valaddr, address, stream, format, deref_ref, recurse,
+ pretty)
+ struct type *type;
+ char *valaddr;
+ CORE_ADDR address;
+ FILE *stream;
+ int format;
+ int deref_ref;
+ int recurse;
+ enum val_prettyprint pretty;
+{
+ LONGEST val;
+ unsigned int i = 0; /* Number of characters printed. */
+ struct type *elttype;
+ CORE_ADDR addr;
+
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_ARRAY:
+ if (TYPE_LENGTH (type) > 0 && TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > 0)
+ {
+ if (prettyprint_arrays)
+ {
+ print_spaces_filtered (2 + 2 * recurse, stream);
+ }
+ fprintf_filtered (stream, "[");
+ val_print_array_elements (type, valaddr, address, stream, format,
+ deref_ref, recurse, pretty, 0);
+ fprintf_filtered (stream, "]");
+ }
+ else
+ {
+ error ("unimplemented in chill_val_print; unspecified array length");
+ }
+ break;
+
+ case TYPE_CODE_INT:
+ format = format ? format : output_format;
+ if (format)
+ {
+ print_scalar_formatted (valaddr, type, format, 0, stream);
+ }
+ else
+ {
+ val_print_type_code_int (type, valaddr, stream);
+ }
+ break;
+
+ case TYPE_CODE_CHAR:
+ format = format ? format : output_format;
+ if (format)
+ {
+ print_scalar_formatted (valaddr, type, format, 0, stream);
+ }
+ else
+ {
+ LA_PRINT_CHAR ((unsigned char) unpack_long (type, valaddr),
+ stream);
+ }
+ break;
+
+ case TYPE_CODE_FLT:
+ if (format)
+ {
+ print_scalar_formatted (valaddr, type, format, 0, stream);
+ }
+ else
+ {
+ print_floating (valaddr, type, stream);
+ }
+ break;
+
+ case TYPE_CODE_BOOL:
+ format = format ? format : output_format;
+ if (format)
+ {
+ print_scalar_formatted (valaddr, type, format, 0, stream);
+ }
+ else
+ {
+ val = unpack_long (builtin_type_chill_bool, valaddr);
+ fprintf_filtered (stream, val ? "TRUE" : "FALSE");
+ }
+ break;
+
+ case TYPE_CODE_UNDEF:
+ /* This happens (without TYPE_FLAG_STUB set) on systems which don't use
+ dbx xrefs (NO_DBX_XREFS in gcc) if a file has a "struct foo *bar"
+ and no complete type for struct foo in that file. */
+ fprintf_filtered (stream, "<incomplete type>");
+ break;
+
+ case TYPE_CODE_PTR:
+ if (format && format != 's')
+ {
+ print_scalar_formatted (valaddr, type, format, 0, stream);
+ break;
+ }
+ addr = unpack_pointer (type, valaddr);
+ elttype = TYPE_TARGET_TYPE (type);
+
+ if (TYPE_CODE (elttype) == TYPE_CODE_FUNC)
+ {
+ /* Try to print what function it points to. */
+ print_address_demangle (addr, stream, demangle);
+ /* Return value is irrelevant except for string pointers. */
+ return (0);
+ }
+ if (addressprint && format != 's')
+ {
+ fprintf_filtered (stream, "H'%lx", (unsigned long) addr);
+ }
+
+ /* For a pointer to char or unsigned char, also print the string
+ pointed to, unless pointer is null. */
+ if (TYPE_LENGTH (elttype) == 1
+ && TYPE_CODE (elttype) == TYPE_CODE_CHAR
+ && (format == 0 || format == 's')
+ && addr != 0
+ && /* If print_max is UINT_MAX, the alloca below will fail.
+ In that case don't try to print the string. */
+ print_max < UINT_MAX)
+ {
+ i = val_print_string (addr, 0, stream);
+ }
+ /* Return number of characters printed, plus one for the
+ terminating null if we have "reached the end". */
+ return (i + (print_max && i != print_max));
+ break;
+
+ case TYPE_CODE_STRING:
+ if (format && format != 's')
+ {
+ print_scalar_formatted (valaddr, type, format, 0, stream);
+ break;
+ }
+ if (addressprint && format != 's')
+ {
+ /* This used to say `addr', which is unset at this point.
+ Is `address' what is meant? */
+ fprintf_filtered (stream, "H'%lx ", (unsigned long) address);
+ }
+ i = TYPE_LENGTH (type);
+ LA_PRINT_STRING (stream, valaddr, i, 0);
+ /* Return number of characters printed, plus one for the terminating
+ null if we have "reached the end". */
+ return (i + (print_max && i != print_max));
+ break;
+
+ case TYPE_CODE_STRUCT:
+ chill_print_value_fields (type, valaddr, stream, format, recurse, pretty,
+ 0);
+ break;
+
+ case TYPE_CODE_REF:
+ if (addressprint)
+ {
+ fprintf_filtered (stream, "LOC(H'%lx)",
+ unpack_long (builtin_type_int, valaddr));
+ if (deref_ref)
+ fputs_filtered (": ", stream);
+ }
+ /* De-reference the reference. */
+ if (deref_ref)
+ {
+ if (TYPE_CODE (TYPE_TARGET_TYPE (type)) != TYPE_CODE_UNDEF)
+ {
+ value deref_val =
+ value_at
+ (TYPE_TARGET_TYPE (type),
+ unpack_pointer (lookup_pointer_type (builtin_type_void),
+ valaddr));
+ val_print (VALUE_TYPE (deref_val),
+ VALUE_CONTENTS (deref_val),
+ VALUE_ADDRESS (deref_val), stream, format,
+ deref_ref, recurse + 1, pretty);
+ }
+ else
+ fputs_filtered ("???", stream);
+ }
+ break;
+
+ case TYPE_CODE_ENUM:
+ c_val_print (type, valaddr, address, stream, format,
+ deref_ref, recurse, pretty);
+ break;
+
+ case TYPE_CODE_MEMBER:
+ case TYPE_CODE_UNION:
+ case TYPE_CODE_FUNC:
+ case TYPE_CODE_VOID:
+ case TYPE_CODE_ERROR:
+ case TYPE_CODE_RANGE:
+ default:
+ /* Let's derfer printing to the C printer, rather than
+ print an error message. FIXME! */
+ c_val_print (type, valaddr, address, stream, format,
+ deref_ref, recurse, pretty);
+ }
+ fflush (stream);
+ return (0);
+}
+
+/* Mutually recursive subroutines of cplus_print_value and c_val_print to
+ print out a structure's fields: cp_print_value_fields and cplus_print_value.
+
+ TYPE, VALADDR, STREAM, RECURSE, and PRETTY have the
+ same meanings as in cplus_print_value and c_val_print.
+
+ DONT_PRINT is an array of baseclass types that we
+ should not print, or zero if called from top level. */
+
+static void
+chill_print_value_fields (type, valaddr, stream, format, recurse, pretty,
+ dont_print)
+ struct type *type;
+ char *valaddr;
+ FILE *stream;
+ int format;
+ int recurse;
+ enum val_prettyprint pretty;
+ struct type **dont_print;
+{
+ int i, len;
+ int fields_seen = 0;
+
+ check_stub_type (type);
+
+ fprintf_filtered (stream, "[");
+ len = TYPE_NFIELDS (type);
+ if (len == 0)
+ {
+ fprintf_filtered (stream, "<No data fields>");
+ }
+ else
+ {
+ for (i = 0; i < len; i++)
+ {
+ if (fields_seen)
+ {
+ fprintf_filtered (stream, ", ");
+ }
+ fields_seen = 1;
+ if (pretty)
+ {
+ fprintf_filtered (stream, "\n");
+ print_spaces_filtered (2 + 2 * recurse, stream);
+ }
+ else
+ {
+ wrap_here (n_spaces (2 + 2 * recurse));
+ }
+ fputs_filtered (".", stream);
+ fprintf_symbol_filtered (stream, TYPE_FIELD_NAME (type, i),
+ language_chill, DMGL_NO_OPTS);
+ fputs_filtered (": ", stream);
+ if (TYPE_FIELD_PACKED (type, i))
+ {
+ value v;
+
+ /* Bitfields require special handling, especially due to byte
+ order problems. */
+ v = value_from_longest (TYPE_FIELD_TYPE (type, i),
+ unpack_field_as_long (type, valaddr, i));
+
+ chill_val_print (TYPE_FIELD_TYPE (type, i), VALUE_CONTENTS (v), 0,
+ stream, format, 0, recurse + 1, pretty);
+ }
+ else
+ {
+ chill_val_print (TYPE_FIELD_TYPE (type, i),
+ valaddr + TYPE_FIELD_BITPOS (type, i) / 8,
+ 0, stream, format, 0, recurse + 1, pretty);
+ }
+ }
+ if (pretty)
+ {
+ fprintf_filtered (stream, "\n");
+ print_spaces_filtered (2 * recurse, stream);
+ }
+ }
+ fprintf_filtered (stream, "]");
+}
+
diff --git a/gnu/usr.bin/gdb/gdb/coff/ecoff.h b/gnu/usr.bin/gdb/gdb/coff/ecoff.h
new file mode 100644
index 0000000..8c7cee2
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/coff/ecoff.h
@@ -0,0 +1,262 @@
+#ifndef ECOFF_H
+#define ECOFF_H
+
+/* Generic ECOFF support.
+ This does not include symbol information, found in sym.h and
+ symconst.h. */
+
+/* Mips magic numbers used in filehdr. MIPS_MAGIC_LITTLE is used on
+ little endian machines. MIPS_MAGIC_BIG is used on big endian
+ machines. Where is MIPS_MAGIC_1 from? */
+#define MIPS_MAGIC_1 0x0180
+#define MIPS_MAGIC_LITTLE 0x0162
+#define MIPS_MAGIC_BIG 0x0160
+
+/* These are the magic numbers used for MIPS code compiled at ISA
+ level 2. */
+#define MIPS_MAGIC_LITTLE2 0x0166
+#define MIPS_MAGIC_BIG2 0x0163
+
+/* These are the magic numbers used for MIPS code compiled at ISA
+ level 3. */
+#define MIPS_MAGIC_LITTLE3 0x142
+#define MIPS_MAGIC_BIG3 0x140
+
+/* Alpha magic numbers used in filehdr. */
+#define ALPHA_MAGIC 0x183
+
+/* Magic numbers used in a.out header. */
+#define ECOFF_AOUT_OMAGIC 0407 /* not demand paged (ld -N). */
+#define ECOFF_AOUT_ZMAGIC 0413 /* demand load format, eg normal ld output */
+
+/* Names of special sections. */
+#define _TEXT ".text"
+#define _DATA ".data"
+#define _BSS ".bss"
+#define _RDATA ".rdata"
+#define _SDATA ".sdata"
+#define _SBSS ".sbss"
+#define _LIT4 ".lit4"
+#define _LIT8 ".lit8"
+#define _LIB ".lib"
+#define _INIT ".init"
+#define _FINI ".fini"
+
+/* ECOFF uses some additional section flags. */
+#define STYP_RDATA 0x100
+#define STYP_SDATA 0x200
+#define STYP_SBSS 0x400
+#define STYP_ECOFF_FINI 0x1000000
+#define STYP_LIT8 0x8000000
+#define STYP_LIT4 0x10000000
+#define STYP_ECOFF_INIT 0x80000000
+#define STYP_OTHER_LOAD (STYP_ECOFF_INIT | STYP_ECOFF_FINI)
+
+/* The linker needs a section to hold small common variables while
+ linking. There is no convenient way to create it when the linker
+ needs it, so we always create one for each BFD. We then avoid
+ writing it out. */
+#define SCOMMON ".scommon"
+
+/* The ECOFF a.out header carries information about register masks and
+ the gp value. The assembler needs to be able to write out this
+ information, and objcopy needs to be able to copy it from one file
+ to another. To handle this in BFD, we use a dummy section to hold
+ the information. We call this section .reginfo, since MIPS ELF has
+ a .reginfo section which serves a similar purpose. When BFD
+ recognizes an ECOFF object, it copies the information into a
+ private data structure. When the .reginfo section is read, the
+ information is retrieved from the private data structure. When the
+ .reginfo section is written, the information in the private data
+ structure is updated. The contents of the .reginfo section, as
+ seen by programs outside BFD, is a ecoff_reginfo structure. The
+ contents of the structure are as seen on the host, so no swapping
+ issues arise.
+
+ The assembler used to update the private BFD data structures
+ directly. With this approach, it instead just creates a .reginfo
+ section and updates that. The real advantage of this approach is
+ that objcopy works automatically. */
+#define REGINFO ".reginfo"
+struct ecoff_reginfo
+{
+ bfd_vma gp_value; /* GP register value. */
+ unsigned long gprmask; /* General registers used. */
+ unsigned long cprmask[4]; /* Coprocessor registers used. */
+ unsigned long fprmask; /* Floating pointer registers used. */
+};
+
+/* If the extern bit in a reloc is 1, then r_symndx is an index into
+ the external symbol table. If the extern bit is 0, then r_symndx
+ indicates a section, and is one of the following values. */
+#define RELOC_SECTION_NONE 0
+#define RELOC_SECTION_TEXT 1
+#define RELOC_SECTION_RDATA 2
+#define RELOC_SECTION_DATA 3
+#define RELOC_SECTION_SDATA 4
+#define RELOC_SECTION_SBSS 5
+#define RELOC_SECTION_BSS 6
+#define RELOC_SECTION_INIT 7
+#define RELOC_SECTION_LIT8 8
+#define RELOC_SECTION_LIT4 9
+#define RELOC_SECTION_XDATA 10
+#define RELOC_SECTION_PDATA 11
+#define RELOC_SECTION_FINI 12
+#define RELOC_SECTION_LITA 13
+#define RELOC_SECTION_ABS 14
+
+/********************** STABS **********************/
+
+/* gcc uses mips-tfile to output type information in special stabs
+ entries. These must match the corresponding definition in
+ gcc/config/mips.h. At some point, these should probably go into a
+ shared include file, but currently gcc and gdb do not share any
+ directories. */
+#define CODE_MASK 0x8F300
+#define ECOFF_IS_STAB(sym) (((sym)->index & 0xFFF00) == CODE_MASK)
+#define ECOFF_MARK_STAB(code) ((code)+CODE_MASK)
+#define ECOFF_UNMARK_STAB(code) ((code)-CODE_MASK)
+#define STABS_SYMBOL "@stabs"
+
+/********************** COFF **********************/
+
+/* gcc also uses mips-tfile to output COFF debugging information.
+ These are the values it uses when outputting the .type directive.
+ These should also be in a shared include file. */
+#define N_BTMASK (017)
+#define N_TMASK (060)
+#define N_BTSHFT (4)
+#define N_TSHIFT (2)
+
+/********************** AUX **********************/
+
+/* The auxiliary type information is the same on all known ECOFF
+ targets. I can't see any reason that it would ever change, so I am
+ going to gamble and define the external structures here, in the
+ target independent ECOFF header file. The internal forms are
+ defined in coff/sym.h, which was originally donated by MIPS
+ Computer Systems. */
+
+/* Type information external record */
+
+struct tir_ext {
+ unsigned char t_bits1[1];
+ unsigned char t_tq45[1];
+ unsigned char t_tq01[1];
+ unsigned char t_tq23[1];
+};
+
+#define TIR_BITS1_FBITFIELD_BIG 0x80
+#define TIR_BITS1_FBITFIELD_LITTLE 0x01
+
+#define TIR_BITS1_CONTINUED_BIG 0x40
+#define TIR_BITS1_CONTINUED_LITTLE 0x02
+
+#define TIR_BITS1_BT_BIG 0x3F
+#define TIR_BITS1_BT_SH_BIG 0
+#define TIR_BITS1_BT_LITTLE 0xFC
+#define TIR_BITS1_BT_SH_LITTLE 2
+
+#define TIR_BITS_TQ4_BIG 0xF0
+#define TIR_BITS_TQ4_SH_BIG 4
+#define TIR_BITS_TQ5_BIG 0x0F
+#define TIR_BITS_TQ5_SH_BIG 0
+#define TIR_BITS_TQ4_LITTLE 0x0F
+#define TIR_BITS_TQ4_SH_LITTLE 0
+#define TIR_BITS_TQ5_LITTLE 0xF0
+#define TIR_BITS_TQ5_SH_LITTLE 4
+
+#define TIR_BITS_TQ0_BIG 0xF0
+#define TIR_BITS_TQ0_SH_BIG 4
+#define TIR_BITS_TQ1_BIG 0x0F
+#define TIR_BITS_TQ1_SH_BIG 0
+#define TIR_BITS_TQ0_LITTLE 0x0F
+#define TIR_BITS_TQ0_SH_LITTLE 0
+#define TIR_BITS_TQ1_LITTLE 0xF0
+#define TIR_BITS_TQ1_SH_LITTLE 4
+
+#define TIR_BITS_TQ2_BIG 0xF0
+#define TIR_BITS_TQ2_SH_BIG 4
+#define TIR_BITS_TQ3_BIG 0x0F
+#define TIR_BITS_TQ3_SH_BIG 0
+#define TIR_BITS_TQ2_LITTLE 0x0F
+#define TIR_BITS_TQ2_SH_LITTLE 0
+#define TIR_BITS_TQ3_LITTLE 0xF0
+#define TIR_BITS_TQ3_SH_LITTLE 4
+
+/* Relative symbol external record */
+
+struct rndx_ext {
+ unsigned char r_bits[4];
+};
+
+#define RNDX_BITS0_RFD_SH_LEFT_BIG 4
+#define RNDX_BITS1_RFD_BIG 0xF0
+#define RNDX_BITS1_RFD_SH_BIG 4
+
+#define RNDX_BITS0_RFD_SH_LEFT_LITTLE 0
+#define RNDX_BITS1_RFD_LITTLE 0x0F
+#define RNDX_BITS1_RFD_SH_LEFT_LITTLE 8
+
+#define RNDX_BITS1_INDEX_BIG 0x0F
+#define RNDX_BITS1_INDEX_SH_LEFT_BIG 16
+#define RNDX_BITS2_INDEX_SH_LEFT_BIG 8
+#define RNDX_BITS3_INDEX_SH_LEFT_BIG 0
+
+#define RNDX_BITS1_INDEX_LITTLE 0xF0
+#define RNDX_BITS1_INDEX_SH_LITTLE 4
+#define RNDX_BITS2_INDEX_SH_LEFT_LITTLE 4
+#define RNDX_BITS3_INDEX_SH_LEFT_LITTLE 12
+
+/* Auxiliary symbol information external record */
+
+union aux_ext {
+ struct tir_ext a_ti;
+ struct rndx_ext a_rndx;
+ unsigned char a_dnLow[4];
+ unsigned char a_dnHigh[4];
+ unsigned char a_isym[4];
+ unsigned char a_iss[4];
+ unsigned char a_width[4];
+ unsigned char a_count[4];
+};
+
+#define AUX_GET_ANY(bigend, ax, field) \
+ ((bigend) ? bfd_getb32 ((ax)->field) : bfd_getl32 ((ax)->field))
+
+#define AUX_GET_DNLOW(bigend, ax) AUX_GET_ANY ((bigend), (ax), a_dnLow)
+#define AUX_GET_DNHIGH(bigend, ax) AUX_GET_ANY ((bigend), (ax), a_dnHigh)
+#define AUX_GET_ISYM(bigend, ax) AUX_GET_ANY ((bigend), (ax), a_isym)
+#define AUX_GET_ISS(bigend, ax) AUX_GET_ANY ((bigend), (ax), a_iss)
+#define AUX_GET_WIDTH(bigend, ax) AUX_GET_ANY ((bigend), (ax), a_width)
+#define AUX_GET_COUNT(bigend, ax) AUX_GET_ANY ((bigend), (ax), a_count)
+
+#define AUX_PUT_ANY(bigend, val, ax, field) \
+ ((bigend) \
+ ? (bfd_putb32 ((bfd_vma) (val), (ax)->field), 0) \
+ : (bfd_putl32 ((bfd_vma) (val), (ax)->field), 0))
+
+#define AUX_PUT_DNLOW(bigend, val, ax) \
+ AUX_PUT_ANY ((bigend), (val), (ax), a_dnLow)
+#define AUX_PUT_DNHIGH(bigend, val, ax) \
+ AUX_PUT_ANY ((bigend), (val), (ax), a_dnHigh)
+#define AUX_PUT_ISYM(bigend, val, ax) \
+ AUX_PUT_ANY ((bigend), (val), (ax), a_isym)
+#define AUX_PUT_ISS(bigend, val, ax) \
+ AUX_PUT_ANY ((bigend), (val), (ax), a_iss)
+#define AUX_PUT_WIDTH(bigend, val, ax) \
+ AUX_PUT_ANY ((bigend), (val), (ax), a_width)
+#define AUX_PUT_COUNT(bigend, val, ax) \
+ AUX_PUT_ANY ((bigend), (val), (ax), a_count)
+
+/* Prototypes for the swapping functions. These require that sym.h be
+ included before this file. */
+
+extern void ecoff_swap_tir_in PARAMS ((int bigend, struct tir_ext *, TIR *));
+extern void ecoff_swap_tir_out PARAMS ((int bigend, TIR *, struct tir_ext *));
+extern void ecoff_swap_rndx_in PARAMS ((int bigend, struct rndx_ext *,
+ RNDXR *));
+extern void ecoff_swap_rndx_out PARAMS ((int bigend, RNDXR *,
+ struct rndx_ext *));
+
+#endif /* ! defined (ECOFF_H) */
diff --git a/gnu/usr.bin/gdb/gdb/coff/internal.h b/gnu/usr.bin/gdb/gdb/coff/internal.h
new file mode 100644
index 0000000..e8cf984
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/coff/internal.h
@@ -0,0 +1,538 @@
+/* Internal format of COFF object file data structures, for GNU BFD.
+ This file is part of BFD, the Binary File Descriptor library. */
+
+/* First, make "signed char" work, even on old compilers. */
+#ifndef signed
+#ifndef __STDC__
+#define signed /**/
+#endif
+#endif
+
+/********************** FILE HEADER **********************/
+struct internal_filehdr
+{
+ unsigned short f_magic; /* magic number */
+ unsigned short f_nscns; /* number of sections */
+ long f_timdat; /* time & date stamp */
+ bfd_vma f_symptr; /* file pointer to symtab */
+ long f_nsyms; /* number of symtab entries */
+ unsigned short f_opthdr; /* sizeof(optional hdr) */
+ unsigned short f_flags; /* flags */
+};
+
+/* Bits for f_flags:
+ * F_RELFLG relocation info stripped from file
+ * F_EXEC file is executable (no unresolved external references)
+ * F_LNNO line numbers stripped from file
+ * F_LSYMS local symbols stripped from file
+ * F_AR16WR file is 16-bit little-endian
+ * F_AR32WR file is 32-bit little-endian
+ * F_AR32W file is 32-bit big-endian
+ * F_DYNLOAD rs/6000 aix: dynamically loadable w/imports & exports
+ * F_SHROBJ rs/6000 aix: file is a shared object
+ */
+
+#define F_RELFLG (0x0001)
+#define F_EXEC (0x0002)
+#define F_LNNO (0x0004)
+#define F_LSYMS (0x0008)
+#define F_AR16WR (0x0080)
+#define F_AR32WR (0x0100)
+#define F_AR32W (0x0200)
+#define F_DYNLOAD (0x1000)
+#define F_SHROBJ (0x2000)
+
+/********************** AOUT "OPTIONAL HEADER" **********************/
+struct internal_aouthdr
+{
+ short magic; /* type of file */
+ short vstamp; /* version stamp */
+ bfd_vma tsize; /* text size in bytes, padded to FW bdry*/
+ bfd_vma dsize; /* initialized data " " */
+ bfd_vma bsize; /* uninitialized data " " */
+ bfd_vma entry; /* entry pt. */
+ bfd_vma text_start; /* base of text used for this file */
+ bfd_vma data_start; /* base of data used for this file */
+
+ /* i960 stuff */
+ unsigned long tagentries; /* number of tag entries to follow */
+
+ /* RS/6000 stuff */
+ unsigned long o_toc; /* address of TOC */
+ short o_snentry; /* section number for entry point */
+ short o_sntext; /* section number for text */
+ short o_sndata; /* section number for data */
+ short o_sntoc; /* section number for toc */
+ short o_snloader; /* section number for loader section */
+ short o_snbss; /* section number for bss */
+ short o_algntext; /* max alignment for text */
+ short o_algndata; /* max alignment for data */
+ short o_modtype; /* Module type field, 1R,RE,RO */
+ unsigned long o_maxstack; /* max stack size allowed. */
+
+ /* ECOFF stuff */
+ bfd_vma bss_start; /* Base of bss section. */
+ bfd_vma gp_value; /* GP register value. */
+ unsigned long gprmask; /* General registers used. */
+ unsigned long cprmask[4]; /* Coprocessor registers used. */
+ unsigned long fprmask; /* Floating pointer registers used. */
+
+ /* Apollo stuff */
+ long o_inlib;
+ long o_sri;
+ long vid[2];
+};
+
+/********************** STORAGE CLASSES **********************/
+
+/* This used to be defined as -1, but now n_sclass is unsigned. */
+#define C_EFCN 0xff /* physical end of function */
+#define C_NULL 0
+#define C_AUTO 1 /* automatic variable */
+#define C_EXT 2 /* external symbol */
+#define C_STAT 3 /* static */
+#define C_REG 4 /* register variable */
+#define C_EXTDEF 5 /* external definition */
+#define C_LABEL 6 /* label */
+#define C_ULABEL 7 /* undefined label */
+#define C_MOS 8 /* member of structure */
+#define C_ARG 9 /* function argument */
+#define C_STRTAG 10 /* structure tag */
+#define C_MOU 11 /* member of union */
+#define C_UNTAG 12 /* union tag */
+#define C_TPDEF 13 /* type definition */
+#define C_USTATIC 14 /* undefined static */
+#define C_ENTAG 15 /* enumeration tag */
+#define C_MOE 16 /* member of enumeration */
+#define C_REGPARM 17 /* register parameter */
+#define C_FIELD 18 /* bit field */
+#define C_AUTOARG 19 /* auto argument */
+#define C_LASTENT 20 /* dummy entry (end of block) */
+#define C_BLOCK 100 /* ".bb" or ".eb" */
+#define C_FCN 101 /* ".bf" or ".ef" */
+#define C_EOS 102 /* end of structure */
+#define C_FILE 103 /* file name */
+#define C_LINE 104 /* line # reformatted as symbol table entry */
+#define C_ALIAS 105 /* duplicate tag */
+#define C_HIDDEN 106 /* ext symbol in dmert public lib */
+
+ /* New storage classes for 80960 */
+
+/* C_LEAFPROC is obsolete. Use C_LEAFEXT or C_LEAFSTAT */
+#define C_LEAFPROC 108 /* Leaf procedure, "call" via BAL */
+
+#define C_SCALL 107 /* Procedure reachable via system call */
+#define C_LEAFEXT 108 /* External leaf */
+#define C_LEAFSTAT 113 /* Static leaf */
+#define C_OPTVAR 109 /* Optimized variable */
+#define C_DEFINE 110 /* Preprocessor #define */
+#define C_PRAGMA 111 /* Advice to compiler or linker */
+#define C_SEGMENT 112 /* 80960 segment name */
+
+ /* Storage classes for m88k */
+#define C_SHADOW 107 /* shadow symbol */
+#define C_VERSION 108 /* coff version symbol */
+
+ /* New storage classes for RS/6000 */
+#define C_HIDEXT 107 /* Un-named external symbol */
+#define C_BINCL 108 /* Marks beginning of include file */
+#define C_EINCL 109 /* Marks ending of include file */
+
+ /* storage classes for stab symbols for RS/6000 */
+#define C_GSYM (0x80)
+#define C_LSYM (0x81)
+#define C_PSYM (0x82)
+#define C_RSYM (0x83)
+#define C_RPSYM (0x84)
+#define C_STSYM (0x85)
+#define C_TCSYM (0x86)
+#define C_BCOMM (0x87)
+#define C_ECOML (0x88)
+#define C_ECOMM (0x89)
+#define C_DECL (0x8c)
+#define C_ENTRY (0x8d)
+#define C_FUN (0x8e)
+#define C_BSTAT (0x8f)
+#define C_ESTAT (0x90)
+
+/********************** SECTION HEADER **********************/
+struct internal_scnhdr
+{
+ char s_name[8]; /* section name */
+ bfd_vma s_paddr; /* physical address, aliased s_nlib */
+ bfd_vma s_vaddr; /* virtual address */
+ bfd_vma s_size; /* section size */
+ bfd_vma s_scnptr; /* file ptr to raw data for section */
+ bfd_vma s_relptr; /* file ptr to relocation */
+ bfd_vma s_lnnoptr; /* file ptr to line numbers */
+ unsigned long s_nreloc; /* number of relocation entries */
+ unsigned long s_nlnno; /* number of line number entries*/
+ long s_flags; /* flags */
+ long s_align; /* used on I960 */
+};
+
+/*
+ * s_flags "type"
+ */
+#define STYP_REG (0x0000) /* "regular": allocated, relocated, loaded */
+#define STYP_DSECT (0x0001) /* "dummy": relocated only*/
+#define STYP_NOLOAD (0x0002) /* "noload": allocated, relocated, not loaded */
+#define STYP_GROUP (0x0004) /* "grouped": formed of input sections */
+#define STYP_PAD (0x0008) /* "padding": not allocated, not relocated, loaded */
+#define STYP_COPY (0x0010) /* "copy": for decision function used by field update; not allocated, not relocated,
+ loaded; reloc & lineno entries processed normally */
+#define STYP_TEXT (0x0020) /* section contains text only */
+#define S_SHRSEG (0x0020) /* In 3b Update files (output of ogen), sections which appear in SHARED segments of the Pfile
+ will have the S_SHRSEG flag set by ogen, to inform dufr that updating 1 copy of the proc. will
+ update all process invocations. */
+#define STYP_DATA (0x0040) /* section contains data only */
+#define STYP_BSS (0x0080) /* section contains bss only */
+#define S_NEWFCN (0x0100) /* In a minimal file or an update file, a new function (as compared with a replaced function) */
+#define STYP_INFO (0x0200) /* comment: not allocated not relocated, not loaded */
+#define STYP_OVER (0x0400) /* overlay: relocated not allocated or loaded */
+#define STYP_LIB (0x0800) /* for .lib: same as INFO */
+#define STYP_MERGE (0x2000) /* merge section -- combines with text, data or bss sections only */
+#define STYP_REVERSE_PAD (0x4000) /* section will be padded with no-op instructions wherever padding is necessary and there is a
+
+ word of contiguous bytes
+ beginning on a word boundary. */
+
+#define STYP_LIT 0x8020 /* Literal data (like STYP_TEXT) */
+/********************** LINE NUMBERS **********************/
+
+/* 1 line number entry for every "breakpointable" source line in a section.
+ * Line numbers are grouped on a per function basis; first entry in a function
+ * grouping will have l_lnno = 0 and in place of physical address will be the
+ * symbol table index of the function name.
+ */
+
+struct internal_lineno
+{
+ union
+ {
+ long l_symndx; /* function name symbol index, iff l_lnno == 0*/
+ long l_paddr; /* (physical) address of line number */
+ } l_addr;
+ unsigned long l_lnno; /* line number */
+};
+
+/********************** SYMBOLS **********************/
+
+#define SYMNMLEN 8 /* # characters in a symbol name */
+#define FILNMLEN 14 /* # characters in a file name */
+#define DIMNUM 4 /* # array dimensions in auxiliary entry */
+
+struct internal_syment
+{
+ union
+ {
+ char _n_name[SYMNMLEN]; /* old COFF version */
+ struct
+ {
+ long _n_zeroes; /* new == 0 */
+ long _n_offset; /* offset into string table */
+ } _n_n;
+ char *_n_nptr[2]; /* allows for overlaying */
+ } _n;
+ long n_value; /* value of symbol */
+ short n_scnum; /* section number */
+ unsigned short n_flags; /* copy of flags from filhdr */
+ unsigned short n_type; /* type and derived type */
+ unsigned char n_sclass; /* storage class */
+ char n_numaux; /* number of aux. entries */
+};
+
+#define n_name _n._n_name
+#define n_zeroes _n._n_n._n_zeroes
+#define n_offset _n._n_n._n_offset
+
+
+/* Relocatable symbols have number of the section in which they are defined,
+ or one of the following: */
+
+#define N_UNDEF ((short)0) /* undefined symbol */
+#define N_ABS ((short)-1) /* value of symbol is absolute */
+#define N_DEBUG ((short)-2) /* debugging symbol -- value is meaningless */
+#define N_TV ((short)-3) /* indicates symbol needs preload transfer vector */
+#define P_TV ((short)-4) /* indicates symbol needs postload transfer vector*/
+
+/*
+ * Type of a symbol, in low N bits of the word
+ */
+#define T_NULL 0
+#define T_VOID 1 /* function argument (only used by compiler) */
+#define T_CHAR 2 /* character */
+#define T_SHORT 3 /* short integer */
+#define T_INT 4 /* integer */
+#define T_LONG 5 /* long integer */
+#define T_FLOAT 6 /* floating point */
+#define T_DOUBLE 7 /* double word */
+#define T_STRUCT 8 /* structure */
+#define T_UNION 9 /* union */
+#define T_ENUM 10 /* enumeration */
+#define T_MOE 11 /* member of enumeration*/
+#define T_UCHAR 12 /* unsigned character */
+#define T_USHORT 13 /* unsigned short */
+#define T_UINT 14 /* unsigned integer */
+#define T_ULONG 15 /* unsigned long */
+#define T_LNGDBL 16 /* long double */
+
+/*
+ * derived types, in n_type
+*/
+#define DT_NON (0) /* no derived type */
+#define DT_PTR (1) /* pointer */
+#define DT_FCN (2) /* function */
+#define DT_ARY (3) /* array */
+
+#define BTYPE(x) ((x) & N_BTMASK)
+
+#define ISPTR(x) (((x) & N_TMASK) == (DT_PTR << N_BTSHFT))
+#define ISFCN(x) (((x) & N_TMASK) == (DT_FCN << N_BTSHFT))
+#define ISARY(x) (((x) & N_TMASK) == (DT_ARY << N_BTSHFT))
+#define ISTAG(x) ((x)==C_STRTAG||(x)==C_UNTAG||(x)==C_ENTAG)
+#define DECREF(x) ((((x)>>N_TSHIFT)&~N_BTMASK)|((x)&N_BTMASK))
+
+
+union internal_auxent
+{
+ struct
+ {
+
+ union
+ {
+ long l; /* str, un, or enum tag indx */
+ struct coff_ptr_struct *p;
+ } x_tagndx;
+
+ union
+ {
+ struct
+ {
+ unsigned short x_lnno; /* declaration line number */
+ unsigned short x_size; /* str/union/array size */
+ } x_lnsz;
+ long x_fsize; /* size of function */
+ } x_misc;
+
+ union
+ {
+ struct
+ { /* if ISFCN, tag, or .bb */
+ long x_lnnoptr; /* ptr to fcn line # */
+ union
+ { /* entry ndx past block end */
+ long l;
+ struct coff_ptr_struct *p;
+ } x_endndx;
+ } x_fcn;
+
+ struct
+ { /* if ISARY, up to 4 dimen. */
+ unsigned short x_dimen[DIMNUM];
+ } x_ary;
+ } x_fcnary;
+
+ unsigned short x_tvndx; /* tv index */
+ } x_sym;
+
+ union
+ {
+ char x_fname[FILNMLEN];
+ struct
+ {
+ long x_zeroes;
+ long x_offset;
+ } x_n;
+ } x_file;
+
+ struct
+ {
+ long x_scnlen; /* section length */
+ unsigned short x_nreloc; /* # relocation entries */
+ unsigned short x_nlinno; /* # line numbers */
+ } x_scn;
+
+ struct
+ {
+ long x_tvfill; /* tv fill value */
+ unsigned short x_tvlen; /* length of .tv */
+ unsigned short x_tvran[2]; /* tv range */
+ } x_tv; /* info about .tv section (in auxent of symbol .tv)) */
+
+ /******************************************
+ * RS/6000-specific auxent - last auxent for every external symbol
+ ******************************************/
+ struct
+ {
+ long x_scnlen; /* csect length */
+ long x_parmhash; /* parm type hash index */
+ unsigned short x_snhash; /* sect num with parm hash */
+ unsigned char x_smtyp; /* symbol align and type */
+ /* 0-4 - Log 2 of alignment */
+ /* 5-7 - symbol type */
+ unsigned char x_smclas; /* storage mapping class */
+ long x_stab; /* dbx stab info index */
+ unsigned short x_snstab; /* sect num with dbx stab */
+ } x_csect; /* csect definition information */
+
+/* x_smtyp values: */
+
+#define SMTYP_ALIGN(x) ((x) >> 3) /* log2 of alignment */
+#define SMTYP_SMTYP(x) ((x) & 0x7) /* symbol type */
+/* Symbol type values: */
+#define XTY_ER 0 /* External reference */
+#define XTY_SD 1 /* Csect definition */
+#define XTY_LD 2 /* Label definition */
+#define XTY_CM 3 /* .BSS */
+#define XTY_EM 4 /* Error message */
+#define XTY_US 5 /* "Reserved for internal use" */
+
+/* x_smclas values: */
+
+#define XMC_PR 0 /* Read-only program code */
+#define XMC_RO 1 /* Read-only constant */
+#define XMC_DB 2 /* Read-only debug dictionary table */
+#define XMC_TC 3 /* Read-write general TOC entry */
+#define XMC_UA 4 /* Read-write unclassified */
+#define XMC_RW 5 /* Read-write data */
+#define XMC_GL 6 /* Read-only global linkage */
+#define XMC_XO 7 /* Read-only extended operation (simulated insn) */
+#define XMC_SV 8 /* Read-only supervisor call */
+#define XMC_BS 9 /* Read-write BSS */
+#define XMC_DS 10 /* Read-write descriptor csect */
+#define XMC_UC 11 /* Read-write unnamed Fortran common */
+#define XMC_TI 12 /* Read-only traceback index csect */
+#define XMC_TB 13 /* Read-only traceback table csect */
+/* 14 ??? */
+#define XMC_TC0 15 /* Read-write TOC anchor for TOC addressability */
+
+
+ /******************************************
+ * I960-specific *2nd* aux. entry formats
+ ******************************************/
+ struct
+ {
+ /* This is a very old typo that keeps getting propagated. */
+#define x_stdindx x_stindx
+ long x_stindx; /* sys. table entry */
+ } x_sc; /* system call entry */
+
+ struct
+ {
+ unsigned long x_balntry; /* BAL entry point */
+ } x_bal; /* BAL-callable function */
+
+ struct
+ {
+ unsigned long x_timestamp; /* time stamp */
+ char x_idstring[20]; /* producer identity string */
+ } x_ident; /* Producer ident info */
+
+};
+
+/********************** RELOCATION DIRECTIVES **********************/
+
+struct internal_reloc
+{
+ bfd_vma r_vaddr; /* Virtual address of reference */
+ long r_symndx; /* Index into symbol table */
+ unsigned short r_type; /* Relocation type */
+ unsigned char r_size; /* Used by RS/6000 and ECOFF */
+ unsigned char r_extern; /* Used by ECOFF */
+ unsigned long r_offset; /* Used by RS/6000 and ECOFF */
+};
+
+#define R_RELBYTE 017
+#define R_RELWORD 020
+#define R_PCRBYTE 022
+#define R_PCRWORD 023
+#define R_PCRLONG 024
+
+#define R_DIR16 01
+#define R_DIR32 06
+#define R_PCLONG 020
+#define R_RELBYTE 017
+#define R_RELWORD 020
+
+
+
+#define R_PCR16L 128
+#define R_PCR26L 129
+#define R_VRT16 130
+#define R_HVRT16 131
+#define R_LVRT16 132
+#define R_VRT32 133
+#define R_RELLONG (0x11) /* Direct 32-bit relocation */
+#define R_IPRSHORT (0x18)
+#define R_IPRMED (0x19) /* 24-bit ip-relative relocation */
+#define R_IPRLONG (0x1a)
+#define R_OPTCALL (0x1b) /* 32-bit optimizable call (leafproc/sysproc) */
+#define R_OPTCALLX (0x1c) /* 64-bit optimizable call (leafproc/sysproc) */
+#define R_GETSEG (0x1d)
+#define R_GETPA (0x1e)
+#define R_TAGWORD (0x1f)
+#define R_JUMPTARG 0x20 /* strange 29k 00xx00xx reloc */
+
+
+#define R_MOVB1 0x41 /* Special h8 16bit or 8 bit reloc for mov.b */
+#define R_MOVB2 0x42 /* Special h8 opcode for 8bit which could be 16 */
+#define R_JMP1 0x43 /* Special h8 16bit jmp which could be pcrel */
+#define R_JMP2 0x44 /* a branch which used to be a jmp */
+#define R_RELLONG_NEG 0x45
+
+#define R_JMPL1 0x46 /* Special h8 24bit jmp which could be pcrel */
+#define R_JMPL_B8 0x47 /* a 8 bit pcrel which used to be a jmp */
+
+#define R_MOVLB1 0x48 /* Special h8 24bit or 8 bit reloc for mov.b */
+#define R_MOVLB2 0x49 /* Special h8 opcode for 8bit which could be 24 */
+
+/* Z8k modes */
+#define R_IMM16 0x01 /* 16 bit abs */
+#define R_JR 0x02 /* jr 8 bit disp */
+#define R_IMM4L 0x23 /* low nibble */
+#define R_IMM8 0x22 /* 8 bit abs */
+#define R_IMM32 R_RELLONG /* 32 bit abs */
+#define R_CALL R_DA /* Absolute address which could be a callr */
+#define R_JP R_DA /* Absolute address which could be a jp */
+#define R_REL16 0x04 /* 16 bit PC rel */
+#define R_CALLR 0x05 /* callr 12 bit disp */
+#define R_SEG 0x10 /* set if in segmented mode */
+#define R_IMM4H 0x24 /* high nibble */
+
+
+/* H8500 modes */
+
+#define R_H8500_IMM8 1 /* 8 bit immediate */
+#define R_H8500_IMM16 2 /* 16 bit immediate */
+#define R_H8500_PCREL8 3 /* 8 bit pcrel */
+#define R_H8500_PCREL16 4 /* 16 bit pcrel */
+#define R_H8500_HIGH8 5 /* high 8 bits of 24 bit address */
+#define R_H8500_LOW16 7 /* low 16 bits of 24 bit immediate */
+#define R_H8500_IMM24 6 /* 24 bit immediate */
+#define R_H8500_IMM32 8 /* 32 bit immediate */
+#define R_H8500_HIGH16 9 /* high 16 bits of 32 bit immediate */
+
+/* SH modes */
+
+#define R_SH_PCREL8 3 /* 8 bit pcrel */
+#define R_SH_PCREL16 4 /* 16 bit pcrel */
+#define R_SH_HIGH8 5 /* high 8 bits of 24 bit address */
+#define R_SH_LOW16 7 /* low 16 bits of 24 bit immediate */
+#define R_SH_IMM24 6 /* 24 bit immediate */
+#define R_SH_PCDISP8BY4 9 /* PC rel 8 bits *4 +ve */
+#define R_SH_PCDISP8BY2 10 /* PC rel 8 bits *2 +ve */
+#define R_SH_PCDISP8 11 /* 8 bit branch */
+#define R_SH_PCDISP 12 /* 12 bit branch */
+#define R_SH_IMM32 14 /* 32 bit immediate */
+#define R_SH_IMM8 16
+#define R_SH_IMM8BY2 17
+#define R_SH_IMM8BY4 18
+#define R_SH_IMM4 19
+#define R_SH_IMM4BY2 20
+#define R_SH_IMM4BY4 21
+#define R_SH_PCRELIMM8BY2 22
+#define R_SH_PCRELIMM8BY4 23
+
+
+
diff --git a/gnu/usr.bin/gdb/gdb/coff/sym.h b/gnu/usr.bin/gdb/gdb/coff/sym.h
new file mode 100644
index 0000000..990eeac
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/coff/sym.h
@@ -0,0 +1,477 @@
+/* Declarations of internal format of MIPS ECOFF symbols.
+ Originally contributed by MIPS Computer Systems and Third Eye Software.
+ Changes contributed by Cygnus Support are in the public domain.
+
+ This file is just aggregated with the files that make up the GNU
+ release; it is not considered part of GAS, GDB, or other GNU
+ programs. */
+
+/*
+ * |-----------------------------------------------------------|
+ * | Copyright (c) 1992, 1991, 1990 MIPS Computer Systems, Inc.|
+ * | MIPS Computer Systems, Inc. grants reproduction and use |
+ * | rights to all parties, PROVIDED that this comment is |
+ * | maintained in the copy. |
+ * |-----------------------------------------------------------|
+ */
+#ifndef _SYM_H
+#define _SYM_H
+
+/* (C) Copyright 1984 by Third Eye Software, Inc.
+ *
+ * Third Eye Software, Inc. grants reproduction and use rights to
+ * all parties, PROVIDED that this comment is maintained in the copy.
+ *
+ * Third Eye makes no claims about the applicability of this
+ * symbol table to a particular use.
+ */
+
+/*
+ * This file contains the definition of the Third Eye Symbol Table.
+ *
+ * Symbols are assumed to be in 'encounter order' - i.e. the order that
+ * the things they represent were encountered by the compiler/assembler/loader.
+ * EXCEPT for globals! These are assumed to be bunched together,
+ * probably right after the last 'normal' symbol. Globals ARE sorted
+ * in ascending order.
+ *
+ * -----------------------------------------------------------------------
+ * A brief word about Third Eye naming/use conventions:
+ *
+ * All arrays and index's are 0 based.
+ * All "ifooMax" values are the highest legal value PLUS ONE. This makes
+ * them good for allocating arrays, etc. All checks are "ifoo < ifooMax".
+ *
+ * "isym" Index into the SYMbol table.
+ * "ipd" Index into the Procedure Descriptor array.
+ * "ifd" Index into the File Descriptor array.
+ * "iss" Index into String Space.
+ * "cb" Count of Bytes.
+ * "rgPd" array whose domain is "0..ipdMax-1" and RanGe is PDR.
+ * "rgFd" array whose domain is "0..ifdMax-1" and RanGe is FDR.
+ */
+
+
+/*
+ * Symbolic Header (HDR) structure.
+ * As long as all the pointers are set correctly,
+ * we don't care WHAT order the various sections come out in!
+ *
+ * A file produced solely for the use of CDB will probably NOT have
+ * any instructions or data areas in it, as these are available
+ * in the original.
+ */
+
+typedef struct {
+ short magic; /* to verify validity of the table */
+ short vstamp; /* version stamp */
+ long ilineMax; /* number of line number entries */
+ bfd_vma cbLine; /* number of bytes for line number entries */
+ bfd_vma cbLineOffset; /* offset to start of line number entries*/
+ long idnMax; /* max index into dense number table */
+ bfd_vma cbDnOffset; /* offset to start dense number table */
+ long ipdMax; /* number of procedures */
+ bfd_vma cbPdOffset; /* offset to procedure descriptor table */
+ long isymMax; /* number of local symbols */
+ bfd_vma cbSymOffset; /* offset to start of local symbols*/
+ long ioptMax; /* max index into optimization symbol entries */
+ bfd_vma cbOptOffset; /* offset to optimization symbol entries */
+ long iauxMax; /* number of auxillary symbol entries */
+ bfd_vma cbAuxOffset; /* offset to start of auxillary symbol entries*/
+ long issMax; /* max index into local strings */
+ bfd_vma cbSsOffset; /* offset to start of local strings */
+ long issExtMax; /* max index into external strings */
+ bfd_vma cbSsExtOffset; /* offset to start of external strings */
+ long ifdMax; /* number of file descriptor entries */
+ bfd_vma cbFdOffset; /* offset to file descriptor table */
+ long crfd; /* number of relative file descriptor entries */
+ bfd_vma cbRfdOffset; /* offset to relative file descriptor table */
+ long iextMax; /* max index into external symbols */
+ bfd_vma cbExtOffset; /* offset to start of external symbol entries*/
+ /* If you add machine dependent fields, add them here */
+ } HDRR, *pHDRR;
+#define cbHDRR sizeof(HDRR)
+#define hdrNil ((pHDRR)0)
+
+/*
+ * The FDR and PDR structures speed mapping of address <-> name.
+ * They are sorted in ascending memory order and are kept in
+ * memory by CDB at runtime.
+ */
+
+/*
+ * File Descriptor
+ *
+ * There is one of these for EVERY FILE, whether compiled with
+ * full debugging symbols or not. The name of a file should be
+ * the path name given to the compiler. This allows the user
+ * to simply specify the names of the directories where the COMPILES
+ * were done, and we will be able to find their files.
+ * A field whose comment starts with "R - " indicates that it will be
+ * setup at runtime.
+ */
+typedef struct fdr {
+ bfd_vma adr; /* memory address of beginning of file */
+ long rss; /* file name (of source, if known) */
+ long issBase; /* file's string space */
+ bfd_vma cbSs; /* number of bytes in the ss */
+ long isymBase; /* beginning of symbols */
+ long csym; /* count file's of symbols */
+ long ilineBase; /* file's line symbols */
+ long cline; /* count of file's line symbols */
+ long ioptBase; /* file's optimization entries */
+ long copt; /* count of file's optimization entries */
+ unsigned short ipdFirst;/* start of procedures for this file */
+ short cpd; /* count of procedures for this file */
+ long iauxBase; /* file's auxiliary entries */
+ long caux; /* count of file's auxiliary entries */
+ long rfdBase; /* index into the file indirect table */
+ long crfd; /* count file indirect entries */
+ unsigned lang: 5; /* language for this file */
+ unsigned fMerge : 1; /* whether this file can be merged */
+ unsigned fReadin : 1; /* true if it was read in (not just created) */
+ unsigned fBigendian : 1;/* if set, was compiled on big endian machine */
+ /* aux's will be in compile host's sex */
+ unsigned glevel : 2; /* level this file was compiled with */
+ unsigned reserved : 22; /* reserved for future use */
+ bfd_vma cbLineOffset; /* byte offset from header for this file ln's */
+ bfd_vma cbLine; /* size of lines for this file */
+ } FDR, *pFDR;
+#define cbFDR sizeof(FDR)
+#define fdNil ((pFDR)0)
+#define ifdNil -1
+#define ifdTemp 0
+#define ilnNil -1
+
+
+/*
+ * Procedure Descriptor
+ *
+ * There is one of these for EVERY TEXT LABEL.
+ * If a procedure is in a file with full symbols, then isym
+ * will point to the PROC symbols, else it will point to the
+ * global symbol for the label.
+ */
+
+typedef struct pdr {
+ bfd_vma adr; /* memory address of start of procedure */
+ long isym; /* start of local symbol entries */
+ long iline; /* start of line number entries*/
+ long regmask; /* save register mask */
+ long regoffset; /* save register offset */
+ long iopt; /* start of optimization symbol entries*/
+ long fregmask; /* save floating point register mask */
+ long fregoffset; /* save floating point register offset */
+ long frameoffset; /* frame size */
+ short framereg; /* frame pointer register */
+ short pcreg; /* offset or reg of return pc */
+ long lnLow; /* lowest line in the procedure */
+ long lnHigh; /* highest line in the procedure */
+ bfd_vma cbLineOffset; /* byte offset for this procedure from the fd base */
+ /* These fields are new for 64 bit ECOFF. */
+ unsigned gp_prologue : 8; /* byte size of GP prologue */
+ unsigned gp_used : 1; /* true if the procedure uses GP */
+ unsigned reg_frame : 1; /* true if register frame procedure */
+ unsigned reserved : 14; /* reserved: must be zero */
+ unsigned localoff : 8; /* offset of local variables from vfp */
+ } PDR, *pPDR;
+#define cbPDR sizeof(PDR)
+#define pdNil ((pPDR) 0)
+#define ipdNil -1
+
+/*
+ * The structure of the runtime procedure descriptor created by the loader
+ * for use by the static exception system.
+ */
+typedef struct runtime_pdr {
+ unsigned long adr; /* memory address of start of procedure */
+ long regmask; /* save register mask */
+ long regoffset; /* save register offset */
+ long fregmask; /* save floating point register mask */
+ long fregoffset; /* save floating point register offset */
+ long frameoffset; /* frame size */
+ short framereg; /* frame pointer register */
+ short pcreg; /* offset or reg of return pc */
+ long irpss; /* index into the runtime string table */
+ long reserved;
+ struct exception_info *exception_info;/* pointer to exception array */
+} RPDR, *pRPDR;
+#define cbRPDR sizeof(RPDR)
+#define rpdNil ((pRPDR) 0)
+
+/*
+ * Line Numbers
+ *
+ * Line Numbers are segregated from the normal symbols because they
+ * are [1] smaller , [2] are of no interest to your
+ * average loader, and [3] are never needed in the middle of normal
+ * scanning and therefore slow things down.
+ *
+ * By definition, the first LINER for any given procedure will have
+ * the first line of a procedure and represent the first address.
+ */
+
+typedef long LINER, *pLINER;
+#define lineNil ((pLINER)0)
+#define cbLINER sizeof(LINER)
+#define ilineNil -1
+
+
+
+/*
+ * The Symbol Structure (GFW, to those who Know!)
+ */
+
+typedef struct {
+ long iss; /* index into String Space of name */
+ long value; /* value of symbol */
+ unsigned st : 6; /* symbol type */
+ unsigned sc : 5; /* storage class - text, data, etc */
+ unsigned reserved : 1; /* reserved */
+ unsigned index : 20; /* index into sym/aux table */
+ } SYMR, *pSYMR;
+#define symNil ((pSYMR)0)
+#define cbSYMR sizeof(SYMR)
+#define isymNil -1
+#define indexNil 0xfffff
+#define issNil -1
+#define issNull 0
+
+
+/* The following converts a memory resident string to an iss.
+ * This hack is recognized in SbFIss, in sym.c of the debugger.
+ */
+#define IssFSb(sb) (0x80000000 | ((unsigned long)(sb)))
+
+/* E X T E R N A L S Y M B O L R E C O R D
+ *
+ * Same as the SYMR except it contains file context to determine where
+ * the index is.
+ */
+typedef struct {
+ unsigned jmptbl:1; /* symbol is a jump table entry for shlibs */
+ unsigned cobol_main:1; /* symbol is a cobol main procedure */
+ unsigned weakext:1; /* symbol is weak external */
+ unsigned reserved:13; /* reserved for future use */
+ int ifd; /* where the iss and index fields point into */
+ SYMR asym; /* symbol for the external */
+ } EXTR, *pEXTR;
+#define extNil ((pEXTR)0)
+#define cbEXTR sizeof(EXTR)
+
+
+/* A U X I L L A R Y T Y P E I N F O R M A T I O N */
+
+/*
+ * Type Information Record
+ */
+typedef struct {
+ unsigned fBitfield : 1; /* set if bit width is specified */
+ unsigned continued : 1; /* indicates additional TQ info in next AUX */
+ unsigned bt : 6; /* basic type */
+ unsigned tq4 : 4;
+ unsigned tq5 : 4;
+ /* ---- 16 bit boundary ---- */
+ unsigned tq0 : 4;
+ unsigned tq1 : 4; /* 6 type qualifiers - tqPtr, etc. */
+ unsigned tq2 : 4;
+ unsigned tq3 : 4;
+ } TIR, *pTIR;
+#define cbTIR sizeof(TIR)
+#define tiNil ((pTIR)0)
+#define itqMax 6
+
+/*
+ * Relative symbol record
+ *
+ * If the rfd field is 4095, the index field indexes into the global symbol
+ * table.
+ */
+
+typedef struct {
+ unsigned rfd : 12; /* index into the file indirect table */
+ unsigned index : 20; /* index int sym/aux/iss tables */
+ } RNDXR, *pRNDXR;
+#define cbRNDXR sizeof(RNDXR)
+#define rndxNil ((pRNDXR)0)
+
+/* dense numbers or sometimes called block numbers are stored in this type,
+ * a rfd of 0xffffffff is an index into the global table.
+ */
+typedef struct {
+ unsigned long rfd; /* index into the file table */
+ unsigned long index; /* index int sym/aux/iss tables */
+ } DNR, *pDNR;
+#define cbDNR sizeof(DNR)
+#define dnNil ((pDNR)0)
+
+
+
+/*
+ * Auxillary information occurs only if needed.
+ * It ALWAYS occurs in this order when present.
+
+ isymMac used by stProc only
+ TIR type info
+ TIR additional TQ info (if first TIR was not enough)
+ rndx if (bt == btStruct,btUnion,btEnum,btSet,btRange,
+ btTypedef):
+ rsym.index == iaux for btSet or btRange
+ else rsym.index == isym
+ dimLow btRange, btSet
+ dimMac btRange, btSet
+ rndx0 As many as there are tq arrays
+ dimLow0
+ dimHigh0
+ ...
+ rndxMax-1
+ dimLowMax-1
+ dimHighMax-1
+ width in bits if (bit field), width in bits.
+ */
+#define cAuxMax (6 + (idimMax*3))
+
+/* a union of all possible info in the AUX universe */
+typedef union {
+ TIR ti; /* type information record */
+ RNDXR rndx; /* relative index into symbol table */
+ long dnLow; /* low dimension */
+ long dnHigh; /* high dimension */
+ long isym; /* symbol table index (end of proc) */
+ long iss; /* index into string space (not used) */
+ long width; /* width for non-default sized struc fields */
+ long count; /* count of ranges for variant arm */
+ } AUXU, *pAUXU;
+#define cbAUXU sizeof(AUXU)
+#define auxNil ((pAUXU)0)
+#define iauxNil -1
+
+
+/*
+ * Optimization symbols
+ *
+ * Optimization symbols contain some overlap information with the normal
+ * symbol table. In particular, the proc information
+ * is somewhat redundant but necessary to easily find the other information
+ * present.
+ *
+ * All of the offsets are relative to the beginning of the last otProc
+ */
+
+typedef struct {
+ unsigned ot: 8; /* optimization type */
+ unsigned value: 24; /* address where we are moving it to */
+ RNDXR rndx; /* points to a symbol or opt entry */
+ unsigned long offset; /* relative offset this occured */
+ } OPTR, *pOPTR;
+#define optNil ((pOPTR) 0)
+#define cbOPTR sizeof(OPTR)
+#define ioptNil -1
+
+/*
+ * File Indirect
+ *
+ * When a symbol is referenced across files the following procedure is used:
+ * 1) use the file index to get the File indirect entry.
+ * 2) use the file indirect entry to get the File descriptor.
+ * 3) add the sym index to the base of that file's sym table
+ *
+ */
+
+typedef long RFDT, *pRFDT;
+#define cbRFDT sizeof(RFDT)
+#define rfdNil -1
+
+/*
+ * The file indirect table in the mips loader is known as an array of FITs.
+ * This is done to keep the code in the loader readable in the area where
+ * these tables are merged. Note this is only a name change.
+ */
+typedef long FIT, *pFIT;
+#define cbFIT sizeof(FIT)
+#define ifiNil -1
+#define fiNil ((pFIT) 0)
+
+#ifdef _LANGUAGE_PASCAL
+#define ifdNil -1
+#define ilnNil -1
+#define ipdNil -1
+#define ilineNil -1
+#define isymNil -1
+#define indexNil 16#fffff
+#define issNil -1
+#define issNull 0
+#define itqMax 6
+#define iauxNil -1
+#define ioptNil -1
+#define rfdNil -1
+#define ifiNil -1
+#endif /* _LANGUAGE_PASCAL */
+
+
+/* Dense numbers
+ *
+ * Rather than use file index, symbol index pairs to represent symbols
+ * and globals, we use dense number so that they can be easily embeded
+ * in intermediate code and the programs that process them can
+ * use direct access tabls instead of hash table (which would be
+ * necesary otherwise because of the sparse name space caused by
+ * file index, symbol index pairs. Dense number are represented
+ * by RNDXRs.
+ */
+
+/*
+ * The following table defines the meaning of each SYM field as
+ * a function of the "st". (scD/B == scData OR scBss)
+ *
+ * Note: the value "isymMac" is used by symbols that have the concept
+ * of enclosing a block of related information. This value is the
+ * isym of the first symbol AFTER the end associated with the primary
+ * symbol. For example if a procedure was at isym==90 and had an
+ * isymMac==155, the associated end would be at isym==154, and the
+ * symbol at 155 would probably (although not necessarily) be the
+ * symbol for the next procedure. This allows rapid skipping over
+ * internal information of various sorts. "stEnd"s ALWAYS have the
+ * isym of the primary symbol that started the block.
+ *
+
+ST SC VALUE INDEX
+-------- ------ -------- ------
+stFile scText address isymMac
+stLabel scText address ---
+stGlobal scD/B address iaux
+stStatic scD/B address iaux
+stParam scAbs offset iaux
+stLocal scAbs offset iaux
+stProc scText address iaux (isymMac is first AUX)
+stStaticProc scText address iaux (isymMac is first AUX)
+
+stMember scNil ordinal --- (if member of enum)
+ (mipsread thinks the case below has a bit, not byte, offset.)
+stMember scNil byte offset iaux (if member of struct/union)
+stMember scBits bit offset iaux (bit field spec)
+
+stBlock scText address isymMac (text block)
+ (the code seems to think that rather than scNil, we see scInfo for
+ the two cases below.)
+stBlock scNil cb isymMac (struct/union member define)
+stBlock scNil cMembers isymMac (enum member define)
+
+ (New types added by SGI to simplify things:)
+stStruct scInfo cb isymMac (struct type define)
+stUnion scInfo cb isymMac (union type define)
+stEnum scInfo cMembers isymMac (enum type define)
+
+stEnd scText address isymStart
+stEnd scNil ------- isymStart (struct/union/enum)
+
+stTypedef scNil ------- iaux
+stRegReloc sc??? value old register number
+stForward sc??? new address isym to original symbol
+
+stConstant scInfo value --- (scalar)
+stConstant scInfo iss --- (complex, e.g. string)
+
+ *
+ */
+#endif
diff --git a/gnu/usr.bin/gdb/gdb/coff/symconst.h b/gnu/usr.bin/gdb/gdb/coff/symconst.h
new file mode 100644
index 0000000..e4ed620
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/coff/symconst.h
@@ -0,0 +1,175 @@
+/* Declarations of constants for internal format of MIPS ECOFF symbols.
+ Originally contributed by MIPS Computer Systems and Third Eye Software.
+ Changes contributed by Cygnus Support are in the public domain.
+
+ This file is just aggregated with the files that make up the GNU
+ release; it is not considered part of GAS, GDB, or other GNU
+ programs. */
+
+/*
+ * |-----------------------------------------------------------|
+ * | Copyright (c) 1992, 1991, 1990 MIPS Computer Systems, Inc.|
+ * | MIPS Computer Systems, Inc. grants reproduction and use |
+ * | rights to all parties, PROVIDED that this comment is |
+ * | maintained in the copy. |
+ * |-----------------------------------------------------------|
+ */
+
+/* (C) Copyright 1984 by Third Eye Software, Inc.
+ *
+ * Third Eye Software, Inc. grants reproduction and use rights to
+ * all parties, PROVIDED that this comment is maintained in the copy.
+ *
+ * Third Eye makes no claims about the applicability of this
+ * symbol table to a particular use.
+ */
+
+/* glevels for field in FDR */
+#define GLEVEL_0 2
+#define GLEVEL_1 1
+#define GLEVEL_2 0 /* for upward compat reasons. */
+#define GLEVEL_3 3
+
+/* magic number fo symheader */
+#define magicSym 0x7009
+/* The Alpha uses this value instead, for some reason. */
+#define magicSym2 0x1992
+
+/* Language codes */
+#define langC 0
+#define langPascal 1
+#define langFortran 2
+#define langAssembler 3 /* one Assembley inst might map to many mach */
+#define langMachine 4
+#define langNil 5
+#define langAda 6
+#define langPl1 7
+#define langCobol 8
+#define langStdc 9 /* FIXME: Collides with SGI langCplusplus */
+#define langCplusplus 9 /* FIXME: Collides with langStdc */
+#define langCplusplusV2 10 /* SGI addition */
+#define langMax 11 /* maximun allowed 32 -- 5 bits */
+
+/* The following are value definitions for the fields in the SYMR */
+
+/*
+ * Storage Classes
+ */
+
+#define scNil 0
+#define scText 1 /* text symbol */
+#define scData 2 /* initialized data symbol */
+#define scBss 3 /* un-initialized data symbol */
+#define scRegister 4 /* value of symbol is register number */
+#define scAbs 5 /* value of symbol is absolute */
+#define scUndefined 6 /* who knows? */
+#define scCdbLocal 7 /* variable's value is IN se->va.?? */
+#define scBits 8 /* this is a bit field */
+#define scCdbSystem 9 /* variable's value is IN CDB's address space */
+#define scDbx 9 /* overlap dbx internal use */
+#define scRegImage 10 /* register value saved on stack */
+#define scInfo 11 /* symbol contains debugger information */
+#define scUserStruct 12 /* address in struct user for current process */
+#define scSData 13 /* load time only small data */
+#define scSBss 14 /* load time only small common */
+#define scRData 15 /* load time only read only data */
+#define scVar 16 /* Var parameter (fortran,pascal) */
+#define scCommon 17 /* common variable */
+#define scSCommon 18 /* small common */
+#define scVarRegister 19 /* Var parameter in a register */
+#define scVariant 20 /* Variant record */
+#define scSUndefined 21 /* small undefined(external) data */
+#define scInit 22 /* .init section symbol */
+#define scBasedVar 23 /* Fortran or PL/1 ptr based var */
+#define scXData 24 /* exception handling data */
+#define scPData 25 /* Procedure section */
+#define scFini 26 /* .fini section */
+#define scMax 32
+
+
+/*
+ * Symbol Types
+ */
+
+#define stNil 0 /* Nuthin' special */
+#define stGlobal 1 /* external symbol */
+#define stStatic 2 /* static */
+#define stParam 3 /* procedure argument */
+#define stLocal 4 /* local variable */
+#define stLabel 5 /* label */
+#define stProc 6 /* " " Procedure */
+#define stBlock 7 /* beginnning of block */
+#define stEnd 8 /* end (of anything) */
+#define stMember 9 /* member (of anything - struct/union/enum */
+#define stTypedef 10 /* type definition */
+#define stFile 11 /* file name */
+#define stRegReloc 12 /* register relocation */
+#define stForward 13 /* forwarding address */
+#define stStaticProc 14 /* load time only static procs */
+#define stConstant 15 /* const */
+#define stStaParam 16 /* Fortran static parameters */
+ /* These new symbol types have been recently added to SGI machines. */
+#define stStruct 26 /* Beginning of block defining a struct type */
+#define stUnion 27 /* Beginning of block defining a union type */
+#define stEnum 28 /* Beginning of block defining an enum type */
+ /* Psuedo-symbols - internal to debugger */
+#define stStr 60 /* string */
+#define stNumber 61 /* pure number (ie. 4 NOR 2+2) */
+#define stExpr 62 /* 2+2 vs. 4 */
+#define stType 63 /* post-coersion SER */
+#define stMax 64
+
+/* definitions for fields in TIR */
+
+/* type qualifiers for ti.tq0 -> ti.(itqMax-1) */
+#define tqNil 0 /* bt is what you see */
+#define tqPtr 1 /* pointer */
+#define tqProc 2 /* procedure */
+#define tqArray 3 /* duh */
+#define tqFar 4 /* longer addressing - 8086/8 land */
+#define tqVol 5 /* volatile */
+#define tqConst 6 /* const */
+#define tqMax 8
+
+/* basic types as seen in ti.bt */
+#define btNil 0 /* undefined (also, enum members) */
+#define btAdr 1 /* address - integer same size as pointer */
+#define btChar 2 /* character */
+#define btUChar 3 /* unsigned character */
+#define btShort 4 /* short */
+#define btUShort 5 /* unsigned short */
+#define btInt 6 /* int */
+#define btUInt 7 /* unsigned int */
+#define btLong 8 /* long */
+#define btULong 9 /* unsigned long */
+#define btFloat 10 /* float (real) */
+#define btDouble 11 /* Double (real) */
+#define btStruct 12 /* Structure (Record) */
+#define btUnion 13 /* Union (variant) */
+#define btEnum 14 /* Enumerated */
+#define btTypedef 15 /* defined via a typedef, isymRef points */
+#define btRange 16 /* subrange of int */
+#define btSet 17 /* pascal sets */
+#define btComplex 18 /* fortran complex */
+#define btDComplex 19 /* fortran double complex */
+#define btIndirect 20 /* forward or unnamed typedef */
+#define btFixedDec 21 /* Fixed Decimal */
+#define btFloatDec 22 /* Float Decimal */
+#define btString 23 /* Varying Length Character String */
+#define btBit 24 /* Aligned Bit String */
+#define btPicture 25 /* Picture */
+#define btVoid 26 /* void */
+#define btLongLong 27 /* long long */
+#define btULongLong 28 /* unsigned long long */
+#define btMax 64
+
+#if (_MFG == _MIPS)
+/* optimization type codes */
+#define otNil 0
+#define otReg 1 /* move var to reg */
+#define otBlock 2 /* begin basic block */
+#define otProc 3 /* procedure */
+#define otInline 4 /* inline procedure */
+#define otEnd 5 /* whatever you started */
+#define otMax 6 /* KEEP UP TO DATE */
+#endif /* (_MFG == _MIPS) */
diff --git a/gnu/usr.bin/gdb/gdb/coffread.c b/gnu/usr.bin/gdb/gdb/coffread.c
new file mode 100644
index 0000000..eb0905b
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/coffread.c
@@ -0,0 +1,2071 @@
+/* Read coff symbol tables and convert to internal format, for GDB.
+ Contributed by David D. Johnson, Brown University (ddj@cs.brown.edu).
+ Copyright 1987, 1988, 1989, 1990, 1991, 1992, 1993
+ Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "breakpoint.h"
+#include "bfd.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "buildsym.h"
+#include "gdb-stabs.h"
+#include "complaints.h"
+#include <obstack.h>
+
+#include <string.h>
+
+#include <time.h> /* For time_t in libbfd.h. */
+#include <sys/types.h> /* For time_t, if not in time.h. */
+#include "libbfd.h" /* FIXME secret internal data from BFD */
+#include "coff/internal.h" /* Internal format of COFF symbols in BFD */
+#include "libcoff.h" /* FIXME secret internal data from BFD */
+
+struct coff_symfile_info {
+ file_ptr min_lineno_offset; /* Where in file lowest line#s are */
+ file_ptr max_lineno_offset; /* 1+last byte of line#s in file */
+
+ asection *stabsect; /* Section pointer for .stab section */
+ asection *stabstrsect; /* Section pointer for .stab section */
+ asection *stabindexsect; /* Section pointer for .stab.index section */
+ char *stabstrdata;
+};
+
+/* Translate an external name string into a user-visible name. */
+#define EXTERNAL_NAME(string, abfd) \
+ (string[0] == bfd_get_symbol_leading_char(abfd)? string+1: string)
+
+/* To be an sdb debug type, type must have at least a basic or primary
+ derived type. Using this rather than checking against T_NULL is
+ said to prevent core dumps if we try to operate on Michael Bloom
+ dbx-in-coff file. */
+
+#define SDB_TYPE(type) (BTYPE(type) | (type & N_TMASK))
+
+/*
+ * Convert from an sdb register number to an internal gdb register number.
+ * This should be defined in tm.h, if REGISTER_NAMES is not set up
+ * to map one to one onto the sdb register numbers.
+ */
+#ifndef SDB_REG_TO_REGNUM
+# define SDB_REG_TO_REGNUM(value) (value)
+#endif
+
+/* Core address of start and end of text of current source file.
+ This comes from a ".text" symbol where x_nlinno > 0. */
+
+static CORE_ADDR cur_src_start_addr;
+static CORE_ADDR cur_src_end_addr;
+
+/* Core address of the end of the first object file. */
+static CORE_ADDR first_object_file_end;
+
+/* The addresses of the symbol table stream and number of symbols
+ of the object file we are reading (as copied into core). */
+
+static FILE *nlist_stream_global;
+static int nlist_nsyms_global;
+
+/* Vector of line number information. */
+
+static struct linetable *line_vector;
+
+/* Index of next entry to go in line_vector_index. */
+
+static int line_vector_index;
+
+/* Last line number recorded in the line vector. */
+
+static int prev_line_number;
+
+/* Number of elements allocated for line_vector currently. */
+
+static int line_vector_length;
+
+/* Pointers to scratch storage, used for reading raw symbols and auxents. */
+
+static char *temp_sym;
+static char *temp_aux;
+
+/* Local variables that hold the shift and mask values for the
+ COFF file that we are currently reading. These come back to us
+ from BFD, and are referenced by their macro names, as well as
+ internally to the BTYPE, ISPTR, ISFCN, ISARY, ISTAG, and DECREF
+ macros from ../internalcoff.h . */
+
+static unsigned local_n_btmask;
+static unsigned local_n_btshft;
+static unsigned local_n_tmask;
+static unsigned local_n_tshift;
+
+#define N_BTMASK local_n_btmask
+#define N_BTSHFT local_n_btshft
+#define N_TMASK local_n_tmask
+#define N_TSHIFT local_n_tshift
+
+/* Local variables that hold the sizes in the file of various COFF structures.
+ (We only need to know this to read them from the file -- BFD will then
+ translate the data in them, into `internal_xxx' structs in the right
+ byte order, alignment, etc.) */
+
+static unsigned local_linesz;
+static unsigned local_symesz;
+static unsigned local_auxesz;
+
+
+/* Chain of typedefs of pointers to empty struct/union types.
+ They are chained thru the SYMBOL_VALUE_CHAIN. */
+
+static struct symbol *opaque_type_chain[HASHSIZE];
+
+#if 0
+/* The type of the function we are currently reading in. This is
+ used by define_symbol to record the type of arguments to a function. */
+
+struct type *in_function_type;
+#endif
+
+struct pending_block *pending_blocks;
+
+/* Complaints about various problems in the file being read */
+
+struct complaint ef_complaint =
+ {"Unmatched .ef symbol(s) ignored starting at symnum %d", 0, 0};
+
+struct complaint bf_no_aux_complaint =
+ {"`.bf' symbol %d has no aux entry", 0, 0};
+
+struct complaint ef_no_aux_complaint =
+ {"`.ef' symbol %d has no aux entry", 0, 0};
+
+struct complaint lineno_complaint =
+ {"Line number pointer %d lower than start of line numbers", 0, 0};
+
+struct complaint unexpected_type_complaint =
+ {"Unexpected type for symbol %s", 0, 0};
+
+struct complaint bad_sclass_complaint =
+ {"Bad n_sclass for symbol %s", 0, 0};
+
+struct complaint misordered_blocks_complaint =
+ {"Blocks out of order at address %x", 0, 0};
+
+struct complaint tagndx_bad_complaint =
+ {"Symbol table entry for %s has bad tagndx value", 0, 0};
+
+struct complaint eb_complaint =
+ {"Mismatched .eb symbol ignored starting at symnum %d", 0, 0};
+
+/* Simplified internal version of coff symbol table information */
+
+struct coff_symbol {
+ char *c_name;
+ int c_symnum; /* symbol number of this entry */
+ int c_naux; /* 0 if syment only, 1 if syment + auxent, etc */
+ long c_value;
+ int c_sclass;
+ int c_secnum;
+ unsigned int c_type;
+};
+
+static struct type *
+coff_read_struct_type PARAMS ((int, int, int));
+
+static struct type *
+decode_base_type PARAMS ((struct coff_symbol *, unsigned int,
+ union internal_auxent *));
+
+static struct type *
+decode_type PARAMS ((struct coff_symbol *, unsigned int,
+ union internal_auxent *));
+
+static struct type *
+decode_function_type PARAMS ((struct coff_symbol *, unsigned int,
+ union internal_auxent *));
+
+static struct type *
+coff_read_enum_type PARAMS ((int, int, int));
+
+static struct symbol *
+process_coff_symbol PARAMS ((struct coff_symbol *, union internal_auxent *,
+ struct objfile *));
+
+static void
+patch_opaque_types PARAMS ((struct symtab *));
+
+static void
+patch_type PARAMS ((struct type *, struct type *));
+
+static void
+enter_linenos PARAMS ((long, int, int));
+
+static void
+free_linetab PARAMS ((void));
+
+static int
+init_lineno PARAMS ((int, long, int));
+
+static char *
+getfilename PARAMS ((union internal_auxent *));
+
+static char *
+getsymname PARAMS ((struct internal_syment *));
+
+static void
+free_stringtab PARAMS ((void));
+
+static int
+init_stringtab PARAMS ((int, long));
+
+static void
+read_one_sym PARAMS ((struct coff_symbol *, struct internal_syment *,
+ union internal_auxent *));
+
+static void
+read_coff_symtab PARAMS ((long, int, struct objfile *));
+
+static void
+find_linenos PARAMS ((bfd *, sec_ptr, PTR));
+
+static void
+coff_symfile_init PARAMS ((struct objfile *));
+
+static void
+coff_new_init PARAMS ((struct objfile *));
+
+static void
+coff_symfile_read PARAMS ((struct objfile *, struct section_offsets *, int));
+
+static void
+coff_symfile_finish PARAMS ((struct objfile *));
+
+static void
+record_minimal_symbol PARAMS ((char *, CORE_ADDR, enum minimal_symbol_type));
+
+static void
+coff_end_symtab PARAMS ((struct objfile *));
+
+static void
+complete_symtab PARAMS ((char *, CORE_ADDR, unsigned int));
+
+static void
+coff_start_symtab PARAMS ((void));
+
+static void
+coff_record_line PARAMS ((int, CORE_ADDR));
+
+static struct type *
+coff_alloc_type PARAMS ((int));
+
+static struct type **
+coff_lookup_type PARAMS ((int));
+
+
+static void
+coff_locate_sections PARAMS ((bfd *, asection *, PTR));
+
+/* We are called once per section from coff_symfile_read. We
+ need to examine each section we are passed, check to see
+ if it is something we are interested in processing, and
+ if so, stash away some access information for the section.
+
+ FIXME: The section names should not be hardwired strings (what
+ should they be? I don't think most object file formats have enough
+ section flags to specify what kind of debug section it is
+ -kingdon). */
+
+static void
+coff_locate_sections (ignore_abfd, sectp, csip)
+ bfd *ignore_abfd;
+ asection *sectp;
+ PTR csip;
+{
+ register struct coff_symfile_info *csi;
+
+ csi = (struct coff_symfile_info *) csip;
+ if (STREQ (sectp->name, ".stab"))
+ {
+ csi->stabsect = sectp;
+ }
+ else if (STREQ (sectp->name, ".stabstr"))
+ {
+ csi->stabstrsect = sectp;
+ }
+ else if (STREQ (sectp->name, ".stab.index"))
+ {
+ csi->stabindexsect = sectp;
+ }
+}
+
+/* Look up a coff type-number index. Return the address of the slot
+ where the type for that index is stored.
+ The type-number is in INDEX.
+
+ This can be used for finding the type associated with that index
+ or for associating a new type with the index. */
+
+static struct type **
+coff_lookup_type (index)
+ register int index;
+{
+ if (index >= type_vector_length)
+ {
+ int old_vector_length = type_vector_length;
+
+ type_vector_length *= 2;
+ if (index /* is still */ >= type_vector_length) {
+ type_vector_length = index * 2;
+ }
+ type_vector = (struct type **)
+ xrealloc ((char *) type_vector,
+ type_vector_length * sizeof (struct type *));
+ memset (&type_vector[old_vector_length], 0,
+ (type_vector_length - old_vector_length) * sizeof(struct type *));
+ }
+ return &type_vector[index];
+}
+
+/* Make sure there is a type allocated for type number index
+ and return the type object.
+ This can create an empty (zeroed) type object. */
+
+static struct type *
+coff_alloc_type (index)
+ int index;
+{
+ register struct type **type_addr = coff_lookup_type (index);
+ register struct type *type = *type_addr;
+
+ /* If we are referring to a type not known at all yet,
+ allocate an empty type for it.
+ We will fill it in later if we find out how. */
+ if (type == NULL)
+ {
+ type = alloc_type (current_objfile);
+ *type_addr = type;
+ }
+ return type;
+}
+
+/* Record a line number entry for line LINE at address PC.
+ FIXME: Use record_line instead. */
+
+static void
+coff_record_line (line, pc)
+ int line;
+ CORE_ADDR pc;
+{
+ struct linetable_entry *e;
+ /* Make sure line vector is big enough. */
+
+ if (line_vector_index + 2 >= line_vector_length)
+ {
+ line_vector_length *= 2;
+ line_vector = (struct linetable *)
+ xrealloc ((char *) line_vector, sizeof (struct linetable)
+ + (line_vector_length
+ * sizeof (struct linetable_entry)));
+ }
+
+ e = line_vector->item + line_vector_index++;
+ e->line = line; e->pc = pc;
+}
+
+/* Start a new symtab for a new source file.
+ This is called when a COFF ".file" symbol is seen;
+ it indicates the start of data for one original source file. */
+
+static void
+coff_start_symtab ()
+{
+ start_symtab (
+ /* We fill in the filename later. start_symtab
+ puts this pointer into last_source file and in
+ coff_end_symtab we assume we can free() it.
+ FIXME: leaks memory. */
+ savestring ("", 0),
+ /* We never know the directory name for COFF. */
+ NULL,
+ /* The start address is irrelevant, since we set
+ last_source_start_addr in coff_end_symtab. */
+ 0);
+
+ /* Initialize the source file line number information for this file. */
+
+ if (line_vector) /* Unlikely, but maybe possible? */
+ free ((PTR)line_vector);
+ line_vector_index = 0;
+ line_vector_length = 1000;
+ prev_line_number = -2; /* Force first line number to be explicit */
+ line_vector = (struct linetable *)
+ xmalloc (sizeof (struct linetable)
+ + line_vector_length * sizeof (struct linetable_entry));
+}
+
+/* Save the vital information from when starting to read a file,
+ for use when closing off the current file.
+ NAME is the file name the symbols came from, START_ADDR is the first
+ text address for the file, and SIZE is the number of bytes of text. */
+
+static void
+complete_symtab (name, start_addr, size)
+ char *name;
+ CORE_ADDR start_addr;
+ unsigned int size;
+{
+ last_source_file = savestring (name, strlen (name));
+ cur_src_start_addr = start_addr;
+ cur_src_end_addr = start_addr + size;
+
+ if (current_objfile -> ei.entry_point >= cur_src_start_addr &&
+ current_objfile -> ei.entry_point < cur_src_end_addr)
+ {
+ current_objfile -> ei.entry_file_lowpc = cur_src_start_addr;
+ current_objfile -> ei.entry_file_highpc = cur_src_end_addr;
+ }
+}
+
+/* Finish the symbol definitions for one main source file,
+ close off all the lexical contexts for that file
+ (creating struct block's for them), then make the
+ struct symtab for that file and put it in the list of all such. */
+
+static void
+coff_end_symtab (objfile)
+ struct objfile *objfile;
+{
+ struct symtab *symtab;
+
+ last_source_start_addr = cur_src_start_addr;
+
+ /* For no good reason, this file stores the number of entries in a
+ separate variable instead of in line_vector->nitems. Fix it. */
+ if (line_vector)
+ line_vector->nitems = line_vector_index;
+
+ /* For COFF, we only have one subfile, so we can just look at
+ subfiles and not worry about there being other elements in the
+ chain. We fill in various fields now because we didn't know them
+ before (or because doing it now is simply an artifact of how this
+ file used to be written). */
+ subfiles->line_vector = line_vector;
+ subfiles->name = last_source_file;
+
+ /* sort_pending is needed for amdcoff, at least.
+ sort_linevec is needed for the SCO compiler. */
+ symtab = end_symtab (cur_src_end_addr, 1, 1, objfile, 0);
+
+ if (symtab != NULL)
+ free_named_symtabs (symtab->filename);
+
+ /* Reinitialize for beginning of new file. */
+ line_vector = 0;
+ line_vector_length = -1;
+ last_source_file = NULL;
+}
+
+static void
+record_minimal_symbol (name, address, type)
+ char *name;
+ CORE_ADDR address;
+ enum minimal_symbol_type type;
+{
+ /* We don't want TDESC entry points in the minimal symbol table */
+ if (name[0] == '@') return;
+
+ prim_record_minimal_symbol (savestring (name, strlen (name)), address, type);
+}
+
+/* coff_symfile_init ()
+ is the coff-specific initialization routine for reading symbols.
+ It is passed a struct objfile which contains, among other things,
+ the BFD for the file whose symbols are being read, and a slot for
+ a pointer to "private data" which we fill with cookies and other
+ treats for coff_symfile_read ().
+
+ We will only be called if this is a COFF or COFF-like file.
+ BFD handles figuring out the format of the file, and code in symtab.c
+ uses BFD's determination to vector to us.
+
+ The ultimate result is a new symtab (or, FIXME, eventually a psymtab). */
+
+static int text_bfd_scnum;
+
+static void
+coff_symfile_init (objfile)
+ struct objfile *objfile;
+{
+ asection *section, *strsection;
+ bfd *abfd = objfile->obfd;
+
+ /* Allocate struct to keep track of stab reading. */
+ objfile->sym_stab_info = (PTR)
+ xmmalloc (objfile -> md, sizeof (struct dbx_symfile_info));
+
+ memset ((PTR) objfile->sym_stab_info, 0, sizeof (struct dbx_symfile_info));
+
+ /* Allocate struct to keep track of the symfile */
+ objfile -> sym_private = xmmalloc (objfile -> md,
+ sizeof (struct coff_symfile_info));
+
+ memset (objfile->sym_private, 0, sizeof (struct coff_symfile_info));
+
+ init_entry_point_info (objfile);
+
+ /* Save the section number for the text section */
+ section = bfd_get_section_by_name (abfd, ".text");
+ if (section)
+ text_bfd_scnum = section->index;
+ else
+ text_bfd_scnum = -1;
+}
+
+/* This function is called for every section; it finds the outer limits
+ of the line table (minimum and maximum file offset) so that the
+ mainline code can read the whole thing for efficiency. */
+
+/* ARGSUSED */
+static void
+find_linenos (abfd, asect, vpinfo)
+ bfd *abfd;
+ sec_ptr asect;
+ PTR vpinfo;
+{
+ struct coff_symfile_info *info;
+ int size, count;
+ file_ptr offset, maxoff;
+
+/* WARNING WILL ROBINSON! ACCESSING BFD-PRIVATE DATA HERE! FIXME! */
+ count = asect->lineno_count;
+/* End of warning */
+
+ if (count == 0)
+ return;
+ size = count * local_linesz;
+
+ info = (struct coff_symfile_info *)vpinfo;
+/* WARNING WILL ROBINSON! ACCESSING BFD-PRIVATE DATA HERE! FIXME! */
+ offset = asect->line_filepos;
+/* End of warning */
+
+ if (offset < info->min_lineno_offset || info->min_lineno_offset == 0)
+ info->min_lineno_offset = offset;
+
+ maxoff = offset + size;
+ if (maxoff > info->max_lineno_offset)
+ info->max_lineno_offset = maxoff;
+}
+
+
+/* The BFD for this file -- only good while we're actively reading
+ symbols into a psymtab or a symtab. */
+
+static bfd *symfile_bfd;
+
+/* Read a symbol file, after initialization by coff_symfile_init. */
+/* FIXME! Addr and Mainline are not used yet -- this will not work for
+ shared libraries or add_file! */
+
+/* ARGSUSED */
+static void
+coff_symfile_read (objfile, section_offsets, mainline)
+ struct objfile *objfile;
+ struct section_offsets *section_offsets;
+ int mainline;
+{
+ struct coff_symfile_info *info;
+ struct dbx_symfile_info *dbxinfo;
+ bfd *abfd = objfile->obfd;
+ coff_data_type *cdata = coff_data (abfd);
+ char *name = bfd_get_filename (abfd);
+ int desc;
+ register int val;
+ int num_symbols;
+ int symtab_offset;
+ int stringtab_offset;
+ struct cleanup *back_to;
+ int stabsize, stabstrsize;
+
+ info = (struct coff_symfile_info *) objfile -> sym_private;
+ dbxinfo = (struct dbx_symfile_info *) objfile->sym_stab_info;
+ symfile_bfd = abfd; /* Kludge for swap routines */
+
+/* WARNING WILL ROBINSON! ACCESSING BFD-PRIVATE DATA HERE! FIXME! */
+ desc = fileno ((FILE *)(abfd->iostream)); /* File descriptor */
+ num_symbols = bfd_get_symcount (abfd); /* How many syms */
+ symtab_offset = cdata->sym_filepos; /* Symbol table file offset */
+ stringtab_offset = symtab_offset + /* String table file offset */
+ num_symbols * cdata->local_symesz;
+
+ /* Set a few file-statics that give us specific information about
+ the particular COFF file format we're reading. */
+ local_linesz = cdata->local_linesz;
+ local_n_btmask = cdata->local_n_btmask;
+ local_n_btshft = cdata->local_n_btshft;
+ local_n_tmask = cdata->local_n_tmask;
+ local_n_tshift = cdata->local_n_tshift;
+ local_linesz = cdata->local_linesz;
+ local_symesz = cdata->local_symesz;
+ local_auxesz = cdata->local_auxesz;
+
+ /* Allocate space for raw symbol and aux entries, based on their
+ space requirements as reported by BFD. */
+ temp_sym = (char *) xmalloc
+ (cdata->local_symesz + cdata->local_auxesz);
+ temp_aux = temp_sym + cdata->local_symesz;
+ back_to = make_cleanup (free_current_contents, &temp_sym);
+/* End of warning */
+
+ /* Read the line number table, all at once. */
+ info->min_lineno_offset = 0;
+ info->max_lineno_offset = 0;
+ bfd_map_over_sections (abfd, find_linenos, (PTR) info);
+
+ make_cleanup (free_linetab, 0);
+ val = init_lineno (desc, info->min_lineno_offset,
+ info->max_lineno_offset - info->min_lineno_offset);
+ if (val < 0)
+ error ("\"%s\": error reading line numbers\n", name);
+
+ /* Now read the string table, all at once. */
+
+ make_cleanup (free_stringtab, 0);
+ val = init_stringtab (desc, stringtab_offset);
+ if (val < 0)
+ error ("\"%s\": can't get string table", name);
+
+ init_minimal_symbol_collection ();
+ make_cleanup (discard_minimal_symbols, 0);
+
+ /* Now that the executable file is positioned at symbol table,
+ process it and define symbols accordingly. */
+
+ read_coff_symtab ((long)symtab_offset, num_symbols, objfile);
+
+ /* Sort symbols alphabetically within each block. */
+
+ sort_all_symtab_syms ();
+
+ /* Install any minimal symbols that have been collected as the current
+ minimal symbols for this objfile. */
+
+ install_minimal_symbols (objfile);
+
+ bfd_map_over_sections (abfd, coff_locate_sections, (PTR) info);
+
+ if (info->stabsect)
+ {
+ /* FIXME: dubious. Why can't we use something normal like
+ bfd_get_section_contents? */
+ fseek ((FILE *) abfd->iostream, abfd->where, 0);
+
+ stabsize = bfd_section_size (abfd, info->stabsect);
+ stabstrsize = bfd_section_size (abfd, info->stabstrsect);
+
+ coffstab_build_psymtabs (objfile,
+ section_offsets,
+ mainline,
+ info->stabsect->filepos, stabsize,
+ info->stabstrsect->filepos, stabstrsize);
+ }
+
+ do_cleanups (back_to);
+}
+
+static void
+coff_new_init (ignore)
+ struct objfile *ignore;
+{
+}
+
+/* Perform any local cleanups required when we are done with a particular
+ objfile. I.E, we are in the process of discarding all symbol information
+ for an objfile, freeing up all memory held for it, and unlinking the
+ objfile struct from the global list of known objfiles. */
+
+static void
+coff_symfile_finish (objfile)
+ struct objfile *objfile;
+{
+ if (objfile -> sym_private != NULL)
+ {
+ mfree (objfile -> md, objfile -> sym_private);
+ }
+}
+
+
+/* Given pointers to a symbol table in coff style exec file,
+ analyze them and create struct symtab's describing the symbols.
+ NSYMS is the number of symbols in the symbol table.
+ We read them one at a time using read_one_sym (). */
+
+static void
+read_coff_symtab (symtab_offset, nsyms, objfile)
+ long symtab_offset;
+ int nsyms;
+ struct objfile *objfile;
+{
+ FILE *stream;
+ register struct context_stack *new;
+ struct coff_symbol coff_symbol;
+ register struct coff_symbol *cs = &coff_symbol;
+ static struct internal_syment main_sym;
+ static union internal_auxent main_aux;
+ struct coff_symbol fcn_cs_saved;
+ static struct internal_syment fcn_sym_saved;
+ static union internal_auxent fcn_aux_saved;
+ struct symtab *s;
+
+ /* A .file is open. */
+ int in_source_file = 0;
+ int num_object_files = 0;
+ int next_file_symnum = -1;
+
+ /* Name of the current file. */
+ char *filestring = "";
+ int depth = 0;
+ int fcn_first_line = 0;
+ int fcn_last_line = 0;
+ int fcn_start_addr = 0;
+ long fcn_line_ptr = 0;
+ int val;
+
+ stream = bfd_cache_lookup(objfile->obfd);
+ if (!stream)
+ perror_with_name(objfile->name);
+
+ /* Work around a stdio bug in SunOS4.1.1 (this makes me nervous....
+ it's hard to know I've really worked around it. The fix should be
+ harmless, anyway). The symptom of the bug is that the first
+ fread (in read_one_sym), will (in my example) actually get data
+ from file offset 268, when the fseek was to 264 (and ftell shows
+ 264). This causes all hell to break loose. I was unable to
+ reproduce this on a short test program which operated on the same
+ file, performing (I think) the same sequence of operations.
+
+ It stopped happening when I put in this rewind().
+
+ FIXME: Find out if this has been reported to Sun, whether it has
+ been fixed in a later release, etc. */
+
+ rewind (stream);
+
+ /* Position to read the symbol table. */
+ val = fseek (stream, (long)symtab_offset, 0);
+ if (val < 0)
+ perror_with_name (objfile->name);
+
+ current_objfile = objfile;
+ nlist_stream_global = stream;
+ nlist_nsyms_global = nsyms;
+ last_source_file = NULL;
+ memset (opaque_type_chain, 0, sizeof opaque_type_chain);
+
+ if (type_vector) /* Get rid of previous one */
+ free ((PTR)type_vector);
+ type_vector_length = 160;
+ type_vector = (struct type **)
+ xmalloc (type_vector_length * sizeof (struct type *));
+ memset (type_vector, 0, type_vector_length * sizeof (struct type *));
+
+ coff_start_symtab ();
+
+ symnum = 0;
+ while (symnum < nsyms)
+ {
+ QUIT; /* Make this command interruptable. */
+ read_one_sym (cs, &main_sym, &main_aux);
+
+#ifdef SEM
+ temp_sem_val = cs->c_name[0] << 24 | cs->c_name[1] << 16 |
+ cs->c_name[2] << 8 | cs->c_name[3];
+ if (int_sem_val == temp_sem_val)
+ last_coffsem = (int) strtol (cs->c_name+4, (char **) NULL, 10);
+#endif
+
+ if (cs->c_symnum == next_file_symnum && cs->c_sclass != C_FILE)
+ {
+ if (last_source_file)
+ coff_end_symtab (objfile);
+
+ coff_start_symtab ();
+ complete_symtab ("_globals_", 0, first_object_file_end);
+ /* done with all files, everything from here on out is globals */
+ }
+
+ /* Special case for file with type declarations only, no text. */
+ if (!last_source_file && SDB_TYPE (cs->c_type)
+ && cs->c_secnum == N_DEBUG)
+ complete_symtab (filestring, 0, 0);
+
+ /* Typedefs should not be treated as symbol definitions. */
+ if (ISFCN (cs->c_type) && cs->c_sclass != C_TPDEF)
+ {
+ /* Record all functions -- external and static -- in minsyms. */
+ record_minimal_symbol (cs->c_name, cs->c_value, mst_text);
+
+ fcn_line_ptr = main_aux.x_sym.x_fcnary.x_fcn.x_lnnoptr;
+ fcn_start_addr = cs->c_value;
+ fcn_cs_saved = *cs;
+ fcn_sym_saved = main_sym;
+ fcn_aux_saved = main_aux;
+ continue;
+ }
+
+ switch (cs->c_sclass)
+ {
+ case C_EFCN:
+ case C_EXTDEF:
+ case C_ULABEL:
+ case C_USTATIC:
+ case C_LINE:
+ case C_ALIAS:
+ case C_HIDDEN:
+ complain (&bad_sclass_complaint, cs->c_name);
+ break;
+
+ case C_FILE:
+ /*
+ * c_value field contains symnum of next .file entry in table
+ * or symnum of first global after last .file.
+ */
+ next_file_symnum = cs->c_value;
+ if (cs->c_naux > 0)
+ filestring = getfilename (&main_aux);
+ else
+ filestring = "";
+
+ /*
+ * Complete symbol table for last object file
+ * containing debugging information.
+ */
+ if (last_source_file)
+ {
+ coff_end_symtab (objfile);
+ coff_start_symtab ();
+ }
+ in_source_file = 1;
+ break;
+
+ case C_STAT:
+ if (cs->c_name[0] == '.') {
+ if (STREQ (cs->c_name, ".text")) {
+ /* FIXME: don't wire in ".text" as section name
+ or symbol name! */
+ if (++num_object_files == 1) {
+ /* last address of startup file */
+ first_object_file_end = cs->c_value +
+ main_aux.x_scn.x_scnlen;
+ }
+ /* Check for in_source_file deals with case of
+ a file with debugging symbols
+ followed by a later file with no symbols. */
+ if (in_source_file)
+ complete_symtab (filestring, cs->c_value,
+ main_aux.x_scn.x_scnlen);
+ in_source_file = 0;
+ }
+ /* flush rest of '.' symbols */
+ break;
+ }
+ else if (!SDB_TYPE (cs->c_type)
+ && cs->c_name[0] == 'L'
+ && (strncmp (cs->c_name, "LI%", 3) == 0
+ || strncmp (cs->c_name, "LF%", 3) == 0
+ || strncmp (cs->c_name,"LC%",3) == 0
+ || strncmp (cs->c_name,"LP%",3) == 0
+ || strncmp (cs->c_name,"LPB%",4) == 0
+ || strncmp (cs->c_name,"LBB%",4) == 0
+ || strncmp (cs->c_name,"LBE%",4) == 0
+ || strncmp (cs->c_name,"LPBX%",5) == 0))
+ /* At least on a 3b1, gcc generates swbeg and string labels
+ that look like this. Ignore them. */
+ break;
+ /* fall in for static symbols that don't start with '.' */
+ case C_EXT:
+ /* Record external symbols in minsyms if we don't have debug
+ info for them. FIXME, this is probably the wrong thing
+ to do. Why don't we record them even if we do have
+ debug symbol info? What really belongs in the minsyms
+ anyway? Fred!?? */
+ if (!SDB_TYPE (cs->c_type)) {
+ /* FIXME: This is BOGUS Will Robinson!
+ Coff should provide the SEC_CODE flag for executable sections,
+ then if we could look up sections by section number we
+ could see if the flags indicate SEC_CODE. If so, then
+ record this symbol as a function in the minimal symbol table.
+ But why are absolute syms recorded as functions, anyway? */
+ if (cs->c_secnum <= text_bfd_scnum+1) {/* text or abs */
+ record_minimal_symbol (cs->c_name, cs->c_value,
+ mst_text);
+ break;
+ } else {
+ record_minimal_symbol (cs->c_name, cs->c_value,
+ mst_data);
+ break;
+ }
+ }
+ process_coff_symbol (cs, &main_aux, objfile);
+ break;
+
+ case C_FCN:
+ if (STREQ (cs->c_name, ".bf"))
+ {
+ within_function = 1;
+
+ /* value contains address of first non-init type code */
+ /* main_aux.x_sym.x_misc.x_lnsz.x_lnno
+ contains line number of '{' } */
+ if (cs->c_naux != 1)
+ complain (&bf_no_aux_complaint, cs->c_symnum);
+ fcn_first_line = main_aux.x_sym.x_misc.x_lnsz.x_lnno;
+
+ /* Might want to check that locals are 0 and
+ context_stack_depth is zero, and complain if not. */
+
+ depth = 0;
+ new = push_context (depth, fcn_start_addr);
+ fcn_cs_saved.c_name = getsymname (&fcn_sym_saved);
+ new->name = process_coff_symbol (&fcn_cs_saved,
+ &fcn_aux_saved, objfile);
+ }
+ else if (STREQ (cs->c_name, ".ef"))
+ {
+ /* the value of .ef is the address of epilogue code;
+ not useful for gdb. */
+ /* { main_aux.x_sym.x_misc.x_lnsz.x_lnno
+ contains number of lines to '}' */
+ new = pop_context ();
+ /* Stack must be empty now. */
+ if (context_stack_depth > 0 || new == NULL)
+ {
+ complain (&ef_complaint, cs->c_symnum);
+ within_function = 0;
+ break;
+ }
+ if (cs->c_naux != 1)
+ {
+ complain (&ef_no_aux_complaint, cs->c_symnum);
+ fcn_last_line = 0x7FFFFFFF;
+ }
+ else
+ {
+ fcn_last_line = main_aux.x_sym.x_misc.x_lnsz.x_lnno;
+ }
+ enter_linenos (fcn_line_ptr, fcn_first_line, fcn_last_line);
+
+ finish_block (new->name, &local_symbols, new->old_blocks,
+ new->start_addr,
+#if defined (FUNCTION_EPILOGUE_SIZE)
+ /* This macro should be defined only on
+ machines where the
+ fcn_aux_saved.x_sym.x_misc.x_fsize
+ field is always zero.
+ So use the .bf record information that
+ points to the epilogue and add the size
+ of the epilogue. */
+ cs->c_value + FUNCTION_EPILOGUE_SIZE,
+#else
+ fcn_cs_saved.c_value +
+ fcn_aux_saved.x_sym.x_misc.x_fsize,
+#endif
+ objfile
+ );
+ within_function = 0;
+ }
+ break;
+
+ case C_BLOCK:
+ if (STREQ (cs->c_name, ".bb"))
+ {
+ push_context (++depth, cs->c_value);
+ }
+ else if (STREQ (cs->c_name, ".eb"))
+ {
+ new = pop_context ();
+ if (depth-- != new->depth)
+ {
+ complain (&eb_complaint, symnum);
+ break;
+ }
+ if (local_symbols && context_stack_depth > 0)
+ {
+ /* Make a block for the local symbols within. */
+ finish_block (0, &local_symbols, new->old_blocks,
+ new->start_addr, cs->c_value, objfile);
+ }
+ /* Now pop locals of block just finished. */
+ local_symbols = new->locals;
+ }
+ break;
+
+ default:
+ process_coff_symbol (cs, &main_aux, objfile);
+ break;
+ }
+ }
+
+ if (last_source_file)
+ coff_end_symtab (objfile);
+
+ /* Patch up any opaque types (references to types that are not defined
+ in the file where they are referenced, e.g. "struct foo *bar"). */
+ ALL_OBJFILE_SYMTABS (objfile, s)
+ patch_opaque_types (s);
+
+ current_objfile = NULL;
+}
+
+/* Routines for reading headers and symbols from executable. */
+
+#ifdef FIXME
+/* Move these XXXMAGIC symbol defns into BFD! */
+
+/* Read COFF file header, check magic number,
+ and return number of symbols. */
+read_file_hdr (chan, file_hdr)
+ int chan;
+ FILHDR *file_hdr;
+{
+ lseek (chan, 0L, 0);
+ if (myread (chan, (char *)file_hdr, FILHSZ) < 0)
+ return -1;
+
+ switch (file_hdr->f_magic)
+ {
+#ifdef MC68MAGIC
+ case MC68MAGIC:
+#endif
+#ifdef NS32GMAGIC
+ case NS32GMAGIC:
+ case NS32SMAGIC:
+#endif
+#ifdef I386MAGIC
+ case I386MAGIC:
+#endif
+#ifdef CLIPPERMAGIC
+ case CLIPPERMAGIC:
+#endif
+#if defined (MC68KWRMAGIC) \
+ && (!defined (MC68MAGIC) || MC68KWRMAGIC != MC68MAGIC)
+ case MC68KWRMAGIC:
+#endif
+#ifdef MC68KROMAGIC
+ case MC68KROMAGIC:
+ case MC68KPGMAGIC:
+#endif
+#ifdef MC88DGMAGIC
+ case MC88DGMAGIC:
+#endif
+#ifdef MC88MAGIC
+ case MC88MAGIC:
+#endif
+#ifdef I960ROMAGIC
+ case I960ROMAGIC: /* Intel 960 */
+#endif
+#ifdef I960RWMAGIC
+ case I960RWMAGIC: /* Intel 960 */
+#endif
+ return file_hdr->f_nsyms;
+
+ default:
+#ifdef BADMAG
+ if (BADMAG(file_hdr))
+ return -1;
+ else
+ return file_hdr->f_nsyms;
+#else
+ return -1;
+#endif
+ }
+}
+#endif
+
+/* Read the next symbol, swap it, and return it in both internal_syment
+ form, and coff_symbol form. Also return its first auxent, if any,
+ in internal_auxent form, and skip any other auxents. */
+
+static void
+read_one_sym (cs, sym, aux)
+ register struct coff_symbol *cs;
+ register struct internal_syment *sym;
+ register union internal_auxent *aux;
+{
+ int i;
+
+ cs->c_symnum = symnum;
+ fread (temp_sym, local_symesz, 1, nlist_stream_global);
+ bfd_coff_swap_sym_in (symfile_bfd, temp_sym, (char *)sym);
+ cs->c_naux = sym->n_numaux & 0xff;
+ if (cs->c_naux >= 1)
+ {
+ fread (temp_aux, local_auxesz, 1, nlist_stream_global);
+ bfd_coff_swap_aux_in (symfile_bfd, temp_aux, sym->n_type, sym->n_sclass,
+ (char *)aux);
+ /* If more than one aux entry, read past it (only the first aux
+ is important). */
+ for (i = 1; i < cs->c_naux; i++)
+ fread (temp_aux, local_auxesz, 1, nlist_stream_global);
+ }
+ cs->c_name = getsymname (sym);
+ cs->c_value = sym->n_value;
+ cs->c_sclass = (sym->n_sclass & 0xff);
+ cs->c_secnum = sym->n_scnum;
+ cs->c_type = (unsigned) sym->n_type;
+ if (!SDB_TYPE (cs->c_type))
+ cs->c_type = 0;
+
+ symnum += 1 + cs->c_naux;
+}
+
+/* Support for string table handling */
+
+static char *stringtab = NULL;
+
+static int
+init_stringtab (chan, offset)
+ int chan;
+ long offset;
+{
+ long length;
+ int val;
+ unsigned char lengthbuf[4];
+
+ free_stringtab ();
+
+ if (lseek (chan, offset, 0) < 0)
+ return -1;
+
+ val = myread (chan, (char *)lengthbuf, sizeof lengthbuf);
+ length = bfd_h_get_32 (symfile_bfd, lengthbuf);
+
+ /* If no string table is needed, then the file may end immediately
+ after the symbols. Just return with `stringtab' set to null. */
+ if (val != sizeof lengthbuf || length < sizeof lengthbuf)
+ return 0;
+
+ stringtab = (char *) xmalloc (length);
+ memcpy (stringtab, &length, sizeof length);
+ if (length == sizeof length) /* Empty table -- just the count */
+ return 0;
+
+ val = myread (chan, stringtab + sizeof lengthbuf, length - sizeof lengthbuf);
+ if (val != length - sizeof lengthbuf || stringtab[length - 1] != '\0')
+ return -1;
+
+ return 0;
+}
+
+static void
+free_stringtab ()
+{
+ if (stringtab)
+ free (stringtab);
+ stringtab = NULL;
+}
+
+static char *
+getsymname (symbol_entry)
+ struct internal_syment *symbol_entry;
+{
+ static char buffer[SYMNMLEN+1];
+ char *result;
+
+ if (symbol_entry->_n._n_n._n_zeroes == 0)
+ {
+ result = stringtab + symbol_entry->_n._n_n._n_offset;
+ }
+ else
+ {
+ strncpy (buffer, symbol_entry->_n._n_name, SYMNMLEN);
+ buffer[SYMNMLEN] = '\0';
+ result = buffer;
+ }
+ return result;
+}
+
+/* Extract the file name from the aux entry of a C_FILE symbol. Return
+ only the last component of the name. Result is in static storage and
+ is only good for temporary use. */
+
+static char *
+getfilename (aux_entry)
+ union internal_auxent *aux_entry;
+{
+ static char buffer[BUFSIZ];
+ register char *temp;
+ char *result;
+
+ if (aux_entry->x_file.x_n.x_zeroes == 0)
+ strcpy (buffer, stringtab + aux_entry->x_file.x_n.x_offset);
+ else
+ {
+ strncpy (buffer, aux_entry->x_file.x_fname, FILNMLEN);
+ buffer[FILNMLEN] = '\0';
+ }
+ result = buffer;
+ if ((temp = strrchr (result, '/')) != NULL)
+ result = temp + 1;
+ return (result);
+}
+
+/* Support for line number handling */
+static char *linetab = NULL;
+static long linetab_offset;
+static unsigned long linetab_size;
+
+/* Read in all the line numbers for fast lookups later. Leave them in
+ external (unswapped) format in memory; we'll swap them as we enter
+ them into GDB's data structures. */
+
+static int
+init_lineno (chan, offset, size)
+ int chan;
+ long offset;
+ int size;
+{
+ int val;
+
+ linetab_offset = offset;
+ linetab_size = size;
+
+ free_linetab();
+
+ if (size == 0)
+ return 0;
+
+ if (lseek (chan, offset, 0) < 0)
+ return -1;
+
+ /* Allocate the desired table, plus a sentinel */
+ linetab = (char *) xmalloc (size + local_linesz);
+
+ val = myread (chan, linetab, size);
+ if (val != size)
+ return -1;
+
+ /* Terminate it with an all-zero sentinel record */
+ memset (linetab + size, 0, local_linesz);
+
+ return 0;
+}
+
+static void
+free_linetab ()
+{
+ if (linetab)
+ free (linetab);
+ linetab = NULL;
+}
+
+#if !defined (L_LNNO32)
+#define L_LNNO32(lp) ((lp)->l_lnno)
+#endif
+
+static void
+enter_linenos (file_offset, first_line, last_line)
+ long file_offset;
+ register int first_line;
+ register int last_line;
+{
+ register char *rawptr;
+ struct internal_lineno lptr;
+
+ if (file_offset < linetab_offset)
+ {
+ complain (&lineno_complaint, file_offset);
+ if (file_offset > linetab_size) /* Too big to be an offset? */
+ return;
+ file_offset += linetab_offset; /* Try reading at that linetab offset */
+ }
+
+ rawptr = &linetab[file_offset - linetab_offset];
+
+ /* skip first line entry for each function */
+ rawptr += local_linesz;
+ /* line numbers start at one for the first line of the function */
+ first_line--;
+
+ for (;;) {
+ bfd_coff_swap_lineno_in (symfile_bfd, rawptr, &lptr);
+ rawptr += local_linesz;
+ /* The next function, or the sentinel, will have L_LNNO32 zero; we exit. */
+ if (L_LNNO32 (&lptr) && L_LNNO32 (&lptr) <= last_line)
+ coff_record_line (first_line + L_LNNO32 (&lptr), lptr.l_addr.l_paddr);
+ else
+ break;
+ }
+}
+
+static void
+patch_type (type, real_type)
+ struct type *type;
+ struct type *real_type;
+{
+ register struct type *target = TYPE_TARGET_TYPE (type);
+ register struct type *real_target = TYPE_TARGET_TYPE (real_type);
+ int field_size = TYPE_NFIELDS (real_target) * sizeof (struct field);
+
+ TYPE_LENGTH (target) = TYPE_LENGTH (real_target);
+ TYPE_NFIELDS (target) = TYPE_NFIELDS (real_target);
+ TYPE_FIELDS (target) = (struct field *) TYPE_ALLOC (target, field_size);
+
+ memcpy (TYPE_FIELDS (target), TYPE_FIELDS (real_target), field_size);
+
+ if (TYPE_NAME (real_target))
+ {
+ if (TYPE_NAME (target))
+ free (TYPE_NAME (target));
+ TYPE_NAME (target) = concat (TYPE_NAME (real_target), NULL);
+ }
+}
+
+/* Patch up all appropriate typedef symbols in the opaque_type_chains
+ so that they can be used to print out opaque data structures properly. */
+
+static void
+patch_opaque_types (s)
+ struct symtab *s;
+{
+ register struct block *b;
+ register int i;
+ register struct symbol *real_sym;
+
+ /* Go through the per-file symbols only */
+ b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), STATIC_BLOCK);
+ for (i = BLOCK_NSYMS (b) - 1; i >= 0; i--)
+ {
+ /* Find completed typedefs to use to fix opaque ones.
+ Remove syms from the chain when their types are stored,
+ but search the whole chain, as there may be several syms
+ from different files with the same name. */
+ real_sym = BLOCK_SYM (b, i);
+ if (SYMBOL_CLASS (real_sym) == LOC_TYPEDEF &&
+ SYMBOL_NAMESPACE (real_sym) == VAR_NAMESPACE &&
+ TYPE_CODE (SYMBOL_TYPE (real_sym)) == TYPE_CODE_PTR &&
+ TYPE_LENGTH (TYPE_TARGET_TYPE (SYMBOL_TYPE (real_sym))) != 0)
+ {
+ register char *name = SYMBOL_NAME (real_sym);
+ register int hash = hashname (name);
+ register struct symbol *sym, *prev;
+
+ prev = 0;
+ for (sym = opaque_type_chain[hash]; sym;)
+ {
+ if (name[0] == SYMBOL_NAME (sym)[0] &&
+ STREQ (name + 1, SYMBOL_NAME (sym) + 1))
+ {
+ if (prev)
+ {
+ SYMBOL_VALUE_CHAIN (prev) = SYMBOL_VALUE_CHAIN (sym);
+ }
+ else
+ {
+ opaque_type_chain[hash] = SYMBOL_VALUE_CHAIN (sym);
+ }
+
+ patch_type (SYMBOL_TYPE (sym), SYMBOL_TYPE (real_sym));
+
+ if (prev)
+ {
+ sym = SYMBOL_VALUE_CHAIN (prev);
+ }
+ else
+ {
+ sym = opaque_type_chain[hash];
+ }
+ }
+ else
+ {
+ prev = sym;
+ sym = SYMBOL_VALUE_CHAIN (sym);
+ }
+ }
+ }
+ }
+}
+
+static struct symbol *
+process_coff_symbol (cs, aux, objfile)
+ register struct coff_symbol *cs;
+ register union internal_auxent *aux;
+ struct objfile *objfile;
+{
+ register struct symbol *sym
+ = (struct symbol *) obstack_alloc (&objfile->symbol_obstack,
+ sizeof (struct symbol));
+ char *name;
+ struct type *temptype;
+
+ memset (sym, 0, sizeof (struct symbol));
+ name = cs->c_name;
+ name = EXTERNAL_NAME (name, objfile->obfd);
+ SYMBOL_NAME (sym) = obstack_copy0 (&objfile->symbol_obstack, name,
+ strlen (name));
+
+ /* default assumptions */
+ SYMBOL_VALUE (sym) = cs->c_value;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+
+ if (ISFCN (cs->c_type))
+ {
+#if 0
+ /* FIXME: This has NOT been tested. The DBX version has.. */
+ /* Generate a template for the type of this function. The
+ types of the arguments will be added as we read the symbol
+ table. */
+ struct type *new = (struct type *)
+ obstack_alloc (&objfile->symbol_obstack, sizeof (struct type));
+
+ memcpy (new, lookup_function_type (decode_function_type (cs, cs->c_type, aux)),
+ sizeof(struct type));
+ SYMBOL_TYPE (sym) = new;
+ in_function_type = SYMBOL_TYPE(sym);
+#else
+ SYMBOL_TYPE(sym) =
+ lookup_function_type (decode_function_type (cs, cs->c_type, aux));
+#endif
+
+ SYMBOL_CLASS (sym) = LOC_BLOCK;
+ if (cs->c_sclass == C_STAT)
+ add_symbol_to_list (sym, &file_symbols);
+ else if (cs->c_sclass == C_EXT)
+ add_symbol_to_list (sym, &global_symbols);
+ }
+ else
+ {
+ SYMBOL_TYPE (sym) = decode_type (cs, cs->c_type, aux);
+ switch (cs->c_sclass)
+ {
+ case C_NULL:
+ break;
+
+ case C_AUTO:
+ SYMBOL_CLASS (sym) = LOC_LOCAL;
+ add_symbol_to_list (sym, &local_symbols);
+ break;
+
+ case C_EXT:
+ SYMBOL_CLASS (sym) = LOC_STATIC;
+ SYMBOL_VALUE_ADDRESS (sym) = (CORE_ADDR) cs->c_value;
+ add_symbol_to_list (sym, &global_symbols);
+ break;
+
+ case C_STAT:
+ SYMBOL_CLASS (sym) = LOC_STATIC;
+ SYMBOL_VALUE_ADDRESS (sym) = (CORE_ADDR) cs->c_value;
+ if (within_function) {
+ /* Static symbol of local scope */
+ add_symbol_to_list (sym, &local_symbols);
+ }
+ else {
+ /* Static symbol at top level of file */
+ add_symbol_to_list (sym, &file_symbols);
+ }
+ break;
+
+#ifdef C_GLBLREG /* AMD coff */
+ case C_GLBLREG:
+#endif
+ case C_REG:
+ SYMBOL_CLASS (sym) = LOC_REGISTER;
+ SYMBOL_VALUE (sym) = SDB_REG_TO_REGNUM(cs->c_value);
+ add_symbol_to_list (sym, &local_symbols);
+ break;
+
+ case C_LABEL:
+ break;
+
+ case C_ARG:
+ SYMBOL_CLASS (sym) = LOC_ARG;
+#if 0
+ /* FIXME: This has not been tested. */
+ /* Add parameter to function. */
+ add_param_to_type(&in_function_type,sym);
+#endif
+ add_symbol_to_list (sym, &local_symbols);
+#if !defined (BELIEVE_PCC_PROMOTION) && (TARGET_BYTE_ORDER == BIG_ENDIAN)
+ /* If PCC says a parameter is a short or a char,
+ aligned on an int boundary, realign it to the "little end"
+ of the int. */
+ temptype = lookup_fundamental_type (current_objfile, FT_INTEGER);
+ if (TYPE_LENGTH (SYMBOL_TYPE (sym)) < TYPE_LENGTH (temptype)
+ && TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_INT
+ && 0 == SYMBOL_VALUE (sym) % TYPE_LENGTH (temptype))
+ {
+ SYMBOL_VALUE (sym) += TYPE_LENGTH (temptype)
+ - TYPE_LENGTH (SYMBOL_TYPE (sym));
+ }
+#endif
+ break;
+
+ case C_REGPARM:
+ SYMBOL_CLASS (sym) = LOC_REGPARM;
+ SYMBOL_VALUE (sym) = SDB_REG_TO_REGNUM(cs->c_value);
+ add_symbol_to_list (sym, &local_symbols);
+#if !defined (BELIEVE_PCC_PROMOTION)
+ /* FIXME: This should retain the current type, since it's just
+ a register value. gnu@adobe, 26Feb93 */
+ /* If PCC says a parameter is a short or a char,
+ it is really an int. */
+ temptype = lookup_fundamental_type (current_objfile, FT_INTEGER);
+ if (TYPE_LENGTH (SYMBOL_TYPE (sym)) < TYPE_LENGTH (temptype)
+ && TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_INT)
+ {
+ SYMBOL_TYPE (sym) = TYPE_UNSIGNED (SYMBOL_TYPE (sym))
+ ? lookup_fundamental_type (current_objfile,
+ FT_UNSIGNED_INTEGER)
+ : temptype;
+ }
+#endif
+ break;
+
+ case C_TPDEF:
+ SYMBOL_CLASS (sym) = LOC_TYPEDEF;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+
+ /* If type has no name, give it one */
+ if (TYPE_NAME (SYMBOL_TYPE (sym)) == 0)
+ TYPE_NAME (SYMBOL_TYPE (sym)) = concat (SYMBOL_NAME (sym), NULL);
+
+ /* Keep track of any type which points to empty structured type,
+ so it can be filled from a definition from another file. A
+ simple forward reference (TYPE_CODE_UNDEF) is not an
+ empty structured type, though; the forward references
+ work themselves out via the magic of coff_lookup_type. */
+ if (TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_PTR &&
+ TYPE_LENGTH (TYPE_TARGET_TYPE (SYMBOL_TYPE (sym))) == 0 &&
+ TYPE_CODE (TYPE_TARGET_TYPE (SYMBOL_TYPE (sym))) !=
+ TYPE_CODE_UNDEF)
+ {
+ register int i = hashname (SYMBOL_NAME (sym));
+
+ SYMBOL_VALUE_CHAIN (sym) = opaque_type_chain[i];
+ opaque_type_chain[i] = sym;
+ }
+ add_symbol_to_list (sym, &file_symbols);
+ break;
+
+ case C_STRTAG:
+ case C_UNTAG:
+ case C_ENTAG:
+ SYMBOL_CLASS (sym) = LOC_TYPEDEF;
+ SYMBOL_NAMESPACE (sym) = STRUCT_NAMESPACE;
+
+ /* Some compilers try to be helpful by inventing "fake"
+ names for anonymous enums, structures, and unions, like
+ "~0fake" or ".0fake". Thanks, but no thanks... */
+ if (TYPE_TAG_NAME (SYMBOL_TYPE (sym)) == 0)
+ if (SYMBOL_NAME(sym) != NULL
+ && *SYMBOL_NAME(sym) != '~'
+ && *SYMBOL_NAME(sym) != '.')
+ TYPE_TAG_NAME (SYMBOL_TYPE (sym)) =
+ concat (SYMBOL_NAME (sym), NULL);
+
+ add_symbol_to_list (sym, &file_symbols);
+ break;
+
+ default:
+ break;
+ }
+ }
+ return sym;
+}
+
+/* Decode a coff type specifier;
+ return the type that is meant. */
+
+static
+struct type *
+decode_type (cs, c_type, aux)
+ register struct coff_symbol *cs;
+ unsigned int c_type;
+ register union internal_auxent *aux;
+{
+ register struct type *type = 0;
+ unsigned int new_c_type;
+
+ if (c_type & ~N_BTMASK)
+ {
+ new_c_type = DECREF (c_type);
+ if (ISPTR (c_type))
+ {
+ type = decode_type (cs, new_c_type, aux);
+ type = lookup_pointer_type (type);
+ }
+ else if (ISFCN (c_type))
+ {
+ type = decode_type (cs, new_c_type, aux);
+ type = lookup_function_type (type);
+ }
+ else if (ISARY (c_type))
+ {
+ int i, n;
+ register unsigned short *dim;
+ struct type *base_type, *index_type, *range_type;
+
+ /* Define an array type. */
+ /* auxent refers to array, not base type */
+ if (aux->x_sym.x_tagndx.l == 0)
+ cs->c_naux = 0;
+
+ /* shift the indices down */
+ dim = &aux->x_sym.x_fcnary.x_ary.x_dimen[0];
+ i = 1;
+ n = dim[0];
+ for (i = 0; *dim && i < DIMNUM - 1; i++, dim++)
+ *dim = *(dim + 1);
+ *dim = 0;
+
+ base_type = decode_type (cs, new_c_type, aux);
+ index_type = lookup_fundamental_type (current_objfile, FT_INTEGER);
+ range_type =
+ create_range_type ((struct type *) NULL, index_type, 0, n - 1);
+ type =
+ create_array_type ((struct type *) NULL, base_type, range_type);
+ }
+ return type;
+ }
+
+ /* Reference to existing type. This only occurs with the
+ struct, union, and enum types. EPI a29k coff
+ fakes us out by producing aux entries with a nonzero
+ x_tagndx for definitions of structs, unions, and enums, so we
+ have to check the c_sclass field. SCO 3.2v4 cc gets confused
+ with pointers to pointers to defined structs, and generates
+ negative x_tagndx fields. */
+ if (cs->c_naux > 0 && aux->x_sym.x_tagndx.l != 0)
+ {
+ if (cs->c_sclass != C_STRTAG
+ && cs->c_sclass != C_UNTAG
+ && cs->c_sclass != C_ENTAG
+ && aux->x_sym.x_tagndx.l >= 0)
+ {
+ type = coff_alloc_type (aux->x_sym.x_tagndx.l);
+ return type;
+ } else {
+ complain (&tagndx_bad_complaint, cs->c_name);
+ /* And fall through to decode_base_type... */
+ }
+ }
+
+ return decode_base_type (cs, BTYPE (c_type), aux);
+}
+
+/* Decode a coff type specifier for function definition;
+ return the type that the function returns. */
+
+static
+struct type *
+decode_function_type (cs, c_type, aux)
+ register struct coff_symbol *cs;
+ unsigned int c_type;
+ register union internal_auxent *aux;
+{
+ if (aux->x_sym.x_tagndx.l == 0)
+ cs->c_naux = 0; /* auxent refers to function, not base type */
+
+ return decode_type (cs, DECREF (c_type), aux);
+}
+
+/* basic C types */
+
+static
+struct type *
+decode_base_type (cs, c_type, aux)
+ register struct coff_symbol *cs;
+ unsigned int c_type;
+ register union internal_auxent *aux;
+{
+ struct type *type;
+
+ switch (c_type)
+ {
+ case T_NULL:
+ /* shows up with "void (*foo)();" structure members */
+ return lookup_fundamental_type (current_objfile, FT_VOID);
+
+#if 0
+/* DGUX actually defines both T_ARG and T_VOID to the same value. */
+#ifdef T_ARG
+ case T_ARG:
+ /* Shows up in DGUX, I think. Not sure where. */
+ return lookup_fundamental_type (current_objfile, FT_VOID); /* shouldn't show up here */
+#endif
+#endif /* 0 */
+
+#ifdef T_VOID
+ case T_VOID:
+ /* Intel 960 COFF has this symbol and meaning. */
+ return lookup_fundamental_type (current_objfile, FT_VOID);
+#endif
+
+ case T_CHAR:
+ return lookup_fundamental_type (current_objfile, FT_CHAR);
+
+ case T_SHORT:
+ return lookup_fundamental_type (current_objfile, FT_SHORT);
+
+ case T_INT:
+ return lookup_fundamental_type (current_objfile, FT_INTEGER);
+
+ case T_LONG:
+ return lookup_fundamental_type (current_objfile, FT_LONG);
+
+ case T_FLOAT:
+ return lookup_fundamental_type (current_objfile, FT_FLOAT);
+
+ case T_DOUBLE:
+ return lookup_fundamental_type (current_objfile, FT_DBL_PREC_FLOAT);
+
+ case T_STRUCT:
+ if (cs->c_naux != 1)
+ {
+ /* anonymous structure type */
+ type = coff_alloc_type (cs->c_symnum);
+ TYPE_CODE (type) = TYPE_CODE_STRUCT;
+ TYPE_NAME (type) = NULL;
+ /* This used to set the tag to "<opaque>". But I think setting it
+ to NULL is right, and the printing code can print it as
+ "struct {...}". */
+ TYPE_TAG_NAME (type) = NULL;
+ INIT_CPLUS_SPECIFIC(type);
+ TYPE_LENGTH (type) = 0;
+ TYPE_FIELDS (type) = 0;
+ TYPE_NFIELDS (type) = 0;
+ }
+ else
+ {
+ type = coff_read_struct_type (cs->c_symnum,
+ aux->x_sym.x_misc.x_lnsz.x_size,
+ aux->x_sym.x_fcnary.x_fcn.x_endndx.l);
+ }
+ return type;
+
+ case T_UNION:
+ if (cs->c_naux != 1)
+ {
+ /* anonymous union type */
+ type = coff_alloc_type (cs->c_symnum);
+ TYPE_NAME (type) = NULL;
+ /* This used to set the tag to "<opaque>". But I think setting it
+ to NULL is right, and the printing code can print it as
+ "union {...}". */
+ TYPE_TAG_NAME (type) = NULL;
+ INIT_CPLUS_SPECIFIC(type);
+ TYPE_LENGTH (type) = 0;
+ TYPE_FIELDS (type) = 0;
+ TYPE_NFIELDS (type) = 0;
+ }
+ else
+ {
+ type = coff_read_struct_type (cs->c_symnum,
+ aux->x_sym.x_misc.x_lnsz.x_size,
+ aux->x_sym.x_fcnary.x_fcn.x_endndx.l);
+ }
+ TYPE_CODE (type) = TYPE_CODE_UNION;
+ return type;
+
+ case T_ENUM:
+ if (cs->c_naux != 1)
+ {
+ /* anonymous enum type */
+ type = coff_alloc_type (cs->c_symnum);
+ TYPE_CODE (type) = TYPE_CODE_ENUM;
+ TYPE_NAME (type) = NULL;
+ /* This used to set the tag to "<opaque>". But I think setting it
+ to NULL is right, and the printing code can print it as
+ "enum {...}". */
+ TYPE_TAG_NAME (type) = NULL;
+ TYPE_LENGTH (type) = 0;
+ TYPE_FIELDS (type) = 0;
+ TYPE_NFIELDS(type) = 0;
+ }
+ else
+ {
+ type = coff_read_enum_type (cs->c_symnum,
+ aux->x_sym.x_misc.x_lnsz.x_size,
+ aux->x_sym.x_fcnary.x_fcn.x_endndx.l);
+ }
+ return type;
+
+ case T_MOE:
+ /* shouldn't show up here */
+ break;
+
+ case T_UCHAR:
+ return lookup_fundamental_type (current_objfile, FT_UNSIGNED_CHAR);
+
+ case T_USHORT:
+ return lookup_fundamental_type (current_objfile, FT_UNSIGNED_SHORT);
+
+ case T_UINT:
+ return lookup_fundamental_type (current_objfile, FT_UNSIGNED_INTEGER);
+
+ case T_ULONG:
+ return lookup_fundamental_type (current_objfile, FT_UNSIGNED_LONG);
+ }
+ complain (&unexpected_type_complaint, cs->c_name);
+ return lookup_fundamental_type (current_objfile, FT_VOID);
+}
+
+/* This page contains subroutines of read_type. */
+
+/* Read the description of a structure (or union type)
+ and return an object describing the type. */
+
+static struct type *
+coff_read_struct_type (index, length, lastsym)
+ int index;
+ int length;
+ int lastsym;
+{
+ struct nextfield
+ {
+ struct nextfield *next;
+ struct field field;
+ };
+
+ register struct type *type;
+ register struct nextfield *list = 0;
+ struct nextfield *new;
+ int nfields = 0;
+ register int n;
+ char *name;
+ struct coff_symbol member_sym;
+ register struct coff_symbol *ms = &member_sym;
+ struct internal_syment sub_sym;
+ union internal_auxent sub_aux;
+ int done = 0;
+
+ type = coff_alloc_type (index);
+ TYPE_CODE (type) = TYPE_CODE_STRUCT;
+ INIT_CPLUS_SPECIFIC(type);
+ TYPE_LENGTH (type) = length;
+
+ while (!done && symnum < lastsym && symnum < nlist_nsyms_global)
+ {
+ read_one_sym (ms, &sub_sym, &sub_aux);
+ name = ms->c_name;
+ name = EXTERNAL_NAME (name, current_objfile->obfd);
+
+ switch (ms->c_sclass)
+ {
+ case C_MOS:
+ case C_MOU:
+
+ /* Get space to record the next field's data. */
+ new = (struct nextfield *) alloca (sizeof (struct nextfield));
+ new->next = list;
+ list = new;
+
+ /* Save the data. */
+ list->field.name = savestring (name, strlen (name));
+ list->field.type = decode_type (ms, ms->c_type, &sub_aux);
+ list->field.bitpos = 8 * ms->c_value;
+ list->field.bitsize = 0;
+ nfields++;
+ break;
+
+ case C_FIELD:
+
+ /* Get space to record the next field's data. */
+ new = (struct nextfield *) alloca (sizeof (struct nextfield));
+ new->next = list;
+ list = new;
+
+ /* Save the data. */
+ list->field.name = savestring (name, strlen (name));
+ list->field.type = decode_type (ms, ms->c_type, &sub_aux);
+ list->field.bitpos = ms->c_value;
+ list->field.bitsize = sub_aux.x_sym.x_misc.x_lnsz.x_size;
+ nfields++;
+ break;
+
+ case C_EOS:
+ done = 1;
+ break;
+ }
+ }
+ /* Now create the vector of fields, and record how big it is. */
+
+ TYPE_NFIELDS (type) = nfields;
+ TYPE_FIELDS (type) = (struct field *)
+ TYPE_ALLOC (type, sizeof (struct field) * nfields);
+
+ /* Copy the saved-up fields into the field vector. */
+
+ for (n = nfields; list; list = list->next)
+ TYPE_FIELD (type, --n) = list->field;
+
+ return type;
+}
+
+/* Read a definition of an enumeration type,
+ and create and return a suitable type object.
+ Also defines the symbols that represent the values of the type. */
+
+/* ARGSUSED */
+static struct type *
+coff_read_enum_type (index, length, lastsym)
+ int index;
+ int length;
+ int lastsym;
+{
+ register struct symbol *sym;
+ register struct type *type;
+ int nsyms = 0;
+ int done = 0;
+ struct pending **symlist;
+ struct coff_symbol member_sym;
+ register struct coff_symbol *ms = &member_sym;
+ struct internal_syment sub_sym;
+ union internal_auxent sub_aux;
+ struct pending *osyms, *syms;
+ int o_nsyms;
+ register int n;
+ char *name;
+
+ type = coff_alloc_type (index);
+ if (within_function)
+ symlist = &local_symbols;
+ else
+ symlist = &file_symbols;
+ osyms = *symlist;
+ o_nsyms = osyms ? osyms->nsyms : 0;
+
+ while (!done && symnum < lastsym && symnum < nlist_nsyms_global)
+ {
+ read_one_sym (ms, &sub_sym, &sub_aux);
+ name = ms->c_name;
+ name = EXTERNAL_NAME (name, current_objfile->obfd);
+
+ switch (ms->c_sclass)
+ {
+ case C_MOE:
+ sym = (struct symbol *) xmalloc (sizeof (struct symbol));
+ memset (sym, 0, sizeof (struct symbol));
+
+ SYMBOL_NAME (sym) = savestring (name, strlen (name));
+ SYMBOL_CLASS (sym) = LOC_CONST;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ SYMBOL_VALUE (sym) = ms->c_value;
+ add_symbol_to_list (sym, symlist);
+ nsyms++;
+ break;
+
+ case C_EOS:
+ /* Sometimes the linker (on 386/ix 2.0.2 at least) screws
+ up the count of how many symbols to read. So stop
+ on .eos. */
+ done = 1;
+ break;
+ }
+ }
+
+ /* Now fill in the fields of the type-structure. */
+
+ if (length > 0)
+ TYPE_LENGTH (type) = length;
+ else
+ TYPE_LENGTH (type) = TARGET_INT_BIT / TARGET_CHAR_BIT; /* Assume ints */
+ TYPE_CODE (type) = TYPE_CODE_ENUM;
+ TYPE_NFIELDS (type) = nsyms;
+ TYPE_FIELDS (type) = (struct field *)
+ TYPE_ALLOC (type, sizeof (struct field) * nsyms);
+
+ /* Find the symbols for the values and put them into the type.
+ The symbols can be found in the symlist that we put them on
+ to cause them to be defined. osyms contains the old value
+ of that symlist; everything up to there was defined by us. */
+ /* Note that we preserve the order of the enum constants, so
+ that in something like "enum {FOO, LAST_THING=FOO}" we print
+ FOO, not LAST_THING. */
+
+ for (syms = *symlist, n = 0; syms; syms = syms->next)
+ {
+ int j = 0;
+ if (syms == osyms)
+ j = o_nsyms;
+ for (; j < syms->nsyms; j++,n++)
+ {
+ struct symbol *xsym = syms->symbol[j];
+ SYMBOL_TYPE (xsym) = type;
+ TYPE_FIELD_NAME (type, n) = SYMBOL_NAME (xsym);
+ TYPE_FIELD_VALUE (type, n) = 0;
+ TYPE_FIELD_BITPOS (type, n) = SYMBOL_VALUE (xsym);
+ TYPE_FIELD_BITSIZE (type, n) = 0;
+ }
+ if (syms == osyms)
+ break;
+ }
+
+#if 0
+ /* This screws up perfectly good C programs with enums. FIXME. */
+ /* Is this Modula-2's BOOLEAN type? Flag it as such if so. */
+ if(TYPE_NFIELDS(type) == 2 &&
+ ((STREQ(TYPE_FIELD_NAME(type,0),"TRUE") &&
+ STREQ(TYPE_FIELD_NAME(type,1),"FALSE")) ||
+ (STREQ(TYPE_FIELD_NAME(type,1),"TRUE") &&
+ STREQ(TYPE_FIELD_NAME(type,0),"FALSE"))))
+ TYPE_CODE(type) = TYPE_CODE_BOOL;
+#endif
+ return type;
+}
+
+struct section_offsets *
+coff_symfile_offsets (objfile, addr)
+ struct objfile *objfile;
+ CORE_ADDR addr;
+{
+ struct section_offsets *section_offsets;
+ int i;
+
+ section_offsets = (struct section_offsets *)
+ obstack_alloc (&objfile -> psymbol_obstack,
+ sizeof (struct section_offsets) +
+ sizeof (section_offsets->offsets) * (SECT_OFF_MAX-1));
+
+ for (i = 0; i < SECT_OFF_MAX; i++)
+ ANOFFSET (section_offsets, i) = addr;
+
+ return section_offsets;
+}
+
+/* Register our ability to parse symbols for coff BFD files */
+
+static struct sym_fns coff_sym_fns =
+{
+ "coff", /* sym_name: name or name prefix of BFD target type */
+ 4, /* sym_namelen: number of significant sym_name chars */
+ coff_new_init, /* sym_new_init: init anything gbl to entire symtab */
+ coff_symfile_init, /* sym_init: read initial info, setup for sym_read() */
+ coff_symfile_read, /* sym_read: read a symbol file into symtab */
+ coff_symfile_finish, /* sym_finish: finished with file, cleanup */
+ coff_symfile_offsets, /* sym_offsets: xlate external to internal form */
+ NULL /* next: pointer to next struct sym_fns */
+};
+
+void
+_initialize_coffread ()
+{
+ add_symtab_fns(&coff_sym_fns);
+}
diff --git a/gnu/usr.bin/gdb/gdb/command.c b/gnu/usr.bin/gdb/gdb/command.c
new file mode 100644
index 0000000..abc2d84
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/command.c
@@ -0,0 +1,1310 @@
+/* Handle lists of commands, their decoding and documentation, for GDB.
+ Copyright 1986, 1989, 1990, 1991 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "gdbcmd.h"
+#include "symtab.h"
+#include "value.h"
+#include <ctype.h>
+#include <string.h>
+
+/* Prototypes for local functions */
+
+static void
+undef_cmd_error PARAMS ((char *, char *));
+
+static void
+show_user PARAMS ((char *, int));
+
+static void
+show_user_1 PARAMS ((struct cmd_list_element *, FILE *));
+
+static void
+make_command PARAMS ((char *, int));
+
+static void
+shell_escape PARAMS ((char *, int));
+
+static int
+parse_binary_operation PARAMS ((char *));
+
+static void
+print_doc_line PARAMS ((FILE *, char *));
+
+/* Add element named NAME.
+ CLASS is the top level category into which commands are broken down
+ for "help" purposes.
+ FUN should be the function to execute the command;
+ it will get a character string as argument, with leading
+ and trailing blanks already eliminated.
+
+ DOC is a documentation string for the command.
+ Its first line should be a complete sentence.
+ It should start with ? for a command that is an abbreviation
+ or with * for a command that most users don't need to know about.
+
+ Add this command to command list *LIST. */
+
+struct cmd_list_element *
+add_cmd (name, class, fun, doc, list)
+ char *name;
+ enum command_class class;
+ void (*fun) PARAMS ((char *, int));
+ char *doc;
+ struct cmd_list_element **list;
+{
+ register struct cmd_list_element *c
+ = (struct cmd_list_element *) xmalloc (sizeof (struct cmd_list_element));
+
+ delete_cmd (name, list);
+ c->next = *list;
+ c->name = name;
+ c->class = class;
+ c->function.cfunc = fun;
+ c->doc = doc;
+ c->prefixlist = 0;
+ c->prefixname = (char *)NULL;
+ c->allow_unknown = 0;
+ c->hook = 0;
+ c->hookee = 0;
+ c->cmd_pointer = 0;
+ c->abbrev_flag = 0;
+ c->type = not_set_cmd;
+ c->completer = make_symbol_completion_list;
+ c->var = 0;
+ c->var_type = var_boolean;
+ c->user_commands = 0;
+ *list = c;
+ return c;
+}
+
+/* Same as above, except that the abbrev_flag is set. */
+
+#if 0 /* Currently unused */
+
+struct cmd_list_element *
+add_abbrev_cmd (name, class, fun, doc, list)
+ char *name;
+ enum command_class class;
+ void (*fun) PARAMS ((char *, int));
+ char *doc;
+ struct cmd_list_element **list;
+{
+ register struct cmd_list_element *c
+ = add_cmd (name, class, fun, doc, list);
+
+ c->abbrev_flag = 1;
+ return c;
+}
+
+#endif
+
+struct cmd_list_element *
+add_alias_cmd (name, oldname, class, abbrev_flag, list)
+ char *name;
+ char *oldname;
+ enum command_class class;
+ int abbrev_flag;
+ struct cmd_list_element **list;
+{
+ /* Must do this since lookup_cmd tries to side-effect its first arg */
+ char *copied_name;
+ register struct cmd_list_element *old;
+ register struct cmd_list_element *c;
+ copied_name = (char *) alloca (strlen (oldname) + 1);
+ strcpy (copied_name, oldname);
+ old = lookup_cmd (&copied_name, *list, "", 1, 1);
+
+ if (old == 0)
+ {
+ delete_cmd (name, list);
+ return 0;
+ }
+
+ c = add_cmd (name, class, old->function.cfunc, old->doc, list);
+ c->prefixlist = old->prefixlist;
+ c->prefixname = old->prefixname;
+ c->allow_unknown = old->allow_unknown;
+ c->abbrev_flag = abbrev_flag;
+ c->cmd_pointer = old;
+ return c;
+}
+
+/* Like add_cmd but adds an element for a command prefix:
+ a name that should be followed by a subcommand to be looked up
+ in another command list. PREFIXLIST should be the address
+ of the variable containing that list. */
+
+struct cmd_list_element *
+add_prefix_cmd (name, class, fun, doc, prefixlist, prefixname,
+ allow_unknown, list)
+ char *name;
+ enum command_class class;
+ void (*fun) PARAMS ((char *, int));
+ char *doc;
+ struct cmd_list_element **prefixlist;
+ char *prefixname;
+ int allow_unknown;
+ struct cmd_list_element **list;
+{
+ register struct cmd_list_element *c = add_cmd (name, class, fun, doc, list);
+ c->prefixlist = prefixlist;
+ c->prefixname = prefixname;
+ c->allow_unknown = allow_unknown;
+ return c;
+}
+
+/* Like add_prefix_cmd but sets the abbrev_flag on the new command. */
+
+struct cmd_list_element *
+add_abbrev_prefix_cmd (name, class, fun, doc, prefixlist, prefixname,
+ allow_unknown, list)
+ char *name;
+ enum command_class class;
+ void (*fun) PARAMS ((char *, int));
+ char *doc;
+ struct cmd_list_element **prefixlist;
+ char *prefixname;
+ int allow_unknown;
+ struct cmd_list_element **list;
+{
+ register struct cmd_list_element *c = add_cmd (name, class, fun, doc, list);
+ c->prefixlist = prefixlist;
+ c->prefixname = prefixname;
+ c->allow_unknown = allow_unknown;
+ c->abbrev_flag = 1;
+ return c;
+}
+
+/* ARGSUSED */
+void
+not_just_help_class_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+}
+
+/* Add element named NAME to command list LIST (the list for set
+ or some sublist thereof).
+ CLASS is as in add_cmd.
+ VAR_TYPE is the kind of thing we are setting.
+ VAR is address of the variable being controlled by this command.
+ DOC is the documentation string. */
+
+struct cmd_list_element *
+add_set_cmd (name, class, var_type, var, doc, list)
+ char *name;
+ enum command_class class;
+ var_types var_type;
+ char *var;
+ char *doc;
+ struct cmd_list_element **list;
+{
+ /* For set/show, we have to call do_setshow_command
+ differently than an ordinary function (take commandlist as
+ well as arg), so the function field isn't helpful. However,
+ function == NULL means that it's a help class, so set the function
+ to not_just_help_class_command. */
+ struct cmd_list_element *c
+ = add_cmd (name, class, not_just_help_class_command, doc, list);
+
+ c->type = set_cmd;
+ c->var_type = var_type;
+ c->var = var;
+ return c;
+}
+
+/* Where SETCMD has already been added, add the corresponding show
+ command to LIST and return a pointer to it. */
+struct cmd_list_element *
+add_show_from_set (setcmd, list)
+ struct cmd_list_element *setcmd;
+ struct cmd_list_element **list;
+{
+ struct cmd_list_element *showcmd =
+ (struct cmd_list_element *) xmalloc (sizeof (struct cmd_list_element));
+
+ memcpy (showcmd, setcmd, sizeof (struct cmd_list_element));
+ delete_cmd (showcmd->name, list);
+ showcmd->type = show_cmd;
+
+ /* Replace "set " at start of docstring with "show ". */
+ if (setcmd->doc[0] == 'S' && setcmd->doc[1] == 'e'
+ && setcmd->doc[2] == 't' && setcmd->doc[3] == ' ')
+ showcmd->doc = concat ("Show ", setcmd->doc + 4, NULL);
+ else
+ fprintf (stderr, "GDB internal error: Bad docstring for set command\n");
+
+ showcmd->next = *list;
+ *list = showcmd;
+ return showcmd;
+}
+
+/* Remove the command named NAME from the command list. */
+
+void
+delete_cmd (name, list)
+ char *name;
+ struct cmd_list_element **list;
+{
+ register struct cmd_list_element *c;
+ struct cmd_list_element *p;
+
+ while (*list && STREQ ((*list)->name, name))
+ {
+ if ((*list)->hookee)
+ (*list)->hookee->hook = 0; /* Hook slips out of its mouth */
+ p = (*list)->next;
+ free ((PTR)*list);
+ *list = p;
+ }
+
+ if (*list)
+ for (c = *list; c->next;)
+ {
+ if (STREQ (c->next->name, name))
+ {
+ if (c->next->hookee)
+ c->next->hookee->hook = 0; /* hooked cmd gets away. */
+ p = c->next->next;
+ free ((PTR)c->next);
+ c->next = p;
+ }
+ else
+ c = c->next;
+ }
+}
+
+/* This command really has to deal with two things:
+ * 1) I want documentation on *this string* (usually called by
+ * "help commandname").
+ * 2) I want documentation on *this list* (usually called by
+ * giving a command that requires subcommands. Also called by saying
+ * just "help".)
+ *
+ * I am going to split this into two seperate comamnds, help_cmd and
+ * help_list.
+ */
+
+void
+help_cmd (command, stream)
+ char *command;
+ FILE *stream;
+{
+ struct cmd_list_element *c;
+ extern struct cmd_list_element *cmdlist;
+
+ if (!command)
+ {
+ help_list (cmdlist, "", all_classes, stream);
+ return;
+ }
+
+ c = lookup_cmd (&command, cmdlist, "", 0, 0);
+
+ if (c == 0)
+ return;
+
+ /* There are three cases here.
+ If c->prefixlist is nonzero, we have a prefix command.
+ Print its documentation, then list its subcommands.
+
+ If c->function is nonzero, we really have a command.
+ Print its documentation and return.
+
+ If c->function is zero, we have a class name.
+ Print its documentation (as if it were a command)
+ and then set class to the number of this class
+ so that the commands in the class will be listed. */
+
+ fputs_filtered (c->doc, stream);
+ fputs_filtered ("\n", stream);
+
+ if (c->prefixlist == 0 && c->function.cfunc != NULL)
+ return;
+ fprintf_filtered (stream, "\n");
+
+ /* If this is a prefix command, print it's subcommands */
+ if (c->prefixlist)
+ help_list (*c->prefixlist, c->prefixname, all_commands, stream);
+
+ /* If this is a class name, print all of the commands in the class */
+ if (c->function.cfunc == NULL)
+ help_list (cmdlist, "", c->class, stream);
+
+ if (c->hook)
+ fprintf_filtered (stream, "\nThis command has a hook defined: %s\n",
+ c->hook->name);
+}
+
+/*
+ * Get a specific kind of help on a command list.
+ *
+ * LIST is the list.
+ * CMDTYPE is the prefix to use in the title string.
+ * CLASS is the class with which to list the nodes of this list (see
+ * documentation for help_cmd_list below), As usual, ALL_COMMANDS for
+ * everything, ALL_CLASSES for just classes, and non-negative for only things
+ * in a specific class.
+ * and STREAM is the output stream on which to print things.
+ * If you call this routine with a class >= 0, it recurses.
+ */
+void
+help_list (list, cmdtype, class, stream)
+ struct cmd_list_element *list;
+ char *cmdtype;
+ enum command_class class;
+ FILE *stream;
+{
+ int len;
+ char *cmdtype1, *cmdtype2;
+
+ /* If CMDTYPE is "foo ", CMDTYPE1 gets " foo" and CMDTYPE2 gets "foo sub" */
+ len = strlen (cmdtype);
+ cmdtype1 = (char *) alloca (len + 1);
+ cmdtype1[0] = 0;
+ cmdtype2 = (char *) alloca (len + 4);
+ cmdtype2[0] = 0;
+ if (len)
+ {
+ cmdtype1[0] = ' ';
+ strncpy (cmdtype1 + 1, cmdtype, len - 1);
+ cmdtype1[len] = 0;
+ strncpy (cmdtype2, cmdtype, len - 1);
+ strcpy (cmdtype2 + len - 1, " sub");
+ }
+
+ if (class == all_classes)
+ fprintf_filtered (stream, "List of classes of %scommands:\n\n", cmdtype2);
+ else
+ fprintf_filtered (stream, "List of %scommands:\n\n", cmdtype2);
+
+ help_cmd_list (list, class, cmdtype, (int)class >= 0, stream);
+
+ if (class == all_classes)
+ fprintf_filtered (stream, "\n\
+Type \"help%s\" followed by a class name for a list of commands in that class.",
+ cmdtype1);
+
+ fprintf_filtered (stream, "\n\
+Type \"help%s\" followed by %scommand name for full documentation.\n\
+Command name abbreviations are allowed if unambiguous.\n",
+ cmdtype1, cmdtype2);
+}
+
+/* Print only the first line of STR on STREAM. */
+static void
+print_doc_line (stream, str)
+ FILE *stream;
+ char *str;
+{
+ static char *line_buffer = 0;
+ static int line_size;
+ register char *p;
+
+ if (!line_buffer)
+ {
+ line_size = 80;
+ line_buffer = (char *) xmalloc (line_size);
+ }
+
+ p = str;
+ while (*p && *p != '\n' && *p != '.' && *p != ',')
+ p++;
+ if (p - str > line_size - 1)
+ {
+ line_size = p - str + 1;
+ free ((PTR)line_buffer);
+ line_buffer = (char *) xmalloc (line_size);
+ }
+ strncpy (line_buffer, str, p - str);
+ line_buffer[p - str] = '\0';
+ if (islower (line_buffer[0]))
+ line_buffer[0] = toupper (line_buffer[0]);
+ fputs_filtered (line_buffer, stream);
+}
+
+/*
+ * Implement a help command on command list LIST.
+ * RECURSE should be non-zero if this should be done recursively on
+ * all sublists of LIST.
+ * PREFIX is the prefix to print before each command name.
+ * STREAM is the stream upon which the output should be written.
+ * CLASS should be:
+ * A non-negative class number to list only commands in that
+ * class.
+ * ALL_COMMANDS to list all commands in list.
+ * ALL_CLASSES to list all classes in list.
+ *
+ * Note that RECURSE will be active on *all* sublists, not just the
+ * ones selected by the criteria above (ie. the selection mechanism
+ * is at the low level, not the high-level).
+ */
+void
+help_cmd_list (list, class, prefix, recurse, stream)
+ struct cmd_list_element *list;
+ enum command_class class;
+ char *prefix;
+ int recurse;
+ FILE *stream;
+{
+ register struct cmd_list_element *c;
+
+ for (c = list; c; c = c->next)
+ {
+ if (c->abbrev_flag == 0 &&
+ (class == all_commands
+ || (class == all_classes && c->function.cfunc == NULL)
+ || (class == c->class && c->function.cfunc != NULL)))
+ {
+ fprintf_filtered (stream, "%s%s -- ", prefix, c->name);
+ print_doc_line (stream, c->doc);
+ fputs_filtered ("\n", stream);
+ }
+ if (recurse
+ && c->prefixlist != 0
+ && c->abbrev_flag == 0)
+ help_cmd_list (*c->prefixlist, class, c->prefixname, 1, stream);
+ }
+}
+
+/* This routine takes a line of TEXT and a CLIST in which to start the
+ lookup. When it returns it will have incremented the text pointer past
+ the section of text it matched, set *RESULT_LIST to point to the list in
+ which the last word was matched, and will return a pointer to the cmd
+ list element which the text matches. It will return NULL if no match at
+ all was possible. It will return -1 (cast appropriately, ick) if ambigous
+ matches are possible; in this case *RESULT_LIST will be set to point to
+ the list in which there are ambiguous choices (and *TEXT will be set to
+ the ambiguous text string).
+
+ If the located command was an abbreviation, this routine returns the base
+ command of the abbreviation.
+
+ It does no error reporting whatsoever; control will always return
+ to the superior routine.
+
+ In the case of an ambiguous return (-1), *RESULT_LIST will be set to point
+ at the prefix_command (ie. the best match) *or* (special case) will be NULL
+ if no prefix command was ever found. For example, in the case of "info a",
+ "info" matches without ambiguity, but "a" could be "args" or "address", so
+ *RESULT_LIST is set to the cmd_list_element for "info". So in this case
+ RESULT_LIST should not be interpeted as a pointer to the beginning of a
+ list; it simply points to a specific command. In the case of an ambiguous
+ return *TEXT is advanced past the last non-ambiguous prefix (e.g.
+ "info t" can be "info types" or "info target"; upon return *TEXT has been
+ advanced past "info ").
+
+ If RESULT_LIST is NULL, don't set *RESULT_LIST (but don't otherwise
+ affect the operation).
+
+ This routine does *not* modify the text pointed to by TEXT.
+
+ If IGNORE_HELP_CLASSES is nonzero, ignore any command list elements which
+ are actually help classes rather than commands (i.e. the function field of
+ the struct cmd_list_element is NULL). */
+
+struct cmd_list_element *
+lookup_cmd_1 (text, clist, result_list, ignore_help_classes)
+ char **text;
+ struct cmd_list_element *clist, **result_list;
+ int ignore_help_classes;
+{
+ char *p, *command;
+ int len, tmp, nfound;
+ struct cmd_list_element *found, *c;
+
+ while (**text == ' ' || **text == '\t')
+ (*text)++;
+
+ /* Treating underscores as part of command words is important
+ so that "set args_foo()" doesn't get interpreted as
+ "set args _foo()". */
+ for (p = *text;
+ *p && (isalnum(*p) || *p == '-' || *p == '_');
+ p++)
+ ;
+
+ /* If nothing but whitespace, return 0. */
+ if (p == *text)
+ return 0;
+
+ len = p - *text;
+
+ /* *text and p now bracket the first command word to lookup (and
+ it's length is len). We copy this into a local temporary,
+ converting to lower case as we go. */
+
+ command = (char *) alloca (len + 1);
+ for (tmp = 0; tmp < len; tmp++)
+ {
+ char x = (*text)[tmp];
+ command[tmp] = isupper(x) ? tolower(x) : x;
+ }
+ command[len] = '\0';
+
+ /* Look it up. */
+ found = 0;
+ nfound = 0;
+ for (c = clist; c; c = c->next)
+ if (!strncmp (command, c->name, len)
+ && (!ignore_help_classes || c->function.cfunc))
+ {
+ found = c;
+ nfound++;
+ if (c->name[len] == '\0')
+ {
+ nfound = 1;
+ break;
+ }
+ }
+
+ /* If nothing matches, we have a simple failure. */
+ if (nfound == 0)
+ return 0;
+
+ if (nfound > 1)
+ {
+ if (result_list != NULL)
+ /* Will be modified in calling routine
+ if we know what the prefix command is. */
+ *result_list = 0;
+ return (struct cmd_list_element *) -1; /* Ambiguous. */
+ }
+
+ /* We've matched something on this list. Move text pointer forward. */
+
+ *text = p;
+
+ /* If this was an abbreviation, use the base command instead. */
+
+ if (found->cmd_pointer)
+ found = found->cmd_pointer;
+
+ /* If we found a prefix command, keep looking. */
+
+ if (found->prefixlist)
+ {
+ c = lookup_cmd_1 (text, *found->prefixlist, result_list,
+ ignore_help_classes);
+ if (!c)
+ {
+ /* Didn't find anything; this is as far as we got. */
+ if (result_list != NULL)
+ *result_list = clist;
+ return found;
+ }
+ else if (c == (struct cmd_list_element *) -1)
+ {
+ /* We've gotten this far properley, but the next step
+ is ambiguous. We need to set the result list to the best
+ we've found (if an inferior hasn't already set it). */
+ if (result_list != NULL)
+ if (!*result_list)
+ /* This used to say *result_list = *found->prefixlist
+ If that was correct, need to modify the documentation
+ at the top of this function to clarify what is supposed
+ to be going on. */
+ *result_list = found;
+ return c;
+ }
+ else
+ {
+ /* We matched! */
+ return c;
+ }
+ }
+ else
+ {
+ if (result_list != NULL)
+ *result_list = clist;
+ return found;
+ }
+}
+
+/* All this hair to move the space to the front of cmdtype */
+
+static void
+undef_cmd_error (cmdtype, q)
+ char *cmdtype, *q;
+{
+ error ("Undefined %scommand: \"%s\". Try \"help%s%.*s\".",
+ cmdtype,
+ q,
+ *cmdtype? " ": "",
+ strlen(cmdtype)-1,
+ cmdtype);
+}
+
+/* Look up the contents of *LINE as a command in the command list LIST.
+ LIST is a chain of struct cmd_list_element's.
+ If it is found, return the struct cmd_list_element for that command
+ and update *LINE to point after the command name, at the first argument.
+ If not found, call error if ALLOW_UNKNOWN is zero
+ otherwise (or if error returns) return zero.
+ Call error if specified command is ambiguous,
+ unless ALLOW_UNKNOWN is negative.
+ CMDTYPE precedes the word "command" in the error message.
+
+ If INGNORE_HELP_CLASSES is nonzero, ignore any command list
+ elements which are actually help classes rather than commands (i.e.
+ the function field of the struct cmd_list_element is 0). */
+
+struct cmd_list_element *
+lookup_cmd (line, list, cmdtype, allow_unknown, ignore_help_classes)
+ char **line;
+ struct cmd_list_element *list;
+ char *cmdtype;
+ int allow_unknown;
+ int ignore_help_classes;
+{
+ struct cmd_list_element *last_list = 0;
+ struct cmd_list_element *c =
+ lookup_cmd_1 (line, list, &last_list, ignore_help_classes);
+ char *ptr = (*line) + strlen (*line) - 1;
+
+ /* Clear off trailing whitespace. */
+ while (ptr >= *line && (*ptr == ' ' || *ptr == '\t'))
+ ptr--;
+ *(ptr + 1) = '\0';
+
+ if (!c)
+ {
+ if (!allow_unknown)
+ {
+ if (!*line)
+ error ("Lack of needed %scommand", cmdtype);
+ else
+ {
+ char *p = *line, *q;
+
+ while (isalnum(*p) || *p == '-')
+ p++;
+
+ q = (char *) alloca (p - *line + 1);
+ strncpy (q, *line, p - *line);
+ q[p-*line] = '\0';
+ undef_cmd_error (cmdtype, q);
+ }
+ }
+ else
+ return 0;
+ }
+ else if (c == (struct cmd_list_element *) -1)
+ {
+ /* Ambigous. Local values should be off prefixlist or called
+ values. */
+ int local_allow_unknown = (last_list ? last_list->allow_unknown :
+ allow_unknown);
+ char *local_cmdtype = last_list ? last_list->prefixname : cmdtype;
+ struct cmd_list_element *local_list =
+ (last_list ? *(last_list->prefixlist) : list);
+
+ if (local_allow_unknown < 0)
+ {
+ if (last_list)
+ return last_list; /* Found something. */
+ else
+ return 0; /* Found nothing. */
+ }
+ else
+ {
+ /* Report as error. */
+ int amb_len;
+ char ambbuf[100];
+
+ for (amb_len = 0;
+ ((*line)[amb_len] && (*line)[amb_len] != ' '
+ && (*line)[amb_len] != '\t');
+ amb_len++)
+ ;
+
+ ambbuf[0] = 0;
+ for (c = local_list; c; c = c->next)
+ if (!strncmp (*line, c->name, amb_len))
+ {
+ if (strlen (ambbuf) + strlen (c->name) + 6 < (int)sizeof ambbuf)
+ {
+ if (strlen (ambbuf))
+ strcat (ambbuf, ", ");
+ strcat (ambbuf, c->name);
+ }
+ else
+ {
+ strcat (ambbuf, "..");
+ break;
+ }
+ }
+ error ("Ambiguous %scommand \"%s\": %s.", local_cmdtype,
+ *line, ambbuf);
+ return 0; /* lint */
+ }
+ }
+ else
+ {
+ /* We've got something. It may still not be what the caller
+ wants (if this command *needs* a subcommand). */
+ while (**line == ' ' || **line == '\t')
+ (*line)++;
+
+ if (c->prefixlist && **line && !c->allow_unknown)
+ undef_cmd_error (c->prefixname, *line);
+
+ /* Seems to be what he wants. Return it. */
+ return c;
+ }
+ return 0;
+}
+
+#if 0
+/* Look up the contents of *LINE as a command in the command list LIST.
+ LIST is a chain of struct cmd_list_element's.
+ If it is found, return the struct cmd_list_element for that command
+ and update *LINE to point after the command name, at the first argument.
+ If not found, call error if ALLOW_UNKNOWN is zero
+ otherwise (or if error returns) return zero.
+ Call error if specified command is ambiguous,
+ unless ALLOW_UNKNOWN is negative.
+ CMDTYPE precedes the word "command" in the error message. */
+
+struct cmd_list_element *
+lookup_cmd (line, list, cmdtype, allow_unknown)
+ char **line;
+ struct cmd_list_element *list;
+ char *cmdtype;
+ int allow_unknown;
+{
+ register char *p;
+ register struct cmd_list_element *c, *found;
+ int nfound;
+ char ambbuf[100];
+ char *processed_cmd;
+ int i, cmd_len;
+
+ /* Skip leading whitespace. */
+
+ while (**line == ' ' || **line == '\t')
+ (*line)++;
+
+ /* Clear out trailing whitespace. */
+
+ p = *line + strlen (*line);
+ while (p != *line && (p[-1] == ' ' || p[-1] == '\t'))
+ p--;
+ *p = 0;
+
+ /* Find end of command name. */
+
+ p = *line;
+ while (*p == '-' || isalnum(*p))
+ p++;
+
+ /* Look up the command name.
+ If exact match, keep that.
+ Otherwise, take command abbreviated, if unique. Note that (in my
+ opinion) a null string does *not* indicate ambiguity; simply the
+ end of the argument. */
+
+ if (p == *line)
+ {
+ if (!allow_unknown)
+ error ("Lack of needed %scommand", cmdtype);
+ return 0;
+ }
+
+ /* Copy over to a local buffer, converting to lowercase on the way.
+ This is in case the command being parsed is a subcommand which
+ doesn't match anything, and that's ok. We want the original
+ untouched for the routine of the original command. */
+
+ processed_cmd = (char *) alloca (p - *line + 1);
+ for (cmd_len = 0; cmd_len < p - *line; cmd_len++)
+ {
+ char x = (*line)[cmd_len];
+ if (isupper(x))
+ processed_cmd[cmd_len] = tolower(x);
+ else
+ processed_cmd[cmd_len] = x;
+ }
+ processed_cmd[cmd_len] = '\0';
+
+ /* Check all possibilities in the current command list. */
+ found = 0;
+ nfound = 0;
+ for (c = list; c; c = c->next)
+ {
+ if (!strncmp (processed_cmd, c->name, cmd_len))
+ {
+ found = c;
+ nfound++;
+ if (c->name[cmd_len] == 0)
+ {
+ nfound = 1;
+ break;
+ }
+ }
+ }
+
+ /* Report error for undefined command name. */
+
+ if (nfound != 1)
+ {
+ if (nfound > 1 && allow_unknown >= 0)
+ {
+ ambbuf[0] = 0;
+ for (c = list; c; c = c->next)
+ if (!strncmp (processed_cmd, c->name, cmd_len))
+ {
+ if (strlen (ambbuf) + strlen (c->name) + 6 < sizeof ambbuf)
+ {
+ if (strlen (ambbuf))
+ strcat (ambbuf, ", ");
+ strcat (ambbuf, c->name);
+ }
+ else
+ {
+ strcat (ambbuf, "..");
+ break;
+ }
+ }
+ error ("Ambiguous %scommand \"%s\": %s.", cmdtype,
+ processed_cmd, ambbuf);
+ }
+ else if (!allow_unknown)
+ error ("Undefined %scommand: \"%s\".", cmdtype, processed_cmd);
+ return 0;
+ }
+
+ /* Skip whitespace before the argument. */
+
+ while (*p == ' ' || *p == '\t') p++;
+ *line = p;
+
+ if (found->prefixlist && *p)
+ {
+ c = lookup_cmd (line, *found->prefixlist, found->prefixname,
+ found->allow_unknown);
+ if (c)
+ return c;
+ }
+
+ return found;
+}
+#endif
+
+/* Helper function for SYMBOL_COMPLETION_FUNCTION. */
+
+/* Return a vector of char pointers which point to the different
+ possible completions in LIST of TEXT.
+
+ WORD points in the same buffer as TEXT, and completions should be
+ returned relative to this position. For example, suppose TEXT is "foo"
+ and we want to complete to "foobar". If WORD is "oo", return
+ "oobar"; if WORD is "baz/foo", return "baz/foobar". */
+
+char **
+complete_on_cmdlist (list, text, word)
+ struct cmd_list_element *list;
+ char *text;
+ char *word;
+{
+ struct cmd_list_element *ptr;
+ char **matchlist;
+ int sizeof_matchlist;
+ int matches;
+ int textlen = strlen (text);
+
+ sizeof_matchlist = 10;
+ matchlist = (char **) xmalloc (sizeof_matchlist * sizeof (char *));
+ matches = 0;
+
+ for (ptr = list; ptr; ptr = ptr->next)
+ if (!strncmp (ptr->name, text, textlen)
+ && !ptr->abbrev_flag
+ && (ptr->function.cfunc
+ || ptr->prefixlist))
+ {
+ if (matches == sizeof_matchlist)
+ {
+ sizeof_matchlist *= 2;
+ matchlist = (char **) xrealloc ((char *)matchlist,
+ (sizeof_matchlist
+ * sizeof (char *)));
+ }
+
+ matchlist[matches] = (char *)
+ xmalloc (strlen (word) + strlen (ptr->name) + 1);
+ if (word == text)
+ strcpy (matchlist[matches], ptr->name);
+ else if (word > text)
+ {
+ /* Return some portion of ptr->name. */
+ strcpy (matchlist[matches], ptr->name + (word - text));
+ }
+ else
+ {
+ /* Return some of text plus ptr->name. */
+ strncpy (matchlist[matches], word, text - word);
+ matchlist[matches][text - word] = '\0';
+ strcat (matchlist[matches], ptr->name);
+ }
+ ++matches;
+ }
+
+ if (matches == 0)
+ {
+ free ((PTR)matchlist);
+ matchlist = 0;
+ }
+ else
+ {
+ matchlist = (char **) xrealloc ((char *)matchlist, ((matches + 1)
+ * sizeof (char *)));
+ matchlist[matches] = (char *) 0;
+ }
+
+ return matchlist;
+}
+
+static int
+parse_binary_operation (arg)
+ char *arg;
+{
+ int length;
+
+ if (!arg || !*arg)
+ return 1;
+
+ length = strlen (arg);
+
+ while (arg[length - 1] == ' ' || arg[length - 1] == '\t')
+ length--;
+
+ if (!strncmp (arg, "on", length)
+ || !strncmp (arg, "1", length)
+ || !strncmp (arg, "yes", length))
+ return 1;
+ else
+ if (!strncmp (arg, "off", length)
+ || !strncmp (arg, "0", length)
+ || !strncmp (arg, "no", length))
+ return 0;
+ else
+ {
+ error ("\"on\" or \"off\" expected.");
+ return 0;
+ }
+}
+
+/* Do a "set" or "show" command. ARG is NULL if no argument, or the text
+ of the argument, and FROM_TTY is nonzero if this command is being entered
+ directly by the user (i.e. these are just like any other
+ command). C is the command list element for the command. */
+void
+do_setshow_command (arg, from_tty, c)
+ char *arg;
+ int from_tty;
+ struct cmd_list_element *c;
+{
+ if (c->type == set_cmd)
+ {
+ switch (c->var_type)
+ {
+ case var_string:
+ {
+ char *new;
+ char *p;
+ char *q;
+ int ch;
+
+ if (arg == NULL)
+ arg = "";
+ new = (char *) xmalloc (strlen (arg) + 2);
+ p = arg; q = new;
+ while ((ch = *p++) != '\000')
+ {
+ if (ch == '\\')
+ {
+ /* \ at end of argument is used after spaces
+ so they won't be lost. */
+ if (*p == 0)
+ break;
+ ch = parse_escape (&p);
+ if (ch == 0)
+ break; /* C loses */
+ else if (ch > 0)
+ *q++ = ch;
+ }
+ else
+ *q++ = ch;
+ }
+ if (*(p - 1) != '\\')
+ *q++ = ' ';
+ *q++ = '\0';
+ new = (char *) xrealloc (new, q - new);
+ if (*(char **)c->var != NULL)
+ free (*(char **)c->var);
+ *(char **) c->var = new;
+ }
+ break;
+ case var_string_noescape:
+ if (arg == NULL)
+ arg = "";
+ if (*(char **)c->var != NULL)
+ free (*(char **)c->var);
+ *(char **) c->var = savestring (arg, strlen (arg));
+ break;
+ case var_filename:
+ if (arg == NULL)
+ error_no_arg ("filename to set it to.");
+ if (*(char **)c->var != NULL)
+ free (*(char **)c->var);
+ *(char **)c->var = tilde_expand (arg);
+ break;
+ case var_boolean:
+ *(int *) c->var = parse_binary_operation (arg);
+ break;
+ case var_uinteger:
+ if (arg == NULL)
+ error_no_arg ("integer to set it to.");
+ *(unsigned int *) c->var = parse_and_eval_address (arg);
+ if (*(unsigned int *) c->var == 0)
+ *(unsigned int *) c->var = UINT_MAX;
+ break;
+ case var_integer:
+ {
+ unsigned int val;
+ if (arg == NULL)
+ error_no_arg ("integer to set it to.");
+ val = parse_and_eval_address (arg);
+ if (val == 0)
+ *(int *) c->var = INT_MAX;
+ else if (val >= INT_MAX)
+ error ("integer %u out of range", val);
+ else
+ *(int *) c->var = val;
+ break;
+ }
+ case var_zinteger:
+ if (arg == NULL)
+ error_no_arg ("integer to set it to.");
+ *(int *) c->var = parse_and_eval_address (arg);
+ break;
+ default:
+ error ("gdb internal error: bad var_type in do_setshow_command");
+ }
+ }
+ else if (c->type == show_cmd)
+ {
+ /* Print doc minus "show" at start. */
+ print_doc_line (stdout, c->doc + 5);
+
+ fputs_filtered (" is ", stdout);
+ wrap_here (" ");
+ switch (c->var_type)
+ {
+ case var_string:
+ {
+ unsigned char *p;
+ fputs_filtered ("\"", stdout);
+ for (p = *(unsigned char **) c->var; *p != '\0'; p++)
+ gdb_printchar (*p, stdout, '"');
+ fputs_filtered ("\"", stdout);
+ }
+ break;
+ case var_string_noescape:
+ case var_filename:
+ fputs_filtered ("\"", stdout);
+ fputs_filtered (*(char **) c->var, stdout);
+ fputs_filtered ("\"", stdout);
+ break;
+ case var_boolean:
+ fputs_filtered (*(int *) c->var ? "on" : "off", stdout);
+ break;
+ case var_uinteger:
+ if (*(unsigned int *) c->var == UINT_MAX) {
+ fputs_filtered ("unlimited", stdout);
+ break;
+ }
+ /* else fall through */
+ case var_zinteger:
+ fprintf_filtered (stdout, "%u", *(unsigned int *) c->var);
+ break;
+ case var_integer:
+ if (*(int *) c->var == INT_MAX)
+ {
+ fputs_filtered ("unlimited", stdout);
+ }
+ else
+ fprintf_filtered (stdout, "%d", *(int *) c->var);
+ break;
+
+ default:
+ error ("gdb internal error: bad var_type in do_setshow_command");
+ }
+ fputs_filtered (".\n", stdout);
+ }
+ else
+ error ("gdb internal error: bad cmd_type in do_setshow_command");
+ (*c->function.sfunc) (NULL, from_tty, c);
+}
+
+/* Show all the settings in a list of show commands. */
+
+void
+cmd_show_list (list, from_tty, prefix)
+ struct cmd_list_element *list;
+ int from_tty;
+ char *prefix;
+{
+ for (; list != NULL; list = list->next) {
+ /* If we find a prefix, run its list, prefixing our output by its
+ prefix (with "show " skipped). */
+ if (list->prefixlist && !list->abbrev_flag)
+ cmd_show_list (*list->prefixlist, from_tty, list->prefixname + 5);
+ if (list->type == show_cmd)
+ {
+ fputs_filtered (prefix, stdout);
+ fputs_filtered (list->name, stdout);
+ fputs_filtered (": ", stdout);
+ do_setshow_command ((char *)NULL, from_tty, list);
+ }
+ }
+}
+
+/* ARGSUSED */
+static void
+shell_escape (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+#ifdef CANT_FORK
+ /* FIXME: what about errors (I don't know how GO32 system() handles
+ them)? */
+ system (arg);
+#else /* Can fork. */
+ int rc, status, pid;
+ char *p, *user_shell;
+
+ if ((user_shell = (char *) getenv ("SHELL")) == NULL)
+ user_shell = "/bin/sh";
+
+ /* Get the name of the shell for arg0 */
+ if ((p = strrchr (user_shell, '/')) == NULL)
+ p = user_shell;
+ else
+ p++; /* Get past '/' */
+
+ if ((pid = fork()) == 0)
+ {
+ if (!arg)
+ execl (user_shell, p, 0);
+ else
+ execl (user_shell, p, "-c", arg, 0);
+
+ fprintf (stderr, "Exec of shell failed\n");
+ exit (0);
+ }
+
+ if (pid != -1)
+ while ((rc = wait (&status)) != pid && rc != -1)
+ ;
+ else
+ error ("Fork failed");
+#endif /* Can fork. */
+}
+
+static void
+make_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ char *p;
+
+ if (arg == 0)
+ p = "make";
+ else
+ {
+ p = xmalloc (sizeof("make ") + strlen(arg));
+ strcpy (p, "make ");
+ strcpy (p + sizeof("make ")-1, arg);
+ }
+
+ shell_escape (p, from_tty);
+}
+
+static void
+show_user_1 (c, stream)
+ struct cmd_list_element *c;
+ FILE *stream;
+{
+ register struct command_line *cmdlines;
+
+ cmdlines = c->user_commands;
+ if (!cmdlines)
+ return;
+ fputs_filtered ("User command ", stream);
+ fputs_filtered (c->name, stream);
+ fputs_filtered (":\n", stream);
+ while (cmdlines)
+ {
+ fputs_filtered (cmdlines->line, stream);
+ fputs_filtered ("\n", stream);
+ cmdlines = cmdlines->next;
+ }
+ fputs_filtered ("\n", stream);
+}
+
+/* ARGSUSED */
+static void
+show_user (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ struct cmd_list_element *c;
+ extern struct cmd_list_element *cmdlist;
+
+ if (args)
+ {
+ c = lookup_cmd (&args, cmdlist, "", 0, 1);
+ if (c->class != class_user)
+ error ("Not a user command.");
+ show_user_1 (c, stdout);
+ }
+ else
+ {
+ for (c = cmdlist; c; c = c->next)
+ {
+ if (c->class == class_user)
+ show_user_1 (c, stdout);
+ }
+ }
+}
+
+void
+_initialize_command ()
+{
+ add_com ("shell", class_support, shell_escape,
+ "Execute the rest of the line as a shell command. \n\
+With no arguments, run an inferior shell.");
+ add_com ("make", class_support, make_command,
+ "Run the ``make'' program using the rest of the line as arguments.");
+ add_cmd ("user", no_class, show_user,
+ "Show definitions of user defined commands.\n\
+Argument is the name of the user defined command.\n\
+With no argument, show definitions of all user defined commands.", &showlist);
+}
diff --git a/gnu/usr.bin/gdb/gdb/command.h b/gnu/usr.bin/gdb/gdb/command.h
new file mode 100644
index 0000000..ee587c3
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/command.h
@@ -0,0 +1,241 @@
+/* Header file for command-reading library command.c.
+ Copyright (C) 1986, 1989, 1990 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (COMMAND_H)
+#define COMMAND_H 1
+
+/* Not a set/show command. Note that some commands which begin with
+ "set" or "show" might be in this category, if their syntax does
+ not fall into one of the following categories. */
+typedef enum cmd_types {
+ not_set_cmd,
+ set_cmd,
+ show_cmd
+} cmd_types;
+
+/* Types of "set" or "show" command. */
+typedef enum var_types {
+ /* "on" or "off". *VAR is an integer which is nonzero for on,
+ zero for off. */
+ var_boolean,
+ /* Unsigned Integer. *VAR is an unsigned int. The user can type 0
+ to mean "unlimited", which is stored in *VAR as UINT_MAX. */
+ var_uinteger,
+
+ /* Like var_uinteger but signed. *VAR is an int. The user can type 0
+ to mean "unlimited", which is stored in *VAR as INT_MAX. */
+ var_integer,
+
+ /* String which the user enters with escapes (e.g. the user types \n and
+ it is a real newline in the stored string).
+ *VAR is a malloc'd string, or NULL if the string is empty. */
+ var_string,
+ /* String which stores what the user types verbatim.
+ *VAR is a malloc'd string, or NULL if the string is empty. */
+ var_string_noescape,
+ /* String which stores a filename.
+ *VAR is a malloc'd string, or NULL if the string is empty. */
+ var_filename,
+ /* ZeroableInteger. *VAR is an int. Like Unsigned Integer except
+ that zero really means zero. */
+ var_zinteger
+} var_types;
+
+/* This structure records one command'd definition. */
+
+struct cmd_list_element
+ {
+ /* Points to next command in this list. */
+ struct cmd_list_element *next;
+
+ /* Name of this command. */
+ char *name;
+
+ /* Command class; class values are chosen by application program. */
+ enum command_class class;
+
+ /* Function definition of this command.
+ Zero for command class names and for help topics that
+ are not really commands. */
+ union {
+ void (*cfunc) PARAMS ((char *args, int from_tty));
+ void (*sfunc) PARAMS ((char *args, int from_tty,
+ struct cmd_list_element *c));
+ } function;
+# define NO_FUNCTION ((void (*) PARAMS((char *args, int from_tty))) 0)
+
+ /* Documentation of this command (or help topic).
+ First line is brief documentation; remaining lines form, with it,
+ the full documentation. First line should end with a period.
+ Entire string should also end with a period, not a newline. */
+ char *doc;
+
+ /* Hook for another command to be executed before this command. */
+ struct cmd_list_element *hook;
+
+ /* Nonzero identifies a prefix command. For them, the address
+ of the variable containing the list of subcommands. */
+ struct cmd_list_element **prefixlist;
+
+ /* For prefix commands only:
+ String containing prefix commands to get here: this one
+ plus any others needed to get to it. Should end in a space.
+ It is used before the word "command" in describing the
+ commands reached through this prefix. */
+ char *prefixname;
+
+ /* For prefix commands only:
+ nonzero means do not get an error if subcommand is not
+ recognized; call the prefix's own function in that case. */
+ char allow_unknown;
+
+ /* Nonzero says this is an abbreviation, and should not
+ be mentioned in lists of commands.
+ This allows "br<tab>" to complete to "break", which it
+ otherwise wouldn't. */
+ char abbrev_flag;
+
+ /* Completion routine for this command. TEXT is the text beyond
+ what was matched for the command itself (leading whitespace is
+ skipped). It stops where we are supposed to stop completing
+ (rl_point) and is '\0' terminated.
+
+ Return value is a malloc'd vector of pointers to possible completions
+ terminated with NULL. If there are no completions, returning a pointer
+ to a NULL would work but returning NULL itself is also valid.
+ WORD points in the same buffer as TEXT, and completions should be
+ returned relative to this position. For example, suppose TEXT is "foo"
+ and we want to complete to "foobar". If WORD is "oo", return
+ "oobar"; if WORD is "baz/foo", return "baz/foobar". */
+ char ** (*completer) PARAMS ((char *text, char *word));
+
+ /* Type of "set" or "show" command (or SET_NOT_SET if not "set"
+ or "show"). */
+ cmd_types type;
+
+ /* Pointer to variable affected by "set" and "show". Doesn't matter
+ if type is not_set. */
+ char *var;
+
+ /* What kind of variable is *VAR? */
+ var_types var_type;
+
+ /* Pointer to command strings of user-defined commands */
+ struct command_line *user_commands;
+
+ /* Pointer to command that is hooked by this one,
+ so the hook can be removed when this one is deleted. */
+ struct cmd_list_element *hookee;
+
+ /* Pointer to command that is aliased by this one, so the
+ aliased command can be located in case it has been hooked. */
+ struct cmd_list_element *cmd_pointer;
+ };
+
+/* Forward-declarations of the entry-points of command.c. */
+
+extern struct cmd_list_element *
+add_cmd PARAMS ((char *, enum command_class, void (*fun) (char *, int),
+ char *, struct cmd_list_element **));
+
+extern struct cmd_list_element *
+add_alias_cmd PARAMS ((char *, char *, enum command_class, int,
+ struct cmd_list_element **));
+
+extern struct cmd_list_element *
+add_prefix_cmd PARAMS ((char *, enum command_class, void (*fun) (char *, int),
+ char *, struct cmd_list_element **, char *, int,
+ struct cmd_list_element **));
+
+extern struct cmd_list_element *
+add_abbrev_prefix_cmd PARAMS ((char *, enum command_class,
+ void (*fun) (char *, int), char *,
+ struct cmd_list_element **, char *, int,
+ struct cmd_list_element **));
+
+extern struct cmd_list_element *
+lookup_cmd PARAMS ((char **, struct cmd_list_element *, char *, int, int));
+
+extern struct cmd_list_element *
+lookup_cmd_1 PARAMS ((char **, struct cmd_list_element *,
+ struct cmd_list_element **, int));
+
+extern void
+add_com PARAMS ((char *, enum command_class, void (*fun)(char *, int),
+ char *));
+
+extern void
+add_com_alias PARAMS ((char *, char *, enum command_class, int));
+
+extern void
+add_info PARAMS ((char *, void (*fun) (char *, int), char *));
+
+extern void
+add_info_alias PARAMS ((char *, char *, int));
+
+extern char **complete_on_cmdlist PARAMS ((struct cmd_list_element *,
+ char *, char *));
+
+extern void
+delete_cmd PARAMS ((char *, struct cmd_list_element **));
+
+extern void
+help_cmd PARAMS ((char *, FILE *));
+
+extern void
+help_list PARAMS ((struct cmd_list_element *, char *, enum command_class,
+ FILE *));
+
+extern void
+help_cmd_list PARAMS ((struct cmd_list_element *, enum command_class, char *,
+ int, FILE *));
+
+extern struct cmd_list_element *
+add_set_cmd PARAMS ((char *, enum command_class, var_types, char *, char *,
+ struct cmd_list_element **));
+
+extern struct cmd_list_element *
+add_show_from_set PARAMS ((struct cmd_list_element *,
+ struct cmd_list_element **));
+
+/* Do a "set" or "show" command. ARG is NULL if no argument, or the text
+ of the argument, and FROM_TTY is nonzero if this command is being entered
+ directly by the user (i.e. these are just like any other
+ command). C is the command list element for the command. */
+
+extern void
+do_setshow_command PARAMS ((char *, int, struct cmd_list_element *));
+
+/* Do a "show" command for each thing on a command list. */
+
+extern void
+cmd_show_list PARAMS ((struct cmd_list_element *, int, char *));
+
+extern void
+error_no_arg PARAMS ((char *));
+
+extern void
+dont_repeat PARAMS ((void));
+
+/* Used to mark commands that don't do anything. If we just leave the
+ function field NULL, the command is interpreted as a help topic, or
+ as a class of commands. */
+
+extern void
+not_just_help_class_command PARAMS ((char *, int));
+
+#endif /* !defined (COMMAND_H) */
diff --git a/gnu/usr.bin/gdb/gdb/complaints.c b/gnu/usr.bin/gdb/gdb/complaints.c
new file mode 100644
index 0000000..079ca5a
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/complaints.c
@@ -0,0 +1,158 @@
+/* Support for complaint handling during symbol reading in GDB.
+ Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "complaints.h"
+#include "gdbcmd.h"
+#include <varargs.h>
+
+/* Structure to manage complaints about symbol file contents. */
+
+struct complaint complaint_root[1] = {
+ {
+ (char *) NULL, /* Complaint message */
+ 0, /* Complaint counter */
+ complaint_root /* Next complaint. */
+ }
+};
+
+/* How many complaints about a particular thing should be printed before
+ we stop whining about it? Default is no whining at all, since so many
+ systems have ill-constructed symbol files. */
+
+static unsigned int stop_whining = 0;
+
+/* Should each complaint be self explanatory, or should we assume that
+ a series of complaints is being produced?
+ case 0: self explanatory message.
+ case 1: First message of a series that must start off with explanation.
+ case 2: Subsequent message, when user already knows we are reading
+ symbols and we can just state our piece. */
+
+static int complaint_series = 0;
+
+/* External variables and functions referenced. */
+
+extern int info_verbose;
+
+
+/* Functions to handle complaints during symbol reading. */
+
+/* Print a complaint about the input symbols, and link the complaint block
+ into a chain for later handling. */
+
+/* VARARGS */
+void
+complain (va_alist)
+ va_dcl
+{
+ va_list args;
+ struct complaint *complaint;
+
+ va_start (args);
+ complaint = va_arg (args, struct complaint *);
+ complaint -> counter++;
+ if (complaint -> next == NULL)
+ {
+ complaint -> next = complaint_root -> next;
+ complaint_root -> next = complaint;
+ }
+ if (complaint -> counter > stop_whining)
+ {
+ return;
+ }
+ wrap_here ("");
+
+ switch (complaint_series + (info_verbose << 1))
+ {
+
+ /* Isolated messages, must be self-explanatory. */
+ case 0:
+ begin_line ();
+ puts_filtered ("During symbol reading, ");
+ wrap_here ("");
+ vprintf_filtered (complaint -> message, args);
+ puts_filtered (".\n");
+ break;
+
+ /* First of a series, without `set verbose'. */
+ case 1:
+ begin_line ();
+ puts_filtered ("During symbol reading...");
+ vprintf_filtered (complaint -> message, args);
+ puts_filtered ("...");
+ wrap_here ("");
+ complaint_series++;
+ break;
+
+ /* Subsequent messages of a series, or messages under `set verbose'.
+ (We'll already have produced a "Reading in symbols for XXX..."
+ message and will clean up at the end with a newline.) */
+ default:
+ vprintf_filtered (complaint -> message, args);
+ puts_filtered ("...");
+ wrap_here ("");
+ }
+ /* If GDB dumps core, we'd like to see the complaints first. Presumably
+ GDB will not be sending so many complaints that this becomes a
+ performance hog. */
+ fflush (stdout);
+ va_end (args);
+}
+
+/* Clear out all complaint counters that have ever been incremented.
+ If sym_reading is 1, be less verbose about successive complaints,
+ since the messages are appearing all together during a command that
+ reads symbols (rather than scattered around as psymtabs get fleshed
+ out into symtabs at random times). If noisy is 1, we are in a
+ noisy symbol reading command, and our caller will print enough
+ context for the user to figure it out. */
+
+void
+clear_complaints (sym_reading, noisy)
+ int sym_reading;
+ int noisy;
+{
+ struct complaint *p;
+
+ for (p = complaint_root -> next; p != complaint_root; p = p -> next)
+ {
+ p -> counter = 0;
+ }
+
+ if (!sym_reading && !noisy && complaint_series > 1)
+ {
+ /* Terminate previous series, since caller won't. */
+ puts_filtered ("\n");
+ }
+
+ complaint_series = sym_reading ? 1 + noisy : 0;
+}
+
+void
+_initialize_complaints ()
+{
+ add_show_from_set
+ (add_set_cmd ("complaints", class_support, var_zinteger,
+ (char *) &stop_whining,
+ "Set max number of complaints about incorrect symbols.",
+ &setlist),
+ &showlist);
+
+}
diff --git a/gnu/usr.bin/gdb/gdb/complaints.h b/gnu/usr.bin/gdb/gdb/complaints.h
new file mode 100644
index 0000000..f7ff5a5
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/complaints.h
@@ -0,0 +1,46 @@
+/* Definitions for complaint handling during symbol reading in GDB.
+ Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* Support for complaining about things in the symbol file that aren't
+ catastrophic.
+
+ Each such thing gets a counter. The first time we have the problem,
+ during a symbol read, we report it. At the end of symbol reading,
+ if verbose, we report how many of each problem we had. */
+
+struct complaint
+{
+ char *message;
+ unsigned counter;
+ struct complaint *next;
+};
+
+/* Root of the chain of complaints that have at some point been issued.
+ This is used to reset the counters, and/or report the total counts. */
+
+extern struct complaint complaint_root[1];
+
+/* Functions that handle complaints. (in complaints.c) */
+
+extern void
+complain ();
+
+extern void
+clear_complaints PARAMS ((int, int));
diff --git a/gnu/usr.bin/gdb/gdb/copying.c b/gnu/usr.bin/gdb/gdb/copying.c
new file mode 100644
index 0000000..ffc884a
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/copying.c
@@ -0,0 +1,327 @@
+/* ==> Do not modify this file!! It is created automatically
+ by copying.awk. Modify copying.awk instead. <== */
+
+#include "defs.h"
+#include "command.h"
+#include "gdbcmd.h"
+
+static void
+show_copying_command PARAMS ((char *, int));
+
+static void
+show_warranty_command PARAMS ((char *, int));
+
+extern int immediate_quit;
+static void
+show_copying_command (ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ immediate_quit++;
+ printf_filtered (" GNU GENERAL PUBLIC LICENSE\n");
+ printf_filtered (" Version 2, June 1991\n");
+ printf_filtered ("\n");
+ printf_filtered (" Copyright (C) 1989, 1991 Free Software Foundation, Inc.\n");
+ printf_filtered (" 675 Mass Ave, Cambridge, MA 02139, USA\n");
+ printf_filtered (" Everyone is permitted to copy and distribute verbatim copies\n");
+ printf_filtered (" of this license document, but changing it is not allowed.\n");
+ printf_filtered ("\n");
+ printf_filtered (" Preamble\n");
+ printf_filtered ("\n");
+ printf_filtered (" The licenses for most software are designed to take away your\n");
+ printf_filtered ("freedom to share and change it. By contrast, the GNU General Public\n");
+ printf_filtered ("License is intended to guarantee your freedom to share and change free\n");
+ printf_filtered ("software--to make sure the software is free for all its users. This\n");
+ printf_filtered ("General Public License applies to most of the Free Software\n");
+ printf_filtered ("Foundation's software and to any other program whose authors commit to\n");
+ printf_filtered ("using it. (Some other Free Software Foundation software is covered by\n");
+ printf_filtered ("the GNU Library General Public License instead.) You can apply it to\n");
+ printf_filtered ("your programs, too.\n");
+ printf_filtered ("\n");
+ printf_filtered (" When we speak of free software, we are referring to freedom, not\n");
+ printf_filtered ("price. Our General Public Licenses are designed to make sure that you\n");
+ printf_filtered ("have the freedom to distribute copies of free software (and charge for\n");
+ printf_filtered ("this service if you wish), that you receive source code or can get it\n");
+ printf_filtered ("if you want it, that you can change the software or use pieces of it\n");
+ printf_filtered ("in new free programs; and that you know you can do these things.\n");
+ printf_filtered ("\n");
+ printf_filtered (" To protect your rights, we need to make restrictions that forbid\n");
+ printf_filtered ("anyone to deny you these rights or to ask you to surrender the rights.\n");
+ printf_filtered ("These restrictions translate to certain responsibilities for you if you\n");
+ printf_filtered ("distribute copies of the software, or if you modify it.\n");
+ printf_filtered ("\n");
+ printf_filtered (" For example, if you distribute copies of such a program, whether\n");
+ printf_filtered ("gratis or for a fee, you must give the recipients all the rights that\n");
+ printf_filtered ("you have. You must make sure that they, too, receive or can get the\n");
+ printf_filtered ("source code. And you must show them these terms so they know their\n");
+ printf_filtered ("rights.\n");
+ printf_filtered ("\n");
+ printf_filtered (" We protect your rights with two steps: (1) copyright the software, and\n");
+ printf_filtered ("(2) offer you this license which gives you legal permission to copy,\n");
+ printf_filtered ("distribute and/or modify the software.\n");
+ printf_filtered ("\n");
+ printf_filtered (" Also, for each author's protection and ours, we want to make certain\n");
+ printf_filtered ("that everyone understands that there is no warranty for this free\n");
+ printf_filtered ("software. If the software is modified by someone else and passed on, we\n");
+ printf_filtered ("want its recipients to know that what they have is not the original, so\n");
+ printf_filtered ("that any problems introduced by others will not reflect on the original\n");
+ printf_filtered ("authors' reputations.\n");
+ printf_filtered ("\n");
+ printf_filtered (" Finally, any free program is threatened constantly by software\n");
+ printf_filtered ("patents. We wish to avoid the danger that redistributors of a free\n");
+ printf_filtered ("program will individually obtain patent licenses, in effect making the\n");
+ printf_filtered ("program proprietary. To prevent this, we have made it clear that any\n");
+ printf_filtered ("patent must be licensed for everyone's free use or not licensed at all.\n");
+ printf_filtered ("\n");
+ printf_filtered (" The precise terms and conditions for copying, distribution and\n");
+ printf_filtered ("modification follow.\n");
+ printf_filtered ("\n");
+ printf_filtered (" GNU GENERAL PUBLIC LICENSE\n");
+ printf_filtered (" TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n");
+ printf_filtered ("\n");
+ printf_filtered (" 0. This License applies to any program or other work which contains\n");
+ printf_filtered ("a notice placed by the copyright holder saying it may be distributed\n");
+ printf_filtered ("under the terms of this General Public License. The \"Program\", below,\n");
+ printf_filtered ("refers to any such program or work, and a \"work based on the Program\"\n");
+ printf_filtered ("means either the Program or any derivative work under copyright law:\n");
+ printf_filtered ("that is to say, a work containing the Program or a portion of it,\n");
+ printf_filtered ("either verbatim or with modifications and/or translated into another\n");
+ printf_filtered ("language. (Hereinafter, translation is included without limitation in\n");
+ printf_filtered ("the term \"modification\".) Each licensee is addressed as \"you\".\n");
+ printf_filtered ("\n");
+ printf_filtered ("Activities other than copying, distribution and modification are not\n");
+ printf_filtered ("covered by this License; they are outside its scope. The act of\n");
+ printf_filtered ("running the Program is not restricted, and the output from the Program\n");
+ printf_filtered ("is covered only if its contents constitute a work based on the\n");
+ printf_filtered ("Program (independent of having been made by running the Program).\n");
+ printf_filtered ("Whether that is true depends on what the Program does.\n");
+ printf_filtered ("\n");
+ printf_filtered (" 1. You may copy and distribute verbatim copies of the Program's\n");
+ printf_filtered ("source code as you receive it, in any medium, provided that you\n");
+ printf_filtered ("conspicuously and appropriately publish on each copy an appropriate\n");
+ printf_filtered ("copyright notice and disclaimer of warranty; keep intact all the\n");
+ printf_filtered ("notices that refer to this License and to the absence of any warranty;\n");
+ printf_filtered ("and give any other recipients of the Program a copy of this License\n");
+ printf_filtered ("along with the Program.\n");
+ printf_filtered ("\n");
+ printf_filtered ("You may charge a fee for the physical act of transferring a copy, and\n");
+ printf_filtered ("you may at your option offer warranty protection in exchange for a fee.\n");
+ printf_filtered ("\n");
+ printf_filtered (" 2. You may modify your copy or copies of the Program or any portion\n");
+ printf_filtered ("of it, thus forming a work based on the Program, and copy and\n");
+ printf_filtered ("distribute such modifications or work under the terms of Section 1\n");
+ printf_filtered ("above, provided that you also meet all of these conditions:\n");
+ printf_filtered ("\n");
+ printf_filtered (" a) You must cause the modified files to carry prominent notices\n");
+ printf_filtered (" stating that you changed the files and the date of any change.\n");
+ printf_filtered ("\n");
+ printf_filtered (" b) You must cause any work that you distribute or publish, that in\n");
+ printf_filtered (" whole or in part contains or is derived from the Program or any\n");
+ printf_filtered (" part thereof, to be licensed as a whole at no charge to all third\n");
+ printf_filtered (" parties under the terms of this License.\n");
+ printf_filtered ("\n");
+ printf_filtered (" c) If the modified program normally reads commands interactively\n");
+ printf_filtered (" when run, you must cause it, when started running for such\n");
+ printf_filtered (" interactive use in the most ordinary way, to print or display an\n");
+ printf_filtered (" announcement including an appropriate copyright notice and a\n");
+ printf_filtered (" notice that there is no warranty (or else, saying that you provide\n");
+ printf_filtered (" a warranty) and that users may redistribute the program under\n");
+ printf_filtered (" these conditions, and telling the user how to view a copy of this\n");
+ printf_filtered (" License. (Exception: if the Program itself is interactive but\n");
+ printf_filtered (" does not normally print such an announcement, your work based on\n");
+ printf_filtered (" the Program is not required to print an announcement.)\n");
+ printf_filtered ("\n");
+ printf_filtered ("These requirements apply to the modified work as a whole. If\n");
+ printf_filtered ("identifiable sections of that work are not derived from the Program,\n");
+ printf_filtered ("and can be reasonably considered independent and separate works in\n");
+ printf_filtered ("themselves, then this License, and its terms, do not apply to those\n");
+ printf_filtered ("sections when you distribute them as separate works. But when you\n");
+ printf_filtered ("distribute the same sections as part of a whole which is a work based\n");
+ printf_filtered ("on the Program, the distribution of the whole must be on the terms of\n");
+ printf_filtered ("this License, whose permissions for other licensees extend to the\n");
+ printf_filtered ("entire whole, and thus to each and every part regardless of who wrote it.\n");
+ printf_filtered ("\n");
+ printf_filtered ("Thus, it is not the intent of this section to claim rights or contest\n");
+ printf_filtered ("your rights to work written entirely by you; rather, the intent is to\n");
+ printf_filtered ("exercise the right to control the distribution of derivative or\n");
+ printf_filtered ("collective works based on the Program.\n");
+ printf_filtered ("\n");
+ printf_filtered ("In addition, mere aggregation of another work not based on the Program\n");
+ printf_filtered ("with the Program (or with a work based on the Program) on a volume of\n");
+ printf_filtered ("a storage or distribution medium does not bring the other work under\n");
+ printf_filtered ("the scope of this License.\n");
+ printf_filtered ("\n");
+ printf_filtered (" 3. You may copy and distribute the Program (or a work based on it,\n");
+ printf_filtered ("under Section 2) in object code or executable form under the terms of\n");
+ printf_filtered ("Sections 1 and 2 above provided that you also do one of the following:\n");
+ printf_filtered ("\n");
+ printf_filtered (" a) Accompany it with the complete corresponding machine-readable\n");
+ printf_filtered (" source code, which must be distributed under the terms of Sections\n");
+ printf_filtered (" 1 and 2 above on a medium customarily used for software interchange; or,\n");
+ printf_filtered ("\n");
+ printf_filtered (" b) Accompany it with a written offer, valid for at least three\n");
+ printf_filtered (" years, to give any third party, for a charge no more than your\n");
+ printf_filtered (" cost of physically performing source distribution, a complete\n");
+ printf_filtered (" machine-readable copy of the corresponding source code, to be\n");
+ printf_filtered (" distributed under the terms of Sections 1 and 2 above on a medium\n");
+ printf_filtered (" customarily used for software interchange; or,\n");
+ printf_filtered ("\n");
+ printf_filtered (" c) Accompany it with the information you received as to the offer\n");
+ printf_filtered (" to distribute corresponding source code. (This alternative is\n");
+ printf_filtered (" allowed only for noncommercial distribution and only if you\n");
+ printf_filtered (" received the program in object code or executable form with such\n");
+ printf_filtered (" an offer, in accord with Subsection b above.)\n");
+ printf_filtered ("\n");
+ printf_filtered ("The source code for a work means the preferred form of the work for\n");
+ printf_filtered ("making modifications to it. For an executable work, complete source\n");
+ printf_filtered ("code means all the source code for all modules it contains, plus any\n");
+ printf_filtered ("associated interface definition files, plus the scripts used to\n");
+ printf_filtered ("control compilation and installation of the executable. However, as a\n");
+ printf_filtered ("special exception, the source code distributed need not include\n");
+ printf_filtered ("anything that is normally distributed (in either source or binary\n");
+ printf_filtered ("form) with the major components (compiler, kernel, and so on) of the\n");
+ printf_filtered ("operating system on which the executable runs, unless that component\n");
+ printf_filtered ("itself accompanies the executable.\n");
+ printf_filtered ("\n");
+ printf_filtered ("If distribution of executable or object code is made by offering\n");
+ printf_filtered ("access to copy from a designated place, then offering equivalent\n");
+ printf_filtered ("access to copy the source code from the same place counts as\n");
+ printf_filtered ("distribution of the source code, even though third parties are not\n");
+ printf_filtered ("compelled to copy the source along with the object code.\n");
+ printf_filtered ("\n");
+ printf_filtered (" 4. You may not copy, modify, sublicense, or distribute the Program\n");
+ printf_filtered ("except as expressly provided under this License. Any attempt\n");
+ printf_filtered ("otherwise to copy, modify, sublicense or distribute the Program is\n");
+ printf_filtered ("void, and will automatically terminate your rights under this License.\n");
+ printf_filtered ("However, parties who have received copies, or rights, from you under\n");
+ printf_filtered ("this License will not have their licenses terminated so long as such\n");
+ printf_filtered ("parties remain in full compliance.\n");
+ printf_filtered ("\n");
+ printf_filtered (" 5. You are not required to accept this License, since you have not\n");
+ printf_filtered ("signed it. However, nothing else grants you permission to modify or\n");
+ printf_filtered ("distribute the Program or its derivative works. These actions are\n");
+ printf_filtered ("prohibited by law if you do not accept this License. Therefore, by\n");
+ printf_filtered ("modifying or distributing the Program (or any work based on the\n");
+ printf_filtered ("Program), you indicate your acceptance of this License to do so, and\n");
+ printf_filtered ("all its terms and conditions for copying, distributing or modifying\n");
+ printf_filtered ("the Program or works based on it.\n");
+ printf_filtered ("\n");
+ printf_filtered (" 6. Each time you redistribute the Program (or any work based on the\n");
+ printf_filtered ("Program), the recipient automatically receives a license from the\n");
+ printf_filtered ("original licensor to copy, distribute or modify the Program subject to\n");
+ printf_filtered ("these terms and conditions. You may not impose any further\n");
+ printf_filtered ("restrictions on the recipients' exercise of the rights granted herein.\n");
+ printf_filtered ("You are not responsible for enforcing compliance by third parties to\n");
+ printf_filtered ("this License.\n");
+ printf_filtered ("\n");
+ printf_filtered (" 7. If, as a consequence of a court judgment or allegation of patent\n");
+ printf_filtered ("infringement or for any other reason (not limited to patent issues),\n");
+ printf_filtered ("conditions are imposed on you (whether by court order, agreement or\n");
+ printf_filtered ("otherwise) that contradict the conditions of this License, they do not\n");
+ printf_filtered ("excuse you from the conditions of this License. If you cannot\n");
+ printf_filtered ("distribute so as to satisfy simultaneously your obligations under this\n");
+ printf_filtered ("License and any other pertinent obligations, then as a consequence you\n");
+ printf_filtered ("may not distribute the Program at all. For example, if a patent\n");
+ printf_filtered ("license would not permit royalty-free redistribution of the Program by\n");
+ printf_filtered ("all those who receive copies directly or indirectly through you, then\n");
+ printf_filtered ("the only way you could satisfy both it and this License would be to\n");
+ printf_filtered ("refrain entirely from distribution of the Program.\n");
+ printf_filtered ("\n");
+ printf_filtered ("If any portion of this section is held invalid or unenforceable under\n");
+ printf_filtered ("any particular circumstance, the balance of the section is intended to\n");
+ printf_filtered ("apply and the section as a whole is intended to apply in other\n");
+ printf_filtered ("circumstances.\n");
+ printf_filtered ("\n");
+ printf_filtered ("It is not the purpose of this section to induce you to infringe any\n");
+ printf_filtered ("patents or other property right claims or to contest validity of any\n");
+ printf_filtered ("such claims; this section has the sole purpose of protecting the\n");
+ printf_filtered ("integrity of the free software distribution system, which is\n");
+ printf_filtered ("implemented by public license practices. Many people have made\n");
+ printf_filtered ("generous contributions to the wide range of software distributed\n");
+ printf_filtered ("through that system in reliance on consistent application of that\n");
+ printf_filtered ("system; it is up to the author/donor to decide if he or she is willing\n");
+ printf_filtered ("to distribute software through any other system and a licensee cannot\n");
+ printf_filtered ("impose that choice.\n");
+ printf_filtered ("\n");
+ printf_filtered ("This section is intended to make thoroughly clear what is believed to\n");
+ printf_filtered ("be a consequence of the rest of this License.\n");
+ printf_filtered ("\n");
+ printf_filtered (" 8. If the distribution and/or use of the Program is restricted in\n");
+ printf_filtered ("certain countries either by patents or by copyrighted interfaces, the\n");
+ printf_filtered ("original copyright holder who places the Program under this License\n");
+ printf_filtered ("may add an explicit geographical distribution limitation excluding\n");
+ printf_filtered ("those countries, so that distribution is permitted only in or among\n");
+ printf_filtered ("countries not thus excluded. In such case, this License incorporates\n");
+ printf_filtered ("the limitation as if written in the body of this License.\n");
+ printf_filtered ("\n");
+ printf_filtered (" 9. The Free Software Foundation may publish revised and/or new versions\n");
+ printf_filtered ("of the General Public License from time to time. Such new versions will\n");
+ printf_filtered ("be similar in spirit to the present version, but may differ in detail to\n");
+ printf_filtered ("address new problems or concerns.\n");
+ printf_filtered ("\n");
+ printf_filtered ("Each version is given a distinguishing version number. If the Program\n");
+ printf_filtered ("specifies a version number of this License which applies to it and \"any\n");
+ printf_filtered ("later version\", you have the option of following the terms and conditions\n");
+ printf_filtered ("either of that version or of any later version published by the Free\n");
+ printf_filtered ("Software Foundation. If the Program does not specify a version number of\n");
+ printf_filtered ("this License, you may choose any version ever published by the Free Software\n");
+ printf_filtered ("Foundation.\n");
+ printf_filtered ("\n");
+ printf_filtered (" 10. If you wish to incorporate parts of the Program into other free\n");
+ printf_filtered ("programs whose distribution conditions are different, write to the author\n");
+ printf_filtered ("to ask for permission. For software which is copyrighted by the Free\n");
+ printf_filtered ("Software Foundation, write to the Free Software Foundation; we sometimes\n");
+ printf_filtered ("make exceptions for this. Our decision will be guided by the two goals\n");
+ printf_filtered ("of preserving the free status of all derivatives of our free software and\n");
+ printf_filtered ("of promoting the sharing and reuse of software generally.\n");
+ printf_filtered ("\n");
+ immediate_quit--;
+}
+
+static void
+show_warranty_command (ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ immediate_quit++;
+ printf_filtered (" NO WARRANTY\n");
+ printf_filtered ("\n");
+ printf_filtered (" 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\n");
+ printf_filtered ("FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN\n");
+ printf_filtered ("OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\n");
+ printf_filtered ("PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\n");
+ printf_filtered ("OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n");
+ printf_filtered ("MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS\n");
+ printf_filtered ("TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE\n");
+ printf_filtered ("PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\n");
+ printf_filtered ("REPAIR OR CORRECTION.\n");
+ printf_filtered ("\n");
+ printf_filtered (" 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\n");
+ printf_filtered ("WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\n");
+ printf_filtered ("REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\n");
+ printf_filtered ("INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\n");
+ printf_filtered ("OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\n");
+ printf_filtered ("TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\n");
+ printf_filtered ("YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\n");
+ printf_filtered ("PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\n");
+ printf_filtered ("POSSIBILITY OF SUCH DAMAGES.\n");
+ printf_filtered ("\n");
+ immediate_quit--;
+}
+
+void
+_initialize_copying ()
+{
+ add_cmd ("copying", no_class, show_copying_command,
+ "Conditions for redistributing copies of GDB.",
+ &showlist);
+ add_cmd ("warranty", no_class, show_warranty_command,
+ "Various kinds of warranty you do not have.",
+ &showlist);
+
+ /* For old-timers, allow "info copying", etc. */
+ add_info ("copying", show_copying_command,
+ "Conditions for redistributing copies of GDB.");
+ add_info ("warranty", show_warranty_command,
+ "Various kinds of warranty you do not have.");
+}
diff --git a/gnu/usr.bin/gdb/gdb/core.c b/gnu/usr.bin/gdb/gdb/core.c
new file mode 100644
index 0000000..36c9ab5
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/core.c
@@ -0,0 +1,291 @@
+/* Core dump and executable file functions above target vector, for GDB.
+ Copyright 1986, 1987, 1989, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include "frame.h" /* required by inferior.h */
+#include "inferior.h"
+#include "symtab.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "bfd.h"
+#include "target.h"
+#include "gdbcore.h"
+#include "dis-asm.h"
+
+extern char registers[];
+
+/* Hook for `exec_file_command' command to call. */
+
+void (*exec_file_display_hook) PARAMS ((char *)) = NULL;
+
+/* Binary file diddling handle for the core file. */
+
+bfd *core_bfd = NULL;
+
+
+/* Backward compatability with old way of specifying core files. */
+
+void
+core_file_command (filename, from_tty)
+ char *filename;
+ int from_tty;
+{
+ struct target_ops *t;
+
+ dont_repeat (); /* Either way, seems bogus. */
+
+ t = find_core_target ();
+ if (t != NULL)
+ if (!filename)
+ (t->to_detach) (filename, from_tty);
+ else
+ (t->to_open) (filename, from_tty);
+ else
+ error ("GDB can't read core files on this machine.");
+}
+
+
+/* Call this to specify the hook for exec_file_command to call back.
+ This is called from the x-window display code. */
+
+void
+specify_exec_file_hook (hook)
+ void (*hook) PARAMS ((char *));
+{
+ exec_file_display_hook = hook;
+}
+
+/* The exec file must be closed before running an inferior.
+ If it is needed again after the inferior dies, it must
+ be reopened. */
+
+void
+close_exec_file ()
+{
+#ifdef FIXME
+ if (exec_bfd)
+ bfd_tempclose (exec_bfd);
+#endif
+}
+
+void
+reopen_exec_file ()
+{
+#ifdef FIXME
+ if (exec_bfd)
+ bfd_reopen (exec_bfd);
+#endif
+}
+
+/* If we have both a core file and an exec file,
+ print a warning if they don't go together. */
+
+void
+validate_files ()
+{
+ if (exec_bfd && core_bfd)
+ {
+ if (!core_file_matches_executable_p (core_bfd, exec_bfd))
+ warning ("core file may not match specified executable file.");
+ else if (bfd_get_mtime(exec_bfd) > bfd_get_mtime(core_bfd))
+ warning ("exec file is newer than core file.");
+ }
+}
+
+/* Return the name of the executable file as a string.
+ ERR nonzero means get error if there is none specified;
+ otherwise return 0 in that case. */
+
+char *
+get_exec_file (err)
+ int err;
+{
+ if (exec_bfd) return bfd_get_filename(exec_bfd);
+ if (!err) return NULL;
+
+ error ("No executable file specified.\n\
+Use the \"file\" or \"exec-file\" command.");
+ return NULL;
+}
+
+
+/* Report a memory error with error(). */
+
+void
+memory_error (status, memaddr)
+ int status;
+ CORE_ADDR memaddr;
+{
+
+ if (status == EIO)
+ {
+ /* Actually, address between memaddr and memaddr + len
+ was out of bounds. */
+ error ("Cannot access memory at address %s.",
+ local_hex_string((unsigned long) memaddr));
+ }
+ else
+ {
+ error ("Error accessing memory address %s: %s.",
+ local_hex_string ((unsigned long) memaddr),
+ safe_strerror (status));
+ }
+}
+
+/* Same as target_read_memory, but report an error if can't read. */
+void
+read_memory (memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+{
+ int status;
+ status = target_read_memory (memaddr, myaddr, len);
+ if (status != 0)
+ memory_error (status, memaddr);
+}
+
+/* Like target_read_memory, but slightly different parameters. */
+
+int
+dis_asm_read_memory (memaddr, myaddr, len, info)
+ bfd_vma memaddr;
+ bfd_byte *myaddr;
+ int len;
+ disassemble_info *info;
+{
+ return target_read_memory (memaddr, (char *) myaddr, len);
+}
+
+/* Like memory_error with slightly different parameters. */
+void
+dis_asm_memory_error (status, memaddr, info)
+ int status;
+ bfd_vma memaddr;
+ disassemble_info *info;
+{
+ memory_error (status, memaddr);
+}
+
+/* Like print_address with slightly different parameters. */
+void
+dis_asm_print_address (addr, info)
+ bfd_vma addr;
+ struct disassemble_info *info;
+{
+ print_address (addr, info->stream);
+}
+
+/* Same as target_write_memory, but report an error if can't write. */
+void
+write_memory (memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+{
+ int status;
+
+ status = target_write_memory (memaddr, myaddr, len);
+ if (status != 0)
+ memory_error (status, memaddr);
+}
+
+/* Read an integer from debugged memory, given address and number of bytes. */
+
+LONGEST
+read_memory_integer (memaddr, len)
+ CORE_ADDR memaddr;
+ int len;
+{
+ char buf[sizeof (LONGEST)];
+
+ read_memory (memaddr, buf, len);
+ return extract_signed_integer (buf, len);
+}
+
+unsigned LONGEST
+read_memory_unsigned_integer (memaddr, len)
+ CORE_ADDR memaddr;
+ int len;
+{
+ char buf[sizeof (unsigned LONGEST)];
+
+ read_memory (memaddr, buf, len);
+ return extract_unsigned_integer (buf, len);
+}
+
+/* The current default bfd target. Points to storage allocated for
+ gnutarget_string. */
+char *gnutarget;
+
+/* Same thing, except it is "auto" not NULL for the default case. */
+static char *gnutarget_string;
+
+static void set_gnutarget_command
+ PARAMS ((char *, int, struct cmd_list_element *));
+
+static void
+set_gnutarget_command (ignore, from_tty, c)
+ char *ignore;
+ int from_tty;
+ struct cmd_list_element *c;
+{
+ if (STREQ (gnutarget_string, "auto"))
+ gnutarget = NULL;
+ else
+ gnutarget = gnutarget_string;
+}
+
+/* Set the gnutarget. */
+void
+set_gnutarget (newtarget)
+ char *newtarget;
+{
+ if (gnutarget_string != NULL)
+ free (gnutarget_string);
+ gnutarget_string = savestring (newtarget, strlen (newtarget));
+ set_gnutarget_command (NULL, 0, NULL);
+}
+
+void
+_initialize_core()
+{
+ struct cmd_list_element *c;
+ c = add_cmd ("core-file", class_files, core_file_command,
+ "Use FILE as core dump for examining memory and registers.\n\
+No arg means have no core file. This command has been superseded by the\n\
+`target core' and `detach' commands.", &cmdlist);
+ c->completer = filename_completer;
+
+ c = add_set_cmd ("gnutarget", class_files, var_string_noescape,
+ (char *) &gnutarget_string,
+ "Set the current BFD target.\n\
+Use `set gnutarget auto' to specify automatic detection.",
+ &setlist);
+ c->function.sfunc = set_gnutarget_command;
+ add_show_from_set (c, &showlist);
+
+ if (getenv ("GNUTARGET"))
+ set_gnutarget (getenv ("GNUTARGET"));
+ else
+ set_gnutarget ("auto");
+}
diff --git a/gnu/usr.bin/gdb/gdb/coredep.c b/gnu/usr.bin/gdb/gdb/coredep.c
new file mode 100644
index 0000000..d94fd98
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/coredep.c
@@ -0,0 +1,118 @@
+/* Extract registers from a "standard" core file, for GDB.
+ Copyright (C) 1988-1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* core.c is supposed to be the more machine-independent aspects of this;
+ this file is more machine-specific. */
+
+#include "defs.h"
+#include <sys/types.h>
+#include <sys/param.h>
+#include "gdbcore.h"
+
+/* These are needed on various systems to expand REGISTER_U_ADDR. */
+#ifndef USG
+#include <sys/dir.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/user.h>
+#ifndef NO_PTRACE_H
+# ifdef PTRACE_IN_WRONG_PLACE
+# include <ptrace.h>
+# else /* !PTRACE_IN_WRONG_PLACE */
+# include <sys/ptrace.h>
+# endif /* !PTRACE_IN_WRONG_PLACE */
+#endif /* NO_PTRACE_H */
+#endif
+
+#ifdef NEED_SYS_CORE_H
+#include <sys/core.h>
+#endif
+
+/* Extract the register values out of the core file and store
+ them where `read_register' will find them.
+
+ CORE_REG_SECT points to the register values themselves, read into memory.
+ CORE_REG_SIZE is the size of that area.
+ WHICH says which set of registers we are handling (0 = int, 2 = float
+ on machines where they are discontiguous).
+ REG_ADDR is the offset from u.u_ar0 to the register values relative to
+ core_reg_sect. This is used with old-fashioned core files to
+ locate the registers in a large upage-plus-stack ".reg" section.
+ Original upage address X is at location core_reg_sect+x+reg_addr.
+ */
+
+void
+fetch_core_registers (core_reg_sect, core_reg_size, which, reg_addr)
+ char *core_reg_sect;
+ unsigned core_reg_size;
+ int which;
+ unsigned reg_addr;
+{
+ register int regno;
+ register unsigned int addr;
+ int bad_reg = -1;
+ register reg_ptr = -reg_addr; /* Original u.u_ar0 is -reg_addr. */
+
+ /* If u.u_ar0 was an absolute address in the core file, relativize it now,
+ so we can use it as an offset into core_reg_sect. When we're done,
+ "register 0" will be at core_reg_sect+reg_ptr, and we can use
+ register_addr to offset to the other registers. If this is a modern
+ core file without a upage, reg_ptr will be zero and this is all a big
+ NOP. */
+ if (reg_ptr > core_reg_size)
+ reg_ptr -= KERNEL_U_ADDR;
+
+ for (regno = 0; regno < NUM_REGS; regno++)
+ {
+ addr = register_addr (regno, reg_ptr);
+ if (addr >= core_reg_size) {
+ if (bad_reg < 0)
+ bad_reg = regno;
+ } else {
+ supply_register (regno, core_reg_sect + addr);
+ }
+ }
+ if (bad_reg >= 0)
+ {
+ error ("Register %s not found in core file.", reg_names[bad_reg]);
+ }
+}
+
+
+#ifdef REGISTER_U_ADDR
+
+/* Return the address in the core dump or inferior of register REGNO.
+ BLOCKEND is the address of the end of the user structure. */
+
+unsigned int
+register_addr (regno, blockend)
+ int regno;
+ int blockend;
+{
+ int addr;
+
+ if (regno < 0 || regno >= NUM_REGS)
+ error ("Invalid register number %d.", regno);
+
+ REGISTER_U_ADDR (addr, blockend, regno);
+
+ return addr;
+}
+
+#endif /* REGISTER_U_ADDR */
diff --git a/gnu/usr.bin/gdb/gdb/corelow.c b/gnu/usr.bin/gdb/gdb/corelow.c
new file mode 100644
index 0000000..1cebc48
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/corelow.c
@@ -0,0 +1,328 @@
+/* Core dump and executable file functions below target vector, for GDB.
+ Copyright 1986, 1987, 1989, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include "frame.h" /* required by inferior.h */
+#include "inferior.h"
+#include "symtab.h"
+#include "command.h"
+#include "bfd.h"
+#include "target.h"
+#include "gdbcore.h"
+
+static void
+core_files_info PARAMS ((struct target_ops *));
+
+#ifdef SOLIB_ADD
+static int
+solib_add_stub PARAMS ((char *));
+#endif
+
+static void
+core_close PARAMS ((int));
+
+static void
+get_core_registers PARAMS ((int));
+
+/* Discard all vestiges of any previous core file
+ and mark data and stack spaces as empty. */
+
+/* ARGSUSED */
+static void
+core_close (quitting)
+ int quitting;
+{
+ inferior_pid = 0; /* Avoid confusion from thread stuff */
+
+ if (core_bfd) {
+ free (bfd_get_filename (core_bfd));
+ bfd_close (core_bfd);
+ core_bfd = NULL;
+#ifdef CLEAR_SOLIB
+ CLEAR_SOLIB ();
+#endif
+ if (core_ops.to_sections) {
+ free ((PTR)core_ops.to_sections);
+ core_ops.to_sections = NULL;
+ core_ops.to_sections_end = NULL;
+ }
+ }
+}
+
+#ifdef SOLIB_ADD
+/* Stub function for catch_errors around shared library hacking. */
+
+static int
+solib_add_stub (from_tty)
+ char *from_tty;
+{
+ SOLIB_ADD (NULL, (int)from_tty, &core_ops);
+ return 0;
+}
+#endif /* SOLIB_ADD */
+
+/* Look for sections whose names start with `.reg/' so that we can extract the
+ list of threads in a core file. */
+
+static void
+add_to_thread_list (abfd, asect, reg_sect_arg)
+ bfd *abfd;
+ asection *asect;
+ PTR reg_sect_arg;
+{
+ int thread_id;
+ asection *reg_sect = (asection *) reg_sect_arg;
+
+ if (strncmp (bfd_section_name (abfd, asect), ".reg/", 5) != 0)
+ return;
+
+ thread_id = atoi (bfd_section_name (abfd, asect) + 5);
+
+ add_thread (thread_id);
+
+/* Warning, Will Robinson, looking at BFD private data! */
+
+ if (asect->filepos == reg_sect->filepos) /* Did we find .reg? */
+ inferior_pid = thread_id; /* Yes, make it current */
+}
+
+/* This routine opens and sets up the core file bfd */
+
+void
+core_open (filename, from_tty)
+ char *filename;
+ int from_tty;
+{
+ const char *p;
+ int siggy;
+ struct cleanup *old_chain;
+ char *temp;
+ bfd *temp_bfd;
+ int ontop;
+ int scratch_chan;
+
+ target_preopen (from_tty);
+ if (!filename)
+ {
+ error (core_bfd?
+ "No core file specified. (Use `detach' to stop debugging a core file.)"
+ : "No core file specified.");
+ }
+
+ filename = tilde_expand (filename);
+ if (filename[0] != '/') {
+ temp = concat (current_directory, "/", filename, NULL);
+ free (filename);
+ filename = temp;
+ }
+
+ old_chain = make_cleanup (free, filename);
+
+ scratch_chan = open (filename, write_files? O_RDWR: O_RDONLY, 0);
+ if (scratch_chan < 0)
+ perror_with_name (filename);
+
+ temp_bfd = bfd_fdopenr (filename, gnutarget, scratch_chan);
+ if (temp_bfd == NULL)
+ {
+ perror_with_name (filename);
+ }
+
+ if (!bfd_check_format (temp_bfd, bfd_core))
+ {
+ /* Do it after the err msg */
+ make_cleanup (bfd_close, temp_bfd);
+ error ("\"%s\" is not a core dump: %s", filename, bfd_errmsg(bfd_error));
+ }
+
+ /* Looks semi-reasonable. Toss the old core file and work on the new. */
+
+ discard_cleanups (old_chain); /* Don't free filename any more */
+ unpush_target (&core_ops);
+ core_bfd = temp_bfd;
+ old_chain = make_cleanup (core_close, core_bfd);
+
+ validate_files ();
+
+ /* Find the data section */
+ if (build_section_table (core_bfd, &core_ops.to_sections,
+ &core_ops.to_sections_end))
+ error ("Can't find sections in `%s': %s", bfd_get_filename(core_bfd),
+ bfd_errmsg (bfd_error));
+
+ ontop = !push_target (&core_ops);
+ discard_cleanups (old_chain);
+
+ p = bfd_core_file_failing_command (core_bfd);
+ if (p)
+ printf_filtered ("Core was generated by `%s'.\n", p);
+
+ siggy = bfd_core_file_failing_signal (core_bfd);
+ if (siggy > 0)
+ printf_filtered ("Program terminated with signal %d, %s.\n", siggy,
+ safe_strsignal (siggy));
+
+ /* Build up thread list from BFD sections. */
+
+ init_thread_list ();
+ bfd_map_over_sections (core_bfd, add_to_thread_list,
+ bfd_get_section_by_name (core_bfd, ".reg"));
+
+ if (ontop) {
+ /* Fetch all registers from core file */
+ target_fetch_registers (-1);
+
+ /* Add symbols and section mappings for any shared libraries */
+#ifdef SOLIB_ADD
+ catch_errors (solib_add_stub, (char *)from_tty, (char *)0,
+ RETURN_MASK_ALL);
+#endif
+
+ /* Now, set up the frame cache, and print the top of stack */
+ set_current_frame (create_new_frame (read_fp (),
+ read_pc ()));
+ select_frame (get_current_frame (), 0);
+ print_stack_frame (selected_frame, selected_frame_level, 1);
+ } else {
+ warning (
+"you won't be able to access this core file until you terminate\n\
+your %s; do ``info files''", current_target->to_longname);
+ }
+}
+
+void
+core_detach (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ if (args)
+ error ("Too many arguments");
+ unpush_target (&core_ops);
+ reinit_frame_cache ();
+ if (from_tty)
+ printf_filtered ("No core file now.\n");
+}
+
+/* Get the registers out of a core file. This is the machine-
+ independent part. Fetch_core_registers is the machine-dependent
+ part, typically implemented in the xm-file for each architecture. */
+
+/* We just get all the registers, so we don't use regno. */
+/* ARGSUSED */
+static void
+get_core_registers (regno)
+ int regno;
+{
+ sec_ptr reg_sec;
+ unsigned size;
+ char *the_regs;
+ char secname[10];
+
+ /* Thread support. If inferior_pid is non-zero, then we have found a core
+ file with threads (or multiple processes). In that case, we need to
+ use the appropriate register section, else we just use `.reg'. */
+
+ /* XXX - same thing needs to be done for floating-point (.reg2) sections. */
+
+ if (inferior_pid)
+ sprintf (secname, ".reg/%d", inferior_pid);
+ else
+ strcpy (secname, ".reg");
+
+ reg_sec = bfd_get_section_by_name (core_bfd, secname);
+ if (!reg_sec) goto cant;
+ size = bfd_section_size (core_bfd, reg_sec);
+ the_regs = alloca (size);
+ if (bfd_get_section_contents (core_bfd, reg_sec, the_regs, (file_ptr)0, size))
+ {
+ fetch_core_registers (the_regs, size, 0,
+ (unsigned) bfd_section_vma (abfd,reg_sec));
+ }
+ else
+ {
+cant:
+ fprintf_filtered (stderr, "Couldn't fetch registers from core file: %s\n",
+ bfd_errmsg (bfd_error));
+ }
+
+ /* Now do it again for the float registers, if they exist. */
+ reg_sec = bfd_get_section_by_name (core_bfd, ".reg2");
+ if (reg_sec) {
+ size = bfd_section_size (core_bfd, reg_sec);
+ the_regs = alloca (size);
+ if (bfd_get_section_contents (core_bfd, reg_sec, the_regs, (file_ptr)0,
+ size))
+ {
+ fetch_core_registers (the_regs, size, 2,
+ (unsigned) bfd_section_vma (abfd,reg_sec));
+ }
+ else
+ {
+ fprintf_filtered (stderr, "Couldn't fetch register set 2 from core file: %s\n",
+ bfd_errmsg (bfd_error));
+ }
+ }
+ registers_fetched();
+}
+
+static void
+core_files_info (t)
+ struct target_ops *t;
+{
+ print_section_info (t, core_bfd);
+}
+
+/* If mourn is being called in all the right places, this could be say
+ `gdb internal error' (since generic_mourn calls breakpoint_init_inferior). */
+
+static int
+ignore (addr, contents)
+ CORE_ADDR addr;
+ char *contents;
+{
+}
+
+struct target_ops core_ops = {
+ "core", "Local core dump file",
+ "Use a core file as a target. Specify the filename of the core file.",
+ core_open, core_close,
+ find_default_attach, core_detach, 0, 0, /* resume, wait */
+ get_core_registers,
+ 0, 0, /* store_regs, prepare_to_store */
+ xfer_memory, core_files_info,
+ ignore, ignore, /* core_insert_breakpoint, core_remove_breakpoint, */
+ 0, 0, 0, 0, 0, /* terminal stuff */
+ 0, 0, 0, /* kill, load, lookup sym */
+ find_default_create_inferior, 0, /* mourn_inferior */
+ 0, /* can_run */
+ 0, /* notice_signals */
+ core_stratum, 0, /* next */
+ 0, 1, 1, 1, 0, /* all mem, mem, stack, regs, exec */
+ 0, 0, /* section pointers */
+ OPS_MAGIC, /* Always the last thing */
+};
+
+void
+_initialize_corelow()
+{
+ add_target (&core_ops);
+}
diff --git a/gnu/usr.bin/gdb/gdb/cp-valprint.c b/gnu/usr.bin/gdb/gdb/cp-valprint.c
new file mode 100644
index 0000000..47b38f8
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/cp-valprint.c
@@ -0,0 +1,484 @@
+/* Support for printing C++ values for GDB, the GNU debugger.
+ Copyright 1986, 1988, 1989, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "obstack.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "expression.h"
+#include "value.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "demangle.h"
+
+int vtblprint; /* Controls printing of vtbl's */
+int objectprint; /* Controls looking up an object's derived type
+ using what we find in its vtables. */
+struct obstack dont_print_obstack;
+
+static void
+cplus_print_value PARAMS ((struct type *, char *, FILE *, int, int,
+ enum val_prettyprint, struct type **));
+
+/* BEGIN-FIXME: Hooks into typeprint.c, find a better home for prototypes. */
+
+extern void
+c_type_print_base PARAMS ((struct type *, FILE *, int, int));
+
+extern void
+c_type_print_varspec_prefix PARAMS ((struct type *, FILE *, int, int));
+
+extern void
+cp_type_print_method_args PARAMS ((struct type **, char *, char *, int,
+ FILE *));
+
+extern struct obstack dont_print_obstack;
+
+/* END-FIXME */
+
+
+/* BEGIN-FIXME: Hooks into c-valprint.c */
+
+extern int
+c_val_print PARAMS ((struct type *, char *, CORE_ADDR, FILE *, int, int, int,
+ enum val_prettyprint));
+/* END-FIXME */
+
+
+void
+cp_print_class_method (valaddr, type, stream)
+ char *valaddr;
+ struct type *type;
+ FILE *stream;
+{
+ struct type *domain;
+ struct fn_field *f = NULL;
+ int j = 0;
+ int len2;
+ int offset;
+ char *kind = "";
+ CORE_ADDR addr;
+ struct symbol *sym;
+ unsigned len;
+ unsigned int i;
+
+ check_stub_type (TYPE_TARGET_TYPE (type));
+ domain = TYPE_DOMAIN_TYPE (TYPE_TARGET_TYPE (type));
+ if (domain == (struct type *)NULL)
+ {
+ fprintf_filtered (stream, "<unknown>");
+ return;
+ }
+ addr = unpack_pointer (lookup_pointer_type (builtin_type_void), valaddr);
+ if (METHOD_PTR_IS_VIRTUAL (addr))
+ {
+ offset = METHOD_PTR_TO_VOFFSET (addr);
+ len = TYPE_NFN_FIELDS (domain);
+ for (i = 0; i < len; i++)
+ {
+ f = TYPE_FN_FIELDLIST1 (domain, i);
+ len2 = TYPE_FN_FIELDLIST_LENGTH (domain, i);
+
+ for (j = 0; j < len2; j++)
+ {
+ QUIT;
+ if (TYPE_FN_FIELD_VOFFSET (f, j) == offset)
+ {
+ kind = "virtual ";
+ goto common;
+ }
+ }
+ }
+ }
+ else
+ {
+ sym = find_pc_function (addr);
+ if (sym == 0)
+ {
+ error ("invalid pointer to member function");
+ }
+ len = TYPE_NFN_FIELDS (domain);
+ for (i = 0; i < len; i++)
+ {
+ f = TYPE_FN_FIELDLIST1 (domain, i);
+ len2 = TYPE_FN_FIELDLIST_LENGTH (domain, i);
+
+ for (j = 0; j < len2; j++)
+ {
+ QUIT;
+ if (TYPE_FN_FIELD_STUB (f, j))
+ check_stub_method (domain, i, j);
+ if (STREQ (SYMBOL_NAME (sym), TYPE_FN_FIELD_PHYSNAME (f, j)))
+ {
+ goto common;
+ }
+ }
+ }
+ }
+ common:
+ if (i < len)
+ {
+ fprintf_filtered (stream, "&");
+ c_type_print_varspec_prefix (TYPE_FN_FIELD_TYPE (f, j), stream, 0, 0);
+ fprintf (stream, kind);
+ if (TYPE_FN_FIELD_PHYSNAME (f, j)[0] == '_'
+ && TYPE_FN_FIELD_PHYSNAME (f, j)[1] == CPLUS_MARKER)
+ {
+ cp_type_print_method_args (TYPE_FN_FIELD_ARGS (f, j) + 1, "~",
+ TYPE_FN_FIELDLIST_NAME (domain, i),
+ 0, stream);
+ }
+ else
+ {
+ cp_type_print_method_args (TYPE_FN_FIELD_ARGS (f, j), "",
+ TYPE_FN_FIELDLIST_NAME (domain, i),
+ 0, stream);
+ }
+ }
+ else
+ {
+ fprintf_filtered (stream, "(");
+ type_print (type, "", stream, -1);
+ fprintf_filtered (stream, ") %d", (int) addr >> 3);
+ }
+}
+
+/* Return truth value for assertion that TYPE is of the type
+ "pointer to virtual function". */
+
+int
+cp_is_vtbl_ptr_type(type)
+ struct type *type;
+{
+ char *typename = type_name_no_tag (type);
+ /* This was what it was for gcc 2.4.5 and earlier. */
+ static const char vtbl_ptr_name_old[] =
+ { CPLUS_MARKER,'v','t','b','l','_','p','t','r','_','t','y','p','e', 0 };
+ /* It was changed to this after 2.4.5. */
+ static const char vtbl_ptr_name[] =
+ { '_','_','v','t','b','l','_','p','t','r','_','t','y','p','e', 0 };
+
+ return (typename != NULL
+ && (STREQ (typename, vtbl_ptr_name)
+ || STREQ (typename, vtbl_ptr_name_old)));
+}
+
+/* Return truth value for the assertion that TYPE is of the type
+ "pointer to virtual function table". */
+
+int
+cp_is_vtbl_member(type)
+ struct type *type;
+{
+ if (TYPE_CODE (type) == TYPE_CODE_PTR)
+ type = TYPE_TARGET_TYPE (type);
+ else
+ return 0;
+
+ if (TYPE_CODE (type) == TYPE_CODE_ARRAY
+ && TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_STRUCT)
+ /* Virtual functions tables are full of pointers to virtual functions. */
+ return cp_is_vtbl_ptr_type (TYPE_TARGET_TYPE (type));
+ return 0;
+}
+
+/* Mutually recursive subroutines of cplus_print_value and c_val_print to
+ print out a structure's fields: cp_print_value_fields and cplus_print_value.
+
+ TYPE, VALADDR, STREAM, RECURSE, and PRETTY have the
+ same meanings as in cplus_print_value and c_val_print.
+
+ DONT_PRINT is an array of baseclass types that we
+ should not print, or zero if called from top level. */
+
+void
+cp_print_value_fields (type, valaddr, stream, format, recurse, pretty,
+ dont_print)
+ struct type *type;
+ char *valaddr;
+ FILE *stream;
+ int format;
+ int recurse;
+ enum val_prettyprint pretty;
+ struct type **dont_print;
+{
+ int i, len, n_baseclasses;
+
+ check_stub_type (type);
+
+ fprintf_filtered (stream, "{");
+ len = TYPE_NFIELDS (type);
+ n_baseclasses = TYPE_N_BASECLASSES (type);
+
+ /* Print out baseclasses such that we don't print
+ duplicates of virtual baseclasses. */
+ if (n_baseclasses > 0)
+ cplus_print_value (type, valaddr, stream, format, recurse+1, pretty,
+ dont_print);
+
+ if (!len && n_baseclasses == 1)
+ fprintf_filtered (stream, "<No data fields>");
+ else
+ {
+ extern int inspect_it;
+ int fields_seen = 0;
+
+ for (i = n_baseclasses; i < len; i++)
+ {
+ /* Check if static field */
+ if (TYPE_FIELD_STATIC (type, i))
+ continue;
+ if (fields_seen)
+ fprintf_filtered (stream, ", ");
+ else if (n_baseclasses > 0)
+ {
+ if (pretty)
+ {
+ fprintf_filtered (stream, "\n");
+ print_spaces_filtered (2 + 2 * recurse, stream);
+ fputs_filtered ("members of ", stream);
+ fputs_filtered (type_name_no_tag (type), stream);
+ fputs_filtered (": ", stream);
+ }
+ }
+ fields_seen = 1;
+
+ if (pretty)
+ {
+ fprintf_filtered (stream, "\n");
+ print_spaces_filtered (2 + 2 * recurse, stream);
+ }
+ else
+ {
+ wrap_here (n_spaces (2 + 2 * recurse));
+ }
+ if (inspect_it)
+ {
+ if (TYPE_CODE (TYPE_FIELD_TYPE (type, i)) == TYPE_CODE_PTR)
+ fputs_filtered ("\"( ptr \"", stream);
+ else
+ fputs_filtered ("\"( nodef \"", stream);
+ fprintf_symbol_filtered (stream, TYPE_FIELD_NAME (type, i),
+ language_cplus,
+ DMGL_PARAMS | DMGL_ANSI);
+ fputs_filtered ("\" \"", stream);
+ fprintf_symbol_filtered (stream, TYPE_FIELD_NAME (type, i),
+ language_cplus,
+ DMGL_PARAMS | DMGL_ANSI);
+ fputs_filtered ("\") \"", stream);
+ }
+ else
+ {
+ fprintf_symbol_filtered (stream, TYPE_FIELD_NAME (type, i),
+ language_cplus,
+ DMGL_PARAMS | DMGL_ANSI);
+ fputs_filtered (" = ", stream);
+ }
+ if (TYPE_FIELD_PACKED (type, i))
+ {
+ value v;
+
+ /* Bitfields require special handling, especially due to byte
+ order problems. */
+ v = value_from_longest (TYPE_FIELD_TYPE (type, i),
+ unpack_field_as_long (type, valaddr, i));
+
+ c_val_print (TYPE_FIELD_TYPE (type, i), VALUE_CONTENTS (v), 0,
+ stream, format, 0, recurse + 1, pretty);
+ }
+ else
+ {
+ c_val_print (TYPE_FIELD_TYPE (type, i),
+ valaddr + TYPE_FIELD_BITPOS (type, i) / 8,
+ 0, stream, format, 0, recurse + 1, pretty);
+ }
+ }
+ if (pretty)
+ {
+ fprintf_filtered (stream, "\n");
+ print_spaces_filtered (2 * recurse, stream);
+ }
+ }
+ fprintf_filtered (stream, "}");
+}
+
+/* Special val_print routine to avoid printing multiple copies of virtual
+ baseclasses. */
+
+static void
+cplus_print_value (type, valaddr, stream, format, recurse, pretty, dont_print)
+ struct type *type;
+ char *valaddr;
+ FILE *stream;
+ int format;
+ int recurse;
+ enum val_prettyprint pretty;
+ struct type **dont_print;
+{
+ struct obstack tmp_obstack;
+ struct type **last_dont_print
+ = (struct type **)obstack_next_free (&dont_print_obstack);
+ int i, n_baseclasses = TYPE_N_BASECLASSES (type);
+
+ if (dont_print == 0)
+ {
+ /* If we're at top level, carve out a completely fresh
+ chunk of the obstack and use that until this particular
+ invocation returns. */
+ tmp_obstack = dont_print_obstack;
+ /* Bump up the high-water mark. Now alpha is omega. */
+ obstack_finish (&dont_print_obstack);
+ }
+
+ for (i = 0; i < n_baseclasses; i++)
+ {
+ char *baddr;
+ int err;
+ char *basename = TYPE_NAME (TYPE_BASECLASS (type, i));
+
+ if (BASETYPE_VIA_VIRTUAL (type, i))
+ {
+ struct type **first_dont_print
+ = (struct type **)obstack_base (&dont_print_obstack);
+
+ int j = (struct type **)obstack_next_free (&dont_print_obstack)
+ - first_dont_print;
+
+ while (--j >= 0)
+ if (TYPE_BASECLASS (type, i) == first_dont_print[j])
+ goto flush_it;
+
+ obstack_ptr_grow (&dont_print_obstack, TYPE_BASECLASS (type, i));
+ }
+
+ /* Fix to use baseclass_offset instead. FIXME */
+ baddr = baseclass_addr (type, i, valaddr, 0, &err);
+ if (err == 0 && baddr == 0)
+ error ("could not find virtual baseclass %s\n",
+ basename ? basename : "");
+
+ if (pretty)
+ {
+ fprintf_filtered (stream, "\n");
+ print_spaces_filtered (2 * recurse, stream);
+ }
+ fputs_filtered ("<", stream);
+ /* Not sure what the best notation is in the case where there is no
+ baseclass name. */
+ fputs_filtered (basename ? basename : "", stream);
+ fputs_filtered ("> = ", stream);
+ if (err != 0)
+ fprintf_filtered (stream,
+ "<invalid address 0x%lx>", (unsigned long) baddr);
+ else
+ cp_print_value_fields (TYPE_BASECLASS (type, i), baddr, stream, format,
+ recurse, pretty,
+ (struct type **) obstack_base (&dont_print_obstack));
+ fputs_filtered (", ", stream);
+
+ flush_it:
+ ;
+ }
+
+ if (dont_print == 0)
+ {
+ /* Free the space used to deal with the printing
+ of this type from top level. */
+ obstack_free (&dont_print_obstack, last_dont_print);
+ /* Reset watermark so that we can continue protecting
+ ourselves from whatever we were protecting ourselves. */
+ dont_print_obstack = tmp_obstack;
+ }
+}
+
+void
+cp_print_class_member (valaddr, domain, stream, prefix)
+ char *valaddr;
+ struct type *domain;
+ FILE *stream;
+ char *prefix;
+{
+
+ /* VAL is a byte offset into the structure type DOMAIN.
+ Find the name of the field for that offset and
+ print it. */
+ int extra = 0;
+ int bits = 0;
+ register unsigned int i;
+ unsigned len = TYPE_NFIELDS (domain);
+ /* @@ Make VAL into bit offset */
+ LONGEST val = unpack_long (builtin_type_int, valaddr) << 3;
+ for (i = TYPE_N_BASECLASSES (domain); i < len; i++)
+ {
+ int bitpos = TYPE_FIELD_BITPOS (domain, i);
+ QUIT;
+ if (val == bitpos)
+ break;
+ if (val < bitpos && i != 0)
+ {
+ /* Somehow pointing into a field. */
+ i -= 1;
+ extra = (val - TYPE_FIELD_BITPOS (domain, i));
+ if (extra & 0x7)
+ bits = 1;
+ else
+ extra >>= 3;
+ break;
+ }
+ }
+ if (i < len)
+ {
+ char *name;
+ fprintf_filtered (stream, prefix);
+ name = type_name_no_tag (domain);
+ if (name)
+ fputs_filtered (name, stream);
+ else
+ c_type_print_base (domain, stream, 0, 0);
+ fprintf_filtered (stream, "::");
+ fputs_filtered (TYPE_FIELD_NAME (domain, i), stream);
+ if (extra)
+ fprintf_filtered (stream, " + %d bytes", extra);
+ if (bits)
+ fprintf_filtered (stream, " (offset in bits)");
+ }
+ else
+ fprintf_filtered (stream, "%d", val >> 3);
+}
+
+void
+_initialize_cp_valprint ()
+{
+ add_show_from_set
+ (add_set_cmd ("vtbl", class_support, var_boolean, (char *)&vtblprint,
+ "Set printing of C++ virtual function tables.",
+ &setprintlist),
+ &showprintlist);
+
+ add_show_from_set
+ (add_set_cmd ("object", class_support, var_boolean, (char *)&objectprint,
+ "Set printing of object's derived type based on vtable info.",
+ &setprintlist),
+ &showprintlist);
+
+ /* Give people the defaults which they are used to. */
+ objectprint = 0;
+ vtblprint = 0;
+ obstack_begin (&dont_print_obstack, 32 * sizeof (struct type *));
+}
diff --git a/gnu/usr.bin/gdb/gdb/dbxread.c b/gnu/usr.bin/gdb/gdb/dbxread.c
new file mode 100644
index 0000000..7f4ef26
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/dbxread.c
@@ -0,0 +1,2234 @@
+/* Read dbx symbol tables and convert to internal format, for GDB.
+ Copyright 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993
+ Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This module provides three functions: dbx_symfile_init,
+ which initializes to read a symbol file; dbx_new_init, which
+ discards existing cached information when all symbols are being
+ discarded; and dbx_symfile_read, which reads a symbol table
+ from a file.
+
+ dbx_symfile_read only does the minimum work necessary for letting the
+ user "name" things symbolically; it does not read the entire symtab.
+ Instead, it reads the external and static symbols and puts them in partial
+ symbol tables. When more extensive information is requested of a
+ file, the corresponding partial symbol table is mutated into a full
+ fledged symbol table by going back and reading the symbols
+ for real. dbx_psymtab_to_symtab() is the function that does this */
+
+#include "defs.h"
+#include <string.h>
+
+#if defined(USG) || defined(__CYGNUSCLIB__)
+#include <sys/types.h>
+#include <fcntl.h>
+#endif
+
+#include <obstack.h>
+#include <sys/param.h>
+#ifndef NO_SYS_FILE
+#include <sys/file.h>
+#endif
+#include <sys/stat.h>
+#include <ctype.h>
+#include "symtab.h"
+#include "breakpoint.h"
+#include "command.h"
+#include "target.h"
+#include "gdbcore.h" /* for bfd stuff */
+#include "libbfd.h" /* FIXME Secret internal BFD stuff (bfd_read) */
+#include "libaout.h" /* FIXME Secret internal BFD stuff for a.out */
+#include "symfile.h"
+#include "objfiles.h"
+#include "buildsym.h"
+#include "stabsread.h"
+#include "gdb-stabs.h"
+#include "demangle.h"
+#include "language.h" /* Needed inside partial-stab.h */
+#include "complaints.h"
+
+#include "aout/aout64.h"
+#include "aout/stab_gnu.h" /* We always use GNU stabs, not native, now */
+
+#if !defined (SEEK_SET)
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#endif
+
+/* Each partial symbol table entry contains a pointer to private data for the
+ read_symtab() function to use when expanding a partial symbol table entry
+ to a full symbol table entry.
+
+ For dbxread this structure contains the offset within the file symbol table
+ of first local symbol for this file, and length (in bytes) of the section
+ of the symbol table devoted to this file's symbols (actually, the section
+ bracketed may contain more than just this file's symbols). It also contains
+ further information needed to locate the symbols if they are in an ELF file.
+
+ If ldsymlen is 0, the only reason for this thing's existence is the
+ dependency list. Nothing else will happen when it is read in. */
+
+#define LDSYMOFF(p) (((struct symloc *)((p)->read_symtab_private))->ldsymoff)
+#define LDSYMLEN(p) (((struct symloc *)((p)->read_symtab_private))->ldsymlen)
+#define SYMLOC(p) ((struct symloc *)((p)->read_symtab_private))
+#define SYMBOL_SIZE(p) (SYMLOC(p)->symbol_size)
+#define SYMBOL_OFFSET(p) (SYMLOC(p)->symbol_offset)
+#define STRING_OFFSET(p) (SYMLOC(p)->string_offset)
+#define FILE_STRING_OFFSET(p) (SYMLOC(p)->file_string_offset)
+
+struct symloc {
+ int ldsymoff;
+ int ldsymlen;
+ int symbol_size;
+ int symbol_offset;
+ int string_offset;
+ int file_string_offset;
+};
+
+/* Macro to determine which symbols to ignore when reading the first symbol
+ of a file. Some machines override this definition. */
+#ifndef IGNORE_SYMBOL
+/* This code is used on Ultrix systems. Ignore it */
+#define IGNORE_SYMBOL(type) (type == (int)N_NSYMS)
+#endif
+
+/* Macro for name of symbol to indicate a file compiled with gcc. */
+#ifndef GCC_COMPILED_FLAG_SYMBOL
+#define GCC_COMPILED_FLAG_SYMBOL "gcc_compiled."
+#endif
+
+/* Macro for name of symbol to indicate a file compiled with gcc2. */
+#ifndef GCC2_COMPILED_FLAG_SYMBOL
+#define GCC2_COMPILED_FLAG_SYMBOL "gcc2_compiled."
+#endif
+
+/* Define this as 1 if a pcc declaration of a char or short argument
+ gives the correct address. Otherwise assume pcc gives the
+ address of the corresponding int, which is not the same on a
+ big-endian machine. */
+
+#ifndef BELIEVE_PCC_PROMOTION
+#define BELIEVE_PCC_PROMOTION 0
+#endif
+
+/* Remember what we deduced to be the source language of this psymtab. */
+
+static enum language psymtab_language = language_unknown;
+
+/* Nonzero means give verbose info on gdb action. From main.c. */
+extern int info_verbose;
+
+/* The BFD for this file -- implicit parameter to next_symbol_text. */
+
+static bfd *symfile_bfd;
+
+/* The size of each symbol in the symbol file (in external form).
+ This is set by dbx_symfile_read when building psymtabs, and by
+ dbx_psymtab_to_symtab when building symtabs. */
+
+static unsigned symbol_size;
+
+/* This is the offset of the symbol table in the executable file */
+static unsigned symbol_table_offset;
+
+/* This is the offset of the string table in the executable file */
+static unsigned string_table_offset;
+
+/* For elf+stab executables, the n_strx field is not a simple index
+ into the string table. Instead, each .o file has a base offset
+ in the string table, and the associated symbols contain offsets
+ from this base. The following two variables contain the base
+ offset for the current and next .o files. */
+static unsigned int file_string_table_offset;
+static unsigned int next_file_string_table_offset;
+
+/* Complaints about the symbols we have encountered. */
+
+struct complaint lbrac_complaint =
+ {"bad block start address patched", 0, 0};
+
+struct complaint string_table_offset_complaint =
+ {"bad string table offset in symbol %d", 0, 0};
+
+struct complaint unknown_symtype_complaint =
+ {"unknown symbol type %s", 0, 0};
+
+struct complaint unknown_symchar_complaint =
+ {"unknown symbol type character `%c'", 0, 0};
+
+struct complaint lbrac_rbrac_complaint =
+ {"block start larger than block end", 0, 0};
+
+struct complaint lbrac_unmatched_complaint =
+ {"unmatched N_LBRAC before symtab pos %d", 0, 0};
+
+struct complaint lbrac_mismatch_complaint =
+ {"N_LBRAC/N_RBRAC symbol mismatch at symtab pos %d", 0, 0};
+
+struct complaint repeated_header_complaint =
+ {"\"repeated\" header file not previously seen, at symtab pos %d", 0, 0};
+
+struct complaint repeated_header_name_complaint =
+ {"\"repeated\" header file not previously seen, named %s", 0, 0};
+
+/* During initial symbol readin, we need to have a structure to keep
+ track of which psymtabs have which bincls in them. This structure
+ is used during readin to setup the list of dependencies within each
+ partial symbol table. */
+
+struct header_file_location
+{
+ char *name; /* Name of header file */
+ int instance; /* See above */
+ struct partial_symtab *pst; /* Partial symtab that has the
+ BINCL/EINCL defs for this file */
+};
+
+/* The actual list and controling variables */
+static struct header_file_location *bincl_list, *next_bincl;
+static int bincls_allocated;
+
+/* Local function prototypes */
+
+static void
+free_header_files PARAMS ((void));
+
+static void
+init_header_files PARAMS ((void));
+
+static void
+read_ofile_symtab PARAMS ((struct partial_symtab *));
+
+static void
+dbx_psymtab_to_symtab PARAMS ((struct partial_symtab *));
+
+static void
+dbx_psymtab_to_symtab_1 PARAMS ((struct partial_symtab *));
+
+static void
+read_dbx_symtab PARAMS ((struct section_offsets *, struct objfile *,
+ CORE_ADDR, int));
+
+static void
+free_bincl_list PARAMS ((struct objfile *));
+
+static struct partial_symtab *
+find_corresponding_bincl_psymtab PARAMS ((char *, int));
+
+static void
+add_bincl_to_list PARAMS ((struct partial_symtab *, char *, int));
+
+static void
+init_bincl_list PARAMS ((int, struct objfile *));
+
+static void
+init_psymbol_list PARAMS ((struct objfile *));
+
+static char *
+dbx_next_symbol_text PARAMS ((void));
+
+static void
+fill_symbuf PARAMS ((bfd *));
+
+static void
+dbx_symfile_init PARAMS ((struct objfile *));
+
+static void
+dbx_new_init PARAMS ((struct objfile *));
+
+static void
+dbx_symfile_read PARAMS ((struct objfile *, struct section_offsets *, int));
+
+static void
+dbx_symfile_finish PARAMS ((struct objfile *));
+
+static void
+record_minimal_symbol PARAMS ((char *, CORE_ADDR, int, struct objfile *));
+
+static void
+add_new_header_file PARAMS ((char *, int));
+
+static void
+add_old_header_file PARAMS ((char *, int));
+
+static void
+add_this_object_header_file PARAMS ((int));
+
+/* Free up old header file tables */
+
+static void
+free_header_files ()
+{
+ register int i;
+
+ if (header_files != NULL)
+ {
+ for (i = 0; i < n_header_files; i++)
+ {
+ free (header_files[i].name);
+ }
+ free ((PTR)header_files);
+ header_files = NULL;
+ n_header_files = 0;
+ }
+ if (this_object_header_files)
+ {
+ free ((PTR)this_object_header_files);
+ this_object_header_files = NULL;
+ }
+ n_allocated_header_files = 0;
+ n_allocated_this_object_header_files = 0;
+}
+
+/* Allocate new header file tables */
+
+static void
+init_header_files ()
+{
+ n_header_files = 0;
+ n_allocated_header_files = 10;
+ header_files = (struct header_file *)
+ xmalloc (10 * sizeof (struct header_file));
+
+ n_allocated_this_object_header_files = 10;
+ this_object_header_files = (int *) xmalloc (10 * sizeof (int));
+}
+
+/* Add header file number I for this object file
+ at the next successive FILENUM. */
+
+static void
+add_this_object_header_file (i)
+ int i;
+{
+ if (n_this_object_header_files == n_allocated_this_object_header_files)
+ {
+ n_allocated_this_object_header_files *= 2;
+ this_object_header_files
+ = (int *) xrealloc ((char *) this_object_header_files,
+ n_allocated_this_object_header_files * sizeof (int));
+ }
+
+ this_object_header_files[n_this_object_header_files++] = i;
+}
+
+/* Add to this file an "old" header file, one already seen in
+ a previous object file. NAME is the header file's name.
+ INSTANCE is its instance code, to select among multiple
+ symbol tables for the same header file. */
+
+static void
+add_old_header_file (name, instance)
+ char *name;
+ int instance;
+{
+ register struct header_file *p = header_files;
+ register int i;
+
+ for (i = 0; i < n_header_files; i++)
+ if (STREQ (p[i].name, name) && instance == p[i].instance)
+ {
+ add_this_object_header_file (i);
+ return;
+ }
+ complain (&repeated_header_complaint, symnum);
+ complain (&repeated_header_name_complaint, name);
+}
+
+/* Add to this file a "new" header file: definitions for its types follow.
+ NAME is the header file's name.
+ Most often this happens only once for each distinct header file,
+ but not necessarily. If it happens more than once, INSTANCE has
+ a different value each time, and references to the header file
+ use INSTANCE values to select among them.
+
+ dbx output contains "begin" and "end" markers for each new header file,
+ but at this level we just need to know which files there have been;
+ so we record the file when its "begin" is seen and ignore the "end". */
+
+static void
+add_new_header_file (name, instance)
+ char *name;
+ int instance;
+{
+ register int i;
+
+ /* Make sure there is room for one more header file. */
+
+ if (n_header_files == n_allocated_header_files)
+ {
+ n_allocated_header_files *= 2;
+ header_files = (struct header_file *)
+ xrealloc ((char *) header_files,
+ (n_allocated_header_files * sizeof (struct header_file)));
+ }
+
+ /* Create an entry for this header file. */
+
+ i = n_header_files++;
+ header_files[i].name = savestring (name, strlen(name));
+ header_files[i].instance = instance;
+ header_files[i].length = 10;
+ header_files[i].vector
+ = (struct type **) xmalloc (10 * sizeof (struct type *));
+ memset (header_files[i].vector, 0, 10 * sizeof (struct type *));
+
+ add_this_object_header_file (i);
+}
+
+#if 0
+static struct type **
+explicit_lookup_type (real_filenum, index)
+ int real_filenum, index;
+{
+ register struct header_file *f = &header_files[real_filenum];
+
+ if (index >= f->length)
+ {
+ f->length *= 2;
+ f->vector = (struct type **)
+ xrealloc (f->vector, f->length * sizeof (struct type *));
+ memset (&f->vector[f->length / 2],
+ '\0', f->length * sizeof (struct type *) / 2);
+ }
+ return &f->vector[index];
+}
+#endif
+
+static void
+record_minimal_symbol (name, address, type, objfile)
+ char *name;
+ CORE_ADDR address;
+ int type;
+ struct objfile *objfile;
+{
+ enum minimal_symbol_type ms_type;
+
+ switch (type)
+ {
+ case N_TEXT | N_EXT: ms_type = mst_text; break;
+ case N_DATA | N_EXT: ms_type = mst_data; break;
+ case N_BSS | N_EXT: ms_type = mst_bss; break;
+ case N_ABS | N_EXT: ms_type = mst_abs; break;
+#ifdef N_SETV
+ case N_SETV | N_EXT: ms_type = mst_data; break;
+ case N_SETV:
+ /* I don't think this type actually exists; since a N_SETV is the result
+ of going over many .o files, it doesn't make sense to have one
+ file local. */
+ ms_type = mst_file_data;
+ break;
+#endif
+ case N_TEXT:
+ /* Don't put gcc_compiled, __gnu_compiled_cplus, and friends into
+ the minimal symbols, because if there is also another symbol
+ at the same address (e.g. the first function of the file),
+ lookup_minimal_symbol_by_pc would have no way of getting the
+ right one. */
+ if (name[0] == 'g'
+ && (strcmp (name, GCC_COMPILED_FLAG_SYMBOL) == 0
+ || strcmp (name, GCC2_COMPILED_FLAG_SYMBOL) == 0))
+ return;
+
+ {
+ char *tempstring = name;
+ if (tempstring[0] == bfd_get_symbol_leading_char (objfile->obfd))
+ ++tempstring;
+ if (STREQN (tempstring, "__gnu_compiled", 14))
+ return;
+ }
+
+ case N_NBTEXT:
+ case N_FN:
+ case N_FN_SEQ:
+ ms_type = mst_file_text;
+ break;
+
+ case N_DATA:
+ ms_type = mst_file_data;
+
+ /* Check for __DYNAMIC, which is used by Sun shared libraries.
+ Record it as global even if it's local, not global, so
+ lookup_minimal_symbol can find it. We don't check symbol_leading_char
+ because for SunOS4 it always is '_'. */
+ if (name[8] == 'C' && STREQ ("__DYNAMIC", name))
+ ms_type = mst_data;
+
+ /* Same with virtual function tables, both global and static. */
+ {
+ char *tempstring = name;
+ if (tempstring[0] == bfd_get_symbol_leading_char (objfile->obfd))
+ ++tempstring;
+ if (VTBL_PREFIX_P ((tempstring)))
+ ms_type = mst_data;
+ }
+ break;
+
+ case N_BSS:
+ ms_type = mst_file_bss;
+ break;
+
+ default: ms_type = mst_unknown; break;
+ }
+
+ prim_record_minimal_symbol
+ (obsavestring (name, strlen (name), &objfile -> symbol_obstack),
+ address,
+ ms_type);
+}
+
+/* Scan and build partial symbols for a symbol file.
+ We have been initialized by a call to dbx_symfile_init, which
+ put all the relevant info into a "struct dbx_symfile_info",
+ hung off the objfile structure.
+
+ SECTION_OFFSETS contains offsets relative to which the symbols in the
+ various sections are (depending where the sections were actually loaded).
+ MAINLINE is true if we are reading the main symbol
+ table (as opposed to a shared lib or dynamically loaded file). */
+
+static void
+dbx_symfile_read (objfile, section_offsets, mainline)
+ struct objfile *objfile;
+ struct section_offsets *section_offsets;
+ int mainline; /* FIXME comments above */
+{
+ bfd *sym_bfd;
+ int val;
+ struct cleanup *back_to;
+
+ sym_bfd = objfile->obfd;
+ val = bfd_seek (objfile->obfd, DBX_SYMTAB_OFFSET (objfile), SEEK_SET);
+ if (val < 0)
+ perror_with_name (objfile->name);
+
+ /* If we are reinitializing, or if we have never loaded syms yet, init */
+ if (mainline || objfile->global_psymbols.size == 0 || objfile->static_psymbols.size == 0)
+ init_psymbol_list (objfile);
+
+ symbol_size = DBX_SYMBOL_SIZE (objfile);
+ symbol_table_offset = DBX_SYMTAB_OFFSET (objfile);
+
+ pending_blocks = 0;
+ back_to = make_cleanup (really_free_pendings, 0);
+
+ init_minimal_symbol_collection ();
+ make_cleanup (discard_minimal_symbols, 0);
+
+ /* Now that the symbol table data of the executable file are all in core,
+ process them and define symbols accordingly. */
+
+ read_dbx_symtab (section_offsets, objfile,
+ bfd_section_vma (sym_bfd, DBX_TEXT_SECT (objfile)),
+ bfd_section_size (sym_bfd, DBX_TEXT_SECT (objfile)));
+
+ /* Install any minimal symbols that have been collected as the current
+ minimal symbols for this objfile. */
+
+ install_minimal_symbols (objfile);
+
+ if (!have_partial_symbols ()) {
+ wrap_here ("");
+ printf_filtered ("(no debugging symbols found)...");
+ wrap_here ("");
+ }
+
+ do_cleanups (back_to);
+}
+
+/* Initialize anything that needs initializing when a completely new
+ symbol file is specified (not just adding some symbols from another
+ file, e.g. a shared library). */
+
+static void
+dbx_new_init (ignore)
+ struct objfile *ignore;
+{
+ stabsread_new_init ();
+ buildsym_new_init ();
+ init_header_files ();
+}
+
+
+/* dbx_symfile_init ()
+ is the dbx-specific initialization routine for reading symbols.
+ It is passed a struct objfile which contains, among other things,
+ the BFD for the file whose symbols are being read, and a slot for a pointer
+ to "private data" which we fill with goodies.
+
+ We read the string table into malloc'd space and stash a pointer to it.
+
+ Since BFD doesn't know how to read debug symbols in a format-independent
+ way (and may never do so...), we have to do it ourselves. We will never
+ be called unless this is an a.out (or very similar) file.
+ FIXME, there should be a cleaner peephole into the BFD environment here. */
+
+#define DBX_STRINGTAB_SIZE_SIZE sizeof(long) /* FIXME */
+
+static void
+dbx_symfile_init (objfile)
+ struct objfile *objfile;
+{
+ int val;
+ bfd *sym_bfd = objfile->obfd;
+ char *name = bfd_get_filename (sym_bfd);
+ unsigned char size_temp[DBX_STRINGTAB_SIZE_SIZE];
+
+ /* Allocate struct to keep track of the symfile */
+ objfile->sym_stab_info = (PTR)
+ xmmalloc (objfile -> md, sizeof (struct dbx_symfile_info));
+
+ /* FIXME POKING INSIDE BFD DATA STRUCTURES */
+#define STRING_TABLE_OFFSET (sym_bfd->origin + obj_str_filepos (sym_bfd))
+#define SYMBOL_TABLE_OFFSET (sym_bfd->origin + obj_sym_filepos (sym_bfd))
+
+ /* FIXME POKING INSIDE BFD DATA STRUCTURES */
+
+ DBX_SYMFILE_INFO (objfile)->stab_section_info = NULL;
+ DBX_TEXT_SECT (objfile) = bfd_get_section_by_name (sym_bfd, ".text");
+ if (!DBX_TEXT_SECT (objfile))
+ error ("Can't find .text section in symbol file");
+
+ DBX_SYMBOL_SIZE (objfile) = obj_symbol_entry_size (sym_bfd);
+ DBX_SYMCOUNT (objfile) = bfd_get_symcount (sym_bfd);
+ DBX_SYMTAB_OFFSET (objfile) = SYMBOL_TABLE_OFFSET;
+
+ /* Read the string table and stash it away in the psymbol_obstack. It is
+ only needed as long as we need to expand psymbols into full symbols,
+ so when we blow away the psymbol the string table goes away as well.
+ Note that gdb used to use the results of attempting to malloc the
+ string table, based on the size it read, as a form of sanity check
+ for botched byte swapping, on the theory that a byte swapped string
+ table size would be so totally bogus that the malloc would fail. Now
+ that we put in on the psymbol_obstack, we can't do this since gdb gets
+ a fatal error (out of virtual memory) if the size is bogus. We can
+ however at least check to see if the size is less than the size of
+ the size field itself, or larger than the size of the entire file.
+ Note that all valid string tables have a size greater than zero, since
+ the bytes used to hold the size are included in the count. */
+
+ if (STRING_TABLE_OFFSET == 0)
+ {
+ /* It appears that with the existing bfd code, STRING_TABLE_OFFSET
+ will never be zero, even when there is no string table. This
+ would appear to be a bug in bfd. */
+ DBX_STRINGTAB_SIZE (objfile) = 0;
+ DBX_STRINGTAB (objfile) = NULL;
+ }
+ else
+ {
+ val = bfd_seek (sym_bfd, STRING_TABLE_OFFSET, SEEK_SET);
+ if (val < 0)
+ perror_with_name (name);
+
+ memset ((PTR) size_temp, 0, sizeof (size_temp));
+ val = bfd_read ((PTR) size_temp, sizeof (size_temp), 1, sym_bfd);
+ if (val < 0)
+ {
+ perror_with_name (name);
+ }
+ else if (val == 0)
+ {
+ /* With the existing bfd code, STRING_TABLE_OFFSET will be set to
+ EOF if there is no string table, and attempting to read the size
+ from EOF will read zero bytes. */
+ DBX_STRINGTAB_SIZE (objfile) = 0;
+ DBX_STRINGTAB (objfile) = NULL;
+ }
+ else
+ {
+ /* Read some data that would appear to be the string table size.
+ If there really is a string table, then it is probably the right
+ size. Byteswap if necessary and validate the size. Note that
+ the minimum is DBX_STRINGTAB_SIZE_SIZE. If we just read some
+ random data that happened to be at STRING_TABLE_OFFSET, because
+ bfd can't tell us there is no string table, the sanity checks may
+ or may not catch this. */
+ DBX_STRINGTAB_SIZE (objfile) = bfd_h_get_32 (sym_bfd, size_temp);
+
+ if (DBX_STRINGTAB_SIZE (objfile) < sizeof (size_temp)
+ || DBX_STRINGTAB_SIZE (objfile) > bfd_get_size (sym_bfd))
+ error ("ridiculous string table size (%d bytes).",
+ DBX_STRINGTAB_SIZE (objfile));
+
+ DBX_STRINGTAB (objfile) =
+ (char *) obstack_alloc (&objfile -> psymbol_obstack,
+ DBX_STRINGTAB_SIZE (objfile));
+
+ /* Now read in the string table in one big gulp. */
+
+ val = bfd_seek (sym_bfd, STRING_TABLE_OFFSET, SEEK_SET);
+ if (val < 0)
+ perror_with_name (name);
+ val = bfd_read (DBX_STRINGTAB (objfile), DBX_STRINGTAB_SIZE (objfile), 1,
+ sym_bfd);
+ if (val != DBX_STRINGTAB_SIZE (objfile))
+ perror_with_name (name);
+ }
+ }
+}
+
+/* Perform any local cleanups required when we are done with a particular
+ objfile. I.E, we are in the process of discarding all symbol information
+ for an objfile, freeing up all memory held for it, and unlinking the
+ objfile struct from the global list of known objfiles. */
+
+static void
+dbx_symfile_finish (objfile)
+ struct objfile *objfile;
+{
+ if (objfile->sym_stab_info != NULL)
+ {
+ mfree (objfile -> md, objfile->sym_stab_info);
+ }
+ free_header_files ();
+}
+
+
+/* Buffer for reading the symbol table entries. */
+static struct internal_nlist symbuf[4096];
+static int symbuf_idx;
+static int symbuf_end;
+
+/* Name of last function encountered. Used in Solaris to approximate
+ object file boundaries. */
+static char *last_function_name;
+
+/* The address in memory of the string table of the object file we are
+ reading (which might not be the "main" object file, but might be a
+ shared library or some other dynamically loaded thing). This is set
+ by read_dbx_symtab when building psymtabs, and by read_ofile_symtab
+ when building symtabs, and is used only by next_symbol_text. */
+static char *stringtab_global;
+
+/* Refill the symbol table input buffer
+ and set the variables that control fetching entries from it.
+ Reports an error if no data available.
+ This function can read past the end of the symbol table
+ (into the string table) but this does no harm. */
+
+static void
+fill_symbuf (sym_bfd)
+ bfd *sym_bfd;
+{
+ int nbytes = bfd_read ((PTR)symbuf, sizeof (symbuf), 1, sym_bfd);
+ if (nbytes < 0)
+ perror_with_name (bfd_get_filename (sym_bfd));
+ else if (nbytes == 0)
+ error ("Premature end of file reading symbol table");
+ symbuf_end = nbytes / symbol_size;
+ symbuf_idx = 0;
+}
+
+#define SWAP_SYMBOL(symp, abfd) \
+ { \
+ (symp)->n_strx = bfd_h_get_32(abfd, \
+ (unsigned char *)&(symp)->n_strx); \
+ (symp)->n_desc = bfd_h_get_16 (abfd, \
+ (unsigned char *)&(symp)->n_desc); \
+ (symp)->n_value = bfd_h_get_32 (abfd, \
+ (unsigned char *)&(symp)->n_value); \
+ }
+
+/* Invariant: The symbol pointed to by symbuf_idx is the first one
+ that hasn't been swapped. Swap the symbol at the same time
+ that symbuf_idx is incremented. */
+
+/* dbx allows the text of a symbol name to be continued into the
+ next symbol name! When such a continuation is encountered
+ (a \ at the end of the text of a name)
+ call this function to get the continuation. */
+
+static char *
+dbx_next_symbol_text ()
+{
+ if (symbuf_idx == symbuf_end)
+ fill_symbuf (symfile_bfd);
+ symnum++;
+ SWAP_SYMBOL(&symbuf[symbuf_idx], symfile_bfd);
+ return symbuf[symbuf_idx++].n_strx + stringtab_global
+ + file_string_table_offset;
+}
+
+/* Initializes storage for all of the partial symbols that will be
+ created by read_dbx_symtab and subsidiaries. */
+
+static void
+init_psymbol_list (objfile)
+ struct objfile *objfile;
+{
+ /* Free any previously allocated psymbol lists. */
+ if (objfile -> global_psymbols.list)
+ mfree (objfile -> md, (PTR)objfile -> global_psymbols.list);
+ if (objfile -> static_psymbols.list)
+ mfree (objfile -> md, (PTR)objfile -> static_psymbols.list);
+
+ /* Current best guess is that there are approximately a twentieth
+ of the total symbols (in a debugging file) are global or static
+ oriented symbols */
+ objfile -> global_psymbols.size = DBX_SYMCOUNT (objfile) / 10;
+ objfile -> static_psymbols.size = DBX_SYMCOUNT (objfile) / 10;
+ objfile -> global_psymbols.next = objfile -> global_psymbols.list = (struct partial_symbol *)
+ xmmalloc (objfile -> md, objfile -> global_psymbols.size * sizeof (struct partial_symbol));
+ objfile -> static_psymbols.next = objfile -> static_psymbols.list = (struct partial_symbol *)
+ xmmalloc (objfile -> md, objfile -> static_psymbols.size * sizeof (struct partial_symbol));
+}
+
+/* Initialize the list of bincls to contain none and have some
+ allocated. */
+
+static void
+init_bincl_list (number, objfile)
+ int number;
+ struct objfile *objfile;
+{
+ bincls_allocated = number;
+ next_bincl = bincl_list = (struct header_file_location *)
+ xmmalloc (objfile -> md, bincls_allocated * sizeof(struct header_file_location));
+}
+
+/* Add a bincl to the list. */
+
+static void
+add_bincl_to_list (pst, name, instance)
+ struct partial_symtab *pst;
+ char *name;
+ int instance;
+{
+ if (next_bincl >= bincl_list + bincls_allocated)
+ {
+ int offset = next_bincl - bincl_list;
+ bincls_allocated *= 2;
+ bincl_list = (struct header_file_location *)
+ xmrealloc (pst->objfile->md, (char *)bincl_list,
+ bincls_allocated * sizeof (struct header_file_location));
+ next_bincl = bincl_list + offset;
+ }
+ next_bincl->pst = pst;
+ next_bincl->instance = instance;
+ next_bincl++->name = name;
+}
+
+/* Given a name, value pair, find the corresponding
+ bincl in the list. Return the partial symtab associated
+ with that header_file_location. */
+
+static struct partial_symtab *
+find_corresponding_bincl_psymtab (name, instance)
+ char *name;
+ int instance;
+{
+ struct header_file_location *bincl;
+
+ for (bincl = bincl_list; bincl < next_bincl; bincl++)
+ if (bincl->instance == instance
+ && STREQ (name, bincl->name))
+ return bincl->pst;
+
+ return (struct partial_symtab *) 0;
+}
+
+/* Free the storage allocated for the bincl list. */
+
+static void
+free_bincl_list (objfile)
+ struct objfile *objfile;
+{
+ mfree (objfile -> md, (PTR)bincl_list);
+ bincls_allocated = 0;
+}
+
+/* Given pointers to an a.out symbol table in core containing dbx
+ style data, setup partial_symtab's describing each source file for
+ which debugging information is available.
+ SYMFILE_NAME is the name of the file we are reading from
+ and SECTION_OFFSETS is the set of offsets for the various sections
+ of the file (a set of zeros if the mainline program). */
+
+static void
+read_dbx_symtab (section_offsets, objfile, text_addr, text_size)
+ struct section_offsets *section_offsets;
+ struct objfile *objfile;
+ CORE_ADDR text_addr;
+ int text_size;
+{
+ register struct internal_nlist *bufp = 0; /* =0 avoids gcc -Wall glitch */
+ register char *namestring;
+ int nsl;
+ int past_first_source_file = 0;
+ CORE_ADDR last_o_file_start = 0;
+ struct cleanup *back_to;
+ bfd *abfd;
+
+ /* End of the text segment of the executable file. */
+ CORE_ADDR end_of_text_addr;
+
+ /* Current partial symtab */
+ struct partial_symtab *pst;
+
+ /* List of current psymtab's include files */
+ char **psymtab_include_list;
+ int includes_allocated;
+ int includes_used;
+
+ /* Index within current psymtab dependency list */
+ struct partial_symtab **dependency_list;
+ int dependencies_used, dependencies_allocated;
+
+ /* FIXME. We probably want to change stringtab_global rather than add this
+ while processing every symbol entry. FIXME. */
+ file_string_table_offset = 0;
+ next_file_string_table_offset = 0;
+
+ stringtab_global = DBX_STRINGTAB (objfile);
+
+ pst = (struct partial_symtab *) 0;
+
+ includes_allocated = 30;
+ includes_used = 0;
+ psymtab_include_list = (char **) alloca (includes_allocated *
+ sizeof (char *));
+
+ dependencies_allocated = 30;
+ dependencies_used = 0;
+ dependency_list =
+ (struct partial_symtab **) alloca (dependencies_allocated *
+ sizeof (struct partial_symtab *));
+
+ /* Init bincl list */
+ init_bincl_list (20, objfile);
+ back_to = make_cleanup (free_bincl_list, objfile);
+
+ last_source_file = NULL;
+
+#ifdef END_OF_TEXT_DEFAULT
+ end_of_text_addr = END_OF_TEXT_DEFAULT;
+#else
+ end_of_text_addr = text_addr + section_offsets->offsets[SECT_OFF_TEXT]
+ + text_size; /* Relocate */
+#endif
+
+ symfile_bfd = objfile->obfd; /* For next_text_symbol */
+ abfd = objfile->obfd;
+ symbuf_end = symbuf_idx = 0;
+ next_symbol_text_func = dbx_next_symbol_text;
+
+ for (symnum = 0; symnum < DBX_SYMCOUNT (objfile); symnum++)
+ {
+ /* Get the symbol for this run and pull out some info */
+ QUIT; /* allow this to be interruptable */
+ if (symbuf_idx == symbuf_end)
+ fill_symbuf (abfd);
+ bufp = &symbuf[symbuf_idx++];
+
+ /*
+ * Special case to speed up readin.
+ */
+ if (bufp->n_type == (unsigned char)N_SLINE) continue;
+
+ SWAP_SYMBOL (bufp, abfd);
+
+ /* Ok. There is a lot of code duplicated in the rest of this
+ switch statement (for efficiency reasons). Since I don't
+ like duplicating code, I will do my penance here, and
+ describe the code which is duplicated:
+
+ *) The assignment to namestring.
+ *) The call to strchr.
+ *) The addition of a partial symbol the the two partial
+ symbol lists. This last is a large section of code, so
+ I've imbedded it in the following macro.
+ */
+
+/* Set namestring based on bufp. If the string table index is invalid,
+ give a fake name, and print a single error message per symbol file read,
+ rather than abort the symbol reading or flood the user with messages. */
+
+/*FIXME: Too many adds and indirections in here for the inner loop. */
+#define SET_NAMESTRING()\
+ if (((unsigned)bufp->n_strx + file_string_table_offset) >= \
+ DBX_STRINGTAB_SIZE (objfile)) { \
+ complain (&string_table_offset_complaint, symnum); \
+ namestring = "foo"; \
+ } else \
+ namestring = bufp->n_strx + file_string_table_offset + \
+ DBX_STRINGTAB (objfile)
+
+#define CUR_SYMBOL_TYPE bufp->n_type
+#define CUR_SYMBOL_VALUE bufp->n_value
+#define DBXREAD_ONLY
+#define START_PSYMTAB(ofile,secoff,fname,low,symoff,global_syms,static_syms)\
+ start_psymtab(ofile, secoff, fname, low, symoff, global_syms, static_syms)
+#define END_PSYMTAB(pst,ilist,ninc,c_off,c_text,dep_list,n_deps)\
+ end_psymtab(pst,ilist,ninc,c_off,c_text,dep_list,n_deps)
+
+#include "partial-stab.h"
+ }
+
+ /* If there's stuff to be cleaned up, clean it up. */
+ if (DBX_SYMCOUNT (objfile) > 0 /* We have some syms */
+/*FIXME, does this have a bug at start address 0? */
+ && last_o_file_start
+ && objfile -> ei.entry_point < bufp->n_value
+ && objfile -> ei.entry_point >= last_o_file_start)
+ {
+ objfile -> ei.entry_file_lowpc = last_o_file_start;
+ objfile -> ei.entry_file_highpc = bufp->n_value;
+ }
+
+ if (pst)
+ {
+ end_psymtab (pst, psymtab_include_list, includes_used,
+ symnum * symbol_size, end_of_text_addr,
+ dependency_list, dependencies_used);
+ }
+
+ do_cleanups (back_to);
+}
+
+/* Allocate and partially fill a partial symtab. It will be
+ completely filled at the end of the symbol list.
+
+ SYMFILE_NAME is the name of the symbol-file we are reading from, and ADDR
+ is the address relative to which its symbols are (incremental) or 0
+ (normal). */
+
+
+struct partial_symtab *
+start_psymtab (objfile, section_offsets,
+ filename, textlow, ldsymoff, global_syms, static_syms)
+ struct objfile *objfile;
+ struct section_offsets *section_offsets;
+ char *filename;
+ CORE_ADDR textlow;
+ int ldsymoff;
+ struct partial_symbol *global_syms;
+ struct partial_symbol *static_syms;
+{
+ struct partial_symtab *result =
+ start_psymtab_common(objfile, section_offsets,
+ filename, textlow, global_syms, static_syms);
+
+ result->read_symtab_private = (char *)
+ obstack_alloc (&objfile -> psymbol_obstack, sizeof (struct symloc));
+ LDSYMOFF(result) = ldsymoff;
+ result->read_symtab = dbx_psymtab_to_symtab;
+ SYMBOL_SIZE(result) = symbol_size;
+ SYMBOL_OFFSET(result) = symbol_table_offset;
+ STRING_OFFSET(result) = string_table_offset;
+ FILE_STRING_OFFSET(result) = file_string_table_offset;
+
+ /* If we're handling an ELF file, drag some section-relocation info
+ for this source file out of the ELF symbol table, to compensate for
+ Sun brain death. This replaces the section_offsets in this psymtab,
+ if successful. */
+ elfstab_offset_sections (objfile, result);
+
+ /* Deduce the source language from the filename for this psymtab. */
+ psymtab_language = deduce_language_from_filename (filename);
+
+ return result;
+}
+
+/* Close off the current usage of PST.
+ Returns PST or NULL if the partial symtab was empty and thrown away.
+
+ FIXME: List variables and peculiarities of same. */
+
+struct partial_symtab *
+end_psymtab (pst, include_list, num_includes, capping_symbol_offset,
+ capping_text, dependency_list, number_dependencies)
+ struct partial_symtab *pst;
+ char **include_list;
+ int num_includes;
+ int capping_symbol_offset;
+ CORE_ADDR capping_text;
+ struct partial_symtab **dependency_list;
+ int number_dependencies;
+/* struct partial_symbol *capping_global, *capping_static;*/
+{
+ int i;
+ struct partial_symtab *p1;
+ struct objfile *objfile = pst -> objfile;
+
+ if (capping_symbol_offset != -1)
+ LDSYMLEN(pst) = capping_symbol_offset - LDSYMOFF(pst);
+ pst->texthigh = capping_text;
+
+ /* Under Solaris, the N_SO symbols always have a value of 0,
+ instead of the usual address of the .o file. Therefore,
+ we have to do some tricks to fill in texthigh and textlow.
+ The first trick is in partial-stab.h: if we see a static
+ or global function, and the textlow for the current pst
+ is still 0, then we use that function's address for
+ the textlow of the pst.
+
+ Now, to fill in texthigh, we remember the last function seen
+ in the .o file (also in partial-stab.h). Also, there's a hack in
+ bfd/elf.c and gdb/elfread.c to pass the ELF st_size field
+ to here via the misc_info field. Therefore, we can fill in
+ a reliable texthigh by taking the address plus size of the
+ last function in the file.
+
+ Unfortunately, that does not cover the case where the last function
+ in the file is static. See the paragraph below for more comments
+ on this situation.
+
+ Finally, if we have a valid textlow for the current file, we run
+ down the partial_symtab_list filling in previous texthighs that
+ are still unknown. */
+
+ if (pst->texthigh == 0 && last_function_name) {
+ char *p;
+ int n;
+ struct minimal_symbol *minsym;
+
+ p = strchr (last_function_name, ':');
+ if (p == NULL)
+ p = last_function_name;
+ n = p - last_function_name;
+ p = alloca (n + 1);
+ strncpy (p, last_function_name, n);
+ p[n] = 0;
+
+ minsym = lookup_minimal_symbol (p, objfile);
+
+ if (minsym) {
+ pst->texthigh = SYMBOL_VALUE_ADDRESS (minsym) +
+ (long) MSYMBOL_INFO (minsym);
+ } else {
+ /* This file ends with a static function, and it's
+ difficult to imagine how hard it would be to track down
+ the elf symbol. Luckily, most of the time no one will notice,
+ since the next file will likely be compiled with -g, so
+ the code below will copy the first fuction's start address
+ back to our texthigh variable. (Also, if this file is the
+ last one in a dynamically linked program, texthigh already
+ has the right value.) If the next file isn't compiled
+ with -g, then the last function in this file winds up owning
+ all of the text space up to the next -g file, or the end (minus
+ shared libraries). This only matters for single stepping,
+ and even then it will still work, except that it will single
+ step through all of the covered functions, instead of setting
+ breakpoints around them as it usualy does. This makes it
+ pretty slow, but at least it doesn't fail.
+
+ We can fix this with a fairly big change to bfd, but we need
+ to coordinate better with Cygnus if we want to do that. FIXME. */
+ }
+ last_function_name = NULL;
+ }
+
+ /* this test will be true if the last .o file is only data */
+ if (pst->textlow == 0)
+ pst->textlow = pst->texthigh;
+
+ /* If we know our own starting text address, then walk through all other
+ psymtabs for this objfile, and if any didn't know their ending text
+ address, set it to our starting address. Take care to not set our
+ own ending address to our starting address, nor to set addresses on
+ `dependency' files that have both textlow and texthigh zero. */
+ if (pst->textlow) {
+ ALL_OBJFILE_PSYMTABS (objfile, p1) {
+ if (p1->texthigh == 0 && p1->textlow != 0 && p1 != pst) {
+ p1->texthigh = pst->textlow;
+ /* if this file has only data, then make textlow match texthigh */
+ if (p1->textlow == 0)
+ p1->textlow = p1->texthigh;
+ }
+ }
+ }
+
+ /* End of kludge for patching Solaris textlow and texthigh. */
+
+
+ pst->n_global_syms =
+ objfile->global_psymbols.next - (objfile->global_psymbols.list + pst->globals_offset);
+ pst->n_static_syms =
+ objfile->static_psymbols.next - (objfile->static_psymbols.list + pst->statics_offset);
+
+ pst->number_of_dependencies = number_dependencies;
+ if (number_dependencies)
+ {
+ pst->dependencies = (struct partial_symtab **)
+ obstack_alloc (&objfile->psymbol_obstack,
+ number_dependencies * sizeof (struct partial_symtab *));
+ memcpy (pst->dependencies, dependency_list,
+ number_dependencies * sizeof (struct partial_symtab *));
+ }
+ else
+ pst->dependencies = 0;
+
+ for (i = 0; i < num_includes; i++)
+ {
+ struct partial_symtab *subpst =
+ allocate_psymtab (include_list[i], objfile);
+
+ subpst->section_offsets = pst->section_offsets;
+ subpst->read_symtab_private =
+ (char *) obstack_alloc (&objfile->psymbol_obstack,
+ sizeof (struct symloc));
+ LDSYMOFF(subpst) =
+ LDSYMLEN(subpst) =
+ subpst->textlow =
+ subpst->texthigh = 0;
+
+ /* We could save slight bits of space by only making one of these,
+ shared by the entire set of include files. FIXME-someday. */
+ subpst->dependencies = (struct partial_symtab **)
+ obstack_alloc (&objfile->psymbol_obstack,
+ sizeof (struct partial_symtab *));
+ subpst->dependencies[0] = pst;
+ subpst->number_of_dependencies = 1;
+
+ subpst->globals_offset =
+ subpst->n_global_syms =
+ subpst->statics_offset =
+ subpst->n_static_syms = 0;
+
+ subpst->readin = 0;
+ subpst->symtab = 0;
+ subpst->read_symtab = pst->read_symtab;
+ }
+
+ sort_pst_symbols (pst);
+
+ /* If there is already a psymtab or symtab for a file of this name, remove it.
+ (If there is a symtab, more drastic things also happen.)
+ This happens in VxWorks. */
+ free_named_symtabs (pst->filename);
+
+ if (num_includes == 0
+ && number_dependencies == 0
+ && pst->n_global_syms == 0
+ && pst->n_static_syms == 0) {
+ /* Throw away this psymtab, it's empty. We can't deallocate it, since
+ it is on the obstack, but we can forget to chain it on the list. */
+ struct partial_symtab *prev_pst;
+
+ /* First, snip it out of the psymtab chain */
+
+ if (pst->objfile->psymtabs == pst)
+ pst->objfile->psymtabs = pst->next;
+ else
+ for (prev_pst = pst->objfile->psymtabs; prev_pst; prev_pst = pst->next)
+ if (prev_pst->next == pst)
+ prev_pst->next = pst->next;
+
+ /* Next, put it on a free list for recycling */
+
+ pst->next = pst->objfile->free_psymtabs;
+ pst->objfile->free_psymtabs = pst;
+
+ /* Indicate that psymtab was thrown away. */
+ pst = (struct partial_symtab *)NULL;
+ }
+ return pst;
+}
+
+static void
+dbx_psymtab_to_symtab_1 (pst)
+ struct partial_symtab *pst;
+{
+ struct cleanup *old_chain;
+ int i;
+
+ if (!pst)
+ return;
+
+ if (pst->readin)
+ {
+ fprintf (stderr, "Psymtab for %s already read in. Shouldn't happen.\n",
+ pst->filename);
+ return;
+ }
+
+ /* Read in all partial symtabs on which this one is dependent */
+ for (i = 0; i < pst->number_of_dependencies; i++)
+ if (!pst->dependencies[i]->readin)
+ {
+ /* Inform about additional files that need to be read in. */
+ if (info_verbose)
+ {
+ fputs_filtered (" ", stdout);
+ wrap_here ("");
+ fputs_filtered ("and ", stdout);
+ wrap_here ("");
+ printf_filtered ("%s...", pst->dependencies[i]->filename);
+ wrap_here (""); /* Flush output */
+ fflush (stdout);
+ }
+ dbx_psymtab_to_symtab_1 (pst->dependencies[i]);
+ }
+
+ if (LDSYMLEN(pst)) /* Otherwise it's a dummy */
+ {
+ /* Init stuff necessary for reading in symbols */
+ stabsread_init ();
+ buildsym_init ();
+ old_chain = make_cleanup (really_free_pendings, 0);
+ file_string_table_offset = FILE_STRING_OFFSET (pst);
+ symbol_size = SYMBOL_SIZE (pst);
+
+ /* Read in this file's symbols */
+ bfd_seek (pst->objfile->obfd, SYMBOL_OFFSET (pst), SEEK_SET);
+ read_ofile_symtab (pst);
+ sort_symtab_syms (pst->symtab);
+
+ do_cleanups (old_chain);
+ }
+
+ pst->readin = 1;
+}
+
+/* Read in all of the symbols for a given psymtab for real.
+ Be verbose about it if the user wants that. */
+
+static void
+dbx_psymtab_to_symtab (pst)
+ struct partial_symtab *pst;
+{
+ bfd *sym_bfd;
+
+ if (!pst)
+ return;
+
+ if (pst->readin)
+ {
+ fprintf (stderr, "Psymtab for %s already read in. Shouldn't happen.\n",
+ pst->filename);
+ return;
+ }
+
+ if (LDSYMLEN(pst) || pst->number_of_dependencies)
+ {
+ /* Print the message now, before reading the string table,
+ to avoid disconcerting pauses. */
+ if (info_verbose)
+ {
+ printf_filtered ("Reading in symbols for %s...", pst->filename);
+ fflush (stdout);
+ }
+
+ sym_bfd = pst->objfile->obfd;
+
+ next_symbol_text_func = dbx_next_symbol_text;
+
+ dbx_psymtab_to_symtab_1 (pst);
+
+ /* Match with global symbols. This only needs to be done once,
+ after all of the symtabs and dependencies have been read in. */
+ scan_file_globals (pst->objfile);
+
+ /* Finish up the debug error message. */
+ if (info_verbose)
+ printf_filtered ("done.\n");
+ }
+}
+
+/* Read in a defined section of a specific object file's symbols. */
+
+static void
+read_ofile_symtab (pst)
+ struct partial_symtab *pst;
+{
+ register char *namestring;
+ register struct internal_nlist *bufp;
+ unsigned char type;
+ unsigned max_symnum;
+ register bfd *abfd;
+ struct objfile *objfile;
+ int sym_offset; /* Offset to start of symbols to read */
+ int sym_size; /* Size of symbols to read */
+ CORE_ADDR text_offset; /* Start of text segment for symbols */
+ int text_size; /* Size of text segment for symbols */
+ struct section_offsets *section_offsets;
+
+ objfile = pst->objfile;
+ sym_offset = LDSYMOFF(pst);
+ sym_size = LDSYMLEN(pst);
+ text_offset = pst->textlow;
+ text_size = pst->texthigh - pst->textlow;
+ section_offsets = pst->section_offsets;
+
+ current_objfile = objfile;
+ subfile_stack = NULL;
+
+ stringtab_global = DBX_STRINGTAB (objfile);
+ last_source_file = NULL;
+
+ abfd = objfile->obfd;
+ symfile_bfd = objfile->obfd; /* Implicit param to next_text_symbol */
+ symbuf_end = symbuf_idx = 0;
+
+ /* It is necessary to actually read one symbol *before* the start
+ of this symtab's symbols, because the GCC_COMPILED_FLAG_SYMBOL
+ occurs before the N_SO symbol.
+
+ Detecting this in read_dbx_symtab
+ would slow down initial readin, so we look for it here instead. */
+ if (!processing_acc_compilation && sym_offset >= (int)symbol_size)
+ {
+ bfd_seek (symfile_bfd, sym_offset - symbol_size, SEEK_CUR);
+ fill_symbuf (abfd);
+ bufp = &symbuf[symbuf_idx++];
+ SWAP_SYMBOL (bufp, abfd);
+
+ SET_NAMESTRING ();
+
+ processing_gcc_compilation = 0;
+ if (bufp->n_type == N_TEXT)
+ {
+ if (STREQ (namestring, GCC_COMPILED_FLAG_SYMBOL))
+ processing_gcc_compilation = 1;
+ else if (STREQ (namestring, GCC2_COMPILED_FLAG_SYMBOL))
+ processing_gcc_compilation = 2;
+ }
+
+ /* Try to select a C++ demangling based on the compilation unit
+ producer. */
+
+ if (processing_gcc_compilation)
+ {
+ if (AUTO_DEMANGLING)
+ {
+ set_demangling_style (GNU_DEMANGLING_STYLE_STRING);
+ }
+ }
+ }
+ else
+ {
+ /* The N_SO starting this symtab is the first symbol, so we
+ better not check the symbol before it. I'm not this can
+ happen, but it doesn't hurt to check for it. */
+ bfd_seek (symfile_bfd, sym_offset, SEEK_CUR);
+ processing_gcc_compilation = 0;
+ }
+
+ if (symbuf_idx == symbuf_end)
+ fill_symbuf (abfd);
+ bufp = &symbuf[symbuf_idx];
+ if (bufp->n_type != (unsigned char)N_SO)
+ error("First symbol in segment of executable not a source symbol");
+
+ max_symnum = sym_size / symbol_size;
+
+ for (symnum = 0;
+ symnum < max_symnum;
+ symnum++)
+ {
+ QUIT; /* Allow this to be interruptable */
+ if (symbuf_idx == symbuf_end)
+ fill_symbuf(abfd);
+ bufp = &symbuf[symbuf_idx++];
+ SWAP_SYMBOL (bufp, abfd);
+
+ type = bufp->n_type;
+
+ SET_NAMESTRING ();
+
+ if (type & N_STAB) {
+ process_one_symbol (type, bufp->n_desc, bufp->n_value,
+ namestring, section_offsets, objfile);
+ }
+ /* We skip checking for a new .o or -l file; that should never
+ happen in this routine. */
+ else if (type == N_TEXT)
+ {
+ /* I don't think this code will ever be executed, because
+ the GCC_COMPILED_FLAG_SYMBOL usually is right before
+ the N_SO symbol which starts this source file.
+ However, there is no reason not to accept
+ the GCC_COMPILED_FLAG_SYMBOL anywhere. */
+
+ if (STREQ (namestring, GCC_COMPILED_FLAG_SYMBOL))
+ processing_gcc_compilation = 1;
+ else if (STREQ (namestring, GCC2_COMPILED_FLAG_SYMBOL))
+ processing_gcc_compilation = 2;
+
+ if (AUTO_DEMANGLING)
+ {
+ set_demangling_style (GNU_DEMANGLING_STYLE_STRING);
+ }
+ }
+ else if (type & N_EXT || type == (unsigned char)N_TEXT
+ || type == (unsigned char)N_NBTEXT
+ ) {
+ /* Global symbol: see if we came across a dbx defintion for
+ a corresponding symbol. If so, store the value. Remove
+ syms from the chain when their values are stored, but
+ search the whole chain, as there may be several syms from
+ different files with the same name. */
+ /* This is probably not true. Since the files will be read
+ in one at a time, each reference to a global symbol will
+ be satisfied in each file as it appears. So we skip this
+ section. */
+ ;
+ }
+ }
+
+ current_objfile = NULL;
+
+ /* In a Solaris elf file, this variable, which comes from the
+ value of the N_SO symbol, will still be 0. Luckily, text_offset,
+ which comes from pst->textlow is correct. */
+ if (last_source_start_addr == 0)
+ last_source_start_addr = text_offset;
+
+ pst->symtab = end_symtab (text_offset + text_size, 0, 0, objfile,
+ SECT_OFF_TEXT);
+ end_stabs ();
+}
+
+
+/* This handles a single symbol from the symbol-file, building symbols
+ into a GDB symtab. It takes these arguments and an implicit argument.
+
+ TYPE is the type field of the ".stab" symbol entry.
+ DESC is the desc field of the ".stab" entry.
+ VALU is the value field of the ".stab" entry.
+ NAME is the symbol name, in our address space.
+ SECTION_OFFSETS is a set of amounts by which the sections of this object
+ file were relocated when it was loaded into memory.
+ All symbols that refer
+ to memory locations need to be offset by these amounts.
+ OBJFILE is the object file from which we are reading symbols.
+ It is used in end_symtab. */
+
+void
+process_one_symbol (type, desc, valu, name, section_offsets, objfile)
+ int type, desc;
+ CORE_ADDR valu;
+ char *name;
+ struct section_offsets *section_offsets;
+ struct objfile *objfile;
+{
+#ifdef SUN_FIXED_LBRAC_BUG
+ /* If SUN_FIXED_LBRAC_BUG is defined, then it tells us whether we need
+ to correct the address of N_LBRAC's. If it is not defined, then
+ we never need to correct the addresses. */
+
+ /* This records the last pc address we've seen. We depend on there being
+ an SLINE or FUN or SO before the first LBRAC, since the variable does
+ not get reset in between reads of different symbol files. */
+ static CORE_ADDR last_pc_address;
+#endif
+
+ register struct context_stack *new;
+ /* This remembers the address of the start of a function. It is used
+ because in Solaris 2, N_LBRAC, N_RBRAC, and N_SLINE entries are
+ relative to the current function's start address. On systems
+ other than Solaris 2, this just holds the SECT_OFF_TEXT value, and is
+ used to relocate these symbol types rather than SECTION_OFFSETS. */
+ static CORE_ADDR function_start_offset;
+
+ /* If this is nonzero, N_LBRAC, N_RBRAC, and N_SLINE entries are relative
+ to the function start address. */
+ int block_address_function_relative;
+
+ /* If this is nonzero, we've seen a non-gcc N_OPT symbol for this source
+ file. Used to detect the SunPRO solaris compiler. */
+ static int n_opt_found;
+
+ /* The stab type used for the definition of the last function.
+ N_STSYM or N_GSYM for SunOS4 acc; N_FUN for other compilers. */
+ static int function_stab_type = 0;
+
+ /* This is true for Solaris (and all other stabs-in-elf systems, hopefully,
+ since it would be silly to do things differently from Solaris), and
+ false for SunOS4 and other a.out file formats. */
+ block_address_function_relative =
+ 0 == strncmp (bfd_get_target (objfile->obfd), "elf", 3);
+
+ if (!block_address_function_relative)
+ /* N_LBRAC, N_RBRAC and N_SLINE entries are not relative to the
+ function start address, so just use the text offset. */
+ function_start_offset = ANOFFSET (section_offsets, SECT_OFF_TEXT);
+
+ /* Something is wrong if we see real data before
+ seeing a source file name. */
+
+ if (last_source_file == NULL && type != (unsigned char)N_SO)
+ {
+ /* Ignore any symbols which appear before an N_SO symbol. Currently
+ no one puts symbols there, but we should deal gracefully with the
+ case. A complain()t might be in order (if !IGNORE_SYMBOL (type)),
+ but this should not be an error (). */
+ return;
+ }
+
+ switch (type)
+ {
+ case N_FUN:
+ case N_FNAME:
+ /* Relocate for dynamic loading */
+ valu += ANOFFSET (section_offsets, SECT_OFF_TEXT);
+ goto define_a_symbol;
+
+ case N_LBRAC:
+ /* This "symbol" just indicates the start of an inner lexical
+ context within a function. */
+
+#if defined(BLOCK_ADDRESS_ABSOLUTE)
+ /* Relocate for dynamic loading (?). */
+ valu += function_start_offset;
+#else
+ if (block_address_function_relative)
+ /* Relocate for Sun ELF acc fn-relative syms. */
+ valu += function_start_offset;
+ else
+ /* On most machines, the block addresses are relative to the
+ N_SO, the linker did not relocate them (sigh). */
+ valu += last_source_start_addr;
+#endif
+
+#ifdef SUN_FIXED_LBRAC_BUG
+ if (!SUN_FIXED_LBRAC_BUG && valu < last_pc_address) {
+ /* Patch current LBRAC pc value to match last handy pc value */
+ complain (&lbrac_complaint);
+ valu = last_pc_address;
+ }
+#endif
+ new = push_context (desc, valu);
+ break;
+
+ case N_RBRAC:
+ /* This "symbol" just indicates the end of an inner lexical
+ context that was started with N_LBRAC. */
+
+#if defined(BLOCK_ADDRESS_ABSOLUTE)
+ /* Relocate for dynamic loading (?). */
+ valu += function_start_offset;
+#else
+ if (block_address_function_relative)
+ /* Relocate for Sun ELF acc fn-relative syms. */
+ valu += function_start_offset;
+ else
+ /* On most machines, the block addresses are relative to the
+ N_SO, the linker did not relocate them (sigh). */
+ valu += last_source_start_addr;
+#endif
+
+ new = pop_context();
+ if (desc != new->depth)
+ complain (&lbrac_mismatch_complaint, symnum);
+
+ /* Some compilers put the variable decls inside of an
+ LBRAC/RBRAC block. This macro should be nonzero if this
+ is true. DESC is N_DESC from the N_RBRAC symbol.
+ GCC_P is true if we've detected the GCC_COMPILED_SYMBOL
+ or the GCC2_COMPILED_SYMBOL. */
+#if !defined (VARIABLES_INSIDE_BLOCK)
+#define VARIABLES_INSIDE_BLOCK(desc, gcc_p) 0
+#endif
+
+ /* Can only use new->locals as local symbols here if we're in
+ gcc or on a machine that puts them before the lbrack. */
+ if (!VARIABLES_INSIDE_BLOCK(desc, processing_gcc_compilation))
+ local_symbols = new->locals;
+
+ /* If this is not the outermost LBRAC...RBRAC pair in the
+ function, its local symbols preceded it, and are the ones
+ just recovered from the context stack. Defined the block for them.
+
+ If this is the outermost LBRAC...RBRAC pair, there is no
+ need to do anything; leave the symbols that preceded it
+ to be attached to the function's own block. However, if
+ it is so, we need to indicate that we just moved outside
+ of the function. */
+ if (local_symbols
+ && (context_stack_depth
+ > !VARIABLES_INSIDE_BLOCK(desc, processing_gcc_compilation)))
+ {
+ /* FIXME Muzzle a compiler bug that makes end < start. */
+ if (new->start_addr > valu)
+ {
+ complain (&lbrac_rbrac_complaint);
+ new->start_addr = valu;
+ }
+ /* Make a block for the local symbols within. */
+ finish_block (0, &local_symbols, new->old_blocks,
+ new->start_addr, valu, objfile);
+ }
+ else
+ {
+ within_function = 0;
+ }
+ if (VARIABLES_INSIDE_BLOCK(desc, processing_gcc_compilation))
+ /* Now pop locals of block just finished. */
+ local_symbols = new->locals;
+ break;
+
+ case N_FN:
+ case N_FN_SEQ:
+ /* This kind of symbol indicates the start of an object file. */
+ /* Relocate for dynamic loading */
+ valu += ANOFFSET (section_offsets, SECT_OFF_TEXT);
+ break;
+
+ case N_SO:
+ /* This type of symbol indicates the start of data
+ for one source file.
+ Finish the symbol table of the previous source file
+ (if any) and start accumulating a new symbol table. */
+ /* Relocate for dynamic loading */
+ valu += ANOFFSET (section_offsets, SECT_OFF_TEXT);
+
+ n_opt_found = 0;
+
+#ifdef SUN_FIXED_LBRAC_BUG
+ last_pc_address = valu; /* Save for SunOS bug circumcision */
+#endif
+
+#ifdef PCC_SOL_BROKEN
+ /* pcc bug, occasionally puts out SO for SOL. */
+ if (context_stack_depth > 0)
+ {
+ start_subfile (name, NULL);
+ break;
+ }
+#endif
+ if (last_source_file)
+ {
+ /* Check if previous symbol was also an N_SO (with some
+ sanity checks). If so, that one was actually the directory
+ name, and the current one is the real file name.
+ Patch things up. */
+ if (previous_stab_code == (unsigned char) N_SO)
+ {
+ patch_subfile_names (current_subfile, name);
+ break; /* Ignore repeated SOs */
+ }
+ end_symtab (valu, 0, 0, objfile, SECT_OFF_TEXT);
+ end_stabs ();
+ }
+ start_stabs ();
+ start_symtab (name, NULL, valu);
+ break;
+
+
+ case N_SOL:
+ /* This type of symbol indicates the start of data for
+ a sub-source-file, one whose contents were copied or
+ included in the compilation of the main source file
+ (whose name was given in the N_SO symbol.) */
+ /* Relocate for dynamic loading */
+ valu += ANOFFSET (section_offsets, SECT_OFF_TEXT);
+ start_subfile (name, current_subfile->dirname);
+ break;
+
+ case N_BINCL:
+ push_subfile ();
+ add_new_header_file (name, valu);
+ start_subfile (name, current_subfile->dirname);
+ break;
+
+ case N_EINCL:
+ start_subfile (pop_subfile (), current_subfile->dirname);
+ break;
+
+ case N_EXCL:
+ add_old_header_file (name, valu);
+ break;
+
+ case N_SLINE:
+ /* This type of "symbol" really just records
+ one line-number -- core-address correspondence.
+ Enter it in the line list for this symbol table. */
+ /* Relocate for dynamic loading and for ELF acc fn-relative syms. */
+ valu += function_start_offset;
+#ifdef SUN_FIXED_LBRAC_BUG
+ last_pc_address = valu; /* Save for SunOS bug circumcision */
+#endif
+ record_line (current_subfile, desc, valu);
+ break;
+
+ case N_BCOMM:
+ common_block_start (name, objfile);
+ break;
+
+ case N_ECOMM:
+ common_block_end (objfile);
+ break;
+
+ /* The following symbol types need to have the appropriate offset added
+ to their value; then we process symbol definitions in the name. */
+
+ case N_STSYM: /* Static symbol in data seg */
+ case N_LCSYM: /* Static symbol in BSS seg */
+ case N_ROSYM: /* Static symbol in Read-only data seg */
+ /* HORRID HACK DEPT. However, it's Sun's furgin' fault.
+ Solaris2's stabs-in-elf makes *most* symbols relative
+ but leaves a few absolute (at least for Solaris 2.1 and version
+ 2.0.1 of the SunPRO compiler). N_STSYM and friends sit on the fence.
+ .stab "foo:S...",N_STSYM is absolute (ld relocates it)
+ .stab "foo:V...",N_STSYM is relative (section base subtracted).
+ This leaves us no choice but to search for the 'S' or 'V'...
+ (or pass the whole section_offsets stuff down ONE MORE function
+ call level, which we really don't want to do). */
+ {
+ char *p;
+ p = strchr (name, ':');
+ if (p != 0 && p[1] == 'S')
+ {
+ /* The linker relocated it. There used to be a kludge here
+ to add the text offset, but that will break if we ever
+ start using the text offset (currently it is always zero). */
+ goto define_a_symbol;
+ }
+ /* Since it's not the kludge case, re-dispatch to the right handler. */
+ switch (type) {
+ case N_STSYM: goto case_N_STSYM;
+ case N_LCSYM: goto case_N_LCSYM;
+ case N_ROSYM: goto case_N_ROSYM;
+ default: abort();
+ }
+ }
+
+ case_N_STSYM: /* Static symbol in data seg */
+ case N_DSLINE: /* Source line number, data seg */
+ valu += ANOFFSET (section_offsets, SECT_OFF_DATA);
+ goto define_a_symbol;
+
+ case_N_LCSYM: /* Static symbol in BSS seg */
+ case N_BSLINE: /* Source line number, bss seg */
+ /* N_BROWS: overlaps with N_BSLINE */
+ valu += ANOFFSET (section_offsets, SECT_OFF_BSS);
+ goto define_a_symbol;
+
+ case_N_ROSYM: /* Static symbol in Read-only data seg */
+ valu += ANOFFSET (section_offsets, SECT_OFF_RODATA);
+ goto define_a_symbol;
+
+ case N_ENTRY: /* Alternate entry point */
+ /* Relocate for dynamic loading */
+ valu += ANOFFSET (section_offsets, SECT_OFF_TEXT);
+ goto define_a_symbol;
+
+ /* The following symbol types we don't know how to process. Handle
+ them in a "default" way, but complain to people who care. */
+ default:
+ case N_CATCH: /* Exception handler catcher */
+ case N_EHDECL: /* Exception handler name */
+ case N_PC: /* Global symbol in Pascal */
+ case N_M2C: /* Modula-2 compilation unit */
+ /* N_MOD2: overlaps with N_EHDECL */
+ case N_SCOPE: /* Modula-2 scope information */
+ case N_ECOML: /* End common (local name) */
+ case N_NBTEXT: /* Gould Non-Base-Register symbols??? */
+ case N_NBDATA:
+ case N_NBBSS:
+ case N_NBSTS:
+ case N_NBLCS:
+ complain (&unknown_symtype_complaint,
+ local_hex_string((unsigned long) type));
+ /* FALLTHROUGH */
+
+ /* The following symbol types don't need the address field relocated,
+ since it is either unused, or is absolute. */
+ define_a_symbol:
+ case N_GSYM: /* Global variable */
+ case N_NSYMS: /* Number of symbols (ultrix) */
+ case N_NOMAP: /* No map? (ultrix) */
+ case N_RSYM: /* Register variable */
+ case N_DEFD: /* Modula-2 GNU module dependency */
+ case N_SSYM: /* Struct or union element */
+ case N_LSYM: /* Local symbol in stack */
+ case N_PSYM: /* Parameter variable */
+ case N_LENG: /* Length of preceding symbol type */
+ if (name)
+ {
+ int deftype;
+ char *colon_pos = strchr (name, ':');
+ if (colon_pos == NULL)
+ deftype = '\0';
+ else
+ deftype = colon_pos[1];
+
+ switch (deftype)
+ {
+ case 'f':
+ case 'F':
+ function_stab_type = type;
+
+#ifdef SUN_FIXED_LBRAC_BUG
+ /* The Sun acc compiler, under SunOS4, puts out
+ functions with N_GSYM or N_STSYM. The problem is
+ that the address of the symbol is no good (for N_GSYM
+ it doesn't even attept an address; for N_STSYM it
+ puts out an address but then it gets relocated
+ relative to the data segment, not the text segment).
+ Currently we can't fix this up later as we do for
+ some types of symbol in scan_file_globals.
+ Fortunately we do have a way of finding the address -
+ we know that the value in last_pc_address is either
+ the one we want (if we're dealing with the first
+ function in an object file), or somewhere in the
+ previous function. This means that we can use the
+ minimal symbol table to get the address. */
+
+ /* On solaris up to 2.2, the N_FUN stab gets relocated.
+ On Solaris 2.3, ld no longer relocates stabs (which
+ is good), and the N_FUN's value is now always zero.
+ The following code can't deal with this, because
+ last_pc_address depends on getting the address from a
+ N_SLINE or some such and in Solaris those are function
+ relative. Best fix is probably to create a Ttext.text symbol
+ and handle this like Ddata.data and so on. */
+
+ if (type == N_GSYM || type == N_STSYM)
+ {
+ struct minimal_symbol *m;
+ int l = colon_pos - name;
+
+ m = lookup_minimal_symbol_by_pc (last_pc_address);
+ if (m && STREQN (SYMBOL_NAME (m), name, l))
+ /* last_pc_address was in this function */
+ valu = SYMBOL_VALUE (m);
+ else if (m && STREQN (SYMBOL_NAME (m+1), name, l))
+ /* last_pc_address was in last function */
+ valu = SYMBOL_VALUE (m+1);
+ else
+ /* Not found - use last_pc_address (for finish_block) */
+ valu = last_pc_address;
+ }
+
+ last_pc_address = valu; /* Save for SunOS bug circumcision */
+#endif
+
+ if (block_address_function_relative)
+ /* For Solaris 2.0 compilers, the block addresses and
+ N_SLINE's are relative to the start of the
+ function. On normal systems, and when using gcc on
+ Solaris 2.0, these addresses are just absolute, or
+ relative to the N_SO, depending on
+ BLOCK_ADDRESS_ABSOLUTE. */
+ function_start_offset = valu;
+
+ within_function = 1;
+ if (context_stack_depth > 0)
+ {
+ new = pop_context ();
+ /* Make a block for the local symbols within. */
+ finish_block (new->name, &local_symbols, new->old_blocks,
+ new->start_addr, valu, objfile);
+ }
+ /* Stack must be empty now. */
+ if (context_stack_depth != 0)
+ complain (&lbrac_unmatched_complaint, symnum);
+
+ new = push_context (0, valu);
+ new->name = define_symbol (valu, name, desc, type, objfile);
+ break;
+
+ default:
+ define_symbol (valu, name, desc, type, objfile);
+ break;
+ }
+ }
+ break;
+
+ /* We use N_OPT to carry the gcc2_compiled flag. Sun uses it
+ for a bunch of other flags, too. Someday we may parse their
+ flags; for now we ignore theirs and hope they'll ignore ours. */
+ case N_OPT: /* Solaris 2: Compiler options */
+ if (name)
+ {
+ if (STREQ (name, GCC2_COMPILED_FLAG_SYMBOL))
+ {
+ processing_gcc_compilation = 2;
+#if 1 /* Works, but is experimental. -fnf */
+ if (AUTO_DEMANGLING)
+ {
+ set_demangling_style (GNU_DEMANGLING_STYLE_STRING);
+ }
+#endif
+ }
+ else
+ n_opt_found = 1;
+ }
+ break;
+
+ /* The following symbol types can be ignored. */
+ case N_OBJ: /* Solaris 2: Object file dir and name */
+ /* N_UNDF: Solaris 2: file separator mark */
+ /* N_UNDF: -- we will never encounter it, since we only process one
+ file's symbols at once. */
+ case N_ENDM: /* Solaris 2: End of module */
+ case N_MAIN: /* Name of main routine. */
+ break;
+ }
+
+ previous_stab_code = type;
+}
+
+/* FIXME: The only difference between this and elfstab_build_psymtabs is
+ the call to install_minimal_symbols for elf. If the differences are
+ really that small, the code should be shared. */
+
+/* Scan and build partial symbols for an coff symbol file.
+ The coff file has already been processed to get its minimal symbols.
+
+ This routine is the equivalent of dbx_symfile_init and dbx_symfile_read
+ rolled into one.
+
+ OBJFILE is the object file we are reading symbols from.
+ ADDR is the address relative to which the symbols are (e.g.
+ the base address of the text segment).
+ MAINLINE is true if we are reading the main symbol
+ table (as opposed to a shared lib or dynamically loaded file).
+ STABOFFSET and STABSIZE define the location in OBJFILE where the .stab
+ section exists.
+ STABSTROFFSET and STABSTRSIZE define the location in OBJFILE where the
+ .stabstr section exists.
+
+ This routine is mostly copied from dbx_symfile_init and dbx_symfile_read,
+ adjusted for coff details. */
+
+void
+coffstab_build_psymtabs (objfile, section_offsets, mainline,
+ staboffset, stabsize,
+ stabstroffset, stabstrsize)
+ struct objfile *objfile;
+ struct section_offsets *section_offsets;
+ int mainline;
+ file_ptr staboffset;
+ unsigned int stabsize;
+ file_ptr stabstroffset;
+ unsigned int stabstrsize;
+{
+ int val;
+ bfd *sym_bfd = objfile->obfd;
+ char *name = bfd_get_filename (sym_bfd);
+ struct dbx_symfile_info *info;
+
+ /* There is already a dbx_symfile_info allocated by our caller.
+ It might even contain some info from the coff symtab to help us. */
+ info = (struct dbx_symfile_info *) objfile->sym_stab_info;
+
+ DBX_TEXT_SECT (objfile) = bfd_get_section_by_name (sym_bfd, ".text");
+ if (!DBX_TEXT_SECT (objfile))
+ error ("Can't find .text section in symbol file");
+
+#define COFF_STABS_SYMBOL_SIZE 12 /* XXX FIXME XXX */
+ DBX_SYMBOL_SIZE (objfile) = COFF_STABS_SYMBOL_SIZE;
+ DBX_SYMCOUNT (objfile) = stabsize / DBX_SYMBOL_SIZE (objfile);
+ DBX_STRINGTAB_SIZE (objfile) = stabstrsize;
+ DBX_SYMTAB_OFFSET (objfile) = staboffset;
+
+ if (stabstrsize > bfd_get_size (sym_bfd))
+ error ("ridiculous string table size: %d bytes", stabstrsize);
+ DBX_STRINGTAB (objfile) = (char *)
+ obstack_alloc (&objfile->psymbol_obstack, stabstrsize+1);
+
+ /* Now read in the string table in one big gulp. */
+
+ val = bfd_seek (sym_bfd, stabstroffset, SEEK_SET);
+ if (val < 0)
+ perror_with_name (name);
+ val = bfd_read (DBX_STRINGTAB (objfile), stabstrsize, 1, sym_bfd);
+ if (val != stabstrsize)
+ perror_with_name (name);
+
+ stabsread_new_init ();
+ buildsym_new_init ();
+ free_header_files ();
+ init_header_files ();
+
+ processing_acc_compilation = 1;
+
+ /* In a coff file, we've already installed the minimal symbols that came
+ from the coff (non-stab) symbol table, so always act like an
+ incremental load here. */
+ dbx_symfile_read (objfile, section_offsets, 0);
+}
+
+/* Scan and build partial symbols for an ELF symbol file.
+ This ELF file has already been processed to get its minimal symbols,
+ and any DWARF symbols that were in it.
+
+ This routine is the equivalent of dbx_symfile_init and dbx_symfile_read
+ rolled into one.
+
+ OBJFILE is the object file we are reading symbols from.
+ ADDR is the address relative to which the symbols are (e.g.
+ the base address of the text segment).
+ MAINLINE is true if we are reading the main symbol
+ table (as opposed to a shared lib or dynamically loaded file).
+ STABOFFSET and STABSIZE define the location in OBJFILE where the .stab
+ section exists.
+ STABSTROFFSET and STABSTRSIZE define the location in OBJFILE where the
+ .stabstr section exists.
+
+ This routine is mostly copied from dbx_symfile_init and dbx_symfile_read,
+ adjusted for elf details. */
+
+void
+elfstab_build_psymtabs (objfile, section_offsets, mainline,
+ staboffset, stabsize,
+ stabstroffset, stabstrsize)
+ struct objfile *objfile;
+ struct section_offsets *section_offsets;
+ int mainline;
+ file_ptr staboffset;
+ unsigned int stabsize;
+ file_ptr stabstroffset;
+ unsigned int stabstrsize;
+{
+ int val;
+ bfd *sym_bfd = objfile->obfd;
+ char *name = bfd_get_filename (sym_bfd);
+ struct dbx_symfile_info *info;
+
+ /* There is already a dbx_symfile_info allocated by our caller.
+ It might even contain some info from the ELF symtab to help us. */
+ info = (struct dbx_symfile_info *) objfile->sym_stab_info;
+
+ DBX_TEXT_SECT (objfile) = bfd_get_section_by_name (sym_bfd, ".text");
+ if (!DBX_TEXT_SECT (objfile))
+ error ("Can't find .text section in symbol file");
+
+#define ELF_STABS_SYMBOL_SIZE 12 /* XXX FIXME XXX */
+ DBX_SYMBOL_SIZE (objfile) = ELF_STABS_SYMBOL_SIZE;
+ DBX_SYMCOUNT (objfile) = stabsize / DBX_SYMBOL_SIZE (objfile);
+ DBX_STRINGTAB_SIZE (objfile) = stabstrsize;
+ DBX_SYMTAB_OFFSET (objfile) = staboffset;
+
+ if (stabstrsize > bfd_get_size (sym_bfd))
+ error ("ridiculous string table size: %d bytes", stabstrsize);
+ DBX_STRINGTAB (objfile) = (char *)
+ obstack_alloc (&objfile->psymbol_obstack, stabstrsize+1);
+
+ /* Now read in the string table in one big gulp. */
+
+ val = bfd_seek (sym_bfd, stabstroffset, SEEK_SET);
+ if (val < 0)
+ perror_with_name (name);
+ val = bfd_read (DBX_STRINGTAB (objfile), stabstrsize, 1, sym_bfd);
+ if (val != stabstrsize)
+ perror_with_name (name);
+
+ stabsread_new_init ();
+ buildsym_new_init ();
+ free_header_files ();
+ init_header_files ();
+ install_minimal_symbols (objfile);
+
+ processing_acc_compilation = 1;
+
+ /* In an elf file, we've already installed the minimal symbols that came
+ from the elf (non-stab) symbol table, so always act like an
+ incremental load here. */
+ dbx_symfile_read (objfile, section_offsets, 0);
+}
+
+/* Scan and build partial symbols for a PA symbol file.
+ This PA file has already been processed to get its minimal symbols.
+
+ OBJFILE is the object file we are reading symbols from.
+ ADDR is the address relative to which the symbols are (e.g.
+ the base address of the text segment).
+ MAINLINE is true if we are reading the main symbol
+ table (as opposed to a shared lib or dynamically loaded file).
+
+ */
+
+void
+pastab_build_psymtabs (objfile, section_offsets, mainline)
+ struct objfile *objfile;
+ struct section_offsets *section_offsets;
+ int mainline;
+{
+ free_header_files ();
+ init_header_files ();
+
+ /* In a PA file, we've already installed the minimal symbols that came
+ from the PA (non-stab) symbol table, so always act like an
+ incremental load here. */
+
+ dbx_symfile_read (objfile, section_offsets, mainline);
+}
+
+/* Parse the user's idea of an offset for dynamic linking, into our idea
+ of how to represent it for fast symbol reading. */
+
+static struct section_offsets *
+dbx_symfile_offsets (objfile, addr)
+ struct objfile *objfile;
+ CORE_ADDR addr;
+{
+ struct section_offsets *section_offsets;
+ int i;
+
+ section_offsets = (struct section_offsets *)
+ obstack_alloc (&objfile -> psymbol_obstack,
+ sizeof (struct section_offsets) +
+ sizeof (section_offsets->offsets) * (SECT_OFF_MAX-1));
+
+ for (i = 0; i < SECT_OFF_MAX; i++)
+ ANOFFSET (section_offsets, i) = addr;
+
+ return section_offsets;
+}
+
+/* Register our willingness to decode symbols for SunOS and a.out and
+ NetBSD and b.out files handled by BFD... */
+static struct sym_fns sunos_sym_fns =
+{
+ "sunOs", /* sym_name: name or name prefix of BFD target type */
+ 6, /* sym_namelen: number of significant sym_name chars */
+ dbx_new_init, /* sym_new_init: init anything gbl to entire symtab */
+ dbx_symfile_init, /* sym_init: read initial info, setup for sym_read() */
+ dbx_symfile_read, /* sym_read: read a symbol file into symtab */
+ dbx_symfile_finish, /* sym_finish: finished with file, cleanup */
+ dbx_symfile_offsets, /* sym_offsets: parse user's offsets to internal form */
+ NULL /* next: pointer to next struct sym_fns */
+};
+
+static struct sym_fns aout_sym_fns =
+{
+ "a.out", /* sym_name: name or name prefix of BFD target type */
+ 5, /* sym_namelen: number of significant sym_name chars */
+ dbx_new_init, /* sym_new_init: init anything gbl to entire symtab */
+ dbx_symfile_init, /* sym_init: read initial info, setup for sym_read() */
+ dbx_symfile_read, /* sym_read: read a symbol file into symtab */
+ dbx_symfile_finish, /* sym_finish: finished with file, cleanup */
+ dbx_symfile_offsets, /* sym_offsets: parse user's offsets to internal form */
+ NULL /* next: pointer to next struct sym_fns */
+};
+
+static struct sym_fns bout_sym_fns =
+{
+ "b.out", /* sym_name: name or name prefix of BFD target type */
+ 5, /* sym_namelen: number of significant sym_name chars */
+ dbx_new_init, /* sym_new_init: init anything gbl to entire symtab */
+ dbx_symfile_init, /* sym_init: read initial info, setup for sym_read() */
+ dbx_symfile_read, /* sym_read: read a symbol file into symtab */
+ dbx_symfile_finish, /* sym_finish: finished with file, cleanup */
+ dbx_symfile_offsets, /* sym_offsets: parse user's offsets to internal form */
+ NULL /* next: pointer to next struct sym_fns */
+};
+
+void
+_initialize_dbxread ()
+{
+ add_symtab_fns(&sunos_sym_fns);
+ add_symtab_fns(&aout_sym_fns);
+ add_symtab_fns(&bout_sym_fns);
+}
diff --git a/gnu/usr.bin/gdb/gdb/dcache.c b/gnu/usr.bin/gdb/gdb/dcache.c
new file mode 100644
index 0000000..aaa01d0
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/dcache.c
@@ -0,0 +1,236 @@
+/* Caching code. Typically used by remote back ends for
+ caching remote memory.
+
+ Copyright 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "dcache.h"
+#include "gdbcmd.h"
+
+extern int insque();
+extern int remque();
+
+int remote_dcache = 0;
+
+/* The data cache records all the data read from the remote machine
+ since the last time it stopped.
+
+ Each cache block holds LINE_SIZE bytes of data
+ starting at a multiple-of-LINE_SIZE address. */
+
+#define LINE_SIZE_MASK ((LINE_SIZE - 1)) /* eg 7*2+1= 111*/
+#define XFORM(x) (((x) & LINE_SIZE_MASK) >> 2)
+
+/* Free all the data cache blocks, thus discarding all cached data. */
+void
+dcache_flush (dcache)
+ DCACHE *dcache;
+{
+ register struct dcache_block *db;
+
+ if (remote_dcache > 0)
+ while ((db = dcache->dcache_valid.next) != &dcache->dcache_valid)
+ {
+ remque (db);
+ insque (db, &dcache->dcache_free);
+ }
+
+ return;
+}
+
+/*
+ * If addr is present in the dcache, return the address of the block
+ * containing it.
+ */
+static
+struct dcache_block *
+dcache_hit (dcache, addr)
+ DCACHE *dcache;
+ unsigned int addr;
+{
+ register struct dcache_block *db;
+
+ if (addr & 3
+ || remote_dcache == 0)
+ abort ();
+
+ /* Search all cache blocks for one that is at this address. */
+ db = dcache->dcache_valid.next;
+ while (db != &dcache->dcache_valid)
+ {
+ if ((addr & ~LINE_SIZE_MASK) == db->addr)
+ return db;
+ db = db->next;
+ }
+
+ return NULL;
+}
+
+/* Return the int data at address ADDR in dcache block DC. */
+static
+int
+dcache_value (db, addr)
+ struct dcache_block *db;
+ unsigned int addr;
+{
+ if (addr & 3
+ || remote_dcache == 0)
+ abort ();
+ return (db->data[XFORM (addr)]);
+}
+
+/* Get a free cache block, put or keep it on the valid list,
+ and return its address. The caller should store into the block
+ the address and data that it describes, then remque it from the
+ free list and insert it into the valid list. This procedure
+ prevents errors from creeping in if a memory retrieval is
+ interrupted (which used to put garbage blocks in the valid
+ list...). */
+static
+struct dcache_block *
+dcache_alloc (dcache)
+ DCACHE *dcache;
+{
+ register struct dcache_block *db;
+
+ if (remote_dcache == 0)
+ abort();
+
+ if ((db = dcache->dcache_free.next) == &dcache->dcache_free)
+ {
+ /* If we can't get one from the free list, take last valid and put
+ it on the free list. */
+ db = dcache->dcache_valid.last;
+ remque (db);
+ insque (db, &dcache->dcache_free);
+ }
+
+ remque (db);
+ insque (db, &dcache->dcache_valid);
+ return (db);
+}
+
+/* Using the data cache DCACHE return the contents of the word at
+ address ADDR in the remote machine. */
+int
+dcache_fetch (dcache, addr)
+ DCACHE *dcache;
+ CORE_ADDR addr;
+{
+ register struct dcache_block *db;
+
+ if (remote_dcache == 0)
+ {
+ int i;
+
+ (*dcache->read_memory) (addr, (unsigned char *) &i, 4);
+ return(i);
+ }
+
+ db = dcache_hit (dcache, addr);
+ if (db == 0)
+ {
+ db = dcache_alloc (dcache);
+ immediate_quit++;
+ (*dcache->read_memory) (addr & ~LINE_SIZE_MASK, (unsigned char *) db->data, LINE_SIZE);
+ immediate_quit--;
+ db->addr = addr & ~LINE_SIZE_MASK;
+ remque (db); /* Off the free list */
+ insque (db, &dcache->dcache_valid); /* On the valid list */
+ }
+ return (dcache_value (db, addr));
+}
+
+/* Write the word at ADDR both in the data cache and in the remote machine. */
+void
+dcache_poke (dcache, addr, data)
+ DCACHE *dcache;
+ CORE_ADDR addr;
+ int data;
+{
+ register struct dcache_block *db;
+
+ if (remote_dcache == 0)
+ {
+ (*dcache->write_memory) (addr, (unsigned char *) &data, 4);
+ return;
+ }
+
+ /* First make sure the word is IN the cache. DB is its cache block. */
+ db = dcache_hit (dcache, addr);
+ if (db == 0)
+ {
+ db = dcache_alloc (dcache);
+ immediate_quit++;
+ (*dcache->write_memory) (addr & ~LINE_SIZE_MASK, (unsigned char *) db->data, LINE_SIZE);
+ immediate_quit--;
+ db->addr = addr & ~LINE_SIZE_MASK;
+ remque (db); /* Off the free list */
+ insque (db, &dcache->dcache_valid); /* On the valid list */
+ }
+
+ /* Modify the word in the cache. */
+ db->data[XFORM (addr)] = data;
+
+ /* Send the changed word. */
+ immediate_quit++;
+ (*dcache->write_memory) (addr, (unsigned char *) &data, 4);
+ immediate_quit--;
+}
+
+/* Initialize the data cache. */
+DCACHE *
+dcache_init (reading, writing)
+ memxferfunc reading;
+ memxferfunc writing;
+{
+ register i;
+ register struct dcache_block *db;
+ DCACHE *dcache;
+
+ dcache = (DCACHE *) xmalloc (sizeof (*dcache));
+ dcache->read_memory = reading;
+ dcache->write_memory = writing;
+ dcache->the_cache = (struct dcache_block *)
+ xmalloc (sizeof (*dcache->the_cache) * DCACHE_SIZE);
+
+ dcache->dcache_free.next = dcache->dcache_free.last = &dcache->dcache_free;
+ dcache->dcache_valid.next = dcache->dcache_valid.last = &dcache->dcache_valid;
+ for (db = dcache->the_cache, i = 0; i < DCACHE_SIZE; i++, db++)
+ insque (db, &dcache->dcache_free);
+
+ return(dcache);
+}
+
+void
+_initialitize_dcache ()
+{
+ add_show_from_set
+ (add_set_cmd ("remotecache", class_support, var_boolean,
+ (char *) &remote_dcache,
+ "\
+Set cache use for remote targets.\n\
+When on, use data caching for remote targets. For many remote targets\n\
+this option can offer better throughput for reading target memory.\n\
+Unfortunately, gdb does not currently know anything about volatile\n\
+registers and thus data caching will produce incorrect results with\n\
+volatile registers are in use. By default, this option is off.",
+ &setlist),
+ &showlist);
+}
diff --git a/gnu/usr.bin/gdb/gdb/dcache.h b/gnu/usr.bin/gdb/gdb/dcache.h
new file mode 100644
index 0000000..bfc0dd7
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/dcache.h
@@ -0,0 +1,83 @@
+/* Declarations for caching. Typically used by remote back ends for
+ caching remote memory.
+
+ Copyright 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef DCACHE_H
+#define DCACHE_H
+
+/* The data cache leads to incorrect results because it doesn't know about
+ volatile variables, thus making it impossible to debug functions which
+ use hardware registers. Therefore it is #if 0'd out. Effect on
+ performance is some, for backtraces of functions with a few
+ arguments each. For functions with many arguments, the stack
+ frames don't fit in the cache blocks, which makes the cache less
+ helpful. Disabling the cache is a big performance win for fetching
+ large structures, because the cache code fetched data in 16-byte
+ chunks. */
+
+#define LINE_SIZE_POWER (4)
+/* eg 1<<3 == 8 */
+#define LINE_SIZE (1 << LINE_SIZE_POWER)
+/* Number of cache blocks */
+#define DCACHE_SIZE (64)
+
+struct dcache_block
+{
+ struct dcache_block *next, *last;
+ unsigned int addr; /* Address for which data is recorded. */
+ int data[LINE_SIZE / sizeof (int)];
+};
+
+typedef int (*memxferfunc) PARAMS((CORE_ADDR memaddr,
+ unsigned char *myaddr,
+ int len));
+
+typedef struct {
+ /* Function to actually read the target memory. */
+ memxferfunc read_memory;
+
+ /* Function to actually write the target memory */
+ memxferfunc write_memory;
+
+ /* free list */
+ struct dcache_block dcache_free;
+
+ /* in use list */
+ struct dcache_block dcache_valid;
+
+ /* The cache itself. */
+ struct dcache_block *the_cache;
+
+} DCACHE;
+
+/* Using the data cache DCACHE return the contents of the word at
+ address ADDR in the remote machine. */
+int dcache_fetch PARAMS((DCACHE *dcache, CORE_ADDR addr));
+
+/* Flush DCACHE. */
+void dcache_flush PARAMS((DCACHE *dcache));
+
+/* Initialize DCACHE. */
+DCACHE *dcache_init PARAMS((memxferfunc reading, memxferfunc writing));
+
+/* Write the word at ADDR both in the data cache and in the remote machine. */
+void dcache_poke PARAMS((DCACHE *dcache, CORE_ADDR addr, int data));
+
+#endif /* DCACHE_H */
diff --git a/gnu/usr.bin/gdb/gdb/defs.h b/gnu/usr.bin/gdb/gdb/defs.h
new file mode 100644
index 0000000..f65d56a
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/defs.h
@@ -0,0 +1,901 @@
+/* Basic, host-specific, and target-specific definitions for GDB.
+ Copyright (C) 1986, 1989, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (DEFS_H)
+#define DEFS_H 1
+
+#include <stdio.h>
+
+/* First include ansidecl.h so we can use the various macro definitions
+ here and in all subsequent file inclusions. */
+
+#include "ansidecl.h"
+
+/* An address in the program being debugged. Host byte order. */
+#ifndef CORE_ADDR_TYPE
+typedef unsigned int CORE_ADDR;
+#else
+typedef CORE_ADDR_TYPE CORE_ADDR;
+#endif
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#define max(a, b) ((a) > (b) ? (a) : (b))
+
+/* Gdb does *lots* of string compares. Use macros to speed them up by
+ avoiding function calls if the first characters are not the same. */
+
+#define STRCMP(a,b) (*(a) == *(b) ? strcmp ((a), (b)) : (int)*(a) - (int)*(b))
+#define STREQ(a,b) (*(a) == *(b) ? !strcmp ((a), (b)) : 0)
+#define STREQN(a,b,c) (*(a) == *(b) ? !strncmp ((a), (b), (c)) : 0)
+
+/* The character GNU C++ uses to build identifiers that must be unique from
+ the program's identifiers (such as $this and $$vptr). */
+#define CPLUS_MARKER '$' /* May be overridden to '.' for SysV */
+
+#include <errno.h> /* System call error return status */
+
+extern int quit_flag;
+extern int immediate_quit;
+extern int sevenbit_strings;
+
+extern void
+quit PARAMS ((void));
+
+#define QUIT { if (quit_flag) quit (); }
+
+/* Command classes are top-level categories into which commands are broken
+ down for "help" purposes.
+ Notes on classes: class_alias is for alias commands which are not
+ abbreviations of the original command. class-pseudo is for commands
+ which are not really commands nor help topics ("stop"). */
+
+enum command_class
+{
+ /* Special args to help_list */
+ all_classes = -2, all_commands = -1,
+ /* Classes of commands */
+ no_class = -1, class_run = 0, class_vars, class_stack,
+ class_files, class_support, class_info, class_breakpoint,
+ class_alias, class_obscure, class_user, class_maintenance,
+ class_pseudo
+};
+
+/* Languages represented in the symbol table and elsewhere.
+ This should probably be in language.h, but since enum's can't
+ be forward declared to satisfy opaque references before their
+ actual definition, needs to be here. */
+
+enum language
+{
+ language_unknown, /* Language not known */
+ language_auto, /* Placeholder for automatic setting */
+ language_c, /* C */
+ language_cplus, /* C++ */
+ language_chill, /* Chill */
+ language_m2 /* Modula-2 */
+};
+
+/* the cleanup list records things that have to be undone
+ if an error happens (descriptors to be closed, memory to be freed, etc.)
+ Each link in the chain records a function to call and an
+ argument to give it.
+
+ Use make_cleanup to add an element to the cleanup chain.
+ Use do_cleanups to do all cleanup actions back to a given
+ point in the chain. Use discard_cleanups to remove cleanups
+ from the chain back to a given point, not doing them. */
+
+struct cleanup
+{
+ struct cleanup *next;
+ void (*function) PARAMS ((PTR));
+ PTR arg;
+};
+
+/* From blockframe.c */
+
+extern int
+inside_entry_func PARAMS ((CORE_ADDR));
+
+extern int
+inside_entry_file PARAMS ((CORE_ADDR addr));
+
+extern int
+inside_main_func PARAMS ((CORE_ADDR pc));
+
+/* From ch-lang.c, for the moment. (FIXME) */
+
+extern char *
+chill_demangle PARAMS ((const char *));
+
+/* From libiberty.a */
+
+extern char *
+cplus_demangle PARAMS ((const char *, int));
+
+extern char *
+cplus_mangle_opname PARAMS ((char *, int));
+
+/* From libmmalloc.a (memory mapped malloc library) */
+
+extern PTR
+mmalloc_attach PARAMS ((int, PTR));
+
+extern PTR
+mmalloc_detach PARAMS ((PTR));
+
+extern PTR
+mmalloc PARAMS ((PTR, long));
+
+extern PTR
+mrealloc PARAMS ((PTR, PTR, long));
+
+extern void
+mfree PARAMS ((PTR, PTR));
+
+extern int
+mmalloc_setkey PARAMS ((PTR, int, PTR));
+
+extern PTR
+mmalloc_getkey PARAMS ((PTR, int));
+
+/* From utils.c */
+
+extern int
+strcmp_iw PARAMS ((const char *, const char *));
+
+extern char *
+safe_strerror PARAMS ((int));
+
+extern char *
+safe_strsignal PARAMS ((int));
+
+extern void
+init_malloc PARAMS ((void *));
+
+extern void
+request_quit PARAMS ((int));
+
+extern void
+do_cleanups PARAMS ((struct cleanup *));
+
+extern void
+discard_cleanups PARAMS ((struct cleanup *));
+
+/* The bare make_cleanup function is one of those rare beasts that
+ takes almost any type of function as the first arg and anything that
+ will fit in a "void *" as the second arg.
+
+ Should be, once all calls and called-functions are cleaned up:
+extern struct cleanup *
+make_cleanup PARAMS ((void (*function) (void *), void *));
+
+ Until then, lint and/or various type-checking compiler options will
+ complain about make_cleanup calls. It'd be wrong to just cast things,
+ since the type actually passed when the function is called would be
+ wrong. */
+
+extern struct cleanup *
+make_cleanup ();
+
+extern struct cleanup *
+save_cleanups PARAMS ((void));
+
+extern void
+restore_cleanups PARAMS ((struct cleanup *));
+
+extern void
+free_current_contents PARAMS ((char **));
+
+extern void
+null_cleanup PARAMS ((char **));
+
+extern int
+myread PARAMS ((int, char *, int));
+
+extern int
+query ();
+
+extern void
+begin_line PARAMS ((void));
+
+extern void
+wrap_here PARAMS ((char *));
+
+extern void
+reinitialize_more_filter PARAMS ((void));
+
+extern int
+print_insn PARAMS ((CORE_ADDR, FILE *));
+
+extern void
+fputs_filtered PARAMS ((const char *, FILE *));
+
+extern void
+puts_filtered PARAMS ((char *));
+
+extern void
+vprintf_filtered ();
+
+extern void
+vfprintf_filtered ();
+
+extern void
+fprintf_filtered ();
+
+extern void
+fprintfi_filtered ();
+
+extern void
+printf_filtered ();
+
+extern void
+printfi_filtered ();
+
+extern void
+print_spaces PARAMS ((int, FILE *));
+
+extern void
+print_spaces_filtered PARAMS ((int, FILE *));
+
+extern char *
+n_spaces PARAMS ((int));
+
+extern void
+gdb_printchar PARAMS ((int, FILE *, int));
+
+extern void
+fprintf_symbol_filtered PARAMS ((FILE *, char *, enum language, int));
+
+extern void
+perror_with_name PARAMS ((char *));
+
+extern void
+print_sys_errmsg PARAMS ((char *, int));
+
+/* From regex.c or libc. BSD 4.4 declares this with the argument type as
+ "const char *" in unistd.h, so we can't declare the argument
+ as "char *". */
+
+extern char *
+re_comp PARAMS ((const char *));
+
+/* From symfile.c */
+
+extern void
+symbol_file_command PARAMS ((char *, int));
+
+/* From main.c */
+
+extern char *
+skip_quoted PARAMS ((char *));
+
+extern char *
+gdb_readline PARAMS ((char *));
+
+extern char *
+command_line_input PARAMS ((char *, int));
+
+extern void
+print_prompt PARAMS ((void));
+
+extern int
+batch_mode PARAMS ((void));
+
+extern int
+input_from_terminal_p PARAMS ((void));
+
+/* From printcmd.c */
+
+extern void
+set_next_address PARAMS ((CORE_ADDR));
+
+extern void
+print_address_symbolic PARAMS ((CORE_ADDR, FILE *, int, char *));
+
+extern void
+print_address PARAMS ((CORE_ADDR, FILE *));
+
+/* From source.c */
+
+extern int
+openp PARAMS ((char *, int, char *, int, int, char **));
+
+extern void
+mod_path PARAMS ((char *, char **));
+
+extern void
+directory_command PARAMS ((char *, int));
+
+extern void
+init_source_path PARAMS ((void));
+
+/* From findvar.c */
+
+extern int
+read_relative_register_raw_bytes PARAMS ((int, char *));
+
+/* From readline (but not in any readline .h files). */
+
+extern char *
+tilde_expand PARAMS ((char *));
+
+/* Structure for saved commands lines
+ (for breakpoints, defined commands, etc). */
+
+struct command_line
+{
+ struct command_line *next;
+ char *line;
+};
+
+extern struct command_line *
+read_command_lines PARAMS ((void));
+
+extern void
+free_command_lines PARAMS ((struct command_line **));
+
+/* String containing the current directory (what getwd would return). */
+
+extern char *current_directory;
+
+/* Default radixes for input and output. Only some values supported. */
+extern unsigned input_radix;
+extern unsigned output_radix;
+
+/* Possibilities for prettyprint parameters to routines which print
+ things. Like enum language, this should be in value.h, but needs
+ to be here for the same reason. FIXME: If we can eliminate this
+ as an arg to LA_VAL_PRINT, then we can probably move it back to
+ value.h. */
+
+enum val_prettyprint
+{
+ Val_no_prettyprint = 0,
+ Val_prettyprint,
+ /* Use the default setting which the user has specified. */
+ Val_pretty_default
+};
+
+
+/* Host machine definition. This will be a symlink to one of the
+ xm-*.h files, built by the `configure' script. */
+
+#include "xm.h"
+
+/* Native machine support. This will be a symlink to one of the
+ nm-*.h files, built by the `configure' script. */
+
+#include "nm.h"
+
+/* If the xm.h file did not define the mode string used to open the
+ files, assume that binary files are opened the same way as text
+ files */
+#ifndef FOPEN_RB
+#include "fopen-same.h"
+#endif
+
+/*
+ * Allow things in gdb to be declared "const". If compiling ANSI, it
+ * just works. If compiling with gcc but non-ansi, redefine to __const__.
+ * If non-ansi, non-gcc, then eliminate "const" entirely, making those
+ * objects be read-write rather than read-only.
+ */
+
+#ifndef const
+#ifndef __STDC__
+# ifdef __GNUC__
+# define const __const__
+# else
+# define const /*nothing*/
+# endif /* GNUC */
+#endif /* STDC */
+#endif /* const */
+
+#ifndef volatile
+#ifndef __STDC__
+# ifdef __GNUC__
+# define volatile __volatile__
+# else
+# define volatile /*nothing*/
+# endif /* GNUC */
+#endif /* STDC */
+#endif /* volatile */
+
+#if 1
+#define NORETURN /*nothing*/
+#else /* not 1 */
+/* FIXME: This is bogus. Having "volatile void" mean a function doesn't
+ return is a gcc extension and should be based on #ifdef __GNUC__.
+ Also, as of Sep 93 I'm told gcc is changing the syntax for ansi
+ reasons (so declaring exit here as "volatile void" and as "void" in
+ a system header loses). Using the new "__attributes__ ((noreturn));"
+ syntax would lose for old versions of gcc; using
+ typedef void exit_fn_type PARAMS ((int));
+ volatile exit_fn_type exit;
+ would win. */
+/* Some compilers (many AT&T SVR4 compilers for instance), do not accept
+ declarations of functions that never return (exit for instance) as
+ "volatile void". For such compilers "NORETURN" can be defined away
+ to keep them happy */
+
+#ifndef NORETURN
+# ifdef __lucid
+# define NORETURN /*nothing*/
+# else
+# define NORETURN volatile
+# endif
+#endif
+#endif /* not 1 */
+
+/* Defaults for system-wide constants (if not defined by xm.h, we fake it). */
+
+#if !defined (UINT_MAX)
+#define UINT_MAX ((unsigned int)(~0)) /* 0xFFFFFFFF for 32-bits */
+#endif
+
+#if !defined (INT_MAX)
+#define INT_MAX ((int)(UINT_MAX >> 1)) /* 0x7FFFFFFF for 32-bits */
+#endif
+
+#if !defined (INT_MIN)
+#define INT_MIN (-INT_MAX - 1) /* 0x80000000 for 32-bits */
+#endif
+
+#if !defined (ULONG_MAX)
+#define ULONG_MAX ((unsigned long)(~0L)) /* 0xFFFFFFFF for 32-bits */
+#endif
+
+#if !defined (LONG_MAX)
+#define LONG_MAX ((long)(ULONG_MAX >> 1)) /* 0x7FFFFFFF for 32-bits */
+#endif
+
+/* Number of bits in a char or unsigned char for the target machine.
+ Just like CHAR_BIT in <limits.h> but describes the target machine. */
+#if !defined (TARGET_CHAR_BIT)
+#define TARGET_CHAR_BIT 8
+#endif
+
+/* Number of bits in a short or unsigned short for the target machine. */
+#if !defined (TARGET_SHORT_BIT)
+#define TARGET_SHORT_BIT (2 * TARGET_CHAR_BIT)
+#endif
+
+/* Number of bits in an int or unsigned int for the target machine. */
+#if !defined (TARGET_INT_BIT)
+#define TARGET_INT_BIT (4 * TARGET_CHAR_BIT)
+#endif
+
+/* Number of bits in a long or unsigned long for the target machine. */
+#if !defined (TARGET_LONG_BIT)
+#define TARGET_LONG_BIT (4 * TARGET_CHAR_BIT)
+#endif
+
+/* Number of bits in a long long or unsigned long long for the target machine. */
+#if !defined (TARGET_LONG_LONG_BIT)
+#define TARGET_LONG_LONG_BIT (2 * TARGET_LONG_BIT)
+#endif
+
+/* Number of bits in a float for the target machine. */
+#if !defined (TARGET_FLOAT_BIT)
+#define TARGET_FLOAT_BIT (4 * TARGET_CHAR_BIT)
+#endif
+
+/* Number of bits in a double for the target machine. */
+#if !defined (TARGET_DOUBLE_BIT)
+#define TARGET_DOUBLE_BIT (8 * TARGET_CHAR_BIT)
+#endif
+
+/* Number of bits in a long double for the target machine. */
+#if !defined (TARGET_LONG_DOUBLE_BIT)
+#define TARGET_LONG_DOUBLE_BIT (2 * TARGET_DOUBLE_BIT)
+#endif
+
+/* Number of bits in a "complex" for the target machine. */
+#if !defined (TARGET_COMPLEX_BIT)
+#define TARGET_COMPLEX_BIT (2 * TARGET_FLOAT_BIT)
+#endif
+
+/* Number of bits in a "double complex" for the target machine. */
+#if !defined (TARGET_DOUBLE_COMPLEX_BIT)
+#define TARGET_DOUBLE_COMPLEX_BIT (2 * TARGET_DOUBLE_BIT)
+#endif
+
+/* Number of bits in a pointer for the target machine */
+#if !defined (TARGET_PTR_BIT)
+#define TARGET_PTR_BIT TARGET_INT_BIT
+#endif
+
+/* Default to support for "long long" if the host compiler being used is gcc.
+ Config files must define CC_HAS_LONG_LONG to use other host compilers
+ that are capable of supporting "long long", and to cause gdb to use that
+ support. Not defining CC_HAS_LONG_LONG will suppress use of "long long"
+ regardless of what compiler is used.
+
+ FIXME: For now, automatic selection of "long long" as the default when
+ gcc is used is disabled, pending further testing. Concerns include the
+ impact on gdb performance and the universality of bugfree long long
+ support on platforms that do have gcc. Compiling with FORCE_LONG_LONG
+ will select "long long" use for testing purposes. -fnf */
+
+#ifndef CC_HAS_LONG_LONG
+# if defined (__GNUC__) && defined (FORCE_LONG_LONG) /* See FIXME above */
+# define CC_HAS_LONG_LONG 1
+# endif
+#endif
+
+/* LONGEST should not be a typedef, because "unsigned LONGEST" needs to work.
+ CC_HAS_LONG_LONG is defined if the host compiler supports "long long"
+ variables and we wish to make use of that support. */
+
+#ifndef LONGEST
+# ifdef CC_HAS_LONG_LONG
+# define LONGEST long long
+# else
+# define LONGEST long
+# endif
+#endif
+
+/* Convert a LONGEST to an int. This is used in contexts (e.g. number of
+ arguments to a function, number in a value history, register number, etc.)
+ where the value must not be larger than can fit in an int. */
+
+#ifndef longest_to_int
+# ifdef CC_HAS_LONG_LONG
+# define longest_to_int(x) (((x) > INT_MAX || (x) < INT_MIN) \
+ ? (error ("Value out of range."),0) : (int) (x))
+# else
+ /* Assume sizeof (int) == sizeof (long). */
+# define longest_to_int(x) ((int) (x))
+# endif
+#endif
+
+/* If we picked up a copy of CHAR_BIT from a configuration file
+ (which may get it by including <limits.h>) then use it to set
+ the number of bits in a host char. If not, use the same size
+ as the target. */
+
+#if defined (CHAR_BIT)
+#define HOST_CHAR_BIT CHAR_BIT
+#else
+#define HOST_CHAR_BIT TARGET_CHAR_BIT
+#endif
+
+/* Assorted functions we can declare, now that const and volatile are
+ defined. */
+
+extern char *
+savestring PARAMS ((const char *, int));
+
+extern char *
+msavestring PARAMS ((void *, const char *, int));
+
+extern char *
+strsave PARAMS ((const char *));
+
+extern char *
+mstrsave PARAMS ((void *, const char *));
+
+extern char *
+concat PARAMS ((char *, ...));
+
+extern PTR
+xmalloc PARAMS ((long));
+
+extern PTR
+xrealloc PARAMS ((PTR, long));
+
+extern PTR
+xmmalloc PARAMS ((PTR, long));
+
+extern PTR
+xmrealloc PARAMS ((PTR, PTR, long));
+
+extern PTR
+mmalloc PARAMS ((PTR, long));
+
+extern PTR
+mrealloc PARAMS ((PTR, PTR, long));
+
+extern void
+mfree PARAMS ((PTR, PTR));
+
+extern int
+mmcheck PARAMS ((PTR, void (*) (void)));
+
+extern int
+mmtrace PARAMS ((void));
+
+extern int
+parse_escape PARAMS ((char **));
+
+extern const char * const reg_names[];
+
+extern NORETURN void /* Does not return to the caller. */
+error ();
+
+extern NORETURN void /* Does not return to the caller. */
+fatal ();
+
+extern NORETURN void /* Not specified as volatile in ... */
+exit PARAMS ((int)); /* 4.10.4.3 */
+
+extern NORETURN void /* Does not return to the caller. */
+nomem PARAMS ((long));
+
+/* Reasons for calling return_to_top_level. */
+enum return_reason {
+ /* User interrupt. */
+ RETURN_QUIT,
+
+ /* Any other error. */
+ RETURN_ERROR
+};
+
+#define RETURN_MASK_QUIT (1 << (int)RETURN_QUIT)
+#define RETURN_MASK_ERROR (1 << (int)RETURN_ERROR)
+#define RETURN_MASK_ALL (RETURN_MASK_QUIT | RETURN_MASK_ERROR)
+typedef int return_mask;
+
+extern NORETURN void /* Does not return to the caller. */
+return_to_top_level PARAMS ((enum return_reason));
+
+extern int catch_errors PARAMS ((int (*) (char *), void *, char *,
+ return_mask));
+
+extern void
+warning_setup PARAMS ((void));
+
+extern void
+warning ();
+
+/* Global functions from other, non-gdb GNU thingies (libiberty for
+ instance) */
+
+extern char *
+basename PARAMS ((char *));
+
+extern char *
+getenv PARAMS ((const char *));
+
+extern char **
+buildargv PARAMS ((char *));
+
+extern void
+freeargv PARAMS ((char **));
+
+extern char *
+strerrno PARAMS ((int));
+
+extern char *
+strsigno PARAMS ((int));
+
+extern int
+errno_max PARAMS ((void));
+
+extern int
+signo_max PARAMS ((void));
+
+extern int
+strtoerrno PARAMS ((char *));
+
+extern int
+strtosigno PARAMS ((char *));
+
+extern char *
+strsignal PARAMS ((int));
+
+/* From other system libraries */
+
+#ifndef PSIGNAL_IN_SIGNAL_H
+extern void
+psignal PARAMS ((unsigned, const char *));
+#endif
+
+/* For now, we can't include <stdlib.h> because it conflicts with
+ "../include/getopt.h". (FIXME)
+
+ However, if a function is defined in the ANSI C standard and a prototype
+ for that function is defined and visible in any header file in an ANSI
+ conforming environment, then that prototype must match the definition in
+ the ANSI standard. So we can just duplicate them here without conflict,
+ since they must be the same in all conforming ANSI environments. If
+ these cause problems, then the environment is not ANSI conformant. */
+
+#ifdef __STDC__
+#include <stddef.h>
+#endif
+
+extern int
+fclose PARAMS ((FILE *stream)); /* 4.9.5.1 */
+
+extern void
+perror PARAMS ((const char *)); /* 4.9.10.4 */
+
+extern double
+atof PARAMS ((const char *nptr)); /* 4.10.1.1 */
+
+extern int
+atoi PARAMS ((const char *)); /* 4.10.1.2 */
+
+#ifndef MALLOC_INCOMPATIBLE
+
+extern PTR
+malloc PARAMS ((size_t size)); /* 4.10.3.3 */
+
+extern PTR
+realloc PARAMS ((void *ptr, size_t size)); /* 4.10.3.4 */
+
+extern void
+free PARAMS ((void *)); /* 4.10.3.2 */
+
+#endif /* MALLOC_INCOMPATIBLE */
+
+extern void
+qsort PARAMS ((void *base, size_t nmemb, /* 4.10.5.2 */
+ size_t size,
+ int (*comp)(const void *, const void *)));
+
+#ifndef MEM_FNS_DECLARED /* Some non-ANSI use void *, not char *. */
+extern PTR
+memcpy PARAMS ((void *, const void *, size_t)); /* 4.11.2.1 */
+
+extern int
+memcmp PARAMS ((const void *, const void *, size_t)); /* 4.11.4.1 */
+#endif
+
+extern char *
+strchr PARAMS ((const char *, int)); /* 4.11.5.2 */
+
+extern char *
+strrchr PARAMS ((const char *, int)); /* 4.11.5.5 */
+
+extern char *
+strstr PARAMS ((const char *, const char *)); /* 4.11.5.7 */
+
+extern char *
+strtok PARAMS ((char *, const char *)); /* 4.11.5.8 */
+
+#ifndef MEM_FNS_DECLARED /* Some non-ANSI use void *, not char *. */
+extern PTR
+memset PARAMS ((void *, int, size_t)); /* 4.11.6.1 */
+#endif
+
+extern char *
+strerror PARAMS ((int)); /* 4.11.6.2 */
+
+/* Various possibilities for alloca. */
+#ifndef alloca
+# ifdef __GNUC__
+# define alloca __builtin_alloca
+# else
+# ifdef sparc
+# include <alloca.h> /* NOTE: Doesn't declare alloca() */
+# endif
+# ifdef __STDC__
+ extern void *alloca (size_t);
+# else /* __STDC__ */
+ extern char *alloca ();
+# endif
+# endif
+#endif
+
+/* TARGET_BYTE_ORDER and HOST_BYTE_ORDER must be defined to one of these. */
+
+#if !defined (BIG_ENDIAN)
+#define BIG_ENDIAN 4321
+#endif
+
+#if !defined (LITTLE_ENDIAN)
+#define LITTLE_ENDIAN 1234
+#endif
+
+/* Target-system-dependent parameters for GDB. */
+
+/* Target machine definition. This will be a symlink to one of the
+ tm-*.h files, built by the `configure' script. */
+
+#include "tm.h"
+
+/* The bit byte-order has to do just with numbering of bits in
+ debugging symbols and such. Conceptually, it's quite separate
+ from byte/word byte order. */
+
+#if !defined (BITS_BIG_ENDIAN)
+#if TARGET_BYTE_ORDER == BIG_ENDIAN
+#define BITS_BIG_ENDIAN 1
+#endif /* Big endian. */
+
+#if TARGET_BYTE_ORDER == LITTLE_ENDIAN
+#define BITS_BIG_ENDIAN 0
+#endif /* Little endian. */
+#endif /* BITS_BIG_ENDIAN not defined. */
+
+/* Swap LEN bytes at BUFFER between target and host byte-order. This is
+ the wrong way to do byte-swapping because it assumes that you have a way
+ to have a host variable of exactly the right size.
+ extract_* are the right way. */
+#if TARGET_BYTE_ORDER == HOST_BYTE_ORDER
+#define SWAP_TARGET_AND_HOST(buffer,len)
+#else /* Target and host byte order differ. */
+#define SWAP_TARGET_AND_HOST(buffer,len) \
+ { \
+ char tmp; \
+ char *p = (char *)(buffer); \
+ char *q = ((char *)(buffer)) + len - 1; \
+ for (; p < q; p++, q--) \
+ { \
+ tmp = *q; \
+ *q = *p; \
+ *p = tmp; \
+ } \
+ }
+#endif /* Target and host byte order differ. */
+
+/* In findvar.c. */
+LONGEST extract_signed_integer PARAMS ((void *, int));
+unsigned LONGEST extract_unsigned_integer PARAMS ((void *, int));
+CORE_ADDR extract_address PARAMS ((void *, int));
+
+void store_signed_integer PARAMS ((void *, int, LONGEST));
+void store_unsigned_integer PARAMS ((void *, int, unsigned LONGEST));
+void store_address PARAMS ((void *, int, CORE_ADDR));
+
+/* On some machines there are bits in addresses which are not really
+ part of the address, but are used by the kernel, the hardware, etc.
+ for special purposes. ADDR_BITS_REMOVE takes out any such bits
+ so we get a "real" address such as one would find in a symbol
+ table. This is used only for addresses of instructions, and even then
+ I'm not sure it's used in all contexts. It exists to deal with there
+ being a few stray bits in the PC which would mislead us, not as some sort
+ of generic thing to handle alignment or segmentation (it's possible it
+ should be in TARGET_READ_PC instead). */
+#if !defined (ADDR_BITS_REMOVE)
+#define ADDR_BITS_REMOVE(addr) (addr)
+#endif /* No ADDR_BITS_REMOVE. */
+
+/* From valops.c */
+
+extern CORE_ADDR
+push_bytes PARAMS ((CORE_ADDR, char *, int));
+
+/* In some modules, we don't have a definition of REGISTER_TYPE yet, so we
+ must avoid prototyping this function for now. FIXME. Should be:
+extern CORE_ADDR
+push_word PARAMS ((CORE_ADDR, REGISTER_TYPE));
+ */
+extern CORE_ADDR
+push_word ();
+
+/* Some parts of gdb might be considered optional, in the sense that they
+ are not essential for being able to build a working, usable debugger
+ for a specific environment. For example, the maintenance commands
+ are there for the benefit of gdb maintainers. As another example,
+ some environments really don't need gdb's that are able to read N
+ different object file formats. In order to make it possible (but
+ not necessarily recommended) to build "stripped down" versions of
+ gdb, the following defines control selective compilation of those
+ parts of gdb which can be safely left out when necessary. Note that
+ the default is to include everything. */
+
+#ifndef MAINTENANCE_CMDS
+#define MAINTENANCE_CMDS 1
+#endif
+
+#endif /* !defined (DEFS_H) */
diff --git a/gnu/usr.bin/gdb/gdb/demangle.c b/gnu/usr.bin/gdb/gdb/demangle.c
new file mode 100644
index 0000000..a134bb7
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/demangle.c
@@ -0,0 +1,190 @@
+/* Basic C++ demangling support for GDB.
+ Copyright 1991, 1992 Free Software Foundation, Inc.
+ Written by Fred Fish at Cygnus Support.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file contains support code for C++ demangling that is common
+ to a styles of demangling, and GDB specific. */
+
+#include "defs.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "demangle.h"
+#include <string.h>
+
+/* Select the default C++ demangling style to use. The default is "auto",
+ which allows gdb to attempt to pick an appropriate demangling style for
+ the executable it has loaded. It can be set to a specific style ("gnu",
+ "lucid", "arm", etc.) in which case gdb will never attempt to do auto
+ selection of the style unless you do an explicit "set demangle auto".
+ To select one of these as the default, set DEFAULT_DEMANGLING_STYLE in
+ the appropriate target configuration file. */
+
+#ifndef DEFAULT_DEMANGLING_STYLE
+# define DEFAULT_DEMANGLING_STYLE AUTO_DEMANGLING_STYLE_STRING
+#endif
+
+/* String name for the current demangling style. Set by the "set demangling"
+ command, printed as part of the output by the "show demangling" command. */
+
+static char *current_demangling_style_string;
+
+/* List of supported demangling styles. Contains the name of the style as
+ seen by the user, and the enum value that corresponds to that style. */
+
+static const struct demangler
+{
+ char *demangling_style_name;
+ enum demangling_styles demangling_style;
+ char *demangling_style_doc;
+} demanglers [] =
+{
+ {AUTO_DEMANGLING_STYLE_STRING,
+ auto_demangling,
+ "Automatic selection based on executable"},
+ {GNU_DEMANGLING_STYLE_STRING,
+ gnu_demangling,
+ "GNU (g++) style demangling"},
+ {LUCID_DEMANGLING_STYLE_STRING,
+ lucid_demangling,
+ "Lucid (lcc) style demangling"},
+ {ARM_DEMANGLING_STYLE_STRING,
+ arm_demangling,
+ "ARM style demangling"},
+ {NULL, unknown_demangling, NULL}
+};
+
+/* show current demangling style. */
+
+static void
+show_demangling_command (ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ /* done automatically by show command. */
+}
+
+
+/* set current demangling style. called by the "set demangling" command
+ after it has updated the current_demangling_style_string to match
+ what the user has entered.
+
+ if the user has entered a string that matches a known demangling style
+ name in the demanglers[] array then just leave the string alone and update
+ the current_demangling_style enum value to match.
+
+ if the user has entered a string that doesn't match, including an empty
+ string, then print a list of the currently known styles and restore
+ the current_demangling_style_string to match the current_demangling_style
+ enum value.
+
+ Note: Assumes that current_demangling_style_string always points to
+ a malloc'd string, even if it is a null-string. */
+
+static void
+set_demangling_command (ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ const struct demangler *dem;
+
+ /* First just try to match whatever style name the user supplied with
+ one of the known ones. Don't bother special casing for an empty
+ name, we just treat it as any other style name that doesn't match.
+ If we match, update the current demangling style enum. */
+
+ for (dem = demanglers; dem -> demangling_style_name != NULL; dem++)
+ {
+ if (STREQ (current_demangling_style_string,
+ dem -> demangling_style_name))
+ {
+ current_demangling_style = dem -> demangling_style;
+ break;
+ }
+ }
+
+ /* Check to see if we found a match. If not, gripe about any non-empty
+ style name and supply a list of valid ones. FIXME: This should
+ probably be done with some sort of completion and with help. */
+
+ if (dem -> demangling_style_name == NULL)
+ {
+ if (*current_demangling_style_string != '\0')
+ {
+ printf ("Unknown demangling style `%s'.\n",
+ current_demangling_style_string);
+ }
+ printf ("The currently understood settings are:\n\n");
+ for (dem = demanglers; dem -> demangling_style_name != NULL; dem++)
+ {
+ printf ("%-10s %s\n", dem -> demangling_style_name,
+ dem -> demangling_style_doc);
+ if (dem -> demangling_style == current_demangling_style)
+ {
+ free (current_demangling_style_string);
+ current_demangling_style_string =
+ strdup (dem -> demangling_style_name);
+ }
+ }
+ if (current_demangling_style == unknown_demangling)
+ {
+ /* This can happen during initialization if gdb is compiled with
+ a DEMANGLING_STYLE value that is unknown, so pick the first
+ one as the default. */
+ current_demangling_style = demanglers[0].demangling_style;
+ current_demangling_style_string =
+ strdup (demanglers[0].demangling_style_name);
+ warning ("`%s' style demangling chosen as the default.\n",
+ current_demangling_style_string);
+ }
+ }
+}
+
+/* Fake a "set demangling" command. */
+
+void
+set_demangling_style (style)
+ char *style;
+{
+ if (current_demangling_style_string != NULL)
+ {
+ free (current_demangling_style_string);
+ }
+ current_demangling_style_string = strdup (style);
+ set_demangling_command ((char *) NULL, 0);
+}
+
+void
+_initialize_demangler ()
+{
+ struct cmd_list_element *set, *show;
+
+ set = add_set_cmd ("demangle-style", class_support, var_string_noescape,
+ (char *) &current_demangling_style_string,
+ "Set the current C++ demangling style.\n\
+Use `set demangle-style' without arguments for a list of demangling styles.",
+ &setlist);
+ show = add_show_from_set (set, &showlist);
+ set -> function.cfunc = set_demangling_command;
+ show -> function.cfunc = show_demangling_command;
+
+ /* Set the default demangling style chosen at compilation time. */
+ set_demangling_style (DEFAULT_DEMANGLING_STYLE);
+ set_cplus_marker_for_demangling (CPLUS_MARKER);
+}
diff --git a/gnu/usr.bin/gdb/gdb/demangle.h b/gnu/usr.bin/gdb/gdb/demangle.h
new file mode 100644
index 0000000..4f191a2
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/demangle.h
@@ -0,0 +1,77 @@
+/* Defs for interface to demanglers.
+ Copyright 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#if !defined (DEMANGLE_H)
+#define DEMANGLE_H
+
+#include <ansidecl.h>
+
+/* Options passed to cplus_demangle (in 2nd parameter). */
+
+#define DMGL_NO_OPTS 0 /* For readability... */
+#define DMGL_PARAMS (1 << 0) /* Include function args */
+#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
+
+#define DMGL_AUTO (1 << 8)
+#define DMGL_GNU (1 << 9)
+#define DMGL_LUCID (1 << 10)
+#define DMGL_ARM (1 << 11)
+/* If none of these are set, use 'current_demangling_style' as the default. */
+#define DMGL_STYLE_MASK (DMGL_AUTO|DMGL_GNU|DMGL_LUCID|DMGL_ARM)
+
+/* Enumeration of possible demangling styles.
+
+ Lucid and ARM styles are still kept logically distinct, even though
+ they now both behave identically. The resulting style is actual the
+ union of both. I.E. either style recognizes both "__pt__" and "__rf__"
+ for operator "->", even though the first is lucid style and the second
+ is ARM style. (FIXME?) */
+
+extern enum demangling_styles
+{
+ unknown_demangling = 0,
+ auto_demangling = DMGL_AUTO,
+ gnu_demangling = DMGL_GNU,
+ lucid_demangling = DMGL_LUCID,
+ arm_demangling = DMGL_ARM
+} current_demangling_style;
+
+/* Define string names for the various demangling styles. */
+
+#define AUTO_DEMANGLING_STYLE_STRING "auto"
+#define GNU_DEMANGLING_STYLE_STRING "gnu"
+#define LUCID_DEMANGLING_STYLE_STRING "lucid"
+#define ARM_DEMANGLING_STYLE_STRING "arm"
+
+/* Some macros to test what demangling style is active. */
+
+#define CURRENT_DEMANGLING_STYLE current_demangling_style
+#define AUTO_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_AUTO)
+#define GNU_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_GNU)
+#define LUCID_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_LUCID)
+#define ARM_DEMANGLING (CURRENT_DEMANGLING_STYLE & DMGL_ARM)
+
+extern char *
+cplus_demangle PARAMS ((CONST char *mangled, int options));
+
+/* Note: This sets global state. FIXME if you care about multi-threading. */
+
+extern void
+set_cplus_marker_for_demangling PARAMS ((int ch));
+
+#endif /* DEMANGLE_H */
diff --git a/gnu/usr.bin/gdb/gdb/dis-asm.h b/gnu/usr.bin/gdb/gdb/dis-asm.h
new file mode 100644
index 0000000..e7f106c
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/dis-asm.h
@@ -0,0 +1,176 @@
+/* Interface between the opcode library and its callers.
+ Written by Cygnus Support, 1993.
+
+ The opcode library (libopcodes.a) provides instruction decoders for
+ a large variety of instruction sets, callable with an identical
+ interface, for making instruction-processing programs more independent
+ of the instruction set being processed. */
+
+#include <stdio.h>
+#include "bfd.h"
+
+typedef int (*fprintf_ftype) PARAMS((FILE*, const char*, ...));
+
+enum dis_insn_type {
+ dis_noninsn, /* Not a valid instruction */
+ dis_nonbranch, /* Not a branch instruction */
+ dis_branch, /* Unconditional branch */
+ dis_condbranch, /* Conditional branch */
+ dis_jsr, /* Jump to subroutine */
+ dis_condjsr, /* Conditional jump to subroutine */
+ dis_dref, /* Data reference instruction */
+ dis_dref2, /* Two data references in instruction */
+};
+
+/* This struct is passed into the instruction decoding routine,
+ and is passed back out into each callback. The various fields are used
+ for conveying information from your main routine into your callbacks,
+ for passing information into the instruction decoders (such as the
+ addresses of the callback functions), or for passing information
+ back from the instruction decoders to their callers.
+
+ It must be initialized before it is first passed; this can be done
+ by hand, or using one of the initialization macros below. */
+
+typedef struct disassemble_info {
+ fprintf_ftype fprintf_func;
+ FILE *stream;
+ PTR application_data;
+
+ /* For use by the disassembler. */
+ int flags;
+ PTR private_data;
+
+ /* Function used to get bytes to disassemble. MEMADDR is the
+ address of the stuff to be disassembled, MYADDR is the address to
+ put the bytes in, and LENGTH is the number of bytes to read.
+ INFO is a pointer to this struct.
+ Returns an errno value or 0 for success. */
+ int (*read_memory_func)
+ PARAMS ((bfd_vma memaddr, bfd_byte *myaddr, int length,
+ struct disassemble_info *info));
+
+ /* Function which should be called if we get an error that we can't
+ recover from. STATUS is the errno value from read_memory_func and
+ MEMADDR is the address that we were trying to read. INFO is a
+ pointer to this struct. */
+ void (*memory_error_func)
+ PARAMS ((int status, bfd_vma memaddr, struct disassemble_info *info));
+
+ /* Function called to print ADDR. */
+ void (*print_address_func)
+ PARAMS ((bfd_vma addr, struct disassemble_info *info));
+
+ /* These are for buffer_read_memory. */
+ bfd_byte *buffer;
+ bfd_vma buffer_vma;
+ int buffer_length;
+
+ /* Results from instruction decoders. Not all decoders yet support
+ this information. This info is set each time an instruction is
+ decoded, and is only valid for the last such instruction.
+
+ To determine whether this decoder supports this information, set
+ insn_info_valid to 0, decode an instruction, then check it. */
+
+ char insn_info_valid; /* Branch info has been set. */
+ char branch_delay_insns; /* How many sequential insn's will run before
+ a branch takes effect. (0 = normal) */
+ char data_size; /* Size of data reference in insn, in bytes */
+ enum dis_insn_type insn_type; /* Type of instruction */
+ bfd_vma target; /* Target address of branch or dref, if known;
+ zero if unknown. */
+ bfd_vma target2; /* Second target address for dref2 */
+
+} disassemble_info;
+
+
+
+
+
+
+/* Standard disassemblers. Disassemble one instruction at the given
+ target address. Return number of bytes processed. */
+typedef int (*disassembler_ftype)
+ PARAMS((bfd_vma, disassemble_info *));
+
+extern int print_insn_big_mips PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_little_mips PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_i386 PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_m68k PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_z8001 PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_z8002 PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_h8300 PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_h8300h PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_h8500 PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_alpha PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_sparc PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_big_a29k PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_little_a29k PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_i960 PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_sh PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_hppa PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_m88k PARAMS ((bfd_vma, disassemble_info*));
+
+
+
+
+
+
+/* This block of definitions is for particular callers who read instructions
+ into a buffer before calling the instruction decoder. */
+
+/* Here is a function which callers may wish to use for read_memory_func.
+ It gets bytes from a buffer. */
+extern int buffer_read_memory
+ PARAMS ((bfd_vma, bfd_byte *, int, struct disassemble_info *));
+
+/* This function goes with buffer_read_memory.
+ It prints a message using info->fprintf_func and info->stream. */
+extern void perror_memory PARAMS ((int, bfd_vma, struct disassemble_info *));
+
+
+/* Just print the address is hex. This is included for completeness even
+ though both GDB and objdump provide their own (to print symbolic
+ addresses). */
+extern void generic_print_address
+ PARAMS ((bfd_vma, struct disassemble_info *));
+
+#define INIT_DISASSEMBLE_INFO(INFO, STREAM) \
+ (INFO).fprintf_func = (fprintf_ftype)fprintf, \
+ (INFO).stream = (STREAM), \
+ (INFO).buffer = NULL, \
+ (INFO).buffer_vma = 0, \
+ (INFO).buffer_length = 0, \
+ (INFO).read_memory_func = buffer_read_memory, \
+ (INFO).memory_error_func = perror_memory, \
+ (INFO).print_address_func = generic_print_address, \
+ (INFO).insn_info_valid = 0
+
+
+
+
+/* This block of definitions is for calling the instruction decoders
+ from GDB. */
+
+/* GDB--Like target_read_memory, but slightly different parameters. */
+extern int
+dis_asm_read_memory PARAMS ((bfd_vma memaddr, bfd_byte *myaddr, int len,
+ disassemble_info *info));
+
+/* GDB--Like memory_error with slightly different parameters. */
+extern void
+dis_asm_memory_error
+ PARAMS ((int status, bfd_vma memaddr, disassemble_info *info));
+
+/* GDB--Like print_address with slightly different parameters. */
+extern void
+dis_asm_print_address PARAMS ((bfd_vma addr, disassemble_info *info));
+
+#define GDB_INIT_DISASSEMBLE_INFO(INFO, STREAM) \
+ (INFO).fprintf_func = (fprintf_ftype)fprintf_filtered, \
+ (INFO).stream = (STREAM), \
+ (INFO).read_memory_func = dis_asm_read_memory, \
+ (INFO).memory_error_func = dis_asm_memory_error, \
+ (INFO).print_address_func = dis_asm_print_address, \
+ (INFO).insn_info_valid = 0
diff --git a/gnu/usr.bin/gdb/gdb/dis-buf.c b/gnu/usr.bin/gdb/gdb/dis-buf.c
new file mode 100644
index 0000000..d07da6f
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/dis-buf.c
@@ -0,0 +1,69 @@
+/* Disassemble from a buffer, for GNU.
+ Copyright (C) 1993 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "dis-asm.h"
+#include <errno.h>
+
+/* Get LENGTH bytes from info's buffer, at target address memaddr.
+ Transfer them to myaddr. */
+int
+buffer_read_memory (memaddr, myaddr, length, info)
+ bfd_vma memaddr;
+ bfd_byte *myaddr;
+ int length;
+ struct disassemble_info *info;
+{
+ if (memaddr < info->buffer_vma
+ || memaddr + length > info->buffer_vma + info->buffer_length)
+ /* Out of bounds. Use EIO because GDB uses it. */
+ return EIO;
+ memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length);
+ return 0;
+}
+
+/* Print an error message. We can assume that this is in response to
+ an error return from buffer_read_memory. */
+void
+perror_memory (status, memaddr, info)
+ int status;
+ bfd_vma memaddr;
+ struct disassemble_info *info;
+{
+ if (status != EIO)
+ /* Can't happen. */
+ (*info->fprintf_func) (info->stream, "Unknown error %d\n", status);
+ else
+ /* Actually, address between memaddr and memaddr + len was
+ out of bounds. */
+ (*info->fprintf_func) (info->stream,
+ "Address 0x%x is out of bounds.\n", memaddr);
+}
+
+/* This could be in a separate file, to save miniscule amounts of space
+ in statically linked executables. */
+
+/* Just print the address is hex. This is included for completeness even
+ though both GDB and objdump provide their own (to print symbolic
+ addresses). */
+
+void
+generic_print_address (addr, info)
+ bfd_vma addr;
+ struct disassemble_info *info;
+{
+ (*info->fprintf_func) (info->stream, "0x%x", addr);
+}
diff --git a/gnu/usr.bin/gdb/gdb/dwarfread.c b/gnu/usr.bin/gdb/gdb/dwarfread.c
new file mode 100644
index 0000000..5d19bf8
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/dwarfread.c
@@ -0,0 +1,3866 @@
+/* DWARF debugging format support for GDB.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+ Written by Fred Fish at Cygnus Support. Portions based on dbxread.c,
+ mipsread.c, coffread.c, and dwarfread.c from a Data General SVR4 gdb port.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+
+FIXME: Do we need to generate dependencies in partial symtabs?
+(Perhaps we don't need to).
+
+FIXME: Resolve minor differences between what information we put in the
+partial symbol table and what dbxread puts in. For example, we don't yet
+put enum constants there. And dbxread seems to invent a lot of typedefs
+we never see. Use the new printpsym command to see the partial symbol table
+contents.
+
+FIXME: Figure out a better way to tell gdb about the name of the function
+contain the user's entry point (I.E. main())
+
+FIXME: See other FIXME's and "ifdef 0" scattered throughout the code for
+other things to work on, if you get bored. :-)
+
+*/
+
+#include "defs.h"
+#include "bfd.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include <time.h> /* For time_t in libbfd.h. */
+#include <sys/types.h> /* For time_t, if not in time.h. */
+#include "libbfd.h" /* FIXME Secret Internal BFD stuff (bfd_read) */
+#include "elf/dwarf.h"
+#include "buildsym.h"
+#include "demangle.h"
+#include "expression.h" /* Needed for enum exp_opcode in language.h, sigh... */
+#include "language.h"
+#include "complaints.h"
+
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+
+#ifndef NO_SYS_FILE
+#include <sys/file.h>
+#endif
+
+/* FIXME -- convert this to SEEK_SET a la POSIX, move to config files. */
+#ifndef L_SET
+#define L_SET 0
+#endif
+
+/* Some macros to provide DIE info for complaints. */
+
+#define DIE_ID (curdie!=NULL ? curdie->die_ref : 0)
+#define DIE_NAME (curdie!=NULL && curdie->at_name!=NULL) ? curdie->at_name : ""
+
+/* Complaints that can be issued during DWARF debug info reading. */
+
+struct complaint no_bfd_get_N =
+{
+ "DIE @ 0x%x \"%s\", no bfd support for %d byte data object", 0, 0
+};
+
+struct complaint malformed_die =
+{
+ "DIE @ 0x%x \"%s\", malformed DIE, bad length (%d bytes)", 0, 0
+};
+
+struct complaint bad_die_ref =
+{
+ "DIE @ 0x%x \"%s\", reference to DIE (0x%x) outside compilation unit", 0, 0
+};
+
+struct complaint unknown_attribute_form =
+{
+ "DIE @ 0x%x \"%s\", unknown attribute form (0x%x)", 0, 0
+};
+
+struct complaint unknown_attribute_length =
+{
+ "DIE @ 0x%x \"%s\", unknown attribute length, skipped remaining attributes", 0, 0
+};
+
+struct complaint unexpected_fund_type =
+{
+ "DIE @ 0x%x \"%s\", unexpected fundamental type 0x%x", 0, 0
+};
+
+struct complaint unknown_type_modifier =
+{
+ "DIE @ 0x%x \"%s\", unknown type modifier %u", 0, 0
+};
+
+struct complaint volatile_ignored =
+{
+ "DIE @ 0x%x \"%s\", type modifier 'volatile' ignored", 0, 0
+};
+
+struct complaint const_ignored =
+{
+ "DIE @ 0x%x \"%s\", type modifier 'const' ignored", 0, 0
+};
+
+struct complaint botched_modified_type =
+{
+ "DIE @ 0x%x \"%s\", botched modified type decoding (mtype 0x%x)", 0, 0
+};
+
+struct complaint op_deref2 =
+{
+ "DIE @ 0x%x \"%s\", OP_DEREF2 address 0x%x not handled", 0, 0
+};
+
+struct complaint op_deref4 =
+{
+ "DIE @ 0x%x \"%s\", OP_DEREF4 address 0x%x not handled", 0, 0
+};
+
+struct complaint basereg_not_handled =
+{
+ "DIE @ 0x%x \"%s\", BASEREG %d not handled", 0, 0
+};
+
+struct complaint dup_user_type_allocation =
+{
+ "DIE @ 0x%x \"%s\", internal error: duplicate user type allocation", 0, 0
+};
+
+struct complaint dup_user_type_definition =
+{
+ "DIE @ 0x%x \"%s\", internal error: duplicate user type definition", 0, 0
+};
+
+struct complaint missing_tag =
+{
+ "DIE @ 0x%x \"%s\", missing class, structure, or union tag", 0, 0
+};
+
+struct complaint bad_array_element_type =
+{
+ "DIE @ 0x%x \"%s\", bad array element type attribute 0x%x", 0, 0
+};
+
+struct complaint subscript_data_items =
+{
+ "DIE @ 0x%x \"%s\", can't decode subscript data items", 0, 0
+};
+
+struct complaint unhandled_array_subscript_format =
+{
+ "DIE @ 0x%x \"%s\", array subscript format 0x%x not handled yet", 0, 0
+};
+
+struct complaint unknown_array_subscript_format =
+{
+ "DIE @ 0x%x \"%s\", unknown array subscript format %x", 0, 0
+};
+
+struct complaint not_row_major =
+{
+ "DIE @ 0x%x \"%s\", array not row major; not handled correctly", 0, 0
+};
+
+typedef unsigned int DIE_REF; /* Reference to a DIE */
+
+#ifndef GCC_PRODUCER
+#define GCC_PRODUCER "GNU C "
+#endif
+
+#ifndef GPLUS_PRODUCER
+#define GPLUS_PRODUCER "GNU C++ "
+#endif
+
+#ifndef LCC_PRODUCER
+#define LCC_PRODUCER "NCR C/C++"
+#endif
+
+#ifndef CHILL_PRODUCER
+#define CHILL_PRODUCER "GNU Chill "
+#endif
+
+/* Flags to target_to_host() that tell whether or not the data object is
+ expected to be signed. Used, for example, when fetching a signed
+ integer in the target environment which is used as a signed integer
+ in the host environment, and the two environments have different sized
+ ints. In this case, *somebody* has to sign extend the smaller sized
+ int. */
+
+#define GET_UNSIGNED 0 /* No sign extension required */
+#define GET_SIGNED 1 /* Sign extension required */
+
+/* Defines for things which are specified in the document "DWARF Debugging
+ Information Format" published by UNIX International, Programming Languages
+ SIG. These defines are based on revision 1.0.0, Jan 20, 1992. */
+
+#define SIZEOF_DIE_LENGTH 4
+#define SIZEOF_DIE_TAG 2
+#define SIZEOF_ATTRIBUTE 2
+#define SIZEOF_FORMAT_SPECIFIER 1
+#define SIZEOF_FMT_FT 2
+#define SIZEOF_LINETBL_LENGTH 4
+#define SIZEOF_LINETBL_LINENO 4
+#define SIZEOF_LINETBL_STMT 2
+#define SIZEOF_LINETBL_DELTA 4
+#define SIZEOF_LOC_ATOM_CODE 1
+
+#define FORM_FROM_ATTR(attr) ((attr) & 0xF) /* Implicitly specified */
+
+/* Macros that return the sizes of various types of data in the target
+ environment.
+
+ FIXME: Currently these are just compile time constants (as they are in
+ other parts of gdb as well). They need to be able to get the right size
+ either from the bfd or possibly from the DWARF info. It would be nice if
+ the DWARF producer inserted DIES that describe the fundamental types in
+ the target environment into the DWARF info, similar to the way dbx stabs
+ producers produce information about their fundamental types. */
+
+#define TARGET_FT_POINTER_SIZE(objfile) (TARGET_PTR_BIT / TARGET_CHAR_BIT)
+#define TARGET_FT_LONG_SIZE(objfile) (TARGET_LONG_BIT / TARGET_CHAR_BIT)
+
+/* The Amiga SVR4 header file <dwarf.h> defines AT_element_list as a
+ FORM_BLOCK2, and this is the value emitted by the AT&T compiler.
+ However, the Issue 2 DWARF specification from AT&T defines it as
+ a FORM_BLOCK4, as does the latest specification from UI/PLSIG.
+ For backwards compatibility with the AT&T compiler produced executables
+ we define AT_short_element_list for this variant. */
+
+#define AT_short_element_list (0x00f0|FORM_BLOCK2)
+
+/* External variables referenced. */
+
+extern int info_verbose; /* From main.c; nonzero => verbose */
+extern char *warning_pre_print; /* From utils.c */
+
+/* The DWARF debugging information consists of two major pieces,
+ one is a block of DWARF Information Entries (DIE's) and the other
+ is a line number table. The "struct dieinfo" structure contains
+ the information for a single DIE, the one currently being processed.
+
+ In order to make it easier to randomly access the attribute fields
+ of the current DIE, which are specifically unordered within the DIE,
+ each DIE is scanned and an instance of the "struct dieinfo"
+ structure is initialized.
+
+ Initialization is done in two levels. The first, done by basicdieinfo(),
+ just initializes those fields that are vital to deciding whether or not
+ to use this DIE, how to skip past it, etc. The second, done by the
+ function completedieinfo(), fills in the rest of the information.
+
+ Attributes which have block forms are not interpreted at the time
+ the DIE is scanned, instead we just save pointers to the start
+ of their value fields.
+
+ Some fields have a flag <name>_p that is set when the value of the
+ field is valid (I.E. we found a matching attribute in the DIE). Since
+ we may want to test for the presence of some attributes in the DIE,
+ such as AT_low_pc, without restricting the values of the field,
+ we need someway to note that we found such an attribute.
+
+ */
+
+typedef char BLOCK;
+
+struct dieinfo {
+ char * die; /* Pointer to the raw DIE data */
+ unsigned long die_length; /* Length of the raw DIE data */
+ DIE_REF die_ref; /* Offset of this DIE */
+ unsigned short die_tag; /* Tag for this DIE */
+ unsigned long at_padding;
+ unsigned long at_sibling;
+ BLOCK * at_location;
+ char * at_name;
+ unsigned short at_fund_type;
+ BLOCK * at_mod_fund_type;
+ unsigned long at_user_def_type;
+ BLOCK * at_mod_u_d_type;
+ unsigned short at_ordering;
+ BLOCK * at_subscr_data;
+ unsigned long at_byte_size;
+ unsigned short at_bit_offset;
+ unsigned long at_bit_size;
+ BLOCK * at_element_list;
+ unsigned long at_stmt_list;
+ unsigned long at_low_pc;
+ unsigned long at_high_pc;
+ unsigned long at_language;
+ unsigned long at_member;
+ unsigned long at_discr;
+ BLOCK * at_discr_value;
+ BLOCK * at_string_length;
+ char * at_comp_dir;
+ char * at_producer;
+ unsigned long at_start_scope;
+ unsigned long at_stride_size;
+ unsigned long at_src_info;
+ char * at_prototyped;
+ unsigned int has_at_low_pc:1;
+ unsigned int has_at_stmt_list:1;
+ unsigned int has_at_byte_size:1;
+ unsigned int short_element_list:1;
+};
+
+static int diecount; /* Approximate count of dies for compilation unit */
+static struct dieinfo *curdie; /* For warnings and such */
+
+static char *dbbase; /* Base pointer to dwarf info */
+static int dbsize; /* Size of dwarf info in bytes */
+static int dbroff; /* Relative offset from start of .debug section */
+static char *lnbase; /* Base pointer to line section */
+static int isreg; /* Kludge to identify register variables */
+/* Kludge to identify basereg references. Nonzero if we have an offset
+ relative to a basereg. */
+static int offreg;
+/* Which base register is it relative to? */
+static int basereg;
+
+/* This value is added to each symbol value. FIXME: Generalize to
+ the section_offsets structure used by dbxread (once this is done,
+ pass the appropriate section number to end_symtab). */
+static CORE_ADDR baseaddr; /* Add to each symbol value */
+
+/* The section offsets used in the current psymtab or symtab. FIXME,
+ only used to pass one value (baseaddr) at the moment. */
+static struct section_offsets *base_section_offsets;
+
+/* Each partial symbol table entry contains a pointer to private data for the
+ read_symtab() function to use when expanding a partial symbol table entry
+ to a full symbol table entry. For DWARF debugging info, this data is
+ contained in the following structure and macros are provided for easy
+ access to the members given a pointer to a partial symbol table entry.
+
+ dbfoff Always the absolute file offset to the start of the ".debug"
+ section for the file containing the DIE's being accessed.
+
+ dbroff Relative offset from the start of the ".debug" access to the
+ first DIE to be accessed. When building the partial symbol
+ table, this value will be zero since we are accessing the
+ entire ".debug" section. When expanding a partial symbol
+ table entry, this value will be the offset to the first
+ DIE for the compilation unit containing the symbol that
+ triggers the expansion.
+
+ dblength The size of the chunk of DIE's being examined, in bytes.
+
+ lnfoff The absolute file offset to the line table fragment. Ignored
+ when building partial symbol tables, but used when expanding
+ them, and contains the absolute file offset to the fragment
+ of the ".line" section containing the line numbers for the
+ current compilation unit.
+ */
+
+struct dwfinfo {
+ file_ptr dbfoff; /* Absolute file offset to start of .debug section */
+ int dbroff; /* Relative offset from start of .debug section */
+ int dblength; /* Size of the chunk of DIE's being examined */
+ file_ptr lnfoff; /* Absolute file offset to line table fragment */
+};
+
+#define DBFOFF(p) (((struct dwfinfo *)((p)->read_symtab_private))->dbfoff)
+#define DBROFF(p) (((struct dwfinfo *)((p)->read_symtab_private))->dbroff)
+#define DBLENGTH(p) (((struct dwfinfo *)((p)->read_symtab_private))->dblength)
+#define LNFOFF(p) (((struct dwfinfo *)((p)->read_symtab_private))->lnfoff)
+
+/* The generic symbol table building routines have separate lists for
+ file scope symbols and all all other scopes (local scopes). So
+ we need to select the right one to pass to add_symbol_to_list().
+ We do it by keeping a pointer to the correct list in list_in_scope.
+
+ FIXME: The original dwarf code just treated the file scope as the first
+ local scope, and all other local scopes as nested local scopes, and worked
+ fine. Check to see if we really need to distinguish these in buildsym.c */
+
+struct pending **list_in_scope = &file_symbols;
+
+/* DIES which have user defined types or modified user defined types refer to
+ other DIES for the type information. Thus we need to associate the offset
+ of a DIE for a user defined type with a pointer to the type information.
+
+ Originally this was done using a simple but expensive algorithm, with an
+ array of unsorted structures, each containing an offset/type-pointer pair.
+ This array was scanned linearly each time a lookup was done. The result
+ was that gdb was spending over half it's startup time munging through this
+ array of pointers looking for a structure that had the right offset member.
+
+ The second attempt used the same array of structures, but the array was
+ sorted using qsort each time a new offset/type was recorded, and a binary
+ search was used to find the type pointer for a given DIE offset. This was
+ even slower, due to the overhead of sorting the array each time a new
+ offset/type pair was entered.
+
+ The third attempt uses a fixed size array of type pointers, indexed by a
+ value derived from the DIE offset. Since the minimum DIE size is 4 bytes,
+ we can divide any DIE offset by 4 to obtain a unique index into this fixed
+ size array. Since each element is a 4 byte pointer, it takes exactly as
+ much memory to hold this array as to hold the DWARF info for a given
+ compilation unit. But it gets freed as soon as we are done with it.
+ This has worked well in practice, as a reasonable tradeoff between memory
+ consumption and speed, without having to resort to much more complicated
+ algorithms. */
+
+static struct type **utypes; /* Pointer to array of user type pointers */
+static int numutypes; /* Max number of user type pointers */
+
+/* Maintain an array of referenced fundamental types for the current
+ compilation unit being read. For DWARF version 1, we have to construct
+ the fundamental types on the fly, since no information about the
+ fundamental types is supplied. Each such fundamental type is created by
+ calling a language dependent routine to create the type, and then a
+ pointer to that type is then placed in the array at the index specified
+ by it's FT_<TYPENAME> value. The array has a fixed size set by the
+ FT_NUM_MEMBERS compile time constant, which is the number of predefined
+ fundamental types gdb knows how to construct. */
+
+static struct type *ftypes[FT_NUM_MEMBERS]; /* Fundamental types */
+
+/* Record the language for the compilation unit which is currently being
+ processed. We know it once we have seen the TAG_compile_unit DIE,
+ and we need it while processing the DIE's for that compilation unit.
+ It is eventually saved in the symtab structure, but we don't finalize
+ the symtab struct until we have processed all the DIE's for the
+ compilation unit. We also need to get and save a pointer to the
+ language struct for this language, so we can call the language
+ dependent routines for doing things such as creating fundamental
+ types. */
+
+static enum language cu_language;
+static const struct language_defn *cu_language_defn;
+
+/* Forward declarations of static functions so we don't have to worry
+ about ordering within this file. */
+
+static int
+attribute_size PARAMS ((unsigned int));
+
+static unsigned long
+target_to_host PARAMS ((char *, int, int, struct objfile *));
+
+static void
+add_enum_psymbol PARAMS ((struct dieinfo *, struct objfile *));
+
+static void
+handle_producer PARAMS ((char *));
+
+static void
+read_file_scope PARAMS ((struct dieinfo *, char *, char *, struct objfile *));
+
+static void
+read_func_scope PARAMS ((struct dieinfo *, char *, char *, struct objfile *));
+
+static void
+read_lexical_block_scope PARAMS ((struct dieinfo *, char *, char *,
+ struct objfile *));
+
+static void
+scan_partial_symbols PARAMS ((char *, char *, struct objfile *));
+
+static void
+scan_compilation_units PARAMS ((char *, char *, file_ptr,
+ file_ptr, struct objfile *));
+
+static void
+add_partial_symbol PARAMS ((struct dieinfo *, struct objfile *));
+
+static void
+init_psymbol_list PARAMS ((struct objfile *, int));
+
+static void
+basicdieinfo PARAMS ((struct dieinfo *, char *, struct objfile *));
+
+static void
+completedieinfo PARAMS ((struct dieinfo *, struct objfile *));
+
+static void
+dwarf_psymtab_to_symtab PARAMS ((struct partial_symtab *));
+
+static void
+psymtab_to_symtab_1 PARAMS ((struct partial_symtab *));
+
+static void
+read_ofile_symtab PARAMS ((struct partial_symtab *));
+
+static void
+process_dies PARAMS ((char *, char *, struct objfile *));
+
+static void
+read_structure_scope PARAMS ((struct dieinfo *, char *, char *,
+ struct objfile *));
+
+static struct type *
+decode_array_element_type PARAMS ((char *));
+
+static struct type *
+decode_subscript_data_item PARAMS ((char *, char *));
+
+static void
+dwarf_read_array_type PARAMS ((struct dieinfo *));
+
+static void
+read_tag_pointer_type PARAMS ((struct dieinfo *dip));
+
+static void
+read_tag_string_type PARAMS ((struct dieinfo *dip));
+
+static void
+read_subroutine_type PARAMS ((struct dieinfo *, char *, char *));
+
+static void
+read_enumeration PARAMS ((struct dieinfo *, char *, char *, struct objfile *));
+
+static struct type *
+struct_type PARAMS ((struct dieinfo *, char *, char *, struct objfile *));
+
+static struct type *
+enum_type PARAMS ((struct dieinfo *, struct objfile *));
+
+static void
+decode_line_numbers PARAMS ((char *));
+
+static struct type *
+decode_die_type PARAMS ((struct dieinfo *));
+
+static struct type *
+decode_mod_fund_type PARAMS ((char *));
+
+static struct type *
+decode_mod_u_d_type PARAMS ((char *));
+
+static struct type *
+decode_modified_type PARAMS ((char *, unsigned int, int));
+
+static struct type *
+decode_fund_type PARAMS ((unsigned int));
+
+static char *
+create_name PARAMS ((char *, struct obstack *));
+
+static struct type *
+lookup_utype PARAMS ((DIE_REF));
+
+static struct type *
+alloc_utype PARAMS ((DIE_REF, struct type *));
+
+static struct symbol *
+new_symbol PARAMS ((struct dieinfo *, struct objfile *));
+
+static void
+synthesize_typedef PARAMS ((struct dieinfo *, struct objfile *,
+ struct type *));
+
+static int
+locval PARAMS ((char *));
+
+static void
+set_cu_language PARAMS ((struct dieinfo *));
+
+static struct type *
+dwarf_fundamental_type PARAMS ((struct objfile *, int));
+
+
+/*
+
+LOCAL FUNCTION
+
+ dwarf_fundamental_type -- lookup or create a fundamental type
+
+SYNOPSIS
+
+ struct type *
+ dwarf_fundamental_type (struct objfile *objfile, int typeid)
+
+DESCRIPTION
+
+ DWARF version 1 doesn't supply any fundamental type information,
+ so gdb has to construct such types. It has a fixed number of
+ fundamental types that it knows how to construct, which is the
+ union of all types that it knows how to construct for all languages
+ that it knows about. These are enumerated in gdbtypes.h.
+
+ As an example, assume we find a DIE that references a DWARF
+ fundamental type of FT_integer. We first look in the ftypes
+ array to see if we already have such a type, indexed by the
+ gdb internal value of FT_INTEGER. If so, we simply return a
+ pointer to that type. If not, then we ask an appropriate
+ language dependent routine to create a type FT_INTEGER, using
+ defaults reasonable for the current target machine, and install
+ that type in ftypes for future reference.
+
+RETURNS
+
+ Pointer to a fundamental type.
+
+*/
+
+static struct type *
+dwarf_fundamental_type (objfile, typeid)
+ struct objfile *objfile;
+ int typeid;
+{
+ if (typeid < 0 || typeid >= FT_NUM_MEMBERS)
+ {
+ error ("internal error - invalid fundamental type id %d", typeid);
+ }
+
+ /* Look for this particular type in the fundamental type vector. If one is
+ not found, create and install one appropriate for the current language
+ and the current target machine. */
+
+ if (ftypes[typeid] == NULL)
+ {
+ ftypes[typeid] = cu_language_defn -> la_fund_type(objfile, typeid);
+ }
+
+ return (ftypes[typeid]);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ set_cu_language -- set local copy of language for compilation unit
+
+SYNOPSIS
+
+ void
+ set_cu_language (struct dieinfo *dip)
+
+DESCRIPTION
+
+ Decode the language attribute for a compilation unit DIE and
+ remember what the language was. We use this at various times
+ when processing DIE's for a given compilation unit.
+
+RETURNS
+
+ No return value.
+
+ */
+
+static void
+set_cu_language (dip)
+ struct dieinfo *dip;
+{
+ switch (dip -> at_language)
+ {
+ case LANG_C89:
+ case LANG_C:
+ cu_language = language_c;
+ break;
+ case LANG_C_PLUS_PLUS:
+ cu_language = language_cplus;
+ break;
+ case LANG_CHILL:
+ cu_language = language_chill;
+ break;
+ case LANG_MODULA2:
+ cu_language = language_m2;
+ break;
+ case LANG_ADA83:
+ case LANG_COBOL74:
+ case LANG_COBOL85:
+ case LANG_FORTRAN77:
+ case LANG_FORTRAN90:
+ case LANG_PASCAL83:
+ /* We don't know anything special about these yet. */
+ cu_language = language_unknown;
+ break;
+ default:
+ /* If no at_language, try to deduce one from the filename */
+ cu_language = deduce_language_from_filename (dip -> at_name);
+ break;
+ }
+ cu_language_defn = language_def (cu_language);
+}
+
+/*
+
+GLOBAL FUNCTION
+
+ dwarf_build_psymtabs -- build partial symtabs from DWARF debug info
+
+SYNOPSIS
+
+ void dwarf_build_psymtabs (struct objfile *objfile,
+ struct section_offsets *section_offsets,
+ int mainline, file_ptr dbfoff, unsigned int dbfsize,
+ file_ptr lnoffset, unsigned int lnsize)
+
+DESCRIPTION
+
+ This function is called upon to build partial symtabs from files
+ containing DIE's (Dwarf Information Entries) and DWARF line numbers.
+
+ It is passed a bfd* containing the DIES
+ and line number information, the corresponding filename for that
+ file, a base address for relocating the symbols, a flag indicating
+ whether or not this debugging information is from a "main symbol
+ table" rather than a shared library or dynamically linked file,
+ and file offset/size pairs for the DIE information and line number
+ information.
+
+RETURNS
+
+ No return value.
+
+ */
+
+void
+dwarf_build_psymtabs (objfile, section_offsets, mainline, dbfoff, dbfsize,
+ lnoffset, lnsize)
+ struct objfile *objfile;
+ struct section_offsets *section_offsets;
+ int mainline;
+ file_ptr dbfoff;
+ unsigned int dbfsize;
+ file_ptr lnoffset;
+ unsigned int lnsize;
+{
+ bfd *abfd = objfile->obfd;
+ struct cleanup *back_to;
+
+ current_objfile = objfile;
+ dbsize = dbfsize;
+ dbbase = xmalloc (dbsize);
+ dbroff = 0;
+ if ((bfd_seek (abfd, dbfoff, L_SET) != 0) ||
+ (bfd_read (dbbase, dbsize, 1, abfd) != dbsize))
+ {
+ free (dbbase);
+ error ("can't read DWARF data from '%s'", bfd_get_filename (abfd));
+ }
+ back_to = make_cleanup (free, dbbase);
+
+ /* If we are reinitializing, or if we have never loaded syms yet, init.
+ Since we have no idea how many DIES we are looking at, we just guess
+ some arbitrary value. */
+
+ if (mainline || objfile -> global_psymbols.size == 0 ||
+ objfile -> static_psymbols.size == 0)
+ {
+ init_psymbol_list (objfile, 1024);
+ }
+
+ /* Save the relocation factor where everybody can see it. */
+
+ base_section_offsets = section_offsets;
+ baseaddr = ANOFFSET (section_offsets, 0);
+
+ /* Follow the compilation unit sibling chain, building a partial symbol
+ table entry for each one. Save enough information about each compilation
+ unit to locate the full DWARF information later. */
+
+ scan_compilation_units (dbbase, dbbase + dbsize, dbfoff, lnoffset, objfile);
+
+ do_cleanups (back_to);
+ current_objfile = NULL;
+}
+
+/*
+
+LOCAL FUNCTION
+
+ read_lexical_block_scope -- process all dies in a lexical block
+
+SYNOPSIS
+
+ static void read_lexical_block_scope (struct dieinfo *dip,
+ char *thisdie, char *enddie)
+
+DESCRIPTION
+
+ Process all the DIES contained within a lexical block scope.
+ Start a new scope, process the dies, and then close the scope.
+
+ */
+
+static void
+read_lexical_block_scope (dip, thisdie, enddie, objfile)
+ struct dieinfo *dip;
+ char *thisdie;
+ char *enddie;
+ struct objfile *objfile;
+{
+ register struct context_stack *new;
+
+ push_context (0, dip -> at_low_pc);
+ process_dies (thisdie + dip -> die_length, enddie, objfile);
+ new = pop_context ();
+ if (local_symbols != NULL)
+ {
+ finish_block (0, &local_symbols, new -> old_blocks, new -> start_addr,
+ dip -> at_high_pc, objfile);
+ }
+ local_symbols = new -> locals;
+}
+
+/*
+
+LOCAL FUNCTION
+
+ lookup_utype -- look up a user defined type from die reference
+
+SYNOPSIS
+
+ static type *lookup_utype (DIE_REF die_ref)
+
+DESCRIPTION
+
+ Given a DIE reference, lookup the user defined type associated with
+ that DIE, if it has been registered already. If not registered, then
+ return NULL. Alloc_utype() can be called to register an empty
+ type for this reference, which will be filled in later when the
+ actual referenced DIE is processed.
+ */
+
+static struct type *
+lookup_utype (die_ref)
+ DIE_REF die_ref;
+{
+ struct type *type = NULL;
+ int utypeidx;
+
+ utypeidx = (die_ref - dbroff) / 4;
+ if ((utypeidx < 0) || (utypeidx >= numutypes))
+ {
+ complain (&bad_die_ref, DIE_ID, DIE_NAME);
+ }
+ else
+ {
+ type = *(utypes + utypeidx);
+ }
+ return (type);
+}
+
+
+/*
+
+LOCAL FUNCTION
+
+ alloc_utype -- add a user defined type for die reference
+
+SYNOPSIS
+
+ static type *alloc_utype (DIE_REF die_ref, struct type *utypep)
+
+DESCRIPTION
+
+ Given a die reference DIE_REF, and a possible pointer to a user
+ defined type UTYPEP, register that this reference has a user
+ defined type and either use the specified type in UTYPEP or
+ make a new empty type that will be filled in later.
+
+ We should only be called after calling lookup_utype() to verify that
+ there is not currently a type registered for DIE_REF.
+ */
+
+static struct type *
+alloc_utype (die_ref, utypep)
+ DIE_REF die_ref;
+ struct type *utypep;
+{
+ struct type **typep;
+ int utypeidx;
+
+ utypeidx = (die_ref - dbroff) / 4;
+ typep = utypes + utypeidx;
+ if ((utypeidx < 0) || (utypeidx >= numutypes))
+ {
+ utypep = dwarf_fundamental_type (current_objfile, FT_INTEGER);
+ complain (&bad_die_ref, DIE_ID, DIE_NAME);
+ }
+ else if (*typep != NULL)
+ {
+ utypep = *typep;
+ complain (&dup_user_type_allocation, DIE_ID, DIE_NAME);
+ }
+ else
+ {
+ if (utypep == NULL)
+ {
+ utypep = alloc_type (current_objfile);
+ }
+ *typep = utypep;
+ }
+ return (utypep);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ decode_die_type -- return a type for a specified die
+
+SYNOPSIS
+
+ static struct type *decode_die_type (struct dieinfo *dip)
+
+DESCRIPTION
+
+ Given a pointer to a die information structure DIP, decode the
+ type of the die and return a pointer to the decoded type. All
+ dies without specific types default to type int.
+ */
+
+static struct type *
+decode_die_type (dip)
+ struct dieinfo *dip;
+{
+ struct type *type = NULL;
+
+ if (dip -> at_fund_type != 0)
+ {
+ type = decode_fund_type (dip -> at_fund_type);
+ }
+ else if (dip -> at_mod_fund_type != NULL)
+ {
+ type = decode_mod_fund_type (dip -> at_mod_fund_type);
+ }
+ else if (dip -> at_user_def_type)
+ {
+ if ((type = lookup_utype (dip -> at_user_def_type)) == NULL)
+ {
+ type = alloc_utype (dip -> at_user_def_type, NULL);
+ }
+ }
+ else if (dip -> at_mod_u_d_type)
+ {
+ type = decode_mod_u_d_type (dip -> at_mod_u_d_type);
+ }
+ else
+ {
+ type = dwarf_fundamental_type (current_objfile, FT_INTEGER);
+ }
+ return (type);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ struct_type -- compute and return the type for a struct or union
+
+SYNOPSIS
+
+ static struct type *struct_type (struct dieinfo *dip, char *thisdie,
+ char *enddie, struct objfile *objfile)
+
+DESCRIPTION
+
+ Given pointer to a die information structure for a die which
+ defines a union or structure (and MUST define one or the other),
+ and pointers to the raw die data that define the range of dies which
+ define the members, compute and return the user defined type for the
+ structure or union.
+ */
+
+static struct type *
+struct_type (dip, thisdie, enddie, objfile)
+ struct dieinfo *dip;
+ char *thisdie;
+ char *enddie;
+ struct objfile *objfile;
+{
+ struct type *type;
+ struct nextfield {
+ struct nextfield *next;
+ struct field field;
+ };
+ struct nextfield *list = NULL;
+ struct nextfield *new;
+ int nfields = 0;
+ int n;
+ struct dieinfo mbr;
+ char *nextdie;
+#if !BITS_BIG_ENDIAN
+ int anonymous_size;
+#endif
+
+ if ((type = lookup_utype (dip -> die_ref)) == NULL)
+ {
+ /* No forward references created an empty type, so install one now */
+ type = alloc_utype (dip -> die_ref, NULL);
+ }
+ INIT_CPLUS_SPECIFIC(type);
+ switch (dip -> die_tag)
+ {
+ case TAG_class_type:
+ TYPE_CODE (type) = TYPE_CODE_CLASS;
+ break;
+ case TAG_structure_type:
+ TYPE_CODE (type) = TYPE_CODE_STRUCT;
+ break;
+ case TAG_union_type:
+ TYPE_CODE (type) = TYPE_CODE_UNION;
+ break;
+ default:
+ /* Should never happen */
+ TYPE_CODE (type) = TYPE_CODE_UNDEF;
+ complain (&missing_tag, DIE_ID, DIE_NAME);
+ break;
+ }
+ /* Some compilers try to be helpful by inventing "fake" names for
+ anonymous enums, structures, and unions, like "~0fake" or ".0fake".
+ Thanks, but no thanks... */
+ if (dip -> at_name != NULL
+ && *dip -> at_name != '~'
+ && *dip -> at_name != '.')
+ {
+ TYPE_TAG_NAME (type) = obconcat (&objfile -> type_obstack,
+ "", "", dip -> at_name);
+ }
+ /* Use whatever size is known. Zero is a valid size. We might however
+ wish to check has_at_byte_size to make sure that some byte size was
+ given explicitly, but DWARF doesn't specify that explicit sizes of
+ zero have to present, so complaining about missing sizes should
+ probably not be the default. */
+ TYPE_LENGTH (type) = dip -> at_byte_size;
+ thisdie += dip -> die_length;
+ while (thisdie < enddie)
+ {
+ basicdieinfo (&mbr, thisdie, objfile);
+ completedieinfo (&mbr, objfile);
+ if (mbr.die_length <= SIZEOF_DIE_LENGTH)
+ {
+ break;
+ }
+ else if (mbr.at_sibling != 0)
+ {
+ nextdie = dbbase + mbr.at_sibling - dbroff;
+ }
+ else
+ {
+ nextdie = thisdie + mbr.die_length;
+ }
+ switch (mbr.die_tag)
+ {
+ case TAG_member:
+ /* Get space to record the next field's data. */
+ new = (struct nextfield *) alloca (sizeof (struct nextfield));
+ new -> next = list;
+ list = new;
+ /* Save the data. */
+ list -> field.name =
+ obsavestring (mbr.at_name, strlen (mbr.at_name),
+ &objfile -> type_obstack);
+ list -> field.type = decode_die_type (&mbr);
+ list -> field.bitpos = 8 * locval (mbr.at_location);
+ /* Handle bit fields. */
+ list -> field.bitsize = mbr.at_bit_size;
+#if BITS_BIG_ENDIAN
+ /* For big endian bits, the at_bit_offset gives the additional
+ bit offset from the MSB of the containing anonymous object to
+ the MSB of the field. We don't have to do anything special
+ since we don't need to know the size of the anonymous object. */
+ list -> field.bitpos += mbr.at_bit_offset;
+#else
+ /* For little endian bits, we need to have a non-zero at_bit_size,
+ so that we know we are in fact dealing with a bitfield. Compute
+ the bit offset to the MSB of the anonymous object, subtract off
+ the number of bits from the MSB of the field to the MSB of the
+ object, and then subtract off the number of bits of the field
+ itself. The result is the bit offset of the LSB of the field. */
+ if (mbr.at_bit_size > 0)
+ {
+ if (mbr.has_at_byte_size)
+ {
+ /* The size of the anonymous object containing the bit field
+ is explicit, so use the indicated size (in bytes). */
+ anonymous_size = mbr.at_byte_size;
+ }
+ else
+ {
+ /* The size of the anonymous object containing the bit field
+ matches the size of an object of the bit field's type.
+ DWARF allows at_byte_size to be left out in such cases,
+ as a debug information size optimization. */
+ anonymous_size = TYPE_LENGTH (list -> field.type);
+ }
+ list -> field.bitpos +=
+ anonymous_size * 8 - mbr.at_bit_offset - mbr.at_bit_size;
+ }
+#endif
+ nfields++;
+ break;
+ default:
+ process_dies (thisdie, nextdie, objfile);
+ break;
+ }
+ thisdie = nextdie;
+ }
+ /* Now create the vector of fields, and record how big it is. We may
+ not even have any fields, if this DIE was generated due to a reference
+ to an anonymous structure or union. In this case, TYPE_FLAG_STUB is
+ set, which clues gdb in to the fact that it needs to search elsewhere
+ for the full structure definition. */
+ if (nfields == 0)
+ {
+ TYPE_FLAGS (type) |= TYPE_FLAG_STUB;
+ }
+ else
+ {
+ TYPE_NFIELDS (type) = nfields;
+ TYPE_FIELDS (type) = (struct field *)
+ TYPE_ALLOC (type, sizeof (struct field) * nfields);
+ /* Copy the saved-up fields into the field vector. */
+ for (n = nfields; list; list = list -> next)
+ {
+ TYPE_FIELD (type, --n) = list -> field;
+ }
+ }
+ return (type);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ read_structure_scope -- process all dies within struct or union
+
+SYNOPSIS
+
+ static void read_structure_scope (struct dieinfo *dip,
+ char *thisdie, char *enddie, struct objfile *objfile)
+
+DESCRIPTION
+
+ Called when we find the DIE that starts a structure or union
+ scope (definition) to process all dies that define the members
+ of the structure or union. DIP is a pointer to the die info
+ struct for the DIE that names the structure or union.
+
+NOTES
+
+ Note that we need to call struct_type regardless of whether or not
+ the DIE has an at_name attribute, since it might be an anonymous
+ structure or union. This gets the type entered into our set of
+ user defined types.
+
+ However, if the structure is incomplete (an opaque struct/union)
+ then suppress creating a symbol table entry for it since gdb only
+ wants to find the one with the complete definition. Note that if
+ it is complete, we just call new_symbol, which does it's own
+ checking about whether the struct/union is anonymous or not (and
+ suppresses creating a symbol table entry itself).
+
+ */
+
+static void
+read_structure_scope (dip, thisdie, enddie, objfile)
+ struct dieinfo *dip;
+ char *thisdie;
+ char *enddie;
+ struct objfile *objfile;
+{
+ struct type *type;
+ struct symbol *sym;
+
+ type = struct_type (dip, thisdie, enddie, objfile);
+ if (!(TYPE_FLAGS (type) & TYPE_FLAG_STUB))
+ {
+ sym = new_symbol (dip, objfile);
+ if (sym != NULL)
+ {
+ SYMBOL_TYPE (sym) = type;
+ if (cu_language == language_cplus)
+ {
+ synthesize_typedef (dip, objfile, type);
+ }
+ }
+ }
+}
+
+/*
+
+LOCAL FUNCTION
+
+ decode_array_element_type -- decode type of the array elements
+
+SYNOPSIS
+
+ static struct type *decode_array_element_type (char *scan, char *end)
+
+DESCRIPTION
+
+ As the last step in decoding the array subscript information for an
+ array DIE, we need to decode the type of the array elements. We are
+ passed a pointer to this last part of the subscript information and
+ must return the appropriate type. If the type attribute is not
+ recognized, just warn about the problem and return type int.
+ */
+
+static struct type *
+decode_array_element_type (scan)
+ char *scan;
+{
+ struct type *typep;
+ DIE_REF die_ref;
+ unsigned short attribute;
+ unsigned short fundtype;
+ int nbytes;
+
+ attribute = target_to_host (scan, SIZEOF_ATTRIBUTE, GET_UNSIGNED,
+ current_objfile);
+ scan += SIZEOF_ATTRIBUTE;
+ if ((nbytes = attribute_size (attribute)) == -1)
+ {
+ complain (&bad_array_element_type, DIE_ID, DIE_NAME, attribute);
+ typep = dwarf_fundamental_type (current_objfile, FT_INTEGER);
+ }
+ else
+ {
+ switch (attribute)
+ {
+ case AT_fund_type:
+ fundtype = target_to_host (scan, nbytes, GET_UNSIGNED,
+ current_objfile);
+ typep = decode_fund_type (fundtype);
+ break;
+ case AT_mod_fund_type:
+ typep = decode_mod_fund_type (scan);
+ break;
+ case AT_user_def_type:
+ die_ref = target_to_host (scan, nbytes, GET_UNSIGNED,
+ current_objfile);
+ if ((typep = lookup_utype (die_ref)) == NULL)
+ {
+ typep = alloc_utype (die_ref, NULL);
+ }
+ break;
+ case AT_mod_u_d_type:
+ typep = decode_mod_u_d_type (scan);
+ break;
+ default:
+ complain (&bad_array_element_type, DIE_ID, DIE_NAME, attribute);
+ typep = dwarf_fundamental_type (current_objfile, FT_INTEGER);
+ break;
+ }
+ }
+ return (typep);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ decode_subscript_data_item -- decode array subscript item
+
+SYNOPSIS
+
+ static struct type *
+ decode_subscript_data_item (char *scan, char *end)
+
+DESCRIPTION
+
+ The array subscripts and the data type of the elements of an
+ array are described by a list of data items, stored as a block
+ of contiguous bytes. There is a data item describing each array
+ dimension, and a final data item describing the element type.
+ The data items are ordered the same as their appearance in the
+ source (I.E. leftmost dimension first, next to leftmost second,
+ etc).
+
+ The data items describing each array dimension consist of four
+ parts: (1) a format specifier, (2) type type of the subscript
+ index, (3) a description of the low bound of the array dimension,
+ and (4) a description of the high bound of the array dimension.
+
+ The last data item is the description of the type of each of
+ the array elements.
+
+ We are passed a pointer to the start of the block of bytes
+ containing the remaining data items, and a pointer to the first
+ byte past the data. This function recursively decodes the
+ remaining data items and returns a type.
+
+ If we somehow fail to decode some data, we complain about it
+ and return a type "array of int".
+
+BUGS
+ FIXME: This code only implements the forms currently used
+ by the AT&T and GNU C compilers.
+
+ The end pointer is supplied for error checking, maybe we should
+ use it for that...
+ */
+
+static struct type *
+decode_subscript_data_item (scan, end)
+ char *scan;
+ char *end;
+{
+ struct type *typep = NULL; /* Array type we are building */
+ struct type *nexttype; /* Type of each element (may be array) */
+ struct type *indextype; /* Type of this index */
+ struct type *rangetype;
+ unsigned int format;
+ unsigned short fundtype;
+ unsigned long lowbound;
+ unsigned long highbound;
+ int nbytes;
+
+ format = target_to_host (scan, SIZEOF_FORMAT_SPECIFIER, GET_UNSIGNED,
+ current_objfile);
+ scan += SIZEOF_FORMAT_SPECIFIER;
+ switch (format)
+ {
+ case FMT_ET:
+ typep = decode_array_element_type (scan);
+ break;
+ case FMT_FT_C_C:
+ fundtype = target_to_host (scan, SIZEOF_FMT_FT, GET_UNSIGNED,
+ current_objfile);
+ indextype = decode_fund_type (fundtype);
+ scan += SIZEOF_FMT_FT;
+ nbytes = TARGET_FT_LONG_SIZE (current_objfile);
+ lowbound = target_to_host (scan, nbytes, GET_UNSIGNED, current_objfile);
+ scan += nbytes;
+ highbound = target_to_host (scan, nbytes, GET_UNSIGNED, current_objfile);
+ scan += nbytes;
+ nexttype = decode_subscript_data_item (scan, end);
+ if (nexttype == NULL)
+ {
+ /* Munged subscript data or other problem, fake it. */
+ complain (&subscript_data_items, DIE_ID, DIE_NAME);
+ nexttype = dwarf_fundamental_type (current_objfile, FT_INTEGER);
+ }
+ rangetype = create_range_type ((struct type *) NULL, indextype,
+ lowbound, highbound);
+ typep = create_array_type ((struct type *) NULL, nexttype, rangetype);
+ break;
+ case FMT_FT_C_X:
+ case FMT_FT_X_C:
+ case FMT_FT_X_X:
+ case FMT_UT_C_C:
+ case FMT_UT_C_X:
+ case FMT_UT_X_C:
+ case FMT_UT_X_X:
+ complain (&unhandled_array_subscript_format, DIE_ID, DIE_NAME, format);
+ nexttype = dwarf_fundamental_type (current_objfile, FT_INTEGER);
+ rangetype = create_range_type ((struct type *) NULL, nexttype, 0, 0);
+ typep = create_array_type ((struct type *) NULL, nexttype, rangetype);
+ break;
+ default:
+ complain (&unknown_array_subscript_format, DIE_ID, DIE_NAME, format);
+ nexttype = dwarf_fundamental_type (current_objfile, FT_INTEGER);
+ rangetype = create_range_type ((struct type *) NULL, nexttype, 0, 0);
+ typep = create_array_type ((struct type *) NULL, nexttype, rangetype);
+ break;
+ }
+ return (typep);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ dwarf_read_array_type -- read TAG_array_type DIE
+
+SYNOPSIS
+
+ static void dwarf_read_array_type (struct dieinfo *dip)
+
+DESCRIPTION
+
+ Extract all information from a TAG_array_type DIE and add to
+ the user defined type vector.
+ */
+
+static void
+dwarf_read_array_type (dip)
+ struct dieinfo *dip;
+{
+ struct type *type;
+ struct type *utype;
+ char *sub;
+ char *subend;
+ unsigned short blocksz;
+ int nbytes;
+
+ if (dip -> at_ordering != ORD_row_major)
+ {
+ /* FIXME: Can gdb even handle column major arrays? */
+ complain (&not_row_major, DIE_ID, DIE_NAME);
+ }
+ if ((sub = dip -> at_subscr_data) != NULL)
+ {
+ nbytes = attribute_size (AT_subscr_data);
+ blocksz = target_to_host (sub, nbytes, GET_UNSIGNED, current_objfile);
+ subend = sub + nbytes + blocksz;
+ sub += nbytes;
+ type = decode_subscript_data_item (sub, subend);
+ if ((utype = lookup_utype (dip -> die_ref)) == NULL)
+ {
+ /* Install user defined type that has not been referenced yet. */
+ alloc_utype (dip -> die_ref, type);
+ }
+ else if (TYPE_CODE (utype) == TYPE_CODE_UNDEF)
+ {
+ /* Ick! A forward ref has already generated a blank type in our
+ slot, and this type probably already has things pointing to it
+ (which is what caused it to be created in the first place).
+ If it's just a place holder we can plop our fully defined type
+ on top of it. We can't recover the space allocated for our
+ new type since it might be on an obstack, but we could reuse
+ it if we kept a list of them, but it might not be worth it
+ (FIXME). */
+ *utype = *type;
+ }
+ else
+ {
+ /* Double ick! Not only is a type already in our slot, but
+ someone has decorated it. Complain and leave it alone. */
+ complain (&dup_user_type_definition, DIE_ID, DIE_NAME);
+ }
+ }
+}
+
+/*
+
+LOCAL FUNCTION
+
+ read_tag_pointer_type -- read TAG_pointer_type DIE
+
+SYNOPSIS
+
+ static void read_tag_pointer_type (struct dieinfo *dip)
+
+DESCRIPTION
+
+ Extract all information from a TAG_pointer_type DIE and add to
+ the user defined type vector.
+ */
+
+static void
+read_tag_pointer_type (dip)
+ struct dieinfo *dip;
+{
+ struct type *type;
+ struct type *utype;
+
+ type = decode_die_type (dip);
+ if ((utype = lookup_utype (dip -> die_ref)) == NULL)
+ {
+ utype = lookup_pointer_type (type);
+ alloc_utype (dip -> die_ref, utype);
+ }
+ else
+ {
+ TYPE_TARGET_TYPE (utype) = type;
+ TYPE_POINTER_TYPE (type) = utype;
+
+ /* We assume the machine has only one representation for pointers! */
+ /* FIXME: This confuses host<->target data representations, and is a
+ poor assumption besides. */
+
+ TYPE_LENGTH (utype) = sizeof (char *);
+ TYPE_CODE (utype) = TYPE_CODE_PTR;
+ }
+}
+
+/*
+
+LOCAL FUNCTION
+
+ read_tag_string_type -- read TAG_string_type DIE
+
+SYNOPSIS
+
+ static void read_tag_string_type (struct dieinfo *dip)
+
+DESCRIPTION
+
+ Extract all information from a TAG_string_type DIE and add to
+ the user defined type vector. It isn't really a user defined
+ type, but it behaves like one, with other DIE's using an
+ AT_user_def_type attribute to reference it.
+ */
+
+static void
+read_tag_string_type (dip)
+ struct dieinfo *dip;
+{
+ struct type *utype;
+ struct type *indextype;
+ struct type *rangetype;
+ unsigned long lowbound = 0;
+ unsigned long highbound;
+
+ if (dip -> has_at_byte_size)
+ {
+ /* A fixed bounds string */
+ highbound = dip -> at_byte_size - 1;
+ }
+ else
+ {
+ /* A varying length string. Stub for now. (FIXME) */
+ highbound = 1;
+ }
+ indextype = dwarf_fundamental_type (current_objfile, FT_INTEGER);
+ rangetype = create_range_type ((struct type *) NULL, indextype, lowbound,
+ highbound);
+
+ utype = lookup_utype (dip -> die_ref);
+ if (utype == NULL)
+ {
+ /* No type defined, go ahead and create a blank one to use. */
+ utype = alloc_utype (dip -> die_ref, (struct type *) NULL);
+ }
+ else
+ {
+ /* Already a type in our slot due to a forward reference. Make sure it
+ is a blank one. If not, complain and leave it alone. */
+ if (TYPE_CODE (utype) != TYPE_CODE_UNDEF)
+ {
+ complain (&dup_user_type_definition, DIE_ID, DIE_NAME);
+ return;
+ }
+ }
+
+ /* Create the string type using the blank type we either found or created. */
+ utype = create_string_type (utype, rangetype);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ read_subroutine_type -- process TAG_subroutine_type dies
+
+SYNOPSIS
+
+ static void read_subroutine_type (struct dieinfo *dip, char thisdie,
+ char *enddie)
+
+DESCRIPTION
+
+ Handle DIES due to C code like:
+
+ struct foo {
+ int (*funcp)(int a, long l); (Generates TAG_subroutine_type DIE)
+ int b;
+ };
+
+NOTES
+
+ The parameter DIES are currently ignored. See if gdb has a way to
+ include this info in it's type system, and decode them if so. Is
+ this what the type structure's "arg_types" field is for? (FIXME)
+ */
+
+static void
+read_subroutine_type (dip, thisdie, enddie)
+ struct dieinfo *dip;
+ char *thisdie;
+ char *enddie;
+{
+ struct type *type; /* Type that this function returns */
+ struct type *ftype; /* Function that returns above type */
+
+ /* Decode the type that this subroutine returns */
+
+ type = decode_die_type (dip);
+
+ /* Check to see if we already have a partially constructed user
+ defined type for this DIE, from a forward reference. */
+
+ if ((ftype = lookup_utype (dip -> die_ref)) == NULL)
+ {
+ /* This is the first reference to one of these types. Make
+ a new one and place it in the user defined types. */
+ ftype = lookup_function_type (type);
+ alloc_utype (dip -> die_ref, ftype);
+ }
+ else if (TYPE_CODE (ftype) == TYPE_CODE_UNDEF)
+ {
+ /* We have an existing partially constructed type, so bash it
+ into the correct type. */
+ TYPE_TARGET_TYPE (ftype) = type;
+ TYPE_FUNCTION_TYPE (type) = ftype;
+ TYPE_LENGTH (ftype) = 1;
+ TYPE_CODE (ftype) = TYPE_CODE_FUNC;
+ }
+ else
+ {
+ complain (&dup_user_type_definition, DIE_ID, DIE_NAME);
+ }
+}
+
+/*
+
+LOCAL FUNCTION
+
+ read_enumeration -- process dies which define an enumeration
+
+SYNOPSIS
+
+ static void read_enumeration (struct dieinfo *dip, char *thisdie,
+ char *enddie, struct objfile *objfile)
+
+DESCRIPTION
+
+ Given a pointer to a die which begins an enumeration, process all
+ the dies that define the members of the enumeration.
+
+NOTES
+
+ Note that we need to call enum_type regardless of whether or not we
+ have a symbol, since we might have an enum without a tag name (thus
+ no symbol for the tagname).
+ */
+
+static void
+read_enumeration (dip, thisdie, enddie, objfile)
+ struct dieinfo *dip;
+ char *thisdie;
+ char *enddie;
+ struct objfile *objfile;
+{
+ struct type *type;
+ struct symbol *sym;
+
+ type = enum_type (dip, objfile);
+ sym = new_symbol (dip, objfile);
+ if (sym != NULL)
+ {
+ SYMBOL_TYPE (sym) = type;
+ if (cu_language == language_cplus)
+ {
+ synthesize_typedef (dip, objfile, type);
+ }
+ }
+}
+
+/*
+
+LOCAL FUNCTION
+
+ enum_type -- decode and return a type for an enumeration
+
+SYNOPSIS
+
+ static type *enum_type (struct dieinfo *dip, struct objfile *objfile)
+
+DESCRIPTION
+
+ Given a pointer to a die information structure for the die which
+ starts an enumeration, process all the dies that define the members
+ of the enumeration and return a type pointer for the enumeration.
+
+ At the same time, for each member of the enumeration, create a
+ symbol for it with namespace VAR_NAMESPACE and class LOC_CONST,
+ and give it the type of the enumeration itself.
+
+NOTES
+
+ Note that the DWARF specification explicitly mandates that enum
+ constants occur in reverse order from the source program order,
+ for "consistency" and because this ordering is easier for many
+ compilers to generate. (Draft 6, sec 3.8.5, Enumeration type
+ Entries). Because gdb wants to see the enum members in program
+ source order, we have to ensure that the order gets reversed while
+ we are processing them.
+ */
+
+static struct type *
+enum_type (dip, objfile)
+ struct dieinfo *dip;
+ struct objfile *objfile;
+{
+ struct type *type;
+ struct nextfield {
+ struct nextfield *next;
+ struct field field;
+ };
+ struct nextfield *list = NULL;
+ struct nextfield *new;
+ int nfields = 0;
+ int n;
+ char *scan;
+ char *listend;
+ unsigned short blocksz;
+ struct symbol *sym;
+ int nbytes;
+
+ if ((type = lookup_utype (dip -> die_ref)) == NULL)
+ {
+ /* No forward references created an empty type, so install one now */
+ type = alloc_utype (dip -> die_ref, NULL);
+ }
+ TYPE_CODE (type) = TYPE_CODE_ENUM;
+ /* Some compilers try to be helpful by inventing "fake" names for
+ anonymous enums, structures, and unions, like "~0fake" or ".0fake".
+ Thanks, but no thanks... */
+ if (dip -> at_name != NULL
+ && *dip -> at_name != '~'
+ && *dip -> at_name != '.')
+ {
+ TYPE_TAG_NAME (type) = obconcat (&objfile -> type_obstack,
+ "", "", dip -> at_name);
+ }
+ if (dip -> at_byte_size != 0)
+ {
+ TYPE_LENGTH (type) = dip -> at_byte_size;
+ }
+ if ((scan = dip -> at_element_list) != NULL)
+ {
+ if (dip -> short_element_list)
+ {
+ nbytes = attribute_size (AT_short_element_list);
+ }
+ else
+ {
+ nbytes = attribute_size (AT_element_list);
+ }
+ blocksz = target_to_host (scan, nbytes, GET_UNSIGNED, objfile);
+ listend = scan + nbytes + blocksz;
+ scan += nbytes;
+ while (scan < listend)
+ {
+ new = (struct nextfield *) alloca (sizeof (struct nextfield));
+ new -> next = list;
+ list = new;
+ list -> field.type = NULL;
+ list -> field.bitsize = 0;
+ list -> field.bitpos =
+ target_to_host (scan, TARGET_FT_LONG_SIZE (objfile), GET_SIGNED,
+ objfile);
+ scan += TARGET_FT_LONG_SIZE (objfile);
+ list -> field.name = obsavestring (scan, strlen (scan),
+ &objfile -> type_obstack);
+ scan += strlen (scan) + 1;
+ nfields++;
+ /* Handcraft a new symbol for this enum member. */
+ sym = (struct symbol *) obstack_alloc (&objfile->symbol_obstack,
+ sizeof (struct symbol));
+ memset (sym, 0, sizeof (struct symbol));
+ SYMBOL_NAME (sym) = create_name (list -> field.name,
+ &objfile->symbol_obstack);
+ SYMBOL_INIT_LANGUAGE_SPECIFIC (sym, cu_language);
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ SYMBOL_CLASS (sym) = LOC_CONST;
+ SYMBOL_TYPE (sym) = type;
+ SYMBOL_VALUE (sym) = list -> field.bitpos;
+ add_symbol_to_list (sym, list_in_scope);
+ }
+ /* Now create the vector of fields, and record how big it is. This is
+ where we reverse the order, by pulling the members off the list in
+ reverse order from how they were inserted. If we have no fields
+ (this is apparently possible in C++) then skip building a field
+ vector. */
+ if (nfields > 0)
+ {
+ TYPE_NFIELDS (type) = nfields;
+ TYPE_FIELDS (type) = (struct field *)
+ obstack_alloc (&objfile->symbol_obstack, sizeof (struct field) * nfields);
+ /* Copy the saved-up fields into the field vector. */
+ for (n = 0; (n < nfields) && (list != NULL); list = list -> next)
+ {
+ TYPE_FIELD (type, n++) = list -> field;
+ }
+ }
+ }
+ return (type);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ read_func_scope -- process all dies within a function scope
+
+DESCRIPTION
+
+ Process all dies within a given function scope. We are passed
+ a die information structure pointer DIP for the die which
+ starts the function scope, and pointers into the raw die data
+ that define the dies within the function scope.
+
+ For now, we ignore lexical block scopes within the function.
+ The problem is that AT&T cc does not define a DWARF lexical
+ block scope for the function itself, while gcc defines a
+ lexical block scope for the function. We need to think about
+ how to handle this difference, or if it is even a problem.
+ (FIXME)
+ */
+
+static void
+read_func_scope (dip, thisdie, enddie, objfile)
+ struct dieinfo *dip;
+ char *thisdie;
+ char *enddie;
+ struct objfile *objfile;
+{
+ register struct context_stack *new;
+
+ if (objfile -> ei.entry_point >= dip -> at_low_pc &&
+ objfile -> ei.entry_point < dip -> at_high_pc)
+ {
+ objfile -> ei.entry_func_lowpc = dip -> at_low_pc;
+ objfile -> ei.entry_func_highpc = dip -> at_high_pc;
+ }
+ if (STREQ (dip -> at_name, "main")) /* FIXME: hardwired name */
+ {
+ objfile -> ei.main_func_lowpc = dip -> at_low_pc;
+ objfile -> ei.main_func_highpc = dip -> at_high_pc;
+ }
+ new = push_context (0, dip -> at_low_pc);
+ new -> name = new_symbol (dip, objfile);
+ list_in_scope = &local_symbols;
+ process_dies (thisdie + dip -> die_length, enddie, objfile);
+ new = pop_context ();
+ /* Make a block for the local symbols within. */
+ finish_block (new -> name, &local_symbols, new -> old_blocks,
+ new -> start_addr, dip -> at_high_pc, objfile);
+ list_in_scope = &file_symbols;
+}
+
+
+/*
+
+LOCAL FUNCTION
+
+ handle_producer -- process the AT_producer attribute
+
+DESCRIPTION
+
+ Perform any operations that depend on finding a particular
+ AT_producer attribute.
+
+ */
+
+static void
+handle_producer (producer)
+ char *producer;
+{
+
+ /* If this compilation unit was compiled with g++ or gcc, then set the
+ processing_gcc_compilation flag. */
+
+ processing_gcc_compilation =
+ STREQN (producer, GPLUS_PRODUCER, strlen (GPLUS_PRODUCER))
+ || STREQN (producer, CHILL_PRODUCER, strlen (CHILL_PRODUCER))
+ || STREQN (producer, GCC_PRODUCER, strlen (GCC_PRODUCER));
+
+ /* Select a demangling style if we can identify the producer and if
+ the current style is auto. We leave the current style alone if it
+ is not auto. We also leave the demangling style alone if we find a
+ gcc (cc1) producer, as opposed to a g++ (cc1plus) producer. */
+
+ if (AUTO_DEMANGLING)
+ {
+ if (STREQN (producer, GPLUS_PRODUCER, strlen (GPLUS_PRODUCER)))
+ {
+ set_demangling_style (GNU_DEMANGLING_STYLE_STRING);
+ }
+ else if (STREQN (producer, LCC_PRODUCER, strlen (LCC_PRODUCER)))
+ {
+ set_demangling_style (LUCID_DEMANGLING_STYLE_STRING);
+ }
+ }
+}
+
+
+/*
+
+LOCAL FUNCTION
+
+ read_file_scope -- process all dies within a file scope
+
+DESCRIPTION
+
+ Process all dies within a given file scope. We are passed a
+ pointer to the die information structure for the die which
+ starts the file scope, and pointers into the raw die data which
+ mark the range of dies within the file scope.
+
+ When the partial symbol table is built, the file offset for the line
+ number table for each compilation unit is saved in the partial symbol
+ table entry for that compilation unit. As the symbols for each
+ compilation unit are read, the line number table is read into memory
+ and the variable lnbase is set to point to it. Thus all we have to
+ do is use lnbase to access the line number table for the current
+ compilation unit.
+ */
+
+static void
+read_file_scope (dip, thisdie, enddie, objfile)
+ struct dieinfo *dip;
+ char *thisdie;
+ char *enddie;
+ struct objfile *objfile;
+{
+ struct cleanup *back_to;
+ struct symtab *symtab;
+
+ if (objfile -> ei.entry_point >= dip -> at_low_pc &&
+ objfile -> ei.entry_point < dip -> at_high_pc)
+ {
+ objfile -> ei.entry_file_lowpc = dip -> at_low_pc;
+ objfile -> ei.entry_file_highpc = dip -> at_high_pc;
+ }
+ set_cu_language (dip);
+ if (dip -> at_producer != NULL)
+ {
+ handle_producer (dip -> at_producer);
+ }
+ numutypes = (enddie - thisdie) / 4;
+ utypes = (struct type **) xmalloc (numutypes * sizeof (struct type *));
+ back_to = make_cleanup (free, utypes);
+ memset (utypes, 0, numutypes * sizeof (struct type *));
+ memset (ftypes, 0, FT_NUM_MEMBERS * sizeof (struct type *));
+ start_symtab (dip -> at_name, dip -> at_comp_dir, dip -> at_low_pc);
+ decode_line_numbers (lnbase);
+ process_dies (thisdie + dip -> die_length, enddie, objfile);
+
+ symtab = end_symtab (dip -> at_high_pc, 0, 0, objfile, 0);
+ if (symtab != NULL)
+ {
+ symtab -> language = cu_language;
+ }
+ do_cleanups (back_to);
+ utypes = NULL;
+ numutypes = 0;
+}
+
+/*
+
+LOCAL FUNCTION
+
+ process_dies -- process a range of DWARF Information Entries
+
+SYNOPSIS
+
+ static void process_dies (char *thisdie, char *enddie,
+ struct objfile *objfile)
+
+DESCRIPTION
+
+ Process all DIE's in a specified range. May be (and almost
+ certainly will be) called recursively.
+ */
+
+static void
+process_dies (thisdie, enddie, objfile)
+ char *thisdie;
+ char *enddie;
+ struct objfile *objfile;
+{
+ char *nextdie;
+ struct dieinfo di;
+
+ while (thisdie < enddie)
+ {
+ basicdieinfo (&di, thisdie, objfile);
+ if (di.die_length < SIZEOF_DIE_LENGTH)
+ {
+ break;
+ }
+ else if (di.die_tag == TAG_padding)
+ {
+ nextdie = thisdie + di.die_length;
+ }
+ else
+ {
+ completedieinfo (&di, objfile);
+ if (di.at_sibling != 0)
+ {
+ nextdie = dbbase + di.at_sibling - dbroff;
+ }
+ else
+ {
+ nextdie = thisdie + di.die_length;
+ }
+ switch (di.die_tag)
+ {
+ case TAG_compile_unit:
+ read_file_scope (&di, thisdie, nextdie, objfile);
+ break;
+ case TAG_global_subroutine:
+ case TAG_subroutine:
+ if (di.has_at_low_pc)
+ {
+ read_func_scope (&di, thisdie, nextdie, objfile);
+ }
+ break;
+ case TAG_lexical_block:
+ read_lexical_block_scope (&di, thisdie, nextdie, objfile);
+ break;
+ case TAG_class_type:
+ case TAG_structure_type:
+ case TAG_union_type:
+ read_structure_scope (&di, thisdie, nextdie, objfile);
+ break;
+ case TAG_enumeration_type:
+ read_enumeration (&di, thisdie, nextdie, objfile);
+ break;
+ case TAG_subroutine_type:
+ read_subroutine_type (&di, thisdie, nextdie);
+ break;
+ case TAG_array_type:
+ dwarf_read_array_type (&di);
+ break;
+ case TAG_pointer_type:
+ read_tag_pointer_type (&di);
+ break;
+ case TAG_string_type:
+ read_tag_string_type (&di);
+ break;
+ default:
+ new_symbol (&di, objfile);
+ break;
+ }
+ }
+ thisdie = nextdie;
+ }
+}
+
+/*
+
+LOCAL FUNCTION
+
+ decode_line_numbers -- decode a line number table fragment
+
+SYNOPSIS
+
+ static void decode_line_numbers (char *tblscan, char *tblend,
+ long length, long base, long line, long pc)
+
+DESCRIPTION
+
+ Translate the DWARF line number information to gdb form.
+
+ The ".line" section contains one or more line number tables, one for
+ each ".line" section from the objects that were linked.
+
+ The AT_stmt_list attribute for each TAG_source_file entry in the
+ ".debug" section contains the offset into the ".line" section for the
+ start of the table for that file.
+
+ The table itself has the following structure:
+
+ <table length><base address><source statement entry>
+ 4 bytes 4 bytes 10 bytes
+
+ The table length is the total size of the table, including the 4 bytes
+ for the length information.
+
+ The base address is the address of the first instruction generated
+ for the source file.
+
+ Each source statement entry has the following structure:
+
+ <line number><statement position><address delta>
+ 4 bytes 2 bytes 4 bytes
+
+ The line number is relative to the start of the file, starting with
+ line 1.
+
+ The statement position either -1 (0xFFFF) or the number of characters
+ from the beginning of the line to the beginning of the statement.
+
+ The address delta is the difference between the base address and
+ the address of the first instruction for the statement.
+
+ Note that we must copy the bytes from the packed table to our local
+ variables before attempting to use them, to avoid alignment problems
+ on some machines, particularly RISC processors.
+
+BUGS
+
+ Does gdb expect the line numbers to be sorted? They are now by
+ chance/luck, but are not required to be. (FIXME)
+
+ The line with number 0 is unused, gdb apparently can discover the
+ span of the last line some other way. How? (FIXME)
+ */
+
+static void
+decode_line_numbers (linetable)
+ char *linetable;
+{
+ char *tblscan;
+ char *tblend;
+ unsigned long length;
+ unsigned long base;
+ unsigned long line;
+ unsigned long pc;
+
+ if (linetable != NULL)
+ {
+ tblscan = tblend = linetable;
+ length = target_to_host (tblscan, SIZEOF_LINETBL_LENGTH, GET_UNSIGNED,
+ current_objfile);
+ tblscan += SIZEOF_LINETBL_LENGTH;
+ tblend += length;
+ base = target_to_host (tblscan, TARGET_FT_POINTER_SIZE (objfile),
+ GET_UNSIGNED, current_objfile);
+ tblscan += TARGET_FT_POINTER_SIZE (objfile);
+ base += baseaddr;
+ while (tblscan < tblend)
+ {
+ line = target_to_host (tblscan, SIZEOF_LINETBL_LINENO, GET_UNSIGNED,
+ current_objfile);
+ tblscan += SIZEOF_LINETBL_LINENO + SIZEOF_LINETBL_STMT;
+ pc = target_to_host (tblscan, SIZEOF_LINETBL_DELTA, GET_UNSIGNED,
+ current_objfile);
+ tblscan += SIZEOF_LINETBL_DELTA;
+ pc += base;
+ if (line != 0)
+ {
+ record_line (current_subfile, line, pc);
+ }
+ }
+ }
+}
+
+/*
+
+LOCAL FUNCTION
+
+ locval -- compute the value of a location attribute
+
+SYNOPSIS
+
+ static int locval (char *loc)
+
+DESCRIPTION
+
+ Given pointer to a string of bytes that define a location, compute
+ the location and return the value.
+
+ When computing values involving the current value of the frame pointer,
+ the value zero is used, which results in a value relative to the frame
+ pointer, rather than the absolute value. This is what GDB wants
+ anyway.
+
+ When the result is a register number, the global isreg flag is set,
+ otherwise it is cleared. This is a kludge until we figure out a better
+ way to handle the problem. Gdb's design does not mesh well with the
+ DWARF notion of a location computing interpreter, which is a shame
+ because the flexibility goes unused.
+
+NOTES
+
+ Note that stack[0] is unused except as a default error return.
+ Note that stack overflow is not yet handled.
+ */
+
+static int
+locval (loc)
+ char *loc;
+{
+ unsigned short nbytes;
+ unsigned short locsize;
+ auto long stack[64];
+ int stacki;
+ char *end;
+ int loc_atom_code;
+ int loc_value_size;
+
+ nbytes = attribute_size (AT_location);
+ locsize = target_to_host (loc, nbytes, GET_UNSIGNED, current_objfile);
+ loc += nbytes;
+ end = loc + locsize;
+ stacki = 0;
+ stack[stacki] = 0;
+ isreg = 0;
+ offreg = 0;
+ loc_value_size = TARGET_FT_LONG_SIZE (current_objfile);
+ while (loc < end)
+ {
+ loc_atom_code = target_to_host (loc, SIZEOF_LOC_ATOM_CODE, GET_UNSIGNED,
+ current_objfile);
+ loc += SIZEOF_LOC_ATOM_CODE;
+ switch (loc_atom_code)
+ {
+ case 0:
+ /* error */
+ loc = end;
+ break;
+ case OP_REG:
+ /* push register (number) */
+ stack[++stacki] = target_to_host (loc, loc_value_size,
+ GET_UNSIGNED, current_objfile);
+ loc += loc_value_size;
+ isreg = 1;
+ break;
+ case OP_BASEREG:
+ /* push value of register (number) */
+ /* Actually, we compute the value as if register has 0, so the
+ value ends up being the offset from that register. */
+ offreg = 1;
+ basereg = target_to_host (loc, loc_value_size, GET_UNSIGNED,
+ current_objfile);
+ loc += loc_value_size;
+ stack[++stacki] = 0;
+ break;
+ case OP_ADDR:
+ /* push address (relocated address) */
+ stack[++stacki] = target_to_host (loc, loc_value_size,
+ GET_UNSIGNED, current_objfile);
+ loc += loc_value_size;
+ break;
+ case OP_CONST:
+ /* push constant (number) FIXME: signed or unsigned! */
+ stack[++stacki] = target_to_host (loc, loc_value_size,
+ GET_SIGNED, current_objfile);
+ loc += loc_value_size;
+ break;
+ case OP_DEREF2:
+ /* pop, deref and push 2 bytes (as a long) */
+ complain (&op_deref2, DIE_ID, DIE_NAME, stack[stacki]);
+ break;
+ case OP_DEREF4: /* pop, deref and push 4 bytes (as a long) */
+ complain (&op_deref4, DIE_ID, DIE_NAME, stack[stacki]);
+ break;
+ case OP_ADD: /* pop top 2 items, add, push result */
+ stack[stacki - 1] += stack[stacki];
+ stacki--;
+ break;
+ }
+ }
+ return (stack[stacki]);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ read_ofile_symtab -- build a full symtab entry from chunk of DIE's
+
+SYNOPSIS
+
+ static void read_ofile_symtab (struct partial_symtab *pst)
+
+DESCRIPTION
+
+ When expanding a partial symbol table entry to a full symbol table
+ entry, this is the function that gets called to read in the symbols
+ for the compilation unit. A pointer to the newly constructed symtab,
+ which is now the new first one on the objfile's symtab list, is
+ stashed in the partial symbol table entry.
+ */
+
+static void
+read_ofile_symtab (pst)
+ struct partial_symtab *pst;
+{
+ struct cleanup *back_to;
+ unsigned long lnsize;
+ file_ptr foffset;
+ bfd *abfd;
+ char lnsizedata[SIZEOF_LINETBL_LENGTH];
+
+ abfd = pst -> objfile -> obfd;
+ current_objfile = pst -> objfile;
+
+ /* Allocate a buffer for the entire chunk of DIE's for this compilation
+ unit, seek to the location in the file, and read in all the DIE's. */
+
+ diecount = 0;
+ dbsize = DBLENGTH (pst);
+ dbbase = xmalloc (dbsize);
+ dbroff = DBROFF(pst);
+ foffset = DBFOFF(pst) + dbroff;
+ base_section_offsets = pst->section_offsets;
+ baseaddr = ANOFFSET (pst->section_offsets, 0);
+ if (bfd_seek (abfd, foffset, L_SET) ||
+ (bfd_read (dbbase, dbsize, 1, abfd) != dbsize))
+ {
+ free (dbbase);
+ error ("can't read DWARF data");
+ }
+ back_to = make_cleanup (free, dbbase);
+
+ /* If there is a line number table associated with this compilation unit
+ then read the size of this fragment in bytes, from the fragment itself.
+ Allocate a buffer for the fragment and read it in for future
+ processing. */
+
+ lnbase = NULL;
+ if (LNFOFF (pst))
+ {
+ if (bfd_seek (abfd, LNFOFF (pst), L_SET) ||
+ (bfd_read ((PTR) lnsizedata, sizeof (lnsizedata), 1, abfd) !=
+ sizeof (lnsizedata)))
+ {
+ error ("can't read DWARF line number table size");
+ }
+ lnsize = target_to_host (lnsizedata, SIZEOF_LINETBL_LENGTH,
+ GET_UNSIGNED, pst -> objfile);
+ lnbase = xmalloc (lnsize);
+ if (bfd_seek (abfd, LNFOFF (pst), L_SET) ||
+ (bfd_read (lnbase, lnsize, 1, abfd) != lnsize))
+ {
+ free (lnbase);
+ error ("can't read DWARF line numbers");
+ }
+ make_cleanup (free, lnbase);
+ }
+
+ process_dies (dbbase, dbbase + dbsize, pst -> objfile);
+ do_cleanups (back_to);
+ current_objfile = NULL;
+ pst -> symtab = pst -> objfile -> symtabs;
+}
+
+/*
+
+LOCAL FUNCTION
+
+ psymtab_to_symtab_1 -- do grunt work for building a full symtab entry
+
+SYNOPSIS
+
+ static void psymtab_to_symtab_1 (struct partial_symtab *pst)
+
+DESCRIPTION
+
+ Called once for each partial symbol table entry that needs to be
+ expanded into a full symbol table entry.
+
+*/
+
+static void
+psymtab_to_symtab_1 (pst)
+ struct partial_symtab *pst;
+{
+ int i;
+ struct cleanup *old_chain;
+
+ if (pst != NULL)
+ {
+ if (pst->readin)
+ {
+ warning ("psymtab for %s already read in. Shouldn't happen.",
+ pst -> filename);
+ }
+ else
+ {
+ /* Read in all partial symtabs on which this one is dependent */
+ for (i = 0; i < pst -> number_of_dependencies; i++)
+ {
+ if (!pst -> dependencies[i] -> readin)
+ {
+ /* Inform about additional files that need to be read in. */
+ if (info_verbose)
+ {
+ fputs_filtered (" ", stdout);
+ wrap_here ("");
+ fputs_filtered ("and ", stdout);
+ wrap_here ("");
+ printf_filtered ("%s...",
+ pst -> dependencies[i] -> filename);
+ wrap_here ("");
+ fflush (stdout); /* Flush output */
+ }
+ psymtab_to_symtab_1 (pst -> dependencies[i]);
+ }
+ }
+ if (DBLENGTH (pst)) /* Otherwise it's a dummy */
+ {
+ buildsym_init ();
+ old_chain = make_cleanup (really_free_pendings, 0);
+ read_ofile_symtab (pst);
+ if (info_verbose)
+ {
+ printf_filtered ("%d DIE's, sorting...", diecount);
+ wrap_here ("");
+ fflush (stdout);
+ }
+ sort_symtab_syms (pst -> symtab);
+ do_cleanups (old_chain);
+ }
+ pst -> readin = 1;
+ }
+ }
+}
+
+/*
+
+LOCAL FUNCTION
+
+ dwarf_psymtab_to_symtab -- build a full symtab entry from partial one
+
+SYNOPSIS
+
+ static void dwarf_psymtab_to_symtab (struct partial_symtab *pst)
+
+DESCRIPTION
+
+ This is the DWARF support entry point for building a full symbol
+ table entry from a partial symbol table entry. We are passed a
+ pointer to the partial symbol table entry that needs to be expanded.
+
+*/
+
+static void
+dwarf_psymtab_to_symtab (pst)
+ struct partial_symtab *pst;
+{
+
+ if (pst != NULL)
+ {
+ if (pst -> readin)
+ {
+ warning ("psymtab for %s already read in. Shouldn't happen.",
+ pst -> filename);
+ }
+ else
+ {
+ if (DBLENGTH (pst) || pst -> number_of_dependencies)
+ {
+ /* Print the message now, before starting serious work, to avoid
+ disconcerting pauses. */
+ if (info_verbose)
+ {
+ printf_filtered ("Reading in symbols for %s...",
+ pst -> filename);
+ fflush (stdout);
+ }
+
+ psymtab_to_symtab_1 (pst);
+
+#if 0 /* FIXME: Check to see what dbxread is doing here and see if
+ we need to do an equivalent or is this something peculiar to
+ stabs/a.out format.
+ Match with global symbols. This only needs to be done once,
+ after all of the symtabs and dependencies have been read in.
+ */
+ scan_file_globals (pst -> objfile);
+#endif
+
+ /* Finish up the verbose info message. */
+ if (info_verbose)
+ {
+ printf_filtered ("done.\n");
+ fflush (stdout);
+ }
+ }
+ }
+ }
+}
+
+/*
+
+LOCAL FUNCTION
+
+ init_psymbol_list -- initialize storage for partial symbols
+
+SYNOPSIS
+
+ static void init_psymbol_list (struct objfile *objfile, int total_symbols)
+
+DESCRIPTION
+
+ Initializes storage for all of the partial symbols that will be
+ created by dwarf_build_psymtabs and subsidiaries.
+ */
+
+static void
+init_psymbol_list (objfile, total_symbols)
+ struct objfile *objfile;
+ int total_symbols;
+{
+ /* Free any previously allocated psymbol lists. */
+
+ if (objfile -> global_psymbols.list)
+ {
+ mfree (objfile -> md, (PTR)objfile -> global_psymbols.list);
+ }
+ if (objfile -> static_psymbols.list)
+ {
+ mfree (objfile -> md, (PTR)objfile -> static_psymbols.list);
+ }
+
+ /* Current best guess is that there are approximately a twentieth
+ of the total symbols (in a debugging file) are global or static
+ oriented symbols */
+
+ objfile -> global_psymbols.size = total_symbols / 10;
+ objfile -> static_psymbols.size = total_symbols / 10;
+ objfile -> global_psymbols.next =
+ objfile -> global_psymbols.list = (struct partial_symbol *)
+ xmmalloc (objfile -> md, objfile -> global_psymbols.size
+ * sizeof (struct partial_symbol));
+ objfile -> static_psymbols.next =
+ objfile -> static_psymbols.list = (struct partial_symbol *)
+ xmmalloc (objfile -> md, objfile -> static_psymbols.size
+ * sizeof (struct partial_symbol));
+}
+
+/*
+
+LOCAL FUNCTION
+
+ add_enum_psymbol -- add enumeration members to partial symbol table
+
+DESCRIPTION
+
+ Given pointer to a DIE that is known to be for an enumeration,
+ extract the symbolic names of the enumeration members and add
+ partial symbols for them.
+*/
+
+static void
+add_enum_psymbol (dip, objfile)
+ struct dieinfo *dip;
+ struct objfile *objfile;
+{
+ char *scan;
+ char *listend;
+ unsigned short blocksz;
+ int nbytes;
+
+ if ((scan = dip -> at_element_list) != NULL)
+ {
+ if (dip -> short_element_list)
+ {
+ nbytes = attribute_size (AT_short_element_list);
+ }
+ else
+ {
+ nbytes = attribute_size (AT_element_list);
+ }
+ blocksz = target_to_host (scan, nbytes, GET_UNSIGNED, objfile);
+ scan += nbytes;
+ listend = scan + blocksz;
+ while (scan < listend)
+ {
+ scan += TARGET_FT_LONG_SIZE (objfile);
+ ADD_PSYMBOL_TO_LIST (scan, strlen (scan), VAR_NAMESPACE, LOC_CONST,
+ objfile -> static_psymbols, 0, cu_language,
+ objfile);
+ scan += strlen (scan) + 1;
+ }
+ }
+}
+
+/*
+
+LOCAL FUNCTION
+
+ add_partial_symbol -- add symbol to partial symbol table
+
+DESCRIPTION
+
+ Given a DIE, if it is one of the types that we want to
+ add to a partial symbol table, finish filling in the die info
+ and then add a partial symbol table entry for it.
+
+NOTES
+
+ The caller must ensure that the DIE has a valid name attribute.
+*/
+
+static void
+add_partial_symbol (dip, objfile)
+ struct dieinfo *dip;
+ struct objfile *objfile;
+{
+ switch (dip -> die_tag)
+ {
+ case TAG_global_subroutine:
+ ADD_PSYMBOL_TO_LIST (dip -> at_name, strlen (dip -> at_name),
+ VAR_NAMESPACE, LOC_BLOCK,
+ objfile -> global_psymbols,
+ dip -> at_low_pc, cu_language, objfile);
+ break;
+ case TAG_global_variable:
+ ADD_PSYMBOL_TO_LIST (dip -> at_name, strlen (dip -> at_name),
+ VAR_NAMESPACE, LOC_STATIC,
+ objfile -> global_psymbols,
+ 0, cu_language, objfile);
+ break;
+ case TAG_subroutine:
+ ADD_PSYMBOL_TO_LIST (dip -> at_name, strlen (dip -> at_name),
+ VAR_NAMESPACE, LOC_BLOCK,
+ objfile -> static_psymbols,
+ dip -> at_low_pc, cu_language, objfile);
+ break;
+ case TAG_local_variable:
+ ADD_PSYMBOL_TO_LIST (dip -> at_name, strlen (dip -> at_name),
+ VAR_NAMESPACE, LOC_STATIC,
+ objfile -> static_psymbols,
+ 0, cu_language, objfile);
+ break;
+ case TAG_typedef:
+ ADD_PSYMBOL_TO_LIST (dip -> at_name, strlen (dip -> at_name),
+ VAR_NAMESPACE, LOC_TYPEDEF,
+ objfile -> static_psymbols,
+ 0, cu_language, objfile);
+ break;
+ case TAG_class_type:
+ case TAG_structure_type:
+ case TAG_union_type:
+ case TAG_enumeration_type:
+ ADD_PSYMBOL_TO_LIST (dip -> at_name, strlen (dip -> at_name),
+ STRUCT_NAMESPACE, LOC_TYPEDEF,
+ objfile -> static_psymbols,
+ 0, cu_language, objfile);
+ if (cu_language == language_cplus)
+ {
+ /* For C++, these implicitly act as typedefs as well. */
+ ADD_PSYMBOL_TO_LIST (dip -> at_name, strlen (dip -> at_name),
+ VAR_NAMESPACE, LOC_TYPEDEF,
+ objfile -> static_psymbols,
+ 0, cu_language, objfile);
+ }
+ break;
+ }
+}
+
+/*
+
+LOCAL FUNCTION
+
+ scan_partial_symbols -- scan DIE's within a single compilation unit
+
+DESCRIPTION
+
+ Process the DIE's within a single compilation unit, looking for
+ interesting DIE's that contribute to the partial symbol table entry
+ for this compilation unit.
+
+NOTES
+
+ There are some DIE's that may appear both at file scope and within
+ the scope of a function. We are only interested in the ones at file
+ scope, and the only way to tell them apart is to keep track of the
+ scope. For example, consider the test case:
+
+ static int i;
+ main () { int j; }
+
+ for which the relevant DWARF segment has the structure:
+
+ 0x51:
+ 0x23 global subrtn sibling 0x9b
+ name main
+ fund_type FT_integer
+ low_pc 0x800004cc
+ high_pc 0x800004d4
+
+ 0x74:
+ 0x23 local var sibling 0x97
+ name j
+ fund_type FT_integer
+ location OP_BASEREG 0xe
+ OP_CONST 0xfffffffc
+ OP_ADD
+ 0x97:
+ 0x4
+
+ 0x9b:
+ 0x1d local var sibling 0xb8
+ name i
+ fund_type FT_integer
+ location OP_ADDR 0x800025dc
+
+ 0xb8:
+ 0x4
+
+ We want to include the symbol 'i' in the partial symbol table, but
+ not the symbol 'j'. In essence, we want to skip all the dies within
+ the scope of a TAG_global_subroutine DIE.
+
+ Don't attempt to add anonymous structures or unions since they have
+ no name. Anonymous enumerations however are processed, because we
+ want to extract their member names (the check for a tag name is
+ done later).
+
+ Also, for variables and subroutines, check that this is the place
+ where the actual definition occurs, rather than just a reference
+ to an external.
+ */
+
+static void
+scan_partial_symbols (thisdie, enddie, objfile)
+ char *thisdie;
+ char *enddie;
+ struct objfile *objfile;
+{
+ char *nextdie;
+ char *temp;
+ struct dieinfo di;
+
+ while (thisdie < enddie)
+ {
+ basicdieinfo (&di, thisdie, objfile);
+ if (di.die_length < SIZEOF_DIE_LENGTH)
+ {
+ break;
+ }
+ else
+ {
+ nextdie = thisdie + di.die_length;
+ /* To avoid getting complete die information for every die, we
+ only do it (below) for the cases we are interested in. */
+ switch (di.die_tag)
+ {
+ case TAG_global_subroutine:
+ case TAG_subroutine:
+ completedieinfo (&di, objfile);
+ if (di.at_name && (di.has_at_low_pc || di.at_location))
+ {
+ add_partial_symbol (&di, objfile);
+ /* If there is a sibling attribute, adjust the nextdie
+ pointer to skip the entire scope of the subroutine.
+ Apply some sanity checking to make sure we don't
+ overrun or underrun the range of remaining DIE's */
+ if (di.at_sibling != 0)
+ {
+ temp = dbbase + di.at_sibling - dbroff;
+ if ((temp < thisdie) || (temp >= enddie))
+ {
+ complain (&bad_die_ref, DIE_ID, DIE_NAME,
+ di.at_sibling);
+ }
+ else
+ {
+ nextdie = temp;
+ }
+ }
+ }
+ break;
+ case TAG_global_variable:
+ case TAG_local_variable:
+ completedieinfo (&di, objfile);
+ if (di.at_name && (di.has_at_low_pc || di.at_location))
+ {
+ add_partial_symbol (&di, objfile);
+ }
+ break;
+ case TAG_typedef:
+ case TAG_class_type:
+ case TAG_structure_type:
+ case TAG_union_type:
+ completedieinfo (&di, objfile);
+ if (di.at_name)
+ {
+ add_partial_symbol (&di, objfile);
+ }
+ break;
+ case TAG_enumeration_type:
+ completedieinfo (&di, objfile);
+ if (di.at_name)
+ {
+ add_partial_symbol (&di, objfile);
+ }
+ add_enum_psymbol (&di, objfile);
+ break;
+ }
+ }
+ thisdie = nextdie;
+ }
+}
+
+/*
+
+LOCAL FUNCTION
+
+ scan_compilation_units -- build a psymtab entry for each compilation
+
+DESCRIPTION
+
+ This is the top level dwarf parsing routine for building partial
+ symbol tables.
+
+ It scans from the beginning of the DWARF table looking for the first
+ TAG_compile_unit DIE, and then follows the sibling chain to locate
+ each additional TAG_compile_unit DIE.
+
+ For each TAG_compile_unit DIE it creates a partial symtab structure,
+ calls a subordinate routine to collect all the compilation unit's
+ global DIE's, file scope DIEs, typedef DIEs, etc, and then links the
+ new partial symtab structure into the partial symbol table. It also
+ records the appropriate information in the partial symbol table entry
+ to allow the chunk of DIE's and line number table for this compilation
+ unit to be located and re-read later, to generate a complete symbol
+ table entry for the compilation unit.
+
+ Thus it effectively partitions up a chunk of DIE's for multiple
+ compilation units into smaller DIE chunks and line number tables,
+ and associates them with a partial symbol table entry.
+
+NOTES
+
+ If any compilation unit has no line number table associated with
+ it for some reason (a missing at_stmt_list attribute, rather than
+ just one with a value of zero, which is valid) then we ensure that
+ the recorded file offset is zero so that the routine which later
+ reads line number table fragments knows that there is no fragment
+ to read.
+
+RETURNS
+
+ Returns no value.
+
+ */
+
+static void
+scan_compilation_units (thisdie, enddie, dbfoff, lnoffset, objfile)
+ char *thisdie;
+ char *enddie;
+ file_ptr dbfoff;
+ file_ptr lnoffset;
+ struct objfile *objfile;
+{
+ char *nextdie;
+ struct dieinfo di;
+ struct partial_symtab *pst;
+ int culength;
+ int curoff;
+ file_ptr curlnoffset;
+
+ while (thisdie < enddie)
+ {
+ basicdieinfo (&di, thisdie, objfile);
+ if (di.die_length < SIZEOF_DIE_LENGTH)
+ {
+ break;
+ }
+ else if (di.die_tag != TAG_compile_unit)
+ {
+ nextdie = thisdie + di.die_length;
+ }
+ else
+ {
+ completedieinfo (&di, objfile);
+ set_cu_language (&di);
+ if (di.at_sibling != 0)
+ {
+ nextdie = dbbase + di.at_sibling - dbroff;
+ }
+ else
+ {
+ nextdie = thisdie + di.die_length;
+ }
+ curoff = thisdie - dbbase;
+ culength = nextdie - thisdie;
+ curlnoffset = di.has_at_stmt_list ? lnoffset + di.at_stmt_list : 0;
+
+ /* First allocate a new partial symbol table structure */
+
+ pst = start_psymtab_common (objfile, base_section_offsets,
+ di.at_name, di.at_low_pc,
+ objfile -> global_psymbols.next,
+ objfile -> static_psymbols.next);
+
+ pst -> texthigh = di.at_high_pc;
+ pst -> read_symtab_private = (char *)
+ obstack_alloc (&objfile -> psymbol_obstack,
+ sizeof (struct dwfinfo));
+ DBFOFF (pst) = dbfoff;
+ DBROFF (pst) = curoff;
+ DBLENGTH (pst) = culength;
+ LNFOFF (pst) = curlnoffset;
+ pst -> read_symtab = dwarf_psymtab_to_symtab;
+
+ /* Now look for partial symbols */
+
+ scan_partial_symbols (thisdie + di.die_length, nextdie, objfile);
+
+ pst -> n_global_syms = objfile -> global_psymbols.next -
+ (objfile -> global_psymbols.list + pst -> globals_offset);
+ pst -> n_static_syms = objfile -> static_psymbols.next -
+ (objfile -> static_psymbols.list + pst -> statics_offset);
+ sort_pst_symbols (pst);
+ /* If there is already a psymtab or symtab for a file of this name,
+ remove it. (If there is a symtab, more drastic things also
+ happen.) This happens in VxWorks. */
+ free_named_symtabs (pst -> filename);
+ }
+ thisdie = nextdie;
+ }
+}
+
+/*
+
+LOCAL FUNCTION
+
+ new_symbol -- make a symbol table entry for a new symbol
+
+SYNOPSIS
+
+ static struct symbol *new_symbol (struct dieinfo *dip,
+ struct objfile *objfile)
+
+DESCRIPTION
+
+ Given a pointer to a DWARF information entry, figure out if we need
+ to make a symbol table entry for it, and if so, create a new entry
+ and return a pointer to it.
+ */
+
+static struct symbol *
+new_symbol (dip, objfile)
+ struct dieinfo *dip;
+ struct objfile *objfile;
+{
+ struct symbol *sym = NULL;
+
+ if (dip -> at_name != NULL)
+ {
+ sym = (struct symbol *) obstack_alloc (&objfile -> symbol_obstack,
+ sizeof (struct symbol));
+ memset (sym, 0, sizeof (struct symbol));
+ SYMBOL_NAME (sym) = create_name (dip -> at_name,
+ &objfile->symbol_obstack);
+ /* default assumptions */
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ SYMBOL_CLASS (sym) = LOC_STATIC;
+ SYMBOL_TYPE (sym) = decode_die_type (dip);
+
+ /* If this symbol is from a C++ compilation, then attempt to cache the
+ demangled form for future reference. This is a typical time versus
+ space tradeoff, that was decided in favor of time because it sped up
+ C++ symbol lookups by a factor of about 20. */
+
+ SYMBOL_LANGUAGE (sym) = cu_language;
+ SYMBOL_INIT_DEMANGLED_NAME (sym, &objfile -> symbol_obstack);
+ switch (dip -> die_tag)
+ {
+ case TAG_label:
+ SYMBOL_VALUE (sym) = dip -> at_low_pc;
+ SYMBOL_CLASS (sym) = LOC_LABEL;
+ break;
+ case TAG_global_subroutine:
+ case TAG_subroutine:
+ SYMBOL_VALUE (sym) = dip -> at_low_pc;
+ SYMBOL_TYPE (sym) = lookup_function_type (SYMBOL_TYPE (sym));
+ SYMBOL_CLASS (sym) = LOC_BLOCK;
+ if (dip -> die_tag == TAG_global_subroutine)
+ {
+ add_symbol_to_list (sym, &global_symbols);
+ }
+ else
+ {
+ add_symbol_to_list (sym, list_in_scope);
+ }
+ break;
+ case TAG_global_variable:
+ if (dip -> at_location != NULL)
+ {
+ SYMBOL_VALUE (sym) = locval (dip -> at_location);
+ add_symbol_to_list (sym, &global_symbols);
+ SYMBOL_CLASS (sym) = LOC_STATIC;
+ SYMBOL_VALUE (sym) += baseaddr;
+ }
+ break;
+ case TAG_local_variable:
+ if (dip -> at_location != NULL)
+ {
+ SYMBOL_VALUE (sym) = locval (dip -> at_location);
+ add_symbol_to_list (sym, list_in_scope);
+ if (isreg)
+ {
+ SYMBOL_CLASS (sym) = LOC_REGISTER;
+ }
+ else if (offreg)
+ {
+ SYMBOL_CLASS (sym) = LOC_BASEREG;
+ SYMBOL_BASEREG (sym) = basereg;
+ }
+ else
+ {
+ SYMBOL_CLASS (sym) = LOC_STATIC;
+ SYMBOL_VALUE (sym) += baseaddr;
+ }
+ }
+ break;
+ case TAG_formal_parameter:
+ if (dip -> at_location != NULL)
+ {
+ SYMBOL_VALUE (sym) = locval (dip -> at_location);
+ }
+ add_symbol_to_list (sym, list_in_scope);
+ if (isreg)
+ {
+ SYMBOL_CLASS (sym) = LOC_REGPARM;
+ }
+ else if (offreg)
+ {
+ SYMBOL_CLASS (sym) = LOC_BASEREG_ARG;
+ SYMBOL_BASEREG (sym) = basereg;
+ }
+ else
+ {
+ SYMBOL_CLASS (sym) = LOC_ARG;
+ }
+ break;
+ case TAG_unspecified_parameters:
+ /* From varargs functions; gdb doesn't seem to have any interest in
+ this information, so just ignore it for now. (FIXME?) */
+ break;
+ case TAG_class_type:
+ case TAG_structure_type:
+ case TAG_union_type:
+ case TAG_enumeration_type:
+ SYMBOL_CLASS (sym) = LOC_TYPEDEF;
+ SYMBOL_NAMESPACE (sym) = STRUCT_NAMESPACE;
+ add_symbol_to_list (sym, list_in_scope);
+ break;
+ case TAG_typedef:
+ SYMBOL_CLASS (sym) = LOC_TYPEDEF;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, list_in_scope);
+ break;
+ default:
+ /* Not a tag we recognize. Hopefully we aren't processing trash
+ data, but since we must specifically ignore things we don't
+ recognize, there is nothing else we should do at this point. */
+ break;
+ }
+ }
+ return (sym);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ synthesize_typedef -- make a symbol table entry for a "fake" typedef
+
+SYNOPSIS
+
+ static void synthesize_typedef (struct dieinfo *dip,
+ struct objfile *objfile,
+ struct type *type);
+
+DESCRIPTION
+
+ Given a pointer to a DWARF information entry, synthesize a typedef
+ for the name in the DIE, using the specified type.
+
+ This is used for C++ class, structs, unions, and enumerations to
+ set up the tag name as a type.
+
+ */
+
+static void
+synthesize_typedef (dip, objfile, type)
+ struct dieinfo *dip;
+ struct objfile *objfile;
+ struct type *type;
+{
+ struct symbol *sym = NULL;
+
+ if (dip -> at_name != NULL)
+ {
+ sym = (struct symbol *)
+ obstack_alloc (&objfile -> symbol_obstack, sizeof (struct symbol));
+ memset (sym, 0, sizeof (struct symbol));
+ SYMBOL_NAME (sym) = create_name (dip -> at_name,
+ &objfile->symbol_obstack);
+ SYMBOL_INIT_LANGUAGE_SPECIFIC (sym, cu_language);
+ SYMBOL_TYPE (sym) = type;
+ SYMBOL_CLASS (sym) = LOC_TYPEDEF;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, list_in_scope);
+ }
+}
+
+/*
+
+LOCAL FUNCTION
+
+ decode_mod_fund_type -- decode a modified fundamental type
+
+SYNOPSIS
+
+ static struct type *decode_mod_fund_type (char *typedata)
+
+DESCRIPTION
+
+ Decode a block of data containing a modified fundamental
+ type specification. TYPEDATA is a pointer to the block,
+ which starts with a length containing the size of the rest
+ of the block. At the end of the block is a fundmental type
+ code value that gives the fundamental type. Everything
+ in between are type modifiers.
+
+ We simply compute the number of modifiers and call the general
+ function decode_modified_type to do the actual work.
+*/
+
+static struct type *
+decode_mod_fund_type (typedata)
+ char *typedata;
+{
+ struct type *typep = NULL;
+ unsigned short modcount;
+ int nbytes;
+
+ /* Get the total size of the block, exclusive of the size itself */
+
+ nbytes = attribute_size (AT_mod_fund_type);
+ modcount = target_to_host (typedata, nbytes, GET_UNSIGNED, current_objfile);
+ typedata += nbytes;
+
+ /* Deduct the size of the fundamental type bytes at the end of the block. */
+
+ modcount -= attribute_size (AT_fund_type);
+
+ /* Now do the actual decoding */
+
+ typep = decode_modified_type (typedata, modcount, AT_mod_fund_type);
+ return (typep);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ decode_mod_u_d_type -- decode a modified user defined type
+
+SYNOPSIS
+
+ static struct type *decode_mod_u_d_type (char *typedata)
+
+DESCRIPTION
+
+ Decode a block of data containing a modified user defined
+ type specification. TYPEDATA is a pointer to the block,
+ which consists of a two byte length, containing the size
+ of the rest of the block. At the end of the block is a
+ four byte value that gives a reference to a user defined type.
+ Everything in between are type modifiers.
+
+ We simply compute the number of modifiers and call the general
+ function decode_modified_type to do the actual work.
+*/
+
+static struct type *
+decode_mod_u_d_type (typedata)
+ char *typedata;
+{
+ struct type *typep = NULL;
+ unsigned short modcount;
+ int nbytes;
+
+ /* Get the total size of the block, exclusive of the size itself */
+
+ nbytes = attribute_size (AT_mod_u_d_type);
+ modcount = target_to_host (typedata, nbytes, GET_UNSIGNED, current_objfile);
+ typedata += nbytes;
+
+ /* Deduct the size of the reference type bytes at the end of the block. */
+
+ modcount -= attribute_size (AT_user_def_type);
+
+ /* Now do the actual decoding */
+
+ typep = decode_modified_type (typedata, modcount, AT_mod_u_d_type);
+ return (typep);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ decode_modified_type -- decode modified user or fundamental type
+
+SYNOPSIS
+
+ static struct type *decode_modified_type (char *modifiers,
+ unsigned short modcount, int mtype)
+
+DESCRIPTION
+
+ Decode a modified type, either a modified fundamental type or
+ a modified user defined type. MODIFIERS is a pointer to the
+ block of bytes that define MODCOUNT modifiers. Immediately
+ following the last modifier is a short containing the fundamental
+ type or a long containing the reference to the user defined
+ type. Which one is determined by MTYPE, which is either
+ AT_mod_fund_type or AT_mod_u_d_type to indicate what modified
+ type we are generating.
+
+ We call ourself recursively to generate each modified type,`
+ until MODCOUNT reaches zero, at which point we have consumed
+ all the modifiers and generate either the fundamental type or
+ user defined type. When the recursion unwinds, each modifier
+ is applied in turn to generate the full modified type.
+
+NOTES
+
+ If we find a modifier that we don't recognize, and it is not one
+ of those reserved for application specific use, then we issue a
+ warning and simply ignore the modifier.
+
+BUGS
+
+ We currently ignore MOD_const and MOD_volatile. (FIXME)
+
+ */
+
+static struct type *
+decode_modified_type (modifiers, modcount, mtype)
+ char *modifiers;
+ unsigned int modcount;
+ int mtype;
+{
+ struct type *typep = NULL;
+ unsigned short fundtype;
+ DIE_REF die_ref;
+ char modifier;
+ int nbytes;
+
+ if (modcount == 0)
+ {
+ switch (mtype)
+ {
+ case AT_mod_fund_type:
+ nbytes = attribute_size (AT_fund_type);
+ fundtype = target_to_host (modifiers, nbytes, GET_UNSIGNED,
+ current_objfile);
+ typep = decode_fund_type (fundtype);
+ break;
+ case AT_mod_u_d_type:
+ nbytes = attribute_size (AT_user_def_type);
+ die_ref = target_to_host (modifiers, nbytes, GET_UNSIGNED,
+ current_objfile);
+ if ((typep = lookup_utype (die_ref)) == NULL)
+ {
+ typep = alloc_utype (die_ref, NULL);
+ }
+ break;
+ default:
+ complain (&botched_modified_type, DIE_ID, DIE_NAME, mtype);
+ typep = dwarf_fundamental_type (current_objfile, FT_INTEGER);
+ break;
+ }
+ }
+ else
+ {
+ modifier = *modifiers++;
+ typep = decode_modified_type (modifiers, --modcount, mtype);
+ switch (modifier)
+ {
+ case MOD_pointer_to:
+ typep = lookup_pointer_type (typep);
+ break;
+ case MOD_reference_to:
+ typep = lookup_reference_type (typep);
+ break;
+ case MOD_const:
+ complain (&const_ignored, DIE_ID, DIE_NAME); /* FIXME */
+ break;
+ case MOD_volatile:
+ complain (&volatile_ignored, DIE_ID, DIE_NAME); /* FIXME */
+ break;
+ default:
+ if (!(MOD_lo_user <= (unsigned char) modifier
+ && (unsigned char) modifier <= MOD_hi_user))
+ {
+ complain (&unknown_type_modifier, DIE_ID, DIE_NAME, modifier);
+ }
+ break;
+ }
+ }
+ return (typep);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ decode_fund_type -- translate basic DWARF type to gdb base type
+
+DESCRIPTION
+
+ Given an integer that is one of the fundamental DWARF types,
+ translate it to one of the basic internal gdb types and return
+ a pointer to the appropriate gdb type (a "struct type *").
+
+NOTES
+
+ For robustness, if we are asked to translate a fundamental
+ type that we are unprepared to deal with, we return int so
+ callers can always depend upon a valid type being returned,
+ and so gdb may at least do something reasonable by default.
+ If the type is not in the range of those types defined as
+ application specific types, we also issue a warning.
+*/
+
+static struct type *
+decode_fund_type (fundtype)
+ unsigned int fundtype;
+{
+ struct type *typep = NULL;
+
+ switch (fundtype)
+ {
+
+ case FT_void:
+ typep = dwarf_fundamental_type (current_objfile, FT_VOID);
+ break;
+
+ case FT_boolean: /* Was FT_set in AT&T version */
+ typep = dwarf_fundamental_type (current_objfile, FT_BOOLEAN);
+ break;
+
+ case FT_pointer: /* (void *) */
+ typep = dwarf_fundamental_type (current_objfile, FT_VOID);
+ typep = lookup_pointer_type (typep);
+ break;
+
+ case FT_char:
+ typep = dwarf_fundamental_type (current_objfile, FT_CHAR);
+ break;
+
+ case FT_signed_char:
+ typep = dwarf_fundamental_type (current_objfile, FT_SIGNED_CHAR);
+ break;
+
+ case FT_unsigned_char:
+ typep = dwarf_fundamental_type (current_objfile, FT_UNSIGNED_CHAR);
+ break;
+
+ case FT_short:
+ typep = dwarf_fundamental_type (current_objfile, FT_SHORT);
+ break;
+
+ case FT_signed_short:
+ typep = dwarf_fundamental_type (current_objfile, FT_SIGNED_SHORT);
+ break;
+
+ case FT_unsigned_short:
+ typep = dwarf_fundamental_type (current_objfile, FT_UNSIGNED_SHORT);
+ break;
+
+ case FT_integer:
+ typep = dwarf_fundamental_type (current_objfile, FT_INTEGER);
+ break;
+
+ case FT_signed_integer:
+ typep = dwarf_fundamental_type (current_objfile, FT_SIGNED_INTEGER);
+ break;
+
+ case FT_unsigned_integer:
+ typep = dwarf_fundamental_type (current_objfile, FT_UNSIGNED_INTEGER);
+ break;
+
+ case FT_long:
+ typep = dwarf_fundamental_type (current_objfile, FT_LONG);
+ break;
+
+ case FT_signed_long:
+ typep = dwarf_fundamental_type (current_objfile, FT_SIGNED_LONG);
+ break;
+
+ case FT_unsigned_long:
+ typep = dwarf_fundamental_type (current_objfile, FT_UNSIGNED_LONG);
+ break;
+
+ case FT_long_long:
+ typep = dwarf_fundamental_type (current_objfile, FT_LONG_LONG);
+ break;
+
+ case FT_signed_long_long:
+ typep = dwarf_fundamental_type (current_objfile, FT_SIGNED_LONG_LONG);
+ break;
+
+ case FT_unsigned_long_long:
+ typep = dwarf_fundamental_type (current_objfile, FT_UNSIGNED_LONG_LONG);
+ break;
+
+ case FT_float:
+ typep = dwarf_fundamental_type (current_objfile, FT_FLOAT);
+ break;
+
+ case FT_dbl_prec_float:
+ typep = dwarf_fundamental_type (current_objfile, FT_DBL_PREC_FLOAT);
+ break;
+
+ case FT_ext_prec_float:
+ typep = dwarf_fundamental_type (current_objfile, FT_EXT_PREC_FLOAT);
+ break;
+
+ case FT_complex:
+ typep = dwarf_fundamental_type (current_objfile, FT_COMPLEX);
+ break;
+
+ case FT_dbl_prec_complex:
+ typep = dwarf_fundamental_type (current_objfile, FT_DBL_PREC_COMPLEX);
+ break;
+
+ case FT_ext_prec_complex:
+ typep = dwarf_fundamental_type (current_objfile, FT_EXT_PREC_COMPLEX);
+ break;
+
+ }
+
+ if (typep == NULL)
+ {
+ typep = dwarf_fundamental_type (current_objfile, FT_INTEGER);
+ if (!(FT_lo_user <= fundtype && fundtype <= FT_hi_user))
+ {
+ complain (&unexpected_fund_type, DIE_ID, DIE_NAME, fundtype);
+ }
+ }
+
+ return (typep);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ create_name -- allocate a fresh copy of a string on an obstack
+
+DESCRIPTION
+
+ Given a pointer to a string and a pointer to an obstack, allocates
+ a fresh copy of the string on the specified obstack.
+
+*/
+
+static char *
+create_name (name, obstackp)
+ char *name;
+ struct obstack *obstackp;
+{
+ int length;
+ char *newname;
+
+ length = strlen (name) + 1;
+ newname = (char *) obstack_alloc (obstackp, length);
+ strcpy (newname, name);
+ return (newname);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ basicdieinfo -- extract the minimal die info from raw die data
+
+SYNOPSIS
+
+ void basicdieinfo (char *diep, struct dieinfo *dip,
+ struct objfile *objfile)
+
+DESCRIPTION
+
+ Given a pointer to raw DIE data, and a pointer to an instance of a
+ die info structure, this function extracts the basic information
+ from the DIE data required to continue processing this DIE, along
+ with some bookkeeping information about the DIE.
+
+ The information we absolutely must have includes the DIE tag,
+ and the DIE length. If we need the sibling reference, then we
+ will have to call completedieinfo() to process all the remaining
+ DIE information.
+
+ Note that since there is no guarantee that the data is properly
+ aligned in memory for the type of access required (indirection
+ through anything other than a char pointer), and there is no
+ guarantee that it is in the same byte order as the gdb host,
+ we call a function which deals with both alignment and byte
+ swapping issues. Possibly inefficient, but quite portable.
+
+ We also take care of some other basic things at this point, such
+ as ensuring that the instance of the die info structure starts
+ out completely zero'd and that curdie is initialized for use
+ in error reporting if we have a problem with the current die.
+
+NOTES
+
+ All DIE's must have at least a valid length, thus the minimum
+ DIE size is SIZEOF_DIE_LENGTH. In order to have a valid tag, the
+ DIE size must be at least SIZEOF_DIE_TAG larger, otherwise they
+ are forced to be TAG_padding DIES.
+
+ Padding DIES must be at least SIZEOF_DIE_LENGTH in length, implying
+ that if a padding DIE is used for alignment and the amount needed is
+ less than SIZEOF_DIE_LENGTH, then the padding DIE has to be big
+ enough to align to the next alignment boundry.
+
+ We do some basic sanity checking here, such as verifying that the
+ length of the die would not cause it to overrun the recorded end of
+ the buffer holding the DIE info. If we find a DIE that is either
+ too small or too large, we force it's length to zero which should
+ cause the caller to take appropriate action.
+ */
+
+static void
+basicdieinfo (dip, diep, objfile)
+ struct dieinfo *dip;
+ char *diep;
+ struct objfile *objfile;
+{
+ curdie = dip;
+ memset (dip, 0, sizeof (struct dieinfo));
+ dip -> die = diep;
+ dip -> die_ref = dbroff + (diep - dbbase);
+ dip -> die_length = target_to_host (diep, SIZEOF_DIE_LENGTH, GET_UNSIGNED,
+ objfile);
+ if ((dip -> die_length < SIZEOF_DIE_LENGTH) ||
+ ((diep + dip -> die_length) > (dbbase + dbsize)))
+ {
+ complain (&malformed_die, DIE_ID, DIE_NAME, dip -> die_length);
+ dip -> die_length = 0;
+ }
+ else if (dip -> die_length < (SIZEOF_DIE_LENGTH + SIZEOF_DIE_TAG))
+ {
+ dip -> die_tag = TAG_padding;
+ }
+ else
+ {
+ diep += SIZEOF_DIE_LENGTH;
+ dip -> die_tag = target_to_host (diep, SIZEOF_DIE_TAG, GET_UNSIGNED,
+ objfile);
+ }
+}
+
+/*
+
+LOCAL FUNCTION
+
+ completedieinfo -- finish reading the information for a given DIE
+
+SYNOPSIS
+
+ void completedieinfo (struct dieinfo *dip, struct objfile *objfile)
+
+DESCRIPTION
+
+ Given a pointer to an already partially initialized die info structure,
+ scan the raw DIE data and finish filling in the die info structure
+ from the various attributes found.
+
+ Note that since there is no guarantee that the data is properly
+ aligned in memory for the type of access required (indirection
+ through anything other than a char pointer), and there is no
+ guarantee that it is in the same byte order as the gdb host,
+ we call a function which deals with both alignment and byte
+ swapping issues. Possibly inefficient, but quite portable.
+
+NOTES
+
+ Each time we are called, we increment the diecount variable, which
+ keeps an approximate count of the number of dies processed for
+ each compilation unit. This information is presented to the user
+ if the info_verbose flag is set.
+
+ */
+
+static void
+completedieinfo (dip, objfile)
+ struct dieinfo *dip;
+ struct objfile *objfile;
+{
+ char *diep; /* Current pointer into raw DIE data */
+ char *end; /* Terminate DIE scan here */
+ unsigned short attr; /* Current attribute being scanned */
+ unsigned short form; /* Form of the attribute */
+ int nbytes; /* Size of next field to read */
+
+ diecount++;
+ diep = dip -> die;
+ end = diep + dip -> die_length;
+ diep += SIZEOF_DIE_LENGTH + SIZEOF_DIE_TAG;
+ while (diep < end)
+ {
+ attr = target_to_host (diep, SIZEOF_ATTRIBUTE, GET_UNSIGNED, objfile);
+ diep += SIZEOF_ATTRIBUTE;
+ if ((nbytes = attribute_size (attr)) == -1)
+ {
+ complain (&unknown_attribute_length, DIE_ID, DIE_NAME);
+ diep = end;
+ continue;
+ }
+ switch (attr)
+ {
+ case AT_fund_type:
+ dip -> at_fund_type = target_to_host (diep, nbytes, GET_UNSIGNED,
+ objfile);
+ break;
+ case AT_ordering:
+ dip -> at_ordering = target_to_host (diep, nbytes, GET_UNSIGNED,
+ objfile);
+ break;
+ case AT_bit_offset:
+ dip -> at_bit_offset = target_to_host (diep, nbytes, GET_UNSIGNED,
+ objfile);
+ break;
+ case AT_sibling:
+ dip -> at_sibling = target_to_host (diep, nbytes, GET_UNSIGNED,
+ objfile);
+ break;
+ case AT_stmt_list:
+ dip -> at_stmt_list = target_to_host (diep, nbytes, GET_UNSIGNED,
+ objfile);
+ dip -> has_at_stmt_list = 1;
+ break;
+ case AT_low_pc:
+ dip -> at_low_pc = target_to_host (diep, nbytes, GET_UNSIGNED,
+ objfile);
+ dip -> at_low_pc += baseaddr;
+ dip -> has_at_low_pc = 1;
+ break;
+ case AT_high_pc:
+ dip -> at_high_pc = target_to_host (diep, nbytes, GET_UNSIGNED,
+ objfile);
+ dip -> at_high_pc += baseaddr;
+ break;
+ case AT_language:
+ dip -> at_language = target_to_host (diep, nbytes, GET_UNSIGNED,
+ objfile);
+ break;
+ case AT_user_def_type:
+ dip -> at_user_def_type = target_to_host (diep, nbytes,
+ GET_UNSIGNED, objfile);
+ break;
+ case AT_byte_size:
+ dip -> at_byte_size = target_to_host (diep, nbytes, GET_UNSIGNED,
+ objfile);
+ dip -> has_at_byte_size = 1;
+ break;
+ case AT_bit_size:
+ dip -> at_bit_size = target_to_host (diep, nbytes, GET_UNSIGNED,
+ objfile);
+ break;
+ case AT_member:
+ dip -> at_member = target_to_host (diep, nbytes, GET_UNSIGNED,
+ objfile);
+ break;
+ case AT_discr:
+ dip -> at_discr = target_to_host (diep, nbytes, GET_UNSIGNED,
+ objfile);
+ break;
+ case AT_location:
+ dip -> at_location = diep;
+ break;
+ case AT_mod_fund_type:
+ dip -> at_mod_fund_type = diep;
+ break;
+ case AT_subscr_data:
+ dip -> at_subscr_data = diep;
+ break;
+ case AT_mod_u_d_type:
+ dip -> at_mod_u_d_type = diep;
+ break;
+ case AT_element_list:
+ dip -> at_element_list = diep;
+ dip -> short_element_list = 0;
+ break;
+ case AT_short_element_list:
+ dip -> at_element_list = diep;
+ dip -> short_element_list = 1;
+ break;
+ case AT_discr_value:
+ dip -> at_discr_value = diep;
+ break;
+ case AT_string_length:
+ dip -> at_string_length = diep;
+ break;
+ case AT_name:
+ dip -> at_name = diep;
+ break;
+ case AT_comp_dir:
+ /* For now, ignore any "hostname:" portion, since gdb doesn't
+ know how to deal with it. (FIXME). */
+ dip -> at_comp_dir = strrchr (diep, ':');
+ if (dip -> at_comp_dir != NULL)
+ {
+ dip -> at_comp_dir++;
+ }
+ else
+ {
+ dip -> at_comp_dir = diep;
+ }
+ break;
+ case AT_producer:
+ dip -> at_producer = diep;
+ break;
+ case AT_start_scope:
+ dip -> at_start_scope = target_to_host (diep, nbytes, GET_UNSIGNED,
+ objfile);
+ break;
+ case AT_stride_size:
+ dip -> at_stride_size = target_to_host (diep, nbytes, GET_UNSIGNED,
+ objfile);
+ break;
+ case AT_src_info:
+ dip -> at_src_info = target_to_host (diep, nbytes, GET_UNSIGNED,
+ objfile);
+ break;
+ case AT_prototyped:
+ dip -> at_prototyped = diep;
+ break;
+ default:
+ /* Found an attribute that we are unprepared to handle. However
+ it is specifically one of the design goals of DWARF that
+ consumers should ignore unknown attributes. As long as the
+ form is one that we recognize (so we know how to skip it),
+ we can just ignore the unknown attribute. */
+ break;
+ }
+ form = FORM_FROM_ATTR (attr);
+ switch (form)
+ {
+ case FORM_DATA2:
+ diep += 2;
+ break;
+ case FORM_DATA4:
+ case FORM_REF:
+ diep += 4;
+ break;
+ case FORM_DATA8:
+ diep += 8;
+ break;
+ case FORM_ADDR:
+ diep += TARGET_FT_POINTER_SIZE (objfile);
+ break;
+ case FORM_BLOCK2:
+ diep += 2 + target_to_host (diep, nbytes, GET_UNSIGNED, objfile);
+ break;
+ case FORM_BLOCK4:
+ diep += 4 + target_to_host (diep, nbytes, GET_UNSIGNED, objfile);
+ break;
+ case FORM_STRING:
+ diep += strlen (diep) + 1;
+ break;
+ default:
+ complain (&unknown_attribute_form, DIE_ID, DIE_NAME, form);
+ diep = end;
+ break;
+ }
+ }
+}
+
+/*
+
+LOCAL FUNCTION
+
+ target_to_host -- swap in target data to host
+
+SYNOPSIS
+
+ target_to_host (char *from, int nbytes, int signextend,
+ struct objfile *objfile)
+
+DESCRIPTION
+
+ Given pointer to data in target format in FROM, a byte count for
+ the size of the data in NBYTES, a flag indicating whether or not
+ the data is signed in SIGNEXTEND, and a pointer to the current
+ objfile in OBJFILE, convert the data to host format and return
+ the converted value.
+
+NOTES
+
+ FIXME: If we read data that is known to be signed, and expect to
+ use it as signed data, then we need to explicitly sign extend the
+ result until the bfd library is able to do this for us.
+
+ */
+
+static unsigned long
+target_to_host (from, nbytes, signextend, objfile)
+ char *from;
+ int nbytes;
+ int signextend; /* FIXME: Unused */
+ struct objfile *objfile;
+{
+ unsigned long rtnval;
+
+ switch (nbytes)
+ {
+ case 8:
+ rtnval = bfd_get_64 (objfile -> obfd, (bfd_byte *) from);
+ break;
+ case 4:
+ rtnval = bfd_get_32 (objfile -> obfd, (bfd_byte *) from);
+ break;
+ case 2:
+ rtnval = bfd_get_16 (objfile -> obfd, (bfd_byte *) from);
+ break;
+ case 1:
+ rtnval = bfd_get_8 (objfile -> obfd, (bfd_byte *) from);
+ break;
+ default:
+ complain (&no_bfd_get_N, DIE_ID, DIE_NAME, nbytes);
+ rtnval = 0;
+ break;
+ }
+ return (rtnval);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ attribute_size -- compute size of data for a DWARF attribute
+
+SYNOPSIS
+
+ static int attribute_size (unsigned int attr)
+
+DESCRIPTION
+
+ Given a DWARF attribute in ATTR, compute the size of the first
+ piece of data associated with this attribute and return that
+ size.
+
+ Returns -1 for unrecognized attributes.
+
+ */
+
+static int
+attribute_size (attr)
+ unsigned int attr;
+{
+ int nbytes; /* Size of next data for this attribute */
+ unsigned short form; /* Form of the attribute */
+
+ form = FORM_FROM_ATTR (attr);
+ switch (form)
+ {
+ case FORM_STRING: /* A variable length field is next */
+ nbytes = 0;
+ break;
+ case FORM_DATA2: /* Next 2 byte field is the data itself */
+ case FORM_BLOCK2: /* Next 2 byte field is a block length */
+ nbytes = 2;
+ break;
+ case FORM_DATA4: /* Next 4 byte field is the data itself */
+ case FORM_BLOCK4: /* Next 4 byte field is a block length */
+ case FORM_REF: /* Next 4 byte field is a DIE offset */
+ nbytes = 4;
+ break;
+ case FORM_DATA8: /* Next 8 byte field is the data itself */
+ nbytes = 8;
+ break;
+ case FORM_ADDR: /* Next field size is target sizeof(void *) */
+ nbytes = TARGET_FT_POINTER_SIZE (objfile);
+ break;
+ default:
+ complain (&unknown_attribute_form, DIE_ID, DIE_NAME, form);
+ nbytes = -1;
+ break;
+ }
+ return (nbytes);
+}
diff --git a/gnu/usr.bin/gdb/gdb/elf/common.h b/gnu/usr.bin/gdb/gdb/elf/common.h
new file mode 100644
index 0000000..cd708f1
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/elf/common.h
@@ -0,0 +1,216 @@
+/* ELF support for BFD.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ Written by Fred Fish @ Cygnus Support, from information published
+ in "UNIX System V Release 4, Programmers Guide: ANSI C and
+ Programming Support Tools".
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file is part of ELF support for BFD, and contains the portions
+ that are common to both the internal and external representations.
+ For example, ELFMAG0 is the byte 0x7F in both the internal (in-memory)
+ and external (in-file) representations. */
+
+
+/* Fields in e_ident[] */
+
+#define EI_MAG0 0 /* File identification byte 0 index */
+#define ELFMAG0 0x7F /* Magic number byte 0 */
+
+#define EI_MAG1 1 /* File identification byte 1 index */
+#define ELFMAG1 'E' /* Magic number byte 1 */
+
+#define EI_MAG2 2 /* File identification byte 2 index */
+#define ELFMAG2 'L' /* Magic number byte 2 */
+
+#define EI_MAG3 3 /* File identification byte 3 index */
+#define ELFMAG3 'F' /* Magic number byte 3 */
+
+#define EI_CLASS 4 /* File class */
+#define ELFCLASSNONE 0 /* Invalid class */
+#define ELFCLASS32 1 /* 32-bit objects */
+#define ELFCLASS64 2 /* 64-bit objects */
+
+#define EI_DATA 5 /* Data encoding */
+#define ELFDATANONE 0 /* Invalid data encoding */
+#define ELFDATA2LSB 1 /* 2's complement, little endian */
+#define ELFDATA2MSB 2 /* 2's complement, big endian */
+
+#define EI_VERSION 6 /* File version */
+
+#define EI_PAD 7 /* Start of padding bytes */
+
+
+/* Values for e_type, which identifies the object file type */
+
+#define ET_NONE 0 /* No file type */
+#define ET_REL 1 /* Relocatable file */
+#define ET_EXEC 2 /* Executable file */
+#define ET_DYN 3 /* Shared object file */
+#define ET_CORE 4 /* Core file */
+#define ET_LOPROC 0xFF00 /* Processor-specific */
+#define ET_HIPROC 0xFFFF /* Processor-specific */
+
+/* Values for e_machine, which identifies the architecture */
+
+#define EM_NONE 0 /* No machine */
+#define EM_M32 1 /* AT&T WE 32100 */
+#define EM_SPARC 2 /* SUN SPARC */
+#define EM_386 3 /* Intel 80386 */
+#define EM_68K 4 /* Motorola m68k family */
+#define EM_88K 5 /* Motorola m88k family */
+#define EM_860 7 /* Intel 80860 */
+#define EM_MIPS 8 /* MIPS R3000 */
+#define EM_HPPA 9 /* HP PA-RISC */
+
+
+/* Values for e_version */
+
+#define EV_NONE 0 /* Invalid ELF version */
+#define EV_CURRENT 1 /* Current version */
+
+/* Values for program header, p_type field */
+
+#define PT_NULL 0 /* Program header table entry unused */
+#define PT_LOAD 1 /* Loadable program segment */
+#define PT_DYNAMIC 2 /* Dynamic linking information */
+#define PT_INTERP 3 /* Program interpreter */
+#define PT_NOTE 4 /* Auxiliary information */
+#define PT_SHLIB 5 /* Reserved, unspecified semantics */
+#define PT_PHDR 6 /* Entry for header table itself */
+#define PT_LOPROC 0x70000000 /* Processor-specific */
+#define PT_HIPROC 0x7FFFFFFF /* Processor-specific */
+
+/* Program segment permissions, in program header p_flags field */
+
+#define PF_X (1 << 0) /* Segment is executable */
+#define PF_W (1 << 1) /* Segment is writable */
+#define PF_R (1 << 2) /* Segment is readable */
+#define PF_MASKPROC 0xF0000000 /* Processor-specific reserved bits */
+
+/* Values for section header, sh_type field */
+
+#define SHT_NULL 0 /* Section header table entry unused */
+#define SHT_PROGBITS 1 /* Program specific (private) data */
+#define SHT_SYMTAB 2 /* Link editing symbol table */
+#define SHT_STRTAB 3 /* A string table */
+#define SHT_RELA 4 /* Relocation entries with addends */
+#define SHT_HASH 5 /* A symbol hash table */
+#define SHT_DYNAMIC 6 /* Information for dynamic linking */
+#define SHT_NOTE 7 /* Information that marks file */
+#define SHT_NOBITS 8 /* Section occupies no space in file */
+#define SHT_REL 9 /* Relocation entries, no addends */
+#define SHT_SHLIB 10 /* Reserved, unspecified semantics */
+#define SHT_DYNSYM 11 /* Dynamic linking symbol table */
+#define SHT_LOPROC 0x70000000 /* Processor-specific semantics, lo */
+#define SHT_HIPROC 0x7FFFFFFF /* Processor-specific semantics, hi */
+#define SHT_LOUSER 0x80000000 /* Application-specific semantics */
+#define SHT_HIUSER 0x8FFFFFFF /* Application-specific semantics */
+
+/* Values for section header, sh_flags field */
+
+#define SHF_WRITE (1 << 0) /* Writable data during execution */
+#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */
+#define SHF_EXECINSTR (1 << 2) /* Executable machine instructions */
+#define SHF_MASKPROC 0xF0000000 /* Processor-specific semantics */
+
+/* Values of note segment descriptor types for core files. */
+
+#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */
+#define NT_FPREGSET 2 /* Contains copy of fpregset struct */
+#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */
+
+/* Values of note segment descriptor types for object files. */
+/* (Only for hppa right now. Should this be moved elsewhere?) */
+
+#define NT_VERSION 1 /* Contains a version string. */
+
+/* These three macros disassemble and assemble a symbol table st_info field,
+ which contains the symbol binding and symbol type. The STB_ and STT_
+ defines identify the binding and type. */
+
+#define ELF_ST_BIND(val) (((unsigned int)(val)) >> 4)
+#define ELF_ST_TYPE(val) ((val) & 0xF)
+#define ELF_ST_INFO(bind,type) (((bind) << 4) + ((type) & 0xF))
+
+#define STN_UNDEF 0 /* undefined symbol index */
+
+#define STB_LOCAL 0 /* Symbol not visible outside obj */
+#define STB_GLOBAL 1 /* Symbol visible outside obj */
+#define STB_WEAK 2 /* Like globals, lower precedence */
+#define STB_LOPROC 13 /* Application-specific semantics */
+#define STB_HIPROC 15 /* Application-specific semantics */
+
+#define STT_NOTYPE 0 /* Symbol type is unspecified */
+#define STT_OBJECT 1 /* Symbol is a data object */
+#define STT_FUNC 2 /* Symbol is a code object */
+#define STT_SECTION 3 /* Symbol associated with a section */
+#define STT_FILE 4 /* Symbol gives a file name */
+#define STT_LOPROC 13 /* Application-specific semantics */
+#define STT_HIPROC 15 /* Application-specific semantics */
+
+/* Special section indices, which may show up in st_shndx fields, among
+ other places. */
+
+#define SHN_UNDEF 0 /* Undefined section reference */
+#define SHN_LORESERV 0xFF00 /* Begin range of reserved indices */
+#define SHN_LOPROC 0xFF00 /* Begin range of appl-specific */
+#define SHN_HIPROC 0xFF1F /* End range of appl-specific */
+#define SHN_ABS 0xFFF1 /* Associated symbol is absolute */
+#define SHN_COMMON 0xFFF2 /* Associated symbol is in common */
+#define SHN_HIRESERVE 0xFFFF /* End range of reserved indices */
+
+/* relocation info handling macros */
+
+#define ELF32_R_SYM(i) ((i)>>8)
+#define ELF32_R_TYPE(i) ((unsigned char)(i))
+#define ELF32_R_INFO(s,t) (((s)<<8)+(unsigned char)(t))
+
+#define ELF64_R_SYM(i) ((i)>>32)
+#define ELF64_R_TYPE(i) ((Elf64_Word)(i))
+#define ELF64_R_INFO(s,t) (((Elf64_Xword)(s)<<32)+(Elf64_Xword)(t))
+
+/* Dynamic section tags */
+
+#define DT_NULL 0
+#define DT_NEEDED 1
+#define DT_PLTRELSZ 2
+#define DT_PLTGOT 3
+#define DT_HASH 4
+#define DT_STRTAB 5
+#define DT_SYMTAB 6
+#define DT_RELA 7
+#define DT_RELASZ 8
+#define DT_RELAENT 9
+#define DT_STRSZ 10
+#define DT_SYMENT 11
+#define DT_INIT 12
+#define DT_FINI 13
+#define DT_SONAME 14
+#define DT_RPATH 15
+#define DT_SYMBOLIC 16
+#define DT_REL 17
+#define DT_RELSZ 18
+#define DT_RELENT 19
+#define DT_PLTREL 20
+#define DT_DEBUG 21
+#define DT_TEXTREL 22
+#define DT_JMPREL 23
+#define DT_LOPROC 0x70000000
+#define DT_HIPROC 0x7fffffff
diff --git a/gnu/usr.bin/gdb/gdb/elf/dwarf.h b/gnu/usr.bin/gdb/gdb/elf/dwarf.h
new file mode 100644
index 0000000..bc9723ae
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/elf/dwarf.h
@@ -0,0 +1,314 @@
+/* Declarations and definitions of codes relating to the DWARF symbolic
+ debugging information format.
+
+ Written by Ron Guilmette (rfg@ncd.com)
+
+Copyright (C) 1992 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This file is derived from the DWARF specification (a public document)
+ Revision 1.0.1 (April 8, 1992) developed by the UNIX International
+ Programming Languages Special Interest Group (UI/PLSIG) and distributed
+ by UNIX International. Copies of this specification are available from
+ UNIX International, 20 Waterview Boulevard, Parsippany, NJ, 07054.
+*/
+
+/* Tag names and codes. */
+
+enum dwarf_tag {
+ TAG_padding = 0x0000,
+ TAG_array_type = 0x0001,
+ TAG_class_type = 0x0002,
+ TAG_entry_point = 0x0003,
+ TAG_enumeration_type = 0x0004,
+ TAG_formal_parameter = 0x0005,
+ TAG_global_subroutine = 0x0006,
+ TAG_global_variable = 0x0007,
+ /* 0x0008 -- reserved */
+ /* 0x0009 -- reserved */
+ TAG_label = 0x000a,
+ TAG_lexical_block = 0x000b,
+ TAG_local_variable = 0x000c,
+ TAG_member = 0x000d,
+ /* 0x000e -- reserved */
+ TAG_pointer_type = 0x000f,
+ TAG_reference_type = 0x0010,
+ TAG_compile_unit = 0x0011,
+ TAG_string_type = 0x0012,
+ TAG_structure_type = 0x0013,
+ TAG_subroutine = 0x0014,
+ TAG_subroutine_type = 0x0015,
+ TAG_typedef = 0x0016,
+ TAG_union_type = 0x0017,
+ TAG_unspecified_parameters = 0x0018,
+ TAG_variant = 0x0019,
+ TAG_common_block = 0x001a,
+ TAG_common_inclusion = 0x001b,
+ TAG_inheritance = 0x001c,
+ TAG_inlined_subroutine = 0x001d,
+ TAG_module = 0x001e,
+ TAG_ptr_to_member_type = 0x001f,
+ TAG_set_type = 0x0020,
+ TAG_subrange_type = 0x0021,
+ TAG_with_stmt = 0x0022,
+
+ /* GNU extensions */
+
+ TAG_format_label = 0x8000, /* for FORTRAN 77 and Fortran 90 */
+ TAG_namelist = 0x8001, /* For Fortran 90 */
+ TAG_function_template = 0x8002, /* for C++ */
+ TAG_class_template = 0x8003 /* for C++ */
+};
+
+#define TAG_lo_user 0x8000 /* implementation-defined range start */
+#define TAG_hi_user 0xffff /* implementation-defined range end */
+#define TAG_source_file TAG_compile_unit /* for backward compatibility */
+
+/* Form names and codes. */
+
+enum dwarf_form {
+ FORM_ADDR = 0x1,
+ FORM_REF = 0x2,
+ FORM_BLOCK2 = 0x3,
+ FORM_BLOCK4 = 0x4,
+ FORM_DATA2 = 0x5,
+ FORM_DATA4 = 0x6,
+ FORM_DATA8 = 0x7,
+ FORM_STRING = 0x8
+};
+
+/* Attribute names and codes. */
+
+enum dwarf_attribute {
+ AT_sibling = (0x0010|FORM_REF),
+ AT_location = (0x0020|FORM_BLOCK2),
+ AT_name = (0x0030|FORM_STRING),
+ AT_fund_type = (0x0050|FORM_DATA2),
+ AT_mod_fund_type = (0x0060|FORM_BLOCK2),
+ AT_user_def_type = (0x0070|FORM_REF),
+ AT_mod_u_d_type = (0x0080|FORM_BLOCK2),
+ AT_ordering = (0x0090|FORM_DATA2),
+ AT_subscr_data = (0x00a0|FORM_BLOCK2),
+ AT_byte_size = (0x00b0|FORM_DATA4),
+ AT_bit_offset = (0x00c0|FORM_DATA2),
+ AT_bit_size = (0x00d0|FORM_DATA4),
+ /* (0x00e0|FORM_xxxx) -- reserved */
+ AT_element_list = (0x00f0|FORM_BLOCK4),
+ AT_stmt_list = (0x0100|FORM_DATA4),
+ AT_low_pc = (0x0110|FORM_ADDR),
+ AT_high_pc = (0x0120|FORM_ADDR),
+ AT_language = (0x0130|FORM_DATA4),
+ AT_member = (0x0140|FORM_REF),
+ AT_discr = (0x0150|FORM_REF),
+ AT_discr_value = (0x0160|FORM_BLOCK2),
+ /* (0x0170|FORM_xxxx) -- reserved */
+ /* (0x0180|FORM_xxxx) -- reserved */
+ AT_string_length = (0x0190|FORM_BLOCK2),
+ AT_common_reference = (0x01a0|FORM_REF),
+ AT_comp_dir = (0x01b0|FORM_STRING),
+ AT_const_value_string = (0x01c0|FORM_STRING),
+ AT_const_value_data2 = (0x01c0|FORM_DATA2),
+ AT_const_value_data4 = (0x01c0|FORM_DATA4),
+ AT_const_value_data8 = (0x01c0|FORM_DATA8),
+ AT_const_value_block2 = (0x01c0|FORM_BLOCK2),
+ AT_const_value_block4 = (0x01c0|FORM_BLOCK4),
+ AT_containing_type = (0x01d0|FORM_REF),
+ AT_default_value_addr = (0x01e0|FORM_ADDR),
+ AT_default_value_data2 = (0x01e0|FORM_DATA2),
+ AT_default_value_data4 = (0x01e0|FORM_DATA4),
+ AT_default_value_data8 = (0x01e0|FORM_DATA8),
+ AT_default_value_string = (0x01e0|FORM_STRING),
+ AT_friends = (0x01f0|FORM_BLOCK2),
+ AT_inline = (0x0200|FORM_STRING),
+ AT_is_optional = (0x0210|FORM_STRING),
+ AT_lower_bound_ref = (0x0220|FORM_REF),
+ AT_lower_bound_data2 = (0x0220|FORM_DATA2),
+ AT_lower_bound_data4 = (0x0220|FORM_DATA4),
+ AT_lower_bound_data8 = (0x0220|FORM_DATA8),
+ AT_private = (0x0240|FORM_STRING),
+ AT_producer = (0x0250|FORM_STRING),
+ AT_program = (0x0230|FORM_STRING),
+ AT_protected = (0x0260|FORM_STRING),
+ AT_prototyped = (0x0270|FORM_STRING),
+ AT_public = (0x0280|FORM_STRING),
+ AT_pure_virtual = (0x0290|FORM_STRING),
+ AT_return_addr = (0x02a0|FORM_BLOCK2),
+ AT_abstract_origin = (0x02b0|FORM_REF),
+ AT_start_scope = (0x02c0|FORM_DATA4),
+ AT_stride_size = (0x02e0|FORM_DATA4),
+ AT_upper_bound_ref = (0x02f0|FORM_REF),
+ AT_upper_bound_data2 = (0x02f0|FORM_DATA2),
+ AT_upper_bound_data4 = (0x02f0|FORM_DATA4),
+ AT_upper_bound_data8 = (0x02f0|FORM_DATA8),
+ AT_virtual = (0x0300|FORM_STRING),
+
+ /* GNU extensions. */
+
+ AT_sf_names = (0x8000|FORM_DATA4),
+ AT_src_info = (0x8010|FORM_DATA4),
+ AT_mac_info = (0x8020|FORM_DATA4),
+ AT_src_coords = (0x8030|FORM_DATA4),
+ AT_body_begin = (0x8040|FORM_ADDR),
+ AT_body_end = (0x8050|FORM_ADDR)
+};
+
+#define AT_lo_user 0x8000 /* implementation-defined range start */
+#define AT_hi_user 0xffff /* implementation-defined range end */
+
+/* Location atom names and codes. */
+
+enum dwarf_location_atom {
+ OP_REG = 0x01,
+ OP_BASEREG = 0x02,
+ OP_ADDR = 0x03,
+ OP_CONST = 0x04,
+ OP_DEREF2 = 0x05,
+ OP_DEREF4 = 0x06,
+ OP_ADD = 0x07
+};
+
+#define OP_LO_USER 0x80 /* implementation-defined range start */
+#define OP_HI_USER 0xff /* implementation-defined range end */
+
+/* Fundamental type names and codes. */
+
+enum dwarf_fundamental_type {
+ FT_char = 0x0001,
+ FT_signed_char = 0x0002,
+ FT_unsigned_char = 0x0003,
+ FT_short = 0x0004,
+ FT_signed_short = 0x0005,
+ FT_unsigned_short = 0x0006,
+ FT_integer = 0x0007,
+ FT_signed_integer = 0x0008,
+ FT_unsigned_integer = 0x0009,
+ FT_long = 0x000a,
+ FT_signed_long = 0x000b,
+ FT_unsigned_long = 0x000c,
+ FT_pointer = 0x000d, /* an alias for (void *) */
+ FT_float = 0x000e,
+ FT_dbl_prec_float = 0x000f,
+ FT_ext_prec_float = 0x0010, /* breaks "classic" svr4 SDB */
+ FT_complex = 0x0011, /* breaks "classic" svr4 SDB */
+ FT_dbl_prec_complex = 0x0012, /* breaks "classic" svr4 SDB */
+ /* 0x0013 -- reserved */
+ FT_void = 0x0014,
+ FT_boolean = 0x0015, /* breaks "classic" svr4 SDB */
+ FT_ext_prec_complex = 0x0016, /* breaks "classic" svr4 SDB */
+ FT_label = 0x0017,
+
+ /* GNU extensions
+ The low order byte must indicate the size (in bytes) for the type.
+ All of these types will probably break "classic" svr4 SDB */
+
+ FT_long_long = 0x8008,
+ FT_signed_long_long = 0x8108,
+ FT_unsigned_long_long = 0x8208,
+
+ FT_int8 = 0x9001,
+ FT_signed_int8 = 0x9101,
+ FT_unsigned_int8 = 0x9201,
+ FT_int16 = 0x9302,
+ FT_signed_int16 = 0x9402,
+ FT_unsigned_int16 = 0x9502,
+ FT_int32 = 0x9604,
+ FT_signed_int32 = 0x9704,
+ FT_unsigned_int32 = 0x9804,
+ FT_int64 = 0x9908,
+ FT_signed_int64 = 0x9a08,
+ FT_unsigned_int64 = 0x9b08,
+
+ FT_real32 = 0xa004,
+ FT_real64 = 0xa108,
+ FT_real96 = 0xa20c,
+ FT_real128 = 0xa310
+};
+
+#define FT_lo_user 0x8000 /* implementation-defined range start */
+#define FT_hi_user 0xffff /* implementation defined range end */
+
+/* Type modifier names and codes. */
+
+enum dwarf_type_modifier {
+ MOD_pointer_to = 0x01,
+ MOD_reference_to = 0x02,
+ MOD_const = 0x03,
+ MOD_volatile = 0x04
+};
+
+#define MOD_lo_user 0x80 /* implementation-defined range start */
+#define MOD_hi_user 0xff /* implementation-defined range end */
+
+/* Array ordering names and codes. */
+
+enum dwarf_array_dim_ordering {
+ ORD_row_major = 0,
+ ORD_col_major = 1
+};
+
+/* Array subscript format names and codes. */
+
+enum dwarf_subscr_data_formats {
+ FMT_FT_C_C = 0x0,
+ FMT_FT_C_X = 0x1,
+ FMT_FT_X_C = 0x2,
+ FMT_FT_X_X = 0x3,
+ FMT_UT_C_C = 0x4,
+ FMT_UT_C_X = 0x5,
+ FMT_UT_X_C = 0x6,
+ FMT_UT_X_X = 0x7,
+ FMT_ET = 0x8
+};
+
+/* Derived from above for ease of use. */
+
+#define FMT_CODE(_FUNDAMENTAL_TYPE_P, _UB_CONST_P, _LB_CONST_P) \
+ (((_FUNDAMENTAL_TYPE_P) ? 0 : 4) \
+ | ((_UB_CONST_P) ? 0 : 2) \
+ | ((_LB_CONST_P) ? 0 : 1))
+
+/* Source language names and codes. */
+
+enum dwarf_source_language {
+ LANG_C89 = 0x00000001,
+ LANG_C = 0x00000002,
+ LANG_ADA83 = 0x00000003,
+ LANG_C_PLUS_PLUS = 0x00000004,
+ LANG_COBOL74 = 0x00000005,
+ LANG_COBOL85 = 0x00000006,
+ LANG_FORTRAN77 = 0x00000007,
+ LANG_FORTRAN90 = 0x00000008,
+ LANG_PASCAL83 = 0x00000009,
+ LANG_MODULA2 = 0x0000000a,
+
+ /* GNU extensions */
+
+ LANG_CHILL = 0x00009af3 /* random value for GNU Chill */
+};
+
+#define LANG_lo_user 0x00008000 /* implementation-defined range start */
+#define LANG_hi_user 0x0000ffff /* implementation-defined range end */
+
+/* Names and codes for GNU "macinfo" extension. */
+
+enum dwarf_macinfo_record_type {
+ MACINFO_start = 's',
+ MACINFO_resume = 'r',
+ MACINFO_define = 'd',
+ MACINFO_undef = 'u'
+};
diff --git a/gnu/usr.bin/gdb/gdb/elf/external.h b/gnu/usr.bin/gdb/gdb/elf/external.h
new file mode 100644
index 0000000..f2ab63e
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/elf/external.h
@@ -0,0 +1,190 @@
+/* ELF support for BFD.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ Written by Fred Fish @ Cygnus Support, from information published
+ in "UNIX System V Release 4, Programmers Guide: ANSI C and
+ Programming Support Tools".
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file is part of ELF support for BFD, and contains the portions
+ that describe how ELF is represented externally by the BFD library.
+ I.E. it describes the in-file representation of ELF. It requires
+ the elf-common.h file which contains the portions that are common to
+ both the internal and external representations. */
+
+/* The 64-bit stuff is kind of random. Perhaps someone will publish a
+ spec someday. */
+
+/* ELF Header (32-bit implementations) */
+
+typedef struct {
+ unsigned char e_ident[16]; /* ELF "magic number" */
+ unsigned char e_type[2]; /* Identifies object file type */
+ unsigned char e_machine[2]; /* Specifies required architecture */
+ unsigned char e_version[4]; /* Identifies object file version */
+ unsigned char e_entry[4]; /* Entry point virtual address */
+ unsigned char e_phoff[4]; /* Program header table file offset */
+ unsigned char e_shoff[4]; /* Section header table file offset */
+ unsigned char e_flags[4]; /* Processor-specific flags */
+ unsigned char e_ehsize[2]; /* ELF header size in bytes */
+ unsigned char e_phentsize[2]; /* Program header table entry size */
+ unsigned char e_phnum[2]; /* Program header table entry count */
+ unsigned char e_shentsize[2]; /* Section header table entry size */
+ unsigned char e_shnum[2]; /* Section header table entry count */
+ unsigned char e_shstrndx[2]; /* Section header string table index */
+} Elf32_External_Ehdr;
+
+typedef struct {
+ unsigned char e_ident[16]; /* ELF "magic number" */
+ unsigned char e_type[2]; /* Identifies object file type */
+ unsigned char e_machine[2]; /* Specifies required architecture */
+ unsigned char e_version[4]; /* Identifies object file version */
+ unsigned char e_entry[8]; /* Entry point virtual address */
+ unsigned char e_phoff[8]; /* Program header table file offset */
+ unsigned char e_shoff[8]; /* Section header table file offset */
+ unsigned char e_flags[4]; /* Processor-specific flags */
+ unsigned char e_ehsize[2]; /* ELF header size in bytes */
+ unsigned char e_phentsize[2]; /* Program header table entry size */
+ unsigned char e_phnum[2]; /* Program header table entry count */
+ unsigned char e_shentsize[2]; /* Section header table entry size */
+ unsigned char e_shnum[2]; /* Section header table entry count */
+ unsigned char e_shstrndx[2]; /* Section header string table index */
+} Elf64_External_Ehdr;
+
+/* Program header */
+
+typedef struct {
+ unsigned char p_type[4]; /* Identifies program segment type */
+ unsigned char p_offset[4]; /* Segment file offset */
+ unsigned char p_vaddr[4]; /* Segment virtual address */
+ unsigned char p_paddr[4]; /* Segment physical address */
+ unsigned char p_filesz[4]; /* Segment size in file */
+ unsigned char p_memsz[4]; /* Segment size in memory */
+ unsigned char p_flags[4]; /* Segment flags */
+ unsigned char p_align[4]; /* Segment alignment, file & memory */
+} Elf32_External_Phdr;
+
+typedef struct {
+ unsigned char p_type[4]; /* Identifies program segment type */
+ unsigned char p_flags[4]; /* Segment flags */
+ unsigned char p_offset[8]; /* Segment file offset */
+ unsigned char p_vaddr[8]; /* Segment virtual address */
+ unsigned char p_paddr[8]; /* Segment physical address */
+ unsigned char p_filesz[8]; /* Segment size in file */
+ unsigned char p_memsz[8]; /* Segment size in memory */
+ unsigned char p_align[8]; /* Segment alignment, file & memory */
+} Elf64_External_Phdr;
+
+/* Section header */
+
+typedef struct {
+ unsigned char sh_name[4]; /* Section name, index in string tbl */
+ unsigned char sh_type[4]; /* Type of section */
+ unsigned char sh_flags[4]; /* Miscellaneous section attributes */
+ unsigned char sh_addr[4]; /* Section virtual addr at execution */
+ unsigned char sh_offset[4]; /* Section file offset */
+ unsigned char sh_size[4]; /* Size of section in bytes */
+ unsigned char sh_link[4]; /* Index of another section */
+ unsigned char sh_info[4]; /* Additional section information */
+ unsigned char sh_addralign[4]; /* Section alignment */
+ unsigned char sh_entsize[4]; /* Entry size if section holds table */
+} Elf32_External_Shdr;
+
+typedef struct {
+ unsigned char sh_name[4]; /* Section name, index in string tbl */
+ unsigned char sh_type[4]; /* Type of section */
+ unsigned char sh_flags[8]; /* Miscellaneous section attributes */
+ unsigned char sh_addr[8]; /* Section virtual addr at execution */
+ unsigned char sh_offset[8]; /* Section file offset */
+ unsigned char sh_size[8]; /* Size of section in bytes */
+ unsigned char sh_link[4]; /* Index of another section */
+ unsigned char sh_info[4]; /* Additional section information */
+ unsigned char sh_addralign[8]; /* Section alignment */
+ unsigned char sh_entsize[8]; /* Entry size if section holds table */
+} Elf64_External_Shdr;
+
+/* Symbol table entry */
+
+typedef struct {
+ unsigned char st_name[4]; /* Symbol name, index in string tbl */
+ unsigned char st_value[4]; /* Value of the symbol */
+ unsigned char st_size[4]; /* Associated symbol size */
+ unsigned char st_info[1]; /* Type and binding attributes */
+ unsigned char st_other[1]; /* No defined meaning, 0 */
+ unsigned char st_shndx[2]; /* Associated section index */
+} Elf32_External_Sym;
+
+typedef struct {
+ unsigned char st_name[4]; /* Symbol name, index in string tbl */
+ unsigned char st_info[1]; /* Type and binding attributes */
+ unsigned char st_other[1]; /* No defined meaning, 0 */
+ unsigned char st_shndx[2]; /* Associated section index */
+ unsigned char st_value[8]; /* Value of the symbol */
+ unsigned char st_size[8]; /* Associated symbol size */
+} Elf64_External_Sym;
+
+/* Note segments */
+
+typedef struct {
+ unsigned char namesz[4]; /* Size of entry's owner string */
+ unsigned char descsz[4]; /* Size of the note descriptor */
+ unsigned char type[4]; /* Interpretation of the descriptor */
+ char name[1]; /* Start of the name+desc data */
+} Elf_External_Note;
+
+/* Relocation Entries */
+typedef struct {
+ unsigned char r_offset[4]; /* Location at which to apply the action */
+ unsigned char r_info[4]; /* index and type of relocation */
+} Elf32_External_Rel;
+
+typedef struct {
+ unsigned char r_offset[4]; /* Location at which to apply the action */
+ unsigned char r_info[4]; /* index and type of relocation */
+ unsigned char r_addend[4]; /* Constant addend used to compute value */
+} Elf32_External_Rela;
+
+typedef struct {
+ unsigned char r_offset[8]; /* Location at which to apply the action */
+ unsigned char r_info[8]; /* index and type of relocation */
+} Elf64_External_Rel;
+
+typedef struct {
+ unsigned char r_offset[8]; /* Location at which to apply the action */
+ unsigned char r_info[8]; /* index and type of relocation */
+ unsigned char r_addend[8]; /* Constant addend used to compute value */
+} Elf64_External_Rela;
+
+/* dynamic section structure */
+
+typedef struct {
+ unsigned char d_tag[4]; /* entry tag value */
+ union {
+ unsigned char d_val[4];
+ unsigned char d_ptr[4];
+ } d_un;
+} Elf32_External_Dyn;
+
+typedef struct {
+ unsigned char d_tag[8]; /* entry tag value */
+ union {
+ unsigned char d_val[8];
+ unsigned char d_ptr[8];
+ } d_un;
+} Elf64_External_Dyn;
diff --git a/gnu/usr.bin/gdb/gdb/elf/internal.h b/gnu/usr.bin/gdb/gdb/elf/internal.h
new file mode 100644
index 0000000..ae4bdad
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/elf/internal.h
@@ -0,0 +1,195 @@
+/* ELF support for BFD.
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ Written by Fred Fish @ Cygnus Support, from information published
+ in "UNIX System V Release 4, Programmers Guide: ANSI C and
+ Programming Support Tools".
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file is part of ELF support for BFD, and contains the portions
+ that describe how ELF is represented internally in the BFD library.
+ I.E. it describes the in-memory representation of ELF. It requires
+ the elf-common.h file which contains the portions that are common to
+ both the internal and external representations. */
+
+/* Types used by various structures, functions, etc. */
+
+typedef unsigned long Elf32_Addr; /* Unsigned program address */
+typedef unsigned long Elf32_Off; /* Unsigned file offset */
+typedef long Elf32_Sword; /* Signed large integer */
+typedef unsigned long Elf32_Word; /* Unsigned large integer */
+typedef unsigned short Elf32_Half; /* Unsigned medium integer */
+typedef unsigned char Elf32_Char; /* Unsigned tiny integer */
+
+#ifdef HOST_64_BIT
+typedef unsigned HOST_64_BIT Elf64_Addr;
+typedef unsigned HOST_64_BIT Elf64_Off;
+typedef HOST_64_BIT Elf64_Sxword;
+typedef unsigned HOST_64_BIT Elf64_Xword;
+#endif
+typedef long Elf64_Sword;
+typedef unsigned long Elf64_Word;
+typedef unsigned short Elf64_Half;
+
+/* NOTE that these structures are not kept in the same order as they appear
+ in the object file. In some cases they've been reordered for more optimal
+ packing under various circumstances. */
+
+/* ELF Header */
+
+#define EI_NIDENT 16 /* Size of e_ident[] */
+
+typedef struct elf_internal_ehdr {
+ unsigned char e_ident[EI_NIDENT]; /* ELF "magic number" */
+ bfd_vma e_entry; /* Entry point virtual address */
+ bfd_signed_vma e_phoff; /* Program header table file offset */
+ bfd_signed_vma e_shoff; /* Section header table file offset */
+ unsigned long e_version; /* Identifies object file version */
+ unsigned long e_flags; /* Processor-specific flags */
+ unsigned short e_type; /* Identifies object file type */
+ unsigned short e_machine; /* Specifies required architecture */
+ unsigned short e_ehsize; /* ELF header size in bytes */
+ unsigned short e_phentsize; /* Program header table entry size */
+ unsigned short e_phnum; /* Program header table entry count */
+ unsigned short e_shentsize; /* Section header table entry size */
+ unsigned short e_shnum; /* Section header table entry count */
+ unsigned short e_shstrndx; /* Section header string table index */
+} Elf_Internal_Ehdr;
+
+#define elf32_internal_ehdr elf_internal_ehdr
+#define Elf32_Internal_Ehdr Elf_Internal_Ehdr
+#define elf64_internal_ehdr elf_internal_ehdr
+#define Elf64_Internal_Ehdr Elf_Internal_Ehdr
+
+/* Program header */
+
+struct elf_internal_phdr {
+ unsigned long p_type; /* Identifies program segment type */
+ unsigned long p_flags; /* Segment flags */
+ bfd_vma p_offset; /* Segment file offset */
+ bfd_vma p_vaddr; /* Segment virtual address */
+ bfd_vma p_paddr; /* Segment physical address */
+ bfd_vma p_filesz; /* Segment size in file */
+ bfd_vma p_memsz; /* Segment size in memory */
+ bfd_vma p_align; /* Segment alignment, file & memory */
+};
+
+typedef struct elf_internal_phdr Elf_Internal_Phdr;
+#define elf32_internal_phdr elf_internal_phdr
+#define Elf32_Internal_Phdr Elf_Internal_Phdr
+#define elf64_internal_phdr elf_internal_phdr
+#define Elf64_Internal_Phdr Elf_Internal_Phdr
+
+/* Section header */
+
+typedef struct elf_internal_shdr {
+ unsigned int sh_name; /* Section name, index in string tbl */
+ unsigned int sh_type; /* Type of section */
+ bfd_vma sh_flags; /* Miscellaneous section attributes */
+ bfd_vma sh_addr; /* Section virtual addr at execution */
+ bfd_size_type sh_size; /* Size of section in bytes */
+ bfd_size_type sh_entsize; /* Entry size if section holds table */
+ unsigned long sh_link; /* Index of another section */
+ unsigned long sh_info; /* Additional section information */
+ file_ptr sh_offset; /* Section file offset */
+ unsigned int sh_addralign; /* Section alignment */
+
+ /* The internal rep also has some cached info associated with it. */
+ void *rawdata; /* null if unused... */
+ void *contents; /* null if unused... */
+ bfd_vma size; /* size of contents (0 if unused) */
+} Elf_Internal_Shdr;
+
+#define elf32_internal_shdr elf_internal_shdr
+#define Elf32_Internal_Shdr Elf_Internal_Shdr
+#define elf64_internal_shdr elf_internal_shdr
+#define Elf64_Internal_Shdr Elf_Internal_Shdr
+
+/* Symbol table entry */
+
+struct elf_internal_sym {
+ bfd_vma st_value; /* Value of the symbol */
+ bfd_vma st_size; /* Associated symbol size */
+ unsigned long st_name; /* Symbol name, index in string tbl */
+ unsigned char st_info; /* Type and binding attributes */
+ unsigned char st_other; /* No defined meaning, 0 */
+ unsigned short st_shndx; /* Associated section index */
+};
+
+typedef struct elf_internal_sym Elf_Internal_Sym;
+
+#define elf32_internal_sym elf_internal_sym
+#define elf64_internal_sym elf_internal_sym
+#define Elf32_Internal_Sym Elf_Internal_Sym
+#define Elf64_Internal_Sym Elf_Internal_Sym
+
+/* Note segments */
+
+typedef struct elf_internal_note {
+ unsigned long namesz; /* Size of entry's owner string */
+ unsigned long descsz; /* Size of the note descriptor */
+ unsigned long type; /* Interpretation of the descriptor */
+ char name[1]; /* Start of the name+desc data */
+} Elf_Internal_Note;
+#define Elf32_Internal_Note Elf_Internal_Note
+#define elf32_internal_note elf_internal_note
+
+/* Relocation Entries */
+
+typedef struct elf_internal_rel {
+ bfd_vma r_offset; /* Location at which to apply the action */
+ /* This needs to support 64-bit values in elf64. */
+ bfd_vma r_info; /* index and type of relocation */
+} Elf_Internal_Rel;
+
+#define elf32_internal_rel elf_internal_rel
+#define Elf32_Internal_Rel Elf_Internal_Rel
+#define elf64_internal_rel elf_internal_rel
+#define Elf64_Internal_Rel Elf_Internal_Rel
+
+typedef struct elf_internal_rela {
+ bfd_vma r_offset; /* Location at which to apply the action */
+ bfd_vma r_info; /* Index and Type of relocation */
+ bfd_signed_vma r_addend; /* Constant addend used to compute value */
+} Elf_Internal_Rela;
+
+#define elf32_internal_rela elf_internal_rela
+#define elf64_internal_rela elf_internal_rela
+#define Elf32_Internal_Rela Elf_Internal_Rela
+#define Elf64_Internal_Rela Elf_Internal_Rela
+
+/* dynamic section structure */
+
+typedef struct {
+ Elf32_Sword d_tag; /* entry tag value */
+ union {
+ Elf32_Word d_val;
+ Elf32_Addr d_ptr;
+ } d_un;
+} Elf32_Internal_Dyn;
+
+#ifdef HOST_64_BIT
+typedef struct {
+ Elf64_Xword d_tag; /* entry tag value */
+ union {
+ Elf64_Xword d_val;
+ Elf64_Addr d_ptr;
+ } d_un;
+} Elf64_Internal_Dyn;
+#endif
diff --git a/gnu/usr.bin/gdb/gdb/elfread.c b/gnu/usr.bin/gdb/gdb/elfread.c
new file mode 100644
index 0000000..230afec
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/elfread.c
@@ -0,0 +1,729 @@
+/* Read ELF (Executable and Linking Format) object files for GDB.
+ Copyright 1991, 1992 Free Software Foundation, Inc.
+ Written by Fred Fish at Cygnus Support.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "bfd.h"
+#include <time.h> /* For time_t in libbfd.h. */
+#include <sys/types.h> /* For time_t, if not in time.h. */
+#include "libbfd.h" /* For bfd_elf_find_section */
+#include "libelf.h"
+#include "symtab.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "buildsym.h"
+#include "stabsread.h"
+#include "gdb-stabs.h"
+#include "complaints.h"
+#include <string.h>
+#include "demangle.h"
+
+/* The struct elfinfo is available only during ELF symbol table and
+ psymtab reading. It is destroyed at the complation of psymtab-reading.
+ It's local to elf_symfile_read. */
+
+struct elfinfo {
+ file_ptr dboffset; /* Offset to dwarf debug section */
+ unsigned int dbsize; /* Size of dwarf debug section */
+ file_ptr lnoffset; /* Offset to dwarf line number section */
+ unsigned int lnsize; /* Size of dwarf line number section */
+ asection *stabsect; /* Section pointer for .stab section */
+ asection *stabindexsect; /* Section pointer for .stab.index section */
+};
+
+/* Various things we might complain about... */
+
+struct complaint section_info_complaint =
+ {"elf/stab section information %s without a preceding file symbol", 0, 0};
+
+struct complaint section_info_dup_complaint =
+ {"duplicated elf/stab section information for %s", 0, 0};
+
+struct complaint stab_info_mismatch_complaint =
+ {"elf/stab section information missing for %s", 0, 0};
+
+struct complaint stab_info_questionable_complaint =
+ {"elf/stab section information questionable for %s", 0, 0};
+
+static void
+elf_symfile_init PARAMS ((struct objfile *));
+
+static void
+elf_new_init PARAMS ((struct objfile *));
+
+static void
+elf_symfile_read PARAMS ((struct objfile *, struct section_offsets *, int));
+
+static void
+elf_symfile_finish PARAMS ((struct objfile *));
+
+static void
+elf_symtab_read PARAMS ((bfd *, CORE_ADDR, struct objfile *));
+
+static void
+free_elfinfo PARAMS ((void *));
+
+static struct section_offsets *
+elf_symfile_offsets PARAMS ((struct objfile *, CORE_ADDR));
+
+static void
+record_minimal_symbol_and_info PARAMS ((char *, CORE_ADDR,
+ enum minimal_symbol_type, char *,
+ struct objfile *));
+
+static void
+elf_locate_sections PARAMS ((bfd *, asection *, void *));
+
+/* We are called once per section from elf_symfile_read. We
+ need to examine each section we are passed, check to see
+ if it is something we are interested in processing, and
+ if so, stash away some access information for the section.
+
+ For now we recognize the dwarf debug information sections and
+ line number sections from matching their section names. The
+ ELF definition is no real help here since it has no direct
+ knowledge of DWARF (by design, so any debugging format can be
+ used).
+
+ We also recognize the ".stab" sections used by the Sun compilers
+ released with Solaris 2.
+
+ FIXME: The section names should not be hardwired strings (what
+ should they be? I don't think most object file formats have enough
+ section flags to specify what kind of debug section it is
+ -kingdon). */
+
+static void
+elf_locate_sections (ignore_abfd, sectp, eip)
+ bfd *ignore_abfd;
+ asection *sectp;
+ PTR eip;
+{
+ register struct elfinfo *ei;
+
+ ei = (struct elfinfo *) eip;
+ if (STREQ (sectp -> name, ".debug"))
+ {
+ ei -> dboffset = sectp -> filepos;
+ ei -> dbsize = bfd_get_section_size_before_reloc (sectp);
+ }
+ else if (STREQ (sectp -> name, ".line"))
+ {
+ ei -> lnoffset = sectp -> filepos;
+ ei -> lnsize = bfd_get_section_size_before_reloc (sectp);
+ }
+ else if (STREQ (sectp -> name, ".stab"))
+ {
+ ei -> stabsect = sectp;
+ }
+ else if (STREQ (sectp -> name, ".stab.index"))
+ {
+ ei -> stabindexsect = sectp;
+ }
+}
+
+#if 0 /* Currently unused */
+
+char *
+elf_interpreter (abfd)
+ bfd *abfd;
+{
+ sec_ptr interp_sec;
+ unsigned size;
+ char *interp = NULL;
+
+ interp_sec = bfd_get_section_by_name (abfd, ".interp");
+ if (interp_sec)
+ {
+ size = bfd_section_size (abfd, interp_sec);
+ interp = alloca (size);
+ if (bfd_get_section_contents (abfd, interp_sec, interp, (file_ptr)0,
+ size))
+ {
+ interp = savestring (interp, size - 1);
+ }
+ else
+ {
+ interp = NULL;
+ }
+ }
+ return (interp);
+}
+
+#endif
+
+static void
+record_minimal_symbol_and_info (name, address, ms_type, info, objfile)
+ char *name;
+ CORE_ADDR address;
+ enum minimal_symbol_type ms_type;
+ char *info; /* FIXME, is this really char *? */
+ struct objfile *objfile;
+{
+ int section;
+
+ /* Guess the section from the type. This is likely to be wrong in
+ some cases. */
+ switch (ms_type)
+ {
+ case mst_text:
+ case mst_file_text:
+ section = SECT_OFF_TEXT;
+ break;
+ case mst_data:
+ case mst_file_data:
+ section = SECT_OFF_DATA;
+ break;
+ case mst_bss:
+ case mst_file_bss:
+ section = SECT_OFF_BSS;
+ break;
+ default:
+ section = -1;
+ break;
+ }
+
+ name = obsavestring (name, strlen (name), &objfile -> symbol_obstack);
+ prim_record_minimal_symbol_and_info (name, address, ms_type, info, section);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ elf_symtab_read -- read the symbol table of an ELF file
+
+SYNOPSIS
+
+ void elf_symtab_read (bfd *abfd, CORE_ADDR addr,
+ struct objfile *objfile)
+
+DESCRIPTION
+
+ Given an open bfd, a base address to relocate symbols to, and a
+ flag that specifies whether or not this bfd is for an executable
+ or not (may be shared library for example), add all the global
+ function and data symbols to the minimal symbol table.
+
+ In stabs-in-ELF, as implemented by Sun, there are some local symbols
+ defined in the ELF symbol table, which can be used to locate
+ the beginnings of sections from each ".o" file that was linked to
+ form the executable objfile. We gather any such info and record it
+ in data structures hung off the objfile's private data.
+
+*/
+
+static void
+elf_symtab_read (abfd, addr, objfile)
+ bfd *abfd;
+ CORE_ADDR addr;
+ struct objfile *objfile;
+{
+ unsigned int storage_needed;
+ asymbol *sym;
+ asymbol **symbol_table;
+ unsigned int number_of_symbols;
+ unsigned int i;
+ int index;
+ struct cleanup *back_to;
+ CORE_ADDR symaddr;
+ enum minimal_symbol_type ms_type;
+ /* If sectinfo is nonNULL, it contains section info that should end up
+ filed in the objfile. */
+ struct stab_section_info *sectinfo = NULL;
+ /* If filesym is nonzero, it points to a file symbol, but we haven't
+ seen any section info for it yet. */
+ asymbol *filesym = 0;
+ struct dbx_symfile_info *dbx = (struct dbx_symfile_info *)
+ objfile->sym_stab_info;
+ unsigned long size;
+
+ storage_needed = get_symtab_upper_bound (abfd);
+ if (storage_needed > 0)
+ {
+ symbol_table = (asymbol **) xmalloc (storage_needed);
+ back_to = make_cleanup (free, symbol_table);
+ number_of_symbols = bfd_canonicalize_symtab (abfd, symbol_table);
+ for (i = 0; i < number_of_symbols; i++)
+ {
+ sym = symbol_table[i];
+ if (sym -> name == NULL || *sym -> name == '\0')
+ {
+ /* Skip names that don't exist (shouldn't happen), or names
+ that are null strings (may happen). */
+ continue;
+ }
+ if (sym -> flags & BSF_FILE)
+ {
+ /* STT_FILE debugging symbol that helps stabs-in-elf debugging.
+ Chain any old one onto the objfile; remember new sym. */
+ if (sectinfo != NULL)
+ {
+ sectinfo -> next = dbx -> stab_section_info;
+ dbx -> stab_section_info = sectinfo;
+ sectinfo = NULL;
+ }
+ filesym = sym;
+ }
+ else if (sym -> flags & (BSF_GLOBAL | BSF_LOCAL | BSF_WEAK))
+ {
+ /* Select global/local/weak symbols. Note that bfd puts abs
+ symbols in their own section, so all symbols we are
+ interested in will have a section. */
+ /* Bfd symbols are section relative. */
+ symaddr = sym -> value + sym -> section -> vma;
+ /* Relocate all non-absolute symbols by base address. */
+ if (sym -> section != &bfd_abs_section)
+ {
+ symaddr += addr;
+ }
+ /* For non-absolute symbols, use the type of the section
+ they are relative to, to intuit text/data. Bfd provides
+ no way of figuring this out for absolute symbols. */
+ if (sym -> section == &bfd_abs_section)
+ {
+ ms_type = mst_abs;
+ }
+ else if (sym -> section -> flags & SEC_CODE)
+ {
+ if (sym -> flags & BSF_GLOBAL)
+ {
+ ms_type = mst_text;
+ }
+ else if (sym->name[0] == '.' && sym->name[1] == 'L')
+ /* Looks like a compiler-generated label. Skip it.
+ The assembler should be skipping these (to keep
+ executables small), but apparently with gcc on the
+ delta m88k SVR4, it loses. So to have us check too
+ should be harmless (but I encourage people to fix this
+ in the assembler instead of adding checks here). */
+ continue;
+ else
+ {
+ ms_type = mst_file_text;
+ }
+ }
+ else if (sym -> section -> flags & SEC_ALLOC)
+ {
+ if (sym -> flags & BSF_GLOBAL)
+ {
+ if (sym -> section -> flags & SEC_LOAD)
+ {
+ ms_type = mst_data;
+ }
+ else
+ {
+ ms_type = mst_bss;
+ }
+ }
+ else if (sym -> flags & BSF_LOCAL)
+ {
+ /* Named Local variable in a Data section. Check its
+ name for stabs-in-elf. The STREQ macro checks the
+ first character inline, so we only actually do a
+ strcmp function call on names that start with 'B'
+ or 'D' */
+ index = SECT_OFF_MAX;
+ if (STREQ ("Bbss.bss", sym -> name))
+ {
+ index = SECT_OFF_BSS;
+ }
+ else if (STREQ ("Ddata.data", sym -> name))
+ {
+ index = SECT_OFF_DATA;
+ }
+ else if (STREQ ("Drodata.rodata", sym -> name))
+ {
+ index = SECT_OFF_RODATA;
+ }
+ if (index != SECT_OFF_MAX)
+ {
+ /* Found a special local symbol. Allocate a
+ sectinfo, if needed, and fill it in. */
+ if (sectinfo == NULL)
+ {
+ sectinfo = (struct stab_section_info *)
+ xmmalloc (objfile -> md, sizeof (*sectinfo));
+ memset ((PTR) sectinfo, 0, sizeof (*sectinfo));
+ if (filesym == NULL)
+ {
+ complain (&section_info_complaint,
+ sym -> name);
+ }
+ else
+ {
+ sectinfo -> filename =
+ (char *) filesym -> name;
+ }
+ }
+ if (sectinfo -> sections[index] != 0)
+ {
+ complain (&section_info_dup_complaint,
+ sectinfo -> filename);
+ }
+ /* Bfd symbols are section relative. */
+ symaddr = sym -> value + sym -> section -> vma;
+ /* Relocate non-absolute symbols by base address. */
+ if (sym -> section != &bfd_abs_section)
+ {
+ symaddr += addr;
+ }
+ sectinfo -> sections[index] = symaddr;
+ /* The special local symbols don't go in the
+ minimal symbol table, so ignore this one. */
+ continue;
+ }
+ /* Not a special stabs-in-elf symbol, do regular
+ symbol processing. */
+ if (sym -> section -> flags & SEC_LOAD)
+ {
+ ms_type = mst_file_data;
+ }
+ else
+ {
+ ms_type = mst_file_bss;
+ }
+ }
+ else
+ {
+ ms_type = mst_unknown;
+ }
+ }
+ else
+ {
+ /* FIXME: Solaris2 shared libraries include lots of
+ odd "absolute" and "undefined" symbols, that play
+ hob with actions like finding what function the PC
+ is in. Ignore them if they aren't text, data, or bss. */
+ /* ms_type = mst_unknown; */
+ continue; /* Skip this symbol. */
+ }
+ /* Pass symbol size field in via BFD. FIXME!!! */
+ size = ((elf_symbol_type *) sym) -> internal_elf_sym.st_size;
+ record_minimal_symbol_and_info ((char *) sym -> name, symaddr,
+ ms_type, (PTR) size, objfile);
+ }
+ }
+ do_cleanups (back_to);
+ }
+}
+
+/* Scan and build partial symbols for a symbol file.
+ We have been initialized by a call to elf_symfile_init, which
+ currently does nothing.
+
+ SECTION_OFFSETS is a set of offsets to apply to relocate the symbols
+ in each section. We simplify it down to a single offset for all
+ symbols. FIXME.
+
+ MAINLINE is true if we are reading the main symbol
+ table (as opposed to a shared lib or dynamically loaded file).
+
+ This function only does the minimum work necessary for letting the
+ user "name" things symbolically; it does not read the entire symtab.
+ Instead, it reads the external and static symbols and puts them in partial
+ symbol tables. When more extensive information is requested of a
+ file, the corresponding partial symbol table is mutated into a full
+ fledged symbol table by going back and reading the symbols
+ for real.
+
+ We look for sections with specific names, to tell us what debug
+ format to look for: FIXME!!!
+
+ dwarf_build_psymtabs() builds psymtabs for DWARF symbols;
+ elfstab_build_psymtabs() handles STABS symbols.
+
+ Note that ELF files have a "minimal" symbol table, which looks a lot
+ like a COFF symbol table, but has only the minimal information necessary
+ for linking. We process this also, and use the information to
+ build gdb's minimal symbol table. This gives us some minimal debugging
+ capability even for files compiled without -g. */
+
+static void
+elf_symfile_read (objfile, section_offsets, mainline)
+ struct objfile *objfile;
+ struct section_offsets *section_offsets;
+ int mainline;
+{
+ bfd *abfd = objfile->obfd;
+ struct elfinfo ei;
+ struct cleanup *back_to;
+ CORE_ADDR offset;
+
+ init_minimal_symbol_collection ();
+ back_to = make_cleanup (discard_minimal_symbols, 0);
+
+ memset ((char *) &ei, 0, sizeof (ei));
+
+ /* Allocate struct to keep track of the symfile */
+ objfile->sym_stab_info = (PTR)
+ xmmalloc (objfile -> md, sizeof (struct dbx_symfile_info));
+ memset ((char *) objfile->sym_stab_info, 0, sizeof (struct dbx_symfile_info));
+ make_cleanup (free_elfinfo, (PTR) objfile);
+
+ /* Process the normal ELF symbol table first. This may write some
+ chain of info into the dbx_symfile_info in objfile->sym_stab_info,
+ which can later be used by elfstab_offset_sections. */
+
+ /* FIXME, should take a section_offsets param, not just an offset. */
+ offset = ANOFFSET (section_offsets, 0);
+ elf_symtab_read (abfd, offset, objfile);
+
+ /* Now process debugging information, which is contained in
+ special ELF sections. We first have to find them... */
+
+ bfd_map_over_sections (abfd, elf_locate_sections, (PTR) &ei);
+ if (ei.dboffset && ei.lnoffset)
+ {
+ /* DWARF sections */
+ dwarf_build_psymtabs (objfile,
+ section_offsets, mainline,
+ ei.dboffset, ei.dbsize,
+ ei.lnoffset, ei.lnsize);
+ }
+ if (ei.stabsect)
+ {
+ /* STABS sections */
+
+ /* FIXME: Sun didn't really know how to implement this well.
+ They made .stab sections that don't point to the .stabstr
+ section with the sh_link field. BFD doesn't make string table
+ sections visible to the caller. So we have to search the
+ ELF section table, not the BFD section table, for the string
+ table. */
+ struct elf32_internal_shdr *elf_sect;
+
+ elf_sect = bfd_elf_find_section (abfd, ".stabstr");
+ if (elf_sect)
+ elfstab_build_psymtabs (objfile,
+ section_offsets,
+ mainline,
+ ei.stabsect->filepos, /* .stab offset */
+ bfd_get_section_size_before_reloc (ei.stabsect),/* .stab size */
+ (file_ptr) elf_sect->sh_offset, /* .stabstr offset */
+ elf_sect->sh_size); /* .stabstr size */
+ }
+
+ if (!have_partial_symbols ())
+ {
+ wrap_here ("");
+ printf_filtered ("(no debugging symbols found)...");
+ wrap_here ("");
+ }
+
+ /* Install any minimal symbols that have been collected as the current
+ minimal symbols for this objfile. */
+
+ install_minimal_symbols (objfile);
+
+ do_cleanups (back_to);
+}
+
+/* This cleans up the objfile's sym_stab_info pointer, and the chain of
+ stab_section_info's, that might be dangling from it. */
+
+static void
+free_elfinfo (objp)
+ PTR objp;
+{
+ struct objfile *objfile = (struct objfile *)objp;
+ struct dbx_symfile_info *dbxinfo = (struct dbx_symfile_info *)
+ objfile->sym_stab_info;
+ struct stab_section_info *ssi, *nssi;
+
+ ssi = dbxinfo->stab_section_info;
+ while (ssi)
+ {
+ nssi = ssi->next;
+ mfree (objfile->md, ssi);
+ ssi = nssi;
+ }
+
+ dbxinfo->stab_section_info = 0; /* Just say No mo info about this. */
+}
+
+
+/* Initialize anything that needs initializing when a completely new symbol
+ file is specified (not just adding some symbols from another file, e.g. a
+ shared library).
+
+ We reinitialize buildsym, since we may be reading stabs from an ELF file. */
+
+static void
+elf_new_init (ignore)
+ struct objfile *ignore;
+{
+ stabsread_new_init ();
+ buildsym_new_init ();
+}
+
+/* Perform any local cleanups required when we are done with a particular
+ objfile. I.E, we are in the process of discarding all symbol information
+ for an objfile, freeing up all memory held for it, and unlinking the
+ objfile struct from the global list of known objfiles. */
+
+static void
+elf_symfile_finish (objfile)
+ struct objfile *objfile;
+{
+ if (objfile -> sym_stab_info != NULL)
+ {
+ mfree (objfile -> md, objfile -> sym_stab_info);
+ }
+}
+
+/* ELF specific initialization routine for reading symbols.
+
+ It is passed a pointer to a struct sym_fns which contains, among other
+ things, the BFD for the file whose symbols are being read, and a slot for
+ a pointer to "private data" which we can fill with goodies.
+
+ For now at least, we have nothing in particular to do, so this function is
+ just a stub. */
+
+static void
+elf_symfile_init (ignore)
+ struct objfile *ignore;
+{
+}
+
+/* ELF specific parsing routine for section offsets.
+
+ Plain and simple for now. */
+
+static
+struct section_offsets *
+elf_symfile_offsets (objfile, addr)
+ struct objfile *objfile;
+ CORE_ADDR addr;
+{
+ struct section_offsets *section_offsets;
+ int i;
+
+ section_offsets = (struct section_offsets *)
+ obstack_alloc (&objfile -> psymbol_obstack,
+ sizeof (struct section_offsets) +
+ sizeof (section_offsets->offsets) * (SECT_OFF_MAX-1));
+
+ for (i = 0; i < SECT_OFF_MAX; i++)
+ ANOFFSET (section_offsets, i) = addr;
+
+ return section_offsets;
+}
+
+/* When handling an ELF file that contains Sun STABS debug info,
+ some of the debug info is relative to the particular chunk of the
+ section that was generated in its individual .o file. E.g.
+ offsets to static variables are relative to the start of the data
+ segment *for that module before linking*. This information is
+ painfully squirreled away in the ELF symbol table as local symbols
+ with wierd names. Go get 'em when needed. */
+
+void
+elfstab_offset_sections (objfile, pst)
+ struct objfile *objfile;
+ struct partial_symtab *pst;
+{
+ char *filename = pst->filename;
+ struct dbx_symfile_info *dbx = (struct dbx_symfile_info *)
+ objfile->sym_stab_info;
+ struct stab_section_info *maybe = dbx->stab_section_info;
+ struct stab_section_info *questionable = 0;
+ int i;
+ char *p;
+
+ /* The ELF symbol info doesn't include path names, so strip the path
+ (if any) from the psymtab filename. */
+ while (0 != (p = strchr (filename, '/')))
+ filename = p+1;
+
+ /* FIXME: This linear search could speed up significantly
+ if it was chained in the right order to match how we search it,
+ and if we unchained when we found a match. */
+ for (; maybe; maybe = maybe->next)
+ {
+ if (filename[0] == maybe->filename[0]
+ && STREQ (filename, maybe->filename))
+ {
+ /* We found a match. But there might be several source files
+ (from different directories) with the same name. */
+ if (0 == maybe->found)
+ break;
+ questionable = maybe; /* Might use it later. */
+ }
+ }
+
+ if (maybe == 0 && questionable != 0)
+ {
+ complain (&stab_info_questionable_complaint, filename);
+ maybe = questionable;
+ }
+
+ if (maybe)
+ {
+ /* Found it! Allocate a new psymtab struct, and fill it in. */
+ maybe->found++;
+ pst->section_offsets = (struct section_offsets *)
+ obstack_alloc (&objfile -> psymbol_obstack,
+ sizeof (struct section_offsets) +
+ sizeof (pst->section_offsets->offsets) * (SECT_OFF_MAX-1));
+
+ for (i = 0; i < SECT_OFF_MAX; i++)
+ ANOFFSET (pst->section_offsets, i) = maybe->sections[i];
+ return;
+ }
+
+ /* We were unable to find any offsets for this file. Complain. */
+ if (dbx->stab_section_info) /* If there *is* any info, */
+ complain (&stab_info_mismatch_complaint, filename);
+}
+
+/* Register that we are able to handle ELF object file formats and DWARF
+ debugging formats.
+
+ Unlike other object file formats, where the debugging information format
+ is implied by the object file format, the ELF object file format and the
+ DWARF debugging information format are two distinct, and potentially
+ separate entities. I.E. it is perfectly possible to have ELF objects
+ with debugging formats other than DWARF. And it is conceivable that the
+ DWARF debugging format might be used with another object file format,
+ like COFF, by simply using COFF's custom section feature.
+
+ GDB, and to a lesser extent BFD, should support the notion of separate
+ object file formats and debugging information formats. For now, we just
+ use "elf" in the same sense as "a.out" or "coff", to imply both the ELF
+ object file format and the DWARF debugging format. */
+
+static struct sym_fns elf_sym_fns =
+{
+ "elf", /* sym_name: name or name prefix of BFD target type */
+ 3, /* sym_namelen: number of significant sym_name chars */
+ elf_new_init, /* sym_new_init: init anything gbl to entire symtab */
+ elf_symfile_init, /* sym_init: read initial info, setup for sym_read() */
+ elf_symfile_read, /* sym_read: read a symbol file into symtab */
+ elf_symfile_finish, /* sym_finish: finished with file, cleanup */
+ elf_symfile_offsets, /* sym_offsets: Translate ext. to int. relocation */
+ NULL /* next: pointer to next struct sym_fns */
+};
+
+void
+_initialize_elfread ()
+{
+ add_symtab_fns (&elf_sym_fns);
+}
diff --git a/gnu/usr.bin/gdb/gdb/environ.c b/gnu/usr.bin/gdb/gdb/environ.c
new file mode 100644
index 0000000..4089212
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/environ.c
@@ -0,0 +1,198 @@
+/* environ.c -- library for manipulating environments for GNU.
+ Copyright (C) 1986, 1989 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#define max(a, b) ((a) > (b) ? (a) : (b))
+
+#include "defs.h"
+#include "environ.h"
+#include <string.h>
+#include "defs.h" /* For strsave(). */
+
+
+/* Return a new environment object. */
+
+struct environ *
+make_environ ()
+{
+ register struct environ *e;
+
+ e = (struct environ *) xmalloc (sizeof (struct environ));
+
+ e->allocated = 10;
+ e->vector = (char **) xmalloc ((e->allocated + 1) * sizeof (char *));
+ e->vector[0] = 0;
+ return e;
+}
+
+/* Free an environment and all the strings in it. */
+
+void
+free_environ (e)
+ register struct environ *e;
+{
+ register char **vector = e->vector;
+
+ while (*vector)
+ free (*vector++);
+
+ free (e);
+}
+
+/* Copy the environment given to this process into E.
+ Also copies all the strings in it, so we can be sure
+ that all strings in these environments are safe to free. */
+
+void
+init_environ (e)
+ register struct environ *e;
+{
+ extern char **environ;
+ register int i;
+
+ for (i = 0; environ[i]; i++) /*EMPTY*/;
+
+ if (e->allocated < i)
+ {
+ e->allocated = max (i, e->allocated + 10);
+ e->vector = (char **) xrealloc ((char *)e->vector,
+ (e->allocated + 1) * sizeof (char *));
+ }
+
+ memcpy (e->vector, environ, (i + 1) * sizeof (char *));
+
+ while (--i >= 0)
+ {
+ register int len = strlen (e->vector[i]);
+ register char *new = (char *) xmalloc (len + 1);
+ memcpy (new, e->vector[i], len + 1);
+ e->vector[i] = new;
+ }
+}
+
+/* Return the vector of environment E.
+ This is used to get something to pass to execve. */
+
+char **
+environ_vector (e)
+ struct environ *e;
+{
+ return e->vector;
+}
+
+/* Return the value in environment E of variable VAR. */
+
+char *
+get_in_environ (e, var)
+ const struct environ *e;
+ const char *var;
+{
+ register int len = strlen (var);
+ register char **vector = e->vector;
+ register char *s;
+
+ for (; (s = *vector) != NULL; vector++)
+ if (STREQN (s, var, len) && s[len] == '=')
+ return &s[len + 1];
+
+ return 0;
+}
+
+/* Store the value in E of VAR as VALUE. */
+
+void
+set_in_environ (e, var, value)
+ struct environ *e;
+ const char *var;
+ const char *value;
+{
+ register int i;
+ register int len = strlen (var);
+ register char **vector = e->vector;
+ register char *s;
+
+ for (i = 0; (s = vector[i]) != NULL; i++)
+ if (STREQN (s, var, len) && s[len] == '=')
+ break;
+
+ if (s == 0)
+ {
+ if (i == e->allocated)
+ {
+ e->allocated += 10;
+ vector = (char **) xrealloc ((char *)vector,
+ (e->allocated + 1) * sizeof (char *));
+ e->vector = vector;
+ }
+ vector[i + 1] = 0;
+ }
+ else
+ free (s);
+
+ s = (char *) xmalloc (len + strlen (value) + 2);
+ strcpy (s, var);
+ strcat (s, "=");
+ strcat (s, value);
+ vector[i] = s;
+
+ /* Certain variables get exported back to the parent (e.g. our)
+ environment, too. FIXME: this is a hideous hack and should not be
+ allowed to live. What if we want to change the environment we pass to
+ the program without affecting GDB's behavior? */
+ if (STREQ(var, "PATH")) /* Object file location */
+ {
+ putenv (strsave (s));
+ }
+
+ /* This is a compatibility hack, since GDB 4.10 and older didn't have
+ `set gnutarget'. Eventually it should go away, so that (for example)
+ you can debug objdump's handling of GNUTARGET without affecting GDB's
+ behavior. */
+ if (STREQ (var, "GNUTARGET"))
+ {
+ set_gnutarget (value);
+ }
+ return;
+}
+
+/* Remove the setting for variable VAR from environment E. */
+
+void
+unset_in_environ (e, var)
+ struct environ *e;
+ char *var;
+{
+ register int len = strlen (var);
+ register char **vector = e->vector;
+ register char *s;
+
+ for (; (s = *vector) != NULL; vector++)
+ {
+ if (STREQN (s, var, len) && s[len] == '=')
+ {
+ free (s);
+ /* Walk through the vector, shuffling args down by one, including
+ the NULL terminator. Can't use memcpy() here since the regions
+ overlap, and memmove() might not be available. */
+ while ((vector[0] = vector[1]) != NULL)
+ {
+ vector++;
+ }
+ break;
+ }
+ }
+}
diff --git a/gnu/usr.bin/gdb/gdb/environ.h b/gnu/usr.bin/gdb/gdb/environ.h
new file mode 100644
index 0000000..6c9fd03
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/environ.h
@@ -0,0 +1,58 @@
+/* Header for environment manipulation library.
+ Copyright 1989, 1992 Free Software Foundation.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (ENVIRON_H)
+#define ENVIRON_H 1
+
+/* We manipulate environments represented as these structures. */
+
+struct environ
+{
+ /* Number of usable slots allocated in VECTOR.
+ VECTOR always has one slot not counted here,
+ to hold the terminating zero. */
+ int allocated;
+ /* A vector of slots, ALLOCATED + 1 of them.
+ The first few slots contain strings "VAR=VALUE"
+ and the next one contains zero.
+ Then come some unused slots. */
+ char **vector;
+};
+
+extern struct environ *
+make_environ PARAMS ((void));
+
+extern void
+free_environ PARAMS ((struct environ *));
+
+extern void
+init_environ PARAMS ((struct environ *));
+
+extern char *
+get_in_environ PARAMS ((const struct environ *, const char *));
+
+extern void
+set_in_environ PARAMS ((struct environ *, const char *,
+ const char *));
+
+extern void
+unset_in_environ PARAMS ((struct environ *, char *));
+
+extern char **
+environ_vector PARAMS ((struct environ *));
+
+#endif /* defined (ENVIRON_H) */
diff --git a/gnu/usr.bin/gdb/gdb/eval.c b/gnu/usr.bin/gdb/gdb/eval.c
new file mode 100644
index 0000000..f641a65
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/eval.c
@@ -0,0 +1,1213 @@
+/* Evaluate expressions for GDB.
+ Copyright 1986, 1987, 1989, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "value.h"
+#include "expression.h"
+#include "target.h"
+#include "frame.h"
+#include "language.h" /* For CAST_IS_CONVERSION */
+
+/* Values of NOSIDE argument to eval_subexp. */
+enum noside
+{ EVAL_NORMAL,
+ EVAL_SKIP, /* Only effect is to increment pos. */
+ EVAL_AVOID_SIDE_EFFECTS /* Don't modify any variables or
+ call any functions. The value
+ returned will have the correct
+ type, and will have an
+ approximately correct lvalue
+ type (inaccuracy: anything that is
+ listed as being in a register in
+ the function in which it was
+ declared will be lval_register). */
+};
+
+/* Prototypes for local functions. */
+
+static value
+evaluate_subexp_for_sizeof PARAMS ((struct expression *, int *));
+
+static value
+evaluate_subexp_with_coercion PARAMS ((struct expression *, int *,
+ enum noside));
+
+static value
+evaluate_subexp_for_address PARAMS ((struct expression *, int *,
+ enum noside));
+
+static value
+evaluate_subexp PARAMS ((struct type *, struct expression *, int *,
+ enum noside));
+
+
+/* Parse the string EXP as a C expression, evaluate it,
+ and return the result as a number. */
+
+CORE_ADDR
+parse_and_eval_address (exp)
+ char *exp;
+{
+ struct expression *expr = parse_expression (exp);
+ register CORE_ADDR addr;
+ register struct cleanup *old_chain =
+ make_cleanup (free_current_contents, &expr);
+
+ addr = value_as_pointer (evaluate_expression (expr));
+ do_cleanups (old_chain);
+ return addr;
+}
+
+/* Like parse_and_eval_address but takes a pointer to a char * variable
+ and advanced that variable across the characters parsed. */
+
+CORE_ADDR
+parse_and_eval_address_1 (expptr)
+ char **expptr;
+{
+ struct expression *expr = parse_exp_1 (expptr, (struct block *)0, 0);
+ register CORE_ADDR addr;
+ register struct cleanup *old_chain =
+ make_cleanup (free_current_contents, &expr);
+
+ addr = value_as_pointer (evaluate_expression (expr));
+ do_cleanups (old_chain);
+ return addr;
+}
+
+value
+parse_and_eval (exp)
+ char *exp;
+{
+ struct expression *expr = parse_expression (exp);
+ register value val;
+ register struct cleanup *old_chain
+ = make_cleanup (free_current_contents, &expr);
+
+ val = evaluate_expression (expr);
+ do_cleanups (old_chain);
+ return val;
+}
+
+/* Parse up to a comma (or to a closeparen)
+ in the string EXPP as an expression, evaluate it, and return the value.
+ EXPP is advanced to point to the comma. */
+
+value
+parse_to_comma_and_eval (expp)
+ char **expp;
+{
+ struct expression *expr = parse_exp_1 (expp, (struct block *) 0, 1);
+ register value val;
+ register struct cleanup *old_chain
+ = make_cleanup (free_current_contents, &expr);
+
+ val = evaluate_expression (expr);
+ do_cleanups (old_chain);
+ return val;
+}
+
+/* Evaluate an expression in internal prefix form
+ such as is constructed by parse.y.
+
+ See expression.h for info on the format of an expression. */
+
+static value evaluate_subexp ();
+static value evaluate_subexp_for_address ();
+static value evaluate_subexp_for_sizeof ();
+static value evaluate_subexp_with_coercion ();
+
+value
+evaluate_expression (exp)
+ struct expression *exp;
+{
+ int pc = 0;
+ return evaluate_subexp (NULL_TYPE, exp, &pc, EVAL_NORMAL);
+}
+
+/* Evaluate an expression, avoiding all memory references
+ and getting a value whose type alone is correct. */
+
+value
+evaluate_type (exp)
+ struct expression *exp;
+{
+ int pc = 0;
+ return evaluate_subexp (NULL_TYPE, exp, &pc, EVAL_AVOID_SIDE_EFFECTS);
+}
+
+static value
+evaluate_subexp (expect_type, exp, pos, noside)
+ struct type *expect_type;
+ register struct expression *exp;
+ register int *pos;
+ enum noside noside;
+{
+ enum exp_opcode op;
+ int tem, tem2, tem3;
+ register int pc, pc2 = 0, oldpos;
+ register value arg1 = NULL, arg2 = NULL, arg3;
+ struct type *type;
+ int nargs;
+ value *argvec;
+
+ pc = (*pos)++;
+ op = exp->elts[pc].opcode;
+
+ switch (op)
+ {
+ case OP_SCOPE:
+ tem = longest_to_int (exp->elts[pc + 2].longconst);
+ (*pos) += 4 + BYTES_TO_EXP_ELEM (tem + 1);
+ arg1 = value_struct_elt_for_reference (exp->elts[pc + 1].type,
+ 0,
+ exp->elts[pc + 1].type,
+ &exp->elts[pc + 3].string,
+ expect_type);
+ if (arg1 == NULL)
+ error ("There is no field named %s", &exp->elts[pc + 3].string);
+ return arg1;
+
+ case OP_LONG:
+ (*pos) += 3;
+ return value_from_longest (exp->elts[pc + 1].type,
+ exp->elts[pc + 2].longconst);
+
+ case OP_DOUBLE:
+ (*pos) += 3;
+ return value_from_double (exp->elts[pc + 1].type,
+ exp->elts[pc + 2].doubleconst);
+
+ case OP_VAR_VALUE:
+ (*pos) += 3;
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ {
+ struct symbol * sym = exp->elts[pc + 2].symbol;
+ enum lval_type lv;
+
+ switch (SYMBOL_CLASS (sym))
+ {
+ case LOC_CONST:
+ case LOC_LABEL:
+ case LOC_CONST_BYTES:
+ lv = not_lval;
+ break;
+
+ case LOC_REGISTER:
+ case LOC_REGPARM:
+ lv = lval_register;
+ break;
+
+ default:
+ lv = lval_memory;
+ break;
+ }
+
+ return value_zero (SYMBOL_TYPE (sym), lv);
+ }
+ else
+ return value_of_variable (exp->elts[pc + 2].symbol,
+ exp->elts[pc + 1].block);
+
+ case OP_LAST:
+ (*pos) += 2;
+ return
+ access_value_history (longest_to_int (exp->elts[pc + 1].longconst));
+
+ case OP_REGISTER:
+ (*pos) += 2;
+ return value_of_register (longest_to_int (exp->elts[pc + 1].longconst));
+
+ case OP_BOOL:
+ (*pos) += 2;
+ return value_from_longest (builtin_type_chill_bool,
+ exp->elts[pc + 1].longconst);
+
+ case OP_INTERNALVAR:
+ (*pos) += 2;
+ return value_of_internalvar (exp->elts[pc + 1].internalvar);
+
+ case OP_STRING:
+ tem = longest_to_int (exp->elts[pc + 1].longconst);
+ (*pos) += 3 + BYTES_TO_EXP_ELEM (tem + 1);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ return value_string (&exp->elts[pc + 2].string, tem);
+
+ case OP_BITSTRING:
+ error ("support for OP_BITSTRING unimplemented");
+ break;
+
+ case OP_ARRAY:
+ (*pos) += 3;
+ tem2 = longest_to_int (exp->elts[pc + 1].longconst);
+ tem3 = longest_to_int (exp->elts[pc + 2].longconst);
+ nargs = tem3 - tem2 + 1;
+ argvec = (value *) alloca (sizeof (value) * nargs);
+ for (tem = 0; tem < nargs; tem++)
+ {
+ /* Ensure that array expressions are coerced into pointer objects. */
+ argvec[tem] = evaluate_subexp_with_coercion (exp, pos, noside);
+ }
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ return (value_array (tem2, tem3, argvec));
+ break;
+
+ case TERNOP_COND:
+ /* Skip third and second args to evaluate the first one. */
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ if (value_logical_not (arg1))
+ {
+ evaluate_subexp (NULL_TYPE, exp, pos, EVAL_SKIP);
+ return evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ }
+ else
+ {
+ arg2 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ evaluate_subexp (NULL_TYPE, exp, pos, EVAL_SKIP);
+ return arg2;
+ }
+
+ case OP_FUNCALL:
+ (*pos) += 2;
+ op = exp->elts[*pos].opcode;
+ if (op == STRUCTOP_MEMBER || op == STRUCTOP_MPTR)
+ {
+ int fnptr;
+
+ nargs = longest_to_int (exp->elts[pc + 1].longconst) + 1;
+ /* First, evaluate the structure into arg2 */
+ pc2 = (*pos)++;
+
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+
+ if (op == STRUCTOP_MEMBER)
+ {
+ arg2 = evaluate_subexp_for_address (exp, pos, noside);
+ }
+ else
+ {
+ arg2 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ }
+
+ /* If the function is a virtual function, then the
+ aggregate value (providing the structure) plays
+ its part by providing the vtable. Otherwise,
+ it is just along for the ride: call the function
+ directly. */
+
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+
+ fnptr = longest_to_int (value_as_long (arg1));
+
+ if (METHOD_PTR_IS_VIRTUAL(fnptr))
+ {
+ int fnoffset = METHOD_PTR_TO_VOFFSET(fnptr);
+ struct type *basetype;
+ struct type *domain_type =
+ TYPE_DOMAIN_TYPE (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)));
+ int i, j;
+ basetype = TYPE_TARGET_TYPE (VALUE_TYPE (arg2));
+ if (domain_type != basetype)
+ arg2 = value_cast(lookup_pointer_type (domain_type), arg2);
+ basetype = TYPE_VPTR_BASETYPE (domain_type);
+ for (i = TYPE_NFN_FIELDS (basetype) - 1; i >= 0; i--)
+ {
+ struct fn_field *f = TYPE_FN_FIELDLIST1 (basetype, i);
+ /* If one is virtual, then all are virtual. */
+ if (TYPE_FN_FIELD_VIRTUAL_P (f, 0))
+ for (j = TYPE_FN_FIELDLIST_LENGTH (basetype, i) - 1; j >= 0; --j)
+ if (TYPE_FN_FIELD_VOFFSET (f, j) == fnoffset)
+ {
+ value temp = value_ind (arg2);
+ arg1 = value_virtual_fn_field (&temp, f, j, domain_type, 0);
+ arg2 = value_addr (temp);
+ goto got_it;
+ }
+ }
+ if (i < 0)
+ error ("virtual function at index %d not found", fnoffset);
+ }
+ else
+ {
+ VALUE_TYPE (arg1) = lookup_pointer_type (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)));
+ }
+ got_it:
+
+ /* Now, say which argument to start evaluating from */
+ tem = 2;
+ }
+ else if (op == STRUCTOP_STRUCT || op == STRUCTOP_PTR)
+ {
+ /* Hair for method invocations */
+ int tem2;
+
+ nargs = longest_to_int (exp->elts[pc + 1].longconst) + 1;
+ /* First, evaluate the structure into arg2 */
+ pc2 = (*pos)++;
+ tem2 = longest_to_int (exp->elts[pc2 + 1].longconst);
+ *pos += 3 + BYTES_TO_EXP_ELEM (tem2 + 1);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+
+ if (op == STRUCTOP_STRUCT)
+ {
+ /* If v is a variable in a register, and the user types
+ v.method (), this will produce an error, because v has
+ no address.
+
+ A possible way around this would be to allocate a
+ copy of the variable on the stack, copy in the
+ contents, call the function, and copy out the
+ contents. I.e. convert this from call by reference
+ to call by copy-return (or whatever it's called).
+ However, this does not work because it is not the
+ same: the method being called could stash a copy of
+ the address, and then future uses through that address
+ (after the method returns) would be expected to
+ use the variable itself, not some copy of it. */
+ arg2 = evaluate_subexp_for_address (exp, pos, noside);
+ }
+ else
+ {
+ arg2 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ }
+ /* Now, say which argument to start evaluating from */
+ tem = 2;
+ }
+ else
+ {
+ nargs = longest_to_int (exp->elts[pc + 1].longconst);
+ tem = 0;
+ }
+ /* Allocate arg vector, including space for the function to be
+ called in argvec[0] and a terminating NULL */
+ argvec = (value *) alloca (sizeof (value) * (nargs + 2));
+ for (; tem <= nargs; tem++)
+ /* Ensure that array expressions are coerced into pointer objects. */
+ argvec[tem] = evaluate_subexp_with_coercion (exp, pos, noside);
+
+ /* signal end of arglist */
+ argvec[tem] = 0;
+
+ if (op == STRUCTOP_STRUCT || op == STRUCTOP_PTR)
+ {
+ int static_memfuncp;
+ value temp = arg2;
+
+ argvec[1] = arg2;
+ argvec[0] =
+ value_struct_elt (&temp, argvec+1, &exp->elts[pc2 + 2].string,
+ &static_memfuncp,
+ op == STRUCTOP_STRUCT
+ ? "structure" : "structure pointer");
+ arg2 = value_from_longest (lookup_pointer_type (VALUE_TYPE (temp)),
+ VALUE_ADDRESS (temp)+VALUE_OFFSET (temp));
+ argvec[1] = arg2;
+ if (static_memfuncp)
+ {
+ argvec[1] = argvec[0];
+ nargs--;
+ argvec++;
+ }
+ }
+ else if (op == STRUCTOP_MEMBER || op == STRUCTOP_MPTR)
+ {
+ argvec[1] = arg2;
+ argvec[0] = arg1;
+ }
+
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ {
+ /* If the return type doesn't look like a function type, call an
+ error. This can happen if somebody tries to turn a variable into
+ a function call. This is here because people often want to
+ call, eg, strcmp, which gdb doesn't know is a function. If
+ gdb isn't asked for it's opinion (ie. through "whatis"),
+ it won't offer it. */
+
+ struct type *ftype =
+ TYPE_TARGET_TYPE (VALUE_TYPE (argvec[0]));
+
+ if (ftype)
+ return allocate_value (TYPE_TARGET_TYPE (VALUE_TYPE (argvec[0])));
+ else
+ error ("Expression of type other than \"Function returning ...\" used as function");
+ }
+ return call_function_by_hand (argvec[0], nargs, argvec + 1);
+
+ case STRUCTOP_STRUCT:
+ tem = longest_to_int (exp->elts[pc + 1].longconst);
+ (*pos) += 3 + BYTES_TO_EXP_ELEM (tem + 1);
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ return value_zero (lookup_struct_elt_type (VALUE_TYPE (arg1),
+ &exp->elts[pc + 2].string,
+ 0),
+ lval_memory);
+ else
+ {
+ value temp = arg1;
+ return value_struct_elt (&temp, (value *)0, &exp->elts[pc + 2].string,
+ (int *) 0, "structure");
+ }
+
+ case STRUCTOP_PTR:
+ tem = longest_to_int (exp->elts[pc + 1].longconst);
+ (*pos) += 3 + BYTES_TO_EXP_ELEM (tem + 1);
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ return value_zero (lookup_struct_elt_type (VALUE_TYPE (arg1),
+ &exp->elts[pc + 2].string,
+ 0),
+ lval_memory);
+ else
+ {
+ value temp = arg1;
+ return value_struct_elt (&temp, (value *)0, &exp->elts[pc + 2].string,
+ (int *) 0, "structure pointer");
+ }
+
+ case STRUCTOP_MEMBER:
+ arg1 = evaluate_subexp_for_address (exp, pos, noside);
+ goto handle_pointer_to_member;
+ case STRUCTOP_MPTR:
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ handle_pointer_to_member:
+ arg2 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_PTR)
+ goto bad_pointer_to_member;
+ type = TYPE_TARGET_TYPE (VALUE_TYPE (arg2));
+ if (TYPE_CODE (type) == TYPE_CODE_METHOD)
+ error ("not implemented: pointer-to-method in pointer-to-member construct");
+ if (TYPE_CODE (type) != TYPE_CODE_MEMBER)
+ goto bad_pointer_to_member;
+ /* Now, convert these values to an address. */
+ arg1 = value_cast (lookup_pointer_type (TYPE_DOMAIN_TYPE (type)),
+ arg1);
+ arg3 = value_from_longest (lookup_pointer_type (TYPE_TARGET_TYPE (type)),
+ value_as_long (arg1) + value_as_long (arg2));
+ return value_ind (arg3);
+ bad_pointer_to_member:
+ error("non-pointer-to-member value used in pointer-to-member construct");
+
+ case BINOP_CONCAT:
+ arg1 = evaluate_subexp_with_coercion (exp, pos, noside);
+ arg2 = evaluate_subexp_with_coercion (exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (binop_user_defined_p (op, arg1, arg2))
+ return value_x_binop (arg1, arg2, op, OP_NULL);
+ else
+ return value_concat (arg1, arg2);
+
+ case BINOP_ASSIGN:
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+ if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+ return arg1;
+ if (binop_user_defined_p (op, arg1, arg2))
+ return value_x_binop (arg1, arg2, op, OP_NULL);
+ else
+ return value_assign (arg1, arg2);
+
+ case BINOP_ASSIGN_MODIFY:
+ (*pos) += 2;
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+ if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+ return arg1;
+ op = exp->elts[pc + 1].opcode;
+ if (binop_user_defined_p (op, arg1, arg2))
+ return value_x_binop (arg1, arg2, BINOP_ASSIGN_MODIFY, op);
+ else if (op == BINOP_ADD)
+ arg2 = value_add (arg1, arg2);
+ else if (op == BINOP_SUB)
+ arg2 = value_sub (arg1, arg2);
+ else
+ arg2 = value_binop (arg1, arg2, op);
+ return value_assign (arg1, arg2);
+
+ case BINOP_ADD:
+ arg1 = evaluate_subexp_with_coercion (exp, pos, noside);
+ arg2 = evaluate_subexp_with_coercion (exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (binop_user_defined_p (op, arg1, arg2))
+ return value_x_binop (arg1, arg2, op, OP_NULL);
+ else
+ return value_add (arg1, arg2);
+
+ case BINOP_SUB:
+ arg1 = evaluate_subexp_with_coercion (exp, pos, noside);
+ arg2 = evaluate_subexp_with_coercion (exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (binop_user_defined_p (op, arg1, arg2))
+ return value_x_binop (arg1, arg2, op, OP_NULL);
+ else
+ return value_sub (arg1, arg2);
+
+ case BINOP_MUL:
+ case BINOP_DIV:
+ case BINOP_REM:
+ case BINOP_MOD:
+ case BINOP_LSH:
+ case BINOP_RSH:
+ case BINOP_BITWISE_AND:
+ case BINOP_BITWISE_IOR:
+ case BINOP_BITWISE_XOR:
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ arg2 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (binop_user_defined_p (op, arg1, arg2))
+ return value_x_binop (arg1, arg2, op, OP_NULL);
+ else
+ if (noside == EVAL_AVOID_SIDE_EFFECTS
+ && (op == BINOP_DIV || op == BINOP_REM || op == BINOP_MOD))
+ return value_zero (VALUE_TYPE (arg1), not_lval);
+ else
+ return value_binop (arg1, arg2, op);
+
+ case BINOP_SUBSCRIPT:
+ arg1 = evaluate_subexp_with_coercion (exp, pos, noside);
+ arg2 = evaluate_subexp_with_coercion (exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ {
+ /* If the user attempts to subscript something that has no target
+ type (like a plain int variable for example), then report this
+ as an error. */
+
+ type = TYPE_TARGET_TYPE (VALUE_TYPE (arg1));
+ if (type)
+ return value_zero (type, VALUE_LVAL (arg1));
+ else
+ error ("cannot subscript something of type `%s'",
+ TYPE_NAME (VALUE_TYPE (arg1)));
+ }
+
+ if (binop_user_defined_p (op, arg1, arg2))
+ return value_x_binop (arg1, arg2, op, OP_NULL);
+ else
+ return value_subscript (arg1, arg2);
+
+ case MULTI_SUBSCRIPT:
+ (*pos) += 2;
+ nargs = longest_to_int (exp->elts[pc + 1].longconst);
+ arg1 = evaluate_subexp_with_coercion (exp, pos, noside);
+ while (nargs-- > 0)
+ {
+ arg2 = evaluate_subexp_with_coercion (exp, pos, noside);
+ /* FIXME: EVAL_SKIP handling may not be correct. */
+ if (noside == EVAL_SKIP)
+ {
+ if (nargs > 0)
+ {
+ continue;
+ }
+ else
+ {
+ goto nosideret;
+ }
+ }
+ /* FIXME: EVAL_AVOID_SIDE_EFFECTS handling may not be correct. */
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ {
+ /* If the user attempts to subscript something that has no target
+ type (like a plain int variable for example), then report this
+ as an error. */
+
+ type = TYPE_TARGET_TYPE (VALUE_TYPE (arg1));
+ if (type != NULL)
+ {
+ arg1 = value_zero (type, VALUE_LVAL (arg1));
+ noside = EVAL_SKIP;
+ continue;
+ }
+ else
+ {
+ error ("cannot subscript something of type `%s'",
+ TYPE_NAME (VALUE_TYPE (arg1)));
+ }
+ }
+
+ if (binop_user_defined_p (op, arg1, arg2))
+ {
+ arg1 = value_x_binop (arg1, arg2, op, OP_NULL);
+ }
+ else
+ {
+ arg1 = value_subscript (arg1, arg2);
+ }
+ }
+ return (arg1);
+
+ case BINOP_LOGICAL_AND:
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ {
+ arg2 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ goto nosideret;
+ }
+
+ oldpos = *pos;
+ arg2 = evaluate_subexp (NULL_TYPE, exp, pos, EVAL_AVOID_SIDE_EFFECTS);
+ *pos = oldpos;
+
+ if (binop_user_defined_p (op, arg1, arg2))
+ {
+ arg2 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ return value_x_binop (arg1, arg2, op, OP_NULL);
+ }
+ else
+ {
+ tem = value_logical_not (arg1);
+ arg2 = evaluate_subexp (NULL_TYPE, exp, pos,
+ (tem ? EVAL_SKIP : noside));
+ return value_from_longest (builtin_type_int,
+ (LONGEST) (!tem && !value_logical_not (arg2)));
+ }
+
+ case BINOP_LOGICAL_OR:
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ {
+ arg2 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ goto nosideret;
+ }
+
+ oldpos = *pos;
+ arg2 = evaluate_subexp (NULL_TYPE, exp, pos, EVAL_AVOID_SIDE_EFFECTS);
+ *pos = oldpos;
+
+ if (binop_user_defined_p (op, arg1, arg2))
+ {
+ arg2 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ return value_x_binop (arg1, arg2, op, OP_NULL);
+ }
+ else
+ {
+ tem = value_logical_not (arg1);
+ arg2 = evaluate_subexp (NULL_TYPE, exp, pos,
+ (!tem ? EVAL_SKIP : noside));
+ return value_from_longest (builtin_type_int,
+ (LONGEST) (!tem || !value_logical_not (arg2)));
+ }
+
+ case BINOP_EQUAL:
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (binop_user_defined_p (op, arg1, arg2))
+ {
+ return value_x_binop (arg1, arg2, op, OP_NULL);
+ }
+ else
+ {
+ tem = value_equal (arg1, arg2);
+ return value_from_longest (builtin_type_int, (LONGEST) tem);
+ }
+
+ case BINOP_NOTEQUAL:
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (binop_user_defined_p (op, arg1, arg2))
+ {
+ return value_x_binop (arg1, arg2, op, OP_NULL);
+ }
+ else
+ {
+ tem = value_equal (arg1, arg2);
+ return value_from_longest (builtin_type_int, (LONGEST) ! tem);
+ }
+
+ case BINOP_LESS:
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (binop_user_defined_p (op, arg1, arg2))
+ {
+ return value_x_binop (arg1, arg2, op, OP_NULL);
+ }
+ else
+ {
+ tem = value_less (arg1, arg2);
+ return value_from_longest (builtin_type_int, (LONGEST) tem);
+ }
+
+ case BINOP_GTR:
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (binop_user_defined_p (op, arg1, arg2))
+ {
+ return value_x_binop (arg1, arg2, op, OP_NULL);
+ }
+ else
+ {
+ tem = value_less (arg2, arg1);
+ return value_from_longest (builtin_type_int, (LONGEST) tem);
+ }
+
+ case BINOP_GEQ:
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (binop_user_defined_p (op, arg1, arg2))
+ {
+ return value_x_binop (arg1, arg2, op, OP_NULL);
+ }
+ else
+ {
+ tem = value_less (arg2, arg1) || value_equal (arg1, arg2);
+ return value_from_longest (builtin_type_int, (LONGEST) tem);
+ }
+
+ case BINOP_LEQ:
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (binop_user_defined_p (op, arg1, arg2))
+ {
+ return value_x_binop (arg1, arg2, op, OP_NULL);
+ }
+ else
+ {
+ tem = value_less (arg1, arg2) || value_equal (arg1, arg2);
+ return value_from_longest (builtin_type_int, (LONGEST) tem);
+ }
+
+ case BINOP_REPEAT:
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ arg2 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_INT)
+ error ("Non-integral right operand for \"@\" operator.");
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ return allocate_repeat_value (VALUE_TYPE (arg1),
+ longest_to_int (value_as_long (arg2)));
+ else
+ return value_repeat (arg1, longest_to_int (value_as_long (arg2)));
+
+ case BINOP_COMMA:
+ evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ return evaluate_subexp (NULL_TYPE, exp, pos, noside);
+
+ case UNOP_NEG:
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (unop_user_defined_p (op, arg1))
+ return value_x_unop (arg1, op);
+ else
+ return value_neg (arg1);
+
+ case UNOP_COMPLEMENT:
+ /* C++: check for and handle destructor names. */
+ op = exp->elts[*pos].opcode;
+
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (unop_user_defined_p (UNOP_COMPLEMENT, arg1))
+ return value_x_unop (arg1, UNOP_COMPLEMENT);
+ else
+ return value_complement (arg1);
+
+ case UNOP_LOGICAL_NOT:
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (unop_user_defined_p (op, arg1))
+ return value_x_unop (arg1, op);
+ else
+ return value_from_longest (builtin_type_int,
+ (LONGEST) value_logical_not (arg1));
+
+ case UNOP_IND:
+ if (expect_type && TYPE_CODE (expect_type) == TYPE_CODE_PTR)
+ expect_type = TYPE_TARGET_TYPE (expect_type);
+ arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ {
+ if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR
+ || TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_REF
+ /* In C you can dereference an array to get the 1st elt. */
+ || TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_ARRAY
+ )
+ return value_zero (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)),
+ lval_memory);
+ else if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_INT)
+ /* GDB allows dereferencing an int. */
+ return value_zero (builtin_type_int, lval_memory);
+ else
+ error ("Attempt to take contents of a non-pointer value.");
+ }
+ return value_ind (arg1);
+
+ case UNOP_ADDR:
+ /* C++: check for and handle pointer to members. */
+
+ op = exp->elts[*pos].opcode;
+
+ if (noside == EVAL_SKIP)
+ {
+ if (op == OP_SCOPE)
+ {
+ int temm = longest_to_int (exp->elts[pc+3].longconst);
+ (*pos) += 3 + BYTES_TO_EXP_ELEM (temm + 1);
+ }
+ else
+ evaluate_subexp (expect_type, exp, pos, EVAL_SKIP);
+ goto nosideret;
+ }
+
+ return evaluate_subexp_for_address (exp, pos, noside);
+
+ case UNOP_SIZEOF:
+ if (noside == EVAL_SKIP)
+ {
+ evaluate_subexp (NULL_TYPE, exp, pos, EVAL_SKIP);
+ goto nosideret;
+ }
+ return evaluate_subexp_for_sizeof (exp, pos);
+
+ case UNOP_CAST:
+ (*pos) += 2;
+ arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ return value_cast (exp->elts[pc + 1].type, arg1);
+
+ case UNOP_MEMVAL:
+ (*pos) += 2;
+ arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ return value_zero (exp->elts[pc + 1].type, lval_memory);
+ else
+ return value_at_lazy (exp->elts[pc + 1].type,
+ value_as_pointer (arg1));
+
+ case UNOP_PREINCREMENT:
+ arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+ if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+ return arg1;
+ else if (unop_user_defined_p (op, arg1))
+ {
+ return value_x_unop (arg1, op);
+ }
+ else
+ {
+ arg2 = value_add (arg1, value_from_longest (builtin_type_char,
+ (LONGEST) 1));
+ return value_assign (arg1, arg2);
+ }
+
+ case UNOP_PREDECREMENT:
+ arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+ if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+ return arg1;
+ else if (unop_user_defined_p (op, arg1))
+ {
+ return value_x_unop (arg1, op);
+ }
+ else
+ {
+ arg2 = value_sub (arg1, value_from_longest (builtin_type_char,
+ (LONGEST) 1));
+ return value_assign (arg1, arg2);
+ }
+
+ case UNOP_POSTINCREMENT:
+ arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+ if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+ return arg1;
+ else if (unop_user_defined_p (op, arg1))
+ {
+ return value_x_unop (arg1, op);
+ }
+ else
+ {
+ arg2 = value_add (arg1, value_from_longest (builtin_type_char,
+ (LONGEST) 1));
+ value_assign (arg1, arg2);
+ return arg1;
+ }
+
+ case UNOP_POSTDECREMENT:
+ arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+ if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+ return arg1;
+ else if (unop_user_defined_p (op, arg1))
+ {
+ return value_x_unop (arg1, op);
+ }
+ else
+ {
+ arg2 = value_sub (arg1, value_from_longest (builtin_type_char,
+ (LONGEST) 1));
+ value_assign (arg1, arg2);
+ return arg1;
+ }
+
+ case OP_THIS:
+ (*pos) += 1;
+ return value_of_this (1);
+
+ case OP_TYPE:
+ error ("Attempt to use a type name as an expression");
+
+ default:
+ /* Removing this case and compiling with gcc -Wall reveals that
+ a lot of cases are hitting this case. Some of these should
+ probably be removed from expression.h (e.g. do we need a BINOP_SCOPE
+ and an OP_SCOPE?); others are legitimate expressions which are
+ (apparently) not fully implemented.
+
+ If there are any cases landing here which mean a user error,
+ then they should be separate cases, with more descriptive
+ error messages. */
+
+ error ("\
+GDB does not (yet) know how to evaluated that kind of expression");
+ }
+
+ nosideret:
+ return value_from_longest (builtin_type_long, (LONGEST) 1);
+}
+
+/* Evaluate a subexpression of EXP, at index *POS,
+ and return the address of that subexpression.
+ Advance *POS over the subexpression.
+ If the subexpression isn't an lvalue, get an error.
+ NOSIDE may be EVAL_AVOID_SIDE_EFFECTS;
+ then only the type of the result need be correct. */
+
+static value
+evaluate_subexp_for_address (exp, pos, noside)
+ register struct expression *exp;
+ register int *pos;
+ enum noside noside;
+{
+ enum exp_opcode op;
+ register int pc;
+ struct symbol *var;
+
+ pc = (*pos);
+ op = exp->elts[pc].opcode;
+
+ switch (op)
+ {
+ case UNOP_IND:
+ (*pos)++;
+ return evaluate_subexp (NULL_TYPE, exp, pos, noside);
+
+ case UNOP_MEMVAL:
+ (*pos) += 3;
+ return value_cast (lookup_pointer_type (exp->elts[pc + 1].type),
+ evaluate_subexp (NULL_TYPE, exp, pos, noside));
+
+ case OP_VAR_VALUE:
+ var = exp->elts[pc + 2].symbol;
+
+ /* C++: The "address" of a reference should yield the address
+ * of the object pointed to. Let value_addr() deal with it. */
+ if (TYPE_CODE (SYMBOL_TYPE (var)) == TYPE_CODE_REF)
+ goto default_case;
+
+ (*pos) += 4;
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ {
+ struct type *type =
+ lookup_pointer_type (SYMBOL_TYPE (var));
+ enum address_class sym_class = SYMBOL_CLASS (var);
+
+ if (sym_class == LOC_CONST
+ || sym_class == LOC_CONST_BYTES
+ || sym_class == LOC_REGISTER
+ || sym_class == LOC_REGPARM)
+ error ("Attempt to take address of register or constant.");
+
+ return
+ value_zero (type, not_lval);
+ }
+ else
+ return
+ locate_var_value
+ (var,
+ block_innermost_frame (exp->elts[pc + 1].block));
+
+ default:
+ default_case:
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ {
+ value x = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ if (VALUE_LVAL (x) == lval_memory)
+ return value_zero (lookup_pointer_type (VALUE_TYPE (x)),
+ not_lval);
+ else
+ error ("Attempt to take address of non-lval");
+ }
+ return value_addr (evaluate_subexp (NULL_TYPE, exp, pos, noside));
+ }
+}
+
+/* Evaluate like `evaluate_subexp' except coercing arrays to pointers.
+ When used in contexts where arrays will be coerced anyway, this is
+ equivalent to `evaluate_subexp' but much faster because it avoids
+ actually fetching array contents (perhaps obsolete now that we have
+ VALUE_LAZY).
+
+ Note that we currently only do the coercion for C expressions, where
+ arrays are zero based and the coercion is correct. For other languages,
+ with nonzero based arrays, coercion loses. Use CAST_IS_CONVERSION
+ to decide if coercion is appropriate.
+
+ */
+
+static value
+evaluate_subexp_with_coercion (exp, pos, noside)
+ register struct expression *exp;
+ register int *pos;
+ enum noside noside;
+{
+ register enum exp_opcode op;
+ register int pc;
+ register value val;
+ struct symbol *var;
+
+ pc = (*pos);
+ op = exp->elts[pc].opcode;
+
+ switch (op)
+ {
+ case OP_VAR_VALUE:
+ var = exp->elts[pc + 2].symbol;
+ if (TYPE_CODE (SYMBOL_TYPE (var)) == TYPE_CODE_ARRAY
+ && CAST_IS_CONVERSION)
+ {
+ (*pos) += 4;
+ val =
+ locate_var_value
+ (var, block_innermost_frame (exp->elts[pc + 1].block));
+ return value_cast (lookup_pointer_type (TYPE_TARGET_TYPE (SYMBOL_TYPE (var))),
+ val);
+ }
+ /* FALLTHROUGH */
+
+ default:
+ return evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ }
+}
+
+/* Evaluate a subexpression of EXP, at index *POS,
+ and return a value for the size of that subexpression.
+ Advance *POS over the subexpression. */
+
+static value
+evaluate_subexp_for_sizeof (exp, pos)
+ register struct expression *exp;
+ register int *pos;
+{
+ enum exp_opcode op;
+ register int pc;
+ value val;
+
+ pc = (*pos);
+ op = exp->elts[pc].opcode;
+
+ switch (op)
+ {
+ /* This case is handled specially
+ so that we avoid creating a value for the result type.
+ If the result type is very big, it's desirable not to
+ create a value unnecessarily. */
+ case UNOP_IND:
+ (*pos)++;
+ val = evaluate_subexp (NULL_TYPE, exp, pos, EVAL_AVOID_SIDE_EFFECTS);
+ return value_from_longest (builtin_type_int, (LONGEST)
+ TYPE_LENGTH (TYPE_TARGET_TYPE (VALUE_TYPE (val))));
+
+ case UNOP_MEMVAL:
+ (*pos) += 3;
+ return value_from_longest (builtin_type_int,
+ (LONGEST) TYPE_LENGTH (exp->elts[pc + 1].type));
+
+ case OP_VAR_VALUE:
+ (*pos) += 4;
+ return
+ value_from_longest
+ (builtin_type_int,
+ (LONGEST) TYPE_LENGTH (SYMBOL_TYPE (exp->elts[pc + 2].symbol)));
+
+ default:
+ val = evaluate_subexp (NULL_TYPE, exp, pos, EVAL_AVOID_SIDE_EFFECTS);
+ return value_from_longest (builtin_type_int,
+ (LONGEST) TYPE_LENGTH (VALUE_TYPE (val)));
+ }
+}
+
+/* Parse a type expression in the string [P..P+LENGTH). */
+
+struct type *
+parse_and_eval_type (p, length)
+ char *p;
+ int length;
+{
+ char *tmp = (char *)alloca (length + 4);
+ struct expression *expr;
+ tmp[0] = '(';
+ memcpy (tmp+1, p, length);
+ tmp[length+1] = ')';
+ tmp[length+2] = '0';
+ tmp[length+3] = '\0';
+ expr = parse_expression (tmp);
+ if (expr->elts[0].opcode != UNOP_CAST)
+ error ("Internal error in eval_type.");
+ return expr->elts[1].type;
+}
diff --git a/gnu/usr.bin/gdb/gdb/exec.c b/gnu/usr.bin/gdb/gdb/exec.c
new file mode 100644
index 0000000..f66a33c
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/exec.c
@@ -0,0 +1,489 @@
+/* Work with executable files, for GDB.
+ Copyright 1988, 1989, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "frame.h"
+#include "inferior.h"
+#include "target.h"
+#include "gdbcmd.h"
+
+#ifdef USG
+#include <sys/types.h>
+#endif
+
+#include <sys/param.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "gdbcore.h"
+
+#include <ctype.h>
+#include <sys/stat.h>
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/* Prototypes for local functions */
+
+static void
+add_to_section_table PARAMS ((bfd *, sec_ptr, PTR));
+
+static void
+exec_close PARAMS ((int));
+
+static void
+file_command PARAMS ((char *, int));
+
+static void
+set_section_command PARAMS ((char *, int));
+
+static void
+exec_files_info PARAMS ((struct target_ops *));
+
+extern int info_verbose;
+
+/* The Binary File Descriptor handle for the executable file. */
+
+bfd *exec_bfd = NULL;
+
+/* Whether to open exec and core files read-only or read-write. */
+
+int write_files = 0;
+
+/* Text start and end addresses (KLUDGE) if needed */
+
+#ifdef NEED_TEXT_START_END
+CORE_ADDR text_start = 0;
+CORE_ADDR text_end = 0;
+#endif
+
+/* Forward decl */
+
+extern struct target_ops exec_ops;
+
+/* ARGSUSED */
+static void
+exec_close (quitting)
+ int quitting;
+{
+ if (exec_bfd) {
+ char *name = bfd_get_filename (exec_bfd);
+ bfd_close (exec_bfd);
+ free (name);
+ exec_bfd = NULL;
+ }
+ if (exec_ops.to_sections) {
+ free ((PTR)exec_ops.to_sections);
+ exec_ops.to_sections = NULL;
+ exec_ops.to_sections_end = NULL;
+ }
+}
+
+/* Process the first arg in ARGS as the new exec file.
+
+ Note that we have to explicitly ignore additional args, since we can
+ be called from file_command(), which also calls symbol_file_command()
+ which can take multiple args. */
+
+void
+exec_file_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ char **argv;
+ char *filename;
+
+ target_preopen (from_tty);
+
+ /* Remove any previous exec file. */
+ unpush_target (&exec_ops);
+
+ /* Now open and digest the file the user requested, if any. */
+
+ if (args)
+ {
+ char *scratch_pathname;
+ int scratch_chan;
+
+ /* Scan through the args and pick up the first non option arg
+ as the filename. */
+
+ if ((argv = buildargv (args)) == NULL)
+ {
+ nomem (0);
+ }
+ make_cleanup (freeargv, (char *) argv);
+
+ for (; (*argv != NULL) && (**argv == '-'); argv++) {;}
+ if (*argv == NULL)
+ {
+ error ("no exec file name was specified");
+ }
+
+ filename = tilde_expand (*argv);
+ make_cleanup (free, filename);
+
+ scratch_chan = openp (getenv ("PATH"), 1, filename,
+ write_files? O_RDWR|O_BINARY: O_RDONLY|O_BINARY, 0,
+ &scratch_pathname);
+ if (scratch_chan < 0)
+ perror_with_name (filename);
+
+ exec_bfd = bfd_fdopenr (scratch_pathname, gnutarget, scratch_chan);
+ if (!exec_bfd)
+ error ("Could not open `%s' as an executable file: %s",
+ scratch_pathname, bfd_errmsg (bfd_error));
+ if (!bfd_check_format (exec_bfd, bfd_object))
+ error ("\"%s\": not in executable format: %s.",
+ scratch_pathname, bfd_errmsg (bfd_error));
+
+ if (build_section_table (exec_bfd, &exec_ops.to_sections,
+ &exec_ops.to_sections_end))
+ error ("Can't find the file sections in `%s': %s",
+ exec_bfd->filename, bfd_errmsg (bfd_error));
+
+#ifdef NEED_TEXT_START_END
+
+ /* text_end is sometimes used for where to put call dummies. A
+ few ports use these for other purposes too. */
+
+ {
+ struct section_table *p;
+
+ /* Set text_start to the lowest address of the start of any
+ readonly code section and set text_end to the highest
+ address of the end of any readonly code section. */
+
+ text_start = ~(CORE_ADDR)0;
+ text_end = (CORE_ADDR)0;
+ for (p = exec_ops.to_sections; p < exec_ops.to_sections_end; p++)
+ if (bfd_get_section_flags (p->bfd, p->sec_ptr)
+ & (SEC_CODE | SEC_READONLY))
+ {
+ if (text_start > p->addr)
+ text_start = p->addr;
+ if (text_end < p->endaddr)
+ text_end = p->endaddr;
+ }
+ }
+#endif
+
+ validate_files ();
+
+ push_target (&exec_ops);
+
+ /* Tell display code (if any) about the changed file name. */
+ if (exec_file_display_hook)
+ (*exec_file_display_hook) (filename);
+ }
+ else if (from_tty)
+ printf ("No exec file now.\n");
+}
+
+/* Set both the exec file and the symbol file, in one command.
+ What a novelty. Why did GDB go through four major releases before this
+ command was added? */
+
+static void
+file_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ /* FIXME, if we lose on reading the symbol file, we should revert
+ the exec file, but that's rough. */
+ exec_file_command (arg, from_tty);
+ symbol_file_command (arg, from_tty);
+}
+
+
+/* Locate all mappable sections of a BFD file.
+ table_pp_char is a char * to get it through bfd_map_over_sections;
+ we cast it back to its proper type. */
+
+static void
+add_to_section_table (abfd, asect, table_pp_char)
+ bfd *abfd;
+ sec_ptr asect;
+ PTR table_pp_char;
+{
+ struct section_table **table_pp = (struct section_table **)table_pp_char;
+ flagword aflag;
+
+ aflag = bfd_get_section_flags (abfd, asect);
+ /* FIXME, we need to handle BSS segment here...it alloc's but doesn't load */
+ if (!(aflag & SEC_LOAD))
+ return;
+ if (0 == bfd_section_size (abfd, asect))
+ return;
+ (*table_pp)->bfd = abfd;
+ (*table_pp)->sec_ptr = asect;
+ (*table_pp)->addr = bfd_section_vma (abfd, asect);
+ (*table_pp)->endaddr = (*table_pp)->addr + bfd_section_size (abfd, asect);
+ (*table_pp)++;
+}
+
+/* Builds a section table, given args BFD, SECTABLE_PTR, SECEND_PTR.
+ Returns 0 if OK, 1 on error. */
+
+int
+build_section_table (some_bfd, start, end)
+ bfd *some_bfd;
+ struct section_table **start, **end;
+{
+ unsigned count;
+
+ count = bfd_count_sections (some_bfd);
+ if (*start)
+ free ((PTR)*start);
+ *start = (struct section_table *) xmalloc (count * sizeof (**start));
+ *end = *start;
+ bfd_map_over_sections (some_bfd, add_to_section_table, (char *)end);
+ if (*end > *start + count)
+ abort();
+ /* We could realloc the table, but it probably loses for most files. */
+ return 0;
+}
+
+/* Read or write the exec file.
+
+ Args are address within a BFD file, address within gdb address-space,
+ length, and a flag indicating whether to read or write.
+
+ Result is a length:
+
+ 0: We cannot handle this address and length.
+ > 0: We have handled N bytes starting at this address.
+ (If N == length, we did it all.) We might be able
+ to handle more bytes beyond this length, but no
+ promises.
+ < 0: We cannot handle this address, but if somebody
+ else handles (-N) bytes, we can start from there.
+
+ The same routine is used to handle both core and exec files;
+ we just tail-call it with more arguments to select between them. */
+
+int
+xfer_memory (memaddr, myaddr, len, write, target)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+ int write;
+ struct target_ops *target;
+{
+ boolean res;
+ struct section_table *p;
+ CORE_ADDR nextsectaddr, memend;
+ boolean (*xfer_fn) PARAMS ((bfd *, sec_ptr, PTR, file_ptr, bfd_size_type));
+
+ if (len <= 0)
+ abort();
+
+ memend = memaddr + len;
+ xfer_fn = write? bfd_set_section_contents: bfd_get_section_contents;
+ nextsectaddr = memend;
+
+ for (p = target->to_sections; p < target->to_sections_end; p++)
+ {
+ if (p->addr <= memaddr)
+ if (p->endaddr >= memend)
+ {
+ /* Entire transfer is within this section. */
+ res = xfer_fn (p->bfd, p->sec_ptr, myaddr, memaddr - p->addr, len);
+ return (res != false)? len: 0;
+ }
+ else if (p->endaddr <= memaddr)
+ {
+ /* This section ends before the transfer starts. */
+ continue;
+ }
+ else
+ {
+ /* This section overlaps the transfer. Just do half. */
+ len = p->endaddr - memaddr;
+ res = xfer_fn (p->bfd, p->sec_ptr, myaddr, memaddr - p->addr, len);
+ return (res != false)? len: 0;
+ }
+ else if (p->addr < nextsectaddr)
+ nextsectaddr = p->addr;
+ }
+
+ if (nextsectaddr >= memend)
+ return 0; /* We can't help */
+ else
+ return - (nextsectaddr - memaddr); /* Next boundary where we can help */
+}
+
+#ifdef FIXME
+#ifdef REG_STACK_SEGMENT
+/* MOVE TO BFD... */
+ /* Pyramids and AM29000s have an extra segment in the virtual address space
+ for the (control) stack of register-window frames. The AM29000 folk
+ call it the "register stack" rather than the "memory stack". */
+ else if (memaddr >= reg_stack_start && memaddr < reg_stack_end)
+ {
+ i = min (len, reg_stack_end - memaddr);
+ fileptr = memaddr - reg_stack_start + reg_stack_offset;
+ wanna_xfer = coredata;
+ }
+#endif /* REG_STACK_SEGMENT */
+#endif /* FIXME */
+
+void
+print_section_info (t, abfd)
+ struct target_ops *t;
+ bfd *abfd;
+{
+ struct section_table *p;
+
+ printf_filtered ("\t`%s', ", bfd_get_filename(abfd));
+ wrap_here (" ");
+ printf_filtered ("file type %s.\n", bfd_get_target(abfd));
+ printf_filtered ("\tEntry point: %s\n",
+ local_hex_string ((unsigned long) bfd_get_start_address (exec_bfd)));
+ for (p = t->to_sections; p < t->to_sections_end; p++) {
+ printf_filtered ("\t%s", local_hex_string_custom ((unsigned long) p->addr, "08l"));
+ printf_filtered (" - %s", local_hex_string_custom ((unsigned long) p->endaddr, "08l"));
+ if (info_verbose)
+ printf_filtered (" @ %s",
+ local_hex_string_custom ((unsigned long) p->sec_ptr->filepos, "08l"));
+ printf_filtered (" is %s", bfd_section_name (p->bfd, p->sec_ptr));
+ if (p->bfd != abfd) {
+ printf_filtered (" in %s", bfd_get_filename (p->bfd));
+ }
+ printf_filtered ("\n");
+ }
+}
+
+static void
+exec_files_info (t)
+ struct target_ops *t;
+{
+ print_section_info (t, exec_bfd);
+}
+
+static void
+set_section_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ struct section_table *p;
+ char *secname;
+ unsigned seclen;
+ unsigned long secaddr;
+ char secprint[100];
+ long offset;
+
+ if (args == 0)
+ error ("Must specify section name and its virtual address");
+
+ /* Parse out section name */
+ for (secname = args; !isspace(*args); args++) ;
+ seclen = args - secname;
+
+ /* Parse out new virtual address */
+ secaddr = parse_and_eval_address (args);
+
+ for (p = exec_ops.to_sections; p < exec_ops.to_sections_end; p++) {
+ if (!strncmp (secname, bfd_section_name (exec_bfd, p->sec_ptr), seclen)
+ && bfd_section_name (exec_bfd, p->sec_ptr)[seclen] == '\0') {
+ offset = secaddr - p->addr;
+ p->addr += offset;
+ p->endaddr += offset;
+ if (from_tty)
+ exec_files_info(&exec_ops);
+ return;
+ }
+ }
+ if (seclen >= sizeof (secprint))
+ seclen = sizeof (secprint) - 1;
+ strncpy (secprint, secname, seclen);
+ secprint[seclen] = '\0';
+ error ("Section %s not found", secprint);
+}
+
+/* If mourn is being called in all the right places, this could be say
+ `gdb internal error' (since generic_mourn calls breakpoint_init_inferior). */
+
+static int
+ignore (addr, contents)
+ CORE_ADDR addr;
+ char *contents;
+{
+ return 0;
+}
+
+struct target_ops exec_ops = {
+ "exec", "Local exec file",
+ "Use an executable file as a target.\n\
+Specify the filename of the executable file.",
+ exec_file_command, exec_close, /* open, close */
+ find_default_attach, 0, 0, 0, /* attach, detach, resume, wait, */
+ 0, 0, /* fetch_registers, store_registers, */
+ 0, /* prepare_to_store, */
+ xfer_memory, exec_files_info,
+ ignore, ignore, /* insert_breakpoint, remove_breakpoint, */
+ 0, 0, 0, 0, 0, /* terminal stuff */
+ 0, 0, /* kill, load */
+ 0, /* lookup sym */
+ find_default_create_inferior,
+ 0, /* mourn_inferior */
+ 0, /* can_run */
+ 0, /* notice_signals */
+ file_stratum, 0, /* next */
+ 0, 1, 0, 0, 0, /* all mem, mem, stack, regs, exec */
+ 0, 0, /* section pointers */
+ OPS_MAGIC, /* Always the last thing */
+};
+
+void
+_initialize_exec()
+{
+ struct cmd_list_element *c;
+
+ c = add_cmd ("file", class_files, file_command,
+ "Use FILE as program to be debugged.\n\
+It is read for its symbols, for getting the contents of pure memory,\n\
+and it is the program executed when you use the `run' command.\n\
+If FILE cannot be found as specified, your execution directory path\n\
+($PATH) is searched for a command of that name.\n\
+No arg means to have no executable file and no symbols.", &cmdlist);
+ c->completer = filename_completer;
+
+ c = add_cmd ("exec-file", class_files, exec_file_command,
+ "Use FILE as program for getting contents of pure memory.\n\
+If FILE cannot be found as specified, your execution directory path\n\
+is searched for a command of that name.\n\
+No arg means have no executable file.", &cmdlist);
+ c->completer = filename_completer;
+
+ add_com ("section", class_files, set_section_command,
+ "Change the base address of section SECTION of the exec file to ADDR.\n\
+This can be used if the exec file does not contain section addresses,\n\
+(such as in the a.out format), or when the addresses specified in the\n\
+file itself are wrong. Each section must be changed separately. The\n\
+``info files'' command lists all the sections and their addresses.");
+
+ add_show_from_set
+ (add_set_cmd ("write", class_support, var_boolean, (char *)&write_files,
+ "Set writing into executable and core files.",
+ &setlist),
+ &showlist);
+
+ add_target (&exec_ops);
+}
diff --git a/gnu/usr.bin/gdb/gdb/expprint.c b/gnu/usr.bin/gdb/gdb/expprint.c
new file mode 100644
index 0000000..607b3df
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/expprint.c
@@ -0,0 +1,623 @@
+/* Print in infix form a struct expression.
+ Copyright (C) 1986, 1989, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "expression.h"
+#include "value.h"
+#include "language.h"
+#include "parser-defs.h"
+
+/* Prototypes for local functions */
+
+static void
+print_subexp PARAMS ((struct expression *, int *, FILE *, enum precedence));
+
+static void
+print_simple_m2_func PARAMS ((char *, struct expression *, int *, FILE *));
+
+void
+print_expression (exp, stream)
+ struct expression *exp;
+ FILE *stream;
+{
+ int pc = 0;
+ print_subexp (exp, &pc, stream, PREC_NULL);
+}
+
+/* Print the subexpression of EXP that starts in position POS, on STREAM.
+ PREC is the precedence of the surrounding operator;
+ if the precedence of the main operator of this subexpression is less,
+ parentheses are needed here. */
+
+static void
+print_subexp (exp, pos, stream, prec)
+ register struct expression *exp;
+ register int *pos;
+ FILE *stream;
+ enum precedence prec;
+{
+ register unsigned tem;
+ register const struct op_print *op_print_tab;
+ register int pc;
+ unsigned nargs;
+ register char *op_str;
+ int assign_modify = 0;
+ enum exp_opcode opcode;
+ enum precedence myprec = PREC_NULL;
+ /* Set to 1 for a right-associative operator. */
+ int assoc = 0;
+ value val;
+ char *tempstr = NULL;
+
+ op_print_tab = exp->language_defn->la_op_print_tab;
+ pc = (*pos)++;
+ opcode = exp->elts[pc].opcode;
+ switch (opcode)
+ {
+ /* Common ops */
+
+ case OP_SCOPE:
+ myprec = PREC_PREFIX;
+ assoc = 0;
+ fputs_filtered (type_name_no_tag (exp->elts[pc + 1].type), stream);
+ fputs_filtered ("::", stream);
+ nargs = longest_to_int (exp->elts[pc + 2].longconst);
+ (*pos) += 4 + BYTES_TO_EXP_ELEM (nargs + 1);
+ fputs_filtered (&exp->elts[pc + 3].string, stream);
+ return;
+
+ case OP_LONG:
+ (*pos) += 3;
+ value_print (value_from_longest (exp->elts[pc + 1].type,
+ exp->elts[pc + 2].longconst),
+ stream, 0, Val_no_prettyprint);
+ return;
+
+ case OP_DOUBLE:
+ (*pos) += 3;
+ value_print (value_from_double (exp->elts[pc + 1].type,
+ exp->elts[pc + 2].doubleconst),
+ stream, 0, Val_no_prettyprint);
+ return;
+
+ case OP_VAR_VALUE:
+ {
+ struct block *b;
+ (*pos) += 3;
+ b = exp->elts[pc + 1].block;
+ if (b != NULL
+ && BLOCK_FUNCTION (b) != NULL
+ && SYMBOL_SOURCE_NAME (BLOCK_FUNCTION (b)) != NULL)
+ {
+ fputs_filtered (SYMBOL_SOURCE_NAME (BLOCK_FUNCTION (b)), stream);
+ fputs_filtered ("::", stream);
+ }
+ fputs_filtered (SYMBOL_SOURCE_NAME (exp->elts[pc + 2].symbol), stream);
+ }
+ return;
+
+ case OP_LAST:
+ (*pos) += 2;
+ fprintf_filtered (stream, "$%d",
+ longest_to_int (exp->elts[pc + 1].longconst));
+ return;
+
+ case OP_REGISTER:
+ (*pos) += 2;
+ fprintf_filtered (stream, "$%s",
+ reg_names[longest_to_int (exp->elts[pc + 1].longconst)]);
+ return;
+
+ case OP_BOOL:
+ (*pos) += 2;
+ fprintf_filtered (stream, "%s",
+ longest_to_int (exp->elts[pc + 1].longconst)
+ ? "TRUE" : "FALSE");
+ return;
+
+ case OP_INTERNALVAR:
+ (*pos) += 2;
+ fprintf_filtered (stream, "$%s",
+ internalvar_name (exp->elts[pc + 1].internalvar));
+ return;
+
+ case OP_FUNCALL:
+ (*pos) += 2;
+ nargs = longest_to_int (exp->elts[pc + 1].longconst);
+ print_subexp (exp, pos, stream, PREC_SUFFIX);
+ fputs_filtered (" (", stream);
+ for (tem = 0; tem < nargs; tem++)
+ {
+ if (tem != 0)
+ fputs_filtered (", ", stream);
+ print_subexp (exp, pos, stream, PREC_ABOVE_COMMA);
+ }
+ fputs_filtered (")", stream);
+ return;
+
+ case OP_STRING:
+ nargs = longest_to_int (exp -> elts[pc + 1].longconst);
+ (*pos) += 3 + BYTES_TO_EXP_ELEM (nargs + 1);
+ /* LA_PRINT_STRING will print using the current repeat count threshold.
+ If necessary, we can temporarily set it to zero, or pass it as an
+ additional parameter to LA_PRINT_STRING. -fnf */
+ LA_PRINT_STRING (stream, &exp->elts[pc + 2].string, nargs, 0);
+ return;
+
+ case OP_BITSTRING:
+ error ("support for OP_BITSTRING unimplemented");
+ break;
+
+ case OP_ARRAY:
+ (*pos) += 3;
+ nargs = longest_to_int (exp->elts[pc + 2].longconst);
+ nargs -= longest_to_int (exp->elts[pc + 1].longconst);
+ nargs++;
+ tem = 0;
+ if (exp->elts[pc + 4].opcode == OP_LONG
+ && exp->elts[pc + 5].type == builtin_type_char
+ && exp->language_defn->la_language == language_c)
+ {
+ /* Attempt to print C character arrays using string syntax.
+ Walk through the args, picking up one character from each
+ of the OP_LONG expression elements. If any array element
+ does not match our expection of what we should find for
+ a simple string, revert back to array printing. Note that
+ the last expression element is an explicit null terminator
+ byte, which doesn't get printed. */
+ tempstr = alloca (nargs);
+ pc += 4;
+ while (tem < nargs)
+ {
+ if (exp->elts[pc].opcode != OP_LONG
+ || exp->elts[pc + 1].type != builtin_type_char)
+ {
+ /* Not a simple array of char, use regular array printing. */
+ tem = 0;
+ break;
+ }
+ else
+ {
+ tempstr[tem++] =
+ longest_to_int (exp->elts[pc + 2].longconst);
+ pc += 4;
+ }
+ }
+ }
+ if (tem > 0)
+ {
+ LA_PRINT_STRING (stream, tempstr, nargs - 1, 0);
+ (*pos) = pc;
+ }
+ else
+ {
+ fputs_filtered (" {", stream);
+ for (tem = 0; tem < nargs; tem++)
+ {
+ if (tem != 0)
+ {
+ fputs_filtered (", ", stream);
+ }
+ print_subexp (exp, pos, stream, PREC_ABOVE_COMMA);
+ }
+ fputs_filtered ("}", stream);
+ }
+ return;
+
+ case TERNOP_COND:
+ if ((int) prec > (int) PREC_COMMA)
+ fputs_filtered ("(", stream);
+ /* Print the subexpressions, forcing parentheses
+ around any binary operations within them.
+ This is more parentheses than are strictly necessary,
+ but it looks clearer. */
+ print_subexp (exp, pos, stream, PREC_HYPER);
+ fputs_filtered (" ? ", stream);
+ print_subexp (exp, pos, stream, PREC_HYPER);
+ fputs_filtered (" : ", stream);
+ print_subexp (exp, pos, stream, PREC_HYPER);
+ if ((int) prec > (int) PREC_COMMA)
+ fputs_filtered (")", stream);
+ return;
+
+ case STRUCTOP_STRUCT:
+ tem = longest_to_int (exp->elts[pc + 1].longconst);
+ (*pos) += 3 + BYTES_TO_EXP_ELEM (tem + 1);
+ print_subexp (exp, pos, stream, PREC_SUFFIX);
+ fputs_filtered (".", stream);
+ fputs_filtered (&exp->elts[pc + 2].string, stream);
+ return;
+
+ /* Will not occur for Modula-2 */
+ case STRUCTOP_PTR:
+ tem = longest_to_int (exp->elts[pc + 1].longconst);
+ (*pos) += 3 + BYTES_TO_EXP_ELEM (tem + 1);
+ print_subexp (exp, pos, stream, PREC_SUFFIX);
+ fputs_filtered ("->", stream);
+ fputs_filtered (&exp->elts[pc + 2].string, stream);
+ return;
+
+ case BINOP_SUBSCRIPT:
+ print_subexp (exp, pos, stream, PREC_SUFFIX);
+ fputs_filtered ("[", stream);
+ print_subexp (exp, pos, stream, PREC_ABOVE_COMMA);
+ fputs_filtered ("]", stream);
+ return;
+
+ case UNOP_POSTINCREMENT:
+ print_subexp (exp, pos, stream, PREC_SUFFIX);
+ fputs_filtered ("++", stream);
+ return;
+
+ case UNOP_POSTDECREMENT:
+ print_subexp (exp, pos, stream, PREC_SUFFIX);
+ fputs_filtered ("--", stream);
+ return;
+
+ case UNOP_CAST:
+ (*pos) += 2;
+ if ((int) prec > (int) PREC_PREFIX)
+ fputs_filtered ("(", stream);
+ fputs_filtered ("(", stream);
+ type_print (exp->elts[pc + 1].type, "", stream, 0);
+ fputs_filtered (") ", stream);
+ print_subexp (exp, pos, stream, PREC_PREFIX);
+ if ((int) prec > (int) PREC_PREFIX)
+ fputs_filtered (")", stream);
+ return;
+
+ case UNOP_MEMVAL:
+ (*pos) += 2;
+ if ((int) prec > (int) PREC_PREFIX)
+ fputs_filtered ("(", stream);
+ if (exp->elts[pc + 1].type->code == TYPE_CODE_FUNC &&
+ exp->elts[pc + 3].opcode == OP_LONG) {
+ /* We have a minimal symbol fn, probably. It's encoded
+ as a UNOP_MEMVAL (function-type) of an OP_LONG (int, address).
+ Swallow the OP_LONG (including both its opcodes); ignore
+ its type; print the value in the type of the MEMVAL. */
+ (*pos) += 4;
+ val = value_at_lazy (exp->elts[pc + 1].type,
+ (CORE_ADDR) exp->elts[pc + 5].longconst);
+ value_print (val, stream, 0, Val_no_prettyprint);
+ } else {
+ fputs_filtered ("{", stream);
+ type_print (exp->elts[pc + 1].type, "", stream, 0);
+ fputs_filtered ("} ", stream);
+ print_subexp (exp, pos, stream, PREC_PREFIX);
+ }
+ if ((int) prec > (int) PREC_PREFIX)
+ fputs_filtered (")", stream);
+ return;
+
+ case BINOP_ASSIGN_MODIFY:
+ opcode = exp->elts[pc + 1].opcode;
+ (*pos) += 2;
+ myprec = PREC_ASSIGN;
+ assoc = 1;
+ assign_modify = 1;
+ op_str = "???";
+ for (tem = 0; op_print_tab[tem].opcode != OP_NULL; tem++)
+ if (op_print_tab[tem].opcode == opcode)
+ {
+ op_str = op_print_tab[tem].string;
+ break;
+ }
+ if (op_print_tab[tem].opcode != opcode)
+ /* Not found; don't try to keep going because we don't know how
+ to interpret further elements. */
+ error ("Invalid expression");
+ break;
+
+ /* C++ ops */
+
+ case OP_THIS:
+ ++(*pos);
+ fputs_filtered ("this", stream);
+ return;
+
+ /* Modula-2 ops */
+
+ case MULTI_SUBSCRIPT:
+ (*pos) += 2;
+ nargs = longest_to_int (exp->elts[pc + 1].longconst);
+ print_subexp (exp, pos, stream, PREC_SUFFIX);
+ fprintf (stream, " [");
+ for (tem = 0; tem < nargs; tem++)
+ {
+ if (tem != 0)
+ fprintf (stream, ", ");
+ print_subexp (exp, pos, stream, PREC_ABOVE_COMMA);
+ }
+ fprintf (stream, "]");
+ return;
+
+ case BINOP_VAL:
+ (*pos)+=2;
+ fprintf(stream,"VAL(");
+ type_print(exp->elts[pc+1].type,"",stream,0);
+ fprintf(stream,",");
+ print_subexp(exp,pos,stream,PREC_PREFIX);
+ fprintf(stream,")");
+ return;
+
+ case UNOP_CAP:
+ print_simple_m2_func("CAP",exp,pos,stream);
+ return;
+
+ case UNOP_CHR:
+ print_simple_m2_func("CHR",exp,pos,stream);
+ return;
+
+ case UNOP_ORD:
+ print_simple_m2_func("ORD",exp,pos,stream);
+ return;
+
+ case UNOP_ABS:
+ print_simple_m2_func("ABS",exp,pos,stream);
+ return;
+
+ case UNOP_FLOAT:
+ print_simple_m2_func("FLOAT",exp,pos,stream);
+ return;
+
+ case UNOP_HIGH:
+ print_simple_m2_func("HIGH",exp,pos,stream);
+ return;
+
+ case UNOP_MAX:
+ print_simple_m2_func("MAX",exp,pos,stream);
+ return;
+
+ case UNOP_MIN:
+ print_simple_m2_func("MIN",exp,pos,stream);
+ return;
+
+ case UNOP_ODD:
+ print_simple_m2_func("ODD",exp,pos,stream);
+ return;
+
+ case UNOP_TRUNC:
+ print_simple_m2_func("TRUNC",exp,pos,stream);
+ return;
+
+ case BINOP_INCL:
+ case BINOP_EXCL:
+ error("print_subexp: Not implemented.");
+
+ /* Default ops */
+
+ default:
+ op_str = "???";
+ for (tem = 0; op_print_tab[tem].opcode != OP_NULL; tem++)
+ if (op_print_tab[tem].opcode == opcode)
+ {
+ op_str = op_print_tab[tem].string;
+ myprec = op_print_tab[tem].precedence;
+ assoc = op_print_tab[tem].right_assoc;
+ break;
+ }
+ if (op_print_tab[tem].opcode != opcode)
+ /* Not found; don't try to keep going because we don't know how
+ to interpret further elements. For example, this happens
+ if opcode is OP_TYPE. */
+ error ("Invalid expression");
+ }
+
+ if ((int) myprec < (int) prec)
+ fputs_filtered ("(", stream);
+ if ((int) opcode > (int) BINOP_END)
+ {
+ /* Unary prefix operator. */
+ fputs_filtered (op_str, stream);
+ print_subexp (exp, pos, stream, PREC_PREFIX);
+ }
+ else
+ {
+ /* Binary operator. */
+ /* Print left operand.
+ If operator is right-associative,
+ increment precedence for this operand. */
+ print_subexp (exp, pos, stream,
+ (enum precedence) ((int) myprec + assoc));
+ /* Print the operator itself. */
+ if (assign_modify)
+ fprintf_filtered (stream, " %s= ", op_str);
+ else if (op_str[0] == ',')
+ fprintf_filtered (stream, "%s ", op_str);
+ else
+ fprintf_filtered (stream, " %s ", op_str);
+ /* Print right operand.
+ If operator is left-associative,
+ increment precedence for this operand. */
+ print_subexp (exp, pos, stream,
+ (enum precedence) ((int) myprec + !assoc));
+ }
+
+ if ((int) myprec < (int) prec)
+ fputs_filtered (")", stream);
+}
+
+/* Print out something of the form <s>(<arg>).
+ This is used to print out some builtin Modula-2
+ functions.
+ FIXME: There is probably some way to get the precedence
+ rules to do this (print a unary operand with parens around it). */
+static void
+print_simple_m2_func(s,exp,pos,stream)
+ char *s;
+ register struct expression *exp;
+ register int *pos;
+ FILE *stream;
+{
+ fprintf(stream,"%s(",s);
+ print_subexp(exp,pos,stream,PREC_PREFIX);
+ fprintf(stream,")");
+}
+
+/* Return the operator corresponding to opcode OP as
+ a string. NULL indicates that the opcode was not found in the
+ current language table. */
+char *
+op_string(op)
+ enum exp_opcode op;
+{
+ int tem;
+ register const struct op_print *op_print_tab;
+
+ op_print_tab = current_language->la_op_print_tab;
+ for (tem = 0; op_print_tab[tem].opcode != OP_NULL; tem++)
+ if (op_print_tab[tem].opcode == op)
+ return op_print_tab[tem].string;
+ return NULL;
+}
+
+#ifdef DEBUG_EXPRESSIONS
+
+/* Support for dumping the raw data from expressions in a human readable
+ form. */
+
+void
+dump_expression (exp, stream, note)
+ struct expression *exp;
+ FILE *stream;
+ char *note;
+{
+ int elt;
+ char *opcode_name;
+ char *eltscan;
+ int eltsize;
+
+ fprintf_filtered (stream, "Dump of expression @ 0x%lx, %s:\n",
+ (unsigned long) exp, note);
+ fprintf_filtered (stream, "\tLanguage %s, %d elements, %d bytes each.\n",
+ exp->language_defn->la_name, exp -> nelts,
+ sizeof (union exp_element));
+ fprintf_filtered (stream, "\t%5s %20s %16s %s\n", "Index", "Opcode",
+ "Hex Value", "String Value");
+ for (elt = 0; elt < exp -> nelts; elt++)
+ {
+ fprintf_filtered (stream, "\t%5d ", elt);
+ switch (exp -> elts[elt].opcode)
+ {
+ default: opcode_name = "<unknown>"; break;
+ case OP_NULL: opcode_name = "OP_NULL"; break;
+ case BINOP_ADD: opcode_name = "BINOP_ADD"; break;
+ case BINOP_SUB: opcode_name = "BINOP_SUB"; break;
+ case BINOP_MUL: opcode_name = "BINOP_MUL"; break;
+ case BINOP_DIV: opcode_name = "BINOP_DIV"; break;
+ case BINOP_REM: opcode_name = "BINOP_REM"; break;
+ case BINOP_MOD: opcode_name = "BINOP_MOD"; break;
+ case BINOP_LSH: opcode_name = "BINOP_LSH"; break;
+ case BINOP_RSH: opcode_name = "BINOP_RSH"; break;
+ case BINOP_LOGICAL_AND: opcode_name = "BINOP_LOGICAL_AND"; break;
+ case BINOP_LOGICAL_OR: opcode_name = "BINOP_LOGICAL_OR"; break;
+ case BINOP_BITWISE_AND: opcode_name = "BINOP_BITWISE_AND"; break;
+ case BINOP_BITWISE_IOR: opcode_name = "BINOP_BITWISE_IOR"; break;
+ case BINOP_BITWISE_XOR: opcode_name = "BINOP_BITWISE_XOR"; break;
+ case BINOP_EQUAL: opcode_name = "BINOP_EQUAL"; break;
+ case BINOP_NOTEQUAL: opcode_name = "BINOP_NOTEQUAL"; break;
+ case BINOP_LESS: opcode_name = "BINOP_LESS"; break;
+ case BINOP_GTR: opcode_name = "BINOP_GTR"; break;
+ case BINOP_LEQ: opcode_name = "BINOP_LEQ"; break;
+ case BINOP_GEQ: opcode_name = "BINOP_GEQ"; break;
+ case BINOP_REPEAT: opcode_name = "BINOP_REPEAT"; break;
+ case BINOP_ASSIGN: opcode_name = "BINOP_ASSIGN"; break;
+ case BINOP_COMMA: opcode_name = "BINOP_COMMA"; break;
+ case BINOP_SUBSCRIPT: opcode_name = "BINOP_SUBSCRIPT"; break;
+ case MULTI_SUBSCRIPT: opcode_name = "MULTI_SUBSCRIPT"; break;
+ case BINOP_EXP: opcode_name = "BINOP_EXP"; break;
+ case BINOP_MIN: opcode_name = "BINOP_MIN"; break;
+ case BINOP_MAX: opcode_name = "BINOP_MAX"; break;
+ case BINOP_SCOPE: opcode_name = "BINOP_SCOPE"; break;
+ case STRUCTOP_MEMBER: opcode_name = "STRUCTOP_MEMBER"; break;
+ case STRUCTOP_MPTR: opcode_name = "STRUCTOP_MPTR"; break;
+ case BINOP_INTDIV: opcode_name = "BINOP_INTDIV"; break;
+ case BINOP_ASSIGN_MODIFY: opcode_name = "BINOP_ASSIGN_MODIFY"; break;
+ case BINOP_VAL: opcode_name = "BINOP_VAL"; break;
+ case BINOP_INCL: opcode_name = "BINOP_INCL"; break;
+ case BINOP_EXCL: opcode_name = "BINOP_EXCL"; break;
+ case BINOP_CONCAT: opcode_name = "BINOP_CONCAT"; break;
+ case BINOP_END: opcode_name = "BINOP_END"; break;
+ case TERNOP_COND: opcode_name = "TERNOP_COND"; break;
+ case OP_LONG: opcode_name = "OP_LONG"; break;
+ case OP_DOUBLE: opcode_name = "OP_DOUBLE"; break;
+ case OP_VAR_VALUE: opcode_name = "OP_VAR_VALUE"; break;
+ case OP_LAST: opcode_name = "OP_LAST"; break;
+ case OP_REGISTER: opcode_name = "OP_REGISTER"; break;
+ case OP_INTERNALVAR: opcode_name = "OP_INTERNALVAR"; break;
+ case OP_FUNCALL: opcode_name = "OP_FUNCALL"; break;
+ case OP_STRING: opcode_name = "OP_STRING"; break;
+ case OP_BITSTRING: opcode_name = "OP_BITSTRING"; break;
+ case OP_ARRAY: opcode_name = "OP_ARRAY"; break;
+ case UNOP_CAST: opcode_name = "UNOP_CAST"; break;
+ case UNOP_MEMVAL: opcode_name = "UNOP_MEMVAL"; break;
+ case UNOP_NEG: opcode_name = "UNOP_NEG"; break;
+ case UNOP_LOGICAL_NOT: opcode_name = "UNOP_LOGICAL_NOT"; break;
+ case UNOP_COMPLEMENT: opcode_name = "UNOP_COMPLEMENT"; break;
+ case UNOP_IND: opcode_name = "UNOP_IND"; break;
+ case UNOP_ADDR: opcode_name = "UNOP_ADDR"; break;
+ case UNOP_PREINCREMENT: opcode_name = "UNOP_PREINCREMENT"; break;
+ case UNOP_POSTINCREMENT: opcode_name = "UNOP_POSTINCREMENT"; break;
+ case UNOP_PREDECREMENT: opcode_name = "UNOP_PREDECREMENT"; break;
+ case UNOP_POSTDECREMENT: opcode_name = "UNOP_POSTDECREMENT"; break;
+ case UNOP_SIZEOF: opcode_name = "UNOP_SIZEOF"; break;
+ case UNOP_PLUS: opcode_name = "UNOP_PLUS"; break;
+ case UNOP_CAP: opcode_name = "UNOP_CAP"; break;
+ case UNOP_CHR: opcode_name = "UNOP_CHR"; break;
+ case UNOP_ORD: opcode_name = "UNOP_ORD"; break;
+ case UNOP_ABS: opcode_name = "UNOP_ABS"; break;
+ case UNOP_FLOAT: opcode_name = "UNOP_FLOAT"; break;
+ case UNOP_HIGH: opcode_name = "UNOP_HIGH"; break;
+ case UNOP_MAX: opcode_name = "UNOP_MAX"; break;
+ case UNOP_MIN: opcode_name = "UNOP_MIN"; break;
+ case UNOP_ODD: opcode_name = "UNOP_ODD"; break;
+ case UNOP_TRUNC: opcode_name = "UNOP_TRUNC"; break;
+ case OP_BOOL: opcode_name = "OP_BOOL"; break;
+ case OP_M2_STRING: opcode_name = "OP_M2_STRING"; break;
+ case STRUCTOP_STRUCT: opcode_name = "STRUCTOP_STRUCT"; break;
+ case STRUCTOP_PTR: opcode_name = "STRUCTOP_PTR"; break;
+ case OP_THIS: opcode_name = "OP_THIS"; break;
+ case OP_SCOPE: opcode_name = "OP_SCOPE"; break;
+ case OP_TYPE: opcode_name = "OP_TYPE"; break;
+ }
+ fprintf_filtered (stream, "%20s ", opcode_name);
+ fprintf_filtered (stream,
+#if defined (PRINTF_HAS_LONG_LONG)
+ "%ll16x ",
+#else
+ "%l16x ",
+#endif
+ exp -> elts[elt].longconst);
+
+ for (eltscan = (char *) &exp->elts[elt],
+ eltsize = sizeof (union exp_element) ;
+ eltsize-- > 0;
+ eltscan++)
+ {
+ fprintf_filtered (stream, "%c",
+ isprint (*eltscan) ? (*eltscan & 0xFF) : '.');
+ }
+ fprintf_filtered (stream, "\n");
+ }
+}
+
+#endif /* DEBUG_EXPRESSIONS */
diff --git a/gnu/usr.bin/gdb/gdb/expression.h b/gnu/usr.bin/gdb/gdb/expression.h
new file mode 100644
index 0000000..521c25c
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/expression.h
@@ -0,0 +1,313 @@
+/* Definitions for expressions stored in reversed prefix form, for GDB.
+ Copyright 1986, 1989, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (EXPRESSION_H)
+#define EXPRESSION_H 1
+
+#ifdef __STDC__
+struct block; /* Forward declaration for prototypes */
+#endif
+
+/* Definitions for saved C expressions. */
+
+/* An expression is represented as a vector of union exp_element's.
+ Each exp_element is an opcode, except that some opcodes cause
+ the following exp_element to be treated as a long or double constant
+ or as a variable. The opcodes are obeyed, using a stack for temporaries.
+ The value is left on the temporary stack at the end. */
+
+/* When it is necessary to include a string,
+ it can occupy as many exp_elements as it needs.
+ We find the length of the string using strlen,
+ divide to find out how many exp_elements are used up,
+ and skip that many. Strings, like numbers, are indicated
+ by the preceding opcode. */
+
+enum exp_opcode
+{
+ /* Used when it's necessary to pass an opcode which will be ignored,
+ or to catch uninitialized values. */
+ OP_NULL,
+
+/* BINOP_... operate on two values computed by following subexpressions,
+ replacing them by one result value. They take no immediate arguments. */
+ BINOP_ADD, /* + */
+ BINOP_SUB, /* - */
+ BINOP_MUL, /* * */
+ BINOP_DIV, /* / */
+ BINOP_REM, /* % */
+ BINOP_MOD, /* mod (Knuth 1.2.4) */
+ BINOP_LSH, /* << */
+ BINOP_RSH, /* >> */
+ BINOP_LOGICAL_AND, /* && */
+ BINOP_LOGICAL_OR, /* || */
+ BINOP_BITWISE_AND, /* & */
+ BINOP_BITWISE_IOR, /* | */
+ BINOP_BITWISE_XOR, /* ^ */
+ BINOP_EQUAL, /* == */
+ BINOP_NOTEQUAL, /* != */
+ BINOP_LESS, /* < */
+ BINOP_GTR, /* > */
+ BINOP_LEQ, /* <= */
+ BINOP_GEQ, /* >= */
+ BINOP_REPEAT, /* @ */
+ BINOP_ASSIGN, /* = */
+ BINOP_COMMA, /* , */
+ BINOP_SUBSCRIPT, /* x[y] */
+ BINOP_EXP, /* Exponentiation */
+
+/* C++. */
+ BINOP_MIN, /* <? */
+ BINOP_MAX, /* >? */
+ BINOP_SCOPE, /* :: */
+
+ /* STRUCTOP_MEMBER is used for pointer-to-member constructs.
+ X . * Y translates into X STRUCTOP_MEMBER Y. */
+ STRUCTOP_MEMBER,
+ /* STRUCTOP_MPTR is used for pointer-to-member constructs
+ when X is a pointer instead of an aggregate. */
+ STRUCTOP_MPTR,
+/* end of C++. */
+
+ /* For Modula-2 integer division DIV */
+ BINOP_INTDIV,
+
+ BINOP_ASSIGN_MODIFY, /* +=, -=, *=, and so on.
+ The following exp_element is another opcode,
+ a BINOP_, saying how to modify.
+ Then comes another BINOP_ASSIGN_MODIFY,
+ making three exp_elements in total. */
+
+ /* Modula-2 standard (binary) procedures*/
+ BINOP_VAL,
+ BINOP_INCL,
+ BINOP_EXCL,
+
+ /* Concatenate two operands, such as character strings or bitstrings.
+ If the first operand is a integer expression, then it means concatenate
+ the second operand with itself that many times. */
+ BINOP_CONCAT,
+
+ /* This must be the highest BINOP_ value, for expprint.c. */
+ BINOP_END,
+
+/* Operates on three values computed by following subexpressions. */
+ TERNOP_COND, /* ?: */
+
+/* Multidimensional subscript operator, such as Modula-2 x[a,b,...].
+ The dimensionality is encoded in the operator, like the number of
+ function arguments in OP_FUNCALL, I.E. <OP><dimension><OP>.
+ The value of the first following subexpression is subscripted
+ by each of the next following subexpressions, one per dimension. */
+
+ MULTI_SUBSCRIPT,
+
+/* The OP_... series take immediate following arguments.
+ After the arguments come another OP_... (the same one)
+ so that the grouping can be recognized from the end. */
+
+/* OP_LONG is followed by a type pointer in the next exp_element
+ and the long constant value in the following exp_element.
+ Then comes another OP_LONG.
+ Thus, the operation occupies four exp_elements. */
+
+ OP_LONG,
+/* OP_DOUBLE is similar but takes a double constant instead of a long one. */
+ OP_DOUBLE,
+
+ /* OP_VAR_VALUE takes one struct block * in the following element,
+ and one struct symbol * in the following exp_element, followed by
+ another OP_VAR_VALUE, making four exp_elements. If the block is
+ non-NULL, evaluate the symbol relative to the innermost frame
+ executing in that block; if the block is NULL use the selected frame. */
+
+ OP_VAR_VALUE,
+
+/* OP_LAST is followed by an integer in the next exp_element.
+ The integer is zero for the last value printed,
+ or it is the absolute number of a history element.
+ With another OP_LAST at the end, this makes three exp_elements. */
+ OP_LAST,
+/* OP_REGISTER is followed by an integer in the next exp_element.
+ This is the number of a register to fetch (as an int).
+ With another OP_REGISTER at the end, this makes three exp_elements. */
+ OP_REGISTER,
+/* OP_INTERNALVAR is followed by an internalvar ptr in the next exp_element.
+ With another OP_INTERNALVAR at the end, this makes three exp_elements. */
+ OP_INTERNALVAR,
+/* OP_FUNCALL is followed by an integer in the next exp_element.
+ The integer is the number of args to the function call.
+ That many plus one values from following subexpressions
+ are used, the first one being the function.
+ The integer is followed by a repeat of OP_FUNCALL,
+ making three exp_elements. */
+ OP_FUNCALL,
+/* OP_STRING represents a string constant.
+ Its format is the same as that of a STRUCTOP, but the string
+ data is just made into a string constant when the operation
+ is executed. */
+ OP_STRING,
+/* OP_BITSTRING represents a packed bitstring constant.
+ Its format is the same as that of a STRUCTOP, but the bitstring
+ data is just made into a bitstring constant when the operation
+ is executed. */
+ OP_BITSTRING,
+/* OP_ARRAY creates an array constant out of the following subexpressions.
+ It is followed by two exp_elements, the first containing an integer
+ that is the lower bound of the array and the second containing another
+ integer that is the upper bound of the array. The second integer is
+ followed by a repeat of OP_ARRAY, making four exp_elements total.
+ The bounds are used to compute the number of following subexpressions
+ to consume, as well as setting the bounds in the created array constant.
+ The type of the elements is taken from the type of the first subexp,
+ and they must all match. */
+ OP_ARRAY,
+
+/* UNOP_CAST is followed by a type pointer in the next exp_element.
+ With another UNOP_CAST at the end, this makes three exp_elements.
+ It casts the value of the following subexpression. */
+ UNOP_CAST,
+/* UNOP_MEMVAL is followed by a type pointer in the next exp_element
+ With another UNOP_MEMVAL at the end, this makes three exp_elements.
+ It casts the contents of the word addressed by the value of the
+ following subexpression. */
+ UNOP_MEMVAL,
+/* UNOP_... operate on one value from a following subexpression
+ and replace it with a result. They take no immediate arguments. */
+ UNOP_NEG, /* Unary - */
+ UNOP_LOGICAL_NOT, /* Unary ! */
+ UNOP_COMPLEMENT, /* Unary ~ */
+ UNOP_IND, /* Unary * */
+ UNOP_ADDR, /* Unary & */
+ UNOP_PREINCREMENT, /* ++ before an expression */
+ UNOP_POSTINCREMENT, /* ++ after an expression */
+ UNOP_PREDECREMENT, /* -- before an expression */
+ UNOP_POSTDECREMENT, /* -- after an expression */
+ UNOP_SIZEOF, /* Unary sizeof (followed by expression) */
+
+ UNOP_PLUS, /* Unary plus */
+
+ UNOP_CAP, /* Modula-2 standard (unary) procedures */
+ UNOP_CHR,
+ UNOP_ORD,
+ UNOP_ABS,
+ UNOP_FLOAT,
+ UNOP_HIGH,
+ UNOP_MAX,
+ UNOP_MIN,
+ UNOP_ODD,
+ UNOP_TRUNC,
+
+ OP_BOOL, /* Modula-2 builtin BOOLEAN type */
+ OP_M2_STRING, /* Modula-2 string constants */
+
+/* STRUCTOP_... operate on a value from a following subexpression
+ by extracting a structure component specified by a string
+ that appears in the following exp_elements (as many as needed).
+ STRUCTOP_STRUCT is used for "." and STRUCTOP_PTR for "->".
+ They differ only in the error message given in case the value is
+ not suitable or the structure component specified is not found.
+
+ The length of the string follows the opcode, followed by
+ BYTES_TO_EXP_ELEM(length) elements containing the data of the
+ string, followed by the length again and the opcode again. */
+
+ STRUCTOP_STRUCT,
+ STRUCTOP_PTR,
+
+/* C++ */
+ /* OP_THIS is just a placeholder for the class instance variable.
+ It just comes in a tight (OP_THIS, OP_THIS) pair. */
+ OP_THIS,
+
+ /* OP_SCOPE surrounds a type name and a field name. The type
+ name is encoded as one element, but the field name stays as
+ a string, which, of course, is variable length. */
+ OP_SCOPE,
+
+ /* OP_TYPE is for parsing types, and used with the "ptype" command
+ so we can look up types that are qualified by scope, either with
+ the GDB "::" operator, or the Modula-2 '.' operator. */
+ OP_TYPE
+};
+
+union exp_element
+{
+ enum exp_opcode opcode;
+ struct symbol *symbol;
+ LONGEST longconst;
+ double doubleconst;
+ /* Really sizeof (union exp_element) characters (or less for the last
+ element of a string). */
+ char string;
+ struct type *type;
+ struct internalvar *internalvar;
+ struct block *block;
+};
+
+struct expression
+{
+ const struct language_defn *language_defn; /* language it was entered in */
+ int nelts;
+ union exp_element elts[1];
+};
+
+/* Macros for converting between number of expression elements and bytes
+ to store that many expression elements. */
+
+#define EXP_ELEM_TO_BYTES(elements) \
+ ((elements) * sizeof (union exp_element))
+#define BYTES_TO_EXP_ELEM(bytes) \
+ (((bytes) + sizeof (union exp_element) - 1) / sizeof (union exp_element))
+
+/* From parse.c */
+
+extern struct expression *
+parse_expression PARAMS ((char *));
+
+extern struct expression *
+parse_exp_1 PARAMS ((char **, struct block *, int));
+
+/* The innermost context required by the stack and register variables
+ we've encountered so far. To use this, set it to NULL, then call
+ parse_<whatever>, then look at it. */
+extern struct block *innermost_block;
+
+/* From expprint.c */
+
+extern void
+print_expression PARAMS ((struct expression *, FILE *));
+
+extern char *
+op_string PARAMS ((enum exp_opcode));
+
+/* To enable dumping of all parsed expressions in a human readable
+ form, define DEBUG_EXPRESSIONS. This is a compile time constant
+ at the moment, since it's not clear that this feature is important
+ enough to include by default. */
+
+#ifdef DEBUG_EXPRESSIONS
+extern void
+dump_expression PARAMS ((struct expression *, FILE *, char *));
+#define DUMP_EXPRESSION(exp,file,note) dump_expression ((exp), (file), (note))
+#else
+#define DUMP_EXPRESSION(exp,file,note) /* Null expansion */
+#endif /* DEBUG_EXPRESSIONS */
+
+#endif /* !defined (EXPRESSION_H) */
diff --git a/gnu/usr.bin/gdb/gdb/findvar.c b/gnu/usr.bin/gdb/gdb/findvar.c
new file mode 100644
index 0000000..bf50e1f
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/findvar.c
@@ -0,0 +1,971 @@
+/* Find a variable's value in memory, for GDB, the GNU debugger.
+ Copyright 1986, 1987, 1989, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "frame.h"
+#include "value.h"
+#include "gdbcore.h"
+#include "inferior.h"
+#include "target.h"
+
+/* Basic byte-swapping routines. GDB has needed these for a long time...
+ All extract a target-format integer at ADDR which is LEN bytes long. */
+
+#if TARGET_CHAR_BIT != 8 || HOST_CHAR_BIT != 8
+ /* 8 bit characters are a pretty safe assumption these days, so we
+ assume it throughout all these swapping routines. If we had to deal with
+ 9 bit characters, we would need to make len be in bits and would have
+ to re-write these routines... */
+ you lose
+#endif
+
+LONGEST
+extract_signed_integer (addr, len)
+ PTR addr;
+ int len;
+{
+ LONGEST retval;
+ unsigned char *p;
+ unsigned char *startaddr = (unsigned char *)addr;
+ unsigned char *endaddr = startaddr + len;
+
+ if (len > sizeof (LONGEST))
+ error ("\
+That operation is not available on integers of more than %d bytes.",
+ sizeof (LONGEST));
+
+ /* Start at the most significant end of the integer, and work towards
+ the least significant. */
+#if TARGET_BYTE_ORDER == BIG_ENDIAN
+ p = startaddr;
+#else
+ p = endaddr - 1;
+#endif
+ /* Do the sign extension once at the start. */
+ retval = ((LONGEST)*p ^ 0x80) - 0x80;
+#if TARGET_BYTE_ORDER == BIG_ENDIAN
+ for (++p; p < endaddr; ++p)
+#else
+ for (--p; p >= startaddr; --p)
+#endif
+ {
+ retval = (retval << 8) | *p;
+ }
+ return retval;
+}
+
+unsigned LONGEST
+extract_unsigned_integer (addr, len)
+ PTR addr;
+ int len;
+{
+ unsigned LONGEST retval;
+ unsigned char *p;
+ unsigned char *startaddr = (unsigned char *)addr;
+ unsigned char *endaddr = startaddr + len;
+
+ if (len > sizeof (unsigned LONGEST))
+ error ("\
+That operation is not available on integers of more than %d bytes.",
+ sizeof (unsigned LONGEST));
+
+ /* Start at the most significant end of the integer, and work towards
+ the least significant. */
+ retval = 0;
+#if TARGET_BYTE_ORDER == BIG_ENDIAN
+ for (p = startaddr; p < endaddr; ++p)
+#else
+ for (p = endaddr - 1; p >= startaddr; --p)
+#endif
+ {
+ retval = (retval << 8) | *p;
+ }
+ return retval;
+}
+
+CORE_ADDR
+extract_address (addr, len)
+ PTR addr;
+ int len;
+{
+ /* Assume a CORE_ADDR can fit in a LONGEST (for now). Not sure
+ whether we want this to be true eventually. */
+ return extract_unsigned_integer (addr, len);
+}
+
+void
+store_signed_integer (addr, len, val)
+ PTR addr;
+ int len;
+ LONGEST val;
+{
+ unsigned char *p;
+ unsigned char *startaddr = (unsigned char *)addr;
+ unsigned char *endaddr = startaddr + len;
+
+ /* Start at the least significant end of the integer, and work towards
+ the most significant. */
+#if TARGET_BYTE_ORDER == BIG_ENDIAN
+ for (p = endaddr - 1; p >= startaddr; --p)
+#else
+ for (p = startaddr; p < endaddr; ++p)
+#endif
+ {
+ *p = val & 0xff;
+ val >>= 8;
+ }
+}
+
+void
+store_unsigned_integer (addr, len, val)
+ PTR addr;
+ int len;
+ unsigned LONGEST val;
+{
+ unsigned char *p;
+ unsigned char *startaddr = (unsigned char *)addr;
+ unsigned char *endaddr = startaddr + len;
+
+ /* Start at the least significant end of the integer, and work towards
+ the most significant. */
+#if TARGET_BYTE_ORDER == BIG_ENDIAN
+ for (p = endaddr - 1; p >= startaddr; --p)
+#else
+ for (p = startaddr; p < endaddr; ++p)
+#endif
+ {
+ *p = val & 0xff;
+ val >>= 8;
+ }
+}
+
+void
+store_address (addr, len, val)
+ PTR addr;
+ int len;
+ CORE_ADDR val;
+{
+ /* Assume a CORE_ADDR can fit in a LONGEST (for now). Not sure
+ whether we want this to be true eventually. */
+ store_unsigned_integer (addr, len, (LONGEST)val);
+}
+
+#if !defined (GET_SAVED_REGISTER)
+
+/* Return the address in which frame FRAME's value of register REGNUM
+ has been saved in memory. Or return zero if it has not been saved.
+ If REGNUM specifies the SP, the value we return is actually
+ the SP value, not an address where it was saved. */
+
+CORE_ADDR
+find_saved_register (frame, regnum)
+ FRAME frame;
+ int regnum;
+{
+ struct frame_info *fi;
+ struct frame_saved_regs saved_regs;
+
+ register FRAME frame1 = 0;
+ register CORE_ADDR addr = 0;
+
+ if (frame == 0) /* No regs saved if want current frame */
+ return 0;
+
+#ifdef HAVE_REGISTER_WINDOWS
+ /* We assume that a register in a register window will only be saved
+ in one place (since the name changes and/or disappears as you go
+ towards inner frames), so we only call get_frame_saved_regs on
+ the current frame. This is directly in contradiction to the
+ usage below, which assumes that registers used in a frame must be
+ saved in a lower (more interior) frame. This change is a result
+ of working on a register window machine; get_frame_saved_regs
+ always returns the registers saved within a frame, within the
+ context (register namespace) of that frame. */
+
+ /* However, note that we don't want this to return anything if
+ nothing is saved (if there's a frame inside of this one). Also,
+ callers to this routine asking for the stack pointer want the
+ stack pointer saved for *this* frame; this is returned from the
+ next frame. */
+
+
+ if (REGISTER_IN_WINDOW_P(regnum))
+ {
+ frame1 = get_next_frame (frame);
+ if (!frame1) return 0; /* Registers of this frame are
+ active. */
+
+ /* Get the SP from the next frame in; it will be this
+ current frame. */
+ if (regnum != SP_REGNUM)
+ frame1 = frame;
+
+ fi = get_frame_info (frame1);
+ get_frame_saved_regs (fi, &saved_regs);
+ return saved_regs.regs[regnum]; /* ... which might be zero */
+ }
+#endif /* HAVE_REGISTER_WINDOWS */
+
+ /* Note that this next routine assumes that registers used in
+ frame x will be saved only in the frame that x calls and
+ frames interior to it. This is not true on the sparc, but the
+ above macro takes care of it, so we should be all right. */
+ while (1)
+ {
+ QUIT;
+ frame1 = get_prev_frame (frame1);
+ if (frame1 == 0 || frame1 == frame)
+ break;
+ fi = get_frame_info (frame1);
+ get_frame_saved_regs (fi, &saved_regs);
+ if (saved_regs.regs[regnum])
+ addr = saved_regs.regs[regnum];
+ }
+
+ return addr;
+}
+
+/* Find register number REGNUM relative to FRAME and put its (raw,
+ target format) contents in *RAW_BUFFER. Set *OPTIMIZED if the
+ variable was optimized out (and thus can't be fetched). Set *LVAL
+ to lval_memory, lval_register, or not_lval, depending on whether
+ the value was fetched from memory, from a register, or in a strange
+ and non-modifiable way (e.g. a frame pointer which was calculated
+ rather than fetched). Set *ADDRP to the address, either in memory
+ on as a REGISTER_BYTE offset into the registers array.
+
+ Note that this implementation never sets *LVAL to not_lval. But
+ it can be replaced by defining GET_SAVED_REGISTER and supplying
+ your own.
+
+ The argument RAW_BUFFER must point to aligned memory. */
+
+void
+get_saved_register (raw_buffer, optimized, addrp, frame, regnum, lval)
+ char *raw_buffer;
+ int *optimized;
+ CORE_ADDR *addrp;
+ FRAME frame;
+ int regnum;
+ enum lval_type *lval;
+{
+ CORE_ADDR addr;
+ /* Normal systems don't optimize out things with register numbers. */
+ if (optimized != NULL)
+ *optimized = 0;
+ addr = find_saved_register (frame, regnum);
+ if (addr != 0)
+ {
+ if (lval != NULL)
+ *lval = lval_memory;
+ if (regnum == SP_REGNUM)
+ {
+ if (raw_buffer != NULL)
+ {
+ /* Put it back in target format. */
+ store_address (raw_buffer, REGISTER_RAW_SIZE (regnum), addr);
+ }
+ if (addrp != NULL)
+ *addrp = 0;
+ return;
+ }
+ if (raw_buffer != NULL)
+ read_memory (addr, raw_buffer, REGISTER_RAW_SIZE (regnum));
+ }
+ else
+ {
+ if (lval != NULL)
+ *lval = lval_register;
+ addr = REGISTER_BYTE (regnum);
+ if (raw_buffer != NULL)
+ read_register_gen (regnum, raw_buffer);
+ }
+ if (addrp != NULL)
+ *addrp = addr;
+}
+#endif /* GET_SAVED_REGISTER. */
+
+/* Copy the bytes of register REGNUM, relative to the current stack frame,
+ into our memory at MYADDR, in target byte order.
+ The number of bytes copied is REGISTER_RAW_SIZE (REGNUM).
+
+ Returns 1 if could not be read, 0 if could. */
+
+int
+read_relative_register_raw_bytes (regnum, myaddr)
+ int regnum;
+ char *myaddr;
+{
+ int optim;
+ if (regnum == FP_REGNUM && selected_frame)
+ {
+ /* Put it back in target format. */
+ store_address (myaddr, REGISTER_RAW_SIZE(FP_REGNUM),
+ FRAME_FP(selected_frame));
+ return 0;
+ }
+
+ get_saved_register (myaddr, &optim, (CORE_ADDR *) NULL, selected_frame,
+ regnum, (enum lval_type *)NULL);
+ return optim;
+}
+
+/* Return a `value' with the contents of register REGNUM
+ in its virtual format, with the type specified by
+ REGISTER_VIRTUAL_TYPE. */
+
+value
+value_of_register (regnum)
+ int regnum;
+{
+ CORE_ADDR addr;
+ int optim;
+ register value val;
+ char raw_buffer[MAX_REGISTER_RAW_SIZE];
+ char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE];
+ enum lval_type lval;
+
+ get_saved_register (raw_buffer, &optim, &addr,
+ selected_frame, regnum, &lval);
+
+ REGISTER_CONVERT_TO_VIRTUAL (regnum, raw_buffer, virtual_buffer);
+ val = allocate_value (REGISTER_VIRTUAL_TYPE (regnum));
+ memcpy (VALUE_CONTENTS_RAW (val), virtual_buffer,
+ REGISTER_VIRTUAL_SIZE (regnum));
+ VALUE_LVAL (val) = lval;
+ VALUE_ADDRESS (val) = addr;
+ VALUE_REGNO (val) = regnum;
+ VALUE_OPTIMIZED_OUT (val) = optim;
+ return val;
+}
+
+/* Low level examining and depositing of registers.
+
+ The caller is responsible for making
+ sure that the inferior is stopped before calling the fetching routines,
+ or it will get garbage. (a change from GDB version 3, in which
+ the caller got the value from the last stop). */
+
+/* Contents of the registers in target byte order.
+ We allocate some extra slop since we do a lot of memcpy's around `registers',
+ and failing-soft is better than failing hard. */
+char registers[REGISTER_BYTES + /* SLOP */ 256];
+
+/* Nonzero if that register has been fetched. */
+char register_valid[NUM_REGS];
+
+/* Indicate that registers may have changed, so invalidate the cache. */
+void
+registers_changed ()
+{
+ int i;
+ for (i = 0; i < NUM_REGS; i++)
+ register_valid[i] = 0;
+}
+
+/* Indicate that all registers have been fetched, so mark them all valid. */
+void
+registers_fetched ()
+{
+ int i;
+ for (i = 0; i < NUM_REGS; i++)
+ register_valid[i] = 1;
+}
+
+/* Copy LEN bytes of consecutive data from registers
+ starting with the REGBYTE'th byte of register data
+ into memory at MYADDR. */
+
+void
+read_register_bytes (regbyte, myaddr, len)
+ int regbyte;
+ char *myaddr;
+ int len;
+{
+ /* Fetch all registers. */
+ int i;
+ for (i = 0; i < NUM_REGS; i++)
+ if (!register_valid[i])
+ {
+ target_fetch_registers (-1);
+ break;
+ }
+ if (myaddr != NULL)
+ memcpy (myaddr, &registers[regbyte], len);
+}
+
+/* Read register REGNO into memory at MYADDR, which must be large enough
+ for REGISTER_RAW_BYTES (REGNO). Target byte-order.
+ If the register is known to be the size of a CORE_ADDR or smaller,
+ read_register can be used instead. */
+void
+read_register_gen (regno, myaddr)
+ int regno;
+ char *myaddr;
+{
+ if (!register_valid[regno])
+ target_fetch_registers (regno);
+ memcpy (myaddr, &registers[REGISTER_BYTE (regno)],
+ REGISTER_RAW_SIZE (regno));
+}
+
+/* Copy LEN bytes of consecutive data from memory at MYADDR
+ into registers starting with the REGBYTE'th byte of register data. */
+
+void
+write_register_bytes (regbyte, myaddr, len)
+ int regbyte;
+ char *myaddr;
+ int len;
+{
+ /* Make sure the entire registers array is valid. */
+ read_register_bytes (0, (char *)NULL, REGISTER_BYTES);
+ memcpy (&registers[regbyte], myaddr, len);
+ target_store_registers (-1);
+}
+
+/* Return the raw contents of register REGNO, regarding it as an integer. */
+/* This probably should be returning LONGEST rather than CORE_ADDR. */
+
+CORE_ADDR
+read_register (regno)
+ int regno;
+{
+ if (!register_valid[regno])
+ target_fetch_registers (regno);
+
+ return extract_address (&registers[REGISTER_BYTE (regno)],
+ REGISTER_RAW_SIZE(regno));
+}
+
+/* Registers we shouldn't try to store. */
+#if !defined (CANNOT_STORE_REGISTER)
+#define CANNOT_STORE_REGISTER(regno) 0
+#endif
+
+/* Store VALUE, into the raw contents of register number REGNO. */
+/* FIXME: The val arg should probably be a LONGEST. */
+
+void
+write_register (regno, val)
+ int regno;
+ LONGEST val;
+{
+ PTR buf;
+ int size;
+
+ /* On the sparc, writing %g0 is a no-op, so we don't even want to change
+ the registers array if something writes to this register. */
+ if (CANNOT_STORE_REGISTER (regno))
+ return;
+
+ size = REGISTER_RAW_SIZE(regno);
+ buf = alloca (size);
+ store_signed_integer (buf, size, (LONGEST) val);
+
+ /* If we have a valid copy of the register, and new value == old value,
+ then don't bother doing the actual store. */
+
+ if (register_valid [regno])
+ {
+ if (memcmp (&registers[REGISTER_BYTE (regno)], buf, size) == 0)
+ return;
+ }
+
+ target_prepare_to_store ();
+
+ memcpy (&registers[REGISTER_BYTE (regno)], buf, size);
+
+ register_valid [regno] = 1;
+
+ target_store_registers (regno);
+}
+
+/* Record that register REGNO contains VAL.
+ This is used when the value is obtained from the inferior or core dump,
+ so there is no need to store the value there. */
+
+void
+supply_register (regno, val)
+ int regno;
+ char *val;
+{
+ register_valid[regno] = 1;
+ memcpy (&registers[REGISTER_BYTE (regno)], val, REGISTER_RAW_SIZE (regno));
+
+ /* On some architectures, e.g. HPPA, there are a few stray bits in some
+ registers, that the rest of the code would like to ignore. */
+#ifdef CLEAN_UP_REGISTER_VALUE
+ CLEAN_UP_REGISTER_VALUE(regno, &registers[REGISTER_BYTE(regno)]);
+#endif
+}
+
+/* Will calling read_var_value or locate_var_value on SYM end
+ up caring what frame it is being evaluated relative to? SYM must
+ be non-NULL. */
+int
+symbol_read_needs_frame (sym)
+ struct symbol *sym;
+{
+ switch (SYMBOL_CLASS (sym))
+ {
+ /* All cases listed explicitly so that gcc -Wall will detect it if
+ we failed to consider one. */
+ case LOC_REGISTER:
+ case LOC_ARG:
+ case LOC_REF_ARG:
+ case LOC_REGPARM:
+ case LOC_REGPARM_ADDR:
+ case LOC_LOCAL:
+ case LOC_LOCAL_ARG:
+ case LOC_BASEREG:
+ case LOC_BASEREG_ARG:
+ return 1;
+
+ case LOC_UNDEF:
+ case LOC_CONST:
+ case LOC_STATIC:
+ case LOC_TYPEDEF:
+
+ case LOC_LABEL:
+ /* Getting the address of a label can be done independently of the block,
+ even if some *uses* of that address wouldn't work so well without
+ the right frame. */
+
+ case LOC_BLOCK:
+ case LOC_CONST_BYTES:
+ case LOC_OPTIMIZED_OUT:
+ return 0;
+ }
+}
+
+/* Given a struct symbol for a variable,
+ and a stack frame id, read the value of the variable
+ and return a (pointer to a) struct value containing the value.
+ If the variable cannot be found, return a zero pointer.
+ If FRAME is NULL, use the selected_frame. */
+
+value
+read_var_value (var, frame)
+ register struct symbol *var;
+ FRAME frame;
+{
+ register value v;
+ struct frame_info *fi;
+ struct type *type = SYMBOL_TYPE (var);
+ CORE_ADDR addr;
+ register int len;
+
+ v = allocate_value (type);
+ VALUE_LVAL (v) = lval_memory; /* The most likely possibility. */
+ len = TYPE_LENGTH (type);
+
+ if (frame == 0) frame = selected_frame;
+
+ switch (SYMBOL_CLASS (var))
+ {
+ case LOC_CONST:
+ /* Put the constant back in target format. */
+ store_signed_integer (VALUE_CONTENTS_RAW (v), len,
+ (LONGEST) SYMBOL_VALUE (var));
+ VALUE_LVAL (v) = not_lval;
+ return v;
+
+ case LOC_LABEL:
+ /* Put the constant back in target format. */
+ store_address (VALUE_CONTENTS_RAW (v), len, SYMBOL_VALUE_ADDRESS (var));
+ VALUE_LVAL (v) = not_lval;
+ return v;
+
+ case LOC_CONST_BYTES:
+ {
+ char *bytes_addr;
+ bytes_addr = SYMBOL_VALUE_BYTES (var);
+ memcpy (VALUE_CONTENTS_RAW (v), bytes_addr, len);
+ VALUE_LVAL (v) = not_lval;
+ return v;
+ }
+
+ case LOC_STATIC:
+ addr = SYMBOL_VALUE_ADDRESS (var);
+ break;
+
+ case LOC_ARG:
+ fi = get_frame_info (frame);
+ if (fi == NULL)
+ return 0;
+ addr = FRAME_ARGS_ADDRESS (fi);
+ if (!addr)
+ {
+ return 0;
+ }
+ addr += SYMBOL_VALUE (var);
+ break;
+
+ case LOC_REF_ARG:
+ fi = get_frame_info (frame);
+ if (fi == NULL)
+ return 0;
+ addr = FRAME_ARGS_ADDRESS (fi);
+ if (!addr)
+ {
+ return 0;
+ }
+ addr += SYMBOL_VALUE (var);
+ addr = read_memory_unsigned_integer
+ (addr, TARGET_PTR_BIT / TARGET_CHAR_BIT);
+ break;
+
+ case LOC_LOCAL:
+ case LOC_LOCAL_ARG:
+ fi = get_frame_info (frame);
+ if (fi == NULL)
+ return 0;
+ addr = FRAME_LOCALS_ADDRESS (fi);
+ addr += SYMBOL_VALUE (var);
+ break;
+
+ case LOC_BASEREG:
+ case LOC_BASEREG_ARG:
+ {
+ char buf[MAX_REGISTER_RAW_SIZE];
+ get_saved_register (buf, NULL, NULL, frame, SYMBOL_BASEREG (var),
+ NULL);
+ addr = extract_address (buf, REGISTER_RAW_SIZE (SYMBOL_BASEREG (var)));
+ addr += SYMBOL_VALUE (var);
+ break;
+ }
+
+ case LOC_TYPEDEF:
+ error ("Cannot look up value of a typedef");
+ break;
+
+ case LOC_BLOCK:
+ VALUE_ADDRESS (v) = BLOCK_START (SYMBOL_BLOCK_VALUE (var));
+ return v;
+
+ case LOC_REGISTER:
+ case LOC_REGPARM:
+ case LOC_REGPARM_ADDR:
+ {
+ struct block *b;
+
+ if (frame == NULL)
+ return 0;
+ b = get_frame_block (frame);
+
+ v = value_from_register (type, SYMBOL_VALUE (var), frame);
+
+ if (SYMBOL_CLASS (var) == LOC_REGPARM_ADDR)
+ {
+ addr = *(CORE_ADDR *)VALUE_CONTENTS (v);
+ VALUE_LVAL (v) = lval_memory;
+ }
+ else
+ return v;
+ }
+ break;
+
+ case LOC_OPTIMIZED_OUT:
+ VALUE_LVAL (v) = not_lval;
+ VALUE_OPTIMIZED_OUT (v) = 1;
+ return v;
+
+ default:
+ error ("Cannot look up value of a botched symbol.");
+ break;
+ }
+
+ VALUE_ADDRESS (v) = addr;
+ VALUE_LAZY (v) = 1;
+ return v;
+}
+
+/* Return a value of type TYPE, stored in register REGNUM, in frame
+ FRAME. */
+
+value
+value_from_register (type, regnum, frame)
+ struct type *type;
+ int regnum;
+ FRAME frame;
+{
+ char raw_buffer [MAX_REGISTER_RAW_SIZE];
+ char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE];
+ CORE_ADDR addr;
+ int optim;
+ value v = allocate_value (type);
+ int len = TYPE_LENGTH (type);
+ char *value_bytes = 0;
+ int value_bytes_copied = 0;
+ int num_storage_locs;
+ enum lval_type lval;
+
+ VALUE_REGNO (v) = regnum;
+
+ num_storage_locs = (len > REGISTER_VIRTUAL_SIZE (regnum) ?
+ ((len - 1) / REGISTER_RAW_SIZE (regnum)) + 1 :
+ 1);
+
+ if (num_storage_locs > 1
+#ifdef GDB_TARGET_IS_H8500
+ || TYPE_CODE (type) == TYPE_CODE_PTR
+#endif
+ )
+ {
+ /* Value spread across multiple storage locations. */
+
+ int local_regnum;
+ int mem_stor = 0, reg_stor = 0;
+ int mem_tracking = 1;
+ CORE_ADDR last_addr = 0;
+ CORE_ADDR first_addr = 0;
+
+ value_bytes = (char *) alloca (len + MAX_REGISTER_RAW_SIZE);
+
+ /* Copy all of the data out, whereever it may be. */
+
+#ifdef GDB_TARGET_IS_H8500
+/* This piece of hideosity is required because the H8500 treats registers
+ differently depending upon whether they are used as pointers or not. As a
+ pointer, a register needs to have a page register tacked onto the front.
+ An alternate way to do this would be to have gcc output different register
+ numbers for the pointer & non-pointer form of the register. But, it
+ doesn't, so we're stuck with this. */
+
+ if (TYPE_CODE (type) == TYPE_CODE_PTR
+ && len > 2)
+ {
+ int page_regnum;
+
+ switch (regnum)
+ {
+ case R0_REGNUM: case R1_REGNUM: case R2_REGNUM: case R3_REGNUM:
+ page_regnum = SEG_D_REGNUM;
+ break;
+ case R4_REGNUM: case R5_REGNUM:
+ page_regnum = SEG_E_REGNUM;
+ break;
+ case R6_REGNUM: case R7_REGNUM:
+ page_regnum = SEG_T_REGNUM;
+ break;
+ }
+
+ value_bytes[0] = 0;
+ get_saved_register (value_bytes + 1,
+ &optim,
+ &addr,
+ frame,
+ page_regnum,
+ &lval);
+
+ if (lval == lval_register)
+ reg_stor++;
+ else
+ mem_stor++;
+ first_addr = addr;
+ last_addr = addr;
+
+ get_saved_register (value_bytes + 2,
+ &optim,
+ &addr,
+ frame,
+ regnum,
+ &lval);
+
+ if (lval == lval_register)
+ reg_stor++;
+ else
+ {
+ mem_stor++;
+ mem_tracking = mem_tracking && (addr == last_addr);
+ }
+ last_addr = addr;
+ }
+ else
+#endif /* GDB_TARGET_IS_H8500 */
+ for (local_regnum = regnum;
+ value_bytes_copied < len;
+ (value_bytes_copied += REGISTER_RAW_SIZE (local_regnum),
+ ++local_regnum))
+ {
+ get_saved_register (value_bytes + value_bytes_copied,
+ &optim,
+ &addr,
+ frame,
+ local_regnum,
+ &lval);
+
+ if (regnum == local_regnum)
+ first_addr = addr;
+ if (lval == lval_register)
+ reg_stor++;
+ else
+ {
+ mem_stor++;
+
+ mem_tracking =
+ (mem_tracking
+ && (regnum == local_regnum
+ || addr == last_addr));
+ }
+ last_addr = addr;
+ }
+
+ if ((reg_stor && mem_stor)
+ || (mem_stor && !mem_tracking))
+ /* Mixed storage; all of the hassle we just went through was
+ for some good purpose. */
+ {
+ VALUE_LVAL (v) = lval_reg_frame_relative;
+ VALUE_FRAME (v) = FRAME_FP (frame);
+ VALUE_FRAME_REGNUM (v) = regnum;
+ }
+ else if (mem_stor)
+ {
+ VALUE_LVAL (v) = lval_memory;
+ VALUE_ADDRESS (v) = first_addr;
+ }
+ else if (reg_stor)
+ {
+ VALUE_LVAL (v) = lval_register;
+ VALUE_ADDRESS (v) = first_addr;
+ }
+ else
+ fatal ("value_from_register: Value not stored anywhere!");
+
+ VALUE_OPTIMIZED_OUT (v) = optim;
+
+ /* Any structure stored in more than one register will always be
+ an integral number of registers. Otherwise, you'd need to do
+ some fiddling with the last register copied here for little
+ endian machines. */
+
+ /* Copy into the contents section of the value. */
+ memcpy (VALUE_CONTENTS_RAW (v), value_bytes, len);
+
+ /* Finally do any conversion necessary when extracting this
+ type from more than one register. */
+#ifdef REGISTER_CONVERT_TO_TYPE
+ REGISTER_CONVERT_TO_TYPE(regnum, type, VALUE_CONTENTS_RAW(v));
+#endif
+ return v;
+ }
+
+ /* Data is completely contained within a single register. Locate the
+ register's contents in a real register or in core;
+ read the data in raw format. */
+
+ get_saved_register (raw_buffer, &optim, &addr, frame, regnum, &lval);
+ VALUE_OPTIMIZED_OUT (v) = optim;
+ VALUE_LVAL (v) = lval;
+ VALUE_ADDRESS (v) = addr;
+
+ /* Convert the raw contents to virtual contents.
+ (Just copy them if the formats are the same.) */
+
+ REGISTER_CONVERT_TO_VIRTUAL (regnum, raw_buffer, virtual_buffer);
+
+ if (REGISTER_CONVERTIBLE (regnum))
+ {
+ /* When the raw and virtual formats differ, the virtual format
+ corresponds to a specific data type. If we want that type,
+ copy the data into the value.
+ Otherwise, do a type-conversion. */
+
+ if (type != REGISTER_VIRTUAL_TYPE (regnum))
+ {
+ /* eg a variable of type `float' in a 68881 register
+ with raw type `extended' and virtual type `double'.
+ Fetch it as a `double' and then convert to `float'. */
+ /* FIXME: This value will be not_lval, which means we can't assign
+ to it. Probably the right fix is to do the cast on a temporary
+ value, and just copy the VALUE_CONTENTS over. */
+ v = allocate_value (REGISTER_VIRTUAL_TYPE (regnum));
+ memcpy (VALUE_CONTENTS_RAW (v), virtual_buffer,
+ REGISTER_VIRTUAL_SIZE (regnum));
+ v = value_cast (type, v);
+ }
+ else
+ memcpy (VALUE_CONTENTS_RAW (v), virtual_buffer, len);
+ }
+ else
+ {
+ /* Raw and virtual formats are the same for this register. */
+
+#if TARGET_BYTE_ORDER == BIG_ENDIAN
+ if (len < REGISTER_RAW_SIZE (regnum))
+ {
+ /* Big-endian, and we want less than full size. */
+ VALUE_OFFSET (v) = REGISTER_RAW_SIZE (regnum) - len;
+ }
+#endif
+
+ memcpy (VALUE_CONTENTS_RAW (v), virtual_buffer + VALUE_OFFSET (v), len);
+ }
+
+ return v;
+}
+
+/* Given a struct symbol for a variable or function,
+ and a stack frame id,
+ return a (pointer to a) struct value containing the properly typed
+ address. */
+
+value
+locate_var_value (var, frame)
+ register struct symbol *var;
+ FRAME frame;
+{
+ CORE_ADDR addr = 0;
+ struct type *type = SYMBOL_TYPE (var);
+ value lazy_value;
+
+ /* Evaluate it first; if the result is a memory address, we're fine.
+ Lazy evaluation pays off here. */
+
+ lazy_value = read_var_value (var, frame);
+ if (lazy_value == 0)
+ error ("Address of \"%s\" is unknown.", SYMBOL_SOURCE_NAME (var));
+
+ if (VALUE_LAZY (lazy_value)
+ || TYPE_CODE (type) == TYPE_CODE_FUNC)
+ {
+ addr = VALUE_ADDRESS (lazy_value);
+ return value_from_longest (lookup_pointer_type (type), (LONGEST) addr);
+ }
+
+ /* Not a memory address; check what the problem was. */
+ switch (VALUE_LVAL (lazy_value))
+ {
+ case lval_register:
+ case lval_reg_frame_relative:
+ error ("Address requested for identifier \"%s\" which is in a register.",
+ SYMBOL_SOURCE_NAME (var));
+ break;
+
+ default:
+ error ("Can't take address of \"%s\" which isn't an lvalue.",
+ SYMBOL_SOURCE_NAME (var));
+ break;
+ }
+ return 0; /* For lint -- never reached */
+}
diff --git a/gnu/usr.bin/gdb/gdb/fopen-same.h b/gnu/usr.bin/gdb/gdb/fopen-same.h
new file mode 100644
index 0000000..0f37529
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/fopen-same.h
@@ -0,0 +1,27 @@
+/* Macros for the 'type' part of an fopen, freopen or fdopen.
+
+ <Read|Write>[Update]<Binary file|text file>
+
+ This version is for "same" systems, where text and binary files are
+ the same. An example is Unix. Many Unix systems could also add a
+ "b" to the string, indicating binary files, but some reject this
+ (and thereby don't conform to ANSI C, but what else is new?).
+
+ This file is designed for inclusion by host-dependent .h files. No
+ user application should include it directly, since that would make
+ the application unable to be configured for both "same" and "binary"
+ variant systems. */
+
+#define FOPEN_RB "r"
+#define FOPEN_WB "w"
+#define FOPEN_AB "a"
+#define FOPEN_RUB "r+"
+#define FOPEN_WUB "w+"
+#define FOPEN_AUB "a+"
+
+#define FOPEN_RT "r"
+#define FOPEN_WT "w"
+#define FOPEN_AT "a"
+#define FOPEN_RUT "r+"
+#define FOPEN_WUT "w+"
+#define FOPEN_AUT "a+"
diff --git a/gnu/usr.bin/gdb/gdb/fork-child.c b/gnu/usr.bin/gdb/gdb/fork-child.c
new file mode 100644
index 0000000..3c01b60
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/fork-child.c
@@ -0,0 +1,310 @@
+/* Fork a Unix child process, and set up to debug it, for GDB.
+ Copyright 1990, 1991, 1992 Free Software Foundation, Inc.
+ Contributed by Cygnus Support.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "frame.h" /* required by inferior.h */
+#include "inferior.h"
+#include "target.h"
+#include "wait.h"
+#include "gdbcore.h"
+#include "terminal.h"
+
+#include <signal.h>
+
+#ifdef SET_STACK_LIMIT_HUGE
+#include <sys/time.h>
+#include <sys/resource.h>
+
+extern int original_stack_limit;
+#endif /* SET_STACK_LIMIT_HUGE */
+
+extern char **environ;
+
+/* Start an inferior Unix child process and sets inferior_pid to its pid.
+ EXEC_FILE is the file to run.
+ ALLARGS is a string containing the arguments to the program.
+ ENV is the environment vector to pass. Errors reported with error(). */
+
+#ifndef SHELL_FILE
+#define SHELL_FILE "/bin/sh"
+#endif
+
+void
+fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun)
+ char *exec_file;
+ char *allargs;
+ char **env;
+ void (*traceme_fun) PARAMS ((void));
+ void (*init_trace_fun) PARAMS ((int));
+{
+ int pid;
+ char *shell_command;
+ char *shell_file;
+ static char default_shell_file[] = SHELL_FILE;
+ int len;
+ int pending_execs;
+ int terminal_initted;
+ /* Set debug_fork then attach to the child while it sleeps, to debug. */
+ static int debug_fork = 0;
+ /* This is set to the result of setpgrp, which if vforked, will be visible
+ to you in the parent process. It's only used by humans for debugging. */
+ static int debug_setpgrp = 657473;
+ char **save_our_env;
+
+ /* If no exec file handed to us, get it from the exec-file command -- with
+ a good, common error message if none is specified. */
+ if (exec_file == 0)
+ exec_file = get_exec_file(1);
+
+ /* The user might want tilde-expansion, and in general probably wants
+ the program to behave the same way as if run from
+ his/her favorite shell. So we let the shell run it for us.
+ FIXME, this should probably search the local environment (as
+ modified by the setenv command), not the env gdb inherited. */
+ shell_file = getenv ("SHELL");
+ if (shell_file == NULL)
+ shell_file = default_shell_file;
+
+ /* Multiplying the length of exec_file by 4 is to account for the fact
+ that it may expand when quoted; it is a worst-case number based on
+ every character being '. */
+ len = 5 + 4 * strlen (exec_file) + 1 + strlen (allargs) + 1 + /*slop*/ 12;
+ /* If desired, concat something onto the front of ALLARGS.
+ SHELL_COMMAND is the result. */
+#ifdef SHELL_COMMAND_CONCAT
+ shell_command = (char *) alloca (strlen (SHELL_COMMAND_CONCAT) + len);
+ strcpy (shell_command, SHELL_COMMAND_CONCAT);
+#else
+ shell_command = (char *) alloca (len);
+ shell_command[0] = '\0';
+#endif
+ strcat (shell_command, "exec ");
+
+ /* Now add exec_file, quoting as necessary. */
+ {
+ char *p;
+ int need_to_quote;
+
+ /* Quoting in this style is said to work with all shells. But csh
+ on IRIX 4.0.1 can't deal with it. So we only quote it if we need
+ to. */
+ p = exec_file;
+ while (1)
+ {
+ switch (*p)
+ {
+ case '\'':
+ case '"':
+ case '(':
+ case ')':
+ case '$':
+ case '&':
+ case ';':
+ case '<':
+ case '>':
+ case ' ':
+ case '\n':
+ case '\t':
+ need_to_quote = 1;
+ goto end_scan;
+
+ case '\0':
+ need_to_quote = 0;
+ goto end_scan;
+
+ default:
+ break;
+ }
+ ++p;
+ }
+ end_scan:
+ if (need_to_quote)
+ {
+ strcat (shell_command, "'");
+ for (p = exec_file; *p != '\0'; ++p)
+ {
+ if (*p == '\'')
+ strcat (shell_command, "'\\''");
+ else
+ strncat (shell_command, p, 1);
+ }
+ strcat (shell_command, "'");
+ }
+ else
+ strcat (shell_command, exec_file);
+ }
+
+ strcat (shell_command, " ");
+ strcat (shell_command, allargs);
+
+ /* exec is said to fail if the executable is open. */
+ close_exec_file ();
+
+ /* Retain a copy of our environment variables, since the child will
+ replace the value of environ and if we're vforked, we have to
+ restore it. */
+ save_our_env = environ;
+
+ /* Tell the terminal handling subsystem what tty we plan to run on;
+ it will just record the information for later. */
+
+ new_tty_prefork (inferior_io_terminal);
+
+ /* It is generally good practice to flush any possible pending stdio
+ output prior to doing a fork, to avoid the possibility of both the
+ parent and child flushing the same data after the fork. */
+
+ fflush (stdout);
+ fflush (stderr);
+
+#if defined(USG) && !defined(HAVE_VFORK)
+ pid = fork ();
+#else
+ if (debug_fork)
+ pid = fork ();
+ else
+ pid = vfork ();
+#endif
+
+ if (pid < 0)
+ perror_with_name ("vfork");
+
+ if (pid == 0)
+ {
+ if (debug_fork)
+ sleep (debug_fork);
+
+ /* Run inferior in a separate process group. */
+ debug_setpgrp = gdb_setpgid ();
+ if (debug_setpgrp == -1)
+ perror("setpgrp failed in child");
+
+#ifdef SET_STACK_LIMIT_HUGE
+ /* Reset the stack limit back to what it was. */
+ {
+ struct rlimit rlim;
+
+ getrlimit (RLIMIT_STACK, &rlim);
+ rlim.rlim_cur = original_stack_limit;
+ setrlimit (RLIMIT_STACK, &rlim);
+ }
+#endif /* SET_STACK_LIMIT_HUGE */
+
+ /* Ask the tty subsystem to switch to the one we specified earlier
+ (or to share the current terminal, if none was specified). */
+
+ new_tty ();
+
+ /* Changing the signal handlers for the inferior after
+ a vfork can also change them for the superior, so we don't mess
+ with signals here. See comments in
+ initialize_signals for how we get the right signal handlers
+ for the inferior. */
+
+ /* "Trace me, Dr. Memory!" */
+ (*traceme_fun) ();
+
+ /* There is no execlpe call, so we have to set the environment
+ for our child in the global variable. If we've vforked, this
+ clobbers the parent, but environ is restored a few lines down
+ in the parent. By the way, yes we do need to look down the
+ path to find $SHELL. Rich Pixley says so, and I agree. */
+ environ = env;
+ execlp (shell_file, shell_file, "-c", shell_command, (char *)0);
+
+ fprintf (stderr, "Cannot exec %s: %s.\n", shell_file,
+ safe_strerror (errno));
+ fflush (stderr);
+ _exit (0177);
+ }
+
+ /* Restore our environment in case a vforked child clob'd it. */
+ environ = save_our_env;
+
+ init_thread_list();
+
+ /* Now that we have a child process, make it our target, and
+ initialize anything target-vector-specific that needs initializing. */
+ (*init_trace_fun)(pid);
+
+ /* The process was started by the fork that created it,
+ but it will have stopped one instruction after execing the shell.
+ Here we must get it up to actual execution of the real program. */
+
+ inferior_pid = pid; /* Needed for wait_for_inferior stuff below */
+
+ clear_proceed_status ();
+
+ /* We will get a trace trap after one instruction.
+ Continue it automatically. Eventually (after shell does an exec)
+ it will get another trace trap. Then insert breakpoints and continue. */
+
+#ifdef START_INFERIOR_TRAPS_EXPECTED
+ pending_execs = START_INFERIOR_TRAPS_EXPECTED;
+#else
+ pending_execs = 2;
+#endif
+
+ init_wait_for_inferior ();
+
+ terminal_initted = 0;
+
+ while (1)
+ {
+ stop_soon_quietly = 1; /* Make wait_for_inferior be quiet */
+ wait_for_inferior ();
+ if (stop_signal != SIGTRAP)
+ {
+ /* Let shell child handle its own signals in its own way */
+ /* FIXME, what if child has exit()ed? Must exit loop somehow */
+ resume (0, stop_signal);
+ }
+ else
+ {
+ /* We handle SIGTRAP, however; it means child did an exec. */
+ if (!terminal_initted)
+ {
+ /* Now that the child has exec'd we know it has already set its
+ process group. On POSIX systems, tcsetpgrp will fail with
+ EPERM if we try it before the child's setpgid. */
+
+ /* Set up the "saved terminal modes" of the inferior
+ based on what modes we are starting it with. */
+ target_terminal_init ();
+
+ /* Install inferior's terminal modes. */
+ target_terminal_inferior ();
+
+ terminal_initted = 1;
+ }
+ if (0 == --pending_execs)
+ break;
+ resume (0, 0); /* Just make it go on */
+ }
+ }
+ stop_soon_quietly = 0;
+
+ /* We are now in the child process of interest, having exec'd the
+ correct program, and are poised at the first instruction of the
+ new program. */
+#ifdef SOLIB_CREATE_INFERIOR_HOOK
+ SOLIB_CREATE_INFERIOR_HOOK (pid);
+#endif
+}
diff --git a/gnu/usr.bin/gdb/gdb/frame.h b/gnu/usr.bin/gdb/gdb/frame.h
new file mode 100644
index 0000000..1fe6fb6
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/frame.h
@@ -0,0 +1,238 @@
+/* Definitions for dealing with stack frames, for GDB, the GNU debugger.
+ Copyright 1986, 1989, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (FRAME_H)
+#define FRAME_H 1
+
+/* A FRAME identifies a specific stack frame. It is not constant over
+ calls to the inferior (frame addresses are, see below).
+
+ This is implemented as a "struct frame_info *". This file and
+ blockframe.c are the only places which are allowed to use the
+ equivalence between FRAME and struct frame_info *. Exception:
+ Prototypes in other files use "struct frame_info *" because this
+ file might not be included.
+
+ The distinction between a FRAME and a "struct frame_info *" is made
+ with the idea of maybe someday changing a FRAME to be something else,
+ but seems to me that a "struct frame_info *" is fully general (since
+ any necessarily fields can be added; changing the meaning of existing
+ fields is not helped by the FRAME distinction), and this distinction
+ merely creates unnecessary hair. -kingdon, 18 May 93. */
+typedef struct frame_info *FRAME;
+
+/* Convert from a "struct frame_info *" into a FRAME. */
+#define FRAME_INFO_ID(f) (f)
+
+/* Convert from a FRAME into a "struct frame_info *". */
+extern struct frame_info *
+get_frame_info PARAMS ((FRAME));
+
+/* Type of the address of a frame. It is widely assumed (at least in
+ prototypes in headers which might not include this header) that
+ this is the same as CORE_ADDR, and no one can think of a case in
+ which it wouldn't be, so it might be best to remove this typedef. */
+typedef CORE_ADDR FRAME_ADDR;
+
+/* Convert from a FRAME into a frame address. Except in the
+ machine-dependent *FRAME* macros, a frame address has no defined
+ meaning other than as a magic cookie which identifies a frame over
+ calls to the inferior. The only known exception is inferior.h
+ (PC_IN_CALL_DUMMY) [ON_STACK]; see comments there. You cannot
+ assume that a frame address contains enough information to
+ reconstruct the frame; if you want more than just to identify the
+ frame (e.g. be able to fetch variables relative to that frame),
+ then save the whole struct frame_info (and the next struct
+ frame_info, since the latter is used for fetching variables on some
+ machines). */
+
+#define FRAME_FP(fr) ((fr)->frame)
+
+/* We keep a cache of stack frames, each of which is a "struct
+ frame_info". The innermost one gets allocated (in
+ wait_for_inferior) each time the inferior stops; current_frame
+ points to it. Additional frames get allocated (in
+ get_prev_frame_info) as needed, and are chained through the next
+ and prev fields. Any time that the frame cache becomes invalid
+ (most notably when we execute something, but also if we change how
+ we interpret the frames (e.g. "set heuristic-fence-post" in
+ mips-tdep.c, or anything which reads new symbols)), we should call
+ reinit_frame_cache. */
+
+struct frame_info
+ {
+ /* Nominal address of the frame described. See comments at FRAME_FP
+ about what this means outside the *FRAME* macros; in the *FRAME*
+ macros, it can mean whatever makes most sense for this machine. */
+ FRAME_ADDR frame;
+
+ /* Address at which execution is occurring in this frame.
+ For the innermost frame, it's the current pc.
+ For other frames, it is a pc saved in the next frame. */
+ CORE_ADDR pc;
+
+ /* Nonzero if this is a frame associated with calling a signal handler.
+
+ Set by machine-dependent code. On some machines, if
+ the machine-dependent code fails to check for this, the backtrace
+ will look relatively normal. For example, on the i386
+ #3 0x158728 in sighold ()
+ On other machines (e.g. rs6000), the machine-dependent code better
+ set this to prevent us from trying to print it like a normal frame. */
+ int signal_handler_caller;
+
+ /* Anything extra for this structure that may have been defined
+ in the machine dependent files. */
+#ifdef EXTRA_FRAME_INFO
+ EXTRA_FRAME_INFO
+#endif
+
+ /* We should probably also store a "struct frame_saved_regs" here.
+ This is already done by some machines (e.g. config/m88k/tm-m88k.h)
+ but there is no reason it couldn't be general. */
+
+ /* Pointers to the next and previous frame_info's in the frame cache. */
+ FRAME next, prev;
+ };
+
+/* Describe the saved registers of a frame. */
+
+struct frame_saved_regs
+ {
+
+ /* For each register, address of where it was saved on entry to
+ the frame, or zero if it was not saved on entry to this frame.
+ This includes special registers such as pc and fp saved in
+ special ways in the stack frame. The SP_REGNUM is even more
+ special, the address here is the sp for the next frame, not the
+ address where the sp was saved. */
+
+ CORE_ADDR regs[NUM_REGS];
+ };
+
+/* Define a default FRAME_CHAIN_VALID, in the form that is suitable for most
+ targets. If FRAME_CHAIN_VALID returns zero it means that the given frame
+ is the outermost one and has no caller.
+
+ If a particular target needs a different definition, then it can override
+ the definition here by providing one in the tm file. */
+
+#if !defined (FRAME_CHAIN_VALID)
+
+#if defined (FRAME_CHAIN_VALID_ALTERNATE)
+
+/* Use the alternate method of avoiding running up off the end of the frame
+ chain or following frames back into the startup code. See the comments
+ in objfiles.h. */
+
+#define FRAME_CHAIN_VALID(chain, thisframe) \
+ ((chain) != 0 \
+ && !inside_main_func ((thisframe) -> pc) \
+ && !inside_entry_func ((thisframe) -> pc))
+
+#else
+
+#define FRAME_CHAIN_VALID(chain, thisframe) \
+ ((chain) != 0 \
+ && !inside_entry_file (FRAME_SAVED_PC (thisframe)))
+
+#endif /* FRAME_CHAIN_VALID_ALTERNATE */
+
+#endif /* FRAME_CHAIN_VALID */
+
+/* The stack frame that the user has specified for commands to act on.
+ Note that one cannot assume this is the address of valid data. */
+
+extern FRAME selected_frame;
+
+/* Level of the selected frame:
+ 0 for innermost, 1 for its caller, ...
+ or -1 for frame specified by address with no defined level. */
+
+extern int selected_frame_level;
+
+extern struct frame_info *
+get_prev_frame_info PARAMS ((FRAME));
+
+extern FRAME
+create_new_frame PARAMS ((FRAME_ADDR, CORE_ADDR));
+
+extern void
+flush_cached_frames PARAMS ((void));
+
+extern void
+reinit_frame_cache PARAMS ((void));
+
+extern void
+get_frame_saved_regs PARAMS ((struct frame_info *, struct frame_saved_regs *));
+
+extern void
+set_current_frame PARAMS ((FRAME));
+
+extern FRAME
+get_prev_frame PARAMS ((FRAME));
+
+extern FRAME
+get_current_frame PARAMS ((void));
+
+extern FRAME
+get_next_frame PARAMS ((FRAME));
+
+extern struct block *
+get_frame_block PARAMS ((FRAME));
+
+extern struct block *
+get_current_block PARAMS ((void));
+
+extern struct block *
+get_selected_block PARAMS ((void));
+
+extern struct symbol *
+get_frame_function PARAMS ((FRAME));
+
+extern CORE_ADDR
+get_frame_pc PARAMS ((FRAME));
+
+extern CORE_ADDR
+get_pc_function_start PARAMS ((CORE_ADDR));
+
+extern struct block * block_for_pc PARAMS ((CORE_ADDR));
+
+extern int frameless_look_for_prologue PARAMS ((FRAME));
+
+extern void print_frame_args PARAMS ((struct symbol *, struct frame_info *,
+ int, FILE *));
+
+extern FRAME find_relative_frame PARAMS ((FRAME, int*));
+
+extern void print_stack_frame PARAMS ((FRAME, int, int));
+
+extern void select_frame PARAMS ((FRAME, int));
+
+extern void record_selected_frame PARAMS ((FRAME_ADDR *, int *));
+
+extern void print_frame_info PARAMS ((struct frame_info *, int, int, int));
+
+extern CORE_ADDR find_saved_register PARAMS ((FRAME, int));
+
+extern FRAME block_innermost_frame PARAMS ((struct block *));
+
+extern CORE_ADDR sigtramp_saved_pc PARAMS ((FRAME));
+
+#endif /* !defined (FRAME_H) */
diff --git a/gnu/usr.bin/gdb/gdb/freebsd-nat.c b/gnu/usr.bin/gdb/gdb/freebsd-nat.c
new file mode 100644
index 0000000..deb68eb
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/freebsd-nat.c
@@ -0,0 +1,323 @@
+/* Native-dependent code for BSD Unix running on i386's, for GDB.
+ Copyright 1988, 1989, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include <machine/reg.h>
+
+/* this table must line up with REGISTER_NAMES in tm-i386.h */
+/* symbols like 'tEAX' come from <machine/reg.h> */
+static int tregmap[] =
+{
+ tEAX, tECX, tEDX, tEBX,
+ tESP, tEBP, tESI, tEDI,
+ tEIP, tEFLAGS, tCS, tSS
+};
+#ifdef sEAX
+static int sregmap[] =
+{
+ sEAX, sECX, sEDX, sEBX,
+ sESP, sEBP, sESI, sEDI,
+ sEIP, sEFLAGS, sCS, sSS
+};
+#endif
+/* blockend is the value of u.u_ar0, and points to the
+ place where ES is stored. */
+
+int
+i386_register_u_addr (blockend, regnum)
+ int blockend;
+ int regnum;
+{
+ /* The following condition is a kludge to get at the proper register map
+ depending upon the state of pcb_flag.
+ The proper condition would be
+ if (u.u_pcb.pcb_flag & FM_TRAP)
+ but that would require a ptrace call here and wouldn't work
+ for corefiles. */
+
+#ifdef sEAX
+ if (blockend < 0x1fcc)
+ return (blockend + 4 * tregmap[regnum]);
+ else
+ return (blockend + 4 * sregmap[regnum]);
+#else
+ return (blockend + 4 * tregmap[regnum]);
+#endif
+}
+
+#ifdef FLOAT_INFO
+#include <sys/param.h>
+#include <sys/dir.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#include <a.out.h>
+
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/uio.h>
+#define curpcb Xcurpcb /* XXX avoid leaking declaration from pcb.h */
+#include <sys/user.h>
+#undef curpcb
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/ptrace.h>
+
+#define fpstate save87
+#define U_FPSTATE(u) u.u_pcb.pcb_savefpu
+
+i387_to_double (from, to)
+ char *from;
+ char *to;
+{
+ long *lp;
+ /* push extended mode on 387 stack, then pop in double mode
+ *
+ * first, set exception masks so no error is generated -
+ * number will be rounded to inf or 0, if necessary
+ */
+ asm ("pushl %eax"); /* grab a stack slot */
+ asm ("fstcw (%esp)"); /* get 387 control word */
+ asm ("movl (%esp),%eax"); /* save old value */
+ asm ("orl $0x3f,%eax"); /* mask all exceptions */
+ asm ("pushl %eax");
+ asm ("fldcw (%esp)"); /* load new value into 387 */
+
+ asm ("movl 8(%ebp),%eax");
+ asm ("fldt (%eax)"); /* push extended number on 387 stack */
+ asm ("fwait");
+ asm ("movl 12(%ebp),%eax");
+ asm ("fstpl (%eax)"); /* pop double */
+ asm ("fwait");
+
+ asm ("popl %eax"); /* flush modified control word */
+ asm ("fnclex"); /* clear exceptions */
+ asm ("fldcw (%esp)"); /* restore original control word */
+ asm ("popl %eax"); /* flush saved copy */
+}
+
+double_to_i387 (from, to)
+ char *from;
+ char *to;
+{
+ /* push double mode on 387 stack, then pop in extended mode
+ * no errors are possible because every 64-bit pattern
+ * can be converted to an extended
+ */
+ asm ("movl 8(%ebp),%eax");
+ asm ("fldl (%eax)");
+ asm ("fwait");
+ asm ("movl 12(%ebp),%eax");
+ asm ("fstpt (%eax)");
+ asm ("fwait");
+}
+
+struct env387
+{
+ unsigned short control;
+ unsigned short r0;
+ unsigned short status;
+ unsigned short r1;
+ unsigned short tag;
+ unsigned short r2;
+ unsigned long eip;
+ unsigned short code_seg;
+ unsigned short opcode;
+ unsigned long operand;
+ unsigned short operand_seg;
+ unsigned short r3;
+ unsigned char regs[8][10];
+};
+
+void
+print_387_control_word (control)
+unsigned int control;
+{
+ printf ("control 0x%04x: ", control);
+ printf ("compute to ");
+ switch ((control >> 8) & 3)
+ {
+ case 0: printf ("24 bits; "); break;
+ case 1: printf ("(bad); "); break;
+ case 2: printf ("53 bits; "); break;
+ case 3: printf ("64 bits; "); break;
+ }
+ printf ("round ");
+ switch ((control >> 10) & 3)
+ {
+ case 0: printf ("NEAREST; "); break;
+ case 1: printf ("DOWN; "); break;
+ case 2: printf ("UP; "); break;
+ case 3: printf ("CHOP; "); break;
+ }
+ if (control & 0x3f)
+ {
+ printf ("mask:");
+ if (control & 0x0001) printf (" INVALID");
+ if (control & 0x0002) printf (" DENORM");
+ if (control & 0x0004) printf (" DIVZ");
+ if (control & 0x0008) printf (" OVERF");
+ if (control & 0x0010) printf (" UNDERF");
+ if (control & 0x0020) printf (" LOS");
+ printf (";");
+ }
+ printf ("\n");
+ if (control & 0xe080) printf ("warning: reserved bits on 0x%x\n",
+ control & 0xe080);
+}
+
+void
+print_387_status_word (status)
+ unsigned int status;
+{
+ printf ("status 0x%04x: ", status);
+ if (status & 0xff)
+ {
+ printf ("exceptions:");
+ if (status & 0x0001) printf (" INVALID");
+ if (status & 0x0002) printf (" DENORM");
+ if (status & 0x0004) printf (" DIVZ");
+ if (status & 0x0008) printf (" OVERF");
+ if (status & 0x0010) printf (" UNDERF");
+ if (status & 0x0020) printf (" LOS");
+ if (status & 0x0040) printf (" FPSTACK");
+ printf ("; ");
+ }
+ printf ("flags: %d%d%d%d; ",
+ (status & 0x4000) != 0,
+ (status & 0x0400) != 0,
+ (status & 0x0200) != 0,
+ (status & 0x0100) != 0);
+
+ printf ("top %d\n", (status >> 11) & 7);
+}
+
+static
+print_387_status (status, ep)
+ unsigned short status;
+ struct env387 *ep;
+{
+ int i;
+ int bothstatus;
+ int top;
+ int fpreg;
+ unsigned char *p;
+
+ bothstatus = ((status != 0) && (ep->status != 0));
+ if (status != 0)
+ {
+ if (bothstatus)
+ printf ("u: ");
+ print_387_status_word ((unsigned int)status);
+ }
+
+ if (ep->status != 0)
+ {
+ if (bothstatus)
+ printf ("e: ");
+ print_387_status_word ((unsigned int)ep->status);
+ }
+
+ print_387_control_word ((unsigned int)ep->control);
+ printf ("last exception: ");
+ printf ("opcode 0x%x; ", ep->opcode);
+ printf ("pc 0x%x:0x%x; ", ep->code_seg, ep->eip);
+ printf ("operand 0x%x:0x%x\n", ep->operand_seg, ep->operand);
+
+ top = (ep->status >> 11) & 7;
+
+ printf (" regno tag msb lsb value\n");
+ for (fpreg = 7; fpreg >= 0; fpreg--)
+ {
+ int st_regno;
+ double val;
+
+ /* The physical regno `fpreg' is only relevant as an index into the
+ * tag word. Logical `%st' numbers are required for indexing `p->regs.
+ */
+ st_regno = (fpreg + 8 - top) & 0x7;
+
+ printf ("%%st(%d) %s ", st_regno, fpreg == top ? "=>" : " ");
+
+ switch ((ep->tag >> (fpreg * 2)) & 3)
+ {
+ case 0: printf ("valid "); break;
+ case 1: printf ("zero "); break;
+ case 2: printf ("trap "); break;
+ case 3: printf ("empty "); break;
+ }
+ for (i = 9; i >= 0; i--)
+ printf ("%02x", ep->regs[st_regno][i]);
+
+ i387_to_double (ep->regs[st_regno], (char *)&val);
+ printf (" %g\n", val);
+ }
+}
+
+i386_float_info ()
+{
+ struct user u; /* just for address computations */
+ int i;
+ /* fpstate defined in <sys/user.h> */
+ struct fpstate *fpstatep;
+ char buf[sizeof (struct fpstate) + 2 * sizeof (int)];
+ unsigned int uaddr;
+ char fpvalid;
+ unsigned int rounded_addr;
+ unsigned int rounded_size;
+ /*extern int corechan;*/
+ int skip;
+ extern int inferior_pid;
+
+ uaddr = (char *)&U_FPSTATE(u) - (char *)&u;
+ if (inferior_pid)
+ {
+ int *ip;
+
+ rounded_addr = uaddr & -sizeof (int);
+ rounded_size = (((uaddr + sizeof (struct fpstate)) - uaddr) +
+ sizeof (int) - 1) / sizeof (int);
+ skip = uaddr - rounded_addr;
+
+ ip = (int *)buf;
+ for (i = 0; i < rounded_size; i++)
+ {
+ *ip++ = ptrace (PT_READ_U, inferior_pid, (caddr_t)rounded_addr, 0);
+ rounded_addr += sizeof (int);
+ }
+ }
+ else
+ {
+ printf("float info: can't do a core file (yet)\n");
+
+ return;
+#if 0
+ if (lseek (corechan, uaddr, 0) < 0)
+ perror_with_name ("seek on core file");
+ if (myread (corechan, buf, sizeof (struct fpstate)) < 0)
+ perror_with_name ("read from core file");
+ skip = 0;
+#endif
+ }
+
+ print_387_status (0, (struct env387 *)buf);
+}
+
+#endif
diff --git a/gnu/usr.bin/gdb/gdb/freebsd-solib.c b/gnu/usr.bin/gdb/gdb/freebsd-solib.c
new file mode 100644
index 0000000..5b6e4c0
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/freebsd-solib.c
@@ -0,0 +1,1469 @@
+/* Handle SunOS and SVR4 shared libraries for GDB, the GNU Debugger.
+ Copyright 1990, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+/* modified for FreeBSD, since the names in link.h are totally different!
+ 6.1.94 */
+
+#include "defs.h"
+
+#include <sys/types.h>
+#include <signal.h>
+#include <string.h>
+#ifndef SVR4_SHARED_LIBS
+ /* SunOS shared libs need the nlist structure. */
+#include <a.out.h>
+#endif
+#include <link.h>
+#include <sys/param.h>
+#include <fcntl.h>
+
+#include "symtab.h"
+#include "bfd.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "gdbcore.h"
+#include "command.h"
+#include "target.h"
+#include "frame.h"
+#include "regex.h"
+#include "inferior.h"
+#include "language.h"
+
+#define MAX_PATH_SIZE 256 /* FIXME: Should be dynamic */
+
+/* On SVR4 systems, for the initial implementation, use some runtime startup
+ symbol as the "startup mapping complete" breakpoint address. The models
+ for SunOS and SVR4 dynamic linking debugger support are different in that
+ SunOS hits one breakpoint when all mapping is complete while using the SVR4
+ debugger support takes two breakpoint hits for each file mapped, and
+ there is no way to know when the "last" one is hit. Both these
+ mechanisms should be tied to a "breakpoint service routine" that
+ gets automatically executed whenever one of the breakpoints indicating
+ a change in mapping is hit. This is a future enhancement. (FIXME) */
+
+#define BKPT_AT_SYMBOL 1
+
+#if defined (BKPT_AT_SYMBOL) && defined (SVR4_SHARED_LIBS)
+static char *bkpt_names[] = {
+#ifdef SOLIB_BKPT_NAME
+ SOLIB_BKPT_NAME, /* Prefer configured name if it exists. */
+#endif
+ "_start",
+ "main",
+ NULL
+};
+#endif
+
+/* local data declarations */
+
+#ifndef SVR4_SHARED_LIBS
+
+#define DEBUG_BASE "_DYNAMIC"
+#ifdef OLD_FreeBSD_LD
+#define LM_ADDR(so) ((so) -> lm.lm_addr)
+#define LM_NEXT(so) ((so) -> lm.lm_next)
+#define LM_NAME(so) ((so) -> lm.lm_name)
+static struct link_dynamic dynamic_copy;
+static struct link_dynamic_2 ld_2_copy;
+static struct ld_debug debug_copy;
+#else
+#define LM_ADDR(so) ((so) -> lm.som_addr)
+#define LM_NEXT(so) ((so) -> lm.som_next)
+#define LM_NAME(so) ((so) -> lm.som_path)
+static struct _dynamic dynamic_copy;
+static struct section_dispatch_table ld_2_copy;
+static struct so_debug debug_copy;
+#endif
+static CORE_ADDR debug_addr;
+static CORE_ADDR flag_addr;
+
+#else /* SVR4_SHARED_LIBS */
+
+#define DEBUG_BASE "_r_debug"
+#define LM_ADDR(so) ((so) -> lm.l_addr)
+#define LM_NEXT(so) ((so) -> lm.l_next)
+#define LM_NAME(so) ((so) -> lm.l_name)
+static struct r_debug debug_copy;
+char shadow_contents[BREAKPOINT_MAX]; /* Stash old bkpt addr contents */
+
+#endif /* !SVR4_SHARED_LIBS */
+
+struct so_list {
+ struct so_list *next; /* next structure in linked list */
+#ifdef OLD_FreeBSD_LD
+ struct link_map lm; /* copy of link map from inferior */
+ struct link_map *lmaddr; /* addr in inferior lm was read from */
+#else
+ struct so_map lm; /* copy of link map from inferior */
+ struct so_map *lmaddr; /* addr in inferior lm was read from */
+#endif
+ CORE_ADDR lmend; /* upper addr bound of mapped object */
+ char so_name[MAX_PATH_SIZE]; /* shared object lib name (FIXME) */
+ char symbols_loaded; /* flag: symbols read in yet? */
+ char from_tty; /* flag: print msgs? */
+ struct objfile *objfile; /* objfile for loaded lib */
+ struct section_table *sections;
+ struct section_table *sections_end;
+ struct section_table *textsection;
+ bfd *abfd;
+};
+
+static struct so_list *so_list_head; /* List of known shared objects */
+static CORE_ADDR debug_base; /* Base of dynamic linker structures */
+static CORE_ADDR breakpoint_addr; /* Address where end bkpt is set */
+
+extern int
+fdmatch PARAMS ((int, int)); /* In libiberty */
+
+/* Local function prototypes */
+
+static void
+special_symbol_handling PARAMS ((struct so_list *));
+
+static void
+sharedlibrary_command PARAMS ((char *, int));
+
+static int
+enable_break PARAMS ((void));
+
+static int
+disable_break PARAMS ((void));
+
+static void
+info_sharedlibrary_command PARAMS ((char *, int));
+
+static int
+symbol_add_stub PARAMS ((char *));
+
+static struct so_list *
+find_solib PARAMS ((struct so_list *));
+
+#ifdef OLD_FreeBSD_LD
+static struct link_map *
+#else
+static struct so_map *
+#endif
+first_link_map_member PARAMS ((void));
+
+static CORE_ADDR
+locate_base PARAMS ((void));
+
+static void
+solib_map_sections PARAMS ((struct so_list *));
+
+#ifdef SVR4_SHARED_LIBS
+
+static int
+look_for_base PARAMS ((int, CORE_ADDR));
+
+static CORE_ADDR
+bfd_lookup_symbol PARAMS ((bfd *, char *));
+
+#else
+
+static void
+solib_add_common_symbols PARAMS ((struct rt_symbol *, struct objfile *));
+
+#endif
+
+/*
+
+LOCAL FUNCTION
+
+ solib_map_sections -- open bfd and build sections for shared lib
+
+SYNOPSIS
+
+ static void solib_map_sections (struct so_list *so)
+
+DESCRIPTION
+
+ Given a pointer to one of the shared objects in our list
+ of mapped objects, use the recorded name to open a bfd
+ descriptor for the object, build a section table, and then
+ relocate all the section addresses by the base address at
+ which the shared object was mapped.
+
+FIXMES
+
+ In most (all?) cases the shared object file name recorded in the
+ dynamic linkage tables will be a fully qualified pathname. For
+ cases where it isn't, do we really mimic the systems search
+ mechanism correctly in the below code (particularly the tilde
+ expansion stuff?).
+ */
+
+static void
+solib_map_sections (so)
+ struct so_list *so;
+{
+ char *filename;
+ char *scratch_pathname;
+ int scratch_chan;
+ struct section_table *p;
+ struct cleanup *old_chain;
+ bfd *abfd;
+
+ filename = tilde_expand (so -> so_name);
+ old_chain = make_cleanup (free, filename);
+
+ scratch_chan = openp (getenv ("PATH"), 1, filename, O_RDONLY, 0,
+ &scratch_pathname);
+ if (scratch_chan < 0)
+ {
+ scratch_chan = openp (getenv ("LD_LIBRARY_PATH"), 1, filename,
+ O_RDONLY, 0, &scratch_pathname);
+ }
+ if (scratch_chan < 0)
+ {
+ perror_with_name (filename);
+ }
+ /* Leave scratch_pathname allocated. abfd->name will point to it. */
+
+ abfd = bfd_fdopenr (scratch_pathname, gnutarget, scratch_chan);
+ if (!abfd)
+ {
+ close (scratch_chan);
+ error ("Could not open `%s' as an executable file: %s",
+ scratch_pathname, bfd_errmsg (bfd_error));
+ }
+ /* Leave bfd open, core_xfer_memory and "info files" need it. */
+ so -> abfd = abfd;
+ abfd -> cacheable = true;
+
+ if (!bfd_check_format (abfd, bfd_object))
+ {
+ error ("\"%s\": not in executable format: %s.",
+ scratch_pathname, bfd_errmsg (bfd_error));
+ }
+ if (build_section_table (abfd, &so -> sections, &so -> sections_end))
+ {
+ error ("Can't find the file sections in `%s': %s",
+ bfd_get_filename (exec_bfd), bfd_errmsg (bfd_error));
+ }
+
+ for (p = so -> sections; p < so -> sections_end; p++)
+ {
+ /* Relocate the section binding addresses as recorded in the shared
+ object's file by the base address to which the object was actually
+ mapped. */
+ p -> addr += (CORE_ADDR) LM_ADDR (so);
+ p -> endaddr += (CORE_ADDR) LM_ADDR (so);
+ so -> lmend = (CORE_ADDR) max (p -> endaddr, so -> lmend);
+ if (STREQ (p -> sec_ptr -> name, ".text"))
+ {
+ so -> textsection = p;
+ }
+ }
+
+ /* Free the file names, close the file now. */
+ do_cleanups (old_chain);
+}
+
+/* Read all dynamically loaded common symbol definitions from the inferior
+ and add them to the minimal symbol table for the shared library objfile. */
+
+#ifndef SVR4_SHARED_LIBS
+
+/* In GDB 4.9 this routine was a real performance hog. According to
+ some gprof data which mtranle@paris.IntelliCorp.COM (Minh Tran-Le)
+ sent, almost all the time spend in solib_add (up to 20 minutes with
+ 35 shared libraries) was spent here, with 5/6 in
+ lookup_minimal_symbol and 1/6 in read_memory.
+
+ To fix this, we moved the call to special_symbol_handling out of the
+ loop in solib_add, so this only gets called once, rather than once
+ for every shared library, and also removed the call to lookup_minimal_symbol
+ in this routine. */
+
+static void
+solib_add_common_symbols (rtc_symp, objfile)
+ struct rt_symbol *rtc_symp;
+ struct objfile *objfile;
+{
+ struct rt_symbol inferior_rtc_symb;
+ struct nzlist inferior_rtc_nzlist;
+ int len;
+ char *name;
+ char *origname;
+
+ init_minimal_symbol_collection ();
+ make_cleanup (discard_minimal_symbols, 0);
+
+ while (rtc_symp)
+ {
+ read_memory ((CORE_ADDR) rtc_symp,
+ (char *) &inferior_rtc_symb,
+ sizeof (inferior_rtc_symb));
+ read_memory ((CORE_ADDR) inferior_rtc_symb.rt_sp,
+ (char *) &inferior_rtc_nzlist,
+ sizeof(inferior_rtc_nzlist));
+ if (inferior_rtc_nzlist.nz_type == N_COMM)
+ {
+ /* FIXME: The length of the symbol name is not available, but in the
+ current implementation the common symbol is allocated immediately
+ behind the name of the symbol. */
+ len = inferior_rtc_nzlist.nz_value - inferior_rtc_nzlist.nz_strx;
+
+ origname = name = xmalloc (len);
+ read_memory ((CORE_ADDR) inferior_rtc_nzlist.nz_name, name, len);
+
+ /* Don't enter the symbol twice if the target is re-run. */
+
+ if (name[0] == bfd_get_symbol_leading_char (objfile->obfd))
+ {
+ name++;
+ }
+
+#if 0
+ /* I think this is unnecessary, GDB can probably deal with
+ duplicate minimal symbols, more or less. And the duplication
+ which used to happen because this was called for each shared
+ library is gone now that we are just called once. */
+ /* FIXME: Do we really want to exclude symbols which happen
+ to match symbols for other locations in the inferior's
+ address space, even when they are in different linkage units? */
+ if (lookup_minimal_symbol (name, (struct objfile *) NULL) == NULL)
+#endif
+ {
+ name = obsavestring (name, strlen (name),
+ &objfile -> symbol_obstack);
+ prim_record_minimal_symbol (name, inferior_rtc_nzlist.nz_value,
+ mst_bss);
+ }
+ free (origname);
+ }
+ rtc_symp = inferior_rtc_symb.rt_next;
+ }
+
+ /* Install any minimal symbols that have been collected as the current
+ minimal symbols for this objfile. */
+
+ install_minimal_symbols (objfile);
+}
+
+#endif /* SVR4_SHARED_LIBS */
+
+#ifdef SVR4_SHARED_LIBS
+
+/*
+
+LOCAL FUNCTION
+
+ bfd_lookup_symbol -- lookup the value for a specific symbol
+
+SYNOPSIS
+
+ CORE_ADDR bfd_lookup_symbol (bfd *abfd, char *symname)
+
+DESCRIPTION
+
+ An expensive way to lookup the value of a single symbol for
+ bfd's that are only temporary anyway. This is used by the
+ shared library support to find the address of the debugger
+ interface structures in the shared library.
+
+ Note that 0 is specifically allowed as an error return (no
+ such symbol).
+
+ FIXME: See if there is a less "expensive" way of doing this.
+ Also see if there is already another bfd or gdb function
+ that specifically does this, and if so, use it.
+*/
+
+static CORE_ADDR
+bfd_lookup_symbol (abfd, symname)
+ bfd *abfd;
+ char *symname;
+{
+ unsigned int storage_needed;
+ asymbol *sym;
+ asymbol **symbol_table;
+ unsigned int number_of_symbols;
+ unsigned int i;
+ struct cleanup *back_to;
+ CORE_ADDR symaddr = 0;
+
+ storage_needed = get_symtab_upper_bound (abfd);
+
+ if (storage_needed > 0)
+ {
+ symbol_table = (asymbol **) xmalloc (storage_needed);
+ back_to = make_cleanup (free, (PTR)symbol_table);
+ number_of_symbols = bfd_canonicalize_symtab (abfd, symbol_table);
+
+ for (i = 0; i < number_of_symbols; i++)
+ {
+ sym = *symbol_table++;
+ if (STREQ (sym -> name, symname))
+ {
+ /* Bfd symbols are section relative. */
+ symaddr = sym -> value + sym -> section -> vma;
+ break;
+ }
+ }
+ do_cleanups (back_to);
+ }
+ return (symaddr);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ look_for_base -- examine file for each mapped address segment
+
+SYNOPSYS
+
+ static int look_for_base (int fd, CORE_ADDR baseaddr)
+
+DESCRIPTION
+
+ This function is passed to proc_iterate_over_mappings, which
+ causes it to get called once for each mapped address space, with
+ an open file descriptor for the file mapped to that space, and the
+ base address of that mapped space.
+
+ Our job is to find the symbol DEBUG_BASE in the file that this
+ fd is open on, if it exists, and if so, initialize the dynamic
+ linker structure base address debug_base.
+
+ Note that this is a computationally expensive proposition, since
+ we basically have to open a bfd on every call, so we specifically
+ avoid opening the exec file.
+ */
+
+static int
+look_for_base (fd, baseaddr)
+ int fd;
+ CORE_ADDR baseaddr;
+{
+ bfd *interp_bfd;
+ CORE_ADDR address;
+
+ /* If the fd is -1, then there is no file that corresponds to this
+ mapped memory segment, so skip it. Also, if the fd corresponds
+ to the exec file, skip it as well. */
+
+ if ((fd == -1) || fdmatch (fileno ((FILE *)(exec_bfd -> iostream)), fd))
+ {
+ return (0);
+ }
+
+ /* Try to open whatever random file this fd corresponds to. Note that
+ we have no way currently to find the filename. Don't gripe about
+ any problems we might have, just fail. */
+
+ if ((interp_bfd = bfd_fdopenr ("unnamed", gnutarget, fd)) == NULL)
+ {
+ return (0);
+ }
+ if (!bfd_check_format (interp_bfd, bfd_object))
+ {
+ bfd_close (interp_bfd);
+ return (0);
+ }
+
+ /* Now try to find our DEBUG_BASE symbol in this file, which we at
+ least know to be a valid ELF executable or shared library. */
+
+ if ((address = bfd_lookup_symbol (interp_bfd, DEBUG_BASE)) == 0)
+ {
+ bfd_close (interp_bfd);
+ return (0);
+ }
+
+ /* Eureka! We found the symbol. But now we may need to relocate it
+ by the base address. If the symbol's value is less than the base
+ address of the shared library, then it hasn't yet been relocated
+ by the dynamic linker, and we have to do it ourself. FIXME: Note
+ that we make the assumption that the first segment that corresponds
+ to the shared library has the base address to which the library
+ was relocated. */
+
+ if (address < baseaddr)
+ {
+ address += baseaddr;
+ }
+ debug_base = address;
+ bfd_close (interp_bfd);
+ return (1);
+}
+
+#endif
+
+/*
+
+LOCAL FUNCTION
+
+ locate_base -- locate the base address of dynamic linker structs
+
+SYNOPSIS
+
+ CORE_ADDR locate_base (void)
+
+DESCRIPTION
+
+ For both the SunOS and SVR4 shared library implementations, if the
+ inferior executable has been linked dynamically, there is a single
+ address somewhere in the inferior's data space which is the key to
+ locating all of the dynamic linker's runtime structures. This
+ address is the value of the symbol defined by the macro DEBUG_BASE.
+ The job of this function is to find and return that address, or to
+ return 0 if there is no such address (the executable is statically
+ linked for example).
+
+ For SunOS, the job is almost trivial, since the dynamic linker and
+ all of it's structures are statically linked to the executable at
+ link time. Thus the symbol for the address we are looking for has
+ already been added to the minimal symbol table for the executable's
+ objfile at the time the symbol file's symbols were read, and all we
+ have to do is look it up there. Note that we explicitly do NOT want
+ to find the copies in the shared library.
+
+ The SVR4 version is much more complicated because the dynamic linker
+ and it's structures are located in the shared C library, which gets
+ run as the executable's "interpreter" by the kernel. We have to go
+ to a lot more work to discover the address of DEBUG_BASE. Because
+ of this complexity, we cache the value we find and return that value
+ on subsequent invocations. Note there is no copy in the executable
+ symbol tables.
+
+ Note that we can assume nothing about the process state at the time
+ we need to find this address. We may be stopped on the first instruc-
+ tion of the interpreter (C shared library), the first instruction of
+ the executable itself, or somewhere else entirely (if we attached
+ to the process for example).
+
+ */
+
+static CORE_ADDR
+locate_base ()
+{
+
+#ifndef SVR4_SHARED_LIBS
+
+ struct minimal_symbol *msymbol;
+ CORE_ADDR address = 0;
+
+ /* For SunOS, we want to limit the search for DEBUG_BASE to the executable
+ being debugged, since there is a duplicate named symbol in the shared
+ library. We don't want the shared library versions. */
+
+ msymbol = lookup_minimal_symbol (DEBUG_BASE, symfile_objfile);
+ if ((msymbol != NULL) && (SYMBOL_VALUE_ADDRESS (msymbol) != 0))
+ {
+ address = SYMBOL_VALUE_ADDRESS (msymbol);
+ }
+ return (address);
+
+#else /* SVR4_SHARED_LIBS */
+
+ /* Check to see if we have a currently valid address, and if so, avoid
+ doing all this work again and just return the cached address. If
+ we have no cached address, ask the /proc support interface to iterate
+ over the list of mapped address segments, calling look_for_base() for
+ each segment. When we are done, we will have either found the base
+ address or not. */
+
+ if (debug_base == 0)
+ {
+ proc_iterate_over_mappings (look_for_base);
+ }
+ return (debug_base);
+
+#endif /* !SVR4_SHARED_LIBS */
+
+}
+
+/*
+
+LOCAL FUNCTION
+
+ first_link_map_member -- locate first member in dynamic linker's map
+
+SYNOPSIS
+
+ static struct link_map *first_link_map_member (void)
+
+DESCRIPTION
+
+ Read in a copy of the first member in the inferior's dynamic
+ link map from the inferior's dynamic linker structures, and return
+ a pointer to the copy in our address space.
+*/
+
+#ifdef OLD_FreeBSD_LD
+static struct link_map *
+#else
+static struct so_map *
+#endif
+first_link_map_member ()
+{
+#ifdef OLD_FreeBSD_LD
+ struct link_map *lm = NULL;
+#else
+ struct so_map *lm = NULL;
+#endif
+
+#ifndef SVR4_SHARED_LIBS
+
+ read_memory (debug_base, (char *) &dynamic_copy, sizeof (dynamic_copy));
+#ifdef OLD_FreeBSD_LD
+ if (dynamic_copy.ld_version >= 2)
+ {
+ /* It is a version that we can deal with, so read in the secondary
+ structure and find the address of the link map list from it. */
+ read_memory ((CORE_ADDR) dynamic_copy.ld_un.ld_2, (char *) &ld_2_copy,
+ sizeof (struct link_dynamic_2));
+ lm = ld_2_copy.ld_loaded;
+ }
+#else
+ if (dynamic_copy.d_version >= 2)
+ {
+ /* It is a version that we can deal with, so read in the secondary
+ structure and find the address of the link map list from it. */
+ read_memory ((CORE_ADDR) dynamic_copy.d_un.d_sdt, (char *) &ld_2_copy,
+ sizeof (struct section_dispatch_table));
+ lm = ld_2_copy.sdt_loaded;
+ }
+#endif
+#else /* SVR4_SHARED_LIBS */
+
+ read_memory (debug_base, (char *) &debug_copy, sizeof (struct r_debug));
+ /* FIXME: Perhaps we should validate the info somehow, perhaps by
+ checking r_version for a known version number, or r_state for
+ RT_CONSISTENT. */
+ lm = debug_copy.r_map;
+
+#endif /* !SVR4_SHARED_LIBS */
+
+ return (lm);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ find_solib -- step through list of shared objects
+
+SYNOPSIS
+
+ struct so_list *find_solib (struct so_list *so_list_ptr)
+
+DESCRIPTION
+
+ This module contains the routine which finds the names of any
+ loaded "images" in the current process. The argument in must be
+ NULL on the first call, and then the returned value must be passed
+ in on subsequent calls. This provides the capability to "step" down
+ the list of loaded objects. On the last object, a NULL value is
+ returned.
+
+ The arg and return value are "struct link_map" pointers, as defined
+ in <link.h>.
+ */
+
+static struct so_list *
+find_solib (so_list_ptr)
+ struct so_list *so_list_ptr; /* Last lm or NULL for first one */
+{
+ struct so_list *so_list_next = NULL;
+#ifdef OLD_FreeBSD_LD
+ struct link_map *lm = NULL;
+#else
+ struct so_map *lm = NULL;
+#endif
+ struct so_list *new;
+
+ if (so_list_ptr == NULL)
+ {
+ /* We are setting up for a new scan through the loaded images. */
+ if ((so_list_next = so_list_head) == NULL)
+ {
+ /* We have not already read in the dynamic linking structures
+ from the inferior, lookup the address of the base structure. */
+ debug_base = locate_base ();
+ if (debug_base != 0)
+ {
+ /* Read the base structure in and find the address of the first
+ link map list member. */
+ lm = first_link_map_member ();
+ }
+ }
+ }
+ else
+ {
+ /* We have been called before, and are in the process of walking
+ the shared library list. Advance to the next shared object. */
+ if ((lm = LM_NEXT (so_list_ptr)) == NULL)
+ {
+ /* We have hit the end of the list, so check to see if any were
+ added, but be quiet if we can't read from the target any more. */
+ int status = target_read_memory ((CORE_ADDR) so_list_ptr -> lmaddr,
+ (char *) &(so_list_ptr -> lm),
+#ifdef OLD_FreeBSD_LD
+ sizeof (struct link_map));
+#else
+ sizeof (struct so_map));
+#endif
+ if (status == 0)
+ {
+ lm = LM_NEXT (so_list_ptr);
+ }
+ else
+ {
+ lm = NULL;
+ }
+ }
+ so_list_next = so_list_ptr -> next;
+ }
+ if ((so_list_next == NULL) && (lm != NULL))
+ {
+ /* Get next link map structure from inferior image and build a local
+ abbreviated load_map structure */
+ new = (struct so_list *) xmalloc (sizeof (struct so_list));
+ memset ((char *) new, 0, sizeof (struct so_list));
+ new -> lmaddr = lm;
+ /* Add the new node as the next node in the list, or as the root
+ node if this is the first one. */
+ if (so_list_ptr != NULL)
+ {
+ so_list_ptr -> next = new;
+ }
+ else
+ {
+ so_list_head = new;
+ }
+ so_list_next = new;
+ read_memory ((CORE_ADDR) lm, (char *) &(new -> lm),
+#ifdef OLD_FreeBSD_LD
+ sizeof (struct link_map));
+#else
+ sizeof (struct so_map));
+#endif
+ /* For the SVR4 version, there is one entry that has no name
+ (for the inferior executable) since it is not a shared object. */
+ if (LM_NAME (new) != 0)
+ {
+ if (!target_read_string((CORE_ADDR) LM_NAME (new), new -> so_name,
+ MAX_PATH_SIZE - 1))
+ error ("find_solib: Can't read pathname for load map\n");
+ new -> so_name[MAX_PATH_SIZE - 1] = 0;
+ solib_map_sections (new);
+ }
+ }
+ return (so_list_next);
+}
+
+/* A small stub to get us past the arg-passing pinhole of catch_errors. */
+
+static int
+symbol_add_stub (arg)
+ char *arg;
+{
+ register struct so_list *so = (struct so_list *) arg; /* catch_errs bogon */
+
+ so -> objfile = symbol_file_add (so -> so_name, so -> from_tty,
+ (unsigned int) so -> textsection -> addr,
+ 0, 0, 0);
+ return (1);
+}
+
+/*
+
+GLOBAL FUNCTION
+
+ solib_add -- add a shared library file to the symtab and section list
+
+SYNOPSIS
+
+ void solib_add (char *arg_string, int from_tty,
+ struct target_ops *target)
+
+DESCRIPTION
+
+*/
+
+void
+solib_add (arg_string, from_tty, target)
+ char *arg_string;
+ int from_tty;
+ struct target_ops *target;
+{
+ register struct so_list *so = NULL; /* link map state variable */
+
+ /* Last shared library that we read. */
+ struct so_list *so_last = NULL;
+
+ char *re_err;
+ int count;
+ int old;
+
+ if ((re_err = re_comp (arg_string ? arg_string : ".")) != NULL)
+ {
+ error ("Invalid regexp: %s", re_err);
+ }
+
+ /* Getting new symbols may change our opinion about what is
+ frameless. */
+ reinit_frame_cache ();
+
+ while ((so = find_solib (so)) != NULL)
+ {
+ if (so -> so_name[0] && re_exec (so -> so_name))
+ {
+ so -> from_tty = from_tty;
+ if (so -> symbols_loaded)
+ {
+ if (from_tty)
+ {
+ printf ("Symbols already loaded for %s\n", so -> so_name);
+ }
+ }
+ else if (catch_errors
+ (symbol_add_stub, (char *) so,
+ "Error while reading shared library symbols:\n",
+ RETURN_MASK_ALL))
+ {
+ so_last = so;
+ so -> symbols_loaded = 1;
+ }
+ }
+ }
+
+ /* Now add the shared library sections to the section table of the
+ specified target, if any. */
+ if (target)
+ {
+ /* Count how many new section_table entries there are. */
+ so = NULL;
+ count = 0;
+ while ((so = find_solib (so)) != NULL)
+ {
+ if (so -> so_name[0])
+ {
+ count += so -> sections_end - so -> sections;
+ }
+ }
+
+ if (count)
+ {
+ /* Reallocate the target's section table including the new size. */
+ if (target -> to_sections)
+ {
+ old = target -> to_sections_end - target -> to_sections;
+ target -> to_sections = (struct section_table *)
+ xrealloc ((char *)target -> to_sections,
+ (sizeof (struct section_table)) * (count + old));
+ }
+ else
+ {
+ old = 0;
+ target -> to_sections = (struct section_table *)
+ xmalloc ((sizeof (struct section_table)) * count);
+ }
+ target -> to_sections_end = target -> to_sections + (count + old);
+
+ /* Add these section table entries to the target's table. */
+ while ((so = find_solib (so)) != NULL)
+ {
+ if (so -> so_name[0])
+ {
+ count = so -> sections_end - so -> sections;
+ memcpy ((char *) (target -> to_sections + old),
+ so -> sections,
+ (sizeof (struct section_table)) * count);
+ old += count;
+ }
+ }
+ }
+ }
+
+ /* Calling this once at the end means that we put all the minimal
+ symbols for commons into the objfile for the last shared library.
+ Since they are in common, this should not be a problem. If we
+ delete the objfile with the minimal symbols, we can put all the
+ symbols into a new objfile (and will on the next call to solib_add).
+
+ An alternate approach would be to create an objfile just for
+ common minsyms, thus not needing any objfile argument to
+ solib_add_common_symbols. */
+
+ if (so_last)
+ special_symbol_handling (so_last);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ info_sharedlibrary_command -- code for "info sharedlibrary"
+
+SYNOPSIS
+
+ static void info_sharedlibrary_command ()
+
+DESCRIPTION
+
+ Walk through the shared library list and print information
+ about each attached library.
+*/
+
+static void
+info_sharedlibrary_command (ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ register struct so_list *so = NULL; /* link map state variable */
+ int header_done = 0;
+
+ if (exec_bfd == NULL)
+ {
+ printf ("No exec file.\n");
+ return;
+ }
+ while ((so = find_solib (so)) != NULL)
+ {
+ if (so -> so_name[0])
+ {
+ if (!header_done)
+ {
+ printf("%-12s%-12s%-12s%s\n", "From", "To", "Syms Read",
+ "Shared Object Library");
+ header_done++;
+ }
+ printf ("%-12s",
+ local_hex_string_custom ((unsigned long) LM_ADDR (so),
+ "08l"));
+ printf ("%-12s",
+ local_hex_string_custom ((unsigned long) so -> lmend,
+ "08l"));
+ printf ("%-12s", so -> symbols_loaded ? "Yes" : "No");
+ printf ("%s\n", so -> so_name);
+ }
+ }
+ if (so_list_head == NULL)
+ {
+ printf ("No shared libraries loaded at this time.\n");
+ }
+}
+
+/*
+
+GLOBAL FUNCTION
+
+ solib_address -- check to see if an address is in a shared lib
+
+SYNOPSIS
+
+ int solib_address (CORE_ADDR address)
+
+DESCRIPTION
+
+ Provides a hook for other gdb routines to discover whether or
+ not a particular address is within the mapped address space of
+ a shared library. Any address between the base mapping address
+ and the first address beyond the end of the last mapping, is
+ considered to be within the shared library address space, for
+ our purposes.
+
+ For example, this routine is called at one point to disable
+ breakpoints which are in shared libraries that are not currently
+ mapped in.
+ */
+
+int
+solib_address (address)
+ CORE_ADDR address;
+{
+ register struct so_list *so = 0; /* link map state variable */
+
+ while ((so = find_solib (so)) != NULL)
+ {
+ if (so -> so_name[0])
+ {
+ if ((address >= (CORE_ADDR) LM_ADDR (so)) &&
+ (address < (CORE_ADDR) so -> lmend))
+ {
+ return (1);
+ }
+ }
+ }
+ return (0);
+}
+
+/* Called by free_all_symtabs */
+
+void
+clear_solib()
+{
+ struct so_list *next;
+ char *bfd_filename;
+
+ while (so_list_head)
+ {
+ if (so_list_head -> sections)
+ {
+ free ((PTR)so_list_head -> sections);
+ }
+ if (so_list_head -> abfd)
+ {
+ bfd_filename = bfd_get_filename (so_list_head -> abfd);
+ bfd_close (so_list_head -> abfd);
+ }
+ else
+ /* This happens for the executable on SVR4. */
+ bfd_filename = NULL;
+
+ next = so_list_head -> next;
+ if (bfd_filename)
+ free ((PTR)bfd_filename);
+ free ((PTR)so_list_head);
+ so_list_head = next;
+ }
+ debug_base = 0;
+}
+
+/*
+
+LOCAL FUNCTION
+
+ disable_break -- remove the "mapping changed" breakpoint
+
+SYNOPSIS
+
+ static int disable_break ()
+
+DESCRIPTION
+
+ Removes the breakpoint that gets hit when the dynamic linker
+ completes a mapping change.
+
+*/
+
+static int
+disable_break ()
+{
+ int status = 1;
+
+#ifndef SVR4_SHARED_LIBS
+
+ int in_debugger = 0;
+
+ /* Read the debugger structure from the inferior to retrieve the
+ address of the breakpoint and the original contents of the
+ breakpoint address. Remove the breakpoint by writing the original
+ contents back. */
+
+ read_memory (debug_addr, (char *) &debug_copy, sizeof (debug_copy));
+
+ /* Set `in_debugger' to zero now. */
+
+ write_memory (flag_addr, (char *) &in_debugger, sizeof (in_debugger));
+
+#ifdef OLD_FreeBSD_LD
+ breakpoint_addr = (CORE_ADDR) debug_copy.ldd_bp_addr;
+ write_memory (breakpoint_addr, (char *) &debug_copy.ldd_bp_inst,
+ sizeof (debug_copy.ldd_bp_inst));
+#else
+ breakpoint_addr = (CORE_ADDR) debug_copy.dd_bpt_addr;
+ write_memory (breakpoint_addr, (char *) &debug_copy.dd_bpt_shadow,
+ sizeof (debug_copy.dd_bpt_shadow));
+#endif
+
+#else /* SVR4_SHARED_LIBS */
+
+ /* Note that breakpoint address and original contents are in our address
+ space, so we just need to write the original contents back. */
+
+ if (memory_remove_breakpoint (breakpoint_addr, shadow_contents) != 0)
+ {
+ status = 0;
+ }
+
+#endif /* !SVR4_SHARED_LIBS */
+
+ /* For the SVR4 version, we always know the breakpoint address. For the
+ SunOS version we don't know it until the above code is executed.
+ Grumble if we are stopped anywhere besides the breakpoint address. */
+
+ if (stop_pc != breakpoint_addr)
+ {
+ warning ("stopped at unknown breakpoint while handling shared libraries");
+ }
+
+ return (status);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ enable_break -- arrange for dynamic linker to hit breakpoint
+
+SYNOPSIS
+
+ int enable_break (void)
+
+DESCRIPTION
+
+ Both the SunOS and the SVR4 dynamic linkers have, as part of their
+ debugger interface, support for arranging for the inferior to hit
+ a breakpoint after mapping in the shared libraries. This function
+ enables that breakpoint.
+
+ For SunOS, there is a special flag location (in_debugger) which we
+ set to 1. When the dynamic linker sees this flag set, it will set
+ a breakpoint at a location known only to itself, after saving the
+ original contents of that place and the breakpoint address itself,
+ in it's own internal structures. When we resume the inferior, it
+ will eventually take a SIGTRAP when it runs into the breakpoint.
+ We handle this (in a different place) by restoring the contents of
+ the breakpointed location (which is only known after it stops),
+ chasing around to locate the shared libraries that have been
+ loaded, then resuming.
+
+ For SVR4, the debugger interface structure contains a member (r_brk)
+ which is statically initialized at the time the shared library is
+ built, to the offset of a function (_r_debug_state) which is guaran-
+ teed to be called once before mapping in a library, and again when
+ the mapping is complete. At the time we are examining this member,
+ it contains only the unrelocated offset of the function, so we have
+ to do our own relocation. Later, when the dynamic linker actually
+ runs, it relocates r_brk to be the actual address of _r_debug_state().
+
+ The debugger interface structure also contains an enumeration which
+ is set to either RT_ADD or RT_DELETE prior to changing the mapping,
+ depending upon whether or not the library is being mapped or unmapped,
+ and then set to RT_CONSISTENT after the library is mapped/unmapped.
+*/
+
+static int
+enable_break ()
+{
+ int success = 0;
+
+#ifndef SVR4_SHARED_LIBS
+
+ int j;
+ int in_debugger;
+
+ /* Get link_dynamic structure */
+
+ j = target_read_memory (debug_base, (char *) &dynamic_copy,
+ sizeof (dynamic_copy));
+ if (j)
+ {
+ /* unreadable */
+ return (0);
+ }
+
+ /* Calc address of debugger interface structure */
+
+#ifdef OLD_FreeBSD_LD
+ debug_addr = (CORE_ADDR) dynamic_copy.ldd;
+#else
+ debug_addr = (CORE_ADDR) dynamic_copy.d_debug;
+#endif
+
+ /* Calc address of `in_debugger' member of debugger interface structure */
+
+#ifdef OLD_FreeBSD_LD
+ flag_addr = debug_addr + (CORE_ADDR) ((char *) &debug_copy.ldd_in_debugger -
+ (char *) &debug_copy);
+#else
+ flag_addr = debug_addr + (CORE_ADDR) ((char *) &debug_copy.dd_in_debugger -
+ (char *) &debug_copy);
+#endif
+
+ /* Write a value of 1 to this member. */
+
+ in_debugger = 1;
+ write_memory (flag_addr, (char *) &in_debugger, sizeof (in_debugger));
+ success = 1;
+
+#else /* SVR4_SHARED_LIBS */
+
+#ifdef BKPT_AT_SYMBOL
+
+ struct minimal_symbol *msymbol;
+ char **bkpt_namep;
+ CORE_ADDR bkpt_addr;
+
+ /* Scan through the list of symbols, trying to look up the symbol and
+ set a breakpoint there. Terminate loop when we/if we succeed. */
+
+ breakpoint_addr = 0;
+ for (bkpt_namep = bkpt_names; *bkpt_namep != NULL; bkpt_namep++)
+ {
+ msymbol = lookup_minimal_symbol (*bkpt_namep, symfile_objfile);
+ if ((msymbol != NULL) && (SYMBOL_VALUE_ADDRESS (msymbol) != 0))
+ {
+ bkpt_addr = SYMBOL_VALUE_ADDRESS (msymbol);
+ if (target_insert_breakpoint (bkpt_addr, shadow_contents) == 0)
+ {
+ breakpoint_addr = bkpt_addr;
+ success = 1;
+ break;
+ }
+ }
+ }
+
+#else /* !BKPT_AT_SYMBOL */
+
+ struct symtab_and_line sal;
+
+ /* Read the debugger interface structure directly. */
+
+ read_memory (debug_base, (char *) &debug_copy, sizeof (debug_copy));
+
+ /* Set breakpoint at the debugger interface stub routine that will
+ be called just prior to each mapping change and again after the
+ mapping change is complete. Set up the (nonexistent) handler to
+ deal with hitting these breakpoints. (FIXME). */
+
+ warning ("'%s': line %d: missing SVR4 support code", __FILE__, __LINE__);
+ success = 1;
+
+#endif /* BKPT_AT_SYMBOL */
+
+#endif /* !SVR4_SHARED_LIBS */
+
+ return (success);
+}
+
+/*
+
+GLOBAL FUNCTION
+
+ solib_create_inferior_hook -- shared library startup support
+
+SYNOPSIS
+
+ void solib_create_inferior_hook()
+
+DESCRIPTION
+
+ When gdb starts up the inferior, it nurses it along (through the
+ shell) until it is ready to execute it's first instruction. At this
+ point, this function gets called via expansion of the macro
+ SOLIB_CREATE_INFERIOR_HOOK.
+
+ For SunOS executables, this first instruction is typically the
+ one at "_start", or a similar text label, regardless of whether
+ the executable is statically or dynamically linked. The runtime
+ startup code takes care of dynamically linking in any shared
+ libraries, once gdb allows the inferior to continue.
+
+ For SVR4 executables, this first instruction is either the first
+ instruction in the dynamic linker (for dynamically linked
+ executables) or the instruction at "start" for statically linked
+ executables. For dynamically linked executables, the system
+ first exec's /lib/libc.so.N, which contains the dynamic linker,
+ and starts it running. The dynamic linker maps in any needed
+ shared libraries, maps in the actual user executable, and then
+ jumps to "start" in the user executable.
+
+ For both SunOS shared libraries, and SVR4 shared libraries, we
+ can arrange to cooperate with the dynamic linker to discover the
+ names of shared libraries that are dynamically linked, and the
+ base addresses to which they are linked.
+
+ This function is responsible for discovering those names and
+ addresses, and saving sufficient information about them to allow
+ their symbols to be read at a later time.
+
+FIXME
+
+ Between enable_break() and disable_break(), this code does not
+ properly handle hitting breakpoints which the user might have
+ set in the startup code or in the dynamic linker itself. Proper
+ handling will probably have to wait until the implementation is
+ changed to use the "breakpoint handler function" method.
+
+ Also, what if child has exit()ed? Must exit loop somehow.
+ */
+
+void
+solib_create_inferior_hook()
+{
+ /* If we are using the BKPT_AT_SYMBOL code, then we don't need the base
+ yet. In fact, in the case of a SunOS4 executable being run on
+ Solaris, we can't get it yet. find_solib will get it when it needs
+ it. */
+#if !(defined (SVR4_SHARED_LIBS) && defined (BKPT_AT_SYMBOL))
+ if ((debug_base = locate_base ()) == 0)
+ {
+ /* Can't find the symbol or the executable is statically linked. */
+ return;
+ }
+#endif
+
+ if (!enable_break ())
+ {
+ warning ("shared library handler failed to enable breakpoint");
+ return;
+ }
+
+ /* Now run the target. It will eventually hit the breakpoint, at
+ which point all of the libraries will have been mapped in and we
+ can go groveling around in the dynamic linker structures to find
+ out what we need to know about them. */
+
+ clear_proceed_status ();
+ stop_soon_quietly = 1;
+ stop_signal = 0;
+ do
+ {
+ target_resume (-1, 0, stop_signal);
+ wait_for_inferior ();
+ }
+ while (stop_signal != SIGTRAP);
+ stop_soon_quietly = 0;
+
+ /* We are now either at the "mapping complete" breakpoint (or somewhere
+ else, a condition we aren't prepared to deal with anyway), so adjust
+ the PC as necessary after a breakpoint, disable the breakpoint, and
+ add any shared libraries that were mapped in. */
+
+ if (DECR_PC_AFTER_BREAK)
+ {
+ stop_pc -= DECR_PC_AFTER_BREAK;
+ write_register (PC_REGNUM, stop_pc);
+ }
+
+ if (!disable_break ())
+ {
+ warning ("shared library handler failed to disable breakpoint");
+ }
+
+ solib_add ((char *) 0, 0, (struct target_ops *) 0);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ special_symbol_handling -- additional shared library symbol handling
+
+SYNOPSIS
+
+ void special_symbol_handling (struct so_list *so)
+
+DESCRIPTION
+
+ Once the symbols from a shared object have been loaded in the usual
+ way, we are called to do any system specific symbol handling that
+ is needed.
+
+ For Suns, this consists of grunging around in the dynamic linkers
+ structures to find symbol definitions for "common" symbols and
+ adding them to the minimal symbol table for the corresponding
+ objfile.
+
+*/
+
+static void
+special_symbol_handling (so)
+struct so_list *so;
+{
+#ifndef SVR4_SHARED_LIBS
+ int j;
+
+ if (debug_addr == 0)
+ {
+ /* Get link_dynamic structure */
+
+ j = target_read_memory (debug_base, (char *) &dynamic_copy,
+ sizeof (dynamic_copy));
+ if (j)
+ {
+ /* unreadable */
+ return;
+ }
+
+ /* Calc address of debugger interface structure */
+ /* FIXME, this needs work for cross-debugging of core files
+ (byteorder, size, alignment, etc). */
+
+#ifdef OLD_FreeBSD_LD
+ debug_addr = (CORE_ADDR) dynamic_copy.ldd;
+#else
+ debug_addr = (CORE_ADDR) dynamic_copy.d_debug;
+#endif
+ }
+
+ /* Read the debugger structure from the inferior, just to make sure
+ we have a current copy. */
+
+ j = target_read_memory (debug_addr, (char *) &debug_copy,
+ sizeof (debug_copy));
+ if (j)
+ return; /* unreadable */
+
+ /* Get common symbol definitions for the loaded object. */
+
+#ifdef OLD_FreeBSD_LD
+ if (debug_copy.ldd_cp)
+ {
+ solib_add_common_symbols (debug_copy.ldd_cp, so -> objfile);
+ }
+#else
+ if (debug_copy.dd_cc)
+ {
+ solib_add_common_symbols (debug_copy.dd_cc, so -> objfile);
+ }
+#endif
+
+#endif /* !SVR4_SHARED_LIBS */
+}
+
+
+/*
+
+LOCAL FUNCTION
+
+ sharedlibrary_command -- handle command to explicitly add library
+
+SYNOPSIS
+
+ static void sharedlibrary_command (char *args, int from_tty)
+
+DESCRIPTION
+
+*/
+
+static void
+sharedlibrary_command (args, from_tty)
+char *args;
+int from_tty;
+{
+ dont_repeat ();
+ solib_add (args, from_tty, (struct target_ops *) 0);
+}
+
+void
+_initialize_solib()
+{
+
+ add_com ("sharedlibrary", class_files, sharedlibrary_command,
+ "Load shared object library symbols for files matching REGEXP.");
+ add_info ("sharedlibrary", info_sharedlibrary_command,
+ "Status of loaded shared object libraries.");
+}
diff --git a/gnu/usr.bin/gdb/gdb/gdb-stabs.h b/gnu/usr.bin/gdb/gdb/gdb-stabs.h
new file mode 100644
index 0000000..c1e0253
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/gdb-stabs.h
@@ -0,0 +1,76 @@
+/* Definitions for symbol-reading containing "stabs", for GDB.
+ Copyright 1992 Free Software Foundation, Inc.
+ Contributed by Cygnus Support. Written by John Gilmore.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This file exists to hold the common definitions required of most of
+ the symbol-readers that end up using stabs. The common use of
+ these `symbol-type-specific' customizations of the generic data
+ structures makes the stabs-oriented symbol readers able to call
+ each others' functions as required. */
+
+#if !defined (GDBSTABS_H)
+#define GDBSTABS_H
+
+/* Offsets in the psymtab's section_offsets array for various kinds of
+ stabs symbols. Every psymtab built from stabs will have these offsets
+ filled in by these guidelines, so that when actually reading symbols, the
+ proper offset can simply be selected and added to the symbol value. */
+
+#define SECT_OFF_TEXT 0
+#define SECT_OFF_DATA 1
+#define SECT_OFF_BSS 2
+#define SECT_OFF_RODATA 3
+#define SECT_OFF_MAX 4 /* Count of possible values */
+
+/* The stab_section_info chain remembers info from the ELF symbol table,
+ while psymtabs are being built for the other symbol tables in the
+ objfile. It is destroyed at the complation of psymtab-reading.
+ Any info that was used from it has been copied into psymtabs. */
+
+struct stab_section_info {
+ char *filename;
+ CORE_ADDR sections[SECT_OFF_MAX];
+ struct stab_section_info *next;
+ int found; /* Count of times it's found in searching */
+};
+
+/* Information is passed among various dbxread routines for accessing
+ symbol files. A pointer to this structure is kept in the sym_stab_info
+ field of the objfile struct. */
+
+struct dbx_symfile_info {
+ asection *text_sect; /* Text section accessor */
+ int symcount; /* How many symbols are there in the file */
+ char *stringtab; /* The actual string table */
+ int stringtab_size; /* Its size */
+ file_ptr symtab_offset; /* Offset in file to symbol table */
+ int symbol_size; /* Bytes in a single symbol */
+ struct stab_section_info *stab_section_info; /* section starting points
+ of the original .o files before linking. */
+};
+
+#define DBX_SYMFILE_INFO(o) ((struct dbx_symfile_info *)((o)->sym_stab_info))
+#define DBX_TEXT_SECT(o) (DBX_SYMFILE_INFO(o)->text_sect)
+#define DBX_SYMCOUNT(o) (DBX_SYMFILE_INFO(o)->symcount)
+#define DBX_STRINGTAB(o) (DBX_SYMFILE_INFO(o)->stringtab)
+#define DBX_STRINGTAB_SIZE(o) (DBX_SYMFILE_INFO(o)->stringtab_size)
+#define DBX_SYMTAB_OFFSET(o) (DBX_SYMFILE_INFO(o)->symtab_offset)
+#define DBX_SYMBOL_SIZE(o) (DBX_SYMFILE_INFO(o)->symbol_size)
+
+#endif /* GDBSTABS_H */
diff --git a/gnu/usr.bin/gdb/gdb/gdb.1 b/gnu/usr.bin/gdb/gdb/gdb.1
new file mode 100644
index 0000000..ccb216e
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/gdb.1
@@ -0,0 +1,371 @@
+.\" Copyright (c) 1991 Free Software Foundation
+.\" See section COPYING for conditions for redistribution
+.\" $Id: gdb.1,v 1.1.1.1 1993/10/30 21:59:13 jkh Exp $
+.TH gdb 1 "4nov1991" "GNU Tools" "GNU Tools"
+.SH NAME
+gdb \- The GNU Debugger
+.SH SYNOPSIS
+.na
+.TP
+.B gdb
+.RB "[\|" \-help "\|]"
+.RB "[\|" \-nx "\|]"
+.RB "[\|" \-q "\|]"
+.RB "[\|" \-batch "\|]"
+.RB "[\|" \-cd=\c
+.I dir\c
+\|]
+.RB "[\|" \-f "\|]"
+.RB "[\|" "\-b\ "\c
+.IR bps "\|]"
+.RB "[\|" "\-tty="\c
+.IR dev "\|]"
+.RB "[\|" "\-s "\c
+.I symfile\c
+\&\|]
+.RB "[\|" "\-e "\c
+.I prog\c
+\&\|]
+.RB "[\|" "\-se "\c
+.I prog\c
+\&\|]
+.RB "[\|" "\-c "\c
+.I core\c
+\&\|]
+.RB "[\|" "\-x "\c
+.I cmds\c
+\&\|]
+.RB "[\|" "\-d "\c
+.I dir\c
+\&\|]
+.RB "[\|" \c
+.I prog\c
+.RB "[\|" \c
+.IR core \||\| procID\c
+\&\|]\&\|]
+.ad b
+.SH DESCRIPTION
+The purpose of a debugger such as GDB is to allow you to see what is
+going on ``inside'' another program while it executes\(em\&or what another
+program was doing at the moment it crashed.
+
+GDB can do four main kinds of things (plus other things in support of
+these) to help you catch bugs in the act:
+
+.TP
+\ \ \ \(bu
+Start your program, specifying anything that might affect its behavior.
+
+.TP
+\ \ \ \(bu
+Make your program stop on specified conditions.
+
+.TP
+\ \ \ \(bu
+Examine what has happened, when your program has stopped.
+
+.TP
+\ \ \ \(bu
+Change things in your program, so you can experiment with correcting the
+effects of one bug and go on to learn about another.
+.PP
+
+You can use GDB to debug programs written in C, C++, and Modula-2.
+Fortran support will be added when a GNU Fortran compiler is ready.
+
+GDB is invoked with the shell command \c
+.B gdb\c
+\&. Once started, it reads
+commands from the terminal until you tell it to exit with the GDB
+command \c
+.B quit\c
+\&. You can get online help from \c
+.B gdb\c
+\& itself
+by using the command \c
+.B help\c
+\&.
+
+You can run \c
+.B gdb\c
+\& with no arguments or options; but the most
+usual way to start GDB is with one argument or two, specifying an
+executable program as the argument:
+.sp
+.br
+gdb\ program
+.br
+.sp
+
+You can also start with both an executable program and a core file specified:
+.sp
+.br
+gdb\ program\ core
+.br
+.sp
+
+You can, instead, specify a process ID as a second argument, if you want
+to debug a running process:
+.sp
+.br
+gdb\ program\ 1234
+.br
+.sp
+
+would attach GDB to process \c
+.B 1234\c
+\& (unless you also have a file
+named `\|\c
+.B 1234\c
+\&\|'; GDB does check for a core file first).
+
+Here are some of the most frequently needed GDB commands:
+.TP
+.B break \fR[\|\fIfile\fB:\fR\|]\fIfunction
+\&
+Set a breakpoint at \c
+.I function\c
+\& (in \c
+.I file\c
+\&).
+.TP
+.B run \fR[\|\fIarglist\fR\|]
+Start your program (with \c
+.I arglist\c
+\&, if specified).
+.TP
+.B bt
+Backtrace: display the program stack.
+.TP
+.BI print " expr"\c
+\&
+Display the value of an expression.
+.TP
+.B c
+Continue running your program (after stopping, e.g. at a breakpoint).
+.TP
+.B next
+Execute next program line (after stopping); step \c
+.I over\c
+\& any
+function calls in the line.
+.TP
+.B step
+Execute next program line (after stopping); step \c
+.I into\c
+\& any
+function calls in the line.
+.TP
+.B help \fR[\|\fIname\fR\|]
+Show information about GDB command \c
+.I name\c
+\&, or general information
+about using GDB.
+.TP
+.B quit
+Exit from GDB.
+.PP
+For full details on GDB, see \c
+.I
+Using GDB: A Guide to the GNU Source-Level Debugger\c
+\&, by Richard M. Stallman and Roland H. Pesch. The same text is available online
+as the \c
+.B gdb\c
+\& entry in the \c
+.B info\c
+\& program.
+.SH OPTIONS
+Any arguments other than options specify an executable
+file and core file (or process ID); that is, the first argument
+encountered with no
+associated option flag is equivalent to a `\|\c
+.B \-se\c
+\&\|' option, and the
+second, if any, is equivalent to a `\|\c
+.B \-c\c
+\&\|' option if it's the name of a file. Many options have
+both long and short forms; both are shown here. The long forms are also
+recognized if you truncate them, so long as enough of the option is
+present to be unambiguous. (If you prefer, you can flag option
+arguments with `\|\c
+.B +\c
+\&\|' rather than `\|\c
+.B \-\c
+\&\|', though we illustrate the
+more usual convention.)
+
+All the options and command line arguments you give are processed
+in sequential order. The order makes a difference when the
+`\|\c
+.B \-x\c
+\&\|' option is used.
+
+.TP
+.B \-help
+.TP
+.B \-h
+List all options, with brief explanations.
+
+.TP
+.BI "\-symbols=" "file"\c
+.TP
+.BI "\-s " "file"\c
+\&
+Read symbol table from file \c
+.I file\c
+\&.
+
+.TP
+.BI "\-exec=" "file"\c
+.TP
+.BI "\-e " "file"\c
+\&
+Use file \c
+.I file\c
+\& as the executable file to execute when
+appropriate, and for examining pure data in conjunction with a core
+dump.
+
+.TP
+.BI "\-se=" "file"\c
+\&
+Read symbol table from file \c
+.I file\c
+\& and use it as the executable
+file.
+
+.TP
+.BI "\-core=" "file"\c
+.TP
+.BI "\-c " "file"\c
+\&
+Use file \c
+.I file\c
+\& as a core dump to examine.
+
+.TP
+.BI "\-command=" "file"\c
+.TP
+.BI "\-x " "file"\c
+\&
+Execute GDB commands from file \c
+.I file\c
+\&.
+
+.TP
+.BI "\-directory=" "directory"\c
+.TP
+.BI "\-d " "directory"\c
+\&
+Add \c
+.I directory\c
+\& to the path to search for source files.
+.PP
+
+.TP
+.B \-nx
+.TP
+.B \-n
+Do not execute commands from any `\|\c
+.B .gdbinit\c
+\&\|' initialization files.
+Normally, the commands in these files are executed after all the
+command options and arguments have been processed.
+
+
+.TP
+.B \-quiet
+.TP
+.B \-q
+``Quiet''. Do not print the introductory and copyright messages. These
+messages are also suppressed in batch mode.
+
+.TP
+.B \-batch
+Run in batch mode. Exit with status \c
+.B 0\c
+\& after processing all the command
+files specified with `\|\c
+.B \-x\c
+\&\|' (and `\|\c
+.B .gdbinit\c
+\&\|', if not inhibited).
+Exit with nonzero status if an error occurs in executing the GDB
+commands in the command files.
+
+Batch mode may be useful for running GDB as a filter, for example to
+download and run a program on another computer; in order to make this
+more useful, the message
+.sp
+.br
+Program\ exited\ normally.
+.br
+.sp
+
+(which is ordinarily issued whenever a program running under GDB control
+terminates) is not issued when running in batch mode.
+
+.TP
+.BI "\-cd=" "directory"\c
+\&
+Run GDB using \c
+.I directory\c
+\& as its working directory,
+instead of the current directory.
+
+.TP
+.B \-fullname
+.TP
+.B \-f
+Emacs sets this option when it runs GDB as a subprocess. It tells GDB
+to output the full file name and line number in a standard,
+recognizable fashion each time a stack frame is displayed (which
+includes each time the program stops). This recognizable format looks
+like two `\|\c
+.B \032\c
+\&\|' characters, followed by the file name, line number
+and character position separated by colons, and a newline. The
+Emacs-to-GDB interface program uses the two `\|\c
+.B \032\c
+\&\|' characters as
+a signal to display the source code for the frame.
+
+.TP
+.BI "\-b " "bps"\c
+\&
+Set the line speed (baud rate or bits per second) of any serial
+interface used by GDB for remote debugging.
+
+.TP
+.BI "\-tty=" "device"\c
+\&
+Run using \c
+.I device\c
+\& for your program's standard input and output.
+.PP
+
+.SH "SEE ALSO"
+.RB "`\|" gdb "\|'"
+entry in
+.B info\c
+\&;
+.I
+Using GDB: A Guide to the GNU Source-Level Debugger\c
+, Richard M. Stallman and Roland H. Pesch, July 1991.
+.SH COPYING
+Copyright (c) 1991 Free Software Foundation, Inc.
+.PP
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+.PP
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+.PP
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
diff --git a/gnu/usr.bin/gdb/gdb/gdbcmd.h b/gnu/usr.bin/gdb/gdb/gdbcmd.h
new file mode 100644
index 0000000..88f323c
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/gdbcmd.h
@@ -0,0 +1,101 @@
+/* Header file for GDB-specific command-line stuff.
+ Copyright 1986, 1989, 1990, 1992 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (GDBCMD_H)
+#define GDBCMD_H 1
+
+#include "command.h"
+
+/* Chain containing all defined commands. */
+
+extern struct cmd_list_element *cmdlist;
+
+/* Chain containing all defined info subcommands. */
+
+extern struct cmd_list_element *infolist;
+
+/* Chain containing all defined enable subcommands. */
+
+extern struct cmd_list_element *enablelist;
+
+/* Chain containing all defined disable subcommands. */
+
+extern struct cmd_list_element *disablelist;
+
+/* Chain containing all defined delete subcommands. */
+
+extern struct cmd_list_element *deletelist;
+
+/* Chain containing all defined "enable breakpoint" subcommands. */
+
+extern struct cmd_list_element *enablebreaklist;
+
+/* Chain containing all defined set subcommands */
+
+extern struct cmd_list_element *setlist;
+
+/* Chain containing all defined unset subcommands */
+
+extern struct cmd_list_element *unsetlist;
+
+/* Chain containing all defined show subcommands. */
+
+extern struct cmd_list_element *showlist;
+
+/* Chain containing all defined \"set history\". */
+
+extern struct cmd_list_element *sethistlist;
+
+/* Chain containing all defined \"show history\". */
+
+extern struct cmd_list_element *showhistlist;
+
+/* Chain containing all defined \"unset history\". */
+
+extern struct cmd_list_element *unsethistlist;
+
+/* Chain containing all defined maintenance subcommands. */
+
+extern struct cmd_list_element *maintenancelist;
+
+/* Chain containing all defined "maintenance info" subcommands. */
+
+extern struct cmd_list_element *maintenanceinfolist;
+
+/* Chain containing all defined "maintenance print" subcommands. */
+
+extern struct cmd_list_element *maintenanceprintlist;
+
+extern struct cmd_list_element *setprintlist;
+
+extern struct cmd_list_element *showprintlist;
+
+extern struct cmd_list_element *setchecklist;
+
+extern struct cmd_list_element *showchecklist;
+
+extern void
+execute_user_command PARAMS ((struct cmd_list_element *, char *));
+
+extern void
+execute_command PARAMS ((char *, int));
+
+extern char **noop_completer PARAMS ((char *, char *));
+
+extern char **filename_completer PARAMS ((char *, char *));
+
+#endif /* !defined (GDBCMD_H) */
diff --git a/gnu/usr.bin/gdb/gdb/gdbcore.h b/gnu/usr.bin/gdb/gdb/gdbcore.h
new file mode 100644
index 0000000..ec0f1b5
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/gdbcore.h
@@ -0,0 +1,129 @@
+/* Machine independent variables that describe the core file under GDB.
+ Copyright 1986, 1987, 1989, 1990, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Interface routines for core, executable, etc. */
+
+#if !defined (GDBCORE_H)
+#define GDBCORE_H 1
+
+#include "bfd.h" /* Binary File Description */
+
+/* Return the name of the executable file as a string.
+ ERR nonzero means get error if there is none specified;
+ otherwise return 0 in that case. */
+
+extern char *
+get_exec_file PARAMS ((int err));
+
+/* Nonzero if there is a core file. */
+
+extern int
+have_core_file_p PARAMS ((void));
+
+/* Read "memory data" from whatever target or inferior we have.
+ Returns zero if successful, errno value if not. EIO is used
+ for address out of bounds. If breakpoints are inserted, returns
+ shadow contents, not the breakpoints themselves. From breakpoint.c. */
+
+extern int
+read_memory_nobpt PARAMS ((CORE_ADDR memaddr, char *myaddr, unsigned len));
+
+/* Report a memory error with error(). */
+
+extern void
+memory_error PARAMS ((int status, CORE_ADDR memaddr));
+
+/* Like target_read_memory, but report an error if can't read. */
+
+extern void
+read_memory PARAMS ((CORE_ADDR memaddr, char *myaddr, int len));
+
+/* Read an integer from debugged memory, given address and number of bytes. */
+
+extern LONGEST
+read_memory_integer PARAMS ((CORE_ADDR memaddr, int len));
+
+/* Read an unsigned integer from debugged memory, given address and number of bytes. */
+
+extern unsigned LONGEST
+read_memory_unsigned_integer PARAMS ((CORE_ADDR memaddr, int len));
+
+/* If this is prototyped, need to deal with void* vs. char*. */
+
+extern void
+write_memory PARAMS ((CORE_ADDR memaddr, char *myaddr, int len));
+
+/* Hook for `exec_file_command' command to call. */
+
+extern void (*exec_file_display_hook) PARAMS ((char *filename));
+
+extern void
+specify_exec_file_hook PARAMS ((void (*hook) (char *filename)));
+
+/* Binary File Diddlers for the exec and core files */
+extern bfd *core_bfd;
+extern bfd *exec_bfd;
+
+/* Whether to open exec and core files read-only or read-write. */
+
+extern int write_files;
+
+extern void
+core_file_command PARAMS ((char *filename, int from_tty));
+
+extern void
+exec_file_command PARAMS ((char *filename, int from_tty));
+
+extern void
+validate_files PARAMS ((void));
+
+extern unsigned int
+register_addr PARAMS ((int regno, int blockend));
+
+extern int
+xfer_core_file PARAMS ((CORE_ADDR memaddr, char *myaddr, int len));
+
+extern void
+fetch_core_registers PARAMS ((char *core_reg_sect, unsigned core_reg_size,
+ int which, unsigned int reg_addr));
+
+extern void
+registers_fetched PARAMS ((void));
+
+#if !defined (KERNEL_U_ADDR)
+extern CORE_ADDR kernel_u_addr;
+#define KERNEL_U_ADDR kernel_u_addr
+#endif
+
+/* The target vector for core files */
+extern struct target_ops core_ops;
+
+ /* target vector functions called directly from elsewhere */
+void
+core_open PARAMS ((char *, int));
+
+void
+core_detach PARAMS ((char *, int));
+
+/* The current default bfd target. */
+extern char *gnutarget;
+
+extern void set_gnutarget PARAMS ((char *));
+
+#endif /* !defined (GDBCORE_H) */
diff --git a/gnu/usr.bin/gdb/gdb/gdbtypes.c b/gnu/usr.bin/gdb/gdb/gdbtypes.c
new file mode 100644
index 0000000..f346469
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/gdbtypes.c
@@ -0,0 +1,1440 @@
+/* Support routines for manipulating internal types for GDB.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+ Contributed by Cygnus Support, using pieces from other GDB modules.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include <string.h>
+#include "bfd.h"
+#include "symtab.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "gdbtypes.h"
+#include "expression.h"
+#include "language.h"
+#include "target.h"
+#include "value.h"
+#include "demangle.h"
+#include "complaints.h"
+
+/* These variables point to the objects
+ representing the predefined C data types. */
+
+struct type *builtin_type_void;
+struct type *builtin_type_char;
+struct type *builtin_type_short;
+struct type *builtin_type_int;
+struct type *builtin_type_long;
+struct type *builtin_type_long_long;
+struct type *builtin_type_signed_char;
+struct type *builtin_type_unsigned_char;
+struct type *builtin_type_unsigned_short;
+struct type *builtin_type_unsigned_int;
+struct type *builtin_type_unsigned_long;
+struct type *builtin_type_unsigned_long_long;
+struct type *builtin_type_float;
+struct type *builtin_type_double;
+struct type *builtin_type_long_double;
+struct type *builtin_type_complex;
+struct type *builtin_type_double_complex;
+struct type *builtin_type_string;
+
+/* Alloc a new type structure and fill it with some defaults. If
+ OBJFILE is non-NULL, then allocate the space for the type structure
+ in that objfile's type_obstack. */
+
+struct type *
+alloc_type (objfile)
+ struct objfile *objfile;
+{
+ register struct type *type;
+
+ /* Alloc the structure and start off with all fields zeroed. */
+
+ if (objfile == NULL)
+ {
+ type = (struct type *) xmalloc (sizeof (struct type));
+ }
+ else
+ {
+ type = (struct type *) obstack_alloc (&objfile -> type_obstack,
+ sizeof (struct type));
+ }
+ memset ((char *) type, 0, sizeof (struct type));
+
+ /* Initialize the fields that might not be zero. */
+
+ TYPE_CODE (type) = TYPE_CODE_UNDEF;
+ TYPE_OBJFILE (type) = objfile;
+ TYPE_VPTR_FIELDNO (type) = -1;
+
+ return (type);
+}
+
+/* Lookup a pointer to a type TYPE. TYPEPTR, if nonzero, points
+ to a pointer to memory where the pointer type should be stored.
+ If *TYPEPTR is zero, update it to point to the pointer type we return.
+ We allocate new memory if needed. */
+
+struct type *
+make_pointer_type (type, typeptr)
+ struct type *type;
+ struct type **typeptr;
+{
+ register struct type *ntype; /* New type */
+ struct objfile *objfile;
+
+ ntype = TYPE_POINTER_TYPE (type);
+
+ if (ntype)
+ if (typeptr == 0)
+ return ntype; /* Don't care about alloc, and have new type. */
+ else if (*typeptr == 0)
+ {
+ *typeptr = ntype; /* Tracking alloc, and we have new type. */
+ return ntype;
+ }
+
+ if (typeptr == 0 || *typeptr == 0) /* We'll need to allocate one. */
+ {
+ ntype = alloc_type (TYPE_OBJFILE (type));
+ if (typeptr)
+ *typeptr = ntype;
+ }
+ else /* We have storage, but need to reset it. */
+ {
+ ntype = *typeptr;
+ objfile = TYPE_OBJFILE (ntype);
+ memset ((char *) ntype, 0, sizeof (struct type));
+ TYPE_OBJFILE (ntype) = objfile;
+ }
+
+ TYPE_TARGET_TYPE (ntype) = type;
+ TYPE_POINTER_TYPE (type) = ntype;
+
+ /* FIXME! Assume the machine has only one representation for pointers! */
+
+ TYPE_LENGTH (ntype) = TARGET_PTR_BIT / TARGET_CHAR_BIT;
+ TYPE_CODE (ntype) = TYPE_CODE_PTR;
+
+ /* pointers are unsigned */
+ TYPE_FLAGS (ntype) |= TYPE_FLAG_UNSIGNED;
+
+ if (!TYPE_POINTER_TYPE (type)) /* Remember it, if don't have one. */
+ TYPE_POINTER_TYPE (type) = ntype;
+
+ return ntype;
+}
+
+/* Given a type TYPE, return a type of pointers to that type.
+ May need to construct such a type if this is the first use. */
+
+struct type *
+lookup_pointer_type (type)
+ struct type *type;
+{
+ return make_pointer_type (type, (struct type **)0);
+}
+
+/* Lookup a C++ `reference' to a type TYPE. TYPEPTR, if nonzero, points
+ to a pointer to memory where the reference type should be stored.
+ If *TYPEPTR is zero, update it to point to the reference type we return.
+ We allocate new memory if needed. */
+
+struct type *
+make_reference_type (type, typeptr)
+ struct type *type;
+ struct type **typeptr;
+{
+ register struct type *ntype; /* New type */
+ struct objfile *objfile;
+
+ ntype = TYPE_REFERENCE_TYPE (type);
+
+ if (ntype)
+ if (typeptr == 0)
+ return ntype; /* Don't care about alloc, and have new type. */
+ else if (*typeptr == 0)
+ {
+ *typeptr = ntype; /* Tracking alloc, and we have new type. */
+ return ntype;
+ }
+
+ if (typeptr == 0 || *typeptr == 0) /* We'll need to allocate one. */
+ {
+ ntype = alloc_type (TYPE_OBJFILE (type));
+ if (typeptr)
+ *typeptr = ntype;
+ }
+ else /* We have storage, but need to reset it. */
+ {
+ ntype = *typeptr;
+ objfile = TYPE_OBJFILE (ntype);
+ memset ((char *) ntype, 0, sizeof (struct type));
+ TYPE_OBJFILE (ntype) = objfile;
+ }
+
+ TYPE_TARGET_TYPE (ntype) = type;
+ TYPE_REFERENCE_TYPE (type) = ntype;
+
+ /* FIXME! Assume the machine has only one representation for references,
+ and that it matches the (only) representation for pointers! */
+
+ TYPE_LENGTH (ntype) = TARGET_PTR_BIT / TARGET_CHAR_BIT;
+ TYPE_CODE (ntype) = TYPE_CODE_REF;
+
+ if (!TYPE_REFERENCE_TYPE (type)) /* Remember it, if don't have one. */
+ TYPE_REFERENCE_TYPE (type) = ntype;
+
+ return ntype;
+}
+
+/* Same as above, but caller doesn't care about memory allocation details. */
+
+struct type *
+lookup_reference_type (type)
+ struct type *type;
+{
+ return make_reference_type (type, (struct type **)0);
+}
+
+/* Lookup a function type that returns type TYPE. TYPEPTR, if nonzero, points
+ to a pointer to memory where the function type should be stored.
+ If *TYPEPTR is zero, update it to point to the function type we return.
+ We allocate new memory if needed. */
+
+struct type *
+make_function_type (type, typeptr)
+ struct type *type;
+ struct type **typeptr;
+{
+ register struct type *ntype; /* New type */
+ struct objfile *objfile;
+
+ ntype = TYPE_FUNCTION_TYPE (type);
+
+ if (ntype)
+ if (typeptr == 0)
+ return ntype; /* Don't care about alloc, and have new type. */
+ else if (*typeptr == 0)
+ {
+ *typeptr = ntype; /* Tracking alloc, and we have new type. */
+ return ntype;
+ }
+
+ if (typeptr == 0 || *typeptr == 0) /* We'll need to allocate one. */
+ {
+ ntype = alloc_type (TYPE_OBJFILE (type));
+ if (typeptr)
+ *typeptr = ntype;
+ }
+ else /* We have storage, but need to reset it. */
+ {
+ ntype = *typeptr;
+ objfile = TYPE_OBJFILE (ntype);
+ memset ((char *) ntype, 0, sizeof (struct type));
+ TYPE_OBJFILE (ntype) = objfile;
+ }
+
+ TYPE_TARGET_TYPE (ntype) = type;
+ TYPE_FUNCTION_TYPE (type) = ntype;
+
+ TYPE_LENGTH (ntype) = 1;
+ TYPE_CODE (ntype) = TYPE_CODE_FUNC;
+
+ if (!TYPE_FUNCTION_TYPE (type)) /* Remember it, if don't have one. */
+ TYPE_FUNCTION_TYPE (type) = ntype;
+
+ return ntype;
+}
+
+
+/* Given a type TYPE, return a type of functions that return that type.
+ May need to construct such a type if this is the first use. */
+
+struct type *
+lookup_function_type (type)
+ struct type *type;
+{
+ return make_function_type (type, (struct type **)0);
+}
+
+/* Implement direct support for MEMBER_TYPE in GNU C++.
+ May need to construct such a type if this is the first use.
+ The TYPE is the type of the member. The DOMAIN is the type
+ of the aggregate that the member belongs to. */
+
+struct type *
+lookup_member_type (type, domain)
+ struct type *type;
+ struct type *domain;
+{
+ register struct type *mtype;
+
+ mtype = alloc_type (TYPE_OBJFILE (type));
+ smash_to_member_type (mtype, domain, type);
+ return (mtype);
+}
+
+/* Allocate a stub method whose return type is TYPE.
+ This apparently happens for speed of symbol reading, since parsing
+ out the arguments to the method is cpu-intensive, the way we are doing
+ it. So, we will fill in arguments later.
+ This always returns a fresh type. */
+
+struct type *
+allocate_stub_method (type)
+ struct type *type;
+{
+ struct type *mtype;
+
+ mtype = alloc_type (TYPE_OBJFILE (type));
+ TYPE_TARGET_TYPE (mtype) = type;
+ /* _DOMAIN_TYPE (mtype) = unknown yet */
+ /* _ARG_TYPES (mtype) = unknown yet */
+ TYPE_FLAGS (mtype) = TYPE_FLAG_STUB;
+ TYPE_CODE (mtype) = TYPE_CODE_METHOD;
+ TYPE_LENGTH (mtype) = 1;
+ return (mtype);
+}
+
+/* Create a range type using either a blank type supplied in RESULT_TYPE,
+ or creating a new type, inheriting the objfile from INDEX_TYPE.
+
+ Indices will be of type INDEX_TYPE, and will range from LOW_BOUND to
+ HIGH_BOUND, inclusive.
+
+ FIXME: Maybe we should check the TYPE_CODE of RESULT_TYPE to make
+ sure it is TYPE_CODE_UNDEF before we bash it into a range type? */
+
+struct type *
+create_range_type (result_type, index_type, low_bound, high_bound)
+ struct type *result_type;
+ struct type *index_type;
+ int low_bound;
+ int high_bound;
+{
+ if (result_type == NULL)
+ {
+ result_type = alloc_type (TYPE_OBJFILE (index_type));
+ }
+ TYPE_CODE (result_type) = TYPE_CODE_RANGE;
+ TYPE_TARGET_TYPE (result_type) = index_type;
+ TYPE_LENGTH (result_type) = TYPE_LENGTH (index_type);
+ TYPE_NFIELDS (result_type) = 2;
+ TYPE_FIELDS (result_type) = (struct field *)
+ TYPE_ALLOC (result_type, 2 * sizeof (struct field));
+ memset (TYPE_FIELDS (result_type), 0, 2 * sizeof (struct field));
+ TYPE_FIELD_BITPOS (result_type, 0) = low_bound;
+ TYPE_FIELD_BITPOS (result_type, 1) = high_bound;
+ TYPE_FIELD_TYPE (result_type, 0) = builtin_type_int; /* FIXME */
+ TYPE_FIELD_TYPE (result_type, 1) = builtin_type_int; /* FIXME */
+
+ return (result_type);
+}
+
+
+/* Create an array type using either a blank type supplied in RESULT_TYPE,
+ or creating a new type, inheriting the objfile from RANGE_TYPE.
+
+ Elements will be of type ELEMENT_TYPE, the indices will be of type
+ RANGE_TYPE.
+
+ FIXME: Maybe we should check the TYPE_CODE of RESULT_TYPE to make
+ sure it is TYPE_CODE_UNDEF before we bash it into an array type? */
+
+struct type *
+create_array_type (result_type, element_type, range_type)
+ struct type *result_type;
+ struct type *element_type;
+ struct type *range_type;
+{
+ int low_bound;
+ int high_bound;
+
+ if (TYPE_CODE (range_type) != TYPE_CODE_RANGE)
+ {
+ /* FIXME: We only handle range types at the moment. Complain and
+ create a dummy range type to use. */
+ warning ("internal error: array index type must be a range type");
+ range_type = lookup_fundamental_type (TYPE_OBJFILE (range_type),
+ FT_INTEGER);
+ range_type = create_range_type ((struct type *) NULL, range_type, 0, 0);
+ }
+ if (result_type == NULL)
+ {
+ result_type = alloc_type (TYPE_OBJFILE (range_type));
+ }
+ TYPE_CODE (result_type) = TYPE_CODE_ARRAY;
+ TYPE_TARGET_TYPE (result_type) = element_type;
+ low_bound = TYPE_FIELD_BITPOS (range_type, 0);
+ high_bound = TYPE_FIELD_BITPOS (range_type, 1);
+ TYPE_LENGTH (result_type) =
+ TYPE_LENGTH (element_type) * (high_bound - low_bound + 1);
+ TYPE_NFIELDS (result_type) = 1;
+ TYPE_FIELDS (result_type) =
+ (struct field *) TYPE_ALLOC (result_type, sizeof (struct field));
+ memset (TYPE_FIELDS (result_type), 0, sizeof (struct field));
+ TYPE_FIELD_TYPE (result_type, 0) = range_type;
+ TYPE_VPTR_FIELDNO (result_type) = -1;
+
+ return (result_type);
+}
+
+/* Create a string type using either a blank type supplied in RESULT_TYPE,
+ or creating a new type. String types are similar enough to array of
+ char types that we can use create_array_type to build the basic type
+ and then bash it into a string type.
+
+ For fixed length strings, the range type contains 0 as the lower
+ bound and the length of the string minus one as the upper bound.
+
+ FIXME: Maybe we should check the TYPE_CODE of RESULT_TYPE to make
+ sure it is TYPE_CODE_UNDEF before we bash it into a string type? */
+
+struct type *
+create_string_type (result_type, range_type)
+ struct type *result_type;
+ struct type *range_type;
+{
+ result_type = create_array_type (result_type, builtin_type_char, range_type);
+ TYPE_CODE (result_type) = TYPE_CODE_STRING;
+ return (result_type);
+}
+
+/* Smash TYPE to be a type of members of DOMAIN with type TO_TYPE.
+ A MEMBER is a wierd thing -- it amounts to a typed offset into
+ a struct, e.g. "an int at offset 8". A MEMBER TYPE doesn't
+ include the offset (that's the value of the MEMBER itself), but does
+ include the structure type into which it points (for some reason).
+
+ When "smashing" the type, we preserve the objfile that the
+ old type pointed to, since we aren't changing where the type is actually
+ allocated. */
+
+void
+smash_to_member_type (type, domain, to_type)
+ struct type *type;
+ struct type *domain;
+ struct type *to_type;
+{
+ struct objfile *objfile;
+
+ objfile = TYPE_OBJFILE (type);
+
+ memset ((char *) type, 0, sizeof (struct type));
+ TYPE_OBJFILE (type) = objfile;
+ TYPE_TARGET_TYPE (type) = to_type;
+ TYPE_DOMAIN_TYPE (type) = domain;
+ TYPE_LENGTH (type) = 1; /* In practice, this is never needed. */
+ TYPE_CODE (type) = TYPE_CODE_MEMBER;
+}
+
+/* Smash TYPE to be a type of method of DOMAIN with type TO_TYPE.
+ METHOD just means `function that gets an extra "this" argument'.
+
+ When "smashing" the type, we preserve the objfile that the
+ old type pointed to, since we aren't changing where the type is actually
+ allocated. */
+
+void
+smash_to_method_type (type, domain, to_type, args)
+ struct type *type;
+ struct type *domain;
+ struct type *to_type;
+ struct type **args;
+{
+ struct objfile *objfile;
+
+ objfile = TYPE_OBJFILE (type);
+
+ memset ((char *) type, 0, sizeof (struct type));
+ TYPE_OBJFILE (type) = objfile;
+ TYPE_TARGET_TYPE (type) = to_type;
+ TYPE_DOMAIN_TYPE (type) = domain;
+ TYPE_ARG_TYPES (type) = args;
+ TYPE_LENGTH (type) = 1; /* In practice, this is never needed. */
+ TYPE_CODE (type) = TYPE_CODE_METHOD;
+}
+
+/* Return a typename for a struct/union/enum type without "struct ",
+ "union ", or "enum ". If the type has a NULL name, return NULL. */
+
+char *
+type_name_no_tag (type)
+ register const struct type *type;
+{
+ if (TYPE_TAG_NAME (type) != NULL)
+ return TYPE_TAG_NAME (type);
+
+ /* Is there code which expects this to return the name if there is no
+ tag name? My guess is that this is mainly used for C++ in cases where
+ the two will always be the same. */
+ return TYPE_NAME (type);
+}
+
+/* Lookup a primitive type named NAME.
+ Return zero if NAME is not a primitive type.*/
+
+struct type *
+lookup_primitive_typename (name)
+ char *name;
+{
+ struct type ** const *p;
+
+ for (p = current_language -> la_builtin_type_vector; *p != NULL; p++)
+ {
+ if (STREQ ((**p) -> name, name))
+ {
+ return (**p);
+ }
+ }
+ return (NULL);
+}
+
+/* Lookup a typedef or primitive type named NAME,
+ visible in lexical block BLOCK.
+ If NOERR is nonzero, return zero if NAME is not suitably defined. */
+
+struct type *
+lookup_typename (name, block, noerr)
+ char *name;
+ struct block *block;
+ int noerr;
+{
+ register struct symbol *sym;
+ register struct type *tmp;
+
+ sym = lookup_symbol (name, block, VAR_NAMESPACE, 0, (struct symtab **) NULL);
+ if (sym == NULL || SYMBOL_CLASS (sym) != LOC_TYPEDEF)
+ {
+ tmp = lookup_primitive_typename (name);
+ if (tmp)
+ {
+ return (tmp);
+ }
+ else if (!tmp && noerr)
+ {
+ return (NULL);
+ }
+ else
+ {
+ error ("No type named %s.", name);
+ }
+ }
+ return (SYMBOL_TYPE (sym));
+}
+
+struct type *
+lookup_unsigned_typename (name)
+ char *name;
+{
+ char *uns = alloca (strlen (name) + 10);
+
+ strcpy (uns, "unsigned ");
+ strcpy (uns + 9, name);
+ return (lookup_typename (uns, (struct block *) NULL, 0));
+}
+
+struct type *
+lookup_signed_typename (name)
+ char *name;
+{
+ struct type *t;
+ char *uns = alloca (strlen (name) + 8);
+
+ strcpy (uns, "signed ");
+ strcpy (uns + 7, name);
+ t = lookup_typename (uns, (struct block *) NULL, 1);
+ /* If we don't find "signed FOO" just try again with plain "FOO". */
+ if (t != NULL)
+ return t;
+ return lookup_typename (name, (struct block *) NULL, 0);
+}
+
+/* Lookup a structure type named "struct NAME",
+ visible in lexical block BLOCK. */
+
+struct type *
+lookup_struct (name, block)
+ char *name;
+ struct block *block;
+{
+ register struct symbol *sym;
+
+ sym = lookup_symbol (name, block, STRUCT_NAMESPACE, 0,
+ (struct symtab **) NULL);
+
+ if (sym == NULL)
+ {
+ error ("No struct type named %s.", name);
+ }
+ if (TYPE_CODE (SYMBOL_TYPE (sym)) != TYPE_CODE_STRUCT)
+ {
+ error ("This context has class, union or enum %s, not a struct.", name);
+ }
+ return (SYMBOL_TYPE (sym));
+}
+
+/* Lookup a union type named "union NAME",
+ visible in lexical block BLOCK. */
+
+struct type *
+lookup_union (name, block)
+ char *name;
+ struct block *block;
+{
+ register struct symbol *sym;
+
+ sym = lookup_symbol (name, block, STRUCT_NAMESPACE, 0,
+ (struct symtab **) NULL);
+
+ if (sym == NULL)
+ {
+ error ("No union type named %s.", name);
+ }
+ if (TYPE_CODE (SYMBOL_TYPE (sym)) != TYPE_CODE_UNION)
+ {
+ error ("This context has class, struct or enum %s, not a union.", name);
+ }
+ return (SYMBOL_TYPE (sym));
+}
+
+/* Lookup an enum type named "enum NAME",
+ visible in lexical block BLOCK. */
+
+struct type *
+lookup_enum (name, block)
+ char *name;
+ struct block *block;
+{
+ register struct symbol *sym;
+
+ sym = lookup_symbol (name, block, STRUCT_NAMESPACE, 0,
+ (struct symtab **) NULL);
+ if (sym == NULL)
+ {
+ error ("No enum type named %s.", name);
+ }
+ if (TYPE_CODE (SYMBOL_TYPE (sym)) != TYPE_CODE_ENUM)
+ {
+ error ("This context has class, struct or union %s, not an enum.", name);
+ }
+ return (SYMBOL_TYPE (sym));
+}
+
+/* Lookup a template type named "template NAME<TYPE>",
+ visible in lexical block BLOCK. */
+
+struct type *
+lookup_template_type (name, type, block)
+ char *name;
+ struct type *type;
+ struct block *block;
+{
+ struct symbol *sym;
+ char *nam = (char*) alloca(strlen(name) + strlen(type->name) + 4);
+ strcpy (nam, name);
+ strcat (nam, "<");
+ strcat (nam, type->name);
+ strcat (nam, " >"); /* FIXME, extra space still introduced in gcc? */
+
+ sym = lookup_symbol (nam, block, VAR_NAMESPACE, 0, (struct symtab **)NULL);
+
+ if (sym == NULL)
+ {
+ error ("No template type named %s.", name);
+ }
+ if (TYPE_CODE (SYMBOL_TYPE (sym)) != TYPE_CODE_STRUCT)
+ {
+ error ("This context has class, union or enum %s, not a struct.", name);
+ }
+ return (SYMBOL_TYPE (sym));
+}
+
+/* Given a type TYPE, lookup the type of the component of type named NAME.
+
+ TYPE can be either a struct or union, or a pointer or reference to a struct or
+ union. If it is a pointer or reference, its target type is automatically used.
+ Thus '.' and '->' are interchangable, as specified for the definitions of the
+ expression element types STRUCTOP_STRUCT and STRUCTOP_PTR.
+
+ If NOERR is nonzero, return zero if NAME is not suitably defined.
+ If NAME is the name of a baseclass type, return that type. */
+
+struct type *
+lookup_struct_elt_type (type, name, noerr)
+ struct type *type;
+ char *name;
+ int noerr;
+{
+ int i;
+
+ if (TYPE_CODE (type) == TYPE_CODE_PTR ||
+ TYPE_CODE (type) == TYPE_CODE_REF)
+ type = TYPE_TARGET_TYPE (type);
+
+ if (TYPE_CODE (type) != TYPE_CODE_STRUCT &&
+ TYPE_CODE (type) != TYPE_CODE_UNION)
+ {
+ target_terminal_ours ();
+ fflush (stdout);
+ fprintf (stderr, "Type ");
+ type_print (type, "", stderr, -1);
+ error (" is not a structure or union type.");
+ }
+
+ check_stub_type (type);
+
+#if 0
+ /* FIXME: This change put in by Michael seems incorrect for the case where
+ the structure tag name is the same as the member name. I.E. when doing
+ "ptype bell->bar" for "struct foo { int bar; int foo; } bell;"
+ Disabled by fnf. */
+ {
+ char *typename;
+
+ typename = type_name_no_tag (type);
+ if (typename != NULL && STREQ (typename, name))
+ return type;
+ }
+#endif
+
+ for (i = TYPE_NFIELDS (type) - 1; i >= TYPE_N_BASECLASSES (type); i--)
+ {
+ char *t_field_name = TYPE_FIELD_NAME (type, i);
+
+ if (t_field_name && STREQ (t_field_name, name))
+ {
+ return TYPE_FIELD_TYPE (type, i);
+ }
+ }
+
+ /* OK, it's not in this class. Recursively check the baseclasses. */
+ for (i = TYPE_N_BASECLASSES (type) - 1; i >= 0; i--)
+ {
+ struct type *t;
+
+ t = lookup_struct_elt_type (TYPE_BASECLASS (type, i), name, noerr);
+ if (t != NULL)
+ {
+ return t;
+ }
+ }
+
+ if (noerr)
+ {
+ return NULL;
+ }
+
+ target_terminal_ours ();
+ fflush (stdout);
+ fprintf (stderr, "Type ");
+ type_print (type, "", stderr, -1);
+ fprintf (stderr, " has no component named ");
+ fputs_filtered (name, stderr);
+ error (".");
+ return (struct type *)-1; /* For lint */
+}
+
+/* If possible, make the vptr_fieldno and vptr_basetype fields of TYPE
+ valid. Callers should be aware that in some cases (for example,
+ the type or one of its baseclasses is a stub type and we are
+ debugging a .o file), this function will not be able to find the virtual
+ function table pointer, and vptr_fieldno will remain -1 and vptr_basetype
+ will remain NULL. */
+
+void
+fill_in_vptr_fieldno (type)
+ struct type *type;
+{
+ check_stub_type (type);
+
+ if (TYPE_VPTR_FIELDNO (type) < 0)
+ {
+ int i;
+
+ /* We must start at zero in case the first (and only) baseclass is
+ virtual (and hence we cannot share the table pointer). */
+ for (i = 0; i < TYPE_N_BASECLASSES (type); i++)
+ {
+ fill_in_vptr_fieldno (TYPE_BASECLASS (type, i));
+ if (TYPE_VPTR_FIELDNO (TYPE_BASECLASS (type, i)) >= 0)
+ {
+ TYPE_VPTR_FIELDNO (type)
+ = TYPE_VPTR_FIELDNO (TYPE_BASECLASS (type, i));
+ TYPE_VPTR_BASETYPE (type)
+ = TYPE_VPTR_BASETYPE (TYPE_BASECLASS (type, i));
+ break;
+ }
+ }
+ }
+}
+
+/* Added by Bryan Boreham, Kewill, Sun Sep 17 18:07:17 1989.
+
+ If this is a stubbed struct (i.e. declared as struct foo *), see if
+ we can find a full definition in some other file. If so, copy this
+ definition, so we can use it in future. If not, set a flag so we
+ don't waste too much time in future. (FIXME, this doesn't seem
+ to be happening...)
+
+ This used to be coded as a macro, but I don't think it is called
+ often enough to merit such treatment.
+*/
+
+struct complaint stub_noname_complaint =
+ {"stub type has NULL name", 0, 0};
+
+void
+check_stub_type (type)
+ struct type *type;
+{
+ if (TYPE_FLAGS(type) & TYPE_FLAG_STUB)
+ {
+ char* name = type_name_no_tag (type);
+ /* FIXME: shouldn't we separately check the TYPE_NAME and the
+ TYPE_TAG_NAME, and look in STRUCT_NAMESPACE and/or VAR_NAMESPACE
+ as appropriate? (this code was written before TYPE_NAME and
+ TYPE_TAG_NAME were separate). */
+ struct symbol *sym;
+ if (name == NULL)
+ {
+ complain (&stub_noname_complaint);
+ return;
+ }
+ sym = lookup_symbol (name, 0, STRUCT_NAMESPACE, 0,
+ (struct symtab **) NULL);
+ if (sym)
+ {
+ memcpy ((char *)type, (char *)SYMBOL_TYPE(sym), sizeof (struct type));
+ }
+ }
+}
+
+/* Ugly hack to convert method stubs into method types.
+
+ He ain't kiddin'. This demangles the name of the method into a string
+ including argument types, parses out each argument type, generates
+ a string casting a zero to that type, evaluates the string, and stuffs
+ the resulting type into an argtype vector!!! Then it knows the type
+ of the whole function (including argument types for overloading),
+ which info used to be in the stab's but was removed to hack back
+ the space required for them. */
+
+void
+check_stub_method (type, i, j)
+ struct type *type;
+ int i;
+ int j;
+{
+ struct fn_field *f;
+ char *mangled_name = gdb_mangle_name (type, i, j);
+ char *demangled_name = cplus_demangle (mangled_name,
+ DMGL_PARAMS | DMGL_ANSI);
+ char *argtypetext, *p;
+ int depth = 0, argcount = 1;
+ struct type **argtypes;
+ struct type *mtype;
+
+ if (demangled_name == NULL)
+ {
+ error ("Internal: Cannot demangle mangled name `%s'.", mangled_name);
+ }
+
+ /* Now, read in the parameters that define this type. */
+ argtypetext = strchr (demangled_name, '(') + 1;
+ p = argtypetext;
+ while (*p)
+ {
+ if (*p == '(')
+ {
+ depth += 1;
+ }
+ else if (*p == ')')
+ {
+ depth -= 1;
+ }
+ else if (*p == ',' && depth == 0)
+ {
+ argcount += 1;
+ }
+
+ p += 1;
+ }
+
+ /* We need two more slots: one for the THIS pointer, and one for the
+ NULL [...] or void [end of arglist]. */
+
+ argtypes = (struct type **)
+ TYPE_ALLOC (type, (argcount + 2) * sizeof (struct type *));
+ p = argtypetext;
+ argtypes[0] = lookup_pointer_type (type);
+ argcount = 1;
+
+ if (*p != ')') /* () means no args, skip while */
+ {
+ depth = 0;
+ while (*p)
+ {
+ if (depth <= 0 && (*p == ',' || *p == ')'))
+ {
+ argtypes[argcount] =
+ parse_and_eval_type (argtypetext, p - argtypetext);
+ argcount += 1;
+ argtypetext = p + 1;
+ }
+
+ if (*p == '(')
+ {
+ depth += 1;
+ }
+ else if (*p == ')')
+ {
+ depth -= 1;
+ }
+
+ p += 1;
+ }
+ }
+
+ if (p[-2] != '.') /* Not '...' */
+ {
+ argtypes[argcount] = builtin_type_void; /* List terminator */
+ }
+ else
+ {
+ argtypes[argcount] = NULL; /* Ellist terminator */
+ }
+
+ free (demangled_name);
+
+ f = TYPE_FN_FIELDLIST1 (type, i);
+ TYPE_FN_FIELD_PHYSNAME (f, j) = mangled_name;
+
+ /* Now update the old "stub" type into a real type. */
+ mtype = TYPE_FN_FIELD_TYPE (f, j);
+ TYPE_DOMAIN_TYPE (mtype) = type;
+ TYPE_ARG_TYPES (mtype) = argtypes;
+ TYPE_FLAGS (mtype) &= ~TYPE_FLAG_STUB;
+ TYPE_FN_FIELD_STUB (f, j) = 0;
+}
+
+const struct cplus_struct_type cplus_struct_default;
+
+void
+allocate_cplus_struct_type (type)
+ struct type *type;
+{
+ if (!HAVE_CPLUS_STRUCT (type))
+ {
+ TYPE_CPLUS_SPECIFIC (type) = (struct cplus_struct_type *)
+ TYPE_ALLOC (type, sizeof (struct cplus_struct_type));
+ *(TYPE_CPLUS_SPECIFIC(type)) = cplus_struct_default;
+ }
+}
+
+/* Helper function to initialize the standard scalar types.
+
+ If NAME is non-NULL and OBJFILE is non-NULL, then we make a copy
+ of the string pointed to by name in the type_obstack for that objfile,
+ and initialize the type name to that copy. There are places (mipsread.c
+ in particular, where init_type is called with a NULL value for NAME). */
+
+struct type *
+init_type (code, length, flags, name, objfile)
+ enum type_code code;
+ int length;
+ int flags;
+ char *name;
+ struct objfile *objfile;
+{
+ register struct type *type;
+
+ type = alloc_type (objfile);
+ TYPE_CODE (type) = code;
+ TYPE_LENGTH (type) = length;
+ TYPE_FLAGS (type) |= flags;
+ if ((name != NULL) && (objfile != NULL))
+ {
+ TYPE_NAME (type) =
+ obsavestring (name, strlen (name), &objfile -> type_obstack);
+ }
+ else
+ {
+ TYPE_NAME (type) = name;
+ }
+
+ /* C++ fancies. */
+
+ if (code == TYPE_CODE_STRUCT || code == TYPE_CODE_UNION)
+ {
+ INIT_CPLUS_SPECIFIC (type);
+ }
+ return (type);
+}
+
+/* Look up a fundamental type for the specified objfile.
+ May need to construct such a type if this is the first use.
+
+ Some object file formats (ELF, COFF, etc) do not define fundamental
+ types such as "int" or "double". Others (stabs for example), do
+ define fundamental types.
+
+ For the formats which don't provide fundamental types, gdb can create
+ such types, using defaults reasonable for the current language and
+ the current target machine.
+
+ NOTE: This routine is obsolescent. Each debugging format reader
+ should manage it's own fundamental types, either creating them from
+ suitable defaults or reading them from the debugging information,
+ whichever is appropriate. The DWARF reader has already been
+ fixed to do this. Once the other readers are fixed, this routine
+ will go away. Also note that fundamental types should be managed
+ on a compilation unit basis in a multi-language environment, not
+ on a linkage unit basis as is done here. */
+
+
+struct type *
+lookup_fundamental_type (objfile, typeid)
+ struct objfile *objfile;
+ int typeid;
+{
+ register struct type **typep;
+ register int nbytes;
+
+ if (typeid < 0 || typeid >= FT_NUM_MEMBERS)
+ {
+ error ("internal error - invalid fundamental type id %d", typeid);
+ }
+
+ /* If this is the first time we need a fundamental type for this objfile
+ then we need to initialize the vector of type pointers. */
+
+ if (objfile -> fundamental_types == NULL)
+ {
+ nbytes = FT_NUM_MEMBERS * sizeof (struct type *);
+ objfile -> fundamental_types = (struct type **)
+ obstack_alloc (&objfile -> type_obstack, nbytes);
+ memset ((char *) objfile -> fundamental_types, 0, nbytes);
+ }
+
+ /* Look for this particular type in the fundamental type vector. If one is
+ not found, create and install one appropriate for the current language. */
+
+ typep = objfile -> fundamental_types + typeid;
+ if (*typep == NULL)
+ {
+ *typep = create_fundamental_type (objfile, typeid);
+ }
+
+ return (*typep);
+}
+
+#if MAINTENANCE_CMDS
+
+static void
+print_bit_vector (bits, nbits)
+ B_TYPE *bits;
+ int nbits;
+{
+ int bitno;
+
+ for (bitno = 0; bitno < nbits; bitno++)
+ {
+ if ((bitno % 8) == 0)
+ {
+ puts_filtered (" ");
+ }
+ if (B_TST (bits, bitno))
+ {
+ printf_filtered ("1");
+ }
+ else
+ {
+ printf_filtered ("0");
+ }
+ }
+}
+
+/* The args list is a strange beast. It is either terminated by a NULL
+ pointer for varargs functions, or by a pointer to a TYPE_CODE_VOID
+ type for normal fixed argcount functions. (FIXME someday)
+ Also note the first arg should be the "this" pointer, we may not want to
+ include it since we may get into a infinitely recursive situation. */
+
+static void
+print_arg_types (args, spaces)
+ struct type **args;
+ int spaces;
+{
+ if (args != NULL)
+ {
+ while (*args != NULL)
+ {
+ recursive_dump_type (*args, spaces + 2);
+ if ((*args++) -> code == TYPE_CODE_VOID)
+ {
+ break;
+ }
+ }
+ }
+}
+
+static void
+dump_fn_fieldlists (type, spaces)
+ struct type *type;
+ int spaces;
+{
+ int method_idx;
+ int overload_idx;
+ struct fn_field *f;
+
+ printfi_filtered (spaces, "fn_fieldlists 0x%lx\n",
+ (unsigned long) TYPE_FN_FIELDLISTS (type));
+ for (method_idx = 0; method_idx < TYPE_NFN_FIELDS (type); method_idx++)
+ {
+ f = TYPE_FN_FIELDLIST1 (type, method_idx);
+ printfi_filtered (spaces + 2, "[%d] name '%s' (0x%lx) length %d\n",
+ method_idx,
+ TYPE_FN_FIELDLIST_NAME (type, method_idx),
+ (unsigned long) TYPE_FN_FIELDLIST_NAME (type, method_idx),
+ TYPE_FN_FIELDLIST_LENGTH (type, method_idx));
+ for (overload_idx = 0;
+ overload_idx < TYPE_FN_FIELDLIST_LENGTH (type, method_idx);
+ overload_idx++)
+ {
+ printfi_filtered (spaces + 4, "[%d] physname '%s' (0x%lx)\n",
+ overload_idx,
+ TYPE_FN_FIELD_PHYSNAME (f, overload_idx),
+ (unsigned long) TYPE_FN_FIELD_PHYSNAME (f, overload_idx));
+ printfi_filtered (spaces + 8, "type 0x%lx\n",
+ (unsigned long) TYPE_FN_FIELD_TYPE (f, overload_idx));
+ recursive_dump_type (TYPE_FN_FIELD_TYPE (f, overload_idx),
+ spaces + 8 + 2);
+ printfi_filtered (spaces + 8, "args 0x%lx\n",
+ (unsigned long) TYPE_FN_FIELD_ARGS (f, overload_idx));
+ print_arg_types (TYPE_FN_FIELD_ARGS (f, overload_idx), spaces);
+ printfi_filtered (spaces + 8, "fcontext 0x%lx\n",
+ (unsigned long) TYPE_FN_FIELD_FCONTEXT (f, overload_idx));
+ printfi_filtered (spaces + 8, "is_const %d\n",
+ TYPE_FN_FIELD_CONST (f, overload_idx));
+ printfi_filtered (spaces + 8, "is_volatile %d\n",
+ TYPE_FN_FIELD_VOLATILE (f, overload_idx));
+ printfi_filtered (spaces + 8, "is_private %d\n",
+ TYPE_FN_FIELD_PRIVATE (f, overload_idx));
+ printfi_filtered (spaces + 8, "is_protected %d\n",
+ TYPE_FN_FIELD_PROTECTED (f, overload_idx));
+ printfi_filtered (spaces + 8, "is_stub %d\n",
+ TYPE_FN_FIELD_STUB (f, overload_idx));
+ printfi_filtered (spaces + 8, "voffset %u\n",
+ TYPE_FN_FIELD_VOFFSET (f, overload_idx));
+ }
+ }
+}
+
+static void
+print_cplus_stuff (type, spaces)
+ struct type *type;
+ int spaces;
+{
+ printfi_filtered (spaces, "n_baseclasses %d\n",
+ TYPE_N_BASECLASSES (type));
+ printfi_filtered (spaces, "nfn_fields %d\n",
+ TYPE_NFN_FIELDS (type));
+ printfi_filtered (spaces, "nfn_fields_total %d\n",
+ TYPE_NFN_FIELDS_TOTAL (type));
+ if (TYPE_N_BASECLASSES (type) > 0)
+ {
+ printfi_filtered (spaces, "virtual_field_bits (%d bits at *0x%lx)",
+ TYPE_N_BASECLASSES (type),
+ (unsigned long) TYPE_FIELD_VIRTUAL_BITS (type));
+ print_bit_vector (TYPE_FIELD_VIRTUAL_BITS (type),
+ TYPE_N_BASECLASSES (type));
+ puts_filtered ("\n");
+ }
+ if (TYPE_NFIELDS (type) > 0)
+ {
+ if (TYPE_FIELD_PRIVATE_BITS (type) != NULL)
+ {
+ printfi_filtered (spaces, "private_field_bits (%d bits at *0x%lx)",
+ TYPE_NFIELDS (type),
+ (unsigned long) TYPE_FIELD_PRIVATE_BITS (type));
+ print_bit_vector (TYPE_FIELD_PRIVATE_BITS (type),
+ TYPE_NFIELDS (type));
+ puts_filtered ("\n");
+ }
+ if (TYPE_FIELD_PROTECTED_BITS (type) != NULL)
+ {
+ printfi_filtered (spaces, "protected_field_bits (%d bits at *0x%lx)",
+ TYPE_NFIELDS (type),
+ (unsigned long) TYPE_FIELD_PROTECTED_BITS (type));
+ print_bit_vector (TYPE_FIELD_PROTECTED_BITS (type),
+ TYPE_NFIELDS (type));
+ puts_filtered ("\n");
+ }
+ }
+ if (TYPE_NFN_FIELDS (type) > 0)
+ {
+ dump_fn_fieldlists (type, spaces);
+ }
+}
+
+void
+recursive_dump_type (type, spaces)
+ struct type *type;
+ int spaces;
+{
+ int idx;
+
+ printfi_filtered (spaces, "type node 0x%lx\n", (unsigned long)type);
+ printfi_filtered (spaces, "name '%s' (0x%lx)\n",
+ TYPE_NAME (type) ? TYPE_NAME (type) : "<NULL>",
+ (unsigned long)TYPE_NAME (type));
+ if (TYPE_TAG_NAME (type) != NULL)
+ printfi_filtered (spaces, "tagname '%s' (0x%lx)\n",
+ TYPE_TAG_NAME (type),
+ (unsigned long)TYPE_TAG_NAME (type));
+ printfi_filtered (spaces, "code 0x%x ", TYPE_CODE (type));
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_UNDEF:
+ printf_filtered ("(TYPE_CODE_UNDEF)");
+ break;
+ case TYPE_CODE_PTR:
+ printf_filtered ("(TYPE_CODE_PTR)");
+ break;
+ case TYPE_CODE_ARRAY:
+ printf_filtered ("(TYPE_CODE_ARRAY)");
+ break;
+ case TYPE_CODE_STRUCT:
+ printf_filtered ("(TYPE_CODE_STRUCT)");
+ break;
+ case TYPE_CODE_UNION:
+ printf_filtered ("(TYPE_CODE_UNION)");
+ break;
+ case TYPE_CODE_ENUM:
+ printf_filtered ("(TYPE_CODE_ENUM)");
+ break;
+ case TYPE_CODE_FUNC:
+ printf_filtered ("(TYPE_CODE_FUNC)");
+ break;
+ case TYPE_CODE_INT:
+ printf_filtered ("(TYPE_CODE_INT)");
+ break;
+ case TYPE_CODE_FLT:
+ printf_filtered ("(TYPE_CODE_FLT)");
+ break;
+ case TYPE_CODE_VOID:
+ printf_filtered ("(TYPE_CODE_VOID)");
+ break;
+ case TYPE_CODE_SET:
+ printf_filtered ("(TYPE_CODE_SET)");
+ break;
+ case TYPE_CODE_RANGE:
+ printf_filtered ("(TYPE_CODE_RANGE)");
+ break;
+ case TYPE_CODE_STRING:
+ printf_filtered ("(TYPE_CODE_STRING)");
+ break;
+ case TYPE_CODE_ERROR:
+ printf_filtered ("(TYPE_CODE_ERROR)");
+ break;
+ case TYPE_CODE_MEMBER:
+ printf_filtered ("(TYPE_CODE_MEMBER)");
+ break;
+ case TYPE_CODE_METHOD:
+ printf_filtered ("(TYPE_CODE_METHOD)");
+ break;
+ case TYPE_CODE_REF:
+ printf_filtered ("(TYPE_CODE_REF)");
+ break;
+ case TYPE_CODE_CHAR:
+ printf_filtered ("(TYPE_CODE_CHAR)");
+ break;
+ case TYPE_CODE_BOOL:
+ printf_filtered ("(TYPE_CODE_BOOL)");
+ break;
+ default:
+ printf_filtered ("(UNKNOWN TYPE CODE)");
+ break;
+ }
+ puts_filtered ("\n");
+ printfi_filtered (spaces, "length %d\n", TYPE_LENGTH (type));
+ printfi_filtered (spaces, "objfile 0x%lx\n",
+ (unsigned long) TYPE_OBJFILE (type));
+ printfi_filtered (spaces, "target_type 0x%lx\n",
+ (unsigned long) TYPE_TARGET_TYPE (type));
+ if (TYPE_TARGET_TYPE (type) != NULL)
+ {
+ recursive_dump_type (TYPE_TARGET_TYPE (type), spaces + 2);
+ }
+ printfi_filtered (spaces, "pointer_type 0x%lx\n",
+ (unsigned long) TYPE_POINTER_TYPE (type));
+ printfi_filtered (spaces, "reference_type 0x%lx\n",
+ (unsigned long) TYPE_REFERENCE_TYPE (type));
+ printfi_filtered (spaces, "function_type 0x%lx\n",
+ (unsigned long) TYPE_FUNCTION_TYPE (type));
+ printfi_filtered (spaces, "flags 0x%x", TYPE_FLAGS (type));
+ if (TYPE_FLAGS (type) & TYPE_FLAG_UNSIGNED)
+ {
+ puts_filtered (" TYPE_FLAG_UNSIGNED");
+ }
+ if (TYPE_FLAGS (type) & TYPE_FLAG_SIGNED)
+ {
+ puts_filtered (" TYPE_FLAG_SIGNED");
+ }
+ if (TYPE_FLAGS (type) & TYPE_FLAG_STUB)
+ {
+ puts_filtered (" TYPE_FLAG_STUB");
+ }
+ puts_filtered ("\n");
+ printfi_filtered (spaces, "nfields %d 0x%lx\n", TYPE_NFIELDS (type),
+ (unsigned long) TYPE_FIELDS (type));
+ for (idx = 0; idx < TYPE_NFIELDS (type); idx++)
+ {
+ printfi_filtered (spaces + 2,
+ "[%d] bitpos %d bitsize %d type 0x%lx name '%s' (0x%lx)\n",
+ idx, TYPE_FIELD_BITPOS (type, idx),
+ TYPE_FIELD_BITSIZE (type, idx),
+ (unsigned long) TYPE_FIELD_TYPE (type, idx),
+ TYPE_FIELD_NAME (type, idx) != NULL
+ ? TYPE_FIELD_NAME (type, idx)
+ : "<NULL>",
+ (unsigned long) TYPE_FIELD_NAME (type, idx));
+ if (TYPE_FIELD_TYPE (type, idx) != NULL)
+ {
+ recursive_dump_type (TYPE_FIELD_TYPE (type, idx), spaces + 4);
+ }
+ }
+ printfi_filtered (spaces, "vptr_basetype 0x%lx\n",
+ (unsigned long) TYPE_VPTR_BASETYPE (type));
+ if (TYPE_VPTR_BASETYPE (type) != NULL)
+ {
+ recursive_dump_type (TYPE_VPTR_BASETYPE (type), spaces + 2);
+ }
+ printfi_filtered (spaces, "vptr_fieldno %d\n", TYPE_VPTR_FIELDNO (type));
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_METHOD:
+ case TYPE_CODE_FUNC:
+ printfi_filtered (spaces, "arg_types 0x%lx\n",
+ (unsigned long) TYPE_ARG_TYPES (type));
+ print_arg_types (TYPE_ARG_TYPES (type), spaces);
+ break;
+
+ case TYPE_CODE_STRUCT:
+ printfi_filtered (spaces, "cplus_stuff 0x%lx\n",
+ (unsigned long) TYPE_CPLUS_SPECIFIC (type));
+ print_cplus_stuff (type, spaces);
+ break;
+
+ default:
+ /* We have to pick one of the union types to be able print and test
+ the value. Pick cplus_struct_type, even though we know it isn't
+ any particular one. */
+ printfi_filtered (spaces, "type_specific 0x%lx",
+ (unsigned long) TYPE_CPLUS_SPECIFIC (type));
+ if (TYPE_CPLUS_SPECIFIC (type) != NULL)
+ {
+ printf_filtered (" (unknown data form)");
+ }
+ printf_filtered ("\n");
+ break;
+
+ }
+}
+
+#endif /* MAINTENANCE_CMDS */
+
+void
+_initialize_gdbtypes ()
+{
+ builtin_type_void =
+ init_type (TYPE_CODE_VOID, 1,
+ 0,
+ "void", (struct objfile *) NULL);
+ builtin_type_char =
+ init_type (TYPE_CODE_INT, TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+ 0,
+ "char", (struct objfile *) NULL);
+ builtin_type_signed_char =
+ init_type (TYPE_CODE_INT, TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_SIGNED,
+ "signed char", (struct objfile *) NULL);
+ builtin_type_unsigned_char =
+ init_type (TYPE_CODE_INT, TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED,
+ "unsigned char", (struct objfile *) NULL);
+ builtin_type_short =
+ init_type (TYPE_CODE_INT, TARGET_SHORT_BIT / TARGET_CHAR_BIT,
+ 0,
+ "short", (struct objfile *) NULL);
+ builtin_type_unsigned_short =
+ init_type (TYPE_CODE_INT, TARGET_SHORT_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED,
+ "unsigned short", (struct objfile *) NULL);
+ builtin_type_int =
+ init_type (TYPE_CODE_INT, TARGET_INT_BIT / TARGET_CHAR_BIT,
+ 0,
+ "int", (struct objfile *) NULL);
+ builtin_type_unsigned_int =
+ init_type (TYPE_CODE_INT, TARGET_INT_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED,
+ "unsigned int", (struct objfile *) NULL);
+ builtin_type_long =
+ init_type (TYPE_CODE_INT, TARGET_LONG_BIT / TARGET_CHAR_BIT,
+ 0,
+ "long", (struct objfile *) NULL);
+ builtin_type_unsigned_long =
+ init_type (TYPE_CODE_INT, TARGET_LONG_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED,
+ "unsigned long", (struct objfile *) NULL);
+ builtin_type_long_long =
+ init_type (TYPE_CODE_INT, TARGET_LONG_LONG_BIT / TARGET_CHAR_BIT,
+ 0,
+ "long long", (struct objfile *) NULL);
+ builtin_type_unsigned_long_long =
+ init_type (TYPE_CODE_INT, TARGET_LONG_LONG_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED,
+ "unsigned long long", (struct objfile *) NULL);
+ builtin_type_float =
+ init_type (TYPE_CODE_FLT, TARGET_FLOAT_BIT / TARGET_CHAR_BIT,
+ 0,
+ "float", (struct objfile *) NULL);
+ builtin_type_double =
+ init_type (TYPE_CODE_FLT, TARGET_DOUBLE_BIT / TARGET_CHAR_BIT,
+ 0,
+ "double", (struct objfile *) NULL);
+ builtin_type_long_double =
+ init_type (TYPE_CODE_FLT, TARGET_LONG_DOUBLE_BIT / TARGET_CHAR_BIT,
+ 0,
+ "long double", (struct objfile *) NULL);
+ builtin_type_complex =
+ init_type (TYPE_CODE_FLT, TARGET_COMPLEX_BIT / TARGET_CHAR_BIT,
+ 0,
+ "complex", (struct objfile *) NULL);
+ builtin_type_double_complex =
+ init_type (TYPE_CODE_FLT, TARGET_DOUBLE_COMPLEX_BIT / TARGET_CHAR_BIT,
+ 0,
+ "double complex", (struct objfile *) NULL);
+ builtin_type_string =
+ init_type (TYPE_CODE_STRING, TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+ 0,
+ "string", (struct objfile *) NULL);
+}
diff --git a/gnu/usr.bin/gdb/gdb/gdbtypes.h b/gnu/usr.bin/gdb/gdb/gdbtypes.h
new file mode 100644
index 0000000..89cad6c
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/gdbtypes.h
@@ -0,0 +1,704 @@
+/* Internal type definitions for GDB.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+ Contributed by Cygnus Support, using pieces from other GDB modules.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (GDBTYPES_H)
+#define GDBTYPES_H 1
+
+/* When gdb creates fundamental types, it uses one of the following
+ type identifiers. The identifiers are used to index a vector of
+ pointers to any types that are created. */
+
+#define FT_VOID 0
+#define FT_BOOLEAN 1
+#define FT_CHAR 2
+#define FT_SIGNED_CHAR 3
+#define FT_UNSIGNED_CHAR 4
+#define FT_SHORT 5
+#define FT_SIGNED_SHORT 6
+#define FT_UNSIGNED_SHORT 7
+#define FT_INTEGER 8
+#define FT_SIGNED_INTEGER 9
+#define FT_UNSIGNED_INTEGER 10
+#define FT_LONG 11
+#define FT_SIGNED_LONG 12
+#define FT_UNSIGNED_LONG 13
+#define FT_LONG_LONG 14
+#define FT_SIGNED_LONG_LONG 15
+#define FT_UNSIGNED_LONG_LONG 16
+#define FT_FLOAT 17
+#define FT_DBL_PREC_FLOAT 18
+#define FT_EXT_PREC_FLOAT 19
+#define FT_COMPLEX 20
+#define FT_DBL_PREC_COMPLEX 21
+#define FT_EXT_PREC_COMPLEX 22
+#define FT_STRING 23
+#define FT_FIXED_DECIMAL 24
+#define FT_FLOAT_DECIMAL 25
+#define FT_BYTE 26
+#define FT_UNSIGNED_BYTE 27
+
+#define FT_NUM_MEMBERS 28 /* Highest FT_* above, plus one. */
+
+/* Some macros for char-based bitfields. */
+
+#define B_SET(a,x) ((a)[(x)>>3] |= (1 << ((x)&7)))
+#define B_CLR(a,x) ((a)[(x)>>3] &= ~(1 << ((x)&7)))
+#define B_TST(a,x) ((a)[(x)>>3] & (1 << ((x)&7)))
+#define B_TYPE unsigned char
+#define B_BYTES(x) ( 1 + ((x)>>3) )
+#define B_CLRALL(a,x) memset ((a), 0, B_BYTES(x))
+
+/* Different kinds of data types are distinguished by the `code' field. */
+
+enum type_code
+{
+ TYPE_CODE_UNDEF, /* Not used; catches errors */
+ TYPE_CODE_PTR, /* Pointer type */
+ TYPE_CODE_ARRAY, /* Array type with lower & upper bounds. */
+ TYPE_CODE_STRUCT, /* C struct or Pascal record */
+ TYPE_CODE_UNION, /* C union or Pascal variant part */
+ TYPE_CODE_ENUM, /* Enumeration type */
+ TYPE_CODE_FUNC, /* Function type */
+ TYPE_CODE_INT, /* Integer type */
+
+ /* Floating type. This is *NOT* a complex type. Complex types, when
+ we have them, will have their own type code (or TYPE_CODE_ERROR if
+ we can parse a complex type but not manipulate it). There are parts
+ of GDB which bogusly assume that TYPE_CODE_FLT can mean complex. */
+ TYPE_CODE_FLT,
+
+ /* Void type (values zero length; the length field is ignored). */
+ TYPE_CODE_VOID,
+
+ TYPE_CODE_SET, /* Pascal sets */
+ TYPE_CODE_RANGE, /* Range (integers within spec'd bounds) */
+ TYPE_CODE_STRING, /* String types, distinct from array of char */
+ TYPE_CODE_BITSTRING, /* String of bits, distinct from bool array */
+
+ /* Unknown type. The length field is valid if we were able to
+ deduce that much about the type, or 0 if we don't even know that. */
+ TYPE_CODE_ERROR,
+
+ /* C++ */
+ TYPE_CODE_MEMBER, /* Member type */
+ TYPE_CODE_METHOD, /* Method type */
+ TYPE_CODE_REF, /* C++ Reference types */
+
+ /* Modula-2 */
+ TYPE_CODE_CHAR, /* *real* character type */
+ TYPE_CODE_BOOL /* BOOLEAN type */
+};
+
+/* For now allow source to use TYPE_CODE_CLASS for C++ classes, as an
+ alias for TYPE_CODE_STRUCT. Eventually these should probably be
+ officially distinct types within gdb. */
+
+#define TYPE_CODE_CLASS TYPE_CODE_STRUCT
+
+/* Some bits for the type's flags word. */
+
+/* Explicitly unsigned integer type */
+
+#define TYPE_FLAG_UNSIGNED (1 << 0)
+
+/* Explicitly signed integer type */
+
+#define TYPE_FLAG_SIGNED (1 << 1)
+
+/* This appears in a type's flags word if it is a stub type (e.g., if
+ someone referenced a type that wasn't defined in a source file
+ via (struct sir_not_appearing_in_this_film *)). */
+
+#define TYPE_FLAG_STUB (1 << 2)
+
+
+struct type
+{
+
+ /* Code for kind of type */
+
+ enum type_code code;
+
+ /* Name of this type, or NULL if none.
+
+ This is used for printing only, except by poorly designed C++ code.
+ For looking up a name, look for a symbol in the VAR_NAMESPACE. */
+
+ char *name;
+
+ /* Tag name for this type, or NULL if none. This means that the
+ name of the type consists of a keyword followed by the tag name.
+ Which keyword is determined by the type code ("struct" for
+ TYPE_CODE_STRUCT, etc.). As far as I know C/C++ are the only languages
+ with this feature.
+
+ This is used for printing only, except by poorly designed C++ code.
+ For looking up a name, look for a symbol in the STRUCT_NAMESPACE.
+ One more legitimate use is that if TYPE_FLAG_STUB is set, this is
+ the name to use to look for definitions in other files. */
+
+ char *tag_name;
+
+ /* Length, in units of TARGET_CHAR_BIT bits,
+ of storage for a value of this type */
+
+ unsigned length;
+
+ /* Every type is now associated with a particular objfile, and the
+ type is allocated on the type_obstack for that objfile. One problem
+ however, is that there are times when gdb allocates new types while
+ it is not in the process of reading symbols from a particular objfile.
+ Fortunately, these happen when the type being created is a derived
+ type of an existing type, such as in lookup_pointer_type(). So
+ we can just allocate the new type using the same objfile as the
+ existing type, but to do this we need a backpointer to the objfile
+ from the existing type. Yes this is somewhat ugly, but without
+ major overhaul of the internal type system, it can't be avoided
+ for now. */
+
+ struct objfile *objfile;
+
+ /* For a pointer type, describes the type of object pointed to.
+ For an array type, describes the type of the elements.
+ For a function or method type, describes the type of the return value.
+ For a range type, describes the type of the full range.
+ Unused otherwise. */
+
+ struct type *target_type;
+
+ /* Type that is a pointer to this type.
+ NULL if no such pointer-to type is known yet.
+ The debugger may add the address of such a type
+ if it has to construct one later. */
+
+ struct type *pointer_type;
+
+ /* C++: also need a reference type. */
+
+ struct type *reference_type;
+
+ /* Type that is a function returning this type.
+ NULL if no such function type is known here.
+ The debugger may add the address of such a type
+ if it has to construct one later. */
+
+ struct type *function_type;
+
+ /* Flags about this type. */
+
+ short flags;
+
+ /* Number of fields described for this type */
+
+ short nfields;
+
+ /* For structure and union types, a description of each field.
+ For set and pascal array types, there is one "field",
+ whose type is the domain type of the set or array.
+ For range types, there are two "fields",
+ the minimum and maximum values (both inclusive).
+ For enum types, each possible value is described by one "field".
+ For C++ classes, there is one field for each base class (if it is
+ a derived class) plus one field for each class data member. Member
+ functions are recorded elsewhere.
+
+ Using a pointer to a separate array of fields
+ allows all types to have the same size, which is useful
+ because we can allocate the space for a type before
+ we know what to put in it. */
+
+ struct field
+ {
+
+ /* Position of this field, counting in bits from start of
+ containing structure. For a function type, this is the
+ position in the argument list of this argument.
+ For a range bound or enum value, this is the value itself.
+ (FIXME: What about ranges larger than host int size?)
+ For BITS_BIG_ENDIAN=1 targets, it is the bit offset to the MSB.
+ For BITS_BIG_ENDIAN=0 targets, it is the bit offset to the LSB. */
+
+ int bitpos;
+
+ /* Size of this field, in bits, or zero if not packed.
+ For an unpacked field, the field's type's length
+ says how many bytes the field occupies. */
+ /* FIXME: This is abused by TYPE_FIELD_STATIC_PHYSNAME to contain
+ a pointer, so it has to be long. */
+
+ long bitsize;
+
+ /* In a struct or enum type, type of this field.
+ In a function type, type of this argument.
+ In an array type, the domain-type of the array. */
+
+ struct type *type;
+
+ /* Name of field, value or argument.
+ NULL for range bounds and array domains. */
+
+ char *name;
+
+ } *fields;
+
+ /* For types with virtual functions, VPTR_BASETYPE is the base class which
+ defined the virtual function table pointer.
+
+ For types that are pointer to member types, VPTR_BASETYPE
+ is the type that this pointer is a member of.
+
+ Unused otherwise. */
+
+ struct type *vptr_basetype;
+
+ /* Field number of the virtual function table pointer in
+ VPTR_BASETYPE. If -1, we were unable to find the virtual
+ function table pointer in initial symbol reading, and
+ fill_in_vptr_fieldno should be called to find it if possible.
+
+ Unused if this type does not have virtual functions. */
+
+ int vptr_fieldno;
+
+ /* Slot to point to additional language-specific fields of this type. */
+
+ union type_specific
+ {
+
+ /* ARG_TYPES is for TYPE_CODE_METHOD and TYPE_CODE_FUNC. */
+
+ struct type **arg_types;
+
+ /* CPLUS_STUFF is for TYPE_CODE_STRUCT. It is initialized to point to
+ cplus_struct_default, a default static instance of a struct
+ cplus_struct_type. */
+
+ struct cplus_struct_type *cplus_stuff;
+
+ } type_specific;
+};
+
+#define NULL_TYPE ((struct type *) 0)
+
+/* C++ language-specific information for TYPE_CODE_STRUCT and TYPE_CODE_UNION
+ nodes. */
+
+struct cplus_struct_type
+{
+ /* Number of base classes this type derives from. The baseclasses are
+ stored in the first N_BASECLASSES fields (i.e. the `fields' field of
+ the struct type). I think only the `type' field of such a field has
+ any meaning. */
+
+ short n_baseclasses;
+
+ /* Number of methods with unique names. All overloaded methods with
+ the same name count only once. */
+
+ short nfn_fields;
+
+ /* Number of methods described for this type plus all the
+ methods that it derives from. */
+
+ int nfn_fields_total;
+
+ /* For derived classes, the number of base classes is given by n_baseclasses
+ and virtual_field_bits is a bit vector containing one bit per base class.
+ If the base class is virtual, the corresponding bit will be set.
+ I.E, given:
+
+ class A{};
+ class B{};
+ class C : public B, public virtual A {};
+
+ B is a baseclass of C; A is a virtual baseclass for C.
+ This is a C++ 2.0 language feature. */
+
+ B_TYPE *virtual_field_bits;
+
+ /* For classes with private fields, the number of fields is given by
+ nfields and private_field_bits is a bit vector containing one bit
+ per field.
+ If the field is private, the corresponding bit will be set. */
+
+ B_TYPE *private_field_bits;
+
+ /* For classes with protected fields, the number of fields is given by
+ nfields and protected_field_bits is a bit vector containing one bit
+ per field.
+ If the field is private, the corresponding bit will be set. */
+
+ B_TYPE *protected_field_bits;
+
+ /* For classes, structures, and unions, a description of each field,
+ which consists of an overloaded name, followed by the types of
+ arguments that the method expects, and then the name after it
+ has been renamed to make it distinct.
+
+ fn_fieldlists points to an array of nfn_fields of these. */
+
+ struct fn_fieldlist
+ {
+
+ /* The overloaded name. */
+
+ char *name;
+
+ /* The number of methods with this name. */
+
+ int length;
+
+ /* The list of methods. */
+
+ struct fn_field
+ {
+
+ /* If is_stub is clear, this is the mangled name which we can
+ look up to find the address of the method (FIXME: it would
+ be cleaner to have a pointer to the struct symbol here
+ instead). */
+
+ /* If is_stub is set, this is the portion of the mangled
+ name which specifies the arguments. For example, "ii",
+ if there are two int arguments, or "" if there are no
+ arguments. See gdb_mangle_name for the conversion from this
+ format to the one used if is_stub is clear. */
+
+ char *physname;
+
+ /* The return value of the method */
+
+ struct type *type;
+
+ /* The argument list. Only valid if is_stub is clear. Contains
+ the type of each argument, including `this', and ending with
+ a NULL pointer after the last argument. */
+
+ struct type **args;
+
+ /* For virtual functions.
+ First baseclass that defines this virtual function. */
+
+ struct type *fcontext;
+
+ /* Attributes. */
+
+ unsigned int is_const : 1;
+ unsigned int is_volatile : 1;
+ unsigned int is_private : 1;
+ unsigned int is_protected : 1;
+
+ /* A stub method only has some fields valid (but they are enough
+ to reconstruct the rest of the fields). */
+ unsigned int is_stub : 1;
+
+ /* Unused. */
+ unsigned int dummy : 3;
+
+ /* Index into that baseclass's virtual function table,
+ minus 2; else if static: VOFFSET_STATIC; else: 0. */
+
+ unsigned int voffset : 24;
+
+# define VOFFSET_STATIC 1
+
+ } *fn_fields;
+
+ } *fn_fieldlists;
+
+};
+
+/* The default value of TYPE_CPLUS_SPECIFIC(T) points to the
+ this shared static structure. */
+
+extern const struct cplus_struct_type cplus_struct_default;
+
+extern void
+allocate_cplus_struct_type PARAMS ((struct type *));
+
+#define INIT_CPLUS_SPECIFIC(type) \
+ (TYPE_CPLUS_SPECIFIC(type)=(struct cplus_struct_type*)&cplus_struct_default)
+#define ALLOCATE_CPLUS_STRUCT_TYPE(type) allocate_cplus_struct_type (type)
+#define HAVE_CPLUS_STRUCT(type) \
+ (TYPE_CPLUS_SPECIFIC(type) != &cplus_struct_default)
+
+#define TYPE_NAME(thistype) (thistype)->name
+#define TYPE_TAG_NAME(type) ((type)->tag_name)
+#define TYPE_TARGET_TYPE(thistype) (thistype)->target_type
+#define TYPE_POINTER_TYPE(thistype) (thistype)->pointer_type
+#define TYPE_REFERENCE_TYPE(thistype) (thistype)->reference_type
+#define TYPE_FUNCTION_TYPE(thistype) (thistype)->function_type
+#define TYPE_LENGTH(thistype) (thistype)->length
+#define TYPE_OBJFILE(thistype) (thistype)->objfile
+#define TYPE_FLAGS(thistype) (thistype)->flags
+#define TYPE_UNSIGNED(thistype) ((thistype)->flags & TYPE_FLAG_UNSIGNED)
+#define TYPE_CODE(thistype) (thistype)->code
+#define TYPE_NFIELDS(thistype) (thistype)->nfields
+#define TYPE_FIELDS(thistype) (thistype)->fields
+
+/* C++ */
+
+#define TYPE_VPTR_BASETYPE(thistype) (thistype)->vptr_basetype
+#define TYPE_DOMAIN_TYPE(thistype) (thistype)->vptr_basetype
+#define TYPE_VPTR_FIELDNO(thistype) (thistype)->vptr_fieldno
+#define TYPE_FN_FIELDS(thistype) TYPE_CPLUS_SPECIFIC(thistype)->fn_fields
+#define TYPE_NFN_FIELDS(thistype) TYPE_CPLUS_SPECIFIC(thistype)->nfn_fields
+#define TYPE_NFN_FIELDS_TOTAL(thistype) TYPE_CPLUS_SPECIFIC(thistype)->nfn_fields_total
+#define TYPE_TYPE_SPECIFIC(thistype) (thistype)->type_specific
+#define TYPE_ARG_TYPES(thistype) (thistype)->type_specific.arg_types
+#define TYPE_CPLUS_SPECIFIC(thistype) (thistype)->type_specific.cplus_stuff
+#define TYPE_BASECLASS(thistype,index) (thistype)->fields[index].type
+#define TYPE_N_BASECLASSES(thistype) TYPE_CPLUS_SPECIFIC(thistype)->n_baseclasses
+#define TYPE_BASECLASS_NAME(thistype,index) (thistype)->fields[index].name
+#define TYPE_BASECLASS_BITPOS(thistype,index) (thistype)->fields[index].bitpos
+#define BASETYPE_VIA_PUBLIC(thistype, index) (!TYPE_FIELD_PRIVATE(thistype, index))
+#define BASETYPE_VIA_VIRTUAL(thistype, index) \
+ B_TST(TYPE_CPLUS_SPECIFIC(thistype)->virtual_field_bits, (index))
+
+#define TYPE_FIELD(thistype, n) (thistype)->fields[n]
+#define TYPE_FIELD_TYPE(thistype, n) (thistype)->fields[n].type
+#define TYPE_FIELD_NAME(thistype, n) (thistype)->fields[n].name
+#define TYPE_FIELD_VALUE(thistype, n) (* (int*) &(thistype)->fields[n].type)
+#define TYPE_FIELD_BITPOS(thistype, n) (thistype)->fields[n].bitpos
+#define TYPE_FIELD_BITSIZE(thistype, n) (thistype)->fields[n].bitsize
+#define TYPE_FIELD_PACKED(thistype, n) (thistype)->fields[n].bitsize
+
+#define TYPE_FIELD_PRIVATE_BITS(thistype) \
+ TYPE_CPLUS_SPECIFIC(thistype)->private_field_bits
+#define TYPE_FIELD_PROTECTED_BITS(thistype) \
+ TYPE_CPLUS_SPECIFIC(thistype)->protected_field_bits
+#define TYPE_FIELD_VIRTUAL_BITS(thistype) \
+ TYPE_CPLUS_SPECIFIC(thistype)->virtual_field_bits
+#define SET_TYPE_FIELD_PRIVATE(thistype, n) \
+ B_SET (TYPE_CPLUS_SPECIFIC(thistype)->private_field_bits, (n))
+#define SET_TYPE_FIELD_PROTECTED(thistype, n) \
+ B_SET (TYPE_CPLUS_SPECIFIC(thistype)->protected_field_bits, (n))
+#define SET_TYPE_FIELD_VIRTUAL(thistype, n) \
+ B_SET (TYPE_CPLUS_SPECIFIC(thistype)->virtual_field_bits, (n))
+#define TYPE_FIELD_PRIVATE(thistype, n) \
+ (TYPE_CPLUS_SPECIFIC(thistype)->private_field_bits == NULL ? 0 \
+ : B_TST(TYPE_CPLUS_SPECIFIC(thistype)->private_field_bits, (n)))
+#define TYPE_FIELD_PROTECTED(thistype, n) \
+ (TYPE_CPLUS_SPECIFIC(thistype)->protected_field_bits == NULL ? 0 \
+ : B_TST(TYPE_CPLUS_SPECIFIC(thistype)->protected_field_bits, (n)))
+#define TYPE_FIELD_VIRTUAL(thistype, n) \
+ B_TST(TYPE_CPLUS_SPECIFIC(thistype)->virtual_field_bits, (n))
+
+#define TYPE_FIELD_STATIC(thistype, n) ((thistype)->fields[n].bitpos == -1)
+#define TYPE_FIELD_STATIC_PHYSNAME(thistype, n) ((char *)(thistype)->fields[n].bitsize)
+
+#define TYPE_FN_FIELDLISTS(thistype) TYPE_CPLUS_SPECIFIC(thistype)->fn_fieldlists
+#define TYPE_FN_FIELDLIST(thistype, n) TYPE_CPLUS_SPECIFIC(thistype)->fn_fieldlists[n]
+#define TYPE_FN_FIELDLIST1(thistype, n) TYPE_CPLUS_SPECIFIC(thistype)->fn_fieldlists[n].fn_fields
+#define TYPE_FN_FIELDLIST_NAME(thistype, n) TYPE_CPLUS_SPECIFIC(thistype)->fn_fieldlists[n].name
+#define TYPE_FN_FIELDLIST_LENGTH(thistype, n) TYPE_CPLUS_SPECIFIC(thistype)->fn_fieldlists[n].length
+
+#define TYPE_FN_FIELD(thisfn, n) (thisfn)[n]
+#define TYPE_FN_FIELD_PHYSNAME(thisfn, n) (thisfn)[n].physname
+#define TYPE_FN_FIELD_TYPE(thisfn, n) (thisfn)[n].type
+#define TYPE_FN_FIELD_ARGS(thisfn, n) TYPE_ARG_TYPES ((thisfn)[n].type)
+#define TYPE_FN_FIELD_CONST(thisfn, n) ((thisfn)[n].is_const)
+#define TYPE_FN_FIELD_VOLATILE(thisfn, n) ((thisfn)[n].is_volatile)
+#define TYPE_FN_FIELD_PRIVATE(thisfn, n) ((thisfn)[n].is_private)
+#define TYPE_FN_FIELD_PROTECTED(thisfn, n) ((thisfn)[n].is_protected)
+#define TYPE_FN_FIELD_STUB(thisfn, n) ((thisfn)[n].is_stub)
+#define TYPE_FN_FIELD_FCONTEXT(thisfn, n) ((thisfn)[n].fcontext)
+#define TYPE_FN_FIELD_VOFFSET(thisfn, n) ((thisfn)[n].voffset-2)
+#define TYPE_FN_FIELD_VIRTUAL_P(thisfn, n) ((thisfn)[n].voffset > 1)
+#define TYPE_FN_FIELD_STATIC_P(thisfn, n) ((thisfn)[n].voffset == VOFFSET_STATIC)
+
+extern struct type *builtin_type_void;
+extern struct type *builtin_type_char;
+extern struct type *builtin_type_short;
+extern struct type *builtin_type_int;
+extern struct type *builtin_type_long;
+extern struct type *builtin_type_signed_char;
+extern struct type *builtin_type_unsigned_char;
+extern struct type *builtin_type_unsigned_short;
+extern struct type *builtin_type_unsigned_int;
+extern struct type *builtin_type_unsigned_long;
+extern struct type *builtin_type_float;
+extern struct type *builtin_type_double;
+extern struct type *builtin_type_long_double;
+extern struct type *builtin_type_complex;
+extern struct type *builtin_type_double_complex;
+extern struct type *builtin_type_string;
+
+/* This type represents a type that was unrecognized in symbol
+ read-in. */
+
+extern struct type *builtin_type_error;
+
+extern struct type *builtin_type_long_long;
+extern struct type *builtin_type_unsigned_long_long;
+
+/* Modula-2 types */
+
+extern struct type *builtin_type_m2_char;
+extern struct type *builtin_type_m2_int;
+extern struct type *builtin_type_m2_card;
+extern struct type *builtin_type_m2_real;
+extern struct type *builtin_type_m2_bool;
+
+/* Chill types */
+
+extern struct type *builtin_type_chill_bool;
+extern struct type *builtin_type_chill_char;
+extern struct type *builtin_type_chill_long;
+extern struct type *builtin_type_chill_ulong;
+extern struct type *builtin_type_chill_real;
+
+/* CC_HAS_LONG_LONG is defined if the host has "long long". */
+
+#ifdef CC_HAS_LONG_LONG
+
+#define BUILTIN_TYPE_LONGEST builtin_type_long_long
+#define BUILTIN_TYPE_UNSIGNED_LONGEST builtin_type_unsigned_long_long
+
+#else /* not CC_HAS_LONG_LONG. */
+
+#define BUILTIN_TYPE_LONGEST builtin_type_long
+#define BUILTIN_TYPE_UNSIGNED_LONGEST builtin_type_unsigned_long
+
+#endif /* not CC_HAS_LONG_LONG. */
+
+/* Maximum and minimum values of built-in types */
+
+#define MAX_OF_TYPE(t) \
+ TYPE_UNSIGNED(t) ? UMAX_OF_SIZE(TYPE_LENGTH(t)) \
+ : MAX_OF_SIZE(TYPE_LENGTH(t))
+
+#define MIN_OF_TYPE(t) \
+ TYPE_UNSIGNED(t) ? UMIN_OF_SIZE(TYPE_LENGTH(t)) \
+ : MIN_OF_SIZE(TYPE_LENGTH(t))
+
+/* Allocate space for storing data associated with a particular type.
+ We ensure that the space is allocated using the same mechanism that
+ was used to allocate the space for the type structure itself. I.E.
+ if the type is on an objfile's type_obstack, then the space for data
+ associated with that type will also be allocated on the type_obstack.
+ If the type is not associated with any particular objfile (such as
+ builtin types), then the data space will be allocated with xmalloc,
+ the same as for the type structure. */
+
+#define TYPE_ALLOC(t,size) \
+ (TYPE_OBJFILE (t) != NULL \
+ ? obstack_alloc (&TYPE_OBJFILE (t) -> type_obstack, size) \
+ : xmalloc (size))
+
+extern struct type *
+alloc_type PARAMS ((struct objfile *));
+
+extern struct type *
+init_type PARAMS ((enum type_code, int, int, char *, struct objfile *));
+
+extern struct type *
+lookup_reference_type PARAMS ((struct type *));
+
+extern struct type *
+make_reference_type PARAMS ((struct type *, struct type **));
+
+extern struct type *
+lookup_member_type PARAMS ((struct type *, struct type *));
+
+extern void
+smash_to_method_type PARAMS ((struct type *, struct type *, struct type *,
+ struct type **));
+
+extern void
+smash_to_member_type PARAMS ((struct type *, struct type *, struct type *));
+
+extern struct type *
+allocate_stub_method PARAMS ((struct type *));
+
+extern char *
+type_name_no_tag PARAMS ((const struct type *));
+
+extern struct type *
+lookup_struct_elt_type PARAMS ((struct type *, char *, int));
+
+extern struct type *
+make_pointer_type PARAMS ((struct type *, struct type **));
+
+extern struct type *
+lookup_pointer_type PARAMS ((struct type *));
+
+extern struct type *
+make_function_type PARAMS ((struct type *, struct type **));
+
+extern struct type *
+lookup_function_type PARAMS ((struct type *));
+
+extern struct type *
+create_range_type PARAMS ((struct type *, struct type *, int, int));
+
+extern struct type *
+create_array_type PARAMS ((struct type *, struct type *, struct type *));
+
+extern struct type *
+create_string_type PARAMS ((struct type *, struct type *));
+
+extern struct type *
+lookup_unsigned_typename PARAMS ((char *));
+
+extern struct type *
+lookup_signed_typename PARAMS ((char *));
+
+extern void
+check_stub_type PARAMS ((struct type *));
+
+extern void
+check_stub_method PARAMS ((struct type *, int, int));
+
+extern struct type *
+lookup_primitive_typename PARAMS ((char *));
+
+extern char *
+gdb_mangle_name PARAMS ((struct type *, int, int));
+
+extern struct type *
+builtin_type PARAMS ((char **));
+
+extern struct type *
+lookup_typename PARAMS ((char *, struct block *, int));
+
+extern struct type *
+lookup_template_type PARAMS ((char *, struct type *, struct block *));
+
+extern struct type *
+lookup_fundamental_type PARAMS ((struct objfile *, int));
+
+extern void
+fill_in_vptr_fieldno PARAMS ((struct type *));
+
+#if MAINTENANCE_CMDS
+extern void recursive_dump_type PARAMS ((struct type *, int));
+#endif
+
+/* printcmd.c */
+
+extern void
+print_scalar_formatted PARAMS ((char *, struct type *, int, int, FILE *));
+
+#if MAINTENANCE_CMDS
+extern void maintenance_print_type PARAMS ((char *, int));
+#endif
+
+#endif /* GDBTYPES_H */
diff --git a/gnu/usr.bin/gdb/gdb/getopt.h b/gnu/usr.bin/gdb/gdb/getopt.h
new file mode 100644
index 0000000..1b546b2
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/getopt.h
@@ -0,0 +1,129 @@
+/* Declarations for getopt.
+ Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License
+ as published by the Free Software Foundation; either version 2, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized. */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+#if __STDC__
+ const char *name;
+#else
+ char *name;
+#endif
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+#if __STDC__
+#if defined(__GNU_LIBRARY__)
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* not __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind);
+
+/* Internal only. Users should not call this directly. */
+extern int _getopt_internal (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* not __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/gnu/usr.bin/gdb/gdb/i386-dis.c b/gnu/usr.bin/gdb/gdb/i386-dis.c
new file mode 100644
index 0000000..3e4bbb2
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/i386-dis.c
@@ -0,0 +1,1952 @@
+/* Print i386 instructions for GDB, the GNU debugger.
+ Copyright (C) 1988, 1989, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * 80386 instruction printer by Pace Willisson (pace@prep.ai.mit.edu)
+ * July 1988
+ * modified by John Hassey (hassey@dg-rtp.dg.com)
+ */
+
+/*
+ * The main tables describing the instructions is essentially a copy
+ * of the "Opcode Map" chapter (Appendix A) of the Intel 80386
+ * Programmers Manual. Usually, there is a capital letter, followed
+ * by a small letter. The capital letter tell the addressing mode,
+ * and the small letter tells about the operand size. Refer to
+ * the Intel manual for details.
+ */
+
+#include "dis-asm.h"
+
+#define MAXLEN 20
+
+#include <setjmp.h>
+
+struct private
+{
+ /* Points to first byte not fetched. */
+ bfd_byte *max_fetched;
+ bfd_byte the_buffer[MAXLEN];
+ bfd_vma insn_start;
+ jmp_buf bailout;
+};
+
+/* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive)
+ to ADDR (exclusive) are valid. Returns 1 for success, longjmps
+ on error. */
+#define FETCH_DATA(info, addr) \
+ ((addr) <= ((struct private *)(info->private_data))->max_fetched \
+ ? 1 : fetch_data ((info), (addr)))
+
+static int
+fetch_data (info, addr)
+ struct disassemble_info *info;
+ bfd_byte *addr;
+{
+ int status;
+ struct private *priv = (struct private *)info->private_data;
+ bfd_vma start = priv->insn_start + (priv->max_fetched - priv->the_buffer);
+
+ status = (*info->read_memory_func) (start,
+ priv->max_fetched,
+ addr - priv->max_fetched,
+ info);
+ if (status != 0)
+ {
+ (*info->memory_error_func) (status, start, info);
+ longjmp (priv->bailout, 1);
+ }
+ else
+ priv->max_fetched = addr;
+ return 1;
+}
+
+#define Eb OP_E, b_mode
+#define indirEb OP_indirE, b_mode
+#define Gb OP_G, b_mode
+#define Ev OP_E, v_mode
+#define indirEv OP_indirE, v_mode
+#define Ew OP_E, w_mode
+#define Ma OP_E, v_mode
+#define M OP_E, 0
+#define Mp OP_E, 0 /* ? */
+#define Gv OP_G, v_mode
+#define Gw OP_G, w_mode
+#define Rw OP_rm, w_mode
+#define Rd OP_rm, d_mode
+#define Ib OP_I, b_mode
+#define sIb OP_sI, b_mode /* sign extened byte */
+#define Iv OP_I, v_mode
+#define Iw OP_I, w_mode
+#define Jb OP_J, b_mode
+#define Jv OP_J, v_mode
+#define ONE OP_ONE, 0
+#define Cd OP_C, d_mode
+#define Dd OP_D, d_mode
+#define Td OP_T, d_mode
+
+#define eAX OP_REG, eAX_reg
+#define eBX OP_REG, eBX_reg
+#define eCX OP_REG, eCX_reg
+#define eDX OP_REG, eDX_reg
+#define eSP OP_REG, eSP_reg
+#define eBP OP_REG, eBP_reg
+#define eSI OP_REG, eSI_reg
+#define eDI OP_REG, eDI_reg
+#define AL OP_REG, al_reg
+#define CL OP_REG, cl_reg
+#define DL OP_REG, dl_reg
+#define BL OP_REG, bl_reg
+#define AH OP_REG, ah_reg
+#define CH OP_REG, ch_reg
+#define DH OP_REG, dh_reg
+#define BH OP_REG, bh_reg
+#define AX OP_REG, ax_reg
+#define DX OP_REG, dx_reg
+#define indirDX OP_REG, indir_dx_reg
+
+#define Sw OP_SEG, w_mode
+#define Ap OP_DIR, lptr
+#define Av OP_DIR, v_mode
+#define Ob OP_OFF, b_mode
+#define Ov OP_OFF, v_mode
+#define Xb OP_DSSI, b_mode
+#define Xv OP_DSSI, v_mode
+#define Yb OP_ESDI, b_mode
+#define Yv OP_ESDI, v_mode
+
+#define es OP_REG, es_reg
+#define ss OP_REG, ss_reg
+#define cs OP_REG, cs_reg
+#define ds OP_REG, ds_reg
+#define fs OP_REG, fs_reg
+#define gs OP_REG, gs_reg
+
+int OP_E(), OP_indirE(), OP_G(), OP_I(), OP_sI(), OP_REG();
+int OP_J(), OP_SEG();
+int OP_DIR(), OP_OFF(), OP_DSSI(), OP_ESDI(), OP_ONE(), OP_C();
+int OP_D(), OP_T(), OP_rm();
+
+static void dofloat (), putop (), append_prefix (), set_op ();
+static int get16 (), get32 ();
+
+#define b_mode 1
+#define v_mode 2
+#define w_mode 3
+#define d_mode 4
+
+#define es_reg 100
+#define cs_reg 101
+#define ss_reg 102
+#define ds_reg 103
+#define fs_reg 104
+#define gs_reg 105
+#define eAX_reg 107
+#define eCX_reg 108
+#define eDX_reg 109
+#define eBX_reg 110
+#define eSP_reg 111
+#define eBP_reg 112
+#define eSI_reg 113
+#define eDI_reg 114
+
+#define lptr 115
+
+#define al_reg 116
+#define cl_reg 117
+#define dl_reg 118
+#define bl_reg 119
+#define ah_reg 120
+#define ch_reg 121
+#define dh_reg 122
+#define bh_reg 123
+
+#define ax_reg 124
+#define cx_reg 125
+#define dx_reg 126
+#define bx_reg 127
+#define sp_reg 128
+#define bp_reg 129
+#define si_reg 130
+#define di_reg 131
+
+#define indir_dx_reg 150
+
+#define GRP1b NULL, NULL, 0
+#define GRP1S NULL, NULL, 1
+#define GRP1Ss NULL, NULL, 2
+#define GRP2b NULL, NULL, 3
+#define GRP2S NULL, NULL, 4
+#define GRP2b_one NULL, NULL, 5
+#define GRP2S_one NULL, NULL, 6
+#define GRP2b_cl NULL, NULL, 7
+#define GRP2S_cl NULL, NULL, 8
+#define GRP3b NULL, NULL, 9
+#define GRP3S NULL, NULL, 10
+#define GRP4 NULL, NULL, 11
+#define GRP5 NULL, NULL, 12
+#define GRP6 NULL, NULL, 13
+#define GRP7 NULL, NULL, 14
+#define GRP8 NULL, NULL, 15
+
+#define FLOATCODE 50
+#define FLOAT NULL, NULL, FLOATCODE
+
+struct dis386 {
+ char *name;
+ int (*op1)();
+ int bytemode1;
+ int (*op2)();
+ int bytemode2;
+ int (*op3)();
+ int bytemode3;
+};
+
+struct dis386 dis386[] = {
+ /* 00 */
+ { "addb", Eb, Gb },
+ { "addS", Ev, Gv },
+ { "addb", Gb, Eb },
+ { "addS", Gv, Ev },
+ { "addb", AL, Ib },
+ { "addS", eAX, Iv },
+ { "pushl", es },
+ { "popl", es },
+ /* 08 */
+ { "orb", Eb, Gb },
+ { "orS", Ev, Gv },
+ { "orb", Gb, Eb },
+ { "orS", Gv, Ev },
+ { "orb", AL, Ib },
+ { "orS", eAX, Iv },
+ { "pushl", cs },
+ { "(bad)" }, /* 0x0f extended opcode escape */
+ /* 10 */
+ { "adcb", Eb, Gb },
+ { "adcS", Ev, Gv },
+ { "adcb", Gb, Eb },
+ { "adcS", Gv, Ev },
+ { "adcb", AL, Ib },
+ { "adcS", eAX, Iv },
+ { "pushl", ss },
+ { "popl", ss },
+ /* 18 */
+ { "sbbb", Eb, Gb },
+ { "sbbS", Ev, Gv },
+ { "sbbb", Gb, Eb },
+ { "sbbS", Gv, Ev },
+ { "sbbb", AL, Ib },
+ { "sbbS", eAX, Iv },
+ { "pushl", ds },
+ { "popl", ds },
+ /* 20 */
+ { "andb", Eb, Gb },
+ { "andS", Ev, Gv },
+ { "andb", Gb, Eb },
+ { "andS", Gv, Ev },
+ { "andb", AL, Ib },
+ { "andS", eAX, Iv },
+ { "(bad)" }, /* SEG ES prefix */
+ { "daa" },
+ /* 28 */
+ { "subb", Eb, Gb },
+ { "subS", Ev, Gv },
+ { "subb", Gb, Eb },
+ { "subS", Gv, Ev },
+ { "subb", AL, Ib },
+ { "subS", eAX, Iv },
+ { "(bad)" }, /* SEG CS prefix */
+ { "das" },
+ /* 30 */
+ { "xorb", Eb, Gb },
+ { "xorS", Ev, Gv },
+ { "xorb", Gb, Eb },
+ { "xorS", Gv, Ev },
+ { "xorb", AL, Ib },
+ { "xorS", eAX, Iv },
+ { "(bad)" }, /* SEG SS prefix */
+ { "aaa" },
+ /* 38 */
+ { "cmpb", Eb, Gb },
+ { "cmpS", Ev, Gv },
+ { "cmpb", Gb, Eb },
+ { "cmpS", Gv, Ev },
+ { "cmpb", AL, Ib },
+ { "cmpS", eAX, Iv },
+ { "(bad)" }, /* SEG DS prefix */
+ { "aas" },
+ /* 40 */
+ { "incS", eAX },
+ { "incS", eCX },
+ { "incS", eDX },
+ { "incS", eBX },
+ { "incS", eSP },
+ { "incS", eBP },
+ { "incS", eSI },
+ { "incS", eDI },
+ /* 48 */
+ { "decS", eAX },
+ { "decS", eCX },
+ { "decS", eDX },
+ { "decS", eBX },
+ { "decS", eSP },
+ { "decS", eBP },
+ { "decS", eSI },
+ { "decS", eDI },
+ /* 50 */
+ { "pushS", eAX },
+ { "pushS", eCX },
+ { "pushS", eDX },
+ { "pushS", eBX },
+ { "pushS", eSP },
+ { "pushS", eBP },
+ { "pushS", eSI },
+ { "pushS", eDI },
+ /* 58 */
+ { "popS", eAX },
+ { "popS", eCX },
+ { "popS", eDX },
+ { "popS", eBX },
+ { "popS", eSP },
+ { "popS", eBP },
+ { "popS", eSI },
+ { "popS", eDI },
+ /* 60 */
+ { "pusha" },
+ { "popa" },
+ { "boundS", Gv, Ma },
+ { "arpl", Ew, Gw },
+ { "(bad)" }, /* seg fs */
+ { "(bad)" }, /* seg gs */
+ { "(bad)" }, /* op size prefix */
+ { "(bad)" }, /* adr size prefix */
+ /* 68 */
+ { "pushS", Iv }, /* 386 book wrong */
+ { "imulS", Gv, Ev, Iv },
+ { "pushl", sIb }, /* push of byte really pushes 4 bytes */
+ { "imulS", Gv, Ev, Ib },
+ { "insb", Yb, indirDX },
+ { "insS", Yv, indirDX },
+ { "outsb", indirDX, Xb },
+ { "outsS", indirDX, Xv },
+ /* 70 */
+ { "jo", Jb },
+ { "jno", Jb },
+ { "jb", Jb },
+ { "jae", Jb },
+ { "je", Jb },
+ { "jne", Jb },
+ { "jbe", Jb },
+ { "ja", Jb },
+ /* 78 */
+ { "js", Jb },
+ { "jns", Jb },
+ { "jp", Jb },
+ { "jnp", Jb },
+ { "jl", Jb },
+ { "jnl", Jb },
+ { "jle", Jb },
+ { "jg", Jb },
+ /* 80 */
+ { GRP1b },
+ { GRP1S },
+ { "(bad)" },
+ { GRP1Ss },
+ { "testb", Eb, Gb },
+ { "testS", Ev, Gv },
+ { "xchgb", Eb, Gb },
+ { "xchgS", Ev, Gv },
+ /* 88 */
+ { "movb", Eb, Gb },
+ { "movS", Ev, Gv },
+ { "movb", Gb, Eb },
+ { "movS", Gv, Ev },
+ { "movw", Ew, Sw },
+ { "leaS", Gv, M },
+ { "movw", Sw, Ew },
+ { "popS", Ev },
+ /* 90 */
+ { "nop" },
+ { "xchgS", eCX, eAX },
+ { "xchgS", eDX, eAX },
+ { "xchgS", eBX, eAX },
+ { "xchgS", eSP, eAX },
+ { "xchgS", eBP, eAX },
+ { "xchgS", eSI, eAX },
+ { "xchgS", eDI, eAX },
+ /* 98 */
+ { "cwtl" },
+ { "cltd" },
+ { "lcall", Ap },
+ { "(bad)" }, /* fwait */
+ { "pushf" },
+ { "popf" },
+ { "sahf" },
+ { "lahf" },
+ /* a0 */
+ { "movb", AL, Ob },
+ { "movS", eAX, Ov },
+ { "movb", Ob, AL },
+ { "movS", Ov, eAX },
+ { "movsb", Yb, Xb },
+ { "movsS", Yv, Xv },
+ { "cmpsb", Yb, Xb },
+ { "cmpsS", Yv, Xv },
+ /* a8 */
+ { "testb", AL, Ib },
+ { "testS", eAX, Iv },
+ { "stosb", Yb, AL },
+ { "stosS", Yv, eAX },
+ { "lodsb", AL, Xb },
+ { "lodsS", eAX, Xv },
+ { "scasb", AL, Xb },
+ { "scasS", eAX, Xv },
+ /* b0 */
+ { "movb", AL, Ib },
+ { "movb", CL, Ib },
+ { "movb", DL, Ib },
+ { "movb", BL, Ib },
+ { "movb", AH, Ib },
+ { "movb", CH, Ib },
+ { "movb", DH, Ib },
+ { "movb", BH, Ib },
+ /* b8 */
+ { "movS", eAX, Iv },
+ { "movS", eCX, Iv },
+ { "movS", eDX, Iv },
+ { "movS", eBX, Iv },
+ { "movS", eSP, Iv },
+ { "movS", eBP, Iv },
+ { "movS", eSI, Iv },
+ { "movS", eDI, Iv },
+ /* c0 */
+ { GRP2b },
+ { GRP2S },
+ { "ret", Iw },
+ { "ret" },
+ { "lesS", Gv, Mp },
+ { "ldsS", Gv, Mp },
+ { "movb", Eb, Ib },
+ { "movS", Ev, Iv },
+ /* c8 */
+ { "enter", Iw, Ib },
+ { "leave" },
+ { "lret", Iw },
+ { "lret" },
+ { "int3" },
+ { "int", Ib },
+ { "into" },
+ { "iret" },
+ /* d0 */
+ { GRP2b_one },
+ { GRP2S_one },
+ { GRP2b_cl },
+ { GRP2S_cl },
+ { "aam", Ib },
+ { "aad", Ib },
+ { "(bad)" },
+ { "xlat" },
+ /* d8 */
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ /* e0 */
+ { "loopne", Jb },
+ { "loope", Jb },
+ { "loop", Jb },
+ { "jCcxz", Jb },
+ { "inb", AL, Ib },
+ { "inS", eAX, Ib },
+ { "outb", Ib, AL },
+ { "outS", Ib, eAX },
+ /* e8 */
+ { "call", Av },
+ { "jmp", Jv },
+ { "ljmp", Ap },
+ { "jmp", Jb },
+ { "inb", AL, indirDX },
+ { "inS", eAX, indirDX },
+ { "outb", indirDX, AL },
+ { "outS", indirDX, eAX },
+ /* f0 */
+ { "(bad)" }, /* lock prefix */
+ { "(bad)" },
+ { "(bad)" }, /* repne */
+ { "(bad)" }, /* repz */
+ { "hlt" },
+ { "cmc" },
+ { GRP3b },
+ { GRP3S },
+ /* f8 */
+ { "clc" },
+ { "stc" },
+ { "cli" },
+ { "sti" },
+ { "cld" },
+ { "std" },
+ { GRP4 },
+ { GRP5 },
+};
+
+struct dis386 dis386_twobyte[] = {
+ /* 00 */
+ { GRP6 },
+ { GRP7 },
+ { "larS", Gv, Ew },
+ { "lslS", Gv, Ew },
+ { "(bad)" },
+ { "(bad)" },
+ { "clts" },
+ { "(bad)" },
+ /* 08 */
+ { "invd" },
+ { "wbinvd" },
+ { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 10 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 18 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 20 */
+ /* these are all backward in appendix A of the intel book */
+ { "movl", Rd, Cd },
+ { "movl", Rd, Dd },
+ { "movl", Cd, Rd },
+ { "movl", Dd, Rd },
+ { "movl", Rd, Td },
+ { "(bad)" },
+ { "movl", Td, Rd },
+ { "(bad)" },
+ /* 28 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 30 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 38 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 40 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 48 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 50 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 58 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 60 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 68 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 70 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 78 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* 80 */
+ { "jo", Jv },
+ { "jno", Jv },
+ { "jb", Jv },
+ { "jae", Jv },
+ { "je", Jv },
+ { "jne", Jv },
+ { "jbe", Jv },
+ { "ja", Jv },
+ /* 88 */
+ { "js", Jv },
+ { "jns", Jv },
+ { "jp", Jv },
+ { "jnp", Jv },
+ { "jl", Jv },
+ { "jge", Jv },
+ { "jle", Jv },
+ { "jg", Jv },
+ /* 90 */
+ { "seto", Eb },
+ { "setno", Eb },
+ { "setb", Eb },
+ { "setae", Eb },
+ { "sete", Eb },
+ { "setne", Eb },
+ { "setbe", Eb },
+ { "seta", Eb },
+ /* 98 */
+ { "sets", Eb },
+ { "setns", Eb },
+ { "setp", Eb },
+ { "setnp", Eb },
+ { "setl", Eb },
+ { "setge", Eb },
+ { "setle", Eb },
+ { "setg", Eb },
+ /* a0 */
+ { "pushl", fs },
+ { "popl", fs },
+ { "(bad)" },
+ { "btS", Ev, Gv },
+ { "shldS", Ev, Gv, Ib },
+ { "shldS", Ev, Gv, CL },
+ { "(bad)" },
+ { "(bad)" },
+ /* a8 */
+ { "pushl", gs },
+ { "popl", gs },
+ { "(bad)" },
+ { "btsS", Ev, Gv },
+ { "shrdS", Ev, Gv, Ib },
+ { "shrdS", Ev, Gv, CL },
+ { "(bad)" },
+ { "imulS", Gv, Ev },
+ /* b0 */
+ { "cmpxchgb", Eb, Gb },
+ { "cmpxchgS", Ev, Gv },
+ { "lssS", Gv, Mp }, /* 386 lists only Mp */
+ { "btrS", Ev, Gv },
+ { "lfsS", Gv, Mp }, /* 386 lists only Mp */
+ { "lgsS", Gv, Mp }, /* 386 lists only Mp */
+ { "movzbS", Gv, Eb },
+ { "movzwS", Gv, Ew },
+ /* b8 */
+ { "(bad)" },
+ { "(bad)" },
+ { GRP8 },
+ { "btcS", Ev, Gv },
+ { "bsfS", Gv, Ev },
+ { "bsrS", Gv, Ev },
+ { "movsbS", Gv, Eb },
+ { "movswS", Gv, Ew },
+ /* c0 */
+ { "xaddb", Eb, Gb },
+ { "xaddS", Ev, Gv },
+ { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* c8 */
+ { "bswap", eAX },
+ { "bswap", eCX },
+ { "bswap", eDX },
+ { "bswap", eBX },
+ { "bswap", eSP },
+ { "bswap", eBP },
+ { "bswap", eSI },
+ { "bswap", eDI },
+ /* d0 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* d8 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* e0 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* e8 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* f0 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ /* f8 */
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+ { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
+};
+
+static char obuf[100];
+static char *obufp;
+static char scratchbuf[100];
+static unsigned char *start_codep;
+static unsigned char *codep;
+static disassemble_info *the_info;
+static int mod;
+static int rm;
+static int reg;
+static void oappend ();
+
+static char *names32[]={
+ "%eax","%ecx","%edx","%ebx", "%esp","%ebp","%esi","%edi",
+};
+static char *names16[] = {
+ "%ax","%cx","%dx","%bx","%sp","%bp","%si","%di",
+};
+static char *names8[] = {
+ "%al","%cl","%dl","%bl","%ah","%ch","%dh","%bh",
+};
+static char *names_seg[] = {
+ "%es","%cs","%ss","%ds","%fs","%gs","%?","%?",
+};
+
+struct dis386 grps[][8] = {
+ /* GRP1b */
+ {
+ { "addb", Eb, Ib },
+ { "orb", Eb, Ib },
+ { "adcb", Eb, Ib },
+ { "sbbb", Eb, Ib },
+ { "andb", Eb, Ib },
+ { "subb", Eb, Ib },
+ { "xorb", Eb, Ib },
+ { "cmpb", Eb, Ib }
+ },
+ /* GRP1S */
+ {
+ { "addS", Ev, Iv },
+ { "orS", Ev, Iv },
+ { "adcS", Ev, Iv },
+ { "sbbS", Ev, Iv },
+ { "andS", Ev, Iv },
+ { "subS", Ev, Iv },
+ { "xorS", Ev, Iv },
+ { "cmpS", Ev, Iv }
+ },
+ /* GRP1Ss */
+ {
+ { "addS", Ev, sIb },
+ { "orS", Ev, sIb },
+ { "adcS", Ev, sIb },
+ { "sbbS", Ev, sIb },
+ { "andS", Ev, sIb },
+ { "subS", Ev, sIb },
+ { "xorS", Ev, sIb },
+ { "cmpS", Ev, sIb }
+ },
+ /* GRP2b */
+ {
+ { "rolb", Eb, Ib },
+ { "rorb", Eb, Ib },
+ { "rclb", Eb, Ib },
+ { "rcrb", Eb, Ib },
+ { "shlb", Eb, Ib },
+ { "shrb", Eb, Ib },
+ { "(bad)" },
+ { "sarb", Eb, Ib },
+ },
+ /* GRP2S */
+ {
+ { "rolS", Ev, Ib },
+ { "rorS", Ev, Ib },
+ { "rclS", Ev, Ib },
+ { "rcrS", Ev, Ib },
+ { "shlS", Ev, Ib },
+ { "shrS", Ev, Ib },
+ { "(bad)" },
+ { "sarS", Ev, Ib },
+ },
+ /* GRP2b_one */
+ {
+ { "rolb", Eb },
+ { "rorb", Eb },
+ { "rclb", Eb },
+ { "rcrb", Eb },
+ { "shlb", Eb },
+ { "shrb", Eb },
+ { "(bad)" },
+ { "sarb", Eb },
+ },
+ /* GRP2S_one */
+ {
+ { "rolS", Ev },
+ { "rorS", Ev },
+ { "rclS", Ev },
+ { "rcrS", Ev },
+ { "shlS", Ev },
+ { "shrS", Ev },
+ { "(bad)" },
+ { "sarS", Ev },
+ },
+ /* GRP2b_cl */
+ {
+ { "rolb", Eb, CL },
+ { "rorb", Eb, CL },
+ { "rclb", Eb, CL },
+ { "rcrb", Eb, CL },
+ { "shlb", Eb, CL },
+ { "shrb", Eb, CL },
+ { "(bad)" },
+ { "sarb", Eb, CL },
+ },
+ /* GRP2S_cl */
+ {
+ { "rolS", Ev, CL },
+ { "rorS", Ev, CL },
+ { "rclS", Ev, CL },
+ { "rcrS", Ev, CL },
+ { "shlS", Ev, CL },
+ { "shrS", Ev, CL },
+ { "(bad)" },
+ { "sarS", Ev, CL }
+ },
+ /* GRP3b */
+ {
+ { "testb", Eb, Ib },
+ { "(bad)", Eb },
+ { "notb", Eb },
+ { "negb", Eb },
+ { "mulb", AL, Eb },
+ { "imulb", AL, Eb },
+ { "divb", AL, Eb },
+ { "idivb", AL, Eb }
+ },
+ /* GRP3S */
+ {
+ { "testS", Ev, Iv },
+ { "(bad)" },
+ { "notS", Ev },
+ { "negS", Ev },
+ { "mulS", eAX, Ev },
+ { "imulS", eAX, Ev },
+ { "divS", eAX, Ev },
+ { "idivS", eAX, Ev },
+ },
+ /* GRP4 */
+ {
+ { "incb", Eb },
+ { "decb", Eb },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ },
+ /* GRP5 */
+ {
+ { "incS", Ev },
+ { "decS", Ev },
+ { "call", indirEv },
+ { "lcall", indirEv },
+ { "jmp", indirEv },
+ { "ljmp", indirEv },
+ { "pushS", Ev },
+ { "(bad)" },
+ },
+ /* GRP6 */
+ {
+ { "sldt", Ew },
+ { "str", Ew },
+ { "lldt", Ew },
+ { "ltr", Ew },
+ { "verr", Ew },
+ { "verw", Ew },
+ { "(bad)" },
+ { "(bad)" }
+ },
+ /* GRP7 */
+ {
+ { "sgdt", Ew },
+ { "sidt", Ew },
+ { "lgdt", Ew },
+ { "lidt", Ew },
+ { "smsw", Ew },
+ { "(bad)" },
+ { "lmsw", Ew },
+ { "invlpg", Ew },
+ },
+ /* GRP8 */
+ {
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { "btS", Ev, Ib },
+ { "btsS", Ev, Ib },
+ { "btrS", Ev, Ib },
+ { "btcS", Ev, Ib },
+ }
+};
+
+#define PREFIX_REPZ 1
+#define PREFIX_REPNZ 2
+#define PREFIX_LOCK 4
+#define PREFIX_CS 8
+#define PREFIX_SS 0x10
+#define PREFIX_DS 0x20
+#define PREFIX_ES 0x40
+#define PREFIX_FS 0x80
+#define PREFIX_GS 0x100
+#define PREFIX_DATA 0x200
+#define PREFIX_ADR 0x400
+#define PREFIX_FWAIT 0x800
+
+static int prefixes;
+
+static void
+ckprefix ()
+{
+ prefixes = 0;
+ while (1)
+ {
+ FETCH_DATA (the_info, codep + 1);
+ switch (*codep)
+ {
+ case 0xf3:
+ prefixes |= PREFIX_REPZ;
+ break;
+ case 0xf2:
+ prefixes |= PREFIX_REPNZ;
+ break;
+ case 0xf0:
+ prefixes |= PREFIX_LOCK;
+ break;
+ case 0x2e:
+ prefixes |= PREFIX_CS;
+ break;
+ case 0x36:
+ prefixes |= PREFIX_SS;
+ break;
+ case 0x3e:
+ prefixes |= PREFIX_DS;
+ break;
+ case 0x26:
+ prefixes |= PREFIX_ES;
+ break;
+ case 0x64:
+ prefixes |= PREFIX_FS;
+ break;
+ case 0x65:
+ prefixes |= PREFIX_GS;
+ break;
+ case 0x66:
+ prefixes |= PREFIX_DATA;
+ break;
+ case 0x67:
+ prefixes |= PREFIX_ADR;
+ break;
+ case 0x9b:
+ prefixes |= PREFIX_FWAIT;
+ break;
+ default:
+ return;
+ }
+ codep++;
+ }
+}
+
+static int dflag;
+static int aflag;
+
+static char op1out[100], op2out[100], op3out[100];
+static int op_address[3], op_ad, op_index[3];
+static int start_pc;
+
+
+/*
+ * On the 386's of 1988, the maximum length of an instruction is 15 bytes.
+ * (see topic "Redundant prefixes" in the "Differences from 8086"
+ * section of the "Virtual 8086 Mode" chapter.)
+ * 'pc' should be the address of this instruction, it will
+ * be used to print the target address if this is a relative jump or call
+ * The function returns the length of this instruction in bytes.
+ */
+
+int
+print_insn_i386 (pc, info)
+ bfd_vma pc;
+ disassemble_info *info;
+{
+ struct dis386 *dp;
+ int i;
+ int enter_instruction;
+ char *first, *second, *third;
+ int needcomma;
+
+ struct private priv;
+ bfd_byte *inbuf = priv.the_buffer;
+
+ info->private_data = (PTR) &priv;
+ priv.max_fetched = priv.the_buffer;
+ priv.insn_start = pc;
+ if (setjmp (priv.bailout) != 0)
+ /* Error return. */
+ return -1;
+
+ obuf[0] = 0;
+ op1out[0] = 0;
+ op2out[0] = 0;
+ op3out[0] = 0;
+
+ op_index[0] = op_index[1] = op_index[2] = -1;
+
+ the_info = info;
+ start_pc = pc;
+ start_codep = inbuf;
+ codep = inbuf;
+
+ ckprefix ();
+
+ FETCH_DATA (info, codep + 1);
+ if (*codep == 0xc8)
+ enter_instruction = 1;
+ else
+ enter_instruction = 0;
+
+ obufp = obuf;
+
+ if (prefixes & PREFIX_REPZ)
+ oappend ("repz ");
+ if (prefixes & PREFIX_REPNZ)
+ oappend ("repnz ");
+ if (prefixes & PREFIX_LOCK)
+ oappend ("lock ");
+
+ if ((prefixes & PREFIX_FWAIT)
+ && ((*codep < 0xd8) || (*codep > 0xdf)))
+ {
+ /* fwait not followed by floating point instruction */
+ (*info->fprintf_func) (info->stream, "fwait");
+ return (1);
+ }
+
+ /* these would be initialized to 0 if disassembling for 8086 or 286 */
+ dflag = 1;
+ aflag = 1;
+
+ if (prefixes & PREFIX_DATA)
+ dflag ^= 1;
+
+ if (prefixes & PREFIX_ADR)
+ {
+ aflag ^= 1;
+ oappend ("addr16 ");
+ }
+
+ if (*codep == 0x0f)
+ {
+ FETCH_DATA (info, codep + 2);
+ dp = &dis386_twobyte[*++codep];
+ }
+ else
+ dp = &dis386[*codep];
+ codep++;
+ FETCH_DATA (info, codep + 1);
+ mod = (*codep >> 6) & 3;
+ reg = (*codep >> 3) & 7;
+ rm = *codep & 7;
+
+ if (dp->name == NULL && dp->bytemode1 == FLOATCODE)
+ {
+ dofloat ();
+ }
+ else
+ {
+ if (dp->name == NULL)
+ dp = &grps[dp->bytemode1][reg];
+
+ putop (dp->name);
+
+ obufp = op1out;
+ op_ad = 2;
+ if (dp->op1)
+ (*dp->op1)(dp->bytemode1);
+
+ obufp = op2out;
+ op_ad = 1;
+ if (dp->op2)
+ (*dp->op2)(dp->bytemode2);
+
+ obufp = op3out;
+ op_ad = 0;
+ if (dp->op3)
+ (*dp->op3)(dp->bytemode3);
+ }
+
+ obufp = obuf + strlen (obuf);
+ for (i = strlen (obuf); i < 6; i++)
+ oappend (" ");
+ oappend (" ");
+ (*info->fprintf_func) (info->stream, "%s", obuf);
+
+ /* enter instruction is printed with operands in the
+ * same order as the intel book; everything else
+ * is printed in reverse order
+ */
+ if (enter_instruction)
+ {
+ first = op1out;
+ second = op2out;
+ third = op3out;
+ op_ad = op_index[0];
+ op_index[0] = op_index[2];
+ op_index[2] = op_ad;
+ }
+ else
+ {
+ first = op3out;
+ second = op2out;
+ third = op1out;
+ }
+ needcomma = 0;
+ if (*first)
+ {
+ if (op_index[0] != -1)
+ (*info->print_address_func) (op_address[op_index[0]], info);
+ else
+ (*info->fprintf_func) (info->stream, "%s", first);
+ needcomma = 1;
+ }
+ if (*second)
+ {
+ if (needcomma)
+ (*info->fprintf_func) (info->stream, ",");
+ if (op_index[1] != -1)
+ (*info->print_address_func) (op_address[op_index[1]], info);
+ else
+ (*info->fprintf_func) (info->stream, "%s", second);
+ needcomma = 1;
+ }
+ if (*third)
+ {
+ if (needcomma)
+ (*info->fprintf_func) (info->stream, ",");
+ if (op_index[2] != -1)
+ (*info->print_address_func) (op_address[op_index[2]], info);
+ else
+ (*info->fprintf_func) (info->stream, "%s", third);
+ }
+ return (codep - inbuf);
+}
+
+char *float_mem[] = {
+ /* d8 */
+ "fadds",
+ "fmuls",
+ "fcoms",
+ "fcomps",
+ "fsubs",
+ "fsubrs",
+ "fdivs",
+ "fdivrs",
+ /* d9 */
+ "flds",
+ "(bad)",
+ "fsts",
+ "fstps",
+ "fldenv",
+ "fldcw",
+ "fNstenv",
+ "fNstcw",
+ /* da */
+ "fiaddl",
+ "fimull",
+ "ficoml",
+ "ficompl",
+ "fisubl",
+ "fisubrl",
+ "fidivl",
+ "fidivrl",
+ /* db */
+ "fildl",
+ "(bad)",
+ "fistl",
+ "fistpl",
+ "(bad)",
+ "fldt",
+ "(bad)",
+ "fstpt",
+ /* dc */
+ "faddl",
+ "fmull",
+ "fcoml",
+ "fcompl",
+ "fsubl",
+ "fsubrl",
+ "fdivl",
+ "fdivrl",
+ /* dd */
+ "fldl",
+ "(bad)",
+ "fstl",
+ "fstpl",
+ "frstor",
+ "(bad)",
+ "fNsave",
+ "fNstsw",
+ /* de */
+ "fiadd",
+ "fimul",
+ "ficom",
+ "ficomp",
+ "fisub",
+ "fisubr",
+ "fidiv",
+ "fidivr",
+ /* df */
+ "fild",
+ "(bad)",
+ "fist",
+ "fistp",
+ "fbld",
+ "fildll",
+ "fbstp",
+ "fistpll",
+};
+
+#define ST OP_ST, 0
+#define STi OP_STi, 0
+int OP_ST(), OP_STi();
+
+#define FGRPd9_2 NULL, NULL, 0
+#define FGRPd9_4 NULL, NULL, 1
+#define FGRPd9_5 NULL, NULL, 2
+#define FGRPd9_6 NULL, NULL, 3
+#define FGRPd9_7 NULL, NULL, 4
+#define FGRPda_5 NULL, NULL, 5
+#define FGRPdb_4 NULL, NULL, 6
+#define FGRPde_3 NULL, NULL, 7
+#define FGRPdf_4 NULL, NULL, 8
+
+struct dis386 float_reg[][8] = {
+ /* d8 */
+ {
+ { "fadd", ST, STi },
+ { "fmul", ST, STi },
+ { "fcom", STi },
+ { "fcomp", STi },
+ { "fsub", ST, STi },
+ { "fsubr", ST, STi },
+ { "fdiv", ST, STi },
+ { "fdivr", ST, STi },
+ },
+ /* d9 */
+ {
+ { "fld", STi },
+ { "fxch", STi },
+ { FGRPd9_2 },
+ { "(bad)" },
+ { FGRPd9_4 },
+ { FGRPd9_5 },
+ { FGRPd9_6 },
+ { FGRPd9_7 },
+ },
+ /* da */
+ {
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { FGRPda_5 },
+ { "(bad)" },
+ { "(bad)" },
+ },
+ /* db */
+ {
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { FGRPdb_4 },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ },
+ /* dc */
+ {
+ { "fadd", STi, ST },
+ { "fmul", STi, ST },
+ { "(bad)" },
+ { "(bad)" },
+ { "fsub", STi, ST },
+ { "fsubr", STi, ST },
+ { "fdiv", STi, ST },
+ { "fdivr", STi, ST },
+ },
+ /* dd */
+ {
+ { "ffree", STi },
+ { "(bad)" },
+ { "fst", STi },
+ { "fstp", STi },
+ { "fucom", STi },
+ { "fucomp", STi },
+ { "(bad)" },
+ { "(bad)" },
+ },
+ /* de */
+ {
+ { "faddp", STi, ST },
+ { "fmulp", STi, ST },
+ { "(bad)" },
+ { FGRPde_3 },
+ { "fsubp", STi, ST },
+ { "fsubrp", STi, ST },
+ { "fdivp", STi, ST },
+ { "fdivrp", STi, ST },
+ },
+ /* df */
+ {
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ { FGRPdf_4 },
+ { "(bad)" },
+ { "(bad)" },
+ { "(bad)" },
+ },
+};
+
+
+char *fgrps[][8] = {
+ /* d9_2 0 */
+ {
+ "fnop","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+ },
+
+ /* d9_4 1 */
+ {
+ "fchs","fabs","(bad)","(bad)","ftst","fxam","(bad)","(bad)",
+ },
+
+ /* d9_5 2 */
+ {
+ "fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz","(bad)",
+ },
+
+ /* d9_6 3 */
+ {
+ "f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp",
+ },
+
+ /* d9_7 4 */
+ {
+ "fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos",
+ },
+
+ /* da_5 5 */
+ {
+ "(bad)","fucompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+ },
+
+ /* db_4 6 */
+ {
+ "feni(287 only)","fdisi(287 only)","fNclex","fNinit",
+ "fNsetpm(287 only)","(bad)","(bad)","(bad)",
+ },
+
+ /* de_3 7 */
+ {
+ "(bad)","fcompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+ },
+
+ /* df_4 8 */
+ {
+ "fNstsw","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+ },
+};
+
+static void
+dofloat ()
+{
+ struct dis386 *dp;
+ unsigned char floatop;
+
+ floatop = codep[-1];
+
+ if (mod != 3)
+ {
+ putop (float_mem[(floatop - 0xd8) * 8 + reg]);
+ obufp = op1out;
+ OP_E (v_mode);
+ return;
+ }
+ codep++;
+
+ dp = &float_reg[floatop - 0xd8][reg];
+ if (dp->name == NULL)
+ {
+ putop (fgrps[dp->bytemode1][rm]);
+ /* instruction fnstsw is only one with strange arg */
+ if (floatop == 0xdf
+ && FETCH_DATA (the_info, codep + 1)
+ && *codep == 0xe0)
+ strcpy (op1out, "%eax");
+ }
+ else
+ {
+ putop (dp->name);
+ obufp = op1out;
+ if (dp->op1)
+ (*dp->op1)(dp->bytemode1);
+ obufp = op2out;
+ if (dp->op2)
+ (*dp->op2)(dp->bytemode2);
+ }
+}
+
+/* ARGSUSED */
+int
+OP_ST (ignore)
+ int ignore;
+{
+ oappend ("%st");
+ return (0);
+}
+
+/* ARGSUSED */
+int
+OP_STi (ignore)
+ int ignore;
+{
+ sprintf (scratchbuf, "%%st(%d)", rm);
+ oappend (scratchbuf);
+ return (0);
+}
+
+
+/* capital letters in template are macros */
+static void
+putop (template)
+ char *template;
+{
+ char *p;
+
+ for (p = template; *p; p++)
+ {
+ switch (*p)
+ {
+ default:
+ *obufp++ = *p;
+ break;
+ case 'C': /* For jcxz/jecxz */
+ if (aflag == 0)
+ *obufp++ = 'e';
+ break;
+ case 'N':
+ if ((prefixes & PREFIX_FWAIT) == 0)
+ *obufp++ = 'n';
+ break;
+ case 'S':
+ /* operand size flag */
+ if (dflag)
+ *obufp++ = 'l';
+ else
+ *obufp++ = 'w';
+ break;
+ }
+ }
+ *obufp = 0;
+}
+
+static void
+oappend (s)
+ char *s;
+{
+ strcpy (obufp, s);
+ obufp += strlen (s);
+ *obufp = 0;
+}
+
+static void
+append_prefix ()
+{
+ if (prefixes & PREFIX_CS)
+ oappend ("%cs:");
+ if (prefixes & PREFIX_DS)
+ oappend ("%ds:");
+ if (prefixes & PREFIX_SS)
+ oappend ("%ss:");
+ if (prefixes & PREFIX_ES)
+ oappend ("%es:");
+ if (prefixes & PREFIX_FS)
+ oappend ("%fs:");
+ if (prefixes & PREFIX_GS)
+ oappend ("%gs:");
+}
+
+int
+OP_indirE (bytemode)
+ int bytemode;
+{
+ oappend ("*");
+ OP_E (bytemode);
+ return (0);
+}
+
+int
+OP_E (bytemode)
+ int bytemode;
+{
+ int disp;
+ int havesib;
+ int base;
+ int index;
+ int scale;
+ int havebase;
+
+ /* skip mod/rm byte */
+ codep++;
+
+ havesib = 0;
+ havebase = 0;
+ disp = 0;
+
+ if (mod == 3)
+ {
+ switch (bytemode)
+ {
+ case b_mode:
+ oappend (names8[rm]);
+ break;
+ case w_mode:
+ oappend (names16[rm]);
+ break;
+ case v_mode:
+ if (dflag)
+ oappend (names32[rm]);
+ else
+ oappend (names16[rm]);
+ break;
+ default:
+ oappend ("<bad dis table>");
+ break;
+ }
+ return (0);
+ }
+
+ append_prefix ();
+ if (rm == 4)
+ {
+ havesib = 1;
+ havebase = 1;
+ FETCH_DATA (the_info, codep + 1);
+ scale = (*codep >> 6) & 3;
+ index = (*codep >> 3) & 7;
+ base = *codep & 7;
+ codep++;
+ }
+
+ switch (mod)
+ {
+ case 0:
+ switch (rm)
+ {
+ case 4:
+ /* implies havesib and havebase */
+ if (base == 5) {
+ havebase = 0;
+ disp = get32 ();
+ }
+ break;
+ case 5:
+ disp = get32 ();
+ break;
+ default:
+ havebase = 1;
+ base = rm;
+ break;
+ }
+ break;
+ case 1:
+ FETCH_DATA (the_info, codep + 1);
+ disp = *(char *)codep++;
+ if (rm != 4)
+ {
+ havebase = 1;
+ base = rm;
+ }
+ break;
+ case 2:
+ disp = get32 ();
+ if (rm != 4)
+ {
+ havebase = 1;
+ base = rm;
+ }
+ break;
+ }
+
+ if (mod != 0 || rm == 5 || (havesib && base == 5))
+ {
+ sprintf (scratchbuf, "0x%x", disp);
+ oappend (scratchbuf);
+ }
+
+ if (havebase || havesib)
+ {
+ oappend ("(");
+ if (havebase)
+ oappend (names32[base]);
+ if (havesib)
+ {
+ if (index != 4)
+ {
+ sprintf (scratchbuf, ",%s", names32[index]);
+ oappend (scratchbuf);
+ }
+ sprintf (scratchbuf, ",%d", 1 << scale);
+ oappend (scratchbuf);
+ }
+ oappend (")");
+ }
+ return (0);
+}
+
+int
+OP_G (bytemode)
+ int bytemode;
+{
+ switch (bytemode)
+ {
+ case b_mode:
+ oappend (names8[reg]);
+ break;
+ case w_mode:
+ oappend (names16[reg]);
+ break;
+ case d_mode:
+ oappend (names32[reg]);
+ break;
+ case v_mode:
+ if (dflag)
+ oappend (names32[reg]);
+ else
+ oappend (names16[reg]);
+ break;
+ default:
+ oappend ("<internal disassembler error>");
+ break;
+ }
+ return (0);
+}
+
+static int
+get32 ()
+{
+ int x = 0;
+
+ FETCH_DATA (the_info, codep + 4);
+ x = *codep++ & 0xff;
+ x |= (*codep++ & 0xff) << 8;
+ x |= (*codep++ & 0xff) << 16;
+ x |= (*codep++ & 0xff) << 24;
+ return (x);
+}
+
+static int
+get16 ()
+{
+ int x = 0;
+
+ FETCH_DATA (the_info, codep + 2);
+ x = *codep++ & 0xff;
+ x |= (*codep++ & 0xff) << 8;
+ return (x);
+}
+
+static void
+set_op (op)
+ int op;
+{
+ op_index[op_ad] = op_ad;
+ op_address[op_ad] = op;
+}
+
+int
+OP_REG (code)
+ int code;
+{
+ char *s;
+
+ switch (code)
+ {
+ case indir_dx_reg: s = "(%dx)"; break;
+ case ax_reg: case cx_reg: case dx_reg: case bx_reg:
+ case sp_reg: case bp_reg: case si_reg: case di_reg:
+ s = names16[code - ax_reg];
+ break;
+ case es_reg: case ss_reg: case cs_reg:
+ case ds_reg: case fs_reg: case gs_reg:
+ s = names_seg[code - es_reg];
+ break;
+ case al_reg: case ah_reg: case cl_reg: case ch_reg:
+ case dl_reg: case dh_reg: case bl_reg: case bh_reg:
+ s = names8[code - al_reg];
+ break;
+ case eAX_reg: case eCX_reg: case eDX_reg: case eBX_reg:
+ case eSP_reg: case eBP_reg: case eSI_reg: case eDI_reg:
+ if (dflag)
+ s = names32[code - eAX_reg];
+ else
+ s = names16[code - eAX_reg];
+ break;
+ default:
+ s = "<internal disassembler error>";
+ break;
+ }
+ oappend (s);
+ return (0);
+}
+
+int
+OP_I (bytemode)
+ int bytemode;
+{
+ int op;
+
+ switch (bytemode)
+ {
+ case b_mode:
+ FETCH_DATA (the_info, codep + 1);
+ op = *codep++ & 0xff;
+ break;
+ case v_mode:
+ if (dflag)
+ op = get32 ();
+ else
+ op = get16 ();
+ break;
+ case w_mode:
+ op = get16 ();
+ break;
+ default:
+ oappend ("<internal disassembler error>");
+ return (0);
+ }
+ sprintf (scratchbuf, "$0x%x", op);
+ oappend (scratchbuf);
+ return (0);
+}
+
+int
+OP_sI (bytemode)
+ int bytemode;
+{
+ int op;
+
+ switch (bytemode)
+ {
+ case b_mode:
+ FETCH_DATA (the_info, codep + 1);
+ op = *(char *)codep++;
+ break;
+ case v_mode:
+ if (dflag)
+ op = get32 ();
+ else
+ op = (short)get16();
+ break;
+ case w_mode:
+ op = (short)get16 ();
+ break;
+ default:
+ oappend ("<internal disassembler error>");
+ return (0);
+ }
+ sprintf (scratchbuf, "$0x%x", op);
+ oappend (scratchbuf);
+ return (0);
+}
+
+int
+OP_J (bytemode)
+ int bytemode;
+{
+ int disp;
+ int mask = -1;
+
+ switch (bytemode)
+ {
+ case b_mode:
+ FETCH_DATA (the_info, codep + 1);
+ disp = *(char *)codep++;
+ break;
+ case v_mode:
+ if (dflag)
+ disp = get32 ();
+ else
+ {
+ disp = (short)get16 ();
+ /* for some reason, a data16 prefix on a jump instruction
+ means that the pc is masked to 16 bits after the
+ displacement is added! */
+ mask = 0xffff;
+ }
+ break;
+ default:
+ oappend ("<internal disassembler error>");
+ return (0);
+ }
+ disp = (start_pc + codep - start_codep + disp) & mask;
+ set_op (disp);
+ sprintf (scratchbuf, "0x%x", disp);
+ oappend (scratchbuf);
+ return (0);
+}
+
+/* ARGSUSED */
+int
+OP_SEG (dummy)
+ int dummy;
+{
+ static char *sreg[] = {
+ "%es","%cs","%ss","%ds","%fs","%gs","%?","%?",
+ };
+
+ oappend (sreg[reg]);
+ return (0);
+}
+
+int
+OP_DIR (size)
+ int size;
+{
+ int seg, offset;
+
+ switch (size)
+ {
+ case lptr:
+ if (aflag)
+ {
+ offset = get32 ();
+ seg = get16 ();
+ }
+ else
+ {
+ offset = get16 ();
+ seg = get16 ();
+ }
+ sprintf (scratchbuf, "0x%x,0x%x", seg, offset);
+ oappend (scratchbuf);
+ break;
+ case v_mode:
+ if (aflag)
+ offset = get32 ();
+ else
+ offset = (short)get16 ();
+
+ offset = start_pc + codep - start_codep + offset;
+ set_op (offset);
+ sprintf (scratchbuf, "0x%x", offset);
+ oappend (scratchbuf);
+ break;
+ default:
+ oappend ("<internal disassembler error>");
+ break;
+ }
+ return (0);
+}
+
+/* ARGSUSED */
+int
+OP_OFF (bytemode)
+ int bytemode;
+{
+ int off;
+
+ if (aflag)
+ off = get32 ();
+ else
+ off = get16 ();
+
+ sprintf (scratchbuf, "0x%x", off);
+ oappend (scratchbuf);
+ return (0);
+}
+
+/* ARGSUSED */
+int
+OP_ESDI (dummy)
+ int dummy;
+{
+ oappend ("%es:(");
+ oappend (aflag ? "%edi" : "%di");
+ oappend (")");
+ return (0);
+}
+
+/* ARGSUSED */
+int
+OP_DSSI (dummy)
+ int dummy;
+{
+ oappend ("%ds:(");
+ oappend (aflag ? "%esi" : "%si");
+ oappend (")");
+ return (0);
+}
+
+/* ARGSUSED */
+int
+OP_ONE (dummy)
+ int dummy;
+{
+ oappend ("1");
+ return (0);
+}
+
+/* ARGSUSED */
+int
+OP_C (dummy)
+ int dummy;
+{
+ codep++; /* skip mod/rm */
+ sprintf (scratchbuf, "%%cr%d", reg);
+ oappend (scratchbuf);
+ return (0);
+}
+
+/* ARGSUSED */
+int
+OP_D (dummy)
+ int dummy;
+{
+ codep++; /* skip mod/rm */
+ sprintf (scratchbuf, "%%db%d", reg);
+ oappend (scratchbuf);
+ return (0);
+}
+
+/* ARGSUSED */
+int
+OP_T (dummy)
+ int dummy;
+{
+ codep++; /* skip mod/rm */
+ sprintf (scratchbuf, "%%tr%d", reg);
+ oappend (scratchbuf);
+ return (0);
+}
+
+int
+OP_rm (bytemode)
+ int bytemode;
+{
+ switch (bytemode)
+ {
+ case d_mode:
+ oappend (names32[rm]);
+ break;
+ case w_mode:
+ oappend (names16[rm]);
+ break;
+ }
+ return (0);
+}
diff --git a/gnu/usr.bin/gdb/gdb/i386-pinsn.c b/gnu/usr.bin/gdb/gdb/i386-pinsn.c
new file mode 100644
index 0000000..b6d7fe9
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/i386-pinsn.c
@@ -0,0 +1,37 @@
+/* Print i386 instructions for GDB, the GNU debugger.
+ Copyright 1986, 1987, 1989, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "dis-asm.h"
+
+
+/* Print the instruction at address MEMADDR in debugged memory,
+ on STREAM. Returns length of the instruction, in bytes. */
+
+int
+print_insn (memaddr, stream)
+ CORE_ADDR memaddr;
+ FILE *stream;
+{
+ disassemble_info info;
+
+ GDB_INIT_DISASSEMBLE_INFO(info, stream);
+
+ return print_insn_i386 (memaddr, &info);
+}
diff --git a/gnu/usr.bin/gdb/gdb/i386-tdep.c b/gnu/usr.bin/gdb/gdb/i386-tdep.c
new file mode 100644
index 0000000..3c64d72
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/i386-tdep.c
@@ -0,0 +1,595 @@
+/* Intel 386 target-dependent stuff.
+ Copyright (C) 1988, 1989, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "frame.h"
+#include "inferior.h"
+#include "gdbcore.h"
+#include "target.h"
+
+static long
+i386_get_frame_setup PARAMS ((int));
+
+static void
+i386_follow_jump PARAMS ((void));
+
+static void
+codestream_read PARAMS ((unsigned char *, int));
+
+static void
+codestream_seek PARAMS ((int));
+
+static unsigned char
+codestream_fill PARAMS ((int));
+
+/* helper functions for tm-i386.h */
+
+/* Stdio style buffering was used to minimize calls to ptrace, but this
+ buffering did not take into account that the code section being accessed
+ may not be an even number of buffers long (even if the buffer is only
+ sizeof(int) long). In cases where the code section size happened to
+ be a non-integral number of buffers long, attempting to read the last
+ buffer would fail. Simply using target_read_memory and ignoring errors,
+ rather than read_memory, is not the correct solution, since legitimate
+ access errors would then be totally ignored. To properly handle this
+ situation and continue to use buffering would require that this code
+ be able to determine the minimum code section size granularity (not the
+ alignment of the section itself, since the actual failing case that
+ pointed out this problem had a section alignment of 4 but was not a
+ multiple of 4 bytes long), on a target by target basis, and then
+ adjust it's buffer size accordingly. This is messy, but potentially
+ feasible. It probably needs the bfd library's help and support. For
+ now, the buffer size is set to 1. (FIXME -fnf) */
+
+#define CODESTREAM_BUFSIZ 1 /* Was sizeof(int), see note above. */
+static CORE_ADDR codestream_next_addr;
+static CORE_ADDR codestream_addr;
+static unsigned char codestream_buf[CODESTREAM_BUFSIZ];
+static int codestream_off;
+static int codestream_cnt;
+
+#define codestream_tell() (codestream_addr + codestream_off)
+#define codestream_peek() (codestream_cnt == 0 ? \
+ codestream_fill(1): codestream_buf[codestream_off])
+#define codestream_get() (codestream_cnt-- == 0 ? \
+ codestream_fill(0) : codestream_buf[codestream_off++])
+
+static unsigned char
+codestream_fill (peek_flag)
+ int peek_flag;
+{
+ codestream_addr = codestream_next_addr;
+ codestream_next_addr += CODESTREAM_BUFSIZ;
+ codestream_off = 0;
+ codestream_cnt = CODESTREAM_BUFSIZ;
+ read_memory (codestream_addr, (char *) codestream_buf, CODESTREAM_BUFSIZ);
+
+ if (peek_flag)
+ return (codestream_peek());
+ else
+ return (codestream_get());
+}
+
+static void
+codestream_seek (place)
+ int place;
+{
+ codestream_next_addr = place / CODESTREAM_BUFSIZ;
+ codestream_next_addr *= CODESTREAM_BUFSIZ;
+ codestream_cnt = 0;
+ codestream_fill (1);
+ while (codestream_tell() != place)
+ codestream_get ();
+}
+
+static void
+codestream_read (buf, count)
+ unsigned char *buf;
+ int count;
+{
+ unsigned char *p;
+ int i;
+ p = buf;
+ for (i = 0; i < count; i++)
+ *p++ = codestream_get ();
+}
+
+/* next instruction is a jump, move to target */
+
+static void
+i386_follow_jump ()
+{
+ unsigned char buf[4];
+ long delta;
+
+ int data16;
+ CORE_ADDR pos;
+
+ pos = codestream_tell ();
+
+ data16 = 0;
+ if (codestream_peek () == 0x66)
+ {
+ codestream_get ();
+ data16 = 1;
+ }
+
+ switch (codestream_get ())
+ {
+ case 0xe9:
+ /* relative jump: if data16 == 0, disp32, else disp16 */
+ if (data16)
+ {
+ codestream_read (buf, 2);
+ delta = extract_signed_integer (buf, 2);
+
+ /* include size of jmp inst (including the 0x66 prefix). */
+ pos += delta + 4;
+ }
+ else
+ {
+ codestream_read (buf, 4);
+ delta = extract_signed_integer (buf, 4);
+
+ pos += delta + 5;
+ }
+ break;
+ case 0xeb:
+ /* relative jump, disp8 (ignore data16) */
+ codestream_read (buf, 1);
+ /* Sign-extend it. */
+ delta = extract_signed_integer (buf, 1);
+
+ pos += delta + 2;
+ break;
+ }
+ codestream_seek (pos);
+}
+
+/*
+ * find & return amound a local space allocated, and advance codestream to
+ * first register push (if any)
+ *
+ * if entry sequence doesn't make sense, return -1, and leave
+ * codestream pointer random
+ */
+
+static long
+i386_get_frame_setup (pc)
+ int pc;
+{
+ unsigned char op;
+
+ codestream_seek (pc);
+
+ i386_follow_jump ();
+
+ op = codestream_get ();
+
+ if (op == 0x58) /* popl %eax */
+ {
+ /*
+ * this function must start with
+ *
+ * popl %eax 0x58
+ * xchgl %eax, (%esp) 0x87 0x04 0x24
+ * or xchgl %eax, 0(%esp) 0x87 0x44 0x24 0x00
+ *
+ * (the system 5 compiler puts out the second xchg
+ * inst, and the assembler doesn't try to optimize it,
+ * so the 'sib' form gets generated)
+ *
+ * this sequence is used to get the address of the return
+ * buffer for a function that returns a structure
+ */
+ int pos;
+ unsigned char buf[4];
+ static unsigned char proto1[3] = { 0x87,0x04,0x24 };
+ static unsigned char proto2[4] = { 0x87,0x44,0x24,0x00 };
+ pos = codestream_tell ();
+ codestream_read (buf, 4);
+ if (memcmp (buf, proto1, 3) == 0)
+ pos += 3;
+ else if (memcmp (buf, proto2, 4) == 0)
+ pos += 4;
+
+ codestream_seek (pos);
+ op = codestream_get (); /* update next opcode */
+ }
+
+ if (op == 0x55) /* pushl %ebp */
+ {
+ /* check for movl %esp, %ebp - can be written two ways */
+ switch (codestream_get ())
+ {
+ case 0x8b:
+ if (codestream_get () != 0xec)
+ return (-1);
+ break;
+ case 0x89:
+ if (codestream_get () != 0xe5)
+ return (-1);
+ break;
+ default:
+ return (-1);
+ }
+ /* check for stack adjustment
+ *
+ * subl $XXX, %esp
+ *
+ * note: you can't subtract a 16 bit immediate
+ * from a 32 bit reg, so we don't have to worry
+ * about a data16 prefix
+ */
+ op = codestream_peek ();
+ if (op == 0x83)
+ {
+ /* subl with 8 bit immed */
+ codestream_get ();
+ if (codestream_get () != 0xec)
+ /* Some instruction starting with 0x83 other than subl. */
+ {
+ codestream_seek (codestream_tell () - 2);
+ return 0;
+ }
+ /* subl with signed byte immediate
+ * (though it wouldn't make sense to be negative)
+ */
+ return (codestream_get());
+ }
+ else if (op == 0x81)
+ {
+ char buf[4];
+ /* Maybe it is subl with 32 bit immedediate. */
+ codestream_get();
+ if (codestream_get () != 0xec)
+ /* Some instruction starting with 0x81 other than subl. */
+ {
+ codestream_seek (codestream_tell () - 2);
+ return 0;
+ }
+ /* It is subl with 32 bit immediate. */
+ codestream_read ((unsigned char *)buf, 4);
+ return extract_signed_integer (buf, 4);
+ }
+ else
+ {
+ return (0);
+ }
+ }
+ else if (op == 0xc8)
+ {
+ char buf[2];
+ /* enter instruction: arg is 16 bit unsigned immed */
+ codestream_read ((unsigned char *)buf, 2);
+ codestream_get (); /* flush final byte of enter instruction */
+ return extract_unsigned_integer (buf, 2);
+ }
+ return (-1);
+}
+
+/* Return number of args passed to a frame.
+ Can return -1, meaning no way to tell. */
+
+int
+i386_frame_num_args (fi)
+ struct frame_info *fi;
+{
+#if 1
+ return -1;
+#else
+ /* This loses because not only might the compiler not be popping the
+ args right after the function call, it might be popping args from both
+ this call and a previous one, and we would say there are more args
+ than there really are. */
+
+ int retpc;
+ unsigned char op;
+ struct frame_info *pfi;
+
+ /* on the 386, the instruction following the call could be:
+ popl %ecx - one arg
+ addl $imm, %esp - imm/4 args; imm may be 8 or 32 bits
+ anything else - zero args */
+
+ int frameless;
+
+ FRAMELESS_FUNCTION_INVOCATION (fi, frameless);
+ if (frameless)
+ /* In the absence of a frame pointer, GDB doesn't get correct values
+ for nameless arguments. Return -1, so it doesn't print any
+ nameless arguments. */
+ return -1;
+
+ pfi = get_prev_frame_info (fi);
+ if (pfi == 0)
+ {
+ /* Note: this can happen if we are looking at the frame for
+ main, because FRAME_CHAIN_VALID won't let us go into
+ start. If we have debugging symbols, that's not really
+ a big deal; it just means it will only show as many arguments
+ to main as are declared. */
+ return -1;
+ }
+ else
+ {
+ retpc = pfi->pc;
+ op = read_memory_integer (retpc, 1);
+ if (op == 0x59)
+ /* pop %ecx */
+ return 1;
+ else if (op == 0x83)
+ {
+ op = read_memory_integer (retpc+1, 1);
+ if (op == 0xc4)
+ /* addl $<signed imm 8 bits>, %esp */
+ return (read_memory_integer (retpc+2,1)&0xff)/4;
+ else
+ return 0;
+ }
+ else if (op == 0x81)
+ { /* add with 32 bit immediate */
+ op = read_memory_integer (retpc+1, 1);
+ if (op == 0xc4)
+ /* addl $<imm 32>, %esp */
+ return read_memory_integer (retpc+2, 4) / 4;
+ else
+ return 0;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+#endif
+}
+
+/*
+ * parse the first few instructions of the function to see
+ * what registers were stored.
+ *
+ * We handle these cases:
+ *
+ * The startup sequence can be at the start of the function,
+ * or the function can start with a branch to startup code at the end.
+ *
+ * %ebp can be set up with either the 'enter' instruction, or
+ * 'pushl %ebp, movl %esp, %ebp' (enter is too slow to be useful,
+ * but was once used in the sys5 compiler)
+ *
+ * Local space is allocated just below the saved %ebp by either the
+ * 'enter' instruction, or by 'subl $<size>, %esp'. 'enter' has
+ * a 16 bit unsigned argument for space to allocate, and the
+ * 'addl' instruction could have either a signed byte, or
+ * 32 bit immediate.
+ *
+ * Next, the registers used by this function are pushed. In
+ * the sys5 compiler they will always be in the order: %edi, %esi, %ebx
+ * (and sometimes a harmless bug causes it to also save but not restore %eax);
+ * however, the code below is willing to see the pushes in any order,
+ * and will handle up to 8 of them.
+ *
+ * If the setup sequence is at the end of the function, then the
+ * next instruction will be a branch back to the start.
+ */
+
+void
+i386_frame_find_saved_regs (fip, fsrp)
+ struct frame_info *fip;
+ struct frame_saved_regs *fsrp;
+{
+ long locals;
+ unsigned char op;
+ CORE_ADDR dummy_bottom;
+ CORE_ADDR adr;
+ int i;
+
+ memset (fsrp, 0, sizeof *fsrp);
+
+ /* if frame is the end of a dummy, compute where the
+ * beginning would be
+ */
+ dummy_bottom = fip->frame - 4 - REGISTER_BYTES - CALL_DUMMY_LENGTH;
+
+ /* check if the PC is in the stack, in a dummy frame */
+ if (dummy_bottom <= fip->pc && fip->pc <= fip->frame)
+ {
+ /* all regs were saved by push_call_dummy () */
+ adr = fip->frame;
+ for (i = 0; i < NUM_REGS; i++)
+ {
+ adr -= REGISTER_RAW_SIZE (i);
+ fsrp->regs[i] = adr;
+ }
+ return;
+ }
+
+ locals = i386_get_frame_setup (get_pc_function_start (fip->pc));
+
+ if (locals >= 0)
+ {
+ adr = fip->frame - 4 - locals;
+ for (i = 0; i < 8; i++)
+ {
+ op = codestream_get ();
+ if (op < 0x50 || op > 0x57)
+ break;
+ fsrp->regs[op - 0x50] = adr;
+ adr -= 4;
+ }
+ }
+
+ fsrp->regs[PC_REGNUM] = fip->frame + 4;
+ fsrp->regs[FP_REGNUM] = fip->frame;
+}
+
+/* return pc of first real instruction */
+
+int
+i386_skip_prologue (pc)
+ int pc;
+{
+ unsigned char op;
+ int i;
+
+ if (i386_get_frame_setup (pc) < 0)
+ return (pc);
+
+ /* found valid frame setup - codestream now points to
+ * start of push instructions for saving registers
+ */
+
+ /* skip over register saves */
+ for (i = 0; i < 8; i++)
+ {
+ op = codestream_peek ();
+ /* break if not pushl inst */
+ if (op < 0x50 || op > 0x57)
+ break;
+ codestream_get ();
+ }
+
+ i386_follow_jump ();
+
+ return (codestream_tell ());
+}
+
+void
+i386_push_dummy_frame ()
+{
+ CORE_ADDR sp = read_register (SP_REGNUM);
+ int regnum;
+ char regbuf[MAX_REGISTER_RAW_SIZE];
+
+ sp = push_word (sp, read_register (PC_REGNUM));
+ sp = push_word (sp, read_register (FP_REGNUM));
+ write_register (FP_REGNUM, sp);
+ for (regnum = 0; regnum < NUM_REGS; regnum++)
+ {
+ read_register_gen (regnum, regbuf);
+ sp = push_bytes (sp, regbuf, REGISTER_RAW_SIZE (regnum));
+ }
+ write_register (SP_REGNUM, sp);
+}
+
+void
+i386_pop_frame ()
+{
+ FRAME frame = get_current_frame ();
+ CORE_ADDR fp;
+ int regnum;
+ struct frame_saved_regs fsr;
+ struct frame_info *fi;
+ char regbuf[MAX_REGISTER_RAW_SIZE];
+
+ fi = get_frame_info (frame);
+ fp = fi->frame;
+ get_frame_saved_regs (fi, &fsr);
+ for (regnum = 0; regnum < NUM_REGS; regnum++)
+ {
+ CORE_ADDR adr;
+ adr = fsr.regs[regnum];
+ if (adr)
+ {
+ read_memory (adr, regbuf, REGISTER_RAW_SIZE (regnum));
+ write_register_bytes (REGISTER_BYTE (regnum), regbuf,
+ REGISTER_RAW_SIZE (regnum));
+ }
+ }
+ write_register (FP_REGNUM, read_memory_integer (fp, 4));
+ write_register (PC_REGNUM, read_memory_integer (fp + 4, 4));
+ write_register (SP_REGNUM, fp + 8);
+ flush_cached_frames ();
+ set_current_frame ( create_new_frame (read_register (FP_REGNUM),
+ read_pc ()));
+}
+
+#ifdef GET_LONGJMP_TARGET
+
+/* Figure out where the longjmp will land. Slurp the args out of the stack.
+ We expect the first arg to be a pointer to the jmp_buf structure from which
+ we extract the pc (JB_PC) that we will land at. The pc is copied into PC.
+ This routine returns true on success. */
+
+int
+get_longjmp_target(pc)
+ CORE_ADDR *pc;
+{
+ char buf[TARGET_PTR_BIT / TARGET_CHAR_BIT];
+ CORE_ADDR sp, jb_addr;
+
+ sp = read_register (SP_REGNUM);
+
+ if (target_read_memory (sp + SP_ARG0, /* Offset of first arg on stack */
+ buf,
+ TARGET_PTR_BIT / TARGET_CHAR_BIT))
+ return 0;
+
+ jb_addr = extract_address (buf, TARGET_PTR_BIT / TARGET_CHAR_BIT);
+
+ if (target_read_memory (jb_addr + JB_PC * JB_ELEMENT_SIZE, buf,
+ TARGET_PTR_BIT / TARGET_CHAR_BIT))
+ return 0;
+
+ *pc = extract_address (buf, TARGET_PTR_BIT / TARGET_CHAR_BIT);
+
+ return 1;
+}
+
+#endif /* GET_LONGJMP_TARGET */
+
+#ifdef I386_AIX_TARGET
+/* On AIX, floating point values are returned in floating point registers. */
+
+void
+i386_extract_return_value(type, regbuf, valbuf)
+ struct type *type;
+ char regbuf[REGISTER_BYTES];
+ char *valbuf;
+{
+ if (TYPE_CODE_FLT == TYPE_CODE(type))
+ {
+ extern struct ext_format ext_format_i387;
+ double d;
+ /* 387 %st(0), gcc uses this */
+ ieee_extended_to_double (&ext_format_i387,
+ &regbuf[REGISTER_BYTE(FP0_REGNUM)],
+ &d);
+ switch (TYPE_LENGTH(type))
+ {
+ case 4: /* float */
+ {
+ float f = (float) d;
+ memcpy (valbuf, &f, 4);
+ break;
+ }
+ case 8: /* double */
+ memcpy (valbuf, &d, 8);
+ break;
+ default:
+ error("Unknown floating point size");
+ break;
+ }
+ }
+ else
+ {
+ memcpy (valbuf, regbuf, TYPE_LENGTH (type));
+ }
+}
+#endif /* I386_AIX_TARGET */
diff --git a/gnu/usr.bin/gdb/gdb/infcmd.c b/gnu/usr.bin/gdb/gdb/infcmd.c
new file mode 100644
index 0000000..5859ed2
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/infcmd.c
@@ -0,0 +1,1423 @@
+/* Memory-access and commands for "inferior" (child) process, for GDB.
+ Copyright 1986, 1987, 1988, 1989, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include <signal.h>
+#include <sys/param.h>
+#include <string.h>
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "frame.h"
+#include "inferior.h"
+#include "environ.h"
+#include "value.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "target.h"
+
+static void
+continue_command PARAMS ((char *, int));
+
+static void
+until_next_command PARAMS ((int));
+
+static void
+until_command PARAMS ((char *, int));
+
+static void
+path_info PARAMS ((char *, int));
+
+static void
+path_command PARAMS ((char *, int));
+
+static void
+unset_command PARAMS ((char *, int));
+
+static void
+float_info PARAMS ((char *, int));
+
+static void
+detach_command PARAMS ((char *, int));
+
+static void
+nofp_registers_info PARAMS ((char *, int));
+
+static void
+all_registers_info PARAMS ((char *, int));
+
+static void
+registers_info PARAMS ((char *, int));
+
+static void
+do_registers_info PARAMS ((int, int));
+
+static void
+unset_environment_command PARAMS ((char *, int));
+
+static void
+set_environment_command PARAMS ((char *, int));
+
+static void
+environment_info PARAMS ((char *, int));
+
+static void
+program_info PARAMS ((char *, int));
+
+static void
+finish_command PARAMS ((char *, int));
+
+static void
+signal_command PARAMS ((char *, int));
+
+static void
+jump_command PARAMS ((char *, int));
+
+static void
+step_1 PARAMS ((int, int, char *));
+
+static void
+nexti_command PARAMS ((char *, int));
+
+static void
+stepi_command PARAMS ((char *, int));
+
+static void
+next_command PARAMS ((char *, int));
+
+static void
+step_command PARAMS ((char *, int));
+
+static void
+run_command PARAMS ((char *, int));
+
+#define ERROR_NO_INFERIOR \
+ if (!target_has_execution) error ("The program is not being run.");
+
+/* String containing arguments to give to the program, separated by spaces.
+ Empty string (pointer to '\0') means no args. */
+
+static char *inferior_args;
+
+/* File name for default use for standard in/out in the inferior. */
+
+char *inferior_io_terminal;
+
+/* Pid of our debugged inferior, or 0 if no inferior now.
+ Since various parts of infrun.c test this to see whether there is a program
+ being debugged it should be nonzero (currently 3 is used) for remote
+ debugging. */
+
+int inferior_pid;
+
+/* Last signal that the inferior received (why it stopped). */
+
+int stop_signal;
+
+/* Address at which inferior stopped. */
+
+CORE_ADDR stop_pc;
+
+/* Stack frame when program stopped. */
+
+FRAME_ADDR stop_frame_address;
+
+/* Chain containing status of breakpoint(s) that we have stopped at. */
+
+bpstat stop_bpstat;
+
+/* Flag indicating that a command has proceeded the inferior past the
+ current breakpoint. */
+
+int breakpoint_proceeded;
+
+/* Nonzero if stopped due to a step command. */
+
+int stop_step;
+
+/* Nonzero if stopped due to completion of a stack dummy routine. */
+
+int stop_stack_dummy;
+
+/* Nonzero if stopped due to a random (unexpected) signal in inferior
+ process. */
+
+int stopped_by_random_signal;
+
+/* Range to single step within.
+ If this is nonzero, respond to a single-step signal
+ by continuing to step if the pc is in this range. */
+
+CORE_ADDR step_range_start; /* Inclusive */
+CORE_ADDR step_range_end; /* Exclusive */
+
+/* Stack frame address as of when stepping command was issued.
+ This is how we know when we step into a subroutine call,
+ and how to set the frame for the breakpoint used to step out. */
+
+FRAME_ADDR step_frame_address;
+
+/* 1 means step over all subroutine calls.
+ 0 means don't step over calls (used by stepi).
+ -1 means step over calls to undebuggable functions. */
+
+int step_over_calls;
+
+/* If stepping, nonzero means step count is > 1
+ so don't print frame next time inferior stops
+ if it stops due to stepping. */
+
+int step_multi;
+
+/* Environment to use for running inferior,
+ in format described in environ.h. */
+
+struct environ *inferior_environ;
+
+
+/* ARGSUSED */
+void
+tty_command (file, from_tty)
+ char *file;
+ int from_tty;
+{
+ if (file == 0)
+ error_no_arg ("terminal name for running target process");
+
+ inferior_io_terminal = savestring (file, strlen (file));
+}
+
+static void
+run_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ char *exec_file;
+
+ dont_repeat ();
+
+ /* Shouldn't this be target_has_execution? FIXME. */
+ if (inferior_pid)
+ {
+ if (
+ !query ("The program being debugged has been started already.\n\
+Start it from the beginning? "))
+ error ("Program not restarted.");
+ target_kill ();
+ }
+
+ exec_file = (char *) get_exec_file (0);
+
+ /* The exec file is re-read every time we do a generic_mourn_inferior, so
+ we just have to worry about the symbol file. */
+ reread_symbols ();
+
+ if (args)
+ {
+ char *cmd;
+ cmd = concat ("set args ", args, NULL);
+ make_cleanup (free, cmd);
+ execute_command (cmd, from_tty);
+ }
+
+ if (from_tty)
+ {
+ puts_filtered("Starting program: ");
+ if (exec_file)
+ puts_filtered(exec_file);
+ puts_filtered(" ");
+ puts_filtered(inferior_args);
+ puts_filtered("\n");
+ fflush (stdout);
+ }
+
+ target_create_inferior (exec_file, inferior_args,
+ environ_vector (inferior_environ));
+}
+
+static void
+continue_command (proc_count_exp, from_tty)
+ char *proc_count_exp;
+ int from_tty;
+{
+ ERROR_NO_INFERIOR;
+
+ /* If have argument, set proceed count of breakpoint we stopped at. */
+
+ if (proc_count_exp != NULL)
+ {
+ bpstat bs = stop_bpstat;
+ int num = bpstat_num (&bs);
+ if (num == 0 && from_tty)
+ {
+ printf_filtered
+ ("Not stopped at any breakpoint; argument ignored.\n");
+ }
+ while (num != 0)
+ {
+ set_ignore_count (num,
+ parse_and_eval_address (proc_count_exp) - 1,
+ from_tty);
+ /* set_ignore_count prints a message ending with a period.
+ So print two spaces before "Continuing.". */
+ if (from_tty)
+ printf_filtered (" ");
+ num = bpstat_num (&bs);
+ }
+ }
+
+ if (from_tty)
+ printf_filtered ("Continuing.\n");
+
+ clear_proceed_status ();
+
+ proceed ((CORE_ADDR) -1, -1, 0);
+}
+
+/* Step until outside of current statement. */
+
+/* ARGSUSED */
+static void
+step_command (count_string, from_tty)
+ char *count_string;
+ int from_tty;
+{
+ step_1 (0, 0, count_string);
+}
+
+/* Likewise, but skip over subroutine calls as if single instructions. */
+
+/* ARGSUSED */
+static void
+next_command (count_string, from_tty)
+ char *count_string;
+ int from_tty;
+{
+ step_1 (1, 0, count_string);
+}
+
+/* Likewise, but step only one instruction. */
+
+/* ARGSUSED */
+static void
+stepi_command (count_string, from_tty)
+ char *count_string;
+ int from_tty;
+{
+ step_1 (0, 1, count_string);
+}
+
+/* ARGSUSED */
+static void
+nexti_command (count_string, from_tty)
+ char *count_string;
+ int from_tty;
+{
+ step_1 (1, 1, count_string);
+}
+
+static void
+step_1 (skip_subroutines, single_inst, count_string)
+ int skip_subroutines;
+ int single_inst;
+ char *count_string;
+{
+ register int count = 1;
+ FRAME fr;
+ struct cleanup *cleanups = 0;
+
+ ERROR_NO_INFERIOR;
+ count = count_string ? parse_and_eval_address (count_string) : 1;
+
+ if (!single_inst || skip_subroutines) /* leave si command alone */
+ {
+ enable_longjmp_breakpoint();
+ cleanups = make_cleanup(disable_longjmp_breakpoint, 0);
+ }
+
+ for (; count > 0; count--)
+ {
+ clear_proceed_status ();
+
+ fr = get_current_frame ();
+ if (!fr) /* Avoid coredump here. Why tho? */
+ error ("No current frame");
+ step_frame_address = FRAME_FP (fr);
+
+ if (! single_inst)
+ {
+ find_pc_line_pc_range (stop_pc, &step_range_start, &step_range_end);
+ if (step_range_end == 0)
+ {
+ char *name;
+ if (find_pc_partial_function (stop_pc, &name, &step_range_start,
+ &step_range_end) == 0)
+ error ("Cannot find bounds of current function");
+
+ target_terminal_ours ();
+ printf_filtered ("\
+Single stepping until exit from function %s, \n\
+which has no line number information.\n", name);
+ fflush (stdout);
+ }
+ }
+ else
+ {
+ /* Say we are stepping, but stop after one insn whatever it does. */
+ step_range_start = step_range_end = 1;
+ if (!skip_subroutines)
+ /* It is stepi.
+ Don't step over function calls, not even to functions lacking
+ line numbers. */
+ step_over_calls = 0;
+ }
+
+ if (skip_subroutines)
+ step_over_calls = 1;
+
+ step_multi = (count > 1);
+ proceed ((CORE_ADDR) -1, -1, 1);
+ if (! stop_step)
+ break;
+
+ /* FIXME: On nexti, this may have already been done (when we hit the
+ step resume break, I think). Probably this should be moved to
+ wait_for_inferior (near the top). */
+#if defined (SHIFT_INST_REGS)
+ SHIFT_INST_REGS();
+#endif
+ }
+
+ if (!single_inst || skip_subroutines)
+ do_cleanups(cleanups);
+}
+
+/* Continue program at specified address. */
+
+static void
+jump_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ register CORE_ADDR addr;
+ struct symtabs_and_lines sals;
+ struct symtab_and_line sal;
+ struct symbol *fn;
+ struct symbol *sfn;
+
+ ERROR_NO_INFERIOR;
+
+ if (!arg)
+ error_no_arg ("starting address");
+
+ sals = decode_line_spec_1 (arg, 1);
+ if (sals.nelts != 1)
+ {
+ error ("Unreasonable jump request");
+ }
+
+ sal = sals.sals[0];
+ free ((PTR)sals.sals);
+
+ if (sal.symtab == 0 && sal.pc == 0)
+ error ("No source file has been specified.");
+
+ resolve_sal_pc (&sal); /* May error out */
+
+ /* See if we are trying to jump to another function. */
+ fn = get_frame_function (get_current_frame ());
+ sfn = find_pc_function (sal.pc);
+ if (fn != NULL && sfn != fn)
+ {
+ if (!query ("Line %d is not in `%s'. Jump anyway? ", sal.line,
+ SYMBOL_SOURCE_NAME (fn)))
+ {
+ error ("Not confirmed.");
+ /* NOTREACHED */
+ }
+ }
+
+ addr = sal.pc;
+
+ if (from_tty)
+ printf_filtered ("Continuing at %s.\n",
+ local_hex_string((unsigned long) addr));
+
+ clear_proceed_status ();
+ proceed (addr, 0, 0);
+}
+
+/* Continue program giving it specified signal. */
+
+static void
+signal_command (signum_exp, from_tty)
+ char *signum_exp;
+ int from_tty;
+{
+ register int signum;
+
+ dont_repeat (); /* Too dangerous. */
+ ERROR_NO_INFERIOR;
+
+ if (!signum_exp)
+ error_no_arg ("signal number");
+
+ /* It would be even slicker to make signal names be valid expressions,
+ (the type could be "enum $signal" or some such), then the user could
+ assign them to convenience variables. */
+ signum = strtosigno (signum_exp);
+
+ if (signum == 0)
+ /* Not found as a name, try it as an expression. */
+ signum = parse_and_eval_address (signum_exp);
+
+ if (from_tty)
+ {
+ char *signame = strsigno (signum);
+ printf_filtered ("Continuing with signal ");
+ if (signame == NULL || signum == 0)
+ printf_filtered ("%d.\n", signum);
+ else
+ /* Do we need to print the number as well as the name? */
+ printf_filtered ("%s (%d).\n", signame, signum);
+ }
+
+ clear_proceed_status ();
+ proceed (stop_pc, signum, 0);
+}
+
+/* Call breakpoint_auto_delete on the current contents of the bpstat
+ pointed to by arg (which is really a bpstat *). */
+void
+breakpoint_auto_delete_contents (arg)
+ PTR arg;
+{
+ breakpoint_auto_delete (*(bpstat *)arg);
+}
+
+/* Execute a "stack dummy", a piece of code stored in the stack
+ by the debugger to be executed in the inferior.
+
+ To call: first, do PUSH_DUMMY_FRAME.
+ Then push the contents of the dummy. It should end with a breakpoint insn.
+ Then call here, passing address at which to start the dummy.
+
+ The contents of all registers are saved before the dummy frame is popped
+ and copied into the buffer BUFFER.
+
+ The dummy's frame is automatically popped whenever that break is hit.
+ If that is the first time the program stops, run_stack_dummy
+ returns to its caller with that frame already gone and returns 0.
+ Otherwise, run_stack-dummy returns 1 (the frame will eventually be popped
+ when we do hit that breakpoint). */
+
+/* DEBUG HOOK: 4 => return instead of letting the stack dummy run. */
+
+static int stack_dummy_testing = 0;
+
+int
+run_stack_dummy (addr, buffer)
+ CORE_ADDR addr;
+ char buffer[REGISTER_BYTES];
+{
+ struct cleanup *old_cleanups = make_cleanup (null_cleanup, 0);
+
+ /* Now proceed, having reached the desired place. */
+ clear_proceed_status ();
+ if (stack_dummy_testing & 4)
+ {
+ POP_FRAME;
+ return(0);
+ }
+#ifdef CALL_DUMMY_BREAKPOINT_OFFSET
+ {
+ struct breakpoint *bpt;
+ struct symtab_and_line sal;
+
+#if CALL_DUMMY_LOCATION != AT_ENTRY_POINT
+ sal.pc = addr - CALL_DUMMY_START_OFFSET + CALL_DUMMY_BREAKPOINT_OFFSET;
+#else
+ sal.pc = entry_point_address ();
+#endif
+ sal.symtab = NULL;
+ sal.line = 0;
+
+ /* Set up a FRAME for the dummy frame so we can pass it to
+ set_momentary_breakpoint. We need to give the breakpoint a
+ frame in case there is only one copy of the dummy (e.g.
+ CALL_DUMMY_LOCATION == AFTER_TEXT_END). */
+ flush_cached_frames ();
+ set_current_frame (create_new_frame (read_fp (), sal.pc));
+
+ /* If defined, CALL_DUMMY_BREAKPOINT_OFFSET is where we need to put
+ a breakpoint instruction. If not, the call dummy already has the
+ breakpoint instruction in it.
+
+ addr is the address of the call dummy plus the CALL_DUMMY_START_OFFSET,
+ so we need to subtract the CALL_DUMMY_START_OFFSET. */
+ bpt = set_momentary_breakpoint (sal,
+ get_current_frame (),
+ bp_call_dummy);
+ bpt->disposition = delete;
+
+ /* If all error()s out of proceed ended up calling normal_stop (and
+ perhaps they should; it already does in the special case of error
+ out of resume()), then we wouldn't need this. */
+ make_cleanup (breakpoint_auto_delete_contents, &stop_bpstat);
+ }
+#endif /* CALL_DUMMY_BREAKPOINT_OFFSET. */
+
+ proceed_to_finish = 1; /* We want stop_registers, please... */
+ proceed (addr, 0, 0);
+
+ discard_cleanups (old_cleanups);
+
+ if (!stop_stack_dummy)
+ return 1;
+
+ /* On return, the stack dummy has been popped already. */
+
+ memcpy (buffer, stop_registers, sizeof stop_registers);
+ return 0;
+}
+
+/* Proceed until we reach a different source line with pc greater than
+ our current one or exit the function. We skip calls in both cases.
+
+ Note that eventually this command should probably be changed so
+ that only source lines are printed out when we hit the breakpoint
+ we set. I'm going to postpone this until after a hopeful rewrite
+ of wait_for_inferior and the proceed status code. -- randy */
+
+/* ARGSUSED */
+static void
+until_next_command (from_tty)
+ int from_tty;
+{
+ FRAME frame;
+ CORE_ADDR pc;
+ struct symbol *func;
+ struct symtab_and_line sal;
+
+ clear_proceed_status ();
+
+ frame = get_current_frame ();
+
+ /* Step until either exited from this function or greater
+ than the current line (if in symbolic section) or pc (if
+ not). */
+
+ pc = read_pc ();
+ func = find_pc_function (pc);
+
+ if (!func)
+ {
+ struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (pc);
+
+ if (msymbol == NULL)
+ error ("Execution is not within a known function.");
+
+ step_range_start = SYMBOL_VALUE_ADDRESS (msymbol);
+ step_range_end = pc;
+ }
+ else
+ {
+ sal = find_pc_line (pc, 0);
+
+ step_range_start = BLOCK_START (SYMBOL_BLOCK_VALUE (func));
+ step_range_end = sal.end;
+ }
+
+ step_over_calls = 1;
+ step_frame_address = FRAME_FP (frame);
+
+ step_multi = 0; /* Only one call to proceed */
+
+ proceed ((CORE_ADDR) -1, -1, 1);
+}
+
+static void
+until_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ if (!target_has_execution)
+ error ("The program is not running.");
+ if (arg)
+ until_break_command (arg, from_tty);
+ else
+ until_next_command (from_tty);
+}
+
+/* "finish": Set a temporary breakpoint at the place
+ the selected frame will return to, then continue. */
+
+static void
+finish_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ struct symtab_and_line sal;
+ register FRAME frame;
+ struct frame_info *fi;
+ register struct symbol *function;
+ struct breakpoint *breakpoint;
+ struct cleanup *old_chain;
+
+ if (arg)
+ error ("The \"finish\" command does not take any arguments.");
+ if (!target_has_execution)
+ error ("The program is not running.");
+ if (selected_frame == NULL)
+ error ("No selected frame.");
+
+ frame = get_prev_frame (selected_frame);
+ if (frame == 0)
+ error ("\"finish\" not meaningful in the outermost frame.");
+
+ clear_proceed_status ();
+
+ fi = get_frame_info (frame);
+ sal = find_pc_line (fi->pc, 0);
+ sal.pc = fi->pc;
+
+ breakpoint = set_momentary_breakpoint (sal, frame, bp_finish);
+
+ old_chain = make_cleanup(delete_breakpoint, breakpoint);
+
+ /* Find the function we will return from. */
+
+ fi = get_frame_info (selected_frame);
+ function = find_pc_function (fi->pc);
+
+ /* Print info on the selected frame, including level number
+ but not source. */
+ if (from_tty)
+ {
+ printf_filtered ("Run till exit from ");
+ print_stack_frame (selected_frame, selected_frame_level, 0);
+ }
+
+ proceed_to_finish = 1; /* We want stop_registers, please... */
+ proceed ((CORE_ADDR) -1, -1, 0);
+
+ /* Did we stop at our breakpoint? */
+ if (bpstat_find_breakpoint(stop_bpstat, breakpoint) != NULL
+ && function != 0)
+ {
+ struct type *value_type;
+ register value val;
+ CORE_ADDR funcaddr;
+
+ value_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
+ if (!value_type)
+ fatal ("internal: finish_command: function has no target type");
+
+ if (TYPE_CODE (value_type) == TYPE_CODE_VOID)
+ return;
+
+ funcaddr = BLOCK_START (SYMBOL_BLOCK_VALUE (function));
+
+ val = value_being_returned (value_type, stop_registers,
+ using_struct_return (value_of_variable (function, NULL),
+ funcaddr,
+ value_type,
+ BLOCK_GCC_COMPILED (SYMBOL_BLOCK_VALUE (function))));
+
+ printf_filtered ("Value returned is $%d = ", record_latest_value (val));
+ value_print (val, stdout, 0, Val_no_prettyprint);
+ printf_filtered ("\n");
+ }
+ do_cleanups(old_chain);
+}
+
+/* ARGSUSED */
+static void
+program_info (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ bpstat bs = stop_bpstat;
+ int num = bpstat_num (&bs);
+
+ if (!target_has_execution)
+ {
+ printf_filtered ("The program being debugged is not being run.\n");
+ return;
+ }
+
+ target_files_info ();
+ printf_filtered ("Program stopped at %s.\n",
+ local_hex_string((unsigned long) stop_pc));
+ if (stop_step)
+ printf_filtered ("It stopped after being stepped.\n");
+ else if (num != 0)
+ {
+ /* There may be several breakpoints in the same place, so this
+ isn't as strange as it seems. */
+ while (num != 0)
+ {
+ if (num < 0)
+ printf_filtered ("It stopped at a breakpoint that has since been deleted.\n");
+ else
+ printf_filtered ("It stopped at breakpoint %d.\n", num);
+ num = bpstat_num (&bs);
+ }
+ }
+ else if (stop_signal)
+ {
+#ifdef PRINT_RANDOM_SIGNAL
+ PRINT_RANDOM_SIGNAL (stop_signal);
+#else
+ char *signame = strsigno (stop_signal);
+ printf_filtered ("It stopped with signal ");
+ if (signame == NULL)
+ printf_filtered ("%d", stop_signal);
+ else
+ /* Do we need to print the number as well as the name? */
+ printf_filtered ("%s (%d)", signame, stop_signal);
+ printf_filtered (", %s.\n", safe_strsignal (stop_signal));
+#endif
+ }
+
+ if (!from_tty)
+ printf_filtered ("Type \"info stack\" or \"info registers\" for more information.\n");
+}
+
+static void
+environment_info (var, from_tty)
+ char *var;
+ int from_tty;
+{
+ if (var)
+ {
+ register char *val = get_in_environ (inferior_environ, var);
+ if (val)
+ {
+ puts_filtered (var);
+ puts_filtered (" = ");
+ puts_filtered (val);
+ puts_filtered ("\n");
+ }
+ else
+ {
+ puts_filtered ("Environment variable \"");
+ puts_filtered (var);
+ puts_filtered ("\" not defined.\n");
+ }
+ }
+ else
+ {
+ register char **vector = environ_vector (inferior_environ);
+ while (*vector)
+ {
+ puts_filtered (*vector++);
+ puts_filtered ("\n");
+ }
+ }
+}
+
+static void
+set_environment_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ register char *p, *val, *var;
+ int nullset = 0;
+
+ if (arg == 0)
+ error_no_arg ("environment variable and value");
+
+ /* Find seperation between variable name and value */
+ p = (char *) strchr (arg, '=');
+ val = (char *) strchr (arg, ' ');
+
+ if (p != 0 && val != 0)
+ {
+ /* We have both a space and an equals. If the space is before the
+ equals, walk forward over the spaces til we see a nonspace
+ (possibly the equals). */
+ if (p > val)
+ while (*val == ' ')
+ val++;
+
+ /* Now if the = is after the char following the spaces,
+ take the char following the spaces. */
+ if (p > val)
+ p = val - 1;
+ }
+ else if (val != 0 && p == 0)
+ p = val;
+
+ if (p == arg)
+ error_no_arg ("environment variable to set");
+
+ if (p == 0 || p[1] == 0)
+ {
+ nullset = 1;
+ if (p == 0)
+ p = arg + strlen (arg); /* So that savestring below will work */
+ }
+ else
+ {
+ /* Not setting variable value to null */
+ val = p + 1;
+ while (*val == ' ' || *val == '\t')
+ val++;
+ }
+
+ while (p != arg && (p[-1] == ' ' || p[-1] == '\t')) p--;
+
+ var = savestring (arg, p - arg);
+ if (nullset)
+ {
+ printf_filtered ("Setting environment variable \"%s\" to null value.\n", var);
+ set_in_environ (inferior_environ, var, "");
+ }
+ else
+ set_in_environ (inferior_environ, var, val);
+ free (var);
+}
+
+static void
+unset_environment_command (var, from_tty)
+ char *var;
+ int from_tty;
+{
+ if (var == 0)
+ {
+ /* If there is no argument, delete all environment variables.
+ Ask for confirmation if reading from the terminal. */
+ if (!from_tty || query ("Delete all environment variables? "))
+ {
+ free_environ (inferior_environ);
+ inferior_environ = make_environ ();
+ }
+ }
+ else
+ unset_in_environ (inferior_environ, var);
+}
+
+/* Handle the execution path (PATH variable) */
+
+static const char path_var_name[] = "PATH";
+
+/* ARGSUSED */
+static void
+path_info (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ puts_filtered ("Executable and object file path: ");
+ puts_filtered (get_in_environ (inferior_environ, path_var_name));
+ puts_filtered ("\n");
+}
+
+/* Add zero or more directories to the front of the execution path. */
+
+static void
+path_command (dirname, from_tty)
+ char *dirname;
+ int from_tty;
+{
+ char *exec_path;
+
+ dont_repeat ();
+ exec_path = strsave (get_in_environ (inferior_environ, path_var_name));
+ mod_path (dirname, &exec_path);
+ set_in_environ (inferior_environ, path_var_name, exec_path);
+ free (exec_path);
+ if (from_tty)
+ path_info ((char *)NULL, from_tty);
+}
+
+/* This routine is getting awfully cluttered with #if's. It's probably
+ time to turn this into READ_PC and define it in the tm.h file.
+ Ditto for write_pc. */
+
+CORE_ADDR
+read_pc ()
+{
+#ifdef TARGET_READ_PC
+ return TARGET_READ_PC ();
+#else
+ return ADDR_BITS_REMOVE ((CORE_ADDR) read_register (PC_REGNUM));
+#endif
+}
+
+void
+write_pc (val)
+ CORE_ADDR val;
+{
+#ifdef TARGET_WRITE_PC
+ TARGET_WRITE_PC (val);
+#else
+ write_register (PC_REGNUM, (long) val);
+#ifdef NPC_REGNUM
+ write_register (NPC_REGNUM, (long) val + 4);
+#ifdef NNPC_REGNUM
+ write_register (NNPC_REGNUM, (long) val + 8);
+#endif
+#endif
+#endif
+}
+
+/* Cope with strage ways of getting to the stack and frame pointers */
+
+CORE_ADDR
+read_sp ()
+{
+#ifdef TARGET_READ_SP
+ return TARGET_READ_SP ();
+#else
+ return read_register (SP_REGNUM);
+#endif
+}
+
+void
+write_sp (val)
+ CORE_ADDR val;
+{
+#ifdef TARGET_WRITE_SP
+ TARGET_WRITE_SP (val);
+#else
+ write_register (SP_REGNUM, val);
+#endif
+}
+
+
+CORE_ADDR
+read_fp ()
+{
+#ifdef TARGET_READ_FP
+ return TARGET_READ_FP ();
+#else
+ return read_register (FP_REGNUM);
+#endif
+}
+
+void
+write_fp (val)
+ CORE_ADDR val;
+{
+#ifdef TARGET_WRITE_FP
+ TARGET_WRITE_FP (val);
+#else
+ write_register (FP_REGNUM, val);
+#endif
+}
+
+const char * const reg_names[] = REGISTER_NAMES;
+
+/* Print out the machine register regnum. If regnum is -1,
+ print all registers (fpregs == 1) or all non-float registers
+ (fpregs == 0).
+
+ For most machines, having all_registers_info() print the
+ register(s) one per line is good enough. If a different format
+ is required, (eg, for MIPS or Pyramid 90x, which both have
+ lots of regs), or there is an existing convention for showing
+ all the registers, define the macro DO_REGISTERS_INFO(regnum, fp)
+ to provide that format. */
+
+#if !defined (DO_REGISTERS_INFO)
+#define DO_REGISTERS_INFO(regnum, fp) do_registers_info(regnum, fp)
+static void
+do_registers_info (regnum, fpregs)
+ int regnum;
+ int fpregs;
+{
+ register int i;
+
+ for (i = 0; i < NUM_REGS; i++)
+ {
+ char raw_buffer[MAX_REGISTER_RAW_SIZE];
+ char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE];
+
+ /* Decide between printing all regs, nonfloat regs, or specific reg. */
+ if (regnum == -1) {
+ if (TYPE_CODE (REGISTER_VIRTUAL_TYPE (i)) == TYPE_CODE_FLT && !fpregs)
+ continue;
+ } else {
+ if (i != regnum)
+ continue;
+ }
+
+ fputs_filtered (reg_names[i], stdout);
+ print_spaces_filtered (15 - strlen (reg_names[i]), stdout);
+
+ /* Get the data in raw format, then convert also to virtual format. */
+ if (read_relative_register_raw_bytes (i, raw_buffer))
+ {
+ printf_filtered ("Invalid register contents\n");
+ continue;
+ }
+
+ REGISTER_CONVERT_TO_VIRTUAL (i, raw_buffer, virtual_buffer);
+
+ /* If virtual format is floating, print it that way, and in raw hex. */
+ if (TYPE_CODE (REGISTER_VIRTUAL_TYPE (i)) == TYPE_CODE_FLT
+ && ! INVALID_FLOAT (virtual_buffer, REGISTER_VIRTUAL_SIZE (i)))
+ {
+ register int j;
+
+ val_print (REGISTER_VIRTUAL_TYPE (i), virtual_buffer, 0,
+ stdout, 0, 1, 0, Val_pretty_default);
+
+ printf_filtered ("\t(raw 0x");
+ for (j = 0; j < REGISTER_RAW_SIZE (i); j++)
+ printf_filtered ("%02x", (unsigned char)raw_buffer[j]);
+ printf_filtered (")");
+ }
+
+/* FIXME! val_print probably can handle all of these cases now... */
+
+ /* Else if virtual format is too long for printf,
+ print in hex a byte at a time. */
+ else if (REGISTER_VIRTUAL_SIZE (i) > sizeof (long))
+ {
+ register int j;
+ printf_filtered ("0x");
+ for (j = 0; j < REGISTER_VIRTUAL_SIZE (i); j++)
+ printf_filtered ("%02x", (unsigned char)virtual_buffer[j]);
+ }
+ /* Else print as integer in hex and in decimal. */
+ else
+ {
+ val_print (REGISTER_VIRTUAL_TYPE (i), raw_buffer, 0,
+ stdout, 'x', 1, 0, Val_pretty_default);
+ printf_filtered ("\t");
+ val_print (REGISTER_VIRTUAL_TYPE (i), raw_buffer, 0,
+ stdout, 0, 1, 0, Val_pretty_default);
+ }
+
+ /* The SPARC wants to print even-numbered float regs as doubles
+ in addition to printing them as floats. */
+#ifdef PRINT_REGISTER_HOOK
+ PRINT_REGISTER_HOOK (i);
+#endif
+
+ printf_filtered ("\n");
+ }
+}
+#endif /* no DO_REGISTERS_INFO. */
+
+static void
+registers_info (addr_exp, fpregs)
+ char *addr_exp;
+ int fpregs;
+{
+ int regnum;
+ register char *end;
+
+ if (!target_has_registers)
+ error ("The program has no registers now.");
+
+ if (!addr_exp)
+ {
+ DO_REGISTERS_INFO(-1, fpregs);
+ return;
+ }
+
+ do
+ {
+ if (addr_exp[0] == '$')
+ addr_exp++;
+ end = addr_exp;
+ while (*end != '\0' && *end != ' ' && *end != '\t')
+ ++end;
+ for (regnum = 0; regnum < NUM_REGS; regnum++)
+ if (!strncmp (addr_exp, reg_names[regnum], end - addr_exp)
+ && strlen (reg_names[regnum]) == end - addr_exp)
+ goto found;
+ if (*addr_exp >= '0' && *addr_exp <= '9')
+ regnum = atoi (addr_exp); /* Take a number */
+ if (regnum >= NUM_REGS) /* Bad name, or bad number */
+ error ("%.*s: invalid register", end - addr_exp, addr_exp);
+
+found:
+ DO_REGISTERS_INFO(regnum, fpregs);
+
+ addr_exp = end;
+ while (*addr_exp == ' ' || *addr_exp == '\t')
+ ++addr_exp;
+ } while (*addr_exp != '\0');
+}
+
+static void
+all_registers_info (addr_exp, from_tty)
+ char *addr_exp;
+ int from_tty;
+{
+ registers_info (addr_exp, 1);
+}
+
+static void
+nofp_registers_info (addr_exp, from_tty)
+ char *addr_exp;
+ int from_tty;
+{
+ registers_info (addr_exp, 0);
+}
+
+/*
+ * TODO:
+ * Should save/restore the tty state since it might be that the
+ * program to be debugged was started on this tty and it wants
+ * the tty in some state other than what we want. If it's running
+ * on another terminal or without a terminal, then saving and
+ * restoring the tty state is a harmless no-op.
+ * This only needs to be done if we are attaching to a process.
+ */
+
+/*
+ attach_command --
+ takes a program started up outside of gdb and ``attaches'' to it.
+ This stops it cold in its tracks and allows us to start debugging it.
+ and wait for the trace-trap that results from attaching. */
+
+void
+attach_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ dont_repeat (); /* Not for the faint of heart */
+
+ if (target_has_execution)
+ {
+ if (query ("A program is being debugged already. Kill it? "))
+ target_kill ();
+ else
+ error ("Not killed.");
+ }
+
+ target_attach (args, from_tty);
+
+ /* Set up the "saved terminal modes" of the inferior
+ based on what modes we are starting it with. */
+ target_terminal_init ();
+
+ /* Install inferior's terminal modes. */
+ target_terminal_inferior ();
+
+ /* Set up execution context to know that we should return from
+ wait_for_inferior as soon as the target reports a stop. */
+ init_wait_for_inferior ();
+ clear_proceed_status ();
+ stop_soon_quietly = 1;
+
+ wait_for_inferior ();
+
+#ifdef SOLIB_ADD
+ /* Add shared library symbols from the newly attached process, if any. */
+ SOLIB_ADD ((char *)0, from_tty, (struct target_ops *)0);
+#endif
+
+ normal_stop ();
+}
+
+/*
+ * detach_command --
+ * takes a program previously attached to and detaches it.
+ * The program resumes execution and will no longer stop
+ * on signals, etc. We better not have left any breakpoints
+ * in the program or it'll die when it hits one. For this
+ * to work, it may be necessary for the process to have been
+ * previously attached. It *might* work if the program was
+ * started via the normal ptrace (PTRACE_TRACEME).
+ */
+
+static void
+detach_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ dont_repeat (); /* Not for the faint of heart */
+ target_detach (args, from_tty);
+}
+
+/* ARGSUSED */
+static void
+float_info (addr_exp, from_tty)
+ char *addr_exp;
+ int from_tty;
+{
+#ifdef FLOAT_INFO
+ FLOAT_INFO;
+#else
+ printf_filtered ("No floating point info available for this processor.\n");
+#endif
+}
+
+/* ARGSUSED */
+static void
+unset_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ printf_filtered ("\"unset\" must be followed by the name of an unset subcommand.\n");
+ help_list (unsetlist, "unset ", -1, stdout);
+}
+
+void
+_initialize_infcmd ()
+{
+ struct cmd_list_element *c;
+
+ add_com ("tty", class_run, tty_command,
+ "Set terminal for future runs of program being debugged.");
+
+ add_show_from_set
+ (add_set_cmd ("args", class_run, var_string_noescape, (char *)&inferior_args,
+
+"Set arguments to give program being debugged when it is started.\n\
+Follow this command with any number of args, to be passed to the program.",
+ &setlist),
+ &showlist);
+
+ c = add_cmd
+ ("environment", no_class, environment_info,
+ "The environment to give the program, or one variable's value.\n\
+With an argument VAR, prints the value of environment variable VAR to\n\
+give the program being debugged. With no arguments, prints the entire\n\
+environment to be given to the program.", &showlist);
+ c->completer = noop_completer;
+
+ add_prefix_cmd ("unset", no_class, unset_command,
+ "Complement to certain \"set\" commands",
+ &unsetlist, "unset ", 0, &cmdlist);
+
+ c = add_cmd ("environment", class_run, unset_environment_command,
+ "Cancel environment variable VAR for the program.\n\
+This does not affect the program until the next \"run\" command.",
+ &unsetlist);
+ c->completer = noop_completer;
+
+ c = add_cmd ("environment", class_run, set_environment_command,
+ "Set environment variable value to give the program.\n\
+Arguments are VAR VALUE where VAR is variable name and VALUE is value.\n\
+VALUES of environment variables are uninterpreted strings.\n\
+This does not affect the program until the next \"run\" command.",
+ &setlist);
+ c->completer = noop_completer;
+
+ add_com ("path", class_files, path_command,
+ "Add directory DIR(s) to beginning of search path for object files.\n\
+$cwd in the path means the current working directory.\n\
+This path is equivalent to the $PATH shell variable. It is a list of\n\
+directories, separated by colons. These directories are searched to find\n\
+fully linked executable files and separately compiled object files as needed.");
+
+ c = add_cmd ("paths", no_class, path_info,
+ "Current search path for finding object files.\n\
+$cwd in the path means the current working directory.\n\
+This path is equivalent to the $PATH shell variable. It is a list of\n\
+directories, separated by colons. These directories are searched to find\n\
+fully linked executable files and separately compiled object files as needed.", &showlist);
+ c->completer = noop_completer;
+
+ add_com ("attach", class_run, attach_command,
+ "Attach to a process or file outside of GDB.\n\
+This command attaches to another target, of the same type as your last\n\
+`target' command (`info files' will show your target stack).\n\
+The command may take as argument a process id or a device file.\n\
+For a process id, you must have permission to send the process a signal,\n\
+and it must have the same effective uid as the debugger.\n\
+When using \"attach\", you should use the \"file\" command to specify\n\
+the program running in the process, and to load its symbol table.");
+
+ add_com ("detach", class_run, detach_command,
+ "Detach a process or file previously attached.\n\
+If a process, it is no longer traced, and it continues its execution. If you\n\
+were debugging a file, the file is closed and gdb no longer accesses it.");
+
+ add_com ("signal", class_run, signal_command,
+ "Continue program giving it signal number SIGNUMBER.");
+
+ add_com ("stepi", class_run, stepi_command,
+ "Step one instruction exactly.\n\
+Argument N means do this N times (or till program stops for another reason).");
+ add_com_alias ("si", "stepi", class_alias, 0);
+
+ add_com ("nexti", class_run, nexti_command,
+ "Step one instruction, but proceed through subroutine calls.\n\
+Argument N means do this N times (or till program stops for another reason).");
+ add_com_alias ("ni", "nexti", class_alias, 0);
+
+ add_com ("finish", class_run, finish_command,
+ "Execute until selected stack frame returns.\n\
+Upon return, the value returned is printed and put in the value history.");
+
+ add_com ("next", class_run, next_command,
+ "Step program, proceeding through subroutine calls.\n\
+Like the \"step\" command as long as subroutine calls do not happen;\n\
+when they do, the call is treated as one instruction.\n\
+Argument N means do this N times (or till program stops for another reason).");
+ add_com_alias ("n", "next", class_run, 1);
+
+ add_com ("step", class_run, step_command,
+ "Step program until it reaches a different source line.\n\
+Argument N means do this N times (or till program stops for another reason).");
+ add_com_alias ("s", "step", class_run, 1);
+
+ add_com ("until", class_run, until_command,
+ "Execute until the program reaches a source line greater than the current\n\
+or a specified line or address or function (same args as break command).\n\
+Execution will also stop upon exit from the current stack frame.");
+ add_com_alias ("u", "until", class_run, 1);
+
+ add_com ("jump", class_run, jump_command,
+ "Continue program being debugged at specified line or address.\n\
+Give as argument either LINENUM or *ADDR, where ADDR is an expression\n\
+for an address to start at.");
+
+ add_com ("continue", class_run, continue_command,
+ "Continue program being debugged, after signal or breakpoint.\n\
+If proceeding from breakpoint, a number N may be used as an argument,\n\
+which means to set the ignore count of that breakpoint to N - 1 (so that\n\
+the breakpoint won't break until the Nth time it is reached).");
+ add_com_alias ("c", "cont", class_run, 1);
+ add_com_alias ("fg", "cont", class_run, 1);
+
+ add_com ("run", class_run, run_command,
+ "Start debugged program. You may specify arguments to give it.\n\
+Args may include \"*\", or \"[...]\"; they are expanded using \"sh\".\n\
+Input and output redirection with \">\", \"<\", or \">>\" are also allowed.\n\n\
+With no arguments, uses arguments last specified (with \"run\" or \"set args\").\n\
+To cancel previous arguments and run with no arguments,\n\
+use \"set args\" without arguments.");
+ add_com_alias ("r", "run", class_run, 1);
+
+ add_info ("registers", nofp_registers_info,
+ "List of integer registers and their contents, for selected stack frame.\n\
+Register name as argument means describe only that register.");
+
+ add_info ("all-registers", all_registers_info,
+"List of all registers and their contents, for selected stack frame.\n\
+Register name as argument means describe only that register.");
+
+ add_info ("program", program_info,
+ "Execution status of the program.");
+
+ add_info ("float", float_info,
+ "Print the status of the floating point unit\n");
+
+ inferior_args = savestring ("", 1); /* Initially no args */
+ inferior_environ = make_environ ();
+ init_environ (inferior_environ);
+}
diff --git a/gnu/usr.bin/gdb/gdb/inferior.h b/gnu/usr.bin/gdb/gdb/inferior.h
new file mode 100644
index 0000000..0f5499a
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/inferior.h
@@ -0,0 +1,401 @@
+/* Variables that describe the inferior process running under GDB:
+ Where it is, why it stopped, and how to step it.
+ Copyright 1986, 1989, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (INFERIOR_H)
+#define INFERIOR_H 1
+
+/* For bpstat. */
+#include "breakpoint.h"
+
+/* For FRAME_ADDR. */
+#include "frame.h"
+
+/*
+ * Structure in which to save the status of the inferior. Save
+ * through "save_inferior_status", restore through
+ * "restore_inferior_status".
+ * This pair of routines should be called around any transfer of
+ * control to the inferior which you don't want showing up in your
+ * control variables.
+ */
+struct inferior_status {
+ int stop_signal;
+ CORE_ADDR stop_pc;
+ FRAME_ADDR stop_frame_address;
+ bpstat stop_bpstat;
+ int stop_step;
+ int stop_stack_dummy;
+ int stopped_by_random_signal;
+ int trap_expected;
+ CORE_ADDR step_range_start;
+ CORE_ADDR step_range_end;
+ FRAME_ADDR step_frame_address;
+ int step_over_calls;
+ CORE_ADDR step_resume_break_address;
+ int stop_after_trap;
+ int stop_soon_quietly;
+ FRAME_ADDR selected_frame_address;
+ int selected_level;
+ char stop_registers[REGISTER_BYTES];
+
+ /* These are here because if call_function_by_hand has written some
+ registers and then decides to call error(), we better not have changed
+ any registers. */
+ char registers[REGISTER_BYTES];
+
+ int breakpoint_proceeded;
+ int restore_stack_info;
+ int proceed_to_finish;
+};
+
+extern void
+save_inferior_status PARAMS ((struct inferior_status *, int));
+
+extern void
+restore_inferior_status PARAMS ((struct inferior_status *));
+
+/* File name for default use for standard in/out in the inferior. */
+
+extern char *inferior_io_terminal;
+
+/* Pid of our debugged inferior, or 0 if no inferior now. */
+
+extern int inferior_pid;
+
+/* Character array containing an image of the inferior programs' registers. */
+
+extern char registers[];
+
+/* Array of validity bits (one per register). Nonzero at position XXX_REGNUM
+ means that `registers' contains a valid copy of inferior register XXX. */
+
+extern char register_valid[NUM_REGS];
+
+extern void
+clear_proceed_status PARAMS ((void));
+
+extern void
+proceed PARAMS ((CORE_ADDR, int, int));
+
+extern void
+kill_inferior PARAMS ((void));
+
+extern void
+generic_mourn_inferior PARAMS ((void));
+
+extern void
+terminal_ours PARAMS ((void));
+
+extern int run_stack_dummy PARAMS ((CORE_ADDR, char [REGISTER_BYTES]));
+
+extern CORE_ADDR
+read_pc PARAMS ((void));
+
+extern void
+write_pc PARAMS ((CORE_ADDR));
+
+extern CORE_ADDR
+read_sp PARAMS ((void));
+
+extern void
+write_sp PARAMS ((CORE_ADDR));
+
+extern CORE_ADDR
+read_fp PARAMS ((void));
+
+extern void
+write_fp PARAMS ((CORE_ADDR));
+
+extern void
+wait_for_inferior PARAMS ((void));
+
+extern void
+init_wait_for_inferior PARAMS ((void));
+
+extern void
+close_exec_file PARAMS ((void));
+
+extern void
+reopen_exec_file PARAMS ((void));
+
+/* The `resume' routine should only be called in special circumstances.
+ Normally, use `proceed', which handles a lot of bookkeeping. */
+extern void
+resume PARAMS ((int, int));
+
+/* From misc files */
+
+extern void
+store_inferior_registers PARAMS ((int));
+
+extern void
+fetch_inferior_registers PARAMS ((int));
+
+extern void
+solib_create_inferior_hook PARAMS ((void));
+
+extern void
+child_terminal_info PARAMS ((char *, int));
+
+extern void
+term_info PARAMS ((char *, int));
+
+extern void
+terminal_ours_for_output PARAMS ((void));
+
+extern void
+terminal_inferior PARAMS ((void));
+
+extern void
+terminal_init_inferior PARAMS ((void));
+
+/* From infptrace.c */
+
+extern int
+attach PARAMS ((int));
+
+void
+detach PARAMS ((int));
+
+extern void
+child_resume PARAMS ((int, int, int));
+
+#ifndef PTRACE_ARG3_TYPE
+#define PTRACE_ARG3_TYPE int /* Correct definition for most systems. */
+#endif
+
+extern int
+call_ptrace PARAMS ((int, int, PTRACE_ARG3_TYPE, int));
+
+/* From procfs.c */
+
+extern int
+proc_iterate_over_mappings PARAMS ((int (*) (int, CORE_ADDR)));
+
+/* From fork-child.c */
+
+extern void
+fork_inferior PARAMS ((char *, char *, char **,
+ void (*) (void),
+ void (*) (int)));
+
+/* From inflow.c */
+
+extern void
+new_tty_prefork PARAMS ((char *));
+
+extern int gdb_has_a_terminal PARAMS ((void));
+
+/* From infrun.c */
+
+extern void
+start_remote PARAMS ((void));
+
+extern void
+normal_stop PARAMS ((void));
+
+extern int
+signal_stop_state PARAMS ((int));
+
+extern int
+signal_print_state PARAMS ((int));
+
+extern int
+signal_pass_state PARAMS ((int));
+
+/* From infcmd.c */
+
+extern void
+tty_command PARAMS ((char *, int));
+
+extern void
+attach_command PARAMS ((char *, int));
+
+/* Last signal that the inferior received (why it stopped). */
+
+extern int stop_signal;
+
+/* Address at which inferior stopped. */
+
+extern CORE_ADDR stop_pc;
+
+/* Stack frame when program stopped. */
+
+extern FRAME_ADDR stop_frame_address;
+
+/* Chain containing status of breakpoint(s) that we have stopped at. */
+
+extern bpstat stop_bpstat;
+
+/* Flag indicating that a command has proceeded the inferior past the
+ current breakpoint. */
+
+extern int breakpoint_proceeded;
+
+/* Nonzero if stopped due to a step command. */
+
+extern int stop_step;
+
+/* Nonzero if stopped due to completion of a stack dummy routine. */
+
+extern int stop_stack_dummy;
+
+/* Nonzero if program stopped due to a random (unexpected) signal in
+ inferior process. */
+
+extern int stopped_by_random_signal;
+
+/* Range to single step within.
+ If this is nonzero, respond to a single-step signal
+ by continuing to step if the pc is in this range.
+
+ If step_range_start and step_range_end are both 1, it means to step for
+ a single instruction (FIXME: it might clean up wait_for_inferior in a
+ minor way if this were changed to the address of the instruction and
+ that address plus one. But maybe not.). */
+
+extern CORE_ADDR step_range_start; /* Inclusive */
+extern CORE_ADDR step_range_end; /* Exclusive */
+
+/* Stack frame address as of when stepping command was issued.
+ This is how we know when we step into a subroutine call,
+ and how to set the frame for the breakpoint used to step out. */
+
+extern FRAME_ADDR step_frame_address;
+
+/* 1 means step over all subroutine calls.
+ -1 means step over calls to undebuggable functions. */
+
+extern int step_over_calls;
+
+/* If stepping, nonzero means step count is > 1
+ so don't print frame next time inferior stops
+ if it stops due to stepping. */
+
+extern int step_multi;
+
+/* Nonzero means expecting a trap and caller will handle it themselves.
+ It is used after attach, due to attaching to a process;
+ when running in the shell before the child program has been exec'd;
+ and when running some kinds of remote stuff (FIXME?). */
+
+extern int stop_soon_quietly;
+
+/* Nonzero if proceed is being used for a "finish" command or a similar
+ situation when stop_registers should be saved. */
+
+extern int proceed_to_finish;
+
+/* Save register contents here when about to pop a stack dummy frame,
+ if-and-only-if proceed_to_finish is set.
+ Thus this contains the return value from the called function (assuming
+ values are returned in a register). */
+
+extern char stop_registers[REGISTER_BYTES];
+
+/* Nonzero if the child process in inferior_pid was attached rather
+ than forked. */
+
+extern int attach_flag;
+
+/* Sigtramp is a routine that the kernel calls (which then calls the
+ signal handler). On most machines it is a library routine that
+ is linked into the executable.
+
+ This macro, given a program counter value and the name of the
+ function in which that PC resides (which can be null if the
+ name is not known), returns nonzero if the PC and name show
+ that we are in sigtramp.
+
+ On most machines just see if the name is sigtramp (and if we have
+ no name, assume we are not in sigtramp). */
+#if !defined (IN_SIGTRAMP)
+# if defined (SIGTRAMP_START)
+# define IN_SIGTRAMP(pc, name) \
+ ((pc) >= SIGTRAMP_START \
+ && (pc) < SIGTRAMP_END \
+ )
+# else
+# define IN_SIGTRAMP(pc, name) \
+ (name && STREQ ("_sigtramp", name))
+# endif
+#endif
+
+/* Possible values for CALL_DUMMY_LOCATION. */
+#define ON_STACK 1
+#define BEFORE_TEXT_END 2
+#define AFTER_TEXT_END 3
+#define AT_ENTRY_POINT 4
+
+#if !defined (CALL_DUMMY_LOCATION)
+#define CALL_DUMMY_LOCATION ON_STACK
+#endif /* No CALL_DUMMY_LOCATION. */
+
+/* Are we in a call dummy? The code below which allows DECR_PC_AFTER_BREAK
+ below is for infrun.c, which may give the macro a pc without that
+ subtracted out. */
+#if !defined (PC_IN_CALL_DUMMY)
+#if CALL_DUMMY_LOCATION == BEFORE_TEXT_END
+extern CORE_ADDR text_end;
+#define PC_IN_CALL_DUMMY(pc, sp, frame_address) \
+ ((pc) >= text_end - CALL_DUMMY_LENGTH \
+ && (pc) <= text_end + DECR_PC_AFTER_BREAK)
+#endif /* Before text_end. */
+
+#if CALL_DUMMY_LOCATION == AFTER_TEXT_END
+extern CORE_ADDR text_end;
+#define PC_IN_CALL_DUMMY(pc, sp, frame_address) \
+ ((pc) >= text_end \
+ && (pc) <= text_end + CALL_DUMMY_LENGTH + DECR_PC_AFTER_BREAK)
+#endif /* After text_end. */
+
+#if CALL_DUMMY_LOCATION == ON_STACK
+/* Is the PC in a call dummy? SP and FRAME_ADDRESS are the bottom and
+ top of the stack frame which we are checking, where "bottom" and
+ "top" refer to some section of memory which contains the code for
+ the call dummy. Calls to this macro assume that the contents of
+ SP_REGNUM and FP_REGNUM (or the saved values thereof), respectively,
+ are the things to pass.
+
+ This won't work on the 29k, where SP_REGNUM and FP_REGNUM don't
+ have that meaning, but the 29k doesn't use ON_STACK. This could be
+ fixed by generalizing this scheme, perhaps by passing in a frame
+ and adding a few fields, at least on machines which need them for
+ PC_IN_CALL_DUMMY.
+
+ Something simpler, like checking for the stack segment, doesn't work,
+ since various programs (threads implementations, gcc nested function
+ stubs, etc) may either allocate stack frames in another segment, or
+ allocate other kinds of code on the stack. */
+
+#define PC_IN_CALL_DUMMY(pc, sp, frame_address) \
+ ((sp) INNER_THAN (pc) && (frame_address != 0) && (pc) INNER_THAN (frame_address))
+#endif /* On stack. */
+
+#if CALL_DUMMY_LOCATION == AT_ENTRY_POINT
+extern CORE_ADDR
+entry_point_address PARAMS ((void));
+#define PC_IN_CALL_DUMMY(pc, sp, frame_address) \
+ ((pc) >= entry_point_address () \
+ && (pc) <= (entry_point_address () + DECR_PC_AFTER_BREAK))
+#endif /* At entry point. */
+#endif /* No PC_IN_CALL_DUMMY. */
+
+#endif /* !defined (INFERIOR_H) */
diff --git a/gnu/usr.bin/gdb/gdb/inflow.c b/gnu/usr.bin/gdb/gdb/inflow.c
new file mode 100644
index 0000000..be0b43b
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/inflow.c
@@ -0,0 +1,662 @@
+/* Low level interface to ptrace, for GDB when running under Unix.
+ Copyright 1986, 1987, 1989, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "frame.h"
+#include "inferior.h"
+#include "command.h"
+#include "signals.h"
+#include "serial.h"
+#include "terminal.h"
+#include "target.h"
+
+#include <signal.h>
+#include <fcntl.h>
+
+#if !defined (HAVE_TERMIOS) && !defined (HAVE_TERMIO) && !defined (HAVE_SGTTY) && !defined (__GO32__)
+#define HAVE_SGTTY
+#endif
+
+#if defined (HAVE_TERMIOS)
+#include <termios.h>
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_TERMIOS
+#define PROCESS_GROUP_TYPE pid_t
+#endif
+
+#ifdef HAVE_SGTTY
+#ifdef SHORT_PGRP
+/* This is only used for the ultra. Does it have pid_t? */
+#define PROCESS_GROUP_TYPE short
+#else
+#define PROCESS_GROUP_TYPE int
+#endif
+#endif /* sgtty */
+
+static void
+kill_command PARAMS ((char *, int));
+
+static void
+terminal_ours_1 PARAMS ((int));
+
+/* Nonzero if we are debugging an attached outside process
+ rather than an inferior. */
+
+int attach_flag;
+
+
+/* Record terminal status separately for debugger and inferior. */
+
+static serial_t stdin_serial;
+
+/* TTY state for the inferior. We save it whenever the inferior stops, and
+ restore it when it resumes. */
+static serial_ttystate inferior_ttystate;
+
+/* Our own tty state, which we restore every time we need to deal with the
+ terminal. We only set it once, when GDB first starts. The settings of
+ flags which readline saves and restores and unimportant. */
+static serial_ttystate our_ttystate;
+
+/* fcntl flags for us and the inferior. Saved and restored just like
+ {our,inferior}_ttystate. */
+static int tflags_inferior;
+static int tflags_ours;
+
+#ifdef PROCESS_GROUP_TYPE
+/* Process group for us and the inferior. Saved and restored just like
+ {our,inferior}_ttystate. */
+PROCESS_GROUP_TYPE our_process_group;
+PROCESS_GROUP_TYPE inferior_process_group;
+#endif
+
+/* While the inferior is running, we want SIGINT and SIGQUIT to go to the
+ inferior only. If we have job control, that takes care of it. If not,
+ we save our handlers in these two variables and set SIGINT and SIGQUIT
+ to SIG_IGN. */
+static void (*sigint_ours) ();
+static void (*sigquit_ours) ();
+
+/* The name of the tty (from the `tty' command) that we gave to the inferior
+ when it was last started. */
+
+static char *inferior_thisrun_terminal;
+
+/* Nonzero if our terminal settings are in effect. Zero if the
+ inferior's settings are in effect. Ignored if !gdb_has_a_terminal
+ (). */
+
+static int terminal_is_ours;
+
+enum {yes, no, have_not_checked} gdb_has_a_terminal_flag = have_not_checked;
+
+/* Does GDB have a terminal (on stdin)? */
+int
+gdb_has_a_terminal ()
+{
+ switch (gdb_has_a_terminal_flag)
+ {
+ case yes:
+ return 1;
+ case no:
+ return 0;
+ case have_not_checked:
+ /* Get all the current tty settings (including whether we have a tty at
+ all!). Can't do this in _initialize_inflow because SERIAL_FDOPEN
+ won't work until the serial_ops_list is initialized. */
+
+#ifdef F_GETFL
+ tflags_ours = fcntl (0, F_GETFL, 0);
+#endif
+
+ gdb_has_a_terminal_flag = no;
+ stdin_serial = SERIAL_FDOPEN (0);
+ if (stdin_serial != NULL)
+ {
+ our_ttystate = SERIAL_GET_TTY_STATE (stdin_serial);
+
+ if (our_ttystate != NULL)
+ {
+ gdb_has_a_terminal_flag = yes;
+#ifdef HAVE_TERMIOS
+ our_process_group = tcgetpgrp (0);
+#endif
+#ifdef HAVE_SGTTY
+ ioctl (0, TIOCGPGRP, &our_process_group);
+#endif
+ }
+ }
+
+ return gdb_has_a_terminal_flag == yes;
+ }
+}
+
+/* Macro for printing errors from ioctl operations */
+
+#define OOPSY(what) \
+ if (result == -1) \
+ fprintf(stderr, "[%s failed in terminal_inferior: %s]\n", \
+ what, strerror (errno))
+
+static void terminal_ours_1 PARAMS ((int));
+
+/* Initialize the terminal settings we record for the inferior,
+ before we actually run the inferior. */
+
+void
+terminal_init_inferior ()
+{
+ if (gdb_has_a_terminal ())
+ {
+ /* We could just as well copy our_ttystate (if we felt like adding
+ a new function SERIAL_COPY_TTY_STATE). */
+ if (inferior_ttystate)
+ free (inferior_ttystate);
+ inferior_ttystate = SERIAL_GET_TTY_STATE (stdin_serial);
+#ifdef PROCESS_GROUP_TYPE
+ inferior_process_group = inferior_pid;
+#endif
+
+ /* Make sure that next time we call terminal_inferior (which will be
+ before the program runs, as it needs to be), we install the new
+ process group. */
+ terminal_is_ours = 1;
+ }
+}
+
+/* Put the inferior's terminal settings into effect.
+ This is preparation for starting or resuming the inferior. */
+
+void
+terminal_inferior ()
+{
+ if (gdb_has_a_terminal () && terminal_is_ours
+ && inferior_thisrun_terminal == 0)
+ {
+ int result;
+
+#ifdef F_GETFL
+ /* Is there a reason this is being done twice? It happens both
+ places we use F_SETFL, so I'm inclined to think perhaps there
+ is some reason, however perverse. Perhaps not though... */
+ result = fcntl (0, F_SETFL, tflags_inferior);
+ result = fcntl (0, F_SETFL, tflags_inferior);
+ OOPSY ("fcntl F_SETFL");
+#endif
+
+ /* Because we were careful to not change in or out of raw mode in
+ terminal_ours, we will not change in our out of raw mode with
+ this call, so we don't flush any input. */
+ result = SERIAL_SET_TTY_STATE (stdin_serial, inferior_ttystate);
+ OOPSY ("setting tty state");
+
+ if (!job_control)
+ {
+ sigint_ours = (void (*) ()) signal (SIGINT, SIG_IGN);
+ sigquit_ours = (void (*) ()) signal (SIGQUIT, SIG_IGN);
+ }
+
+ /* If attach_flag is set, we don't know whether we are sharing a
+ terminal with the inferior or not. (attaching a process
+ without a terminal is one case where we do not; attaching a
+ process which we ran from the same shell as GDB via `&' is
+ one case where we do, I think (but perhaps this is not
+ `sharing' in the sense that we need to save and restore tty
+ state)). I don't know if there is any way to tell whether we
+ are sharing a terminal. So what we do is to go through all
+ the saving and restoring of the tty state, but ignore errors
+ setting the process group, which will happen if we are not
+ sharing a terminal). */
+
+ if (job_control)
+ {
+#ifdef HAVE_TERMIOS
+ result = tcsetpgrp (0, inferior_process_group);
+ if (!attach_flag)
+ OOPSY ("tcsetpgrp");
+#endif
+
+#ifdef HAVE_SGTTY
+ result = ioctl (0, TIOCSPGRP, &inferior_process_group);
+ if (!attach_flag)
+ OOPSY ("TIOCSPGRP");
+#endif
+ }
+
+ }
+ terminal_is_ours = 0;
+}
+
+/* Put some of our terminal settings into effect,
+ enough to get proper results from our output,
+ but do not change into or out of RAW mode
+ so that no input is discarded.
+
+ After doing this, either terminal_ours or terminal_inferior
+ should be called to get back to a normal state of affairs. */
+
+void
+terminal_ours_for_output ()
+{
+ terminal_ours_1 (1);
+}
+
+/* Put our terminal settings into effect.
+ First record the inferior's terminal settings
+ so they can be restored properly later. */
+
+void
+terminal_ours ()
+{
+ terminal_ours_1 (0);
+}
+
+/* output_only is not used, and should not be used unless we introduce
+ separate terminal_is_ours and terminal_is_ours_for_output
+ flags. */
+
+static void
+terminal_ours_1 (output_only)
+ int output_only;
+{
+ /* Checking inferior_thisrun_terminal is necessary so that
+ if GDB is running in the background, it won't block trying
+ to do the ioctl()'s below. Checking gdb_has_a_terminal
+ avoids attempting all the ioctl's when running in batch. */
+ if (inferior_thisrun_terminal != 0 || gdb_has_a_terminal () == 0)
+ return;
+
+ if (!terminal_is_ours)
+ {
+ /* Ignore this signal since it will happen when we try to set the
+ pgrp. */
+ void (*osigttou) ();
+ int result;
+
+ terminal_is_ours = 1;
+
+#ifdef SIGTTOU
+ if (job_control)
+ osigttou = (void (*) ()) signal (SIGTTOU, SIG_IGN);
+#endif
+
+ if (inferior_ttystate)
+ free (inferior_ttystate);
+ inferior_ttystate = SERIAL_GET_TTY_STATE (stdin_serial);
+#ifdef HAVE_TERMIOS
+ inferior_process_group = tcgetpgrp (0);
+#endif
+#ifdef HAVE_SGTTY
+ ioctl (0, TIOCGPGRP, &inferior_process_group);
+#endif
+
+ /* Here we used to set ICANON in our ttystate, but I believe this
+ was an artifact from before when we used readline. Readline sets
+ the tty state when it needs to. */
+
+ /* Set tty state to our_ttystate. We don't change in our out of raw
+ mode, to avoid flushing input. We need to do the same thing
+ regardless of output_only, because we don't have separate
+ terminal_is_ours and terminal_is_ours_for_output flags. It's OK,
+ though, since readline will deal with raw mode when/if it needs to.
+ */
+ SERIAL_NOFLUSH_SET_TTY_STATE (stdin_serial, our_ttystate,
+ inferior_ttystate);
+
+ if (job_control)
+ {
+#ifdef HAVE_TERMIOS
+ result = tcsetpgrp (0, our_process_group);
+#if 0
+ /* This fails on Ultrix with EINVAL if you run the testsuite
+ in the background with nohup, and then log out. GDB never
+ used to check for an error here, so perhaps there are other
+ such situations as well. */
+ if (result == -1)
+ fprintf (stderr, "[tcsetpgrp failed in terminal_ours: %s]\n",
+ strerror (errno));
+#endif
+#endif /* termios */
+
+#ifdef HAVE_SGTTY
+ result = ioctl (0, TIOCSPGRP, &our_process_group);
+#endif
+ }
+
+#ifdef SIGTTOU
+ if (job_control)
+ signal (SIGTTOU, osigttou);
+#endif
+
+ if (!job_control)
+ {
+ signal (SIGINT, sigint_ours);
+ signal (SIGQUIT, sigquit_ours);
+ }
+
+#ifdef F_GETFL
+ tflags_inferior = fcntl (0, F_GETFL, 0);
+
+ /* Is there a reason this is being done twice? It happens both
+ places we use F_SETFL, so I'm inclined to think perhaps there
+ is some reason, however perverse. Perhaps not though... */
+ result = fcntl (0, F_SETFL, tflags_ours);
+ result = fcntl (0, F_SETFL, tflags_ours);
+#endif
+
+ result = result; /* lint */
+ }
+}
+
+/* ARGSUSED */
+void
+term_info (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ target_terminal_info (arg, from_tty);
+}
+
+/* ARGSUSED */
+void
+child_terminal_info (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ if (!gdb_has_a_terminal ())
+ {
+ printf_filtered ("This GDB does not control a terminal.\n");
+ return;
+ }
+
+ printf_filtered ("Inferior's terminal status (currently saved by GDB):\n");
+
+ /* First the fcntl flags. */
+ {
+ int flags;
+
+ flags = tflags_inferior;
+
+ printf_filtered ("File descriptor flags = ");
+
+#ifndef O_ACCMODE
+#define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
+#endif
+ /* (O_ACCMODE) parens are to avoid Ultrix header file bug */
+ switch (flags & (O_ACCMODE))
+ {
+ case O_RDONLY: printf_filtered ("O_RDONLY"); break;
+ case O_WRONLY: printf_filtered ("O_WRONLY"); break;
+ case O_RDWR: printf_filtered ("O_RDWR"); break;
+ }
+ flags &= ~(O_ACCMODE);
+
+#ifdef O_NONBLOCK
+ if (flags & O_NONBLOCK)
+ printf_filtered (" | O_NONBLOCK");
+ flags &= ~O_NONBLOCK;
+#endif
+
+#if defined (O_NDELAY)
+ /* If O_NDELAY and O_NONBLOCK are defined to the same thing, we will
+ print it as O_NONBLOCK, which is good cause that is what POSIX
+ has, and the flag will already be cleared by the time we get here. */
+ if (flags & O_NDELAY)
+ printf_filtered (" | O_NDELAY");
+ flags &= ~O_NDELAY;
+#endif
+
+ if (flags & O_APPEND)
+ printf_filtered (" | O_APPEND");
+ flags &= ~O_APPEND;
+
+#if defined (O_BINARY)
+ if (flags & O_BINARY)
+ printf_filtered (" | O_BINARY");
+ flags &= ~O_BINARY;
+#endif
+
+ if (flags)
+ printf_filtered (" | 0x%x", flags);
+ printf_filtered ("\n");
+ }
+
+#ifdef PROCESS_GROUP_TYPE
+ printf_filtered ("Process group = %d\n", inferior_process_group);
+#endif
+
+ SERIAL_PRINT_TTY_STATE (stdin_serial, inferior_ttystate);
+}
+
+/* NEW_TTY_PREFORK is called before forking a new child process,
+ so we can record the state of ttys in the child to be formed.
+ TTYNAME is null if we are to share the terminal with gdb;
+ or points to a string containing the name of the desired tty.
+
+ NEW_TTY is called in new child processes under Unix, which will
+ become debugger target processes. This actually switches to
+ the terminal specified in the NEW_TTY_PREFORK call. */
+
+void
+new_tty_prefork (ttyname)
+ char *ttyname;
+{
+ /* Save the name for later, for determining whether we and the child
+ are sharing a tty. */
+ inferior_thisrun_terminal = ttyname;
+}
+
+void
+new_tty ()
+{
+ register int tty;
+
+ if (inferior_thisrun_terminal == 0)
+ return;
+#if !defined(__GO32__)
+#ifdef TIOCNOTTY
+ /* Disconnect the child process from our controlling terminal. On some
+ systems (SVR4 for example), this may cause a SIGTTOU, so temporarily
+ ignore SIGTTOU. */
+ tty = open("/dev/tty", O_RDWR);
+ if (tty > 0)
+ {
+ void (*osigttou) ();
+
+ osigttou = (void (*)()) signal(SIGTTOU, SIG_IGN);
+ ioctl(tty, TIOCNOTTY, 0);
+ close(tty);
+ signal(SIGTTOU, osigttou);
+ }
+#endif
+
+ /* Now open the specified new terminal. */
+
+#ifdef USE_O_NOCTTY
+ tty = open(inferior_thisrun_terminal, O_RDWR | O_NOCTTY);
+#else
+ tty = open(inferior_thisrun_terminal, O_RDWR);
+#endif
+ if (tty == -1)
+ {
+ print_sys_errmsg (inferior_thisrun_terminal, errno);
+ _exit(1);
+ }
+
+ /* Avoid use of dup2; doesn't exist on all systems. */
+ if (tty != 0)
+ { close (0); dup (tty); }
+ if (tty != 1)
+ { close (1); dup (tty); }
+ if (tty != 2)
+ { close (2); dup (tty); }
+ if (tty > 2)
+ close(tty);
+#endif /* !go32 */
+}
+
+/* Kill the inferior process. Make us have no inferior. */
+
+/* ARGSUSED */
+static void
+kill_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ /* Shouldn't this be target_has_execution? FIXME. */
+ if (inferior_pid == 0)
+ error ("The program is not being run.");
+ if (!query ("Kill the program being debugged? "))
+ error ("Not confirmed.");
+ target_kill ();
+
+ init_thread_list(); /* Destroy thread info */
+
+ /* Killing off the inferior can leave us with a core file. If so,
+ print the state we are left in. */
+ if (target_has_stack) {
+ printf_filtered ("In %s,\n", current_target->to_longname);
+ if (selected_frame == NULL)
+ fputs_filtered ("No selected stack frame.\n", stdout);
+ else
+ print_stack_frame (selected_frame, selected_frame_level, 1);
+ }
+}
+
+/* The inferior process has died. Long live the inferior! */
+
+void
+generic_mourn_inferior ()
+{
+ inferior_pid = 0;
+ attach_flag = 0;
+ breakpoint_init_inferior ();
+ registers_changed ();
+
+#ifdef CLEAR_DEFERRED_STORES
+ /* Delete any pending stores to the inferior... */
+ CLEAR_DEFERRED_STORES;
+#endif
+
+ reopen_exec_file ();
+ reinit_frame_cache ();
+
+ /* It is confusing to the user for ignore counts to stick around
+ from previous runs of the inferior. So clear them. */
+ breakpoint_clear_ignore_counts ();
+}
+
+/* Call set_sigint_trap when you need to pass a signal on to an attached
+ process when handling SIGINT */
+
+/* ARGSUSED */
+static void
+pass_signal (signo)
+ int signo;
+{
+ kill (inferior_pid, SIGINT);
+}
+
+static void (*osig)();
+
+void
+set_sigint_trap()
+{
+ osig = (void (*) ()) signal (SIGINT, pass_signal);
+}
+
+void
+clear_sigint_trap()
+{
+ signal (SIGINT, osig);
+}
+
+
+int job_control;
+
+/* This is here because this is where we figure out whether we (probably)
+ have job control. Just using job_control only does part of it because
+ setpgid or setpgrp might not exist on a system without job control.
+ It might be considered misplaced (on the other hand, process groups and
+ job control are closely related to ttys).
+
+ For a more clean implementation, in libiberty, put a setpgid which merely
+ calls setpgrp and a setpgrp which does nothing (any system with job control
+ will have one or the other). */
+int
+gdb_setpgid ()
+{
+ int retval = 0;
+ if (job_control)
+ {
+#if defined (NEED_POSIX_SETPGID) || defined (HAVE_TERMIOS)
+ /* Do all systems with termios have setpgid? I hope so. */
+ /* setpgid (0, 0) is supposed to work and mean the same thing as
+ this, but on Ultrix 4.2A it fails with EPERM (and
+ setpgid (getpid (), getpid ()) succeeds). */
+ retval = setpgid (getpid (), getpid ());
+#else
+#if defined (TIOCGPGRP)
+#if defined(USG) && !defined(SETPGRP_ARGS)
+ retval = setpgrp ();
+#else
+ retval = setpgrp (getpid (), getpid ());
+#endif /* USG */
+#endif /* TIOCGPGRP. */
+#endif /* NEED_POSIX_SETPGID */
+ }
+ return retval;
+}
+
+void
+_initialize_inflow ()
+{
+ add_info ("terminal", term_info,
+ "Print inferior's saved terminal status.");
+
+ add_com ("kill", class_run, kill_command,
+ "Kill execution of program being debugged.");
+
+ inferior_pid = 0;
+
+ terminal_is_ours = 1;
+
+ /* OK, figure out whether we have job control. If neither termios nor
+ sgtty (i.e. termio or go32), leave job_control 0. */
+
+#if defined (HAVE_TERMIOS)
+ /* Do all systems with termios have the POSIX way of identifying job
+ control? I hope so. */
+#ifdef _POSIX_JOB_CONTROL
+ job_control = 1;
+#else
+ job_control = sysconf (_SC_JOB_CONTROL);
+#endif
+#endif /* termios */
+
+#ifdef HAVE_SGTTY
+#ifdef TIOCGPGRP
+ job_control = 1;
+#else
+ job_control = 0;
+#endif /* TIOCGPGRP */
+#endif /* sgtty */
+}
diff --git a/gnu/usr.bin/gdb/gdb/infptrace.c b/gnu/usr.bin/gdb/gdb/infptrace.c
new file mode 100644
index 0000000..a152d67
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/infptrace.c
@@ -0,0 +1,436 @@
+/* Low level Unix child interface to ptrace, for GDB when running under Unix.
+ Copyright 1988, 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "frame.h"
+#include "inferior.h"
+#include "target.h"
+
+#ifdef USG
+#include <sys/types.h>
+#endif
+
+#include <sys/param.h>
+#include <sys/dir.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+
+#ifndef NO_PTRACE_H
+#ifdef PTRACE_IN_WRONG_PLACE
+#include <ptrace.h>
+#else
+#include <sys/ptrace.h>
+#endif
+#endif /* NO_PTRACE_H */
+
+#if !defined (PT_KILL)
+#define PT_KILL 8
+#endif
+
+#if !defined (PT_STEP)
+#define PT_STEP 9
+#define PT_CONTINUE 7
+#define PT_READ_U 3
+#define PT_WRITE_U 6
+#define PT_READ_I 1
+#define PT_READ_D 2
+#define PT_WRITE_I 4
+#define PT_WRITE_D 5
+#endif /* No PT_STEP. */
+
+#ifndef PT_ATTACH
+#define PT_ATTACH PTRACE_ATTACH
+#endif
+#ifndef PT_DETACH
+#define PT_DETACH PTRACE_DETACH
+#endif
+
+#include "gdbcore.h"
+#ifndef NO_SYS_FILE
+#include <sys/file.h>
+#endif
+#if 0
+/* Don't think this is used anymore. On the sequent (not sure whether it's
+ dynix or ptx or both), it is included unconditionally by sys/user.h and
+ not protected against multiple inclusion. */
+#include <sys/stat.h>
+#endif
+
+#if !defined (FETCH_INFERIOR_REGISTERS)
+#include <sys/user.h> /* Probably need to poke the user structure */
+#if defined (KERNEL_U_ADDR_BSD)
+#include <a.out.h> /* For struct nlist */
+#endif /* KERNEL_U_ADDR_BSD. */
+#endif /* !FETCH_INFERIOR_REGISTERS */
+
+
+/* This function simply calls ptrace with the given arguments.
+ It exists so that all calls to ptrace are isolated in this
+ machine-dependent file. */
+int
+call_ptrace (request, pid, addr, data)
+ int request, pid;
+ PTRACE_ARG3_TYPE addr;
+ int data;
+{
+ return ptrace (request, pid, addr, data
+#if defined (FIVE_ARG_PTRACE)
+ /* Deal with HPUX 8.0 braindamage. We never use the
+ calls which require the fifth argument. */
+ , 0
+#endif
+ );
+}
+
+#if defined (DEBUG_PTRACE) || defined (FIVE_ARG_PTRACE)
+/* For the rest of the file, use an extra level of indirection */
+/* This lets us breakpoint usefully on call_ptrace. */
+#define ptrace call_ptrace
+#endif
+
+void
+kill_inferior ()
+{
+ if (inferior_pid == 0)
+ return;
+ /* ptrace PT_KILL only works if process is stopped!!! So stop it with
+ a real signal first, if we can. */
+ kill (inferior_pid, SIGKILL);
+ ptrace (PT_KILL, inferior_pid, (PTRACE_ARG3_TYPE) 0, 0);
+ wait ((int *)0);
+ target_mourn_inferior ();
+}
+
+/* Resume execution of the inferior process.
+ If STEP is nonzero, single-step it.
+ If SIGNAL is nonzero, give it that signal. */
+
+void
+child_resume (pid, step, signal)
+ int pid;
+ int step;
+ int signal;
+{
+ errno = 0;
+
+ if (pid == -1)
+ pid = inferior_pid;
+
+ /* An address of (PTRACE_ARG3_TYPE)1 tells ptrace to continue from where
+ it was. (If GDB wanted it to start some other way, we have already
+ written a new PC value to the child.)
+
+ If this system does not support PT_STEP, a higher level function will
+ have called single_step() to transmute the step request into a
+ continue request (by setting breakpoints on all possible successor
+ instructions), so we don't have to worry about that here. */
+
+ if (step)
+ ptrace (PT_STEP, pid, (PTRACE_ARG3_TYPE) 1, signal);
+ else
+ ptrace (PT_CONTINUE, pid, (PTRACE_ARG3_TYPE) 1, signal);
+
+ if (errno)
+ perror_with_name ("ptrace");
+}
+
+#ifdef ATTACH_DETACH
+/* Start debugging the process whose number is PID. */
+int
+attach (pid)
+ int pid;
+{
+ errno = 0;
+ ptrace (PT_ATTACH, pid, (PTRACE_ARG3_TYPE) 0, 0);
+ if (errno)
+ perror_with_name ("ptrace");
+ attach_flag = 1;
+ return pid;
+}
+
+/* Stop debugging the process whose number is PID
+ and continue it with signal number SIGNAL.
+ SIGNAL = 0 means just continue it. */
+
+void
+detach (signal)
+ int signal;
+{
+ errno = 0;
+ ptrace (PT_DETACH, inferior_pid, (PTRACE_ARG3_TYPE) 1, signal);
+ if (errno)
+ perror_with_name ("ptrace");
+ attach_flag = 0;
+}
+#endif /* ATTACH_DETACH */
+
+/* Default the type of the ptrace transfer to int. */
+#ifndef PTRACE_XFER_TYPE
+#define PTRACE_XFER_TYPE int
+#endif
+
+#if !defined (FETCH_INFERIOR_REGISTERS)
+
+/* KERNEL_U_ADDR is the amount to subtract from u.u_ar0
+ to get the offset in the core file of the register values. */
+#if defined (KERNEL_U_ADDR_BSD)
+/* Get kernel_u_addr using BSD-style nlist(). */
+CORE_ADDR kernel_u_addr;
+
+void
+_initialize_kernel_u_addr ()
+{
+ struct nlist names[2];
+
+ names[0].n_un.n_name = "_u";
+ names[1].n_un.n_name = NULL;
+ if (nlist ("/vmunix", names) == 0)
+ kernel_u_addr = names[0].n_value;
+ else
+ fatal ("Unable to get kernel u area address.");
+}
+#endif /* KERNEL_U_ADDR_BSD. */
+
+#if !defined (offsetof)
+#define offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE *)0)->MEMBER)
+#endif
+
+/* U_REGS_OFFSET is the offset of the registers within the u area. */
+#if !defined (U_REGS_OFFSET)
+#define U_REGS_OFFSET \
+ ptrace (PT_READ_U, inferior_pid, \
+ (PTRACE_ARG3_TYPE) (offsetof (struct user, u_ar0)), 0) \
+ - KERNEL_U_ADDR
+#endif
+
+/* Registers we shouldn't try to fetch. */
+#if !defined (CANNOT_FETCH_REGISTER)
+#define CANNOT_FETCH_REGISTER(regno) 0
+#endif
+
+/* Fetch one register. */
+
+static void
+fetch_register (regno)
+ int regno;
+{
+ register unsigned int regaddr;
+ char buf[MAX_REGISTER_RAW_SIZE];
+ char mess[128]; /* For messages */
+ register int i;
+
+ /* Offset of registers within the u area. */
+ unsigned int offset;
+
+ if (CANNOT_FETCH_REGISTER (regno))
+ {
+ memset (buf, '\0', REGISTER_RAW_SIZE (regno)); /* Supply zeroes */
+ supply_register (regno, buf);
+ return;
+ }
+
+ offset = U_REGS_OFFSET;
+
+ regaddr = register_addr (regno, offset);
+ for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (PTRACE_XFER_TYPE))
+ {
+ errno = 0;
+ *(PTRACE_XFER_TYPE *) &buf[i] = ptrace (PT_READ_U, inferior_pid,
+ (PTRACE_ARG3_TYPE) regaddr, 0);
+ regaddr += sizeof (PTRACE_XFER_TYPE);
+ if (errno != 0)
+ {
+ sprintf (mess, "reading register %s (#%d)", reg_names[regno], regno);
+ perror_with_name (mess);
+ }
+ }
+ supply_register (regno, buf);
+}
+
+
+/* Fetch all registers, or just one, from the child process. */
+
+void
+fetch_inferior_registers (regno)
+ int regno;
+{
+ if (regno == -1)
+ for (regno = 0; regno < NUM_REGS; regno++)
+ fetch_register (regno);
+ else
+ fetch_register (regno);
+}
+
+/* Registers we shouldn't try to store. */
+#if !defined (CANNOT_STORE_REGISTER)
+#define CANNOT_STORE_REGISTER(regno) 0
+#endif
+
+/* Store our register values back into the inferior.
+ If REGNO is -1, do this for all registers.
+ Otherwise, REGNO specifies which register (so we can save time). */
+
+void
+store_inferior_registers (regno)
+ int regno;
+{
+ register unsigned int regaddr;
+ char buf[80];
+ register int i;
+
+ unsigned int offset = U_REGS_OFFSET;
+
+ if (regno >= 0)
+ {
+ regaddr = register_addr (regno, offset);
+ for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof(PTRACE_XFER_TYPE))
+ {
+ errno = 0;
+ ptrace (PT_WRITE_U, inferior_pid, (PTRACE_ARG3_TYPE) regaddr,
+ *(PTRACE_XFER_TYPE *) &registers[REGISTER_BYTE (regno) + i]);
+ if (errno != 0)
+ {
+ sprintf (buf, "writing register number %d(%d)", regno, i);
+ perror_with_name (buf);
+ }
+ regaddr += sizeof(PTRACE_XFER_TYPE);
+ }
+ }
+ else
+ {
+ for (regno = 0; regno < NUM_REGS; regno++)
+ {
+ if (CANNOT_STORE_REGISTER (regno))
+ continue;
+ regaddr = register_addr (regno, offset);
+ for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof(PTRACE_XFER_TYPE))
+ {
+ errno = 0;
+ ptrace (PT_WRITE_U, inferior_pid, (PTRACE_ARG3_TYPE) regaddr,
+ *(PTRACE_XFER_TYPE *) &registers[REGISTER_BYTE (regno) + i]);
+ if (errno != 0)
+ {
+ sprintf (buf, "writing register number %d(%d)", regno, i);
+ perror_with_name (buf);
+ }
+ regaddr += sizeof(PTRACE_XFER_TYPE);
+ }
+ }
+ }
+}
+#endif /* !defined (FETCH_INFERIOR_REGISTERS). */
+
+/* NOTE! I tried using PTRACE_READDATA, etc., to read and write memory
+ in the NEW_SUN_PTRACE case.
+ It ought to be straightforward. But it appears that writing did
+ not write the data that I specified. I cannot understand where
+ it got the data that it actually did write. */
+
+/* Copy LEN bytes to or from inferior's memory starting at MEMADDR
+ to debugger memory starting at MYADDR. Copy to inferior if
+ WRITE is nonzero.
+
+ Returns the length copied, which is either the LEN argument or zero.
+ This xfer function does not do partial moves, since child_ops
+ doesn't allow memory operations to cross below us in the target stack
+ anyway. */
+
+int
+child_xfer_memory (memaddr, myaddr, len, write, target)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+ int write;
+ struct target_ops *target; /* ignored */
+{
+ register int i;
+ /* Round starting address down to longword boundary. */
+ register CORE_ADDR addr = memaddr & - sizeof (PTRACE_XFER_TYPE);
+ /* Round ending address up; get number of longwords that makes. */
+ register int count
+ = (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1)
+ / sizeof (PTRACE_XFER_TYPE);
+ /* Allocate buffer of that many longwords. */
+ register PTRACE_XFER_TYPE *buffer
+ = (PTRACE_XFER_TYPE *) alloca (count * sizeof (PTRACE_XFER_TYPE));
+
+ if (write)
+ {
+ /* Fill start and end extra bytes of buffer with existing memory data. */
+
+ if (addr != memaddr || len < (int) sizeof (PTRACE_XFER_TYPE)) {
+ /* Need part of initial word -- fetch it. */
+ buffer[0] = ptrace (PT_READ_I, inferior_pid, (PTRACE_ARG3_TYPE) addr,
+ 0);
+ }
+
+ if (count > 1) /* FIXME, avoid if even boundary */
+ {
+ buffer[count - 1]
+ = ptrace (PT_READ_I, inferior_pid,
+ ((PTRACE_ARG3_TYPE)
+ (addr + (count - 1) * sizeof (PTRACE_XFER_TYPE))),
+ 0);
+ }
+
+ /* Copy data to be written over corresponding part of buffer */
+
+ memcpy ((char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)),
+ myaddr,
+ len);
+
+ /* Write the entire buffer. */
+
+ for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE))
+ {
+ errno = 0;
+ ptrace (PT_WRITE_D, inferior_pid, (PTRACE_ARG3_TYPE) addr,
+ buffer[i]);
+ if (errno)
+ {
+ /* Using the appropriate one (I or D) is necessary for
+ Gould NP1, at least. */
+ errno = 0;
+ ptrace (PT_WRITE_I, inferior_pid, (PTRACE_ARG3_TYPE) addr,
+ buffer[i]);
+ }
+ if (errno)
+ return 0;
+ }
+ }
+ else
+ {
+ /* Read all the longwords */
+ for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE))
+ {
+ errno = 0;
+ buffer[i] = ptrace (PT_READ_I, inferior_pid,
+ (PTRACE_ARG3_TYPE) addr, 0);
+ if (errno)
+ return 0;
+ QUIT;
+ }
+
+ /* Copy appropriate bytes out of the buffer. */
+ memcpy (myaddr,
+ (char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)),
+ len);
+ }
+ return len;
+}
diff --git a/gnu/usr.bin/gdb/gdb/infrun.c b/gnu/usr.bin/gdb/gdb/infrun.c
new file mode 100644
index 0000000..9266c31
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/infrun.c
@@ -0,0 +1,1848 @@
+/* Target-struct-independent code to start (run) and stop an inferior process.
+ Copyright 1986, 1987, 1988, 1989, 1991, 1992, 1993
+ Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include <string.h>
+#include <ctype.h>
+#include "symtab.h"
+#include "frame.h"
+#include "inferior.h"
+#include "breakpoint.h"
+#include "wait.h"
+#include "gdbcore.h"
+#include "gdbcmd.h"
+#include "target.h"
+
+#include <signal.h>
+
+/* unistd.h is needed to #define X_OK */
+#ifdef USG
+#include <unistd.h>
+#else
+#include <sys/file.h>
+#endif
+
+/* Prototypes for local functions */
+
+static void
+signals_info PARAMS ((char *, int));
+
+static void
+handle_command PARAMS ((char *, int));
+
+static void
+sig_print_info PARAMS ((int));
+
+static void
+sig_print_header PARAMS ((void));
+
+static void
+resume_cleanups PARAMS ((int));
+
+static int
+hook_stop_stub PARAMS ((char *));
+
+/* GET_LONGJMP_TARGET returns the PC at which longjmp() will resume the
+ program. It needs to examine the jmp_buf argument and extract the PC
+ from it. The return value is non-zero on success, zero otherwise. */
+#ifndef GET_LONGJMP_TARGET
+#define GET_LONGJMP_TARGET(PC_ADDR) 0
+#endif
+
+
+/* Some machines have trampoline code that sits between function callers
+ and the actual functions themselves. If this machine doesn't have
+ such things, disable their processing. */
+#ifndef SKIP_TRAMPOLINE_CODE
+#define SKIP_TRAMPOLINE_CODE(pc) 0
+#endif
+
+/* For SVR4 shared libraries, each call goes through a small piece of
+ trampoline code in the ".init" section. IN_SOLIB_TRAMPOLINE evaluates
+ to nonzero if we are current stopped in one of these. */
+#ifndef IN_SOLIB_TRAMPOLINE
+#define IN_SOLIB_TRAMPOLINE(pc,name) 0
+#endif
+
+/* On some systems, the PC may be left pointing at an instruction that won't
+ actually be executed. This is usually indicated by a bit in the PSW. If
+ we find ourselves in such a state, then we step the target beyond the
+ nullified instruction before returning control to the user so as to avoid
+ confusion. */
+
+#ifndef INSTRUCTION_NULLIFIED
+#define INSTRUCTION_NULLIFIED 0
+#endif
+
+/* Tables of how to react to signals; the user sets them. */
+
+static unsigned char *signal_stop;
+static unsigned char *signal_print;
+static unsigned char *signal_program;
+
+#define SET_SIGS(nsigs,sigs,flags) \
+ do { \
+ int signum = (nsigs); \
+ while (signum-- > 0) \
+ if ((sigs)[signum]) \
+ (flags)[signum] = 1; \
+ } while (0)
+
+#define UNSET_SIGS(nsigs,sigs,flags) \
+ do { \
+ int signum = (nsigs); \
+ while (signum-- > 0) \
+ if ((sigs)[signum]) \
+ (flags)[signum] = 0; \
+ } while (0)
+
+
+/* Command list pointer for the "stop" placeholder. */
+
+static struct cmd_list_element *stop_command;
+
+/* Nonzero if breakpoints are now inserted in the inferior. */
+
+static int breakpoints_inserted;
+
+/* Function inferior was in as of last step command. */
+
+static struct symbol *step_start_function;
+
+/* Nonzero if we are expecting a trace trap and should proceed from it. */
+
+static int trap_expected;
+
+/* Nonzero if the next time we try to continue the inferior, it will
+ step one instruction and generate a spurious trace trap.
+ This is used to compensate for a bug in HP-UX. */
+
+static int trap_expected_after_continue;
+
+/* Nonzero means expecting a trace trap
+ and should stop the inferior and return silently when it happens. */
+
+int stop_after_trap;
+
+/* Nonzero means expecting a trap and caller will handle it themselves.
+ It is used after attach, due to attaching to a process;
+ when running in the shell before the child program has been exec'd;
+ and when running some kinds of remote stuff (FIXME?). */
+
+int stop_soon_quietly;
+
+/* Nonzero if proceed is being used for a "finish" command or a similar
+ situation when stop_registers should be saved. */
+
+int proceed_to_finish;
+
+/* Save register contents here when about to pop a stack dummy frame,
+ if-and-only-if proceed_to_finish is set.
+ Thus this contains the return value from the called function (assuming
+ values are returned in a register). */
+
+char stop_registers[REGISTER_BYTES];
+
+/* Nonzero if program stopped due to error trying to insert breakpoints. */
+
+static int breakpoints_failed;
+
+/* Nonzero after stop if current stack frame should be printed. */
+
+static int stop_print_frame;
+
+#ifdef NO_SINGLE_STEP
+extern int one_stepped; /* From machine dependent code */
+extern void single_step (); /* Same. */
+#endif /* NO_SINGLE_STEP */
+
+
+/* Things to clean up if we QUIT out of resume (). */
+/* ARGSUSED */
+static void
+resume_cleanups (arg)
+ int arg;
+{
+ normal_stop ();
+}
+
+/* Resume the inferior, but allow a QUIT. This is useful if the user
+ wants to interrupt some lengthy single-stepping operation
+ (for child processes, the SIGINT goes to the inferior, and so
+ we get a SIGINT random_signal, but for remote debugging and perhaps
+ other targets, that's not true).
+
+ STEP nonzero if we should step (zero to continue instead).
+ SIG is the signal to give the inferior (zero for none). */
+void
+resume (step, sig)
+ int step;
+ int sig;
+{
+ struct cleanup *old_cleanups = make_cleanup (resume_cleanups, 0);
+ QUIT;
+
+#ifdef CANNOT_STEP_BREAKPOINT
+ /* Most targets can step a breakpoint instruction, thus executing it
+ normally. But if this one cannot, just continue and we will hit
+ it anyway. */
+ if (step && breakpoints_inserted && breakpoint_here_p (read_pc ()))
+ step = 0;
+#endif
+
+#ifdef NO_SINGLE_STEP
+ if (step) {
+ single_step(sig); /* Do it the hard way, w/temp breakpoints */
+ step = 0; /* ...and don't ask hardware to do it. */
+ }
+#endif
+
+ /* Handle any optimized stores to the inferior NOW... */
+#ifdef DO_DEFERRED_STORES
+ DO_DEFERRED_STORES;
+#endif
+
+ /* Install inferior's terminal modes. */
+ target_terminal_inferior ();
+
+ target_resume (-1, step, sig);
+ discard_cleanups (old_cleanups);
+}
+
+
+/* Clear out all variables saying what to do when inferior is continued.
+ First do this, then set the ones you want, then call `proceed'. */
+
+void
+clear_proceed_status ()
+{
+ trap_expected = 0;
+ step_range_start = 0;
+ step_range_end = 0;
+ step_frame_address = 0;
+ step_over_calls = -1;
+ stop_after_trap = 0;
+ stop_soon_quietly = 0;
+ proceed_to_finish = 0;
+ breakpoint_proceeded = 1; /* We're about to proceed... */
+
+ /* Discard any remaining commands or status from previous stop. */
+ bpstat_clear (&stop_bpstat);
+}
+
+/* Basic routine for continuing the program in various fashions.
+
+ ADDR is the address to resume at, or -1 for resume where stopped.
+ SIGGNAL is the signal to give it, or 0 for none,
+ or -1 for act according to how it stopped.
+ STEP is nonzero if should trap after one instruction.
+ -1 means return after that and print nothing.
+ You should probably set various step_... variables
+ before calling here, if you are stepping.
+
+ You should call clear_proceed_status before calling proceed. */
+
+void
+proceed (addr, siggnal, step)
+ CORE_ADDR addr;
+ int siggnal;
+ int step;
+{
+ int oneproc = 0;
+
+ if (step > 0)
+ step_start_function = find_pc_function (read_pc ());
+ if (step < 0)
+ stop_after_trap = 1;
+
+ if (addr == (CORE_ADDR)-1)
+ {
+ /* If there is a breakpoint at the address we will resume at,
+ step one instruction before inserting breakpoints
+ so that we do not stop right away. */
+
+ if (breakpoint_here_p (read_pc ()))
+ oneproc = 1;
+ }
+ else
+ write_pc (addr);
+
+ if (trap_expected_after_continue)
+ {
+ /* If (step == 0), a trap will be automatically generated after
+ the first instruction is executed. Force step one
+ instruction to clear this condition. This should not occur
+ if step is nonzero, but it is harmless in that case. */
+ oneproc = 1;
+ trap_expected_after_continue = 0;
+ }
+
+ if (oneproc)
+ /* We will get a trace trap after one instruction.
+ Continue it automatically and insert breakpoints then. */
+ trap_expected = 1;
+ else
+ {
+ int temp = insert_breakpoints ();
+ if (temp)
+ {
+ print_sys_errmsg ("ptrace", temp);
+ error ("Cannot insert breakpoints.\n\
+The same program may be running in another process.");
+ }
+ breakpoints_inserted = 1;
+ }
+
+ if (siggnal >= 0)
+ stop_signal = siggnal;
+ /* If this signal should not be seen by program,
+ give it zero. Used for debugging signals. */
+ else if (stop_signal < NSIG && !signal_program[stop_signal])
+ stop_signal= 0;
+
+ /* Resume inferior. */
+ resume (oneproc || step || bpstat_should_step (), stop_signal);
+
+ /* Wait for it to stop (if not standalone)
+ and in any case decode why it stopped, and act accordingly. */
+
+ wait_for_inferior ();
+ normal_stop ();
+}
+
+/* Record the pc and sp of the program the last time it stopped.
+ These are just used internally by wait_for_inferior, but need
+ to be preserved over calls to it and cleared when the inferior
+ is started. */
+static CORE_ADDR prev_pc;
+static CORE_ADDR prev_sp;
+static CORE_ADDR prev_func_start;
+static char *prev_func_name;
+
+
+/* Start remote-debugging of a machine over a serial link. */
+
+void
+start_remote ()
+{
+ init_wait_for_inferior ();
+ clear_proceed_status ();
+ stop_soon_quietly = 1;
+ trap_expected = 0;
+ wait_for_inferior ();
+ normal_stop ();
+}
+
+/* Initialize static vars when a new inferior begins. */
+
+void
+init_wait_for_inferior ()
+{
+ /* These are meaningless until the first time through wait_for_inferior. */
+ prev_pc = 0;
+ prev_sp = 0;
+ prev_func_start = 0;
+ prev_func_name = NULL;
+
+ trap_expected_after_continue = 0;
+ breakpoints_inserted = 0;
+ breakpoint_init_inferior ();
+ stop_signal = 0; /* Don't confuse first call to proceed(). */
+}
+
+static void
+delete_breakpoint_current_contents (arg)
+ PTR arg;
+{
+ struct breakpoint **breakpointp = (struct breakpoint **)arg;
+ if (*breakpointp != NULL)
+ delete_breakpoint (*breakpointp);
+}
+
+/* Wait for control to return from inferior to debugger.
+ If inferior gets a signal, we may decide to start it up again
+ instead of returning. That is why there is a loop in this function.
+ When this function actually returns it means the inferior
+ should be left stopped and GDB should read more commands. */
+
+void
+wait_for_inferior ()
+{
+ struct cleanup *old_cleanups;
+ WAITTYPE w;
+ int another_trap;
+ int random_signal;
+ CORE_ADDR stop_sp = 0;
+ CORE_ADDR stop_func_start;
+ char *stop_func_name;
+ CORE_ADDR prologue_pc = 0, tmp;
+ struct symtab_and_line sal;
+ int remove_breakpoints_on_following_step = 0;
+ int current_line;
+ int handling_longjmp = 0; /* FIXME */
+ struct breakpoint *step_resume_breakpoint = NULL;
+ int pid;
+
+ old_cleanups = make_cleanup (delete_breakpoint_current_contents,
+ &step_resume_breakpoint);
+ sal = find_pc_line(prev_pc, 0);
+ current_line = sal.line;
+
+ /* Are we stepping? */
+#define CURRENTLY_STEPPING() ((step_resume_breakpoint == NULL \
+ && !handling_longjmp \
+ && (step_range_end \
+ || trap_expected)) \
+ || bpstat_should_step ())
+
+ while (1)
+ {
+ /* Clean up saved state that will become invalid. */
+ flush_cached_frames ();
+ registers_changed ();
+
+ pid = target_wait (-1, &w);
+
+#ifdef SIGTRAP_STOP_AFTER_LOAD
+
+ /* Somebody called load(2), and it gave us a "trap signal after load".
+ Ignore it gracefully. */
+
+ SIGTRAP_STOP_AFTER_LOAD (w);
+#endif
+
+ /* See if the process still exists; clean up if it doesn't. */
+ if (WIFEXITED (w))
+ {
+ target_terminal_ours (); /* Must do this before mourn anyway */
+ if (WEXITSTATUS (w))
+ printf_filtered ("\nProgram exited with code 0%o.\n",
+ (unsigned int)WEXITSTATUS (w));
+ else
+ if (!batch_mode())
+ printf_filtered ("\nProgram exited normally.\n");
+ fflush (stdout);
+ target_mourn_inferior ();
+#ifdef NO_SINGLE_STEP
+ one_stepped = 0;
+#endif
+ stop_print_frame = 0;
+ break;
+ }
+ else if (!WIFSTOPPED (w))
+ {
+ char *signame;
+
+ stop_print_frame = 0;
+ stop_signal = WTERMSIG (w);
+ target_terminal_ours (); /* Must do this before mourn anyway */
+ target_kill (); /* kill mourns as well */
+#ifdef PRINT_RANDOM_SIGNAL
+ printf_filtered ("\nProgram terminated: ");
+ PRINT_RANDOM_SIGNAL (stop_signal);
+#else
+ printf_filtered ("\nProgram terminated with signal ");
+ signame = strsigno (stop_signal);
+ if (signame == NULL)
+ printf_filtered ("%d", stop_signal);
+ else
+ /* Do we need to print the number in addition to the name? */
+ printf_filtered ("%s (%d)", signame, stop_signal);
+ printf_filtered (", %s\n", safe_strsignal (stop_signal));
+#endif
+ printf_filtered ("The program no longer exists.\n");
+ fflush (stdout);
+#ifdef NO_SINGLE_STEP
+ one_stepped = 0;
+#endif
+ break;
+ }
+
+ stop_signal = WSTOPSIG (w);
+
+ if (pid != inferior_pid)
+ {
+ int save_pid = inferior_pid;
+
+ inferior_pid = pid; /* Setup for target memory/regs */
+ registers_changed ();
+ stop_pc = read_pc ();
+ inferior_pid = save_pid;
+ registers_changed ();
+ }
+ else
+ stop_pc = read_pc ();
+
+ if (stop_signal == SIGTRAP
+ && breakpoint_here_p (stop_pc - DECR_PC_AFTER_BREAK))
+ if (!breakpoint_thread_match (stop_pc - DECR_PC_AFTER_BREAK, pid))
+ {
+ /* Saw a breakpoint, but it was hit by the wrong thread. Just continue. */
+ if (breakpoints_inserted)
+ {
+ remove_breakpoints ();
+ target_resume (pid, 1, 0); /* Single step */
+ /* FIXME: What if a signal arrives instead of the single-step
+ happening? */
+ target_wait (pid, NULL);
+ insert_breakpoints ();
+ }
+ target_resume (-1, 0, 0);
+ continue;
+ }
+ else
+ if (pid != inferior_pid)
+ goto switch_thread;
+
+ if (pid != inferior_pid)
+ {
+ int printed = 0;
+
+ if (!in_thread_list (pid))
+ {
+ fprintf (stderr, "[New %s]\n", target_pid_to_str (pid));
+ add_thread (pid);
+
+ target_resume (-1, 0, 0);
+ continue;
+ }
+ else
+ {
+ if (stop_signal >= NSIG || signal_print[stop_signal])
+ {
+ char *signame;
+
+ printed = 1;
+ target_terminal_ours_for_output ();
+ printf_filtered ("\nProgram received signal ");
+ signame = strsigno (stop_signal);
+ if (signame == NULL)
+ printf_filtered ("%d", stop_signal);
+ else
+ printf_filtered ("%s (%d)", signame, stop_signal);
+ printf_filtered (", %s\n", safe_strsignal (stop_signal));
+
+ fflush (stdout);
+ }
+
+ if (stop_signal == SIGTRAP
+ || stop_signal >= NSIG
+ || signal_stop[stop_signal])
+ {
+switch_thread:
+ inferior_pid = pid;
+ printf_filtered ("[Switching to %s]\n", target_pid_to_str (pid));
+
+ flush_cached_frames ();
+ registers_changed ();
+ trap_expected = 0;
+ if (step_resume_breakpoint)
+ {
+ delete_breakpoint (step_resume_breakpoint);
+ step_resume_breakpoint = NULL;
+ }
+ prev_pc = 0;
+ prev_sp = 0;
+ prev_func_name = NULL;
+ step_range_start = 0;
+ step_range_end = 0;
+ step_frame_address = 0;
+ handling_longjmp = 0;
+ another_trap = 0;
+ }
+ else
+ {
+ if (printed)
+ target_terminal_inferior ();
+
+ /* Clear the signal if it should not be passed. */
+ if (signal_program[stop_signal] == 0)
+ stop_signal = 0;
+
+ target_resume (-1, 0, stop_signal);
+ continue;
+ }
+ }
+ }
+
+same_pid:
+
+#ifdef NO_SINGLE_STEP
+ if (one_stepped)
+ single_step (0); /* This actually cleans up the ss */
+#endif /* NO_SINGLE_STEP */
+
+/* If PC is pointing at a nullified instruction, then step beyond it so that
+ the user won't be confused when GDB appears to be ready to execute it. */
+
+ if (INSTRUCTION_NULLIFIED)
+ {
+ resume (1, 0);
+ continue;
+ }
+
+ set_current_frame ( create_new_frame (read_fp (), stop_pc));
+
+ stop_frame_address = FRAME_FP (get_current_frame ());
+ stop_sp = read_sp ();
+ stop_func_start = 0;
+ stop_func_name = 0;
+ /* Don't care about return value; stop_func_start and stop_func_name
+ will both be 0 if it doesn't work. */
+ find_pc_partial_function (stop_pc, &stop_func_name, &stop_func_start,
+ NULL);
+ stop_func_start += FUNCTION_START_OFFSET;
+ another_trap = 0;
+ bpstat_clear (&stop_bpstat);
+ stop_step = 0;
+ stop_stack_dummy = 0;
+ stop_print_frame = 1;
+ random_signal = 0;
+ stopped_by_random_signal = 0;
+ breakpoints_failed = 0;
+
+ /* Look at the cause of the stop, and decide what to do.
+ The alternatives are:
+ 1) break; to really stop and return to the debugger,
+ 2) drop through to start up again
+ (set another_trap to 1 to single step once)
+ 3) set random_signal to 1, and the decision between 1 and 2
+ will be made according to the signal handling tables. */
+
+ /* First, distinguish signals caused by the debugger from signals
+ that have to do with the program's own actions.
+ Note that breakpoint insns may cause SIGTRAP or SIGILL
+ or SIGEMT, depending on the operating system version.
+ Here we detect when a SIGILL or SIGEMT is really a breakpoint
+ and change it to SIGTRAP. */
+
+ if (stop_signal == SIGTRAP
+ || (breakpoints_inserted &&
+ (stop_signal == SIGILL
+#ifdef SIGEMT
+ || stop_signal == SIGEMT
+#endif
+ ))
+ || stop_soon_quietly)
+ {
+ if (stop_signal == SIGTRAP && stop_after_trap)
+ {
+ stop_print_frame = 0;
+ break;
+ }
+ if (stop_soon_quietly)
+ break;
+
+ /* Don't even think about breakpoints
+ if just proceeded over a breakpoint.
+
+ However, if we are trying to proceed over a breakpoint
+ and end up in sigtramp, then step_resume_breakpoint
+ will be set and we should check whether we've hit the
+ step breakpoint. */
+ if (stop_signal == SIGTRAP && trap_expected
+ && step_resume_breakpoint == NULL)
+ bpstat_clear (&stop_bpstat);
+ else
+ {
+ /* See if there is a breakpoint at the current PC. */
+ stop_bpstat = bpstat_stop_status
+ (&stop_pc, stop_frame_address,
+#if DECR_PC_AFTER_BREAK
+ /* Notice the case of stepping through a jump
+ that lands just after a breakpoint.
+ Don't confuse that with hitting the breakpoint.
+ What we check for is that 1) stepping is going on
+ and 2) the pc before the last insn does not match
+ the address of the breakpoint before the current pc. */
+ (prev_pc != stop_pc - DECR_PC_AFTER_BREAK
+ && CURRENTLY_STEPPING ())
+#else /* DECR_PC_AFTER_BREAK zero */
+ 0
+#endif /* DECR_PC_AFTER_BREAK zero */
+ );
+ /* Following in case break condition called a
+ function. */
+ stop_print_frame = 1;
+ }
+
+ if (stop_signal == SIGTRAP)
+ random_signal
+ = !(bpstat_explains_signal (stop_bpstat)
+ || trap_expected
+#ifndef CALL_DUMMY_BREAKPOINT_OFFSET
+ || PC_IN_CALL_DUMMY (stop_pc, stop_sp, stop_frame_address)
+#endif /* No CALL_DUMMY_BREAKPOINT_OFFSET. */
+ || (step_range_end && step_resume_breakpoint == NULL));
+ else
+ {
+ random_signal
+ = !(bpstat_explains_signal (stop_bpstat)
+ /* End of a stack dummy. Some systems (e.g. Sony
+ news) give another signal besides SIGTRAP,
+ so check here as well as above. */
+#ifndef CALL_DUMMY_BREAKPOINT_OFFSET
+ || PC_IN_CALL_DUMMY (stop_pc, stop_sp, stop_frame_address)
+#endif /* No CALL_DUMMY_BREAKPOINT_OFFSET. */
+ );
+ if (!random_signal)
+ stop_signal = SIGTRAP;
+ }
+ }
+ else
+ random_signal = 1;
+
+ /* For the program's own signals, act according to
+ the signal handling tables. */
+
+ if (random_signal)
+ {
+ /* Signal not for debugging purposes. */
+ int printed = 0;
+
+ stopped_by_random_signal = 1;
+
+ if (stop_signal >= NSIG
+ || signal_print[stop_signal])
+ {
+ char *signame;
+ printed = 1;
+ target_terminal_ours_for_output ();
+#ifdef PRINT_RANDOM_SIGNAL
+ PRINT_RANDOM_SIGNAL (stop_signal);
+#else
+ printf_filtered ("\nProgram received signal ");
+ signame = strsigno (stop_signal);
+ if (signame == NULL)
+ printf_filtered ("%d", stop_signal);
+ else
+ /* Do we need to print the number as well as the name? */
+ printf_filtered ("%s (%d)", signame, stop_signal);
+ printf_filtered (", %s\n", safe_strsignal (stop_signal));
+#endif /* PRINT_RANDOM_SIGNAL */
+ fflush (stdout);
+ }
+ if (stop_signal >= NSIG
+ || signal_stop[stop_signal])
+ break;
+ /* If not going to stop, give terminal back
+ if we took it away. */
+ else if (printed)
+ target_terminal_inferior ();
+
+ /* Clear the signal if it should not be passed. */
+ if (signal_program[stop_signal] == 0)
+ stop_signal = 0;
+
+ /* I'm not sure whether this needs to be check_sigtramp2 or
+ whether it could/should be keep_going. */
+ goto check_sigtramp2;
+ }
+
+ /* Handle cases caused by hitting a breakpoint. */
+ {
+ CORE_ADDR jmp_buf_pc;
+ struct bpstat_what what;
+
+ what = bpstat_what (stop_bpstat);
+
+ if (what.call_dummy)
+ {
+ stop_stack_dummy = 1;
+#ifdef HP_OS_BUG
+ trap_expected_after_continue = 1;
+#endif
+ }
+
+ switch (what.main_action)
+ {
+ case BPSTAT_WHAT_SET_LONGJMP_RESUME:
+ /* If we hit the breakpoint at longjmp, disable it for the
+ duration of this command. Then, install a temporary
+ breakpoint at the target of the jmp_buf. */
+ disable_longjmp_breakpoint();
+ remove_breakpoints ();
+ breakpoints_inserted = 0;
+ if (!GET_LONGJMP_TARGET(&jmp_buf_pc)) goto keep_going;
+
+ /* Need to blow away step-resume breakpoint, as it
+ interferes with us */
+ if (step_resume_breakpoint != NULL)
+ {
+ delete_breakpoint (step_resume_breakpoint);
+ step_resume_breakpoint = NULL;
+ what.step_resume = 0;
+ }
+
+#if 0
+ /* FIXME - Need to implement nested temporary breakpoints */
+ if (step_over_calls > 0)
+ set_longjmp_resume_breakpoint(jmp_buf_pc,
+ get_current_frame());
+ else
+#endif /* 0 */
+ set_longjmp_resume_breakpoint(jmp_buf_pc, NULL);
+ handling_longjmp = 1; /* FIXME */
+ goto keep_going;
+
+ case BPSTAT_WHAT_CLEAR_LONGJMP_RESUME:
+ case BPSTAT_WHAT_CLEAR_LONGJMP_RESUME_SINGLE:
+ remove_breakpoints ();
+ breakpoints_inserted = 0;
+#if 0
+ /* FIXME - Need to implement nested temporary breakpoints */
+ if (step_over_calls
+ && (stop_frame_address
+ INNER_THAN step_frame_address))
+ {
+ another_trap = 1;
+ goto keep_going;
+ }
+#endif /* 0 */
+ disable_longjmp_breakpoint();
+ handling_longjmp = 0; /* FIXME */
+ if (what.main_action == BPSTAT_WHAT_CLEAR_LONGJMP_RESUME)
+ break;
+ /* else fallthrough */
+
+ case BPSTAT_WHAT_SINGLE:
+ if (breakpoints_inserted)
+ remove_breakpoints ();
+ breakpoints_inserted = 0;
+ another_trap = 1;
+ /* Still need to check other stuff, at least the case
+ where we are stepping and step out of the right range. */
+ break;
+
+ case BPSTAT_WHAT_STOP_NOISY:
+ stop_print_frame = 1;
+ /* We are about to nuke the step_resume_breakpoint via the
+ cleanup chain, so no need to worry about it here. */
+ goto stop_stepping;
+
+ case BPSTAT_WHAT_STOP_SILENT:
+ stop_print_frame = 0;
+ /* We are about to nuke the step_resume_breakpoint via the
+ cleanup chain, so no need to worry about it here. */
+ goto stop_stepping;
+
+ case BPSTAT_WHAT_KEEP_CHECKING:
+ break;
+ }
+
+ if (what.step_resume)
+ {
+ delete_breakpoint (step_resume_breakpoint);
+ step_resume_breakpoint = NULL;
+
+ /* If were waiting for a trap, hitting the step_resume_break
+ doesn't count as getting it. */
+ if (trap_expected)
+ another_trap = 1;
+ }
+ }
+
+ /* We come here if we hit a breakpoint but should not
+ stop for it. Possibly we also were stepping
+ and should stop for that. So fall through and
+ test for stepping. But, if not stepping,
+ do not stop. */
+
+#ifndef CALL_DUMMY_BREAKPOINT_OFFSET
+ /* This is the old way of detecting the end of the stack dummy.
+ An architecture which defines CALL_DUMMY_BREAKPOINT_OFFSET gets
+ handled above. As soon as we can test it on all of them, all
+ architectures should define it. */
+
+ /* If this is the breakpoint at the end of a stack dummy,
+ just stop silently, unless the user was doing an si/ni, in which
+ case she'd better know what she's doing. */
+
+ if (PC_IN_CALL_DUMMY (stop_pc, stop_sp, stop_frame_address)
+ && !step_range_end)
+ {
+ stop_print_frame = 0;
+ stop_stack_dummy = 1;
+#ifdef HP_OS_BUG
+ trap_expected_after_continue = 1;
+#endif
+ break;
+ }
+#endif /* No CALL_DUMMY_BREAKPOINT_OFFSET. */
+
+ if (step_resume_breakpoint)
+ /* Having a step-resume breakpoint overrides anything
+ else having to do with stepping commands until
+ that breakpoint is reached. */
+ /* I suspect this could/should be keep_going, because if the
+ check_sigtramp2 check succeeds, then it will put in another
+ step_resume_breakpoint, and we aren't (yet) prepared to nest
+ them. */
+ goto check_sigtramp2;
+
+ if (step_range_end == 0)
+ /* Likewise if we aren't even stepping. */
+ /* I'm not sure whether this needs to be check_sigtramp2 or
+ whether it could/should be keep_going. */
+ goto check_sigtramp2;
+
+ /* If stepping through a line, keep going if still within it. */
+ if (stop_pc >= step_range_start
+ && stop_pc < step_range_end
+ /* The step range might include the start of the
+ function, so if we are at the start of the
+ step range and either the stack or frame pointers
+ just changed, we've stepped outside */
+ && !(stop_pc == step_range_start
+ && stop_frame_address
+ && (stop_sp INNER_THAN prev_sp
+ || stop_frame_address != step_frame_address)))
+ {
+ /* We might be doing a BPSTAT_WHAT_SINGLE and getting a signal.
+ So definately need to check for sigtramp here. */
+ goto check_sigtramp2;
+ }
+
+ /* We stepped out of the stepping range. See if that was due
+ to a subroutine call that we should proceed to the end of. */
+
+ /* Did we just take a signal? */
+ if (IN_SIGTRAMP (stop_pc, stop_func_name)
+ && !IN_SIGTRAMP (prev_pc, prev_func_name))
+ {
+ /* This code is needed at least in the following case:
+ The user types "next" and then a signal arrives (before
+ the "next" is done). */
+ /* We've just taken a signal; go until we are back to
+ the point where we took it and one more. */
+ {
+ struct symtab_and_line sr_sal;
+
+ sr_sal.pc = prev_pc;
+ sr_sal.symtab = NULL;
+ sr_sal.line = 0;
+ step_resume_breakpoint =
+ set_momentary_breakpoint (sr_sal, get_current_frame (),
+ bp_step_resume);
+ if (breakpoints_inserted)
+ insert_breakpoints ();
+ }
+
+ /* If this is stepi or nexti, make sure that the stepping range
+ gets us past that instruction. */
+ if (step_range_end == 1)
+ /* FIXME: Does this run afoul of the code below which, if
+ we step into the middle of a line, resets the stepping
+ range? */
+ step_range_end = (step_range_start = prev_pc) + 1;
+
+ remove_breakpoints_on_following_step = 1;
+ goto keep_going;
+ }
+
+ if (stop_func_start)
+ {
+ /* Do this after the IN_SIGTRAMP check; it might give
+ an error. */
+ prologue_pc = stop_func_start;
+ SKIP_PROLOGUE (prologue_pc);
+ }
+
+ if ((/* Might be a non-recursive call. If the symbols are missing
+ enough that stop_func_start == prev_func_start even though
+ they are really two functions, we will treat some calls as
+ jumps. */
+ stop_func_start != prev_func_start
+
+ /* Might be a recursive call if either we have a prologue
+ or the call instruction itself saves the PC on the stack. */
+ || prologue_pc != stop_func_start
+ || stop_sp != prev_sp)
+ && (/* PC is completely out of bounds of any known objfiles. Treat
+ like a subroutine call. */
+ !stop_func_start
+
+ /* If we do a call, we will be at the start of a function. */
+ || stop_pc == stop_func_start
+
+#if 0
+ /* Not conservative enough for 4.11. FIXME: enable this
+ after 4.11. */
+ /* Except on the Alpha with -O (and perhaps other machines
+ with similar calling conventions), in which we might
+ call the address after the load of gp. Since prologues
+ don't contain calls, we can't return to within one, and
+ we don't jump back into them, so this check is OK. */
+ || stop_pc < prologue_pc
+#endif
+
+ /* If we end up in certain places, it means we did a subroutine
+ call. I'm not completely sure this is necessary now that we
+ have the above checks with stop_func_start (and now that
+ find_pc_partial_function is pickier). */
+ || IN_SOLIB_TRAMPOLINE (stop_pc, stop_func_name)
+
+ /* If none of the above apply, it is a jump within a function,
+ or a return from a subroutine. The other case is longjmp,
+ which can no longer happen here as long as the
+ handling_longjmp stuff is working. */
+ ))
+ {
+ /* It's a subroutine call. */
+
+ if (step_over_calls == 0)
+ {
+ /* I presume that step_over_calls is only 0 when we're
+ supposed to be stepping at the assembly language level
+ ("stepi"). Just stop. */
+ stop_step = 1;
+ break;
+ }
+
+ if (step_over_calls > 0)
+ /* We're doing a "next". */
+ goto step_over_function;
+
+ /* If we are in a function call trampoline (a stub between
+ the calling routine and the real function), locate the real
+ function. That's what tells us (a) whether we want to step
+ into it at all, and (b) what prologue we want to run to
+ the end of, if we do step into it. */
+ tmp = SKIP_TRAMPOLINE_CODE (stop_pc);
+ if (tmp != 0)
+ stop_func_start = tmp;
+
+ /* If we have line number information for the function we
+ are thinking of stepping into, step into it.
+
+ If there are several symtabs at that PC (e.g. with include
+ files), just want to know whether *any* of them have line
+ numbers. find_pc_line handles this. */
+ {
+ struct symtab_and_line tmp_sal;
+
+ tmp_sal = find_pc_line (stop_func_start, 0);
+ if (tmp_sal.line != 0)
+ goto step_into_function;
+ }
+
+step_over_function:
+ /* A subroutine call has happened. */
+ {
+ /* Set a special breakpoint after the return */
+ struct symtab_and_line sr_sal;
+ sr_sal.pc =
+ ADDR_BITS_REMOVE
+ (SAVED_PC_AFTER_CALL (get_current_frame ()));
+ sr_sal.symtab = NULL;
+ sr_sal.line = 0;
+ step_resume_breakpoint =
+ set_momentary_breakpoint (sr_sal, get_current_frame (),
+ bp_step_resume);
+ if (breakpoints_inserted)
+ insert_breakpoints ();
+ }
+ goto keep_going;
+
+step_into_function:
+ /* Subroutine call with source code we should not step over.
+ Do step to the first line of code in it. */
+ SKIP_PROLOGUE (stop_func_start);
+ sal = find_pc_line (stop_func_start, 0);
+ /* Use the step_resume_break to step until
+ the end of the prologue, even if that involves jumps
+ (as it seems to on the vax under 4.2). */
+ /* If the prologue ends in the middle of a source line,
+ continue to the end of that source line.
+ Otherwise, just go to end of prologue. */
+#ifdef PROLOGUE_FIRSTLINE_OVERLAP
+ /* no, don't either. It skips any code that's
+ legitimately on the first line. */
+#else
+ if (sal.end && sal.pc != stop_func_start)
+ stop_func_start = sal.end;
+#endif
+
+ if (stop_func_start == stop_pc)
+ {
+ /* We are already there: stop now. */
+ stop_step = 1;
+ break;
+ }
+ else
+ /* Put the step-breakpoint there and go until there. */
+ {
+ struct symtab_and_line sr_sal;
+
+ sr_sal.pc = stop_func_start;
+ sr_sal.symtab = NULL;
+ sr_sal.line = 0;
+ /* Do not specify what the fp should be when we stop
+ since on some machines the prologue
+ is where the new fp value is established. */
+ step_resume_breakpoint =
+ set_momentary_breakpoint (sr_sal, NULL, bp_step_resume);
+ if (breakpoints_inserted)
+ insert_breakpoints ();
+
+ /* And make sure stepping stops right away then. */
+ step_range_end = step_range_start;
+ }
+ goto keep_going;
+ }
+
+ /* We've wandered out of the step range (but haven't done a
+ subroutine call or return). (Is that true? I think we get
+ here if we did a return and maybe a longjmp). */
+
+ sal = find_pc_line(stop_pc, 0);
+
+ if (step_range_end == 1)
+ {
+ /* It is stepi or nexti. We always want to stop stepping after
+ one instruction. */
+ stop_step = 1;
+ break;
+ }
+
+ if (sal.line == 0)
+ {
+ /* We have no line number information. That means to stop
+ stepping (does this always happen right after one instruction,
+ when we do "s" in a function with no line numbers,
+ or can this happen as a result of a return or longjmp?). */
+ stop_step = 1;
+ break;
+ }
+
+ if (stop_pc == sal.pc && current_line != sal.line)
+ {
+ /* We are at the start of a different line. So stop. Note that
+ we don't stop if we step into the middle of a different line.
+ That is said to make things like for (;;) statements work
+ better. */
+ stop_step = 1;
+ break;
+ }
+
+ /* We aren't done stepping.
+
+ Optimize by setting the stepping range to the line.
+ (We might not be in the original line, but if we entered a
+ new line in mid-statement, we continue stepping. This makes
+ things like for(;;) statements work better.) */
+ step_range_start = sal.pc;
+ step_range_end = sal.end;
+ goto keep_going;
+
+ check_sigtramp2:
+ if (trap_expected
+ && IN_SIGTRAMP (stop_pc, stop_func_name)
+ && !IN_SIGTRAMP (prev_pc, prev_func_name))
+ {
+ /* What has happened here is that we have just stepped the inferior
+ with a signal (because it is a signal which shouldn't make
+ us stop), thus stepping into sigtramp.
+
+ So we need to set a step_resume_break_address breakpoint
+ and continue until we hit it, and then step. FIXME: This should
+ be more enduring than a step_resume breakpoint; we should know
+ that we will later need to keep going rather than re-hitting
+ the breakpoint here (see testsuite/gdb.t06/signals.exp where
+ it says "exceedingly difficult"). */
+ struct symtab_and_line sr_sal;
+
+ sr_sal.pc = prev_pc;
+ sr_sal.symtab = NULL;
+ sr_sal.line = 0;
+ step_resume_breakpoint =
+ set_momentary_breakpoint (sr_sal, get_current_frame (),
+ bp_step_resume);
+ if (breakpoints_inserted)
+ insert_breakpoints ();
+
+ remove_breakpoints_on_following_step = 1;
+ another_trap = 1;
+ }
+
+ keep_going:
+ /* Come to this label when you need to resume the inferior.
+ It's really much cleaner to do a goto than a maze of if-else
+ conditions. */
+
+ /* Save the pc before execution, to compare with pc after stop. */
+ prev_pc = read_pc (); /* Might have been DECR_AFTER_BREAK */
+ prev_func_start = stop_func_start; /* Ok, since if DECR_PC_AFTER
+ BREAK is defined, the
+ original pc would not have
+ been at the start of a
+ function. */
+
+ prev_func_name = stop_func_name;
+ prev_sp = stop_sp;
+
+ /* If we did not do break;, it means we should keep
+ running the inferior and not return to debugger. */
+
+ if (trap_expected && stop_signal != SIGTRAP)
+ {
+ /* We took a signal (which we are supposed to pass through to
+ the inferior, else we'd have done a break above) and we
+ haven't yet gotten our trap. Simply continue. */
+ resume (CURRENTLY_STEPPING (), stop_signal);
+ }
+ else
+ {
+ /* Either the trap was not expected, but we are continuing
+ anyway (the user asked that this signal be passed to the
+ child)
+ -- or --
+ The signal was SIGTRAP, e.g. it was our signal, but we
+ decided we should resume from it.
+
+ We're going to run this baby now!
+
+ Insert breakpoints now, unless we are trying
+ to one-proceed past a breakpoint. */
+ /* If we've just finished a special step resume and we don't
+ want to hit a breakpoint, pull em out. */
+ if (step_resume_breakpoint == NULL &&
+ remove_breakpoints_on_following_step)
+ {
+ remove_breakpoints_on_following_step = 0;
+ remove_breakpoints ();
+ breakpoints_inserted = 0;
+ }
+ else if (!breakpoints_inserted &&
+ (step_resume_breakpoint != NULL || !another_trap))
+ {
+ breakpoints_failed = insert_breakpoints ();
+ if (breakpoints_failed)
+ break;
+ breakpoints_inserted = 1;
+ }
+
+ trap_expected = another_trap;
+
+ if (stop_signal == SIGTRAP)
+ stop_signal = 0;
+
+#ifdef SHIFT_INST_REGS
+ /* I'm not sure when this following segment applies. I do know, now,
+ that we shouldn't rewrite the regs when we were stopped by a
+ random signal from the inferior process. */
+ /* FIXME: Shouldn't this be based on the valid bit of the SXIP?
+ (this is only used on the 88k). */
+
+ if (!bpstat_explains_signal (stop_bpstat)
+ && (stop_signal != SIGCLD)
+ && !stopped_by_random_signal)
+ SHIFT_INST_REGS();
+#endif /* SHIFT_INST_REGS */
+
+ resume (CURRENTLY_STEPPING (), stop_signal);
+ }
+ }
+
+ stop_stepping:
+ if (target_has_execution)
+ {
+ /* Assuming the inferior still exists, set these up for next
+ time, just like we did above if we didn't break out of the
+ loop. */
+ prev_pc = read_pc ();
+ prev_func_start = stop_func_start;
+ prev_func_name = stop_func_name;
+ prev_sp = stop_sp;
+ }
+ do_cleanups (old_cleanups);
+}
+
+/* Here to return control to GDB when the inferior stops for real.
+ Print appropriate messages, remove breakpoints, give terminal our modes.
+
+ STOP_PRINT_FRAME nonzero means print the executing frame
+ (pc, function, args, file, line number and line text).
+ BREAKPOINTS_FAILED nonzero means stop was due to error
+ attempting to insert breakpoints. */
+
+void
+normal_stop ()
+{
+ /* Make sure that the current_frame's pc is correct. This
+ is a correction for setting up the frame info before doing
+ DECR_PC_AFTER_BREAK */
+ if (target_has_execution && get_current_frame())
+ (get_current_frame ())->pc = read_pc ();
+
+ if (breakpoints_failed)
+ {
+ target_terminal_ours_for_output ();
+ print_sys_errmsg ("ptrace", breakpoints_failed);
+ printf_filtered ("Stopped; cannot insert breakpoints.\n\
+The same program may be running in another process.\n");
+ }
+
+ if (target_has_execution && breakpoints_inserted)
+ if (remove_breakpoints ())
+ {
+ target_terminal_ours_for_output ();
+ printf_filtered ("Cannot remove breakpoints because program is no longer writable.\n\
+It might be running in another process.\n\
+Further execution is probably impossible.\n");
+ }
+
+ breakpoints_inserted = 0;
+
+ /* Delete the breakpoint we stopped at, if it wants to be deleted.
+ Delete any breakpoint that is to be deleted at the next stop. */
+
+ breakpoint_auto_delete (stop_bpstat);
+
+ /* If an auto-display called a function and that got a signal,
+ delete that auto-display to avoid an infinite recursion. */
+
+ if (stopped_by_random_signal)
+ disable_current_display ();
+
+ if (step_multi && stop_step)
+ return;
+
+ target_terminal_ours ();
+
+ /* Look up the hook_stop and run it if it exists. */
+
+ if (stop_command->hook)
+ {
+ catch_errors (hook_stop_stub, (char *)stop_command->hook,
+ "Error while running hook_stop:\n", RETURN_MASK_ALL);
+ }
+
+ if (!target_has_stack)
+ return;
+
+ /* Select innermost stack frame except on return from a stack dummy routine,
+ or if the program has exited. Print it without a level number if
+ we have changed functions or hit a breakpoint. Print source line
+ if we have one. */
+ if (!stop_stack_dummy)
+ {
+ select_frame (get_current_frame (), 0);
+
+ if (stop_print_frame)
+ {
+ int source_only;
+
+ source_only = bpstat_print (stop_bpstat);
+ source_only = source_only ||
+ ( stop_step
+ && step_frame_address == stop_frame_address
+ && step_start_function == find_pc_function (stop_pc));
+
+ print_stack_frame (selected_frame, -1, source_only? -1: 1);
+
+ /* Display the auto-display expressions. */
+ do_displays ();
+ }
+ }
+
+ /* Save the function value return registers, if we care.
+ We might be about to restore their previous contents. */
+ if (proceed_to_finish)
+ read_register_bytes (0, stop_registers, REGISTER_BYTES);
+
+ if (stop_stack_dummy)
+ {
+ /* Pop the empty frame that contains the stack dummy.
+ POP_FRAME ends with a setting of the current frame, so we
+ can use that next. */
+ POP_FRAME;
+ select_frame (get_current_frame (), 0);
+ }
+}
+
+static int
+hook_stop_stub (cmd)
+ char *cmd;
+{
+ execute_user_command ((struct cmd_list_element *)cmd, 0);
+ return (0);
+}
+
+int signal_stop_state (signo)
+ int signo;
+{
+ return ((signo >= 0 && signo < NSIG) ? signal_stop[signo] : 0);
+}
+
+int signal_print_state (signo)
+ int signo;
+{
+ return ((signo >= 0 && signo < NSIG) ? signal_print[signo] : 0);
+}
+
+int signal_pass_state (signo)
+ int signo;
+{
+ return ((signo >= 0 && signo < NSIG) ? signal_program[signo] : 0);
+}
+
+static void
+sig_print_header ()
+{
+ printf_filtered ("Signal\t\tStop\tPrint\tPass to program\tDescription\n");
+}
+
+static void
+sig_print_info (number)
+ int number;
+{
+ char *name;
+
+ if ((name = strsigno (number)) == NULL)
+ printf_filtered ("%d\t\t", number);
+ else
+ printf_filtered ("%s (%d)\t", name, number);
+ printf_filtered ("%s\t", signal_stop[number] ? "Yes" : "No");
+ printf_filtered ("%s\t", signal_print[number] ? "Yes" : "No");
+ printf_filtered ("%s\t\t", signal_program[number] ? "Yes" : "No");
+ printf_filtered ("%s\n", safe_strsignal (number));
+}
+
+/* Specify how various signals in the inferior should be handled. */
+
+static void
+handle_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ char **argv;
+ int digits, wordlen;
+ int sigfirst, signum, siglast;
+ int allsigs;
+ int nsigs;
+ unsigned char *sigs;
+ struct cleanup *old_chain;
+
+ if (args == NULL)
+ {
+ error_no_arg ("signal to handle");
+ }
+
+ /* Allocate and zero an array of flags for which signals to handle. */
+
+ nsigs = signo_max () + 1;
+ sigs = (unsigned char *) alloca (nsigs);
+ memset (sigs, 0, nsigs);
+
+ /* Break the command line up into args. */
+
+ argv = buildargv (args);
+ if (argv == NULL)
+ {
+ nomem (0);
+ }
+ old_chain = make_cleanup (freeargv, (char *) argv);
+
+ /* Walk through the args, looking for signal numbers, signal names, and
+ actions. Signal numbers and signal names may be interspersed with
+ actions, with the actions being performed for all signals cumulatively
+ specified. Signal ranges can be specified as <LOW>-<HIGH>. */
+
+ while (*argv != NULL)
+ {
+ wordlen = strlen (*argv);
+ for (digits = 0; isdigit ((*argv)[digits]); digits++) {;}
+ allsigs = 0;
+ sigfirst = siglast = -1;
+
+ if (wordlen >= 1 && !strncmp (*argv, "all", wordlen))
+ {
+ /* Apply action to all signals except those used by the
+ debugger. Silently skip those. */
+ allsigs = 1;
+ sigfirst = 0;
+ siglast = nsigs - 1;
+ }
+ else if (wordlen >= 1 && !strncmp (*argv, "stop", wordlen))
+ {
+ SET_SIGS (nsigs, sigs, signal_stop);
+ SET_SIGS (nsigs, sigs, signal_print);
+ }
+ else if (wordlen >= 1 && !strncmp (*argv, "ignore", wordlen))
+ {
+ UNSET_SIGS (nsigs, sigs, signal_program);
+ }
+ else if (wordlen >= 2 && !strncmp (*argv, "print", wordlen))
+ {
+ SET_SIGS (nsigs, sigs, signal_print);
+ }
+ else if (wordlen >= 2 && !strncmp (*argv, "pass", wordlen))
+ {
+ SET_SIGS (nsigs, sigs, signal_program);
+ }
+ else if (wordlen >= 3 && !strncmp (*argv, "nostop", wordlen))
+ {
+ UNSET_SIGS (nsigs, sigs, signal_stop);
+ }
+ else if (wordlen >= 3 && !strncmp (*argv, "noignore", wordlen))
+ {
+ SET_SIGS (nsigs, sigs, signal_program);
+ }
+ else if (wordlen >= 4 && !strncmp (*argv, "noprint", wordlen))
+ {
+ UNSET_SIGS (nsigs, sigs, signal_print);
+ UNSET_SIGS (nsigs, sigs, signal_stop);
+ }
+ else if (wordlen >= 4 && !strncmp (*argv, "nopass", wordlen))
+ {
+ UNSET_SIGS (nsigs, sigs, signal_program);
+ }
+ else if (digits > 0)
+ {
+ sigfirst = siglast = atoi (*argv);
+ if ((*argv)[digits] == '-')
+ {
+ siglast = atoi ((*argv) + digits + 1);
+ }
+ if (sigfirst > siglast)
+ {
+ /* Bet he didn't figure we'd think of this case... */
+ signum = sigfirst;
+ sigfirst = siglast;
+ siglast = signum;
+ }
+ if (sigfirst < 0 || sigfirst >= nsigs)
+ {
+ error ("Signal %d not in range 0-%d", sigfirst, nsigs - 1);
+ }
+ if (siglast < 0 || siglast >= nsigs)
+ {
+ error ("Signal %d not in range 0-%d", siglast, nsigs - 1);
+ }
+ }
+ else if ((signum = strtosigno (*argv)) != 0)
+ {
+ sigfirst = siglast = signum;
+ }
+ else
+ {
+ /* Not a number and not a recognized flag word => complain. */
+ error ("Unrecognized or ambiguous flag word: \"%s\".", *argv);
+ }
+
+ /* If any signal numbers or symbol names were found, set flags for
+ which signals to apply actions to. */
+
+ for (signum = sigfirst; signum >= 0 && signum <= siglast; signum++)
+ {
+ switch (signum)
+ {
+ case SIGTRAP:
+ case SIGINT:
+ if (!allsigs && !sigs[signum])
+ {
+ if (query ("%s is used by the debugger.\nAre you sure you want to change it? ", strsigno (signum)))
+ {
+ sigs[signum] = 1;
+ }
+ else
+ {
+ printf ("Not confirmed, unchanged.\n");
+ fflush (stdout);
+ }
+ }
+ break;
+ default:
+ sigs[signum] = 1;
+ break;
+ }
+ }
+
+ argv++;
+ }
+
+ target_notice_signals(inferior_pid);
+
+ if (from_tty)
+ {
+ /* Show the results. */
+ sig_print_header ();
+ for (signum = 0; signum < nsigs; signum++)
+ {
+ if (sigs[signum])
+ {
+ sig_print_info (signum);
+ }
+ }
+ }
+
+ do_cleanups (old_chain);
+}
+
+/* Print current contents of the tables set by the handle command. */
+
+static void
+signals_info (signum_exp, from_tty)
+ char *signum_exp;
+ int from_tty;
+{
+ register int i;
+ sig_print_header ();
+
+ if (signum_exp)
+ {
+ /* First see if this is a symbol name. */
+ i = strtosigno (signum_exp);
+ if (i == 0)
+ {
+ /* Nope, maybe it's an address which evaluates to a signal
+ number. */
+ i = parse_and_eval_address (signum_exp);
+ if (i >= NSIG || i < 0)
+ error ("Signal number out of bounds.");
+ }
+ sig_print_info (i);
+ return;
+ }
+
+ printf_filtered ("\n");
+ for (i = 0; i < NSIG; i++)
+ {
+ QUIT;
+
+ sig_print_info (i);
+ }
+
+ printf_filtered ("\nUse the \"handle\" command to change these tables.\n");
+}
+
+/* Save all of the information associated with the inferior<==>gdb
+ connection. INF_STATUS is a pointer to a "struct inferior_status"
+ (defined in inferior.h). */
+
+void
+save_inferior_status (inf_status, restore_stack_info)
+ struct inferior_status *inf_status;
+ int restore_stack_info;
+{
+ inf_status->stop_signal = stop_signal;
+ inf_status->stop_pc = stop_pc;
+ inf_status->stop_frame_address = stop_frame_address;
+ inf_status->stop_step = stop_step;
+ inf_status->stop_stack_dummy = stop_stack_dummy;
+ inf_status->stopped_by_random_signal = stopped_by_random_signal;
+ inf_status->trap_expected = trap_expected;
+ inf_status->step_range_start = step_range_start;
+ inf_status->step_range_end = step_range_end;
+ inf_status->step_frame_address = step_frame_address;
+ inf_status->step_over_calls = step_over_calls;
+ inf_status->stop_after_trap = stop_after_trap;
+ inf_status->stop_soon_quietly = stop_soon_quietly;
+ /* Save original bpstat chain here; replace it with copy of chain.
+ If caller's caller is walking the chain, they'll be happier if we
+ hand them back the original chain when restore_i_s is called. */
+ inf_status->stop_bpstat = stop_bpstat;
+ stop_bpstat = bpstat_copy (stop_bpstat);
+ inf_status->breakpoint_proceeded = breakpoint_proceeded;
+ inf_status->restore_stack_info = restore_stack_info;
+ inf_status->proceed_to_finish = proceed_to_finish;
+
+ memcpy (inf_status->stop_registers, stop_registers, REGISTER_BYTES);
+
+ read_register_bytes (0, inf_status->registers, REGISTER_BYTES);
+
+ record_selected_frame (&(inf_status->selected_frame_address),
+ &(inf_status->selected_level));
+ return;
+}
+
+struct restore_selected_frame_args {
+ FRAME_ADDR frame_address;
+ int level;
+};
+
+static int restore_selected_frame PARAMS ((char *));
+
+/* Restore the selected frame. args is really a struct
+ restore_selected_frame_args * (declared as char * for catch_errors)
+ telling us what frame to restore. Returns 1 for success, or 0 for
+ failure. An error message will have been printed on error. */
+static int
+restore_selected_frame (args)
+ char *args;
+{
+ struct restore_selected_frame_args *fr =
+ (struct restore_selected_frame_args *) args;
+ FRAME fid;
+ int level = fr->level;
+
+ fid = find_relative_frame (get_current_frame (), &level);
+
+ /* If inf_status->selected_frame_address is NULL, there was no
+ previously selected frame. */
+ if (fid == 0 ||
+ FRAME_FP (fid) != fr->frame_address ||
+ level != 0)
+ {
+ warning ("Unable to restore previously selected frame.\n");
+ return 0;
+ }
+ select_frame (fid, fr->level);
+ return(1);
+}
+
+void
+restore_inferior_status (inf_status)
+ struct inferior_status *inf_status;
+{
+ stop_signal = inf_status->stop_signal;
+ stop_pc = inf_status->stop_pc;
+ stop_frame_address = inf_status->stop_frame_address;
+ stop_step = inf_status->stop_step;
+ stop_stack_dummy = inf_status->stop_stack_dummy;
+ stopped_by_random_signal = inf_status->stopped_by_random_signal;
+ trap_expected = inf_status->trap_expected;
+ step_range_start = inf_status->step_range_start;
+ step_range_end = inf_status->step_range_end;
+ step_frame_address = inf_status->step_frame_address;
+ step_over_calls = inf_status->step_over_calls;
+ stop_after_trap = inf_status->stop_after_trap;
+ stop_soon_quietly = inf_status->stop_soon_quietly;
+ bpstat_clear (&stop_bpstat);
+ stop_bpstat = inf_status->stop_bpstat;
+ breakpoint_proceeded = inf_status->breakpoint_proceeded;
+ proceed_to_finish = inf_status->proceed_to_finish;
+
+ memcpy (stop_registers, inf_status->stop_registers, REGISTER_BYTES);
+
+ /* The inferior can be gone if the user types "print exit(0)"
+ (and perhaps other times). */
+ if (target_has_execution)
+ write_register_bytes (0, inf_status->registers, REGISTER_BYTES);
+
+ /* The inferior can be gone if the user types "print exit(0)"
+ (and perhaps other times). */
+
+ /* FIXME: If we are being called after stopping in a function which
+ is called from gdb, we should not be trying to restore the
+ selected frame; it just prints a spurious error message (The
+ message is useful, however, in detecting bugs in gdb (like if gdb
+ clobbers the stack)). In fact, should we be restoring the
+ inferior status at all in that case? . */
+
+ if (target_has_stack && inf_status->restore_stack_info)
+ {
+ struct restore_selected_frame_args fr;
+ fr.level = inf_status->selected_level;
+ fr.frame_address = inf_status->selected_frame_address;
+ /* The point of catch_errors is that if the stack is clobbered,
+ walking the stack might encounter a garbage pointer and error()
+ trying to dereference it. */
+ if (catch_errors (restore_selected_frame, &fr,
+ "Unable to restore previously selected frame:\n",
+ RETURN_MASK_ERROR) == 0)
+ /* Error in restoring the selected frame. Select the innermost
+ frame. */
+ select_frame (get_current_frame (), 0);
+ }
+}
+
+
+void
+_initialize_infrun ()
+{
+ register int i;
+ register int numsigs;
+
+ add_info ("signals", signals_info,
+ "What debugger does when program gets various signals.\n\
+Specify a signal number as argument to print info on that signal only.");
+ add_info_alias ("handle", "signals", 0);
+
+ add_com ("handle", class_run, handle_command,
+ "Specify how to handle a signal.\n\
+Args are signal numbers and actions to apply to those signals.\n\
+Signal numbers may be numeric (ex. 11) or symbolic (ex. SIGSEGV).\n\
+Numeric ranges may be specified with the form LOW-HIGH (ex. 14-21).\n\
+The special arg \"all\" is recognized to mean all signals except those\n\
+used by the debugger, typically SIGTRAP and SIGINT.\n\
+Recognized actions include \"stop\", \"nostop\", \"print\", \"noprint\",\n\
+\"pass\", \"nopass\", \"ignore\", or \"noignore\".\n\
+Stop means reenter debugger if this signal happens (implies print).\n\
+Print means print a message if this signal happens.\n\
+Pass means let program see this signal; otherwise program doesn't know.\n\
+Ignore is a synonym for nopass and noignore is a synonym for pass.\n\
+Pass and Stop may be combined.");
+
+ stop_command = add_cmd ("stop", class_obscure, not_just_help_class_command,
+ "There is no `stop' command, but you can set a hook on `stop'.\n\
+This allows you to set a list of commands to be run each time execution\n\
+of the program stops.", &cmdlist);
+
+ numsigs = signo_max () + 1;
+ signal_stop = (unsigned char *)
+ xmalloc (sizeof (signal_stop[0]) * numsigs);
+ signal_print = (unsigned char *)
+ xmalloc (sizeof (signal_print[0]) * numsigs);
+ signal_program = (unsigned char *)
+ xmalloc (sizeof (signal_program[0]) * numsigs);
+ for (i = 0; i < numsigs; i++)
+ {
+ signal_stop[i] = 1;
+ signal_print[i] = 1;
+ signal_program[i] = 1;
+ }
+
+ /* Signals caused by debugger's own actions
+ should not be given to the program afterwards. */
+ signal_program[SIGTRAP] = 0;
+ signal_program[SIGINT] = 0;
+
+ /* Signals that are not errors should not normally enter the debugger. */
+#ifdef SIGALRM
+ signal_stop[SIGALRM] = 0;
+ signal_print[SIGALRM] = 0;
+#endif /* SIGALRM */
+#ifdef SIGVTALRM
+ signal_stop[SIGVTALRM] = 0;
+ signal_print[SIGVTALRM] = 0;
+#endif /* SIGVTALRM */
+#ifdef SIGPROF
+ signal_stop[SIGPROF] = 0;
+ signal_print[SIGPROF] = 0;
+#endif /* SIGPROF */
+#ifdef SIGCHLD
+ signal_stop[SIGCHLD] = 0;
+ signal_print[SIGCHLD] = 0;
+#endif /* SIGCHLD */
+#ifdef SIGCLD
+ signal_stop[SIGCLD] = 0;
+ signal_print[SIGCLD] = 0;
+#endif /* SIGCLD */
+#ifdef SIGIO
+ signal_stop[SIGIO] = 0;
+ signal_print[SIGIO] = 0;
+#endif /* SIGIO */
+#ifdef SIGURG
+ signal_stop[SIGURG] = 0;
+ signal_print[SIGURG] = 0;
+#endif /* SIGURG */
+}
diff --git a/gnu/usr.bin/gdb/gdb/inftarg.c b/gnu/usr.bin/gdb/gdb/inftarg.c
new file mode 100644
index 0000000..f937be4
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/inftarg.c
@@ -0,0 +1,310 @@
+/* Target-vector operations for controlling Unix child processes, for GDB.
+ Copyright 1990, 1991, 1992 Free Software Foundation, Inc.
+ Contributed by Cygnus Support.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "frame.h" /* required by inferior.h */
+#include "inferior.h"
+#include "target.h"
+#include "wait.h"
+#include "gdbcore.h"
+
+#include <signal.h>
+
+static void
+child_prepare_to_store PARAMS ((void));
+
+#ifndef CHILD_WAIT
+static int
+child_wait PARAMS ((int, int *));
+#endif /* CHILD_WAIT */
+
+static void
+child_open PARAMS ((char *, int));
+
+static void
+child_files_info PARAMS ((struct target_ops *));
+
+static void
+child_detach PARAMS ((char *, int));
+
+static void
+child_attach PARAMS ((char *, int));
+
+static void
+ptrace_me PARAMS ((void));
+
+static void
+ptrace_him PARAMS ((int));
+
+static void
+child_create_inferior PARAMS ((char *, char *, char **));
+
+static void
+child_mourn_inferior PARAMS ((void));
+
+static int
+child_can_run PARAMS ((void));
+
+extern char **environ;
+
+/* Forward declaration */
+extern struct target_ops child_ops;
+
+#ifndef CHILD_WAIT
+
+/* Wait for child to do something. Return pid of child, or -1 in case
+ of error; store status through argument pointer STATUS. */
+
+static int
+child_wait (pid, status)
+ int pid;
+ int *status;
+{
+ int save_errno;
+
+ do {
+ if (attach_flag)
+ set_sigint_trap(); /* Causes SIGINT to be passed on to the
+ attached process. */
+ pid = wait (status);
+ save_errno = errno;
+
+ if (attach_flag)
+ clear_sigint_trap();
+
+ if (pid == -1)
+ {
+ if (save_errno == EINTR)
+ continue;
+ fprintf (stderr, "Child process unexpectedly missing: %s.\n",
+ safe_strerror (save_errno));
+ *status = 42; /* Claim it exited with signal 42 */
+ return -1;
+ }
+ } while (pid != inferior_pid); /* Some other child died or stopped */
+ return pid;
+}
+#endif /* CHILD_WAIT */
+
+/* Attach to process PID, then initialize for debugging it. */
+
+static void
+child_attach (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ char *exec_file;
+ int pid;
+
+ if (!args)
+ error_no_arg ("process-id to attach");
+
+#ifndef ATTACH_DETACH
+ error ("Can't attach to a process on this machine.");
+#else
+ pid = atoi (args);
+
+ if (pid == getpid()) /* Trying to masturbate? */
+ error ("I refuse to debug myself!");
+
+ if (from_tty)
+ {
+ exec_file = (char *) get_exec_file (0);
+
+ if (exec_file)
+ printf ("Attaching to program `%s', %s\n", exec_file, target_pid_to_str (pid));
+ else
+ printf ("Attaching to %s\n", target_pid_to_str (pid));
+
+ fflush (stdout);
+ }
+
+ attach (pid);
+ inferior_pid = pid;
+ push_target (&child_ops);
+#endif /* ATTACH_DETACH */
+}
+
+
+/* Take a program previously attached to and detaches it.
+ The program resumes execution and will no longer stop
+ on signals, etc. We'd better not have left any breakpoints
+ in the program or it'll die when it hits one. For this
+ to work, it may be necessary for the process to have been
+ previously attached. It *might* work if the program was
+ started via the normal ptrace (PTRACE_TRACEME). */
+
+static void
+child_detach (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ int siggnal = 0;
+
+#ifdef ATTACH_DETACH
+ if (from_tty)
+ {
+ char *exec_file = get_exec_file (0);
+ if (exec_file == 0)
+ exec_file = "";
+ printf ("Detaching from program: %s %s\n", exec_file,
+ target_pid_to_str (inferior_pid));
+ fflush (stdout);
+ }
+ if (args)
+ siggnal = atoi (args);
+
+ detach (siggnal);
+ inferior_pid = 0;
+ unpush_target (&child_ops); /* Pop out of handling an inferior */
+#else
+ error ("This version of Unix does not support detaching a process.");
+#endif
+}
+
+/* Get ready to modify the registers array. On machines which store
+ individual registers, this doesn't need to do anything. On machines
+ which store all the registers in one fell swoop, this makes sure
+ that registers contains all the registers from the program being
+ debugged. */
+
+static void
+child_prepare_to_store ()
+{
+#ifdef CHILD_PREPARE_TO_STORE
+ CHILD_PREPARE_TO_STORE ();
+#endif
+}
+
+/* Print status information about what we're accessing. */
+
+static void
+child_files_info (ignore)
+ struct target_ops *ignore;
+{
+ printf ("\tUsing the running image of %s %s.\n",
+ attach_flag? "attached": "child", target_pid_to_str (inferior_pid));
+}
+
+/* ARGSUSED */
+static void
+child_open (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ error ("Use the \"run\" command to start a Unix child process.");
+}
+
+/* Stub function which causes the inferior that runs it, to be ptrace-able
+ by its parent process. */
+
+static void
+ptrace_me ()
+{
+ /* "Trace me, Dr. Memory!" */
+ call_ptrace (0, 0, (PTRACE_ARG3_TYPE) 0, 0);
+}
+
+/* Stub function which causes the GDB that runs it, to start ptrace-ing
+ the child process. */
+
+static void
+ptrace_him (pid)
+ int pid;
+{
+ push_target (&child_ops);
+}
+
+/* Start an inferior Unix child process and sets inferior_pid to its pid.
+ EXEC_FILE is the file to run.
+ ALLARGS is a string containing the arguments to the program.
+ ENV is the environment vector to pass. Errors reported with error(). */
+
+static void
+child_create_inferior (exec_file, allargs, env)
+ char *exec_file;
+ char *allargs;
+ char **env;
+{
+ fork_inferior (exec_file, allargs, env, ptrace_me, ptrace_him);
+ /* We are at the first instruction we care about. */
+ /* Pedal to the metal... */
+ proceed ((CORE_ADDR) -1, 0, 0);
+}
+
+static void
+child_mourn_inferior ()
+{
+ unpush_target (&child_ops);
+ generic_mourn_inferior ();
+}
+
+static int
+child_can_run ()
+{
+ return(1);
+}
+
+struct target_ops child_ops = {
+ "child", /* to_shortname */
+ "Unix child process", /* to_longname */
+ "Unix child process (started by the \"run\" command).", /* to_doc */
+ child_open, /* to_open */
+ 0, /* to_close */
+ child_attach, /* to_attach */
+ child_detach, /* to_detach */
+ child_resume, /* to_resume */
+ child_wait, /* to_wait */
+ fetch_inferior_registers, /* to_fetch_registers */
+ store_inferior_registers, /* to_store_registers */
+ child_prepare_to_store, /* to_prepare_to_store */
+ child_xfer_memory, /* to_xfer_memory */
+ child_files_info, /* to_files_info */
+ memory_insert_breakpoint, /* to_insert_breakpoint */
+ memory_remove_breakpoint, /* to_remove_breakpoint */
+ terminal_init_inferior, /* to_terminal_init */
+ terminal_inferior, /* to_terminal_inferior */
+ terminal_ours_for_output, /* to_terminal_ours_for_output */
+ terminal_ours, /* to_terminal_ours */
+ child_terminal_info, /* to_terminal_info */
+ kill_inferior, /* to_kill */
+ 0, /* to_load */
+ 0, /* to_lookup_symbol */
+ child_create_inferior, /* to_create_inferior */
+ child_mourn_inferior, /* to_mourn_inferior */
+ child_can_run, /* to_can_run */
+ 0, /* to_notice_signals */
+ process_stratum, /* to_stratum */
+ 0, /* to_next */
+ 1, /* to_has_all_memory */
+ 1, /* to_has_memory */
+ 1, /* to_has_stack */
+ 1, /* to_has_registers */
+ 1, /* to_has_execution */
+ 0, /* sections */
+ 0, /* sections_end */
+ OPS_MAGIC /* to_magic */
+};
+
+void
+_initialize_inftarg ()
+{
+ add_target (&child_ops);
+}
diff --git a/gnu/usr.bin/gdb/gdb/init.c b/gnu/usr.bin/gdb/gdb/init.c
new file mode 100644
index 0000000..26afe1d
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/init.c
@@ -0,0 +1,47 @@
+/* Do not modify this file. It is created automatically by "munch". */
+void initialize_all_files () {
+ {extern void _initialize_blockframe (); _initialize_blockframe ();}
+ {extern void _initialize_breakpoint (); _initialize_breakpoint ();}
+ {extern void _initialize_buildsym (); _initialize_buildsym ();}
+ {extern void _initialize_c_language (); _initialize_c_language ();}
+ {extern void _initialize_chill_language (); _initialize_chill_language ();}
+ {extern void _initialize_coffread (); _initialize_coffread ();}
+ {extern void _initialize_command (); _initialize_command ();}
+ {extern void _initialize_complaints (); _initialize_complaints ();}
+ {extern void _initialize_copying (); _initialize_copying ();}
+ {extern void _initialize_core (); _initialize_core ();}
+ {extern void _initialize_corelow (); _initialize_corelow ();}
+ {extern void _initialize_cp_valprint (); _initialize_cp_valprint ();}
+ {extern void _initialize_dbxread (); _initialize_dbxread ();}
+ {extern void _initialize_demangler (); _initialize_demangler ();}
+ {extern void _initialize_elfread (); _initialize_elfread ();}
+ {extern void _initialize_exec (); _initialize_exec ();}
+ {extern void _initialize_gdbtypes (); _initialize_gdbtypes ();}
+ {extern void _initialize_infcmd (); _initialize_infcmd ();}
+ {extern void _initialize_inflow (); _initialize_inflow ();}
+ {extern void _initialize_infrun (); _initialize_infrun ();}
+ {extern void _initialize_inftarg (); _initialize_inftarg ();}
+ {extern void _initialize_language (); _initialize_language ();}
+ {extern void _initialize_m2_language (); _initialize_m2_language ();}
+ {extern void _initialize_maint_cmds (); _initialize_maint_cmds ();}
+ {extern void _initialize_mipsread (); _initialize_mipsread ();}
+ {extern void _initialize_nlmread (); _initialize_nlmread ();}
+ {extern void _initialize_parse (); _initialize_parse ();}
+ {extern void _initialize_printcmd (); _initialize_printcmd ();}
+ {extern void _initialize_remote (); _initialize_remote ();}
+ {extern void _initialize_ser_hardwire (); _initialize_ser_hardwire ();}
+ {extern void _initialize_solib (); _initialize_solib ();}
+ {extern void _initialize_source (); _initialize_source ();}
+ {extern void _initialize_sr_support (); _initialize_sr_support ();}
+ {extern void _initialize_stabsread (); _initialize_stabsread ();}
+ {extern void _initialize_stack (); _initialize_stack ();}
+ {extern void _initialize_symfile (); _initialize_symfile ();}
+ {extern void _initialize_symmisc (); _initialize_symmisc ();}
+ {extern void _initialize_symtab (); _initialize_symtab ();}
+ {extern void _initialize_targets (); _initialize_targets ();}
+ {extern void _initialize_thread (); _initialize_thread ();}
+ {extern void _initialize_typeprint (); _initialize_typeprint ();}
+ {extern void _initialize_utils (); _initialize_utils ();}
+ {extern void _initialize_valprint (); _initialize_valprint ();}
+ {extern void _initialize_values (); _initialize_values ();}
+}
diff --git a/gnu/usr.bin/gdb/gdb/language.c b/gnu/usr.bin/gdb/gdb/language.c
new file mode 100644
index 0000000..be51953
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/language.c
@@ -0,0 +1,1332 @@
+/* Multiple source language support for GDB.
+ Copyright 1991, 1992 Free Software Foundation, Inc.
+ Contributed by the Department of Computer Science at the State University
+ of New York at Buffalo.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This file contains functions that return things that are specific
+ to languages. Each function should examine current_language if necessary,
+ and return the appropriate result. */
+
+/* FIXME: Most of these would be better organized as macros which
+ return data out of a "language-specific" struct pointer that is set
+ whenever the working language changes. That would be a lot faster. */
+
+#include "defs.h"
+#include <string.h>
+#include <varargs.h>
+
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "value.h"
+#include "gdbcmd.h"
+#include "frame.h"
+#include "expression.h"
+#include "language.h"
+#include "target.h"
+#include "parser-defs.h"
+
+static void
+show_language_command PARAMS ((char *, int));
+
+static void
+set_language_command PARAMS ((char *, int));
+
+static void
+show_type_command PARAMS ((char *, int));
+
+static void
+set_type_command PARAMS ((char *, int));
+
+static void
+show_range_command PARAMS ((char *, int));
+
+static void
+set_range_command PARAMS ((char *, int));
+
+static void
+set_range_str PARAMS ((void));
+
+static void
+set_type_str PARAMS ((void));
+
+static void
+set_lang_str PARAMS ((void));
+
+static void
+unk_lang_error PARAMS ((char *));
+
+static int
+unk_lang_parser PARAMS ((void));
+
+static void
+show_check PARAMS ((char *, int));
+
+static void
+set_check PARAMS ((char *, int));
+
+static void
+set_type_range PARAMS ((void));
+
+/* Forward declaration */
+extern const struct language_defn unknown_language_defn;
+extern char *warning_pre_print;
+
+/* The current (default at startup) state of type and range checking.
+ (If the modes are set to "auto", though, these are changed based
+ on the default language at startup, and then again based on the
+ language of the first source file. */
+
+enum range_mode range_mode = range_mode_auto;
+enum range_check range_check = range_check_off;
+enum type_mode type_mode = type_mode_auto;
+enum type_check type_check = type_check_off;
+
+/* The current language and language_mode (see language.h) */
+
+const struct language_defn *current_language = &unknown_language_defn;
+enum language_mode language_mode = language_mode_auto;
+
+/* The language that the user expects to be typing in (the language
+ of main(), or the last language we notified them about, or C). */
+
+const struct language_defn *expected_language;
+
+/* The list of supported languages. The list itself is malloc'd. */
+
+static const struct language_defn **languages;
+static unsigned languages_size;
+static unsigned languages_allocsize;
+#define DEFAULT_ALLOCSIZE 4
+
+/* The "set language/type/range" commands all put stuff in these
+ buffers. This is to make them work as set/show commands. The
+ user's string is copied here, then the set_* commands look at
+ them and update them to something that looks nice when it is
+ printed out. */
+
+static char *language;
+static char *type;
+static char *range;
+
+/* Warning issued when current_language and the language of the current
+ frame do not match. */
+char lang_frame_mismatch_warn[] =
+ "Warning: the current language does not match this frame.";
+
+
+/* This page contains the functions corresponding to GDB commands
+ and their helpers. */
+
+/* Show command. Display a warning if the language set
+ does not match the frame. */
+static void
+show_language_command (ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ enum language flang; /* The language of the current frame */
+
+ flang = get_frame_language();
+ if (flang != language_unknown &&
+ language_mode == language_mode_manual &&
+ current_language->la_language != flang)
+ printf_filtered("%s\n",lang_frame_mismatch_warn);
+}
+
+/* Set command. Change the current working language. */
+static void
+set_language_command (ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ int i;
+ enum language flang;
+ char *err_lang;
+
+ /* FIXME -- do this from the list, with HELP. */
+ if (!language || !language[0]) {
+ printf("The currently understood settings are:\n\n");
+ printf ("local or auto Automatic setting based on source file\n");
+ printf ("c Use the C language\n");
+ printf ("c++ Use the C++ language\n");
+ printf ("chill Use the Chill language\n");
+ printf ("modula-2 Use the Modula-2 language\n");
+ /* Restore the silly string. */
+ set_language(current_language->la_language);
+ return;
+ }
+
+ /* Search the list of languages for a match. */
+ for (i = 0; i < languages_size; i++) {
+ if (STREQ (languages[i]->la_name, language)) {
+ /* Found it! Go into manual mode, and use this language. */
+ if (languages[i]->la_language == language_auto) {
+ /* Enter auto mode. Set to the current frame's language, if known. */
+ language_mode = language_mode_auto;
+ flang = get_frame_language();
+ if (flang!=language_unknown)
+ set_language(flang);
+ expected_language = current_language;
+ return;
+ } else {
+ /* Enter manual mode. Set the specified language. */
+ language_mode = language_mode_manual;
+ current_language = languages[i];
+ set_type_range ();
+ set_lang_str();
+ expected_language = current_language;
+ return;
+ }
+ }
+ }
+
+ /* Reset the language (esp. the global string "language") to the
+ correct values. */
+ err_lang=savestring(language,strlen(language));
+ make_cleanup (free, err_lang); /* Free it after error */
+ set_language(current_language->la_language);
+ error ("Unknown language `%s'.",err_lang);
+}
+
+/* Show command. Display a warning if the type setting does
+ not match the current language. */
+static void
+show_type_command(ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ if (type_check != current_language->la_type_check)
+ printf(
+"Warning: the current type check setting does not match the language.\n");
+}
+
+/* Set command. Change the setting for type checking. */
+static void
+set_type_command(ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ if (STREQ(type,"on"))
+ {
+ type_check = type_check_on;
+ type_mode = type_mode_manual;
+ }
+ else if (STREQ(type,"warn"))
+ {
+ type_check = type_check_warn;
+ type_mode = type_mode_manual;
+ }
+ else if (STREQ(type,"off"))
+ {
+ type_check = type_check_off;
+ type_mode = type_mode_manual;
+ }
+ else if (STREQ(type,"auto"))
+ {
+ type_mode = type_mode_auto;
+ set_type_range();
+ /* Avoid hitting the set_type_str call below. We
+ did it in set_type_range. */
+ return;
+ }
+ set_type_str();
+ show_type_command((char *)NULL, from_tty);
+}
+
+/* Show command. Display a warning if the range setting does
+ not match the current language. */
+static void
+show_range_command(ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+
+ if (range_check != current_language->la_range_check)
+ printf(
+"Warning: the current range check setting does not match the language.\n");
+}
+
+/* Set command. Change the setting for range checking. */
+static void
+set_range_command(ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ if (STREQ(range,"on"))
+ {
+ range_check = range_check_on;
+ range_mode = range_mode_manual;
+ }
+ else if (STREQ(range,"warn"))
+ {
+ range_check = range_check_warn;
+ range_mode = range_mode_manual;
+ }
+ else if (STREQ(range,"off"))
+ {
+ range_check = range_check_off;
+ range_mode = range_mode_manual;
+ }
+ else if (STREQ(range,"auto"))
+ {
+ range_mode = range_mode_auto;
+ set_type_range();
+ /* Avoid hitting the set_range_str call below. We
+ did it in set_type_range. */
+ return;
+ }
+ set_range_str();
+ show_range_command((char *)0, from_tty);
+}
+
+/* Set the status of range and type checking based on
+ the current modes and the current language.
+ If SHOW is non-zero, then print out the current language,
+ type and range checking status. */
+static void
+set_type_range()
+{
+
+ if (range_mode == range_mode_auto)
+ range_check = current_language->la_range_check;
+
+ if (type_mode == type_mode_auto)
+ type_check = current_language->la_type_check;
+
+ set_type_str();
+ set_range_str();
+}
+
+/* Set current language to (enum language) LANG. */
+
+void
+set_language(lang)
+ enum language lang;
+{
+ int i;
+
+ for (i = 0; i < languages_size; i++) {
+ if (languages[i]->la_language == lang) {
+ current_language = languages[i];
+ set_type_range ();
+ set_lang_str();
+ break;
+ }
+ }
+}
+
+/* This page contains functions that update the global vars
+ language, type and range. */
+static void
+set_lang_str()
+{
+ char *prefix = "";
+
+ free (language);
+ if (language_mode == language_mode_auto)
+ prefix = "auto; currently ";
+
+ language = concat(prefix, current_language->la_name, NULL);
+}
+
+static void
+set_type_str()
+{
+ char *tmp, *prefix = "";
+
+ free (type);
+ if (type_mode==type_mode_auto)
+ prefix = "auto; currently ";
+
+ switch(type_check)
+ {
+ case type_check_on:
+ tmp = "on";
+ break;
+ case type_check_off:
+ tmp = "off";
+ break;
+ case type_check_warn:
+ tmp = "warn";
+ break;
+ default:
+ error ("Unrecognized type check setting.");
+ }
+
+ type = concat(prefix,tmp,NULL);
+}
+
+static void
+set_range_str()
+{
+ char *tmp, *pref = "";
+
+ free (range);
+ if (range_mode==range_mode_auto)
+ pref = "auto; currently ";
+
+ switch(range_check)
+ {
+ case range_check_on:
+ tmp = "on";
+ break;
+ case range_check_off:
+ tmp = "off";
+ break;
+ case range_check_warn:
+ tmp = "warn";
+ break;
+ default:
+ error ("Unrecognized range check setting.");
+ }
+
+ range = concat(pref,tmp,NULL);
+}
+
+
+/* Print out the current language settings: language, range and
+ type checking. If QUIETLY, print only what has changed. */
+
+void
+language_info (quietly)
+ int quietly;
+{
+ if (quietly && expected_language == current_language)
+ return;
+
+ expected_language = current_language;
+ printf("Current language: %s\n",language);
+ show_language_command((char *)0, 1);
+
+ if (!quietly)
+ {
+ printf("Type checking: %s\n",type);
+ show_type_command((char *)0, 1);
+ printf("Range checking: %s\n",range);
+ show_range_command((char *)0, 1);
+ }
+}
+
+/* Return the result of a binary operation. */
+
+#if 0 /* Currently unused */
+
+struct type *
+binop_result_type(v1,v2)
+ value v1,v2;
+{
+ int l1,l2,size,uns;
+
+ l1 = TYPE_LENGTH(VALUE_TYPE(v1));
+ l2 = TYPE_LENGTH(VALUE_TYPE(v2));
+
+ switch(current_language->la_language)
+ {
+ case language_c:
+ case language_cplus:
+ if (TYPE_CODE(VALUE_TYPE(v1))==TYPE_CODE_FLT)
+ return TYPE_CODE(VALUE_TYPE(v2)) == TYPE_CODE_FLT && l2 > l1 ?
+ VALUE_TYPE(v2) : VALUE_TYPE(v1);
+ else if (TYPE_CODE(VALUE_TYPE(v2))==TYPE_CODE_FLT)
+ return TYPE_CODE(VALUE_TYPE(v1)) == TYPE_CODE_FLT && l1 > l2 ?
+ VALUE_TYPE(v1) : VALUE_TYPE(v2);
+ else if (TYPE_UNSIGNED(VALUE_TYPE(v1)) && l1 > l2)
+ return VALUE_TYPE(v1);
+ else if (TYPE_UNSIGNED(VALUE_TYPE(v2)) && l2 > l1)
+ return VALUE_TYPE(v2);
+ else /* Both are signed. Result is the longer type */
+ return l1 > l2 ? VALUE_TYPE(v1) : VALUE_TYPE(v2);
+ break;
+ case language_m2:
+ /* If we are doing type-checking, l1 should equal l2, so this is
+ not needed. */
+ return l1 > l2 ? VALUE_TYPE(v1) : VALUE_TYPE(v2);
+ break;
+ case language_chill:
+ error ("Missing Chill support in function binop_result_check.");/*FIXME*/
+ }
+ abort();
+ return (struct type *)0; /* For lint */
+}
+
+#endif /* 0 */
+
+
+/* This page contains functions that return format strings for
+ printf for printing out numbers in different formats */
+
+/* Returns the appropriate printf format for hexadecimal
+ numbers. */
+char *
+local_hex_format_custom(pre)
+ char *pre;
+{
+ static char form[50];
+
+ strcpy (form, local_hex_format_prefix ());
+ strcat (form, "%");
+ strcat (form, pre);
+ strcat (form, local_hex_format_specifier ());
+ strcat (form, local_hex_format_suffix ());
+ return form;
+}
+
+/* Converts a number to hexadecimal and stores it in a static
+ string. Returns a pointer to this string. */
+char *
+local_hex_string (num)
+ unsigned long num;
+{
+ static char res[50];
+
+ sprintf (res, local_hex_format(), num);
+ return res;
+}
+
+/* Converts a number to custom hexadecimal and stores it in a static
+ string. Returns a pointer to this string. */
+char *
+local_hex_string_custom(num,pre)
+ unsigned long num;
+ char *pre;
+{
+ static char res[50];
+
+ sprintf (res, local_hex_format_custom(pre), num);
+ return res;
+}
+
+/* Returns the appropriate printf format for octal
+ numbers. */
+char *
+local_octal_format_custom(pre)
+ char *pre;
+{
+ static char form[50];
+
+ strcpy (form, local_octal_format_prefix ());
+ strcat (form, "%");
+ strcat (form, pre);
+ strcat (form, local_octal_format_specifier ());
+ strcat (form, local_octal_format_suffix ());
+ return form;
+}
+
+/* Returns the appropriate printf format for decimal numbers. */
+char *
+local_decimal_format_custom(pre)
+ char *pre;
+{
+ static char form[50];
+
+ strcpy (form, local_decimal_format_prefix ());
+ strcat (form, "%");
+ strcat (form, pre);
+ strcat (form, local_decimal_format_specifier ());
+ strcat (form, local_decimal_format_suffix ());
+ return form;
+}
+
+/* This page contains functions that are used in type/range checking.
+ They all return zero if the type/range check fails.
+
+ It is hoped that these will make extending GDB to parse different
+ languages a little easier. These are primarily used in eval.c when
+ evaluating expressions and making sure that their types are correct.
+ Instead of having a mess of conjucted/disjuncted expressions in an "if",
+ the ideas of type can be wrapped up in the following functions.
+
+ Note that some of them are not currently dependent upon which language
+ is currently being parsed. For example, floats are the same in
+ C and Modula-2 (ie. the only floating point type has TYPE_CODE of
+ TYPE_CODE_FLT), while booleans are different. */
+
+/* Returns non-zero if its argument is a simple type. This is the same for
+ both Modula-2 and for C. In the C case, TYPE_CODE_CHAR will never occur,
+ and thus will never cause the failure of the test. */
+int
+simple_type(type)
+ struct type *type;
+{
+ switch (TYPE_CODE (type)) {
+ case TYPE_CODE_INT:
+ case TYPE_CODE_CHAR:
+ case TYPE_CODE_ENUM:
+ case TYPE_CODE_FLT:
+ case TYPE_CODE_RANGE:
+ case TYPE_CODE_BOOL:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/* Returns non-zero if its argument is of an ordered type.
+ An ordered type is one in which the elements can be tested for the
+ properties of "greater than", "less than", etc, or for which the
+ operations "increment" or "decrement" make sense. */
+int
+ordered_type (type)
+ struct type *type;
+{
+ switch (TYPE_CODE (type)) {
+ case TYPE_CODE_INT:
+ case TYPE_CODE_CHAR:
+ case TYPE_CODE_ENUM:
+ case TYPE_CODE_FLT:
+ case TYPE_CODE_RANGE:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/* Returns non-zero if the two types are the same */
+int
+same_type (arg1, arg2)
+ struct type *arg1, *arg2;
+{
+ if (structured_type(arg1) ? !structured_type(arg2) : structured_type(arg2))
+ /* One is structured and one isn't */
+ return 0;
+ else if (structured_type(arg1) && structured_type(arg2))
+ return arg1 == arg2;
+ else if (numeric_type(arg1) && numeric_type(arg2))
+ return (TYPE_CODE(arg2) == TYPE_CODE(arg1)) &&
+ (TYPE_UNSIGNED(arg1) == TYPE_UNSIGNED(arg2))
+ ? 1 : 0;
+ else
+ return arg1==arg2;
+}
+
+/* Returns non-zero if the type is integral */
+int
+integral_type (type)
+ struct type *type;
+{
+ switch(current_language->la_language)
+ {
+ case language_c:
+ case language_cplus:
+ return (TYPE_CODE(type) != TYPE_CODE_INT) &&
+ (TYPE_CODE(type) != TYPE_CODE_ENUM) ? 0 : 1;
+ case language_m2:
+ return TYPE_CODE(type) != TYPE_CODE_INT ? 0 : 1;
+ case language_chill:
+ error ("Missing Chill support in function integral_type."); /*FIXME*/
+ default:
+ error ("Language not supported.");
+ }
+}
+
+/* Returns non-zero if the value is numeric */
+int
+numeric_type (type)
+ struct type *type;
+{
+ switch (TYPE_CODE (type)) {
+ case TYPE_CODE_INT:
+ case TYPE_CODE_FLT:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/* Returns non-zero if the value is a character type */
+int
+character_type (type)
+ struct type *type;
+{
+ switch(current_language->la_language)
+ {
+ case language_chill:
+ case language_m2:
+ return TYPE_CODE(type) != TYPE_CODE_CHAR ? 0 : 1;
+
+ case language_c:
+ case language_cplus:
+ return (TYPE_CODE(type) == TYPE_CODE_INT) &&
+ TYPE_LENGTH(type) == sizeof(char)
+ ? 1 : 0;
+ default:
+ return (0);
+ }
+}
+
+/* Returns non-zero if the value is a string type */
+int
+string_type (type)
+ struct type *type;
+{
+ switch(current_language->la_language)
+ {
+ case language_chill:
+ case language_m2:
+ return TYPE_CODE(type) != TYPE_CODE_STRING ? 0 : 1;
+
+ case language_c:
+ case language_cplus:
+ /* C does not have distinct string type. */
+ return (0);
+ default:
+ return (0);
+ }
+}
+
+/* Returns non-zero if the value is a boolean type */
+int
+boolean_type (type)
+ struct type *type;
+{
+ switch(current_language->la_language)
+ {
+ case language_chill:
+ case language_m2:
+ return TYPE_CODE(type) != TYPE_CODE_BOOL ? 0 : 1;
+
+ case language_c:
+ case language_cplus:
+ return TYPE_CODE(type) != TYPE_CODE_INT ? 0 : 1;
+ default:
+ return (0);
+ }
+}
+
+/* Returns non-zero if the value is a floating-point type */
+int
+float_type (type)
+ struct type *type;
+{
+ return TYPE_CODE(type) == TYPE_CODE_FLT;
+}
+
+/* Returns non-zero if the value is a pointer type */
+int
+pointer_type(type)
+ struct type *type;
+{
+ return TYPE_CODE(type) == TYPE_CODE_PTR ||
+ TYPE_CODE(type) == TYPE_CODE_REF;
+}
+
+/* Returns non-zero if the value is a structured type */
+int
+structured_type(type)
+ struct type *type;
+{
+ switch(current_language->la_language)
+ {
+ case language_c:
+ case language_cplus:
+ return (TYPE_CODE(type) == TYPE_CODE_STRUCT) ||
+ (TYPE_CODE(type) == TYPE_CODE_UNION) ||
+ (TYPE_CODE(type) == TYPE_CODE_ARRAY);
+ case language_m2:
+ return (TYPE_CODE(type) == TYPE_CODE_STRUCT) ||
+ (TYPE_CODE(type) == TYPE_CODE_SET) ||
+ (TYPE_CODE(type) == TYPE_CODE_ARRAY);
+ case language_chill:
+ error ("Missing Chill support in function structured_type."); /*FIXME*/
+ default:
+ return (0);
+ }
+}
+
+/* This page contains functions that return info about
+ (struct value) values used in GDB. */
+
+/* Returns non-zero if the value VAL represents a true value. */
+int
+value_true(val)
+ value val;
+{
+ int len, i;
+ struct type *type;
+ LONGEST v;
+
+ switch (current_language->la_language) {
+
+ case language_c:
+ case language_cplus:
+ return !value_logical_not (val);
+
+ case language_m2:
+ type = VALUE_TYPE(val);
+ if (TYPE_CODE (type) != TYPE_CODE_BOOL)
+ return 0; /* Not a BOOLEAN at all */
+ /* Search the fields for one that matches the current value. */
+ len = TYPE_NFIELDS (type);
+ v = value_as_long (val);
+ for (i = 0; i < len; i++)
+ {
+ QUIT;
+ if (v == TYPE_FIELD_BITPOS (type, i))
+ break;
+ }
+ if (i >= len)
+ return 0; /* Not a valid BOOLEAN value */
+ if (STREQ ("TRUE", TYPE_FIELD_NAME(VALUE_TYPE(val), i)))
+ return 1; /* BOOLEAN with value TRUE */
+ else
+ return 0; /* BOOLEAN with value FALSE */
+ break;
+
+ case language_chill:
+ error ("Missing Chill support in function value_type."); /*FIXME*/
+
+ default:
+ error ("Language not supported.");
+ }
+}
+
+/* Returns non-zero if the operator OP is defined on
+ the values ARG1 and ARG2. */
+
+#if 0 /* Currently unused */
+
+void
+binop_type_check(arg1,arg2,op)
+ value arg1,arg2;
+ int op;
+{
+ struct type *t1, *t2;
+
+ /* If we're not checking types, always return success. */
+ if (!STRICT_TYPE)
+ return;
+
+ t1=VALUE_TYPE(arg1);
+ if (arg2!=(value)NULL)
+ t2=VALUE_TYPE(arg2);
+ else
+ t2=NULL;
+
+ switch(op)
+ {
+ case BINOP_ADD:
+ case BINOP_SUB:
+ if ((numeric_type(t1) && pointer_type(t2)) ||
+ (pointer_type(t1) && numeric_type(t2)))
+ {
+ warning ("combining pointer and integer.\n");
+ break;
+ }
+ case BINOP_MUL:
+ case BINOP_LSH:
+ case BINOP_RSH:
+ if (!numeric_type(t1) || !numeric_type(t2))
+ type_op_error ("Arguments to %s must be numbers.",op);
+ else if (!same_type(t1,t2))
+ type_op_error ("Arguments to %s must be of the same type.",op);
+ break;
+
+ case BINOP_LOGICAL_AND:
+ case BINOP_LOGICAL_OR:
+ if (!boolean_type(t1) || !boolean_type(t2))
+ type_op_error ("Arguments to %s must be of boolean type.",op);
+ break;
+
+ case BINOP_EQUAL:
+ if ((pointer_type(t1) && !(pointer_type(t2) || integral_type(t2))) ||
+ (pointer_type(t2) && !(pointer_type(t1) || integral_type(t1))))
+ type_op_error ("A pointer can only be compared to an integer or pointer.",op);
+ else if ((pointer_type(t1) && integral_type(t2)) ||
+ (integral_type(t1) && pointer_type(t2)))
+ {
+ warning ("combining integer and pointer.\n");
+ break;
+ }
+ else if (!simple_type(t1) || !simple_type(t2))
+ type_op_error ("Arguments to %s must be of simple type.",op);
+ else if (!same_type(t1,t2))
+ type_op_error ("Arguments to %s must be of the same type.",op);
+ break;
+
+ case BINOP_REM:
+ case BINOP_MOD:
+ if (!integral_type(t1) || !integral_type(t2))
+ type_op_error ("Arguments to %s must be of integral type.",op);
+ break;
+
+ case BINOP_LESS:
+ case BINOP_GTR:
+ case BINOP_LEQ:
+ case BINOP_GEQ:
+ if (!ordered_type(t1) || !ordered_type(t2))
+ type_op_error ("Arguments to %s must be of ordered type.",op);
+ else if (!same_type(t1,t2))
+ type_op_error ("Arguments to %s must be of the same type.",op);
+ break;
+
+ case BINOP_ASSIGN:
+ if (pointer_type(t1) && !integral_type(t2))
+ type_op_error ("A pointer can only be assigned an integer.",op);
+ else if (pointer_type(t1) && integral_type(t2))
+ {
+ warning ("combining integer and pointer.");
+ break;
+ }
+ else if (!simple_type(t1) || !simple_type(t2))
+ type_op_error ("Arguments to %s must be of simple type.",op);
+ else if (!same_type(t1,t2))
+ type_op_error ("Arguments to %s must be of the same type.",op);
+ break;
+
+ case BINOP_CONCAT:
+ /* FIXME: Needs to handle bitstrings as well. */
+ if (!(string_type(t1) || character_type(t1) || integral_type(t1))
+ || !(string_type(t2) || character_type(t2) || integral_type(t2)))
+ type_op_error ("Arguments to %s must be strings or characters.", op);
+ break;
+
+ /* Unary checks -- arg2 is null */
+
+ case UNOP_LOGICAL_NOT:
+ if (!boolean_type(t1))
+ type_op_error ("Argument to %s must be of boolean type.",op);
+ break;
+
+ case UNOP_PLUS:
+ case UNOP_NEG:
+ if (!numeric_type(t1))
+ type_op_error ("Argument to %s must be of numeric type.",op);
+ break;
+
+ case UNOP_IND:
+ if (integral_type(t1))
+ {
+ warning ("combining pointer and integer.\n");
+ break;
+ }
+ else if (!pointer_type(t1))
+ type_op_error ("Argument to %s must be a pointer.",op);
+ break;
+
+ case UNOP_PREINCREMENT:
+ case UNOP_POSTINCREMENT:
+ case UNOP_PREDECREMENT:
+ case UNOP_POSTDECREMENT:
+ if (!ordered_type(t1))
+ type_op_error ("Argument to %s must be of an ordered type.",op);
+ break;
+
+ default:
+ /* Ok. The following operators have different meanings in
+ different languages. */
+ switch(current_language->la_language)
+ {
+#ifdef _LANG_c
+ case language_c:
+ case language_cplus:
+ switch(op)
+ {
+ case BINOP_DIV:
+ if (!numeric_type(t1) || !numeric_type(t2))
+ type_op_error ("Arguments to %s must be numbers.",op);
+ break;
+ }
+ break;
+#endif
+
+#ifdef _LANG_m2
+ case language_m2:
+ switch(op)
+ {
+ case BINOP_DIV:
+ if (!float_type(t1) || !float_type(t2))
+ type_op_error ("Arguments to %s must be floating point numbers.",op);
+ break;
+ case BINOP_INTDIV:
+ if (!integral_type(t1) || !integral_type(t2))
+ type_op_error ("Arguments to %s must be of integral type.",op);
+ break;
+ }
+#endif
+
+#ifdef _LANG_chill
+ case language_chill:
+ error ("Missing Chill support in function binop_type_check.");/*FIXME*/
+#endif
+
+ }
+ }
+}
+
+#endif /* 0 */
+
+
+/* This page contains functions for the printing out of
+ error messages that occur during type- and range-
+ checking. */
+
+/* Prints the format string FMT with the operator as a string
+ corresponding to the opcode OP. If FATAL is non-zero, then
+ this is an error and error () is called. Otherwise, it is
+ a warning and printf() is called. */
+void
+op_error (fmt,op,fatal)
+ char *fmt;
+ enum exp_opcode op;
+ int fatal;
+{
+ if (fatal)
+ error (fmt,op_string(op));
+ else
+ {
+ warning (fmt,op_string(op));
+ }
+}
+
+/* These are called when a language fails a type- or range-check.
+ The first argument should be a printf()-style format string, and
+ the rest of the arguments should be its arguments. If
+ [type|range]_check is [type|range]_check_on, then return_to_top_level()
+ is called in the style of error (). Otherwise, the message is prefixed
+ by the value of warning_pre_print and we do not return to the top level. */
+
+void
+type_error (va_alist)
+ va_dcl
+{
+ va_list args;
+ char *string;
+
+ if (type_check==type_check_warn)
+ fprintf(stderr,warning_pre_print);
+ else
+ target_terminal_ours();
+
+ va_start (args);
+ string = va_arg (args, char *);
+ vfprintf (stderr, string, args);
+ fprintf (stderr, "\n");
+ va_end (args);
+ if (type_check==type_check_on)
+ return_to_top_level (RETURN_ERROR);
+}
+
+void
+range_error (va_alist)
+ va_dcl
+{
+ va_list args;
+ char *string;
+
+ if (range_check==range_check_warn)
+ fprintf(stderr,warning_pre_print);
+ else
+ target_terminal_ours();
+
+ va_start (args);
+ string = va_arg (args, char *);
+ vfprintf (stderr, string, args);
+ fprintf (stderr, "\n");
+ va_end (args);
+ if (range_check==range_check_on)
+ return_to_top_level (RETURN_ERROR);
+}
+
+
+/* This page contains miscellaneous functions */
+
+/* Return the language struct for a given language enum. */
+
+const struct language_defn *
+language_def(lang)
+ enum language lang;
+{
+ int i;
+
+ for (i = 0; i < languages_size; i++) {
+ if (languages[i]->la_language == lang) {
+ return languages[i];
+ }
+ }
+ return NULL;
+}
+
+/* Return the language as a string */
+char *
+language_str(lang)
+ enum language lang;
+{
+ int i;
+
+ for (i = 0; i < languages_size; i++) {
+ if (languages[i]->la_language == lang) {
+ return languages[i]->la_name;
+ }
+ }
+ return "Unknown";
+}
+
+static void
+set_check (ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ printf(
+"\"set check\" must be followed by the name of a check subcommand.\n");
+ help_list(setchecklist, "set check ", -1, stdout);
+}
+
+static void
+show_check (ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ cmd_show_list(showchecklist, from_tty, "");
+}
+
+/* Add a language to the set of known languages. */
+
+void
+add_language (lang)
+ const struct language_defn *lang;
+{
+ if (lang->la_magic != LANG_MAGIC)
+ {
+ fprintf(stderr, "Magic number of %s language struct wrong\n",
+ lang->la_name);
+ abort();
+ }
+
+ if (!languages)
+ {
+ languages_allocsize = DEFAULT_ALLOCSIZE;
+ languages = (const struct language_defn **) xmalloc
+ (languages_allocsize * sizeof (*languages));
+ }
+ if (languages_size >= languages_allocsize)
+ {
+ languages_allocsize *= 2;
+ languages = (const struct language_defn **) xrealloc ((char *) languages,
+ languages_allocsize * sizeof (*languages));
+ }
+ languages[languages_size++] = lang;
+}
+
+/* Define the language that is no language. */
+
+static int
+unk_lang_parser ()
+{
+ return 1;
+}
+
+static void
+unk_lang_error (msg)
+ char *msg;
+{
+ error ("Attempted to parse an expression with unknown language");
+}
+
+static void
+unk_lang_printchar (c, stream)
+ register int c;
+ FILE *stream;
+{
+ error ("internal error - unimplemented function unk_lang_printchar called.");
+}
+
+static void
+unk_lang_printstr (stream, string, length, force_ellipses)
+ FILE *stream;
+ char *string;
+ unsigned int length;
+ int force_ellipses;
+{
+ error ("internal error - unimplemented function unk_lang_printstr called.");
+}
+
+static struct type *
+unk_lang_create_fundamental_type (objfile, typeid)
+ struct objfile *objfile;
+ int typeid;
+{
+ error ("internal error - unimplemented function unk_lang_create_fundamental_type called.");
+}
+
+void
+unk_lang_print_type (type, varstring, stream, show, level)
+ struct type *type;
+ char *varstring;
+ FILE *stream;
+ int show;
+ int level;
+{
+ error ("internal error - unimplemented function unk_lang_print_type called.");
+}
+
+int
+unk_lang_val_print (type, valaddr, address, stream, format, deref_ref,
+ recurse, pretty)
+ struct type *type;
+ char *valaddr;
+ CORE_ADDR address;
+ FILE *stream;
+ int format;
+ int deref_ref;
+ int recurse;
+ enum val_prettyprint pretty;
+{
+ error ("internal error - unimplemented function unk_lang_val_print called.");
+}
+
+static struct type ** const (unknown_builtin_types[]) = { 0 };
+static const struct op_print unk_op_print_tab[] = {
+ {NULL, OP_NULL, PREC_NULL, 0}
+};
+
+const struct language_defn unknown_language_defn = {
+ "unknown",
+ language_unknown,
+ &unknown_builtin_types[0],
+ range_check_off,
+ type_check_off,
+ unk_lang_parser,
+ unk_lang_error,
+ unk_lang_printchar, /* Print character constant */
+ unk_lang_printstr,
+ unk_lang_create_fundamental_type,
+ unk_lang_print_type, /* Print a type using appropriate syntax */
+ unk_lang_val_print, /* Print a value using appropriate syntax */
+ &builtin_type_error, /* longest signed integral type */
+ &builtin_type_error, /* longest unsigned integral type */
+ &builtin_type_error, /* longest floating point type */
+ {"", "", "", ""}, /* Binary format info */
+ {"0%lo", "0", "o", ""}, /* Octal format info */
+ {"%ld", "", "d", ""}, /* Decimal format info */
+ {"0x%lx", "0x", "x", ""}, /* Hex format info */
+ unk_op_print_tab, /* expression operators for printing */
+ LANG_MAGIC
+};
+
+/* These two structs define fake entries for the "local" and "auto" options. */
+const struct language_defn auto_language_defn = {
+ "auto",
+ language_auto,
+ &unknown_builtin_types[0],
+ range_check_off,
+ type_check_off,
+ unk_lang_parser,
+ unk_lang_error,
+ unk_lang_printchar, /* Print character constant */
+ unk_lang_printstr,
+ unk_lang_create_fundamental_type,
+ unk_lang_print_type, /* Print a type using appropriate syntax */
+ unk_lang_val_print, /* Print a value using appropriate syntax */
+ &builtin_type_error, /* longest signed integral type */
+ &builtin_type_error, /* longest unsigned integral type */
+ &builtin_type_error, /* longest floating point type */
+ {"", "", "", ""}, /* Binary format info */
+ {"0%lo", "0", "o", ""}, /* Octal format info */
+ {"%ld", "", "d", ""}, /* Decimal format info */
+ {"0x%lx", "0x", "x", ""}, /* Hex format info */
+ unk_op_print_tab, /* expression operators for printing */
+ LANG_MAGIC
+};
+
+const struct language_defn local_language_defn = {
+ "local",
+ language_auto,
+ &unknown_builtin_types[0],
+ range_check_off,
+ type_check_off,
+ unk_lang_parser,
+ unk_lang_error,
+ unk_lang_printchar, /* Print character constant */
+ unk_lang_printstr,
+ unk_lang_create_fundamental_type,
+ unk_lang_print_type, /* Print a type using appropriate syntax */
+ unk_lang_val_print, /* Print a value using appropriate syntax */
+ &builtin_type_error, /* longest signed integral type */
+ &builtin_type_error, /* longest unsigned integral type */
+ &builtin_type_error, /* longest floating point type */
+ {"", "", "", ""}, /* Binary format info */
+ {"0%lo", "0", "o", ""}, /* Octal format info */
+ {"%ld", "", "d", ""}, /* Decimal format info */
+ {"0x%lx", "0x", "x", ""}, /* Hex format info */
+ unk_op_print_tab, /* expression operators for printing */
+ LANG_MAGIC
+};
+
+/* Initialize the language routines */
+
+void
+_initialize_language()
+{
+ struct cmd_list_element *set, *show;
+
+ /* GDB commands for language specific stuff */
+
+ set = add_set_cmd ("language", class_support, var_string_noescape,
+ (char *)&language,
+ "Set the current source language.",
+ &setlist);
+ show = add_show_from_set (set, &showlist);
+ set->function.cfunc = set_language_command;
+ show->function.cfunc = show_language_command;
+
+ add_prefix_cmd ("check", no_class, set_check,
+ "Set the status of the type/range checker",
+ &setchecklist, "set check ", 0, &setlist);
+ add_alias_cmd ("c", "check", no_class, 1, &setlist);
+ add_alias_cmd ("ch", "check", no_class, 1, &setlist);
+
+ add_prefix_cmd ("check", no_class, show_check,
+ "Show the status of the type/range checker",
+ &showchecklist, "show check ", 0, &showlist);
+ add_alias_cmd ("c", "check", no_class, 1, &showlist);
+ add_alias_cmd ("ch", "check", no_class, 1, &showlist);
+
+ set = add_set_cmd ("type", class_support, var_string_noescape,
+ (char *)&type,
+ "Set type checking. (on/warn/off/auto)",
+ &setchecklist);
+ show = add_show_from_set (set, &showchecklist);
+ set->function.cfunc = set_type_command;
+ show->function.cfunc = show_type_command;
+
+ set = add_set_cmd ("range", class_support, var_string_noescape,
+ (char *)&range,
+ "Set range checking. (on/warn/off/auto)",
+ &setchecklist);
+ show = add_show_from_set (set, &showchecklist);
+ set->function.cfunc = set_range_command;
+ show->function.cfunc = show_range_command;
+
+ add_language (&unknown_language_defn);
+ add_language (&local_language_defn);
+ add_language (&auto_language_defn);
+
+ language = savestring ("auto",strlen("auto"));
+ range = savestring ("auto",strlen("auto"));
+ type = savestring ("auto",strlen("auto"));
+
+ /* Have the above take effect */
+
+ set_language_command (language, 0);
+ set_type_command (NULL, 0);
+ set_range_command (NULL, 0);
+}
diff --git a/gnu/usr.bin/gdb/gdb/language.h b/gnu/usr.bin/gdb/gdb/language.h
new file mode 100644
index 0000000..9df5345
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/language.h
@@ -0,0 +1,413 @@
+/* Source-language-related definitions for GDB.
+ Copyright 1991, 1992 Free Software Foundation, Inc.
+ Contributed by the Department of Computer Science at the State University
+ of New York at Buffalo.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (LANGUAGE_H)
+#define LANGUAGE_H 1
+
+#ifdef __STDC__ /* Forward decls for prototypes */
+struct value;
+struct objfile;
+/* enum exp_opcode; ANSI's `wisdom' didn't include forward enum decls. */
+#endif
+
+/* This used to be included to configure GDB for one or more specific
+ languages. Now it is shortcutted to configure for all of them. FIXME. */
+/* #include "lang_def.h" */
+#define _LANG_c
+#define _LANG_m2
+#define _LANG_chill
+
+/* range_mode ==
+ range_mode_auto: range_check set automatically to default of language.
+ range_mode_manual: range_check set manually by user. */
+
+extern enum range_mode {range_mode_auto, range_mode_manual} range_mode;
+
+/* range_check ==
+ range_check_on: Ranges are checked in GDB expressions, producing errors.
+ range_check_warn: Ranges are checked, producing warnings.
+ range_check_off: Ranges are not checked in GDB expressions. */
+
+extern enum range_check
+ {range_check_off, range_check_warn, range_check_on} range_check;
+
+/* type_mode ==
+ type_mode_auto: type_check set automatically to default of language
+ type_mode_manual: type_check set manually by user. */
+
+extern enum type_mode {type_mode_auto, type_mode_manual} type_mode;
+
+/* type_check ==
+ type_check_on: Types are checked in GDB expressions, producing errors.
+ type_check_warn: Types are checked, producing warnings.
+ type_check_off: Types are not checked in GDB expressions. */
+
+extern enum type_check
+ {type_check_off, type_check_warn, type_check_on} type_check;
+
+/* Information for doing language dependent formatting of printed values. */
+
+struct language_format_info
+{
+ /* The format that can be passed directly to standard C printf functions
+ to generate a completely formatted value in the format appropriate for
+ the language. */
+
+ char *la_format;
+
+ /* The prefix to be used when directly printing a value, or constructing
+ a standard C printf format. This generally is everything up to the
+ conversion specification (the part introduced by the '%' character
+ and terminated by the conversion specifier character). */
+
+ char *la_format_prefix;
+
+ /* The conversion specifier. This is generally everything after the
+ field width and precision, typically only a single character such
+ as 'o' for octal format or 'x' for hexadecimal format. */
+
+ char *la_format_specifier;
+
+ /* The suffix to be used when directly printing a value, or constructing
+ a standard C printf format. This generally is everything after the
+ conversion specification (the part introduced by the '%' character
+ and terminated by the conversion specifier character). */
+
+ char *la_format_suffix; /* Suffix for custom format string */
+};
+
+/* Structure tying together assorted information about a language. */
+
+struct language_defn
+{
+ /* Name of the language */
+
+ char *la_name;
+
+ /* its symtab language-enum (defs.h) */
+
+ enum language la_language;
+
+ /* Its builtin types */
+
+ struct type ** const *la_builtin_type_vector;
+
+ /* Default range checking */
+
+ enum range_check la_range_check;
+
+ /* Default type checking */
+
+ enum type_check la_type_check;
+
+ /* Parser function. */
+
+ int (*la_parser) PARAMS((void));
+
+ /* Parser error function */
+
+ void (*la_error) PARAMS ((char *));
+
+ void (*la_printchar) PARAMS ((int, FILE *));
+
+ void (*la_printstr) PARAMS ((FILE *, char *, unsigned int, int));
+
+ struct type *(*la_fund_type) PARAMS ((struct objfile *, int));
+
+ /* Print a type using syntax appropriate for this language. */
+
+ void (*la_print_type) PARAMS ((struct type *, char *, FILE *, int, int));
+
+ /* Print a value using syntax appropriate for this language. */
+
+ int (*la_val_print) PARAMS ((struct type *, char *, CORE_ADDR, FILE *,
+ int, int, int, enum val_prettyprint));
+
+ /* Longest signed integral type */
+
+ struct type **la_longest_int;
+
+ /* Longest unsigned integral type */
+
+ struct type **la_longest_unsigned_int;
+
+ /* Longest floating point type */
+
+ struct type **la_longest_float;
+
+ /* Base 2 (binary) formats. */
+
+ struct language_format_info la_binary_format;
+
+ /* Base 8 (octal) formats. */
+
+ struct language_format_info la_octal_format;
+
+ /* Base 10 (decimal) formats */
+
+ struct language_format_info la_decimal_format;
+
+ /* Base 16 (hexadecimal) formats */
+
+ struct language_format_info la_hex_format;
+
+
+ /* Table for printing expressions */
+
+ const struct op_print *la_op_print_tab;
+
+ /* Add fields above this point, so the magic number is always last. */
+ /* Magic number for compat checking */
+
+ long la_magic;
+
+};
+
+#define LANG_MAGIC 910823L
+
+/* Pointer to the language_defn for our current language. This pointer
+ always points to *some* valid struct; it can be used without checking
+ it for validity.
+
+ The current language affects expression parsing and evaluation
+ (FIXME: it might be cleaner to make the evaluation-related stuff
+ separate exp_opcodes for each different set of semantics. We
+ should at least think this through more clearly with respect to
+ what happens if the language is changed between parsing and
+ evaluation) and printing of things like types and arrays. It does
+ *not* affect symbol-reading-- each source file in a symbol-file has
+ its own language and we should keep track of that regardless of the
+ language when symbols are read. If we want some manual setting for
+ the language of symbol files (e.g. detecting when ".c" files are
+ C++), it should be a seprate setting from the current_language. */
+
+extern const struct language_defn *current_language;
+
+/* Pointer to the language_defn expected by the user, e.g. the language
+ of main(), or the language we last mentioned in a message, or C. */
+
+extern const struct language_defn *expected_language;
+
+/* language_mode ==
+ language_mode_auto: current_language automatically set upon selection
+ of scope (e.g. stack frame)
+ language_mode_manual: current_language set only by user. */
+
+extern enum language_mode
+ {language_mode_auto, language_mode_manual} language_mode;
+
+/* These macros define the behaviour of the expression
+ evaluator. */
+
+/* Should we strictly type check expressions? */
+#define STRICT_TYPE (type_check != type_check_off)
+
+/* Should we range check values against the domain of their type? */
+#define RANGE_CHECK (range_check != range_check_off)
+
+/* "cast" really means conversion */
+/* FIXME -- should be a setting in language_defn */
+#define CAST_IS_CONVERSION (current_language->la_language == language_c || \
+ current_language->la_language == language_cplus)
+
+extern void
+language_info PARAMS ((int));
+
+extern void
+set_language PARAMS ((enum language));
+
+
+/* This page contains functions that return things that are
+ specific to languages. Each of these functions is based on
+ the current setting of working_lang, which the user sets
+ with the "set language" command. */
+
+/* Returns some built-in types */
+#define longest_int() (*current_language->la_longest_int)
+#define longest_unsigned_int() (*current_language->la_longest_unsigned_int)
+#define longest_float() (*current_language->la_longest_float)
+
+#define create_fundamental_type(objfile,typeid) \
+ (current_language->la_fund_type(objfile, typeid))
+
+#define LA_PRINT_TYPE(type,varstring,stream,show,level) \
+ (current_language->la_print_type(type,varstring,stream,show,level))
+
+#define LA_VAL_PRINT(type,valaddr,addr,stream,fmt,deref,recurse,pretty) \
+ (current_language->la_val_print(type,valaddr,addr,stream,fmt,deref, \
+ recurse,pretty))
+
+/* Return a format string for printf that will print a number in one of
+ the local (language-specific) formats. Result is static and is
+ overwritten by the next call. Takes printf options like "08" or "l"
+ (to produce e.g. %08x or %lx). */
+
+#define local_binary_format() \
+ (current_language->la_binary_format.la_format)
+#define local_binary_format_prefix() \
+ (current_language->la_binary_format.la_format_prefix)
+#define local_binary_format_specifier() \
+ (current_language->la_binary_format.la_format_specifier)
+#define local_binary_format_suffix() \
+ (current_language->la_binary_format.la_format_suffix)
+
+#define local_octal_format() \
+ (current_language->la_octal_format.la_format)
+#define local_octal_format_prefix() \
+ (current_language->la_octal_format.la_format_prefix)
+#define local_octal_format_specifier() \
+ (current_language->la_octal_format.la_format_specifier)
+#define local_octal_format_suffix() \
+ (current_language->la_octal_format.la_format_suffix)
+
+#define local_decimal_format() \
+ (current_language->la_decimal_format.la_format)
+#define local_decimal_format_prefix() \
+ (current_language->la_decimal_format.la_format_prefix)
+#define local_decimal_format_specifier() \
+ (current_language->la_decimal_format.la_format_specifier)
+#define local_decimal_format_suffix() \
+ (current_language->la_decimal_format.la_format_suffix)
+
+#define local_hex_format() \
+ (current_language->la_hex_format.la_format)
+#define local_hex_format_prefix() \
+ (current_language->la_hex_format.la_format_prefix)
+#define local_hex_format_specifier() \
+ (current_language->la_hex_format.la_format_specifier)
+#define local_hex_format_suffix() \
+ (current_language->la_hex_format.la_format_suffix)
+
+#define LA_PRINT_CHAR(ch, stream) \
+ (current_language->la_printchar(ch, stream))
+#define LA_PRINT_STRING(stream, string, length, force_ellipses) \
+ (current_language->la_printstr(stream, string, length, force_ellipses))
+
+/* Test a character to decide whether it can be printed in literal form
+ or needs to be printed in another representation. For example,
+ in C the literal form of the character with octal value 141 is 'a'
+ and the "other representation" is '\141'. The "other representation"
+ is program language dependent. */
+
+#define PRINT_LITERAL_FORM(c) \
+ ((c)>=0x20 && ((c)<0x7F || (c)>=0xA0) && (!sevenbit_strings || (c)<0x80))
+
+/* Return a format string for printf that will print a number in one of
+ the local (language-specific) formats. Result is static and is
+ overwritten by the next call. Takes printf options like "08" or "l"
+ (to produce e.g. %08x or %lx). */
+
+extern char *
+local_decimal_format_custom PARAMS ((char *)); /* language.c */
+
+extern char *
+local_octal_format_custom PARAMS ((char *)); /* language.c */
+
+extern char *
+local_hex_format_custom PARAMS ((char *)); /* language.c */
+
+/* Return a string that contains a number formatted in one of the local
+ (language-specific) formats. Result is static and is overwritten by
+ the next call. Takes printf options like "08" or "l". */
+
+extern char *
+local_hex_string PARAMS ((unsigned long)); /* language.c */
+
+extern char *
+local_hex_string_custom PARAMS ((unsigned long, char *)); /* language.c */
+
+/* Type predicates */
+
+extern int
+simple_type PARAMS ((struct type *));
+
+extern int
+ordered_type PARAMS ((struct type *));
+
+extern int
+same_type PARAMS ((struct type *, struct type *));
+
+extern int
+integral_type PARAMS ((struct type *));
+
+extern int
+numeric_type PARAMS ((struct type *));
+
+extern int
+character_type PARAMS ((struct type *));
+
+extern int
+boolean_type PARAMS ((struct type *));
+
+extern int
+float_type PARAMS ((struct type *));
+
+extern int
+pointer_type PARAMS ((struct type *));
+
+extern int
+structured_type PARAMS ((struct type *));
+
+/* Checks Binary and Unary operations for semantic type correctness */
+/* FIXME: Does not appear to be used */
+#define unop_type_check(v,o) binop_type_check((v),NULL,(o))
+
+extern void
+binop_type_check PARAMS ((struct value *, struct value *, int));
+
+/* Error messages */
+
+extern void
+op_error PARAMS ((char *fmt, enum exp_opcode, int));
+
+#define type_op_error(f,o) \
+ op_error((f),(o),type_check==type_check_on ? 1 : 0)
+#define range_op_error(f,o) \
+ op_error((f),(o),range_check==range_check_on ? 1 : 0)
+
+extern void
+type_error ();
+
+void
+range_error ();
+
+/* Data: Does this value represent "truth" to the current language? */
+
+extern int
+value_true PARAMS ((struct value *));
+
+/* Misc: The string representing a particular enum language. */
+
+extern const struct language_defn *
+language_def PARAMS ((enum language));
+
+extern char *
+language_str PARAMS ((enum language));
+
+/* Add a language to the set known by GDB (at initialization time). */
+
+extern void
+add_language PARAMS ((const struct language_defn *));
+
+extern enum language
+get_frame_language PARAMS ((void)); /* In stack.c */
+
+#endif /* defined (LANGUAGE_H) */
diff --git a/gnu/usr.bin/gdb/gdb/m2-exp.tab.c b/gnu/usr.bin/gdb/gdb/m2-exp.tab.c
new file mode 100644
index 0000000..53b1385
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/m2-exp.tab.c
@@ -0,0 +1,1991 @@
+#ifndef lint
+static char yysccsid[] = "@(#)yaccpar 1.9 (Berkeley) 02/21/93";
+#endif
+#define YYBYACC 1
+#define YYMAJOR 1
+#define YYMINOR 9
+#define yyclearin (yychar=(-1))
+#define yyerrok (yyerrflag=0)
+#define YYRECOVERING (yyerrflag!=0)
+#define yyparse m2_parse
+#define yylex m2_lex
+#define yyerror m2_error
+#define yychar m2_char
+#define yyval m2_val
+#define yylval m2_lval
+#define yydebug m2_debug
+#define yynerrs m2_nerrs
+#define yyerrflag m2_errflag
+#define yyss m2_ss
+#define yyssp m2_ssp
+#define yyvs m2_vs
+#define yyvsp m2_vsp
+#define yylhs m2_lhs
+#define yylen m2_len
+#define yydefred m2_defred
+#define yydgoto m2_dgoto
+#define yysindex m2_sindex
+#define yyrindex m2_rindex
+#define yygindex m2_gindex
+#define yytable m2_table
+#define yycheck m2_check
+#define yyname m2_name
+#define yyrule m2_rule
+#define YYPREFIX "m2_"
+#line 40 "./m2-exp.y"
+
+#include "defs.h"
+#include "expression.h"
+#include "language.h"
+#include "value.h"
+#include "parser-defs.h"
+#include "m2-lang.h"
+
+/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
+ as well as gratuitiously global symbol names, so we can have multiple
+ yacc generated parsers in gdb. Note that these are only the variables
+ produced by yacc. If other parser generators (bison, byacc, etc) produce
+ additional global names that conflict at link time, then those parser
+ generators need to be fixed instead of adding those names to this list. */
+
+#define yymaxdepth m2_maxdepth
+#define yyparse m2_parse
+#define yylex m2_lex
+#define yyerror m2_error
+#define yylval m2_lval
+#define yychar m2_char
+#define yydebug m2_debug
+#define yypact m2_pact
+#define yyr1 m2_r1
+#define yyr2 m2_r2
+#define yydef m2_def
+#define yychk m2_chk
+#define yypgo m2_pgo
+#define yyact m2_act
+#define yyexca m2_exca
+#define yyerrflag m2_errflag
+#define yynerrs m2_nerrs
+#define yyps m2_ps
+#define yypv m2_pv
+#define yys m2_s
+#define yy_yys m2_yys
+#define yystate m2_state
+#define yytmp m2_tmp
+#define yyv m2_v
+#define yy_yyv m2_yyv
+#define yyval m2_val
+#define yylloc m2_lloc
+#define yyreds m2_reds /* With YYDEBUG defined */
+#define yytoks m2_toks /* With YYDEBUG defined */
+
+#ifndef YYDEBUG
+#define YYDEBUG 0 /* Default to no yydebug support */
+#endif
+
+int
+yyparse PARAMS ((void));
+
+static int
+yylex PARAMS ((void));
+
+void
+yyerror PARAMS ((char *));
+
+#if 0
+static char *
+make_qualname PARAMS ((char *, char *));
+#endif
+
+static int
+parse_number PARAMS ((int));
+
+/* The sign of the number being parsed. */
+static int number_sign = 1;
+
+/* The block that the module specified by the qualifer on an identifer is
+ contained in, */
+#if 0
+static struct block *modblock=0;
+#endif
+
+#line 121 "./m2-exp.y"
+typedef union
+ {
+ LONGEST lval;
+ unsigned LONGEST ulval;
+ double dval;
+ struct symbol *sym;
+ struct type *tval;
+ struct stoken sval;
+ int voidval;
+ struct block *bval;
+ enum exp_opcode opcode;
+ struct internalvar *ivar;
+
+ struct type **tvec;
+ int *ivec;
+ } YYSTYPE;
+#line 129 "y.tab.c"
+#define INT 257
+#define HEX 258
+#define ERROR 259
+#define UINT 260
+#define M2_TRUE 261
+#define M2_FALSE 262
+#define CHAR 263
+#define FLOAT 264
+#define STRING 265
+#define NAME 266
+#define BLOCKNAME 267
+#define IDENT 268
+#define VARNAME 269
+#define TYPENAME 270
+#define SIZE 271
+#define CAP 272
+#define ORD 273
+#define HIGH 274
+#define ABS 275
+#define MIN_FUNC 276
+#define MAX_FUNC 277
+#define FLOAT_FUNC 278
+#define VAL 279
+#define CHR 280
+#define ODD 281
+#define TRUNC 282
+#define INC 283
+#define DEC 284
+#define INCL 285
+#define EXCL 286
+#define COLONCOLON 287
+#define LAST 288
+#define REGNAME 289
+#define INTERNAL_VAR 290
+#define ABOVE_COMMA 291
+#define ASSIGN 292
+#define LEQ 293
+#define GEQ 294
+#define NOTEQUAL 295
+#define IN 296
+#define OROR 297
+#define LOGICAL_AND 298
+#define DIV 299
+#define MOD 300
+#define UNARY 301
+#define DOT 302
+#define NOT 303
+#define QID 304
+#define YYERRCODE 256
+short m2_lhs[] = { -1,
+ 0, 0, 2, 1, 8, 1, 1, 1, 9, 9,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 3, 3, 12, 1, 13, 1, 10, 10, 10,
+ 11, 11, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 6, 7, 7, 4, 4, 4, 4,
+ 5,
+};
+short m2_len[] = { 2,
+ 1, 1, 1, 2, 0, 3, 2, 2, 1, 1,
+ 4, 4, 4, 4, 4, 4, 4, 6, 4, 4,
+ 4, 2, 4, 6, 4, 6, 3, 1, 3, 6,
+ 6, 3, 4, 0, 5, 0, 5, 0, 1, 3,
+ 1, 3, 4, 4, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 4, 1, 1, 1, 3, 1, 1, 3, 1,
+ 1,
+};
+short m2_defred[] = { 0,
+ 65, 66, 63, 64, 67, 68, 73, 80, 75, 81,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 70, 71, 78, 0,
+ 5, 0, 9, 10, 0, 0, 0, 2, 28, 69,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 4, 0, 34, 36,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 45, 0, 0, 32, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 29, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 27, 0, 0, 0, 0,
+ 79, 76, 72, 11, 12, 14, 13, 15, 16, 17,
+ 0, 19, 20, 21, 0, 23, 0, 25, 0, 0,
+ 0, 0, 0, 0, 0, 44, 33, 0, 0, 0,
+ 0, 0, 0, 35, 37, 18, 24, 26, 30, 31,
+ 0,
+};
+short m2_dgoto[] = { 36,
+ 66, 38, 39, 40, 47, 42, 43, 64, 44, 68,
+ 164, 137, 138,
+};
+short m2_sindex[] = { 1597,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1779, -27, -21, -15, -10, -6, -3, -2, 18, 20,
+ 24, 31, 38, 39, 59, 77, 0, 0, 0, 1597,
+ 0, 1597, 0, 0, 1597, 0, 1670, 0, 0, 0,
+ -26, -256, 0, 1597, 1597, -24, -26, 1597, 1597, 1597,
+ 1597, -218, -218, 1597, -218, 1597, 1597, 1597, 1597, 1597,
+ 1597, 1597, -24, 1597, 939, 1670, -37, -17, 1597, 1597,
+ 1597, 1597, 1597, 1597, 1597, 1597, -118, 1597, 1597, 1597,
+ 1597, 1597, 1597, 1597, 1597, 1597, 0, -186, 0, 0,
+ 1597, 1597, -259, -24, -30, 967, 1002, 1044, 1079, 78,
+ 83, 1160, 74, 1268, 1323, 1351, 866, 894, 1183, 1404,
+ -24, 0, 1597, 1597, 0, 1727, -25, -25, -25, -25,
+ -25, -25, -25, 1597, 0, 8, 80, 192, 117, 49,
+ 49, -24, -24, -24, -24, 0, 1597, 1597, 1449, -11,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1597, 0, 0, 0, 1597, 0, 1597, 0, 1597, 1597,
+ -24, 1670, 1670, -44, -20, 0, 0, 1484, 1512, 1547,
+ 1617, 1628, 1597, 0, 0, 0, 0, 0, 0, 0,
+ 1670,
+};
+short m2_rindex[] = { 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, -9, 0, 121, 0, 0, 0,
+ 135, 0, 1, 0, 0, 12, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 40, 0, 0, -35, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -9, 0, 68, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 106, 0, 0, 0, 0, 98, 568, 575, 598, 653,
+ 677, 779, 838, -9, 0, 0, 561, 539, 502, 465,
+ 489, 134, 145, 220, 411, 0, 0, -12, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 435, -18, -42, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ -16,
+};
+short m2_gindex[] = { 0,
+ 2066, 0, 61, 0, 341, 0, 0, 0, 0, -88,
+ 0, 0, 0,
+};
+#define YYTABLESIZE 2239
+short m2_table[] = { 173,
+ 77, 41, 91, 140, 124, 39, 141, 142, 39, 91,
+ 143, 22, 48, 91, 90, 90, 83, 81, 49, 82,
+ 175, 84, 40, 114, 50, 40, 114, 42, 38, 51,
+ 93, 38, 114, 52, 38, 77, 53, 54, 80, 7,
+ 77, 77, 77, 77, 77, 77, 22, 77, 174, 165,
+ 41, 10, 22, 22, 22, 22, 22, 55, 22, 56,
+ 77, 77, 77, 57, 77, 89, 89, 8, 87, 87,
+ 58, 22, 22, 22, 7, 22, 42, 59, 60, 136,
+ 7, 7, 7, 7, 7, 92, 7, 113, 90, 39,
+ 83, 77, 92, 77, 77, 84, 92, 62, 61, 7,
+ 7, 7, 8, 7, 22, 6, 40, 115, 8, 8,
+ 8, 8, 8, 167, 8, 38, 62, 151, 148, 90,
+ 1, 83, 81, 149, 82, 77, 84, 8, 8, 8,
+ 92, 8, 7, 47, 3, 0, 22, 125, 62, 89,
+ 6, 62, 87, 80, 48, 0, 6, 6, 6, 6,
+ 6, 10, 6, 0, 0, 0, 90, 0, 83, 81,
+ 8, 82, 0, 84, 7, 6, 6, 6, 47, 6,
+ 89, 0, 0, 87, 47, 47, 47, 47, 47, 48,
+ 47, 0, 0, 0, 0, 48, 48, 48, 48, 48,
+ 62, 48, 8, 47, 47, 47, 0, 47, 6, 0,
+ 0, 0, 0, 0, 48, 48, 48, 89, 48, 0,
+ 87, 0, 0, 0, 0, 0, 0, 0, 0, 49,
+ 0, 0, 62, 0, 0, 0, 47, 0, 0, 0,
+ 6, 90, 0, 83, 81, 0, 82, 48, 84, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 49, 80, 0, 0, 47, 0,
+ 49, 49, 49, 49, 49, 0, 49, 0, 0, 48,
+ 0, 78, 79, 85, 86, 0, 88, 88, 0, 49,
+ 49, 49, 89, 49, 0, 87, 0, 74, 0, 0,
+ 0, 0, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 0, 77, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 49, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 41, 0, 0, 0, 49, 0, 0, 85, 86, 0,
+ 88, 0, 0, 0, 0, 0, 0, 0, 0, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 0, 0,
+ 0, 0, 0, 0, 0, 67, 0, 79, 85, 86,
+ 0, 88, 0, 0, 0, 95, 0, 0, 0, 0,
+ 0, 0, 100, 101, 0, 103, 0, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 0, 0, 0, 0,
+ 50, 0, 0, 0, 0, 85, 86, 126, 88, 0,
+ 0, 0, 0, 0, 0, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 43, 0, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 50, 0, 0, 0, 0,
+ 0, 50, 50, 50, 50, 50, 0, 50, 0, 0,
+ 0, 0, 0, 0, 51, 0, 0, 0, 0, 43,
+ 50, 50, 50, 0, 50, 43, 43, 43, 43, 43,
+ 0, 43, 0, 0, 0, 0, 0, 0, 52, 0,
+ 85, 86, 0, 88, 43, 43, 43, 0, 43, 51,
+ 0, 46, 0, 50, 0, 51, 0, 51, 51, 51,
+ 0, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 0, 0, 0, 52, 51, 51, 51, 43, 51, 52,
+ 0, 52, 52, 52, 0, 50, 46, 0, 60, 0,
+ 0, 0, 46, 0, 0, 46, 0, 0, 52, 52,
+ 52, 0, 52, 0, 0, 0, 0, 51, 0, 43,
+ 61, 46, 46, 46, 0, 46, 0, 58, 0, 0,
+ 0, 0, 0, 60, 59, 0, 0, 0, 0, 60,
+ 0, 52, 60, 0, 0, 0, 0, 0, 0, 51,
+ 0, 0, 0, 0, 46, 61, 0, 56, 60, 60,
+ 60, 61, 58, 0, 61, 0, 0, 0, 58, 59,
+ 0, 58, 0, 52, 0, 59, 0, 0, 59, 0,
+ 61, 61, 61, 0, 0, 0, 46, 58, 58, 58,
+ 0, 60, 56, 0, 59, 59, 59, 0, 56, 0,
+ 0, 56, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 57, 61, 0, 0, 0, 56, 56, 56,
+ 58, 0, 0, 60, 0, 0, 0, 59, 0, 0,
+ 0, 0, 0, 0, 0, 0, 53, 0, 0, 0,
+ 0, 0, 0, 0, 0, 61, 0, 57, 0, 0,
+ 56, 0, 58, 57, 0, 0, 57, 0, 0, 59,
+ 0, 0, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 53, 57, 57, 57, 0, 0, 53, 0, 0,
+ 53, 0, 56, 0, 0, 0, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 0, 53, 53, 53, 0,
+ 0, 0, 0, 0, 0, 57, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 51, 51, 51, 51,
+ 51, 51, 51, 0, 0, 0, 0, 0, 0, 53,
+ 0, 0, 0, 0, 0, 0, 0, 57, 54, 0,
+ 52, 52, 52, 52, 52, 52, 52, 0, 0, 0,
+ 0, 0, 0, 46, 46, 46, 46, 46, 46, 46,
+ 0, 53, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 54, 0, 0, 0, 0, 0, 54,
+ 0, 0, 54, 0, 0, 0, 0, 0, 0, 0,
+ 60, 60, 60, 60, 60, 60, 60, 55, 54, 54,
+ 54, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 61, 61, 61, 61, 61, 61, 0, 58,
+ 58, 58, 58, 58, 0, 0, 59, 59, 59, 59,
+ 59, 54, 55, 0, 0, 0, 0, 0, 55, 0,
+ 0, 55, 0, 0, 0, 0, 0, 0, 0, 56,
+ 56, 56, 56, 56, 0, 0, 0, 55, 55, 55,
+ 76, 0, 0, 54, 0, 90, 156, 83, 81, 155,
+ 82, 0, 84, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 70, 74, 71, 76, 80,
+ 55, 0, 0, 90, 158, 83, 81, 157, 82, 0,
+ 84, 0, 0, 0, 57, 57, 57, 57, 57, 0,
+ 0, 0, 0, 70, 74, 71, 89, 80, 0, 87,
+ 0, 0, 55, 0, 0, 0, 0, 0, 53, 53,
+ 53, 53, 53, 76, 0, 0, 0, 0, 90, 112,
+ 83, 81, 0, 82, 89, 84, 0, 87, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 70, 74,
+ 71, 76, 80, 0, 0, 0, 90, 144, 83, 81,
+ 0, 82, 0, 84, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 70, 74, 71, 89,
+ 80, 0, 87, 0, 0, 0, 76, 0, 0, 0,
+ 0, 90, 145, 83, 81, 0, 82, 0, 84, 0,
+ 0, 0, 0, 0, 0, 0, 0, 89, 0, 0,
+ 87, 70, 74, 71, 0, 80, 0, 0, 0, 0,
+ 54, 54, 54, 54, 54, 0, 0, 0, 76, 0,
+ 0, 0, 0, 90, 146, 83, 81, 0, 82, 0,
+ 84, 0, 89, 0, 0, 87, 0, 0, 0, 0,
+ 0, 0, 0, 70, 74, 71, 0, 80, 0, 0,
+ 0, 0, 0, 76, 0, 0, 0, 0, 90, 147,
+ 83, 81, 0, 82, 0, 84, 0, 0, 0, 55,
+ 55, 55, 55, 55, 89, 0, 0, 87, 70, 74,
+ 71, 0, 80, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 69, 72, 73,
+ 75, 77, 78, 79, 85, 86, 0, 88, 0, 89,
+ 0, 0, 87, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 69, 72, 73, 75, 77,
+ 78, 79, 85, 86, 76, 88, 0, 0, 0, 90,
+ 150, 83, 81, 0, 82, 0, 84, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 76, 0, 70,
+ 74, 71, 90, 80, 83, 81, 159, 82, 0, 84,
+ 69, 72, 73, 75, 77, 78, 79, 85, 86, 0,
+ 88, 0, 70, 74, 71, 0, 80, 0, 0, 0,
+ 89, 0, 0, 87, 0, 0, 0, 0, 69, 72,
+ 73, 75, 77, 78, 79, 85, 86, 0, 88, 0,
+ 0, 0, 0, 89, 0, 0, 87, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 69, 72, 73, 75, 77, 78, 79,
+ 85, 86, 76, 88, 0, 0, 0, 90, 152, 83,
+ 81, 0, 82, 0, 84, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 70, 74, 71,
+ 0, 80, 0, 0, 0, 69, 72, 73, 75, 77,
+ 78, 79, 85, 86, 0, 88, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 76, 89, 0,
+ 0, 87, 90, 153, 83, 81, 0, 82, 0, 84,
+ 69, 72, 73, 75, 77, 78, 79, 85, 86, 0,
+ 88, 0, 70, 74, 71, 76, 80, 0, 0, 0,
+ 90, 154, 83, 81, 0, 82, 0, 84, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 70, 74, 71, 89, 80, 0, 87, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 76, 0,
+ 0, 89, 0, 90, 87, 83, 81, 160, 82, 0,
+ 84, 69, 72, 73, 75, 77, 78, 79, 85, 86,
+ 0, 88, 0, 70, 74, 71, 0, 80, 0, 0,
+ 0, 0, 0, 0, 69, 72, 73, 75, 77, 78,
+ 79, 85, 86, 76, 88, 0, 0, 0, 90, 166,
+ 83, 81, 0, 82, 89, 84, 0, 87, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 70, 74,
+ 71, 0, 80, 0, 0, 0, 0, 0, 76, 0,
+ 0, 0, 0, 90, 176, 83, 81, 0, 82, 0,
+ 84, 0, 0, 0, 0, 0, 0, 0, 0, 89,
+ 0, 0, 87, 70, 74, 71, 76, 80, 0, 0,
+ 0, 90, 177, 83, 81, 0, 82, 0, 84, 69,
+ 72, 73, 75, 77, 78, 79, 85, 86, 0, 88,
+ 0, 70, 74, 71, 89, 80, 0, 87, 0, 0,
+ 0, 76, 0, 0, 0, 0, 90, 178, 83, 81,
+ 0, 82, 0, 84, 0, 0, 0, 0, 0, 0,
+ 0, 0, 89, 0, 0, 87, 70, 74, 71, 0,
+ 80, 0, 0, 0, 69, 72, 73, 75, 77, 78,
+ 79, 85, 86, 0, 88, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 32, 89, 0, 30,
+ 87, 31, 69, 72, 73, 75, 77, 78, 79, 85,
+ 86, 76, 88, 0, 0, 0, 90, 179, 83, 81,
+ 0, 82, 76, 84, 0, 0, 0, 90, 180, 83,
+ 81, 0, 82, 0, 84, 0, 70, 74, 71, 0,
+ 80, 0, 0, 0, 0, 0, 0, 70, 74, 71,
+ 0, 80, 0, 0, 0, 69, 72, 73, 75, 77,
+ 78, 79, 85, 86, 76, 88, 0, 89, 0, 90,
+ 87, 83, 81, 0, 82, 0, 84, 0, 89, 35,
+ 0, 87, 34, 0, 0, 0, 0, 0, 0, 70,
+ 74, 71, 0, 80, 0, 0, 0, 0, 0, 0,
+ 69, 72, 73, 75, 77, 78, 79, 85, 86, 0,
+ 88, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 89, 76, 0, 87, 0, 0, 90, 0, 83, 81,
+ 0, 82, 0, 84, 0, 69, 72, 73, 75, 77,
+ 78, 79, 85, 86, 0, 88, 70, 74, 71, 0,
+ 80, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 69, 72, 73, 75, 77, 78, 79,
+ 85, 86, 0, 88, 0, 0, 0, 89, 45, 0,
+ 87, 30, 0, 31, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 69, 72,
+ 73, 75, 77, 78, 79, 85, 86, 0, 88, 0,
+ 0, 0, 0, 1, 0, 0, 2, 3, 4, 5,
+ 6, 7, 8, 9, 0, 0, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 0, 27, 28, 29, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 33,
+ 0, 35, 0, 0, 34, 0, 0, 0, 69, 72,
+ 73, 75, 77, 78, 79, 85, 86, 0, 88, 69,
+ 72, 73, 75, 77, 78, 79, 85, 86, 0, 88,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 69, 72, 73, 75, 77, 78, 79, 85, 86,
+ 0, 88, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 72,
+ 73, 75, 77, 78, 79, 85, 86, 0, 88, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 2, 3,
+ 4, 5, 6, 7, 8, 9, 0, 0, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 37, 27, 28, 29, 0,
+ 0, 0, 0, 0, 0, 0, 46, 0, 0, 0,
+ 0, 33, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 63, 0, 65, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 94,
+ 65, 0, 0, 96, 97, 98, 99, 0, 0, 102,
+ 0, 104, 105, 106, 107, 108, 109, 110, 0, 111,
+ 0, 0, 0, 0, 116, 117, 118, 119, 120, 121,
+ 122, 123, 0, 127, 128, 129, 130, 131, 132, 133,
+ 134, 135, 0, 0, 0, 0, 139, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 161, 162,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 163, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 168, 0, 0, 0,
+ 169, 0, 170, 0, 171, 172, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 181,
+};
+short m2_check[] = { 44,
+ 0, 44, 40, 92, 123, 41, 266, 267, 44, 40,
+ 41, 0, 40, 40, 40, 40, 42, 43, 40, 45,
+ 41, 47, 41, 44, 40, 44, 44, 44, 41, 40,
+ 287, 44, 44, 40, 44, 35, 40, 40, 64, 0,
+ 40, 41, 42, 43, 44, 45, 35, 47, 93, 138,
+ 93, 270, 41, 42, 43, 44, 45, 40, 47, 40,
+ 60, 61, 62, 40, 64, 91, 91, 0, 94, 94,
+ 40, 60, 61, 62, 35, 64, 93, 40, 40, 266,
+ 41, 42, 43, 44, 45, 123, 47, 125, 40, 125,
+ 42, 91, 123, 93, 94, 47, 123, 0, 40, 60,
+ 61, 62, 35, 64, 93, 0, 125, 125, 41, 42,
+ 43, 44, 45, 125, 47, 125, 40, 44, 41, 40,
+ 0, 42, 43, 41, 45, 125, 47, 60, 61, 62,
+ 123, 64, 93, 0, 0, -1, 125, 77, 41, 91,
+ 35, 44, 94, 64, 0, -1, 41, 42, 43, 44,
+ 45, 270, 47, -1, -1, -1, 40, -1, 42, 43,
+ 93, 45, -1, 47, 125, 60, 61, 62, 35, 64,
+ 91, -1, -1, 94, 41, 42, 43, 44, 45, 35,
+ 47, -1, -1, -1, -1, 41, 42, 43, 44, 45,
+ 93, 47, 125, 60, 61, 62, -1, 64, 93, -1,
+ -1, -1, -1, -1, 60, 61, 62, 91, 64, -1,
+ 94, -1, -1, -1, -1, -1, -1, -1, -1, 0,
+ -1, -1, 125, -1, -1, -1, 93, -1, -1, -1,
+ 125, 40, -1, 42, 43, -1, 45, 93, 47, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 35, 64, -1, -1, 125, -1,
+ 41, 42, 43, 44, 45, -1, 47, -1, -1, 125,
+ -1, 297, 298, 299, 300, -1, 302, 302, -1, 60,
+ 61, 62, 91, 64, -1, 94, -1, 287, -1, -1,
+ -1, -1, 292, 293, 294, 295, 296, 297, 298, 299,
+ 300, -1, 302, 292, 293, 294, 295, 296, 297, 298,
+ 299, 300, 93, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 292, 293, 294, 295, 296, 297, 298, 299, 300,
+ 0, -1, -1, -1, 125, -1, -1, 299, 300, -1,
+ 302, -1, -1, -1, -1, -1, -1, -1, -1, 292,
+ 293, 294, 295, 296, 297, 298, 299, 300, -1, -1,
+ -1, -1, -1, -1, -1, 35, -1, 298, 299, 300,
+ -1, 302, -1, -1, -1, 45, -1, -1, -1, -1,
+ -1, -1, 52, 53, -1, 55, -1, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, -1, -1, -1, -1,
+ 0, -1, -1, -1, -1, 299, 300, 77, 302, -1,
+ -1, -1, -1, -1, -1, 292, 293, 294, 295, 296,
+ 297, 298, 299, 300, 0, -1, 292, 293, 294, 295,
+ 296, 297, 298, 299, 300, 35, -1, -1, -1, -1,
+ -1, 41, 42, 43, 44, 45, -1, 47, -1, -1,
+ -1, -1, -1, -1, 0, -1, -1, -1, -1, 35,
+ 60, 61, 62, -1, 64, 41, 42, 43, 44, 45,
+ -1, 47, -1, -1, -1, -1, -1, -1, 0, -1,
+ 299, 300, -1, 302, 60, 61, 62, -1, 64, 35,
+ -1, 0, -1, 93, -1, 41, -1, 43, 44, 45,
+ -1, 292, 293, 294, 295, 296, 297, 298, 299, 300,
+ -1, -1, -1, 35, 60, 61, 62, 93, 64, 41,
+ -1, 43, 44, 45, -1, 125, 35, -1, 0, -1,
+ -1, -1, 41, -1, -1, 44, -1, -1, 60, 61,
+ 62, -1, 64, -1, -1, -1, -1, 93, -1, 125,
+ 0, 60, 61, 62, -1, 64, -1, 0, -1, -1,
+ -1, -1, -1, 35, 0, -1, -1, -1, -1, 41,
+ -1, 93, 44, -1, -1, -1, -1, -1, -1, 125,
+ -1, -1, -1, -1, 93, 35, -1, 0, 60, 61,
+ 62, 41, 35, -1, 44, -1, -1, -1, 41, 35,
+ -1, 44, -1, 125, -1, 41, -1, -1, 44, -1,
+ 60, 61, 62, -1, -1, -1, 125, 60, 61, 62,
+ -1, 93, 35, -1, 60, 61, 62, -1, 41, -1,
+ -1, 44, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 0, 93, -1, -1, -1, 60, 61, 62,
+ 93, -1, -1, 125, -1, -1, -1, 93, -1, -1,
+ -1, -1, -1, -1, -1, -1, 0, -1, -1, -1,
+ -1, -1, -1, -1, -1, 125, -1, 35, -1, -1,
+ 93, -1, 125, 41, -1, -1, 44, -1, -1, 125,
+ -1, -1, 292, 293, 294, 295, 296, 297, 298, 299,
+ 300, 35, 60, 61, 62, -1, -1, 41, -1, -1,
+ 44, -1, 125, -1, -1, -1, 292, 293, 294, 295,
+ 296, 297, 298, 299, 300, -1, 60, 61, 62, -1,
+ -1, -1, -1, -1, -1, 93, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 292, 293, 294, 295,
+ 296, 297, 298, -1, -1, -1, -1, -1, -1, 93,
+ -1, -1, -1, -1, -1, -1, -1, 125, 0, -1,
+ 292, 293, 294, 295, 296, 297, 298, -1, -1, -1,
+ -1, -1, -1, 292, 293, 294, 295, 296, 297, 298,
+ -1, 125, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 35, -1, -1, -1, -1, -1, 41,
+ -1, -1, 44, -1, -1, -1, -1, -1, -1, -1,
+ 292, 293, 294, 295, 296, 297, 298, 0, 60, 61,
+ 62, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 292, 293, 294, 295, 296, 297, -1, 292,
+ 293, 294, 295, 296, -1, -1, 292, 293, 294, 295,
+ 296, 93, 35, -1, -1, -1, -1, -1, 41, -1,
+ -1, 44, -1, -1, -1, -1, -1, -1, -1, 292,
+ 293, 294, 295, 296, -1, -1, -1, 60, 61, 62,
+ 35, -1, -1, 125, -1, 40, 41, 42, 43, 44,
+ 45, -1, 47, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 60, 61, 62, 35, 64,
+ 93, -1, -1, 40, 41, 42, 43, 44, 45, -1,
+ 47, -1, -1, -1, 292, 293, 294, 295, 296, -1,
+ -1, -1, -1, 60, 61, 62, 91, 64, -1, 94,
+ -1, -1, 125, -1, -1, -1, -1, -1, 292, 293,
+ 294, 295, 296, 35, -1, -1, -1, -1, 40, 41,
+ 42, 43, -1, 45, 91, 47, -1, 94, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 60, 61,
+ 62, 35, 64, -1, -1, -1, 40, 41, 42, 43,
+ -1, 45, -1, 47, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 60, 61, 62, 91,
+ 64, -1, 94, -1, -1, -1, 35, -1, -1, -1,
+ -1, 40, 41, 42, 43, -1, 45, -1, 47, -1,
+ -1, -1, -1, -1, -1, -1, -1, 91, -1, -1,
+ 94, 60, 61, 62, -1, 64, -1, -1, -1, -1,
+ 292, 293, 294, 295, 296, -1, -1, -1, 35, -1,
+ -1, -1, -1, 40, 41, 42, 43, -1, 45, -1,
+ 47, -1, 91, -1, -1, 94, -1, -1, -1, -1,
+ -1, -1, -1, 60, 61, 62, -1, 64, -1, -1,
+ -1, -1, -1, 35, -1, -1, -1, -1, 40, 41,
+ 42, 43, -1, 45, -1, 47, -1, -1, -1, 292,
+ 293, 294, 295, 296, 91, -1, -1, 94, 60, 61,
+ 62, -1, 64, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, -1, 302, -1, 91,
+ -1, -1, 94, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 292, 293, 294, 295, 296,
+ 297, 298, 299, 300, 35, 302, -1, -1, -1, 40,
+ 41, 42, 43, -1, 45, -1, 47, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 35, -1, 60,
+ 61, 62, 40, 64, 42, 43, 44, 45, -1, 47,
+ 292, 293, 294, 295, 296, 297, 298, 299, 300, -1,
+ 302, -1, 60, 61, 62, -1, 64, -1, -1, -1,
+ 91, -1, -1, 94, -1, -1, -1, -1, 292, 293,
+ 294, 295, 296, 297, 298, 299, 300, -1, 302, -1,
+ -1, -1, -1, 91, -1, -1, 94, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 292, 293, 294, 295, 296, 297, 298,
+ 299, 300, 35, 302, -1, -1, -1, 40, 41, 42,
+ 43, -1, 45, -1, 47, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 60, 61, 62,
+ -1, 64, -1, -1, -1, 292, 293, 294, 295, 296,
+ 297, 298, 299, 300, -1, 302, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 35, 91, -1,
+ -1, 94, 40, 41, 42, 43, -1, 45, -1, 47,
+ 292, 293, 294, 295, 296, 297, 298, 299, 300, -1,
+ 302, -1, 60, 61, 62, 35, 64, -1, -1, -1,
+ 40, 41, 42, 43, -1, 45, -1, 47, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 60, 61, 62, 91, 64, -1, 94, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 35, -1,
+ -1, 91, -1, 40, 94, 42, 43, 44, 45, -1,
+ 47, 292, 293, 294, 295, 296, 297, 298, 299, 300,
+ -1, 302, -1, 60, 61, 62, -1, 64, -1, -1,
+ -1, -1, -1, -1, 292, 293, 294, 295, 296, 297,
+ 298, 299, 300, 35, 302, -1, -1, -1, 40, 41,
+ 42, 43, -1, 45, 91, 47, -1, 94, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 60, 61,
+ 62, -1, 64, -1, -1, -1, -1, -1, 35, -1,
+ -1, -1, -1, 40, 41, 42, 43, -1, 45, -1,
+ 47, -1, -1, -1, -1, -1, -1, -1, -1, 91,
+ -1, -1, 94, 60, 61, 62, 35, 64, -1, -1,
+ -1, 40, 41, 42, 43, -1, 45, -1, 47, 292,
+ 293, 294, 295, 296, 297, 298, 299, 300, -1, 302,
+ -1, 60, 61, 62, 91, 64, -1, 94, -1, -1,
+ -1, 35, -1, -1, -1, -1, 40, 41, 42, 43,
+ -1, 45, -1, 47, -1, -1, -1, -1, -1, -1,
+ -1, -1, 91, -1, -1, 94, 60, 61, 62, -1,
+ 64, -1, -1, -1, 292, 293, 294, 295, 296, 297,
+ 298, 299, 300, -1, 302, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 40, 91, -1, 43,
+ 94, 45, 292, 293, 294, 295, 296, 297, 298, 299,
+ 300, 35, 302, -1, -1, -1, 40, 41, 42, 43,
+ -1, 45, 35, 47, -1, -1, -1, 40, 41, 42,
+ 43, -1, 45, -1, 47, -1, 60, 61, 62, -1,
+ 64, -1, -1, -1, -1, -1, -1, 60, 61, 62,
+ -1, 64, -1, -1, -1, 292, 293, 294, 295, 296,
+ 297, 298, 299, 300, 35, 302, -1, 91, -1, 40,
+ 94, 42, 43, -1, 45, -1, 47, -1, 91, 123,
+ -1, 94, 126, -1, -1, -1, -1, -1, -1, 60,
+ 61, 62, -1, 64, -1, -1, -1, -1, -1, -1,
+ 292, 293, 294, 295, 296, 297, 298, 299, 300, -1,
+ 302, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 91, 35, -1, 94, -1, -1, 40, -1, 42, 43,
+ -1, 45, -1, 47, -1, 292, 293, 294, 295, 296,
+ 297, 298, 299, 300, -1, 302, 60, 61, 62, -1,
+ 64, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 292, 293, 294, 295, 296, 297, 298,
+ 299, 300, -1, 302, -1, -1, -1, 91, 40, -1,
+ 94, 43, -1, 45, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 292, 293,
+ 294, 295, 296, 297, 298, 299, 300, -1, 302, -1,
+ -1, -1, -1, 257, -1, -1, 260, 261, 262, 263,
+ 264, 265, 266, 267, -1, -1, 270, 271, 272, 273,
+ 274, 275, 276, 277, 278, 279, 280, 281, 282, 283,
+ 284, 285, 286, -1, 288, 289, 290, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 303,
+ -1, 123, -1, -1, 126, -1, -1, -1, 292, 293,
+ 294, 295, 296, 297, 298, 299, 300, -1, 302, 292,
+ 293, 294, 295, 296, 297, 298, 299, 300, -1, 302,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 292, 293, 294, 295, 296, 297, 298, 299, 300,
+ -1, 302, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 293,
+ 294, 295, 296, 297, 298, 299, 300, -1, 302, -1,
+ -1, -1, -1, -1, -1, 257, -1, -1, 260, 261,
+ 262, 263, 264, 265, 266, 267, -1, -1, 270, 271,
+ 272, 273, 274, 275, 276, 277, 278, 279, 280, 281,
+ 282, 283, 284, 285, 286, 0, 288, 289, 290, -1,
+ -1, -1, -1, -1, -1, -1, 11, -1, -1, -1,
+ -1, 303, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 30, -1, 32, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 44,
+ 45, -1, -1, 48, 49, 50, 51, -1, -1, 54,
+ -1, 56, 57, 58, 59, 60, 61, 62, -1, 64,
+ -1, -1, -1, -1, 69, 70, 71, 72, 73, 74,
+ 75, 76, -1, 78, 79, 80, 81, 82, 83, 84,
+ 85, 86, -1, -1, -1, -1, 91, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 113, 114,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 137, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 151, -1, -1, -1,
+ 155, -1, 157, -1, 159, 160, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 173,
+};
+#define YYFINAL 36
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYMAXTOKEN 304
+#if YYDEBUG
+char *m2_name[] = {
+"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,"'#'",0,0,"'&'",0,"'('","')'","'*'","'+'","','","'-'",0,"'/'",0,0,0,0,0,0,0,0,
+0,0,0,0,"'<'","'='","'>'",0,"'@'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,"'['",0,"']'","'^'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,"'{'",0,"'}'","'~'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"INT","HEX","ERROR","UINT","M2_TRUE",
+"M2_FALSE","CHAR","FLOAT","STRING","NAME","BLOCKNAME","IDENT","VARNAME",
+"TYPENAME","SIZE","CAP","ORD","HIGH","ABS","MIN_FUNC","MAX_FUNC","FLOAT_FUNC",
+"VAL","CHR","ODD","TRUNC","INC","DEC","INCL","EXCL","COLONCOLON","LAST",
+"REGNAME","INTERNAL_VAR","ABOVE_COMMA","ASSIGN","LEQ","GEQ","NOTEQUAL","IN",
+"OROR","LOGICAL_AND","DIV","MOD","UNARY","DOT","NOT","QID",
+};
+char *m2_rule[] = {
+"$accept : start",
+"start : exp",
+"start : type_exp",
+"type_exp : type",
+"exp : exp '^'",
+"$$1 :",
+"exp : '-' $$1 exp",
+"exp : '+' exp",
+"exp : not_exp exp",
+"not_exp : NOT",
+"not_exp : '~'",
+"exp : CAP '(' exp ')'",
+"exp : ORD '(' exp ')'",
+"exp : ABS '(' exp ')'",
+"exp : HIGH '(' exp ')'",
+"exp : MIN_FUNC '(' type ')'",
+"exp : MAX_FUNC '(' type ')'",
+"exp : FLOAT_FUNC '(' exp ')'",
+"exp : VAL '(' type ',' exp ')'",
+"exp : CHR '(' exp ')'",
+"exp : ODD '(' exp ')'",
+"exp : TRUNC '(' exp ')'",
+"exp : SIZE exp",
+"exp : INC '(' exp ')'",
+"exp : INC '(' exp ',' exp ')'",
+"exp : DEC '(' exp ')'",
+"exp : DEC '(' exp ',' exp ')'",
+"exp : exp DOT NAME",
+"exp : set",
+"exp : exp IN set",
+"exp : INCL '(' exp ',' exp ')'",
+"exp : EXCL '(' exp ',' exp ')'",
+"set : '{' arglist '}'",
+"set : type '{' arglist '}'",
+"$$2 :",
+"exp : exp '[' $$2 non_empty_arglist ']'",
+"$$3 :",
+"exp : exp '(' $$3 arglist ')'",
+"arglist :",
+"arglist : exp",
+"arglist : arglist ',' exp",
+"non_empty_arglist : exp",
+"non_empty_arglist : non_empty_arglist ',' exp",
+"exp : '{' type '}' exp",
+"exp : type '(' exp ')'",
+"exp : '(' exp ')'",
+"exp : exp '@' exp",
+"exp : exp '*' exp",
+"exp : exp '/' exp",
+"exp : exp DIV exp",
+"exp : exp MOD exp",
+"exp : exp '+' exp",
+"exp : exp '-' exp",
+"exp : exp '=' exp",
+"exp : exp NOTEQUAL exp",
+"exp : exp '#' exp",
+"exp : exp LEQ exp",
+"exp : exp GEQ exp",
+"exp : exp '<' exp",
+"exp : exp '>' exp",
+"exp : exp LOGICAL_AND exp",
+"exp : exp OROR exp",
+"exp : exp ASSIGN exp",
+"exp : M2_TRUE",
+"exp : M2_FALSE",
+"exp : INT",
+"exp : UINT",
+"exp : CHAR",
+"exp : FLOAT",
+"exp : variable",
+"exp : LAST",
+"exp : REGNAME",
+"exp : SIZE '(' type ')'",
+"exp : STRING",
+"block : fblock",
+"fblock : BLOCKNAME",
+"fblock : block COLONCOLON BLOCKNAME",
+"variable : fblock",
+"variable : INTERNAL_VAR",
+"variable : block COLONCOLON NAME",
+"variable : NAME",
+"type : TYPENAME",
+};
+#endif
+#ifdef YYSTACKSIZE
+#undef YYMAXDEPTH
+#define YYMAXDEPTH YYSTACKSIZE
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 500
+#define YYMAXDEPTH 500
+#endif
+#endif
+int yydebug;
+int yynerrs;
+int yyerrflag;
+int yychar;
+short *yyssp;
+YYSTYPE *yyvsp;
+YYSTYPE yyval;
+YYSTYPE yylval;
+short yyss[YYSTACKSIZE];
+YYSTYPE yyvs[YYSTACKSIZE];
+#define yystacksize YYSTACKSIZE
+#line 658 "./m2-exp.y"
+
+#if 0 /* FIXME! */
+int
+overflow(a,b)
+ long a,b;
+{
+ return (MAX_OF_TYPE(builtin_type_m2_int) - b) < a;
+}
+
+int
+uoverflow(a,b)
+ unsigned long a,b;
+{
+ return (MAX_OF_TYPE(builtin_type_m2_card) - b) < a;
+}
+#endif /* FIXME */
+
+/* Take care of parsing a number (anything that starts with a digit).
+ Set yylval and return the token type; update lexptr.
+ LEN is the number of characters in it. */
+
+/*** Needs some error checking for the float case ***/
+
+static int
+parse_number (olen)
+ int olen;
+{
+ register char *p = lexptr;
+ register LONGEST n = 0;
+ register LONGEST prevn = 0;
+ register int c,i,ischar=0;
+ register int base = input_radix;
+ register int len = olen;
+ int unsigned_p = number_sign == 1 ? 1 : 0;
+
+ if(p[len-1] == 'H')
+ {
+ base = 16;
+ len--;
+ }
+ else if(p[len-1] == 'C' || p[len-1] == 'B')
+ {
+ base = 8;
+ ischar = p[len-1] == 'C';
+ len--;
+ }
+
+ /* Scan the number */
+ for (c = 0; c < len; c++)
+ {
+ if (p[c] == '.' && base == 10)
+ {
+ /* It's a float since it contains a point. */
+ yylval.dval = atof (p);
+ lexptr += len;
+ return FLOAT;
+ }
+ if (p[c] == '.' && base != 10)
+ error("Floating point numbers must be base 10.");
+ if (base == 10 && (p[c] < '0' || p[c] > '9'))
+ error("Invalid digit \'%c\' in number.",p[c]);
+ }
+
+ while (len-- > 0)
+ {
+ c = *p++;
+ n *= base;
+ if( base == 8 && (c == '8' || c == '9'))
+ error("Invalid digit \'%c\' in octal number.",c);
+ if (c >= '0' && c <= '9')
+ i = c - '0';
+ else
+ {
+ if (base == 16 && c >= 'A' && c <= 'F')
+ i = c - 'A' + 10;
+ else
+ return ERROR;
+ }
+ n+=i;
+ if(i >= base)
+ return ERROR;
+ if(!unsigned_p && number_sign == 1 && (prevn >= n))
+ unsigned_p=1; /* Try something unsigned */
+ /* Don't do the range check if n==i and i==0, since that special
+ case will give an overflow error. */
+ if(RANGE_CHECK && n!=i && i)
+ {
+ if((unsigned_p && (unsigned)prevn >= (unsigned)n) ||
+ ((!unsigned_p && number_sign==-1) && -prevn <= -n))
+ range_error("Overflow on numeric constant.");
+ }
+ prevn=n;
+ }
+
+ lexptr = p;
+ if(*p == 'B' || *p == 'C' || *p == 'H')
+ lexptr++; /* Advance past B,C or H */
+
+ if (ischar)
+ {
+ yylval.ulval = n;
+ return CHAR;
+ }
+ else if ( unsigned_p && number_sign == 1)
+ {
+ yylval.ulval = n;
+ return UINT;
+ }
+ else if((unsigned_p && (n<0))) {
+ range_error("Overflow on numeric constant -- number too large.");
+ /* But, this can return if range_check == range_warn. */
+ }
+ yylval.lval = n;
+ return INT;
+}
+
+
+/* Some tokens */
+
+static struct
+{
+ char name[2];
+ int token;
+} tokentab2[] =
+{
+ { {'<', '>'}, NOTEQUAL },
+ { {':', '='}, ASSIGN },
+ { {'<', '='}, LEQ },
+ { {'>', '='}, GEQ },
+ { {':', ':'}, COLONCOLON },
+
+};
+
+/* Some specific keywords */
+
+struct keyword {
+ char keyw[10];
+ int token;
+};
+
+static struct keyword keytab[] =
+{
+ {"OR" , OROR },
+ {"IN", IN },/* Note space after IN */
+ {"AND", LOGICAL_AND},
+ {"ABS", ABS },
+ {"CHR", CHR },
+ {"DEC", DEC },
+ {"NOT", NOT },
+ {"DIV", DIV },
+ {"INC", INC },
+ {"MAX", MAX_FUNC },
+ {"MIN", MIN_FUNC },
+ {"MOD", MOD },
+ {"ODD", ODD },
+ {"CAP", CAP },
+ {"ORD", ORD },
+ {"VAL", VAL },
+ {"EXCL", EXCL },
+ {"HIGH", HIGH },
+ {"INCL", INCL },
+ {"SIZE", SIZE },
+ {"FLOAT", FLOAT_FUNC },
+ {"TRUNC", TRUNC },
+};
+
+
+/* Read one token, getting characters through lexptr. */
+
+/* This is where we will check to make sure that the language and the operators used are
+ compatible */
+
+static int
+yylex ()
+{
+ register int c;
+ register int namelen;
+ register int i;
+ register char *tokstart;
+ register char quote;
+
+ retry:
+
+ tokstart = lexptr;
+
+
+ /* See if it is a special token of length 2 */
+ for( i = 0 ; i < sizeof tokentab2 / sizeof tokentab2[0] ; i++)
+ if(STREQN(tokentab2[i].name, tokstart, 2))
+ {
+ lexptr += 2;
+ return tokentab2[i].token;
+ }
+
+ switch (c = *tokstart)
+ {
+ case 0:
+ return 0;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ lexptr++;
+ goto retry;
+
+ case '(':
+ paren_depth++;
+ lexptr++;
+ return c;
+
+ case ')':
+ if (paren_depth == 0)
+ return 0;
+ paren_depth--;
+ lexptr++;
+ return c;
+
+ case ',':
+ if (comma_terminates && paren_depth == 0)
+ return 0;
+ lexptr++;
+ return c;
+
+ case '.':
+ /* Might be a floating point number. */
+ if (lexptr[1] >= '0' && lexptr[1] <= '9')
+ break; /* Falls into number code. */
+ else
+ {
+ lexptr++;
+ return DOT;
+ }
+
+/* These are character tokens that appear as-is in the YACC grammar */
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '^':
+ case '<':
+ case '>':
+ case '[':
+ case ']':
+ case '=':
+ case '{':
+ case '}':
+ case '#':
+ case '@':
+ case '~':
+ case '&':
+ lexptr++;
+ return c;
+
+ case '\'' :
+ case '"':
+ quote = c;
+ for (namelen = 1; (c = tokstart[namelen]) != quote && c != '\0'; namelen++)
+ if (c == '\\')
+ {
+ c = tokstart[++namelen];
+ if (c >= '0' && c <= '9')
+ {
+ c = tokstart[++namelen];
+ if (c >= '0' && c <= '9')
+ c = tokstart[++namelen];
+ }
+ }
+ if(c != quote)
+ error("Unterminated string or character constant.");
+ yylval.sval.ptr = tokstart + 1;
+ yylval.sval.length = namelen - 1;
+ lexptr += namelen + 1;
+
+ if(namelen == 2) /* Single character */
+ {
+ yylval.ulval = tokstart[1];
+ return CHAR;
+ }
+ else
+ return STRING;
+ }
+
+ /* Is it a number? */
+ /* Note: We have already dealt with the case of the token '.'.
+ See case '.' above. */
+ if ((c >= '0' && c <= '9'))
+ {
+ /* It's a number. */
+ int got_dot = 0, got_e = 0;
+ register char *p = tokstart;
+ int toktype;
+
+ for (++p ;; ++p)
+ {
+ if (!got_e && (*p == 'e' || *p == 'E'))
+ got_dot = got_e = 1;
+ else if (!got_dot && *p == '.')
+ got_dot = 1;
+ else if (got_e && (p[-1] == 'e' || p[-1] == 'E')
+ && (*p == '-' || *p == '+'))
+ /* This is the sign of the exponent, not the end of the
+ number. */
+ continue;
+ else if ((*p < '0' || *p > '9') &&
+ (*p < 'A' || *p > 'F') &&
+ (*p != 'H')) /* Modula-2 hexadecimal number */
+ break;
+ }
+ toktype = parse_number (p - tokstart);
+ if (toktype == ERROR)
+ {
+ char *err_copy = (char *) alloca (p - tokstart + 1);
+
+ memcpy (err_copy, tokstart, p - tokstart);
+ err_copy[p - tokstart] = 0;
+ error ("Invalid number \"%s\".", err_copy);
+ }
+ lexptr = p;
+ return toktype;
+ }
+
+ if (!(c == '_' || c == '$'
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
+ /* We must have come across a bad character (e.g. ';'). */
+ error ("Invalid character '%c' in expression.", c);
+
+ /* It's a name. See how long it is. */
+ namelen = 0;
+ for (c = tokstart[namelen];
+ (c == '_' || c == '$' || (c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
+ c = tokstart[++namelen])
+ ;
+
+ /* The token "if" terminates the expression and is NOT
+ removed from the input stream. */
+ if (namelen == 2 && tokstart[0] == 'i' && tokstart[1] == 'f')
+ {
+ return 0;
+ }
+
+ lexptr += namelen;
+
+ /* Handle the tokens $digits; also $ (short for $0) and $$ (short for $$1)
+ and $$digits (equivalent to $<-digits> if you could type that).
+ Make token type LAST, and put the number (the digits) in yylval. */
+
+ if (*tokstart == '$')
+ {
+ register int negate = 0;
+ c = 1;
+ /* Double dollar means negate the number and add -1 as well.
+ Thus $$ alone means -1. */
+ if (namelen >= 2 && tokstart[1] == '$')
+ {
+ negate = 1;
+ c = 2;
+ }
+ if (c == namelen)
+ {
+ /* Just dollars (one or two) */
+ yylval.lval = - negate;
+ return LAST;
+ }
+ /* Is the rest of the token digits? */
+ for (; c < namelen; c++)
+ if (!(tokstart[c] >= '0' && tokstart[c] <= '9'))
+ break;
+ if (c == namelen)
+ {
+ yylval.lval = atoi (tokstart + 1 + negate);
+ if (negate)
+ yylval.lval = - yylval.lval;
+ return LAST;
+ }
+ }
+
+ /* Handle tokens that refer to machine registers:
+ $ followed by a register name. */
+
+ if (*tokstart == '$') {
+ for (c = 0; c < NUM_REGS; c++)
+ if (namelen - 1 == strlen (reg_names[c])
+ && STREQN (tokstart + 1, reg_names[c], namelen - 1))
+ {
+ yylval.lval = c;
+ return REGNAME;
+ }
+ for (c = 0; c < num_std_regs; c++)
+ if (namelen - 1 == strlen (std_regs[c].name)
+ && STREQN (tokstart + 1, std_regs[c].name, namelen - 1))
+ {
+ yylval.lval = std_regs[c].regnum;
+ return REGNAME;
+ }
+ }
+
+
+ /* Lookup special keywords */
+ for(i = 0 ; i < sizeof(keytab) / sizeof(keytab[0]) ; i++)
+ if(namelen == strlen(keytab[i].keyw) && STREQN(tokstart,keytab[i].keyw,namelen))
+ return keytab[i].token;
+
+ yylval.sval.ptr = tokstart;
+ yylval.sval.length = namelen;
+
+ /* Any other names starting in $ are debugger internal variables. */
+
+ if (*tokstart == '$')
+ {
+ yylval.ivar = (struct internalvar *) lookup_internalvar (copy_name (yylval.sval) + 1);
+ return INTERNAL_VAR;
+ }
+
+
+ /* Use token-type BLOCKNAME for symbols that happen to be defined as
+ functions. If this is not so, then ...
+ Use token-type TYPENAME for symbols that happen to be defined
+ currently as names of types; NAME for other symbols.
+ The caller is not constrained to care about the distinction. */
+ {
+
+
+ char *tmp = copy_name (yylval.sval);
+ struct symbol *sym;
+
+ if (lookup_partial_symtab (tmp))
+ return BLOCKNAME;
+ sym = lookup_symbol (tmp, expression_context_block,
+ VAR_NAMESPACE, 0, NULL);
+ if (sym && SYMBOL_CLASS (sym) == LOC_BLOCK)
+ return BLOCKNAME;
+ if (lookup_typename (copy_name (yylval.sval), expression_context_block, 1))
+ return TYPENAME;
+
+ if(sym)
+ {
+ switch(sym->class)
+ {
+ case LOC_STATIC:
+ case LOC_REGISTER:
+ case LOC_ARG:
+ case LOC_REF_ARG:
+ case LOC_REGPARM:
+ case LOC_REGPARM_ADDR:
+ case LOC_LOCAL:
+ case LOC_LOCAL_ARG:
+ case LOC_BASEREG:
+ case LOC_BASEREG_ARG:
+ case LOC_CONST:
+ case LOC_CONST_BYTES:
+ case LOC_OPTIMIZED_OUT:
+ return NAME;
+
+ case LOC_TYPEDEF:
+ return TYPENAME;
+
+ case LOC_BLOCK:
+ return BLOCKNAME;
+
+ case LOC_UNDEF:
+ error("internal: Undefined class in m2lex()");
+
+ case LOC_LABEL:
+ error("internal: Unforseen case in m2lex()");
+ }
+ }
+ else
+ {
+ /* Built-in BOOLEAN type. This is sort of a hack. */
+ if(STREQN(tokstart,"TRUE",4))
+ {
+ yylval.ulval = 1;
+ return M2_TRUE;
+ }
+ else if(STREQN(tokstart,"FALSE",5))
+ {
+ yylval.ulval = 0;
+ return M2_FALSE;
+ }
+ }
+
+ /* Must be another type of name... */
+ return NAME;
+ }
+}
+
+#if 0 /* Unused */
+static char *
+make_qualname(mod,ident)
+ char *mod, *ident;
+{
+ char *new = xmalloc(strlen(mod)+strlen(ident)+2);
+
+ strcpy(new,mod);
+ strcat(new,".");
+ strcat(new,ident);
+ return new;
+}
+#endif /* 0 */
+
+void
+yyerror(msg)
+ char *msg; /* unused */
+{
+ printf("Parsing: %s\n",lexptr);
+ if (yychar < 256)
+ error("Invalid syntax in expression near character '%c'.",yychar);
+ else
+ error("Invalid syntax in expression");
+}
+
+#line 1366 "y.tab.c"
+#define YYABORT goto yyabort
+#define YYREJECT goto yyabort
+#define YYACCEPT goto yyaccept
+#define YYERROR goto yyerrlab
+int
+yyparse()
+{
+ register int yym, yyn, yystate;
+#if YYDEBUG
+ register char *yys;
+ extern char *getenv();
+
+ if (yys = getenv("YYDEBUG"))
+ {
+ yyn = *yys;
+ if (yyn >= '0' && yyn <= '9')
+ yydebug = yyn - '0';
+ }
+#endif
+
+ yynerrs = 0;
+ yyerrflag = 0;
+ yychar = (-1);
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+ *yyssp = yystate = 0;
+
+yyloop:
+ if (yyn = yydefred[yystate]) goto yyreduce;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ }
+ if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, shifting to state %d\n",
+ YYPREFIX, yystate, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ yychar = (-1);
+ if (yyerrflag > 0) --yyerrflag;
+ goto yyloop;
+ }
+ if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+ yyn = yytable[yyn];
+ goto yyreduce;
+ }
+ if (yyerrflag) goto yyinrecovery;
+#ifdef lint
+ goto yynewerror;
+#endif
+yynewerror:
+ yyerror("syntax error");
+#ifdef lint
+ goto yyerrlab;
+#endif
+yyerrlab:
+ ++yynerrs;
+yyinrecovery:
+ if (yyerrflag < 3)
+ {
+ yyerrflag = 3;
+ for (;;)
+ {
+ if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, error recovery shifting\
+ to state %d\n", YYPREFIX, *yyssp, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ goto yyloop;
+ }
+ else
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: error recovery discarding state %d\n",
+ YYPREFIX, *yyssp);
+#endif
+ if (yyssp <= yyss) goto yyabort;
+ --yyssp;
+ --yyvsp;
+ }
+ }
+ }
+ else
+ {
+ if (yychar == 0) goto yyabort;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, error recovery discards token %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ yychar = (-1);
+ goto yyloop;
+ }
+yyreduce:
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, reducing by rule %d (%s)\n",
+ YYPREFIX, yystate, yyn, yyrule[yyn]);
+#endif
+ yym = yylen[yyn];
+ yyval = yyvsp[1-yym];
+ switch (yyn)
+ {
+case 3:
+#line 197 "./m2-exp.y"
+{ write_exp_elt_opcode(OP_TYPE);
+ write_exp_elt_type(yyvsp[0].tval);
+ write_exp_elt_opcode(OP_TYPE);
+ }
+break;
+case 4:
+#line 206 "./m2-exp.y"
+{ write_exp_elt_opcode (UNOP_IND); }
+break;
+case 5:
+#line 209 "./m2-exp.y"
+{ number_sign = -1; }
+break;
+case 6:
+#line 211 "./m2-exp.y"
+{ number_sign = 1;
+ write_exp_elt_opcode (UNOP_NEG); }
+break;
+case 7:
+#line 216 "./m2-exp.y"
+{ write_exp_elt_opcode(UNOP_PLUS); }
+break;
+case 8:
+#line 220 "./m2-exp.y"
+{ write_exp_elt_opcode (UNOP_LOGICAL_NOT); }
+break;
+case 11:
+#line 228 "./m2-exp.y"
+{ write_exp_elt_opcode (UNOP_CAP); }
+break;
+case 12:
+#line 232 "./m2-exp.y"
+{ write_exp_elt_opcode (UNOP_ORD); }
+break;
+case 13:
+#line 236 "./m2-exp.y"
+{ write_exp_elt_opcode (UNOP_ABS); }
+break;
+case 14:
+#line 240 "./m2-exp.y"
+{ write_exp_elt_opcode (UNOP_HIGH); }
+break;
+case 15:
+#line 244 "./m2-exp.y"
+{ write_exp_elt_opcode (UNOP_MIN);
+ write_exp_elt_type (yyvsp[-1].tval);
+ write_exp_elt_opcode (UNOP_MIN); }
+break;
+case 16:
+#line 250 "./m2-exp.y"
+{ write_exp_elt_opcode (UNOP_MAX);
+ write_exp_elt_type (yyvsp[-1].tval);
+ write_exp_elt_opcode (UNOP_MIN); }
+break;
+case 17:
+#line 256 "./m2-exp.y"
+{ write_exp_elt_opcode (UNOP_FLOAT); }
+break;
+case 18:
+#line 260 "./m2-exp.y"
+{ write_exp_elt_opcode (BINOP_VAL);
+ write_exp_elt_type (yyvsp[-3].tval);
+ write_exp_elt_opcode (BINOP_VAL); }
+break;
+case 19:
+#line 266 "./m2-exp.y"
+{ write_exp_elt_opcode (UNOP_CHR); }
+break;
+case 20:
+#line 270 "./m2-exp.y"
+{ write_exp_elt_opcode (UNOP_ODD); }
+break;
+case 21:
+#line 274 "./m2-exp.y"
+{ write_exp_elt_opcode (UNOP_TRUNC); }
+break;
+case 22:
+#line 278 "./m2-exp.y"
+{ write_exp_elt_opcode (UNOP_SIZEOF); }
+break;
+case 23:
+#line 283 "./m2-exp.y"
+{ write_exp_elt_opcode(UNOP_PREINCREMENT); }
+break;
+case 24:
+#line 287 "./m2-exp.y"
+{ write_exp_elt_opcode(BINOP_ASSIGN_MODIFY);
+ write_exp_elt_opcode(BINOP_ADD);
+ write_exp_elt_opcode(BINOP_ASSIGN_MODIFY); }
+break;
+case 25:
+#line 293 "./m2-exp.y"
+{ write_exp_elt_opcode(UNOP_PREDECREMENT);}
+break;
+case 26:
+#line 297 "./m2-exp.y"
+{ write_exp_elt_opcode(BINOP_ASSIGN_MODIFY);
+ write_exp_elt_opcode(BINOP_SUB);
+ write_exp_elt_opcode(BINOP_ASSIGN_MODIFY); }
+break;
+case 27:
+#line 303 "./m2-exp.y"
+{ write_exp_elt_opcode (STRUCTOP_STRUCT);
+ write_exp_string (yyvsp[0].sval);
+ write_exp_elt_opcode (STRUCTOP_STRUCT); }
+break;
+case 29:
+#line 312 "./m2-exp.y"
+{ error("Sets are not implemented.");}
+break;
+case 30:
+#line 316 "./m2-exp.y"
+{ error("Sets are not implemented.");}
+break;
+case 31:
+#line 320 "./m2-exp.y"
+{ error("Sets are not implemented.");}
+break;
+case 32:
+#line 323 "./m2-exp.y"
+{ error("Sets are not implemented.");}
+break;
+case 33:
+#line 325 "./m2-exp.y"
+{ error("Sets are not implemented.");}
+break;
+case 34:
+#line 334 "./m2-exp.y"
+{ start_arglist(); }
+break;
+case 35:
+#line 336 "./m2-exp.y"
+{ write_exp_elt_opcode (MULTI_SUBSCRIPT);
+ write_exp_elt_longcst ((LONGEST) end_arglist());
+ write_exp_elt_opcode (MULTI_SUBSCRIPT); }
+break;
+case 36:
+#line 344 "./m2-exp.y"
+{ start_arglist (); }
+break;
+case 37:
+#line 346 "./m2-exp.y"
+{ write_exp_elt_opcode (OP_FUNCALL);
+ write_exp_elt_longcst ((LONGEST) end_arglist ());
+ write_exp_elt_opcode (OP_FUNCALL); }
+break;
+case 39:
+#line 355 "./m2-exp.y"
+{ arglist_len = 1; }
+break;
+case 40:
+#line 359 "./m2-exp.y"
+{ arglist_len++; }
+break;
+case 41:
+#line 364 "./m2-exp.y"
+{ arglist_len = 1; }
+break;
+case 42:
+#line 369 "./m2-exp.y"
+{ arglist_len++; }
+break;
+case 43:
+#line 374 "./m2-exp.y"
+{ write_exp_elt_opcode (UNOP_MEMVAL);
+ write_exp_elt_type (yyvsp[-2].tval);
+ write_exp_elt_opcode (UNOP_MEMVAL); }
+break;
+case 44:
+#line 380 "./m2-exp.y"
+{ write_exp_elt_opcode (UNOP_CAST);
+ write_exp_elt_type (yyvsp[-3].tval);
+ write_exp_elt_opcode (UNOP_CAST); }
+break;
+case 45:
+#line 386 "./m2-exp.y"
+{ }
+break;
+case 46:
+#line 394 "./m2-exp.y"
+{ write_exp_elt_opcode (BINOP_REPEAT); }
+break;
+case 47:
+#line 398 "./m2-exp.y"
+{ write_exp_elt_opcode (BINOP_MUL); }
+break;
+case 48:
+#line 402 "./m2-exp.y"
+{ write_exp_elt_opcode (BINOP_DIV); }
+break;
+case 49:
+#line 406 "./m2-exp.y"
+{ write_exp_elt_opcode (BINOP_INTDIV); }
+break;
+case 50:
+#line 410 "./m2-exp.y"
+{ write_exp_elt_opcode (BINOP_REM); }
+break;
+case 51:
+#line 414 "./m2-exp.y"
+{ write_exp_elt_opcode (BINOP_ADD); }
+break;
+case 52:
+#line 418 "./m2-exp.y"
+{ write_exp_elt_opcode (BINOP_SUB); }
+break;
+case 53:
+#line 422 "./m2-exp.y"
+{ write_exp_elt_opcode (BINOP_EQUAL); }
+break;
+case 54:
+#line 426 "./m2-exp.y"
+{ write_exp_elt_opcode (BINOP_NOTEQUAL); }
+break;
+case 55:
+#line 428 "./m2-exp.y"
+{ write_exp_elt_opcode (BINOP_NOTEQUAL); }
+break;
+case 56:
+#line 432 "./m2-exp.y"
+{ write_exp_elt_opcode (BINOP_LEQ); }
+break;
+case 57:
+#line 436 "./m2-exp.y"
+{ write_exp_elt_opcode (BINOP_GEQ); }
+break;
+case 58:
+#line 440 "./m2-exp.y"
+{ write_exp_elt_opcode (BINOP_LESS); }
+break;
+case 59:
+#line 444 "./m2-exp.y"
+{ write_exp_elt_opcode (BINOP_GTR); }
+break;
+case 60:
+#line 448 "./m2-exp.y"
+{ write_exp_elt_opcode (BINOP_LOGICAL_AND); }
+break;
+case 61:
+#line 452 "./m2-exp.y"
+{ write_exp_elt_opcode (BINOP_LOGICAL_OR); }
+break;
+case 62:
+#line 456 "./m2-exp.y"
+{ write_exp_elt_opcode (BINOP_ASSIGN); }
+break;
+case 63:
+#line 463 "./m2-exp.y"
+{ write_exp_elt_opcode (OP_BOOL);
+ write_exp_elt_longcst ((LONGEST) yyvsp[0].ulval);
+ write_exp_elt_opcode (OP_BOOL); }
+break;
+case 64:
+#line 469 "./m2-exp.y"
+{ write_exp_elt_opcode (OP_BOOL);
+ write_exp_elt_longcst ((LONGEST) yyvsp[0].ulval);
+ write_exp_elt_opcode (OP_BOOL); }
+break;
+case 65:
+#line 475 "./m2-exp.y"
+{ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_m2_int);
+ write_exp_elt_longcst ((LONGEST) yyvsp[0].lval);
+ write_exp_elt_opcode (OP_LONG); }
+break;
+case 66:
+#line 482 "./m2-exp.y"
+{
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_m2_card);
+ write_exp_elt_longcst ((LONGEST) yyvsp[0].ulval);
+ write_exp_elt_opcode (OP_LONG);
+ }
+break;
+case 67:
+#line 491 "./m2-exp.y"
+{ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_m2_char);
+ write_exp_elt_longcst ((LONGEST) yyvsp[0].ulval);
+ write_exp_elt_opcode (OP_LONG); }
+break;
+case 68:
+#line 499 "./m2-exp.y"
+{ write_exp_elt_opcode (OP_DOUBLE);
+ write_exp_elt_type (builtin_type_m2_real);
+ write_exp_elt_dblcst (yyvsp[0].dval);
+ write_exp_elt_opcode (OP_DOUBLE); }
+break;
+case 70:
+#line 510 "./m2-exp.y"
+{ write_exp_elt_opcode (OP_LAST);
+ write_exp_elt_longcst ((LONGEST) yyvsp[0].lval);
+ write_exp_elt_opcode (OP_LAST); }
+break;
+case 71:
+#line 516 "./m2-exp.y"
+{ write_exp_elt_opcode (OP_REGISTER);
+ write_exp_elt_longcst ((LONGEST) yyvsp[0].lval);
+ write_exp_elt_opcode (OP_REGISTER); }
+break;
+case 72:
+#line 522 "./m2-exp.y"
+{ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_int);
+ write_exp_elt_longcst ((LONGEST) TYPE_LENGTH (yyvsp[-1].tval));
+ write_exp_elt_opcode (OP_LONG); }
+break;
+case 73:
+#line 529 "./m2-exp.y"
+{ write_exp_elt_opcode (OP_M2_STRING);
+ write_exp_string (yyvsp[0].sval);
+ write_exp_elt_opcode (OP_M2_STRING); }
+break;
+case 74:
+#line 536 "./m2-exp.y"
+{ yyval.bval = SYMBOL_BLOCK_VALUE(yyvsp[0].sym); }
+break;
+case 75:
+#line 540 "./m2-exp.y"
+{ struct symbol *sym
+ = lookup_symbol (copy_name (yyvsp[0].sval), expression_context_block,
+ VAR_NAMESPACE, 0, NULL);
+ yyval.sym = sym;}
+break;
+case 76:
+#line 549 "./m2-exp.y"
+{ struct symbol *tem
+ = lookup_symbol (copy_name (yyvsp[0].sval), yyvsp[-2].bval,
+ VAR_NAMESPACE, 0, NULL);
+ if (!tem || SYMBOL_CLASS (tem) != LOC_BLOCK)
+ error ("No function \"%s\" in specified context.",
+ copy_name (yyvsp[0].sval));
+ yyval.sym = tem;
+ }
+break;
+case 77:
+#line 561 "./m2-exp.y"
+{ write_exp_elt_opcode(OP_VAR_VALUE);
+ write_exp_elt_block (NULL);
+ write_exp_elt_sym (yyvsp[0].sym);
+ write_exp_elt_opcode (OP_VAR_VALUE); }
+break;
+case 78:
+#line 569 "./m2-exp.y"
+{ write_exp_elt_opcode (OP_INTERNALVAR);
+ write_exp_elt_intern (yyvsp[0].ivar);
+ write_exp_elt_opcode (OP_INTERNALVAR); }
+break;
+case 79:
+#line 576 "./m2-exp.y"
+{ struct symbol *sym;
+ sym = lookup_symbol (copy_name (yyvsp[0].sval), yyvsp[-2].bval,
+ VAR_NAMESPACE, 0, NULL);
+ if (sym == 0)
+ error ("No symbol \"%s\" in specified context.",
+ copy_name (yyvsp[0].sval));
+
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ /* block_found is set by lookup_symbol. */
+ write_exp_elt_block (block_found);
+ write_exp_elt_sym (sym);
+ write_exp_elt_opcode (OP_VAR_VALUE); }
+break;
+case 80:
+#line 592 "./m2-exp.y"
+{ struct symbol *sym;
+ int is_a_field_of_this;
+
+ sym = lookup_symbol (copy_name (yyvsp[0].sval),
+ expression_context_block,
+ VAR_NAMESPACE,
+ &is_a_field_of_this,
+ NULL);
+ if (sym)
+ {
+ if (symbol_read_needs_frame (sym))
+ {
+ if (innermost_block == 0 ||
+ contained_in (block_found,
+ innermost_block))
+ innermost_block = block_found;
+ }
+
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ /* We want to use the selected frame, not
+ another more inner frame which happens to
+ be in the same block. */
+ write_exp_elt_block (NULL);
+ write_exp_elt_sym (sym);
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ }
+ else
+ {
+ struct minimal_symbol *msymbol;
+ register char *arg = copy_name (yyvsp[0].sval);
+
+ msymbol = lookup_minimal_symbol (arg,
+ (struct objfile *) NULL);
+ if (msymbol != NULL)
+ {
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_long);
+ write_exp_elt_longcst ((LONGEST) SYMBOL_VALUE_ADDRESS (msymbol));
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_opcode (UNOP_MEMVAL);
+ if (msymbol -> type == mst_data ||
+ msymbol -> type == mst_bss)
+ write_exp_elt_type (builtin_type_int);
+ else if (msymbol -> type == mst_text)
+ write_exp_elt_type (lookup_function_type (builtin_type_int));
+ else
+ write_exp_elt_type (builtin_type_char);
+ write_exp_elt_opcode (UNOP_MEMVAL);
+ }
+ else if (!have_full_symbols () && !have_partial_symbols ())
+ error ("No symbol table is loaded. Use the \"symbol-file\" command.");
+ else
+ error ("No symbol \"%s\" in current context.",
+ copy_name (yyvsp[0].sval));
+ }
+ }
+break;
+case 81:
+#line 652 "./m2-exp.y"
+{ yyval.tval = lookup_typename (copy_name (yyvsp[0].sval),
+ expression_context_block, 0); }
+break;
+#line 1936 "y.tab.c"
+ }
+ yyssp -= yym;
+ yystate = *yyssp;
+ yyvsp -= yym;
+ yym = yylhs[yyn];
+ if (yystate == 0 && yym == 0)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state 0 to\
+ state %d\n", YYPREFIX, YYFINAL);
+#endif
+ yystate = YYFINAL;
+ *++yyssp = YYFINAL;
+ *++yyvsp = yyval;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, YYFINAL, yychar, yys);
+ }
+#endif
+ }
+ if (yychar == 0) goto yyaccept;
+ goto yyloop;
+ }
+ if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yystate)
+ yystate = yytable[yyn];
+ else
+ yystate = yydgoto[yym];
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state %d \
+to state %d\n", YYPREFIX, *yyssp, yystate);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate;
+ *++yyvsp = yyval;
+ goto yyloop;
+yyoverflow:
+ yyerror("yacc stack overflow");
+yyabort:
+ return (1);
+yyaccept:
+ return (0);
+}
diff --git a/gnu/usr.bin/gdb/gdb/m2-exp.y b/gnu/usr.bin/gdb/gdb/m2-exp.y
new file mode 100644
index 0000000..cc4001f
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/m2-exp.y
@@ -0,0 +1,1169 @@
+/* YACC grammar for Modula-2 expressions, for GDB.
+ Copyright (C) 1986, 1989, 1990, 1991 Free Software Foundation, Inc.
+ Generated from expread.y (now c-exp.y) and contributed by the Department
+ of Computer Science at the State University of New York at Buffalo, 1991.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Parse a Modula-2 expression from text in a string,
+ and return the result as a struct expression pointer.
+ That structure contains arithmetic operations in reverse polish,
+ with constants represented by operations that are followed by special data.
+ See expression.h for the details of the format.
+ What is important here is that it can be built up sequentially
+ during the process of parsing; the lower levels of the tree always
+ come first in the result.
+
+ Note that malloc's and realloc's in this file are transformed to
+ xmalloc and xrealloc respectively by the same sed command in the
+ makefile that remaps any other malloc/realloc inserted by the parser
+ generator. Doing this with #defines and trying to control the interaction
+ with include files (<malloc.h> and <stdlib.h> for example) just became
+ too messy, particularly when such includes can be inserted at random
+ times by the parser generator. */
+
+%{
+
+#include "defs.h"
+#include "expression.h"
+#include "language.h"
+#include "value.h"
+#include "parser-defs.h"
+#include "m2-lang.h"
+
+/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
+ as well as gratuitiously global symbol names, so we can have multiple
+ yacc generated parsers in gdb. Note that these are only the variables
+ produced by yacc. If other parser generators (bison, byacc, etc) produce
+ additional global names that conflict at link time, then those parser
+ generators need to be fixed instead of adding those names to this list. */
+
+#define yymaxdepth m2_maxdepth
+#define yyparse m2_parse
+#define yylex m2_lex
+#define yyerror m2_error
+#define yylval m2_lval
+#define yychar m2_char
+#define yydebug m2_debug
+#define yypact m2_pact
+#define yyr1 m2_r1
+#define yyr2 m2_r2
+#define yydef m2_def
+#define yychk m2_chk
+#define yypgo m2_pgo
+#define yyact m2_act
+#define yyexca m2_exca
+#define yyerrflag m2_errflag
+#define yynerrs m2_nerrs
+#define yyps m2_ps
+#define yypv m2_pv
+#define yys m2_s
+#define yy_yys m2_yys
+#define yystate m2_state
+#define yytmp m2_tmp
+#define yyv m2_v
+#define yy_yyv m2_yyv
+#define yyval m2_val
+#define yylloc m2_lloc
+#define yyreds m2_reds /* With YYDEBUG defined */
+#define yytoks m2_toks /* With YYDEBUG defined */
+
+#ifndef YYDEBUG
+#define YYDEBUG 0 /* Default to no yydebug support */
+#endif
+
+int
+yyparse PARAMS ((void));
+
+static int
+yylex PARAMS ((void));
+
+void
+yyerror PARAMS ((char *));
+
+#if 0
+static char *
+make_qualname PARAMS ((char *, char *));
+#endif
+
+static int
+parse_number PARAMS ((int));
+
+/* The sign of the number being parsed. */
+static int number_sign = 1;
+
+/* The block that the module specified by the qualifer on an identifer is
+ contained in, */
+#if 0
+static struct block *modblock=0;
+#endif
+
+%}
+
+/* Although the yacc "value" of an expression is not used,
+ since the result is stored in the structure being created,
+ other node types do have values. */
+
+%union
+ {
+ LONGEST lval;
+ unsigned LONGEST ulval;
+ double dval;
+ struct symbol *sym;
+ struct type *tval;
+ struct stoken sval;
+ int voidval;
+ struct block *bval;
+ enum exp_opcode opcode;
+ struct internalvar *ivar;
+
+ struct type **tvec;
+ int *ivec;
+ }
+
+%type <voidval> exp type_exp start set
+%type <voidval> variable
+%type <tval> type
+%type <bval> block
+%type <sym> fblock
+
+%token <lval> INT HEX ERROR
+%token <ulval> UINT M2_TRUE M2_FALSE CHAR
+%token <dval> FLOAT
+
+/* Both NAME and TYPENAME tokens represent symbols in the input,
+ and both convey their data as strings.
+ But a TYPENAME is a string that happens to be defined as a typedef
+ or builtin type name (such as int or char)
+ and a NAME is any other symbol.
+
+ Contexts where this distinction is not important can use the
+ nonterminal "name", which matches either NAME or TYPENAME. */
+
+%token <sval> STRING
+%token <sval> NAME BLOCKNAME IDENT VARNAME
+%token <sval> TYPENAME
+
+%token SIZE CAP ORD HIGH ABS MIN_FUNC MAX_FUNC FLOAT_FUNC VAL CHR ODD TRUNC
+%token INC DEC INCL EXCL
+
+/* The GDB scope operator */
+%token COLONCOLON
+
+%token <lval> LAST REGNAME
+
+%token <ivar> INTERNAL_VAR
+
+/* M2 tokens */
+%left ','
+%left ABOVE_COMMA
+%nonassoc ASSIGN
+%left '<' '>' LEQ GEQ '=' NOTEQUAL '#' IN
+%left OROR
+%left LOGICAL_AND '&'
+%left '@'
+%left '+' '-'
+%left '*' '/' DIV MOD
+%right UNARY
+%right '^' DOT '[' '('
+%right NOT '~'
+%left COLONCOLON QID
+/* This is not an actual token ; it is used for precedence.
+%right QID
+*/
+
+
+%%
+
+start : exp
+ | type_exp
+ ;
+
+type_exp: type
+ { write_exp_elt_opcode(OP_TYPE);
+ write_exp_elt_type($1);
+ write_exp_elt_opcode(OP_TYPE);
+ }
+ ;
+
+/* Expressions */
+
+exp : exp '^' %prec UNARY
+ { write_exp_elt_opcode (UNOP_IND); }
+
+exp : '-'
+ { number_sign = -1; }
+ exp %prec UNARY
+ { number_sign = 1;
+ write_exp_elt_opcode (UNOP_NEG); }
+ ;
+
+exp : '+' exp %prec UNARY
+ { write_exp_elt_opcode(UNOP_PLUS); }
+ ;
+
+exp : not_exp exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_LOGICAL_NOT); }
+ ;
+
+not_exp : NOT
+ | '~'
+ ;
+
+exp : CAP '(' exp ')'
+ { write_exp_elt_opcode (UNOP_CAP); }
+ ;
+
+exp : ORD '(' exp ')'
+ { write_exp_elt_opcode (UNOP_ORD); }
+ ;
+
+exp : ABS '(' exp ')'
+ { write_exp_elt_opcode (UNOP_ABS); }
+ ;
+
+exp : HIGH '(' exp ')'
+ { write_exp_elt_opcode (UNOP_HIGH); }
+ ;
+
+exp : MIN_FUNC '(' type ')'
+ { write_exp_elt_opcode (UNOP_MIN);
+ write_exp_elt_type ($3);
+ write_exp_elt_opcode (UNOP_MIN); }
+ ;
+
+exp : MAX_FUNC '(' type ')'
+ { write_exp_elt_opcode (UNOP_MAX);
+ write_exp_elt_type ($3);
+ write_exp_elt_opcode (UNOP_MIN); }
+ ;
+
+exp : FLOAT_FUNC '(' exp ')'
+ { write_exp_elt_opcode (UNOP_FLOAT); }
+ ;
+
+exp : VAL '(' type ',' exp ')'
+ { write_exp_elt_opcode (BINOP_VAL);
+ write_exp_elt_type ($3);
+ write_exp_elt_opcode (BINOP_VAL); }
+ ;
+
+exp : CHR '(' exp ')'
+ { write_exp_elt_opcode (UNOP_CHR); }
+ ;
+
+exp : ODD '(' exp ')'
+ { write_exp_elt_opcode (UNOP_ODD); }
+ ;
+
+exp : TRUNC '(' exp ')'
+ { write_exp_elt_opcode (UNOP_TRUNC); }
+ ;
+
+exp : SIZE exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_SIZEOF); }
+ ;
+
+
+exp : INC '(' exp ')'
+ { write_exp_elt_opcode(UNOP_PREINCREMENT); }
+ ;
+
+exp : INC '(' exp ',' exp ')'
+ { write_exp_elt_opcode(BINOP_ASSIGN_MODIFY);
+ write_exp_elt_opcode(BINOP_ADD);
+ write_exp_elt_opcode(BINOP_ASSIGN_MODIFY); }
+ ;
+
+exp : DEC '(' exp ')'
+ { write_exp_elt_opcode(UNOP_PREDECREMENT);}
+ ;
+
+exp : DEC '(' exp ',' exp ')'
+ { write_exp_elt_opcode(BINOP_ASSIGN_MODIFY);
+ write_exp_elt_opcode(BINOP_SUB);
+ write_exp_elt_opcode(BINOP_ASSIGN_MODIFY); }
+ ;
+
+exp : exp DOT NAME
+ { write_exp_elt_opcode (STRUCTOP_STRUCT);
+ write_exp_string ($3);
+ write_exp_elt_opcode (STRUCTOP_STRUCT); }
+ ;
+
+exp : set
+ ;
+
+exp : exp IN set
+ { error("Sets are not implemented.");}
+ ;
+
+exp : INCL '(' exp ',' exp ')'
+ { error("Sets are not implemented.");}
+ ;
+
+exp : EXCL '(' exp ',' exp ')'
+ { error("Sets are not implemented.");}
+
+set : '{' arglist '}'
+ { error("Sets are not implemented.");}
+ | type '{' arglist '}'
+ { error("Sets are not implemented.");}
+ ;
+
+
+/* Modula-2 array subscript notation [a,b,c...] */
+exp : exp '['
+ /* This function just saves the number of arguments
+ that follow in the list. It is *not* specific to
+ function types */
+ { start_arglist(); }
+ non_empty_arglist ']' %prec DOT
+ { write_exp_elt_opcode (MULTI_SUBSCRIPT);
+ write_exp_elt_longcst ((LONGEST) end_arglist());
+ write_exp_elt_opcode (MULTI_SUBSCRIPT); }
+ ;
+
+exp : exp '('
+ /* This is to save the value of arglist_len
+ being accumulated by an outer function call. */
+ { start_arglist (); }
+ arglist ')' %prec DOT
+ { write_exp_elt_opcode (OP_FUNCALL);
+ write_exp_elt_longcst ((LONGEST) end_arglist ());
+ write_exp_elt_opcode (OP_FUNCALL); }
+ ;
+
+arglist :
+ ;
+
+arglist : exp
+ { arglist_len = 1; }
+ ;
+
+arglist : arglist ',' exp %prec ABOVE_COMMA
+ { arglist_len++; }
+ ;
+
+non_empty_arglist
+ : exp
+ { arglist_len = 1; }
+ ;
+
+non_empty_arglist
+ : non_empty_arglist ',' exp %prec ABOVE_COMMA
+ { arglist_len++; }
+ ;
+
+/* GDB construct */
+exp : '{' type '}' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_MEMVAL);
+ write_exp_elt_type ($2);
+ write_exp_elt_opcode (UNOP_MEMVAL); }
+ ;
+
+exp : type '(' exp ')' %prec UNARY
+ { write_exp_elt_opcode (UNOP_CAST);
+ write_exp_elt_type ($1);
+ write_exp_elt_opcode (UNOP_CAST); }
+ ;
+
+exp : '(' exp ')'
+ { }
+ ;
+
+/* Binary operators in order of decreasing precedence. Note that some
+ of these operators are overloaded! (ie. sets) */
+
+/* GDB construct */
+exp : exp '@' exp
+ { write_exp_elt_opcode (BINOP_REPEAT); }
+ ;
+
+exp : exp '*' exp
+ { write_exp_elt_opcode (BINOP_MUL); }
+ ;
+
+exp : exp '/' exp
+ { write_exp_elt_opcode (BINOP_DIV); }
+ ;
+
+exp : exp DIV exp
+ { write_exp_elt_opcode (BINOP_INTDIV); }
+ ;
+
+exp : exp MOD exp
+ { write_exp_elt_opcode (BINOP_REM); }
+ ;
+
+exp : exp '+' exp
+ { write_exp_elt_opcode (BINOP_ADD); }
+ ;
+
+exp : exp '-' exp
+ { write_exp_elt_opcode (BINOP_SUB); }
+ ;
+
+exp : exp '=' exp
+ { write_exp_elt_opcode (BINOP_EQUAL); }
+ ;
+
+exp : exp NOTEQUAL exp
+ { write_exp_elt_opcode (BINOP_NOTEQUAL); }
+ | exp '#' exp
+ { write_exp_elt_opcode (BINOP_NOTEQUAL); }
+ ;
+
+exp : exp LEQ exp
+ { write_exp_elt_opcode (BINOP_LEQ); }
+ ;
+
+exp : exp GEQ exp
+ { write_exp_elt_opcode (BINOP_GEQ); }
+ ;
+
+exp : exp '<' exp
+ { write_exp_elt_opcode (BINOP_LESS); }
+ ;
+
+exp : exp '>' exp
+ { write_exp_elt_opcode (BINOP_GTR); }
+ ;
+
+exp : exp LOGICAL_AND exp
+ { write_exp_elt_opcode (BINOP_LOGICAL_AND); }
+ ;
+
+exp : exp OROR exp
+ { write_exp_elt_opcode (BINOP_LOGICAL_OR); }
+ ;
+
+exp : exp ASSIGN exp
+ { write_exp_elt_opcode (BINOP_ASSIGN); }
+ ;
+
+
+/* Constants */
+
+exp : M2_TRUE
+ { write_exp_elt_opcode (OP_BOOL);
+ write_exp_elt_longcst ((LONGEST) $1);
+ write_exp_elt_opcode (OP_BOOL); }
+ ;
+
+exp : M2_FALSE
+ { write_exp_elt_opcode (OP_BOOL);
+ write_exp_elt_longcst ((LONGEST) $1);
+ write_exp_elt_opcode (OP_BOOL); }
+ ;
+
+exp : INT
+ { write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_m2_int);
+ write_exp_elt_longcst ((LONGEST) $1);
+ write_exp_elt_opcode (OP_LONG); }
+ ;
+
+exp : UINT
+ {
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_m2_card);
+ write_exp_elt_longcst ((LONGEST) $1);
+ write_exp_elt_opcode (OP_LONG);
+ }
+ ;
+
+exp : CHAR
+ { write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_m2_char);
+ write_exp_elt_longcst ((LONGEST) $1);
+ write_exp_elt_opcode (OP_LONG); }
+ ;
+
+
+exp : FLOAT
+ { write_exp_elt_opcode (OP_DOUBLE);
+ write_exp_elt_type (builtin_type_m2_real);
+ write_exp_elt_dblcst ($1);
+ write_exp_elt_opcode (OP_DOUBLE); }
+ ;
+
+exp : variable
+ ;
+
+/* The GDB internal variable $$, et al. */
+exp : LAST
+ { write_exp_elt_opcode (OP_LAST);
+ write_exp_elt_longcst ((LONGEST) $1);
+ write_exp_elt_opcode (OP_LAST); }
+ ;
+
+exp : REGNAME
+ { write_exp_elt_opcode (OP_REGISTER);
+ write_exp_elt_longcst ((LONGEST) $1);
+ write_exp_elt_opcode (OP_REGISTER); }
+ ;
+
+exp : SIZE '(' type ')' %prec UNARY
+ { write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_int);
+ write_exp_elt_longcst ((LONGEST) TYPE_LENGTH ($3));
+ write_exp_elt_opcode (OP_LONG); }
+ ;
+
+exp : STRING
+ { write_exp_elt_opcode (OP_M2_STRING);
+ write_exp_string ($1);
+ write_exp_elt_opcode (OP_M2_STRING); }
+ ;
+
+/* This will be used for extensions later. Like adding modules. */
+block : fblock
+ { $$ = SYMBOL_BLOCK_VALUE($1); }
+ ;
+
+fblock : BLOCKNAME
+ { struct symbol *sym
+ = lookup_symbol (copy_name ($1), expression_context_block,
+ VAR_NAMESPACE, 0, NULL);
+ $$ = sym;}
+ ;
+
+
+/* GDB scope operator */
+fblock : block COLONCOLON BLOCKNAME
+ { struct symbol *tem
+ = lookup_symbol (copy_name ($3), $1,
+ VAR_NAMESPACE, 0, NULL);
+ if (!tem || SYMBOL_CLASS (tem) != LOC_BLOCK)
+ error ("No function \"%s\" in specified context.",
+ copy_name ($3));
+ $$ = tem;
+ }
+ ;
+
+/* Useful for assigning to PROCEDURE variables */
+variable: fblock
+ { write_exp_elt_opcode(OP_VAR_VALUE);
+ write_exp_elt_block (NULL);
+ write_exp_elt_sym ($1);
+ write_exp_elt_opcode (OP_VAR_VALUE); }
+ ;
+
+/* GDB internal ($foo) variable */
+variable: INTERNAL_VAR
+ { write_exp_elt_opcode (OP_INTERNALVAR);
+ write_exp_elt_intern ($1);
+ write_exp_elt_opcode (OP_INTERNALVAR); }
+ ;
+
+/* GDB scope operator */
+variable: block COLONCOLON NAME
+ { struct symbol *sym;
+ sym = lookup_symbol (copy_name ($3), $1,
+ VAR_NAMESPACE, 0, NULL);
+ if (sym == 0)
+ error ("No symbol \"%s\" in specified context.",
+ copy_name ($3));
+
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ /* block_found is set by lookup_symbol. */
+ write_exp_elt_block (block_found);
+ write_exp_elt_sym (sym);
+ write_exp_elt_opcode (OP_VAR_VALUE); }
+ ;
+
+/* Base case for variables. */
+variable: NAME
+ { struct symbol *sym;
+ int is_a_field_of_this;
+
+ sym = lookup_symbol (copy_name ($1),
+ expression_context_block,
+ VAR_NAMESPACE,
+ &is_a_field_of_this,
+ NULL);
+ if (sym)
+ {
+ if (symbol_read_needs_frame (sym))
+ {
+ if (innermost_block == 0 ||
+ contained_in (block_found,
+ innermost_block))
+ innermost_block = block_found;
+ }
+
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ /* We want to use the selected frame, not
+ another more inner frame which happens to
+ be in the same block. */
+ write_exp_elt_block (NULL);
+ write_exp_elt_sym (sym);
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ }
+ else
+ {
+ struct minimal_symbol *msymbol;
+ register char *arg = copy_name ($1);
+
+ msymbol = lookup_minimal_symbol (arg,
+ (struct objfile *) NULL);
+ if (msymbol != NULL)
+ {
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (builtin_type_long);
+ write_exp_elt_longcst ((LONGEST) SYMBOL_VALUE_ADDRESS (msymbol));
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_opcode (UNOP_MEMVAL);
+ if (msymbol -> type == mst_data ||
+ msymbol -> type == mst_bss)
+ write_exp_elt_type (builtin_type_int);
+ else if (msymbol -> type == mst_text)
+ write_exp_elt_type (lookup_function_type (builtin_type_int));
+ else
+ write_exp_elt_type (builtin_type_char);
+ write_exp_elt_opcode (UNOP_MEMVAL);
+ }
+ else if (!have_full_symbols () && !have_partial_symbols ())
+ error ("No symbol table is loaded. Use the \"symbol-file\" command.");
+ else
+ error ("No symbol \"%s\" in current context.",
+ copy_name ($1));
+ }
+ }
+ ;
+
+type
+ : TYPENAME
+ { $$ = lookup_typename (copy_name ($1),
+ expression_context_block, 0); }
+
+ ;
+
+%%
+
+#if 0 /* FIXME! */
+int
+overflow(a,b)
+ long a,b;
+{
+ return (MAX_OF_TYPE(builtin_type_m2_int) - b) < a;
+}
+
+int
+uoverflow(a,b)
+ unsigned long a,b;
+{
+ return (MAX_OF_TYPE(builtin_type_m2_card) - b) < a;
+}
+#endif /* FIXME */
+
+/* Take care of parsing a number (anything that starts with a digit).
+ Set yylval and return the token type; update lexptr.
+ LEN is the number of characters in it. */
+
+/*** Needs some error checking for the float case ***/
+
+static int
+parse_number (olen)
+ int olen;
+{
+ register char *p = lexptr;
+ register LONGEST n = 0;
+ register LONGEST prevn = 0;
+ register int c,i,ischar=0;
+ register int base = input_radix;
+ register int len = olen;
+ int unsigned_p = number_sign == 1 ? 1 : 0;
+
+ if(p[len-1] == 'H')
+ {
+ base = 16;
+ len--;
+ }
+ else if(p[len-1] == 'C' || p[len-1] == 'B')
+ {
+ base = 8;
+ ischar = p[len-1] == 'C';
+ len--;
+ }
+
+ /* Scan the number */
+ for (c = 0; c < len; c++)
+ {
+ if (p[c] == '.' && base == 10)
+ {
+ /* It's a float since it contains a point. */
+ yylval.dval = atof (p);
+ lexptr += len;
+ return FLOAT;
+ }
+ if (p[c] == '.' && base != 10)
+ error("Floating point numbers must be base 10.");
+ if (base == 10 && (p[c] < '0' || p[c] > '9'))
+ error("Invalid digit \'%c\' in number.",p[c]);
+ }
+
+ while (len-- > 0)
+ {
+ c = *p++;
+ n *= base;
+ if( base == 8 && (c == '8' || c == '9'))
+ error("Invalid digit \'%c\' in octal number.",c);
+ if (c >= '0' && c <= '9')
+ i = c - '0';
+ else
+ {
+ if (base == 16 && c >= 'A' && c <= 'F')
+ i = c - 'A' + 10;
+ else
+ return ERROR;
+ }
+ n+=i;
+ if(i >= base)
+ return ERROR;
+ if(!unsigned_p && number_sign == 1 && (prevn >= n))
+ unsigned_p=1; /* Try something unsigned */
+ /* Don't do the range check if n==i and i==0, since that special
+ case will give an overflow error. */
+ if(RANGE_CHECK && n!=i && i)
+ {
+ if((unsigned_p && (unsigned)prevn >= (unsigned)n) ||
+ ((!unsigned_p && number_sign==-1) && -prevn <= -n))
+ range_error("Overflow on numeric constant.");
+ }
+ prevn=n;
+ }
+
+ lexptr = p;
+ if(*p == 'B' || *p == 'C' || *p == 'H')
+ lexptr++; /* Advance past B,C or H */
+
+ if (ischar)
+ {
+ yylval.ulval = n;
+ return CHAR;
+ }
+ else if ( unsigned_p && number_sign == 1)
+ {
+ yylval.ulval = n;
+ return UINT;
+ }
+ else if((unsigned_p && (n<0))) {
+ range_error("Overflow on numeric constant -- number too large.");
+ /* But, this can return if range_check == range_warn. */
+ }
+ yylval.lval = n;
+ return INT;
+}
+
+
+/* Some tokens */
+
+static struct
+{
+ char name[2];
+ int token;
+} tokentab2[] =
+{
+ { {'<', '>'}, NOTEQUAL },
+ { {':', '='}, ASSIGN },
+ { {'<', '='}, LEQ },
+ { {'>', '='}, GEQ },
+ { {':', ':'}, COLONCOLON },
+
+};
+
+/* Some specific keywords */
+
+struct keyword {
+ char keyw[10];
+ int token;
+};
+
+static struct keyword keytab[] =
+{
+ {"OR" , OROR },
+ {"IN", IN },/* Note space after IN */
+ {"AND", LOGICAL_AND},
+ {"ABS", ABS },
+ {"CHR", CHR },
+ {"DEC", DEC },
+ {"NOT", NOT },
+ {"DIV", DIV },
+ {"INC", INC },
+ {"MAX", MAX_FUNC },
+ {"MIN", MIN_FUNC },
+ {"MOD", MOD },
+ {"ODD", ODD },
+ {"CAP", CAP },
+ {"ORD", ORD },
+ {"VAL", VAL },
+ {"EXCL", EXCL },
+ {"HIGH", HIGH },
+ {"INCL", INCL },
+ {"SIZE", SIZE },
+ {"FLOAT", FLOAT_FUNC },
+ {"TRUNC", TRUNC },
+};
+
+
+/* Read one token, getting characters through lexptr. */
+
+/* This is where we will check to make sure that the language and the operators used are
+ compatible */
+
+static int
+yylex ()
+{
+ register int c;
+ register int namelen;
+ register int i;
+ register char *tokstart;
+ register char quote;
+
+ retry:
+
+ tokstart = lexptr;
+
+
+ /* See if it is a special token of length 2 */
+ for( i = 0 ; i < sizeof tokentab2 / sizeof tokentab2[0] ; i++)
+ if(STREQN(tokentab2[i].name, tokstart, 2))
+ {
+ lexptr += 2;
+ return tokentab2[i].token;
+ }
+
+ switch (c = *tokstart)
+ {
+ case 0:
+ return 0;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ lexptr++;
+ goto retry;
+
+ case '(':
+ paren_depth++;
+ lexptr++;
+ return c;
+
+ case ')':
+ if (paren_depth == 0)
+ return 0;
+ paren_depth--;
+ lexptr++;
+ return c;
+
+ case ',':
+ if (comma_terminates && paren_depth == 0)
+ return 0;
+ lexptr++;
+ return c;
+
+ case '.':
+ /* Might be a floating point number. */
+ if (lexptr[1] >= '0' && lexptr[1] <= '9')
+ break; /* Falls into number code. */
+ else
+ {
+ lexptr++;
+ return DOT;
+ }
+
+/* These are character tokens that appear as-is in the YACC grammar */
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '^':
+ case '<':
+ case '>':
+ case '[':
+ case ']':
+ case '=':
+ case '{':
+ case '}':
+ case '#':
+ case '@':
+ case '~':
+ case '&':
+ lexptr++;
+ return c;
+
+ case '\'' :
+ case '"':
+ quote = c;
+ for (namelen = 1; (c = tokstart[namelen]) != quote && c != '\0'; namelen++)
+ if (c == '\\')
+ {
+ c = tokstart[++namelen];
+ if (c >= '0' && c <= '9')
+ {
+ c = tokstart[++namelen];
+ if (c >= '0' && c <= '9')
+ c = tokstart[++namelen];
+ }
+ }
+ if(c != quote)
+ error("Unterminated string or character constant.");
+ yylval.sval.ptr = tokstart + 1;
+ yylval.sval.length = namelen - 1;
+ lexptr += namelen + 1;
+
+ if(namelen == 2) /* Single character */
+ {
+ yylval.ulval = tokstart[1];
+ return CHAR;
+ }
+ else
+ return STRING;
+ }
+
+ /* Is it a number? */
+ /* Note: We have already dealt with the case of the token '.'.
+ See case '.' above. */
+ if ((c >= '0' && c <= '9'))
+ {
+ /* It's a number. */
+ int got_dot = 0, got_e = 0;
+ register char *p = tokstart;
+ int toktype;
+
+ for (++p ;; ++p)
+ {
+ if (!got_e && (*p == 'e' || *p == 'E'))
+ got_dot = got_e = 1;
+ else if (!got_dot && *p == '.')
+ got_dot = 1;
+ else if (got_e && (p[-1] == 'e' || p[-1] == 'E')
+ && (*p == '-' || *p == '+'))
+ /* This is the sign of the exponent, not the end of the
+ number. */
+ continue;
+ else if ((*p < '0' || *p > '9') &&
+ (*p < 'A' || *p > 'F') &&
+ (*p != 'H')) /* Modula-2 hexadecimal number */
+ break;
+ }
+ toktype = parse_number (p - tokstart);
+ if (toktype == ERROR)
+ {
+ char *err_copy = (char *) alloca (p - tokstart + 1);
+
+ memcpy (err_copy, tokstart, p - tokstart);
+ err_copy[p - tokstart] = 0;
+ error ("Invalid number \"%s\".", err_copy);
+ }
+ lexptr = p;
+ return toktype;
+ }
+
+ if (!(c == '_' || c == '$'
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
+ /* We must have come across a bad character (e.g. ';'). */
+ error ("Invalid character '%c' in expression.", c);
+
+ /* It's a name. See how long it is. */
+ namelen = 0;
+ for (c = tokstart[namelen];
+ (c == '_' || c == '$' || (c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
+ c = tokstart[++namelen])
+ ;
+
+ /* The token "if" terminates the expression and is NOT
+ removed from the input stream. */
+ if (namelen == 2 && tokstart[0] == 'i' && tokstart[1] == 'f')
+ {
+ return 0;
+ }
+
+ lexptr += namelen;
+
+ /* Handle the tokens $digits; also $ (short for $0) and $$ (short for $$1)
+ and $$digits (equivalent to $<-digits> if you could type that).
+ Make token type LAST, and put the number (the digits) in yylval. */
+
+ if (*tokstart == '$')
+ {
+ register int negate = 0;
+ c = 1;
+ /* Double dollar means negate the number and add -1 as well.
+ Thus $$ alone means -1. */
+ if (namelen >= 2 && tokstart[1] == '$')
+ {
+ negate = 1;
+ c = 2;
+ }
+ if (c == namelen)
+ {
+ /* Just dollars (one or two) */
+ yylval.lval = - negate;
+ return LAST;
+ }
+ /* Is the rest of the token digits? */
+ for (; c < namelen; c++)
+ if (!(tokstart[c] >= '0' && tokstart[c] <= '9'))
+ break;
+ if (c == namelen)
+ {
+ yylval.lval = atoi (tokstart + 1 + negate);
+ if (negate)
+ yylval.lval = - yylval.lval;
+ return LAST;
+ }
+ }
+
+ /* Handle tokens that refer to machine registers:
+ $ followed by a register name. */
+
+ if (*tokstart == '$') {
+ for (c = 0; c < NUM_REGS; c++)
+ if (namelen - 1 == strlen (reg_names[c])
+ && STREQN (tokstart + 1, reg_names[c], namelen - 1))
+ {
+ yylval.lval = c;
+ return REGNAME;
+ }
+ for (c = 0; c < num_std_regs; c++)
+ if (namelen - 1 == strlen (std_regs[c].name)
+ && STREQN (tokstart + 1, std_regs[c].name, namelen - 1))
+ {
+ yylval.lval = std_regs[c].regnum;
+ return REGNAME;
+ }
+ }
+
+
+ /* Lookup special keywords */
+ for(i = 0 ; i < sizeof(keytab) / sizeof(keytab[0]) ; i++)
+ if(namelen == strlen(keytab[i].keyw) && STREQN(tokstart,keytab[i].keyw,namelen))
+ return keytab[i].token;
+
+ yylval.sval.ptr = tokstart;
+ yylval.sval.length = namelen;
+
+ /* Any other names starting in $ are debugger internal variables. */
+
+ if (*tokstart == '$')
+ {
+ yylval.ivar = (struct internalvar *) lookup_internalvar (copy_name (yylval.sval) + 1);
+ return INTERNAL_VAR;
+ }
+
+
+ /* Use token-type BLOCKNAME for symbols that happen to be defined as
+ functions. If this is not so, then ...
+ Use token-type TYPENAME for symbols that happen to be defined
+ currently as names of types; NAME for other symbols.
+ The caller is not constrained to care about the distinction. */
+ {
+
+
+ char *tmp = copy_name (yylval.sval);
+ struct symbol *sym;
+
+ if (lookup_partial_symtab (tmp))
+ return BLOCKNAME;
+ sym = lookup_symbol (tmp, expression_context_block,
+ VAR_NAMESPACE, 0, NULL);
+ if (sym && SYMBOL_CLASS (sym) == LOC_BLOCK)
+ return BLOCKNAME;
+ if (lookup_typename (copy_name (yylval.sval), expression_context_block, 1))
+ return TYPENAME;
+
+ if(sym)
+ {
+ switch(sym->class)
+ {
+ case LOC_STATIC:
+ case LOC_REGISTER:
+ case LOC_ARG:
+ case LOC_REF_ARG:
+ case LOC_REGPARM:
+ case LOC_REGPARM_ADDR:
+ case LOC_LOCAL:
+ case LOC_LOCAL_ARG:
+ case LOC_BASEREG:
+ case LOC_BASEREG_ARG:
+ case LOC_CONST:
+ case LOC_CONST_BYTES:
+ case LOC_OPTIMIZED_OUT:
+ return NAME;
+
+ case LOC_TYPEDEF:
+ return TYPENAME;
+
+ case LOC_BLOCK:
+ return BLOCKNAME;
+
+ case LOC_UNDEF:
+ error("internal: Undefined class in m2lex()");
+
+ case LOC_LABEL:
+ error("internal: Unforseen case in m2lex()");
+ }
+ }
+ else
+ {
+ /* Built-in BOOLEAN type. This is sort of a hack. */
+ if(STREQN(tokstart,"TRUE",4))
+ {
+ yylval.ulval = 1;
+ return M2_TRUE;
+ }
+ else if(STREQN(tokstart,"FALSE",5))
+ {
+ yylval.ulval = 0;
+ return M2_FALSE;
+ }
+ }
+
+ /* Must be another type of name... */
+ return NAME;
+ }
+}
+
+#if 0 /* Unused */
+static char *
+make_qualname(mod,ident)
+ char *mod, *ident;
+{
+ char *new = malloc(strlen(mod)+strlen(ident)+2);
+
+ strcpy(new,mod);
+ strcat(new,".");
+ strcat(new,ident);
+ return new;
+}
+#endif /* 0 */
+
+void
+yyerror(msg)
+ char *msg; /* unused */
+{
+ printf("Parsing: %s\n",lexptr);
+ if (yychar < 256)
+ error("Invalid syntax in expression near character '%c'.",yychar);
+ else
+ error("Invalid syntax in expression");
+}
+
diff --git a/gnu/usr.bin/gdb/gdb/m2-lang.c b/gnu/usr.bin/gdb/gdb/m2-lang.c
new file mode 100644
index 0000000..0b678fd
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/m2-lang.c
@@ -0,0 +1,457 @@
+/* Modula 2 language support routines for GDB, the GNU debugger.
+ Copyright 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "expression.h"
+#include "parser-defs.h"
+#include "language.h"
+#include "m2-lang.h"
+
+/* Print the character C on STREAM as part of the contents of a literal
+ string whose delimiter is QUOTER. Note that that format for printing
+ characters and strings is language specific.
+ FIXME: This is a copy of the same function from c-exp.y. It should
+ be replaced with a true Modula version.
+ */
+
+static void
+emit_char (c, stream, quoter)
+ register int c;
+ FILE *stream;
+ int quoter;
+{
+
+ c &= 0xFF; /* Avoid sign bit follies */
+
+ if (PRINT_LITERAL_FORM (c))
+ {
+ if (c == '\\' || c == quoter)
+ {
+ fputs_filtered ("\\", stream);
+ }
+ fprintf_filtered (stream, "%c", c);
+ }
+ else
+ {
+ switch (c)
+ {
+ case '\n':
+ fputs_filtered ("\\n", stream);
+ break;
+ case '\b':
+ fputs_filtered ("\\b", stream);
+ break;
+ case '\t':
+ fputs_filtered ("\\t", stream);
+ break;
+ case '\f':
+ fputs_filtered ("\\f", stream);
+ break;
+ case '\r':
+ fputs_filtered ("\\r", stream);
+ break;
+ case '\033':
+ fputs_filtered ("\\e", stream);
+ break;
+ case '\007':
+ fputs_filtered ("\\a", stream);
+ break;
+ default:
+ fprintf_filtered (stream, "\\%.3o", (unsigned int) c);
+ break;
+ }
+ }
+}
+
+/* FIXME: This is a copy of the same function from c-exp.y. It should
+ be replaced with a true Modula version. */
+
+static void
+m2_printchar (c, stream)
+ int c;
+ FILE *stream;
+{
+ fputs_filtered ("'", stream);
+ emit_char (c, stream, '\'');
+ fputs_filtered ("'", stream);
+}
+
+/* Print the character string STRING, printing at most LENGTH characters.
+ Printing stops early if the number hits print_max; repeat counts
+ are printed as appropriate. Print ellipses at the end if we
+ had to stop before printing LENGTH characters, or if FORCE_ELLIPSES.
+ FIXME: This is a copy of the same function from c-exp.y. It should
+ be replaced with a true Modula version. */
+
+static void
+m2_printstr (stream, string, length, force_ellipses)
+ FILE *stream;
+ char *string;
+ unsigned int length;
+ int force_ellipses;
+{
+ register unsigned int i;
+ unsigned int things_printed = 0;
+ int in_quotes = 0;
+ int need_comma = 0;
+ extern int inspect_it;
+ extern int repeat_count_threshold;
+ extern int print_max;
+
+ if (length == 0)
+ {
+ fputs_filtered ("\"\"", stdout);
+ return;
+ }
+
+ for (i = 0; i < length && things_printed < print_max; ++i)
+ {
+ /* Position of the character we are examining
+ to see whether it is repeated. */
+ unsigned int rep1;
+ /* Number of repetitions we have detected so far. */
+ unsigned int reps;
+
+ QUIT;
+
+ if (need_comma)
+ {
+ fputs_filtered (", ", stream);
+ need_comma = 0;
+ }
+
+ rep1 = i + 1;
+ reps = 1;
+ while (rep1 < length && string[rep1] == string[i])
+ {
+ ++rep1;
+ ++reps;
+ }
+
+ if (reps > repeat_count_threshold)
+ {
+ if (in_quotes)
+ {
+ if (inspect_it)
+ fputs_filtered ("\\\", ", stream);
+ else
+ fputs_filtered ("\", ", stream);
+ in_quotes = 0;
+ }
+ m2_printchar (string[i], stream);
+ fprintf_filtered (stream, " <repeats %u times>", reps);
+ i = rep1 - 1;
+ things_printed += repeat_count_threshold;
+ need_comma = 1;
+ }
+ else
+ {
+ if (!in_quotes)
+ {
+ if (inspect_it)
+ fputs_filtered ("\\\"", stream);
+ else
+ fputs_filtered ("\"", stream);
+ in_quotes = 1;
+ }
+ emit_char (string[i], stream, '"');
+ ++things_printed;
+ }
+ }
+
+ /* Terminate the quotes if necessary. */
+ if (in_quotes)
+ {
+ if (inspect_it)
+ fputs_filtered ("\\\"", stream);
+ else
+ fputs_filtered ("\"", stream);
+ }
+
+ if (force_ellipses || i < length)
+ fputs_filtered ("...", stream);
+}
+
+/* FIXME: This is a copy of c_create_fundamental_type(), before
+ all the non-C types were stripped from it. Needs to be fixed
+ by an experienced Modula programmer. */
+
+static struct type *
+m2_create_fundamental_type (objfile, typeid)
+ struct objfile *objfile;
+ int typeid;
+{
+ register struct type *type = NULL;
+
+ switch (typeid)
+ {
+ default:
+ /* FIXME: For now, if we are asked to produce a type not in this
+ language, create the equivalent of a C integer type with the
+ name "<?type?>". When all the dust settles from the type
+ reconstruction work, this should probably become an error. */
+ type = init_type (TYPE_CODE_INT,
+ TARGET_INT_BIT / TARGET_CHAR_BIT,
+ 0, "<?type?>", objfile);
+ warning ("internal error: no Modula fundamental type %d", typeid);
+ break;
+ case FT_VOID:
+ type = init_type (TYPE_CODE_VOID,
+ TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+ 0, "void", objfile);
+ break;
+ case FT_BOOLEAN:
+ type = init_type (TYPE_CODE_BOOL,
+ TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED, "boolean", objfile);
+ break;
+ case FT_STRING:
+ type = init_type (TYPE_CODE_STRING,
+ TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+ 0, "string", objfile);
+ break;
+ case FT_CHAR:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+ 0, "char", objfile);
+ break;
+ case FT_SIGNED_CHAR:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_SIGNED, "signed char", objfile);
+ break;
+ case FT_UNSIGNED_CHAR:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED, "unsigned char", objfile);
+ break;
+ case FT_SHORT:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_SHORT_BIT / TARGET_CHAR_BIT,
+ 0, "short", objfile);
+ break;
+ case FT_SIGNED_SHORT:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_SHORT_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_SIGNED, "short", objfile); /* FIXME-fnf */
+ break;
+ case FT_UNSIGNED_SHORT:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_SHORT_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED, "unsigned short", objfile);
+ break;
+ case FT_INTEGER:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_INT_BIT / TARGET_CHAR_BIT,
+ 0, "int", objfile);
+ break;
+ case FT_SIGNED_INTEGER:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_INT_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_SIGNED, "int", objfile); /* FIXME -fnf */
+ break;
+ case FT_UNSIGNED_INTEGER:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_INT_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED, "unsigned int", objfile);
+ break;
+ case FT_FIXED_DECIMAL:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_INT_BIT / TARGET_CHAR_BIT,
+ 0, "fixed decimal", objfile);
+ break;
+ case FT_LONG:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_LONG_BIT / TARGET_CHAR_BIT,
+ 0, "long", objfile);
+ break;
+ case FT_SIGNED_LONG:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_LONG_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_SIGNED, "long", objfile); /* FIXME -fnf */
+ break;
+ case FT_UNSIGNED_LONG:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_LONG_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED, "unsigned long", objfile);
+ break;
+ case FT_LONG_LONG:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_LONG_LONG_BIT / TARGET_CHAR_BIT,
+ 0, "long long", objfile);
+ break;
+ case FT_SIGNED_LONG_LONG:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_LONG_LONG_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_SIGNED, "signed long long", objfile);
+ break;
+ case FT_UNSIGNED_LONG_LONG:
+ type = init_type (TYPE_CODE_INT,
+ TARGET_LONG_LONG_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED, "unsigned long long", objfile);
+ break;
+ case FT_FLOAT:
+ type = init_type (TYPE_CODE_FLT,
+ TARGET_FLOAT_BIT / TARGET_CHAR_BIT,
+ 0, "float", objfile);
+ break;
+ case FT_DBL_PREC_FLOAT:
+ type = init_type (TYPE_CODE_FLT,
+ TARGET_DOUBLE_BIT / TARGET_CHAR_BIT,
+ 0, "double", objfile);
+ break;
+ case FT_FLOAT_DECIMAL:
+ type = init_type (TYPE_CODE_FLT,
+ TARGET_DOUBLE_BIT / TARGET_CHAR_BIT,
+ 0, "floating decimal", objfile);
+ break;
+ case FT_EXT_PREC_FLOAT:
+ type = init_type (TYPE_CODE_FLT,
+ TARGET_LONG_DOUBLE_BIT / TARGET_CHAR_BIT,
+ 0, "long double", objfile);
+ break;
+ case FT_COMPLEX:
+ type = init_type (TYPE_CODE_FLT,
+ TARGET_COMPLEX_BIT / TARGET_CHAR_BIT,
+ 0, "complex", objfile);
+ break;
+ case FT_DBL_PREC_COMPLEX:
+ type = init_type (TYPE_CODE_FLT,
+ TARGET_DOUBLE_COMPLEX_BIT / TARGET_CHAR_BIT,
+ 0, "double complex", objfile);
+ break;
+ case FT_EXT_PREC_COMPLEX:
+ type = init_type (TYPE_CODE_FLT,
+ TARGET_DOUBLE_COMPLEX_BIT / TARGET_CHAR_BIT,
+ 0, "long double complex", objfile);
+ break;
+ }
+ return (type);
+}
+
+
+/* Table of operators and their precedences for printing expressions. */
+
+static const struct op_print m2_op_print_tab[] = {
+ {"+", BINOP_ADD, PREC_ADD, 0},
+ {"+", UNOP_PLUS, PREC_PREFIX, 0},
+ {"-", BINOP_SUB, PREC_ADD, 0},
+ {"-", UNOP_NEG, PREC_PREFIX, 0},
+ {"*", BINOP_MUL, PREC_MUL, 0},
+ {"/", BINOP_DIV, PREC_MUL, 0},
+ {"DIV", BINOP_INTDIV, PREC_MUL, 0},
+ {"MOD", BINOP_REM, PREC_MUL, 0},
+ {":=", BINOP_ASSIGN, PREC_ASSIGN, 1},
+ {"OR", BINOP_LOGICAL_OR, PREC_LOGICAL_OR, 0},
+ {"AND", BINOP_LOGICAL_AND, PREC_LOGICAL_AND, 0},
+ {"NOT", UNOP_LOGICAL_NOT, PREC_PREFIX, 0},
+ {"=", BINOP_EQUAL, PREC_EQUAL, 0},
+ {"<>", BINOP_NOTEQUAL, PREC_EQUAL, 0},
+ {"<=", BINOP_LEQ, PREC_ORDER, 0},
+ {">=", BINOP_GEQ, PREC_ORDER, 0},
+ {">", BINOP_GTR, PREC_ORDER, 0},
+ {"<", BINOP_LESS, PREC_ORDER, 0},
+ {"^", UNOP_IND, PREC_PREFIX, 0},
+ {"@", BINOP_REPEAT, PREC_REPEAT, 0},
+ {NULL, 0, 0, 0}
+};
+
+/* The built-in types of Modula-2. */
+
+struct type *builtin_type_m2_char;
+struct type *builtin_type_m2_int;
+struct type *builtin_type_m2_card;
+struct type *builtin_type_m2_real;
+struct type *builtin_type_m2_bool;
+
+struct type ** const (m2_builtin_types[]) =
+{
+ &builtin_type_m2_char,
+ &builtin_type_m2_int,
+ &builtin_type_m2_card,
+ &builtin_type_m2_real,
+ &builtin_type_m2_bool,
+ 0
+};
+
+const struct language_defn m2_language_defn = {
+ "modula-2",
+ language_m2,
+ m2_builtin_types,
+ range_check_on,
+ type_check_on,
+ m2_parse, /* parser */
+ m2_error, /* parser error function */
+ m2_printchar, /* Print character constant */
+ m2_printstr, /* function to print string constant */
+ m2_create_fundamental_type, /* Create fundamental type in this language */
+ m2_print_type, /* Print a type using appropriate syntax */
+ m2_val_print, /* Print a value using appropriate syntax */
+ &builtin_type_m2_int, /* longest signed integral type */
+ &builtin_type_m2_card, /* longest unsigned integral type */
+ &builtin_type_m2_real, /* longest floating point type */
+ {"", "", "", ""}, /* Binary format info */
+ {"%loB", "", "o", "B"}, /* Octal format info */
+ {"%ld", "", "d", ""}, /* Decimal format info */
+ {"0%lXH", "0", "X", "H"}, /* Hex format info */
+ m2_op_print_tab, /* expression operators for printing */
+ LANG_MAGIC
+};
+
+/* Initialization for Modula-2 */
+
+void
+_initialize_m2_language ()
+{
+ /* Modula-2 "pervasive" types. NOTE: these can be redefined!!! */
+ builtin_type_m2_int =
+ init_type (TYPE_CODE_INT, TARGET_INT_BIT / TARGET_CHAR_BIT,
+ 0,
+ "INTEGER", (struct objfile *) NULL);
+ builtin_type_m2_card =
+ init_type (TYPE_CODE_INT, TARGET_INT_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED,
+ "CARDINAL", (struct objfile *) NULL);
+ builtin_type_m2_real =
+ init_type (TYPE_CODE_FLT, TARGET_FLOAT_BIT / TARGET_CHAR_BIT,
+ 0,
+ "REAL", (struct objfile *) NULL);
+ builtin_type_m2_char =
+ init_type (TYPE_CODE_CHAR, TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED,
+ "CHAR", (struct objfile *) NULL);
+ builtin_type_m2_bool =
+ init_type (TYPE_CODE_BOOL, TARGET_INT_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED,
+ "BOOLEAN", (struct objfile *) NULL);
+
+ TYPE_NFIELDS(builtin_type_m2_bool) = 2;
+ TYPE_FIELDS(builtin_type_m2_bool) =
+ (struct field *) xmalloc (sizeof (struct field) * 2);
+ TYPE_FIELD_BITPOS(builtin_type_m2_bool,0) = 0;
+ TYPE_FIELD_NAME(builtin_type_m2_bool,0) = (char *)xmalloc(6);
+ strcpy(TYPE_FIELD_NAME(builtin_type_m2_bool,0),"FALSE");
+ TYPE_FIELD_BITPOS(builtin_type_m2_bool,1) = 1;
+ TYPE_FIELD_NAME(builtin_type_m2_bool,1) = (char *)xmalloc(5);
+ strcpy(TYPE_FIELD_NAME(builtin_type_m2_bool,1),"TRUE");
+
+ add_language (&m2_language_defn);
+}
diff --git a/gnu/usr.bin/gdb/gdb/m2-lang.h b/gnu/usr.bin/gdb/gdb/m2-lang.h
new file mode 100644
index 0000000..4bc57f5
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/m2-lang.h
@@ -0,0 +1,31 @@
+/* Modula 2 language support definitions for GDB, the GNU debugger.
+ Copyright 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+extern int
+m2_parse PARAMS ((void)); /* Defined in m2-exp.y */
+
+extern void
+m2_error PARAMS ((char *)); /* Defined in m2-exp.y */
+
+extern void /* Defined in m2-typeprint.c */
+m2_print_type PARAMS ((struct type *, char *, FILE *, int, int));
+
+extern int
+m2_val_print PARAMS ((struct type *, char *, CORE_ADDR, FILE *, int, int,
+ int, enum val_prettyprint));
diff --git a/gnu/usr.bin/gdb/gdb/m2-typeprint.c b/gnu/usr.bin/gdb/gdb/m2-typeprint.c
new file mode 100644
index 0000000..ef66a80
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/m2-typeprint.c
@@ -0,0 +1,49 @@
+/* Support for printing Modula 2 types for GDB, the GNU debugger.
+ Copyright 1986, 1988, 1989, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "obstack.h"
+#include "bfd.h" /* Binary File Description */
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "expression.h"
+#include "value.h"
+#include "gdbcore.h"
+#include "target.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "language.h"
+#include "demangle.h"
+#include "m2-lang.h"
+
+#include <string.h>
+#include <errno.h>
+
+void
+m2_print_type (type, varstring, stream, show, level)
+ struct type *type;
+ char *varstring;
+ FILE *stream;
+ int show;
+ int level;
+{
+ extern void c_print_type PARAMS ((struct type *, char *, FILE *, int, int));
+
+ c_print_type (type, varstring, stream, show, level); /* FIXME */
+}
diff --git a/gnu/usr.bin/gdb/gdb/m2-valprint.c b/gnu/usr.bin/gdb/gdb/m2-valprint.c
new file mode 100644
index 0000000..fc17ea5
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/m2-valprint.c
@@ -0,0 +1,45 @@
+/* Support for printing Modula 2 values for GDB, the GNU debugger.
+ Copyright 1986, 1988, 1989, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "obstack.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "valprint.h"
+
+/* FIXME: For now, just explicitly declare c_val_print and use it instead */
+
+int
+m2_val_print (type, valaddr, address, stream, format, deref_ref, recurse,
+ pretty)
+ struct type *type;
+ char *valaddr;
+ CORE_ADDR address;
+ FILE *stream;
+ int format;
+ int deref_ref;
+ int recurse;
+ enum val_prettyprint pretty;
+{
+ extern int
+ c_val_print PARAMS ((struct type *, char *, CORE_ADDR, FILE *, int, int,
+ int, enum val_prettyprint));
+ return (c_val_print (type, valaddr, address, stream, format, deref_ref,
+ recurse, pretty));
+}
diff --git a/gnu/usr.bin/gdb/gdb/main.c b/gnu/usr.bin/gdb/gdb/main.c
new file mode 100644
index 0000000..deaf624
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/main.c
@@ -0,0 +1,2799 @@
+/* Top level `main' program for GDB, the GNU debugger.
+ Copyright 1986, 1987, 1988, 1989, 1990, 1991, 1992
+ Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "gdbcmd.h"
+#include "call-cmds.h"
+#include "symtab.h"
+#include "inferior.h"
+#include "signals.h"
+#include "target.h"
+#include "breakpoint.h"
+#include "gdbtypes.h"
+#include "expression.h"
+#include "language.h"
+#include "terminal.h" /* For job_control. */
+
+#include "getopt.h"
+
+/* readline include files */
+#include "readline.h"
+#include "history.h"
+
+/* readline defines this. */
+#undef savestring
+
+#ifdef USG
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#include <string.h>
+#ifndef NO_SYS_FILE
+#include <sys/file.h>
+#endif
+#include <setjmp.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <ctype.h>
+
+#ifdef SET_STACK_LIMIT_HUGE
+#include <sys/time.h>
+#include <sys/resource.h>
+
+int original_stack_limit;
+#endif
+
+/* Prototypes for local functions */
+
+static char *
+symbol_completion_function PARAMS ((char *, int));
+
+static void
+command_loop PARAMS ((void));
+
+static void
+command_loop_marker PARAMS ((int));
+
+static void
+print_gdb_version PARAMS ((FILE *));
+
+static void
+quit_command PARAMS ((char *, int));
+
+static void
+init_main PARAMS ((void));
+
+static void
+init_history PARAMS ((void));
+
+static void
+init_cmd_lists PARAMS ((void));
+
+static void
+float_handler PARAMS ((int));
+
+static void
+source_command PARAMS ((char *, int));
+
+static void cd_command PARAMS ((char *, int));
+
+static void
+print_gnu_advertisement PARAMS ((void));
+
+static void
+init_signals PARAMS ((void));
+
+static void
+read_command_file PARAMS ((FILE *));
+
+static void
+set_verbose PARAMS ((char *, int, struct cmd_list_element *));
+
+static void
+show_history PARAMS ((char *, int));
+
+static void
+set_history PARAMS ((char *, int));
+
+static void
+set_history_size_command PARAMS ((char *, int, struct cmd_list_element *));
+
+static void
+show_commands PARAMS ((char *, int));
+
+static void
+echo_command PARAMS ((char *, int));
+
+static void
+pwd_command PARAMS ((char *, int));
+
+static void
+show_version PARAMS ((char *, int));
+
+static void
+document_command PARAMS ((char *, int));
+
+static void
+define_command PARAMS ((char *, int));
+
+static void
+validate_comname PARAMS ((char *));
+
+static void
+help_command PARAMS ((char *, int));
+
+static void
+show_command PARAMS ((char *, int));
+
+static void
+info_command PARAMS ((char *, int));
+
+static void
+do_nothing PARAMS ((int));
+
+static int
+quit_cover PARAMS ((char *));
+
+static void
+disconnect PARAMS ((int));
+
+static void
+source_cleanup PARAMS ((FILE *));
+
+/* If this definition isn't overridden by the header files, assume
+ that isatty and fileno exist on this system. */
+#ifndef ISATTY
+#define ISATTY(FP) (isatty (fileno (FP)))
+#endif
+
+/* Initialization file name for gdb. This is overridden in some configs. */
+
+#ifndef GDBINIT_FILENAME
+#define GDBINIT_FILENAME ".gdbinit"
+#endif
+static char gdbinit[] = GDBINIT_FILENAME;
+static int inhibit_gdbinit = 0;
+
+#define ALL_CLEANUPS ((struct cleanup *)0)
+
+/* Version number of GDB, as a string. */
+
+extern char *version;
+
+/* Canonical host name as a string. */
+
+extern char *host_canonical;
+
+/* Canonical target name as a string. */
+
+extern char *target_canonical;
+
+/* Message to be printed before the error message, when an error occurs. */
+
+extern char *error_pre_print;
+
+/* Message to be printed before the warning message, when a warning occurs. */
+
+extern char *warning_pre_print;
+
+extern char lang_frame_mismatch_warn[]; /* language.c */
+
+/* Flag for whether we want all the "from_tty" gubbish printed. */
+
+int caution = 1; /* Default is yes, sigh. */
+
+/*
+ * Define all cmd_list_element's
+ */
+
+/* Chain containing all defined commands. */
+
+struct cmd_list_element *cmdlist;
+
+/* Chain containing all defined info subcommands. */
+
+struct cmd_list_element *infolist;
+
+/* Chain containing all defined enable subcommands. */
+
+struct cmd_list_element *enablelist;
+
+/* Chain containing all defined disable subcommands. */
+
+struct cmd_list_element *disablelist;
+
+/* Chain containing all defined delete subcommands. */
+
+struct cmd_list_element *deletelist;
+
+/* Chain containing all defined "enable breakpoint" subcommands. */
+
+struct cmd_list_element *enablebreaklist;
+
+/* Chain containing all defined set subcommands */
+
+struct cmd_list_element *setlist;
+
+/* Chain containing all defined unset subcommands */
+
+struct cmd_list_element *unsetlist;
+
+/* Chain containing all defined show subcommands. */
+
+struct cmd_list_element *showlist;
+
+/* Chain containing all defined \"set history\". */
+
+struct cmd_list_element *sethistlist;
+
+/* Chain containing all defined \"show history\". */
+
+struct cmd_list_element *showhistlist;
+
+/* Chain containing all defined \"unset history\". */
+
+struct cmd_list_element *unsethistlist;
+
+/* Chain containing all defined maintenance subcommands. */
+
+#if MAINTENANCE_CMDS
+struct cmd_list_element *maintenancelist;
+#endif
+
+/* Chain containing all defined "maintenance info" subcommands. */
+
+#if MAINTENANCE_CMDS
+struct cmd_list_element *maintenanceinfolist;
+#endif
+
+/* Chain containing all defined "maintenance print" subcommands. */
+
+#if MAINTENANCE_CMDS
+struct cmd_list_element *maintenanceprintlist;
+#endif
+
+struct cmd_list_element *setprintlist;
+
+struct cmd_list_element *showprintlist;
+
+struct cmd_list_element *setchecklist;
+
+struct cmd_list_element *showchecklist;
+
+/* stdio stream that command input is being read from. Set to stdin normally.
+ Set by source_command to the file we are sourcing. Set to NULL if we are
+ executing a user-defined command. */
+
+FILE *instream;
+
+/* Current working directory. */
+
+char *current_directory;
+
+/* The directory name is actually stored here (usually). */
+static char dirbuf[1024];
+
+/* Function to call before reading a command, if nonzero.
+ The function receives two args: an input stream,
+ and a prompt string. */
+
+void (*window_hook) PARAMS ((FILE *, char *));
+
+extern int mapped_symbol_files;
+extern int readnow_symbol_files;
+
+int epoch_interface;
+int xgdb_verbose;
+
+/* gdb prints this when reading a command interactively */
+static char *prompt;
+
+/* Buffer used for reading command lines, and the size
+ allocated for it so far. */
+
+char *line;
+int linesize = 100;
+
+/* Baud rate specified for talking to serial target systems. Default
+ is left as -1, so targets can choose their own defaults. */
+
+int baud_rate = -1;
+
+/* Non-zero tells remote* modules to output debugging info. */
+
+int remote_debug = 0;
+
+/* Signal to catch ^Z typed while reading a command: SIGTSTP or SIGCONT. */
+
+#ifndef STOP_SIGNAL
+#ifdef SIGTSTP
+#define STOP_SIGNAL SIGTSTP
+static void stop_sig PARAMS ((int));
+#endif
+#endif
+
+/* Some System V have job control but not sigsetmask(). */
+#if !defined (HAVE_SIGSETMASK)
+#define HAVE_SIGSETMASK !defined (USG)
+#endif
+
+#if 0 == (HAVE_SIGSETMASK)
+#define sigsetmask(n)
+#endif
+
+/* Where to go for return_to_top_level (RETURN_ERROR). */
+static jmp_buf error_return;
+/* Where to go for return_to_top_level (RETURN_QUIT). */
+static jmp_buf quit_return;
+
+/* Temporary variable for SET_TOP_LEVEL. */
+static int top_level_val;
+
+/* Do a setjmp on error_return and quit_return. catch_errors is
+ generally a cleaner way to do this, but main() would look pretty
+ ugly if it had to use catch_errors each time. */
+
+#define SET_TOP_LEVEL() \
+ (((top_level_val = setjmp (error_return)) \
+ ? (PTR) 0 : (PTR) memcpy (quit_return, error_return, sizeof (jmp_buf))) \
+ , top_level_val)
+
+/* Return for reason REASON. This generally gets back to the command
+ loop, but can be caught via catch_errors. */
+
+NORETURN void
+return_to_top_level (reason)
+ enum return_reason reason;
+{
+ quit_flag = 0;
+ immediate_quit = 0;
+
+ /* Perhaps it would be cleaner to do this via the cleanup chain (not sure
+ I can think of a reason why that is vital, though). */
+ bpstat_clear_actions(stop_bpstat); /* Clear queued breakpoint commands */
+
+ disable_current_display ();
+ do_cleanups (ALL_CLEANUPS);
+ (NORETURN void) longjmp
+ (reason == RETURN_ERROR ? error_return : quit_return, 1);
+}
+
+/* Call FUNC with arg ARGS, catching any errors. If there is no
+ error, return the value returned by FUNC. If there is an error,
+ print ERRSTRING, print the specific error message, then return
+ zero.
+
+ Must not be called with immediate_quit in effect (bad things might
+ happen, say we got a signal in the middle of a memcpy to quit_return).
+ This is an OK restriction; with very few exceptions immediate_quit can
+ be replaced by judicious use of QUIT.
+
+ MASK specifies what to catch; it is normally set to
+ RETURN_MASK_ALL, if for no other reason than that the code which
+ calls catch_errors might not be set up to deal with a quit which
+ isn't caught. But if the code can deal with it, it generally
+ should be RETURN_MASK_ERROR, unless for some reason it is more
+ useful to abort only the portion of the operation inside the
+ catch_errors. Note that quit should return to the command line
+ fairly quickly, even if some further processing is being done. */
+
+int
+catch_errors (func, args, errstring, mask)
+ int (*func) PARAMS ((char *));
+ PTR args;
+ char *errstring;
+ return_mask mask;
+{
+ jmp_buf saved_error;
+ jmp_buf saved_quit;
+ jmp_buf tmp_jmp;
+ int val;
+ struct cleanup *saved_cleanup_chain;
+ char *saved_error_pre_print;
+
+ saved_cleanup_chain = save_cleanups ();
+ saved_error_pre_print = error_pre_print;
+
+ if (mask & RETURN_MASK_ERROR)
+ memcpy ((char *)saved_error, (char *)error_return, sizeof (jmp_buf));
+ if (mask & RETURN_MASK_QUIT)
+ memcpy (saved_quit, quit_return, sizeof (jmp_buf));
+ error_pre_print = errstring;
+
+ if (setjmp (tmp_jmp) == 0)
+ {
+ if (mask & RETURN_MASK_ERROR)
+ memcpy (error_return, tmp_jmp, sizeof (jmp_buf));
+ if (mask & RETURN_MASK_QUIT)
+ memcpy (quit_return, tmp_jmp, sizeof (jmp_buf));
+ val = (*func) (args);
+ }
+ else
+ val = 0;
+
+ restore_cleanups (saved_cleanup_chain);
+
+ error_pre_print = saved_error_pre_print;
+ if (mask & RETURN_MASK_ERROR)
+ memcpy (error_return, saved_error, sizeof (jmp_buf));
+ if (mask & RETURN_MASK_QUIT)
+ memcpy (quit_return, saved_quit, sizeof (jmp_buf));
+ return val;
+}
+
+/* Handler for SIGHUP. */
+
+static void
+disconnect (signo)
+int signo;
+{
+ catch_errors (quit_cover, NULL,
+ "Could not kill the program being debugged", RETURN_MASK_ALL);
+ signal (SIGHUP, SIG_DFL);
+ kill (getpid (), SIGHUP);
+}
+
+/* Just a little helper function for disconnect(). */
+
+static int
+quit_cover (s)
+char *s;
+{
+ caution = 0; /* Throw caution to the wind -- we're exiting.
+ This prevents asking the user dumb questions. */
+ quit_command((char *)0, 0);
+ return 0;
+}
+
+/* Clean up on error during a "source" command (or execution of a
+ user-defined command). */
+
+static void
+source_cleanup (stream)
+ FILE *stream;
+{
+ /* Restore the previous input stream. */
+ instream = stream;
+}
+
+/* Read commands from STREAM. */
+static void
+read_command_file (stream)
+ FILE *stream;
+{
+ struct cleanup *cleanups;
+
+ cleanups = make_cleanup (source_cleanup, instream);
+ instream = stream;
+ command_loop ();
+ do_cleanups (cleanups);
+}
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int count;
+ static int quiet = 0;
+ static int batch = 0;
+
+ /* Pointers to various arguments from command line. */
+ char *symarg = NULL;
+ char *execarg = NULL;
+ char *corearg = NULL;
+ char *cdarg = NULL;
+ char *ttyarg = NULL;
+
+ /* These are static so that we can take their address in an initializer. */
+ static int print_help;
+ static int print_version;
+
+ /* Pointers to all arguments of --command option. */
+ char **cmdarg;
+ /* Allocated size of cmdarg. */
+ int cmdsize;
+ /* Number of elements of cmdarg used. */
+ int ncmd;
+
+ /* Indices of all arguments of --directory option. */
+ char **dirarg;
+ /* Allocated size. */
+ int dirsize;
+ /* Number of elements used. */
+ int ndir;
+
+ struct stat homebuf, cwdbuf;
+ char *homedir, *homeinit;
+
+ register int i;
+
+ /* This needs to happen before the first use of malloc. */
+ init_malloc ((PTR) NULL);
+
+#if defined (ALIGN_STACK_ON_STARTUP)
+ i = (int) &count & 0x3;
+ if (i != 0)
+ alloca (4 - i);
+#endif
+
+ /* If error() is called from initialization code, just exit */
+ if (SET_TOP_LEVEL ()) {
+ exit(1);
+ }
+
+ cmdsize = 1;
+ cmdarg = (char **) xmalloc (cmdsize * sizeof (*cmdarg));
+ ncmd = 0;
+ dirsize = 1;
+ dirarg = (char **) xmalloc (dirsize * sizeof (*dirarg));
+ ndir = 0;
+
+ quit_flag = 0;
+ line = (char *) xmalloc (linesize);
+ line[0] = '\0'; /* Terminate saved (now empty) cmd line */
+ instream = stdin;
+
+ getcwd (dirbuf, sizeof (dirbuf));
+ current_directory = dirbuf;
+
+#ifdef SET_STACK_LIMIT_HUGE
+ {
+ struct rlimit rlim;
+
+ /* Set the stack limit huge so that alloca (particularly stringtab
+ * in dbxread.c) does not fail. */
+ getrlimit (RLIMIT_STACK, &rlim);
+ original_stack_limit = rlim.rlim_cur;
+ rlim.rlim_cur = rlim.rlim_max;
+ setrlimit (RLIMIT_STACK, &rlim);
+ }
+#endif /* SET_STACK_LIMIT_HUGE */
+
+ /* Parse arguments and options. */
+ {
+ int c;
+ /* When var field is 0, use flag field to record the equivalent
+ short option (or arbitrary numbers starting at 10 for those
+ with no equivalent). */
+ static struct option long_options[] =
+ {
+ {"readnow", no_argument, &readnow_symbol_files, 1},
+ {"r", no_argument, &readnow_symbol_files, 1},
+ {"mapped", no_argument, &mapped_symbol_files, 1},
+ {"m", no_argument, &mapped_symbol_files, 1},
+ {"quiet", no_argument, &quiet, 1},
+ {"q", no_argument, &quiet, 1},
+ {"silent", no_argument, &quiet, 1},
+ {"nx", no_argument, &inhibit_gdbinit, 1},
+ {"n", no_argument, &inhibit_gdbinit, 1},
+ {"batch", no_argument, &batch, 1},
+ {"epoch", no_argument, &epoch_interface, 1},
+ {"fullname", no_argument, &frame_file_full_name, 1},
+ {"f", no_argument, &frame_file_full_name, 1},
+ {"help", no_argument, &print_help, 1},
+ {"se", required_argument, 0, 10},
+ {"symbols", required_argument, 0, 's'},
+ {"s", required_argument, 0, 's'},
+ {"exec", required_argument, 0, 'e'},
+ {"e", required_argument, 0, 'e'},
+ {"core", required_argument, 0, 'c'},
+ {"c", required_argument, 0, 'c'},
+ {"command", required_argument, 0, 'x'},
+ {"version", no_argument, &print_version, 1},
+ {"x", required_argument, 0, 'x'},
+ {"directory", required_argument, 0, 'd'},
+ {"cd", required_argument, 0, 11},
+ {"tty", required_argument, 0, 't'},
+ {"baud", required_argument, 0, 'b'},
+ {"b", required_argument, 0, 'b'},
+/* Allow machine descriptions to add more options... */
+#ifdef ADDITIONAL_OPTIONS
+ ADDITIONAL_OPTIONS
+#endif
+ {0, no_argument, 0, 0},
+ };
+
+ while (1)
+ {
+ int option_index;
+
+ c = getopt_long_only (argc, argv, "",
+ long_options, &option_index);
+ if (c == EOF)
+ break;
+
+ /* Long option that takes an argument. */
+ if (c == 0 && long_options[option_index].flag == 0)
+ c = long_options[option_index].val;
+
+ switch (c)
+ {
+ case 0:
+ /* Long option that just sets a flag. */
+ break;
+ case 10:
+ symarg = optarg;
+ execarg = optarg;
+ break;
+ case 11:
+ cdarg = optarg;
+ break;
+ case 's':
+ symarg = optarg;
+ break;
+ case 'e':
+ execarg = optarg;
+ break;
+ case 'c':
+ corearg = optarg;
+ break;
+ case 'x':
+ cmdarg[ncmd++] = optarg;
+ if (ncmd >= cmdsize)
+ {
+ cmdsize *= 2;
+ cmdarg = (char **) xrealloc ((char *)cmdarg,
+ cmdsize * sizeof (*cmdarg));
+ }
+ break;
+ case 'd':
+ dirarg[ndir++] = optarg;
+ if (ndir >= dirsize)
+ {
+ dirsize *= 2;
+ dirarg = (char **) xrealloc ((char *)dirarg,
+ dirsize * sizeof (*dirarg));
+ }
+ break;
+ case 't':
+ ttyarg = optarg;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'b':
+ {
+ int i;
+ char *p;
+
+ i = strtol (optarg, &p, 0);
+ if (i == 0 && p == optarg)
+ warning ("Could not set baud rate to `%s'.\n", optarg);
+ else
+ baud_rate = i;
+ }
+ break;
+
+#ifdef ADDITIONAL_OPTION_CASES
+ ADDITIONAL_OPTION_CASES
+#endif
+ case '?':
+ fprintf (stderr,
+ "Use `%s --help' for a complete list of options.\n",
+ argv[0]);
+ exit (1);
+ }
+ }
+
+ /* OK, that's all the options. The other arguments are filenames. */
+ count = 0;
+ for (; optind < argc; optind++)
+ switch (++count)
+ {
+ case 1:
+ symarg = argv[optind];
+ execarg = argv[optind];
+ break;
+ case 2:
+ corearg = argv[optind];
+ break;
+ case 3:
+ fprintf (stderr,
+ "Excess command line arguments ignored. (%s%s)\n",
+ argv[optind], (optind == argc - 1) ? "" : " ...");
+ break;
+ }
+ if (batch)
+ quiet = 1;
+ }
+
+ /* Run the init function of each source file */
+
+ init_cmd_lists (); /* This needs to be done first */
+ initialize_all_files ();
+ init_main (); /* But that omits this file! Do it now */
+ init_signals ();
+
+ /* Do these (and anything which might call wrap_here or *_filtered)
+ after initialize_all_files. */
+ if (print_version)
+ {
+ print_gdb_version (stdout);
+ wrap_here ("");
+ printf_filtered ("\n");
+ exit (0);
+ }
+
+ if (print_help)
+ {
+ /* --version is intentionally not documented here, because we
+ are printing the version here, and the help is long enough
+ already. */
+
+ print_gdb_version (stdout);
+ /* Make sure the output gets printed. */
+ wrap_here ("");
+ printf_filtered ("\n");
+
+ /* But don't use *_filtered here. We don't want to prompt for continue
+ no matter how small the screen or how much we're going to print. */
+ fputs ("\
+This is the GNU debugger. Usage:\n\
+ gdb [options] [executable-file [core-file or process-id]]\n\
+Options:\n\
+ --help Print this message.\n\
+ --quiet Do not print version number on startup.\n\
+ --fullname Output information used by emacs-GDB interface.\n\
+ --epoch Output information used by epoch emacs-GDB interface.\n\
+ --batch Exit after processing options.\n\
+ --nx Do not read .gdbinit file.\n\
+ --tty=TTY Use TTY for input/output by the program being debugged.\n\
+ --cd=DIR Change current directory to DIR.\n\
+ --directory=DIR Search for source files in DIR.\n\
+ --command=FILE Execute GDB commands from FILE.\n\
+ --symbols=SYMFILE Read symbols from SYMFILE.\n\
+ --exec=EXECFILE Use EXECFILE as the executable.\n\
+ --se=FILE Use FILE as symbol file and executable file.\n\
+ --core=COREFILE Analyze the core dump COREFILE.\n\
+ -b BAUDRATE Set serial port baud rate used for remote debugging.\n\
+ --mapped Use mapped symbol files if supported on this system.\n\
+ --readnow Fully read symbol files on first access.\n\
+", stdout);
+#ifdef ADDITIONAL_OPTION_HELP
+ fputs (ADDITIONAL_OPTION_HELP, stdout);
+#endif
+ fputs ("\n\
+For more information, type \"help\" from within GDB, or consult the\n\
+GDB manual (available as on-line info or a printed manual).\n", stdout);
+ exit (0);
+ }
+
+ if (!quiet)
+ {
+ /* Print all the junk at the top, with trailing "..." if we are about
+ to read a symbol file (possibly slowly). */
+ print_gnu_advertisement ();
+ print_gdb_version (stdout);
+ if (symarg)
+ printf_filtered ("..");
+ wrap_here("");
+ fflush (stdout); /* Force to screen during slow operations */
+ }
+
+ error_pre_print = "\n\n";
+ /* We may get more than one warning, don't double space all of them... */
+ warning_pre_print = "\nwarning: ";
+
+ /* We need a default language for parsing expressions, so simple things like
+ "set width 0" won't fail if no language is explicitly set in a config file
+ or implicitly set by reading an executable during startup. */
+ set_language (language_c);
+ expected_language = current_language; /* don't warn about the change. */
+
+ /* Read and execute $HOME/.gdbinit file, if it exists. This is done
+ *before* all the command line arguments are processed; it sets
+ global parameters, which are independent of what file you are
+ debugging or what directory you are in. */
+ homedir = getenv ("HOME");
+ if (homedir)
+ {
+ homeinit = (char *) alloca (strlen (getenv ("HOME")) +
+ strlen (gdbinit) + 10);
+ strcpy (homeinit, getenv ("HOME"));
+ strcat (homeinit, "/");
+ strcat (homeinit, gdbinit);
+ if (!inhibit_gdbinit && access (homeinit, R_OK) == 0)
+ {
+ if (!SET_TOP_LEVEL ())
+ source_command (homeinit, 0);
+ }
+ do_cleanups (ALL_CLEANUPS);
+
+ /* Do stats; no need to do them elsewhere since we'll only
+ need them if homedir is set. Make sure that they are
+ zero in case one of them fails (this guarantees that they
+ won't match if either exists). */
+
+ memset (&homebuf, 0, sizeof (struct stat));
+ memset (&cwdbuf, 0, sizeof (struct stat));
+
+ stat (homeinit, &homebuf);
+ stat (gdbinit, &cwdbuf); /* We'll only need this if
+ homedir was set. */
+ }
+
+ /* Now perform all the actions indicated by the arguments. */
+ if (cdarg != NULL)
+ {
+ if (!SET_TOP_LEVEL ())
+ {
+ cd_command (cdarg, 0);
+ init_source_path ();
+ }
+ }
+ do_cleanups (ALL_CLEANUPS);
+
+ for (i = 0; i < ndir; i++)
+ if (!SET_TOP_LEVEL ())
+ directory_command (dirarg[i], 0);
+ free ((PTR)dirarg);
+ do_cleanups (ALL_CLEANUPS);
+
+ if (execarg != NULL
+ && symarg != NULL
+ && STREQ (execarg, symarg))
+ {
+ /* The exec file and the symbol-file are the same. If we can't open
+ it, better only print one error message. */
+ if (!SET_TOP_LEVEL ())
+ {
+ exec_file_command (execarg, !batch);
+ symbol_file_command (symarg, 0);
+ }
+ }
+ else
+ {
+ if (execarg != NULL)
+ if (!SET_TOP_LEVEL ())
+ exec_file_command (execarg, !batch);
+ if (symarg != NULL)
+ if (!SET_TOP_LEVEL ())
+ symbol_file_command (symarg, 0);
+ }
+ do_cleanups (ALL_CLEANUPS);
+
+ /* After the symbol file has been read, print a newline to get us
+ beyond the copyright line... But errors should still set off
+ the error message with a (single) blank line. */
+ if (!quiet)
+ printf_filtered ("\n");
+ error_pre_print = "\n";
+ warning_pre_print = "\nwarning: ";
+
+ if (corearg != NULL)
+ if (!SET_TOP_LEVEL ())
+ core_file_command (corearg, !batch);
+ else if (isdigit (corearg[0]) && !SET_TOP_LEVEL ())
+ attach_command (corearg, !batch);
+ do_cleanups (ALL_CLEANUPS);
+
+ if (ttyarg != NULL)
+ if (!SET_TOP_LEVEL ())
+ tty_command (ttyarg, !batch);
+ do_cleanups (ALL_CLEANUPS);
+
+#ifdef ADDITIONAL_OPTION_HANDLER
+ ADDITIONAL_OPTION_HANDLER;
+#endif
+
+ /* Error messages should no longer be distinguished with extra output. */
+ error_pre_print = 0;
+ warning_pre_print = "warning: ";
+
+ /* Read the .gdbinit file in the current directory, *if* it isn't
+ the same as the $HOME/.gdbinit file (it should exist, also). */
+
+ if (!homedir
+ || memcmp ((char *) &homebuf, (char *) &cwdbuf, sizeof (struct stat)))
+ if (!inhibit_gdbinit && access (gdbinit, R_OK) == 0)
+ {
+ if (!SET_TOP_LEVEL ())
+ source_command (gdbinit, 0);
+ }
+ do_cleanups (ALL_CLEANUPS);
+
+ for (i = 0; i < ncmd; i++)
+ {
+ if (!SET_TOP_LEVEL ())
+ {
+ if (cmdarg[i][0] == '-' && cmdarg[i][1] == '\0')
+ read_command_file (stdin);
+ else
+ source_command (cmdarg[i], !batch);
+ do_cleanups (ALL_CLEANUPS);
+ }
+ }
+ free ((PTR)cmdarg);
+
+ /* Read in the old history after all the command files have been read. */
+ init_history();
+
+ if (batch)
+ {
+ /* We have hit the end of the batch file. */
+ exit (0);
+ }
+
+ /* Do any host- or target-specific hacks. This is used for i960 targets
+ to force the user to set a nindy target and spec its parameters. */
+
+#ifdef BEFORE_MAIN_LOOP_HOOK
+ BEFORE_MAIN_LOOP_HOOK;
+#endif
+
+ /* The command loop. */
+
+ while (1)
+ {
+ if (!SET_TOP_LEVEL ())
+ {
+ do_cleanups (ALL_CLEANUPS); /* Do complete cleanup */
+ command_loop ();
+ quit_command ((char *)0, instream == stdin);
+ }
+ }
+ /* No exit -- exit is through quit_command. */
+}
+
+void
+execute_user_command (c, args)
+ struct cmd_list_element *c;
+ char *args;
+{
+ register struct command_line *cmdlines;
+ struct cleanup *old_chain;
+
+ if (args)
+ error ("User-defined commands cannot take arguments.");
+
+ cmdlines = c->user_commands;
+ if (cmdlines == 0)
+ /* Null command */
+ return;
+
+ /* Set the instream to 0, indicating execution of a
+ user-defined function. */
+ old_chain = make_cleanup (source_cleanup, instream);
+ instream = (FILE *) 0;
+ while (cmdlines)
+ {
+ execute_command (cmdlines->line, 0);
+ cmdlines = cmdlines->next;
+ }
+ do_cleanups (old_chain);
+}
+
+/* Execute the line P as a command.
+ Pass FROM_TTY as second argument to the defining function. */
+
+void
+execute_command (p, from_tty)
+ char *p;
+ int from_tty;
+{
+ register struct cmd_list_element *c;
+ register enum language flang;
+ static int warned = 0;
+
+ free_all_values ();
+
+ /* This can happen when command_line_input hits end of file. */
+ if (p == NULL)
+ return;
+
+ while (*p == ' ' || *p == '\t') p++;
+ if (*p)
+ {
+ char *arg;
+
+ c = lookup_cmd (&p, cmdlist, "", 0, 1);
+ /* Pass null arg rather than an empty one. */
+ arg = *p ? p : 0;
+
+ /* If this command has been hooked, run the hook first. */
+ if (c->hook)
+ execute_user_command (c->hook, (char *)0);
+
+ if (c->class == class_user)
+ execute_user_command (c, arg);
+ else if (c->type == set_cmd || c->type == show_cmd)
+ do_setshow_command (arg, from_tty & caution, c);
+ else if (c->function.cfunc == NO_FUNCTION)
+ error ("That is not a command, just a help topic.");
+ else
+ (*c->function.cfunc) (arg, from_tty & caution);
+ }
+
+ /* Tell the user if the language has changed (except first time). */
+ if (current_language != expected_language)
+ {
+ if (language_mode == language_mode_auto) {
+ language_info (1); /* Print what changed. */
+ }
+ warned = 0;
+ }
+
+ /* Warn the user if the working language does not match the
+ language of the current frame. Only warn the user if we are
+ actually running the program, i.e. there is a stack. */
+ /* FIXME: This should be cacheing the frame and only running when
+ the frame changes. */
+ if (target_has_stack)
+ {
+ flang = get_frame_language ();
+ if (!warned
+ && flang != language_unknown
+ && flang != current_language->la_language)
+ {
+ printf_filtered ("%s\n", lang_frame_mismatch_warn);
+ warned = 1;
+ }
+ }
+}
+
+/* ARGSUSED */
+static void
+command_loop_marker (foo)
+ int foo;
+{
+}
+
+/* Read commands from `instream' and execute them
+ until end of file or error reading instream. */
+static void
+command_loop ()
+{
+ struct cleanup *old_chain;
+ char *command;
+ int stdin_is_tty = ISATTY (stdin);
+
+ while (!feof (instream))
+ {
+ if (window_hook && instream == stdin)
+ (*window_hook) (instream, prompt);
+
+ quit_flag = 0;
+ if (instream == stdin && stdin_is_tty)
+ reinitialize_more_filter ();
+ old_chain = make_cleanup (command_loop_marker, 0);
+ command = command_line_input (instream == stdin ? prompt : (char *) NULL,
+ instream == stdin);
+ if (command == 0)
+ return;
+ execute_command (command, instream == stdin);
+ /* Do any commands attached to breakpoint we stopped at. */
+ bpstat_do_actions (&stop_bpstat);
+ do_cleanups (old_chain);
+ }
+}
+
+/* Commands call this if they do not want to be repeated by null lines. */
+
+void
+dont_repeat ()
+{
+ /* If we aren't reading from standard input, we are saving the last
+ thing read from stdin in line and don't want to delete it. Null lines
+ won't repeat here in any case. */
+ if (instream == stdin)
+ *line = 0;
+}
+
+/* Read a line from the stream "instream" without command line editing.
+
+ It prints PRROMPT once at the start.
+ Action is compatible with "readline", e.g. space for the result is
+ malloc'd and should be freed by the caller.
+
+ A NULL return means end of file. */
+char *
+gdb_readline (prrompt)
+ char *prrompt;
+{
+ int c;
+ char *result;
+ int input_index = 0;
+ int result_size = 80;
+
+ if (prrompt)
+ {
+ /* Don't use a _filtered function here. It causes the assumed
+ character position to be off, since the newline we read from
+ the user is not accounted for. */
+ fputs (prrompt, stdout);
+ fflush (stdout);
+ }
+
+ result = (char *) xmalloc (result_size);
+
+ while (1)
+ {
+ /* Read from stdin if we are executing a user defined command.
+ This is the right thing for prompt_for_continue, at least. */
+ c = fgetc (instream ? instream : stdin);
+
+ if (c == EOF)
+ {
+ if (input_index > 0)
+ /* The last line does not end with a newline. Return it, and
+ if we are called again fgetc will still return EOF and
+ we'll return NULL then. */
+ break;
+ free (result);
+ return NULL;
+ }
+
+ if (c == '\n')
+ break;
+
+ result[input_index++] = c;
+ while (input_index >= result_size)
+ {
+ result_size *= 2;
+ result = (char *) xrealloc (result, result_size);
+ }
+ }
+
+ result[input_index++] = '\0';
+ return result;
+}
+
+/* Variables which control command line editing and history
+ substitution. These variables are given default values at the end
+ of this file. */
+static int command_editing_p;
+static int history_expansion_p;
+static int write_history_p;
+static int history_size;
+static char *history_filename;
+
+/* readline uses the word breaks for two things:
+ (1) In figuring out where to point the TEXT parameter to the
+ rl_completion_entry_function. Since we don't use TEXT for much,
+ it doesn't matter a lot what the word breaks are for this purpose, but
+ it does affect how much stuff M-? lists.
+ (2) If one of the matches contains a word break character, readline
+ will quote it. That's why we switch between
+ gdb_completer_word_break_characters and
+ gdb_completer_command_word_break_characters. I'm not sure when
+ we need this behavior (perhaps for funky characters in C++ symbols?). */
+
+/* Variables which are necessary for fancy command line editing. */
+char *gdb_completer_word_break_characters =
+ " \t\n!@#$%^&*()+=|~`}{[]\"';:?/>.<,-";
+
+/* When completing on command names, we remove '-' from the list of
+ word break characters, since we use it in command names. If the
+ readline library sees one in any of the current completion strings,
+ it thinks that the string needs to be quoted and automatically supplies
+ a leading quote. */
+char *gdb_completer_command_word_break_characters =
+ " \t\n!@#$%^&*()+=|~`}{[]\"';:?/>.<,";
+
+/* Characters that can be used to quote completion strings. Note that we
+ can't include '"' because the gdb C parser treats such quoted sequences
+ as strings. */
+char *gdb_completer_quote_characters =
+ "'";
+
+/* Functions that are used as part of the fancy command line editing. */
+
+/* This can be used for functions which don't want to complete on symbols
+ but don't want to complete on anything else either. */
+/* ARGSUSED */
+char **
+noop_completer (text, prefix)
+ char *text;
+ char *prefix;
+{
+ return NULL;
+}
+
+/* Complete on filenames. */
+char **
+filename_completer (text, word)
+ char *text;
+ char *word;
+{
+ /* From readline. */
+ extern char *filename_completion_function ();
+ int subsequent_name;
+ char **return_val;
+ int return_val_used;
+ int return_val_alloced;
+
+ return_val_used = 0;
+ /* Small for testing. */
+ return_val_alloced = 1;
+ return_val = (char **) xmalloc (return_val_alloced * sizeof (char *));
+
+ subsequent_name = 0;
+ while (1)
+ {
+ char *p;
+ p = filename_completion_function (text, subsequent_name);
+ if (return_val_used >= return_val_alloced)
+ {
+ return_val_alloced *= 2;
+ return_val =
+ (char **) xrealloc (return_val,
+ return_val_alloced * sizeof (char *));
+ }
+ if (p == NULL)
+ {
+ return_val[return_val_used++] = p;
+ break;
+ }
+ /* Like emacs, don't complete on old versions. Especially useful
+ in the "source" command. */
+ if (p[strlen (p) - 1] == '~')
+ continue;
+
+ {
+ char *q;
+ if (word == text)
+ /* Return exactly p. */
+ return_val[return_val_used++] = p;
+ else if (word > text)
+ {
+ /* Return some portion of p. */
+ q = xmalloc (strlen (p) + 5);
+ strcpy (q, p + (word - text));
+ return_val[return_val_used++] = q;
+ free (p);
+ }
+ else
+ {
+ /* Return some of TEXT plus p. */
+ q = xmalloc (strlen (p) + (text - word) + 5);
+ strncpy (q, word, text - word);
+ q[text - word] = '\0';
+ strcat (q, p);
+ return_val[return_val_used++] = q;
+ free (p);
+ }
+ }
+ subsequent_name = 1;
+ }
+#if 0
+ /* There is no way to do this just long enough to affect quote inserting
+ without also affecting the next completion. This should be fixed in
+ readline. FIXME. */
+ /* Insure that readline does the right thing
+ with respect to inserting quotes. */
+ rl_completer_word_break_characters = "";
+#endif
+ return return_val;
+}
+
+/* Here are some useful test cases for completion. FIXME: These should
+ be put in the test suite. They should be tested with both M-? and TAB.
+
+ "show output-" "radix"
+ "show output" "-radix"
+ "p" ambiguous (commands starting with p--path, print, printf, etc.)
+ "p " ambiguous (all symbols)
+ "info t foo" no completions
+ "info t " no completions
+ "info t" ambiguous ("info target", "info terminal", etc.)
+ "info ajksdlfk" no completions
+ "info ajksdlfk " no completions
+ "info" " "
+ "info " ambiguous (all info commands)
+ "p \"a" no completions (string constant)
+ "p 'a" ambiguous (all symbols starting with a)
+ "p b-a" ambiguous (all symbols starting with a)
+ "p b-" ambiguous (all symbols)
+ "file Make" "file" (word break hard to screw up here)
+ "file ../gdb.stabs/wi" "erd" (needs to not break word at slash)
+ */
+
+/* Generate completions one by one for the completer. Each time we are
+ called return another potential completion to the caller. The function
+ is misnamed; it just completes on commands or passes the buck to the
+ command's completer function; the stuff specific to symbol completion
+ is in make_symbol_completion_list.
+
+ TEXT is readline's idea of the "word" we are looking at; we don't really
+ like readline's ideas about word breaking so we ignore it.
+
+ MATCHES is the number of matches that have currently been collected from
+ calling this completion function. When zero, then we need to initialize,
+ otherwise the initialization has already taken place and we can just
+ return the next potential completion string.
+
+ Returns NULL if there are no more completions, else a pointer to a string
+ which is a possible completion.
+
+ RL_LINE_BUFFER is available to be looked at; it contains the entire text
+ of the line. RL_POINT is the offset in that line of the cursor. You
+ should pretend that the line ends at RL_POINT. */
+
+static char *
+symbol_completion_function (text, matches)
+ char *text;
+ int matches;
+{
+ static char **list = (char **)NULL; /* Cache of completions */
+ static int index; /* Next cached completion */
+ char *output = NULL;
+ char *tmp_command, *p;
+ /* Pointer within tmp_command which corresponds to text. */
+ char *word;
+ struct cmd_list_element *c, *result_list;
+ extern char *rl_line_buffer;
+ extern int rl_point;
+
+ if (matches == 0)
+ {
+ /* The caller is beginning to accumulate a new set of completions, so
+ we need to find all of them now, and cache them for returning one at
+ a time on future calls. */
+
+ if (list)
+ {
+ /* Free the storage used by LIST, but not by the strings inside.
+ This is because rl_complete_internal () frees the strings. */
+ free ((PTR)list);
+ }
+ list = 0;
+ index = 0;
+
+ /* Choose the default set of word break characters to break completions.
+ If we later find out that we are doing completions on command strings
+ (as opposed to strings supplied by the individual command completer
+ functions, which can be any string) then we will switch to the
+ special word break set for command strings, which leaves out the
+ '-' character used in some commands. */
+
+ rl_completer_word_break_characters =
+ gdb_completer_word_break_characters;
+
+ /* Decide whether to complete on a list of gdb commands or on symbols. */
+ tmp_command = (char *) alloca (rl_point + 1);
+ p = tmp_command;
+
+ strncpy (tmp_command, rl_line_buffer, rl_point);
+ tmp_command[rl_point] = '\0';
+ /* Since text always contains some number of characters leading up
+ to rl_point, we can find the equivalent position in tmp_command
+ by subtracting that many characters from the end of tmp_command. */
+ word = tmp_command + rl_point - strlen (text);
+
+ if (rl_point == 0)
+ {
+ /* An empty line we want to consider ambiguous; that is, it
+ could be any command. */
+ c = (struct cmd_list_element *) -1;
+ result_list = 0;
+ }
+ else
+ {
+ c = lookup_cmd_1 (&p, cmdlist, &result_list, 1);
+ }
+
+ /* Move p up to the next interesting thing. */
+ while (*p == ' ' || *p == '\t')
+ {
+ p++;
+ }
+
+ if (!c)
+ {
+ /* It is an unrecognized command. So there are no
+ possible completions. */
+ list = NULL;
+ }
+ else if (c == (struct cmd_list_element *) -1)
+ {
+ char *q;
+
+ /* lookup_cmd_1 advances p up to the first ambiguous thing, but
+ doesn't advance over that thing itself. Do so now. */
+ q = p;
+ while (*q && (isalnum (*q) || *q == '-' || *q == '_'))
+ ++q;
+ if (q != tmp_command + rl_point)
+ {
+ /* There is something beyond the ambiguous
+ command, so there are no possible completions. For
+ example, "info t " or "info t foo" does not complete
+ to anything, because "info t" can be "info target" or
+ "info terminal". */
+ list = NULL;
+ }
+ else
+ {
+ /* We're trying to complete on the command which was ambiguous.
+ This we can deal with. */
+ if (result_list)
+ {
+ list = complete_on_cmdlist (*result_list->prefixlist, p,
+ word);
+ }
+ else
+ {
+ list = complete_on_cmdlist (cmdlist, p, word);
+ }
+ /* Insure that readline does the right thing with respect to
+ inserting quotes. */
+ rl_completer_word_break_characters =
+ gdb_completer_command_word_break_characters;
+ }
+ }
+ else
+ {
+ /* We've recognized a full command. */
+
+ if (p == tmp_command + rl_point)
+ {
+ /* There is no non-whitespace in the line beyond the command. */
+
+ if (p[-1] == ' ' || p[-1] == '\t')
+ {
+ /* The command is followed by whitespace; we need to complete
+ on whatever comes after command. */
+ if (c->prefixlist)
+ {
+ /* It is a prefix command; what comes after it is
+ a subcommand (e.g. "info "). */
+ list = complete_on_cmdlist (*c->prefixlist, p, word);
+
+ /* Insure that readline does the right thing
+ with respect to inserting quotes. */
+ rl_completer_word_break_characters =
+ gdb_completer_command_word_break_characters;
+ }
+ else
+ {
+ /* It is a normal command; what comes after it is
+ completed by the command's completer function. */
+ list = (*c->completer) (p, word);
+ }
+ }
+ else
+ {
+ /* The command is not followed by whitespace; we need to
+ complete on the command itself. e.g. "p" which is a
+ command itself but also can complete to "print", "ptype"
+ etc. */
+ char *q;
+
+ /* Find the command we are completing on. */
+ q = p;
+ while (q > tmp_command)
+ {
+ if (isalnum (q[-1]) || q[-1] == '-' || q[-1] == '_')
+ --q;
+ else
+ break;
+ }
+
+ list = complete_on_cmdlist (result_list, q, word);
+
+ /* Insure that readline does the right thing
+ with respect to inserting quotes. */
+ rl_completer_word_break_characters =
+ gdb_completer_command_word_break_characters;
+ }
+ }
+ else
+ {
+ /* There is non-whitespace beyond the command. */
+
+ if (c->prefixlist && !c->allow_unknown)
+ {
+ /* It is an unrecognized subcommand of a prefix command,
+ e.g. "info adsfkdj". */
+ list = NULL;
+ }
+ else
+ {
+ /* It is a normal command. */
+ list = (*c->completer) (p, word);
+ }
+ }
+ }
+ }
+
+ /* If we found a list of potential completions during initialization then
+ dole them out one at a time. The vector of completions is NULL
+ terminated, so after returning the last one, return NULL (and continue
+ to do so) each time we are called after that, until a new list is
+ available. */
+
+ if (list)
+ {
+ output = list[index];
+ if (output)
+ {
+ index++;
+ }
+ }
+
+#if 0
+ /* Can't do this because readline hasn't yet checked the word breaks
+ for figuring out whether to insert a quote. */
+ if (output == NULL)
+ /* Make sure the word break characters are set back to normal for the
+ next time that readline tries to complete something. */
+ rl_completer_word_break_characters =
+ gdb_completer_word_break_characters;
+#endif
+
+ return (output);
+}
+
+/* Skip over a possibly quoted word (as defined by the quote characters
+ and word break characters the completer uses). Returns pointer to the
+ location after the "word". */
+
+char *
+skip_quoted (str)
+ char *str;
+{
+ char quote_char = '\0';
+ char *scan;
+
+ for (scan = str; *scan != '\0'; scan++)
+ {
+ if (quote_char != '\0')
+ {
+ /* Ignore everything until the matching close quote char */
+ if (*scan == quote_char)
+ {
+ /* Found matching close quote. */
+ scan++;
+ break;
+ }
+ }
+ else if (strchr (gdb_completer_quote_characters, *scan))
+ {
+ /* Found start of a quoted string. */
+ quote_char = *scan;
+ }
+ else if (strchr (gdb_completer_word_break_characters, *scan))
+ {
+ break;
+ }
+ }
+ return (scan);
+}
+
+
+#ifdef STOP_SIGNAL
+static void
+stop_sig (signo)
+int signo;
+{
+#if STOP_SIGNAL == SIGTSTP
+ signal (SIGTSTP, SIG_DFL);
+ sigsetmask (0);
+ kill (getpid (), SIGTSTP);
+ signal (SIGTSTP, stop_sig);
+#else
+ signal (STOP_SIGNAL, stop_sig);
+#endif
+ printf ("%s", prompt);
+ fflush (stdout);
+
+ /* Forget about any previous command -- null line now will do nothing. */
+ dont_repeat ();
+}
+#endif /* STOP_SIGNAL */
+
+/* Initialize signal handlers. */
+static void
+do_nothing (signo)
+int signo;
+{
+}
+
+static void
+init_signals ()
+{
+ signal (SIGINT, request_quit);
+
+ /* If we initialize SIGQUIT to SIG_IGN, then the SIG_IGN will get
+ passed to the inferior, which we don't want. It would be
+ possible to do a "signal (SIGQUIT, SIG_DFL)" after we fork, but
+ on BSD4.3 systems using vfork, that can affect the
+ GDB process as well as the inferior (the signal handling tables
+ might be in memory, shared between the two). Since we establish
+ a handler for SIGQUIT, when we call exec it will set the signal
+ to SIG_DFL for us. */
+ signal (SIGQUIT, do_nothing);
+ if (signal (SIGHUP, do_nothing) != SIG_IGN)
+ signal (SIGHUP, disconnect);
+ signal (SIGFPE, float_handler);
+
+#if defined(SIGWINCH) && defined(SIGWINCH_HANDLER)
+ signal (SIGWINCH, SIGWINCH_HANDLER);
+#endif
+}
+
+/* Read one line from the command input stream `instream'
+ into the local static buffer `linebuffer' (whose current length
+ is `linelength').
+ The buffer is made bigger as necessary.
+ Returns the address of the start of the line.
+
+ NULL is returned for end of file.
+
+ *If* the instream == stdin & stdin is a terminal, the line read
+ is copied into the file line saver (global var char *line,
+ length linesize) so that it can be duplicated.
+
+ This routine either uses fancy command line editing or
+ simple input as the user has requested. */
+
+char *
+command_line_input (prrompt, repeat)
+ char *prrompt;
+ int repeat;
+{
+ static char *linebuffer = 0;
+ static unsigned linelength = 0;
+ register char *p;
+ char *p1;
+ char *rl;
+ char *local_prompt = prrompt;
+ register int c;
+ char *nline;
+ char got_eof = 0;
+
+ if (linebuffer == 0)
+ {
+ linelength = 80;
+ linebuffer = (char *) xmalloc (linelength);
+ }
+
+ p = linebuffer;
+
+ /* Control-C quits instantly if typed while in this loop
+ since it should not wait until the user types a newline. */
+ immediate_quit++;
+#ifdef STOP_SIGNAL
+ if (job_control)
+ signal (STOP_SIGNAL, stop_sig);
+#endif
+
+ while (1)
+ {
+ /* Reports are that some Sys V's don't flush stdout/err on reads
+ from stdin, when stdin/out are sockets rather than ttys. So we
+ have to do it ourselves, to make emacs-gdb and xxgdb work.
+ On other machines, doing this once per input should be a cheap nop. */
+ fflush (stdout);
+ fflush (stderr);
+
+ /* Don't use fancy stuff if not talking to stdin. */
+ if (command_editing_p && instream == stdin
+ && ISATTY (instream))
+ rl = readline (local_prompt);
+ else
+ rl = gdb_readline (local_prompt);
+
+ if (!rl || rl == (char *) EOF)
+ {
+ got_eof = 1;
+ break;
+ }
+ if (strlen(rl) + 1 + (p - linebuffer) > linelength)
+ {
+ linelength = strlen(rl) + 1 + (p - linebuffer);
+ nline = (char *) xrealloc (linebuffer, linelength);
+ p += nline - linebuffer;
+ linebuffer = nline;
+ }
+ p1 = rl;
+ /* Copy line. Don't copy null at end. (Leaves line alone
+ if this was just a newline) */
+ while (*p1)
+ *p++ = *p1++;
+
+ free (rl); /* Allocated in readline. */
+
+ if (p == linebuffer || *(p - 1) != '\\')
+ break;
+
+ p--; /* Put on top of '\'. */
+ local_prompt = (char *) 0;
+ }
+
+#ifdef STOP_SIGNAL
+ if (job_control)
+ signal (STOP_SIGNAL, SIG_DFL);
+#endif
+ immediate_quit--;
+
+ if (got_eof)
+ return NULL;
+
+ /* Do history expansion if that is wished. */
+ if (history_expansion_p && instream == stdin
+ && ISATTY (instream))
+ {
+ char *history_value;
+ int expanded;
+
+ *p = '\0'; /* Insert null now. */
+ expanded = history_expand (linebuffer, &history_value);
+ if (expanded)
+ {
+ /* Print the changes. */
+ printf ("%s\n", history_value);
+
+ /* If there was an error, call this function again. */
+ if (expanded < 0)
+ {
+ free (history_value);
+ return command_line_input (prrompt, repeat);
+ }
+ if (strlen (history_value) > linelength)
+ {
+ linelength = strlen (history_value) + 1;
+ linebuffer = (char *) xrealloc (linebuffer, linelength);
+ }
+ strcpy (linebuffer, history_value);
+ p = linebuffer + strlen(linebuffer);
+ free (history_value);
+ }
+ }
+
+ /* If we just got an empty line, and that is supposed
+ to repeat the previous command, return the value in the
+ global buffer. */
+ if (repeat)
+ {
+ if (p == linebuffer)
+ return line;
+ p1 = linebuffer;
+ while (*p1 == ' ' || *p1 == '\t')
+ p1++;
+ if (!*p1)
+ return line;
+ }
+
+ *p = 0;
+
+ /* Add line to history if appropriate. */
+ if (instream == stdin
+ && ISATTY (stdin) && *linebuffer)
+ add_history (linebuffer);
+
+ /* Note: lines consisting soley of comments are added to the command
+ history. This is useful when you type a command, and then
+ realize you don't want to execute it quite yet. You can comment
+ out the command and then later fetch it from the value history
+ and remove the '#'. The kill ring is probably better, but some
+ people are in the habit of commenting things out. */
+ p1 = linebuffer;
+ while ((c = *p1++) != '\0')
+ {
+ if (c == '"')
+ while ((c = *p1++) != '"')
+ {
+ /* Make sure an escaped '"' doesn't make us think the string
+ is ended. */
+ if (c == '\\')
+ parse_escape (&p1);
+ if (c == '\0')
+ break;
+ }
+ else if (c == '\'')
+ while ((c = *p1++) != '\'')
+ {
+ /* Make sure an escaped '\'' doesn't make us think the string
+ is ended. */
+ if (c == '\\')
+ parse_escape (&p1);
+ if (c == '\0')
+ break;
+ }
+ else if (c == '#')
+ {
+ /* Found a comment. */
+ p1[-1] = '\0';
+ break;
+ }
+ }
+
+ /* Save into global buffer if appropriate. */
+ if (repeat)
+ {
+ if (linelength > linesize)
+ {
+ line = xrealloc (line, linelength);
+ linesize = linelength;
+ }
+ strcpy (line, linebuffer);
+ return line;
+ }
+
+ return linebuffer;
+}
+
+/* Read lines from the input stream
+ and accumulate them in a chain of struct command_line's
+ which is then returned. */
+
+struct command_line *
+read_command_lines ()
+{
+ struct command_line *first = 0;
+ register struct command_line *next, *tail = 0;
+ register char *p, *p1;
+ struct cleanup *old_chain = 0;
+
+ while (1)
+ {
+ dont_repeat ();
+ p = command_line_input ((char *) NULL, instream == stdin);
+ if (p == NULL)
+ /* Treat end of file like "end". */
+ break;
+
+ /* Remove leading and trailing blanks. */
+ while (*p == ' ' || *p == '\t') p++;
+ p1 = p + strlen (p);
+ while (p1 != p && (p1[-1] == ' ' || p1[-1] == '\t')) p1--;
+
+ /* Is this "end"? */
+ if (p1 - p == 3 && !strncmp (p, "end", 3))
+ break;
+
+ /* No => add this line to the chain of command lines. */
+ next = (struct command_line *) xmalloc (sizeof (struct command_line));
+ next->line = savestring (p, p1 - p);
+ next->next = 0;
+ if (tail)
+ {
+ tail->next = next;
+ }
+ else
+ {
+ /* We just read the first line.
+ From now on, arrange to throw away the lines we have
+ if we quit or get an error while inside this function. */
+ first = next;
+ old_chain = make_cleanup (free_command_lines, &first);
+ }
+ tail = next;
+ }
+
+ dont_repeat ();
+
+ /* Now we are about to return the chain to our caller,
+ so freeing it becomes his responsibility. */
+ if (first)
+ discard_cleanups (old_chain);
+ return first;
+}
+
+/* Free a chain of struct command_line's. */
+
+void
+free_command_lines (lptr)
+ struct command_line **lptr;
+{
+ register struct command_line *l = *lptr;
+ register struct command_line *next;
+
+ while (l)
+ {
+ next = l->next;
+ free (l->line);
+ free ((PTR)l);
+ l = next;
+ }
+}
+
+/* Add an element to the list of info subcommands. */
+
+void
+add_info (name, fun, doc)
+ char *name;
+ void (*fun) PARAMS ((char *, int));
+ char *doc;
+{
+ add_cmd (name, no_class, fun, doc, &infolist);
+}
+
+/* Add an alias to the list of info subcommands. */
+
+void
+add_info_alias (name, oldname, abbrev_flag)
+ char *name;
+ char *oldname;
+ int abbrev_flag;
+{
+ add_alias_cmd (name, oldname, 0, abbrev_flag, &infolist);
+}
+
+/* The "info" command is defined as a prefix, with allow_unknown = 0.
+ Therefore, its own definition is called only for "info" with no args. */
+
+/* ARGSUSED */
+static void
+info_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ printf ("\"info\" must be followed by the name of an info command.\n");
+ help_list (infolist, "info ", -1, stdout);
+}
+
+/* The "show" command with no arguments shows all the settings. */
+
+/* ARGSUSED */
+static void
+show_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ cmd_show_list (showlist, from_tty, "");
+}
+
+/* Add an element to the list of commands. */
+
+void
+add_com (name, class, fun, doc)
+ char *name;
+ enum command_class class;
+ void (*fun) PARAMS ((char *, int));
+ char *doc;
+{
+ add_cmd (name, class, fun, doc, &cmdlist);
+}
+
+/* Add an alias or abbreviation command to the list of commands. */
+
+void
+add_com_alias (name, oldname, class, abbrev_flag)
+ char *name;
+ char *oldname;
+ enum command_class class;
+ int abbrev_flag;
+{
+ add_alias_cmd (name, oldname, class, abbrev_flag, &cmdlist);
+}
+
+void
+error_no_arg (why)
+ char *why;
+{
+ error ("Argument required (%s).", why);
+}
+
+/* ARGSUSED */
+static void
+help_command (command, from_tty)
+ char *command;
+ int from_tty; /* Ignored */
+{
+ help_cmd (command, stdout);
+}
+
+static void
+validate_comname (comname)
+ char *comname;
+{
+ register char *p;
+
+ if (comname == 0)
+ error_no_arg ("name of command to define");
+
+ p = comname;
+ while (*p)
+ {
+ if (!isalnum(*p) && *p != '-')
+ error ("Junk in argument list: \"%s\"", p);
+ p++;
+ }
+}
+
+/* This is just a placeholder in the command data structures. */
+static void
+user_defined_command (ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+}
+
+static void
+define_command (comname, from_tty)
+ char *comname;
+ int from_tty;
+{
+ register struct command_line *cmds;
+ register struct cmd_list_element *c, *newc, *hookc = 0;
+ char *tem = comname;
+#define HOOK_STRING "hook-"
+#define HOOK_LEN 5
+
+ validate_comname (comname);
+
+ /* Look it up, and verify that we got an exact match. */
+ c = lookup_cmd (&tem, cmdlist, "", -1, 1);
+ if (c && !STREQ (comname, c->name))
+ c = 0;
+
+ if (c)
+ {
+ if (c->class == class_user || c->class == class_alias)
+ tem = "Redefine command \"%s\"? ";
+ else
+ tem = "Really redefine built-in command \"%s\"? ";
+ if (!query (tem, c->name))
+ error ("Command \"%s\" not redefined.", c->name);
+ }
+
+ /* If this new command is a hook, then mark the command which it
+ is hooking. Note that we allow hooking `help' commands, so that
+ we can hook the `stop' pseudo-command. */
+
+ if (!strncmp (comname, HOOK_STRING, HOOK_LEN))
+ {
+ /* Look up cmd it hooks, and verify that we got an exact match. */
+ tem = comname+HOOK_LEN;
+ hookc = lookup_cmd (&tem, cmdlist, "", -1, 0);
+ if (hookc && !STREQ (comname+HOOK_LEN, hookc->name))
+ hookc = 0;
+ if (!hookc)
+ {
+ warning ("Your new `%s' command does not hook any existing command.",
+ comname);
+ if (!query ("Proceed? ", (char *)0))
+ error ("Not confirmed.");
+ }
+ }
+
+ comname = savestring (comname, strlen (comname));
+
+ /* If the rest of the commands will be case insensitive, this one
+ should behave in the same manner. */
+ for (tem = comname; *tem; tem++)
+ if (isupper(*tem)) *tem = tolower(*tem);
+
+ if (from_tty)
+ {
+ printf ("Type commands for definition of \"%s\".\n\
+End with a line saying just \"end\".\n", comname);
+ fflush (stdout);
+ }
+
+ cmds = read_command_lines ();
+
+ if (c && c->class == class_user)
+ free_command_lines (&c->user_commands);
+
+ newc = add_cmd (comname, class_user, user_defined_command,
+ (c && c->class == class_user)
+ ? c->doc : savestring ("User-defined.", 13), &cmdlist);
+ newc->user_commands = cmds;
+
+ /* If this new command is a hook, then mark both commands as being
+ tied. */
+ if (hookc)
+ {
+ hookc->hook = newc; /* Target gets hooked. */
+ newc->hookee = hookc; /* We are marked as hooking target cmd. */
+ }
+}
+
+static void
+document_command (comname, from_tty)
+ char *comname;
+ int from_tty;
+{
+ struct command_line *doclines;
+ register struct cmd_list_element *c;
+ char *tem = comname;
+
+ validate_comname (comname);
+
+ c = lookup_cmd (&tem, cmdlist, "", 0, 1);
+
+ if (c->class != class_user)
+ error ("Command \"%s\" is built-in.", comname);
+
+ if (from_tty)
+ printf ("Type documentation for \"%s\".\n\
+End with a line saying just \"end\".\n", comname);
+
+ doclines = read_command_lines ();
+
+ if (c->doc) free (c->doc);
+
+ {
+ register struct command_line *cl1;
+ register int len = 0;
+
+ for (cl1 = doclines; cl1; cl1 = cl1->next)
+ len += strlen (cl1->line) + 1;
+
+ c->doc = (char *) xmalloc (len + 1);
+ *c->doc = 0;
+
+ for (cl1 = doclines; cl1; cl1 = cl1->next)
+ {
+ strcat (c->doc, cl1->line);
+ if (cl1->next)
+ strcat (c->doc, "\n");
+ }
+ }
+
+ free_command_lines (&doclines);
+}
+
+static void
+print_gnu_advertisement()
+{
+ printf ("\
+GDB is free software and you are welcome to distribute copies of it\n\
+ under certain conditions; type \"show copying\" to see the conditions.\n\
+There is absolutely no warranty for GDB; type \"show warranty\" for details.\n\
+");
+}
+
+static void
+print_gdb_version (stream)
+ FILE *stream;
+{
+ fprintf_filtered (stream, "\
+GDB %s (%s", version, host_canonical);
+
+ if (strcmp(host_canonical, target_canonical))
+ fprintf_filtered (stream, " --target %s", target_canonical);
+
+ fprintf_filtered (stream, "), ");
+ wrap_here("");
+ fprintf_filtered (stream, "Copyright 1993 Free Software Foundation, Inc.");
+}
+
+/* ARGSUSED */
+static void
+show_version (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ immediate_quit++;
+ print_gnu_advertisement ();
+ print_gdb_version (stdout);
+ printf_filtered ("\n");
+ immediate_quit--;
+}
+
+/* xgdb calls this to reprint the usual GDB prompt. */
+
+void
+print_prompt ()
+{
+ printf ("%s", prompt);
+ fflush (stdout);
+}
+
+static void
+quit_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ if (inferior_pid != 0 && target_has_execution)
+ {
+ if (attach_flag)
+ {
+ if (query ("The program is running. Quit anyway (and detach it)? "))
+ target_detach (args, from_tty);
+ else
+ error ("Not confirmed.");
+ }
+ else
+ {
+ if (query ("The program is running. Quit anyway (and kill it)? "))
+ target_kill ();
+ else
+ error ("Not confirmed.");
+ }
+ }
+ /* Save the history information if it is appropriate to do so. */
+ if (write_history_p && history_filename)
+ write_history (history_filename);
+ exit (0);
+}
+
+/* Returns whether GDB is running on a terminal and whether the user
+ desires that questions be asked of them on that terminal. */
+
+int
+input_from_terminal_p ()
+{
+ return gdb_has_a_terminal () && (instream == stdin) & caution;
+}
+
+/* ARGSUSED */
+static void
+pwd_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ if (args) error ("The \"pwd\" command does not take an argument: %s", args);
+ getcwd (dirbuf, sizeof (dirbuf));
+
+ if (!STREQ (dirbuf, current_directory))
+ printf ("Working directory %s\n (canonically %s).\n",
+ current_directory, dirbuf);
+ else
+ printf ("Working directory %s.\n", current_directory);
+}
+
+static void
+cd_command (dir, from_tty)
+ char *dir;
+ int from_tty;
+{
+ int len;
+ /* Found something other than leading repetitions of "/..". */
+ int found_real_path;
+ char *p;
+
+ /* If the new directory is absolute, repeat is a no-op; if relative,
+ repeat might be useful but is more likely to be a mistake. */
+ dont_repeat ();
+
+ if (dir == 0)
+ error_no_arg ("new working directory");
+
+ dir = tilde_expand (dir);
+ make_cleanup (free, dir);
+
+ if (chdir (dir) < 0)
+ perror_with_name (dir);
+
+ len = strlen (dir);
+ dir = savestring (dir, len - (len > 1 && dir[len-1] == '/'));
+ if (dir[0] == '/')
+ current_directory = dir;
+ else
+ {
+ if (current_directory[0] == '/' && current_directory[1] == '\0')
+ current_directory = concat (current_directory, dir, NULL);
+ else
+ current_directory = concat (current_directory, "/", dir, NULL);
+ free (dir);
+ }
+
+ /* Now simplify any occurrences of `.' and `..' in the pathname. */
+
+ found_real_path = 0;
+ for (p = current_directory; *p;)
+ {
+ if (p[0] == '/' && p[1] == '.' && (p[2] == 0 || p[2] == '/'))
+ strcpy (p, p + 2);
+ else if (p[0] == '/' && p[1] == '.' && p[2] == '.'
+ && (p[3] == 0 || p[3] == '/'))
+ {
+ if (found_real_path)
+ {
+ /* Search backwards for the directory just before the "/.."
+ and obliterate it and the "/..". */
+ char *q = p;
+ while (q != current_directory && q[-1] != '/')
+ --q;
+
+ if (q == current_directory)
+ /* current_directory is
+ a relative pathname ("can't happen"--leave it alone). */
+ ++p;
+ else
+ {
+ strcpy (q - 1, p + 3);
+ p = q - 1;
+ }
+ }
+ else
+ /* We are dealing with leading repetitions of "/..", for example
+ "/../..", which is the Mach super-root. */
+ p += 3;
+ }
+ else
+ {
+ found_real_path = 1;
+ ++p;
+ }
+ }
+
+ forget_cached_source_info ();
+
+ if (from_tty)
+ pwd_command ((char *) 0, 1);
+}
+
+/* ARGSUSED */
+static void
+source_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ FILE *stream;
+ struct cleanup *cleanups;
+ char *file = args;
+
+ if (file == NULL)
+ {
+ error ("source command requires pathname of file to source.");
+ }
+
+ file = tilde_expand (file);
+ make_cleanup (free, file);
+
+ stream = fopen (file, FOPEN_RT);
+ if (stream == 0)
+ perror_with_name (file);
+
+ cleanups = make_cleanup (fclose, stream);
+
+ read_command_file (stream);
+
+ do_cleanups (cleanups);
+}
+
+/* ARGSUSED */
+static void
+echo_command (text, from_tty)
+ char *text;
+ int from_tty;
+{
+ char *p = text;
+ register int c;
+
+ if (text)
+ while ((c = *p++) != '\0')
+ {
+ if (c == '\\')
+ {
+ /* \ at end of argument is used after spaces
+ so they won't be lost. */
+ if (*p == 0)
+ return;
+
+ c = parse_escape (&p);
+ if (c >= 0)
+ printf_filtered ("%c", c);
+ }
+ else
+ printf_filtered ("%c", c);
+ }
+
+ /* Force this output to appear now. */
+ wrap_here ("");
+ fflush (stdout);
+}
+
+
+/* Functions to manipulate command line editing control variables. */
+
+/* Number of commands to print in each call to show_commands. */
+#define Hist_print 10
+static void
+show_commands (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ /* Index for history commands. Relative to history_base. */
+ int offset;
+
+ /* Number of the history entry which we are planning to display next.
+ Relative to history_base. */
+ static int num = 0;
+
+ /* The first command in the history which doesn't exist (i.e. one more
+ than the number of the last command). Relative to history_base. */
+ int hist_len;
+
+ extern struct _hist_entry *history_get PARAMS ((int));
+ extern int history_base;
+
+ /* Print out some of the commands from the command history. */
+ /* First determine the length of the history list. */
+ hist_len = history_size;
+ for (offset = 0; offset < history_size; offset++)
+ {
+ if (!history_get (history_base + offset))
+ {
+ hist_len = offset;
+ break;
+ }
+ }
+
+ if (args)
+ {
+ if (args[0] == '+' && args[1] == '\0')
+ /* "info editing +" should print from the stored position. */
+ ;
+ else
+ /* "info editing <exp>" should print around command number <exp>. */
+ num = (parse_and_eval_address (args) - history_base) - Hist_print / 2;
+ }
+ /* "show commands" means print the last Hist_print commands. */
+ else
+ {
+ num = hist_len - Hist_print;
+ }
+
+ if (num < 0)
+ num = 0;
+
+ /* If there are at least Hist_print commands, we want to display the last
+ Hist_print rather than, say, the last 6. */
+ if (hist_len - num < Hist_print)
+ {
+ num = hist_len - Hist_print;
+ if (num < 0)
+ num = 0;
+ }
+
+ for (offset = num; offset < num + Hist_print && offset < hist_len; offset++)
+ {
+ printf_filtered ("%5d %s\n", history_base + offset,
+ (history_get (history_base + offset))->line);
+ }
+
+ /* The next command we want to display is the next one that we haven't
+ displayed yet. */
+ num += Hist_print;
+
+ /* If the user repeats this command with return, it should do what
+ "show commands +" does. This is unnecessary if arg is null,
+ because "show commands +" is not useful after "show commands". */
+ if (from_tty && args)
+ {
+ args[0] = '+';
+ args[1] = '\0';
+ }
+}
+
+/* Called by do_setshow_command. */
+/* ARGSUSED */
+static void
+set_history_size_command (args, from_tty, c)
+ char *args;
+ int from_tty;
+ struct cmd_list_element *c;
+{
+ if (history_size == INT_MAX)
+ unstifle_history ();
+ else if (history_size >= 0)
+ stifle_history (history_size);
+ else
+ {
+ history_size = INT_MAX;
+ error ("History size must be non-negative");
+ }
+}
+
+/* ARGSUSED */
+static void
+set_history (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ printf ("\"set history\" must be followed by the name of a history subcommand.\n");
+ help_list (sethistlist, "set history ", -1, stdout);
+}
+
+/* ARGSUSED */
+static void
+show_history (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ cmd_show_list (showhistlist, from_tty, "");
+}
+
+int info_verbose = 0; /* Default verbose msgs off */
+
+/* Called by do_setshow_command. An elaborate joke. */
+/* ARGSUSED */
+static void
+set_verbose (args, from_tty, c)
+ char *args;
+ int from_tty;
+ struct cmd_list_element *c;
+{
+ char *cmdname = "verbose";
+ struct cmd_list_element *showcmd;
+
+ showcmd = lookup_cmd_1 (&cmdname, showlist, NULL, 1);
+
+ if (info_verbose)
+ {
+ c->doc = "Set verbose printing of informational messages.";
+ showcmd->doc = "Show verbose printing of informational messages.";
+ }
+ else
+ {
+ c->doc = "Set verbosity.";
+ showcmd->doc = "Show verbosity.";
+ }
+}
+
+static void
+float_handler (signo)
+int signo;
+{
+ /* This message is based on ANSI C, section 4.7. Note that integer
+ divide by zero causes this, so "float" is a misnomer. */
+ signal (SIGFPE, float_handler);
+ error ("Erroneous arithmetic operation.");
+}
+
+/* Return whether we are running a batch file or from terminal. */
+int
+batch_mode ()
+{
+ return !(instream == stdin && ISATTY (stdin));
+}
+
+
+static void
+init_cmd_lists ()
+{
+ cmdlist = NULL;
+ infolist = NULL;
+ enablelist = NULL;
+ disablelist = NULL;
+ deletelist = NULL;
+ enablebreaklist = NULL;
+ setlist = NULL;
+ unsetlist = NULL;
+ showlist = NULL;
+ sethistlist = NULL;
+ showhistlist = NULL;
+ unsethistlist = NULL;
+#if MAINTENANCE_CMDS
+ maintenancelist = NULL;
+ maintenanceinfolist = NULL;
+ maintenanceprintlist = NULL;
+#endif
+ setprintlist = NULL;
+ showprintlist = NULL;
+ setchecklist = NULL;
+ showchecklist = NULL;
+}
+
+/* Init the history buffer. Note that we are called after the init file(s)
+ * have been read so that the user can change the history file via his
+ * .gdbinit file (for instance). The GDBHISTFILE environment variable
+ * overrides all of this.
+ */
+
+static void
+init_history()
+{
+ char *tmpenv;
+
+ tmpenv = getenv ("HISTSIZE");
+ if (tmpenv)
+ history_size = atoi (tmpenv);
+ else if (!history_size)
+ history_size = 256;
+
+ stifle_history (history_size);
+
+ tmpenv = getenv ("GDBHISTFILE");
+ if (tmpenv)
+ history_filename = savestring (tmpenv, strlen(tmpenv));
+ else if (!history_filename) {
+ /* We include the current directory so that if the user changes
+ directories the file written will be the same as the one
+ that was read. */
+ history_filename = concat (current_directory, "/.gdb_history", NULL);
+ }
+ read_history (history_filename);
+}
+
+static void
+init_main ()
+{
+ struct cmd_list_element *c;
+
+#ifdef DEFAULT_PROMPT
+ prompt = savestring (DEFAULT_PROMPT, strlen(DEFAULT_PROMPT));
+#else
+ prompt = savestring ("(gdb) ", 6);
+#endif
+
+ /* Set the important stuff up for command editing. */
+ command_editing_p = 1;
+ history_expansion_p = 0;
+ write_history_p = 0;
+
+ /* Setup important stuff for command line editing. */
+ rl_completion_entry_function = (int (*)()) symbol_completion_function;
+ rl_completer_word_break_characters = gdb_completer_word_break_characters;
+ rl_completer_quote_characters = gdb_completer_quote_characters;
+ rl_readline_name = "gdb";
+
+ /* Define the classes of commands.
+ They will appear in the help list in the reverse of this order. */
+
+ add_cmd ("internals", class_maintenance, NO_FUNCTION,
+ "Maintenance commands.\n\
+Some gdb commands are provided just for use by gdb maintainers.\n\
+These commands are subject to frequent change, and may not be as\n\
+well documented as user commands.",
+ &cmdlist);
+ add_cmd ("obscure", class_obscure, NO_FUNCTION, "Obscure features.", &cmdlist);
+ add_cmd ("aliases", class_alias, NO_FUNCTION, "Aliases of other commands.", &cmdlist);
+ add_cmd ("user-defined", class_user, NO_FUNCTION, "User-defined commands.\n\
+The commands in this class are those defined by the user.\n\
+Use the \"define\" command to define a command.", &cmdlist);
+ add_cmd ("support", class_support, NO_FUNCTION, "Support facilities.", &cmdlist);
+ add_cmd ("status", class_info, NO_FUNCTION, "Status inquiries.", &cmdlist);
+ add_cmd ("files", class_files, NO_FUNCTION, "Specifying and examining files.", &cmdlist);
+ add_cmd ("breakpoints", class_breakpoint, NO_FUNCTION, "Making program stop at certain points.", &cmdlist);
+ add_cmd ("data", class_vars, NO_FUNCTION, "Examining data.", &cmdlist);
+ add_cmd ("stack", class_stack, NO_FUNCTION, "Examining the stack.\n\
+The stack is made up of stack frames. Gdb assigns numbers to stack frames\n\
+counting from zero for the innermost (currently executing) frame.\n\n\
+At any time gdb identifies one frame as the \"selected\" frame.\n\
+Variable lookups are done with respect to the selected frame.\n\
+When the program being debugged stops, gdb selects the innermost frame.\n\
+The commands below can be used to select other frames by number or address.",
+ &cmdlist);
+ add_cmd ("running", class_run, NO_FUNCTION, "Running the program.", &cmdlist);
+
+ add_com ("pwd", class_files, pwd_command,
+ "Print working directory. This is used for your program as well.");
+ c = add_cmd ("cd", class_files, cd_command,
+ "Set working directory to DIR for debugger and program being debugged.\n\
+The change does not take effect for the program being debugged\n\
+until the next time it is started.", &cmdlist);
+ c->completer = filename_completer;
+
+ add_show_from_set
+ (add_set_cmd ("prompt", class_support, var_string, (char *)&prompt,
+ "Set gdb's prompt",
+ &setlist),
+ &showlist);
+
+ add_com ("echo", class_support, echo_command,
+ "Print a constant string. Give string as argument.\n\
+C escape sequences may be used in the argument.\n\
+No newline is added at the end of the argument;\n\
+use \"\\n\" if you want a newline to be printed.\n\
+Since leading and trailing whitespace are ignored in command arguments,\n\
+if you want to print some you must use \"\\\" before leading whitespace\n\
+to be printed or after trailing whitespace.");
+ add_com ("document", class_support, document_command,
+ "Document a user-defined command.\n\
+Give command name as argument. Give documentation on following lines.\n\
+End with a line of just \"end\".");
+ add_com ("define", class_support, define_command,
+ "Define a new command name. Command name is argument.\n\
+Definition appears on following lines, one command per line.\n\
+End with a line of just \"end\".\n\
+Use the \"document\" command to give documentation for the new command.\n\
+Commands defined in this way do not take arguments.");
+
+#ifdef __STDC__
+ c = add_cmd ("source", class_support, source_command,
+ "Read commands from a file named FILE.\n\
+Note that the file \"" GDBINIT_FILENAME "\" is read automatically in this way\n\
+when gdb is started.", &cmdlist);
+#else
+ /* Punt file name, we can't help it easily. */
+ c = add_cmd ("source", class_support, source_command,
+ "Read commands from a file named FILE.\n\
+Note that the file \".gdbinit\" is read automatically in this way\n\
+when gdb is started.", &cmdlist);
+#endif
+ c->completer = filename_completer;
+
+ add_com ("quit", class_support, quit_command, "Exit gdb.");
+ add_com ("help", class_support, help_command, "Print list of commands.");
+ add_com_alias ("q", "quit", class_support, 1);
+ add_com_alias ("h", "help", class_support, 1);
+
+
+ c = add_set_cmd ("verbose", class_support, var_boolean, (char *)&info_verbose,
+ "Set ",
+ &setlist),
+ add_show_from_set (c, &showlist);
+ c->function.sfunc = set_verbose;
+ set_verbose (NULL, 0, c);
+
+ add_show_from_set
+ (add_set_cmd ("editing", class_support, var_boolean, (char *)&command_editing_p,
+ "Set editing of command lines as they are typed.\n\
+Use \"on\" to enable to enable the editing, and \"off\" to disable it.\n\
+Without an argument, command line editing is enabled. To edit, use\n\
+EMACS-like or VI-like commands like control-P or ESC.", &setlist),
+ &showlist);
+
+ add_prefix_cmd ("history", class_support, set_history,
+ "Generic command for setting command history parameters.",
+ &sethistlist, "set history ", 0, &setlist);
+ add_prefix_cmd ("history", class_support, show_history,
+ "Generic command for showing command history parameters.",
+ &showhistlist, "show history ", 0, &showlist);
+
+ add_show_from_set
+ (add_set_cmd ("expansion", no_class, var_boolean, (char *)&history_expansion_p,
+ "Set history expansion on command input.\n\
+Without an argument, history expansion is enabled.", &sethistlist),
+ &showhistlist);
+
+ add_show_from_set
+ (add_set_cmd ("save", no_class, var_boolean, (char *)&write_history_p,
+ "Set saving of the history record on exit.\n\
+Use \"on\" to enable to enable the saving, and \"off\" to disable it.\n\
+Without an argument, saving is enabled.", &sethistlist),
+ &showhistlist);
+
+ c = add_set_cmd ("size", no_class, var_integer, (char *)&history_size,
+ "Set the size of the command history, \n\
+ie. the number of previous commands to keep a record of.", &sethistlist);
+ add_show_from_set (c, &showhistlist);
+ c->function.sfunc = set_history_size_command;
+
+ add_show_from_set
+ (add_set_cmd ("filename", no_class, var_filename, (char *)&history_filename,
+ "Set the filename in which to record the command history\n\
+ (the list of previous commands of which a record is kept).", &sethistlist),
+ &showhistlist);
+
+ add_show_from_set
+ (add_set_cmd ("confirm", class_support, var_boolean,
+ (char *)&caution,
+ "Set whether to confirm potentially dangerous operations.",
+ &setlist),
+ &showlist);
+
+ add_prefix_cmd ("info", class_info, info_command,
+ "Generic command for showing things about the program being debugged.",
+ &infolist, "info ", 0, &cmdlist);
+ add_com_alias ("i", "info", class_info, 1);
+
+ add_prefix_cmd ("show", class_info, show_command,
+ "Generic command for showing things about the debugger.",
+ &showlist, "show ", 0, &cmdlist);
+ /* Another way to get at the same thing. */
+ add_info ("set", show_command, "Show all GDB settings.");
+
+ add_cmd ("commands", no_class, show_commands,
+ "Show the the history of commands you typed.\n\
+You can supply a command number to start with, or a `+' to start after\n\
+the previous command number shown.",
+ &showlist);
+
+ add_cmd ("version", no_class, show_version,
+ "Show what version of GDB this is.", &showlist);
+
+ add_show_from_set (
+ add_set_cmd ("remotedebug", no_class, var_boolean, (char *)&remote_debug,
+ "Set debugging of remote protocol.\n\
+When enabled, each packet sent or received with the remote target\n\
+is displayed.", &setlist),
+ &showlist);
+}
diff --git a/gnu/usr.bin/gdb/gdb/maint.c b/gnu/usr.bin/gdb/gdb/maint.c
new file mode 100644
index 0000000..b533451
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/maint.c
@@ -0,0 +1,305 @@
+/* Support for GDB maintenance commands.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+ Written by Fred Fish at Cygnus Support.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "defs.h"
+
+#if MAINTENANCE_CMDS /* Entire file goes away if not including maint cmds */
+
+#include <signal.h>
+#include "command.h"
+#include "gdbcmd.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "demangle.h"
+#include "gdbcore.h"
+
+static void
+maintenance_command PARAMS ((char *, int));
+
+static void
+maintenance_dump_me PARAMS ((char *, int));
+
+static void
+maintenance_demangle PARAMS ((char *, int));
+
+/*
+
+LOCAL FUNCTION
+
+ maintenance_command -- access the maintenance subcommands
+
+SYNOPSIS
+
+ void maintenance_command (char *args, int from_tty)
+
+DESCRIPTION
+
+*/
+
+static void
+maintenance_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ printf ("\"maintenance\" must be followed by the name of a maintenance command.\n");
+ help_list (maintenancelist, "maintenance ", -1, stdout);
+}
+
+
+/* ARGSUSED */
+static void
+maintenance_dump_me (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ if (query ("Should GDB dump core? "))
+ {
+ signal (SIGQUIT, SIG_DFL);
+ kill (getpid (), SIGQUIT);
+ }
+}
+
+/* Someday we should allow demangling for things other than just
+ explicit strings. For example, we might want to be able to
+ specify the address of a string in either GDB's process space
+ or the debuggee's process space, and have gdb fetch and demangle
+ that string. If we have a char* pointer "ptr" that points to
+ a string, we might want to be able to given just the name and
+ have GDB demangle and print what it points to, etc. (FIXME) */
+
+static void
+maintenance_demangle (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ char *demangled;
+
+ if (args == NULL || *args == '\0')
+ {
+ printf ("\"maintenance demangle\" takes an argument to demangle.\n");
+ }
+ else
+ {
+ demangled = cplus_demangle (args, DMGL_ANSI | DMGL_PARAMS);
+ if (demangled != NULL)
+ {
+ printf ("%s\n", demangled);
+ free (demangled);
+ }
+ else
+ {
+ printf ("Can't demangle \"%s\"\n", args);
+ }
+ }
+}
+
+/* The "maintenance info" command is defined as a prefix, with allow_unknown 0.
+ Therefore, its own definition is called only for "maintenance info" with
+ no args. */
+
+/* ARGSUSED */
+static void
+maintenance_info_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ printf ("\"maintenance info\" must be followed by the name of an info command.\n");
+ help_list (maintenanceinfolist, "maintenance info ", -1, stdout);
+}
+
+static void
+print_section_table (abfd, asect, ignore)
+ bfd *abfd;
+ asection *asect;
+ PTR ignore;
+{
+ flagword flags;
+
+ flags = bfd_get_section_flags (abfd, asect);
+
+ printf_filtered (" %s",
+ local_hex_string_custom
+ ((unsigned long) bfd_section_vma (abfd, asect), "08l"));
+ printf_filtered ("->%s",
+ local_hex_string_custom
+ ((unsigned long) (bfd_section_vma (abfd, asect)
+ + bfd_section_size (abfd, asect)),
+ "08l"));
+ printf_filtered (" at %s",
+ local_hex_string_custom
+ ((unsigned long) asect->filepos, "08l"));
+ printf_filtered (": %s", bfd_section_name (abfd, asect));
+
+ if (flags & SEC_ALLOC)
+ printf_filtered (" ALLOC");
+ if (flags & SEC_LOAD)
+ printf_filtered (" LOAD");
+ if (flags & SEC_RELOC)
+ printf_filtered (" RELOC");
+ if (flags & SEC_READONLY)
+ printf_filtered (" READONLY");
+ if (flags & SEC_CODE)
+ printf_filtered (" CODE");
+ if (flags & SEC_DATA)
+ printf_filtered (" DATA");
+ if (flags & SEC_ROM)
+ printf_filtered (" ROM");
+ if (flags & SEC_CONSTRUCTOR)
+ printf_filtered (" CONSTRUCTOR");
+ if (flags & SEC_HAS_CONTENTS)
+ printf_filtered (" HAS_CONTENTS");
+ if (flags & SEC_NEVER_LOAD)
+ printf_filtered (" NEVER_LOAD");
+ if (flags & SEC_SHARED_LIBRARY)
+ printf_filtered (" SHARED_LIBRARY");
+ if (flags & SEC_IS_COMMON)
+ printf_filtered (" IS_COMMON");
+
+ printf_filtered ("\n");
+}
+
+/* ARGSUSED */
+static void
+maintenance_info_sections (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ if (exec_bfd)
+ {
+ printf_filtered ("Exec file:\n");
+ printf_filtered (" `%s', ", bfd_get_filename(exec_bfd));
+ wrap_here (" ");
+ printf_filtered ("file type %s.\n", bfd_get_target(exec_bfd));
+ bfd_map_over_sections(exec_bfd, print_section_table, 0);
+ }
+
+ if (core_bfd)
+ {
+ printf_filtered ("Core file:\n");
+ printf_filtered (" `%s', ", bfd_get_filename(core_bfd));
+ wrap_here (" ");
+ printf_filtered ("file type %s.\n", bfd_get_target(core_bfd));
+ bfd_map_over_sections(core_bfd, print_section_table, 0);
+ }
+}
+
+/* The "maintenance print" command is defined as a prefix, with allow_unknown
+ 0. Therefore, its own definition is called only for "maintenance print"
+ with no args. */
+
+/* ARGSUSED */
+static void
+maintenance_print_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ printf ("\"maintenance print\" must be followed by the name of a print command.\n");
+ help_list (maintenanceprintlist, "maintenance print ", -1, stdout);
+}
+
+/*
+
+GLOBAL FUNCTION
+
+ _initialize_maint_cmds -- initialize the process file system stuff
+
+SYNOPSIS
+
+ void _initialize_maint_cmds (void)
+
+DESCRIPTION
+
+ Do required initializations during gdb startup for using the
+ /proc file system interface.
+
+*/
+
+
+void
+_initialize_maint_cmds ()
+{
+ add_prefix_cmd ("maintenance", class_maintenance, maintenance_command,
+ "Commands for use by GDB maintainers.\n\
+Includes commands to dump specific internal GDB structures in\n\
+a human readable form, to cause GDB to deliberately dump core,\n\
+to test internal functions such as the C++ demangler, etc.",
+ &maintenancelist, "maintenance ", 0,
+ &cmdlist);
+
+ add_com_alias ("mt", "maintenance", class_maintenance, 1);
+
+ add_prefix_cmd ("info", class_maintenance, maintenance_info_command,
+ "Commands for showing internal info about the program being debugged.",
+ &maintenanceinfolist, "maintenance info ", 0,
+ &maintenancelist);
+
+ add_cmd ("sections", class_maintenance, maintenance_info_sections,
+ "List the BFD sections of the exec and core files.",
+ &maintenanceinfolist);
+
+ add_prefix_cmd ("print", class_maintenance, maintenance_print_command,
+ "Maintenance command for printing GDB internal state.",
+ &maintenanceprintlist, "maintenance print ", 0,
+ &maintenancelist);
+
+ add_cmd ("dump-me", class_maintenance, maintenance_dump_me,
+ "Get fatal error; make debugger dump its core.\n\
+GDB sets it's handling of SIGQUIT back to SIG_DFL and then sends\n\
+itself a SIGQUIT signal.",
+ &maintenancelist);
+
+ add_cmd ("demangle", class_maintenance, maintenance_demangle,
+ "Demangle a C++ mangled name.\n\
+Call internal GDB demangler routine to demangle a C++ link name\n\
+and prints the result.",
+ &maintenancelist);
+
+ add_cmd ("type", class_maintenance, maintenance_print_type,
+ "Print a type chain for a given symbol.\n\
+For each node in a type chain, print the raw data for each member of\n\
+the type structure, and the interpretation of the data.",
+ &maintenanceprintlist);
+
+ add_cmd ("symbols", class_maintenance, maintenance_print_symbols,
+ "Print dump of current symbol definitions.\n\
+Entries in the full symbol table are dumped to file OUTFILE.\n\
+If a SOURCE file is specified, dump only that file's symbols.",
+ &maintenanceprintlist);
+
+ add_cmd ("msymbols", class_maintenance, maintenance_print_msymbols,
+ "Print dump of current minimal symbol definitions.\n\
+Entries in the minimal symbol table are dumped to file OUTFILE.\n\
+If a SOURCE file is specified, dump only that file's minimal symbols.",
+ &maintenanceprintlist);
+
+ add_cmd ("psymbols", class_maintenance, maintenance_print_psymbols,
+ "Print dump of current partial symbol definitions.\n\
+Entries in the partial symbol table are dumped to file OUTFILE.\n\
+If a SOURCE file is specified, dump only that file's partial symbols.",
+ &maintenanceprintlist);
+
+ add_cmd ("objfiles", class_maintenance, maintenance_print_objfiles,
+ "Print dump of current object file definitions.",
+ &maintenanceprintlist);
+
+}
+
+#endif /* MAINTENANCE_CMDS */
diff --git a/gnu/usr.bin/gdb/gdb/mem-break.c b/gnu/usr.bin/gdb/gdb/mem-break.c
new file mode 100644
index 0000000..74dfaa1
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/mem-break.c
@@ -0,0 +1,104 @@
+/* Simulate breakpoints by patching locations in the target system, for GDB.
+ Copyright 1990, 1991 Free Software Foundation, Inc.
+ Contributed by Cygnus Support. Written by John Gilmore.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+
+#ifdef BREAKPOINT
+/* This file is only useful if BREAKPOINT is set. If not, we punt. */
+
+#include "symtab.h"
+#include "breakpoint.h"
+#include "inferior.h"
+#include "target.h"
+
+/* This is the sequence of bytes we insert for a breakpoint. On some
+ machines, breakpoints are handled by the target environment and we
+ don't have to worry about them here. */
+
+static unsigned char break_insn[] = BREAKPOINT;
+
+/* This is only to check that BREAKPOINT fits in BREAKPOINT_MAX bytes. */
+
+static unsigned char check_break_insn_size[BREAKPOINT_MAX] = BREAKPOINT;
+
+/* Insert a breakpoint on targets that don't have any better breakpoint
+ support. We read the contents of the target location and stash it,
+ then overwrite it with a breakpoint instruction. ADDR is the target
+ location in the target machine. CONTENTS_CACHE is a pointer to
+ memory allocated for saving the target contents. It is guaranteed
+ by the caller to be long enough to save sizeof BREAKPOINT bytes (this
+ is accomplished via BREAKPOINT_MAX). */
+
+int
+memory_insert_breakpoint (addr, contents_cache)
+ CORE_ADDR addr;
+ char *contents_cache;
+{
+ int val;
+
+ val = target_read_memory (addr, contents_cache, sizeof break_insn);
+
+ if (val == 0)
+ val = target_write_memory (addr, (char *)break_insn, sizeof break_insn);
+
+ return val;
+}
+
+
+int
+memory_remove_breakpoint (addr, contents_cache)
+ CORE_ADDR addr;
+ char *contents_cache;
+{
+ return target_write_memory (addr, contents_cache, sizeof break_insn);
+}
+
+
+/* FIXME: This is a hack and should depend on the debugging target.
+ See comment in breakpoint.c where this is used. */
+
+int memory_breakpoint_size = sizeof (break_insn);
+
+
+#else /* BREAKPOINT */
+
+char nogo[] = "Breakpoints not implemented for this target.";
+
+int
+memory_insert_breakpoint (addr, contents_cache)
+ CORE_ADDR addr;
+ char *contents_cache;
+{
+ error (nogo);
+ return 0; /* lint */
+}
+
+int
+memory_remove_breakpoint (addr, contents_cache)
+ CORE_ADDR addr;
+ char *contents_cache;
+{
+ error (nogo);
+ return 0; /* lint */
+}
+
+int memory_breakpoint_size = -1;
+
+#endif /* BREAKPOINT */
diff --git a/gnu/usr.bin/gdb/gdb/minsyms.c b/gnu/usr.bin/gdb/gdb/minsyms.c
new file mode 100644
index 0000000..dbb4e79
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/minsyms.c
@@ -0,0 +1,597 @@
+/* GDB routines for manipulating the minimal symbol tables.
+ Copyright 1992 Free Software Foundation, Inc.
+ Contributed by Cygnus Support, using pieces from other GDB modules.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file contains support routines for creating, manipulating, and
+ destroying minimal symbol tables.
+
+ Minimal symbol tables are used to hold some very basic information about
+ all defined global symbols (text, data, bss, abs, etc). The only two
+ required pieces of information are the symbol's name and the address
+ associated with that symbol.
+
+ In many cases, even if a file was compiled with no special options for
+ debugging at all, as long as was not stripped it will contain sufficient
+ information to build useful minimal symbol tables using this structure.
+
+ Even when a file contains enough debugging information to build a full
+ symbol table, these minimal symbols are still useful for quickly mapping
+ between names and addresses, and vice versa. They are also sometimes used
+ to figure out what full symbol table entries need to be read in. */
+
+
+#include "defs.h"
+#include "symtab.h"
+#include "bfd.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "demangle.h"
+
+/* Accumulate the minimal symbols for each objfile in bunches of BUNCH_SIZE.
+ At the end, copy them all into one newly allocated location on an objfile's
+ symbol obstack. */
+
+#define BUNCH_SIZE 127
+
+struct msym_bunch
+{
+ struct msym_bunch *next;
+ struct minimal_symbol contents[BUNCH_SIZE];
+};
+
+/* Bunch currently being filled up.
+ The next field points to chain of filled bunches. */
+
+static struct msym_bunch *msym_bunch;
+
+/* Number of slots filled in current bunch. */
+
+static int msym_bunch_index;
+
+/* Total number of minimal symbols recorded so far for the objfile. */
+
+static int msym_count;
+
+/* Prototypes for local functions. */
+
+static int
+compare_minimal_symbols PARAMS ((const void *, const void *));
+
+static int
+compact_minimal_symbols PARAMS ((struct minimal_symbol *, int));
+
+/* Look through all the current minimal symbol tables and find the first
+ minimal symbol that matches NAME. If OBJF is non-NULL, it specifies a
+ particular objfile and the search is limited to that objfile. Returns
+ a pointer to the minimal symbol that matches, or NULL if no match is found.
+
+ Note: One instance where there may be duplicate minimal symbols with
+ the same name is when the symbol tables for a shared library and the
+ symbol tables for an executable contain global symbols with the same
+ names (the dynamic linker deals with the duplication). */
+
+struct minimal_symbol *
+lookup_minimal_symbol (name, objf)
+ register const char *name;
+ struct objfile *objf;
+{
+ struct objfile *objfile;
+ struct minimal_symbol *msymbol;
+ struct minimal_symbol *found_symbol = NULL;
+ struct minimal_symbol *found_file_symbol = NULL;
+#ifdef IBM6000_TARGET
+ struct minimal_symbol *trampoline_symbol = NULL;
+#endif
+
+ for (objfile = object_files;
+ objfile != NULL && found_symbol == NULL;
+ objfile = objfile -> next)
+ {
+ if (objf == NULL || objf == objfile)
+ {
+ for (msymbol = objfile -> msymbols;
+ msymbol != NULL && SYMBOL_NAME (msymbol) != NULL &&
+ found_symbol == NULL;
+ msymbol++)
+ {
+ if (SYMBOL_MATCHES_NAME (msymbol, name))
+ {
+ switch (MSYMBOL_TYPE (msymbol))
+ {
+ case mst_file_text:
+ case mst_file_data:
+ case mst_file_bss:
+ /* It is file-local. If we find more than one, just
+ return the latest one (the user can't expect
+ useful behavior in that case). */
+ found_file_symbol = msymbol;
+ break;
+
+ case mst_unknown:
+#ifdef IBM6000_TARGET
+ /* I *think* all platforms using shared
+ libraries (and trampoline code) will suffer
+ this problem. Consider a case where there are
+ 5 shared libraries, each referencing `foo'
+ with a trampoline entry. When someone wants
+ to put a breakpoint on `foo' and the only
+ info we have is minimal symbol vector, we
+ want to use the real `foo', rather than one
+ of those trampoline entries. MGO */
+
+ /* If a trampoline symbol is found, we prefer to
+ keep looking for the *real* symbol. If the
+ actual symbol not found, then we'll use the
+ trampoline entry. Sorry for the machine
+ dependent code here, but I hope this will
+ benefit other platforms as well. For
+ trampoline entries, we used mst_unknown
+ earlier. Perhaps we should define a
+ `mst_trampoline' type?? */
+
+ if (trampoline_symbol == NULL)
+ trampoline_symbol = msymbol;
+ break;
+#else
+ /* FALLTHROUGH */
+#endif
+ default:
+ found_symbol = msymbol;
+ break;
+ }
+ }
+ }
+ }
+ }
+ /* External symbols are best. */
+ if (found_symbol)
+ return found_symbol;
+
+ /* File-local symbols are next best. */
+ if (found_file_symbol)
+ return found_file_symbol;
+
+ /* Symbols for IBM shared library trampolines are next best. */
+#ifdef IBM6000_TARGET
+ if (trampoline_symbol)
+ return trampoline_symbol;
+#endif
+
+ return NULL;
+}
+
+
+/* Search through the minimal symbol table for each objfile and find the
+ symbol whose address is the largest address that is still less than or
+ equal to PC. Returns a pointer to the minimal symbol if such a symbol
+ is found, or NULL if PC is not in a suitable range. Note that we need
+ to look through ALL the minimal symbol tables before deciding on the
+ symbol that comes closest to the specified PC. This is because objfiles
+ can overlap, for example objfile A has .text at 0x100 and .data at 0x40000
+ and objfile B has .text at 0x234 and .data at 0x40048. */
+
+struct minimal_symbol *
+lookup_minimal_symbol_by_pc (pc)
+ register CORE_ADDR pc;
+{
+ register int lo;
+ register int hi;
+ register int new;
+ register struct objfile *objfile;
+ register struct minimal_symbol *msymbol;
+ register struct minimal_symbol *best_symbol = NULL;
+
+ for (objfile = object_files;
+ objfile != NULL;
+ objfile = objfile -> next)
+ {
+ /* If this objfile has a minimal symbol table, go search it using
+ a binary search. Note that a minimal symbol table always consists
+ of at least two symbols, a "real" symbol and the terminating
+ "null symbol". If there are no real symbols, then there is no
+ minimal symbol table at all. */
+
+ if ((msymbol = objfile -> msymbols) != NULL)
+ {
+ lo = 0;
+ hi = objfile -> minimal_symbol_count - 1;
+
+ /* This code assumes that the minimal symbols are sorted by
+ ascending address values. If the pc value is greater than or
+ equal to the first symbol's address, then some symbol in this
+ minimal symbol table is a suitable candidate for being the
+ "best" symbol. This includes the last real symbol, for cases
+ where the pc value is larger than any address in this vector.
+
+ By iterating until the address associated with the current
+ hi index (the endpoint of the test interval) is less than
+ or equal to the desired pc value, we accomplish two things:
+ (1) the case where the pc value is larger than any minimal
+ symbol address is trivially solved, (2) the address associated
+ with the hi index is always the one we want when the interation
+ terminates. In essence, we are iterating the test interval
+ down until the pc value is pushed out of it from the high end.
+
+ Warning: this code is trickier than it would appear at first. */
+
+ /* Should also requires that pc is <= end of objfile. FIXME! */
+ if (pc >= SYMBOL_VALUE_ADDRESS (&msymbol[lo]))
+ {
+ while (SYMBOL_VALUE_ADDRESS (&msymbol[hi]) > pc)
+ {
+ /* pc is still strictly less than highest address */
+ /* Note "new" will always be >= lo */
+ new = (lo + hi) / 2;
+ if ((SYMBOL_VALUE_ADDRESS (&msymbol[new]) >= pc) ||
+ (lo == new))
+ {
+ hi = new;
+ }
+ else
+ {
+ lo = new;
+ }
+ }
+ /* The minimal symbol indexed by hi now is the best one in this
+ objfile's minimal symbol table. See if it is the best one
+ overall. */
+
+ /* Skip any absolute symbols. This is apparently what adb
+ and dbx do, and is needed for the CM-5. There are two
+ known possible problems: (1) on ELF, apparently end, edata,
+ etc. are absolute. Not sure ignoring them here is a big
+ deal, but if we want to use them, the fix would go in
+ elfread.c. (2) I think shared library entry points on the
+ NeXT are absolute. If we want special handling for this
+ it probably should be triggered by a special
+ mst_abs_or_lib or some such. */
+ while (hi >= 0
+ && msymbol[hi].type == mst_abs)
+ --hi;
+
+ if (hi >= 0
+ && ((best_symbol == NULL) ||
+ (SYMBOL_VALUE_ADDRESS (best_symbol) <
+ SYMBOL_VALUE_ADDRESS (&msymbol[hi]))))
+ {
+ best_symbol = &msymbol[hi];
+ }
+ }
+ }
+ }
+ return (best_symbol);
+}
+
+/* Prepare to start collecting minimal symbols. Note that presetting
+ msym_bunch_index to BUNCH_SIZE causes the first call to save a minimal
+ symbol to allocate the memory for the first bunch. */
+
+void
+init_minimal_symbol_collection ()
+{
+ msym_count = 0;
+ msym_bunch = NULL;
+ msym_bunch_index = BUNCH_SIZE;
+}
+
+void
+prim_record_minimal_symbol (name, address, ms_type)
+ const char *name;
+ CORE_ADDR address;
+ enum minimal_symbol_type ms_type;
+{
+ register struct msym_bunch *new;
+ register struct minimal_symbol *msymbol;
+
+ if (msym_bunch_index == BUNCH_SIZE)
+ {
+ new = (struct msym_bunch *) xmalloc (sizeof (struct msym_bunch));
+ msym_bunch_index = 0;
+ new -> next = msym_bunch;
+ msym_bunch = new;
+ }
+ msymbol = &msym_bunch -> contents[msym_bunch_index];
+ SYMBOL_NAME (msymbol) = (char *) name;
+ SYMBOL_INIT_LANGUAGE_SPECIFIC (msymbol, language_unknown);
+ SYMBOL_VALUE_ADDRESS (msymbol) = address;
+ SYMBOL_SECTION (msymbol) = -1;
+ MSYMBOL_TYPE (msymbol) = ms_type;
+ /* FIXME: This info, if it remains, needs its own field. */
+ MSYMBOL_INFO (msymbol) = NULL; /* FIXME! */
+ msym_bunch_index++;
+ msym_count++;
+}
+
+/* FIXME: Why don't we just combine this function with the one above
+ and pass it a NULL info pointer value if info is not needed? */
+
+void
+prim_record_minimal_symbol_and_info (name, address, ms_type, info, section)
+ const char *name;
+ CORE_ADDR address;
+ enum minimal_symbol_type ms_type;
+ char *info;
+ int section;
+{
+ register struct msym_bunch *new;
+ register struct minimal_symbol *msymbol;
+
+ if (msym_bunch_index == BUNCH_SIZE)
+ {
+ new = (struct msym_bunch *) xmalloc (sizeof (struct msym_bunch));
+ msym_bunch_index = 0;
+ new -> next = msym_bunch;
+ msym_bunch = new;
+ }
+ msymbol = &msym_bunch -> contents[msym_bunch_index];
+ SYMBOL_NAME (msymbol) = (char *) name;
+ SYMBOL_INIT_LANGUAGE_SPECIFIC (msymbol, language_unknown);
+ SYMBOL_VALUE_ADDRESS (msymbol) = address;
+ SYMBOL_SECTION (msymbol) = section;
+ MSYMBOL_TYPE (msymbol) = ms_type;
+ /* FIXME: This info, if it remains, needs its own field. */
+ MSYMBOL_INFO (msymbol) = info; /* FIXME! */
+ msym_bunch_index++;
+ msym_count++;
+}
+
+/* Compare two minimal symbols by address and return a signed result based
+ on unsigned comparisons, so that we sort into unsigned numeric order. */
+
+static int
+compare_minimal_symbols (fn1p, fn2p)
+ const PTR fn1p;
+ const PTR fn2p;
+{
+ register const struct minimal_symbol *fn1;
+ register const struct minimal_symbol *fn2;
+
+ fn1 = (const struct minimal_symbol *) fn1p;
+ fn2 = (const struct minimal_symbol *) fn2p;
+
+ if (SYMBOL_VALUE_ADDRESS (fn1) < SYMBOL_VALUE_ADDRESS (fn2))
+ {
+ return (-1);
+ }
+ else if (SYMBOL_VALUE_ADDRESS (fn1) > SYMBOL_VALUE_ADDRESS (fn2))
+ {
+ return (1);
+ }
+ else
+ {
+ return (0);
+ }
+}
+
+/* Discard the currently collected minimal symbols, if any. If we wish
+ to save them for later use, we must have already copied them somewhere
+ else before calling this function.
+
+ FIXME: We could allocate the minimal symbol bunches on their own
+ obstack and then simply blow the obstack away when we are done with
+ it. Is it worth the extra trouble though? */
+
+/* ARGSUSED */
+void
+discard_minimal_symbols (foo)
+ int foo;
+{
+ register struct msym_bunch *next;
+
+ while (msym_bunch != NULL)
+ {
+ next = msym_bunch -> next;
+ free ((PTR)msym_bunch);
+ msym_bunch = next;
+ }
+}
+
+/* Compact duplicate entries out of a minimal symbol table by walking
+ through the table and compacting out entries with duplicate addresses
+ and matching names. Return the number of entries remaining.
+
+ On entry, the table resides between msymbol[0] and msymbol[mcount].
+ On exit, it resides between msymbol[0] and msymbol[result_count].
+
+ When files contain multiple sources of symbol information, it is
+ possible for the minimal symbol table to contain many duplicate entries.
+ As an example, SVR4 systems use ELF formatted object files, which
+ usually contain at least two different types of symbol tables (a
+ standard ELF one and a smaller dynamic linking table), as well as
+ DWARF debugging information for files compiled with -g.
+
+ Without compacting, the minimal symbol table for gdb itself contains
+ over a 1000 duplicates, about a third of the total table size. Aside
+ from the potential trap of not noticing that two successive entries
+ identify the same location, this duplication impacts the time required
+ to linearly scan the table, which is done in a number of places. So we
+ just do one linear scan here and toss out the duplicates.
+
+ Note that we are not concerned here about recovering the space that
+ is potentially freed up, because the strings themselves are allocated
+ on the symbol_obstack, and will get automatically freed when the symbol
+ table is freed. The caller can free up the unused minimal symbols at
+ the end of the compacted region if their allocation strategy allows it.
+
+ Also note we only go up to the next to last entry within the loop
+ and then copy the last entry explicitly after the loop terminates.
+
+ Since the different sources of information for each symbol may
+ have different levels of "completeness", we may have duplicates
+ that have one entry with type "mst_unknown" and the other with a
+ known type. So if the one we are leaving alone has type mst_unknown,
+ overwrite its type with the type from the one we are compacting out. */
+
+static int
+compact_minimal_symbols (msymbol, mcount)
+ struct minimal_symbol *msymbol;
+ int mcount;
+{
+ struct minimal_symbol *copyfrom;
+ struct minimal_symbol *copyto;
+
+ if (mcount > 0)
+ {
+ copyfrom = copyto = msymbol;
+ while (copyfrom < msymbol + mcount - 1)
+ {
+ if (SYMBOL_VALUE_ADDRESS (copyfrom) ==
+ SYMBOL_VALUE_ADDRESS ((copyfrom + 1)) &&
+ (STREQ (SYMBOL_NAME (copyfrom), SYMBOL_NAME ((copyfrom + 1)))))
+ {
+ if (MSYMBOL_TYPE((copyfrom + 1)) == mst_unknown)
+ {
+ MSYMBOL_TYPE ((copyfrom + 1)) = MSYMBOL_TYPE (copyfrom);
+ }
+ copyfrom++;
+ }
+ else
+ {
+ *copyto++ = *copyfrom++;
+ }
+ }
+ *copyto++ = *copyfrom++;
+ mcount = copyto - msymbol;
+ }
+ return (mcount);
+}
+
+/* Add the minimal symbols in the existing bunches to the objfile's official
+ minimal symbol table. In most cases there is no minimal symbol table yet
+ for this objfile, and the existing bunches are used to create one. Once
+ in a while (for shared libraries for example), we add symbols (e.g. common
+ symbols) to an existing objfile.
+
+ Because of the way minimal symbols are collected, we generally have no way
+ of knowing what source language applies to any particular minimal symbol.
+ Specifically, we have no way of knowing if the minimal symbol comes from a
+ C++ compilation unit or not. So for the sake of supporting cached
+ demangled C++ names, we have no choice but to try and demangle each new one
+ that comes in. If the demangling succeeds, then we assume it is a C++
+ symbol and set the symbol's language and demangled name fields
+ appropriately. Note that in order to avoid unnecessary demanglings, and
+ allocating obstack space that subsequently can't be freed for the demangled
+ names, we mark all newly added symbols with language_auto. After
+ compaction of the minimal symbols, we go back and scan the entire minimal
+ symbol table looking for these new symbols. For each new symbol we attempt
+ to demangle it, and if successful, record it as a language_cplus symbol
+ and cache the demangled form on the symbol obstack. Symbols which don't
+ demangle are marked as language_unknown symbols, which inhibits future
+ attempts to demangle them if we later add more minimal symbols. */
+
+void
+install_minimal_symbols (objfile)
+ struct objfile *objfile;
+{
+ register int bindex;
+ register int mcount;
+ register struct msym_bunch *bunch;
+ register struct minimal_symbol *msymbols;
+ int alloc_count;
+ register char leading_char;
+
+ if (msym_count > 0)
+ {
+ /* Allocate enough space in the obstack, into which we will gather the
+ bunches of new and existing minimal symbols, sort them, and then
+ compact out the duplicate entries. Once we have a final table,
+ we will give back the excess space. */
+
+ alloc_count = msym_count + objfile->minimal_symbol_count + 1;
+ obstack_blank (&objfile->symbol_obstack,
+ alloc_count * sizeof (struct minimal_symbol));
+ msymbols = (struct minimal_symbol *)
+ obstack_base (&objfile->symbol_obstack);
+
+ /* Copy in the existing minimal symbols, if there are any. */
+
+ if (objfile->minimal_symbol_count)
+ memcpy ((char *)msymbols, (char *)objfile->msymbols,
+ objfile->minimal_symbol_count * sizeof (struct minimal_symbol));
+
+ /* Walk through the list of minimal symbol bunches, adding each symbol
+ to the new contiguous array of symbols. Note that we start with the
+ current, possibly partially filled bunch (thus we use the current
+ msym_bunch_index for the first bunch we copy over), and thereafter
+ each bunch is full. */
+
+ mcount = objfile->minimal_symbol_count;
+ leading_char = bfd_get_symbol_leading_char (objfile->obfd);
+
+ for (bunch = msym_bunch; bunch != NULL; bunch = bunch -> next)
+ {
+ for (bindex = 0; bindex < msym_bunch_index; bindex++, mcount++)
+ {
+ msymbols[mcount] = bunch -> contents[bindex];
+ SYMBOL_LANGUAGE (&msymbols[mcount]) = language_auto;
+ if (SYMBOL_NAME (&msymbols[mcount])[0] == leading_char)
+ {
+ SYMBOL_NAME(&msymbols[mcount])++;
+ }
+ }
+ msym_bunch_index = BUNCH_SIZE;
+ }
+
+ /* Sort the minimal symbols by address. */
+
+ qsort (msymbols, mcount, sizeof (struct minimal_symbol),
+ compare_minimal_symbols);
+
+ /* Compact out any duplicates, and free up whatever space we are
+ no longer using. */
+
+ mcount = compact_minimal_symbols (msymbols, mcount);
+
+ obstack_blank (&objfile->symbol_obstack,
+ (mcount + 1 - alloc_count) * sizeof (struct minimal_symbol));
+ msymbols = (struct minimal_symbol *)
+ obstack_finish (&objfile->symbol_obstack);
+
+ /* We also terminate the minimal symbol table with a "null symbol",
+ which is *not* included in the size of the table. This makes it
+ easier to find the end of the table when we are handed a pointer
+ to some symbol in the middle of it. Zero out the fields in the
+ "null symbol" allocated at the end of the array. Note that the
+ symbol count does *not* include this null symbol, which is why it
+ is indexed by mcount and not mcount-1. */
+
+ SYMBOL_NAME (&msymbols[mcount]) = NULL;
+ SYMBOL_VALUE_ADDRESS (&msymbols[mcount]) = 0;
+ MSYMBOL_INFO (&msymbols[mcount]) = NULL;
+ MSYMBOL_TYPE (&msymbols[mcount]) = mst_unknown;
+ SYMBOL_INIT_LANGUAGE_SPECIFIC (&msymbols[mcount], language_unknown);
+
+ /* Attach the minimal symbol table to the specified objfile.
+ The strings themselves are also located in the symbol_obstack
+ of this objfile. */
+
+ objfile -> minimal_symbol_count = mcount;
+ objfile -> msymbols = msymbols;
+
+ /* Now walk through all the minimal symbols, selecting the newly added
+ ones and attempting to cache their C++ demangled names. */
+
+ for ( ; mcount-- > 0 ; msymbols++)
+ {
+ SYMBOL_INIT_DEMANGLED_NAME (msymbols, &objfile->symbol_obstack);
+ }
+ }
+}
+
diff --git a/gnu/usr.bin/gdb/gdb/mipsread.c b/gnu/usr.bin/gdb/gdb/mipsread.c
new file mode 100644
index 0000000..199092b
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/mipsread.c
@@ -0,0 +1,3653 @@
+/* Read a symbol table in MIPS' format (Third-Eye).
+ Copyright 1986, 1987, 1989, 1990, 1991, 1992, 1993 Free Software
+ Foundation, Inc.
+ Contributed by Alessandro Forin (af@cs.cmu.edu) at CMU. Major work
+ by Per Bothner, John Gilmore and Ian Lance Taylor at Cygnus Support.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This module provides three functions: mipscoff_symfile_init,
+ which initializes to read a symbol file; mipscoff_new_init, which
+ discards existing cached information when all symbols are being
+ discarded; and mipscoff_symfile_read, which reads a symbol table
+ from a file.
+
+ mipscoff_symfile_read only does the minimum work necessary for letting the
+ user "name" things symbolically; it does not read the entire symtab.
+ Instead, it reads the external and static symbols and puts them in partial
+ symbol tables. When more extensive information is requested of a
+ file, the corresponding partial symbol table is mutated into a full
+ fledged symbol table by going back and reading the symbols
+ for real. mipscoff_psymtab_to_symtab() is called indirectly through
+ a pointer in the psymtab to do this.
+
+ ECOFF symbol tables are mostly written in the byte order of the
+ target machine. However, one section of the table (the auxiliary
+ symbol information) is written in the host byte order. There is a
+ bit in the other symbol info which describes which host byte order
+ was used. ECOFF thereby takes the trophy from Intel `b.out' for
+ the most brain-dead adaptation of a file format to byte order.
+
+ This module can read all four of the known byte-order combinations,
+ on any type of host. */
+
+#include "defs.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "gdbcore.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "obstack.h"
+#include "buildsym.h"
+#include "stabsread.h"
+#include "complaints.h"
+
+/* These are needed if the tm.h file does not contain the necessary
+ mips specific definitions. */
+
+#ifndef MIPS_EFI_SYMBOL_NAME
+#define MIPS_EFI_SYMBOL_NAME "__GDB_EFI_INFO__"
+#include "coff/sym.h"
+#include "coff/symconst.h"
+typedef struct mips_extra_func_info {
+ long numargs;
+ PDR pdr;
+} *mips_extra_func_info_t;
+#ifndef RA_REGNUM
+#define RA_REGNUM 0
+#endif
+#endif
+
+#ifdef USG
+#include <sys/types.h>
+#endif
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include "gdb-stabs.h"
+
+#include "bfd.h"
+
+#include "coff/internal.h"
+#include "coff/ecoff.h" /* COFF-like aspects of ecoff files */
+
+/* FIXME: coff/internal.h and aout/aout64.h both define N_ABS. We
+ want the definition from aout/aout64.h. */
+#undef N_ABS
+
+#include "libaout.h" /* Private BFD a.out information. */
+#include "aout/aout64.h"
+#include "aout/stab_gnu.h" /* STABS information */
+
+/* FIXME: libcoff.h and libaout.h both define a couple of macros. We
+ don't use them. */
+#undef exec_hdr
+#undef obj_sym_filepos
+
+#include "libcoff.h" /* Private BFD COFF information. */
+#include "libecoff.h" /* Private BFD ECOFF information. */
+
+#include "expression.h"
+#include "language.h" /* Needed inside partial-stab.h */
+
+/* Provide a default mapping from a ecoff register number to a gdb REGNUM. */
+#ifndef ECOFF_REG_TO_REGNUM
+#define ECOFF_REG_TO_REGNUM(num) (num)
+#endif
+
+/* Information is passed among various mipsread routines for accessing
+ symbol files. A pointer to this structure is kept in the sym_private
+ field of the objfile struct. */
+
+struct ecoff_symfile_info {
+ struct mips_pending **pending_list;
+};
+#define ECOFF_SYMFILE_INFO(o) ((struct ecoff_symfile_info *)((o)->sym_private))
+#define ECOFF_PENDING_LIST(o) (ECOFF_SYMFILE_INFO(o)->pending_list)
+
+
+/* Each partial symbol table entry contains a pointer to private data
+ for the read_symtab() function to use when expanding a partial
+ symbol table entry to a full symbol table entry.
+
+ For mipsread this structure contains the index of the FDR that this
+ psymtab represents and a pointer to the BFD that the psymtab was
+ created from. */
+
+#define PST_PRIVATE(p) ((struct symloc *)(p)->read_symtab_private)
+#define FDR_IDX(p) (PST_PRIVATE(p)->fdr_idx)
+#define CUR_BFD(p) (PST_PRIVATE(p)->cur_bfd)
+
+struct symloc
+{
+ int fdr_idx;
+ bfd *cur_bfd;
+ EXTR *extern_tab; /* Pointer to external symbols for this file. */
+ int extern_count; /* Size of extern_tab. */
+ enum language pst_language;
+};
+
+/* Things we import explicitly from other modules */
+
+extern int info_verbose;
+
+/* Various complaints about symbol reading that don't abort the process */
+
+struct complaint bad_file_number_complaint =
+{"bad file number %d", 0, 0};
+
+struct complaint index_complaint =
+{"bad aux index at symbol %s", 0, 0};
+
+struct complaint aux_index_complaint =
+{"bad proc end in aux found from symbol %s", 0, 0};
+
+struct complaint block_index_complaint =
+{"bad aux index at block symbol %s", 0, 0};
+
+struct complaint unknown_ext_complaint =
+{"unknown external symbol %s", 0, 0};
+
+struct complaint unknown_sym_complaint =
+{"unknown local symbol %s", 0, 0};
+
+struct complaint unknown_st_complaint =
+{"with type %d", 0, 0};
+
+struct complaint block_overflow_complaint =
+{"block containing %s overfilled", 0, 0};
+
+struct complaint basic_type_complaint =
+{"cannot map MIPS basic type 0x%x for %s", 0, 0};
+
+struct complaint unknown_type_qual_complaint =
+{"unknown type qualifier 0x%x", 0, 0};
+
+struct complaint array_index_type_complaint =
+{"illegal array index type for %s, assuming int", 0, 0};
+
+struct complaint bad_tag_guess_complaint =
+{"guessed tag type of %s incorrectly", 0, 0};
+
+struct complaint block_member_complaint =
+{"declaration block contains unhandled symbol type %d", 0, 0};
+
+struct complaint stEnd_complaint =
+{"stEnd with storage class %d not handled", 0, 0};
+
+struct complaint unknown_mips_symtype_complaint =
+{"unknown symbol type 0x%x", 0, 0};
+
+struct complaint stab_unknown_complaint =
+{"unknown stabs symbol %s", 0, 0};
+
+struct complaint pdr_for_nonsymbol_complaint =
+{"PDR for %s, but no symbol", 0, 0};
+
+struct complaint pdr_static_symbol_complaint =
+{"can't handle PDR for static proc at 0x%lx", 0, 0};
+
+struct complaint bad_setjmp_pdr_complaint =
+{"fixing bad setjmp PDR from libc", 0, 0};
+
+struct complaint bad_fbitfield_complaint =
+{"can't handle TIR fBitfield for %s", 0, 0};
+
+struct complaint bad_continued_complaint =
+{"illegal TIR continued for %s", 0, 0};
+
+struct complaint bad_rfd_entry_complaint =
+{"bad rfd entry for %s: file %d, index %d", 0, 0};
+
+struct complaint unexpected_type_code_complaint =
+{"unexpected type code for %s", 0, 0};
+
+struct complaint unable_to_cross_ref_complaint =
+{"unable to cross ref btTypedef for %s", 0, 0};
+
+struct complaint illegal_forward_tq0_complaint =
+{"illegal tq0 in forward typedef for %s", 0, 0};
+
+struct complaint illegal_forward_bt_complaint =
+{"illegal bt %d in forward typedef for %s", 0, 0};
+
+struct complaint bad_linetable_guess_complaint =
+{"guessed size of linetable for %s incorrectly", 0, 0};
+
+/* Macros and extra defs */
+
+/* Puns: hard to find whether -g was used and how */
+
+#define MIN_GLEVEL GLEVEL_0
+#define compare_glevel(a,b) \
+ (((a) == GLEVEL_3) ? ((b) < GLEVEL_3) : \
+ ((b) == GLEVEL_3) ? -1 : (int)((b) - (a)))
+
+/* Things that really are local to this module */
+
+/* Remember what we deduced to be the source language of this psymtab. */
+
+static enum language psymtab_language = language_unknown;
+
+/* Current BFD. */
+
+static bfd *cur_bfd;
+
+/* Pointer to current file decriptor record, and its index */
+
+static FDR *cur_fdr;
+static int cur_fd;
+
+/* Index of current symbol */
+
+static int cur_sdx;
+
+/* Note how much "debuggable" this image is. We would like
+ to see at least one FDR with full symbols */
+
+static max_gdbinfo;
+static max_glevel;
+
+/* When examining .o files, report on undefined symbols */
+
+static int n_undef_symbols, n_undef_labels, n_undef_vars, n_undef_procs;
+
+/* Pseudo symbol to use when putting stabs into the symbol table. */
+
+static char stabs_symbol[] = STABS_SYMBOL;
+
+/* Extra builtin types */
+
+struct type *builtin_type_complex;
+struct type *builtin_type_double_complex;
+struct type *builtin_type_fixed_dec;
+struct type *builtin_type_float_dec;
+struct type *builtin_type_string;
+
+/* Forward declarations */
+
+static void
+read_mips_symtab PARAMS ((struct objfile *, struct section_offsets *));
+
+static void
+read_the_mips_symtab PARAMS ((bfd *));
+
+static int
+upgrade_type PARAMS ((int, struct type **, int, union aux_ext *, int, char *));
+
+static void
+parse_partial_symbols PARAMS ((struct objfile *,
+ struct section_offsets *));
+
+static int
+cross_ref PARAMS ((int, union aux_ext *, struct type **, enum type_code,
+ char **, int, char *));
+
+static void
+fixup_sigtramp PARAMS ((void));
+
+static struct symbol *
+new_symbol PARAMS ((char *));
+
+static struct type *
+new_type PARAMS ((char *));
+
+static struct block *
+new_block PARAMS ((int));
+
+static struct symtab *
+new_symtab PARAMS ((char *, int, int, struct objfile *));
+
+static struct linetable *
+new_linetable PARAMS ((int));
+
+static struct blockvector *
+new_bvect PARAMS ((int));
+
+static int
+parse_symbol PARAMS ((SYMR *, union aux_ext *, char *, int));
+
+static struct type *
+parse_type PARAMS ((int, union aux_ext *, unsigned int, int *, int, char *));
+
+static struct symbol *
+mylookup_symbol PARAMS ((char *, struct block *, enum namespace,
+ enum address_class));
+
+static struct block *
+shrink_block PARAMS ((struct block *, struct symtab *));
+
+static PTR
+xzalloc PARAMS ((unsigned int));
+
+static void
+sort_blocks PARAMS ((struct symtab *));
+
+static int
+compare_blocks PARAMS ((const void *, const void *));
+
+static struct partial_symtab *
+new_psymtab PARAMS ((char *, struct objfile *));
+
+static void
+psymtab_to_symtab_1 PARAMS ((struct partial_symtab *, char *));
+
+static void
+add_block PARAMS ((struct block *, struct symtab *));
+
+static void
+add_symbol PARAMS ((struct symbol *, struct block *));
+
+static int
+add_line PARAMS ((struct linetable *, int, CORE_ADDR, int));
+
+static struct linetable *
+shrink_linetable PARAMS ((struct linetable *));
+
+static char *
+mips_next_symbol_text PARAMS ((void));
+
+/* Things we export to other modules */
+
+/* Address bounds for the signal trampoline in inferior, if any */
+/* FIXME: Nothing really seems to use this. Why is it here? */
+
+CORE_ADDR sigtramp_address, sigtramp_end;
+
+static void
+mipscoff_new_init (ignore)
+ struct objfile *ignore;
+{
+ sigtramp_address = 0;
+ stabsread_new_init ();
+ buildsym_new_init ();
+}
+
+static void
+mipscoff_symfile_init (objfile)
+ struct objfile *objfile;
+{
+ if (objfile->sym_private != NULL)
+ {
+ mfree (objfile->md, objfile->sym_private);
+ }
+ objfile->sym_private = (PTR)
+ xmmalloc (objfile->md, sizeof (struct ecoff_symfile_info));
+}
+
+static void
+mipscoff_symfile_read (objfile, section_offsets, mainline)
+ struct objfile *objfile;
+ struct section_offsets *section_offsets;
+ int mainline;
+{
+ struct cleanup * back_to;
+
+ init_minimal_symbol_collection ();
+ back_to = make_cleanup (discard_minimal_symbols, 0);
+
+ /* Now that the executable file is positioned at symbol table,
+ process it and define symbols accordingly. */
+
+ read_mips_symtab (objfile, section_offsets);
+
+ /* Install any minimal symbols that have been collected as the current
+ minimal symbols for this objfile. */
+
+ install_minimal_symbols (objfile);
+
+ do_cleanups (back_to);
+}
+
+/* Perform any local cleanups required when we are done with a particular
+ objfile. I.E, we are in the process of discarding all symbol information
+ for an objfile, freeing up all memory held for it, and unlinking the
+ objfile struct from the global list of known objfiles. */
+
+static void
+mipscoff_symfile_finish (objfile)
+ struct objfile *objfile;
+{
+ if (objfile->sym_private != NULL)
+ {
+ mfree (objfile->md, objfile->sym_private);
+ }
+
+ cur_bfd = 0;
+}
+
+/* Allocate zeroed memory */
+
+static PTR
+xzalloc (size)
+ unsigned int size;
+{
+ PTR p = xmalloc (size);
+
+ memset (p, 0, size);
+ return p;
+}
+
+/* Exported procedure: Builds a symtab from the PST partial one.
+ Restores the environment in effect when PST was created, delegates
+ most of the work to an ancillary procedure, and sorts
+ and reorders the symtab list at the end */
+
+static void
+mipscoff_psymtab_to_symtab (pst)
+ struct partial_symtab *pst;
+{
+
+ if (!pst)
+ return;
+
+ if (info_verbose)
+ {
+ printf_filtered ("Reading in symbols for %s...", pst->filename);
+ fflush (stdout);
+ }
+
+ next_symbol_text_func = mips_next_symbol_text;
+
+ psymtab_to_symtab_1 (pst, pst->filename);
+
+ /* Match with global symbols. This only needs to be done once,
+ after all of the symtabs and dependencies have been read in. */
+ scan_file_globals (pst->objfile);
+
+ if (info_verbose)
+ printf_filtered ("done.\n");
+}
+
+/* Exported procedure: Is PC in the signal trampoline code */
+
+int
+in_sigtramp (pc, ignore)
+ CORE_ADDR pc;
+ char *ignore; /* function name */
+{
+ if (sigtramp_address == 0)
+ fixup_sigtramp ();
+ return (pc >= sigtramp_address && pc < sigtramp_end);
+}
+
+/* File-level interface functions */
+
+/* Read the symtab information from file ABFD into memory. */
+
+static void
+read_the_mips_symtab (abfd)
+ bfd *abfd;
+{
+ if (ecoff_slurp_symbolic_info (abfd) == false)
+ error ("Error reading symbol table: %s", bfd_errmsg (bfd_error));
+}
+
+/* Find a file descriptor given its index RF relative to a file CF */
+
+static FDR *
+get_rfd (cf, rf)
+ int cf, rf;
+{
+ FDR *fdrs;
+ register FDR *f;
+ RFDT rfd;
+
+ fdrs = ecoff_data (cur_bfd)->fdr;
+ f = fdrs + cf;
+ /* Object files do not have the RFD table, all refs are absolute */
+ if (f->rfdBase == 0)
+ return fdrs + rf;
+ (*ecoff_backend (cur_bfd)->swap_rfd_in)
+ (cur_bfd,
+ ((char *) ecoff_data (cur_bfd)->external_rfd
+ + (f->rfdBase + rf) * ecoff_backend (cur_bfd)->external_rfd_size),
+ &rfd);
+ return fdrs + rfd;
+}
+
+/* Return a safer print NAME for a file descriptor */
+
+static char *
+fdr_name (f)
+ FDR *f;
+{
+ if (f->rss == -1)
+ return "<stripped file>";
+ if (f->rss == 0)
+ return "<NFY>";
+ return ecoff_data (cur_bfd)->ss + f->issBase + f->rss;
+}
+
+
+/* Read in and parse the symtab of the file OBJFILE. Symbols from
+ different sections are relocated via the SECTION_OFFSETS. */
+
+static void
+read_mips_symtab (objfile, section_offsets)
+ struct objfile *objfile;
+ struct section_offsets *section_offsets;
+{
+ cur_bfd = objfile->obfd;
+
+ read_the_mips_symtab (objfile->obfd);
+
+ parse_partial_symbols (objfile, section_offsets);
+
+#if 0
+ /* Check to make sure file was compiled with -g. If not, warn the
+ user of this limitation. */
+ if (compare_glevel (max_glevel, GLEVEL_2) < 0)
+ {
+ if (max_gdbinfo == 0)
+ printf ("\n%s not compiled with -g, debugging support is limited.\n",
+ objfile->name);
+ printf ("You should compile with -g2 or -g3 for best debugging support.\n");
+ fflush (stdout);
+ }
+#endif
+}
+
+/* Local utilities */
+
+/* Map of FDR indexes to partial symtabs */
+
+struct pst_map
+{
+ struct partial_symtab *pst; /* the psymtab proper */
+ long n_globals; /* exported globals (external symbols) */
+ long globals_offset; /* cumulative */
+};
+
+
+/* Utility stack, used to nest procedures and blocks properly.
+ It is a doubly linked list, to avoid too many alloc/free.
+ Since we might need it quite a few times it is NOT deallocated
+ after use. */
+
+static struct parse_stack
+{
+ struct parse_stack *next, *prev;
+ struct symtab *cur_st; /* Current symtab. */
+ struct block *cur_block; /* Block in it. */
+ int blocktype; /* What are we parsing. */
+ int maxsyms; /* Max symbols in this block. */
+ struct type *cur_type; /* Type we parse fields for. */
+ int cur_field; /* Field number in cur_type. */
+ CORE_ADDR procadr; /* Start addres of this procedure */
+ int numargs; /* Its argument count */
+}
+
+ *top_stack; /* Top stack ptr */
+
+
+/* Enter a new lexical context */
+
+static void
+push_parse_stack ()
+{
+ struct parse_stack *new;
+
+ /* Reuse frames if possible */
+ if (top_stack && top_stack->prev)
+ new = top_stack->prev;
+ else
+ new = (struct parse_stack *) xzalloc (sizeof (struct parse_stack));
+ /* Initialize new frame with previous content */
+ if (top_stack)
+ {
+ register struct parse_stack *prev = new->prev;
+
+ *new = *top_stack;
+ top_stack->prev = new;
+ new->prev = prev;
+ new->next = top_stack;
+ }
+ top_stack = new;
+}
+
+/* Exit a lexical context */
+
+static void
+pop_parse_stack ()
+{
+ if (!top_stack)
+ return;
+ if (top_stack->next)
+ top_stack = top_stack->next;
+}
+
+
+/* Cross-references might be to things we haven't looked at
+ yet, e.g. type references. To avoid too many type
+ duplications we keep a quick fixup table, an array
+ of lists of references indexed by file descriptor */
+
+struct mips_pending
+{
+ struct mips_pending *next; /* link */
+ char *s; /* the unswapped symbol */
+ struct type *t; /* its partial type descriptor */
+};
+
+
+/* Check whether we already saw symbol SH in file FH */
+
+static struct mips_pending *
+is_pending_symbol (fh, sh)
+ FDR *fh;
+ char *sh;
+{
+ int f_idx = fh - ecoff_data (cur_bfd)->fdr;
+ register struct mips_pending *p;
+ struct mips_pending **pending_list = ECOFF_PENDING_LIST (current_objfile);
+
+ /* Linear search is ok, list is typically no more than 10 deep */
+ for (p = pending_list[f_idx]; p; p = p->next)
+ if (p->s == sh)
+ break;
+ return p;
+}
+
+/* Add a new symbol SH of type T */
+
+static void
+add_pending (fh, sh, t)
+ FDR *fh;
+ char *sh;
+ struct type *t;
+{
+ int f_idx = fh - ecoff_data (cur_bfd)->fdr;
+ struct mips_pending *p = is_pending_symbol (fh, sh);
+
+ /* Make sure we do not make duplicates */
+ if (!p)
+ {
+ struct mips_pending **pending_list = ECOFF_PENDING_LIST (current_objfile);
+
+ p = ((struct mips_pending *)
+ obstack_alloc (&current_objfile->psymbol_obstack,
+ sizeof (struct mips_pending)));
+ p->s = sh;
+ p->t = t;
+ p->next = pending_list[f_idx];
+ pending_list[f_idx] = p;
+ }
+}
+
+
+/* Parsing Routines proper. */
+
+/* Parse a single symbol. Mostly just make up a GDB symbol for it.
+ For blocks, procedures and types we open a new lexical context.
+ This is basically just a big switch on the symbol's type. Argument
+ AX is the base pointer of aux symbols for this file (fh->iauxBase).
+ EXT_SH points to the unswapped symbol, which is needed for struct,
+ union, etc., types; it is NULL for an EXTR. BIGEND says whether
+ aux symbols are big-endian or little-endian. Return count of
+ SYMR's handled (normally one). */
+
+static int
+parse_symbol (sh, ax, ext_sh, bigend)
+ SYMR *sh;
+ union aux_ext *ax;
+ char *ext_sh;
+ int bigend;
+{
+ const bfd_size_type external_sym_size
+ = ecoff_backend (cur_bfd)->external_sym_size;
+ void (* const swap_sym_in) PARAMS ((bfd *, PTR, SYMR *)) =
+ ecoff_backend (cur_bfd)->swap_sym_in;
+ char *name;
+ struct symbol *s;
+ struct block *b;
+ struct mips_pending *pend;
+ struct type *t;
+ struct field *f;
+ int count = 1;
+ enum address_class class;
+ TIR tir;
+ long svalue = sh->value;
+ int bitsize;
+
+ if (ext_sh == (char *) NULL)
+ name = ecoff_data (cur_bfd)->ssext + sh->iss;
+ else
+ name = ecoff_data (cur_bfd)->ss + cur_fdr->issBase + sh->iss;
+
+ switch (sh->st)
+ {
+ case stNil:
+ break;
+
+ case stGlobal: /* external symbol, goes into global block */
+ class = LOC_STATIC;
+ b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (top_stack->cur_st),
+ GLOBAL_BLOCK);
+ s = new_symbol (name);
+ SYMBOL_VALUE_ADDRESS (s) = (CORE_ADDR) sh->value;
+ goto data;
+
+ case stStatic: /* static data, goes into current block. */
+ class = LOC_STATIC;
+ b = top_stack->cur_block;
+ s = new_symbol (name);
+ if (sh->sc == scCommon)
+ {
+ /* It is a FORTRAN common block. At least for SGI Fortran the
+ address is not in the symbol; we need to fix it later in
+ scan_file_globals. */
+ int bucket = hashname (SYMBOL_NAME (s));
+ SYMBOL_VALUE_CHAIN (s) = global_sym_chain[bucket];
+ global_sym_chain[bucket] = s;
+ }
+ else
+ SYMBOL_VALUE_ADDRESS (s) = (CORE_ADDR) sh->value;
+ goto data;
+
+ case stLocal: /* local variable, goes into current block */
+ if (sh->sc == scRegister)
+ {
+ class = LOC_REGISTER;
+ svalue = ECOFF_REG_TO_REGNUM (svalue);
+ }
+ else
+ class = LOC_LOCAL;
+ b = top_stack->cur_block;
+ s = new_symbol (name);
+ SYMBOL_VALUE (s) = svalue;
+
+ data: /* Common code for symbols describing data */
+ SYMBOL_NAMESPACE (s) = VAR_NAMESPACE;
+ SYMBOL_CLASS (s) = class;
+ add_symbol (s, b);
+
+ /* Type could be missing in a number of cases */
+ if (sh->sc == scUndefined || sh->sc == scNil ||
+ sh->index == 0xfffff)
+ SYMBOL_TYPE (s) = builtin_type_int; /* undefined? */
+ else
+ SYMBOL_TYPE (s) = parse_type (cur_fd, ax, sh->index, 0, bigend, name);
+ /* Value of a data symbol is its memory address */
+ break;
+
+ case stParam: /* arg to procedure, goes into current block */
+ max_gdbinfo++;
+ top_stack->numargs++;
+
+ /* Special GNU C++ name. */
+ if (name[0] == CPLUS_MARKER && name[1] == 't' && name[2] == 0)
+ name = "this"; /* FIXME, not alloc'd in obstack */
+ s = new_symbol (name);
+
+ SYMBOL_NAMESPACE (s) = VAR_NAMESPACE;
+ switch (sh->sc)
+ {
+ case scRegister:
+ /* Pass by value in register. */
+ SYMBOL_CLASS(s) = LOC_REGPARM;
+ svalue = ECOFF_REG_TO_REGNUM (svalue);
+ break;
+ case scVar:
+ /* Pass by reference on stack. */
+ SYMBOL_CLASS(s) = LOC_REF_ARG;
+ break;
+ case scVarRegister:
+ /* Pass by reference in register. */
+ SYMBOL_CLASS(s) = LOC_REGPARM_ADDR;
+ svalue = ECOFF_REG_TO_REGNUM (svalue);
+ break;
+ default:
+ /* Pass by value on stack. */
+ SYMBOL_CLASS(s) = LOC_ARG;
+ break;
+ }
+ SYMBOL_VALUE (s) = svalue;
+ SYMBOL_TYPE (s) = parse_type (cur_fd, ax, sh->index, 0, bigend, name);
+ add_symbol (s, top_stack->cur_block);
+#if 0
+ /* FIXME: This has not been tested. See dbxread.c */
+ /* Add the type of this parameter to the function/procedure
+ type of this block. */
+ add_param_to_type (&top_stack->cur_block->function->type, s);
+#endif
+ break;
+
+ case stLabel: /* label, goes into current block */
+ s = new_symbol (name);
+ SYMBOL_NAMESPACE (s) = VAR_NAMESPACE; /* so that it can be used */
+ SYMBOL_CLASS (s) = LOC_LABEL; /* but not misused */
+ SYMBOL_VALUE_ADDRESS (s) = (CORE_ADDR) sh->value;
+ SYMBOL_TYPE (s) = builtin_type_int;
+ add_symbol (s, top_stack->cur_block);
+ break;
+
+ case stProc: /* Procedure, usually goes into global block */
+ case stStaticProc: /* Static procedure, goes into current block */
+ s = new_symbol (name);
+ SYMBOL_NAMESPACE (s) = VAR_NAMESPACE;
+ SYMBOL_CLASS (s) = LOC_BLOCK;
+ /* Type of the return value */
+ if (sh->sc == scUndefined || sh->sc == scNil)
+ t = builtin_type_int;
+ else
+ t = parse_type (cur_fd, ax, sh->index + 1, 0, bigend, name);
+ b = top_stack->cur_block;
+ if (sh->st == stProc)
+ {
+ struct blockvector *bv = BLOCKVECTOR (top_stack->cur_st);
+ /* The next test should normally be true,
+ but provides a hook for nested functions
+ (which we don't want to make global). */
+ if (b == BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK))
+ b = BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK);
+ }
+ add_symbol (s, b);
+
+ /* Make a type for the procedure itself */
+#if 0
+ /* FIXME: This has not been tested yet! See dbxread.c */
+ /* Generate a template for the type of this function. The
+ types of the arguments will be added as we read the symbol
+ table. */
+ memcpy (lookup_function_type (t), SYMBOL_TYPE (s), sizeof (struct type));
+#else
+ SYMBOL_TYPE (s) = lookup_function_type (t);
+#endif
+
+ /* Create and enter a new lexical context */
+ b = new_block (top_stack->maxsyms);
+ SYMBOL_BLOCK_VALUE (s) = b;
+ BLOCK_FUNCTION (b) = s;
+ BLOCK_START (b) = BLOCK_END (b) = sh->value;
+ BLOCK_SUPERBLOCK (b) = top_stack->cur_block;
+ add_block (b, top_stack->cur_st);
+
+ /* Not if we only have partial info */
+ if (sh->sc == scUndefined || sh->sc == scNil)
+ break;
+
+ push_parse_stack ();
+ top_stack->cur_block = b;
+ top_stack->blocktype = sh->st;
+ top_stack->cur_type = SYMBOL_TYPE (s);
+ top_stack->cur_field = -1;
+ top_stack->procadr = sh->value;
+ top_stack->numargs = 0;
+ break;
+
+ /* Beginning of code for structure, union, and enum definitions.
+ They all share a common set of local variables, defined here. */
+ {
+ enum type_code type_code;
+ char *ext_tsym;
+ int nfields;
+ long max_value;
+ struct field *f;
+
+ case stStruct: /* Start a block defining a struct type */
+ type_code = TYPE_CODE_STRUCT;
+ goto structured_common;
+
+ case stUnion: /* Start a block defining a union type */
+ type_code = TYPE_CODE_UNION;
+ goto structured_common;
+
+ case stEnum: /* Start a block defining an enum type */
+ type_code = TYPE_CODE_ENUM;
+ goto structured_common;
+
+ case stBlock: /* Either a lexical block, or some type */
+ if (sh->sc != scInfo && sh->sc != scCommon)
+ goto case_stBlock_code; /* Lexical block */
+
+ type_code = TYPE_CODE_UNDEF; /* We have a type. */
+
+ /* Common code for handling struct, union, enum, and/or as-yet-
+ unknown-type blocks of info about structured data. `type_code'
+ has been set to the proper TYPE_CODE, if we know it. */
+ structured_common:
+ push_parse_stack ();
+ top_stack->blocktype = stBlock;
+
+ /* First count the number of fields and the highest value. */
+ nfields = 0;
+ max_value = 0;
+ for (ext_tsym = ext_sh + external_sym_size;
+ ;
+ ext_tsym += external_sym_size)
+ {
+ SYMR tsym;
+
+ (*swap_sym_in) (cur_bfd, ext_tsym, &tsym);
+
+ switch (tsym.st)
+ {
+ case stEnd:
+ goto end_of_fields;
+
+ case stMember:
+ if (nfields == 0 && type_code == TYPE_CODE_UNDEF)
+ /* If the type of the member is Nil (or Void),
+ without qualifiers, assume the tag is an
+ enumeration. */
+ if (tsym.index == indexNil)
+ type_code = TYPE_CODE_ENUM;
+ else
+ {
+ ecoff_swap_tir_in (bigend,
+ &ax[tsym.index].a_ti,
+ &tir);
+ if ((tir.bt == btNil || tir.bt == btVoid)
+ && tir.tq0 == tqNil)
+ type_code = TYPE_CODE_ENUM;
+ }
+ nfields++;
+ if (tsym.value > max_value)
+ max_value = tsym.value;
+ break;
+
+ case stBlock:
+ case stUnion:
+ case stEnum:
+ case stStruct:
+ {
+#if 0
+ /* This is a no-op; is it trying to tell us something
+ we should be checking? */
+ if (tsym.sc == scVariant); /*UNIMPLEMENTED*/
+#endif
+ if (tsym.index != 0)
+ {
+ /* This is something like a struct within a
+ struct. Skip over the fields of the inner
+ struct. The -1 is because the for loop will
+ increment ext_tsym. */
+ ext_tsym = ((char *) ecoff_data (cur_bfd)->external_sym
+ + ((cur_fdr->isymBase + tsym.index - 1)
+ * external_sym_size));
+ }
+ }
+ break;
+
+ case stTypedef:
+ /* mips cc puts out a typedef for struct x if it is not yet
+ defined when it encounters
+ struct y { struct x *xp; };
+ Just ignore it. */
+ break;
+
+ default:
+ complain (&block_member_complaint, tsym.st);
+ }
+ }
+ end_of_fields:;
+
+ /* In an stBlock, there is no way to distinguish structs,
+ unions, and enums at this point. This is a bug in the
+ original design (that has been fixed with the recent
+ addition of the stStruct, stUnion, and stEnum symbol
+ types.) The way you can tell is if/when you see a variable
+ or field of that type. In that case the variable's type
+ (in the AUX table) says if the type is struct, union, or
+ enum, and points back to the stBlock here. So you can
+ patch the tag kind up later - but only if there actually is
+ a variable or field of that type.
+
+ So until we know for sure, we will guess at this point.
+ The heuristic is:
+ If the first member has index==indexNil or a void type,
+ assume we have an enumeration.
+ Otherwise, if there is more than one member, and all
+ the members have offset 0, assume we have a union.
+ Otherwise, assume we have a struct.
+
+ The heuristic could guess wrong in the case of of an
+ enumeration with no members or a union with one (or zero)
+ members, or when all except the last field of a struct have
+ width zero. These are uncommon and/or illegal situations,
+ and in any case guessing wrong probably doesn't matter
+ much.
+
+ But if we later do find out we were wrong, we fixup the tag
+ kind. Members of an enumeration must be handled
+ differently from struct/union fields, and that is harder to
+ patch up, but luckily we shouldn't need to. (If there are
+ any enumeration members, we can tell for sure it's an enum
+ here.) */
+
+ if (type_code == TYPE_CODE_UNDEF)
+ if (nfields > 1 && max_value == 0)
+ type_code = TYPE_CODE_UNION;
+ else
+ type_code = TYPE_CODE_STRUCT;
+
+ /* Create a new type or use the pending type. */
+ pend = is_pending_symbol (cur_fdr, ext_sh);
+ if (pend == (struct mips_pending *) NULL)
+ {
+ t = new_type (NULL);
+ add_pending (cur_fdr, ext_sh, t);
+ }
+ else
+ t = pend->t;
+
+ /* Alpha cc unnamed structs do not get a tag name. */
+ if (sh->iss == 0)
+ TYPE_TAG_NAME (t) = NULL;
+ else
+ TYPE_TAG_NAME (t) = obconcat (&current_objfile->symbol_obstack,
+ "", "", name);
+
+ TYPE_CODE (t) = type_code;
+ TYPE_LENGTH (t) = sh->value;
+ TYPE_NFIELDS (t) = nfields;
+ TYPE_FIELDS (t) = f = ((struct field *)
+ TYPE_ALLOC (t,
+ nfields * sizeof (struct field)));
+
+ if (type_code == TYPE_CODE_ENUM)
+ {
+ /* This is a non-empty enum. */
+ for (ext_tsym = ext_sh + external_sym_size;
+ ;
+ ext_tsym += external_sym_size)
+ {
+ SYMR tsym;
+ struct symbol *enum_sym;
+
+ (*swap_sym_in) (cur_bfd, ext_tsym, &tsym);
+
+ if (tsym.st != stMember)
+ break;
+
+ f->bitpos = tsym.value;
+ f->type = t;
+ f->name = (ecoff_data (cur_bfd)->ss
+ + cur_fdr->issBase
+ + tsym.iss);
+ f->bitsize = 0;
+
+ enum_sym = ((struct symbol *)
+ obstack_alloc (&current_objfile->symbol_obstack,
+ sizeof (struct symbol)));
+ memset ((PTR) enum_sym, 0, sizeof (struct symbol));
+ SYMBOL_NAME (enum_sym) = f->name;
+ SYMBOL_CLASS (enum_sym) = LOC_CONST;
+ SYMBOL_TYPE (enum_sym) = t;
+ SYMBOL_NAMESPACE (enum_sym) = VAR_NAMESPACE;
+ SYMBOL_VALUE (enum_sym) = tsym.value;
+ add_symbol (enum_sym, top_stack->cur_block);
+
+ /* Skip the stMembers that we've handled. */
+ count++;
+ f++;
+ }
+ }
+ /* make this the current type */
+ top_stack->cur_type = t;
+ top_stack->cur_field = 0;
+
+ /* Do not create a symbol for alpha cc unnamed structs. */
+ if (sh->iss == 0)
+ break;
+ s = new_symbol (name);
+ SYMBOL_NAMESPACE (s) = STRUCT_NAMESPACE;
+ SYMBOL_CLASS (s) = LOC_TYPEDEF;
+ SYMBOL_VALUE (s) = 0;
+ SYMBOL_TYPE (s) = t;
+
+ /* gcc puts out an empty struct for an opaque struct definitions. */
+ if (TYPE_NFIELDS (t) == 0)
+ {
+ TYPE_FLAGS (t) |= TYPE_FLAG_STUB;
+ SYMBOL_NAMESPACE (s) = VAR_NAMESPACE;
+ }
+ add_symbol (s, top_stack->cur_block);
+ break;
+
+ /* End of local variables shared by struct, union, enum, and
+ block (as yet unknown struct/union/enum) processing. */
+ }
+
+ case_stBlock_code:
+ /* beginnning of (code) block. Value of symbol
+ is the displacement from procedure start */
+ push_parse_stack ();
+ top_stack->blocktype = stBlock;
+ b = new_block (top_stack->maxsyms);
+ BLOCK_START (b) = sh->value + top_stack->procadr;
+ BLOCK_SUPERBLOCK (b) = top_stack->cur_block;
+ top_stack->cur_block = b;
+ add_block (b, top_stack->cur_st);
+ break;
+
+ case stEnd: /* end (of anything) */
+ if (sh->sc == scInfo || sh->sc == scCommon)
+ {
+ /* Finished with type */
+ top_stack->cur_type = 0;
+ }
+ else if (sh->sc == scText &&
+ (top_stack->blocktype == stProc ||
+ top_stack->blocktype == stStaticProc))
+ {
+ /* Finished with procedure */
+ struct blockvector *bv = BLOCKVECTOR (top_stack->cur_st);
+ struct mips_extra_func_info *e;
+ struct block *b;
+ int i;
+
+ BLOCK_END (top_stack->cur_block) += sh->value; /* size */
+
+ /* Make up special symbol to contain procedure specific info */
+ s = new_symbol (MIPS_EFI_SYMBOL_NAME);
+ SYMBOL_NAMESPACE (s) = LABEL_NAMESPACE;
+ SYMBOL_CLASS (s) = LOC_CONST;
+ SYMBOL_TYPE (s) = builtin_type_void;
+ e = ((struct mips_extra_func_info *)
+ obstack_alloc (&current_objfile->symbol_obstack,
+ sizeof (struct mips_extra_func_info)));
+ SYMBOL_VALUE (s) = (long) e;
+ e->numargs = top_stack->numargs;
+ add_symbol (s, top_stack->cur_block);
+
+ /* Reallocate symbols, saving memory */
+ b = shrink_block (top_stack->cur_block, top_stack->cur_st);
+
+ /* f77 emits proc-level with address bounds==[0,0],
+ So look for such child blocks, and patch them. */
+ for (i = 0; i < BLOCKVECTOR_NBLOCKS (bv); i++)
+ {
+ struct block *b_bad = BLOCKVECTOR_BLOCK (bv, i);
+ if (BLOCK_SUPERBLOCK (b_bad) == b
+ && BLOCK_START (b_bad) == top_stack->procadr
+ && BLOCK_END (b_bad) == top_stack->procadr)
+ {
+ BLOCK_START (b_bad) = BLOCK_START (b);
+ BLOCK_END (b_bad) = BLOCK_END (b);
+ }
+ }
+ }
+ else if (sh->sc == scText && top_stack->blocktype == stBlock)
+ {
+ /* End of (code) block. The value of the symbol is the
+ displacement from the procedure`s start address of the
+ end of this block. */
+ BLOCK_END (top_stack->cur_block) = sh->value + top_stack->procadr;
+ shrink_block (top_stack->cur_block, top_stack->cur_st);
+ }
+ else if (sh->sc == scText && top_stack->blocktype == stFile)
+ {
+ /* End of file. Pop parse stack and ignore. Higher
+ level code deals with this. */
+ ;
+ }
+ else
+ complain (&stEnd_complaint, sh->sc);
+
+ pop_parse_stack (); /* restore previous lexical context */
+ break;
+
+ case stMember: /* member of struct or union */
+ f = &TYPE_FIELDS (top_stack->cur_type)[top_stack->cur_field++];
+ f->name = name;
+ f->bitpos = sh->value;
+ bitsize = 0;
+ f->type = parse_type (cur_fd, ax, sh->index, &bitsize, bigend, name);
+ f->bitsize = bitsize;
+ break;
+
+ case stTypedef: /* type definition */
+ /* Typedefs for forward declarations and opaque structs from alpha cc
+ are handled by cross_ref, skip them. */
+ if (sh->iss == 0)
+ break;
+
+ /* Parse the type or use the pending type. */
+ pend = is_pending_symbol (cur_fdr, ext_sh);
+ if (pend == (struct mips_pending *) NULL)
+ {
+ t = parse_type (cur_fd, ax, sh->index, (int *)NULL, bigend, name);
+ add_pending (cur_fdr, ext_sh, t);
+ }
+ else
+ t = pend->t;
+
+ /* mips cc puts out a typedef with the name of the struct for forward
+ declarations. These should not go into the symbol table and
+ TYPE_NAME should not be set for them.
+ They can't be distinguished from an intentional typedef to
+ the same name however:
+ x.h:
+ struct x { int ix; int jx; };
+ struct xx;
+ x.c:
+ typedef struct x x;
+ struct xx {int ixx; int jxx; };
+ generates a cross referencing stTypedef for x and xx.
+ The user visible effect of this is that the type of a pointer
+ to struct foo sometimes is given as `foo *' instead of `struct foo *'.
+ The problem is fixed with alpha cc. */
+
+ s = new_symbol (name);
+ SYMBOL_NAMESPACE (s) = VAR_NAMESPACE;
+ SYMBOL_CLASS (s) = LOC_TYPEDEF;
+ SYMBOL_BLOCK_VALUE (s) = top_stack->cur_block;
+ SYMBOL_TYPE (s) = t;
+ add_symbol (s, top_stack->cur_block);
+
+ /* Incomplete definitions of structs should not get a name. */
+ if (TYPE_NAME (SYMBOL_TYPE (s)) == NULL
+ && (TYPE_NFIELDS (SYMBOL_TYPE (s)) != 0
+ || (TYPE_CODE (SYMBOL_TYPE (s)) != TYPE_CODE_STRUCT
+ && TYPE_CODE (SYMBOL_TYPE (s)) != TYPE_CODE_UNION)))
+ {
+ if (TYPE_CODE (SYMBOL_TYPE (s)) == TYPE_CODE_PTR
+ || TYPE_CODE (SYMBOL_TYPE (s)) == TYPE_CODE_FUNC)
+ {
+ /* If we are giving a name to a type such as "pointer to
+ foo" or "function returning foo", we better not set
+ the TYPE_NAME. If the program contains "typedef char
+ *caddr_t;", we don't want all variables of type char
+ * to print as caddr_t. This is not just a
+ consequence of GDB's type management; CC and GCC (at
+ least through version 2.4) both output variables of
+ either type char * or caddr_t with the type
+ refering to the stTypedef symbol for caddr_t. If a future
+ compiler cleans this up it GDB is not ready for it
+ yet, but if it becomes ready we somehow need to
+ disable this check (without breaking the PCC/GCC2.4
+ case).
+
+ Sigh.
+
+ Fortunately, this check seems not to be necessary
+ for anything except pointers or functions. */
+ }
+ else
+ TYPE_NAME (SYMBOL_TYPE (s)) = SYMBOL_NAME (s);
+ }
+ break;
+
+ case stFile: /* file name */
+ push_parse_stack ();
+ top_stack->blocktype = sh->st;
+ break;
+
+ /* I`ve never seen these for C */
+ case stRegReloc:
+ break; /* register relocation */
+ case stForward:
+ break; /* forwarding address */
+ case stConstant:
+ break; /* constant */
+ default:
+ complain (&unknown_mips_symtype_complaint, sh->st);
+ break;
+ }
+
+ return count;
+}
+
+/* Parse the type information provided in the raw AX entries for
+ the symbol SH. Return the bitfield size in BS, in case.
+ We must byte-swap the AX entries before we use them; BIGEND says whether
+ they are big-endian or little-endian (from fh->fBigendian). */
+
+static struct type *
+parse_type (fd, ax, aux_index, bs, bigend, sym_name)
+ int fd;
+ union aux_ext *ax;
+ unsigned int aux_index;
+ int *bs;
+ int bigend;
+ char *sym_name;
+{
+ /* Null entries in this map are treated specially */
+ static struct type **map_bt[] =
+ {
+ &builtin_type_void, /* btNil */
+ 0, /* btAdr */
+ &builtin_type_char, /* btChar */
+ &builtin_type_unsigned_char,/* btUChar */
+ &builtin_type_short, /* btShort */
+ &builtin_type_unsigned_short, /* btUShort */
+ &builtin_type_int, /* btInt */
+ &builtin_type_unsigned_int, /* btUInt */
+ &builtin_type_long, /* btLong */
+ &builtin_type_unsigned_long,/* btULong */
+ &builtin_type_float, /* btFloat */
+ &builtin_type_double, /* btDouble */
+ 0, /* btStruct */
+ 0, /* btUnion */
+ 0, /* btEnum */
+ 0, /* btTypedef */
+ 0, /* btRange */
+ 0, /* btSet */
+ &builtin_type_complex, /* btComplex */
+ &builtin_type_double_complex, /* btDComplex */
+ 0, /* btIndirect */
+ &builtin_type_fixed_dec, /* btFixedDec */
+ &builtin_type_float_dec, /* btFloatDec */
+ &builtin_type_string, /* btString */
+ 0, /* btBit */
+ 0, /* btPicture */
+ &builtin_type_void, /* btVoid */
+ 0, /* DEC C++: Pointer to member */
+ 0, /* DEC C++: Virtual function table */
+ 0, /* DEC C++: Class (Record) */
+ &builtin_type_long, /* btLong64 */
+ &builtin_type_unsigned_long, /* btULong64 */
+ &builtin_type_long_long, /* btLongLong64 */
+ &builtin_type_unsigned_long_long, /* btULongLong64 */
+ &builtin_type_unsigned_long, /* btAdr64 */
+ &builtin_type_long, /* btInt64 */
+ &builtin_type_unsigned_long, /* btUInt64 */
+ };
+
+ TIR t[1];
+ struct type *tp = 0;
+ enum type_code type_code = TYPE_CODE_UNDEF;
+
+ /* Handle corrupt aux indices. */
+ if (aux_index >= (ecoff_data (cur_bfd)->fdr + fd)->caux)
+ {
+ complain (&index_complaint, sym_name);
+ return builtin_type_int;
+ }
+ ax += aux_index;
+
+ /* Use aux as a type information record, map its basic type. */
+ ecoff_swap_tir_in (bigend, &ax->a_ti, t);
+ if (t->bt >= (sizeof (map_bt) / sizeof (*map_bt)))
+ {
+ complain (&basic_type_complaint, t->bt, sym_name);
+ return builtin_type_int;
+ }
+ if (map_bt[t->bt])
+ {
+ tp = *map_bt[t->bt];
+ }
+ else
+ {
+ tp = NULL;
+ /* Cannot use builtin types -- build our own */
+ switch (t->bt)
+ {
+ case btAdr:
+ tp = lookup_pointer_type (builtin_type_void);
+ break;
+ case btStruct:
+ type_code = TYPE_CODE_STRUCT;
+ break;
+ case btUnion:
+ type_code = TYPE_CODE_UNION;
+ break;
+ case btEnum:
+ type_code = TYPE_CODE_ENUM;
+ break;
+ case btRange:
+ type_code = TYPE_CODE_RANGE;
+ break;
+ case btSet:
+ type_code = TYPE_CODE_SET;
+ break;
+ case btTypedef:
+ /* alpha cc uses this for typedefs. The true type will be
+ obtained by crossreferencing below. */
+ type_code = TYPE_CODE_ERROR;
+ break;
+ default:
+ complain (&basic_type_complaint, t->bt, sym_name);
+ return builtin_type_int;
+ }
+ }
+
+ /* Move on to next aux */
+ ax++;
+
+ if (t->fBitfield)
+ {
+ /* Inhibit core dumps with some cfront generated objects that
+ corrupt the TIR. */
+ if (bs == (int *)NULL)
+ {
+ complain (&bad_fbitfield_complaint, sym_name);
+ return builtin_type_int;
+ }
+ *bs = AUX_GET_WIDTH (bigend, ax);
+ ax++;
+ }
+
+ /* All these types really point to some (common) MIPS type
+ definition, and only the type-qualifiers fully identify
+ them. We'll make the same effort at sharing. */
+ if (t->bt == btStruct ||
+ t->bt == btUnion ||
+ t->bt == btEnum ||
+
+ /* btSet (I think) implies that the name is a tag name, not a typedef
+ name. This apparently is a MIPS extension for C sets. */
+ t->bt == btSet)
+ {
+ char *name;
+
+ /* Try to cross reference this type, build new type on failure. */
+ ax += cross_ref (fd, ax, &tp, type_code, &name, bigend, sym_name);
+ if (tp == (struct type *) NULL)
+ tp = init_type (type_code, 0, 0, (char *) NULL, current_objfile);
+
+ /* Make sure that TYPE_CODE(tp) has an expected type code.
+ Any type may be returned from cross_ref if file indirect entries
+ are corrupted. */
+ if (TYPE_CODE (tp) != TYPE_CODE_STRUCT
+ && TYPE_CODE (tp) != TYPE_CODE_UNION
+ && TYPE_CODE (tp) != TYPE_CODE_ENUM)
+ {
+ complain (&unexpected_type_code_complaint, sym_name);
+ }
+ else
+ {
+
+ /* Usually, TYPE_CODE(tp) is already type_code. The main
+ exception is if we guessed wrong re struct/union/enum.
+ But for struct vs. union a wrong guess is harmless, so
+ don't complain(). */
+ if ((TYPE_CODE (tp) == TYPE_CODE_ENUM
+ && type_code != TYPE_CODE_ENUM)
+ || (TYPE_CODE (tp) != TYPE_CODE_ENUM
+ && type_code == TYPE_CODE_ENUM))
+ {
+ complain (&bad_tag_guess_complaint, sym_name);
+ }
+
+ if (TYPE_CODE (tp) != type_code)
+ {
+ TYPE_CODE (tp) = type_code;
+ }
+
+ /* Do not set the tag name if it is a compiler generated tag name
+ (.Fxx or .xxfake or empty) for unnamed struct/union/enums. */
+ if (name[0] == '.' || name[0] == '\0')
+ TYPE_TAG_NAME (tp) = NULL;
+ else if (TYPE_TAG_NAME (tp) == NULL
+ || !STREQ (TYPE_TAG_NAME (tp), name))
+ TYPE_TAG_NAME (tp) = obsavestring (name, strlen (name),
+ &current_objfile->type_obstack);
+ }
+ }
+
+ /* All these types really point to some (common) MIPS type
+ definition, and only the type-qualifiers fully identify
+ them. We'll make the same effort at sharing.
+ FIXME: btIndirect cannot happen here as it is handled by the
+ switch t->bt above. And we are not doing any guessing on range types. */
+ if (t->bt == btIndirect ||
+ t->bt == btRange)
+ {
+ char *name;
+
+ /* Try to cross reference this type, build new type on failure. */
+ ax += cross_ref (fd, ax, &tp, type_code, &name, bigend, sym_name);
+ if (tp == (struct type *) NULL)
+ tp = init_type (type_code, 0, 0, (char *) NULL, current_objfile);
+
+ /* Make sure that TYPE_CODE(tp) has an expected type code.
+ Any type may be returned from cross_ref if file indirect entries
+ are corrupted. */
+ if (TYPE_CODE (tp) != TYPE_CODE_RANGE)
+ {
+ complain (&unexpected_type_code_complaint, sym_name);
+ }
+ else
+ {
+ /* Usually, TYPE_CODE(tp) is already type_code. The main
+ exception is if we guessed wrong re struct/union/enum. */
+ if (TYPE_CODE (tp) != type_code)
+ {
+ complain (&bad_tag_guess_complaint, sym_name);
+ TYPE_CODE (tp) = type_code;
+ }
+ if (TYPE_NAME (tp) == NULL || !STREQ (TYPE_NAME (tp), name))
+ TYPE_NAME (tp) = obsavestring (name, strlen (name),
+ &current_objfile->type_obstack);
+ }
+ }
+ if (t->bt == btTypedef)
+ {
+ char *name;
+
+ /* Try to cross reference this type, it should succeed. */
+ ax += cross_ref (fd, ax, &tp, type_code, &name, bigend, sym_name);
+ if (tp == (struct type *) NULL)
+ {
+ complain (&unable_to_cross_ref_complaint, sym_name);
+ tp = builtin_type_int;
+ }
+ }
+
+ /* Deal with range types */
+ if (t->bt == btRange)
+ {
+ TYPE_NFIELDS (tp) = 2;
+ TYPE_FIELDS (tp) = ((struct field *)
+ TYPE_ALLOC (tp, 2 * sizeof (struct field)));
+ TYPE_FIELD_NAME (tp, 0) = obsavestring ("Low", strlen ("Low"),
+ &current_objfile->type_obstack);
+ TYPE_FIELD_BITPOS (tp, 0) = AUX_GET_DNLOW (bigend, ax);
+ ax++;
+ TYPE_FIELD_NAME (tp, 1) = obsavestring ("High", strlen ("High"),
+ &current_objfile->type_obstack);
+ TYPE_FIELD_BITPOS (tp, 1) = AUX_GET_DNHIGH (bigend, ax);
+ ax++;
+ }
+
+ /* Parse all the type qualifiers now. If there are more
+ than 6 the game will continue in the next aux */
+
+ while (1)
+ {
+#define PARSE_TQ(tq) \
+ if (t->tq != tqNil) \
+ ax += upgrade_type(fd, &tp, t->tq, ax, bigend, sym_name); \
+ else \
+ break;
+
+ PARSE_TQ (tq0);
+ PARSE_TQ (tq1);
+ PARSE_TQ (tq2);
+ PARSE_TQ (tq3);
+ PARSE_TQ (tq4);
+ PARSE_TQ (tq5);
+#undef PARSE_TQ
+
+ /* mips cc 2.x and gcc never put out continued aux entries. */
+ if (!t->continued)
+ break;
+
+ ecoff_swap_tir_in (bigend, &ax->a_ti, t);
+ ax++;
+ }
+
+ /* Complain for illegal continuations due to corrupt aux entries. */
+ if (t->continued)
+ complain (&bad_continued_complaint, sym_name);
+
+ return tp;
+}
+
+/* Make up a complex type from a basic one. Type is passed by
+ reference in TPP and side-effected as necessary. The type
+ qualifier TQ says how to handle the aux symbols at AX for
+ the symbol SX we are currently analyzing. BIGEND says whether
+ aux symbols are big-endian or little-endian.
+ Returns the number of aux symbols we parsed. */
+
+static int
+upgrade_type (fd, tpp, tq, ax, bigend, sym_name)
+ int fd;
+ struct type **tpp;
+ int tq;
+ union aux_ext *ax;
+ int bigend;
+ char *sym_name;
+{
+ int off;
+ struct type *t;
+
+ /* Used in array processing */
+ int rf, id;
+ FDR *fh;
+ struct type *range;
+ struct type *indx;
+ int lower, upper;
+ RNDXR rndx;
+
+ switch (tq)
+ {
+ case tqPtr:
+ t = lookup_pointer_type (*tpp);
+ *tpp = t;
+ return 0;
+
+ case tqProc:
+ t = lookup_function_type (*tpp);
+ *tpp = t;
+ return 0;
+
+ case tqArray:
+ off = 0;
+
+ /* Determine and record the domain type (type of index) */
+ ecoff_swap_rndx_in (bigend, &ax->a_rndx, &rndx);
+ id = rndx.index;
+ rf = rndx.rfd;
+ if (rf == 0xfff)
+ {
+ ax++;
+ rf = AUX_GET_ISYM (bigend, ax);
+ off++;
+ }
+ fh = get_rfd (fd, rf);
+
+ indx = parse_type (fd,
+ ecoff_data (cur_bfd)->external_aux + fh->iauxBase,
+ id, (int *) NULL, bigend, sym_name);
+
+ /* The bounds type should be an integer type, but might be anything
+ else due to corrupt aux entries. */
+ if (TYPE_CODE (indx) != TYPE_CODE_INT)
+ {
+ complain (&array_index_type_complaint, sym_name);
+ indx = builtin_type_int;
+ }
+
+ /* Get the bounds, and create the array type. */
+ ax++;
+ lower = AUX_GET_DNLOW (bigend, ax);
+ ax++;
+ upper = AUX_GET_DNHIGH (bigend, ax);
+ ax++;
+ rf = AUX_GET_WIDTH (bigend, ax); /* bit size of array element */
+
+ range = create_range_type ((struct type *) NULL, indx,
+ lower, upper);
+
+ t = create_array_type ((struct type *) NULL, *tpp, range);
+
+ /* We used to fill in the supplied array element bitsize
+ here if the TYPE_LENGTH of the target type was zero.
+ This happens for a `pointer to an array of anonymous structs',
+ but in this case the array element bitsize is also zero,
+ so nothing is gained.
+ And we used to check the TYPE_LENGTH of the target type against
+ the supplied array element bitsize.
+ gcc causes a mismatch for `pointer to array of object',
+ since the sdb directives it uses do not have a way of
+ specifying the bitsize, but it does no harm (the
+ TYPE_LENGTH should be correct) and we should be able to
+ ignore the erroneous bitsize from the auxiliary entry safely.
+ dbx seems to ignore it too. */
+
+ *tpp = t;
+ return 4 + off;
+
+ case tqVol:
+ /* Volatile -- currently ignored */
+ return 0;
+
+ case tqConst:
+ /* Const -- currently ignored */
+ return 0;
+
+ default:
+ complain (&unknown_type_qual_complaint, tq);
+ return 0;
+ }
+}
+
+
+/* Parse a procedure descriptor record PR. Note that the procedure is
+ parsed _after_ the local symbols, now we just insert the extra
+ information we need into a MIPS_EFI_SYMBOL_NAME symbol that has
+ already been placed in the procedure's main block. Note also that
+ images that have been partially stripped (ld -x) have been deprived
+ of local symbols, and we have to cope with them here. FIRST_OFF is
+ the offset of the first procedure for this FDR; we adjust the
+ address by this amount, but I don't know why. SEARCH_SYMTAB is the symtab
+ to look for the function which contains the MIPS_EFI_SYMBOL_NAME symbol
+ in question, or NULL to use top_stack->cur_block. */
+
+static void parse_procedure PARAMS ((PDR *, struct symtab *, unsigned long));
+
+static void
+parse_procedure (pr, search_symtab, first_off)
+ PDR *pr;
+ struct symtab *search_symtab;
+ unsigned long first_off;
+{
+ struct symbol *s, *i;
+ struct block *b;
+ struct mips_extra_func_info *e;
+ char *sh_name;
+
+ /* Simple rule to find files linked "-x" */
+ if (cur_fdr->rss == -1)
+ {
+ if (pr->isym == -1)
+ {
+ /* Static procedure at address pr->adr. Sigh. */
+ complain (&pdr_static_symbol_complaint, (unsigned long) pr->adr);
+ return;
+ }
+ else
+ {
+ /* external */
+ EXTR she;
+
+ (*ecoff_backend (cur_bfd)->swap_ext_in)
+ (cur_bfd,
+ ((char *) ecoff_data (cur_bfd)->external_ext
+ + pr->isym * ecoff_backend (cur_bfd)->external_ext_size),
+ &she);
+ sh_name = ecoff_data (cur_bfd)->ssext + she.asym.iss;
+ }
+ }
+ else
+ {
+ /* Full symbols */
+ SYMR sh;
+
+ (*ecoff_backend (cur_bfd)->swap_sym_in)
+ (cur_bfd,
+ ((char *) ecoff_data (cur_bfd)->external_sym
+ + ((cur_fdr->isymBase + pr->isym)
+ * ecoff_backend (cur_bfd)->external_sym_size)),
+ &sh);
+ sh_name = ecoff_data (cur_bfd)->ss + cur_fdr->issBase + sh.iss;
+ }
+
+ if (search_symtab != NULL)
+ {
+#if 0
+ /* This loses both in the case mentioned (want a static, find a global),
+ but also if we are looking up a non-mangled name which happens to
+ match the name of a mangled function. */
+ /* We have to save the cur_fdr across the call to lookup_symbol.
+ If the pdr is for a static function and if a global function with
+ the same name exists, lookup_symbol will eventually read in the symtab
+ for the global function and clobber cur_fdr. */
+ FDR *save_cur_fdr = cur_fdr;
+ s = lookup_symbol (sh_name, NULL, VAR_NAMESPACE, 0, NULL);
+ cur_fdr = save_cur_fdr;
+#else
+ s = mylookup_symbol
+ (sh_name,
+ BLOCKVECTOR_BLOCK (BLOCKVECTOR (search_symtab), STATIC_BLOCK),
+ VAR_NAMESPACE,
+ LOC_BLOCK);
+#endif
+ }
+ else
+ s = mylookup_symbol (sh_name, top_stack->cur_block,
+ VAR_NAMESPACE, LOC_BLOCK);
+
+ if (s != 0)
+ {
+ b = SYMBOL_BLOCK_VALUE (s);
+ }
+ else
+ {
+ complain (&pdr_for_nonsymbol_complaint, sh_name);
+#if 1
+ return;
+#else
+/* FIXME -- delete. We can't do symbol allocation now; it's all done. */
+ s = new_symbol (sh_name);
+ SYMBOL_NAMESPACE (s) = VAR_NAMESPACE;
+ SYMBOL_CLASS (s) = LOC_BLOCK;
+ /* Donno its type, hope int is ok */
+ SYMBOL_TYPE (s) = lookup_function_type (builtin_type_int);
+ add_symbol (s, top_stack->cur_block);
+ /* Wont have symbols for this one */
+ b = new_block (2);
+ SYMBOL_BLOCK_VALUE (s) = b;
+ BLOCK_FUNCTION (b) = s;
+ BLOCK_START (b) = pr->adr;
+ /* BOUND used to be the end of procedure's text, but the
+ argument is no longer passed in. */
+ BLOCK_END (b) = bound;
+ BLOCK_SUPERBLOCK (b) = top_stack->cur_block;
+ add_block (b, top_stack->cur_st);
+#endif
+ }
+
+ i = mylookup_symbol (MIPS_EFI_SYMBOL_NAME, b, LABEL_NAMESPACE, LOC_CONST);
+
+ if (i)
+ {
+ e = (struct mips_extra_func_info *) SYMBOL_VALUE (i);
+ e->pdr = *pr;
+ e->pdr.isym = (long) s;
+ e->pdr.adr += cur_fdr->adr - first_off;
+
+ /* Correct incorrect setjmp procedure descriptor from the library
+ to make backtrace through setjmp work. */
+ if (e->pdr.pcreg == 0 && STREQ (sh_name, "setjmp"))
+ {
+ complain (&bad_setjmp_pdr_complaint, 0);
+ e->pdr.pcreg = RA_REGNUM;
+ e->pdr.regmask = 0x80000000;
+ e->pdr.regoffset = -4;
+ }
+
+ /* Fake PC_REGNUM for alpha __sigtramp so that read_next_frame_reg
+ will use the saved user pc from the sigcontext. */
+ if (STREQ (sh_name, "__sigtramp"))
+ e->pdr.pcreg = PC_REGNUM;
+ }
+}
+
+/* Parse the external symbol ES. Just call parse_symbol() after
+ making sure we know where the aux are for it. For procedures,
+ parsing of the PDRs has already provided all the needed
+ information, we only parse them if SKIP_PROCEDURES is false,
+ and only if this causes no symbol duplication.
+ BIGEND says whether aux entries are big-endian or little-endian.
+
+ This routine clobbers top_stack->cur_block and ->cur_st. */
+
+static void
+parse_external (es, skip_procedures, bigend)
+ EXTR *es;
+ int skip_procedures;
+ int bigend;
+{
+ union aux_ext *ax;
+
+ if (es->ifd != ifdNil)
+ {
+ cur_fd = es->ifd;
+ cur_fdr = ecoff_data (cur_bfd)->fdr + cur_fd;
+ ax = ecoff_data (cur_bfd)->external_aux + cur_fdr->iauxBase;
+ }
+ else
+ {
+ cur_fdr = ecoff_data (cur_bfd)->fdr;
+ ax = 0;
+ }
+
+ /* Reading .o files */
+ if (es->asym.sc == scUndefined || es->asym.sc == scNil)
+ {
+ char *what;
+ switch (es->asym.st)
+ {
+ case stNil:
+ /* These are generated for static symbols in .o files,
+ ignore them. */
+ return;
+ case stStaticProc:
+ case stProc:
+ what = "procedure";
+ n_undef_procs++;
+ break;
+ case stGlobal:
+ what = "variable";
+ n_undef_vars++;
+ break;
+ case stLabel:
+ what = "label";
+ n_undef_labels++;
+ break;
+ default:
+ what = "symbol";
+ break;
+ }
+ n_undef_symbols++;
+ /* FIXME: Turn this into a complaint? */
+ if (info_verbose)
+ printf_filtered ("Warning: %s `%s' is undefined (in %s)\n",
+ what,
+ ecoff_data (cur_bfd)->ssext + es->asym.iss,
+ fdr_name (cur_fdr));
+ return;
+ }
+
+ switch (es->asym.st)
+ {
+ case stProc:
+ /* If we have full symbols we do not need more */
+ if (skip_procedures)
+ return;
+ if (mylookup_symbol (ecoff_data (cur_bfd)->ssext + es->asym.iss,
+ top_stack->cur_block,
+ VAR_NAMESPACE, LOC_BLOCK))
+ break;
+ /* fall through */
+ case stGlobal:
+ case stLabel:
+ /* Note that the case of a symbol with indexNil must be handled
+ anyways by parse_symbol(). */
+ parse_symbol (&es->asym, ax, (char *) NULL, bigend);
+ break;
+ default:
+ break;
+ }
+}
+
+/* Parse the line number info for file descriptor FH into
+ GDB's linetable LT. MIPS' encoding requires a little bit
+ of magic to get things out. Note also that MIPS' line
+ numbers can go back and forth, apparently we can live
+ with that and do not need to reorder our linetables */
+
+static void
+parse_lines (fh, pr, lt, maxlines)
+ FDR *fh;
+ PDR *pr;
+ struct linetable *lt;
+ int maxlines;
+{
+ unsigned char *base;
+ int j, k;
+ int delta, count, lineno = 0;
+ unsigned long first_off = pr->adr;
+
+ if (fh->cbLine == 0)
+ return;
+
+ base = ecoff_data (cur_bfd)->line + fh->cbLineOffset;
+
+ /* Scan by procedure descriptors */
+ k = 0;
+ for (j = 0; j < fh->cpd; j++, pr++)
+ {
+ long l;
+ unsigned long adr;
+ unsigned char *halt;
+
+ /* No code for this one */
+ if (pr->iline == ilineNil ||
+ pr->lnLow == -1 || pr->lnHigh == -1)
+ continue;
+
+ /* Determine start and end address of compressed line bytes for
+ this procedure. */
+ base = ecoff_data (cur_bfd)->line + fh->cbLineOffset;
+ if (j != (fh->cpd - 1))
+ halt = base + pr[1].cbLineOffset;
+ else
+ halt = base + fh->cbLine;
+ base += pr->cbLineOffset;
+
+ adr = fh->adr + pr->adr - first_off;
+ l = adr >> 2; /* in words */
+ for (lineno = pr->lnLow; base < halt; )
+ {
+ count = *base & 0x0f;
+ delta = *base++ >> 4;
+ if (delta >= 8)
+ delta -= 16;
+ if (delta == -8)
+ {
+ delta = (base[0] << 8) | base[1];
+ if (delta >= 0x8000)
+ delta -= 0x10000;
+ base += 2;
+ }
+ lineno += delta; /* first delta is 0 */
+
+ /* Complain if the line table overflows. Could happen
+ with corrupt binaries. */
+ if (lt->nitems >= maxlines)
+ {
+ complain (&bad_linetable_guess_complaint, fdr_name (fh));
+ break;
+ }
+ k = add_line (lt, lineno, l, k);
+ l += count + 1;
+ }
+ }
+}
+
+/* Master parsing procedure for first-pass reading of file symbols
+ into a partial_symtab. */
+
+static void
+parse_partial_symbols (objfile, section_offsets)
+ struct objfile *objfile;
+ struct section_offsets *section_offsets;
+{
+ const struct ecoff_backend_data * const backend = ecoff_backend (cur_bfd);
+ const bfd_size_type external_sym_size = backend->external_sym_size;
+ const bfd_size_type external_rfd_size = backend->external_rfd_size;
+ const bfd_size_type external_ext_size = backend->external_ext_size;
+ void (* const swap_ext_in) PARAMS ((bfd *, PTR, EXTR *))
+ = backend->swap_ext_in;
+ void (* const swap_sym_in) PARAMS ((bfd *, PTR, SYMR *))
+ = backend->swap_sym_in;
+ void (* const swap_rfd_in) PARAMS ((bfd *, PTR, RFDT *))
+ = backend->swap_rfd_in;
+ int f_idx, s_idx;
+ HDRR *hdr = &ecoff_data (cur_bfd)->symbolic_header;
+ /* Running pointers */
+ FDR *fh;
+ char *ext_out;
+ char *ext_out_end;
+ EXTR *ext_block;
+ register EXTR *ext_in;
+ EXTR *ext_in_end;
+ SYMR sh;
+ struct partial_symtab *pst;
+
+ int past_first_source_file = 0;
+
+ /* List of current psymtab's include files */
+ char **psymtab_include_list;
+ int includes_allocated;
+ int includes_used;
+ EXTR *extern_tab;
+ struct pst_map *fdr_to_pst;
+ /* Index within current psymtab dependency list */
+ struct partial_symtab **dependency_list;
+ int dependencies_used, dependencies_allocated;
+ struct cleanup *old_chain;
+ char *name;
+ enum language prev_language;
+
+ extern_tab = (EXTR *) obstack_alloc (&objfile->psymbol_obstack,
+ sizeof (EXTR) * hdr->iextMax);
+
+ includes_allocated = 30;
+ includes_used = 0;
+ psymtab_include_list = (char **) alloca (includes_allocated *
+ sizeof (char *));
+ next_symbol_text_func = mips_next_symbol_text;
+
+ dependencies_allocated = 30;
+ dependencies_used = 0;
+ dependency_list =
+ (struct partial_symtab **) alloca (dependencies_allocated *
+ sizeof (struct partial_symtab *));
+
+ last_source_file = NULL;
+
+ /*
+ * Big plan:
+ *
+ * Only parse the Local and External symbols, and the Relative FDR.
+ * Fixup enough of the loader symtab to be able to use it.
+ * Allocate space only for the file's portions we need to
+ * look at. (XXX)
+ */
+
+ max_gdbinfo = 0;
+ max_glevel = MIN_GLEVEL;
+
+ /* Allocate the map FDR -> PST.
+ Minor hack: -O3 images might claim some global data belongs
+ to FDR -1. We`ll go along with that */
+ fdr_to_pst = (struct pst_map *) xzalloc ((hdr->ifdMax + 1) * sizeof *fdr_to_pst);
+ old_chain = make_cleanup (free, fdr_to_pst);
+ fdr_to_pst++;
+ {
+ struct partial_symtab *pst = new_psymtab ("", objfile);
+ fdr_to_pst[-1].pst = pst;
+ FDR_IDX (pst) = -1;
+ }
+
+ /* Allocate the global pending list. */
+ ECOFF_PENDING_LIST (objfile) =
+ ((struct mips_pending **)
+ obstack_alloc (&objfile->psymbol_obstack,
+ hdr->ifdMax * sizeof (struct mips_pending *)));
+ memset ((PTR) ECOFF_PENDING_LIST (objfile), 0,
+ hdr->ifdMax * sizeof (struct mips_pending *));
+
+ /* Pass 0 over external syms: swap them in. */
+ ext_block = (EXTR *) xmalloc (hdr->iextMax * sizeof (EXTR));
+ make_cleanup (free, ext_block);
+
+ ext_out = (char *) ecoff_data (cur_bfd)->external_ext;
+ ext_out_end = ext_out + hdr->iextMax * external_ext_size;
+ ext_in = ext_block;
+ for (; ext_out < ext_out_end; ext_out += external_ext_size, ext_in++)
+ (*swap_ext_in) (cur_bfd, ext_out, ext_in);
+
+ /* Pass 1 over external syms: Presize and partition the list */
+ ext_in = ext_block;
+ ext_in_end = ext_in + hdr->iextMax;
+ for (; ext_in < ext_in_end; ext_in++)
+ fdr_to_pst[ext_in->ifd].n_globals++;
+
+ /* Pass 1.5 over files: partition out global symbol space */
+ s_idx = 0;
+ for (f_idx = -1; f_idx < hdr->ifdMax; f_idx++)
+ {
+ fdr_to_pst[f_idx].globals_offset = s_idx;
+ s_idx += fdr_to_pst[f_idx].n_globals;
+ fdr_to_pst[f_idx].n_globals = 0;
+ }
+
+ /* Pass 2 over external syms: fill in external symbols */
+ ext_in = ext_block;
+ ext_in_end = ext_in + hdr->iextMax;
+ for (; ext_in < ext_in_end; ext_in++)
+ {
+ enum minimal_symbol_type ms_type = mst_text;
+
+ extern_tab[fdr_to_pst[ext_in->ifd].globals_offset
+ + fdr_to_pst[ext_in->ifd].n_globals++] = *ext_in;
+
+ if (ext_in->asym.sc == scUndefined || ext_in->asym.sc == scNil)
+ continue;
+
+ name = ecoff_data (cur_bfd)->ssext + ext_in->asym.iss;
+ switch (ext_in->asym.st)
+ {
+ case stProc:
+ break;
+ case stStaticProc:
+ ms_type = mst_file_text;
+ break;
+ case stGlobal:
+ if (ext_in->asym.sc == scData
+ || ext_in->asym.sc == scSData
+ || ext_in->asym.sc == scRData)
+ ms_type = mst_data;
+ else
+ ms_type = mst_bss;
+ break;
+ case stLabel:
+ if (ext_in->asym.sc == scAbs)
+ ms_type = mst_abs;
+ else if (ext_in->asym.sc == scText)
+ ms_type = mst_text;
+ else if (ext_in->asym.sc == scData
+ || ext_in->asym.sc == scSData
+ || ext_in->asym.sc == scRData)
+ ms_type = mst_data;
+ else
+ ms_type = mst_bss;
+ break;
+ case stLocal:
+ /* The alpha has the section start addresses in stLocal symbols
+ whose name starts with a `.'. Skip those but complain for all
+ other stLocal symbols. */
+ if (name[0] == '.')
+ continue;
+ /* Fall through. */
+ default:
+ ms_type = mst_unknown;
+ complain (&unknown_ext_complaint, name);
+ }
+ prim_record_minimal_symbol (name, ext_in->asym.value, ms_type);
+ }
+
+ /* Pass 3 over files, over local syms: fill in static symbols */
+ for (f_idx = 0; f_idx < hdr->ifdMax; f_idx++)
+ {
+ struct partial_symtab *save_pst;
+ EXTR *ext_ptr;
+
+ cur_fdr = fh = ecoff_data (cur_bfd)->fdr + f_idx;
+
+ if (fh->csym == 0)
+ {
+ fdr_to_pst[f_idx].pst = NULL;
+ continue;
+ }
+ pst = start_psymtab_common (objfile, section_offsets,
+ fdr_name (fh),
+ fh->cpd ? fh->adr : 0,
+ objfile->global_psymbols.next,
+ objfile->static_psymbols.next);
+ pst->read_symtab_private = ((char *)
+ obstack_alloc (&objfile->psymbol_obstack,
+ sizeof (struct symloc)));
+ memset ((PTR) pst->read_symtab_private, 0, sizeof (struct symloc));
+
+ save_pst = pst;
+ FDR_IDX (pst) = f_idx;
+ CUR_BFD (pst) = cur_bfd;
+
+ /* The way to turn this into a symtab is to call... */
+ pst->read_symtab = mipscoff_psymtab_to_symtab;
+
+ /* Set up language for the pst.
+ The language from the FDR is used if it is unambigious (e.g. cfront
+ with native cc and g++ will set the language to C).
+ Otherwise we have to deduce the language from the filename.
+ Native ecoff has every header file in a separate FDR, so
+ deduce_language_from_filename will return language_unknown for
+ a header file, which is not what we want.
+ But the FDRs for the header files are after the FDR for the source
+ file, so we can assign the language of the source file to the
+ following header files. Then we save the language in the private
+ pst data so that we can reuse it when building symtabs. */
+ prev_language = psymtab_language;
+
+ switch (fh->lang)
+ {
+ case langCplusplusV2:
+ psymtab_language = language_cplus;
+ break;
+ default:
+ psymtab_language = deduce_language_from_filename (fdr_name (fh));
+ break;
+ }
+ if (psymtab_language == language_unknown)
+ psymtab_language = prev_language;
+ PST_PRIVATE (pst)->pst_language = psymtab_language;
+
+ pst->texthigh = pst->textlow;
+
+ /* For stabs-in-ecoff files, the second symbol must be @stab.
+ This symbol is emitted by mips-tfile to signal that the
+ current object file uses encapsulated stabs instead of mips
+ ecoff for local symbols. (It is the second symbol because
+ the first symbol is the stFile used to signal the start of a
+ file). */
+ processing_gcc_compilation = 0;
+ if (fh->csym >= 2)
+ {
+ (*swap_sym_in) (cur_bfd,
+ ((char *) ecoff_data (cur_bfd)->external_sym
+ + (fh->isymBase + 1) * external_sym_size),
+ &sh);
+ if (STREQ (ecoff_data (cur_bfd)->ss + fh->issBase + sh.iss,
+ stabs_symbol))
+ processing_gcc_compilation = 2;
+ }
+
+ if (processing_gcc_compilation != 0)
+ {
+ for (cur_sdx = 2; cur_sdx < fh->csym; cur_sdx++)
+ {
+ int type_code;
+ char *namestring;
+
+ (*swap_sym_in) (cur_bfd,
+ ((char *) ecoff_data (cur_bfd)->external_sym
+ + (fh->isymBase + cur_sdx) * external_sym_size),
+ &sh);
+ type_code = ECOFF_UNMARK_STAB (sh.index);
+ if (!ECOFF_IS_STAB (&sh))
+ {
+ if (sh.st == stProc || sh.st == stStaticProc)
+ {
+ long procaddr = sh.value;
+ long isym;
+
+
+ isym = AUX_GET_ISYM (fh->fBigendian,
+ (ecoff_data (cur_bfd)->external_aux
+ + fh->iauxBase
+ + sh.index));
+ (*swap_sym_in) (cur_bfd,
+ (((char *)
+ ecoff_data (cur_bfd)->external_sym)
+ + ((fh->isymBase + isym - 1)
+ * external_sym_size)),
+ &sh);
+ if (sh.st == stEnd)
+ {
+ long high = procaddr + sh.value;
+ if (high > pst->texthigh)
+ pst->texthigh = high;
+ }
+ }
+ continue;
+ }
+#define SET_NAMESTRING() \
+ namestring = ecoff_data (cur_bfd)->ss + fh->issBase + sh.iss
+#define CUR_SYMBOL_TYPE type_code
+#define CUR_SYMBOL_VALUE sh.value
+#define START_PSYMTAB(ofile,secoff,fname,low,symoff,global_syms,static_syms)\
+ pst = save_pst
+#define END_PSYMTAB(pst,ilist,ninc,c_off,c_text,dep_list,n_deps) (void)0
+#define HANDLE_RBRAC(val) \
+ if ((val) > save_pst->texthigh) save_pst->texthigh = (val);
+#include "partial-stab.h"
+ }
+ }
+ else
+ {
+ for (cur_sdx = 0; cur_sdx < fh->csym;)
+ {
+ char *name;
+ enum address_class class;
+
+ (*swap_sym_in) (cur_bfd,
+ ((char *) ecoff_data (cur_bfd)->external_sym
+ + ((fh->isymBase + cur_sdx)
+ * external_sym_size)),
+ &sh);
+
+ if (ECOFF_IS_STAB (&sh))
+ {
+ cur_sdx++;
+ continue;
+ }
+
+ /* Non absolute static symbols go into the minimal table. */
+ if (sh.sc == scUndefined || sh.sc == scNil
+ || (sh.index == indexNil
+ && (sh.st != stStatic || sh.sc == scAbs)))
+ {
+ /* FIXME, premature? */
+ cur_sdx++;
+ continue;
+ }
+
+ name = ecoff_data (cur_bfd)->ss + fh->issBase + sh.iss;
+
+ switch (sh.st)
+ {
+ long high;
+ long procaddr;
+ int new_sdx;
+
+ case stStaticProc: /* Function */
+ /* I believe this is used only for file-local functions.
+ The comment in symconst.h ("load time only static procs")
+ isn't particularly clear on this point. */
+ prim_record_minimal_symbol (name, sh.value, mst_file_text);
+ /* FALLTHROUGH */
+
+ case stProc: /* Asm labels apparently */
+ ADD_PSYMBOL_TO_LIST (name, strlen (name),
+ VAR_NAMESPACE, LOC_BLOCK,
+ objfile->static_psymbols, sh.value,
+ psymtab_language, objfile);
+ /* Skip over procedure to next one. */
+ if (sh.index >= hdr->iauxMax)
+ {
+ /* Should not happen, but does when cross-compiling
+ with the MIPS compiler. FIXME -- pull later. */
+ complain (&index_complaint, name);
+ new_sdx = cur_sdx + 1; /* Don't skip at all */
+ }
+ else
+ new_sdx = AUX_GET_ISYM (fh->fBigendian,
+ (ecoff_data (cur_bfd)->external_aux
+ + fh->iauxBase
+ + sh.index));
+ procaddr = sh.value;
+
+ if (new_sdx <= cur_sdx)
+ {
+ /* This should not happen either... FIXME. */
+ complain (&aux_index_complaint, name);
+ new_sdx = cur_sdx + 1; /* Don't skip backward */
+ }
+
+ cur_sdx = new_sdx;
+ (*swap_sym_in) (cur_bfd,
+ ((char *) ecoff_data (cur_bfd)->external_sym
+ + ((fh->isymBase + cur_sdx - 1)
+ * external_sym_size)),
+ &sh);
+ if (sh.st != stEnd)
+ continue;
+ high = procaddr + sh.value;
+ if (high > pst->texthigh)
+ pst->texthigh = high;
+ continue;
+
+ case stStatic: /* Variable */
+ if (sh.sc == scData || sh.sc == scSData || sh.sc == scRData)
+ prim_record_minimal_symbol (name, sh.value, mst_file_data);
+ else
+ prim_record_minimal_symbol (name, sh.value, mst_file_bss);
+ class = LOC_STATIC;
+ break;
+
+ case stTypedef:/* Typedef */
+ class = LOC_TYPEDEF;
+ break;
+
+ case stConstant: /* Constant decl */
+ class = LOC_CONST;
+ break;
+
+ case stUnion:
+ case stStruct:
+ case stEnum:
+ case stBlock: /* { }, str, un, enum*/
+ if (sh.sc == scInfo || sh.sc == scCommon)
+ {
+ ADD_PSYMBOL_TO_LIST (name, strlen (name),
+ STRUCT_NAMESPACE, LOC_TYPEDEF,
+ objfile->static_psymbols,
+ sh.value,
+ psymtab_language, objfile);
+ }
+ /* Skip over the block */
+ new_sdx = sh.index;
+ if (new_sdx <= cur_sdx)
+ {
+ /* This happens with the Ultrix kernel. */
+ complain (&block_index_complaint, name);
+ new_sdx = cur_sdx + 1; /* Don't skip backward */
+ }
+ cur_sdx = new_sdx;
+ continue;
+
+ case stFile: /* File headers */
+ case stLabel: /* Labels */
+ case stEnd: /* Ends of files */
+ goto skip;
+
+ case stLocal: /* Local variables */
+ /* Normally these are skipped because we skip over
+ all blocks we see. However, these can occur
+ as visible symbols in a .h file that contains code. */
+ goto skip;
+
+ default:
+ /* Both complaints are valid: one gives symbol name,
+ the other the offending symbol type. */
+ complain (&unknown_sym_complaint, name);
+ complain (&unknown_st_complaint, sh.st);
+ cur_sdx++;
+ continue;
+ }
+ /* Use this gdb symbol */
+ ADD_PSYMBOL_TO_LIST (name, strlen (name),
+ VAR_NAMESPACE, class,
+ objfile->static_psymbols, sh.value,
+ psymtab_language, objfile);
+ skip:
+ cur_sdx++; /* Go to next file symbol */
+ }
+
+ /* Now do enter the external symbols. */
+ ext_ptr = &extern_tab[fdr_to_pst[f_idx].globals_offset];
+ cur_sdx = fdr_to_pst[f_idx].n_globals;
+ PST_PRIVATE (save_pst)->extern_count = cur_sdx;
+ PST_PRIVATE (save_pst)->extern_tab = ext_ptr;
+ for (; --cur_sdx >= 0; ext_ptr++)
+ {
+ enum address_class class;
+ SYMR *psh;
+ char *name;
+
+ if (ext_ptr->ifd != f_idx)
+ abort ();
+ psh = &ext_ptr->asym;
+
+ /* Do not add undefined symbols to the partial symbol table. */
+ if (psh->sc == scUndefined || psh->sc == scNil)
+ continue;
+
+ switch (psh->st)
+ {
+ case stNil:
+ /* These are generated for static symbols in .o files,
+ ignore them. */
+ continue;
+ case stProc:
+ case stStaticProc:
+ class = LOC_BLOCK;
+ break;
+ case stLabel:
+ class = LOC_LABEL;
+ break;
+ default:
+ complain (&unknown_ext_complaint,
+ ecoff_data (cur_bfd)->ssext + psh->iss);
+ /* Fall through, pretend it's global. */
+ case stGlobal:
+ class = LOC_STATIC;
+ break;
+ }
+ name = ecoff_data (cur_bfd)->ssext + psh->iss;
+ ADD_PSYMBOL_ADDR_TO_LIST (name, strlen (name),
+ VAR_NAMESPACE, class,
+ objfile->global_psymbols, (CORE_ADDR) psh->value,
+ psymtab_language, objfile);
+ }
+ }
+
+ /* Link pst to FDR. end_psymtab returns NULL if the psymtab was
+ empty and put on the free list. */
+ fdr_to_pst[f_idx].pst = end_psymtab (save_pst,
+ psymtab_include_list, includes_used,
+ -1, save_pst->texthigh,
+ dependency_list, dependencies_used);
+ if (objfile->ei.entry_point >= save_pst->textlow &&
+ objfile->ei.entry_point < save_pst->texthigh)
+ {
+ objfile->ei.entry_file_lowpc = save_pst->textlow;
+ objfile->ei.entry_file_highpc = save_pst->texthigh;
+ }
+ }
+
+ /* Now scan the FDRs for dependencies */
+ for (f_idx = 0; f_idx < hdr->ifdMax; f_idx++)
+ {
+ fh = f_idx + ecoff_data (cur_bfd)->fdr;
+ pst = fdr_to_pst[f_idx].pst;
+
+ if (pst == (struct partial_symtab *)NULL)
+ continue;
+
+ /* This should catch stabs-in-ecoff. */
+ if (fh->crfd <= 1)
+ continue;
+
+ /* Skip the first file indirect entry as it is a self dependency
+ for source files or a reverse .h -> .c dependency for header files. */
+ pst->number_of_dependencies = 0;
+ pst->dependencies =
+ ((struct partial_symtab **)
+ obstack_alloc (&objfile->psymbol_obstack,
+ ((fh->crfd - 1)
+ * sizeof (struct partial_symtab *))));
+ for (s_idx = 1; s_idx < fh->crfd; s_idx++)
+ {
+ RFDT rh;
+
+ (*swap_rfd_in) (cur_bfd,
+ ((char *) ecoff_data (cur_bfd)->external_rfd
+ + (fh->rfdBase + s_idx) * external_rfd_size),
+ &rh);
+ if (rh < 0 || rh >= hdr->ifdMax)
+ {
+ complain (&bad_file_number_complaint, rh);
+ continue;
+ }
+
+ /* Skip self dependencies of header files. */
+ if (rh == f_idx)
+ continue;
+
+ /* Do not add to dependeny list if psymtab was empty. */
+ if (fdr_to_pst[rh].pst == (struct partial_symtab *)NULL)
+ continue;
+ pst->dependencies[pst->number_of_dependencies++] = fdr_to_pst[rh].pst;
+ }
+ }
+ do_cleanups (old_chain);
+}
+
+
+static char *
+mips_next_symbol_text ()
+{
+ SYMR sh;
+
+ cur_sdx++;
+ (*ecoff_backend (cur_bfd)->swap_sym_in)
+ (cur_bfd,
+ ((char *) ecoff_data (cur_bfd)->external_sym
+ + ((cur_fdr->isymBase + cur_sdx)
+ * ecoff_backend (cur_bfd)->external_sym_size)),
+ &sh);
+ return ecoff_data (cur_bfd)->ss + cur_fdr->issBase + sh.iss;
+}
+
+/* Ancillary function to psymtab_to_symtab(). Does all the work
+ for turning the partial symtab PST into a symtab, recurring
+ first on all dependent psymtabs. The argument FILENAME is
+ only passed so we can see in debug stack traces what file
+ is being read.
+
+ This function has a split personality, based on whether the
+ symbol table contains ordinary ecoff symbols, or stabs-in-ecoff.
+ The flow of control and even the memory allocation differs. FIXME. */
+
+static void
+psymtab_to_symtab_1 (pst, filename)
+ struct partial_symtab *pst;
+ char *filename;
+{
+ const bfd_size_type external_sym_size
+ = ecoff_backend (cur_bfd)->external_sym_size;
+ const bfd_size_type external_pdr_size
+ = ecoff_backend (cur_bfd)->external_pdr_size;
+ void (* const swap_sym_in) PARAMS ((bfd *, PTR, SYMR *))
+ = ecoff_backend (cur_bfd)->swap_sym_in;
+ void (* const swap_pdr_in) PARAMS ((bfd *, PTR, PDR *))
+ = ecoff_backend (cur_bfd)->swap_pdr_in;
+ int i;
+ struct symtab *st;
+ FDR *fh;
+ struct linetable *lines;
+
+ if (pst->readin)
+ return;
+ pst->readin = 1;
+
+ /* Read in all partial symbtabs on which this one is dependent.
+ NOTE that we do have circular dependencies, sigh. We solved
+ that by setting pst->readin before this point. */
+
+ for (i = 0; i < pst->number_of_dependencies; i++)
+ if (!pst->dependencies[i]->readin)
+ {
+ /* Inform about additional files to be read in. */
+ if (info_verbose)
+ {
+ fputs_filtered (" ", stdout);
+ wrap_here ("");
+ fputs_filtered ("and ", stdout);
+ wrap_here ("");
+ printf_filtered ("%s...",
+ pst->dependencies[i]->filename);
+ wrap_here (""); /* Flush output */
+ fflush (stdout);
+ }
+ /* We only pass the filename for debug purposes */
+ psymtab_to_symtab_1 (pst->dependencies[i],
+ pst->dependencies[i]->filename);
+ }
+
+ /* Do nothing if this is a dummy psymtab. */
+
+ if (pst->n_global_syms == 0 && pst->n_static_syms == 0
+ && pst->textlow == 0 && pst->texthigh == 0)
+ return;
+
+ /* Now read the symbols for this symtab */
+
+ cur_bfd = CUR_BFD (pst);
+ current_objfile = pst->objfile;
+ cur_fd = FDR_IDX (pst);
+ fh = (cur_fd == -1) ? (FDR *) NULL : ecoff_data (cur_bfd)->fdr + cur_fd;
+ cur_fdr = fh;
+
+ /* See comment in parse_partial_symbols about the @stabs sentinel. */
+ processing_gcc_compilation = 0;
+ if (fh != (FDR *) NULL && fh->csym >= 2)
+ {
+ SYMR sh;
+
+ (*swap_sym_in) (cur_bfd,
+ ((char *) ecoff_data (cur_bfd)->external_sym
+ + (fh->isymBase + 1) * external_sym_size),
+ &sh);
+ if (STREQ (ecoff_data (cur_bfd)->ss + fh->issBase + sh.iss,
+ stabs_symbol))
+ {
+ /* We indicate that this is a GCC compilation so that certain
+ features will be enabled in stabsread/dbxread. */
+ processing_gcc_compilation = 2;
+ }
+ }
+
+ if (processing_gcc_compilation != 0)
+ {
+ char *pdr_ptr;
+ char *pdr_end;
+ int first_pdr;
+ unsigned long first_off = 0;
+
+ /* This symbol table contains stabs-in-ecoff entries. */
+
+ /* Parse local symbols first */
+
+ if (fh->csym <= 2) /* FIXME, this blows psymtab->symtab ptr */
+ {
+ current_objfile = NULL;
+ return;
+ }
+ for (cur_sdx = 2; cur_sdx < fh->csym; cur_sdx++)
+ {
+ SYMR sh;
+ char *name;
+ CORE_ADDR valu;
+
+ (*swap_sym_in) (cur_bfd,
+ ((char *) ecoff_data (cur_bfd)->external_sym
+ + (fh->isymBase + cur_sdx) * external_sym_size),
+ &sh);
+ name = ecoff_data (cur_bfd)->ss + fh->issBase + sh.iss;
+ valu = sh.value;
+ if (ECOFF_IS_STAB (&sh))
+ {
+ int type_code = ECOFF_UNMARK_STAB (sh.index);
+ process_one_symbol (type_code, 0, valu, name,
+ pst->section_offsets, pst->objfile);
+ if (type_code == N_FUN)
+ {
+ /* Make up special symbol to contain
+ procedure specific info */
+ struct mips_extra_func_info *e =
+ ((struct mips_extra_func_info *)
+ obstack_alloc (&current_objfile->symbol_obstack,
+ sizeof (struct mips_extra_func_info)));
+ struct symbol *s = new_symbol (MIPS_EFI_SYMBOL_NAME);
+ SYMBOL_NAMESPACE (s) = LABEL_NAMESPACE;
+ SYMBOL_CLASS (s) = LOC_CONST;
+ SYMBOL_TYPE (s) = builtin_type_void;
+ SYMBOL_VALUE (s) = (long) e;
+ add_symbol_to_list (s, &local_symbols);
+ }
+ }
+ else if (sh.st == stLabel && sh.index != indexNil)
+ {
+ /* Handle encoded stab line number. */
+ record_line (current_subfile, sh.index, valu);
+ }
+ else if (sh.st == stProc || sh.st == stStaticProc || sh.st == stEnd)
+ /* These are generated by gcc-2.x, do not complain */
+ ;
+ else
+ complain (&stab_unknown_complaint, name);
+ }
+ st = end_symtab (pst->texthigh, 0, 0, pst->objfile, SECT_OFF_TEXT);
+ end_stabs ();
+
+ /* Sort the symbol table now, we are done adding symbols to it.
+ We must do this before parse_procedure calls lookup_symbol. */
+ sort_symtab_syms (st);
+
+ /* This may not be necessary for stabs symtabs. FIXME. */
+ sort_blocks (st);
+
+ /* Fill in procedure info next. */
+ first_pdr = 1;
+ pdr_ptr = ((char *) ecoff_data (cur_bfd)->external_pdr
+ + fh->ipdFirst * external_pdr_size);
+ pdr_end = pdr_ptr + fh->cpd * external_pdr_size;
+ for (; pdr_ptr < pdr_end; pdr_ptr += external_pdr_size)
+ {
+ PDR pr;
+
+ (*swap_pdr_in) (cur_bfd, pdr_ptr, &pr);
+ if (first_pdr)
+ {
+ first_off = pr.adr;
+ first_pdr = 0;
+ }
+ parse_procedure (&pr, st, first_off);
+ }
+ }
+ else
+ {
+ /* This symbol table contains ordinary ecoff entries. */
+
+ /* FIXME: doesn't use pst->section_offsets. */
+
+ int f_max;
+ int maxlines;
+ EXTR *ext_ptr;
+
+ /* How many symbols will we need */
+ /* FIXME, this does not count enum values. */
+ f_max = pst->n_global_syms + pst->n_static_syms;
+ if (fh == 0)
+ {
+ maxlines = 0;
+ st = new_symtab ("unknown", f_max, 0, pst->objfile);
+ }
+ else
+ {
+ f_max += fh->csym + fh->cpd;
+ maxlines = 2 * fh->cline;
+ st = new_symtab (pst->filename, 2 * f_max, maxlines, pst->objfile);
+
+ /* The proper language was already determined when building
+ the psymtab, use it. */
+ st->language = PST_PRIVATE (pst)->pst_language;
+ }
+
+ psymtab_language = st->language;
+
+ lines = LINETABLE (st);
+
+ /* Get a new lexical context */
+
+ push_parse_stack ();
+ top_stack->cur_st = st;
+ top_stack->cur_block = BLOCKVECTOR_BLOCK (BLOCKVECTOR (st),
+ STATIC_BLOCK);
+ BLOCK_START (top_stack->cur_block) = fh ? fh->adr : 0;
+ BLOCK_END (top_stack->cur_block) = 0;
+ top_stack->blocktype = stFile;
+ top_stack->maxsyms = 2 * f_max;
+ top_stack->cur_type = 0;
+ top_stack->procadr = 0;
+ top_stack->numargs = 0;
+
+ if (fh)
+ {
+ char *sym_ptr;
+ char *sym_end;
+
+ /* Parse local symbols first */
+ sym_ptr = ((char *) ecoff_data (cur_bfd)->external_sym
+ + fh->isymBase * external_sym_size);
+ sym_end = sym_ptr + fh->csym * external_sym_size;
+ while (sym_ptr < sym_end)
+ {
+ SYMR sh;
+ int c;
+
+ (*swap_sym_in) (cur_bfd, sym_ptr, &sh);
+ c = parse_symbol (&sh,
+ (ecoff_data (cur_bfd)->external_aux
+ + fh->iauxBase),
+ sym_ptr, fh->fBigendian);
+ sym_ptr += c * external_sym_size;
+ }
+
+ /* Linenumbers. At the end, check if we can save memory.
+ parse_lines has to look ahead an arbitrary number of PDR
+ structures, so we swap them all first. */
+ if (fh->cpd > 0)
+ {
+ PDR *pr_block;
+ struct cleanup *old_chain;
+ char *pdr_ptr;
+ char *pdr_end;
+ PDR *pdr_in;
+ PDR *pdr_in_end;
+
+ pr_block = (PDR *) xmalloc (fh->cpd * sizeof (PDR));
+
+ old_chain = make_cleanup (free, pr_block);
+
+ pdr_ptr = ((char *) ecoff_data (cur_bfd)->external_pdr
+ + fh->ipdFirst * external_pdr_size);
+ pdr_end = pdr_ptr + fh->cpd * external_pdr_size;
+ pdr_in = pr_block;
+ for (;
+ pdr_ptr < pdr_end;
+ pdr_ptr += external_pdr_size, pdr_in++)
+ (*swap_pdr_in) (cur_bfd, pdr_ptr, pdr_in);
+
+ parse_lines (fh, pr_block, lines, maxlines);
+ if (lines->nitems < fh->cline)
+ lines = shrink_linetable (lines);
+
+ /* Fill in procedure info next. */
+ pdr_in = pr_block;
+ pdr_in_end = pdr_in + fh->cpd;
+ for (; pdr_in < pdr_in_end; pdr_in++)
+ parse_procedure (pdr_in, 0, pr_block->adr);
+
+ do_cleanups (old_chain);
+ }
+ }
+
+ LINETABLE (st) = lines;
+
+ /* .. and our share of externals.
+ XXX use the global list to speed up things here. how?
+ FIXME, Maybe quit once we have found the right number of ext's? */
+ top_stack->cur_st = st;
+ top_stack->cur_block = BLOCKVECTOR_BLOCK (BLOCKVECTOR (top_stack->cur_st),
+ GLOBAL_BLOCK);
+ top_stack->blocktype = stFile;
+ top_stack->maxsyms = (ecoff_data (cur_bfd)->symbolic_header.isymMax
+ + ecoff_data (cur_bfd)->symbolic_header.ipdMax
+ + ecoff_data (cur_bfd)->symbolic_header.iextMax);
+
+ ext_ptr = PST_PRIVATE (pst)->extern_tab;
+ for (i = PST_PRIVATE (pst)->extern_count; --i >= 0; ext_ptr++)
+ parse_external (ext_ptr, 1, fh->fBigendian);
+
+ /* If there are undefined symbols, tell the user.
+ The alpha has an undefined symbol for every symbol that is
+ from a shared library, so tell the user only if verbose is on. */
+ if (info_verbose && n_undef_symbols)
+ {
+ printf_filtered ("File %s contains %d unresolved references:",
+ st->filename, n_undef_symbols);
+ printf_filtered ("\n\t%4d variables\n\t%4d procedures\n\t%4d labels\n",
+ n_undef_vars, n_undef_procs, n_undef_labels);
+ n_undef_symbols = n_undef_labels = n_undef_vars = n_undef_procs = 0;
+
+ }
+ pop_parse_stack ();
+
+ /* Sort the symbol table now, we are done adding symbols to it.*/
+ sort_symtab_syms (st);
+
+ sort_blocks (st);
+ }
+
+ /* Now link the psymtab and the symtab. */
+ pst->symtab = st;
+
+ current_objfile = NULL;
+}
+
+/* Ancillary parsing procedures. */
+
+/* Lookup the type at relative index RN. Return it in TPP
+ if found and in any event come up with its name PNAME.
+ BIGEND says whether aux symbols are big-endian or not (from fh->fBigendian).
+ Return value says how many aux symbols we ate. */
+
+static int
+cross_ref (fd, ax, tpp, type_code, pname, bigend, sym_name)
+ int fd;
+ union aux_ext *ax;
+ struct type **tpp;
+ enum type_code type_code; /* Use to alloc new type if none is found. */
+ char **pname;
+ int bigend;
+ char *sym_name;
+{
+ RNDXR rn[1];
+ unsigned int rf;
+ int result = 1;
+ FDR *fh;
+ char *esh;
+ SYMR sh;
+ int xref_fd;
+ struct mips_pending *pend;
+
+ *tpp = (struct type *)NULL;
+
+ ecoff_swap_rndx_in (bigend, &ax->a_rndx, rn);
+
+ /* Escape index means 'the next one' */
+ if (rn->rfd == 0xfff)
+ {
+ result++;
+ rf = AUX_GET_ISYM (bigend, ax + 1);
+ }
+ else
+ {
+ rf = rn->rfd;
+ }
+
+ /* mips cc uses a rf of -1 for opaque struct definitions.
+ Set TYPE_FLAG_STUB for these types so that check_stub_type will
+ resolve them if the struct gets defined in another compilation unit. */
+ if (rf == -1)
+ {
+ *pname = "<undefined>";
+ *tpp = init_type (type_code, 0, 0, (char *) NULL, current_objfile);
+ TYPE_FLAGS (*tpp) |= TYPE_FLAG_STUB;
+ return result;
+ }
+
+ /* mips cc uses an escaped rn->index of 0 for struct return types
+ of procedures that were compiled without -g. These will always remain
+ undefined. */
+ if (rn->rfd == 0xfff && rn->index == 0)
+ {
+ *pname = "<undefined>";
+ return result;
+ }
+
+ /* Find the relative file descriptor and the symbol in it. */
+ fh = get_rfd (fd, rf);
+ xref_fd = fh - ecoff_data (cur_bfd)->fdr;
+
+ if (rn->index >= fh->csym)
+ {
+ /* File indirect entry is corrupt. */
+ *pname = "<illegal>";
+ complain (&bad_rfd_entry_complaint,
+ sym_name, xref_fd, rn->index);
+ return result;
+ }
+
+ /* If we have processed this symbol then we left a forwarding
+ pointer to the type in the pending list. If not, we`ll put
+ it in a list of pending types, to be processed later when
+ the file will be. In any event, we collect the name for the
+ type here. */
+
+ esh = ((char *) ecoff_data (cur_bfd)->external_sym
+ + ((fh->isymBase + rn->index)
+ * ecoff_backend (cur_bfd)->external_sym_size));
+ (*ecoff_backend (cur_bfd)->swap_sym_in) (cur_bfd, esh, &sh);
+
+ /* Make sure that this type of cross reference can be handled. */
+ if (sh.sc != scInfo
+ || (sh.st != stBlock && sh.st != stTypedef
+ && sh.st != stStruct && sh.st != stUnion
+ && sh.st != stEnum))
+ {
+ /* File indirect entry is corrupt. */
+ *pname = "<illegal>";
+ complain (&bad_rfd_entry_complaint,
+ sym_name, xref_fd, rn->index);
+ return result;
+ }
+
+ *pname = ecoff_data (cur_bfd)->ss + fh->issBase + sh.iss;
+
+ pend = is_pending_symbol (fh, esh);
+ if (pend)
+ *tpp = pend->t;
+ else
+ {
+ /* We have not yet seen this type. */
+
+ if (sh.iss == 0 && sh.st == stTypedef)
+ {
+ TIR tir;
+
+ /* alpha cc puts out a stTypedef with a sh.iss of zero for
+ two cases:
+ a) forward declarations of structs/unions/enums which are not
+ defined in this compilation unit.
+ For these the type will be void. This is a bad design decision
+ as cross referencing across compilation units is impossible
+ due to the missing name.
+ b) forward declarations of structs/unions/enums which are defined
+ later in this file or in another file in the same compilation
+ unit. Simply cross reference those again to get the
+ true type.
+ The forward references are not entered in the pending list and
+ in the symbol table. */
+
+ ecoff_swap_tir_in (bigend,
+ &(ecoff_data (cur_bfd)->external_aux
+ + fh->iauxBase + sh.index)->a_ti,
+ &tir);
+ if (tir.tq0 != tqNil)
+ complain (&illegal_forward_tq0_complaint, sym_name);
+ switch (tir.bt)
+ {
+ case btVoid:
+ *tpp = init_type (type_code, 0, 0, (char *) NULL,
+ current_objfile);
+ *pname = "<undefined>";
+ break;
+
+ case btStruct:
+ case btUnion:
+ case btEnum:
+ cross_ref (xref_fd,
+ (ecoff_data (cur_bfd)->external_aux
+ + fh->iauxBase + sh.index + 1),
+ tpp, type_code, pname,
+ fh->fBigendian, sym_name);
+ break;
+
+ default:
+ complain (&illegal_forward_bt_complaint, tir.bt, sym_name);
+ *tpp = init_type (type_code, 0, 0, (char *) NULL,
+ current_objfile);
+ break;
+ }
+ return result;
+ }
+ else if (sh.st == stTypedef)
+ {
+ /* Parse the type for a normal typedef. This might recursively call
+ cross_ref till we get a non typedef'ed type.
+ FIXME: This is not correct behaviour, but gdb currently
+ cannot handle typedefs without type copying. But type copying is
+ impossible as we might have mutual forward references between
+ two files and the copied type would not get filled in when
+ we later parse its definition. */
+ *tpp = parse_type (xref_fd,
+ ecoff_data (cur_bfd)->external_aux + fh->iauxBase,
+ sh.index,
+ (int *)NULL,
+ fh->fBigendian,
+ (ecoff_data (cur_bfd)->ss
+ + fh->issBase + sh.iss));
+ }
+ else
+ {
+ /* Cross reference to a struct/union/enum which is defined
+ in another file in the same compilation unit but that file
+ has not been parsed yet.
+ Initialize the type only, it will be filled in when
+ it's definition is parsed. */
+ *tpp = init_type (type_code, 0, 0, (char *) NULL, current_objfile);
+ }
+ add_pending (fh, esh, *tpp);
+ }
+
+ /* We used one auxent normally, two if we got a "next one" rf. */
+ return result;
+}
+
+
+/* Quick&dirty lookup procedure, to avoid the MI ones that require
+ keeping the symtab sorted */
+
+static struct symbol *
+mylookup_symbol (name, block, namespace, class)
+ char *name;
+ register struct block *block;
+ enum namespace namespace;
+ enum address_class class;
+{
+ register int bot, top, inc;
+ register struct symbol *sym;
+
+ bot = 0;
+ top = BLOCK_NSYMS (block);
+ inc = name[0];
+ while (bot < top)
+ {
+ sym = BLOCK_SYM (block, bot);
+ if (SYMBOL_NAME (sym)[0] == inc
+ && SYMBOL_NAMESPACE (sym) == namespace
+ && SYMBOL_CLASS (sym) == class
+ && strcmp (SYMBOL_NAME (sym), name) == 0)
+ return sym;
+ bot++;
+ }
+ block = BLOCK_SUPERBLOCK (block);
+ if (block)
+ return mylookup_symbol (name, block, namespace, class);
+ return 0;
+}
+
+
+/* Add a new symbol S to a block B.
+ Infrequently, we will need to reallocate the block to make it bigger.
+ We only detect this case when adding to top_stack->cur_block, since
+ that's the only time we know how big the block is. FIXME. */
+
+static void
+add_symbol (s, b)
+ struct symbol *s;
+ struct block *b;
+{
+ int nsyms = BLOCK_NSYMS (b)++;
+ struct block *origb;
+ struct parse_stack *stackp;
+
+ if (b == top_stack->cur_block &&
+ nsyms >= top_stack->maxsyms)
+ {
+ complain (&block_overflow_complaint, SYMBOL_NAME (s));
+ /* In this case shrink_block is actually grow_block, since
+ BLOCK_NSYMS(b) is larger than its current size. */
+ origb = b;
+ b = shrink_block (top_stack->cur_block, top_stack->cur_st);
+
+ /* Now run through the stack replacing pointers to the
+ original block. shrink_block has already done this
+ for the blockvector and BLOCK_FUNCTION. */
+ for (stackp = top_stack; stackp; stackp = stackp->next)
+ {
+ if (stackp->cur_block == origb)
+ {
+ stackp->cur_block = b;
+ stackp->maxsyms = BLOCK_NSYMS (b);
+ }
+ }
+ }
+ BLOCK_SYM (b, nsyms) = s;
+}
+
+/* Add a new block B to a symtab S */
+
+static void
+add_block (b, s)
+ struct block *b;
+ struct symtab *s;
+{
+ struct blockvector *bv = BLOCKVECTOR (s);
+
+ bv = (struct blockvector *) xrealloc ((PTR) bv,
+ (sizeof (struct blockvector)
+ + BLOCKVECTOR_NBLOCKS (bv)
+ * sizeof (bv->block)));
+ if (bv != BLOCKVECTOR (s))
+ BLOCKVECTOR (s) = bv;
+
+ BLOCKVECTOR_BLOCK (bv, BLOCKVECTOR_NBLOCKS (bv)++) = b;
+}
+
+/* Add a new linenumber entry (LINENO,ADR) to a linevector LT.
+ MIPS' linenumber encoding might need more than one byte
+ to describe it, LAST is used to detect these continuation lines.
+
+ Combining lines with the same line number seems like a bad idea.
+ E.g: There could be a line number entry with the same line number after the
+ prologue and GDB should not ignore it (this is a better way to find
+ a prologue than mips_skip_prologue).
+ But due to the compressed line table format there are line number entries
+ for the same line which are needed to bridge the gap to the next
+ line number entry. These entries have a bogus address info with them
+ and we are unable to tell them from intended duplicate line number
+ entries.
+ This is another reason why -ggdb debugging format is preferable. */
+
+static int
+add_line (lt, lineno, adr, last)
+ struct linetable *lt;
+ int lineno;
+ CORE_ADDR adr;
+ int last;
+{
+ if (last == 0)
+ last = -2; /* make sure we record first line */
+
+ if (last == lineno) /* skip continuation lines */
+ return lineno;
+
+ lt->item[lt->nitems].line = lineno;
+ lt->item[lt->nitems++].pc = adr << 2;
+ return lineno;
+}
+
+/* Sorting and reordering procedures */
+
+/* Blocks with a smaller low bound should come first */
+
+static int
+compare_blocks (arg1, arg2)
+ const PTR arg1;
+ const PTR arg2;
+{
+ register int addr_diff;
+ struct block **b1 = (struct block **) arg1;
+ struct block **b2 = (struct block **) arg2;
+
+ addr_diff = (BLOCK_START ((*b1))) - (BLOCK_START ((*b2)));
+ if (addr_diff == 0)
+ return (BLOCK_END ((*b2))) - (BLOCK_END ((*b1)));
+ return addr_diff;
+}
+
+/* Sort the blocks of a symtab S.
+ Reorder the blocks in the blockvector by code-address,
+ as required by some MI search routines */
+
+static void
+sort_blocks (s)
+ struct symtab *s;
+{
+ struct blockvector *bv = BLOCKVECTOR (s);
+
+ if (BLOCKVECTOR_NBLOCKS (bv) <= 2)
+ {
+ /* Cosmetic */
+ if (BLOCK_END (BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK)) == 0)
+ BLOCK_START (BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK)) = 0;
+ if (BLOCK_END (BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK)) == 0)
+ BLOCK_START (BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK)) = 0;
+ return;
+ }
+ /*
+ * This is very unfortunate: normally all functions are compiled in
+ * the order they are found, but if the file is compiled -O3 things
+ * are very different. It would be nice to find a reliable test
+ * to detect -O3 images in advance.
+ */
+ if (BLOCKVECTOR_NBLOCKS (bv) > 3)
+ qsort (&BLOCKVECTOR_BLOCK (bv, FIRST_LOCAL_BLOCK),
+ BLOCKVECTOR_NBLOCKS (bv) - FIRST_LOCAL_BLOCK,
+ sizeof (struct block *),
+ compare_blocks);
+
+ {
+ register CORE_ADDR high = 0;
+ register int i, j = BLOCKVECTOR_NBLOCKS (bv);
+
+ for (i = FIRST_LOCAL_BLOCK; i < j; i++)
+ if (high < BLOCK_END (BLOCKVECTOR_BLOCK (bv, i)))
+ high = BLOCK_END (BLOCKVECTOR_BLOCK (bv, i));
+ BLOCK_END (BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK)) = high;
+ }
+
+ BLOCK_START (BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK)) =
+ BLOCK_START (BLOCKVECTOR_BLOCK (bv, FIRST_LOCAL_BLOCK));
+
+ BLOCK_START (BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK)) =
+ BLOCK_START (BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK));
+ BLOCK_END (BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK)) =
+ BLOCK_END (BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK));
+}
+
+
+/* Constructor/restructor/destructor procedures */
+
+/* Allocate a new symtab for NAME. Needs an estimate of how many symbols
+ MAXSYMS and linenumbers MAXLINES we'll put in it */
+
+static struct symtab *
+new_symtab (name, maxsyms, maxlines, objfile)
+ char *name;
+ int maxsyms;
+ int maxlines;
+ struct objfile *objfile;
+{
+ struct symtab *s = allocate_symtab (name, objfile);
+
+ LINETABLE (s) = new_linetable (maxlines);
+
+ /* All symtabs must have at least two blocks */
+ BLOCKVECTOR (s) = new_bvect (2);
+ BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), GLOBAL_BLOCK) = new_block (maxsyms);
+ BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), STATIC_BLOCK) = new_block (maxsyms);
+ BLOCK_SUPERBLOCK (BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), STATIC_BLOCK)) =
+ BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), GLOBAL_BLOCK);
+
+ s->free_code = free_linetable;
+
+ return (s);
+}
+
+/* Allocate a new partial_symtab NAME */
+
+static struct partial_symtab *
+new_psymtab (name, objfile)
+ char *name;
+ struct objfile *objfile;
+{
+ struct partial_symtab *psymtab;
+
+ psymtab = allocate_psymtab (name, objfile);
+
+ /* Keep a backpointer to the file's symbols */
+
+ psymtab->read_symtab_private = ((char *)
+ obstack_alloc (&objfile->psymbol_obstack,
+ sizeof (struct symloc)));
+ memset ((PTR) psymtab->read_symtab_private, 0, sizeof (struct symloc));
+ CUR_BFD (psymtab) = cur_bfd;
+
+ /* The way to turn this into a symtab is to call... */
+ psymtab->read_symtab = mipscoff_psymtab_to_symtab;
+ return (psymtab);
+}
+
+
+/* Allocate a linetable array of the given SIZE. Since the struct
+ already includes one item, we subtract one when calculating the
+ proper size to allocate. */
+
+static struct linetable *
+new_linetable (size)
+ int size;
+{
+ struct linetable *l;
+
+ size = (size - 1) * sizeof (l->item) + sizeof (struct linetable);
+ l = (struct linetable *) xmalloc (size);
+ l->nitems = 0;
+ return l;
+}
+
+/* Oops, too big. Shrink it. This was important with the 2.4 linetables,
+ I am not so sure about the 3.4 ones.
+
+ Since the struct linetable already includes one item, we subtract one when
+ calculating the proper size to allocate. */
+
+static struct linetable *
+shrink_linetable (lt)
+ struct linetable *lt;
+{
+
+ return (struct linetable *) xrealloc ((PTR) lt,
+ (sizeof (struct linetable)
+ + ((lt->nitems - 1)
+ * sizeof (lt->item))));
+}
+
+/* Allocate and zero a new blockvector of NBLOCKS blocks. */
+
+static struct blockvector *
+new_bvect (nblocks)
+ int nblocks;
+{
+ struct blockvector *bv;
+ int size;
+
+ size = sizeof (struct blockvector) + nblocks * sizeof (struct block *);
+ bv = (struct blockvector *) xzalloc (size);
+
+ BLOCKVECTOR_NBLOCKS (bv) = nblocks;
+
+ return bv;
+}
+
+/* Allocate and zero a new block of MAXSYMS symbols */
+
+static struct block *
+new_block (maxsyms)
+ int maxsyms;
+{
+ int size = sizeof (struct block) + (maxsyms - 1) * sizeof (struct symbol *);
+
+ return (struct block *) xzalloc (size);
+}
+
+/* Ooops, too big. Shrink block B in symtab S to its minimal size.
+ Shrink_block can also be used by add_symbol to grow a block. */
+
+static struct block *
+shrink_block (b, s)
+ struct block *b;
+ struct symtab *s;
+{
+ struct block *new;
+ struct blockvector *bv = BLOCKVECTOR (s);
+ int i;
+
+ /* Just reallocate it and fix references to the old one */
+
+ new = (struct block *) xrealloc ((PTR) b,
+ (sizeof (struct block)
+ + ((BLOCK_NSYMS (b) - 1)
+ * sizeof (struct symbol *))));
+
+ /* Should chase pointers to old one. Fortunately, that`s just
+ the block`s function and inferior blocks */
+ if (BLOCK_FUNCTION (new) && SYMBOL_BLOCK_VALUE (BLOCK_FUNCTION (new)) == b)
+ SYMBOL_BLOCK_VALUE (BLOCK_FUNCTION (new)) = new;
+ for (i = 0; i < BLOCKVECTOR_NBLOCKS (bv); i++)
+ if (BLOCKVECTOR_BLOCK (bv, i) == b)
+ BLOCKVECTOR_BLOCK (bv, i) = new;
+ else if (BLOCK_SUPERBLOCK (BLOCKVECTOR_BLOCK (bv, i)) == b)
+ BLOCK_SUPERBLOCK (BLOCKVECTOR_BLOCK (bv, i)) = new;
+ return new;
+}
+
+/* Create a new symbol with printname NAME */
+
+static struct symbol *
+new_symbol (name)
+ char *name;
+{
+ struct symbol *s = ((struct symbol *)
+ obstack_alloc (&current_objfile->symbol_obstack,
+ sizeof (struct symbol)));
+
+ memset ((PTR) s, 0, sizeof (*s));
+ SYMBOL_NAME (s) = name;
+ SYMBOL_LANGUAGE (s) = psymtab_language;
+ SYMBOL_INIT_DEMANGLED_NAME (s, &current_objfile->symbol_obstack);
+ return s;
+}
+
+/* Create a new type with printname NAME */
+
+static struct type *
+new_type (name)
+ char *name;
+{
+ struct type *t;
+
+ t = alloc_type (current_objfile);
+ TYPE_NAME (t) = name;
+ TYPE_CPLUS_SPECIFIC (t) = (struct cplus_struct_type *) &cplus_struct_default;
+ return t;
+}
+
+
+/* Things used for calling functions in the inferior.
+ These functions are exported to our companion
+ mips-tdep.c file and are here because they play
+ with the symbol-table explicitly. */
+
+/* Sigtramp: make sure we have all the necessary information
+ about the signal trampoline code. Since the official code
+ from MIPS does not do so, we make up that information ourselves.
+ If they fix the library (unlikely) this code will neutralize itself. */
+
+static void
+fixup_sigtramp ()
+{
+ struct symbol *s;
+ struct symtab *st;
+ struct block *b, *b0 = NULL;
+
+ sigtramp_address = -1;
+
+ /* We have to handle the following cases here:
+ a) The Mips library has a sigtramp label within sigvec.
+ b) Irix has a _sigtramp which we want to use, but it also has sigvec. */
+ s = lookup_symbol ("sigvec", 0, VAR_NAMESPACE, 0, NULL);
+ if (s != 0)
+ {
+ b0 = SYMBOL_BLOCK_VALUE (s);
+ s = lookup_symbol ("sigtramp", b0, VAR_NAMESPACE, 0, NULL);
+ }
+ if (s == 0)
+ {
+ /* No sigvec or no sigtramp inside sigvec, try _sigtramp. */
+ s = lookup_symbol ("_sigtramp", 0, VAR_NAMESPACE, 0, NULL);
+ }
+
+ /* But maybe this program uses its own version of sigvec */
+ if (s == 0)
+ return;
+
+ /* Did we or MIPSco fix the library ? */
+ if (SYMBOL_CLASS (s) == LOC_BLOCK)
+ {
+ sigtramp_address = BLOCK_START (SYMBOL_BLOCK_VALUE (s));
+ sigtramp_end = BLOCK_END (SYMBOL_BLOCK_VALUE (s));
+ return;
+ }
+
+ sigtramp_address = SYMBOL_VALUE (s);
+ sigtramp_end = sigtramp_address + 0x88; /* black magic */
+
+ /* But what symtab does it live in ? */
+ st = find_pc_symtab (SYMBOL_VALUE (s));
+
+ /*
+ * Ok, there goes the fix: turn it into a procedure, with all the
+ * needed info. Note we make it a nested procedure of sigvec,
+ * which is the way the (assembly) code is actually written.
+ */
+ SYMBOL_NAMESPACE (s) = VAR_NAMESPACE;
+ SYMBOL_CLASS (s) = LOC_BLOCK;
+ SYMBOL_TYPE (s) = init_type (TYPE_CODE_FUNC, 4, 0, (char *) NULL,
+ st->objfile);
+ TYPE_TARGET_TYPE (SYMBOL_TYPE (s)) = builtin_type_void;
+
+ /* Need a block to allocate MIPS_EFI_SYMBOL_NAME in */
+ b = new_block (1);
+ SYMBOL_BLOCK_VALUE (s) = b;
+ BLOCK_START (b) = sigtramp_address;
+ BLOCK_END (b) = sigtramp_end;
+ BLOCK_FUNCTION (b) = s;
+ BLOCK_SUPERBLOCK (b) = BLOCK_SUPERBLOCK (b0);
+ add_block (b, st);
+ sort_blocks (st);
+
+ /* Make a MIPS_EFI_SYMBOL_NAME entry for it */
+ {
+ struct mips_extra_func_info *e =
+ ((struct mips_extra_func_info *)
+ xzalloc (sizeof (struct mips_extra_func_info)));
+
+ e->numargs = 0; /* the kernel thinks otherwise */
+ /* align_longword(sigcontext + SIGFRAME) */
+ e->pdr.frameoffset = 0x150;
+ e->pdr.framereg = SP_REGNUM;
+ /* read_next_frame_reg provides the true pc at the time of signal */
+ e->pdr.pcreg = PC_REGNUM;
+ e->pdr.regmask = -2;
+ e->pdr.regoffset = -(41 * sizeof (int));
+ e->pdr.fregmask = -1;
+ e->pdr.fregoffset = -(7 * sizeof (int));
+ e->pdr.isym = (long) s;
+ e->pdr.adr = sigtramp_address;
+
+ current_objfile = st->objfile; /* Keep new_symbol happy */
+ s = new_symbol (MIPS_EFI_SYMBOL_NAME);
+ SYMBOL_VALUE (s) = (long) e;
+ SYMBOL_NAMESPACE (s) = LABEL_NAMESPACE;
+ SYMBOL_CLASS (s) = LOC_CONST;
+ SYMBOL_TYPE (s) = builtin_type_void;
+ current_objfile = NULL;
+ }
+
+ BLOCK_SYM (b, BLOCK_NSYMS (b)++) = s;
+}
+
+
+/* Fake up identical offsets for all sections. */
+
+struct section_offsets *
+mipscoff_symfile_offsets (objfile, addr)
+ struct objfile *objfile;
+ CORE_ADDR addr;
+{
+ struct section_offsets *section_offsets;
+ int i;
+
+ section_offsets = ((struct section_offsets *)
+ obstack_alloc (&objfile->psymbol_obstack,
+ (sizeof (struct section_offsets)
+ + (sizeof (section_offsets->offsets)
+ * (SECT_OFF_MAX - 1)))));
+
+ for (i = 0; i < SECT_OFF_MAX; i++)
+ ANOFFSET (section_offsets, i) = addr;
+
+ return section_offsets;
+}
+
+/* Initialization */
+
+static struct sym_fns ecoff_sym_fns =
+{
+ "ecoff", /* sym_name: name or name prefix of BFD target type */
+ 5, /* sym_namelen: number of significant sym_name chars */
+ mipscoff_new_init, /* sym_new_init: init anything gbl to entire symtab */
+ mipscoff_symfile_init, /* sym_init: read initial info, setup for sym_read() */
+ mipscoff_symfile_read, /* sym_read: read a symbol file into symtab */
+ mipscoff_symfile_finish, /* sym_finish: finished with file, cleanup */
+ mipscoff_symfile_offsets, /* sym_offsets: dummy FIXME til implem sym reloc */
+ NULL /* next: pointer to next struct sym_fns */
+};
+
+
+void
+_initialize_mipsread ()
+{
+ add_symtab_fns (&ecoff_sym_fns);
+
+ /* Missing basic types */
+
+ builtin_type_string =
+ init_type (TYPE_CODE_STRING,
+ TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+ 0, "string",
+ (struct objfile *) NULL);
+ builtin_type_complex =
+ init_type (TYPE_CODE_FLT,
+ TARGET_COMPLEX_BIT / TARGET_CHAR_BIT,
+ 0, "complex",
+ (struct objfile *) NULL);
+ builtin_type_double_complex =
+ init_type (TYPE_CODE_FLT,
+ TARGET_DOUBLE_COMPLEX_BIT / TARGET_CHAR_BIT,
+ 0, "double complex",
+ (struct objfile *) NULL);
+ builtin_type_fixed_dec =
+ init_type (TYPE_CODE_INT,
+ TARGET_INT_BIT / TARGET_CHAR_BIT,
+ 0, "fixed decimal",
+ (struct objfile *) NULL);
+ builtin_type_float_dec =
+ init_type (TYPE_CODE_FLT,
+ TARGET_DOUBLE_BIT / TARGET_CHAR_BIT,
+ 0, "floating decimal",
+ (struct objfile *) NULL);
+}
diff --git a/gnu/usr.bin/gdb/gdb/nlmread.c b/gnu/usr.bin/gdb/gdb/nlmread.c
new file mode 100644
index 0000000..fde3af2
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/nlmread.c
@@ -0,0 +1,300 @@
+/* Read NLM (NetWare Loadable Module) format executable files for GDB.
+ Copyright 1993 Free Software Foundation, Inc.
+ Written by Fred Fish at Cygnus Support (fnf@cygnus.com).
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "bfd.h"
+#include "symtab.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "gdb-stabs.h"
+
+static void
+nlm_new_init PARAMS ((struct objfile *));
+
+static void
+nlm_symfile_init PARAMS ((struct objfile *));
+
+static void
+nlm_symfile_read PARAMS ((struct objfile *, struct section_offsets *, int));
+
+static void
+nlm_symfile_finish PARAMS ((struct objfile *));
+
+static void
+nlm_symtab_read PARAMS ((bfd *, CORE_ADDR, struct objfile *));
+
+static struct section_offsets *
+nlm_symfile_offsets PARAMS ((struct objfile *, CORE_ADDR));
+
+static void
+record_minimal_symbol PARAMS ((char *, CORE_ADDR, enum minimal_symbol_type,
+ struct objfile *));
+
+
+/* Initialize anything that needs initializing when a completely new symbol
+ file is specified (not just adding some symbols from another file, e.g. a
+ shared library).
+
+ We reinitialize buildsym, since gdb will be able to read stabs from an NLM
+ file at some point in the near future. */
+
+static void
+nlm_new_init (ignore)
+ struct objfile *ignore;
+{
+ stabsread_new_init ();
+ buildsym_new_init ();
+}
+
+
+/* NLM specific initialization routine for reading symbols.
+
+ It is passed a pointer to a struct sym_fns which contains, among other
+ things, the BFD for the file whose symbols are being read, and a slot for
+ a pointer to "private data" which we can fill with goodies.
+
+ For now at least, we have nothing in particular to do, so this function is
+ just a stub. */
+
+static void
+nlm_symfile_init (ignore)
+ struct objfile *ignore;
+{
+}
+
+static void
+record_minimal_symbol (name, address, ms_type, objfile)
+ char *name;
+ CORE_ADDR address;
+ enum minimal_symbol_type ms_type;
+ struct objfile *objfile;
+{
+ name = obsavestring (name, strlen (name), &objfile -> symbol_obstack);
+ prim_record_minimal_symbol (name, address, ms_type);
+}
+
+
+/*
+
+LOCAL FUNCTION
+
+ nlm_symtab_read -- read the symbol table of an NLM file
+
+SYNOPSIS
+
+ void nlm_symtab_read (bfd *abfd, CORE_ADDR addr,
+ struct objfile *objfile)
+
+DESCRIPTION
+
+ Given an open bfd, a base address to relocate symbols to, and a
+ flag that specifies whether or not this bfd is for an executable
+ or not (may be shared library for example), add all the global
+ function and data symbols to the minimal symbol table.
+*/
+
+static void
+nlm_symtab_read (abfd, addr, objfile)
+ bfd *abfd;
+ CORE_ADDR addr;
+ struct objfile *objfile;
+{
+ unsigned int storage_needed;
+ asymbol *sym;
+ asymbol **symbol_table;
+ unsigned int number_of_symbols;
+ unsigned int i;
+ struct cleanup *back_to;
+ CORE_ADDR symaddr;
+ enum minimal_symbol_type ms_type;
+
+ storage_needed = get_symtab_upper_bound (abfd);
+ if (storage_needed > 0)
+ {
+ symbol_table = (asymbol **) xmalloc (storage_needed);
+ back_to = make_cleanup (free, symbol_table);
+ number_of_symbols = bfd_canonicalize_symtab (abfd, symbol_table);
+
+ for (i = 0; i < number_of_symbols; i++)
+ {
+ sym = symbol_table[i];
+ if (sym -> flags & BSF_GLOBAL)
+ {
+ /* Bfd symbols are section relative. */
+ symaddr = sym -> value + sym -> section -> vma;
+ /* Relocate all non-absolute symbols by base address. */
+ if (sym -> section != &bfd_abs_section)
+ {
+ symaddr += addr;
+ }
+
+ /* For non-absolute symbols, use the type of the section
+ they are relative to, to intuit text/data. Bfd provides
+ no way of figuring this out for absolute symbols. */
+ if (sym -> section -> flags & SEC_CODE)
+ {
+ ms_type = mst_text;
+ }
+ else if (sym -> section -> flags & SEC_DATA)
+ {
+ ms_type = mst_data;
+ }
+ else
+ {
+ ms_type = mst_unknown;
+ }
+ record_minimal_symbol ((char *) sym -> name, symaddr, ms_type,
+ objfile);
+ }
+ }
+ do_cleanups (back_to);
+ }
+}
+
+
+/* Scan and build partial symbols for a symbol file.
+ We have been initialized by a call to nlm_symfile_init, which
+ currently does nothing.
+
+ SECTION_OFFSETS is a set of offsets to apply to relocate the symbols
+ in each section. We simplify it down to a single offset for all
+ symbols. FIXME.
+
+ MAINLINE is true if we are reading the main symbol
+ table (as opposed to a shared lib or dynamically loaded file).
+
+ This function only does the minimum work necessary for letting the
+ user "name" things symbolically; it does not read the entire symtab.
+ Instead, it reads the external and static symbols and puts them in partial
+ symbol tables. When more extensive information is requested of a
+ file, the corresponding partial symbol table is mutated into a full
+ fledged symbol table by going back and reading the symbols
+ for real.
+
+ Note that NLM files have two sets of information that is potentially
+ useful for building gdb's minimal symbol table. The first is a list
+ of the publically exported symbols, and is currently used to build
+ bfd's canonical symbol table. The second is an optional native debugging
+ format which contains additional symbols (and possibly duplicates of
+ the publically exported symbols). The optional native debugging format
+ is not currently used. */
+
+static void
+nlm_symfile_read (objfile, section_offsets, mainline)
+ struct objfile *objfile;
+ struct section_offsets *section_offsets;
+ int mainline;
+{
+ bfd *abfd = objfile -> obfd;
+ struct cleanup *back_to;
+ CORE_ADDR offset;
+
+ init_minimal_symbol_collection ();
+ back_to = make_cleanup (discard_minimal_symbols, 0);
+
+ /* FIXME, should take a section_offsets param, not just an offset. */
+
+ offset = ANOFFSET (section_offsets, 0);
+
+ /* Process the NLM export records, which become the bfd's canonical symbol
+ table. */
+
+ nlm_symtab_read (abfd, offset, objfile);
+
+ /* FIXME: We could locate and read the optional native debugging format
+ here and add the symbols to the minimal symbol table. */
+
+ if (!have_partial_symbols ())
+ {
+ wrap_here ("");
+ printf_filtered ("(no debugging symbols found)...");
+ wrap_here ("");
+ }
+
+ /* Install any minimal symbols that have been collected as the current
+ minimal symbols for this objfile. */
+
+ install_minimal_symbols (objfile);
+
+ do_cleanups (back_to);
+}
+
+
+/* Perform any local cleanups required when we are done with a particular
+ objfile. I.E, we are in the process of discarding all symbol information
+ for an objfile, freeing up all memory held for it, and unlinking the
+ objfile struct from the global list of known objfiles. */
+
+static void
+nlm_symfile_finish (objfile)
+ struct objfile *objfile;
+{
+ if (objfile -> sym_private != NULL)
+ {
+ mfree (objfile -> md, objfile -> sym_private);
+ }
+}
+
+/* NLM specific parsing routine for section offsets.
+ FIXME: This may or may not be necessary. All the symbol readers seem
+ to have similar code. See if it can be generalized and moved elsewhere. */
+
+static
+struct section_offsets *
+nlm_symfile_offsets (objfile, addr)
+ struct objfile *objfile;
+ CORE_ADDR addr;
+{
+ struct section_offsets *section_offsets;
+ int i;
+
+ section_offsets = (struct section_offsets *)
+ obstack_alloc (&objfile -> psymbol_obstack,
+ sizeof (struct section_offsets) +
+ sizeof (section_offsets->offsets) * (SECT_OFF_MAX-1));
+
+ for (i = 0; i < SECT_OFF_MAX; i++)
+ {
+ ANOFFSET (section_offsets, i) = addr;
+ }
+
+ return (section_offsets);
+}
+
+
+/* Register that we are able to handle NLM file format. */
+
+static struct sym_fns nlm_sym_fns =
+{
+ "nlm", /* sym_name: name or name prefix of BFD target type */
+ 3, /* sym_namelen: number of significant sym_name chars */
+ nlm_new_init, /* sym_new_init: init anything gbl to entire symtab */
+ nlm_symfile_init, /* sym_init: read initial info, setup for sym_read() */
+ nlm_symfile_read, /* sym_read: read a symbol file into symtab */
+ nlm_symfile_finish, /* sym_finish: finished with file, cleanup */
+ nlm_symfile_offsets, /* sym_offsets: Translate ext. to int. relocation */
+ NULL /* next: pointer to next struct sym_fns */
+};
+
+void
+_initialize_nlmread ()
+{
+ add_symtab_fns (&nlm_sym_fns);
+}
diff --git a/gnu/usr.bin/gdb/gdb/nm.h b/gnu/usr.bin/gdb/gdb/nm.h
new file mode 100644
index 0000000..a7af00f
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/nm.h
@@ -0,0 +1,44 @@
+/* Native-dependent definitions for Intel 386 running BSD Unix, for GDB.
+ Copyright 1986, 1987, 1989, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef NM_FREEBSD_H
+#define NM_FREEBSD_H
+
+/* Be shared lib aware */
+#include "solib.h"
+
+/* This is the amount to subtract from u.u_ar0
+ to get the offset in the core file of the register values. */
+
+#include <machine/vmparam.h>
+#define KERNEL_U_ADDR USRSTACK
+
+/* #undef FLOAT_INFO /* No float info yet */
+#define FLOAT_INFO extern i386_float_info (); \
+ i386_float_info ()
+
+#define REGISTER_U_ADDR(addr, blockend, regno) \
+ (addr) = i386_register_u_addr ((blockend),(regno));
+
+extern int
+i386_register_u_addr PARAMS ((int, int));
+
+#define PTRACE_ARG3_TYPE char*
+
+#endif /* NM_FREEBSD_H */
diff --git a/gnu/usr.bin/gdb/gdb/objfiles.c b/gnu/usr.bin/gdb/gdb/objfiles.c
new file mode 100644
index 0000000..b111f00
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/objfiles.c
@@ -0,0 +1,773 @@
+/* GDB routines for manipulating objfiles.
+ Copyright 1992 Free Software Foundation, Inc.
+ Contributed by Cygnus Support, using pieces from other GDB modules.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This file contains support routines for creating, manipulating, and
+ destroying objfile structures. */
+
+#include "defs.h"
+#include "bfd.h" /* Binary File Description */
+#include "symtab.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "gdb-stabs.h"
+#include "target.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <obstack.h>
+
+/* Prototypes for local functions */
+
+#if !defined(NO_MMALLOC) && defined(HAVE_MMAP)
+
+static int
+open_existing_mapped_file PARAMS ((char *, long, int));
+
+static int
+open_mapped_file PARAMS ((char *filename, long mtime, int mapped));
+
+static CORE_ADDR
+map_to_address PARAMS ((void));
+
+#endif /* !defined(NO_MMALLOC) && defined(HAVE_MMAP) */
+
+/* Message to be printed before the error message, when an error occurs. */
+
+extern char *error_pre_print;
+
+/* Externally visible variables that are owned by this module.
+ See declarations in objfile.h for more info. */
+
+struct objfile *object_files; /* Linked list of all objfiles */
+struct objfile *current_objfile; /* For symbol file being read in */
+struct objfile *symfile_objfile; /* Main symbol table loaded from */
+
+int mapped_symbol_files; /* Try to use mapped symbol files */
+
+/* Locate all mappable sections of a BFD file.
+ objfile_p_char is a char * to get it through
+ bfd_map_over_sections; we cast it back to its proper type. */
+
+static void
+add_to_objfile_sections (abfd, asect, objfile_p_char)
+ bfd *abfd;
+ sec_ptr asect;
+ PTR objfile_p_char;
+{
+ struct objfile *objfile = (struct objfile *) objfile_p_char;
+ struct obj_section section;
+ flagword aflag;
+
+ aflag = bfd_get_section_flags (abfd, asect);
+ /* FIXME, we need to handle BSS segment here...it alloc's but doesn't load */
+ if (!(aflag & SEC_LOAD))
+ return;
+ if (0 == bfd_section_size (abfd, asect))
+ return;
+ section.offset = 0;
+ section.objfile = objfile;
+ section.sec_ptr = asect;
+ section.addr = bfd_section_vma (abfd, asect);
+ section.endaddr = section.addr + bfd_section_size (abfd, asect);
+ obstack_grow (&objfile->psymbol_obstack, &section, sizeof(section));
+ objfile->sections_end = (struct obj_section *) (((unsigned long) objfile->sections_end) + 1);
+}
+
+/* Builds a section table for OBJFILE.
+ Returns 0 if OK, 1 on error. */
+
+static int
+build_objfile_section_table (objfile)
+ struct objfile *objfile;
+{
+ if (objfile->sections)
+ abort();
+
+ objfile->sections_end = 0;
+ bfd_map_over_sections (objfile->obfd, add_to_objfile_sections, (char *)objfile);
+ objfile->sections = (struct obj_section *)
+ obstack_finish (&objfile->psymbol_obstack);
+ objfile->sections_end = objfile->sections + (unsigned long) objfile->sections_end;
+ return(0);
+}
+
+/* Given a pointer to an initialized bfd (ABFD) and a flag that indicates
+ whether or not an objfile is to be mapped (MAPPED), allocate a new objfile
+ struct, fill it in as best we can, link it into the list of all known
+ objfiles, and return a pointer to the new objfile struct. */
+
+struct objfile *
+allocate_objfile (abfd, mapped)
+ bfd *abfd;
+ int mapped;
+{
+ struct objfile *objfile = NULL;
+ int fd;
+ PTR md;
+ CORE_ADDR mapto;
+
+ mapped |= mapped_symbol_files;
+
+#if !defined(NO_MMALLOC) && defined(HAVE_MMAP)
+
+ /* If we can support mapped symbol files, try to open/reopen the mapped file
+ that corresponds to the file from which we wish to read symbols. If the
+ objfile is to be mapped, we must malloc the structure itself using the
+ mmap version, and arrange that all memory allocation for the objfile uses
+ the mmap routines. If we are reusing an existing mapped file, from which
+ we get our objfile pointer, we have to make sure that we update the
+ pointers to the alloc/free functions in the obstack, in case these
+ functions have moved within the current gdb. */
+
+ fd = open_mapped_file (bfd_get_filename (abfd), bfd_get_mtime (abfd),
+ mapped);
+ if (fd >= 0)
+ {
+ if (((mapto = map_to_address ()) == 0) ||
+ ((md = mmalloc_attach (fd, (PTR) mapto)) == NULL))
+ {
+ close (fd);
+ }
+ else if ((objfile = (struct objfile *) mmalloc_getkey (md, 0)) != NULL)
+ {
+ /* Update memory corruption handler function addresses. */
+ init_malloc (md);
+ objfile -> md = md;
+ objfile -> mmfd = fd;
+ /* Update pointers to functions to *our* copies */
+ obstack_chunkfun (&objfile -> psymbol_obstack, xmmalloc);
+ obstack_freefun (&objfile -> psymbol_obstack, mfree);
+ obstack_chunkfun (&objfile -> symbol_obstack, xmmalloc);
+ obstack_freefun (&objfile -> symbol_obstack, mfree);
+ obstack_chunkfun (&objfile -> type_obstack, xmmalloc);
+ obstack_freefun (&objfile -> type_obstack, mfree);
+ /* If already in objfile list, unlink it. */
+ unlink_objfile (objfile);
+ /* Forget things specific to a particular gdb, may have changed. */
+ objfile -> sf = NULL;
+ }
+ else
+ {
+ /* Set up to detect internal memory corruption. MUST be done before
+ the first malloc. See comments in init_malloc() and mmcheck(). */
+ init_malloc (md);
+ objfile = (struct objfile *) xmmalloc (md, sizeof (struct objfile));
+ memset (objfile, 0, sizeof (struct objfile));
+ objfile -> md = md;
+ objfile -> mmfd = fd;
+ objfile -> flags |= OBJF_MAPPED;
+ mmalloc_setkey (objfile -> md, 0, objfile);
+ obstack_specify_allocation_with_arg (&objfile -> psymbol_obstack,
+ 0, 0, xmmalloc, mfree,
+ objfile -> md);
+ obstack_specify_allocation_with_arg (&objfile -> symbol_obstack,
+ 0, 0, xmmalloc, mfree,
+ objfile -> md);
+ obstack_specify_allocation_with_arg (&objfile -> type_obstack,
+ 0, 0, xmmalloc, mfree,
+ objfile -> md);
+ }
+ }
+
+ if (mapped && (objfile == NULL))
+ {
+ warning ("symbol table for '%s' will not be mapped",
+ bfd_get_filename (abfd));
+ }
+
+#else /* defined(NO_MMALLOC) || !defined(HAVE_MMAP) */
+
+ if (mapped)
+ {
+ warning ("this version of gdb does not support mapped symbol tables.");
+
+ /* Turn off the global flag so we don't try to do mapped symbol tables
+ any more, which shuts up gdb unless the user specifically gives the
+ "mapped" keyword again. */
+
+ mapped_symbol_files = 0;
+ }
+
+#endif /* !defined(NO_MMALLOC) && defined(HAVE_MMAP) */
+
+ /* If we don't support mapped symbol files, didn't ask for the file to be
+ mapped, or failed to open the mapped file for some reason, then revert
+ back to an unmapped objfile. */
+
+ if (objfile == NULL)
+ {
+ objfile = (struct objfile *) xmalloc (sizeof (struct objfile));
+ memset (objfile, 0, sizeof (struct objfile));
+ objfile -> md = NULL;
+ obstack_specify_allocation (&objfile -> psymbol_obstack, 0, 0, xmalloc,
+ free);
+ obstack_specify_allocation (&objfile -> symbol_obstack, 0, 0, xmalloc,
+ free);
+ obstack_specify_allocation (&objfile -> type_obstack, 0, 0, xmalloc,
+ free);
+ }
+
+ /* Update the per-objfile information that comes from the bfd, ensuring
+ that any data that is reference is saved in the per-objfile data
+ region. */
+
+ objfile -> obfd = abfd;
+ if (objfile -> name != NULL)
+ {
+ mfree (objfile -> md, objfile -> name);
+ }
+ objfile -> name = mstrsave (objfile -> md, bfd_get_filename (abfd));
+ objfile -> mtime = bfd_get_mtime (abfd);
+
+ /* Build section table. */
+
+ if (build_objfile_section_table (objfile))
+ {
+ error ("Can't find the file sections in `%s': %s",
+ objfile -> name, bfd_errmsg (bfd_error));
+ }
+
+ /* Push this file onto the head of the linked list of other such files. */
+
+ objfile -> next = object_files;
+ object_files = objfile;
+
+ return (objfile);
+}
+
+/* Unlink OBJFILE from the list of known objfiles, if it is found in the
+ list.
+
+ It is not a bug, or error, to call this function if OBJFILE is not known
+ to be in the current list. This is done in the case of mapped objfiles,
+ for example, just to ensure that the mapped objfile doesn't appear twice
+ in the list. Since the list is threaded, linking in a mapped objfile
+ twice would create a circular list.
+
+ If OBJFILE turns out to be in the list, we zap it's NEXT pointer after
+ unlinking it, just to ensure that we have completely severed any linkages
+ between the OBJFILE and the list. */
+
+void
+unlink_objfile (objfile)
+ struct objfile *objfile;
+{
+ struct objfile** objpp;
+
+ for (objpp = &object_files; *objpp != NULL; objpp = &((*objpp) -> next))
+ {
+ if (*objpp == objfile)
+ {
+ *objpp = (*objpp) -> next;
+ objfile -> next = NULL;
+ break;
+ }
+ }
+}
+
+
+/* Destroy an objfile and all the symtabs and psymtabs under it. Note
+ that as much as possible is allocated on the symbol_obstack and
+ psymbol_obstack, so that the memory can be efficiently freed.
+
+ Things which we do NOT free because they are not in malloc'd memory
+ or not in memory specific to the objfile include:
+
+ objfile -> sf
+
+ FIXME: If the objfile is using reusable symbol information (via mmalloc),
+ then we need to take into account the fact that more than one process
+ may be using the symbol information at the same time (when mmalloc is
+ extended to support cooperative locking). When more than one process
+ is using the mapped symbol info, we need to be more careful about when
+ we free objects in the reusable area. */
+
+void
+free_objfile (objfile)
+ struct objfile *objfile;
+{
+ int mmfd;
+
+ /* First do any symbol file specific actions required when we are
+ finished with a particular symbol file. Note that if the objfile
+ is using reusable symbol information (via mmalloc) then each of
+ these routines is responsible for doing the correct thing, either
+ freeing things which are valid only during this particular gdb
+ execution, or leaving them to be reused during the next one. */
+
+ if (objfile -> sf != NULL)
+ {
+ (*objfile -> sf -> sym_finish) (objfile);
+ }
+
+ /* We always close the bfd. */
+
+ if (objfile -> obfd != NULL)
+ {
+ char *name = bfd_get_filename (objfile->obfd);
+ bfd_close (objfile -> obfd);
+ free (name);
+ }
+
+ /* Remove it from the chain of all objfiles. */
+
+ unlink_objfile (objfile);
+
+ /* Before the symbol table code was redone to make it easier to
+ selectively load and remove information particular to a specific
+ linkage unit, gdb used to do these things whenever the monolithic
+ symbol table was blown away. How much still needs to be done
+ is unknown, but we play it safe for now and keep each action until
+ it is shown to be no longer needed. */
+
+#if defined (CLEAR_SOLIB)
+ CLEAR_SOLIB ();
+ /* CLEAR_SOLIB closes the bfd's for any shared libraries. But
+ the to_sections for a core file might refer to those bfd's. So
+ detach any core file. */
+ {
+ struct target_ops *t = find_core_target ();
+ if (t != NULL)
+ (t->to_detach) (NULL, 0);
+ }
+#endif
+ clear_pc_function_cache ();
+
+ /* The last thing we do is free the objfile struct itself for the
+ non-reusable case, or detach from the mapped file for the reusable
+ case. Note that the mmalloc_detach or the mfree is the last thing
+ we can do with this objfile. */
+
+#if !defined(NO_MMALLOC) && defined(HAVE_MMAP)
+
+ if (objfile -> flags & OBJF_MAPPED)
+ {
+ /* Remember the fd so we can close it. We can't close it before
+ doing the detach, and after the detach the objfile is gone. */
+ mmfd = objfile -> mmfd;
+ mmalloc_detach (objfile -> md);
+ objfile = NULL;
+ close (mmfd);
+ }
+
+#endif /* !defined(NO_MMALLOC) && defined(HAVE_MMAP) */
+
+ /* If we still have an objfile, then either we don't support reusable
+ objfiles or this one was not reusable. So free it normally. */
+
+ if (objfile != NULL)
+ {
+ if (objfile -> name != NULL)
+ {
+ mfree (objfile -> md, objfile -> name);
+ }
+ if (objfile->global_psymbols.list)
+ mfree (objfile->md, objfile->global_psymbols.list);
+ if (objfile->static_psymbols.list)
+ mfree (objfile->md, objfile->static_psymbols.list);
+ /* Free the obstacks for non-reusable objfiles */
+ obstack_free (&objfile -> psymbol_obstack, 0);
+ obstack_free (&objfile -> symbol_obstack, 0);
+ obstack_free (&objfile -> type_obstack, 0);
+ mfree (objfile -> md, objfile);
+ objfile = NULL;
+ }
+}
+
+
+/* Free all the object files at once and clean up their users. */
+
+void
+free_all_objfiles ()
+{
+ struct objfile *objfile, *temp;
+
+ ALL_OBJFILES_SAFE (objfile, temp)
+ {
+ free_objfile (objfile);
+ }
+ clear_symtab_users ();
+}
+
+/* Relocate OBJFILE to NEW_OFFSETS. There should be OBJFILE->NUM_SECTIONS
+ entries in new_offsets. */
+void
+objfile_relocate (objfile, new_offsets)
+ struct objfile *objfile;
+ struct section_offsets *new_offsets;
+{
+ struct section_offsets *delta = (struct section_offsets *) alloca
+ (sizeof (struct section_offsets)
+ + objfile->num_sections * sizeof (delta->offsets));
+
+ {
+ int i;
+ int something_changed = 0;
+ for (i = 0; i < objfile->num_sections; ++i)
+ {
+ ANOFFSET (delta, i) =
+ ANOFFSET (new_offsets, i) - ANOFFSET (objfile->section_offsets, i);
+ if (ANOFFSET (delta, i) != 0)
+ something_changed = 1;
+ }
+ if (!something_changed)
+ return;
+ }
+
+ /* OK, get all the symtabs. */
+ {
+ struct symtab *s;
+
+ for (s = objfile->symtabs; s; s = s->next)
+ {
+ struct linetable *l;
+ struct blockvector *bv;
+ int i;
+
+ /* First the line table. */
+ l = LINETABLE (s);
+ if (l)
+ {
+ for (i = 0; i < l->nitems; ++i)
+ l->item[i].pc += ANOFFSET (delta, s->block_line_section);
+ }
+
+ /* Don't relocate a shared blockvector more than once. */
+ if (!s->primary)
+ continue;
+
+ bv = BLOCKVECTOR (s);
+ for (i = 0; i < BLOCKVECTOR_NBLOCKS (bv); ++i)
+ {
+ struct block *b;
+ int j;
+
+ b = BLOCKVECTOR_BLOCK (bv, i);
+ BLOCK_START (b) += ANOFFSET (delta, s->block_line_section);
+ BLOCK_END (b) += ANOFFSET (delta, s->block_line_section);
+
+ for (j = 0; j < BLOCK_NSYMS (b); ++j)
+ {
+ struct symbol *sym = BLOCK_SYM (b, j);
+ /* The RS6000 code from which this was taken skipped
+ any symbols in STRUCT_NAMESPACE or UNDEF_NAMESPACE.
+ But I'm leaving out that test, on the theory that
+ they can't possibly pass the tests below. */
+ if ((SYMBOL_CLASS (sym) == LOC_LABEL
+ || SYMBOL_CLASS (sym) == LOC_STATIC)
+ && SYMBOL_SECTION (sym) >= 0)
+ {
+ SYMBOL_VALUE_ADDRESS (sym) +=
+ ANOFFSET (delta, SYMBOL_SECTION (sym));
+ }
+ }
+ }
+ }
+ }
+
+ {
+ struct partial_symtab *p;
+
+ ALL_OBJFILE_PSYMTABS (objfile, p)
+ {
+ /* FIXME: specific to symbol readers which use gdb-stabs.h.
+ We can only get away with it since objfile_relocate is only
+ used on XCOFF, which lacks psymtabs, and for gdb-stabs.h
+ targets. */
+ p->textlow += ANOFFSET (delta, SECT_OFF_TEXT);
+ p->texthigh += ANOFFSET (delta, SECT_OFF_TEXT);
+ }
+ }
+
+ {
+ struct partial_symbol *psym;
+
+ for (psym = objfile->global_psymbols.list;
+ psym < objfile->global_psymbols.next;
+ psym++)
+ if (SYMBOL_SECTION (psym) >= 0)
+ SYMBOL_VALUE_ADDRESS (psym) += ANOFFSET (delta, SYMBOL_SECTION (psym));
+ for (psym = objfile->static_psymbols.list;
+ psym < objfile->static_psymbols.next;
+ psym++)
+ if (SYMBOL_SECTION (psym) >= 0)
+ SYMBOL_VALUE_ADDRESS (psym) += ANOFFSET (delta, SYMBOL_SECTION (psym));
+ }
+
+ {
+ struct minimal_symbol *msym;
+ ALL_OBJFILE_MSYMBOLS (objfile, msym)
+ if (SYMBOL_SECTION (msym) >= 0)
+ SYMBOL_VALUE_ADDRESS (msym) += ANOFFSET (delta, SYMBOL_SECTION (msym));
+ }
+
+ {
+ int i;
+ for (i = 0; i < objfile->num_sections; ++i)
+ ANOFFSET (objfile->section_offsets, i) = ANOFFSET (new_offsets, i);
+ }
+}
+
+/* Many places in gdb want to test just to see if we have any partial
+ symbols available. This function returns zero if none are currently
+ available, nonzero otherwise. */
+
+int
+have_partial_symbols ()
+{
+ struct objfile *ofp;
+
+ ALL_OBJFILES (ofp)
+ {
+ if (ofp -> psymtabs != NULL)
+ {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Many places in gdb want to test just to see if we have any full
+ symbols available. This function returns zero if none are currently
+ available, nonzero otherwise. */
+
+int
+have_full_symbols ()
+{
+ struct objfile *ofp;
+
+ ALL_OBJFILES (ofp)
+ {
+ if (ofp -> symtabs != NULL)
+ {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Many places in gdb want to test just to see if we have any minimal
+ symbols available. This function returns zero if none are currently
+ available, nonzero otherwise. */
+
+int
+have_minimal_symbols ()
+{
+ struct objfile *ofp;
+
+ ALL_OBJFILES (ofp)
+ {
+ if (ofp -> msymbols != NULL)
+ {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#if !defined(NO_MMALLOC) && defined(HAVE_MMAP)
+
+/* Given the name of a mapped symbol file in SYMSFILENAME, and the timestamp
+ of the corresponding symbol file in MTIME, try to open an existing file
+ with the name SYMSFILENAME and verify it is more recent than the base
+ file by checking it's timestamp against MTIME.
+
+ If SYMSFILENAME does not exist (or can't be stat'd), simply returns -1.
+
+ If SYMSFILENAME does exist, but is out of date, we check to see if the
+ user has specified creation of a mapped file. If so, we don't issue
+ any warning message because we will be creating a new mapped file anyway,
+ overwriting the old one. If not, then we issue a warning message so that
+ the user will know why we aren't using this existing mapped symbol file.
+ In either case, we return -1.
+
+ If SYMSFILENAME does exist and is not out of date, but can't be opened for
+ some reason, then prints an appropriate system error message and returns -1.
+
+ Otherwise, returns the open file descriptor. */
+
+static int
+open_existing_mapped_file (symsfilename, mtime, mapped)
+ char *symsfilename;
+ long mtime;
+ int mapped;
+{
+ int fd = -1;
+ struct stat sbuf;
+
+ if (stat (symsfilename, &sbuf) == 0)
+ {
+ if (sbuf.st_mtime < mtime)
+ {
+ if (!mapped)
+ {
+ warning ("mapped symbol file `%s' is out of date, ignored it",
+ symsfilename);
+ }
+ }
+ else if ((fd = open (symsfilename, O_RDWR)) < 0)
+ {
+ if (error_pre_print)
+ {
+ printf (error_pre_print);
+ }
+ print_sys_errmsg (symsfilename, errno);
+ }
+ }
+ return (fd);
+}
+
+/* Look for a mapped symbol file that corresponds to FILENAME and is more
+ recent than MTIME. If MAPPED is nonzero, the user has asked that gdb
+ use a mapped symbol file for this file, so create a new one if one does
+ not currently exist.
+
+ If found, then return an open file descriptor for the file, otherwise
+ return -1.
+
+ This routine is responsible for implementing the policy that generates
+ the name of the mapped symbol file from the name of a file containing
+ symbols that gdb would like to read. Currently this policy is to append
+ ".syms" to the name of the file.
+
+ This routine is also responsible for implementing the policy that
+ determines where the mapped symbol file is found (the search path).
+ This policy is that when reading an existing mapped file, a file of
+ the correct name in the current directory takes precedence over a
+ file of the correct name in the same directory as the symbol file.
+ When creating a new mapped file, it is always created in the current
+ directory. This helps to minimize the chances of a user unknowingly
+ creating big mapped files in places like /bin and /usr/local/bin, and
+ allows a local copy to override a manually installed global copy (in
+ /bin for example). */
+
+static int
+open_mapped_file (filename, mtime, mapped)
+ char *filename;
+ long mtime;
+ int mapped;
+{
+ int fd;
+ char *symsfilename;
+
+ /* First try to open an existing file in the current directory, and
+ then try the directory where the symbol file is located. */
+
+ symsfilename = concat ("./", basename (filename), ".syms", (char *) NULL);
+ if ((fd = open_existing_mapped_file (symsfilename, mtime, mapped)) < 0)
+ {
+ free (symsfilename);
+ symsfilename = concat (filename, ".syms", (char *) NULL);
+ fd = open_existing_mapped_file (symsfilename, mtime, mapped);
+ }
+
+ /* If we don't have an open file by now, then either the file does not
+ already exist, or the base file has changed since it was created. In
+ either case, if the user has specified use of a mapped file, then
+ create a new mapped file, truncating any existing one. If we can't
+ create one, print a system error message saying why we can't.
+
+ By default the file is rw for everyone, with the user's umask taking
+ care of turning off the permissions the user wants off. */
+
+ if ((fd < 0) && mapped)
+ {
+ free (symsfilename);
+ symsfilename = concat ("./", basename (filename), ".syms",
+ (char *) NULL);
+ if ((fd = open (symsfilename, O_RDWR | O_CREAT | O_TRUNC, 0666)) < 0)
+ {
+ if (error_pre_print)
+ {
+ printf (error_pre_print);
+ }
+ print_sys_errmsg (symsfilename, errno);
+ }
+ }
+
+ free (symsfilename);
+ return (fd);
+}
+
+/* Return the base address at which we would like the next objfile's
+ mapped data to start.
+
+ For now, we use the kludge that the configuration specifies a base
+ address to which it is safe to map the first mmalloc heap, and an
+ increment to add to this address for each successive heap. There are
+ a lot of issues to deal with here to make this work reasonably, including:
+
+ Avoid memory collisions with existing mapped address spaces
+
+ Reclaim address spaces when their mmalloc heaps are unmapped
+
+ When mmalloc heaps are shared between processes they have to be
+ mapped at the same addresses in each
+
+ Once created, a mmalloc heap that is to be mapped back in must be
+ mapped at the original address. I.E. each objfile will expect to
+ be remapped at it's original address. This becomes a problem if
+ the desired address is already in use.
+
+ etc, etc, etc.
+
+ */
+
+
+static CORE_ADDR
+map_to_address ()
+{
+
+#if defined(MMAP_BASE_ADDRESS) && defined (MMAP_INCREMENT)
+
+ static CORE_ADDR next = MMAP_BASE_ADDRESS;
+ CORE_ADDR mapto = next;
+
+ next += MMAP_INCREMENT;
+ return (mapto);
+
+#else
+
+ return (0);
+
+#endif
+
+}
+
+#endif /* !defined(NO_MMALLOC) && defined(HAVE_MMAP) */
+
+/* Returns a section whose range includes PC or NULL if none found. */
+
+struct obj_section *
+find_pc_section(pc)
+ CORE_ADDR pc;
+{
+ struct obj_section *s;
+ struct objfile *objfile;
+
+ ALL_OBJFILES (objfile)
+ for (s = objfile->sections; s < objfile->sections_end; ++s)
+ if (s->addr <= pc
+ && pc < s->endaddr)
+ return(s);
+
+ return(NULL);
+}
diff --git a/gnu/usr.bin/gdb/gdb/objfiles.h b/gnu/usr.bin/gdb/gdb/objfiles.h
new file mode 100644
index 0000000..50226ff
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/objfiles.h
@@ -0,0 +1,437 @@
+/* Definitions for symbol file management in GDB.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (OBJFILES_H)
+#define OBJFILES_H
+
+/* This structure maintains information on a per-objfile basis about the
+ "entry point" of the objfile, and the scope within which the entry point
+ exists. It is possible that gdb will see more than one objfile that is
+ executable, each with it's own entry point.
+
+ For example, for dynamically linked executables in SVR4, the dynamic linker
+ code is contained within the shared C library, which is actually executable
+ and is run by the kernel first when an exec is done of a user executable
+ that is dynamically linked. The dynamic linker within the shared C library
+ then maps in the various program segments in the user executable and jumps
+ to the user executable's recorded entry point, as if the call had been made
+ directly by the kernel.
+
+ The traditional gdb method of using this info is to use the recorded entry
+ point to set the variables entry_file_lowpc and entry_file_highpc from
+ the debugging information, where these values are the starting address
+ (inclusive) and ending address (exclusive) of the instruction space in the
+ executable which correspond to the "startup file", I.E. crt0.o in most
+ cases. This file is assumed to be a startup file and frames with pc's
+ inside it are treated as nonexistent. Setting these variables is necessary
+ so that backtraces do not fly off the bottom of the stack (or top, depending
+ upon your stack orientation).
+
+ Gdb also supports an alternate method to avoid running off the top/bottom
+ of the stack.
+
+ There are two frames that are "special", the frame for the function
+ containing the process entry point, since it has no predecessor frame,
+ and the frame for the function containing the user code entry point
+ (the main() function), since all the predecessor frames are for the
+ process startup code. Since we have no guarantee that the linked
+ in startup modules have any debugging information that gdb can use,
+ we need to avoid following frame pointers back into frames that might
+ have been built in the startup code, as we might get hopelessly
+ confused. However, we almost always have debugging information
+ available for main().
+
+ These variables are used to save the range of PC values which are valid
+ within the main() function and within the function containing the process
+ entry point. If we always consider the frame for main() as the outermost
+ frame when debugging user code, and the frame for the process entry
+ point function as the outermost frame when debugging startup code, then
+ all we have to do is have FRAME_CHAIN_VALID return false whenever a
+ frame's current PC is within the range specified by these variables.
+ In essence, we set "ceilings" in the frame chain beyond which we will
+ not proceed when following the frame chain back up the stack.
+
+ A nice side effect is that we can still debug startup code without
+ running off the end of the frame chain, assuming that we have usable
+ debugging information in the startup modules, and if we choose to not
+ use the block at main, or can't find it for some reason, everything
+ still works as before. And if we have no startup code debugging
+ information but we do have usable information for main(), backtraces
+ from user code don't go wandering off into the startup code.
+
+ To use this method, define your FRAME_CHAIN_VALID macro like:
+
+ #define FRAME_CHAIN_VALID(chain, thisframe) \
+ (chain != 0 \
+ && !(inside_main_func ((thisframe)->pc)) \
+ && !(inside_entry_func ((thisframe)->pc)))
+
+ and add initializations of the four scope controlling variables inside
+ the object file / debugging information processing modules. */
+
+struct entry_info
+{
+
+ /* The value we should use for this objects entry point.
+ The illegal/unknown value needs to be something other than 0, ~0
+ for instance, which is much less likely than 0. */
+
+ CORE_ADDR entry_point;
+
+ /* Start (inclusive) and end (exclusive) of function containing the
+ entry point. */
+
+ CORE_ADDR entry_func_lowpc;
+ CORE_ADDR entry_func_highpc;
+
+ /* Start (inclusive) and end (exclusive) of object file containing the
+ entry point. */
+
+ CORE_ADDR entry_file_lowpc;
+ CORE_ADDR entry_file_highpc;
+
+ /* Start (inclusive) and end (exclusive) of the user code main() function. */
+
+ CORE_ADDR main_func_lowpc;
+ CORE_ADDR main_func_highpc;
+
+};
+
+
+/* Sections in an objfile.
+
+ It is strange that we have both this notion of "sections"
+ and the one used by section_offsets. Section as used
+ here, (currently at least) means a BFD section, and the sections
+ are set up from the BFD sections in allocate_objfile.
+
+ The sections in section_offsets have their meaning determined by
+ the symbol format, and they are set up by the sym_offsets function
+ for that symbol file format.
+
+ I'm not sure this could or should be changed, however. */
+
+struct obj_section {
+ CORE_ADDR addr; /* lowest address in section */
+ CORE_ADDR endaddr; /* 1+highest address in section */
+
+ /* This field is being used for nefarious purposes by syms_from_objfile.
+ It is said to be redundant with section_offsets; it's not really being
+ used that way, however, it's some sort of hack I don't understand
+ and am not going to try to eliminate (yet, anyway). FIXME.
+
+ It was documented as "offset between (end)addr and actual memory
+ addresses", but that's not true; addr & endaddr are actual memory
+ addresses. */
+ CORE_ADDR offset;
+
+ sec_ptr sec_ptr; /* BFD section pointer */
+
+ /* Objfile this section is part of. Not currently used, but I'm sure
+ that someone will want the bfd that the sec_ptr goes with or something
+ like that before long. */
+ struct objfile *objfile;
+};
+
+/* Master structure for keeping track of each input file from which
+ gdb reads symbols. One of these is allocated for each such file we
+ access, e.g. the exec_file, symbol_file, and any shared library object
+ files. */
+
+struct objfile
+{
+
+ /* All struct objfile's are chained together by their next pointers.
+ The global variable "object_files" points to the first link in this
+ chain.
+
+ FIXME: There is a problem here if the objfile is reusable, and if
+ multiple users are to be supported. The problem is that the objfile
+ list is linked through a member of the objfile struct itself, which
+ is only valid for one gdb process. The list implementation needs to
+ be changed to something like:
+
+ struct list {struct list *next; struct objfile *objfile};
+
+ where the list structure is completely maintained separately within
+ each gdb process. */
+
+ struct objfile *next;
+
+ /* The object file's name. Malloc'd; free it if you free this struct. */
+
+ char *name;
+
+ /* Some flag bits for this objfile. */
+
+ unsigned short flags;
+
+ /* Each objfile points to a linked list of symtabs derived from this file,
+ one symtab structure for each compilation unit (source file). Each link
+ in the symtab list contains a backpointer to this objfile. */
+
+ struct symtab *symtabs;
+
+ /* Each objfile points to a linked list of partial symtabs derived from
+ this file, one partial symtab structure for each compilation unit
+ (source file). */
+
+ struct partial_symtab *psymtabs;
+
+ /* List of freed partial symtabs, available for re-use */
+
+ struct partial_symtab *free_psymtabs;
+
+ /* The object file's BFD. Can be null, in which case bfd_open (name) and
+ put the result here. */
+
+ bfd *obfd;
+
+ /* The modification timestamp of the object file, as of the last time
+ we read its symbols. */
+
+ long mtime;
+
+ /* Obstacks to hold objects that should be freed when we load a new symbol
+ table from this object file. */
+
+ struct obstack psymbol_obstack; /* Partial symbols */
+ struct obstack symbol_obstack; /* Full symbols */
+ struct obstack type_obstack; /* Types */
+
+ /* Vectors of all partial symbols read in from file. The actual data
+ is stored in the psymbol_obstack. */
+
+ struct psymbol_allocation_list global_psymbols;
+ struct psymbol_allocation_list static_psymbols;
+
+ /* Each file contains a pointer to an array of minimal symbols for all
+ global symbols that are defined within the file. The array is terminated
+ by a "null symbol", one that has a NULL pointer for the name and a zero
+ value for the address. This makes it easy to walk through the array
+ when passed a pointer to somewhere in the middle of it. There is also
+ a count of the number of symbols, which does include the terminating
+ null symbol. The array itself, as well as all the data that it points
+ to, should be allocated on the symbol_obstack for this file. */
+
+ struct minimal_symbol *msymbols;
+ int minimal_symbol_count;
+
+ /* For object file formats which don't specify fundamental types, gdb
+ can create such types. For now, it maintains a vector of pointers
+ to these internally created fundamental types on a per objfile basis,
+ however it really should ultimately keep them on a per-compilation-unit
+ basis, to account for linkage-units that consist of a number of
+ compilation units that may have different fundamental types, such as
+ linking C modules with ADA modules, or linking C modules that are
+ compiled with 32-bit ints with C modules that are compiled with 64-bit
+ ints (not inherently evil with a smarter linker). */
+
+ struct type **fundamental_types;
+
+ /* The mmalloc() malloc-descriptor for this objfile if we are using
+ the memory mapped malloc() package to manage storage for this objfile's
+ data. NULL if we are not. */
+
+ PTR md;
+
+ /* The file descriptor that was used to obtain the mmalloc descriptor
+ for this objfile. If we call mmalloc_detach with the malloc descriptor
+ we should then close this file descriptor. */
+
+ int mmfd;
+
+ /* Structure which keeps track of functions that manipulate objfile's
+ of the same type as this objfile. I.E. the function to read partial
+ symbols for example. Note that this structure is in statically
+ allocated memory, and is shared by all objfiles that use the
+ object module reader of this type. */
+
+ struct sym_fns *sf;
+
+ /* The per-objfile information about the entry point, the scope (file/func)
+ containing the entry point, and the scope of the user's main() func. */
+
+ struct entry_info ei;
+
+ /* Information about stabs. Will be filled in with a dbx_symfile_info
+ struct by those readers that need it. */
+
+ PTR sym_stab_info;
+
+ /* Hook for information for use by the symbol reader (currently used
+ for information shared by sym_init and sym_read). It is
+ typically a pointer to malloc'd memory. The symbol reader's finish
+ function is responsible for freeing the memory thusly allocated. */
+
+ PTR sym_private;
+
+ /* Hook for target-architecture-specific information. This must
+ point to memory allocated on one of the obstacks in this objfile,
+ so that it gets freed automatically when reading a new object
+ file. */
+
+ PTR obj_private;
+
+ /* Set of relocation offsets to apply to each section.
+ Currently on the psymbol_obstack (which makes no sense, but I'm
+ not sure it's harming anything).
+
+ These offsets indicate that all symbols (including partial and
+ minimal symbols) which have been read have been relocated by this
+ much. Symbols which are yet to be read need to be relocated by
+ it. */
+
+ struct section_offsets *section_offsets;
+ int num_sections;
+
+ /* set of section begin and end addresses used to map pc addresses
+ into sections. Currently on the psymbol_obstack (which makes no
+ sense, but I'm not sure it's harming anything). */
+
+ struct obj_section
+ *sections,
+ *sections_end;
+};
+
+/* Defines for the objfile flag word. */
+
+/* Gdb can arrange to allocate storage for all objects related to a
+ particular objfile in a designated section of it's address space,
+ managed at a low level by mmap() and using a special version of
+ malloc that handles malloc/free/realloc on top of the mmap() interface.
+ This allows the "internal gdb state" for a particular objfile to be
+ dumped to a gdb state file and subsequently reloaded at a later time. */
+
+#define OBJF_MAPPED (1 << 0) /* Objfile data is mmap'd */
+
+/* When using mapped/remapped predigested gdb symbol information, we need
+ a flag that indicates that we have previously done an initial symbol
+ table read from this particular objfile. We can't just look for the
+ absence of any of the three symbol tables (msymbols, psymtab, symtab)
+ because if the file has no symbols for example, none of these will
+ exist. */
+
+#define OBJF_SYMS (1 << 1) /* Have tried to read symbols */
+
+/* The object file that the main symbol table was loaded from (e.g. the
+ argument to the "symbol-file" or "file" command). */
+
+extern struct objfile *symfile_objfile;
+
+/* When we need to allocate a new type, we need to know which type_obstack
+ to allocate the type on, since there is one for each objfile. The places
+ where types are allocated are deeply buried in function call hierarchies
+ which know nothing about objfiles, so rather than trying to pass a
+ particular objfile down to them, we just do an end run around them and
+ set current_objfile to be whatever objfile we expect to be using at the
+ time types are being allocated. For instance, when we start reading
+ symbols for a particular objfile, we set current_objfile to point to that
+ objfile, and when we are done, we set it back to NULL, to ensure that we
+ never put a type someplace other than where we are expecting to put it.
+ FIXME: Maybe we should review the entire type handling system and
+ see if there is a better way to avoid this problem. */
+
+extern struct objfile *current_objfile;
+
+/* All known objfiles are kept in a linked list. This points to the
+ root of this list. */
+
+extern struct objfile *object_files;
+
+/* Declarations for functions defined in objfiles.c */
+
+extern struct objfile *
+allocate_objfile PARAMS ((bfd *, int));
+
+extern void
+unlink_objfile PARAMS ((struct objfile *));
+
+extern void
+free_objfile PARAMS ((struct objfile *));
+
+extern void
+free_all_objfiles PARAMS ((void));
+
+extern void
+objfile_relocate PARAMS ((struct objfile *, struct section_offsets *));
+
+extern int
+have_partial_symbols PARAMS ((void));
+
+extern int
+have_full_symbols PARAMS ((void));
+
+/* Functions for dealing with the minimal symbol table, really a misc
+ address<->symbol mapping for things we don't have debug symbols for. */
+
+extern int
+have_minimal_symbols PARAMS ((void));
+
+extern struct obj_section *
+find_pc_section PARAMS((CORE_ADDR pc));
+
+/* Traverse all object files. ALL_OBJFILES_SAFE works even if you delete
+ the objfile during the traversal. */
+
+#define ALL_OBJFILES(obj) \
+ for ((obj) = object_files; (obj) != NULL; (obj) = (obj)->next)
+
+#define ALL_OBJFILES_SAFE(obj,nxt) \
+ for ((obj) = object_files; \
+ (obj) != NULL? ((nxt)=(obj)->next,1) :0; \
+ (obj) = (nxt))
+
+
+/* Traverse all symtabs in one objfile. */
+
+#define ALL_OBJFILE_SYMTABS(objfile, s) \
+ for ((s) = (objfile) -> symtabs; (s) != NULL; (s) = (s) -> next)
+
+/* Traverse all psymtabs in one objfile. */
+
+#define ALL_OBJFILE_PSYMTABS(objfile, p) \
+ for ((p) = (objfile) -> psymtabs; (p) != NULL; (p) = (p) -> next)
+
+/* Traverse all minimal symbols in one objfile. */
+
+#define ALL_OBJFILE_MSYMBOLS(objfile, m) \
+ for ((m) = (objfile) -> msymbols; SYMBOL_NAME(m) != NULL; (m)++)
+
+
+/* Traverse all symtabs in all objfiles. */
+
+#define ALL_SYMTABS(objfile, s) \
+ ALL_OBJFILES (objfile) \
+ ALL_OBJFILE_SYMTABS (objfile, s)
+
+/* Traverse all psymtabs in all objfiles. */
+
+#define ALL_PSYMTABS(objfile, p) \
+ ALL_OBJFILES (objfile) \
+ ALL_OBJFILE_PSYMTABS (objfile, p)
+
+/* Traverse all minimal symbols in all objfiles. */
+
+#define ALL_MSYMBOLS(objfile, m) \
+ ALL_OBJFILES (objfile) \
+ if ((objfile)->msymbols) \
+ ALL_OBJFILE_MSYMBOLS (objfile, m)
+
+#endif /* !defined (OBJFILES_H) */
diff --git a/gnu/usr.bin/gdb/gdb/obstack.h b/gnu/usr.bin/gdb/gdb/obstack.h
new file mode 100644
index 0000000..689f148
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/obstack.h
@@ -0,0 +1,490 @@
+/* obstack.h - object stack macros
+ Copyright (C) 1988, 1992 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU Library General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public License
+along with this program; if not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Summary:
+
+All the apparent functions defined here are macros. The idea
+is that you would use these pre-tested macros to solve a
+very specific set of problems, and they would run fast.
+Caution: no side-effects in arguments please!! They may be
+evaluated MANY times!!
+
+These macros operate a stack of objects. Each object starts life
+small, and may grow to maturity. (Consider building a word syllable
+by syllable.) An object can move while it is growing. Once it has
+been "finished" it never changes address again. So the "top of the
+stack" is typically an immature growing object, while the rest of the
+stack is of mature, fixed size and fixed address objects.
+
+These routines grab large chunks of memory, using a function you
+supply, called `obstack_chunk_alloc'. On occasion, they free chunks,
+by calling `obstack_chunk_free'. You must define them and declare
+them before using any obstack macros.
+
+Each independent stack is represented by a `struct obstack'.
+Each of the obstack macros expects a pointer to such a structure
+as the first argument.
+
+One motivation for this package is the problem of growing char strings
+in symbol tables. Unless you are "fascist pig with a read-only mind"
+--Gosper's immortal quote from HAKMEM item 154, out of context--you
+would not like to put any arbitrary upper limit on the length of your
+symbols.
+
+In practice this often means you will build many short symbols and a
+few long symbols. At the time you are reading a symbol you don't know
+how long it is. One traditional method is to read a symbol into a
+buffer, realloc()ating the buffer every time you try to read a symbol
+that is longer than the buffer. This is beaut, but you still will
+want to copy the symbol from the buffer to a more permanent
+symbol-table entry say about half the time.
+
+With obstacks, you can work differently. Use one obstack for all symbol
+names. As you read a symbol, grow the name in the obstack gradually.
+When the name is complete, finalize it. Then, if the symbol exists already,
+free the newly read name.
+
+The way we do this is to take a large chunk, allocating memory from
+low addresses. When you want to build a symbol in the chunk you just
+add chars above the current "high water mark" in the chunk. When you
+have finished adding chars, because you got to the end of the symbol,
+you know how long the chars are, and you can create a new object.
+Mostly the chars will not burst over the highest address of the chunk,
+because you would typically expect a chunk to be (say) 100 times as
+long as an average object.
+
+In case that isn't clear, when we have enough chars to make up
+the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed)
+so we just point to it where it lies. No moving of chars is
+needed and this is the second win: potentially long strings need
+never be explicitly shuffled. Once an object is formed, it does not
+change its address during its lifetime.
+
+When the chars burst over a chunk boundary, we allocate a larger
+chunk, and then copy the partly formed object from the end of the old
+chunk to the beginning of the new larger chunk. We then carry on
+accreting characters to the end of the object as we normally would.
+
+A special macro is provided to add a single char at a time to a
+growing object. This allows the use of register variables, which
+break the ordinary 'growth' macro.
+
+Summary:
+ We allocate large chunks.
+ We carve out one object at a time from the current chunk.
+ Once carved, an object never moves.
+ We are free to append data of any size to the currently
+ growing object.
+ Exactly one object is growing in an obstack at any one time.
+ You can run one obstack per control block.
+ You may have as many control blocks as you dare.
+ Because of the way we do it, you can `unwind' an obstack
+ back to a previous state. (You may remove objects much
+ as you would with a stack.)
+*/
+
+
+/* Don't do the contents of this file more than once. */
+
+#ifndef __OBSTACKS__
+#define __OBSTACKS__
+
+/* We use subtraction of (char *)0 instead of casting to int
+ because on word-addressable machines a simple cast to int
+ may ignore the byte-within-word field of the pointer. */
+
+#ifndef __PTR_TO_INT
+#define __PTR_TO_INT(P) ((P) - (char *)0)
+#endif
+
+#ifndef __INT_TO_PTR
+#define __INT_TO_PTR(P) ((P) + (char *)0)
+#endif
+
+/* We need the type of the resulting object. In ANSI C it is ptrdiff_t
+ but in traditional C it is usually long. If we are in ANSI C and
+ don't already have ptrdiff_t get it. */
+
+#if defined (__STDC__) && ! defined (offsetof)
+#if defined (__GNUC__) && defined (IN_GCC)
+/* On Next machine, the system's stddef.h screws up if included
+ after we have defined just ptrdiff_t, so include all of gstddef.h.
+ Otherwise, define just ptrdiff_t, which is all we need. */
+#ifndef __NeXT__
+#define __need_ptrdiff_t
+#endif
+
+/* While building GCC, the stddef.h that goes with GCC has this name. */
+#include "gstddef.h"
+#else
+#include <stddef.h>
+#endif
+#endif
+
+#ifdef __STDC__
+#define PTR_INT_TYPE ptrdiff_t
+#else
+#define PTR_INT_TYPE long
+#endif
+
+struct _obstack_chunk /* Lives at front of each chunk. */
+{
+ char *limit; /* 1 past end of this chunk */
+ struct _obstack_chunk *prev; /* address of prior chunk or NULL */
+ char contents[4]; /* objects begin here */
+};
+
+struct obstack /* control current object in current chunk */
+{
+ long chunk_size; /* preferred size to allocate chunks in */
+ struct _obstack_chunk* chunk; /* address of current struct obstack_chunk */
+ char *object_base; /* address of object we are building */
+ char *next_free; /* where to add next char to current object */
+ char *chunk_limit; /* address of char after current chunk */
+ PTR_INT_TYPE temp; /* Temporary for some macros. */
+ int alignment_mask; /* Mask of alignment for each object. */
+ struct _obstack_chunk *(*chunkfun) (); /* User's fcn to allocate a chunk. */
+ void (*freefun) (); /* User's function to free a chunk. */
+ char *extra_arg; /* first arg for chunk alloc/dealloc funcs */
+ unsigned use_extra_arg:1; /* chunk alloc/dealloc funcs take extra arg */
+ unsigned maybe_empty_object:1;/* There is a possibility that the current
+ chunk contains a zero-length object. This
+ prevents freeing the chunk if we allocate
+ a bigger chunk to replace it. */
+};
+
+/* Declare the external functions we use; they are in obstack.c. */
+
+#ifdef __STDC__
+extern void _obstack_newchunk (struct obstack *, int);
+extern void _obstack_free (struct obstack *, void *);
+extern void _obstack_begin (struct obstack *, int, int,
+ void *(*) (), void (*) ());
+extern void _obstack_begin_1 (struct obstack *, int, int,
+ void *(*) (), void (*) (), void *);
+#else
+extern void _obstack_newchunk ();
+extern void _obstack_free ();
+extern void _obstack_begin ();
+extern void _obstack_begin_1 ();
+#endif
+
+#ifdef __STDC__
+
+/* Do the function-declarations after the structs
+ but before defining the macros. */
+
+void obstack_init (struct obstack *obstack);
+
+void * obstack_alloc (struct obstack *obstack, int size);
+
+void * obstack_copy (struct obstack *obstack, void *address, int size);
+void * obstack_copy0 (struct obstack *obstack, void *address, int size);
+
+void obstack_free (struct obstack *obstack, void *block);
+
+void obstack_blank (struct obstack *obstack, int size);
+
+void obstack_grow (struct obstack *obstack, void *data, int size);
+void obstack_grow0 (struct obstack *obstack, void *data, int size);
+
+void obstack_1grow (struct obstack *obstack, int data_char);
+void obstack_ptr_grow (struct obstack *obstack, void *data);
+void obstack_int_grow (struct obstack *obstack, int data);
+
+void * obstack_finish (struct obstack *obstack);
+
+int obstack_object_size (struct obstack *obstack);
+
+int obstack_room (struct obstack *obstack);
+void obstack_1grow_fast (struct obstack *obstack, int data_char);
+void obstack_ptr_grow_fast (struct obstack *obstack, void *data);
+void obstack_int_grow_fast (struct obstack *obstack, int data);
+void obstack_blank_fast (struct obstack *obstack, int size);
+
+void * obstack_base (struct obstack *obstack);
+void * obstack_next_free (struct obstack *obstack);
+int obstack_alignment_mask (struct obstack *obstack);
+int obstack_chunk_size (struct obstack *obstack);
+
+#endif /* __STDC__ */
+
+/* Non-ANSI C cannot really support alternative functions for these macros,
+ so we do not declare them. */
+
+/* Pointer to beginning of object being allocated or to be allocated next.
+ Note that this might not be the final address of the object
+ because a new chunk might be needed to hold the final size. */
+
+#define obstack_base(h) ((h)->object_base)
+
+/* Size for allocating ordinary chunks. */
+
+#define obstack_chunk_size(h) ((h)->chunk_size)
+
+/* Pointer to next byte not yet allocated in current chunk. */
+
+#define obstack_next_free(h) ((h)->next_free)
+
+/* Mask specifying low bits that should be clear in address of an object. */
+
+#define obstack_alignment_mask(h) ((h)->alignment_mask)
+
+#define obstack_init(h) \
+ _obstack_begin ((h), 0, 0, \
+ (void *(*) ()) obstack_chunk_alloc, (void (*) ()) obstack_chunk_free)
+
+#define obstack_begin(h, size) \
+ _obstack_begin ((h), (size), 0, \
+ (void *(*) ()) obstack_chunk_alloc, (void (*) ()) obstack_chunk_free)
+
+#define obstack_specify_allocation(h, size, alignment, chunkfun, freefun) \
+ _obstack_begin ((h), (size), (alignment), \
+ (void *(*) ()) (chunkfun), (void (*) ()) (freefun))
+
+#define obstack_specify_allocation_with_arg(h, size, alignment, chunkfun, freefun, arg) \
+ _obstack_begin_1 ((h), (size), (alignment), \
+ (void *(*) ()) (chunkfun), (void (*) ()) (freefun), (arg))
+
+#define obstack_chunkfun(h, newchunkfun) \
+ ((h) -> chunkfun = (struct _obstack_chunk *(*)()) (newchunkfun))
+
+#define obstack_freefun(h, newfreefun) \
+ ((h) -> freefun = (void (*)()) (newfreefun))
+
+#define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = achar)
+
+#define obstack_blank_fast(h,n) ((h)->next_free += (n))
+
+#if defined (__GNUC__) && defined (__STDC__)
+#if __GNUC__ < 2
+#define __extension__
+#endif
+
+/* For GNU C, if not -traditional,
+ we can define these macros to compute all args only once
+ without using a global variable.
+ Also, we can avoid using the `temp' slot, to make faster code. */
+
+#define obstack_object_size(OBSTACK) \
+ __extension__ \
+ ({ struct obstack *__o = (OBSTACK); \
+ (unsigned) (__o->next_free - __o->object_base); })
+
+#define obstack_room(OBSTACK) \
+ __extension__ \
+ ({ struct obstack *__o = (OBSTACK); \
+ (unsigned) (__o->chunk_limit - __o->next_free); })
+
+/* Note that the call to _obstack_newchunk is enclosed in (..., 0)
+ so that we can avoid having void expressions
+ in the arms of the conditional expression.
+ Casting the third operand to void was tried before,
+ but some compilers won't accept it. */
+#define obstack_grow(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ int __len = (length); \
+ ((__o->next_free + __len > __o->chunk_limit) \
+ ? (_obstack_newchunk (__o, __len), 0) : 0); \
+ bcopy (where, __o->next_free, __len); \
+ __o->next_free += __len; \
+ (void) 0; })
+
+#define obstack_grow0(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ int __len = (length); \
+ ((__o->next_free + __len + 1 > __o->chunk_limit) \
+ ? (_obstack_newchunk (__o, __len + 1), 0) : 0), \
+ bcopy (where, __o->next_free, __len), \
+ __o->next_free += __len, \
+ *(__o->next_free)++ = 0; \
+ (void) 0; })
+
+#define obstack_1grow(OBSTACK,datum) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ ((__o->next_free + 1 > __o->chunk_limit) \
+ ? (_obstack_newchunk (__o, 1), 0) : 0), \
+ *(__o->next_free)++ = (datum); \
+ (void) 0; })
+
+/* These assume that the obstack alignment is good enough for pointers or ints,
+ and that the data added so far to the current object
+ shares that much alignment. */
+
+#define obstack_ptr_grow(OBSTACK,datum) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ ((__o->next_free + sizeof (void *) > __o->chunk_limit) \
+ ? (_obstack_newchunk (__o, sizeof (void *)), 0) : 0), \
+ *((void **)__o->next_free)++ = ((void *)datum); \
+ (void) 0; })
+
+#define obstack_int_grow(OBSTACK,datum) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ ((__o->next_free + sizeof (int) > __o->chunk_limit) \
+ ? (_obstack_newchunk (__o, sizeof (int)), 0) : 0), \
+ *((int *)__o->next_free)++ = ((int)datum); \
+ (void) 0; })
+
+#define obstack_ptr_grow_fast(h,aptr) (*((void **)(h)->next_free)++ = (void *)aptr)
+#define obstack_int_grow_fast(h,aint) (*((int *)(h)->next_free)++ = (int)aint)
+
+#define obstack_blank(OBSTACK,length) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ int __len = (length); \
+ ((__o->chunk_limit - __o->next_free < __len) \
+ ? (_obstack_newchunk (__o, __len), 0) : 0); \
+ __o->next_free += __len; \
+ (void) 0; })
+
+#define obstack_alloc(OBSTACK,length) \
+__extension__ \
+({ struct obstack *__h = (OBSTACK); \
+ obstack_blank (__h, (length)); \
+ obstack_finish (__h); })
+
+#define obstack_copy(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__h = (OBSTACK); \
+ obstack_grow (__h, (where), (length)); \
+ obstack_finish (__h); })
+
+#define obstack_copy0(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__h = (OBSTACK); \
+ obstack_grow0 (__h, (where), (length)); \
+ obstack_finish (__h); })
+
+/* The local variable is named __o1 to avoid a name conflict
+ when obstack_blank is called. */
+#define obstack_finish(OBSTACK) \
+__extension__ \
+({ struct obstack *__o1 = (OBSTACK); \
+ void *value = (void *) __o1->object_base; \
+ if (__o1->next_free == value) \
+ __o1->maybe_empty_object = 1; \
+ __o1->next_free \
+ = __INT_TO_PTR ((__PTR_TO_INT (__o1->next_free)+__o1->alignment_mask)\
+ & ~ (__o1->alignment_mask)); \
+ ((__o1->next_free - (char *)__o1->chunk \
+ > __o1->chunk_limit - (char *)__o1->chunk) \
+ ? (__o1->next_free = __o1->chunk_limit) : 0); \
+ __o1->object_base = __o1->next_free; \
+ value; })
+
+#define obstack_free(OBSTACK, OBJ) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ void *__obj = (OBJ); \
+ if (__obj > (void *)__o->chunk && __obj < (void *)__o->chunk_limit) \
+ __o->next_free = __o->object_base = __obj; \
+ else (obstack_free) (__o, __obj); })
+
+#else /* not __GNUC__ or not __STDC__ */
+
+#define obstack_object_size(h) \
+ (unsigned) ((h)->next_free - (h)->object_base)
+
+#define obstack_room(h) \
+ (unsigned) ((h)->chunk_limit - (h)->next_free)
+
+#define obstack_grow(h,where,length) \
+( (h)->temp = (length), \
+ (((h)->next_free + (h)->temp > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), (h)->temp), 0) : 0), \
+ bcopy (where, (h)->next_free, (h)->temp), \
+ (h)->next_free += (h)->temp)
+
+#define obstack_grow0(h,where,length) \
+( (h)->temp = (length), \
+ (((h)->next_free + (h)->temp + 1 > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), (h)->temp + 1), 0) : 0), \
+ bcopy (where, (h)->next_free, (h)->temp), \
+ (h)->next_free += (h)->temp, \
+ *((h)->next_free)++ = 0)
+
+#define obstack_1grow(h,datum) \
+( (((h)->next_free + 1 > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), 1), 0) : 0), \
+ *((h)->next_free)++ = (datum))
+
+#define obstack_ptr_grow(h,datum) \
+( (((h)->next_free + sizeof (char *) > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), sizeof (char *)), 0) : 0), \
+ *((char **)(((h)->next_free+=sizeof(char *))-sizeof(char *))) = ((char *)datum))
+
+#define obstack_int_grow(h,datum) \
+( (((h)->next_free + sizeof (int) > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), sizeof (int)), 0) : 0), \
+ *((int *)(((h)->next_free+=sizeof(int))-sizeof(int))) = ((int)datum))
+
+#define obstack_ptr_grow_fast(h,aptr) (*((char **)(h)->next_free)++ = (char *)aptr)
+#define obstack_int_grow_fast(h,aint) (*((int *)(h)->next_free)++ = (int)aint)
+
+#define obstack_blank(h,length) \
+( (h)->temp = (length), \
+ (((h)->chunk_limit - (h)->next_free < (h)->temp) \
+ ? (_obstack_newchunk ((h), (h)->temp), 0) : 0), \
+ (h)->next_free += (h)->temp)
+
+#define obstack_alloc(h,length) \
+ (obstack_blank ((h), (length)), obstack_finish ((h)))
+
+#define obstack_copy(h,where,length) \
+ (obstack_grow ((h), (where), (length)), obstack_finish ((h)))
+
+#define obstack_copy0(h,where,length) \
+ (obstack_grow0 ((h), (where), (length)), obstack_finish ((h)))
+
+#define obstack_finish(h) \
+( ((h)->next_free == (h)->object_base \
+ ? (((h)->maybe_empty_object = 1), 0) \
+ : 0), \
+ (h)->temp = __PTR_TO_INT ((h)->object_base), \
+ (h)->next_free \
+ = __INT_TO_PTR ((__PTR_TO_INT ((h)->next_free)+(h)->alignment_mask) \
+ & ~ ((h)->alignment_mask)), \
+ (((h)->next_free - (char *)(h)->chunk \
+ > (h)->chunk_limit - (char *)(h)->chunk) \
+ ? ((h)->next_free = (h)->chunk_limit) : 0), \
+ (h)->object_base = (h)->next_free, \
+ __INT_TO_PTR ((h)->temp))
+
+#ifdef __STDC__
+#define obstack_free(h,obj) \
+( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \
+ (((h)->temp > 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\
+ ? (int) ((h)->next_free = (h)->object_base \
+ = (h)->temp + (char *) (h)->chunk) \
+ : (((obstack_free) ((h), (h)->temp + (char *) (h)->chunk), 0), 0)))
+#else
+#define obstack_free(h,obj) \
+( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \
+ (((h)->temp > 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\
+ ? (int) ((h)->next_free = (h)->object_base \
+ = (h)->temp + (char *) (h)->chunk) \
+ : (_obstack_free ((h), (h)->temp + (char *) (h)->chunk), 0)))
+#endif
+
+#endif /* not __GNUC__ or not __STDC__ */
+
+#endif /* not __OBSTACKS__ */
diff --git a/gnu/usr.bin/gdb/gdb/parse.c b/gnu/usr.bin/gdb/gdb/parse.c
new file mode 100644
index 0000000..08f2b7e
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/parse.c
@@ -0,0 +1,827 @@
+/* Parse expressions for GDB.
+ Copyright (C) 1986, 1989, 1990, 1991 Free Software Foundation, Inc.
+ Modified from expread.y by the Department of Computer Science at the
+ State University of New York at Buffalo, 1991.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Parse an expression from text in a string,
+ and return the result as a struct expression pointer.
+ That structure contains arithmetic operations in reverse polish,
+ with constants represented by operations that are followed by special data.
+ See expression.h for the details of the format.
+ What is important here is that it can be built up sequentially
+ during the process of parsing; the lower levels of the tree always
+ come first in the result. */
+
+#include "defs.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "frame.h"
+#include "expression.h"
+#include "value.h"
+#include "command.h"
+#include "language.h"
+#include "parser-defs.h"
+
+static void
+free_funcalls PARAMS ((void));
+
+static void
+prefixify_expression PARAMS ((struct expression *));
+
+static int
+length_of_subexp PARAMS ((struct expression *, int));
+
+static void
+prefixify_subexp PARAMS ((struct expression *, struct expression *, int, int));
+
+/* Data structure for saving values of arglist_len for function calls whose
+ arguments contain other function calls. */
+
+struct funcall
+ {
+ struct funcall *next;
+ int arglist_len;
+ };
+
+static struct funcall *funcall_chain;
+
+/* Assign machine-independent names to certain registers
+ (unless overridden by the REGISTER_NAMES table) */
+
+#ifdef NO_STD_REGS
+unsigned num_std_regs = 0;
+struct std_regs std_regs[1];
+#else
+struct std_regs std_regs[] = {
+
+#ifdef PC_REGNUM
+ { "pc", PC_REGNUM },
+#endif
+#ifdef FP_REGNUM
+ { "fp", FP_REGNUM },
+#endif
+#ifdef SP_REGNUM
+ { "sp", SP_REGNUM },
+#endif
+#ifdef PS_REGNUM
+ { "ps", PS_REGNUM },
+#endif
+
+};
+
+unsigned num_std_regs = (sizeof std_regs / sizeof std_regs[0]);
+
+#endif
+
+
+/* Begin counting arguments for a function call,
+ saving the data about any containing call. */
+
+void
+start_arglist ()
+{
+ register struct funcall *new;
+
+ new = (struct funcall *) xmalloc (sizeof (struct funcall));
+ new->next = funcall_chain;
+ new->arglist_len = arglist_len;
+ arglist_len = 0;
+ funcall_chain = new;
+}
+
+/* Return the number of arguments in a function call just terminated,
+ and restore the data for the containing function call. */
+
+int
+end_arglist ()
+{
+ register int val = arglist_len;
+ register struct funcall *call = funcall_chain;
+ funcall_chain = call->next;
+ arglist_len = call->arglist_len;
+ free ((PTR)call);
+ return val;
+}
+
+/* Free everything in the funcall chain.
+ Used when there is an error inside parsing. */
+
+static void
+free_funcalls ()
+{
+ register struct funcall *call, *next;
+
+ for (call = funcall_chain; call; call = next)
+ {
+ next = call->next;
+ free ((PTR)call);
+ }
+}
+
+/* This page contains the functions for adding data to the struct expression
+ being constructed. */
+
+/* Add one element to the end of the expression. */
+
+/* To avoid a bug in the Sun 4 compiler, we pass things that can fit into
+ a register through here */
+
+void
+write_exp_elt (expelt)
+ union exp_element expelt;
+{
+ if (expout_ptr >= expout_size)
+ {
+ expout_size *= 2;
+ expout = (struct expression *)
+ xrealloc ((char *) expout, sizeof (struct expression)
+ + EXP_ELEM_TO_BYTES (expout_size));
+ }
+ expout->elts[expout_ptr++] = expelt;
+}
+
+void
+write_exp_elt_opcode (expelt)
+ enum exp_opcode expelt;
+{
+ union exp_element tmp;
+
+ tmp.opcode = expelt;
+
+ write_exp_elt (tmp);
+}
+
+void
+write_exp_elt_sym (expelt)
+ struct symbol *expelt;
+{
+ union exp_element tmp;
+
+ tmp.symbol = expelt;
+
+ write_exp_elt (tmp);
+}
+
+void
+write_exp_elt_block (b)
+ struct block *b;
+{
+ union exp_element tmp;
+ tmp.block = b;
+ write_exp_elt (tmp);
+}
+
+void
+write_exp_elt_longcst (expelt)
+ LONGEST expelt;
+{
+ union exp_element tmp;
+
+ tmp.longconst = expelt;
+
+ write_exp_elt (tmp);
+}
+
+void
+write_exp_elt_dblcst (expelt)
+ double expelt;
+{
+ union exp_element tmp;
+
+ tmp.doubleconst = expelt;
+
+ write_exp_elt (tmp);
+}
+
+void
+write_exp_elt_type (expelt)
+ struct type *expelt;
+{
+ union exp_element tmp;
+
+ tmp.type = expelt;
+
+ write_exp_elt (tmp);
+}
+
+void
+write_exp_elt_intern (expelt)
+ struct internalvar *expelt;
+{
+ union exp_element tmp;
+
+ tmp.internalvar = expelt;
+
+ write_exp_elt (tmp);
+}
+
+/* Add a string constant to the end of the expression.
+
+ String constants are stored by first writing an expression element
+ that contains the length of the string, then stuffing the string
+ constant itself into however many expression elements are needed
+ to hold it, and then writing another expression element that contains
+ the length of the string. I.E. an expression element at each end of
+ the string records the string length, so you can skip over the
+ expression elements containing the actual string bytes from either
+ end of the string. Note that this also allows gdb to handle
+ strings with embedded null bytes, as is required for some languages.
+
+ Don't be fooled by the fact that the string is null byte terminated,
+ this is strictly for the convenience of debugging gdb itself. Gdb
+ Gdb does not depend up the string being null terminated, since the
+ actual length is recorded in expression elements at each end of the
+ string. The null byte is taken into consideration when computing how
+ many expression elements are required to hold the string constant, of
+ course. */
+
+
+void
+write_exp_string (str)
+ struct stoken str;
+{
+ register int len = str.length;
+ register int lenelt;
+ register char *strdata;
+
+ /* Compute the number of expression elements required to hold the string
+ (including a null byte terminator), along with one expression element
+ at each end to record the actual string length (not including the
+ null byte terminator). */
+
+ lenelt = 2 + BYTES_TO_EXP_ELEM (len + 1);
+
+ /* Ensure that we have enough available expression elements to store
+ everything. */
+
+ if ((expout_ptr + lenelt) >= expout_size)
+ {
+ expout_size = max (expout_size * 2, expout_ptr + lenelt + 10);
+ expout = (struct expression *)
+ xrealloc ((char *) expout, (sizeof (struct expression)
+ + EXP_ELEM_TO_BYTES (expout_size)));
+ }
+
+ /* Write the leading length expression element (which advances the current
+ expression element index), then write the string constant followed by a
+ terminating null byte, and then write the trailing length expression
+ element. */
+
+ write_exp_elt_longcst ((LONGEST) len);
+ strdata = (char *) &expout->elts[expout_ptr];
+ memcpy (strdata, str.ptr, len);
+ *(strdata + len) = '\0';
+ expout_ptr += lenelt - 2;
+ write_exp_elt_longcst ((LONGEST) len);
+}
+
+/* Add a bitstring constant to the end of the expression.
+
+ Bitstring constants are stored by first writing an expression element
+ that contains the length of the bitstring (in bits), then stuffing the
+ bitstring constant itself into however many expression elements are
+ needed to hold it, and then writing another expression element that
+ contains the length of the bitstring. I.E. an expression element at
+ each end of the bitstring records the bitstring length, so you can skip
+ over the expression elements containing the actual bitstring bytes from
+ either end of the bitstring. */
+
+void
+write_exp_bitstring (str)
+ struct stoken str;
+{
+ register int bits = str.length; /* length in bits */
+ register int len = (bits + HOST_CHAR_BIT - 1) / HOST_CHAR_BIT;
+ register int lenelt;
+ register char *strdata;
+
+ /* Compute the number of expression elements required to hold the bitstring,
+ along with one expression element at each end to record the actual
+ bitstring length in bits. */
+
+ lenelt = 2 + BYTES_TO_EXP_ELEM (len);
+
+ /* Ensure that we have enough available expression elements to store
+ everything. */
+
+ if ((expout_ptr + lenelt) >= expout_size)
+ {
+ expout_size = max (expout_size * 2, expout_ptr + lenelt + 10);
+ expout = (struct expression *)
+ xrealloc ((char *) expout, (sizeof (struct expression)
+ + EXP_ELEM_TO_BYTES (expout_size)));
+ }
+
+ /* Write the leading length expression element (which advances the current
+ expression element index), then write the bitstring constant, and then
+ write the trailing length expression element. */
+
+ write_exp_elt_longcst ((LONGEST) bits);
+ strdata = (char *) &expout->elts[expout_ptr];
+ memcpy (strdata, str.ptr, len);
+ expout_ptr += lenelt - 2;
+ write_exp_elt_longcst ((LONGEST) bits);
+}
+
+/* Return a null-terminated temporary copy of the name
+ of a string token. */
+
+char *
+copy_name (token)
+ struct stoken token;
+{
+ memcpy (namecopy, token.ptr, token.length);
+ namecopy[token.length] = 0;
+ return namecopy;
+}
+
+/* Reverse an expression from suffix form (in which it is constructed)
+ to prefix form (in which we can conveniently print or execute it). */
+
+static void
+prefixify_expression (expr)
+ register struct expression *expr;
+{
+ register int len =
+ sizeof (struct expression) + EXP_ELEM_TO_BYTES (expr->nelts);
+ register struct expression *temp;
+ register int inpos = expr->nelts, outpos = 0;
+
+ temp = (struct expression *) alloca (len);
+
+ /* Copy the original expression into temp. */
+ memcpy (temp, expr, len);
+
+ prefixify_subexp (temp, expr, inpos, outpos);
+}
+
+/* Return the number of exp_elements in the subexpression of EXPR
+ whose last exp_element is at index ENDPOS - 1 in EXPR. */
+
+static int
+length_of_subexp (expr, endpos)
+ register struct expression *expr;
+ register int endpos;
+{
+ register int oplen = 1;
+ register int args = 0;
+ register int i;
+
+ if (endpos < 1)
+ error ("?error in length_of_subexp");
+
+ i = (int) expr->elts[endpos - 1].opcode;
+
+ switch (i)
+ {
+ /* C++ */
+ case OP_SCOPE:
+ oplen = longest_to_int (expr->elts[endpos - 2].longconst);
+ oplen = 5 + BYTES_TO_EXP_ELEM (oplen + 1);
+ break;
+
+ case OP_LONG:
+ case OP_DOUBLE:
+ case OP_VAR_VALUE:
+ oplen = 4;
+ break;
+
+ case OP_TYPE:
+ case OP_BOOL:
+ case OP_LAST:
+ case OP_REGISTER:
+ case OP_INTERNALVAR:
+ oplen = 3;
+ break;
+
+ case OP_FUNCALL:
+ oplen = 3;
+ args = 1 + longest_to_int (expr->elts[endpos - 2].longconst);
+ break;
+
+ case UNOP_MAX:
+ case UNOP_MIN:
+ oplen = 3;
+ break;
+
+ case BINOP_VAL:
+ case UNOP_CAST:
+ case UNOP_MEMVAL:
+ oplen = 3;
+ args = 1;
+ break;
+
+ case UNOP_ABS:
+ case UNOP_CAP:
+ case UNOP_CHR:
+ case UNOP_FLOAT:
+ case UNOP_HIGH:
+ case UNOP_ODD:
+ case UNOP_ORD:
+ case UNOP_TRUNC:
+ oplen = 1;
+ args = 1;
+ break;
+
+ case STRUCTOP_STRUCT:
+ case STRUCTOP_PTR:
+ args = 1;
+ /* fall through */
+ case OP_M2_STRING:
+ case OP_STRING:
+ oplen = longest_to_int (expr->elts[endpos - 2].longconst);
+ oplen = 4 + BYTES_TO_EXP_ELEM (oplen + 1);
+ break;
+
+ case OP_BITSTRING:
+ oplen = longest_to_int (expr->elts[endpos - 2].longconst);
+ oplen = (oplen + HOST_CHAR_BIT - 1) / HOST_CHAR_BIT;
+ oplen = 4 + BYTES_TO_EXP_ELEM (oplen);
+ break;
+
+ case OP_ARRAY:
+ oplen = 4;
+ args = longest_to_int (expr->elts[endpos - 2].longconst);
+ args -= longest_to_int (expr->elts[endpos - 3].longconst);
+ args += 1;
+ break;
+
+ case TERNOP_COND:
+ args = 3;
+ break;
+
+ /* Modula-2 */
+ case MULTI_SUBSCRIPT:
+ oplen=3;
+ args = 1 + longest_to_int (expr->elts[endpos- 2].longconst);
+ break;
+
+ case BINOP_ASSIGN_MODIFY:
+ oplen = 3;
+ args = 2;
+ break;
+
+ /* C++ */
+ case OP_THIS:
+ oplen = 2;
+ break;
+
+ default:
+ args = 1 + (i < (int) BINOP_END);
+ }
+
+ while (args > 0)
+ {
+ oplen += length_of_subexp (expr, endpos - oplen);
+ args--;
+ }
+
+ return oplen;
+}
+
+/* Copy the subexpression ending just before index INEND in INEXPR
+ into OUTEXPR, starting at index OUTBEG.
+ In the process, convert it from suffix to prefix form. */
+
+static void
+prefixify_subexp (inexpr, outexpr, inend, outbeg)
+ register struct expression *inexpr;
+ struct expression *outexpr;
+ register int inend;
+ int outbeg;
+{
+ register int oplen = 1;
+ register int args = 0;
+ register int i;
+ int *arglens;
+ enum exp_opcode opcode;
+
+ /* Compute how long the last operation is (in OPLEN),
+ and also how many preceding subexpressions serve as
+ arguments for it (in ARGS). */
+
+ opcode = inexpr->elts[inend - 1].opcode;
+ switch (opcode)
+ {
+ /* C++ */
+ case OP_SCOPE:
+ oplen = longest_to_int (inexpr->elts[inend - 2].longconst);
+ oplen = 5 + BYTES_TO_EXP_ELEM (oplen + 1);
+ break;
+
+ case OP_LONG:
+ case OP_DOUBLE:
+ case OP_VAR_VALUE:
+ oplen = 4;
+ break;
+
+ case OP_TYPE:
+ case OP_BOOL:
+ case OP_LAST:
+ case OP_REGISTER:
+ case OP_INTERNALVAR:
+ oplen = 3;
+ break;
+
+ case OP_FUNCALL:
+ oplen = 3;
+ args = 1 + longest_to_int (inexpr->elts[inend - 2].longconst);
+ break;
+
+ case UNOP_MIN:
+ case UNOP_MAX:
+ oplen = 3;
+ break;
+
+ case UNOP_CAST:
+ case UNOP_MEMVAL:
+ oplen = 3;
+ args = 1;
+ break;
+
+ case UNOP_ABS:
+ case UNOP_CAP:
+ case UNOP_CHR:
+ case UNOP_FLOAT:
+ case UNOP_HIGH:
+ case UNOP_ODD:
+ case UNOP_ORD:
+ case UNOP_TRUNC:
+ oplen=1;
+ args=1;
+ break;
+
+ case STRUCTOP_STRUCT:
+ case STRUCTOP_PTR:
+ args = 1;
+ /* fall through */
+ case OP_M2_STRING:
+ case OP_STRING:
+ oplen = longest_to_int (inexpr->elts[inend - 2].longconst);
+ oplen = 4 + BYTES_TO_EXP_ELEM (oplen + 1);
+ break;
+
+ case OP_BITSTRING:
+ oplen = longest_to_int (inexpr->elts[inend - 2].longconst);
+ oplen = (oplen + HOST_CHAR_BIT - 1) / HOST_CHAR_BIT;
+ oplen = 4 + BYTES_TO_EXP_ELEM (oplen);
+ break;
+
+ case OP_ARRAY:
+ oplen = 4;
+ args = longest_to_int (inexpr->elts[inend - 2].longconst);
+ args -= longest_to_int (inexpr->elts[inend - 3].longconst);
+ args += 1;
+ break;
+
+ case TERNOP_COND:
+ args = 3;
+ break;
+
+ case BINOP_ASSIGN_MODIFY:
+ oplen = 3;
+ args = 2;
+ break;
+
+ /* Modula-2 */
+ case MULTI_SUBSCRIPT:
+ oplen=3;
+ args = 1 + longest_to_int (inexpr->elts[inend - 2].longconst);
+ break;
+
+ /* C++ */
+ case OP_THIS:
+ oplen = 2;
+ break;
+
+ default:
+ args = 1 + ((int) opcode < (int) BINOP_END);
+ }
+
+ /* Copy the final operator itself, from the end of the input
+ to the beginning of the output. */
+ inend -= oplen;
+ memcpy (&outexpr->elts[outbeg], &inexpr->elts[inend],
+ EXP_ELEM_TO_BYTES (oplen));
+ outbeg += oplen;
+
+ /* Find the lengths of the arg subexpressions. */
+ arglens = (int *) alloca (args * sizeof (int));
+ for (i = args - 1; i >= 0; i--)
+ {
+ oplen = length_of_subexp (inexpr, inend);
+ arglens[i] = oplen;
+ inend -= oplen;
+ }
+
+ /* Now copy each subexpression, preserving the order of
+ the subexpressions, but prefixifying each one.
+ In this loop, inend starts at the beginning of
+ the expression this level is working on
+ and marches forward over the arguments.
+ outbeg does similarly in the output. */
+ for (i = 0; i < args; i++)
+ {
+ oplen = arglens[i];
+ inend += oplen;
+ prefixify_subexp (inexpr, outexpr, inend, outbeg);
+ outbeg += oplen;
+ }
+}
+
+/* This page contains the two entry points to this file. */
+
+/* Read an expression from the string *STRINGPTR points to,
+ parse it, and return a pointer to a struct expression that we malloc.
+ Use block BLOCK as the lexical context for variable names;
+ if BLOCK is zero, use the block of the selected stack frame.
+ Meanwhile, advance *STRINGPTR to point after the expression,
+ at the first nonwhite character that is not part of the expression
+ (possibly a null character).
+
+ If COMMA is nonzero, stop if a comma is reached. */
+
+struct expression *
+parse_exp_1 (stringptr, block, comma)
+ char **stringptr;
+ struct block *block;
+ int comma;
+{
+ struct cleanup *old_chain;
+
+ lexptr = *stringptr;
+
+ paren_depth = 0;
+ type_stack_depth = 0;
+
+ comma_terminates = comma;
+
+ if (lexptr == 0 || *lexptr == 0)
+ error_no_arg ("expression to compute");
+
+ old_chain = make_cleanup (free_funcalls, 0);
+ funcall_chain = 0;
+
+ expression_context_block = block ? block : get_selected_block ();
+
+ namecopy = (char *) alloca (strlen (lexptr) + 1);
+ expout_size = 10;
+ expout_ptr = 0;
+ expout = (struct expression *)
+ xmalloc (sizeof (struct expression) + EXP_ELEM_TO_BYTES (expout_size));
+ expout->language_defn = current_language;
+ make_cleanup (free_current_contents, &expout);
+
+ if (current_language->la_parser ())
+ current_language->la_error (NULL);
+
+ discard_cleanups (old_chain);
+
+ /* Record the actual number of expression elements, and then
+ reallocate the expression memory so that we free up any
+ excess elements. */
+
+ expout->nelts = expout_ptr;
+ expout = (struct expression *)
+ xrealloc ((char *) expout,
+ sizeof (struct expression) + EXP_ELEM_TO_BYTES (expout_ptr));;
+
+ /* Convert expression from postfix form as generated by yacc
+ parser, to a prefix form. */
+
+ DUMP_EXPRESSION (expout, stdout, "before conversion to prefix form");
+ prefixify_expression (expout);
+ DUMP_EXPRESSION (expout, stdout, "after conversion to prefix form");
+
+ *stringptr = lexptr;
+ return expout;
+}
+
+/* Parse STRING as an expression, and complain if this fails
+ to use up all of the contents of STRING. */
+
+struct expression *
+parse_expression (string)
+ char *string;
+{
+ register struct expression *exp;
+ exp = parse_exp_1 (&string, 0, 0);
+ if (*string)
+ error ("Junk after end of expression.");
+ return exp;
+}
+
+/* Stuff for maintaining a stack of types. Currently just used by C, but
+ probably useful for any language which declares its types "backwards". */
+
+void
+push_type (tp)
+ enum type_pieces tp;
+{
+ if (type_stack_depth == type_stack_size)
+ {
+ type_stack_size *= 2;
+ type_stack = (union type_stack_elt *)
+ xrealloc ((char *) type_stack, type_stack_size * sizeof (*type_stack));
+ }
+ type_stack[type_stack_depth++].piece = tp;
+}
+
+void
+push_type_int (n)
+ int n;
+{
+ if (type_stack_depth == type_stack_size)
+ {
+ type_stack_size *= 2;
+ type_stack = (union type_stack_elt *)
+ xrealloc ((char *) type_stack, type_stack_size * sizeof (*type_stack));
+ }
+ type_stack[type_stack_depth++].int_val = n;
+}
+
+enum type_pieces
+pop_type ()
+{
+ if (type_stack_depth)
+ return type_stack[--type_stack_depth].piece;
+ return tp_end;
+}
+
+int
+pop_type_int ()
+{
+ if (type_stack_depth)
+ return type_stack[--type_stack_depth].int_val;
+ /* "Can't happen". */
+ return 0;
+}
+
+/* Pop the type stack and return the type which corresponds to FOLLOW_TYPE
+ as modified by all the stuff on the stack. */
+struct type *
+follow_types (follow_type)
+ struct type *follow_type;
+{
+ int done = 0;
+ int array_size;
+ struct type *range_type;
+
+ while (!done)
+ switch (pop_type ())
+ {
+ case tp_end:
+ done = 1;
+ break;
+ case tp_pointer:
+ follow_type = lookup_pointer_type (follow_type);
+ break;
+ case tp_reference:
+ follow_type = lookup_reference_type (follow_type);
+ break;
+ case tp_array:
+ array_size = pop_type_int ();
+ if (array_size != -1)
+ {
+ range_type =
+ create_range_type ((struct type *) NULL,
+ builtin_type_int, 0,
+ array_size - 1);
+ follow_type =
+ create_array_type ((struct type *) NULL,
+ follow_type, range_type);
+ }
+ else
+ follow_type = lookup_pointer_type (follow_type);
+ break;
+ case tp_function:
+ follow_type = lookup_function_type (follow_type);
+ break;
+ }
+ return follow_type;
+}
+
+void
+_initialize_parse ()
+{
+ type_stack_size = 80;
+ type_stack_depth = 0;
+ type_stack = (union type_stack_elt *)
+ xmalloc (type_stack_size * sizeof (*type_stack));
+}
diff --git a/gnu/usr.bin/gdb/gdb/parser-defs.h b/gnu/usr.bin/gdb/gdb/parser-defs.h
new file mode 100644
index 0000000..5c8710e
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/parser-defs.h
@@ -0,0 +1,188 @@
+/* Parser definitions for GDB.
+ Copyright (C) 1986, 1989, 1990, 1991 Free Software Foundation, Inc.
+ Modified from expread.y by the Department of Computer Science at the
+ State University of New York at Buffalo.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (PARSER_DEFS_H)
+#define PARSER_DEFS_H 1
+
+struct std_regs {
+ char *name;
+ int regnum;
+};
+
+extern struct std_regs std_regs[];
+extern unsigned num_std_regs;
+
+struct expression *expout;
+int expout_size;
+int expout_ptr;
+
+/* If this is nonzero, this block is used as the lexical context
+ for symbol names. */
+
+struct block *expression_context_block;
+
+/* The innermost context required by the stack and register variables
+ we've encountered so far. */
+struct block *innermost_block;
+
+/* The block in which the most recently discovered symbol was found. */
+struct block *block_found;
+
+/* Number of arguments seen so far in innermost function call. */
+int arglist_len;
+
+/* A string token, either a char-string or bit-string. Char-strings are
+ used, for example, for the names of symbols. */
+
+struct stoken
+ {
+ /* Pointer to first byte of char-string or first bit of bit-string */
+ char *ptr;
+ /* Length of string in bytes for char-string or bits for bit-string */
+ int length;
+ };
+
+struct ttype
+ {
+ struct stoken stoken;
+ struct type *type;
+ };
+
+struct symtoken
+ {
+ struct stoken stoken;
+ struct symbol *sym;
+ int is_a_field_of_this;
+ };
+
+/* For parsing of complicated types.
+ An array should be preceded in the list by the size of the array. */
+enum type_pieces
+ {tp_end = -1, tp_pointer, tp_reference, tp_array, tp_function};
+/* The stack can contain either an enum type_pieces or an int. */
+union type_stack_elt {
+ enum type_pieces piece;
+ int int_val;
+};
+union type_stack_elt *type_stack;
+int type_stack_depth, type_stack_size;
+
+extern void
+write_exp_elt PARAMS ((union exp_element));
+
+extern void
+write_exp_elt_opcode PARAMS ((enum exp_opcode));
+
+extern void
+write_exp_elt_sym PARAMS ((struct symbol *));
+
+extern void
+write_exp_elt_longcst PARAMS ((LONGEST));
+
+extern void
+write_exp_elt_dblcst PARAMS ((double));
+
+extern void
+write_exp_elt_type PARAMS ((struct type *));
+
+extern void
+write_exp_elt_intern PARAMS ((struct internalvar *));
+
+extern void
+write_exp_string PARAMS ((struct stoken));
+
+extern void
+write_exp_bitstring PARAMS ((struct stoken));
+
+extern void
+start_arglist PARAMS ((void));
+
+extern int
+end_arglist PARAMS ((void));
+
+extern char *
+copy_name PARAMS ((struct stoken));
+
+extern void
+push_type PARAMS ((enum type_pieces));
+
+extern void
+push_type_int PARAMS ((int));
+
+extern enum type_pieces
+pop_type PARAMS ((void));
+
+extern int
+pop_type_int PARAMS ((void));
+
+extern struct type *follow_types PARAMS ((struct type *));
+
+/* During parsing of a C expression, the pointer to the next character
+ is in this variable. */
+
+char *lexptr;
+
+/* Tokens that refer to names do so with explicit pointer and length,
+ so they can share the storage that lexptr is parsing.
+
+ When it is necessary to pass a name to a function that expects
+ a null-terminated string, the substring is copied out
+ into a block of storage that namecopy points to.
+
+ namecopy is allocated once, guaranteed big enough, for each parsing. */
+
+char *namecopy;
+
+/* Current depth in parentheses within the expression. */
+
+int paren_depth;
+
+/* Nonzero means stop parsing on first comma (if not within parentheses). */
+
+int comma_terminates;
+
+/* These codes indicate operator precedences for expression printing,
+ least tightly binding first. */
+/* Adding 1 to a precedence value is done for binary operators,
+ on the operand which is more tightly bound, so that operators
+ of equal precedence within that operand will get parentheses. */
+/* PREC_HYPER and PREC_ABOVE_COMMA are not the precedence of any operator;
+ they are used as the "surrounding precedence" to force
+ various kinds of things to be parenthesized. */
+enum precedence
+{ PREC_NULL, PREC_COMMA, PREC_ABOVE_COMMA, PREC_ASSIGN, PREC_LOGICAL_OR,
+ PREC_LOGICAL_AND, PREC_BITWISE_IOR, PREC_BITWISE_AND, PREC_BITWISE_XOR,
+ PREC_EQUAL, PREC_ORDER, PREC_SHIFT, PREC_ADD, PREC_MUL, PREC_REPEAT,
+ PREC_HYPER, PREC_PREFIX, PREC_SUFFIX };
+
+/* Table mapping opcodes into strings for printing operators
+ and precedences of the operators. */
+
+struct op_print
+{
+ char *string;
+ enum exp_opcode opcode;
+ /* Precedence of operator. These values are used only by comparisons. */
+ enum precedence precedence;
+ int right_assoc;
+};
+
+#endif /* PARSER_DEFS_H */
diff --git a/gnu/usr.bin/gdb/gdb/partial-stab.h b/gnu/usr.bin/gdb/gdb/partial-stab.h
new file mode 100644
index 0000000..3be0be6
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/partial-stab.h
@@ -0,0 +1,618 @@
+/* Shared code to pre-read a stab (dbx-style), when building a psymtab.
+ Copyright 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993
+ Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* The following need to be defined:
+ SET_NAMESTRING() --Set namestring to name of symbol.
+ CUR_SYMBOL_TYPE --Type code of current symbol.
+ CUR_SYMBOL_VALUE --Value field of current symbol. May be adjusted here.
+ */
+
+/* End of macro definitions, now let's handle them symbols! */
+
+ switch (CUR_SYMBOL_TYPE)
+ {
+ char *p;
+ /*
+ * Standard, external, non-debugger, symbols
+ */
+
+ case N_TEXT | N_EXT:
+ case N_NBTEXT | N_EXT:
+ CUR_SYMBOL_VALUE += ANOFFSET (section_offsets, SECT_OFF_TEXT);
+ goto record_it;
+
+ case N_DATA | N_EXT:
+ case N_NBDATA | N_EXT:
+ CUR_SYMBOL_VALUE += ANOFFSET (section_offsets, SECT_OFF_DATA);
+ goto record_it;
+
+ case N_BSS:
+ case N_BSS | N_EXT:
+ case N_NBBSS | N_EXT:
+ case N_SETV | N_EXT: /* FIXME, is this in BSS? */
+ CUR_SYMBOL_VALUE += ANOFFSET (section_offsets, SECT_OFF_BSS);
+ goto record_it;
+
+ case N_ABS | N_EXT:
+ record_it:
+#ifdef DBXREAD_ONLY
+ SET_NAMESTRING();
+
+ bss_ext_symbol:
+ record_minimal_symbol (namestring, CUR_SYMBOL_VALUE,
+ CUR_SYMBOL_TYPE, objfile); /* Always */
+#endif /* DBXREAD_ONLY */
+ continue;
+
+ /* Standard, local, non-debugger, symbols */
+
+ case N_NBTEXT:
+
+ /* We need to be able to deal with both N_FN or N_TEXT,
+ because we have no way of knowing whether the sys-supplied ld
+ or GNU ld was used to make the executable. Sequents throw
+ in another wrinkle -- they renumbered N_FN. */
+
+ case N_FN:
+ case N_FN_SEQ:
+ case N_TEXT:
+#ifdef DBXREAD_ONLY
+ CUR_SYMBOL_VALUE += ANOFFSET (section_offsets, SECT_OFF_TEXT);
+ SET_NAMESTRING();
+ if ((namestring[0] == '-' && namestring[1] == 'l')
+ || (namestring [(nsl = strlen (namestring)) - 1] == 'o'
+ && namestring [nsl - 2] == '.')
+#ifdef GDB_TARGET_IS_HPPA
+ /* some cooperation from gcc to get around ld stupidity */
+ || (namestring[0] == 'e' && STREQ (namestring, "end_file."))
+#endif
+ )
+ {
+#ifndef GDB_TARGET_IS_HPPA
+ if (objfile -> ei.entry_point < CUR_SYMBOL_VALUE &&
+ objfile -> ei.entry_point >= last_o_file_start)
+ {
+ objfile -> ei.entry_file_lowpc = last_o_file_start;
+ objfile -> ei.entry_file_highpc = CUR_SYMBOL_VALUE;
+ }
+#endif
+ if (past_first_source_file && pst
+ /* The gould NP1 uses low values for .o and -l symbols
+ which are not the address. */
+ && CUR_SYMBOL_VALUE >= pst->textlow)
+ {
+ END_PSYMTAB (pst, psymtab_include_list, includes_used,
+ symnum * symbol_size, CUR_SYMBOL_VALUE,
+ dependency_list, dependencies_used);
+ pst = (struct partial_symtab *) 0;
+ includes_used = 0;
+ dependencies_used = 0;
+ }
+ else
+ past_first_source_file = 1;
+ last_o_file_start = CUR_SYMBOL_VALUE;
+ }
+ else
+ goto record_it;
+#endif /* DBXREAD_ONLY */
+ continue;
+
+ case N_DATA:
+ CUR_SYMBOL_VALUE += ANOFFSET (section_offsets, SECT_OFF_DATA);
+ goto record_it;
+
+ case N_UNDF | N_EXT:
+#ifdef DBXREAD_ONLY
+ if (CUR_SYMBOL_VALUE != 0) {
+ /* This is a "Fortran COMMON" symbol. See if the target
+ environment knows where it has been relocated to. */
+
+ CORE_ADDR reladdr;
+
+ SET_NAMESTRING();
+ if (target_lookup_symbol (namestring, &reladdr)) {
+ continue; /* Error in lookup; ignore symbol for now. */
+ }
+ CUR_SYMBOL_TYPE ^= (N_BSS^N_UNDF); /* Define it as a bss-symbol */
+ CUR_SYMBOL_VALUE = reladdr;
+ goto bss_ext_symbol;
+ }
+#endif /* DBXREAD_ONLY */
+ continue; /* Just undefined, not COMMON */
+
+ case N_UNDF:
+#ifdef DBXREAD_ONLY
+ if (processing_acc_compilation && bufp->n_strx == 1) {
+ /* Deal with relative offsets in the string table
+ used in ELF+STAB under Solaris. If we want to use the
+ n_strx field, which contains the name of the file,
+ we must adjust file_string_table_offset *before* calling
+ SET_NAMESTRING(). */
+ past_first_source_file = 1;
+ file_string_table_offset = next_file_string_table_offset;
+ next_file_string_table_offset =
+ file_string_table_offset + bufp->n_value;
+ if (next_file_string_table_offset < file_string_table_offset)
+ error ("string table offset backs up at %d", symnum);
+ /* FIXME -- replace error() with complaint. */
+ continue;
+ }
+#endif /* DBXREAD_ONLY */
+ continue;
+
+ /* Lots of symbol types we can just ignore. */
+
+ case N_ABS:
+ case N_NBDATA:
+ case N_NBBSS:
+ continue;
+
+ /* Keep going . . .*/
+
+ /*
+ * Special symbol types for GNU
+ */
+ case N_INDR:
+ case N_INDR | N_EXT:
+ case N_SETA:
+ case N_SETA | N_EXT:
+ case N_SETT:
+ case N_SETT | N_EXT:
+ case N_SETD:
+ case N_SETD | N_EXT:
+ case N_SETB:
+ case N_SETB | N_EXT:
+ case N_SETV:
+ continue;
+
+ /*
+ * Debugger symbols
+ */
+
+ case N_SO: {
+ unsigned long valu;
+ static int prev_so_symnum = -10;
+ static int first_so_symnum;
+ char *p;
+
+ valu = CUR_SYMBOL_VALUE + ANOFFSET (section_offsets, SECT_OFF_TEXT);
+
+ past_first_source_file = 1;
+
+ if (prev_so_symnum != symnum - 1)
+ { /* Here if prev stab wasn't N_SO */
+ first_so_symnum = symnum;
+
+ if (pst)
+ {
+ END_PSYMTAB (pst, psymtab_include_list, includes_used,
+ symnum * symbol_size, valu,
+ dependency_list, dependencies_used);
+ pst = (struct partial_symtab *) 0;
+ includes_used = 0;
+ dependencies_used = 0;
+ }
+ }
+
+ prev_so_symnum = symnum;
+
+ /* End the current partial symtab and start a new one */
+
+ SET_NAMESTRING();
+
+ /* Some compilers (including gcc) emit a pair of initial N_SOs.
+ The first one is a directory name; the second the file name.
+ If pst exists, is empty, and has a filename ending in '/',
+ we assume the previous N_SO was a directory name. */
+
+ p = strrchr (namestring, '/');
+ if (p && *(p+1) == '\000')
+ continue; /* Simply ignore directory name SOs */
+
+ /* Some other compilers (C++ ones in particular) emit useless
+ SOs for non-existant .c files. We ignore all subsequent SOs that
+ immediately follow the first. */
+
+ if (!pst)
+ pst = START_PSYMTAB (objfile, section_offsets,
+ namestring, valu,
+ first_so_symnum * symbol_size,
+ objfile -> global_psymbols.next,
+ objfile -> static_psymbols.next);
+ continue;
+ }
+
+ case N_BINCL:
+#ifdef DBXREAD_ONLY
+ /* Add this bincl to the bincl_list for future EXCLs. No
+ need to save the string; it'll be around until
+ read_dbx_symtab function returns */
+
+ SET_NAMESTRING();
+
+ add_bincl_to_list (pst, namestring, CUR_SYMBOL_VALUE);
+
+ /* Mark down an include file in the current psymtab */
+
+ goto record_include_file;
+
+#else /* DBXREAD_ONLY */
+ continue;
+#endif
+
+ case N_SOL:
+ /* Mark down an include file in the current psymtab */
+
+ SET_NAMESTRING();
+
+ /* In C++, one may expect the same filename to come round many
+ times, when code is coming alternately from the main file
+ and from inline functions in other files. So I check to see
+ if this is a file we've seen before -- either the main
+ source file, or a previously included file.
+
+ This seems to be a lot of time to be spending on N_SOL, but
+ things like "break c-exp.y:435" need to work (I
+ suppose the psymtab_include_list could be hashed or put
+ in a binary tree, if profiling shows this is a major hog). */
+ if (pst && STREQ (namestring, pst->filename))
+ continue;
+ {
+ register int i;
+ for (i = 0; i < includes_used; i++)
+ if (STREQ (namestring, psymtab_include_list[i]))
+ {
+ i = -1;
+ break;
+ }
+ if (i == -1)
+ continue;
+ }
+
+#ifdef DBXREAD_ONLY
+ record_include_file:
+#endif
+
+ psymtab_include_list[includes_used++] = namestring;
+ if (includes_used >= includes_allocated)
+ {
+ char **orig = psymtab_include_list;
+
+ psymtab_include_list = (char **)
+ alloca ((includes_allocated *= 2) *
+ sizeof (char *));
+ memcpy ((PTR)psymtab_include_list, (PTR)orig,
+ includes_used * sizeof (char *));
+ }
+ continue;
+
+ case N_LSYM: /* Typedef or automatic variable. */
+ case N_STSYM: /* Data seg var -- static */
+ case N_LCSYM: /* BSS " */
+ case N_ROSYM: /* Read-only data seg var -- static. */
+ case N_NBSTS: /* Gould nobase. */
+ case N_NBLCS: /* symbols. */
+ case N_FUN:
+ case N_GSYM: /* Global (extern) variable; can be
+ data or bss (sigh FIXME). */
+
+ /* Following may probably be ignored; I'll leave them here
+ for now (until I do Pascal and Modula 2 extensions). */
+
+ case N_PC: /* I may or may not need this; I
+ suspect not. */
+ case N_M2C: /* I suspect that I can ignore this here. */
+ case N_SCOPE: /* Same. */
+
+ SET_NAMESTRING();
+
+ p = (char *) strchr (namestring, ':');
+ if (!p)
+ continue; /* Not a debugging symbol. */
+
+
+
+ /* Main processing section for debugging symbols which
+ the initial read through the symbol tables needs to worry
+ about. If we reach this point, the symbol which we are
+ considering is definitely one we are interested in.
+ p must also contain the (valid) index into the namestring
+ which indicates the debugging type symbol. */
+
+ switch (p[1])
+ {
+ case 'S':
+ CUR_SYMBOL_VALUE += ANOFFSET (section_offsets, SECT_OFF_DATA);
+ ADD_PSYMBOL_ADDR_TO_LIST (namestring, p - namestring,
+ VAR_NAMESPACE, LOC_STATIC,
+ objfile->static_psymbols,
+ CUR_SYMBOL_VALUE,
+ psymtab_language, objfile);
+ continue;
+ case 'G':
+ CUR_SYMBOL_VALUE += ANOFFSET (section_offsets, SECT_OFF_DATA);
+ /* The addresses in these entries are reported to be
+ wrong. See the code that reads 'G's for symtabs. */
+ ADD_PSYMBOL_ADDR_TO_LIST (namestring, p - namestring,
+ VAR_NAMESPACE, LOC_STATIC,
+ objfile->global_psymbols,
+ CUR_SYMBOL_VALUE,
+ psymtab_language, objfile);
+ continue;
+
+ case 'T':
+ if (p != namestring) /* a name is there, not just :T... */
+ {
+ ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+ STRUCT_NAMESPACE, LOC_TYPEDEF,
+ objfile->static_psymbols,
+ CUR_SYMBOL_VALUE,
+ psymtab_language, objfile);
+ if (p[2] == 't')
+ {
+ /* Also a typedef with the same name. */
+ ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+ VAR_NAMESPACE, LOC_TYPEDEF,
+ objfile->static_psymbols,
+ CUR_SYMBOL_VALUE, psymtab_language,
+ objfile);
+ p += 1;
+ }
+ }
+ goto check_enum;
+ case 't':
+ if (p != namestring) /* a name is there, not just :T... */
+ {
+ ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+ VAR_NAMESPACE, LOC_TYPEDEF,
+ objfile->static_psymbols,
+ CUR_SYMBOL_VALUE,
+ psymtab_language, objfile);
+ }
+ check_enum:
+ /* If this is an enumerated type, we need to
+ add all the enum constants to the partial symbol
+ table. This does not cover enums without names, e.g.
+ "enum {a, b} c;" in C, but fortunately those are
+ rare. There is no way for GDB to find those from the
+ enum type without spending too much time on it. Thus
+ to solve this problem, the compiler needs to put out the
+ enum in a nameless type. GCC2 does this. */
+
+ /* We are looking for something of the form
+ <name> ":" ("t" | "T") [<number> "="] "e"
+ {<constant> ":" <value> ","} ";". */
+
+ /* Skip over the colon and the 't' or 'T'. */
+ p += 2;
+ /* This type may be given a number. Also, numbers can come
+ in pairs like (0,26). Skip over it. */
+ while ((*p >= '0' && *p <= '9')
+ || *p == '(' || *p == ',' || *p == ')'
+ || *p == '=')
+ p++;
+
+ if (*p++ == 'e')
+ {
+ /* We have found an enumerated type. */
+ /* According to comments in read_enum_type
+ a comma could end it instead of a semicolon.
+ I don't know where that happens.
+ Accept either. */
+ while (*p && *p != ';' && *p != ',')
+ {
+ char *q;
+
+ /* Check for and handle cretinous dbx symbol name
+ continuation! */
+ if (*p == '\\')
+ p = next_symbol_text ();
+
+ /* Point to the character after the name
+ of the enum constant. */
+ for (q = p; *q && *q != ':'; q++)
+ ;
+ /* Note that the value doesn't matter for
+ enum constants in psymtabs, just in symtabs. */
+ ADD_PSYMBOL_TO_LIST (p, q - p,
+ VAR_NAMESPACE, LOC_CONST,
+ objfile->static_psymbols, 0,
+ psymtab_language, objfile);
+ /* Point past the name. */
+ p = q;
+ /* Skip over the value. */
+ while (*p && *p != ',')
+ p++;
+ /* Advance past the comma. */
+ if (*p)
+ p++;
+ }
+ }
+ continue;
+ case 'c':
+ /* Constant, e.g. from "const" in Pascal. */
+ ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+ VAR_NAMESPACE, LOC_CONST,
+ objfile->static_psymbols, CUR_SYMBOL_VALUE,
+ psymtab_language, objfile);
+ continue;
+
+ case 'f':
+#ifdef DBXREAD_ONLY
+ /* Kludges for ELF/STABS with Sun ACC */
+ last_function_name = namestring;
+ if (pst && pst->textlow == 0)
+ pst->textlow = CUR_SYMBOL_VALUE;
+#if 0
+ if (startup_file_end == 0)
+ startup_file_end = CUR_SYMBOL_VALUE;
+#endif
+ /* End kludge. */
+#endif /* DBXREAD_ONLY */
+ ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+ VAR_NAMESPACE, LOC_BLOCK,
+ objfile->static_psymbols, CUR_SYMBOL_VALUE,
+ psymtab_language, objfile);
+ continue;
+
+ /* Global functions were ignored here, but now they
+ are put into the global psymtab like one would expect.
+ They're also in the minimal symbol table. */
+ case 'F':
+#ifdef DBXREAD_ONLY
+ /* Kludges for ELF/STABS with Sun ACC */
+ last_function_name = namestring;
+ if (pst && pst->textlow == 0)
+ pst->textlow = CUR_SYMBOL_VALUE;
+#if 0
+ if (startup_file_end == 0)
+ startup_file_end = CUR_SYMBOL_VALUE;
+#endif
+ /* End kludge. */
+#endif /* DBXREAD_ONLY */
+ ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+ VAR_NAMESPACE, LOC_BLOCK,
+ objfile->global_psymbols, CUR_SYMBOL_VALUE,
+ psymtab_language, objfile);
+ continue;
+
+ /* Two things show up here (hopefully); static symbols of
+ local scope (static used inside braces) or extensions
+ of structure symbols. We can ignore both. */
+ case 'V':
+ case '(':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ continue;
+
+ default:
+ /* Unexpected symbol. Ignore it; perhaps it is an extension
+ that we don't know about.
+
+ Someone says sun cc puts out symbols like
+ /foo/baz/maclib::/usr/local/bin/maclib,
+ which would get here with a symbol type of ':'. */
+ complain (&unknown_symchar_complaint, p[1]);
+ continue;
+ }
+
+ case N_EXCL:
+#ifdef DBXREAD_ONLY
+
+ SET_NAMESTRING();
+
+ /* Find the corresponding bincl and mark that psymtab on the
+ psymtab dependency list */
+ {
+ struct partial_symtab *needed_pst =
+ find_corresponding_bincl_psymtab (namestring, CUR_SYMBOL_VALUE);
+
+ /* If this include file was defined earlier in this file,
+ leave it alone. */
+ if (needed_pst == pst) continue;
+
+ if (needed_pst)
+ {
+ int i;
+ int found = 0;
+
+ for (i = 0; i < dependencies_used; i++)
+ if (dependency_list[i] == needed_pst)
+ {
+ found = 1;
+ break;
+ }
+
+ /* If it's already in the list, skip the rest. */
+ if (found) continue;
+
+ dependency_list[dependencies_used++] = needed_pst;
+ if (dependencies_used >= dependencies_allocated)
+ {
+ struct partial_symtab **orig = dependency_list;
+ dependency_list =
+ (struct partial_symtab **)
+ alloca ((dependencies_allocated *= 2)
+ * sizeof (struct partial_symtab *));
+ memcpy ((PTR)dependency_list, (PTR)orig,
+ (dependencies_used
+ * sizeof (struct partial_symtab *)));
+#ifdef DEBUG_INFO
+ fprintf (stderr, "Had to reallocate dependency list.\n");
+ fprintf (stderr, "New dependencies allocated: %d\n",
+ dependencies_allocated);
+#endif
+ }
+ }
+ else
+ error ("Invalid symbol data: \"repeated\" header file not previously seen, at symtab pos %d.",
+ symnum);
+ }
+#endif /* DBXREAD_ONLY */
+ continue;
+
+ case N_RBRAC:
+#ifdef HANDLE_RBRAC
+ HANDLE_RBRAC(CUR_SYMBOL_VALUE);
+ continue;
+#endif
+ case N_EINCL:
+ case N_DSLINE:
+ case N_BSLINE:
+ case N_SSYM: /* Claim: Structure or union element.
+ Hopefully, I can ignore this. */
+ case N_ENTRY: /* Alternate entry point; can ignore. */
+ case N_MAIN: /* Can definitely ignore this. */
+ case N_CATCH: /* These are GNU C++ extensions */
+ case N_EHDECL: /* that can safely be ignored here. */
+ case N_LENG:
+ case N_BCOMM:
+ case N_ECOMM:
+ case N_ECOML:
+ case N_FNAME:
+ case N_SLINE:
+ case N_RSYM:
+ case N_PSYM:
+ case N_LBRAC:
+ case N_NSYMS: /* Ultrix 4.0: symbol count */
+ case N_DEFD: /* GNU Modula-2 */
+
+ case N_OBJ: /* useless types from Solaris */
+ case N_OPT:
+ case N_ENDM:
+ /* These symbols aren't interesting; don't worry about them */
+
+ continue;
+
+ default:
+ /* If we haven't found it yet, ignore it. It's probably some
+ new type we don't know about yet. */
+ complain (&unknown_symtype_complaint,
+ local_hex_string ((unsigned long) CUR_SYMBOL_TYPE));
+ continue;
+ }
diff --git a/gnu/usr.bin/gdb/gdb/printcmd.c b/gnu/usr.bin/gdb/gdb/printcmd.c
new file mode 100644
index 0000000..3530ac2
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/printcmd.c
@@ -0,0 +1,2078 @@
+/* Print values for GNU debugger GDB.
+ Copyright 1986, 1987, 1988, 1989, 1990, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include <string.h>
+#include <varargs.h>
+#include "frame.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "value.h"
+#include "language.h"
+#include "expression.h"
+#include "gdbcore.h"
+#include "gdbcmd.h"
+#include "target.h"
+#include "breakpoint.h"
+#include "demangle.h"
+
+extern int asm_demangle; /* Whether to demangle syms in asm printouts */
+extern int addressprint; /* Whether to print hex addresses in HLL " */
+
+struct format_data
+{
+ int count;
+ char format;
+ char size;
+};
+
+/* Last specified output format. */
+
+static char last_format = 'x';
+
+/* Last specified examination size. 'b', 'h', 'w' or `q'. */
+
+static char last_size = 'w';
+
+/* Default address to examine next. */
+
+static CORE_ADDR next_address;
+
+/* Last address examined. */
+
+static CORE_ADDR last_examine_address;
+
+/* Contents of last address examined.
+ This is not valid past the end of the `x' command! */
+
+static value last_examine_value;
+
+/* Largest offset between a symbolic value and an address, that will be
+ printed as `0x1234 <symbol+offset>'. */
+
+static unsigned int max_symbolic_offset = UINT_MAX;
+
+/* Append the source filename and linenumber of the symbol when
+ printing a symbolic value as `<symbol at filename:linenum>' if set. */
+static int print_symbol_filename = 0;
+
+/* Number of auto-display expression currently being displayed.
+ So that we can disable it if we get an error or a signal within it.
+ -1 when not doing one. */
+
+int current_display_number;
+
+/* Flag to low-level print routines that this value is being printed
+ in an epoch window. We'd like to pass this as a parameter, but
+ every routine would need to take it. Perhaps we can encapsulate
+ this in the I/O stream once we have GNU stdio. */
+
+int inspect_it = 0;
+
+struct display
+{
+ /* Chain link to next auto-display item. */
+ struct display *next;
+ /* Expression to be evaluated and displayed. */
+ struct expression *exp;
+ /* Item number of this auto-display item. */
+ int number;
+ /* Display format specified. */
+ struct format_data format;
+ /* Innermost block required by this expression when evaluated */
+ struct block *block;
+ /* Status of this display (enabled or disabled) */
+ enum enable status;
+};
+
+/* Chain of expressions whose values should be displayed
+ automatically each time the program stops. */
+
+static struct display *display_chain;
+
+static int display_number;
+
+/* Prototypes for local functions */
+
+static void
+delete_display PARAMS ((int));
+
+static void
+enable_display PARAMS ((char *, int));
+
+static void
+disable_display_command PARAMS ((char *, int));
+
+static void
+disassemble_command PARAMS ((char *, int));
+
+static void
+printf_command PARAMS ((char *, int));
+
+static void
+print_frame_nameless_args PARAMS ((struct frame_info *, long, int, int,
+ FILE *));
+
+static void
+display_info PARAMS ((char *, int));
+
+static void
+do_one_display PARAMS ((struct display *));
+
+static void
+undisplay_command PARAMS ((char *, int));
+
+static void
+free_display PARAMS ((struct display *));
+
+static void
+display_command PARAMS ((char *, int));
+
+static void
+x_command PARAMS ((char *, int));
+
+static void
+address_info PARAMS ((char *, int));
+
+static void
+set_command PARAMS ((char *, int));
+
+static void
+output_command PARAMS ((char *, int));
+
+static void
+call_command PARAMS ((char *, int));
+
+static void
+inspect_command PARAMS ((char *, int));
+
+static void
+print_command PARAMS ((char *, int));
+
+static void
+print_command_1 PARAMS ((char *, int, int));
+
+static void
+validate_format PARAMS ((struct format_data, char *));
+
+static void
+do_examine PARAMS ((struct format_data, CORE_ADDR));
+
+static void
+print_formatted PARAMS ((value, int, int));
+
+static struct format_data
+decode_format PARAMS ((char **, int, int));
+
+
+/* Decode a format specification. *STRING_PTR should point to it.
+ OFORMAT and OSIZE are used as defaults for the format and size
+ if none are given in the format specification.
+ If OSIZE is zero, then the size field of the returned value
+ should be set only if a size is explicitly specified by the
+ user.
+ The structure returned describes all the data
+ found in the specification. In addition, *STRING_PTR is advanced
+ past the specification and past all whitespace following it. */
+
+static struct format_data
+decode_format (string_ptr, oformat, osize)
+ char **string_ptr;
+ int oformat;
+ int osize;
+{
+ struct format_data val;
+ register char *p = *string_ptr;
+
+ val.format = '?';
+ val.size = '?';
+ val.count = 1;
+
+ if (*p >= '0' && *p <= '9')
+ val.count = atoi (p);
+ while (*p >= '0' && *p <= '9') p++;
+
+ /* Now process size or format letters that follow. */
+
+ while (1)
+ {
+ if (*p == 'b' || *p == 'h' || *p == 'w' || *p == 'g')
+ val.size = *p++;
+ else if (*p >= 'a' && *p <= 'z')
+ val.format = *p++;
+ else
+ break;
+ }
+
+#ifndef CC_HAS_LONG_LONG
+ /* Make sure 'g' size is not used on integer types.
+ Well, actually, we can handle hex. */
+ if (val.size == 'g' && val.format != 'f' && val.format != 'x')
+ val.size = 'w';
+#endif
+
+ while (*p == ' ' || *p == '\t') p++;
+ *string_ptr = p;
+
+ /* Set defaults for format and size if not specified. */
+ if (val.format == '?')
+ {
+ if (val.size == '?')
+ {
+ /* Neither has been specified. */
+ val.format = oformat;
+ val.size = osize;
+ }
+ else
+ /* If a size is specified, any format makes a reasonable
+ default except 'i'. */
+ val.format = oformat == 'i' ? 'x' : oformat;
+ }
+ else if (val.size == '?')
+ switch (val.format)
+ {
+ case 'a':
+ case 's':
+ /* Addresses must be words. */
+ val.size = osize ? 'w' : osize;
+ break;
+ case 'f':
+ /* Floating point has to be word or giantword. */
+ if (osize == 'w' || osize == 'g')
+ val.size = osize;
+ else
+ /* Default it to giantword if the last used size is not
+ appropriate. */
+ val.size = osize ? 'g' : osize;
+ break;
+ case 'c':
+ /* Characters default to one byte. */
+ val.size = osize ? 'b' : osize;
+ break;
+ default:
+ /* The default is the size most recently specified. */
+ val.size = osize;
+ }
+
+ return val;
+}
+
+/* Print value VAL on stdout according to FORMAT, a letter or 0.
+ Do not end with a newline.
+ 0 means print VAL according to its own type.
+ SIZE is the letter for the size of datum being printed.
+ This is used to pad hex numbers so they line up. */
+
+static void
+print_formatted (val, format, size)
+ register value val;
+ register int format;
+ int size;
+{
+ int len = TYPE_LENGTH (VALUE_TYPE (val));
+
+ if (VALUE_LVAL (val) == lval_memory)
+ next_address = VALUE_ADDRESS (val) + len;
+
+ switch (format)
+ {
+ case 's':
+ next_address = VALUE_ADDRESS (val)
+ + value_print (value_addr (val), stdout, format, Val_pretty_default);
+ break;
+
+ case 'i':
+ /* The old comment says
+ "Force output out, print_insn not using _filtered".
+ I'm not completely sure what that means, I suspect most print_insn
+ now do use _filtered, so I guess it's obsolete. */
+ /* We often wrap here if there are long symbolic names. */
+ wrap_here (" ");
+ next_address = VALUE_ADDRESS (val)
+ + print_insn (VALUE_ADDRESS (val), stdout);
+ break;
+
+ default:
+ if (format == 0
+ || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_ARRAY
+ || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_STRING
+ || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_STRUCT
+ || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_UNION
+ || VALUE_REPEATED (val))
+ value_print (val, stdout, format, Val_pretty_default);
+ else
+ print_scalar_formatted (VALUE_CONTENTS (val), VALUE_TYPE (val),
+ format, size, stdout);
+ }
+}
+
+/* Print a scalar of data of type TYPE, pointed to in GDB by VALADDR,
+ according to letters FORMAT and SIZE on STREAM.
+ FORMAT may not be zero. Formats s and i are not supported at this level.
+
+ This is how the elements of an array or structure are printed
+ with a format. */
+
+void
+print_scalar_formatted (valaddr, type, format, size, stream)
+ char *valaddr;
+ struct type *type;
+ int format;
+ int size;
+ FILE *stream;
+{
+ LONGEST val_long;
+ int len = TYPE_LENGTH (type);
+
+ if (len > sizeof (LONGEST)
+ && (format == 't'
+ || format == 'c'
+ || format == 'o'
+ || format == 'u'
+ || format == 'd'
+ || format == 'x'))
+ {
+ /* We can't print it normally, but we can print it in hex.
+ Printing it in the wrong radix is more useful than saying
+ "use /x, you dummy". */
+ /* FIXME: we could also do octal or binary if that was the
+ desired format. */
+ /* FIXME: we should be using the size field to give us a minimum
+ field width to print. */
+ val_print_type_code_int (type, valaddr, stream);
+ return;
+ }
+
+ val_long = unpack_long (type, valaddr);
+
+ /* If we are printing it as unsigned, truncate it in case it is actually
+ a negative signed value (e.g. "print/u (short)-1" should print 65535
+ (if shorts are 16 bits) instead of 4294967295). */
+ if (format != 'd')
+ {
+ if (len < sizeof (LONGEST))
+ val_long &= ((LONGEST) 1 << HOST_CHAR_BIT * len) - 1;
+ }
+
+ switch (format)
+ {
+ case 'x':
+ if (!size)
+ {
+ /* no size specified, like in print. Print varying # of digits. */
+ print_longest (stream, 'x', 1, val_long);
+ }
+ else
+ switch (size)
+ {
+ case 'b':
+ case 'h':
+ case 'w':
+ case 'g':
+ print_longest (stream, size, 1, val_long);
+ break;
+ default:
+ error ("Undefined output size \"%c\".", size);
+ }
+ break;
+
+ case 'd':
+ print_longest (stream, 'd', 1, val_long);
+ break;
+
+ case 'u':
+ print_longest (stream, 'u', 0, val_long);
+ break;
+
+ case 'o':
+ if (val_long)
+ print_longest (stream, 'o', 1, val_long);
+ else
+ fprintf_filtered (stream, "0");
+ break;
+
+ case 'a':
+ print_address (unpack_pointer (type, valaddr), stream);
+ break;
+
+ case 'c':
+ value_print (value_from_longest (builtin_type_char, val_long), stream, 0,
+ Val_pretty_default);
+ break;
+
+ case 'f':
+ if (len == sizeof (float))
+ type = builtin_type_float;
+ else if (len == sizeof (double))
+ type = builtin_type_double;
+ print_floating (valaddr, type, stream);
+ break;
+
+ case 0:
+ abort ();
+
+ case 't':
+ /* Binary; 't' stands for "two". */
+ {
+ char bits[8*(sizeof val_long) + 1];
+ char *cp = bits;
+ int width;
+
+ if (!size)
+ width = 8*(sizeof val_long);
+ else
+ switch (size)
+ {
+ case 'b':
+ width = 8;
+ break;
+ case 'h':
+ width = 16;
+ break;
+ case 'w':
+ width = 32;
+ break;
+ case 'g':
+ width = 64;
+ break;
+ default:
+ error ("Undefined output size \"%c\".", size);
+ }
+
+ bits[width] = '\0';
+ while (width-- > 0)
+ {
+ bits[width] = (val_long & 1) ? '1' : '0';
+ val_long >>= 1;
+ }
+ if (!size)
+ {
+ while (*cp && *cp == '0')
+ cp++;
+ if (*cp == '\0')
+ cp--;
+ }
+ fprintf_filtered (stream, local_binary_format_prefix());
+ fprintf_filtered (stream, cp);
+ fprintf_filtered (stream, local_binary_format_suffix());
+ }
+ break;
+
+ default:
+ error ("Undefined output format \"%c\".", format);
+ }
+}
+
+/* Specify default address for `x' command.
+ `info lines' uses this. */
+
+void
+set_next_address (addr)
+ CORE_ADDR addr;
+{
+ next_address = addr;
+
+ /* Make address available to the user as $_. */
+ set_internalvar (lookup_internalvar ("_"),
+ value_from_longest (lookup_pointer_type (builtin_type_void),
+ (LONGEST) addr));
+}
+
+/* Optionally print address ADDR symbolically as <SYMBOL+OFFSET> on STREAM,
+ after LEADIN. Print nothing if no symbolic name is found nearby.
+ DO_DEMANGLE controls whether to print a symbol in its native "raw" form,
+ or to interpret it as a possible C++ name and convert it back to source
+ form. However note that DO_DEMANGLE can be overridden by the specific
+ settings of the demangle and asm_demangle variables. */
+
+void
+print_address_symbolic (addr, stream, do_demangle, leadin)
+ CORE_ADDR addr;
+ FILE *stream;
+ int do_demangle;
+ char *leadin;
+{
+ CORE_ADDR name_location;
+ register struct symbol *symbol;
+ char *name;
+
+ /* First try to find the address in the symbol tables to find
+ static functions. If that doesn't succeed we try the minimal symbol
+ vector for symbols in non-text space.
+ FIXME: Should find a way to get at the static non-text symbols too. */
+
+ symbol = find_pc_function (addr);
+ if (symbol)
+ {
+ name_location = BLOCK_START (SYMBOL_BLOCK_VALUE (symbol));
+ if (do_demangle)
+ name = SYMBOL_SOURCE_NAME (symbol);
+ else
+ name = SYMBOL_LINKAGE_NAME (symbol);
+ }
+ else
+ {
+ register struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (addr);
+
+ /* If nothing comes out, don't print anything symbolic. */
+ if (msymbol == NULL)
+ return;
+ name_location = SYMBOL_VALUE_ADDRESS (msymbol);
+ if (do_demangle)
+ name = SYMBOL_SOURCE_NAME (msymbol);
+ else
+ name = SYMBOL_LINKAGE_NAME (msymbol);
+ }
+
+ /* If the nearest symbol is too far away, don't print anything symbolic. */
+
+ /* For when CORE_ADDR is larger than unsigned int, we do math in
+ CORE_ADDR. But when we detect unsigned wraparound in the
+ CORE_ADDR math, we ignore this test and print the offset,
+ because addr+max_symbolic_offset has wrapped through the end
+ of the address space back to the beginning, giving bogus comparison. */
+ if (addr > name_location + max_symbolic_offset
+ && name_location + max_symbolic_offset > name_location)
+ return;
+
+ fputs_filtered (leadin, stream);
+ fputs_filtered ("<", stream);
+ fputs_filtered (name, stream);
+ if (addr != name_location)
+ fprintf_filtered (stream, "+%u", (unsigned int)(addr - name_location));
+
+ /* Append source filename and line number if desired. */
+ if (symbol && print_symbol_filename)
+ {
+ struct symtab_and_line sal;
+
+ sal = find_pc_line (addr, 0);
+ if (sal.symtab)
+ fprintf_filtered (stream, " at %s:%d", sal.symtab->filename, sal.line);
+ }
+ fputs_filtered (">", stream);
+}
+
+/* Print address ADDR symbolically on STREAM.
+ First print it as a number. Then perhaps print
+ <SYMBOL + OFFSET> after the number. */
+
+void
+print_address (addr, stream)
+ CORE_ADDR addr;
+ FILE *stream;
+{
+#if 0 && defined (ADDR_BITS_REMOVE)
+ /* This is wrong for pointer to char, in which we do want to print
+ the low bits. */
+ fprintf_filtered (stream, local_hex_format(),
+ (unsigned long) ADDR_BITS_REMOVE(addr));
+#else
+ fprintf_filtered (stream, local_hex_format(), (unsigned long) addr);
+#endif
+ print_address_symbolic (addr, stream, asm_demangle, " ");
+}
+
+/* Print address ADDR symbolically on STREAM. Parameter DEMANGLE
+ controls whether to print the symbolic name "raw" or demangled.
+ Global setting "addressprint" controls whether to print hex address
+ or not. */
+
+void
+print_address_demangle (addr, stream, do_demangle)
+ CORE_ADDR addr;
+ FILE *stream;
+ int do_demangle;
+{
+ if (addr == 0) {
+ fprintf_filtered (stream, "0");
+ } else if (addressprint) {
+ fprintf_filtered (stream, local_hex_format(), (unsigned long) addr);
+ print_address_symbolic (addr, stream, do_demangle, " ");
+ } else {
+ print_address_symbolic (addr, stream, do_demangle, "");
+ }
+}
+
+
+/* These are the types that $__ will get after an examine command of one
+ of these sizes. */
+
+static struct type *examine_b_type;
+static struct type *examine_h_type;
+static struct type *examine_w_type;
+static struct type *examine_g_type;
+
+/* Examine data at address ADDR in format FMT.
+ Fetch it from memory and print on stdout. */
+
+static void
+do_examine (fmt, addr)
+ struct format_data fmt;
+ CORE_ADDR addr;
+{
+ register char format = 0;
+ register char size;
+ register int count = 1;
+ struct type *val_type = NULL;
+ register int i;
+ register int maxelts;
+
+ format = fmt.format;
+ size = fmt.size;
+ count = fmt.count;
+ next_address = addr;
+
+ /* String or instruction format implies fetch single bytes
+ regardless of the specified size. */
+ if (format == 's' || format == 'i')
+ size = 'b';
+
+ if (size == 'b')
+ val_type = examine_b_type;
+ else if (size == 'h')
+ val_type = examine_h_type;
+ else if (size == 'w')
+ val_type = examine_w_type;
+ else if (size == 'g')
+ val_type = examine_g_type;
+
+ maxelts = 8;
+ if (size == 'w')
+ maxelts = 4;
+ if (size == 'g')
+ maxelts = 2;
+ if (format == 's' || format == 'i')
+ maxelts = 1;
+
+ /* Print as many objects as specified in COUNT, at most maxelts per line,
+ with the address of the next one at the start of each line. */
+
+ while (count > 0)
+ {
+ print_address (next_address, stdout);
+ printf_filtered (":");
+ for (i = maxelts;
+ i > 0 && count > 0;
+ i--, count--)
+ {
+ printf_filtered ("\t");
+ /* Note that print_formatted sets next_address for the next
+ object. */
+ last_examine_address = next_address;
+ last_examine_value = value_at (val_type, next_address);
+ print_formatted (last_examine_value, format, size);
+ }
+ printf_filtered ("\n");
+ fflush (stdout);
+ }
+}
+
+static void
+validate_format (fmt, cmdname)
+ struct format_data fmt;
+ char *cmdname;
+{
+ if (fmt.size != 0)
+ error ("Size letters are meaningless in \"%s\" command.", cmdname);
+ if (fmt.count != 1)
+ error ("Item count other than 1 is meaningless in \"%s\" command.",
+ cmdname);
+ if (fmt.format == 'i' || fmt.format == 's')
+ error ("Format letter \"%c\" is meaningless in \"%s\" command.",
+ fmt.format, cmdname);
+}
+
+/* Evaluate string EXP as an expression in the current language and
+ print the resulting value. EXP may contain a format specifier as the
+ first argument ("/x myvar" for example, to print myvar in hex).
+ */
+
+static void
+print_command_1 (exp, inspect, voidprint)
+ char *exp;
+ int inspect;
+ int voidprint;
+{
+ struct expression *expr;
+ register struct cleanup *old_chain = 0;
+ register char format = 0;
+ register value val;
+ struct format_data fmt;
+ int cleanup = 0;
+
+ /* Pass inspect flag to the rest of the print routines in a global (sigh). */
+ inspect_it = inspect;
+
+ if (exp && *exp == '/')
+ {
+ exp++;
+ fmt = decode_format (&exp, last_format, 0);
+ validate_format (fmt, "print");
+ last_format = format = fmt.format;
+ }
+ else
+ {
+ fmt.count = 1;
+ fmt.format = 0;
+ fmt.size = 0;
+ }
+
+ if (exp && *exp)
+ {
+ extern int objectprint;
+ struct type *type;
+ expr = parse_expression (exp);
+ old_chain = make_cleanup (free_current_contents, &expr);
+ cleanup = 1;
+ val = evaluate_expression (expr);
+
+ /* C++: figure out what type we actually want to print it as. */
+ type = VALUE_TYPE (val);
+
+ if (objectprint
+ && ( TYPE_CODE (type) == TYPE_CODE_PTR
+ || TYPE_CODE (type) == TYPE_CODE_REF)
+ && ( TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_STRUCT
+ || TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_UNION))
+ {
+ value v;
+
+ v = value_from_vtable_info (val, TYPE_TARGET_TYPE (type));
+ if (v != 0)
+ {
+ val = v;
+ type = VALUE_TYPE (val);
+ }
+ }
+ }
+ else
+ val = access_value_history (0);
+
+ if (voidprint || (val && VALUE_TYPE (val) &&
+ TYPE_CODE (VALUE_TYPE (val)) != TYPE_CODE_VOID))
+ {
+ int histindex = record_latest_value (val);
+
+ if (inspect)
+ printf ("\031(gdb-makebuffer \"%s\" %d '(\"", exp, histindex);
+ else
+ if (histindex >= 0) printf_filtered ("$%d = ", histindex);
+
+ print_formatted (val, format, fmt.size);
+ printf_filtered ("\n");
+ if (inspect)
+ printf("\") )\030");
+ }
+
+ if (cleanup)
+ do_cleanups (old_chain);
+ inspect_it = 0; /* Reset print routines to normal */
+}
+
+/* ARGSUSED */
+static void
+print_command (exp, from_tty)
+ char *exp;
+ int from_tty;
+{
+ print_command_1 (exp, 0, 1);
+}
+
+/* Same as print, except in epoch, it gets its own window */
+/* ARGSUSED */
+static void
+inspect_command (exp, from_tty)
+ char *exp;
+ int from_tty;
+{
+ extern int epoch_interface;
+
+ print_command_1 (exp, epoch_interface, 1);
+}
+
+/* Same as print, except it doesn't print void results. */
+/* ARGSUSED */
+static void
+call_command (exp, from_tty)
+ char *exp;
+ int from_tty;
+{
+ print_command_1 (exp, 0, 0);
+}
+
+/* ARGSUSED */
+static void
+output_command (exp, from_tty)
+ char *exp;
+ int from_tty;
+{
+ struct expression *expr;
+ register struct cleanup *old_chain;
+ register char format = 0;
+ register value val;
+ struct format_data fmt;
+
+ if (exp && *exp == '/')
+ {
+ exp++;
+ fmt = decode_format (&exp, 0, 0);
+ validate_format (fmt, "output");
+ format = fmt.format;
+ }
+
+ expr = parse_expression (exp);
+ old_chain = make_cleanup (free_current_contents, &expr);
+
+ val = evaluate_expression (expr);
+
+ print_formatted (val, format, fmt.size);
+
+ do_cleanups (old_chain);
+}
+
+/* ARGSUSED */
+static void
+set_command (exp, from_tty)
+ char *exp;
+ int from_tty;
+{
+ struct expression *expr = parse_expression (exp);
+ register struct cleanup *old_chain
+ = make_cleanup (free_current_contents, &expr);
+ evaluate_expression (expr);
+ do_cleanups (old_chain);
+}
+
+/* ARGSUSED */
+static void
+address_info (exp, from_tty)
+ char *exp;
+ int from_tty;
+{
+ register struct symbol *sym;
+ register struct minimal_symbol *msymbol;
+ register long val;
+ register long basereg;
+ int is_a_field_of_this; /* C++: lookup_symbol sets this to nonzero
+ if exp is a field of `this'. */
+
+ if (exp == 0)
+ error ("Argument required.");
+
+ sym = lookup_symbol (exp, get_selected_block (), VAR_NAMESPACE,
+ &is_a_field_of_this, (struct symtab **)NULL);
+ if (sym == NULL)
+ {
+ if (is_a_field_of_this)
+ {
+ printf ("Symbol \"%s\" is a field of the local class variable `this'\n", exp);
+ return;
+ }
+
+ msymbol = lookup_minimal_symbol (exp, (struct objfile *) NULL);
+
+ if (msymbol != NULL)
+ printf ("Symbol \"%s\" is at %s in a file compiled without debugging.\n",
+ exp,
+ local_hex_string((unsigned long) SYMBOL_VALUE_ADDRESS (msymbol)));
+ else
+ error ("No symbol \"%s\" in current context.", exp);
+ return;
+ }
+
+ printf ("Symbol \"%s\" is ", SYMBOL_NAME (sym));
+ val = SYMBOL_VALUE (sym);
+ basereg = SYMBOL_BASEREG (sym);
+
+ switch (SYMBOL_CLASS (sym))
+ {
+ case LOC_CONST:
+ case LOC_CONST_BYTES:
+ printf ("constant");
+ break;
+
+ case LOC_LABEL:
+ printf ("a label at address %s",
+ local_hex_string((unsigned long) SYMBOL_VALUE_ADDRESS (sym)));
+ break;
+
+ case LOC_REGISTER:
+ printf ("a variable in register %s", reg_names[val]);
+ break;
+
+ case LOC_STATIC:
+ printf ("static storage at address %s",
+ local_hex_string((unsigned long) SYMBOL_VALUE_ADDRESS (sym)));
+ break;
+
+ case LOC_REGPARM:
+ printf ("an argument in register %s", reg_names[val]);
+ break;
+
+ case LOC_REGPARM_ADDR:
+ printf ("address of an argument in register %s", reg_names[val]);
+ break;
+
+ case LOC_ARG:
+ printf ("an argument at offset %ld", val);
+ break;
+
+ case LOC_LOCAL_ARG:
+ printf ("an argument at frame offset %ld", val);
+ break;
+
+ case LOC_LOCAL:
+ printf ("a local variable at frame offset %ld", val);
+ break;
+
+ case LOC_REF_ARG:
+ printf ("a reference argument at offset %ld", val);
+ break;
+
+ case LOC_BASEREG:
+ printf ("a variable at offset %ld from register %s",
+ val, reg_names[basereg]);
+ break;
+
+ case LOC_BASEREG_ARG:
+ printf ("an argument at offset %ld from register %s",
+ val, reg_names[basereg]);
+ break;
+
+ case LOC_TYPEDEF:
+ printf ("a typedef");
+ break;
+
+ case LOC_BLOCK:
+ printf ("a function at address %s",
+ local_hex_string((unsigned long) BLOCK_START (SYMBOL_BLOCK_VALUE (sym))));
+ break;
+
+ case LOC_OPTIMIZED_OUT:
+ printf_filtered ("optimized out");
+ break;
+
+ default:
+ printf ("of unknown (botched) type");
+ break;
+ }
+ printf (".\n");
+}
+
+static void
+x_command (exp, from_tty)
+ char *exp;
+ int from_tty;
+{
+ struct expression *expr;
+ struct format_data fmt;
+ struct cleanup *old_chain;
+ struct value *val;
+
+ fmt.format = last_format;
+ fmt.size = last_size;
+ fmt.count = 1;
+
+ if (exp && *exp == '/')
+ {
+ exp++;
+ fmt = decode_format (&exp, last_format, last_size);
+ }
+
+ /* If we have an expression, evaluate it and use it as the address. */
+
+ if (exp != 0 && *exp != 0)
+ {
+ expr = parse_expression (exp);
+ /* Cause expression not to be there any more
+ if this command is repeated with Newline.
+ But don't clobber a user-defined command's definition. */
+ if (from_tty)
+ *exp = 0;
+ old_chain = make_cleanup (free_current_contents, &expr);
+ val = evaluate_expression (expr);
+ if (TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_REF)
+ val = value_ind (val);
+ /* In rvalue contexts, such as this, functions are coerced into
+ pointers to functions. This makes "x/i main" work. */
+ if (/* last_format == 'i'
+ && */ TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_FUNC
+ && VALUE_LVAL (val) == lval_memory)
+ next_address = VALUE_ADDRESS (val);
+ else
+ next_address = value_as_pointer (val);
+ do_cleanups (old_chain);
+ }
+
+ do_examine (fmt, next_address);
+
+ /* If the examine succeeds, we remember its size and format for next time. */
+ last_size = fmt.size;
+ last_format = fmt.format;
+
+ /* Set a couple of internal variables if appropriate. */
+ if (last_examine_value)
+ {
+ /* Make last address examined available to the user as $_. Use
+ the correct pointer type. */
+ set_internalvar (lookup_internalvar ("_"),
+ value_from_longest (
+ lookup_pointer_type (VALUE_TYPE (last_examine_value)),
+ (LONGEST) last_examine_address));
+
+ /* Make contents of last address examined available to the user as $__.*/
+ set_internalvar (lookup_internalvar ("__"), last_examine_value);
+ }
+}
+
+
+/* Add an expression to the auto-display chain.
+ Specify the expression. */
+
+static void
+display_command (exp, from_tty)
+ char *exp;
+ int from_tty;
+{
+ struct format_data fmt;
+ register struct expression *expr;
+ register struct display *new;
+
+ if (exp == 0)
+ {
+ do_displays ();
+ return;
+ }
+
+ if (*exp == '/')
+ {
+ exp++;
+ fmt = decode_format (&exp, 0, 0);
+ if (fmt.size && fmt.format == 0)
+ fmt.format = 'x';
+ if (fmt.format == 'i' || fmt.format == 's')
+ fmt.size = 'b';
+ }
+ else
+ {
+ fmt.format = 0;
+ fmt.size = 0;
+ fmt.count = 0;
+ }
+
+ innermost_block = 0;
+ expr = parse_expression (exp);
+
+ new = (struct display *) xmalloc (sizeof (struct display));
+
+ new->exp = expr;
+ new->block = innermost_block;
+ new->next = display_chain;
+ new->number = ++display_number;
+ new->format = fmt;
+ new->status = enabled;
+ display_chain = new;
+
+ if (from_tty && target_has_execution)
+ do_one_display (new);
+
+ dont_repeat ();
+}
+
+static void
+free_display (d)
+ struct display *d;
+{
+ free ((PTR)d->exp);
+ free ((PTR)d);
+}
+
+/* Clear out the display_chain.
+ Done when new symtabs are loaded, since this invalidates
+ the types stored in many expressions. */
+
+void
+clear_displays ()
+{
+ register struct display *d;
+
+ while ((d = display_chain) != NULL)
+ {
+ free ((PTR)d->exp);
+ display_chain = d->next;
+ free ((PTR)d);
+ }
+}
+
+/* Delete the auto-display number NUM. */
+
+static void
+delete_display (num)
+ int num;
+{
+ register struct display *d1, *d;
+
+ if (!display_chain)
+ error ("No display number %d.", num);
+
+ if (display_chain->number == num)
+ {
+ d1 = display_chain;
+ display_chain = d1->next;
+ free_display (d1);
+ }
+ else
+ for (d = display_chain; ; d = d->next)
+ {
+ if (d->next == 0)
+ error ("No display number %d.", num);
+ if (d->next->number == num)
+ {
+ d1 = d->next;
+ d->next = d1->next;
+ free_display (d1);
+ break;
+ }
+ }
+}
+
+/* Delete some values from the auto-display chain.
+ Specify the element numbers. */
+
+static void
+undisplay_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ register char *p = args;
+ register char *p1;
+ register int num;
+
+ if (args == 0)
+ {
+ if (query ("Delete all auto-display expressions? "))
+ clear_displays ();
+ dont_repeat ();
+ return;
+ }
+
+ while (*p)
+ {
+ p1 = p;
+ while (*p1 >= '0' && *p1 <= '9') p1++;
+ if (*p1 && *p1 != ' ' && *p1 != '\t')
+ error ("Arguments must be display numbers.");
+
+ num = atoi (p);
+
+ delete_display (num);
+
+ p = p1;
+ while (*p == ' ' || *p == '\t') p++;
+ }
+ dont_repeat ();
+}
+
+/* Display a single auto-display.
+ Do nothing if the display cannot be printed in the current context,
+ or if the display is disabled. */
+
+static void
+do_one_display (d)
+ struct display *d;
+{
+ int within_current_scope;
+
+ if (d->status == disabled)
+ return;
+
+ if (d->block)
+ within_current_scope = contained_in (get_selected_block (), d->block);
+ else
+ within_current_scope = 1;
+ if (!within_current_scope)
+ return;
+
+ current_display_number = d->number;
+
+ printf_filtered ("%d: ", d->number);
+ if (d->format.size)
+ {
+ CORE_ADDR addr;
+
+ printf_filtered ("x/");
+ if (d->format.count != 1)
+ printf_filtered ("%d", d->format.count);
+ printf_filtered ("%c", d->format.format);
+ if (d->format.format != 'i' && d->format.format != 's')
+ printf_filtered ("%c", d->format.size);
+ printf_filtered (" ");
+ print_expression (d->exp, stdout);
+ if (d->format.count != 1)
+ printf_filtered ("\n");
+ else
+ printf_filtered (" ");
+
+ addr = value_as_pointer (evaluate_expression (d->exp));
+ if (d->format.format == 'i')
+ addr = ADDR_BITS_REMOVE (addr);
+
+ do_examine (d->format, addr);
+ }
+ else
+ {
+ if (d->format.format)
+ printf_filtered ("/%c ", d->format.format);
+ print_expression (d->exp, stdout);
+ printf_filtered (" = ");
+ print_formatted (evaluate_expression (d->exp),
+ d->format.format, d->format.size);
+ printf_filtered ("\n");
+ }
+
+ fflush (stdout);
+ current_display_number = -1;
+}
+
+/* Display all of the values on the auto-display chain which can be
+ evaluated in the current scope. */
+
+void
+do_displays ()
+{
+ register struct display *d;
+
+ for (d = display_chain; d; d = d->next)
+ do_one_display (d);
+}
+
+/* Delete the auto-display which we were in the process of displaying.
+ This is done when there is an error or a signal. */
+
+void
+disable_display (num)
+ int num;
+{
+ register struct display *d;
+
+ for (d = display_chain; d; d = d->next)
+ if (d->number == num)
+ {
+ d->status = disabled;
+ return;
+ }
+ printf ("No display number %d.\n", num);
+}
+
+void
+disable_current_display ()
+{
+ if (current_display_number >= 0)
+ {
+ disable_display (current_display_number);
+ fprintf (stderr, "Disabling display %d to avoid infinite recursion.\n",
+ current_display_number);
+ }
+ current_display_number = -1;
+}
+
+static void
+display_info (ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ register struct display *d;
+
+ if (!display_chain)
+ printf ("There are no auto-display expressions now.\n");
+ else
+ printf_filtered ("Auto-display expressions now in effect:\n\
+Num Enb Expression\n");
+
+ for (d = display_chain; d; d = d->next)
+ {
+ printf_filtered ("%d: %c ", d->number, "ny"[(int)d->status]);
+ if (d->format.size)
+ printf_filtered ("/%d%c%c ", d->format.count, d->format.size,
+ d->format.format);
+ else if (d->format.format)
+ printf_filtered ("/%c ", d->format.format);
+ print_expression (d->exp, stdout);
+ if (d->block && !contained_in (get_selected_block (), d->block))
+ printf_filtered (" (cannot be evaluated in the current context)");
+ printf_filtered ("\n");
+ fflush (stdout);
+ }
+}
+
+static void
+enable_display (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ register char *p = args;
+ register char *p1;
+ register int num;
+ register struct display *d;
+
+ if (p == 0)
+ {
+ for (d = display_chain; d; d = d->next)
+ d->status = enabled;
+ }
+ else
+ while (*p)
+ {
+ p1 = p;
+ while (*p1 >= '0' && *p1 <= '9')
+ p1++;
+ if (*p1 && *p1 != ' ' && *p1 != '\t')
+ error ("Arguments must be display numbers.");
+
+ num = atoi (p);
+
+ for (d = display_chain; d; d = d->next)
+ if (d->number == num)
+ {
+ d->status = enabled;
+ goto win;
+ }
+ printf ("No display number %d.\n", num);
+ win:
+ p = p1;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ }
+}
+
+/* ARGSUSED */
+static void
+disable_display_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ register char *p = args;
+ register char *p1;
+ register struct display *d;
+
+ if (p == 0)
+ {
+ for (d = display_chain; d; d = d->next)
+ d->status = disabled;
+ }
+ else
+ while (*p)
+ {
+ p1 = p;
+ while (*p1 >= '0' && *p1 <= '9')
+ p1++;
+ if (*p1 && *p1 != ' ' && *p1 != '\t')
+ error ("Arguments must be display numbers.");
+
+ disable_display (atoi (p));
+
+ p = p1;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ }
+}
+
+
+/* Print the value in stack frame FRAME of a variable
+ specified by a struct symbol. */
+
+void
+print_variable_value (var, frame, stream)
+ struct symbol *var;
+ FRAME frame;
+ FILE *stream;
+{
+ value val = read_var_value (var, frame);
+ value_print (val, stream, 0, Val_pretty_default);
+}
+
+/* Print the arguments of a stack frame, given the function FUNC
+ running in that frame (as a symbol), the info on the frame,
+ and the number of args according to the stack frame (or -1 if unknown). */
+
+/* References here and elsewhere to "number of args according to the
+ stack frame" appear in all cases to refer to "number of ints of args
+ according to the stack frame". At least for VAX, i386, isi. */
+
+void
+print_frame_args (func, fi, num, stream)
+ struct symbol *func;
+ struct frame_info *fi;
+ int num;
+ FILE *stream;
+{
+ struct block *b = NULL;
+ int nsyms = 0;
+ int first = 1;
+ register int i;
+ register struct symbol *sym;
+ register value val;
+ /* Offset of next stack argument beyond the one we have seen that is
+ at the highest offset.
+ -1 if we haven't come to a stack argument yet. */
+ long highest_offset = -1;
+ int arg_size;
+ /* Number of ints of arguments that we have printed so far. */
+ int args_printed = 0;
+
+ if (func)
+ {
+ b = SYMBOL_BLOCK_VALUE (func);
+ nsyms = BLOCK_NSYMS (b);
+ }
+
+ for (i = 0; i < nsyms; i++)
+ {
+ QUIT;
+ sym = BLOCK_SYM (b, i);
+
+ /* Keep track of the highest stack argument offset seen, and
+ skip over any kinds of symbols we don't care about. */
+
+ switch (SYMBOL_CLASS (sym)) {
+ case LOC_ARG:
+ case LOC_REF_ARG:
+ {
+ long current_offset = SYMBOL_VALUE (sym);
+
+ arg_size = TYPE_LENGTH (SYMBOL_TYPE (sym));
+
+ /* Compute address of next argument by adding the size of
+ this argument and rounding to an int boundary. */
+ current_offset
+ = ((current_offset + arg_size + sizeof (int) - 1)
+ & ~(sizeof (int) - 1));
+
+ /* If this is the highest offset seen yet, set highest_offset. */
+ if (highest_offset == -1
+ || (current_offset > highest_offset))
+ highest_offset = current_offset;
+
+ /* Add the number of ints we're about to print to args_printed. */
+ args_printed += (arg_size + sizeof (int) - 1) / sizeof (int);
+ }
+
+ /* We care about types of symbols, but don't need to keep track of
+ stack offsets in them. */
+ case LOC_REGPARM:
+ case LOC_REGPARM_ADDR:
+ case LOC_LOCAL_ARG:
+ case LOC_BASEREG_ARG:
+ break;
+
+ /* Other types of symbols we just skip over. */
+ default:
+ continue;
+ }
+
+ /* We have to look up the symbol because arguments can have
+ two entries (one a parameter, one a local) and the one we
+ want is the local, which lookup_symbol will find for us.
+ This includes gcc1 (not gcc2) on the sparc when passing a
+ small structure and gcc2 when the argument type is float
+ and it is passed as a double and converted to float by
+ the prologue (in the latter case the type of the LOC_ARG
+ symbol is double and the type of the LOC_LOCAL symbol is
+ float). There are also LOC_ARG/LOC_REGISTER pairs which
+ are not combined in symbol-reading. */
+ /* But if the parameter name is null, don't try it.
+ Null parameter names occur on the RS/6000, for traceback tables.
+ FIXME, should we even print them? */
+
+ if (*SYMBOL_NAME (sym))
+ sym = lookup_symbol
+ (SYMBOL_NAME (sym),
+ b, VAR_NAMESPACE, (int *)NULL, (struct symtab **)NULL);
+
+ /* Print the current arg. */
+ if (! first)
+ fprintf_filtered (stream, ", ");
+ wrap_here (" ");
+ fprintf_symbol_filtered (stream, SYMBOL_SOURCE_NAME (sym),
+ SYMBOL_LANGUAGE (sym), DMGL_PARAMS | DMGL_ANSI);
+ fputs_filtered ("=", stream);
+
+ /* Avoid value_print because it will deref ref parameters. We just
+ want to print their addresses. Print ??? for args whose address
+ we do not know. We pass 2 as "recurse" to val_print because our
+ standard indentation here is 4 spaces, and val_print indents
+ 2 for each recurse. */
+ val = read_var_value (sym, FRAME_INFO_ID (fi));
+ if (val)
+ val_print (VALUE_TYPE (val), VALUE_CONTENTS (val), VALUE_ADDRESS (val),
+ stream, 0, 0, 2, Val_no_prettyprint);
+ else
+ fputs_filtered ("???", stream);
+ first = 0;
+ }
+
+ /* Don't print nameless args in situations where we don't know
+ enough about the stack to find them. */
+ if (num != -1)
+ {
+ long start;
+
+ if (highest_offset == -1)
+ start = FRAME_ARGS_SKIP;
+ else
+ start = highest_offset;
+
+ print_frame_nameless_args (fi, start, num - args_printed,
+ first, stream);
+ }
+}
+
+/* Print nameless args on STREAM.
+ FI is the frameinfo for this frame, START is the offset
+ of the first nameless arg, and NUM is the number of nameless args to
+ print. FIRST is nonzero if this is the first argument (not just
+ the first nameless arg). */
+static void
+print_frame_nameless_args (fi, start, num, first, stream)
+ struct frame_info *fi;
+ long start;
+ int num;
+ int first;
+ FILE *stream;
+{
+ int i;
+ CORE_ADDR argsaddr;
+ long arg_value;
+
+ for (i = 0; i < num; i++)
+ {
+ QUIT;
+#ifdef NAMELESS_ARG_VALUE
+ NAMELESS_ARG_VALUE (fi, start, &arg_value);
+#else
+ argsaddr = FRAME_ARGS_ADDRESS (fi);
+ if (!argsaddr)
+ return;
+
+ arg_value = read_memory_integer (argsaddr + start, sizeof (int));
+#endif
+
+ if (!first)
+ fprintf_filtered (stream, ", ");
+
+#ifdef PRINT_NAMELESS_INTEGER
+ PRINT_NAMELESS_INTEGER (stream, arg_value);
+#else
+#ifdef PRINT_TYPELESS_INTEGER
+ PRINT_TYPELESS_INTEGER (stream, builtin_type_int, (LONGEST) arg_value);
+#else
+ fprintf_filtered (stream, "%d", arg_value);
+#endif /* PRINT_TYPELESS_INTEGER */
+#endif /* PRINT_NAMELESS_INTEGER */
+ first = 0;
+ start += sizeof (int);
+ }
+}
+
+/* ARGSUSED */
+static void
+printf_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ register char *f;
+ register char *s = arg;
+ char *string;
+ value *val_args;
+ char *substrings;
+ char *current_substring;
+ int nargs = 0;
+ int allocated_args = 20;
+ va_list args_to_vprintf;
+ struct cleanup *old_cleanups;
+
+ val_args = (value *) xmalloc (allocated_args * sizeof (value));
+ old_cleanups = make_cleanup (free_current_contents, &val_args);
+
+ if (s == 0)
+ error_no_arg ("format-control string and values to print");
+
+ /* Skip white space before format string */
+ while (*s == ' ' || *s == '\t') s++;
+
+ /* A format string should follow, enveloped in double quotes */
+ if (*s++ != '"')
+ error ("Bad format string, missing '\"'.");
+
+ /* Parse the format-control string and copy it into the string STRING,
+ processing some kinds of escape sequence. */
+
+ f = string = (char *) alloca (strlen (s) + 1);
+
+ while (*s != '"')
+ {
+ int c = *s++;
+ switch (c)
+ {
+ case '\0':
+ error ("Bad format string, non-terminated '\"'.");
+
+ case '\\':
+ switch (c = *s++)
+ {
+ case '\\':
+ *f++ = '\\';
+ break;
+ case 'n':
+ *f++ = '\n';
+ break;
+ case 't':
+ *f++ = '\t';
+ break;
+ case 'r':
+ *f++ = '\r';
+ break;
+ case '"':
+ *f++ = '"';
+ break;
+ default:
+ /* ??? TODO: handle other escape sequences */
+ error ("Unrecognized \\ escape character in format string.");
+ }
+ break;
+
+ default:
+ *f++ = c;
+ }
+ }
+
+ /* Skip over " and following space and comma. */
+ s++;
+ *f++ = '\0';
+ while (*s == ' ' || *s == '\t') s++;
+
+ if (*s != ',' && *s != 0)
+ error ("Invalid argument syntax");
+
+ if (*s == ',') s++;
+ while (*s == ' ' || *s == '\t') s++;
+
+ /* Need extra space for the '\0's. Doubling the size is sufficient. */
+ substrings = alloca (strlen (string) * 2);
+ current_substring = substrings;
+
+ {
+ /* Now scan the string for %-specs and see what kinds of args they want.
+ argclass[I] classifies the %-specs so we can give vprintf something
+ of the right size. */
+
+ enum argclass {no_arg, int_arg, string_arg, double_arg, long_long_arg};
+ enum argclass *argclass;
+ enum argclass this_argclass;
+ char *last_arg;
+ int nargs_wanted;
+ int lcount;
+ int i;
+
+ argclass = (enum argclass *) alloca (strlen (s) * sizeof *argclass);
+ nargs_wanted = 0;
+ f = string;
+ last_arg = string;
+ while (*f)
+ if (*f++ == '%')
+ {
+ lcount = 0;
+ while (strchr ("0123456789.hlL-+ #", *f))
+ {
+ if (*f == 'l' || *f == 'L')
+ lcount++;
+ f++;
+ }
+ switch (*f)
+ {
+ case 's':
+ this_argclass = string_arg;
+ break;
+
+ case 'e':
+ case 'f':
+ case 'g':
+ this_argclass = double_arg;
+ break;
+
+ case '*':
+ error ("`*' not supported for precision or width in printf");
+
+ case 'n':
+ error ("Format specifier `n' not supported in printf");
+
+ case '%':
+ this_argclass = no_arg;
+ break;
+
+ default:
+ if (lcount > 1)
+ this_argclass = long_long_arg;
+ else
+ this_argclass = int_arg;
+ break;
+ }
+ f++;
+ if (this_argclass != no_arg)
+ {
+ strncpy (current_substring, last_arg, f - last_arg);
+ current_substring += f - last_arg;
+ *current_substring++ = '\0';
+ last_arg = f;
+ argclass[nargs_wanted++] = this_argclass;
+ }
+ }
+
+ /* Now, parse all arguments and evaluate them.
+ Store the VALUEs in VAL_ARGS. */
+
+ while (*s != '\0')
+ {
+ char *s1;
+ if (nargs == allocated_args)
+ val_args = (value *) xrealloc ((char *) val_args,
+ (allocated_args *= 2)
+ * sizeof (value));
+ s1 = s;
+ val_args[nargs] = parse_to_comma_and_eval (&s1);
+
+ /* If format string wants a float, unchecked-convert the value to
+ floating point of the same size */
+
+ if (argclass[nargs] == double_arg)
+ {
+ if (TYPE_LENGTH (VALUE_TYPE (val_args[nargs])) == sizeof (float))
+ VALUE_TYPE (val_args[nargs]) = builtin_type_float;
+ if (TYPE_LENGTH (VALUE_TYPE (val_args[nargs])) == sizeof (double))
+ VALUE_TYPE (val_args[nargs]) = builtin_type_double;
+ }
+ nargs++;
+ s = s1;
+ if (*s == ',')
+ s++;
+ }
+
+ if (nargs != nargs_wanted)
+ error ("Wrong number of arguments for specified format-string");
+
+ /* FIXME: We should be using vprintf_filtered, but as long as it
+ has an arbitrary limit that is unacceptable. Correct fix is
+ for vprintf_filtered to scan down the format string so it knows
+ how big a buffer it needs (perhaps by putting a vasprintf (see
+ GNU C library) in libiberty).
+
+ But for now, just force out any pending output, so at least the output
+ appears in the correct order. */
+ wrap_here ((char *)NULL);
+
+ /* Now actually print them. */
+ current_substring = substrings;
+ for (i = 0; i < nargs; i++)
+ {
+ switch (argclass[i])
+ {
+ case string_arg:
+ {
+ char *str;
+ CORE_ADDR tem;
+ int j;
+ tem = value_as_pointer (val_args[i]);
+
+ /* This is a %s argument. Find the length of the string. */
+ for (j = 0; ; j++)
+ {
+ char c;
+ QUIT;
+ read_memory (tem + j, &c, 1);
+ if (c == 0)
+ break;
+ }
+
+ /* Copy the string contents into a string inside GDB. */
+ str = (char *) alloca (j + 1);
+ read_memory (tem, str, j);
+ str[j] = 0;
+
+ /* Don't use printf_filtered because of arbitrary limit. */
+ printf (current_substring, str);
+ }
+ break;
+ case double_arg:
+ {
+ double val = value_as_double (val_args[i]);
+ /* Don't use printf_filtered because of arbitrary limit. */
+ printf (current_substring, val);
+ break;
+ }
+ case long_long_arg:
+#if defined (CC_HAS_LONG_LONG) && defined (PRINTF_HAS_LONG_LONG)
+ {
+ long long val = value_as_long (val_args[i]);
+ /* Don't use printf_filtered because of arbitrary limit. */
+ printf (current_substring, val);
+ break;
+ }
+#else
+ error ("long long not supported in printf");
+#endif
+ case int_arg:
+ {
+ /* FIXME: there should be separate int_arg and long_arg. */
+ long val = value_as_long (val_args[i]);
+ /* Don't use printf_filtered because of arbitrary limit. */
+ printf (current_substring, val);
+ break;
+ }
+ default:
+ error ("internal error in printf_command");
+ }
+ /* Skip to the next substring. */
+ current_substring += strlen (current_substring) + 1;
+ }
+ /* Print the portion of the format string after the last argument. */
+ /* It would be OK to use printf_filtered here. */
+ printf (last_arg);
+ }
+ do_cleanups (old_cleanups);
+}
+
+/* Dump a specified section of assembly code. With no command line
+ arguments, this command will dump the assembly code for the
+ function surrounding the pc value in the selected frame. With one
+ argument, it will dump the assembly code surrounding that pc value.
+ Two arguments are interpeted as bounds within which to dump
+ assembly. */
+
+/* ARGSUSED */
+static void
+disassemble_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ CORE_ADDR low, high;
+ char *name;
+ CORE_ADDR pc;
+ char *space_index;
+
+ name = NULL;
+ if (!arg)
+ {
+ if (!selected_frame)
+ error ("No frame selected.\n");
+
+ pc = get_frame_pc (selected_frame);
+ if (find_pc_partial_function (pc, &name, &low, &high) == 0)
+ error ("No function contains program counter for selected frame.\n");
+ }
+ else if (!(space_index = (char *) strchr (arg, ' ')))
+ {
+ /* One argument. */
+ pc = parse_and_eval_address (arg);
+ if (find_pc_partial_function (pc, &name, &low, &high) == 0)
+ error ("No function contains specified address.\n");
+ }
+ else
+ {
+ /* Two arguments. */
+ *space_index = '\0';
+ low = parse_and_eval_address (arg);
+ high = parse_and_eval_address (space_index + 1);
+ }
+
+ printf_filtered ("Dump of assembler code ");
+ if (name != NULL)
+ {
+ printf_filtered ("for function %s:\n", name);
+ }
+ else
+ {
+ printf_filtered ("from %s ", local_hex_string((unsigned long) low));
+ printf_filtered ("to %s:\n", local_hex_string((unsigned long) high));
+ }
+
+ /* Dump the specified range. */
+ for (pc = low; pc < high; )
+ {
+ QUIT;
+ print_address (pc, stdout);
+ printf_filtered (":\t");
+ pc += print_insn (pc, stdout);
+ printf_filtered ("\n");
+ }
+ printf_filtered ("End of assembler dump.\n");
+ fflush (stdout);
+}
+
+
+void
+_initialize_printcmd ()
+{
+ current_display_number = -1;
+
+ add_info ("address", address_info,
+ "Describe where variable VAR is stored.");
+
+ add_com ("x", class_vars, x_command,
+ "Examine memory: x/FMT ADDRESS.\n\
+ADDRESS is an expression for the memory address to examine.\n\
+FMT is a repeat count followed by a format letter and a size letter.\n\
+Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),\n\
+ t(binary), f(float), a(address), i(instruction), c(char) and s(string).\n\
+Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).\n\
+The specified number of objects of the specified size are printed\n\
+according to the format.\n\n\
+Defaults for format and size letters are those previously used.\n\
+Default count is 1. Default address is following last thing printed\n\
+with this command or \"print\".");
+
+ add_com ("disassemble", class_vars, disassemble_command,
+ "Disassemble a specified section of memory.\n\
+Default is the function surrounding the pc of the selected frame.\n\
+With a single argument, the function surrounding that address is dumped.\n\
+Two arguments are taken as a range of memory to dump.");
+
+#if 0
+ add_com ("whereis", class_vars, whereis_command,
+ "Print line number and file of definition of variable.");
+#endif
+
+ add_info ("display", display_info,
+ "Expressions to display when program stops, with code numbers.");
+
+ add_cmd ("undisplay", class_vars, undisplay_command,
+ "Cancel some expressions to be displayed when program stops.\n\
+Arguments are the code numbers of the expressions to stop displaying.\n\
+No argument means cancel all automatic-display expressions.\n\
+\"delete display\" has the same effect as this command.\n\
+Do \"info display\" to see current list of code numbers.",
+ &cmdlist);
+
+ add_com ("display", class_vars, display_command,
+ "Print value of expression EXP each time the program stops.\n\
+/FMT may be used before EXP as in the \"print\" command.\n\
+/FMT \"i\" or \"s\" or including a size-letter is allowed,\n\
+as in the \"x\" command, and then EXP is used to get the address to examine\n\
+and examining is done as in the \"x\" command.\n\n\
+With no argument, display all currently requested auto-display expressions.\n\
+Use \"undisplay\" to cancel display requests previously made.");
+
+ add_cmd ("display", class_vars, enable_display,
+ "Enable some expressions to be displayed when program stops.\n\
+Arguments are the code numbers of the expressions to resume displaying.\n\
+No argument means enable all automatic-display expressions.\n\
+Do \"info display\" to see current list of code numbers.", &enablelist);
+
+ add_cmd ("display", class_vars, disable_display_command,
+ "Disable some expressions to be displayed when program stops.\n\
+Arguments are the code numbers of the expressions to stop displaying.\n\
+No argument means disable all automatic-display expressions.\n\
+Do \"info display\" to see current list of code numbers.", &disablelist);
+
+ add_cmd ("display", class_vars, undisplay_command,
+ "Cancel some expressions to be displayed when program stops.\n\
+Arguments are the code numbers of the expressions to stop displaying.\n\
+No argument means cancel all automatic-display expressions.\n\
+Do \"info display\" to see current list of code numbers.", &deletelist);
+
+ add_com ("printf", class_vars, printf_command,
+ "printf \"printf format string\", arg1, arg2, arg3, ..., argn\n\
+This is useful for formatted output in user-defined commands.");
+ add_com ("output", class_vars, output_command,
+ "Like \"print\" but don't put in value history and don't print newline.\n\
+This is useful in user-defined commands.");
+
+ add_prefix_cmd ("set", class_vars, set_command,
+"Evaluate expression EXP and assign result to variable VAR, using assignment\n\
+syntax appropriate for the current language (VAR = EXP or VAR := EXP for\n\
+example). VAR may be a debugger \"convenience\" variable (names starting\n\
+with $), a register (a few standard names starting with $), or an actual\n\
+variable in the program being debugged. EXP is any valid expression.\n\
+Use \"set variable\" for variables with names identical to set subcommands.\n\
+\nWith a subcommand, this command modifies parts of the gdb environment.\n\
+You can see these environment settings with the \"show\" command.",
+ &setlist, "set ", 1, &cmdlist);
+
+ /* "call" is the same as "set", but handy for dbx users to call fns. */
+ add_com ("call", class_vars, call_command,
+ "Call a function in the program.\n\
+The argument is the function name and arguments, in the notation of the\n\
+current working language. The result is printed and saved in the value\n\
+history, if it is not void.");
+
+ add_cmd ("variable", class_vars, set_command,
+"Evaluate expression EXP and assign result to variable VAR, using assignment\n\
+syntax appropriate for the current language (VAR = EXP or VAR := EXP for\n\
+example). VAR may be a debugger \"convenience\" variable (names starting\n\
+with $), a register (a few standard names starting with $), or an actual\n\
+variable in the program being debugged. EXP is any valid expression.\n\
+This may usually be abbreviated to simply \"set\".",
+ &setlist);
+
+ add_com ("print", class_vars, print_command,
+ concat ("Print value of expression EXP.\n\
+Variables accessible are those of the lexical environment of the selected\n\
+stack frame, plus all those whose scope is global or an entire file.\n\
+\n\
+$NUM gets previous value number NUM. $ and $$ are the last two values.\n\
+$$NUM refers to NUM'th value back from the last one.\n\
+Names starting with $ refer to registers (with the values they would have\n\
+if the program were to return to the stack frame now selected, restoring\n\
+all registers saved by frames farther in) or else to debugger\n\
+\"convenience\" variables (any such name not a known register).\n\
+Use assignment expressions to give values to convenience variables.\n",
+ "\n\
+{TYPE}ADREXP refers to a datum of data type TYPE, located at address ADREXP.\n\
+@ is a binary operator for treating consecutive data objects\n\
+anywhere in memory as an array. FOO@NUM gives an array whose first\n\
+element is FOO, whose second element is stored in the space following\n\
+where FOO is stored, etc. FOO must be an expression whose value\n\
+resides in memory.\n",
+ "\n\
+EXP may be preceded with /FMT, where FMT is a format letter\n\
+but no count or size letter (see \"x\" command).", NULL));
+ add_com_alias ("p", "print", class_vars, 1);
+
+ add_com ("inspect", class_vars, inspect_command,
+"Same as \"print\" command, except that if you are running in the epoch\n\
+environment, the value is printed in its own window.");
+
+ add_show_from_set (
+ add_set_cmd ("max-symbolic-offset", no_class, var_uinteger,
+ (char *)&max_symbolic_offset,
+ "Set the largest offset that will be printed in <symbol+1234> form.",
+ &setprintlist),
+ &showprintlist);
+ add_show_from_set (
+ add_set_cmd ("symbol-filename", no_class, var_boolean,
+ (char *)&print_symbol_filename,
+ "Set printing of source filename and line number with <symbol>.",
+ &setprintlist),
+ &showprintlist);
+
+ examine_b_type = init_type (TYPE_CODE_INT, 1, 0, NULL, NULL);
+ examine_h_type = init_type (TYPE_CODE_INT, 2, 0, NULL, NULL);
+ examine_w_type = init_type (TYPE_CODE_INT, 4, 0, NULL, NULL);
+ examine_g_type = init_type (TYPE_CODE_INT, 8, 0, NULL, NULL);
+}
diff --git a/gnu/usr.bin/gdb/gdb/putenv.c b/gnu/usr.bin/gdb/gdb/putenv.c
new file mode 100644
index 0000000..e2ea357
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/putenv.c
@@ -0,0 +1,111 @@
+/****************************************************************/
+/* */
+/* putenv(3) */
+/* */
+/* Change or add an environment entry */
+/* */
+/****************************************************************/
+/* origination 1987-Oct-7 T. Holm */
+/****************************************************************/
+
+/*
+Path: hoptoad!pacbell!ames!ll-xn!mit-eddie!uw-beaver!ssc-vax!uvicctr!tholm
+From: tholm@uvicctr.UUCP (Terrence W. Holm)
+Newsgroups: comp.os.minix
+Subject: putenv(3)
+Message-ID: <395@uvicctr.UUCP>
+Date: 5 May 88 06:40:52 GMT
+Organization: University of Victoria, Victoria B.C. Canada
+
+EFTH Minix report #2 - May 1988 - putenv(3)
+
+This is an implementation of putenv(3) that we
+wrote for Minix. Please consider this a public
+domain program.
+*/
+
+#include <stdio.h>
+
+#define PSIZE sizeof(char *)
+
+extern char **environ;
+
+char *strchr();
+char *malloc();
+
+/****************************************************************/
+/* */
+/* int */
+/* putenv( entry ) */
+/* */
+/* The "entry" should follow the form */
+/* "NAME=VALUE". This routine will search the */
+/* user environment for "NAME" and replace its */
+/* value with "VALUE". */
+/* */
+/* Note that "entry" is not copied, it is used */
+/* as the environment entry. This means that it */
+/* must not be unallocated or otherwise modifed */
+/* by the caller, unless it is replaced by a */
+/* subsequent putenv(). */
+/* */
+/* If the name is not found in the environment, */
+/* then a new vector of pointers is allocated, */
+/* "entry" is put at the end and the global */
+/* variable "environ" is updated. */
+/* */
+/* This function normally returns NULL, but -1 */
+/* is returned if it can not allocate enough */
+/* space using malloc(3), or "entry" does not */
+/* contain a '='. */
+/* */
+/****************************************************************/
+
+
+int
+putenv( entry )
+ char *entry;
+{
+ unsigned length;
+ unsigned size;
+ char *temp;
+ char **p;
+ char **new_environ;
+
+ /* Find the length of the "NAME=" */
+
+ temp = strchr(entry,'=');
+ if ( temp == 0 )
+ return( -1 );
+
+ length = (unsigned) (temp - entry + 1);
+
+
+ /* Scan through the environment looking for "NAME=" */
+
+ for ( p=environ; *p != 0 ; p++ )
+ if ( strncmp( entry, *p, length ) == 0 )
+ {
+ *p = entry;
+ return( 0 );
+ }
+
+
+ /* The name was not found, build a bigger environment */
+
+ size = p - environ;
+
+ new_environ = (char **) malloc( (size+2)*PSIZE );
+
+ if ( new_environ == (char **) NULL )
+ return( -1 );
+
+ memcpy ((char *) new_environ, (char *) environ, size*PSIZE );
+
+ new_environ[size] = entry;
+ new_environ[size+1] = NULL;
+
+ environ = new_environ;
+
+ return(0);
+}
diff --git a/gnu/usr.bin/gdb/gdb/regex.c b/gnu/usr.bin/gdb/gdb/regex.c
new file mode 100644
index 0000000..75bf4e9
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/regex.c
@@ -0,0 +1,1725 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 1985, 1989 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* To test, compile with -Dtest.
+ This Dtestable feature turns this into a self-contained program
+ which reads a pattern, describes how it compiles,
+ then reads a string and searches for it. */
+
+#ifdef emacs
+
+/* The `emacs' switch turns on certain special matching commands
+ that make sense only in emacs. */
+
+#include "config.h"
+#include "lisp.h"
+#include "buffer.h"
+#include "syntax.h"
+
+#else /* not emacs */
+
+/* Make alloca work the best possible way. */
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else
+#ifdef sparc
+#include <alloca.h>
+#endif
+#endif
+
+/*
+ * Define the syntax stuff, so we can do the \<...\> things.
+ */
+
+#ifndef Sword /* must be non-zero in some of the tests below... */
+#define Sword 1
+#endif
+
+#define SYNTAX(c) re_syntax_table[c]
+
+#ifdef SYNTAX_TABLE
+
+char *re_syntax_table;
+
+#else
+
+static char re_syntax_table[256];
+
+static void
+init_syntax_once ()
+{
+ register int c;
+ static int done = 0;
+
+ if (done)
+ return;
+
+ memset (re_syntax_table, '\0', sizeof re_syntax_table);
+
+ for (c = 'a'; c <= 'z'; c++)
+ re_syntax_table[c] = Sword;
+
+ for (c = 'A'; c <= 'Z'; c++)
+ re_syntax_table[c] = Sword;
+
+ for (c = '0'; c <= '9'; c++)
+ re_syntax_table[c] = Sword;
+
+ done = 1;
+}
+
+#endif /* SYNTAX_TABLE */
+#endif /* not emacs */
+
+#include "regex.h"
+
+/* Number of failure points to allocate space for initially,
+ when matching. If this number is exceeded, more space is allocated,
+ so it is not a hard limit. */
+
+#ifndef NFAILURES
+#define NFAILURES 80
+#endif /* NFAILURES */
+
+/* width of a byte in bits */
+
+#define BYTEWIDTH 8
+
+#ifndef SIGN_EXTEND_CHAR
+#define SIGN_EXTEND_CHAR(x) (x)
+#endif
+
+static int obscure_syntax = 0;
+
+/* Specify the precise syntax of regexp for compilation.
+ This provides for compatibility for various utilities
+ which historically have different, incompatible syntaxes.
+
+ The argument SYNTAX is a bit-mask containing the two bits
+ RE_NO_BK_PARENS and RE_NO_BK_VBAR. */
+
+int
+re_set_syntax (syntax)
+ int syntax;
+{
+ int ret;
+
+ ret = obscure_syntax;
+ obscure_syntax = syntax;
+ return ret;
+}
+
+/* re_compile_pattern takes a regular-expression string
+ and converts it into a buffer full of byte commands for matching.
+
+ PATTERN is the address of the pattern string
+ SIZE is the length of it.
+ BUFP is a struct re_pattern_buffer * which points to the info
+ on where to store the byte commands.
+ This structure contains a char * which points to the
+ actual space, which should have been obtained with malloc.
+ re_compile_pattern may use realloc to grow the buffer space.
+
+ The number of bytes of commands can be found out by looking in
+ the struct re_pattern_buffer that bufp pointed to,
+ after re_compile_pattern returns.
+*/
+
+#define PATPUSH(ch) (*b++ = (char) (ch))
+
+#define PATFETCH(c) \
+ {if (p == pend) goto end_of_pattern; \
+ c = * (unsigned char *) p++; \
+ if (translate) c = translate[c]; }
+
+#define PATFETCH_RAW(c) \
+ {if (p == pend) goto end_of_pattern; \
+ c = * (unsigned char *) p++; }
+
+#define PATUNFETCH p--
+
+#define EXTEND_BUFFER \
+ { char *old_buffer = bufp->buffer; \
+ if (bufp->allocated == (1<<16)) goto too_big; \
+ bufp->allocated *= 2; \
+ if (bufp->allocated > (1<<16)) bufp->allocated = (1<<16); \
+ if (!(bufp->buffer = (char *) realloc (bufp->buffer, bufp->allocated))) \
+ goto memory_exhausted; \
+ c = bufp->buffer - old_buffer; \
+ b += c; \
+ if (fixup_jump) \
+ fixup_jump += c; \
+ if (laststart) \
+ laststart += c; \
+ begalt += c; \
+ if (pending_exact) \
+ pending_exact += c; \
+ }
+
+static void store_jump (), insert_jump ();
+
+char *
+re_compile_pattern (pattern, size, bufp)
+ char *pattern;
+ int size;
+ struct re_pattern_buffer *bufp;
+{
+ register char *b = bufp->buffer;
+ register char *p = pattern;
+ char *pend = pattern + size;
+ register unsigned c, c1;
+ char *p1;
+ unsigned char *translate = (unsigned char *) bufp->translate;
+
+ /* address of the count-byte of the most recently inserted "exactn" command.
+ This makes it possible to tell whether a new exact-match character
+ can be added to that command or requires a new "exactn" command. */
+
+ char *pending_exact = 0;
+
+ /* address of the place where a forward-jump should go
+ to the end of the containing expression.
+ Each alternative of an "or", except the last, ends with a forward-jump
+ of this sort. */
+
+ char *fixup_jump = 0;
+
+ /* address of start of the most recently finished expression.
+ This tells postfix * where to find the start of its operand. */
+
+ char *laststart = 0;
+
+ /* In processing a repeat, 1 means zero matches is allowed */
+
+ char zero_times_ok;
+
+ /* In processing a repeat, 1 means many matches is allowed */
+
+ char many_times_ok;
+
+ /* address of beginning of regexp, or inside of last \( */
+
+ char *begalt = b;
+
+ /* Stack of information saved by \( and restored by \).
+ Four stack elements are pushed by each \(:
+ First, the value of b.
+ Second, the value of fixup_jump.
+ Third, the value of regnum.
+ Fourth, the value of begalt. */
+
+ int stackb[40];
+ int *stackp = stackb;
+ int *stacke = stackb + 40;
+ int *stackt;
+
+ /* Counts \('s as they are encountered. Remembered for the matching \),
+ where it becomes the "register number" to put in the stop_memory command */
+
+ int regnum = 1;
+
+ bufp->fastmap_accurate = 0;
+
+#ifndef emacs
+#ifndef SYNTAX_TABLE
+ /*
+ * Initialize the syntax table.
+ */
+ init_syntax_once();
+#endif
+#endif
+
+ if (bufp->allocated == 0)
+ {
+ bufp->allocated = 28;
+ if (bufp->buffer)
+ /* EXTEND_BUFFER loses when bufp->allocated is 0 */
+ bufp->buffer = (char *) realloc (bufp->buffer, 28);
+ else
+ /* Caller did not allocate a buffer. Do it for him */
+ bufp->buffer = (char *) malloc (28);
+ if (!bufp->buffer) goto memory_exhausted;
+ begalt = b = bufp->buffer;
+ }
+
+ while (p != pend)
+ {
+ if (b - bufp->buffer > bufp->allocated - 10)
+ /* Note that EXTEND_BUFFER clobbers c */
+ EXTEND_BUFFER;
+
+ PATFETCH (c);
+
+ switch (c)
+ {
+ case '$':
+ if (obscure_syntax & RE_TIGHT_VBAR)
+ {
+ if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS) && p != pend)
+ goto normal_char;
+ /* Make operand of last vbar end before this `$'. */
+ if (fixup_jump)
+ store_jump (fixup_jump, jump, b);
+ fixup_jump = 0;
+ PATPUSH (endline);
+ break;
+ }
+
+ /* $ means succeed if at end of line, but only in special contexts.
+ If randomly in the middle of a pattern, it is a normal character. */
+ if (p == pend || *p == '\n'
+ || (obscure_syntax & RE_CONTEXT_INDEP_OPS)
+ || (obscure_syntax & RE_NO_BK_PARENS
+ ? *p == ')'
+ : *p == '\\' && p[1] == ')')
+ || (obscure_syntax & RE_NO_BK_VBAR
+ ? *p == '|'
+ : *p == '\\' && p[1] == '|'))
+ {
+ PATPUSH (endline);
+ break;
+ }
+ goto normal_char;
+
+ case '^':
+ /* ^ means succeed if at beg of line, but only if no preceding pattern. */
+
+ if (laststart && p[-2] != '\n'
+ && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS))
+ goto normal_char;
+ if (obscure_syntax & RE_TIGHT_VBAR)
+ {
+ if (p != pattern + 1
+ && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS))
+ goto normal_char;
+ PATPUSH (begline);
+ begalt = b;
+ }
+ else
+ PATPUSH (begline);
+ break;
+
+ case '+':
+ case '?':
+ if (obscure_syntax & RE_BK_PLUS_QM)
+ goto normal_char;
+ handle_plus:
+ case '*':
+ /* If there is no previous pattern, char not special. */
+ if (!laststart && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS))
+ goto normal_char;
+ /* If there is a sequence of repetition chars,
+ collapse it down to equivalent to just one. */
+ zero_times_ok = 0;
+ many_times_ok = 0;
+ while (1)
+ {
+ zero_times_ok |= c != '+';
+ many_times_ok |= c != '?';
+ if (p == pend)
+ break;
+ PATFETCH (c);
+ if (c == '*')
+ ;
+ else if (!(obscure_syntax & RE_BK_PLUS_QM)
+ && (c == '+' || c == '?'))
+ ;
+ else if ((obscure_syntax & RE_BK_PLUS_QM)
+ && c == '\\')
+ {
+ int c1;
+ PATFETCH (c1);
+ if (!(c1 == '+' || c1 == '?'))
+ {
+ PATUNFETCH;
+ PATUNFETCH;
+ break;
+ }
+ c = c1;
+ }
+ else
+ {
+ PATUNFETCH;
+ break;
+ }
+ }
+
+ /* Star, etc. applied to an empty pattern is equivalent
+ to an empty pattern. */
+ if (!laststart)
+ break;
+
+ /* Now we know whether 0 matches is allowed,
+ and whether 2 or more matches is allowed. */
+ if (many_times_ok)
+ {
+ /* If more than one repetition is allowed,
+ put in a backward jump at the end. */
+ store_jump (b, maybe_finalize_jump, laststart - 3);
+ b += 3;
+ }
+ insert_jump (on_failure_jump, laststart, b + 3, b);
+ pending_exact = 0;
+ b += 3;
+ if (!zero_times_ok)
+ {
+ /* At least one repetition required: insert before the loop
+ a skip over the initial on-failure-jump instruction */
+ insert_jump (dummy_failure_jump, laststart, laststart + 6, b);
+ b += 3;
+ }
+ break;
+
+ case '.':
+ laststart = b;
+ PATPUSH (anychar);
+ break;
+
+ case '[':
+ while (b - bufp->buffer
+ > bufp->allocated - 3 - (1 << BYTEWIDTH) / BYTEWIDTH)
+ /* Note that EXTEND_BUFFER clobbers c */
+ EXTEND_BUFFER;
+
+ laststart = b;
+ if (*p == '^')
+ PATPUSH (charset_not), p++;
+ else
+ PATPUSH (charset);
+ p1 = p;
+
+ PATPUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
+ /* Clear the whole map */
+ memset (b, '\0', (1 << BYTEWIDTH) / BYTEWIDTH);
+ /* Read in characters and ranges, setting map bits */
+ while (1)
+ {
+ PATFETCH (c);
+ if (c == ']' && p != p1 + 1) break;
+ if (*p == '-' && p[1] != ']')
+ {
+ PATFETCH (c1);
+ PATFETCH (c1);
+ while (c <= c1)
+ b[c / BYTEWIDTH] |= 1 << (c % BYTEWIDTH), c++;
+ }
+ else
+ {
+ b[c / BYTEWIDTH] |= 1 << (c % BYTEWIDTH);
+ }
+ }
+ /* Discard any bitmap bytes that are all 0 at the end of the map.
+ Decrement the map-length byte too. */
+ while ((int) b[-1] > 0 && b[b[-1] - 1] == 0)
+ b[-1]--;
+ b += b[-1];
+ break;
+
+ case '(':
+ if (! (obscure_syntax & RE_NO_BK_PARENS))
+ goto normal_char;
+ else
+ goto handle_open;
+
+ case ')':
+ if (! (obscure_syntax & RE_NO_BK_PARENS))
+ goto normal_char;
+ else
+ goto handle_close;
+
+ case '\n':
+ if (! (obscure_syntax & RE_NEWLINE_OR))
+ goto normal_char;
+ else
+ goto handle_bar;
+
+ case '|':
+ if (! (obscure_syntax & RE_NO_BK_VBAR))
+ goto normal_char;
+ else
+ goto handle_bar;
+
+ case '\\':
+ if (p == pend) goto invalid_pattern;
+ PATFETCH_RAW (c);
+ switch (c)
+ {
+ case '(':
+ if (obscure_syntax & RE_NO_BK_PARENS)
+ goto normal_backsl;
+ handle_open:
+ if (stackp == stacke) goto nesting_too_deep;
+ if (regnum < RE_NREGS)
+ {
+ PATPUSH (start_memory);
+ PATPUSH (regnum);
+ }
+ *stackp++ = b - bufp->buffer;
+ *stackp++ = fixup_jump ? fixup_jump - bufp->buffer + 1 : 0;
+ *stackp++ = regnum++;
+ *stackp++ = begalt - bufp->buffer;
+ fixup_jump = 0;
+ laststart = 0;
+ begalt = b;
+ break;
+
+ case ')':
+ if (obscure_syntax & RE_NO_BK_PARENS)
+ goto normal_backsl;
+ handle_close:
+ if (stackp == stackb) goto unmatched_close;
+ begalt = *--stackp + bufp->buffer;
+ if (fixup_jump)
+ store_jump (fixup_jump, jump, b);
+ if (stackp[-1] < RE_NREGS)
+ {
+ PATPUSH (stop_memory);
+ PATPUSH (stackp[-1]);
+ }
+ stackp -= 2;
+ fixup_jump = 0;
+ if (*stackp)
+ fixup_jump = *stackp + bufp->buffer - 1;
+ laststart = *--stackp + bufp->buffer;
+ break;
+
+ case '|':
+ if (obscure_syntax & RE_NO_BK_VBAR)
+ goto normal_backsl;
+ handle_bar:
+ insert_jump (on_failure_jump, begalt, b + 6, b);
+ pending_exact = 0;
+ b += 3;
+ if (fixup_jump)
+ store_jump (fixup_jump, jump, b);
+ fixup_jump = b;
+ b += 3;
+ laststart = 0;
+ begalt = b;
+ break;
+
+#ifdef emacs
+ case '=':
+ PATPUSH (at_dot);
+ break;
+
+ case 's':
+ laststart = b;
+ PATPUSH (syntaxspec);
+ PATFETCH (c);
+ PATPUSH (syntax_spec_code[c]);
+ break;
+
+ case 'S':
+ laststart = b;
+ PATPUSH (notsyntaxspec);
+ PATFETCH (c);
+ PATPUSH (syntax_spec_code[c]);
+ break;
+#endif /* emacs */
+
+ case 'w':
+ laststart = b;
+ PATPUSH (wordchar);
+ break;
+
+ case 'W':
+ laststart = b;
+ PATPUSH (notwordchar);
+ break;
+
+ case '<':
+ PATPUSH (wordbeg);
+ break;
+
+ case '>':
+ PATPUSH (wordend);
+ break;
+
+ case 'b':
+ PATPUSH (wordbound);
+ break;
+
+ case 'B':
+ PATPUSH (notwordbound);
+ break;
+
+ case '`':
+ PATPUSH (begbuf);
+ break;
+
+ case '\'':
+ PATPUSH (endbuf);
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ c1 = c - '0';
+ if (c1 >= regnum)
+ goto normal_char;
+ for (stackt = stackp - 2; stackt > stackb; stackt -= 4)
+ if (*stackt == c1)
+ goto normal_char;
+ laststart = b;
+ PATPUSH (duplicate);
+ PATPUSH (c1);
+ break;
+
+ case '+':
+ case '?':
+ if (obscure_syntax & RE_BK_PLUS_QM)
+ goto handle_plus;
+
+ default:
+ normal_backsl:
+ /* You might think it would be useful for \ to mean
+ not to translate; but if we don't translate it
+ it will never match anything. */
+ if (translate) c = translate[c];
+ goto normal_char;
+ }
+ break;
+
+ default:
+ normal_char:
+ if (!pending_exact || pending_exact + *pending_exact + 1 != b
+ || *pending_exact == 0177 || *p == '*' || *p == '^'
+ || ((obscure_syntax & RE_BK_PLUS_QM)
+ ? *p == '\\' && (p[1] == '+' || p[1] == '?')
+ : (*p == '+' || *p == '?')))
+ {
+ laststart = b;
+ PATPUSH (exactn);
+ pending_exact = b;
+ PATPUSH (0);
+ }
+ PATPUSH (c);
+ (*pending_exact)++;
+ }
+ }
+
+ if (fixup_jump)
+ store_jump (fixup_jump, jump, b);
+
+ if (stackp != stackb) goto unmatched_open;
+
+ bufp->used = b - bufp->buffer;
+ return 0;
+
+ invalid_pattern:
+ return "Invalid regular expression";
+
+ unmatched_open:
+ return "Unmatched \\(";
+
+ unmatched_close:
+ return "Unmatched \\)";
+
+ end_of_pattern:
+ return "Premature end of regular expression";
+
+ nesting_too_deep:
+ return "Nesting too deep";
+
+ too_big:
+ return "Regular expression too big";
+
+ memory_exhausted:
+ return "Memory exhausted";
+}
+
+/* Store where `from' points a jump operation to jump to where `to' points.
+ `opcode' is the opcode to store. */
+
+static void
+store_jump (from, opcode, to)
+ char *from, *to;
+ char opcode;
+{
+ from[0] = opcode;
+ from[1] = (to - (from + 3)) & 0377;
+ from[2] = (to - (from + 3)) >> 8;
+}
+
+/* Open up space at char FROM, and insert there a jump to TO.
+ CURRENT_END gives te end of the storage no in use,
+ so we know how much data to copy up.
+ OP is the opcode of the jump to insert.
+
+ If you call this function, you must zero out pending_exact. */
+
+static void
+insert_jump (op, from, to, current_end)
+ char op;
+ char *from, *to, *current_end;
+{
+ register char *pto = current_end + 3;
+ register char *pfrom = current_end;
+ while (pfrom != from)
+ *--pto = *--pfrom;
+ store_jump (from, op, to);
+}
+
+/* Given a pattern, compute a fastmap from it.
+ The fastmap records which of the (1 << BYTEWIDTH) possible characters
+ can start a string that matches the pattern.
+ This fastmap is used by re_search to skip quickly over totally implausible text.
+
+ The caller must supply the address of a (1 << BYTEWIDTH)-byte data area
+ as bufp->fastmap.
+ The other components of bufp describe the pattern to be used. */
+
+void
+re_compile_fastmap (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ unsigned char *pattern = (unsigned char *) bufp->buffer;
+ int size = bufp->used;
+ register char *fastmap = bufp->fastmap;
+ register unsigned char *p = pattern;
+ register unsigned char *pend = pattern + size;
+ register int j;
+ unsigned char *translate = (unsigned char *) bufp->translate;
+
+ unsigned char *stackb[NFAILURES];
+ unsigned char **stackp = stackb;
+
+ memset (fastmap, '\0', (1 << BYTEWIDTH));
+ bufp->fastmap_accurate = 1;
+ bufp->can_be_null = 0;
+
+ while (p)
+ {
+ if (p == pend)
+ {
+ bufp->can_be_null = 1;
+ break;
+ }
+#ifdef SWITCH_ENUM_BUG
+ switch ((int) ((enum regexpcode) *p++))
+#else
+ switch ((enum regexpcode) *p++)
+#endif
+ {
+ case exactn:
+ if (translate)
+ fastmap[translate[p[1]]] = 1;
+ else
+ fastmap[p[1]] = 1;
+ break;
+
+ case begline:
+ case before_dot:
+ case at_dot:
+ case after_dot:
+ case begbuf:
+ case endbuf:
+ case wordbound:
+ case notwordbound:
+ case wordbeg:
+ case wordend:
+ continue;
+
+ case endline:
+ if (translate)
+ fastmap[translate['\n']] = 1;
+ else
+ fastmap['\n'] = 1;
+ if (bufp->can_be_null != 1)
+ bufp->can_be_null = 2;
+ break;
+
+ case finalize_jump:
+ case maybe_finalize_jump:
+ case jump:
+ case dummy_failure_jump:
+ bufp->can_be_null = 1;
+ j = *p++ & 0377;
+ j += SIGN_EXTEND_CHAR (*(char *)p) << 8;
+ p += j + 1; /* The 1 compensates for missing ++ above */
+ if (j > 0)
+ continue;
+ /* Jump backward reached implies we just went through
+ the body of a loop and matched nothing.
+ Opcode jumped to should be an on_failure_jump.
+ Just treat it like an ordinary jump.
+ For a * loop, it has pushed its failure point already;
+ if so, discard that as redundant. */
+ if ((enum regexpcode) *p != on_failure_jump)
+ continue;
+ p++;
+ j = *p++ & 0377;
+ j += SIGN_EXTEND_CHAR (*(char *)p) << 8;
+ p += j + 1; /* The 1 compensates for missing ++ above */
+ if (stackp != stackb && *stackp == p)
+ stackp--;
+ continue;
+
+ case on_failure_jump:
+ j = *p++ & 0377;
+ j += SIGN_EXTEND_CHAR (*(char *)p) << 8;
+ p++;
+ *++stackp = p + j;
+ continue;
+
+ case start_memory:
+ case stop_memory:
+ p++;
+ continue;
+
+ case duplicate:
+ bufp->can_be_null = 1;
+ fastmap['\n'] = 1;
+ case anychar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (j != '\n')
+ fastmap[j] = 1;
+ if (bufp->can_be_null)
+ return;
+ /* Don't return; check the alternative paths
+ so we can set can_be_null if appropriate. */
+ break;
+
+ case wordchar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) == Sword)
+ fastmap[j] = 1;
+ break;
+
+ case notwordchar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) != Sword)
+ fastmap[j] = 1;
+ break;
+
+#ifdef emacs
+ case syntaxspec:
+ k = *p++;
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) == (enum syntaxcode) k)
+ fastmap[j] = 1;
+ break;
+
+ case notsyntaxspec:
+ k = *p++;
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) != (enum syntaxcode) k)
+ fastmap[j] = 1;
+ break;
+#endif /* emacs */
+
+ case charset:
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
+ {
+ if (translate)
+ fastmap[translate[j]] = 1;
+ else
+ fastmap[j] = 1;
+ }
+ break;
+
+ case charset_not:
+ /* Chars beyond end of map must be allowed */
+ for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
+ if (translate)
+ fastmap[translate[j]] = 1;
+ else
+ fastmap[j] = 1;
+
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
+ {
+ if (translate)
+ fastmap[translate[j]] = 1;
+ else
+ fastmap[j] = 1;
+ }
+ break;
+ }
+
+ /* Get here means we have successfully found the possible starting characters
+ of one path of the pattern. We need not follow this path any farther.
+ Instead, look at the next alternative remembered in the stack. */
+ if (stackp != stackb)
+ p = *stackp--;
+ else
+ break;
+ }
+}
+
+/* Like re_search_2, below, but only one string is specified. */
+
+int
+re_search (pbufp, string, size, startpos, range, regs)
+ struct re_pattern_buffer *pbufp;
+ char *string;
+ int size, startpos, range;
+ struct re_registers *regs;
+{
+ return re_search_2 (pbufp, 0, 0, string, size, startpos, range, regs, size);
+}
+
+/* Like re_match_2 but tries first a match starting at index STARTPOS,
+ then at STARTPOS + 1, and so on.
+ RANGE is the number of places to try before giving up.
+ If RANGE is negative, the starting positions tried are
+ STARTPOS, STARTPOS - 1, etc.
+ It is up to the caller to make sure that range is not so large
+ as to take the starting position outside of the input strings.
+
+The value returned is the position at which the match was found,
+ or -1 if no match was found,
+ or -2 if error (such as failure stack overflow). */
+
+int
+re_search_2 (pbufp, string1, size1, string2, size2, startpos, range, regs, mstop)
+ struct re_pattern_buffer *pbufp;
+ char *string1, *string2;
+ int size1, size2;
+ int startpos;
+ register int range;
+ struct re_registers *regs;
+ int mstop;
+{
+ register char *fastmap = pbufp->fastmap;
+ register unsigned char *translate = (unsigned char *) pbufp->translate;
+ int total = size1 + size2;
+ int val;
+
+ /* Update the fastmap now if not correct already */
+ if (fastmap && !pbufp->fastmap_accurate)
+ re_compile_fastmap (pbufp);
+
+ /* Don't waste time in a long search for a pattern
+ that says it is anchored. */
+ if (pbufp->used > 0 && (enum regexpcode) pbufp->buffer[0] == begbuf
+ && range > 0)
+ {
+ if (startpos > 0)
+ return -1;
+ else
+ range = 1;
+ }
+
+ while (1)
+ {
+ /* If a fastmap is supplied, skip quickly over characters
+ that cannot possibly be the start of a match.
+ Note, however, that if the pattern can possibly match
+ the null string, we must test it at each starting point
+ so that we take the first null string we get. */
+
+ if (fastmap && startpos < total && pbufp->can_be_null != 1)
+ {
+ if (range > 0)
+ {
+ register int lim = 0;
+ register unsigned char *p;
+ int irange = range;
+ if (startpos < size1 && startpos + range >= size1)
+ lim = range - (size1 - startpos);
+
+ p = ((unsigned char *)
+ &(startpos >= size1 ? string2 - size1 : string1)[startpos]);
+
+ if (translate)
+ {
+ while (range > lim && !fastmap[translate[*p++]])
+ range--;
+ }
+ else
+ {
+ while (range > lim && !fastmap[*p++])
+ range--;
+ }
+ startpos += irange - range;
+ }
+ else
+ {
+ register unsigned char c;
+ if (startpos >= size1)
+ c = string2[startpos - size1];
+ else
+ c = string1[startpos];
+ c &= 0xff;
+ if (translate ? !fastmap[translate[c]] : !fastmap[c])
+ goto advance;
+ }
+ }
+
+ if (range >= 0 && startpos == total
+ && fastmap && pbufp->can_be_null == 0)
+ return -1;
+
+ val = re_match_2 (pbufp, string1, size1, string2, size2, startpos, regs, mstop);
+ if (0 <= val)
+ {
+ if (val == -2)
+ return -2;
+ return startpos;
+ }
+
+#ifdef C_ALLOCA
+ alloca (0);
+#endif /* C_ALLOCA */
+
+ advance:
+ if (!range) break;
+ if (range > 0) range--, startpos++; else range++, startpos--;
+ }
+ return -1;
+}
+
+#ifndef emacs /* emacs never uses this */
+int
+re_match (pbufp, string, size, pos, regs)
+ struct re_pattern_buffer *pbufp;
+ char *string;
+ int size, pos;
+ struct re_registers *regs;
+{
+ return re_match_2 (pbufp, 0, 0, string, size, pos, regs, size);
+}
+#endif /* emacs */
+
+/* Maximum size of failure stack. Beyond this, overflow is an error. */
+
+int re_max_failures = 2000;
+
+static int memcmp_translate();
+/* Match the pattern described by PBUFP
+ against data which is the virtual concatenation of STRING1 and STRING2.
+ SIZE1 and SIZE2 are the sizes of the two data strings.
+ Start the match at position POS.
+ Do not consider matching past the position MSTOP.
+
+ If pbufp->fastmap is nonzero, then it had better be up to date.
+
+ The reason that the data to match are specified as two components
+ which are to be regarded as concatenated
+ is so this function can be used directly on the contents of an Emacs buffer.
+
+ -1 is returned if there is no match. -2 is returned if there is
+ an error (such as match stack overflow). Otherwise the value is the length
+ of the substring which was matched. */
+
+int
+re_match_2 (pbufp, string1, size1, string2, size2, pos, regs, mstop)
+ struct re_pattern_buffer *pbufp;
+ unsigned char *string1, *string2;
+ int size1, size2;
+ int pos;
+ struct re_registers *regs;
+ int mstop;
+{
+ register unsigned char *p = (unsigned char *) pbufp->buffer;
+ register unsigned char *pend = p + pbufp->used;
+ /* End of first string */
+ unsigned char *end1;
+ /* End of second string */
+ unsigned char *end2;
+ /* Pointer just past last char to consider matching */
+ unsigned char *end_match_1, *end_match_2;
+ register unsigned char *d, *dend;
+ register int mcnt;
+ unsigned char *translate = (unsigned char *) pbufp->translate;
+
+ /* Failure point stack. Each place that can handle a failure further down the line
+ pushes a failure point on this stack. It consists of two char *'s.
+ The first one pushed is where to resume scanning the pattern;
+ the second pushed is where to resume scanning the strings.
+ If the latter is zero, the failure point is a "dummy".
+ If a failure happens and the innermost failure point is dormant,
+ it discards that failure point and tries the next one. */
+
+ unsigned char *initial_stack[2 * NFAILURES];
+ unsigned char **stackb = initial_stack;
+ unsigned char **stackp = stackb, **stacke = &stackb[2 * NFAILURES];
+
+ /* Information on the "contents" of registers.
+ These are pointers into the input strings; they record
+ just what was matched (on this attempt) by some part of the pattern.
+ The start_memory command stores the start of a register's contents
+ and the stop_memory command stores the end.
+
+ At that point, regstart[regnum] points to the first character in the register,
+ regend[regnum] points to the first character beyond the end of the register,
+ regstart_seg1[regnum] is true iff regstart[regnum] points into string1,
+ and regend_seg1[regnum] is true iff regend[regnum] points into string1. */
+
+ unsigned char *regstart[RE_NREGS];
+ unsigned char *regend[RE_NREGS];
+ unsigned char regstart_seg1[RE_NREGS], regend_seg1[RE_NREGS];
+
+ /* Set up pointers to ends of strings.
+ Don't allow the second string to be empty unless both are empty. */
+ if (!size2)
+ {
+ string2 = string1;
+ size2 = size1;
+ string1 = 0;
+ size1 = 0;
+ }
+ end1 = string1 + size1;
+ end2 = string2 + size2;
+
+ /* Compute where to stop matching, within the two strings */
+ if (mstop <= size1)
+ {
+ end_match_1 = string1 + mstop;
+ end_match_2 = string2;
+ }
+ else
+ {
+ end_match_1 = end1;
+ end_match_2 = string2 + mstop - size1;
+ }
+
+ /* Initialize \) text positions to -1
+ to mark ones that no \( or \) has been seen for. */
+
+ for (mcnt = 0; mcnt < sizeof (regend) / sizeof (*regend); mcnt++)
+ regend[mcnt] = (unsigned char *) -1;
+
+ /* `p' scans through the pattern as `d' scans through the data.
+ `dend' is the end of the input string that `d' points within.
+ `d' is advanced into the following input string whenever necessary,
+ but this happens before fetching;
+ therefore, at the beginning of the loop,
+ `d' can be pointing at the end of a string,
+ but it cannot equal string2. */
+
+ if (pos <= size1)
+ d = string1 + pos, dend = end_match_1;
+ else
+ d = string2 + pos - size1, dend = end_match_2;
+
+/* Write PREFETCH; just before fetching a character with *d. */
+#define PREFETCH \
+ while (d == dend) \
+ { if (dend == end_match_2) goto fail; /* end of string2 => failure */ \
+ d = string2; /* end of string1 => advance to string2. */ \
+ dend = end_match_2; }
+
+ /* This loop loops over pattern commands.
+ It exits by returning from the function if match is complete,
+ or it drops through if match fails at this starting point in the input data. */
+
+ while (1)
+ {
+ if (p == pend)
+ /* End of pattern means we have succeeded! */
+ {
+ /* If caller wants register contents data back, convert it to indices */
+ if (regs)
+ {
+ regs->start[0] = pos;
+ if (dend == end_match_1)
+ regs->end[0] = d - string1;
+ else
+ regs->end[0] = d - string2 + size1;
+ for (mcnt = 1; mcnt < RE_NREGS; mcnt++)
+ {
+ if (regend[mcnt] == (unsigned char *) -1)
+ {
+ regs->start[mcnt] = -1;
+ regs->end[mcnt] = -1;
+ continue;
+ }
+ if (regstart_seg1[mcnt])
+ regs->start[mcnt] = regstart[mcnt] - string1;
+ else
+ regs->start[mcnt] = regstart[mcnt] - string2 + size1;
+ if (regend_seg1[mcnt])
+ regs->end[mcnt] = regend[mcnt] - string1;
+ else
+ regs->end[mcnt] = regend[mcnt] - string2 + size1;
+ }
+ }
+ if (dend == end_match_1)
+ return (d - string1 - pos);
+ else
+ return d - string2 + size1 - pos;
+ }
+
+ /* Otherwise match next pattern command */
+#ifdef SWITCH_ENUM_BUG
+ switch ((int) ((enum regexpcode) *p++))
+#else
+ switch ((enum regexpcode) *p++)
+#endif
+ {
+
+ /* \( is represented by a start_memory, \) by a stop_memory.
+ Both of those commands contain a "register number" argument.
+ The text matched within the \( and \) is recorded under that number.
+ Then, \<digit> turns into a `duplicate' command which
+ is followed by the numeric value of <digit> as the register number. */
+
+ case start_memory:
+ regstart[*p] = d;
+ regstart_seg1[*p++] = (dend == end_match_1);
+ break;
+
+ case stop_memory:
+ regend[*p] = d;
+ regend_seg1[*p++] = (dend == end_match_1);
+ break;
+
+ case duplicate:
+ {
+ int regno = *p++; /* Get which register to match against */
+ register unsigned char *d2, *dend2;
+
+ d2 = regstart[regno];
+ dend2 = ((regstart_seg1[regno] == regend_seg1[regno])
+ ? regend[regno] : end_match_1);
+ while (1)
+ {
+ /* Advance to next segment in register contents, if necessary */
+ while (d2 == dend2)
+ {
+ if (dend2 == end_match_2) break;
+ if (dend2 == regend[regno]) break;
+ d2 = string2, dend2 = regend[regno]; /* end of string1 => advance to string2. */
+ }
+ /* At end of register contents => success */
+ if (d2 == dend2) break;
+
+ /* Advance to next segment in data being matched, if necessary */
+ PREFETCH;
+
+ /* mcnt gets # consecutive chars to compare */
+ mcnt = dend - d;
+ if (mcnt > dend2 - d2)
+ mcnt = dend2 - d2;
+ /* Compare that many; failure if mismatch, else skip them. */
+ if (translate ? memcmp_translate (d, d2, mcnt, translate) : memcmp (d, d2, mcnt))
+ goto fail;
+ d += mcnt, d2 += mcnt;
+ }
+ }
+ break;
+
+ case anychar:
+ /* fetch a data character */
+ PREFETCH;
+ /* Match anything but a newline. */
+ if ((translate ? translate[*d++] : *d++) == '\n')
+ goto fail;
+ break;
+
+ case charset:
+ case charset_not:
+ {
+ /* Nonzero for charset_not */
+ int not = 0;
+ register int c;
+ if (*(p - 1) == (unsigned char) charset_not)
+ not = 1;
+
+ /* fetch a data character */
+ PREFETCH;
+
+ if (translate)
+ c = translate [*d];
+ else
+ c = *d;
+
+ if (c < *p * BYTEWIDTH
+ && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+ not = !not;
+
+ p += 1 + *p;
+
+ if (!not) goto fail;
+ d++;
+ break;
+ }
+
+ case begline:
+ if (d == string1 || d[-1] == '\n')
+ break;
+ goto fail;
+
+ case endline:
+ if (d == end2
+ || (d == end1 ? (size2 == 0 || *string2 == '\n') : *d == '\n'))
+ break;
+ goto fail;
+
+ /* "or" constructs ("|") are handled by starting each alternative
+ with an on_failure_jump that points to the start of the next alternative.
+ Each alternative except the last ends with a jump to the joining point.
+ (Actually, each jump except for the last one really jumps
+ to the following jump, because tensioning the jumps is a hassle.) */
+
+ /* The start of a stupid repeat has an on_failure_jump that points
+ past the end of the repeat text.
+ This makes a failure point so that, on failure to match a repetition,
+ matching restarts past as many repetitions have been found
+ with no way to fail and look for another one. */
+
+ /* A smart repeat is similar but loops back to the on_failure_jump
+ so that each repetition makes another failure point. */
+
+ case on_failure_jump:
+ if (stackp == stacke)
+ {
+ unsigned char **stackx;
+ if (stacke - stackb > re_max_failures * 2)
+ return -2;
+ stackx = (unsigned char **) alloca (2 * (stacke - stackb)
+ * sizeof (char *));
+ memcpy (stackx, stackb, (stacke - stackb) * sizeof (char *));
+ stackp = stackx + (stackp - stackb);
+ stacke = stackx + 2 * (stacke - stackb);
+ stackb = stackx;
+ }
+ mcnt = *p++ & 0377;
+ mcnt += SIGN_EXTEND_CHAR (*(char *)p) << 8;
+ p++;
+ *stackp++ = mcnt + p;
+ *stackp++ = d;
+ break;
+
+ /* The end of a smart repeat has an maybe_finalize_jump back.
+ Change it either to a finalize_jump or an ordinary jump. */
+
+ case maybe_finalize_jump:
+ mcnt = *p++ & 0377;
+ mcnt += SIGN_EXTEND_CHAR (*(char *)p) << 8;
+ p++;
+ {
+ register unsigned char *p2 = p;
+ /* Compare what follows with the begining of the repeat.
+ If we can establish that there is nothing that they would
+ both match, we can change to finalize_jump */
+ while (p2 != pend
+ && (*p2 == (unsigned char) stop_memory
+ || *p2 == (unsigned char) start_memory))
+ p2++;
+ if (p2 == pend)
+ p[-3] = (unsigned char) finalize_jump;
+ else if (*p2 == (unsigned char) exactn
+ || *p2 == (unsigned char) endline)
+ {
+ register int c = *p2 == (unsigned char) endline ? '\n' : p2[2];
+ register unsigned char *p1 = p + mcnt;
+ /* p1[0] ... p1[2] are an on_failure_jump.
+ Examine what follows that */
+ if (p1[3] == (unsigned char) exactn && p1[5] != c)
+ p[-3] = (unsigned char) finalize_jump;
+ else if (p1[3] == (unsigned char) charset
+ || p1[3] == (unsigned char) charset_not)
+ {
+ int not = p1[3] == (unsigned char) charset_not;
+ if (c < p1[4] * BYTEWIDTH
+ && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+ not = !not;
+ /* not is 1 if c would match */
+ /* That means it is not safe to finalize */
+ if (!not)
+ p[-3] = (unsigned char) finalize_jump;
+ }
+ }
+ }
+ p -= 2;
+ if (p[-1] != (unsigned char) finalize_jump)
+ {
+ p[-1] = (unsigned char) jump;
+ goto nofinalize;
+ }
+
+ /* The end of a stupid repeat has a finalize-jump
+ back to the start, where another failure point will be made
+ which will point after all the repetitions found so far. */
+
+ case finalize_jump:
+ stackp -= 2;
+
+ case jump:
+ nofinalize:
+ mcnt = *p++ & 0377;
+ mcnt += SIGN_EXTEND_CHAR (*(char *)p) << 8;
+ p += mcnt + 1; /* The 1 compensates for missing ++ above */
+ break;
+
+ case dummy_failure_jump:
+ if (stackp == stacke)
+ {
+ unsigned char **stackx
+ = (unsigned char **) alloca (2 * (stacke - stackb)
+ * sizeof (char *));
+ memcpy (stackx, stackb, (stacke - stackb) * sizeof (char *));
+ stackp = stackx + (stackp - stackb);
+ stacke = stackx + 2 * (stacke - stackb);
+ stackb = stackx;
+ }
+ *stackp++ = 0;
+ *stackp++ = 0;
+ goto nofinalize;
+
+ case wordbound:
+ if (d == string1 /* Points to first char */
+ || d == end2 /* Points to end */
+ || (d == end1 && size2 == 0)) /* Points to end */
+ break;
+ if ((SYNTAX (d[-1]) == Sword)
+ != (SYNTAX (d == end1 ? *string2 : *d) == Sword))
+ break;
+ goto fail;
+
+ case notwordbound:
+ if (d == string1 /* Points to first char */
+ || d == end2 /* Points to end */
+ || (d == end1 && size2 == 0)) /* Points to end */
+ goto fail;
+ if ((SYNTAX (d[-1]) == Sword)
+ != (SYNTAX (d == end1 ? *string2 : *d) == Sword))
+ goto fail;
+ break;
+
+ case wordbeg:
+ if (d == end2 /* Points to end */
+ || (d == end1 && size2 == 0) /* Points to end */
+ || SYNTAX (* (d == end1 ? string2 : d)) != Sword) /* Next char not a letter */
+ goto fail;
+ if (d == string1 /* Points to first char */
+ || SYNTAX (d[-1]) != Sword) /* prev char not letter */
+ break;
+ goto fail;
+
+ case wordend:
+ if (d == string1 /* Points to first char */
+ || SYNTAX (d[-1]) != Sword) /* prev char not letter */
+ goto fail;
+ if (d == end2 /* Points to end */
+ || (d == end1 && size2 == 0) /* Points to end */
+ || SYNTAX (d == end1 ? *string2 : *d) != Sword) /* Next char not a letter */
+ break;
+ goto fail;
+
+#ifdef emacs
+ case before_dot:
+ if (((d - string2 <= (unsigned) size2)
+ ? d - bf_p2 : d - bf_p1)
+ <= point)
+ goto fail;
+ break;
+
+ case at_dot:
+ if (((d - string2 <= (unsigned) size2)
+ ? d - bf_p2 : d - bf_p1)
+ == point)
+ goto fail;
+ break;
+
+ case after_dot:
+ if (((d - string2 <= (unsigned) size2)
+ ? d - bf_p2 : d - bf_p1)
+ >= point)
+ goto fail;
+ break;
+
+ case wordchar:
+ mcnt = (int) Sword;
+ goto matchsyntax;
+
+ case syntaxspec:
+ mcnt = *p++;
+ matchsyntax:
+ PREFETCH;
+ if (SYNTAX (*d++) != (enum syntaxcode) mcnt) goto fail;
+ break;
+
+ case notwordchar:
+ mcnt = (int) Sword;
+ goto matchnotsyntax;
+
+ case notsyntaxspec:
+ mcnt = *p++;
+ matchnotsyntax:
+ PREFETCH;
+ if (SYNTAX (*d++) == (enum syntaxcode) mcnt) goto fail;
+ break;
+#else
+ case wordchar:
+ PREFETCH;
+ if (SYNTAX (*d++) == 0) goto fail;
+ break;
+
+ case notwordchar:
+ PREFETCH;
+ if (SYNTAX (*d++) != 0) goto fail;
+ break;
+#endif /* not emacs */
+
+ case begbuf:
+ if (d == string1) /* Note, d cannot equal string2 */
+ break; /* unless string1 == string2. */
+ goto fail;
+
+ case endbuf:
+ if (d == end2 || (d == end1 && size2 == 0))
+ break;
+ goto fail;
+
+ case exactn:
+ /* Match the next few pattern characters exactly.
+ mcnt is how many characters to match. */
+ mcnt = *p++;
+ if (translate)
+ {
+ do
+ {
+ PREFETCH;
+ if (translate[*d++] != *p++) goto fail;
+ }
+ while (--mcnt);
+ }
+ else
+ {
+ do
+ {
+ PREFETCH;
+ if (*d++ != *p++) goto fail;
+ }
+ while (--mcnt);
+ }
+ break;
+ }
+ continue; /* Successfully matched one pattern command; keep matching */
+
+ /* Jump here if any matching operation fails. */
+ fail:
+ if (stackp != stackb)
+ /* A restart point is known. Restart there and pop it. */
+ {
+ if (!stackp[-2])
+ { /* If innermost failure point is dormant, flush it and keep looking */
+ stackp -= 2;
+ goto fail;
+ }
+ d = *--stackp;
+ p = *--stackp;
+ if (d >= string1 && d <= end1)
+ dend = end_match_1;
+ }
+ else break; /* Matching at this starting point really fails! */
+ }
+ return -1; /* Failure to match */
+}
+
+static int
+memcmp_translate (s1, s2, len, translate)
+ unsigned char *s1, *s2;
+ register int len;
+ unsigned char *translate;
+{
+ register unsigned char *p1 = s1, *p2 = s2;
+ while (len)
+ {
+ if (translate [*p1++] != translate [*p2++]) return 1;
+ len--;
+ }
+ return 0;
+}
+
+/* Entry points compatible with bsd4.2 regex library */
+
+#ifndef emacs
+
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+re_comp (s)
+ char *s;
+{
+ if (!s)
+ {
+ if (!re_comp_buf.buffer)
+ return "No previous regular expression";
+ return 0;
+ }
+
+ if (!re_comp_buf.buffer)
+ {
+ if (!(re_comp_buf.buffer = (char *) malloc (200)))
+ return "Memory exhausted";
+ re_comp_buf.allocated = 200;
+ if (!(re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH)))
+ return "Memory exhausted";
+ }
+ return re_compile_pattern (s, strlen (s), &re_comp_buf);
+}
+
+int
+re_exec (s)
+ char *s;
+{
+ int len = strlen (s);
+ return 0 <= re_search (&re_comp_buf, s, len, 0, len, 0);
+}
+
+#endif /* emacs */
+
+#ifdef test
+
+#include <stdio.h>
+
+/* Indexed by a character, gives the upper case equivalent of the character */
+
+static char upcase[0400] =
+ { 000, 001, 002, 003, 004, 005, 006, 007,
+ 010, 011, 012, 013, 014, 015, 016, 017,
+ 020, 021, 022, 023, 024, 025, 026, 027,
+ 030, 031, 032, 033, 034, 035, 036, 037,
+ 040, 041, 042, 043, 044, 045, 046, 047,
+ 050, 051, 052, 053, 054, 055, 056, 057,
+ 060, 061, 062, 063, 064, 065, 066, 067,
+ 070, 071, 072, 073, 074, 075, 076, 077,
+ 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
+ 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
+ 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
+ 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137,
+ 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
+ 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
+ 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
+ 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177,
+ 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
+ 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
+ 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
+ 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
+ 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
+ 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
+ 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
+ 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
+ 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
+ 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
+ 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
+ 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
+ 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
+ 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
+ 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
+ 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
+ };
+
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ char pat[80];
+ struct re_pattern_buffer buf;
+ int i;
+ char c;
+ char fastmap[(1 << BYTEWIDTH)];
+
+ /* Allow a command argument to specify the style of syntax. */
+ if (argc > 1)
+ obscure_syntax = atoi (argv[1]);
+
+ buf.allocated = 40;
+ buf.buffer = (char *) malloc (buf.allocated);
+ buf.fastmap = fastmap;
+ buf.translate = upcase;
+
+ while (1)
+ {
+ gets (pat);
+
+ if (*pat)
+ {
+ re_compile_pattern (pat, strlen(pat), &buf);
+
+ for (i = 0; i < buf.used; i++)
+ printchar (buf.buffer[i]);
+
+ putchar ('\n');
+
+ printf ("%d allocated, %d used.\n", buf.allocated, buf.used);
+
+ re_compile_fastmap (&buf);
+ printf ("Allowed by fastmap: ");
+ for (i = 0; i < (1 << BYTEWIDTH); i++)
+ if (fastmap[i]) printchar (i);
+ putchar ('\n');
+ }
+
+ gets (pat); /* Now read the string to match against */
+
+ i = re_match (&buf, pat, strlen (pat), 0, 0);
+ printf ("Match value %d.\n", i);
+ }
+}
+
+#ifdef NOTDEF
+print_buf (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ int i;
+
+ printf ("buf is :\n----------------\n");
+ for (i = 0; i < bufp->used; i++)
+ printchar (bufp->buffer[i]);
+
+ printf ("\n%d allocated, %d used.\n", bufp->allocated, bufp->used);
+
+ printf ("Allowed by fastmap: ");
+ for (i = 0; i < (1 << BYTEWIDTH); i++)
+ if (bufp->fastmap[i])
+ printchar (i);
+ printf ("\nAllowed by translate: ");
+ if (bufp->translate)
+ for (i = 0; i < (1 << BYTEWIDTH); i++)
+ if (bufp->translate[i])
+ printchar (i);
+ printf ("\nfastmap is%s accurate\n", bufp->fastmap_accurate ? "" : "n't");
+ printf ("can %s be null\n----------", bufp->can_be_null ? "" : "not");
+}
+#endif
+
+printchar (c)
+ char c;
+{
+ if (c < 041 || c >= 0177)
+ {
+ putchar ('\\');
+ putchar (((c >> 6) & 3) + '0');
+ putchar (((c >> 3) & 7) + '0');
+ putchar ((c & 7) + '0');
+ }
+ else
+ putchar (c);
+}
+
+error (string)
+ char *string;
+{
+ puts (string);
+ exit (1);
+}
+
+#endif /* test */
diff --git a/gnu/usr.bin/gdb/gdb/regex.h b/gnu/usr.bin/gdb/gdb/regex.h
new file mode 100644
index 0000000..6348c3e
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/regex.h
@@ -0,0 +1,179 @@
+/* Definitions for data structures callers pass the regex library.
+ Copyright (C) 1985, 1989 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Define number of parens for which we record the beginnings and ends.
+ This affects how much space the `struct re_registers' type takes up. */
+#ifndef RE_NREGS
+#define RE_NREGS 10
+#endif
+
+/* These bits are used in the obscure_syntax variable to choose among
+ alternative regexp syntaxes. */
+
+/* 1 means plain parentheses serve as grouping, and backslash
+ parentheses are needed for literal searching.
+ 0 means backslash-parentheses are grouping, and plain parentheses
+ are for literal searching. */
+#define RE_NO_BK_PARENS 1
+
+/* 1 means plain | serves as the "or"-operator, and \| is a literal.
+ 0 means \| serves as the "or"-operator, and | is a literal. */
+#define RE_NO_BK_VBAR 2
+
+/* 0 means plain + or ? serves as an operator, and \+, \? are literals.
+ 1 means \+, \? are operators and plain +, ? are literals. */
+#define RE_BK_PLUS_QM 4
+
+/* 1 means | binds tighter than ^ or $.
+ 0 means the contrary. */
+#define RE_TIGHT_VBAR 8
+
+/* 1 means treat \n as an _OR operator
+ 0 means treat it as a normal character */
+#define RE_NEWLINE_OR 16
+
+/* 0 means that a special characters (such as *, ^, and $) always have
+ their special meaning regardless of the surrounding context.
+ 1 means that special characters may act as normal characters in some
+ contexts. Specifically, this applies to:
+ ^ - only special at the beginning, or after ( or |
+ $ - only special at the end, or before ) or |
+ *, +, ? - only special when not after the beginning, (, or | */
+#define RE_CONTEXT_INDEP_OPS 32
+
+/* Now define combinations of bits for the standard possibilities. */
+#define RE_SYNTAX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_CONTEXT_INDEP_OPS)
+#define RE_SYNTAX_EGREP (RE_SYNTAX_AWK | RE_NEWLINE_OR)
+#define RE_SYNTAX_GREP (RE_BK_PLUS_QM | RE_NEWLINE_OR)
+#define RE_SYNTAX_EMACS 0
+
+/* This data structure is used to represent a compiled pattern. */
+
+struct re_pattern_buffer
+ {
+ char *buffer; /* Space holding the compiled pattern commands. */
+ int allocated; /* Size of space that buffer points to */
+ int used; /* Length of portion of buffer actually occupied */
+ char *fastmap; /* Pointer to fastmap, if any, or zero if none. */
+ /* re_search uses the fastmap, if there is one,
+ to skip quickly over totally implausible characters */
+ char *translate; /* Translate table to apply to all characters before comparing.
+ Or zero for no translation.
+ The translation is applied to a pattern when it is compiled
+ and to data when it is matched. */
+ char fastmap_accurate;
+ /* Set to zero when a new pattern is stored,
+ set to one when the fastmap is updated from it. */
+ char can_be_null; /* Set to one by compiling fastmap
+ if this pattern might match the null string.
+ It does not necessarily match the null string
+ in that case, but if this is zero, it cannot.
+ 2 as value means can match null string
+ but at end of range or before a character
+ listed in the fastmap. */
+ };
+
+/* Structure to store "register" contents data in.
+
+ Pass the address of such a structure as an argument to re_match, etc.,
+ if you want this information back.
+
+ start[i] and end[i] record the string matched by \( ... \) grouping i,
+ for i from 1 to RE_NREGS - 1.
+ start[0] and end[0] record the entire string matched. */
+
+struct re_registers
+ {
+ int start[RE_NREGS];
+ int end[RE_NREGS];
+ };
+
+/* These are the command codes that appear in compiled regular expressions, one per byte.
+ Some command codes are followed by argument bytes.
+ A command code can specify any interpretation whatever for its arguments.
+ Zero-bytes may appear in the compiled regular expression. */
+
+enum regexpcode
+ {
+ unused,
+ exactn, /* followed by one byte giving n, and then by n literal bytes */
+ begline, /* fails unless at beginning of line */
+ endline, /* fails unless at end of line */
+ jump, /* followed by two bytes giving relative address to jump to */
+ on_failure_jump, /* followed by two bytes giving relative address of place
+ to resume at in case of failure. */
+ finalize_jump, /* Throw away latest failure point and then jump to address. */
+ maybe_finalize_jump, /* Like jump but finalize if safe to do so.
+ This is used to jump back to the beginning
+ of a repeat. If the command that follows
+ this jump is clearly incompatible with the
+ one at the beginning of the repeat, such that
+ we can be sure that there is no use backtracking
+ out of repetitions already completed,
+ then we finalize. */
+ dummy_failure_jump, /* jump, and push a dummy failure point.
+ This failure point will be thrown away
+ if an attempt is made to use it for a failure.
+ A + construct makes this before the first repeat. */
+ anychar, /* matches any one character */
+ charset, /* matches any one char belonging to specified set.
+ First following byte is # bitmap bytes.
+ Then come bytes for a bit-map saying which chars are in.
+ Bits in each byte are ordered low-bit-first.
+ A character is in the set if its bit is 1.
+ A character too large to have a bit in the map
+ is automatically not in the set */
+ charset_not, /* similar but match any character that is NOT one of those specified */
+ start_memory, /* starts remembering the text that is matched
+ and stores it in a memory register.
+ followed by one byte containing the register number.
+ Register numbers must be in the range 0 through NREGS. */
+ stop_memory, /* stops remembering the text that is matched
+ and stores it in a memory register.
+ followed by one byte containing the register number.
+ Register numbers must be in the range 0 through NREGS. */
+ duplicate, /* match a duplicate of something remembered.
+ Followed by one byte containing the index of the memory register. */
+ before_dot, /* Succeeds if before dot */
+ at_dot, /* Succeeds if at dot */
+ after_dot, /* Succeeds if after dot */
+ begbuf, /* Succeeds if at beginning of buffer */
+ endbuf, /* Succeeds if at end of buffer */
+ wordchar, /* Matches any word-constituent character */
+ notwordchar, /* Matches any char that is not a word-constituent */
+ wordbeg, /* Succeeds if at word beginning */
+ wordend, /* Succeeds if at word end */
+ wordbound, /* Succeeds if at a word boundary */
+ notwordbound, /* Succeeds if not at a word boundary */
+ syntaxspec, /* Matches any character whose syntax is specified.
+ followed by a byte which contains a syntax code, Sword or such like */
+ notsyntaxspec /* Matches any character whose syntax differs from the specified. */
+ };
+
+extern char *re_compile_pattern ();
+/* Is this really advertised? */
+extern void re_compile_fastmap ();
+extern int re_search (), re_search_2 ();
+extern int re_match (), re_match_2 ();
+
+/* 4.2 bsd compatibility (yuck) */
+extern char *re_comp ();
+extern int re_exec ();
+
+#ifdef SYNTAX_TABLE
+extern char *re_syntax_table;
+#endif
diff --git a/gnu/usr.bin/gdb/gdb/remote-utils.c b/gnu/usr.bin/gdb/gdb/remote-utils.c
new file mode 100644
index 0000000..f4f25e4
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/remote-utils.c
@@ -0,0 +1,645 @@
+/* Generic support for remote debugging interfaces.
+
+ Copyright 1993 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This file actually contains two distinct logical "packages". They
+ are packaged together in this one file because they are typically
+ used together.
+
+ The first package is an addition to the serial package. The
+ addition provides reading and writing with debugging output and
+ timeouts based on user settable variables. These routines are
+ intended to support serial port based remote backends. These
+ functions are prefixed with sr_.
+
+ The second package is a collection of more or less generic
+ functions for use by remote backends. They support user settable
+ variables for debugging, retries, and the like.
+
+ Todo:
+
+ * a pass through mode a la kermit or telnet.
+ * autobaud.
+ * ask remote to change his baud rate.
+ * put generic load here.
+
+ */
+
+#include <ctype.h>
+
+#include "defs.h"
+#include "gdbcmd.h"
+#include "target.h"
+#include "serial.h"
+#include "gdbcore.h" /* for exec_bfd */
+#include "inferior.h" /* for generic_mourn_inferior */
+#include "remote-utils.h"
+
+struct _sr_settings sr_settings = {
+ 4, /* timeout:
+ remote-hms.c had 2
+ remote-bug.c had "with a timeout of 2, we time out waiting for
+ the prompt after an s-record dump."
+
+ remote.c had (2): This was 5 seconds, which is a long time to
+ sit and wait. Unless this is going though some terminal server
+ or multiplexer or other form of hairy serial connection, I
+ would think 2 seconds would be plenty.
+*/
+
+ 10, /* retries */
+ NULL, /* device */
+ NULL, /* descriptor */
+};
+
+struct gr_settings *gr_settings = NULL;
+
+static void
+usage(proto, junk)
+ char *proto;
+ char *junk;
+{
+ if (junk != NULL)
+ fprintf(stderr, "Unrecognized arguments: `%s'.\n", junk);
+
+ /* FIXME-now: service@host? */
+
+ error("Usage: target %s <device <speed <debug>>>\n\
+or target %s <host> <port>\n", proto, proto);
+
+ return;
+}
+
+#define CHECKDONE(p, q) \
+{ \
+ if (q == p) \
+ { \
+ if (*p == '\0') \
+ return; \
+ else \
+ usage(proto, p); \
+ } \
+}
+
+void
+sr_scan_args(proto, args)
+ char *proto;
+ char *args;
+{
+ int n;
+ char *p, *q;
+
+ extern int strtol();
+
+ /* if no args, then nothing to do. */
+ if (args == NULL || *args == '\0')
+ return;
+
+ /* scan off white space. */
+ for (p = args; isspace(*p); ++p) ;;
+
+ /* find end of device name. */
+ for (q = p; *q != '\0' && !isspace(*q); ++q) ;;
+
+ /* check for missing or empty device name. */
+ CHECKDONE(p, q);
+ sr_set_device(savestring(p, q - p));
+
+ /* look for baud rate. */
+ n = strtol(q, &p, 10);
+
+ /* check for missing or empty baud rate. */
+ CHECKDONE(p, q);
+ sr_set_baud_rate(n);
+
+ /* look for debug value. */
+ n = strtol(p, &q, 10);
+
+ /* check for missing or empty debug value. */
+ CHECKDONE(p, q);
+ sr_set_debug(n);
+
+ /* scan off remaining white space. */
+ for (p = q; isspace(*p); ++p) ;;
+
+ /* if not end of string, then there's unrecognized junk. */
+ if (*p != '\0')
+ usage(proto, p);
+
+ return;
+}
+
+void
+gr_generic_checkin()
+{
+ sr_write_cr("");
+ gr_expect_prompt();
+}
+
+void
+gr_open(args, from_tty, gr)
+ char *args;
+ int from_tty;
+ struct gr_settings *gr;
+{
+ target_preopen(from_tty);
+ sr_scan_args(gr->ops->to_shortname, args);
+ unpush_target(gr->ops);
+
+ gr_settings = gr;
+
+ gr_set_dcache(dcache_init(gr->readfunc, gr->writefunc));
+
+ if (sr_get_desc() != NULL)
+ gr_close (0);
+
+ sr_set_desc(SERIAL_OPEN (sr_get_device()));
+ if (!sr_get_desc())
+ perror_with_name((char *) sr_get_device());
+
+ if (SERIAL_SETBAUDRATE(sr_get_desc(), sr_get_baud_rate()) != 0)
+ {
+ SERIAL_CLOSE(sr_get_desc());
+ perror_with_name(sr_get_device());
+ }
+
+ SERIAL_RAW (sr_get_desc());
+
+ /* If there is something sitting in the buffer we might take it as a
+ response to a command, which would be bad. */
+ SERIAL_FLUSH_INPUT (sr_get_desc ());
+
+ /* default retries */
+ if (sr_get_retries() == 0)
+ sr_set_retries(1);
+
+ /* default clear breakpoint function */
+ if (gr_settings->clear_all_breakpoints == NULL)
+ gr_settings->clear_all_breakpoints = remove_breakpoints;
+
+ if (from_tty)
+ printf_filtered ("Remote debugging using `%s' at baud rate of %d\n",
+ sr_get_device(), sr_get_baud_rate());
+
+ push_target(gr->ops);
+ gr_checkin();
+ gr_clear_all_breakpoints ();
+ return;
+}
+
+/* Read a character from the remote system masking it down to 7 bits
+ and doing all the fancy timeout stuff. */
+
+int
+sr_readchar ()
+{
+ int buf;
+
+ buf = SERIAL_READCHAR (sr_get_desc(), sr_get_timeout());
+
+ if (buf == SERIAL_TIMEOUT)
+ error ("Timeout reading from remote system.");
+
+ if (sr_get_debug() > 0)
+ printf ("%c", buf);
+
+ return buf & 0x7f;
+}
+
+int
+sr_pollchar()
+{
+ int buf;
+
+ buf = SERIAL_READCHAR (sr_get_desc(), 0);
+ if (buf == SERIAL_TIMEOUT)
+ buf = 0;
+ if (sr_get_debug() > 0)
+ if (buf)
+ printf ("%c", buf);
+ else
+ printf ("<empty character poll>");
+
+ return buf & 0x7f;
+}
+
+/* Keep discarding input from the remote system, until STRING is found.
+ Let the user break out immediately. */
+void
+sr_expect (string)
+ char *string;
+{
+ char *p = string;
+
+ immediate_quit = 1;
+ while (1)
+ {
+ if (sr_readchar () == *p)
+ {
+ p++;
+ if (*p == '\0')
+ {
+ immediate_quit = 0;
+ return;
+ }
+ }
+ else
+ p = string;
+ }
+}
+
+void
+sr_write (a, l)
+ char *a;
+ int l;
+{
+ int i;
+
+ if (SERIAL_WRITE (sr_get_desc(), a, l) != 0)
+ perror_with_name ("sr_write: Error writing to remote");
+
+ if (sr_get_debug() > 0)
+ for (i = 0; i < l; i++)
+ printf ("%c", a[i]);
+
+ return;
+}
+
+void
+sr_write_cr (s)
+ char *s;
+{
+ sr_write (s, strlen (s));
+ sr_write ("\r", 1);
+ return;
+}
+
+int
+sr_timed_read (buf, n)
+ char *buf;
+ int n;
+{
+ int i;
+ char c;
+
+ i = 0;
+ while (i < n)
+ {
+ c = sr_readchar ();
+
+ if (c == 0)
+ return i;
+ buf[i] = c;
+ i++;
+
+ }
+ return i;
+}
+
+/* Get a hex digit from the remote system & return its value. If
+ ignore_space is nonzero, ignore spaces (not newline, tab, etc). */
+
+int
+sr_get_hex_digit (ignore_space)
+ int ignore_space;
+{
+ int ch;
+
+ while (1)
+ {
+ ch = sr_readchar ();
+ if (ch >= '0' && ch <= '9')
+ return ch - '0';
+ else if (ch >= 'A' && ch <= 'F')
+ return ch - 'A' + 10;
+ else if (ch >= 'a' && ch <= 'f')
+ return ch - 'a' + 10;
+ else if (ch != ' ' || !ignore_space)
+ {
+ gr_expect_prompt ();
+ error ("Invalid hex digit from remote system.");
+ }
+ }
+}
+
+/* Get a byte from the remote and put it in *BYT. Accept any number
+ leading spaces. */
+void
+sr_get_hex_byte (byt)
+ char *byt;
+{
+ int val;
+
+ val = sr_get_hex_digit (1) << 4;
+ val |= sr_get_hex_digit (0);
+ *byt = val;
+}
+
+/* Read a 32-bit hex word from the remote, preceded by a space */
+long
+sr_get_hex_word ()
+{
+ long val;
+ int j;
+
+ val = 0;
+ for (j = 0; j < 8; j++)
+ val = (val << 4) + sr_get_hex_digit (j == 0);
+ return val;
+}
+
+/* Put a command string, in args, out to the remote. The remote is assumed to
+ be in raw mode, all writing/reading done through desc.
+ Ouput from the remote is placed on the users terminal until the
+ prompt from the remote is seen.
+ FIXME: Can't handle commands that take input. */
+
+void
+sr_com (args, fromtty)
+ char *args;
+ int fromtty;
+{
+ sr_check_open ();
+
+ if (!args)
+ return;
+
+ /* Clear all input so only command relative output is displayed */
+
+ sr_write_cr (args);
+ sr_write ("\030", 1);
+ gr_expect_prompt ();
+}
+
+void
+gr_close(quitting)
+ int quitting;
+{
+ gr_clear_all_breakpoints();
+
+ if (sr_is_open())
+ {
+ SERIAL_CLOSE (sr_get_desc());
+ sr_set_desc(NULL);
+ }
+
+ return;
+}
+
+/* gr_detach()
+ takes a program previously attached to and detaches it.
+ We better not have left any breakpoints
+ in the program or it'll die when it hits one.
+ Close the open connection to the remote debugger.
+ Use this when you want to detach and do something else
+ with your gdb. */
+
+void
+gr_detach(args, from_tty)
+ char *args;
+ int from_tty;
+{
+ if (args)
+ error ("Argument given to \"detach\" when remotely debugging.");
+
+ if (sr_is_open())
+ gr_clear_all_breakpoints ();
+
+ pop_target ();
+ if (from_tty)
+ puts_filtered ("Ending remote debugging.\n");
+
+ return;
+}
+
+void
+gr_files_info (ops)
+ struct target_ops *ops;
+{
+ char *file = "nothing";
+
+ if (exec_bfd)
+ file = bfd_get_filename (exec_bfd);
+
+ if (exec_bfd)
+ {
+#ifdef __GO32__
+ printf_filtered ("\tAttached to DOS asynctsr\n");
+#else
+ printf_filtered ("\tAttached to %s at %d baud\n",
+ sr_get_device(), sr_get_baud_rate());
+#endif
+ }
+
+ printf_filtered ("\tand running program %s\n", file);
+ printf_filtered ("\tusing the %s protocol.\n", ops->to_shortname);
+}
+
+void
+gr_mourn ()
+{
+ gr_clear_all_breakpoints ();
+ unpush_target (gr_get_ops());
+ generic_mourn_inferior ();
+}
+
+void
+gr_kill ()
+{
+ return;
+}
+
+/* This is called not only when we first attach, but also when the
+ user types "run" after having attached. */
+void
+gr_create_inferior (execfile, args, env)
+ char *execfile;
+ char *args;
+ char **env;
+{
+ int entry_pt;
+
+ if (args && *args)
+ error ("Can't pass arguments to remote process.");
+
+ if (execfile == 0 || exec_bfd == 0)
+ error ("No exec file specified");
+
+ entry_pt = (int) bfd_get_start_address (exec_bfd);
+ sr_check_open ();
+
+ gr_kill ();
+ gr_clear_all_breakpoints ();
+
+ init_wait_for_inferior ();
+ gr_checkin();
+
+ insert_breakpoints (); /* Needed to get correct instruction in cache */
+ proceed (entry_pt, -1, 0);
+}
+
+/* Given a null terminated list of strings LIST, read the input until we find one of
+ them. Return the index of the string found or -1 on error. '?' means match
+ any single character. Note that with the algorithm we use, the initial
+ character of the string cannot recur in the string, or we will not find some
+ cases of the string in the input. If PASSTHROUGH is non-zero, then
+ pass non-matching data on. */
+
+int
+gr_multi_scan (list, passthrough)
+ char *list[];
+ int passthrough;
+{
+ char *swallowed = NULL; /* holding area */
+ char *swallowed_p = swallowed; /* Current position in swallowed. */
+ int ch;
+ int ch_handled;
+ int i;
+ int string_count;
+ int max_length;
+ char **plist;
+
+ /* Look through the strings. Count them. Find the largest one so we can
+ allocate a holding area. */
+
+ for (max_length = string_count = i = 0;
+ list[i] != NULL;
+ ++i, ++string_count)
+ {
+ int length = strlen(list[i]);
+
+ if (length > max_length)
+ max_length = length;
+ }
+
+ /* if we have no strings, then something is wrong. */
+ if (string_count == 0)
+ return(-1);
+
+ /* otherwise, we will need a holding area big enough to hold almost two
+ copies of our largest string. */
+ swallowed_p = swallowed = alloca(max_length << 1);
+
+ /* and a list of pointers to current scan points. */
+ plist = (char **) alloca (string_count * sizeof(*plist));
+
+ /* and initialize */
+ for (i = 0; i < string_count; ++i)
+ plist[i] = list[i];
+
+ for (ch = sr_readchar(); /* loop forever */ ; ch = sr_readchar())
+ {
+ QUIT; /* Let user quit and leave process running */
+ ch_handled = 0;
+
+ for (i = 0; i < string_count; ++i)
+ {
+ if (ch == *plist[i] || *plist[i] == '?')
+ {
+ ++plist[i];
+ if (*plist[i] == '\0')
+ return(i);
+
+ if (!ch_handled)
+ *swallowed_p++ = ch;
+
+ ch_handled = 1;
+ }
+ else
+ plist[i] = list[i];
+ }
+
+ if (!ch_handled)
+ {
+ char *p;
+
+ /* Print out any characters which have been swallowed. */
+ if (passthrough)
+ {
+ for (p = swallowed; p < swallowed_p; ++p)
+ putc (*p, stdout);
+
+ putc (ch, stdout);
+ }
+
+ swallowed_p = swallowed;
+ }
+ }
+#if 0
+ /* Never reached. */
+ return(-1);
+#endif
+}
+
+/* Get ready to modify the registers array. On machines which store
+ individual registers, this doesn't need to do anything. On machines
+ which store all the registers in one fell swoop, this makes sure
+ that registers contains all the registers from the program being
+ debugged. */
+
+void
+gr_prepare_to_store ()
+{
+ /* Do nothing, since we assume we can store individual regs */
+}
+
+/* Read a word from remote address ADDR and return it.
+ * This goes through the data cache.
+ */
+int
+gr_fetch_word (addr)
+ CORE_ADDR addr;
+{
+ return dcache_fetch (gr_get_dcache(), addr);
+}
+
+/* Write a word WORD into remote address ADDR.
+ This goes through the data cache. */
+
+void
+gr_store_word (addr, word)
+ CORE_ADDR addr;
+ int word;
+{
+ dcache_poke (gr_get_dcache(), addr, word);
+}
+
+void
+_initialize_sr_support ()
+{
+/* FIXME-now: if target is open when baud changes... */
+ add_show_from_set (add_set_cmd ("remotebaud", no_class,
+ var_zinteger, (char *)&baud_rate,
+ "Set baud rate for remote serial I/O.\n\
+This value is used to set the speed of the serial port when debugging\n\
+using remote targets.", &setlist),
+ &showlist);
+
+/* FIXME-now: if target is open... */
+ add_show_from_set (add_set_cmd ("remotedevice", no_class,
+ var_filename, (char *)&sr_settings.device,
+ "Set device for remote serial I/O.\n\
+This device is used as the serial port when debugging using remote\n\
+targets.", &setlist),
+ &showlist);
+
+ add_com ("remote <command>", class_obscure, sr_com,
+ "Send a command to the remote monitor.");
+
+}
diff --git a/gnu/usr.bin/gdb/gdb/remote-utils.h b/gnu/usr.bin/gdb/gdb/remote-utils.h
new file mode 100644
index 0000000..59f4d3b
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/remote-utils.h
@@ -0,0 +1,149 @@
+/* Generic support for remote debugging interfaces.
+
+ Copyright 1993 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef REMOTE_UTILS_H
+#define REMOTE_UTILS_H
+
+#include "serial.h"
+#include "target.h"
+#include "dcache.h"
+
+/* Stuff that should be shared (and handled consistently) among the various
+ remote targets. */
+
+struct _sr_settings {
+ unsigned int timeout;
+
+ int retries;
+
+ char *device;
+ serial_t desc;
+
+};
+
+extern struct _sr_settings sr_settings;
+extern int remote_debug;
+extern int baud_rate;
+
+/* get and set debug value. */
+#define sr_get_debug() (remote_debug)
+#define sr_set_debug(newval) (remote_debug = (newval))
+
+/* get and set baud rate. */
+#define sr_get_baud_rate() (baud_rate)
+#define sr_set_baud_rate(newval) (baud_rate = (newval))
+
+/* get and set timeout. */
+#define sr_get_timeout() (sr_settings.timeout)
+#define sr_set_timeout(newval) (sr_settings.timeout = (newval))
+
+/* get and set device. */
+#define sr_get_device() (sr_settings.device)
+#define sr_set_device(newval) \
+{ \
+ if (sr_settings.device) free(sr_settings.device); \
+ sr_settings.device = (newval); \
+}
+
+/* get and set descriptor value. */
+#define sr_get_desc() (sr_settings.desc)
+#define sr_set_desc(newval) (sr_settings.desc = (newval))
+
+/* get and set retries. */
+#define sr_get_retries() (sr_settings.retries)
+#define sr_set_retries(newval) (sr_settings.retries = (newval))
+
+#define sr_is_open() (sr_settings.desc != NULL)
+
+#define sr_check_open() { if (!sr_is_open()) \
+ error ("Remote device not open"); }
+
+struct gr_settings {
+ /* This is our data cache. */
+ DCACHE *dcache;
+ char *prompt;
+ struct target_ops *ops;
+ int (*clear_all_breakpoints)PARAMS((void));
+ memxferfunc readfunc;
+ memxferfunc writefunc;
+ void (*checkin)PARAMS((void));
+};
+
+extern struct gr_settings *gr_settings;
+
+/* get and set dcache. */
+#define gr_get_dcache() (gr_settings->dcache)
+#define gr_set_dcache(newval) (gr_settings->dcache = (newval))
+
+/* get and set prompt. */
+#define gr_get_prompt() (gr_settings->prompt)
+#define gr_set_prompt(newval) (gr_settings->prompt = (newval))
+
+/* get and set ops. */
+#define gr_get_ops() (gr_settings->ops)
+#define gr_set_ops(newval) (gr_settings->ops = (newval))
+
+#define gr_clear_all_breakpoints() ((gr_settings->clear_all_breakpoints)())
+#define gr_checkin() ((gr_settings->checkin)())
+
+/* Keep discarding input until we see the prompt.
+
+ The convention for dealing with the prompt is that you
+ o give your command
+ o *then* wait for the prompt.
+
+ Thus the last thing that a procedure does with the serial line
+ will be an gr_expect_prompt(). Exception: resume does not
+ wait for the prompt, because the terminal is being handed over
+ to the inferior. However, the next thing which happens after that
+ is a bug_wait which does wait for the prompt.
+ Note that this includes abnormal exit, e.g. error(). This is
+ necessary to prevent getting into states from which we can't
+ recover. */
+
+#define gr_expect_prompt() sr_expect(gr_get_prompt())
+
+int gr_fetch_word PARAMS((CORE_ADDR addr));
+int gr_multi_scan PARAMS((char *list[], int passthrough));
+int sr_get_hex_digit PARAMS((int ignore_space));
+int sr_pollchar PARAMS((void));
+int sr_readchar PARAMS((void));
+int sr_timed_read PARAMS((char *buf, int n));
+long sr_get_hex_word PARAMS((void));
+void gr_close PARAMS((int quitting));
+void gr_create_inferior PARAMS((char *execfile, char *args, char **env));
+void gr_detach PARAMS((char *args, int from_tty));
+void gr_files_info PARAMS((struct target_ops *ops));
+void gr_generic_checkin PARAMS((void));
+void gr_kill PARAMS((void));
+void gr_mourn PARAMS((void));
+void gr_prepare_to_store PARAMS((void));
+void gr_store_word PARAMS((CORE_ADDR addr, int word));
+void sr_expect PARAMS((char *string));
+void sr_get_hex_byte PARAMS((char *byt));
+void sr_scan_args PARAMS((char *proto, char *args));
+void sr_write PARAMS((char *a, int l));
+void sr_write_cr PARAMS((char *s));
+
+void gr_open PARAMS((char *args, int from_tty,
+ struct gr_settings *gr_settings));
+
+
+#endif /* REMOTE_UTILS_H */
diff --git a/gnu/usr.bin/gdb/gdb/remote.c b/gnu/usr.bin/gdb/gdb/remote.c
new file mode 100644
index 0000000..266d5f3
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/remote.c
@@ -0,0 +1,1272 @@
+/* Remote target communications for serial-line targets in custom GDB protocol
+ Copyright 1988, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Remote communication protocol.
+
+ A debug packet whose contents are <data>
+ is encapsulated for transmission in the form:
+
+ $ <data> # CSUM1 CSUM2
+
+ <data> must be ASCII alphanumeric and cannot include characters
+ '$' or '#'
+
+ CSUM1 and CSUM2 are ascii hex representation of an 8-bit
+ checksum of <data>, the most significant nibble is sent first.
+ the hex digits 0-9,a-f are used.
+
+ Receiver responds with:
+
+ + - if CSUM is correct and ready for next packet
+ - - if CSUM is incorrect
+
+ <data> is as follows:
+ All values are encoded in ascii hex digits.
+
+ Request Packet
+
+ read registers g
+ reply XX....X Each byte of register data
+ is described by two hex digits.
+ Registers are in the internal order
+ for GDB, and the bytes in a register
+ are in the same order the machine uses.
+ or ENN for an error.
+
+ write regs GXX..XX Each byte of register data
+ is described by two hex digits.
+ reply OK for success
+ ENN for an error
+
+ read mem mAA..AA,LLLL AA..AA is address, LLLL is length.
+ reply XX..XX XX..XX is mem contents
+ Can be fewer bytes than requested
+ if able to read only part of the data.
+ or ENN NN is errno
+
+ write mem MAA..AA,LLLL:XX..XX
+ AA..AA is address,
+ LLLL is number of bytes,
+ XX..XX is data
+ reply OK for success
+ ENN for an error (this includes the case
+ where only part of the data was
+ written).
+
+ cont cAA..AA AA..AA is address to resume
+ If AA..AA is omitted,
+ resume at same address.
+
+ step sAA..AA AA..AA is address to resume
+ If AA..AA is omitted,
+ resume at same address.
+
+ last signal ? Reply the current reason for stopping.
+ This is the same reply as is generated
+ for step or cont : SAA where AA is the
+ signal number.
+
+ There is no immediate reply to step or cont.
+ The reply comes when the machine stops.
+ It is SAA AA is the "signal number"
+
+ or... TAAn...:r...;n:r...;n...:r...;
+ AA = signal number
+ n... = register number
+ r... = register contents
+ or... WAA The process extited, and AA is
+ the exit status. This is only
+ applicable for certains sorts of
+ targets.
+ or... NAATT;DD;BB Relocate the object file.
+ AA = signal number
+ TT = text address
+ DD = data address
+ BB = bss address
+ This is used by the NLM stub,
+ which is why it only has three
+ addresses rather than one per
+ section: the NLM stub always
+ sees only three sections, even
+ though gdb may see more.
+
+ kill request k
+
+ toggle debug d toggle debug flag (see 386 & 68k stubs)
+ reset r reset -- see sparc stub.
+ reserved <other> On other requests, the stub should
+ ignore the request and send an empty
+ response ($#<checksum>). This way
+ we can extend the protocol and GDB
+ can tell whether the stub it is
+ talking to uses the old or the new.
+*/
+
+#include "defs.h"
+#include <string.h>
+#include <fcntl.h>
+#include "frame.h"
+#include "inferior.h"
+#include "bfd.h"
+#include "symfile.h"
+#include "target.h"
+#include "wait.h"
+#include "terminal.h"
+#include "gdbcmd.h"
+#include "objfiles.h"
+#include "gdb-stabs.h"
+
+#include "dcache.h"
+
+#if !defined(DONT_USE_REMOTE)
+#ifdef USG
+#include <sys/types.h>
+#endif
+
+#include <signal.h>
+#include "serial.h"
+
+/* Prototypes for local functions */
+
+static int
+remote_write_bytes PARAMS ((CORE_ADDR memaddr, unsigned char *myaddr, int len));
+
+static int
+remote_read_bytes PARAMS ((CORE_ADDR memaddr, unsigned char *myaddr, int len));
+
+static void
+remote_files_info PARAMS ((struct target_ops *ignore));
+
+static int
+remote_xfer_memory PARAMS ((CORE_ADDR memaddr, char *myaddr, int len,
+ int should_write, struct target_ops *target));
+
+static void
+remote_prepare_to_store PARAMS ((void));
+
+static void
+remote_fetch_registers PARAMS ((int regno));
+
+static void
+remote_resume PARAMS ((int pid, int step, int siggnal));
+
+static int
+remote_start_remote PARAMS ((char *dummy));
+
+static void
+remote_open PARAMS ((char *name, int from_tty));
+
+static void
+remote_close PARAMS ((int quitting));
+
+static void
+remote_store_registers PARAMS ((int regno));
+
+static void
+getpkt PARAMS ((char *buf, int forever));
+
+static void
+putpkt PARAMS ((char *buf));
+
+static void
+remote_send PARAMS ((char *buf));
+
+static int
+readchar PARAMS ((void));
+
+static int
+remote_wait PARAMS ((int pid, WAITTYPE *status));
+
+static int
+tohex PARAMS ((int nib));
+
+static int
+fromhex PARAMS ((int a));
+
+static void
+remote_detach PARAMS ((char *args, int from_tty));
+
+static void
+remote_interrupt PARAMS ((int signo));
+
+static void
+remote_interrupt_twice PARAMS ((int signo));
+
+static void
+interrupt_query PARAMS ((void));
+
+extern struct target_ops remote_ops; /* Forward decl */
+
+extern int baud_rate;
+
+extern int remote_debug;
+
+/* This was 5 seconds, which is a long time to sit and wait.
+ Unless this is going though some terminal server or multiplexer or
+ other form of hairy serial connection, I would think 2 seconds would
+ be plenty. */
+static int timeout = 2;
+
+#if 0
+int icache;
+#endif
+
+/* Descriptor for I/O to remote machine. Initialize it to NULL so that
+ remote_open knows that we don't have a file open when the program
+ starts. */
+serial_t remote_desc = NULL;
+
+#define PBUFSIZ 1024
+
+/* Maximum number of bytes to read/write at once. The value here
+ is chosen to fill up a packet (the headers account for the 32). */
+#define MAXBUFBYTES ((PBUFSIZ-32)/2)
+
+/* Round up PBUFSIZ to hold all the registers, at least. */
+#if REGISTER_BYTES > MAXBUFBYTES
+#undef PBUFSIZ
+#define PBUFSIZ (REGISTER_BYTES * 2 + 32)
+#endif
+
+/* Clean up connection to a remote debugger. */
+
+/* ARGSUSED */
+static void
+remote_close (quitting)
+ int quitting;
+{
+ if (remote_desc)
+ SERIAL_CLOSE (remote_desc);
+ remote_desc = NULL;
+}
+
+/* Stub for catch_errors. */
+
+static int
+remote_start_remote (dummy)
+ char *dummy;
+{
+ immediate_quit = 1; /* Allow user to interrupt it */
+
+ /* Ack any packet which the remote side has already sent. */
+ /* I'm not sure this \r is needed; we don't use it any other time we
+ send an ack. */
+ SERIAL_WRITE (remote_desc, "+\r", 2);
+ putpkt ("?"); /* initiate a query from remote machine */
+ immediate_quit = 0;
+
+ start_remote (); /* Initialize gdb process mechanisms */
+ return 1;
+}
+
+/* Open a connection to a remote debugger.
+ NAME is the filename used for communication. */
+
+static DCACHE *remote_dcache;
+
+static void
+remote_open (name, from_tty)
+ char *name;
+ int from_tty;
+{
+ if (name == 0)
+ error (
+"To open a remote debug connection, you need to specify what serial\n\
+device is attached to the remote system (e.g. /dev/ttya).");
+
+ target_preopen (from_tty);
+
+ unpush_target (&remote_ops);
+
+ remote_dcache = dcache_init (remote_read_bytes, remote_write_bytes);
+
+ remote_desc = SERIAL_OPEN (name);
+ if (!remote_desc)
+ perror_with_name (name);
+
+ if (SERIAL_SETBAUDRATE (remote_desc, baud_rate))
+ {
+ SERIAL_CLOSE (remote_desc);
+ perror_with_name (name);
+ }
+
+ SERIAL_RAW (remote_desc);
+
+ /* If there is something sitting in the buffer we might take it as a
+ response to a command, which would be bad. */
+ SERIAL_FLUSH_INPUT (remote_desc);
+
+ if (from_tty)
+ {
+ puts_filtered ("Remote debugging using ");
+ puts_filtered (name);
+ puts_filtered ("\n");
+ }
+ push_target (&remote_ops); /* Switch to using remote target now */
+
+ /* Start the remote connection; if error (0), discard this target.
+ In particular, if the user quits, be sure to discard it
+ (we'd be in an inconsistent state otherwise). */
+ if (!catch_errors (remote_start_remote, (char *)0,
+ "Couldn't establish connection to remote target\n", RETURN_MASK_ALL))
+ pop_target();
+}
+
+/* remote_detach()
+ takes a program previously attached to and detaches it.
+ We better not have left any breakpoints
+ in the program or it'll die when it hits one.
+ Close the open connection to the remote debugger.
+ Use this when you want to detach and do something else
+ with your gdb. */
+
+static void
+remote_detach (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ if (args)
+ error ("Argument given to \"detach\" when remotely debugging.");
+
+ pop_target ();
+ if (from_tty)
+ puts_filtered ("Ending remote debugging.\n");
+}
+
+/* Convert hex digit A to a number. */
+
+static int
+fromhex (a)
+ int a;
+{
+ if (a >= '0' && a <= '9')
+ return a - '0';
+ else if (a >= 'a' && a <= 'f')
+ return a - 'a' + 10;
+ else
+ error ("Reply contains invalid hex digit");
+ return -1;
+}
+
+/* Convert number NIB to a hex digit. */
+
+static int
+tohex (nib)
+ int nib;
+{
+ if (nib < 10)
+ return '0'+nib;
+ else
+ return 'a'+nib-10;
+}
+
+/* Tell the remote machine to resume. */
+
+static void
+remote_resume (pid, step, siggnal)
+ int pid, step, siggnal;
+{
+ char buf[PBUFSIZ];
+
+ if (siggnal)
+ {
+ char *name;
+ target_terminal_ours_for_output ();
+ printf_filtered ("Can't send signals to a remote system. ");
+ name = strsigno (siggnal);
+ if (name)
+ printf_filtered (name);
+ else
+ printf_filtered ("Signal %d", siggnal);
+ printf_filtered (" not sent.\n");
+ target_terminal_inferior ();
+ }
+
+ dcache_flush (remote_dcache);
+
+ strcpy (buf, step ? "s": "c");
+
+ putpkt (buf);
+}
+
+/* Send ^C to target to halt it. Target will respond, and send us a
+ packet. */
+
+static void
+remote_interrupt (signo)
+ int signo;
+{
+ /* If this doesn't work, try more severe steps. */
+ signal (signo, remote_interrupt_twice);
+
+ if (remote_debug)
+ printf ("remote_interrupt called\n");
+
+ SERIAL_WRITE (remote_desc, "\003", 1); /* Send a ^C */
+}
+
+static void (*ofunc)();
+
+/* The user typed ^C twice. */
+static void
+remote_interrupt_twice (signo)
+ int signo;
+{
+ signal (signo, ofunc);
+
+ interrupt_query ();
+
+ signal (signo, remote_interrupt);
+}
+
+/* Ask the user what to do when an interrupt is received. */
+
+static void
+interrupt_query ()
+{
+ target_terminal_ours ();
+
+ if (query ("Interrupted while waiting for the program.\n\
+Give up (and stop debugging it)? "))
+ {
+ target_mourn_inferior ();
+ return_to_top_level (RETURN_QUIT);
+ }
+
+ target_terminal_inferior ();
+}
+
+/* Wait until the remote machine stops, then return,
+ storing status in STATUS just as `wait' would.
+ Returns "pid" (though it's not clear what, if anything, that
+ means in the case of this target). */
+
+static int
+remote_wait (pid, status)
+ int pid;
+ WAITTYPE *status;
+{
+ unsigned char buf[PBUFSIZ];
+
+ WSETEXIT ((*status), 0);
+
+ while (1)
+ {
+ unsigned char *p;
+
+ ofunc = (void (*)()) signal (SIGINT, remote_interrupt);
+ getpkt ((char *) buf, 1);
+ signal (SIGINT, ofunc);
+
+ if (buf[0] == 'E')
+ warning ("Remote failure reply: %s", buf);
+ else if (buf[0] == 'T')
+ {
+ int i;
+ long regno;
+ char regs[MAX_REGISTER_RAW_SIZE];
+
+ /* Expedited reply, containing Signal, {regno, reg} repeat */
+ /* format is: 'Tssn...:r...;n...:r...;n...:r...;#cc', where
+ ss = signal number
+ n... = register number
+ r... = register contents
+ */
+
+ p = &buf[3]; /* after Txx */
+
+ while (*p)
+ {
+ unsigned char *p1;
+
+ regno = strtol (p, &p1, 16); /* Read the register number */
+
+ if (p1 == p)
+ warning ("Remote sent badly formed register number: %s\nPacket: '%s'\n",
+ p1, buf);
+
+ p = p1;
+
+ if (*p++ != ':')
+ warning ("Malformed packet (missing colon): %s\nPacket: '%s'\n",
+ p, buf);
+
+ if (regno >= NUM_REGS)
+ warning ("Remote sent bad register number %d: %s\nPacket: '%s'\n",
+ regno, p, buf);
+
+ for (i = 0; i < REGISTER_RAW_SIZE (regno); i++)
+ {
+ if (p[0] == 0 || p[1] == 0)
+ warning ("Remote reply is too short: %s", buf);
+ regs[i] = fromhex (p[0]) * 16 + fromhex (p[1]);
+ p += 2;
+ }
+
+ if (*p++ != ';')
+ warning ("Remote register badly formatted: %s", buf);
+
+ supply_register (regno, regs);
+ }
+ break;
+ }
+ else if (buf[0] == 'N')
+ {
+ unsigned char *p1;
+ bfd_vma text_addr, data_addr, bss_addr;
+
+ /* Relocate object file. Format is NAATT;DD;BB where AA is
+ the signal number, TT is the new text address, DD is the
+ new data address, and BB is the new bss address. This is
+ used by the NLM stub; gdb may see more sections. */
+ p = &buf[3];
+ text_addr = strtoul (p, &p1, 16);
+ if (p1 == p || *p1 != ';')
+ warning ("Malformed relocation packet: Packet '%s'", buf);
+ p = p1 + 1;
+ data_addr = strtoul (p, &p1, 16);
+ if (p1 == p || *p1 != ';')
+ warning ("Malformed relocation packet: Packet '%s'", buf);
+ p = p1 + 1;
+ bss_addr = strtoul (p, &p1, 16);
+ if (p1 == p)
+ warning ("Malformed relocation packet: Packet '%s'", buf);
+
+ if (symfile_objfile != NULL
+ && (ANOFFSET (symfile_objfile->section_offsets,
+ SECT_OFF_TEXT) != text_addr
+ || ANOFFSET (symfile_objfile->section_offsets,
+ SECT_OFF_DATA) != data_addr
+ || ANOFFSET (symfile_objfile->section_offsets,
+ SECT_OFF_BSS) != bss_addr))
+ {
+ struct section_offsets *offs;
+
+ /* FIXME: This code assumes gdb-stabs.h is being used;
+ it's broken for xcoff, dwarf, sdb-coff, etc. But
+ there is no simple canonical representation for this
+ stuff. (Just what does "text" as seen by the stub
+ mean, anyway?). */
+
+ /* FIXME: Why don't the various symfile_offsets routines
+ in the sym_fns vectors set this?
+ (no good reason -kingdon). */
+ if (symfile_objfile->num_sections == 0)
+ symfile_objfile->num_sections = SECT_OFF_MAX;
+
+ offs = ((struct section_offsets *)
+ alloca (sizeof (struct section_offsets)
+ + (symfile_objfile->num_sections
+ * sizeof (offs->offsets))));
+ memcpy (offs, symfile_objfile->section_offsets,
+ (sizeof (struct section_offsets)
+ + (symfile_objfile->num_sections
+ * sizeof (offs->offsets))));
+ ANOFFSET (offs, SECT_OFF_TEXT) = text_addr;
+ ANOFFSET (offs, SECT_OFF_DATA) = data_addr;
+ ANOFFSET (offs, SECT_OFF_BSS) = bss_addr;
+
+ objfile_relocate (symfile_objfile, offs);
+ {
+ struct obj_section *s;
+ bfd *abfd;
+
+ abfd = symfile_objfile->obfd;
+
+ for (s = symfile_objfile->sections;
+ s < symfile_objfile->sections_end; ++s)
+ {
+ flagword flags;
+
+ flags = bfd_get_section_flags (abfd, s->sec_ptr);
+
+ if (flags & SEC_CODE)
+ {
+ s->addr += text_addr;
+ s->endaddr += text_addr;
+ }
+ else if (flags & (SEC_DATA | SEC_LOAD))
+ {
+ s->addr += data_addr;
+ s->endaddr += data_addr;
+ }
+ else if (flags & SEC_ALLOC)
+ {
+ s->addr += bss_addr;
+ s->endaddr += bss_addr;
+ }
+ }
+ }
+ }
+ break;
+ }
+ else if (buf[0] == 'W')
+ {
+ /* The remote process exited. */
+ WSETEXIT (*status, (fromhex (buf[1]) << 4) + fromhex (buf[2]));
+ return 0;
+ }
+ else if (buf[0] == 'S')
+ break;
+ else
+ warning ("Invalid remote reply: %s", buf);
+ }
+
+ WSETSTOP ((*status), (((fromhex (buf[1])) << 4) + (fromhex (buf[2]))));
+
+ return 0;
+}
+
+/* Number of bytes of registers this stub implements. */
+static int register_bytes_found;
+
+/* Read the remote registers into the block REGS. */
+/* Currently we just read all the registers, so we don't use regno. */
+/* ARGSUSED */
+static void
+remote_fetch_registers (regno)
+ int regno;
+{
+ char buf[PBUFSIZ];
+ int i;
+ char *p;
+ char regs[REGISTER_BYTES];
+
+ sprintf (buf, "g");
+ remote_send (buf);
+
+ /* Unimplemented registers read as all bits zero. */
+ memset (regs, 0, REGISTER_BYTES);
+
+ /* We can get out of synch in various cases. If the first character
+ in the buffer is not a hex character, assume that has happened
+ and try to fetch another packet to read. */
+ while ((buf[0] < '0' || buf[0] > '9')
+ && (buf[0] < 'a' || buf[0] > 'f'))
+ {
+ if (remote_debug)
+ printf ("Bad register packet; fetching a new packet\n");
+ getpkt (buf, 0);
+ }
+
+ /* Reply describes registers byte by byte, each byte encoded as two
+ hex characters. Suck them all up, then supply them to the
+ register cacheing/storage mechanism. */
+
+ p = buf;
+ for (i = 0; i < REGISTER_BYTES; i++)
+ {
+ if (p[0] == 0)
+ break;
+ if (p[1] == 0)
+ {
+ warning ("Remote reply is of odd length: %s", buf);
+ /* Don't change register_bytes_found in this case, and don't
+ print a second warning. */
+ goto supply_them;
+ }
+ regs[i] = fromhex (p[0]) * 16 + fromhex (p[1]);
+ p += 2;
+ }
+
+ if (i != register_bytes_found)
+ {
+ register_bytes_found = i;
+#ifdef REGISTER_BYTES_OK
+ if (!REGISTER_BYTES_OK (i))
+ warning ("Remote reply is too short: %s", buf);
+#endif
+ }
+
+ supply_them:
+ for (i = 0; i < NUM_REGS; i++)
+ supply_register (i, &regs[REGISTER_BYTE(i)]);
+}
+
+/* Prepare to store registers. Since we send them all, we have to
+ read out the ones we don't want to change first. */
+
+static void
+remote_prepare_to_store ()
+{
+ /* Make sure the entire registers array is valid. */
+ read_register_bytes (0, (char *)NULL, REGISTER_BYTES);
+}
+
+/* Store the remote registers from the contents of the block REGISTERS.
+ FIXME, eventually just store one register if that's all that is needed. */
+
+/* ARGSUSED */
+static void
+remote_store_registers (regno)
+ int regno;
+{
+ char buf[PBUFSIZ];
+ int i;
+ char *p;
+
+ buf[0] = 'G';
+
+ /* Command describes registers byte by byte,
+ each byte encoded as two hex characters. */
+
+ p = buf + 1;
+ /* remote_prepare_to_store insures that register_bytes_found gets set. */
+ for (i = 0; i < register_bytes_found; i++)
+ {
+ *p++ = tohex ((registers[i] >> 4) & 0xf);
+ *p++ = tohex (registers[i] & 0xf);
+ }
+ *p = '\0';
+
+ remote_send (buf);
+}
+
+#if 0
+
+/* Use of the data cache is disabled because it loses for looking at
+ and changing hardware I/O ports and the like. Accepting `volatile'
+ would perhaps be one way to fix it, but a better way which would
+ win for more cases would be to use the executable file for the text
+ segment, like the `icache' code below but done cleanly (in some
+ target-independent place, perhaps in target_xfer_memory, perhaps
+ based on assigning each target a speed or perhaps by some simpler
+ mechanism). */
+
+/* Read a word from remote address ADDR and return it.
+ This goes through the data cache. */
+
+static int
+remote_fetch_word (addr)
+ CORE_ADDR addr;
+{
+#if 0
+ if (icache)
+ {
+ extern CORE_ADDR text_start, text_end;
+
+ if (addr >= text_start && addr < text_end)
+ {
+ int buffer;
+ xfer_core_file (addr, &buffer, sizeof (int));
+ return buffer;
+ }
+ }
+#endif
+ return dcache_fetch (remote_dcache, addr);
+}
+
+/* Write a word WORD into remote address ADDR.
+ This goes through the data cache. */
+
+static void
+remote_store_word (addr, word)
+ CORE_ADDR addr;
+ int word;
+{
+ dcache_poke (remote_dcache, addr, word);
+}
+#endif /* 0 */
+
+/* Write memory data directly to the remote machine.
+ This does not inform the data cache; the data cache uses this.
+ MEMADDR is the address in the remote memory space.
+ MYADDR is the address of the buffer in our space.
+ LEN is the number of bytes.
+
+ Returns number of bytes transferred, or 0 for error. */
+
+static int
+remote_write_bytes (memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ unsigned char *myaddr;
+ int len;
+{
+ char buf[PBUFSIZ];
+ int i;
+ char *p;
+
+ if (len > PBUFSIZ / 2 - 20)
+ abort ();
+
+ sprintf (buf, "M%x,%x:", memaddr, len);
+
+ /* We send target system values byte by byte, in increasing byte addresses,
+ each byte encoded as two hex characters. */
+
+ p = buf + strlen (buf);
+ for (i = 0; i < len; i++)
+ {
+ *p++ = tohex ((myaddr[i] >> 4) & 0xf);
+ *p++ = tohex (myaddr[i] & 0xf);
+ }
+ *p = '\0';
+
+ putpkt (buf);
+ getpkt (buf, 0);
+
+ if (buf[0] == 'E')
+ {
+ /* There is no correspondance between what the remote protocol uses
+ for errors and errno codes. We would like a cleaner way of
+ representing errors (big enough to include errno codes, bfd_error
+ codes, and others). But for now just return EIO. */
+ errno = EIO;
+ return 0;
+ }
+ return len;
+}
+
+/* Read memory data directly from the remote machine.
+ This does not use the data cache; the data cache uses this.
+ MEMADDR is the address in the remote memory space.
+ MYADDR is the address of the buffer in our space.
+ LEN is the number of bytes.
+
+ Returns number of bytes transferred, or 0 for error. */
+
+static int
+remote_read_bytes (memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ unsigned char *myaddr;
+ int len;
+{
+ char buf[PBUFSIZ];
+ int i;
+ char *p;
+
+ if (len > PBUFSIZ / 2 - 1)
+ abort ();
+
+ sprintf (buf, "m%x,%x", memaddr, len);
+ putpkt (buf);
+ getpkt (buf, 0);
+
+ if (buf[0] == 'E')
+ {
+ /* There is no correspondance between what the remote protocol uses
+ for errors and errno codes. We would like a cleaner way of
+ representing errors (big enough to include errno codes, bfd_error
+ codes, and others). But for now just return EIO. */
+ errno = EIO;
+ return 0;
+ }
+
+ /* Reply describes memory byte by byte,
+ each byte encoded as two hex characters. */
+
+ p = buf;
+ for (i = 0; i < len; i++)
+ {
+ if (p[0] == 0 || p[1] == 0)
+ /* Reply is short. This means that we were able to read only part
+ of what we wanted to. */
+ break;
+ myaddr[i] = fromhex (p[0]) * 16 + fromhex (p[1]);
+ p += 2;
+ }
+ return i;
+}
+
+/* Read or write LEN bytes from inferior memory at MEMADDR, transferring
+ to or from debugger address MYADDR. Write to inferior if SHOULD_WRITE is
+ nonzero. Returns length of data written or read; 0 for error. */
+
+/* ARGSUSED */
+static int
+remote_xfer_memory(memaddr, myaddr, len, should_write, target)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+ int should_write;
+ struct target_ops *target; /* ignored */
+{
+ int xfersize;
+ int bytes_xferred;
+ int total_xferred = 0;
+
+ while (len > 0)
+ {
+ if (len > MAXBUFBYTES)
+ xfersize = MAXBUFBYTES;
+ else
+ xfersize = len;
+
+ if (should_write)
+ bytes_xferred = remote_write_bytes (memaddr, myaddr, xfersize);
+ else
+ bytes_xferred = remote_read_bytes (memaddr, myaddr, xfersize);
+
+ /* If we get an error, we are done xferring. */
+ if (bytes_xferred == 0)
+ break;
+
+ memaddr += bytes_xferred;
+ myaddr += bytes_xferred;
+ len -= bytes_xferred;
+ total_xferred += bytes_xferred;
+ }
+ return total_xferred;
+}
+
+static void
+remote_files_info (ignore)
+ struct target_ops *ignore;
+{
+ puts_filtered ("Debugging a target over a serial line.\n");
+}
+
+/* Stuff for dealing with the packets which are part of this protocol.
+ See comment at top of file for details. */
+
+/* Read a single character from the remote end, masking it down to 7 bits. */
+
+static int
+readchar ()
+{
+ int ch;
+
+ ch = SERIAL_READCHAR (remote_desc, timeout);
+
+ if (ch < 0)
+ return ch;
+
+ return ch & 0x7f;
+}
+
+/* Send the command in BUF to the remote machine,
+ and read the reply into BUF.
+ Report an error if we get an error reply. */
+
+static void
+remote_send (buf)
+ char *buf;
+{
+
+ putpkt (buf);
+ getpkt (buf, 0);
+
+ if (buf[0] == 'E')
+ error ("Remote failure reply: %s", buf);
+}
+
+/* Send a packet to the remote machine, with error checking.
+ The data of the packet is in BUF. */
+
+static void
+putpkt (buf)
+ char *buf;
+{
+ int i;
+ unsigned char csum = 0;
+ char buf2[PBUFSIZ];
+ int cnt = strlen (buf);
+ int ch;
+ char *p;
+
+ /* Copy the packet into buffer BUF2, encapsulating it
+ and giving it a checksum. */
+
+ if (cnt > sizeof(buf2) - 5) /* Prosanity check */
+ abort();
+
+ p = buf2;
+ *p++ = '$';
+
+ for (i = 0; i < cnt; i++)
+ {
+ csum += buf[i];
+ *p++ = buf[i];
+ }
+ *p++ = '#';
+ *p++ = tohex ((csum >> 4) & 0xf);
+ *p++ = tohex (csum & 0xf);
+
+ /* Send it over and over until we get a positive ack. */
+
+ while (1)
+ {
+ if (remote_debug)
+ {
+ *p = '\0';
+ printf ("Sending packet: %s...", buf2); fflush(stdout);
+ }
+ if (SERIAL_WRITE (remote_desc, buf2, p - buf2))
+ perror_with_name ("putpkt: write failed");
+
+ /* read until either a timeout occurs (-2) or '+' is read */
+ while (1)
+ {
+ ch = readchar ();
+
+ switch (ch)
+ {
+ case '+':
+ if (remote_debug)
+ printf("Ack\n");
+ return;
+ case SERIAL_TIMEOUT:
+ break; /* Retransmit buffer */
+ case SERIAL_ERROR:
+ perror_with_name ("putpkt: couldn't read ACK");
+ case SERIAL_EOF:
+ error ("putpkt: EOF while trying to read ACK");
+ default:
+ if (remote_debug)
+ printf ("%02X %c ", ch&0xFF, ch);
+ continue;
+ }
+ break; /* Here to retransmit */
+ }
+
+ if (quit_flag)
+ {
+ quit_flag = 0;
+ interrupt_query ();
+ }
+ }
+}
+
+/* Read a packet from the remote machine, with error checking,
+ and store it in BUF. BUF is expected to be of size PBUFSIZ.
+ If FOREVER, wait forever rather than timing out; this is used
+ while the target is executing user code. */
+
+static void
+getpkt (buf, forever)
+ char *buf;
+ int forever;
+{
+ char *bp;
+ unsigned char csum;
+ int c = 0;
+ unsigned char c1, c2;
+ int retries = 0;
+#define MAX_RETRIES 10
+
+ while (1)
+ {
+ if (quit_flag)
+ {
+ quit_flag = 0;
+ interrupt_query ();
+ }
+
+ /* This can loop forever if the remote side sends us characters
+ continuously, but if it pauses, we'll get a zero from readchar
+ because of timeout. Then we'll count that as a retry. */
+
+ c = readchar();
+ if (c > 0 && c != '$')
+ continue;
+
+ if (c == SERIAL_TIMEOUT)
+ {
+ if (forever)
+ continue;
+ if (++retries >= MAX_RETRIES)
+ if (remote_debug) puts_filtered ("Timed out.\n");
+ goto out;
+ }
+
+ if (c == SERIAL_EOF)
+ error ("Remote connection closed");
+ if (c == SERIAL_ERROR)
+ perror_with_name ("Remote communication error");
+
+ /* Force csum to be zero here because of possible error retry. */
+ csum = 0;
+ bp = buf;
+
+ while (1)
+ {
+ c = readchar ();
+ if (c == SERIAL_TIMEOUT)
+ {
+ if (remote_debug)
+ puts_filtered ("Timeout in mid-packet, retrying\n");
+ goto whole; /* Start a new packet, count retries */
+ }
+ if (c == '$')
+ {
+ if (remote_debug)
+ puts_filtered ("Saw new packet start in middle of old one\n");
+ goto whole; /* Start a new packet, count retries */
+ }
+ if (c == '#')
+ break;
+ if (bp >= buf+PBUFSIZ-1)
+ {
+ *bp = '\0';
+ puts_filtered ("Remote packet too long: ");
+ puts_filtered (buf);
+ puts_filtered ("\n");
+ goto whole;
+ }
+ *bp++ = c;
+ csum += c;
+ }
+ *bp = 0;
+
+ c1 = fromhex (readchar ());
+ c2 = fromhex (readchar ());
+ if ((csum & 0xff) == (c1 << 4) + c2)
+ break;
+ printf_filtered ("Bad checksum, sentsum=0x%x, csum=0x%x, buf=",
+ (c1 << 4) + c2, csum & 0xff);
+ puts_filtered (buf);
+ puts_filtered ("\n");
+
+ /* Try the whole thing again. */
+whole:
+ if (++retries < MAX_RETRIES)
+ {
+ SERIAL_WRITE (remote_desc, "-", 1);
+ }
+ else
+ {
+ printf ("Ignoring packet error, continuing...\n");
+ break;
+ }
+ }
+
+out:
+
+ SERIAL_WRITE (remote_desc, "+", 1);
+
+ if (remote_debug)
+ fprintf (stderr,"Packet received: %s\n", buf);
+}
+
+static void
+remote_kill ()
+{
+ putpkt ("k");
+ /* Don't wait for it to die. I'm not really sure it matters whether
+ we do or not. For the existing stubs, kill is a noop. */
+ target_mourn_inferior ();
+}
+
+static void
+remote_mourn ()
+{
+ unpush_target (&remote_ops);
+ generic_mourn_inferior ();
+}
+
+#ifdef REMOTE_BREAKPOINT
+
+/* On some machines, e.g. 68k, we may use a different breakpoint instruction
+ than other targets. */
+static unsigned char break_insn[] = REMOTE_BREAKPOINT;
+
+/* Check that it fits in BREAKPOINT_MAX bytes. */
+static unsigned char check_break_insn_size[BREAKPOINT_MAX] = REMOTE_BREAKPOINT;
+
+#else /* No REMOTE_BREAKPOINT. */
+
+/* Same old breakpoint instruction. This code does nothing different
+ than mem-break.c. */
+static unsigned char break_insn[] = BREAKPOINT;
+
+#endif /* No REMOTE_BREAKPOINT. */
+
+/* Insert a breakpoint on targets that don't have any better breakpoint
+ support. We read the contents of the target location and stash it,
+ then overwrite it with a breakpoint instruction. ADDR is the target
+ location in the target machine. CONTENTS_CACHE is a pointer to
+ memory allocated for saving the target contents. It is guaranteed
+ by the caller to be long enough to save sizeof BREAKPOINT bytes (this
+ is accomplished via BREAKPOINT_MAX). */
+
+static int
+remote_insert_breakpoint (addr, contents_cache)
+ CORE_ADDR addr;
+ char *contents_cache;
+{
+ int val;
+
+ val = target_read_memory (addr, contents_cache, sizeof break_insn);
+
+ if (val == 0)
+ val = target_write_memory (addr, (char *)break_insn, sizeof break_insn);
+
+ return val;
+}
+
+static int
+remote_remove_breakpoint (addr, contents_cache)
+ CORE_ADDR addr;
+ char *contents_cache;
+{
+ return target_write_memory (addr, contents_cache, sizeof break_insn);
+}
+
+/* Define the target subroutine names */
+
+struct target_ops remote_ops = {
+ "remote", /* to_shortname */
+ "Remote serial target in gdb-specific protocol", /* to_longname */
+ "Use a remote computer via a serial line, using a gdb-specific protocol.\n\
+Specify the serial device it is connected to (e.g. /dev/ttya).", /* to_doc */
+ remote_open, /* to_open */
+ remote_close, /* to_close */
+ NULL, /* to_attach */
+ remote_detach, /* to_detach */
+ remote_resume, /* to_resume */
+ remote_wait, /* to_wait */
+ remote_fetch_registers, /* to_fetch_registers */
+ remote_store_registers, /* to_store_registers */
+ remote_prepare_to_store, /* to_prepare_to_store */
+ remote_xfer_memory, /* to_xfer_memory */
+ remote_files_info, /* to_files_info */
+
+ remote_insert_breakpoint, /* to_insert_breakpoint */
+ remote_remove_breakpoint, /* to_remove_breakpoint */
+
+ NULL, /* to_terminal_init */
+ NULL, /* to_terminal_inferior */
+ NULL, /* to_terminal_ours_for_output */
+ NULL, /* to_terminal_ours */
+ NULL, /* to_terminal_info */
+ remote_kill, /* to_kill */
+ generic_load, /* to_load */
+ NULL, /* to_lookup_symbol */
+ NULL, /* to_create_inferior */
+ remote_mourn, /* to_mourn_inferior */
+ 0, /* to_can_run */
+ 0, /* to_notice_signals */
+ process_stratum, /* to_stratum */
+ NULL, /* to_next */
+ 1, /* to_has_all_memory */
+ 1, /* to_has_memory */
+ 1, /* to_has_stack */
+ 1, /* to_has_registers */
+ 1, /* to_has_execution */
+ NULL, /* sections */
+ NULL, /* sections_end */
+ OPS_MAGIC /* to_magic */
+};
+
+void
+_initialize_remote ()
+{
+ add_target (&remote_ops);
+}
+#endif
diff --git a/gnu/usr.bin/gdb/gdb/ser-unix.c b/gnu/usr.bin/gdb/gdb/ser-unix.c
new file mode 100644
index 0000000..b306e8a
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/ser-unix.c
@@ -0,0 +1,633 @@
+/* Serial interface for local (hardwired) serial ports on Un*x like systems
+ Copyright 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "serial.h"
+#include <fcntl.h>
+#include <sys/types.h>
+
+#if !defined (HAVE_TERMIOS) && !defined (HAVE_TERMIO) && !defined (HAVE_SGTTY)
+#define HAVE_SGTTY
+#endif
+
+#ifdef HAVE_TERMIOS
+#include <termios.h>
+#include <unistd.h>
+
+struct hardwire_ttystate
+{
+ struct termios termios;
+};
+#endif /* termios */
+
+#ifdef HAVE_TERMIO
+#include <termio.h>
+
+/* It is believed that all systems which have added job control to SVR3
+ (e.g. sco) have also added termios. Even if not, trying to figure out
+ all the variations (TIOCGPGRP vs. TCGETPGRP, etc.) would be pretty
+ bewildering. So we don't attempt it. */
+
+struct hardwire_ttystate
+{
+ struct termio termio;
+};
+#endif /* termio */
+
+#ifdef HAVE_SGTTY
+/* Needed for the code which uses select(). We would include <sys/select.h>
+ too if it existed on all systems. */
+#include <sys/time.h>
+
+#include <sgtty.h>
+
+struct hardwire_ttystate
+{
+ struct sgttyb sgttyb;
+ struct tchars tc;
+ struct ltchars ltc;
+ /* Line discipline flags. */
+ int lmode;
+};
+#endif /* sgtty */
+
+static int hardwire_open PARAMS ((serial_t scb, const char *name));
+static void hardwire_raw PARAMS ((serial_t scb));
+static int wait_for PARAMS ((serial_t scb, int timeout));
+static int hardwire_readchar PARAMS ((serial_t scb, int timeout));
+static int rate_to_code PARAMS ((int rate));
+static int hardwire_setbaudrate PARAMS ((serial_t scb, int rate));
+static int hardwire_write PARAMS ((serial_t scb, const char *str, int len));
+/* FIXME: static void hardwire_restore PARAMS ((serial_t scb)); */
+static void hardwire_close PARAMS ((serial_t scb));
+static int get_tty_state PARAMS ((serial_t scb, struct hardwire_ttystate *state));
+static int set_tty_state PARAMS ((serial_t scb, struct hardwire_ttystate *state));
+static serial_ttystate hardwire_get_tty_state PARAMS ((serial_t scb));
+static int hardwire_set_tty_state PARAMS ((serial_t scb, serial_ttystate state));
+
+/* Open up a real live device for serial I/O */
+
+static int
+hardwire_open(scb, name)
+ serial_t scb;
+ const char *name;
+{
+ scb->fd = open (name, O_RDWR);
+ if (scb->fd < 0)
+ return -1;
+
+ return 0;
+}
+
+static int
+get_tty_state(scb, state)
+ serial_t scb;
+ struct hardwire_ttystate *state;
+{
+#ifdef HAVE_TERMIOS
+ extern int errno;
+
+ if (tcgetattr(scb->fd, &state->termios) < 0)
+ return -1;
+
+ return 0;
+#endif
+
+#ifdef HAVE_TERMIO
+ if (ioctl (scb->fd, TCGETA, &state->termio) < 0)
+ return -1;
+ return 0;
+#endif
+
+#ifdef HAVE_SGTTY
+ if (ioctl (scb->fd, TIOCGETP, &state->sgttyb) < 0)
+ return -1;
+ if (ioctl (scb->fd, TIOCGETC, &state->tc) < 0)
+ return -1;
+ if (ioctl (scb->fd, TIOCGLTC, &state->ltc) < 0)
+ return -1;
+ if (ioctl (scb->fd, TIOCLGET, &state->lmode) < 0)
+ return -1;
+
+ return 0;
+#endif
+}
+
+static int
+set_tty_state(scb, state)
+ serial_t scb;
+ struct hardwire_ttystate *state;
+{
+#ifdef HAVE_TERMIOS
+ if (tcsetattr(scb->fd, TCSANOW, &state->termios) < 0)
+ return -1;
+
+ return 0;
+#endif
+
+#ifdef HAVE_TERMIO
+ if (ioctl (scb->fd, TCSETA, &state->termio) < 0)
+ return -1;
+ return 0;
+#endif
+
+#ifdef HAVE_SGTTY
+ if (ioctl (scb->fd, TIOCSETN, &state->sgttyb) < 0)
+ return -1;
+
+ return 0;
+#endif
+}
+
+static serial_ttystate
+hardwire_get_tty_state(scb)
+ serial_t scb;
+{
+ struct hardwire_ttystate *state;
+
+ state = (struct hardwire_ttystate *)xmalloc(sizeof *state);
+
+ if (get_tty_state(scb, state))
+ return NULL;
+
+ return (serial_ttystate)state;
+}
+
+static int
+hardwire_set_tty_state(scb, ttystate)
+ serial_t scb;
+ serial_ttystate ttystate;
+{
+ struct hardwire_ttystate *state;
+
+ state = (struct hardwire_ttystate *)ttystate;
+
+ return set_tty_state(scb, state);
+}
+
+static int
+hardwire_noflush_set_tty_state (scb, new_ttystate, old_ttystate)
+ serial_t scb;
+ serial_ttystate new_ttystate;
+ serial_ttystate old_ttystate;
+{
+ struct hardwire_ttystate new_state;
+ struct hardwire_ttystate *state = (struct hardwire_ttystate *) old_ttystate;
+
+ new_state = *(struct hardwire_ttystate *)new_ttystate;
+
+#ifdef HAVE_TERMIOS
+ /* I'm not sure whether this is necessary; the manpage makes no mention
+ of discarding input when switching to/from ICANON. */
+ if (state->termios.c_lflag & ICANON)
+ new_state.termios.c_lflag |= ICANON;
+ else
+ new_state.termios.c_lflag &= ~ICANON;
+#endif
+
+#ifdef HAVE_TERMIO
+ /* I'm not sure whether this is necessary; the manpage makes no mention
+ of discarding input when switching to/from ICANON. */
+ if (state->termio.c_lflag & ICANON)
+ new_state.termio.c_lflag |= ICANON;
+ else
+ new_state.termio.c_lflag &= ~ICANON;
+#endif
+
+#ifdef HAVE_SGTTY
+ if (state->sgttyb.sg_flags & RAW)
+ new_state.sgttyb.sg_flags |= RAW;
+ else
+ new_state.sgttyb.sg_flags &= ~RAW;
+
+ /* I'm not sure whether this is necessary; the manpage just mentions
+ RAW not CBREAK. */
+ if (state->sgttyb.sg_flags & CBREAK)
+ new_state.sgttyb.sg_flags |= CBREAK;
+ else
+ new_state.sgttyb.sg_flags &= ~CBREAK;
+#endif
+
+ return set_tty_state (scb, &new_state);
+}
+
+static void
+hardwire_print_tty_state (scb, ttystate)
+ serial_t scb;
+ serial_ttystate ttystate;
+{
+ struct hardwire_ttystate *state = (struct hardwire_ttystate *) ttystate;
+ int i;
+
+#ifdef HAVE_TERMIOS
+ printf_filtered ("c_iflag = 0x%x, c_oflag = 0x%x,\n",
+ state->termios.c_iflag, state->termios.c_oflag);
+ printf_filtered ("c_cflag = 0x%x, c_lflag = 0x%x\n",
+ state->termios.c_cflag, state->termios.c_lflag);
+#if 0
+ /* This not in POSIX, and is not really documented by those systems
+ which have it (at least not Sun). */
+ printf_filtered ("c_line = 0x%x.\n", state->termios.c_line);
+#endif
+ printf_filtered ("c_cc: ");
+ for (i = 0; i < NCCS; i += 1)
+ printf_filtered ("0x%x ", state->termios.c_cc[i]);
+ printf_filtered ("\n");
+#endif
+
+#ifdef HAVE_TERMIO
+ printf_filtered ("c_iflag = 0x%x, c_oflag = 0x%x,\n",
+ state->termio.c_iflag, state->termio.c_oflag);
+ printf_filtered ("c_cflag = 0x%x, c_lflag = 0x%x, c_line = 0x%x.\n",
+ state->termio.c_cflag, state->termio.c_lflag,
+ state->termio.c_line);
+ printf_filtered ("c_cc: ");
+ for (i = 0; i < NCC; i += 1)
+ printf_filtered ("0x%x ", state->termio.c_cc[i]);
+ printf_filtered ("\n");
+#endif
+
+#ifdef HAVE_SGTTY
+ printf_filtered ("sgttyb.sg_flags = 0x%x.\n", state->sgttyb.sg_flags);
+
+ printf_filtered ("tchars: ");
+ for (i = 0; i < (int)sizeof (struct tchars); i++)
+ printf_filtered ("0x%x ", ((unsigned char *)&state->tc)[i]);
+ printf_filtered ("\n");
+
+ printf_filtered ("ltchars: ");
+ for (i = 0; i < (int)sizeof (struct ltchars); i++)
+ printf_filtered ("0x%x ", ((unsigned char *)&state->ltc)[i]);
+ printf_filtered ("\n");
+
+ printf_filtered ("lmode: 0x%x\n", state->lmode);
+#endif
+}
+
+static int
+hardwire_flush_output (scb)
+ serial_t scb;
+{
+#ifdef HAVE_TERMIOS
+ return tcflush (scb->fd, TCOFLUSH);
+#endif
+
+#ifdef HAVE_TERMIO
+ return ioctl (scb->fd, TCFLSH, 1);
+#endif
+
+#ifdef HAVE_SGTTY
+ /* This flushes both input and output, but we can't do better. */
+ return ioctl (scb->fd, TIOCFLUSH, 0);
+#endif
+}
+
+static int
+hardwire_flush_input (scb)
+ serial_t scb;
+{
+#ifdef HAVE_TERMIOS
+ return tcflush (scb->fd, TCIFLUSH);
+#endif
+
+#ifdef HAVE_TERMIO
+ return ioctl (scb->fd, TCFLSH, 0);
+#endif
+
+#ifdef HAVE_SGTTY
+ /* This flushes both input and output, but we can't do better. */
+ return ioctl (scb->fd, TIOCFLUSH, 0);
+#endif
+}
+
+static int
+hardwire_send_break (scb)
+ serial_t scb;
+{
+#ifdef HAVE_TERMIOS
+ return tcsendbreak (scb->fd, 0);
+#endif
+
+#ifdef HAVE_TERMIO
+ return ioctl (scb->fd, TCSBRK, 0);
+#endif
+
+#ifdef HAVE_SGTTY
+ {
+ int status;
+ struct timeval timeout;
+
+ status = ioctl (scb->fd, TIOCSBRK, 0);
+
+ /* Can't use usleep; it doesn't exist in BSD 4.2. */
+ /* Note that if this select() is interrupted by a signal it will not wait
+ the full length of time. I think that is OK. */
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 250000;
+ select (0, 0, 0, 0, &timeout);
+ status = ioctl (scb->fd, TIOCCBRK, 0);
+ return status;
+ }
+#endif
+}
+
+static void
+hardwire_raw(scb)
+ serial_t scb;
+{
+ struct hardwire_ttystate state;
+
+ if (get_tty_state(scb, &state))
+ fprintf(stderr, "get_tty_state failed: %s\n", safe_strerror(errno));
+
+#ifdef HAVE_TERMIOS
+ state.termios.c_iflag = 0;
+ state.termios.c_oflag = 0;
+ state.termios.c_lflag = 0;
+ state.termios.c_cflag &= ~(CSIZE|PARENB);
+ state.termios.c_cflag |= CS8;
+ state.termios.c_cc[VMIN] = 0;
+ state.termios.c_cc[VTIME] = 0;
+#endif
+
+#ifdef HAVE_TERMIO
+ state.termio.c_iflag = 0;
+ state.termio.c_oflag = 0;
+ state.termio.c_lflag = 0;
+ state.termio.c_cflag &= ~(CSIZE|PARENB);
+ state.termio.c_cflag |= CS8;
+ state.termio.c_cc[VMIN] = 0;
+ state.termio.c_cc[VTIME] = 0;
+#endif
+
+#ifdef HAVE_SGTTY
+ state.sgttyb.sg_flags |= RAW | ANYP;
+ state.sgttyb.sg_flags &= ~(CBREAK | ECHO);
+#endif
+
+ scb->current_timeout = 0;
+
+ if (set_tty_state (scb, &state))
+ fprintf(stderr, "set_tty_state failed: %s\n", safe_strerror(errno));
+}
+
+/* Wait for input on scb, with timeout seconds. Returns 0 on success,
+ otherwise SERIAL_TIMEOUT or SERIAL_ERROR.
+
+ For termio{s}, we actually just setup VTIME if necessary, and let the
+ timeout occur in the read() in hardwire_read().
+ */
+
+static int
+wait_for(scb, timeout)
+ serial_t scb;
+ int timeout;
+{
+#ifdef HAVE_SGTTY
+ struct timeval tv;
+ fd_set readfds;
+
+ FD_ZERO (&readfds);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ FD_SET(scb->fd, &readfds);
+
+ while (1)
+ {
+ int numfds;
+
+ if (timeout >= 0)
+ numfds = select(scb->fd+1, &readfds, 0, 0, &tv);
+ else
+ numfds = select(scb->fd+1, &readfds, 0, 0, 0);
+
+ if (numfds <= 0)
+ if (numfds == 0)
+ return SERIAL_TIMEOUT;
+ else if (errno == EINTR)
+ continue;
+ else
+ return SERIAL_ERROR; /* Got an error from select or poll */
+
+ return 0;
+ }
+
+#endif /* HAVE_SGTTY */
+
+#if defined HAVE_TERMIO || defined HAVE_TERMIOS
+ if (timeout == scb->current_timeout)
+ return 0;
+
+ {
+ struct hardwire_ttystate state;
+
+ if (get_tty_state(scb, &state))
+ fprintf(stderr, "get_tty_state failed: %s\n", safe_strerror(errno));
+
+#ifdef HAVE_TERMIOS
+ state.termios.c_cc[VTIME] = timeout * 10;
+#endif
+
+#ifdef HAVE_TERMIO
+ state.termio.c_cc[VTIME] = timeout * 10;
+#endif
+
+ scb->current_timeout = timeout;
+
+ if (set_tty_state (scb, &state))
+ fprintf(stderr, "set_tty_state failed: %s\n", safe_strerror(errno));
+
+ return 0;
+ }
+#endif /* HAVE_TERMIO || HAVE_TERMIOS */
+}
+
+/* Read a character with user-specified timeout. TIMEOUT is number of seconds
+ to wait, or -1 to wait forever. Use timeout of 0 to effect a poll. Returns
+ char if successful. Returns SERIAL_TIMEOUT if timeout expired, EOF if line
+ dropped dead, or SERIAL_ERROR for any other error (see errno in that case). */
+
+static int
+hardwire_readchar(scb, timeout)
+ serial_t scb;
+ int timeout;
+{
+ int status;
+
+ if (scb->bufcnt-- > 0)
+ return *scb->bufp++;
+
+ status = wait_for(scb, timeout);
+
+ if (status < 0)
+ return status;
+
+ scb->bufcnt = read(scb->fd, scb->buf, BUFSIZ);
+
+ if (scb->bufcnt <= 0)
+ if (scb->bufcnt == 0)
+ return SERIAL_TIMEOUT; /* 0 chars means timeout [may need to
+ distinguish between EOF & timeouts
+ someday] */
+ else
+ return SERIAL_ERROR; /* Got an error from read */
+
+ scb->bufcnt--;
+ scb->bufp = scb->buf;
+ return *scb->bufp++;
+}
+
+#ifndef B19200
+#define B19200 EXTA
+#endif
+
+#ifndef B38400
+#define B38400 EXTB
+#endif
+
+/* Translate baud rates from integers to damn B_codes. Unix should
+ have outgrown this crap years ago, but even POSIX wouldn't buck it. */
+
+static struct
+{
+ int rate;
+ int code;
+}
+baudtab[] =
+{
+ {50, B50},
+ {75, B75},
+ {110, B110},
+ {134, B134},
+ {150, B150},
+ {200, B200},
+ {300, B300},
+ {600, B600},
+ {1200, B1200},
+ {1800, B1800},
+ {2400, B2400},
+ {4800, B4800},
+ {9600, B9600},
+ {19200, B19200},
+ {38400, B38400},
+ {-1, -1},
+};
+
+static int
+rate_to_code(rate)
+ int rate;
+{
+ int i;
+
+ for (i = 0; baudtab[i].rate != -1; i++)
+ if (rate == baudtab[i].rate)
+ return baudtab[i].code;
+
+ return -1;
+}
+
+static int
+hardwire_setbaudrate(scb, rate)
+ serial_t scb;
+ int rate;
+{
+ struct hardwire_ttystate state;
+
+ if (get_tty_state(scb, &state))
+ return -1;
+
+#ifdef HAVE_TERMIOS
+ cfsetospeed (&state.termios, rate_to_code (rate));
+ cfsetispeed (&state.termios, rate_to_code (rate));
+#endif
+
+#ifdef HAVE_TERMIO
+#ifndef CIBAUD
+#define CIBAUD CBAUD
+#endif
+
+ state.termio.c_cflag &= ~(CBAUD | CIBAUD);
+ state.termio.c_cflag |= rate_to_code (rate);
+#endif
+
+#ifdef HAVE_SGTTY
+ state.sgttyb.sg_ispeed = rate_to_code (rate);
+ state.sgttyb.sg_ospeed = rate_to_code (rate);
+#endif
+
+ return set_tty_state (scb, &state);
+}
+
+static int
+hardwire_write(scb, str, len)
+ serial_t scb;
+ const char *str;
+ int len;
+{
+ int cc;
+
+ while (len > 0)
+ {
+ cc = write(scb->fd, str, len);
+
+ if (cc < 0)
+ return 1;
+ len -= cc;
+ str += cc;
+ }
+ return 0;
+}
+
+static void
+hardwire_close(scb)
+ serial_t scb;
+{
+ if (scb->fd < 0)
+ return;
+
+ close(scb->fd);
+ scb->fd = -1;
+}
+
+static struct serial_ops hardwire_ops =
+{
+ "hardwire",
+ 0,
+ hardwire_open,
+ hardwire_close,
+ hardwire_readchar,
+ hardwire_write,
+ hardwire_flush_output,
+ hardwire_flush_input,
+ hardwire_send_break,
+ hardwire_raw,
+ hardwire_get_tty_state,
+ hardwire_set_tty_state,
+ hardwire_print_tty_state,
+ hardwire_noflush_set_tty_state,
+ hardwire_setbaudrate,
+};
+
+void
+_initialize_ser_hardwire ()
+{
+ serial_add_interface (&hardwire_ops);
+}
diff --git a/gnu/usr.bin/gdb/gdb/serial.c b/gnu/usr.bin/gdb/gdb/serial.c
new file mode 100644
index 0000000..6913fd6
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/serial.c
@@ -0,0 +1,261 @@
+/* Generic serial interface routines
+ Copyright 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "serial.h"
+
+/* Linked list of serial I/O handlers */
+
+static struct serial_ops *serial_ops_list = NULL;
+
+/* This is the last serial stream opened. Used by connect command. */
+
+static serial_t last_serial_opened = NULL;
+
+static struct serial_ops *
+serial_interface_lookup (name)
+ char *name;
+{
+ struct serial_ops *ops;
+
+ for (ops = serial_ops_list; ops; ops = ops->next)
+ if (strcmp (name, ops->name) == 0)
+ return ops;
+
+ return NULL;
+}
+
+void
+serial_add_interface(optable)
+ struct serial_ops *optable;
+{
+ optable->next = serial_ops_list;
+ serial_ops_list = optable;
+}
+
+/* Open up a device or a network socket, depending upon the syntax of NAME. */
+
+serial_t
+serial_open(name)
+ const char *name;
+{
+ serial_t scb;
+ struct serial_ops *ops;
+
+ if (strchr (name, ':'))
+ ops = serial_interface_lookup ("tcp");
+ else
+ ops = serial_interface_lookup ("hardwire");
+
+ if (!ops)
+ return NULL;
+
+ scb = (serial_t)xmalloc (sizeof (struct _serial_t));
+
+ scb->ops = ops;
+
+ scb->bufcnt = 0;
+ scb->bufp = scb->buf;
+
+ if (scb->ops->open(scb, name))
+ {
+ free (scb);
+ return NULL;
+ }
+
+ last_serial_opened = scb;
+
+ return scb;
+}
+
+serial_t
+serial_fdopen(fd)
+ const int fd;
+{
+ serial_t scb;
+ struct serial_ops *ops;
+
+ ops = serial_interface_lookup ("hardwire");
+
+ if (!ops)
+ return NULL;
+
+ scb = (serial_t)xmalloc (sizeof (struct _serial_t));
+
+ scb->ops = ops;
+
+ scb->bufcnt = 0;
+ scb->bufp = scb->buf;
+
+ scb->fd = fd;
+
+ last_serial_opened = scb;
+
+ return scb;
+}
+
+void
+serial_close(scb)
+ serial_t scb;
+{
+ last_serial_opened = NULL;
+
+/* This is bogus. It's not our fault if you pass us a bad scb...! Rob, you
+ should fix your code instead. */
+
+ if (!scb)
+ return;
+
+ scb->ops->close(scb);
+ free(scb);
+}
+
+#if 0
+/*
+The connect command is #if 0 because I hadn't thought of an elegant
+way to wait for I/O on two serial_t's simultaneously. Two solutions
+came to mind:
+
+ 1) Fork, and have have one fork handle the to user direction,
+ and have the other hand the to target direction. This
+ obviously won't cut it for MSDOS.
+
+ 2) Use something like select. This assumes that stdin and
+ the target side can both be waited on via the same
+ mechanism. This may not be true for DOS, if GDB is
+ talking to the target via a TCP socket.
+-grossman, 8 Jun 93
+*/
+
+/* Connect the user directly to the remote system. This command acts just like
+ the 'cu' or 'tip' command. Use <CR>~. or <CR>~^D to break out. */
+
+static serial_t tty_desc; /* Controlling terminal */
+
+static void
+cleanup_tty(ttystate)
+ serial_ttystate ttystate;
+{
+ printf ("\r\n[Exiting connect mode]\r\n");
+ SERIAL_SET_TTY_STATE (tty_desc, ttystate);
+ free (ttystate);
+ SERIAL_CLOSE (tty_desc);
+}
+
+static void
+connect_command (args, fromtty)
+ char *args;
+ int fromtty;
+{
+ int c;
+ char cur_esc = 0;
+ serial_ttystate ttystate;
+ serial_t port_desc; /* TTY port */
+
+ dont_repeat();
+
+ if (args)
+ fprintf(stderr, "This command takes no args. They have been ignored.\n");
+
+ printf("[Entering connect mode. Use ~. or ~^D to escape]\n");
+
+ tty_desc = SERIAL_FDOPEN (0);
+ port_desc = last_serial_opened;
+
+ ttystate = SERIAL_GET_TTY_STATE (tty_desc);
+
+ SERIAL_RAW (tty_desc);
+ SERIAL_RAW (port_desc);
+
+ make_cleanup (cleanup_tty, ttystate);
+
+ while (1)
+ {
+ int mask;
+
+ mask = SERIAL_WAIT_2 (tty_desc, port_desc, -1);
+
+ if (mask & 2)
+ { /* tty input */
+ char cx;
+
+ while (1)
+ {
+ c = SERIAL_READCHAR(tty_desc, 0);
+
+ if (c == SERIAL_TIMEOUT)
+ break;
+
+ if (c < 0)
+ perror_with_name("connect");
+
+ cx = c;
+ SERIAL_WRITE(port_desc, &cx, 1);
+
+ switch (cur_esc)
+ {
+ case 0:
+ if (c == '\r')
+ cur_esc = c;
+ break;
+ case '\r':
+ if (c == '~')
+ cur_esc = c;
+ else
+ cur_esc = 0;
+ break;
+ case '~':
+ if (c == '.' || c == '\004')
+ return;
+ else
+ cur_esc = 0;
+ }
+ }
+ }
+
+ if (mask & 1)
+ { /* Port input */
+ char cx;
+
+ while (1)
+ {
+ c = SERIAL_READCHAR(port_desc, 0);
+
+ if (c == SERIAL_TIMEOUT)
+ break;
+
+ if (c < 0)
+ perror_with_name("connect");
+
+ cx = c;
+
+ SERIAL_WRITE(tty_desc, &cx, 1);
+ }
+ }
+ }
+}
+
+void
+_initialize_serial ()
+{
+ add_com ("connect", class_obscure, connect_command,
+ "Connect the terminal directly up to the command monitor.\n\
+Use <CR>~. or <CR>~^D to break out.");
+}
+#endif /* 0 */
diff --git a/gnu/usr.bin/gdb/gdb/serial.h b/gnu/usr.bin/gdb/gdb/serial.h
new file mode 100644
index 0000000..7e7e530
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/serial.h
@@ -0,0 +1,153 @@
+/* Remote serial support interface definitions for GDB, the GNU Debugger.
+ Copyright 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef SERIAL_H
+#define SERIAL_H
+
+/* Terminal state pointer. This is specific to each type of interface. */
+
+typedef PTR serial_ttystate;
+
+struct _serial_t
+{
+ int fd; /* File descriptor */
+ struct serial_ops *ops; /* Function vector */
+ serial_ttystate ttystate; /* Not used (yet) */
+ int bufcnt; /* Amount of data in receive buffer */
+ unsigned char *bufp; /* Current byte */
+ unsigned char buf[BUFSIZ]; /* Da buffer itself */
+ int current_timeout; /* (termio{s} only), last value of VTIME */
+};
+
+typedef struct _serial_t *serial_t;
+
+struct serial_ops {
+ char *name;
+ struct serial_ops *next;
+ int (*open) PARAMS ((serial_t, const char *name));
+ void (*close) PARAMS ((serial_t));
+ int (*readchar) PARAMS ((serial_t, int timeout));
+ int (*write) PARAMS ((serial_t, const char *str, int len));
+ int (*flush_output) PARAMS ((serial_t));
+ int (*flush_input) PARAMS ((serial_t));
+ int (*send_break) PARAMS ((serial_t));
+ void (*go_raw) PARAMS ((serial_t));
+ serial_ttystate (*get_tty_state) PARAMS ((serial_t));
+ int (*set_tty_state) PARAMS ((serial_t, serial_ttystate));
+ void (*print_tty_state) PARAMS ((serial_t, serial_ttystate));
+ int (*noflush_set_tty_state)
+ PARAMS ((serial_t, serial_ttystate, serial_ttystate));
+ int (*setbaudrate) PARAMS ((serial_t, int rate));
+};
+
+/* Add a new serial interface to the interface list */
+
+void serial_add_interface PARAMS ((struct serial_ops *optable));
+
+serial_t serial_open PARAMS ((const char *name));
+
+serial_t serial_fdopen PARAMS ((int fd));
+
+/* For most routines, if a failure is indicated, then errno should be
+ examined. */
+
+/* Try to open NAME. Returns a new serial_t on success, NULL on failure.
+ */
+
+#define SERIAL_OPEN(NAME) serial_open(NAME)
+
+/* Open a new serial stream using a file handle. */
+
+#define SERIAL_FDOPEN(FD) serial_fdopen(FD)
+
+/* Flush pending output. Might also flush input (if this system can't flush
+ only output). */
+
+#define SERIAL_FLUSH_OUTPUT(SERIAL_T) \
+ ((SERIAL_T)->ops->flush_output((SERIAL_T)))
+
+/* Flush pending input. Might also flush output (if this system can't flush
+ only input). */
+
+#define SERIAL_FLUSH_INPUT(SERIAL_T)\
+ ((*(SERIAL_T)->ops->flush_input) ((SERIAL_T)))
+
+/* Send a break between 0.25 and 0.5 seconds long. */
+
+#define SERIAL_SEND_BREAK(SERIAL_T) \
+ ((*(SERIAL_T)->ops->send_break) (SERIAL_T))
+
+/* Turn the port into raw mode. */
+
+#define SERIAL_RAW(SERIAL_T) (SERIAL_T)->ops->go_raw((SERIAL_T))
+
+/* Return a pointer to a newly malloc'd ttystate containing the state
+ of the tty. */
+#define SERIAL_GET_TTY_STATE(SERIAL_T) (SERIAL_T)->ops->get_tty_state((SERIAL_T))
+
+/* Set the state of the tty to TTYSTATE. The change is immediate.
+ When changing to or from raw mode, input might be discarded. */
+#define SERIAL_SET_TTY_STATE(SERIAL_T, TTYSTATE) (SERIAL_T)->ops->set_tty_state((SERIAL_T), (TTYSTATE))
+
+/* printf_filtered a user-comprehensible description of ttystate. */
+#define SERIAL_PRINT_TTY_STATE(SERIAL_T, TTYSTATE) \
+ ((*((SERIAL_T)->ops->print_tty_state)) ((SERIAL_T), (TTYSTATE)))
+
+/* Set the tty state to NEW_TTYSTATE, where OLD_TTYSTATE is the
+ current state (generally obtained from a recent call to
+ SERIAL_GET_TTY_STATE), but be careful not to discard any input.
+ This means that we never switch in or out of raw mode, even
+ if NEW_TTYSTATE specifies a switch. */
+#define SERIAL_NOFLUSH_SET_TTY_STATE(SERIAL_T, NEW_TTYSTATE, OLD_TTYSTATE) \
+ ((*((SERIAL_T)->ops->noflush_set_tty_state)) \
+ ((SERIAL_T), (NEW_TTYSTATE), (OLD_TTYSTATE)))
+
+/* Read one char from the serial device with TIMEOUT seconds to wait
+ or -1 to wait forever. Use timeout of 0 to effect a poll. Returns
+ char if ok, else one of the following codes. Note that all error
+ codes are guaranteed to be < 0. */
+
+#define SERIAL_ERROR -1 /* General error, see errno for details */
+#define SERIAL_TIMEOUT -2
+#define SERIAL_EOF -3
+
+#define SERIAL_READCHAR(SERIAL_T, TIMEOUT) ((SERIAL_T)->ops->readchar((SERIAL_T), TIMEOUT))
+
+/* Set the baudrate to the decimal value supplied. Returns 0 for success,
+ -1 for failure. */
+
+#define SERIAL_SETBAUDRATE(SERIAL_T, RATE) ((SERIAL_T)->ops->setbaudrate((SERIAL_T), RATE))
+
+/* Write LEN chars from STRING to the port SERIAL_T. Returns 0 for
+ success, non-zero for failure. */
+
+#define SERIAL_WRITE(SERIAL_T, STRING, LEN) ((SERIAL_T)->ops->write((SERIAL_T), STRING, LEN))
+
+/* Push out all buffers, close the device and destroy SERIAL_T. */
+
+void serial_close PARAMS ((serial_t));
+
+#define SERIAL_CLOSE(SERIAL_T) serial_close(SERIAL_T)
+
+/* Destroy SERIAL_T without doing the rest of the stuff that SERIAL_CLOSE
+ does. */
+
+#define SERIAL_UN_FDOPEN(SERIAL_T) (free (SERIAL_T))
+
+#endif /* SERIAL_H */
diff --git a/gnu/usr.bin/gdb/gdb/signals.h b/gnu/usr.bin/gdb/gdb/signals.h
new file mode 100644
index 0000000..08fa606
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/signals.h
@@ -0,0 +1,27 @@
+/* Signal handler definitions for GDB, the GNU Debugger.
+ Copyright (C) 1986, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file is almost the same as including <signal.h> except that it
+ eliminates certain signal names when job control is not supported,
+ (or, on some systems, when job control is there but doesn't work
+ the way GDB expects it to work). */
+/* This has been superceded by the job_control variable in serial.h. */
+
+#include <signal.h>
diff --git a/gnu/usr.bin/gdb/gdb/solib.h b/gnu/usr.bin/gdb/gdb/solib.h
new file mode 100644
index 0000000..ddabf74
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/solib.h
@@ -0,0 +1,56 @@
+/* Shared library declarations for GDB, the GNU Debugger.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef __STDC__ /* Forward decl's for prototypes */
+struct target_ops;
+#endif
+
+/* Called when we free all symtabs, to free the shared library information
+ as well. */
+
+#define CLEAR_SOLIB clear_solib
+
+extern void
+clear_solib PARAMS ((void));
+
+/* Called to add symbols from a shared library to gdb's symbol table. */
+
+#define SOLIB_ADD(filename, from_tty, targ) \
+ solib_add (filename, from_tty, targ)
+
+extern void
+solib_add PARAMS ((char *, int, struct target_ops *));
+
+/* Function to be called when the inferior starts up, to discover the names
+ of shared libraries that are dynamically linked, the base addresses to
+ which they are linked, and sufficient information to read in their symbols
+ at a later time. */
+
+#define SOLIB_CREATE_INFERIOR_HOOK(PID) solib_create_inferior_hook()
+
+extern void
+solib_create_inferior_hook PARAMS((void)); /* solib.c */
+
+/* If we can't set a breakpoint, and it's in a shared library, just
+ disable it. */
+
+#define DISABLE_UNSETTABLE_BREAK(addr) solib_address(addr)
+
+extern int
+solib_address PARAMS ((CORE_ADDR)); /* solib.c */
diff --git a/gnu/usr.bin/gdb/gdb/source.c b/gnu/usr.bin/gdb/gdb/source.c
new file mode 100644
index 0000000..88781d7
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/source.c
@@ -0,0 +1,1398 @@
+/* List lines of source files for GDB, the GNU debugger.
+ Copyright (C) 1986, 1987, 1988, 1989, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "symtab.h"
+#include "expression.h"
+#include "language.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "frame.h"
+
+#ifdef USG
+#include <sys/types.h>
+#endif
+
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "gdbcore.h"
+#include "regex.h"
+#include "symfile.h"
+#include "objfiles.h"
+
+/* Prototypes for local functions. */
+
+static int
+open_source_file PARAMS ((struct symtab *));
+
+static int
+get_filename_and_charpos PARAMS ((struct symtab *, char **));
+
+static void
+reverse_search_command PARAMS ((char *, int));
+
+static void
+forward_search_command PARAMS ((char *, int));
+
+static void
+line_info PARAMS ((char *, int));
+
+static void
+list_command PARAMS ((char *, int));
+
+static void
+ambiguous_line_spec PARAMS ((struct symtabs_and_lines *));
+
+static void
+source_info PARAMS ((char *, int));
+
+static void
+show_directories PARAMS ((char *, int));
+
+static void
+find_source_lines PARAMS ((struct symtab *, int));
+
+/* If we use this declaration, it breaks because of fucking ANSI "const" stuff
+ on some systems. We just have to not declare it at all, have it default
+ to int, and possibly botch on a few systems. Thanks, ANSIholes... */
+/* extern char *strstr(); */
+
+/* Path of directories to search for source files.
+ Same format as the PATH environment variable's value. */
+
+char *source_path;
+
+/* Symtab of default file for listing lines of. */
+
+struct symtab *current_source_symtab;
+
+/* Default next line to list. */
+
+int current_source_line;
+
+/* Default number of lines to print with commands like "list".
+ This is based on guessing how many long (i.e. more than chars_per_line
+ characters) lines there will be. To be completely correct, "list"
+ and friends should be rewritten to count characters and see where
+ things are wrapping, but that would be a fair amount of work. */
+
+int lines_to_list = 10;
+
+/* Line number of last line printed. Default for various commands.
+ current_source_line is usually, but not always, the same as this. */
+
+static int last_line_listed;
+
+/* First line number listed by last listing command. */
+
+static int first_line_listed;
+
+
+/* Set the source file default for the "list" command to be S.
+
+ If S is NULL, and we don't have a default, find one. This
+ should only be called when the user actually tries to use the
+ default, since we produce an error if we can't find a reasonable
+ default. Also, since this can cause symbols to be read, doing it
+ before we need to would make things slower than necessary. */
+
+void
+select_source_symtab (s)
+ register struct symtab *s;
+{
+ struct symtabs_and_lines sals;
+ struct symtab_and_line sal;
+ struct partial_symtab *ps;
+ struct partial_symtab *cs_pst = 0;
+ struct objfile *ofp;
+
+ if (s)
+ {
+ current_source_symtab = s;
+ current_source_line = 1;
+ return;
+ }
+
+ if (current_source_symtab)
+ return;
+
+ /* Make the default place to list be the function `main'
+ if one exists. */
+ if (lookup_symbol ("main", 0, VAR_NAMESPACE, 0, NULL))
+ {
+ sals = decode_line_spec ("main", 1);
+ sal = sals.sals[0];
+ free (sals.sals);
+ current_source_symtab = sal.symtab;
+ current_source_line = max (sal.line - (lines_to_list - 1), 1);
+ if (current_source_symtab)
+ return;
+ }
+
+ /* All right; find the last file in the symtab list (ignoring .h's). */
+
+ current_source_line = 1;
+
+ for (ofp = object_files; ofp != NULL; ofp = ofp -> next)
+ {
+ for (s = ofp -> symtabs; s; s = s->next)
+ {
+ char *name = s -> filename;
+ int len = strlen (name);
+ if (! (len > 2 && (STREQ (&name[len - 2], ".h"))))
+ {
+ current_source_symtab = s;
+ }
+ }
+ }
+ if (current_source_symtab)
+ return;
+
+ /* Howabout the partial symbol tables? */
+
+ for (ofp = object_files; ofp != NULL; ofp = ofp -> next)
+ {
+ for (ps = ofp -> psymtabs; ps != NULL; ps = ps -> next)
+ {
+ char *name = ps -> filename;
+ int len = strlen (name);
+ if (! (len > 2 && (STREQ (&name[len - 2], ".h"))))
+ {
+ cs_pst = ps;
+ }
+ }
+ }
+ if (cs_pst)
+ {
+ if (cs_pst -> readin)
+ {
+ fatal ("Internal: select_source_symtab: readin pst found and no symtabs.");
+ }
+ else
+ {
+ current_source_symtab = PSYMTAB_TO_SYMTAB (cs_pst);
+ }
+ }
+
+ error ("Can't find a default source file");
+}
+
+static void
+show_directories (ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ puts_filtered ("Source directories searched: ");
+ puts_filtered (source_path);
+ puts_filtered ("\n");
+}
+
+/* Forget what we learned about line positions in source files,
+ and which directories contain them;
+ must check again now since files may be found in
+ a different directory now. */
+
+void
+forget_cached_source_info ()
+{
+ register struct symtab *s;
+ register struct objfile *objfile;
+
+ for (objfile = object_files; objfile != NULL; objfile = objfile -> next)
+ {
+ for (s = objfile -> symtabs; s != NULL; s = s -> next)
+ {
+ if (s -> line_charpos != NULL)
+ {
+ mfree (objfile -> md, s -> line_charpos);
+ s -> line_charpos = NULL;
+ }
+ if (s -> fullname != NULL)
+ {
+ mfree (objfile -> md, s -> fullname);
+ s -> fullname = NULL;
+ }
+ }
+ }
+}
+
+void
+init_source_path ()
+{
+ source_path = savestring ("$cdir:$cwd", /* strlen of it */ 10);
+ forget_cached_source_info ();
+}
+
+/* Add zero or more directories to the front of the source path. */
+
+void
+directory_command (dirname, from_tty)
+ char *dirname;
+ int from_tty;
+{
+ dont_repeat ();
+ /* FIXME, this goes to "delete dir"... */
+ if (dirname == 0)
+ {
+ if (query ("Reinitialize source path to empty? ", ""))
+ {
+ free (source_path);
+ init_source_path ();
+ }
+ }
+ else
+ mod_path (dirname, &source_path);
+ if (from_tty)
+ show_directories ((char *)0, from_tty);
+ forget_cached_source_info ();
+}
+
+/* Add zero or more directories to the front of an arbitrary path. */
+
+void
+mod_path (dirname, which_path)
+ char *dirname;
+ char **which_path;
+{
+ char *old = *which_path;
+ int prefix = 0;
+
+ if (dirname == 0)
+ return;
+
+ dirname = strsave (dirname);
+ make_cleanup (free, dirname);
+
+ do
+ {
+ char *name = dirname;
+ register char *p;
+ struct stat st;
+
+ {
+ char *colon = strchr (name, ':');
+ char *space = strchr (name, ' ');
+ char *tab = strchr (name, '\t');
+ if (colon == 0 && space == 0 && tab == 0)
+ p = dirname = name + strlen (name);
+ else
+ {
+ p = 0;
+ if (colon != 0 && (p == 0 || colon < p))
+ p = colon;
+ if (space != 0 && (p == 0 || space < p))
+ p = space;
+ if (tab != 0 && (p == 0 || tab < p))
+ p = tab;
+ dirname = p + 1;
+ while (*dirname == ':' || *dirname == ' ' || *dirname == '\t')
+ ++dirname;
+ }
+ }
+
+ if (p[-1] == '/')
+ /* Sigh. "foo/" => "foo" */
+ --p;
+ *p = '\0';
+
+ while (p[-1] == '.')
+ {
+ if (p - name == 1)
+ {
+ /* "." => getwd (). */
+ name = current_directory;
+ goto append;
+ }
+ else if (p[-2] == '/')
+ {
+ if (p - name == 2)
+ {
+ /* "/." => "/". */
+ *--p = '\0';
+ goto append;
+ }
+ else
+ {
+ /* "...foo/." => "...foo". */
+ p -= 2;
+ *p = '\0';
+ continue;
+ }
+ }
+ else
+ break;
+ }
+
+ if (name[0] == '~')
+ name = tilde_expand (name);
+ else if (name[0] != '/' && name[0] != '$')
+ name = concat (current_directory, "/", name, NULL);
+ else
+ name = savestring (name, p - name);
+ make_cleanup (free, name);
+
+ /* Unless it's a variable, check existence. */
+ if (name[0] != '$') {
+ /* These are warnings, not errors, since we don't want a
+ non-existent directory in a .gdbinit file to stop processing
+ of the .gdbinit file.
+
+ Whether they get added to the path is more debatable. Current
+ answer is yes, in case the user wants to go make the directory
+ or whatever. If the directory continues to not exist/not be
+ a directory/etc, then having them in the path should be
+ harmless. */
+ if (stat (name, &st) < 0)
+ {
+ int save_errno = errno;
+ fprintf (stderr, "Warning: ");
+ print_sys_errmsg (name, save_errno);
+ }
+ else if ((st.st_mode & S_IFMT) != S_IFDIR)
+ warning ("%s is not a directory.", name);
+ }
+
+ append:
+ {
+ register unsigned int len = strlen (name);
+
+ p = *which_path;
+ while (1)
+ {
+ if (!strncmp (p, name, len)
+ && (p[len] == '\0' || p[len] == ':'))
+ {
+ /* Found it in the search path, remove old copy */
+ if (p > *which_path)
+ p--; /* Back over leading colon */
+ if (prefix > p - *which_path)
+ goto skip_dup; /* Same dir twice in one cmd */
+ strcpy (p, &p[len+1]); /* Copy from next \0 or : */
+ }
+ p = strchr (p, ':');
+ if (p != 0)
+ ++p;
+ else
+ break;
+ }
+ if (p == 0)
+ {
+ /* If we have already tacked on a name(s) in this command, be sure they stay on the front as we tack on some more. */
+ if (prefix)
+ {
+ char *temp, c;
+
+ c = old[prefix];
+ old[prefix] = '\0';
+ temp = concat (old, ":", name, NULL);
+ old[prefix] = c;
+ *which_path = concat (temp, "", &old[prefix], NULL);
+ prefix = strlen (temp);
+ free (temp);
+ }
+ else
+ {
+ *which_path = concat (name, (old[0]? ":" : old), old, NULL);
+ prefix = strlen (name);
+ }
+ free (old);
+ old = *which_path;
+ }
+ }
+ skip_dup: ;
+ } while (*dirname != '\0');
+}
+
+
+static void
+source_info (ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ register struct symtab *s = current_source_symtab;
+
+ if (!s)
+ {
+ printf_filtered("No current source file.\n");
+ return;
+ }
+ printf_filtered ("Current source file is %s\n", s->filename);
+ if (s->dirname)
+ printf_filtered ("Compilation directory is %s\n", s->dirname);
+ if (s->fullname)
+ printf_filtered ("Located in %s\n", s->fullname);
+ if (s->nlines)
+ printf_filtered ("Contains %d line%s.\n", s->nlines,
+ s->nlines == 1 ? "" : "s");
+
+ printf_filtered("Source language is %s.\n", language_str (s->language));
+}
+
+
+
+/* Open a file named STRING, searching path PATH (dir names sep by colons)
+ using mode MODE and protection bits PROT in the calls to open.
+
+ If TRY_CWD_FIRST, try to open ./STRING before searching PATH.
+ (ie pretend the first element of PATH is "."). This also indicates
+ that a slash in STRING disables searching of the path (this is
+ so that "exec-file ./foo" or "symbol-file ./foo" insures that you
+ get that particular version of foo or an error message).
+
+ If FILENAMED_OPENED is non-null, set it to a newly allocated string naming
+ the actual file opened (this string will always start with a "/". We
+ have to take special pains to avoid doubling the "/" between the directory
+ and the file, sigh! Emacs gets confuzzed by this when we print the
+ source file name!!!
+
+ If a file is found, return the descriptor.
+ Otherwise, return -1, with errno set for the last name we tried to open. */
+
+/* >>>> This should only allow files of certain types,
+ >>>> eg executable, non-directory */
+int
+openp (path, try_cwd_first, string, mode, prot, filename_opened)
+ char *path;
+ int try_cwd_first;
+ char *string;
+ int mode;
+ int prot;
+ char **filename_opened;
+{
+ register int fd;
+ register char *filename;
+ register char *p, *p1;
+ register int len;
+ int alloclen;
+
+ if (!path)
+ path = ".";
+
+ if (try_cwd_first || string[0] == '/')
+ {
+ filename = string;
+ fd = open (filename, mode, prot);
+ if (fd >= 0 || string[0] == '/' || strchr (string, '/'))
+ goto done;
+ }
+
+ /* ./foo => foo */
+ while (string[0] == '.' && string[1] == '/')
+ string += 2;
+
+ alloclen = strlen (path) + strlen (string) + 2;
+ filename = (char *) alloca (alloclen);
+ fd = -1;
+ for (p = path; p; p = p1 ? p1 + 1 : 0)
+ {
+ p1 = (char *) strchr (p, ':');
+ if (p1)
+ len = p1 - p;
+ else
+ len = strlen (p);
+
+ if (len == 4 && p[0] == '$' && p[1] == 'c'
+ && p[2] == 'w' && p[3] == 'd') {
+ /* Name is $cwd -- insert current directory name instead. */
+ int newlen;
+
+ /* First, realloc the filename buffer if too short. */
+ len = strlen (current_directory);
+ newlen = len + strlen (string) + 2;
+ if (newlen > alloclen) {
+ alloclen = newlen;
+ filename = (char *) alloca (alloclen);
+ }
+ strcpy (filename, current_directory);
+ } else {
+ /* Normal file name in path -- just use it. */
+ strncpy (filename, p, len);
+ filename[len] = 0;
+ }
+
+ /* Remove trailing slashes */
+ while (len > 0 && filename[len-1] == '/')
+ filename[--len] = 0;
+
+ strcat (filename+len, "/");
+ strcat (filename, string);
+
+ fd = open (filename, mode, prot);
+ if (fd >= 0) break;
+ }
+
+ done:
+ if (filename_opened)
+ if (fd < 0)
+ *filename_opened = (char *) 0;
+ else if (filename[0] == '/')
+ *filename_opened = savestring (filename, strlen (filename));
+ else
+ {
+ /* Beware the // my son, the Emacs barfs, the botch that catch... */
+
+ *filename_opened = concat (current_directory,
+ '/' == current_directory[strlen(current_directory)-1]? "": "/",
+ filename, NULL);
+ }
+
+ return fd;
+}
+
+/* Open a source file given a symtab S. Returns a file descriptor
+ or negative number for error. */
+
+static int
+open_source_file (s)
+ struct symtab *s;
+{
+ char *path = source_path;
+ char *p;
+ int result;
+ char *fullname;
+
+ /* Quick way out if we already know its full name */
+ if (s->fullname)
+ {
+ result = open (s->fullname, O_RDONLY);
+ if (result >= 0)
+ return result;
+ /* Didn't work -- free old one, try again. */
+ mfree (s->objfile->md, s->fullname);
+ s->fullname = NULL;
+ }
+
+ if (s->dirname != NULL)
+ {
+ /* Replace a path entry of $cdir with the compilation directory name */
+#define cdir_len 5
+ /* We cast strstr's result in case an ANSIhole has made it const,
+ which produces a "required warning" when assigned to a nonconst. */
+ p = (char *)strstr (source_path, "$cdir");
+ if (p && (p == path || p[-1] == ':')
+ && (p[cdir_len] == ':' || p[cdir_len] == '\0')) {
+ int len;
+
+ path = (char *)
+ alloca (strlen (source_path) + 1 + strlen (s->dirname) + 1);
+ len = p - source_path;
+ strncpy (path, source_path, len); /* Before $cdir */
+ strcpy (path + len, s->dirname); /* new stuff */
+ strcat (path + len, source_path + len + cdir_len); /* After $cdir */
+ }
+ }
+
+ result = openp (path, 0, s->filename, O_RDONLY, 0, &s->fullname);
+ if (result < 0)
+ {
+ /* Didn't work. Try using just the basename. */
+ p = basename (s->filename);
+ if (p != s->filename)
+ result = openp(path, 0, p, O_RDONLY,0, &s->fullname);
+ }
+ if (result >= 0)
+ {
+ fullname = s -> fullname;
+ s -> fullname = mstrsave (s -> objfile -> md, s -> fullname);
+ free (fullname);
+ }
+ return result;
+}
+
+
+/* Create and initialize the table S->line_charpos that records
+ the positions of the lines in the source file, which is assumed
+ to be open on descriptor DESC.
+ All set S->nlines to the number of such lines. */
+
+static void
+find_source_lines (s, desc)
+ struct symtab *s;
+ int desc;
+{
+ struct stat st;
+ register char *data, *p, *end;
+ int nlines = 0;
+ int lines_allocated = 1000;
+ int *line_charpos;
+ long exec_mtime;
+ int size;
+#ifdef LSEEK_NOT_LINEAR
+ char c;
+#endif
+
+ line_charpos = (int *) xmmalloc (s -> objfile -> md,
+ lines_allocated * sizeof (int));
+ if (fstat (desc, &st) < 0)
+ perror_with_name (s->filename);
+
+ if (exec_bfd) {
+ exec_mtime = bfd_get_mtime(exec_bfd);
+ if (exec_mtime && exec_mtime < st.st_mtime)
+ printf_filtered ("Source file is more recent than executable.\n");
+ }
+
+#ifdef LSEEK_NOT_LINEAR
+ /* Have to read it byte by byte to find out where the chars live */
+
+ line_charpos[0] = tell(desc);
+ nlines = 1;
+ while (myread(desc, &c, 1)>0)
+ {
+ if (c == '\n')
+ {
+ if (nlines == lines_allocated)
+ {
+ lines_allocated *= 2;
+ line_charpos =
+ (int *) xmrealloc (s -> objfile -> md, (char *) line_charpos,
+ sizeof (int) * lines_allocated);
+ }
+ line_charpos[nlines++] = tell(desc);
+ }
+ }
+
+#else
+ /* st_size might be a large type, but we only support source files whose
+ size fits in an int. FIXME. */
+ size = (int) st.st_size;
+
+#ifdef BROKEN_LARGE_ALLOCA
+ data = (char *) xmalloc (size);
+ make_cleanup (free, data);
+#else
+ data = (char *) alloca (size);
+#endif
+ if (myread (desc, data, size) < 0)
+ perror_with_name (s->filename);
+ end = data + size;
+ p = data;
+ line_charpos[0] = 0;
+ nlines = 1;
+ while (p != end)
+ {
+ if (*p++ == '\n'
+ /* A newline at the end does not start a new line. */
+ && p != end)
+ {
+ if (nlines == lines_allocated)
+ {
+ lines_allocated *= 2;
+ line_charpos =
+ (int *) xmrealloc (s -> objfile -> md, (char *) line_charpos,
+ sizeof (int) * lines_allocated);
+ }
+ line_charpos[nlines++] = p - data;
+ }
+ }
+#endif
+ s->nlines = nlines;
+ s->line_charpos =
+ (int *) xmrealloc (s -> objfile -> md, (char *) line_charpos,
+ nlines * sizeof (int));
+
+}
+
+/* Return the character position of a line LINE in symtab S.
+ Return 0 if anything is invalid. */
+
+#if 0 /* Currently unused */
+
+int
+source_line_charpos (s, line)
+ struct symtab *s;
+ int line;
+{
+ if (!s) return 0;
+ if (!s->line_charpos || line <= 0) return 0;
+ if (line > s->nlines)
+ line = s->nlines;
+ return s->line_charpos[line - 1];
+}
+
+/* Return the line number of character position POS in symtab S. */
+
+int
+source_charpos_line (s, chr)
+ register struct symtab *s;
+ register int chr;
+{
+ register int line = 0;
+ register int *lnp;
+
+ if (s == 0 || s->line_charpos == 0) return 0;
+ lnp = s->line_charpos;
+ /* Files are usually short, so sequential search is Ok */
+ while (line < s->nlines && *lnp <= chr)
+ {
+ line++;
+ lnp++;
+ }
+ if (line >= s->nlines)
+ line = s->nlines;
+ return line;
+}
+
+#endif /* 0 */
+
+
+/* Get full pathname and line number positions for a symtab.
+ Return nonzero if line numbers may have changed.
+ Set *FULLNAME to actual name of the file as found by `openp',
+ or to 0 if the file is not found. */
+
+static int
+get_filename_and_charpos (s, fullname)
+ struct symtab *s;
+ char **fullname;
+{
+ register int desc, linenums_changed = 0;
+
+ desc = open_source_file (s);
+ if (desc < 0)
+ {
+ if (fullname)
+ *fullname = NULL;
+ return 0;
+ }
+ if (fullname)
+ *fullname = s->fullname;
+ if (s->line_charpos == 0) linenums_changed = 1;
+ if (linenums_changed) find_source_lines (s, desc);
+ close (desc);
+ return linenums_changed;
+}
+
+/* Print text describing the full name of the source file S
+ and the line number LINE and its corresponding character position.
+ The text starts with two Ctrl-z so that the Emacs-GDB interface
+ can easily find it.
+
+ MID_STATEMENT is nonzero if the PC is not at the beginning of that line.
+
+ Return 1 if successful, 0 if could not find the file. */
+
+int
+identify_source_line (s, line, mid_statement, pc)
+ struct symtab *s;
+ int line;
+ int mid_statement;
+ CORE_ADDR pc;
+{
+ if (s->line_charpos == 0)
+ get_filename_and_charpos (s, (char **)NULL);
+ if (s->fullname == 0)
+ return 0;
+ if (line > s->nlines)
+ /* Don't index off the end of the line_charpos array. */
+ return 0;
+ printf ("\032\032%s:%d:%d:%s:0x%lx\n", s->fullname,
+ line, s->line_charpos[line - 1],
+ mid_statement ? "middle" : "beg",
+ (unsigned long) pc);
+ current_source_line = line;
+ first_line_listed = line;
+ last_line_listed = line;
+ current_source_symtab = s;
+ return 1;
+}
+
+/* Print source lines from the file of symtab S,
+ starting with line number LINE and stopping before line number STOPLINE. */
+
+void
+print_source_lines (s, line, stopline, noerror)
+ struct symtab *s;
+ int line, stopline;
+ int noerror;
+{
+ register int c;
+ register int desc;
+ register FILE *stream;
+ int nlines = stopline - line;
+
+ /* Regardless of whether we can open the file, set current_source_symtab. */
+ current_source_symtab = s;
+ current_source_line = line;
+ first_line_listed = line;
+
+ desc = open_source_file (s);
+ if (desc < 0)
+ {
+ if (! noerror) {
+ char *name = alloca (strlen (s->filename) + 100);
+ sprintf (name, "%s:%d", s->filename, line);
+ print_sys_errmsg (name, errno);
+ }
+ return;
+ }
+
+ if (s->line_charpos == 0)
+ find_source_lines (s, desc);
+
+ if (line < 1 || line > s->nlines)
+ {
+ close (desc);
+ error ("Line number %d out of range; %s has %d lines.",
+ line, s->filename, s->nlines);
+ }
+
+ if (lseek (desc, s->line_charpos[line - 1], 0) < 0)
+ {
+ close (desc);
+ perror_with_name (s->filename);
+ }
+
+ stream = fdopen (desc, FOPEN_RT);
+ clearerr (stream);
+
+ while (nlines-- > 0)
+ {
+ c = fgetc (stream);
+ if (c == EOF) break;
+ last_line_listed = current_source_line;
+ printf_filtered ("%d\t", current_source_line++);
+ do
+ {
+ if (c < 040 && c != '\t' && c != '\n' && c != '\r')
+ printf_filtered ("^%c", c + 0100);
+ else if (c == 0177)
+ printf_filtered ("^?");
+ else
+ printf_filtered ("%c", c);
+ } while (c != '\n' && (c = fgetc (stream)) >= 0);
+ }
+
+ fclose (stream);
+}
+
+
+
+/*
+ C++
+ Print a list of files and line numbers which a user may choose from
+ in order to list a function which was specified ambiguously
+ (as with `list classname::overloadedfuncname', for example).
+ The vector in SALS provides the filenames and line numbers.
+ */
+static void
+ambiguous_line_spec (sals)
+ struct symtabs_and_lines *sals;
+{
+ int i;
+
+ for (i = 0; i < sals->nelts; ++i)
+ printf_filtered("file: \"%s\", line number: %d\n",
+ sals->sals[i].symtab->filename, sals->sals[i].line);
+}
+
+
+static void
+list_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ struct symtabs_and_lines sals, sals_end;
+ struct symtab_and_line sal, sal_end;
+ struct symbol *sym;
+ char *arg1;
+ int no_end = 1;
+ int dummy_end = 0;
+ int dummy_beg = 0;
+ int linenum_beg = 0;
+ char *p;
+
+ if (!have_full_symbols () && !have_partial_symbols())
+ error ("No symbol table is loaded. Use the \"file\" command.");
+
+ /* Pull in a current source symtab if necessary */
+ if (current_source_symtab == 0 &&
+ (arg == 0 || arg[0] == '+' || arg[0] == '-'))
+ select_source_symtab (0);
+
+ /* "l" or "l +" lists next ten lines. */
+
+ if (arg == 0 || STREQ (arg, "+"))
+ {
+ if (current_source_symtab == 0)
+ error ("No default source file yet. Do \"help list\".");
+ print_source_lines (current_source_symtab, current_source_line,
+ current_source_line + lines_to_list, 0);
+ return;
+ }
+
+ /* "l -" lists previous ten lines, the ones before the ten just listed. */
+ if (STREQ (arg, "-"))
+ {
+ if (current_source_symtab == 0)
+ error ("No default source file yet. Do \"help list\".");
+ print_source_lines (current_source_symtab,
+ max (first_line_listed - lines_to_list, 1),
+ first_line_listed, 0);
+ return;
+ }
+
+ /* Now if there is only one argument, decode it in SAL
+ and set NO_END.
+ If there are two arguments, decode them in SAL and SAL_END
+ and clear NO_END; however, if one of the arguments is blank,
+ set DUMMY_BEG or DUMMY_END to record that fact. */
+
+ arg1 = arg;
+ if (*arg1 == ',')
+ dummy_beg = 1;
+ else
+ {
+ sals = decode_line_1 (&arg1, 0, 0, 0, 0);
+
+ if (! sals.nelts) return; /* C++ */
+ if (sals.nelts > 1)
+ {
+ ambiguous_line_spec (&sals);
+ free (sals.sals);
+ return;
+ }
+
+ sal = sals.sals[0];
+ free (sals.sals);
+ }
+
+ /* Record whether the BEG arg is all digits. */
+
+ for (p = arg; p != arg1 && *p >= '0' && *p <= '9'; p++);
+ linenum_beg = (p == arg1);
+
+ while (*arg1 == ' ' || *arg1 == '\t')
+ arg1++;
+ if (*arg1 == ',')
+ {
+ no_end = 0;
+ arg1++;
+ while (*arg1 == ' ' || *arg1 == '\t')
+ arg1++;
+ if (*arg1 == 0)
+ dummy_end = 1;
+ else
+ {
+ if (dummy_beg)
+ sals_end = decode_line_1 (&arg1, 0, 0, 0, 0);
+ else
+ sals_end = decode_line_1 (&arg1, 0, sal.symtab, sal.line, 0);
+ if (sals_end.nelts == 0)
+ return;
+ if (sals_end.nelts > 1)
+ {
+ ambiguous_line_spec (&sals_end);
+ free (sals_end.sals);
+ return;
+ }
+ sal_end = sals_end.sals[0];
+ free (sals_end.sals);
+ }
+ }
+
+ if (*arg1)
+ error ("Junk at end of line specification.");
+
+ if (!no_end && !dummy_beg && !dummy_end
+ && sal.symtab != sal_end.symtab)
+ error ("Specified start and end are in different files.");
+ if (dummy_beg && dummy_end)
+ error ("Two empty args do not say what lines to list.");
+
+ /* if line was specified by address,
+ first print exactly which line, and which file.
+ In this case, sal.symtab == 0 means address is outside
+ of all known source files, not that user failed to give a filename. */
+ if (*arg == '*')
+ {
+ if (sal.symtab == 0)
+ error ("No source file for address %s.",
+ local_hex_string((unsigned long) sal.pc));
+ sym = find_pc_function (sal.pc);
+ if (sym)
+ {
+ printf_filtered ("%s is in ",
+ local_hex_string((unsigned long) sal.pc));
+ fputs_filtered (SYMBOL_SOURCE_NAME (sym), stdout);
+ printf_filtered (" (%s:%d).\n", sal.symtab->filename, sal.line);
+ }
+ else
+ printf_filtered ("%s is at %s:%d.\n",
+ local_hex_string((unsigned long) sal.pc),
+ sal.symtab->filename, sal.line);
+ }
+
+ /* If line was not specified by just a line number,
+ and it does not imply a symtab, it must be an undebuggable symbol
+ which means no source code. */
+
+ if (! linenum_beg && sal.symtab == 0)
+ error ("No line number known for %s.", arg);
+
+ /* If this command is repeated with RET,
+ turn it into the no-arg variant. */
+
+ if (from_tty)
+ *arg = 0;
+
+ if (dummy_beg && sal_end.symtab == 0)
+ error ("No default source file yet. Do \"help list\".");
+ if (dummy_beg)
+ print_source_lines (sal_end.symtab,
+ max (sal_end.line - (lines_to_list - 1), 1),
+ sal_end.line + 1, 0);
+ else if (sal.symtab == 0)
+ error ("No default source file yet. Do \"help list\".");
+ else if (no_end)
+ print_source_lines (sal.symtab,
+ max (sal.line - (lines_to_list / 2), 1),
+ sal.line + (lines_to_list / 2), 0);
+ else
+ print_source_lines (sal.symtab, sal.line,
+ (dummy_end
+ ? sal.line + lines_to_list
+ : sal_end.line + 1),
+ 0);
+}
+
+/* Print info on range of pc's in a specified line. */
+
+static void
+line_info (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ struct symtabs_and_lines sals;
+ struct symtab_and_line sal;
+ CORE_ADDR start_pc, end_pc;
+ int i;
+
+ if (arg == 0)
+ {
+ sal.symtab = current_source_symtab;
+ sal.line = last_line_listed;
+ sals.nelts = 1;
+ sals.sals = (struct symtab_and_line *)
+ xmalloc (sizeof (struct symtab_and_line));
+ sals.sals[0] = sal;
+ }
+ else
+ {
+ sals = decode_line_spec_1 (arg, 0);
+
+ dont_repeat ();
+ }
+
+ /* C++ More than one line may have been specified, as when the user
+ specifies an overloaded function name. Print info on them all. */
+ for (i = 0; i < sals.nelts; i++)
+ {
+ sal = sals.sals[i];
+
+ if (sal.symtab == 0)
+ {
+ printf_filtered ("No line number information available");
+ if (sal.pc != 0)
+ {
+ /* This is useful for "info line *0x7f34". If we can't tell the
+ user about a source line, at least let them have the symbolic
+ address. */
+ printf_filtered (" for address ");
+ wrap_here (" ");
+ print_address (sal.pc, stdout);
+ }
+ else
+ printf_filtered (".");
+ printf_filtered ("\n");
+ }
+ else if (sal.line > 0
+ && find_line_pc_range (sal.symtab, sal.line, &start_pc, &end_pc))
+ {
+ if (start_pc == end_pc)
+ {
+ printf_filtered ("Line %d of \"%s\"",
+ sal.line, sal.symtab->filename);
+ wrap_here (" ");
+ printf_filtered (" is at address ");
+ print_address (start_pc, stdout);
+ wrap_here (" ");
+ printf_filtered (" but contains no code.\n");
+ }
+ else
+ {
+ printf_filtered ("Line %d of \"%s\"",
+ sal.line, sal.symtab->filename);
+ wrap_here (" ");
+ printf_filtered (" starts at address ");
+ print_address (start_pc, stdout);
+ wrap_here (" ");
+ printf_filtered (" and ends at ");
+ print_address (end_pc, stdout);
+ printf_filtered (".\n");
+ }
+
+ /* x/i should display this line's code. */
+ set_next_address (start_pc);
+
+ /* Repeating "info line" should do the following line. */
+ last_line_listed = sal.line + 1;
+
+ /* If this is the only line, show the source code. If it could
+ not find the file, don't do anything special. */
+ if (frame_file_full_name && sals.nelts == 1)
+ identify_source_line (sal.symtab, sal.line, 0, start_pc);
+ }
+ else
+ /* Is there any case in which we get here, and have an address
+ which the user would want to see? If we have debugging symbols
+ and no line numbers? */
+ printf_filtered ("Line number %d is out of range for \"%s\".\n",
+ sal.line, sal.symtab->filename);
+ }
+ free (sals.sals);
+}
+
+/* Commands to search the source file for a regexp. */
+
+/* ARGSUSED */
+static void
+forward_search_command (regex, from_tty)
+ char *regex;
+ int from_tty;
+{
+ register int c;
+ register int desc;
+ register FILE *stream;
+ int line = last_line_listed + 1;
+ char *msg;
+
+ msg = (char *) re_comp (regex);
+ if (msg)
+ error (msg);
+
+ if (current_source_symtab == 0)
+ select_source_symtab (0);
+
+ /* Search from last_line_listed+1 in current_source_symtab */
+
+ desc = open_source_file (current_source_symtab);
+ if (desc < 0)
+ perror_with_name (current_source_symtab->filename);
+
+ if (current_source_symtab->line_charpos == 0)
+ find_source_lines (current_source_symtab, desc);
+
+ if (line < 1 || line > current_source_symtab->nlines)
+ {
+ close (desc);
+ error ("Expression not found");
+ }
+
+ if (lseek (desc, current_source_symtab->line_charpos[line - 1], 0) < 0)
+ {
+ close (desc);
+ perror_with_name (current_source_symtab->filename);
+ }
+
+ stream = fdopen (desc, FOPEN_RT);
+ clearerr (stream);
+ while (1) {
+/* FIXME!!! We walk right off the end of buf if we get a long line!!! */
+ char buf[4096]; /* Should be reasonable??? */
+ register char *p = buf;
+
+ c = getc (stream);
+ if (c == EOF)
+ break;
+ do {
+ *p++ = c;
+ } while (c != '\n' && (c = getc (stream)) >= 0);
+
+ /* we now have a source line in buf, null terminate and match */
+ *p = 0;
+ if (re_exec (buf) > 0)
+ {
+ /* Match! */
+ fclose (stream);
+ print_source_lines (current_source_symtab,
+ line, line+1, 0);
+ current_source_line = max (line - lines_to_list / 2, 1);
+ return;
+ }
+ line++;
+ }
+
+ printf_filtered ("Expression not found\n");
+ fclose (stream);
+}
+
+/* ARGSUSED */
+static void
+reverse_search_command (regex, from_tty)
+ char *regex;
+ int from_tty;
+{
+ register int c;
+ register int desc;
+ register FILE *stream;
+ int line = last_line_listed - 1;
+ char *msg;
+
+ msg = (char *) re_comp (regex);
+ if (msg)
+ error (msg);
+
+ if (current_source_symtab == 0)
+ select_source_symtab (0);
+
+ /* Search from last_line_listed-1 in current_source_symtab */
+
+ desc = open_source_file (current_source_symtab);
+ if (desc < 0)
+ perror_with_name (current_source_symtab->filename);
+
+ if (current_source_symtab->line_charpos == 0)
+ find_source_lines (current_source_symtab, desc);
+
+ if (line < 1 || line > current_source_symtab->nlines)
+ {
+ close (desc);
+ error ("Expression not found");
+ }
+
+ if (lseek (desc, current_source_symtab->line_charpos[line - 1], 0) < 0)
+ {
+ close (desc);
+ perror_with_name (current_source_symtab->filename);
+ }
+
+ stream = fdopen (desc, FOPEN_RT);
+ clearerr (stream);
+ while (line > 1)
+ {
+/* FIXME!!! We walk right off the end of buf if we get a long line!!! */
+ char buf[4096]; /* Should be reasonable??? */
+ register char *p = buf;
+
+ c = getc (stream);
+ if (c == EOF)
+ break;
+ do {
+ *p++ = c;
+ } while (c != '\n' && (c = getc (stream)) >= 0);
+
+ /* We now have a source line in buf; null terminate and match. */
+ *p = 0;
+ if (re_exec (buf) > 0)
+ {
+ /* Match! */
+ fclose (stream);
+ print_source_lines (current_source_symtab,
+ line, line+1, 0);
+ current_source_line = max (line - lines_to_list / 2, 1);
+ return;
+ }
+ line--;
+ if (fseek (stream, current_source_symtab->line_charpos[line - 1], 0) < 0)
+ {
+ fclose (stream);
+ perror_with_name (current_source_symtab->filename);
+ }
+ }
+
+ printf_filtered ("Expression not found\n");
+ fclose (stream);
+ return;
+}
+
+void
+_initialize_source ()
+{
+ struct cmd_list_element *c;
+ current_source_symtab = 0;
+ init_source_path ();
+
+ /* The intention is to use POSIX Basic Regular Expressions.
+ Always use the GNU regex routine for consistency across all hosts.
+ Our current GNU regex.c does not have all the POSIX features, so this is
+ just an approximation. */
+ re_set_syntax (RE_SYNTAX_GREP);
+
+ c = add_cmd ("directory", class_files, directory_command,
+ "Add directory DIR to beginning of search path for source files.\n\
+Forget cached info on source file locations and line positions.\n\
+DIR can also be $cwd for the current working directory, or $cdir for the\n\
+directory in which the source file was compiled into object code.\n\
+With no argument, reset the search path to $cdir:$cwd, the default.",
+ &cmdlist);
+ c->completer = filename_completer;
+
+ add_cmd ("directories", no_class, show_directories,
+ "Current search path for finding source files.\n\
+$cwd in the path means the current working directory.\n\
+$cdir in the path means the compilation directory of the source file.",
+ &showlist);
+
+ add_info ("source", source_info,
+ "Information about the current source file.");
+
+ add_info ("line", line_info,
+ "Core addresses of the code for a source line.\n\
+Line can be specified as\n\
+ LINENUM, to list around that line in current file,\n\
+ FILE:LINENUM, to list around that line in that file,\n\
+ FUNCTION, to list around beginning of that function,\n\
+ FILE:FUNCTION, to distinguish among like-named static functions.\n\
+Default is to describe the last source line that was listed.\n\n\
+This sets the default address for \"x\" to the line's first instruction\n\
+so that \"x/i\" suffices to start examining the machine code.\n\
+The address is also stored as the value of \"$_\".");
+
+ add_com ("forward-search", class_files, forward_search_command,
+ "Search for regular expression (see regex(3)) from last line listed.");
+ add_com_alias ("search", "forward-search", class_files, 0);
+
+ add_com ("reverse-search", class_files, reverse_search_command,
+ "Search backward for regular expression (see regex(3)) from last line listed.");
+
+ add_com ("list", class_files, list_command,
+ "List specified function or line.\n\
+With no argument, lists ten more lines after or around previous listing.\n\
+\"list -\" lists the ten lines before a previous ten-line listing.\n\
+One argument specifies a line, and ten lines are listed around that line.\n\
+Two arguments with comma between specify starting and ending lines to list.\n\
+Lines can be specified in these ways:\n\
+ LINENUM, to list around that line in current file,\n\
+ FILE:LINENUM, to list around that line in that file,\n\
+ FUNCTION, to list around beginning of that function,\n\
+ FILE:FUNCTION, to distinguish among like-named static functions.\n\
+ *ADDRESS, to list around the line containing that address.\n\
+With two args if one is empty it stands for ten lines away from the other arg.");
+ add_com_alias ("l", "list", class_files, 1);
+
+ add_show_from_set
+ (add_set_cmd ("listsize", class_support, var_uinteger,
+ (char *)&lines_to_list,
+ "Set number of source lines gdb will list by default.",
+ &setlist),
+ &showlist);
+}
diff --git a/gnu/usr.bin/gdb/gdb/stabsread.c b/gnu/usr.bin/gdb/gdb/stabsread.c
new file mode 100644
index 0000000..e81c314
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/stabsread.c
@@ -0,0 +1,3770 @@
+/* Support routines for decoding "stabs" debugging information format.
+ Copyright 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993
+ Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Support routines for reading and decoding debugging information in
+ the "stabs" format. This format is used with many systems that use
+ the a.out object file format, as well as some systems that use
+ COFF or ELF where the stabs data is placed in a special section.
+ Avoid placing any object file format specific code in this file. */
+
+#include "defs.h"
+#include "bfd.h"
+#include "obstack.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "aout/stab_gnu.h" /* We always use GNU stabs, not native */
+#include "buildsym.h"
+#include "complaints.h"
+#include "demangle.h"
+
+#include <ctype.h>
+
+/* Ask stabsread.h to define the vars it normally declares `extern'. */
+#define EXTERN /**/
+#include "stabsread.h" /* Our own declarations */
+#undef EXTERN
+
+/* The routines that read and process a complete stabs for a C struct or
+ C++ class pass lists of data member fields and lists of member function
+ fields in an instance of a field_info structure, as defined below.
+ This is part of some reorganization of low level C++ support and is
+ expected to eventually go away... (FIXME) */
+
+struct field_info
+{
+ struct nextfield
+ {
+ struct nextfield *next;
+ int visibility;
+ struct field field;
+ } *list;
+ struct next_fnfieldlist
+ {
+ struct next_fnfieldlist *next;
+ struct fn_fieldlist fn_fieldlist;
+ } *fnlist;
+};
+
+static struct type *
+dbx_alloc_type PARAMS ((int [2], struct objfile *));
+
+static long read_huge_number PARAMS ((char **, int, int *));
+
+static struct type *error_type PARAMS ((char **));
+
+static void
+patch_block_stabs PARAMS ((struct pending *, struct pending_stabs *,
+ struct objfile *));
+
+static void
+fix_common_block PARAMS ((struct symbol *, int));
+
+static int
+read_type_number PARAMS ((char **, int *));
+
+static struct type *
+read_range_type PARAMS ((char **, int [2], struct objfile *));
+
+static struct type *
+read_sun_builtin_type PARAMS ((char **, int [2], struct objfile *));
+
+static struct type *
+read_sun_floating_type PARAMS ((char **, int [2], struct objfile *));
+
+static struct type *
+read_enum_type PARAMS ((char **, struct type *, struct objfile *));
+
+static struct type *
+rs6000_builtin_type PARAMS ((int));
+
+static int
+read_member_functions PARAMS ((struct field_info *, char **, struct type *,
+ struct objfile *));
+
+static int
+read_struct_fields PARAMS ((struct field_info *, char **, struct type *,
+ struct objfile *));
+
+static int
+read_baseclasses PARAMS ((struct field_info *, char **, struct type *,
+ struct objfile *));
+
+static int
+read_tilde_fields PARAMS ((struct field_info *, char **, struct type *,
+ struct objfile *));
+
+static int
+attach_fn_fields_to_type PARAMS ((struct field_info *, struct type *));
+
+static int
+attach_fields_to_type PARAMS ((struct field_info *, struct type *,
+ struct objfile *));
+
+static struct type *
+read_struct_type PARAMS ((char **, struct type *, struct objfile *));
+
+static struct type *
+read_array_type PARAMS ((char **, struct type *, struct objfile *));
+
+static struct type **
+read_args PARAMS ((char **, int, struct objfile *));
+
+static int
+read_cpp_abbrev PARAMS ((struct field_info *, char **, struct type *,
+ struct objfile *));
+
+static const char vptr_name[] = { '_','v','p','t','r',CPLUS_MARKER,'\0' };
+static const char vb_name[] = { '_','v','b',CPLUS_MARKER,'\0' };
+
+/* Define this as 1 if a pcc declaration of a char or short argument
+ gives the correct address. Otherwise assume pcc gives the
+ address of the corresponding int, which is not the same on a
+ big-endian machine. */
+
+#ifndef BELIEVE_PCC_PROMOTION
+#define BELIEVE_PCC_PROMOTION 0
+#endif
+
+#if 0
+/* I think this can go away, all current uses have been removed.
+ GCC emits a few crazy types which can only be distinguished by the
+ name (complex, long long on some machines), but I'd say fix GCC. */
+
+/* During some calls to read_type (and thus to read_range_type), this
+ contains the name of the type being defined. Range types are only
+ used in C as basic types. We use the name to distinguish the otherwise
+ identical basic types "int" and "long" and their unsigned versions.
+ FIXME, this should disappear with better type management. */
+
+static char *long_kludge_name;
+#endif
+
+#if 0
+struct complaint dbx_class_complaint =
+{
+ "encountered DBX-style class variable debugging information.\n\
+You seem to have compiled your program with \
+\"g++ -g0\" instead of \"g++ -g\".\n\
+Therefore GDB will not know about your class variables", 0, 0
+};
+#endif
+
+struct complaint invalid_cpp_abbrev_complaint =
+ {"invalid C++ abbreviation `%s'", 0, 0};
+
+struct complaint invalid_cpp_type_complaint =
+ {"C++ abbreviated type name unknown at symtab pos %d", 0, 0};
+
+struct complaint member_fn_complaint =
+ {"member function type missing, got '%c'", 0, 0};
+
+struct complaint const_vol_complaint =
+ {"const/volatile indicator missing, got '%c'", 0, 0};
+
+struct complaint error_type_complaint =
+ {"debug info mismatch between compiler and debugger", 0, 0};
+
+struct complaint invalid_member_complaint =
+ {"invalid (minimal) member type data format at symtab pos %d.", 0, 0};
+
+struct complaint range_type_base_complaint =
+ {"base type %d of range type is not defined", 0, 0};
+
+struct complaint reg_value_complaint =
+ {"register number too large in symbol %s", 0, 0};
+
+struct complaint vtbl_notfound_complaint =
+ {"virtual function table pointer not found when defining class `%s'", 0, 0};
+
+struct complaint unrecognized_cplus_name_complaint =
+ {"Unknown C++ symbol name `%s'", 0, 0};
+
+struct complaint rs6000_builtin_complaint =
+ {"Unknown builtin type %d", 0, 0};
+
+struct complaint stabs_general_complaint =
+ {"%s", 0, 0};
+
+/* Make a list of forward references which haven't been defined. */
+
+static struct type **undef_types;
+static int undef_types_allocated;
+static int undef_types_length;
+
+/* Check for and handle cretinous stabs symbol name continuation! */
+#define STABS_CONTINUE(pp) \
+ do { \
+ if (**(pp) == '\\') *(pp) = next_symbol_text (); \
+ } while (0)
+
+
+/* Look up a dbx type-number pair. Return the address of the slot
+ where the type for that number-pair is stored.
+ The number-pair is in TYPENUMS.
+
+ This can be used for finding the type associated with that pair
+ or for associating a new type with the pair. */
+
+struct type **
+dbx_lookup_type (typenums)
+ int typenums[2];
+{
+ register int filenum = typenums[0];
+ register int index = typenums[1];
+ unsigned old_len;
+ register int real_filenum;
+ register struct header_file *f;
+ int f_orig_length;
+
+ if (filenum == -1) /* -1,-1 is for temporary types. */
+ return 0;
+
+ if (filenum < 0 || filenum >= n_this_object_header_files)
+ {
+ static struct complaint msg = {"\
+Invalid symbol data: type number (%d,%d) out of range at symtab pos %d.",
+ 0, 0};
+ complain (&msg, filenum, index, symnum);
+ goto error_return;
+ }
+
+ if (filenum == 0)
+ {
+ if (index < 0)
+ {
+ /* Caller wants address of address of type. We think
+ that negative (rs6k builtin) types will never appear as
+ "lvalues", (nor should they), so we stuff the real type
+ pointer into a temp, and return its address. If referenced,
+ this will do the right thing. */
+ static struct type *temp_type;
+
+ temp_type = rs6000_builtin_type(index);
+ return &temp_type;
+ }
+
+ /* Type is defined outside of header files.
+ Find it in this object file's type vector. */
+ if (index >= type_vector_length)
+ {
+ old_len = type_vector_length;
+ if (old_len == 0)
+ {
+ type_vector_length = INITIAL_TYPE_VECTOR_LENGTH;
+ type_vector = (struct type **)
+ malloc (type_vector_length * sizeof (struct type *));
+ }
+ while (index >= type_vector_length)
+ {
+ type_vector_length *= 2;
+ }
+ type_vector = (struct type **)
+ xrealloc ((char *) type_vector,
+ (type_vector_length * sizeof (struct type *)));
+ memset (&type_vector[old_len], 0,
+ (type_vector_length - old_len) * sizeof (struct type *));
+ }
+ return (&type_vector[index]);
+ }
+ else
+ {
+ real_filenum = this_object_header_files[filenum];
+
+ if (real_filenum >= n_header_files)
+ {
+ struct type *temp_type;
+ struct type **temp_type_p;
+
+ warning ("GDB internal error: bad real_filenum");
+
+ error_return:
+ temp_type = init_type (TYPE_CODE_ERROR, 0, 0, NULL, NULL);
+ temp_type_p = (struct type **) xmalloc (sizeof (struct type *));
+ *temp_type_p = temp_type;
+ return temp_type_p;
+ }
+
+ f = &header_files[real_filenum];
+
+ f_orig_length = f->length;
+ if (index >= f_orig_length)
+ {
+ while (index >= f->length)
+ {
+ f->length *= 2;
+ }
+ f->vector = (struct type **)
+ xrealloc ((char *) f->vector, f->length * sizeof (struct type *));
+ memset (&f->vector[f_orig_length], 0,
+ (f->length - f_orig_length) * sizeof (struct type *));
+ }
+ return (&f->vector[index]);
+ }
+}
+
+/* Make sure there is a type allocated for type numbers TYPENUMS
+ and return the type object.
+ This can create an empty (zeroed) type object.
+ TYPENUMS may be (-1, -1) to return a new type object that is not
+ put into the type vector, and so may not be referred to by number. */
+
+static struct type *
+dbx_alloc_type (typenums, objfile)
+ int typenums[2];
+ struct objfile *objfile;
+{
+ register struct type **type_addr;
+
+ if (typenums[0] == -1)
+ {
+ return (alloc_type (objfile));
+ }
+
+ type_addr = dbx_lookup_type (typenums);
+
+ /* If we are referring to a type not known at all yet,
+ allocate an empty type for it.
+ We will fill it in later if we find out how. */
+ if (*type_addr == 0)
+ {
+ *type_addr = alloc_type (objfile);
+ }
+
+ return (*type_addr);
+}
+
+/* for all the stabs in a given stab vector, build appropriate types
+ and fix their symbols in given symbol vector. */
+
+static void
+patch_block_stabs (symbols, stabs, objfile)
+ struct pending *symbols;
+ struct pending_stabs *stabs;
+ struct objfile *objfile;
+{
+ int ii;
+ char *name;
+ char *pp;
+ struct symbol *sym;
+
+ if (stabs)
+ {
+
+ /* for all the stab entries, find their corresponding symbols and
+ patch their types! */
+
+ for (ii = 0; ii < stabs->count; ++ii)
+ {
+ name = stabs->stab[ii];
+ pp = (char*) strchr (name, ':');
+ sym = find_symbol_in_list (symbols, name, pp-name);
+ if (!sym)
+ {
+ /* On xcoff, if a global is defined and never referenced,
+ ld will remove it from the executable. There is then
+ a N_GSYM stab for it, but no regular (C_EXT) symbol. */
+ sym = (struct symbol *)
+ obstack_alloc (&objfile->symbol_obstack,
+ sizeof (struct symbol));
+
+ memset (sym, 0, sizeof (struct symbol));
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ SYMBOL_CLASS (sym) = LOC_OPTIMIZED_OUT;
+ SYMBOL_NAME (sym) =
+ obstack_copy0 (&objfile->symbol_obstack, name, pp - name);
+ pp += 2;
+ if (*(pp-1) == 'F' || *(pp-1) == 'f')
+ {
+ /* I don't think the linker does this with functions,
+ so as far as I know this is never executed.
+ But it doesn't hurt to check. */
+ SYMBOL_TYPE (sym) =
+ lookup_function_type (read_type (&pp, objfile));
+ }
+ else
+ {
+ SYMBOL_TYPE (sym) = read_type (&pp, objfile);
+ }
+ add_symbol_to_list (sym, &global_symbols);
+ }
+ else
+ {
+ pp += 2;
+ if (*(pp-1) == 'F' || *(pp-1) == 'f')
+ {
+ SYMBOL_TYPE (sym) =
+ lookup_function_type (read_type (&pp, objfile));
+ }
+ else
+ {
+ SYMBOL_TYPE (sym) = read_type (&pp, objfile);
+ }
+ }
+ }
+ }
+}
+
+
+/* Read a number by which a type is referred to in dbx data,
+ or perhaps read a pair (FILENUM, TYPENUM) in parentheses.
+ Just a single number N is equivalent to (0,N).
+ Return the two numbers by storing them in the vector TYPENUMS.
+ TYPENUMS will then be used as an argument to dbx_lookup_type.
+
+ Returns 0 for success, -1 for error. */
+
+static int
+read_type_number (pp, typenums)
+ register char **pp;
+ register int *typenums;
+{
+ int nbits;
+ if (**pp == '(')
+ {
+ (*pp)++;
+ typenums[0] = read_huge_number (pp, ',', &nbits);
+ if (nbits != 0) return -1;
+ typenums[1] = read_huge_number (pp, ')', &nbits);
+ if (nbits != 0) return -1;
+ }
+ else
+ {
+ typenums[0] = 0;
+ typenums[1] = read_huge_number (pp, 0, &nbits);
+ if (nbits != 0) return -1;
+ }
+ return 0;
+}
+
+
+/* To handle GNU C++ typename abbreviation, we need to be able to
+ fill in a type's name as soon as space for that type is allocated.
+ `type_synonym_name' is the name of the type being allocated.
+ It is cleared as soon as it is used (lest all allocated types
+ get this name). */
+
+static char *type_synonym_name;
+
+/* ARGSUSED */
+struct symbol *
+define_symbol (valu, string, desc, type, objfile)
+ CORE_ADDR valu;
+ char *string;
+ int desc;
+ int type;
+ struct objfile *objfile;
+{
+ register struct symbol *sym;
+ char *p = (char *) strchr (string, ':');
+ int deftype;
+ int synonym = 0;
+ register int i;
+
+ /* We would like to eliminate nameless symbols, but keep their types.
+ E.g. stab entry ":t10=*2" should produce a type 10, which is a pointer
+ to type 2, but, should not create a symbol to address that type. Since
+ the symbol will be nameless, there is no way any user can refer to it. */
+
+ int nameless;
+
+ /* Ignore syms with empty names. */
+ if (string[0] == 0)
+ return 0;
+
+ /* Ignore old-style symbols from cc -go */
+ if (p == 0)
+ return 0;
+
+ /* If a nameless stab entry, all we need is the type, not the symbol.
+ e.g. ":t10=*2" or a nameless enum like " :T16=ered:0,green:1,blue:2,;" */
+ nameless = (p == string || ((string[0] == ' ') && (string[1] == ':')));
+
+ sym = (struct symbol *)
+ obstack_alloc (&objfile -> symbol_obstack, sizeof (struct symbol));
+ memset (sym, 0, sizeof (struct symbol));
+
+ if (processing_gcc_compilation)
+ {
+ /* GCC 2.x puts the line number in desc. SunOS apparently puts in the
+ number of bytes occupied by a type or object, which we ignore. */
+ SYMBOL_LINE(sym) = desc;
+ }
+ else
+ {
+ SYMBOL_LINE(sym) = 0; /* unknown */
+ }
+
+ if (string[0] == CPLUS_MARKER)
+ {
+ /* Special GNU C++ names. */
+ switch (string[1])
+ {
+ case 't':
+ SYMBOL_NAME (sym) = obsavestring ("this", strlen ("this"),
+ &objfile -> symbol_obstack);
+ break;
+
+ case 'v': /* $vtbl_ptr_type */
+ /* Was: SYMBOL_NAME (sym) = "vptr"; */
+ goto normal;
+
+ case 'e':
+ SYMBOL_NAME (sym) = obsavestring ("eh_throw", strlen ("eh_throw"),
+ &objfile -> symbol_obstack);
+ break;
+
+ case '_':
+ /* This was an anonymous type that was never fixed up. */
+ goto normal;
+
+ default:
+ complain (&unrecognized_cplus_name_complaint, string);
+ goto normal; /* Do *something* with it */
+ }
+ }
+ else
+ {
+ normal:
+ SYMBOL_LANGUAGE (sym) = current_subfile -> language;
+ SYMBOL_NAME (sym) = (char *)
+ obstack_alloc (&objfile -> symbol_obstack, ((p - string) + 1));
+ /* Open-coded memcpy--saves function call time. */
+ /* FIXME: Does it really? Try replacing with simple strcpy and
+ try it on an executable with a large symbol table. */
+ /* FIXME: considering that gcc can open code memcpy anyway, I
+ doubt it. xoxorich. */
+ {
+ register char *p1 = string;
+ register char *p2 = SYMBOL_NAME (sym);
+ while (p1 != p)
+ {
+ *p2++ = *p1++;
+ }
+ *p2++ = '\0';
+ }
+
+ /* If this symbol is from a C++ compilation, then attempt to cache the
+ demangled form for future reference. This is a typical time versus
+ space tradeoff, that was decided in favor of time because it sped up
+ C++ symbol lookups by a factor of about 20. */
+
+ SYMBOL_INIT_DEMANGLED_NAME (sym, &objfile->symbol_obstack);
+ }
+ p++;
+
+ /* Determine the type of name being defined. */
+#if 0
+ /* Getting GDB to correctly skip the symbol on an undefined symbol
+ descriptor and not ever dump core is a very dodgy proposition if
+ we do things this way. I say the acorn RISC machine can just
+ fix their compiler. */
+ /* The Acorn RISC machine's compiler can put out locals that don't
+ start with "234=" or "(3,4)=", so assume anything other than the
+ deftypes we know how to handle is a local. */
+ if (!strchr ("cfFGpPrStTvVXCR", *p))
+#else
+ if (isdigit (*p) || *p == '(' || *p == '-')
+#endif
+ deftype = 'l';
+ else
+ deftype = *p++;
+
+ switch (deftype)
+ {
+ case 'c':
+ /* c is a special case, not followed by a type-number.
+ SYMBOL:c=iVALUE for an integer constant symbol.
+ SYMBOL:c=rVALUE for a floating constant symbol.
+ SYMBOL:c=eTYPE,INTVALUE for an enum constant symbol.
+ e.g. "b:c=e6,0" for "const b = blob1"
+ (where type 6 is defined by "blobs:t6=eblob1:0,blob2:1,;"). */
+ if (*p != '=')
+ {
+ SYMBOL_CLASS (sym) = LOC_CONST;
+ SYMBOL_TYPE (sym) = error_type (&p);
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &file_symbols);
+ return sym;
+ }
+ ++p;
+ switch (*p++)
+ {
+ case 'r':
+ {
+ double d = atof (p);
+ char *dbl_valu;
+
+ /* FIXME: lookup_fundamental_type is a hack. We should be
+ creating a type especially for the type of float constants.
+ Problem is, what type should it be? We currently have to
+ read this in host floating point format, but what type
+ represents a host format "double"?
+
+ Also, what should the name of this type be? Should we
+ be using 'S' constants (see stabs.texinfo) instead? */
+
+ SYMBOL_TYPE (sym) = lookup_fundamental_type (objfile,
+ FT_DBL_PREC_FLOAT);
+ dbl_valu = (char *)
+ obstack_alloc (&objfile -> symbol_obstack, sizeof (double));
+ memcpy (dbl_valu, &d, sizeof (double));
+ /* Put it in target byte order, but it's still in host
+ floating point format. */
+ SWAP_TARGET_AND_HOST (dbl_valu, sizeof (double));
+ SYMBOL_VALUE_BYTES (sym) = dbl_valu;
+ SYMBOL_CLASS (sym) = LOC_CONST_BYTES;
+ }
+ break;
+ case 'i':
+ {
+ /* Defining integer constants this way is kind of silly,
+ since 'e' constants allows the compiler to give not
+ only the value, but the type as well. C has at least
+ int, long, unsigned int, and long long as constant
+ types; other languages probably should have at least
+ unsigned as well as signed constants. */
+
+ /* We just need one int constant type for all objfiles.
+ It doesn't depend on languages or anything (arguably its
+ name should be a language-specific name for a type of
+ that size, but I'm inclined to say that if the compiler
+ wants a nice name for the type, it can use 'e'). */
+ static struct type *int_const_type;
+
+ /* Yes, this is as long as a *host* int. That is because we
+ use atoi. */
+ if (int_const_type == NULL)
+ int_const_type =
+ init_type (TYPE_CODE_INT,
+ sizeof (int) * HOST_CHAR_BIT / TARGET_CHAR_BIT, 0,
+ "integer constant",
+ (struct objfile *)NULL);
+ SYMBOL_TYPE (sym) = int_const_type;
+ SYMBOL_VALUE (sym) = atoi (p);
+ SYMBOL_CLASS (sym) = LOC_CONST;
+ }
+ break;
+ case 'e':
+ /* SYMBOL:c=eTYPE,INTVALUE for a constant symbol whose value
+ can be represented as integral.
+ e.g. "b:c=e6,0" for "const b = blob1"
+ (where type 6 is defined by "blobs:t6=eblob1:0,blob2:1,;"). */
+ {
+ SYMBOL_CLASS (sym) = LOC_CONST;
+ SYMBOL_TYPE (sym) = read_type (&p, objfile);
+
+ if (*p != ',')
+ {
+ SYMBOL_TYPE (sym) = error_type (&p);
+ break;
+ }
+ ++p;
+
+ /* If the value is too big to fit in an int (perhaps because
+ it is unsigned), or something like that, we silently get
+ a bogus value. The type and everything else about it is
+ correct. Ideally, we should be using whatever we have
+ available for parsing unsigned and long long values,
+ however. */
+ SYMBOL_VALUE (sym) = atoi (p);
+ }
+ break;
+ default:
+ {
+ SYMBOL_CLASS (sym) = LOC_CONST;
+ SYMBOL_TYPE (sym) = error_type (&p);
+ }
+ }
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &file_symbols);
+ return sym;
+
+ case 'C':
+ /* The name of a caught exception. */
+ SYMBOL_TYPE (sym) = read_type (&p, objfile);
+ SYMBOL_CLASS (sym) = LOC_LABEL;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ SYMBOL_VALUE_ADDRESS (sym) = valu;
+ add_symbol_to_list (sym, &local_symbols);
+ break;
+
+ case 'f':
+ /* A static function definition. */
+ SYMBOL_TYPE (sym) = read_type (&p, objfile);
+ SYMBOL_CLASS (sym) = LOC_BLOCK;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &file_symbols);
+ /* fall into process_function_types. */
+
+ process_function_types:
+ /* Function result types are described as the result type in stabs.
+ We need to convert this to the function-returning-type-X type
+ in GDB. E.g. "int" is converted to "function returning int". */
+ if (TYPE_CODE (SYMBOL_TYPE (sym)) != TYPE_CODE_FUNC)
+ {
+#if 0
+ /* This code doesn't work -- it needs to realloc and can't. */
+ /* Attempt to set up to record a function prototype... */
+ struct type *new = alloc_type (objfile);
+
+ /* Generate a template for the type of this function. The
+ types of the arguments will be added as we read the symbol
+ table. */
+ *new = *lookup_function_type (SYMBOL_TYPE(sym));
+ SYMBOL_TYPE(sym) = new;
+ TYPE_OBJFILE (new) = objfile;
+ in_function_type = new;
+#else
+ SYMBOL_TYPE (sym) = lookup_function_type (SYMBOL_TYPE (sym));
+#endif
+ }
+ /* fall into process_prototype_types */
+
+ process_prototype_types:
+ /* Sun acc puts declared types of arguments here. We don't care
+ about their actual types (FIXME -- we should remember the whole
+ function prototype), but the list may define some new types
+ that we have to remember, so we must scan it now. */
+ while (*p == ';') {
+ p++;
+ read_type (&p, objfile);
+ }
+ break;
+
+ case 'F':
+ /* A global function definition. */
+ SYMBOL_TYPE (sym) = read_type (&p, objfile);
+ SYMBOL_CLASS (sym) = LOC_BLOCK;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &global_symbols);
+ goto process_function_types;
+
+ case 'G':
+ /* For a class G (global) symbol, it appears that the
+ value is not correct. It is necessary to search for the
+ corresponding linker definition to find the value.
+ These definitions appear at the end of the namelist. */
+ SYMBOL_TYPE (sym) = read_type (&p, objfile);
+ i = hashname (SYMBOL_NAME (sym));
+ SYMBOL_VALUE_CHAIN (sym) = global_sym_chain[i];
+ global_sym_chain[i] = sym;
+ SYMBOL_CLASS (sym) = LOC_STATIC;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &global_symbols);
+ break;
+
+ /* This case is faked by a conditional above,
+ when there is no code letter in the dbx data.
+ Dbx data never actually contains 'l'. */
+ case 'l':
+ SYMBOL_TYPE (sym) = read_type (&p, objfile);
+ SYMBOL_CLASS (sym) = LOC_LOCAL;
+ SYMBOL_VALUE (sym) = valu;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &local_symbols);
+ break;
+
+ case 'p':
+ if (*p == 'F')
+ /* pF is a two-letter code that means a function parameter in Fortran.
+ The type-number specifies the type of the return value.
+ Translate it into a pointer-to-function type. */
+ {
+ p++;
+ SYMBOL_TYPE (sym)
+ = lookup_pointer_type
+ (lookup_function_type (read_type (&p, objfile)));
+ }
+ else
+ SYMBOL_TYPE (sym) = read_type (&p, objfile);
+
+ /* Normally this is a parameter, a LOC_ARG. On the i960, it
+ can also be a LOC_LOCAL_ARG depending on symbol type. */
+#ifndef DBX_PARM_SYMBOL_CLASS
+#define DBX_PARM_SYMBOL_CLASS(type) LOC_ARG
+#endif
+
+ SYMBOL_CLASS (sym) = DBX_PARM_SYMBOL_CLASS (type);
+ SYMBOL_VALUE (sym) = valu;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+#if 0
+ /* This doesn't work yet. */
+ add_param_to_type (&in_function_type, sym);
+#endif
+ add_symbol_to_list (sym, &local_symbols);
+
+#if TARGET_BYTE_ORDER == LITTLE_ENDIAN
+ /* On little-endian machines, this crud is never necessary, and,
+ if the extra bytes contain garbage, is harmful. */
+ break;
+#else /* Big endian. */
+ /* If it's gcc-compiled, if it says `short', believe it. */
+ if (processing_gcc_compilation || BELIEVE_PCC_PROMOTION)
+ break;
+
+#if !BELIEVE_PCC_PROMOTION
+ {
+ /* This is the signed type which arguments get promoted to. */
+ static struct type *pcc_promotion_type;
+ /* This is the unsigned type which arguments get promoted to. */
+ static struct type *pcc_unsigned_promotion_type;
+
+ /* Call it "int" because this is mainly C lossage. */
+ if (pcc_promotion_type == NULL)
+ pcc_promotion_type =
+ init_type (TYPE_CODE_INT, TARGET_INT_BIT / TARGET_CHAR_BIT,
+ 0, "int", NULL);
+
+ if (pcc_unsigned_promotion_type == NULL)
+ pcc_unsigned_promotion_type =
+ init_type (TYPE_CODE_INT, TARGET_INT_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED, "unsigned int", NULL);
+
+#if defined(BELIEVE_PCC_PROMOTION_TYPE)
+ /* This macro is defined on machines (e.g. sparc) where
+ we should believe the type of a PCC 'short' argument,
+ but shouldn't believe the address (the address is
+ the address of the corresponding int). Note that
+ this is only different from the BELIEVE_PCC_PROMOTION
+ case on big-endian machines.
+
+ My guess is that this correction, as opposed to changing
+ the parameter to an 'int' (as done below, for PCC
+ on most machines), is the right thing to do
+ on all machines, but I don't want to risk breaking
+ something that already works. On most PCC machines,
+ the sparc problem doesn't come up because the calling
+ function has to zero the top bytes (not knowing whether
+ the called function wants an int or a short), so there
+ is no practical difference between an int and a short
+ (except perhaps what happens when the GDB user types
+ "print short_arg = 0x10000;").
+
+ Hacked for SunOS 4.1 by gnu@cygnus.com. In 4.1, the compiler
+ actually produces the correct address (we don't need to fix it
+ up). I made this code adapt so that it will offset the symbol
+ if it was pointing at an int-aligned location and not
+ otherwise. This way you can use the same gdb for 4.0.x and
+ 4.1 systems.
+
+ If the parameter is shorter than an int, and is integral
+ (e.g. char, short, or unsigned equivalent), and is claimed to
+ be passed on an integer boundary, don't believe it! Offset the
+ parameter's address to the tail-end of that integer. */
+
+ if (TYPE_LENGTH (SYMBOL_TYPE (sym)) < TYPE_LENGTH (pcc_promotion_type)
+ && TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_INT
+ && 0 == SYMBOL_VALUE (sym) % TYPE_LENGTH (pcc_promotion_type))
+ {
+ SYMBOL_VALUE (sym) += TYPE_LENGTH (pcc_promotion_type)
+ - TYPE_LENGTH (SYMBOL_TYPE (sym));
+ }
+ break;
+
+#else /* no BELIEVE_PCC_PROMOTION_TYPE. */
+
+ /* If PCC says a parameter is a short or a char,
+ it is really an int. */
+ if (TYPE_LENGTH (SYMBOL_TYPE (sym)) < TYPE_LENGTH (pcc_promotion_type)
+ && TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_INT)
+ {
+ SYMBOL_TYPE (sym) =
+ TYPE_UNSIGNED (SYMBOL_TYPE (sym))
+ ? pcc_unsigned_promotion_type
+ : pcc_promotion_type;
+ }
+ break;
+
+#endif /* no BELIEVE_PCC_PROMOTION_TYPE. */
+ }
+#endif /* !BELIEVE_PCC_PROMOTION. */
+#endif /* Big endian. */
+
+ case 'P':
+ /* acc seems to use P to delare the prototypes of functions that
+ are referenced by this file. gdb is not prepared to deal
+ with this extra information. FIXME, it ought to. */
+ if (type == N_FUN)
+ {
+ read_type (&p, objfile);
+ goto process_prototype_types;
+ }
+ /*FALLTHROUGH*/
+
+ case 'R':
+ /* Parameter which is in a register. */
+ SYMBOL_TYPE (sym) = read_type (&p, objfile);
+ SYMBOL_CLASS (sym) = LOC_REGPARM;
+ SYMBOL_VALUE (sym) = STAB_REG_TO_REGNUM (valu);
+ if (SYMBOL_VALUE (sym) >= NUM_REGS)
+ {
+ complain (&reg_value_complaint, SYMBOL_SOURCE_NAME (sym));
+ SYMBOL_VALUE (sym) = SP_REGNUM; /* Known safe, though useless */
+ }
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &local_symbols);
+ break;
+
+ case 'r':
+ /* Register variable (either global or local). */
+ SYMBOL_TYPE (sym) = read_type (&p, objfile);
+ SYMBOL_CLASS (sym) = LOC_REGISTER;
+ SYMBOL_VALUE (sym) = STAB_REG_TO_REGNUM (valu);
+ if (SYMBOL_VALUE (sym) >= NUM_REGS)
+ {
+ complain (&reg_value_complaint, SYMBOL_SOURCE_NAME (sym));
+ SYMBOL_VALUE (sym) = SP_REGNUM; /* Known safe, though useless */
+ }
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ if (within_function)
+ {
+ /* Sun cc uses a pair of symbols, one 'p' and one 'r' with the same
+ name to represent an argument passed in a register.
+ GCC uses 'P' for the same case. So if we find such a symbol pair
+ we combine it into one 'P' symbol.
+ Note that this code illegally combines
+ main(argc) int argc; { register int argc = 1; }
+ but this case is considered pathological and causes a warning
+ from a decent compiler. */
+ if (local_symbols
+ && local_symbols->nsyms > 0)
+ {
+ struct symbol *prev_sym;
+ prev_sym = local_symbols->symbol[local_symbols->nsyms - 1];
+ if (SYMBOL_CLASS (prev_sym) == LOC_ARG
+ && STREQ (SYMBOL_NAME (prev_sym), SYMBOL_NAME(sym)))
+ {
+ SYMBOL_CLASS (prev_sym) = LOC_REGPARM;
+ SYMBOL_VALUE (prev_sym) = SYMBOL_VALUE (sym);
+ sym = prev_sym;
+ break;
+ }
+ }
+ add_symbol_to_list (sym, &local_symbols);
+ }
+ else
+ add_symbol_to_list (sym, &file_symbols);
+ break;
+
+ case 'S':
+ /* Static symbol at top level of file */
+ SYMBOL_TYPE (sym) = read_type (&p, objfile);
+ SYMBOL_CLASS (sym) = LOC_STATIC;
+ SYMBOL_VALUE_ADDRESS (sym) = valu;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &file_symbols);
+ break;
+
+ case 't':
+#if 0
+ /* See comment where long_kludge_name is declared. */
+ /* Here we save the name of the symbol for read_range_type, which
+ ends up reading in the basic types. In stabs, unfortunately there
+ is no distinction between "int" and "long" types except their
+ names. Until we work out a saner type policy (eliminating most
+ builtin types and using the names specified in the files), we
+ save away the name so that far away from here in read_range_type,
+ we can examine it to decide between "int" and "long". FIXME. */
+ long_kludge_name = SYMBOL_NAME (sym);
+#endif
+ SYMBOL_TYPE (sym) = read_type (&p, objfile);
+
+ /* For a nameless type, we don't want a create a symbol, thus we
+ did not use `sym'. Return without further processing. */
+ if (nameless) return NULL;
+
+ SYMBOL_CLASS (sym) = LOC_TYPEDEF;
+ SYMBOL_VALUE (sym) = valu;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ /* C++ vagaries: we may have a type which is derived from
+ a base type which did not have its name defined when the
+ derived class was output. We fill in the derived class's
+ base part member's name here in that case. */
+ if (TYPE_NAME (SYMBOL_TYPE (sym)) != NULL)
+ if ((TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_STRUCT
+ || TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_UNION)
+ && TYPE_N_BASECLASSES (SYMBOL_TYPE (sym)))
+ {
+ int j;
+ for (j = TYPE_N_BASECLASSES (SYMBOL_TYPE (sym)) - 1; j >= 0; j--)
+ if (TYPE_BASECLASS_NAME (SYMBOL_TYPE (sym), j) == 0)
+ TYPE_BASECLASS_NAME (SYMBOL_TYPE (sym), j) =
+ type_name_no_tag (TYPE_BASECLASS (SYMBOL_TYPE (sym), j));
+ }
+
+ if (TYPE_NAME (SYMBOL_TYPE (sym)) == NULL)
+ {
+ if (TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_PTR
+ || TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_FUNC)
+ {
+ /* If we are giving a name to a type such as "pointer to
+ foo" or "function returning foo", we better not set
+ the TYPE_NAME. If the program contains "typedef char
+ *caddr_t;", we don't want all variables of type char
+ * to print as caddr_t. This is not just a
+ consequence of GDB's type management; PCC and GCC (at
+ least through version 2.4) both output variables of
+ either type char * or caddr_t with the type number
+ defined in the 't' symbol for caddr_t. If a future
+ compiler cleans this up it GDB is not ready for it
+ yet, but if it becomes ready we somehow need to
+ disable this check (without breaking the PCC/GCC2.4
+ case).
+
+ Sigh.
+
+ Fortunately, this check seems not to be necessary
+ for anything except pointers or functions. */
+ }
+ else
+ TYPE_NAME (SYMBOL_TYPE (sym)) = SYMBOL_NAME (sym);
+ }
+
+ add_symbol_to_list (sym, &file_symbols);
+ break;
+
+ case 'T':
+ /* Struct, union, or enum tag. For GNU C++, this can be be followed
+ by 't' which means we are typedef'ing it as well. */
+ synonym = *p == 't';
+
+ if (synonym)
+ {
+ p++;
+ type_synonym_name = obsavestring (SYMBOL_NAME (sym),
+ strlen (SYMBOL_NAME (sym)),
+ &objfile -> symbol_obstack);
+ }
+
+ SYMBOL_TYPE (sym) = read_type (&p, objfile);
+
+ /* For a nameless type, we don't want a create a symbol, thus we
+ did not use `sym'. Return without further processing. */
+ if (nameless) return NULL;
+
+ SYMBOL_CLASS (sym) = LOC_TYPEDEF;
+ SYMBOL_VALUE (sym) = valu;
+ SYMBOL_NAMESPACE (sym) = STRUCT_NAMESPACE;
+ if (TYPE_TAG_NAME (SYMBOL_TYPE (sym)) == 0)
+ TYPE_TAG_NAME (SYMBOL_TYPE (sym))
+ = obconcat (&objfile -> type_obstack, "", "", SYMBOL_NAME (sym));
+ add_symbol_to_list (sym, &file_symbols);
+
+ if (synonym)
+ {
+ /* Clone the sym and then modify it. */
+ register struct symbol *typedef_sym = (struct symbol *)
+ obstack_alloc (&objfile -> symbol_obstack, sizeof (struct symbol));
+ *typedef_sym = *sym;
+ SYMBOL_CLASS (typedef_sym) = LOC_TYPEDEF;
+ SYMBOL_VALUE (typedef_sym) = valu;
+ SYMBOL_NAMESPACE (typedef_sym) = VAR_NAMESPACE;
+ if (TYPE_NAME (SYMBOL_TYPE (sym)) == 0)
+ TYPE_NAME (SYMBOL_TYPE (sym))
+ = obconcat (&objfile -> type_obstack, "", "", SYMBOL_NAME (sym));
+ add_symbol_to_list (typedef_sym, &file_symbols);
+ }
+ break;
+
+ case 'V':
+ /* Static symbol of local scope */
+ SYMBOL_TYPE (sym) = read_type (&p, objfile);
+ SYMBOL_CLASS (sym) = LOC_STATIC;
+ SYMBOL_VALUE_ADDRESS (sym) = valu;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &local_symbols);
+ break;
+
+ case 'v':
+ /* Reference parameter */
+ SYMBOL_TYPE (sym) = read_type (&p, objfile);
+ SYMBOL_CLASS (sym) = LOC_REF_ARG;
+ SYMBOL_VALUE (sym) = valu;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &local_symbols);
+ break;
+
+ case 'X':
+ /* This is used by Sun FORTRAN for "function result value".
+ Sun claims ("dbx and dbxtool interfaces", 2nd ed)
+ that Pascal uses it too, but when I tried it Pascal used
+ "x:3" (local symbol) instead. */
+ SYMBOL_TYPE (sym) = read_type (&p, objfile);
+ SYMBOL_CLASS (sym) = LOC_LOCAL;
+ SYMBOL_VALUE (sym) = valu;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &local_symbols);
+ break;
+
+ default:
+ SYMBOL_TYPE (sym) = error_type (&p);
+ SYMBOL_CLASS (sym) = LOC_CONST;
+ SYMBOL_VALUE (sym) = 0;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ add_symbol_to_list (sym, &file_symbols);
+ break;
+ }
+
+ /* When passing structures to a function, some systems sometimes pass
+ the address in a register, not the structure itself.
+
+ If REG_STRUCT_HAS_ADDR yields non-zero we have to convert LOC_REGPARM
+ to LOC_REGPARM_ADDR for structures and unions. */
+
+#if !defined (REG_STRUCT_HAS_ADDR)
+#define REG_STRUCT_HAS_ADDR(gcc_p) 0
+#endif
+
+ if (SYMBOL_CLASS (sym) == LOC_REGPARM
+ && REG_STRUCT_HAS_ADDR (processing_gcc_compilation)
+ && ( (TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_STRUCT)
+ || (TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_UNION)))
+ SYMBOL_CLASS (sym) = LOC_REGPARM_ADDR;
+
+ return sym;
+}
+
+
+/* Skip rest of this symbol and return an error type.
+
+ General notes on error recovery: error_type always skips to the
+ end of the symbol (modulo cretinous dbx symbol name continuation).
+ Thus code like this:
+
+ if (*(*pp)++ != ';')
+ return error_type (pp);
+
+ is wrong because if *pp starts out pointing at '\0' (typically as the
+ result of an earlier error), it will be incremented to point to the
+ start of the next symbol, which might produce strange results, at least
+ if you run off the end of the string table. Instead use
+
+ if (**pp != ';')
+ return error_type (pp);
+ ++*pp;
+
+ or
+
+ if (**pp != ';')
+ foo = error_type (pp);
+ else
+ ++*pp;
+
+ And in case it isn't obvious, the point of all this hair is so the compiler
+ can define new types and new syntaxes, and old versions of the
+ debugger will be able to read the new symbol tables. */
+
+static struct type *
+error_type (pp)
+ char **pp;
+{
+ complain (&error_type_complaint);
+ while (1)
+ {
+ /* Skip to end of symbol. */
+ while (**pp != '\0')
+ {
+ (*pp)++;
+ }
+
+ /* Check for and handle cretinous dbx symbol name continuation! */
+ if ((*pp)[-1] == '\\')
+ {
+ *pp = next_symbol_text ();
+ }
+ else
+ {
+ break;
+ }
+ }
+ return (builtin_type_error);
+}
+
+
+/* Read type information or a type definition; return the type. Even
+ though this routine accepts either type information or a type
+ definition, the distinction is relevant--some parts of stabsread.c
+ assume that type information starts with a digit, '-', or '(' in
+ deciding whether to call read_type. */
+
+struct type *
+read_type (pp, objfile)
+ register char **pp;
+ struct objfile *objfile;
+{
+ register struct type *type = 0;
+ struct type *type1;
+ int typenums[2];
+ int xtypenums[2];
+ char type_descriptor;
+
+ /* Size in bits of type if specified by a type attribute, or -1 if
+ there is no size attribute. */
+ int type_size = -1;
+
+ /* Read type number if present. The type number may be omitted.
+ for instance in a two-dimensional array declared with type
+ "ar1;1;10;ar1;1;10;4". */
+ if ((**pp >= '0' && **pp <= '9')
+ || **pp == '('
+ || **pp == '-')
+ {
+ if (read_type_number (pp, typenums) != 0)
+ return error_type (pp);
+
+ /* Type is not being defined here. Either it already exists,
+ or this is a forward reference to it. dbx_alloc_type handles
+ both cases. */
+ if (**pp != '=')
+ return dbx_alloc_type (typenums, objfile);
+
+ /* Type is being defined here. */
+ /* Skip the '='. */
+ ++(*pp);
+
+ while (**pp == '@')
+ {
+ char *p = *pp + 1;
+ /* It might be a type attribute or a member type. */
+ if (isdigit (*p) || *p == '(' || *p == '-')
+ /* Member type. */
+ break;
+ else
+ {
+ /* Type attributes. */
+ char *attr = p;
+
+ /* Skip to the semicolon. */
+ while (*p != ';' && *p != '\0')
+ ++p;
+ *pp = p;
+ if (*p == '\0')
+ return error_type (pp);
+ else
+ /* Skip the semicolon. */
+ ++*pp;
+
+ switch (*attr)
+ {
+ case 's':
+ type_size = atoi (attr + 1);
+ if (type_size <= 0)
+ type_size = -1;
+ break;
+ default:
+ /* Ignore unrecognized type attributes, so future compilers
+ can invent new ones. */
+ break;
+ }
+ }
+ }
+ /* Skip the type descriptor, we get it below with (*pp)[-1]. */
+ ++(*pp);
+ }
+ else
+ {
+ /* 'typenums=' not present, type is anonymous. Read and return
+ the definition, but don't put it in the type vector. */
+ typenums[0] = typenums[1] = -1;
+ (*pp)++;
+ }
+
+ type_descriptor = (*pp)[-1];
+ switch (type_descriptor)
+ {
+ case 'x':
+ {
+ enum type_code code;
+
+ /* Used to index through file_symbols. */
+ struct pending *ppt;
+ int i;
+
+ /* Name including "struct", etc. */
+ char *type_name;
+
+ {
+ char *from, *to;
+
+ /* Set the type code according to the following letter. */
+ switch ((*pp)[0])
+ {
+ case 's':
+ code = TYPE_CODE_STRUCT;
+ break;
+ case 'u':
+ code = TYPE_CODE_UNION;
+ break;
+ case 'e':
+ code = TYPE_CODE_ENUM;
+ break;
+ default:
+ return error_type (pp);
+ }
+
+ to = type_name = (char *)
+ obstack_alloc (&objfile -> type_obstack,
+ (((char *) strchr (*pp, ':') - (*pp)) + 1));
+
+ /* Copy the name. */
+ from = *pp + 1;
+ while ((*to++ = *from++) != ':')
+ ;
+ *--to = '\0';
+
+ /* Set the pointer ahead of the name which we just read. */
+ *pp = from;
+ }
+
+ /* Now check to see whether the type has already been declared. */
+ /* This is necessary at least in the case where the
+ program says something like
+ struct foo bar[5];
+ The compiler puts out a cross-reference; we better find
+ set the length of the structure correctly so we can
+ set the length of the array. */
+ for (ppt = file_symbols; ppt; ppt = ppt->next)
+ for (i = 0; i < ppt->nsyms; i++)
+ {
+ struct symbol *sym = ppt->symbol[i];
+
+ if (SYMBOL_CLASS (sym) == LOC_TYPEDEF
+ && SYMBOL_NAMESPACE (sym) == STRUCT_NAMESPACE
+ && (TYPE_CODE (SYMBOL_TYPE (sym)) == code)
+ && STREQ (SYMBOL_NAME (sym), type_name))
+ {
+ obstack_free (&objfile -> type_obstack, type_name);
+ type = SYMBOL_TYPE (sym);
+ return type;
+ }
+ }
+
+ /* Didn't find the type to which this refers, so we must
+ be dealing with a forward reference. Allocate a type
+ structure for it, and keep track of it so we can
+ fill in the rest of the fields when we get the full
+ type. */
+ type = dbx_alloc_type (typenums, objfile);
+ TYPE_CODE (type) = code;
+ TYPE_TAG_NAME (type) = type_name;
+ INIT_CPLUS_SPECIFIC(type);
+ TYPE_FLAGS (type) |= TYPE_FLAG_STUB;
+
+ add_undefined_type (type);
+ return type;
+ }
+
+ case '-': /* RS/6000 built-in type */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '(':
+
+ (*pp)--;
+ if (read_type_number (pp, xtypenums) != 0)
+ return error_type (pp);
+
+ if (typenums[0] == xtypenums[0] && typenums[1] == xtypenums[1])
+ /* It's being defined as itself. That means it is "void". */
+ type = init_type (TYPE_CODE_VOID, 0, 0, NULL, objfile);
+ else
+ {
+ struct type *xtype = *dbx_lookup_type (xtypenums);
+
+ /* This can happen if we had '-' followed by a garbage character,
+ for example. */
+ if (xtype == NULL)
+ return error_type (pp);
+
+ /* The type is being defined to another type. So we copy the type.
+ This loses if we copy a C++ class and so we lose track of how
+ the names are mangled (but g++ doesn't output stabs like this
+ now anyway). */
+
+ type = alloc_type (objfile);
+ memcpy (type, xtype, sizeof (struct type));
+
+ /* The idea behind clearing the names is that the only purpose
+ for defining a type to another type is so that the name of
+ one can be different. So we probably don't need to worry much
+ about the case where the compiler doesn't give a name to the
+ new type. */
+ TYPE_NAME (type) = NULL;
+ TYPE_TAG_NAME (type) = NULL;
+ }
+ if (typenums[0] != -1)
+ *dbx_lookup_type (typenums) = type;
+ break;
+
+ /* In the following types, we must be sure to overwrite any existing
+ type that the typenums refer to, rather than allocating a new one
+ and making the typenums point to the new one. This is because there
+ may already be pointers to the existing type (if it had been
+ forward-referenced), and we must change it to a pointer, function,
+ reference, or whatever, *in-place*. */
+
+ case '*':
+ type1 = read_type (pp, objfile);
+ type = make_pointer_type (type1, dbx_lookup_type (typenums));
+ break;
+
+ case '&': /* Reference to another type */
+ type1 = read_type (pp, objfile);
+ type = make_reference_type (type1, dbx_lookup_type (typenums));
+ break;
+
+ case 'f': /* Function returning another type */
+ type1 = read_type (pp, objfile);
+ type = make_function_type (type1, dbx_lookup_type (typenums));
+ break;
+
+ case 'k': /* Const qualifier on some type (Sun) */
+ type = read_type (pp, objfile);
+ /* FIXME! For now, we ignore const and volatile qualifiers. */
+ break;
+
+ case 'B': /* Volatile qual on some type (Sun) */
+ type = read_type (pp, objfile);
+ /* FIXME! For now, we ignore const and volatile qualifiers. */
+ break;
+
+/* FIXME -- we should be doing smash_to_XXX types here. */
+ case '@': /* Member (class & variable) type */
+ {
+ struct type *domain = read_type (pp, objfile);
+ struct type *memtype;
+
+ if (**pp != ',')
+ /* Invalid member type data format. */
+ return error_type (pp);
+ ++*pp;
+
+ memtype = read_type (pp, objfile);
+ type = dbx_alloc_type (typenums, objfile);
+ smash_to_member_type (type, domain, memtype);
+ }
+ break;
+
+ case '#': /* Method (class & fn) type */
+ if ((*pp)[0] == '#')
+ {
+ /* We'll get the parameter types from the name. */
+ struct type *return_type;
+
+ (*pp)++;
+ return_type = read_type (pp, objfile);
+ if (*(*pp)++ != ';')
+ complain (&invalid_member_complaint, symnum);
+ type = allocate_stub_method (return_type);
+ if (typenums[0] != -1)
+ *dbx_lookup_type (typenums) = type;
+ }
+ else
+ {
+ struct type *domain = read_type (pp, objfile);
+ struct type *return_type;
+ struct type **args;
+
+ if (**pp != ',')
+ /* Invalid member type data format. */
+ return error_type (pp);
+ else
+ ++(*pp);
+
+ return_type = read_type (pp, objfile);
+ args = read_args (pp, ';', objfile);
+ type = dbx_alloc_type (typenums, objfile);
+ smash_to_method_type (type, domain, return_type, args);
+ }
+ break;
+
+ case 'r': /* Range type */
+ type = read_range_type (pp, typenums, objfile);
+ if (typenums[0] != -1)
+ *dbx_lookup_type (typenums) = type;
+ break;
+
+ case 'b': /* Sun ACC builtin int type */
+ type = read_sun_builtin_type (pp, typenums, objfile);
+ if (typenums[0] != -1)
+ *dbx_lookup_type (typenums) = type;
+ break;
+
+ case 'R': /* Sun ACC builtin float type */
+ type = read_sun_floating_type (pp, typenums, objfile);
+ if (typenums[0] != -1)
+ *dbx_lookup_type (typenums) = type;
+ break;
+
+ case 'e': /* Enumeration type */
+ type = dbx_alloc_type (typenums, objfile);
+ type = read_enum_type (pp, type, objfile);
+ if (typenums[0] != -1)
+ *dbx_lookup_type (typenums) = type;
+ break;
+
+ case 's': /* Struct type */
+ case 'u': /* Union type */
+ type = dbx_alloc_type (typenums, objfile);
+ if (!TYPE_NAME (type))
+ {
+ TYPE_NAME (type) = type_synonym_name;
+ }
+ type_synonym_name = NULL;
+ switch (type_descriptor)
+ {
+ case 's':
+ TYPE_CODE (type) = TYPE_CODE_STRUCT;
+ break;
+ case 'u':
+ TYPE_CODE (type) = TYPE_CODE_UNION;
+ break;
+ }
+ type = read_struct_type (pp, type, objfile);
+ break;
+
+ case 'a': /* Array type */
+ if (**pp != 'r')
+ return error_type (pp);
+ ++*pp;
+
+ type = dbx_alloc_type (typenums, objfile);
+ type = read_array_type (pp, type, objfile);
+ break;
+
+ default:
+ --*pp; /* Go back to the symbol in error */
+ /* Particularly important if it was \0! */
+ return error_type (pp);
+ }
+
+ if (type == 0)
+ {
+ warning ("GDB internal error, type is NULL in stabsread.c\n");
+ return error_type (pp);
+ }
+
+ /* Size specified in a type attribute overrides any other size. */
+ if (type_size != -1)
+ TYPE_LENGTH (type) = type_size / TARGET_CHAR_BIT;
+
+ return type;
+}
+
+/* RS/6000 xlc/dbx combination uses a set of builtin types, starting from -1.
+ Return the proper type node for a given builtin type number. */
+
+static struct type *
+rs6000_builtin_type (typenum)
+ int typenum;
+{
+ /* We recognize types numbered from -NUMBER_RECOGNIZED to -1. */
+#define NUMBER_RECOGNIZED 30
+ /* This includes an empty slot for type number -0. */
+ static struct type *negative_types[NUMBER_RECOGNIZED + 1];
+ struct type *rettype = NULL;
+
+ if (typenum >= 0 || typenum < -NUMBER_RECOGNIZED)
+ {
+ complain (&rs6000_builtin_complaint, typenum);
+ return builtin_type_error;
+ }
+ if (negative_types[-typenum] != NULL)
+ return negative_types[-typenum];
+
+#if TARGET_CHAR_BIT != 8
+ #error This code wrong for TARGET_CHAR_BIT not 8
+ /* These definitions all assume that TARGET_CHAR_BIT is 8. I think
+ that if that ever becomes not true, the correct fix will be to
+ make the size in the struct type to be in bits, not in units of
+ TARGET_CHAR_BIT. */
+#endif
+
+ switch (-typenum)
+ {
+ case 1:
+ /* The size of this and all the other types are fixed, defined
+ by the debugging format. If there is a type called "int" which
+ is other than 32 bits, then it should use a new negative type
+ number (or avoid negative type numbers for that case).
+ See stabs.texinfo. */
+ rettype = init_type (TYPE_CODE_INT, 4, 0, "int", NULL);
+ break;
+ case 2:
+ rettype = init_type (TYPE_CODE_INT, 1, 0, "char", NULL);
+ break;
+ case 3:
+ rettype = init_type (TYPE_CODE_INT, 2, 0, "short", NULL);
+ break;
+ case 4:
+ rettype = init_type (TYPE_CODE_INT, 4, 0, "long", NULL);
+ break;
+ case 5:
+ rettype = init_type (TYPE_CODE_INT, 1, TYPE_FLAG_UNSIGNED,
+ "unsigned char", NULL);
+ break;
+ case 6:
+ rettype = init_type (TYPE_CODE_INT, 1, 0, "signed char", NULL);
+ break;
+ case 7:
+ rettype = init_type (TYPE_CODE_INT, 2, TYPE_FLAG_UNSIGNED,
+ "unsigned short", NULL);
+ break;
+ case 8:
+ rettype = init_type (TYPE_CODE_INT, 4, TYPE_FLAG_UNSIGNED,
+ "unsigned int", NULL);
+ break;
+ case 9:
+ rettype = init_type (TYPE_CODE_INT, 4, TYPE_FLAG_UNSIGNED,
+ "unsigned", NULL);
+ case 10:
+ rettype = init_type (TYPE_CODE_INT, 4, TYPE_FLAG_UNSIGNED,
+ "unsigned long", NULL);
+ break;
+ case 11:
+ rettype = init_type (TYPE_CODE_VOID, 0, 0, "void", NULL);
+ break;
+ case 12:
+ /* IEEE single precision (32 bit). */
+ rettype = init_type (TYPE_CODE_FLT, 4, 0, "float", NULL);
+ break;
+ case 13:
+ /* IEEE double precision (64 bit). */
+ rettype = init_type (TYPE_CODE_FLT, 8, 0, "double", NULL);
+ break;
+ case 14:
+ /* This is an IEEE double on the RS/6000, and different machines with
+ different sizes for "long double" should use different negative
+ type numbers. See stabs.texinfo. */
+ rettype = init_type (TYPE_CODE_FLT, 8, 0, "long double", NULL);
+ break;
+ case 15:
+ rettype = init_type (TYPE_CODE_INT, 4, 0, "integer", NULL);
+ break;
+ case 16:
+ rettype = init_type (TYPE_CODE_BOOL, 4, 0, "boolean", NULL);
+ break;
+ case 17:
+ rettype = init_type (TYPE_CODE_FLT, 4, 0, "short real", NULL);
+ break;
+ case 18:
+ rettype = init_type (TYPE_CODE_FLT, 8, 0, "real", NULL);
+ break;
+ case 19:
+ rettype = init_type (TYPE_CODE_ERROR, 0, 0, "stringptr", NULL);
+ break;
+ case 20:
+ rettype = init_type (TYPE_CODE_CHAR, 1, TYPE_FLAG_UNSIGNED,
+ "character", NULL);
+ break;
+ case 21:
+ rettype = init_type (TYPE_CODE_BOOL, 1, TYPE_FLAG_UNSIGNED,
+ "logical*1", NULL);
+ break;
+ case 22:
+ rettype = init_type (TYPE_CODE_BOOL, 2, TYPE_FLAG_UNSIGNED,
+ "logical*2", NULL);
+ break;
+ case 23:
+ rettype = init_type (TYPE_CODE_BOOL, 4, TYPE_FLAG_UNSIGNED,
+ "logical*4", NULL);
+ break;
+ case 24:
+ rettype = init_type (TYPE_CODE_BOOL, 4, TYPE_FLAG_UNSIGNED,
+ "logical", NULL);
+ break;
+ case 25:
+ /* Complex type consisting of two IEEE single precision values. */
+ rettype = init_type (TYPE_CODE_ERROR, 8, 0, "complex", NULL);
+ break;
+ case 26:
+ /* Complex type consisting of two IEEE double precision values. */
+ rettype = init_type (TYPE_CODE_ERROR, 16, 0, "double complex", NULL);
+ break;
+ case 27:
+ rettype = init_type (TYPE_CODE_INT, 1, 0, "integer*1", NULL);
+ break;
+ case 28:
+ rettype = init_type (TYPE_CODE_INT, 2, 0, "integer*2", NULL);
+ break;
+ case 29:
+ rettype = init_type (TYPE_CODE_INT, 4, 0, "integer*4", NULL);
+ break;
+ case 30:
+ rettype = init_type (TYPE_CODE_CHAR, 2, 0, "wchar", NULL);
+ break;
+ }
+ negative_types[-typenum] = rettype;
+ return rettype;
+}
+
+/* This page contains subroutines of read_type. */
+
+#define VISIBILITY_PRIVATE '0' /* Stabs character for private field */
+#define VISIBILITY_PROTECTED '1' /* Stabs character for protected fld */
+#define VISIBILITY_PUBLIC '2' /* Stabs character for public field */
+
+/* Read member function stabs info for C++ classes. The form of each member
+ function data is:
+
+ NAME :: TYPENUM[=type definition] ARGS : PHYSNAME ;
+
+ An example with two member functions is:
+
+ afunc1::20=##15;:i;2A.;afunc2::20:i;2A.;
+
+ For the case of overloaded operators, the format is op$::*.funcs, where
+ $ is the CPLUS_MARKER (usually '$'), `*' holds the place for an operator
+ name (such as `+=') and `.' marks the end of the operator name.
+
+ Returns 1 for success, 0 for failure. */
+
+static int
+read_member_functions (fip, pp, type, objfile)
+ struct field_info *fip;
+ char **pp;
+ struct type *type;
+ struct objfile *objfile;
+{
+ int nfn_fields = 0;
+ int length = 0;
+ /* Total number of member functions defined in this class. If the class
+ defines two `f' functions, and one `g' function, then this will have
+ the value 3. */
+ int total_length = 0;
+ int i;
+ struct next_fnfield
+ {
+ struct next_fnfield *next;
+ struct fn_field fn_field;
+ } *sublist;
+ struct type *look_ahead_type;
+ struct next_fnfieldlist *new_fnlist;
+ struct next_fnfield *new_sublist;
+ char *main_fn_name;
+ register char *p;
+
+ /* Process each list until we find something that is not a member function
+ or find the end of the functions. */
+
+ while (**pp != ';')
+ {
+ /* We should be positioned at the start of the function name.
+ Scan forward to find the first ':' and if it is not the
+ first of a "::" delimiter, then this is not a member function. */
+ p = *pp;
+ while (*p != ':')
+ {
+ p++;
+ }
+ if (p[1] != ':')
+ {
+ break;
+ }
+
+ sublist = NULL;
+ look_ahead_type = NULL;
+ length = 0;
+
+ new_fnlist = (struct next_fnfieldlist *)
+ xmalloc (sizeof (struct next_fnfieldlist));
+ make_cleanup (free, new_fnlist);
+ memset (new_fnlist, 0, sizeof (struct next_fnfieldlist));
+
+ if ((*pp)[0] == 'o' && (*pp)[1] == 'p' && (*pp)[2] == CPLUS_MARKER)
+ {
+ /* This is a completely wierd case. In order to stuff in the
+ names that might contain colons (the usual name delimiter),
+ Mike Tiemann defined a different name format which is
+ signalled if the identifier is "op$". In that case, the
+ format is "op$::XXXX." where XXXX is the name. This is
+ used for names like "+" or "=". YUUUUUUUK! FIXME! */
+ /* This lets the user type "break operator+".
+ We could just put in "+" as the name, but that wouldn't
+ work for "*". */
+ static char opname[32] = {'o', 'p', CPLUS_MARKER};
+ char *o = opname + 3;
+
+ /* Skip past '::'. */
+ *pp = p + 2;
+
+ STABS_CONTINUE (pp);
+ p = *pp;
+ while (*p != '.')
+ {
+ *o++ = *p++;
+ }
+ main_fn_name = savestring (opname, o - opname);
+ /* Skip past '.' */
+ *pp = p + 1;
+ }
+ else
+ {
+ main_fn_name = savestring (*pp, p - *pp);
+ /* Skip past '::'. */
+ *pp = p + 2;
+ }
+ new_fnlist -> fn_fieldlist.name = main_fn_name;
+
+ do
+ {
+ new_sublist =
+ (struct next_fnfield *) xmalloc (sizeof (struct next_fnfield));
+ make_cleanup (free, new_sublist);
+ memset (new_sublist, 0, sizeof (struct next_fnfield));
+
+ /* Check for and handle cretinous dbx symbol name continuation! */
+ if (look_ahead_type == NULL)
+ {
+ /* Normal case. */
+ STABS_CONTINUE (pp);
+
+ new_sublist -> fn_field.type = read_type (pp, objfile);
+ if (**pp != ':')
+ {
+ /* Invalid symtab info for member function. */
+ return 0;
+ }
+ }
+ else
+ {
+ /* g++ version 1 kludge */
+ new_sublist -> fn_field.type = look_ahead_type;
+ look_ahead_type = NULL;
+ }
+
+ (*pp)++;
+ p = *pp;
+ while (*p != ';')
+ {
+ p++;
+ }
+
+ /* If this is just a stub, then we don't have the real name here. */
+
+ if (TYPE_FLAGS (new_sublist -> fn_field.type) & TYPE_FLAG_STUB)
+ {
+ if (!TYPE_DOMAIN_TYPE (new_sublist -> fn_field.type))
+ TYPE_DOMAIN_TYPE (new_sublist -> fn_field.type) = type;
+ new_sublist -> fn_field.is_stub = 1;
+ }
+ new_sublist -> fn_field.physname = savestring (*pp, p - *pp);
+ *pp = p + 1;
+
+ /* Set this member function's visibility fields. */
+ switch (*(*pp)++)
+ {
+ case VISIBILITY_PRIVATE:
+ new_sublist -> fn_field.is_private = 1;
+ break;
+ case VISIBILITY_PROTECTED:
+ new_sublist -> fn_field.is_protected = 1;
+ break;
+ }
+
+ STABS_CONTINUE (pp);
+ switch (**pp)
+ {
+ case 'A': /* Normal functions. */
+ new_sublist -> fn_field.is_const = 0;
+ new_sublist -> fn_field.is_volatile = 0;
+ (*pp)++;
+ break;
+ case 'B': /* `const' member functions. */
+ new_sublist -> fn_field.is_const = 1;
+ new_sublist -> fn_field.is_volatile = 0;
+ (*pp)++;
+ break;
+ case 'C': /* `volatile' member function. */
+ new_sublist -> fn_field.is_const = 0;
+ new_sublist -> fn_field.is_volatile = 1;
+ (*pp)++;
+ break;
+ case 'D': /* `const volatile' member function. */
+ new_sublist -> fn_field.is_const = 1;
+ new_sublist -> fn_field.is_volatile = 1;
+ (*pp)++;
+ break;
+ case '*': /* File compiled with g++ version 1 -- no info */
+ case '?':
+ case '.':
+ break;
+ default:
+ complain (&const_vol_complaint, **pp);
+ break;
+ }
+
+ switch (*(*pp)++)
+ {
+ case '*':
+ {
+ int nbits;
+ /* virtual member function, followed by index.
+ The sign bit is set to distinguish pointers-to-methods
+ from virtual function indicies. Since the array is
+ in words, the quantity must be shifted left by 1
+ on 16 bit machine, and by 2 on 32 bit machine, forcing
+ the sign bit out, and usable as a valid index into
+ the array. Remove the sign bit here. */
+ new_sublist -> fn_field.voffset =
+ (0x7fffffff & read_huge_number (pp, ';', &nbits)) + 2;
+ if (nbits != 0)
+ return 0;
+
+ STABS_CONTINUE (pp);
+ if (**pp == ';' || **pp == '\0')
+ {
+ /* Must be g++ version 1. */
+ new_sublist -> fn_field.fcontext = 0;
+ }
+ else
+ {
+ /* Figure out from whence this virtual function came.
+ It may belong to virtual function table of
+ one of its baseclasses. */
+ look_ahead_type = read_type (pp, objfile);
+ if (**pp == ':')
+ {
+ /* g++ version 1 overloaded methods. */
+ }
+ else
+ {
+ new_sublist -> fn_field.fcontext = look_ahead_type;
+ if (**pp != ';')
+ {
+ return 0;
+ }
+ else
+ {
+ ++*pp;
+ }
+ look_ahead_type = NULL;
+ }
+ }
+ break;
+ }
+ case '?':
+ /* static member function. */
+ new_sublist -> fn_field.voffset = VOFFSET_STATIC;
+ if (strncmp (new_sublist -> fn_field.physname,
+ main_fn_name, strlen (main_fn_name)))
+ {
+ new_sublist -> fn_field.is_stub = 1;
+ }
+ break;
+
+ default:
+ /* error */
+ complain (&member_fn_complaint, (*pp)[-1]);
+ /* Fall through into normal member function. */
+
+ case '.':
+ /* normal member function. */
+ new_sublist -> fn_field.voffset = 0;
+ new_sublist -> fn_field.fcontext = 0;
+ break;
+ }
+
+ new_sublist -> next = sublist;
+ sublist = new_sublist;
+ length++;
+ STABS_CONTINUE (pp);
+ }
+ while (**pp != ';' && **pp != '\0');
+
+ (*pp)++;
+
+ new_fnlist -> fn_fieldlist.fn_fields = (struct fn_field *)
+ obstack_alloc (&objfile -> type_obstack,
+ sizeof (struct fn_field) * length);
+ memset (new_fnlist -> fn_fieldlist.fn_fields, 0,
+ sizeof (struct fn_field) * length);
+ for (i = length; (i--, sublist); sublist = sublist -> next)
+ {
+ new_fnlist -> fn_fieldlist.fn_fields[i] = sublist -> fn_field;
+ }
+
+ new_fnlist -> fn_fieldlist.length = length;
+ new_fnlist -> next = fip -> fnlist;
+ fip -> fnlist = new_fnlist;
+ nfn_fields++;
+ total_length += length;
+ STABS_CONTINUE (pp);
+ }
+
+ if (nfn_fields)
+ {
+ ALLOCATE_CPLUS_STRUCT_TYPE (type);
+ TYPE_FN_FIELDLISTS (type) = (struct fn_fieldlist *)
+ TYPE_ALLOC (type, sizeof (struct fn_fieldlist) * nfn_fields);
+ memset (TYPE_FN_FIELDLISTS (type), 0,
+ sizeof (struct fn_fieldlist) * nfn_fields);
+ TYPE_NFN_FIELDS (type) = nfn_fields;
+ TYPE_NFN_FIELDS_TOTAL (type) = total_length;
+ }
+
+ return 1;
+}
+
+/* Special GNU C++ name.
+
+ Returns 1 for success, 0 for failure. "failure" means that we can't
+ keep parsing and it's time for error_type(). */
+
+static int
+read_cpp_abbrev (fip, pp, type, objfile)
+ struct field_info *fip;
+ char **pp;
+ struct type *type;
+ struct objfile *objfile;
+{
+ register char *p;
+ char *name;
+ char cpp_abbrev;
+ struct type *context;
+
+ p = *pp;
+ if (*++p == 'v')
+ {
+ name = NULL;
+ cpp_abbrev = *++p;
+
+ *pp = p + 1;
+
+ /* At this point, *pp points to something like "22:23=*22...",
+ where the type number before the ':' is the "context" and
+ everything after is a regular type definition. Lookup the
+ type, find it's name, and construct the field name. */
+
+ context = read_type (pp, objfile);
+
+ switch (cpp_abbrev)
+ {
+ case 'f': /* $vf -- a virtual function table pointer */
+ fip->list->field.name =
+ obconcat (&objfile->type_obstack, vptr_name, "", "");
+ break;
+
+ case 'b': /* $vb -- a virtual bsomethingorother */
+ name = type_name_no_tag (context);
+ if (name == NULL)
+ {
+ complain (&invalid_cpp_type_complaint, symnum);
+ name = "FOO";
+ }
+ fip->list->field.name =
+ obconcat (&objfile->type_obstack, vb_name, name, "");
+ break;
+
+ default:
+ complain (&invalid_cpp_abbrev_complaint, *pp);
+ fip->list->field.name =
+ obconcat (&objfile->type_obstack,
+ "INVALID_CPLUSPLUS_ABBREV", "", "");
+ break;
+ }
+
+ /* At this point, *pp points to the ':'. Skip it and read the
+ field type. */
+
+ p = ++(*pp);
+ if (p[-1] != ':')
+ {
+ complain (&invalid_cpp_abbrev_complaint, *pp);
+ return 0;
+ }
+ fip->list->field.type = read_type (pp, objfile);
+ if (**pp == ',')
+ (*pp)++; /* Skip the comma. */
+ else
+ return 0;
+
+ {
+ int nbits;
+ fip->list->field.bitpos = read_huge_number (pp, ';', &nbits);
+ if (nbits != 0)
+ return 0;
+ }
+ /* This field is unpacked. */
+ fip->list->field.bitsize = 0;
+ fip->list->visibility = VISIBILITY_PRIVATE;
+ }
+ else
+ {
+ complain (&invalid_cpp_abbrev_complaint, *pp);
+ /* We have no idea what syntax an unrecognized abbrev would have, so
+ better return 0. If we returned 1, we would need to at least advance
+ *pp to avoid an infinite loop. */
+ return 0;
+ }
+ return 1;
+}
+
+static void
+read_one_struct_field (fip, pp, p, type, objfile)
+ struct field_info *fip;
+ char **pp;
+ char *p;
+ struct type *type;
+ struct objfile *objfile;
+{
+ fip -> list -> field.name =
+ obsavestring (*pp, p - *pp, &objfile -> type_obstack);
+ *pp = p + 1;
+
+ /* This means we have a visibility for a field coming. */
+ if (**pp == '/')
+ {
+ (*pp)++;
+ fip -> list -> visibility = *(*pp)++;
+ switch (fip -> list -> visibility)
+ {
+ case VISIBILITY_PRIVATE:
+ case VISIBILITY_PROTECTED:
+ break;
+
+ case VISIBILITY_PUBLIC:
+ /* Nothing to do */
+ break;
+
+ default:
+ /* Unknown visibility specifier. */
+ complain (&stabs_general_complaint,
+ "unknown visibility specifier");
+ return;
+ break;
+ }
+ }
+ else
+ {
+ /* normal dbx-style format, no explicit visibility */
+ fip -> list -> visibility = VISIBILITY_PUBLIC;
+ }
+
+ fip -> list -> field.type = read_type (pp, objfile);
+ if (**pp == ':')
+ {
+ p = ++(*pp);
+#if 0
+ /* Possible future hook for nested types. */
+ if (**pp == '!')
+ {
+ fip -> list -> field.bitpos = (long)-2; /* nested type */
+ p = ++(*pp);
+ }
+ else
+#endif
+ {
+ /* Static class member. */
+ fip -> list -> field.bitpos = (long) -1;
+ }
+ while (*p != ';')
+ {
+ p++;
+ }
+ fip -> list -> field.bitsize = (long) savestring (*pp, p - *pp);
+ *pp = p + 1;
+ return;
+ }
+ else if (**pp != ',')
+ {
+ /* Bad structure-type format. */
+ complain (&stabs_general_complaint, "bad structure-type format");
+ return;
+ }
+
+ (*pp)++; /* Skip the comma. */
+
+ {
+ int nbits;
+ fip -> list -> field.bitpos = read_huge_number (pp, ',', &nbits);
+ if (nbits != 0)
+ {
+ complain (&stabs_general_complaint, "bad structure-type format");
+ return;
+ }
+ fip -> list -> field.bitsize = read_huge_number (pp, ';', &nbits);
+ if (nbits != 0)
+ {
+ complain (&stabs_general_complaint, "bad structure-type format");
+ return;
+ }
+ }
+#if 0
+ /* FIXME-tiemann: Can't the compiler put out something which
+ lets us distinguish these? (or maybe just not put out anything
+ for the field). What is the story here? What does the compiler
+ really do? Also, patch gdb.texinfo for this case; I document
+ it as a possible problem there. Search for "DBX-style". */
+
+ /* This is wrong because this is identical to the symbols
+ produced for GCC 0-size arrays. For example:
+ typedef union {
+ int num;
+ char str[0];
+ } foo;
+ The code which dumped core in such circumstances should be
+ fixed not to dump core. */
+
+ /* g++ -g0 can put out bitpos & bitsize zero for a static
+ field. This does not give us any way of getting its
+ class, so we can't know its name. But we can just
+ ignore the field so we don't dump core and other nasty
+ stuff. */
+ if (fip -> list -> field.bitpos == 0 && fip -> list -> field.bitsize == 0)
+ {
+ complain (&dbx_class_complaint);
+ /* Ignore this field. */
+ fip -> list = fip -> list -> next;
+ }
+ else
+#endif /* 0 */
+ {
+ /* Detect an unpacked field and mark it as such.
+ dbx gives a bit size for all fields.
+ Note that forward refs cannot be packed,
+ and treat enums as if they had the width of ints. */
+
+ if (TYPE_CODE (fip -> list -> field.type) != TYPE_CODE_INT
+ && TYPE_CODE (fip -> list -> field.type) != TYPE_CODE_ENUM)
+ {
+ fip -> list -> field.bitsize = 0;
+ }
+ if ((fip -> list -> field.bitsize
+ == TARGET_CHAR_BIT * TYPE_LENGTH (fip -> list -> field.type)
+ || (TYPE_CODE (fip -> list -> field.type) == TYPE_CODE_ENUM
+ && (fip -> list -> field.bitsize
+ == TARGET_INT_BIT)
+ )
+ )
+ &&
+ fip -> list -> field.bitpos % 8 == 0)
+ {
+ fip -> list -> field.bitsize = 0;
+ }
+ }
+}
+
+
+/* Read struct or class data fields. They have the form:
+
+ NAME : [VISIBILITY] TYPENUM , BITPOS , BITSIZE ;
+
+ At the end, we see a semicolon instead of a field.
+
+ In C++, this may wind up being NAME:?TYPENUM:PHYSNAME; for
+ a static field.
+
+ The optional VISIBILITY is one of:
+
+ '/0' (VISIBILITY_PRIVATE)
+ '/1' (VISIBILITY_PROTECTED)
+ '/2' (VISIBILITY_PUBLIC)
+
+ or nothing, for C style fields with public visibility.
+
+ Returns 1 for success, 0 for failure. */
+
+static int
+read_struct_fields (fip, pp, type, objfile)
+ struct field_info *fip;
+ char **pp;
+ struct type *type;
+ struct objfile *objfile;
+{
+ register char *p;
+ struct nextfield *new;
+
+ /* We better set p right now, in case there are no fields at all... */
+
+ p = *pp;
+
+ /* Read each data member type until we find the terminating ';' at the end of
+ the data member list, or break for some other reason such as finding the
+ start of the member function list. */
+
+ while (**pp != ';')
+ {
+ STABS_CONTINUE (pp);
+ /* Get space to record the next field's data. */
+ new = (struct nextfield *) xmalloc (sizeof (struct nextfield));
+ make_cleanup (free, new);
+ memset (new, 0, sizeof (struct nextfield));
+ new -> next = fip -> list;
+ fip -> list = new;
+
+ /* Get the field name. */
+ p = *pp;
+ /* If is starts with CPLUS_MARKER it is a special abbreviation, unless
+ the CPLUS_MARKER is followed by an underscore, in which case it is
+ just the name of an anonymous type, which we should handle like any
+ other type name. */
+ if (*p == CPLUS_MARKER && p[1] != '_')
+ {
+ if (!read_cpp_abbrev (fip, pp, type, objfile))
+ return 0;
+ continue;
+ }
+
+ /* Look for the ':' that separates the field name from the field
+ values. Data members are delimited by a single ':', while member
+ functions are delimited by a pair of ':'s. When we hit the member
+ functions (if any), terminate scan loop and return. */
+
+ while (*p != ':' && *p != '\0')
+ {
+ p++;
+ }
+ if (*p == '\0')
+ return 0;
+
+ /* Check to see if we have hit the member functions yet. */
+ if (p[1] == ':')
+ {
+ break;
+ }
+ read_one_struct_field (fip, pp, p, type, objfile);
+ }
+ if (p[1] == ':')
+ {
+ /* chill the list of fields: the last entry (at the head) is a
+ partially constructed entry which we now scrub. */
+ fip -> list = fip -> list -> next;
+ }
+ return 1;
+}
+
+/* The stabs for C++ derived classes contain baseclass information which
+ is marked by a '!' character after the total size. This function is
+ called when we encounter the baseclass marker, and slurps up all the
+ baseclass information.
+
+ Immediately following the '!' marker is the number of base classes that
+ the class is derived from, followed by information for each base class.
+ For each base class, there are two visibility specifiers, a bit offset
+ to the base class information within the derived class, a reference to
+ the type for the base class, and a terminating semicolon.
+
+ A typical example, with two base classes, would be "!2,020,19;0264,21;".
+ ^^ ^ ^ ^ ^ ^ ^
+ Baseclass information marker __________________|| | | | | | |
+ Number of baseclasses __________________________| | | | | | |
+ Visibility specifiers (2) ________________________| | | | | |
+ Offset in bits from start of class _________________| | | | |
+ Type number for base class ___________________________| | | |
+ Visibility specifiers (2) _______________________________| | |
+ Offset in bits from start of class ________________________| |
+ Type number of base class ____________________________________|
+
+ Return 1 for success, 0 for (error-type-inducing) failure. */
+
+static int
+read_baseclasses (fip, pp, type, objfile)
+ struct field_info *fip;
+ char **pp;
+ struct type *type;
+ struct objfile *objfile;
+{
+ int i;
+ struct nextfield *new;
+
+ if (**pp != '!')
+ {
+ return 1;
+ }
+ else
+ {
+ /* Skip the '!' baseclass information marker. */
+ (*pp)++;
+ }
+
+ ALLOCATE_CPLUS_STRUCT_TYPE (type);
+ {
+ int nbits;
+ TYPE_N_BASECLASSES (type) = read_huge_number (pp, ',', &nbits);
+ if (nbits != 0)
+ return 0;
+ }
+
+#if 0
+ /* Some stupid compilers have trouble with the following, so break
+ it up into simpler expressions. */
+ TYPE_FIELD_VIRTUAL_BITS (type) = (B_TYPE *)
+ TYPE_ALLOC (type, B_BYTES (TYPE_N_BASECLASSES (type)));
+#else
+ {
+ int num_bytes = B_BYTES (TYPE_N_BASECLASSES (type));
+ char *pointer;
+
+ pointer = (char *) TYPE_ALLOC (type, num_bytes);
+ TYPE_FIELD_VIRTUAL_BITS (type) = (B_TYPE *) pointer;
+ }
+#endif /* 0 */
+
+ B_CLRALL (TYPE_FIELD_VIRTUAL_BITS (type), TYPE_N_BASECLASSES (type));
+
+ for (i = 0; i < TYPE_N_BASECLASSES (type); i++)
+ {
+ new = (struct nextfield *) xmalloc (sizeof (struct nextfield));
+ make_cleanup (free, new);
+ memset (new, 0, sizeof (struct nextfield));
+ new -> next = fip -> list;
+ fip -> list = new;
+ new -> field.bitsize = 0; /* this should be an unpacked field! */
+
+ STABS_CONTINUE (pp);
+ switch (*(*pp)++)
+ {
+ case '0':
+ /* Nothing to do. */
+ break;
+ case '1':
+ SET_TYPE_FIELD_VIRTUAL (type, i);
+ break;
+ default:
+ /* Bad visibility format. */
+ return 0;
+ }
+
+ new -> visibility = *(*pp)++;
+ switch (new -> visibility)
+ {
+ case VISIBILITY_PRIVATE:
+ case VISIBILITY_PROTECTED:
+ case VISIBILITY_PUBLIC:
+ break;
+ default:
+ /* Bad visibility format. */
+ return 0;
+ }
+
+ {
+ int nbits;
+
+ /* The remaining value is the bit offset of the portion of the object
+ corresponding to this baseclass. Always zero in the absence of
+ multiple inheritance. */
+
+ new -> field.bitpos = read_huge_number (pp, ',', &nbits);
+ if (nbits != 0)
+ return 0;
+ }
+
+ /* The last piece of baseclass information is the type of the
+ base class. Read it, and remember it's type name as this
+ field's name. */
+
+ new -> field.type = read_type (pp, objfile);
+ new -> field.name = type_name_no_tag (new -> field.type);
+
+ /* skip trailing ';' and bump count of number of fields seen */
+ if (**pp == ';')
+ (*pp)++;
+ else
+ return 0;
+ }
+ return 1;
+}
+
+/* The tail end of stabs for C++ classes that contain a virtual function
+ pointer contains a tilde, a %, and a type number.
+ The type number refers to the base class (possibly this class itself) which
+ contains the vtable pointer for the current class.
+
+ This function is called when we have parsed all the method declarations,
+ so we can look for the vptr base class info. */
+
+static int
+read_tilde_fields (fip, pp, type, objfile)
+ struct field_info *fip;
+ char **pp;
+ struct type *type;
+ struct objfile *objfile;
+{
+ register char *p;
+
+ STABS_CONTINUE (pp);
+
+ /* If we are positioned at a ';', then skip it. */
+ if (**pp == ';')
+ {
+ (*pp)++;
+ }
+
+ if (**pp == '~')
+ {
+ (*pp)++;
+
+ if (**pp == '=' || **pp == '+' || **pp == '-')
+ {
+ /* Obsolete flags that used to indicate the presence
+ of constructors and/or destructors. */
+ (*pp)++;
+ }
+
+ /* Read either a '%' or the final ';'. */
+ if (*(*pp)++ == '%')
+ {
+ /* The next number is the type number of the base class
+ (possibly our own class) which supplies the vtable for
+ this class. Parse it out, and search that class to find
+ its vtable pointer, and install those into TYPE_VPTR_BASETYPE
+ and TYPE_VPTR_FIELDNO. */
+
+ struct type *t;
+ int i;
+
+ t = read_type (pp, objfile);
+ p = (*pp)++;
+ while (*p != '\0' && *p != ';')
+ {
+ p++;
+ }
+ if (*p == '\0')
+ {
+ /* Premature end of symbol. */
+ return 0;
+ }
+
+ TYPE_VPTR_BASETYPE (type) = t;
+ if (type == t) /* Our own class provides vtbl ptr */
+ {
+ for (i = TYPE_NFIELDS (t) - 1;
+ i >= TYPE_N_BASECLASSES (t);
+ --i)
+ {
+ if (! strncmp (TYPE_FIELD_NAME (t, i), vptr_name,
+ sizeof (vptr_name) - 1))
+ {
+ TYPE_VPTR_FIELDNO (type) = i;
+ goto gotit;
+ }
+ }
+ /* Virtual function table field not found. */
+ complain (&vtbl_notfound_complaint, TYPE_NAME (type));
+ return 0;
+ }
+ else
+ {
+ TYPE_VPTR_FIELDNO (type) = TYPE_VPTR_FIELDNO (t);
+ }
+
+ gotit:
+ *pp = p + 1;
+ }
+ }
+ return 1;
+}
+
+static int
+attach_fn_fields_to_type (fip, type)
+ struct field_info *fip;
+ register struct type *type;
+{
+ register int n;
+
+ for (n = 0; n < TYPE_N_BASECLASSES (type); n++)
+ {
+ if (TYPE_CODE (TYPE_BASECLASS (type, n)) == TYPE_CODE_UNDEF)
+ {
+ /* @@ Memory leak on objfile -> type_obstack? */
+ return 0;
+ }
+ TYPE_NFN_FIELDS_TOTAL (type) +=
+ TYPE_NFN_FIELDS_TOTAL (TYPE_BASECLASS (type, n));
+ }
+
+ for (n = TYPE_NFN_FIELDS (type);
+ fip -> fnlist != NULL;
+ fip -> fnlist = fip -> fnlist -> next)
+ {
+ --n; /* Circumvent Sun3 compiler bug */
+ TYPE_FN_FIELDLISTS (type)[n] = fip -> fnlist -> fn_fieldlist;
+ }
+ return 1;
+}
+
+/* Create the vector of fields, and record how big it is.
+ We need this info to record proper virtual function table information
+ for this class's virtual functions. */
+
+static int
+attach_fields_to_type (fip, type, objfile)
+ struct field_info *fip;
+ register struct type *type;
+ struct objfile *objfile;
+{
+ register int nfields = 0;
+ register int non_public_fields = 0;
+ register struct nextfield *scan;
+
+ /* Count up the number of fields that we have, as well as taking note of
+ whether or not there are any non-public fields, which requires us to
+ allocate and build the private_field_bits and protected_field_bits
+ bitfields. */
+
+ for (scan = fip -> list; scan != NULL; scan = scan -> next)
+ {
+ nfields++;
+ if (scan -> visibility != VISIBILITY_PUBLIC)
+ {
+ non_public_fields++;
+ }
+ }
+
+ /* Now we know how many fields there are, and whether or not there are any
+ non-public fields. Record the field count, allocate space for the
+ array of fields, and create blank visibility bitfields if necessary. */
+
+ TYPE_NFIELDS (type) = nfields;
+ TYPE_FIELDS (type) = (struct field *)
+ TYPE_ALLOC (type, sizeof (struct field) * nfields);
+ memset (TYPE_FIELDS (type), 0, sizeof (struct field) * nfields);
+
+ if (non_public_fields)
+ {
+ ALLOCATE_CPLUS_STRUCT_TYPE (type);
+
+ TYPE_FIELD_PRIVATE_BITS (type) =
+ (B_TYPE *) TYPE_ALLOC (type, B_BYTES (nfields));
+ B_CLRALL (TYPE_FIELD_PRIVATE_BITS (type), nfields);
+
+ TYPE_FIELD_PROTECTED_BITS (type) =
+ (B_TYPE *) TYPE_ALLOC (type, B_BYTES (nfields));
+ B_CLRALL (TYPE_FIELD_PROTECTED_BITS (type), nfields);
+ }
+
+ /* Copy the saved-up fields into the field vector. Start from the head
+ of the list, adding to the tail of the field array, so that they end
+ up in the same order in the array in which they were added to the list. */
+
+ while (nfields-- > 0)
+ {
+ TYPE_FIELD (type, nfields) = fip -> list -> field;
+ switch (fip -> list -> visibility)
+ {
+ case VISIBILITY_PRIVATE:
+ SET_TYPE_FIELD_PRIVATE (type, nfields);
+ break;
+
+ case VISIBILITY_PROTECTED:
+ SET_TYPE_FIELD_PROTECTED (type, nfields);
+ break;
+
+ case VISIBILITY_PUBLIC:
+ break;
+
+ default:
+ /* Should warn about this unknown visibility? */
+ break;
+ }
+ fip -> list = fip -> list -> next;
+ }
+ return 1;
+}
+
+/* Read the description of a structure (or union type) and return an object
+ describing the type.
+
+ PP points to a character pointer that points to the next unconsumed token
+ in the the stabs string. For example, given stabs "A:T4=s4a:1,0,32;;",
+ *PP will point to "4a:1,0,32;;".
+
+ TYPE points to an incomplete type that needs to be filled in.
+
+ OBJFILE points to the current objfile from which the stabs information is
+ being read. (Note that it is redundant in that TYPE also contains a pointer
+ to this same objfile, so it might be a good idea to eliminate it. FIXME).
+ */
+
+static struct type *
+read_struct_type (pp, type, objfile)
+ char **pp;
+ struct type *type;
+ struct objfile *objfile;
+{
+ struct cleanup *back_to;
+ struct field_info fi;
+
+ fi.list = NULL;
+ fi.fnlist = NULL;
+
+ back_to = make_cleanup (null_cleanup, 0);
+
+ INIT_CPLUS_SPECIFIC (type);
+ TYPE_FLAGS (type) &= ~TYPE_FLAG_STUB;
+
+ /* First comes the total size in bytes. */
+
+ {
+ int nbits;
+ TYPE_LENGTH (type) = read_huge_number (pp, 0, &nbits);
+ if (nbits != 0)
+ return error_type (pp);
+ }
+
+ /* Now read the baseclasses, if any, read the regular C struct or C++
+ class member fields, attach the fields to the type, read the C++
+ member functions, attach them to the type, and then read any tilde
+ field (baseclass specifier for the class holding the main vtable). */
+
+ if (!read_baseclasses (&fi, pp, type, objfile)
+ || !read_struct_fields (&fi, pp, type, objfile)
+ || !attach_fields_to_type (&fi, type, objfile)
+ || !read_member_functions (&fi, pp, type, objfile)
+ || !attach_fn_fields_to_type (&fi, type)
+ || !read_tilde_fields (&fi, pp, type, objfile))
+ {
+ do_cleanups (back_to);
+ return (error_type (pp));
+ }
+
+ do_cleanups (back_to);
+ return (type);
+}
+
+/* Read a definition of an array type,
+ and create and return a suitable type object.
+ Also creates a range type which represents the bounds of that
+ array. */
+
+static struct type *
+read_array_type (pp, type, objfile)
+ register char **pp;
+ register struct type *type;
+ struct objfile *objfile;
+{
+ struct type *index_type, *element_type, *range_type;
+ int lower, upper;
+ int adjustable = 0;
+ int nbits;
+
+ /* Format of an array type:
+ "ar<index type>;lower;upper;<array_contents_type>". Put code in
+ to handle this.
+
+ Fortran adjustable arrays use Adigits or Tdigits for lower or upper;
+ for these, produce a type like float[][]. */
+
+ index_type = read_type (pp, objfile);
+ if (**pp != ';')
+ /* Improper format of array type decl. */
+ return error_type (pp);
+ ++*pp;
+
+ if (!(**pp >= '0' && **pp <= '9'))
+ {
+ (*pp)++;
+ adjustable = 1;
+ }
+ lower = read_huge_number (pp, ';', &nbits);
+ if (nbits != 0)
+ return error_type (pp);
+
+ if (!(**pp >= '0' && **pp <= '9'))
+ {
+ (*pp)++;
+ adjustable = 1;
+ }
+ upper = read_huge_number (pp, ';', &nbits);
+ if (nbits != 0)
+ return error_type (pp);
+
+ element_type = read_type (pp, objfile);
+
+ if (adjustable)
+ {
+ lower = 0;
+ upper = -1;
+ }
+
+ range_type =
+ create_range_type ((struct type *) NULL, index_type, lower, upper);
+ type = create_array_type (type, element_type, range_type);
+
+ /* If we have an array whose element type is not yet known, but whose
+ bounds *are* known, record it to be adjusted at the end of the file. */
+
+ if (TYPE_LENGTH (element_type) == 0 && !adjustable)
+ {
+ add_undefined_type (type);
+ }
+
+ return type;
+}
+
+
+/* Read a definition of an enumeration type,
+ and create and return a suitable type object.
+ Also defines the symbols that represent the values of the type. */
+
+static struct type *
+read_enum_type (pp, type, objfile)
+ register char **pp;
+ register struct type *type;
+ struct objfile *objfile;
+{
+ register char *p;
+ char *name;
+ register long n;
+ register struct symbol *sym;
+ int nsyms = 0;
+ struct pending **symlist;
+ struct pending *osyms, *syms;
+ int o_nsyms;
+
+#if 0
+ /* FIXME! The stabs produced by Sun CC merrily define things that ought
+ to be file-scope, between N_FN entries, using N_LSYM. What's a mother
+ to do? For now, force all enum values to file scope. */
+ if (within_function)
+ symlist = &local_symbols;
+ else
+#endif
+ symlist = &file_symbols;
+ osyms = *symlist;
+ o_nsyms = osyms ? osyms->nsyms : 0;
+
+ /* Read the value-names and their values.
+ The input syntax is NAME:VALUE,NAME:VALUE, and so on.
+ A semicolon or comma instead of a NAME means the end. */
+ while (**pp && **pp != ';' && **pp != ',')
+ {
+ int nbits;
+ STABS_CONTINUE (pp);
+ p = *pp;
+ while (*p != ':') p++;
+ name = obsavestring (*pp, p - *pp, &objfile -> symbol_obstack);
+ *pp = p + 1;
+ n = read_huge_number (pp, ',', &nbits);
+ if (nbits != 0)
+ return error_type (pp);
+
+ sym = (struct symbol *)
+ obstack_alloc (&objfile -> symbol_obstack, sizeof (struct symbol));
+ memset (sym, 0, sizeof (struct symbol));
+ SYMBOL_NAME (sym) = name;
+ SYMBOL_LANGUAGE (sym) = current_subfile -> language;
+ SYMBOL_CLASS (sym) = LOC_CONST;
+ SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+ SYMBOL_VALUE (sym) = n;
+ add_symbol_to_list (sym, symlist);
+ nsyms++;
+ }
+
+ if (**pp == ';')
+ (*pp)++; /* Skip the semicolon. */
+
+ /* Now fill in the fields of the type-structure. */
+
+ TYPE_LENGTH (type) = sizeof (int);
+ TYPE_CODE (type) = TYPE_CODE_ENUM;
+ TYPE_FLAGS (type) &= ~TYPE_FLAG_STUB;
+ TYPE_NFIELDS (type) = nsyms;
+ TYPE_FIELDS (type) = (struct field *)
+ TYPE_ALLOC (type, sizeof (struct field) * nsyms);
+ memset (TYPE_FIELDS (type), 0, sizeof (struct field) * nsyms);
+
+ /* Find the symbols for the values and put them into the type.
+ The symbols can be found in the symlist that we put them on
+ to cause them to be defined. osyms contains the old value
+ of that symlist; everything up to there was defined by us. */
+ /* Note that we preserve the order of the enum constants, so
+ that in something like "enum {FOO, LAST_THING=FOO}" we print
+ FOO, not LAST_THING. */
+
+ for (syms = *symlist, n = 0; syms; syms = syms->next)
+ {
+ int j = 0;
+ if (syms == osyms)
+ j = o_nsyms;
+ for (; j < syms->nsyms; j++,n++)
+ {
+ struct symbol *xsym = syms->symbol[j];
+ SYMBOL_TYPE (xsym) = type;
+ TYPE_FIELD_NAME (type, n) = SYMBOL_NAME (xsym);
+ TYPE_FIELD_VALUE (type, n) = 0;
+ TYPE_FIELD_BITPOS (type, n) = SYMBOL_VALUE (xsym);
+ TYPE_FIELD_BITSIZE (type, n) = 0;
+ }
+ if (syms == osyms)
+ break;
+ }
+
+#if 0
+ /* This screws up perfectly good C programs with enums. FIXME. */
+ /* Is this Modula-2's BOOLEAN type? Flag it as such if so. */
+ if(TYPE_NFIELDS(type) == 2 &&
+ ((STREQ(TYPE_FIELD_NAME(type,0),"TRUE") &&
+ STREQ(TYPE_FIELD_NAME(type,1),"FALSE")) ||
+ (STREQ(TYPE_FIELD_NAME(type,1),"TRUE") &&
+ STREQ(TYPE_FIELD_NAME(type,0),"FALSE"))))
+ TYPE_CODE(type) = TYPE_CODE_BOOL;
+#endif
+
+ return type;
+}
+
+/* Sun's ACC uses a somewhat saner method for specifying the builtin
+ typedefs in every file (for int, long, etc):
+
+ type = b <signed> <width>; <offset>; <nbits>
+ signed = u or s. Possible c in addition to u or s (for char?).
+ offset = offset from high order bit to start bit of type.
+ width is # bytes in object of this type, nbits is # bits in type.
+
+ The width/offset stuff appears to be for small objects stored in
+ larger ones (e.g. `shorts' in `int' registers). We ignore it for now,
+ FIXME. */
+
+static struct type *
+read_sun_builtin_type (pp, typenums, objfile)
+ char **pp;
+ int typenums[2];
+ struct objfile *objfile;
+{
+ int type_bits;
+ int nbits;
+ int signed_type;
+
+ switch (**pp)
+ {
+ case 's':
+ signed_type = 1;
+ break;
+ case 'u':
+ signed_type = 0;
+ break;
+ default:
+ return error_type (pp);
+ }
+ (*pp)++;
+
+ /* For some odd reason, all forms of char put a c here. This is strange
+ because no other type has this honor. We can safely ignore this because
+ we actually determine 'char'acterness by the number of bits specified in
+ the descriptor. */
+
+ if (**pp == 'c')
+ (*pp)++;
+
+ /* The first number appears to be the number of bytes occupied
+ by this type, except that unsigned short is 4 instead of 2.
+ Since this information is redundant with the third number,
+ we will ignore it. */
+ read_huge_number (pp, ';', &nbits);
+ if (nbits != 0)
+ return error_type (pp);
+
+ /* The second number is always 0, so ignore it too. */
+ read_huge_number (pp, ';', &nbits);
+ if (nbits != 0)
+ return error_type (pp);
+
+ /* The third number is the number of bits for this type. */
+ type_bits = read_huge_number (pp, 0, &nbits);
+ if (nbits != 0)
+ return error_type (pp);
+
+#if 0
+ /* FIXME. Here we should just be able to make a type of the right
+ number of bits and signedness. FIXME. */
+
+ if (type_bits == TARGET_LONG_LONG_BIT)
+ return (lookup_fundamental_type (objfile,
+ signed_type? FT_LONG_LONG: FT_UNSIGNED_LONG_LONG));
+
+ if (type_bits == TARGET_INT_BIT)
+ {
+ /* FIXME -- the only way to distinguish `int' from `long'
+ is to look at its name! */
+ if (signed_type)
+ {
+ if (long_kludge_name && long_kludge_name[0] == 'l' /* long */)
+ return lookup_fundamental_type (objfile, FT_LONG);
+ else
+ return lookup_fundamental_type (objfile, FT_INTEGER);
+ }
+ else
+ {
+ if (long_kludge_name
+ && ((long_kludge_name[0] == 'u' /* unsigned */ &&
+ long_kludge_name[9] == 'l' /* long */)
+ || (long_kludge_name[0] == 'l' /* long unsigned */)))
+ return lookup_fundamental_type (objfile, FT_UNSIGNED_LONG);
+ else
+ return lookup_fundamental_type (objfile, FT_UNSIGNED_INTEGER);
+ }
+ }
+
+ if (type_bits == TARGET_SHORT_BIT)
+ return (lookup_fundamental_type (objfile,
+ signed_type? FT_SHORT: FT_UNSIGNED_SHORT));
+
+ if (type_bits == TARGET_CHAR_BIT)
+ return (lookup_fundamental_type (objfile,
+ signed_type? FT_CHAR: FT_UNSIGNED_CHAR));
+
+ if (type_bits == 0)
+ return lookup_fundamental_type (objfile, FT_VOID);
+
+ return error_type (pp);
+#else
+ return init_type (type_bits == 0 ? TYPE_CODE_VOID : TYPE_CODE_INT,
+ type_bits / TARGET_CHAR_BIT,
+ signed_type ? 0 : TYPE_FLAG_UNSIGNED, (char *)NULL,
+ objfile);
+#endif
+}
+
+static struct type *
+read_sun_floating_type (pp, typenums, objfile)
+ char **pp;
+ int typenums[2];
+ struct objfile *objfile;
+{
+ int nbits;
+ int details;
+ int nbytes;
+
+ /* The first number has more details about the type, for example
+ FN_COMPLEX. */
+ details = read_huge_number (pp, ';', &nbits);
+ if (nbits != 0)
+ return error_type (pp);
+
+ /* The second number is the number of bytes occupied by this type */
+ nbytes = read_huge_number (pp, ';', &nbits);
+ if (nbits != 0)
+ return error_type (pp);
+
+ if (details == NF_COMPLEX || details == NF_COMPLEX16
+ || details == NF_COMPLEX32)
+ /* This is a type we can't handle, but we do know the size.
+ We also will be able to give it a name. */
+ return init_type (TYPE_CODE_ERROR, nbytes, 0, NULL, objfile);
+
+ return init_type (TYPE_CODE_FLT, nbytes, 0, NULL, objfile);
+}
+
+/* Read a number from the string pointed to by *PP.
+ The value of *PP is advanced over the number.
+ If END is nonzero, the character that ends the
+ number must match END, or an error happens;
+ and that character is skipped if it does match.
+ If END is zero, *PP is left pointing to that character.
+
+ If the number fits in a long, set *BITS to 0 and return the value.
+ If not, set *BITS to be the number of bits in the number and return 0.
+
+ If encounter garbage, set *BITS to -1 and return 0. */
+
+static long
+read_huge_number (pp, end, bits)
+ char **pp;
+ int end;
+ int *bits;
+{
+ char *p = *pp;
+ int sign = 1;
+ long n = 0;
+ int radix = 10;
+ char overflow = 0;
+ int nbits = 0;
+ int c;
+ long upper_limit;
+
+ if (*p == '-')
+ {
+ sign = -1;
+ p++;
+ }
+
+ /* Leading zero means octal. GCC uses this to output values larger
+ than an int (because that would be hard in decimal). */
+ if (*p == '0')
+ {
+ radix = 8;
+ p++;
+ }
+
+ upper_limit = LONG_MAX / radix;
+ while ((c = *p++) >= '0' && c < ('0' + radix))
+ {
+ if (n <= upper_limit)
+ {
+ n *= radix;
+ n += c - '0'; /* FIXME this overflows anyway */
+ }
+ else
+ overflow = 1;
+
+ /* This depends on large values being output in octal, which is
+ what GCC does. */
+ if (radix == 8)
+ {
+ if (nbits == 0)
+ {
+ if (c == '0')
+ /* Ignore leading zeroes. */
+ ;
+ else if (c == '1')
+ nbits = 1;
+ else if (c == '2' || c == '3')
+ nbits = 2;
+ else
+ nbits = 3;
+ }
+ else
+ nbits += 3;
+ }
+ }
+ if (end)
+ {
+ if (c && c != end)
+ {
+ if (bits != NULL)
+ *bits = -1;
+ return 0;
+ }
+ }
+ else
+ --p;
+
+ *pp = p;
+ if (overflow)
+ {
+ if (nbits == 0)
+ {
+ /* Large decimal constants are an error (because it is hard to
+ count how many bits are in them). */
+ if (bits != NULL)
+ *bits = -1;
+ return 0;
+ }
+
+ /* -0x7f is the same as 0x80. So deal with it by adding one to
+ the number of bits. */
+ if (sign == -1)
+ ++nbits;
+ if (bits)
+ *bits = nbits;
+ }
+ else
+ {
+ if (bits)
+ *bits = 0;
+ return n * sign;
+ }
+ /* It's *BITS which has the interesting information. */
+ return 0;
+}
+
+static struct type *
+read_range_type (pp, typenums, objfile)
+ char **pp;
+ int typenums[2];
+ struct objfile *objfile;
+{
+ int rangenums[2];
+ long n2, n3;
+ int n2bits, n3bits;
+ int self_subrange;
+ struct type *result_type;
+ struct type *index_type;
+
+ /* First comes a type we are a subrange of.
+ In C it is usually 0, 1 or the type being defined. */
+ /* FIXME: according to stabs.texinfo and AIX doc, this can be a type-id
+ not just a type number. */
+ if (read_type_number (pp, rangenums) != 0)
+ return error_type (pp);
+ self_subrange = (rangenums[0] == typenums[0] &&
+ rangenums[1] == typenums[1]);
+
+ /* A semicolon should now follow; skip it. */
+ if (**pp == ';')
+ (*pp)++;
+
+ /* The remaining two operands are usually lower and upper bounds
+ of the range. But in some special cases they mean something else. */
+ n2 = read_huge_number (pp, ';', &n2bits);
+ n3 = read_huge_number (pp, ';', &n3bits);
+
+ if (n2bits == -1 || n3bits == -1)
+ return error_type (pp);
+
+ /* If limits are huge, must be large integral type. */
+ if (n2bits != 0 || n3bits != 0)
+ {
+ char got_signed = 0;
+ char got_unsigned = 0;
+ /* Number of bits in the type. */
+ int nbits = 0;
+
+ /* Range from 0 to <large number> is an unsigned large integral type. */
+ if ((n2bits == 0 && n2 == 0) && n3bits != 0)
+ {
+ got_unsigned = 1;
+ nbits = n3bits;
+ }
+ /* Range from <large number> to <large number>-1 is a large signed
+ integral type. Take care of the case where <large number> doesn't
+ fit in a long but <large number>-1 does. */
+ else if ((n2bits != 0 && n3bits != 0 && n2bits == n3bits + 1)
+ || (n2bits != 0 && n3bits == 0
+ && (n2bits == sizeof (long) * HOST_CHAR_BIT)
+ && n3 == LONG_MAX))
+ {
+ got_signed = 1;
+ nbits = n2bits;
+ }
+
+ if (got_signed || got_unsigned)
+ {
+ return init_type (TYPE_CODE_INT, nbits / TARGET_CHAR_BIT,
+ got_unsigned ? TYPE_FLAG_UNSIGNED : 0, NULL,
+ objfile);
+ }
+ else
+ return error_type (pp);
+ }
+
+ /* A type defined as a subrange of itself, with bounds both 0, is void. */
+ if (self_subrange && n2 == 0 && n3 == 0)
+ return init_type (TYPE_CODE_VOID, 0, 0, NULL, objfile);
+
+ /* If n3 is zero and n2 is not, we want a floating type,
+ and n2 is the width in bytes.
+
+ Fortran programs appear to use this for complex types also,
+ and they give no way to distinguish between double and single-complex!
+
+ GDB does not have complex types.
+
+ Just return the complex as a float of that size. It won't work right
+ for the complex values, but at least it makes the file loadable. */
+
+ if (n3 == 0 && n2 > 0)
+ {
+ return init_type (TYPE_CODE_FLT, n2, 0, NULL, objfile);
+ }
+
+ /* If the upper bound is -1, it must really be an unsigned int. */
+
+ else if (n2 == 0 && n3 == -1)
+ {
+ /* It is unsigned int or unsigned long. */
+ /* GCC 2.3.3 uses this for long long too, but that is just a GDB 3.5
+ compatibility hack. */
+ return init_type (TYPE_CODE_INT, TARGET_INT_BIT / TARGET_CHAR_BIT,
+ TYPE_FLAG_UNSIGNED, NULL, objfile);
+ }
+
+ /* Special case: char is defined (Who knows why) as a subrange of
+ itself with range 0-127. */
+ else if (self_subrange && n2 == 0 && n3 == 127)
+ return init_type (TYPE_CODE_INT, 1, 0, NULL, objfile);
+
+ /* We used to do this only for subrange of self or subrange of int. */
+ else if (n2 == 0)
+ {
+ if (n3 < 0)
+ /* n3 actually gives the size. */
+ return init_type (TYPE_CODE_INT, - n3, TYPE_FLAG_UNSIGNED,
+ NULL, objfile);
+ if (n3 == 0xff)
+ return init_type (TYPE_CODE_INT, 1, TYPE_FLAG_UNSIGNED, NULL, objfile);
+ if (n3 == 0xffff)
+ return init_type (TYPE_CODE_INT, 2, TYPE_FLAG_UNSIGNED, NULL, objfile);
+
+ /* -1 is used for the upper bound of (4 byte) "unsigned int" and
+ "unsigned long", and we already checked for that,
+ so don't need to test for it here. */
+ }
+ /* I think this is for Convex "long long". Since I don't know whether
+ Convex sets self_subrange, I also accept that particular size regardless
+ of self_subrange. */
+ else if (n3 == 0 && n2 < 0
+ && (self_subrange
+ || n2 == - TARGET_LONG_LONG_BIT / TARGET_CHAR_BIT))
+ return init_type (TYPE_CODE_INT, - n2, 0, NULL, objfile);
+ else if (n2 == -n3 -1)
+ {
+ if (n3 == 0x7f)
+ return init_type (TYPE_CODE_INT, 1, 0, NULL, objfile);
+ if (n3 == 0x7fff)
+ return init_type (TYPE_CODE_INT, 2, 0, NULL, objfile);
+ if (n3 == 0x7fffffff)
+ return init_type (TYPE_CODE_INT, 4, 0, NULL, objfile);
+ }
+
+ /* We have a real range type on our hands. Allocate space and
+ return a real pointer. */
+
+ /* At this point I don't have the faintest idea how to deal with
+ a self_subrange type; I'm going to assume that this is used
+ as an idiom, and that all of them are special cases. So . . . */
+ if (self_subrange)
+ return error_type (pp);
+
+ index_type = *dbx_lookup_type (rangenums);
+ if (index_type == NULL)
+ {
+ /* Does this actually ever happen? Is that why we are worrying
+ about dealing with it rather than just calling error_type? */
+
+ static struct type *range_type_index;
+
+ complain (&range_type_base_complaint, rangenums[1]);
+ if (range_type_index == NULL)
+ range_type_index =
+ init_type (TYPE_CODE_INT, TARGET_INT_BIT / TARGET_CHAR_BIT,
+ 0, "range type index type", NULL);
+ index_type = range_type_index;
+ }
+
+ result_type = create_range_type ((struct type *) NULL, index_type, n2, n3);
+ return (result_type);
+}
+
+/* Read in an argument list. This is a list of types, separated by commas
+ and terminated with END. Return the list of types read in, or (struct type
+ **)-1 if there is an error. */
+
+static struct type **
+read_args (pp, end, objfile)
+ char **pp;
+ int end;
+ struct objfile *objfile;
+{
+ /* FIXME! Remove this arbitrary limit! */
+ struct type *types[1024], **rval; /* allow for fns of 1023 parameters */
+ int n = 0;
+
+ while (**pp != end)
+ {
+ if (**pp != ',')
+ /* Invalid argument list: no ','. */
+ return (struct type **)-1;
+ (*pp)++;
+ STABS_CONTINUE (pp);
+ types[n++] = read_type (pp, objfile);
+ }
+ (*pp)++; /* get past `end' (the ':' character) */
+
+ if (n == 1)
+ {
+ rval = (struct type **) xmalloc (2 * sizeof (struct type *));
+ }
+ else if (TYPE_CODE (types[n-1]) != TYPE_CODE_VOID)
+ {
+ rval = (struct type **) xmalloc ((n + 1) * sizeof (struct type *));
+ memset (rval + n, 0, sizeof (struct type *));
+ }
+ else
+ {
+ rval = (struct type **) xmalloc (n * sizeof (struct type *));
+ }
+ memcpy (rval, types, n * sizeof (struct type *));
+ return rval;
+}
+
+/* Common block handling. */
+
+/* List of symbols declared since the last BCOMM. This list is a tail
+ of local_symbols. When ECOMM is seen, the symbols on the list
+ are noted so their proper addresses can be filled in later,
+ using the common block base address gotten from the assembler
+ stabs. */
+
+static struct pending *common_block;
+static int common_block_i;
+
+/* Name of the current common block. We get it from the BCOMM instead of the
+ ECOMM to match IBM documentation (even though IBM puts the name both places
+ like everyone else). */
+static char *common_block_name;
+
+/* Process a N_BCOMM symbol. The storage for NAME is not guaranteed
+ to remain after this function returns. */
+
+void
+common_block_start (name, objfile)
+ char *name;
+ struct objfile *objfile;
+{
+ if (common_block_name != NULL)
+ {
+ static struct complaint msg = {
+ "Invalid symbol data: common block within common block",
+ 0, 0};
+ complain (&msg);
+ }
+ common_block = local_symbols;
+ common_block_i = local_symbols ? local_symbols->nsyms : 0;
+ common_block_name = obsavestring (name, strlen (name),
+ &objfile -> symbol_obstack);
+}
+
+/* Process a N_ECOMM symbol. */
+
+void
+common_block_end (objfile)
+ struct objfile *objfile;
+{
+ /* Symbols declared since the BCOMM are to have the common block
+ start address added in when we know it. common_block and
+ common_block_i point to the first symbol after the BCOMM in
+ the local_symbols list; copy the list and hang it off the
+ symbol for the common block name for later fixup. */
+ int i;
+ struct symbol *sym;
+ struct pending *new = 0;
+ struct pending *next;
+ int j;
+
+ if (common_block_name == NULL)
+ {
+ static struct complaint msg = {"ECOMM symbol unmatched by BCOMM", 0, 0};
+ complain (&msg);
+ return;
+ }
+
+ sym = (struct symbol *)
+ obstack_alloc (&objfile -> symbol_obstack, sizeof (struct symbol));
+ memset (sym, 0, sizeof (struct symbol));
+ SYMBOL_NAME (sym) = common_block_name;
+ SYMBOL_CLASS (sym) = LOC_BLOCK;
+
+ /* Now we copy all the symbols which have been defined since the BCOMM. */
+
+ /* Copy all the struct pendings before common_block. */
+ for (next = local_symbols;
+ next != NULL && next != common_block;
+ next = next->next)
+ {
+ for (j = 0; j < next->nsyms; j++)
+ add_symbol_to_list (next->symbol[j], &new);
+ }
+
+ /* Copy however much of COMMON_BLOCK we need. If COMMON_BLOCK is
+ NULL, it means copy all the local symbols (which we already did
+ above). */
+
+ if (common_block != NULL)
+ for (j = common_block_i; j < common_block->nsyms; j++)
+ add_symbol_to_list (common_block->symbol[j], &new);
+
+ SYMBOL_NAMESPACE (sym) = (enum namespace)((long) new);
+
+ /* Should we be putting local_symbols back to what it was?
+ Does it matter? */
+
+ i = hashname (SYMBOL_NAME (sym));
+ SYMBOL_VALUE_CHAIN (sym) = global_sym_chain[i];
+ global_sym_chain[i] = sym;
+ common_block_name = NULL;
+}
+
+/* Add a common block's start address to the offset of each symbol
+ declared to be in it (by being between a BCOMM/ECOMM pair that uses
+ the common block name). */
+
+static void
+fix_common_block (sym, valu)
+ struct symbol *sym;
+ int valu;
+{
+ struct pending *next = (struct pending *) SYMBOL_NAMESPACE (sym);
+ for ( ; next; next = next->next)
+ {
+ register int j;
+ for (j = next->nsyms - 1; j >= 0; j--)
+ SYMBOL_VALUE_ADDRESS (next->symbol[j]) += valu;
+ }
+}
+
+
+
+/* What about types defined as forward references inside of a small lexical
+ scope? */
+/* Add a type to the list of undefined types to be checked through
+ once this file has been read in. */
+
+void
+add_undefined_type (type)
+ struct type *type;
+{
+ if (undef_types_length == undef_types_allocated)
+ {
+ undef_types_allocated *= 2;
+ undef_types = (struct type **)
+ xrealloc ((char *) undef_types,
+ undef_types_allocated * sizeof (struct type *));
+ }
+ undef_types[undef_types_length++] = type;
+}
+
+/* Go through each undefined type, see if it's still undefined, and fix it
+ up if possible. We have two kinds of undefined types:
+
+ TYPE_CODE_ARRAY: Array whose target type wasn't defined yet.
+ Fix: update array length using the element bounds
+ and the target type's length.
+ TYPE_CODE_STRUCT, TYPE_CODE_UNION: Structure whose fields were not
+ yet defined at the time a pointer to it was made.
+ Fix: Do a full lookup on the struct/union tag. */
+void
+cleanup_undefined_types ()
+{
+ struct type **type;
+
+ for (type = undef_types; type < undef_types + undef_types_length; type++)
+ {
+ switch (TYPE_CODE (*type))
+ {
+
+ case TYPE_CODE_STRUCT:
+ case TYPE_CODE_UNION:
+ case TYPE_CODE_ENUM:
+ {
+ /* Check if it has been defined since. */
+ if (TYPE_FLAGS (*type) & TYPE_FLAG_STUB)
+ {
+ struct pending *ppt;
+ int i;
+ /* Name of the type, without "struct" or "union" */
+ char *typename = TYPE_TAG_NAME (*type);
+
+ if (typename == NULL)
+ {
+ static struct complaint msg = {"need a type name", 0, 0};
+ complain (&msg);
+ break;
+ }
+ for (ppt = file_symbols; ppt; ppt = ppt->next)
+ {
+ for (i = 0; i < ppt->nsyms; i++)
+ {
+ struct symbol *sym = ppt->symbol[i];
+
+ if (SYMBOL_CLASS (sym) == LOC_TYPEDEF
+ && SYMBOL_NAMESPACE (sym) == STRUCT_NAMESPACE
+ && (TYPE_CODE (SYMBOL_TYPE (sym)) ==
+ TYPE_CODE (*type))
+ && STREQ (SYMBOL_NAME (sym), typename))
+ {
+ memcpy (*type, SYMBOL_TYPE (sym),
+ sizeof (struct type));
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case TYPE_CODE_ARRAY:
+ {
+ struct type *range_type;
+ int lower, upper;
+
+ if (TYPE_LENGTH (*type) != 0) /* Better be unknown */
+ goto badtype;
+ if (TYPE_NFIELDS (*type) != 1)
+ goto badtype;
+ range_type = TYPE_FIELD_TYPE (*type, 0);
+ if (TYPE_CODE (range_type) != TYPE_CODE_RANGE)
+ goto badtype;
+
+ /* Now recompute the length of the array type, based on its
+ number of elements and the target type's length. */
+ lower = TYPE_FIELD_BITPOS (range_type, 0);
+ upper = TYPE_FIELD_BITPOS (range_type, 1);
+ TYPE_LENGTH (*type) = (upper - lower + 1)
+ * TYPE_LENGTH (TYPE_TARGET_TYPE (*type));
+ }
+ break;
+
+ default:
+ badtype:
+ {
+ static struct complaint msg = {"\
+GDB internal error. cleanup_undefined_types with bad type %d.", 0, 0};
+ complain (&msg, TYPE_CODE (*type));
+ }
+ break;
+ }
+ }
+ undef_types_length = 0;
+}
+
+/* Scan through all of the global symbols defined in the object file,
+ assigning values to the debugging symbols that need to be assigned
+ to. Get these symbols from the minimal symbol table. */
+
+void
+scan_file_globals (objfile)
+ struct objfile *objfile;
+{
+ int hash;
+ struct minimal_symbol *msymbol;
+ struct symbol *sym, *prev;
+
+ if (objfile->msymbols == 0) /* Beware the null file. */
+ return;
+
+ for (msymbol = objfile -> msymbols; SYMBOL_NAME (msymbol) != NULL; msymbol++)
+ {
+ QUIT;
+
+ prev = NULL;
+
+ /* Get the hash index and check all the symbols
+ under that hash index. */
+
+ hash = hashname (SYMBOL_NAME (msymbol));
+
+ for (sym = global_sym_chain[hash]; sym;)
+ {
+ if (SYMBOL_NAME (msymbol)[0] == SYMBOL_NAME (sym)[0] &&
+ STREQ(SYMBOL_NAME (msymbol) + 1, SYMBOL_NAME (sym) + 1))
+ {
+ /* Splice this symbol out of the hash chain and
+ assign the value we have to it. */
+ if (prev)
+ {
+ SYMBOL_VALUE_CHAIN (prev) = SYMBOL_VALUE_CHAIN (sym);
+ }
+ else
+ {
+ global_sym_chain[hash] = SYMBOL_VALUE_CHAIN (sym);
+ }
+
+ /* Check to see whether we need to fix up a common block. */
+ /* Note: this code might be executed several times for
+ the same symbol if there are multiple references. */
+
+ if (SYMBOL_CLASS (sym) == LOC_BLOCK)
+ {
+ fix_common_block (sym, SYMBOL_VALUE_ADDRESS (msymbol));
+ }
+ else
+ {
+ SYMBOL_VALUE_ADDRESS (sym) = SYMBOL_VALUE_ADDRESS (msymbol);
+ }
+
+ if (prev)
+ {
+ sym = SYMBOL_VALUE_CHAIN (prev);
+ }
+ else
+ {
+ sym = global_sym_chain[hash];
+ }
+ }
+ else
+ {
+ prev = sym;
+ sym = SYMBOL_VALUE_CHAIN (sym);
+ }
+ }
+ }
+}
+
+/* Initialize anything that needs initializing when starting to read
+ a fresh piece of a symbol file, e.g. reading in the stuff corresponding
+ to a psymtab. */
+
+void
+stabsread_init ()
+{
+}
+
+/* Initialize anything that needs initializing when a completely new
+ symbol file is specified (not just adding some symbols from another
+ file, e.g. a shared library). */
+
+void
+stabsread_new_init ()
+{
+ /* Empty the hash table of global syms looking for values. */
+ memset (global_sym_chain, 0, sizeof (global_sym_chain));
+}
+
+/* Initialize anything that needs initializing at the same time as
+ start_symtab() is called. */
+
+void start_stabs ()
+{
+ global_stabs = NULL; /* AIX COFF */
+ /* Leave FILENUM of 0 free for builtin types and this file's types. */
+ n_this_object_header_files = 1;
+ type_vector_length = 0;
+ type_vector = (struct type **) 0;
+
+ /* FIXME: If common_block_name is not already NULL, we should complain(). */
+ common_block_name = NULL;
+}
+
+/* Call after end_symtab() */
+
+void end_stabs ()
+{
+ if (type_vector)
+ {
+ free ((char *) type_vector);
+ }
+ type_vector = 0;
+ type_vector_length = 0;
+ previous_stab_code = 0;
+}
+
+void
+finish_global_stabs (objfile)
+ struct objfile *objfile;
+{
+ if (global_stabs)
+ {
+ patch_block_stabs (global_symbols, global_stabs, objfile);
+ free ((PTR) global_stabs);
+ global_stabs = NULL;
+ }
+}
+
+/* Initializer for this module */
+
+void
+_initialize_stabsread ()
+{
+ undef_types_allocated = 20;
+ undef_types_length = 0;
+ undef_types = (struct type **)
+ xmalloc (undef_types_allocated * sizeof (struct type *));
+}
diff --git a/gnu/usr.bin/gdb/gdb/stabsread.h b/gnu/usr.bin/gdb/gdb/stabsread.h
new file mode 100644
index 0000000..3b890d8
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/stabsread.h
@@ -0,0 +1,194 @@
+/* Include file for stabs debugging format support functions.
+ Copyright 1986-1991, 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Definitions, prototypes, etc for stabs debugging format support
+ functions.
+
+ Variables declared in this file can be defined by #define-ing
+ the name EXTERN to null. It is used to declare variables that
+ are normally extern, but which get defined in a single module
+ using this technique. */
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+/* Convert stab register number (from `r' declaration) to a gdb REGNUM. */
+
+#ifndef STAB_REG_TO_REGNUM
+#define STAB_REG_TO_REGNUM(VALUE) (VALUE)
+#endif
+
+/* Hash table of global symbols whose values are not known yet.
+ They are chained thru the SYMBOL_VALUE_CHAIN, since we don't
+ have the correct data for that slot yet.
+
+ The use of the LOC_BLOCK code in this chain is nonstandard--
+ it refers to a FORTRAN common block rather than the usual meaning. */
+
+EXTERN struct symbol *global_sym_chain[HASHSIZE];
+
+extern void common_block_start PARAMS ((char *, struct objfile *));
+extern void common_block_end PARAMS ((struct objfile *));
+
+/* Kludge for xcoffread.c */
+
+struct pending_stabs
+{
+ int count;
+ int length;
+ char *stab[1];
+};
+
+EXTERN struct pending_stabs *global_stabs;
+
+/* The type code that process_one_symbol saw on its previous invocation.
+ Used to detect pairs of N_SO symbols. */
+
+EXTERN int previous_stab_code;
+
+/* Support for Sun changes to dbx symbol format */
+
+/* For each identified header file, we have a table of types defined
+ in that header file.
+
+ header_files maps header file names to their type tables.
+ It is a vector of n_header_files elements.
+ Each element describes one header file.
+ It contains a vector of types.
+
+ Sometimes it can happen that the same header file produces
+ different results when included in different places.
+ This can result from conditionals or from different
+ things done before including the file.
+ When this happens, there are multiple entries for the file in this table,
+ one entry for each distinct set of results.
+ The entries are distinguished by the INSTANCE field.
+ The INSTANCE field appears in the N_BINCL and N_EXCL symbol table and is
+ used to match header-file references to their corresponding data. */
+
+struct header_file
+{
+
+ /* Name of header file */
+
+ char *name;
+
+ /* Numeric code distinguishing instances of one header file that produced
+ different results when included. It comes from the N_BINCL or N_EXCL. */
+
+ int instance;
+
+ /* Pointer to vector of types */
+
+ struct type **vector;
+
+ /* Allocated length (# elts) of that vector */
+
+ int length;
+
+};
+
+EXTERN struct header_file *header_files;
+
+EXTERN int n_header_files;
+
+EXTERN int n_allocated_header_files;
+
+/* Within each object file, various header files are assigned numbers.
+ A type is defined or referred to with a pair of numbers
+ (FILENUM,TYPENUM) where FILENUM is the number of the header file
+ and TYPENUM is the number within that header file.
+ TYPENUM is the index within the vector of types for that header file.
+
+ FILENUM == 1 is special; it refers to the main source of the object file,
+ and not to any header file. FILENUM != 1 is interpreted by looking it up
+ in the following table, which contains indices in header_files. */
+
+EXTERN int *this_object_header_files;
+
+EXTERN int n_this_object_header_files;
+
+EXTERN int n_allocated_this_object_header_files;
+
+extern struct complaint unknown_symtype_complaint;
+extern struct complaint unknown_symchar_complaint;
+
+extern struct type *
+read_type PARAMS ((char **, struct objfile *));
+
+extern void
+cleanup_undefined_types PARAMS ((void));
+
+extern struct type **
+dbx_lookup_type PARAMS ((int [2]));
+
+extern long
+read_number PARAMS ((char **, int));
+
+extern void
+add_undefined_type PARAMS ((struct type *));
+
+extern struct symbol *
+define_symbol PARAMS ((CORE_ADDR, char *, int, int, struct objfile *));
+
+extern void
+stabsread_init PARAMS ((void));
+
+extern void
+stabsread_new_init PARAMS ((void));
+
+extern void
+start_stabs PARAMS ((void));
+
+extern void
+end_stabs PARAMS ((void));
+
+extern void
+finish_global_stabs PARAMS ((struct objfile *objfile));
+
+/* Functions exported by dbxread.c. These are not in stabsread.h because
+ they are only used by some stabs readers. */
+
+extern struct partial_symtab *
+start_psymtab PARAMS ((struct objfile *, struct section_offsets *, char *,
+ CORE_ADDR, int, struct partial_symbol *,
+ struct partial_symbol *));
+
+extern struct partial_symtab *
+end_psymtab PARAMS ((struct partial_symtab *, char **, int, int, CORE_ADDR,
+ struct partial_symtab **, int));
+
+extern void
+process_one_symbol PARAMS ((int, int, CORE_ADDR, char *,
+ struct section_offsets *, struct objfile *));
+
+extern void
+elfstab_build_psymtabs PARAMS ((struct objfile *objfile,
+ struct section_offsets *section_offsets,
+ int mainline,
+ file_ptr staboff, unsigned int stabsize,
+ file_ptr stabstroffset,
+ unsigned int stabstrsize));
+
+extern void
+pastab_build_psymtabs PARAMS ((struct objfile *, struct section_offsets *,
+ int));
+
+#undef EXTERN
diff --git a/gnu/usr.bin/gdb/gdb/stack.c b/gnu/usr.bin/gdb/gdb/stack.c
new file mode 100644
index 0000000..6fdd8c1
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/stack.c
@@ -0,0 +1,1379 @@
+/* Print and select stack frames for GDB, the GNU debugger.
+ Copyright 1986, 1987, 1989, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "value.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "expression.h"
+#include "language.h"
+#include "frame.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "target.h"
+#include "breakpoint.h"
+#include "demangle.h"
+#include "inferior.h"
+
+static void
+return_command PARAMS ((char *, int));
+
+static void
+down_command PARAMS ((char *, int));
+
+static void
+down_silently_command PARAMS ((char *, int));
+
+static void
+up_command PARAMS ((char *, int));
+
+static void
+up_silently_command PARAMS ((char *, int));
+
+static void
+frame_command PARAMS ((char *, int));
+
+static void
+select_frame_command PARAMS ((char *, int));
+
+static void
+args_info PARAMS ((char *, int));
+
+static void
+print_frame_arg_vars PARAMS ((FRAME, FILE *));
+
+static void
+catch_info PARAMS ((char *, int));
+
+static void
+locals_info PARAMS ((char *, int));
+
+static void
+print_frame_label_vars PARAMS ((FRAME, int, FILE *));
+
+static void
+print_frame_local_vars PARAMS ((FRAME, FILE *));
+
+static int
+print_block_frame_labels PARAMS ((struct block *, int *, FILE *));
+
+static int
+print_block_frame_locals PARAMS ((struct block *, FRAME, FILE *));
+
+static void
+backtrace_command PARAMS ((char *, int));
+
+static FRAME
+parse_frame_specification PARAMS ((char *));
+
+static void
+frame_info PARAMS ((char *, int));
+
+
+extern int addressprint; /* Print addresses, or stay symbolic only? */
+extern int info_verbose; /* Verbosity of symbol reading msgs */
+extern int lines_to_list; /* # of lines "list" command shows by default */
+
+/* The "selected" stack frame is used by default for local and arg access.
+ May be zero, for no selected frame. */
+
+FRAME selected_frame;
+
+/* Level of the selected frame:
+ 0 for innermost, 1 for its caller, ...
+ or -1 for frame specified by address with no defined level. */
+
+int selected_frame_level;
+
+/* Nonzero means print the full filename and linenumber
+ when a frame is printed, and do so in a format programs can parse. */
+
+int frame_file_full_name = 0;
+
+
+struct print_stack_frame_args {
+ struct frame_info *fi;
+ int level;
+ int source;
+ int args;
+};
+
+static int print_stack_frame_stub PARAMS ((char *));
+
+/* Pass the args the way catch_errors wants them. */
+static int
+print_stack_frame_stub (args)
+ char *args;
+{
+ struct print_stack_frame_args *p = (struct print_stack_frame_args *)args;
+ print_frame_info (p->fi, p->level, p->source, p->args);
+ return 0;
+}
+
+/* Print a stack frame briefly. FRAME should be the frame id
+ and LEVEL should be its level in the stack (or -1 for level not defined).
+ This prints the level, the function executing, the arguments,
+ and the file name and line number.
+ If the pc is not at the beginning of the source line,
+ the actual pc is printed at the beginning.
+
+ If SOURCE is 1, print the source line as well.
+ If SOURCE is -1, print ONLY the source line. */
+
+void
+print_stack_frame (frame, level, source)
+ FRAME frame;
+ int level;
+ int source;
+{
+ struct print_stack_frame_args args;
+
+ args.fi = get_frame_info (frame);
+ args.level = level;
+ args.source = source;
+ args.args = 1;
+
+ catch_errors (print_stack_frame_stub, (char *)&args, "", RETURN_MASK_ERROR);
+}
+
+struct print_args_args {
+ struct symbol *func;
+ struct frame_info *fi;
+};
+
+static int print_args_stub PARAMS ((char *));
+
+/* Pass the args the way catch_errors wants them. */
+static int
+print_args_stub (args)
+ char *args;
+{
+ int numargs;
+ struct print_args_args *p = (struct print_args_args *)args;
+ FRAME_NUM_ARGS (numargs, (p->fi));
+ print_frame_args (p->func, p->fi, numargs, stdout);
+ return 0;
+}
+
+void
+print_frame_info (fi, level, source, args)
+ struct frame_info *fi;
+ register int level;
+ int source;
+ int args;
+{
+ struct symtab_and_line sal;
+ struct symbol *func;
+ register char *funname = 0;
+ enum language funlang = language_unknown;
+ char buf[MAX_REGISTER_RAW_SIZE];
+ CORE_ADDR sp;
+
+ /* Get the value of SP_REGNUM relative to the frame. */
+ get_saved_register (buf, (int *)NULL, (CORE_ADDR *)NULL,
+ FRAME_INFO_ID (fi), SP_REGNUM, (enum lval_type *)NULL);
+ sp = extract_address (buf, REGISTER_RAW_SIZE (SP_REGNUM));
+
+ /* This is not a perfect test, because if a function alloca's some
+ memory, puts some code there, and then jumps into it, then the test
+ will succeed even though there is no call dummy. Probably best is
+ to check for a bp_call_dummy breakpoint. */
+ if (PC_IN_CALL_DUMMY (fi->pc, sp, fi->frame))
+ {
+ /* Do this regardless of SOURCE because we don't have any source
+ to list for this frame. */
+ if (level >= 0)
+ printf_filtered ("#%-2d ", level);
+ printf_filtered ("<function called from gdb>\n");
+ return;
+ }
+ if (fi->signal_handler_caller)
+ {
+ /* Do this regardless of SOURCE because we don't have any source
+ to list for this frame. */
+ if (level >= 0)
+ printf_filtered ("#%-2d ", level);
+ printf_filtered ("<signal handler called>\n");
+ return;
+ }
+
+ /* If fi is not the innermost frame, that normally means that fi->pc
+ points to *after* the call instruction, and we want to get the line
+ containing the call, never the next line. But if the next frame is
+ a signal_handler_caller frame, then the next frame was not entered
+ as the result of a call, and we want to get the line containing
+ fi->pc. */
+ sal =
+ find_pc_line (fi->pc,
+ fi->next != NULL && fi->next->signal_handler_caller == 0);
+
+ func = find_pc_function (fi->pc);
+ if (func)
+ {
+ /* In certain pathological cases, the symtabs give the wrong
+ function (when we are in the first function in a file which
+ is compiled without debugging symbols, the previous function
+ is compiled with debugging symbols, and the "foo.o" symbol
+ that is supposed to tell us where the file with debugging symbols
+ ends has been truncated by ar because it is longer than 15
+ characters). This also occurs if the user uses asm() to create
+ a function but not stabs for it (in a file compiled -g).
+
+ So look in the minimal symbol tables as well, and if it comes
+ up with a larger address for the function use that instead.
+ I don't think this can ever cause any problems; there shouldn't
+ be any minimal symbols in the middle of a function; if this is
+ ever changed many parts of GDB will need to be changed (and we'll
+ create a find_pc_minimal_function or some such). */
+
+ struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (fi->pc);
+ if (msymbol != NULL
+ && (SYMBOL_VALUE_ADDRESS (msymbol)
+ > BLOCK_START (SYMBOL_BLOCK_VALUE (func))))
+ {
+ /* In this case we have no way of knowing the source file
+ and line number, so don't print them. */
+ sal.symtab = 0;
+ /* We also don't know anything about the function besides
+ its address and name. */
+ func = 0;
+ funname = SYMBOL_NAME (msymbol);
+ funlang = SYMBOL_LANGUAGE (msymbol);
+ }
+ else
+ {
+ funname = SYMBOL_NAME (func);
+ funlang = SYMBOL_LANGUAGE (func);
+ }
+ }
+ else
+ {
+ register struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (fi->pc);
+ if (msymbol != NULL)
+ {
+ funname = SYMBOL_NAME (msymbol);
+ funlang = SYMBOL_LANGUAGE (msymbol);
+ }
+ }
+
+ if (source >= 0 || !sal.symtab)
+ {
+ if (level >= 0)
+ printf_filtered ("#%-2d ", level);
+ if (addressprint)
+ if (fi->pc != sal.pc || !sal.symtab)
+ printf_filtered ("%s in ", local_hex_string((unsigned long) fi->pc));
+ fprintf_symbol_filtered (stdout, funname ? funname : "??", funlang,
+ DMGL_NO_OPTS);
+ wrap_here (" ");
+ fputs_filtered (" (", stdout);
+ if (args)
+ {
+ struct print_args_args args;
+ args.fi = fi;
+ args.func = func;
+ catch_errors (print_args_stub, (char *)&args, "", RETURN_MASK_ERROR);
+ }
+ printf_filtered (")");
+ if (sal.symtab && sal.symtab->filename)
+ {
+ wrap_here (" ");
+ printf_filtered (" at %s:%d", sal.symtab->filename, sal.line);
+ }
+
+#ifdef PC_LOAD_SEGMENT
+ /* If we couldn't print out function name but if can figure out what
+ load segment this pc value is from, at least print out some info
+ about its load segment. */
+ if (!funname) {
+ wrap_here (" ");
+ printf_filtered (" from %s", PC_LOAD_SEGMENT (fi->pc));
+ }
+#endif
+ printf_filtered ("\n");
+ }
+
+ if ((source != 0) && sal.symtab)
+ {
+ int done = 0;
+ int mid_statement = source < 0 && fi->pc != sal.pc;
+ if (frame_file_full_name)
+ done = identify_source_line (sal.symtab, sal.line, mid_statement,
+ fi->pc);
+ if (!done)
+ {
+ if (addressprint && mid_statement)
+ printf_filtered ("%s\t", local_hex_string((unsigned long) fi->pc));
+ print_source_lines (sal.symtab, sal.line, sal.line + 1, 0);
+ }
+ current_source_line = max (sal.line - lines_to_list/2, 1);
+ }
+ if (source != 0)
+ set_default_breakpoint (1, fi->pc, sal.symtab, sal.line);
+
+ fflush (stdout);
+}
+
+/*
+ * Read a frame specification in whatever the appropriate format is.
+ * Call error() if the specification is in any way invalid (i.e.
+ * this function never returns NULL).
+ */
+static FRAME
+parse_frame_specification (frame_exp)
+ char *frame_exp;
+{
+ int numargs = 0;
+#define MAXARGS 4
+ CORE_ADDR args[MAXARGS];
+
+ if (frame_exp)
+ {
+ char *addr_string, *p;
+ struct cleanup *tmp_cleanup;
+
+ while (*frame_exp == ' ') frame_exp++;
+
+ while (*frame_exp)
+ {
+ if (numargs > MAXARGS)
+ error ("Too many args in frame specification");
+ /* Parse an argument. */
+ for (p = frame_exp; *p && *p != ' '; p++)
+ ;
+ addr_string = savestring(frame_exp, p - frame_exp);
+
+ {
+ tmp_cleanup = make_cleanup (free, addr_string);
+ args[numargs++] = parse_and_eval_address (addr_string);
+ do_cleanups (tmp_cleanup);
+ }
+
+ /* Skip spaces, move to possible next arg. */
+ while (*p == ' ') p++;
+ frame_exp = p;
+ }
+ }
+
+ switch (numargs)
+ {
+ case 0:
+ if (selected_frame == NULL)
+ error ("No selected frame.");
+ return selected_frame;
+ /* NOTREACHED */
+ case 1:
+ {
+ int level = args[0];
+ FRAME fid = find_relative_frame (get_current_frame (), &level);
+ FRAME tfid;
+
+ if (level == 0)
+ /* find_relative_frame was successful */
+ return fid;
+
+ /* If (s)he specifies the frame with an address, he deserves what
+ (s)he gets. Still, give the highest one that matches. */
+
+ for (fid = get_current_frame ();
+ fid && FRAME_FP (fid) != args[0];
+ fid = get_prev_frame (fid))
+ ;
+
+ if (fid)
+ while ((tfid = get_prev_frame (fid)) &&
+ (FRAME_FP (tfid) == args[0]))
+ fid = tfid;
+
+ /* We couldn't identify the frame as an existing frame, but
+ perhaps we can create one with a single argument.
+ Fall through to default case; it's up to SETUP_ARBITRARY_FRAME
+ to complain if it doesn't like a single arg. */
+ }
+
+ default:
+#ifdef SETUP_ARBITRARY_FRAME
+ return SETUP_ARBITRARY_FRAME (numargs, args);
+#else
+ /* Usual case. Do it here rather than have everyone supply
+ a SETUP_ARBITRARY_FRAME that does this. */
+ if (numargs == 1)
+ return create_new_frame (args[0], 0);
+ error ("Too many args in frame specification");
+#endif
+ /* NOTREACHED */
+ }
+ /* NOTREACHED */
+}
+
+/* FRAME_ARGS_ADDRESS_CORRECT is just like FRAME_ARGS_ADDRESS except
+ that if it is unsure about the answer, it returns 0
+ instead of guessing (this happens on the VAX and i960, for example).
+
+ On most machines, we never have to guess about the args address,
+ so FRAME_ARGS_ADDRESS{,_CORRECT} are the same. */
+#if !defined (FRAME_ARGS_ADDRESS_CORRECT)
+#define FRAME_ARGS_ADDRESS_CORRECT FRAME_ARGS_ADDRESS
+#endif
+
+/* Print verbosely the selected frame or the frame at address ADDR.
+ This means absolutely all information in the frame is printed. */
+
+static void
+frame_info (addr_exp, from_tty)
+ char *addr_exp;
+ int from_tty;
+{
+ FRAME frame;
+ struct frame_info *fi;
+ struct frame_saved_regs fsr;
+ struct symtab_and_line sal;
+ struct symbol *func;
+ struct symtab *s;
+ FRAME calling_frame;
+ int i, count;
+ char *funname = 0;
+ enum language funlang = language_unknown;
+
+ if (!target_has_stack)
+ error ("No stack.");
+
+ frame = parse_frame_specification (addr_exp);
+ if (!frame)
+ error ("Invalid frame specified.");
+
+ fi = get_frame_info (frame);
+ sal = find_pc_line (fi->pc,
+ fi->next != NULL && fi->next->signal_handler_caller == 0);
+ func = get_frame_function (frame);
+ s = find_pc_symtab(fi->pc);
+ if (func)
+ {
+ funname = SYMBOL_NAME (func);
+ funlang = SYMBOL_LANGUAGE (func);
+ }
+ else
+ {
+ register struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (fi->pc);
+ if (msymbol != NULL)
+ {
+ funname = SYMBOL_NAME (msymbol);
+ funlang = SYMBOL_LANGUAGE (msymbol);
+ }
+ }
+ calling_frame = get_prev_frame (frame);
+
+ if (!addr_exp && selected_frame_level >= 0) {
+ printf_filtered ("Stack level %d, frame at %s:\n",
+ selected_frame_level,
+ local_hex_string((unsigned long) FRAME_FP(frame)));
+ } else {
+ printf_filtered ("Stack frame at %s:\n",
+ local_hex_string((unsigned long) FRAME_FP(frame)));
+ }
+ printf_filtered (" %s = %s",
+ reg_names[PC_REGNUM],
+ local_hex_string((unsigned long) fi->pc));
+
+ wrap_here (" ");
+ if (funname)
+ {
+ printf_filtered (" in ");
+ fprintf_symbol_filtered (stdout, funname, funlang,
+ DMGL_ANSI | DMGL_PARAMS);
+ }
+ wrap_here (" ");
+ if (sal.symtab)
+ printf_filtered (" (%s:%d)", sal.symtab->filename, sal.line);
+ puts_filtered ("; ");
+ wrap_here (" ");
+ printf_filtered ("saved %s %s\n", reg_names[PC_REGNUM],
+ local_hex_string((unsigned long) FRAME_SAVED_PC (frame)));
+
+ {
+ int frameless = 0;
+#ifdef FRAMELESS_FUNCTION_INVOCATION
+ FRAMELESS_FUNCTION_INVOCATION (fi, frameless);
+#endif
+ if (frameless)
+ printf_filtered (" (FRAMELESS),");
+ }
+
+ if (calling_frame)
+ printf_filtered (" called by frame at %s",
+ local_hex_string((unsigned long) FRAME_FP (calling_frame)));
+ if (fi->next && calling_frame)
+ puts_filtered (",");
+ wrap_here (" ");
+ if (fi->next)
+ printf_filtered (" caller of frame at %s",
+ local_hex_string ((unsigned long) fi->next->frame));
+ if (fi->next || calling_frame)
+ puts_filtered ("\n");
+ if (s)
+ printf_filtered(" source language %s.\n", language_str(s->language));
+
+#ifdef PRINT_EXTRA_FRAME_INFO
+ PRINT_EXTRA_FRAME_INFO (fi);
+#endif
+
+ {
+ /* Address of the argument list for this frame, or 0. */
+ CORE_ADDR arg_list = FRAME_ARGS_ADDRESS_CORRECT (fi);
+ /* Number of args for this frame, or -1 if unknown. */
+ int numargs;
+
+ if (arg_list == 0)
+ printf_filtered (" Arglist at unknown address.\n");
+ else
+ {
+ printf_filtered (" Arglist at %s,",
+ local_hex_string((unsigned long) arg_list));
+
+ FRAME_NUM_ARGS (numargs, fi);
+ if (numargs < 0)
+ puts_filtered (" args: ");
+ else if (numargs == 0)
+ puts_filtered (" no args.");
+ else if (numargs == 1)
+ puts_filtered (" 1 arg: ");
+ else
+ printf_filtered (" %d args: ", numargs);
+ print_frame_args (func, fi, numargs, stdout);
+ puts_filtered ("\n");
+ }
+ }
+ {
+ /* Address of the local variables for this frame, or 0. */
+ CORE_ADDR arg_list = FRAME_LOCALS_ADDRESS (fi);
+
+ if (arg_list == 0)
+ printf_filtered (" Locals at unknown address,");
+ else
+ printf_filtered (" Locals at %s,",
+ local_hex_string((unsigned long) arg_list));
+ }
+
+#if defined (FRAME_FIND_SAVED_REGS)
+ get_frame_saved_regs (fi, &fsr);
+ /* The sp is special; what's returned isn't the save address, but
+ actually the value of the previous frame's sp. */
+ printf_filtered (" Previous frame's sp is %s\n",
+ local_hex_string((unsigned long) fsr.regs[SP_REGNUM]));
+ count = 0;
+ for (i = 0; i < NUM_REGS; i++)
+ if (fsr.regs[i] && i != SP_REGNUM)
+ {
+ if (count == 0)
+ puts_filtered (" Saved registers:\n ");
+ else
+ puts_filtered (",");
+ wrap_here (" ");
+ printf_filtered (" %s at %s", reg_names[i],
+ local_hex_string((unsigned long) fsr.regs[i]));
+ count++;
+ }
+ if (count)
+ puts_filtered ("\n");
+#endif /* Have FRAME_FIND_SAVED_REGS. */
+}
+
+#if 0
+/* Set a limit on the number of frames printed by default in a
+ backtrace. */
+
+static int backtrace_limit;
+
+static void
+set_backtrace_limit_command (count_exp, from_tty)
+ char *count_exp;
+ int from_tty;
+{
+ int count = parse_and_eval_address (count_exp);
+
+ if (count < 0)
+ error ("Negative argument not meaningful as backtrace limit.");
+
+ backtrace_limit = count;
+}
+
+static void
+backtrace_limit_info (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ if (arg)
+ error ("\"Info backtrace-limit\" takes no arguments.");
+
+ printf ("Backtrace limit: %d.\n", backtrace_limit);
+}
+#endif
+
+/* Print briefly all stack frames or just the innermost COUNT frames. */
+
+static void
+backtrace_command (count_exp, from_tty)
+ char *count_exp;
+ int from_tty;
+{
+ struct frame_info *fi;
+ register int count;
+ register FRAME frame;
+ register int i;
+ register FRAME trailing;
+ register int trailing_level;
+
+ if (!target_has_stack)
+ error ("No stack.");
+
+ /* The following code must do two things. First, it must
+ set the variable TRAILING to the frame from which we should start
+ printing. Second, it must set the variable count to the number
+ of frames which we should print, or -1 if all of them. */
+ trailing = get_current_frame ();
+ trailing_level = 0;
+ if (count_exp)
+ {
+ count = parse_and_eval_address (count_exp);
+ if (count < 0)
+ {
+ FRAME current;
+
+ count = -count;
+
+ current = trailing;
+ while (current && count--)
+ {
+ QUIT;
+ current = get_prev_frame (current);
+ }
+
+ /* Will stop when CURRENT reaches the top of the stack. TRAILING
+ will be COUNT below it. */
+ while (current)
+ {
+ QUIT;
+ trailing = get_prev_frame (trailing);
+ current = get_prev_frame (current);
+ trailing_level++;
+ }
+
+ count = -1;
+ }
+ }
+ else
+ count = -1;
+
+ if (info_verbose)
+ {
+ struct partial_symtab *ps;
+
+ /* Read in symbols for all of the frames. Need to do this in
+ a separate pass so that "Reading in symbols for xxx" messages
+ don't screw up the appearance of the backtrace. Also
+ if people have strong opinions against reading symbols for
+ backtrace this may have to be an option. */
+ i = count;
+ for (frame = trailing;
+ frame != NULL && i--;
+ frame = get_prev_frame (frame))
+ {
+ QUIT;
+ fi = get_frame_info (frame);
+ ps = find_pc_psymtab (fi->pc);
+ if (ps)
+ PSYMTAB_TO_SYMTAB (ps); /* Force syms to come in */
+ }
+ }
+
+ for (i = 0, frame = trailing;
+ frame && count--;
+ i++, frame = get_prev_frame (frame))
+ {
+ QUIT;
+ fi = get_frame_info (frame);
+
+ /* Don't use print_stack_frame; if an error() occurs it probably
+ means further attempts to backtrace would fail (on the other
+ hand, perhaps the code does or could be fixed to make sure
+ the frame->prev field gets set to NULL in that case). */
+ print_frame_info (fi, trailing_level + i, 0, 1);
+ }
+
+ /* If we've stopped before the end, mention that. */
+ if (frame && from_tty)
+ printf_filtered ("(More stack frames follow...)\n");
+}
+
+/* Print the local variables of a block B active in FRAME.
+ Return 1 if any variables were printed; 0 otherwise. */
+
+static int
+print_block_frame_locals (b, frame, stream)
+ struct block *b;
+ register FRAME frame;
+ register FILE *stream;
+{
+ int nsyms;
+ register int i;
+ register struct symbol *sym;
+ register int values_printed = 0;
+
+ nsyms = BLOCK_NSYMS (b);
+
+ for (i = 0; i < nsyms; i++)
+ {
+ sym = BLOCK_SYM (b, i);
+ if (SYMBOL_CLASS (sym) == LOC_LOCAL
+ || SYMBOL_CLASS (sym) == LOC_REGISTER
+ || SYMBOL_CLASS (sym) == LOC_STATIC)
+ {
+ values_printed = 1;
+ fputs_filtered (SYMBOL_SOURCE_NAME (sym), stream);
+ fputs_filtered (" = ", stream);
+ print_variable_value (sym, frame, stream);
+ fprintf_filtered (stream, "\n");
+ }
+ }
+ return values_printed;
+}
+
+/* Same, but print labels. */
+
+static int
+print_block_frame_labels (b, have_default, stream)
+ struct block *b;
+ int *have_default;
+ register FILE *stream;
+{
+ int nsyms;
+ register int i;
+ register struct symbol *sym;
+ register int values_printed = 0;
+
+ nsyms = BLOCK_NSYMS (b);
+
+ for (i = 0; i < nsyms; i++)
+ {
+ sym = BLOCK_SYM (b, i);
+ if (STREQ (SYMBOL_NAME (sym), "default"))
+ {
+ if (*have_default)
+ continue;
+ *have_default = 1;
+ }
+ if (SYMBOL_CLASS (sym) == LOC_LABEL)
+ {
+ struct symtab_and_line sal;
+ sal = find_pc_line (SYMBOL_VALUE_ADDRESS (sym), 0);
+ values_printed = 1;
+ fputs_filtered (SYMBOL_SOURCE_NAME (sym), stream);
+ if (addressprint)
+ fprintf_filtered (stream, " %s",
+ local_hex_string((unsigned long) SYMBOL_VALUE_ADDRESS (sym)));
+ fprintf_filtered (stream, " in file %s, line %d\n",
+ sal.symtab->filename, sal.line);
+ }
+ }
+ return values_printed;
+}
+
+/* Print on STREAM all the local variables in frame FRAME,
+ including all the blocks active in that frame
+ at its current pc.
+
+ Returns 1 if the job was done,
+ or 0 if nothing was printed because we have no info
+ on the function running in FRAME. */
+
+static void
+print_frame_local_vars (frame, stream)
+ register FRAME frame;
+ register FILE *stream;
+{
+ register struct block *block = get_frame_block (frame);
+ register int values_printed = 0;
+
+ if (block == 0)
+ {
+ fprintf_filtered (stream, "No symbol table info available.\n");
+ return;
+ }
+
+ while (block != 0)
+ {
+ if (print_block_frame_locals (block, frame, stream))
+ values_printed = 1;
+ /* After handling the function's top-level block, stop.
+ Don't continue to its superblock, the block of
+ per-file symbols. */
+ if (BLOCK_FUNCTION (block))
+ break;
+ block = BLOCK_SUPERBLOCK (block);
+ }
+
+ if (!values_printed)
+ {
+ fprintf_filtered (stream, "No locals.\n");
+ }
+}
+
+/* Same, but print labels. */
+
+static void
+print_frame_label_vars (frame, this_level_only, stream)
+ register FRAME frame;
+ int this_level_only;
+ register FILE *stream;
+{
+ register struct blockvector *bl;
+ register struct block *block = get_frame_block (frame);
+ register int values_printed = 0;
+ int index, have_default = 0;
+ char *blocks_printed;
+ struct frame_info *fi = get_frame_info (frame);
+ CORE_ADDR pc = fi->pc;
+
+ if (block == 0)
+ {
+ fprintf_filtered (stream, "No symbol table info available.\n");
+ return;
+ }
+
+ bl = blockvector_for_pc (BLOCK_END (block) - 4, &index);
+ blocks_printed = (char *) alloca (BLOCKVECTOR_NBLOCKS (bl) * sizeof (char));
+ memset (blocks_printed, 0, BLOCKVECTOR_NBLOCKS (bl) * sizeof (char));
+
+ while (block != 0)
+ {
+ CORE_ADDR end = BLOCK_END (block) - 4;
+ int last_index;
+
+ if (bl != blockvector_for_pc (end, &index))
+ error ("blockvector blotch");
+ if (BLOCKVECTOR_BLOCK (bl, index) != block)
+ error ("blockvector botch");
+ last_index = BLOCKVECTOR_NBLOCKS (bl);
+ index += 1;
+
+ /* Don't print out blocks that have gone by. */
+ while (index < last_index
+ && BLOCK_END (BLOCKVECTOR_BLOCK (bl, index)) < pc)
+ index++;
+
+ while (index < last_index
+ && BLOCK_END (BLOCKVECTOR_BLOCK (bl, index)) < end)
+ {
+ if (blocks_printed[index] == 0)
+ {
+ if (print_block_frame_labels (BLOCKVECTOR_BLOCK (bl, index), &have_default, stream))
+ values_printed = 1;
+ blocks_printed[index] = 1;
+ }
+ index++;
+ }
+ if (have_default)
+ return;
+ if (values_printed && this_level_only)
+ return;
+
+ /* After handling the function's top-level block, stop.
+ Don't continue to its superblock, the block of
+ per-file symbols. */
+ if (BLOCK_FUNCTION (block))
+ break;
+ block = BLOCK_SUPERBLOCK (block);
+ }
+
+ if (!values_printed && !this_level_only)
+ {
+ fprintf_filtered (stream, "No catches.\n");
+ }
+}
+
+/* ARGSUSED */
+static void
+locals_info (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ if (!selected_frame)
+ error ("No frame selected.");
+ print_frame_local_vars (selected_frame, stdout);
+}
+
+static void
+catch_info (ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ if (!selected_frame)
+ error ("No frame selected.");
+ print_frame_label_vars (selected_frame, 0, stdout);
+}
+
+static void
+print_frame_arg_vars (frame, stream)
+ register FRAME frame;
+ register FILE *stream;
+{
+ struct symbol *func = get_frame_function (frame);
+ register struct block *b;
+ int nsyms;
+ register int i;
+ register struct symbol *sym, *sym2;
+ register int values_printed = 0;
+
+ if (func == 0)
+ {
+ fprintf_filtered (stream, "No symbol table info available.\n");
+ return;
+ }
+
+ b = SYMBOL_BLOCK_VALUE (func);
+ nsyms = BLOCK_NSYMS (b);
+
+ for (i = 0; i < nsyms; i++)
+ {
+ sym = BLOCK_SYM (b, i);
+ switch (SYMBOL_CLASS (sym))
+ {
+ case LOC_ARG:
+ case LOC_LOCAL_ARG:
+ case LOC_REF_ARG:
+ case LOC_REGPARM:
+ case LOC_REGPARM_ADDR:
+ case LOC_BASEREG_ARG:
+ values_printed = 1;
+ fputs_filtered (SYMBOL_SOURCE_NAME (sym), stream);
+ fputs_filtered (" = ", stream);
+
+ /* We have to look up the symbol because arguments can have
+ two entries (one a parameter, one a local) and the one we
+ want is the local, which lookup_symbol will find for us.
+ This includes gcc1 (not gcc2) on the sparc when passing a
+ small structure and gcc2 when the argument type is float
+ and it is passed as a double and converted to float by
+ the prologue (in the latter case the type of the LOC_ARG
+ symbol is double and the type of the LOC_LOCAL symbol is
+ float). There are also LOC_ARG/LOC_REGISTER pairs which
+ are not combined in symbol-reading. */
+
+ sym2 = lookup_symbol (SYMBOL_NAME (sym),
+ b, VAR_NAMESPACE, (int *)NULL, (struct symtab **)NULL);
+ print_variable_value (sym2, frame, stream);
+ fprintf_filtered (stream, "\n");
+ break;
+
+ default:
+ /* Don't worry about things which aren't arguments. */
+ break;
+ }
+ }
+
+ if (!values_printed)
+ {
+ fprintf_filtered (stream, "No arguments.\n");
+ }
+}
+
+static void
+args_info (ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ if (!selected_frame)
+ error ("No frame selected.");
+ print_frame_arg_vars (selected_frame, stdout);
+}
+
+/* Select frame FRAME, and note that its stack level is LEVEL.
+ LEVEL may be -1 if an actual level number is not known. */
+
+void
+select_frame (frame, level)
+ FRAME frame;
+ int level;
+{
+ register struct symtab *s;
+
+ selected_frame = frame;
+ selected_frame_level = level;
+
+ /* Ensure that symbols for this frame are read in. Also, determine the
+ source language of this frame, and switch to it if desired. */
+ if (frame)
+ {
+ s = find_pc_symtab (get_frame_info (frame)->pc);
+ if (s
+ && s->language != current_language->la_language
+ && s->language != language_unknown
+ && language_mode == language_mode_auto) {
+ set_language(s->language);
+ }
+ }
+}
+
+/* Store the selected frame and its level into *FRAMEP and *LEVELP.
+ If there is no selected frame, *FRAMEP is set to NULL. */
+
+void
+record_selected_frame (frameaddrp, levelp)
+ FRAME_ADDR *frameaddrp;
+ int *levelp;
+{
+ *frameaddrp = selected_frame ? FRAME_FP (selected_frame) : 0;
+ *levelp = selected_frame_level;
+}
+
+/* Return the symbol-block in which the selected frame is executing.
+ Can return zero under various legitimate circumstances. */
+
+struct block *
+get_selected_block ()
+{
+ if (!target_has_stack)
+ return 0;
+
+ if (!selected_frame)
+ return get_current_block ();
+ return get_frame_block (selected_frame);
+}
+
+/* Find a frame a certain number of levels away from FRAME.
+ LEVEL_OFFSET_PTR points to an int containing the number of levels.
+ Positive means go to earlier frames (up); negative, the reverse.
+ The int that contains the number of levels is counted toward
+ zero as the frames for those levels are found.
+ If the top or bottom frame is reached, that frame is returned,
+ but the final value of *LEVEL_OFFSET_PTR is nonzero and indicates
+ how much farther the original request asked to go. */
+
+FRAME
+find_relative_frame (frame, level_offset_ptr)
+ register FRAME frame;
+ register int* level_offset_ptr;
+{
+ register FRAME prev;
+ register FRAME frame1;
+
+ /* Going up is simple: just do get_prev_frame enough times
+ or until initial frame is reached. */
+ while (*level_offset_ptr > 0)
+ {
+ prev = get_prev_frame (frame);
+ if (prev == 0)
+ break;
+ (*level_offset_ptr)--;
+ frame = prev;
+ }
+ /* Going down is just as simple. */
+ if (*level_offset_ptr < 0)
+ {
+ while (*level_offset_ptr < 0) {
+ frame1 = get_next_frame (frame);
+ if (!frame1)
+ break;
+ frame = frame1;
+ (*level_offset_ptr)++;
+ }
+ }
+ return frame;
+}
+
+/* The "select_frame" command. With no arg, NOP.
+ With arg LEVEL_EXP, select the frame at level LEVEL if it is a
+ valid level. Otherwise, treat level_exp as an address expression
+ and select it. See parse_frame_specification for more info on proper
+ frame expressions. */
+
+/* ARGSUSED */
+static void
+select_frame_command (level_exp, from_tty)
+ char *level_exp;
+ int from_tty;
+{
+ register FRAME frame, frame1;
+ unsigned int level = 0;
+
+ if (!target_has_stack)
+ error ("No stack.");
+
+ frame = parse_frame_specification (level_exp);
+
+ /* Try to figure out what level this frame is. But if there is
+ no current stack, don't error out -- let the user set one. */
+ frame1 = 0;
+ if (get_current_frame()) {
+ for (frame1 = get_prev_frame (0);
+ frame1 && frame1 != frame;
+ frame1 = get_prev_frame (frame1))
+ level++;
+ }
+
+ if (!frame1)
+ level = 0;
+
+ select_frame (frame, level);
+}
+
+/* The "frame" command. With no arg, print selected frame briefly.
+ With arg, behaves like select_frame and then prints the selected
+ frame. */
+
+static void
+frame_command (level_exp, from_tty)
+ char *level_exp;
+ int from_tty;
+{
+ select_frame_command (level_exp, from_tty);
+ print_stack_frame (selected_frame, selected_frame_level, 1);
+}
+
+/* Select the frame up one or COUNT stack levels
+ from the previously selected frame, and print it briefly. */
+
+/* ARGSUSED */
+static void
+up_silently_command (count_exp, from_tty)
+ char *count_exp;
+ int from_tty;
+{
+ register FRAME frame;
+ int count = 1, count1;
+ if (count_exp)
+ count = parse_and_eval_address (count_exp);
+ count1 = count;
+
+ if (target_has_stack == 0 || selected_frame == 0)
+ error ("No stack.");
+
+ frame = find_relative_frame (selected_frame, &count1);
+ if (count1 != 0 && count_exp == 0)
+ error ("Initial frame selected; you cannot go up.");
+ select_frame (frame, selected_frame_level + count - count1);
+}
+
+static void
+up_command (count_exp, from_tty)
+ char *count_exp;
+ int from_tty;
+{
+ up_silently_command (count_exp, from_tty);
+ print_stack_frame (selected_frame, selected_frame_level, 1);
+}
+
+/* Select the frame down one or COUNT stack levels
+ from the previously selected frame, and print it briefly. */
+
+/* ARGSUSED */
+static void
+down_silently_command (count_exp, from_tty)
+ char *count_exp;
+ int from_tty;
+{
+ register FRAME frame;
+ int count = -1, count1;
+ if (count_exp)
+ count = - parse_and_eval_address (count_exp);
+ count1 = count;
+
+ if (target_has_stack == 0 || selected_frame == 0)
+ error ("No stack.");
+
+ frame = find_relative_frame (selected_frame, &count1);
+ if (count1 != 0 && count_exp == 0)
+ error ("Bottom (i.e., innermost) frame selected; you cannot go down.");
+ select_frame (frame, selected_frame_level + count - count1);
+}
+
+
+static void
+down_command (count_exp, from_tty)
+ char *count_exp;
+ int from_tty;
+{
+ down_silently_command (count_exp, from_tty);
+ print_stack_frame (selected_frame, selected_frame_level, 1);
+}
+
+static void
+return_command (retval_exp, from_tty)
+ char *retval_exp;
+ int from_tty;
+{
+ struct symbol *thisfun;
+ FRAME_ADDR selected_frame_addr;
+ CORE_ADDR selected_frame_pc;
+ FRAME frame;
+ value return_value = NULL;
+
+ if (selected_frame == NULL)
+ error ("No selected frame.");
+ thisfun = get_frame_function (selected_frame);
+ selected_frame_addr = FRAME_FP (selected_frame);
+ selected_frame_pc = (get_frame_info (selected_frame))->pc;
+
+ /* Compute the return value (if any -- possibly getting errors here). */
+
+ if (retval_exp)
+ {
+ return_value = parse_and_eval (retval_exp);
+
+ /* Make sure we have fully evaluated it, since
+ it might live in the stack frame we're about to pop. */
+ if (VALUE_LAZY (return_value))
+ value_fetch_lazy (return_value);
+ }
+
+ /* If interactive, require confirmation. */
+
+ if (from_tty)
+ {
+ if (thisfun != 0)
+ {
+ if (!query ("Make %s return now? ", SYMBOL_SOURCE_NAME (thisfun)))
+ {
+ error ("Not confirmed.");
+ /* NOTREACHED */
+ }
+ }
+ else
+ if (!query ("Make selected stack frame return now? "))
+ error ("Not confirmed.");
+ }
+
+ /* Do the real work. Pop until the specified frame is current. We
+ use this method because the selected_frame is not valid after
+ a POP_FRAME. The pc comparison makes this work even if the
+ selected frame shares its fp with another frame. */
+
+ while ( selected_frame_addr != FRAME_FP (frame = get_current_frame())
+ || selected_frame_pc != (get_frame_info (frame))->pc )
+ POP_FRAME;
+
+ /* Then pop that frame. */
+
+ POP_FRAME;
+
+ /* Compute the return value (if any) and store in the place
+ for return values. */
+
+ if (retval_exp)
+ set_return_value (return_value);
+
+ /* If interactive, print the frame that is now current. */
+
+ if (from_tty)
+ frame_command ("0", 1);
+}
+
+/* Gets the language of the current frame. */
+enum language
+get_frame_language()
+{
+ register struct symtab *s;
+ FRAME fr;
+ enum language flang; /* The language of the current frame */
+
+ fr = get_frame_info(selected_frame);
+ if(fr)
+ {
+ s = find_pc_symtab(fr->pc);
+ if(s)
+ flang = s->language;
+ else
+ flang = language_unknown;
+ }
+ else
+ flang = language_unknown;
+
+ return flang;
+}
+
+void
+_initialize_stack ()
+{
+#if 0
+ backtrace_limit = 30;
+#endif
+
+ add_com ("return", class_stack, return_command,
+ "Make selected stack frame return to its caller.\n\
+Control remains in the debugger, but when you continue\n\
+execution will resume in the frame above the one now selected.\n\
+If an argument is given, it is an expression for the value to return.");
+
+ add_com ("up", class_stack, up_command,
+ "Select and print stack frame that called this one.\n\
+An argument says how many frames up to go.");
+ add_com ("up-silently", class_support, up_silently_command,
+ "Same as the `up' command, but does not print anything.\n\
+This is useful in command scripts.");
+
+ add_com ("down", class_stack, down_command,
+ "Select and print stack frame called by this one.\n\
+An argument says how many frames down to go.");
+ add_com_alias ("do", "down", class_stack, 1);
+ add_com_alias ("dow", "down", class_stack, 1);
+ add_com ("down-silently", class_support, down_silently_command,
+ "Same as the `down' command, but does not print anything.\n\
+This is useful in command scripts.");
+
+ add_com ("frame", class_stack, frame_command,
+ "Select and print a stack frame.\n\
+With no argument, print the selected stack frame. (See also \"info frame\").\n\
+An argument specifies the frame to select.\n\
+It can be a stack frame number or the address of the frame.\n\
+With argument, nothing is printed if input is coming from\n\
+a command file or a user-defined command.");
+
+ add_com_alias ("f", "frame", class_stack, 1);
+
+ add_com ("select-frame", class_stack, select_frame_command,
+ "Select a stack frame without printing anything.\n\
+An argument specifies the frame to select.\n\
+It can be a stack frame number or the address of the frame.\n");
+
+ add_com ("backtrace", class_stack, backtrace_command,
+ "Print backtrace of all stack frames, or innermost COUNT frames.\n\
+With a negative argument, print outermost -COUNT frames.");
+ add_com_alias ("bt", "backtrace", class_stack, 0);
+ add_com_alias ("where", "backtrace", class_alias, 0);
+ add_info ("stack", backtrace_command,
+ "Backtrace of the stack, or innermost COUNT frames.");
+ add_info_alias ("s", "stack", 1);
+ add_info ("frame", frame_info,
+ "All about selected stack frame, or frame at ADDR.");
+ add_info_alias ("f", "frame", 1);
+ add_info ("locals", locals_info,
+ "Local variables of current stack frame.");
+ add_info ("args", args_info,
+ "Argument variables of current stack frame.");
+ add_info ("catch", catch_info,
+ "Exceptions that can be caught in the current stack frame.");
+
+#if 0
+ add_cmd ("backtrace-limit", class_stack, set_backtrace_limit_command,
+ "Specify maximum number of frames for \"backtrace\" to print by default.",
+ &setlist);
+ add_info ("backtrace-limit", backtrace_limit_info,
+ "The maximum number of frames for \"backtrace\" to print by default.");
+#endif
+}
diff --git a/gnu/usr.bin/gdb/gdb/symfile.c b/gnu/usr.bin/gdb/gdb/symfile.c
new file mode 100644
index 0000000..197c0c3
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/symfile.c
@@ -0,0 +1,1489 @@
+/* Generic symbol file reading for the GNU debugger, GDB.
+ Copyright 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+ Contributed by Cygnus Support, using pieces from other GDB modules.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "gdbcore.h"
+#include "frame.h"
+#include "target.h"
+#include "value.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "gdbcmd.h"
+#include "breakpoint.h"
+#include "language.h"
+#include "complaints.h"
+#include "demangle.h"
+#include "inferior.h" /* for write_pc */
+
+#include <obstack.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <ctype.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/* Global variables owned by this file */
+
+int readnow_symbol_files; /* Read full symbols immediately */
+
+struct complaint oldsyms_complaint = {
+ "Replacing old symbols for `%s'", 0, 0
+};
+
+struct complaint empty_symtab_complaint = {
+ "Empty symbol table found for `%s'", 0, 0
+};
+
+/* External variables and functions referenced. */
+
+extern int info_verbose;
+
+/* Functions this file defines */
+
+static void
+set_initial_language PARAMS ((void));
+
+static void
+load_command PARAMS ((char *, int));
+
+static void
+add_symbol_file_command PARAMS ((char *, int));
+
+static void
+cashier_psymtab PARAMS ((struct partial_symtab *));
+
+static int
+compare_psymbols PARAMS ((const void *, const void *));
+
+static int
+compare_symbols PARAMS ((const void *, const void *));
+
+static bfd *
+symfile_bfd_open PARAMS ((char *));
+
+static void
+find_sym_fns PARAMS ((struct objfile *));
+
+/* List of all available sym_fns. On gdb startup, each object file reader
+ calls add_symtab_fns() to register information on each format it is
+ prepared to read. */
+
+static struct sym_fns *symtab_fns = NULL;
+
+/* Structures with which to manage partial symbol allocation. */
+
+struct psymbol_allocation_list global_psymbols = {0}, static_psymbols = {0};
+
+/* Flag for whether user will be reloading symbols multiple times.
+ Defaults to ON for VxWorks, otherwise OFF. */
+
+#ifdef SYMBOL_RELOADING_DEFAULT
+int symbol_reloading = SYMBOL_RELOADING_DEFAULT;
+#else
+int symbol_reloading = 0;
+#endif
+
+
+/* Since this function is called from within qsort, in an ANSI environment
+ it must conform to the prototype for qsort, which specifies that the
+ comparison function takes two "void *" pointers. */
+
+static int
+compare_symbols (s1p, s2p)
+ const PTR s1p;
+ const PTR s2p;
+{
+ register struct symbol **s1, **s2;
+
+ s1 = (struct symbol **) s1p;
+ s2 = (struct symbol **) s2p;
+
+ return (STRCMP (SYMBOL_NAME (*s1), SYMBOL_NAME (*s2)));
+}
+
+/*
+
+LOCAL FUNCTION
+
+ compare_psymbols -- compare two partial symbols by name
+
+DESCRIPTION
+
+ Given pointer to two partial symbol table entries, compare
+ them by name and return -N, 0, or +N (ala strcmp). Typically
+ used by sorting routines like qsort().
+
+NOTES
+
+ Does direct compare of first two characters before punting
+ and passing to strcmp for longer compares. Note that the
+ original version had a bug whereby two null strings or two
+ identically named one character strings would return the
+ comparison of memory following the null byte.
+
+ */
+
+static int
+compare_psymbols (s1p, s2p)
+ const PTR s1p;
+ const PTR s2p;
+{
+ register char *st1 = SYMBOL_NAME ((struct partial_symbol *) s1p);
+ register char *st2 = SYMBOL_NAME ((struct partial_symbol *) s2p);
+
+ if ((st1[0] - st2[0]) || !st1[0])
+ {
+ return (st1[0] - st2[0]);
+ }
+ else if ((st1[1] - st2[1]) || !st1[1])
+ {
+ return (st1[1] - st2[1]);
+ }
+ else
+ {
+ return (STRCMP (st1 + 2, st2 + 2));
+ }
+}
+
+void
+sort_pst_symbols (pst)
+ struct partial_symtab *pst;
+{
+ /* Sort the global list; don't sort the static list */
+
+ qsort (pst -> objfile -> global_psymbols.list + pst -> globals_offset,
+ pst -> n_global_syms, sizeof (struct partial_symbol),
+ compare_psymbols);
+}
+
+/* Call sort_block_syms to sort alphabetically the symbols of one block. */
+
+void
+sort_block_syms (b)
+ register struct block *b;
+{
+ qsort (&BLOCK_SYM (b, 0), BLOCK_NSYMS (b),
+ sizeof (struct symbol *), compare_symbols);
+}
+
+/* Call sort_symtab_syms to sort alphabetically
+ the symbols of each block of one symtab. */
+
+void
+sort_symtab_syms (s)
+ register struct symtab *s;
+{
+ register struct blockvector *bv;
+ int nbl;
+ int i;
+ register struct block *b;
+
+ if (s == 0)
+ return;
+ bv = BLOCKVECTOR (s);
+ nbl = BLOCKVECTOR_NBLOCKS (bv);
+ for (i = 0; i < nbl; i++)
+ {
+ b = BLOCKVECTOR_BLOCK (bv, i);
+ if (BLOCK_SHOULD_SORT (b))
+ sort_block_syms (b);
+ }
+}
+
+void
+sort_all_symtab_syms ()
+{
+ register struct symtab *s;
+ register struct objfile *objfile;
+
+ for (objfile = object_files; objfile != NULL; objfile = objfile -> next)
+ {
+ for (s = objfile -> symtabs; s != NULL; s = s -> next)
+ {
+ sort_symtab_syms (s);
+ }
+ }
+}
+
+/* Make a copy of the string at PTR with SIZE characters in the symbol obstack
+ (and add a null character at the end in the copy).
+ Returns the address of the copy. */
+
+char *
+obsavestring (ptr, size, obstackp)
+ char *ptr;
+ int size;
+ struct obstack *obstackp;
+{
+ register char *p = (char *) obstack_alloc (obstackp, size + 1);
+ /* Open-coded memcpy--saves function call time.
+ These strings are usually short. */
+ {
+ register char *p1 = ptr;
+ register char *p2 = p;
+ char *end = ptr + size;
+ while (p1 != end)
+ *p2++ = *p1++;
+ }
+ p[size] = 0;
+ return p;
+}
+
+/* Concatenate strings S1, S2 and S3; return the new string.
+ Space is found in the symbol_obstack. */
+
+char *
+obconcat (obstackp, s1, s2, s3)
+ struct obstack *obstackp;
+ const char *s1, *s2, *s3;
+{
+ register int len = strlen (s1) + strlen (s2) + strlen (s3) + 1;
+ register char *val = (char *) obstack_alloc (obstackp, len);
+ strcpy (val, s1);
+ strcat (val, s2);
+ strcat (val, s3);
+ return val;
+}
+
+/* Get the symbol table that corresponds to a partial_symtab.
+ This is fast after the first time you do it. In fact, there
+ is an even faster macro PSYMTAB_TO_SYMTAB that does the fast
+ case inline. */
+
+struct symtab *
+psymtab_to_symtab (pst)
+ register struct partial_symtab *pst;
+{
+ /* If it's been looked up before, return it. */
+ if (pst->symtab)
+ return pst->symtab;
+
+ /* If it has not yet been read in, read it. */
+ if (!pst->readin)
+ {
+ (*pst->read_symtab) (pst);
+ }
+
+ return pst->symtab;
+}
+
+/* Initialize entry point information for this objfile. */
+
+void
+init_entry_point_info (objfile)
+ struct objfile *objfile;
+{
+ /* Save startup file's range of PC addresses to help blockframe.c
+ decide where the bottom of the stack is. */
+
+ if (bfd_get_file_flags (objfile -> obfd) & EXEC_P)
+ {
+ /* Executable file -- record its entry point so we'll recognize
+ the startup file because it contains the entry point. */
+ objfile -> ei.entry_point = bfd_get_start_address (objfile -> obfd);
+ }
+ else
+ {
+ /* Examination of non-executable.o files. Short-circuit this stuff. */
+ /* ~0 will not be in any file, we hope. */
+ objfile -> ei.entry_point = ~0;
+ /* set the startup file to be an empty range. */
+ objfile -> ei.entry_file_lowpc = 0;
+ objfile -> ei.entry_file_highpc = 0;
+ }
+}
+
+/* Get current entry point address. */
+
+CORE_ADDR
+entry_point_address()
+{
+ return symfile_objfile ? symfile_objfile->ei.entry_point : 0;
+}
+
+/* Remember the lowest-addressed loadable section we've seen.
+ This function is called via bfd_map_over_sections. */
+
+#if 0 /* Not used yet */
+static void
+find_lowest_section (abfd, sect, obj)
+ bfd *abfd;
+ asection *sect;
+ PTR obj;
+{
+ asection **lowest = (asection **)obj;
+
+ if (0 == (bfd_get_section_flags (abfd, sect) & SEC_LOAD))
+ return;
+ if (!*lowest)
+ *lowest = sect; /* First loadable section */
+ else if (bfd_section_vma (abfd, *lowest) >= bfd_section_vma (abfd, sect))
+ *lowest = sect; /* A lower loadable section */
+}
+#endif
+
+/* Process a symbol file, as either the main file or as a dynamically
+ loaded file.
+
+ NAME is the file name (which will be tilde-expanded and made
+ absolute herein) (but we don't free or modify NAME itself).
+ FROM_TTY says how verbose to be. MAINLINE specifies whether this
+ is the main symbol file, or whether it's an extra symbol file such
+ as dynamically loaded code. If !mainline, ADDR is the address
+ where the text segment was loaded. If VERBO, the caller has printed
+ a verbose message about the symbol reading (and complaints can be
+ more terse about it). */
+
+void
+syms_from_objfile (objfile, addr, mainline, verbo)
+ struct objfile *objfile;
+ CORE_ADDR addr;
+ int mainline;
+ int verbo;
+{
+ struct section_offsets *section_offsets;
+ asection *lowest_sect;
+ struct cleanup *old_chain;
+
+ init_entry_point_info (objfile);
+ find_sym_fns (objfile);
+
+ /* Make sure that partially constructed symbol tables will be cleaned up
+ if an error occurs during symbol reading. */
+ old_chain = make_cleanup (free_objfile, objfile);
+
+ if (mainline)
+ {
+ /* We will modify the main symbol table, make sure that all its users
+ will be cleaned up if an error occurs during symbol reading. */
+ make_cleanup (clear_symtab_users, 0);
+
+ /* Since no error yet, throw away the old symbol table. */
+
+ if (symfile_objfile != NULL)
+ {
+ free_objfile (symfile_objfile);
+ symfile_objfile = NULL;
+ }
+
+ (*objfile -> sf -> sym_new_init) (objfile);
+ }
+
+ /* Convert addr into an offset rather than an absolute address.
+ We find the lowest address of a loaded segment in the objfile,
+ and assume that <addr> is where that got loaded. Due to historical
+ precedent, we warn if that doesn't happen to be the ".text"
+ segment. */
+
+ if (mainline)
+ {
+ addr = 0; /* No offset from objfile addresses. */
+ }
+ else
+ {
+ lowest_sect = bfd_get_section_by_name (objfile->obfd, ".text");
+#if 0
+ lowest_sect = 0;
+ bfd_map_over_sections (objfile->obfd, find_lowest_section,
+ (PTR) &lowest_sect);
+#endif
+
+ if (lowest_sect == 0)
+ warning ("no loadable sections found in added symbol-file %s",
+ objfile->name);
+ else if (0 == bfd_get_section_name (objfile->obfd, lowest_sect)
+ || !STREQ (".text",
+ bfd_get_section_name (objfile->obfd, lowest_sect)))
+ warning ("Lowest section in %s is %s at 0x%lx",
+ objfile->name,
+ bfd_section_name (objfile->obfd, lowest_sect),
+ (unsigned long) bfd_section_vma (objfile->obfd, lowest_sect));
+
+ if (lowest_sect)
+ addr -= bfd_section_vma (objfile->obfd, lowest_sect);
+ }
+
+ /* Initialize symbol reading routines for this objfile, allow complaints to
+ appear for this new file, and record how verbose to be, then do the
+ initial symbol reading for this file. */
+
+ (*objfile -> sf -> sym_init) (objfile);
+ clear_complaints (1, verbo);
+
+ /* If objfile->sf->sym_offsets doesn't set this, we don't care
+ (currently). */
+ objfile->num_sections = 0; /* krp-FIXME: why zero? */
+ section_offsets = (*objfile -> sf -> sym_offsets) (objfile, addr);
+ objfile->section_offsets = section_offsets;
+
+#ifndef IBM6000_TARGET
+ /* This is a SVR4/SunOS specific hack, I think. In any event, it
+ screws RS/6000. sym_offsets should be doing this sort of thing,
+ because it knows the mapping between bfd sections and
+ section_offsets. */
+ /* This is a hack. As far as I can tell, section offsets are not
+ target dependent. They are all set to addr with a couple of
+ exceptions. The exceptions are sysvr4 shared libraries, whose
+ offsets are kept in solib structures anyway and rs6000 xcoff
+ which handles shared libraries in a completely unique way.
+
+ Section offsets are built similarly, except that they are built
+ by adding addr in all cases because there is no clear mapping
+ from section_offsets into actual sections. Note that solib.c
+ has a different algorythm for finding section offsets.
+
+ These should probably all be collapsed into some target
+ independent form of shared library support. FIXME. */
+
+ if (addr)
+ {
+ struct obj_section *s;
+
+ for (s = objfile->sections; s < objfile->sections_end; ++s)
+ {
+ s->addr -= s->offset;
+ s->addr += addr;
+ s->endaddr -= s->offset;
+ s->endaddr += addr;
+ s->offset += addr;
+ }
+ }
+#endif /* not IBM6000_TARGET */
+
+ (*objfile -> sf -> sym_read) (objfile, section_offsets, mainline);
+
+ /* Don't allow char * to have a typename (else would get caddr_t.) */
+ /* Ditto void *. FIXME should do this for all the builtin types. */
+
+ TYPE_NAME (lookup_pointer_type (builtin_type_char)) = 0;
+ TYPE_NAME (lookup_pointer_type (builtin_type_void)) = 0;
+
+ /* Mark the objfile has having had initial symbol read attempted. Note
+ that this does not mean we found any symbols... */
+
+ objfile -> flags |= OBJF_SYMS;
+
+ /* Discard cleanups as symbol reading was successful. */
+
+ discard_cleanups (old_chain);
+}
+
+/* Perform required actions after either reading in the initial
+ symbols for a new objfile, or mapping in the symbols from a reusable
+ objfile. */
+
+void
+new_symfile_objfile (objfile, mainline, verbo)
+ struct objfile *objfile;
+ int mainline;
+ int verbo;
+{
+
+ /* If this is the main symbol file we have to clean up all users of the
+ old main symbol file. Otherwise it is sufficient to fixup all the
+ breakpoints that may have been redefined by this symbol file. */
+ if (mainline)
+ {
+ /* OK, make it the "real" symbol file. */
+ symfile_objfile = objfile;
+
+ clear_symtab_users ();
+ }
+ else
+ {
+ breakpoint_re_set ();
+ }
+
+ /* We're done reading the symbol file; finish off complaints. */
+ clear_complaints (0, verbo);
+}
+
+/* Process a symbol file, as either the main file or as a dynamically
+ loaded file.
+
+ NAME is the file name (which will be tilde-expanded and made
+ absolute herein) (but we don't free or modify NAME itself).
+ FROM_TTY says how verbose to be. MAINLINE specifies whether this
+ is the main symbol file, or whether it's an extra symbol file such
+ as dynamically loaded code. If !mainline, ADDR is the address
+ where the text segment was loaded.
+
+ Upon success, returns a pointer to the objfile that was added.
+ Upon failure, jumps back to command level (never returns). */
+
+struct objfile *
+symbol_file_add (name, from_tty, addr, mainline, mapped, readnow)
+ char *name;
+ int from_tty;
+ CORE_ADDR addr;
+ int mainline;
+ int mapped;
+ int readnow;
+{
+ struct objfile *objfile;
+ struct partial_symtab *psymtab;
+ bfd *abfd;
+
+ /* Open a bfd for the file, and give user a chance to burp if we'd be
+ interactively wiping out any existing symbols. */
+
+ abfd = symfile_bfd_open (name);
+
+ if ((have_full_symbols () || have_partial_symbols ())
+ && mainline
+ && from_tty
+ && !query ("Load new symbol table from \"%s\"? ", name))
+ error ("Not confirmed.");
+
+ objfile = allocate_objfile (abfd, mapped);
+
+ /* If the objfile uses a mapped symbol file, and we have a psymtab for
+ it, then skip reading any symbols at this time. */
+
+ if ((objfile -> flags & OBJF_MAPPED) && (objfile -> flags & OBJF_SYMS))
+ {
+ /* We mapped in an existing symbol table file that already has had
+ initial symbol reading performed, so we can skip that part. Notify
+ the user that instead of reading the symbols, they have been mapped.
+ */
+ if (from_tty || info_verbose)
+ {
+ printf_filtered ("Mapped symbols for %s...", name);
+ wrap_here ("");
+ fflush (stdout);
+ }
+ init_entry_point_info (objfile);
+ find_sym_fns (objfile);
+ }
+ else
+ {
+ /* We either created a new mapped symbol table, mapped an existing
+ symbol table file which has not had initial symbol reading
+ performed, or need to read an unmapped symbol table. */
+ if (from_tty || info_verbose)
+ {
+ printf_filtered ("Reading symbols from %s...", name);
+ wrap_here ("");
+ fflush (stdout);
+ }
+ syms_from_objfile (objfile, addr, mainline, from_tty);
+ }
+
+ /* We now have at least a partial symbol table. Check to see if the
+ user requested that all symbols be read on initial access via either
+ the gdb startup command line or on a per symbol file basis. Expand
+ all partial symbol tables for this objfile if so. */
+
+ if (readnow || readnow_symbol_files)
+ {
+ if (from_tty || info_verbose)
+ {
+ printf_filtered ("expanding to full symbols...");
+ wrap_here ("");
+ fflush (stdout);
+ }
+
+ for (psymtab = objfile -> psymtabs;
+ psymtab != NULL;
+ psymtab = psymtab -> next)
+ {
+ psymtab_to_symtab (psymtab);
+ }
+ }
+
+ if (from_tty || info_verbose)
+ {
+ printf_filtered ("done.\n");
+ fflush (stdout);
+ }
+
+ new_symfile_objfile (objfile, mainline, from_tty);
+
+ /* Getting new symbols may change our opinion about what is
+ frameless. */
+
+ reinit_frame_cache ();
+
+ return (objfile);
+}
+
+/* This is the symbol-file command. Read the file, analyze its symbols,
+ and add a struct symtab to a symtab list. */
+
+void
+symbol_file_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ char **argv;
+ char *name = NULL;
+ struct cleanup *cleanups;
+ int mapped = 0;
+ int readnow = 0;
+
+ dont_repeat ();
+
+ if (args == NULL)
+ {
+ if ((have_full_symbols () || have_partial_symbols ())
+ && from_tty
+ && !query ("Discard symbol table from `%s'? ",
+ symfile_objfile -> name))
+ error ("Not confirmed.");
+ free_all_objfiles ();
+ symfile_objfile = NULL;
+ if (from_tty)
+ {
+ printf ("No symbol file now.\n");
+ }
+ }
+ else
+ {
+ if ((argv = buildargv (args)) == NULL)
+ {
+ nomem (0);
+ }
+ cleanups = make_cleanup (freeargv, (char *) argv);
+ while (*argv != NULL)
+ {
+ if (STREQ (*argv, "-mapped"))
+ {
+ mapped = 1;
+ }
+ else if (STREQ (*argv, "-readnow"))
+ {
+ readnow = 1;
+ }
+ else if (**argv == '-')
+ {
+ error ("unknown option `%s'", *argv);
+ }
+ else
+ {
+ name = *argv;
+ }
+ argv++;
+ }
+
+ if (name == NULL)
+ {
+ error ("no symbol file name was specified");
+ }
+ else
+ {
+ symbol_file_add (name, from_tty, (CORE_ADDR)0, 1, mapped, readnow);
+ set_initial_language ();
+ }
+ do_cleanups (cleanups);
+ }
+}
+
+/* Set the initial language.
+
+ A better solution would be to record the language in the psymtab when reading
+ partial symbols, and then use it (if known) to set the language. This would
+ be a win for formats that encode the language in an easily discoverable place,
+ such as DWARF. For stabs, we can jump through hoops looking for specially
+ named symbols or try to intuit the language from the specific type of stabs
+ we find, but we can't do that until later when we read in full symbols.
+ FIXME. */
+
+static void
+set_initial_language ()
+{
+ struct partial_symtab *pst;
+ enum language lang = language_unknown;
+
+ pst = find_main_psymtab ();
+ if (pst != NULL)
+ {
+ if (pst -> filename != NULL)
+ {
+ lang = deduce_language_from_filename (pst -> filename);
+ }
+ if (lang == language_unknown)
+ {
+ /* Make C the default language */
+ lang = language_c;
+ }
+ set_language (lang);
+ expected_language = current_language; /* Don't warn the user */
+ }
+}
+
+/* Open file specified by NAME and hand it off to BFD for preliminary
+ analysis. Result is a newly initialized bfd *, which includes a newly
+ malloc'd` copy of NAME (tilde-expanded and made absolute).
+ In case of trouble, error() is called. */
+
+static bfd *
+symfile_bfd_open (name)
+ char *name;
+{
+ bfd *sym_bfd;
+ int desc;
+ char *absolute_name;
+
+ name = tilde_expand (name); /* Returns 1st new malloc'd copy */
+
+ /* Look down path for it, allocate 2nd new malloc'd copy. */
+ desc = openp (getenv ("PATH"), 1, name, O_RDONLY | O_BINARY, 0, &absolute_name);
+ if (desc < 0)
+ {
+ make_cleanup (free, name);
+ perror_with_name (name);
+ }
+ free (name); /* Free 1st new malloc'd copy */
+ name = absolute_name; /* Keep 2nd malloc'd copy in bfd */
+ /* It'll be freed in free_objfile(). */
+
+ sym_bfd = bfd_fdopenr (name, gnutarget, desc);
+ if (!sym_bfd)
+ {
+ close (desc);
+ make_cleanup (free, name);
+ error ("\"%s\": can't open to read symbols: %s.", name,
+ bfd_errmsg (bfd_error));
+ }
+ sym_bfd->cacheable = true;
+
+ if (!bfd_check_format (sym_bfd, bfd_object))
+ {
+ bfd_close (sym_bfd); /* This also closes desc */
+ make_cleanup (free, name);
+ error ("\"%s\": can't read symbols: %s.", name,
+ bfd_errmsg (bfd_error));
+ }
+
+ return (sym_bfd);
+}
+
+/* Link a new symtab_fns into the global symtab_fns list. Called on gdb
+ startup by the _initialize routine in each object file format reader,
+ to register information about each format the the reader is prepared
+ to handle. */
+
+void
+add_symtab_fns (sf)
+ struct sym_fns *sf;
+{
+ sf->next = symtab_fns;
+ symtab_fns = sf;
+}
+
+
+/* Initialize to read symbols from the symbol file sym_bfd. It either
+ returns or calls error(). The result is an initialized struct sym_fns
+ in the objfile structure, that contains cached information about the
+ symbol file. */
+
+static void
+find_sym_fns (objfile)
+ struct objfile *objfile;
+{
+ struct sym_fns *sf;
+
+ for (sf = symtab_fns; sf != NULL; sf = sf -> next)
+ {
+ if (strncmp (bfd_get_target (objfile -> obfd),
+ sf -> sym_name, sf -> sym_namelen) == 0)
+ {
+ objfile -> sf = sf;
+ return;
+ }
+ }
+ error ("I'm sorry, Dave, I can't do that. Symbol format `%s' unknown.",
+ bfd_get_target (objfile -> obfd));
+}
+
+/* This function runs the load command of our current target. */
+
+static void
+load_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ target_load (arg, from_tty);
+}
+
+/* This version of "load" should be usable for any target. Currently
+ it is just used for remote targets, not inftarg.c or core files,
+ on the theory that only in that case is it useful.
+
+ Avoiding xmodem and the like seems like a win (a) because we don't have
+ to worry about finding it, and (b) On VMS, fork() is very slow and so
+ we don't want to run a subprocess. On the other hand, I'm not sure how
+ performance compares. */
+void
+generic_load (filename, from_tty)
+ char *filename;
+ int from_tty;
+{
+ struct cleanup *old_cleanups;
+ asection *s;
+ bfd *loadfile_bfd = bfd_openr (filename, gnutarget);
+ if (loadfile_bfd == NULL)
+ {
+ perror_with_name (filename);
+ return;
+ }
+ old_cleanups = make_cleanup (bfd_close, loadfile_bfd);
+
+ if (!bfd_check_format (loadfile_bfd, bfd_object))
+ {
+ error ("\"%s\" is not an object file: %s", filename,
+ bfd_errmsg (bfd_error));
+ }
+
+ for (s = loadfile_bfd->sections; s; s = s->next)
+ {
+ if (s->flags & SEC_LOAD)
+ {
+ bfd_size_type size;
+
+ size = bfd_get_section_size_before_reloc (s);
+ if (size > 0)
+ {
+ char *buffer;
+ struct cleanup *old_chain;
+ bfd_vma vma;
+
+ buffer = xmalloc (size);
+ old_chain = make_cleanup (free, buffer);
+
+ vma = bfd_get_section_vma (loadfile_bfd, s);
+
+ /* Is this really necessary? I guess it gives the user something
+ to look at during a long download. */
+ printf_filtered ("Loading section %s, size 0x%lx vma 0x%lx\n",
+ bfd_get_section_name (loadfile_bfd, s),
+ (unsigned long) size, (unsigned long) vma);
+
+ bfd_get_section_contents (loadfile_bfd, s, buffer, 0, size);
+
+ target_write_memory (vma, buffer, size);
+
+ do_cleanups (old_chain);
+ }
+ }
+ }
+
+ /* We were doing this in remote-mips.c, I suspect it is right
+ for other targets too. */
+ write_pc (loadfile_bfd->start_address);
+
+ /* FIXME: are we supposed to call symbol_file_add or not? According to
+ a comment from remote-mips.c (where a call to symbol_file_add was
+ commented out), making the call confuses GDB if more than one file is
+ loaded in. remote-nindy.c had no call to symbol_file_add, but remote-vx.c
+ does. */
+
+ do_cleanups (old_cleanups);
+}
+
+/* This function allows the addition of incrementally linked object files.
+ It does not modify any state in the target, only in the debugger. */
+
+/* ARGSUSED */
+static void
+add_symbol_file_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ char *name = NULL;
+ CORE_ADDR text_addr;
+ char *arg;
+ int readnow = 0;
+ int mapped = 0;
+
+ dont_repeat ();
+
+ if (args == NULL)
+ {
+ error ("add-symbol-file takes a file name and an address");
+ }
+
+ /* Make a copy of the string that we can safely write into. */
+
+ args = strdup (args);
+ make_cleanup (free, args);
+
+ /* Pick off any -option args and the file name. */
+
+ while ((*args != '\000') && (name == NULL))
+ {
+ while (isspace (*args)) {args++;}
+ arg = args;
+ while ((*args != '\000') && !isspace (*args)) {args++;}
+ if (*args != '\000')
+ {
+ *args++ = '\000';
+ }
+ if (*arg != '-')
+ {
+ name = arg;
+ }
+ else if (STREQ (arg, "-mapped"))
+ {
+ mapped = 1;
+ }
+ else if (STREQ (arg, "-readnow"))
+ {
+ readnow = 1;
+ }
+ else
+ {
+ error ("unknown option `%s'", arg);
+ }
+ }
+
+ /* After picking off any options and the file name, args should be
+ left pointing at the remainder of the command line, which should
+ be the address expression to evaluate. */
+
+ if ((name == NULL) || (*args == '\000') )
+ {
+ error ("add-symbol-file takes a file name and an address");
+ }
+ name = tilde_expand (name);
+ make_cleanup (free, name);
+
+ text_addr = parse_and_eval_address (args);
+
+ if (!query ("add symbol table from file \"%s\" at text_addr = %s?\n",
+ name, local_hex_string ((unsigned long)text_addr)))
+ error ("Not confirmed.");
+
+ symbol_file_add (name, 0, text_addr, 0, mapped, readnow);
+}
+
+/* Re-read symbols if a symbol-file has changed. */
+void
+reread_symbols ()
+{
+ struct objfile *objfile;
+ long new_modtime;
+ int reread_one = 0;
+ struct stat new_statbuf;
+ int res;
+
+ /* With the addition of shared libraries, this should be modified,
+ the load time should be saved in the partial symbol tables, since
+ different tables may come from different source files. FIXME.
+ This routine should then walk down each partial symbol table
+ and see if the symbol table that it originates from has been changed */
+
+the_big_top:
+ for (objfile = object_files; objfile; objfile = objfile->next) {
+ if (objfile->obfd) {
+#ifdef IBM6000_TARGET
+ /* If this object is from a shared library, then you should
+ stat on the library name, not member name. */
+
+ if (objfile->obfd->my_archive)
+ res = stat (objfile->obfd->my_archive->filename, &new_statbuf);
+ else
+#endif
+ res = stat (objfile->name, &new_statbuf);
+ if (res != 0) {
+ /* FIXME, should use print_sys_errmsg but it's not filtered. */
+ printf_filtered ("`%s' has disappeared; keeping its symbols.\n",
+ objfile->name);
+ continue;
+ }
+ new_modtime = new_statbuf.st_mtime;
+ if (new_modtime != objfile->mtime) {
+ printf_filtered ("`%s' has changed; re-reading symbols.\n",
+ objfile->name);
+ /* FIXME, this should use a different command...that would only
+ affect this objfile's symbols, and would reset objfile->mtime.
+ (objfile->mtime = new_modtime;)
+ HOWEVER, that command isn't written yet -- so call symbol_file_
+ command, and restart the scan from the top, because it munges
+ the object_files list. */
+ symbol_file_command (objfile->name, 0);
+ reread_one = 1;
+ goto the_big_top; /* Start over. */
+ }
+ }
+ }
+
+ if (reread_one)
+ breakpoint_re_set ();
+}
+
+
+enum language
+deduce_language_from_filename (filename)
+ char *filename;
+{
+ char *c;
+
+ if (0 == filename)
+ ; /* Get default */
+ else if (0 == (c = strrchr (filename, '.')))
+ ; /* Get default. */
+ else if (STREQ(c,".mod"))
+ return language_m2;
+ else if (STREQ(c,".c"))
+ return language_c;
+ else if (STREQ (c,".cc") || STREQ (c,".C") || STREQ (c, ".cxx"))
+ return language_cplus;
+ else if (STREQ (c,".ch") || STREQ (c,".c186") || STREQ (c,".c286"))
+ return language_chill;
+
+ return language_unknown; /* default */
+}
+
+/* allocate_symtab:
+
+ Allocate and partly initialize a new symbol table. Return a pointer
+ to it. error() if no space.
+
+ Caller must set these fields:
+ LINETABLE(symtab)
+ symtab->blockvector
+ symtab->dirname
+ symtab->free_code
+ symtab->free_ptr
+ initialize any EXTRA_SYMTAB_INFO
+ possibly free_named_symtabs (symtab->filename);
+ */
+
+struct symtab *
+allocate_symtab (filename, objfile)
+ char *filename;
+ struct objfile *objfile;
+{
+ register struct symtab *symtab;
+
+ symtab = (struct symtab *)
+ obstack_alloc (&objfile -> symbol_obstack, sizeof (struct symtab));
+ memset (symtab, 0, sizeof (*symtab));
+ symtab -> filename = obsavestring (filename, strlen (filename),
+ &objfile -> symbol_obstack);
+ symtab -> fullname = NULL;
+ symtab -> language = deduce_language_from_filename (filename);
+
+ /* Hook it to the objfile it comes from */
+
+ symtab -> objfile = objfile;
+ symtab -> next = objfile -> symtabs;
+ objfile -> symtabs = symtab;
+
+#ifdef INIT_EXTRA_SYMTAB_INFO
+ INIT_EXTRA_SYMTAB_INFO (symtab);
+#endif
+
+ return (symtab);
+}
+
+struct partial_symtab *
+allocate_psymtab (filename, objfile)
+ char *filename;
+ struct objfile *objfile;
+{
+ struct partial_symtab *psymtab;
+
+ if (objfile -> free_psymtabs)
+ {
+ psymtab = objfile -> free_psymtabs;
+ objfile -> free_psymtabs = psymtab -> next;
+ }
+ else
+ psymtab = (struct partial_symtab *)
+ obstack_alloc (&objfile -> psymbol_obstack,
+ sizeof (struct partial_symtab));
+
+ memset (psymtab, 0, sizeof (struct partial_symtab));
+ psymtab -> filename = obsavestring (filename, strlen (filename),
+ &objfile -> psymbol_obstack);
+ psymtab -> symtab = NULL;
+
+ /* Hook it to the objfile it comes from */
+
+ psymtab -> objfile = objfile;
+ psymtab -> next = objfile -> psymtabs;
+ objfile -> psymtabs = psymtab;
+
+ return (psymtab);
+}
+
+
+/* Reset all data structures in gdb which may contain references to symbol
+ table date. */
+
+void
+clear_symtab_users ()
+{
+ /* Someday, we should do better than this, by only blowing away
+ the things that really need to be blown. */
+ clear_value_history ();
+ clear_displays ();
+ clear_internalvars ();
+ breakpoint_re_set ();
+ set_default_breakpoint (0, 0, 0, 0);
+ current_source_symtab = 0;
+ current_source_line = 0;
+}
+
+/* clear_symtab_users_once:
+
+ This function is run after symbol reading, or from a cleanup.
+ If an old symbol table was obsoleted, the old symbol table
+ has been blown away, but the other GDB data structures that may
+ reference it have not yet been cleared or re-directed. (The old
+ symtab was zapped, and the cleanup queued, in free_named_symtab()
+ below.)
+
+ This function can be queued N times as a cleanup, or called
+ directly; it will do all the work the first time, and then will be a
+ no-op until the next time it is queued. This works by bumping a
+ counter at queueing time. Much later when the cleanup is run, or at
+ the end of symbol processing (in case the cleanup is discarded), if
+ the queued count is greater than the "done-count", we do the work
+ and set the done-count to the queued count. If the queued count is
+ less than or equal to the done-count, we just ignore the call. This
+ is needed because reading a single .o file will often replace many
+ symtabs (one per .h file, for example), and we don't want to reset
+ the breakpoints N times in the user's face.
+
+ The reason we both queue a cleanup, and call it directly after symbol
+ reading, is because the cleanup protects us in case of errors, but is
+ discarded if symbol reading is successful. */
+
+#if 0
+/* FIXME: As free_named_symtabs is currently a big noop this function
+ is no longer needed. */
+static void
+clear_symtab_users_once PARAMS ((void));
+
+static int clear_symtab_users_queued;
+static int clear_symtab_users_done;
+
+static void
+clear_symtab_users_once ()
+{
+ /* Enforce once-per-`do_cleanups'-semantics */
+ if (clear_symtab_users_queued <= clear_symtab_users_done)
+ return;
+ clear_symtab_users_done = clear_symtab_users_queued;
+
+ clear_symtab_users ();
+}
+#endif
+
+/* Delete the specified psymtab, and any others that reference it. */
+
+static void
+cashier_psymtab (pst)
+ struct partial_symtab *pst;
+{
+ struct partial_symtab *ps, *pprev = NULL;
+ int i;
+
+ /* Find its previous psymtab in the chain */
+ for (ps = pst->objfile->psymtabs; ps; ps = ps->next) {
+ if (ps == pst)
+ break;
+ pprev = ps;
+ }
+
+ if (ps) {
+ /* Unhook it from the chain. */
+ if (ps == pst->objfile->psymtabs)
+ pst->objfile->psymtabs = ps->next;
+ else
+ pprev->next = ps->next;
+
+ /* FIXME, we can't conveniently deallocate the entries in the
+ partial_symbol lists (global_psymbols/static_psymbols) that
+ this psymtab points to. These just take up space until all
+ the psymtabs are reclaimed. Ditto the dependencies list and
+ filename, which are all in the psymbol_obstack. */
+
+ /* We need to cashier any psymtab that has this one as a dependency... */
+again:
+ for (ps = pst->objfile->psymtabs; ps; ps = ps->next) {
+ for (i = 0; i < ps->number_of_dependencies; i++) {
+ if (ps->dependencies[i] == pst) {
+ cashier_psymtab (ps);
+ goto again; /* Must restart, chain has been munged. */
+ }
+ }
+ }
+ }
+}
+
+/* If a symtab or psymtab for filename NAME is found, free it along
+ with any dependent breakpoints, displays, etc.
+ Used when loading new versions of object modules with the "add-file"
+ command. This is only called on the top-level symtab or psymtab's name;
+ it is not called for subsidiary files such as .h files.
+
+ Return value is 1 if we blew away the environment, 0 if not.
+ FIXME. The return valu appears to never be used.
+
+ FIXME. I think this is not the best way to do this. We should
+ work on being gentler to the environment while still cleaning up
+ all stray pointers into the freed symtab. */
+
+int
+free_named_symtabs (name)
+ char *name;
+{
+#if 0
+ /* FIXME: With the new method of each objfile having it's own
+ psymtab list, this function needs serious rethinking. In particular,
+ why was it ever necessary to toss psymtabs with specific compilation
+ unit filenames, as opposed to all psymtabs from a particular symbol
+ file? -- fnf
+ Well, the answer is that some systems permit reloading of particular
+ compilation units. We want to blow away any old info about these
+ compilation units, regardless of which objfiles they arrived in. --gnu. */
+
+ register struct symtab *s;
+ register struct symtab *prev;
+ register struct partial_symtab *ps;
+ struct blockvector *bv;
+ int blewit = 0;
+
+ /* We only wack things if the symbol-reload switch is set. */
+ if (!symbol_reloading)
+ return 0;
+
+ /* Some symbol formats have trouble providing file names... */
+ if (name == 0 || *name == '\0')
+ return 0;
+
+ /* Look for a psymtab with the specified name. */
+
+again2:
+ for (ps = partial_symtab_list; ps; ps = ps->next) {
+ if (STREQ (name, ps->filename)) {
+ cashier_psymtab (ps); /* Blow it away...and its little dog, too. */
+ goto again2; /* Must restart, chain has been munged */
+ }
+ }
+
+ /* Look for a symtab with the specified name. */
+
+ for (s = symtab_list; s; s = s->next)
+ {
+ if (STREQ (name, s->filename))
+ break;
+ prev = s;
+ }
+
+ if (s)
+ {
+ if (s == symtab_list)
+ symtab_list = s->next;
+ else
+ prev->next = s->next;
+
+ /* For now, queue a delete for all breakpoints, displays, etc., whether
+ or not they depend on the symtab being freed. This should be
+ changed so that only those data structures affected are deleted. */
+
+ /* But don't delete anything if the symtab is empty.
+ This test is necessary due to a bug in "dbxread.c" that
+ causes empty symtabs to be created for N_SO symbols that
+ contain the pathname of the object file. (This problem
+ has been fixed in GDB 3.9x). */
+
+ bv = BLOCKVECTOR (s);
+ if (BLOCKVECTOR_NBLOCKS (bv) > 2
+ || BLOCK_NSYMS (BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK))
+ || BLOCK_NSYMS (BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK)))
+ {
+ complain (&oldsyms_complaint, name);
+
+ clear_symtab_users_queued++;
+ make_cleanup (clear_symtab_users_once, 0);
+ blewit = 1;
+ } else {
+ complain (&empty_symtab_complaint, name);
+ }
+
+ free_symtab (s);
+ }
+ else
+ {
+ /* It is still possible that some breakpoints will be affected
+ even though no symtab was found, since the file might have
+ been compiled without debugging, and hence not be associated
+ with a symtab. In order to handle this correctly, we would need
+ to keep a list of text address ranges for undebuggable files.
+ For now, we do nothing, since this is a fairly obscure case. */
+ ;
+ }
+
+ /* FIXME, what about the minimal symbol table? */
+ return blewit;
+#else
+ return (0);
+#endif
+}
+
+/* Allocate and partially fill a partial symtab. It will be
+ completely filled at the end of the symbol list.
+
+ SYMFILE_NAME is the name of the symbol-file we are reading from, and ADDR
+ is the address relative to which its symbols are (incremental) or 0
+ (normal). */
+
+
+struct partial_symtab *
+start_psymtab_common (objfile, section_offsets,
+ filename, textlow, global_syms, static_syms)
+ struct objfile *objfile;
+ struct section_offsets *section_offsets;
+ char *filename;
+ CORE_ADDR textlow;
+ struct partial_symbol *global_syms;
+ struct partial_symbol *static_syms;
+{
+ struct partial_symtab *psymtab;
+
+ psymtab = allocate_psymtab (filename, objfile);
+ psymtab -> section_offsets = section_offsets;
+ psymtab -> textlow = textlow;
+ psymtab -> texthigh = psymtab -> textlow; /* default */
+ psymtab -> globals_offset = global_syms - objfile -> global_psymbols.list;
+ psymtab -> statics_offset = static_syms - objfile -> static_psymbols.list;
+ return (psymtab);
+}
+
+/* Debugging versions of functions that are usually inline macros
+ (see symfile.h). */
+
+#if !INLINE_ADD_PSYMBOL
+
+/* Add a symbol with a long value to a psymtab.
+ Since one arg is a struct, we pass in a ptr and deref it (sigh). */
+
+void
+add_psymbol_to_list (name, namelength, namespace, class, list, val, language,
+ objfile)
+ char *name;
+ int namelength;
+ enum namespace namespace;
+ enum address_class class;
+ struct psymbol_allocation_list *list;
+ long val;
+ enum language language;
+ struct objfile *objfile;
+{
+ register struct partial_symbol *psym;
+ register char *demangled_name;
+
+ if (list->next >= list->list + list->size)
+ {
+ extend_psymbol_list (list,objfile);
+ }
+ psym = list->next++;
+
+ SYMBOL_NAME (psym) =
+ (char *) obstack_alloc (&objfile->psymbol_obstack, namelength + 1);
+ memcpy (SYMBOL_NAME (psym), name, namelength);
+ SYMBOL_NAME (psym)[namelength] = '\0';
+ SYMBOL_VALUE (psym) = val;
+ SYMBOL_LANGUAGE (psym) = language;
+ PSYMBOL_NAMESPACE (psym) = namespace;
+ PSYMBOL_CLASS (psym) = class;
+ SYMBOL_INIT_DEMANGLED_NAME (psym, &objfile->psymbol_obstack);
+}
+
+/* Add a symbol with a CORE_ADDR value to a psymtab. */
+
+void
+add_psymbol_addr_to_list (name, namelength, namespace, class, list, val,
+ language, objfile)
+ char *name;
+ int namelength;
+ enum namespace namespace;
+ enum address_class class;
+ struct psymbol_allocation_list *list;
+ CORE_ADDR val;
+ enum language language;
+ struct objfile *objfile;
+{
+ register struct partial_symbol *psym;
+ register char *demangled_name;
+
+ if (list->next >= list->list + list->size)
+ {
+ extend_psymbol_list (list,objfile);
+ }
+ psym = list->next++;
+
+ SYMBOL_NAME (psym) =
+ (char *) obstack_alloc (&objfile->psymbol_obstack, namelength + 1);
+ memcpy (SYMBOL_NAME (psym), name, namelength);
+ SYMBOL_NAME (psym)[namelength] = '\0';
+ SYMBOL_VALUE_ADDRESS (psym) = val;
+ SYMBOL_LANGUAGE (psym) = language;
+ PSYMBOL_NAMESPACE (psym) = namespace;
+ PSYMBOL_CLASS (psym) = class;
+ SYMBOL_INIT_DEMANGLED_NAME (psym, &objfile->psymbol_obstack);
+}
+
+#endif /* !INLINE_ADD_PSYMBOL */
+
+
+void
+_initialize_symfile ()
+{
+ struct cmd_list_element *c;
+
+ c = add_cmd ("symbol-file", class_files, symbol_file_command,
+ "Load symbol table from executable file FILE.\n\
+The `file' command can also load symbol tables, as well as setting the file\n\
+to execute.", &cmdlist);
+ c->completer = filename_completer;
+
+ c = add_cmd ("add-symbol-file", class_files, add_symbol_file_command,
+ "Load the symbols from FILE, assuming FILE has been dynamically loaded.\n\
+The second argument provides the starting address of the file's text.",
+ &cmdlist);
+ c->completer = filename_completer;
+
+ c = add_cmd ("load", class_files, load_command,
+ "Dynamically load FILE into the running program, and record its symbols\n\
+for access from GDB.", &cmdlist);
+ c->completer = filename_completer;
+
+ add_show_from_set
+ (add_set_cmd ("symbol-reloading", class_support, var_boolean,
+ (char *)&symbol_reloading,
+ "Set dynamic symbol table reloading multiple times in one run.",
+ &setlist),
+ &showlist);
+
+}
diff --git a/gnu/usr.bin/gdb/gdb/symfile.h b/gnu/usr.bin/gdb/gdb/symfile.h
new file mode 100644
index 0000000..70bc73d
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/symfile.h
@@ -0,0 +1,228 @@
+/* Definitions for reading symbol files into GDB.
+ Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (SYMFILE_H)
+#define SYMFILE_H
+
+/* This file requires that you first include "bfd.h". */
+
+struct psymbol_allocation_list {
+ struct partial_symbol *list;
+ struct partial_symbol *next;
+ int size;
+};
+
+/* Structure to keep track of symbol reading functions for various
+ object file types. */
+
+struct sym_fns {
+
+ /* is the name, or name prefix, of the BFD "target type" that this
+ set of functions handles. E.g. "a.out" or "sunOs" or "coff" or "elf". */
+
+ char *sym_name;
+
+ /* counts how many bytes of sym_name should be checked against the
+ BFD target type of the file being read. If an exact match is
+ desired, specify the number of characters in sym_name plus 1 for
+ the '\0'. If a prefix match is desired, specify the number of
+ characters in sym_name. */
+
+ int sym_namelen;
+
+ /* Initializes anything that is global to the entire symbol table. It is
+ called during symbol_file_add, when we begin debugging an entirely new
+ program. */
+
+ void (*sym_new_init) PARAMS ((struct objfile *));
+
+ /* Reads any initial information from a symbol file, and initializes the
+ struct sym_fns SF in preparation for sym_read(). It is called every
+ time we read a symbol file for any reason. */
+
+ void (*sym_init) PARAMS ((struct objfile *));
+
+ /* sym_read (objfile, addr, mainline)
+ Reads a symbol file into a psymtab (or possibly a symtab).
+ OBJFILE is the objfile struct for the file we are reading.
+ SECTION_OFFSETS
+ are the offset between the file's specified section addresses and
+ their true addresses in memory.
+ MAINLINE is 1 if this is the
+ main symbol table being read, and 0 if a secondary
+ symbol file (e.g. shared library or dynamically loaded file)
+ is being read. */
+
+ void (*sym_read) PARAMS ((struct objfile *, struct section_offsets *, int));
+
+ /* Called when we are finished with an objfile. Should do all cleanup
+ that is specific to the object file format for the particular objfile. */
+
+ void (*sym_finish) PARAMS ((struct objfile *));
+
+ /* This function produces a file-dependent section_offsets structure,
+ allocated in the objfile's storage, and based on the parameter.
+ The parameter is currently a CORE_ADDR (FIXME!) for backward compatibility
+ with the higher levels of GDB. It should probably be changed to
+ a string, where NULL means the default, and others are parsed in a file
+ dependent way. The result of this function is handed in to sym_read. */
+
+ struct section_offsets *(*sym_offsets) PARAMS ((struct objfile *, CORE_ADDR));
+
+ /* Finds the next struct sym_fns. They are allocated and initialized
+ in whatever module implements the functions pointed to; an
+ initializer calls add_symtab_fns to add them to the global chain. */
+
+ struct sym_fns *next;
+
+};
+
+extern void
+extend_psymbol_list PARAMS ((struct psymbol_allocation_list *,
+ struct objfile *));
+
+/* Add any kind of symbol to a psymbol_allocation_list. */
+
+#ifndef INLINE_ADD_PSYMBOL
+#define INLINE_ADD_PSYMBOL 1
+#endif
+
+#if !INLINE_ADD_PSYMBOL
+
+/* Since one arg is a struct, we have to pass in a ptr and deref it (sigh) */
+
+#define ADD_PSYMBOL_TO_LIST(name, namelength, namespace, class, list, value, language, objfile) \
+ add_psymbol_to_list (name, namelength, namespace, class, &list, value, language, objfile)
+
+#define ADD_PSYMBOL_ADDR_TO_LIST(name, namelength, namespace, class, list, value, language, objfile) \
+ add_psymbol_addr_to_list (name, namelength, namespace, class, &list, value, language, objfile)
+
+#else /* !INLINE_ADD_PSYMBOL */
+
+#include "demangle.h"
+
+#define ADD_PSYMBOL_VT_TO_LIST(NAME,NAMELENGTH,NAMESPACE,CLASS,LIST,VALUE,VT,LANGUAGE, OBJFILE) \
+ do { \
+ register struct partial_symbol *psym; \
+ if ((LIST).next >= (LIST).list + (LIST).size) \
+ extend_psymbol_list (&(LIST),(OBJFILE)); \
+ psym = (LIST).next++; \
+ SYMBOL_NAME (psym) = \
+ (char *) obstack_alloc (&objfile->psymbol_obstack, \
+ (NAMELENGTH) + 1); \
+ memcpy (SYMBOL_NAME (psym), (NAME), (NAMELENGTH)); \
+ SYMBOL_NAME (psym)[(NAMELENGTH)] = '\0'; \
+ SYMBOL_NAMESPACE (psym) = (NAMESPACE); \
+ PSYMBOL_CLASS (psym) = (CLASS); \
+ VT (psym) = (VALUE); \
+ SYMBOL_LANGUAGE (psym) = (LANGUAGE); \
+ SYMBOL_INIT_DEMANGLED_NAME (psym, &objfile->psymbol_obstack); \
+ } while (0);
+
+/* Add a symbol with an integer value to a psymtab. */
+
+#define ADD_PSYMBOL_TO_LIST(name, namelength, namespace, class, list, value, language, objfile) \
+ ADD_PSYMBOL_VT_TO_LIST (name, namelength, namespace, class, list, value, SYMBOL_VALUE, language, objfile)
+
+/* Add a symbol with a CORE_ADDR value to a psymtab. */
+
+#define ADD_PSYMBOL_ADDR_TO_LIST(name, namelength, namespace, class, list, value, language, objfile)\
+ ADD_PSYMBOL_VT_TO_LIST (name, namelength, namespace, class, list, value, SYMBOL_VALUE_ADDRESS, language, objfile)
+
+#endif /* INLINE_ADD_PSYMBOL */
+
+ /* Functions */
+
+extern void
+sort_pst_symbols PARAMS ((struct partial_symtab *));
+
+extern struct symtab *
+allocate_symtab PARAMS ((char *, struct objfile *));
+
+extern int
+free_named_symtabs PARAMS ((char *));
+
+extern void
+fill_in_vptr_fieldno PARAMS ((struct type *));
+
+extern void
+add_symtab_fns PARAMS ((struct sym_fns *));
+
+extern void
+init_entry_point_info PARAMS ((struct objfile *));
+
+extern void
+syms_from_objfile PARAMS ((struct objfile *, CORE_ADDR, int, int));
+
+extern void
+new_symfile_objfile PARAMS ((struct objfile *, int, int));
+
+extern struct partial_symtab *
+start_psymtab_common PARAMS ((struct objfile *, struct section_offsets *,
+ char *, CORE_ADDR,
+ struct partial_symbol *,
+ struct partial_symbol *));
+
+/* Sorting your symbols for fast lookup or alphabetical printing. */
+
+extern void
+sort_block_syms PARAMS ((struct block *));
+
+extern void
+sort_symtab_syms PARAMS ((struct symtab *));
+
+extern void
+sort_all_symtab_syms PARAMS ((void));
+
+/* Make a copy of the string at PTR with SIZE characters in the symbol obstack
+ (and add a null character at the end in the copy).
+ Returns the address of the copy. */
+
+extern char *
+obsavestring PARAMS ((char *, int, struct obstack *));
+
+/* Concatenate strings S1, S2 and S3; return the new string.
+ Space is found in the symbol_obstack. */
+
+extern char *
+obconcat PARAMS ((struct obstack *obstackp, const char *, const char *,
+ const char *));
+
+ /* Variables */
+
+/* From symfile.c */
+
+extern struct partial_symtab *
+allocate_psymtab PARAMS ((char *, struct objfile *));
+
+/* Remote targets may wish to use this as their load function. */
+extern void generic_load PARAMS ((char *name, int from_tty));
+
+/* From dwarfread.c */
+
+extern void
+dwarf_build_psymtabs PARAMS ((struct objfile *, struct section_offsets *, int,
+ file_ptr, unsigned int, file_ptr, unsigned int));
+
+/* From demangle.c */
+
+extern void
+set_demangling_style PARAMS ((char *));
+
+#endif /* !defined(SYMFILE_H) */
diff --git a/gnu/usr.bin/gdb/gdb/symmisc.c b/gnu/usr.bin/gdb/gdb/symmisc.c
new file mode 100644
index 0000000..411a53b
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/symmisc.c
@@ -0,0 +1,857 @@
+/* Do various things to symbol tables (other than lookup), for GDB.
+ Copyright 1986, 1987, 1989, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "bfd.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "breakpoint.h"
+#include "command.h"
+#include "obstack.h"
+#include "language.h"
+
+#include <string.h>
+
+#ifndef DEV_TTY
+#define DEV_TTY "/dev/tty"
+#endif
+
+/* Unfortunately for debugging, stderr is usually a macro. This is painful
+ when calling functions that take FILE *'s from the debugger.
+ So we make a variable which has the same value and which is accessible when
+ debugging GDB with itself. Because stdin et al need not be constants,
+ we initialize them in the _initialize_symmisc function at the bottom
+ of the file. */
+FILE *std_in;
+FILE *std_out;
+FILE *std_err;
+
+/* Prototypes for local functions */
+
+static void
+dump_symtab PARAMS ((struct objfile *, struct symtab *, FILE *));
+
+static void
+dump_psymtab PARAMS ((struct objfile *, struct partial_symtab *, FILE *));
+
+static void
+dump_msymbols PARAMS ((struct objfile *, FILE *));
+
+static void
+dump_objfile PARAMS ((struct objfile *));
+
+static int
+block_depth PARAMS ((struct block *));
+
+static void
+print_partial_symbol PARAMS ((struct partial_symbol *, int, char *, FILE *));
+
+struct print_symbol_args {
+ struct symbol *symbol;
+ int depth;
+ FILE *outfile;
+};
+
+static int print_symbol PARAMS ((char *));
+
+static void
+free_symtab_block PARAMS ((struct objfile *, struct block *));
+
+
+/* Free a struct block <- B and all the symbols defined in that block. */
+
+static void
+free_symtab_block (objfile, b)
+ struct objfile *objfile;
+ struct block *b;
+{
+ register int i, n;
+ n = BLOCK_NSYMS (b);
+ for (i = 0; i < n; i++)
+ {
+ mfree (objfile -> md, SYMBOL_NAME (BLOCK_SYM (b, i)));
+ mfree (objfile -> md, (PTR) BLOCK_SYM (b, i));
+ }
+ mfree (objfile -> md, (PTR) b);
+}
+
+/* Free all the storage associated with the struct symtab <- S.
+ Note that some symtabs have contents malloc'ed structure by structure,
+ while some have contents that all live inside one big block of memory,
+ and some share the contents of another symbol table and so you should
+ not free the contents on their behalf (except sometimes the linetable,
+ which maybe per symtab even when the rest is not).
+ It is s->free_code that says which alternative to use. */
+
+void
+free_symtab (s)
+ register struct symtab *s;
+{
+ register int i, n;
+ register struct blockvector *bv;
+
+ switch (s->free_code)
+ {
+ case free_nothing:
+ /* All the contents are part of a big block of memory (an obstack),
+ and some other symtab is in charge of freeing that block.
+ Therefore, do nothing. */
+ break;
+
+ case free_contents:
+ /* Here all the contents were malloc'ed structure by structure
+ and must be freed that way. */
+ /* First free the blocks (and their symbols. */
+ bv = BLOCKVECTOR (s);
+ n = BLOCKVECTOR_NBLOCKS (bv);
+ for (i = 0; i < n; i++)
+ free_symtab_block (s -> objfile, BLOCKVECTOR_BLOCK (bv, i));
+ /* Free the blockvector itself. */
+ mfree (s -> objfile -> md, (PTR) bv);
+ /* Also free the linetable. */
+
+ case free_linetable:
+ /* Everything will be freed either by our `free_ptr'
+ or by some other symtab, except for our linetable.
+ Free that now. */
+ if (LINETABLE (s))
+ mfree (s -> objfile -> md, (PTR) LINETABLE (s));
+ break;
+ }
+
+ /* If there is a single block of memory to free, free it. */
+ if (s -> free_ptr != NULL)
+ mfree (s -> objfile -> md, s -> free_ptr);
+
+ /* Free source-related stuff */
+ if (s -> line_charpos != NULL)
+ mfree (s -> objfile -> md, (PTR) s -> line_charpos);
+ if (s -> fullname != NULL)
+ mfree (s -> objfile -> md, s -> fullname);
+ mfree (s -> objfile -> md, (PTR) s);
+}
+
+#if MAINTENANCE_CMDS
+
+static void
+dump_objfile (objfile)
+ struct objfile *objfile;
+{
+ struct symtab *symtab;
+ struct partial_symtab *psymtab;
+
+ printf_filtered ("\nObject file %s: ", objfile -> name);
+ printf_filtered ("Objfile at %lx, bfd at %lx, %d minsyms\n\n",
+ (unsigned long) objfile,
+ (unsigned long) objfile -> obfd,
+ objfile->minimal_symbol_count);
+
+ if (objfile -> psymtabs)
+ {
+ printf_filtered ("Psymtabs:\n");
+ for (psymtab = objfile -> psymtabs;
+ psymtab != NULL;
+ psymtab = psymtab -> next)
+ {
+ printf_filtered ("%s at %lx, ",
+ psymtab -> filename, (unsigned long) psymtab);
+ if (psymtab -> objfile != objfile)
+ {
+ printf_filtered ("NOT ON CHAIN! ");
+ }
+ wrap_here (" ");
+ }
+ printf_filtered ("\n\n");
+ }
+
+ if (objfile -> symtabs)
+ {
+ printf_filtered ("Symtabs:\n");
+ for (symtab = objfile -> symtabs;
+ symtab != NULL;
+ symtab = symtab->next)
+ {
+ printf_filtered ("%s at %lx, ",
+ symtab -> filename, (unsigned long) symtab);
+ if (symtab -> objfile != objfile)
+ {
+ printf_filtered ("NOT ON CHAIN! ");
+ }
+ wrap_here (" ");
+ }
+ printf_filtered ("\n\n");
+ }
+}
+
+/* Print minimal symbols from this objfile. */
+
+static void
+dump_msymbols (objfile, outfile)
+ struct objfile *objfile;
+ FILE *outfile;
+{
+ struct minimal_symbol *msymbol;
+ int index;
+ char ms_type;
+
+ fprintf_filtered (outfile, "\nObject file %s:\n\n", objfile -> name);
+ if (objfile -> minimal_symbol_count == 0)
+ {
+ fprintf_filtered (outfile, "No minimal symbols found.\n");
+ return;
+ }
+ for (index = 0, msymbol = objfile -> msymbols;
+ SYMBOL_NAME (msymbol) != NULL; msymbol++, index++)
+ {
+ switch (msymbol -> type)
+ {
+ case mst_unknown:
+ ms_type = 'u';
+ break;
+ case mst_text:
+ ms_type = 'T';
+ break;
+ case mst_data:
+ ms_type = 'D';
+ break;
+ case mst_bss:
+ ms_type = 'B';
+ break;
+ case mst_abs:
+ ms_type = 'A';
+ break;
+ case mst_file_text:
+ ms_type = 't';
+ break;
+ case mst_file_data:
+ ms_type = 'd';
+ break;
+ case mst_file_bss:
+ ms_type = 'b';
+ break;
+ default:
+ ms_type = '?';
+ break;
+ }
+ fprintf_filtered (outfile, "[%2d] %c %#10lx %s", index, ms_type,
+ SYMBOL_VALUE_ADDRESS (msymbol), SYMBOL_NAME (msymbol));
+ if (SYMBOL_DEMANGLED_NAME (msymbol) != NULL)
+ {
+ fprintf_filtered (outfile, " %s", SYMBOL_DEMANGLED_NAME (msymbol));
+ }
+ fputs_filtered ("\n", outfile);
+ }
+ if (objfile -> minimal_symbol_count != index)
+ {
+ warning ("internal error: minimal symbol count %d != %d",
+ objfile -> minimal_symbol_count, index);
+ }
+ fprintf_filtered (outfile, "\n");
+}
+
+static void
+dump_psymtab (objfile, psymtab, outfile)
+ struct objfile *objfile;
+ struct partial_symtab *psymtab;
+ FILE *outfile;
+{
+ int i;
+
+ fprintf_filtered (outfile, "\nPartial symtab for source file %s ",
+ psymtab -> filename);
+ fprintf_filtered (outfile, "(object 0x%lx)\n\n", (unsigned long) psymtab);
+ fprintf (outfile, " Read from object file %s (0x%lx)\n",
+ objfile -> name, (unsigned long) objfile);
+
+ if (psymtab -> readin)
+ {
+ fprintf_filtered (outfile,
+ " Full symtab was read (at 0x%lx by function at 0x%lx)\n",
+ (unsigned long) psymtab -> symtab,
+ (unsigned long) psymtab -> read_symtab);
+ }
+
+ /* FIXME, we need to be able to print the relocation stuff. */
+ /* This prints some garbage for anything but stabs right now. FIXME. */
+ if (psymtab->section_offsets)
+ fprintf_filtered (outfile,
+ " Relocate symbols by 0x%lx, 0x%lx, 0x%lx, 0x%lx.\n",
+ (unsigned long) ANOFFSET (psymtab->section_offsets, 0),
+ (unsigned long) ANOFFSET (psymtab->section_offsets, 1),
+ (unsigned long) ANOFFSET (psymtab->section_offsets, 2),
+ (unsigned long) ANOFFSET (psymtab->section_offsets, 3));
+
+ fprintf_filtered (outfile, " Symbols cover text addresses 0x%lx-0x%lx\n",
+ (unsigned long) psymtab -> textlow,
+ (unsigned long) psymtab -> texthigh);
+ fprintf_filtered (outfile, " Depends on %d other partial symtabs.\n",
+ psymtab -> number_of_dependencies);
+ for (i = 0; i < psymtab -> number_of_dependencies; i++)
+ {
+ fprintf_filtered (outfile, " %d 0x%lx %s\n", i,
+ (unsigned long) psymtab -> dependencies[i],
+ psymtab -> dependencies[i] -> filename);
+ }
+ if (psymtab -> n_global_syms > 0)
+ {
+ print_partial_symbol (objfile -> global_psymbols.list
+ + psymtab -> globals_offset,
+ psymtab -> n_global_syms, "Global", outfile);
+ }
+ if (psymtab -> n_static_syms > 0)
+ {
+ print_partial_symbol (objfile -> static_psymbols.list
+ + psymtab -> statics_offset,
+ psymtab -> n_static_syms, "Static", outfile);
+ }
+ fprintf_filtered (outfile, "\n");
+}
+
+static void
+dump_symtab (objfile, symtab, outfile)
+ struct objfile *objfile;
+ struct symtab *symtab;
+ FILE *outfile;
+{
+ register int i, j;
+ int len, blen;
+ register struct linetable *l;
+ struct blockvector *bv;
+ register struct block *b;
+ int depth;
+
+ fprintf (outfile, "\nSymtab for file %s\n", symtab->filename);
+ fprintf (outfile, "Read from object file %s (%lx)\n", objfile->name,
+ (unsigned long) objfile);
+ fprintf (outfile, "Language: %s\n", language_str (symtab -> language));
+
+ /* First print the line table. */
+ l = LINETABLE (symtab);
+ if (l) {
+ fprintf (outfile, "\nLine table:\n\n");
+ len = l->nitems;
+ for (i = 0; i < len; i++)
+ fprintf (outfile, " line %ld at %lx\n", l->item[i].line,
+ (unsigned long) l->item[i].pc);
+ }
+ /* Now print the block info. */
+ fprintf (outfile, "\nBlockvector:\n\n");
+ bv = BLOCKVECTOR (symtab);
+ len = BLOCKVECTOR_NBLOCKS (bv);
+ for (i = 0; i < len; i++)
+ {
+ b = BLOCKVECTOR_BLOCK (bv, i);
+ depth = block_depth (b) * 2;
+ print_spaces (depth, outfile);
+ fprintf (outfile, "block #%03d (object 0x%lx) ", i, (unsigned long) b);
+ fprintf (outfile, "[0x%lx..0x%lx]",
+ (unsigned long) BLOCK_START (b),
+ (unsigned long) BLOCK_END (b));
+ if (BLOCK_SUPERBLOCK (b))
+ fprintf (outfile, " (under 0x%lx)",
+ (unsigned long) BLOCK_SUPERBLOCK (b));
+ if (BLOCK_FUNCTION (b))
+ {
+ fprintf (outfile, " %s", SYMBOL_NAME (BLOCK_FUNCTION (b)));
+ if (SYMBOL_DEMANGLED_NAME (BLOCK_FUNCTION (b)) != NULL)
+ {
+ fprintf (outfile, " %s",
+ SYMBOL_DEMANGLED_NAME (BLOCK_FUNCTION (b)));
+ }
+ }
+ if (BLOCK_GCC_COMPILED(b))
+ fprintf (outfile, " gcc%d compiled", BLOCK_GCC_COMPILED(b));
+ fputc ('\n', outfile);
+ blen = BLOCK_NSYMS (b);
+ for (j = 0; j < blen; j++)
+ {
+ struct print_symbol_args s;
+ s.symbol = BLOCK_SYM (b, j);
+ s.depth = depth + 1;
+ s.outfile = outfile;
+ catch_errors (print_symbol, &s, "Error printing symbol:\n",
+ RETURN_MASK_ERROR);
+ }
+ }
+ fprintf (outfile, "\n");
+}
+
+void
+maintenance_print_symbols (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ char **argv;
+ FILE *outfile;
+ struct cleanup *cleanups;
+ char *symname = NULL;
+ char *filename = DEV_TTY;
+ struct objfile *objfile;
+ struct symtab *s;
+
+ dont_repeat ();
+
+ if (args == NULL)
+ {
+ error ("print-symbols takes an output file name and optional symbol file name");
+ }
+ else if ((argv = buildargv (args)) == NULL)
+ {
+ nomem (0);
+ }
+ cleanups = make_cleanup (freeargv, (char *) argv);
+
+ if (argv[0] != NULL)
+ {
+ filename = argv[0];
+ /* If a second arg is supplied, it is a source file name to match on */
+ if (argv[1] != NULL)
+ {
+ symname = argv[1];
+ }
+ }
+
+ filename = tilde_expand (filename);
+ make_cleanup (free, filename);
+
+ outfile = fopen (filename, FOPEN_WT);
+ if (outfile == 0)
+ perror_with_name (filename);
+ make_cleanup (fclose, (char *) outfile);
+
+ immediate_quit++;
+ ALL_SYMTABS (objfile, s)
+ if (symname == NULL || (STREQ (symname, s -> filename)))
+ dump_symtab (objfile, s, outfile);
+ immediate_quit--;
+ do_cleanups (cleanups);
+}
+
+/* Print symbol ARGS->SYMBOL on ARGS->OUTFILE. ARGS->DEPTH says how
+ far to indent. ARGS is really a struct print_symbol_args *, but is
+ declared as char * to get it past catch_errors. Returns 0 for error,
+ 1 for success. */
+
+static int
+print_symbol (args)
+ char *args;
+{
+ struct symbol *symbol = ((struct print_symbol_args *)args)->symbol;
+ int depth = ((struct print_symbol_args *)args)->depth;
+ FILE *outfile = ((struct print_symbol_args *)args)->outfile;
+
+ print_spaces (depth, outfile);
+ if (SYMBOL_NAMESPACE (symbol) == LABEL_NAMESPACE)
+ {
+ fprintf (outfile, "label %s at 0x%lx\n", SYMBOL_SOURCE_NAME (symbol),
+ (unsigned long) SYMBOL_VALUE_ADDRESS (symbol));
+ return 1;
+ }
+ if (SYMBOL_NAMESPACE (symbol) == STRUCT_NAMESPACE)
+ {
+ if (TYPE_TAG_NAME (SYMBOL_TYPE (symbol)))
+ {
+ LA_PRINT_TYPE (SYMBOL_TYPE (symbol), "", outfile, 1, depth);
+ }
+ else
+ {
+ fprintf (outfile, "%s %s = ",
+ (TYPE_CODE (SYMBOL_TYPE (symbol)) == TYPE_CODE_ENUM
+ ? "enum"
+ : (TYPE_CODE (SYMBOL_TYPE (symbol)) == TYPE_CODE_STRUCT
+ ? "struct" : "union")),
+ SYMBOL_NAME (symbol));
+ LA_PRINT_TYPE (SYMBOL_TYPE (symbol), "", outfile, 1, depth);
+ }
+ fprintf (outfile, ";\n");
+ }
+ else
+ {
+ if (SYMBOL_CLASS (symbol) == LOC_TYPEDEF)
+ fprintf (outfile, "typedef ");
+ if (SYMBOL_TYPE (symbol))
+ {
+ /* Print details of types, except for enums where it's clutter. */
+ LA_PRINT_TYPE (SYMBOL_TYPE (symbol), SYMBOL_SOURCE_NAME (symbol),
+ outfile,
+ TYPE_CODE (SYMBOL_TYPE (symbol)) != TYPE_CODE_ENUM,
+ depth);
+ fprintf (outfile, "; ");
+ }
+ else
+ fprintf (outfile, "%s ", SYMBOL_SOURCE_NAME (symbol));
+
+ switch (SYMBOL_CLASS (symbol))
+ {
+ case LOC_CONST:
+ fprintf (outfile, "const %ld (0x%lx),",
+ SYMBOL_VALUE (symbol),
+ (unsigned long) SYMBOL_VALUE (symbol));
+ break;
+
+ case LOC_CONST_BYTES:
+ fprintf (outfile, "const %u hex bytes:",
+ TYPE_LENGTH (SYMBOL_TYPE (symbol)));
+ {
+ unsigned i;
+ for (i = 0; i < TYPE_LENGTH (SYMBOL_TYPE (symbol)); i++)
+ fprintf (outfile, " %02x",
+ (unsigned)SYMBOL_VALUE_BYTES (symbol) [i]);
+ fprintf (outfile, ",");
+ }
+ break;
+
+ case LOC_STATIC:
+ fprintf (outfile, "static at 0x%lx,",
+ (unsigned long) SYMBOL_VALUE_ADDRESS (symbol));
+ break;
+
+ case LOC_REGISTER:
+ fprintf (outfile, "register %ld,", SYMBOL_VALUE (symbol));
+ break;
+
+ case LOC_ARG:
+ fprintf (outfile, "arg at 0x%lx,", SYMBOL_VALUE (symbol));
+ break;
+
+ case LOC_LOCAL_ARG:
+ fprintf (outfile, "arg at offset 0x%lx from fp,",
+ SYMBOL_VALUE (symbol));
+ break;
+
+ case LOC_REF_ARG:
+ fprintf (outfile, "reference arg at 0x%lx,", SYMBOL_VALUE (symbol));
+ break;
+
+ case LOC_REGPARM:
+ fprintf (outfile, "parameter register %ld,", SYMBOL_VALUE (symbol));
+ break;
+
+ case LOC_REGPARM_ADDR:
+ fprintf (outfile, "address parameter register %ld,", SYMBOL_VALUE (symbol));
+ break;
+
+ case LOC_LOCAL:
+ fprintf (outfile, "local at 0x%lx,", SYMBOL_VALUE (symbol));
+ break;
+
+ case LOC_BASEREG:
+ fprintf (outfile, "local at 0x%lx from register %d",
+ SYMBOL_VALUE (symbol), SYMBOL_BASEREG (symbol));
+ break;
+
+ case LOC_BASEREG_ARG:
+ fprintf (outfile, "arg at 0x%lx from register %d,",
+ SYMBOL_VALUE (symbol), SYMBOL_BASEREG (symbol));
+ break;
+
+ case LOC_TYPEDEF:
+ break;
+
+ case LOC_LABEL:
+ fprintf (outfile, "label at 0x%lx",
+ (unsigned long) SYMBOL_VALUE_ADDRESS (symbol));
+ break;
+
+ case LOC_BLOCK:
+ fprintf (outfile, "block (object 0x%lx) starting at 0x%lx,",
+ (unsigned long) SYMBOL_BLOCK_VALUE (symbol),
+ (unsigned long) BLOCK_START (SYMBOL_BLOCK_VALUE (symbol)));
+ break;
+
+ case LOC_OPTIMIZED_OUT:
+ fprintf (outfile, "optimized out");
+ break;
+
+ default:
+ fprintf (outfile, "botched symbol class %x", SYMBOL_CLASS (symbol));
+ break;
+ }
+ }
+ fprintf (outfile, "\n");
+ return 1;
+}
+
+void
+maintenance_print_psymbols (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ char **argv;
+ FILE *outfile;
+ struct cleanup *cleanups;
+ char *symname = NULL;
+ char *filename = DEV_TTY;
+ struct objfile *objfile;
+ struct partial_symtab *ps;
+
+ dont_repeat ();
+
+ if (args == NULL)
+ {
+ error ("print-psymbols takes an output file name and optional symbol file name");
+ }
+ else if ((argv = buildargv (args)) == NULL)
+ {
+ nomem (0);
+ }
+ cleanups = make_cleanup (freeargv, (char *) argv);
+
+ if (argv[0] != NULL)
+ {
+ filename = argv[0];
+ /* If a second arg is supplied, it is a source file name to match on */
+ if (argv[1] != NULL)
+ {
+ symname = argv[1];
+ }
+ }
+
+ filename = tilde_expand (filename);
+ make_cleanup (free, filename);
+
+ outfile = fopen (filename, FOPEN_WT);
+ if (outfile == 0)
+ perror_with_name (filename);
+ make_cleanup (fclose, outfile);
+
+ immediate_quit++;
+ ALL_PSYMTABS (objfile, ps)
+ if (symname == NULL || (STREQ (symname, ps -> filename)))
+ dump_psymtab (objfile, ps, outfile);
+ immediate_quit--;
+ do_cleanups (cleanups);
+}
+
+static void
+print_partial_symbol (p, count, what, outfile)
+ struct partial_symbol *p;
+ int count;
+ char *what;
+ FILE *outfile;
+{
+
+ fprintf_filtered (outfile, " %s partial symbols:\n", what);
+ while (count-- > 0)
+ {
+ fprintf_filtered (outfile, " `%s'", SYMBOL_NAME(p));
+ if (SYMBOL_DEMANGLED_NAME (p) != NULL)
+ {
+ fprintf_filtered (outfile, " `%s'", SYMBOL_DEMANGLED_NAME (p));
+ }
+ fputs_filtered (", ", outfile);
+ switch (SYMBOL_NAMESPACE (p))
+ {
+ case UNDEF_NAMESPACE:
+ fputs_filtered ("undefined namespace, ", outfile);
+ break;
+ case VAR_NAMESPACE:
+ /* This is the usual thing -- don't print it */
+ break;
+ case STRUCT_NAMESPACE:
+ fputs_filtered ("struct namespace, ", outfile);
+ break;
+ case LABEL_NAMESPACE:
+ fputs_filtered ("label namespace, ", outfile);
+ break;
+ default:
+ fputs_filtered ("<invalid namespace>, ", outfile);
+ break;
+ }
+ switch (SYMBOL_CLASS (p))
+ {
+ case LOC_UNDEF:
+ fputs_filtered ("undefined", outfile);
+ break;
+ case LOC_CONST:
+ fputs_filtered ("constant int", outfile);
+ break;
+ case LOC_STATIC:
+ fputs_filtered ("static", outfile);
+ break;
+ case LOC_REGISTER:
+ fputs_filtered ("register", outfile);
+ break;
+ case LOC_ARG:
+ fputs_filtered ("pass by value", outfile);
+ break;
+ case LOC_REF_ARG:
+ fputs_filtered ("pass by reference", outfile);
+ break;
+ case LOC_REGPARM:
+ fputs_filtered ("register parameter", outfile);
+ break;
+ case LOC_REGPARM_ADDR:
+ fputs_filtered ("register address parameter", outfile);
+ break;
+ case LOC_LOCAL:
+ fputs_filtered ("stack parameter", outfile);
+ break;
+ case LOC_TYPEDEF:
+ fputs_filtered ("type", outfile);
+ break;
+ case LOC_LABEL:
+ fputs_filtered ("label", outfile);
+ break;
+ case LOC_BLOCK:
+ fputs_filtered ("function", outfile);
+ break;
+ case LOC_CONST_BYTES:
+ fputs_filtered ("constant bytes", outfile);
+ break;
+ case LOC_LOCAL_ARG:
+ fputs_filtered ("shuffled arg", outfile);
+ break;
+ case LOC_OPTIMIZED_OUT:
+ fputs_filtered ("optimized out", outfile);
+ break;
+ default:
+ fputs_filtered ("<invalid location>", outfile);
+ break;
+ }
+ fputs_filtered (", ", outfile);
+ fprintf_filtered (outfile, "0x%lx\n", SYMBOL_VALUE (p));
+ p++;
+ }
+}
+
+void
+maintenance_print_msymbols (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ char **argv;
+ FILE *outfile;
+ struct cleanup *cleanups;
+ char *filename = DEV_TTY;
+ char *symname = NULL;
+ struct objfile *objfile;
+
+ dont_repeat ();
+
+ if (args == NULL)
+ {
+ error ("print-msymbols takes an output file name and optional symbol file name");
+ }
+ else if ((argv = buildargv (args)) == NULL)
+ {
+ nomem (0);
+ }
+ cleanups = make_cleanup (freeargv, argv);
+
+ if (argv[0] != NULL)
+ {
+ filename = argv[0];
+ /* If a second arg is supplied, it is a source file name to match on */
+ if (argv[1] != NULL)
+ {
+ symname = argv[1];
+ }
+ }
+
+ filename = tilde_expand (filename);
+ make_cleanup (free, filename);
+
+ outfile = fopen (filename, FOPEN_WT);
+ if (outfile == 0)
+ perror_with_name (filename);
+ make_cleanup (fclose, outfile);
+
+ immediate_quit++;
+ ALL_OBJFILES (objfile)
+ if (symname == NULL || (STREQ (symname, objfile -> name)))
+ dump_msymbols (objfile, outfile);
+ immediate_quit--;
+ fprintf_filtered (outfile, "\n\n");
+ do_cleanups (cleanups);
+}
+
+void
+maintenance_print_objfiles (ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ struct objfile *objfile;
+
+ dont_repeat ();
+
+ immediate_quit++;
+ ALL_OBJFILES (objfile)
+ dump_objfile (objfile);
+ immediate_quit--;
+}
+
+
+/* Return the nexting depth of a block within other blocks in its symtab. */
+
+static int
+block_depth (block)
+ struct block *block;
+{
+ register int i = 0;
+ while ((block = BLOCK_SUPERBLOCK (block)) != NULL)
+ {
+ i++;
+ }
+ return i;
+}
+
+#endif /* MAINTENANCE_CMDS */
+
+
+/* Increase the space allocated for LISTP, which is probably
+ global_psymbol_list or static_psymbol_list. This space will eventually
+ be freed in free_objfile(). */
+
+void
+extend_psymbol_list (listp, objfile)
+ register struct psymbol_allocation_list *listp;
+ struct objfile *objfile;
+{
+ int new_size;
+ if (listp->size == 0)
+ {
+ new_size = 255;
+ listp->list = (struct partial_symbol *)
+ xmmalloc (objfile -> md, new_size * sizeof (struct partial_symbol));
+ }
+ else
+ {
+ new_size = listp->size * 2;
+ listp->list = (struct partial_symbol *)
+ xmrealloc (objfile -> md, (char *) listp->list,
+ new_size * sizeof (struct partial_symbol));
+ }
+ /* Next assumes we only went one over. Should be good if
+ program works correctly */
+ listp->next = listp->list + listp->size;
+ listp->size = new_size;
+}
+
+
+/* Do early runtime initializations. */
+void
+_initialize_symmisc ()
+{
+ std_in = stdin;
+ std_out = stdout;
+ std_err = stderr;
+}
diff --git a/gnu/usr.bin/gdb/gdb/symtab.c b/gnu/usr.bin/gdb/gdb/symtab.c
new file mode 100644
index 0000000..0d0255c
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/symtab.c
@@ -0,0 +1,3035 @@
+/* Symbol table lookup for the GNU debugger, GDB.
+ Copyright 1986, 1987, 1988, 1989, 1990, 1991, 1992
+ Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "gdbcore.h"
+#include "frame.h"
+#include "target.h"
+#include "value.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "gdbcmd.h"
+#include "call-cmds.h"
+#include "regex.h"
+#include "expression.h"
+#include "language.h"
+#include "demangle.h"
+
+#include <obstack.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <ctype.h>
+
+/* Prototypes for local functions */
+
+extern int
+find_methods PARAMS ((struct type *, char *, struct symbol **));
+
+static void
+completion_list_add_name PARAMS ((char *, char *, int, char *, char *));
+
+static void
+build_canonical_line_spec PARAMS ((struct symtab_and_line *, char *, char ***));
+
+static struct symtabs_and_lines
+decode_line_2 PARAMS ((struct symbol *[], int, int, char ***));
+
+static void
+rbreak_command PARAMS ((char *, int));
+
+static void
+types_info PARAMS ((char *, int));
+
+static void
+functions_info PARAMS ((char *, int));
+
+static void
+variables_info PARAMS ((char *, int));
+
+static void
+sources_info PARAMS ((char *, int));
+
+static void
+list_symbols PARAMS ((char *, int, int));
+
+static void
+output_source_filename PARAMS ((char *, int *));
+
+static char *
+operator_chars PARAMS ((char *, char **));
+
+static int find_line_common PARAMS ((struct linetable *, int, int *));
+
+static struct partial_symbol *
+lookup_partial_symbol PARAMS ((struct partial_symtab *, const char *,
+ int, enum namespace));
+
+static struct symtab *
+lookup_symtab_1 PARAMS ((char *));
+
+/* */
+
+/* The single non-language-specific builtin type */
+struct type *builtin_type_error;
+
+/* Block in which the most recently searched-for symbol was found.
+ Might be better to make this a parameter to lookup_symbol and
+ value_of_this. */
+
+const struct block *block_found;
+
+char no_symtab_msg[] = "No symbol table is loaded. Use the \"file\" command.";
+
+/* While the C++ support is still in flux, issue a possibly helpful hint on
+ using the new command completion feature on single quoted demangled C++
+ symbols. Remove when loose ends are cleaned up. FIXME -fnf */
+
+void
+cplusplus_hint (name)
+ char *name;
+{
+ printf ("Hint: try '%s<TAB> or '%s<ESC-?>\n", name, name);
+ printf ("(Note leading single quote.)\n");
+}
+
+/* Check for a symtab of a specific name; first in symtabs, then in
+ psymtabs. *If* there is no '/' in the name, a match after a '/'
+ in the symtab filename will also work. */
+
+static struct symtab *
+lookup_symtab_1 (name)
+ char *name;
+{
+ register struct symtab *s;
+ register struct partial_symtab *ps;
+ register char *slash;
+ register struct objfile *objfile;
+
+ got_symtab:
+
+ /* First, search for an exact match */
+
+ ALL_SYMTABS (objfile, s)
+ if (STREQ (name, s->filename))
+ return s;
+
+ slash = strchr (name, '/');
+
+ /* Now, search for a matching tail (only if name doesn't have any dirs) */
+
+ if (!slash)
+ ALL_SYMTABS (objfile, s)
+ {
+ char *p = s -> filename;
+ char *tail = strrchr (p, '/');
+
+ if (tail)
+ p = tail + 1;
+
+ if (STREQ (p, name))
+ return s;
+ }
+
+ /* Same search rules as above apply here, but now we look thru the
+ psymtabs. */
+
+ ps = lookup_partial_symtab (name);
+ if (!ps)
+ return (NULL);
+
+ if (ps -> readin)
+ error ("Internal: readin %s pst for `%s' found when no symtab found.",
+ ps -> filename, name);
+
+ s = PSYMTAB_TO_SYMTAB (ps);
+
+ if (s)
+ return s;
+
+ /* At this point, we have located the psymtab for this file, but
+ the conversion to a symtab has failed. This usually happens
+ when we are looking up an include file. In this case,
+ PSYMTAB_TO_SYMTAB doesn't return a symtab, even though one has
+ been created. So, we need to run through the symtabs again in
+ order to find the file.
+ XXX - This is a crock, and should be fixed inside of the the
+ symbol parsing routines. */
+ goto got_symtab;
+}
+
+/* Lookup the symbol table of a source file named NAME. Try a couple
+ of variations if the first lookup doesn't work. */
+
+struct symtab *
+lookup_symtab (name)
+ char *name;
+{
+ register struct symtab *s;
+ register char *copy;
+
+ s = lookup_symtab_1 (name);
+ if (s) return s;
+
+ /* If name not found as specified, see if adding ".c" helps. */
+ /* Why is this? Is it just a user convenience? (If so, it's pretty
+ questionable in the presence of C++, FORTRAN, etc.). It's not in
+ the GDB manual. */
+
+ copy = (char *) alloca (strlen (name) + 3);
+ strcpy (copy, name);
+ strcat (copy, ".c");
+ s = lookup_symtab_1 (copy);
+ if (s) return s;
+
+ /* We didn't find anything; die. */
+ return 0;
+}
+
+/* Lookup the partial symbol table of a source file named NAME.
+ *If* there is no '/' in the name, a match after a '/'
+ in the psymtab filename will also work. */
+
+struct partial_symtab *
+lookup_partial_symtab (name)
+char *name;
+{
+ register struct partial_symtab *pst;
+ register struct objfile *objfile;
+
+ ALL_PSYMTABS (objfile, pst)
+ {
+ if (STREQ (name, pst -> filename))
+ {
+ return (pst);
+ }
+ }
+
+ /* Now, search for a matching tail (only if name doesn't have any dirs) */
+
+ if (!strchr (name, '/'))
+ ALL_PSYMTABS (objfile, pst)
+ {
+ char *p = pst -> filename;
+ char *tail = strrchr (p, '/');
+
+ if (tail)
+ p = tail + 1;
+
+ if (STREQ (p, name))
+ return (pst);
+ }
+
+ return (NULL);
+}
+
+/* Demangle a GDB method stub type.
+ Note that this function is g++ specific. */
+
+char *
+gdb_mangle_name (type, i, j)
+ struct type *type;
+ int i, j;
+{
+ int mangled_name_len;
+ char *mangled_name;
+ struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i);
+ struct fn_field *method = &f[j];
+ char *field_name = TYPE_FN_FIELDLIST_NAME (type, i);
+ char *physname = TYPE_FN_FIELD_PHYSNAME (f, j);
+ char *newname = type_name_no_tag (type);
+ int is_constructor;
+ int is_destructor = DESTRUCTOR_PREFIX_P (physname);
+ /* Need a new type prefix. */
+ char *const_prefix = method->is_const ? "C" : "";
+ char *volatile_prefix = method->is_volatile ? "V" : "";
+ char buf[20];
+ int len = (newname == NULL ? 0 : strlen (newname));
+ char *opname;
+
+ is_constructor = newname && STREQ(field_name, newname);
+ if (!is_constructor)
+ is_constructor = (physname[0]=='_' && physname[1]=='_' &&
+ (isdigit(physname[2]) || physname[2]=='Q' || physname[2]=='t'));
+ if (!is_constructor)
+ is_constructor = (strncmp(physname, "__ct", 4) == 0);
+ if (!is_destructor)
+ is_destructor = (strncmp(physname, "__dt", 4) == 0);
+
+#ifndef GCC_MANGLE_BUG
+ if (is_destructor)
+ {
+ mangled_name = (char*) xmalloc(strlen(physname)+1);
+ strcpy(mangled_name, physname);
+ return mangled_name;
+ }
+
+ if (len == 0)
+ {
+ sprintf (buf, "__%s%s", const_prefix, volatile_prefix);
+ if (strcmp(buf, "__") == 0)
+ buf[0] = '\0';
+ }
+ else
+ {
+ sprintf (buf, "__%s%s%d", const_prefix, volatile_prefix, len);
+ }
+ mangled_name_len = ((is_constructor ? 0 : strlen (field_name))
+ + strlen (buf) + len
+ + strlen (physname)
+ + 1);
+
+ /* Only needed for GNU-mangled names. ANSI-mangled names
+ work with the normal mechanisms. */
+ if (OPNAME_PREFIX_P (field_name))
+ {
+ char *opname = cplus_mangle_opname (field_name + 3, 0);
+ if (opname == NULL)
+ error ("No mangling for \"%s\"", field_name);
+ mangled_name_len += strlen (opname);
+ mangled_name = (char *)xmalloc (mangled_name_len);
+
+ strncpy (mangled_name, field_name, 3);
+ mangled_name[3] = '\0';
+ strcat (mangled_name, opname);
+ }
+ else
+ {
+ mangled_name = (char *)xmalloc (mangled_name_len);
+ if (is_constructor)
+ mangled_name[0] = '\0';
+ else
+ strcpy (mangled_name, field_name);
+ }
+ strcat (mangled_name, buf);
+ /* If the class doesn't have a name, i.e. newname NULL, then we just
+ mangle it using 0 for the length of the class. Thus it gets mangled
+ as something starting with `::' rather than `classname::'. */
+ if (newname != NULL)
+ strcat (mangled_name, newname);
+
+#else
+
+ if (is_constructor)
+ {
+ buf[0] = '\0';
+ }
+ else
+ {
+ sprintf (buf, "__%s%s", const_prefix, volatile_prefix);
+ }
+
+ mangled_name_len = ((is_constructor ? 0 : strlen (field_name))
+ + strlen (buf) + strlen (physname) + 1);
+
+ /* Only needed for GNU-mangled names. ANSI-mangled names
+ work with the normal mechanisms. */
+ if (OPNAME_PREFIX_P (field_name))
+ {
+ opname = cplus_mangle_opname (field_name + 3, 0);
+ if (opname == NULL)
+ {
+ error ("No mangling for \"%s\"", field_name);
+ }
+ mangled_name_len += strlen (opname);
+ mangled_name = (char *) xmalloc (mangled_name_len);
+
+ strncpy (mangled_name, field_name, 3);
+ strcpy (mangled_name + 3, opname);
+ }
+ else
+ {
+ mangled_name = (char *) xmalloc (mangled_name_len);
+ if (is_constructor)
+ {
+ mangled_name[0] = '\0';
+ }
+ else
+ {
+ strcpy (mangled_name, field_name);
+ }
+ }
+ strcat (mangled_name, buf);
+
+#endif
+ strcat (mangled_name, physname);
+ return (mangled_name);
+}
+
+
+/* Find which partial symtab on contains PC. Return 0 if none. */
+
+struct partial_symtab *
+find_pc_psymtab (pc)
+ register CORE_ADDR pc;
+{
+ register struct partial_symtab *pst;
+ register struct objfile *objfile;
+
+ ALL_PSYMTABS (objfile, pst)
+ {
+ if (pc >= pst->textlow && pc < pst->texthigh)
+ return (pst);
+ }
+ return (NULL);
+}
+
+/* Find which partial symbol within a psymtab contains PC. Return 0
+ if none. Check all psymtabs if PSYMTAB is 0. */
+struct partial_symbol *
+find_pc_psymbol (psymtab, pc)
+ struct partial_symtab *psymtab;
+ CORE_ADDR pc;
+{
+ struct partial_symbol *best = NULL, *p;
+ CORE_ADDR best_pc;
+
+ if (!psymtab)
+ psymtab = find_pc_psymtab (pc);
+ if (!psymtab)
+ return 0;
+
+ best_pc = psymtab->textlow - 1;
+
+ for (p = psymtab->objfile->static_psymbols.list + psymtab->statics_offset;
+ (p - (psymtab->objfile->static_psymbols.list + psymtab->statics_offset)
+ < psymtab->n_static_syms);
+ p++)
+ if (SYMBOL_NAMESPACE (p) == VAR_NAMESPACE
+ && SYMBOL_CLASS (p) == LOC_BLOCK
+ && pc >= SYMBOL_VALUE_ADDRESS (p)
+ && SYMBOL_VALUE_ADDRESS (p) > best_pc)
+ {
+ best_pc = SYMBOL_VALUE_ADDRESS (p);
+ best = p;
+ }
+ if (best_pc == psymtab->textlow - 1)
+ return 0;
+ return best;
+}
+
+
+/* Find the definition for a specified symbol name NAME
+ in namespace NAMESPACE, visible from lexical block BLOCK.
+ Returns the struct symbol pointer, or zero if no symbol is found.
+ If SYMTAB is non-NULL, store the symbol table in which the
+ symbol was found there, or NULL if not found.
+ C++: if IS_A_FIELD_OF_THIS is nonzero on entry, check to see if
+ NAME is a field of the current implied argument `this'. If so set
+ *IS_A_FIELD_OF_THIS to 1, otherwise set it to zero.
+ BLOCK_FOUND is set to the block in which NAME is found (in the case of
+ a field of `this', value_of_this sets BLOCK_FOUND to the proper value.) */
+
+struct symbol *
+lookup_symbol (name, block, namespace, is_a_field_of_this, symtab)
+ const char *name;
+ register const struct block *block;
+ const enum namespace namespace;
+ int *is_a_field_of_this;
+ struct symtab **symtab;
+{
+ register struct symbol *sym;
+ register struct symtab *s = NULL;
+ register struct partial_symtab *ps;
+ struct blockvector *bv;
+ register struct objfile *objfile;
+ register struct block *b;
+ register struct minimal_symbol *msymbol;
+
+ /* Search specified block and its superiors. */
+
+ while (block != 0)
+ {
+ sym = lookup_block_symbol (block, name, namespace);
+ if (sym)
+ {
+ block_found = block;
+ if (symtab != NULL)
+ {
+ /* Search the list of symtabs for one which contains the
+ address of the start of this block. */
+ ALL_SYMTABS (objfile, s)
+ {
+ bv = BLOCKVECTOR (s);
+ b = BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK);
+ if (BLOCK_START (b) <= BLOCK_START (block)
+ && BLOCK_END (b) > BLOCK_START (block))
+ goto found;
+ }
+found:
+ *symtab = s;
+ }
+
+ return (sym);
+ }
+ block = BLOCK_SUPERBLOCK (block);
+ }
+
+ /* FIXME: this code is never executed--block is always NULL at this
+ point. What is it trying to do, anyway? We already should have
+ checked the STATIC_BLOCK above (it is the superblock of top-level
+ blocks). Why is VAR_NAMESPACE special-cased? */
+ /* Don't need to mess with the psymtabs; if we have a block,
+ that file is read in. If we don't, then we deal later with
+ all the psymtab stuff that needs checking. */
+ if (namespace == VAR_NAMESPACE && block != NULL)
+ {
+ struct block *b;
+ /* Find the right symtab. */
+ ALL_SYMTABS (objfile, s)
+ {
+ bv = BLOCKVECTOR (s);
+ b = BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK);
+ if (BLOCK_START (b) <= BLOCK_START (block)
+ && BLOCK_END (b) > BLOCK_START (block))
+ {
+ sym = lookup_block_symbol (b, name, VAR_NAMESPACE);
+ if (sym)
+ {
+ block_found = b;
+ if (symtab != NULL)
+ *symtab = s;
+ return sym;
+ }
+ }
+ }
+ }
+
+
+ /* C++: If requested to do so by the caller,
+ check to see if NAME is a field of `this'. */
+ if (is_a_field_of_this)
+ {
+ struct value *v = value_of_this (0);
+
+ *is_a_field_of_this = 0;
+ if (v && check_field (v, name))
+ {
+ *is_a_field_of_this = 1;
+ if (symtab != NULL)
+ *symtab = NULL;
+ return 0;
+ }
+ }
+
+ /* Now search all global blocks. Do the symtab's first, then
+ check the psymtab's */
+
+ ALL_SYMTABS (objfile, s)
+ {
+ bv = BLOCKVECTOR (s);
+ block = BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK);
+ sym = lookup_block_symbol (block, name, namespace);
+ if (sym)
+ {
+ block_found = block;
+ if (symtab != NULL)
+ *symtab = s;
+ return sym;
+ }
+ }
+
+ /* Check for the possibility of the symbol being a global function
+ that is stored in one of the minimal symbol tables. Eventually, all
+ global symbols might be resolved in this way. */
+
+ if (namespace == VAR_NAMESPACE)
+ {
+ msymbol = lookup_minimal_symbol (name, (struct objfile *) NULL);
+ if (msymbol != NULL)
+ {
+ s = find_pc_symtab (SYMBOL_VALUE_ADDRESS (msymbol));
+ /* If S is NULL, there are no debug symbols for this file.
+ Skip this stuff and check for matching static symbols below. */
+ if (s != NULL)
+ {
+ bv = BLOCKVECTOR (s);
+ block = BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK);
+ sym = lookup_block_symbol (block, SYMBOL_NAME (msymbol),
+ namespace);
+ /* We kept static functions in minimal symbol table as well as
+ in static scope. We want to find them in the symbol table. */
+ if (!sym) {
+ block = BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK);
+ sym = lookup_block_symbol (block, SYMBOL_NAME (msymbol),
+ namespace);
+ }
+
+ /* sym == 0 if symbol was found in the minimal symbol table
+ but not in the symtab.
+ Return 0 to use the msymbol definition of "foo_".
+
+ This happens for Fortran "foo_" symbols,
+ which are "foo" in the symtab.
+
+ This can also happen if "asm" is used to make a
+ regular symbol but not a debugging symbol, e.g.
+ asm(".globl _main");
+ asm("_main:");
+ */
+
+ if (symtab != NULL)
+ *symtab = s;
+ return sym;
+ }
+ }
+ }
+
+ ALL_PSYMTABS (objfile, ps)
+ {
+ if (!ps->readin && lookup_partial_symbol (ps, name, 1, namespace))
+ {
+ s = PSYMTAB_TO_SYMTAB(ps);
+ bv = BLOCKVECTOR (s);
+ block = BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK);
+ sym = lookup_block_symbol (block, name, namespace);
+ if (!sym)
+ error ("Internal: global symbol `%s' found in %s psymtab but not in symtab", name, ps->filename);
+ if (symtab != NULL)
+ *symtab = s;
+ return sym;
+ }
+ }
+
+ /* Now search all per-file blocks.
+ Not strictly correct, but more useful than an error.
+ Do the symtabs first, then check the psymtabs */
+
+ ALL_SYMTABS (objfile, s)
+ {
+ bv = BLOCKVECTOR (s);
+ block = BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK);
+ sym = lookup_block_symbol (block, name, namespace);
+ if (sym)
+ {
+ block_found = block;
+ if (symtab != NULL)
+ *symtab = s;
+ return sym;
+ }
+ }
+
+ ALL_PSYMTABS (objfile, ps)
+ {
+ if (!ps->readin && lookup_partial_symbol (ps, name, 0, namespace))
+ {
+ s = PSYMTAB_TO_SYMTAB(ps);
+ bv = BLOCKVECTOR (s);
+ block = BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK);
+ sym = lookup_block_symbol (block, name, namespace);
+ if (!sym)
+ error ("Internal: static symbol `%s' found in %s psymtab but not in symtab", name, ps->filename);
+ if (symtab != NULL)
+ *symtab = s;
+ return sym;
+ }
+ }
+
+ /* Now search all per-file blocks for static mangled symbols.
+ Do the symtabs first, then check the psymtabs. */
+
+ if (namespace == VAR_NAMESPACE)
+ {
+ ALL_SYMTABS (objfile, s)
+ {
+ bv = BLOCKVECTOR (s);
+ block = BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK);
+ sym = lookup_block_symbol (block, name, VAR_NAMESPACE);
+ if (sym)
+ {
+ block_found = block;
+ if (symtab != NULL)
+ *symtab = s;
+ return sym;
+ }
+ }
+
+ ALL_PSYMTABS (objfile, ps)
+ {
+ if (!ps->readin && lookup_partial_symbol (ps, name, 0, VAR_NAMESPACE))
+ {
+ s = PSYMTAB_TO_SYMTAB(ps);
+ bv = BLOCKVECTOR (s);
+ block = BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK);
+ sym = lookup_block_symbol (block, name, VAR_NAMESPACE);
+ if (!sym)
+ error ("Internal: mangled static symbol `%s' found in %s psymtab but not in symtab", name, ps->filename);
+ if (symtab != NULL)
+ *symtab = s;
+ return sym;
+ }
+ }
+ }
+
+ if (symtab != NULL)
+ *symtab = NULL;
+ return 0;
+}
+
+/* Look, in partial_symtab PST, for symbol NAME. Check the global
+ symbols if GLOBAL, the static symbols if not */
+
+static struct partial_symbol *
+lookup_partial_symbol (pst, name, global, namespace)
+ struct partial_symtab *pst;
+ const char *name;
+ int global;
+ enum namespace namespace;
+{
+ struct partial_symbol *start, *psym;
+ struct partial_symbol *top, *bottom, *center;
+ int length = (global ? pst->n_global_syms : pst->n_static_syms);
+ int do_linear_search = 1;
+
+ if (length == 0)
+ {
+ return (NULL);
+ }
+
+ start = (global ?
+ pst->objfile->global_psymbols.list + pst->globals_offset :
+ pst->objfile->static_psymbols.list + pst->statics_offset );
+
+ if (global) /* This means we can use a binary search. */
+ {
+ do_linear_search = 0;
+
+ /* Binary search. This search is guaranteed to end with center
+ pointing at the earliest partial symbol with the correct
+ name. At that point *all* partial symbols with that name
+ will be checked against the correct namespace. */
+
+ bottom = start;
+ top = start + length - 1;
+ while (top > bottom)
+ {
+ center = bottom + (top - bottom) / 2;
+ assert (center < top);
+ if (!do_linear_search && SYMBOL_LANGUAGE (center) == language_cplus)
+ {
+ do_linear_search = 1;
+ }
+ if (STRCMP (SYMBOL_NAME (center), name) >= 0)
+ {
+ top = center;
+ }
+ else
+ {
+ bottom = center + 1;
+ }
+ }
+ assert (top == bottom);
+ while (STREQ (SYMBOL_NAME (top), name))
+ {
+ if (SYMBOL_NAMESPACE (top) == namespace)
+ {
+ return top;
+ }
+ top ++;
+ }
+ }
+
+ /* Can't use a binary search or else we found during the binary search that
+ we should also do a linear search. */
+
+ if (do_linear_search)
+ {
+ for (psym = start; psym < start + length; psym++)
+ {
+ if (namespace == SYMBOL_NAMESPACE (psym))
+ {
+ if (SYMBOL_MATCHES_NAME (psym, name))
+ {
+ return (psym);
+ }
+ }
+ }
+ }
+
+ return (NULL);
+}
+
+/* Find the psymtab containing main(). */
+/* FIXME: What about languages without main() or specially linked
+ executables that have no main() ? */
+
+struct partial_symtab *
+find_main_psymtab ()
+{
+ register struct partial_symtab *pst;
+ register struct objfile *objfile;
+
+ ALL_PSYMTABS (objfile, pst)
+ {
+ if (lookup_partial_symbol (pst, "main", 1, VAR_NAMESPACE))
+ {
+ return (pst);
+ }
+ }
+ return (NULL);
+}
+
+/* Search BLOCK for symbol NAME in NAMESPACE.
+
+ Note that if NAME is the demangled form of a C++ symbol, we will fail
+ to find a match during the binary search of the non-encoded names, but
+ for now we don't worry about the slight inefficiency of looking for
+ a match we'll never find, since it will go pretty quick. Once the
+ binary search terminates, we drop through and do a straight linear
+ search on the symbols. Each symbol which is marked as being a C++
+ symbol (language_cplus set) has both the encoded and non-encoded names
+ tested for a match. */
+
+struct symbol *
+lookup_block_symbol (block, name, namespace)
+ register const struct block *block;
+ const char *name;
+ const enum namespace namespace;
+{
+ register int bot, top, inc;
+ register struct symbol *sym;
+ register struct symbol *sym_found = NULL;
+ register int do_linear_search = 1;
+
+ /* If the blocks's symbols were sorted, start with a binary search. */
+
+ if (BLOCK_SHOULD_SORT (block))
+ {
+ /* Reset the linear search flag so if the binary search fails, we
+ won't do the linear search once unless we find some reason to
+ do so, such as finding a C++ symbol during the binary search.
+ Note that for C++ modules, ALL the symbols in a block should
+ end up marked as C++ symbols. */
+
+ do_linear_search = 0;
+ top = BLOCK_NSYMS (block);
+ bot = 0;
+
+ /* Advance BOT to not far before the first symbol whose name is NAME. */
+
+ while (1)
+ {
+ inc = (top - bot + 1);
+ /* No need to keep binary searching for the last few bits worth. */
+ if (inc < 4)
+ {
+ break;
+ }
+ inc = (inc >> 1) + bot;
+ sym = BLOCK_SYM (block, inc);
+ if (!do_linear_search && SYMBOL_LANGUAGE (sym) == language_cplus)
+ {
+ do_linear_search = 1;
+ }
+ if (SYMBOL_NAME (sym)[0] < name[0])
+ {
+ bot = inc;
+ }
+ else if (SYMBOL_NAME (sym)[0] > name[0])
+ {
+ top = inc;
+ }
+ else if (STRCMP (SYMBOL_NAME (sym), name) < 0)
+ {
+ bot = inc;
+ }
+ else
+ {
+ top = inc;
+ }
+ }
+
+ /* Now scan forward until we run out of symbols, find one whose
+ name is greater than NAME, or find one we want. If there is
+ more than one symbol with the right name and namespace, we
+ return the first one; I believe it is now impossible for us
+ to encounter two symbols with the same name and namespace
+ here, because blocks containing argument symbols are no
+ longer sorted. */
+
+ top = BLOCK_NSYMS (block);
+ while (bot < top)
+ {
+ sym = BLOCK_SYM (block, bot);
+ inc = SYMBOL_NAME (sym)[0] - name[0];
+ if (inc == 0)
+ {
+ inc = STRCMP (SYMBOL_NAME (sym), name);
+ }
+ if (inc == 0 && SYMBOL_NAMESPACE (sym) == namespace)
+ {
+ return (sym);
+ }
+ if (inc > 0)
+ {
+ break;
+ }
+ bot++;
+ }
+ }
+
+ /* Here if block isn't sorted, or we fail to find a match during the
+ binary search above. If during the binary search above, we find a
+ symbol which is a C++ symbol, then we have re-enabled the linear
+ search flag which was reset when starting the binary search.
+
+ This loop is equivalent to the loop above, but hacked greatly for speed.
+
+ Note that parameter symbols do not always show up last in the
+ list; this loop makes sure to take anything else other than
+ parameter symbols first; it only uses parameter symbols as a
+ last resort. Note that this only takes up extra computation
+ time on a match. */
+
+ if (do_linear_search)
+ {
+ top = BLOCK_NSYMS (block);
+ bot = 0;
+ while (bot < top)
+ {
+ sym = BLOCK_SYM (block, bot);
+ if (SYMBOL_NAMESPACE (sym) == namespace &&
+ SYMBOL_MATCHES_NAME (sym, name))
+ {
+ sym_found = sym;
+ if (SYMBOL_CLASS (sym) != LOC_ARG &&
+ SYMBOL_CLASS (sym) != LOC_LOCAL_ARG &&
+ SYMBOL_CLASS (sym) != LOC_REF_ARG &&
+ SYMBOL_CLASS (sym) != LOC_REGPARM &&
+ SYMBOL_CLASS (sym) != LOC_REGPARM_ADDR &&
+ SYMBOL_CLASS (sym) != LOC_BASEREG_ARG)
+ {
+ break;
+ }
+ }
+ bot++;
+ }
+ }
+ return (sym_found); /* Will be NULL if not found. */
+}
+
+
+/* Return the symbol for the function which contains a specified
+ lexical block, described by a struct block BL. */
+
+struct symbol *
+block_function (bl)
+ struct block *bl;
+{
+ while (BLOCK_FUNCTION (bl) == 0 && BLOCK_SUPERBLOCK (bl) != 0)
+ bl = BLOCK_SUPERBLOCK (bl);
+
+ return BLOCK_FUNCTION (bl);
+}
+
+/* Find the symtab associated with PC. Look through the psymtabs and read in
+ another symtab if necessary. */
+
+struct symtab *
+find_pc_symtab (pc)
+ register CORE_ADDR pc;
+{
+ register struct block *b;
+ struct blockvector *bv;
+ register struct symtab *s = NULL;
+ register struct symtab *best_s = NULL;
+ register struct partial_symtab *ps;
+ register struct objfile *objfile;
+ int distance = 0;
+
+ /* Search all symtabs for the one whose file contains our address, and which
+ is the smallest of all the ones containing the address. This is designed
+ to deal with a case like symtab a is at 0x1000-0x2000 and 0x3000-0x4000
+ and symtab b is at 0x2000-0x3000. So the GLOBAL_BLOCK for a is from
+ 0x1000-0x4000, but for address 0x2345 we want to return symtab b.
+ This is said to happen for the mips; it might be swifter to create
+ several symtabs with the same name like xcoff does (I'm not sure). */
+
+ ALL_SYMTABS (objfile, s)
+ {
+ bv = BLOCKVECTOR (s);
+ b = BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK);
+ if (BLOCK_START (b) <= pc
+ && BLOCK_END (b) > pc
+ && (distance == 0
+ || BLOCK_END (b) - BLOCK_START (b) < distance))
+ {
+ distance = BLOCK_END (b) - BLOCK_START (b);
+ best_s = s;
+ }
+ }
+
+ if (best_s != NULL)
+ return(best_s);
+
+ s = NULL;
+ ps = find_pc_psymtab (pc);
+ if (ps)
+ {
+ if (ps->readin)
+ /* Might want to error() here (in case symtab is corrupt and
+ will cause a core dump), but maybe we can successfully
+ continue, so let's not. */
+ warning ("\
+(Internal error: pc 0x%lx in read in psymtab, but not in symtab.)\n",
+ (unsigned long) pc);
+ s = PSYMTAB_TO_SYMTAB (ps);
+ }
+ return (s);
+}
+
+/* Find the source file and line number for a given PC value.
+ Return a structure containing a symtab pointer, a line number,
+ and a pc range for the entire source line.
+ The value's .pc field is NOT the specified pc.
+ NOTCURRENT nonzero means, if specified pc is on a line boundary,
+ use the line that ends there. Otherwise, in that case, the line
+ that begins there is used. */
+
+/* The big complication here is that a line may start in one file, and end just
+ before the start of another file. This usually occurs when you #include
+ code in the middle of a subroutine. To properly find the end of a line's PC
+ range, we must search all symtabs associated with this compilation unit, and
+ find the one whose first PC is closer than that of the next line in this
+ symtab. */
+
+/* If it's worth the effort, we could be using a binary search. */
+
+struct symtab_and_line
+find_pc_line (pc, notcurrent)
+ CORE_ADDR pc;
+ int notcurrent;
+{
+ struct symtab *s;
+ register struct linetable *l;
+ register int len;
+ register int i;
+ register struct linetable_entry *item;
+ struct symtab_and_line val;
+ struct blockvector *bv;
+
+ /* Info on best line seen so far, and where it starts, and its file. */
+
+ struct linetable_entry *best = NULL;
+ CORE_ADDR best_end = 0;
+ struct symtab *best_symtab = 0;
+
+ /* Store here the first line number
+ of a file which contains the line at the smallest pc after PC.
+ If we don't find a line whose range contains PC,
+ we will use a line one less than this,
+ with a range from the start of that file to the first line's pc. */
+ struct linetable_entry *alt = NULL;
+ struct symtab *alt_symtab = 0;
+
+ /* Info on best line seen in this file. */
+
+ struct linetable_entry *prev;
+
+ /* If this pc is not from the current frame,
+ it is the address of the end of a call instruction.
+ Quite likely that is the start of the following statement.
+ But what we want is the statement containing the instruction.
+ Fudge the pc to make sure we get that. */
+
+ if (notcurrent) pc -= 1;
+
+ s = find_pc_symtab (pc);
+ if (!s)
+ {
+ val.symtab = 0;
+ val.line = 0;
+ val.pc = pc;
+ val.end = 0;
+ return val;
+ }
+
+ bv = BLOCKVECTOR (s);
+
+ /* Look at all the symtabs that share this blockvector.
+ They all have the same apriori range, that we found was right;
+ but they have different line tables. */
+
+ for (; s && BLOCKVECTOR (s) == bv; s = s->next)
+ {
+ /* Find the best line in this symtab. */
+ l = LINETABLE (s);
+ if (!l)
+ continue;
+ len = l->nitems;
+ if (len <= 0)
+ {
+ /* I think len can be zero if the symtab lacks line numbers
+ (e.g. gcc -g1). (Either that or the LINETABLE is NULL;
+ I'm not sure which, and maybe it depends on the symbol
+ reader). */
+ continue;
+ }
+
+ prev = NULL;
+ item = l->item; /* Get first line info */
+
+ /* Is this file's first line closer than the first lines of other files?
+ If so, record this file, and its first line, as best alternate. */
+ if (item->pc > pc && (!alt || item->pc < alt->pc))
+ {
+ alt = item;
+ alt_symtab = s;
+ }
+
+ for (i = 0; i < len; i++, item++)
+ {
+ /* Return the last line that did not start after PC. */
+ if (item->pc > pc)
+ break;
+
+ prev = item;
+ }
+
+ /* At this point, prev points at the line whose start addr is <= pc, and
+ item points at the next line. If we ran off the end of the linetable
+ (pc >= start of the last line), then prev == item. If pc < start of
+ the first line, prev will not be set. */
+
+ /* Is this file's best line closer than the best in the other files?
+ If so, record this file, and its best line, as best so far. */
+
+ if (prev && (!best || prev->pc > best->pc))
+ {
+ best = prev;
+ best_symtab = s;
+ /* If another line is in the linetable, and its PC is closer
+ than the best_end we currently have, take it as best_end. */
+ if (i < len && (best_end == 0 || best_end > item->pc))
+ best_end = item->pc;
+ }
+ }
+
+ if (!best_symtab)
+ {
+ if (!alt_symtab)
+ { /* If we didn't find any line # info, just
+ return zeros. */
+ val.symtab = 0;
+ val.line = 0;
+ val.pc = pc;
+ val.end = 0;
+ }
+ else
+ {
+ val.symtab = alt_symtab;
+ val.line = alt->line - 1;
+ val.pc = BLOCK_END (BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK));
+ val.end = alt->pc;
+ }
+ }
+ else
+ {
+ val.symtab = best_symtab;
+ val.line = best->line;
+ val.pc = best->pc;
+ if (best_end && (!alt || best_end < alt->pc))
+ val.end = best_end;
+ else if (alt)
+ val.end = alt->pc;
+ else
+ val.end = BLOCK_END (BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK));
+ }
+ return val;
+}
+
+static int find_line_symtab PARAMS ((struct symtab *, int, struct linetable **,
+ int *, int *));
+
+/* Find line number LINE in any symtab whose name is the same as
+ SYMTAB.
+
+ If found, return 1, set *LINETABLE to the linetable in which it was
+ found, set *INDEX to the index in the linetable of the best entry
+ found, and set *EXACT_MATCH nonzero if the value returned is an
+ exact match.
+
+ If not found, return 0. */
+
+static int
+find_line_symtab (symtab, line, linetable, index, exact_match)
+ struct symtab *symtab;
+ int line;
+ struct linetable **linetable;
+ int *index;
+ int *exact_match;
+{
+ int exact;
+
+ /* BEST_INDEX and BEST_LINETABLE identify the smallest linenumber > LINE
+ so far seen. */
+
+ int best_index;
+ struct linetable *best_linetable;
+
+ /* First try looking it up in the given symtab. */
+ best_linetable = LINETABLE (symtab);
+ best_index = find_line_common (best_linetable, line, &exact);
+ if (best_index < 0 || !exact)
+ {
+ /* Didn't find an exact match. So we better keep looking for
+ another symtab with the same name. In the case of xcoff,
+ multiple csects for one source file (produced by IBM's FORTRAN
+ compiler) produce multiple symtabs (this is unavoidable
+ assuming csects can be at arbitrary places in memory and that
+ the GLOBAL_BLOCK of a symtab has a begin and end address). */
+
+ /* BEST is the smallest linenumber > LINE so far seen,
+ or 0 if none has been seen so far.
+ BEST_INDEX and BEST_LINETABLE identify the item for it. */
+ int best;
+
+ struct objfile *objfile;
+ struct symtab *s;
+
+ if (best_index >= 0)
+ best = best_linetable->item[best_index].line;
+ else
+ best = 0;
+
+ ALL_SYMTABS (objfile, s)
+ {
+ struct linetable *l;
+ int ind;
+
+ if (!STREQ (symtab->filename, s->filename))
+ continue;
+ l = LINETABLE (s);
+ ind = find_line_common (l, line, &exact);
+ if (ind >= 0)
+ {
+ if (exact)
+ {
+ best_index = ind;
+ best_linetable = l;
+ goto done;
+ }
+ if (best == 0 || l->item[ind].line < best)
+ {
+ best = l->item[ind].line;
+ best_index = ind;
+ best_linetable = l;
+ }
+ }
+ }
+ }
+ done:
+ if (best_index < 0)
+ return 0;
+
+ if (index)
+ *index = best_index;
+ if (linetable)
+ *linetable = best_linetable;
+ if (exact_match)
+ *exact_match = exact;
+ return 1;
+}
+
+/* Find the PC value for a given source file and line number.
+ Returns zero for invalid line number.
+ The source file is specified with a struct symtab. */
+
+CORE_ADDR
+find_line_pc (symtab, line)
+ struct symtab *symtab;
+ int line;
+{
+ struct linetable *l;
+ int ind;
+
+ if (symtab == 0)
+ return 0;
+ if (find_line_symtab (symtab, line, &l, &ind, NULL))
+ return l->item[ind].pc;
+ else
+ return 0;
+}
+
+/* Find the range of pc values in a line.
+ Store the starting pc of the line into *STARTPTR
+ and the ending pc (start of next line) into *ENDPTR.
+ Returns 1 to indicate success.
+ Returns 0 if could not find the specified line. */
+
+int
+find_line_pc_range (symtab, thisline, startptr, endptr)
+ struct symtab *symtab;
+ int thisline;
+ CORE_ADDR *startptr, *endptr;
+{
+ struct linetable *l;
+ int ind;
+ int exact_match; /* did we get an exact linenumber match */
+
+ if (symtab == 0)
+ return 0;
+
+ if (find_line_symtab (symtab, thisline, &l, &ind, &exact_match))
+ {
+ *startptr = l->item[ind].pc;
+ /* If we have not seen an entry for the specified line,
+ assume that means the specified line has zero bytes. */
+ if (!exact_match || ind == l->nitems-1)
+ *endptr = *startptr;
+ else
+ /* Perhaps the following entry is for the following line.
+ It's worth a try. */
+ if (ind+1 < l->nitems
+ && l->item[ind+1].line == thisline + 1)
+ *endptr = l->item[ind+1].pc;
+ else
+ *endptr = find_line_pc (symtab, thisline+1);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Given a line table and a line number, return the index into the line
+ table for the pc of the nearest line whose number is >= the specified one.
+ Return -1 if none is found. The value is >= 0 if it is an index.
+
+ Set *EXACT_MATCH nonzero if the value returned is an exact match. */
+
+static int
+find_line_common (l, lineno, exact_match)
+ register struct linetable *l;
+ register int lineno;
+ int *exact_match;
+{
+ register int i;
+ register int len;
+
+ /* BEST is the smallest linenumber > LINENO so far seen,
+ or 0 if none has been seen so far.
+ BEST_INDEX identifies the item for it. */
+
+ int best_index = -1;
+ int best = 0;
+
+ if (lineno <= 0)
+ return -1;
+ if (l == 0)
+ return -1;
+
+ len = l->nitems;
+ for (i = 0; i < len; i++)
+ {
+ register struct linetable_entry *item = &(l->item[i]);
+
+ if (item->line == lineno)
+ {
+ /* Return the first (lowest address) entry which matches. */
+ *exact_match = 1;
+ return i;
+ }
+
+ if (item->line > lineno && (best == 0 || item->line < best))
+ {
+ best = item->line;
+ best_index = i;
+ }
+ }
+
+ /* If we got here, we didn't get an exact match. */
+
+ *exact_match = 0;
+ return best_index;
+}
+
+int
+find_pc_line_pc_range (pc, startptr, endptr)
+ CORE_ADDR pc;
+ CORE_ADDR *startptr, *endptr;
+{
+ struct symtab_and_line sal;
+ sal = find_pc_line (pc, 0);
+ *startptr = sal.pc;
+ *endptr = sal.end;
+ return sal.symtab != 0;
+}
+
+/* If P is of the form "operator[ \t]+..." where `...' is
+ some legitimate operator text, return a pointer to the
+ beginning of the substring of the operator text.
+ Otherwise, return "". */
+static char *
+operator_chars (p, end)
+ char *p;
+ char **end;
+{
+ *end = "";
+ if (strncmp (p, "operator", 8))
+ return *end;
+ p += 8;
+
+ /* Don't get faked out by `operator' being part of a longer
+ identifier. */
+ if (isalpha(*p) || *p == '_' || *p == '$' || *p == '\0')
+ return *end;
+
+ /* Allow some whitespace between `operator' and the operator symbol. */
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ /* Recognize 'operator TYPENAME'. */
+
+ if (isalpha(*p) || *p == '_' || *p == '$')
+ {
+ register char *q = p+1;
+ while (isalnum(*q) || *q == '_' || *q == '$')
+ q++;
+ *end = q;
+ return p;
+ }
+
+ switch (*p)
+ {
+ case '!':
+ case '=':
+ case '*':
+ case '/':
+ case '%':
+ case '^':
+ if (p[1] == '=')
+ *end = p+2;
+ else
+ *end = p+1;
+ return p;
+ case '<':
+ case '>':
+ case '+':
+ case '-':
+ case '&':
+ case '|':
+ if (p[1] == '=' || p[1] == p[0])
+ *end = p+2;
+ else
+ *end = p+1;
+ return p;
+ case '~':
+ case ',':
+ *end = p+1;
+ return p;
+ case '(':
+ if (p[1] != ')')
+ error ("`operator ()' must be specified without whitespace in `()'");
+ *end = p+2;
+ return p;
+ case '?':
+ if (p[1] != ':')
+ error ("`operator ?:' must be specified without whitespace in `?:'");
+ *end = p+2;
+ return p;
+ case '[':
+ if (p[1] != ']')
+ error ("`operator []' must be specified without whitespace in `[]'");
+ *end = p+2;
+ return p;
+ default:
+ error ("`operator %s' not supported", p);
+ break;
+ }
+ *end = "";
+ return *end;
+}
+
+/* Recursive helper function for decode_line_1.
+ * Look for methods named NAME in type T.
+ * Return number of matches.
+ * Put matches in SYM_ARR (which better be big enough!).
+ * These allocations seem to define "big enough":
+ * sym_arr = (struct symbol **) alloca(TYPE_NFN_FIELDS_TOTAL (t) * sizeof(struct symbol*));
+ * Note that this function is g++ specific.
+ */
+
+int
+find_methods (t, name, sym_arr)
+ struct type *t;
+ char *name;
+ struct symbol **sym_arr;
+{
+ int i1 = 0;
+ int ibase;
+ struct symbol *sym_class;
+ char *class_name = type_name_no_tag (t);
+ /* Ignore this class if it doesn't have a name. This is ugly, but
+ unless we figure out how to get the physname without the name of
+ the class, then the loop can't do any good. */
+ if (class_name
+ && (sym_class = lookup_symbol (class_name,
+ (struct block *)NULL,
+ STRUCT_NAMESPACE,
+ (int *)NULL,
+ (struct symtab **)NULL)))
+ {
+ int method_counter;
+ /* FIXME: Shouldn't this just be check_stub_type (t)? */
+ t = SYMBOL_TYPE (sym_class);
+ for (method_counter = TYPE_NFN_FIELDS (t) - 1;
+ method_counter >= 0;
+ --method_counter)
+ {
+ int field_counter;
+ struct fn_field *f = TYPE_FN_FIELDLIST1 (t, method_counter);
+
+ char *method_name = TYPE_FN_FIELDLIST_NAME (t, method_counter);
+ if (STREQ (name, method_name))
+ /* Find all the fields with that name. */
+ for (field_counter = TYPE_FN_FIELDLIST_LENGTH (t, method_counter) - 1;
+ field_counter >= 0;
+ --field_counter)
+ {
+ char *phys_name;
+ if (TYPE_FN_FIELD_STUB (f, field_counter))
+ check_stub_method (t, method_counter, field_counter);
+ phys_name = TYPE_FN_FIELD_PHYSNAME (f, field_counter);
+ /* Destructor is handled by caller, dont add it to the list */
+ if (DESTRUCTOR_PREFIX_P (phys_name))
+ continue;
+
+ /* FIXME: Why are we looking this up in the
+ SYMBOL_BLOCK_VALUE (sym_class)? It is intended as a hook
+ for nested types? If so, it should probably hook to the
+ type, not the symbol. mipsread.c is the only symbol
+ reader which sets the SYMBOL_BLOCK_VALUE for types, and
+ this is not documented in symtab.h. -26Aug93. */
+
+ sym_arr[i1] = lookup_symbol (phys_name,
+ SYMBOL_BLOCK_VALUE (sym_class),
+ VAR_NAMESPACE,
+ (int *) NULL,
+ (struct symtab **) NULL);
+ if (sym_arr[i1]) i1++;
+ else
+ {
+ fputs_filtered("(Cannot find method ", stdout);
+ fprintf_symbol_filtered (stdout, phys_name,
+ language_cplus, DMGL_PARAMS);
+ fputs_filtered(" - possibly inlined.)\n", stdout);
+ }
+ }
+ }
+ }
+
+ /* Only search baseclasses if there is no match yet, since names in
+ derived classes override those in baseclasses.
+
+ FIXME: The above is not true; it is only true of member functions
+ if they have the same number of arguments (??? - section 13.1 of the
+ ARM says the function members are not in the same scope but doesn't
+ really spell out the rules in a way I understand. In any case, if
+ the number of arguments differ this is a case in which we can overload
+ rather than hiding without any problem, and gcc 2.4.5 does overload
+ rather than hiding in this case). */
+
+ if (i1)
+ return i1;
+ for (ibase = 0; ibase < TYPE_N_BASECLASSES (t); ibase++)
+ i1 += find_methods(TYPE_BASECLASS(t, ibase), name,
+ sym_arr + i1);
+ return i1;
+}
+
+/* Helper function for decode_line_1.
+ Build a canonical line spec in CANONICAL if it is non-NULL and if
+ the SAL has a symtab.
+ If SYMNAME is non-NULL the canonical line spec is `filename:symname'.
+ If SYMNAME is NULL the line number from SAL is used and the canonical
+ line spec is `filename:linenum'. */
+
+static void
+build_canonical_line_spec (sal, symname, canonical)
+ struct symtab_and_line *sal;
+ char *symname;
+ char ***canonical;
+{
+ char **canonical_arr;
+ char *canonical_name;
+ char *filename;
+ struct symtab *s = sal->symtab;
+
+ if (s == (struct symtab *)NULL
+ || s->filename == (char *)NULL
+ || canonical == (char ***)NULL)
+ return;
+
+ canonical_arr = (char **) xmalloc (sizeof (char *));
+ *canonical = canonical_arr;
+
+ filename = s->filename;
+ if (symname != NULL)
+ {
+ canonical_name = xmalloc (strlen (filename) + strlen (symname) + 2);
+ sprintf (canonical_name, "%s:%s", filename, symname);
+ }
+ else
+ {
+ canonical_name = xmalloc (strlen (filename) + 30);
+ sprintf (canonical_name, "%s:%d", filename, sal->line);
+ }
+ canonical_arr[0] = canonical_name;
+}
+
+/* Parse a string that specifies a line number.
+ Pass the address of a char * variable; that variable will be
+ advanced over the characters actually parsed.
+
+ The string can be:
+
+ LINENUM -- that line number in current file. PC returned is 0.
+ FILE:LINENUM -- that line in that file. PC returned is 0.
+ FUNCTION -- line number of openbrace of that function.
+ PC returned is the start of the function.
+ VARIABLE -- line number of definition of that variable.
+ PC returned is 0.
+ FILE:FUNCTION -- likewise, but prefer functions in that file.
+ *EXPR -- line in which address EXPR appears.
+
+ FUNCTION may be an undebuggable function found in minimal symbol table.
+
+ If the argument FUNFIRSTLINE is nonzero, we want the first line
+ of real code inside a function when a function is specified.
+
+ DEFAULT_SYMTAB specifies the file to use if none is specified.
+ It defaults to current_source_symtab.
+ DEFAULT_LINE specifies the line number to use for relative
+ line numbers (that start with signs). Defaults to current_source_line.
+ If CANONICAL is non-NULL, store an array of strings containing the canonical
+ line specs there if necessary. Currently overloaded member functions and
+ line numbers or static functions without a filename yield a canonical
+ line spec. The array and the line spec strings are allocated on the heap,
+ it is the callers responsibility to free them.
+
+ Note that it is possible to return zero for the symtab
+ if no file is validly specified. Callers must check that.
+ Also, the line number returned may be invalid. */
+
+struct symtabs_and_lines
+decode_line_1 (argptr, funfirstline, default_symtab, default_line, canonical)
+ char **argptr;
+ int funfirstline;
+ struct symtab *default_symtab;
+ int default_line;
+ char ***canonical;
+{
+ struct symtabs_and_lines values;
+#ifdef HPPA_COMPILER_BUG
+ /* FIXME: The native HP 9000/700 compiler has a bug which appears
+ when optimizing this file with target i960-vxworks. I haven't
+ been able to construct a simple test case. The problem is that
+ in the second call to SKIP_PROLOGUE below, the compiler somehow
+ does not realize that the statement val = find_pc_line (...) will
+ change the values of the fields of val. It extracts the elements
+ into registers at the top of the block, and does not update the
+ registers after the call to find_pc_line. You can check this by
+ inserting a printf at the end of find_pc_line to show what values
+ it is returning for val.pc and val.end and another printf after
+ the call to see what values the function actually got (remember,
+ this is compiling with cc -O, with this patch removed). You can
+ also examine the assembly listing: search for the second call to
+ skip_prologue; the LDO statement before the next call to
+ find_pc_line loads the address of the structure which
+ find_pc_line will return; if there is a LDW just before the LDO,
+ which fetches an element of the structure, then the compiler
+ still has the bug.
+
+ Setting val to volatile avoids the problem. We must undef
+ volatile, because the HPPA native compiler does not define
+ __STDC__, although it does understand volatile, and so volatile
+ will have been defined away in defs.h. */
+#undef volatile
+ volatile struct symtab_and_line val;
+#define volatile /*nothing*/
+#else
+ struct symtab_and_line val;
+#endif
+ register char *p, *p1;
+ char *q, *q1;
+ register struct symtab *s;
+
+ register struct symbol *sym;
+ /* The symtab that SYM was found in. */
+ struct symtab *sym_symtab;
+
+ register CORE_ADDR pc;
+ register struct minimal_symbol *msymbol;
+ char *copy;
+ struct symbol *sym_class;
+ int i1;
+ int is_quoted;
+ struct symbol **sym_arr;
+ struct type *t;
+ char *saved_arg = *argptr;
+ extern char *gdb_completer_quote_characters;
+
+ /* Defaults have defaults. */
+
+ if (default_symtab == 0)
+ {
+ default_symtab = current_source_symtab;
+ default_line = current_source_line;
+ }
+
+ /* See if arg is *PC */
+
+ if (**argptr == '*')
+ {
+ if (**argptr == '*')
+ {
+ (*argptr)++;
+ }
+ pc = parse_and_eval_address_1 (argptr);
+ values.sals = (struct symtab_and_line *)
+ xmalloc (sizeof (struct symtab_and_line));
+ values.nelts = 1;
+ values.sals[0] = find_pc_line (pc, 0);
+ values.sals[0].pc = pc;
+ build_canonical_line_spec (values.sals, NULL, canonical);
+ return values;
+ }
+
+ /* Maybe arg is FILE : LINENUM or FILE : FUNCTION */
+
+ s = NULL;
+ is_quoted = (strchr (gdb_completer_quote_characters, **argptr) != NULL);
+
+ for (p = *argptr; *p; p++)
+ {
+ if (p[0] == ':' || p[0] == ' ' || p[0] == '\t')
+ break;
+ }
+ while (p[0] == ' ' || p[0] == '\t') p++;
+
+ if ((p[0] == ':') && !is_quoted)
+ {
+
+ /* C++ */
+ if (p[1] ==':')
+ {
+ /* Extract the class name. */
+ p1 = p;
+ while (p != *argptr && p[-1] == ' ') --p;
+ copy = (char *) alloca (p - *argptr + 1);
+ memcpy (copy, *argptr, p - *argptr);
+ copy[p - *argptr] = 0;
+
+ /* Discard the class name from the arg. */
+ p = p1 + 2;
+ while (*p == ' ' || *p == '\t') p++;
+ *argptr = p;
+
+ sym_class = lookup_symbol (copy, 0, STRUCT_NAMESPACE, 0,
+ (struct symtab **)NULL);
+
+ if (sym_class &&
+ ( TYPE_CODE (SYMBOL_TYPE (sym_class)) == TYPE_CODE_STRUCT
+ || TYPE_CODE (SYMBOL_TYPE (sym_class)) == TYPE_CODE_UNION))
+ {
+ /* Arg token is not digits => try it as a function name
+ Find the next token (everything up to end or next whitespace). */
+ p = *argptr;
+ while (*p && *p != ' ' && *p != '\t' && *p != ',' && *p !=':') p++;
+ q = operator_chars (*argptr, &q1);
+
+ if (q1 - q)
+ {
+ char *opname;
+ char *tmp = alloca (q1 - q + 1);
+ memcpy (tmp, q, q1 - q);
+ tmp[q1 - q] = '\0';
+ opname = cplus_mangle_opname (tmp, DMGL_ANSI);
+ if (opname == NULL)
+ {
+ warning ("no mangling for \"%s\"", tmp);
+ cplusplus_hint (saved_arg);
+ return_to_top_level (RETURN_ERROR);
+ }
+ copy = (char*) alloca (3 + strlen(opname));
+ sprintf (copy, "__%s", opname);
+ p = q1;
+ }
+ else
+ {
+ copy = (char *) alloca (p - *argptr + 1 + (q1 - q));
+ memcpy (copy, *argptr, p - *argptr);
+ copy[p - *argptr] = '\0';
+ }
+
+ /* no line number may be specified */
+ while (*p == ' ' || *p == '\t') p++;
+ *argptr = p;
+
+ sym = 0;
+ i1 = 0; /* counter for the symbol array */
+ t = SYMBOL_TYPE (sym_class);
+ sym_arr = (struct symbol **) alloca(TYPE_NFN_FIELDS_TOTAL (t) * sizeof(struct symbol*));
+
+ /* Cfront objects don't have fieldlists. */
+ if (destructor_name_p (copy, t) && TYPE_FN_FIELDLISTS (t) != NULL)
+ {
+ /* destructors are a special case. */
+ struct fn_field *f = TYPE_FN_FIELDLIST1 (t, 0);
+ int len = TYPE_FN_FIELDLIST_LENGTH (t, 0) - 1;
+ /* gcc 1.x puts destructor in last field,
+ gcc 2.x puts destructor in first field. */
+ char *phys_name = TYPE_FN_FIELD_PHYSNAME (f, len);
+ if (!DESTRUCTOR_PREFIX_P (phys_name))
+ {
+ phys_name = TYPE_FN_FIELD_PHYSNAME (f, 0);
+ if (!DESTRUCTOR_PREFIX_P (phys_name))
+ phys_name = "";
+ }
+ sym_arr[i1] =
+ lookup_symbol (phys_name, SYMBOL_BLOCK_VALUE (sym_class),
+ VAR_NAMESPACE, 0, (struct symtab **)NULL);
+ if (sym_arr[i1]) i1++;
+ }
+ else
+ i1 = find_methods (t, copy, sym_arr);
+ if (i1 == 1)
+ {
+ /* There is exactly one field with that name. */
+ sym = sym_arr[0];
+
+ if (sym && SYMBOL_CLASS (sym) == LOC_BLOCK)
+ {
+ /* Arg is the name of a function */
+ pc = BLOCK_START (SYMBOL_BLOCK_VALUE (sym)) + FUNCTION_START_OFFSET;
+ if (funfirstline)
+ SKIP_PROLOGUE (pc);
+ values.sals = (struct symtab_and_line *)xmalloc (sizeof (struct symtab_and_line));
+ values.nelts = 1;
+ values.sals[0] = find_pc_line (pc, 0);
+ values.sals[0].pc = (values.sals[0].end && values.sals[0].pc != pc) ? values.sals[0].end : pc;
+ }
+ else
+ {
+ values.nelts = 0;
+ }
+ return values;
+ }
+ if (i1 > 0)
+ {
+ /* There is more than one field with that name
+ (overloaded). Ask the user which one to use. */
+ return decode_line_2 (sym_arr, i1, funfirstline, canonical);
+ }
+ else
+ {
+ char *tmp;
+
+ if (OPNAME_PREFIX_P (copy))
+ {
+ tmp = (char *)alloca (strlen (copy+3) + 9);
+ strcpy (tmp, "operator ");
+ strcat (tmp, copy+3);
+ }
+ else
+ tmp = copy;
+ if (tmp[0] == '~')
+ warning ("the class `%s' does not have destructor defined",
+ SYMBOL_SOURCE_NAME(sym_class));
+ else
+ warning ("the class %s does not have any method named %s",
+ SYMBOL_SOURCE_NAME(sym_class), tmp);
+ cplusplus_hint (saved_arg);
+ return_to_top_level (RETURN_ERROR);
+ }
+ }
+ else
+ {
+ /* The quotes are important if copy is empty. */
+ warning ("can't find class, struct, or union named \"%s\"",
+ copy);
+ cplusplus_hint (saved_arg);
+ return_to_top_level (RETURN_ERROR);
+ }
+ }
+ /* end of C++ */
+
+
+ /* Extract the file name. */
+ p1 = p;
+ while (p != *argptr && p[-1] == ' ') --p;
+ copy = (char *) alloca (p - *argptr + 1);
+ memcpy (copy, *argptr, p - *argptr);
+ copy[p - *argptr] = 0;
+
+ /* Find that file's data. */
+ s = lookup_symtab (copy);
+ if (s == 0)
+ {
+ if (!have_full_symbols () && !have_partial_symbols ())
+ error (no_symtab_msg);
+ error ("No source file named %s.", copy);
+ }
+
+ /* Discard the file name from the arg. */
+ p = p1 + 1;
+ while (*p == ' ' || *p == '\t') p++;
+ *argptr = p;
+ }
+
+ /* S is specified file's symtab, or 0 if no file specified.
+ arg no longer contains the file name. */
+
+ /* Check whether arg is all digits (and sign) */
+
+ p = *argptr;
+ if (*p == '-' || *p == '+') p++;
+ while (*p >= '0' && *p <= '9')
+ p++;
+
+ if (p != *argptr && (*p == 0 || *p == ' ' || *p == '\t' || *p == ','))
+ {
+ /* We found a token consisting of all digits -- at least one digit. */
+ enum sign {none, plus, minus} sign = none;
+
+ /* We might need a canonical line spec if no file was specified. */
+ int need_canonical = (s == 0) ? 1 : 0;
+
+ /* This is where we need to make sure that we have good defaults.
+ We must guarantee that this section of code is never executed
+ when we are called with just a function name, since
+ select_source_symtab calls us with such an argument */
+
+ if (s == 0 && default_symtab == 0)
+ {
+ select_source_symtab (0);
+ default_symtab = current_source_symtab;
+ default_line = current_source_line;
+ }
+
+ if (**argptr == '+')
+ sign = plus, (*argptr)++;
+ else if (**argptr == '-')
+ sign = minus, (*argptr)++;
+ val.line = atoi (*argptr);
+ switch (sign)
+ {
+ case plus:
+ if (p == *argptr)
+ val.line = 5;
+ if (s == 0)
+ val.line = default_line + val.line;
+ break;
+ case minus:
+ if (p == *argptr)
+ val.line = 15;
+ if (s == 0)
+ val.line = default_line - val.line;
+ else
+ val.line = 1;
+ break;
+ case none:
+ break; /* No need to adjust val.line. */
+ }
+
+ while (*p == ' ' || *p == '\t') p++;
+ *argptr = p;
+ if (s == 0)
+ s = default_symtab;
+ val.symtab = s;
+ val.pc = 0;
+ values.sals = (struct symtab_and_line *)xmalloc (sizeof (struct symtab_and_line));
+ values.sals[0] = val;
+ values.nelts = 1;
+ if (need_canonical)
+ build_canonical_line_spec (values.sals, NULL, canonical);
+ return values;
+ }
+
+ /* Arg token is not digits => try it as a variable name
+ Find the next token (everything up to end or next whitespace). */
+
+ p = skip_quoted (*argptr);
+ if (is_quoted && p[-1] != '\'')
+ error ("Unmatched single quote.");
+ copy = (char *) alloca (p - *argptr + 1);
+ memcpy (copy, *argptr, p - *argptr);
+ copy[p - *argptr] = '\0';
+ if ((copy[0] == copy [p - *argptr - 1])
+ && strchr (gdb_completer_quote_characters, copy[0]) != NULL)
+ {
+ copy [p - *argptr - 1] = '\0';
+ copy++;
+ }
+ while (*p == ' ' || *p == '\t') p++;
+ *argptr = p;
+
+ /* Look up that token as a variable.
+ If file specified, use that file's per-file block to start with. */
+
+ sym = lookup_symbol (copy,
+ (s ? BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), STATIC_BLOCK)
+ : get_selected_block ()),
+ VAR_NAMESPACE, 0, &sym_symtab);
+
+ if (sym != NULL)
+ {
+ if (SYMBOL_CLASS (sym) == LOC_BLOCK)
+ {
+ /* Arg is the name of a function */
+ pc = BLOCK_START (SYMBOL_BLOCK_VALUE (sym)) + FUNCTION_START_OFFSET;
+ if (funfirstline)
+ SKIP_PROLOGUE (pc);
+ val = find_pc_line (pc, 0);
+#ifdef PROLOGUE_FIRSTLINE_OVERLAP
+ /* Convex: no need to suppress code on first line, if any */
+ val.pc = pc;
+#else
+ /* Check if SKIP_PROLOGUE left us in mid-line, and the next
+ line is still part of the same function. */
+ if (val.pc != pc
+ && BLOCK_START (SYMBOL_BLOCK_VALUE (sym)) <= val.end
+ && val.end < BLOCK_END (SYMBOL_BLOCK_VALUE (sym)))
+ {
+ /* First pc of next line */
+ pc = val.end;
+ /* Recalculate the line number (might not be N+1). */
+ val = find_pc_line (pc, 0);
+ }
+ val.pc = pc;
+#endif
+ values.sals = (struct symtab_and_line *)xmalloc (sizeof (struct symtab_and_line));
+ values.sals[0] = val;
+ values.nelts = 1;
+
+ /* I think this is always the same as the line that
+ we calculate above, but the general principle is
+ "trust the symbols more than stuff like
+ SKIP_PROLOGUE". */
+ if (SYMBOL_LINE (sym) != 0)
+ values.sals[0].line = SYMBOL_LINE (sym);
+
+ /* We might need a canonical line spec if it is a static function. */
+ if (s == 0)
+ {
+ struct blockvector *bv = BLOCKVECTOR (sym_symtab);
+ struct block *b = BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK);
+ if (lookup_block_symbol (b, copy, VAR_NAMESPACE) != NULL)
+ build_canonical_line_spec (values.sals, copy, canonical);
+ }
+ return values;
+ }
+ else if (SYMBOL_LINE (sym) != 0)
+ {
+ /* We know its line number. */
+ values.sals = (struct symtab_and_line *)
+ xmalloc (sizeof (struct symtab_and_line));
+ values.nelts = 1;
+ memset (&values.sals[0], 0, sizeof (values.sals[0]));
+ values.sals[0].symtab = sym_symtab;
+ values.sals[0].line = SYMBOL_LINE (sym);
+ return values;
+ }
+ else
+ /* This can happen if it is compiled with a compiler which doesn't
+ put out line numbers for variables. */
+ /* FIXME: Shouldn't we just set .line and .symtab to zero and
+ return? For example, "info line foo" could print the address. */
+ error ("Line number not known for symbol \"%s\"", copy);
+ }
+
+ msymbol = lookup_minimal_symbol (copy, (struct objfile *) NULL);
+ if (msymbol != NULL)
+ {
+ val.symtab = 0;
+ val.line = 0;
+ val.pc = SYMBOL_VALUE_ADDRESS (msymbol) + FUNCTION_START_OFFSET;
+ if (funfirstline)
+ SKIP_PROLOGUE (val.pc);
+ values.sals = (struct symtab_and_line *)xmalloc (sizeof (struct symtab_and_line));
+ values.sals[0] = val;
+ values.nelts = 1;
+ return values;
+ }
+
+ if (!have_full_symbols () &&
+ !have_partial_symbols () && !have_minimal_symbols ())
+ error (no_symtab_msg);
+
+ error ("Function \"%s\" not defined.", copy);
+ return values; /* for lint */
+}
+
+struct symtabs_and_lines
+decode_line_spec (string, funfirstline)
+ char *string;
+ int funfirstline;
+{
+ struct symtabs_and_lines sals;
+ if (string == 0)
+ error ("Empty line specification.");
+ sals = decode_line_1 (&string, funfirstline,
+ current_source_symtab, current_source_line,
+ (char ***)NULL);
+ if (*string)
+ error ("Junk at end of line specification: %s", string);
+ return sals;
+}
+
+/* Given a list of NELTS symbols in SYM_ARR, return a list of lines to
+ operate on (ask user if necessary).
+ If CANONICAL is non-NULL return a corresponding array of mangled names
+ as canonical line specs there. */
+
+static struct symtabs_and_lines
+decode_line_2 (sym_arr, nelts, funfirstline, canonical)
+ struct symbol *sym_arr[];
+ int nelts;
+ int funfirstline;
+ char ***canonical;
+{
+ struct symtabs_and_lines values, return_values;
+ register CORE_ADDR pc;
+ char *args, *arg1;
+ int i;
+ char *prompt;
+ char *symname;
+ struct cleanup *old_chain;
+ char **canonical_arr = (char **)NULL;
+
+ values.sals = (struct symtab_and_line *) alloca (nelts * sizeof(struct symtab_and_line));
+ return_values.sals = (struct symtab_and_line *) xmalloc (nelts * sizeof(struct symtab_and_line));
+ old_chain = make_cleanup (free, return_values.sals);
+
+ if (canonical)
+ {
+ canonical_arr = (char **) xmalloc (nelts * sizeof (char *));
+ make_cleanup (free, canonical_arr);
+ memset (canonical_arr, 0, nelts * sizeof (char *));
+ *canonical = canonical_arr;
+ }
+
+ i = 0;
+ printf("[0] cancel\n[1] all\n");
+ while (i < nelts)
+ {
+ if (sym_arr[i] && SYMBOL_CLASS (sym_arr[i]) == LOC_BLOCK)
+ {
+ /* Arg is the name of a function */
+ pc = BLOCK_START (SYMBOL_BLOCK_VALUE (sym_arr[i]))
+ + FUNCTION_START_OFFSET;
+ if (funfirstline)
+ SKIP_PROLOGUE (pc);
+ values.sals[i] = find_pc_line (pc, 0);
+ values.sals[i].pc = (values.sals[i].end && values.sals[i].pc != pc) ?
+ values.sals[i].end : pc;
+ printf("[%d] %s at %s:%d\n", (i+2), SYMBOL_SOURCE_NAME (sym_arr[i]),
+ values.sals[i].symtab->filename, values.sals[i].line);
+ }
+ else printf ("?HERE\n");
+ i++;
+ }
+
+ if ((prompt = getenv ("PS2")) == NULL)
+ {
+ prompt = ">";
+ }
+ printf("%s ",prompt);
+ fflush(stdout);
+
+ args = command_line_input ((char *) NULL, 0);
+
+ if (args == 0 || *args == 0)
+ error_no_arg ("one or more choice numbers");
+
+ i = 0;
+ while (*args)
+ {
+ int num;
+
+ arg1 = args;
+ while (*arg1 >= '0' && *arg1 <= '9') arg1++;
+ if (*arg1 && *arg1 != ' ' && *arg1 != '\t')
+ error ("Arguments must be choice numbers.");
+
+ num = atoi (args);
+
+ if (num == 0)
+ error ("cancelled");
+ else if (num == 1)
+ {
+ if (canonical_arr)
+ {
+ for (i = 0; i < nelts; i++)
+ {
+ if (canonical_arr[i] == NULL)
+ {
+ symname = SYMBOL_NAME (sym_arr[i]);
+ canonical_arr[i] = savestring (symname, strlen (symname));
+ }
+ }
+ }
+ memcpy (return_values.sals, values.sals,
+ (nelts * sizeof(struct symtab_and_line)));
+ return_values.nelts = nelts;
+ discard_cleanups (old_chain);
+ return return_values;
+ }
+
+ if (num > nelts + 2)
+ {
+ printf ("No choice number %d.\n", num);
+ }
+ else
+ {
+ num -= 2;
+ if (values.sals[num].pc)
+ {
+ if (canonical_arr)
+ {
+ symname = SYMBOL_NAME (sym_arr[num]);
+ make_cleanup (free, symname);
+ canonical_arr[i] = savestring (symname, strlen (symname));
+ }
+ return_values.sals[i++] = values.sals[num];
+ values.sals[num].pc = 0;
+ }
+ else
+ {
+ printf ("duplicate request for %d ignored.\n", num);
+ }
+ }
+
+ args = arg1;
+ while (*args == ' ' || *args == '\t') args++;
+ }
+ return_values.nelts = i;
+ discard_cleanups (old_chain);
+ return return_values;
+}
+
+
+/* Slave routine for sources_info. Force line breaks at ,'s.
+ NAME is the name to print and *FIRST is nonzero if this is the first
+ name printed. Set *FIRST to zero. */
+static void
+output_source_filename (name, first)
+ char *name;
+ int *first;
+{
+ /* Table of files printed so far. Since a single source file can
+ result in several partial symbol tables, we need to avoid printing
+ it more than once. Note: if some of the psymtabs are read in and
+ some are not, it gets printed both under "Source files for which
+ symbols have been read" and "Source files for which symbols will
+ be read in on demand". I consider this a reasonable way to deal
+ with the situation. I'm not sure whether this can also happen for
+ symtabs; it doesn't hurt to check. */
+ static char **tab = NULL;
+ /* Allocated size of tab in elements.
+ Start with one 256-byte block (when using GNU malloc.c).
+ 24 is the malloc overhead when range checking is in effect. */
+ static int tab_alloc_size = (256 - 24) / sizeof (char *);
+ /* Current size of tab in elements. */
+ static int tab_cur_size;
+
+ char **p;
+
+ if (*first)
+ {
+ if (tab == NULL)
+ tab = (char **) xmalloc (tab_alloc_size * sizeof (*tab));
+ tab_cur_size = 0;
+ }
+
+ /* Is NAME in tab? */
+ for (p = tab; p < tab + tab_cur_size; p++)
+ if (STREQ (*p, name))
+ /* Yes; don't print it again. */
+ return;
+ /* No; add it to tab. */
+ if (tab_cur_size == tab_alloc_size)
+ {
+ tab_alloc_size *= 2;
+ tab = (char **) xrealloc ((char *) tab, tab_alloc_size * sizeof (*tab));
+ }
+ tab[tab_cur_size++] = name;
+
+ if (*first)
+ {
+ *first = 0;
+ }
+ else
+ {
+ printf_filtered (", ");
+ }
+
+ wrap_here ("");
+ fputs_filtered (name, stdout);
+}
+
+static void
+sources_info (ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ register struct symtab *s;
+ register struct partial_symtab *ps;
+ register struct objfile *objfile;
+ int first;
+
+ if (!have_full_symbols () && !have_partial_symbols ())
+ {
+ error (no_symtab_msg);
+ }
+
+ printf_filtered ("Source files for which symbols have been read in:\n\n");
+
+ first = 1;
+ ALL_SYMTABS (objfile, s)
+ {
+ output_source_filename (s -> filename, &first);
+ }
+ printf_filtered ("\n\n");
+
+ printf_filtered ("Source files for which symbols will be read in on demand:\n\n");
+
+ first = 1;
+ ALL_PSYMTABS (objfile, ps)
+ {
+ if (!ps->readin)
+ {
+ output_source_filename (ps -> filename, &first);
+ }
+ }
+ printf_filtered ("\n");
+}
+
+/* List all symbols (if REGEXP is NULL) or all symbols matching REGEXP.
+ If CLASS is zero, list all symbols except functions, type names, and
+ constants (enums).
+ If CLASS is 1, list only functions.
+ If CLASS is 2, list only type names.
+ If CLASS is 3, list only method names.
+
+ BPT is non-zero if we should set a breakpoint at the functions
+ we find. */
+
+static void
+list_symbols (regexp, class, bpt)
+ char *regexp;
+ int class;
+ int bpt;
+{
+ register struct symtab *s;
+ register struct partial_symtab *ps;
+ register struct blockvector *bv;
+ struct blockvector *prev_bv = 0;
+ register struct block *b;
+ register int i, j;
+ register struct symbol *sym;
+ struct partial_symbol *psym;
+ struct objfile *objfile;
+ struct minimal_symbol *msymbol;
+ char *val;
+ static char *classnames[]
+ = {"variable", "function", "type", "method"};
+ int found_in_file = 0;
+ int found_misc = 0;
+ static enum minimal_symbol_type types[]
+ = {mst_data, mst_text, mst_abs, mst_unknown};
+ static enum minimal_symbol_type types2[]
+ = {mst_bss, mst_text, mst_abs, mst_unknown};
+ enum minimal_symbol_type ourtype = types[class];
+ enum minimal_symbol_type ourtype2 = types2[class];
+
+ if (regexp != NULL)
+ {
+ /* Make sure spacing is right for C++ operators.
+ This is just a courtesy to make the matching less sensitive
+ to how many spaces the user leaves between 'operator'
+ and <TYPENAME> or <OPERATOR>. */
+ char *opend;
+ char *opname = operator_chars (regexp, &opend);
+ if (*opname)
+ {
+ int fix = -1; /* -1 means ok; otherwise number of spaces needed. */
+ if (isalpha(*opname) || *opname == '_' || *opname == '$')
+ {
+ /* There should 1 space between 'operator' and 'TYPENAME'. */
+ if (opname[-1] != ' ' || opname[-2] == ' ')
+ fix = 1;
+ }
+ else
+ {
+ /* There should 0 spaces between 'operator' and 'OPERATOR'. */
+ if (opname[-1] == ' ')
+ fix = 0;
+ }
+ /* If wrong number of spaces, fix it. */
+ if (fix >= 0)
+ {
+ char *tmp = (char*) alloca(opend-opname+10);
+ sprintf(tmp, "operator%.*s%s", fix, " ", opname);
+ regexp = tmp;
+ }
+ }
+
+ if (0 != (val = re_comp (regexp)))
+ error ("Invalid regexp (%s): %s", val, regexp);
+ }
+
+ /* Search through the partial symtabs *first* for all symbols
+ matching the regexp. That way we don't have to reproduce all of
+ the machinery below. */
+
+ ALL_PSYMTABS (objfile, ps)
+ {
+ struct partial_symbol *bound, *gbound, *sbound;
+ int keep_going = 1;
+
+ if (ps->readin) continue;
+
+ gbound = objfile->global_psymbols.list + ps->globals_offset + ps->n_global_syms;
+ sbound = objfile->static_psymbols.list + ps->statics_offset + ps->n_static_syms;
+ bound = gbound;
+
+ /* Go through all of the symbols stored in a partial
+ symtab in one loop. */
+ psym = objfile->global_psymbols.list + ps->globals_offset;
+ while (keep_going)
+ {
+ if (psym >= bound)
+ {
+ if (bound == gbound && ps->n_static_syms != 0)
+ {
+ psym = objfile->static_psymbols.list + ps->statics_offset;
+ bound = sbound;
+ }
+ else
+ keep_going = 0;
+ continue;
+ }
+ else
+ {
+ QUIT;
+
+ /* If it would match (logic taken from loop below)
+ load the file and go on to the next one */
+ if ((regexp == NULL || SYMBOL_MATCHES_REGEXP (psym))
+ && ((class == 0 && SYMBOL_CLASS (psym) != LOC_TYPEDEF
+ && SYMBOL_CLASS (psym) != LOC_BLOCK)
+ || (class == 1 && SYMBOL_CLASS (psym) == LOC_BLOCK)
+ || (class == 2 && SYMBOL_CLASS (psym) == LOC_TYPEDEF)
+ || (class == 3 && SYMBOL_CLASS (psym) == LOC_BLOCK)))
+ {
+ PSYMTAB_TO_SYMTAB(ps);
+ keep_going = 0;
+ }
+ }
+ psym++;
+ }
+ }
+
+ /* Here, we search through the minimal symbol tables for functions that
+ match, and call find_pc_symtab on them to force their symbols to
+ be read. The symbol will then be found during the scan of symtabs
+ below. If find_pc_symtab fails, set found_misc so that we will
+ rescan to print any matching symbols without debug info. */
+
+ if (class == 1)
+ {
+ ALL_MSYMBOLS (objfile, msymbol)
+ {
+ if (MSYMBOL_TYPE (msymbol) == ourtype ||
+ MSYMBOL_TYPE (msymbol) == ourtype2)
+ {
+ if (regexp == NULL || SYMBOL_MATCHES_REGEXP (msymbol))
+ {
+ if (0 == find_pc_symtab (SYMBOL_VALUE_ADDRESS (msymbol)))
+ {
+ found_misc = 1;
+ }
+ }
+ }
+ }
+ }
+
+ /* Printout here so as to get after the "Reading in symbols"
+ messages which will be generated above. */
+ if (!bpt)
+ printf_filtered (regexp
+ ? "All %ss matching regular expression \"%s\":\n"
+ : "All defined %ss:\n",
+ classnames[class],
+ regexp);
+
+ ALL_SYMTABS (objfile, s)
+ {
+ found_in_file = 0;
+ bv = BLOCKVECTOR (s);
+ /* Often many files share a blockvector.
+ Scan each blockvector only once so that
+ we don't get every symbol many times.
+ It happens that the first symtab in the list
+ for any given blockvector is the main file. */
+ if (bv != prev_bv)
+ for (i = GLOBAL_BLOCK; i <= STATIC_BLOCK; i++)
+ {
+ b = BLOCKVECTOR_BLOCK (bv, i);
+ /* Skip the sort if this block is always sorted. */
+ if (!BLOCK_SHOULD_SORT (b))
+ sort_block_syms (b);
+ for (j = 0; j < BLOCK_NSYMS (b); j++)
+ {
+ QUIT;
+ sym = BLOCK_SYM (b, j);
+ if ((regexp == NULL || SYMBOL_MATCHES_REGEXP (sym))
+ && ((class == 0 && SYMBOL_CLASS (sym) != LOC_TYPEDEF
+ && SYMBOL_CLASS (sym) != LOC_BLOCK
+ && SYMBOL_CLASS (sym) != LOC_CONST)
+ || (class == 1 && SYMBOL_CLASS (sym) == LOC_BLOCK)
+ || (class == 2 && SYMBOL_CLASS (sym) == LOC_TYPEDEF)
+ || (class == 3 && SYMBOL_CLASS (sym) == LOC_BLOCK)))
+ {
+ if (bpt)
+ {
+ /* Set a breakpoint here, if it's a function */
+ if (class == 1)
+ {
+ /* There may be more than one function with the
+ same name but in different files. In order to
+ set breakpoints on all of them, we must give
+ both the file name and the function name to
+ break_command. */
+ char *string =
+ (char *) alloca (strlen (s->filename)
+ + strlen (SYMBOL_NAME(sym))
+ + 2);
+ strcpy (string, s->filename);
+ strcat (string, ":");
+ strcat (string, SYMBOL_NAME(sym));
+ break_command (string, 0);
+ }
+ }
+ else if (!found_in_file)
+ {
+ fputs_filtered ("\nFile ", stdout);
+ fputs_filtered (s->filename, stdout);
+ fputs_filtered (":\n", stdout);
+ }
+ found_in_file = 1;
+
+ if (class != 2 && i == STATIC_BLOCK)
+ printf_filtered ("static ");
+
+ /* Typedef that is not a C++ class */
+ if (class == 2
+ && SYMBOL_NAMESPACE (sym) != STRUCT_NAMESPACE)
+ c_typedef_print (SYMBOL_TYPE(sym), sym, stdout);
+ /* variable, func, or typedef-that-is-c++-class */
+ else if (class < 2 ||
+ (class == 2 &&
+ SYMBOL_NAMESPACE(sym) == STRUCT_NAMESPACE))
+ {
+ type_print (SYMBOL_TYPE (sym),
+ (SYMBOL_CLASS (sym) == LOC_TYPEDEF
+ ? "" : SYMBOL_SOURCE_NAME (sym)),
+ stdout, 0);
+
+ printf_filtered (";\n");
+ }
+ else
+ {
+# if 0 /* FIXME, why is this zapped out? */
+ char buf[1024];
+ c_type_print_base (TYPE_FN_FIELD_TYPE(t, i),
+ stdout, 0, 0);
+ c_type_print_varspec_prefix (TYPE_FN_FIELD_TYPE(t, i),
+ stdout, 0);
+ sprintf (buf, " %s::", type_name_no_tag (t));
+ cp_type_print_method_args (TYPE_FN_FIELD_ARGS (t, i),
+ buf, name, stdout);
+# endif
+ }
+ }
+ }
+ }
+ prev_bv = bv;
+ }
+
+ /* If there are no eyes, avoid all contact. I mean, if there are
+ no debug symbols, then print directly from the msymbol_vector. */
+
+ if (found_misc || class != 1)
+ {
+ found_in_file = 0;
+ ALL_MSYMBOLS (objfile, msymbol)
+ {
+ if (MSYMBOL_TYPE (msymbol) == ourtype ||
+ MSYMBOL_TYPE (msymbol) == ourtype2)
+ {
+ if (regexp == NULL || SYMBOL_MATCHES_REGEXP (msymbol))
+ {
+ /* Functions: Look up by address. */
+ if (class != 1 ||
+ (0 == find_pc_symtab (SYMBOL_VALUE_ADDRESS (msymbol))))
+ {
+ /* Variables/Absolutes: Look up by name */
+ if (lookup_symbol (SYMBOL_NAME (msymbol),
+ (struct block *) NULL, VAR_NAMESPACE,
+ 0, (struct symtab **) NULL) == NULL)
+ {
+ if (!found_in_file)
+ {
+ printf_filtered ("\nNon-debugging symbols:\n");
+ found_in_file = 1;
+ }
+ printf_filtered (" %08lx %s\n",
+ (unsigned long) SYMBOL_VALUE_ADDRESS (msymbol),
+ SYMBOL_SOURCE_NAME (msymbol));
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+static void
+variables_info (regexp, from_tty)
+ char *regexp;
+ int from_tty;
+{
+ list_symbols (regexp, 0, 0);
+}
+
+static void
+functions_info (regexp, from_tty)
+ char *regexp;
+ int from_tty;
+{
+ list_symbols (regexp, 1, 0);
+}
+
+static void
+types_info (regexp, from_tty)
+ char *regexp;
+ int from_tty;
+{
+ list_symbols (regexp, 2, 0);
+}
+
+#if 0
+/* Tiemann says: "info methods was never implemented." */
+static void
+methods_info (regexp)
+ char *regexp;
+{
+ list_symbols (regexp, 3, 0);
+}
+#endif /* 0 */
+
+/* Breakpoint all functions matching regular expression. */
+static void
+rbreak_command (regexp, from_tty)
+ char *regexp;
+ int from_tty;
+{
+ list_symbols (regexp, 1, 1);
+}
+
+
+/* Return Nonzero if block a is lexically nested within block b,
+ or if a and b have the same pc range.
+ Return zero otherwise. */
+int
+contained_in (a, b)
+ struct block *a, *b;
+{
+ if (!a || !b)
+ return 0;
+ return BLOCK_START (a) >= BLOCK_START (b)
+ && BLOCK_END (a) <= BLOCK_END (b);
+}
+
+
+/* Helper routine for make_symbol_completion_list. */
+
+static int return_val_size;
+static int return_val_index;
+static char **return_val;
+
+#define COMPLETION_LIST_ADD_SYMBOL(symbol, sym_text, len, text, word) \
+ do { \
+ if (SYMBOL_DEMANGLED_NAME (symbol) != NULL) \
+ /* Put only the mangled name on the list. */ \
+ /* Advantage: "b foo<TAB>" completes to "b foo(int, int)" */ \
+ /* Disadvantage: "b foo__i<TAB>" doesn't complete. */ \
+ completion_list_add_name \
+ (SYMBOL_DEMANGLED_NAME (symbol), (sym_text), (len), (text), (word)); \
+ else \
+ completion_list_add_name \
+ (SYMBOL_NAME (symbol), (sym_text), (len), (text), (word)); \
+ } while (0)
+
+/* Test to see if the symbol specified by SYMNAME (which is already
+ demangled for C++ symbols) matches SYM_TEXT in the first SYM_TEXT_LEN
+ characters. If so, add it to the current completion list. */
+
+static void
+completion_list_add_name (symname, sym_text, sym_text_len, text, word)
+ char *symname;
+ char *sym_text;
+ int sym_text_len;
+ char *text;
+ char *word;
+{
+ int newsize;
+ int i;
+
+ /* clip symbols that cannot match */
+
+ if (strncmp (symname, sym_text, sym_text_len) != 0)
+ {
+ return;
+ }
+
+ /* Clip any symbol names that we've already considered. (This is a
+ time optimization) */
+
+ for (i = 0; i < return_val_index; ++i)
+ {
+ if (STREQ (symname, return_val[i]))
+ {
+ return;
+ }
+ }
+
+ /* We have a match for a completion, so add SYMNAME to the current list
+ of matches. Note that the name is moved to freshly malloc'd space. */
+
+ {
+ char *new;
+ if (word == sym_text)
+ {
+ new = xmalloc (strlen (symname) + 5);
+ strcpy (new, symname);
+ }
+ else if (word > sym_text)
+ {
+ /* Return some portion of symname. */
+ new = xmalloc (strlen (symname) + 5);
+ strcpy (new, symname + (word - sym_text));
+ }
+ else
+ {
+ /* Return some of SYM_TEXT plus symname. */
+ new = xmalloc (strlen (symname) + (sym_text - word) + 5);
+ strncpy (new, word, sym_text - word);
+ new[sym_text - word] = '\0';
+ strcat (new, symname);
+ }
+
+ if (return_val_index + 3 > return_val_size)
+ {
+ newsize = (return_val_size *= 2) * sizeof (char *);
+ return_val = (char **) xrealloc ((char *) return_val, newsize);
+ }
+ return_val[return_val_index++] = new;
+ return_val[return_val_index] = NULL;
+ }
+}
+
+/* Return a NULL terminated array of all symbols (regardless of class) which
+ begin by matching TEXT. If the answer is no symbols, then the return value
+ is an array which contains only a NULL pointer.
+
+ Problem: All of the symbols have to be copied because readline frees them.
+ I'm not going to worry about this; hopefully there won't be that many. */
+
+char **
+make_symbol_completion_list (text, word)
+ char *text;
+ char *word;
+{
+ register struct symbol *sym;
+ register struct symtab *s;
+ register struct partial_symtab *ps;
+ register struct minimal_symbol *msymbol;
+ register struct objfile *objfile;
+ register struct block *b, *surrounding_static_block = 0;
+ register int i, j;
+ struct partial_symbol *psym;
+ /* The symbol we are completing on. Points in same buffer as text. */
+ char *sym_text;
+ /* Length of sym_text. */
+ int sym_text_len;
+
+ /* Now look for the symbol we are supposed to complete on.
+ FIXME: This should be language-specific. */
+ {
+ char *p;
+ char quote_found;
+ char *quote_pos = NULL;
+
+ /* First see if this is a quoted string. */
+ quote_found = '\0';
+ for (p = text; *p != '\0'; ++p)
+ {
+ if (quote_found != '\0')
+ {
+ if (*p == quote_found)
+ /* Found close quote. */
+ quote_found = '\0';
+ else if (*p == '\\' && p[1] == quote_found)
+ /* A backslash followed by the quote character
+ doesn't end the string. */
+ ++p;
+ }
+ else if (*p == '\'' || *p == '"')
+ {
+ quote_found = *p;
+ quote_pos = p;
+ }
+ }
+ if (quote_found == '\'')
+ /* A string within single quotes can be a symbol, so complete on it. */
+ sym_text = quote_pos + 1;
+ else if (quote_found == '"')
+ /* A double-quoted string is never a symbol, nor does it make sense
+ to complete it any other way. */
+ return NULL;
+ else
+ {
+ /* It is not a quoted string. Break it based on the characters
+ which are in symbols. */
+ while (p > text)
+ {
+ if (isalnum (p[-1]) || p[-1] == '_' || p[-1] == '\0')
+ --p;
+ else
+ break;
+ }
+ sym_text = p;
+ }
+ }
+
+ sym_text_len = strlen (sym_text);
+
+ return_val_size = 100;
+ return_val_index = 0;
+ return_val = (char **) xmalloc ((return_val_size + 1) * sizeof (char *));
+ return_val[0] = NULL;
+
+ /* Look through the partial symtabs for all symbols which begin
+ by matching SYM_TEXT. Add each one that you find to the list. */
+
+ ALL_PSYMTABS (objfile, ps)
+ {
+ /* If the psymtab's been read in we'll get it when we search
+ through the blockvector. */
+ if (ps->readin) continue;
+
+ for (psym = objfile->global_psymbols.list + ps->globals_offset;
+ psym < (objfile->global_psymbols.list + ps->globals_offset
+ + ps->n_global_syms);
+ psym++)
+ {
+ /* If interrupted, then quit. */
+ QUIT;
+ COMPLETION_LIST_ADD_SYMBOL (psym, sym_text, sym_text_len, text, word);
+ }
+
+ for (psym = objfile->static_psymbols.list + ps->statics_offset;
+ psym < (objfile->static_psymbols.list + ps->statics_offset
+ + ps->n_static_syms);
+ psym++)
+ {
+ QUIT;
+ COMPLETION_LIST_ADD_SYMBOL (psym, sym_text, sym_text_len, text, word);
+ }
+ }
+
+ /* At this point scan through the misc symbol vectors and add each
+ symbol you find to the list. Eventually we want to ignore
+ anything that isn't a text symbol (everything else will be
+ handled by the psymtab code above). */
+
+ ALL_MSYMBOLS (objfile, msymbol)
+ {
+ QUIT;
+ COMPLETION_LIST_ADD_SYMBOL (msymbol, sym_text, sym_text_len, text, word);
+ }
+
+ /* Search upwards from currently selected frame (so that we can
+ complete on local vars. */
+
+ for (b = get_selected_block (); b != NULL; b = BLOCK_SUPERBLOCK (b))
+ {
+ if (!BLOCK_SUPERBLOCK (b))
+ {
+ surrounding_static_block = b; /* For elmin of dups */
+ }
+
+ /* Also catch fields of types defined in this places which match our
+ text string. Only complete on types visible from current context. */
+
+ for (i = 0; i < BLOCK_NSYMS (b); i++)
+ {
+ sym = BLOCK_SYM (b, i);
+ COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text, word);
+ if (SYMBOL_CLASS (sym) == LOC_TYPEDEF)
+ {
+ struct type *t = SYMBOL_TYPE (sym);
+ enum type_code c = TYPE_CODE (t);
+
+ if (c == TYPE_CODE_UNION || c == TYPE_CODE_STRUCT)
+ {
+ for (j = TYPE_N_BASECLASSES (t); j < TYPE_NFIELDS (t); j++)
+ {
+ if (TYPE_FIELD_NAME (t, j))
+ {
+ completion_list_add_name (TYPE_FIELD_NAME (t, j),
+ sym_text, sym_text_len, text, word);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Go through the symtabs and check the externs and statics for
+ symbols which match. */
+
+ ALL_SYMTABS (objfile, s)
+ {
+ QUIT;
+ b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), GLOBAL_BLOCK);
+ for (i = 0; i < BLOCK_NSYMS (b); i++)
+ {
+ sym = BLOCK_SYM (b, i);
+ COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text, word);
+ }
+ }
+
+ ALL_SYMTABS (objfile, s)
+ {
+ QUIT;
+ b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), STATIC_BLOCK);
+ /* Don't do this block twice. */
+ if (b == surrounding_static_block) continue;
+ for (i = 0; i < BLOCK_NSYMS (b); i++)
+ {
+ sym = BLOCK_SYM (b, i);
+ COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text, word);
+ }
+ }
+
+ return (return_val);
+}
+
+
+#if 0
+/* Add the type of the symbol sym to the type of the current
+ function whose block we are in (assumed). The type of
+ this current function is contained in *TYPE.
+
+ This basically works as follows: When we find a function
+ symbol (N_FUNC with a 'f' or 'F' in the symbol name), we record
+ a pointer to its type in the global in_function_type. Every
+ time we come across a parameter symbol ('p' in its name), then
+ this procedure adds the name and type of that parameter
+ to the function type pointed to by *TYPE. (Which should correspond
+ to in_function_type if it was called correctly).
+
+ Note that since we are modifying a type, the result of
+ lookup_function_type() should be memcpy()ed before calling
+ this. When not in strict typing mode, the expression
+ evaluator can choose to ignore this.
+
+ Assumption: All of a function's parameter symbols will
+ appear before another function symbol is found. The parameters
+ appear in the same order in the argument list as they do in the
+ symbol table. */
+
+void
+add_param_to_type (type,sym)
+ struct type **type;
+ struct symbol *sym;
+{
+ int num = ++(TYPE_NFIELDS(*type));
+
+ if(TYPE_NFIELDS(*type)-1)
+ TYPE_FIELDS(*type) = (struct field *)
+ (*current_objfile->xrealloc) ((char *)(TYPE_FIELDS(*type)),
+ num*sizeof(struct field));
+ else
+ TYPE_FIELDS(*type) = (struct field *)
+ (*current_objfile->xmalloc) (num*sizeof(struct field));
+
+ TYPE_FIELD_BITPOS(*type,num-1) = num-1;
+ TYPE_FIELD_BITSIZE(*type,num-1) = 0;
+ TYPE_FIELD_TYPE(*type,num-1) = SYMBOL_TYPE(sym);
+ TYPE_FIELD_NAME(*type,num-1) = SYMBOL_NAME(sym);
+}
+#endif
+
+void
+_initialize_symtab ()
+{
+ add_info ("variables", variables_info,
+ "All global and static variable names, or those matching REGEXP.");
+ add_info ("functions", functions_info,
+ "All function names, or those matching REGEXP.");
+
+ /* FIXME: This command has at least the following problems:
+ 1. It prints builtin types (in a very strange and confusing fashion).
+ 2. It doesn't print right, e.g. with
+ typedef struct foo *FOO
+ type_print prints "FOO" when we want to make it (in this situation)
+ print "struct foo *".
+ I also think "ptype" or "whatis" is more likely to be useful (but if
+ there is much disagreement "info types" can be fixed). */
+ add_info ("types", types_info,
+ "All type names, or those matching REGEXP.");
+
+#if 0
+ add_info ("methods", methods_info,
+ "All method names, or those matching REGEXP::REGEXP.\n\
+If the class qualifier is omitted, it is assumed to be the current scope.\n\
+If the first REGEXP is omitted, then all methods matching the second REGEXP\n\
+are listed.");
+#endif
+ add_info ("sources", sources_info,
+ "Source files in the program.");
+
+ add_com ("rbreak", no_class, rbreak_command,
+ "Set a breakpoint for all functions matching REGEXP.");
+
+ /* Initialize the one built-in type that isn't language dependent... */
+ builtin_type_error = init_type (TYPE_CODE_ERROR, 0, 0,
+ "<unknown type>", (struct objfile *) NULL);
+}
diff --git a/gnu/usr.bin/gdb/gdb/symtab.h b/gnu/usr.bin/gdb/gdb/symtab.h
new file mode 100644
index 0000000..9570b39
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/symtab.h
@@ -0,0 +1,1124 @@
+/* Symbol table definitions for GDB.
+ Copyright (C) 1986, 1989, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (SYMTAB_H)
+#define SYMTAB_H 1
+
+/* Some definitions and declarations to go with use of obstacks. */
+
+#include "obstack.h"
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+
+/* Define a structure for the information that is common to all symbol types,
+ including minimal symbols, partial symbols, and full symbols. In a
+ multilanguage environment, some language specific information may need to
+ be recorded along with each symbol. */
+
+struct general_symbol_info
+{
+ /* Name of the symbol. This is a required field. Storage for the name is
+ allocated on the psymbol_obstack or symbol_obstack for the associated
+ objfile. */
+
+ char *name;
+
+ /* Value of the symbol. Which member of this union to use, and what
+ it means, depends on what kind of symbol this is and its
+ SYMBOL_CLASS. See comments there for more details. All of these
+ are in host byte order (though what they point to might be in
+ target byte order, e.g. LOC_CONST_BYTES). */
+
+ union
+ {
+ long value;
+
+ struct block *block;
+
+ char *bytes;
+
+ CORE_ADDR address;
+
+ /* for opaque typedef struct chain */
+
+ struct symbol *chain;
+ }
+ value;
+
+ /* Record the source code language that applies to this symbol.
+ This is used to select one of the fields from the language specific
+ union below. */
+
+ enum language language;
+
+ /* Since one and only one language can apply, wrap the language specific
+ information inside a union. */
+
+ union
+ {
+ struct cplus_specific /* For C++ */
+ {
+ char *demangled_name;
+ } cplus_specific;
+ struct chill_specific /* For Chill */
+ {
+ char *demangled_name;
+ } chill_specific;
+ } language_specific;
+
+ /* Which section is this symbol in? This is an index into
+ section_offsets for this objfile. Negative means that the symbol
+ does not get relocated relative to a section.
+ Disclaimer: currently this is just used for xcoff, so don't
+ expect all symbol-reading code to set it correctly (the ELF code
+ also tries to set it correctly). */
+
+ int section;
+};
+
+#define SYMBOL_NAME(symbol) (symbol)->ginfo.name
+#define SYMBOL_VALUE(symbol) (symbol)->ginfo.value.value
+#define SYMBOL_VALUE_ADDRESS(symbol) (symbol)->ginfo.value.address
+#define SYMBOL_VALUE_BYTES(symbol) (symbol)->ginfo.value.bytes
+#define SYMBOL_BLOCK_VALUE(symbol) (symbol)->ginfo.value.block
+#define SYMBOL_VALUE_CHAIN(symbol) (symbol)->ginfo.value.chain
+#define SYMBOL_LANGUAGE(symbol) (symbol)->ginfo.language
+#define SYMBOL_SECTION(symbol) (symbol)->ginfo.section
+
+#define SYMBOL_CPLUS_DEMANGLED_NAME(symbol) \
+ (symbol)->ginfo.language_specific.cplus_specific.demangled_name
+
+
+extern int demangle; /* We reference it, so go ahead and declare it. */
+
+/* Macro that initializes the language dependent portion of a symbol
+ depending upon the language for the symbol. */
+
+#define SYMBOL_INIT_LANGUAGE_SPECIFIC(symbol,language) \
+ do { \
+ SYMBOL_LANGUAGE (symbol) = language; \
+ if (SYMBOL_LANGUAGE (symbol) == language_cplus) \
+ { \
+ SYMBOL_CPLUS_DEMANGLED_NAME (symbol) = NULL; \
+ } \
+ else if (SYMBOL_LANGUAGE (symbol) == language_chill) \
+ { \
+ SYMBOL_CHILL_DEMANGLED_NAME (symbol) = NULL; \
+ } \
+ else \
+ { \
+ memset (&(symbol)->ginfo.language_specific, 0, \
+ sizeof ((symbol)->ginfo.language_specific)); \
+ } \
+ } while (0)
+
+/* Macro that attempts to initialize the demangled name for a symbol,
+ based on the language of that symbol. If the language is set to
+ language_auto, it will attempt to find any demangling algorithm
+ that works and then set the language appropriately. If no demangling
+ of any kind is found, the language is set back to language_unknown,
+ so we can avoid doing this work again the next time we encounter
+ the symbol. Any required space to store the name is obtained from the
+ specified obstack. */
+
+#define SYMBOL_INIT_DEMANGLED_NAME(symbol,obstack) \
+ do { \
+ char *demangled = NULL; \
+ if (SYMBOL_LANGUAGE (symbol) == language_cplus \
+ || SYMBOL_LANGUAGE (symbol) == language_auto) \
+ { \
+ demangled = \
+ cplus_demangle (SYMBOL_NAME (symbol), DMGL_PARAMS | DMGL_ANSI);\
+ if (demangled != NULL) \
+ { \
+ SYMBOL_LANGUAGE (symbol) = language_cplus; \
+ SYMBOL_CPLUS_DEMANGLED_NAME (symbol) = \
+ obsavestring (demangled, strlen (demangled), (obstack)); \
+ free (demangled); \
+ } \
+ else \
+ { \
+ SYMBOL_CPLUS_DEMANGLED_NAME (symbol) = NULL; \
+ } \
+ } \
+ if (demangled == NULL \
+ && (SYMBOL_LANGUAGE (symbol) == language_chill \
+ || SYMBOL_LANGUAGE (symbol) == language_auto)) \
+ { \
+ demangled = \
+ chill_demangle (SYMBOL_NAME (symbol)); \
+ if (demangled != NULL) \
+ { \
+ SYMBOL_LANGUAGE (symbol) = language_chill; \
+ SYMBOL_CHILL_DEMANGLED_NAME (symbol) = \
+ obsavestring (demangled, strlen (demangled), (obstack)); \
+ free (demangled); \
+ } \
+ else \
+ { \
+ SYMBOL_CHILL_DEMANGLED_NAME (symbol) = NULL; \
+ } \
+ } \
+ if (SYMBOL_LANGUAGE (symbol) == language_auto) \
+ { \
+ SYMBOL_LANGUAGE (symbol) = language_unknown; \
+ } \
+ } while (0)
+
+/* Macro that returns the demangled name for a symbol based on the language
+ for that symbol. If no demangled name exists, returns NULL. */
+
+#define SYMBOL_DEMANGLED_NAME(symbol) \
+ (SYMBOL_LANGUAGE (symbol) == language_cplus \
+ ? SYMBOL_CPLUS_DEMANGLED_NAME (symbol) \
+ : (SYMBOL_LANGUAGE (symbol) == language_chill \
+ ? SYMBOL_CHILL_DEMANGLED_NAME (symbol) \
+ : NULL))
+
+#define SYMBOL_CHILL_DEMANGLED_NAME(symbol) \
+ (symbol)->ginfo.language_specific.chill_specific.demangled_name
+
+/* Macro that returns the "natural source name" of a symbol. In C++ this is
+ the "demangled" form of the name if demangle is on and the "mangled" form
+ of the name if demangle is off. In other languages this is just the
+ symbol name. The result should never be NULL. */
+
+#define SYMBOL_SOURCE_NAME(symbol) \
+ (demangle && SYMBOL_DEMANGLED_NAME (symbol) != NULL \
+ ? SYMBOL_DEMANGLED_NAME (symbol) \
+ : SYMBOL_NAME (symbol))
+
+/* Macro that returns the "natural assembly name" of a symbol. In C++ this is
+ the "mangled" form of the name if demangle is off, or if demangle is on and
+ asm_demangle is off. Otherwise if asm_demangle is on it is the "demangled"
+ form. In other languages this is just the symbol name. The result should
+ never be NULL. */
+
+#define SYMBOL_LINKAGE_NAME(symbol) \
+ (demangle && asm_demangle && SYMBOL_DEMANGLED_NAME (symbol) != NULL \
+ ? SYMBOL_DEMANGLED_NAME (symbol) \
+ : SYMBOL_NAME (symbol))
+
+/* From utils.c. */
+extern int demangle;
+extern int asm_demangle;
+
+/* Macro that tests a symbol for a match against a specified name string.
+ First test the unencoded name, then looks for and test a C++ encoded
+ name if it exists. Note that whitespace is ignored while attempting to
+ match a C++ encoded name, so that "foo::bar(int,long)" is the same as
+ "foo :: bar (int, long)".
+ Evaluates to zero if the match fails, or nonzero if it succeeds. */
+
+#define SYMBOL_MATCHES_NAME(symbol, name) \
+ (STREQ (SYMBOL_NAME (symbol), (name)) \
+ || (SYMBOL_DEMANGLED_NAME (symbol) != NULL \
+ && strcmp_iw (SYMBOL_DEMANGLED_NAME (symbol), (name)) == 0))
+
+/* Macro that tests a symbol for an re-match against the last compiled regular
+ expression. First test the unencoded name, then look for and test a C++
+ encoded name if it exists.
+ Evaluates to zero if the match fails, or nonzero if it succeeds. */
+
+#define SYMBOL_MATCHES_REGEXP(symbol) \
+ (re_exec (SYMBOL_NAME (symbol)) != 0 \
+ || (SYMBOL_DEMANGLED_NAME (symbol) != NULL \
+ && re_exec (SYMBOL_DEMANGLED_NAME (symbol)) != 0))
+
+/* Define a simple structure used to hold some very basic information about
+ all defined global symbols (text, data, bss, abs, etc). The only required
+ information is the general_symbol_info.
+
+ In many cases, even if a file was compiled with no special options for
+ debugging at all, as long as was not stripped it will contain sufficient
+ information to build a useful minimal symbol table using this structure.
+ Even when a file contains enough debugging information to build a full
+ symbol table, these minimal symbols are still useful for quickly mapping
+ between names and addresses, and vice versa. They are also sometimes
+ used to figure out what full symbol table entries need to be read in. */
+
+struct minimal_symbol
+{
+
+ /* The general symbol info required for all types of symbols.
+
+ The SYMBOL_VALUE_ADDRESS contains the address that this symbol
+ corresponds to. */
+
+ struct general_symbol_info ginfo;
+
+ /* The info field is available for caching machine-specific information that
+ The AMD 29000 tdep.c uses it to remember things it has decoded from the
+ instructions in the function header, so it doesn't have to rederive the
+ info constantly (over a serial line). It is initialized to zero and
+ stays that way until target-dependent code sets it. Storage for any data
+ pointed to by this field should be allocated on the symbol_obstack for
+ the associated objfile. The type would be "void *" except for reasons
+ of compatibility with older compilers. This field is optional. */
+
+ char *info;
+
+ /* Classification types for this symbol. These should be taken as "advisory
+ only", since if gdb can't easily figure out a classification it simply
+ selects mst_unknown. It may also have to guess when it can't figure out
+ which is a better match between two types (mst_data versus mst_bss) for
+ example. Since the minimal symbol info is sometimes derived from the
+ BFD library's view of a file, we need to live with what information bfd
+ supplies. */
+
+ enum minimal_symbol_type
+ {
+ mst_unknown = 0, /* Unknown type, the default */
+ mst_text, /* Generally executable instructions */
+ mst_data, /* Generally initialized data */
+ mst_bss, /* Generally uninitialized data */
+ mst_abs, /* Generally absolute (nonrelocatable) */
+ /* For the mst_file* types, the names are only guaranteed to be unique
+ within a given .o file. */
+ mst_file_text, /* Static version of mst_text */
+ mst_file_data, /* Static version of mst_data */
+ mst_file_bss /* Static version of mst_bss */
+ } type;
+
+};
+
+#define MSYMBOL_INFO(msymbol) (msymbol)->info
+#define MSYMBOL_TYPE(msymbol) (msymbol)->type
+
+
+/* All of the name-scope contours of the program
+ are represented by `struct block' objects.
+ All of these objects are pointed to by the blockvector.
+
+ Each block represents one name scope.
+ Each lexical context has its own block.
+
+ The blockvector begins with some special blocks.
+ The GLOBAL_BLOCK contains all the symbols defined in this compilation
+ whose scope is the entire program linked together.
+ The STATIC_BLOCK contains all the symbols whose scope is the
+ entire compilation excluding other separate compilations.
+ Blocks starting with the FIRST_LOCAL_BLOCK are not special.
+
+ Each block records a range of core addresses for the code that
+ is in the scope of the block. The STATIC_BLOCK and GLOBAL_BLOCK
+ give, for the range of code, the entire range of code produced
+ by the compilation that the symbol segment belongs to.
+
+ The blocks appear in the blockvector
+ in order of increasing starting-address,
+ and, within that, in order of decreasing ending-address.
+
+ This implies that within the body of one function
+ the blocks appear in the order of a depth-first tree walk. */
+
+struct blockvector
+{
+ /* Number of blocks in the list. */
+ int nblocks;
+ /* The blocks themselves. */
+ struct block *block[1];
+};
+
+#define BLOCKVECTOR_NBLOCKS(blocklist) (blocklist)->nblocks
+#define BLOCKVECTOR_BLOCK(blocklist,n) (blocklist)->block[n]
+
+/* Special block numbers */
+
+#define GLOBAL_BLOCK 0
+#define STATIC_BLOCK 1
+#define FIRST_LOCAL_BLOCK 2
+
+struct block
+{
+
+ /* Addresses in the executable code that are in this block. */
+
+ CORE_ADDR startaddr;
+ CORE_ADDR endaddr;
+
+ /* The symbol that names this block, if the block is the body of a
+ function; otherwise, zero. */
+
+ struct symbol *function;
+
+ /* The `struct block' for the containing block, or 0 if none.
+
+ The superblock of a top-level local block (i.e. a function in the
+ case of C) is the STATIC_BLOCK. The superblock of the
+ STATIC_BLOCK is the GLOBAL_BLOCK. */
+
+ struct block *superblock;
+
+ /* Version of GCC used to compile the function corresponding
+ to this block, or 0 if not compiled with GCC. When possible,
+ GCC should be compatible with the native compiler, or if that
+ is not feasible, the differences should be fixed during symbol
+ reading. As of 16 Apr 93, this flag is never used to distinguish
+ between gcc2 and the native compiler.
+
+ If there is no function corresponding to this block, this meaning
+ of this flag is undefined. */
+
+ unsigned char gcc_compile_flag;
+
+ /* Number of local symbols. */
+
+ int nsyms;
+
+ /* The symbols. If some of them are arguments, then they must be
+ in the order in which we would like to print them. */
+
+ struct symbol *sym[1];
+};
+
+#define BLOCK_START(bl) (bl)->startaddr
+#define BLOCK_END(bl) (bl)->endaddr
+#define BLOCK_NSYMS(bl) (bl)->nsyms
+#define BLOCK_SYM(bl, n) (bl)->sym[n]
+#define BLOCK_FUNCTION(bl) (bl)->function
+#define BLOCK_SUPERBLOCK(bl) (bl)->superblock
+#define BLOCK_GCC_COMPILED(bl) (bl)->gcc_compile_flag
+
+/* Nonzero if symbols of block BL should be sorted alphabetically.
+ Don't sort a block which corresponds to a function. If we did the
+ sorting would have to preserve the order of the symbols for the
+ arguments. */
+
+#define BLOCK_SHOULD_SORT(bl) ((bl)->nsyms >= 40 && BLOCK_FUNCTION (bl) == NULL)
+
+
+/* Represent one symbol name; a variable, constant, function or typedef. */
+
+/* Different name spaces for symbols. Looking up a symbol specifies a
+ namespace and ignores symbol definitions in other name spaces. */
+
+enum namespace
+{
+ /* UNDEF_NAMESPACE is used when a namespace has not been discovered or
+ none of the following apply. This usually indicates an error either
+ in the symbol information or in gdb's handling of symbols. */
+
+ UNDEF_NAMESPACE,
+
+ /* VAR_NAMESPACE is the usual namespace. In C, this contains variables,
+ function names, typedef names and enum type values. */
+
+ VAR_NAMESPACE,
+
+ /* STRUCT_NAMESPACE is used in C to hold struct, union and enum type names.
+ Thus, if `struct foo' is used in a C program, it produces a symbol named
+ `foo' in the STRUCT_NAMESPACE. */
+
+ STRUCT_NAMESPACE,
+
+ /* LABEL_NAMESPACE may be used for names of labels (for gotos);
+ currently it is not used and labels are not recorded at all. */
+
+ LABEL_NAMESPACE
+};
+
+/* An address-class says where to find the value of a symbol. */
+
+enum address_class
+{
+ /* Not used; catches errors */
+
+ LOC_UNDEF,
+
+ /* Value is constant int SYMBOL_VALUE, host byteorder */
+
+ LOC_CONST,
+
+ /* Value is at fixed address SYMBOL_VALUE_ADDRESS */
+
+ LOC_STATIC,
+
+ /* Value is in register. SYMBOL_VALUE is the register number. */
+
+ LOC_REGISTER,
+
+ /* It's an argument; the value is at SYMBOL_VALUE offset in arglist. */
+
+ LOC_ARG,
+
+ /* Value address is at SYMBOL_VALUE offset in arglist. */
+
+ LOC_REF_ARG,
+
+ /* Value is in register number SYMBOL_VALUE. Just like LOC_REGISTER
+ except this is an argument. Probably the cleaner way to handle
+ this would be to separate address_class (which would include
+ separate ARG and LOCAL to deal with FRAME_ARGS_ADDRESS versus
+ FRAME_LOCALS_ADDRESS), and an is_argument flag.
+
+ For some symbol formats (stabs, for some compilers at least),
+ the compiler generates two symbols, an argument and a register.
+ In some cases we combine them to a single LOC_REGPARM in symbol
+ reading, but currently not for all cases (e.g. it's passed on the
+ stack and then loaded into a register). */
+
+ LOC_REGPARM,
+
+ /* Value is in specified register. Just like LOC_REGPARM except the
+ register holds the address of the argument instead of the argument
+ itself. This is currently used for the passing of structs and unions
+ on sparc and hppa. It is also used for call by reference where the
+ address is in a register, at least by mipsread.c. */
+
+ LOC_REGPARM_ADDR,
+
+ /* Value is a local variable at SYMBOL_VALUE offset in stack frame. */
+
+ LOC_LOCAL,
+
+ /* Value not used; definition in SYMBOL_TYPE. Symbols in the namespace
+ STRUCT_NAMESPACE all have this class. */
+
+ LOC_TYPEDEF,
+
+ /* Value is address SYMBOL_VALUE_ADDRESS in the code */
+
+ LOC_LABEL,
+
+ /* In a symbol table, value is SYMBOL_BLOCK_VALUE of a `struct block'.
+ In a partial symbol table, SYMBOL_VALUE_ADDRESS is the start address
+ of the block. Function names have this class. */
+
+ LOC_BLOCK,
+
+ /* Value is a constant byte-sequence pointed to by SYMBOL_VALUE_BYTES, in
+ target byte order. */
+
+ LOC_CONST_BYTES,
+
+ /* Value is arg at SYMBOL_VALUE offset in stack frame. Differs from
+ LOC_LOCAL in that symbol is an argument; differs from LOC_ARG in
+ that we find it in the frame (FRAME_LOCALS_ADDRESS), not in the
+ arglist (FRAME_ARGS_ADDRESS). Added for i960, which passes args
+ in regs then copies to frame. */
+
+ LOC_LOCAL_ARG,
+
+ /* Value is at SYMBOL_VALUE offset from the current value of
+ register number SYMBOL_BASEREG. This exists mainly for the same
+ things that LOC_LOCAL and LOC_ARG do; but we need to do this
+ instead because on 88k DWARF gives us the offset from the
+ frame/stack pointer, rather than the offset from the "canonical
+ frame address" used by COFF, stabs, etc., and we don't know how
+ to convert between these until we start examining prologues.
+
+ Note that LOC_BASEREG is much less general than a DWARF expression.
+ We don't need the generality (at least not yet), and storing a general
+ DWARF expression would presumably take up more space than the existing
+ scheme. */
+
+ LOC_BASEREG,
+
+ /* Same as LOC_BASEREG but it is an argument. */
+
+ LOC_BASEREG_ARG,
+
+ /* The variable does not actually exist in the program.
+ The value is ignored. */
+
+ LOC_OPTIMIZED_OUT
+};
+
+struct symbol
+{
+
+ /* The general symbol info required for all types of symbols. */
+
+ struct general_symbol_info ginfo;
+
+ /* Name space code. */
+
+ enum namespace namespace;
+
+ /* Address class */
+
+ enum address_class class;
+
+ /* Data type of value */
+
+ struct type *type;
+
+ /* Line number of definition. FIXME: Should we really make the assumption
+ that nobody will try to debug files longer than 64K lines? What about
+ machine generated programs? */
+
+ unsigned short line;
+
+ /* Some symbols require an additional value to be recorded on a per-
+ symbol basis. Stash those values here. */
+
+ union
+ {
+ /* Used by LOC_BASEREG and LOC_BASEREG_ARG. */
+ short basereg;
+ }
+ aux_value;
+
+};
+
+#define SYMBOL_NAMESPACE(symbol) (symbol)->namespace
+#define SYMBOL_CLASS(symbol) (symbol)->class
+#define SYMBOL_TYPE(symbol) (symbol)->type
+#define SYMBOL_LINE(symbol) (symbol)->line
+#define SYMBOL_BASEREG(symbol) (symbol)->aux_value.basereg
+
+/* A partial_symbol records the name, namespace, and address class of
+ symbols whose types we have not parsed yet. For functions, it also
+ contains their memory address, so we can find them from a PC value.
+ Each partial_symbol sits in a partial_symtab, all of which are chained
+ on a partial symtab list and which points to the corresponding
+ normal symtab once the partial_symtab has been referenced. */
+
+struct partial_symbol
+{
+
+ /* The general symbol info required for all types of symbols. */
+
+ struct general_symbol_info ginfo;
+
+ /* Name space code. */
+
+ enum namespace namespace;
+
+ /* Address class (for info_symbols) */
+
+ enum address_class class;
+
+};
+
+#define PSYMBOL_NAMESPACE(psymbol) (psymbol)->namespace
+#define PSYMBOL_CLASS(psymbol) (psymbol)->class
+
+
+/* Source-file information. This describes the relation between source files,
+ ine numbers and addresses in the program text. */
+
+struct sourcevector
+{
+ int length; /* Number of source files described */
+ struct source *source[1]; /* Descriptions of the files */
+};
+
+/* Each item represents a line-->pc (or the reverse) mapping. This is
+ somewhat more wasteful of space than one might wish, but since only
+ the files which are actually debugged are read in to core, we don't
+ waste much space. */
+
+struct linetable_entry
+{
+ int line;
+ CORE_ADDR pc;
+};
+
+/* The order of entries in the linetable is significant. They should
+ be sorted by increasing values of the pc field. If there is more than
+ one entry for a given pc, then I'm not sure what should happen (and
+ I not sure whether we currently handle it the best way).
+
+ Example: a C for statement generally looks like this
+
+ 10 0x100 - for the init/test part of a for stmt.
+ 20 0x200
+ 30 0x300
+ 10 0x400 - for the increment part of a for stmt.
+
+ */
+
+struct linetable
+{
+ int nitems;
+
+ /* Actually NITEMS elements. If you don't like this use of the
+ `struct hack', you can shove it up your ANSI (seriously, if the
+ committee tells us how to do it, we can probably go along). */
+ struct linetable_entry item[1];
+};
+
+/* All the information on one source file. */
+
+struct source
+{
+ char *name; /* Name of file */
+ struct linetable contents;
+};
+
+/* How to relocate the symbols from each section in a symbol file.
+ Each struct contains an array of offsets.
+ The ordering and meaning of the offsets is file-type-dependent;
+ typically it is indexed by section numbers or symbol types or
+ something like that.
+
+ To give us flexibility in changing the internal representation
+ of these offsets, the ANOFFSET macro must be used to insert and
+ extract offset values in the struct. */
+
+struct section_offsets
+ {
+ CORE_ADDR offsets[1]; /* As many as needed. */
+ };
+
+#define ANOFFSET(secoff, whichone) (secoff->offsets[whichone])
+
+/* Each source file or header is represented by a struct symtab.
+ These objects are chained through the `next' field. */
+
+struct symtab
+ {
+
+ /* Chain of all existing symtabs. */
+
+ struct symtab *next;
+
+ /* List of all symbol scope blocks for this symtab. May be shared
+ between different symtabs (and normally is for all the symtabs
+ in a given compilation unit). */
+
+ struct blockvector *blockvector;
+
+ /* Table mapping core addresses to line numbers for this file.
+ Can be NULL if none. Never shared between different symtabs. */
+
+ struct linetable *linetable;
+
+ /* Section in objfile->section_offsets for the blockvector and
+ the linetable. */
+
+ int block_line_section;
+
+ /* If several symtabs share a blockvector, exactly one of them
+ should be designed the primary, so that the blockvector
+ is relocated exactly once by objfile_relocate. */
+
+ int primary;
+
+ /* Name of this source file. */
+
+ char *filename;
+
+ /* Directory in which it was compiled, or NULL if we don't know. */
+
+ char *dirname;
+
+ /* This component says how to free the data we point to:
+ free_contents => do a tree walk and free each object.
+ free_nothing => do nothing; some other symtab will free
+ the data this one uses.
+ free_linetable => free just the linetable. FIXME: Is this redundant
+ with the primary field? */
+
+ enum free_code
+ {
+ free_nothing, free_contents, free_linetable
+ }
+ free_code;
+
+ /* Pointer to one block of storage to be freed, if nonzero. */
+ /* This is IN ADDITION to the action indicated by free_code. */
+
+ char *free_ptr;
+
+ /* Total number of lines found in source file. */
+
+ int nlines;
+
+ /* line_charpos[N] is the position of the (N-1)th line of the
+ source file. "position" means something we can lseek() to; it
+ is not guaranteed to be useful any other way. */
+
+ int *line_charpos;
+
+ /* Language of this source file. */
+
+ enum language language;
+
+ /* String of version information. May be zero. */
+
+ char *version;
+
+ /* Full name of file as found by searching the source path.
+ NULL if not yet known. */
+
+ char *fullname;
+
+ /* Object file from which this symbol information was read. */
+
+ struct objfile *objfile;
+
+ /* Anything extra for this symtab. This is for target machines
+ with special debugging info of some sort (which cannot just
+ be represented in a normal symtab). */
+
+#if defined (EXTRA_SYMTAB_INFO)
+ EXTRA_SYMTAB_INFO
+#endif
+
+ };
+
+#define BLOCKVECTOR(symtab) (symtab)->blockvector
+#define LINETABLE(symtab) (symtab)->linetable
+
+
+/* Each source file that has not been fully read in is represented by
+ a partial_symtab. This contains the information on where in the
+ executable the debugging symbols for a specific file are, and a
+ list of names of global symbols which are located in this file.
+ They are all chained on partial symtab lists.
+
+ Even after the source file has been read into a symtab, the
+ partial_symtab remains around. They are allocated on an obstack,
+ psymbol_obstack. FIXME, this is bad for dynamic linking or VxWorks-
+ style execution of a bunch of .o's. */
+
+struct partial_symtab
+{
+
+ /* Chain of all existing partial symtabs. */
+
+ struct partial_symtab *next;
+
+ /* Name of the source file which this partial_symtab defines */
+
+ char *filename;
+
+ /* Information about the object file from which symbols should be read. */
+
+ struct objfile *objfile;
+
+ /* Set of relocation offsets to apply to each section. */
+
+ struct section_offsets *section_offsets;
+
+ /* Range of text addresses covered by this file; texthigh is the
+ beginning of the next section. */
+
+ CORE_ADDR textlow;
+ CORE_ADDR texthigh;
+
+ /* Array of pointers to all of the partial_symtab's which this one
+ depends on. Since this array can only be set to previous or
+ the current (?) psymtab, this dependency tree is guaranteed not
+ to have any loops. "depends on" means that symbols must be read
+ for the dependencies before being read for this psymtab; this is
+ for type references in stabs, where if foo.c includes foo.h, declarations
+ in foo.h may use type numbers defined in foo.c. For other debugging
+ formats there may be no need to use dependencies. */
+
+ struct partial_symtab **dependencies;
+
+ int number_of_dependencies;
+
+ /* Global symbol list. This list will be sorted after readin to
+ improve access. Binary search will be the usual method of
+ finding a symbol within it. globals_offset is an integer offset
+ within global_psymbols[]. */
+
+ int globals_offset;
+ int n_global_syms;
+
+ /* Static symbol list. This list will *not* be sorted after readin;
+ to find a symbol in it, exhaustive search must be used. This is
+ reasonable because searches through this list will eventually
+ lead to either the read in of a files symbols for real (assumed
+ to take a *lot* of time; check) or an error (and we don't care
+ how long errors take). This is an offset and size within
+ static_psymbols[]. */
+
+ int statics_offset;
+ int n_static_syms;
+
+ /* Pointer to symtab eventually allocated for this source file, 0 if
+ !readin or if we haven't looked for the symtab after it was readin. */
+
+ struct symtab *symtab;
+
+ /* Pointer to function which will read in the symtab corresponding to
+ this psymtab. */
+
+ void (*read_symtab) PARAMS ((struct partial_symtab *));
+
+ /* Information that lets read_symtab() locate the part of the symbol table
+ that this psymtab corresponds to. This information is private to the
+ format-dependent symbol reading routines. For further detail examine
+ the various symbol reading modules. Should really be (void *) but is
+ (char *) as with other such gdb variables. (FIXME) */
+
+ char *read_symtab_private;
+
+ /* Non-zero if the symtab corresponding to this psymtab has been readin */
+
+ unsigned char readin;
+};
+
+/* A fast way to get from a psymtab to its symtab (after the first time). */
+#define PSYMTAB_TO_SYMTAB(pst) \
+ ((pst) -> symtab != NULL ? (pst) -> symtab : psymtab_to_symtab (pst))
+
+
+/* The virtual function table is now an array of structures which have the
+ form { int16 offset, delta; void *pfn; }.
+
+ In normal virtual function tables, OFFSET is unused.
+ DELTA is the amount which is added to the apparent object's base
+ address in order to point to the actual object to which the
+ virtual function should be applied.
+ PFN is a pointer to the virtual function.
+
+ Note that this macro is g++ specific (FIXME). */
+
+#define VTBL_FNADDR_OFFSET 2
+
+/* Macro that yields non-zero value iff NAME is the prefix for C++ operator
+ names. If you leave out the parenthesis here you will lose!
+ Currently 'o' 'p' CPLUS_MARKER is used for both the symbol in the
+ symbol-file and the names in gdb's symbol table.
+ Note that this macro is g++ specific (FIXME). */
+
+#define OPNAME_PREFIX_P(NAME) \
+ ((NAME)[0] == 'o' && (NAME)[1] == 'p' && (NAME)[2] == CPLUS_MARKER)
+
+/* Macro that yields non-zero value iff NAME is the prefix for C++ vtbl
+ names. Note that this macro is g++ specific (FIXME). */
+
+#define VTBL_PREFIX_P(NAME) \
+ ((NAME)[3] == CPLUS_MARKER && !strncmp ((NAME), "_vt", 3))
+
+/* Macro that yields non-zero value iff NAME is the prefix for C++ destructor
+ names. Note that this macro is g++ specific (FIXME). */
+
+#define DESTRUCTOR_PREFIX_P(NAME) \
+ ((NAME)[0] == '_' && (NAME)[1] == CPLUS_MARKER && (NAME)[2] == '_')
+
+
+/* External variables and functions for the objects described above. */
+
+/* This symtab variable specifies the current file for printing source lines */
+
+extern struct symtab *current_source_symtab;
+
+/* This is the next line to print for listing source lines. */
+
+extern int current_source_line;
+
+/* See the comment in symfile.c about how current_objfile is used. */
+
+extern struct objfile *current_objfile;
+
+extern struct symtab *
+lookup_symtab PARAMS ((char *));
+
+extern struct symbol *
+lookup_symbol PARAMS ((const char *, const struct block *,
+ const enum namespace, int *, struct symtab **));
+
+extern struct symbol *
+lookup_block_symbol PARAMS ((const struct block *, const char *,
+ const enum namespace));
+
+extern struct type *
+lookup_struct PARAMS ((char *, struct block *));
+
+extern struct type *
+lookup_union PARAMS ((char *, struct block *));
+
+extern struct type *
+lookup_enum PARAMS ((char *, struct block *));
+
+extern struct symbol *
+block_function PARAMS ((struct block *));
+
+extern struct symbol *
+find_pc_function PARAMS ((CORE_ADDR));
+
+extern int find_pc_partial_function
+ PARAMS ((CORE_ADDR, char **, CORE_ADDR *, CORE_ADDR *));
+
+extern void
+clear_pc_function_cache PARAMS ((void));
+
+extern struct partial_symtab *
+lookup_partial_symtab PARAMS ((char *));
+
+extern struct partial_symtab *
+find_pc_psymtab PARAMS ((CORE_ADDR));
+
+extern struct symtab *
+find_pc_symtab PARAMS ((CORE_ADDR));
+
+extern struct partial_symbol *
+find_pc_psymbol PARAMS ((struct partial_symtab *, CORE_ADDR));
+
+extern int
+find_pc_line_pc_range PARAMS ((CORE_ADDR, CORE_ADDR *, CORE_ADDR *));
+
+extern int
+contained_in PARAMS ((struct block *, struct block *));
+
+extern void
+reread_symbols PARAMS ((void));
+
+/* Functions for dealing with the minimal symbol table, really a misc
+ address<->symbol mapping for things we don't have debug symbols for. */
+
+extern void
+prim_record_minimal_symbol PARAMS ((const char *, CORE_ADDR,
+ enum minimal_symbol_type));
+
+extern void
+prim_record_minimal_symbol_and_info PARAMS ((const char *, CORE_ADDR,
+ enum minimal_symbol_type,
+ char *info, int section));
+
+extern struct minimal_symbol *
+lookup_minimal_symbol PARAMS ((const char *, struct objfile *));
+
+extern struct minimal_symbol *
+lookup_minimal_symbol_by_pc PARAMS ((CORE_ADDR));
+
+extern void
+init_minimal_symbol_collection PARAMS ((void));
+
+extern void
+discard_minimal_symbols PARAMS ((int));
+
+extern void
+install_minimal_symbols PARAMS ((struct objfile *));
+
+struct symtab_and_line
+{
+ struct symtab *symtab;
+
+ /* Line number. Line numbers start at 1 and proceed through symtab->nlines.
+ 0 is never a valid line number; it is used to indicate that line number
+ information is not available. */
+ int line;
+
+ CORE_ADDR pc;
+ CORE_ADDR end;
+};
+
+struct symtabs_and_lines
+{
+ struct symtab_and_line *sals;
+ int nelts;
+};
+
+/* Given a pc value, return line number it is in. Second arg nonzero means
+ if pc is on the boundary use the previous statement's line number. */
+
+extern struct symtab_and_line
+find_pc_line PARAMS ((CORE_ADDR, int));
+
+/* Given a symtab and line number, return the pc there. */
+
+extern CORE_ADDR
+find_line_pc PARAMS ((struct symtab *, int));
+
+extern int
+find_line_pc_range PARAMS ((struct symtab *, int, CORE_ADDR *, CORE_ADDR *));
+
+extern void
+resolve_sal_pc PARAMS ((struct symtab_and_line *));
+
+/* Given a string, return the line specified by it. For commands like "list"
+ and "breakpoint". */
+
+extern struct symtabs_and_lines
+decode_line_spec PARAMS ((char *, int));
+
+extern struct symtabs_and_lines
+decode_line_spec_1 PARAMS ((char *, int));
+
+extern struct symtabs_and_lines
+decode_line_1 PARAMS ((char **, int, struct symtab *, int, char ***));
+
+/* Symmisc.c */
+
+#if MAINTENANCE_CMDS
+
+void
+maintenance_print_symbols PARAMS ((char *, int));
+
+void
+maintenance_print_psymbols PARAMS ((char *, int));
+
+void
+maintenance_print_msymbols PARAMS ((char *, int));
+
+void
+maintenance_print_objfiles PARAMS ((char *, int));
+
+#endif
+
+extern void
+free_symtab PARAMS ((struct symtab *));
+
+/* Symbol-reading stuff in symfile.c and solib.c. */
+
+extern struct symtab *
+psymtab_to_symtab PARAMS ((struct partial_symtab *));
+
+extern void
+clear_solib PARAMS ((void));
+
+extern struct objfile *
+symbol_file_add PARAMS ((char *, int, CORE_ADDR, int, int, int));
+
+/* source.c */
+
+extern int frame_file_full_name; /* in stack.c */
+
+extern int
+identify_source_line PARAMS ((struct symtab *, int, int, CORE_ADDR));
+
+extern void
+print_source_lines PARAMS ((struct symtab *, int, int, int));
+
+extern void
+forget_cached_source_info PARAMS ((void));
+
+extern void
+select_source_symtab PARAMS ((struct symtab *));
+
+extern char **make_symbol_completion_list PARAMS ((char *, char *));
+
+/* symtab.c */
+
+extern struct partial_symtab *
+find_main_psymtab PARAMS ((void));
+
+/* blockframe.c */
+
+extern struct blockvector *
+blockvector_for_pc PARAMS ((CORE_ADDR, int *));
+
+/* symfile.c */
+
+extern void
+clear_symtab_users PARAMS ((void));
+
+extern enum language
+deduce_language_from_filename PARAMS ((char *));
+
+#endif /* !defined(SYMTAB_H) */
diff --git a/gnu/usr.bin/gdb/gdb/target.c b/gnu/usr.bin/gdb/gdb/target.c
new file mode 100644
index 0000000..ea1bd93
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/target.c
@@ -0,0 +1,789 @@
+/* Select target systems and architectures at runtime for GDB.
+ Copyright 1990, 1992, 1993 Free Software Foundation, Inc.
+ Contributed by Cygnus Support.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include <errno.h>
+#include <ctype.h>
+#include "target.h"
+#include "gdbcmd.h"
+#include "symtab.h"
+#include "inferior.h"
+#include "bfd.h"
+#include "symfile.h"
+#include "objfiles.h"
+
+extern int errno;
+
+static void
+target_info PARAMS ((char *, int));
+
+static void
+cleanup_target PARAMS ((struct target_ops *));
+
+static void
+maybe_kill_then_create_inferior PARAMS ((char *, char *, char **));
+
+static void
+maybe_kill_then_attach PARAMS ((char *, int));
+
+static void
+kill_or_be_killed PARAMS ((int));
+
+static void
+default_terminal_info PARAMS ((char *, int));
+
+static int
+nosymbol PARAMS ((char *, CORE_ADDR *));
+
+static void
+tcomplain PARAMS ((void));
+
+static int
+nomemory PARAMS ((CORE_ADDR, char *, int, int));
+
+static int
+return_zero PARAMS ((void));
+
+static void
+ignore PARAMS ((void));
+
+static void
+target_command PARAMS ((char *, int));
+
+static struct target_ops *
+find_default_run_target PARAMS ((char *));
+
+/* Pointer to array of target architecture structures; the size of the
+ array; the current index into the array; the allocated size of the
+ array. */
+struct target_ops **target_structs;
+unsigned target_struct_size;
+unsigned target_struct_index;
+unsigned target_struct_allocsize;
+#define DEFAULT_ALLOCSIZE 10
+
+/* The initial current target, so that there is always a semi-valid
+ current target. */
+
+struct target_ops dummy_target = {"None", "None", "",
+ 0, 0, /* open, close */
+ find_default_attach, 0, /* attach, detach */
+ 0, 0, /* resume, wait */
+ 0, 0, 0, /* registers */
+ 0, 0, /* memory */
+ 0, 0, /* bkpts */
+ 0, 0, 0, 0, 0, /* terminal */
+ 0, 0, /* kill, load */
+ 0, /* lookup_symbol */
+ find_default_create_inferior, /* create_inferior */
+ 0, /* mourn_inferior */
+ 0, /* can_run */
+ 0, /* notice_signals */
+ dummy_stratum, 0, /* stratum, next */
+ 0, 0, 0, 0, 0, /* all mem, mem, stack, regs, exec */
+ 0, 0, /* section pointers */
+ OPS_MAGIC,
+};
+
+/* The target structure we are currently using to talk to a process
+ or file or whatever "inferior" we have. */
+
+struct target_ops *current_target;
+
+/* The stack of target structures that have been pushed. */
+
+struct target_ops **current_target_stack;
+
+/* Command list for target. */
+
+static struct cmd_list_element *targetlist = NULL;
+
+/* The user just typed 'target' without the name of a target. */
+
+/* ARGSUSED */
+static void
+target_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ fputs_filtered ("Argument required (target name). Try `help target'\n",
+ stdout);
+}
+
+/* Add a possible target architecture to the list. */
+
+void
+add_target (t)
+ struct target_ops *t;
+{
+ if (t->to_magic != OPS_MAGIC)
+ {
+ fprintf(stderr, "Magic number of %s target struct wrong\n",
+ t->to_shortname);
+ abort();
+ }
+
+ if (!target_structs)
+ {
+ target_struct_allocsize = DEFAULT_ALLOCSIZE;
+ target_structs = (struct target_ops **) xmalloc
+ (target_struct_allocsize * sizeof (*target_structs));
+ }
+ if (target_struct_size >= target_struct_allocsize)
+ {
+ target_struct_allocsize *= 2;
+ target_structs = (struct target_ops **)
+ xrealloc ((char *) target_structs,
+ target_struct_allocsize * sizeof (*target_structs));
+ }
+ target_structs[target_struct_size++] = t;
+ cleanup_target (t);
+
+ if (targetlist == NULL)
+ add_prefix_cmd ("target", class_run, target_command,
+ "Connect to a target machine or process.\n\
+The first argument is the type or protocol of the target machine.\n\
+Remaining arguments are interpreted by the target protocol. For more\n\
+information on the arguments for a particular protocol, type\n\
+`help target ' followed by the protocol name.",
+ &targetlist, "target ", 0, &cmdlist);
+ add_cmd (t->to_shortname, no_class, t->to_open, t->to_doc, &targetlist);
+}
+
+/* Stub functions */
+
+static void
+ignore ()
+{
+}
+
+/* ARGSUSED */
+static int
+nomemory (memaddr, myaddr, len, write)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+ int write;
+{
+ errno = EIO; /* Can't read/write this location */
+ return 0; /* No bytes handled */
+}
+
+static void
+tcomplain ()
+{
+ error ("You can't do that when your target is `%s'",
+ current_target->to_shortname);
+}
+
+void
+noprocess ()
+{
+ error ("You can't do that without a process to debug");
+}
+
+/* ARGSUSED */
+static int
+nosymbol (name, addrp)
+ char *name;
+ CORE_ADDR *addrp;
+{
+ return 1; /* Symbol does not exist in target env */
+}
+
+/* ARGSUSED */
+static void
+default_terminal_info (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ printf("No saved terminal information.\n");
+}
+
+#if 0
+/* With strata, this function is no longer needed. FIXME. */
+/* This is the default target_create_inferior function. It looks up
+ the stack for some target that cares to create inferiors, then
+ calls it -- or complains if not found. */
+
+static void
+upstack_create_inferior (exec, args, env)
+ char *exec;
+ char *args;
+ char **env;
+{
+ struct target_ops *t;
+
+ for (t = current_target;
+ t;
+ t = t->to_next)
+ {
+ if (t->to_create_inferior != upstack_create_inferior)
+ {
+ t->to_create_inferior (exec, args, env);
+ return;
+ }
+
+ }
+ tcomplain();
+}
+#endif
+
+/* This is the default target_create_inferior and target_attach function.
+ If the current target is executing, it asks whether to kill it off.
+ If this function returns without calling error(), it has killed off
+ the target, and the operation should be attempted. */
+
+static void
+kill_or_be_killed (from_tty)
+ int from_tty;
+{
+ if (target_has_execution)
+ {
+ printf ("You are already running a program:\n");
+ target_files_info ();
+ if (query ("Kill it? ")) {
+ target_kill ();
+ if (target_has_execution)
+ error ("Killing the program did not help.");
+ return;
+ } else {
+ error ("Program not killed.");
+ }
+ }
+ tcomplain();
+}
+
+static void
+maybe_kill_then_attach (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ kill_or_be_killed (from_tty);
+ target_attach (args, from_tty);
+}
+
+static void
+maybe_kill_then_create_inferior (exec, args, env)
+ char *exec;
+ char *args;
+ char **env;
+{
+ kill_or_be_killed (0);
+ target_create_inferior (exec, args, env);
+}
+
+/* Clean up a target struct so it no longer has any zero pointers in it.
+ We default entries, at least to stubs that print error messages. */
+
+static void
+cleanup_target (t)
+ struct target_ops *t;
+{
+
+ /* Check magic number. If wrong, it probably means someone changed
+ the struct definition, but not all the places that initialize one. */
+ if (t->to_magic != OPS_MAGIC)
+ {
+ fprintf(stderr, "Magic number of %s target struct wrong\n",
+ t->to_shortname);
+ abort();
+ }
+
+#define de_fault(field, value) \
+ if (!t->field) t->field = value
+
+ /* FIELD DEFAULT VALUE */
+
+ de_fault (to_open, (void (*)())tcomplain);
+ de_fault (to_close, (void (*)())ignore);
+ de_fault (to_attach, maybe_kill_then_attach);
+ de_fault (to_detach, (void (*)())ignore);
+ de_fault (to_resume, (void (*)())noprocess);
+ de_fault (to_wait, (int (*)())noprocess);
+ de_fault (to_fetch_registers, (void (*)())ignore);
+ de_fault (to_store_registers, (void (*)())noprocess);
+ de_fault (to_prepare_to_store, (void (*)())noprocess);
+ de_fault (to_xfer_memory, (int (*)())nomemory);
+ de_fault (to_files_info, (void (*)())ignore);
+ de_fault (to_insert_breakpoint, memory_insert_breakpoint);
+ de_fault (to_remove_breakpoint, memory_remove_breakpoint);
+ de_fault (to_terminal_init, ignore);
+ de_fault (to_terminal_inferior, ignore);
+ de_fault (to_terminal_ours_for_output,ignore);
+ de_fault (to_terminal_ours, ignore);
+ de_fault (to_terminal_info, default_terminal_info);
+ de_fault (to_kill, (void (*)())noprocess);
+ de_fault (to_load, (void (*)())tcomplain);
+ de_fault (to_lookup_symbol, nosymbol);
+ de_fault (to_create_inferior, maybe_kill_then_create_inferior);
+ de_fault (to_mourn_inferior, (void (*)())noprocess);
+ de_fault (to_can_run, return_zero);
+ de_fault (to_notice_signals, (void (*)())ignore);
+ de_fault (to_next, 0);
+ de_fault (to_has_all_memory, 0);
+ de_fault (to_has_memory, 0);
+ de_fault (to_has_stack, 0);
+ de_fault (to_has_registers, 0);
+ de_fault (to_has_execution, 0);
+
+#undef de_fault
+}
+
+/* Push a new target type into the stack of the existing target accessors,
+ possibly superseding some of the existing accessors.
+
+ Result is zero if the pushed target ended up on top of the stack,
+ nonzero if at least one target is on top of it.
+
+ Rather than allow an empty stack, we always have the dummy target at
+ the bottom stratum, so we can call the function vectors without
+ checking them. */
+
+int
+push_target (t)
+ struct target_ops *t;
+{
+ struct target_ops *st, *prev;
+
+ for (prev = 0, st = current_target;
+ st;
+ prev = st, st = st->to_next) {
+ if ((int)(t->to_stratum) >= (int)(st->to_stratum))
+ break;
+ }
+
+ while (t->to_stratum == st->to_stratum) {
+ /* There's already something on this stratum. Close it off. */
+ (st->to_close) (0);
+ if (prev)
+ prev->to_next = st->to_next; /* Unchain old target_ops */
+ else
+ current_target = st->to_next; /* Unchain first on list */
+ st = st->to_next;
+ }
+
+ /* We have removed all targets in our stratum, now add ourself. */
+ t->to_next = st;
+ if (prev)
+ prev->to_next = t;
+ else
+ current_target = t;
+
+ cleanup_target (current_target);
+ return prev != 0;
+}
+
+/* Remove a target_ops vector from the stack, wherever it may be.
+ Return how many times it was removed (0 or 1 unless bug). */
+
+int
+unpush_target (t)
+ struct target_ops *t;
+{
+ struct target_ops *u, *v;
+ int result = 0;
+
+ for (u = current_target, v = 0;
+ u;
+ v = u, u = u->to_next)
+ if (u == t)
+ {
+ if (v == 0)
+ pop_target(); /* unchain top copy */
+ else {
+ (t->to_close)(0); /* Let it clean up */
+ v->to_next = t->to_next; /* unchain middle copy */
+ }
+ result++;
+ }
+ return result;
+}
+
+void
+pop_target ()
+{
+ (current_target->to_close)(0); /* Let it clean up */
+ current_target = current_target->to_next;
+#if 0
+ /* This will dump core if ever called--push_target expects current_target
+ to be non-NULL. But I don't think it's needed; I don't see how the
+ dummy_target could ever be removed from the stack. */
+ if (!current_target) /* At bottom, push dummy. */
+ push_target (&dummy_target);
+#endif
+}
+
+#undef MIN
+#define MIN(A, B) (((A) <= (B)) ? (A) : (B))
+
+/* target_read_string -- read a null terminated string from MEMADDR in target.
+ The read may also be terminated early by getting an error from target_xfer_
+ memory.
+ LEN is the size of the buffer pointed to by MYADDR. Note that a terminating
+ null will only be written if there is sufficient room. The return value is
+ is the number of bytes (including the null) actually transferred.
+*/
+
+int
+target_read_string (memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+{
+ int tlen, origlen, offset, i;
+ char buf[4];
+
+ origlen = len;
+
+ while (len > 0)
+ {
+ tlen = MIN (len, 4 - (memaddr & 3));
+ offset = memaddr & 3;
+
+ if (target_xfer_memory (memaddr & ~3, buf, 4, 0))
+ return origlen - len;
+
+ for (i = 0; i < tlen; i++)
+ {
+ *myaddr++ = buf[i + offset];
+ if (buf[i + offset] == '\000')
+ return (origlen - len) + i + 1;
+ }
+
+ memaddr += tlen;
+ len -= tlen;
+ }
+ return origlen;
+}
+
+/* Read LEN bytes of target memory at address MEMADDR, placing the results in
+ GDB's memory at MYADDR. Returns either 0 for success or an errno value
+ if any error occurs.
+
+ If an error occurs, no guarantee is made about the contents of the data at
+ MYADDR. In particular, the caller should not depend upon partial reads
+ filling the buffer with good data. There is no way for the caller to know
+ how much good data might have been transfered anyway. Callers that can
+ deal with partial reads should call target_read_memory_partial. */
+
+int
+target_read_memory (memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+{
+ return target_xfer_memory (memaddr, myaddr, len, 0);
+}
+
+/* Read LEN bytes of target memory at address MEMADDR, placing the results
+ in GDB's memory at MYADDR. Returns a count of the bytes actually read,
+ and optionally an errno value in the location pointed to by ERRNOPTR
+ if ERRNOPTR is non-null. */
+
+int
+target_read_memory_partial (memaddr, myaddr, len, errnoptr)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+ int *errnoptr;
+{
+ int nread; /* Number of bytes actually read. */
+ int errcode; /* Error from last read. */
+
+ /* First try a complete read. */
+ errcode = target_xfer_memory (memaddr, myaddr, len, 0);
+ if (errcode == 0)
+ {
+ /* Got it all. */
+ nread = len;
+ }
+ else
+ {
+ /* Loop, reading one byte at a time until we get as much as we can. */
+ for (errcode = 0, nread = 0; len > 0 && errcode == 0; nread++, len--)
+ {
+ errcode = target_xfer_memory (memaddr++, myaddr++, 1, 0);
+ }
+ /* If an error, the last read was unsuccessful, so adjust count. */
+ if (errcode != 0)
+ {
+ nread--;
+ }
+ }
+ if (errnoptr != NULL)
+ {
+ *errnoptr = errcode;
+ }
+ return (nread);
+}
+
+int
+target_write_memory (memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+{
+ return target_xfer_memory (memaddr, myaddr, len, 1);
+}
+
+/* Move memory to or from the targets. Iterate until all of it has
+ been moved, if necessary. The top target gets priority; anything
+ it doesn't want, is offered to the next one down, etc. Note the
+ business with curlen: if an early target says "no, but I have a
+ boundary overlapping this xfer" then we shorten what we offer to
+ the subsequent targets so the early guy will get a chance at the
+ tail before the subsequent ones do.
+
+ Result is 0 or errno value. */
+
+int
+target_xfer_memory (memaddr, myaddr, len, write)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+ int write;
+{
+ int curlen;
+ int res;
+ struct target_ops *t;
+
+ /* to_xfer_memory is not guaranteed to set errno, even when it returns
+ 0. */
+ errno = 0;
+
+ /* The quick case is that the top target does it all. */
+ res = current_target->to_xfer_memory
+ (memaddr, myaddr, len, write, current_target);
+ if (res == len)
+ return 0;
+
+ if (res > 0)
+ goto bump;
+ /* If res <= 0 then we call it again in the loop. Ah well. */
+
+ for (; len > 0;)
+ {
+ curlen = len; /* Want to do it all */
+ for (t = current_target;
+ t;
+ t = t->to_has_all_memory? 0: t->to_next)
+ {
+ res = t->to_xfer_memory(memaddr, myaddr, curlen, write, t);
+ if (res > 0) break; /* Handled all or part of xfer */
+ if (res == 0) continue; /* Handled none */
+ curlen = -res; /* Could handle once we get past res bytes */
+ }
+ if (res <= 0)
+ {
+ /* If this address is for nonexistent memory,
+ read zeros if reading, or do nothing if writing. Return error. */
+ if (!write)
+ memset (myaddr, 0, len);
+ if (errno == 0)
+ return EIO;
+ else
+ return errno;
+ }
+bump:
+ memaddr += res;
+ myaddr += res;
+ len -= res;
+ }
+ return 0; /* We managed to cover it all somehow. */
+}
+
+
+/* ARGSUSED */
+static void
+target_info (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ struct target_ops *t;
+ int has_all_mem = 0;
+
+ if (symfile_objfile != NULL)
+ printf ("Symbols from \"%s\".\n", symfile_objfile->name);
+
+#ifdef FILES_INFO_HOOK
+ if (FILES_INFO_HOOK ())
+ return;
+#endif
+
+ for (t = current_target;
+ t;
+ t = t->to_next)
+ {
+ if ((int)(t->to_stratum) <= (int)dummy_stratum)
+ continue;
+ if (has_all_mem)
+ printf("\tWhile running this, gdb does not access memory from...\n");
+ printf("%s:\n", t->to_longname);
+ (t->to_files_info)(t);
+ has_all_mem = t->to_has_all_memory;
+ }
+}
+
+/* This is to be called by the open routine before it does
+ anything. */
+
+void
+target_preopen (from_tty)
+ int from_tty;
+{
+ dont_repeat();
+
+ if (target_has_execution)
+ {
+ if (query ("A program is being debugged already. Kill it? "))
+ target_kill ();
+ else
+ error ("Program not killed.");
+ }
+}
+
+/* Detach a target after doing deferred register stores. */
+
+void
+target_detach (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ /* Handle any optimized stores to the inferior. */
+#ifdef DO_DEFERRED_STORES
+ DO_DEFERRED_STORES;
+#endif
+ (current_target->to_detach) (args, from_tty);
+}
+
+/* Look through the list of possible targets for a target that can
+ execute a run or attach command without any other data. This is
+ used to locate the default process stratum.
+
+ Result is always valid (error() is called for errors). */
+
+static struct target_ops *
+find_default_run_target (do_mesg)
+ char *do_mesg;
+{
+ struct target_ops **t;
+ struct target_ops *runable = NULL;
+ int count;
+
+ count = 0;
+
+ for (t = target_structs; t < target_structs + target_struct_size;
+ ++t)
+ {
+ if (target_can_run(*t))
+ {
+ runable = *t;
+ ++count;
+ }
+ }
+
+ if (count != 1)
+ error ("Don't know how to %s. Try \"help target\".", do_mesg);
+
+ return runable;
+}
+
+void
+find_default_attach (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ struct target_ops *t;
+
+ t = find_default_run_target("attach");
+ (t->to_attach) (args, from_tty);
+ return;
+}
+
+void
+find_default_create_inferior (exec_file, allargs, env)
+ char *exec_file;
+ char *allargs;
+ char **env;
+{
+ struct target_ops *t;
+
+ t = find_default_run_target("run");
+ (t->to_create_inferior) (exec_file, allargs, env);
+ return;
+}
+
+static int
+return_zero ()
+{
+ return 0;
+}
+
+struct target_ops *
+find_core_target ()
+{
+ struct target_ops **t;
+ struct target_ops *runable = NULL;
+ int count;
+
+ count = 0;
+
+ for (t = target_structs; t < target_structs + target_struct_size;
+ ++t)
+ {
+ if ((*t)->to_stratum == core_stratum)
+ {
+ runable = *t;
+ ++count;
+ }
+ }
+
+ return(count == 1 ? runable : NULL);
+}
+
+/* Convert a normal process ID to a string. Returns the string in a static
+ buffer. */
+
+char *
+normal_pid_to_str (pid)
+ int pid;
+{
+ static char buf[30];
+
+ sprintf (buf, "process %d", pid);
+
+ return buf;
+}
+
+static char targ_desc[] =
+ "Names of targets and files being debugged.\n\
+Shows the entire stack of targets currently in use (including the exec-file,\n\
+core-file, and process, if any), as well as the symbol file name.";
+
+void
+_initialize_targets ()
+{
+ current_target = &dummy_target;
+ cleanup_target (current_target);
+
+ add_info ("target", target_info, targ_desc);
+ add_info ("files", target_info, targ_desc);
+}
diff --git a/gnu/usr.bin/gdb/gdb/target.h b/gnu/usr.bin/gdb/gdb/target.h
new file mode 100644
index 0000000..c112b4a
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/target.h
@@ -0,0 +1,465 @@
+/* Interface between GDB and target environments, including files and processes
+ Copyright 1990, 1991, 1992 Free Software Foundation, Inc.
+ Contributed by Cygnus Support. Written by John Gilmore.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (TARGET_H)
+#define TARGET_H
+
+/* This include file defines the interface between the main part
+ of the debugger, and the part which is target-specific, or
+ specific to the communications interface between us and the
+ target.
+
+ A TARGET is an interface between the debugger and a particular
+ kind of file or process. Targets can be STACKED in STRATA,
+ so that more than one target can potentially respond to a request.
+ In particular, memory accesses will walk down the stack of targets
+ until they find a target that is interested in handling that particular
+ address. STRATA are artificial boundaries on the stack, within
+ which particular kinds of targets live. Strata exist so that
+ people don't get confused by pushing e.g. a process target and then
+ a file target, and wondering why they can't see the current values
+ of variables any more (the file target is handling them and they
+ never get to the process target). So when you push a file target,
+ it goes into the file stratum, which is always below the process
+ stratum. */
+
+#include "bfd.h"
+
+enum strata {
+ dummy_stratum, /* The lowest of the low */
+ file_stratum, /* Executable files, etc */
+ core_stratum, /* Core dump files */
+ process_stratum /* Executing processes */
+};
+
+struct target_ops
+{
+ char *to_shortname; /* Name this target type */
+ char *to_longname; /* Name for printing */
+ char *to_doc; /* Documentation. Does not include trailing
+ newline, and starts with a one-line descrip-
+ tion (probably similar to to_longname). */
+ void (*to_open) PARAMS ((char *, int));
+ void (*to_close) PARAMS ((int));
+ void (*to_attach) PARAMS ((char *, int));
+ void (*to_detach) PARAMS ((char *, int));
+ void (*to_resume) PARAMS ((int, int, int));
+ int (*to_wait) PARAMS ((int, int *));
+ void (*to_fetch_registers) PARAMS ((int));
+ void (*to_store_registers) PARAMS ((int));
+ void (*to_prepare_to_store) PARAMS ((void));
+
+ /* Transfer LEN bytes of memory between GDB address MYADDR and
+ target address MEMADDR. If WRITE, transfer them to the target, else
+ transfer them from the target. TARGET is the target from which we
+ get this function.
+
+ Return value, N, is one of the following:
+
+ 0 means that we can't handle this. If errno has been set, it is the
+ error which prevented us from doing it (FIXME: What about bfd_error?).
+
+ positive (call it N) means that we have transferred N bytes
+ starting at MEMADDR. We might be able to handle more bytes
+ beyond this length, but no promises.
+
+ negative (call its absolute value N) means that we cannot
+ transfer right at MEMADDR, but we could transfer at least
+ something at MEMADDR + N. */
+
+ int (*to_xfer_memory) PARAMS ((CORE_ADDR memaddr, char *myaddr,
+ int len, int write,
+ struct target_ops * target));
+
+ void (*to_files_info) PARAMS ((struct target_ops *));
+ int (*to_insert_breakpoint) PARAMS ((CORE_ADDR, char *));
+ int (*to_remove_breakpoint) PARAMS ((CORE_ADDR, char *));
+ void (*to_terminal_init) PARAMS ((void));
+ void (*to_terminal_inferior) PARAMS ((void));
+ void (*to_terminal_ours_for_output) PARAMS ((void));
+ void (*to_terminal_ours) PARAMS ((void));
+ void (*to_terminal_info) PARAMS ((char *, int));
+ void (*to_kill) PARAMS ((void));
+ void (*to_load) PARAMS ((char *, int));
+ int (*to_lookup_symbol) PARAMS ((char *, CORE_ADDR *));
+ void (*to_create_inferior) PARAMS ((char *, char *, char **));
+ void (*to_mourn_inferior) PARAMS ((void));
+ int (*to_can_run) PARAMS ((void));
+ void (*to_notice_signals) PARAMS ((int pid));
+ enum strata to_stratum;
+ struct target_ops
+ *to_next;
+ int to_has_all_memory;
+ int to_has_memory;
+ int to_has_stack;
+ int to_has_registers;
+ int to_has_execution;
+ struct section_table
+ *to_sections;
+ struct section_table
+ *to_sections_end;
+ int to_magic;
+ /* Need sub-structure for target machine related rather than comm related? */
+};
+
+/* Magic number for checking ops size. If a struct doesn't end with this
+ number, somebody changed the declaration but didn't change all the
+ places that initialize one. */
+
+#define OPS_MAGIC 3840
+
+/* The ops structure for our "current" target process. This should
+ never be NULL. If there is no target, it points to the dummy_target. */
+
+extern struct target_ops *current_target;
+
+/* Define easy words for doing these operations on our current target. */
+
+#define target_shortname (current_target->to_shortname)
+#define target_longname (current_target->to_longname)
+
+/* The open routine takes the rest of the parameters from the command,
+ and (if successful) pushes a new target onto the stack.
+ Targets should supply this routine, if only to provide an error message. */
+#define target_open(name, from_tty) \
+ (*current_target->to_open) (name, from_tty)
+
+/* Does whatever cleanup is required for a target that we are no longer
+ going to be calling. Argument says whether we are quitting gdb and
+ should not get hung in case of errors, or whether we want a clean
+ termination even if it takes a while. This routine is automatically
+ always called just before a routine is popped off the target stack.
+ Closing file descriptors and freeing memory are typical things it should
+ do. */
+
+#define target_close(quitting) \
+ (*current_target->to_close) (quitting)
+
+/* Attaches to a process on the target side. Arguments are as passed
+ to the `attach' command by the user. This routine can be called
+ when the target is not on the target-stack, if the target_can_run
+ routine returns 1; in that case, it must push itself onto the stack.
+ Upon exit, the target should be ready for normal operations, and
+ should be ready to deliver the status of the process immediately
+ (without waiting) to an upcoming target_wait call. */
+
+#define target_attach(args, from_tty) \
+ (*current_target->to_attach) (args, from_tty)
+
+/* Takes a program previously attached to and detaches it.
+ The program may resume execution (some targets do, some don't) and will
+ no longer stop on signals, etc. We better not have left any breakpoints
+ in the program or it'll die when it hits one. ARGS is arguments
+ typed by the user (e.g. a signal to send the process). FROM_TTY
+ says whether to be verbose or not. */
+
+extern void
+target_detach PARAMS ((char *, int));
+
+/* Resume execution of the target process PID. STEP says whether to
+ single-step or to run free; SIGGNAL is the signal value (e.g. SIGINT) to be
+ given to the target, or zero for no signal. */
+
+#define target_resume(pid, step, siggnal) \
+ (*current_target->to_resume) (pid, step, siggnal)
+
+/* Wait for process pid to do something. Pid = -1 to wait for any pid to do
+ something. Return pid of child, or -1 in case of error; store status
+ through argument pointer STATUS. */
+
+#define target_wait(pid, status) \
+ (*current_target->to_wait) (pid, status)
+
+/* Fetch register REGNO, or all regs if regno == -1. No result. */
+
+#define target_fetch_registers(regno) \
+ (*current_target->to_fetch_registers) (regno)
+
+/* Store at least register REGNO, or all regs if REGNO == -1.
+ It can store as many registers as it wants to, so target_prepare_to_store
+ must have been previously called. Calls error() if there are problems. */
+
+#define target_store_registers(regs) \
+ (*current_target->to_store_registers) (regs)
+
+/* Get ready to modify the registers array. On machines which store
+ individual registers, this doesn't need to do anything. On machines
+ which store all the registers in one fell swoop, this makes sure
+ that REGISTERS contains all the registers from the program being
+ debugged. */
+
+#define target_prepare_to_store() \
+ (*current_target->to_prepare_to_store) ()
+
+extern int
+target_read_string PARAMS ((CORE_ADDR, char *, int));
+
+extern int
+target_read_memory PARAMS ((CORE_ADDR, char *, int));
+
+extern int
+target_read_memory_partial PARAMS ((CORE_ADDR, char *, int, int *));
+
+extern int
+target_write_memory PARAMS ((CORE_ADDR, char *, int));
+
+extern int
+xfer_memory PARAMS ((CORE_ADDR, char *, int, int, struct target_ops *));
+
+extern int
+child_xfer_memory PARAMS ((CORE_ADDR, char *, int, int, struct target_ops *));
+
+/* Transfer LEN bytes between target address MEMADDR and GDB address MYADDR.
+ Returns 0 for success, errno code for failure (which includes partial
+ transfers--if you want a more useful response to partial transfers, try
+ target_read_memory_partial). */
+
+extern int target_xfer_memory PARAMS ((CORE_ADDR memaddr, char *myaddr,
+ int len, int write));
+
+/* From exec.c */
+
+extern void
+print_section_info PARAMS ((struct target_ops *, bfd *));
+
+/* Print a line about the current target. */
+
+#define target_files_info() \
+ (*current_target->to_files_info) (current_target)
+
+/* Insert a breakpoint at address ADDR in the target machine.
+ SAVE is a pointer to memory allocated for saving the
+ target contents. It is guaranteed by the caller to be long enough
+ to save "sizeof BREAKPOINT" bytes. Result is 0 for success, or
+ an errno value. */
+
+#define target_insert_breakpoint(addr, save) \
+ (*current_target->to_insert_breakpoint) (addr, save)
+
+/* Remove a breakpoint at address ADDR in the target machine.
+ SAVE is a pointer to the same save area
+ that was previously passed to target_insert_breakpoint.
+ Result is 0 for success, or an errno value. */
+
+#define target_remove_breakpoint(addr, save) \
+ (*current_target->to_remove_breakpoint) (addr, save)
+
+/* Initialize the terminal settings we record for the inferior,
+ before we actually run the inferior. */
+
+#define target_terminal_init() \
+ (*current_target->to_terminal_init) ()
+
+/* Put the inferior's terminal settings into effect.
+ This is preparation for starting or resuming the inferior. */
+
+#define target_terminal_inferior() \
+ (*current_target->to_terminal_inferior) ()
+
+/* Put some of our terminal settings into effect,
+ enough to get proper results from our output,
+ but do not change into or out of RAW mode
+ so that no input is discarded.
+
+ After doing this, either terminal_ours or terminal_inferior
+ should be called to get back to a normal state of affairs. */
+
+#define target_terminal_ours_for_output() \
+ (*current_target->to_terminal_ours_for_output) ()
+
+/* Put our terminal settings into effect.
+ First record the inferior's terminal settings
+ so they can be restored properly later. */
+
+#define target_terminal_ours() \
+ (*current_target->to_terminal_ours) ()
+
+/* Print useful information about our terminal status, if such a thing
+ exists. */
+
+#define target_terminal_info(arg, from_tty) \
+ (*current_target->to_terminal_info) (arg, from_tty)
+
+/* Kill the inferior process. Make it go away. */
+
+#define target_kill() \
+ (*current_target->to_kill) ()
+
+/* Load an executable file into the target process. This is expected to
+ not only bring new code into the target process, but also to update
+ GDB's symbol tables to match. */
+
+#define target_load(arg, from_tty) \
+ (*current_target->to_load) (arg, from_tty)
+
+/* Look up a symbol in the target's symbol table. NAME is the symbol
+ name. ADDRP is a CORE_ADDR * pointing to where the value of the symbol
+ should be returned. The result is 0 if successful, nonzero if the
+ symbol does not exist in the target environment. This function should
+ not call error() if communication with the target is interrupted, since
+ it is called from symbol reading, but should return nonzero, possibly
+ doing a complain(). */
+
+#define target_lookup_symbol(name, addrp) \
+ (*current_target->to_lookup_symbol) (name, addrp)
+
+/* Start an inferior process and set inferior_pid to its pid.
+ EXEC_FILE is the file to run.
+ ALLARGS is a string containing the arguments to the program.
+ ENV is the environment vector to pass. Errors reported with error().
+ On VxWorks and various standalone systems, we ignore exec_file. */
+
+#define target_create_inferior(exec_file, args, env) \
+ (*current_target->to_create_inferior) (exec_file, args, env)
+
+/* The inferior process has died. Do what is right. */
+
+#define target_mourn_inferior() \
+ (*current_target->to_mourn_inferior) ()
+
+/* Does target have enough data to do a run or attach command? */
+
+#define target_can_run(t) \
+ ((t)->to_can_run) ()
+
+/* post process changes to signal handling in the inferior. */
+
+#define target_notice_signals(pid) \
+ (*current_target->to_notice_signals) (pid)
+
+/* Pointer to next target in the chain, e.g. a core file and an exec file. */
+
+#define target_next \
+ (current_target->to_next)
+
+/* Does the target include all of memory, or only part of it? This
+ determines whether we look up the target chain for other parts of
+ memory if this target can't satisfy a request. */
+
+#define target_has_all_memory \
+ (current_target->to_has_all_memory)
+
+/* Does the target include memory? (Dummy targets don't.) */
+
+#define target_has_memory \
+ (current_target->to_has_memory)
+
+/* Does the target have a stack? (Exec files don't, VxWorks doesn't, until
+ we start a process.) */
+
+#define target_has_stack \
+ (current_target->to_has_stack)
+
+/* Does the target have registers? (Exec files don't.) */
+
+#define target_has_registers \
+ (current_target->to_has_registers)
+
+/* Does the target have execution? Can we make it jump (through
+ hoops), or pop its stack a few times? FIXME: If this is to work that
+ way, it needs to check whether an inferior actually exists.
+ remote-udi.c and probably other targets can be the current target
+ when the inferior doesn't actually exist at the moment. Right now
+ this just tells us whether this target is *capable* of execution. */
+
+#define target_has_execution \
+ (current_target->to_has_execution)
+
+/* Converts a process id to a string. Usually, the string just contains
+ `process xyz', but on some systems it may contain
+ `process xyz thread abc'. */
+
+#ifndef target_pid_to_str
+#define target_pid_to_str(PID) \
+ normal_pid_to_str (PID)
+extern char *normal_pid_to_str PARAMS ((int pid));
+#endif
+
+/* Routines for maintenance of the target structures...
+
+ add_target: Add a target to the list of all possible targets.
+
+ push_target: Make this target the top of the stack of currently used
+ targets, within its particular stratum of the stack. Result
+ is 0 if now atop the stack, nonzero if not on top (maybe
+ should warn user).
+
+ unpush_target: Remove this from the stack of currently used targets,
+ no matter where it is on the list. Returns 0 if no
+ change, 1 if removed from stack.
+
+ pop_target: Remove the top thing on the stack of current targets. */
+
+extern void
+add_target PARAMS ((struct target_ops *));
+
+extern int
+push_target PARAMS ((struct target_ops *));
+
+extern int
+unpush_target PARAMS ((struct target_ops *));
+
+extern void
+target_preopen PARAMS ((int));
+
+extern void
+pop_target PARAMS ((void));
+
+/* Struct section_table maps address ranges to file sections. It is
+ mostly used with BFD files, but can be used without (e.g. for handling
+ raw disks, or files not in formats handled by BFD). */
+
+struct section_table {
+ CORE_ADDR addr; /* Lowest address in section */
+ CORE_ADDR endaddr; /* 1+highest address in section */
+ sec_ptr sec_ptr; /* BFD section pointer */
+ bfd *bfd; /* BFD file pointer */
+};
+
+/* Builds a section table, given args BFD, SECTABLE_PTR, SECEND_PTR.
+ Returns 0 if OK, 1 on error. */
+
+extern int
+build_section_table PARAMS ((bfd *, struct section_table **,
+ struct section_table **));
+
+/* From mem-break.c */
+
+extern int
+memory_remove_breakpoint PARAMS ((CORE_ADDR, char *));
+
+extern int
+memory_insert_breakpoint PARAMS ((CORE_ADDR, char *));
+
+/* From target.c */
+
+void
+noprocess PARAMS ((void));
+
+void
+find_default_attach PARAMS ((char *, int));
+
+void
+find_default_create_inferior PARAMS ((char *, char *, char **));
+
+struct target_ops *
+find_core_target PARAMS ((void));
+
+#endif /* !defined (TARGET_H) */
diff --git a/gnu/usr.bin/gdb/gdb/terminal.h b/gnu/usr.bin/gdb/gdb/terminal.h
new file mode 100644
index 0000000..f76fa90
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/terminal.h
@@ -0,0 +1,62 @@
+/* Terminal interface definitions for GDB, the GNU Debugger.
+ Copyright 1986, 1989, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (TERMINAL_H)
+#define TERMINAL_H 1
+
+#if !defined(__GO32__) && !defined (HAVE_TERMIOS)
+
+/* Define a common set of macros -- BSD based -- and redefine whatever
+ the system offers to make it look like that. FIXME: serial.h and
+ ser-*.c deal with this in a much cleaner fashion; as soon as stuff
+ is converted to use them, can get rid of this crap. */
+
+#ifdef HAVE_TERMIO
+
+#include <termio.h>
+
+#undef TIOCGETP
+#define TIOCGETP TCGETA
+#undef TIOCSETN
+#define TIOCSETN TCSETA
+#undef TIOCSETP
+#define TIOCSETP TCSETAF
+#define TERMINAL struct termio
+
+#else /* sgtty */
+
+#include <fcntl.h>
+#include <sgtty.h>
+#include <sys/ioctl.h>
+#define TERMINAL struct sgttyb
+
+#endif /* sgtty */
+#endif /* termio or sgtty */
+
+extern void new_tty PARAMS ((void));
+
+/* Do we have job control? Can be assumed to always be the same within
+ a given run of GDB. In inflow.c. */
+extern int job_control;
+
+/* Set the process group of the caller to its own pid, or do nothing if
+ we lack job control. */
+extern int gdb_setpgid PARAMS ((void));
+
+#endif /* !defined (TERMINAL_H) */
diff --git a/gnu/usr.bin/gdb/gdb/thread.c b/gnu/usr.bin/gdb/gdb/thread.c
new file mode 100644
index 0000000..f14b41f
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/thread.c
@@ -0,0 +1,248 @@
+/* Multi-process/thread control for GDB, the GNU debugger.
+ Copyright 1986, 1987, 1988, 1993
+
+ Contributed by Lynx Real-Time Systems, Inc. Los Gatos, CA.
+ Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "symtab.h"
+#include "frame.h"
+#include "inferior.h"
+#include "environ.h"
+#include "value.h"
+#include "target.h"
+#include "thread.h"
+#include "command.h"
+
+#include <sys/types.h>
+#include <signal.h>
+
+/*#include "lynxos-core.h"*/
+
+struct thread_info
+{
+ struct thread_info *next;
+ int pid; /* Actual process id */
+ int num; /* Convenient handle */
+};
+
+static struct thread_info *thread_list = NULL;
+static int highest_thread_num;
+
+static void thread_command PARAMS ((char * tidstr, int from_tty));
+
+static void prune_threads PARAMS ((void));
+
+static void thread_switch PARAMS ((int pid));
+
+static struct thread_info * find_thread_id PARAMS ((int num));
+
+void
+init_thread_list ()
+{
+ struct thread_info *tp, *tpnext;
+
+ if (!thread_list)
+ return;
+
+ for (tp = thread_list; tp; tp = tpnext)
+ {
+ tpnext = tp->next;
+ free (tp);
+ }
+
+ thread_list = NULL;
+ highest_thread_num = 0;
+}
+
+void
+add_thread (pid)
+ int pid;
+{
+ struct thread_info *tp;
+
+ tp = (struct thread_info *) xmalloc (sizeof (struct thread_info));
+
+ tp->pid = pid;
+ tp->num = ++highest_thread_num;
+ tp->next = thread_list;
+ thread_list = tp;
+}
+
+static struct thread_info *
+find_thread_id (num)
+ int num;
+{
+ struct thread_info *tp;
+
+ for (tp = thread_list; tp; tp = tp->next)
+ if (tp->num == num)
+ return tp;
+
+ return NULL;
+}
+
+int
+valid_thread_id (num)
+ int num;
+{
+ struct thread_info *tp;
+
+ for (tp = thread_list; tp; tp = tp->next)
+ if (tp->num == num)
+ return 1;
+
+ return 0;
+}
+
+int
+pid_to_thread_id (pid)
+ int pid;
+{
+ struct thread_info *tp;
+
+ for (tp = thread_list; tp; tp = tp->next)
+ if (tp->pid == pid)
+ return tp->num;
+
+ return 0;
+}
+
+int
+in_thread_list (pid)
+ int pid;
+{
+ struct thread_info *tp;
+
+ for (tp = thread_list; tp; tp = tp->next)
+ if (tp->pid == pid)
+ return 1;
+
+ return 0; /* Never heard of 'im */
+}
+
+static void
+prune_threads ()
+{
+ struct thread_info *tp, *tpprev;
+
+ tpprev = 0;
+
+ for (tp = thread_list; tp; tp = tp->next)
+ if (tp->pid == -1)
+ {
+ if (tpprev)
+ tpprev->next = tp->next;
+ else
+ thread_list = NULL;
+
+ free (tp);
+ }
+ else
+ tpprev = tp;
+}
+
+/* Print information about currently known threads */
+
+static void
+info_threads_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ struct thread_info *tp;
+ int current_pid = inferior_pid;
+
+ for (tp = thread_list; tp; tp = tp->next)
+ {
+ if (target_has_execution
+ && kill (tp->pid, 0) == -1)
+ {
+ tp->pid = -1; /* Mark it as dead */
+ continue;
+ }
+
+ if (tp->pid == current_pid)
+ printf_filtered ("* ");
+ else
+ printf_filtered (" ");
+
+ printf_filtered ("%d %s ", tp->num, target_pid_to_str (tp->pid));
+
+ thread_switch (tp->pid);
+ print_stack_frame (selected_frame, -1, 0);
+ }
+
+ thread_switch (current_pid);
+ prune_threads ();
+}
+
+/* Switch from one thread to another. */
+
+static void
+thread_switch (pid)
+ int pid;
+{
+ if (pid == inferior_pid)
+ return;
+
+ inferior_pid = pid;
+ flush_cached_frames ();
+ registers_changed ();
+ stop_pc = read_pc();
+ set_current_frame (create_new_frame (read_fp (), stop_pc));
+ stop_frame_address = FRAME_FP (get_current_frame ());
+ select_frame (get_current_frame (), 0);
+}
+
+static void
+thread_command (tidstr, from_tty)
+ char *tidstr;
+ int from_tty;
+{
+ int num;
+ struct thread_info *tp;
+
+ if (!tidstr)
+ error ("Please specify a thread ID. Use the \"info threads\" command to\n\
+see the IDs of currently known threads.");
+
+
+ num = atoi (tidstr);
+
+ tp = find_thread_id (num);
+
+ if (!tp)
+ error ("Thread ID %d not known. Use the \"info threads\" command to\n\
+see the IDs of currently known threads.", num);
+
+ thread_switch (tp->pid);
+
+ printf_filtered ("[Switching to %s]\n", target_pid_to_str (inferior_pid));
+ print_stack_frame (selected_frame, selected_frame_level, 1);
+}
+
+void
+_initialize_thread ()
+{
+ add_info ("threads", info_threads_command,
+ "IDs of currently known threads.");
+ add_com ("thread", class_info, thread_command,
+ "Use this command to switch between threads.\n\
+The new thread ID must be currently known.");
+}
diff --git a/gnu/usr.bin/gdb/gdb/thread.h b/gnu/usr.bin/gdb/gdb/thread.h
new file mode 100644
index 0000000..2ec94fc
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/thread.h
@@ -0,0 +1,36 @@
+/* Multi-process/thread control defs for GDB, the GNU debugger.
+ Copyright 1987, 1988, 1989, 1990, 1991, 1992, 1993
+
+ Contributed by Lynx Real-Time Systems, Inc. Los Gatos, CA.
+ Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef THREAD_H
+#define THREAD_H
+
+extern void init_thread_list PARAMS ((void));
+
+extern void add_thread PARAMS ((int pid));
+
+extern int in_thread_list PARAMS ((int pid));
+
+extern int pid_to_thread_id PARAMS ((int pid));
+
+extern int valid_thread_id PARAMS ((int thread));
+
+#endif /* THREAD_H */
diff --git a/gnu/usr.bin/gdb/gdb/tm-i386v.h b/gnu/usr.bin/gdb/gdb/tm-i386v.h
new file mode 100644
index 0000000..f80f519
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/tm-i386v.h
@@ -0,0 +1,309 @@
+/* Macro definitions for i386, Unix System V.
+ Copyright 1986, 1987, 1989, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (TM_I386V_H)
+#define TM_I386V_H 1
+
+/*
+ * Changes for 80386 by Pace Willisson (pace@prep.ai.mit.edu)
+ * July 1988
+ */
+
+#define TARGET_BYTE_ORDER LITTLE_ENDIAN
+
+/* turn this on when rest of gdb is ready */
+#define IEEE_FLOAT
+
+/* number of traps that happen between exec'ing the shell
+ * to run an inferior, and when we finally get to
+ * the inferior code. This is 2 on most implementations.
+ */
+#ifndef START_INFERIOR_TRAPS_EXPECTED
+#define START_INFERIOR_TRAPS_EXPECTED 4
+#endif
+
+/* Offset from address of function to start of its code.
+ Zero on most machines. */
+
+#define FUNCTION_START_OFFSET 0
+
+/* Advance PC across any function entry prologue instructions
+ to reach some "real" code. */
+
+#define SKIP_PROLOGUE(frompc) {(frompc) = i386_skip_prologue((frompc));}
+
+extern int
+i386_skip_prologue PARAMS ((int));
+
+/* Immediately after a function call, return the saved pc.
+ Can't always go through the frames for this because on some machines
+ the new frame is not set up until the new function executes
+ some instructions. */
+
+#define SAVED_PC_AFTER_CALL(frame) \
+ (read_memory_integer (read_register (SP_REGNUM), 4))
+
+/* Stack grows downward. */
+
+#define INNER_THAN <
+
+/* Sequence of bytes for breakpoint instruction. */
+
+#define BREAKPOINT {0xcc}
+
+/* Amount PC must be decremented by after a breakpoint.
+ This is often the number of bytes in BREAKPOINT
+ but not always. */
+
+#ifndef DECR_PC_AFTER_BREAK
+#define DECR_PC_AFTER_BREAK 1
+#endif
+
+/* Nonzero if instruction at PC is a return instruction. */
+
+#define ABOUT_TO_RETURN(pc) (read_memory_integer (pc, 1) == 0xc3)
+
+/* Return 1 if P points to an invalid floating point value.
+ LEN is the length in bytes -- not relevant on the 386. */
+
+#define INVALID_FLOAT(p, len) (0)
+
+/* Say how long (ordinary) registers are. */
+
+#define REGISTER_TYPE long
+
+/* Number of machine registers */
+
+#define NUM_REGS 16
+
+/* Initializer for an array of names of registers.
+ There should be NUM_REGS strings in this initializer. */
+
+/* the order of the first 8 registers must match the compiler's
+ * numbering scheme (which is the same as the 386 scheme)
+ * also, this table must match regmap in i386-pinsn.c.
+ */
+#define REGISTER_NAMES { "eax", "ecx", "edx", "ebx", \
+ "esp", "ebp", "esi", "edi", \
+ "eip", "ps", "cs", "ss", \
+ "ds", "es", "fs", "gs", \
+ }
+
+/* Register numbers of various important registers.
+ Note that some of these values are "real" register numbers,
+ and correspond to the general registers of the machine,
+ and some are "phony" register numbers which are too large
+ to be actual register numbers as far as the user is concerned
+ but do serve to get the desired values when passed to read_register. */
+
+#define FP_REGNUM 5 /* Contains address of executing stack frame */
+#define SP_REGNUM 4 /* Contains address of top of stack */
+
+#define PC_REGNUM 8
+#define PS_REGNUM 9
+
+/* Total amount of space needed to store our copies of the machine's
+ register state, the array `registers'. */
+#define REGISTER_BYTES (NUM_REGS * 4)
+
+/* Index within `registers' of the first byte of the space for
+ register N. */
+
+#define REGISTER_BYTE(N) ((N)*4)
+
+/* Number of bytes of storage in the actual machine representation
+ for register N. */
+
+#define REGISTER_RAW_SIZE(N) (4)
+
+/* Number of bytes of storage in the program's representation
+ for register N. */
+
+#define REGISTER_VIRTUAL_SIZE(N) (4)
+
+/* Largest value REGISTER_RAW_SIZE can have. */
+
+#define MAX_REGISTER_RAW_SIZE 4
+
+/* Largest value REGISTER_VIRTUAL_SIZE can have. */
+
+#define MAX_REGISTER_VIRTUAL_SIZE 4
+
+/* Nonzero if register N requires conversion
+ from raw format to virtual format. */
+
+#define REGISTER_CONVERTIBLE(N) (0)
+
+/* Convert data from raw format for register REGNUM
+ to virtual format for register REGNUM. */
+
+#define REGISTER_CONVERT_TO_VIRTUAL(REGNUM,FROM,TO) \
+ {memcpy ((TO), (FROM), 4);}
+
+/* Convert data from virtual format for register REGNUM
+ to raw format for register REGNUM. */
+
+#define REGISTER_CONVERT_TO_RAW(REGNUM,FROM,TO) \
+ {memcpy ((TO), (FROM), 4);}
+
+/* Return the GDB type object for the "standard" data type
+ of data in register N. */
+/* Perhaps si and di should go here, but potentially they could be
+ used for things other than address. */
+#define REGISTER_VIRTUAL_TYPE(N) \
+ ((N) == PC_REGNUM || (N) == FP_REGNUM || (N) == SP_REGNUM ? \
+ lookup_pointer_type (builtin_type_void) : builtin_type_int)
+
+/* Store the address of the place in which to copy the structure the
+ subroutine will return. This is called from call_function. */
+
+#define STORE_STRUCT_RETURN(ADDR, SP) \
+ { (SP) -= sizeof (ADDR); \
+ write_memory ((SP), (char *) &(ADDR), sizeof (ADDR)); }
+
+/* Extract from an array REGBUF containing the (raw) register state
+ a function return value of type TYPE, and copy that, in virtual format,
+ into VALBUF. */
+
+#define EXTRACT_RETURN_VALUE(TYPE,REGBUF,VALBUF) \
+ memcpy ((VALBUF), (REGBUF), TYPE_LENGTH (TYPE))
+
+/* Write into appropriate registers a function return value
+ of type TYPE, given in virtual format. */
+
+#define STORE_RETURN_VALUE(TYPE,VALBUF) \
+ write_register_bytes (0, VALBUF, TYPE_LENGTH (TYPE))
+
+/* Extract from an array REGBUF containing the (raw) register state
+ the address in which a function should return its structure value,
+ as a CORE_ADDR (or an expression that can be used as one). */
+
+#define EXTRACT_STRUCT_VALUE_ADDRESS(REGBUF) (*(int *)(REGBUF))
+
+
+/* Describe the pointer in each stack frame to the previous stack frame
+ (its caller). */
+
+/* FRAME_CHAIN takes a frame's nominal address
+ and produces the frame's chain-pointer. */
+
+#define FRAME_CHAIN(thisframe) \
+ (!inside_entry_file ((thisframe)->pc) ? \
+ read_memory_integer ((thisframe)->frame, 4) :\
+ 0)
+
+/* Define other aspects of the stack frame. */
+
+/* A macro that tells us whether the function invocation represented
+ by FI does not have a frame on the stack associated with it. If it
+ does not, FRAMELESS is set to 1, else 0. */
+#define FRAMELESS_FUNCTION_INVOCATION(FI, FRAMELESS) \
+ (FRAMELESS) = frameless_look_for_prologue(FI)
+
+#define FRAME_SAVED_PC(FRAME) (read_memory_integer ((FRAME)->frame + 4, 4))
+
+#define FRAME_ARGS_ADDRESS(fi) ((fi)->frame)
+
+#define FRAME_LOCALS_ADDRESS(fi) ((fi)->frame)
+
+/* Return number of args passed to a frame.
+ Can return -1, meaning no way to tell. */
+
+#define FRAME_NUM_ARGS(numargs, fi) (numargs) = -1
+
+#ifdef __STDC__ /* Forward decl's for prototypes */
+struct frame_info;
+struct frame_saved_regs;
+#endif
+
+extern int
+i386_frame_num_args PARAMS ((struct frame_info *));
+
+/* Return number of bytes at start of arglist that are not really args. */
+
+#define FRAME_ARGS_SKIP 8
+
+/* Put here the code to store, into a struct frame_saved_regs,
+ the addresses of the saved registers of frame described by FRAME_INFO.
+ This includes special registers such as pc and fp saved in special
+ ways in the stack frame. sp is even more special:
+ the address we return for it IS the sp for the next frame. */
+
+#define FRAME_FIND_SAVED_REGS(frame_info, frame_saved_regs) \
+{ i386_frame_find_saved_regs ((frame_info), &(frame_saved_regs)); }
+
+extern void
+i386_frame_find_saved_regs PARAMS ((struct frame_info *,
+ struct frame_saved_regs *));
+
+
+/* Things needed for making the inferior call functions. */
+
+/* Push an empty stack frame, to record the current PC, etc. */
+
+#define PUSH_DUMMY_FRAME { i386_push_dummy_frame (); }
+
+extern void
+i386_push_dummy_frame PARAMS ((void));
+
+/* Discard from the stack the innermost frame, restoring all registers. */
+
+#define POP_FRAME { i386_pop_frame (); }
+
+extern void
+i386_pop_frame PARAMS ((void));
+
+/* this is
+ * call 11223344 (32 bit relative)
+ * int3
+ */
+
+#define CALL_DUMMY { 0x223344e8, 0xcc11 }
+
+#define CALL_DUMMY_LENGTH 8
+
+#define CALL_DUMMY_START_OFFSET 0 /* Start execution at beginning of dummy */
+
+/* Insert the specified number of args and function address
+ into a call sequence of the above form stored at DUMMYNAME. */
+
+#define FIX_CALL_DUMMY(dummyname, pc, fun, nargs, args, type, gcc_p) \
+{ \
+ int from, to, delta, loc; \
+ loc = (int)(read_register (SP_REGNUM) - CALL_DUMMY_LENGTH); \
+ from = loc + 5; \
+ to = (int)(fun); \
+ delta = to - from; \
+ *((char *)(dummyname) + 1) = (delta & 0xff); \
+ *((char *)(dummyname) + 2) = ((delta >> 8) & 0xff); \
+ *((char *)(dummyname) + 3) = ((delta >> 16) & 0xff); \
+ *((char *)(dummyname) + 4) = ((delta >> 24) & 0xff); \
+}
+
+extern void
+print_387_control_word PARAMS ((unsigned int));
+
+extern void
+print_387_status_word PARAMS ((unsigned int));
+
+/* Offset from SP to first arg on stack at first instruction of a function */
+
+#define SP_ARG0 (1 * 4)
+
+#endif /* !defined (TM_I386V_H) */
diff --git a/gnu/usr.bin/gdb/gdb/tm.h b/gnu/usr.bin/gdb/gdb/tm.h
new file mode 100644
index 0000000..25b66c7
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/tm.h
@@ -0,0 +1,76 @@
+/* Macro definitions for i386 running under BSD Unix.
+ Copyright 1986, 1987, 1989, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Override number of expected traps from sysv. */
+#define START_INFERIOR_TRAPS_EXPECTED 2
+
+/* Most definitions from sysv could be used. */
+#include "tm-i386v.h"
+
+/* 386BSD cannot handle the segment registers. */
+/* BSDI can't handle them either. */
+#undef NUM_REGS
+#define NUM_REGS 10
+
+/* On 386 bsd, sigtramp is above the user stack and immediately below
+ the user area. Using constants here allows for cross debugging.
+ These are tested for BSDI but should work on 386BSD. */
+#define SIGTRAMP_START 0xfdbfdfc0
+#define SIGTRAMP_END 0xfdbfe000
+
+/* The following redefines make backtracing through sigtramp work.
+ They manufacture a fake sigtramp frame and obtain the saved pc in sigtramp
+ from the sigcontext structure which is pushed by the kernel on the
+ user stack, along with a pointer to it. */
+
+/* FRAME_CHAIN takes a frame's nominal address and produces the frame's
+ chain-pointer.
+ In the case of the i386, the frame's nominal address
+ is the address of a 4-byte word containing the calling frame's address. */
+#undef FRAME_CHAIN
+#define FRAME_CHAIN(thisframe) \
+ (thisframe->signal_handler_caller \
+ ? thisframe->frame \
+ : (!inside_entry_file ((thisframe)->pc) \
+ ? read_memory_integer ((thisframe)->frame, 4) \
+ : 0))
+
+/* A macro that tells us whether the function invocation represented
+ by FI does not have a frame on the stack associated with it. If it
+ does not, FRAMELESS is set to 1, else 0. */
+#undef FRAMELESS_FUNCTION_INVOCATION
+#define FRAMELESS_FUNCTION_INVOCATION(FI, FRAMELESS) \
+ do { \
+ if ((FI)->signal_handler_caller) \
+ (FRAMELESS) = 0; \
+ else \
+ (FRAMELESS) = frameless_look_for_prologue(FI); \
+ } while (0)
+
+/* Saved Pc. Get it from sigcontext if within sigtramp. */
+
+/* Offset to saved PC in sigcontext, from <sys/signal.h>. */
+#define SIGCONTEXT_PC_OFFSET 20
+
+#undef FRAME_SAVED_PC(FRAME)
+#define FRAME_SAVED_PC(FRAME) \
+ (((FRAME)->signal_handler_caller \
+ ? sigtramp_saved_pc (FRAME) \
+ : read_memory_integer ((FRAME)->frame + 4, 4)) \
+ )
diff --git a/gnu/usr.bin/gdb/gdb/typeprint.c b/gnu/usr.bin/gdb/gdb/typeprint.c
new file mode 100644
index 0000000..5e13e07
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/typeprint.c
@@ -0,0 +1,297 @@
+/* Language independent support for printing types for GDB, the GNU debugger.
+ Copyright 1986, 1988, 1989, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "obstack.h"
+#include "bfd.h" /* Binary File Description */
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "expression.h"
+#include "value.h"
+#include "gdbcore.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "target.h"
+#include "language.h"
+#include "demangle.h"
+
+#include <string.h>
+#include <errno.h>
+
+static void
+ptype_command PARAMS ((char *, int));
+
+static struct type *
+ptype_eval PARAMS ((struct expression *));
+
+static void
+whatis_command PARAMS ((char *, int));
+
+static void
+whatis_exp PARAMS ((char *, int));
+
+/* Print a description of a type TYPE in the form of a declaration of a
+ variable named VARSTRING. (VARSTRING is demangled if necessary.)
+ Output goes to STREAM (via stdio).
+ If SHOW is positive, we show the contents of the outermost level
+ of structure even if there is a type name that could be used instead.
+ If SHOW is negative, we never show the details of elements' types. */
+
+void
+type_print (type, varstring, stream, show)
+ struct type *type;
+ char *varstring;
+ FILE *stream;
+ int show;
+{
+ LA_PRINT_TYPE (type, varstring, stream, show, 0);
+}
+
+/* Print type of EXP, or last thing in value history if EXP == NULL.
+ show is passed to type_print. */
+
+static void
+whatis_exp (exp, show)
+ char *exp;
+ int show;
+{
+ struct expression *expr;
+ register value val;
+ register struct cleanup *old_chain = NULL;
+
+ if (exp)
+ {
+ expr = parse_expression (exp);
+ old_chain = make_cleanup (free_current_contents, &expr);
+ val = evaluate_type (expr);
+ }
+ else
+ val = access_value_history (0);
+
+ printf_filtered ("type = ");
+ type_print (VALUE_TYPE (val), "", stdout, show);
+ printf_filtered ("\n");
+
+ if (exp)
+ do_cleanups (old_chain);
+}
+
+/* ARGSUSED */
+static void
+whatis_command (exp, from_tty)
+ char *exp;
+ int from_tty;
+{
+ /* Most of the time users do not want to see all the fields
+ in a structure. If they do they can use the "ptype" command.
+ Hence the "-1" below. */
+ whatis_exp (exp, -1);
+}
+
+/* Simple subroutine for ptype_command. */
+
+static struct type *
+ptype_eval (exp)
+ struct expression *exp;
+{
+ if (exp->elts[0].opcode == OP_TYPE)
+ {
+ return (exp->elts[1].type);
+ }
+ else
+ {
+ return (NULL);
+ }
+}
+
+/* TYPENAME is either the name of a type, or an expression. */
+
+/* ARGSUSED */
+static void
+ptype_command (typename, from_tty)
+ char *typename;
+ int from_tty;
+{
+ register struct type *type;
+ struct expression *expr;
+ register struct cleanup *old_chain;
+
+ if (typename == NULL)
+ {
+ /* Print type of last thing in value history. */
+ whatis_exp (typename, 1);
+ }
+ else
+ {
+ expr = parse_expression (typename);
+ old_chain = make_cleanup (free_current_contents, &expr);
+ type = ptype_eval (expr);
+ if (type != NULL)
+ {
+ /* User did "ptype <typename>" */
+ printf_filtered ("type = ");
+ type_print (type, "", stdout, 1);
+ printf_filtered ("\n");
+ do_cleanups (old_chain);
+ }
+ else
+ {
+ /* User did "ptype <symbolname>" */
+ do_cleanups (old_chain);
+ whatis_exp (typename, 1);
+ }
+ }
+}
+
+/* Print integral scalar data VAL, of type TYPE, onto stdio stream STREAM.
+ Used to print data from type structures in a specified type. For example,
+ array bounds may be characters or booleans in some languages, and this
+ allows the ranges to be printed in their "natural" form rather than as
+ decimal integer values.
+
+ FIXME: This is here simply because only the type printing routines
+ currently use it, and it wasn't clear if it really belonged somewhere
+ else (like printcmd.c). There are a lot of other gdb routines that do
+ something similar, but they are generally concerned with printing values
+ that come from the inferior in target byte order and target size. */
+
+void
+print_type_scalar (type, val, stream)
+ struct type *type;
+ LONGEST val;
+ FILE *stream;
+{
+ unsigned int i;
+ unsigned len;
+
+ switch (TYPE_CODE (type))
+ {
+
+ case TYPE_CODE_ENUM:
+ len = TYPE_NFIELDS (type);
+ for (i = 0; i < len; i++)
+ {
+ if (TYPE_FIELD_BITPOS (type, i) == val)
+ {
+ break;
+ }
+ }
+ if (i < len)
+ {
+ fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
+ }
+ else
+ {
+ print_longest (stream, 'd', 0, val);
+ }
+ break;
+
+ case TYPE_CODE_INT:
+ print_longest (stream, TYPE_UNSIGNED (type) ? 'u' : 'd', 0, val);
+ break;
+
+ case TYPE_CODE_CHAR:
+ LA_PRINT_CHAR ((unsigned char) val, stream);
+ break;
+
+ case TYPE_CODE_BOOL:
+ fprintf_filtered (stream, val ? "TRUE" : "FALSE");
+ break;
+
+ case TYPE_CODE_UNDEF:
+ case TYPE_CODE_PTR:
+ case TYPE_CODE_ARRAY:
+ case TYPE_CODE_STRUCT:
+ case TYPE_CODE_UNION:
+ case TYPE_CODE_FUNC:
+ case TYPE_CODE_FLT:
+ case TYPE_CODE_VOID:
+ case TYPE_CODE_SET:
+ case TYPE_CODE_RANGE:
+ case TYPE_CODE_STRING:
+ case TYPE_CODE_ERROR:
+ case TYPE_CODE_MEMBER:
+ case TYPE_CODE_METHOD:
+ case TYPE_CODE_REF:
+ error ("internal error: unhandled type in print_type_scalar");
+ break;
+
+ default:
+ error ("Invalid type code in symbol table.");
+ }
+ fflush (stream);
+}
+
+#if MAINTENANCE_CMDS
+
+/* Dump details of a type specified either directly or indirectly.
+ Uses the same sort of type lookup mechanism as ptype_command()
+ and whatis_command(). */
+
+void
+maintenance_print_type (typename, from_tty)
+ char *typename;
+ int from_tty;
+{
+ register value val;
+ register struct type *type;
+ register struct cleanup *old_chain;
+ struct expression *expr;
+
+ if (typename != NULL)
+ {
+ expr = parse_expression (typename);
+ old_chain = make_cleanup (free_current_contents, &expr);
+ if (expr -> elts[0].opcode == OP_TYPE)
+ {
+ /* The user expression names a type directly, just use that type. */
+ type = expr -> elts[1].type;
+ }
+ else
+ {
+ /* The user expression may name a type indirectly by naming an
+ object of that type. Find that indirectly named type. */
+ val = evaluate_type (expr);
+ type = VALUE_TYPE (val);
+ }
+ if (type != NULL)
+ {
+ recursive_dump_type (type, 0);
+ }
+ do_cleanups (old_chain);
+ }
+}
+
+#endif /* MAINTENANCE_CMDS */
+
+
+void
+_initialize_typeprint ()
+{
+
+ add_com ("ptype", class_vars, ptype_command,
+ "Print definition of type TYPE.\n\
+Argument may be a type name defined by typedef, or \"struct STRUCTNAME\"\n\
+or \"union UNIONNAME\" or \"enum ENUMNAME\".\n\
+The selected stack frame's lexical context is used to look up the name.");
+
+ add_com ("whatis", class_vars, whatis_command,
+ "Print data type of expression EXP.");
+
+}
diff --git a/gnu/usr.bin/gdb/gdb/typeprint.h b/gnu/usr.bin/gdb/gdb/typeprint.h
new file mode 100644
index 0000000..e6740db
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/typeprint.h
@@ -0,0 +1,21 @@
+/* Language independent support for printing types for GDB, the GNU debugger.
+ Copyright 1986, 1988, 1989, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+void
+print_type_scalar PARAMS ((struct type *type, LONGEST, FILE *));
diff --git a/gnu/usr.bin/gdb/gdb/utils.c b/gnu/usr.bin/gdb/gdb/utils.c
new file mode 100644
index 0000000..38010e3
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/utils.c
@@ -0,0 +1,1547 @@
+/* General utility routines for GDB, the GNU debugger.
+ Copyright 1986, 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#if !defined(__GO32__)
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <pwd.h>
+#endif
+#include <varargs.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "signals.h"
+#include "gdbcmd.h"
+#include "serial.h"
+#include "terminal.h" /* For job_control */
+#include "bfd.h"
+#include "target.h"
+#include "demangle.h"
+#include "expression.h"
+#include "language.h"
+
+/* Prototypes for local functions */
+
+#if defined (NO_MMALLOC) || defined (NO_MMALLOC_CHECK)
+#else
+
+static void
+malloc_botch PARAMS ((void));
+
+#endif /* NO_MMALLOC, etc */
+
+static void
+fatal_dump_core (); /* Can't prototype with <varargs.h> usage... */
+
+static void
+prompt_for_continue PARAMS ((void));
+
+static void
+set_width_command PARAMS ((char *, int, struct cmd_list_element *));
+
+/* If this definition isn't overridden by the header files, assume
+ that isatty and fileno exist on this system. */
+#ifndef ISATTY
+#define ISATTY(FP) (isatty (fileno (FP)))
+#endif
+
+/* Chain of cleanup actions established with make_cleanup,
+ to be executed if an error happens. */
+
+static struct cleanup *cleanup_chain;
+
+/* Nonzero means a quit has been requested. */
+
+int quit_flag;
+
+/* Nonzero means quit immediately if Control-C is typed now, rather
+ than waiting until QUIT is executed. Be careful in setting this;
+ code which executes with immediate_quit set has to be very careful
+ about being able to deal with being interrupted at any time. It is
+ almost always better to use QUIT; the only exception I can think of
+ is being able to quit out of a system call (using EINTR loses if
+ the SIGINT happens between the previous QUIT and the system call).
+ To immediately quit in the case in which a SIGINT happens between
+ the previous QUIT and setting immediate_quit (desirable anytime we
+ expect to block), call QUIT after setting immediate_quit. */
+
+int immediate_quit;
+
+/* Nonzero means that encoded C++ names should be printed out in their
+ C++ form rather than raw. */
+
+int demangle = 1;
+
+/* Nonzero means that encoded C++ names should be printed out in their
+ C++ form even in assembler language displays. If this is set, but
+ DEMANGLE is zero, names are printed raw, i.e. DEMANGLE controls. */
+
+int asm_demangle = 0;
+
+/* Nonzero means that strings with character values >0x7F should be printed
+ as octal escapes. Zero means just print the value (e.g. it's an
+ international character, and the terminal or window can cope.) */
+
+int sevenbit_strings = 0;
+
+/* String to be printed before error messages, if any. */
+
+char *error_pre_print;
+char *warning_pre_print = "\nwarning: ";
+
+/* Add a new cleanup to the cleanup_chain,
+ and return the previous chain pointer
+ to be passed later to do_cleanups or discard_cleanups.
+ Args are FUNCTION to clean up with, and ARG to pass to it. */
+
+struct cleanup *
+make_cleanup (function, arg)
+ void (*function) PARAMS ((PTR));
+ PTR arg;
+{
+ register struct cleanup *new
+ = (struct cleanup *) xmalloc (sizeof (struct cleanup));
+ register struct cleanup *old_chain = cleanup_chain;
+
+ new->next = cleanup_chain;
+ new->function = function;
+ new->arg = arg;
+ cleanup_chain = new;
+
+ return old_chain;
+}
+
+/* Discard cleanups and do the actions they describe
+ until we get back to the point OLD_CHAIN in the cleanup_chain. */
+
+void
+do_cleanups (old_chain)
+ register struct cleanup *old_chain;
+{
+ register struct cleanup *ptr;
+ while ((ptr = cleanup_chain) != old_chain)
+ {
+ cleanup_chain = ptr->next; /* Do this first incase recursion */
+ (*ptr->function) (ptr->arg);
+ free (ptr);
+ }
+}
+
+/* Discard cleanups, not doing the actions they describe,
+ until we get back to the point OLD_CHAIN in the cleanup_chain. */
+
+void
+discard_cleanups (old_chain)
+ register struct cleanup *old_chain;
+{
+ register struct cleanup *ptr;
+ while ((ptr = cleanup_chain) != old_chain)
+ {
+ cleanup_chain = ptr->next;
+ free ((PTR)ptr);
+ }
+}
+
+/* Set the cleanup_chain to 0, and return the old cleanup chain. */
+struct cleanup *
+save_cleanups ()
+{
+ struct cleanup *old_chain = cleanup_chain;
+
+ cleanup_chain = 0;
+ return old_chain;
+}
+
+/* Restore the cleanup chain from a previously saved chain. */
+void
+restore_cleanups (chain)
+ struct cleanup *chain;
+{
+ cleanup_chain = chain;
+}
+
+/* This function is useful for cleanups.
+ Do
+
+ foo = xmalloc (...);
+ old_chain = make_cleanup (free_current_contents, &foo);
+
+ to arrange to free the object thus allocated. */
+
+void
+free_current_contents (location)
+ char **location;
+{
+ free (*location);
+}
+
+/* Provide a known function that does nothing, to use as a base for
+ for a possibly long chain of cleanups. This is useful where we
+ use the cleanup chain for handling normal cleanups as well as dealing
+ with cleanups that need to be done as a result of a call to error().
+ In such cases, we may not be certain where the first cleanup is, unless
+ we have a do-nothing one to always use as the base. */
+
+/* ARGSUSED */
+void
+null_cleanup (arg)
+ char **arg;
+{
+}
+
+
+/* Provide a hook for modules wishing to print their own warning messages
+ to set up the terminal state in a compatible way, without them having
+ to import all the target_<...> macros. */
+
+void
+warning_setup ()
+{
+ target_terminal_ours ();
+ wrap_here(""); /* Force out any buffered output */
+ fflush (stdout);
+}
+
+/* Print a warning message.
+ The first argument STRING is the warning message, used as a fprintf string,
+ and the remaining args are passed as arguments to it.
+ The primary difference between warnings and errors is that a warning
+ does not force the return to command level. */
+
+/* VARARGS */
+void
+warning (va_alist)
+ va_dcl
+{
+ va_list args;
+ char *string;
+
+ va_start (args);
+ target_terminal_ours ();
+ wrap_here(""); /* Force out any buffered output */
+ fflush (stdout);
+ if (warning_pre_print)
+ fprintf (stderr, warning_pre_print);
+ string = va_arg (args, char *);
+ vfprintf (stderr, string, args);
+ fprintf (stderr, "\n");
+ va_end (args);
+}
+
+/* Print an error message and return to command level.
+ The first argument STRING is the error message, used as a fprintf string,
+ and the remaining args are passed as arguments to it. */
+
+/* VARARGS */
+NORETURN void
+error (va_alist)
+ va_dcl
+{
+ va_list args;
+ char *string;
+
+ va_start (args);
+ target_terminal_ours ();
+ wrap_here(""); /* Force out any buffered output */
+ fflush (stdout);
+ if (error_pre_print)
+ fprintf_filtered (stderr, error_pre_print);
+ string = va_arg (args, char *);
+ vfprintf_filtered (stderr, string, args);
+ fprintf_filtered (stderr, "\n");
+ va_end (args);
+ return_to_top_level (RETURN_ERROR);
+}
+
+/* Print an error message and exit reporting failure.
+ This is for a error that we cannot continue from.
+ The arguments are printed a la printf.
+
+ This function cannot be declared volatile (NORETURN) in an
+ ANSI environment because exit() is not declared volatile. */
+
+/* VARARGS */
+NORETURN void
+fatal (va_alist)
+ va_dcl
+{
+ va_list args;
+ char *string;
+
+ va_start (args);
+ string = va_arg (args, char *);
+ fprintf (stderr, "\ngdb: ");
+ vfprintf (stderr, string, args);
+ fprintf (stderr, "\n");
+ va_end (args);
+ exit (1);
+}
+
+/* Print an error message and exit, dumping core.
+ The arguments are printed a la printf (). */
+
+/* VARARGS */
+static void
+fatal_dump_core (va_alist)
+ va_dcl
+{
+ va_list args;
+ char *string;
+
+ va_start (args);
+ string = va_arg (args, char *);
+ /* "internal error" is always correct, since GDB should never dump
+ core, no matter what the input. */
+ fprintf (stderr, "\ngdb internal error: ");
+ vfprintf (stderr, string, args);
+ fprintf (stderr, "\n");
+ va_end (args);
+
+ signal (SIGQUIT, SIG_DFL);
+ kill (getpid (), SIGQUIT);
+ /* We should never get here, but just in case... */
+ exit (1);
+}
+
+/* The strerror() function can return NULL for errno values that are
+ out of range. Provide a "safe" version that always returns a
+ printable string. */
+
+char *
+safe_strerror (errnum)
+ int errnum;
+{
+ char *msg;
+ static char buf[32];
+
+ if ((msg = strerror (errnum)) == NULL)
+ {
+ sprintf (buf, "(undocumented errno %d)", errnum);
+ msg = buf;
+ }
+ return (msg);
+}
+
+/* The strsignal() function can return NULL for signal values that are
+ out of range. Provide a "safe" version that always returns a
+ printable string. */
+
+char *
+safe_strsignal (signo)
+ int signo;
+{
+ char *msg;
+ static char buf[32];
+
+ if ((msg = strsignal (signo)) == NULL)
+ {
+ sprintf (buf, "(undocumented signal %d)", signo);
+ msg = buf;
+ }
+ return (msg);
+}
+
+
+/* Print the system error message for errno, and also mention STRING
+ as the file name for which the error was encountered.
+ Then return to command level. */
+
+void
+perror_with_name (string)
+ char *string;
+{
+ char *err;
+ char *combined;
+
+ err = safe_strerror (errno);
+ combined = (char *) alloca (strlen (err) + strlen (string) + 3);
+ strcpy (combined, string);
+ strcat (combined, ": ");
+ strcat (combined, err);
+
+ /* I understand setting these is a matter of taste. Still, some people
+ may clear errno but not know about bfd_error. Doing this here is not
+ unreasonable. */
+ bfd_error = no_error;
+ errno = 0;
+
+ error ("%s.", combined);
+}
+
+/* Print the system error message for ERRCODE, and also mention STRING
+ as the file name for which the error was encountered. */
+
+void
+print_sys_errmsg (string, errcode)
+ char *string;
+ int errcode;
+{
+ char *err;
+ char *combined;
+
+ err = safe_strerror (errcode);
+ combined = (char *) alloca (strlen (err) + strlen (string) + 3);
+ strcpy (combined, string);
+ strcat (combined, ": ");
+ strcat (combined, err);
+
+ fprintf (stderr, "%s.\n", combined);
+}
+
+/* Control C eventually causes this to be called, at a convenient time. */
+
+void
+quit ()
+{
+ serial_t stdout_serial = serial_fdopen (1);
+
+ target_terminal_ours ();
+ wrap_here ((char *)0); /* Force out any pending output */
+
+ SERIAL_FLUSH_OUTPUT (stdout_serial);
+
+ SERIAL_UN_FDOPEN (stdout_serial);
+
+ /* Don't use *_filtered; we don't want to prompt the user to continue. */
+ if (error_pre_print)
+ fprintf (stderr, error_pre_print);
+
+ if (job_control
+ /* If there is no terminal switching for this target, then we can't
+ possibly get screwed by the lack of job control. */
+ || current_target->to_terminal_ours == NULL)
+ fprintf (stderr, "Quit\n");
+ else
+ fprintf (stderr,
+ "Quit (expect signal SIGINT when the program is resumed)\n");
+ return_to_top_level (RETURN_QUIT);
+}
+
+
+#ifdef __GO32__
+
+/* In the absence of signals, poll keyboard for a quit.
+ Called from #define QUIT pollquit() in xm-go32.h. */
+
+void
+pollquit()
+{
+ if (kbhit ())
+ {
+ int k = getkey ();
+ if (k == 1)
+ quit_flag = 1;
+ else if (k == 2)
+ immediate_quit = 1;
+ quit ();
+ }
+}
+
+#endif
+
+/* Control C comes here */
+
+void
+request_quit (signo)
+ int signo;
+{
+ quit_flag = 1;
+
+#ifdef USG
+ /* Restore the signal handler. */
+ signal (signo, request_quit);
+#endif
+
+ if (immediate_quit)
+ quit ();
+}
+
+
+/* Memory management stuff (malloc friends). */
+
+#if defined (NO_MMALLOC)
+
+PTR
+mmalloc (md, size)
+ PTR md;
+ long size;
+{
+ return (malloc (size));
+}
+
+PTR
+mrealloc (md, ptr, size)
+ PTR md;
+ PTR ptr;
+ long size;
+{
+ if (ptr == 0) /* Guard against old realloc's */
+ return malloc (size);
+ else
+ return realloc (ptr, size);
+}
+
+void
+mfree (md, ptr)
+ PTR md;
+ PTR ptr;
+{
+ free (ptr);
+}
+
+#endif /* NO_MMALLOC */
+
+#if defined (NO_MMALLOC) || defined (NO_MMALLOC_CHECK)
+
+void
+init_malloc (md)
+ PTR md;
+{
+}
+
+#else /* have mmalloc and want corruption checking */
+
+static void
+malloc_botch ()
+{
+ fatal_dump_core ("Memory corruption");
+}
+
+/* Attempt to install hooks in mmalloc/mrealloc/mfree for the heap specified
+ by MD, to detect memory corruption. Note that MD may be NULL to specify
+ the default heap that grows via sbrk.
+
+ Note that for freshly created regions, we must call mmcheck prior to any
+ mallocs in the region. Otherwise, any region which was allocated prior to
+ installing the checking hooks, which is later reallocated or freed, will
+ fail the checks! The mmcheck function only allows initial hooks to be
+ installed before the first mmalloc. However, anytime after we have called
+ mmcheck the first time to install the checking hooks, we can call it again
+ to update the function pointer to the memory corruption handler.
+
+ Returns zero on failure, non-zero on success. */
+
+void
+init_malloc (md)
+ PTR md;
+{
+ if (!mmcheck (md, malloc_botch))
+ {
+ warning ("internal error: failed to install memory consistency checks");
+ }
+
+ mmtrace ();
+}
+
+#endif /* Have mmalloc and want corruption checking */
+
+/* Called when a memory allocation fails, with the number of bytes of
+ memory requested in SIZE. */
+
+NORETURN void
+nomem (size)
+ long size;
+{
+ if (size > 0)
+ {
+ fatal ("virtual memory exhausted: can't allocate %ld bytes.", size);
+ }
+ else
+ {
+ fatal ("virtual memory exhausted.");
+ }
+}
+
+/* Like mmalloc but get error if no storage available, and protect against
+ the caller wanting to allocate zero bytes. Whether to return NULL for
+ a zero byte request, or translate the request into a request for one
+ byte of zero'd storage, is a religious issue. */
+
+PTR
+xmmalloc (md, size)
+ PTR md;
+ long size;
+{
+ register PTR val;
+
+ if (size == 0)
+ {
+ val = NULL;
+ }
+ else if ((val = mmalloc (md, size)) == NULL)
+ {
+ nomem (size);
+ }
+ return (val);
+}
+
+/* Like mrealloc but get error if no storage available. */
+
+PTR
+xmrealloc (md, ptr, size)
+ PTR md;
+ PTR ptr;
+ long size;
+{
+ register PTR val;
+
+ if (ptr != NULL)
+ {
+ val = mrealloc (md, ptr, size);
+ }
+ else
+ {
+ val = mmalloc (md, size);
+ }
+ if (val == NULL)
+ {
+ nomem (size);
+ }
+ return (val);
+}
+
+/* Like malloc but get error if no storage available, and protect against
+ the caller wanting to allocate zero bytes. */
+
+PTR
+xmalloc (size)
+ long size;
+{
+ return (xmmalloc ((PTR) NULL, size));
+}
+
+/* Like mrealloc but get error if no storage available. */
+
+PTR
+xrealloc (ptr, size)
+ PTR ptr;
+ long size;
+{
+ return (xmrealloc ((PTR) NULL, ptr, size));
+}
+
+
+/* My replacement for the read system call.
+ Used like `read' but keeps going if `read' returns too soon. */
+
+int
+myread (desc, addr, len)
+ int desc;
+ char *addr;
+ int len;
+{
+ register int val;
+ int orglen = len;
+
+ while (len > 0)
+ {
+ val = read (desc, addr, len);
+ if (val < 0)
+ return val;
+ if (val == 0)
+ return orglen - len;
+ len -= val;
+ addr += val;
+ }
+ return orglen;
+}
+
+/* Make a copy of the string at PTR with SIZE characters
+ (and add a null character at the end in the copy).
+ Uses malloc to get the space. Returns the address of the copy. */
+
+char *
+savestring (ptr, size)
+ const char *ptr;
+ int size;
+{
+ register char *p = (char *) xmalloc (size + 1);
+ memcpy (p, ptr, size);
+ p[size] = 0;
+ return p;
+}
+
+char *
+msavestring (md, ptr, size)
+ PTR md;
+ const char *ptr;
+ int size;
+{
+ register char *p = (char *) xmmalloc (md, size + 1);
+ memcpy (p, ptr, size);
+ p[size] = 0;
+ return p;
+}
+
+/* The "const" is so it compiles under DGUX (which prototypes strsave
+ in <string.h>. FIXME: This should be named "xstrsave", shouldn't it?
+ Doesn't real strsave return NULL if out of memory? */
+char *
+strsave (ptr)
+ const char *ptr;
+{
+ return savestring (ptr, strlen (ptr));
+}
+
+char *
+mstrsave (md, ptr)
+ PTR md;
+ const char *ptr;
+{
+ return (msavestring (md, ptr, strlen (ptr)));
+}
+
+void
+print_spaces (n, file)
+ register int n;
+ register FILE *file;
+{
+ while (n-- > 0)
+ fputc (' ', file);
+}
+
+/* Ask user a y-or-n question and return 1 iff answer is yes.
+ Takes three args which are given to printf to print the question.
+ The first, a control string, should end in "? ".
+ It should not say how to answer, because we do that. */
+
+/* VARARGS */
+int
+query (va_alist)
+ va_dcl
+{
+ va_list args;
+ char *ctlstr;
+ register int answer;
+ register int ans2;
+
+ /* Automatically answer "yes" if input is not from a terminal. */
+ if (!input_from_terminal_p ())
+ return 1;
+
+ while (1)
+ {
+ wrap_here (""); /* Flush any buffered output */
+ fflush (stdout);
+ va_start (args);
+ ctlstr = va_arg (args, char *);
+ vfprintf_filtered (stdout, ctlstr, args);
+ va_end (args);
+ printf_filtered ("(y or n) ");
+ fflush (stdout);
+ answer = fgetc (stdin);
+ clearerr (stdin); /* in case of C-d */
+ if (answer == EOF) /* C-d */
+ return 1;
+ if (answer != '\n') /* Eat rest of input line, to EOF or newline */
+ do
+ {
+ ans2 = fgetc (stdin);
+ clearerr (stdin);
+ }
+ while (ans2 != EOF && ans2 != '\n');
+ if (answer >= 'a')
+ answer -= 040;
+ if (answer == 'Y')
+ return 1;
+ if (answer == 'N')
+ return 0;
+ printf_filtered ("Please answer y or n.\n");
+ }
+}
+
+
+/* Parse a C escape sequence. STRING_PTR points to a variable
+ containing a pointer to the string to parse. That pointer
+ should point to the character after the \. That pointer
+ is updated past the characters we use. The value of the
+ escape sequence is returned.
+
+ A negative value means the sequence \ newline was seen,
+ which is supposed to be equivalent to nothing at all.
+
+ If \ is followed by a null character, we return a negative
+ value and leave the string pointer pointing at the null character.
+
+ If \ is followed by 000, we return 0 and leave the string pointer
+ after the zeros. A value of 0 does not mean end of string. */
+
+int
+parse_escape (string_ptr)
+ char **string_ptr;
+{
+ register int c = *(*string_ptr)++;
+ switch (c)
+ {
+ case 'a':
+ return 007; /* Bell (alert) char */
+ case 'b':
+ return '\b';
+ case 'e': /* Escape character */
+ return 033;
+ case 'f':
+ return '\f';
+ case 'n':
+ return '\n';
+ case 'r':
+ return '\r';
+ case 't':
+ return '\t';
+ case 'v':
+ return '\v';
+ case '\n':
+ return -2;
+ case 0:
+ (*string_ptr)--;
+ return 0;
+ case '^':
+ c = *(*string_ptr)++;
+ if (c == '\\')
+ c = parse_escape (string_ptr);
+ if (c == '?')
+ return 0177;
+ return (c & 0200) | (c & 037);
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ register int i = c - '0';
+ register int count = 0;
+ while (++count < 3)
+ {
+ if ((c = *(*string_ptr)++) >= '0' && c <= '7')
+ {
+ i *= 8;
+ i += c - '0';
+ }
+ else
+ {
+ (*string_ptr)--;
+ break;
+ }
+ }
+ return i;
+ }
+ default:
+ return c;
+ }
+}
+
+/* Print the character C on STREAM as part of the contents of a literal
+ string whose delimiter is QUOTER. Note that this routine should only
+ be call for printing things which are independent of the language
+ of the program being debugged. */
+
+void
+gdb_printchar (c, stream, quoter)
+ register int c;
+ FILE *stream;
+ int quoter;
+{
+
+ c &= 0xFF; /* Avoid sign bit follies */
+
+ if ( c < 0x20 || /* Low control chars */
+ (c >= 0x7F && c < 0xA0) || /* DEL, High controls */
+ (sevenbit_strings && c >= 0x80)) { /* high order bit set */
+ switch (c)
+ {
+ case '\n':
+ fputs_filtered ("\\n", stream);
+ break;
+ case '\b':
+ fputs_filtered ("\\b", stream);
+ break;
+ case '\t':
+ fputs_filtered ("\\t", stream);
+ break;
+ case '\f':
+ fputs_filtered ("\\f", stream);
+ break;
+ case '\r':
+ fputs_filtered ("\\r", stream);
+ break;
+ case '\033':
+ fputs_filtered ("\\e", stream);
+ break;
+ case '\007':
+ fputs_filtered ("\\a", stream);
+ break;
+ default:
+ fprintf_filtered (stream, "\\%.3o", (unsigned int) c);
+ break;
+ }
+ } else {
+ if (c == '\\' || c == quoter)
+ fputs_filtered ("\\", stream);
+ fprintf_filtered (stream, "%c", c);
+ }
+}
+
+/* Number of lines per page or UINT_MAX if paging is disabled. */
+static unsigned int lines_per_page;
+/* Number of chars per line or UNIT_MAX is line folding is disabled. */
+static unsigned int chars_per_line;
+/* Current count of lines printed on this page, chars on this line. */
+static unsigned int lines_printed, chars_printed;
+
+/* Buffer and start column of buffered text, for doing smarter word-
+ wrapping. When someone calls wrap_here(), we start buffering output
+ that comes through fputs_filtered(). If we see a newline, we just
+ spit it out and forget about the wrap_here(). If we see another
+ wrap_here(), we spit it out and remember the newer one. If we see
+ the end of the line, we spit out a newline, the indent, and then
+ the buffered output. */
+
+/* Malloc'd buffer with chars_per_line+2 bytes. Contains characters which
+ are waiting to be output (they have already been counted in chars_printed).
+ When wrap_buffer[0] is null, the buffer is empty. */
+static char *wrap_buffer;
+
+/* Pointer in wrap_buffer to the next character to fill. */
+static char *wrap_pointer;
+
+/* String to indent by if the wrap occurs. Must not be NULL if wrap_column
+ is non-zero. */
+static char *wrap_indent;
+
+/* Column number on the screen where wrap_buffer begins, or 0 if wrapping
+ is not in effect. */
+static int wrap_column;
+
+/* ARGSUSED */
+static void
+set_width_command (args, from_tty, c)
+ char *args;
+ int from_tty;
+ struct cmd_list_element *c;
+{
+ if (!wrap_buffer)
+ {
+ wrap_buffer = (char *) xmalloc (chars_per_line + 2);
+ wrap_buffer[0] = '\0';
+ }
+ else
+ wrap_buffer = (char *) xrealloc (wrap_buffer, chars_per_line + 2);
+ wrap_pointer = wrap_buffer; /* Start it at the beginning */
+}
+
+/* Wait, so the user can read what's on the screen. Prompt the user
+ to continue by pressing RETURN. */
+
+static void
+prompt_for_continue ()
+{
+ char *ignore;
+
+ /* We must do this *before* we call gdb_readline, else it will eventually
+ call us -- thinking that we're trying to print beyond the end of the
+ screen. */
+ reinitialize_more_filter ();
+
+ immediate_quit++;
+ /* On a real operating system, the user can quit with SIGINT.
+ But not on GO32.
+
+ 'q' is provided on all systems so users don't have to change habits
+ from system to system, and because telling them what to do in
+ the prompt is more user-friendly than expecting them to think of
+ SIGINT. */
+ ignore =
+ gdb_readline ("---Type <return> to continue, or q <return> to quit---");
+ if (ignore)
+ {
+ char *p = ignore;
+ while (*p == ' ' || *p == '\t')
+ ++p;
+ if (p[0] == 'q')
+ request_quit (SIGINT);
+ free (ignore);
+ }
+ immediate_quit--;
+
+ /* Now we have to do this again, so that GDB will know that it doesn't
+ need to save the ---Type <return>--- line at the top of the screen. */
+ reinitialize_more_filter ();
+
+ dont_repeat (); /* Forget prev cmd -- CR won't repeat it. */
+}
+
+/* Reinitialize filter; ie. tell it to reset to original values. */
+
+void
+reinitialize_more_filter ()
+{
+ lines_printed = 0;
+ chars_printed = 0;
+}
+
+/* Indicate that if the next sequence of characters overflows the line,
+ a newline should be inserted here rather than when it hits the end.
+ If INDENT is non-null, it is a string to be printed to indent the
+ wrapped part on the next line. INDENT must remain accessible until
+ the next call to wrap_here() or until a newline is printed through
+ fputs_filtered().
+
+ If the line is already overfull, we immediately print a newline and
+ the indentation, and disable further wrapping.
+
+ If we don't know the width of lines, but we know the page height,
+ we must not wrap words, but should still keep track of newlines
+ that were explicitly printed.
+
+ INDENT should not contain tabs, as that will mess up the char count
+ on the next line. FIXME.
+
+ This routine is guaranteed to force out any output which has been
+ squirreled away in the wrap_buffer, so wrap_here ((char *)0) can be
+ used to force out output from the wrap_buffer. */
+
+void
+wrap_here(indent)
+ char *indent;
+{
+ if (wrap_buffer[0])
+ {
+ *wrap_pointer = '\0';
+ fputs (wrap_buffer, stdout);
+ }
+ wrap_pointer = wrap_buffer;
+ wrap_buffer[0] = '\0';
+ if (chars_per_line == UINT_MAX) /* No line overflow checking */
+ {
+ wrap_column = 0;
+ }
+ else if (chars_printed >= chars_per_line)
+ {
+ puts_filtered ("\n");
+ if (indent != NULL)
+ puts_filtered (indent);
+ wrap_column = 0;
+ }
+ else
+ {
+ wrap_column = chars_printed;
+ if (indent == NULL)
+ wrap_indent = "";
+ else
+ wrap_indent = indent;
+ }
+}
+
+/* Ensure that whatever gets printed next, using the filtered output
+ commands, starts at the beginning of the line. I.E. if there is
+ any pending output for the current line, flush it and start a new
+ line. Otherwise do nothing. */
+
+void
+begin_line ()
+{
+ if (chars_printed > 0)
+ {
+ puts_filtered ("\n");
+ }
+}
+
+/* Like fputs but pause after every screenful, and can wrap at points
+ other than the final character of a line.
+ Unlike fputs, fputs_filtered does not return a value.
+ It is OK for LINEBUFFER to be NULL, in which case just don't print
+ anything.
+
+ Note that a longjmp to top level may occur in this routine
+ (since prompt_for_continue may do so) so this routine should not be
+ called when cleanups are not in place. */
+
+void
+fputs_filtered (linebuffer, stream)
+ const char *linebuffer;
+ FILE *stream;
+{
+ const char *lineptr;
+
+ if (linebuffer == 0)
+ return;
+
+ /* Don't do any filtering if it is disabled. */
+ if (stream != stdout
+ || (lines_per_page == UINT_MAX && chars_per_line == UINT_MAX))
+ {
+ fputs (linebuffer, stream);
+ return;
+ }
+
+ /* Go through and output each character. Show line extension
+ when this is necessary; prompt user for new page when this is
+ necessary. */
+
+ lineptr = linebuffer;
+ while (*lineptr)
+ {
+ /* Possible new page. */
+ if (lines_printed >= lines_per_page - 1)
+ prompt_for_continue ();
+
+ while (*lineptr && *lineptr != '\n')
+ {
+ /* Print a single line. */
+ if (*lineptr == '\t')
+ {
+ if (wrap_column)
+ *wrap_pointer++ = '\t';
+ else
+ putc ('\t', stream);
+ /* Shifting right by 3 produces the number of tab stops
+ we have already passed, and then adding one and
+ shifting left 3 advances to the next tab stop. */
+ chars_printed = ((chars_printed >> 3) + 1) << 3;
+ lineptr++;
+ }
+ else
+ {
+ if (wrap_column)
+ *wrap_pointer++ = *lineptr;
+ else
+ putc (*lineptr, stream);
+ chars_printed++;
+ lineptr++;
+ }
+
+ if (chars_printed >= chars_per_line)
+ {
+ unsigned int save_chars = chars_printed;
+
+ chars_printed = 0;
+ lines_printed++;
+ /* If we aren't actually wrapping, don't output newline --
+ if chars_per_line is right, we probably just overflowed
+ anyway; if it's wrong, let us keep going. */
+ if (wrap_column)
+ putc ('\n', stream);
+
+ /* Possible new page. */
+ if (lines_printed >= lines_per_page - 1)
+ prompt_for_continue ();
+
+ /* Now output indentation and wrapped string */
+ if (wrap_column)
+ {
+ fputs (wrap_indent, stream);
+ *wrap_pointer = '\0'; /* Null-terminate saved stuff */
+ fputs (wrap_buffer, stream); /* and eject it */
+ /* FIXME, this strlen is what prevents wrap_indent from
+ containing tabs. However, if we recurse to print it
+ and count its chars, we risk trouble if wrap_indent is
+ longer than (the user settable) chars_per_line.
+ Note also that this can set chars_printed > chars_per_line
+ if we are printing a long string. */
+ chars_printed = strlen (wrap_indent)
+ + (save_chars - wrap_column);
+ wrap_pointer = wrap_buffer; /* Reset buffer */
+ wrap_buffer[0] = '\0';
+ wrap_column = 0; /* And disable fancy wrap */
+ }
+ }
+ }
+
+ if (*lineptr == '\n')
+ {
+ chars_printed = 0;
+ wrap_here ((char *)0); /* Spit out chars, cancel further wraps */
+ lines_printed++;
+ putc ('\n', stream);
+ lineptr++;
+ }
+ }
+}
+
+/* Print a variable number of ARGS using format FORMAT. If this
+ information is going to put the amount written (since the last call
+ to REINITIALIZE_MORE_FILTER or the last page break) over the page size,
+ print out a pause message and do a gdb_readline to get the users
+ permision to continue.
+
+ Unlike fprintf, this function does not return a value.
+
+ We implement three variants, vfprintf (takes a vararg list and stream),
+ fprintf (takes a stream to write on), and printf (the usual).
+
+ Note that this routine has a restriction that the length of the
+ final output line must be less than 255 characters *or* it must be
+ less than twice the size of the format string. This is a very
+ arbitrary restriction, but it is an internal restriction, so I'll
+ put it in. This means that the %s format specifier is almost
+ useless; unless the caller can GUARANTEE that the string is short
+ enough, fputs_filtered should be used instead.
+
+ Note also that a longjmp to top level may occur in this routine
+ (since prompt_for_continue may do so) so this routine should not be
+ called when cleanups are not in place. */
+
+#define MIN_LINEBUF 255
+
+void
+vfprintf_filtered (stream, format, args)
+ FILE *stream;
+ char *format;
+ va_list args;
+{
+ char line_buf[MIN_LINEBUF+10];
+ char *linebuffer = line_buf;
+ int format_length;
+
+ format_length = strlen (format);
+
+ /* Reallocate buffer to a larger size if this is necessary. */
+ if (format_length * 2 > MIN_LINEBUF)
+ {
+ linebuffer = alloca (10 + format_length * 2);
+ }
+
+ /* This won't blow up if the restrictions described above are
+ followed. */
+ vsprintf (linebuffer, format, args);
+
+ fputs_filtered (linebuffer, stream);
+}
+
+void
+vprintf_filtered (format, args)
+ char *format;
+ va_list args;
+{
+ vfprintf_filtered (stdout, format, args);
+}
+
+/* VARARGS */
+void
+fprintf_filtered (va_alist)
+ va_dcl
+{
+ va_list args;
+ FILE *stream;
+ char *format;
+
+ va_start (args);
+ stream = va_arg (args, FILE *);
+ format = va_arg (args, char *);
+
+ /* This won't blow up if the restrictions described above are
+ followed. */
+ vfprintf_filtered (stream, format, args);
+ va_end (args);
+}
+
+/* Like fprintf_filtered, but prints it's result indent.
+ Called as fprintfi_filtered (spaces, stream, format, ...); */
+
+/* VARARGS */
+void
+fprintfi_filtered (va_alist)
+ va_dcl
+{
+ va_list args;
+ int spaces;
+ FILE *stream;
+ char *format;
+
+ va_start (args);
+ spaces = va_arg (args, int);
+ stream = va_arg (args, FILE *);
+ format = va_arg (args, char *);
+ print_spaces_filtered (spaces, stream);
+
+ /* This won't blow up if the restrictions described above are
+ followed. */
+ vfprintf_filtered (stream, format, args);
+ va_end (args);
+}
+
+/* VARARGS */
+void
+printf_filtered (va_alist)
+ va_dcl
+{
+ va_list args;
+ char *format;
+
+ va_start (args);
+ format = va_arg (args, char *);
+
+ vfprintf_filtered (stdout, format, args);
+ va_end (args);
+}
+
+/* Like printf_filtered, but prints it's result indented.
+ Called as printfi_filtered (spaces, format, ...); */
+
+/* VARARGS */
+void
+printfi_filtered (va_alist)
+ va_dcl
+{
+ va_list args;
+ int spaces;
+ char *format;
+
+ va_start (args);
+ spaces = va_arg (args, int);
+ format = va_arg (args, char *);
+ print_spaces_filtered (spaces, stdout);
+ vfprintf_filtered (stdout, format, args);
+ va_end (args);
+}
+
+/* Easy -- but watch out!
+
+ This routine is *not* a replacement for puts()! puts() appends a newline.
+ This one doesn't, and had better not! */
+
+void
+puts_filtered (string)
+ char *string;
+{
+ fputs_filtered (string, stdout);
+}
+
+/* Return a pointer to N spaces and a null. The pointer is good
+ until the next call to here. */
+char *
+n_spaces (n)
+ int n;
+{
+ register char *t;
+ static char *spaces;
+ static int max_spaces;
+
+ if (n > max_spaces)
+ {
+ if (spaces)
+ free (spaces);
+ spaces = (char *) xmalloc (n+1);
+ for (t = spaces+n; t != spaces;)
+ *--t = ' ';
+ spaces[n] = '\0';
+ max_spaces = n;
+ }
+
+ return spaces + max_spaces - n;
+}
+
+/* Print N spaces. */
+void
+print_spaces_filtered (n, stream)
+ int n;
+ FILE *stream;
+{
+ fputs_filtered (n_spaces (n), stream);
+}
+
+/* C++ demangler stuff. */
+
+/* fprintf_symbol_filtered attempts to demangle NAME, a symbol in language
+ LANG, using demangling args ARG_MODE, and print it filtered to STREAM.
+ If the name is not mangled, or the language for the name is unknown, or
+ demangling is off, the name is printed in its "raw" form. */
+
+void
+fprintf_symbol_filtered (stream, name, lang, arg_mode)
+ FILE *stream;
+ char *name;
+ enum language lang;
+ int arg_mode;
+{
+ char *demangled;
+
+ if (name != NULL)
+ {
+ /* If user wants to see raw output, no problem. */
+ if (!demangle)
+ {
+ fputs_filtered (name, stream);
+ }
+ else
+ {
+ switch (lang)
+ {
+ case language_cplus:
+ demangled = cplus_demangle (name, arg_mode);
+ break;
+ case language_chill:
+ demangled = chill_demangle (name);
+ break;
+ default:
+ demangled = NULL;
+ break;
+ }
+ fputs_filtered (demangled ? demangled : name, stream);
+ if (demangled != NULL)
+ {
+ free (demangled);
+ }
+ }
+ }
+}
+
+/* Do a strcmp() type operation on STRING1 and STRING2, ignoring any
+ differences in whitespace. Returns 0 if they match, non-zero if they
+ don't (slightly different than strcmp()'s range of return values).
+
+ As an extra hack, string1=="FOO(ARGS)" matches string2=="FOO".
+ This "feature" is useful when searching for matching C++ function names
+ (such as if the user types 'break FOO', where FOO is a mangled C++
+ function). */
+
+int
+strcmp_iw (string1, string2)
+ const char *string1;
+ const char *string2;
+{
+ while ((*string1 != '\0') && (*string2 != '\0'))
+ {
+ while (isspace (*string1))
+ {
+ string1++;
+ }
+ while (isspace (*string2))
+ {
+ string2++;
+ }
+ if (*string1 != *string2)
+ {
+ break;
+ }
+ if (*string1 != '\0')
+ {
+ string1++;
+ string2++;
+ }
+ }
+ return (*string1 != '\0' && *string1 != '(') || (*string2 != '\0');
+}
+
+
+void
+_initialize_utils ()
+{
+ struct cmd_list_element *c;
+
+ c = add_set_cmd ("width", class_support, var_uinteger,
+ (char *)&chars_per_line,
+ "Set number of characters gdb thinks are in a line.",
+ &setlist);
+ add_show_from_set (c, &showlist);
+ c->function.sfunc = set_width_command;
+
+ add_show_from_set
+ (add_set_cmd ("height", class_support,
+ var_uinteger, (char *)&lines_per_page,
+ "Set number of lines gdb thinks are in a page.", &setlist),
+ &showlist);
+
+ /* These defaults will be used if we are unable to get the correct
+ values from termcap. */
+#if defined(__GO32__)
+ lines_per_page = ScreenRows();
+ chars_per_line = ScreenCols();
+#else
+ lines_per_page = 24;
+ chars_per_line = 80;
+ /* Initialize the screen height and width from termcap. */
+ {
+ char *termtype = getenv ("TERM");
+
+ /* Positive means success, nonpositive means failure. */
+ int status;
+
+ /* 2048 is large enough for all known terminals, according to the
+ GNU termcap manual. */
+ char term_buffer[2048];
+
+ if (termtype)
+ {
+ status = tgetent (term_buffer, termtype);
+ if (status > 0)
+ {
+ int val;
+
+ val = tgetnum ("li");
+ if (val >= 0)
+ lines_per_page = val;
+ else
+ /* The number of lines per page is not mentioned
+ in the terminal description. This probably means
+ that paging is not useful (e.g. emacs shell window),
+ so disable paging. */
+ lines_per_page = UINT_MAX;
+
+ val = tgetnum ("co");
+ if (val >= 0)
+ chars_per_line = val;
+ }
+ }
+ }
+
+#if defined(SIGWINCH) && defined(SIGWINCH_HANDLER)
+
+ /* If there is a better way to determine the window size, use it. */
+ SIGWINCH_HANDLER ();
+#endif
+#endif
+ /* If the output is not a terminal, don't paginate it. */
+ if (!ISATTY (stdout))
+ lines_per_page = UINT_MAX;
+
+ set_width_command ((char *)NULL, 0, c);
+
+ add_show_from_set
+ (add_set_cmd ("demangle", class_support, var_boolean,
+ (char *)&demangle,
+ "Set demangling of encoded C++ names when displaying symbols.",
+ &setprintlist),
+ &showprintlist);
+
+ add_show_from_set
+ (add_set_cmd ("sevenbit-strings", class_support, var_boolean,
+ (char *)&sevenbit_strings,
+ "Set printing of 8-bit characters in strings as \\nnn.",
+ &setprintlist),
+ &showprintlist);
+
+ add_show_from_set
+ (add_set_cmd ("asm-demangle", class_support, var_boolean,
+ (char *)&asm_demangle,
+ "Set demangling of C++ names in disassembly listings.",
+ &setprintlist),
+ &showprintlist);
+}
+
+/* Machine specific function to handle SIGWINCH signal. */
+
+#ifdef SIGWINCH_HANDLER_BODY
+ SIGWINCH_HANDLER_BODY
+#endif
+
diff --git a/gnu/usr.bin/gdb/gdb/valarith.c b/gnu/usr.bin/gdb/gdb/valarith.c
new file mode 100644
index 0000000..3711a15
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/valarith.c
@@ -0,0 +1,969 @@
+/* Perform arithmetic and other operations on values, for GDB.
+ Copyright 1986, 1989, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "value.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "expression.h"
+#include "target.h"
+#include "language.h"
+#include <string.h>
+
+/* Define whether or not the C operator '/' truncates towards zero for
+ differently signed operands (truncation direction is undefined in C). */
+
+#ifndef TRUNCATION_TOWARDS_ZERO
+#define TRUNCATION_TOWARDS_ZERO ((-5 / 2) == -2)
+#endif
+
+static value
+value_subscripted_rvalue PARAMS ((value, value));
+
+
+value
+value_add (arg1, arg2)
+ value arg1, arg2;
+{
+ register value valint, valptr;
+ register int len;
+
+ COERCE_ARRAY (arg1);
+ COERCE_ARRAY (arg2);
+
+ if ((TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR
+ || TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_PTR)
+ &&
+ (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_INT
+ || TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_INT))
+ /* Exactly one argument is a pointer, and one is an integer. */
+ {
+ if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR)
+ {
+ valptr = arg1;
+ valint = arg2;
+ }
+ else
+ {
+ valptr = arg2;
+ valint = arg1;
+ }
+ len = TYPE_LENGTH (TYPE_TARGET_TYPE (VALUE_TYPE (valptr)));
+ if (len == 0) len = 1; /* For (void *) */
+ return value_from_longest (VALUE_TYPE (valptr),
+ value_as_long (valptr)
+ + (len * value_as_long (valint)));
+ }
+
+ return value_binop (arg1, arg2, BINOP_ADD);
+}
+
+value
+value_sub (arg1, arg2)
+ value arg1, arg2;
+{
+
+ COERCE_ARRAY (arg1);
+ COERCE_ARRAY (arg2);
+
+ if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR)
+ {
+ if (TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_INT)
+ {
+ /* pointer - integer. */
+ return value_from_longest
+ (VALUE_TYPE (arg1),
+ value_as_long (arg1)
+ - (TYPE_LENGTH (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)))
+ * value_as_long (arg2)));
+ }
+ else if (VALUE_TYPE (arg1) == VALUE_TYPE (arg2))
+ {
+ /* pointer to <type x> - pointer to <type x>. */
+ return value_from_longest
+ (builtin_type_long, /* FIXME -- should be ptrdiff_t */
+ (value_as_long (arg1) - value_as_long (arg2))
+ / TYPE_LENGTH (TYPE_TARGET_TYPE (VALUE_TYPE (arg1))));
+ }
+ else
+ {
+ error ("\
+First argument of `-' is a pointer and second argument is neither\n\
+an integer nor a pointer of the same type.");
+ }
+ }
+
+ return value_binop (arg1, arg2, BINOP_SUB);
+}
+
+/* Return the value of ARRAY[IDX].
+ See comments in value_coerce_array() for rationale for reason for
+ doing lower bounds adjustment here rather than there.
+ FIXME: Perhaps we should validate that the index is valid and if
+ verbosity is set, warn about invalid indices (but still use them). */
+
+value
+value_subscript (array, idx)
+ value array, idx;
+{
+ int lowerbound;
+ value bound;
+ struct type *range_type;
+
+ COERCE_REF (array);
+
+ if (TYPE_CODE (VALUE_TYPE (array)) == TYPE_CODE_ARRAY)
+ {
+ range_type = TYPE_FIELD_TYPE (VALUE_TYPE (array), 0);
+ lowerbound = TYPE_FIELD_BITPOS (range_type, 0);
+ if (lowerbound != 0)
+ {
+ bound = value_from_longest (builtin_type_int, (LONGEST) lowerbound);
+ idx = value_sub (idx, bound);
+ }
+ if (VALUE_LVAL (array) != lval_memory)
+ {
+ return value_subscripted_rvalue (array, idx);
+ }
+ }
+ return value_ind (value_add (array, idx));
+}
+
+/* Return the value of EXPR[IDX], expr an aggregate rvalue
+ (eg, a vector register). This routine used to promote floats
+ to doubles, but no longer does. */
+
+static value
+value_subscripted_rvalue (array, idx)
+ value array, idx;
+{
+ struct type *elt_type = TYPE_TARGET_TYPE (VALUE_TYPE (array));
+ int elt_size = TYPE_LENGTH (elt_type);
+ int elt_offs = elt_size * longest_to_int (value_as_long (idx));
+ value v;
+
+ if (elt_offs >= TYPE_LENGTH (VALUE_TYPE (array)))
+ error ("no such vector element");
+
+ v = allocate_value (elt_type);
+ memcpy (VALUE_CONTENTS (v), VALUE_CONTENTS (array) + elt_offs, elt_size);
+
+ if (VALUE_LVAL (array) == lval_internalvar)
+ VALUE_LVAL (v) = lval_internalvar_component;
+ else
+ VALUE_LVAL (v) = not_lval;
+ VALUE_ADDRESS (v) = VALUE_ADDRESS (array);
+ VALUE_OFFSET (v) = VALUE_OFFSET (array) + elt_offs;
+ VALUE_BITSIZE (v) = elt_size * 8;
+ return v;
+}
+
+/* Check to see if either argument is a structure. This is called so
+ we know whether to go ahead with the normal binop or look for a
+ user defined function instead.
+
+ For now, we do not overload the `=' operator. */
+
+int
+binop_user_defined_p (op, arg1, arg2)
+ enum exp_opcode op;
+ value arg1, arg2;
+{
+ if (op == BINOP_ASSIGN)
+ return 0;
+ return (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_STRUCT
+ || TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_STRUCT
+ || (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_REF
+ && TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg1))) == TYPE_CODE_STRUCT)
+ || (TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_REF
+ && TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2))) == TYPE_CODE_STRUCT));
+}
+
+/* Check to see if argument is a structure. This is called so
+ we know whether to go ahead with the normal unop or look for a
+ user defined function instead.
+
+ For now, we do not overload the `&' operator. */
+
+int unop_user_defined_p (op, arg1)
+ enum exp_opcode op;
+ value arg1;
+{
+ if (op == UNOP_ADDR)
+ return 0;
+ return (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_STRUCT
+ || (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_REF
+ && TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg1))) == TYPE_CODE_STRUCT));
+}
+
+/* We know either arg1 or arg2 is a structure, so try to find the right
+ user defined function. Create an argument vector that calls
+ arg1.operator @ (arg1,arg2) and return that value (where '@' is any
+ binary operator which is legal for GNU C++).
+
+ OP is the operatore, and if it is BINOP_ASSIGN_MODIFY, then OTHEROP
+ is the opcode saying how to modify it. Otherwise, OTHEROP is
+ unused. */
+
+value
+value_x_binop (arg1, arg2, op, otherop)
+ value arg1, arg2;
+ enum exp_opcode op, otherop;
+{
+ value * argvec;
+ char *ptr;
+ char tstr[13];
+ int static_memfuncp;
+
+ COERCE_REF (arg1);
+ COERCE_REF (arg2);
+ COERCE_ENUM (arg1);
+ COERCE_ENUM (arg2);
+
+ /* now we know that what we have to do is construct our
+ arg vector and find the right function to call it with. */
+
+ if (TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_STRUCT)
+ error ("Can't do that binary op on that type"); /* FIXME be explicit */
+
+ argvec = (value *) alloca (sizeof (value) * 4);
+ argvec[1] = value_addr (arg1);
+ argvec[2] = arg2;
+ argvec[3] = 0;
+
+ /* make the right function name up */
+ strcpy(tstr, "operator__");
+ ptr = tstr+8;
+ switch (op)
+ {
+ case BINOP_ADD: strcpy(ptr,"+"); break;
+ case BINOP_SUB: strcpy(ptr,"-"); break;
+ case BINOP_MUL: strcpy(ptr,"*"); break;
+ case BINOP_DIV: strcpy(ptr,"/"); break;
+ case BINOP_REM: strcpy(ptr,"%"); break;
+ case BINOP_LSH: strcpy(ptr,"<<"); break;
+ case BINOP_RSH: strcpy(ptr,">>"); break;
+ case BINOP_BITWISE_AND: strcpy(ptr,"&"); break;
+ case BINOP_BITWISE_IOR: strcpy(ptr,"|"); break;
+ case BINOP_BITWISE_XOR: strcpy(ptr,"^"); break;
+ case BINOP_LOGICAL_AND: strcpy(ptr,"&&"); break;
+ case BINOP_LOGICAL_OR: strcpy(ptr,"||"); break;
+ case BINOP_MIN: strcpy(ptr,"<?"); break;
+ case BINOP_MAX: strcpy(ptr,">?"); break;
+ case BINOP_ASSIGN: strcpy(ptr,"="); break;
+ case BINOP_ASSIGN_MODIFY:
+ switch (otherop)
+ {
+ case BINOP_ADD: strcpy(ptr,"+="); break;
+ case BINOP_SUB: strcpy(ptr,"-="); break;
+ case BINOP_MUL: strcpy(ptr,"*="); break;
+ case BINOP_DIV: strcpy(ptr,"/="); break;
+ case BINOP_REM: strcpy(ptr,"%="); break;
+ case BINOP_BITWISE_AND: strcpy(ptr,"&="); break;
+ case BINOP_BITWISE_IOR: strcpy(ptr,"|="); break;
+ case BINOP_BITWISE_XOR: strcpy(ptr,"^="); break;
+ case BINOP_MOD: /* invalid */
+ default:
+ error ("Invalid binary operation specified.");
+ }
+ break;
+ case BINOP_SUBSCRIPT: strcpy(ptr,"[]"); break;
+ case BINOP_EQUAL: strcpy(ptr,"=="); break;
+ case BINOP_NOTEQUAL: strcpy(ptr,"!="); break;
+ case BINOP_LESS: strcpy(ptr,"<"); break;
+ case BINOP_GTR: strcpy(ptr,">"); break;
+ case BINOP_GEQ: strcpy(ptr,">="); break;
+ case BINOP_LEQ: strcpy(ptr,"<="); break;
+ case BINOP_MOD: /* invalid */
+ default:
+ error ("Invalid binary operation specified.");
+ }
+ argvec[0] = value_struct_elt (&arg1, argvec+1, tstr, &static_memfuncp, "structure");
+ if (argvec[0])
+ {
+ if (static_memfuncp)
+ {
+ argvec[1] = argvec[0];
+ argvec++;
+ }
+ return call_function_by_hand (argvec[0], 2 - static_memfuncp, argvec + 1);
+ }
+ error ("member function %s not found", tstr);
+#ifdef lint
+ return call_function_by_hand (argvec[0], 2 - static_memfuncp, argvec + 1);
+#endif
+}
+
+/* We know that arg1 is a structure, so try to find a unary user
+ defined operator that matches the operator in question.
+ Create an argument vector that calls arg1.operator @ (arg1)
+ and return that value (where '@' is (almost) any unary operator which
+ is legal for GNU C++). */
+
+value
+value_x_unop (arg1, op)
+ value arg1;
+ enum exp_opcode op;
+{
+ value * argvec;
+ char *ptr;
+ char tstr[13];
+ int static_memfuncp;
+
+ COERCE_ENUM (arg1);
+
+ /* now we know that what we have to do is construct our
+ arg vector and find the right function to call it with. */
+
+ if (TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_STRUCT)
+ error ("Can't do that unary op on that type"); /* FIXME be explicit */
+
+ argvec = (value *) alloca (sizeof (value) * 3);
+ argvec[1] = value_addr (arg1);
+ argvec[2] = 0;
+
+ /* make the right function name up */
+ strcpy(tstr,"operator__");
+ ptr = tstr+8;
+ switch (op)
+ {
+ case UNOP_PREINCREMENT: strcpy(ptr,"++"); break;
+ case UNOP_PREDECREMENT: strcpy(ptr,"++"); break;
+ case UNOP_POSTINCREMENT: strcpy(ptr,"++"); break;
+ case UNOP_POSTDECREMENT: strcpy(ptr,"++"); break;
+ case UNOP_LOGICAL_NOT: strcpy(ptr,"!"); break;
+ case UNOP_COMPLEMENT: strcpy(ptr,"~"); break;
+ case UNOP_NEG: strcpy(ptr,"-"); break;
+ default:
+ error ("Invalid binary operation specified.");
+ }
+ argvec[0] = value_struct_elt (&arg1, argvec+1, tstr, &static_memfuncp, "structure");
+ if (argvec[0])
+ {
+ if (static_memfuncp)
+ {
+ argvec[1] = argvec[0];
+ argvec++;
+ }
+ return call_function_by_hand (argvec[0], 1 - static_memfuncp, argvec + 1);
+ }
+ error ("member function %s not found", tstr);
+ return 0; /* For lint -- never reached */
+}
+
+
+/* Concatenate two values with the following conditions:
+
+ (1) Both values must be either bitstring values or character string
+ values and the resulting value consists of the concatenation of
+ ARG1 followed by ARG2.
+
+ or
+
+ One value must be an integer value and the other value must be
+ either a bitstring value or character string value, which is
+ to be repeated by the number of times specified by the integer
+ value.
+
+
+ (2) Boolean values are also allowed and are treated as bit string
+ values of length 1.
+
+ (3) Character values are also allowed and are treated as character
+ string values of length 1.
+*/
+
+value
+value_concat (arg1, arg2)
+ value arg1, arg2;
+{
+ register value inval1, inval2, outval;
+ int inval1len, inval2len;
+ int count, idx;
+ char *ptr;
+ char inchar;
+
+ /* First figure out if we are dealing with two values to be concatenated
+ or a repeat count and a value to be repeated. INVAL1 is set to the
+ first of two concatenated values, or the repeat count. INVAL2 is set
+ to the second of the two concatenated values or the value to be
+ repeated. */
+
+ if (TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_INT)
+ {
+ inval1 = arg2;
+ inval2 = arg1;
+ }
+ else
+ {
+ inval1 = arg1;
+ inval2 = arg2;
+ }
+
+ /* Now process the input values. */
+
+ if (TYPE_CODE (VALUE_TYPE (inval1)) == TYPE_CODE_INT)
+ {
+ /* We have a repeat count. Validate the second value and then
+ construct a value repeated that many times. */
+ if (TYPE_CODE (VALUE_TYPE (inval2)) == TYPE_CODE_STRING
+ || TYPE_CODE (VALUE_TYPE (inval2)) == TYPE_CODE_CHAR)
+ {
+ count = longest_to_int (value_as_long (inval1));
+ inval2len = TYPE_LENGTH (VALUE_TYPE (inval2));
+ ptr = (char *) alloca (count * inval2len);
+ if (TYPE_CODE (VALUE_TYPE (inval2)) == TYPE_CODE_CHAR)
+ {
+ inchar = (char) unpack_long (VALUE_TYPE (inval2),
+ VALUE_CONTENTS (inval2));
+ for (idx = 0; idx < count; idx++)
+ {
+ *(ptr + idx) = inchar;
+ }
+ }
+ else
+ {
+ for (idx = 0; idx < count; idx++)
+ {
+ memcpy (ptr + (idx * inval2len), VALUE_CONTENTS (inval2),
+ inval2len);
+ }
+ }
+ outval = value_string (ptr, count * inval2len);
+ }
+ else if (TYPE_CODE (VALUE_TYPE (inval2)) == TYPE_CODE_BITSTRING
+ || TYPE_CODE (VALUE_TYPE (inval2)) == TYPE_CODE_BOOL)
+ {
+ error ("unimplemented support for bitstring/boolean repeats");
+ }
+ else
+ {
+ error ("can't repeat values of that type");
+ }
+ }
+ else if (TYPE_CODE (VALUE_TYPE (inval1)) == TYPE_CODE_STRING
+ || TYPE_CODE (VALUE_TYPE (inval1)) == TYPE_CODE_CHAR)
+ {
+ /* We have two character strings to concatenate. */
+ if (TYPE_CODE (VALUE_TYPE (inval2)) != TYPE_CODE_STRING
+ && TYPE_CODE (VALUE_TYPE (inval2)) != TYPE_CODE_CHAR)
+ {
+ error ("Strings can only be concatenated with other strings.");
+ }
+ inval1len = TYPE_LENGTH (VALUE_TYPE (inval1));
+ inval2len = TYPE_LENGTH (VALUE_TYPE (inval2));
+ ptr = (char *) alloca (inval1len + inval2len);
+ if (TYPE_CODE (VALUE_TYPE (inval1)) == TYPE_CODE_CHAR)
+ {
+ *ptr = (char) unpack_long (VALUE_TYPE (inval1), VALUE_CONTENTS (inval1));
+ }
+ else
+ {
+ memcpy (ptr, VALUE_CONTENTS (inval1), inval1len);
+ }
+ if (TYPE_CODE (VALUE_TYPE (inval2)) == TYPE_CODE_CHAR)
+ {
+ *(ptr + inval1len) =
+ (char) unpack_long (VALUE_TYPE (inval2), VALUE_CONTENTS (inval2));
+ }
+ else
+ {
+ memcpy (ptr + inval1len, VALUE_CONTENTS (inval2), inval2len);
+ }
+ outval = value_string (ptr, inval1len + inval2len);
+ }
+ else if (TYPE_CODE (VALUE_TYPE (inval1)) == TYPE_CODE_BITSTRING
+ || TYPE_CODE (VALUE_TYPE (inval1)) == TYPE_CODE_BOOL)
+ {
+ /* We have two bitstrings to concatenate. */
+ if (TYPE_CODE (VALUE_TYPE (inval2)) != TYPE_CODE_BITSTRING
+ && TYPE_CODE (VALUE_TYPE (inval2)) != TYPE_CODE_BOOL)
+ {
+ error ("Bitstrings or booleans can only be concatenated with other bitstrings or booleans.");
+ }
+ error ("unimplemented support for bitstring/boolean concatenation.");
+ }
+ else
+ {
+ /* We don't know how to concatenate these operands. */
+ error ("illegal operands for concatenation.");
+ }
+ return (outval);
+}
+
+
+/* Perform a binary operation on two operands which have reasonable
+ representations as integers or floats. This includes booleans,
+ characters, integers, or floats.
+ Does not support addition and subtraction on pointers;
+ use value_add or value_sub if you want to handle those possibilities. */
+
+value
+value_binop (arg1, arg2, op)
+ value arg1, arg2;
+ enum exp_opcode op;
+{
+ register value val;
+
+ COERCE_ENUM (arg1);
+ COERCE_ENUM (arg2);
+
+ if ((TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_FLT
+ &&
+ TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_CHAR
+ &&
+ TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_INT
+ &&
+ TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_BOOL)
+ ||
+ (TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_FLT
+ &&
+ TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_CHAR
+ &&
+ TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_INT
+ &&
+ TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_BOOL))
+ error ("Argument to arithmetic operation not a number or boolean.");
+
+ if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_FLT
+ ||
+ TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_FLT)
+ {
+ double v1, v2, v;
+ v1 = value_as_double (arg1);
+ v2 = value_as_double (arg2);
+ switch (op)
+ {
+ case BINOP_ADD:
+ v = v1 + v2;
+ break;
+
+ case BINOP_SUB:
+ v = v1 - v2;
+ break;
+
+ case BINOP_MUL:
+ v = v1 * v2;
+ break;
+
+ case BINOP_DIV:
+ v = v1 / v2;
+ break;
+
+ default:
+ error ("Integer-only operation on floating point number.");
+ }
+
+ val = allocate_value (builtin_type_double);
+ SWAP_TARGET_AND_HOST (&v, sizeof (v));
+ *(double *) VALUE_CONTENTS_RAW (val) = v;
+ }
+ else if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_BOOL
+ &&
+ TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_BOOL)
+ {
+ LONGEST v1, v2, v;
+ v1 = value_as_long (arg1);
+ v2 = value_as_long (arg2);
+
+ switch (op)
+ {
+ case BINOP_BITWISE_AND:
+ v = v1 & v2;
+ break;
+
+ case BINOP_BITWISE_IOR:
+ v = v1 | v2;
+ break;
+
+ case BINOP_BITWISE_XOR:
+ v = v1 ^ v2;
+ break;
+
+ default:
+ error ("Invalid operation on booleans.");
+ }
+
+ val = allocate_value (builtin_type_chill_bool);
+ store_signed_integer (VALUE_CONTENTS_RAW (val),
+ TYPE_LENGTH (VALUE_TYPE (val)),
+ v);
+ }
+ else
+ /* Integral operations here. */
+ /* FIXME: Also mixed integral/booleans, with result an integer. */
+ {
+ /* Should we promote to unsigned longest? */
+ if ((TYPE_UNSIGNED (VALUE_TYPE (arg1))
+ || TYPE_UNSIGNED (VALUE_TYPE (arg2)))
+ && (TYPE_LENGTH (VALUE_TYPE (arg1)) >= sizeof (unsigned LONGEST)
+ || TYPE_LENGTH (VALUE_TYPE (arg2)) >= sizeof (unsigned LONGEST)))
+ {
+ unsigned LONGEST v1, v2, v;
+ v1 = (unsigned LONGEST) value_as_long (arg1);
+ v2 = (unsigned LONGEST) value_as_long (arg2);
+
+ switch (op)
+ {
+ case BINOP_ADD:
+ v = v1 + v2;
+ break;
+
+ case BINOP_SUB:
+ v = v1 - v2;
+ break;
+
+ case BINOP_MUL:
+ v = v1 * v2;
+ break;
+
+ case BINOP_DIV:
+ v = v1 / v2;
+ break;
+
+ case BINOP_REM:
+ v = v1 % v2;
+ break;
+
+ case BINOP_MOD:
+ /* Knuth 1.2.4, integer only. Note that unlike the C '%' op,
+ v1 mod 0 has a defined value, v1. */
+ /* Chill specifies that v2 must be > 0, so check for that. */
+ if (current_language -> la_language == language_chill
+ && value_as_long (arg2) <= 0)
+ {
+ error ("Second operand of MOD must be greater than zero.");
+ }
+ if (v2 == 0)
+ {
+ v = v1;
+ }
+ else
+ {
+ v = v1/v2;
+ /* Note floor(v1/v2) == v1/v2 for unsigned. */
+ v = v1 - (v2 * v);
+ }
+ break;
+
+ case BINOP_LSH:
+ v = v1 << v2;
+ break;
+
+ case BINOP_RSH:
+ v = v1 >> v2;
+ break;
+
+ case BINOP_BITWISE_AND:
+ v = v1 & v2;
+ break;
+
+ case BINOP_BITWISE_IOR:
+ v = v1 | v2;
+ break;
+
+ case BINOP_BITWISE_XOR:
+ v = v1 ^ v2;
+ break;
+
+ case BINOP_LOGICAL_AND:
+ v = v1 && v2;
+ break;
+
+ case BINOP_LOGICAL_OR:
+ v = v1 || v2;
+ break;
+
+ case BINOP_MIN:
+ v = v1 < v2 ? v1 : v2;
+ break;
+
+ case BINOP_MAX:
+ v = v1 > v2 ? v1 : v2;
+ break;
+
+ default:
+ error ("Invalid binary operation on numbers.");
+ }
+
+ val = allocate_value (BUILTIN_TYPE_UNSIGNED_LONGEST);
+ store_unsigned_integer (VALUE_CONTENTS_RAW (val),
+ TYPE_LENGTH (VALUE_TYPE (val)),
+ v);
+ }
+ else
+ {
+ LONGEST v1, v2, v;
+ v1 = value_as_long (arg1);
+ v2 = value_as_long (arg2);
+
+ switch (op)
+ {
+ case BINOP_ADD:
+ v = v1 + v2;
+ break;
+
+ case BINOP_SUB:
+ v = v1 - v2;
+ break;
+
+ case BINOP_MUL:
+ v = v1 * v2;
+ break;
+
+ case BINOP_DIV:
+ v = v1 / v2;
+ break;
+
+ case BINOP_REM:
+ v = v1 % v2;
+ break;
+
+ case BINOP_MOD:
+ /* Knuth 1.2.4, integer only. Note that unlike the C '%' op,
+ X mod 0 has a defined value, X. */
+ /* Chill specifies that v2 must be > 0, so check for that. */
+ if (current_language -> la_language == language_chill
+ && v2 <= 0)
+ {
+ error ("Second operand of MOD must be greater than zero.");
+ }
+ if (v2 == 0)
+ {
+ v = v1;
+ }
+ else
+ {
+ v = v1/v2;
+ /* Compute floor. */
+ if (TRUNCATION_TOWARDS_ZERO && (v < 0) && ((v1 % v2) != 0))
+ {
+ v--;
+ }
+ v = v1 - (v2 * v);
+ }
+ break;
+
+ case BINOP_LSH:
+ v = v1 << v2;
+ break;
+
+ case BINOP_RSH:
+ v = v1 >> v2;
+ break;
+
+ case BINOP_BITWISE_AND:
+ v = v1 & v2;
+ break;
+
+ case BINOP_BITWISE_IOR:
+ v = v1 | v2;
+ break;
+
+ case BINOP_BITWISE_XOR:
+ v = v1 ^ v2;
+ break;
+
+ case BINOP_LOGICAL_AND:
+ v = v1 && v2;
+ break;
+
+ case BINOP_LOGICAL_OR:
+ v = v1 || v2;
+ break;
+
+ case BINOP_MIN:
+ v = v1 < v2 ? v1 : v2;
+ break;
+
+ case BINOP_MAX:
+ v = v1 > v2 ? v1 : v2;
+ break;
+
+ default:
+ error ("Invalid binary operation on numbers.");
+ }
+
+ val = allocate_value (BUILTIN_TYPE_LONGEST);
+ store_signed_integer (VALUE_CONTENTS_RAW (val),
+ TYPE_LENGTH (VALUE_TYPE (val)),
+ v);
+ }
+ }
+
+ return val;
+}
+
+/* Simulate the C operator ! -- return 1 if ARG1 contains zero. */
+
+int
+value_logical_not (arg1)
+ value arg1;
+{
+ register int len;
+ register char *p;
+
+ COERCE_ARRAY (arg1);
+
+ if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_FLT)
+ return 0 == value_as_double (arg1);
+
+ len = TYPE_LENGTH (VALUE_TYPE (arg1));
+ p = VALUE_CONTENTS (arg1);
+
+ while (--len >= 0)
+ {
+ if (*p++)
+ break;
+ }
+
+ return len < 0;
+}
+
+/* Simulate the C operator == by returning a 1
+ iff ARG1 and ARG2 have equal contents. */
+
+int
+value_equal (arg1, arg2)
+ register value arg1, arg2;
+
+{
+ register int len;
+ register char *p1, *p2;
+ enum type_code code1;
+ enum type_code code2;
+
+ COERCE_ARRAY (arg1);
+ COERCE_ARRAY (arg2);
+
+ code1 = TYPE_CODE (VALUE_TYPE (arg1));
+ code2 = TYPE_CODE (VALUE_TYPE (arg2));
+
+ if (code1 == TYPE_CODE_INT && code2 == TYPE_CODE_INT)
+ return value_as_long (arg1) == value_as_long (arg2);
+ else if ((code1 == TYPE_CODE_FLT || code1 == TYPE_CODE_INT)
+ && (code2 == TYPE_CODE_FLT || code2 == TYPE_CODE_INT))
+ return value_as_double (arg1) == value_as_double (arg2);
+
+ /* FIXME: Need to promote to either CORE_ADDR or LONGEST, whichever
+ is bigger. */
+ else if (code1 == TYPE_CODE_PTR && code2 == TYPE_CODE_INT)
+ return value_as_pointer (arg1) == (CORE_ADDR) value_as_long (arg2);
+ else if (code2 == TYPE_CODE_PTR && code1 == TYPE_CODE_INT)
+ return (CORE_ADDR) value_as_long (arg1) == value_as_pointer (arg2);
+
+ else if (code1 == code2
+ && ((len = TYPE_LENGTH (VALUE_TYPE (arg1)))
+ == TYPE_LENGTH (VALUE_TYPE (arg2))))
+ {
+ p1 = VALUE_CONTENTS (arg1);
+ p2 = VALUE_CONTENTS (arg2);
+ while (--len >= 0)
+ {
+ if (*p1++ != *p2++)
+ break;
+ }
+ return len < 0;
+ }
+ else
+ {
+ error ("Invalid type combination in equality test.");
+ return 0; /* For lint -- never reached */
+ }
+}
+
+/* Simulate the C operator < by returning 1
+ iff ARG1's contents are less than ARG2's. */
+
+int
+value_less (arg1, arg2)
+ register value arg1, arg2;
+{
+ register enum type_code code1;
+ register enum type_code code2;
+
+ COERCE_ARRAY (arg1);
+ COERCE_ARRAY (arg2);
+
+ code1 = TYPE_CODE (VALUE_TYPE (arg1));
+ code2 = TYPE_CODE (VALUE_TYPE (arg2));
+
+ if (code1 == TYPE_CODE_INT && code2 == TYPE_CODE_INT)
+ {
+ if (TYPE_UNSIGNED (VALUE_TYPE (arg1))
+ || TYPE_UNSIGNED (VALUE_TYPE (arg2)))
+ return ((unsigned LONGEST) value_as_long (arg1)
+ < (unsigned LONGEST) value_as_long (arg2));
+ else
+ return value_as_long (arg1) < value_as_long (arg2);
+ }
+ else if ((code1 == TYPE_CODE_FLT || code1 == TYPE_CODE_INT)
+ && (code2 == TYPE_CODE_FLT || code2 == TYPE_CODE_INT))
+ return value_as_double (arg1) < value_as_double (arg2);
+ else if (code1 == TYPE_CODE_PTR && code2 == TYPE_CODE_PTR)
+ return value_as_pointer (arg1) < value_as_pointer (arg2);
+
+ /* FIXME: Need to promote to either CORE_ADDR or LONGEST, whichever
+ is bigger. */
+ else if (code1 == TYPE_CODE_PTR && code2 == TYPE_CODE_INT)
+ return value_as_pointer (arg1) < (CORE_ADDR) value_as_long (arg2);
+ else if (code2 == TYPE_CODE_PTR && code1 == TYPE_CODE_INT)
+ return (CORE_ADDR) value_as_long (arg1) < value_as_pointer (arg2);
+
+ else
+ {
+ error ("Invalid type combination in ordering comparison.");
+ return 0;
+ }
+}
+
+/* The unary operators - and ~. Both free the argument ARG1. */
+
+value
+value_neg (arg1)
+ register value arg1;
+{
+ register struct type *type;
+
+ COERCE_ENUM (arg1);
+
+ type = VALUE_TYPE (arg1);
+
+ if (TYPE_CODE (type) == TYPE_CODE_FLT)
+ return value_from_double (type, - value_as_double (arg1));
+ else if (TYPE_CODE (type) == TYPE_CODE_INT)
+ return value_from_longest (type, - value_as_long (arg1));
+ else {
+ error ("Argument to negate operation not a number.");
+ return 0; /* For lint -- never reached */
+ }
+}
+
+value
+value_complement (arg1)
+ register value arg1;
+{
+ COERCE_ENUM (arg1);
+
+ if (TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_INT)
+ error ("Argument to complement operation not an integer.");
+
+ return value_from_longest (VALUE_TYPE (arg1), ~ value_as_long (arg1));
+}
+
diff --git a/gnu/usr.bin/gdb/gdb/valops.c b/gnu/usr.bin/gdb/gdb/valops.c
new file mode 100644
index 0000000..dc4d82a
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/valops.c
@@ -0,0 +1,1819 @@
+/* Perform non-arithmetic operations on values, for GDB.
+ Copyright 1986, 1987, 1989, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "value.h"
+#include "frame.h"
+#include "inferior.h"
+#include "gdbcore.h"
+#include "target.h"
+#include "demangle.h"
+#include "language.h"
+
+#include <errno.h>
+
+/* Local functions. */
+
+static int
+typecmp PARAMS ((int staticp, struct type *t1[], value t2[]));
+
+static CORE_ADDR
+find_function_addr PARAMS ((value, struct type **));
+
+static CORE_ADDR
+value_push PARAMS ((CORE_ADDR, value));
+
+static CORE_ADDR
+value_arg_push PARAMS ((CORE_ADDR, value));
+
+static value
+search_struct_field PARAMS ((char *, value, int, struct type *, int));
+
+static value
+search_struct_method PARAMS ((char *, value *, value *, int, int *,
+ struct type *));
+
+static int
+check_field_in PARAMS ((struct type *, const char *));
+
+static CORE_ADDR
+allocate_space_in_inferior PARAMS ((int));
+
+
+/* Allocate NBYTES of space in the inferior using the inferior's malloc
+ and return a value that is a pointer to the allocated space. */
+
+static CORE_ADDR
+allocate_space_in_inferior (len)
+ int len;
+{
+ register value val;
+ register struct symbol *sym;
+ struct minimal_symbol *msymbol;
+ struct type *type;
+ value blocklen;
+ LONGEST maddr;
+
+ /* Find the address of malloc in the inferior. */
+
+ sym = lookup_symbol ("malloc", 0, VAR_NAMESPACE, 0, NULL);
+ if (sym != NULL)
+ {
+ if (SYMBOL_CLASS (sym) != LOC_BLOCK)
+ {
+ error ("\"malloc\" exists in this program but is not a function.");
+ }
+ val = value_of_variable (sym, NULL);
+ }
+ else
+ {
+ msymbol = lookup_minimal_symbol ("malloc", (struct objfile *) NULL);
+ if (msymbol != NULL)
+ {
+ type = lookup_pointer_type (builtin_type_char);
+ type = lookup_function_type (type);
+ type = lookup_pointer_type (type);
+ maddr = (LONGEST) SYMBOL_VALUE_ADDRESS (msymbol);
+ val = value_from_longest (type, maddr);
+ }
+ else
+ {
+ error ("evaluation of this expression requires the program to have a function \"malloc\".");
+ }
+ }
+
+ blocklen = value_from_longest (builtin_type_int, (LONGEST) len);
+ val = call_function_by_hand (val, 1, &blocklen);
+ if (value_logical_not (val))
+ {
+ error ("No memory available to program.");
+ }
+ return (value_as_long (val));
+}
+
+/* Cast value ARG2 to type TYPE and return as a value.
+ More general than a C cast: accepts any two types of the same length,
+ and if ARG2 is an lvalue it can be cast into anything at all. */
+/* In C++, casts may change pointer or object representations. */
+
+value
+value_cast (type, arg2)
+ struct type *type;
+ register value arg2;
+{
+ register enum type_code code1;
+ register enum type_code code2;
+ register int scalar;
+
+ /* Coerce arrays but not enums. Enums will work as-is
+ and coercing them would cause an infinite recursion. */
+ if (TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_ENUM)
+ COERCE_ARRAY (arg2);
+
+ code1 = TYPE_CODE (type);
+ code2 = TYPE_CODE (VALUE_TYPE (arg2));
+ scalar = (code2 == TYPE_CODE_INT || code2 == TYPE_CODE_FLT
+ || code2 == TYPE_CODE_ENUM);
+
+ if ( code1 == TYPE_CODE_STRUCT
+ && code2 == TYPE_CODE_STRUCT
+ && TYPE_NAME (type) != 0)
+ {
+ /* Look in the type of the source to see if it contains the
+ type of the target as a superclass. If so, we'll need to
+ offset the object in addition to changing its type. */
+ value v = search_struct_field (type_name_no_tag (type),
+ arg2, 0, VALUE_TYPE (arg2), 1);
+ if (v)
+ {
+ VALUE_TYPE (v) = type;
+ return v;
+ }
+ }
+ if (code1 == TYPE_CODE_FLT && scalar)
+ return value_from_double (type, value_as_double (arg2));
+ else if ((code1 == TYPE_CODE_INT || code1 == TYPE_CODE_ENUM)
+ && (scalar || code2 == TYPE_CODE_PTR))
+ return value_from_longest (type, value_as_long (arg2));
+ else if (TYPE_LENGTH (type) == TYPE_LENGTH (VALUE_TYPE (arg2)))
+ {
+ if (code1 == TYPE_CODE_PTR && code2 == TYPE_CODE_PTR)
+ {
+ /* Look in the type of the source to see if it contains the
+ type of the target as a superclass. If so, we'll need to
+ offset the pointer rather than just change its type. */
+ struct type *t1 = TYPE_TARGET_TYPE (type);
+ struct type *t2 = TYPE_TARGET_TYPE (VALUE_TYPE (arg2));
+ if ( TYPE_CODE (t1) == TYPE_CODE_STRUCT
+ && TYPE_CODE (t2) == TYPE_CODE_STRUCT
+ && TYPE_NAME (t1) != 0) /* if name unknown, can't have supercl */
+ {
+ value v = search_struct_field (type_name_no_tag (t1),
+ value_ind (arg2), 0, t2, 1);
+ if (v)
+ {
+ v = value_addr (v);
+ VALUE_TYPE (v) = type;
+ return v;
+ }
+ }
+ /* No superclass found, just fall through to change ptr type. */
+ }
+ VALUE_TYPE (arg2) = type;
+ return arg2;
+ }
+ else if (VALUE_LVAL (arg2) == lval_memory)
+ {
+ return value_at_lazy (type, VALUE_ADDRESS (arg2) + VALUE_OFFSET (arg2));
+ }
+ else if (code1 == TYPE_CODE_VOID)
+ {
+ return value_zero (builtin_type_void, not_lval);
+ }
+ else
+ {
+ error ("Invalid cast.");
+ return 0;
+ }
+}
+
+/* Create a value of type TYPE that is zero, and return it. */
+
+value
+value_zero (type, lv)
+ struct type *type;
+ enum lval_type lv;
+{
+ register value val = allocate_value (type);
+
+ memset (VALUE_CONTENTS (val), 0, TYPE_LENGTH (type));
+ VALUE_LVAL (val) = lv;
+
+ return val;
+}
+
+/* Return a value with type TYPE located at ADDR.
+
+ Call value_at only if the data needs to be fetched immediately;
+ if we can be 'lazy' and defer the fetch, perhaps indefinately, call
+ value_at_lazy instead. value_at_lazy simply records the address of
+ the data and sets the lazy-evaluation-required flag. The lazy flag
+ is tested in the VALUE_CONTENTS macro, which is used if and when
+ the contents are actually required. */
+
+value
+value_at (type, addr)
+ struct type *type;
+ CORE_ADDR addr;
+{
+ register value val = allocate_value (type);
+
+ read_memory (addr, VALUE_CONTENTS_RAW (val), TYPE_LENGTH (type));
+
+ VALUE_LVAL (val) = lval_memory;
+ VALUE_ADDRESS (val) = addr;
+
+ return val;
+}
+
+/* Return a lazy value with type TYPE located at ADDR (cf. value_at). */
+
+value
+value_at_lazy (type, addr)
+ struct type *type;
+ CORE_ADDR addr;
+{
+ register value val = allocate_value (type);
+
+ VALUE_LVAL (val) = lval_memory;
+ VALUE_ADDRESS (val) = addr;
+ VALUE_LAZY (val) = 1;
+
+ return val;
+}
+
+/* Called only from the VALUE_CONTENTS macro, if the current data for
+ a variable needs to be loaded into VALUE_CONTENTS(VAL). Fetches the
+ data from the user's process, and clears the lazy flag to indicate
+ that the data in the buffer is valid.
+
+ If the value is zero-length, we avoid calling read_memory, which would
+ abort. We mark the value as fetched anyway -- all 0 bytes of it.
+
+ This function returns a value because it is used in the VALUE_CONTENTS
+ macro as part of an expression, where a void would not work. The
+ value is ignored. */
+
+int
+value_fetch_lazy (val)
+ register value val;
+{
+ CORE_ADDR addr = VALUE_ADDRESS (val) + VALUE_OFFSET (val);
+
+ if (TYPE_LENGTH (VALUE_TYPE (val)))
+ read_memory (addr, VALUE_CONTENTS_RAW (val),
+ TYPE_LENGTH (VALUE_TYPE (val)));
+ VALUE_LAZY (val) = 0;
+ return 0;
+}
+
+
+/* Store the contents of FROMVAL into the location of TOVAL.
+ Return a new value with the location of TOVAL and contents of FROMVAL. */
+
+value
+value_assign (toval, fromval)
+ register value toval, fromval;
+{
+ register struct type *type = VALUE_TYPE (toval);
+ register value val;
+ char raw_buffer[MAX_REGISTER_RAW_SIZE];
+ char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE];
+ int use_buffer = 0;
+
+ COERCE_ARRAY (fromval);
+ COERCE_REF (toval);
+
+ if (VALUE_LVAL (toval) != lval_internalvar)
+ fromval = value_cast (type, fromval);
+
+ /* If TOVAL is a special machine register requiring conversion
+ of program values to a special raw format,
+ convert FROMVAL's contents now, with result in `raw_buffer',
+ and set USE_BUFFER to the number of bytes to write. */
+
+ if (VALUE_REGNO (toval) >= 0
+ && REGISTER_CONVERTIBLE (VALUE_REGNO (toval)))
+ {
+ int regno = VALUE_REGNO (toval);
+ if (VALUE_TYPE (fromval) != REGISTER_VIRTUAL_TYPE (regno))
+ fromval = value_cast (REGISTER_VIRTUAL_TYPE (regno), fromval);
+ memcpy (virtual_buffer, VALUE_CONTENTS (fromval),
+ REGISTER_VIRTUAL_SIZE (regno));
+ REGISTER_CONVERT_TO_RAW (regno, virtual_buffer, raw_buffer);
+ use_buffer = REGISTER_RAW_SIZE (regno);
+ }
+
+ switch (VALUE_LVAL (toval))
+ {
+ case lval_internalvar:
+ set_internalvar (VALUE_INTERNALVAR (toval), fromval);
+ break;
+
+ case lval_internalvar_component:
+ set_internalvar_component (VALUE_INTERNALVAR (toval),
+ VALUE_OFFSET (toval),
+ VALUE_BITPOS (toval),
+ VALUE_BITSIZE (toval),
+ fromval);
+ break;
+
+ case lval_memory:
+ if (VALUE_BITSIZE (toval))
+ {
+ int v; /* FIXME, this won't work for large bitfields */
+ read_memory (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+ (char *) &v, sizeof v);
+ modify_field ((char *) &v, value_as_long (fromval),
+ VALUE_BITPOS (toval), VALUE_BITSIZE (toval));
+ write_memory (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+ (char *)&v, sizeof v);
+ }
+ else if (use_buffer)
+ write_memory (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+ raw_buffer, use_buffer);
+ else
+ write_memory (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+ VALUE_CONTENTS (fromval), TYPE_LENGTH (type));
+ break;
+
+ case lval_register:
+ if (VALUE_BITSIZE (toval))
+ {
+ int v;
+
+ read_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+ (char *) &v, sizeof v);
+ modify_field ((char *) &v, value_as_long (fromval),
+ VALUE_BITPOS (toval), VALUE_BITSIZE (toval));
+ write_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+ (char *) &v, sizeof v);
+ }
+ else if (use_buffer)
+ write_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+ raw_buffer, use_buffer);
+ else
+ {
+ /* Do any conversion necessary when storing this type to more
+ than one register. */
+#ifdef REGISTER_CONVERT_FROM_TYPE
+ memcpy (raw_buffer, VALUE_CONTENTS (fromval), TYPE_LENGTH (type));
+ REGISTER_CONVERT_FROM_TYPE(VALUE_REGNO (toval), type, raw_buffer);
+ write_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+ raw_buffer, TYPE_LENGTH (type));
+#else
+ write_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+ VALUE_CONTENTS (fromval), TYPE_LENGTH (type));
+#endif
+ }
+ break;
+
+ case lval_reg_frame_relative:
+ {
+ /* value is stored in a series of registers in the frame
+ specified by the structure. Copy that value out, modify
+ it, and copy it back in. */
+ int amount_to_copy = (VALUE_BITSIZE (toval) ? 1 : TYPE_LENGTH (type));
+ int reg_size = REGISTER_RAW_SIZE (VALUE_FRAME_REGNUM (toval));
+ int byte_offset = VALUE_OFFSET (toval) % reg_size;
+ int reg_offset = VALUE_OFFSET (toval) / reg_size;
+ int amount_copied;
+ char *buffer = (char *) alloca (amount_to_copy);
+ int regno;
+ FRAME frame;
+
+ /* Figure out which frame this is in currently. */
+ for (frame = get_current_frame ();
+ frame && FRAME_FP (frame) != VALUE_FRAME (toval);
+ frame = get_prev_frame (frame))
+ ;
+
+ if (!frame)
+ error ("Value being assigned to is no longer active.");
+
+ amount_to_copy += (reg_size - amount_to_copy % reg_size);
+
+ /* Copy it out. */
+ for ((regno = VALUE_FRAME_REGNUM (toval) + reg_offset,
+ amount_copied = 0);
+ amount_copied < amount_to_copy;
+ amount_copied += reg_size, regno++)
+ {
+ get_saved_register (buffer + amount_copied,
+ (int *)NULL, (CORE_ADDR *)NULL,
+ frame, regno, (enum lval_type *)NULL);
+ }
+
+ /* Modify what needs to be modified. */
+ if (VALUE_BITSIZE (toval))
+ modify_field (buffer + byte_offset,
+ value_as_long (fromval),
+ VALUE_BITPOS (toval), VALUE_BITSIZE (toval));
+ else if (use_buffer)
+ memcpy (buffer + byte_offset, raw_buffer, use_buffer);
+ else
+ memcpy (buffer + byte_offset, VALUE_CONTENTS (fromval),
+ TYPE_LENGTH (type));
+
+ /* Copy it back. */
+ for ((regno = VALUE_FRAME_REGNUM (toval) + reg_offset,
+ amount_copied = 0);
+ amount_copied < amount_to_copy;
+ amount_copied += reg_size, regno++)
+ {
+ enum lval_type lval;
+ CORE_ADDR addr;
+ int optim;
+
+ /* Just find out where to put it. */
+ get_saved_register ((char *)NULL,
+ &optim, &addr, frame, regno, &lval);
+
+ if (optim)
+ error ("Attempt to assign to a value that was optimized out.");
+ if (lval == lval_memory)
+ write_memory (addr, buffer + amount_copied, reg_size);
+ else if (lval == lval_register)
+ write_register_bytes (addr, buffer + amount_copied, reg_size);
+ else
+ error ("Attempt to assign to an unmodifiable value.");
+ }
+ }
+ break;
+
+
+ default:
+ error ("Left side of = operation is not an lvalue.");
+ }
+
+ /* Return a value just like TOVAL except with the contents of FROMVAL
+ (except in the case of the type if TOVAL is an internalvar). */
+
+ if (VALUE_LVAL (toval) == lval_internalvar
+ || VALUE_LVAL (toval) == lval_internalvar_component)
+ {
+ type = VALUE_TYPE (fromval);
+ }
+
+ /* FIXME: This loses if fromval is a different size than toval, for
+ example because fromval got cast in the REGISTER_CONVERTIBLE case
+ above. */
+ val = allocate_value (type);
+ memcpy (val, toval, VALUE_CONTENTS_RAW (val) - (char *) val);
+ memcpy (VALUE_CONTENTS_RAW (val), VALUE_CONTENTS (fromval),
+ TYPE_LENGTH (type));
+ VALUE_TYPE (val) = type;
+
+ return val;
+}
+
+/* Extend a value VAL to COUNT repetitions of its type. */
+
+value
+value_repeat (arg1, count)
+ value arg1;
+ int count;
+{
+ register value val;
+
+ if (VALUE_LVAL (arg1) != lval_memory)
+ error ("Only values in memory can be extended with '@'.");
+ if (count < 1)
+ error ("Invalid number %d of repetitions.", count);
+
+ val = allocate_repeat_value (VALUE_TYPE (arg1), count);
+
+ read_memory (VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1),
+ VALUE_CONTENTS_RAW (val),
+ TYPE_LENGTH (VALUE_TYPE (val)) * count);
+ VALUE_LVAL (val) = lval_memory;
+ VALUE_ADDRESS (val) = VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1);
+
+ return val;
+}
+
+value
+value_of_variable (var, b)
+ struct symbol *var;
+ struct block *b;
+{
+ value val;
+ FRAME fr;
+
+ if (b == NULL)
+ /* Use selected frame. */
+ fr = NULL;
+ else
+ {
+ fr = block_innermost_frame (b);
+ if (fr == NULL && symbol_read_needs_frame (var))
+ {
+ if (BLOCK_FUNCTION (b) != NULL
+ && SYMBOL_NAME (BLOCK_FUNCTION (b)) != NULL)
+ error ("No frame is currently executing in block %s.",
+ SYMBOL_NAME (BLOCK_FUNCTION (b)));
+ else
+ error ("No frame is currently executing in specified block");
+ }
+ }
+ val = read_var_value (var, fr);
+ if (val == 0)
+ error ("Address of symbol \"%s\" is unknown.", SYMBOL_SOURCE_NAME (var));
+ return val;
+}
+
+/* Given a value which is an array, return a value which is a pointer to its
+ first element, regardless of whether or not the array has a nonzero lower
+ bound.
+
+ FIXME: A previous comment here indicated that this routine should be
+ substracting the array's lower bound. It's not clear to me that this
+ is correct. Given an array subscripting operation, it would certainly
+ work to do the adjustment here, essentially computing:
+
+ (&array[0] - (lowerbound * sizeof array[0])) + (index * sizeof array[0])
+
+ However I believe a more appropriate and logical place to account for
+ the lower bound is to do so in value_subscript, essentially computing:
+
+ (&array[0] + ((index - lowerbound) * sizeof array[0]))
+
+ As further evidence consider what would happen with operations other
+ than array subscripting, where the caller would get back a value that
+ had an address somewhere before the actual first element of the array,
+ and the information about the lower bound would be lost because of
+ the coercion to pointer type.
+ */
+
+value
+value_coerce_array (arg1)
+ value arg1;
+{
+ register struct type *type;
+
+ if (VALUE_LVAL (arg1) != lval_memory)
+ error ("Attempt to take address of value not located in memory.");
+
+ /* Get type of elements. */
+ if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_ARRAY)
+ type = TYPE_TARGET_TYPE (VALUE_TYPE (arg1));
+ else
+ /* A phony array made by value_repeat.
+ Its type is the type of the elements, not an array type. */
+ type = VALUE_TYPE (arg1);
+
+ return value_from_longest (lookup_pointer_type (type),
+ (LONGEST) (VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1)));
+}
+
+/* Given a value which is a function, return a value which is a pointer
+ to it. */
+
+value
+value_coerce_function (arg1)
+ value arg1;
+{
+
+ if (VALUE_LVAL (arg1) != lval_memory)
+ error ("Attempt to take address of value not located in memory.");
+
+ return value_from_longest (lookup_pointer_type (VALUE_TYPE (arg1)),
+ (LONGEST) (VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1)));
+}
+
+/* Return a pointer value for the object for which ARG1 is the contents. */
+
+value
+value_addr (arg1)
+ value arg1;
+{
+ struct type *type = VALUE_TYPE (arg1);
+ if (TYPE_CODE (type) == TYPE_CODE_REF)
+ {
+ /* Copy the value, but change the type from (T&) to (T*).
+ We keep the same location information, which is efficient,
+ and allows &(&X) to get the location containing the reference. */
+ value arg2 = value_copy (arg1);
+ VALUE_TYPE (arg2) = lookup_pointer_type (TYPE_TARGET_TYPE (type));
+ return arg2;
+ }
+ if (VALUE_REPEATED (arg1)
+ || TYPE_CODE (type) == TYPE_CODE_ARRAY)
+ return value_coerce_array (arg1);
+ if (TYPE_CODE (type) == TYPE_CODE_FUNC)
+ return value_coerce_function (arg1);
+
+ if (VALUE_LVAL (arg1) != lval_memory)
+ error ("Attempt to take address of value not located in memory.");
+
+ return value_from_longest (lookup_pointer_type (type),
+ (LONGEST) (VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1)));
+}
+
+/* Given a value of a pointer type, apply the C unary * operator to it. */
+
+value
+value_ind (arg1)
+ value arg1;
+{
+ COERCE_ARRAY (arg1);
+
+ if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_MEMBER)
+ error ("not implemented: member types in value_ind");
+
+ /* Allow * on an integer so we can cast it to whatever we want.
+ This returns an int, which seems like the most C-like thing
+ to do. "long long" variables are rare enough that
+ BUILTIN_TYPE_LONGEST would seem to be a mistake. */
+ if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_INT)
+ return value_at (builtin_type_int,
+ (CORE_ADDR) value_as_long (arg1));
+ else if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR)
+ return value_at_lazy (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)),
+ value_as_pointer (arg1));
+ error ("Attempt to take contents of a non-pointer value.");
+ return 0; /* For lint -- never reached */
+}
+
+/* Pushing small parts of stack frames. */
+
+/* Push one word (the size of object that a register holds). */
+
+CORE_ADDR
+push_word (sp, word)
+ CORE_ADDR sp;
+ REGISTER_TYPE word;
+{
+ register int len = sizeof (REGISTER_TYPE);
+ char buffer[MAX_REGISTER_RAW_SIZE];
+
+ store_unsigned_integer (buffer, len, word);
+#if 1 INNER_THAN 2
+ sp -= len;
+ write_memory (sp, buffer, len);
+#else /* stack grows upward */
+ write_memory (sp, buffer, len);
+ sp += len;
+#endif /* stack grows upward */
+
+ return sp;
+}
+
+/* Push LEN bytes with data at BUFFER. */
+
+CORE_ADDR
+push_bytes (sp, buffer, len)
+ CORE_ADDR sp;
+ char *buffer;
+ int len;
+{
+#if 1 INNER_THAN 2
+ sp -= len;
+ write_memory (sp, buffer, len);
+#else /* stack grows upward */
+ write_memory (sp, buffer, len);
+ sp += len;
+#endif /* stack grows upward */
+
+ return sp;
+}
+
+/* Push onto the stack the specified value VALUE. */
+
+static CORE_ADDR
+value_push (sp, arg)
+ register CORE_ADDR sp;
+ value arg;
+{
+ register int len = TYPE_LENGTH (VALUE_TYPE (arg));
+
+#if 1 INNER_THAN 2
+ sp -= len;
+ write_memory (sp, VALUE_CONTENTS (arg), len);
+#else /* stack grows upward */
+ write_memory (sp, VALUE_CONTENTS (arg), len);
+ sp += len;
+#endif /* stack grows upward */
+
+ return sp;
+}
+
+/* Perform the standard coercions that are specified
+ for arguments to be passed to C functions. */
+
+value
+value_arg_coerce (arg)
+ value arg;
+{
+ register struct type *type;
+
+ /* FIXME: We should coerce this according to the prototype (if we have
+ one). Right now we do a little bit of this in typecmp(), but that
+ doesn't always get called. For example, if passing a ref to a function
+ without a prototype, we probably should de-reference it. Currently
+ we don't. */
+
+ if (TYPE_CODE (VALUE_TYPE (arg)) == TYPE_CODE_ENUM)
+ arg = value_cast (builtin_type_unsigned_int, arg);
+
+#if 1 /* FIXME: This is only a temporary patch. -fnf */
+ if (VALUE_REPEATED (arg)
+ || TYPE_CODE (VALUE_TYPE (arg)) == TYPE_CODE_ARRAY)
+ arg = value_coerce_array (arg);
+ if (TYPE_CODE (VALUE_TYPE (arg)) == TYPE_CODE_FUNC)
+ arg = value_coerce_function (arg);
+#endif
+
+ type = VALUE_TYPE (arg);
+
+ if (TYPE_CODE (type) == TYPE_CODE_INT
+ && TYPE_LENGTH (type) < TYPE_LENGTH (builtin_type_int))
+ return value_cast (builtin_type_int, arg);
+
+ if (TYPE_CODE (type) == TYPE_CODE_FLT
+ && TYPE_LENGTH (type) < TYPE_LENGTH (builtin_type_double))
+ return value_cast (builtin_type_double, arg);
+
+ return arg;
+}
+
+/* Push the value ARG, first coercing it as an argument
+ to a C function. */
+
+static CORE_ADDR
+value_arg_push (sp, arg)
+ register CORE_ADDR sp;
+ value arg;
+{
+ return value_push (sp, value_arg_coerce (arg));
+}
+
+/* Determine a function's address and its return type from its value.
+ Calls error() if the function is not valid for calling. */
+
+static CORE_ADDR
+find_function_addr (function, retval_type)
+ value function;
+ struct type **retval_type;
+{
+ register struct type *ftype = VALUE_TYPE (function);
+ register enum type_code code = TYPE_CODE (ftype);
+ struct type *value_type;
+ CORE_ADDR funaddr;
+
+ /* If it's a member function, just look at the function
+ part of it. */
+
+ /* Determine address to call. */
+ if (code == TYPE_CODE_FUNC || code == TYPE_CODE_METHOD)
+ {
+ funaddr = VALUE_ADDRESS (function);
+ value_type = TYPE_TARGET_TYPE (ftype);
+ }
+ else if (code == TYPE_CODE_PTR)
+ {
+ funaddr = value_as_pointer (function);
+ if (TYPE_CODE (TYPE_TARGET_TYPE (ftype)) == TYPE_CODE_FUNC
+ || TYPE_CODE (TYPE_TARGET_TYPE (ftype)) == TYPE_CODE_METHOD)
+ value_type = TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (ftype));
+ else
+ value_type = builtin_type_int;
+ }
+ else if (code == TYPE_CODE_INT)
+ {
+ /* Handle the case of functions lacking debugging info.
+ Their values are characters since their addresses are char */
+ if (TYPE_LENGTH (ftype) == 1)
+ funaddr = value_as_pointer (value_addr (function));
+ else
+ /* Handle integer used as address of a function. */
+ funaddr = (CORE_ADDR) value_as_long (function);
+
+ value_type = builtin_type_int;
+ }
+ else
+ error ("Invalid data type for function to be called.");
+
+ *retval_type = value_type;
+ return funaddr;
+}
+
+#if defined (CALL_DUMMY)
+/* All this stuff with a dummy frame may seem unnecessarily complicated
+ (why not just save registers in GDB?). The purpose of pushing a dummy
+ frame which looks just like a real frame is so that if you call a
+ function and then hit a breakpoint (get a signal, etc), "backtrace"
+ will look right. Whether the backtrace needs to actually show the
+ stack at the time the inferior function was called is debatable, but
+ it certainly needs to not display garbage. So if you are contemplating
+ making dummy frames be different from normal frames, consider that. */
+
+/* Perform a function call in the inferior.
+ ARGS is a vector of values of arguments (NARGS of them).
+ FUNCTION is a value, the function to be called.
+ Returns a value representing what the function returned.
+ May fail to return, if a breakpoint or signal is hit
+ during the execution of the function. */
+
+value
+call_function_by_hand (function, nargs, args)
+ value function;
+ int nargs;
+ value *args;
+{
+ register CORE_ADDR sp;
+ register int i;
+ CORE_ADDR start_sp;
+ /* CALL_DUMMY is an array of words (REGISTER_TYPE), but each word
+ is in host byte order. It is switched to target byte order before calling
+ FIX_CALL_DUMMY. */
+ static REGISTER_TYPE dummy[] = CALL_DUMMY;
+ REGISTER_TYPE dummy1[sizeof dummy / sizeof (REGISTER_TYPE)];
+ CORE_ADDR old_sp;
+ struct type *value_type;
+ unsigned char struct_return;
+ CORE_ADDR struct_addr;
+ struct inferior_status inf_status;
+ struct cleanup *old_chain;
+ CORE_ADDR funaddr;
+ int using_gcc;
+ CORE_ADDR real_pc;
+
+ if (!target_has_execution)
+ noprocess();
+
+ save_inferior_status (&inf_status, 1);
+ old_chain = make_cleanup (restore_inferior_status, &inf_status);
+
+ /* PUSH_DUMMY_FRAME is responsible for saving the inferior registers
+ (and POP_FRAME for restoring them). (At least on most machines)
+ they are saved on the stack in the inferior. */
+ PUSH_DUMMY_FRAME;
+
+ old_sp = sp = read_sp ();
+
+#if 1 INNER_THAN 2 /* Stack grows down */
+ sp -= sizeof dummy;
+ start_sp = sp;
+#else /* Stack grows up */
+ start_sp = sp;
+ sp += sizeof dummy;
+#endif
+
+ funaddr = find_function_addr (function, &value_type);
+
+ {
+ struct block *b = block_for_pc (funaddr);
+ /* If compiled without -g, assume GCC. */
+ using_gcc = b == NULL || BLOCK_GCC_COMPILED (b);
+ }
+
+ /* Are we returning a value using a structure return or a normal
+ value return? */
+
+ struct_return = using_struct_return (function, funaddr, value_type,
+ using_gcc);
+
+ /* Create a call sequence customized for this function
+ and the number of arguments for it. */
+ for (i = 0; i < sizeof dummy / sizeof (REGISTER_TYPE); i++)
+ store_unsigned_integer (&dummy1[i], sizeof (REGISTER_TYPE),
+ (unsigned LONGEST)dummy[i]);
+
+#ifdef GDB_TARGET_IS_HPPA
+ real_pc = FIX_CALL_DUMMY (dummy1, start_sp, funaddr, nargs, args,
+ value_type, using_gcc);
+#else
+ FIX_CALL_DUMMY (dummy1, start_sp, funaddr, nargs, args,
+ value_type, using_gcc);
+ real_pc = start_sp;
+#endif
+
+#if CALL_DUMMY_LOCATION == ON_STACK
+ write_memory (start_sp, (char *)dummy1, sizeof dummy);
+#endif /* On stack. */
+
+#if CALL_DUMMY_LOCATION == BEFORE_TEXT_END
+ /* Convex Unix prohibits executing in the stack segment. */
+ /* Hope there is empty room at the top of the text segment. */
+ {
+ extern CORE_ADDR text_end;
+ static checked = 0;
+ if (!checked)
+ for (start_sp = text_end - sizeof dummy; start_sp < text_end; ++start_sp)
+ if (read_memory_integer (start_sp, 1) != 0)
+ error ("text segment full -- no place to put call");
+ checked = 1;
+ sp = old_sp;
+ real_pc = text_end - sizeof dummy;
+ write_memory (real_pc, (char *)dummy1, sizeof dummy);
+ }
+#endif /* Before text_end. */
+
+#if CALL_DUMMY_LOCATION == AFTER_TEXT_END
+ {
+ extern CORE_ADDR text_end;
+ int errcode;
+ sp = old_sp;
+ real_pc = text_end;
+ errcode = target_write_memory (real_pc, (char *)dummy1, sizeof dummy);
+ if (errcode != 0)
+ error ("Cannot write text segment -- call_function failed");
+ }
+#endif /* After text_end. */
+
+#if CALL_DUMMY_LOCATION == AT_ENTRY_POINT
+ real_pc = funaddr;
+#endif /* At entry point. */
+
+#ifdef lint
+ sp = old_sp; /* It really is used, for some ifdef's... */
+#endif
+
+#ifdef STACK_ALIGN
+ /* If stack grows down, we must leave a hole at the top. */
+ {
+ int len = 0;
+
+ /* Reserve space for the return structure to be written on the
+ stack, if necessary */
+
+ if (struct_return)
+ len += TYPE_LENGTH (value_type);
+
+ for (i = nargs - 1; i >= 0; i--)
+ len += TYPE_LENGTH (VALUE_TYPE (value_arg_coerce (args[i])));
+#ifdef CALL_DUMMY_STACK_ADJUST
+ len += CALL_DUMMY_STACK_ADJUST;
+#endif
+#if 1 INNER_THAN 2
+ sp -= STACK_ALIGN (len) - len;
+#else
+ sp += STACK_ALIGN (len) - len;
+#endif
+ }
+#endif /* STACK_ALIGN */
+
+ /* Reserve space for the return structure to be written on the
+ stack, if necessary */
+
+ if (struct_return)
+ {
+#if 1 INNER_THAN 2
+ sp -= TYPE_LENGTH (value_type);
+ struct_addr = sp;
+#else
+ struct_addr = sp;
+ sp += TYPE_LENGTH (value_type);
+#endif
+ }
+
+#if defined (REG_STRUCT_HAS_ADDR)
+ {
+ /* This is a machine like the sparc, where we need to pass a pointer
+ to the structure, not the structure itself. */
+ if (REG_STRUCT_HAS_ADDR (using_gcc))
+ for (i = nargs - 1; i >= 0; i--)
+ if (TYPE_CODE (VALUE_TYPE (args[i])) == TYPE_CODE_STRUCT)
+ {
+ CORE_ADDR addr;
+#if !(1 INNER_THAN 2)
+ /* The stack grows up, so the address of the thing we push
+ is the stack pointer before we push it. */
+ addr = sp;
+#endif
+ /* Push the structure. */
+ sp = value_push (sp, args[i]);
+#if 1 INNER_THAN 2
+ /* The stack grows down, so the address of the thing we push
+ is the stack pointer after we push it. */
+ addr = sp;
+#endif
+ /* The value we're going to pass is the address of the thing
+ we just pushed. */
+ args[i] = value_from_longest (lookup_pointer_type (value_type),
+ (LONGEST) addr);
+ }
+ }
+#endif /* REG_STRUCT_HAS_ADDR. */
+
+#ifdef PUSH_ARGUMENTS
+ PUSH_ARGUMENTS(nargs, args, sp, struct_return, struct_addr);
+#else /* !PUSH_ARGUMENTS */
+ for (i = nargs - 1; i >= 0; i--)
+ sp = value_arg_push (sp, args[i]);
+#endif /* !PUSH_ARGUMENTS */
+
+#ifdef CALL_DUMMY_STACK_ADJUST
+#if 1 INNER_THAN 2
+ sp -= CALL_DUMMY_STACK_ADJUST;
+#else
+ sp += CALL_DUMMY_STACK_ADJUST;
+#endif
+#endif /* CALL_DUMMY_STACK_ADJUST */
+
+ /* Store the address at which the structure is supposed to be
+ written. Note that this (and the code which reserved the space
+ above) assumes that gcc was used to compile this function. Since
+ it doesn't cost us anything but space and if the function is pcc
+ it will ignore this value, we will make that assumption.
+
+ Also note that on some machines (like the sparc) pcc uses a
+ convention like gcc's. */
+
+ if (struct_return)
+ STORE_STRUCT_RETURN (struct_addr, sp);
+
+ /* Write the stack pointer. This is here because the statements above
+ might fool with it. On SPARC, this write also stores the register
+ window into the right place in the new stack frame, which otherwise
+ wouldn't happen. (See store_inferior_registers in sparc-nat.c.) */
+ write_sp (sp);
+
+ {
+ char retbuf[REGISTER_BYTES];
+ char *name;
+ struct symbol *symbol;
+
+ name = NULL;
+ symbol = find_pc_function (funaddr);
+ if (symbol)
+ {
+ name = SYMBOL_SOURCE_NAME (symbol);
+ }
+ else
+ {
+ /* Try the minimal symbols. */
+ struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (funaddr);
+
+ if (msymbol)
+ {
+ name = SYMBOL_SOURCE_NAME (msymbol);
+ }
+ }
+ if (name == NULL)
+ {
+ char format[80];
+ sprintf (format, "at %s", local_hex_format ());
+ name = alloca (80);
+ sprintf (name, format, (unsigned long) funaddr);
+ }
+
+ /* Execute the stack dummy routine, calling FUNCTION.
+ When it is done, discard the empty frame
+ after storing the contents of all regs into retbuf. */
+ if (run_stack_dummy (real_pc + CALL_DUMMY_START_OFFSET, retbuf))
+ {
+ /* We stopped somewhere besides the call dummy. */
+
+ /* If we did the cleanups, we would print a spurious error message
+ (Unable to restore previously selected frame), would write the
+ registers from the inf_status (which is wrong), and would do other
+ wrong things (like set stop_bpstat to the wrong thing). */
+ discard_cleanups (old_chain);
+ /* Prevent memory leak. */
+ bpstat_clear (&inf_status.stop_bpstat);
+
+ /* The following error message used to say "The expression
+ which contained the function call has been discarded." It
+ is a hard concept to explain in a few words. Ideally, GDB
+ would be able to resume evaluation of the expression when
+ the function finally is done executing. Perhaps someday
+ this will be implemented (it would not be easy). */
+
+ /* FIXME: Insert a bunch of wrap_here; name can be very long if it's
+ a C++ name with arguments and stuff. */
+ error ("\
+The program being debugged stopped while in a function called from GDB.\n\
+When the function (%s) is done executing, GDB will silently\n\
+stop (instead of continuing to evaluate the expression containing\n\
+the function call).", name);
+ }
+
+ do_cleanups (old_chain);
+
+ /* Figure out the value returned by the function. */
+ return value_being_returned (value_type, retbuf, struct_return);
+ }
+}
+#else /* no CALL_DUMMY. */
+value
+call_function_by_hand (function, nargs, args)
+ value function;
+ int nargs;
+ value *args;
+{
+ error ("Cannot invoke functions on this machine.");
+}
+#endif /* no CALL_DUMMY. */
+
+
+/* Create a value for an array by allocating space in the inferior, copying
+ the data into that space, and then setting up an array value.
+
+ The array bounds are set from LOWBOUND and HIGHBOUND, and the array is
+ populated from the values passed in ELEMVEC.
+
+ The element type of the array is inherited from the type of the
+ first element, and all elements must have the same size (though we
+ don't currently enforce any restriction on their types). */
+
+value
+value_array (lowbound, highbound, elemvec)
+ int lowbound;
+ int highbound;
+ value *elemvec;
+{
+ int nelem;
+ int idx;
+ int typelength;
+ value val;
+ struct type *rangetype;
+ struct type *arraytype;
+ CORE_ADDR addr;
+
+ /* Validate that the bounds are reasonable and that each of the elements
+ have the same size. */
+
+ nelem = highbound - lowbound + 1;
+ if (nelem <= 0)
+ {
+ error ("bad array bounds (%d, %d)", lowbound, highbound);
+ }
+ typelength = TYPE_LENGTH (VALUE_TYPE (elemvec[0]));
+ for (idx = 0; idx < nelem; idx++)
+ {
+ if (TYPE_LENGTH (VALUE_TYPE (elemvec[idx])) != typelength)
+ {
+ error ("array elements must all be the same size");
+ }
+ }
+
+ /* Allocate space to store the array in the inferior, and then initialize
+ it by copying in each element. FIXME: Is it worth it to create a
+ local buffer in which to collect each value and then write all the
+ bytes in one operation? */
+
+ addr = allocate_space_in_inferior (nelem * typelength);
+ for (idx = 0; idx < nelem; idx++)
+ {
+ write_memory (addr + (idx * typelength), VALUE_CONTENTS (elemvec[idx]),
+ typelength);
+ }
+
+ /* Create the array type and set up an array value to be evaluated lazily. */
+
+ rangetype = create_range_type ((struct type *) NULL, builtin_type_int,
+ lowbound, highbound);
+ arraytype = create_array_type ((struct type *) NULL,
+ VALUE_TYPE (elemvec[0]), rangetype);
+ val = value_at_lazy (arraytype, addr);
+ return (val);
+}
+
+/* Create a value for a string constant by allocating space in the inferior,
+ copying the data into that space, and returning the address with type
+ TYPE_CODE_STRING. PTR points to the string constant data; LEN is number
+ of characters.
+ Note that string types are like array of char types with a lower bound of
+ zero and an upper bound of LEN - 1. Also note that the string may contain
+ embedded null bytes. */
+
+value
+value_string (ptr, len)
+ char *ptr;
+ int len;
+{
+ value val;
+ struct type *rangetype;
+ struct type *stringtype;
+ CORE_ADDR addr;
+
+ /* Allocate space to store the string in the inferior, and then
+ copy LEN bytes from PTR in gdb to that address in the inferior. */
+
+ addr = allocate_space_in_inferior (len);
+ write_memory (addr, ptr, len);
+
+ /* Create the string type and set up a string value to be evaluated
+ lazily. */
+
+ rangetype = create_range_type ((struct type *) NULL, builtin_type_int,
+ 0, len - 1);
+ stringtype = create_string_type ((struct type *) NULL, rangetype);
+ val = value_at_lazy (stringtype, addr);
+ return (val);
+}
+
+/* See if we can pass arguments in T2 to a function which takes arguments
+ of types T1. Both t1 and t2 are NULL-terminated vectors. If some
+ arguments need coercion of some sort, then the coerced values are written
+ into T2. Return value is 0 if the arguments could be matched, or the
+ position at which they differ if not.
+
+ STATICP is nonzero if the T1 argument list came from a
+ static member function.
+
+ For non-static member functions, we ignore the first argument,
+ which is the type of the instance variable. This is because we want
+ to handle calls with objects from derived classes. This is not
+ entirely correct: we should actually check to make sure that a
+ requested operation is type secure, shouldn't we? FIXME. */
+
+static int
+typecmp (staticp, t1, t2)
+ int staticp;
+ struct type *t1[];
+ value t2[];
+{
+ int i;
+
+ if (t2 == 0)
+ return 1;
+ if (staticp && t1 == 0)
+ return t2[1] != 0;
+ if (t1 == 0)
+ return 1;
+ if (TYPE_CODE (t1[0]) == TYPE_CODE_VOID) return 0;
+ if (t1[!staticp] == 0) return 0;
+ for (i = !staticp; t1[i] && TYPE_CODE (t1[i]) != TYPE_CODE_VOID; i++)
+ {
+ if (! t2[i])
+ return i+1;
+ if (TYPE_CODE (t1[i]) == TYPE_CODE_REF
+ /* We should be doing hairy argument matching, as below. */
+ && (TYPE_CODE (TYPE_TARGET_TYPE (t1[i]))
+ == TYPE_CODE (VALUE_TYPE (t2[i]))))
+ {
+ t2[i] = value_addr (t2[i]);
+ continue;
+ }
+
+ if (TYPE_CODE (t1[i]) == TYPE_CODE_PTR
+ && TYPE_CODE (VALUE_TYPE (t2[i])) == TYPE_CODE_ARRAY)
+ /* Array to pointer is a `trivial conversion' according to the ARM. */
+ continue;
+
+ /* We should be doing much hairier argument matching (see section 13.2
+ of the ARM), but as a quick kludge, just check for the same type
+ code. */
+ if (TYPE_CODE (t1[i]) != TYPE_CODE (VALUE_TYPE (t2[i])))
+ return i+1;
+ }
+ if (!t1[i]) return 0;
+ return t2[i] ? i+1 : 0;
+}
+
+/* Helper function used by value_struct_elt to recurse through baseclasses.
+ Look for a field NAME in ARG1. Adjust the address of ARG1 by OFFSET bytes,
+ and search in it assuming it has (class) type TYPE.
+ If found, return value, else return NULL.
+
+ If LOOKING_FOR_BASECLASS, then instead of looking for struct fields,
+ look for a baseclass named NAME. */
+
+static value
+search_struct_field (name, arg1, offset, type, looking_for_baseclass)
+ char *name;
+ register value arg1;
+ int offset;
+ register struct type *type;
+ int looking_for_baseclass;
+{
+ int i;
+
+ check_stub_type (type);
+
+ if (! looking_for_baseclass)
+ for (i = TYPE_NFIELDS (type) - 1; i >= TYPE_N_BASECLASSES (type); i--)
+ {
+ char *t_field_name = TYPE_FIELD_NAME (type, i);
+
+ if (t_field_name && STREQ (t_field_name, name))
+ {
+ value v;
+ if (TYPE_FIELD_STATIC (type, i))
+ {
+ char *phys_name = TYPE_FIELD_STATIC_PHYSNAME (type, i);
+ struct symbol *sym =
+ lookup_symbol (phys_name, 0, VAR_NAMESPACE, 0, NULL);
+ if (sym == NULL)
+ error ("Internal error: could not find physical static variable named %s",
+ phys_name);
+ v = value_at (TYPE_FIELD_TYPE (type, i),
+ (CORE_ADDR)SYMBOL_BLOCK_VALUE (sym));
+ }
+ else
+ v = value_primitive_field (arg1, offset, i, type);
+ if (v == 0)
+ error("there is no field named %s", name);
+ return v;
+ }
+ }
+
+ for (i = TYPE_N_BASECLASSES (type) - 1; i >= 0; i--)
+ {
+ value v;
+ /* If we are looking for baseclasses, this is what we get when we
+ hit them. But it could happen that the base part's member name
+ is not yet filled in. */
+ int found_baseclass = (looking_for_baseclass
+ && TYPE_BASECLASS_NAME (type, i) != NULL
+ && STREQ (name, TYPE_BASECLASS_NAME (type, i)));
+
+ if (BASETYPE_VIA_VIRTUAL (type, i))
+ {
+ value v2;
+ /* Fix to use baseclass_offset instead. FIXME */
+ baseclass_addr (type, i, VALUE_CONTENTS (arg1) + offset,
+ &v2, (int *)NULL);
+ if (v2 == 0)
+ error ("virtual baseclass botch");
+ if (found_baseclass)
+ return v2;
+ v = search_struct_field (name, v2, 0, TYPE_BASECLASS (type, i),
+ looking_for_baseclass);
+ }
+ else if (found_baseclass)
+ v = value_primitive_field (arg1, offset, i, type);
+ else
+ v = search_struct_field (name, arg1,
+ offset + TYPE_BASECLASS_BITPOS (type, i) / 8,
+ TYPE_BASECLASS (type, i),
+ looking_for_baseclass);
+ if (v) return v;
+ }
+ return NULL;
+}
+
+/* Helper function used by value_struct_elt to recurse through baseclasses.
+ Look for a field NAME in ARG1. Adjust the address of ARG1 by OFFSET bytes,
+ and search in it assuming it has (class) type TYPE.
+ If found, return value, else if name matched and args not return (value)-1,
+ else return NULL. */
+
+static value
+search_struct_method (name, arg1p, args, offset, static_memfuncp, type)
+ char *name;
+ register value *arg1p, *args;
+ int offset, *static_memfuncp;
+ register struct type *type;
+{
+ int i;
+ static int name_matched = 0;
+
+ check_stub_type (type);
+ for (i = TYPE_NFN_FIELDS (type) - 1; i >= 0; i--)
+ {
+ char *t_field_name = TYPE_FN_FIELDLIST_NAME (type, i);
+ if (t_field_name && STREQ (t_field_name, name))
+ {
+ int j = TYPE_FN_FIELDLIST_LENGTH (type, i) - 1;
+ struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i);
+ name_matched = 1;
+
+ if (j > 0 && args == 0)
+ error ("cannot resolve overloaded method `%s'", name);
+ while (j >= 0)
+ {
+ if (TYPE_FN_FIELD_STUB (f, j))
+ check_stub_method (type, i, j);
+ if (!typecmp (TYPE_FN_FIELD_STATIC_P (f, j),
+ TYPE_FN_FIELD_ARGS (f, j), args))
+ {
+ if (TYPE_FN_FIELD_VIRTUAL_P (f, j))
+ return (value)value_virtual_fn_field (arg1p, f, j, type, offset);
+ if (TYPE_FN_FIELD_STATIC_P (f, j) && static_memfuncp)
+ *static_memfuncp = 1;
+ return (value)value_fn_field (arg1p, f, j, type, offset);
+ }
+ j--;
+ }
+ }
+ }
+
+ for (i = TYPE_N_BASECLASSES (type) - 1; i >= 0; i--)
+ {
+ value v;
+ int base_offset;
+
+ if (BASETYPE_VIA_VIRTUAL (type, i))
+ {
+ base_offset = baseclass_offset (type, i, *arg1p, offset);
+ if (base_offset == -1)
+ error ("virtual baseclass botch");
+ }
+ else
+ {
+ base_offset = TYPE_BASECLASS_BITPOS (type, i) / 8;
+ }
+ v = search_struct_method (name, arg1p, args, base_offset + offset,
+ static_memfuncp, TYPE_BASECLASS (type, i));
+ if (v == (value) -1)
+ {
+ name_matched = 1;
+ }
+ else if (v)
+ {
+/* FIXME-bothner: Why is this commented out? Why is it here? */
+/* *arg1p = arg1_tmp;*/
+ return v;
+ }
+ }
+ if (name_matched) return (value) -1;
+ else return NULL;
+}
+
+/* Given *ARGP, a value of type (pointer to a)* structure/union,
+ extract the component named NAME from the ultimate target structure/union
+ and return it as a value with its appropriate type.
+ ERR is used in the error message if *ARGP's type is wrong.
+
+ C++: ARGS is a list of argument types to aid in the selection of
+ an appropriate method. Also, handle derived types.
+
+ STATIC_MEMFUNCP, if non-NULL, points to a caller-supplied location
+ where the truthvalue of whether the function that was resolved was
+ a static member function or not is stored.
+
+ ERR is an error message to be printed in case the field is not found. */
+
+value
+value_struct_elt (argp, args, name, static_memfuncp, err)
+ register value *argp, *args;
+ char *name;
+ int *static_memfuncp;
+ char *err;
+{
+ register struct type *t;
+ value v;
+
+ COERCE_ARRAY (*argp);
+
+ t = VALUE_TYPE (*argp);
+
+ /* Follow pointers until we get to a non-pointer. */
+
+ while (TYPE_CODE (t) == TYPE_CODE_PTR || TYPE_CODE (t) == TYPE_CODE_REF)
+ {
+ *argp = value_ind (*argp);
+ /* Don't coerce fn pointer to fn and then back again! */
+ if (TYPE_CODE (VALUE_TYPE (*argp)) != TYPE_CODE_FUNC)
+ COERCE_ARRAY (*argp);
+ t = VALUE_TYPE (*argp);
+ }
+
+ if (TYPE_CODE (t) == TYPE_CODE_MEMBER)
+ error ("not implemented: member type in value_struct_elt");
+
+ if ( TYPE_CODE (t) != TYPE_CODE_STRUCT
+ && TYPE_CODE (t) != TYPE_CODE_UNION)
+ error ("Attempt to extract a component of a value that is not a %s.", err);
+
+ /* Assume it's not, unless we see that it is. */
+ if (static_memfuncp)
+ *static_memfuncp =0;
+
+ if (!args)
+ {
+ /* if there are no arguments ...do this... */
+
+ /* Try as a field first, because if we succeed, there
+ is less work to be done. */
+ v = search_struct_field (name, *argp, 0, t, 0);
+ if (v)
+ return v;
+
+ /* C++: If it was not found as a data field, then try to
+ return it as a pointer to a method. */
+
+ if (destructor_name_p (name, t))
+ error ("Cannot get value of destructor");
+
+ v = search_struct_method (name, argp, args, 0, static_memfuncp, t);
+
+ if (v == 0)
+ {
+ if (TYPE_NFN_FIELDS (t))
+ error ("There is no member or method named %s.", name);
+ else
+ error ("There is no member named %s.", name);
+ }
+ return v;
+ }
+
+ if (destructor_name_p (name, t))
+ {
+ if (!args[1])
+ {
+ /* destructors are a special case. */
+ return (value)value_fn_field (NULL, TYPE_FN_FIELDLIST1 (t, 0),
+ TYPE_FN_FIELDLIST_LENGTH (t, 0),
+ 0, 0);
+ }
+ else
+ {
+ error ("destructor should not have any argument");
+ }
+ }
+ else
+ v = search_struct_method (name, argp, args, 0, static_memfuncp, t);
+
+ if (v == (value) -1)
+ {
+ error("Argument list of %s mismatch with component in the structure.", name);
+ }
+ else if (v == 0)
+ {
+ /* See if user tried to invoke data as function. If so,
+ hand it back. If it's not callable (i.e., a pointer to function),
+ gdb should give an error. */
+ v = search_struct_field (name, *argp, 0, t, 0);
+ }
+
+ if (!v)
+ error ("Structure has no component named %s.", name);
+ return v;
+}
+
+/* C++: return 1 is NAME is a legitimate name for the destructor
+ of type TYPE. If TYPE does not have a destructor, or
+ if NAME is inappropriate for TYPE, an error is signaled. */
+int
+destructor_name_p (name, type)
+ const char *name;
+ const struct type *type;
+{
+ /* destructors are a special case. */
+
+ if (name[0] == '~')
+ {
+ char *dname = type_name_no_tag (type);
+ if (!STREQ (dname, name+1))
+ error ("name of destructor must equal name of class");
+ else
+ return 1;
+ }
+ return 0;
+}
+
+/* Helper function for check_field: Given TYPE, a structure/union,
+ return 1 if the component named NAME from the ultimate
+ target structure/union is defined, otherwise, return 0. */
+
+static int
+check_field_in (type, name)
+ register struct type *type;
+ const char *name;
+{
+ register int i;
+
+ for (i = TYPE_NFIELDS (type) - 1; i >= TYPE_N_BASECLASSES (type); i--)
+ {
+ char *t_field_name = TYPE_FIELD_NAME (type, i);
+ if (t_field_name && STREQ (t_field_name, name))
+ return 1;
+ }
+
+ /* C++: If it was not found as a data field, then try to
+ return it as a pointer to a method. */
+
+ /* Destructors are a special case. */
+ if (destructor_name_p (name, type))
+ return 1;
+
+ for (i = TYPE_NFN_FIELDS (type) - 1; i >= 0; --i)
+ {
+ if (STREQ (TYPE_FN_FIELDLIST_NAME (type, i), name))
+ return 1;
+ }
+
+ for (i = TYPE_N_BASECLASSES (type) - 1; i >= 0; i--)
+ if (check_field_in (TYPE_BASECLASS (type, i), name))
+ return 1;
+
+ return 0;
+}
+
+
+/* C++: Given ARG1, a value of type (pointer to a)* structure/union,
+ return 1 if the component named NAME from the ultimate
+ target structure/union is defined, otherwise, return 0. */
+
+int
+check_field (arg1, name)
+ register value arg1;
+ const char *name;
+{
+ register struct type *t;
+
+ COERCE_ARRAY (arg1);
+
+ t = VALUE_TYPE (arg1);
+
+ /* Follow pointers until we get to a non-pointer. */
+
+ while (TYPE_CODE (t) == TYPE_CODE_PTR || TYPE_CODE (t) == TYPE_CODE_REF)
+ t = TYPE_TARGET_TYPE (t);
+
+ if (TYPE_CODE (t) == TYPE_CODE_MEMBER)
+ error ("not implemented: member type in check_field");
+
+ if ( TYPE_CODE (t) != TYPE_CODE_STRUCT
+ && TYPE_CODE (t) != TYPE_CODE_UNION)
+ error ("Internal error: `this' is not an aggregate");
+
+ return check_field_in (t, name);
+}
+
+/* C++: Given an aggregate type CURTYPE, and a member name NAME,
+ return the address of this member as a "pointer to member"
+ type. If INTYPE is non-null, then it will be the type
+ of the member we are looking for. This will help us resolve
+ "pointers to member functions". This function is used
+ to resolve user expressions of the form "DOMAIN::NAME". */
+
+value
+value_struct_elt_for_reference (domain, offset, curtype, name, intype)
+ struct type *domain, *curtype, *intype;
+ int offset;
+ char *name;
+{
+ register struct type *t = curtype;
+ register int i;
+ value v;
+
+ if ( TYPE_CODE (t) != TYPE_CODE_STRUCT
+ && TYPE_CODE (t) != TYPE_CODE_UNION)
+ error ("Internal error: non-aggregate type to value_struct_elt_for_reference");
+
+ for (i = TYPE_NFIELDS (t) - 1; i >= TYPE_N_BASECLASSES (t); i--)
+ {
+ char *t_field_name = TYPE_FIELD_NAME (t, i);
+
+ if (t_field_name && STREQ (t_field_name, name))
+ {
+ if (TYPE_FIELD_STATIC (t, i))
+ {
+ char *phys_name = TYPE_FIELD_STATIC_PHYSNAME (t, i);
+ struct symbol *sym =
+ lookup_symbol (phys_name, 0, VAR_NAMESPACE, 0, NULL);
+ if (sym == NULL)
+ error ("Internal error: could not find physical static variable named %s",
+ phys_name);
+ return value_at (SYMBOL_TYPE (sym),
+ (CORE_ADDR)SYMBOL_BLOCK_VALUE (sym));
+ }
+ if (TYPE_FIELD_PACKED (t, i))
+ error ("pointers to bitfield members not allowed");
+
+ return value_from_longest
+ (lookup_reference_type (lookup_member_type (TYPE_FIELD_TYPE (t, i),
+ domain)),
+ offset + (LONGEST) (TYPE_FIELD_BITPOS (t, i) >> 3));
+ }
+ }
+
+ /* C++: If it was not found as a data field, then try to
+ return it as a pointer to a method. */
+
+ /* Destructors are a special case. */
+ if (destructor_name_p (name, t))
+ {
+ error ("member pointers to destructors not implemented yet");
+ }
+
+ /* Perform all necessary dereferencing. */
+ while (intype && TYPE_CODE (intype) == TYPE_CODE_PTR)
+ intype = TYPE_TARGET_TYPE (intype);
+
+ for (i = TYPE_NFN_FIELDS (t) - 1; i >= 0; --i)
+ {
+ if (STREQ (TYPE_FN_FIELDLIST_NAME (t, i), name))
+ {
+ int j = TYPE_FN_FIELDLIST_LENGTH (t, i);
+ struct fn_field *f = TYPE_FN_FIELDLIST1 (t, i);
+
+ if (intype == 0 && j > 1)
+ error ("non-unique member `%s' requires type instantiation", name);
+ if (intype)
+ {
+ while (j--)
+ if (TYPE_FN_FIELD_TYPE (f, j) == intype)
+ break;
+ if (j < 0)
+ error ("no member function matches that type instantiation");
+ }
+ else
+ j = 0;
+
+ if (TYPE_FN_FIELD_STUB (f, j))
+ check_stub_method (t, i, j);
+ if (TYPE_FN_FIELD_VIRTUAL_P (f, j))
+ {
+ return value_from_longest
+ (lookup_reference_type
+ (lookup_member_type (TYPE_FN_FIELD_TYPE (f, j),
+ domain)),
+ (LONGEST) METHOD_PTR_FROM_VOFFSET
+ (TYPE_FN_FIELD_VOFFSET (f, j)));
+ }
+ else
+ {
+ struct symbol *s = lookup_symbol (TYPE_FN_FIELD_PHYSNAME (f, j),
+ 0, VAR_NAMESPACE, 0, NULL);
+ if (s == NULL)
+ {
+ v = 0;
+ }
+ else
+ {
+ v = read_var_value (s, 0);
+#if 0
+ VALUE_TYPE (v) = lookup_reference_type
+ (lookup_member_type (TYPE_FN_FIELD_TYPE (f, j),
+ domain));
+#endif
+ }
+ return v;
+ }
+ }
+ }
+ for (i = TYPE_N_BASECLASSES (t) - 1; i >= 0; i--)
+ {
+ value v;
+ int base_offset;
+
+ if (BASETYPE_VIA_VIRTUAL (t, i))
+ base_offset = 0;
+ else
+ base_offset = TYPE_BASECLASS_BITPOS (t, i) / 8;
+ v = value_struct_elt_for_reference (domain,
+ offset + base_offset,
+ TYPE_BASECLASS (t, i),
+ name,
+ intype);
+ if (v)
+ return v;
+ }
+ return 0;
+}
+
+/* C++: return the value of the class instance variable, if one exists.
+ Flag COMPLAIN signals an error if the request is made in an
+ inappropriate context. */
+value
+value_of_this (complain)
+ int complain;
+{
+ extern FRAME selected_frame;
+ struct symbol *func, *sym;
+ struct block *b;
+ int i;
+ static const char funny_this[] = "this";
+ value this;
+
+ if (selected_frame == 0)
+ if (complain)
+ error ("no frame selected");
+ else return 0;
+
+ func = get_frame_function (selected_frame);
+ if (!func)
+ {
+ if (complain)
+ error ("no `this' in nameless context");
+ else return 0;
+ }
+
+ b = SYMBOL_BLOCK_VALUE (func);
+ i = BLOCK_NSYMS (b);
+ if (i <= 0)
+ if (complain)
+ error ("no args, no `this'");
+ else return 0;
+
+ /* Calling lookup_block_symbol is necessary to get the LOC_REGISTER
+ symbol instead of the LOC_ARG one (if both exist). */
+ sym = lookup_block_symbol (b, funny_this, VAR_NAMESPACE);
+ if (sym == NULL)
+ {
+ if (complain)
+ error ("current stack frame not in method");
+ else
+ return NULL;
+ }
+
+ this = read_var_value (sym, selected_frame);
+ if (this == 0 && complain)
+ error ("`this' argument at unknown address");
+ return this;
+}
diff --git a/gnu/usr.bin/gdb/gdb/valprint.c b/gnu/usr.bin/gdb/gdb/valprint.c
new file mode 100644
index 0000000..b805645
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/valprint.c
@@ -0,0 +1,1063 @@
+/* Print values for GDB, the GNU debugger.
+ Copyright 1986, 1988, 1989, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include <string.h>
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "value.h"
+#include "gdbcore.h"
+#include "gdbcmd.h"
+#include "target.h"
+#include "obstack.h"
+#include "language.h"
+#include "demangle.h"
+
+#include <errno.h>
+
+/* Prototypes for local functions */
+
+static void
+print_hex_chars PARAMS ((FILE *, unsigned char *, unsigned int));
+
+static void
+show_print PARAMS ((char *, int));
+
+static void
+set_print PARAMS ((char *, int));
+
+static void
+set_radix PARAMS ((char *, int));
+
+static void
+show_radix PARAMS ((char *, int));
+
+static void
+set_input_radix PARAMS ((char *, int, struct cmd_list_element *));
+
+static void
+set_input_radix_1 PARAMS ((int, unsigned));
+
+static void
+set_output_radix PARAMS ((char *, int, struct cmd_list_element *));
+
+static void
+set_output_radix_1 PARAMS ((int, unsigned));
+
+static void
+value_print_array_elements PARAMS ((value, FILE *, int, enum val_prettyprint));
+
+/* Maximum number of chars to print for a string pointer value or vector
+ contents, or UINT_MAX for no limit. Note that "set print elements 0"
+ stores UINT_MAX in print_max, which displays in a show command as
+ "unlimited". */
+
+unsigned int print_max;
+#define PRINT_MAX_DEFAULT 200 /* Start print_max off at this value. */
+
+/* Default input and output radixes, and output format letter. */
+
+unsigned input_radix = 10;
+unsigned output_radix = 10;
+int output_format = 0;
+
+/* Print repeat counts if there are more than this many repetitions of an
+ element in an array. Referenced by the low level language dependent
+ print routines. */
+
+unsigned int repeat_count_threshold = 10;
+
+int prettyprint_structs; /* Controls pretty printing of structures */
+int prettyprint_arrays; /* Controls pretty printing of arrays. */
+
+/* If nonzero, causes unions inside structures or other unions to be
+ printed. */
+
+int unionprint; /* Controls printing of nested unions. */
+
+/* If nonzero, causes machine addresses to be printed in certain contexts. */
+
+int addressprint; /* Controls printing of machine addresses */
+
+
+/* Print data of type TYPE located at VALADDR (within GDB), which came from
+ the inferior at address ADDRESS, onto stdio stream STREAM according to
+ FORMAT (a letter, or 0 for natural format using TYPE).
+
+ If DEREF_REF is nonzero, then dereference references, otherwise just print
+ them like pointers.
+
+ The PRETTY parameter controls prettyprinting.
+
+ If the data are a string pointer, returns the number of string characters
+ printed.
+
+ FIXME: The data at VALADDR is in target byte order. If gdb is ever
+ enhanced to be able to debug more than the single target it was compiled
+ for (specific CPU type and thus specific target byte ordering), then
+ either the print routines are going to have to take this into account,
+ or the data is going to have to be passed into here already converted
+ to the host byte ordering, whichever is more convenient. */
+
+
+int
+val_print (type, valaddr, address, stream, format, deref_ref, recurse, pretty)
+ struct type *type;
+ char *valaddr;
+ CORE_ADDR address;
+ FILE *stream;
+ int format;
+ int deref_ref;
+ int recurse;
+ enum val_prettyprint pretty;
+{
+ if (pretty == Val_pretty_default)
+ {
+ pretty = prettyprint_structs ? Val_prettyprint : Val_no_prettyprint;
+ }
+
+ QUIT;
+
+ /* Ensure that the type is complete and not just a stub. If the type is
+ only a stub and we can't find and substitute its complete type, then
+ print appropriate string and return. Typical types that my be stubs
+ are structs, unions, and C++ methods. */
+
+ check_stub_type (type);
+ if (TYPE_FLAGS (type) & TYPE_FLAG_STUB)
+ {
+ fprintf_filtered (stream, "<incomplete type>");
+ fflush (stream);
+ return (0);
+ }
+
+ return (LA_VAL_PRINT (type, valaddr, address, stream, format, deref_ref,
+ recurse, pretty));
+}
+
+/* Print the value VAL in C-ish syntax on stream STREAM.
+ FORMAT is a format-letter, or 0 for print in natural format of data type.
+ If the object printed is a string pointer, returns
+ the number of string bytes printed. */
+
+int
+value_print (val, stream, format, pretty)
+ value val;
+ FILE *stream;
+ int format;
+ enum val_prettyprint pretty;
+{
+ register unsigned int n, typelen;
+
+ if (val == 0)
+ {
+ printf_filtered ("<address of value unknown>");
+ return 0;
+ }
+ if (VALUE_OPTIMIZED_OUT (val))
+ {
+ printf_filtered ("<value optimized out>");
+ return 0;
+ }
+
+ /* A "repeated" value really contains several values in a row.
+ They are made by the @ operator.
+ Print such values as if they were arrays. */
+
+ if (VALUE_REPEATED (val))
+ {
+ n = VALUE_REPETITIONS (val);
+ typelen = TYPE_LENGTH (VALUE_TYPE (val));
+ fprintf_filtered (stream, "{");
+ /* Print arrays of characters using string syntax. */
+ if (typelen == 1 && TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_INT
+ && format == 0)
+ LA_PRINT_STRING (stream, VALUE_CONTENTS (val), n, 0);
+ else
+ {
+ value_print_array_elements (val, stream, format, pretty);
+ }
+ fprintf_filtered (stream, "}");
+ return (n * typelen);
+ }
+ else
+ {
+ struct type *type = VALUE_TYPE (val);
+
+ /* If it is a pointer, indicate what it points to.
+
+ Print type also if it is a reference.
+
+ C++: if it is a member pointer, we will take care
+ of that when we print it. */
+ if (TYPE_CODE (type) == TYPE_CODE_PTR ||
+ TYPE_CODE (type) == TYPE_CODE_REF)
+ {
+ /* Hack: remove (char *) for char strings. Their
+ type is indicated by the quoted string anyway. */
+ if (TYPE_CODE (type) == TYPE_CODE_PTR &&
+ TYPE_LENGTH (TYPE_TARGET_TYPE (type)) == sizeof(char) &&
+ TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_INT &&
+ !TYPE_UNSIGNED (TYPE_TARGET_TYPE (type)))
+ {
+ /* Print nothing */
+ }
+ else
+ {
+ fprintf_filtered (stream, "(");
+ type_print (type, "", stream, -1);
+ fprintf_filtered (stream, ") ");
+ }
+ }
+ return (val_print (type, VALUE_CONTENTS (val),
+ VALUE_ADDRESS (val), stream, format, 1, 0, pretty));
+ }
+}
+
+/* Called by various <lang>_val_print routines to print TYPE_CODE_INT's */
+
+void
+val_print_type_code_int (type, valaddr, stream)
+ struct type *type;
+ char *valaddr;
+ FILE *stream;
+{
+ char *p;
+ /* Pointer to first (i.e. lowest address) nonzero character. */
+ char *first_addr;
+ unsigned int len;
+
+ if (TYPE_LENGTH (type) > sizeof (LONGEST))
+ {
+ if (TYPE_UNSIGNED (type))
+ {
+ /* First figure out whether the number in fact has zeros
+ in all its bytes more significant than least significant
+ sizeof (LONGEST) ones. */
+ len = TYPE_LENGTH (type);
+
+#if TARGET_BYTE_ORDER == BIG_ENDIAN
+ for (p = valaddr;
+ len > sizeof (LONGEST) && p < valaddr + TYPE_LENGTH (type);
+ p++)
+#else /* Little endian. */
+ first_addr = valaddr;
+ for (p = valaddr + TYPE_LENGTH (type) - 1;
+ len > sizeof (LONGEST) && p >= valaddr;
+ p--)
+#endif /* Little endian. */
+ {
+ if (*p == 0)
+ {
+ len--;
+ }
+ else
+ {
+ break;
+ }
+ }
+#if TARGET_BYTE_ORDER == BIG_ENDIAN
+ first_addr = p;
+#endif
+ if (len <= sizeof (LONGEST))
+ {
+ /* We can print it in decimal. */
+ print_longest (stream, 'u', 0,
+ unpack_long (BUILTIN_TYPE_LONGEST, first_addr));
+ }
+ else
+ {
+ /* It is big, so print it in hex. */
+ print_hex_chars (stream, (unsigned char *) first_addr, len);
+ }
+ }
+ else
+ {
+ /* Signed. One could assume two's complement (a reasonable
+ assumption, I think) and do better than this. */
+ print_hex_chars (stream, (unsigned char *) valaddr,
+ TYPE_LENGTH (type));
+ }
+ }
+ else
+ {
+#ifdef PRINT_TYPELESS_INTEGER
+ PRINT_TYPELESS_INTEGER (stream, type, unpack_long (type, valaddr));
+#else
+ print_longest (stream, TYPE_UNSIGNED (type) ? 'u' : 'd', 0,
+ unpack_long (type, valaddr));
+#endif
+ }
+}
+
+/* Print a number according to FORMAT which is one of d,u,x,o,b,h,w,g.
+ The raison d'etre of this function is to consolidate printing of LONG_LONG's
+ into this one function. Some platforms have long longs but don't have a
+ printf() that supports "ll" in the format string. We handle these by seeing
+ if the number is actually a long, and if not we just bail out and print the
+ number in hex. The format chars b,h,w,g are from
+ print_scalar_formatted(). USE_LOCAL says whether or not to call the
+ local formatting routine to get the format. */
+
+void
+print_longest (stream, format, use_local, val_long)
+ FILE *stream;
+ int format;
+ int use_local;
+ LONGEST val_long;
+{
+#if defined (CC_HAS_LONG_LONG) && !defined (PRINTF_HAS_LONG_LONG)
+ long vtop, vbot;
+
+ vtop = val_long >> (sizeof (long) * HOST_CHAR_BIT);
+ vbot = (long) val_long;
+
+ if ((format == 'd' && (val_long < INT_MIN || val_long > INT_MAX))
+ || ((format == 'u' || format == 'x') && val_long > UINT_MAX))
+ {
+ fprintf_filtered (stream, "0x%lx%08lx", vtop, vbot);
+ return;
+ }
+#endif
+
+#ifdef PRINTF_HAS_LONG_LONG
+ switch (format)
+ {
+ case 'd':
+ fprintf_filtered (stream,
+ use_local ? local_decimal_format_custom ("ll")
+ : "%lld",
+ val_long);
+ break;
+ case 'u':
+ fprintf_filtered (stream, "%llu", val_long);
+ break;
+ case 'x':
+ fprintf_filtered (stream,
+ use_local ? local_hex_format_custom ("ll")
+ : "%llx",
+ val_long);
+ break;
+ case 'o':
+ fprintf_filtered (stream,
+ use_local ? local_octal_format_custom ("ll")
+ : "%llo",
+ break;
+ case 'b':
+ fprintf_filtered (stream, local_hex_format_custom ("02ll"), val_long);
+ break;
+ case 'h':
+ fprintf_filtered (stream, local_hex_format_custom ("04ll"), val_long);
+ break;
+ case 'w':
+ fprintf_filtered (stream, local_hex_format_custom ("08ll"), val_long);
+ break;
+ case 'g':
+ fprintf_filtered (stream, local_hex_format_custom ("016ll"), val_long);
+ break;
+ default:
+ abort ();
+ }
+#else /* !PRINTF_HAS_LONG_LONG */
+ /* In the following it is important to coerce (val_long) to a long. It does
+ nothing if !LONG_LONG, but it will chop off the top half (which we know
+ we can ignore) if the host supports long longs. */
+
+ switch (format)
+ {
+ case 'd':
+ fprintf_filtered (stream,
+ use_local ? local_decimal_format_custom ("l")
+ : "%ld",
+ (long) val_long);
+ break;
+ case 'u':
+ fprintf_filtered (stream, "%lu", (unsigned long) val_long);
+ break;
+ case 'x':
+ fprintf_filtered (stream,
+ use_local ? local_hex_format_custom ("l")
+ : "%lx",
+ (long) val_long);
+ break;
+ case 'o':
+ fprintf_filtered (stream,
+ use_local ? local_octal_format_custom ("l")
+ : "%lo",
+ (long) val_long);
+ break;
+ case 'b':
+ fprintf_filtered (stream, local_hex_format_custom ("02l"),
+ (long) val_long);
+ break;
+ case 'h':
+ fprintf_filtered (stream, local_hex_format_custom ("04l"),
+ (long) val_long);
+ break;
+ case 'w':
+ fprintf_filtered (stream, local_hex_format_custom ("08l"),
+ (long) val_long);
+ break;
+ case 'g':
+ fprintf_filtered (stream, local_hex_format_custom ("016l"),
+ (long) val_long);
+ break;
+ default:
+ abort ();
+ }
+#endif /* !PRINTF_HAS_LONG_LONG */
+}
+
+/* Print a floating point value of type TYPE, pointed to in GDB by VALADDR,
+ on STREAM. */
+
+void
+print_floating (valaddr, type, stream)
+ char *valaddr;
+ struct type *type;
+ FILE *stream;
+{
+ double doub;
+ int inv;
+ unsigned len = TYPE_LENGTH (type);
+
+#if defined (IEEE_FLOAT)
+
+ /* Check for NaN's. Note that this code does not depend on us being
+ on an IEEE conforming system. It only depends on the target
+ machine using IEEE representation. This means (a)
+ cross-debugging works right, and (2) IEEE_FLOAT can (and should)
+ be defined for systems like the 68881, which uses IEEE
+ representation, but is not IEEE conforming. */
+
+ {
+ long low, high;
+ /* Is the sign bit 0? */
+ int nonnegative;
+ /* Is it is a NaN (i.e. the exponent is all ones and
+ the fraction is nonzero)? */
+ int is_nan;
+
+ if (len == sizeof (float))
+ {
+ /* It's single precision. */
+ memcpy ((char *) &low, valaddr, sizeof (low));
+ /* target -> host. */
+ SWAP_TARGET_AND_HOST (&low, sizeof (float));
+ nonnegative = low >= 0;
+ is_nan = ((((low >> 23) & 0xFF) == 0xFF)
+ && 0 != (low & 0x7FFFFF));
+ low &= 0x7fffff;
+ high = 0;
+ }
+ else
+ {
+ /* It's double precision. Get the high and low words. */
+
+#if TARGET_BYTE_ORDER == BIG_ENDIAN
+ memcpy (&low, valaddr+4, sizeof (low));
+ memcpy (&high, valaddr+0, sizeof (high));
+#else
+ memcpy (&low, valaddr+0, sizeof (low));
+ memcpy (&high, valaddr+4, sizeof (high));
+#endif
+ SWAP_TARGET_AND_HOST (&low, sizeof (low));
+ SWAP_TARGET_AND_HOST (&high, sizeof (high));
+ nonnegative = high >= 0;
+ is_nan = (((high >> 20) & 0x7ff) == 0x7ff
+ && ! ((((high & 0xfffff) == 0)) && (low == 0)));
+ high &= 0xfffff;
+ }
+
+ if (is_nan)
+ {
+ /* The meaning of the sign and fraction is not defined by IEEE.
+ But the user might know what they mean. For example, they
+ (in an implementation-defined manner) distinguish between
+ signaling and quiet NaN's. */
+ if (high)
+ fprintf_filtered (stream, "-NaN(0x%lx%.8lx)" + nonnegative,
+ high, low);
+ else
+ fprintf_filtered (stream, "-NaN(0x%lx)" + nonnegative, low);
+ return;
+ }
+ }
+#endif /* IEEE_FLOAT. */
+
+ doub = unpack_double (type, valaddr, &inv);
+ if (inv)
+ fprintf_filtered (stream, "<invalid float value>");
+ else
+ fprintf_filtered (stream, len <= sizeof(float) ? "%.9g" : "%.17g", doub);
+}
+
+/* VALADDR points to an integer of LEN bytes. Print it in hex on stream. */
+
+static void
+print_hex_chars (stream, valaddr, len)
+ FILE *stream;
+ unsigned char *valaddr;
+ unsigned len;
+{
+ unsigned char *p;
+
+ /* FIXME: We should be not printing leading zeroes in most cases. */
+
+ fprintf_filtered (stream, local_hex_format_prefix ());
+#if TARGET_BYTE_ORDER == BIG_ENDIAN
+ for (p = valaddr;
+ p < valaddr + len;
+ p++)
+#else /* Little endian. */
+ for (p = valaddr + len - 1;
+ p >= valaddr;
+ p--)
+#endif
+ {
+ fprintf_filtered (stream, "%02x", *p);
+ }
+ fprintf_filtered (stream, local_hex_format_suffix ());
+}
+
+/* Called by various <lang>_val_print routines to print elements of an
+ array in the form "<elem1>, <elem2>, <elem3>, ...".
+
+ (FIXME?) Assumes array element separator is a comma, which is correct
+ for all languages currently handled.
+ (FIXME?) Some languages have a notation for repeated array elements,
+ perhaps we should try to use that notation when appropriate.
+ */
+
+void
+val_print_array_elements (type, valaddr, address, stream, format, deref_ref,
+ recurse, pretty, i)
+ struct type *type;
+ char *valaddr;
+ CORE_ADDR address;
+ FILE *stream;
+ int format;
+ int deref_ref;
+ int recurse;
+ enum val_prettyprint pretty;
+ unsigned int i;
+{
+ unsigned int things_printed = 0;
+ unsigned len;
+ struct type *elttype;
+ unsigned eltlen;
+ /* Position of the array element we are examining to see
+ whether it is repeated. */
+ unsigned int rep1;
+ /* Number of repetitions we have detected so far. */
+ unsigned int reps;
+
+ elttype = TYPE_TARGET_TYPE (type);
+ eltlen = TYPE_LENGTH (elttype);
+ len = TYPE_LENGTH (type) / eltlen;
+
+ for (; i < len && things_printed < print_max; i++)
+ {
+ if (i != 0)
+ {
+ if (prettyprint_arrays)
+ {
+ fprintf_filtered (stream, ",\n");
+ print_spaces_filtered (2 + 2 * recurse, stream);
+ }
+ else
+ {
+ fprintf_filtered (stream, ", ");
+ }
+ }
+ wrap_here (n_spaces (2 + 2 * recurse));
+
+ rep1 = i + 1;
+ reps = 1;
+ while ((rep1 < len) &&
+ !memcmp (valaddr + i * eltlen, valaddr + rep1 * eltlen, eltlen))
+ {
+ ++reps;
+ ++rep1;
+ }
+
+ if (reps > repeat_count_threshold)
+ {
+ val_print (elttype, valaddr + i * eltlen, 0, stream, format,
+ deref_ref, recurse + 1, pretty);
+ fprintf_filtered (stream, " <repeats %u times>", reps);
+ i = rep1 - 1;
+ things_printed += repeat_count_threshold;
+ }
+ else
+ {
+ val_print (elttype, valaddr + i * eltlen, 0, stream, format,
+ deref_ref, recurse + 1, pretty);
+ things_printed++;
+ }
+ }
+ if (i < len)
+ {
+ fprintf_filtered (stream, "...");
+ }
+}
+
+static void
+value_print_array_elements (val, stream, format, pretty)
+ value val;
+ FILE *stream;
+ int format;
+ enum val_prettyprint pretty;
+{
+ unsigned int things_printed = 0;
+ register unsigned int i, n, typelen;
+ /* Position of the array elem we are examining to see if it is repeated. */
+ unsigned int rep1;
+ /* Number of repetitions we have detected so far. */
+ unsigned int reps;
+
+ n = VALUE_REPETITIONS (val);
+ typelen = TYPE_LENGTH (VALUE_TYPE (val));
+ for (i = 0; i < n && things_printed < print_max; i++)
+ {
+ if (i != 0)
+ {
+ fprintf_filtered (stream, ", ");
+ }
+ wrap_here ("");
+
+ rep1 = i + 1;
+ reps = 1;
+ while (rep1 < n && !memcmp (VALUE_CONTENTS (val) + typelen * i,
+ VALUE_CONTENTS (val) + typelen * rep1,
+ typelen))
+ {
+ ++reps;
+ ++rep1;
+ }
+
+ if (reps > repeat_count_threshold)
+ {
+ val_print (VALUE_TYPE (val), VALUE_CONTENTS (val) + typelen * i,
+ VALUE_ADDRESS (val) + typelen * i, stream, format, 1,
+ 0, pretty);
+ fprintf (stream, " <repeats %u times>", reps);
+ i = rep1 - 1;
+ things_printed += repeat_count_threshold;
+ }
+ else
+ {
+ val_print (VALUE_TYPE (val), VALUE_CONTENTS (val) + typelen * i,
+ VALUE_ADDRESS (val) + typelen * i, stream, format, 1,
+ 0, pretty);
+ things_printed++;
+ }
+ }
+ if (i < n)
+ {
+ fprintf_filtered (stream, "...");
+ }
+}
+
+/* Print a string from the inferior, starting at ADDR and printing up to LEN
+ characters, to STREAM. If LEN is zero, printing stops at the first null
+ byte, otherwise printing proceeds (including null bytes) until either
+ print_max or LEN characters have been printed, whichever is smaller. */
+
+int
+val_print_string (addr, len, stream)
+ CORE_ADDR addr;
+ unsigned int len;
+ FILE *stream;
+{
+ int force_ellipsis = 0; /* Force ellipsis to be printed if nonzero. */
+ int errcode; /* Errno returned from bad reads. */
+ unsigned int fetchlimit; /* Maximum number of bytes to fetch. */
+ unsigned int nfetch; /* Bytes to fetch / bytes fetched. */
+ unsigned int chunksize; /* Size of each fetch, in bytes. */
+ int bufsize; /* Size of current fetch buffer. */
+ char *buffer = NULL; /* Dynamically growable fetch buffer. */
+ char *bufptr; /* Pointer to next available byte in buffer. */
+ char *limit; /* First location past end of fetch buffer. */
+ struct cleanup *old_chain = NULL; /* Top of the old cleanup chain. */
+ char peekchar; /* Place into which we can read one char. */
+
+ /* First we need to figure out the limit on the number of characters we are
+ going to attempt to fetch and print. This is actually pretty simple. If
+ LEN is nonzero, then the limit is the minimum of LEN and print_max. If
+ LEN is zero, then the limit is print_max. This is true regardless of
+ whether print_max is zero, UINT_MAX (unlimited), or something in between,
+ because finding the null byte (or available memory) is what actually
+ limits the fetch. */
+
+ fetchlimit = (len == 0 ? print_max : min (len, print_max));
+
+ /* Now decide how large of chunks to try to read in one operation. This
+ is also pretty simple. If LEN is nonzero, then we want fetchlimit bytes,
+ so we might as well read them all in one operation. If LEN is zero, we
+ are looking for a null terminator to end the fetching, so we might as
+ well read in blocks that are large enough to be efficient, but not so
+ large as to be slow if fetchlimit happens to be large. So we choose the
+ minimum of DEFAULT_PRINT_MAX and fetchlimit. */
+
+ chunksize = (len == 0 ? min (PRINT_MAX_DEFAULT, fetchlimit) : fetchlimit);
+
+ /* Loop until we either have all the characters to print, or we encounter
+ some error, such as bumping into the end of the address space. */
+
+ bufsize = 0;
+ do {
+ QUIT;
+ /* Figure out how much to fetch this time, and grow the buffer to fit. */
+ nfetch = min (chunksize, fetchlimit - bufsize);
+ bufsize += nfetch;
+ if (buffer == NULL)
+ {
+ buffer = (char *) xmalloc (bufsize);
+ bufptr = buffer;
+ }
+ else
+ {
+ discard_cleanups (old_chain);
+ buffer = (char *) xrealloc (buffer, bufsize);
+ bufptr = buffer + bufsize - nfetch;
+ }
+ old_chain = make_cleanup (free, buffer);
+
+ /* Read as much as we can. */
+ nfetch = target_read_memory_partial (addr, bufptr, nfetch, &errcode);
+ if (len != 0)
+ {
+ addr += nfetch;
+ bufptr += nfetch;
+ }
+ else
+ {
+ /* Scan this chunk for the null byte that terminates the string
+ to print. If found, we don't need to fetch any more. Note
+ that bufptr is explicitly left pointing at the next character
+ after the null byte, or at the next character after the end of
+ the buffer. */
+ limit = bufptr + nfetch;
+ do {
+ addr++;
+ bufptr++;
+ } while (bufptr < limit && *(bufptr - 1) != '\0');
+ }
+ } while (errcode == 0 /* no error */
+ && bufptr < buffer + fetchlimit /* no overrun */
+ && !(len == 0 && *(bufptr - 1) == '\0')); /* no null term */
+
+ /* We now have either successfully filled the buffer to fetchlimit, or
+ terminated early due to an error or finding a null byte when LEN is
+ zero. */
+
+ if (len == 0 && *(bufptr - 1) != '\0')
+ {
+ /* We didn't find a null terminator we were looking for. Attempt
+ to peek at the next character. If not successful, or it is not
+ a null byte, then force ellipsis to be printed. */
+ if (target_read_memory (addr, &peekchar, 1) != 0 || peekchar != '\0')
+ {
+ force_ellipsis = 1;
+ }
+ }
+ else if ((len != 0 && errcode != 0) || (len > bufptr - buffer))
+ {
+ /* Getting an error when we have a requested length, or fetching less
+ than the number of characters actually requested, always make us
+ print ellipsis. */
+ force_ellipsis = 1;
+ }
+
+ QUIT;
+
+ if (addressprint)
+ {
+ fputs_filtered (" ", stream);
+ }
+ LA_PRINT_STRING (stream, buffer, bufptr - buffer, force_ellipsis);
+
+ if (errcode != 0 && force_ellipsis)
+ {
+ if (errcode == EIO)
+ {
+ fprintf_filtered (stream,
+ " <Address 0x%lx out of bounds>",
+ (unsigned long) addr);
+ }
+ else
+ {
+ error ("Error reading memory address 0x%lx: %s.",
+ (unsigned long) addr,
+ safe_strerror (errcode));
+ }
+ }
+ fflush (stream);
+ do_cleanups (old_chain);
+ return (bufptr - buffer);
+}
+
+
+/* Validate an input or output radix setting, and make sure the user
+ knows what they really did here. Radix setting is confusing, e.g.
+ setting the input radix to "10" never changes it! */
+
+/* ARGSUSED */
+static void
+set_input_radix (args, from_tty, c)
+ char *args;
+ int from_tty;
+ struct cmd_list_element *c;
+{
+ set_input_radix_1 (from_tty, *(unsigned *)c->var);
+}
+
+/* ARGSUSED */
+static void
+set_input_radix_1 (from_tty, radix)
+ int from_tty;
+ unsigned radix;
+{
+ /* We don't currently disallow any input radix except 0 or 1, which don't
+ make any mathematical sense. In theory, we can deal with any input
+ radix greater than 1, even if we don't have unique digits for every
+ value from 0 to radix-1, but in practice we lose on large radix values.
+ We should either fix the lossage or restrict the radix range more.
+ (FIXME). */
+
+ if (radix < 2)
+ {
+ error ("Nonsense input radix ``decimal %u''; input radix unchanged.",
+ radix);
+ }
+ input_radix = radix;
+ if (from_tty)
+ {
+ printf_filtered ("Input radix now set to decimal %u, hex %x, octal %o.\n",
+ radix, radix, radix);
+ }
+}
+
+/* ARGSUSED */
+static void
+set_output_radix (args, from_tty, c)
+ char *args;
+ int from_tty;
+ struct cmd_list_element *c;
+{
+ set_output_radix_1 (from_tty, *(unsigned *)c->var);
+}
+
+static void
+set_output_radix_1 (from_tty, radix)
+ int from_tty;
+ unsigned radix;
+{
+ /* Validate the radix and disallow ones that we aren't prepared to
+ handle correctly, leaving the radix unchanged. */
+ switch (radix)
+ {
+ case 16:
+ output_format = 'x'; /* hex */
+ break;
+ case 10:
+ output_format = 0; /* decimal */
+ break;
+ case 8:
+ output_format = 'o'; /* octal */
+ break;
+ default:
+ error ("Unsupported output radix ``decimal %u''; output radix unchanged.",
+ radix);
+ }
+ output_radix = radix;
+ if (from_tty)
+ {
+ printf_filtered ("Output radix now set to decimal %u, hex %x, octal %o.\n",
+ radix, radix, radix);
+ }
+}
+
+/* Set both the input and output radix at once. Try to set the output radix
+ first, since it has the most restrictive range. An radix that is valid as
+ an output radix is also valid as an input radix.
+
+ It may be useful to have an unusual input radix. If the user wishes to
+ set an input radix that is not valid as an output radix, he needs to use
+ the 'set input-radix' command. */
+
+static void
+set_radix (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ unsigned radix;
+
+ radix = (arg == NULL) ? 10 : parse_and_eval_address (arg);
+ set_output_radix_1 (0, radix);
+ set_input_radix_1 (0, radix);
+ if (from_tty)
+ {
+ printf_filtered ("Input and output radices now set to decimal %u, hex %x, octal %o.\n",
+ radix, radix, radix);
+ }
+}
+
+/* Show both the input and output radices. */
+
+/*ARGSUSED*/
+static void
+show_radix (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ if (from_tty)
+ {
+ if (input_radix == output_radix)
+ {
+ printf_filtered ("Input and output radices set to decimal %u, hex %x, octal %o.\n",
+ input_radix, input_radix, input_radix);
+ }
+ else
+ {
+ printf_filtered ("Input radix set to decimal %u, hex %x, octal %o.\n",
+ input_radix, input_radix, input_radix);
+ printf_filtered ("Output radix set to decimal %u, hex %x, octal %o.\n",
+ output_radix, output_radix, output_radix);
+ }
+ }
+}
+
+
+/*ARGSUSED*/
+static void
+set_print (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ printf (
+"\"set print\" must be followed by the name of a print subcommand.\n");
+ help_list (setprintlist, "set print ", -1, stdout);
+}
+
+/*ARGSUSED*/
+static void
+show_print (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ cmd_show_list (showprintlist, from_tty, "");
+}
+
+void
+_initialize_valprint ()
+{
+ struct cmd_list_element *c;
+
+ add_prefix_cmd ("print", no_class, set_print,
+ "Generic command for setting how things print.",
+ &setprintlist, "set print ", 0, &setlist);
+ add_alias_cmd ("p", "print", no_class, 1, &setlist);
+ /* prefer set print to set prompt */
+ add_alias_cmd ("pr", "print", no_class, 1, &setlist);
+
+ add_prefix_cmd ("print", no_class, show_print,
+ "Generic command for showing print settings.",
+ &showprintlist, "show print ", 0, &showlist);
+ add_alias_cmd ("p", "print", no_class, 1, &showlist);
+ add_alias_cmd ("pr", "print", no_class, 1, &showlist);
+
+ add_show_from_set
+ (add_set_cmd ("elements", no_class, var_uinteger, (char *)&print_max,
+ "Set limit on string chars or array elements to print.\n\
+\"set print elements 0\" causes there to be no limit.",
+ &setprintlist),
+ &showprintlist);
+
+ add_show_from_set
+ (add_set_cmd ("repeats", no_class, var_uinteger,
+ (char *)&repeat_count_threshold,
+ "Set threshold for repeated print elements.\n\
+\"set print repeats 0\" causes all elements to be individually printed.",
+ &setprintlist),
+ &showprintlist);
+
+ add_show_from_set
+ (add_set_cmd ("pretty", class_support, var_boolean,
+ (char *)&prettyprint_structs,
+ "Set prettyprinting of structures.",
+ &setprintlist),
+ &showprintlist);
+
+ add_show_from_set
+ (add_set_cmd ("union", class_support, var_boolean, (char *)&unionprint,
+ "Set printing of unions interior to structures.",
+ &setprintlist),
+ &showprintlist);
+
+ add_show_from_set
+ (add_set_cmd ("array", class_support, var_boolean,
+ (char *)&prettyprint_arrays,
+ "Set prettyprinting of arrays.",
+ &setprintlist),
+ &showprintlist);
+
+ add_show_from_set
+ (add_set_cmd ("address", class_support, var_boolean, (char *)&addressprint,
+ "Set printing of addresses.",
+ &setprintlist),
+ &showprintlist);
+
+ c = add_set_cmd ("input-radix", class_support, var_uinteger,
+ (char *)&input_radix,
+ "Set default input radix for entering numbers.",
+ &setlist);
+ add_show_from_set (c, &showlist);
+ c->function.sfunc = set_input_radix;
+
+ c = add_set_cmd ("output-radix", class_support, var_uinteger,
+ (char *)&output_radix,
+ "Set default output radix for printing of values.",
+ &setlist);
+ add_show_from_set (c, &showlist);
+ c->function.sfunc = set_output_radix;
+
+ /* The "set radix" and "show radix" commands are special in that they are
+ like normal set and show commands but allow two normally independent
+ variables to be either set or shown with a single command. So the
+ usual add_set_cmd() and add_show_from_set() commands aren't really
+ appropriate. */
+ add_cmd ("radix", class_support, set_radix,
+ "Set default input and output number radices.\n\
+Use 'set input-radix' or 'set output-radix' to independently set each.\n\
+Without an argument, sets both radices back to the default value of 10.",
+ &setlist);
+ add_cmd ("radix", class_support, show_radix,
+ "Show the default input and output number radices.\n\
+Use 'show input-radix' or 'show output-radix' to independently show each.",
+ &showlist);
+
+ /* Give people the defaults which they are used to. */
+ prettyprint_structs = 0;
+ prettyprint_arrays = 0;
+ unionprint = 1;
+ addressprint = 1;
+ print_max = PRINT_MAX_DEFAULT;
+}
diff --git a/gnu/usr.bin/gdb/gdb/valprint.h b/gnu/usr.bin/gdb/gdb/valprint.h
new file mode 100644
index 0000000..5918def
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/valprint.h
@@ -0,0 +1,40 @@
+/* Declarations for value printing routines for GDB, the GNU debugger.
+ Copyright 1986, 1988, 1989, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+extern int prettyprint_arrays; /* Controls pretty printing of arrays. */
+extern int prettyprint_structs; /* Controls pretty printing of structures */
+extern int prettyprint_arrays; /* Controls pretty printing of arrays. */
+
+extern int vtblprint; /* Controls printing of vtbl's */
+extern int unionprint; /* Controls printing of nested unions. */
+extern int addressprint; /* Controls pretty printing of addresses. */
+extern int objectprint; /* Controls looking up an object's derived type
+ using what we find in its vtables. */
+
+extern unsigned int print_max; /* Max # of chars for strings/vectors */
+extern int output_format;
+
+extern void
+val_print_array_elements PARAMS ((struct type *, char *, CORE_ADDR, FILE *,
+ int, int, int, enum val_prettyprint, int));
+
+extern void
+val_print_type_code_int PARAMS ((struct type *, char *, FILE *));
+
diff --git a/gnu/usr.bin/gdb/gdb/value.h b/gnu/usr.bin/gdb/gdb/value.h
new file mode 100644
index 0000000..b8e1676
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/value.h
@@ -0,0 +1,502 @@
+/* Definitions for values of C expressions, for GDB.
+ Copyright 1986, 1987, 1989, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (VALUE_H)
+#define VALUE_H 1
+
+/*
+ * The structure which defines the type of a value. It should never
+ * be possible for a program lval value to survive over a call to the inferior
+ * (ie to be put into the history list or an internal variable).
+ */
+enum lval_type {
+ /* Not an lval. */
+ not_lval,
+ /* In memory. Could be a saved register. */
+ lval_memory,
+ /* In a register. */
+ lval_register,
+ /* In a gdb internal variable. */
+ lval_internalvar,
+ /* Part of a gdb internal variable (structure field). */
+ lval_internalvar_component,
+ /* In a register series in a frame not the current one, which may have been
+ partially saved or saved in different places (otherwise would be
+ lval_register or lval_memory). */
+ lval_reg_frame_relative
+};
+
+struct value
+ {
+ /* Type of value; either not an lval, or one of the various
+ different possible kinds of lval. */
+ enum lval_type lval;
+ /* Location of value (if lval). */
+ union
+ {
+ /* Address in inferior or byte of registers structure. */
+ CORE_ADDR address;
+ /* Pointer to internal variable. */
+ struct internalvar *internalvar;
+ /* Number of register. Only used with
+ lval_reg_frame_relative. */
+ int regnum;
+ } location;
+ /* Describes offset of a value within lval a structure in bytes. */
+ int offset;
+ /* Only used for bitfields; number of bits contained in them. */
+ int bitsize;
+ /* Only used for bitfields; position of start of field.
+ For BITS_BIG_ENDIAN=0 targets, it is the position of the LSB.
+ For BITS_BIG_ENDIAN=1 targets, it is the position of the MSB. */
+ int bitpos;
+ /* Frame value is relative to. In practice, this address is only
+ used if the value is stored in several registers in other than
+ the current frame, and these registers have not all been saved
+ at the same place in memory. This will be described in the
+ lval enum above as "lval_reg_frame_relative". */
+ CORE_ADDR frame_addr;
+ /* Type of the value. */
+ struct type *type;
+ /* Values are stored in a chain, so that they can be deleted
+ easily over calls to the inferior. Values assigned to internal
+ variables or put into the value history are taken off this
+ list. */
+ struct value *next;
+ /* If an lval is forced to repeat, a new value is created with
+ these fields set. The new value is not an lval. */
+ short repeated;
+ short repetitions;
+ /* Register number if the value is from a register. Is not kept
+ if you take a field of a structure that is stored in a
+ register. Shouldn't it be? */
+ short regno;
+ /* If zero, contents of this value are in the contents field.
+ If nonzero, contents are in inferior memory at address
+ in the location.address field plus the offset field
+ (and the lval field should be lval_memory). */
+ char lazy;
+ /* If nonzero, this is the value of a variable which does not
+ actually exist in the program. */
+ char optimized_out;
+ /* Actual contents of the value. For use of this value; setting
+ it uses the stuff above. Not valid if lazy is nonzero.
+ Target byte-order. We force it to be aligned properly for any
+ possible value. */
+ union {
+ long contents[1];
+ double force_double_align;
+#ifdef CC_HAS_LONG_LONG
+ long long force_longlong_align;
+#endif
+ } aligner;
+
+ };
+
+typedef struct value *value;
+
+#define VALUE_TYPE(val) (val)->type
+#define VALUE_LAZY(val) (val)->lazy
+/* VALUE_CONTENTS and VALUE_CONTENTS_RAW both return the address of
+ the gdb buffer used to hold a copy of the contents of the lval.
+ VALUE_CONTENTS is used when the contents of the buffer are needed --
+ it uses value_fetch_lazy() to load the buffer from the process being
+ debugged if it hasn't already been loaded. VALUE_CONTENTS_RAW is
+ used when data is being stored into the buffer, or when it is
+ certain that the contents of the buffer are valid. */
+#define VALUE_CONTENTS_RAW(val) ((char *) (val)->aligner.contents)
+#define VALUE_CONTENTS(val) ((void)(VALUE_LAZY(val) && value_fetch_lazy(val)),\
+ VALUE_CONTENTS_RAW(val))
+extern int
+value_fetch_lazy PARAMS ((value val));
+
+#define VALUE_LVAL(val) (val)->lval
+#define VALUE_ADDRESS(val) (val)->location.address
+#define VALUE_INTERNALVAR(val) (val)->location.internalvar
+#define VALUE_FRAME_REGNUM(val) ((val)->location.regnum)
+#define VALUE_FRAME(val) ((val)->frame_addr)
+#define VALUE_OFFSET(val) (val)->offset
+#define VALUE_BITSIZE(val) (val)->bitsize
+#define VALUE_BITPOS(val) (val)->bitpos
+#define VALUE_NEXT(val) (val)->next
+#define VALUE_REPEATED(val) (val)->repeated
+#define VALUE_REPETITIONS(val) (val)->repetitions
+#define VALUE_REGNO(val) (val)->regno
+#define VALUE_OPTIMIZED_OUT(val) ((val)->optimized_out)
+
+/* Convert a REF to the object referenced. */
+
+#define COERCE_REF(arg) \
+{ if (TYPE_CODE (VALUE_TYPE (arg)) == TYPE_CODE_REF) \
+ arg = value_at_lazy (TYPE_TARGET_TYPE (VALUE_TYPE (arg)), \
+ unpack_long (VALUE_TYPE (arg), \
+ VALUE_CONTENTS (arg)));}
+
+/* If ARG is an array, convert it to a pointer.
+ If ARG is an enum, convert it to an integer.
+ If ARG is a function, convert it to a function pointer.
+
+ References are dereferenced. */
+
+#define COERCE_ARRAY(arg) \
+{ COERCE_REF(arg); \
+ if (VALUE_REPEATED (arg) \
+ || TYPE_CODE (VALUE_TYPE (arg)) == TYPE_CODE_ARRAY) \
+ arg = value_coerce_array (arg); \
+ if (TYPE_CODE (VALUE_TYPE (arg)) == TYPE_CODE_FUNC) \
+ arg = value_coerce_function (arg); \
+ if (TYPE_CODE (VALUE_TYPE (arg)) == TYPE_CODE_ENUM) \
+ arg = value_cast (builtin_type_unsigned_int, arg); \
+}
+
+/* If ARG is an enum, convert it to an integer. */
+
+#define COERCE_ENUM(arg) \
+{ COERCE_REF (arg); \
+ if (TYPE_CODE (VALUE_TYPE (arg)) == TYPE_CODE_ENUM) \
+ arg = value_cast (builtin_type_unsigned_int, arg); \
+}
+
+/* Internal variables (variables for convenience of use of debugger)
+ are recorded as a chain of these structures. */
+
+struct internalvar
+{
+ struct internalvar *next;
+ char *name;
+ value value;
+};
+
+/* Pointer to member function. Depends on compiler implementation. */
+
+#define METHOD_PTR_IS_VIRTUAL(ADDR) ((ADDR) & 0x80000000)
+#define METHOD_PTR_FROM_VOFFSET(OFFSET) (0x80000000 + (OFFSET))
+#define METHOD_PTR_TO_VOFFSET(ADDR) (~0x80000000 & (ADDR))
+
+
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "expression.h"
+
+#ifdef __STDC__
+struct frame_info;
+struct fn_field;
+#endif
+
+extern void
+print_address_demangle PARAMS ((CORE_ADDR, FILE *, int));
+
+extern LONGEST
+value_as_long PARAMS ((value val));
+
+extern double
+value_as_double PARAMS ((value val));
+
+extern CORE_ADDR
+value_as_pointer PARAMS ((value val));
+
+extern LONGEST
+unpack_long PARAMS ((struct type *type, char *valaddr));
+
+extern double
+unpack_double PARAMS ((struct type *type, char *valaddr, int *invp));
+
+extern CORE_ADDR unpack_pointer PARAMS ((struct type *type, char *valaddr));
+
+extern LONGEST unpack_field_as_long PARAMS ((struct type *type, char *valaddr,
+ int fieldno));
+
+extern value value_from_longest PARAMS ((struct type *type, LONGEST num));
+
+extern value value_from_double PARAMS ((struct type *type, double num));
+
+extern value value_at PARAMS ((struct type *type, CORE_ADDR addr));
+
+extern value value_at_lazy PARAMS ((struct type *type, CORE_ADDR addr));
+
+/* FIXME: Assumes equivalence of "struct frame_info *" and "FRAME" */
+extern value value_from_register PARAMS ((struct type *type, int regnum,
+ struct frame_info * frame));
+
+extern value value_of_variable PARAMS ((struct symbol *var, struct block *b));
+
+extern value value_of_register PARAMS ((int regnum));
+
+extern int symbol_read_needs_frame PARAMS ((struct symbol *));
+
+/* FIXME: Assumes equivalence of "struct frame_info *" and "FRAME" */
+extern value read_var_value PARAMS ((struct symbol *var,
+ struct frame_info *frame));
+
+/* FIXME: Assumes equivalence of "struct frame_info *" and "FRAME" */
+extern value locate_var_value PARAMS ((struct symbol *var,
+ struct frame_info *frame));
+
+extern value allocate_value PARAMS ((struct type *type));
+
+extern value allocate_repeat_value PARAMS ((struct type *type, int count));
+
+extern value value_mark PARAMS ((void));
+
+extern void value_free_to_mark PARAMS ((value mark));
+
+extern value value_string PARAMS ((char *ptr, int len));
+
+extern value value_array PARAMS ((int lowbound, int highbound,
+ value *elemvec));
+
+extern value value_concat PARAMS ((value arg1, value arg2));
+
+extern value value_binop PARAMS ((value arg1, value arg2, enum exp_opcode op));
+
+extern value value_add PARAMS ((value arg1, value arg2));
+
+extern value value_sub PARAMS ((value arg1, value arg2));
+
+extern value value_coerce_array PARAMS ((value arg1));
+
+extern value value_coerce_function PARAMS ((value arg1));
+
+extern value value_ind PARAMS ((value arg1));
+
+extern value value_addr PARAMS ((value arg1));
+
+extern value value_assign PARAMS ((value toval, value fromval));
+
+extern value value_neg PARAMS ((value arg1));
+
+extern value value_complement PARAMS ((value arg1));
+
+extern value value_struct_elt PARAMS ((value *argp, value *args, char *name,
+ int *static_memfuncp, char *err));
+
+extern value value_struct_elt_for_reference PARAMS ((struct type *domain,
+ int offset,
+ struct type *curtype,
+ char *name,
+ struct type *intype));
+
+extern value value_field PARAMS ((value arg1, int fieldno));
+
+extern value value_primitive_field PARAMS ((value arg1, int offset,
+ int fieldno,
+ struct type *arg_type));
+
+extern value value_cast PARAMS ((struct type *type, value arg2));
+
+extern value value_zero PARAMS ((struct type *type, enum lval_type lv));
+
+extern value value_repeat PARAMS ((value arg1, int count));
+
+extern value value_subscript PARAMS ((value array, value idx));
+
+extern value value_from_vtable_info PARAMS ((value arg, struct type *type));
+
+extern value value_being_returned PARAMS ((struct type *valtype,
+ char retbuf[REGISTER_BYTES],
+ int struct_return));
+
+extern int
+using_struct_return PARAMS ((value function, CORE_ADDR funcaddr,
+ struct type *value_type, int gcc_p));
+
+extern void
+set_return_value PARAMS ((value val));
+
+extern value
+evaluate_expression PARAMS ((struct expression *exp));
+
+extern value
+evaluate_type PARAMS ((struct expression *exp));
+
+extern value
+parse_and_eval PARAMS ((char *exp));
+
+extern value
+parse_to_comma_and_eval PARAMS ((char **expp));
+
+extern struct type *
+parse_and_eval_type PARAMS ((char *p, int length));
+
+extern CORE_ADDR
+parse_and_eval_address PARAMS ((char *exp));
+
+extern CORE_ADDR
+parse_and_eval_address_1 PARAMS ((char **expptr));
+
+extern value
+access_value_history PARAMS ((int num));
+
+extern value
+value_of_internalvar PARAMS ((struct internalvar *var));
+
+extern void
+set_internalvar PARAMS ((struct internalvar *var, value val));
+
+extern void
+set_internalvar_component PARAMS ((struct internalvar *var, int offset,
+ int bitpos, int bitsize,
+ value newvalue));
+
+extern struct internalvar *
+lookup_internalvar PARAMS ((char *name));
+
+extern int
+value_equal PARAMS ((value arg1, value arg2));
+
+extern int
+value_less PARAMS ((value arg1, value arg2));
+
+extern int
+value_logical_not PARAMS ((value arg1));
+
+/* C++ */
+
+extern value
+value_of_this PARAMS ((int complain));
+
+extern value
+value_x_binop PARAMS ((value arg1, value arg2, enum exp_opcode op,
+ enum exp_opcode otherop));
+
+extern value
+value_x_unop PARAMS ((value arg1, enum exp_opcode op));
+
+extern value
+value_fn_field PARAMS ((value *arg1p, struct fn_field *f, int j,
+ struct type* type, int offset));
+
+extern value
+value_virtual_fn_field PARAMS ((value *arg1p, struct fn_field *f, int j,
+ struct type *type, int offset));
+
+extern int
+binop_user_defined_p PARAMS ((enum exp_opcode op, value arg1, value arg2));
+
+extern int
+unop_user_defined_p PARAMS ((enum exp_opcode op, value arg1));
+
+extern int
+destructor_name_p PARAMS ((const char *name, const struct type *type));
+
+#define value_free(val) free ((PTR)val)
+
+extern void
+free_all_values PARAMS ((void));
+
+extern void
+release_value PARAMS ((value val));
+
+extern int
+record_latest_value PARAMS ((value val));
+
+extern void
+registers_changed PARAMS ((void));
+
+extern void
+read_register_bytes PARAMS ((int regbyte, char *myaddr, int len));
+
+extern void
+write_register_bytes PARAMS ((int regbyte, char *myaddr, int len));
+
+extern void
+read_register_gen PARAMS ((int regno, char *myaddr));
+
+extern CORE_ADDR
+read_register PARAMS ((int regno));
+
+extern void
+write_register PARAMS ((int regno, LONGEST val));
+
+extern void
+supply_register PARAMS ((int regno, char *val));
+
+/* FIXME: Assumes equivalence of "struct frame_info *" and "FRAME" */
+extern void
+get_saved_register PARAMS ((char *raw_buffer, int *optimized,
+ CORE_ADDR *addrp, struct frame_info *frame,
+ int regnum, enum lval_type *lval));
+
+extern void
+modify_field PARAMS ((char *addr, LONGEST fieldval, int bitpos, int bitsize));
+
+extern void
+type_print PARAMS ((struct type *type, char *varstring, FILE *stream,
+ int show));
+
+extern char *
+baseclass_addr PARAMS ((struct type *type, int index, char *valaddr,
+ value *valuep, int *errp));
+
+extern void
+print_longest PARAMS ((FILE *stream, int format, int use_local,
+ LONGEST value));
+
+extern void
+print_floating PARAMS ((char *valaddr, struct type *type, FILE *stream));
+
+extern int
+value_print PARAMS ((value val, FILE *stream, int format,
+ enum val_prettyprint pretty));
+
+extern int
+val_print PARAMS ((struct type *type, char *valaddr, CORE_ADDR address,
+ FILE *stream, int format, int deref_ref,
+ int recurse, enum val_prettyprint pretty));
+
+extern int
+val_print_string PARAMS ((CORE_ADDR addr, unsigned int len, FILE *stream));
+
+/* FIXME: Assumes equivalence of "struct frame_info *" and "FRAME" */
+extern void
+print_variable_value PARAMS ((struct symbol *var, struct frame_info *frame,
+ FILE *stream));
+
+extern value
+value_arg_coerce PARAMS ((value));
+
+extern int
+check_field PARAMS ((value, const char *));
+
+extern void
+c_typedef_print PARAMS ((struct type *type, struct symbol *new, FILE *stream));
+
+extern char *
+internalvar_name PARAMS ((struct internalvar *var));
+
+extern void
+clear_value_history PARAMS ((void));
+
+extern void
+clear_internalvars PARAMS ((void));
+
+/* From values.c */
+
+extern value
+value_copy PARAMS ((value));
+
+extern int
+baseclass_offset PARAMS ((struct type *, int, value, int));
+
+/* From valops.c */
+
+extern value
+call_function_by_hand PARAMS ((value, int, value *));
+
+#endif /* !defined (VALUE_H) */
diff --git a/gnu/usr.bin/gdb/gdb/values.c b/gnu/usr.bin/gdb/gdb/values.c
new file mode 100644
index 0000000..a592f9e
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/values.c
@@ -0,0 +1,1502 @@
+/* Low level packing and unpacking of values for GDB, the GNU Debugger.
+ Copyright 1986, 1987, 1989, 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include <string.h>
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "value.h"
+#include "gdbcore.h"
+#include "frame.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "target.h"
+#include "demangle.h"
+
+/* Local function prototypes. */
+
+static value
+value_headof PARAMS ((value, struct type *, struct type *));
+
+static void
+show_values PARAMS ((char *, int));
+
+static void
+show_convenience PARAMS ((char *, int));
+
+/* The value-history records all the values printed
+ by print commands during this session. Each chunk
+ records 60 consecutive values. The first chunk on
+ the chain records the most recent values.
+ The total number of values is in value_history_count. */
+
+#define VALUE_HISTORY_CHUNK 60
+
+struct value_history_chunk
+{
+ struct value_history_chunk *next;
+ value values[VALUE_HISTORY_CHUNK];
+};
+
+/* Chain of chunks now in use. */
+
+static struct value_history_chunk *value_history_chain;
+
+static int value_history_count; /* Abs number of last entry stored */
+
+/* List of all value objects currently allocated
+ (except for those released by calls to release_value)
+ This is so they can be freed after each command. */
+
+static value all_values;
+
+/* Allocate a value that has the correct length for type TYPE. */
+
+value
+allocate_value (type)
+ struct type *type;
+{
+ register value val;
+
+ check_stub_type (type);
+
+ val = (value) xmalloc (sizeof (struct value) + TYPE_LENGTH (type));
+ VALUE_NEXT (val) = all_values;
+ all_values = val;
+ VALUE_TYPE (val) = type;
+ VALUE_LVAL (val) = not_lval;
+ VALUE_ADDRESS (val) = 0;
+ VALUE_FRAME (val) = 0;
+ VALUE_OFFSET (val) = 0;
+ VALUE_BITPOS (val) = 0;
+ VALUE_BITSIZE (val) = 0;
+ VALUE_REPEATED (val) = 0;
+ VALUE_REPETITIONS (val) = 0;
+ VALUE_REGNO (val) = -1;
+ VALUE_LAZY (val) = 0;
+ VALUE_OPTIMIZED_OUT (val) = 0;
+ return val;
+}
+
+/* Allocate a value that has the correct length
+ for COUNT repetitions type TYPE. */
+
+value
+allocate_repeat_value (type, count)
+ struct type *type;
+ int count;
+{
+ register value val;
+
+ val = (value) xmalloc (sizeof (struct value) + TYPE_LENGTH (type) * count);
+ VALUE_NEXT (val) = all_values;
+ all_values = val;
+ VALUE_TYPE (val) = type;
+ VALUE_LVAL (val) = not_lval;
+ VALUE_ADDRESS (val) = 0;
+ VALUE_FRAME (val) = 0;
+ VALUE_OFFSET (val) = 0;
+ VALUE_BITPOS (val) = 0;
+ VALUE_BITSIZE (val) = 0;
+ VALUE_REPEATED (val) = 1;
+ VALUE_REPETITIONS (val) = count;
+ VALUE_REGNO (val) = -1;
+ VALUE_LAZY (val) = 0;
+ VALUE_OPTIMIZED_OUT (val) = 0;
+ return val;
+}
+
+/* Return a mark in the value chain. All values allocated after the
+ mark is obtained (except for those released) are subject to being freed
+ if a subsequent value_free_to_mark is passed the mark. */
+value
+value_mark ()
+{
+ return all_values;
+}
+
+/* Free all values allocated since MARK was obtained by value_mark
+ (except for those released). */
+void
+value_free_to_mark (mark)
+ value mark;
+{
+ value val, next;
+
+ for (val = all_values; val && val != mark; val = next)
+ {
+ next = VALUE_NEXT (val);
+ value_free (val);
+ }
+ all_values = val;
+}
+
+/* Free all the values that have been allocated (except for those released).
+ Called after each command, successful or not. */
+
+void
+free_all_values ()
+{
+ register value val, next;
+
+ for (val = all_values; val; val = next)
+ {
+ next = VALUE_NEXT (val);
+ value_free (val);
+ }
+
+ all_values = 0;
+}
+
+/* Remove VAL from the chain all_values
+ so it will not be freed automatically. */
+
+void
+release_value (val)
+ register value val;
+{
+ register value v;
+
+ if (all_values == val)
+ {
+ all_values = val->next;
+ return;
+ }
+
+ for (v = all_values; v; v = v->next)
+ {
+ if (v->next == val)
+ {
+ v->next = val->next;
+ break;
+ }
+ }
+}
+
+/* Return a copy of the value ARG.
+ It contains the same contents, for same memory address,
+ but it's a different block of storage. */
+
+value
+value_copy (arg)
+ value arg;
+{
+ register value val;
+ register struct type *type = VALUE_TYPE (arg);
+ if (VALUE_REPEATED (arg))
+ val = allocate_repeat_value (type, VALUE_REPETITIONS (arg));
+ else
+ val = allocate_value (type);
+ VALUE_LVAL (val) = VALUE_LVAL (arg);
+ VALUE_ADDRESS (val) = VALUE_ADDRESS (arg);
+ VALUE_OFFSET (val) = VALUE_OFFSET (arg);
+ VALUE_BITPOS (val) = VALUE_BITPOS (arg);
+ VALUE_BITSIZE (val) = VALUE_BITSIZE (arg);
+ VALUE_REGNO (val) = VALUE_REGNO (arg);
+ VALUE_LAZY (val) = VALUE_LAZY (arg);
+ if (!VALUE_LAZY (val))
+ {
+ memcpy (VALUE_CONTENTS_RAW (val), VALUE_CONTENTS_RAW (arg),
+ TYPE_LENGTH (VALUE_TYPE (arg))
+ * (VALUE_REPEATED (arg) ? VALUE_REPETITIONS (arg) : 1));
+ }
+ return val;
+}
+
+/* Access to the value history. */
+
+/* Record a new value in the value history.
+ Returns the absolute history index of the entry.
+ Result of -1 indicates the value was not saved; otherwise it is the
+ value history index of this new item. */
+
+int
+record_latest_value (val)
+ value val;
+{
+ int i;
+
+ /* Check error now if about to store an invalid float. We return -1
+ to the caller, but allow them to continue, e.g. to print it as "Nan". */
+ if (TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_FLT)
+ {
+ unpack_double (VALUE_TYPE (val), VALUE_CONTENTS (val), &i);
+ if (i) return -1; /* Indicate value not saved in history */
+ }
+
+ /* Here we treat value_history_count as origin-zero
+ and applying to the value being stored now. */
+
+ i = value_history_count % VALUE_HISTORY_CHUNK;
+ if (i == 0)
+ {
+ register struct value_history_chunk *new
+ = (struct value_history_chunk *)
+ xmalloc (sizeof (struct value_history_chunk));
+ memset (new->values, 0, sizeof new->values);
+ new->next = value_history_chain;
+ value_history_chain = new;
+ }
+
+ value_history_chain->values[i] = val;
+
+ /* We don't want this value to have anything to do with the inferior anymore.
+ In particular, "set $1 = 50" should not affect the variable from which
+ the value was taken, and fast watchpoints should be able to assume that
+ a value on the value history never changes. */
+ if (VALUE_LAZY (val))
+ value_fetch_lazy (val);
+ VALUE_LVAL (val) = not_lval;
+ release_value (val);
+
+ /* Now we regard value_history_count as origin-one
+ and applying to the value just stored. */
+
+ return ++value_history_count;
+}
+
+/* Return a copy of the value in the history with sequence number NUM. */
+
+value
+access_value_history (num)
+ int num;
+{
+ register struct value_history_chunk *chunk;
+ register int i;
+ register int absnum = num;
+
+ if (absnum <= 0)
+ absnum += value_history_count;
+
+ if (absnum <= 0)
+ {
+ if (num == 0)
+ error ("The history is empty.");
+ else if (num == 1)
+ error ("There is only one value in the history.");
+ else
+ error ("History does not go back to $$%d.", -num);
+ }
+ if (absnum > value_history_count)
+ error ("History has not yet reached $%d.", absnum);
+
+ absnum--;
+
+ /* Now absnum is always absolute and origin zero. */
+
+ chunk = value_history_chain;
+ for (i = (value_history_count - 1) / VALUE_HISTORY_CHUNK - absnum / VALUE_HISTORY_CHUNK;
+ i > 0; i--)
+ chunk = chunk->next;
+
+ return value_copy (chunk->values[absnum % VALUE_HISTORY_CHUNK]);
+}
+
+/* Clear the value history entirely.
+ Must be done when new symbol tables are loaded,
+ because the type pointers become invalid. */
+
+void
+clear_value_history ()
+{
+ register struct value_history_chunk *next;
+ register int i;
+ register value val;
+
+ while (value_history_chain)
+ {
+ for (i = 0; i < VALUE_HISTORY_CHUNK; i++)
+ if ((val = value_history_chain->values[i]) != NULL)
+ free ((PTR)val);
+ next = value_history_chain->next;
+ free ((PTR)value_history_chain);
+ value_history_chain = next;
+ }
+ value_history_count = 0;
+}
+
+static void
+show_values (num_exp, from_tty)
+ char *num_exp;
+ int from_tty;
+{
+ register int i;
+ register value val;
+ static int num = 1;
+
+ if (num_exp)
+ {
+ /* "info history +" should print from the stored position.
+ "info history <exp>" should print around value number <exp>. */
+ if (num_exp[0] != '+' || num_exp[1] != '\0')
+ num = parse_and_eval_address (num_exp) - 5;
+ }
+ else
+ {
+ /* "info history" means print the last 10 values. */
+ num = value_history_count - 9;
+ }
+
+ if (num <= 0)
+ num = 1;
+
+ for (i = num; i < num + 10 && i <= value_history_count; i++)
+ {
+ val = access_value_history (i);
+ printf_filtered ("$%d = ", i);
+ value_print (val, stdout, 0, Val_pretty_default);
+ printf_filtered ("\n");
+ }
+
+ /* The next "info history +" should start after what we just printed. */
+ num += 10;
+
+ /* Hitting just return after this command should do the same thing as
+ "info history +". If num_exp is null, this is unnecessary, since
+ "info history +" is not useful after "info history". */
+ if (from_tty && num_exp)
+ {
+ num_exp[0] = '+';
+ num_exp[1] = '\0';
+ }
+}
+
+/* Internal variables. These are variables within the debugger
+ that hold values assigned by debugger commands.
+ The user refers to them with a '$' prefix
+ that does not appear in the variable names stored internally. */
+
+static struct internalvar *internalvars;
+
+/* Look up an internal variable with name NAME. NAME should not
+ normally include a dollar sign.
+
+ If the specified internal variable does not exist,
+ one is created, with a void value. */
+
+struct internalvar *
+lookup_internalvar (name)
+ char *name;
+{
+ register struct internalvar *var;
+
+ for (var = internalvars; var; var = var->next)
+ if (STREQ (var->name, name))
+ return var;
+
+ var = (struct internalvar *) xmalloc (sizeof (struct internalvar));
+ var->name = concat (name, NULL);
+ var->value = allocate_value (builtin_type_void);
+ release_value (var->value);
+ var->next = internalvars;
+ internalvars = var;
+ return var;
+}
+
+value
+value_of_internalvar (var)
+ struct internalvar *var;
+{
+ register value val;
+
+#ifdef IS_TRAPPED_INTERNALVAR
+ if (IS_TRAPPED_INTERNALVAR (var->name))
+ return VALUE_OF_TRAPPED_INTERNALVAR (var);
+#endif
+
+ val = value_copy (var->value);
+ if (VALUE_LAZY (val))
+ value_fetch_lazy (val);
+ VALUE_LVAL (val) = lval_internalvar;
+ VALUE_INTERNALVAR (val) = var;
+ return val;
+}
+
+void
+set_internalvar_component (var, offset, bitpos, bitsize, newval)
+ struct internalvar *var;
+ int offset, bitpos, bitsize;
+ value newval;
+{
+ register char *addr = VALUE_CONTENTS (var->value) + offset;
+
+#ifdef IS_TRAPPED_INTERNALVAR
+ if (IS_TRAPPED_INTERNALVAR (var->name))
+ SET_TRAPPED_INTERNALVAR (var, newval, bitpos, bitsize, offset);
+#endif
+
+ if (bitsize)
+ modify_field (addr, value_as_long (newval),
+ bitpos, bitsize);
+ else
+ memcpy (addr, VALUE_CONTENTS (newval), TYPE_LENGTH (VALUE_TYPE (newval)));
+}
+
+void
+set_internalvar (var, val)
+ struct internalvar *var;
+ value val;
+{
+#ifdef IS_TRAPPED_INTERNALVAR
+ if (IS_TRAPPED_INTERNALVAR (var->name))
+ SET_TRAPPED_INTERNALVAR (var, val, 0, 0, 0);
+#endif
+
+ free ((PTR)var->value);
+ var->value = value_copy (val);
+ /* Force the value to be fetched from the target now, to avoid problems
+ later when this internalvar is referenced and the target is gone or
+ has changed. */
+ if (VALUE_LAZY (var->value))
+ value_fetch_lazy (var->value);
+ release_value (var->value);
+}
+
+char *
+internalvar_name (var)
+ struct internalvar *var;
+{
+ return var->name;
+}
+
+/* Free all internalvars. Done when new symtabs are loaded,
+ because that makes the values invalid. */
+
+void
+clear_internalvars ()
+{
+ register struct internalvar *var;
+
+ while (internalvars)
+ {
+ var = internalvars;
+ internalvars = var->next;
+ free ((PTR)var->name);
+ free ((PTR)var->value);
+ free ((PTR)var);
+ }
+}
+
+static void
+show_convenience (ignore, from_tty)
+ char *ignore;
+ int from_tty;
+{
+ register struct internalvar *var;
+ int varseen = 0;
+
+ for (var = internalvars; var; var = var->next)
+ {
+#ifdef IS_TRAPPED_INTERNALVAR
+ if (IS_TRAPPED_INTERNALVAR (var->name))
+ continue;
+#endif
+ if (!varseen)
+ {
+ varseen = 1;
+ }
+ printf_filtered ("$%s = ", var->name);
+ value_print (var->value, stdout, 0, Val_pretty_default);
+ printf_filtered ("\n");
+ }
+ if (!varseen)
+ printf ("No debugger convenience variables now defined.\n\
+Convenience variables have names starting with \"$\";\n\
+use \"set\" as in \"set $foo = 5\" to define them.\n");
+}
+
+/* Extract a value as a C number (either long or double).
+ Knows how to convert fixed values to double, or
+ floating values to long.
+ Does not deallocate the value. */
+
+LONGEST
+value_as_long (val)
+ register value val;
+{
+ /* This coerces arrays and functions, which is necessary (e.g.
+ in disassemble_command). It also dereferences references, which
+ I suspect is the most logical thing to do. */
+ if (TYPE_CODE (VALUE_TYPE (val)) != TYPE_CODE_ENUM)
+ COERCE_ARRAY (val);
+ return unpack_long (VALUE_TYPE (val), VALUE_CONTENTS (val));
+}
+
+double
+value_as_double (val)
+ register value val;
+{
+ double foo;
+ int inv;
+
+ foo = unpack_double (VALUE_TYPE (val), VALUE_CONTENTS (val), &inv);
+ if (inv)
+ error ("Invalid floating value found in program.");
+ return foo;
+}
+/* Extract a value as a C pointer.
+ Does not deallocate the value. */
+CORE_ADDR
+value_as_pointer (val)
+ value val;
+{
+ /* Assume a CORE_ADDR can fit in a LONGEST (for now). Not sure
+ whether we want this to be true eventually. */
+#if 0
+ /* ADDR_BITS_REMOVE is wrong if we are being called for a
+ non-address (e.g. argument to "signal", "info break", etc.), or
+ for pointers to char, in which the low bits *are* significant. */
+ return ADDR_BITS_REMOVE(value_as_long (val));
+#else
+ return value_as_long (val);
+#endif
+}
+
+/* Unpack raw data (copied from debugee, target byte order) at VALADDR
+ as a long, or as a double, assuming the raw data is described
+ by type TYPE. Knows how to convert different sizes of values
+ and can convert between fixed and floating point. We don't assume
+ any alignment for the raw data. Return value is in host byte order.
+
+ If you want functions and arrays to be coerced to pointers, and
+ references to be dereferenced, call value_as_long() instead.
+
+ C++: It is assumed that the front-end has taken care of
+ all matters concerning pointers to members. A pointer
+ to member which reaches here is considered to be equivalent
+ to an INT (or some size). After all, it is only an offset. */
+
+/* FIXME: This should be rewritten as a switch statement for speed and
+ ease of comprehension. */
+
+LONGEST
+unpack_long (type, valaddr)
+ struct type *type;
+ char *valaddr;
+{
+ register enum type_code code = TYPE_CODE (type);
+ register int len = TYPE_LENGTH (type);
+ register int nosign = TYPE_UNSIGNED (type);
+
+ if (code == TYPE_CODE_ENUM || code == TYPE_CODE_BOOL)
+ code = TYPE_CODE_INT;
+ if (code == TYPE_CODE_FLT)
+ {
+ if (len == sizeof (float))
+ {
+ float retval;
+ memcpy (&retval, valaddr, sizeof (retval));
+ SWAP_TARGET_AND_HOST (&retval, sizeof (retval));
+ return retval;
+ }
+
+ if (len == sizeof (double))
+ {
+ double retval;
+ memcpy (&retval, valaddr, sizeof (retval));
+ SWAP_TARGET_AND_HOST (&retval, sizeof (retval));
+ return retval;
+ }
+ else
+ {
+ error ("Unexpected type of floating point number.");
+ }
+ }
+ else if ((code == TYPE_CODE_INT || code == TYPE_CODE_CHAR) && nosign)
+ {
+ return extract_unsigned_integer (valaddr, len);
+ }
+ else if (code == TYPE_CODE_INT || code == TYPE_CODE_CHAR)
+ {
+ return extract_signed_integer (valaddr, len);
+ }
+ /* Assume a CORE_ADDR can fit in a LONGEST (for now). Not sure
+ whether we want this to be true eventually. */
+ else if (code == TYPE_CODE_PTR || code == TYPE_CODE_REF)
+ {
+ return extract_address (valaddr, len);
+ }
+ else if (code == TYPE_CODE_MEMBER)
+ error ("not implemented: member types in unpack_long");
+
+ error ("Value not integer or pointer.");
+ return 0; /* For lint -- never reached */
+}
+
+/* Return a double value from the specified type and address.
+ INVP points to an int which is set to 0 for valid value,
+ 1 for invalid value (bad float format). In either case,
+ the returned double is OK to use. Argument is in target
+ format, result is in host format. */
+
+double
+unpack_double (type, valaddr, invp)
+ struct type *type;
+ char *valaddr;
+ int *invp;
+{
+ register enum type_code code = TYPE_CODE (type);
+ register int len = TYPE_LENGTH (type);
+ register int nosign = TYPE_UNSIGNED (type);
+
+ *invp = 0; /* Assume valid. */
+ if (code == TYPE_CODE_FLT)
+ {
+ if (INVALID_FLOAT (valaddr, len))
+ {
+ *invp = 1;
+ return 1.234567891011121314;
+ }
+
+ if (len == sizeof (float))
+ {
+ float retval;
+ memcpy (&retval, valaddr, sizeof (retval));
+ SWAP_TARGET_AND_HOST (&retval, sizeof (retval));
+ return retval;
+ }
+
+ if (len == sizeof (double))
+ {
+ double retval;
+ memcpy (&retval, valaddr, sizeof (retval));
+ SWAP_TARGET_AND_HOST (&retval, sizeof (retval));
+ return retval;
+ }
+ else
+ {
+ error ("Unexpected type of floating point number.");
+ return 0; /* Placate lint. */
+ }
+ }
+ else if (nosign) {
+ /* Unsigned -- be sure we compensate for signed LONGEST. */
+ return (unsigned LONGEST) unpack_long (type, valaddr);
+ } else {
+ /* Signed -- we are OK with unpack_long. */
+ return unpack_long (type, valaddr);
+ }
+}
+
+/* Unpack raw data (copied from debugee, target byte order) at VALADDR
+ as a CORE_ADDR, assuming the raw data is described by type TYPE.
+ We don't assume any alignment for the raw data. Return value is in
+ host byte order.
+
+ If you want functions and arrays to be coerced to pointers, and
+ references to be dereferenced, call value_as_pointer() instead.
+
+ C++: It is assumed that the front-end has taken care of
+ all matters concerning pointers to members. A pointer
+ to member which reaches here is considered to be equivalent
+ to an INT (or some size). After all, it is only an offset. */
+
+CORE_ADDR
+unpack_pointer (type, valaddr)
+ struct type *type;
+ char *valaddr;
+{
+ /* Assume a CORE_ADDR can fit in a LONGEST (for now). Not sure
+ whether we want this to be true eventually. */
+ return unpack_long (type, valaddr);
+}
+
+/* Given a value ARG1 (offset by OFFSET bytes)
+ of a struct or union type ARG_TYPE,
+ extract and return the value of one of its fields.
+ FIELDNO says which field.
+
+ For C++, must also be able to return values from static fields */
+
+value
+value_primitive_field (arg1, offset, fieldno, arg_type)
+ register value arg1;
+ int offset;
+ register int fieldno;
+ register struct type *arg_type;
+{
+ register value v;
+ register struct type *type;
+
+ check_stub_type (arg_type);
+ type = TYPE_FIELD_TYPE (arg_type, fieldno);
+
+ /* Handle packed fields */
+
+ offset += TYPE_FIELD_BITPOS (arg_type, fieldno) / 8;
+ if (TYPE_FIELD_BITSIZE (arg_type, fieldno))
+ {
+ v = value_from_longest (type,
+ unpack_field_as_long (arg_type,
+ VALUE_CONTENTS (arg1),
+ fieldno));
+ VALUE_BITPOS (v) = TYPE_FIELD_BITPOS (arg_type, fieldno) % 8;
+ VALUE_BITSIZE (v) = TYPE_FIELD_BITSIZE (arg_type, fieldno);
+ }
+ else
+ {
+ v = allocate_value (type);
+ if (VALUE_LAZY (arg1))
+ VALUE_LAZY (v) = 1;
+ else
+ memcpy (VALUE_CONTENTS_RAW (v), VALUE_CONTENTS_RAW (arg1) + offset,
+ TYPE_LENGTH (type));
+ }
+ VALUE_LVAL (v) = VALUE_LVAL (arg1);
+ if (VALUE_LVAL (arg1) == lval_internalvar)
+ VALUE_LVAL (v) = lval_internalvar_component;
+ VALUE_ADDRESS (v) = VALUE_ADDRESS (arg1);
+ VALUE_OFFSET (v) = offset + VALUE_OFFSET (arg1);
+ return v;
+}
+
+/* Given a value ARG1 of a struct or union type,
+ extract and return the value of one of its fields.
+ FIELDNO says which field.
+
+ For C++, must also be able to return values from static fields */
+
+value
+value_field (arg1, fieldno)
+ register value arg1;
+ register int fieldno;
+{
+ return value_primitive_field (arg1, 0, fieldno, VALUE_TYPE (arg1));
+}
+
+/* Return a non-virtual function as a value.
+ F is the list of member functions which contains the desired method.
+ J is an index into F which provides the desired method. */
+
+value
+value_fn_field (arg1p, f, j, type, offset)
+ value *arg1p;
+ struct fn_field *f;
+ int j;
+ struct type *type;
+ int offset;
+{
+ register value v;
+ register struct type *ftype = TYPE_FN_FIELD_TYPE (f, j);
+ struct symbol *sym;
+
+ sym = lookup_symbol (TYPE_FN_FIELD_PHYSNAME (f, j),
+ 0, VAR_NAMESPACE, 0, NULL);
+ if (! sym) error ("Internal error: could not find physical method named %s",
+ TYPE_FN_FIELD_PHYSNAME (f, j));
+
+ v = allocate_value (ftype);
+ VALUE_ADDRESS (v) = BLOCK_START (SYMBOL_BLOCK_VALUE (sym));
+ VALUE_TYPE (v) = ftype;
+
+ if (arg1p)
+ {
+ if (type != VALUE_TYPE (*arg1p))
+ *arg1p = value_ind (value_cast (lookup_pointer_type (type),
+ value_addr (*arg1p)));
+
+ /* Move the `this' pointer according to the offset.
+ VALUE_OFFSET (*arg1p) += offset;
+ */
+ }
+
+ return v;
+}
+
+/* Return a virtual function as a value.
+ ARG1 is the object which provides the virtual function
+ table pointer. *ARG1P is side-effected in calling this function.
+ F is the list of member functions which contains the desired virtual
+ function.
+ J is an index into F which provides the desired virtual function.
+
+ TYPE is the type in which F is located. */
+value
+value_virtual_fn_field (arg1p, f, j, type, offset)
+ value *arg1p;
+ struct fn_field *f;
+ int j;
+ struct type *type;
+ int offset;
+{
+ value arg1 = *arg1p;
+ /* First, get the virtual function table pointer. That comes
+ with a strange type, so cast it to type `pointer to long' (which
+ should serve just fine as a function type). Then, index into
+ the table, and convert final value to appropriate function type. */
+ value entry, vfn, vtbl;
+ value vi = value_from_longest (builtin_type_int,
+ (LONGEST) TYPE_FN_FIELD_VOFFSET (f, j));
+ struct type *fcontext = TYPE_FN_FIELD_FCONTEXT (f, j);
+ struct type *context;
+ if (fcontext == NULL)
+ /* We don't have an fcontext (e.g. the program was compiled with
+ g++ version 1). Try to get the vtbl from the TYPE_VPTR_BASETYPE.
+ This won't work right for multiple inheritance, but at least we
+ should do as well as GDB 3.x did. */
+ fcontext = TYPE_VPTR_BASETYPE (type);
+ context = lookup_pointer_type (fcontext);
+ /* Now context is a pointer to the basetype containing the vtbl. */
+ if (TYPE_TARGET_TYPE (context) != VALUE_TYPE (arg1))
+ arg1 = value_ind (value_cast (context, value_addr (arg1)));
+
+ context = VALUE_TYPE (arg1);
+ /* Now context is the basetype containing the vtbl. */
+
+ /* This type may have been defined before its virtual function table
+ was. If so, fill in the virtual function table entry for the
+ type now. */
+ if (TYPE_VPTR_FIELDNO (context) < 0)
+ fill_in_vptr_fieldno (context);
+
+ /* The virtual function table is now an array of structures
+ which have the form { int16 offset, delta; void *pfn; }. */
+ vtbl = value_ind (value_primitive_field (arg1, 0,
+ TYPE_VPTR_FIELDNO (context),
+ TYPE_VPTR_BASETYPE (context)));
+
+ /* Index into the virtual function table. This is hard-coded because
+ looking up a field is not cheap, and it may be important to save
+ time, e.g. if the user has set a conditional breakpoint calling
+ a virtual function. */
+ entry = value_subscript (vtbl, vi);
+
+ /* Move the `this' pointer according to the virtual function table. */
+ VALUE_OFFSET (arg1) += value_as_long (value_field (entry, 0))/* + offset*/;
+
+ if (! VALUE_LAZY (arg1))
+ {
+ VALUE_LAZY (arg1) = 1;
+ value_fetch_lazy (arg1);
+ }
+
+ vfn = value_field (entry, 2);
+ /* Reinstantiate the function pointer with the correct type. */
+ VALUE_TYPE (vfn) = lookup_pointer_type (TYPE_FN_FIELD_TYPE (f, j));
+
+ *arg1p = arg1;
+ return vfn;
+}
+
+/* ARG is a pointer to an object we know to be at least
+ a DTYPE. BTYPE is the most derived basetype that has
+ already been searched (and need not be searched again).
+ After looking at the vtables between BTYPE and DTYPE,
+ return the most derived type we find. The caller must
+ be satisfied when the return value == DTYPE.
+
+ FIXME-tiemann: should work with dossier entries as well. */
+
+static value
+value_headof (in_arg, btype, dtype)
+ value in_arg;
+ struct type *btype, *dtype;
+{
+ /* First collect the vtables we must look at for this object. */
+ /* FIXME-tiemann: right now, just look at top-most vtable. */
+ value arg, vtbl, entry, best_entry = 0;
+ int i, nelems;
+ int offset, best_offset = 0;
+ struct symbol *sym;
+ CORE_ADDR pc_for_sym;
+ char *demangled_name;
+ struct minimal_symbol *msymbol;
+
+ btype = TYPE_VPTR_BASETYPE (dtype);
+ check_stub_type (btype);
+ arg = in_arg;
+ if (btype != dtype)
+ arg = value_cast (lookup_pointer_type (btype), arg);
+ vtbl = value_ind (value_field (value_ind (arg), TYPE_VPTR_FIELDNO (btype)));
+
+ /* Check that VTBL looks like it points to a virtual function table. */
+ msymbol = lookup_minimal_symbol_by_pc (VALUE_ADDRESS (vtbl));
+ if (msymbol == NULL
+ || !VTBL_PREFIX_P (demangled_name = SYMBOL_NAME (msymbol)))
+ {
+ /* If we expected to find a vtable, but did not, let the user
+ know that we aren't happy, but don't throw an error.
+ FIXME: there has to be a better way to do this. */
+ struct type *error_type = (struct type *)xmalloc (sizeof (struct type));
+ memcpy (error_type, VALUE_TYPE (in_arg), sizeof (struct type));
+ TYPE_NAME (error_type) = savestring ("suspicious *", sizeof ("suspicious *"));
+ VALUE_TYPE (in_arg) = error_type;
+ return in_arg;
+ }
+
+ /* Now search through the virtual function table. */
+ entry = value_ind (vtbl);
+ nelems = longest_to_int (value_as_long (value_field (entry, 2)));
+ for (i = 1; i <= nelems; i++)
+ {
+ entry = value_subscript (vtbl, value_from_longest (builtin_type_int,
+ (LONGEST) i));
+ offset = longest_to_int (value_as_long (value_field (entry, 0)));
+ /* If we use '<=' we can handle single inheritance
+ * where all offsets are zero - just use the first entry found. */
+ if (offset <= best_offset)
+ {
+ best_offset = offset;
+ best_entry = entry;
+ }
+ }
+ /* Move the pointer according to BEST_ENTRY's offset, and figure
+ out what type we should return as the new pointer. */
+ if (best_entry == 0)
+ {
+ /* An alternative method (which should no longer be necessary).
+ * But we leave it in for future use, when we will hopefully
+ * have optimizes the vtable to use thunks instead of offsets. */
+ /* Use the name of vtable itself to extract a base type. */
+ demangled_name += 4; /* Skip _vt$ prefix. */
+ }
+ else
+ {
+ pc_for_sym = value_as_pointer (value_field (best_entry, 2));
+ sym = find_pc_function (pc_for_sym);
+ demangled_name = cplus_demangle (SYMBOL_NAME (sym), DMGL_ANSI);
+ *(strchr (demangled_name, ':')) = '\0';
+ }
+ sym = lookup_symbol (demangled_name, 0, VAR_NAMESPACE, 0, 0);
+ if (sym == NULL)
+ error ("could not find type declaration for `%s'", demangled_name);
+ if (best_entry)
+ {
+ free (demangled_name);
+ arg = value_add (value_cast (builtin_type_int, arg),
+ value_field (best_entry, 0));
+ }
+ else arg = in_arg;
+ VALUE_TYPE (arg) = lookup_pointer_type (SYMBOL_TYPE (sym));
+ return arg;
+}
+
+/* ARG is a pointer object of type TYPE. If TYPE has virtual
+ function tables, probe ARG's tables (including the vtables
+ of its baseclasses) to figure out the most derived type that ARG
+ could actually be a pointer to. */
+
+value
+value_from_vtable_info (arg, type)
+ value arg;
+ struct type *type;
+{
+ /* Take care of preliminaries. */
+ if (TYPE_VPTR_FIELDNO (type) < 0)
+ fill_in_vptr_fieldno (type);
+ if (TYPE_VPTR_FIELDNO (type) < 0 || VALUE_REPEATED (arg))
+ return 0;
+
+ return value_headof (arg, 0, type);
+}
+
+/* Return true if the INDEXth field of TYPE is a virtual baseclass
+ pointer which is for the base class whose type is BASECLASS. */
+
+static int
+vb_match (type, index, basetype)
+ struct type *type;
+ int index;
+ struct type *basetype;
+{
+ struct type *fieldtype;
+ char *name = TYPE_FIELD_NAME (type, index);
+ char *field_class_name = NULL;
+
+ if (*name != '_')
+ return 0;
+ /* gcc 2.4 uses _vb$. */
+ if (name[1] == 'v' && name[2] == 'b' && name[3] == CPLUS_MARKER)
+ field_class_name = name + 4;
+ /* gcc 2.5 will use __vb_. */
+ if (name[1] == '_' && name[2] == 'v' && name[3] == 'b' && name[4] == '_')
+ field_class_name = name + 5;
+
+ if (field_class_name == NULL)
+ /* This field is not a virtual base class pointer. */
+ return 0;
+
+ /* It's a virtual baseclass pointer, now we just need to find out whether
+ it is for this baseclass. */
+ fieldtype = TYPE_FIELD_TYPE (type, index);
+ if (fieldtype == NULL
+ || TYPE_CODE (fieldtype) != TYPE_CODE_PTR)
+ /* "Can't happen". */
+ return 0;
+
+ /* What we check for is that either the types are equal (needed for
+ nameless types) or have the same name. This is ugly, and a more
+ elegant solution should be devised (which would probably just push
+ the ugliness into symbol reading unless we change the stabs format). */
+ if (TYPE_TARGET_TYPE (fieldtype) == basetype)
+ return 1;
+
+ if (TYPE_NAME (basetype) != NULL
+ && TYPE_NAME (TYPE_TARGET_TYPE (fieldtype)) != NULL
+ && STREQ (TYPE_NAME (basetype),
+ TYPE_NAME (TYPE_TARGET_TYPE (fieldtype))))
+ return 1;
+ return 0;
+}
+
+/* Compute the offset of the baseclass which is
+ the INDEXth baseclass of class TYPE, for a value ARG,
+ wih extra offset of OFFSET.
+ The result is the offste of the baseclass value relative
+ to (the address of)(ARG) + OFFSET.
+
+ -1 is returned on error. */
+
+int
+baseclass_offset (type, index, arg, offset)
+ struct type *type;
+ int index;
+ value arg;
+ int offset;
+{
+ struct type *basetype = TYPE_BASECLASS (type, index);
+
+ if (BASETYPE_VIA_VIRTUAL (type, index))
+ {
+ /* Must hunt for the pointer to this virtual baseclass. */
+ register int i, len = TYPE_NFIELDS (type);
+ register int n_baseclasses = TYPE_N_BASECLASSES (type);
+
+ /* First look for the virtual baseclass pointer
+ in the fields. */
+ for (i = n_baseclasses; i < len; i++)
+ {
+ if (vb_match (type, i, basetype))
+ {
+ CORE_ADDR addr
+ = unpack_pointer (TYPE_FIELD_TYPE (type, i),
+ VALUE_CONTENTS (arg) + VALUE_OFFSET (arg)
+ + offset
+ + (TYPE_FIELD_BITPOS (type, i) / 8));
+
+ if (VALUE_LVAL (arg) != lval_memory)
+ return -1;
+
+ return addr -
+ (LONGEST) (VALUE_ADDRESS (arg) + VALUE_OFFSET (arg) + offset);
+ }
+ }
+ /* Not in the fields, so try looking through the baseclasses. */
+ for (i = index+1; i < n_baseclasses; i++)
+ {
+ int boffset =
+ baseclass_offset (type, i, arg, offset);
+ if (boffset)
+ return boffset;
+ }
+ /* Not found. */
+ return -1;
+ }
+
+ /* Baseclass is easily computed. */
+ return TYPE_BASECLASS_BITPOS (type, index) / 8;
+}
+
+/* Compute the address of the baseclass which is
+ the INDEXth baseclass of class TYPE. The TYPE base
+ of the object is at VALADDR.
+
+ If ERRP is non-NULL, set *ERRP to be the errno code of any error,
+ or 0 if no error. In that case the return value is not the address
+ of the baseclasss, but the address which could not be read
+ successfully. */
+
+/* FIXME Fix remaining uses of baseclass_addr to use baseclass_offset */
+
+char *
+baseclass_addr (type, index, valaddr, valuep, errp)
+ struct type *type;
+ int index;
+ char *valaddr;
+ value *valuep;
+ int *errp;
+{
+ struct type *basetype = TYPE_BASECLASS (type, index);
+
+ if (errp)
+ *errp = 0;
+
+ if (BASETYPE_VIA_VIRTUAL (type, index))
+ {
+ /* Must hunt for the pointer to this virtual baseclass. */
+ register int i, len = TYPE_NFIELDS (type);
+ register int n_baseclasses = TYPE_N_BASECLASSES (type);
+
+ /* First look for the virtual baseclass pointer
+ in the fields. */
+ for (i = n_baseclasses; i < len; i++)
+ {
+ if (vb_match (type, i, basetype))
+ {
+ value val = allocate_value (basetype);
+ CORE_ADDR addr;
+ int status;
+
+ addr
+ = unpack_pointer (TYPE_FIELD_TYPE (type, i),
+ valaddr + (TYPE_FIELD_BITPOS (type, i) / 8));
+
+ status = target_read_memory (addr,
+ VALUE_CONTENTS_RAW (val),
+ TYPE_LENGTH (basetype));
+ VALUE_LVAL (val) = lval_memory;
+ VALUE_ADDRESS (val) = addr;
+
+ if (status != 0)
+ {
+ if (valuep)
+ *valuep = NULL;
+ release_value (val);
+ value_free (val);
+ if (errp)
+ *errp = status;
+ return (char *)addr;
+ }
+ else
+ {
+ if (valuep)
+ *valuep = val;
+ return (char *) VALUE_CONTENTS (val);
+ }
+ }
+ }
+ /* Not in the fields, so try looking through the baseclasses. */
+ for (i = index+1; i < n_baseclasses; i++)
+ {
+ char *baddr;
+
+ baddr = baseclass_addr (type, i, valaddr, valuep, errp);
+ if (baddr)
+ return baddr;
+ }
+ /* Not found. */
+ if (valuep)
+ *valuep = 0;
+ return 0;
+ }
+
+ /* Baseclass is easily computed. */
+ if (valuep)
+ *valuep = 0;
+ return valaddr + TYPE_BASECLASS_BITPOS (type, index) / 8;
+}
+
+/* Unpack a field FIELDNO of the specified TYPE, from the anonymous object at
+ VALADDR.
+
+ Extracting bits depends on endianness of the machine. Compute the
+ number of least significant bits to discard. For big endian machines,
+ we compute the total number of bits in the anonymous object, subtract
+ off the bit count from the MSB of the object to the MSB of the
+ bitfield, then the size of the bitfield, which leaves the LSB discard
+ count. For little endian machines, the discard count is simply the
+ number of bits from the LSB of the anonymous object to the LSB of the
+ bitfield.
+
+ If the field is signed, we also do sign extension. */
+
+LONGEST
+unpack_field_as_long (type, valaddr, fieldno)
+ struct type *type;
+ char *valaddr;
+ int fieldno;
+{
+ unsigned LONGEST val;
+ unsigned LONGEST valmask;
+ int bitpos = TYPE_FIELD_BITPOS (type, fieldno);
+ int bitsize = TYPE_FIELD_BITSIZE (type, fieldno);
+ int lsbcount;
+
+ val = extract_unsigned_integer (valaddr + bitpos / 8, sizeof (val));
+
+ /* Extract bits. See comment above. */
+
+#if BITS_BIG_ENDIAN
+ lsbcount = (sizeof val * 8 - bitpos % 8 - bitsize);
+#else
+ lsbcount = (bitpos % 8);
+#endif
+ val >>= lsbcount;
+
+ /* If the field does not entirely fill a LONGEST, then zero the sign bits.
+ If the field is signed, and is negative, then sign extend. */
+
+ if ((bitsize > 0) && (bitsize < 8 * sizeof (val)))
+ {
+ valmask = (((unsigned LONGEST) 1) << bitsize) - 1;
+ val &= valmask;
+ if (!TYPE_UNSIGNED (TYPE_FIELD_TYPE (type, fieldno)))
+ {
+ if (val & (valmask ^ (valmask >> 1)))
+ {
+ val |= ~valmask;
+ }
+ }
+ }
+ return (val);
+}
+
+/* Modify the value of a bitfield. ADDR points to a block of memory in
+ target byte order; the bitfield starts in the byte pointed to. FIELDVAL
+ is the desired value of the field, in host byte order. BITPOS and BITSIZE
+ indicate which bits (in target bit order) comprise the bitfield. */
+
+void
+modify_field (addr, fieldval, bitpos, bitsize)
+ char *addr;
+ LONGEST fieldval;
+ int bitpos, bitsize;
+{
+ LONGEST oword;
+
+ /* Reject values too big to fit in the field in question,
+ otherwise adjoining fields may be corrupted. */
+ if (bitsize < (8 * sizeof (fieldval))
+ && 0 != (fieldval & ~((1<<bitsize)-1)))
+ {
+ /* FIXME: would like to include fieldval in the message, but
+ we don't have a sprintf_longest. */
+ error ("Value does not fit in %d bits.", bitsize);
+ }
+
+ oword = extract_signed_integer (addr, sizeof oword);
+
+ /* Shifting for bit field depends on endianness of the target machine. */
+#if BITS_BIG_ENDIAN
+ bitpos = sizeof (oword) * 8 - bitpos - bitsize;
+#endif
+
+ /* Mask out old value, while avoiding shifts >= size of oword */
+ if (bitsize < 8 * sizeof (oword))
+ oword &= ~(((((unsigned LONGEST)1) << bitsize) - 1) << bitpos);
+ else
+ oword &= ~((~(unsigned LONGEST)0) << bitpos);
+ oword |= fieldval << bitpos;
+
+ store_signed_integer (addr, sizeof oword, oword);
+}
+
+/* Convert C numbers into newly allocated values */
+
+value
+value_from_longest (type, num)
+ struct type *type;
+ register LONGEST num;
+{
+ register value val = allocate_value (type);
+ register enum type_code code = TYPE_CODE (type);
+ register int len = TYPE_LENGTH (type);
+
+ switch (code)
+ {
+ case TYPE_CODE_INT:
+ case TYPE_CODE_CHAR:
+ case TYPE_CODE_ENUM:
+ case TYPE_CODE_BOOL:
+ store_signed_integer (VALUE_CONTENTS_RAW (val), len, num);
+ break;
+
+ case TYPE_CODE_REF:
+ case TYPE_CODE_PTR:
+ /* This assumes that all pointers of a given length
+ have the same form. */
+ store_address (VALUE_CONTENTS_RAW (val), len, (CORE_ADDR) num);
+ break;
+
+ default:
+ error ("Unexpected type encountered for integer constant.");
+ }
+ return val;
+}
+
+value
+value_from_double (type, num)
+ struct type *type;
+ double num;
+{
+ register value val = allocate_value (type);
+ register enum type_code code = TYPE_CODE (type);
+ register int len = TYPE_LENGTH (type);
+
+ if (code == TYPE_CODE_FLT)
+ {
+ if (len == sizeof (float))
+ * (float *) VALUE_CONTENTS_RAW (val) = num;
+ else if (len == sizeof (double))
+ * (double *) VALUE_CONTENTS_RAW (val) = num;
+ else
+ error ("Floating type encountered with unexpected data length.");
+ }
+ else
+ error ("Unexpected type encountered for floating constant.");
+
+ /* num was in host byte order. So now put the value's contents
+ into target byte order. */
+ SWAP_TARGET_AND_HOST (VALUE_CONTENTS_RAW (val), len);
+
+ return val;
+}
+
+/* Deal with the value that is "about to be returned". */
+
+/* Return the value that a function returning now
+ would be returning to its caller, assuming its type is VALTYPE.
+ RETBUF is where we look for what ought to be the contents
+ of the registers (in raw form). This is because it is often
+ desirable to restore old values to those registers
+ after saving the contents of interest, and then call
+ this function using the saved values.
+ struct_return is non-zero when the function in question is
+ using the structure return conventions on the machine in question;
+ 0 when it is using the value returning conventions (this often
+ means returning pointer to where structure is vs. returning value). */
+
+value
+value_being_returned (valtype, retbuf, struct_return)
+ register struct type *valtype;
+ char retbuf[REGISTER_BYTES];
+ int struct_return;
+ /*ARGSUSED*/
+{
+ register value val;
+ CORE_ADDR addr;
+
+#if defined (EXTRACT_STRUCT_VALUE_ADDRESS)
+ /* If this is not defined, just use EXTRACT_RETURN_VALUE instead. */
+ if (struct_return) {
+ addr = EXTRACT_STRUCT_VALUE_ADDRESS (retbuf);
+ if (!addr)
+ error ("Function return value unknown");
+ return value_at (valtype, addr);
+ }
+#endif
+
+ val = allocate_value (valtype);
+ EXTRACT_RETURN_VALUE (valtype, retbuf, VALUE_CONTENTS_RAW (val));
+
+ return val;
+}
+
+/* Should we use EXTRACT_STRUCT_VALUE_ADDRESS instead of
+ EXTRACT_RETURN_VALUE? GCC_P is true if compiled with gcc
+ and TYPE is the type (which is known to be struct, union or array).
+
+ On most machines, the struct convention is used unless we are
+ using gcc and the type is of a special size. */
+/* As of about 31 Mar 93, GCC was changed to be compatible with the
+ native compiler. GCC 2.3.3 was the last release that did it the
+ old way. Since gcc2_compiled was not changed, we have no
+ way to correctly win in all cases, so we just do the right thing
+ for gcc1 and for gcc2 after this change. Thus it loses for gcc
+ 2.0-2.3.3. This is somewhat unfortunate, but changing gcc2_compiled
+ would cause more chaos than dealing with some struct returns being
+ handled wrong. */
+#if !defined (USE_STRUCT_CONVENTION)
+#define USE_STRUCT_CONVENTION(gcc_p, type)\
+ (!((gcc_p == 1) && (TYPE_LENGTH (value_type) == 1 \
+ || TYPE_LENGTH (value_type) == 2 \
+ || TYPE_LENGTH (value_type) == 4 \
+ || TYPE_LENGTH (value_type) == 8 \
+ ) \
+ ))
+#endif
+
+/* Return true if the function specified is using the structure returning
+ convention on this machine to return arguments, or 0 if it is using
+ the value returning convention. FUNCTION is the value representing
+ the function, FUNCADDR is the address of the function, and VALUE_TYPE
+ is the type returned by the function. GCC_P is nonzero if compiled
+ with GCC. */
+
+int
+using_struct_return (function, funcaddr, value_type, gcc_p)
+ value function;
+ CORE_ADDR funcaddr;
+ struct type *value_type;
+ int gcc_p;
+ /*ARGSUSED*/
+{
+ register enum type_code code = TYPE_CODE (value_type);
+
+ if (code == TYPE_CODE_ERROR)
+ error ("Function return type unknown.");
+
+ if (code == TYPE_CODE_STRUCT ||
+ code == TYPE_CODE_UNION ||
+ code == TYPE_CODE_ARRAY)
+ return USE_STRUCT_CONVENTION (gcc_p, value_type);
+
+ return 0;
+}
+
+/* Store VAL so it will be returned if a function returns now.
+ Does not verify that VAL's type matches what the current
+ function wants to return. */
+
+void
+set_return_value (val)
+ value val;
+{
+ register enum type_code code = TYPE_CODE (VALUE_TYPE (val));
+ double dbuf;
+ LONGEST lbuf;
+
+ if (code == TYPE_CODE_ERROR)
+ error ("Function return type unknown.");
+
+ if ( code == TYPE_CODE_STRUCT
+ || code == TYPE_CODE_UNION) /* FIXME, implement struct return. */
+ error ("GDB does not support specifying a struct or union return value.");
+
+ /* FIXME, this is bogus. We don't know what the return conventions
+ are, or how values should be promoted.... */
+ if (code == TYPE_CODE_FLT)
+ {
+ dbuf = value_as_double (val);
+
+ STORE_RETURN_VALUE (VALUE_TYPE (val), (char *)&dbuf);
+ }
+ else
+ {
+ lbuf = value_as_long (val);
+ STORE_RETURN_VALUE (VALUE_TYPE (val), (char *)&lbuf);
+ }
+}
+
+void
+_initialize_values ()
+{
+ add_cmd ("convenience", no_class, show_convenience,
+ "Debugger convenience (\"$foo\") variables.\n\
+These variables are created when you assign them values;\n\
+thus, \"print $foo=1\" gives \"$foo\" the value 1. Values may be any type.\n\n\
+A few convenience variables are given values automatically:\n\
+\"$_\"holds the last address examined with \"x\" or \"info lines\",\n\
+\"$__\" holds the contents of the last address examined with \"x\".",
+ &showlist);
+
+ add_cmd ("values", no_class, show_values,
+ "Elements of value history around item number IDX (or last ten).",
+ &showlist);
+}
diff --git a/gnu/usr.bin/gdb/gdb/version.c b/gnu/usr.bin/gdb/gdb/version.c
new file mode 100644
index 0000000..d32e958
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/version.c
@@ -0,0 +1,3 @@
+char *version = "4.11";
+char *host_canonical = "i386-unknown-freebsd";
+char *target_canonical = "i386-unknown-freebsd";
diff --git a/gnu/usr.bin/gdb/gdb/wait.h b/gnu/usr.bin/gdb/gdb/wait.h
new file mode 100644
index 0000000..a72943c
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/wait.h
@@ -0,0 +1,38 @@
+/* Define how to access the int that the wait system call stores.
+ This has been compatible in all Unix systems since time immemorial,
+ but various well-meaning people have defined various different
+ words for the same old bits in the same old int (sometimes claimed
+ to be a struct). We just know it's an int and we use these macros
+ to access the bits. */
+
+/* The following macros are defined equivalently to their definitions
+ in POSIX.1. We fail to define WNOHANG and WUNTRACED, which POSIX.1
+ <sys/wait.h> defines, since our code does not use waitpid(). We
+ also fail to declare wait() and waitpid(). */
+
+#define WIFEXITED(w) (((w)&0377) == 0)
+#define WIFSIGNALED(w) (((w)&0377) != 0177 && ((w)&~0377) == 0)
+#ifdef IBM6000
+
+/* Unfortunately, the above comment (about being compatible in all Unix
+ systems) is not quite correct for AIX, sigh. And AIX 3.2 can generate
+ status words like 0x57c (sigtrap received after load), and gdb would
+ choke on it. */
+
+#define WIFSTOPPED(w) ((w)&0x40)
+
+#else
+#define WIFSTOPPED(w) (((w)&0377) == 0177)
+#endif
+
+#define WEXITSTATUS(w) (((w) >> 8) & 0377) /* same as WRETCODE */
+#define WTERMSIG(w) ((w) & 0177)
+#define WSTOPSIG WEXITSTATUS
+
+/* These are not defined in POSIX, but are used by our programs. */
+
+#define WAITTYPE int
+
+#define WCOREDUMP(w) (((w)&0200) != 0)
+#define WSETEXIT(w,status) ((w) = (0 | ((status) << 8)))
+#define WSETSTOP(w,sig) ((w) = (0177 | ((sig) << 8)))
diff --git a/gnu/usr.bin/gdb/gdb/xm.h b/gnu/usr.bin/gdb/gdb/xm.h
new file mode 100644
index 0000000..8d28df0
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/xm.h
@@ -0,0 +1,31 @@
+/* Host-dependent definitions for Intel 386 running BSD Unix, for GDB.
+ Copyright 1986, 1987, 1989, 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define HOST_BYTE_ORDER LITTLE_ENDIAN
+
+#include <machine/limits.h> /* for INT_MIN, to avoid "INT_MIN
+ redefined" warnings from defs.h */
+
+/* psignal() is in <signal.h>. */
+
+#define PSIGNAL_IN_SIGNAL_H
+
+/* Get rid of any system-imposed stack limit if possible. */
+
+#define SET_STACK_LIMIT_HUGE
diff --git a/gnu/usr.bin/gdb/gdb/y.tab.h b/gnu/usr.bin/gdb/gdb/y.tab.h
new file mode 100644
index 0000000..62e0883
--- /dev/null
+++ b/gnu/usr.bin/gdb/gdb/y.tab.h
@@ -0,0 +1,65 @@
+#define INT 257
+#define HEX 258
+#define ERROR 259
+#define UINT 260
+#define M2_TRUE 261
+#define M2_FALSE 262
+#define CHAR 263
+#define FLOAT 264
+#define STRING 265
+#define NAME 266
+#define BLOCKNAME 267
+#define IDENT 268
+#define VARNAME 269
+#define TYPENAME 270
+#define SIZE 271
+#define CAP 272
+#define ORD 273
+#define HIGH 274
+#define ABS 275
+#define MIN_FUNC 276
+#define MAX_FUNC 277
+#define FLOAT_FUNC 278
+#define VAL 279
+#define CHR 280
+#define ODD 281
+#define TRUNC 282
+#define INC 283
+#define DEC 284
+#define INCL 285
+#define EXCL 286
+#define COLONCOLON 287
+#define LAST 288
+#define REGNAME 289
+#define INTERNAL_VAR 290
+#define ABOVE_COMMA 291
+#define ASSIGN 292
+#define LEQ 293
+#define GEQ 294
+#define NOTEQUAL 295
+#define IN 296
+#define OROR 297
+#define LOGICAL_AND 298
+#define DIV 299
+#define MOD 300
+#define UNARY 301
+#define DOT 302
+#define NOT 303
+#define QID 304
+typedef union
+ {
+ LONGEST lval;
+ unsigned LONGEST ulval;
+ double dval;
+ struct symbol *sym;
+ struct type *tval;
+ struct stoken sval;
+ int voidval;
+ struct block *bval;
+ enum exp_opcode opcode;
+ struct internalvar *ivar;
+
+ struct type **tvec;
+ int *ivec;
+ } YYSTYPE;
+extern YYSTYPE m2_lval;
diff --git a/gnu/usr.bin/gdb/getpagesize.h b/gnu/usr.bin/gdb/getpagesize.h
new file mode 100644
index 0000000..32adae6
--- /dev/null
+++ b/gnu/usr.bin/gdb/getpagesize.h
@@ -0,0 +1,25 @@
+#ifdef BSD
+#ifndef BSD4_1
+#define HAVE_GETPAGESIZE
+#endif
+#endif
+
+#ifndef HAVE_GETPAGESIZE
+
+#include <sys/param.h>
+
+#ifdef EXEC_PAGESIZE
+#define getpagesize() EXEC_PAGESIZE
+#else
+#ifdef NBPG
+#define getpagesize() NBPG * CLSIZE
+#ifndef CLSIZE
+#define CLSIZE 1
+#endif /* no CLSIZE */
+#else /* no NBPG */
+#define getpagesize() NBPC
+#endif /* no NBPG */
+#endif /* no EXEC_PAGESIZE */
+
+#endif /* not HAVE_GETPAGESIZE */
+
diff --git a/gnu/usr.bin/gdb/infcmd.c b/gnu/usr.bin/gdb/infcmd.c
new file mode 100644
index 0000000..378784f
--- /dev/null
+++ b/gnu/usr.bin/gdb/infcmd.c
@@ -0,0 +1,1204 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)infcmd.c 6.4 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Memory-access and commands for inferior process, for GDB.
+ Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "frame.h"
+#include "inferior.h"
+#include "environ.h"
+#include "value.h"
+
+#include <signal.h>
+#include <sys/param.h>
+
+extern char *sys_siglist[];
+
+#define ERROR_NO_INFERIOR \
+ if (inferior_pid == 0) error ("The program is not being run.");
+
+/* String containing arguments to give to the program,
+ with a space added at the front. Just a space means no args. */
+
+static char *inferior_args;
+
+/* File name for default use for standard in/out in the inferior. */
+
+char *inferior_io_terminal;
+
+/* Pid of our debugged inferior, or 0 if no inferior now. */
+
+int inferior_pid;
+
+/* Last signal that the inferior received (why it stopped). */
+
+int stop_signal;
+
+/* Address at which inferior stopped. */
+
+CORE_ADDR stop_pc;
+
+/* Stack frame when program stopped. */
+
+FRAME_ADDR stop_frame_address;
+
+/* Number of breakpoint it stopped at, or 0 if none. */
+
+int stop_breakpoint;
+
+/* Nonzero if stopped due to a step command. */
+
+int stop_step;
+
+/* Nonzero if stopped due to completion of a stack dummy routine. */
+
+int stop_stack_dummy;
+
+/* Nonzero if stopped due to a random (unexpected) signal in inferior
+ process. */
+
+int stopped_by_random_signal;
+
+/* Range to single step within.
+ If this is nonzero, respond to a single-step signal
+ by continuing to step if the pc is in this range. */
+
+CORE_ADDR step_range_start; /* Inclusive */
+CORE_ADDR step_range_end; /* Exclusive */
+
+/* Stack frame address as of when stepping command was issued.
+ This is how we know when we step into a subroutine call,
+ and how to set the frame for the breakpoint used to step out. */
+
+FRAME_ADDR step_frame_address;
+
+/* 1 means step over all subroutine calls.
+ -1 means step over calls to undebuggable functions. */
+
+int step_over_calls;
+
+/* If stepping, nonzero means step count is > 1
+ so don't print frame next time inferior stops
+ if it stops due to stepping. */
+
+int step_multi;
+
+/* Environment to use for running inferior,
+ in format described in environ.h. */
+
+struct environ *inferior_environ;
+
+CORE_ADDR read_pc ();
+struct command_line *get_breakpoint_commands ();
+void breakpoint_clear_ignore_counts ();
+
+
+int
+have_inferior_p ()
+{
+ return inferior_pid != 0;
+}
+
+static void
+set_args_command (args)
+ char *args;
+{
+ free (inferior_args);
+ if (!args) args = "";
+ inferior_args = concat (" ", args, "");
+}
+
+void
+tty_command (file, from_tty)
+ char *file;
+ int from_tty;
+{
+ if (file == 0)
+ error_no_arg ("terminal name for running target process");
+
+ inferior_io_terminal = savestring (file, strlen (file));
+}
+
+static void
+run_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ extern char **environ;
+ register int i;
+ char *exec_file;
+ char *allargs;
+
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+ extern int errno;
+
+ dont_repeat ();
+
+ if (inferior_pid)
+ {
+ extern int inhibit_confirm;
+ if (!(inhibit_confirm ||
+ query ("The program being debugged has been started already.\n\
+Start it from the beginning? ")))
+ error ("Program not restarted.");
+ kill_inferior ();
+ }
+
+#if 0
+ /* On the other hand, some users want to do
+ break open
+ ignore 1 40
+ run
+ So it's not clear what is best. */
+
+ /* It is confusing to the user for ignore counts to stick around
+ from previous runs of the inferior. So clear them. */
+ breakpoint_clear_ignore_counts ();
+#endif
+
+ exec_file = (char *) get_exec_file (1);
+
+ if (remote_debugging)
+ {
+ if (from_tty)
+ {
+ printf ("Starting program: %s\n", exec_file);
+ fflush (stdout);
+ }
+ }
+ else
+ {
+ if (args)
+ set_args_command (args);
+
+ if (from_tty)
+ {
+ printf ("Starting program: %s%s\n",
+ exec_file, inferior_args);
+ fflush (stdout);
+ }
+
+ allargs = concat ("exec ", exec_file, inferior_args);
+ inferior_pid = create_inferior (allargs, environ_vector (inferior_environ));
+ }
+
+ clear_proceed_status ();
+
+ start_inferior ();
+}
+
+void
+cont_command (proc_count_exp, from_tty)
+ char *proc_count_exp;
+ int from_tty;
+{
+ ERROR_NO_INFERIOR;
+
+ clear_proceed_status ();
+
+ /* If have argument, set proceed count of breakpoint we stopped at. */
+
+ if (stop_breakpoint > 0 && proc_count_exp)
+ {
+ set_ignore_count (stop_breakpoint,
+ parse_and_eval_address (proc_count_exp) - 1,
+ from_tty);
+ if (from_tty)
+ printf (" ");
+ }
+
+ if (from_tty)
+ printf ("Continuing.\n");
+
+ proceed (-1, -1, 0);
+}
+
+/* Step until outside of current statement. */
+static void step_1 ();
+
+static void
+step_command (count_string)
+{
+ step_1 (0, 0, count_string);
+}
+
+/* Likewise, but skip over subroutine calls as if single instructions. */
+
+static void
+next_command (count_string)
+{
+ step_1 (1, 0, count_string);
+}
+
+/* Likewise, but step only one instruction. */
+
+static void
+stepi_command (count_string)
+{
+ step_1 (0, 1, count_string);
+}
+
+static void
+nexti_command (count_string)
+{
+ step_1 (1, 1, count_string);
+}
+
+static void
+step_1 (skip_subroutines, single_inst, count_string)
+ int skip_subroutines;
+ int single_inst;
+ char *count_string;
+{
+ register int count = 1;
+
+ ERROR_NO_INFERIOR;
+ count = count_string ? parse_and_eval_address (count_string) : 1;
+
+ for (; count > 0; count--)
+ {
+ clear_proceed_status ();
+
+ step_frame_address = FRAME_FP (get_current_frame ());
+
+ if (! single_inst)
+ {
+ find_pc_line_pc_range (stop_pc, &step_range_start, &step_range_end);
+ if (step_range_end == 0)
+ {
+ int misc;
+
+ misc = find_pc_misc_function (stop_pc);
+ terminal_ours ();
+ printf ("Current function has no line number information.\n");
+ fflush (stdout);
+
+ /* No info or after _etext ("Can't happen") */
+ if (misc == -1 || misc == misc_function_count - 1)
+ error ("No data available on pc function.");
+
+ printf ("Single stepping until function exit.\n");
+ fflush (stdout);
+
+ step_range_start = misc_function_vector[misc].address;
+ step_range_end = misc_function_vector[misc + 1].address;
+ }
+ }
+ else
+ {
+ /* Say we are stepping, but stop after one insn whatever it does.
+ Don't step through subroutine calls even to undebuggable
+ functions. */
+ step_range_start = step_range_end = 1;
+ if (!skip_subroutines)
+ step_over_calls = 0;
+ }
+
+ if (skip_subroutines)
+ step_over_calls = 1;
+
+ step_multi = (count > 1);
+ proceed (-1, -1, 1);
+ if (! stop_step)
+ break;
+ }
+}
+
+/* Continue program at specified address. */
+
+static void
+jump_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ register CORE_ADDR addr;
+ struct symtabs_and_lines sals;
+ struct symtab_and_line sal;
+
+ ERROR_NO_INFERIOR;
+
+ if (!arg)
+ error_no_arg ("starting address");
+
+ sals = decode_line_spec_1 (arg, 1);
+ if (sals.nelts != 1)
+ {
+ error ("Unreasonable jump request");
+ }
+
+ sal = sals.sals[0];
+ free (sals.sals);
+
+ if (sal.symtab == 0 && sal.pc == 0)
+ error ("No source file has been specified.");
+
+ if (sal.pc == 0)
+ sal.pc = find_line_pc (sal.symtab, sal.line);
+
+ {
+ struct symbol *fn = get_frame_function (get_current_frame ());
+ struct symbol *sfn = find_pc_function (sal.pc);
+ if (fn != 0 && sfn != fn
+ && ! query ("Line %d is not in `%s'. Jump anyway? ",
+ sal.line, SYMBOL_NAME (fn)))
+ error ("Not confirmed.");
+ }
+
+ if (sal.pc == 0)
+ error ("No line %d in file \"%s\".", sal.line, sal.symtab->filename);
+
+ addr = sal.pc;
+
+ clear_proceed_status ();
+
+ if (from_tty)
+ printf ("Continuing at 0x%x.\n", addr);
+
+ proceed (addr, 0, 0);
+}
+
+/* Continue program giving it specified signal. */
+
+static void
+signal_command (signum_exp, from_tty)
+ char *signum_exp;
+ int from_tty;
+{
+ register int signum;
+
+ dont_repeat (); /* Too dangerous. */
+ ERROR_NO_INFERIOR;
+
+ if (!signum_exp)
+ error_no_arg ("signal number");
+
+ signum = parse_and_eval_address (signum_exp);
+
+ clear_proceed_status ();
+
+ if (from_tty)
+ printf ("Continuing with signal %d.\n", signum);
+
+ proceed (stop_pc, signum, 0);
+}
+
+/* Execute a "stack dummy", a piece of code stored in the stack
+ by the debugger to be executed in the inferior.
+
+ To call: first, do PUSH_DUMMY_FRAME.
+ Then push the contents of the dummy. It should end with a breakpoint insn.
+ Then call here, passing address at which to start the dummy.
+
+ The contents of all registers are saved before the dummy frame is popped
+ and copied into the buffer BUFFER.
+
+ The dummy's frame is automatically popped whenever that break is hit.
+ If that is the first time the program stops, run_stack_dummy
+ returns to its caller with that frame already gone.
+ Otherwise, the caller never gets returned to. */
+
+/* 4 => return instead of letting the stack dummy run. */
+
+static int stack_dummy_testing = 0;
+
+void
+run_stack_dummy (addr, buffer)
+ CORE_ADDR addr;
+ REGISTER_TYPE *buffer;
+{
+ /* Now proceed, having reached the desired place. */
+ clear_proceed_status ();
+#ifdef notdef
+ if (stack_dummy_testing & 4)
+ {
+ POP_FRAME;
+ return;
+ }
+#endif
+ proceed (addr, 0, 0);
+
+ if (!stop_stack_dummy)
+ error ("Cannot continue previously requested operation.");
+
+ /* On return, the stack dummy has been popped already. */
+
+ read_register_bytes(0, buffer, REGISTER_BYTES);
+}
+
+/* Proceed until we reach the given line as argument or exit the
+ function. When called with no argument, proceed until we reach a
+ different source line with pc greater than our current one or exit
+ the function. We skip calls in both cases.
+
+ The effect of this command with an argument is identical to setting
+ a momentary breakpoint at the line specified and executing
+ "finish".
+
+ Note that eventually this command should probably be changed so
+ that only source lines are printed out when we hit the breakpoint
+ we set. I'm going to postpone this until after a hopeful rewrite
+ of wait_for_inferior and the proceed status code. -- randy */
+
+void
+until_next_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ FRAME frame;
+ CORE_ADDR pc;
+ struct symbol *func;
+ struct symtab_and_line sal;
+
+ clear_proceed_status ();
+
+ frame = get_current_frame ();
+
+ /* Step until either exited from this function or greater
+ than the current line (if in symbolic section) or pc (if
+ not). */
+
+ pc = read_pc ();
+ func = find_pc_function (pc);
+
+ if (!func)
+ {
+ int misc_func = find_pc_misc_function (pc);
+
+ if (misc_func != -1)
+ error ("Execution is not within a known function.");
+
+ step_range_start = misc_function_vector[misc_func].address;
+ step_range_end = pc;
+ }
+ else
+ {
+ sal = find_pc_line (pc, 0);
+
+ step_range_start = BLOCK_START (SYMBOL_BLOCK_VALUE (func));
+ step_range_end = sal.end;
+ }
+
+ step_over_calls = 1;
+ step_frame_address = FRAME_FP (frame);
+
+ step_multi = 0; /* Only one call to proceed */
+
+ proceed (-1, -1, 1);
+}
+
+void
+until_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ if (!have_inferior_p ())
+ error ("The program is not being run.");
+
+ if (arg)
+ until_break_command (arg, from_tty);
+ else
+ until_next_command (arg, from_tty);
+}
+
+/* "finish": Set a temporary breakpoint at the place
+ the selected frame will return to, then continue. */
+
+static void
+finish_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ struct symtab_and_line sal;
+ register FRAME frame;
+ struct frame_info *fi;
+ register struct symbol *function;
+
+ if (!have_inferior_p ())
+ error ("The program is not being run.");
+ if (arg)
+ error ("The \"finish\" command does not take any arguments.");
+
+ frame = get_prev_frame (selected_frame);
+ if (frame == 0)
+ error ("\"finish\" not meaningful in the outermost frame.");
+
+ clear_proceed_status ();
+
+ fi = get_frame_info (frame);
+ sal = find_pc_line (fi->pc, 0);
+ sal.pc = fi->pc;
+ set_momentary_breakpoint (sal, frame);
+
+ /* Find the function we will return from. */
+
+ fi = get_frame_info (selected_frame);
+ function = find_pc_function (fi->pc);
+
+ if (from_tty)
+ {
+ printf ("Run till exit from ");
+ print_selected_frame ();
+ }
+
+ proceed (-1, -1, 0);
+
+ if (stop_breakpoint == -3 && function != 0)
+ {
+ struct type *value_type;
+ register value val;
+ CORE_ADDR funcaddr;
+ extern char registers[];
+
+ value_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
+ if (!value_type)
+ fatal ("internal: finish_command: function has no target type");
+
+ if (TYPE_CODE (value_type) == TYPE_CODE_VOID)
+ return;
+
+ funcaddr = BLOCK_START (SYMBOL_BLOCK_VALUE (function));
+
+ val = value_being_returned (value_type, registers,
+ using_struct_return (function,
+ funcaddr,
+ value_type));
+
+ printf ("Value returned is $%d = ", record_latest_value (val));
+ value_print (val, stdout, 0, Val_no_prettyprint);
+ putchar ('\n');
+ }
+}
+
+static void
+program_info ()
+{
+ if (inferior_pid == 0)
+ {
+ printf ("The program being debugged is not being run.\n");
+ return;
+ }
+
+ printf ("Program being debugged is in process %d, stopped at 0x%x.\n",
+ inferior_pid, stop_pc);
+ if (stop_step)
+ printf ("It stopped after being stepped.\n");
+ else if (stop_breakpoint > 0)
+ printf ("It stopped at breakpoint %d.\n", stop_breakpoint);
+ else if (stop_signal)
+ printf ("It stopped with signal %d (%s).\n",
+ stop_signal, sys_siglist[stop_signal]);
+
+ printf ("\nType \"info stack\" or \"info reg\" for more information.\n");
+}
+
+static void
+environment_info (var)
+ char *var;
+{
+ if (var)
+ {
+ register char *val = get_in_environ (inferior_environ, var);
+ if (val)
+ printf ("%s = %s\n", var, val);
+ else
+ printf ("Environment variable \"%s\" not defined.\n", var);
+ }
+ else
+ {
+ register char **vector = environ_vector (inferior_environ);
+ while (*vector)
+ printf ("%s\n", *vector++);
+ }
+}
+
+static void
+set_environment_command (arg)
+ char *arg;
+{
+ register char *p, *val, *var;
+ int nullset = 0;
+
+ if (arg == 0)
+ error_no_arg ("environment variable and value");
+
+ /* Find seperation between variable name and value */
+ p = (char *) index (arg, '=');
+ val = (char *) index (arg, ' ');
+
+ if (p != 0 && val != 0)
+ {
+ /* We have both a space and an equals. If the space is before the
+ equals and the only thing between the two is more space, use
+ the equals */
+ if (p > val)
+ while (*val == ' ')
+ val++;
+
+ /* Take the smaller of the two. If there was space before the
+ "=", they will be the same right now. */
+ p = arg + min (p - arg, val - arg);
+ }
+ else if (val != 0 && p == 0)
+ p = val;
+
+ if (p == arg)
+ error_no_arg ("environment variable to set");
+
+ if (p == 0 || p[1] == 0)
+ {
+ nullset = 1;
+ if (p == 0)
+ p = arg + strlen (arg); /* So that savestring below will work */
+ }
+ else
+ {
+ /* Not setting variable value to null */
+ val = p + 1;
+ while (*val == ' ' || *val == '\t')
+ val++;
+ }
+
+ while (p != arg && (p[-1] == ' ' || p[-1] == '\t')) p--;
+
+ var = savestring (arg, p - arg);
+ if (nullset)
+ {
+ printf ("Setting environment variable \"%s\" to null value.\n", var);
+ set_in_environ (inferior_environ, var, "");
+ }
+ else
+ set_in_environ (inferior_environ, var, val);
+ free (var);
+}
+
+static void
+unset_environment_command (var, from_tty)
+ char *var;
+ int from_tty;
+{
+ if (var == 0)
+ /* If there is no argument, delete all environment variables.
+ Ask for confirmation if reading from the terminal. */
+ if (!from_tty || query ("Delete all environment variables? "))
+ {
+ free_environ (inferior_environ);
+ inferior_environ = make_environ ();
+ }
+
+ unset_in_environ (inferior_environ, var);
+}
+
+/* Read an integer from debugged memory, given address and number of bytes. */
+
+long
+read_memory_integer (memaddr, len)
+ CORE_ADDR memaddr;
+ int len;
+{
+ char cbuf;
+ short sbuf;
+ int ibuf;
+ long lbuf;
+ int result_err;
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+
+ if (len == sizeof (char))
+ {
+ result_err = read_memory (memaddr, &cbuf, len);
+ if (result_err)
+ error ("Error reading memory address 0x%x: %s (%d).",
+ memaddr, (result_err < sys_nerr ?
+ sys_errlist[result_err] :
+ "uknown error"), result_err);
+ return cbuf;
+ }
+ if (len == sizeof (short))
+ {
+ result_err = read_memory (memaddr, &sbuf, len);
+ if (result_err)
+ error ("Error reading memory address 0x%x: %s (%d).",
+ memaddr, (result_err < sys_nerr ?
+ sys_errlist[result_err] :
+ "uknown error"), result_err);
+ return sbuf;
+ }
+ if (len == sizeof (int))
+ {
+ result_err = read_memory (memaddr, &ibuf, len);
+ if (result_err)
+ error ("Error reading memory address 0x%x: %s (%d).",
+ memaddr, (result_err < sys_nerr ?
+ sys_errlist[result_err] :
+ "uknown error"), result_err);
+ return ibuf;
+ }
+ if (len == sizeof (lbuf))
+ {
+ result_err = read_memory (memaddr, &lbuf, len);
+ if (result_err)
+ error ("Error reading memory address 0x%x: %s (%d).",
+ memaddr, (result_err < sys_nerr ?
+ sys_errlist[result_err] :
+ "uknown error"), result_err);
+ return lbuf;
+ }
+ error ("Cannot handle integers of %d bytes.", len);
+}
+
+CORE_ADDR
+read_pc ()
+{
+ return (CORE_ADDR) read_register (PC_REGNUM);
+}
+
+void
+write_pc (val)
+ CORE_ADDR val;
+{
+ write_register (PC_REGNUM, (long) val);
+#ifdef NPC_REGNUM
+ write_register (NPC_REGNUM, (long) val+4);
+#endif
+}
+
+char *reg_names[] = REGISTER_NAMES;
+
+#if !defined (DO_REGISTERS_INFO)
+static void
+print_one_register(i)
+ int i;
+{
+ unsigned char raw_buffer[MAX_REGISTER_RAW_SIZE];
+ unsigned char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE];
+ REGISTER_TYPE val;
+
+ /* Get the data in raw format, then convert also to virtual format. */
+ read_relative_register_raw_bytes (i, raw_buffer);
+ REGISTER_CONVERT_TO_VIRTUAL (i, raw_buffer, virtual_buffer);
+
+ fputs_filtered (reg_names[i], stdout);
+ print_spaces_filtered (15 - strlen (reg_names[i]), stdout);
+
+ /* If virtual format is floating, print it that way. */
+ if (TYPE_CODE (REGISTER_VIRTUAL_TYPE (i)) == TYPE_CODE_FLT
+ && ! INVALID_FLOAT (virtual_buffer, REGISTER_VIRTUAL_SIZE (i)))
+ val_print (REGISTER_VIRTUAL_TYPE (i), virtual_buffer, 0,
+ stdout, 0, 1, 0, Val_pretty_default);
+ /* Else if virtual format is too long for printf,
+ print in hex a byte at a time. */
+ else if (REGISTER_VIRTUAL_SIZE (i) > sizeof (long))
+ {
+ register int j;
+ printf_filtered ("0x");
+ for (j = 0; j < REGISTER_VIRTUAL_SIZE (i); j++)
+ printf_filtered ("%02x", virtual_buffer[j]);
+ }
+ /* Else print as integer in hex and in decimal. */
+ else
+ {
+ long val;
+
+ bcopy (virtual_buffer, &val, sizeof (long));
+ if (val == 0)
+ printf_filtered ("0");
+ else
+ printf_filtered ("0x%08x %d", val, val);
+ }
+
+ /* If register has different raw and virtual formats,
+ print the raw format in hex now. */
+
+ if (REGISTER_CONVERTIBLE (i))
+ {
+ register int j;
+
+ printf_filtered (" (raw 0x");
+ for (j = 0; j < REGISTER_RAW_SIZE (i); j++)
+ printf_filtered ("%02x", raw_buffer[j]);
+ printf_filtered (")");
+ }
+ printf_filtered ("\n");
+}
+
+
+/* Print out the machine register regnum. If regnum is -1,
+ print all registers.
+ For most machines, having all_registers_info() print the
+ register(s) one per line is good enough. If a different format
+ is required, (eg, for SPARC or Pyramid 90x, which both have
+ lots of regs), or there is an existing convention for showing
+ all the registers, define the macro DO_REGISTERS_INFO(regnum)
+ to provide that format. */
+static void
+do_registers_info (regnum, fpregs)
+ int regnum;
+ int fpregs;
+{
+ register int i;
+
+ if (regnum >= 0) {
+ print_one_register(regnum);
+ return;
+ }
+#ifdef notdef
+ printf_filtered (
+"Register Contents (relative to selected stack frame)\n\n");
+#endif
+ for (i = 0; i < NUM_REGS; i++)
+ if (TYPE_CODE(REGISTER_VIRTUAL_TYPE(i)) != TYPE_CODE_FLT ||
+ fpregs)
+ print_one_register(i);
+}
+#endif /* no DO_REGISTERS_INFO. */
+
+static void
+registers_info (addr_exp, fpregs)
+ char *addr_exp;
+ int fpregs;
+{
+ int regnum;
+
+ if (!have_inferior_p () && !have_core_file_p ())
+ error ("No inferior or core file");
+
+ if (addr_exp)
+ {
+ if (*addr_exp >= '0' && *addr_exp <= '9')
+ regnum = atoi (addr_exp);
+ else
+ {
+ register char *p = addr_exp;
+ if (p[0] == '$')
+ p++;
+ for (regnum = 0; regnum < NUM_REGS; regnum++)
+ if (!strcmp (p, reg_names[regnum]))
+ break;
+ if (regnum == NUM_REGS)
+ error ("%s: invalid register name.", addr_exp);
+ }
+ }
+ else
+ regnum = -1;
+
+#ifdef DO_REGISTERS_INFO
+ DO_REGISTERS_INFO(regnum);
+#else
+ do_registers_info(regnum, fpregs);
+#endif
+}
+
+static void
+all_registers_info (addr_exp)
+ char *addr_exp;
+{
+ registers_info(addr_exp, 1);
+}
+
+static void
+nofp_registers_info (addr_exp)
+ char *addr_exp;
+{
+ registers_info(addr_exp, 0);
+}
+
+
+#ifdef ATTACH_DETACH
+#define PROCESS_ATTACH_ALLOWED 1
+#else
+#define PROCESS_ATTACH_ALLOWED 0
+#endif
+/*
+ * TODO:
+ * Should save/restore the tty state since it might be that the
+ * program to be debugged was started on this tty and it wants
+ * the tty in some state other than what we want. If it's running
+ * on another terminal or without a terminal, then saving and
+ * restoring the tty state is a harmless no-op.
+ * This only needs to be done if we are attaching to a process.
+ */
+
+/*
+ * attach_command --
+ * takes a program started up outside of gdb and ``attaches'' to it.
+ * This stops it cold in its tracks and allows us to start tracing it.
+ * For this to work, we must be able to send the process a
+ * signal and we must have the same effective uid as the program.
+ */
+static void
+attach_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ char *exec_file;
+ int pid;
+ int remote = 0;
+
+ dont_repeat();
+
+ if (!args)
+ error_no_arg ("process-id or device file to attach");
+
+ while (*args == ' ' || *args == '\t') args++;
+
+ if (args[0] < '0' || args[0] > '9')
+ remote = 1;
+ else
+#ifndef ATTACH_DETACH
+ error ("Can't attach to a process on this machine.");
+#else
+ pid = atoi (args);
+#endif
+
+ if (inferior_pid)
+ {
+ if (query ("A program is being debugged already. Kill it? "))
+ kill_inferior ();
+ else
+ error ("Inferior not killed.");
+ }
+
+ exec_file = (char *) get_exec_file (1);
+
+ if (from_tty)
+ {
+ if (remote)
+ printf ("Attaching remote machine\n");
+ else
+ printf ("Attaching program: %s pid %d\n",
+ exec_file, pid);
+ fflush (stdout);
+ }
+
+ if (remote)
+ {
+ remote_open (args, from_tty);
+ start_remote ();
+ }
+#ifdef ATTACH_DETACH
+ else
+ attach_program (pid);
+#endif
+}
+
+/*
+ * detach_command --
+ * takes a program previously attached to and detaches it.
+ * The program resumes execution and will no longer stop
+ * on signals, etc. We better not have left any breakpoints
+ * in the program or it'll die when it hits one. For this
+ * to work, it may be necessary for the process to have been
+ * previously attached. It *might* work if the program was
+ * started via the normal ptrace (PTRACE_TRACEME).
+ */
+
+static void
+detach_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ int signal = 0;
+
+#ifdef ATTACH_DETACH
+ if (inferior_pid && !remote_debugging)
+ {
+ if (from_tty)
+ {
+ char *exec_file = (char *)get_exec_file (0);
+ if (exec_file == 0)
+ exec_file = "";
+ printf ("Detaching program: %s pid %d\n",
+ exec_file, inferior_pid);
+ fflush (stdout);
+ }
+ if (args)
+ signal = atoi (args);
+
+ detach (signal);
+ inferior_pid = 0;
+ }
+ else
+#endif
+ {
+ if (!remote_debugging)
+ error ("Not currently attached to subsidiary or remote process.");
+
+ if (args)
+ error ("Argument given to \"detach\" when remotely debugging.");
+
+ inferior_pid = 0;
+ remote_close (from_tty);
+ }
+}
+
+/* ARGSUSED */
+static void
+float_info (addr_exp)
+ char *addr_exp;
+{
+#ifdef FLOAT_INFO
+ FLOAT_INFO;
+#else
+ printf ("No floating point info available for this processor.\n");
+#endif
+}
+
+extern struct cmd_list_element *setlist, *deletelist;
+
+void
+_initialize_infcmd ()
+{
+ add_com ("tty", class_run, tty_command,
+ "Set terminal for future runs of program being debugged.");
+
+ add_cmd ("args", class_run, set_args_command,
+ "Specify arguments to give program being debugged when it is started.\n\
+Follow this command with any number of args, to be passed to the program.",
+ &setlist);
+
+ add_info ("environment", environment_info,
+ "The environment to give the program, or one variable's value.\n\
+With an argument VAR, prints the value of environment variable VAR to\n\
+give the program being debugged. With no arguments, prints the entire\n\
+environment to be given to the program.");
+
+ add_cmd ("environment", class_run, unset_environment_command,
+ "Cancel environment variable VAR for the program.\n\
+This does not affect the program until the next \"run\" command.",
+ &deletelist);
+
+ add_cmd ("environment", class_run, set_environment_command,
+ "Set environment variable value to give the program.\n\
+Arguments are VAR VALUE where VAR is variable name and VALUE is value.\n\
+VALUES of environment variables are uninterpreted strings.\n\
+This does not affect the program until the next \"run\" command.",
+ &setlist);
+
+#ifdef ATTACH_DETACH
+ add_com ("attach", class_run, attach_command,
+ "Attach to a process that was started up outside of GDB.\n\
+This command may take as argument a process id or a device file.\n\
+For a process id, you must have permission to send the process a signal,\n\
+and it must have the same effective uid as the debugger.\n\
+For a device file, the file must be a connection to a remote debug server.\n\n\
+Before using \"attach\", you must use the \"exec-file\" command\n\
+to specify the program running in the process,\n\
+and the \"symbol-file\" command to load its symbol table.");
+#else
+ add_com ("attach", class_run, attach_command,
+ "Attach to a process that was started up outside of GDB.\n\
+This commands takes as an argument the name of a device file.\n\
+This file must be a connection to a remote debug server.\n\n\
+Before using \"attach\", you must use the \"exec-file\" command\n\
+to specify the program running in the process,\n\
+and the \"symbol-file\" command to load its symbol table.");
+#endif
+ add_com ("detach", class_run, detach_command,
+ "Detach the process previously attached.\n\
+The process is no longer traced and continues its execution.");
+
+ add_com ("signal", class_run, signal_command,
+ "Continue program giving it signal number SIGNUMBER.");
+
+ add_com ("stepi", class_run, stepi_command,
+ "Step one instruction exactly.\n\
+Argument N means do this N times (or till program stops for another reason).");
+ add_com_alias ("si", "stepi", class_alias, 0);
+
+ add_com ("nexti", class_run, nexti_command,
+ "Step one instruction, but proceed through subroutine calls.\n\
+Argument N means do this N times (or till program stops for another reason).");
+ add_com_alias ("ni", "nexti", class_alias, 0);
+
+ add_com ("finish", class_run, finish_command,
+ "Execute until selected stack frame returns.\n\
+Upon return, the value returned is printed and put in the value history.");
+
+ add_com ("next", class_run, next_command,
+ "Step program, proceeding through subroutine calls.\n\
+Like the \"step\" command as long as subroutine calls do not happen;\n\
+when they do, the call is treated as one instruction.\n\
+Argument N means do this N times (or till program stops for another reason).");
+ add_com_alias ("n", "next", class_run, 1);
+
+ add_com ("step", class_run, step_command,
+ "Step program until it reaches a different source line.\n\
+Argument N means do this N times (or till program stops for another reason).");
+ add_com_alias ("s", "step", class_run, 1);
+
+ add_com ("until", class_run, until_command,
+ "Execute until the program reaches a source line greater than the current\n\
+or a specified line or address or function (same args as break command).\n\
+Execution will also stop upon exit from the current stack frame.");
+ add_com_alias ("u", "until", class_run, 1);
+
+ add_com ("jump", class_run, jump_command,
+ "Continue program being debugged at specified line or address.\n\
+Give as argument either LINENUM or *ADDR, where ADDR is an expression\n\
+for an address to start at.");
+
+ add_com ("cont", class_run, cont_command,
+ "Continue program being debugged, after signal or breakpoint.\n\
+If proceeding from breakpoint, a number N may be used as an argument:\n\
+then the same breakpoint won't break until the Nth time it is reached.");
+ add_com_alias ("c", "cont", class_run, 1);
+
+ add_com ("run", class_run, run_command,
+ "Start debugged program. You may specify arguments to give it.\n\
+Args may include \"*\", or \"[...]\"; they are expanded using \"sh\".\n\
+Input and output redirection with \">\", \"<\", or \">>\" are also allowed.\n\n\
+With no arguments, uses arguments last specified (with \"run\" or \"set args\".\n\
+To cancel previous arguments and run with no arguments,\n\
+use \"set args\" without arguments.");
+ add_com_alias ("r", "run", class_run, 1);
+
+ add_info ("registers", nofp_registers_info,
+ "List of registers and their contents, for selected stack frame.\n\
+Register name as argument means describe only that register.\n\
+(Doesn't display floating point registers; use 'info all-registers'.)\n");
+
+ add_info ("all-registers", all_registers_info,
+ "List of registers and their contents, for selected stack frame.\n\
+Register name as argument means describe only that register.");
+
+ add_info ("program", program_info,
+ "Execution status of the program.");
+
+ add_info ("float", float_info,
+ "Print the status of the floating point unit\n");
+
+ inferior_args = savestring (" ", 1); /* By default, no args. */
+ inferior_environ = make_environ ();
+ init_environ (inferior_environ);
+}
+
diff --git a/gnu/usr.bin/gdb/inferior.h b/gnu/usr.bin/gdb/inferior.h
new file mode 100644
index 0000000..04c662e
--- /dev/null
+++ b/gnu/usr.bin/gdb/inferior.h
@@ -0,0 +1,142 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ *
+ * @(#)inferior.h 6.3 (Berkeley) 5/8/91
+ */
+
+/* Variables that describe the inferior process running under GDB:
+ Where it is, why it stopped, and how to step it.
+ Copyright (C) 1986, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * Structure in which to save the status of the inferior. Save
+ * through "save_inferior_status", restore through
+ * "restore_inferior_status".
+ * This pair of routines should be called around any transfer of
+ * control to the inferior which you don't want showing up in your
+ * control variables.
+ */
+struct inferior_status {
+ int pc_changed;
+ int stop_signal;
+ int stop_pc;
+ int stop_frame_address;
+ int stop_breakpoint;
+ int stop_step;
+ int stop_stack_dummy;
+ int stopped_by_random_signal;
+ int trap_expected;
+ CORE_ADDR step_range_start;
+ CORE_ADDR step_range_end;
+ FRAME_ADDR step_frame_address;
+ int step_over_calls;
+ CORE_ADDR step_resume_break_address;
+ int stop_after_trap;
+ int stop_after_attach;
+ FRAME_ADDR selected_frame_address;
+ int selected_level;
+ struct command_line *breakpoint_commands;
+ char register_context[REGISTER_BYTES];
+ int restore_stack_info;
+};
+
+void save_inferior_status (), restore_inferior_status ();
+
+/* File name for default use for standard in/out in the inferior. */
+
+extern char *inferior_io_terminal;
+
+/* Pid of our debugged inferior, or 0 if no inferior now. */
+
+extern int inferior_pid;
+
+/* Nonzero if debugging a remote machine via a serial link or ethernet. */
+extern int remote_debugging;
+
+/* Routines for use in remote debugging. Documented in remote.c. */
+int remote_read_inferior_memory ();
+int remote_write_inferior_memory ();
+
+/* Last signal that the inferior received (why it stopped). */
+
+extern int stop_signal;
+
+/* Address at which inferior stopped. */
+
+extern CORE_ADDR stop_pc;
+
+/* Stack frame when program stopped. */
+
+extern FRAME_ADDR stop_frame_address;
+
+/* Number of breakpoint it stopped at, or 0 if none. */
+
+extern int stop_breakpoint;
+
+/* Nonzero if stopped due to a step command. */
+
+extern int stop_step;
+
+/* Nonzero if stopped due to completion of a stack dummy routine. */
+
+extern int stop_stack_dummy;
+
+/* Nonzero if program stopped due to a random (unexpected) signal in
+ inferior process. */
+
+extern int stopped_by_random_signal;
+
+/* Range to single step within.
+ If this is nonzero, respond to a single-step signal
+ by continuing to step if the pc is in this range. */
+
+extern CORE_ADDR step_range_start; /* Inclusive */
+extern CORE_ADDR step_range_end; /* Exclusive */
+
+/* Stack frame address as of when stepping command was issued.
+ This is how we know when we step into a subroutine call,
+ and how to set the frame for the breakpoint used to step out. */
+
+extern FRAME_ADDR step_frame_address;
+
+/* 1 means step over all subroutine calls.
+ -1 means step over calls to undebuggable functions. */
+
+extern int step_over_calls;
+
+/* If stepping, nonzero means step count is > 1
+ so don't print frame next time inferior stops
+ if it stops due to stepping. */
+
+extern int step_multi;
+
+/* Save register contents here when about to pop a stack dummy frame. */
+
+extern char stop_registers[REGISTER_BYTES];
+
+/* Nonzero if pc has been changed by the debugger
+ since the inferior stopped. */
+
+extern int pc_changed;
+
+long read_memory_integer ();
diff --git a/gnu/usr.bin/gdb/inflow.c b/gnu/usr.bin/gdb/inflow.c
new file mode 100644
index 0000000..209fcf3
--- /dev/null
+++ b/gnu/usr.bin/gdb/inflow.c
@@ -0,0 +1,569 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)inflow.c 6.5 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Low level interface to ptrace, for GDB when running under Unix.
+ Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "frame.h"
+#include "inferior.h"
+
+#ifdef USG
+#include <sys/types.h>
+#endif
+
+/* Some USG-esque systems (some of which are BSD-esque enough so that USG
+ is not defined) want this header, and it won't do any harm. */
+#include <fcntl.h>
+
+#include <sys/param.h>
+#include <sys/dir.h>
+#include <signal.h>
+
+#ifdef HAVE_TERMIO
+#include <termio.h>
+#undef TIOCGETP
+#define TIOCGETP TCGETA
+#undef TIOCSETN
+#define TIOCSETN TCSETA
+#undef TIOCSETP
+#define TIOCSETP TCSETAF
+#define TERMINAL struct termio
+#else
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sgtty.h>
+#define TERMINAL struct sgttyb
+#endif
+
+#ifdef SET_STACK_LIMIT_HUGE
+#include <sys/time.h>
+#include <sys/resource.h>
+extern int original_stack_limit;
+#endif /* SET_STACK_LIMIT_HUGE */
+
+extern int errno;
+
+/* Nonzero if we are debugging an attached outside process
+ rather than an inferior. */
+
+int attach_flag;
+
+
+/* Record terminal status separately for debugger and inferior. */
+
+static TERMINAL sg_inferior;
+static TERMINAL sg_ours;
+
+static int tflags_inferior;
+static int tflags_ours;
+
+#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN)
+static struct tchars tc_inferior;
+static struct tchars tc_ours;
+#endif
+
+#ifdef TIOCGLTC
+static struct ltchars ltc_inferior;
+static struct ltchars ltc_ours;
+#endif
+
+#ifdef TIOCLGET
+static int lmode_inferior;
+static int lmode_ours;
+#endif
+
+#ifdef TIOCGPGRP
+static int pgrp_inferior;
+static int pgrp_ours;
+#else
+static int (*sigint_ours) ();
+static int (*sigquit_ours) ();
+#endif /* TIOCGPGRP */
+
+/* Copy of inferior_io_terminal when inferior was last started. */
+static char *inferior_thisrun_terminal;
+
+static void terminal_ours_1 ();
+
+/* Nonzero if our terminal settings are in effect.
+ Zero if the inferior's settings are in effect. */
+static int terminal_is_ours;
+
+/* Initialize the terminal settings we record for the inferior,
+ before we actually run the inferior. */
+
+void
+terminal_init_inferior ()
+{
+ if (remote_debugging)
+ return;
+
+ sg_inferior = sg_ours;
+ tflags_inferior = tflags_ours;
+
+#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN)
+ tc_inferior = tc_ours;
+#endif
+
+#ifdef TIOCGLTC
+ ltc_inferior = ltc_ours;
+#endif
+
+#ifdef TIOCLGET
+ lmode_inferior = lmode_ours;
+#endif
+
+#ifdef TIOCGPGRP
+ pgrp_inferior = inferior_pid;
+#endif /* TIOCGPGRP */
+
+ terminal_is_ours = 1;
+}
+
+/* Put the inferior's terminal settings into effect.
+ This is preparation for starting or resuming the inferior. */
+
+void
+terminal_inferior ()
+{
+ if (remote_debugging)
+ return;
+
+ if (terminal_is_ours) /* && inferior_thisrun_terminal == 0) */
+ {
+ fcntl (0, F_SETFL, tflags_inferior);
+ fcntl (0, F_SETFL, tflags_inferior);
+ ioctl (0, TIOCSETN, &sg_inferior);
+
+#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN)
+ ioctl (0, TIOCSETC, &tc_inferior);
+#endif
+#ifdef TIOCGLTC
+ ioctl (0, TIOCSLTC, &ltc_inferior);
+#endif
+#ifdef TIOCLGET
+ ioctl (0, TIOCLSET, &lmode_inferior);
+#endif
+
+#ifdef TIOCGPGRP
+ ioctl (0, TIOCSPGRP, &pgrp_inferior);
+#else
+ sigint_ours = (int (*) ()) signal (SIGINT, SIG_IGN);
+ sigquit_ours = (int (*) ()) signal (SIGQUIT, SIG_IGN);
+#endif /* TIOCGPGRP */
+ }
+ terminal_is_ours = 0;
+}
+
+/* Put some of our terminal settings into effect,
+ enough to get proper results from our output,
+ but do not change into or out of RAW mode
+ so that no input is discarded.
+
+ After doing this, either terminal_ours or terminal_inferior
+ should be called to get back to a normal state of affairs. */
+
+void
+terminal_ours_for_output ()
+{
+ if (remote_debugging)
+ return;
+
+ terminal_ours_1 (1);
+}
+
+/* Put our terminal settings into effect.
+ First record the inferior's terminal settings
+ so they can be restored properly later. */
+
+void
+terminal_ours ()
+{
+ if (remote_debugging)
+ return;
+
+ terminal_ours_1 (0);
+}
+
+static void
+terminal_ours_1 (output_only)
+ int output_only;
+{
+#ifdef TIOCGPGRP
+ /* Ignore this signal since it will happen when we try to set the pgrp. */
+ void (*osigttou) ();
+#endif /* TIOCGPGRP */
+
+ if (!terminal_is_ours) /* && inferior_thisrun_terminal == 0) */
+ {
+ terminal_is_ours = 1;
+
+#ifdef TIOCGPGRP
+ osigttou = signal (SIGTTOU, SIG_IGN);
+
+ ioctl (0, TIOCGPGRP, &pgrp_inferior);
+ ioctl (0, TIOCSPGRP, &pgrp_ours);
+
+ signal (SIGTTOU, osigttou);
+#else
+ signal (SIGINT, sigint_ours);
+ signal (SIGQUIT, sigquit_ours);
+#endif /* TIOCGPGRP */
+
+ tflags_inferior = fcntl (0, F_GETFL, 0);
+ ioctl (0, TIOCGETP, &sg_inferior);
+
+#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN)
+ ioctl (0, TIOCGETC, &tc_inferior);
+#endif
+#ifdef TIOCGLTC
+ ioctl (0, TIOCGLTC, &ltc_inferior);
+#endif
+#ifdef TIOCLGET
+ ioctl (0, TIOCLGET, &lmode_inferior);
+#endif
+ }
+
+#ifdef HAVE_TERMIO
+ sg_ours.c_lflag |= ICANON;
+ if (output_only && !(sg_inferior.c_lflag & ICANON))
+ sg_ours.c_lflag &= ~ICANON;
+#else /* not HAVE_TERMIO */
+ sg_ours.sg_flags &= ~RAW & ~CBREAK;
+ if (output_only)
+ sg_ours.sg_flags |= (RAW | CBREAK) & sg_inferior.sg_flags;
+#endif /* not HAVE_TERMIO */
+
+ fcntl (0, F_SETFL, tflags_ours);
+ fcntl (0, F_SETFL, tflags_ours);
+ ioctl (0, TIOCSETN, &sg_ours);
+
+#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN)
+ ioctl (0, TIOCSETC, &tc_ours);
+#endif
+#ifdef TIOCGLTC
+ ioctl (0, TIOCSLTC, &ltc_ours);
+#endif
+#ifdef TIOCLGET
+ ioctl (0, TIOCLSET, &lmode_ours);
+#endif
+
+#ifdef HAVE_TERMIO
+ sg_ours.c_lflag |= ICANON;
+#else /* not HAVE_TERMIO */
+ sg_ours.sg_flags &= ~RAW & ~CBREAK;
+#endif /* not HAVE_TERMIO */
+}
+
+static void
+term_status_command ()
+{
+ register int i;
+
+ if (remote_debugging)
+ {
+ printf_filtered ("No terminal status when remote debugging.\n");
+ return;
+ }
+
+ printf_filtered ("Inferior's terminal status (currently saved by GDB):\n");
+
+#ifdef HAVE_TERMIO
+
+ printf_filtered ("fcntl flags = 0x%x, c_iflag = 0x%x, c_oflag = 0x%x,\n",
+ tflags_inferior, sg_inferior.c_iflag, sg_inferior.c_oflag);
+ printf_filtered ("c_cflag = 0x%x, c_lflag = 0x%x, c_line = 0x%x.\n",
+ sg_inferior.c_cflag, sg_inferior.c_lflag, sg_inferior.c_line);
+ printf_filtered ("c_cc: ");
+ for (i = 0; (i < NCC); i += 1)
+ printf_filtered ("0x%x ", sg_inferior.c_cc[i]);
+ printf_filtered ("\n");
+
+#else /* not HAVE_TERMIO */
+
+ printf_filtered ("fcntl flags = 0x%x, sgttyb.sg_flags = 0x%x, owner pid = %d.\n",
+ tflags_inferior, sg_inferior.sg_flags, pgrp_inferior);
+
+#endif /* not HAVE_TERMIO */
+
+#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN)
+ printf_filtered ("tchars: ");
+ for (i = 0; i < sizeof (struct tchars); i++)
+ printf_filtered ("0x%x ", ((char *)&tc_inferior)[i]);
+ printf_filtered ("\n");
+#endif
+
+#ifdef TIOCGLTC
+ printf_filtered ("ltchars: ");
+ for (i = 0; i < sizeof (struct ltchars); i++)
+ printf_filtered ("0x%x ", ((char *)&ltc_inferior)[i]);
+ printf_filtered ("\n");
+ ioctl (0, TIOCSLTC, &ltc_ours);
+#endif
+
+#ifdef TIOCLGET
+ printf_filtered ("lmode: %x\n", lmode_inferior);
+#endif
+}
+
+static void
+new_tty (ttyname)
+ char *ttyname;
+{
+ register int tty;
+ register int fd;
+
+#ifdef TIOCNOTTY
+ /* Disconnect the child process from our controlling terminal. */
+ tty = open("/dev/tty", O_RDWR);
+ if (tty > 0)
+ {
+ ioctl(tty, TIOCNOTTY, 0);
+ close(tty);
+ }
+#endif
+
+ /* Now open the specified new terminal. */
+
+ tty = open(ttyname, O_RDWR);
+ if (tty == -1)
+ _exit(1);
+
+ /* Avoid use of dup2; doesn't exist on all systems. */
+ if (tty != 0)
+ { close (0); dup (tty); }
+ if (tty != 1)
+ { close (1); dup (tty); }
+ if (tty != 2)
+ { close (2); dup (tty); }
+ if (tty > 2)
+ close(tty);
+}
+
+/* Start an inferior process and returns its pid.
+ ALLARGS is a string containing shell command to run the program.
+ ENV is the environment vector to pass. */
+
+#ifndef SHELL_FILE
+#define SHELL_FILE "/bin/sh"
+#endif
+
+int
+create_inferior (allargs, env)
+ char *allargs;
+ char **env;
+{
+ int pid;
+ char *shell_command;
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+ extern int errno;
+
+ /* If desired, concat something onto the front of ALLARGS.
+ SHELL_COMMAND is the result. */
+#ifdef SHELL_COMMAND_CONCAT
+ shell_command = (char *) alloca (strlen (SHELL_COMMAND_CONCAT) + strlen (allargs) + 1);
+ strcpy (shell_command, SHELL_COMMAND_CONCAT);
+ strcat (shell_command, allargs);
+#else
+ shell_command = allargs;
+#endif
+
+ /* exec is said to fail if the executable is open. */
+ close_exec_file ();
+
+#if defined(USG) && !defined(HAVE_VFORK)
+ pid = fork ();
+#else
+ pid = vfork ();
+#endif
+
+ if (pid < 0)
+ perror_with_name ("vfork");
+
+ if (pid == 0)
+ {
+#ifdef TIOCGPGRP
+ /* Run inferior in a separate process group. */
+ setpgrp (getpid (), getpid ());
+#endif /* TIOCGPGRP */
+
+#ifdef SET_STACK_LIMIT_HUGE
+ /* Reset the stack limit back to what it was. */
+ {
+ struct rlimit rlim;
+
+ getrlimit (RLIMIT_STACK, &rlim);
+ rlim.rlim_cur = original_stack_limit;
+ setrlimit (RLIMIT_STACK, &rlim);
+ }
+#endif /* SET_STACK_LIMIT_HUGE */
+
+
+ inferior_thisrun_terminal = inferior_io_terminal;
+ if (inferior_io_terminal != 0)
+ new_tty (inferior_io_terminal);
+
+/* It seems that changing the signal handlers for the inferior after
+ a vfork also changes them for the superior. See comments in
+ initialize_signals for how we get the right signal handlers
+ for the inferior. */
+/* Not needed on Sun, at least, and loses there
+ because it clobbers the superior. */
+/*??? signal (SIGQUIT, SIG_DFL);
+ signal (SIGINT, SIG_DFL); */
+
+ call_ptrace (0);
+ execle (SHELL_FILE, "sh", "-c", shell_command, 0, env);
+
+ fprintf (stderr, "Cannot exec %s: %s.\n", SHELL_FILE,
+ errno < sys_nerr ? sys_errlist[errno] : "unknown error");
+ fflush (stderr);
+ _exit (0177);
+ }
+
+#ifdef TIOCGPGRP
+ /* Avoid race with TIOCSPGRP: guarantee that inferior's pgrp exists. */
+ setpgrp (pid, pid);
+#endif /* TIOCGPGRP */
+
+#ifdef CREATE_INFERIOR_HOOK
+ CREATE_INFERIOR_HOOK (pid);
+#endif
+ return pid;
+}
+
+/* Kill the inferior process. Make us have no inferior. */
+
+static void
+kill_command ()
+{
+ if (remote_debugging)
+ {
+ inferior_pid = 0;
+ return;
+ }
+ if (inferior_pid == 0)
+ error ("The program is not being run.");
+ if (!query ("Kill the inferior process? "))
+ error ("Not confirmed.");
+ kill_inferior ();
+}
+
+void
+inferior_died ()
+{
+ inferior_pid = 0;
+ attach_flag = 0;
+ mark_breakpoints_out ();
+ select_frame ((FRAME) 0, -1);
+ reopen_exec_file ();
+ if (have_core_file_p ())
+ set_current_frame ( create_new_frame (read_register (FP_REGNUM),
+ read_pc ()));
+ else
+ set_current_frame (0);
+}
+
+#if 0
+/* This function is just for testing, and on some systems (Sony NewsOS
+ 3.2) <sys/user.h> also includes <sys/time.h> which leads to errors
+ (since on this system at least sys/time.h is not protected against
+ multiple inclusion). */
+static void
+try_writing_regs_command ()
+{
+ register int i;
+ register int value;
+ extern int errno;
+
+ if (inferior_pid == 0)
+ error ("There is no inferior process now.");
+
+ /* A Sun 3/50 or 3/60 (at least) running SunOS 4.0.3 will have a
+ kernel panic if we try to write past the end of the user area.
+ Presumably Sun will fix this bug (it has been reported), but it
+ is tacky to crash the system, so at least on SunOS4 we need to
+ stop writing when we hit the end of the user area. */
+ for (i = 0; i < sizeof (struct user); i += 2)
+ {
+ QUIT;
+ errno = 0;
+ value = call_ptrace (3, inferior_pid, i, 0);
+ call_ptrace (6, inferior_pid, i, value);
+ if (errno == 0)
+ {
+ printf (" Succeeded with address 0x%x; value 0x%x (%d).\n",
+ i, value, value);
+ }
+ else if ((i & 0377) == 0)
+ printf (" Failed at 0x%x.\n", i);
+ }
+}
+#endif
+
+void
+_initialize_inflow ()
+{
+ add_com ("term-status", class_obscure, term_status_command,
+ "Print info on inferior's saved terminal status.");
+
+#if 0
+ add_com ("try-writing-regs", class_obscure, try_writing_regs_command,
+ "Try writing all locations in inferior's system block.\n\
+Report which ones can be written.");
+#endif
+
+ add_com ("kill", class_run, kill_command,
+ "Kill execution of program being debugged.");
+
+ inferior_pid = 0;
+
+ ioctl (0, TIOCGETP, &sg_ours);
+ tflags_ours = fcntl (0, F_GETFL, 0);
+
+#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN)
+ ioctl (0, TIOCGETC, &tc_ours);
+#endif
+#ifdef TIOCGLTC
+ ioctl (0, TIOCGLTC, &ltc_ours);
+#endif
+#ifdef TIOCLGET
+ ioctl (0, TIOCLGET, &lmode_ours);
+#endif
+
+#ifdef TIOCGPGRP
+ ioctl (0, TIOCGPGRP, &pgrp_ours);
+#endif /* TIOCGPGRP */
+
+ terminal_is_ours = 1;
+}
+
diff --git a/gnu/usr.bin/gdb/infrun.c b/gnu/usr.bin/gdb/infrun.c
new file mode 100644
index 0000000..887a0bb
--- /dev/null
+++ b/gnu/usr.bin/gdb/infrun.c
@@ -0,0 +1,1459 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)infrun.c 6.4 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Start and stop the inferior process, for GDB.
+ Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Notes on the algorithm used in wait_for_inferior to determine if we
+ just did a subroutine call when stepping. We have the following
+ information at that point:
+
+ Current and previous (just before this step) pc.
+ Current and previous sp.
+ Current and previous start of current function.
+
+ If the start's of the functions don't match, then
+
+ a) We did a subroutine call.
+
+ In this case, the pc will be at the beginning of a function.
+
+ b) We did a subroutine return.
+
+ Otherwise.
+
+ c) We did a longjmp.
+
+ If we did a longjump, we were doing "nexti", since a next would
+ have attempted to skip over the assembly language routine in which
+ the longjmp is coded and would have simply been the equivalent of a
+ continue. I consider this ok behaivior. We'd like one of two
+ things to happen if we are doing a nexti through the longjmp()
+ routine: 1) It behaves as a stepi, or 2) It acts like a continue as
+ above. Given that this is a special case, and that anybody who
+ thinks that the concept of sub calls is meaningful in the context
+ of a longjmp, I'll take either one. Let's see what happens.
+
+ Acts like a subroutine return. I can handle that with no problem
+ at all.
+
+ -->So: If the current and previous beginnings of the current
+ function don't match, *and* the pc is at the start of a function,
+ we've done a subroutine call. If the pc is not at the start of a
+ function, we *didn't* do a subroutine call.
+
+ -->If the beginnings of the current and previous function do match,
+ either:
+
+ a) We just did a recursive call.
+
+ In this case, we would be at the very beginning of a
+ function and 1) it will have a prologue (don't jump to
+ before prologue, or 2) (we assume here that it doesn't have
+ a prologue) there will have been a change in the stack
+ pointer over the last instruction. (Ie. it's got to put
+ the saved pc somewhere. The stack is the usual place. In
+ a recursive call a register is only an option if there's a
+ prologue to do something with it. This is even true on
+ register window machines; the prologue sets up the new
+ window. It might not be true on a register window machine
+ where the call instruction moved the register window
+ itself. Hmmm. One would hope that the stack pointer would
+ also change. If it doesn't, somebody send me a note, and
+ I'll work out a more general theory.
+ randy@wheaties.ai.mit.edu). This is true (albeit slipperly
+ so) on all machines I'm aware of:
+
+ m68k: Call changes stack pointer. Regular jumps don't.
+
+ sparc: Recursive calls must have frames and therefor,
+ prologues.
+
+ vax: All calls have frames and hence change the
+ stack pointer.
+
+ b) We did a return from a recursive call. I don't see that we
+ have either the ability or the need to distinguish this
+ from an ordinary jump. The stack frame will be printed
+ when and if the frame pointer changes; if we are in a
+ function without a frame pointer, it's the users own
+ lookout.
+
+ c) We did a jump within a function. We assume that this is
+ true if we didn't do a recursive call.
+
+ d) We are in no-man's land ("I see no symbols here"). We
+ don't worry about this; it will make calls look like simple
+ jumps (and the stack frames will be printed when the frame
+ pointer moves), which is a reasonably non-violent response.
+
+#if 0
+ We skip this; it causes more problems than it's worth.
+#ifdef SUN4_COMPILER_FEATURE
+ We do a special ifdef for the sun 4, forcing it to single step
+ into calls which don't have prologues. This means that we can't
+ nexti over leaf nodes, we can probably next over them (since they
+ won't have debugging symbols, usually), and we can next out of
+ functions returning structures (with a "call .stret4" at the end).
+#endif
+#endif
+*/
+
+
+
+
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "frame.h"
+#include "inferior.h"
+#include "wait.h"
+
+#include <signal.h>
+
+/* unistd.h is needed to #define X_OK */
+#ifdef USG
+#include <unistd.h>
+#else
+#include <sys/file.h>
+#endif
+
+#ifdef UMAX_PTRACE
+#include <aouthdr.h>
+#include <sys/param.h>
+#include <sys/ptrace.h>
+#endif /* UMAX_PTRACE */
+
+/* Required by <sys/user.h>. */
+#include <sys/types.h>
+/* Required by <sys/user.h>, at least on system V. */
+#include <sys/dir.h>
+/* Needed by IN_SIGTRAMP on some machines (e.g. vax). */
+#include <sys/param.h>
+/* Needed by IN_SIGTRAMP on some machines (e.g. vax). */
+#include <sys/user.h>
+
+extern char *sys_siglist[];
+extern int errno;
+
+/* Sigtramp is a routine that the kernel calls (which then calls the
+ signal handler). On most machines it is a library routine that
+ is linked into the executable.
+
+ This macro, given a program counter value and the name of the
+ function in which that PC resides (which can be null if the
+ name is not known), returns nonzero if the PC and name show
+ that we are in sigtramp.
+
+ On most machines just see if the name is sigtramp (and if we have
+ no name, assume we are not in sigtramp). */
+#if !defined (IN_SIGTRAMP)
+#define IN_SIGTRAMP(pc, name) \
+ name && !strcmp ("_sigtramp", name)
+#endif
+
+/* Tables of how to react to signals; the user sets them. */
+
+static char signal_stop[NSIG];
+static char signal_print[NSIG];
+static char signal_program[NSIG];
+
+/* Nonzero if breakpoints are now inserted in the inferior. */
+
+static int breakpoints_inserted;
+
+/* Function inferior was in as of last step command. */
+
+static struct symbol *step_start_function;
+
+/* This is the sequence of bytes we insert for a breakpoint. */
+
+static char break_insn[] = BREAKPOINT;
+
+/* Nonzero => address for special breakpoint for resuming stepping. */
+
+static CORE_ADDR step_resume_break_address;
+
+/* Original contents of the byte where the special breakpoint is. */
+
+static char step_resume_break_shadow[sizeof break_insn];
+
+/* Nonzero means the special breakpoint is a duplicate
+ so it has not itself been inserted. */
+
+static int step_resume_break_duplicate;
+
+/* Nonzero if we are expecting a trace trap and should proceed from it.
+ 2 means expecting 2 trace traps and should continue both times.
+ That occurs when we tell sh to exec the program: we will get
+ a trap after the exec of sh and a second when the program is exec'd. */
+
+static int trap_expected;
+
+/* Nonzero if the next time we try to continue the inferior, it will
+ step one instruction and generate a spurious trace trap.
+ This is used to compensate for a bug in HP-UX. */
+
+static int trap_expected_after_continue;
+
+/* Nonzero means expecting a trace trap
+ and should stop the inferior and return silently when it happens. */
+
+int stop_after_trap;
+
+/* Nonzero means expecting a trace trap due to attaching to a process. */
+
+int stop_after_attach;
+
+/* Nonzero if pc has been changed by the debugger
+ since the inferior stopped. */
+
+int pc_changed;
+
+/* Nonzero if debugging a remote machine via a serial link or ethernet. */
+
+int remote_debugging;
+
+/* Nonzero if program stopped due to error trying to insert breakpoints. */
+
+static int breakpoints_failed;
+
+/* Nonzero if inferior is in sh before our program got exec'd. */
+
+static int running_in_shell;
+
+/* Nonzero after stop if current stack frame should be printed. */
+
+static int stop_print_frame;
+
+#ifdef NO_SINGLE_STEP
+extern int one_stepped; /* From machine dependent code */
+extern void single_step (); /* Same. */
+#endif /* NO_SINGLE_STEP */
+
+static void insert_step_breakpoint ();
+static void remove_step_breakpoint ();
+static void wait_for_inferior ();
+static void normal_stop ();
+
+
+/* Clear out all variables saying what to do when inferior is continued.
+ First do this, then set the ones you want, then call `proceed'. */
+
+void
+clear_proceed_status ()
+{
+ trap_expected = 0;
+ step_range_start = 0;
+ step_range_end = 0;
+ step_frame_address = 0;
+ step_over_calls = -1;
+ step_resume_break_address = 0;
+ stop_after_trap = 0;
+ stop_after_attach = 0;
+
+ /* Discard any remaining commands left by breakpoint we had stopped at. */
+ clear_breakpoint_commands ();
+}
+
+/* Basic routine for continuing the program in various fashions.
+
+ ADDR is the address to resume at, or -1 for resume where stopped.
+ SIGNAL is the signal to give it, or 0 for none,
+ or -1 for act according to how it stopped.
+ STEP is nonzero if should trap after one instruction.
+ -1 means return after that and print nothing.
+ You should probably set various step_... variables
+ before calling here, if you are stepping.
+
+ You should call clear_proceed_status before calling proceed. */
+
+void
+proceed (addr, signal, step)
+ CORE_ADDR addr;
+ int signal;
+ int step;
+{
+ int oneproc = 0;
+
+ if (step > 0)
+ step_start_function = find_pc_function (read_pc ());
+ if (step < 0)
+ stop_after_trap = 1;
+
+ if (addr == -1)
+ {
+ /* If there is a breakpoint at the address we will resume at,
+ step one instruction before inserting breakpoints
+ so that we do not stop right away. */
+
+ if (!pc_changed && breakpoint_here_p (read_pc ()))
+ oneproc = 1;
+ }
+ else
+ {
+ write_register (PC_REGNUM, addr);
+#ifdef NPC_REGNUM
+ write_register (NPC_REGNUM, addr + 4);
+#endif
+ }
+
+ if (trap_expected_after_continue)
+ {
+ /* If (step == 0), a trap will be automatically generated after
+ the first instruction is executed. Force step one
+ instruction to clear this condition. This should not occur
+ if step is nonzero, but it is harmless in that case. */
+ oneproc = 1;
+ trap_expected_after_continue = 0;
+ }
+
+ if (oneproc)
+ /* We will get a trace trap after one instruction.
+ Continue it automatically and insert breakpoints then. */
+ trap_expected = 1;
+ else
+ {
+ int temp = insert_breakpoints ();
+ if (temp)
+ {
+ print_sys_errmsg ("ptrace", temp);
+ error ("Cannot insert breakpoints.\n\
+The same program may be running in another process.");
+ }
+ breakpoints_inserted = 1;
+ }
+
+ /* Install inferior's terminal modes. */
+ terminal_inferior ();
+
+ if (signal >= 0)
+ stop_signal = signal;
+ /* If this signal should not be seen by program,
+ give it zero. Used for debugging signals. */
+ else if (stop_signal < NSIG && !signal_program[stop_signal])
+ stop_signal= 0;
+
+ /* Resume inferior. */
+ resume (oneproc || step, stop_signal);
+
+ /* Wait for it to stop (if not standalone)
+ and in any case decode why it stopped, and act accordingly. */
+
+ wait_for_inferior ();
+ normal_stop ();
+}
+
+/* Writing the inferior pc as a register calls this function
+ to inform infrun that the pc has been set in the debugger. */
+
+void
+writing_pc (val)
+ CORE_ADDR val;
+{
+ stop_pc = val;
+ pc_changed = 1;
+}
+
+/* Start an inferior process for the first time.
+ Actually it was started by the fork that created it,
+ but it will have stopped one instruction after execing sh.
+ Here we must get it up to actual execution of the real program. */
+
+void
+start_inferior ()
+{
+ /* We will get a trace trap after one instruction.
+ Continue it automatically. Eventually (after shell does an exec)
+ it will get another trace trap. Then insert breakpoints and continue. */
+
+#ifdef START_INFERIOR_TRAPS_EXPECTED
+ trap_expected = START_INFERIOR_TRAPS_EXPECTED;
+#else
+ trap_expected = 2;
+#endif
+
+ running_in_shell = 0; /* Set to 1 at first SIGTRAP, 0 at second. */
+ trap_expected_after_continue = 0;
+ breakpoints_inserted = 0;
+ mark_breakpoints_out ();
+
+ /* Set up the "saved terminal modes" of the inferior
+ based on what modes we are starting it with. */
+ terminal_init_inferior ();
+
+ /* Install inferior's terminal modes. */
+ terminal_inferior ();
+
+ if (remote_debugging)
+ {
+ trap_expected = 0;
+ fetch_inferior_registers();
+ set_current_frame (create_new_frame (read_register (FP_REGNUM),
+ read_pc ()));
+ stop_frame_address = FRAME_FP (get_current_frame());
+ inferior_pid = 3;
+ if (insert_breakpoints())
+ fatal("Can't insert breakpoints");
+ breakpoints_inserted = 1;
+ proceed(-1, -1, 0);
+ }
+ else
+ {
+ wait_for_inferior ();
+ normal_stop ();
+ }
+}
+
+/* Start or restart remote-debugging of a machine over a serial link. */
+
+void
+restart_remote ()
+{
+ clear_proceed_status ();
+ running_in_shell = 0;
+ trap_expected = 0;
+ stop_after_attach = 1;
+ inferior_pid = 3;
+ wait_for_inferior ();
+ normal_stop();
+}
+
+void
+start_remote ()
+{
+ breakpoints_inserted = 0;
+ mark_breakpoints_out ();
+ restart_remote();
+}
+
+#ifdef ATTACH_DETACH
+
+/* Attach to process PID, then initialize for debugging it
+ and wait for the trace-trap that results from attaching. */
+
+void
+attach_program (pid)
+ int pid;
+{
+ attach (pid);
+ inferior_pid = pid;
+
+ mark_breakpoints_out ();
+ terminal_init_inferior ();
+ clear_proceed_status ();
+ stop_after_attach = 1;
+ /*proceed (-1, 0, -2);*/
+ terminal_inferior ();
+ wait_for_inferior ();
+ normal_stop ();
+}
+#endif /* ATTACH_DETACH */
+
+/* Wait for control to return from inferior to debugger.
+ If inferior gets a signal, we may decide to start it up again
+ instead of returning. That is why there is a loop in this function.
+ When this function actually returns it means the inferior
+ should be left stopped and GDB should read more commands. */
+
+static void
+wait_for_inferior ()
+{
+ register int pid;
+ WAITTYPE w;
+ CORE_ADDR pc;
+ int tem;
+ int another_trap;
+ int random_signal;
+ CORE_ADDR stop_sp, prev_sp;
+ CORE_ADDR prev_func_start, stop_func_start;
+ char *prev_func_name, *stop_func_name;
+ CORE_ADDR prologue_pc;
+ int stop_step_resume_break;
+ CORE_ADDR step_resume_break_sp;
+ int newmisc;
+ int newfun_pc;
+ struct symtab_and_line sal;
+ int prev_pc;
+ extern CORE_ADDR text_end;
+ int remove_breakpoints_on_following_step = 0;
+
+ prev_pc = read_pc ();
+ (void) find_pc_partial_function (prev_pc, &prev_func_name,
+ &prev_func_start);
+ prev_func_start += FUNCTION_START_OFFSET;
+ prev_sp = read_register (SP_REGNUM);
+
+ while (1)
+ {
+ /* Clean up saved state that will become invalid. */
+ pc_changed = 0;
+ flush_cached_frames ();
+
+ if (remote_debugging)
+ remote_wait (&w);
+ else
+ {
+ pid = wait (&w);
+ if (pid != inferior_pid)
+ continue;
+ }
+
+ /* See if the process still exists; clean up if it doesn't. */
+ if (WIFEXITED (w))
+ {
+ terminal_ours_for_output ();
+ if (WEXITSTATUS (w))
+ printf ("\nProgram exited with code 0%o.\n", WEXITSTATUS (w));
+ else
+ printf ("\nProgram exited normally.\n");
+ fflush (stdout);
+ inferior_died ();
+#ifdef NO_SINGLE_STEP
+ one_stepped = 0;
+#endif
+ stop_print_frame = 0;
+ break;
+ }
+ else if (!WIFSTOPPED (w))
+ {
+ kill_inferior ();
+ stop_print_frame = 0;
+ stop_signal = WTERMSIG (w);
+ terminal_ours_for_output ();
+ printf ("\nProgram terminated with signal %d, %s\n",
+ stop_signal,
+ stop_signal < NSIG
+ ? sys_siglist[stop_signal]
+ : "(undocumented)");
+ printf ("The inferior process no longer exists.\n");
+ fflush (stdout);
+#ifdef NO_SINGLE_STEP
+ one_stepped = 0;
+#endif
+ break;
+ }
+
+#ifdef NO_SINGLE_STEP
+ if (one_stepped)
+ single_step (0); /* This actually cleans up the ss */
+#endif /* NO_SINGLE_STEP */
+
+ fetch_inferior_registers ();
+ stop_pc = read_pc ();
+ set_current_frame ( create_new_frame (read_register (FP_REGNUM),
+ read_pc ()));
+
+ stop_frame_address = FRAME_FP (get_current_frame ());
+ stop_sp = read_register (SP_REGNUM);
+ stop_func_start = 0;
+ stop_func_name = 0;
+ /* Don't care about return value; stop_func_start and stop_func_name
+ will both be 0 if it doesn't work. */
+ (void) find_pc_partial_function (stop_pc, &stop_func_name,
+ &stop_func_start);
+ stop_func_start += FUNCTION_START_OFFSET;
+ another_trap = 0;
+ stop_breakpoint = 0;
+ stop_step = 0;
+ stop_stack_dummy = 0;
+ stop_print_frame = 1;
+ stop_step_resume_break = 0;
+ random_signal = 0;
+ stopped_by_random_signal = 0;
+ breakpoints_failed = 0;
+
+ /* Look at the cause of the stop, and decide what to do.
+ The alternatives are:
+ 1) break; to really stop and return to the debugger,
+ 2) drop through to start up again
+ (set another_trap to 1 to single step once)
+ 3) set random_signal to 1, and the decision between 1 and 2
+ will be made according to the signal handling tables. */
+
+ stop_signal = WSTOPSIG (w);
+
+ /* First, distinguish signals caused by the debugger from signals
+ that have to do with the program's own actions.
+ Note that breakpoint insns may cause SIGTRAP or SIGILL
+ or SIGEMT, depending on the operating system version.
+ Here we detect when a SIGILL or SIGEMT is really a breakpoint
+ and change it to SIGTRAP. */
+
+ if (stop_signal == SIGTRAP
+ || (breakpoints_inserted &&
+ (stop_signal == SIGILL
+ || stop_signal == SIGEMT))
+ || stop_after_attach)
+ {
+ if (stop_signal == SIGTRAP && stop_after_trap)
+ {
+ stop_print_frame = 0;
+ break;
+ }
+ if (stop_after_attach)
+ break;
+ /* Don't even think about breakpoints
+ if still running the shell that will exec the program
+ or if just proceeded over a breakpoint. */
+ if (stop_signal == SIGTRAP && trap_expected)
+ stop_breakpoint = 0;
+ else
+ {
+ /* See if there is a breakpoint at the current PC. */
+#if DECR_PC_AFTER_BREAK
+ /* Notice the case of stepping through a jump
+ that leads just after a breakpoint.
+ Don't confuse that with hitting the breakpoint.
+ What we check for is that 1) stepping is going on
+ and 2) the pc before the last insn does not match
+ the address of the breakpoint before the current pc. */
+ if (!(prev_pc != stop_pc - DECR_PC_AFTER_BREAK
+ && step_range_end && !step_resume_break_address))
+#endif /* DECR_PC_AFTER_BREAK not zero */
+ {
+ /* See if we stopped at the special breakpoint for
+ stepping over a subroutine call. */
+ if (stop_pc - DECR_PC_AFTER_BREAK
+ == step_resume_break_address)
+ {
+ stop_step_resume_break = 1;
+ if (DECR_PC_AFTER_BREAK)
+ {
+ stop_pc -= DECR_PC_AFTER_BREAK;
+ write_register (PC_REGNUM, stop_pc);
+ pc_changed = 0;
+ }
+ }
+ else
+ {
+ stop_breakpoint =
+ breakpoint_stop_status (stop_pc, stop_frame_address);
+ /* Following in case break condition called a
+ function. */
+ stop_print_frame = 1;
+ if (stop_breakpoint && DECR_PC_AFTER_BREAK)
+ {
+ stop_pc -= DECR_PC_AFTER_BREAK;
+ write_register (PC_REGNUM, stop_pc);
+#ifdef NPC_REGNUM
+ write_register (NPC_REGNUM, stop_pc + 4);
+#endif
+ pc_changed = 0;
+ }
+ }
+ }
+ }
+
+ if (stop_signal == SIGTRAP)
+ random_signal
+ = !(stop_breakpoint || trap_expected
+ || stop_step_resume_break
+#ifndef CANNOT_EXECUTE_STACK
+ || (stop_sp INNER_THAN stop_pc
+ && stop_pc INNER_THAN stop_frame_address)
+#else
+ || stop_pc == text_end - 2
+#endif
+ || (step_range_end && !step_resume_break_address));
+ else
+ {
+ random_signal
+ = !(stop_breakpoint
+ || stop_step_resume_break
+#ifdef sony_news
+ || (stop_sp INNER_THAN stop_pc
+ && stop_pc INNER_THAN stop_frame_address)
+#endif
+
+ );
+ if (!random_signal)
+ stop_signal = SIGTRAP;
+ }
+ }
+ else
+ random_signal = 1;
+
+ /* For the program's own signals, act according to
+ the signal handling tables. */
+
+ if (random_signal
+ && !(running_in_shell && stop_signal == SIGSEGV))
+ {
+ /* Signal not for debugging purposes. */
+ int printed = 0;
+
+ stopped_by_random_signal = 1;
+
+ if (stop_signal >= NSIG
+ || signal_print[stop_signal])
+ {
+ printed = 1;
+ terminal_ours_for_output ();
+ printf ("\nProgram received signal %d, %s\n",
+ stop_signal,
+ stop_signal < NSIG
+ ? sys_siglist[stop_signal]
+ : "(undocumented)");
+ fflush (stdout);
+ }
+ if (stop_signal >= NSIG
+ || signal_stop[stop_signal])
+ break;
+ /* If not going to stop, give terminal back
+ if we took it away. */
+ else if (printed)
+ terminal_inferior ();
+ }
+
+ /* Handle cases caused by hitting a breakpoint. */
+
+ if (!random_signal
+ && (stop_breakpoint || stop_step_resume_break))
+ {
+ /* Does a breakpoint want us to stop? */
+ if (stop_breakpoint && stop_breakpoint != -1
+ && stop_breakpoint != -0x1000001)
+ {
+ /* 0x1000000 is set in stop_breakpoint as returned by
+ breakpoint_stop_status to indicate a silent
+ breakpoint. */
+ if ((stop_breakpoint > 0 ? stop_breakpoint :
+ -stop_breakpoint)
+ & 0x1000000)
+ {
+ stop_print_frame = 0;
+ if (stop_breakpoint > 0)
+ stop_breakpoint -= 0x1000000;
+ else
+ stop_breakpoint += 0x1000000;
+ }
+ break;
+ }
+ /* But if we have hit the step-resumption breakpoint,
+ remove it. It has done its job getting us here.
+ The sp test is to make sure that we don't get hung
+ up in recursive calls in functions without frame
+ pointers. If the stack pointer isn't outside of
+ where the breakpoint was set (within a routine to be
+ stepped over), we're in the middle of a recursive
+ call. Not true for reg window machines (sparc)
+ because the must change frames to call things and
+ the stack pointer doesn't have to change if it
+ the bp was set in a routine without a frame (pc can
+ be stored in some other window).
+
+ The removal of the sp test is to allow calls to
+ alloca. Nasty things were happening. Oh, well,
+ gdb can only handle one level deep of lack of
+ frame pointer. */
+ if (stop_step_resume_break
+ && (step_frame_address == 0
+ || (stop_frame_address == step_frame_address)))
+ {
+ remove_step_breakpoint ();
+ step_resume_break_address = 0;
+ }
+ /* Otherwise, must remove breakpoints and single-step
+ to get us past the one we hit. */
+ else
+ {
+ remove_breakpoints ();
+ remove_step_breakpoint ();
+ breakpoints_inserted = 0;
+ another_trap = 1;
+ }
+
+ /* We come here if we hit a breakpoint but should not
+ stop for it. Possibly we also were stepping
+ and should stop for that. So fall through and
+ test for stepping. But, if not stepping,
+ do not stop. */
+ }
+
+ /* If this is the breakpoint at the end of a stack dummy,
+ just stop silently. */
+#ifndef CANNOT_EXECUTE_STACK
+ if (stop_sp INNER_THAN stop_pc
+ && stop_pc INNER_THAN stop_frame_address)
+#else
+ if (stop_pc == text_end - 2)
+#endif
+ {
+ stop_print_frame = 0;
+ stop_stack_dummy = 1;
+#ifdef HP_OS_BUG
+ trap_expected_after_continue = 1;
+#endif
+ break;
+ }
+
+ if (step_resume_break_address)
+ /* Having a step-resume breakpoint overrides anything
+ else having to do with stepping commands until
+ that breakpoint is reached. */
+ ;
+ /* If stepping through a line, keep going if still within it. */
+ else if (!random_signal
+ && step_range_end
+ && stop_pc >= step_range_start
+ && stop_pc < step_range_end
+ /* The step range might include the start of the
+ function, so if we are at the start of the
+ step range and either the stack or frame pointers
+ just changed, we've stepped outside */
+ && !(stop_pc == step_range_start
+ && stop_frame_address
+ && (stop_sp INNER_THAN prev_sp
+ || stop_frame_address != step_frame_address)))
+ {
+ /* Don't step through the return from a function
+ unless that is the first instruction stepped through. */
+ if (ABOUT_TO_RETURN (stop_pc))
+ {
+ stop_step = 1;
+ break;
+ }
+ }
+
+ /* We stepped out of the stepping range. See if that was due
+ to a subroutine call that we should proceed to the end of. */
+ else if (!random_signal && step_range_end)
+ {
+ if (stop_func_start)
+ {
+ prologue_pc = stop_func_start;
+ SKIP_PROLOGUE (prologue_pc);
+ }
+
+ /* Did we just take a signal? */
+ if (IN_SIGTRAMP (stop_pc, stop_func_name)
+ && !IN_SIGTRAMP (prev_pc, prev_func_name))
+ {
+ /* This code is needed at least in the following case:
+ The user types "next" and then a signal arrives (before
+ the "next" is done). */
+ /* We've just taken a signal; go until we are back to
+ the point where we took it and one more. */
+ step_resume_break_address = prev_pc;
+ step_resume_break_duplicate =
+ breakpoint_here_p (step_resume_break_address);
+ step_resume_break_sp = stop_sp;
+ if (breakpoints_inserted)
+ insert_step_breakpoint ();
+ /* Make sure that the stepping range gets us past
+ that instruction. */
+ if (step_range_end == 1)
+ step_range_end = (step_range_start = prev_pc) + 1;
+ remove_breakpoints_on_following_step = 1;
+ }
+
+ /* ==> See comments at top of file on this algorithm. <==*/
+
+ else if (stop_pc == stop_func_start
+ && (stop_func_start != prev_func_start
+ || prologue_pc != stop_func_start
+ || stop_sp != prev_sp))
+ {
+ /* It's a subroutine call */
+ if (step_over_calls > 0
+ || (step_over_calls && find_pc_function (stop_pc) == 0))
+ {
+ /* A subroutine call has happened. */
+ /* Set a special breakpoint after the return */
+ step_resume_break_address =
+ SAVED_PC_AFTER_CALL (get_current_frame ());
+ step_resume_break_duplicate
+ = breakpoint_here_p (step_resume_break_address);
+ step_resume_break_sp = stop_sp;
+ if (breakpoints_inserted)
+ insert_step_breakpoint ();
+ }
+ /* Subroutine call with source code we should not step over.
+ Do step to the first line of code in it. */
+ else if (step_over_calls)
+ {
+ SKIP_PROLOGUE (stop_func_start);
+ sal = find_pc_line (stop_func_start, 0);
+ /* Use the step_resume_break to step until
+ the end of the prologue, even if that involves jumps
+ (as it seems to on the vax under 4.2). */
+ /* If the prologue ends in the middle of a source line,
+ continue to the end of that source line.
+ Otherwise, just go to end of prologue. */
+#ifdef PROLOGUE_FIRSTLINE_OVERLAP
+ /* no, don't either. It skips any code that's
+ legitimately on the first line. */
+#else
+ if (sal.end && sal.pc != stop_func_start)
+ stop_func_start = sal.end;
+#endif
+
+ if (stop_func_start == stop_pc)
+ {
+ /* We are already there: stop now. */
+ stop_step = 1;
+ break;
+ }
+ else
+ /* Put the step-breakpoint there and go until there. */
+ {
+ step_resume_break_address = stop_func_start;
+ step_resume_break_sp = stop_sp;
+
+ step_resume_break_duplicate
+ = breakpoint_here_p (step_resume_break_address);
+ if (breakpoints_inserted)
+ insert_step_breakpoint ();
+ /* Do not specify what the fp should be when we stop
+ since on some machines the prologue
+ is where the new fp value is established. */
+ step_frame_address = 0;
+ /* And make sure stepping stops right away then. */
+ step_range_end = step_range_start;
+ }
+ }
+ else
+ {
+ /* We get here only if step_over_calls is 0 and we
+ just stepped into a subroutine. I presume
+ that step_over_calls is only 0 when we're
+ supposed to be stepping at the assembly
+ language level.*/
+ stop_step = 1;
+ break;
+ }
+ }
+ /* No subroutince call; stop now. */
+ else
+ {
+ stop_step = 1;
+ break;
+ }
+ }
+
+ /* Save the pc before execution, to compare with pc after stop. */
+ prev_pc = read_pc (); /* Might have been DECR_AFTER_BREAK */
+ prev_func_start = stop_func_start; /* Ok, since if DECR_PC_AFTER
+ BREAK is defined, the
+ original pc would not have
+ been at the start of a
+ function. */
+ prev_func_name = stop_func_name;
+ prev_sp = stop_sp;
+
+ /* If we did not do break;, it means we should keep
+ running the inferior and not return to debugger. */
+
+ /* If trap_expected is 2, it means continue once more
+ and insert breakpoints at the next trap.
+ If trap_expected is 1 and the signal was SIGSEGV, it means
+ the shell is doing some memory allocation--just resume it
+ with SIGSEGV.
+ Otherwise insert breakpoints now, and possibly single step. */
+
+ if (trap_expected > 1)
+ {
+ trap_expected--;
+ running_in_shell = 1;
+ resume (0, 0);
+ }
+ else if (running_in_shell && stop_signal == SIGSEGV)
+ {
+ resume (0, SIGSEGV);
+ }
+ else if (trap_expected && stop_signal != SIGTRAP)
+ {
+ /* We took a signal which we are supposed to pass through to
+ the inferior and we haven't yet gotten our trap. Simply
+ continue. */
+ resume ((step_range_end && !step_resume_break_address)
+ || trap_expected,
+ stop_signal);
+ }
+ else
+ {
+ /* Here, we are not awaiting another exec to get
+ the program we really want to debug.
+ Insert breakpoints now, unless we are trying
+ to one-proceed past a breakpoint. */
+ running_in_shell = 0;
+ /* If we've just finished a special step resume and we don't
+ want to hit a breakpoint, pull em out. */
+ if (!step_resume_break_address &&
+ remove_breakpoints_on_following_step)
+ {
+ remove_breakpoints_on_following_step = 0;
+ remove_breakpoints ();
+ breakpoints_inserted = 0;
+ }
+ else if (!breakpoints_inserted && !another_trap)
+ {
+ insert_step_breakpoint ();
+ breakpoints_failed = insert_breakpoints ();
+ if (breakpoints_failed)
+ break;
+ breakpoints_inserted = 1;
+ }
+
+ trap_expected = another_trap;
+
+ if (stop_signal == SIGTRAP)
+ stop_signal = 0;
+
+ resume ((step_range_end && !step_resume_break_address)
+ || trap_expected,
+ stop_signal);
+ }
+ }
+}
+
+/* Here to return control to GDB when the inferior stops for real.
+ Print appropriate messages, remove breakpoints, give terminal our modes.
+
+ RUNNING_IN_SHELL nonzero means the shell got a signal before
+ exec'ing the program we wanted to run.
+ STOP_PRINT_FRAME nonzero means print the executing frame
+ (pc, function, args, file, line number and line text).
+ BREAKPOINTS_FAILED nonzero means stop was due to error
+ attempting to insert breakpoints. */
+
+static void
+normal_stop ()
+{
+ /* Make sure that the current_frame's pc is correct. This
+ is a correction for setting up the frame info before doing
+ DECR_PC_AFTER_BREAK */
+ if (inferior_pid)
+ (get_current_frame ())->pc = read_pc ();
+
+ if (breakpoints_failed)
+ {
+ terminal_ours_for_output ();
+ print_sys_errmsg ("ptrace", breakpoints_failed);
+ printf ("Stopped; cannot insert breakpoints.\n\
+The same program may be running in another process.\n");
+ }
+
+ if (inferior_pid)
+ remove_step_breakpoint ();
+
+ if (inferior_pid && breakpoints_inserted)
+ if (remove_breakpoints ())
+ {
+ terminal_ours_for_output ();
+ printf ("Cannot remove breakpoints because program is no longer writable.\n\
+It must be running in another process.\n\
+Further execution is probably impossible.\n");
+ }
+
+ breakpoints_inserted = 0;
+
+ /* Delete the breakpoint we stopped at, if it wants to be deleted.
+ Delete any breakpoint that is to be deleted at the next stop. */
+
+ breakpoint_auto_delete (stop_breakpoint);
+
+ /* If an auto-display called a function and that got a signal,
+ delete that auto-display to avoid an infinite recursion. */
+
+ if (stopped_by_random_signal)
+ disable_current_display ();
+
+ if (step_multi && stop_step)
+ return;
+
+ terminal_ours ();
+
+ if (running_in_shell)
+ {
+ if (stop_signal == SIGSEGV)
+ {
+ char *exec_file = (char *) get_exec_file (1);
+
+ if (access (exec_file, X_OK) != 0)
+ printf ("The file \"%s\" is not executable.\n", exec_file);
+ else
+ /* I don't think we should ever get here.
+ wait_for_inferior now ignores SIGSEGV's which happen in
+ the shell (since the Bourne shell (/bin/sh) has some
+ rather, er, uh, *unorthodox* memory management
+ involving catching SIGSEGV). */
+ printf ("\
+You have just encountered a bug in \"sh\". GDB starts your program\n\
+by running \"sh\" with a command to exec your program.\n\
+This is so that \"sh\" will process wildcards and I/O redirection.\n\
+This time, \"sh\" crashed.\n\
+\n\
+One known bug in \"sh\" bites when the environment takes up a lot of space.\n\
+Try \"info env\" to see the environment; then use \"delete env\" to kill\n\
+some variables whose values are large; then do \"run\" again.\n\
+\n\
+If that works, you might want to put those \"delete env\" commands\n\
+into a \".gdbinit\" file in this directory so they will happen every time.\n");
+ }
+ /* Don't confuse user with his program's symbols on sh's data. */
+ stop_print_frame = 0;
+ }
+
+ if (inferior_pid == 0)
+ return;
+
+ /* Select innermost stack frame except on return from a stack dummy routine,
+ or if the program has exited. */
+ if (!stop_stack_dummy)
+ {
+ select_frame (get_current_frame (), 0);
+
+ if (stop_print_frame)
+ {
+ if (stop_breakpoint > 0)
+ printf ("\nBpt %d, ", stop_breakpoint);
+ print_sel_frame (stop_step
+ && step_frame_address == stop_frame_address
+ && step_start_function == find_pc_function (stop_pc));
+ /* Display the auto-display expressions. */
+ do_displays ();
+ }
+ }
+
+ if (stop_stack_dummy)
+ {
+ /* Pop the empty frame that contains the stack dummy.
+ POP_FRAME ends with a setting of the current frame, so we
+ can use that next. */
+#ifndef NEW_CALL_FUNCTION
+ POP_FRAME;
+#endif
+ select_frame (get_current_frame (), 0);
+ }
+}
+
+static void
+insert_step_breakpoint ()
+{
+ if (step_resume_break_address && !step_resume_break_duplicate)
+ {
+ read_memory (step_resume_break_address,
+ step_resume_break_shadow, sizeof break_insn);
+ write_memory (step_resume_break_address,
+ break_insn, sizeof break_insn);
+ }
+}
+
+static void
+remove_step_breakpoint ()
+{
+ if (step_resume_break_address && !step_resume_break_duplicate)
+ write_memory (step_resume_break_address, step_resume_break_shadow,
+ sizeof break_insn);
+}
+
+/* Specify how various signals in the inferior should be handled. */
+
+static void
+handle_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ register char *p = args;
+ int signum = 0;
+ register int digits, wordlen;
+
+ if (!args)
+ error_no_arg ("signal to handle");
+
+ while (*p)
+ {
+ /* Find the end of the next word in the args. */
+ for (wordlen = 0; p[wordlen] && p[wordlen] != ' ' && p[wordlen] != '\t';
+ wordlen++);
+ for (digits = 0; p[digits] >= '0' && p[digits] <= '9'; digits++);
+
+ /* If it is all digits, it is signal number to operate on. */
+ if (digits == wordlen)
+ {
+ signum = atoi (p);
+ if (signum <= 0 || signum >= NSIG)
+ {
+ p[wordlen] = '\0';
+ error ("Invalid signal %s given as argument to \"handle\".", p);
+ }
+ if (signum == SIGTRAP || signum == SIGINT)
+ {
+ if (!query ("Signal %d is used by the debugger.\nAre you sure you want to change it? ", signum))
+ error ("Not confirmed.");
+ }
+ }
+ else if (signum == 0)
+ error ("First argument is not a signal number.");
+
+ /* Else, if already got a signal number, look for flag words
+ saying what to do for it. */
+ else if (!strncmp (p, "stop", wordlen))
+ {
+ signal_stop[signum] = 1;
+ signal_print[signum] = 1;
+ }
+ else if (wordlen >= 2 && !strncmp (p, "print", wordlen))
+ signal_print[signum] = 1;
+ else if (wordlen >= 2 && !strncmp (p, "pass", wordlen))
+ signal_program[signum] = 1;
+ else if (!strncmp (p, "ignore", wordlen))
+ signal_program[signum] = 0;
+ else if (wordlen >= 3 && !strncmp (p, "nostop", wordlen))
+ signal_stop[signum] = 0;
+ else if (wordlen >= 4 && !strncmp (p, "noprint", wordlen))
+ {
+ signal_print[signum] = 0;
+ signal_stop[signum] = 0;
+ }
+ else if (wordlen >= 4 && !strncmp (p, "nopass", wordlen))
+ signal_program[signum] = 0;
+ else if (wordlen >= 3 && !strncmp (p, "noignore", wordlen))
+ signal_program[signum] = 1;
+ /* Not a number and not a recognized flag word => complain. */
+ else
+ {
+ p[wordlen] = 0;
+ error ("Unrecognized flag word: \"%s\".", p);
+ }
+
+ /* Find start of next word. */
+ p += wordlen;
+ while (*p == ' ' || *p == '\t') p++;
+ }
+
+ if (from_tty)
+ {
+ /* Show the results. */
+ printf ("Number\tStop\tPrint\tPass to program\tDescription\n");
+ printf ("%d\t", signum);
+ printf ("%s\t", signal_stop[signum] ? "Yes" : "No");
+ printf ("%s\t", signal_print[signum] ? "Yes" : "No");
+ printf ("%s\t\t", signal_program[signum] ? "Yes" : "No");
+ printf ("%s\n", sys_siglist[signum]);
+ }
+}
+
+/* Print current contents of the tables set by the handle command. */
+
+static void
+signals_info (signum_exp)
+ char *signum_exp;
+{
+ register int i;
+ printf_filtered ("Number\tStop\tPrint\tPass to program\tDescription\n");
+
+ if (signum_exp)
+ {
+ i = parse_and_eval_address (signum_exp);
+ if (i >= NSIG || i < 0)
+ error ("Signal number out of bounds.");
+ printf_filtered ("%d\t", i);
+ printf_filtered ("%s\t", signal_stop[i] ? "Yes" : "No");
+ printf_filtered ("%s\t", signal_print[i] ? "Yes" : "No");
+ printf_filtered ("%s\t\t", signal_program[i] ? "Yes" : "No");
+ printf_filtered ("%s\n", sys_siglist[i]);
+ return;
+ }
+
+ printf_filtered ("\n");
+ for (i = 0; i < NSIG; i++)
+ {
+ QUIT;
+
+ printf_filtered ("%d\t", i);
+ printf_filtered ("%s\t", signal_stop[i] ? "Yes" : "No");
+ printf_filtered ("%s\t", signal_print[i] ? "Yes" : "No");
+ printf_filtered ("%s\t\t", signal_program[i] ? "Yes" : "No");
+ printf_filtered ("%s\n", sys_siglist[i]);
+ }
+
+ printf_filtered ("\nUse the \"handle\" command to change these tables.\n");
+}
+
+/* Save all of the information associated with the inferior<==>gdb
+ connection. INF_STATUS is a pointer to a "struct inferior_status"
+ (defined in inferior.h). */
+
+struct command_line *get_breakpoint_commands ();
+
+void
+save_inferior_status (inf_status, restore_stack_info)
+ struct inferior_status *inf_status;
+ int restore_stack_info;
+{
+ inf_status->pc_changed = pc_changed;
+ inf_status->stop_signal = stop_signal;
+ inf_status->stop_pc = stop_pc;
+ inf_status->stop_frame_address = stop_frame_address;
+ inf_status->stop_breakpoint = stop_breakpoint;
+ inf_status->stop_step = stop_step;
+ inf_status->stop_stack_dummy = stop_stack_dummy;
+ inf_status->stopped_by_random_signal = stopped_by_random_signal;
+ inf_status->trap_expected = trap_expected;
+ inf_status->step_range_start = step_range_start;
+ inf_status->step_range_end = step_range_end;
+ inf_status->step_frame_address = step_frame_address;
+ inf_status->step_over_calls = step_over_calls;
+ inf_status->step_resume_break_address = step_resume_break_address;
+ inf_status->stop_after_trap = stop_after_trap;
+ inf_status->stop_after_attach = stop_after_attach;
+ inf_status->breakpoint_commands = get_breakpoint_commands ();
+ inf_status->restore_stack_info = restore_stack_info;
+
+ read_register_bytes(0, inf_status->register_context, REGISTER_BYTES);
+ record_selected_frame (&(inf_status->selected_frame_address),
+ &(inf_status->selected_level));
+ return;
+}
+
+void
+restore_inferior_status (inf_status)
+ struct inferior_status *inf_status;
+{
+ FRAME fid;
+ int level = inf_status->selected_level;
+
+ pc_changed = inf_status->pc_changed;
+ stop_signal = inf_status->stop_signal;
+ stop_pc = inf_status->stop_pc;
+ stop_frame_address = inf_status->stop_frame_address;
+ stop_breakpoint = inf_status->stop_breakpoint;
+ stop_step = inf_status->stop_step;
+ stop_stack_dummy = inf_status->stop_stack_dummy;
+ stopped_by_random_signal = inf_status->stopped_by_random_signal;
+ trap_expected = inf_status->trap_expected;
+ step_range_start = inf_status->step_range_start;
+ step_range_end = inf_status->step_range_end;
+ step_frame_address = inf_status->step_frame_address;
+ step_over_calls = inf_status->step_over_calls;
+ step_resume_break_address = inf_status->step_resume_break_address;
+ stop_after_trap = inf_status->stop_after_trap;
+ stop_after_attach = inf_status->stop_after_attach;
+ set_breakpoint_commands (inf_status->breakpoint_commands);
+
+ write_register_bytes(0, inf_status->register_context, REGISTER_BYTES);
+
+ /* The inferior can be gone if the user types "print exit(0)"
+ (and perhaps other times). */
+ if (have_inferior_p() && inf_status->restore_stack_info)
+ {
+ flush_cached_frames();
+ set_current_frame(create_new_frame(read_register (FP_REGNUM),
+ read_pc()));
+
+ fid = find_relative_frame (get_current_frame (), &level);
+
+ if (fid == 0 ||
+ FRAME_FP (fid) != inf_status->selected_frame_address ||
+ level != 0)
+ {
+ /* I'm not sure this error message is a good idea. I have
+ only seen it occur after "Can't continue previously
+ requested operation" (we get called from do_cleanups), in
+ which case it just adds insult to injury (one confusing
+ error message after another. Besides which, does the
+ user really care if we can't restore the previously
+ selected frame? */
+ fprintf (stderr, "Unable to restore previously selected frame.\n");
+ select_frame (get_current_frame (), 0);
+ return;
+ }
+
+ select_frame (fid, inf_status->selected_level);
+ }
+ return;
+}
+
+
+void
+_initialize_infrun ()
+{
+ register int i;
+
+ add_info ("signals", signals_info,
+ "What debugger does when program gets various signals.\n\
+Specify a signal number as argument to print info on that signal only.");
+
+ add_com ("handle", class_run, handle_command,
+ "Specify how to handle a signal.\n\
+Args are signal number followed by flags.\n\
+Flags allowed are \"stop\", \"print\", \"pass\",\n\
+ \"nostop\", \"noprint\" or \"nopass\".\n\
+Print means print a message if this signal happens.\n\
+Stop means reenter debugger if this signal happens (implies print).\n\
+Pass means let program see this signal; otherwise program doesn't know.\n\
+Pass and Stop may be combined.");
+
+ for (i = 0; i < NSIG; i++)
+ {
+ signal_stop[i] = 1;
+ signal_print[i] = 1;
+ signal_program[i] = 1;
+ }
+
+ /* Signals caused by debugger's own actions
+ should not be given to the program afterwards. */
+ signal_program[SIGTRAP] = 0;
+ signal_program[SIGINT] = 0;
+
+ /* Signals that are not errors should not normally enter the debugger. */
+#ifdef SIGALRM
+ signal_stop[SIGALRM] = 0;
+ signal_print[SIGALRM] = 0;
+#endif /* SIGALRM */
+#ifdef SIGVTALRM
+ signal_stop[SIGVTALRM] = 0;
+ signal_print[SIGVTALRM] = 0;
+#endif /* SIGVTALRM */
+#ifdef SIGPROF
+ signal_stop[SIGPROF] = 0;
+ signal_print[SIGPROF] = 0;
+#endif /* SIGPROF */
+#ifdef SIGCHLD
+ signal_stop[SIGCHLD] = 0;
+ signal_print[SIGCHLD] = 0;
+#endif /* SIGCHLD */
+#ifdef SIGCLD
+ signal_stop[SIGCLD] = 0;
+ signal_print[SIGCLD] = 0;
+#endif /* SIGCLD */
+#ifdef SIGIO
+ signal_stop[SIGIO] = 0;
+ signal_print[SIGIO] = 0;
+#endif /* SIGIO */
+#ifdef SIGURG
+ signal_stop[SIGURG] = 0;
+ signal_print[SIGURG] = 0;
+#endif /* SIGURG */
+}
+
diff --git a/gnu/usr.bin/gdb/kgdb_proto.h b/gnu/usr.bin/gdb/kgdb_proto.h
new file mode 100644
index 0000000..8bbd5be
--- /dev/null
+++ b/gnu/usr.bin/gdb/kgdb_proto.h
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Steven McCanne of Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kgdb_proto.h 6.3 (Berkeley) 5/8/91
+ *
+ *
+ * $Header: /home/cvs/386BSD/src/usr.bin/gdb/kgdb_proto.h,v 1.1.1.1 1993/06/12 14:52:25 rgrimes Exp $ (LBL)
+ */
+
+/*
+ * Message types.
+ */
+#define KGDB_MEM_R 0x01
+#define KGDB_MEM_W 0x02
+#define KGDB_REG_R 0x03
+#define KGDB_REG_W 0x04
+#define KGDB_CONT 0x05
+#define KGDB_STEP 0x06
+#define KGDB_KILL 0x07
+#define KGDB_SIGNAL 0x08
+#define KGDB_EXEC 0x09
+
+#define KGDB_CMD(x) ((x) & 0x0f)
+
+/*
+ * Message flags.
+ */
+#define KGDB_ACK 0x80
+#define KGDB_DELTA 0x40
+#define KGDB_MORE 0x20
+#define KGDB_SEQ 0x10
diff --git a/gnu/usr.bin/gdb/libiberty/COPYING.LIB b/gnu/usr.bin/gdb/libiberty/COPYING.LIB
new file mode 100644
index 0000000..eb685a5
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/COPYING.LIB
@@ -0,0 +1,481 @@
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/gnu/usr.bin/gdb/libiberty/Makefile b/gnu/usr.bin/gdb/libiberty/Makefile
new file mode 100644
index 0000000..8387812
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/Makefile
@@ -0,0 +1,13 @@
+LIB= iberty
+SRCS= argv.c basename.c concat.c cplus-dem.c fdmatch.c getopt.c \
+ getopt1.c ieee-float.c obstack.c spaces.c strerror.c strsignal.c \
+ xmalloc.c
+
+CFLAGS+= -I$(.CURDIR)/../gdb/.
+NOPROFILE=no
+NOPIC=no
+
+install:
+ @echo -n
+
+.include <bsd.lib.mk>
diff --git a/gnu/usr.bin/gdb/libiberty/README.FreeBSD b/gnu/usr.bin/gdb/libiberty/README.FreeBSD
new file mode 100644
index 0000000..573fa0a
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/README.FreeBSD
@@ -0,0 +1,7 @@
+This is a greatly pared down libiberty directory. Only what's required to build
+gdb-4.12 on FreeBSD was kept.
+
+This is temporary. In FreeBSD 2.0 a fully ported libiberty will likely appear
+as a system library for use by all the build tools.
+
+paul@freefall.cdrom.com
diff --git a/gnu/usr.bin/gdb/libiberty/alloca-conf.h b/gnu/usr.bin/gdb/libiberty/alloca-conf.h
new file mode 100644
index 0000000..e1d9177
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/alloca-conf.h
@@ -0,0 +1,11 @@
+/* "Normal" configuration for alloca. */
+
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not __GNUC__ */
+#ifdef sparc
+#include <alloca.h>
+#else
+char *alloca ();
+#endif /* sparc */
+#endif /* not __GNUC__ */
diff --git a/gnu/usr.bin/gdb/libiberty/argv.c b/gnu/usr.bin/gdb/libiberty/argv.c
new file mode 100644
index 0000000..fed46e7
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/argv.c
@@ -0,0 +1,332 @@
+/* Create and destroy argument vectors (argv's)
+ Copyright (C) 1992 Free Software Foundation, Inc.
+ Written by Fred Fish @ Cygnus Support
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+
+/* Create and destroy argument vectors. An argument vector is simply an
+ array of string pointers, terminated by a NULL pointer. */
+
+/* AIX requires this to be the first thing in the file. */
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not __GNUC__ */
+#ifdef sparc
+#include <alloca.h>
+extern char *__builtin_alloca(); /* Stupid include file doesn't declare it */
+#else
+#ifdef _AIX
+ #pragma alloca
+#else
+char *alloca ();
+#endif
+#endif /* sparc */
+#endif /* not __GNUC__ */
+
+#define isspace(ch) ((ch) == ' ' || (ch) == '\t')
+
+#include "alloca-conf.h"
+
+/* Routines imported from standard C runtime libraries. */
+
+#ifdef __STDC__
+
+#include <stddef.h>
+extern void *memcpy (void *s1, const void *s2, size_t n); /* 4.11.2.1 */
+extern size_t strlen (const char *s); /* 4.11.6.3 */
+extern void *malloc (size_t size); /* 4.10.3.3 */
+extern void *realloc (void *ptr, size_t size); /* 4.10.3.4 */
+extern void free (void *ptr); /* 4.10.3.2 */
+extern char *strdup (const char *s); /* Non-ANSI */
+
+#else /* !__STDC__ */
+
+extern char *memcpy (); /* Copy memory region */
+extern int strlen (); /* Count length of string */
+extern char *malloc (); /* Standard memory allocater */
+extern char *realloc (); /* Standard memory reallocator */
+extern void free (); /* Free malloc'd memory */
+extern char *strdup (); /* Duplicate a string */
+
+#endif /* __STDC__ */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef EOS
+#define EOS '\0'
+#endif
+
+#define INITIAL_MAXARGC 8 /* Number of args + NULL in initial argv */
+
+
+/*
+
+NAME
+
+ freeargv -- free an argument vector
+
+SYNOPSIS
+
+ void freeargv (vector)
+ char **vector;
+
+DESCRIPTION
+
+ Free an argument vector that was built using buildargv. Simply scans
+ through the vector, freeing the memory for each argument until the
+ terminating NULL is found, and then frees the vector itself.
+
+RETURNS
+
+ No value.
+
+*/
+
+void freeargv (vector)
+char **vector;
+{
+ register char **scan;
+
+ if (vector != NULL)
+ {
+ for (scan = vector; *scan != NULL; scan++)
+ {
+ free (*scan);
+ }
+ free (vector);
+ }
+}
+
+/*
+
+NAME
+
+ buildargv -- build an argument vector from a string
+
+SYNOPSIS
+
+ char **buildargv (sp)
+ char *sp;
+
+DESCRIPTION
+
+ Given a pointer to a string, parse the string extracting fields
+ separated by whitespace and optionally enclosed within either single
+ or double quotes (which are stripped off), and build a vector of
+ pointers to copies of the string for each field. The input string
+ remains unchanged.
+
+ All of the memory for the pointer array and copies of the string
+ is obtained from malloc. All of the memory can be returned to the
+ system with the single function call freeargv, which takes the
+ returned result of buildargv, as it's argument.
+
+ The memory for the argv array is dynamically expanded as necessary.
+
+RETURNS
+
+ Returns a pointer to the argument vector if successful. Returns NULL
+ if the input string pointer is NULL or if there is insufficient
+ memory to complete building the argument vector.
+
+NOTES
+
+ In order to provide a working buffer for extracting arguments into,
+ with appropriate stripping of quotes and translation of backslash
+ sequences, we allocate a working buffer at least as long as the input
+ string. This ensures that we always have enough space in which to
+ work, since the extracted arg is never larger than the input string.
+
+ If the input is a null string (as opposed to a NULL pointer), then
+ buildarg returns an argv that has one arg, a null string.
+
+ Argv is always kept terminated with a NULL arg pointer, so it can
+ be passed to freeargv at any time, or returned, as appropriate.
+*/
+
+char **buildargv (input)
+char *input;
+{
+ char *arg;
+ char *copybuf;
+ int squote = 0;
+ int dquote = 0;
+ int bsquote = 0;
+ int argc = 0;
+ int maxargc = 0;
+ char **argv = NULL;
+ char **nargv;
+
+ if (input != NULL)
+ {
+ copybuf = alloca (strlen (input) + 1);
+ /* Is a do{}while to always execute the loop once. Always return an
+ argv, even for null strings. See NOTES above, test case below. */
+ do
+ {
+ /* Pick off argv[argc] */
+ while (isspace (*input))
+ {
+ input++;
+ }
+ if ((maxargc == 0) || (argc >= (maxargc - 1)))
+ {
+ /* argv needs initialization, or expansion */
+ if (argv == NULL)
+ {
+ maxargc = INITIAL_MAXARGC;
+ nargv = (char **) malloc (maxargc * sizeof (char *));
+ }
+ else
+ {
+ maxargc *= 2;
+ nargv = (char **) realloc (argv, maxargc * sizeof (char *));
+ }
+ if (nargv == NULL)
+ {
+ if (argv != NULL)
+ {
+ freeargv (argv);
+ argv = NULL;
+ }
+ break;
+ }
+ argv = nargv;
+ argv[argc] = NULL;
+ }
+ /* Begin scanning arg */
+ arg = copybuf;
+ while (*input != EOS)
+ {
+ if (isspace (*input) && !squote && !dquote && !bsquote)
+ {
+ break;
+ }
+ else
+ {
+ if (bsquote)
+ {
+ bsquote = 0;
+ *arg++ = *input;
+ }
+ else if (*input == '\\')
+ {
+ bsquote = 1;
+ }
+ else if (squote)
+ {
+ if (*input == '\'')
+ {
+ squote = 0;
+ }
+ else
+ {
+ *arg++ = *input;
+ }
+ }
+ else if (dquote)
+ {
+ if (*input == '"')
+ {
+ dquote = 0;
+ }
+ else
+ {
+ *arg++ = *input;
+ }
+ }
+ else
+ {
+ if (*input == '\'')
+ {
+ squote = 1;
+ }
+ else if (*input == '"')
+ {
+ dquote = 1;
+ }
+ else
+ {
+ *arg++ = *input;
+ }
+ }
+ input++;
+ }
+ }
+ *arg = EOS;
+ argv[argc] = strdup (copybuf);
+ if (argv[argc] == NULL)
+ {
+ freeargv (argv);
+ argv = NULL;
+ break;
+ }
+ argc++;
+ argv[argc] = NULL;
+ }
+ while (*input != EOS);
+ }
+ return (argv);
+}
+
+#ifdef MAIN
+
+/* Simple little test driver. */
+
+static char *tests[] =
+{
+ "a simple command line",
+ "arg 'foo' is single quoted",
+ "arg \"bar\" is double quoted",
+ "arg \"foo bar\" has embedded whitespace",
+ "arg 'Jack said \\'hi\\'' has single quotes",
+ "arg 'Jack said \\\"hi\\\"' has double quotes",
+ "a b c d e f g h i j k l m n o p q r s t u v w x y z 1 2 3 4 5 6 7 8 9",
+ "",
+ NULL
+};
+
+main ()
+{
+ char **argv;
+ char **test;
+ char **targs;
+
+ for (test = tests; *test != NULL; test++)
+ {
+ printf ("buildargv(\"%s\")\n", *test);
+ if ((argv = buildargv (*test)) == NULL)
+ {
+ printf ("failed!\n\n");
+ }
+ else
+ {
+ for (targs = argv; *targs != NULL; targs++)
+ {
+ printf ("\t\"%s\"\n", *targs);
+ }
+ printf ("\n");
+ }
+ freeargv (argv);
+ }
+
+}
+
+#endif /* MAIN */
diff --git a/gnu/usr.bin/gdb/libiberty/basename.c b/gnu/usr.bin/gdb/libiberty/basename.c
new file mode 100644
index 0000000..f61a308
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/basename.c
@@ -0,0 +1,56 @@
+/* Return the basename of a pathname.
+ Copyright (C) 1991 Free Software Foundation, Inc.
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+/*
+
+NAME
+
+ basename -- return pointer to last component of a pathname
+
+SYNOPSIS
+
+ char *basename (char *name)
+
+DESCRIPTION
+
+ Given a pointer to a string containing a typical pathname
+ (/usr/src/cmd/ls/ls.c for example), returns a pointer to the
+ last component of the pathname ("ls.c" in this case).
+
+BUGS
+
+ Presumes a UNIX style path with UNIX style separators.
+*/
+
+
+char *
+basename (name)
+ char *name;
+{
+ char *base = name;
+
+ while (*name)
+ {
+ if (*name++ == '/')
+ {
+ base = name;
+ }
+ }
+ return (base);
+}
diff --git a/gnu/usr.bin/gdb/libiberty/concat.c b/gnu/usr.bin/gdb/libiberty/concat.c
new file mode 100644
index 0000000..61f7d97
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/concat.c
@@ -0,0 +1,118 @@
+/* Concatenate variable number of strings.
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ Written by Fred Fish @ Cygnus Support
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+
+/*
+
+NAME
+
+ concat -- concatenate a variable number of strings
+
+SYNOPSIS
+
+ #include <varargs.h>
+
+ char *concat (s1, s2, s3, ..., NULL)
+
+DESCRIPTION
+
+ Concatenate a variable number of strings and return the result
+ in freshly malloc'd memory.
+
+ Returns NULL if insufficient memory is available. The argument
+ list is terminated by the first NULL pointer encountered. Pointers
+ to empty strings are ignored.
+
+NOTES
+
+ This function uses xmalloc() which is expected to be a front end
+ function to malloc() that deals with low memory situations. In
+ typical use, if malloc() returns NULL then xmalloc() diverts to an
+ error handler routine which never returns, and thus xmalloc will
+ never return a NULL pointer. If the client application wishes to
+ deal with low memory situations itself, it should supply an xmalloc
+ that just directly invokes malloc and blindly returns whatever
+ malloc returns.
+*/
+
+
+#include <varargs.h>
+
+#define NULLP (char *)0
+
+extern char *xmalloc ();
+
+/* VARARGS */
+char *
+concat (va_alist)
+ va_dcl
+{
+ register int length = 0;
+ register char *newstr;
+ register char *end;
+ register char *arg;
+ va_list args;
+
+ /* First compute the size of the result and get sufficient memory. */
+
+ va_start (args);
+ while ((arg = va_arg (args, char *)) != NULLP)
+ {
+ length += strlen (arg);
+ }
+ newstr = (char *) xmalloc (length + 1);
+ va_end (args);
+
+ /* Now copy the individual pieces to the result string. */
+
+ if (newstr != NULLP)
+ {
+ va_start (args);
+ end = newstr;
+ while ((arg = va_arg (args, char *)) != NULLP)
+ {
+ while (*arg)
+ {
+ *end++ = *arg++;
+ }
+ }
+ *end = '\000';
+ va_end (args);
+ }
+
+ return (newstr);
+}
+
+#ifdef MAIN
+
+/* Simple little test driver. */
+
+main ()
+{
+ printf ("\"\" = \"%s\"\n", concat (NULLP));
+ printf ("\"a\" = \"%s\"\n", concat ("a", NULLP));
+ printf ("\"ab\" = \"%s\"\n", concat ("a", "b", NULLP));
+ printf ("\"abc\" = \"%s\"\n", concat ("a", "b", "c", NULLP));
+ printf ("\"abcd\" = \"%s\"\n", concat ("ab", "cd", NULLP));
+ printf ("\"abcde\" = \"%s\"\n", concat ("ab", "c", "de", NULLP));
+ printf ("\"abcdef\" = \"%s\"\n", concat ("", "a", "", "bcd", "ef", NULLP));
+}
+
+#endif
diff --git a/gnu/usr.bin/gdb/libiberty/config.h b/gnu/usr.bin/gdb/libiberty/config.h
new file mode 100644
index 0000000..b37ee84
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/config.h
@@ -0,0 +1 @@
+/* !Automatically generated from ./functions.def - DO NOT EDIT! */
diff --git a/gnu/usr.bin/gdb/libiberty/cplus-dem.c b/gnu/usr.bin/gdb/libiberty/cplus-dem.c
new file mode 100644
index 0000000..2ad0e97
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/cplus-dem.c
@@ -0,0 +1,2669 @@
+/* Demangler for GNU C++
+ Copyright 1989, 1991 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+ Rewritten by Fred Fish (fnf@cygnus.com) for ARM and Lucid demangling
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+/* This file exports two functions; cplus_mangle_opname and cplus_demangle.
+
+ This file imports xmalloc and xrealloc, which are like malloc and
+ realloc except that they generate a fatal error if there is no
+ available memory. */
+
+#include <demangle.h>
+#undef CURRENT_DEMANGLING_STYLE
+#define CURRENT_DEMANGLING_STYLE work->options
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+
+extern char *xmalloc PARAMS((long));
+extern char *xrealloc PARAMS((PTR, long));
+extern char *strstr PARAMS ((const char *, const char *));
+extern void free PARAMS((PTR));
+
+/* In order to allow a single demangler executable to demangle strings
+ using various common values of CPLUS_MARKER, as well as any specific
+ one set at compile time, we maintain a string containing all the
+ commonly used ones, and check to see if the marker we are looking for
+ is in that string. CPLUS_MARKER is usually '$' on systems where the
+ assembler can deal with that. Where the assembler can't, it's usually
+ '.' (but on many systems '.' is used for other things). We put the
+ current defined CPLUS_MARKER first (which defaults to '$'), followed
+ by the next most common value, followed by an explicit '$' in case
+ the value of CPLUS_MARKER is not '$'.
+
+ We could avoid this if we could just get g++ to tell us what the actual
+ cplus marker character is as part of the debug information, perhaps by
+ ensuring that it is the character that terminates the gcc<n>_compiled
+ marker symbol (FIXME). */
+
+#if !defined (CPLUS_MARKER)
+#define CPLUS_MARKER '$'
+#endif
+
+enum demangling_styles current_demangling_style = gnu_demangling;
+
+static char cplus_markers[] = { CPLUS_MARKER, '.', '$', '\0' };
+
+void
+set_cplus_marker_for_demangling (ch)
+ int ch;
+{
+ cplus_markers[0] = ch;
+}
+
+/* Stuff that is shared between sub-routines.
+ * Using a shared structure allows cplus_demangle to be reentrant. */
+
+struct work_stuff
+{
+ int options;
+ char **typevec;
+ int ntypes;
+ int typevec_size;
+ int constructor;
+ int destructor;
+ int static_type; /* A static member function */
+ int const_type; /* A const member function */
+};
+
+#define PRINT_ANSI_QUALIFIERS (work -> options & DMGL_ANSI)
+#define PRINT_ARG_TYPES (work -> options & DMGL_PARAMS)
+
+static CONST struct optable
+{
+ CONST char *in;
+ CONST char *out;
+ int flags;
+} optable[] = {
+ {"nw", " new", DMGL_ANSI}, /* new (1.92, ansi) */
+ {"dl", " delete", DMGL_ANSI}, /* new (1.92, ansi) */
+ {"new", " new", 0}, /* old (1.91, and 1.x) */
+ {"delete", " delete", 0}, /* old (1.91, and 1.x) */
+ {"as", "=", DMGL_ANSI}, /* ansi */
+ {"ne", "!=", DMGL_ANSI}, /* old, ansi */
+ {"eq", "==", DMGL_ANSI}, /* old, ansi */
+ {"ge", ">=", DMGL_ANSI}, /* old, ansi */
+ {"gt", ">", DMGL_ANSI}, /* old, ansi */
+ {"le", "<=", DMGL_ANSI}, /* old, ansi */
+ {"lt", "<", DMGL_ANSI}, /* old, ansi */
+ {"plus", "+", 0}, /* old */
+ {"pl", "+", DMGL_ANSI}, /* ansi */
+ {"apl", "+=", DMGL_ANSI}, /* ansi */
+ {"minus", "-", 0}, /* old */
+ {"mi", "-", DMGL_ANSI}, /* ansi */
+ {"ami", "-=", DMGL_ANSI}, /* ansi */
+ {"mult", "*", 0}, /* old */
+ {"ml", "*", DMGL_ANSI}, /* ansi */
+ {"amu", "*=", DMGL_ANSI}, /* ansi (ARM/Lucid) */
+ {"aml", "*=", DMGL_ANSI}, /* ansi (GNU/g++) */
+ {"convert", "+", 0}, /* old (unary +) */
+ {"negate", "-", 0}, /* old (unary -) */
+ {"trunc_mod", "%", 0}, /* old */
+ {"md", "%", DMGL_ANSI}, /* ansi */
+ {"amd", "%=", DMGL_ANSI}, /* ansi */
+ {"trunc_div", "/", 0}, /* old */
+ {"dv", "/", DMGL_ANSI}, /* ansi */
+ {"adv", "/=", DMGL_ANSI}, /* ansi */
+ {"truth_andif", "&&", 0}, /* old */
+ {"aa", "&&", DMGL_ANSI}, /* ansi */
+ {"truth_orif", "||", 0}, /* old */
+ {"oo", "||", DMGL_ANSI}, /* ansi */
+ {"truth_not", "!", 0}, /* old */
+ {"nt", "!", DMGL_ANSI}, /* ansi */
+ {"postincrement","++", 0}, /* old */
+ {"pp", "++", DMGL_ANSI}, /* ansi */
+ {"postdecrement","--", 0}, /* old */
+ {"mm", "--", DMGL_ANSI}, /* ansi */
+ {"bit_ior", "|", 0}, /* old */
+ {"or", "|", DMGL_ANSI}, /* ansi */
+ {"aor", "|=", DMGL_ANSI}, /* ansi */
+ {"bit_xor", "^", 0}, /* old */
+ {"er", "^", DMGL_ANSI}, /* ansi */
+ {"aer", "^=", DMGL_ANSI}, /* ansi */
+ {"bit_and", "&", 0}, /* old */
+ {"ad", "&", DMGL_ANSI}, /* ansi */
+ {"aad", "&=", DMGL_ANSI}, /* ansi */
+ {"bit_not", "~", 0}, /* old */
+ {"co", "~", DMGL_ANSI}, /* ansi */
+ {"call", "()", 0}, /* old */
+ {"cl", "()", DMGL_ANSI}, /* ansi */
+ {"alshift", "<<", 0}, /* old */
+ {"ls", "<<", DMGL_ANSI}, /* ansi */
+ {"als", "<<=", DMGL_ANSI}, /* ansi */
+ {"arshift", ">>", 0}, /* old */
+ {"rs", ">>", DMGL_ANSI}, /* ansi */
+ {"ars", ">>=", DMGL_ANSI}, /* ansi */
+ {"component", "->", 0}, /* old */
+ {"pt", "->", DMGL_ANSI}, /* ansi; Lucid C++ form */
+ {"rf", "->", DMGL_ANSI}, /* ansi; ARM/GNU form */
+ {"indirect", "*", 0}, /* old */
+ {"method_call", "->()", 0}, /* old */
+ {"addr", "&", 0}, /* old (unary &) */
+ {"array", "[]", 0}, /* old */
+ {"vc", "[]", DMGL_ANSI}, /* ansi */
+ {"compound", ", ", 0}, /* old */
+ {"cm", ", ", DMGL_ANSI}, /* ansi */
+ {"cond", "?:", 0}, /* old */
+ {"cn", "?:", DMGL_ANSI}, /* psuedo-ansi */
+ {"max", ">?", 0}, /* old */
+ {"mx", ">?", DMGL_ANSI}, /* psuedo-ansi */
+ {"min", "<?", 0}, /* old */
+ {"mn", "<?", DMGL_ANSI}, /* psuedo-ansi */
+ {"nop", "", 0}, /* old (for operator=) */
+ {"rm", "->*", DMGL_ANSI} /* ansi */
+};
+
+
+typedef struct string /* Beware: these aren't required to be */
+{ /* '\0' terminated. */
+ char *b; /* pointer to start of string */
+ char *p; /* pointer after last character */
+ char *e; /* pointer after end of allocated space */
+} string;
+
+#define STRING_EMPTY(str) ((str) -> b == (str) -> p)
+#define PREPEND_BLANK(str) {if (!STRING_EMPTY(str)) \
+ string_prepend(str, " ");}
+#define APPEND_BLANK(str) {if (!STRING_EMPTY(str)) \
+ string_append(str, " ");}
+
+#define ARM_VTABLE_STRING "__vtbl__" /* Lucid/ARM virtual table prefix */
+#define ARM_VTABLE_STRLEN 8 /* strlen (ARM_VTABLE_STRING) */
+
+/* Prototypes for local functions */
+
+static char *
+mop_up PARAMS ((struct work_stuff *, string *, int));
+
+#if 0
+static int
+demangle_method_args PARAMS ((struct work_stuff *work, CONST char **, string *));
+#endif
+
+static int
+demangle_template PARAMS ((struct work_stuff *work, CONST char **, string *,
+ string *));
+
+static int
+demangle_qualified PARAMS ((struct work_stuff *, CONST char **, string *,
+ int, int));
+
+static int
+demangle_class PARAMS ((struct work_stuff *, CONST char **, string *));
+
+static int
+demangle_fund_type PARAMS ((struct work_stuff *, CONST char **, string *));
+
+static int
+demangle_signature PARAMS ((struct work_stuff *, CONST char **, string *));
+
+static int
+demangle_prefix PARAMS ((struct work_stuff *, CONST char **, string *));
+
+static int
+gnu_special PARAMS ((struct work_stuff *, CONST char **, string *));
+
+static int
+arm_special PARAMS ((struct work_stuff *, CONST char **, string *));
+
+static void
+string_need PARAMS ((string *, int));
+
+static void
+string_delete PARAMS ((string *));
+
+static void
+string_init PARAMS ((string *));
+
+static void
+string_clear PARAMS ((string *));
+
+#if 0
+static int
+string_empty PARAMS ((string *));
+#endif
+
+static void
+string_append PARAMS ((string *, CONST char *));
+
+static void
+string_appends PARAMS ((string *, string *));
+
+static void
+string_appendn PARAMS ((string *, CONST char *, int));
+
+static void
+string_prepend PARAMS ((string *, CONST char *));
+
+static void
+string_prependn PARAMS ((string *, CONST char *, int));
+
+static int
+get_count PARAMS ((CONST char **, int *));
+
+static int
+consume_count PARAMS ((CONST char **));
+
+static int
+demangle_args PARAMS ((struct work_stuff *, CONST char **, string *));
+
+static int
+do_type PARAMS ((struct work_stuff *, CONST char **, string *));
+
+static int
+do_arg PARAMS ((struct work_stuff *, CONST char **, string *));
+
+static void
+demangle_function_name PARAMS ((struct work_stuff *, CONST char **, string *,
+ CONST char *));
+
+static void
+remember_type PARAMS ((struct work_stuff *, CONST char *, int));
+
+static void
+forget_types PARAMS ((struct work_stuff *));
+
+static void
+string_prepends PARAMS ((string *, string *));
+
+/* Translate count to integer, consuming tokens in the process.
+ Conversion terminates on the first non-digit character.
+ Trying to consume something that isn't a count results in
+ no consumption of input and a return of 0. */
+
+static int
+consume_count (type)
+ CONST char **type;
+{
+ int count = 0;
+
+ while (isdigit (**type))
+ {
+ count *= 10;
+ count += **type - '0';
+ (*type)++;
+ }
+ return (count);
+}
+
+/* Takes operator name as e.g. "++" and returns mangled
+ operator name (e.g. "postincrement_expr"), or NULL if not found.
+
+ If OPTIONS & DMGL_ANSI == 1, return the ANSI name;
+ if OPTIONS & DMGL_ANSI == 0, return the old GNU name. */
+
+char *
+cplus_mangle_opname (opname, options)
+ char *opname;
+ int options;
+{
+ int i;
+ int len;
+
+ len = strlen (opname);
+ for (i = 0; i < sizeof (optable) / sizeof (optable[0]); i++)
+ {
+ if (strlen (optable[i].out) == len
+ && (options & DMGL_ANSI) == (optable[i].flags & DMGL_ANSI)
+ && memcmp (optable[i].out, opname, len) == 0)
+ return ((char *)optable[i].in);
+ }
+ return (0);
+}
+
+/* check to see whether MANGLED can match TEXT in the first TEXT_LEN
+ characters. */
+
+int cplus_match (mangled, text, text_len)
+ CONST char *mangled;
+ char *text;
+ int text_len;
+{
+ if (strncmp (mangled, text, text_len) != 0) {
+ return(0); /* cannot match either */
+ } else {
+ return(1); /* matches mangled, may match demangled */
+ }
+}
+
+/* char *cplus_demangle (const char *name, int options)
+
+ If NAME is a mangled function name produced by GNU C++, then
+ a pointer to a malloced string giving a C++ representation
+ of the name will be returned; otherwise NULL will be returned.
+ It is the caller's responsibility to free the string which
+ is returned.
+
+ The OPTIONS arg may contain one or more of the following bits:
+
+ DMGL_ANSI ANSI qualifiers such as `const' and `void' are
+ included.
+ DMGL_PARAMS Function parameters are included.
+
+ For example,
+
+ cplus_demangle ("foo__1Ai", DMGL_PARAMS) => "A::foo(int)"
+ cplus_demangle ("foo__1Ai", DMGL_PARAMS | DMGL_ANSI) => "A::foo(int)"
+ cplus_demangle ("foo__1Ai", 0) => "A::foo"
+
+ cplus_demangle ("foo__1Afe", DMGL_PARAMS) => "A::foo(float,...)"
+ cplus_demangle ("foo__1Afe", DMGL_PARAMS | DMGL_ANSI)=> "A::foo(float,...)"
+ cplus_demangle ("foo__1Afe", 0) => "A::foo"
+
+ Note that any leading underscores, or other such characters prepended by
+ the compilation system, are presumed to have already been stripped from
+ TYPE. */
+
+char *
+cplus_demangle (mangled, options)
+ CONST char *mangled;
+ int options;
+{
+ string decl;
+ int success = 0;
+ struct work_stuff work[1];
+ char *demangled = NULL;
+
+ if ((mangled != NULL) && (*mangled != '\0'))
+ {
+ memset ((char *) work, 0, sizeof (work));
+ work -> options = options;
+ if ((work->options & DMGL_STYLE_MASK) == 0)
+ work->options |= (int)current_demangling_style & DMGL_STYLE_MASK;
+
+ string_init (&decl);
+
+ /* First check to see if gnu style demangling is active and if the
+ string to be demangled contains a CPLUS_MARKER. If so, attempt to
+ recognize one of the gnu special forms rather than looking for a
+ standard prefix. In particular, don't worry about whether there
+ is a "__" string in the mangled string. Consider "_$_5__foo" for
+ example. */
+
+ if ((AUTO_DEMANGLING || GNU_DEMANGLING))
+ {
+ success = gnu_special (work, &mangled, &decl);
+ }
+ if (!success)
+ {
+ success = demangle_prefix (work, &mangled, &decl);
+ }
+ if (success && (*mangled != '\0'))
+ {
+ success = demangle_signature (work, &mangled, &decl);
+ }
+ if (work->constructor == 2)
+ {
+ string_prepend(&decl, "global constructors keyed to ");
+ work->constructor = 0;
+ }
+ else if (work->destructor == 2)
+ {
+ string_prepend(&decl, "global destructors keyed to ");
+ work->destructor = 0;
+ }
+ demangled = mop_up (work, &decl, success);
+ }
+ return (demangled);
+}
+
+static char *
+mop_up (work, declp, success)
+ struct work_stuff *work;
+ string *declp;
+ int success;
+{
+ char *demangled = NULL;
+
+ /* Discard the remembered types, if any. */
+
+ forget_types (work);
+ if (work -> typevec != NULL)
+ {
+ free ((char *) work -> typevec);
+ }
+
+ /* If demangling was successful, ensure that the demangled string is null
+ terminated and return it. Otherwise, free the demangling decl. */
+
+ if (!success)
+ {
+ string_delete (declp);
+ }
+ else
+ {
+ string_appendn (declp, "", 1);
+ demangled = declp -> b;
+ }
+ return (demangled);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ demangle_signature -- demangle the signature part of a mangled name
+
+SYNOPSIS
+
+ static int
+ demangle_signature (struct work_stuff *work, const char **mangled,
+ string *declp);
+
+DESCRIPTION
+
+ Consume and demangle the signature portion of the mangled name.
+
+ DECLP is the string where demangled output is being built. At
+ entry it contains the demangled root name from the mangled name
+ prefix. I.E. either a demangled operator name or the root function
+ name. In some special cases, it may contain nothing.
+
+ *MANGLED points to the current unconsumed location in the mangled
+ name. As tokens are consumed and demangling is performed, the
+ pointer is updated to continuously point at the next token to
+ be consumed.
+
+ Demangling GNU style mangled names is nasty because there is no
+ explicit token that marks the start of the outermost function
+ argument list.
+*/
+
+static int
+demangle_signature (work, mangled, declp)
+ struct work_stuff *work;
+ CONST char **mangled;
+ string *declp;
+{
+ int success = 1;
+ int func_done = 0;
+ int expect_func = 0;
+ CONST char *oldmangled = NULL;
+ string trawname;
+ string tname;
+
+ while (success && (**mangled != '\0'))
+ {
+ switch (**mangled)
+ {
+ case 'Q':
+ oldmangled = *mangled;
+ success = demangle_qualified (work, mangled, declp, 1, 0);
+ if (success)
+ {
+ remember_type (work, oldmangled, *mangled - oldmangled);
+ }
+ if (AUTO_DEMANGLING || GNU_DEMANGLING)
+ {
+ expect_func = 1;
+ }
+ oldmangled = NULL;
+ break;
+
+ case 'S':
+ /* Static member function */
+ if (oldmangled == NULL)
+ {
+ oldmangled = *mangled;
+ }
+ (*mangled)++;
+ work -> static_type = 1;
+ break;
+
+ case 'C':
+ /* a const member function */
+ if (oldmangled == NULL)
+ {
+ oldmangled = *mangled;
+ }
+ (*mangled)++;
+ work -> const_type = 1;
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (oldmangled == NULL)
+ {
+ oldmangled = *mangled;
+ }
+ success = demangle_class (work, mangled, declp);
+ if (success)
+ {
+ remember_type (work, oldmangled, *mangled - oldmangled);
+ }
+ if (AUTO_DEMANGLING || GNU_DEMANGLING)
+ {
+ expect_func = 1;
+ }
+ oldmangled = NULL;
+ break;
+
+ case 'F':
+ /* Function */
+ /* ARM style demangling includes a specific 'F' character after
+ the class name. For GNU style, it is just implied. So we can
+ safely just consume any 'F' at this point and be compatible
+ with either style. */
+
+ oldmangled = NULL;
+ func_done = 1;
+ (*mangled)++;
+
+ /* For lucid/ARM style we have to forget any types we might
+ have remembered up to this point, since they were not argument
+ types. GNU style considers all types seen as available for
+ back references. See comment in demangle_args() */
+
+ if (LUCID_DEMANGLING || ARM_DEMANGLING)
+ {
+ forget_types (work);
+ }
+ success = demangle_args (work, mangled, declp);
+ break;
+
+ case 't':
+ /* G++ Template */
+ string_init(&trawname);
+ string_init(&tname);
+ success = demangle_template (work, mangled, &tname, &trawname);
+ string_append(&tname, "::");
+ string_prepends(declp, &tname);
+ if (work -> destructor & 1)
+ {
+ string_prepend (&trawname, "~");
+ string_appends (declp, &trawname);
+ work->destructor -= 1;
+ }
+ if ((work->constructor & 1) || (work->destructor & 1))
+ {
+ string_appends (declp, &trawname);
+ work->constructor -= 1;
+ }
+ string_delete(&trawname);
+ string_delete(&tname);
+ expect_func = 1;
+ break;
+
+ case '_':
+ /* At the outermost level, we cannot have a return type specified,
+ so if we run into another '_' at this point we are dealing with
+ a mangled name that is either bogus, or has been mangled by
+ some algorithm we don't know how to deal with. So just
+ reject the entire demangling. */
+ success = 0;
+ break;
+
+ default:
+ if (AUTO_DEMANGLING || GNU_DEMANGLING)
+ {
+ /* Assume we have stumbled onto the first outermost function
+ argument token, and start processing args. */
+ func_done = 1;
+ success = demangle_args (work, mangled, declp);
+ }
+ else
+ {
+ /* Non-GNU demanglers use a specific token to mark the start
+ of the outermost function argument tokens. Typically 'F',
+ for ARM-demangling, for example. So if we find something
+ we are not prepared for, it must be an error. */
+ success = 0;
+ }
+ break;
+ }
+ if (AUTO_DEMANGLING || GNU_DEMANGLING)
+ {
+ if (success && expect_func)
+ {
+ func_done = 1;
+ success = demangle_args (work, mangled, declp);
+ }
+ }
+ }
+ if (success && !func_done)
+ {
+ if (AUTO_DEMANGLING || GNU_DEMANGLING)
+ {
+ /* With GNU style demangling, bar__3foo is 'foo::bar(void)', and
+ bar__3fooi is 'foo::bar(int)'. We get here when we find the
+ first case, and need to ensure that the '(void)' gets added to
+ the current declp. Note that with ARM, the first case
+ represents the name of a static data member 'foo::bar',
+ which is in the current declp, so we leave it alone. */
+ success = demangle_args (work, mangled, declp);
+ }
+ }
+ if (success && work -> static_type && PRINT_ARG_TYPES)
+ {
+ string_append (declp, " static");
+ }
+ if (success && work -> const_type && PRINT_ARG_TYPES)
+ {
+ string_append (declp, " const");
+ }
+ return (success);
+}
+
+#if 0
+
+static int
+demangle_method_args (work, mangled, declp)
+ struct work_stuff *work;
+ CONST char **mangled;
+ string *declp;
+{
+ int success = 0;
+
+ if (work -> static_type)
+ {
+ string_append (declp, *mangled + 1);
+ *mangled += strlen (*mangled);
+ success = 1;
+ }
+ else
+ {
+ success = demangle_args (work, mangled, declp);
+ }
+ return (success);
+}
+
+#endif
+
+static int
+demangle_template (work, mangled, tname, trawname)
+ struct work_stuff *work;
+ CONST char **mangled;
+ string *tname;
+ string *trawname;
+{
+ int i;
+ int is_pointer;
+ int is_real;
+ int is_integral;
+ int r;
+ int need_comma = 0;
+ int success = 0;
+ int done;
+ CONST char *old_p;
+ CONST char *start;
+ int symbol_len;
+ string temp;
+
+ (*mangled)++;
+ start = *mangled;
+ /* get template name */
+ if ((r = consume_count (mangled)) == 0)
+ {
+ return (0);
+ }
+ if (trawname)
+ string_appendn (trawname, *mangled, r);
+ string_appendn (tname, *mangled, r);
+ *mangled += r;
+ string_append (tname, "<");
+ /* get size of template parameter list */
+ if (!get_count (mangled, &r))
+ {
+ return (0);
+ }
+ for (i = 0; i < r; i++)
+ {
+ if (need_comma)
+ {
+ string_append (tname, ", ");
+ }
+ /* Z for type parameters */
+ if (**mangled == 'Z')
+ {
+ (*mangled)++;
+ /* temp is initialized in do_type */
+ success = do_type (work, mangled, &temp);
+ if (success)
+ {
+ string_appends (tname, &temp);
+ }
+ string_delete(&temp);
+ if (!success)
+ {
+ break;
+ }
+ }
+ else
+ {
+ /* otherwise, value parameter */
+ old_p = *mangled;
+ is_pointer = 0;
+ is_real = 0;
+ is_integral = 0;
+ done = 0;
+ /* temp is initialized in do_type */
+ success = do_type (work, mangled, &temp);
+ if (success)
+ {
+ string_appends (tname, &temp);
+ }
+ string_delete(&temp);
+ if (!success)
+ {
+ break;
+ }
+ string_append (tname, "=");
+ while (*old_p && !done)
+ {
+ switch (*old_p)
+ {
+ case 'P':
+ case 'R':
+ done = is_pointer = 1;
+ break;
+ case 'C': /* const */
+ case 'S': /* explicitly signed [char] */
+ case 'U': /* unsigned */
+ case 'V': /* volatile */
+ case 'F': /* function */
+ case 'M': /* member function */
+ case 'O': /* ??? */
+ old_p++;
+ continue;
+ case 'Q': /* repetition of following */
+ case 'T': /* remembered type */
+ abort ();
+ break;
+ case 'v': /* void */
+ abort ();
+ break;
+ case 'x': /* long long */
+ case 'l': /* long */
+ case 'i': /* int */
+ case 's': /* short */
+ case 'c': /* char */
+ case 'w': /* wchar_t */
+ done = is_integral = 1;
+ break;
+ case 'r': /* long double */
+ case 'd': /* double */
+ case 'f': /* float */
+ done = is_real = 1;
+ break;
+ default:
+ abort ();
+ }
+ }
+ if (is_integral)
+ {
+ if (**mangled == 'm')
+ {
+ string_appendn (tname, "-", 1);
+ (*mangled)++;
+ }
+ while (isdigit (**mangled))
+ {
+ string_appendn (tname, *mangled, 1);
+ (*mangled)++;
+ }
+ }
+ else if (is_real)
+ {
+ if (**mangled == 'm')
+ {
+ string_appendn (tname, "-", 1);
+ (*mangled)++;
+ }
+ while (isdigit (**mangled))
+ {
+ string_appendn (tname, *mangled, 1);
+ (*mangled)++;
+ }
+ if (**mangled == '.') /* fraction */
+ {
+ string_appendn (tname, ".", 1);
+ (*mangled)++;
+ while (isdigit (**mangled))
+ {
+ string_appendn (tname, *mangled, 1);
+ (*mangled)++;
+ }
+ }
+ if (**mangled == 'e') /* exponent */
+ {
+ string_appendn (tname, "e", 1);
+ (*mangled)++;
+ while (isdigit (**mangled))
+ {
+ string_appendn (tname, *mangled, 1);
+ (*mangled)++;
+ }
+ }
+ }
+ else if (is_pointer)
+ {
+ if (!get_count (mangled, &symbol_len))
+ {
+ success = 0;
+ break;
+ }
+ string_appendn (tname, *mangled, symbol_len);
+ *mangled += symbol_len;
+ }
+ }
+ need_comma = 1;
+ }
+ string_append (tname, ">");
+
+/*
+ if (work -> static_type)
+ {
+ string_append (declp, *mangled + 1);
+ *mangled += strlen (*mangled);
+ success = 1;
+ }
+ else
+ {
+ success = demangle_args (work, mangled, declp);
+ }
+ }
+*/
+ return (success);
+}
+
+static int
+arm_pt (work, mangled, n, anchor, args)
+ struct work_stuff *work;
+ CONST char *mangled;
+ int n;
+ CONST char **anchor, **args;
+{
+ /* ARM template? */
+ if (ARM_DEMANGLING && (*anchor = strstr(mangled, "__pt__")))
+ {
+ int len;
+ *args = *anchor + 6;
+ len = consume_count (args);
+ if (*args + len == mangled + n && **args == '_')
+ {
+ ++*args;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+demangle_class_name (work, mangled, declp)
+ struct work_stuff *work;
+ CONST char **mangled;
+ string *declp;
+{
+ int n;
+ int success = 0;
+
+ n = consume_count (mangled);
+ if (strlen (*mangled) >= n)
+ {
+ CONST char *p;
+ CONST char *args;
+ CONST char *e = *mangled + n;
+ /* ARM template? */
+ if (arm_pt (work, *mangled, n, &p, &args))
+ {
+ string arg;
+ string_init (&arg);
+ string_appendn (declp, *mangled, p - *mangled);
+ string_append (declp, "<");
+ /* should do error checking here */
+ while (args < e) {
+ string_clear (&arg);
+ do_type (work, &args, &arg);
+ string_appends (declp, &arg);
+ string_append (declp, ",");
+ }
+ string_delete (&arg);
+ --declp->p;
+ string_append (declp, ">");
+ }
+ else
+ {
+ string_appendn (declp, *mangled, n);
+ }
+ *mangled += n;
+ success = 1;
+ }
+ return (success);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ demangle_class -- demangle a mangled class sequence
+
+SYNOPSIS
+
+ static int
+ demangle_class (struct work_stuff *work, const char **mangled,
+ strint *declp)
+
+DESCRIPTION
+
+ DECLP points to the buffer into which demangling is being done.
+
+ *MANGLED points to the current token to be demangled. On input,
+ it points to a mangled class (I.E. "3foo", "13verylongclass", etc.)
+ On exit, it points to the next token after the mangled class on
+ success, or the first unconsumed token on failure.
+
+ If the CONSTRUCTOR or DESTRUCTOR flags are set in WORK, then
+ we are demangling a constructor or destructor. In this case
+ we prepend "class::class" or "class::~class" to DECLP.
+
+ Otherwise, we prepend "class::" to the current DECLP.
+
+ Reset the constructor/destructor flags once they have been
+ "consumed". This allows demangle_class to be called later during
+ the same demangling, to do normal class demangling.
+
+ Returns 1 if demangling is successful, 0 otherwise.
+
+*/
+
+static int
+demangle_class (work, mangled, declp)
+ struct work_stuff *work;
+ CONST char **mangled;
+ string *declp;
+{
+ int success = 0;
+ string class_name;
+
+ string_init (&class_name);
+ if (demangle_class_name (work, mangled, &class_name))
+ {
+ if ((work->constructor & 1) || (work->destructor & 1))
+ {
+ string_prepends (declp, &class_name);
+ if (work -> destructor & 1)
+ {
+ string_prepend (declp, "~");
+ work -> destructor -= 1;
+ }
+ else
+ {
+ work -> constructor -= 1;
+ }
+ }
+ string_prepend (declp, "::");
+ string_prepends (declp, &class_name);
+ success = 1;
+ }
+ string_delete (&class_name);
+ return (success);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ demangle_prefix -- consume the mangled name prefix and find signature
+
+SYNOPSIS
+
+ static int
+ demangle_prefix (struct work_stuff *work, const char **mangled,
+ string *declp);
+
+DESCRIPTION
+
+ Consume and demangle the prefix of the mangled name.
+
+ DECLP points to the string buffer into which demangled output is
+ placed. On entry, the buffer is empty. On exit it contains
+ the root function name, the demangled operator name, or in some
+ special cases either nothing or the completely demangled result.
+
+ MANGLED points to the current pointer into the mangled name. As each
+ token of the mangled name is consumed, it is updated. Upon entry
+ the current mangled name pointer points to the first character of
+ the mangled name. Upon exit, it should point to the first character
+ of the signature if demangling was successful, or to the first
+ unconsumed character if demangling of the prefix was unsuccessful.
+
+ Returns 1 on success, 0 otherwise.
+ */
+
+static int
+demangle_prefix (work, mangled, declp)
+ struct work_stuff *work;
+ CONST char **mangled;
+ string *declp;
+{
+ int success = 1;
+ CONST char *scan;
+ int i;
+
+ if (strncmp(*mangled, "_GLOBAL_$D$", 11) == 0)
+ {
+ /* it's a GNU global destructor to be executed at program exit */
+ (*mangled) += 11;
+ work->destructor = 2;
+ }
+ else if (strncmp(*mangled, "_GLOBAL_$I$", 11) == 0)
+ {
+ /* it's a GNU global constructor to be executed at program initial */
+ (*mangled) += 11;
+ work->constructor = 2;
+ }
+ else if (ARM_DEMANGLING && strncmp(*mangled, "__std__", 7) == 0)
+ {
+ /* it's a ARM global destructor to be executed at program exit */
+ (*mangled) += 7;
+ work->destructor = 2;
+ }
+ else if (ARM_DEMANGLING && strncmp(*mangled, "__sti__", 7) == 0)
+ {
+ /* it's a ARM global constructor to be executed at program initial */
+ (*mangled) += 7;
+ work->constructor = 2;
+ }
+
+/* This block of code is a reduction in strength time optimization
+ of:
+ scan = strstr (*mangled, "__"); */
+
+ {
+ scan = *mangled;
+
+ do {
+ scan = strchr (scan, '_');
+ } while (scan != NULL && *++scan != '_');
+
+ if (scan != NULL) --scan;
+ }
+
+ if (scan != NULL)
+ {
+ /* We found a sequence of two or more '_', ensure that we start at
+ the last pair in the sequence. */
+ i = strspn (scan, "_");
+ if (i > 2)
+ {
+ scan += (i - 2);
+ }
+ }
+
+ if (scan == NULL)
+ {
+ success = 0;
+ }
+ else if (work -> static_type)
+ {
+ if (!isdigit (scan[0]) && (scan[0] != 't'))
+ {
+ success = 0;
+ }
+ }
+ else if ((scan == *mangled) &&
+ (isdigit (scan[2]) || (scan[2] == 'Q') || (scan[2] == 't')))
+ {
+ /* The ARM says nothing about the mangling of local variables.
+ But cfront mangles local variables by prepending __<nesting_level>
+ to them. As an extension to ARM demangling we handle this case. */
+ if ((LUCID_DEMANGLING || ARM_DEMANGLING) && isdigit (scan[2]))
+ {
+ *mangled = scan + 2;
+ consume_count (mangled);
+ string_append (declp, *mangled);
+ *mangled += strlen (*mangled);
+ success = 1;
+ }
+ else
+ {
+ /* A GNU style constructor starts with "__[0-9Qt]. */
+ work -> constructor += 1;
+ *mangled = scan + 2;
+ }
+ }
+ else if ((scan == *mangled) && !isdigit (scan[2]) && (scan[2] != 't'))
+ {
+ /* Mangled name starts with "__". Skip over any leading '_' characters,
+ then find the next "__" that separates the prefix from the signature.
+ */
+ if (!(ARM_DEMANGLING || LUCID_DEMANGLING)
+ || (arm_special (work, mangled, declp) == 0))
+ {
+ while (*scan == '_')
+ {
+ scan++;
+ }
+ if ((scan = strstr (scan, "__")) == NULL || (*(scan + 2) == '\0'))
+ {
+ /* No separator (I.E. "__not_mangled"), or empty signature
+ (I.E. "__not_mangled_either__") */
+ success = 0;
+ }
+ else
+ {
+ demangle_function_name (work, mangled, declp, scan);
+ }
+ }
+ }
+ else if (*(scan + 2) != '\0')
+ {
+ /* Mangled name does not start with "__" but does have one somewhere
+ in there with non empty stuff after it. Looks like a global
+ function name. */
+ demangle_function_name (work, mangled, declp, scan);
+ }
+ else
+ {
+ /* Doesn't look like a mangled name */
+ success = 0;
+ }
+
+ if (!success && (work->constructor == 2 || work->destructor == 2))
+ {
+ string_append (declp, *mangled);
+ *mangled += strlen (*mangled);
+ success = 1;
+ }
+ return (success);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ gnu_special -- special handling of gnu mangled strings
+
+SYNOPSIS
+
+ static int
+ gnu_special (struct work_stuff *work, const char **mangled,
+ string *declp);
+
+
+DESCRIPTION
+
+ Process some special GNU style mangling forms that don't fit
+ the normal pattern. For example:
+
+ _$_3foo (destructor for class foo)
+ _vt$foo (foo virtual table)
+ _vt$foo$bar (foo::bar virtual table)
+ _3foo$varname (static data member)
+ _Q22rs2tu$vw (static data member)
+ __t6vector1Zii (constructor with template)
+ */
+
+static int
+gnu_special (work, mangled, declp)
+ struct work_stuff *work;
+ CONST char **mangled;
+ string *declp;
+{
+ int n;
+ int success = 1;
+ CONST char *p;
+
+ if ((*mangled)[0] == '_'
+ && strchr (cplus_markers, (*mangled)[1]) != NULL
+ && (*mangled)[2] == '_')
+ {
+ /* Found a GNU style destructor, get past "_<CPLUS_MARKER>_" */
+ (*mangled) += 3;
+ work -> destructor += 1;
+ }
+ else if ((*mangled)[0] == '_'
+ && (*mangled)[1] == 'v'
+ && (*mangled)[2] == 't'
+ && strchr (cplus_markers, (*mangled)[3]) != NULL)
+ {
+ /* Found a GNU style virtual table, get past "_vt<CPLUS_MARKER>"
+ and create the decl. Note that we consume the entire mangled
+ input string, which means that demangle_signature has no work
+ to do. */
+ (*mangled) += 4;
+ while (**mangled != '\0')
+ {
+ if (isdigit(*mangled[0]))
+ {
+ n = consume_count(mangled);
+ }
+ else
+ {
+ n = strcspn (*mangled, cplus_markers);
+ }
+ string_appendn (declp, *mangled, n);
+ (*mangled) += n;
+
+ if (**mangled != '\0')
+ {
+ string_append (declp, "::");
+ (*mangled)++;
+ }
+ }
+ string_append (declp, " virtual table");
+ }
+ else if ((*mangled)[0] == '_'
+ && (strchr("0123456789Qt", (*mangled)[1]) != NULL)
+ && (p = strpbrk (*mangled, cplus_markers)) != NULL)
+ {
+ /* static data member, "_3foo$varname" for example */
+ (*mangled)++;
+ switch (**mangled)
+ {
+ case 'Q':
+ success = demangle_qualified (work, mangled, declp, 0, 1);
+ break;
+ case 't':
+ success = demangle_template (work, mangled, declp, 0);
+ break;
+ default:
+ n = consume_count (mangled);
+ string_appendn (declp, *mangled, n);
+ (*mangled) += n;
+ }
+ if (success && (p == *mangled))
+ {
+ /* Consumed everything up to the cplus_marker, append the
+ variable name. */
+ (*mangled)++;
+ string_append (declp, "::");
+ n = strlen (*mangled);
+ string_appendn (declp, *mangled, n);
+ (*mangled) += n;
+ }
+ else
+ {
+ success = 0;
+ }
+ }
+ else
+ {
+ success = 0;
+ }
+ return (success);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ arm_special -- special handling of ARM/lucid mangled strings
+
+SYNOPSIS
+
+ static int
+ arm_special (struct work_stuff *work, const char **mangled,
+ string *declp);
+
+
+DESCRIPTION
+
+ Process some special ARM style mangling forms that don't fit
+ the normal pattern. For example:
+
+ __vtbl__3foo (foo virtual table)
+ __vtbl__3foo__3bar (bar::foo virtual table)
+
+ */
+
+static int
+arm_special (work, mangled, declp)
+ struct work_stuff *work;
+ CONST char **mangled;
+ string *declp;
+{
+ int n;
+ int success = 1;
+ CONST char *scan;
+
+ if (strncmp (*mangled, ARM_VTABLE_STRING, ARM_VTABLE_STRLEN) == 0)
+ {
+ /* Found a ARM style virtual table, get past ARM_VTABLE_STRING
+ and create the decl. Note that we consume the entire mangled
+ input string, which means that demangle_signature has no work
+ to do. */
+ scan = *mangled + ARM_VTABLE_STRLEN;
+ while (*scan != '\0') /* first check it can be demangled */
+ {
+ n = consume_count (&scan);
+ if (n==0)
+ {
+ return (0); /* no good */
+ }
+ scan += n;
+ if (scan[0] == '_' && scan[1] == '_')
+ {
+ scan += 2;
+ }
+ }
+ (*mangled) += ARM_VTABLE_STRLEN;
+ while (**mangled != '\0')
+ {
+ n = consume_count (mangled);
+ string_prependn (declp, *mangled, n);
+ (*mangled) += n;
+ if ((*mangled)[0] == '_' && (*mangled)[1] == '_')
+ {
+ string_prepend (declp, "::");
+ (*mangled) += 2;
+ }
+ }
+ string_append (declp, " virtual table");
+ }
+ else
+ {
+ success = 0;
+ }
+ return (success);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ demangle_qualified -- demangle 'Q' qualified name strings
+
+SYNOPSIS
+
+ static int
+ demangle_qualified (struct work_stuff *, const char *mangled,
+ string *result, int isfuncname, int append);
+
+DESCRIPTION
+
+ Demangle a qualified name, such as "Q25Outer5Inner" which is
+ the mangled form of "Outer::Inner". The demangled output is
+ prepended or appended to the result string according to the
+ state of the append flag.
+
+ If isfuncname is nonzero, then the qualified name we are building
+ is going to be used as a member function name, so if it is a
+ constructor or destructor function, append an appropriate
+ constructor or destructor name. I.E. for the above example,
+ the result for use as a constructor is "Outer::Inner::Inner"
+ and the result for use as a destructor is "Outer::Inner::~Inner".
+
+BUGS
+
+ Numeric conversion is ASCII dependent (FIXME).
+
+ */
+
+static int
+demangle_qualified (work, mangled, result, isfuncname, append)
+ struct work_stuff *work;
+ CONST char **mangled;
+ string *result;
+ int isfuncname;
+ int append;
+{
+ int qualifiers;
+ int namelength;
+ int success = 1;
+ CONST char *p;
+ char num[2];
+ string temp;
+
+ string_init (&temp);
+ switch ((*mangled)[1])
+ {
+ case '_':
+ /* GNU mangled name with more than 9 classes. The count is preceded
+ by an underscore (to distinguish it from the <= 9 case) and followed
+ by an underscore. */
+ p = *mangled + 2;
+ qualifiers = atoi (p);
+ if (!isdigit (*p) || *p == '0')
+ success = 0;
+
+ /* Skip the digits. */
+ while (isdigit (*p))
+ ++p;
+
+ if (*p != '_')
+ success = 0;
+
+ *mangled = p + 1;
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ /* The count is in a single digit. */
+ num[0] = (*mangled)[1];
+ num[1] = '\0';
+ qualifiers = atoi (num);
+
+ /* If there is an underscore after the digit, skip it. This is
+ said to be for ARM-qualified names, but the ARM makes no
+ mention of such an underscore. Perhaps cfront uses one. */
+ if ((*mangled)[2] == '_')
+ {
+ (*mangled)++;
+ }
+ (*mangled) += 2;
+ break;
+
+ case '0':
+ default:
+ success = 0;
+ }
+
+ if (!success)
+ return success;
+
+ /* Pick off the names and collect them in the temp buffer in the order
+ in which they are found, separated by '::'. */
+
+ while (qualifiers-- > 0)
+ {
+ if (*mangled[0] == 't')
+ {
+ success = demangle_template(work, mangled, &temp, 0);
+ if (!success) break;
+ }
+ else
+ {
+ namelength = consume_count (mangled);
+ if (strlen (*mangled) < namelength)
+ {
+ /* Simple sanity check failed */
+ success = 0;
+ break;
+ }
+ string_appendn (&temp, *mangled, namelength);
+ *mangled += namelength;
+ }
+ if (qualifiers > 0)
+ {
+ string_appendn (&temp, "::", 2);
+ }
+ }
+
+ /* If we are using the result as a function name, we need to append
+ the appropriate '::' separated constructor or destructor name.
+ We do this here because this is the most convenient place, where
+ we already have a pointer to the name and the length of the name. */
+
+ if (isfuncname && (work->constructor & 1 || work->destructor & 1))
+ {
+ string_appendn (&temp, "::", 2);
+ if (work -> destructor & 1)
+ {
+ string_append (&temp, "~");
+ }
+ string_appendn (&temp, (*mangled) - namelength, namelength);
+ }
+
+ /* Now either prepend the temp buffer to the result, or append it,
+ depending upon the state of the append flag. */
+
+ if (append)
+ {
+ string_appends (result, &temp);
+ }
+ else
+ {
+ if (!STRING_EMPTY (result))
+ {
+ string_appendn (&temp, "::", 2);
+ }
+ string_prepends (result, &temp);
+ }
+
+ string_delete (&temp);
+ return (success);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ get_count -- convert an ascii count to integer, consuming tokens
+
+SYNOPSIS
+
+ static int
+ get_count (const char **type, int *count)
+
+DESCRIPTION
+
+ Return 0 if no conversion is performed, 1 if a string is converted.
+*/
+
+static int
+get_count (type, count)
+ CONST char **type;
+ int *count;
+{
+ CONST char *p;
+ int n;
+
+ if (!isdigit (**type))
+ {
+ return (0);
+ }
+ else
+ {
+ *count = **type - '0';
+ (*type)++;
+ if (isdigit (**type))
+ {
+ p = *type;
+ n = *count;
+ do
+ {
+ n *= 10;
+ n += *p - '0';
+ p++;
+ }
+ while (isdigit (*p));
+ if (*p == '_')
+ {
+ *type = p + 1;
+ *count = n;
+ }
+ }
+ }
+ return (1);
+}
+
+/* result will be initialised here; it will be freed on failure */
+
+static int
+do_type (work, mangled, result)
+ struct work_stuff *work;
+ CONST char **mangled;
+ string *result;
+{
+ int n;
+ int done;
+ int success;
+ string decl;
+ CONST char *remembered_type;
+ int constp;
+ int volatilep;
+
+ string_init (&decl);
+ string_init (result);
+
+ done = 0;
+ success = 1;
+ while (success && !done)
+ {
+ int member;
+ switch (**mangled)
+ {
+
+ /* A pointer type */
+ case 'P':
+ (*mangled)++;
+ string_prepend (&decl, "*");
+ break;
+
+ /* A reference type */
+ case 'R':
+ (*mangled)++;
+ string_prepend (&decl, "&");
+ break;
+
+ /* An array */
+ case 'A':
+ {
+ CONST char *p = ++(*mangled);
+
+ string_prepend (&decl, "(");
+ string_append (&decl, ")[");
+ /* Copy anything up until the next underscore (the size of the
+ array). */
+ while (**mangled && **mangled != '_')
+ ++(*mangled);
+ if (**mangled == '_')
+ {
+ string_appendn (&decl, p, *mangled - p);
+ string_append (&decl, "]");
+ *mangled += 1;
+ }
+ else
+ success = 0;
+ break;
+ }
+
+ /* A back reference to a previously seen type */
+ case 'T':
+ (*mangled)++;
+ if (!get_count (mangled, &n) || n >= work -> ntypes)
+ {
+ success = 0;
+ }
+ else
+ {
+ remembered_type = work -> typevec[n];
+ mangled = &remembered_type;
+ }
+ break;
+
+ /* A function */
+ case 'F':
+ (*mangled)++;
+ if (!STRING_EMPTY (&decl) && decl.b[0] == '*')
+ {
+ string_prepend (&decl, "(");
+ string_append (&decl, ")");
+ }
+ /* After picking off the function args, we expect to either find the
+ function return type (preceded by an '_') or the end of the
+ string. */
+ if (!demangle_args (work, mangled, &decl)
+ || (**mangled != '_' && **mangled != '\0'))
+ {
+ success = 0;
+ }
+ if (success && (**mangled == '_'))
+ {
+ (*mangled)++;
+ }
+ break;
+
+ case 'M':
+ case 'O':
+ {
+ constp = 0;
+ volatilep = 0;
+
+ member = **mangled == 'M';
+ (*mangled)++;
+ if (!isdigit (**mangled))
+ {
+ success = 0;
+ break;
+ }
+ n = consume_count (mangled);
+ if (strlen (*mangled) < n)
+ {
+ success = 0;
+ break;
+ }
+ string_append (&decl, ")");
+ string_prepend (&decl, "::");
+ string_prependn (&decl, *mangled, n);
+ string_prepend (&decl, "(");
+ *mangled += n;
+ if (member)
+ {
+ if (**mangled == 'C')
+ {
+ (*mangled)++;
+ constp = 1;
+ }
+ if (**mangled == 'V')
+ {
+ (*mangled)++;
+ volatilep = 1;
+ }
+ if (*(*mangled)++ != 'F')
+ {
+ success = 0;
+ break;
+ }
+ }
+ if ((member && !demangle_args (work, mangled, &decl))
+ || **mangled != '_')
+ {
+ success = 0;
+ break;
+ }
+ (*mangled)++;
+ if (! PRINT_ANSI_QUALIFIERS)
+ {
+ break;
+ }
+ if (constp)
+ {
+ APPEND_BLANK (&decl);
+ string_append (&decl, "const");
+ }
+ if (volatilep)
+ {
+ APPEND_BLANK (&decl);
+ string_append (&decl, "volatile");
+ }
+ break;
+ }
+ case 'G':
+ (*mangled)++;
+ break;
+
+ case 'C':
+ (*mangled)++;
+/*
+ if ((*mangled)[1] == 'P')
+ {
+*/
+ if (PRINT_ANSI_QUALIFIERS)
+ {
+ if (!STRING_EMPTY (&decl))
+ {
+ string_prepend (&decl, " ");
+ }
+ string_prepend (&decl, "const");
+ }
+ break;
+/*
+ }
+*/
+
+ /* fall through */
+ default:
+ done = 1;
+ break;
+ }
+ }
+
+ switch (**mangled)
+ {
+ /* A qualified name, such as "Outer::Inner". */
+ case 'Q':
+ success = demangle_qualified (work, mangled, result, 0, 1);
+ break;
+
+ default:
+ success = demangle_fund_type (work, mangled, result);
+ break;
+ }
+
+ if (success)
+ {
+ if (!STRING_EMPTY (&decl))
+ {
+ string_append (result, " ");
+ string_appends (result, &decl);
+ }
+ }
+ else
+ {
+ string_delete (result);
+ }
+ string_delete (&decl);
+ return (success);
+}
+
+/* Given a pointer to a type string that represents a fundamental type
+ argument (int, long, unsigned int, etc) in TYPE, a pointer to the
+ string in which the demangled output is being built in RESULT, and
+ the WORK structure, decode the types and add them to the result.
+
+ For example:
+
+ "Ci" => "const int"
+ "Sl" => "signed long"
+ "CUs" => "const unsigned short"
+
+ */
+
+static int
+demangle_fund_type (work, mangled, result)
+ struct work_stuff *work;
+ CONST char **mangled;
+ string *result;
+{
+ int done = 0;
+ int success = 1;
+
+ /* First pick off any type qualifiers. There can be more than one. */
+
+ while (!done)
+ {
+ switch (**mangled)
+ {
+ case 'C':
+ (*mangled)++;
+ if (PRINT_ANSI_QUALIFIERS)
+ {
+ APPEND_BLANK (result);
+ string_append (result, "const");
+ }
+ break;
+ case 'U':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "unsigned");
+ break;
+ case 'S': /* signed char only */
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "signed");
+ break;
+ case 'V':
+ (*mangled)++;
+ if (PRINT_ANSI_QUALIFIERS)
+ {
+ APPEND_BLANK (result);
+ string_append (result, "volatile");
+ }
+ break;
+ default:
+ done = 1;
+ break;
+ }
+ }
+
+ /* Now pick off the fundamental type. There can be only one. */
+
+ switch (**mangled)
+ {
+ case '\0':
+ case '_':
+ break;
+ case 'v':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "void");
+ break;
+ case 'x':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "long long");
+ break;
+ case 'l':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "long");
+ break;
+ case 'i':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "int");
+ break;
+ case 's':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "short");
+ break;
+ case 'c':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "char");
+ break;
+ case 'w':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "wchar_t");
+ break;
+ case 'r':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "long double");
+ break;
+ case 'd':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "double");
+ break;
+ case 'f':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "float");
+ break;
+ case 'G':
+ (*mangled)++;
+ if (!isdigit (**mangled))
+ {
+ success = 0;
+ break;
+ }
+ /* fall through */
+ /* An explicit type, such as "6mytype" or "7integer" */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ APPEND_BLANK (result);
+ if (!demangle_class_name (work, mangled, result)) {
+ --result->p;
+ success = 0;
+ }
+ break;
+ case 't':
+ success = demangle_template(work,mangled, result, 0);
+ break;
+ default:
+ success = 0;
+ break;
+ }
+
+ return (success);
+}
+
+/* `result' will be initialized in do_type; it will be freed on failure */
+
+static int
+do_arg (work, mangled, result)
+ struct work_stuff *work;
+ CONST char **mangled;
+ string *result;
+{
+ CONST char *start = *mangled;
+
+ if (!do_type (work, mangled, result))
+ {
+ return (0);
+ }
+ else
+ {
+ remember_type (work, start, *mangled - start);
+ return (1);
+ }
+}
+
+static void
+remember_type (work, start, len)
+ struct work_stuff *work;
+ CONST char *start;
+ int len;
+{
+ char *tem;
+
+ if (work -> ntypes >= work -> typevec_size)
+ {
+ if (work -> typevec_size == 0)
+ {
+ work -> typevec_size = 3;
+ work -> typevec =
+ (char **) xmalloc (sizeof (char *) * work -> typevec_size);
+ }
+ else
+ {
+ work -> typevec_size *= 2;
+ work -> typevec =
+ (char **) xrealloc ((char *)work -> typevec,
+ sizeof (char *) * work -> typevec_size);
+ }
+ }
+ tem = xmalloc (len + 1);
+ memcpy (tem, start, len);
+ tem[len] = '\0';
+ work -> typevec[work -> ntypes++] = tem;
+}
+
+/* Forget the remembered types, but not the type vector itself. */
+
+static void
+forget_types (work)
+ struct work_stuff *work;
+{
+ int i;
+
+ while (work -> ntypes > 0)
+ {
+ i = --(work -> ntypes);
+ if (work -> typevec[i] != NULL)
+ {
+ free (work -> typevec[i]);
+ work -> typevec[i] = NULL;
+ }
+ }
+}
+
+/* Process the argument list part of the signature, after any class spec
+ has been consumed, as well as the first 'F' character (if any). For
+ example:
+
+ "__als__3fooRT0" => process "RT0"
+ "complexfunc5__FPFPc_PFl_i" => process "PFPc_PFl_i"
+
+ DECLP must be already initialised, usually non-empty. It won't be freed
+ on failure.
+
+ Note that g++ differs significantly from ARM and lucid style mangling
+ with regards to references to previously seen types. For example, given
+ the source fragment:
+
+ class foo {
+ public:
+ foo::foo (int, foo &ia, int, foo &ib, int, foo &ic);
+ };
+
+ foo::foo (int, foo &ia, int, foo &ib, int, foo &ic) { ia = ib = ic; }
+ void foo (int, foo &ia, int, foo &ib, int, foo &ic) { ia = ib = ic; }
+
+ g++ produces the names:
+
+ __3fooiRT0iT2iT2
+ foo__FiR3fooiT1iT1
+
+ while lcc (and presumably other ARM style compilers as well) produces:
+
+ foo__FiR3fooT1T2T1T2
+ __ct__3fooFiR3fooT1T2T1T2
+
+ Note that g++ bases it's type numbers starting at zero and counts all
+ previously seen types, while lucid/ARM bases it's type numbers starting
+ at one and only considers types after it has seen the 'F' character
+ indicating the start of the function args. For lucid/ARM style, we
+ account for this difference by discarding any previously seen types when
+ we see the 'F' character, and subtracting one from the type number
+ reference.
+
+ */
+
+static int
+demangle_args (work, mangled, declp)
+ struct work_stuff *work;
+ CONST char **mangled;
+ string *declp;
+{
+ string arg;
+ int need_comma = 0;
+ int r;
+ int t;
+ CONST char *tem;
+ char temptype;
+
+ if (PRINT_ARG_TYPES)
+ {
+ string_append (declp, "(");
+ if (**mangled == '\0')
+ {
+ string_append (declp, "void");
+ }
+ }
+
+ while (**mangled != '_' && **mangled != '\0' && **mangled != 'e')
+ {
+ if ((**mangled == 'N') || (**mangled == 'T'))
+ {
+ temptype = *(*mangled)++;
+
+ if (temptype == 'N')
+ {
+ if (!get_count (mangled, &r))
+ {
+ return (0);
+ }
+ }
+ else
+ {
+ r = 1;
+ }
+ if (!get_count (mangled, &t))
+ {
+ return (0);
+ }
+ if (LUCID_DEMANGLING || ARM_DEMANGLING)
+ {
+ t--;
+ }
+ /* Validate the type index. Protect against illegal indices from
+ malformed type strings. */
+ if ((t < 0) || (t >= work -> ntypes))
+ {
+ return (0);
+ }
+ while (--r >= 0)
+ {
+ tem = work -> typevec[t];
+ if (need_comma && PRINT_ARG_TYPES)
+ {
+ string_append (declp, ", ");
+ }
+ if (!do_arg (work, &tem, &arg))
+ {
+ return (0);
+ }
+ if (PRINT_ARG_TYPES)
+ {
+ string_appends (declp, &arg);
+ }
+ string_delete (&arg);
+ need_comma = 1;
+ }
+ }
+ else
+ {
+ if (need_comma & PRINT_ARG_TYPES)
+ {
+ string_append (declp, ", ");
+ }
+ if (!do_arg (work, mangled, &arg))
+ {
+ return (0);
+ }
+ if (PRINT_ARG_TYPES)
+ {
+ string_appends (declp, &arg);
+ }
+ string_delete (&arg);
+ need_comma = 1;
+ }
+ }
+
+ if (**mangled == 'e')
+ {
+ (*mangled)++;
+ if (PRINT_ARG_TYPES)
+ {
+ if (need_comma)
+ {
+ string_append (declp, ",");
+ }
+ string_append (declp, "...");
+ }
+ }
+
+ if (PRINT_ARG_TYPES)
+ {
+ string_append (declp, ")");
+ }
+ return (1);
+}
+
+static void
+demangle_function_name (work, mangled, declp, scan)
+ struct work_stuff *work;
+ CONST char **mangled;
+ string *declp;
+ CONST char *scan;
+{
+ int i;
+ int len;
+ string type;
+ CONST char *tem;
+
+ string_appendn (declp, (*mangled), scan - (*mangled));
+ string_need (declp, 1);
+ *(declp -> p) = '\0';
+
+ /* Consume the function name, including the "__" separating the name
+ from the signature. We are guaranteed that SCAN points to the
+ separator. */
+
+ (*mangled) = scan + 2;
+
+ if (LUCID_DEMANGLING || ARM_DEMANGLING)
+ {
+
+ /* See if we have an ARM style constructor or destructor operator.
+ If so, then just record it, clear the decl, and return.
+ We can't build the actual constructor/destructor decl until later,
+ when we recover the class name from the signature. */
+
+ if (strcmp (declp -> b, "__ct") == 0)
+ {
+ work -> constructor += 1;
+ string_clear (declp);
+ return;
+ }
+ else if (strcmp (declp -> b, "__dt") == 0)
+ {
+ work -> destructor += 1;
+ string_clear (declp);
+ return;
+ }
+ }
+
+ if (declp->p - declp->b >= 3
+ && declp->b[0] == 'o'
+ && declp->b[1] == 'p'
+ && strchr (cplus_markers, declp->b[2]) != NULL)
+ {
+ /* see if it's an assignment expression */
+ if (declp->p - declp->b >= 10 /* op$assign_ */
+ && memcmp (declp->b + 3, "assign_", 7) == 0)
+ {
+ for (i = 0; i < sizeof (optable) / sizeof (optable[0]); i++)
+ {
+ len = declp->p - declp->b - 10;
+ if (strlen (optable[i].in) == len
+ && memcmp (optable[i].in, declp->b + 10, len) == 0)
+ {
+ string_clear (declp);
+ string_append (declp, "operator");
+ string_append (declp, optable[i].out);
+ string_append (declp, "=");
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (i = 0; i < sizeof (optable) / sizeof (optable[0]); i++)
+ {
+ int len = declp->p - declp->b - 3;
+ if (strlen (optable[i].in) == len
+ && memcmp (optable[i].in, declp->b + 3, len) == 0)
+ {
+ string_clear (declp);
+ string_append (declp, "operator");
+ string_append (declp, optable[i].out);
+ break;
+ }
+ }
+ }
+ }
+ else if (declp->p - declp->b >= 5 && memcmp (declp->b, "type$", 5) == 0)
+ {
+ /* type conversion operator */
+ tem = declp->b + 5;
+ if (do_type (work, &tem, &type))
+ {
+ string_clear (declp);
+ string_append (declp, "operator ");
+ string_appends (declp, &type);
+ string_delete (&type);
+ }
+ }
+ else if (declp->b[0] == '_' && declp->b[1] == '_'
+ && declp->b[2] == 'o' && declp->b[3] == 'p')
+ {
+ /* ANSI. */
+ /* type conversion operator. */
+ tem = declp->b + 4;
+ if (do_type (work, &tem, &type))
+ {
+ string_clear (declp);
+ string_append (declp, "operator ");
+ string_appends (declp, &type);
+ string_delete (&type);
+ }
+ }
+ else if (declp->b[0] == '_' && declp->b[1] == '_'
+ && declp->b[2] >= 'a' && declp->b[2] <= 'z'
+ && declp->b[3] >= 'a' && declp->b[3] <= 'z')
+ {
+ if (declp->b[4] == '\0')
+ {
+ /* Operator. */
+ for (i = 0; i < sizeof (optable) / sizeof (optable[0]); i++)
+ {
+ if (strlen (optable[i].in) == 2
+ && memcmp (optable[i].in, declp->b + 2, 2) == 0)
+ {
+ string_clear (declp);
+ string_append (declp, "operator");
+ string_append (declp, optable[i].out);
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (declp->b[2] == 'a' && declp->b[5] == '\0')
+ {
+ /* Assignment. */
+ for (i = 0; i < sizeof (optable) / sizeof (optable[0]); i++)
+ {
+ if (strlen (optable[i].in) == 3
+ && memcmp (optable[i].in, declp->b + 2, 3) == 0)
+ {
+ string_clear (declp);
+ string_append (declp, "operator");
+ string_append (declp, optable[i].out);
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+/* a mini string-handling package */
+
+static void
+string_need (s, n)
+ string *s;
+ int n;
+{
+ int tem;
+
+ if (s->b == NULL)
+ {
+ if (n < 32)
+ {
+ n = 32;
+ }
+ s->p = s->b = xmalloc (n);
+ s->e = s->b + n;
+ }
+ else if (s->e - s->p < n)
+ {
+ tem = s->p - s->b;
+ n += tem;
+ n *= 2;
+ s->b = xrealloc (s->b, n);
+ s->p = s->b + tem;
+ s->e = s->b + n;
+ }
+}
+
+static void
+string_delete (s)
+ string *s;
+{
+ if (s->b != NULL)
+ {
+ free (s->b);
+ s->b = s->e = s->p = NULL;
+ }
+}
+
+static void
+string_init (s)
+ string *s;
+{
+ s->b = s->p = s->e = NULL;
+}
+
+static void
+string_clear (s)
+ string *s;
+{
+ s->p = s->b;
+}
+
+#if 0
+
+static int
+string_empty (s)
+ string *s;
+{
+ return (s->b == s->p);
+}
+
+#endif
+
+static void
+string_append (p, s)
+ string *p;
+ CONST char *s;
+{
+ int n;
+ if (s == NULL || *s == '\0')
+ return;
+ n = strlen (s);
+ string_need (p, n);
+ memcpy (p->p, s, n);
+ p->p += n;
+}
+
+static void
+string_appends (p, s)
+ string *p, *s;
+{
+ int n;
+
+ if (s->b != s->p)
+ {
+ n = s->p - s->b;
+ string_need (p, n);
+ memcpy (p->p, s->b, n);
+ p->p += n;
+ }
+}
+
+static void
+string_appendn (p, s, n)
+ string *p;
+ CONST char *s;
+ int n;
+{
+ if (n != 0)
+ {
+ string_need (p, n);
+ memcpy (p->p, s, n);
+ p->p += n;
+ }
+}
+
+static void
+string_prepend (p, s)
+ string *p;
+ CONST char *s;
+{
+ if (s != NULL && *s != '\0')
+ {
+ string_prependn (p, s, strlen (s));
+ }
+}
+
+static void
+string_prepends (p, s)
+ string *p, *s;
+{
+ if (s->b != s->p)
+ {
+ string_prependn (p, s->b, s->p - s->b);
+ }
+}
+
+static void
+string_prependn (p, s, n)
+ string *p;
+ CONST char *s;
+ int n;
+{
+ char *q;
+
+ if (n != 0)
+ {
+ string_need (p, n);
+ for (q = p->p - 1; q >= p->b; q--)
+ {
+ q[n] = q[0];
+ }
+ memcpy (p->b, s, n);
+ p->p += n;
+ }
+}
+
+/* To generate a standalone demangler program for testing purposes, just
+ compile and link this file with -DMAIN. When run, it demangles each
+ command line arg, or each stdin string, and prints the result on stdout. */
+
+#ifdef MAIN
+
+static void
+demangle_it (mangled_name)
+ char *mangled_name;
+{
+ char *result;
+
+ result = cplus_demangle (mangled_name, DMGL_PARAMS | DMGL_ANSI);
+ if (result == NULL)
+ {
+ printf ("%s\n", mangled_name);
+ }
+ else
+ {
+ printf ("%s\n", result);
+ free (result);
+ }
+}
+
+char *
+xmalloc (size)
+ long size;
+{
+ char * newmem;
+
+ if ((newmem = (char *) malloc ((int) size)) == NULL)
+ {
+ fprintf (stderr, "\nCan't allocate %u bytes\n", size);
+ exit (1);
+ }
+ return (newmem);
+}
+
+char *
+xrealloc (oldmem, size)
+ PTR oldmem;
+ long size;
+{
+ char * newmem;
+
+ if ((newmem = (char *) realloc ((char *) oldmem, (int) size)) == NULL)
+ {
+ fprintf (stderr, "\nCan't reallocate %u bytes\n", size);
+ exit (1);
+ }
+ return (newmem);
+}
+
+#include "getopt.h"
+
+static char *program_name;
+extern char *program_version;
+
+static void
+usage (stream, status)
+ FILE *stream;
+ int status;
+{
+ fprintf (stream, "\
+Usage: %s [-_] [-s {gnu,lucid,arm}] [--strip-underscores]\n\
+ [--format={gnu,lucid,arm}] [--help] [--version] [arg...]\n",
+ program_name);
+ exit (status);
+}
+
+#define MBUF_SIZE 512
+char mbuffer[MBUF_SIZE];
+
+/* Defined in the automatically-generated ../binutils/underscore.c. */
+extern int prepends_underscore;
+
+int strip_underscore = 0;
+
+static struct option long_options[] = {
+ {"strip-underscores", no_argument, 0, '_'},
+ {"format", required_argument, 0, 's'},
+ {"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'v'},
+ {0, no_argument, 0, 0}
+};
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ char *result;
+ int c;
+
+ program_name = argv[0];
+ strip_underscore = prepends_underscore;
+
+ while ((c = getopt_long (argc, argv, "_s:", long_options, (int *) 0)) != EOF)
+ {
+ switch (c)
+ {
+ case '?':
+ usage (stderr, 1);
+ break;
+ case 'h':
+ usage (stdout, 0);
+ case 'v':
+ printf ("GNU %s version %s\n", program_name, program_version);
+ exit (0);
+ case '_':
+ strip_underscore = 1;
+ break;
+ case 's':
+ if (strcmp (optarg, "gnu") == 0)
+ {
+ current_demangling_style = gnu_demangling;
+ }
+ else if (strcmp (optarg, "lucid") == 0)
+ {
+ current_demangling_style = lucid_demangling;
+ }
+ else if (strcmp (optarg, "arm") == 0)
+ {
+ current_demangling_style = arm_demangling;
+ }
+ else
+ {
+ fprintf (stderr, "%s: unknown demangling style `%s'\n",
+ program_name, optarg);
+ exit (1);
+ }
+ break;
+ }
+ }
+
+ if (optind < argc)
+ {
+ for ( ; optind < argc; optind++)
+ {
+ demangle_it (argv[optind]);
+ }
+ }
+ else
+ {
+ for (;;)
+ {
+ int i = 0;
+ c = getchar ();
+ /* Try to read a label. */
+ while (c != EOF && (isalnum(c) || c == '_' || c == '$' || c == '.'))
+ {
+ if (i >= MBUF_SIZE-1)
+ break;
+ mbuffer[i++] = c;
+ c = getchar ();
+ }
+ if (i > 0)
+ {
+ int skip_first = strip_underscore && i > 1 && mbuffer[0] == '_';
+ mbuffer[i] = 0;
+
+ result = cplus_demangle (mbuffer+skip_first,
+ DMGL_PARAMS | DMGL_ANSI);
+ if (result)
+ {
+ fputs (result, stdout);
+ free (result);
+ }
+ else
+ fputs (mbuffer + skip_first, stdout);
+ }
+ if (c == EOF)
+ break;
+ putchar (c);
+ }
+ }
+
+ exit (0);
+}
+
+#endif /* main */
diff --git a/gnu/usr.bin/gdb/libiberty/fdmatch.c b/gnu/usr.bin/gdb/libiberty/fdmatch.c
new file mode 100644
index 0000000..0a6de2a
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/fdmatch.c
@@ -0,0 +1,71 @@
+/* Compare two open file descriptors to see if they refer to the same file.
+ Copyright (C) 1991 Free Software Foundation, Inc.
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+
+/*
+
+NAME
+
+ fdmatch -- see if two file descriptors refer to same file
+
+SYNOPSIS
+
+ int fdmatch (int fd1, int fd2)
+
+DESCRIPTION
+
+ Check to see if two open file descriptors refer to the same file.
+ This is useful, for example, when we have an open file descriptor
+ for an unnamed file, and the name of a file that we believe to
+ correspond to that fd. This can happen when we are exec'd with
+ an already open file (stdout for example) or from the SVR4 /proc
+ calls that return open file descriptors for mapped address spaces.
+ All we have to do is open the file by name and check the two file
+ descriptors for a match, which is done by comparing major&minor
+ device numbers and inode numbers.
+
+BUGS
+
+ (FIXME: does this work for networks?)
+ It works for NFS, which assigns a device number to each mount.
+
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+int fdmatch (fd1, fd2)
+ int fd1;
+ int fd2;
+{
+ struct stat sbuf1;
+ struct stat sbuf2;
+
+ if ((fstat (fd1, &sbuf1) == 0) &&
+ (fstat (fd2, &sbuf2) == 0) &&
+ (sbuf1.st_dev == sbuf2.st_dev) &&
+ (sbuf1.st_ino == sbuf2.st_ino))
+ {
+ return (1);
+ }
+ else
+ {
+ return (0);
+ }
+}
diff --git a/gnu/usr.bin/gdb/libiberty/getopt.c b/gnu/usr.bin/gdb/libiberty/getopt.c
new file mode 100644
index 0000000..c7a8b03
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/getopt.c
@@ -0,0 +1,750 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License
+ as published by the Free Software Foundation; either version 2, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef __STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+#include <stdlib.h>
+#endif /* GNU C library. */
+
+/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
+ long-named option. Because this is not POSIX.2 compliant, it is
+ being phased out. */
+/* #define GETOPT_COMPAT */
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = 0;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* XXX 1003.2 says this must be 1 before any call. */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return EOF with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+#include <string.h>
+#define my_index strchr
+#else
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+char *getenv ();
+
+static char *
+my_index (str, chr)
+ const char *str;
+ int chr;
+{
+ while (*str)
+ {
+ if (*str == chr)
+ return (char *) str;
+ str++;
+ }
+ return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+ If not using GCC, it is ok not to declare it.
+ (Supposedly there are some machines where it might get a warning,
+ but changing this conditional to __STDC__ is too risky.) */
+#ifdef __GNUC__
+#ifdef IN_GCC
+#include "gstddef.h"
+#else
+#include <stddef.h>
+#endif
+extern size_t strlen (const char *);
+#endif
+
+#endif /* GNU C library. */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int bottom = first_nonopt;
+ int middle = last_nonopt;
+ int top = optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
+
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns `EOF'.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ int option_index;
+
+ optarg = 0;
+
+ /* Initialize the internal data when the first call is made.
+ Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ if (optind == 0)
+ {
+ first_nonopt = last_nonopt = optind = 1;
+
+ nextchar = NULL;
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (getenv ("POSIXLY_CORRECT") != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+ }
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Now skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc
+ && (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* Special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return EOF;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if ((argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ {
+ if (ordering == REQUIRE_ORDER)
+ return EOF;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Start decoding its characters. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ if (longopts != NULL
+ && ((argv[optind][0] == '-'
+ && (argv[optind][1] == '-' || long_only))
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ ))
+ {
+ const struct option *p;
+ char *s = nextchar;
+ int exact = 0;
+ int ambig = 0;
+ const struct option *pfound = NULL;
+ int indfound;
+
+ while (*s && *s != '=')
+ s++;
+
+ /* Test all options for either exact match or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name;
+ p++, option_index++)
+ if (!strncmp (p->name, nextchar, s - nextchar))
+ {
+ if (s - nextchar == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' is ambiguous\n",
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*s)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = s + 1;
+ else
+ {
+ if (opterr)
+ {
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ "%s: option `--%s' doesn't allow an argument\n",
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ "%s: option `%c%s' doesn't allow an argument\n",
+ argv[0], argv[optind - 1][0], pfound->name);
+ }
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' requires an argument\n",
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, "%s: unrecognized option `--%s'\n",
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+#if 0
+ if (c < 040 || c >= 0177)
+ fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
+ argv[0], c);
+ else
+ fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
+#endif
+ }
+ optopt = c;
+ return '?';
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = 0;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+#if 0
+ fprintf (stderr, "%s: option `-%c' requires an argument\n",
+ argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: option requires an argument -- %c\n",
+ argv[0], c);
+#endif
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/gnu/usr.bin/gdb/libiberty/getopt1.c b/gnu/usr.bin/gdb/libiberty/getopt1.c
new file mode 100644
index 0000000..6806da5
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/getopt1.c
@@ -0,0 +1,180 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License
+ as published by the Free Software Foundation; either version 2, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "getopt.h"
+
+#ifndef __STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#else
+char *getenv ();
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/gnu/usr.bin/gdb/libiberty/ieee-float.c b/gnu/usr.bin/gdb/libiberty/ieee-float.c
new file mode 100644
index 0000000..b50eb85
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/ieee-float.c
@@ -0,0 +1,150 @@
+/* IEEE floating point support routines, for GDB, the GNU Debugger.
+ Copyright (C) 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ieee-float.h"
+#include <math.h> /* ldexp */
+
+/* Convert an IEEE extended float to a double.
+ FROM is the address of the extended float.
+ Store the double in *TO. */
+
+void
+ieee_extended_to_double (ext_format, from, to)
+ CONST struct ext_format *ext_format;
+ char *from;
+ double *to;
+{
+ unsigned char *ufrom = (unsigned char *)from;
+ double dto;
+ unsigned long mant0, mant1, exponent;
+
+ memcpy (&mant0, &from[MANBYTE_H], 4);
+ memcpy (&mant1, &from[MANBYTE_L], 4);
+ exponent = ((ufrom[EXPBYTE_H] & (unsigned char)~SIGNMASK) << 8) | ufrom[EXPBYTE_L];
+
+#if 0
+ /* We can't do anything useful with a NaN anyway, so ignore its
+ difference. It will end up as Infinity or something close. */
+ if (exponent == EXT_EXP_NAN) {
+ /* We have a NaN source. */
+ dto = 0.123456789; /* Not much else useful to do -- we don't know if
+ the host system even *has* NaNs, nor how to
+ generate an innocuous one if it does. */
+ } else
+#endif
+ if (exponent == 0 && mant0 == 0 && mant1 == 0) {
+ dto = 0;
+ } else {
+ /* Build the result algebraically. Might go infinite, underflow, etc;
+ who cares. */
+ mant0 |= 0x80000000;
+ dto = ldexp ((double)mant0, exponent - EXT_EXP_BIAS - 31);
+ dto += ldexp ((double)mant1, exponent - EXT_EXP_BIAS - 31 - 32);
+ if (ufrom[EXPBYTE_H] & SIGNMASK) /* If negative... */
+ dto = -dto; /* ...negate. */
+ }
+ memcpy (to, &dto, sizeof (dto));
+}
+
+/* The converse: convert the double *FROM to an extended float
+ and store where TO points. Neither FROM nor TO have any alignment
+ restrictions. */
+
+void
+double_to_ieee_extended (ext_format, from, to)
+ CONST struct ext_format *ext_format;
+ double *from;
+ char *to;
+{
+ double dfrom;
+ unsigned long twolongs[2];
+ unsigned long mant0, mant1, exponent;
+ unsigned char tobytes[8];
+
+ memcpy (&dfrom, from, sizeof (dfrom));
+ memset (to, 0, TOTALSIZE);
+ if (dfrom == 0)
+ return; /* Result is zero */
+ if (dfrom != dfrom) {
+ /* From is NaN */
+ to[EXPBYTE_H] = (unsigned char)(EXT_EXP_NAN >> 8);
+ to[EXPBYTE_L] = (unsigned char)EXT_EXP_NAN;
+ to[MANBYTE_H] = 1; /* Be sure it's not infinity, but NaN value is irrel */
+ return; /* Result is NaN */
+ }
+ if (dfrom < 0)
+ to[SIGNBYTE] |= SIGNMASK; /* Set negative sign */
+ /* How to tell an infinity from an ordinary number? FIXME-someday */
+
+ /* The following code assumes that the host has IEEE doubles. FIXME-someday.
+ It also assumes longs are 32 bits! FIXME-someday. */
+ memcpy (twolongs, from, 8);
+ memcpy (tobytes, from, 8);
+#if HOST_BYTE_ORDER == BIG_ENDIAN
+ exponent = ((tobytes[1] & 0xF0) >> 4) | (tobytes[0] & 0x7F) << 4;
+ mant0 = (twolongs[0] << 11) | twolongs[1] >> 21;
+ mant1 = (twolongs[1] << 11);
+#else
+ exponent = ((tobytes[6] & 0xF0) >> 4) | (tobytes[7] & 0x7F) << 4;
+ mant0 = (twolongs[1] << 11) | twolongs[0] >> 21;
+ mant1 = (twolongs[0] << 11);
+#endif
+
+ /* Fiddle with leading 1-bit, implied in double, explicit in extended. */
+ if (exponent == 0)
+ mant0 &= 0x7FFFFFFF;
+ else
+ mant0 |= 0x80000000;
+
+ exponent -= DBL_EXP_BIAS; /* Get integer exp */
+ exponent += EXT_EXP_BIAS; /* Offset for extended */
+
+ /* OK, now store it in extended format. */
+ to[EXPBYTE_H] |= (unsigned char)(exponent >> 8); /* Retain sign */
+ to[EXPBYTE_L] = (unsigned char) exponent;
+
+ memcpy (&to[MANBYTE_H], &mant0, 4);
+ memcpy (&to[MANBYTE_L], &mant1, 4);
+}
+
+
+#ifdef IEEE_DEBUG
+
+/* Test some numbers to see that extended/double conversion works for them. */
+
+ieee_test (n)
+ int n;
+{
+ union { double d; int i[2]; } di;
+ double result;
+ int i;
+ char exten[16];
+ extern struct ext_format ext_format_68881;
+
+ for (i = 0; i < n; i++) {
+ di.i[0] = (random() << 16) | (random() & 0xffff);
+ di.i[1] = (random() << 16) | (random() & 0xffff);
+ double_to_ieee_extended (&ext_format_68881, &di.d, exten);
+ ieee_extended_to_double (&ext_format_68881, exten, &result);
+ if (di.d != result)
+ printf ("Differ: %x %x %g => %x %x %g\n", di.d, di.d, result, result);
+ }
+}
+
+#endif
diff --git a/gnu/usr.bin/gdb/libiberty/ieee-float.h b/gnu/usr.bin/gdb/libiberty/ieee-float.h
new file mode 100644
index 0000000..68ef23b
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/ieee-float.h
@@ -0,0 +1,65 @@
+/* IEEE floating point support declarations, for GDB, the GNU Debugger.
+ Copyright (C) 1991 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if !defined (IEEE_FLOAT_H)
+#define IEEE_FLOAT_H 1
+
+#include "ansidecl.h"
+
+/* Parameters for extended float format: */
+
+struct ext_format {
+ unsigned totalsize; /* Total size of extended number */
+ unsigned signbyte; /* Byte number of sign bit */
+ unsigned char signmask; /* Mask for sign bit */
+ unsigned expbyte_h; /* High byte of exponent */
+ unsigned expbyte_l; /* Low byte of exponent */
+ unsigned manbyte_h; /* High byte of mantissa */
+ unsigned manbyte_l; /* Low byte of mantissa */
+};
+
+#define TOTALSIZE ext_format->totalsize
+#define SIGNBYTE ext_format->signbyte
+#define SIGNMASK ext_format->signmask
+#define EXPBYTE_H ext_format->expbyte_h
+#define EXPBYTE_L ext_format->expbyte_l
+#define MANBYTE_H ext_format->manbyte_h
+#define MANBYTE_L ext_format->manbyte_l
+
+/* Actual ext_format structs for various machines are in the *-tdep.c file
+ for each machine. */
+
+#define EXT_EXP_NAN 0x7FFF /* Exponent value that indicates NaN */
+#define EXT_EXP_BIAS 0x3FFF /* Amount added to "true" exponent for ext */
+#define DBL_EXP_BIAS 0x3FF /* Ditto, for doubles */
+
+/* Convert an IEEE extended float to a double.
+ FROM is the address of the extended float.
+ Store the double in *TO. */
+
+extern void
+ieee_extended_to_double PARAMS ((const struct ext_format *, char *, double *));
+
+/* The converse: convert the double *FROM to an extended float
+ and store where TO points. */
+
+extern void
+double_to_ieee_extended PARAMS ((const struct ext_format *, double *, char *));
+
+#endif /* defined (IEEE_FLOAT_H) */
diff --git a/gnu/usr.bin/gdb/libiberty/obstack.c b/gnu/usr.bin/gdb/libiberty/obstack.c
new file mode 100644
index 0000000..4297bbb
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/obstack.c
@@ -0,0 +1,460 @@
+/* obstack.c - subroutines used implicitly by object stack macros
+ Copyright (C) 1988, 1993 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU Library General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public License
+along with this program; if not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "obstack.h"
+
+/* This is just to get __GNU_LIBRARY__ defined. */
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+#ifdef __STDC__
+#define POINTER void *
+#else
+#define POINTER char *
+#endif
+
+/* Determine default alignment. */
+struct fooalign {char x; double d;};
+#define DEFAULT_ALIGNMENT \
+ ((PTR_INT_TYPE) ((char *)&((struct fooalign *) 0)->d - (char *)0))
+/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT.
+ But in fact it might be less smart and round addresses to as much as
+ DEFAULT_ROUNDING. So we prepare for it to do that. */
+union fooround {long x; double d;};
+#define DEFAULT_ROUNDING (sizeof (union fooround))
+
+/* When we copy a long block of data, this is the unit to do it with.
+ On some machines, copying successive ints does not work;
+ in such a case, redefine COPYING_UNIT to `long' (if that works)
+ or `char' as a last resort. */
+#ifndef COPYING_UNIT
+#define COPYING_UNIT int
+#endif
+
+/* The non-GNU-C macros copy the obstack into this global variable
+ to avoid multiple evaluation. */
+
+struct obstack *_obstack;
+
+/* Define a macro that either calls functions with the traditional malloc/free
+ calling interface, or calls functions with the mmalloc/mfree interface
+ (that adds an extra first argument), based on the state of use_extra_arg.
+ For free, do not use ?:, since some compilers, like the MIPS compilers,
+ do not allow (expr) ? void : void. */
+
+#define CALL_CHUNKFUN(h, size) \
+ (((h) -> use_extra_arg) \
+ ? (*(h)->chunkfun) ((h)->extra_arg, (size)) \
+ : (*(h)->chunkfun) ((size)))
+
+#define CALL_FREEFUN(h, old_chunk) \
+ do { \
+ if ((h) -> use_extra_arg) \
+ (*(h)->freefun) ((h)->extra_arg, (old_chunk)); \
+ else \
+ (*(h)->freefun) ((old_chunk)); \
+ } while (0)
+
+
+/* Initialize an obstack H for use. Specify chunk size SIZE (0 means default).
+ Objects start on multiples of ALIGNMENT (0 means use default).
+ CHUNKFUN is the function to use to allocate chunks,
+ and FREEFUN the function to free them. */
+
+void
+_obstack_begin (h, size, alignment, chunkfun, freefun)
+ struct obstack *h;
+ int size;
+ int alignment;
+ POINTER (*chunkfun) ();
+ void (*freefun) ();
+{
+ register struct _obstack_chunk* chunk; /* points to new chunk */
+
+ if (alignment == 0)
+ alignment = DEFAULT_ALIGNMENT;
+ if (size == 0)
+ /* Default size is what GNU malloc can fit in a 4096-byte block. */
+ {
+ /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
+ Use the values for range checking, because if range checking is off,
+ the extra bytes won't be missed terribly, but if range checking is on
+ and we used a larger request, a whole extra 4096 bytes would be
+ allocated.
+
+ These number are irrelevant to the new GNU malloc. I suspect it is
+ less sensitive to the size of the request. */
+ int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
+ + 4 + DEFAULT_ROUNDING - 1)
+ & ~(DEFAULT_ROUNDING - 1));
+ size = 4096 - extra;
+ }
+
+ h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun;
+ h->freefun = freefun;
+ h->chunk_size = size;
+ h->alignment_mask = alignment - 1;
+ h->use_extra_arg = 0;
+
+ chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
+ h->next_free = h->object_base = chunk->contents;
+ h->chunk_limit = chunk->limit
+ = (char *) chunk + h->chunk_size;
+ chunk->prev = 0;
+ /* The initial chunk now contains no empty object. */
+ h->maybe_empty_object = 0;
+}
+
+void
+_obstack_begin_1 (h, size, alignment, chunkfun, freefun, arg)
+ struct obstack *h;
+ int size;
+ int alignment;
+ POINTER (*chunkfun) ();
+ void (*freefun) ();
+ POINTER arg;
+{
+ register struct _obstack_chunk* chunk; /* points to new chunk */
+
+ if (alignment == 0)
+ alignment = DEFAULT_ALIGNMENT;
+ if (size == 0)
+ /* Default size is what GNU malloc can fit in a 4096-byte block. */
+ {
+ /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
+ Use the values for range checking, because if range checking is off,
+ the extra bytes won't be missed terribly, but if range checking is on
+ and we used a larger request, a whole extra 4096 bytes would be
+ allocated.
+
+ These number are irrelevant to the new GNU malloc. I suspect it is
+ less sensitive to the size of the request. */
+ int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
+ + 4 + DEFAULT_ROUNDING - 1)
+ & ~(DEFAULT_ROUNDING - 1));
+ size = 4096 - extra;
+ }
+
+ h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun;
+ h->freefun = freefun;
+ h->chunk_size = size;
+ h->alignment_mask = alignment - 1;
+ h->extra_arg = arg;
+ h->use_extra_arg = 1;
+
+ chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
+ h->next_free = h->object_base = chunk->contents;
+ h->chunk_limit = chunk->limit
+ = (char *) chunk + h->chunk_size;
+ chunk->prev = 0;
+ /* The initial chunk now contains no empty object. */
+ h->maybe_empty_object = 0;
+}
+
+/* Allocate a new current chunk for the obstack *H
+ on the assumption that LENGTH bytes need to be added
+ to the current object, or a new object of length LENGTH allocated.
+ Copies any partial object from the end of the old chunk
+ to the beginning of the new one. */
+
+void
+_obstack_newchunk (h, length)
+ struct obstack *h;
+ int length;
+{
+ register struct _obstack_chunk* old_chunk = h->chunk;
+ register struct _obstack_chunk* new_chunk;
+ register long new_size;
+ register int obj_size = h->next_free - h->object_base;
+ register int i;
+ int already;
+
+ /* Compute size for new chunk. */
+ new_size = (obj_size + length) + (obj_size >> 3) + 100;
+ if (new_size < h->chunk_size)
+ new_size = h->chunk_size;
+
+ /* Allocate and initialize the new chunk. */
+ new_chunk = h->chunk = CALL_CHUNKFUN (h, new_size);
+ new_chunk->prev = old_chunk;
+ new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size;
+
+ /* Move the existing object to the new chunk.
+ Word at a time is fast and is safe if the object
+ is sufficiently aligned. */
+ if (h->alignment_mask + 1 >= DEFAULT_ALIGNMENT)
+ {
+ for (i = obj_size / sizeof (COPYING_UNIT) - 1;
+ i >= 0; i--)
+ ((COPYING_UNIT *)new_chunk->contents)[i]
+ = ((COPYING_UNIT *)h->object_base)[i];
+ /* We used to copy the odd few remaining bytes as one extra COPYING_UNIT,
+ but that can cross a page boundary on a machine
+ which does not do strict alignment for COPYING_UNITS. */
+ already = obj_size / sizeof (COPYING_UNIT) * sizeof (COPYING_UNIT);
+ }
+ else
+ already = 0;
+ /* Copy remaining bytes one by one. */
+ for (i = already; i < obj_size; i++)
+ new_chunk->contents[i] = h->object_base[i];
+
+ /* If the object just copied was the only data in OLD_CHUNK,
+ free that chunk and remove it from the chain.
+ But not if that chunk might contain an empty object. */
+ if (h->object_base == old_chunk->contents && ! h->maybe_empty_object)
+ {
+ new_chunk->prev = old_chunk->prev;
+ CALL_FREEFUN (h, old_chunk);
+ }
+
+ h->object_base = new_chunk->contents;
+ h->next_free = h->object_base + obj_size;
+ /* The new chunk certainly contains no empty object yet. */
+ h->maybe_empty_object = 0;
+}
+
+/* Return nonzero if object OBJ has been allocated from obstack H.
+ This is here for debugging.
+ If you use it in a program, you are probably losing. */
+
+#ifdef __STDC__
+/* Suppress -Wmissing-prototypes warning. We don't want to declare this in
+ obstack.h because it is just for debugging. */
+int _obstack_allocated_p (struct obstack *h, POINTER obj);
+#endif
+
+int
+_obstack_allocated_p (h, obj)
+ struct obstack *h;
+ POINTER obj;
+{
+ register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk* plp; /* point to previous chunk if any */
+
+ lp = (h)->chunk;
+ /* We use >= rather than > since the object cannot be exactly at
+ the beginning of the chunk but might be an empty object exactly
+ at the end of an adjacent chunk. */
+ while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj))
+ {
+ plp = lp->prev;
+ lp = plp;
+ }
+ return lp != 0;
+}
+
+/* Free objects in obstack H, including OBJ and everything allocate
+ more recently than OBJ. If OBJ is zero, free everything in H. */
+
+#undef obstack_free
+
+/* This function has two names with identical definitions.
+ This is the first one, called from non-ANSI code. */
+
+void
+_obstack_free (h, obj)
+ struct obstack *h;
+ POINTER obj;
+{
+ register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk* plp; /* point to previous chunk if any */
+
+ lp = h->chunk;
+ /* We use >= because there cannot be an object at the beginning of a chunk.
+ But there can be an empty object at that address
+ at the end of another chunk. */
+ while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj))
+ {
+ plp = lp->prev;
+ CALL_FREEFUN (h, lp);
+ lp = plp;
+ /* If we switch chunks, we can't tell whether the new current
+ chunk contains an empty object, so assume that it may. */
+ h->maybe_empty_object = 1;
+ }
+ if (lp)
+ {
+ h->object_base = h->next_free = (char *)(obj);
+ h->chunk_limit = lp->limit;
+ h->chunk = lp;
+ }
+ else if (obj != 0)
+ /* obj is not in any of the chunks! */
+ abort ();
+}
+
+/* This function is used from ANSI code. */
+
+void
+obstack_free (h, obj)
+ struct obstack *h;
+ POINTER obj;
+{
+ register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk* plp; /* point to previous chunk if any */
+
+ lp = h->chunk;
+ /* We use >= because there cannot be an object at the beginning of a chunk.
+ But there can be an empty object at that address
+ at the end of another chunk. */
+ while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj))
+ {
+ plp = lp->prev;
+ CALL_FREEFUN (h, lp);
+ lp = plp;
+ /* If we switch chunks, we can't tell whether the new current
+ chunk contains an empty object, so assume that it may. */
+ h->maybe_empty_object = 1;
+ }
+ if (lp)
+ {
+ h->object_base = h->next_free = (char *)(obj);
+ h->chunk_limit = lp->limit;
+ h->chunk = lp;
+ }
+ else if (obj != 0)
+ /* obj is not in any of the chunks! */
+ abort ();
+}
+
+#if 0
+/* These are now turned off because the applications do not use it
+ and it uses bcopy via obstack_grow, which causes trouble on sysV. */
+
+/* Now define the functional versions of the obstack macros.
+ Define them to simply use the corresponding macros to do the job. */
+
+#ifdef __STDC__
+/* These function definitions do not work with non-ANSI preprocessors;
+ they won't pass through the macro names in parentheses. */
+
+/* The function names appear in parentheses in order to prevent
+ the macro-definitions of the names from being expanded there. */
+
+POINTER (obstack_base) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_base (obstack);
+}
+
+POINTER (obstack_next_free) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_next_free (obstack);
+}
+
+int (obstack_object_size) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_object_size (obstack);
+}
+
+int (obstack_room) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_room (obstack);
+}
+
+void (obstack_grow) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ obstack_grow (obstack, pointer, length);
+}
+
+void (obstack_grow0) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ obstack_grow0 (obstack, pointer, length);
+}
+
+void (obstack_1grow) (obstack, character)
+ struct obstack *obstack;
+ int character;
+{
+ obstack_1grow (obstack, character);
+}
+
+void (obstack_blank) (obstack, length)
+ struct obstack *obstack;
+ int length;
+{
+ obstack_blank (obstack, length);
+}
+
+void (obstack_1grow_fast) (obstack, character)
+ struct obstack *obstack;
+ int character;
+{
+ obstack_1grow_fast (obstack, character);
+}
+
+void (obstack_blank_fast) (obstack, length)
+ struct obstack *obstack;
+ int length;
+{
+ obstack_blank_fast (obstack, length);
+}
+
+POINTER (obstack_finish) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_finish (obstack);
+}
+
+POINTER (obstack_alloc) (obstack, length)
+ struct obstack *obstack;
+ int length;
+{
+ return obstack_alloc (obstack, length);
+}
+
+POINTER (obstack_copy) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ return obstack_copy (obstack, pointer, length);
+}
+
+POINTER (obstack_copy0) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ return obstack_copy0 (obstack, pointer, length);
+}
+
+#endif /* __STDC__ */
+
+#endif /* 0 */
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
diff --git a/gnu/usr.bin/gdb/libiberty/sigsetmask.c b/gnu/usr.bin/gdb/libiberty/sigsetmask.c
new file mode 100644
index 0000000..545b12e
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/sigsetmask.c
@@ -0,0 +1,44 @@
+/* Version of sigsetmask.c
+ Copyright 1991, 1992 Free Software Foundation, Inc.
+ Written by Steve Chamberlain (sac@cygnus.com).
+ Contributed by Cygnus Support.
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+/* Set the current signal mask to the set provided, and return the
+ previous value */
+
+#define _POSIX_SOURCE
+#include <ansidecl.h>
+#include <signal.h>
+
+#ifdef SIG_SETMASK
+int
+DEFUN(sigsetmask,(set),
+ int set)
+{
+ sigset_t new;
+ sigset_t old;
+
+ sigemptyset (&new);
+ if (set != 0) {
+ abort(); /* FIXME, we don't know how to translate old mask to new */
+ }
+ sigprocmask(SIG_SETMASK, &new, &old);
+ return 1; /* FIXME, we always return 1 as old value. */
+}
+#endif
diff --git a/gnu/usr.bin/gdb/libiberty/spaces.c b/gnu/usr.bin/gdb/libiberty/spaces.c
new file mode 100644
index 0000000..28f0746
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/spaces.c
@@ -0,0 +1,67 @@
+/* Allocate memory region filled with spaces.
+ Copyright (C) 1991 Free Software Foundation, Inc.
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+/*
+
+NAME
+
+ spaces -- return a pointer to a buffer full of spaces
+
+SYNOPSIS
+
+ char *spaces (int count)
+
+DESCRIPTION
+
+ Returns a pointer to a memory region filled with the specified
+ number of spaces and null terminated. The returned pointer is
+ valid until at least the next call.
+
+BUGS
+
+*/
+
+
+char *
+spaces (count)
+ int count;
+{
+ register char *t;
+ static char *buf;
+ static int maxsize;
+ extern char *malloc ();
+ extern void free ();
+
+ if (count > maxsize)
+ {
+ if (buf)
+ {
+ free (buf);
+ }
+ buf = malloc (count + 1);
+ for (t = buf + count ; t != buf ; )
+ {
+ *--t = ' ';
+ }
+ maxsize = count;
+ buf[count] = '\0';
+ }
+ return (buf + maxsize - count);
+}
+
diff --git a/gnu/usr.bin/gdb/libiberty/strerror.c b/gnu/usr.bin/gdb/libiberty/strerror.c
new file mode 100644
index 0000000..f377311
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/strerror.c
@@ -0,0 +1,811 @@
+/* Extended support for using errno values.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+ Written by Fred Fish. fnf@cygnus.com
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include "config.h"
+
+#ifndef NEED_sys_errlist
+/* Note that errno.h (not sure what OS) or stdio.h (BSD 4.4, at least)
+ might declare sys_errlist in a way that the compiler might consider
+ incompatible with our later declaration, perhaps by using const
+ attributes. So we hide the declaration in errno.h (if any) using a
+ macro. */
+#define sys_errlist sys_errlist__
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#ifndef NEED_sys_errlist
+#undef sys_errlist
+#endif
+
+/* Routines imported from standard C runtime libraries. */
+
+#ifdef __STDC__
+#include <stddef.h>
+extern void *malloc (size_t size); /* 4.10.3.3 */
+extern void *memset (void *s, int c, size_t n); /* 4.11.6.1 */
+#else /* !__STDC__ */
+#ifndef const
+#define const
+#endif
+extern char *malloc (); /* Standard memory allocater */
+extern char *memset ();
+#endif /* __STDC__ */
+
+#ifndef MAX
+# define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+/* Translation table for errno values. See intro(2) in most UNIX systems
+ Programmers Reference Manuals.
+
+ Note that this table is generally only accessed when it is used at runtime
+ to initialize errno name and message tables that are indexed by errno
+ value.
+
+ Not all of these errnos will exist on all systems. This table is the only
+ thing that should have to be updated as new error numbers are introduced.
+ It's sort of ugly, but at least its portable. */
+
+struct error_info
+{
+ int value; /* The numeric value from <errno.h> */
+ char *name; /* The equivalent symbolic value */
+ char *msg; /* Short message about this value */
+};
+
+static const struct error_info error_table[] =
+{
+#if defined (EPERM)
+ {EPERM, "EPERM", "Not owner"},
+#endif
+#if defined (ENOENT)
+ {ENOENT, "ENOENT", "No such file or directory"},
+#endif
+#if defined (ESRCH)
+ {ESRCH, "ESRCH", "No such process"},
+#endif
+#if defined (EINTR)
+ {EINTR, "EINTR", "Interrupted system call"},
+#endif
+#if defined (EIO)
+ {EIO, "EIO", "I/O error"},
+#endif
+#if defined (ENXIO)
+ {ENXIO, "ENXIO", "No such device or address"},
+#endif
+#if defined (E2BIG)
+ {E2BIG, "E2BIG", "Arg list too long"},
+#endif
+#if defined (ENOEXEC)
+ {ENOEXEC, "ENOEXEC", "Exec format error"},
+#endif
+#if defined (EBADF)
+ {EBADF, "EBADF", "Bad file number"},
+#endif
+#if defined (ECHILD)
+ {ECHILD, "ECHILD", "No child processes"},
+#endif
+#if defined (EWOULDBLOCK) /* Put before EAGAIN, sometimes aliased */
+ {EWOULDBLOCK, "EWOULDBLOCK", "Operation would block"},
+#endif
+#if defined (EAGAIN)
+ {EAGAIN, "EAGAIN", "No more processes"},
+#endif
+#if defined (ENOMEM)
+ {ENOMEM, "ENOMEM", "Not enough space"},
+#endif
+#if defined (EACCES)
+ {EACCES, "EACCES", "Permission denied"},
+#endif
+#if defined (EFAULT)
+ {EFAULT, "EFAULT", "Bad address"},
+#endif
+#if defined (ENOTBLK)
+ {ENOTBLK, "ENOTBLK", "Block device required"},
+#endif
+#if defined (EBUSY)
+ {EBUSY, "EBUSY", "Device busy"},
+#endif
+#if defined (EEXIST)
+ {EEXIST, "EEXIST", "File exists"},
+#endif
+#if defined (EXDEV)
+ {EXDEV, "EXDEV", "Cross-device link"},
+#endif
+#if defined (ENODEV)
+ {ENODEV, "ENODEV", "No such device"},
+#endif
+#if defined (ENOTDIR)
+ {ENOTDIR, "ENOTDIR", "Not a directory"},
+#endif
+#if defined (EISDIR)
+ {EISDIR, "EISDIR", "Is a directory"},
+#endif
+#if defined (EINVAL)
+ {EINVAL, "EINVAL", "Invalid argument"},
+#endif
+#if defined (ENFILE)
+ {ENFILE, "ENFILE", "File table overflow"},
+#endif
+#if defined (EMFILE)
+ {EMFILE, "EMFILE", "Too many open files"},
+#endif
+#if defined (ENOTTY)
+ {ENOTTY, "ENOTTY", "Not a typewriter"},
+#endif
+#if defined (ETXTBSY)
+ {ETXTBSY, "ETXTBSY", "Text file busy"},
+#endif
+#if defined (EFBIG)
+ {EFBIG, "EFBIG", "File too large"},
+#endif
+#if defined (ENOSPC)
+ {ENOSPC, "ENOSPC", "No space left on device"},
+#endif
+#if defined (ESPIPE)
+ {ESPIPE, "ESPIPE", "Illegal seek"},
+#endif
+#if defined (EROFS)
+ {EROFS, "EROFS", "Read-only file system"},
+#endif
+#if defined (EMLINK)
+ {EMLINK, "EMLINK", "Too many links"},
+#endif
+#if defined (EPIPE)
+ {EPIPE, "EPIPE", "Broken pipe"},
+#endif
+#if defined (EDOM)
+ {EDOM, "EDOM", "Math argument out of domain of func"},
+#endif
+#if defined (ERANGE)
+ {ERANGE, "ERANGE", "Math result not representable"},
+#endif
+#if defined (ENOMSG)
+ {ENOMSG, "ENOMSG", "No message of desired type"},
+#endif
+#if defined (EIDRM)
+ {EIDRM, "EIDRM", "Identifier removed"},
+#endif
+#if defined (ECHRNG)
+ {ECHRNG, "ECHRNG", "Channel number out of range"},
+#endif
+#if defined (EL2NSYNC)
+ {EL2NSYNC, "EL2NSYNC", "Level 2 not synchronized"},
+#endif
+#if defined (EL3HLT)
+ {EL3HLT, "EL3HLT", "Level 3 halted"},
+#endif
+#if defined (EL3RST)
+ {EL3RST, "EL3RST", "Level 3 reset"},
+#endif
+#if defined (ELNRNG)
+ {ELNRNG, "ELNRNG", "Link number out of range"},
+#endif
+#if defined (EUNATCH)
+ {EUNATCH, "EUNATCH", "Protocol driver not attached"},
+#endif
+#if defined (ENOCSI)
+ {ENOCSI, "ENOCSI", "No CSI structure available"},
+#endif
+#if defined (EL2HLT)
+ {EL2HLT, "EL2HLT", "Level 2 halted"},
+#endif
+#if defined (EDEADLK)
+ {EDEADLK, "EDEADLK", "Deadlock condition"},
+#endif
+#if defined (ENOLCK)
+ {ENOLCK, "ENOLCK", "No record locks available"},
+#endif
+#if defined (EBADE)
+ {EBADE, "EBADE", "Invalid exchange"},
+#endif
+#if defined (EBADR)
+ {EBADR, "EBADR", "Invalid request descriptor"},
+#endif
+#if defined (EXFULL)
+ {EXFULL, "EXFULL", "Exchange full"},
+#endif
+#if defined (ENOANO)
+ {ENOANO, "ENOANO", "No anode"},
+#endif
+#if defined (EBADRQC)
+ {EBADRQC, "EBADRQC", "Invalid request code"},
+#endif
+#if defined (EBADSLT)
+ {EBADSLT, "EBADSLT", "Invalid slot"},
+#endif
+#if defined (EDEADLOCK)
+ {EDEADLOCK, "EDEADLOCK", "File locking deadlock error"},
+#endif
+#if defined (EBFONT)
+ {EBFONT, "EBFONT", "Bad font file format"},
+#endif
+#if defined (ENOSTR)
+ {ENOSTR, "ENOSTR", "Device not a stream"},
+#endif
+#if defined (ENODATA)
+ {ENODATA, "ENODATA", "No data available"},
+#endif
+#if defined (ETIME)
+ {ETIME, "ETIME", "Timer expired"},
+#endif
+#if defined (ENOSR)
+ {ENOSR, "ENOSR", "Out of streams resources"},
+#endif
+#if defined (ENONET)
+ {ENONET, "ENONET", "Machine is not on the network"},
+#endif
+#if defined (ENOPKG)
+ {ENOPKG, "ENOPKG", "Package not installed"},
+#endif
+#if defined (EREMOTE)
+ {EREMOTE, "EREMOTE", "Object is remote"},
+#endif
+#if defined (ENOLINK)
+ {ENOLINK, "ENOLINK", "Link has been severed"},
+#endif
+#if defined (EADV)
+ {EADV, "EADV", "Advertise error"},
+#endif
+#if defined (ESRMNT)
+ {ESRMNT, "ESRMNT", "Srmount error"},
+#endif
+#if defined (ECOMM)
+ {ECOMM, "ECOMM", "Communication error on send"},
+#endif
+#if defined (EPROTO)
+ {EPROTO, "EPROTO", "Protocol error"},
+#endif
+#if defined (EMULTIHOP)
+ {EMULTIHOP, "EMULTIHOP", "Multihop attempted"},
+#endif
+#if defined (EDOTDOT)
+ {EDOTDOT, "EDOTDOT", "RFS specific error"},
+#endif
+#if defined (EBADMSG)
+ {EBADMSG, "EBADMSG", "Not a data message"},
+#endif
+#if defined (ENAMETOOLONG)
+ {ENAMETOOLONG, "ENAMETOOLONG", "File name too long"},
+#endif
+#if defined (EOVERFLOW)
+ {EOVERFLOW, "EOVERFLOW", "Value too large for defined data type"},
+#endif
+#if defined (ENOTUNIQ)
+ {ENOTUNIQ, "ENOTUNIQ", "Name not unique on network"},
+#endif
+#if defined (EBADFD)
+ {EBADFD, "EBADFD", "File descriptor in bad state"},
+#endif
+#if defined (EREMCHG)
+ {EREMCHG, "EREMCHG", "Remote address changed"},
+#endif
+#if defined (ELIBACC)
+ {ELIBACC, "ELIBACC", "Can not access a needed shared library"},
+#endif
+#if defined (ELIBBAD)
+ {ELIBBAD, "ELIBBAD", "Accessing a corrupted shared library"},
+#endif
+#if defined (ELIBSCN)
+ {ELIBSCN, "ELIBSCN", ".lib section in a.out corrupted"},
+#endif
+#if defined (ELIBMAX)
+ {ELIBMAX, "ELIBMAX", "Attempting to link in too many shared libraries"},
+#endif
+#if defined (ELIBEXEC)
+ {ELIBEXEC, "ELIBEXEC", "Cannot exec a shared library directly"},
+#endif
+#if defined (EILSEQ)
+ {EILSEQ, "EILSEQ", "Illegal byte sequence"},
+#endif
+#if defined (ENOSYS)
+ {ENOSYS, "ENOSYS", "Operation not applicable"},
+#endif
+#if defined (ELOOP)
+ {ELOOP, "ELOOP", "Too many symbolic links encountered"},
+#endif
+#if defined (ERESTART)
+ {ERESTART, "ERESTART", "Interrupted system call should be restarted"},
+#endif
+#if defined (ESTRPIPE)
+ {ESTRPIPE, "ESTRPIPE", "Streams pipe error"},
+#endif
+#if defined (ENOTEMPTY)
+ {ENOTEMPTY, "ENOTEMPTY", "Directory not empty"},
+#endif
+#if defined (EUSERS)
+ {EUSERS, "EUSERS", "Too many users"},
+#endif
+#if defined (ENOTSOCK)
+ {ENOTSOCK, "ENOTSOCK", "Socket operation on non-socket"},
+#endif
+#if defined (EDESTADDRREQ)
+ {EDESTADDRREQ, "EDESTADDRREQ", "Destination address required"},
+#endif
+#if defined (EMSGSIZE)
+ {EMSGSIZE, "EMSGSIZE", "Message too long"},
+#endif
+#if defined (EPROTOTYPE)
+ {EPROTOTYPE, "EPROTOTYPE", "Protocol wrong type for socket"},
+#endif
+#if defined (ENOPROTOOPT)
+ {ENOPROTOOPT, "ENOPROTOOPT", "Protocol not available"},
+#endif
+#if defined (EPROTONOSUPPORT)
+ {EPROTONOSUPPORT, "EPROTONOSUPPORT", "Protocol not supported"},
+#endif
+#if defined (ESOCKTNOSUPPORT)
+ {ESOCKTNOSUPPORT, "ESOCKTNOSUPPORT", "Socket type not supported"},
+#endif
+#if defined (EOPNOTSUPP)
+ {EOPNOTSUPP, "EOPNOTSUPP", "Operation not supported on transport endpoint"},
+#endif
+#if defined (EPFNOSUPPORT)
+ {EPFNOSUPPORT, "EPFNOSUPPORT", "Protocol family not supported"},
+#endif
+#if defined (EAFNOSUPPORT)
+ {EAFNOSUPPORT, "EAFNOSUPPORT", "Address family not supported by protocol"},
+#endif
+#if defined (EADDRINUSE)
+ {EADDRINUSE, "EADDRINUSE", "Address already in use"},
+#endif
+#if defined (EADDRNOTAVAIL)
+ {EADDRNOTAVAIL, "EADDRNOTAVAIL","Cannot assign requested address"},
+#endif
+#if defined (ENETDOWN)
+ {ENETDOWN, "ENETDOWN", "Network is down"},
+#endif
+#if defined (ENETUNREACH)
+ {ENETUNREACH, "ENETUNREACH", "Network is unreachable"},
+#endif
+#if defined (ENETRESET)
+ {ENETRESET, "ENETRESET", "Network dropped connection because of reset"},
+#endif
+#if defined (ECONNABORTED)
+ {ECONNABORTED, "ECONNABORTED", "Software caused connection abort"},
+#endif
+#if defined (ECONNRESET)
+ {ECONNRESET, "ECONNRESET", "Connection reset by peer"},
+#endif
+#if defined (ENOBUFS)
+ {ENOBUFS, "ENOBUFS", "No buffer space available"},
+#endif
+#if defined (EISCONN)
+ {EISCONN, "EISCONN", "Transport endpoint is already connected"},
+#endif
+#if defined (ENOTCONN)
+ {ENOTCONN, "ENOTCONN", "Transport endpoint is not connected"},
+#endif
+#if defined (ESHUTDOWN)
+ {ESHUTDOWN, "ESHUTDOWN", "Cannot send after transport endpoint shutdown"},
+#endif
+#if defined (ETOOMANYREFS)
+ {ETOOMANYREFS, "ETOOMANYREFS", "Too many references: cannot splice"},
+#endif
+#if defined (ETIMEDOUT)
+ {ETIMEDOUT, "ETIMEDOUT", "Connection timed out"},
+#endif
+#if defined (ECONNREFUSED)
+ {ECONNREFUSED, "ECONNREFUSED", "Connection refused"},
+#endif
+#if defined (EHOSTDOWN)
+ {EHOSTDOWN, "EHOSTDOWN", "Host is down"},
+#endif
+#if defined (EHOSTUNREACH)
+ {EHOSTUNREACH, "EHOSTUNREACH", "No route to host"},
+#endif
+#if defined (EALREADY)
+ {EALREADY, "EALREADY", "Operation already in progress"},
+#endif
+#if defined (EINPROGRESS)
+ {EINPROGRESS, "EINPROGRESS", "Operation now in progress"},
+#endif
+#if defined (ESTALE)
+ {ESTALE, "ESTALE", "Stale NFS file handle"},
+#endif
+#if defined (EUCLEAN)
+ {EUCLEAN, "EUCLEAN", "Structure needs cleaning"},
+#endif
+#if defined (ENOTNAM)
+ {ENOTNAM, "ENOTNAM", "Not a XENIX named type file"},
+#endif
+#if defined (ENAVAIL)
+ {ENAVAIL, "ENAVAIL", "No XENIX semaphores available"},
+#endif
+#if defined (EISNAM)
+ {EISNAM, "EISNAM", "Is a named type file"},
+#endif
+#if defined (EREMOTEIO)
+ {EREMOTEIO, "EREMOTEIO", "Remote I/O error"},
+#endif
+ {0, NULL, NULL}
+};
+
+/* Translation table allocated and initialized at runtime. Indexed by the
+ errno value to find the equivalent symbolic value. */
+
+static char **error_names;
+static int num_error_names = 0;
+
+/* Translation table allocated and initialized at runtime, if it does not
+ already exist in the host environment. Indexed by the errno value to find
+ the descriptive string.
+
+ We don't export it for use in other modules because even though it has the
+ same name, it differs from other implementations in that it is dynamically
+ initialized rather than statically initialized. */
+
+#ifdef NEED_sys_errlist
+
+static int sys_nerr;
+static char **sys_errlist;
+
+#else
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#endif
+
+
+/*
+
+NAME
+
+ init_error_tables -- initialize the name and message tables
+
+SYNOPSIS
+
+ static void init_error_tables ();
+
+DESCRIPTION
+
+ Using the error_table, which is initialized at compile time, generate
+ the error_names and the sys_errlist (if needed) tables, which are
+ indexed at runtime by a specific errno value.
+
+BUGS
+
+ The initialization of the tables may fail under low memory conditions,
+ in which case we don't do anything particularly useful, but we don't
+ bomb either. Who knows, it might succeed at a later point if we free
+ some memory in the meantime. In any case, the other routines know
+ how to deal with lack of a table after trying to initialize it. This
+ may or may not be considered to be a bug, that we don't specifically
+ warn about this particular failure mode.
+
+*/
+
+static void
+init_error_tables ()
+{
+ const struct error_info *eip;
+ int nbytes;
+
+ /* If we haven't already scanned the error_table once to find the maximum
+ errno value, then go find it now. */
+
+ if (num_error_names == 0)
+ {
+ for (eip = error_table; eip -> name != NULL; eip++)
+ {
+ if (eip -> value >= num_error_names)
+ {
+ num_error_names = eip -> value + 1;
+ }
+ }
+ }
+
+ /* Now attempt to allocate the error_names table, zero it out, and then
+ initialize it from the statically initialized error_table. */
+
+ if (error_names == NULL)
+ {
+ nbytes = num_error_names * sizeof (char *);
+ if ((error_names = (char **) malloc (nbytes)) != NULL)
+ {
+ memset (error_names, 0, nbytes);
+ for (eip = error_table; eip -> name != NULL; eip++)
+ {
+ error_names[eip -> value] = eip -> name;
+ }
+ }
+ }
+
+#ifdef NEED_sys_errlist
+
+ /* Now attempt to allocate the sys_errlist table, zero it out, and then
+ initialize it from the statically initialized error_table. */
+
+ if (sys_errlist == NULL)
+ {
+ nbytes = num_error_names * sizeof (char *);
+ if ((sys_errlist = (char **) malloc (nbytes)) != NULL)
+ {
+ memset (sys_errlist, 0, nbytes);
+ sys_nerr = num_error_names;
+ for (eip = error_table; eip -> name != NULL; eip++)
+ {
+ sys_errlist[eip -> value] = eip -> msg;
+ }
+ }
+ }
+
+#endif
+
+}
+
+/*
+
+NAME
+
+ errno_max -- return the max errno value
+
+SYNOPSIS
+
+ int errno_max ();
+
+DESCRIPTION
+
+ Returns the maximum errno value for which a corresponding symbolic
+ name or message is available. Note that in the case where
+ we use the sys_errlist supplied by the system, it is possible for
+ there to be more symbolic names than messages, or vice versa.
+ In fact, the manual page for perror(3C) explicitly warns that one
+ should check the size of the table (sys_nerr) before indexing it,
+ since new error codes may be added to the system before they are
+ added to the table. Thus sys_nerr might be smaller than value
+ implied by the largest errno value defined in <errno.h>.
+
+ We return the maximum value that can be used to obtain a meaningful
+ symbolic name or message.
+
+*/
+
+int
+errno_max ()
+{
+ int maxsize;
+
+ if (error_names == NULL)
+ {
+ init_error_tables ();
+ }
+ maxsize = MAX (sys_nerr, num_error_names);
+ return (maxsize - 1);
+}
+
+#ifdef NEED_strerror
+
+/*
+
+NAME
+
+ strerror -- map an error number to an error message string
+
+SYNOPSIS
+
+ char *strerror (int errnoval)
+
+DESCRIPTION
+
+ Maps an errno number to an error message string, the contents of
+ which are implementation defined. On systems which have the external
+ variables sys_nerr and sys_errlist, these strings will be the same
+ as the ones used by perror().
+
+ If the supplied error number is within the valid range of indices
+ for the sys_errlist, but no message is available for the particular
+ error number, then returns the string "Error NUM", where NUM is the
+ error number.
+
+ If the supplied error number is not a valid index into sys_errlist,
+ returns NULL.
+
+ The returned string is only guaranteed to be valid only until the
+ next call to strerror.
+
+*/
+
+char *
+strerror (errnoval)
+ int errnoval;
+{
+ char *msg;
+ static char buf[32];
+
+#ifdef NEED_sys_errlist
+
+ if (error_names == NULL)
+ {
+ init_error_tables ();
+ }
+
+#endif
+
+ if ((errnoval < 0) || (errnoval >= sys_nerr))
+ {
+ /* Out of range, just return NULL */
+ msg = NULL;
+ }
+ else if ((sys_errlist == NULL) || (sys_errlist[errnoval] == NULL))
+ {
+ /* In range, but no sys_errlist or no entry at this index. */
+ sprintf (buf, "Error %d", errnoval);
+ msg = buf;
+ }
+ else
+ {
+ /* In range, and a valid message. Just return the message. */
+ msg = sys_errlist[errnoval];
+ }
+
+ return (msg);
+}
+
+#endif /* NEED_strerror */
+
+
+/*
+
+NAME
+
+ strerrno -- map an error number to a symbolic name string
+
+SYNOPSIS
+
+ char *strerrno (int errnoval)
+
+DESCRIPTION
+
+ Given an error number returned from a system call (typically
+ returned in errno), returns a pointer to a string containing the
+ symbolic name of that error number, as found in <errno.h>.
+
+ If the supplied error number is within the valid range of indices
+ for symbolic names, but no name is available for the particular
+ error number, then returns the string "Error NUM", where NUM is
+ the error number.
+
+ If the supplied error number is not within the range of valid
+ indices, then returns NULL.
+
+BUGS
+
+ The contents of the location pointed to are only guaranteed to be
+ valid until the next call to strerrno.
+
+*/
+
+char *
+strerrno (errnoval)
+ int errnoval;
+{
+ char *name;
+ static char buf[32];
+
+ if (error_names == NULL)
+ {
+ init_error_tables ();
+ }
+
+ if ((errnoval < 0) || (errnoval >= num_error_names))
+ {
+ /* Out of range, just return NULL */
+ name = NULL;
+ }
+ else if ((error_names == NULL) || (error_names[errnoval] == NULL))
+ {
+ /* In range, but no error_names or no entry at this index. */
+ sprintf (buf, "Error %d", errnoval);
+ name = buf;
+ }
+ else
+ {
+ /* In range, and a valid name. Just return the name. */
+ name = error_names[errnoval];
+ }
+
+ return (name);
+}
+
+/*
+
+NAME
+
+ strtoerrno -- map a symbolic errno name to a numeric value
+
+SYNOPSIS
+
+ int strtoerrno (char *name)
+
+DESCRIPTION
+
+ Given the symbolic name of a error number, map it to an errno value.
+ If no translation is found, returns 0.
+
+*/
+
+int
+strtoerrno (name)
+ char *name;
+{
+ int errnoval = 0;
+
+ if (name != NULL)
+ {
+ if (error_names == NULL)
+ {
+ init_error_tables ();
+ }
+ for (errnoval = 0; errnoval < num_error_names; errnoval++)
+ {
+ if ((error_names[errnoval] != NULL) &&
+ (strcmp (name, error_names[errnoval]) == 0))
+ {
+ break;
+ }
+ }
+ if (errnoval == num_error_names)
+ {
+ errnoval = 0;
+ }
+ }
+ return (errnoval);
+}
+
+
+/* A simple little main that does nothing but print all the errno translations
+ if MAIN is defined and this file is compiled and linked. */
+
+#ifdef MAIN
+
+main ()
+{
+ int errn;
+ int errnmax;
+ char *name;
+ char *msg;
+ char *strerrno ();
+ char *strerror ();
+
+ errnmax = errno_max ();
+ printf ("%d entries in names table.\n", num_error_names);
+ printf ("%d entries in messages table.\n", sys_nerr);
+ printf ("%d is max useful index.\n", errnmax);
+
+ /* Keep printing values until we get to the end of *both* tables, not
+ *either* table. Note that knowing the maximum useful index does *not*
+ relieve us of the responsibility of testing the return pointer for
+ NULL. */
+
+ for (errn = 0; errn <= errnmax; errn++)
+ {
+ name = strerrno (errn);
+ name = (name == NULL) ? "<NULL>" : name;
+ msg = strerror (errn);
+ msg = (msg == NULL) ? "<NULL>" : msg;
+ printf ("%-4d%-18s%s\n", errn, name, msg);
+ }
+}
+
+#endif
diff --git a/gnu/usr.bin/gdb/libiberty/strsignal.c b/gnu/usr.bin/gdb/libiberty/strsignal.c
new file mode 100644
index 0000000..15411ff
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/strsignal.c
@@ -0,0 +1,634 @@
+/* Extended support for using signal values.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+ Written by Fred Fish. fnf@cygnus.com
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <ansidecl.h>
+
+#include "config.h"
+
+#ifdef LOSING_SYS_SIGLIST
+#define sys_siglist no_such_symbol
+#endif
+
+#include <stdio.h>
+#include <signal.h>
+
+/* Routines imported from standard C runtime libraries. */
+
+#ifdef __STDC__
+#include <stddef.h>
+extern void *malloc (size_t size); /* 4.10.3.3 */
+extern void *memset (void *s, int c, size_t n); /* 4.11.6.1 */
+#else /* !__STDC__ */
+#ifndef const
+#define const
+#endif
+extern char *malloc (); /* Standard memory allocater */
+extern char *memset ();
+#endif /* __STDC__ */
+
+#ifdef LOSING_SYS_SIGLIST
+#undef sys_siglist
+#endif
+
+
+#ifndef NULL
+# ifdef __STDC__
+# define NULL (void *) 0
+# else
+# define NULL 0
+# endif
+#endif
+
+#ifndef MAX
+# define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+/* Translation table for signal values.
+
+ Note that this table is generally only accessed when it is used at runtime
+ to initialize signal name and message tables that are indexed by signal
+ value.
+
+ Not all of these signals will exist on all systems. This table is the only
+ thing that should have to be updated as new signal numbers are introduced.
+ It's sort of ugly, but at least its portable. */
+
+struct signal_info
+{
+ int value; /* The numeric value from <signal.h> */
+ char *name; /* The equivalent symbolic value */
+ char *msg; /* Short message about this value */
+};
+
+static const struct signal_info signal_table[] =
+{
+#if defined (SIGHUP)
+ {SIGHUP, "SIGHUP", "Hangup"},
+#endif
+#if defined (SIGINT)
+ {SIGINT, "SIGINT", "Interrupt"},
+#endif
+#if defined (SIGQUIT)
+ {SIGQUIT, "SIGQUIT", "Quit"},
+#endif
+#if defined (SIGILL)
+ {SIGILL, "SIGILL", "Illegal instruction"},
+#endif
+#if defined (SIGTRAP)
+ {SIGTRAP, "SIGTRAP", "Trace/breakpoint trap"},
+#endif
+/* Put SIGIOT before SIGABRT, so that if SIGIOT==SIGABRT then SIGABRT
+ overrides SIGIOT. SIGABRT is in ANSI and POSIX.1, and SIGIOT isn't. */
+#if defined (SIGIOT)
+ {SIGIOT, "SIGIOT", "IOT trap"},
+#endif
+#if defined (SIGABRT)
+ {SIGABRT, "SIGABRT", "Aborted"},
+#endif
+#if defined (SIGEMT)
+ {SIGEMT, "SIGEMT", "Emulation trap"},
+#endif
+#if defined (SIGFPE)
+ {SIGFPE, "SIGFPE", "Arithmetic exception"},
+#endif
+#if defined (SIGKILL)
+ {SIGKILL, "SIGKILL", "Killed"},
+#endif
+#if defined (SIGBUS)
+ {SIGBUS, "SIGBUS", "Bus error"},
+#endif
+#if defined (SIGSEGV)
+ {SIGSEGV, "SIGSEGV", "Segmentation fault"},
+#endif
+#if defined (SIGSYS)
+ {SIGSYS, "SIGSYS", "Bad system call"},
+#endif
+#if defined (SIGPIPE)
+ {SIGPIPE, "SIGPIPE", "Broken pipe"},
+#endif
+#if defined (SIGALRM)
+ {SIGALRM, "SIGALRM", "Alarm clock"},
+#endif
+#if defined (SIGTERM)
+ {SIGTERM, "SIGTERM", "Terminated"},
+#endif
+#if defined (SIGUSR1)
+ {SIGUSR1, "SIGUSR1", "User defined signal 1"},
+#endif
+#if defined (SIGUSR2)
+ {SIGUSR2, "SIGUSR2", "User defined signal 2"},
+#endif
+/* Put SIGCLD before SIGCHLD, so that if SIGCLD==SIGCHLD then SIGCHLD
+ overrides SIGCLD. SIGCHLD is in POXIX.1 */
+#if defined (SIGCLD)
+ {SIGCLD, "SIGCLD", "Child status changed"},
+#endif
+#if defined (SIGCHLD)
+ {SIGCHLD, "SIGCHLD", "Child status changed"},
+#endif
+#if defined (SIGPWR)
+ {SIGPWR, "SIGPWR", "Power fail/restart"},
+#endif
+#if defined (SIGWINCH)
+ {SIGWINCH, "SIGWINCH", "Window size changed"},
+#endif
+#if defined (SIGURG)
+ {SIGURG, "SIGURG", "Urgent I/O condition"},
+#endif
+#if defined (SIGIO)
+ /* "I/O pending" has also been suggested, but is misleading since the
+ signal only happens when the process has asked for it, not everytime
+ I/O is pending. */
+ {SIGIO, "SIGIO", "I/O possible"},
+#endif
+#if defined (SIGPOLL)
+ {SIGPOLL, "SIGPOLL", "Pollable event occurred"},
+#endif
+#if defined (SIGSTOP)
+ {SIGSTOP, "SIGSTOP", "Stopped (signal)"},
+#endif
+#if defined (SIGTSTP)
+ {SIGTSTP, "SIGTSTP", "Stopped (user)"},
+#endif
+#if defined (SIGCONT)
+ {SIGCONT, "SIGCONT", "Continued"},
+#endif
+#if defined (SIGTTIN)
+ {SIGTTIN, "SIGTTIN", "Stopped (tty input)"},
+#endif
+#if defined (SIGTTOU)
+ {SIGTTOU, "SIGTTOU", "Stopped (tty output)"},
+#endif
+#if defined (SIGVTALRM)
+ {SIGVTALRM, "SIGVTALRM", "Virtual timer expired"},
+#endif
+#if defined (SIGPROF)
+ {SIGPROF, "SIGPROF", "Profiling timer expired"},
+#endif
+#if defined (SIGXCPU)
+ {SIGXCPU, "SIGXCPU", "CPU time limit exceeded"},
+#endif
+#if defined (SIGXFSZ)
+ {SIGXFSZ, "SIGXFSZ", "File size limit exceeded"},
+#endif
+#if defined (SIGWIND)
+ {SIGWIND, "SIGWIND", "SIGWIND"},
+#endif
+#if defined (SIGPHONE)
+ {SIGPHONE, "SIGPHONE", "SIGPHONE"},
+#endif
+#if defined (SIGLOST)
+ {SIGLOST, "SIGLOST", "Resource lost"},
+#endif
+#if defined (SIGWAITING)
+ {SIGWAITING, "SIGWAITING", "Process's LWPs are blocked"},
+#endif
+#if defined (SIGLWP)
+ {SIGLWP, "SIGLWP", "Signal LWP"},
+#endif
+#if defined (SIGDANGER)
+ {SIGDANGER, "SIGDANGER", "Swap space dangerously low"},
+#endif
+#if defined (SIGGRANT)
+ {SIGGRANT, "SIGGRANT", "Monitor mode granted"},
+#endif
+#if defined (SIGRETRACT)
+ {SIGRETRACT, "SIGRETRACT", "Need to relinguish monitor mode"},
+#endif
+#if defined (SIGMSG)
+ {SIGMSG, "SIGMSG", "Monitor mode data available"},
+#endif
+#if defined (SIGSOUND)
+ {SIGSOUND, "SIGSOUND", "Sound completed"},
+#endif
+#if defined (SIGSAK)
+ {SIGSAK, "SIGSAK", "Secure attention"},
+#endif
+ {0, NULL, NULL}
+};
+
+/* Translation table allocated and initialized at runtime. Indexed by the
+ signal value to find the equivalent symbolic value. */
+
+static char **signal_names;
+static int num_signal_names = 0;
+
+/* Translation table allocated and initialized at runtime, if it does not
+ already exist in the host environment. Indexed by the signal value to find
+ the descriptive string.
+
+ We don't export it for use in other modules because even though it has the
+ same name, it differs from other implementations in that it is dynamically
+ initialized rather than statically initialized. */
+
+#ifdef NEED_sys_siglist
+
+static int sys_nsig;
+static char **sys_siglist;
+
+#else
+
+static int sys_nsig = NSIG;
+extern const char * const sys_siglist[];
+
+#endif
+
+
+/*
+
+NAME
+
+ init_signal_tables -- initialize the name and message tables
+
+SYNOPSIS
+
+ static void init_signal_tables ();
+
+DESCRIPTION
+
+ Using the signal_table, which is initialized at compile time, generate
+ the signal_names and the sys_siglist (if needed) tables, which are
+ indexed at runtime by a specific signal value.
+
+BUGS
+
+ The initialization of the tables may fail under low memory conditions,
+ in which case we don't do anything particularly useful, but we don't
+ bomb either. Who knows, it might succeed at a later point if we free
+ some memory in the meantime. In any case, the other routines know
+ how to deal with lack of a table after trying to initialize it. This
+ may or may not be considered to be a bug, that we don't specifically
+ warn about this particular failure mode.
+
+*/
+
+static void
+init_signal_tables ()
+{
+ const struct signal_info *eip;
+ int nbytes;
+
+ /* If we haven't already scanned the signal_table once to find the maximum
+ signal value, then go find it now. */
+
+ if (num_signal_names == 0)
+ {
+ for (eip = signal_table; eip -> name != NULL; eip++)
+ {
+ if (eip -> value >= num_signal_names)
+ {
+ num_signal_names = eip -> value + 1;
+ }
+ }
+ }
+
+ /* Now attempt to allocate the signal_names table, zero it out, and then
+ initialize it from the statically initialized signal_table. */
+
+ if (signal_names == NULL)
+ {
+ nbytes = num_signal_names * sizeof (char *);
+ if ((signal_names = (char **) malloc (nbytes)) != NULL)
+ {
+ memset (signal_names, 0, nbytes);
+ for (eip = signal_table; eip -> name != NULL; eip++)
+ {
+ signal_names[eip -> value] = eip -> name;
+ }
+ }
+ }
+
+#ifdef NEED_sys_siglist
+
+ /* Now attempt to allocate the sys_siglist table, zero it out, and then
+ initialize it from the statically initialized signal_table. */
+
+ if (sys_siglist == NULL)
+ {
+ nbytes = num_signal_names * sizeof (char *);
+ if ((sys_siglist = (char **) malloc (nbytes)) != NULL)
+ {
+ memset (sys_siglist, 0, nbytes);
+ sys_nsig = num_signal_names;
+ for (eip = signal_table; eip -> name != NULL; eip++)
+ {
+ sys_siglist[eip -> value] = eip -> msg;
+ }
+ }
+ }
+
+#endif
+
+}
+
+
+/*
+
+NAME
+
+ signo_max -- return the max signo value
+
+SYNOPSIS
+
+ int signo_max ();
+
+DESCRIPTION
+
+ Returns the maximum signo value for which a corresponding symbolic
+ name or message is available. Note that in the case where
+ we use the sys_siglist supplied by the system, it is possible for
+ there to be more symbolic names than messages, or vice versa.
+ In fact, the manual page for psignal(3b) explicitly warns that one
+ should check the size of the table (NSIG) before indexing it,
+ since new signal codes may be added to the system before they are
+ added to the table. Thus NSIG might be smaller than value
+ implied by the largest signo value defined in <signal.h>.
+
+ We return the maximum value that can be used to obtain a meaningful
+ symbolic name or message.
+
+*/
+
+int
+signo_max ()
+{
+ int maxsize;
+
+ if (signal_names == NULL)
+ {
+ init_signal_tables ();
+ }
+ maxsize = MAX (sys_nsig, num_signal_names);
+ return (maxsize - 1);
+}
+
+
+/*
+
+NAME
+
+ strsignal -- map a signal number to a signal message string
+
+SYNOPSIS
+
+ char *strsignal (int signo)
+
+DESCRIPTION
+
+ Maps an signal number to an signal message string, the contents of
+ which are implementation defined. On systems which have the external
+ variable sys_siglist, these strings will be the same as the ones used
+ by psignal().
+
+ If the supplied signal number is within the valid range of indices
+ for the sys_siglist, but no message is available for the particular
+ signal number, then returns the string "Signal NUM", where NUM is the
+ signal number.
+
+ If the supplied signal number is not a valid index into sys_siglist,
+ returns NULL.
+
+ The returned string is only guaranteed to be valid only until the
+ next call to strsignal.
+
+*/
+
+char *
+strsignal (signo)
+ int signo;
+{
+ char *msg;
+ static char buf[32];
+
+#ifdef NEED_sys_siglist
+
+ if (signal_names == NULL)
+ {
+ init_signal_tables ();
+ }
+
+#endif
+
+ if ((signo < 0) || (signo >= sys_nsig))
+ {
+ /* Out of range, just return NULL */
+ msg = NULL;
+ }
+ else if ((sys_siglist == NULL) || (sys_siglist[signo] == NULL))
+ {
+ /* In range, but no sys_siglist or no entry at this index. */
+ sprintf (buf, "Signal %d", signo);
+ msg = buf;
+ }
+ else
+ {
+ /* In range, and a valid message. Just return the message. */
+ msg = (char*)sys_siglist[signo];
+ }
+
+ return (msg);
+}
+
+
+/*
+
+NAME
+
+ strsigno -- map an signal number to a symbolic name string
+
+SYNOPSIS
+
+ char *strsigno (int signo)
+
+DESCRIPTION
+
+ Given an signal number, returns a pointer to a string containing
+ the symbolic name of that signal number, as found in <signal.h>.
+
+ If the supplied signal number is within the valid range of indices
+ for symbolic names, but no name is available for the particular
+ signal number, then returns the string "Signal NUM", where NUM is
+ the signal number.
+
+ If the supplied signal number is not within the range of valid
+ indices, then returns NULL.
+
+BUGS
+
+ The contents of the location pointed to are only guaranteed to be
+ valid until the next call to strsigno.
+
+*/
+
+char *
+strsigno (signo)
+ int signo;
+{
+ char *name;
+ static char buf[32];
+
+ if (signal_names == NULL)
+ {
+ init_signal_tables ();
+ }
+
+ if ((signo < 0) || (signo >= num_signal_names))
+ {
+ /* Out of range, just return NULL */
+ name = NULL;
+ }
+ else if ((signal_names == NULL) || (signal_names[signo] == NULL))
+ {
+ /* In range, but no signal_names or no entry at this index. */
+ sprintf (buf, "Signal %d", signo);
+ name = buf;
+ }
+ else
+ {
+ /* In range, and a valid name. Just return the name. */
+ name = signal_names[signo];
+ }
+
+ return (name);
+}
+
+
+/*
+
+NAME
+
+ strtosigno -- map a symbolic signal name to a numeric value
+
+SYNOPSIS
+
+ int strtosigno (char *name)
+
+DESCRIPTION
+
+ Given the symbolic name of a signal, map it to a signal number.
+ If no translation is found, returns 0.
+
+*/
+
+int
+strtosigno (name)
+ char *name;
+{
+ int signo = 0;
+
+ if (name != NULL)
+ {
+ if (signal_names == NULL)
+ {
+ init_signal_tables ();
+ }
+ for (signo = 0; signo < num_signal_names; signo++)
+ {
+ if ((signal_names[signo] != NULL) &&
+ (strcmp (name, signal_names[signo]) == 0))
+ {
+ break;
+ }
+ }
+ if (signo == num_signal_names)
+ {
+ signo = 0;
+ }
+ }
+ return (signo);
+}
+
+
+/*
+
+NAME
+
+ psignal -- print message about signal to stderr
+
+SYNOPSIS
+
+ void psignal (unsigned signo, char *message);
+
+DESCRIPTION
+
+ Print to the standard error the message, followed by a colon,
+ followed by the description of the signal specified by signo,
+ followed by a newline.
+*/
+
+#ifdef NEED_psignal
+
+void
+psignal (signo, message)
+ unsigned signo;
+ char *message;
+{
+ if (signal_names == NULL)
+ {
+ init_signal_tables ();
+ }
+ if ((signo <= 0) || (signo >= sys_nsig))
+ {
+ fprintf (stderr, "%s: unknown signal\n", message);
+ }
+ else
+ {
+ fprintf (stderr, "%s: %s\n", message, sys_siglist[signo]);
+ }
+}
+
+#endif /* NEED_psignal */
+
+
+/* A simple little main that does nothing but print all the signal translations
+ if MAIN is defined and this file is compiled and linked. */
+
+#ifdef MAIN
+
+main ()
+{
+ int signo;
+ int maxsigno;
+ char *name;
+ char *msg;
+ char *strsigno ();
+ char *strsignal ();
+
+ maxsigno = signo_max ();
+ printf ("%d entries in names table.\n", num_signal_names);
+ printf ("%d entries in messages table.\n", sys_nsig);
+ printf ("%d is max useful index.\n", maxsigno);
+
+ /* Keep printing values until we get to the end of *both* tables, not
+ *either* table. Note that knowing the maximum useful index does *not*
+ relieve us of the responsibility of testing the return pointer for
+ NULL. */
+
+ for (signo = 0; signo <= maxsigno; signo++)
+ {
+ name = strsigno (signo);
+ name = (name == NULL) ? "<NULL>" : name;
+ msg = strsignal (signo);
+ msg = (msg == NULL) ? "<NULL>" : msg;
+ printf ("%-4d%-18s%s\n", signo, name, msg);
+ }
+}
+
+#endif
diff --git a/gnu/usr.bin/gdb/libiberty/xmalloc.c b/gnu/usr.bin/gdb/libiberty/xmalloc.c
new file mode 100644
index 0000000..be0c7aa
--- /dev/null
+++ b/gnu/usr.bin/gdb/libiberty/xmalloc.c
@@ -0,0 +1,58 @@
+/* memory allocation routines with error checking.
+ Copyright 1989, 1991, 1993 Free Software Foundation, Inc.
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <ansidecl.h>
+
+#include <stdio.h>
+
+#ifdef __STDC__
+#include <stddef.h>
+#else
+#define size_t unsigned long
+#endif
+
+
+PTR
+xmalloc (size)
+ size_t size;
+{
+ char * newmem;
+
+ if ((newmem = (char *) malloc ((int) size)) == NULL)
+ {
+ fprintf (stderr, "\nCan't allocate %u bytes\n", size);
+ exit (1);
+ }
+ return (newmem);
+}
+
+PTR
+xrealloc (oldmem, size)
+ PTR oldmem;
+ size_t size;
+{
+ char * newmem;
+
+ if ((newmem = (char *) realloc ((char *) oldmem, (int) size)) == NULL)
+ {
+ fprintf (stderr, "\nCan't reallocate %u bytes\n", size);
+ exit (1);
+ }
+ return (newmem);
+}
diff --git a/gnu/usr.bin/gdb/main.c b/gnu/usr.bin/gdb/main.c
new file mode 100644
index 0000000..323de87
--- /dev/null
+++ b/gnu/usr.bin/gdb/main.c
@@ -0,0 +1,2241 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 6.6 (Berkeley) 5/13/91";
+#endif /* not lint */
+
+/* Top level for GDB, the GNU debugger.
+ Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "defs.h"
+#include "command.h"
+#include "param.h"
+#include "expression.h"
+
+#ifdef USG
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#include <sys/file.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <ctype.h>
+
+#ifdef SET_STACK_LIMIT_HUGE
+#include <sys/time.h>
+#include <sys/resource.h>
+
+int original_stack_limit;
+#endif
+
+/* If this definition isn't overridden by the header files, assume
+ that isatty and fileno exist on this system. */
+#ifndef ISATTY
+#define ISATTY(FP) (isatty (fileno (FP)))
+#endif
+
+extern void free ();
+
+/* Version number of GDB, as a string. */
+
+extern char *version;
+
+/*
+ * Declare all cmd_list_element's
+ */
+
+/* Chain containing all defined commands. */
+
+struct cmd_list_element *cmdlist;
+
+/* Chain containing all defined info subcommands. */
+
+struct cmd_list_element *infolist;
+
+/* Chain containing all defined enable subcommands. */
+
+struct cmd_list_element *enablelist;
+
+/* Chain containing all defined disable subcommands. */
+
+struct cmd_list_element *disablelist;
+
+/* Chain containing all defined delete subcommands. */
+
+struct cmd_list_element *deletelist;
+
+/* Chain containing all defined "enable breakpoint" subcommands. */
+
+struct cmd_list_element *enablebreaklist;
+
+/* Chain containing all defined set subcommands */
+
+struct cmd_list_element *setlist;
+
+/* Chain containing all defined \"set history\". */
+
+struct cmd_list_element *sethistlist;
+
+/* Chain containing all defined \"unset history\". */
+
+struct cmd_list_element *unsethistlist;
+
+/* stdio stream that command input is being read from. */
+
+FILE *instream;
+
+/* Current working directory. */
+
+char *current_directory;
+
+/* The directory name is actually stored here (usually). */
+static char dirbuf[MAXPATHLEN];
+
+#ifdef KERNELDEBUG
+/* Nonzero if we're debugging /dev/mem or a kernel crash dump */
+
+int kernel_debugging;
+#endif
+
+/* Nonzero to inhibit confirmation of quitting or restarting
+ a stopped inferior. */
+int inhibit_confirm;
+
+/* Nonzero if we can write in text or core file */
+
+int writeable_text;
+
+/* The number of lines on a page, and the number of spaces
+ in a line. */
+int linesize, pagesize;
+
+/* Nonzero if we should refrain from using an X window. */
+
+int inhibit_windows = 0;
+
+/* Function to call before reading a command, if nonzero.
+ The function receives two args: an input stream,
+ and a prompt string. */
+
+void (*window_hook) ();
+
+extern int frame_file_full_name;
+int xgdb_verbose;
+
+void execute_command();
+void free_command_lines ();
+char *gdb_readline ();
+char *command_line_input ();
+static void initialize_main ();
+static void initialize_cmd_lists ();
+void command_loop ();
+static void source_command ();
+static void print_gdb_version ();
+static void float_handler ();
+static void cd_command ();
+
+char *getenv ();
+
+/* gdb prints this when reading a command interactively */
+static char *prompt;
+
+/* Buffer used for reading command lines, and the size
+ allocated for it so far. */
+
+char *line;
+int linesize;
+
+
+/* This is how `error' returns to command level. */
+
+jmp_buf to_top_level;
+
+void
+return_to_top_level ()
+{
+ quit_flag = 0;
+ immediate_quit = 0;
+ clear_breakpoint_commands ();
+ clear_momentary_breakpoints ();
+ disable_current_display ();
+ do_cleanups (0);
+ longjmp (to_top_level, 1);
+}
+
+/* Call FUNC with arg ARG, catching any errors.
+ If there is no error, return the value returned by FUNC.
+ If there is an error, return zero after printing ERRSTRING
+ (which is in addition to the specific error message already printed). */
+
+int
+catch_errors (func, arg, errstring)
+ int (*func) ();
+ int arg;
+ char *errstring;
+{
+ jmp_buf saved;
+ int val;
+ struct cleanup *saved_cleanup_chain;
+
+ saved_cleanup_chain = save_cleanups ();
+
+ bcopy (to_top_level, saved, sizeof (jmp_buf));
+
+ if (setjmp (to_top_level) == 0)
+ val = (*func) (arg);
+ else
+ {
+ fprintf (stderr, "%s\n", errstring);
+ val = 0;
+ }
+
+ restore_cleanups (saved_cleanup_chain);
+
+ bcopy (saved, to_top_level, sizeof (jmp_buf));
+ return val;
+}
+
+/* Handler for SIGHUP. */
+
+static void
+disconnect ()
+{
+ kill_inferior_fast ();
+ signal (SIGHUP, SIG_DFL);
+ kill (getpid (), SIGHUP);
+}
+
+/* Clean up on error during a "source" command (or execution of a
+ user-defined command).
+ Close the file opened by the command
+ and restore the previous input stream. */
+
+static void
+source_cleanup (stream)
+ FILE *stream;
+{
+ /* Instream may be 0; set to it when executing user-defined command. */
+ if (instream)
+ fclose (instream);
+ instream = stream;
+}
+
+/*
+ * Source $HOME/.gdbinit and $cwd/.gdbinit.
+ * If X is enabled, also $HOME/.xgdbinit and $cwd/.xgdbinit.source
+ */
+void
+source_init_files()
+{
+ char *homedir, initfile[256];
+ int samedir = 0;
+
+ /* Read init file, if it exists in home directory */
+ homedir = getenv ("HOME");
+ if (homedir) {
+ struct stat homebuf, cwdbuf;
+
+ sprintf(initfile, "%s/.gdbinit", homedir);
+ if (access (initfile, R_OK) == 0)
+ if (!setjmp (to_top_level))
+ source_command (initfile);
+ if (!inhibit_windows) {
+ sprintf(initfile, "%s/.xgdbinit", homedir);
+ if (access (initfile, R_OK) == 0)
+ if (!setjmp (to_top_level))
+ source_command (initfile);
+ }
+ /* Determine if current directory is the same as the home
+ directory, so we don't source the same file twice. */
+
+ bzero (&homebuf, sizeof (struct stat));
+ bzero (&cwdbuf, sizeof (struct stat));
+
+ stat(homedir, &homebuf);
+ stat(".", &cwdbuf);
+
+ samedir = bcmp(&homebuf, &cwdbuf, sizeof(struct stat)) == 0;
+ }
+ /* Read the input file in the current directory, *if* it isn't
+ the same file (it should exist, also). */
+ if (!samedir) {
+ if (access (".gdbinit", R_OK) == 0)
+ if (!setjmp (to_top_level))
+ source_command (".gdbinit");
+ if (access (".xgdbinit", R_OK) == 0)
+ if (!setjmp (to_top_level))
+ source_command (".xgdbinit");
+ }
+}
+
+
+int
+main (argc, argv, envp)
+ int argc;
+ char **argv;
+ char **envp;
+{
+ int count;
+ int inhibit_gdbinit = 0;
+ int quiet = 1;
+ int batch = 0;
+ register int i;
+ char *cp;
+
+ /* XXX Windows only for xgdb. */
+ char *strrchr();
+ if (cp = strrchr(argv[0], '/'))
+ ++cp;
+ else
+ cp = argv[0];
+ if (*cp != 'x')
+ inhibit_windows = 1;
+
+#if defined (ALIGN_STACK_ON_STARTUP)
+ i = (int) &count & 0x3;
+ if (i != 0)
+ alloca (4 - i);
+#endif
+
+ quit_flag = 0;
+ linesize = 100;
+ line = (char *) xmalloc (linesize);
+ *line = 0;
+ instream = stdin;
+
+ getwd (dirbuf);
+ current_directory = dirbuf;
+
+#ifdef SET_STACK_LIMIT_HUGE
+ {
+ struct rlimit rlim;
+
+ /* Set the stack limit huge so that alloca (particularly stringtab
+ * in dbxread.c) does not fail. */
+ getrlimit (RLIMIT_STACK, &rlim);
+ original_stack_limit = rlim.rlim_cur;
+ rlim.rlim_cur = rlim.rlim_max;
+ setrlimit (RLIMIT_STACK, &rlim);
+ }
+#endif /* SET_STACK_LIMIT_HUGE */
+
+ /* Look for flag arguments. */
+
+ for (i = 1; i < argc; i++)
+ {
+ if (!strcmp (argv[i], "-q") || !strcmp (argv[i], "-quiet"))
+ quiet = 1;
+ else if (!strcmp (argv[i], "-nx"))
+ inhibit_gdbinit = 1;
+ else if (!strcmp (argv[i], "-nw"))
+ inhibit_windows = 1;
+ else if (!strcmp (argv[i], "-batch"))
+ batch = 1, quiet = 1;
+ else if (!strcmp (argv[i], "-fullname"))
+ frame_file_full_name = 1;
+ else if (!strcmp (argv[i], "-xgdb_verbose"))
+ xgdb_verbose = 1;
+ /* -help: print a summary of command line switches. */
+ else if (!strcmp (argv[i], "-help"))
+ {
+ fputs ("\
+This is GDB, the GNU debugger. Use the command\n\
+ gdb [options] [executable [core-file]]\n\
+to enter the debugger.\n\
+\n\
+Options available are:\n\
+ -help Print this message.\n\
+ -quiet Do not print version number on startup.\n\
+ -fullname Output information used by emacs-GDB interface.\n\
+ -batch Exit after processing options.\n\
+ -nx Do not read .gdbinit file.\n\
+ -tty TTY Use TTY for input/output by the program being debugged.\n\
+ -cd DIR Change current directory to DIR.\n\
+ -directory DIR Search for source files in DIR.\n\
+ -command FILE Execute GDB commands from FILE.\n\
+ -symbols SYMFILE Read symbols from SYMFILE.\n\
+ -exec EXECFILE Use EXECFILE as the executable.\n\
+ -se FILE Use FILE as symbol file and executable file.\n\
+ -core COREFILE Analyze the core dump COREFILE.\n\
+ -k Kernel debugging.\n\
+ -w Writeable text.\n\
+ -v Print GNU message and version number on startup.\n\
+ -nc Don't confirm quit or run commands.\n\
+\n\
+For more information, type \"help\" from within GDB, or consult the\n\
+GDB manual (available as on-line info or a printed manual).\n", stderr);
+ /* Exiting after printing this message seems like
+ the most useful thing to do. */
+ exit (0);
+ }
+#ifdef KERNELDEBUG
+ else if (!strcmp (argv[i], "-k"))
+ kernel_debugging = 1;
+#endif
+ else if (!strcmp (argv[i], "-w"))
+ writeable_text = 1;
+ else if (!strcmp (argv[i], "-v"))
+ quiet = 0;
+ else if (!strcmp (argv[i], "-nc"))
+ inhibit_confirm = 1;
+ else if (argv[i][0] == '-')
+ /* Other options take arguments, so don't confuse an
+ argument with an option. */
+ i++;
+ }
+
+ /* Run the init function of each source file */
+
+ initialize_cmd_lists (); /* This needs to be done first */
+ initialize_all_files ();
+ initialize_main (); /* But that omits this file! Do it now */
+ initialize_signals ();
+
+ if (!quiet)
+ print_gdb_version ();
+
+ /* Process the command line arguments. */
+
+ count = 0;
+ for (i = 1; i < argc; i++)
+ {
+ extern void exec_file_command (), symbol_file_command ();
+ extern void core_file_command ();
+ register char *arg = argv[i];
+ /* Args starting with - say what to do with the following arg
+ as a filename. */
+ if (arg[0] == '-')
+ {
+ extern void tty_command (), directory_command ();
+
+ if (!strcmp (arg, "-q") || !strcmp (arg, "-nx")
+ || !strcmp (arg, "-quiet") || !strcmp (arg, "-batch")
+ || !strcmp (arg, "-fullname") || !strcmp (arg, "-nw")
+ || !strcmp (arg, "-xgdb_verbose")
+ || !strcmp (arg, "-help")
+ || !strcmp (arg, "-k")
+ || !strcmp (arg, "-w")
+ || !strcmp (arg, "-v")
+ || !strcmp (arg, "-nc"))
+ /* Already processed above */
+ continue;
+
+ if (++i == argc)
+ fprintf (stderr, "No argument follows \"%s\".\n", arg);
+ if (!setjmp (to_top_level))
+ {
+ /* -s foo: get syms from foo. -e foo: execute foo.
+ -se foo: do both with foo. -c foo: use foo as core dump. */
+ if (!strcmp (arg, "-se"))
+ {
+ exec_file_command (argv[i], !batch);
+ symbol_file_command (argv[i], !batch);
+ }
+ else if (!strcmp (arg, "-s") || !strcmp (arg, "-symbols"))
+ symbol_file_command (argv[i], !batch);
+ else if (!strcmp (arg, "-e") || !strcmp (arg, "-exec"))
+ exec_file_command (argv[i], !batch);
+ else if (!strcmp (arg, "-c") || !strcmp (arg, "-core"))
+ core_file_command (argv[i], !batch);
+ /* -x foo: execute commands from foo. */
+ else if (!strcmp (arg, "-x") || !strcmp (arg, "-command")
+ || !strcmp (arg, "-commands"))
+ source_command (argv[i]);
+ /* -d foo: add directory `foo' to source-file directory
+ search-list */
+ else if (!strcmp (arg, "-d") || !strcmp (arg, "-dir")
+ || !strcmp (arg, "-directory"))
+ directory_command (argv[i], 0);
+ /* -cd FOO: specify current directory as FOO.
+ GDB remembers the precise string FOO as the dirname. */
+ else if (!strcmp (arg, "-cd"))
+ {
+ cd_command (argv[i], 0);
+ init_source_path ();
+ }
+ /* -t /def/ttyp1: use /dev/ttyp1 for inferior I/O. */
+ else if (!strcmp (arg, "-t") || !strcmp (arg, "-tty"))
+ tty_command (argv[i], 0);
+
+ else
+ error ("Unknown command-line switch: \"%s\"\n", arg);
+ }
+ }
+ else
+ {
+ /* Args not thus accounted for
+ are treated as, first, the symbol/executable file
+ and, second, the core dump file. */
+ count++;
+ if (!setjmp (to_top_level))
+ switch (count)
+ {
+ case 1:
+ exec_file_command (arg, !batch);
+ symbol_file_command (arg, !batch);
+ break;
+
+ case 2:
+ core_file_command (arg, !batch);
+ break;
+
+ case 3:
+ fprintf (stderr, "Excess command line args ignored. (%s%s)\n",
+ arg, (i == argc - 1) ? "" : " ...");
+ }
+ }
+ }
+
+ if (!inhibit_gdbinit)
+ source_init_files();
+
+ if (batch)
+ {
+#if 0
+ fatal ("Attempt to read commands from stdin in batch mode.");
+#endif
+ /* We have hit the end of the batch file. */
+ exit (0);
+ }
+
+ if (!quiet)
+ printf ("Type \"help\" for a list of commands.\n");
+
+ /* The command loop. */
+
+ while (1)
+ {
+ if (!setjmp (to_top_level))
+ command_loop ();
+ if (ISATTY(stdin))
+ clearerr (stdin); /* Don't get hung if C-d is typed. */
+ else if (feof(instream)) /* Avoid endless loops for redirected stdin */
+ break;
+ }
+ exit (0);
+}
+
+
+static void
+do_nothing ()
+{
+}
+
+/* Read commands from `instream' and execute them
+ until end of file. */
+void
+command_loop ()
+{
+ struct cleanup *old_chain;
+ register int toplevel = (instream == stdin);
+ register int interactive = (toplevel && ISATTY(stdin));
+
+ while (!feof (instream))
+ {
+ register char *cmd_line;
+
+ quit_flag = 0;
+ if (interactive)
+ reinitialize_more_filter ();
+ old_chain = make_cleanup (do_nothing, 0);
+ cmd_line = command_line_input (prompt, toplevel);
+ execute_command (cmd_line, toplevel);
+ /* Do any commands attached to breakpoint we stopped at. */
+ do_breakpoint_commands ();
+ do_cleanups (old_chain);
+ }
+}
+
+/* Commands call this if they do not want to be repeated by null lines. */
+
+void
+dont_repeat ()
+{
+ /* If we aren't reading from standard input, we are saving the last
+ thing read from stdin in line and don't want to delete it. Null lines
+ won't repeat here in any case. */
+ if (instream == stdin)
+ *line = 0;
+}
+
+/* Read a line from the stream "instream" without command line editing.
+
+ It prints PROMPT once at the start.
+ Action is compatible with "readline" (i.e., space for typing is
+ malloced & should be freed by caller). */
+char *
+gdb_readline (prompt)
+ char *prompt;
+{
+ int c;
+ char *result;
+ int input_index = 0;
+ int result_size = 80;
+
+ if (prompt)
+ {
+ printf (prompt);
+ fflush (stdout);
+ }
+
+ result = (char *) xmalloc (result_size);
+
+ while (1)
+ {
+ c = fgetc (instream ? instream : stdin);
+ if (c == EOF)
+ {
+ free(result);
+ return ((char *)0);
+ }
+ if (c == '\n')
+ break;
+
+ result[input_index++] = c;
+ if (input_index >= result_size)
+ {
+ result_size <= 1;
+ result = (char *)xrealloc(result, result_size);
+ }
+ }
+ result[input_index++] = '\0';
+ return result;
+}
+
+/* Declaration for fancy readline with command line editing. */
+char *readline ();
+
+/* Variables which control command line editing and history
+ substitution. These variables are given default values at the end
+ of this file. */
+static int command_editing_p;
+static int history_expansion_p;
+static int write_history_p;
+static int history_size;
+static char *history_filename;
+
+/* Variables which are necessary for fancy command line editing. */
+char *gdb_completer_word_break_characters =
+ " \t\n!@#$%^&*()-+=|~`}{[]\"';:?/>.<,";
+
+/* Functions that are used as part of the fancy command line editing. */
+
+/* Generate symbol names one by one for the completer. If STATE is
+ zero, then we need to initialize, otherwise the initialization has
+ already taken place. TEXT is what we expect the symbol to start
+ with. RL_LINE_BUFFER is available to be looked at; it contains the
+ entire text of the line. RL_POINT is the offset in that line of
+ the cursor. You should pretend that the line ends at RL_POINT. */
+char *
+symbol_completion_function (text, state)
+ char *text;
+ int state;
+{
+ char **make_symbol_completion_list ();
+ static char **list = (char **)NULL;
+ static int index;
+ char *output;
+ extern char *rl_line_buffer;
+ extern int rl_point;
+ char *tmp_command, *p;
+ struct cmd_list_element *c, *result_list;
+
+ if (!state)
+ {
+ /* Free the storage used by LIST, but not by the strings inside. This is
+ because rl_complete_internal () frees the strings. */
+ if (list)
+ free (list);
+ list = 0;
+ index = 0;
+
+ /* Decide whether to complete on a list of gdb commands or on
+ symbols. */
+ tmp_command = (char *) alloca (rl_point + 1);
+ p = tmp_command;
+
+ strncpy (tmp_command, rl_line_buffer, rl_point);
+ tmp_command[rl_point] = '\0';
+
+ if (rl_point == 0)
+ {
+ /* An empty line we want to consider ambiguous; that is,
+ it could be any command. */
+ c = (struct cmd_list_element *) -1;
+ result_list = 0;
+ }
+ else
+ c = lookup_cmd_1 (&p, cmdlist, &result_list, 1);
+
+ /* Move p up to the next interesting thing. */
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (!c)
+ /* He's typed something unrecognizable. Sigh. */
+ list = (char **) 0;
+ else if (c == (struct cmd_list_element *) -1)
+ {
+ if (p + strlen(text) != tmp_command + rl_point)
+ error ("Unrecognized command.");
+
+ /* He's typed something ambiguous. This is easier. */
+ if (result_list)
+ list = complete_on_cmdlist (*result_list->prefixlist, text);
+ else
+ list = complete_on_cmdlist (cmdlist, text);
+ }
+ else
+ {
+ /* If we've gotten this far, gdb has recognized a full
+ command. There are several possibilities:
+
+ 1) We need to complete on the command.
+ 2) We need to complete on the possibilities coming after
+ the command.
+ 2) We need to complete the text of what comes after the
+ command. */
+
+ if (!*p && *text)
+ /* Always (might be longer versions of thie command). */
+ list = complete_on_cmdlist (result_list, text);
+ else if (!*p && !*text)
+ {
+ if (c->prefixlist)
+ list = complete_on_cmdlist (*c->prefixlist, "");
+ else
+ list = make_symbol_completion_list ("");
+ }
+ else
+ {
+ if (c->prefixlist && !c->allow_unknown)
+ {
+ *p = '\0';
+ error ("\"%s\" command requires a subcommand.",
+ tmp_command);
+ }
+ else
+ list = make_symbol_completion_list (text);
+ }
+ }
+ }
+
+ /* If the debugged program wasn't compiled with symbols, or if we're
+ clearly completing on a command and no command matches, return
+ NULL. */
+ if (!list)
+ return ((char *)NULL);
+
+ output = list[index];
+ if (output)
+ index++;
+
+ return (output);
+}
+
+
+void
+print_prompt ()
+{
+ if (prompt)
+ {
+ printf ("%s", prompt);
+ fflush (stdout);
+ }
+}
+
+
+#ifdef HAVE_TERMIO
+#include <termio.h>
+static struct termio norm_tty;
+
+static void
+suspend_sig()
+{
+ int tty = fileno(stdin);
+ struct termio cur_tty;
+
+ ioctl(tty, TCGETA, &cur_tty);
+ ioctl(tty, TCSETAW, &norm_tty);
+
+ (void) sigsetmask(0);
+ signal(SIGTSTP, SIG_DFL);
+ kill(0, SIGTSTP);
+
+ /*
+ * we've just been resumed -- current tty params become new
+ * 'normal' params (in case tset/stty was done while we were
+ * suspended). Merge values that readline might have changed
+ * into new params, then restore term mode.
+ */
+ ioctl(tty, TCGETA, &norm_tty);
+ cur_tty.c_lflag = (cur_tty.c_lflag & (ICANON|ECHO|ISIG)) |
+ (norm_tty.c_lflag &~ (ICANON|ECHO|ISIG));
+ cur_tty.c_iflag = (cur_tty.c_iflag & (IXON|ISTRIP|INPCK)) |
+ (norm_tty.c_iflag &~ (IXON|ISTRIP|INPCK));
+ ioctl(tty, TCSETAW, &cur_tty);
+
+ signal(SIGTSTP, suspend_sig);
+ print_prompt();
+
+ /*
+ * Forget about any previous command -- null line now will do
+ * nothing.
+ */
+ dont_repeat();
+}
+
+#else
+
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sgtty.h>
+
+static struct sgttyb norm_tty;
+static struct tchars norm_tchars;
+static struct ltchars norm_ltchars;
+static int norm_lflags;
+
+#ifdef PASS8
+#define RL_TFLAGS (RAW|CRMOD|ECHO|CBREAK|PASS8)
+#else
+#define RL_TFLAGS (RAW|CRMOD|ECHO|CBREAK)
+#endif
+
+static void
+suspend_sig()
+{
+ int tty = fileno(stdin);
+ struct sgttyb cur_tty;
+ struct tchars cur_tchars;
+ struct ltchars cur_ltchars;
+ int cur_lflags;
+ int cur_flags;
+
+ ioctl(tty, TIOCGETP, &cur_tty);
+ ioctl(tty, TIOCGETC, &cur_tchars);
+ ioctl(tty, TIOCLGET, &cur_lflags);
+ ioctl(tty, TIOCGLTC, &cur_ltchars);
+
+ ioctl(tty, TIOCSETP, &norm_tty);
+ ioctl(tty, TIOCSETC, &norm_tchars);
+ ioctl(tty, TIOCLSET, &norm_lflags);
+ ioctl(tty, TIOCSLTC, &norm_ltchars);
+
+ (void) sigsetmask(0);
+ signal(SIGTSTP, SIG_DFL);
+ kill(0, SIGTSTP);
+
+ /*
+ * we've just been resumed -- current tty params become new
+ * 'normal' params (in case tset/stty was done while we were
+ * suspended). Merge values that readline might have changed
+ * into new params, then restore term mode.
+ */
+ ioctl(tty, TIOCGETP, &norm_tty);
+ cur_flags = cur_tty.sg_flags;
+ cur_tty = norm_tty;
+ cur_tty.sg_flags = (cur_tty.sg_flags &~ RL_TFLAGS)
+ | (cur_flags & RL_TFLAGS);
+
+ ioctl(tty, TIOCLGET, &norm_lflags);
+#ifdef LPASS8
+ cur_lflags = (cur_lflags &~ LPASS8) | (cur_flags & LPASS8);
+#endif
+ ioctl(tty, TIOCGETC, &norm_tchars);
+ ioctl(tty, TIOCGLTC, &norm_ltchars);
+
+ ioctl(tty, TIOCSETP, &cur_tty);
+ ioctl(tty, TIOCSETC, &cur_tchars);
+ ioctl(tty, TIOCLSET, &cur_lflags);
+ ioctl(tty, TIOCSLTC, &cur_ltchars);
+
+ signal(SIGTSTP, suspend_sig);
+ print_prompt();
+
+ /*
+ * Forget about any previous command -- null line now will do
+ * nothing.
+ */
+ dont_repeat();
+}
+#endif /* HAVE_TERMIO */
+
+/* Initialize signal handlers. */
+initialize_signals ()
+{
+ extern void request_quit ();
+ int tty = fileno(stdin);
+
+ signal (SIGINT, request_quit);
+
+ /* If we initialize SIGQUIT to SIG_IGN, then the SIG_IGN will get
+ passed to the inferior, which we don't want. It would be
+ possible to do a "signal (SIGQUIT, SIG_DFL)" after we fork, but
+ on BSD4.3 systems using vfork, that will (apparently) affect the
+ GDB process as well as the inferior (the signal handling tables
+ being shared between the two, apparently). Since we establish
+ a handler for SIGQUIT, when we call exec it will set the signal
+ to SIG_DFL for us. */
+ signal (SIGQUIT, do_nothing);
+ if (signal (SIGHUP, do_nothing) != SIG_IGN)
+ signal (SIGHUP, disconnect);
+ signal (SIGFPE, float_handler);
+
+ ioctl(tty, TIOCGETP, &norm_tty);
+ ioctl(tty, TIOCLGET, &norm_lflags);
+ ioctl(tty, TIOCGETC, &norm_tchars);
+ ioctl(tty, TIOCGLTC, &norm_ltchars);
+ signal(SIGTSTP, suspend_sig);
+}
+
+char *
+finish_command_input(inputline, repeat, interactive)
+ register char *inputline;
+ int repeat;
+ int interactive;
+{
+ static char *do_free;
+
+ if (do_free) {
+ free(do_free);
+ do_free = NULL;
+ }
+
+ /* Do history expansion if that is wished. */
+ if (interactive && history_expansion_p) {
+ int expanded;
+
+ expanded = history_expand(inputline, &do_free);
+ if (expanded) {
+ /* Print the changes. */
+ puts(do_free);
+
+ /* An error acts like no input. */
+ if (expanded < 0) {
+ *do_free = 0;
+ return (do_free);
+ }
+ }
+ inputline = do_free;
+ }
+ /* get rid of any leading whitespace */
+ while (isspace(*inputline))
+ ++inputline;
+ /*
+ * If we just got an empty line, and that is supposed to repeat the
+ * previous command, return the value in the global buffer.
+ */
+ if (*inputline == 0) {
+ if (repeat)
+ return (line);
+ } else if (interactive)
+ add_history(inputline);
+
+ /*
+ * If line is a comment, clear it out.
+ * Note: comments are added to the command history. This is useful
+ * when you type a command, and then realize you don't want to
+ * execute it quite yet. You can comment out the command and then
+ * later fetch it from the value history and remove the '#'.
+ */
+ if (*inputline == '#')
+ *inputline = 0;
+ else if (repeat) {
+ /* Save into global buffer. */
+ register int i = strlen(inputline) + 1;
+
+ if (i > linesize) {
+ line = xrealloc(line, i);
+ linesize = i;
+ }
+ strcpy(line, inputline);
+ }
+ return (inputline);
+}
+
+static char *
+get_a_cmd_line(prompt, interactive)
+ char *prompt;
+ int interactive;
+{
+ register char *cp;
+
+ /* Control-C quits instantly if typed while reading input. */
+ immediate_quit++;
+ if (interactive && command_editing_p) {
+ extern void (*rl_event_hook)();
+
+ rl_event_hook = window_hook;
+ cp = readline(prompt);
+ } else {
+ if (interactive) {
+ if (window_hook) {
+ print_prompt();
+ (*window_hook)();
+ }
+ } else
+ prompt = NULL;
+ cp = gdb_readline(prompt);
+ }
+ --immediate_quit;
+ return (cp);
+}
+
+/* Read one line from the command input stream `instream'
+ Returns the address of the start of the line.
+
+ *If* the instream == stdin & stdin is a terminal, the line read
+ is copied into the file line saver (global var char *line,
+ length linesize) so that it can be duplicated.
+
+ This routine either uses fancy command line editing or
+ simple input as the user has requested. */
+
+char *
+command_line_input(prompt, repeat)
+ char *prompt;
+ int repeat;
+{
+ static char *do_free;
+ register int interactive = (instream == stdin && ISATTY(instream));
+ register char *cp;
+ register int i;
+
+ if (do_free) {
+ free(do_free);
+ do_free = NULL;
+ }
+ cp = get_a_cmd_line(prompt, interactive);
+
+ /*
+ * handle continued lines (this loop is not particularly
+ * efficient because it's rare).
+ */
+ while (cp && cp[i = strlen(cp) - 1] == '\\') {
+ register char *np = get_a_cmd_line(prompt, interactive);
+ register int j;
+
+ if (np == NULL) {
+ cp[i] = 0;
+ break;
+ }
+ j = strlen(np);
+ cp = xrealloc(cp, i + j + 1);
+ strcpy(cp + i, np);
+ free(np);
+ }
+ if (cp == NULL)
+ return ("");
+ do_free = cp;
+ return (finish_command_input(cp, repeat, interactive));
+}
+
+
+#define MAX_USER_ARGS 32
+
+static struct user_args {
+ struct {
+ char *arg;
+ int len;
+ } a[10];
+} uargs[MAX_USER_ARGS];
+
+static struct user_args *user_arg = uargs;
+
+static void
+arg_cleanup(ap)
+ struct user_args *ap;
+{
+ user_arg = ap;
+}
+
+/* Bind arguments $arg0, $arg1, ..., for a user defined command. */
+struct cleanup *
+setup_user_args(p)
+ char *p;
+{
+ register int i;
+ struct cleanup *old_chain = make_cleanup(arg_cleanup, user_arg);
+
+ if (++user_arg >= &uargs[MAX_USER_ARGS])
+ error("user defined functions nested too deeply\n");
+
+ bzero(user_arg, sizeof(*user_arg));
+
+ i = 0;
+ while (*p) {
+ while (isspace(*p))
+ ++p;
+ user_arg->a[i].arg = p;
+ while (*p && ! isspace(*p))
+ ++p;
+ user_arg->a[i].len = p - user_arg->a[i].arg;
+ ++i;
+ }
+ return (old_chain);
+}
+
+static char *
+findarg(str)
+ register char *str;
+{
+ register char *cp = str;
+ extern char *index();
+
+ while (cp = index(cp, '$')) {
+ if (strncmp(cp, "$arg", 4) == 0 && isdigit(cp[4]))
+ return (cp);
+ ++cp;
+ }
+ return (char *)0;
+}
+
+/* expand arguments from "line" into "new" */
+static void
+expand_args(line, new)
+ register char *line, *new;
+{
+ register char *cp = findarg(line);
+
+ while (cp = findarg(line)) {
+ int i, len;
+
+ bcopy(line, new, cp - line);
+ new += cp - line;
+ i = cp[4] - '0';
+ if (len = user_arg->a[i].len) {
+ bcopy(user_arg->a[i].arg, new, len);
+ new += len;
+ }
+ line = cp + 5;
+ }
+ strcpy(new, line);
+}
+
+/* expand any arguments in "line" then execute the result */
+static void
+expand_and_execute(line, from_tty)
+ char *line;
+ int from_tty;
+{
+ void execute_command();
+ char new[1024];
+
+ if (! findarg(line)) {
+ execute_command(line, from_tty);
+ return;
+ }
+ expand_args(line, new);
+ execute_command(new, from_tty);
+}
+
+char *
+read_one_command_line(prompt, from_tty)
+ char *prompt;
+{
+ register char *p, *p1;
+
+ dont_repeat();
+ p = command_line_input(prompt, from_tty);
+
+ /* Remove trailing blanks. */
+ p1 = p + strlen(p);
+ while (--p1 > p && (*p1 == ' ' || *p1 == '\t'))
+ ;
+ *++p1 = 0;
+ return (p);
+}
+
+static char cmd_prompt[] = " > ";
+
+int
+parse_control_structure(rootcmd, from_tty, level)
+ struct command_line *rootcmd;
+ int from_tty;
+{
+ struct command_line *cmd = (struct command_line *)xmalloc(sizeof(*cmd));
+ char *prompt;
+
+ ++level;
+ prompt = from_tty? &cmd_prompt[sizeof(cmd_prompt) - 1 - 2*level] :
+ (char *)0;
+ bzero(cmd, sizeof(*cmd));
+ rootcmd->body = cmd;
+ while (1) {
+ char *p = read_one_command_line(prompt, from_tty);
+
+ p = savestring(p, strlen(p));
+ cmd->line = p;
+ if (!strncmp(p, "while ", 6)) {
+ cmd->type = CL_WHILE;
+ if (parse_control_structure(cmd, from_tty, level))
+ return (1);
+ } else if (!strncmp(p, "if ", 3)) {
+ cmd->type = CL_IF;
+ if (parse_control_structure(cmd, from_tty, level)) {
+ struct command_line *tmp;
+ int stat;
+
+ cmd->elsebody = cmd->body;
+ stat = parse_control_structure(cmd, from_tty,
+ level);
+ tmp = cmd->elsebody;
+ cmd->elsebody = cmd->body;
+ cmd->body = tmp;
+ if (stat)
+ return (1);
+ }
+ } else if (!strcmp(p, "else")) {
+ cmd->type = CL_END;
+ return (1);
+ } else if (!strcmp(p, "end")) {
+ cmd->type = CL_END;
+ return (0);
+ } else if (!strcmp(p, "exitloop")) {
+ cmd->type = CL_EXITLOOP;
+ } else {
+ cmd->type = CL_NORMAL;
+ }
+ cmd->next = (struct command_line *)xmalloc(sizeof(*cmd));
+ cmd = cmd->next;
+ bzero(cmd, sizeof(*cmd));
+ }
+ /* NOTREACHED */
+}
+
+int
+execute_control_structure(cmd)
+ register struct command_line *cmd;
+{
+ char expn[1024];
+ struct expression *cond;
+ int stat;
+
+ while (cmd) {
+ QUIT;
+ switch (cmd->type) {
+ case CL_END:
+ return (0);
+ case CL_NORMAL:
+ expand_and_execute(cmd->line, 0);
+ break;
+ case CL_WHILE:
+ expand_args(cmd->line + 6, expn);
+ cond = parse_c_expression(expn);
+ while (breakpoint_cond_eval(cond) == 0)
+ if (execute_control_structure(cmd->body))
+ break;
+ free(cond);
+ break;
+ case CL_IF:
+ expand_args(cmd->line + 3, expn);
+ cond = parse_c_expression(expn);
+ stat = breakpoint_cond_eval(cond);
+ free(cond);
+ if (stat == 0) {
+ if (execute_control_structure(cmd->body))
+ return (1);
+ } else if (cmd->elsebody) {
+ if (execute_control_structure(cmd->elsebody))
+ return (1);
+ }
+ break;
+ case CL_EXITLOOP:
+ return (1);
+ }
+ cmd = cmd->next;
+ }
+ free_all_values();
+}
+
+execute_command_lines(cmd)
+ struct command_line *cmd;
+{
+ struct cleanup *old_chain = make_cleanup(source_cleanup, instream);
+
+ /*
+ * Set the instream to 0, indicating execution of a user-defined
+ * function.
+ */
+ ++immediate_quit;
+ instream = (FILE *) 0;
+ (void)execute_control_structure(cmd);
+ --immediate_quit;
+ do_cleanups(old_chain);
+}
+
+/* do following command lines if expression true */
+if_command(p, from_tty)
+ char *p;
+ int from_tty;
+{
+ struct cleanup *old_chain;
+ struct command_line *cmd = (struct command_line *)xmalloc(sizeof(*cmd));
+ char buf[128];
+
+ sprintf(buf, "if %s", p);
+
+ bzero(cmd, sizeof(*cmd));
+ old_chain = make_cleanup(free_command_lines, cmd);
+ cmd->type = CL_IF;
+ cmd->line = savestring(buf, strlen(buf));
+ /* XXX cmd->line? */
+ if (parse_control_structure(cmd, from_tty, 0)) {
+ struct command_line *tmp;
+
+ cmd->elsebody = cmd->body;
+ (void) parse_control_structure(cmd, from_tty, 0);
+ tmp = cmd->elsebody;
+ cmd->elsebody = cmd->body;
+ cmd->body = tmp;
+ }
+ (void) execute_command_lines(cmd);
+ do_cleanups(old_chain);
+}
+
+/* do following command lines while expression true */
+while_command(p, from_tty)
+ char *p;
+ int from_tty;
+{
+ struct cleanup *old_chain;
+ struct command_line *cmd = (struct command_line *)xmalloc(sizeof(*cmd));
+ char buf[128];
+
+ sprintf(buf, "while %s", p);
+
+ bzero(cmd, sizeof(*cmd));
+ old_chain = make_cleanup(free_command_lines, cmd);
+ cmd->type = CL_WHILE;
+ cmd->line = savestring(buf, strlen(buf));
+ (void)parse_control_structure(cmd, from_tty, 0);
+ (void)execute_command_lines(cmd);
+ do_cleanups(old_chain);
+}
+
+/*
+ * Execute the line P as a command.
+ * Pass FROM_TTY as second argument to the defining function.
+ */
+void
+execute_command (p, from_tty)
+ char *p;
+ int from_tty;
+{
+ register struct cmd_list_element *c;
+ register struct command_line *cmdlines;
+
+ free_all_values();
+ if (*p) {
+ c = lookup_cmd(&p, cmdlist, "", 0, 1);
+ if (c->function == 0)
+ error("That is not a command, just a help topic.");
+ else if (c->class == (int) class_user) {
+ struct cleanup *old_chain = setup_user_args(p);
+
+ cmdlines = (struct command_line *) c->function;
+ if (cmdlines)
+ (void)execute_command_lines(cmdlines);
+
+ do_cleanups(old_chain);
+ } else
+ /* Pass null arg rather than an empty one. */
+ (*c->function) (*p ? p : 0, from_tty);
+ }
+}
+
+/*
+ * Read lines from the input stream and accumulate them in a chain of struct
+ * command_line's which is then returned.
+ */
+struct command_line *
+read_command_lines(from_tty)
+ int from_tty;
+{
+ struct cleanup *old_chain;
+ struct command_line *cmd = (struct command_line *)xmalloc(sizeof(*cmd));
+ struct command_line *next;
+
+ bzero(cmd, sizeof(*cmd));
+ old_chain = make_cleanup(free_command_lines, cmd);
+ cmd->type = CL_NOP;
+ (void)parse_control_structure(cmd, from_tty, 0);
+ dont_repeat();
+ discard_cleanups(old_chain);
+ next = cmd->body;
+ free(cmd);
+ return (next);
+}
+
+/* Free a chain of struct command_line's. */
+
+void
+free_command_lines(cmds)
+ struct command_line *cmds;
+{
+ struct command_line *next;
+
+ while (cmds) {
+ if (cmds->body)
+ free(cmds->body);
+ if (cmds->elsebody)
+ free(cmds->elsebody);
+ if (cmds->line)
+ free(cmds->line);
+ next = cmds->next;
+ free(cmds);
+ cmds = next;
+ }
+}
+
+/* Add an element to the list of info subcommands. */
+
+void
+add_info (name, fun, doc)
+ char *name;
+ void (*fun) ();
+ char *doc;
+{
+ add_cmd (name, no_class, fun, doc, &infolist);
+}
+
+/* Add an alias to the list of info subcommands. */
+
+void
+add_info_alias (name, oldname, abbrev_flag)
+ char *name;
+ char *oldname;
+ int abbrev_flag;
+{
+ add_alias_cmd (name, oldname, 0, abbrev_flag, &infolist);
+}
+
+/* The "info" command is defined as a prefix, with allow_unknown = 0.
+ Therefore, its own definition is called only for "info" with no args. */
+
+static void
+info_command ()
+{
+ printf ("\"info\" must be followed by the name of an info command.\n");
+ help_list (infolist, "info ", -1, stdout);
+}
+
+/* Add an element to the list of commands. */
+
+void
+add_com (name, class, fun, doc)
+ char *name;
+ int class;
+ void (*fun) ();
+ char *doc;
+{
+ add_cmd (name, class, fun, doc, &cmdlist);
+}
+
+/* Add an alias or abbreviation command to the list of commands. */
+
+void
+add_com_alias (name, oldname, class, abbrev_flag)
+ char *name;
+ char *oldname;
+ int class;
+ int abbrev_flag;
+{
+ add_alias_cmd (name, oldname, class, abbrev_flag, &cmdlist);
+}
+
+void
+error_no_arg (why)
+ char *why;
+{
+ error ("Argument required (%s).", why);
+}
+
+static void
+help_command (command, from_tty)
+ char *command;
+ int from_tty; /* Ignored */
+{
+ help_cmd (command, stdout);
+}
+
+static void
+validate_comname (comname)
+ char *comname;
+{
+ register char *p;
+
+ if (comname == 0)
+ error_no_arg ("name of command to define");
+
+ p = comname;
+ while (*p)
+ {
+ if (!(*p >= 'A' && *p <= 'Z')
+ && !(*p >= 'a' && *p <= 'z')
+ && !(*p >= '0' && *p <= '9')
+ && *p != '-')
+ error ("Junk in argument list: \"%s\"", p);
+ p++;
+ }
+}
+
+static void
+define_command (comname, from_tty)
+ char *comname;
+ int from_tty;
+{
+ register struct command_line *cmds;
+ register struct cmd_list_element *c;
+ char *tem = comname;
+
+ validate_comname (comname);
+
+ c = lookup_cmd (&tem, cmdlist, "", -1, 1);
+ if (c)
+ {
+ if (c->class == (int) class_user || c->class == (int) class_alias)
+ tem = "Redefine command \"%s\"? ";
+ else
+ tem = "Really redefine built-in command \"%s\"? ";
+ if (!query (tem, comname))
+ error ("Command \"%s\" not redefined.", comname);
+ }
+
+ if (from_tty)
+ {
+ printf ("Type commands for definition of \"%s\".\n\
+End with a line saying just \"end\".\n", comname);
+ fflush (stdout);
+ }
+ comname = savestring (comname, strlen (comname));
+
+ cmds = read_command_lines (from_tty);
+
+ if (c && c->class == (int) class_user)
+ free_command_lines (c->function);
+
+ add_com (comname, class_user, cmds,
+ (c && c->class == (int) class_user)
+ ? c->doc : savestring ("User-defined.", 13));
+}
+
+static void
+document_command (comname, from_tty)
+ char *comname;
+ int from_tty;
+{
+ register struct cmd_list_element *c;
+ register char *p;
+ register char *cp;
+ register char *doc = 0;
+ register int len;
+ char *tmp = comname;
+
+ validate_comname (comname);
+ c = lookup_cmd (&tmp, cmdlist, "", 0, 1);
+ if (c->class != (int) class_user)
+ error ("Command \"%s\" is built-in.", comname);
+
+ if (from_tty)
+ printf ("Type documentation for \"%s\". \
+End with a line saying just \"end\".\n", comname);
+
+ while (p = read_one_command_line(from_tty? "> " : 0, from_tty))
+ {
+ if (strcmp(p, "end") == 0)
+ break;
+ len = strlen(p) + 1;
+ if (! doc)
+ {
+ doc = xmalloc(len);
+ cp = doc;
+ }
+ else
+ {
+ int i = cp - doc;
+ doc = xrealloc(doc, i + len);
+ cp = doc + i;
+ }
+ strcpy(cp, p);
+ cp += len;
+ cp[-1] = '\n';
+ }
+ if (doc && cp > doc)
+ cp[-1] = 0;
+ if (c->doc)
+ free (c->doc);
+ c->doc = doc;
+}
+
+static void
+print_gdb_version ()
+{
+ printf ("GDB %s, Copyright (C) 1989 Free Software Foundation, Inc.\n\
+There is ABSOLUTELY NO WARRANTY for GDB; type \"info warranty\" for details.\n\
+GDB is free software and you are welcome to distribute copies of it\n\
+ under certain conditions; type \"info copying\" to see the conditions.\n",
+ version);
+}
+
+static void
+version_info ()
+{
+ immediate_quit++;
+ print_gdb_version ();
+ immediate_quit--;
+}
+
+
+/* Command to specify a prompt string instead of "(gdb) ". */
+
+void
+set_prompt_command (text)
+ char *text;
+{
+ char *p, *q;
+ register int c;
+ char *new;
+
+ if (text == 0)
+ error_no_arg ("string to which to set prompt");
+
+ new = (char *) xmalloc (strlen (text) + 2);
+ p = text; q = new;
+ while (c = *p++)
+ {
+ if (c == '\\')
+ {
+ /* \ at end of argument is used after spaces
+ so they won't be lost. */
+ if (*p == 0)
+ break;
+ c = parse_escape (&p);
+ if (c == 0)
+ break; /* C loses */
+ else if (c > 0)
+ *q++ = c;
+ }
+ else
+ *q++ = c;
+ }
+ if (*(p - 1) != '\\')
+ *q++ = ' ';
+ *q++ = '\0';
+ new = (char *) xrealloc (new, q - new);
+ free (prompt);
+ prompt = new;
+}
+
+static void
+quit_command ()
+{
+ extern void exec_file_command ();
+ if (have_inferior_p ())
+ {
+ if (inhibit_confirm || query ("The program is running. Quit anyway? "))
+ {
+ /* Prevent any warning message from reopen_exec_file, in case
+ we have a core file that's inconsistent with the exec file. */
+ exec_file_command (0, 0);
+ kill_inferior ();
+ }
+ else
+ error ("Not confirmed.");
+ }
+ /* Save the history information if it is appropriate to do so. */
+ if (write_history_p && history_filename)
+ write_history (history_filename);
+ exit (0);
+}
+
+int
+input_from_terminal_p ()
+{
+ return instream == stdin;
+}
+
+static void
+pwd_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ if (arg) error ("The \"pwd\" command does not take an argument: %s", arg);
+ getwd (dirbuf);
+
+ if (strcmp (dirbuf, current_directory))
+ printf ("Working directory %s\n (canonically %s).\n",
+ current_directory, dirbuf);
+ else
+ printf ("Working directory %s.\n", current_directory);
+}
+
+static void
+cd_command (dir, from_tty)
+ char *dir;
+ int from_tty;
+{
+ int len;
+ int change;
+
+ if (dir == 0)
+ error_no_arg ("new working directory");
+
+ dir = tilde_expand (dir);
+ make_cleanup (free, dir);
+
+ len = strlen (dir);
+ dir = savestring (dir, len - (len > 1 && dir[len-1] == '/'));
+ if (dir[0] == '/')
+ current_directory = dir;
+ else
+ {
+ current_directory = concat (current_directory, "/", dir);
+ free (dir);
+ }
+
+ /* Now simplify any occurrences of `.' and `..' in the pathname. */
+
+ change = 1;
+ while (change)
+ {
+ char *p;
+ change = 0;
+
+ for (p = current_directory; *p;)
+ {
+ if (!strncmp (p, "/./", 2)
+ && (p[2] == 0 || p[2] == '/'))
+ strcpy (p, p + 2);
+ else if (!strncmp (p, "/..", 3)
+ && (p[3] == 0 || p[3] == '/')
+ && p != current_directory)
+ {
+ char *q = p;
+ while (q != current_directory && q[-1] != '/') q--;
+ if (q != current_directory)
+ {
+ strcpy (q-1, p+3);
+ p = q-1;
+ }
+ }
+ else p++;
+ }
+ }
+
+ if (chdir (dir) < 0)
+ perror_with_name (dir);
+
+ if (from_tty)
+ pwd_command ((char *) 0, 1);
+}
+
+static void
+source_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ FILE *stream;
+ struct cleanup *cleanups;
+ char *file = arg;
+ char *path;
+
+ if (file == 0)
+ /* Let source without arguments read .gdbinit. */
+ file = ".gdbinit";
+
+ file = tilde_expand (file);
+ make_cleanup (free, file);
+
+#ifdef KERNELDEBUG
+ if (path = getenv(kernel_debugging? "KGDBPATH" : "GDBPATH"))
+#else
+ if (path = getenv("GDBPATH"))
+#endif
+ {
+ int fd = openp(path, 1, file, O_RDONLY, 0, 0);
+
+ if (fd == -1)
+ stream = 0;
+ else
+ stream = fdopen(fd, "r");
+ }
+ else
+ stream = fopen (file, "r");
+
+ if (stream == 0)
+ perror_with_name (file);
+
+ cleanups = make_cleanup (source_cleanup, instream);
+
+ instream = stream;
+
+ command_loop ();
+
+ do_cleanups (cleanups);
+}
+
+static void
+echo_command (text)
+ char *text;
+{
+ char *p = text;
+ register int c;
+
+ if (text)
+ while (c = *p++)
+ {
+ if (c == '\\')
+ {
+ /* \ at end of argument is used after spaces
+ so they won't be lost. */
+ if (*p == 0)
+ return;
+
+ c = parse_escape (&p);
+ if (c >= 0)
+ fputc (c, stdout);
+ }
+ else
+ fputc (c, stdout);
+ }
+ fflush(stdout);
+}
+
+static void
+dump_me_command ()
+{
+ if (query ("Should GDB dump core? "))
+ {
+ signal (SIGQUIT, SIG_DFL);
+ kill (getpid (), SIGQUIT);
+ }
+}
+
+int
+parse_binary_operation (caller, arg)
+ char *caller, *arg;
+{
+ int length;
+
+ if (!arg || !*arg)
+ return 1;
+
+ length = strlen (arg);
+
+ while (arg[length - 1] == ' ' || arg[length - 1] == '\t')
+ length--;
+
+ if (!strncmp (arg, "on", length)
+ || !strncmp (arg, "1", length)
+ || !strncmp (arg, "yes", length))
+ return 1;
+ else
+ if (!strncmp (arg, "off", length)
+ || !strncmp (arg, "0", length)
+ || !strncmp (arg, "no", length))
+ return 0;
+ else
+ error ("\"%s\" not given a binary valued argument.", caller);
+}
+
+/* Functions to manipulate command line editing control variables. */
+
+static void
+set_editing (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ command_editing_p = parse_binary_operation ("set command-editing", arg);
+}
+
+/* Number of commands to print in each call to editing_info. */
+#define Hist_print 10
+static void
+editing_info (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ /* Index for history commands. Relative to history_base. */
+ int offset;
+
+ /* Number of the history entry which we are planning to display next.
+ Relative to history_base. */
+ static int num = 0;
+
+ /* The first command in the history which doesn't exist (i.e. one more
+ than the number of the last command). Relative to history_base. */
+ int hist_len;
+
+ struct _hist_entry {
+ char *line;
+ char *data;
+ } *history_get();
+ extern int history_base;
+
+ printf_filtered ("Interactive command editing is %s.\n",
+ command_editing_p ? "on" : "off");
+
+ printf_filtered ("History expansion of command input is %s.\n",
+ history_expansion_p ? "on" : "off");
+ printf_filtered ("Writing of a history record upon exit is %s.\n",
+ write_history_p ? "enabled" : "disabled");
+ printf_filtered ("The size of the history list (number of stored commands) is %d.\n",
+ history_size);
+ printf_filtered ("The name of the history record is \"%s\".\n\n",
+ history_filename ? history_filename : "");
+
+ /* Print out some of the commands from the command history. */
+ /* First determine the length of the history list. */
+ hist_len = history_size;
+ for (offset = 0; offset < history_size; offset++)
+ {
+ if (!history_get (history_base + offset))
+ {
+ hist_len = offset;
+ break;
+ }
+ }
+
+ if (arg)
+ {
+ if (arg[0] == '+' && arg[1] == '\0')
+ /* "info editing +" should print from the stored position. */
+ ;
+ else
+ /* "info editing <exp>" should print around command number <exp>. */
+ num = (parse_and_eval_address (arg) - history_base) - Hist_print / 2;
+ }
+ /* "info editing" means print the last Hist_print commands. */
+ else
+ {
+ num = hist_len - Hist_print;
+ }
+
+ if (num < 0)
+ num = 0;
+
+ /* If there are at least Hist_print commands, we want to display the last
+ Hist_print rather than, say, the last 6. */
+ if (hist_len - num < Hist_print)
+ {
+ num = hist_len - Hist_print;
+ if (num < 0)
+ num = 0;
+ }
+
+ if (num == hist_len - Hist_print)
+ printf_filtered ("The list of the last %d commands is:\n\n", Hist_print);
+ else
+ printf_filtered ("Some of the stored commands are:\n\n");
+
+ for (offset = num; offset < num + Hist_print && offset < hist_len; offset++)
+ {
+ printf_filtered ("%5d %s\n", history_base + offset,
+ (history_get (history_base + offset))->line);
+ }
+
+ /* The next command we want to display is the next one that we haven't
+ displayed yet. */
+ num += Hist_print;
+
+ /* If the user repeats this command with return, it should do what
+ "info editing +" does. This is unnecessary if arg is null,
+ because "info editing +" is not useful after "info editing". */
+ if (from_tty && arg)
+ {
+ arg[0] = '+';
+ arg[1] = '\0';
+ }
+}
+
+static void
+set_history_expansion (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ history_expansion_p = parse_binary_operation ("set history expansion", arg);
+}
+
+static void
+set_history_write (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ write_history_p = parse_binary_operation ("set history write", arg);
+}
+
+static void
+set_history (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ printf ("\"set history\" must be followed by the name of a history subcommand.\n");
+ help_list (sethistlist, "set history ", -1, stdout);
+}
+
+static void
+set_history_size (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ if (!*arg)
+ error_no_arg ("set history size");
+
+ history_size = atoi (arg);
+}
+
+static void
+set_history_filename (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ int i;
+
+ if (!arg)
+ error_no_arg ("history file name");
+
+ arg = tilde_expand (arg);
+ make_cleanup (free, arg);
+
+ i = strlen (arg) - 1;
+
+ free (history_filename);
+
+ while (i > 0 && (arg[i] == ' ' || arg[i] == '\t'))
+ i--;
+ ++i;
+
+ if (!*arg)
+ history_filename = (char *) 0;
+ else
+ history_filename = savestring (arg, i + 1);
+ history_filename[i] = '\0';
+}
+
+int info_verbose;
+
+static void
+set_verbose_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ info_verbose = parse_binary_operation ("set verbose", arg);
+}
+
+static void
+verbose_info (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ if (arg)
+ error ("\"info verbose\" does not take any arguments.\n");
+
+ printf ("Verbose printing of information is %s.\n",
+ info_verbose ? "on" : "off");
+}
+
+static void
+float_handler ()
+{
+ error ("Invalid floating value encountered or computed.");
+}
+
+
+static void
+initialize_cmd_lists ()
+{
+ cmdlist = (struct cmd_list_element *) 0;
+ infolist = (struct cmd_list_element *) 0;
+ enablelist = (struct cmd_list_element *) 0;
+ disablelist = (struct cmd_list_element *) 0;
+ deletelist = (struct cmd_list_element *) 0;
+ enablebreaklist = (struct cmd_list_element *) 0;
+ setlist = (struct cmd_list_element *) 0;
+ sethistlist = (struct cmd_list_element *) 0;
+ unsethistlist = (struct cmd_list_element *) 0;
+}
+
+static void
+initialize_main ()
+{
+ char *tmpenv;
+ /* Command line editing externals. */
+ extern int (*rl_completion_entry_function)();
+ extern char *rl_completer_word_break_characters;
+ extern char *rl_readline_name;
+
+ /* Set default verbose mode on. */
+ info_verbose = 1;
+
+#ifdef KERNELDEBUG
+ if (kernel_debugging)
+ prompt = savestring ("(kgdb) ", 7);
+ else
+#endif
+ prompt = savestring ("(gdb) ", 6);
+
+ /* Set the important stuff up for command editing. */
+ command_editing_p = 1;
+ history_expansion_p = 0;
+ write_history_p = 0;
+
+ if (tmpenv = getenv ("HISTSIZE"))
+ history_size = atoi (tmpenv);
+ else
+ history_size = 256;
+
+ stifle_history (history_size);
+
+ if (tmpenv = getenv ("GDBHISTFILE"))
+ history_filename = savestring (tmpenv, strlen(tmpenv));
+ else
+ /* We include the current directory so that if the user changes
+ directories the file written will be the same as the one
+ that was read. */
+ history_filename = concat (current_directory, "/.gdb_history", "");
+
+ read_history (history_filename);
+
+ /* Setup important stuff for command line editing. */
+ rl_completion_entry_function = (int (*)()) symbol_completion_function;
+ rl_completer_word_break_characters = gdb_completer_word_break_characters;
+ rl_readline_name = "gdb";
+
+ /* Define the classes of commands.
+ They will appear in the help list in the reverse of this order. */
+
+ add_cmd ("obscure", class_obscure, 0, "Obscure features.", &cmdlist);
+ add_cmd ("alias", class_alias, 0, "Aliases of other commands.", &cmdlist);
+ add_cmd ("user", class_user, 0, "User-defined commands.\n\
+The commands in this class are those defined by the user.\n\
+Use the \"define\" command to define a command.", &cmdlist);
+ add_cmd ("support", class_support, 0, "Support facilities.", &cmdlist);
+ add_cmd ("status", class_info, 0, "Status inquiries.", &cmdlist);
+ add_cmd ("files", class_files, 0, "Specifying and examining files.", &cmdlist);
+ add_cmd ("breakpoints", class_breakpoint, 0, "Making program stop at certain points.", &cmdlist);
+ add_cmd ("data", class_vars, 0, "Examining data.", &cmdlist);
+ add_cmd ("stack", class_stack, 0, "Examining the stack.\n\
+The stack is made up of stack frames. Gdb assigns numbers to stack frames\n\
+counting from zero for the innermost (currently executing) frame.\n\n\
+At any time gdb identifies one frame as the \"selected\" frame.\n\
+Variable lookups are done with respect to the selected frame.\n\
+When the program being debugged stops, gdb selects the innermost frame.\n\
+The commands below can be used to select other frames by number or address.",
+ &cmdlist);
+ add_cmd ("running", class_run, 0, "Running the program.", &cmdlist);
+
+ add_com ("pwd", class_files, pwd_command,
+ "Print working directory. This is used for your program as well.");
+ add_com ("cd", class_files, cd_command,
+ "Set working directory to DIR for debugger and program being debugged.\n\
+The change does not take effect for the program being debugged\n\
+until the next time it is started.");
+
+ add_cmd ("prompt", class_support, set_prompt_command,
+ "Change gdb's prompt from the default of \"(gdb)\"",
+ &setlist);
+ add_com ("echo", class_support, echo_command,
+ "Print a constant string. Give string as argument.\n\
+C escape sequences may be used in the argument.\n\
+No newline is added at the end of the argument;\n\
+use \"\\n\" if you want a newline to be printed.\n\
+Since leading and trailing whitespace are ignored in command arguments,\n\
+if you want to print some you must use \"\\\" before leading whitespace\n\
+to be printed or after trailing whitespace.");
+ add_com ("document", class_support, document_command,
+ "Document a user-defined command.\n\
+Give command name as argument. Give documentation on following lines.\n\
+End with a line of just \"end\".");
+ add_com ("define", class_support, define_command,
+ "Define a new command name. Command name is argument.\n\
+Definition appears on following lines, one command per line.\n\
+End with a line of just \"end\".\n\
+Use the \"document\" command to give documentation for the new command.\n\
+Commands defined in this way do not take arguments.");
+
+ add_com ("source", class_support, source_command,
+ "Read commands from a file named FILE.\n\
+Note that the file \".gdbinit\" is read automatically in this way\n\
+when gdb is started.");
+ add_com ("quit", class_support, quit_command, "Exit gdb.");
+ add_com ("help", class_support, help_command, "Print list of commands.");
+ add_com_alias ("q", "quit", class_support, 1);
+ add_com_alias ("h", "help", class_support, 1);
+ add_com ("while", class_support, while_command,
+ "execute following commands while condition is true.\n\
+Expression for condition follows \"while\" keyword.");
+ add_com ("if", class_support, if_command,
+ "execute following commands if condition is true.\n\
+Expression for condition follows \"if\" keyword.");
+ add_cmd ("verbose", class_support, set_verbose_command,
+ "Change the number of informational messages gdb prints.",
+ &setlist);
+ add_info ("verbose", verbose_info,
+ "Status of gdb's verbose printing option.\n");
+
+ add_com ("dump-me", class_obscure, dump_me_command,
+ "Get fatal error; make debugger dump its core.");
+
+ add_cmd ("editing", class_support, set_editing,
+ "Enable or disable command line editing.\n\
+Use \"on\" to enable to enable the editing, and \"off\" to disable it.\n\
+Without an argument, command line editing is enabled.", &setlist);
+
+ add_prefix_cmd ("history", class_support, set_history,
+ "Generic command for setting command history parameters.",
+ &sethistlist, "set history ", 0, &setlist);
+
+ add_cmd ("expansion", no_class, set_history_expansion,
+ "Enable or disable history expansion on command input.\n\
+Without an argument, history expansion is enabled.", &sethistlist);
+
+ add_cmd ("write", no_class, set_history_write,
+ "Enable or disable saving of the history record on exit.\n\
+Use \"on\" to enable to enable the saving, and \"off\" to disable it.\n\
+Without an argument, saving is enabled.", &sethistlist);
+
+ add_cmd ("size", no_class, set_history_size,
+ "Set the size of the command history, \n\
+ie. the number of previous commands to keep a record of.", &sethistlist);
+
+ add_cmd ("filename", no_class, set_history_filename,
+ "Set the filename in which to record the command history\n\
+ (the list of previous commands of which a record is kept).", &sethistlist);
+
+ add_prefix_cmd ("info", class_info, info_command,
+ "Generic command for printing status.",
+ &infolist, "info ", 0, &cmdlist);
+ add_com_alias ("i", "info", class_info, 1);
+
+ add_info ("editing", editing_info, "Status of command editor.");
+
+ add_info ("version", version_info, "Report what version of GDB this is.");
+}
diff --git a/gnu/usr.bin/gdb/mmalloc/Makefile b/gnu/usr.bin/gdb/mmalloc/Makefile
new file mode 100644
index 0000000..1ef0d1d
--- /dev/null
+++ b/gnu/usr.bin/gdb/mmalloc/Makefile
@@ -0,0 +1,10 @@
+LIB= mmalloc
+SRCS= mcalloc.c mfree.c mmalloc.c mmcheck.c mmemalign.c mmstats.c \
+ mmtrace.c mrealloc.c mvalloc.c mmap-sup.c attach.c detach.c keys.c \
+ sbrk-sup.c
+
+NOPROFILE=no
+NOPIC=no
+install:
+ @echo -n
+.include <bsd.lib.mk>
diff --git a/gnu/usr.bin/gdb/mmalloc/README.FreeBSD b/gnu/usr.bin/gdb/mmalloc/README.FreeBSD
new file mode 100644
index 0000000..338400f
--- /dev/null
+++ b/gnu/usr.bin/gdb/mmalloc/README.FreeBSD
@@ -0,0 +1,7 @@
+This is a greatly pared down libmmalloc directory. Only what's required to build
+gdb-4.12 on FreeBSD was kept.
+
+This is temporary. In FreeBSD 2.0 a fully ported libmmalloc will likely appear
+as a system library for use by all the build tools.
+
+paul@freefall.cdrom.com
diff --git a/gnu/usr.bin/gdb/mmalloc/attach.c b/gnu/usr.bin/gdb/mmalloc/attach.c
new file mode 100644
index 0000000..6737fca
--- /dev/null
+++ b/gnu/usr.bin/gdb/mmalloc/attach.c
@@ -0,0 +1,218 @@
+/* Initialization for access to a mmap'd malloc managed region.
+ Copyright 1992 Free Software Foundation, Inc.
+
+ Contributed by Fred Fish at Cygnus Support. fnf@cygnus.com
+
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <sys/types.h>
+#include <fcntl.h> /* After sys/types.h, at least for dpx/2. */
+#include <sys/stat.h>
+#include <string.h>
+#include "mmalloc.h"
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+
+#if defined(HAVE_MMAP)
+
+/* Forward declarations/prototypes for local functions */
+
+static struct mdesc *reuse PARAMS ((int));
+
+/* Initialize access to a mmalloc managed region.
+
+ If FD is a valid file descriptor for an open file then data for the
+ mmalloc managed region is mapped to that file, otherwise "/dev/zero"
+ is used and the data will not exist in any filesystem object.
+
+ If the open file corresponding to FD is from a previous use of
+ mmalloc and passes some basic sanity checks to ensure that it is
+ compatible with the current mmalloc package, then it's data is
+ mapped in and is immediately accessible at the same addresses in
+ the current process as the process that created the file.
+
+ If BASEADDR is not NULL, the mapping is established starting at the
+ specified address in the process address space. If BASEADDR is NULL,
+ the mmalloc package chooses a suitable address at which to start the
+ mapped region, which will be the value of the previous mapping if
+ opening an existing file which was previously built by mmalloc, or
+ for new files will be a value chosen by mmap.
+
+ Specifying BASEADDR provides more control over where the regions
+ start and how big they can be before bumping into existing mapped
+ regions or future mapped regions.
+
+ On success, returns a "malloc descriptor" which is used in subsequent
+ calls to other mmalloc package functions. It is explicitly "void *"
+ ("char *" for systems that don't fully support void) so that users
+ of the package don't have to worry about the actual implementation
+ details.
+
+ On failure returns NULL. */
+
+PTR
+mmalloc_attach (fd, baseaddr)
+ int fd;
+ PTR baseaddr;
+{
+ struct mdesc mtemp;
+ struct mdesc *mdp;
+ PTR mbase;
+ struct stat sbuf;
+
+ /* First check to see if FD is a valid file descriptor, and if so, see
+ if the file has any current contents (size > 0). If it does, then
+ attempt to reuse the file. If we can't reuse the file, either
+ because it isn't a valid mmalloc produced file, was produced by an
+ obsolete version, or any other reason, then we fail to attach to
+ this file. */
+
+ if (fd >= 0)
+ {
+ if (fstat (fd, &sbuf) < 0)
+ {
+ return (NULL);
+ }
+ else if (sbuf.st_size > 0)
+ {
+ return ((PTR) reuse (fd));
+ }
+ }
+
+ /* We start off with the malloc descriptor allocated on the stack, until
+ we build it up enough to call _mmalloc_mmap_morecore() to allocate the
+ first page of the region and copy it there. Ensure that it is zero'd and
+ then initialize the fields that we know values for. */
+
+ mdp = &mtemp;
+ memset ((char *) mdp, 0, sizeof (mtemp));
+ strncpy (mdp -> magic, MMALLOC_MAGIC, MMALLOC_MAGIC_SIZE);
+ mdp -> headersize = sizeof (mtemp);
+ mdp -> version = MMALLOC_VERSION;
+ mdp -> morecore = __mmalloc_mmap_morecore;
+ mdp -> fd = fd;
+ mdp -> base = mdp -> breakval = mdp -> top = baseaddr;
+
+ /* If we have not been passed a valid open file descriptor for the file
+ to map to, then open /dev/zero and use that to map to. */
+
+ if (mdp -> fd < 0)
+ {
+ if ((mdp -> fd = open ("/dev/zero", O_RDWR)) < 0)
+ {
+ return (NULL);
+ }
+ else
+ {
+ mdp -> flags |= MMALLOC_DEVZERO;
+ }
+ }
+
+ /* Now try to map in the first page, copy the malloc descriptor structure
+ there, and arrange to return a pointer to this new copy. If the mapping
+ fails, then close the file descriptor if it was opened by us, and arrange
+ to return a NULL. */
+
+ if ((mbase = mdp -> morecore (mdp, sizeof (mtemp))) != NULL)
+ {
+ memcpy (mbase, mdp, sizeof (mtemp));
+ mdp = (struct mdesc *) mbase;
+ }
+ else
+ {
+ if (mdp -> flags & MMALLOC_DEVZERO)
+ {
+ close (mdp -> fd);
+ }
+ mdp = NULL;
+ }
+
+ return ((PTR) mdp);
+}
+
+/* Given an valid file descriptor on an open file, test to see if that file
+ is a valid mmalloc produced file, and if so, attempt to remap it into the
+ current process at the same address to which it was previously mapped.
+
+ Note that we have to update the file descriptor number in the malloc-
+ descriptor read from the file to match the current valid one, before
+ trying to map the file in, and again after a successful mapping and
+ after we've switched over to using the mapped in malloc descriptor
+ rather than the temporary one on the stack.
+
+ Once we've switched over to using the mapped in malloc descriptor, we
+ have to update the pointer to the morecore function, since it almost
+ certainly will be at a different address if the process reusing the
+ mapped region is from a different executable.
+
+ Also note that if the heap being remapped previously used the mmcheck()
+ routines, we need to update the hooks since their target functions
+ will have certainly moved if the executable has changed in any way.
+ We do this by calling mmcheck() internally.
+
+ Returns a pointer to the malloc descriptor if successful, or NULL if
+ unsuccessful for some reason. */
+
+static struct mdesc *
+reuse (fd)
+ int fd;
+{
+ struct mdesc mtemp;
+ struct mdesc *mdp = NULL;
+
+ if ((lseek (fd, 0L, SEEK_SET) == 0) &&
+ (read (fd, (char *) &mtemp, sizeof (mtemp)) == sizeof (mtemp)) &&
+ (mtemp.headersize == sizeof (mtemp)) &&
+ (strcmp (mtemp.magic, MMALLOC_MAGIC) == 0) &&
+ (mtemp.version <= MMALLOC_VERSION))
+ {
+ mtemp.fd = fd;
+ if (__mmalloc_remap_core (&mtemp) == mtemp.base)
+ {
+ mdp = (struct mdesc *) mtemp.base;
+ mdp -> fd = fd;
+ mdp -> morecore = __mmalloc_mmap_morecore;
+ if (mdp -> mfree_hook != NULL)
+ {
+ mmcheck ((PTR) mdp, (void (*) PARAMS ((void))) NULL);
+ }
+ }
+ }
+ return (mdp);
+}
+
+#else /* !defined (HAVE_MMAP) */
+
+/* For systems without mmap, the library still supplies an entry point
+ to link to, but trying to initialize access to an mmap'd managed region
+ always fails. */
+
+/* ARGSUSED */
+PTR
+mmalloc_attach (fd, baseaddr)
+ int fd;
+ PTR baseaddr;
+{
+ return (NULL);
+}
+
+#endif /* defined (HAVE_MMAP) */
+
diff --git a/gnu/usr.bin/gdb/mmalloc/detach.c b/gnu/usr.bin/gdb/mmalloc/detach.c
new file mode 100644
index 0000000..03d632c
--- /dev/null
+++ b/gnu/usr.bin/gdb/mmalloc/detach.c
@@ -0,0 +1,71 @@
+/* Finish access to a mmap'd malloc managed region.
+ Copyright 1992 Free Software Foundation, Inc.
+
+ Contributed by Fred Fish at Cygnus Support. fnf@cygnus.com
+
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <sys/types.h>
+#include <fcntl.h> /* After sys/types.h, at least for dpx/2. */
+#include "mmalloc.h"
+
+/* Terminate access to a mmalloc managed region by unmapping all memory pages
+ associated with the region, and closing the file descriptor if it is one
+ that we opened.
+
+ Returns NULL on success.
+
+ Returns the malloc descriptor on failure, which can subsequently be used
+ for further action, such as obtaining more information about the nature of
+ the failure by examining the preserved errno value.
+
+ Note that the malloc descriptor that we are using is currently located in
+ region we are about to unmap, so we first make a local copy of it on the
+ stack and use the copy. */
+
+PTR
+mmalloc_detach (md)
+ PTR md;
+{
+ struct mdesc mtemp;
+
+ if (md != NULL)
+ {
+
+ mtemp = *(struct mdesc *) md;
+
+ /* Now unmap all the pages associated with this region by asking for a
+ negative increment equal to the current size of the region. */
+
+ if ((mtemp.morecore (&mtemp, mtemp.base - mtemp.top)) == NULL)
+ {
+ /* Update the original malloc descriptor with any changes */
+ *(struct mdesc *) md = mtemp;
+ }
+ else
+ {
+ if (mtemp.flags & MMALLOC_DEVZERO)
+ {
+ close (mtemp.fd);
+ }
+ md = NULL;
+ }
+ }
+
+ return (md);
+}
diff --git a/gnu/usr.bin/gdb/mmalloc/keys.c b/gnu/usr.bin/gdb/mmalloc/keys.c
new file mode 100644
index 0000000..69d41b9
--- /dev/null
+++ b/gnu/usr.bin/gdb/mmalloc/keys.c
@@ -0,0 +1,66 @@
+/* Access for application keys in mmap'd malloc managed region.
+ Copyright 1992 Free Software Foundation, Inc.
+
+ Contributed by Fred Fish at Cygnus Support. fnf@cygnus.com
+
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+/* This module provides access to some keys that the application can use to
+ provide persistent access to locations in the mapped memory section.
+ The intent is that these keys are to be used sparingly as sort of
+ persistent global variables which the application can use to reinitialize
+ access to data in the mapped region.
+
+ For the moment, these keys are simply stored in the malloc descriptor
+ itself, in an array of fixed length. This should be fixed so that there
+ can be an unlimited number of keys, possibly using a multilevel access
+ scheme of some sort. */
+
+#include "mmalloc.h"
+
+int
+mmalloc_setkey (md, keynum, key)
+ PTR md;
+ int keynum;
+ PTR key;
+{
+ struct mdesc *mdp = (struct mdesc *) md;
+ int result = 0;
+
+ if ((mdp != NULL) && (keynum >= 0) && (keynum < MMALLOC_KEYS))
+ {
+ mdp -> keys [keynum] = key;
+ result++;
+ }
+ return (result);
+}
+
+PTR
+mmalloc_getkey (md, keynum)
+ PTR md;
+ int keynum;
+{
+ struct mdesc *mdp = (struct mdesc *) md;
+ PTR keyval = NULL;
+
+ if ((mdp != NULL) && (keynum >= 0) && (keynum < MMALLOC_KEYS))
+ {
+ keyval = mdp -> keys [keynum];
+ }
+ return (keyval);
+}
diff --git a/gnu/usr.bin/gdb/mmalloc/mcalloc.c b/gnu/usr.bin/gdb/mmalloc/mcalloc.c
new file mode 100644
index 0000000..08f07bf
--- /dev/null
+++ b/gnu/usr.bin/gdb/mmalloc/mcalloc.c
@@ -0,0 +1,53 @@
+/* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <string.h> /* Prototypes for memcpy, memmove, memset, etc */
+
+#include "mmalloc.h"
+
+/* Allocate an array of NMEMB elements each SIZE bytes long.
+ The entire array is initialized to zeros. */
+
+PTR
+mcalloc (md, nmemb, size)
+ PTR md;
+ register size_t nmemb;
+ register size_t size;
+{
+ register PTR result;
+
+ if ((result = mmalloc (md, nmemb * size)) != NULL)
+ {
+ memset (result, 0, nmemb * size);
+ }
+ return (result);
+}
+
+/* When using this package, provide a version of malloc/realloc/free built
+ on top of it, so that if we use the default sbrk() region we will not
+ collide with another malloc package trying to do the same thing, if
+ the application contains any "hidden" calls to malloc/realloc/free (such
+ as inside a system library). */
+
+PTR
+calloc (nmemb, size)
+ size_t nmemb;
+ size_t size;
+{
+ return (mcalloc ((PTR) NULL, nmemb, size));
+}
diff --git a/gnu/usr.bin/gdb/mmalloc/mfree.c b/gnu/usr.bin/gdb/mmalloc/mfree.c
new file mode 100644
index 0000000..aee43aa
--- /dev/null
+++ b/gnu/usr.bin/gdb/mmalloc/mfree.c
@@ -0,0 +1,247 @@
+/* Free a block of memory allocated by `mmalloc'.
+ Copyright 1990, 1991, 1992 Free Software Foundation
+
+ Written May 1989 by Mike Haertel.
+ Heavily modified Mar 1992 by Fred Fish. (fnf@cygnus.com)
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+
+ The author may be reached (Email) at the address mike@ai.mit.edu,
+ or (US mail) as Mike Haertel c/o Free Software Foundation. */
+
+#include "mmalloc.h"
+
+/* Return memory to the heap.
+ Like `mfree' but don't call a mfree_hook if there is one. */
+
+void
+__mmalloc_free (mdp, ptr)
+ struct mdesc *mdp;
+ PTR ptr;
+{
+ int type;
+ size_t block, blocks;
+ register size_t i;
+ struct list *prev, *next;
+
+ block = BLOCK (ptr);
+
+ type = mdp -> heapinfo[block].busy.type;
+ switch (type)
+ {
+ case 0:
+ /* Get as many statistics as early as we can. */
+ mdp -> heapstats.chunks_used--;
+ mdp -> heapstats.bytes_used -=
+ mdp -> heapinfo[block].busy.info.size * BLOCKSIZE;
+ mdp -> heapstats.bytes_free +=
+ mdp -> heapinfo[block].busy.info.size * BLOCKSIZE;
+
+ /* Find the free cluster previous to this one in the free list.
+ Start searching at the last block referenced; this may benefit
+ programs with locality of allocation. */
+ i = mdp -> heapindex;
+ if (i > block)
+ {
+ while (i > block)
+ {
+ i = mdp -> heapinfo[i].free.prev;
+ }
+ }
+ else
+ {
+ do
+ {
+ i = mdp -> heapinfo[i].free.next;
+ }
+ while ((i != 0) && (i < block));
+ i = mdp -> heapinfo[i].free.prev;
+ }
+
+ /* Determine how to link this block into the free list. */
+ if (block == i + mdp -> heapinfo[i].free.size)
+ {
+ /* Coalesce this block with its predecessor. */
+ mdp -> heapinfo[i].free.size +=
+ mdp -> heapinfo[block].busy.info.size;
+ block = i;
+ }
+ else
+ {
+ /* Really link this block back into the free list. */
+ mdp -> heapinfo[block].free.size =
+ mdp -> heapinfo[block].busy.info.size;
+ mdp -> heapinfo[block].free.next = mdp -> heapinfo[i].free.next;
+ mdp -> heapinfo[block].free.prev = i;
+ mdp -> heapinfo[i].free.next = block;
+ mdp -> heapinfo[mdp -> heapinfo[block].free.next].free.prev = block;
+ mdp -> heapstats.chunks_free++;
+ }
+
+ /* Now that the block is linked in, see if we can coalesce it
+ with its successor (by deleting its successor from the list
+ and adding in its size). */
+ if (block + mdp -> heapinfo[block].free.size ==
+ mdp -> heapinfo[block].free.next)
+ {
+ mdp -> heapinfo[block].free.size
+ += mdp -> heapinfo[mdp -> heapinfo[block].free.next].free.size;
+ mdp -> heapinfo[block].free.next
+ = mdp -> heapinfo[mdp -> heapinfo[block].free.next].free.next;
+ mdp -> heapinfo[mdp -> heapinfo[block].free.next].free.prev = block;
+ mdp -> heapstats.chunks_free--;
+ }
+
+ /* Now see if we can return stuff to the system. */
+ blocks = mdp -> heapinfo[block].free.size;
+ if (blocks >= FINAL_FREE_BLOCKS && block + blocks == mdp -> heaplimit
+ && mdp -> morecore (mdp, 0) == ADDRESS (block + blocks))
+ {
+ register size_t bytes = blocks * BLOCKSIZE;
+ mdp -> heaplimit -= blocks;
+ mdp -> morecore (mdp, -bytes);
+ mdp -> heapinfo[mdp -> heapinfo[block].free.prev].free.next
+ = mdp -> heapinfo[block].free.next;
+ mdp -> heapinfo[mdp -> heapinfo[block].free.next].free.prev
+ = mdp -> heapinfo[block].free.prev;
+ block = mdp -> heapinfo[block].free.prev;
+ mdp -> heapstats.chunks_free--;
+ mdp -> heapstats.bytes_free -= bytes;
+ }
+
+ /* Set the next search to begin at this block. */
+ mdp -> heapindex = block;
+ break;
+
+ default:
+ /* Do some of the statistics. */
+ mdp -> heapstats.chunks_used--;
+ mdp -> heapstats.bytes_used -= 1 << type;
+ mdp -> heapstats.chunks_free++;
+ mdp -> heapstats.bytes_free += 1 << type;
+
+ /* Get the address of the first free fragment in this block. */
+ prev = (struct list *)
+ ((char *) ADDRESS(block) +
+ (mdp -> heapinfo[block].busy.info.frag.first << type));
+
+ if (mdp -> heapinfo[block].busy.info.frag.nfree ==
+ (BLOCKSIZE >> type) - 1)
+ {
+ /* If all fragments of this block are free, remove them
+ from the fragment list and free the whole block. */
+ next = prev;
+ for (i = 1; i < (size_t) (BLOCKSIZE >> type); ++i)
+ {
+ next = next -> next;
+ }
+ prev -> prev -> next = next;
+ if (next != NULL)
+ {
+ next -> prev = prev -> prev;
+ }
+ mdp -> heapinfo[block].busy.type = 0;
+ mdp -> heapinfo[block].busy.info.size = 1;
+
+ /* Keep the statistics accurate. */
+ mdp -> heapstats.chunks_used++;
+ mdp -> heapstats.bytes_used += BLOCKSIZE;
+ mdp -> heapstats.chunks_free -= BLOCKSIZE >> type;
+ mdp -> heapstats.bytes_free -= BLOCKSIZE;
+
+ mfree ((PTR) mdp, (PTR) ADDRESS(block));
+ }
+ else if (mdp -> heapinfo[block].busy.info.frag.nfree != 0)
+ {
+ /* If some fragments of this block are free, link this
+ fragment into the fragment list after the first free
+ fragment of this block. */
+ next = (struct list *) ptr;
+ next -> next = prev -> next;
+ next -> prev = prev;
+ prev -> next = next;
+ if (next -> next != NULL)
+ {
+ next -> next -> prev = next;
+ }
+ ++mdp -> heapinfo[block].busy.info.frag.nfree;
+ }
+ else
+ {
+ /* No fragments of this block are free, so link this
+ fragment into the fragment list and announce that
+ it is the first free fragment of this block. */
+ prev = (struct list *) ptr;
+ mdp -> heapinfo[block].busy.info.frag.nfree = 1;
+ mdp -> heapinfo[block].busy.info.frag.first =
+ RESIDUAL (ptr, BLOCKSIZE) >> type;
+ prev -> next = mdp -> fraghead[type].next;
+ prev -> prev = &mdp -> fraghead[type];
+ prev -> prev -> next = prev;
+ if (prev -> next != NULL)
+ {
+ prev -> next -> prev = prev;
+ }
+ }
+ break;
+ }
+}
+
+/* Return memory to the heap. */
+
+void
+mfree (md, ptr)
+ PTR md;
+ PTR ptr;
+{
+ struct mdesc *mdp;
+ register struct alignlist *l;
+
+ if (ptr != NULL)
+ {
+ mdp = MD_TO_MDP (md);
+ for (l = mdp -> aligned_blocks; l != NULL; l = l -> next)
+ {
+ if (l -> aligned == ptr)
+ {
+ l -> aligned = NULL; /* Mark the slot in the list as free. */
+ ptr = l -> exact;
+ break;
+ }
+ }
+ if (mdp -> mfree_hook != NULL)
+ {
+ (*mdp -> mfree_hook) (md, ptr);
+ }
+ else
+ {
+ __mmalloc_free (mdp, ptr);
+ }
+ }
+}
+
+/* When using this package, provide a version of malloc/realloc/free built
+ on top of it, so that if we use the default sbrk() region we will not
+ collide with another malloc package trying to do the same thing, if
+ the application contains any "hidden" calls to malloc/realloc/free (such
+ as inside a system library). */
+
+void
+free (ptr)
+ PTR ptr;
+{
+ mfree ((PTR) NULL, ptr);
+}
diff --git a/gnu/usr.bin/gdb/mmalloc/mmalloc.c b/gnu/usr.bin/gdb/mmalloc/mmalloc.c
new file mode 100644
index 0000000..46b450e
--- /dev/null
+++ b/gnu/usr.bin/gdb/mmalloc/mmalloc.c
@@ -0,0 +1,334 @@
+/* Memory allocator `malloc'.
+ Copyright 1990, 1991, 1992 Free Software Foundation
+
+ Written May 1989 by Mike Haertel.
+ Heavily modified Mar 1992 by Fred Fish for mmap'd version.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+
+ The author may be reached (Email) at the address mike@ai.mit.edu,
+ or (US mail) as Mike Haertel c/o Free Software Foundation. */
+
+#include <string.h> /* Prototypes for memcpy, memmove, memset, etc */
+
+#include "mmalloc.h"
+
+/* Prototypes for local functions */
+
+static int initialize PARAMS ((struct mdesc *));
+static PTR morecore PARAMS ((struct mdesc *, size_t));
+static PTR align PARAMS ((struct mdesc *, size_t));
+
+/* Aligned allocation. */
+
+static PTR
+align (mdp, size)
+ struct mdesc *mdp;
+ size_t size;
+{
+ PTR result;
+ unsigned long int adj;
+
+ result = mdp -> morecore (mdp, size);
+ adj = RESIDUAL (result, BLOCKSIZE);
+ if (adj != 0)
+ {
+ adj = BLOCKSIZE - adj;
+ mdp -> morecore (mdp, adj);
+ result = (char *) result + adj;
+ }
+ return (result);
+}
+
+/* Set everything up and remember that we have. */
+
+static int
+initialize (mdp)
+ struct mdesc *mdp;
+{
+ mdp -> heapsize = HEAP / BLOCKSIZE;
+ mdp -> heapinfo = (malloc_info *)
+ align (mdp, mdp -> heapsize * sizeof (malloc_info));
+ if (mdp -> heapinfo == NULL)
+ {
+ return (0);
+ }
+ memset ((PTR)mdp -> heapinfo, 0, mdp -> heapsize * sizeof (malloc_info));
+ mdp -> heapinfo[0].free.size = 0;
+ mdp -> heapinfo[0].free.next = mdp -> heapinfo[0].free.prev = 0;
+ mdp -> heapindex = 0;
+ mdp -> heapbase = (char *) mdp -> heapinfo;
+ mdp -> flags |= MMALLOC_INITIALIZED;
+ return (1);
+}
+
+/* Get neatly aligned memory, initializing or
+ growing the heap info table as necessary. */
+
+static PTR
+morecore (mdp, size)
+ struct mdesc *mdp;
+ size_t size;
+{
+ PTR result;
+ malloc_info *newinfo, *oldinfo;
+ size_t newsize;
+
+ result = align (mdp, size);
+ if (result == NULL)
+ {
+ return (NULL);
+ }
+
+ /* Check if we need to grow the info table. */
+ if ((size_t) BLOCK ((char *) result + size) > mdp -> heapsize)
+ {
+ newsize = mdp -> heapsize;
+ while ((size_t) BLOCK ((char *) result + size) > newsize)
+ {
+ newsize *= 2;
+ }
+ newinfo = (malloc_info *) align (mdp, newsize * sizeof (malloc_info));
+ if (newinfo == NULL)
+ {
+ mdp -> morecore (mdp, -size);
+ return (NULL);
+ }
+ memset ((PTR) newinfo, 0, newsize * sizeof (malloc_info));
+ memcpy ((PTR) newinfo, (PTR) mdp -> heapinfo,
+ mdp -> heapsize * sizeof (malloc_info));
+ oldinfo = mdp -> heapinfo;
+ newinfo[BLOCK (oldinfo)].busy.type = 0;
+ newinfo[BLOCK (oldinfo)].busy.info.size
+ = BLOCKIFY (mdp -> heapsize * sizeof (malloc_info));
+ mdp -> heapinfo = newinfo;
+ __mmalloc_free (mdp, (PTR)oldinfo);
+ mdp -> heapsize = newsize;
+ }
+
+ mdp -> heaplimit = BLOCK ((char *) result + size);
+ return (result);
+}
+
+/* Allocate memory from the heap. */
+
+PTR
+mmalloc (md, size)
+ PTR md;
+ size_t size;
+{
+ struct mdesc *mdp;
+ PTR result;
+ size_t block, blocks, lastblocks, start;
+ register size_t i;
+ struct list *next;
+ register size_t log;
+
+ if (size == 0)
+ {
+ return (NULL);
+ }
+
+ mdp = MD_TO_MDP (md);
+
+ if (mdp -> mmalloc_hook != NULL)
+ {
+ return ((*mdp -> mmalloc_hook) (md, size));
+ }
+
+ if (!(mdp -> flags & MMALLOC_INITIALIZED))
+ {
+ if (!initialize (mdp))
+ {
+ return (NULL);
+ }
+ }
+
+ if (size < sizeof (struct list))
+ {
+ size = sizeof (struct list);
+ }
+
+ /* Determine the allocation policy based on the request size. */
+ if (size <= BLOCKSIZE / 2)
+ {
+ /* Small allocation to receive a fragment of a block.
+ Determine the logarithm to base two of the fragment size. */
+ log = 1;
+ --size;
+ while ((size /= 2) != 0)
+ {
+ ++log;
+ }
+
+ /* Look in the fragment lists for a
+ free fragment of the desired size. */
+ next = mdp -> fraghead[log].next;
+ if (next != NULL)
+ {
+ /* There are free fragments of this size.
+ Pop a fragment out of the fragment list and return it.
+ Update the block's nfree and first counters. */
+ result = (PTR) next;
+ next -> prev -> next = next -> next;
+ if (next -> next != NULL)
+ {
+ next -> next -> prev = next -> prev;
+ }
+ block = BLOCK (result);
+ if (--mdp -> heapinfo[block].busy.info.frag.nfree != 0)
+ {
+ mdp -> heapinfo[block].busy.info.frag.first =
+ RESIDUAL (next -> next, BLOCKSIZE) >> log;
+ }
+
+ /* Update the statistics. */
+ mdp -> heapstats.chunks_used++;
+ mdp -> heapstats.bytes_used += 1 << log;
+ mdp -> heapstats.chunks_free--;
+ mdp -> heapstats.bytes_free -= 1 << log;
+ }
+ else
+ {
+ /* No free fragments of the desired size, so get a new block
+ and break it into fragments, returning the first. */
+ result = mmalloc (md, BLOCKSIZE);
+ if (result == NULL)
+ {
+ return (NULL);
+ }
+
+ /* Link all fragments but the first into the free list. */
+ for (i = 1; i < (size_t) (BLOCKSIZE >> log); ++i)
+ {
+ next = (struct list *) ((char *) result + (i << log));
+ next -> next = mdp -> fraghead[log].next;
+ next -> prev = &mdp -> fraghead[log];
+ next -> prev -> next = next;
+ if (next -> next != NULL)
+ {
+ next -> next -> prev = next;
+ }
+ }
+
+ /* Initialize the nfree and first counters for this block. */
+ block = BLOCK (result);
+ mdp -> heapinfo[block].busy.type = log;
+ mdp -> heapinfo[block].busy.info.frag.nfree = i - 1;
+ mdp -> heapinfo[block].busy.info.frag.first = i - 1;
+
+ mdp -> heapstats.chunks_free += (BLOCKSIZE >> log) - 1;
+ mdp -> heapstats.bytes_free += BLOCKSIZE - (1 << log);
+ mdp -> heapstats.bytes_used -= BLOCKSIZE - (1 << log);
+ }
+ }
+ else
+ {
+ /* Large allocation to receive one or more blocks.
+ Search the free list in a circle starting at the last place visited.
+ If we loop completely around without finding a large enough
+ space we will have to get more memory from the system. */
+ blocks = BLOCKIFY(size);
+ start = block = MALLOC_SEARCH_START;
+ while (mdp -> heapinfo[block].free.size < blocks)
+ {
+ block = mdp -> heapinfo[block].free.next;
+ if (block == start)
+ {
+ /* Need to get more from the system. Check to see if
+ the new core will be contiguous with the final free
+ block; if so we don't need to get as much. */
+ block = mdp -> heapinfo[0].free.prev;
+ lastblocks = mdp -> heapinfo[block].free.size;
+ if (mdp -> heaplimit != 0 &&
+ block + lastblocks == mdp -> heaplimit &&
+ mdp -> morecore (mdp, 0) == ADDRESS(block + lastblocks) &&
+ (morecore (mdp, (blocks - lastblocks) * BLOCKSIZE)) != NULL)
+ {
+ /* Which block we are extending (the `final free
+ block' referred to above) might have changed, if
+ it got combined with a freed info table. */
+ block = mdp -> heapinfo[0].free.prev;
+
+ mdp -> heapinfo[block].free.size += (blocks - lastblocks);
+ mdp -> heapstats.bytes_free +=
+ (blocks - lastblocks) * BLOCKSIZE;
+ continue;
+ }
+ result = morecore(mdp, blocks * BLOCKSIZE);
+ if (result == NULL)
+ {
+ return (NULL);
+ }
+ block = BLOCK (result);
+ mdp -> heapinfo[block].busy.type = 0;
+ mdp -> heapinfo[block].busy.info.size = blocks;
+ mdp -> heapstats.chunks_used++;
+ mdp -> heapstats.bytes_used += blocks * BLOCKSIZE;
+ return (result);
+ }
+ }
+
+ /* At this point we have found a suitable free list entry.
+ Figure out how to remove what we need from the list. */
+ result = ADDRESS(block);
+ if (mdp -> heapinfo[block].free.size > blocks)
+ {
+ /* The block we found has a bit left over,
+ so relink the tail end back into the free list. */
+ mdp -> heapinfo[block + blocks].free.size
+ = mdp -> heapinfo[block].free.size - blocks;
+ mdp -> heapinfo[block + blocks].free.next
+ = mdp -> heapinfo[block].free.next;
+ mdp -> heapinfo[block + blocks].free.prev
+ = mdp -> heapinfo[block].free.prev;
+ mdp -> heapinfo[mdp -> heapinfo[block].free.prev].free.next
+ = mdp -> heapinfo[mdp -> heapinfo[block].free.next].free.prev
+ = mdp -> heapindex = block + blocks;
+ }
+ else
+ {
+ /* The block exactly matches our requirements,
+ so just remove it from the list. */
+ mdp -> heapinfo[mdp -> heapinfo[block].free.next].free.prev
+ = mdp -> heapinfo[block].free.prev;
+ mdp -> heapinfo[mdp -> heapinfo[block].free.prev].free.next
+ = mdp -> heapindex = mdp -> heapinfo[block].free.next;
+ mdp -> heapstats.chunks_free--;
+ }
+
+ mdp -> heapinfo[block].busy.type = 0;
+ mdp -> heapinfo[block].busy.info.size = blocks;
+ mdp -> heapstats.chunks_used++;
+ mdp -> heapstats.bytes_used += blocks * BLOCKSIZE;
+ mdp -> heapstats.bytes_free -= blocks * BLOCKSIZE;
+ }
+
+ return (result);
+}
+
+/* When using this package, provide a version of malloc/realloc/free built
+ on top of it, so that if we use the default sbrk() region we will not
+ collide with another malloc package trying to do the same thing, if
+ the application contains any "hidden" calls to malloc/realloc/free (such
+ as inside a system library). */
+
+PTR
+malloc (size)
+ size_t size;
+{
+ return (mmalloc ((PTR) NULL, size));
+}
diff --git a/gnu/usr.bin/gdb/mmalloc/mmalloc.h b/gnu/usr.bin/gdb/mmalloc/mmalloc.h
new file mode 100644
index 0000000..54b6ed6
--- /dev/null
+++ b/gnu/usr.bin/gdb/mmalloc/mmalloc.h
@@ -0,0 +1,390 @@
+/* Declarations for `mmalloc' and friends.
+ Copyright 1990, 1991, 1992 Free Software Foundation
+
+ Written May 1989 by Mike Haertel.
+ Heavily modified Mar 1992 by Fred Fish. (fnf@cygnus.com)
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+
+ The author may be reached (Email) at the address mike@ai.mit.edu,
+ or (US mail) as Mike Haertel c/o Free Software Foundation. */
+
+
+#ifndef __MMALLOC_H
+#define __MMALLOC_H 1
+
+#ifdef __STDC__
+# include <stddef.h>
+# define PTR void *
+# define CONST const
+# define PARAMS(paramlist) paramlist
+# include <limits.h>
+# ifndef NULL
+# define NULL (void *) 0
+# endif
+#else
+# define PTR char *
+# define CONST /* nothing */
+# define PARAMS(paramlist) ()
+# ifndef size_t
+# define size_t unsigned int
+# endif
+# ifndef CHAR_BIT
+# define CHAR_BIT 8
+# endif
+# ifndef NULL
+# define NULL 0
+# endif
+#endif
+
+#ifndef MIN
+# define MIN(A, B) ((A) < (B) ? (A) : (B))
+#endif
+
+#define MMALLOC_MAGIC "mmalloc" /* Mapped file magic number */
+#define MMALLOC_MAGIC_SIZE 8 /* Size of magic number buf */
+#define MMALLOC_VERSION 1 /* Current mmalloc version */
+#define MMALLOC_KEYS 16 /* Keys for application use */
+
+/* The allocator divides the heap into blocks of fixed size; large
+ requests receive one or more whole blocks, and small requests
+ receive a fragment of a block. Fragment sizes are powers of two,
+ and all fragments of a block are the same size. When all the
+ fragments in a block have been freed, the block itself is freed. */
+
+#define INT_BIT (CHAR_BIT * sizeof(int))
+#define BLOCKLOG (INT_BIT > 16 ? 12 : 9)
+#define BLOCKSIZE ((unsigned int) 1 << BLOCKLOG)
+#define BLOCKIFY(SIZE) (((SIZE) + BLOCKSIZE - 1) / BLOCKSIZE)
+
+/* The difference between two pointers is a signed int. On machines where
+ the data addresses have the high bit set, we need to ensure that the
+ difference becomes an unsigned int when we are using the address as an
+ integral value. In addition, when using with the '%' operator, the
+ sign of the result is machine dependent for negative values, so force
+ it to be treated as an unsigned int. */
+
+#define ADDR2UINT(addr) ((unsigned int) ((char *) (addr) - (char *) NULL))
+#define RESIDUAL(addr,bsize) ((unsigned int) (ADDR2UINT (addr) % (bsize)))
+
+/* Determine the amount of memory spanned by the initial heap table
+ (not an absolute limit). */
+
+#define HEAP (INT_BIT > 16 ? 4194304 : 65536)
+
+/* Number of contiguous free blocks allowed to build up at the end of
+ memory before they will be returned to the system. */
+
+#define FINAL_FREE_BLOCKS 8
+
+/* Where to start searching the free list when looking for new memory.
+ The two possible values are 0 and heapindex. Starting at 0 seems
+ to reduce total memory usage, while starting at heapindex seems to
+ run faster. */
+
+#define MALLOC_SEARCH_START mdp -> heapindex
+
+/* Address to block number and vice versa. */
+
+#define BLOCK(A) (((char *) (A) - mdp -> heapbase) / BLOCKSIZE + 1)
+
+#define ADDRESS(B) ((PTR) (((B) - 1) * BLOCKSIZE + mdp -> heapbase))
+
+/* Data structure giving per-block information. */
+
+typedef union
+ {
+ /* Heap information for a busy block. */
+ struct
+ {
+ /* Zero for a large block, or positive giving the
+ logarithm to the base two of the fragment size. */
+ int type;
+ union
+ {
+ struct
+ {
+ size_t nfree; /* Free fragments in a fragmented block. */
+ size_t first; /* First free fragment of the block. */
+ } frag;
+ /* Size (in blocks) of a large cluster. */
+ size_t size;
+ } info;
+ } busy;
+ /* Heap information for a free block (that may be the first of
+ a free cluster). */
+ struct
+ {
+ size_t size; /* Size (in blocks) of a free cluster. */
+ size_t next; /* Index of next free cluster. */
+ size_t prev; /* Index of previous free cluster. */
+ } free;
+ } malloc_info;
+
+/* List of blocks allocated with `mmemalign' (or `mvalloc'). */
+
+struct alignlist
+ {
+ struct alignlist *next;
+ PTR aligned; /* The address that mmemaligned returned. */
+ PTR exact; /* The address that malloc returned. */
+ };
+
+/* Doubly linked lists of free fragments. */
+
+struct list
+ {
+ struct list *next;
+ struct list *prev;
+ };
+
+/* Statistics available to the user.
+ FIXME: By design, the internals of the malloc package are no longer
+ exported to the user via an include file, so access to this data needs
+ to be via some other mechanism, such as mmstat_<something> where the
+ return value is the <something> the user is interested in. */
+
+struct mstats
+ {
+ size_t bytes_total; /* Total size of the heap. */
+ size_t chunks_used; /* Chunks allocated by the user. */
+ size_t bytes_used; /* Byte total of user-allocated chunks. */
+ size_t chunks_free; /* Chunks in the free list. */
+ size_t bytes_free; /* Byte total of chunks in the free list. */
+ };
+
+/* Internal structure that defines the format of the malloc-descriptor.
+ This gets written to the base address of the region that mmalloc is
+ managing, and thus also becomes the file header for the mapped file,
+ if such a file exists. */
+
+struct mdesc
+{
+ /* The "magic number" for an mmalloc file. */
+
+ char magic[MMALLOC_MAGIC_SIZE];
+
+ /* The size in bytes of this structure, used as a sanity check when reusing
+ a previously created mapped file. */
+
+ unsigned int headersize;
+
+ /* The version number of the mmalloc package that created this file. */
+
+ unsigned char version;
+
+ /* Some flag bits to keep track of various internal things. */
+
+ unsigned int flags;
+
+ /* If a system call made by the mmalloc package fails, the errno is
+ preserved for future examination. */
+
+ int saved_errno;
+
+ /* Pointer to the function that is used to get more core, or return core
+ to the system, for requests using this malloc descriptor. For memory
+ mapped regions, this is the mmap() based routine. There may also be
+ a single malloc descriptor that points to an sbrk() based routine
+ for systems without mmap() or for applications that call the mmalloc()
+ package with a NULL malloc descriptor.
+
+ FIXME: For mapped regions shared by more than one process, this
+ needs to be maintained on a per-process basis. */
+
+ PTR (*morecore) PARAMS ((struct mdesc *, int));
+
+ /* Pointer to the function that causes an abort when the memory checking
+ features are activated. By default this is set to abort(), but can
+ be set to another function by the application using mmalloc().
+
+ FIXME: For mapped regions shared by more than one process, this
+ needs to be maintained on a per-process basis. */
+
+ void (*abortfunc) PARAMS ((void));
+
+ /* Debugging hook for free.
+
+ FIXME: For mapped regions shared by more than one process, this
+ needs to be maintained on a per-process basis. */
+
+ void (*mfree_hook) PARAMS ((PTR, PTR));
+
+ /* Debugging hook for `malloc'.
+
+ FIXME: For mapped regions shared by more than one process, this
+ needs to be maintained on a per-process basis. */
+
+ PTR (*mmalloc_hook) PARAMS ((PTR, size_t));
+
+ /* Debugging hook for realloc.
+
+ FIXME: For mapped regions shared by more than one process, this
+ needs to be maintained on a per-process basis. */
+
+ PTR (*mrealloc_hook) PARAMS ((PTR, PTR, size_t));
+
+ /* Number of info entries. */
+
+ size_t heapsize;
+
+ /* Pointer to first block of the heap (base of the first block). */
+
+ char *heapbase;
+
+ /* Current search index for the heap table. */
+ /* Search index in the info table. */
+
+ size_t heapindex;
+
+ /* Limit of valid info table indices. */
+
+ size_t heaplimit;
+
+ /* Block information table.
+ Allocated with malign/__mmalloc_free (not mmalloc/mfree). */
+ /* Table indexed by block number giving per-block information. */
+
+ malloc_info *heapinfo;
+
+ /* Instrumentation. */
+
+ struct mstats heapstats;
+
+ /* Free list headers for each fragment size. */
+ /* Free lists for each fragment size. */
+
+ struct list fraghead[BLOCKLOG];
+
+ /* List of blocks allocated by memalign. */
+
+ struct alignlist *aligned_blocks;
+
+ /* The base address of the memory region for this malloc heap. This
+ is the location where the bookkeeping data for mmap and for malloc
+ begins. */
+
+ char *base;
+
+ /* The current location in the memory region for this malloc heap which
+ represents the end of memory in use. */
+
+ char *breakval;
+
+ /* The end of the current memory region for this malloc heap. This is
+ the first location past the end of mapped memory. */
+
+ char *top;
+
+ /* Open file descriptor for the file to which this malloc heap is mapped.
+ This will always be a valid file descriptor, since /dev/zero is used
+ by default if no open file is supplied by the client. Also note that
+ it may change each time the region is mapped and unmapped. */
+
+ int fd;
+
+ /* An array of keys to data within the mapped region, for use by the
+ application. */
+
+ PTR keys[MMALLOC_KEYS];
+
+};
+
+/* Bits to look at in the malloc descriptor flags word */
+
+#define MMALLOC_DEVZERO (1 << 0) /* Have mapped to /dev/zero */
+#define MMALLOC_INITIALIZED (1 << 1) /* Initialized mmalloc */
+#define MMALLOC_MMCHECK_USED (1 << 2) /* mmcheck() called already */
+
+/* Allocate SIZE bytes of memory. */
+
+extern PTR mmalloc PARAMS ((PTR, size_t));
+
+/* Re-allocate the previously allocated block in PTR, making the new block
+ SIZE bytes long. */
+
+extern PTR mrealloc PARAMS ((PTR, PTR, size_t));
+
+/* Allocate NMEMB elements of SIZE bytes each, all initialized to 0. */
+
+extern PTR mcalloc PARAMS ((PTR, size_t, size_t));
+
+/* Free a block allocated by `mmalloc', `mrealloc' or `mcalloc'. */
+
+extern void mfree PARAMS ((PTR, PTR));
+
+/* Allocate SIZE bytes allocated to ALIGNMENT bytes. */
+
+extern PTR mmemalign PARAMS ((PTR, size_t, size_t));
+
+/* Allocate SIZE bytes on a page boundary. */
+
+extern PTR mvalloc PARAMS ((PTR, size_t));
+
+/* Activate a standard collection of debugging hooks. */
+
+extern int mmcheck PARAMS ((PTR, void (*) (void)));
+
+/* Pick up the current statistics. (see FIXME elsewhere) */
+
+extern struct mstats mmstats PARAMS ((PTR));
+
+/* Internal version of `mfree' used in `morecore'. */
+
+extern void __mmalloc_free PARAMS ((struct mdesc *, PTR));
+
+/* Hooks for debugging versions. */
+
+extern void (*__mfree_hook) PARAMS ((PTR, PTR));
+extern PTR (*__mmalloc_hook) PARAMS ((PTR, size_t));
+extern PTR (*__mrealloc_hook) PARAMS ((PTR, PTR, size_t));
+
+/* A default malloc descriptor for the single sbrk() managed region. */
+
+extern struct mdesc *__mmalloc_default_mdp;
+
+/* Initialize the first use of the default malloc descriptor, which uses
+ an sbrk() region. */
+
+extern struct mdesc *__mmalloc_sbrk_init PARAMS ((void));
+
+/* Grow or shrink a contiguous mapped region using mmap().
+ Works much like sbrk() */
+
+#if defined(HAVE_MMAP)
+
+extern PTR __mmalloc_mmap_morecore PARAMS ((struct mdesc *, int));
+
+#endif
+
+/* Remap a mmalloc region that was previously mapped. */
+
+extern PTR __mmalloc_remap_core PARAMS ((struct mdesc *));
+
+/* Macro to convert from a user supplied malloc descriptor to pointer to the
+ internal malloc descriptor. If the user supplied descriptor is NULL, then
+ use the default internal version, initializing it if necessary. Otherwise
+ just cast the user supplied version (which is void *) to the proper type
+ (struct mdesc *). */
+
+#define MD_TO_MDP(md) \
+ ((md) == NULL \
+ ? (__mmalloc_default_mdp == NULL \
+ ? __mmalloc_sbrk_init () \
+ : __mmalloc_default_mdp) \
+ : (struct mdesc *) (md))
+
+#endif /* __MMALLOC_H */
diff --git a/gnu/usr.bin/gdb/mmalloc/mmalloc.texi b/gnu/usr.bin/gdb/mmalloc/mmalloc.texi
new file mode 100644
index 0000000..5e28398
--- /dev/null
+++ b/gnu/usr.bin/gdb/mmalloc/mmalloc.texi
@@ -0,0 +1,258 @@
+\input texinfo @c -*- Texinfo -*-
+@setfilename mmalloc.info
+
+@ifinfo
+@format
+START-INFO-DIR-ENTRY
+* Mmalloc: (mmalloc). The GNU mapped-malloc package.
+END-INFO-DIR-ENTRY
+@end format
+
+This file documents the GNU mmalloc (mapped-malloc) package, written by
+fnf@@cygnus.com, based on GNU malloc written by mike@@ai.mit.edu.
+
+Copyright (C) 1992 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+@ignore
+Permission is granted to process this file through Tex and print the
+results, provided the printed document carries copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+@end ignore
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided also that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions.
+@end ifinfo
+@iftex
+@c @finalout
+@setchapternewpage odd
+@settitle MMALLOC, the GNU memory-mapped malloc package
+@titlepage
+@title mmalloc
+@subtitle The GNU memory-mapped malloc package
+@author Fred Fish
+@author Cygnus Support
+@author Mike Haertel
+@author Free Software Foundation
+@page
+
+@tex
+\def\$#1${{#1}} % Kluge: collect RCS revision info without $...$
+\xdef\manvers{\$Revision: 1.1.1.1 $} % For use in headers, footers too
+{\parskip=0pt
+\hfill Cygnus Support\par
+\hfill fnf\@cygnus.com\par
+\hfill {\it MMALLOC, the GNU memory-mapped malloc package}, \manvers\par
+\hfill \TeX{}info \texinfoversion\par
+}
+@end tex
+
+@vskip 0pt plus 1filll
+Copyright @copyright{} 1992 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided also that
+the entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions.
+@end titlepage
+@end iftex
+
+@ifinfo
+@node Top, Overview, (dir), (dir)
+@top mmalloc
+This file documents the GNU memory-mapped malloc package mmalloc.
+
+@menu
+* Overview:: Overall Description
+* Implementation:: Implementation
+
+ --- The Detailed Node Listing ---
+
+Implementation
+
+* Compatibility:: Backwards Compatibility
+* Functions:: Function Descriptions
+@end menu
+
+@end ifinfo
+
+@node Overview, Implementation, Top, Top
+@chapter Overall Description
+
+This is a heavily modified version of GNU @code{malloc}. It uses
+@code{mmap} as the basic mechanism for for obtaining memory from the
+system, rather than @code{sbrk}. This gives it several advantages over the
+more traditional malloc:
+
+@itemize @bullet
+@item
+Several different heaps can be used, each of them growing
+or shinking under control of @code{mmap}, with the @code{mmalloc} functions
+using a specific heap on a call by call basis.
+
+@item
+By using @code{mmap}, it is easy to create heaps which are intended to
+be persistent and exist as a filesystem object after the creating
+process has gone away.
+
+@item
+Because multiple heaps can be managed, data used for a
+specific purpose can be allocated into its own heap, making
+it easier to allow applications to ``dump'' and ``restore'' initialized
+malloc-managed memory regions. For example, the ``unexec'' hack popularized
+by GNU Emacs could potentially go away.
+@end itemize
+
+@node Implementation, , Overview, Top
+@chapter Implementation
+
+The @code{mmalloc} functions contain no internal static state. All
+@code{mmalloc} internal data is allocated in the mapped in region, along
+with the user data that it manages. This allows it to manage multiple
+such regions and to ``pick up where it left off'' when such regions are
+later dynamically mapped back in.
+
+In some sense, malloc has been ``purified'' to contain no internal state
+information and generalized to use multiple memory regions rather than a
+single region managed by @code{sbrk}. However the new routines now need an
+extra parameter which informs @code{mmalloc} which memory region it is dealing
+with (along with other information). This parameter is called the
+@dfn{malloc descriptor}.
+
+The functions initially provided by @code{mmalloc} are:
+
+@example
+void *mmalloc_attach (int fd, void *baseaddr);
+void *mmalloc_detach (void *md);
+int mmalloc_errno (void *md);
+int mmalloc_setkey (void *md, int keynum, void *key);
+void *mmalloc_getkey (void *md, int keynum);
+
+void *mmalloc (void *md, size_t size);
+void *mrealloc (void *md, void *ptr, size_t size);
+void *mvalloc (void *md, size_t size);
+void mfree (void *md, void *ptr);
+@end example
+
+@menu
+* Compatibility:: Backwards Compatibility
+* Functions:: Function Descriptions
+@end menu
+
+@node Compatibility, Functions, Implementation, Implementation
+@section Backwards Compatibility
+
+To allow a single malloc package to be used in a given application,
+provision is made for the traditional @code{malloc}, @code{realloc}, and
+@code{free} functions to be implemented as special cases of the
+@code{mmalloc} functions. In particular, if any of the functions that
+expect malloc descriptors are called with a @code{NULL} pointer rather than a
+valid malloc descriptor, then they default to using an @code{sbrk} managed
+region.
+The @code{mmalloc} package provides compatible @code{malloc}, @code{realloc},
+and @code{free} functions using this mechanism internally.
+Applications can avoid this extra interface layer by simply including the
+following defines:
+
+@example
+#define malloc(size) mmalloc ((void *)0, (size))
+#define realloc(ptr,size) mrealloc ((void *)0, (ptr), (size));
+#define free(ptr) mfree ((void *)0, (ptr))
+@end example
+
+@noindent
+or replace the existing @code{malloc}, @code{realloc}, and @code{free}
+calls with the above patterns if using @code{#define} causes problems.
+
+@node Functions, , Compatibility, Implementation
+@section Function Descriptions
+
+These are the details on the functions that make up the @code{mmalloc}
+package.
+
+@table @code
+@item void *mmalloc_attach (int @var{fd}, void *@var{baseaddr});
+Initialize access to a @code{mmalloc} managed region.
+
+If @var{fd} is a valid file descriptor for an open file, then data for the
+@code{mmalloc} managed region is mapped to that file. Otherwise
+@file{/dev/zero} is used and the data will not exist in any filesystem object.
+
+If the open file corresponding to @var{fd} is from a previous use of
+@code{mmalloc} and passes some basic sanity checks to ensure that it is
+compatible with the current @code{mmalloc} package, then its data is
+mapped in and is immediately accessible at the same addresses in
+the current process as the process that created the file.
+
+If @var{baseaddr} is not @code{NULL}, the mapping is established
+starting at the specified address in the process address space. If
+@var{baseaddr} is @code{NULL}, the @code{mmalloc} package chooses a
+suitable address at which to start the mapped region, which will be the
+value of the previous mapping if opening an existing file which was
+previously built by @code{mmalloc}, or for new files will be a value
+chosen by @code{mmap}.
+
+Specifying @var{baseaddr} provides more control over where the regions
+start and how big they can be before bumping into existing mapped
+regions or future mapped regions.
+
+On success, returns a malloc descriptor which is used in subsequent
+calls to other @code{mmalloc} package functions. It is explicitly
+@samp{void *} (@samp{char *} for systems that don't fully support
+@code{void}) so that users of the package don't have to worry about the
+actual implementation details.
+
+On failure returns @code{NULL}.
+
+@item void *mmalloc_detach (void *@var{md});
+Terminate access to a @code{mmalloc} managed region identified by the
+descriptor @var{md}, by closing the base file and unmapping all memory
+pages associated with the region.
+
+Returns @code{NULL} on success.
+
+Returns the malloc descriptor on failure, which can subsequently
+be used for further action (such as obtaining more information about
+the nature of the failure).
+
+@item void *mmalloc (void *@var{md}, size_t @var{size});
+Given an @code{mmalloc} descriptor @var{md}, allocate additional memory of
+@var{size} bytes in the associated mapped region.
+
+@item *mrealloc (void *@var{md}, void *@var{ptr}, size_t @var{size});
+Given an @code{mmalloc} descriptor @var{md} and a pointer to memory
+previously allocated by @code{mmalloc} in @var{ptr}, reallocate the
+memory to be @var{size} bytes long, possibly moving the existing
+contents of memory if necessary.
+
+@item void *mvalloc (void *@var{md}, size_t @var{size});
+Like @code{mmalloc} but the resulting memory is aligned on a page boundary.
+
+@item void mfree (void *@var{md}, void *@var{ptr});
+Given an @code{mmalloc} descriptor @var{md} and a pointer to memory previously
+allocated by @code{mmalloc} in @var{ptr}, free the previously allocated memory.
+
+@item int mmalloc_errno (void *@var{md});
+Given a @code{mmalloc} descriptor, if the last @code{mmalloc} operation
+failed for some reason due to a system call failure, then
+returns the associated @code{errno}. Returns 0 otherwise.
+(This function is not yet implemented).
+@end table
+
+@bye
diff --git a/gnu/usr.bin/gdb/mmalloc/mmap-sup.c b/gnu/usr.bin/gdb/mmalloc/mmap-sup.c
new file mode 100644
index 0000000..37b3079
--- /dev/null
+++ b/gnu/usr.bin/gdb/mmalloc/mmap-sup.c
@@ -0,0 +1,144 @@
+/* Support for an sbrk-like function that uses mmap.
+ Copyright 1992 Free Software Foundation, Inc.
+
+ Contributed by Fred Fish at Cygnus Support. fnf@cygnus.com
+
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#if defined(HAVE_MMAP)
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+#include "mmalloc.h"
+
+extern int munmap PARAMS ((caddr_t, size_t)); /* Not in any header file */
+
+/* Cache the pagesize for the current host machine. Note that if the host
+ does not readily provide a getpagesize() function, we need to emulate it
+ elsewhere, not clutter up this file with lots of kluges to try to figure
+ it out. */
+
+static size_t pagesize;
+extern int getpagesize PARAMS ((void));
+
+#define PAGE_ALIGN(addr) (caddr_t) (((long)(addr) + pagesize - 1) & \
+ ~(pagesize - 1))
+
+/* Get core for the memory region specified by MDP, using SIZE as the
+ amount to either add to or subtract from the existing region. Works
+ like sbrk(), but using mmap(). */
+
+PTR
+__mmalloc_mmap_morecore (mdp, size)
+ struct mdesc *mdp;
+ int size;
+{
+ PTR result = NULL;
+ off_t foffset; /* File offset at which new mapping will start */
+ size_t mapbytes; /* Number of bytes to map */
+ caddr_t moveto; /* Address where we wish to move "break value" to */
+ caddr_t mapto; /* Address we actually mapped to */
+ char buf = 0; /* Single byte to write to extend mapped file */
+
+ if (pagesize == 0)
+ {
+ pagesize = getpagesize ();
+ }
+ if (size == 0)
+ {
+ /* Just return the current "break" value. */
+ result = mdp -> breakval;
+ }
+ else if (size < 0)
+ {
+ /* We are deallocating memory. If the amount requested would cause
+ us to try to deallocate back past the base of the mmap'd region
+ then do nothing, and return NULL. Otherwise, deallocate the
+ memory and return the old break value. */
+ if (mdp -> breakval + size >= mdp -> base)
+ {
+ result = (PTR) mdp -> breakval;
+ mdp -> breakval += size;
+ moveto = PAGE_ALIGN (mdp -> breakval);
+ munmap (moveto, (size_t) (mdp -> top - moveto));
+ mdp -> top = moveto;
+ }
+ }
+ else
+ {
+ /* We are allocating memory. Make sure we have an open file
+ descriptor and then go on to get the memory. */
+ if (mdp -> fd < 0)
+ {
+ result = NULL;
+ }
+ else if (mdp -> breakval + size > mdp -> top)
+ {
+ /* The request would move us past the end of the currently
+ mapped memory, so map in enough more memory to satisfy
+ the request. This means we also have to grow the mapped-to
+ file by an appropriate amount, since mmap cannot be used
+ to extend a file. */
+ moveto = PAGE_ALIGN (mdp -> breakval + size);
+ mapbytes = moveto - mdp -> top;
+ foffset = mdp -> top - mdp -> base;
+ /* FIXME: Test results of lseek() and write() */
+ lseek (mdp -> fd, foffset + mapbytes - 1, SEEK_SET);
+ write (mdp -> fd, &buf, 1);
+ mapto = mmap (mdp -> top, mapbytes, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_FIXED, mdp -> fd, foffset);
+ if (mapto == mdp -> top)
+ {
+ mdp -> top = moveto;
+ result = (PTR) mdp -> breakval;
+ mdp -> breakval += size;
+ }
+ }
+ else
+ {
+ result = (PTR) mdp -> breakval;
+ mdp -> breakval += size;
+ }
+ }
+ return (result);
+}
+
+PTR
+__mmalloc_remap_core (mdp)
+ struct mdesc *mdp;
+{
+ caddr_t base;
+
+ /* FIXME: Quick hack, needs error checking and other attention. */
+
+ base = mmap (mdp -> base, mdp -> top - mdp -> base,
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED,
+ mdp -> fd, 0);
+ return ((PTR) base);
+}
+
+#else /* defined(HAVE_MMAP) */
+/* Prevent "empty translation unit" warnings from the idiots at X3J11. */
+static char ansi_c_idiots = 69;
+#endif /* defined(HAVE_MMAP) */
diff --git a/gnu/usr.bin/gdb/mmalloc/mmcheck.c b/gnu/usr.bin/gdb/mmalloc/mmcheck.c
new file mode 100644
index 0000000..c3e29d3
--- /dev/null
+++ b/gnu/usr.bin/gdb/mmalloc/mmcheck.c
@@ -0,0 +1,196 @@
+/* Standard debugging hooks for `mmalloc'.
+ Copyright 1990, 1991, 1992 Free Software Foundation
+
+ Written May 1989 by Mike Haertel.
+ Heavily modified Mar 1992 by Fred Fish (fnf@cygnus.com)
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+
+ The author may be reached (Email) at the address mike@ai.mit.edu,
+ or (US mail) as Mike Haertel c/o Free Software Foundation. */
+
+#include "mmalloc.h"
+
+/* Default function to call when something awful happens. The application
+ can specify an alternate function to be called instead (and probably will
+ want to). */
+
+extern void abort PARAMS ((void));
+
+/* Arbitrary magical numbers. */
+
+#define MAGICWORD (unsigned int) 0xfedabeeb /* Active chunk */
+#define MAGICWORDFREE (unsigned int) 0xdeadbeef /* Inactive chunk */
+#define MAGICBYTE ((char) 0xd7)
+
+/* Each memory allocation is bounded by a header structure and a trailer
+ byte. I.E.
+
+ <size><magicword><user's allocation><magicbyte>
+
+ The pointer returned to the user points to the first byte in the
+ user's allocation area. The magic word can be tested to detect
+ buffer underruns and the magic byte can be tested to detect overruns. */
+
+struct hdr
+ {
+ size_t size; /* Exact size requested by user. */
+ unsigned long int magic; /* Magic number to check header integrity. */
+ };
+
+/* Check the magicword and magicbyte, and if either is corrupted then
+ call the emergency abort function specified for the heap in use. */
+
+static void
+checkhdr (mdp, hdr)
+ struct mdesc *mdp;
+ CONST struct hdr *hdr;
+{
+ if (hdr -> magic != MAGICWORD ||
+ ((char *) &hdr[1])[hdr -> size] != MAGICBYTE)
+ {
+ (*mdp -> abortfunc)();
+ }
+}
+
+static void
+mfree_check (md, ptr)
+ PTR md;
+ PTR ptr;
+{
+ struct hdr *hdr = ((struct hdr *) ptr) - 1;
+ struct mdesc *mdp;
+
+ mdp = MD_TO_MDP (md);
+ checkhdr (mdp, hdr);
+ hdr -> magic = MAGICWORDFREE;
+ mdp -> mfree_hook = NULL;
+ mfree (md, (PTR)hdr);
+ mdp -> mfree_hook = mfree_check;
+}
+
+static PTR
+mmalloc_check (md, size)
+ PTR md;
+ size_t size;
+{
+ struct hdr *hdr;
+ struct mdesc *mdp;
+ size_t nbytes;
+
+ mdp = MD_TO_MDP (md);
+ mdp -> mmalloc_hook = NULL;
+ nbytes = sizeof (struct hdr) + size + 1;
+ hdr = (struct hdr *) mmalloc (md, nbytes);
+ mdp -> mmalloc_hook = mmalloc_check;
+ if (hdr != NULL)
+ {
+ hdr -> size = size;
+ hdr -> magic = MAGICWORD;
+ hdr++;
+ *((char *) hdr + size) = MAGICBYTE;
+ }
+ return ((PTR) hdr);
+}
+
+static PTR
+mrealloc_check (md, ptr, size)
+ PTR md;
+ PTR ptr;
+ size_t size;
+{
+ struct hdr *hdr = ((struct hdr *) ptr) - 1;
+ struct mdesc *mdp;
+ size_t nbytes;
+
+ mdp = MD_TO_MDP (md);
+ checkhdr (mdp, hdr);
+ mdp -> mfree_hook = NULL;
+ mdp -> mmalloc_hook = NULL;
+ mdp -> mrealloc_hook = NULL;
+ nbytes = sizeof (struct hdr) + size + 1;
+ hdr = (struct hdr *) mrealloc (md, (PTR) hdr, nbytes);
+ mdp -> mfree_hook = mfree_check;
+ mdp -> mmalloc_hook = mmalloc_check;
+ mdp -> mrealloc_hook = mrealloc_check;
+ if (hdr != NULL)
+ {
+ hdr -> size = size;
+ hdr++;
+ *((char *) hdr + size) = MAGICBYTE;
+ }
+ return ((PTR) hdr);
+}
+
+/* Turn on default checking for mmalloc/mrealloc/mfree, for the heap specified
+ by MD. If FUNC is non-NULL, it is a pointer to the function to call
+ to abort whenever memory corruption is detected. By default, this is the
+ standard library function abort().
+
+ Note that we disallow installation of initial checking hooks if mmalloc
+ has been called at any time for this particular heap, since if any region
+ that is allocated prior to installation of the hooks is subsequently
+ reallocated or freed after installation of the hooks, it is guaranteed
+ to trigger a memory corruption error. We do this by checking the state
+ of the MMALLOC_INITIALIZED flag.
+
+ However, we can call this function at any time after the initial call,
+ to update the function pointers to the checking routines and to the
+ user defined corruption handler routine, as long as these function pointers
+ have been previously extablished by the initial call. Note that we
+ do this automatically when remapping an previously used heap, to ensure
+ that the hooks get updated to the correct values, although the corruption
+ handler pointer gets set back to the default. The application can then
+ call mmcheck to use a different corruption handler if desired.
+
+ Returns non-zero if checking is successfully enabled, zero otherwise. */
+
+int
+mmcheck (md, func)
+ PTR md;
+ void (*func) PARAMS ((void));
+{
+ struct mdesc *mdp;
+ int rtnval;
+
+ mdp = MD_TO_MDP (md);
+
+ /* We can safely set or update the abort function at any time, regardless
+ of whether or not we successfully do anything else. */
+
+ mdp -> abortfunc = (func != NULL ? func : abort);
+
+ /* If we haven't yet called mmalloc the first time for this heap, or if we
+ have hooks that were previously installed, then allow the hooks to be
+ initialized or updated. */
+
+ if (1 /* FIXME: Always allow installation for now. */ ||
+ !(mdp -> flags & MMALLOC_INITIALIZED) ||
+ (mdp -> mfree_hook != NULL))
+ {
+ mdp -> mfree_hook = mfree_check;
+ mdp -> mmalloc_hook = mmalloc_check;
+ mdp -> mrealloc_hook = mrealloc_check;
+ mdp -> flags |= MMALLOC_MMCHECK_USED;
+ rtnval = 1;
+ }
+ else
+ {
+ rtnval = 0;
+ }
+
+ return (rtnval);
+}
diff --git a/gnu/usr.bin/gdb/mmalloc/mmemalign.c b/gnu/usr.bin/gdb/mmalloc/mmemalign.c
new file mode 100644
index 0000000..63350a2
--- /dev/null
+++ b/gnu/usr.bin/gdb/mmalloc/mmemalign.c
@@ -0,0 +1,64 @@
+/* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include "mmalloc.h"
+
+PTR
+mmemalign (md, alignment, size)
+ PTR md;
+ size_t alignment;
+ size_t size;
+{
+ PTR result;
+ unsigned long int adj;
+ struct alignlist *l;
+ struct mdesc *mdp;
+
+ size = ((size + alignment - 1) / alignment) * alignment;
+
+ if ((result = mmalloc (md, size)) != NULL)
+ {
+ adj = RESIDUAL (result, alignment);
+ if (adj != 0)
+ {
+ mdp = MD_TO_MDP (md);
+ for (l = mdp -> aligned_blocks; l != NULL; l = l -> next)
+ {
+ if (l -> aligned == NULL)
+ {
+ /* This slot is free. Use it. */
+ break;
+ }
+ }
+ if (l == NULL)
+ {
+ l = (struct alignlist *) mmalloc (md, sizeof (struct alignlist));
+ if (l == NULL)
+ {
+ mfree (md, result);
+ return (NULL);
+ }
+ }
+ l -> exact = result;
+ result = l -> aligned = (char *) result + alignment - adj;
+ l -> next = mdp -> aligned_blocks;
+ mdp -> aligned_blocks = l;
+ }
+ }
+ return (result);
+}
diff --git a/gnu/usr.bin/gdb/mmalloc/mmstats.c b/gnu/usr.bin/gdb/mmalloc/mmstats.c
new file mode 100644
index 0000000..d3846eb
--- /dev/null
+++ b/gnu/usr.bin/gdb/mmalloc/mmstats.c
@@ -0,0 +1,46 @@
+/* Access the statistics maintained by `mmalloc'.
+ Copyright 1990, 1991, 1992 Free Software Foundation
+
+ Written May 1989 by Mike Haertel.
+ Modified Mar 1992 by Fred Fish. (fnf@cygnus.com)
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+
+ The author may be reached (Email) at the address mike@ai.mit.edu,
+ or (US mail) as Mike Haertel c/o Free Software Foundation. */
+
+#include "mmalloc.h"
+
+/* FIXME: See the comment in mmalloc.h where struct mstats is defined.
+ None of the internal mmalloc structures should be externally visible
+ outside the library. */
+
+struct mstats
+mmstats (md)
+ PTR md;
+{
+ struct mstats result;
+ struct mdesc *mdp;
+
+ mdp = MD_TO_MDP (md);
+ result.bytes_total =
+ (char *) mdp -> morecore (mdp, 0) - mdp -> heapbase;
+ result.chunks_used = mdp -> heapstats.chunks_used;
+ result.bytes_used = mdp -> heapstats.bytes_used;
+ result.chunks_free = mdp -> heapstats.chunks_free;
+ result.bytes_free = mdp -> heapstats.bytes_free;
+ return (result);
+}
diff --git a/gnu/usr.bin/gdb/mmalloc/mmtrace.c b/gnu/usr.bin/gdb/mmalloc/mmtrace.c
new file mode 100644
index 0000000..73368a1
--- /dev/null
+++ b/gnu/usr.bin/gdb/mmalloc/mmtrace.c
@@ -0,0 +1,166 @@
+/* More debugging hooks for `mmalloc'.
+ Copyright 1991, 1992 Free Software Foundation
+
+ Written April 2, 1991 by John Gilmore of Cygnus Support
+ Based on mcheck.c by Mike Haertel.
+ Modified Mar 1992 by Fred Fish. (fnf@cygnus.com)
+
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "mmalloc.h"
+
+#ifndef __GNU_LIBRARY__
+extern char *getenv ();
+#endif
+
+static FILE *mallstream;
+
+#if 0 /* FIXME: Disabled for now. */
+static char mallenv[] = "MALLOC_TRACE";
+static char mallbuf[BUFSIZ]; /* Buffer for the output. */
+#endif
+
+/* Address to breakpoint on accesses to... */
+static PTR mallwatch;
+
+/* Old hook values. */
+
+static void (*old_mfree_hook) PARAMS ((PTR, PTR));
+static PTR (*old_mmalloc_hook) PARAMS ((PTR, size_t));
+static PTR (*old_mrealloc_hook) PARAMS ((PTR, PTR, size_t));
+
+/* This function is called when the block being alloc'd, realloc'd, or
+ freed has an address matching the variable "mallwatch". In a debugger,
+ set "mallwatch" to the address of interest, then put a breakpoint on
+ tr_break. */
+
+static void
+tr_break ()
+{
+}
+
+static void
+tr_freehook (md, ptr)
+ PTR md;
+ PTR ptr;
+{
+ struct mdesc *mdp;
+
+ mdp = MD_TO_MDP (md);
+ /* Be sure to print it first. */
+ fprintf (mallstream, "- %08x\n", (unsigned int) ptr);
+ if (ptr == mallwatch)
+ tr_break ();
+ mdp -> mfree_hook = old_mfree_hook;
+ mfree (md, ptr);
+ mdp -> mfree_hook = tr_freehook;
+}
+
+static PTR
+tr_mallochook (md, size)
+ PTR md;
+ size_t size;
+{
+ PTR hdr;
+ struct mdesc *mdp;
+
+ mdp = MD_TO_MDP (md);
+ mdp -> mmalloc_hook = old_mmalloc_hook;
+ hdr = (PTR) mmalloc (md, size);
+ mdp -> mmalloc_hook = tr_mallochook;
+
+ /* We could be printing a NULL here; that's OK. */
+ fprintf (mallstream, "+ %08x %x\n", (unsigned int) hdr, size);
+
+ if (hdr == mallwatch)
+ tr_break ();
+
+ return (hdr);
+}
+
+static PTR
+tr_reallochook (md, ptr, size)
+ PTR md;
+ PTR ptr;
+ size_t size;
+{
+ PTR hdr;
+ struct mdesc *mdp;
+
+ mdp = MD_TO_MDP (md);
+
+ if (ptr == mallwatch)
+ tr_break ();
+
+ mdp -> mfree_hook = old_mfree_hook;
+ mdp -> mmalloc_hook = old_mmalloc_hook;
+ mdp -> mrealloc_hook = old_mrealloc_hook;
+ hdr = (PTR) mrealloc (md, ptr, size);
+ mdp -> mfree_hook = tr_freehook;
+ mdp -> mmalloc_hook = tr_mallochook;
+ mdp -> mrealloc_hook = tr_reallochook;
+ if (hdr == NULL)
+ /* Failed realloc. */
+ fprintf (mallstream, "! %08x %x\n", (unsigned int) ptr, size);
+ else
+ fprintf (mallstream, "< %08x\n> %08x %x\n", (unsigned int) ptr,
+ (unsigned int) hdr, size);
+
+ if (hdr == mallwatch)
+ tr_break ();
+
+ return hdr;
+}
+
+/* We enable tracing if either the environment variable MALLOC_TRACE
+ is set, or if the variable mallwatch has been patched to an address
+ that the debugging user wants us to stop on. When patching mallwatch,
+ don't forget to set a breakpoint on tr_break! */
+
+int
+mmtrace ()
+{
+#if 0 /* FIXME! This is disabled for now until we figure out how to
+ maintain a stack of hooks per heap, since we might have other
+ hooks (such as set by mmcheck) active also. */
+ char *mallfile;
+
+ mallfile = getenv (mallenv);
+ if (mallfile != NULL || mallwatch != NULL)
+ {
+ mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "w");
+ if (mallstream != NULL)
+ {
+ /* Be sure it doesn't mmalloc its buffer! */
+ setbuf (mallstream, mallbuf);
+ fprintf (mallstream, "= Start\n");
+ old_mfree_hook = mdp -> mfree_hook;
+ mdp -> mfree_hook = tr_freehook;
+ old_mmalloc_hook = mdp -> mmalloc_hook;
+ mdp -> mmalloc_hook = tr_mallochook;
+ old_mrealloc_hook = mdp -> mrealloc_hook;
+ mdp -> mrealloc_hook = tr_reallochook;
+ }
+ }
+
+#endif /* 0 */
+
+ return (1);
+}
+
diff --git a/gnu/usr.bin/gdb/mmalloc/mrealloc.c b/gnu/usr.bin/gdb/mmalloc/mrealloc.c
new file mode 100644
index 0000000..85bec56
--- /dev/null
+++ b/gnu/usr.bin/gdb/mmalloc/mrealloc.c
@@ -0,0 +1,160 @@
+/* Change the size of a block allocated by `mmalloc'.
+ Copyright 1990, 1991 Free Software Foundation
+ Written May 1989 by Mike Haertel.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+
+ The author may be reached (Email) at the address mike@ai.mit.edu,
+ or (US mail) as Mike Haertel c/o Free Software Foundation. */
+
+#include <string.h> /* Prototypes for memcpy, memmove, memset, etc */
+
+#include "mmalloc.h"
+
+/* Resize the given region to the new size, returning a pointer
+ to the (possibly moved) region. This is optimized for speed;
+ some benchmarks seem to indicate that greater compactness is
+ achieved by unconditionally allocating and copying to a
+ new region. This module has incestuous knowledge of the
+ internals of both mfree and mmalloc. */
+
+PTR
+mrealloc (md, ptr, size)
+ PTR md;
+ PTR ptr;
+ size_t size;
+{
+ struct mdesc *mdp;
+ PTR result;
+ int type;
+ size_t block, blocks, oldlimit;
+
+ if (size == 0)
+ {
+ mfree (md, ptr);
+ return (mmalloc (md, 0));
+ }
+ else if (ptr == NULL)
+ {
+ return (mmalloc (md, size));
+ }
+
+ mdp = MD_TO_MDP (md);
+
+ if (mdp -> mrealloc_hook != NULL)
+ {
+ return ((*mdp -> mrealloc_hook) (md, ptr, size));
+ }
+
+ block = BLOCK (ptr);
+
+ type = mdp -> heapinfo[block].busy.type;
+ switch (type)
+ {
+ case 0:
+ /* Maybe reallocate a large block to a small fragment. */
+ if (size <= BLOCKSIZE / 2)
+ {
+ result = mmalloc (md, size);
+ if (result != NULL)
+ {
+ memcpy (result, ptr, size);
+ mfree (md, ptr);
+ return (result);
+ }
+ }
+
+ /* The new size is a large allocation as well;
+ see if we can hold it in place. */
+ blocks = BLOCKIFY (size);
+ if (blocks < mdp -> heapinfo[block].busy.info.size)
+ {
+ /* The new size is smaller; return excess memory to the free list. */
+ mdp -> heapinfo[block + blocks].busy.type = 0;
+ mdp -> heapinfo[block + blocks].busy.info.size
+ = mdp -> heapinfo[block].busy.info.size - blocks;
+ mdp -> heapinfo[block].busy.info.size = blocks;
+ mfree (md, ADDRESS (block + blocks));
+ result = ptr;
+ }
+ else if (blocks == mdp -> heapinfo[block].busy.info.size)
+ {
+ /* No size change necessary. */
+ result = ptr;
+ }
+ else
+ {
+ /* Won't fit, so allocate a new region that will.
+ Free the old region first in case there is sufficient
+ adjacent free space to grow without moving. */
+ blocks = mdp -> heapinfo[block].busy.info.size;
+ /* Prevent free from actually returning memory to the system. */
+ oldlimit = mdp -> heaplimit;
+ mdp -> heaplimit = 0;
+ mfree (md, ptr);
+ mdp -> heaplimit = oldlimit;
+ result = mmalloc (md, size);
+ if (result == NULL)
+ {
+ mmalloc (md, blocks * BLOCKSIZE);
+ return (NULL);
+ }
+ if (ptr != result)
+ {
+ memmove (result, ptr, blocks * BLOCKSIZE);
+ }
+ }
+ break;
+
+ default:
+ /* Old size is a fragment; type is logarithm
+ to base two of the fragment size. */
+ if (size > (size_t) (1 << (type - 1)) && size <= (size_t) (1 << type))
+ {
+ /* The new size is the same kind of fragment. */
+ result = ptr;
+ }
+ else
+ {
+ /* The new size is different; allocate a new space,
+ and copy the lesser of the new size and the old. */
+ result = mmalloc (md, size);
+ if (result == NULL)
+ {
+ return (NULL);
+ }
+ memcpy (result, ptr, MIN (size, (size_t) 1 << type));
+ mfree (md, ptr);
+ }
+ break;
+ }
+
+ return (result);
+}
+
+/* When using this package, provide a version of malloc/realloc/free built
+ on top of it, so that if we use the default sbrk() region we will not
+ collide with another malloc package trying to do the same thing, if
+ the application contains any "hidden" calls to malloc/realloc/free (such
+ as inside a system library). */
+
+PTR
+realloc (ptr, size)
+ PTR ptr;
+ size_t size;
+{
+ return (mrealloc ((PTR) NULL, ptr, size));
+}
diff --git a/gnu/usr.bin/gdb/mmalloc/mvalloc.c b/gnu/usr.bin/gdb/mmalloc/mvalloc.c
new file mode 100644
index 0000000..1ffba78
--- /dev/null
+++ b/gnu/usr.bin/gdb/mmalloc/mvalloc.c
@@ -0,0 +1,40 @@
+/* Allocate memory on a page boundary.
+ Copyright (C) 1991 Free Software Foundation, Inc.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include "mmalloc.h"
+
+/* Cache the pagesize for the current host machine. Note that if the host
+ does not readily provide a getpagesize() function, we need to emulate it
+ elsewhere, not clutter up this file with lots of kluges to try to figure
+ it out. */
+
+static size_t pagesize;
+extern int getpagesize PARAMS ((void));
+
+PTR
+mvalloc (md, size)
+ PTR md;
+ size_t size;
+{
+ if (pagesize == 0)
+ {
+ pagesize = getpagesize ();
+ }
+
+ return (mmemalign (md, pagesize, size));
+}
diff --git a/gnu/usr.bin/gdb/mmalloc/sbrk-sup.c b/gnu/usr.bin/gdb/mmalloc/sbrk-sup.c
new file mode 100644
index 0000000..e6a57d6
--- /dev/null
+++ b/gnu/usr.bin/gdb/mmalloc/sbrk-sup.c
@@ -0,0 +1,96 @@
+/* Support for sbrk() regions.
+ Copyright 1992 Free Software Foundation, Inc.
+ Contributed by Fred Fish at Cygnus Support. fnf@cygnus.com
+
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <string.h> /* Prototypes for memcpy, memmove, memset, etc */
+
+#include "mmalloc.h"
+
+extern PTR sbrk ();
+
+/* The mmalloc() package can use a single implicit malloc descriptor
+ for mmalloc/mrealloc/mfree operations which do not supply an explicit
+ descriptor. For these operations, sbrk() is used to obtain more core
+ from the system, or return core. This allows mmalloc() to provide
+ backwards compatibility with the non-mmap'd version. */
+
+struct mdesc *__mmalloc_default_mdp;
+
+/* Use sbrk() to get more core. */
+
+static PTR
+sbrk_morecore (mdp, size)
+ struct mdesc *mdp;
+ int size;
+{
+ PTR result;
+
+ if ((result = sbrk (size)) == (PTR) -1)
+ {
+ result = NULL;
+ }
+ else
+ {
+ mdp -> breakval += size;
+ mdp -> top += size;
+ }
+ return (result);
+}
+
+/* Initialize the default malloc descriptor if this is the first time
+ a request has been made to use the default sbrk'd region.
+
+ Since no alignment guarantees are made about the initial value returned
+ by sbrk, test the initial value and (if necessary) sbrk enough additional
+ memory to start off with alignment to BLOCKSIZE. We actually only need
+ it aligned to an alignment suitable for any object, so this is overkill.
+ But at most it wastes just part of one BLOCKSIZE chunk of memory and
+ minimizes portability problems by avoiding us having to figure out
+ what the actual minimal alignment is. The rest of the malloc code
+ avoids this as well, by always aligning to the minimum of the requested
+ size rounded up to a power of two, or to BLOCKSIZE.
+
+ Note that we are going to use some memory starting at this initial sbrk
+ address for the sbrk region malloc descriptor, which is a struct, so the
+ base address must be suitably aligned. */
+
+struct mdesc *
+__mmalloc_sbrk_init ()
+{
+ PTR base;
+ unsigned int adj;
+
+ base = sbrk (0);
+ adj = RESIDUAL (base, BLOCKSIZE);
+ if (adj != 0)
+ {
+ sbrk (BLOCKSIZE - adj);
+ base = sbrk (0);
+ }
+ __mmalloc_default_mdp = (struct mdesc *) sbrk (sizeof (struct mdesc));
+ memset ((char *) __mmalloc_default_mdp, 0, sizeof (struct mdesc));
+ __mmalloc_default_mdp -> morecore = sbrk_morecore;
+ __mmalloc_default_mdp -> base = base;
+ __mmalloc_default_mdp -> breakval = __mmalloc_default_mdp -> top = sbrk (0);
+ __mmalloc_default_mdp -> fd = -1;
+ return (__mmalloc_default_mdp);
+}
+
+
diff --git a/gnu/usr.bin/gdb/ngdb.i386/Makefile b/gnu/usr.bin/gdb/ngdb.i386/Makefile
new file mode 100644
index 0000000..3bf4c6c
--- /dev/null
+++ b/gnu/usr.bin/gdb/ngdb.i386/Makefile
@@ -0,0 +1,27 @@
+# %W% (Berkeley) %G%
+
+.include "../config/Makefile.$(MACHINE)"
+
+PROG= ngdb
+SRCS= i386bsd-dep.c blockframe.c
+GDBOBJS+= i386-pinsn.o \
+ breakpoint.o command.o copying.o core.o \
+ cplus-dem.o dbxread.o environ.o eval.o expprint.o \
+ expread.o findvar.o infcmd.o inflow.o infrun.o \
+ main.o obstack.o printcmd.o regex.o remote.o \
+ remote-sl.o source.o stack.o symmisc.o symtab.o \
+ utils.o valarith.o valops.o valprint.o values.o \
+ version.o \
+ funmap.o history.o keymaps.o readline.o \
+ init.o
+CFLAGS+= -g -I$(.CURDIR) -I.. -I$(.CURDIR)/.. -I$(.CURDIR)/../config \
+ -I/usr/src/sys.newvm \
+ -DNEWVM -DHAVE_VPRINTF -DVI_MODE -DKERNELDEBUG
+# CC= /usr/old/bin/cc
+# CC= cc -traditional
+LDADD+= $(GDBOBJS:S/^/..\//g) -ltermcap
+NOMAN= noman
+
+.PATH: $(.CURDIR)/../config $(.CURDIR)/..
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/gdb/obstack.c b/gnu/usr.bin/gdb/obstack.c
new file mode 100644
index 0000000..6f4b282
--- /dev/null
+++ b/gnu/usr.bin/gdb/obstack.c
@@ -0,0 +1,313 @@
+/* obstack.c - subroutines used implicitly by object stack macros
+ Copyright (C) 1988 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 1, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+In other words, you are welcome to use, share and improve this program.
+You are forbidden to forbid anyone else to use, share and improve
+what you give them. Help stamp out software-hoarding! */
+
+
+#include "obstack.h"
+
+#ifdef __STDC__
+#define POINTER void *
+#else
+#define POINTER char *
+#endif
+
+/* Determine default alignment. */
+struct fooalign {char x; double d;};
+#define DEFAULT_ALIGNMENT ((char *)&((struct fooalign *) 0)->d - (char *)0)
+/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT.
+ But in fact it might be less smart and round addresses to as much as
+ DEFAULT_ROUNDING. So we prepare for it to do that. */
+union fooround {long x; double d;};
+#define DEFAULT_ROUNDING (sizeof (union fooround))
+
+/* When we copy a long block of data, this is the unit to do it with.
+ On some machines, copying successive ints does not work;
+ in such a case, redefine COPYING_UNIT to `long' (if that works)
+ or `char' as a last resort. */
+#ifndef COPYING_UNIT
+#define COPYING_UNIT int
+#endif
+
+/* The non-GNU-C macros copy the obstack into this global variable
+ to avoid multiple evaluation. */
+
+struct obstack *_obstack;
+
+/* Initialize an obstack H for use. Specify chunk size SIZE (0 means default).
+ Objects start on multiples of ALIGNMENT (0 means use default).
+ CHUNKFUN is the function to use to allocate chunks,
+ and FREEFUN the function to free them. */
+
+void
+_obstack_begin (h, size, alignment, chunkfun, freefun)
+ struct obstack *h;
+ int size;
+ int alignment;
+ POINTER (*chunkfun) ();
+ void (*freefun) ();
+{
+ register struct _obstack_chunk* chunk; /* points to new chunk */
+
+ if (alignment == 0)
+ alignment = DEFAULT_ALIGNMENT;
+ if (size == 0)
+ /* Default size is what GNU malloc can fit in a 4096-byte block.
+ Pick a number small enough that when rounded up to DEFAULT_ROUNDING
+ it is still smaller than 4096 - 4. */
+ {
+ int extra = 4;
+ if (extra < DEFAULT_ROUNDING)
+ extra = DEFAULT_ROUNDING;
+ size = 4096 - extra;
+ }
+
+ h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun;
+ h->freefun = freefun;
+ h->chunk_size = size;
+ h->alignment_mask = alignment - 1;
+
+ chunk = h->chunk = (*h->chunkfun) (h->chunk_size);
+ h->next_free = h->object_base = chunk->contents;
+ h->chunk_limit = chunk->limit
+ = (char *) chunk + h->chunk_size;
+ chunk->prev = 0;
+}
+
+/* Allocate a new current chunk for the obstack *H
+ on the assumption that LENGTH bytes need to be added
+ to the current object, or a new object of length LENGTH allocated.
+ Copies any partial object from the end of the old chunk
+ to the beginning of the new one. */
+
+void
+_obstack_newchunk (h, length)
+ struct obstack *h;
+ int length;
+{
+ register struct _obstack_chunk* old_chunk = h->chunk;
+ register struct _obstack_chunk* new_chunk;
+ register long new_size;
+ register int obj_size = h->next_free - h->object_base;
+ register int i;
+
+ /* Compute size for new chunk. */
+ new_size = (obj_size + length) << 1;
+ if (new_size < h->chunk_size)
+ new_size = h->chunk_size;
+
+ /* Allocate and initialize the new chunk. */
+ new_chunk = h->chunk = (*h->chunkfun) (new_size);
+ new_chunk->prev = old_chunk;
+ new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size;
+
+ /* Move the existing object to the new chunk.
+ Word at a time is fast and is safe because these
+ structures are aligned at least that much. */
+ for (i = (obj_size + sizeof (COPYING_UNIT) - 1) / sizeof (COPYING_UNIT) - 1;
+ i >= 0; i--)
+ ((COPYING_UNIT *)new_chunk->contents)[i]
+ = ((COPYING_UNIT *)h->object_base)[i];
+
+ h->object_base = new_chunk->contents;
+ h->next_free = h->object_base + obj_size;
+}
+
+/* Return nonzero if object OBJ has been allocated from obstack H.
+ This is here for debugging.
+ If you use it in a program, you are probably losing. */
+
+int
+_obstack_allocated_p (h, obj)
+ struct obstack *h;
+ POINTER obj;
+{
+ register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk* plp; /* point to previous chunk if any */
+
+ lp = (h)->chunk;
+ while (lp != 0 && ((POINTER)lp > obj || (POINTER)(lp)->limit < obj))
+ {
+ plp = lp -> prev;
+ lp = plp;
+ }
+ return lp != 0;
+}
+
+/* Free objects in obstack H, including OBJ and everything allocate
+ more recently than OBJ. If OBJ is zero, free everything in H. */
+
+void
+#ifdef __STDC__
+#undef obstack_free
+obstack_free (struct obstack *h, POINTER obj)
+#else
+_obstack_free (h, obj)
+ struct obstack *h;
+ POINTER obj;
+#endif
+{
+ register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk* plp; /* point to previous chunk if any */
+
+ lp = (h)->chunk;
+ while (lp != 0 && ((POINTER)lp > obj || (POINTER)(lp)->limit < obj))
+ {
+ plp = lp -> prev;
+ (*h->freefun) (lp);
+ lp = plp;
+ }
+ if (lp)
+ {
+ (h)->object_base = (h)->next_free = (char *)(obj);
+ (h)->chunk_limit = lp->limit;
+ (h)->chunk = lp;
+ }
+ else if (obj != 0)
+ /* obj is not in any of the chunks! */
+ abort ();
+}
+
+/* Let same .o link with output of gcc and other compilers. */
+
+#ifdef __STDC__
+void
+_obstack_free (h, obj)
+ struct obstack *h;
+ POINTER obj;
+{
+ obstack_free (h, obj);
+}
+#endif
+
+#if 0
+/* These are now turned off because the applications do not use it
+ and it uses bcopy via obstack_grow, which causes trouble on sysV. */
+
+/* Now define the functional versions of the obstack macros.
+ Define them to simply use the corresponding macros to do the job. */
+
+#ifdef __STDC__
+/* These function definitions do not work with non-ANSI preprocessors;
+ they won't pass through the macro names in parentheses. */
+
+/* The function names appear in parentheses in order to prevent
+ the macro-definitions of the names from being expanded there. */
+
+POINTER (obstack_base) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_base (obstack);
+}
+
+POINTER (obstack_next_free) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_next_free (obstack);
+}
+
+int (obstack_object_size) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_object_size (obstack);
+}
+
+int (obstack_room) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_room (obstack);
+}
+
+void (obstack_grow) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ obstack_grow (obstack, pointer, length);
+}
+
+void (obstack_grow0) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ obstack_grow0 (obstack, pointer, length);
+}
+
+void (obstack_1grow) (obstack, character)
+ struct obstack *obstack;
+ int character;
+{
+ obstack_1grow (obstack, character);
+}
+
+void (obstack_blank) (obstack, length)
+ struct obstack *obstack;
+ int length;
+{
+ obstack_blank (obstack, length);
+}
+
+void (obstack_1grow_fast) (obstack, character)
+ struct obstack *obstack;
+ int character;
+{
+ obstack_1grow_fast (obstack, character);
+}
+
+void (obstack_blank_fast) (obstack, length)
+ struct obstack *obstack;
+ int length;
+{
+ obstack_blank_fast (obstack, length);
+}
+
+POINTER (obstack_finish) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_finish (obstack);
+}
+
+POINTER (obstack_alloc) (obstack, length)
+ struct obstack *obstack;
+ int length;
+{
+ return obstack_alloc (obstack, length);
+}
+
+POINTER (obstack_copy) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ return obstack_copy (obstack, pointer, length);
+}
+
+POINTER (obstack_copy0) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ return obstack_copy0 (obstack, pointer, length);
+}
+
+#endif /* __STDC__ */
+
+#endif /* 0 */
diff --git a/gnu/usr.bin/gdb/obstack.h b/gnu/usr.bin/gdb/obstack.h
new file mode 100644
index 0000000..27c017e
--- /dev/null
+++ b/gnu/usr.bin/gdb/obstack.h
@@ -0,0 +1,372 @@
+/* obstack.h - object stack macros
+ Copyright (C) 1988 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 1, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+In other words, you are welcome to use, share and improve this program.
+You are forbidden to forbid anyone else to use, share and improve
+what you give them. Help stamp out software-hoarding! */
+
+
+/* Summary:
+
+All the apparent functions defined here are macros. The idea
+is that you would use these pre-tested macros to solve a
+very specific set of problems, and they would run fast.
+Caution: no side-effects in arguments please!! They may be
+evaluated MANY times!!
+
+These macros operate a stack of objects. Each object starts life
+small, and may grow to maturity. (Consider building a word syllable
+by syllable.) An object can move while it is growing. Once it has
+been "finished" it never changes address again. So the "top of the
+stack" is typically an immature growing object, while the rest of the
+stack is of mature, fixed size and fixed address objects.
+
+These routines grab large chunks of memory, using a function you
+supply, called `obstack_chunk_alloc'. On occasion, they free chunks,
+by calling `obstack_chunk_free'. You must define them and declare
+them before using any obstack macros.
+
+Each independent stack is represented by a `struct obstack'.
+Each of the obstack macros expects a pointer to such a structure
+as the first argument.
+
+One motivation for this package is the problem of growing char strings
+in symbol tables. Unless you are "facist pig with a read-only mind"
+[Gosper's immortal quote from HAKMEM item 154, out of context] you
+would not like to put any arbitrary upper limit on the length of your
+symbols.
+
+In practice this often means you will build many short symbols and a
+few long symbols. At the time you are reading a symbol you don't know
+how long it is. One traditional method is to read a symbol into a
+buffer, realloc()ating the buffer every time you try to read a symbol
+that is longer than the buffer. This is beaut, but you still will
+want to copy the symbol from the buffer to a more permanent
+symbol-table entry say about half the time.
+
+With obstacks, you can work differently. Use one obstack for all symbol
+names. As you read a symbol, grow the name in the obstack gradually.
+When the name is complete, finalize it. Then, if the symbol exists already,
+free the newly read name.
+
+The way we do this is to take a large chunk, allocating memory from
+low addresses. When you want to build a aymbol in the chunk you just
+add chars above the current "high water mark" in the chunk. When you
+have finished adding chars, because you got to the end of the symbol,
+you know how long the chars are, and you can create a new object.
+Mostly the chars will not burst over the highest address of the chunk,
+because you would typically expect a chunk to be (say) 100 times as
+long as an average object.
+
+In case that isn't clear, when we have enough chars to make up
+the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed)
+so we just point to it where it lies. No moving of chars is
+needed and this is the second win: potentially long strings need
+never be explicitly shuffled. Once an object is formed, it does not
+change its address during its lifetime.
+
+When the chars burst over a chunk boundary, we allocate a larger
+chunk, and then copy the partly formed object from the end of the old
+chunk to the beggining of the new larger chunk. We then carry on
+accreting characters to the end of the object as we normaly would.
+
+A special macro is provided to add a single char at a time to a
+growing object. This allows the use of register variables, which
+break the ordinary 'growth' macro.
+
+Summary:
+ We allocate large chunks.
+ We carve out one object at a time from the current chunk.
+ Once carved, an object never moves.
+ We are free to append data of any size to the currently
+ growing object.
+ Exactly one object is growing in an obstack at any one time.
+ You can run one obstack per control block.
+ You may have as many control blocks as you dare.
+ Because of the way we do it, you can `unwind' a obstack
+ back to a previous state. (You may remove objects much
+ as you would with a stack.)
+*/
+
+
+/* Don't do the contents of this file more than once. */
+
+#ifndef __OBSTACKS__
+#define __OBSTACKS__
+
+/* We use subtraction of (char *)0 instead of casting to int
+ because on word-addressable machines a simple cast to int
+ may ignore the byte-within-word field of the pointer. */
+
+#ifndef __PTR_TO_INT
+#define __PTR_TO_INT(P) ((P) - (char *)0)
+#endif
+
+#ifndef __INT_TO_PTR
+#define __INT_TO_PTR(P) ((P) + (char *)0)
+#endif
+
+struct _obstack_chunk /* Lives at front of each chunk. */
+{
+ char *limit; /* 1 past end of this chunk */
+ struct _obstack_chunk *prev; /* address of prior chunk or NULL */
+ char contents[4]; /* objects begin here */
+};
+
+struct obstack /* control current object in current chunk */
+{
+ long chunk_size; /* preferred size to allocate chunks in */
+ struct _obstack_chunk* chunk; /* address of current struct obstack_chunk */
+ char *object_base; /* address of object we are building */
+ char *next_free; /* where to add next char to current object */
+ char *chunk_limit; /* address of char after current chunk */
+ int temp; /* Temporary for some macros. */
+ int alignment_mask; /* Mask of alignment for each object. */
+ struct _obstack_chunk *(*chunkfun) (); /* User's fcn to allocate a chunk. */
+ void (*freefun) (); /* User's function to free a chunk. */
+};
+
+#ifdef __STDC__
+
+/* Do the function-declarations after the structs
+ but before defining the macros. */
+
+void obstack_init (struct obstack *obstack);
+
+void * obstack_alloc (struct obstack *obstack, int size);
+
+void * obstack_copy (struct obstack *obstack, void *address, int size);
+void * obstack_copy0 (struct obstack *obstack, void *address, int size);
+
+void obstack_free (struct obstack *obstack, void *block);
+
+void obstack_blank (struct obstack *obstack, int size);
+
+void obstack_grow (struct obstack *obstack, void *data, int size);
+void obstack_grow0 (struct obstack *obstack, void *data, int size);
+
+void obstack_1grow (struct obstack *obstack, int data_char);
+
+void * obstack_finish (struct obstack *obstack);
+
+int obstack_object_size (struct obstack *obstack);
+
+int obstack_room (struct obstack *obstack);
+void obstack_1grow_fast (struct obstack *obstack, int data_char);
+void obstack_blank_fast (struct obstack *obstack, int size);
+
+void * obstack_base (struct obstack *obstack);
+void * obstack_next_free (struct obstack *obstack);
+int obstack_alignment_mask (struct obstack *obstack);
+int obstack_chunk_size (struct obstack *obstack);
+
+#endif /* __STDC__ */
+
+/* Non-ANSI C cannot really support alternative functions for these macros,
+ so we do not declare them. */
+
+/* Pointer to beginning of object being allocated or to be allocated next.
+ Note that this might not be the final address of the object
+ because a new chunk might be needed to hold the final size. */
+
+#define obstack_base(h) ((h)->object_base)
+
+/* Size for allocating ordinary chunks. */
+
+#define obstack_chunk_size(h) ((h)->chunk_size)
+
+/* Pointer to next byte not yet allocated in current chunk. */
+
+#define obstack_next_free(h) ((h)->next_free)
+
+/* Mask specifying low bits that should be clear in address of an object. */
+
+#define obstack_alignment_mask(h) ((h)->alignment_mask)
+
+#define obstack_init(h) \
+ _obstack_begin ((h), 0, 0, obstack_chunk_alloc, obstack_chunk_free)
+
+#define obstack_begin(h, size) \
+ _obstack_begin ((h), (size), 0, obstack_chunk_alloc, obstack_chunk_free)
+
+#define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = achar)
+
+#define obstack_blank_fast(h,n) ((h)->next_free += (n))
+
+#if defined (__GNUC__) && defined (__STDC__)
+
+/* For GNU C, if not -traditional,
+ we can define these macros to compute all args only once
+ without using a global variable.
+ Also, we can avoid using the `temp' slot, to make faster code. */
+
+#define obstack_object_size(OBSTACK) \
+ ({ struct obstack *__o = (OBSTACK); \
+ (unsigned) (__o->next_free - __o->object_base); })
+
+#define obstack_room(OBSTACK) \
+ ({ struct obstack *__o = (OBSTACK); \
+ (unsigned) (__o->chunk_limit - __o->next_free); })
+
+#define obstack_grow(OBSTACK,where,length) \
+({ struct obstack *__o = (OBSTACK); \
+ int __len = (length); \
+ ((__o->next_free + __len > __o->chunk_limit) \
+ ? _obstack_newchunk (__o, __len) : 0); \
+ bcopy (where, __o->next_free, __len); \
+ __o->next_free += __len; \
+ (void) 0; })
+
+#define obstack_grow0(OBSTACK,where,length) \
+({ struct obstack *__o = (OBSTACK); \
+ int __len = (length); \
+ ((__o->next_free + __len + 1 > __o->chunk_limit) \
+ ? _obstack_newchunk (__o, __len + 1) : 0), \
+ bcopy (where, __o->next_free, __len), \
+ __o->next_free += __len, \
+ *(__o->next_free)++ = 0; \
+ (void) 0; })
+
+#define obstack_1grow(OBSTACK,datum) \
+({ struct obstack *__o = (OBSTACK); \
+ ((__o->next_free + 1 > __o->chunk_limit) \
+ ? _obstack_newchunk (__o, 1) : 0), \
+ *(__o->next_free)++ = (datum); \
+ (void) 0; })
+
+#define obstack_blank(OBSTACK,length) \
+({ struct obstack *__o = (OBSTACK); \
+ int __len = (length); \
+ ((__o->next_free + __len > __o->chunk_limit) \
+ ? _obstack_newchunk (__o, __len) : 0); \
+ __o->next_free += __len; \
+ (void) 0; })
+
+#define obstack_alloc(OBSTACK,length) \
+({ struct obstack *__h = (OBSTACK); \
+ obstack_blank (__h, (length)); \
+ obstack_finish (__h); })
+
+#define obstack_copy(OBSTACK,where,length) \
+({ struct obstack *__h = (OBSTACK); \
+ obstack_grow (__h, (where), (length)); \
+ obstack_finish (__h); })
+
+#define obstack_copy0(OBSTACK,where,length) \
+({ struct obstack *__h = (OBSTACK); \
+ obstack_grow0 (__h, (where), (length)); \
+ obstack_finish (__h); })
+
+#define obstack_finish(OBSTACK) \
+({ struct obstack *__o = (OBSTACK); \
+ void *value = (void *) __o->object_base; \
+ __o->next_free \
+ = __INT_TO_PTR ((__PTR_TO_INT (__o->next_free)+__o->alignment_mask)\
+ & ~ (__o->alignment_mask)); \
+ ((__o->next_free - (char *)__o->chunk \
+ > __o->chunk_limit - (char *)__o->chunk) \
+ ? (__o->next_free = __o->chunk_limit) : 0); \
+ __o->object_base = __o->next_free; \
+ value; })
+
+#define obstack_free(OBSTACK, OBJ) \
+({ struct obstack *__o = (OBSTACK); \
+ void *__obj = (OBJ); \
+ if (__obj >= (void *)__o->chunk && __obj < (void *)__o->chunk_limit) \
+ __o->next_free = __o->object_base = __obj; \
+ else (obstack_free) (__o, __obj); })
+
+#else /* not __GNUC__ or not __STDC__ */
+
+/* The non-GNU macros copy the obstack-pointer into this global variable
+ to avoid multiple evaluation. */
+
+extern struct obstack *_obstack;
+
+#define obstack_object_size(h) \
+ (unsigned) (_obstack = (h), (h)->next_free - (h)->object_base)
+
+#define obstack_room(h) \
+ (unsigned) (_obstack = (h), (h)->chunk_limit - (h)->next_free)
+
+#define obstack_grow(h,where,length) \
+( (h)->temp = (length), \
+ (((h)->next_free + (h)->temp > (h)->chunk_limit) \
+ ? _obstack_newchunk ((h), (h)->temp) : 0), \
+ bcopy (where, (h)->next_free, (h)->temp), \
+ (h)->next_free += (h)->temp)
+
+#define obstack_grow0(h,where,length) \
+( (h)->temp = (length), \
+ (((h)->next_free + (h)->temp + 1 > (h)->chunk_limit) \
+ ? _obstack_newchunk ((h), (h)->temp + 1) : 0), \
+ bcopy (where, (h)->next_free, (h)->temp), \
+ (h)->next_free += (h)->temp, \
+ *((h)->next_free)++ = 0)
+
+#define obstack_1grow(h,datum) \
+( (((h)->next_free + 1 > (h)->chunk_limit) \
+ ? _obstack_newchunk ((h), 1) : 0), \
+ *((h)->next_free)++ = (datum))
+
+#define obstack_blank(h,length) \
+( (h)->temp = (length), \
+ (((h)->next_free + (h)->temp > (h)->chunk_limit) \
+ ? _obstack_newchunk ((h), (h)->temp) : 0), \
+ (h)->next_free += (h)->temp)
+
+#define obstack_alloc(h,length) \
+ (obstack_blank ((h), (length)), obstack_finish ((h)))
+
+#define obstack_copy(h,where,length) \
+ (obstack_grow ((h), (where), (length)), obstack_finish ((h)))
+
+#define obstack_copy0(h,where,length) \
+ (obstack_grow0 ((h), (where), (length)), obstack_finish ((h)))
+
+#define obstack_finish(h) \
+( (h)->temp = __PTR_TO_INT ((h)->object_base), \
+ (h)->next_free \
+ = __INT_TO_PTR ((__PTR_TO_INT ((h)->next_free)+(h)->alignment_mask) \
+ & ~ ((h)->alignment_mask)), \
+ (((h)->next_free - (char *)(h)->chunk \
+ > (h)->chunk_limit - (char *)(h)->chunk) \
+ ? ((h)->next_free = (h)->chunk_limit) : 0), \
+ (h)->object_base = (h)->next_free, \
+ __INT_TO_PTR ((h)->temp))
+
+#ifdef __STDC__
+#define obstack_free(h,obj) \
+( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \
+ (((h)->temp >= 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\
+ ? (int) ((h)->next_free = (h)->object_base \
+ = (h)->temp + (char *) (h)->chunk) \
+ : ((obstack_free) ((h), (h)->temp + (char *) (h)->chunk), 0)))
+#else
+#define obstack_free(h,obj) \
+( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \
+ (((h)->temp >= 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\
+ ? (int) ((h)->next_free = (h)->object_base \
+ = (h)->temp + (char *) (h)->chunk) \
+ : (int) _obstack_free ((h), (h)->temp + (char *) (h)->chunk)))
+#endif
+
+#endif /* not __GNUC__ or not __STDC__ */
+
+#endif /* not __OBSTACKS__ */
+
diff --git a/gnu/usr.bin/gdb/printcmd.c b/gnu/usr.bin/gdb/printcmd.c
new file mode 100644
index 0000000..6edd7bd
--- /dev/null
+++ b/gnu/usr.bin/gdb/printcmd.c
@@ -0,0 +1,1867 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)printcmd.c 6.5 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Print values for GNU debugger GDB.
+ Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "frame.h"
+#include "symtab.h"
+#include "value.h"
+#include "expression.h"
+
+struct format_data
+{
+ int count;
+ char format;
+ char size;
+};
+
+/* Last specified output format. */
+
+static char last_format = 'x';
+
+/* Last specified examination size. 'b', 'h', 'w' or `q'. */
+
+static char last_size = 'w';
+
+/* Default address to examine next. */
+
+static CORE_ADDR next_address;
+
+/* Last address examined. */
+
+static CORE_ADDR last_examine_address;
+
+/* Contents of last address examined.
+ This is not valid past the end of the `x' command! */
+
+static value last_examine_value;
+
+/* Number of auto-display expression currently being displayed.
+ So that we can deleted it if we get an error or a signal within it.
+ -1 when not doing one. */
+
+int current_display_number;
+
+static void do_one_display ();
+
+void do_displays ();
+void print_address ();
+void print_floating ();
+void print_scalar_formatted ();
+void print_formatted_address ();
+
+
+/* Decode a format specification. *STRING_PTR should point to it.
+ OFORMAT and OSIZE are used as defaults for the format and size
+ if none are given in the format specification.
+ If OSIZE is zero, then the size field of the returned value
+ should be set only if a size is explicitly specified by the
+ user.
+ The structure returned describes all the data
+ found in the specification. In addition, *STRING_PTR is advanced
+ past the specification and past all whitespace following it. */
+
+struct format_data
+decode_format (string_ptr, oformat, osize)
+ char **string_ptr;
+ char oformat;
+ char osize;
+{
+ struct format_data val;
+ register char *p = *string_ptr;
+
+ val.format = '?';
+ val.size = '?';
+ val.count = 1;
+
+ if (*p >= '0' && *p <= '9')
+ val.count = atoi (p);
+ while (*p >= '0' && *p <= '9') p++;
+
+ /* Now process size or format letters that follow. */
+
+ while (1)
+ {
+ if (*p == 'b' || *p == 'h' || *p == 'w' || *p == 'g')
+ val.size = *p++;
+#ifdef LONG_LONG
+ else if (*p == 'l')
+ {
+ val.size = 'g';
+ p++;
+ }
+#endif
+ else if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z'))
+ val.format = *p++;
+ else
+ break;
+ }
+
+#ifndef LONG_LONG
+ /* Make sure 'g' size is not used on integer types.
+ Well, actually, we can handle hex. */
+ if (val.size == 'g' && val.format != 'f' && val.format != 'x')
+ val.size = 'w';
+#endif
+
+ while (*p == ' ' || *p == '\t') p++;
+ *string_ptr = p;
+
+ /* Set defaults for format and size if not specified. */
+ if (val.format == '?')
+ {
+ if (val.size == '?')
+ {
+ /* Neither has been specified. */
+ val.format = oformat;
+ val.size = osize;
+ }
+ else
+ /* If a size is specified, any format makes a reasonable
+ default except 'i'. */
+ val.format = oformat == 'i' ? 'x' : oformat;
+ }
+ else if (val.size == '?')
+ switch (val.format)
+ {
+ case 'a':
+ case 's':
+ case 'A':
+ /* Addresses must be words. */
+ val.size = osize ? 'w' : osize;
+ break;
+ case 'f':
+ /* Floating point has to be word or giantword. */
+ if (osize == 'w' || osize == 'g')
+ val.size = osize;
+ else
+ /* Default it to giantword if the last used size is not
+ appropriate. */
+ val.size = osize ? 'g' : osize;
+ break;
+ case 'c':
+ /* Characters default to one byte. */
+ val.size = osize ? 'b' : osize;
+ break;
+ default:
+ /* The default is the size most recently specified. */
+ val.size = osize;
+ }
+
+ return val;
+}
+
+/* Print value VAL on stdout according to FORMAT, a letter or 0.
+ Do not end with a newline.
+ 0 means print VAL according to its own type.
+ SIZE is the letter for the size of datum being printed.
+ This is used to pad hex numbers so they line up. */
+
+static void
+print_formatted (val, format, size)
+ register value val;
+ register char format;
+ char size;
+{
+ int len = TYPE_LENGTH (VALUE_TYPE (val));
+
+ if (VALUE_LVAL (val) == lval_memory)
+ next_address = VALUE_ADDRESS (val) + len;
+
+ switch (format)
+ {
+ case 's':
+ next_address = VALUE_ADDRESS (val)
+ + value_print (value_addr (val), stdout, 0, Val_pretty_default);
+ break;
+
+ case 'i':
+ next_address = VALUE_ADDRESS (val)
+ + print_insn (VALUE_ADDRESS (val), stdout);
+ break;
+
+ default:
+ if (format == 0
+ || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_ARRAY
+ || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_STRUCT
+ || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_UNION
+ || VALUE_REPEATED (val))
+ value_print (val, stdout, format, Val_pretty_default);
+ else
+ print_scalar_formatted (VALUE_CONTENTS (val), VALUE_TYPE (val),
+ format, size, stdout);
+ }
+}
+
+/* Print a scalar of data of type TYPE, pointed to in GDB by VALADDR,
+ according to letters FORMAT and SIZE on STREAM.
+ FORMAT may not be zero. Formats s and i are not supported at this level.
+
+ This is how the elements of an array or structure are printed
+ with a format. */
+
+void
+print_scalar_formatted (valaddr, type, format, size, stream)
+ char *valaddr;
+ struct type *type;
+ char format;
+ int size;
+ FILE *stream;
+{
+ LONGEST val_long;
+ int len = TYPE_LENGTH (type);
+
+ if (size == 'g' && sizeof (LONGEST) < 8
+ && format == 'x')
+ {
+ /* ok, we're going to have to get fancy here. Assumption: a
+ long is four bytes. */
+ unsigned long v1, v2, tmp;
+
+ v1 = unpack_long (builtin_type_long, valaddr);
+ v2 = unpack_long (builtin_type_long, valaddr + 4);
+
+#ifdef BYTES_BIG_ENDIAN
+#else
+ /* Little endian -- swap the two for printing */
+ tmp = v1;
+ v1 = v2;
+ v2 = tmp;
+#endif
+
+ switch (format)
+ {
+ case 'x':
+ fprintf_filtered (stream, "0x%08x%08x", v1, v2);
+ break;
+ default:
+ error ("Output size \"g\" unimplemented for format \"%c\".",
+ format);
+ }
+ return;
+ }
+
+ val_long = unpack_long (type, valaddr);
+
+ /* If value is unsigned, truncate it in case negative. */
+ if (format != 'd')
+ {
+ if (len == sizeof (char))
+ val_long &= (1 << 8 * sizeof(char)) - 1;
+ else if (len == sizeof (short))
+ val_long &= (1 << 8 * sizeof(short)) - 1;
+ else if (len == sizeof (long))
+ val_long &= (unsigned long) - 1;
+ }
+
+ switch (format)
+ {
+ case 'x':
+#ifdef LONG_LONG
+ if (!size)
+ size = (len < sizeof (long long) ? 'w' : 'g');
+ switch (size)
+ {
+ case 'b':
+ fprintf_filtered (stream, "0x%02llx", val_long);
+ break;
+ case 'h':
+ fprintf_filtered (stream, "0x%04llx", val_long);
+ break;
+ case 0: /* no size specified, like in print */
+ case 'w':
+ fprintf_filtered (stream, "0x%08llx", val_long);
+ break;
+ case 'g':
+ fprintf_filtered (stream, "0x%016llx", val_long);
+ break;
+ default:
+ error ("Undefined output size \"%c\".", size);
+ }
+#else
+ switch (size)
+ {
+ case 'b':
+ fprintf_filtered (stream, "0x%02x", val_long);
+ break;
+ case 'h':
+ fprintf_filtered (stream, "0x%04x", val_long);
+ break;
+ case 0: /* no size specified, like in print */
+ case 'w':
+ fprintf_filtered (stream, "0x%08x", val_long);
+ break;
+ case 'g':
+ fprintf_filtered (stream, "0x%o16x", val_long);
+ break;
+ default:
+ error ("Undefined output size \"%c\".", size);
+ }
+#endif /* not LONG_LONG */
+ break;
+
+ case 'd':
+#ifdef LONG_LONG
+ fprintf_filtered (stream, "%lld", val_long);
+#else
+ fprintf_filtered (stream, "%d", val_long);
+#endif
+ break;
+
+ case 'u':
+#ifdef LONG_LONG
+ fprintf_filtered (stream, "%llu", val_long);
+#else
+ fprintf_filtered (stream, "%u", val_long);
+#endif
+ break;
+
+ case 'o':
+ if (val_long)
+#ifdef LONG_LONG
+ fprintf_filtered (stream, "0%llo", val_long);
+#else
+ fprintf_filtered (stream, "0%o", val_long);
+#endif
+ else
+ fprintf_filtered (stream, "0");
+ break;
+
+ case 'a':
+ print_address ((CORE_ADDR) val_long, stream);
+ break;
+
+ case 'A':
+ print_formatted_address ((CORE_ADDR) val_long, stream);
+ break;
+
+ case 'c':
+ value_print (value_from_long (builtin_type_char, val_long), stream, 0,
+ Val_pretty_default);
+ break;
+
+ case 'f':
+ if (len == sizeof (float))
+ type = builtin_type_float;
+ else if (len == sizeof (double))
+ type = builtin_type_double;
+ print_floating(valaddr, type, stream);
+ break;
+
+ case 0:
+ abort ();
+
+ default:
+ error ("Undefined output format \"%c\".", format);
+ }
+}
+
+/* Print a floating point value of type TYPE, pointed to in GDB by VALADDR,
+ on STREAM. */
+
+void
+print_floating(valaddr, type, stream)
+ char *valaddr;
+ struct type *type;
+ FILE *stream;
+{
+ double doub;
+ int inv;
+ int len = TYPE_LENGTH (type);
+
+ doub = unpack_double (type, valaddr, &inv);
+ if (inv)
+ fprintf_filtered (stream, "Invalid float value");
+ else if (doub != doub)
+ {
+ /* Surely it is an IEEE floating point NaN. */
+
+ long low, high, *arg = (long *)valaddr; /* ASSUMED 32 BITS */
+ int nonneg;
+
+ if (len <= sizeof(float))
+ {
+ /* It's single precision. */
+ low = *arg;
+ nonneg = low >= 0;
+ low &= 0x7fffff;
+ high = 0;
+ }
+ else
+ {
+ /* It's double precision.
+ Get the high and low words of the fraction.
+ Distinguish big and little-endian machines. */
+#ifdef WORDS_BIG_ENDIAN
+ low = arg[1], high = arg[0];
+#else
+ low = arg[0], high = arg[1];
+#endif
+ nonneg = high >= 0;
+ high &= 0xfffff;
+ }
+ if (high)
+ fprintf_filtered (stream, "-NaN(0x%lx%.8lx)" + nonneg, high, low);
+ else
+ fprintf_filtered (stream, "-NaN(0x%lx)" + nonneg, low);
+ }
+ else
+ fprintf_filtered (stream, len <= sizeof(float) ? "%.6g" : "%.17g", doub);
+}
+
+/* Specify default address for `x' command.
+ `info lines' uses this. */
+
+void
+set_next_address (addr)
+ CORE_ADDR addr;
+{
+ next_address = addr;
+
+ /* Make address available to the user as $_. */
+ set_internalvar (lookup_internalvar ("_"),
+ value_from_long (builtin_type_int, (LONGEST) addr));
+}
+
+/* Optionally print address ADDR symbolically as <SYMBOL+OFFSET> on STREAM. */
+
+void
+print_address_symbolic (addr, stream)
+ CORE_ADDR addr;
+ FILE *stream;
+{
+ register char *format;
+ int name_location;
+ register int i = find_pc_misc_function (addr);
+
+ /* If nothing comes out, don't print anything symbolic. */
+ if (i < 0) return;
+ name_location = misc_function_vector[i].address;
+
+ if (addr - name_location)
+ format = " <%s+%d>";
+ else
+ format = " <%s>";
+
+ fprintf_filtered (stream, format,
+ misc_function_vector[i].name, addr - name_location);
+}
+
+/* Print address ADDR symbolically on STREAM.
+ First print it as a number. Then perhaps print
+ <SYMBOL + OFFSET> after the number. */
+
+void
+print_address (addr, stream)
+ CORE_ADDR addr;
+ FILE *stream;
+{
+ fprintf_filtered (stream, "0x%x", addr);
+ print_address_symbolic (addr, stream);
+}
+
+/* Like print_address but opnly prints symbolically. */
+
+void
+print_formatted_address (addr, stream)
+ CORE_ADDR addr;
+ FILE *stream;
+{
+ register int i = 0;
+ register char *format;
+ register struct symbol *fs;
+ char *name;
+ int name_location;
+
+ i = find_pc_partial_function (addr, &name, &name_location);
+
+ /* If nothing comes out, don't print anything symbolic. */
+
+ if (i == 0)
+ fprintf_filtered (stream, "0x%x", addr);
+ else if (addr - name_location)
+ fprintf_filtered (stream, "%s+%d", name, addr - name_location);
+ else
+ fprintf_filtered (stream, "%s", name);
+}
+
+/* Examine data at address ADDR in format FMT.
+ Fetch it from memory and print on stdout. */
+
+static void
+do_examine (fmt, addr)
+ struct format_data fmt;
+ CORE_ADDR addr;
+{
+ register char format = 0;
+ register char size;
+ register int count = 1;
+ struct type *val_type;
+ register int i;
+ register int maxelts;
+
+ format = fmt.format;
+ size = fmt.size;
+ count = fmt.count;
+ next_address = addr;
+
+ /* String or instruction format implies fetch single bytes
+ regardless of the specified size. */
+ if (format == 's' || format == 'i')
+ size = 'b';
+
+ if (size == 'b')
+ val_type = builtin_type_char;
+ else if (size == 'h')
+ val_type = builtin_type_short;
+ else if (size == 'w')
+ val_type = builtin_type_long;
+ else if (size == 'g')
+#ifndef LONG_LONG
+ val_type = builtin_type_double;
+#else
+ val_type = builtin_type_long_long;
+#endif
+
+ maxelts = 8;
+ if (size == 'w')
+ maxelts = 4;
+ if (size == 'g')
+ maxelts = 2;
+ if (format == 's' || format == 'i')
+ maxelts = 1;
+
+ /* Print as many objects as specified in COUNT, at most maxelts per line,
+ with the address of the next one at the start of each line. */
+
+ while (count > 0)
+ {
+ print_address (next_address, stdout);
+ printf_filtered (":");
+ for (i = maxelts;
+ i > 0 && count > 0;
+ i--, count--)
+ {
+ printf_filtered ("\t");
+ /* Note that print_formatted sets next_address for the next
+ object. */
+ last_examine_address = next_address;
+ last_examine_value = value_at (val_type, next_address);
+ print_formatted (last_examine_value, format, size);
+ }
+ printf_filtered ("\n");
+ fflush (stdout);
+ }
+}
+
+static void
+validate_format (fmt, cmdname)
+ struct format_data fmt;
+ char *cmdname;
+{
+ if (fmt.size != 0)
+ error ("Size letters are meaningless in \"%s\" command.", cmdname);
+ if (fmt.count != 1)
+ error ("Item count other than 1 is meaningless in \"%s\" command.",
+ cmdname);
+ if (fmt.format == 'i' || fmt.format == 's')
+ error ("Format letter \"%c\" is meaningless in \"%s\" command.",
+ fmt.format, cmdname);
+}
+
+static void
+print_command (exp)
+ char *exp;
+{
+ struct expression *expr;
+ register struct cleanup *old_chain = 0;
+ register char format = 0;
+ register value val;
+ struct format_data fmt;
+ int histindex;
+ int cleanup = 0;
+
+ if (exp && *exp == '/')
+ {
+ exp++;
+ fmt = decode_format (&exp, last_format, 0);
+ validate_format (fmt, "print");
+ last_format = format = fmt.format;
+ }
+
+ if (exp && *exp)
+ {
+ expr = parse_c_expression (exp);
+ old_chain = make_cleanup (free_current_contents, &expr);
+ cleanup = 1;
+ val = evaluate_expression (expr);
+ }
+ else
+ val = access_value_history (0);
+
+ histindex = record_latest_value (val);
+ if (histindex >= 0) printf_filtered ("$%d = ", histindex);
+
+ print_formatted (val, format, fmt.size);
+ printf_filtered ("\n");
+
+ if (cleanup)
+ do_cleanups (old_chain);
+}
+
+static void
+output_command (exp)
+ char *exp;
+{
+ struct expression *expr;
+ register struct cleanup *old_chain;
+ register char format = 0;
+ register value val;
+ struct format_data fmt;
+
+ if (exp && *exp == '/')
+ {
+ exp++;
+ fmt = decode_format (&exp, 0, 0);
+ validate_format (fmt, "print");
+ format = fmt.format;
+ }
+
+ expr = parse_c_expression (exp);
+ old_chain = make_cleanup (free_current_contents, &expr);
+
+ val = evaluate_expression (expr);
+
+ print_formatted (val, format, fmt.size);
+
+ do_cleanups (old_chain);
+}
+
+static void
+set_command (exp)
+ char *exp;
+{
+ struct expression *expr = parse_c_expression (exp);
+ register struct cleanup *old_chain
+ = make_cleanup (free_current_contents, &expr);
+ evaluate_expression (expr);
+ do_cleanups (old_chain);
+}
+
+static void
+address_info (exp)
+ char *exp;
+{
+ register struct symbol *sym;
+ register CORE_ADDR val;
+ int is_a_field_of_this; /* C++: lookup_symbol sets this to nonzero
+ if exp is a field of `this'. */
+
+ if (exp == 0)
+ error ("Argument required.");
+
+ sym = lookup_symbol (exp, get_selected_block (), VAR_NAMESPACE,
+ &is_a_field_of_this);
+ if (sym == 0)
+ {
+ register int i;
+
+ if (is_a_field_of_this)
+ {
+ printf ("Symbol \"%s\" is a field of the local class variable `this'\n", exp);
+ return;
+ }
+
+ for (i = 0; i < misc_function_count; i++)
+ if (!strcmp (misc_function_vector[i].name, exp))
+ break;
+
+ if (i < misc_function_count)
+ printf ("Symbol \"%s\" is at 0x%x in a file compiled without -g.\n",
+ exp, misc_function_vector[i].address);
+ else
+ error ("No symbol \"%s\" in current context.", exp);
+ return;
+ }
+
+ printf ("Symbol \"%s\" is ", SYMBOL_NAME (sym));
+ val = SYMBOL_VALUE (sym);
+
+ switch (SYMBOL_CLASS (sym))
+ {
+ case LOC_CONST:
+ case LOC_CONST_BYTES:
+ printf ("constant");
+ break;
+
+ case LOC_LABEL:
+ printf ("a label at address 0x%x", val);
+ break;
+
+ case LOC_REGISTER:
+ printf ("a variable in register %s", reg_names[val]);
+ break;
+
+ case LOC_STATIC:
+ printf ("static at address 0x%x", val);
+ break;
+
+ case LOC_REGPARM:
+ printf ("an argument in register %s", reg_names[val]);
+ break;
+
+ case LOC_ARG:
+ printf ("an argument at offset %d", val);
+ break;
+
+ case LOC_LOCAL:
+ printf ("a local variable at frame offset %d", val);
+ break;
+
+ case LOC_REF_ARG:
+ printf ("a reference argument at offset %d", val);
+ break;
+
+ case LOC_TYPEDEF:
+ printf ("a typedef");
+ break;
+
+ case LOC_BLOCK:
+ printf ("a function at address 0x%x",
+ BLOCK_START (SYMBOL_BLOCK_VALUE (sym)));
+ break;
+ }
+ printf (".\n");
+}
+
+static void
+x_command (exp, from_tty)
+ char *exp;
+ int from_tty;
+{
+ struct expression *expr;
+ struct format_data fmt;
+ struct cleanup *old_chain;
+ struct value *val;
+
+ fmt.format = last_format;
+ fmt.size = last_size;
+ fmt.count = 1;
+
+ if (exp && *exp == '/')
+ {
+ exp++;
+ fmt = decode_format (&exp, last_format, last_size);
+ last_size = fmt.size;
+ last_format = fmt.format;
+ }
+
+ /* If we have an expression, evaluate it and use it as the address. */
+
+ if (exp != 0 && *exp != 0)
+ {
+ expr = parse_c_expression (exp);
+ /* Cause expression not to be there any more
+ if this command is repeated with Newline.
+ But don't clobber a user-defined command's definition. */
+ if (from_tty)
+ *exp = 0;
+ old_chain = make_cleanup (free_current_contents, &expr);
+ val = evaluate_expression (expr);
+ /* In rvalue contexts, such as this, functions are coerced into
+ pointers to functions. This makes "x/i main" work. */
+ if (/* last_format == 'i'
+ && */ TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_FUNC
+ && VALUE_LVAL (val) == lval_memory)
+ next_address = VALUE_ADDRESS (val);
+ else
+ next_address = (CORE_ADDR) value_as_long (val);
+ do_cleanups (old_chain);
+ }
+
+ do_examine (fmt, next_address);
+
+ /* Set a couple of internal variables if appropriate. */
+ if (last_examine_value)
+ {
+ /* Make last address examined available to the user as $_. */
+ set_internalvar (lookup_internalvar ("_"),
+ value_from_long (builtin_type_int,
+ (LONGEST) last_examine_address));
+
+ /* Make contents of last address examined available to the user as $__.*/
+ set_internalvar (lookup_internalvar ("__"), last_examine_value);
+ }
+}
+
+/* Commands for printing types of things. */
+
+static void
+whatis_command (exp)
+ char *exp;
+{
+ struct expression *expr;
+ register value val;
+ register struct cleanup *old_chain;
+
+ if (exp)
+ {
+ expr = parse_c_expression (exp);
+ old_chain = make_cleanup (free_current_contents, &expr);
+ val = evaluate_type (expr);
+ }
+ else
+ val = access_value_history (0);
+
+ printf_filtered ("type = ");
+ /* Most of the time users do not want to see all the fields
+ in a structure. If they do they can use the "ptype" command.
+ Hence the "-1" below. */
+ type_print (VALUE_TYPE (val), "", stdout, -1);
+ printf_filtered ("\n");
+
+ if (exp)
+ do_cleanups (old_chain);
+}
+
+static void
+ptype_command (typename)
+ char *typename;
+{
+ register char *p = typename;
+ register int len;
+ extern struct block *get_current_block ();
+ register struct block *b
+ = (have_inferior_p () || have_core_file_p ()) ? get_current_block () : 0;
+ register struct type *type;
+
+ if (typename == 0)
+ error_no_arg ("type name");
+
+ while (*p && *p != ' ' && *p != '\t') p++;
+ len = p - typename;
+ while (*p == ' ' || *p == '\t') p++;
+
+ if (len == 6 && !strncmp (typename, "struct", 6))
+ type = lookup_struct (p, b);
+ else if (len == 5 && !strncmp (typename, "union", 5))
+ type = lookup_union (p, b);
+ else if (len == 4 && !strncmp (typename, "enum", 4))
+ type = lookup_enum (p, b);
+ else
+ {
+ type = lookup_typename (typename, b, 1);
+ if (type == 0)
+ {
+ register struct symbol *sym
+ = lookup_symbol (typename, b, STRUCT_NAMESPACE, 0);
+ if (sym == 0)
+ error ("No type named %s.", typename);
+ printf_filtered ("No type named %s, but there is a ",
+ typename);
+ switch (TYPE_CODE (SYMBOL_TYPE (sym)))
+ {
+ case TYPE_CODE_STRUCT:
+ printf_filtered ("struct");
+ break;
+
+ case TYPE_CODE_UNION:
+ printf_filtered ("union");
+ break;
+
+ case TYPE_CODE_ENUM:
+ printf_filtered ("enum");
+ }
+ printf_filtered (" %s. Type \"help ptype\".\n", typename);
+ type = SYMBOL_TYPE (sym);
+ }
+ }
+
+ type_print (type, "", stdout, 1);
+ printf_filtered ("\n");
+}
+
+enum display_status {disabled, enabled};
+
+struct display
+{
+ /* Chain link to next auto-display item. */
+ struct display *next;
+ /* Expression to be evaluated and displayed. */
+ struct expression *exp;
+ /* Item number of this auto-display item. */
+ int number;
+ /* Display format specified. */
+ struct format_data format;
+ /* Innermost block required by this expression when evaluated */
+ struct block *block;
+ /* Status of this display (enabled or disabled) */
+ enum display_status status;
+};
+
+/* Chain of expressions whose values should be displayed
+ automatically each time the program stops. */
+
+static struct display *display_chain;
+
+static int display_number;
+
+/* Add an expression to the auto-display chain.
+ Specify the expression. */
+
+static void
+display_command (exp, from_tty)
+ char *exp;
+ int from_tty;
+{
+ struct format_data fmt;
+ register struct expression *expr;
+ register struct display *new;
+ extern struct block *innermost_block;
+
+ if (exp == 0)
+ {
+ do_displays ();
+ return;
+ }
+
+ if (*exp == '/')
+ {
+ exp++;
+ fmt = decode_format (&exp, 0, 0);
+ if (fmt.size && fmt.format == 0)
+ fmt.format = 'x';
+ if (fmt.format == 'i' || fmt.format == 's')
+ fmt.size = 'b';
+ }
+ else
+ {
+ fmt.format = 0;
+ fmt.size = 0;
+ fmt.count = 0;
+ }
+
+ innermost_block = 0;
+ expr = parse_c_expression (exp);
+
+ new = (struct display *) xmalloc (sizeof (struct display));
+
+ new->exp = expr;
+ new->block = innermost_block;
+ new->next = display_chain;
+ new->number = ++display_number;
+ new->format = fmt;
+ new->status = enabled;
+ display_chain = new;
+
+ if (from_tty && have_inferior_p ())
+ do_one_display (new);
+
+ dont_repeat ();
+}
+
+static void
+free_display (d)
+ struct display *d;
+{
+ free (d->exp);
+ free (d);
+}
+
+/* Clear out the display_chain.
+ Done when new symtabs are loaded, since this invalidates
+ the types stored in many expressions. */
+
+void
+clear_displays ()
+{
+ register struct display *d;
+
+ while (d = display_chain)
+ {
+ free (d->exp);
+ display_chain = d->next;
+ free (d);
+ }
+}
+
+/* Delete the auto-display number NUM. */
+
+void
+delete_display (num)
+ int num;
+{
+ register struct display *d1, *d;
+
+ if (!display_chain)
+ error ("No display number %d.", num);
+
+ if (display_chain->number == num)
+ {
+ d1 = display_chain;
+ display_chain = d1->next;
+ free_display (d1);
+ }
+ else
+ for (d = display_chain; ; d = d->next)
+ {
+ if (d->next == 0)
+ error ("No display number %d.", num);
+ if (d->next->number == num)
+ {
+ d1 = d->next;
+ d->next = d1->next;
+ free_display (d1);
+ break;
+ }
+ }
+}
+
+/* Delete some values from the auto-display chain.
+ Specify the element numbers. */
+
+static void
+undisplay_command (args)
+ char *args;
+{
+ register char *p = args;
+ register char *p1;
+ register int num;
+ register struct display *d, *d1;
+
+ if (args == 0)
+ {
+ if (query ("Delete all auto-display expressions? "))
+ clear_displays ();
+ dont_repeat ();
+ return;
+ }
+
+ while (*p)
+ {
+ p1 = p;
+ while (*p1 >= '0' && *p1 <= '9') p1++;
+ if (*p1 && *p1 != ' ' && *p1 != '\t')
+ error ("Arguments must be display numbers.");
+
+ num = atoi (p);
+
+ delete_display (num);
+
+ p = p1;
+ while (*p == ' ' || *p == '\t') p++;
+ }
+ dont_repeat ();
+}
+
+/* Display a single auto-display.
+ Do nothing if the display cannot be printed in the current context,
+ or if the display is disabled. */
+
+static void
+do_one_display (d)
+ struct display *d;
+{
+ int within_current_scope;
+
+ if (d->status == disabled)
+ return;
+
+ if (d->block)
+ within_current_scope = contained_in (get_selected_block (), d->block);
+ else
+ within_current_scope = 1;
+ if (!within_current_scope)
+ return;
+
+ current_display_number = d->number;
+
+ printf_filtered ("%d: ", d->number);
+ if (d->format.size)
+ {
+ printf_filtered ("x/");
+ if (d->format.count != 1)
+ printf_filtered ("%d", d->format.count);
+ printf_filtered ("%c", d->format.format);
+ if (d->format.format != 'i' && d->format.format != 's')
+ printf_filtered ("%c", d->format.size);
+ printf_filtered (" ");
+ print_expression (d->exp, stdout);
+ if (d->format.count != 1)
+ printf_filtered ("\n");
+ else
+ printf_filtered (" ");
+ do_examine (d->format,
+ (CORE_ADDR) value_as_long (evaluate_expression (d->exp)));
+
+ }
+ else
+ {
+ if (d->format.format)
+ printf_filtered ("/%c ", d->format.format);
+ print_expression (d->exp, stdout);
+ printf_filtered (" = ");
+ print_formatted (evaluate_expression (d->exp),
+ d->format.format, d->format.size);
+ printf_filtered ("\n");
+ }
+
+ fflush (stdout);
+ current_display_number = -1;
+}
+
+/* Display all of the values on the auto-display chain which can be
+ evaluated in the current scope. */
+
+void
+do_displays ()
+{
+ register struct display *d;
+
+ for (d = display_chain; d; d = d->next)
+ do_one_display (d);
+}
+
+/* Delete the auto-display which we were in the process of displaying.
+ This is done when there is an error or a signal. */
+
+void
+disable_display (num)
+ int num;
+{
+ register struct display *d;
+
+ for (d = display_chain; d; d = d->next)
+ if (d->number == num)
+ {
+ d->status = disabled;
+ return;
+ }
+ printf ("No display number %d.\n", num);
+}
+
+void
+disable_current_display ()
+{
+ if (current_display_number >= 0)
+ {
+ disable_display (current_display_number);
+ fprintf (stderr, "Disabling display %d to avoid infinite recursion.\n",
+ current_display_number);
+ }
+ current_display_number = -1;
+}
+
+static void
+display_info ()
+{
+ register struct display *d;
+
+ if (!display_chain)
+ printf ("There are no auto-display expressions now.\n");
+ else
+ printf_filtered ("Auto-display expressions now in effect:\n\
+Num Enb Expression\n");
+
+ for (d = display_chain; d; d = d->next)
+ {
+ printf_filtered ("%d: %c ", d->number, "ny"[(int)d->status]);
+ if (d->format.size)
+ printf_filtered ("/%d%c%c ", d->format.count, d->format.size,
+ d->format.format);
+ else if (d->format.format)
+ printf_filtered ("/%c ", d->format.format);
+ print_expression (d->exp, stdout);
+ if (d->block && !contained_in (get_selected_block (), d->block))
+ printf_filtered (" (cannot be evaluated in the current context)");
+ printf_filtered ("\n");
+ fflush (stdout);
+ }
+}
+
+void
+enable_display (args)
+ char *args;
+{
+ register char *p = args;
+ register char *p1;
+ register int num;
+ register struct display *d;
+
+ if (p == 0)
+ {
+ for (d = display_chain; d; d = d->next)
+ d->status = enabled;
+ }
+ else
+ while (*p)
+ {
+ p1 = p;
+ while (*p1 >= '0' && *p1 <= '9')
+ p1++;
+ if (*p1 && *p1 != ' ' && *p1 != '\t')
+ error ("Arguments must be display numbers.");
+
+ num = atoi (p);
+
+ for (d = display_chain; d; d = d->next)
+ if (d->number == num)
+ {
+ d->status = enabled;
+ goto win;
+ }
+ printf ("No display number %d.\n", num);
+ win:
+ p = p1;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ }
+}
+
+void
+disable_display_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ register char *p = args;
+ register char *p1;
+ register int num;
+ register struct display *d;
+
+ if (p == 0)
+ {
+ for (d = display_chain; d; d = d->next)
+ d->status = disabled;
+ }
+ else
+ while (*p)
+ {
+ p1 = p;
+ while (*p1 >= '0' && *p1 <= '9')
+ p1++;
+ if (*p1 && *p1 != ' ' && *p1 != '\t')
+ error ("Arguments must be display numbers.");
+
+ num = atoi (p);
+
+ disable_display (atoi (p));
+
+ p = p1;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ }
+}
+
+
+/* Print the value in stack frame FRAME of a variable
+ specified by a struct symbol. */
+
+void
+print_variable_value (var, frame, stream)
+ struct symbol *var;
+ FRAME frame;
+ FILE *stream;
+{
+ value val = read_var_value (var, frame);
+ value_print (val, stream, 0, Val_pretty_default);
+}
+
+static int
+compare_ints (i, j)
+ int *i, *j;
+{
+ return *i - *j;
+}
+
+/* Print the arguments of a stack frame, given the function FUNC
+ running in that frame (as a symbol), the info on the frame,
+ and the number of args according to the stack frame (or -1 if unknown). */
+
+static void print_frame_nameless_args ();
+
+void
+print_frame_args (func, fi, num, stream)
+ struct symbol *func;
+ struct frame_info *fi;
+ int num;
+ FILE *stream;
+{
+ struct block *b;
+ int nsyms = 0;
+ int first = 1;
+ register int i;
+ register int last_regparm = 0;
+ register struct symbol *lastsym, *sym, *nextsym;
+ register value val;
+ /* Offset of stack argument that is at the highest offset.
+ -1 if we haven't come to a stack argument yet. */
+ CORE_ADDR highest_offset = (CORE_ADDR) -1;
+ register CORE_ADDR addr = FRAME_ARGS_ADDRESS (fi);
+
+ if (func)
+ {
+ b = SYMBOL_BLOCK_VALUE (func);
+ nsyms = BLOCK_NSYMS (b);
+ }
+
+ for (i = 0; i < nsyms; i++)
+ {
+ QUIT;
+ sym = BLOCK_SYM (b, i);
+
+ if (SYMBOL_CLASS (sym) != LOC_REGPARM
+ && SYMBOL_CLASS (sym) != LOC_ARG
+ && SYMBOL_CLASS (sym) != LOC_REF_ARG)
+ continue;
+
+ /* Print the next arg. */
+ if (SYMBOL_CLASS (sym) == LOC_REGPARM)
+ val = value_from_register (SYMBOL_TYPE (sym),
+ SYMBOL_VALUE (sym),
+ FRAME_INFO_ID (fi));
+ else
+ {
+ int current_offset = SYMBOL_VALUE (sym);
+ int arg_size = TYPE_LENGTH (SYMBOL_TYPE (sym));
+
+ if (SYMBOL_CLASS (sym) == LOC_REF_ARG)
+ val = value_at (SYMBOL_TYPE (sym),
+ read_memory_integer (addr + current_offset,
+ sizeof (CORE_ADDR)));
+ else
+ val = value_at (SYMBOL_TYPE (sym), addr + current_offset);
+
+ /* Round up address of next arg to multiple of size of int. */
+ current_offset
+ = (((current_offset + sizeof (int) - 1) / sizeof (int))
+ * sizeof (int));
+
+ /* If this is the highest offset seen yet, set highest_offset. */
+ if (highest_offset == (CORE_ADDR)-1
+ || ((current_offset
+ + (arg_size - sizeof (int) + 3) / (sizeof (int)))
+ > highest_offset))
+ highest_offset = current_offset;
+ }
+
+ if (! first)
+ fprintf_filtered (stream, ", ");
+ fputs_filtered (SYMBOL_NAME (sym), stream);
+ fputs_filtered ("=", stream);
+
+/* Nonzero if a LOC_ARG which is a struct is useless. */
+#if !defined (STRUCT_ARG_SYM_GARBAGE)
+#define STRUCT_ARG_SYM_GARBAGE(gcc_p) 0
+#endif
+
+ if (STRUCT_ARG_SYM_GARBAGE (b->gcc_compile_flag)
+ && TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_STRUCT
+ && SYMBOL_CLASS (sym) == LOC_ARG)
+ {
+ /* Try looking up that name. SunOS4 puts out a usable
+ symbol as a local variable (in addition to the one
+ for the arg). */
+ struct symbol *sym2 =
+ lookup_symbol (SYMBOL_NAME (sym), b, VAR_NAMESPACE, 0);
+
+ if (sym2 != NULL)
+ val = value_of_variable (sym2);
+ else
+ {
+ fputs_filtered ("?", stream);
+ first = 0;
+ continue;
+ }
+ }
+
+ value_print (val, stream, 0, Val_no_prettyprint);
+ first = 0;
+ }
+
+ /* Don't print nameless args in situations where we don't know
+ enough about the stack to find them. */
+ if (num != -1)
+ {
+ if (highest_offset != (CORE_ADDR) -1
+ && num * sizeof (int) + FRAME_ARGS_SKIP > highest_offset)
+ print_frame_nameless_args (fi, addr,
+ highest_offset + sizeof (int),
+ num * sizeof (int) + FRAME_ARGS_SKIP,
+ stream);
+ else
+ print_frame_nameless_args (fi, addr, FRAME_ARGS_SKIP,
+ num * sizeof (int) + FRAME_ARGS_SKIP,
+ stream);
+ }
+}
+
+static void
+print_frame_nameless_args (fi, argsaddr, start, end, stream)
+ struct frame_info *fi;
+ CORE_ADDR argsaddr;
+ int start;
+ int end;
+ FILE *stream;
+{
+ extern void (*default_scalar_print)();
+ LONGEST v;
+ int p = start;
+ char *s = "";
+
+ for (p = start; p < end; p += sizeof(int)) {
+ QUIT;
+#if defined(NAMELESS_ARG)
+ v = NAMELESS_ARG(fi, (p - start) / sizeof(int));
+#else
+ v = read_memory_integer (argsaddr + p, sizeof (int));
+#endif
+ fprintf_filtered (stream, s);
+ s = ", ";
+ (*default_scalar_print) (stream, builtin_type_int, v);
+ }
+}
+
+static void
+printf_command (arg)
+ char *arg;
+{
+ register char *f;
+ register char *s = arg;
+ char *string;
+ value *val_args;
+ int nargs = 0;
+ int allocated_args = 20;
+ char *arg_bytes;
+
+ val_args = (value *) xmalloc (allocated_args * sizeof (value));
+
+ if (s == 0)
+ error_no_arg ("format-control string and values to print");
+
+ /* Skip white space before format string */
+ while (*s == ' ' || *s == '\t') s++;
+
+ /* A format string should follow, enveloped in double quotes */
+ if (*s++ != '"')
+ error ("Bad format string, missing '\"'.");
+
+ /* Parse the format-control string and copy it into the string STRING,
+ processing some kinds of escape sequence. */
+
+ f = string = (char *) alloca (strlen (s) + 1);
+ while (*s != '"')
+ {
+ int c = *s++;
+ switch (c)
+ {
+ case '\0':
+ error ("Bad format string, non-terminated '\"'.");
+ /* doesn't return */
+
+ case '\\':
+ switch (c = *s++)
+ {
+ case '\\':
+ *f++ = '\\';
+ break;
+ case 'n':
+ *f++ = '\n';
+ break;
+ case 't':
+ *f++ = '\t';
+ break;
+ case 'r':
+ *f++ = '\r';
+ break;
+ case '"':
+ *f++ = '"';
+ break;
+ default:
+ /* ??? TODO: handle other escape sequences */
+ error ("Unrecognized \\ escape character in format string.");
+ }
+ break;
+
+ default:
+ *f++ = c;
+ }
+ }
+
+ /* Skip over " and following space and comma. */
+ s++;
+ *f++ = '\0';
+ while (*s == ' ' || *s == '\t') s++;
+
+ if (*s != ',' && *s != 0)
+ error ("Invalid argument syntax");
+
+ if (*s == ',') s++;
+ while (*s == ' ' || *s == '\t') s++;
+
+ {
+ /* Now scan the string for %-specs and see what kinds of args they want.
+ argclass[I] classifies the %-specs so we can give vprintf something
+ of the right size. */
+
+ enum argclass {int_arg, string_arg, double_arg, long_long_arg};
+ enum argclass *argclass;
+ int nargs_wanted;
+ int argindex;
+ int lcount;
+ int i;
+
+ argclass = (enum argclass *) alloca (strlen (s) * sizeof *argclass);
+ nargs_wanted = 0;
+ f = string;
+ while (*f)
+ if (*f++ == '%')
+ {
+ lcount = 0;
+ while (index ("0123456789.hlL-+ #", *f))
+ {
+ if (*f == 'l' || *f == 'L')
+ lcount++;
+ f++;
+ }
+ if (*f == 's')
+ argclass[nargs_wanted++] = string_arg;
+ else if (*f == 'e' || *f == 'f' || *f == 'g')
+ argclass[nargs_wanted++] = double_arg;
+ else if (lcount > 1)
+ argclass[nargs_wanted++] = long_long_arg;
+ else if (*f != '%')
+ argclass[nargs_wanted++] = int_arg;
+ f++;
+ }
+
+ /* Now, parse all arguments and evaluate them.
+ Store the VALUEs in VAL_ARGS. */
+
+ while (*s != '\0')
+ {
+ char *s1;
+ if (nargs == allocated_args)
+ val_args = (value *) xrealloc (val_args,
+ (allocated_args *= 2)
+ * sizeof (value));
+ s1 = s;
+ val_args[nargs] = parse_to_comma_and_eval (&s1);
+
+ /* If format string wants a float, unchecked-convert the value to
+ floating point of the same size */
+
+ if (argclass[nargs] == double_arg)
+ {
+ if (TYPE_LENGTH (VALUE_TYPE (val_args[nargs])) == sizeof (float))
+ VALUE_TYPE (val_args[nargs]) = builtin_type_float;
+ if (TYPE_LENGTH (VALUE_TYPE (val_args[nargs])) == sizeof (double))
+ VALUE_TYPE (val_args[nargs]) = builtin_type_double;
+ }
+ nargs++;
+ s = s1;
+ if (*s == ',')
+ s++;
+ }
+
+ if (nargs != nargs_wanted)
+ error ("Wrong number of arguments for specified format-string");
+
+ /* Now lay out an argument-list containing the arguments
+ as doubles, integers and C pointers. */
+
+ arg_bytes = (char *) alloca (sizeof (double) * nargs);
+ argindex = 0;
+ for (i = 0; i < nargs; i++)
+ {
+ if (argclass[i] == string_arg)
+ {
+ char *str;
+ int tem, j;
+ tem = value_as_long (val_args[i]);
+
+ /* This is a %s argument. Find the length of the string. */
+ for (j = 0; ; j++)
+ {
+ char c;
+ QUIT;
+ read_memory (tem + j, &c, 1);
+ if (c == 0)
+ break;
+ }
+
+ /* Copy the string contents into a string inside GDB. */
+ str = (char *) alloca (j + 1);
+ read_memory (tem, str, j);
+ str[j] = 0;
+
+ /* Pass address of internal copy as the arg to vprintf. */
+ *((int *) &arg_bytes[argindex]) = (int) str;
+ argindex += sizeof (int);
+ }
+ else if (VALUE_TYPE (val_args[i])->code == TYPE_CODE_FLT)
+ {
+ *((double *) &arg_bytes[argindex]) = value_as_double (val_args[i]);
+ argindex += sizeof (double);
+ }
+ else
+#ifdef LONG_LONG
+ if (argclass[i] == long_long_arg)
+ {
+ *(long long *) &arg_bytes[argindex] = value_as_long (val_args[i]);
+ argindex += sizeof (long long);
+ }
+ else
+#endif
+ {
+ *((int *) &arg_bytes[argindex]) = value_as_long (val_args[i]);
+ argindex += sizeof (int);
+ }
+ }
+ }
+ vprintf (string, arg_bytes);
+}
+
+/* Helper function for asdump_command. Finds the bounds of a function
+ for a specified section of text. PC is an address within the
+ function which you want bounds for; *LOW and *HIGH are set to the
+ beginning (inclusive) and end (exclusive) of the function. This
+ function returns 1 on success and 0 on failure. */
+
+static int
+containing_function_bounds (pc, low, high)
+ CORE_ADDR pc, *low, *high;
+{
+ int scan;
+
+ if (!find_pc_partial_function (pc, 0, low))
+ return 0;
+
+ scan = *low;
+ do {
+ scan++;
+ if (!find_pc_partial_function (scan, 0, high))
+ return 0;
+ } while (*low == *high);
+
+ return 1;
+}
+
+/* Dump a specified section of assembly code. With no command line
+ arguments, this command will dump the assembly code for the
+ function surrounding the pc value in the selected frame. With one
+ argument, it will dump the assembly code surrounding that pc value.
+ Two arguments are interpeted as bounds within which to dump
+ assembly. */
+
+static void
+disassemble_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ CORE_ADDR low, high;
+ CORE_ADDR pc;
+ char *space_index;
+
+ if (!arg)
+ {
+ if (!selected_frame)
+ error ("No frame selected.\n");
+
+ pc = get_frame_pc (selected_frame);
+ if (!containing_function_bounds (pc, &low, &high))
+ error ("No function contains pc specified by selected frame.\n");
+ }
+ else if (!(space_index = (char *) index (arg, ' ')))
+ {
+ /* One argument. */
+ pc = parse_and_eval_address (arg);
+ if (!containing_function_bounds (pc, &low, &high))
+ error ("No function contains specified pc.\n");
+ }
+ else
+ {
+ /* Two arguments. */
+ *space_index = '\0';
+ low = parse_and_eval_address (arg);
+ high = parse_and_eval_address (space_index + 1);
+ }
+
+ printf_filtered ("Dump of assembler code ");
+ if (!space_index)
+ {
+ char *name;
+ find_pc_partial_function (pc, &name, 0);
+ printf_filtered ("for function %s:\n", name);
+ }
+ else
+ printf_filtered ("from 0x%x to 0x%x:\n", low, high);
+
+ /* Dump the specified range. */
+ for (pc = low; pc < high; )
+ {
+ QUIT;
+ print_address (pc, stdout);
+ printf_filtered (":\t");
+ pc += print_insn (pc, stdout);
+ printf_filtered ("\n");
+ }
+ printf_filtered ("End of assembler dump.\n");
+ fflush (stdout);
+}
+
+
+extern struct cmd_list_element *enablelist, *disablelist, *deletelist;
+extern struct cmd_list_element *cmdlist, *setlist;
+
+void
+_initialize_printcmd ()
+{
+ current_display_number = -1;
+
+ add_info ("address", address_info,
+ "Describe where variable VAR is stored.");
+
+ add_com ("x", class_vars, x_command,
+ "Examine memory: x/FMT ADDRESS.\n\
+ADDRESS is an expression for the memory address to examine.\n\
+FMT is a repeat count followed by a format letter and a size letter.\n\
+Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),\n\
+ f(float), a(address), i(instruction), c(char) and s(string).\n\
+Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).\n\
+ g is meaningful only with f, for type double.\n\
+The specified number of objects of the specified size are printed\n\
+according to the format.\n\n\
+Defaults for format and size letters are those previously used.\n\
+Default count is 1. Default address is following last thing printed\n\
+with this command or \"print\".");
+
+ add_com ("disassemble", class_vars, disassemble_command,
+ "Disassemble a specified section of memory.\n\
+Default is the function surrounding the pc of the selected frame.\n\
+With a single argument, the function surrounding that address is dumped.\n\
+Two arguments are taken as a range of memory to dump.");
+
+ add_com ("ptype", class_vars, ptype_command,
+ "Print definition of type TYPE.\n\
+Argument may be a type name defined by typedef, or \"struct STRUCTNAME\"\n\
+or \"union UNIONNAME\" or \"enum ENUMNAME\".\n\
+The selected stack frame's lexical context is used to look up the name.");
+
+ add_com ("whatis", class_vars, whatis_command,
+ "Print data type of expression EXP.");
+
+ add_info ("display", display_info,
+ "Expressions to display when program stops, with code numbers.");
+
+ add_cmd ("undisplay", class_vars, undisplay_command,
+ "Cancel some expressions to be displayed when program stops.\n\
+Arguments are the code numbers of the expressions to stop displaying.\n\
+No argument means cancel all automatic-display expressions.\n\
+\"delete display\" has the same effect as this command.\n\
+Do \"info display\" to see current list of code numbers.",
+ &cmdlist);
+
+ add_com ("display", class_vars, display_command,
+ "Print value of expression EXP each time the program stops.\n\
+/FMT may be used before EXP as in the \"print\" command.\n\
+/FMT \"i\" or \"s\" or including a size-letter is allowed,\n\
+as in the \"x\" command, and then EXP is used to get the address to examine\n\
+and examining is done as in the \"x\" command.\n\n\
+With no argument, display all currently requested auto-display expressions.\n\
+Use \"undisplay\" to cancel display requests previously made.");
+
+ add_cmd ("display", class_vars, enable_display,
+ "Enable some expressions to be displayed when program stops.\n\
+Arguments are the code numbers of the expressions to resume displaying.\n\
+No argument means enable all automatic-display expressions.\n\
+Do \"info display\" to see current list of code numbers.", &enablelist);
+
+ add_cmd ("display", class_vars, disable_display_command,
+ "Disable some expressions to be displayed when program stops.\n\
+Arguments are the code numbers of the expressions to stop displaying.\n\
+No argument means disable all automatic-display expressions.\n\
+Do \"info display\" to see current list of code numbers.", &disablelist);
+
+ add_cmd ("display", class_vars, undisplay_command,
+ "Cancel some expressions to be displayed when program stops.\n\
+Arguments are the code numbers of the expressions to stop displaying.\n\
+No argument means cancel all automatic-display expressions.\n\
+Do \"info display\" to see current list of code numbers.", &deletelist);
+
+ add_com ("printf", class_vars, printf_command,
+ "printf \"printf format string\", arg1, arg2, arg3, ..., argn\n\
+This is useful for formatted output in user-defined commands.");
+ add_com ("output", class_vars, output_command,
+ "Like \"print\" but don't put in value history and don't print newline.\n\
+This is useful in user-defined commands.");
+
+ add_prefix_cmd ("set", class_vars, set_command,
+"Perform an assignment VAR = EXP.\n\
+You must type the \"=\". VAR may be a debugger \"convenience\" variable\n\
+(names starting with $), a register (a few standard names starting with $),\n\
+or an actual variable in the program being debugged. EXP is any expression.\n\
+Use \"set variable\" for variables with names identical to set subcommands.\n\
+\nWith a subcommand, this command modifies parts of the gdb environment",
+ &setlist, "set ", 1, &cmdlist);
+
+ add_cmd ("variable", class_vars, set_command,
+ "Perform an assignment VAR = EXP.\n\
+You must type the \"=\". VAR may be a debugger \"convenience\" variable\n\
+(names starting with $), a register (a few standard names starting with $),\n\
+or an actual variable in the program being debugged. EXP is any expression.\n\
+This may usually be abbreviated to simply \"set\".",
+ &setlist);
+
+ add_com ("print", class_vars, print_command,
+ concat ("Print value of expression EXP.\n\
+Variables accessible are those of the lexical environment of the selected\n\
+stack frame, plus all those whose scope is global or an entire file.\n\
+\n\
+$NUM gets previous value number NUM. $ and $$ are the last two values.\n\
+$$NUM refers to NUM'th value back from the last one.\n\
+Names starting with $ refer to registers (with the values they would have\n\
+if the program were to return to the stack frame now selected, restoring\n\
+all registers saved by frames farther in) or else to debugger\n\
+\"convenience\" variables (any such name not a known register).\n\
+Use assignment expressions to give values to convenience variables.\n",
+ "\n\
+\{TYPE}ADREXP refers to a datum of data type TYPE, located at address ADREXP.\n\
+@ is a binary operator for treating consecutive data objects\n\
+anywhere in memory as an array. FOO@NUM gives an array whose first\n\
+element is FOO, whose second element is stored in the space following\n\
+where FOO is stored, etc. FOO must be an expression whose value\n\
+resides in memory.\n",
+ "\n\
+EXP may be preceded with /FMT, where FMT is a format letter\n\
+but no count or size letter (see \"x\" command)."));
+ add_com_alias ("p", "print", class_vars, 1);
+}
diff --git a/gnu/usr.bin/gdb/readline/ChangeLog b/gnu/usr.bin/gdb/readline/ChangeLog
new file mode 100644
index 0000000..b72a59d
--- /dev/null
+++ b/gnu/usr.bin/gdb/readline/ChangeLog
@@ -0,0 +1,98 @@
+Thu Feb 8 01:04:00 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu)
+
+ * Makefile (the *other* libreadline.a): Uncomment out ranlib line.
+
+Thu Feb 1 17:50:22 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu)
+
+ * Makefile (libreadline.a): Uncomment out ranlib line.
+
+Sun Nov 26 16:29:11 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * readline.c (rl_deprep_terminal): Only restore local_mode_flags
+ if they had been set.
+
+Thu Oct 19 17:18:40 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * Move vi_doing_insert from vi_mode.c to readline.c
+
+ * readline.c: Move compare_strings before its use.
+ Remove declarations.
+
+ * readline.c: Move defining_kbd_macro above rl_dispatch.
+ (rl_dispatch): Remove "extern int defining_kbd_macro".
+
+Mon Oct 16 11:56:03 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * readline.c (rl_set_signals): Remove unnecessary "static int
+ rl_signal_handler()".
+
+Sat Sep 30 14:51:56 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * readline.c (rl_initialize): Change parsing_conditionalized_out
+ to static.
+ (rl_dispatch): Change defining_kbd_macro to static.
+ (rl_newline): Change vi_doing_insert to static.
+
+Fri Sep 8 09:00:45 1989 Brian Fox (bfox at aurel)
+
+ * readline.c: rl_prep_terminal (). Only turn on 8th bit
+ as meta-bit iff the terminal is not using parity.
+
+Sun Sep 3 08:57:40 1989 Brian Fox (bfox at aurel)
+
+ * readline.c: start_insert (). Uses multiple
+ insertion call in cases where that makes sense.
+
+ rl_insert (). Read type-ahead buffer for additional
+ keys that are bound to rl_insert, and insert them
+ all at once. Make insertion of single keys given
+ with an argument much more efficient.
+
+Tue Aug 8 18:13:57 1989 Brian Fox (bfox at aurel)
+
+ * readline.c: Changed handling of EOF. readline () returns
+ (char *)EOF or consed string. The EOF character is read from the
+ tty, or if the tty doesn't have one, defaults to C-d.
+
+ * readline.c: Added support for event driven programs.
+ rl_event_hook is the address of a function you want called
+ while Readline is waiting for input.
+
+ * readline.c: Cleanup time. Functions without type declarations
+ do not use return with a value.
+
+ * history.c: history_expand () has new variable which is the
+ characters to ignore immediately following history_expansion_char.
+
+Sun Jul 16 08:14:00 1989 Brian Fox (bfox at aurel)
+
+ * rl_prep_terminal ()
+ BSD version turns off C-s, C-q, C-y, C-v.
+
+ * readline.c -- rl_prep_terminal ()
+ SYSV version hacks readline_echoing_p.
+ BSD version turns on passing of the 8th bit for the duration
+ of reading the line.
+
+Tue Jul 11 06:25:01 1989 Brian Fox (bfox at aurel)
+
+ * readline.c: new variable rl_tilde_expander.
+ If non-null, this contains the address of a function to call if
+ the standard meaning for expanding a tilde fails. The function is
+ called with the text sans tilde (as in "foo"), and returns a
+ malloc()'ed string which is the expansion, or a NULL pointer if
+ there is no expansion.
+
+ * readline.h - new file chardefs.h
+ Separates things that only readline.c needs from the standard
+ header file publishing interesting things about readline.
+
+ * readline.c:
+ readline_default_bindings () now looks at terminal chararacters
+ and binds those as well.
+
+Wed Jun 28 20:20:51 1989 Brian Fox (bfox at aurel)
+
+ * Made readline and history into independent libraries.
+
+
diff --git a/gnu/usr.bin/gdb/readline/Makefile.gnu b/gnu/usr.bin/gdb/readline/Makefile.gnu
new file mode 100644
index 0000000..dc11539
--- /dev/null
+++ b/gnu/usr.bin/gdb/readline/Makefile.gnu
@@ -0,0 +1,114 @@
+## -*- text -*- ####################################################
+# #
+# Makefile for readline and history libraries. #
+# #
+####################################################################
+
+# Here is a rule for making .o files from .c files that doesn't force
+# the type of the machine (like -sun3) into the flags.
+.c.o:
+ $(CC) -c $(CFLAGS) $(LOCAL_INCLUDES) $(CPPFLAGS) $*.c
+
+# Destination installation directory. The libraries are copied to DESTDIR
+# when you do a `make install', and the header files to INCDIR/readline/*.h.
+DESTDIR = /usr/gnu/lib
+INCDIR = /usr/gnu/include
+
+# Define TYPES as -DVOID_SIGHANDLER if your operating system uses
+# a return type of "void" for signal handlers.
+TYPES = -DVOID_SIGHANDLER
+
+# Define SYSV as -DSYSV if you are using a System V operating system.
+#SYSV = -DSYSV
+
+# HP-UX compilation requires the BSD library.
+#LOCAL_LIBS = -lBSD
+
+# Xenix compilation requires -ldir -lx
+#LOCAL_LIBS = -ldir -lx
+
+# Comment this out if you don't think that anyone will ever desire
+# the vi line editing mode and features.
+READLINE_DEFINES = -DVI_MODE
+
+DEBUG_FLAGS = -g
+LDFLAGS = $(DEBUG_FLAGS)
+CFLAGS = $(DEBUG_FLAGS) $(TYPE) $(SYSV) -I.
+
+# A good alternative is gcc -traditional.
+#CC = gcc -traditional
+CC = cc
+RANLIB = /usr/bin/ranlib
+AR = ar
+RM = rm
+CP = cp
+
+LOCAL_INCLUDES = -I../
+
+CSOURCES = readline.c history.c funmap.c keymaps.c vi_mode.c \
+ emacs_keymap.c vi_keymap.c keymaps.c
+
+HSOURCES = readline.h chardefs.h history.h keymaps.h
+SOURCES = $(CSOURCES) $(HSOURCES)
+
+DOCUMENTATION = readline.texinfo inc-readline.texinfo \
+ history.texinfo inc-history.texinfo
+
+SUPPORT = COPYING Makefile $(DOCUMENTATION) ChangeLog
+
+THINGS_TO_TAR = $(SOURCES) $(SUPPORT)
+
+##########################################################################
+
+all: libreadline.a
+
+libreadline.a: readline.o history.o funmap.o keymaps.o
+ $(RM) -f libreadline.a
+ $(AR) clq libreadline.a readline.o history.o funmap.o keymaps.o
+ if [ -f $(RANLIB) ]; then $(RANLIB) libreadline.a; fi
+
+readline.o: readline.h chardefs.h keymaps.h history.h readline.c vi_mode.c
+ $(CC) -c $(CFLAGS) $(CPPFLAGS) $(READLINE_DEFINES) \
+ $(LOCAL_INCLUDES) $*.c
+
+history.o: history.c history.h
+ $(CC) -c $(CFLAGS) $(CPPFLAGS) $(READLINE_DEFINES) \
+ $(LOCAL_INCLUDES) $*.c
+
+funmap.o: readline.h
+ $(CC) -c $(CFLAGS) $(CPPFLAGS) $(READLINE_DEFINES) \
+ $(LOCAL_INCLUDES) $*.c
+
+keymaps.o: emacs_keymap.c vi_keymap.c keymaps.h chardefs.h keymaps.c
+ $(CC) -c $(CFLAGS) $(CPPFLAGS) $(READLINE_DEFINES) \
+ $(LOCAL_INCLUDES) $*.c
+
+libtest: libreadline.a libtest.c
+ $(CC) -o libtest $(CFLAGS) $(CPPFLAGS) -L. libtest.c -lreadline -ltermcap
+
+readline: readline.c history.o keymaps.o funmap.o readline.h chardefs.h
+ $(CC) $(CFLAGS) $(CPPFLAGS) $(READLINE_DEFINES) \
+ $(LOCAL_INCLUDES) -DTEST -o readline readline.c funmap.o \
+ keymaps.o history.o -L. -ltermcap
+
+readline.tar: $(THINGS_TO_TAR)
+ tar -cf readline.tar $(THINGS_TO_TAR)
+
+readline.tar.Z: readline.tar
+ compress -f readline.tar
+
+install: $(DESTDIR)/libreadline.a includes
+
+includes:
+ if [ ! -r $(INCDIR)/readline ]; then\
+ mkdir $(INCDIR)/readline;\
+ chmod a+r $(INCDIR)/readline;\
+ fi
+ $(CP) readline.h keymaps.h chardefs.h $(INCDIR)/readline/
+clean:
+ rm -f *.o *.a *.log *.cp *.tp *.vr *.fn *.aux *.pg *.toc
+
+$(DESTDIR)/libreadline.a: libreadline.a
+ -mv $(DESTDIR)/libreadline.a $(DESTDIR)/libreadline.old
+ cp libreadline.a $(DESTDIR)/libreadline.a
+ $(RANLIB) -t $(DESTDIR)/libreadline.a
diff --git a/gnu/usr.bin/gdb/readline/chardefs.h b/gnu/usr.bin/gdb/readline/chardefs.h
new file mode 100644
index 0000000..9749ae4
--- /dev/null
+++ b/gnu/usr.bin/gdb/readline/chardefs.h
@@ -0,0 +1,50 @@
+/* chardefs.h -- Character definitions for readline. */
+#ifndef _CHARDEFS_
+
+#ifndef savestring
+#define savestring(x) (char *)strcpy (xmalloc (1 + strlen (x)), (x))
+#endif
+
+#ifndef whitespace
+#define whitespace(c) (((c) == ' ') || ((c) == '\t'))
+#endif
+
+#ifdef CTRL
+#undef CTRL
+#endif
+
+/* Some character stuff. */
+#define control_character_threshold 0x020 /* smaller than this is control */
+#define meta_character_threshold 0x07f /* larger than this is Meta. */
+#define control_character_bit 0x40 /* 0x000000, must be off. */
+#define meta_character_bit 0x080 /* x0000000, must be on. */
+
+#define CTRL(c) ((c) & (~control_character_bit))
+#define META(c) ((c) | meta_character_bit)
+
+#define UNMETA(c) ((c) & (~meta_character_bit))
+#define UNCTRL(c) to_upper(((c)|control_character_bit))
+
+#define lowercase_p(c) (((c) > ('a' - 1) && (c) < ('z' + 1)))
+#define uppercase_p(c) (((c) > ('A' - 1) && (c) < ('Z' + 1)))
+
+#define pure_alphabetic(c) (lowercase_p(c) || uppercase_p(c))
+
+#ifndef to_upper
+#define to_upper(c) (lowercase_p(c) ? ((c) - 32) : (c))
+#define to_lower(c) (uppercase_p(c) ? ((c) + 32) : (c))
+#endif
+
+#define CTRL_P(c) ((c) < control_character_threshold)
+#define META_P(c) ((c) > meta_character_threshold)
+
+#define NEWLINE '\n'
+#define RETURN CTRL('M')
+#define RUBOUT 0x07f
+#define TAB '\t'
+#define ABORT_CHAR CTRL('G')
+#define PAGE CTRL('L')
+#define SPACE 0x020
+#define ESC CTRL('[')
+
+#endif /* _CHARDEFS_ */
diff --git a/gnu/usr.bin/gdb/readline/emacs_keymap.c b/gnu/usr.bin/gdb/readline/emacs_keymap.c
new file mode 100644
index 0000000..7030e69
--- /dev/null
+++ b/gnu/usr.bin/gdb/readline/emacs_keymap.c
@@ -0,0 +1,472 @@
+/* emacs_keymap.c -- the keymap for emacs_mode in readline (). */
+
+/* Copyright (C) 1988,1989 Free Software Foundation, Inc.
+
+ This file is part of GNU Readline, a library for reading lines
+ of text with interactive input and history editing.
+
+ Readline is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 1, or (at your option) any
+ later version.
+
+ Readline is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Readline; see the file COPYING. If not, write to the Free
+ Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef FILE
+#include <stdio.h>
+#endif /* FILE */
+
+#include "readline.h"
+
+/* An array of function pointers, one for each possible key.
+ If the type byte is ISKMAP, then the pointer is the address of
+ a keymap. */
+
+KEYMAP_ENTRY_ARRAY emacs_standard_keymap = {
+
+ /* Control keys. */
+ { ISFUNC, (Function *)0x0 }, /* Control-@ */
+ { ISFUNC, rl_beg_of_line }, /* Control-a */
+ { ISFUNC, rl_backward }, /* Control-b */
+ { ISFUNC, (Function *)0x0 }, /* Control-c */
+ { ISFUNC, rl_delete }, /* Control-d */
+ { ISFUNC, rl_end_of_line }, /* Control-e */
+ { ISFUNC, rl_forward }, /* Control-f */
+ { ISFUNC, rl_abort }, /* Control-g */
+ { ISFUNC, rl_backward }, /* Control-h */
+ { ISFUNC, rl_complete }, /* Control-i */
+ { ISFUNC, rl_newline }, /* Control-j */
+ { ISFUNC, rl_kill_line }, /* Control-k */
+ { ISFUNC, rl_clear_screen }, /* Control-l */
+ { ISFUNC, rl_newline }, /* Control-m */
+ { ISFUNC, rl_get_next_history }, /* Control-n */
+ { ISFUNC, (Function *)0x0 }, /* Control-o */
+ { ISFUNC, rl_get_previous_history }, /* Control-p */
+ { ISFUNC, rl_quoted_insert }, /* Control-q */
+ { ISFUNC, rl_reverse_search_history }, /* Control-r */
+ { ISFUNC, rl_forward_search_history }, /* Control-s */
+ { ISFUNC, rl_transpose_chars }, /* Control-t */
+ { ISFUNC, rl_unix_line_discard }, /* Control-u */
+ { ISFUNC, rl_quoted_insert }, /* Control-v */
+ { ISFUNC, rl_unix_word_rubout }, /* Control-w */
+ { ISKMAP, (Function *)emacs_ctlx_keymap }, /* Control-x */
+ { ISFUNC, rl_yank }, /* Control-y */
+ { ISFUNC, (Function *)0x0 }, /* Control-z */
+ { ISKMAP, (Function *)emacs_meta_keymap }, /* Control-[ */
+ { ISFUNC, (Function *)0x0 }, /* Control-\ */
+ { ISFUNC, (Function *)0x0 }, /* Control-] */
+ { ISFUNC, (Function *)0x0 }, /* Control-^ */
+ { ISFUNC, rl_undo_command }, /* Control-_ */
+
+ /* The start of printing characters. */
+ { ISFUNC, rl_insert }, /* SPACE */
+ { ISFUNC, rl_insert }, /* ! */
+ { ISFUNC, rl_insert }, /* " */
+ { ISFUNC, rl_insert }, /* # */
+ { ISFUNC, rl_insert }, /* $ */
+ { ISFUNC, rl_insert }, /* % */
+ { ISFUNC, rl_insert }, /* & */
+ { ISFUNC, rl_insert }, /* ' */
+ { ISFUNC, rl_insert }, /* ( */
+ { ISFUNC, rl_insert }, /* ) */
+ { ISFUNC, rl_insert }, /* * */
+ { ISFUNC, rl_insert }, /* + */
+ { ISFUNC, rl_insert }, /* , */
+ { ISFUNC, rl_insert }, /* - */
+ { ISFUNC, rl_insert }, /* . */
+ { ISFUNC, rl_insert }, /* / */
+
+ /* Regular digits. */
+ { ISFUNC, rl_insert }, /* 0 */
+ { ISFUNC, rl_insert }, /* 1 */
+ { ISFUNC, rl_insert }, /* 2 */
+ { ISFUNC, rl_insert }, /* 3 */
+ { ISFUNC, rl_insert }, /* 4 */
+ { ISFUNC, rl_insert }, /* 5 */
+ { ISFUNC, rl_insert }, /* 6 */
+ { ISFUNC, rl_insert }, /* 7 */
+ { ISFUNC, rl_insert }, /* 8 */
+ { ISFUNC, rl_insert }, /* 9 */
+
+ /* A little more punctuation. */
+ { ISFUNC, rl_insert }, /* : */
+ { ISFUNC, rl_insert }, /* ; */
+ { ISFUNC, rl_insert }, /* < */
+ { ISFUNC, rl_insert }, /* = */
+ { ISFUNC, rl_insert }, /* > */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* @ */
+
+ /* Uppercase alphabet. */
+ { ISFUNC, rl_insert }, /* A */
+ { ISFUNC, rl_insert }, /* B */
+ { ISFUNC, rl_insert }, /* C */
+ { ISFUNC, rl_insert }, /* D */
+ { ISFUNC, rl_insert }, /* E */
+ { ISFUNC, rl_insert }, /* F */
+ { ISFUNC, rl_insert }, /* G */
+ { ISFUNC, rl_insert }, /* H */
+ { ISFUNC, rl_insert }, /* I */
+ { ISFUNC, rl_insert }, /* J */
+ { ISFUNC, rl_insert }, /* K */
+ { ISFUNC, rl_insert }, /* L */
+ { ISFUNC, rl_insert }, /* M */
+ { ISFUNC, rl_insert }, /* N */
+ { ISFUNC, rl_insert }, /* O */
+ { ISFUNC, rl_insert }, /* P */
+ { ISFUNC, rl_insert }, /* Q */
+ { ISFUNC, rl_insert }, /* R */
+ { ISFUNC, rl_insert }, /* S */
+ { ISFUNC, rl_insert }, /* T */
+ { ISFUNC, rl_insert }, /* U */
+ { ISFUNC, rl_insert }, /* V */
+ { ISFUNC, rl_insert }, /* W */
+ { ISFUNC, rl_insert }, /* X */
+ { ISFUNC, rl_insert }, /* Y */
+ { ISFUNC, rl_insert }, /* Z */
+
+ /* Some more punctuation. */
+ { ISFUNC, rl_insert }, /* [ */
+ { ISFUNC, rl_insert }, /* \ */
+ { ISFUNC, rl_insert }, /* ] */
+ { ISFUNC, rl_insert }, /* ^ */
+ { ISFUNC, rl_insert }, /* _ */
+ { ISFUNC, rl_insert }, /* ` */
+
+ /* Lowercase alphabet. */
+ { ISFUNC, rl_insert }, /* a */
+ { ISFUNC, rl_insert }, /* b */
+ { ISFUNC, rl_insert }, /* c */
+ { ISFUNC, rl_insert }, /* d */
+ { ISFUNC, rl_insert }, /* e */
+ { ISFUNC, rl_insert }, /* f */
+ { ISFUNC, rl_insert }, /* g */
+ { ISFUNC, rl_insert }, /* h */
+ { ISFUNC, rl_insert }, /* i */
+ { ISFUNC, rl_insert }, /* j */
+ { ISFUNC, rl_insert }, /* k */
+ { ISFUNC, rl_insert }, /* l */
+ { ISFUNC, rl_insert }, /* m */
+ { ISFUNC, rl_insert }, /* n */
+ { ISFUNC, rl_insert }, /* o */
+ { ISFUNC, rl_insert }, /* p */
+ { ISFUNC, rl_insert }, /* q */
+ { ISFUNC, rl_insert }, /* r */
+ { ISFUNC, rl_insert }, /* s */
+ { ISFUNC, rl_insert }, /* t */
+ { ISFUNC, rl_insert }, /* u */
+ { ISFUNC, rl_insert }, /* v */
+ { ISFUNC, rl_insert }, /* w */
+ { ISFUNC, rl_insert }, /* x */
+ { ISFUNC, rl_insert }, /* y */
+ { ISFUNC, rl_insert }, /* z */
+
+ /* Final punctuation. */
+ { ISFUNC, rl_insert }, /* { */
+ { ISFUNC, rl_insert }, /* | */
+ { ISFUNC, rl_insert }, /* } */
+ { ISFUNC, rl_insert }, /* ~ */
+ { ISFUNC, rl_rubout } /* RUBOUT */
+};
+
+KEYMAP_ENTRY_ARRAY emacs_meta_keymap = {
+
+ /* Meta keys. Just like above, but the high bit is set. */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-@ */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-a */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-b */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-c */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-d */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-e */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-f */
+ { ISFUNC, rl_abort }, /* Meta-Control-g */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-h */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-i */
+ { ISFUNC, rl_vi_editing_mode }, /* Meta-Control-j */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-k */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-l */
+ { ISFUNC, rl_vi_editing_mode }, /* Meta-Control-m */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-n */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-o */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-p */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-q */
+ { ISFUNC, rl_revert_line }, /* Meta-Control-r */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-s */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-t */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-u */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-v */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-w */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-x */
+ { ISFUNC, rl_yank_nth_arg }, /* Meta-Control-y */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-z */
+
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-[ */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-\ */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-] */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-^ */
+ { ISFUNC, (Function *)0x0 }, /* Meta-Control-_ */
+
+ /* The start of printing characters. */
+ { ISFUNC, (Function *)0x0 }, /* Meta-SPACE */
+ { ISFUNC, (Function *)0x0 }, /* Meta-! */
+ { ISFUNC, (Function *)0x0 }, /* Meta-" */
+ { ISFUNC, (Function *)0x0 }, /* Meta-# */
+ { ISFUNC, (Function *)0x0 }, /* Meta-$ */
+ { ISFUNC, (Function *)0x0 }, /* Meta-% */
+ { ISFUNC, (Function *)0x0 }, /* Meta-& */
+ { ISFUNC, (Function *)0x0 }, /* Meta-' */
+ { ISFUNC, (Function *)0x0 }, /* Meta-( */
+ { ISFUNC, (Function *)0x0 }, /* Meta-) */
+ { ISFUNC, (Function *)0x0 }, /* Meta-* */
+ { ISFUNC, (Function *)0x0 }, /* Meta-+ */
+ { ISFUNC, (Function *)0x0 }, /* Meta-, */
+ { ISFUNC, rl_digit_argument }, /* Meta-- */
+ { ISFUNC, (Function *)0x0 }, /* Meta-. */
+ { ISFUNC, (Function *)0x0 }, /* Meta-/ */
+
+ /* Regular digits. */
+ { ISFUNC, rl_digit_argument }, /* Meta-0 */
+ { ISFUNC, rl_digit_argument }, /* Meta-1 */
+ { ISFUNC, rl_digit_argument }, /* Meta-2 */
+ { ISFUNC, rl_digit_argument }, /* Meta-3 */
+ { ISFUNC, rl_digit_argument }, /* Meta-4 */
+ { ISFUNC, rl_digit_argument }, /* Meta-5 */
+ { ISFUNC, rl_digit_argument }, /* Meta-6 */
+ { ISFUNC, rl_digit_argument }, /* Meta-7 */
+ { ISFUNC, rl_digit_argument }, /* Meta-8 */
+ { ISFUNC, rl_digit_argument }, /* Meta-9 */
+
+ /* A little more punctuation. */
+ { ISFUNC, (Function *)0x0 }, /* Meta-: */
+ { ISFUNC, (Function *)0x0 }, /* Meta-; */
+ { ISFUNC, rl_beginning_of_history }, /* Meta-< */
+ { ISFUNC, (Function *)0x0 }, /* Meta-= */
+ { ISFUNC, rl_end_of_history }, /* Meta-> */
+ { ISFUNC, rl_possible_completions }, /* Meta-? */
+ { ISFUNC, (Function *)0x0 }, /* Meta-@ */
+
+ /* Uppercase alphabet. */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-A */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-B */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-C */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-D */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-E */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-F */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-G */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-H */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-I */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-J */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-K */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-L */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-M */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-N */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-O */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-P */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-Q */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-R */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-S */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-T */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-U */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-V */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-W */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-X */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-Y */
+ { ISFUNC, rl_do_lowercase_version }, /* Meta-Z */
+
+ /* Some more punctuation. */
+ { ISFUNC, (Function *)0x0 }, /* Meta-[ */
+ { ISFUNC, (Function *)0x0 }, /* Meta-\ */
+ { ISFUNC, (Function *)0x0 }, /* Meta-] */
+ { ISFUNC, (Function *)0x0 }, /* Meta-^ */
+ { ISFUNC, (Function *)0x0 }, /* Meta-_ */
+ { ISFUNC, (Function *)0x0 }, /* Meta-` */
+
+ /* Lowercase alphabet. */
+ { ISFUNC, (Function *)0x0 }, /* Meta-a */
+ { ISFUNC, rl_backward_word }, /* Meta-b */
+ { ISFUNC, rl_capitalize_word }, /* Meta-c */
+ { ISFUNC, rl_kill_word }, /* Meta-d */
+ { ISFUNC, (Function *)0x0 }, /* Meta-e */
+ { ISFUNC, rl_forward_word }, /* Meta-f */
+ { ISFUNC, (Function *)0x0 }, /* Meta-g */
+ { ISFUNC, (Function *)0x0 }, /* Meta-h */
+ { ISFUNC, (Function *)0x0 }, /* Meta-i */
+ { ISFUNC, (Function *)0x0 }, /* Meta-j */
+ { ISFUNC, (Function *)0x0 }, /* Meta-k */
+ { ISFUNC, rl_downcase_word }, /* Meta-l */
+ { ISFUNC, (Function *)0x0 }, /* Meta-m */
+ { ISFUNC, (Function *)0x0 }, /* Meta-n */
+ { ISFUNC, (Function *)0x0 }, /* Meta-o */
+ { ISFUNC, (Function *)0x0 }, /* Meta-p */
+ { ISFUNC, (Function *)0x0 }, /* Meta-q */
+ { ISFUNC, rl_revert_line }, /* Meta-r */
+ { ISFUNC, (Function *)0x0 }, /* Meta-s */
+ { ISFUNC, rl_transpose_words }, /* Meta-t */
+ { ISFUNC, rl_upcase_word }, /* Meta-u */
+ { ISFUNC, (Function *)0x0 }, /* Meta-v */
+ { ISFUNC, (Function *)0x0 }, /* Meta-w */
+ { ISFUNC, (Function *)0x0 }, /* Meta-x */
+ { ISFUNC, rl_yank_pop }, /* Meta-y */
+ { ISFUNC, (Function *)0x0 }, /* Meta-z */
+
+ /* Final punctuation. */
+ { ISFUNC, (Function *)0x0 }, /* Meta-{ */
+ { ISFUNC, (Function *)0x0 }, /* Meta-| */
+ { ISFUNC, (Function *)0x0 }, /* Meta-} */
+ { ISFUNC, (Function *)0x0 }, /* Meta-~ */
+ { ISFUNC, rl_backward_kill_word } /* Meta-rubout */
+};
+
+KEYMAP_ENTRY_ARRAY emacs_ctlx_keymap = {
+
+ /* Control keys. */
+ { ISFUNC, (Function *)0x0 }, /* Control-@ */
+ { ISFUNC, (Function *)0x0 }, /* Control-a */
+ { ISFUNC, (Function *)0x0 }, /* Control-b */
+ { ISFUNC, (Function *)0x0 }, /* Control-c */
+ { ISFUNC, (Function *)0x0 }, /* Control-d */
+ { ISFUNC, (Function *)0x0 }, /* Control-e */
+ { ISFUNC, (Function *)0x0 }, /* Control-f */
+ { ISFUNC, rl_abort }, /* Control-g */
+ { ISFUNC, (Function *)0x0 }, /* Control-h */
+ { ISFUNC, (Function *)0x0 }, /* Control-i */
+ { ISFUNC, (Function *)0x0 }, /* Control-j */
+ { ISFUNC, (Function *)0x0 }, /* Control-k */
+ { ISFUNC, (Function *)0x0 }, /* Control-l */
+ { ISFUNC, (Function *)0x0 }, /* Control-m */
+ { ISFUNC, (Function *)0x0 }, /* Control-n */
+ { ISFUNC, (Function *)0x0 }, /* Control-o */
+ { ISFUNC, (Function *)0x0 }, /* Control-p */
+ { ISFUNC, (Function *)0x0 }, /* Control-q */
+ { ISFUNC, rl_re_read_init_file }, /* Control-r */
+ { ISFUNC, (Function *)0x0 }, /* Control-s */
+ { ISFUNC, (Function *)0x0 }, /* Control-t */
+ { ISFUNC, rl_undo_command }, /* Control-u */
+ { ISFUNC, (Function *)0x0 }, /* Control-v */
+ { ISFUNC, (Function *)0x0 }, /* Control-w */
+ { ISFUNC, (Function *)0x0 }, /* Control-x */
+ { ISFUNC, (Function *)0x0 }, /* Control-y */
+ { ISFUNC, (Function *)0x0 }, /* Control-z */
+ { ISFUNC, (Function *)0x0 }, /* Control-[ */
+ { ISFUNC, (Function *)0x0 }, /* Control-\ */
+ { ISFUNC, (Function *)0x0 }, /* Control-] */
+ { ISFUNC, (Function *)0x0 }, /* Control-^ */
+ { ISFUNC, (Function *)0x0 }, /* Control-_ */
+
+ /* The start of printing characters. */
+ { ISFUNC, (Function *)0x0 }, /* SPACE */
+ { ISFUNC, (Function *)0x0 }, /* ! */
+ { ISFUNC, (Function *)0x0 }, /* " */
+ { ISFUNC, (Function *)0x0 }, /* # */
+ { ISFUNC, (Function *)0x0 }, /* $ */
+ { ISFUNC, (Function *)0x0 }, /* % */
+ { ISFUNC, (Function *)0x0 }, /* & */
+ { ISFUNC, (Function *)0x0 }, /* ' */
+ { ISFUNC, rl_start_kbd_macro }, /* ( */
+ { ISFUNC, rl_end_kbd_macro }, /* ) */
+ { ISFUNC, (Function *)0x0 }, /* * */
+ { ISFUNC, (Function *)0x0 }, /* + */
+ { ISFUNC, (Function *)0x0 }, /* , */
+ { ISFUNC, (Function *)0x0 }, /* - */
+ { ISFUNC, (Function *)0x0 }, /* . */
+ { ISFUNC, (Function *)0x0 }, /* / */
+
+ /* Regular digits. */
+ { ISFUNC, (Function *)0x0 }, /* 0 */
+ { ISFUNC, (Function *)0x0 }, /* 1 */
+ { ISFUNC, (Function *)0x0 }, /* 2 */
+ { ISFUNC, (Function *)0x0 }, /* 3 */
+ { ISFUNC, (Function *)0x0 }, /* 4 */
+ { ISFUNC, (Function *)0x0 }, /* 5 */
+ { ISFUNC, (Function *)0x0 }, /* 6 */
+ { ISFUNC, (Function *)0x0 }, /* 7 */
+ { ISFUNC, (Function *)0x0 }, /* 8 */
+ { ISFUNC, (Function *)0x0 }, /* 9 */
+
+ /* A little more punctuation. */
+ { ISFUNC, (Function *)0x0 }, /* : */
+ { ISFUNC, (Function *)0x0 }, /* ; */
+ { ISFUNC, (Function *)0x0 }, /* < */
+ { ISFUNC, (Function *)0x0 }, /* = */
+ { ISFUNC, (Function *)0x0 }, /* > */
+ { ISFUNC, (Function *)0x0 }, /* ? */
+ { ISFUNC, (Function *)0x0 }, /* @ */
+
+ /* Uppercase alphabet. */
+ { ISFUNC, rl_do_lowercase_version }, /* A */
+ { ISFUNC, rl_do_lowercase_version }, /* B */
+ { ISFUNC, rl_do_lowercase_version }, /* C */
+ { ISFUNC, rl_do_lowercase_version }, /* D */
+ { ISFUNC, rl_do_lowercase_version }, /* E */
+ { ISFUNC, rl_do_lowercase_version }, /* F */
+ { ISFUNC, rl_do_lowercase_version }, /* G */
+ { ISFUNC, rl_do_lowercase_version }, /* H */
+ { ISFUNC, rl_do_lowercase_version }, /* I */
+ { ISFUNC, rl_do_lowercase_version }, /* J */
+ { ISFUNC, rl_do_lowercase_version }, /* K */
+ { ISFUNC, rl_do_lowercase_version }, /* L */
+ { ISFUNC, rl_do_lowercase_version }, /* M */
+ { ISFUNC, rl_do_lowercase_version }, /* N */
+ { ISFUNC, rl_do_lowercase_version }, /* O */
+ { ISFUNC, rl_do_lowercase_version }, /* P */
+ { ISFUNC, rl_do_lowercase_version }, /* Q */
+ { ISFUNC, rl_do_lowercase_version }, /* R */
+ { ISFUNC, rl_do_lowercase_version }, /* S */
+ { ISFUNC, rl_do_lowercase_version }, /* T */
+ { ISFUNC, rl_do_lowercase_version }, /* U */
+ { ISFUNC, rl_do_lowercase_version }, /* V */
+ { ISFUNC, rl_do_lowercase_version }, /* W */
+ { ISFUNC, rl_do_lowercase_version }, /* X */
+ { ISFUNC, rl_do_lowercase_version }, /* Y */
+ { ISFUNC, rl_do_lowercase_version }, /* Z */
+
+ /* Some more punctuation. */
+ { ISFUNC, (Function *)0x0 }, /* [ */
+ { ISFUNC, (Function *)0x0 }, /* \ */
+ { ISFUNC, (Function *)0x0 }, /* ] */
+ { ISFUNC, (Function *)0x0 }, /* ^ */
+ { ISFUNC, (Function *)0x0 }, /* _ */
+ { ISFUNC, (Function *)0x0 }, /* ` */
+
+ /* Lowercase alphabet. */
+ { ISFUNC, (Function *)0x0 }, /* a */
+ { ISFUNC, (Function *)0x0 }, /* b */
+ { ISFUNC, (Function *)0x0 }, /* c */
+ { ISFUNC, (Function *)0x0 }, /* d */
+ { ISFUNC, rl_call_last_kbd_macro }, /* e */
+ { ISFUNC, (Function *)0x0 }, /* f */
+ { ISFUNC, (Function *)0x0 }, /* g */
+ { ISFUNC, (Function *)0x0 }, /* h */
+ { ISFUNC, (Function *)0x0 }, /* i */
+ { ISFUNC, (Function *)0x0 }, /* j */
+ { ISFUNC, (Function *)0x0 }, /* k */
+ { ISFUNC, (Function *)0x0 }, /* l */
+ { ISFUNC, (Function *)0x0 }, /* m */
+ { ISFUNC, (Function *)0x0 }, /* n */
+ { ISFUNC, (Function *)0x0 }, /* o */
+ { ISFUNC, (Function *)0x0 }, /* p */
+ { ISFUNC, (Function *)0x0 }, /* q */
+ { ISFUNC, rl_re_read_init_file }, /* r */
+ { ISFUNC, (Function *)0x0 }, /* s */
+ { ISFUNC, (Function *)0x0 }, /* t */
+ { ISFUNC, (Function *)0x0 }, /* u */
+ { ISFUNC, (Function *)0x0 }, /* v */
+ { ISFUNC, (Function *)0x0 }, /* w */
+ { ISFUNC, (Function *)0x0 }, /* x */
+ { ISFUNC, (Function *)0x0 }, /* y */
+ { ISFUNC, (Function *)0x0 }, /* z */
+
+ /* Final punctuation. */
+ { ISFUNC, (Function *)0x0 }, /* { */
+ { ISFUNC, (Function *)0x0 }, /* | */
+ { ISFUNC, (Function *)0x0 }, /* } */
+ { ISFUNC, (Function *)0x0 }, /* ~ */
+ { ISFUNC, rl_backward_kill_line } /* RUBOUT */
+};
diff --git a/gnu/usr.bin/gdb/readline/funmap.c b/gnu/usr.bin/gdb/readline/funmap.c
new file mode 100644
index 0000000..357e716
--- /dev/null
+++ b/gnu/usr.bin/gdb/readline/funmap.c
@@ -0,0 +1,217 @@
+/* funmap.c -- attach names to functions. */
+
+/* Copyright (C) 1988,1989 Free Software Foundation, Inc.
+
+ This file is part of GNU Readline, a library for reading lines
+ of text with interactive input and history editing.
+
+ Readline is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 1, or (at your option) any
+ later version.
+
+ Readline is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Readline; see the file COPYING. If not, write to the Free
+ Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define STATIC_MALLOC
+#ifndef STATIC_MALLOC
+extern char *xmalloc (), *xrealloc ();
+#else
+static char *xmalloc (), *xrealloc ();
+#endif
+
+#ifndef FILE
+#include <stdio.h>
+#endif /* FILE */
+
+#include "readline.h"
+
+FUNMAP **funmap = (FUNMAP **)NULL;
+static int funmap_size = 0;
+
+static int just_testing_ar_tmp = 0;
+static int just_testing_ar_tmp_2 = 5;
+int foo_testing_ar;
+
+static int funmap_entry = 0;
+
+static FUNMAP default_funmap[] = {
+ { "beginning-of-line", rl_beg_of_line },
+ { "backward-char", rl_backward },
+ { "delete-char", rl_delete },
+ { "end-of-line", rl_end_of_line },
+ { "forward-char", rl_forward },
+ { "accept-line", rl_newline },
+ { "kill-line", rl_kill_line },
+ { "clear-screen", rl_clear_screen },
+ { "next-history", rl_get_next_history },
+ { "previous-history", rl_get_previous_history },
+ { "quoted-insert", rl_quoted_insert },
+ { "reverse-search-history", rl_reverse_search_history },
+ { "forward-search-history", rl_forward_search_history },
+ { "transpose-chars", rl_transpose_chars },
+ { "unix-line-discard", rl_unix_line_discard },
+ { "unix-word-rubout", rl_unix_word_rubout },
+ { "yank", rl_yank },
+ { "yank-pop", rl_yank_pop },
+ { "yank-nth-arg", rl_yank_nth_arg },
+ { "backward-delete-char", rl_rubout },
+ { "backward-word", rl_backward_word },
+ { "kill-word", rl_kill_word },
+ { "forward-word", rl_forward_word },
+ { "tab-insert", rl_tab_insert },
+ { "backward-kill-word", rl_backward_kill_word },
+ { "backward-kill-line", rl_backward_kill_line },
+ { "transpose-words", rl_transpose_words },
+ { "digit-argument", rl_digit_argument },
+ { "complete", rl_complete },
+ { "possible-completions", rl_possible_completions },
+ { "do-lowercase-version", rl_do_lowercase_version },
+ { "digit-argument", rl_digit_argument },
+ { "universal-argument", rl_universal_argument },
+ { "abort", rl_abort },
+ { "undo", rl_undo_command },
+ { "upcase-word", rl_upcase_word },
+ { "downcase-word", rl_downcase_word },
+ { "capitalize-word", rl_capitalize_word },
+ { "revert-line", rl_revert_line },
+ { "beginning-of-history", rl_beginning_of_history },
+ { "end-of-history", rl_end_of_history },
+ { "self-insert", rl_insert },
+ { "start-kbd-macro", rl_start_kbd_macro },
+ { "end-kbd-macro", rl_end_kbd_macro },
+ { "re-read-init-file", rl_re_read_init_file },
+#ifdef VI_MODE
+ { "vi-movement-mode", rl_vi_movement_mode },
+ { "vi-insertion-mode", rl_vi_insertion_mode },
+ { "vi-arg-digit", rl_vi_arg_digit },
+ { "vi-prev-word", rl_vi_prev_word },
+ { "vi-next-word", rl_vi_next_word },
+ { "vi-char-search", rl_vi_char_search },
+ { "vi-editing-mode", rl_vi_editing_mode },
+ { "vi-eof-maybe", rl_vi_eof_maybe },
+ { "vi-append-mode", rl_vi_append_mode },
+ { "vi-put", rl_vi_put },
+ { "vi-append-eol", rl_vi_append_eol },
+ { "vi-insert-beg", rl_vi_insert_beg },
+ { "vi-delete", rl_vi_delete },
+ { "vi-comment", rl_vi_comment },
+ { "vi-first-print", rl_vi_first_print },
+ { "vi-fword", rl_vi_fword },
+ { "vi-fWord", rl_vi_fWord },
+ { "vi-bword", rl_vi_bword },
+ { "vi-bWord", rl_vi_bWord },
+ { "vi-eword", rl_vi_eword },
+ { "vi-eWord", rl_vi_eWord },
+ { "vi-end-word", rl_vi_end_word },
+ { "vi-change-case", rl_vi_change_case },
+ { "vi-match", rl_vi_match },
+ { "vi-bracktype", rl_vi_bracktype },
+ { "vi-change-char", rl_vi_change_char },
+ { "vi-yank-arg", rl_vi_yank_arg },
+ { "vi-search", rl_vi_search },
+ { "vi-search-again", rl_vi_search_again },
+ { "vi-dosearch", rl_vi_dosearch },
+ { "vi-subst", rl_vi_subst },
+ { "vi-overstrike", rl_vi_overstrike },
+ { "vi-overstrike-delete", rl_vi_overstrike_delete },
+ { "vi-replace, ", rl_vi_replace },
+ { "vi-column", rl_vi_column },
+ { "vi-delete-to", rl_vi_delete_to },
+ { "vi-change-to", rl_vi_change_to },
+ { "vi-yank-to", rl_vi_yank_to },
+ { "vi-complete", rl_vi_complete },
+#endif /* VI_MODE */
+
+ {(char *)NULL, (Function *)NULL }
+};
+
+rl_add_funmap_entry (name, function)
+ char *name;
+ Function *function;
+{
+ if (funmap_entry + 2 >= funmap_size)
+ if (!funmap)
+ funmap = (FUNMAP **)xmalloc ((funmap_size = 80) * sizeof (FUNMAP *));
+ else
+ funmap =
+ (FUNMAP **)xrealloc (funmap, (funmap_size += 80) * sizeof (FUNMAP *));
+
+ funmap[funmap_entry] = (FUNMAP *)xmalloc (sizeof (FUNMAP));
+ funmap[funmap_entry]->name = name;
+ funmap[funmap_entry]->function = function;
+
+ funmap[++funmap_entry] = (FUNMAP *)NULL;
+}
+
+static int funmap_initialized = 0;
+
+/* Make the funmap contain all of the default entries. */
+rl_initialize_funmap ()
+{
+ register int i;
+
+ if (funmap_initialized)
+ return;
+
+ for (i = 0; default_funmap[i].name; i++)
+ rl_add_funmap_entry (default_funmap[i].name, default_funmap[i].function);
+
+ funmap_initialized = 1;
+}
+
+/* Things that mean `Control'. */
+char *possible_control_prefixes[] = {
+ "Control-", "C-", "CTRL-", (char *)NULL
+};
+
+char *possible_meta_prefixes[] = {
+ "Meta", "M-", (char *)NULL
+};
+
+#ifdef STATIC_MALLOC
+
+/* **************************************************************** */
+/* */
+/* xmalloc and xrealloc () */
+/* */
+/* **************************************************************** */
+
+static char *
+xmalloc (bytes)
+ int bytes;
+{
+ static memory_error_and_abort ();
+ char *temp = (char *)malloc (bytes);
+
+ if (!temp)
+ memory_error_and_abort ();
+ return (temp);
+}
+
+static char *
+xrealloc (pointer, bytes)
+ char *pointer;
+ int bytes;
+{
+ static memory_error_and_abort ();
+ char *temp = (char *)realloc (pointer, bytes);
+
+ if (!temp)
+ memory_error_and_abort ();
+ return (temp);
+}
+
+static
+memory_error_and_abort ()
+{
+ fprintf (stderr, "history: Out of virtual memory!\n");
+ abort ();
+}
+#endif /* STATIC_MALLOC */
diff --git a/gnu/usr.bin/gdb/readline/history.c b/gnu/usr.bin/gdb/readline/history.c
new file mode 100644
index 0000000..7087718
--- /dev/null
+++ b/gnu/usr.bin/gdb/readline/history.c
@@ -0,0 +1,1462 @@
+/* History.c -- standalone history library */
+
+/* Copyright (C) 1989 Free Software Foundation, Inc.
+
+ This file contains the GNU History Library (the Library), a set of
+ routines for managing the text of previously typed lines.
+
+ The Library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ The Library is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ The GNU General Public License is often shipped with GNU software, and
+ is generally kept in a file called COPYING or LICENSE. If you do not
+ have a copy of the license, write to the Free Software Foundation,
+ 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* The goal is to make the implementation transparent, so that you
+ don't have to know what data types are used, just what functions
+ you can call. I think I have done that. */
+
+/* Remove these declarations when we have a complete libgnu.a. */
+#define STATIC_MALLOC
+#ifndef STATIC_MALLOC
+extern char *xmalloc (), *xrealloc ();
+#else
+static char *xmalloc (), *xrealloc ();
+#endif
+
+#include <stdio.h>
+
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else
+#if defined (sparc) && defined (sun)
+#include <alloca.h>
+#else
+extern char *alloca ();
+#endif
+#endif
+
+#include "history.h"
+
+#ifndef savestring
+#define savestring(x) (char *)strcpy (xmalloc (1 + strlen (x)), (x))
+#endif
+
+#ifndef whitespace
+#define whitespace(c) (((c) == ' ') || ((c) == '\t'))
+#endif
+
+#ifndef digit
+#define digit(c) ((c) >= '0' && (c) <= '9')
+#endif
+
+#ifndef member
+#define member(c, s) ((c) ? index ((s), (c)) : 0)
+#endif
+
+/* **************************************************************** */
+/* */
+/* History functions */
+/* */
+/* **************************************************************** */
+
+/* An array of HIST_ENTRY. This is where we store the history. */
+static HIST_ENTRY **the_history = (HIST_ENTRY **)NULL;
+
+/* Non-zero means that we have enforced a limit on the amount of
+ history that we save. */
+static int history_stifled = 0;
+
+/* If HISTORY_STIFLED is non-zero, then this is the maximum number of
+ entries to remember. */
+static int max_input_history;
+
+/* The current location of the interactive history pointer. Just makes
+ life easier for outside callers. */
+static int history_offset = 0;
+
+/* The number of strings currently stored in the input_history list. */
+static int history_length = 0;
+
+/* The current number of slots allocated to the input_history. */
+static int history_size = 0;
+
+/* The number of slots to increase the_history by. */
+#define DEFAULT_HISTORY_GROW_SIZE 50
+
+/* The character that represents the start of a history expansion
+ request. This is usually `!'. */
+char history_expansion_char = '!';
+
+/* The character that invokes word substitution if found at the start of
+ a line. This is usually `^'. */
+char history_subst_char = '^';
+
+/* During tokenization, if this character is seen as the first character
+ of a word, then it, and all subsequent characters upto a newline are
+ ignored. For a Bourne shell, this should be '#'. Bash special cases
+ the interactive comment character to not be a comment delimiter. */
+char history_comment_char = '\0';
+
+/* The list of characters which inhibit the expansion of text if found
+ immediately following history_expansion_char. */
+char *history_no_expand_chars = " \t\n\r=";
+
+/* The logical `base' of the history array. It defaults to 1. */
+int history_base = 1;
+
+/* Begin a session in which the history functions might be used. This
+ initializes interactive variables. */
+void
+using_history ()
+{
+ history_offset = history_length;
+}
+
+/* Place STRING at the end of the history list. The data field
+ is set to NULL. */
+void
+add_history (string)
+ char *string;
+{
+ HIST_ENTRY *temp;
+
+ if (history_stifled && (history_length == max_input_history)) {
+ register int i;
+
+ /* If the history is stifled, and history_length is zero,
+ and it equals max_input_history, we don't save items. */
+ if (!history_length)
+ return;
+
+ /* If there is something in the slot, then remove it. */
+ if (the_history[0]) {
+ free (the_history[0]->line);
+ free (the_history[0]);
+ }
+
+ for (i = 0; i < history_length; i++)
+ the_history[i] = the_history[i + 1];
+
+ history_base++;
+
+ } else {
+
+ if (!history_size) {
+ the_history =
+ (HIST_ENTRY **)xmalloc ((history_size = DEFAULT_HISTORY_GROW_SIZE)
+ * sizeof (HIST_ENTRY *));
+ history_length = 1;
+
+ } else {
+ if (history_length == (history_size - 1)) {
+ the_history =
+ (HIST_ENTRY **)xrealloc (the_history,
+ ((history_size += DEFAULT_HISTORY_GROW_SIZE)
+ * sizeof (HIST_ENTRY *)));
+ }
+ history_length++;
+ }
+ }
+
+ temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
+ temp->line = savestring (string);
+ temp->data = (char *)NULL;
+
+ the_history[history_length] = (HIST_ENTRY *)NULL;
+ the_history[history_length - 1] = temp;
+}
+
+/* Make the history entry at WHICH have LINE and DATA. This returns
+ the old entry so you can dispose of the data. In the case of an
+ invalid WHICH, a NULL pointer is returned. */
+HIST_ENTRY *
+replace_history_entry (which, line, data)
+ int which;
+ char *line;
+ char *data;
+{
+ HIST_ENTRY *temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
+ HIST_ENTRY *old_value;
+
+ if (which >= history_length)
+ return ((HIST_ENTRY *)NULL);
+
+ old_value = the_history[which];
+
+ temp->line = savestring (line);
+ temp->data = data;
+ the_history[which] = temp;
+
+ return (old_value);
+}
+
+/* Returns the magic number which says what history element we are
+ looking at now. In this implementation, it returns history_offset. */
+int
+where_history ()
+{
+ return (history_offset);
+}
+
+/* Search the history for STRING, starting at history_offset.
+ If DIRECTION < 0, then the search is through previous entries,
+ else through subsequent. If the string is found, then
+ current_history () is the history entry, and the value of this function
+ is the offset in the line of that history entry that the string was
+ found in. Otherwise, nothing is changed, and a -1 is returned. */
+int
+history_search (string, direction)
+ char *string;
+ int direction;
+{
+ register int i = history_offset;
+ register int reverse = (direction < 0);
+ register char *line;
+ register int index;
+ int string_len = strlen (string);
+
+ /* Take care of trivial cases first. */
+
+ if (!history_length || (i == history_length) && !reverse)
+ return (-1);
+
+ if (reverse && (i == history_length))
+ i--;
+
+ while (1)
+ {
+ /* Search each line in the history list for STRING. */
+
+ /* At limit for direction? */
+ if ((reverse && i < 0) ||
+ (!reverse && i == history_length))
+ return (-1);
+
+ line = the_history[i]->line;
+ index = strlen (line);
+
+ /* If STRING is longer than line, no match. */
+ if (string_len > index)
+ goto next_line;
+
+ /* Do the actual search. */
+ if (reverse)
+ {
+ index -= string_len;
+
+ while (index >= 0)
+ {
+ if (strncmp (string, line + index, string_len) == 0)
+ {
+ history_offset = i;
+ return (index);
+ }
+ index--;
+ }
+ }
+ else
+ {
+ register int limit = (string_len - index) + 1;
+ index = 0;
+
+ while (index < limit)
+ {
+ if (strncmp (string, line + index, string_len) == 0)
+ {
+ history_offset = i;
+ return (index);
+ }
+ index++;
+ }
+ }
+ next_line:
+ if (reverse)
+ i--;
+ else
+ i++;
+ }
+}
+
+/* Remove history element WHICH from the history. The removed
+ element is returned to you so you can free the line, data,
+ and containing structure. */
+HIST_ENTRY *
+remove_history (which)
+ int which;
+{
+ HIST_ENTRY *return_value;
+
+ if (which >= history_length || !history_length)
+ return_value = (HIST_ENTRY *)NULL;
+ else
+ {
+ register int i;
+ return_value = the_history[which];
+
+ for (i = which; i < history_length; i++)
+ the_history[i] = the_history[i + 1];
+
+ history_length--;
+ }
+ return (return_value);
+}
+
+/* Stifle the history list, remembering only MAX number of lines. */
+void
+stifle_history (max)
+ int max;
+{
+ if (history_length > max)
+ {
+ register int i, j;
+
+ /* This loses because we cannot free the data. */
+ for (i = 0; i < (history_length - max); i++)
+ {
+ free (the_history[i]->line);
+ free (the_history[i]);
+ }
+ history_base = i;
+ for (j = 0, i = history_length - max; j < max; i++, j++)
+ the_history[j] = the_history[i];
+ the_history[j] = (HIST_ENTRY *)NULL;
+ history_length = j;
+ }
+ history_stifled = 1;
+ max_input_history = max;
+}
+
+/* Stop stifling the history. This returns the previous amount the history
+ was stifled by. The value is positive if the history was stifled, negative
+ if it wasn't. */
+int
+unstifle_history ()
+{
+ int result = max_input_history;
+ if (history_stifled)
+ {
+ result = - result;
+ history_stifled = 0;
+ }
+ return (result);
+}
+
+/* Return the string that should be used in the place of this
+ filename. This only matters when you don't specify the
+ filename to read_history (), or write_history (). */
+static char *
+history_filename (filename)
+ char *filename;
+{
+ char *return_val = filename ? savestring (filename) : (char *)NULL;
+
+ if (!return_val)
+ {
+ char *home = (char *)getenv ("HOME");
+ if (!home) home = ".";
+ return_val = (char *)xmalloc (2 + strlen (home) + strlen (".history"));
+ strcpy (return_val, home);
+ strcat (return_val, "/");
+ strcat (return_val, ".history");
+ }
+ return (return_val);
+}
+
+/* What to use until the line gets too big. */
+#define TYPICAL_LINE_SIZE 2048
+
+/* Add the contents of FILENAME to the history list, a line at a time.
+ If FILENAME is NULL, then read from ~/.history. Returns 0 if
+ successful, or errno if not. */
+int
+read_history (filename)
+ char *filename;
+{
+ char *input = history_filename (filename);
+ FILE *file = fopen (input, "r");
+ char *line = (char *)xmalloc (TYPICAL_LINE_SIZE);
+ int line_size = TYPICAL_LINE_SIZE;
+ int done = 0;
+
+ if (!file)
+ {
+ extern int errno;
+ free (line);
+ return (errno);
+ }
+
+ while (!done)
+ {
+ int c;
+ int i;
+
+ i = 0;
+ while (!(done = ((c = getc (file)) == EOF)))
+ {
+ if (c == '\n')
+ break;
+
+ line [i++] = c;
+ if (i == line_size)
+ line = (char *)xrealloc (line, line_size += TYPICAL_LINE_SIZE);
+ }
+ line[i] = '\0';
+ if (line[0])
+ add_history (line);
+ }
+ free (line);
+ fclose (file);
+ return (0);
+}
+
+/* Overwrite FILENAME with the current history. If FILENAME is NULL,
+ then write the history list to ~/.history. Values returned
+ are as in read_history ().*/
+int
+write_history (filename)
+ char *filename;
+{
+ extern int errno;
+ char *output = history_filename (filename);
+ FILE *file = fopen (output, "w");
+ register int i;
+
+ if (!file) return (errno);
+ if (!history_length) return (0);
+
+ for (i = 0; i < history_length; i++)
+ fprintf (file, "%s\n", the_history[i]->line);
+
+ fclose (file);
+ return (0);
+}
+
+/* Return the history entry at the current position, as determined by
+ history_offset. If there is no entry there, return a NULL pointer. */
+HIST_ENTRY *
+current_history ()
+{
+ if ((history_offset == history_length) || !the_history)
+ return ((HIST_ENTRY *)NULL);
+ else
+ return (the_history[history_offset]);
+}
+
+/* Back up history_offset to the previous history entry, and return
+ a pointer to that entry. If there is no previous entry then return
+ a NULL pointer. */
+HIST_ENTRY *
+previous_history ()
+{
+ if (!history_offset)
+ return ((HIST_ENTRY *)NULL);
+ else
+ return (the_history[--history_offset]);
+}
+
+/* Move history_offset forward to the next history entry, and return
+ a pointer to that entry. If there is no next entry then return a
+ NULL pointer. */
+HIST_ENTRY *
+next_history ()
+{
+ if (history_offset == history_length)
+ return ((HIST_ENTRY *)NULL);
+ else
+ return (the_history[++history_offset]);
+}
+
+/* Return the current history array. The caller has to be carefull, since this
+ is the actual array of data, and could be bashed or made corrupt easily.
+ The array is terminated with a NULL pointer. */
+HIST_ENTRY **
+history_list ()
+{
+ return (the_history);
+}
+
+/* Return the history entry which is logically at OFFSET in the history array.
+ OFFSET is relative to history_base. */
+HIST_ENTRY *
+history_get (offset)
+ int offset;
+{
+ int index = offset - history_base;
+
+ if (index >= history_length ||
+ index < 0 ||
+ !the_history)
+ return ((HIST_ENTRY *)NULL);
+ return (the_history[index]);
+}
+
+/* Search for STRING in the history list. DIR is < 0 for searching
+ backwards. POS is an absolute index into the history list at
+ which point to begin searching. */
+int
+history_search_pos (string, dir, pos)
+ char *string;
+ int dir, pos;
+{
+ int ret, old = where_history ();
+ history_set_pos (pos);
+ if (history_search (string, dir) == -1)
+ {
+ history_set_pos (old);
+ return (-1);
+ }
+ ret = where_history ();
+ history_set_pos (old);
+ return ret;
+}
+
+/* Make the current history item be the one at POS, an absolute index.
+ Returns zero if POS is out of range, else non-zero. */
+int
+history_set_pos (pos)
+ int pos;
+{
+ if (pos > history_length || pos < 0 || !the_history)
+ return (0);
+ history_offset = pos;
+ return (1);
+}
+
+
+/* **************************************************************** */
+/* */
+/* History Expansion */
+/* */
+/* **************************************************************** */
+
+/* Hairy history expansion on text, not tokens. This is of general
+ use, and thus belongs in this library. */
+
+/* The last string searched for in a !?string? search. */
+static char *search_string = (char *)NULL;
+
+/* Return the event specified at TEXT + OFFSET modifying OFFSET to
+ point to after the event specifier. Just a pointer to the history
+ line is returned; NULL is returned in the event of a bad specifier.
+ You pass STRING with *INDEX equal to the history_expansion_char that
+ begins this specification.
+ DELIMITING_QUOTE is a character that is allowed to end the string
+ specification for what to search for in addition to the normal
+ characters `:', ` ', `\t', `\n', and sometimes `?'.
+ So you might call this function like:
+ line = get_history_event ("!echo:p", &index, 0); */
+char *
+get_history_event (string, caller_index, delimiting_quote)
+ char *string;
+ int *caller_index;
+ int delimiting_quote;
+{
+ register int i = *caller_index;
+ int which, sign = 1;
+ HIST_ENTRY *entry;
+
+ /* The event can be specified in a number of ways.
+
+ !! the previous command
+ !n command line N
+ !-n current command-line minus N
+ !str the most recent command starting with STR
+ !?str[?]
+ the most recent command containing STR
+
+ All values N are determined via HISTORY_BASE. */
+
+ if (string[i] != history_expansion_char)
+ return ((char *)NULL);
+
+ /* Move on to the specification. */
+ i++;
+
+ /* Handle !! case. */
+ if (string[i] == history_expansion_char)
+ {
+ i++;
+ which = history_base + (history_length - 1);
+ *caller_index = i;
+ goto get_which;
+ }
+
+ /* Hack case of numeric line specification. */
+ read_which:
+ if (string[i] == '-')
+ {
+ sign = -1;
+ i++;
+ }
+
+ if (digit (string[i]))
+ {
+ int start = i;
+
+ /* Get the extent of the digits. */
+ for (; digit (string[i]); i++);
+
+ /* Get the digit value. */
+ sscanf (string + start, "%d", &which);
+
+ *caller_index = i;
+
+ if (sign < 0)
+ which = (history_length + history_base) - which;
+
+ get_which:
+ if (entry = history_get (which))
+ return (entry->line);
+
+ return ((char *)NULL);
+ }
+
+ /* This must be something to search for. If the spec begins with
+ a '?', then the string may be anywhere on the line. Otherwise,
+ the string must be found at the start of a line. */
+ {
+ int index;
+ char *temp;
+ int substring_okay = 0;
+
+ if (string[i] == '?')
+ {
+ substring_okay++;
+ i++;
+ }
+
+ for (index = i; string[i]; i++)
+ if (whitespace (string[i]) ||
+ string[i] == '\n' ||
+ string[i] == ':' ||
+ (substring_okay && string[i] == '?') ||
+ string[i] == delimiting_quote)
+ break;
+
+ temp = (char *)alloca (1 + (i - index));
+ strncpy (temp, &string[index], (i - index));
+ temp[i - index] = '\0';
+
+ if (string[i] == '?')
+ i++;
+
+ *caller_index = i;
+
+ search_again:
+
+ index = history_search (temp, -1);
+
+ if (index < 0)
+ search_lost:
+ {
+ history_offset = history_length;
+ return ((char *)NULL);
+ }
+
+ if (index == 0 || substring_okay ||
+ (strncmp (temp, the_history[history_offset]->line,
+ strlen (temp)) == 0))
+ {
+ search_won:
+ entry = current_history ();
+ history_offset = history_length;
+
+ /* If this was a substring search, then remember the string that
+ we matched for word substitution. */
+ if (substring_okay)
+ {
+ if (search_string)
+ free (search_string);
+ search_string = savestring (temp);
+ }
+
+ return (entry->line);
+ }
+
+ if (history_offset)
+ history_offset--;
+ else
+ goto search_lost;
+
+ goto search_again;
+ }
+}
+
+/* Expand the string STRING, placing the result into OUTPUT, a pointer
+ to a string. Returns:
+
+ 0) If no expansions took place (or, if the only change in
+ the text was the de-slashifying of the history expansion
+ character)
+ 1) If expansions did take place
+ -1) If there was an error in expansion.
+
+ If an error ocurred in expansion, then OUTPUT contains a descriptive
+ error message. */
+int
+history_expand (string, output)
+ char *string;
+ char **output;
+{
+ register int j, l = strlen (string);
+ int i, word_spec_error = 0;
+ int cc, modified = 0;
+ char *word_spec, *event;
+ int starting_index, only_printing = 0, substitute_globally = 0;
+
+ char *get_history_word_specifier (), *rindex ();
+
+ /* The output string, and its length. */
+ int len = 0;
+ char *result = (char *)NULL;
+
+ /* Used in add_string; */
+ char *temp, tt[2], tbl[3];
+
+ /* Prepare the buffer for printing error messages. */
+ result = (char *)xmalloc (len = 255);
+
+ result[0] = tt[1] = tbl[2] = '\0';
+ tbl[0] = '\\';
+ tbl[1] = history_expansion_char;
+
+ /* Grovel the string. Only backslash can quote the history escape
+ character. We also handle arg specifiers. */
+
+ /* Before we grovel forever, see if the history_expansion_char appears
+ anywhere within the text. */
+
+ /* The quick substitution character is a history expansion all right. That
+ is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact,
+ that is the substitution that we do. */
+ if (string[0] == history_subst_char)
+ {
+ char *format_string = (char *)alloca (10 + strlen (string));
+
+ sprintf (format_string, "%c%c:s%s",
+ history_expansion_char, history_expansion_char,
+ string);
+ string = format_string;
+ l += 4;
+ goto grovel;
+ }
+
+ /* If not quick substitution, still maybe have to do expansion. */
+
+ /* `!' followed by one of the characters in history_no_expand_chars
+ is NOT an expansion. */
+ for (i = 0; string[i]; i++)
+ if (string[i] == history_expansion_char)
+ if (!string[i + 1] || member (string[i + 1], history_no_expand_chars))
+ continue;
+ else
+ goto grovel;
+
+ free (result);
+ *output = savestring (string);
+ return (0);
+
+ grovel:
+
+ for (i = j = 0; i < l; i++)
+ {
+ int tchar = string[i];
+ if (tchar == history_expansion_char)
+ tchar = -3;
+
+ switch (tchar)
+ {
+ case '\\':
+ if (string[i + 1] == history_expansion_char)
+ {
+ i++;
+ temp = tbl;
+ goto do_add;
+ }
+ else
+ goto add_char;
+
+ /* case history_expansion_char: */
+ case -3:
+ starting_index = i + 1;
+ cc = string[i + 1];
+
+ /* If the history_expansion_char is followed by one of the
+ characters in history_no_expand_chars, then it is not a
+ candidate for expansion of any kind. */
+ if (member (cc, history_no_expand_chars))
+ goto add_char;
+
+ /* There is something that is listed as a `word specifier' in csh
+ documentation which means `the expanded text to this point'.
+ That is not a word specifier, it is an event specifier. */
+
+ if (cc == '#')
+ goto hack_pound_sign;
+
+ /* If it is followed by something that starts a word specifier,
+ then !! is implied as the event specifier. */
+
+ if (member (cc, ":$*%^"))
+ {
+ char fake_s[2];
+ int fake_i = 0;
+ i++;
+ fake_s[0] = fake_s[1] = history_expansion_char;
+ fake_s[2] = '\0';
+ event = get_history_event (fake_s, &fake_i);
+ }
+ else
+ {
+ int quoted_search_delimiter = 0;
+
+ /* If the character before this `!' is a double or single
+ quote, then this expansion takes place inside of the
+ quoted string. If we have to search for some text ("!foo"),
+ allow the delimiter to end the search string. */
+ if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
+ quoted_search_delimiter = string[i - 1];
+
+ event = get_history_event (string, &i, quoted_search_delimiter);
+ }
+
+ if (!event)
+ event_not_found:
+ {
+ int l = 1 + (i - starting_index);
+
+ temp = (char *)alloca (1 + l);
+ strncpy (temp, string + starting_index, l);
+ temp[l - 1] = 0;
+ sprintf (result, "%s: %s.", temp,
+ word_spec_error ? "Bad word specifier" : "Event not found");
+ error_exit:
+ *output = result;
+ return (-1);
+ }
+
+ /* If a word specifier is found, then do what that requires. */
+ starting_index = i;
+
+ word_spec = get_history_word_specifier (string, event, &i);
+
+ /* There is no such thing as a `malformed word specifier'. However,
+ it is possible for a specifier that has no match. In that case,
+ we complain. */
+ if (word_spec == (char *)-1)
+ bad_word_spec:
+ {
+ word_spec_error++;
+ goto event_not_found;
+ }
+
+ /* If no word specifier, than the thing of interest was the event. */
+ if (!word_spec)
+ temp = event;
+ else
+ {
+ temp = (char *)alloca (1 + strlen (word_spec));
+ strcpy (temp, word_spec);
+ free (word_spec);
+ }
+
+ /* Perhaps there are other modifiers involved. Do what they say. */
+
+ hack_specials:
+
+ if (string[i] == ':')
+ {
+ char *tstr;
+
+ switch (string[i + 1])
+ {
+ /* :p means make this the last executed line. So we
+ return an error state after adding this line to the
+ history. */
+ case 'p':
+ only_printing++;
+ goto next_special;
+
+ /* :t discards all but the last part of the pathname. */
+ case 't':
+ tstr = rindex (temp, '/');
+ if (tstr)
+ temp = ++tstr;
+ goto next_special;
+
+ /* :h discards the last part of a pathname. */
+ case 'h':
+ tstr = rindex (temp, '/');
+ if (tstr)
+ *tstr = '\0';
+ goto next_special;
+
+ /* :r discards the suffix. */
+ case 'r':
+ tstr = rindex (temp, '.');
+ if (tstr)
+ *tstr = '\0';
+ goto next_special;
+
+ /* :e discards everything but the suffix. */
+ case 'e':
+ tstr = rindex (temp, '.');
+ if (tstr)
+ temp = tstr;
+ goto next_special;
+
+ /* :s/this/that substitutes `this' for `that'. */
+ /* :gs/this/that substitutes `this' for `that' globally. */
+ case 'g':
+ if (string[i + 2] == 's')
+ {
+ i++;
+ substitute_globally = 1;
+ goto substitute;
+ }
+ else
+
+ case 's':
+ substitute:
+ {
+ char *this, *that, *new_event;
+ int delimiter = 0;
+ int si, l_this, l_that, l_temp = strlen (temp);
+
+ if (i + 2 < strlen (string))
+ delimiter = string[i + 2];
+
+ if (!delimiter)
+ break;
+
+ i += 3;
+
+ /* Get THIS. */
+ for (si = i; string[si] && string[si] != delimiter; si++);
+ l_this = (si - i);
+ this = (char *)alloca (1 + l_this);
+ strncpy (this, string + i, l_this);
+ this[l_this] = '\0';
+
+ i = si;
+ if (string[si])
+ i++;
+
+ /* Get THAT. */
+ for (si = i; string[si] && string[si] != delimiter; si++);
+ l_that = (si - i);
+ that = (char *)alloca (1 + l_that);
+ strncpy (that, string + i, l_that);
+ that[l_that] = '\0';
+
+ i = si;
+ if (string[si]) i++;
+
+ /* Ignore impossible cases. */
+ if (l_this > l_temp)
+ goto cant_substitute;
+
+ /* Find the first occurrence of THIS in TEMP. */
+ si = 0;
+ for (; (si + l_this) <= l_temp; si++)
+ if (strncmp (temp + si, this, l_this) == 0)
+ {
+ new_event =
+ (char *)alloca (1 + (l_that - l_this) + l_temp);
+ strncpy (new_event, temp, si);
+ strncpy (new_event + si, that, l_that);
+ strncpy (new_event + si + l_that,
+ temp + si + l_this,
+ l_temp - (si + l_this));
+ new_event[(l_that - l_this) + l_temp] = '\0';
+ temp = new_event;
+
+ if (substitute_globally)
+ {
+ si += l_that;
+ l_temp = strlen (temp);
+ substitute_globally++;
+ continue;
+ }
+
+ goto hack_specials;
+ }
+
+ cant_substitute:
+
+ if (substitute_globally > 1)
+ {
+ substitute_globally = 0;
+ goto hack_specials;
+ }
+
+ goto event_not_found;
+ }
+
+ /* :# is the line so far. Note that we have to
+ alloca () it since RESULT could be realloc ()'ed
+ below in add_string. */
+ case '#':
+ hack_pound_sign:
+ if (result)
+ {
+ temp = (char *)alloca (1 + strlen (result));
+ strcpy (temp, result);
+ }
+ else
+ temp = "";
+
+ next_special:
+ i += 2;
+ goto hack_specials;
+ }
+
+ }
+ /* Believe it or not, we have to back the pointer up by one. */
+ --i;
+ goto add_string;
+
+ /* A regular character. Just add it to the output string. */
+ default:
+ add_char:
+ tt[0] = string[i];
+ temp = tt;
+ goto do_add;
+
+ add_string:
+ modified++;
+
+ do_add:
+ j += strlen (temp);
+ while (j > len)
+ result = (char *)xrealloc (result, (len += 255));
+
+ strcpy (result + (j - strlen (temp)), temp);
+ }
+ }
+
+ *output = result;
+
+ if (only_printing)
+ {
+ add_history (result);
+ return (-1);
+ }
+
+ return (modified != 0);
+}
+
+/* Return a consed string which is the word specified in SPEC, and found
+ in FROM. NULL is returned if there is no spec. -1 is returned if
+ the word specified cannot be found. CALLER_INDEX is the offset in
+ SPEC to start looking; it is updated to point to just after the last
+ character parsed. */
+char *
+get_history_word_specifier (spec, from, caller_index)
+ char *spec, *from;
+ int *caller_index;
+{
+ register int i = *caller_index;
+ int first, last;
+ int expecting_word_spec = 0;
+ char *history_arg_extract ();
+
+ /* The range of words to return doesn't exist yet. */
+ first = last = 0;
+
+ /* If we found a colon, then this *must* be a word specification. If
+ it isn't, then it is an error. */
+ if (spec[i] == ':')
+ i++, expecting_word_spec++;
+
+ /* Handle special cases first. */
+
+ /* `%' is the word last searched for. */
+ if (spec[i] == '%')
+ {
+ *caller_index = i + 1;
+ if (search_string)
+ return (savestring (search_string));
+ else
+ return (savestring (""));
+ }
+
+ /* `*' matches all of the arguments, but not the command. */
+ if (spec[i] == '*')
+ {
+ *caller_index = i + 1;
+ return (history_arg_extract (1, '$', from));
+ }
+
+ /* `$' is last arg. */
+ if (spec[i] == '$')
+ {
+ *caller_index = i + 1;
+ return (history_arg_extract ('$', '$', from));
+ }
+
+ /* Try to get FIRST and LAST figured out. */
+ if (spec[i] == '-' || spec[i] == '^')
+ {
+ first = 1;
+ goto get_last;
+ }
+
+ get_first:
+ if (digit (spec[i]) && expecting_word_spec)
+ {
+ sscanf (spec + i, "%d", &first);
+ for (; digit (spec[i]); i++);
+ }
+ else
+ return ((char *)NULL);
+
+ get_last:
+ if (spec[i] == '^')
+ {
+ i++;
+ last = 1;
+ goto get_args;
+ }
+
+ if (spec[i] != '-')
+ {
+ last = first;
+ goto get_args;
+ }
+
+ i++;
+
+ if (digit (spec[i]))
+ {
+ sscanf (spec + i, "%d", &last);
+ for (; digit (spec[i]); i++);
+ }
+ else
+ if (spec[i] == '$')
+ {
+ i++;
+ last = '$';
+ }
+
+ get_args:
+ {
+ char *result = (char *)NULL;
+
+ *caller_index = i;
+
+ if (last >= first)
+ result = history_arg_extract (first, last, from);
+
+ if (result)
+ return (result);
+ else
+ return ((char *)-1);
+ }
+}
+
+/* Extract the args specified, starting at FIRST, and ending at LAST.
+ The args are taken from STRING. */
+char *
+history_arg_extract (first, last, string)
+ int first, last;
+ char *string;
+{
+ register int i, len;
+ char *result = (char *)NULL;
+ int size = 0, offset = 0;
+
+ char **history_tokenize (), **list;
+
+ if (!(list = history_tokenize (string)))
+ return ((char *)NULL);
+
+ for (len = 0; list[len]; len++);
+
+ if (last == '$')
+ last = len - 1;
+
+ if (first == '$')
+ first = len - 1;
+
+ last++;
+
+ if (first > len || last > len)
+ result = ((char *)NULL);
+ else {
+ for (i = first; i < last; i++)
+ {
+ int l = strlen (list[i]);
+
+ if (!result)
+ result = (char *)xmalloc ((size = (2 + l)));
+ else
+ result = (char *)xrealloc (result, (size += (2 + l)));
+ strcpy (result + offset, list[i]);
+ offset += l;
+ if (i + 1 < last)
+ {
+ strcpy (result + offset, " ");
+ offset++;
+ }
+ }
+ }
+
+ for (i = 0; i < len; i++)
+ free (list[i]);
+
+ free (list);
+
+ return (result);
+}
+
+#define slashify_in_quotes "\\`\"$"
+
+/* Return an array of tokens, much as the shell might. The tokens are
+ parsed out of STRING. */
+char **
+history_tokenize (string)
+ char *string;
+{
+ char **result = (char **)NULL;
+ register int i, start, result_index, size;
+ int len;
+
+ i = result_index = size = 0;
+
+ /* Get a token, and stuff it into RESULT. The tokens are split
+ exactly where the shell would split them. */
+ get_token:
+
+ /* Skip leading whitespace. */
+ for (; string[i] && whitespace(string[i]); i++);
+
+ start = i;
+
+ if (!string[i] || string[i] == history_comment_char)
+ return (result);
+
+ if (member (string[i], "()\n")) {
+ i++;
+ goto got_token;
+ }
+
+ if (member (string[i], "<>;&|")) {
+ int peek = string[i + 1];
+
+ if (peek == string[i]) {
+ if (peek == '<') {
+ if (string[1 + 2] == '-')
+ i++;
+ i += 2;
+ goto got_token;
+ }
+
+ if (member (peek, ">:&|")) {
+ i += 2;
+ goto got_token;
+ }
+ } else {
+ if ((peek == '&' &&
+ (string[i] == '>' || string[i] == '<')) ||
+ ((peek == '>') &&
+ (string[i] == '&'))) {
+ i += 2;
+ goto got_token;
+ }
+ }
+ i++;
+ goto got_token;
+ }
+
+ /* Get word from string + i; */
+ {
+ int delimiter = 0;
+
+ if (member (string[i], "\"'`"))
+ delimiter = string[i++];
+
+ for (;string[i]; i++) {
+
+ if (string[i] == '\\') {
+
+ if (string[i + 1] == '\n') {
+ i++;
+ continue;
+ } else {
+ if (delimiter != '\'')
+ if ((delimiter != '"') ||
+ (member (string[i], slashify_in_quotes))) {
+ i++;
+ continue;
+ }
+ }
+ }
+
+ if (delimiter && string[i] == delimiter) {
+ delimiter = 0;
+ continue;
+ }
+
+ if (!delimiter && (member (string[i], " \t\n;&()|<>")))
+ goto got_token;
+
+ if (!delimiter && member (string[i], "\"'`")) {
+ delimiter = string[i];
+ continue;
+ }
+ }
+ got_token:
+
+ len = i - start;
+ if (result_index + 2 >= size) {
+ if (!size)
+ result = (char **)xmalloc ((size = 10) * (sizeof (char *)));
+ else
+ result =
+ (char **)xrealloc (result, ((size += 10) * (sizeof (char *))));
+ }
+ result[result_index] = (char *)xmalloc (1 + len);
+ strncpy (result[result_index], string + start, len);
+ result[result_index][len] = '\0';
+ result_index++;
+ result[result_index] = (char *)NULL;
+ }
+ if (string[i])
+ goto get_token;
+
+ return (result);
+}
+
+#ifdef STATIC_MALLOC
+
+/* **************************************************************** */
+/* */
+/* xmalloc and xrealloc () */
+/* */
+/* **************************************************************** */
+
+static char *
+xmalloc (bytes)
+ int bytes;
+{
+ static memory_error_and_abort ();
+ char *temp = (char *)malloc (bytes);
+
+ if (!temp)
+ memory_error_and_abort ();
+ return (temp);
+}
+
+static char *
+xrealloc (pointer, bytes)
+ char *pointer;
+ int bytes;
+{
+ static memory_error_and_abort ();
+ char *temp = (char *)realloc (pointer, bytes);
+
+ if (!temp)
+ memory_error_and_abort ();
+ return (temp);
+}
+
+static
+memory_error_and_abort ()
+{
+ fprintf (stderr, "history: Out of virtual memory!\n");
+ abort ();
+}
+#endif /* STATIC_MALLOC */
+
+
+/* **************************************************************** */
+/* */
+/* Test Code */
+/* */
+/* **************************************************************** */
+#ifdef TEST
+main ()
+{
+ char line[1024], *t;
+ int done = 0;
+
+ line[0] = 0;
+
+ while (!done)
+ {
+ fprintf (stdout, "history%% ");
+ t = gets (line);
+
+ if (!t)
+ strcpy (line, "quit");
+
+ if (line[0])
+ {
+ char *expansion;
+ int result;
+
+ using_history ();
+
+ result = history_expand (line, &expansion);
+ strcpy (line, expansion);
+ free (expansion);
+ if (result)
+ fprintf (stderr, "%s\n", line);
+
+ if (result < 0)
+ continue;
+
+ add_history (line);
+ }
+
+ if (strcmp (line, "quit") == 0) done = 1;
+ if (strcmp (line, "save") == 0) write_history (0);
+ if (strcmp (line, "read") == 0) read_history (0);
+ if (strcmp (line, "list") == 0)
+ {
+ register HIST_ENTRY **the_list = history_list ();
+ register int i;
+
+ if (the_list)
+ for (i = 0; the_list[i]; i++)
+ fprintf (stdout, "%d: %s\n", i + history_base, the_list[i]->line);
+ }
+ if (strncmp (line, "delete", strlen ("delete")) == 0)
+ {
+ int which;
+ if ((sscanf (line + strlen ("delete"), "%d", &which)) == 1)
+ {
+ HIST_ENTRY *entry = remove_history (which);
+ if (!entry)
+ fprintf (stderr, "No such entry %d\n", which);
+ else
+ {
+ free (entry->line);
+ free (entry);
+ }
+ }
+ else
+ {
+ fprintf (stderr, "non-numeric arg given to `delete'\n");
+ }
+ }
+ }
+}
+
+#endif /* TEST */
+
+/*
+* Local variables:
+* compile-command: "gcc -g -DTEST -o history history.c"
+* end:
+*/
diff --git a/gnu/usr.bin/gdb/readline/history.h b/gnu/usr.bin/gdb/readline/history.h
new file mode 100644
index 0000000..0bac209
--- /dev/null
+++ b/gnu/usr.bin/gdb/readline/history.h
@@ -0,0 +1,108 @@
+/* History.h -- the names of functions that you can call in history. */
+
+typedef struct _hist_entry {
+ char *line;
+ char *data;
+} HIST_ENTRY;
+
+/* For convenience only. You set this when interpreting history commands.
+ It is the logical offset of the first history element. */
+extern int history_base;
+
+/* Begin a session in which the history functions might be used. This
+ just initializes the interactive variables. */
+extern void using_history ();
+
+/* Place STRING at the end of the history list.
+ The associated data field (if any) is set to NULL. */
+extern void add_history ();
+
+/* Returns the number which says what history element we are now
+ looking at. */
+extern int where_history ();
+
+/* Set the position in the history list to POS. */
+int history_set_pos ();
+
+/* Search for STRING in the history list, starting at POS, an
+ absolute index into the list. DIR, if negative, says to search
+ backwards from POS, else forwards.
+ Returns the absolute index of the history element where STRING
+ was found, or -1 otherwise. */
+extern int history_search_pos ();
+
+/* A reasonably useless function, only here for completeness. WHICH
+ is the magic number that tells us which element to delete. The
+ elements are numbered from 0. */
+extern HIST_ENTRY *remove_history ();
+
+/* Stifle the history list, remembering only MAX number of entries. */
+extern void stifle_history ();
+
+/* Stop stifling the history. This returns the previous amount the
+ history was stifled by. The value is positive if the history was
+ stifled, negative if it wasn't. */
+extern int unstifle_history ();
+
+/* Add the contents of FILENAME to the history list, a line at a time.
+ If FILENAME is NULL, then read from ~/.history. Returns 0 if
+ successful, or errno if not. */
+extern int read_history ();
+
+/* Append the current history to FILENAME. If FILENAME is NULL,
+ then append the history list to ~/.history. Values returned
+ are as in read_history (). */
+extern int write_history ();
+
+
+/* Make the history entry at WHICH have LINE and DATA. This returns
+ the old entry so you can dispose of the data. In the case of an
+ invalid WHICH, a NULL pointer is returned. */
+extern HIST_ENTRY *replace_history_entry ();
+
+/* Return the history entry at the current position, as determined by
+ history_offset. If there is no entry there, return a NULL pointer. */
+HIST_ENTRY *current_history ();
+
+/* Back up history_offset to the previous history entry, and return
+ a pointer to that entry. If there is no previous entry, return
+ a NULL pointer. */
+extern HIST_ENTRY *previous_history ();
+
+/* Move history_offset forward to the next item in the input_history,
+ and return the a pointer to that entry. If there is no next entry,
+ return a NULL pointer. */
+extern HIST_ENTRY *next_history ();
+
+/* Return a NULL terminated array of HIST_ENTRY which is the current input
+ history. Element 0 of this list is the beginning of time. If there
+ is no history, return NULL. */
+extern HIST_ENTRY **history_list ();
+
+/* Search the history for STRING, starting at history_offset.
+ If DIRECTION < 0, then the search is through previous entries,
+ else through subsequent. If the string is found, then
+ current_history () is the history entry, and the value of this function
+ is the offset in the line of that history entry that the string was
+ found in. Otherwise, nothing is changed, and a -1 is returned. */
+extern int history_search ();
+
+/* Expand the string STRING, placing the result into OUTPUT, a pointer
+ to a string. Returns:
+
+ 0) If no expansions took place (or, if the only change in
+ the text was the de-slashifying of the history expansion
+ character)
+ 1) If expansions did take place
+ -1) If there was an error in expansion.
+
+ If an error ocurred in expansion, then OUTPUT contains a descriptive
+ error message. */
+extern int history_expand ();
+
+/* Extract a string segment consisting of the FIRST through LAST
+ arguments present in STRING. Arguments are broken up as in
+ the shell. */
+extern char *history_arg_extract ();
+
+
diff --git a/gnu/usr.bin/gdb/readline/keymaps.c b/gnu/usr.bin/gdb/readline/keymaps.c
new file mode 100644
index 0000000..e0c5e39
--- /dev/null
+++ b/gnu/usr.bin/gdb/readline/keymaps.c
@@ -0,0 +1,172 @@
+/* keymaps.c -- Functions and keymaps for the GNU Readline library. */
+
+/* Copyright (C) 1988,1989 Free Software Foundation, Inc.
+
+ This file is part of GNU Readline, a library for reading lines
+ of text with interactive input and history editing.
+
+ Readline is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 1, or (at your option) any
+ later version.
+
+ Readline is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Readline; see the file COPYING. If not, write to the Free
+ Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "keymaps.h"
+#include "emacs_keymap.c"
+
+#ifdef VI_MODE
+#include "vi_keymap.c"
+#endif
+
+/* Remove these declarations when we have a complete libgnu.a. */
+#define STATIC_MALLOC
+#ifndef STATIC_MALLOC
+extern char *xmalloc (), *xrealloc ();
+#else
+static char *xmalloc (), *xrealloc ();
+#endif
+
+/* **************************************************************** */
+/* */
+/* Functions for manipulating Keymaps. */
+/* */
+/* **************************************************************** */
+
+
+/* Return a new, empty keymap.
+ Free it with free() when you are done. */
+Keymap
+rl_make_bare_keymap ()
+{
+ register int i;
+ Keymap keymap = (Keymap)xmalloc (128 * sizeof (KEYMAP_ENTRY));
+
+ for (i = 0; i < 128; i++)
+ {
+ keymap[i].type = ISFUNC;
+ keymap[i].function = (Function *)NULL;
+ }
+
+ for (i = 'A'; i < ('Z' + 1); i++)
+ {
+ keymap[i].type = ISFUNC;
+ keymap[i].function = rl_do_lowercase_version;
+ }
+
+ return (keymap);
+}
+
+/* Return a new keymap which is a copy of MAP. */
+Keymap
+rl_copy_keymap (map)
+ Keymap map;
+{
+ register int i;
+ Keymap temp = rl_make_bare_keymap ();
+
+ for (i = 0; i < 128; i++)
+ {
+ temp[i].type = map[i].type;
+ temp[i].function = map[i].function;
+ }
+ return (temp);
+}
+
+/* Return a new keymap with the printing characters bound to rl_insert,
+ the uppercase Meta characters bound to run their lowercase equivalents,
+ and the Meta digits bound to produce numeric arguments. */
+Keymap
+rl_make_keymap ()
+{
+ extern rl_insert (), rl_rubout (), rl_do_lowercase_version ();
+ extern rl_digit_argument ();
+ register int i;
+ Keymap newmap;
+
+ newmap = rl_make_bare_keymap ();
+
+ /* All printing characters are self-inserting. */
+ for (i = ' '; i < 126; i++)
+ newmap[i].function = rl_insert;
+
+ newmap[TAB].function = rl_insert;
+ newmap[RUBOUT].function = rl_rubout;
+
+ return (newmap);
+}
+
+/* Free the storage associated with MAP. */
+rl_discard_keymap (map)
+ Keymap (map);
+{
+ int i;
+
+ if (!map)
+ return;
+
+ for (i = 0; i < 128; i++)
+ {
+ switch (map[i].type)
+ {
+ case ISFUNC:
+ break;
+
+ case ISKMAP:
+ rl_discard_keymap ((Keymap)map[i].function);
+ break;
+
+ case ISMACR:
+ free ((char *)map[i].function);
+ break;
+ }
+ }
+}
+
+#ifdef STATIC_MALLOC
+
+/* **************************************************************** */
+/* */
+/* xmalloc and xrealloc () */
+/* */
+/* **************************************************************** */
+
+static char *
+xmalloc (bytes)
+ int bytes;
+{
+ static memory_error_and_abort ();
+ char *temp = (char *)malloc (bytes);
+
+ if (!temp)
+ memory_error_and_abort ();
+ return (temp);
+}
+
+static char *
+xrealloc (pointer, bytes)
+ char *pointer;
+ int bytes;
+{
+ static memory_error_and_abort ();
+ char *temp = (char *)realloc (pointer, bytes);
+
+ if (!temp)
+ memory_error_and_abort ();
+ return (temp);
+}
+
+static
+memory_error_and_abort ()
+{
+ fprintf (stderr, "readline: Out of virtual memory!\n");
+ abort ();
+}
+#endif /* STATIC_MALLOC */
diff --git a/gnu/usr.bin/gdb/readline/keymaps.h b/gnu/usr.bin/gdb/readline/keymaps.h
new file mode 100644
index 0000000..3c577b3
--- /dev/null
+++ b/gnu/usr.bin/gdb/readline/keymaps.h
@@ -0,0 +1,53 @@
+/* keymaps.h -- Manipulation of readline keymaps. */
+
+#ifndef _KEYMAPS_H_
+#define _KEYMAPS_H_
+
+#include <readline/chardefs.h>
+
+#ifndef __FUNCTION_DEF
+typedef int Function ();
+#define __FUNCTION_DEF
+#endif
+
+/* A keymap contains one entry for each key in the ASCII set.
+ Each entry consists of a type and a pointer.
+ POINTER is the address of a function to run, or the
+ address of a keymap to indirect through.
+ TYPE says which kind of thing POINTER is. */
+typedef struct _keymap_entry {
+ char type;
+ Function *function;
+} KEYMAP_ENTRY;
+
+/* I wanted to make the above structure contain a union of:
+ union { Function *function; struct _keymap_entry *keymap; } value;
+ but this made it impossible for me to create a static array.
+ Maybe I need C lessons. */
+
+typedef KEYMAP_ENTRY KEYMAP_ENTRY_ARRAY[128];
+typedef KEYMAP_ENTRY *Keymap;
+
+/* The values that TYPE can have in a keymap entry. */
+#define ISFUNC 0
+#define ISKMAP 1
+#define ISMACR 2
+
+extern KEYMAP_ENTRY_ARRAY emacs_standard_keymap, emacs_meta_keymap, emacs_ctlx_keymap;
+extern KEYMAP_ENTRY_ARRAY vi_insertion_keymap, vi_movement_keymap;
+
+/* Return a new, empty keymap.
+ Free it with free() when you are done. */
+Keymap rl_make_bare_keymap ();
+
+/* Return a new keymap which is a copy of MAP. */
+Keymap rl_copy_keymap ();
+
+/* Return a new keymap with the printing characters bound to rl_insert,
+ the lowercase Meta characters bound to run their equivalents, and
+ the Meta digits bound to produce numeric arguments. */
+Keymap rl_make_keymap ();
+
+#endif /* _KEYMAPS_H_ */
+
+
diff --git a/gnu/usr.bin/gdb/readline/readline.c b/gnu/usr.bin/gdb/readline/readline.c
new file mode 100644
index 0000000..3e8f9a3
--- /dev/null
+++ b/gnu/usr.bin/gdb/readline/readline.c
@@ -0,0 +1,5557 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)readline.c 6.4 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* readline.c -- a general facility for reading lines of input
+ with emacs style editing and completion. */
+
+/* Copyright (C) 1987,1989 Free Software Foundation, Inc.
+
+ This file contains the Readline Library (the Library), a set of
+ routines for providing Emacs style line input to programs that ask
+ for it.
+
+ The Library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ The Library is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ The GNU General Public License is often shipped with GNU software, and
+ is generally kept in a file called COPYING or LICENSE. If you do not
+ have a copy of the license, write to the Free Software Foundation,
+ 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Remove these declarations when we have a complete libgnu.a. */
+#define STATIC_MALLOC
+#ifndef STATIC_MALLOC
+extern char *xmalloc (), *xrealloc ();
+#else
+static char *xmalloc (), *xrealloc ();
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <signal.h>
+#include <string.h>
+
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else
+#if defined (sparc) && defined (sun)
+#include <alloca.h>
+#endif
+#endif
+
+#define NEW_TTY_DRIVER
+#if defined (SYSV) || defined (hpux)
+#undef NEW_TTY_DRIVER
+#include <termio.h>
+#else
+#include <sgtty.h>
+#endif
+
+#include <errno.h>
+extern int errno;
+
+#include <setjmp.h>
+
+/* These next are for filename completion. Perhaps this belongs
+ in a different place. */
+#include <sys/stat.h>
+
+#include <pwd.h>
+#ifdef SYSV
+struct passwd *getpwuid (), *getpwent ();
+#endif
+
+#define HACK_TERMCAP_MOTION
+
+#ifndef SYSV
+#include <sys/dir.h>
+#else /* SYSV */
+#ifdef hpux
+#include <ndir.h>
+#else
+#include <dirent.h>
+#define direct dirent
+#define d_namlen d_reclen
+#endif /* hpux */
+#endif /* SYSV */
+
+/* Some standard library routines. */
+#include "readline.h"
+#include "history.h"
+
+#ifndef digit
+#define digit(c) ((c) >= '0' && (c) <= '9')
+#endif
+
+#ifndef isletter
+#define isletter(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
+#endif
+
+#ifndef digit_value
+#define digit_value(c) ((c) - '0')
+#endif
+
+#ifndef member
+char *index ();
+#define member(c, s) ((c) ? index ((s), (c)) : 0)
+#endif
+
+#ifndef isident
+#define isident(c) ((isletter(c) || digit(c) || c == '_'))
+#endif
+
+#ifndef exchange
+#define exchange(x, y) {int temp = x; x = y; y = temp;}
+#endif
+
+static update_line ();
+static void output_character_function ();
+static delete_chars ();
+static start_insert ();
+static end_insert ();
+
+/* This typedef is equivalant to the one for Function; it allows us
+ to say SigHandler *foo = signal (SIGKILL, SIG_IGN); */
+typedef void SigHandler ();
+
+#ifdef SIGWINCH
+static void rl_handle_sigwinch ();
+static SigHandler *old_sigwinch = (SigHandler *)NULL;
+#endif
+
+/* If on, then readline handles signals in a way that doesn't screw. */
+/* #define HANDLE_SIGNALS */
+
+#if defined (SYSV)
+#ifdef HANDLE_SIGNALS
+#undef HANDLE_SIGNALS
+#endif
+#endif
+
+/* Stupid comparison routine for qsort () ing strings. */
+static int
+compare_strings (s1, s2)
+ char **s1, **s2;
+{
+ return (strcmp (*s1, *s2));
+}
+
+
+/* **************************************************************** */
+/* */
+/* Line editing input utility */
+/* */
+/* **************************************************************** */
+
+/* A pointer to the keymap that is currently in use.
+ By default, it is the standard emacs keymap. */
+Keymap keymap = emacs_standard_keymap;
+
+#define vi_mode 0
+#define emacs_mode 1
+
+/* The current style of editing. */
+int rl_editing_mode = emacs_mode;
+
+/* Non-zero if the previous command was a kill command. */
+static int last_command_was_kill = 0;
+
+/* The current value of the numeric argument specified by the user. */
+int rl_numeric_arg = 1;
+
+/* Non-zero if an argument was typed. */
+int rl_explicit_arg = 0;
+
+/* Temporary value used while generating the argument. */
+static int arg_sign = 1;
+
+/* Non-zero means we have been called at least once before. */
+static int rl_initialized = 0;
+
+/* If non-zero, this program is running in an EMACS buffer. */
+static char *running_in_emacs = (char *)NULL;
+
+/* The current offset in the current input line. */
+int rl_point;
+
+/* Mark in the current input line. */
+int rl_mark;
+
+/* Length of the current input line. */
+int rl_end;
+
+/* Make this non-zero to return the current input_line. */
+int rl_done;
+
+/* The last function executed by readline. */
+Function *rl_last_func = (Function *)NULL;
+
+/* Top level environment for readline_internal (). */
+static jmp_buf readline_top_level;
+
+/* The streams we interact with. */
+static FILE *in_stream, *out_stream;
+
+/* The names of the streams that we do input and output to. */
+FILE *rl_instream = stdin, *rl_outstream = stdout;
+
+/* Non-zero means echo characters as they are read. */
+int readline_echoing_p = 1;
+
+/* Current prompt. */
+char *rl_prompt;
+
+/* The number of characters read in order to type this complete command. */
+int rl_key_sequence_length = 0;
+
+/* If non-zero, then this is the address of a function to call just
+ before readline_internal () prints the first prompt. */
+Function *rl_startup_hook = (Function *)NULL;
+
+/* What we use internally. You should always refer to RL_LINE_BUFFER. */
+static char *the_line;
+
+/* The character that can generate an EOF. Really read from
+ the terminal driver... just defaulted here. */
+static int eof_char = CTRL ('D');
+
+/* Non-zero makes this the next keystroke to read. */
+int rl_pending_input = 0;
+
+/* Pointer to a useful terminal name. */
+char *rl_terminal_name = (char *)NULL;
+
+/* Line buffer and maintenence. */
+char *rl_line_buffer = (char *)NULL;
+static int rl_line_buffer_len = 0;
+#define DEFAULT_BUFFER_SIZE 256
+
+
+/* **************************************************************** */
+/* */
+/* Top Level Functions */
+/* */
+/* **************************************************************** */
+
+/* Read a line of input. Prompt with PROMPT. A NULL PROMPT means
+ none. A return value of NULL means that EOF was encountered. */
+char *
+readline (prompt)
+ char *prompt;
+{
+ static rl_prep_terminal (), rl_deprep_terminal ();
+ char *readline_internal ();
+ char *value;
+
+ rl_prompt = prompt;
+
+ /* If we are at EOF return a NULL string. */
+ if (rl_pending_input == EOF)
+ {
+ rl_pending_input = 0;
+ return ((char *)NULL);
+ }
+
+ rl_initialize ();
+ rl_prep_terminal ();
+
+#ifdef SIGWINCH
+ old_sigwinch = (SigHandler *)signal (SIGWINCH, rl_handle_sigwinch);
+#endif
+
+#ifdef HANDLE_SIGNALS
+ rl_set_signals ();
+#endif
+
+ value = readline_internal ();
+ rl_deprep_terminal ();
+
+#ifdef SIGWINCH
+ signal (SIGWINCH, old_sigwinch);
+#endif
+
+#ifdef HANDLE_SIGNALS
+ rl_clear_signals ();
+#endif
+
+ return (value);
+}
+
+/* Read a line of input from the global rl_instream, doing output on
+ the global rl_outstream.
+ If rl_prompt is non-null, then that is our prompt. */
+char *
+readline_internal ()
+{
+ int lastc, c, eof_found;
+
+ in_stream = rl_instream; out_stream = rl_outstream;
+ lastc = eof_found = 0;
+
+ if (rl_startup_hook)
+ (*rl_startup_hook) ();
+
+ if (!readline_echoing_p)
+ {
+ if (rl_prompt) {
+ fprintf (out_stream, "%s", rl_prompt);
+ fflush(out_stream);
+ }
+ }
+ else
+ {
+ rl_on_new_line ();
+ rl_redisplay ();
+#ifdef VI_MODE
+ if (rl_editing_mode == vi_mode)
+ rl_vi_insertion_mode ();
+#endif /* VI_MODE */
+ }
+
+ while (!rl_done)
+ {
+ int lk = last_command_was_kill;
+ int code = setjmp (readline_top_level);
+
+ if (code)
+ rl_redisplay ();
+
+ if (!rl_pending_input)
+ {
+ /* Then initialize the argument and number of keys read. */
+ rl_init_argument ();
+ rl_key_sequence_length = 0;
+ }
+
+ c = rl_read_key ();
+
+ /* EOF typed to a non-blank line is a <NL>. */
+ if (c == EOF && rl_end)
+ c = NEWLINE;
+
+ /* The character eof_char typed to blank line, and not as the
+ previous character is interpreted as EOF. */
+ if (((c == eof_char && lastc != c) || c == EOF) && !rl_end)
+ {
+ eof_found = 1;
+ break;
+ }
+
+ lastc = c;
+ rl_dispatch (c, keymap);
+
+ /* If there was no change in last_command_was_kill, then no kill
+ has taken place. Note that if input is pending we are reading
+ a prefix command, so nothing has changed yet. */
+ if (!rl_pending_input)
+ {
+ if (lk == last_command_was_kill)
+ last_command_was_kill = 0;
+ }
+
+#ifdef VI_MODE
+ /* In vi mode, when you exit insert mode, the cursor moves back
+ over the previous character. We explicitly check for that here. */
+ if (rl_editing_mode == vi_mode && keymap == vi_movement_keymap)
+ rl_vi_check ();
+#endif
+
+ if (!rl_done)
+ rl_redisplay ();
+ }
+
+ /* Restore the original of this history line, iff the line that we
+ are editing was originally in the history, AND the line has changed. */
+ {
+ HIST_ENTRY *entry = current_history ();
+
+ if (entry && rl_undo_list)
+ {
+ char *temp = savestring (the_line);
+ rl_revert_line ();
+ entry = replace_history_entry (where_history (), the_line,
+ (HIST_ENTRY *)NULL);
+ free_history_entry (entry);
+
+ strcpy (the_line, temp);
+ free (temp);
+ }
+ }
+
+ /* At any rate, it is highly likely that this line has an undo list. Get
+ rid of it now. */
+ if (rl_undo_list)
+ free_undo_list ();
+
+ if (eof_found)
+ return (char *)NULL;
+ else
+ return (savestring (the_line));
+}
+
+
+/* Variables for keyboard macros. */
+
+/* The currently executing macro string. If this is non-zero,
+ then it is a malloc ()'ed string where input is coming from. */
+static char *executing_macro = (char *)NULL;
+
+/* The offset in the above string to the next character to be read. */
+static int executing_macro_index = 0;
+
+/* Non-zero means to save keys that we dispatch on in a kbd macro. */
+static int defining_kbd_macro = 0;
+
+/* The current macro string being built. Characters get stuffed
+ in here by add_macro_char (). */
+static char *current_macro = (char *)NULL;
+
+/* The size of the buffer allocated to current_macro. */
+static int current_macro_size = 0;
+
+/* The index at which characters are being added to current_macro. */
+static int current_macro_index = 0;
+
+/* A structure used to save nested macro strings.
+ It is a linked list of string/index for each saved macro. */
+struct saved_macro {
+ struct saved_macro *next;
+ char *string;
+ int index;
+};
+
+/* The list of saved macros. */
+struct saved_macro *macro_list = (struct saved_macro *)NULL;
+
+
+/* **************************************************************** */
+/* */
+/* Signal Handling */
+/* */
+/* **************************************************************** */
+
+#ifdef SIGWINCH
+static void
+rl_handle_sigwinch (sig, code, scp)
+ int sig, code;
+ struct sigcontext *scp;
+{
+ char *term = rl_terminal_name, *getenv ();
+
+ if (readline_echoing_p)
+ {
+ if (!term)
+ term = getenv ("TERM");
+ if (!term)
+ term = "dumb";
+ rl_reset_terminal (term);
+#ifdef NEVER
+ crlf ();
+ rl_forced_update_display ();
+#endif
+ }
+
+ if (old_sigwinch &&
+ old_sigwinch != (SigHandler *)SIG_IGN &&
+ old_sigwinch != (SigHandler *)SIG_DFL)
+ (*old_sigwinch)(sig, code, scp);
+}
+#endif /* SIGWINCH */
+
+#ifdef HANDLE_SIGNALS
+/* Interrupt handling. */
+static SigHandler *old_int = (SigHandler *)NULL,
+ *old_tstp = (SigHandler *)NULL,
+ *old_ttou = (SigHandler *)NULL,
+ *old_ttin = (SigHandler *)NULL,
+ *old_cont = (SigHandler *)NULL;
+
+/* Handle an interrupt character. */
+static void
+rl_signal_handler (sig, code, scp)
+ int sig, code;
+ struct sigcontext *scp;
+{
+ static rl_prep_terminal (), rl_deprep_terminal ();
+
+ switch (sig)
+ {
+ case SIGINT:
+ free_undo_list ();
+ rl_clear_message ();
+ rl_init_argument ();
+#ifdef SIGWINCH
+ signal (SIGWINCH, old_sigwinch);
+#endif
+
+#ifdef SIGTSTP
+ case SIGTSTP:
+ case SIGTTOU:
+ case SIGTTIN:
+#endif
+
+ rl_clean_up_for_exit ();
+ rl_deprep_terminal ();
+ rl_clear_signals ();
+ rl_pending_input = 0;
+
+ kill (getpid (), sig);
+ sigsetmask (0);
+
+ rl_prep_terminal ();
+ rl_set_signals ();
+ }
+}
+
+rl_set_signals ()
+{
+ old_int = (SigHandler *)signal (SIGINT, rl_signal_handler);
+
+ if (old_int == (SigHandler *)SIG_IGN)
+ signal (SIGINT, SIG_IGN);
+
+#ifdef SIGTSTP
+ old_tstp = (SigHandler *)signal (SIGTSTP, rl_signal_handler);
+ if (old_tstp == (SigHandler *)SIG_IGN)
+ signal (SIGTSTP, SIG_IGN);
+#endif
+#ifdef SIGTTOU
+ old_ttou = (SigHandler *)signal (SIGTTOU, rl_signal_handler);
+ old_ttin = (SigHandler *)signal (SIGTTIN, rl_signal_handler);
+#endif
+}
+
+rl_clear_signals ()
+{
+ signal (SIGINT, old_int);
+
+#ifdef SIGTSTP
+ signal (SIGTSTP, old_tstp);
+#endif
+#ifdef SIGTTOU
+ signal (SIGTTOU, old_ttou);
+ signal (SIGTTIN, old_ttin);
+#endif
+}
+#endif /* HANDLE_SIGNALS */
+
+
+
+/* **************************************************************** */
+/* */
+/* Character Input Buffering */
+/* */
+/* **************************************************************** */
+
+/* If the terminal was in xoff state when we got to it, then xon_char
+ contains the character that is supposed to start it again. */
+static int xon_char, xoff_state;
+static int pop_index = 0, push_index = 0, ibuffer_len = 511;
+static unsigned char ibuffer[512];
+
+/* Non-null means it is a pointer to a function to run while waiting for
+ character input. */
+Function *rl_event_hook = (Function *)NULL;
+
+#define any_typein (push_index != pop_index)
+
+/* Add KEY to the buffer of characters to be read. */
+rl_stuff_char (key)
+ int key;
+{
+ if (key == EOF)
+ {
+ key = NEWLINE;
+ rl_pending_input = EOF;
+ }
+ ibuffer[push_index++] = key;
+ if (push_index >= ibuffer_len)
+ push_index = 0;
+}
+
+/* Return the amount of space available in the
+ buffer for stuffing characters. */
+int
+ibuffer_space ()
+{
+ if (pop_index > push_index)
+ return (pop_index - push_index);
+ else
+ return (ibuffer_len - (push_index - pop_index));
+}
+
+/* Get a key from the buffer of characters to be read.
+ Result is KEY if there was a key, or -2 if there wasn't. */
+int
+rl_get_char ()
+{
+ int key;
+
+ if (push_index == pop_index)
+ return (-2);
+
+ key = ibuffer[pop_index++];
+
+ if (pop_index >= ibuffer_len)
+ pop_index = 0;
+
+ return (key);
+}
+
+/* Stuff KEY into the *front* of the input buffer.
+ Returns non-zero if successful, zero if there is
+ no space left in the buffer. */
+int
+rl_unget_char (key)
+ int key;
+{
+ if (ibuffer_space ())
+ {
+ pop_index--;
+ if (pop_index < 0)
+ pop_index = ibuffer_len - 1;
+ ibuffer[pop_index] = key;
+ return (1);
+ }
+ return (0);
+}
+
+
+
+static void
+rl_getc (stream)
+ FILE *stream;
+{
+ int result;
+ int nchar;
+ int tty = fileno(stream);
+ char buf[512]; /* XXX - must be at least as large as ibuffer */
+
+ while (1)
+ {
+ if (ioctl(tty, FIONREAD, &nchar) == -1)
+ nchar = sizeof(buf);
+ else if (nchar <= 0)
+ nchar = 1;
+ result = ibuffer_space();
+ if (nchar > result)
+ nchar = result;
+ result = read(tty, buf, nchar);
+ if (result > 0)
+ {
+ register char *cp = buf;
+
+ while (--result >= 0)
+ rl_stuff_char(*cp++);
+ return;
+ }
+ if (errno != EINTR)
+ {
+ rl_stuff_char(EOF);
+ return;
+ }
+ }
+}
+
+/* Read a key, including pending input. */
+int
+rl_read_key ()
+{
+ int c;
+
+ rl_key_sequence_length++;
+
+ if (rl_pending_input)
+ {
+ c = rl_pending_input;
+ rl_pending_input = 0;
+ }
+ else
+ {
+ static int next_macro_key ();
+
+ /* If input is coming from a macro, then use that. */
+ if (c = next_macro_key ())
+ return (c);
+
+ while ((c = rl_get_char()) == -2)
+ {
+ if (rl_event_hook)
+ {
+ (*rl_event_hook) ();
+ if ((c = rl_get_char()) != -2)
+ return (c);
+ }
+ rl_getc(in_stream);
+ }
+ }
+#ifdef TIOCSTART
+ /* Ugh. But I can't think of a better way. */
+ if (xoff_state && c == xon_char)
+ {
+ ioctl (fileno (in_stream), TIOCSTART, 0);
+ xoff_state = 0;
+ return rl_read_key ();
+ }
+#endif /* TIOCSTART */
+ return (c);
+}
+
+/* Do the command associated with KEY in MAP.
+ If the associated command is really a keymap, then read
+ another key, and dispatch into that map. */
+rl_dispatch (key, map)
+ register int key;
+ Keymap map;
+{
+ if (defining_kbd_macro)
+ {
+ static add_macro_char ();
+
+ add_macro_char (key);
+ }
+
+ if (key > 127 && key < 256)
+ {
+ if (map[ESC].type == ISKMAP)
+ {
+ map = (Keymap)map[ESC].function;
+ key -= 128;
+ rl_dispatch (key, map);
+ }
+ else
+ ding ();
+ return;
+ }
+
+ switch (map[key].type)
+ {
+ case ISFUNC:
+ {
+ Function *func = map[key].function;
+
+ if (func != (Function *)NULL)
+ {
+ /* Special case rl_do_lowercase_version (). */
+ if (func == rl_do_lowercase_version)
+ {
+ rl_dispatch (to_lower (key), map);
+ return;
+ }
+
+ (*map[key].function)(rl_numeric_arg * arg_sign, key);
+ }
+ else
+ {
+ ding ();
+ return;
+ }
+ }
+ break;
+
+ case ISKMAP:
+ if (map[key].function != (Function *)NULL)
+ {
+ int newkey;
+
+ rl_key_sequence_length++;
+ newkey = rl_read_key ();
+ rl_dispatch (newkey, (Keymap)map[key].function);
+ }
+ else
+ {
+ ding ();
+ return;
+ }
+ break;
+
+ case ISMACR:
+ if (map[key].function != (Function *)NULL)
+ {
+ static with_macro_input ();
+ char *macro = savestring ((char *)map[key].function);
+
+ with_macro_input (macro);
+ return;
+ }
+ break;
+ }
+
+ /* If we have input pending, then the last command was a prefix
+ command. Don't change the state of rl_last_func. */
+ if (!rl_pending_input)
+ rl_last_func = map[key].function;
+}
+
+
+/* **************************************************************** */
+/* */
+/* Hacking Keyboard Macros */
+/* */
+/* **************************************************************** */
+
+/* Set up to read subsequent input from STRING.
+ STRING is free ()'ed when we are done with it. */
+static
+with_macro_input (string)
+ char *string;
+{
+ static push_executing_macro ();
+
+ push_executing_macro ();
+ executing_macro = string;
+ executing_macro_index = 0;
+}
+
+/* Return the next character available from a macro, or 0 if
+ there are no macro characters. */
+static int
+next_macro_key ()
+{
+ if (!executing_macro)
+ return (0);
+
+ if (!executing_macro[executing_macro_index])
+ {
+ static pop_executing_macro ();
+
+ pop_executing_macro ();
+ return (next_macro_key ());
+ }
+
+ return (executing_macro[executing_macro_index++]);
+}
+
+/* Save the currently executing macro on a stack of saved macros. */
+static
+push_executing_macro ()
+{
+ struct saved_macro *saver;
+
+ saver = (struct saved_macro *)xmalloc (sizeof (struct saved_macro));
+ saver->next = macro_list;
+ saver->index = executing_macro_index;
+ saver->string = executing_macro;
+
+ macro_list = saver;
+}
+
+/* Discard the current macro, replacing it with the one
+ on the top of the stack of saved macros. */
+static
+pop_executing_macro ()
+{
+ if (executing_macro)
+ free (executing_macro);
+
+ executing_macro = (char *)NULL;
+ executing_macro_index = 0;
+
+ if (macro_list)
+ {
+ struct saved_macro *disposer = macro_list;
+ executing_macro = macro_list->string;
+ executing_macro_index = macro_list->index;
+ macro_list = macro_list->next;
+ free (disposer);
+ }
+}
+
+/* Add a character to the macro being built. */
+static
+add_macro_char (c)
+ int c;
+{
+ if (current_macro_index + 1 >= current_macro_size)
+ {
+ if (!current_macro)
+ current_macro = (char *)xmalloc (current_macro_size = 25);
+ else
+ current_macro =
+ (char *)xrealloc (current_macro, current_macro_size += 25);
+ }
+
+ current_macro[current_macro_index++] = c;
+ current_macro[current_macro_index] = '\0';
+}
+
+/* Begin defining a keyboard macro.
+ Keystrokes are recorded as they are executed.
+ End the definition with rl_end_kbd_macro ().
+ If a numeric argument was explicitly typed, then append this
+ definition to the end of the existing macro, and start by
+ re-executing the existing macro. */
+rl_start_kbd_macro (ignore1, ignore2)
+ int ignore1, ignore2;
+{
+ if (defining_kbd_macro)
+ rl_abort ();
+
+ if (rl_explicit_arg)
+ {
+ if (current_macro)
+ with_macro_input (savestring (current_macro));
+ }
+ else
+ current_macro_index = 0;
+
+ defining_kbd_macro = 1;
+}
+
+/* Stop defining a keyboard macro.
+ A numeric argument says to execute the macro right now,
+ that many times, counting the definition as the first time. */
+rl_end_kbd_macro (count, ignore)
+ int count, ignore;
+{
+ if (!defining_kbd_macro)
+ rl_abort ();
+
+ current_macro_index -= (rl_key_sequence_length - 1);
+ current_macro[current_macro_index] = '\0';
+
+ defining_kbd_macro = 0;
+
+ rl_call_last_kbd_macro (--count, 0);
+}
+
+/* Execute the most recently defined keyboard macro.
+ COUNT says how many times to execute it. */
+rl_call_last_kbd_macro (count, ignore)
+ int count, ignore;
+{
+ if (!current_macro)
+ rl_abort ();
+
+ while (count--)
+ with_macro_input (savestring (current_macro));
+}
+
+
+/* Non-zero means do not parse any lines other than comments and
+ parser directives. */
+static unsigned char parsing_conditionalized_out = 0;
+
+/* **************************************************************** */
+/* */
+/* Initializations */
+/* */
+/* **************************************************************** */
+
+/* Initliaze readline (and terminal if not already). */
+rl_initialize ()
+{
+ extern char *rl_display_prompt;
+
+ /* If we have never been called before, initialize the
+ terminal and data structures. */
+ if (!rl_initialized)
+ {
+ readline_initialize_everything ();
+ rl_initialized++;
+ }
+
+ /* Initalize the current line information. */
+ rl_point = rl_end = 0;
+ the_line = rl_line_buffer;
+ the_line[0] = 0;
+
+ /* We aren't done yet. We haven't even gotten started yet! */
+ rl_done = 0;
+
+ /* Tell the history routines what is going on. */
+ start_using_history ();
+
+ /* Make the display buffer match the state of the line. */
+ {
+ extern char *rl_display_prompt;
+ extern int forced_display;
+
+ rl_on_new_line ();
+
+ rl_display_prompt = rl_prompt ? rl_prompt : "";
+ forced_display = 1;
+ }
+
+ /* No such function typed yet. */
+ rl_last_func = (Function *)NULL;
+
+ /* Parsing of key-bindings begins in an enabled state. */
+ {
+ parsing_conditionalized_out = 0;
+ }
+}
+
+/* Initialize the entire state of the world. */
+readline_initialize_everything ()
+{
+ /* Find out if we are running in Emacs. */
+ running_in_emacs = (char *)getenv ("EMACS");
+
+ /* Allocate data structures. */
+ if (!rl_line_buffer)
+ rl_line_buffer =
+ (char *)xmalloc (rl_line_buffer_len = DEFAULT_BUFFER_SIZE);
+
+ /* Initialize the terminal interface. */
+ init_terminal_io ((char *)NULL);
+
+ /* Bind tty characters to readline functions. */
+ readline_default_bindings ();
+
+ /* Initialize the function names. */
+ rl_initialize_funmap ();
+
+ /* Read in the init file. */
+ rl_read_init_file ((char *)NULL);
+
+ /* If the completion parser's default word break characters haven't
+ been set yet, then do so now. */
+ {
+ extern char *rl_completer_word_break_characters;
+ extern char *rl_basic_word_break_characters;
+
+ if (rl_completer_word_break_characters == (char *)NULL)
+ rl_completer_word_break_characters = rl_basic_word_break_characters;
+ }
+}
+
+/* If this system allows us to look at the values of the regular
+ input editing characters, then bind them to their readline
+ equivalents. */
+readline_default_bindings ()
+{
+#ifdef TIOCGETP
+ struct sgttyb ttybuff;
+ int tty = fileno (rl_instream);
+
+ if (ioctl (tty, TIOCGETP, &ttybuff) != -1)
+ {
+ int erase = ttybuff.sg_erase, kill = ttybuff.sg_kill;
+
+ if (erase != -1 && keymap[erase].type == ISFUNC)
+ keymap[erase].function = rl_rubout;
+
+ if (kill != -1 && keymap[kill].type == ISFUNC)
+ keymap[kill].function = rl_unix_line_discard;
+ }
+
+#ifdef TIOCGLTC
+ {
+ struct ltchars lt;
+
+ if (ioctl (tty, TIOCGLTC, &lt) != -1)
+ {
+ int erase = lt.t_werasc, nextc = lt.t_lnextc;
+
+ if (erase != -1 && keymap[erase].type == ISFUNC)
+ keymap[erase].function = rl_unix_word_rubout;
+
+ if (nextc != -1 && keymap[nextc].type == ISFUNC)
+ keymap[nextc].function = rl_quoted_insert;
+ }
+ }
+#endif /* TIOCGLTC */
+#endif /* TIOCGETP */
+}
+
+
+/* **************************************************************** */
+/* */
+/* Numeric Arguments */
+/* */
+/* **************************************************************** */
+
+/* Handle C-u style numeric args, as well as M--, and M-digits. */
+
+/* Add the current digit to the argument in progress. */
+rl_digit_argument (ignore, key)
+ int ignore, key;
+{
+ rl_pending_input = key;
+ rl_digit_loop ();
+}
+
+/* What to do when you abort reading an argument. */
+rl_discard_argument ()
+{
+ ding ();
+ rl_clear_message ();
+ rl_init_argument ();
+}
+
+/* Create a default argument. */
+rl_init_argument ()
+{
+ rl_numeric_arg = arg_sign = 1;
+ rl_explicit_arg = 0;
+}
+
+/* C-u, universal argument. Multiply the current argument by 4.
+ Read a key. If the key has nothing to do with arguments, then
+ dispatch on it. If the key is the abort character then abort. */
+rl_universal_argument ()
+{
+ rl_numeric_arg *= 4;
+ rl_digit_loop ();
+}
+
+rl_digit_loop ()
+{
+ int key, c;
+ while (1)
+ {
+ rl_message ("(arg: %d) ", arg_sign * rl_numeric_arg);
+ key = c = rl_read_key ();
+
+ if (keymap[c].type == ISFUNC &&
+ keymap[c].function == rl_universal_argument)
+ {
+ rl_numeric_arg *= 4;
+ continue;
+ }
+ c = UNMETA (c);
+ if (numeric (c))
+ {
+ if (rl_explicit_arg)
+ rl_numeric_arg = (rl_numeric_arg * 10) + (c - '0');
+ else
+ rl_numeric_arg = (c - '0');
+ rl_explicit_arg = 1;
+ }
+ else
+ {
+ if (c == '-' && !rl_explicit_arg)
+ {
+ rl_numeric_arg = 1;
+ arg_sign = -1;
+ }
+ else
+ {
+ rl_clear_message ();
+ rl_dispatch (key, keymap);
+ return;
+ }
+ }
+ }
+}
+
+
+/* **************************************************************** */
+/* */
+/* Display stuff */
+/* */
+/* **************************************************************** */
+
+/* This is the stuff that is hard for me. I never seem to write good
+ display routines in C. Let's see how I do this time. */
+
+/* (PWP) Well... Good for a simple line updater, but totally ignores
+ the problems of input lines longer than the screen width.
+
+ update_line and the code that calls it makes a multiple line,
+ automatically wrapping line update. Carefull attention needs
+ to be paid to the vertical position variables.
+
+ handling of terminals with autowrap on (incl. DEC braindamage)
+ could be improved a bit. Right now I just cheat and decrement
+ screenwidth by one. */
+
+/* Keep two buffers; one which reflects the current contents of the
+ screen, and the other to draw what we think the new contents should
+ be. Then compare the buffers, and make whatever changes to the
+ screen itself that we should. Finally, make the buffer that we
+ just drew into be the one which reflects the current contents of the
+ screen, and place the cursor where it belongs.
+
+ Commands that want to can fix the display themselves, and then let
+ this function know that the display has been fixed by setting the
+ RL_DISPLAY_FIXED variable. This is good for efficiency. */
+
+/* Termcap variables: */
+extern char *term_up, *term_dc, *term_cr;
+extern int screenheight, screenwidth, terminal_can_insert;
+
+/* What YOU turn on when you have handled all redisplay yourself. */
+int rl_display_fixed = 0;
+
+/* The visible cursor position. If you print some text, adjust this. */
+int last_c_pos = 0;
+int last_v_pos = 0;
+
+/* The last left edge of text that was displayed. This is used when
+ doing horizontal scrolling. It shifts in thirds of a screenwidth. */
+static int last_lmargin = 0;
+
+/* The line display buffers. One is the line currently displayed on
+ the screen. The other is the line about to be displayed. */
+static char *visible_line = (char *)NULL;
+static char *invisible_line = (char *)NULL;
+
+/* Number of lines currently on screen minus 1. */
+int vis_botlin = 0;
+
+/* A buffer for `modeline' messages. */
+char msg_buf[128];
+
+/* Non-zero forces the redisplay even if we thought it was unnecessary. */
+int forced_display = 0;
+
+/* The stuff that gets printed out before the actual text of the line.
+ This is usually pointing to rl_prompt. */
+char *rl_display_prompt = (char *)NULL;
+
+/* Default and initial buffer size. Can grow. */
+static int line_size = 1024;
+
+/* Non-zero means to always use horizontal scrolling in line display. */
+int horizontal_scroll_mode = 0;
+
+/* I really disagree with this, but my boss (among others) insists that we
+ support compilers that don't work. I don't think we are gaining by doing
+ so; what is the advantage in producing better code if we can't use it? */
+/* The following two declarations belong inside the
+ function block, not here. */
+static void move_cursor_relative ();
+static void output_some_chars ();
+
+/* Basic redisplay algorithm. */
+rl_redisplay ()
+{
+ register int in, out, c, linenum;
+ register char *line = invisible_line;
+ int c_pos = 0;
+ int inv_botlin = 0; /* Number of lines in newly drawn buffer. */
+
+ extern int readline_echoing_p;
+
+ if (!readline_echoing_p)
+ return;
+
+ if (!rl_display_prompt)
+ rl_display_prompt = "";
+
+ if (!invisible_line)
+ {
+ visible_line = (char *)xmalloc (line_size);
+ invisible_line = (char *)xmalloc (line_size);
+ line = invisible_line;
+ for (in = 0; in < line_size; in++)
+ {
+ visible_line[in] = 0;
+ invisible_line[in] = 1;
+ }
+ rl_on_new_line ();
+ }
+
+ /* Draw the line into the buffer. */
+ c_pos = -1;
+
+ /* Mark the line as modified or not. We only do this for history
+ lines. */
+ out = 0;
+ if (current_history () && rl_undo_list)
+ {
+ line[out++] = '*';
+ line[out] = '\0';
+ }
+
+ /* If someone thought that the redisplay was handled, but the currently
+ visible line has a different modification state than the one about
+ to become visible, then correct the callers misconception. */
+ if (visible_line[0] != invisible_line[0])
+ rl_display_fixed = 0;
+
+ strncpy (line + out, rl_display_prompt, strlen (rl_display_prompt));
+ out += strlen (rl_display_prompt);
+ line[out] = '\0';
+
+ for (in = 0; in < rl_end; in++)
+ {
+ c = the_line[in];
+
+ if (out + 1 >= line_size)
+ {
+ line_size *= 2;
+ visible_line = (char *)xrealloc (visible_line, line_size);
+ invisible_line = (char *)xrealloc (invisible_line, line_size);
+ line = invisible_line;
+ }
+
+ if (in == rl_point)
+ c_pos = out;
+
+ if (c > 127)
+ {
+ line[out++] = 'M';
+ line[out++] = '-';
+ line[out++] = c - 128;
+ }
+#define DISPLAY_TABS
+#ifdef DISPLAY_TABS
+ else if (c == '\t')
+ {
+ register int newout = (out | (int)7) + 1;
+ while (out < newout)
+ line[out++] = ' ';
+ }
+#endif
+ else if (c < 32)
+ {
+ line[out++] = 'C';
+ line[out++] = '-';
+ line[out++] = c + 64;
+ }
+ else
+ line[out++] = c;
+ }
+ line[out] = '\0';
+ if (c_pos < 0)
+ c_pos = out;
+
+ /* PWP: now is when things get a bit hairy. The visible and invisible
+ line buffers are really multiple lines, which would wrap every
+ (screenwidth - 1) characters. Go through each in turn, finding
+ the changed region and updating it. The line order is top to bottom. */
+
+ /* If we can move the cursor up and down, then use multiple lines,
+ otherwise, let long lines display in a single terminal line, and
+ horizontally scroll it. */
+
+ if (!horizontal_scroll_mode && term_up && *term_up)
+ {
+ int total_screen_chars = (screenwidth * screenheight);
+
+ if (!rl_display_fixed || forced_display)
+ {
+ forced_display = 0;
+
+ /* If we have more than a screenful of material to display, then
+ only display a screenful. We should display the last screen,
+ not the first. I'll fix this in a minute. */
+ if (out >= total_screen_chars)
+ out = total_screen_chars - 1;
+
+ /* Number of screen lines to display. */
+ inv_botlin = out / screenwidth;
+
+ /* For each line in the buffer, do the updating display. */
+ for (linenum = 0; linenum <= inv_botlin; linenum++)
+ update_line (linenum > vis_botlin ? ""
+ : &visible_line[linenum * screenwidth],
+ &invisible_line[linenum * screenwidth],
+ linenum);
+
+ /* We may have deleted some lines. If so, clear the left over
+ blank ones at the bottom out. */
+ if (vis_botlin > inv_botlin)
+ {
+ char *tt;
+ for (; linenum <= vis_botlin; linenum++)
+ {
+ tt = &visible_line[linenum * screenwidth];
+ move_vert (linenum);
+ move_cursor_relative (0, tt);
+ clear_to_eol ((linenum == vis_botlin)?
+ strlen (tt) : screenwidth);
+ }
+ }
+ vis_botlin = inv_botlin;
+
+ /* Move the cursor where it should be. */
+ move_vert (c_pos / screenwidth);
+ move_cursor_relative (c_pos % screenwidth,
+ &invisible_line[(c_pos / screenwidth) * screenwidth]);
+ }
+ }
+ else /* Do horizontal scrolling. */
+ {
+ int lmargin;
+
+ /* Always at top line. */
+ last_v_pos = 0;
+
+ /* If the display position of the cursor would be off the edge
+ of the screen, start the display of this line at an offset that
+ leaves the cursor on the screen. */
+ if (c_pos - last_lmargin > screenwidth - 2)
+ lmargin = (c_pos / (screenwidth / 3) - 2) * (screenwidth / 3);
+ else if (c_pos - last_lmargin < 1)
+ lmargin = ((c_pos - 1) / (screenwidth / 3)) * (screenwidth / 3);
+ else
+ lmargin = last_lmargin;
+
+ /* If the first character on the screen isn't the first character
+ in the display line, indicate this with a special character. */
+ if (lmargin > 0)
+ line[lmargin] = '<';
+
+ if (lmargin + screenwidth < out)
+ line[lmargin + screenwidth - 1] = '>';
+
+ if (!rl_display_fixed || forced_display || lmargin != last_lmargin)
+ {
+ forced_display = 0;
+ update_line (&visible_line[last_lmargin],
+ &invisible_line[lmargin], 0);
+
+ move_cursor_relative (c_pos - lmargin, &invisible_line[lmargin]);
+ last_lmargin = lmargin;
+ }
+ }
+ fflush (out_stream);
+
+ /* Swap visible and non-visible lines. */
+ {
+ char *temp = visible_line;
+ visible_line = invisible_line;
+ invisible_line = temp;
+ rl_display_fixed = 0;
+ }
+}
+
+/* PWP: update_line() is based on finding the middle difference of each
+ line on the screen; vis:
+
+ /old first difference
+ /beginning of line | /old last same /old EOL
+ v v v v
+old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as
+new: eddie> Oh, my little buggy says to me, as lurgid as
+ ^ ^ ^ ^
+ \beginning of line | \new last same \new end of line
+ \new first difference
+
+ All are character pointers for the sake of speed. Special cases for
+ no differences, as well as for end of line additions must be handeled.
+
+ Could be made even smarter, but this works well enough */
+static
+update_line (old, new, current_line)
+ register char *old, *new;
+ int current_line;
+{
+ register char *ofd, *ols, *oe, *nfd, *nls, *ne;
+ int lendiff, wsatend;
+
+ /* Find first difference. */
+ for (ofd = old, nfd = new;
+ (ofd - old < screenwidth) && *ofd && (*ofd == *nfd);
+ ofd++, nfd++)
+ ;
+
+ /* Move to the end of the screen line. */
+ for (oe = ofd; ((oe - old) < screenwidth) && *oe; oe++);
+ for (ne = nfd; ((ne - new) < screenwidth) && *ne; ne++);
+
+ /* If no difference, continue to next line. */
+ if (ofd == oe && nfd == ne)
+ return;
+
+ wsatend = 1; /* flag for trailing whitespace */
+ ols = oe - 1; /* find last same */
+ nls = ne - 1;
+ while ((*ols == *nls) && (ols > ofd) && (nls > nfd))
+ {
+ if (*ols != ' ')
+ wsatend = 0;
+ ols--;
+ nls--;
+ }
+
+ if (wsatend)
+ {
+ ols = oe;
+ nls = ne;
+ }
+ else if (*ols != *nls)
+ {
+ if (*ols) /* don't step past the NUL */
+ ols++;
+ if (*nls)
+ nls++;
+ }
+
+ move_vert (current_line);
+ move_cursor_relative (ofd - old, old);
+
+ /* if (len (new) > len (old)) */
+ lendiff = (nls - nfd) - (ols - ofd);
+
+ /* Insert (diff(len(old),len(new)) ch */
+ if (lendiff > 0)
+ {
+ if (terminal_can_insert)
+ {
+ extern char *term_IC;
+
+ /* Sometimes it is cheaper to print the characters rather than
+ use the terminal's capabilities. */
+ if ((2 * (ne - nfd)) < lendiff && (!term_IC || !*term_IC))
+ {
+ output_some_chars (nfd, (ne - nfd));
+ last_c_pos += (ne - nfd);
+ }
+ else
+ {
+ if (*ols)
+ {
+ start_insert (lendiff);
+ output_some_chars (nfd, lendiff);
+ last_c_pos += lendiff;
+ end_insert ();
+ }
+ else
+ {
+ /* At the end of a line the characters do not have to
+ be "inserted". They can just be placed on the screen. */
+ output_some_chars (nfd, lendiff);
+ last_c_pos += lendiff;
+ }
+ /* Copy (new) chars to screen from first diff to last match. */
+ if (((nls - nfd) - lendiff) > 0)
+ {
+ output_some_chars (&nfd[lendiff], ((nls - nfd) - lendiff));
+ last_c_pos += ((nls - nfd) - lendiff);
+ }
+ }
+ }
+ else
+ { /* cannot insert chars, write to EOL */
+ output_some_chars (nfd, (ne - nfd));
+ last_c_pos += (ne - nfd);
+ }
+ }
+ else /* Delete characters from line. */
+ {
+ /* If possible and inexpensive to use terminal deletion, then do so. */
+ if (term_dc && (2 * (ne - nfd)) >= (-lendiff))
+ {
+ if (lendiff)
+ delete_chars (-lendiff); /* delete (diff) characters */
+
+ /* Copy (new) chars to screen from first diff to last match */
+ if ((nls - nfd) > 0)
+ {
+ output_some_chars (nfd, (nls - nfd));
+ last_c_pos += (nls - nfd);
+ }
+ }
+ /* Otherwise, print over the existing material. */
+ else
+ {
+ output_some_chars (nfd, (ne - nfd));
+ last_c_pos += (ne - nfd);
+ clear_to_eol ((oe - old) - (ne - new));
+ }
+ }
+}
+
+/* (PWP) tell the update routines that we have moved onto a
+ new (empty) line. */
+rl_on_new_line ()
+{
+ if (visible_line)
+ visible_line[0] = '\0';
+
+ last_c_pos = last_v_pos = 0;
+ vis_botlin = last_lmargin = 0;
+}
+
+/* Actually update the display, period. */
+rl_forced_update_display ()
+{
+ if (visible_line)
+ {
+ register char *temp = visible_line;
+
+ while (*temp) *temp++ = '\0';
+ }
+ rl_on_new_line ();
+ forced_display++;
+ rl_redisplay ();
+}
+
+/* Move the cursor from last_c_pos to NEW, which are buffer indices.
+ DATA is the contents of the screen line of interest; i.e., where
+ the movement is being done. */
+static void
+move_cursor_relative (new, data)
+ int new;
+ char *data;
+{
+ register int i;
+ static void output_character_function ();
+
+ /* It may be faster to output a CR, and then move forwards instead
+ of moving backwards. */
+ if (new + 1 < last_c_pos - new)
+ {
+ tputs (term_cr, 1, output_character_function);
+ last_c_pos = 0;
+ }
+
+ if (last_c_pos == new) return;
+
+ if (last_c_pos < new)
+ {
+ /* Move the cursor forward. We do it by printing the command
+ to move the cursor forward if there is one, else print that
+ portion of the output buffer again. Which is cheaper? */
+
+ /* The above comment is left here for posterity. It is faster
+ to print one character (non-control) than to print a control
+ sequence telling the terminal to move forward one character.
+ That kind of control is for people who don't know what the
+ data is underneath the cursor. */
+#ifdef HACK_TERMCAP_MOTION
+ extern char *term_forward_char;
+
+ if (term_forward_char)
+ for (i = last_c_pos; i < new; i++)
+ tputs (term_forward_char, 1, output_character_function);
+ else
+ for (i = last_c_pos; i < new; i++)
+ putc (data[i], out_stream);
+#else
+ for (i = last_c_pos; i < new; i++)
+ putc (data[i], out_stream);
+#endif /* HACK_TERMCAP_MOTION */
+ }
+ else
+ backspace (last_c_pos - new);
+ last_c_pos = new;
+}
+
+/* PWP: move the cursor up or down. */
+move_vert (to)
+ int to;
+{
+ void output_character_function ();
+ register int delta, i;
+
+ if (last_v_pos == to) return;
+
+ if (to > screenheight)
+ return;
+
+ if ((delta = to - last_v_pos) > 0)
+ {
+ for (i = 0; i < delta; i++)
+ putc ('\n', out_stream);
+ tputs (term_cr, 1, output_character_function);
+ last_c_pos = 0; /* because crlf() will do \r\n */
+ }
+ else
+ { /* delta < 0 */
+ if (term_up && *term_up)
+ for (i = 0; i < -delta; i++)
+ tputs (term_up, 1, output_character_function);
+ }
+ last_v_pos = to; /* now to is here */
+}
+
+/* Physically print C on out_stream. This is for functions which know
+ how to optimize the display. */
+rl_show_char (c)
+ int c;
+{
+ if (c > 127)
+ {
+ fprintf (out_stream, "M-");
+ c -= 128;
+ }
+
+#ifdef DISPLAY_TABS
+ if (c < 32 && c != '\t')
+#else
+ if (c < 32)
+#endif
+ {
+
+ c += 64;
+ }
+
+ putc (c, out_stream);
+ fflush (out_stream);
+}
+
+#ifdef DISPLAY_TABS
+int
+rl_character_len (c, pos)
+ register int c, pos;
+{
+ if (c < ' ' || c > 126)
+ {
+ if (c == '\t')
+ return (((pos | (int)7) + 1) - pos);
+ else
+ return (3);
+ }
+ else
+ return (1);
+}
+#else
+int
+rl_character_len (c)
+ int c;
+{
+ if (c < ' ' || c > 126)
+ return (3);
+ else
+ return (1);
+}
+#endif /* DISPLAY_TAB */
+
+/* How to print things in the "echo-area". The prompt is treated as a
+ mini-modeline. */
+rl_message (string, arg1, arg2)
+ char *string;
+{
+ sprintf (msg_buf, string, arg1, arg2);
+ rl_display_prompt = msg_buf;
+ rl_redisplay ();
+}
+
+/* How to clear things from the "echo-area". */
+rl_clear_message ()
+{
+ rl_display_prompt = rl_prompt;
+ rl_redisplay ();
+}
+
+/* **************************************************************** */
+/* */
+/* Terminal and Termcap */
+/* */
+/* **************************************************************** */
+
+static char *term_buffer = (char *)NULL;
+static char *term_string_buffer = (char *)NULL;
+
+/* Non-zero means this terminal can't really do anything. */
+int dumb_term = 0;
+
+char PC;
+char *BC, *UP;
+
+/* Some strings to control terminal actions. These are output by tputs (). */
+char *term_goto, *term_clreol, *term_cr, *term_clrpag, *term_backspace;
+
+int screenwidth, screenheight;
+
+/* Non-zero if we determine that the terminal can do character insertion. */
+int terminal_can_insert = 0;
+
+/* How to insert characters. */
+char *term_im, *term_ei, *term_ic, *term_ip, *term_IC;
+
+/* How to delete characters. */
+char *term_dc, *term_DC;
+
+#ifdef HACK_TERMCAP_MOTION
+char *term_forward_char;
+#endif /* HACK_TERMCAP_MOTION */
+
+/* How to go up a line. */
+char *term_up;
+
+/* Re-initialize the terminal considering that the TERM/TERMCAP variable
+ has changed. */
+rl_reset_terminal (terminal_name)
+ char *terminal_name;
+{
+ init_terminal_io (terminal_name);
+}
+
+init_terminal_io (terminal_name)
+ char *terminal_name;
+{
+ char *term = (terminal_name? terminal_name : (char *)getenv ("TERM"));
+ char *tgetstr (), *buffer;
+
+
+ if (!term_string_buffer)
+ term_string_buffer = (char *)xmalloc (2048);
+
+ if (!term_buffer)
+ term_buffer = (char *)xmalloc (2048);
+
+ buffer = term_string_buffer;
+
+ term_clrpag = term_cr = term_clreol = (char *)NULL;
+
+ if (!term)
+ term = "dumb";
+
+ if (tgetent (term_buffer, term) < 0)
+ {
+ dumb_term = 1;
+ return;
+ }
+
+ BC = tgetstr ("pc", &buffer);
+ PC = buffer ? *buffer : 0;
+
+ term_backspace = tgetstr ("le", &buffer);
+
+ term_cr = tgetstr ("cr", &buffer);
+ term_clreol = tgetstr ("ce", &buffer);
+ term_clrpag = tgetstr ("cl", &buffer);
+
+ if (!term_cr)
+ term_cr = "\r";
+
+#ifdef HACK_TERMCAP_MOTION
+ term_forward_char = tgetstr ("nd", &buffer);
+#endif /* HACK_TERMCAP_MOTION */
+
+ screenwidth = tgetnum ("co");
+ if (screenwidth <= 0)
+ screenwidth = 80;
+ screenwidth--; /* PWP: avoid autowrap bugs */
+
+ screenheight = tgetnum ("li");
+ if (screenheight <= 0)
+ screenheight = 24;
+
+ term_im = tgetstr ("im", &buffer);
+ term_ei = tgetstr ("ei", &buffer);
+ term_IC = tgetstr ("IC", &buffer);
+ term_ic = tgetstr ("ic", &buffer);
+ term_ip = tgetstr ("ip", &buffer);
+ term_IC = tgetstr ("IC", &buffer);
+
+ /* "An application program can assume that the terminal can do
+ character insertion if *any one of* the capabilities `IC',
+ `im', `ic' or `ip' is provided." */
+#ifdef notdef
+ /* XXX Circumvent broken code. */
+ terminal_can_insert = (term_IC || term_im || term_ic || term_ip);
+#endif
+
+ term_up = tgetstr ("up", &buffer);
+ term_dc = tgetstr ("dc", &buffer);
+ term_DC = tgetstr ("DC", &buffer);
+}
+
+/* A function for the use of tputs () */
+static void
+output_character_function (c)
+ int c;
+{
+ putc (c, out_stream);
+}
+
+/* Write COUNT characters from STRING to the output stream. */
+static void
+output_some_chars (string, count)
+ char *string;
+ int count;
+{
+ fwrite (string, 1, count, out_stream);
+}
+
+
+/* Delete COUNT characters from the display line. */
+static
+delete_chars (count)
+ int count;
+{
+ if (count > screenwidth)
+ return;
+
+ if (term_DC && *term_DC)
+ {
+ char *tgoto (), *buffer;
+ buffer = tgoto (term_DC, 0, count);
+ tputs (buffer, 1, output_character_function);
+ }
+ else
+ {
+ if (term_dc && *term_dc)
+ while (count--)
+ tputs (term_dc, 1, output_character_function);
+ }
+}
+
+/* Prepare to insert by inserting COUNT blank spaces. */
+static
+start_insert (count)
+ int count;
+{
+ if (term_im && *term_im)
+ tputs (term_im, 1, output_character_function);
+
+ if (term_IC && *term_IC &&
+ (count > 1 || !term_ic || !*term_ic))
+ {
+ char *tgoto (), *buffer;
+ buffer = tgoto (term_IC, 0, count);
+ tputs (buffer, 1, output_character_function);
+ }
+ else
+ {
+ if (term_ic && *term_ic)
+ while (count--)
+ tputs (term_ic, 1, output_character_function);
+ }
+}
+
+/* We are finished doing our insertion. Send ending string. */
+static
+end_insert ()
+{
+ if (term_ei && *term_ei)
+ tputs (term_ei, 1, output_character_function);
+}
+
+/* Move the cursor back. */
+backspace (count)
+ int count;
+{
+ register int i;
+
+ if (term_backspace)
+ for (i = 0; i < count; i++)
+ tputs (term_backspace, 1, output_character_function);
+ else
+ for (i = 0; i < count; i++)
+ putc ('\b', out_stream);
+}
+
+/* Move to the start of the next line. */
+crlf ()
+{
+ tputs (term_cr, 1, output_character_function);
+ putc ('\n', out_stream);
+}
+
+/* Clear to the end of the line. COUNT is the minimum
+ number of character spaces to clear, */
+clear_to_eol (count)
+ int count;
+{
+ if (term_clreol) {
+ tputs (term_clreol, 1, output_character_function);
+ } else {
+ register int i;
+ /* Do one more character space. */
+ count++;
+ for (i = 0; i < count; i++)
+ putc (' ', out_stream);
+ backspace (count);
+ }
+}
+
+
+/* **************************************************************** */
+/* */
+/* Saving and Restoring the TTY */
+/* */
+/* **************************************************************** */
+
+#ifdef NEW_TTY_DRIVER
+
+/* Standard flags, including ECHO. */
+static int original_tty_flags = 0;
+
+/* Local mode flags, like LPASS8. */
+static int local_mode_flags = 0;
+
+/* Terminal characters. This has C-s and C-q in it. */
+static struct tchars original_tchars;
+
+/* Local special characters. This has the interrupt characters in it. */
+static struct ltchars original_ltchars;
+
+/* We use this to get and set the tty_flags. */
+static struct sgttyb the_ttybuff;
+
+/* Put the terminal in CBREAK mode so that we can detect key presses. */
+static
+rl_prep_terminal ()
+{
+ int tty = fileno (rl_instream);
+
+ /* We always get the latest tty values. Maybe stty changed them. */
+
+ ioctl (tty, TIOCGETP, &the_ttybuff);
+ original_tty_flags = the_ttybuff.sg_flags;
+
+ readline_echoing_p = (original_tty_flags & ECHO);
+
+ /* If this terminal doesn't care how the 8th bit is used,
+ then we can use it for the meta-key.
+ We check by seeing if BOTH odd and even parity are allowed. */
+ if ((the_ttybuff.sg_flags & (ODDP | EVENP)) == (ODDP | EVENP))
+ {
+#ifdef PASS8
+ the_ttybuff.sg_flags |= PASS8;
+#endif
+
+#if defined (TIOCLGET) && defined (LPASS8)
+ {
+ int flags;
+ ioctl (tty, TIOCLGET, &flags);
+ local_mode_flags = flags;
+ flags |= LPASS8;
+ ioctl (tty, TIOCLSET, &flags);
+ }
+#endif
+ }
+
+#ifdef TIOCGETC
+ {
+ struct tchars temp;
+
+ ioctl (tty, TIOCGETC, &original_tchars);
+ bcopy (&original_tchars, &temp, sizeof (struct tchars));
+
+ /* Get rid of C-s and C-q.
+ We remember the value of startc (C-q) so that if the terminal is in
+ xoff state, the user can xon it by pressing that character. */
+ xon_char = temp.t_startc;
+ temp.t_stopc = -1;
+ temp.t_startc = -1;
+
+ /* If there is an XON character, bind it to restart the output. */
+ if (xon_char != -1)
+ rl_bind_key (xon_char, rl_restart_output);
+
+ /* If there is an EOF char, bind eof_char to it. */
+ if (temp.t_eofc != -1)
+ eof_char = temp.t_eofc;
+
+#ifdef NEVER
+ /* Get rid of C-\ and C-c. */
+ temp.t_intrc = temp.t_quitc = -1;
+#endif
+
+ ioctl (tty, TIOCSETC, &temp);
+ }
+#endif /* TIOCGETC */
+
+#ifdef TIOCGLTC
+ {
+ struct ltchars temp;
+
+ ioctl (tty, TIOCGLTC, &original_ltchars);
+ bcopy (&original_ltchars, &temp, sizeof (struct ltchars));
+
+ /* Make the interrupt keys go away. Just enough to make people happy. */
+ temp.t_dsuspc = -1; /* C-y */
+ temp.t_lnextc = -1; /* C-v */
+
+ ioctl (tty, TIOCSLTC, &temp);
+ }
+#endif /* TIOCGLTC */
+
+ the_ttybuff.sg_flags &= ~ECHO;
+ the_ttybuff.sg_flags |= CBREAK;
+ ioctl (tty, TIOCSETN, &the_ttybuff);
+}
+
+/* Restore the terminal to its original state. */
+static
+rl_deprep_terminal ()
+{
+ int tty = fileno (rl_instream);
+
+#if defined (TIOCLGET) && defined (LPASS8)
+ if ((the_ttybuff.sg_flags & (ODDP | EVENP)) == (ODDP | EVENP))
+ ioctl (tty, TIOCLSET, &local_mode_flags);
+#endif
+
+#ifdef TIOCSLTC
+ ioctl (tty, TIOCSLTC, &original_ltchars);
+#endif
+
+#ifdef TIOCSETC
+ ioctl (tty, TIOCSETC, &original_tchars);
+#endif
+
+ the_ttybuff.sg_flags = original_tty_flags;
+ ioctl (tty, TIOCSETN, &the_ttybuff);
+ readline_echoing_p = 1;
+}
+
+#else /* !defined (NEW_TTY_DRIVER) */
+static struct termio otio;
+
+static
+rl_prep_terminal ()
+{
+ int tty = fileno (rl_instream);
+ struct termio tio;
+
+ ioctl (tty, TCGETA, &tio);
+ ioctl (tty, TCGETA, &otio);
+
+ readline_echoing_p = (tio.c_lflag & ECHO);
+
+ tio.c_lflag &= ~(ICANON|ECHO);
+ tio.c_iflag &= ~(IXON|ISTRIP|INPCK);
+
+#ifndef HANDLE_SIGNALS
+ tio.c_lflag &= ~ISIG;
+#endif
+
+ tio.c_cc[VEOF] = 1; /* really: MIN */
+ tio.c_cc[VEOL] = 0; /* really: TIME */
+ ioctl (tty, TCSETAW,&tio);
+}
+
+static
+rl_deprep_terminal ()
+{
+ int tty = fileno (rl_instream);
+ ioctl (tty, TCSETAW, &otio);
+}
+#endif /* NEW_TTY_DRIVER */
+
+
+/* **************************************************************** */
+/* */
+/* Utility Functions */
+/* */
+/* **************************************************************** */
+
+/* Return 0 if C is not a member of the class of characters that belong
+ in words, or 1 if it is. */
+
+int allow_pathname_alphabetic_chars = 0;
+char *pathname_alphabetic_chars = "/-_=~.#$";
+
+int
+alphabetic (c)
+ int c;
+{
+ if (pure_alphabetic (c) || (numeric (c)))
+ return (1);
+
+ if (allow_pathname_alphabetic_chars)
+ return ((int)rindex (pathname_alphabetic_chars, c));
+ else
+ return (0);
+}
+
+/* Return non-zero if C is a numeric character. */
+int
+numeric (c)
+ int c;
+{
+ return (c >= '0' && c <= '9');
+}
+
+/* Ring the terminal bell. */
+int
+ding ()
+{
+ if (readline_echoing_p)
+ {
+ fprintf (stderr, "\007");
+ fflush (stderr);
+ }
+ return (-1);
+}
+
+/* How to abort things. */
+rl_abort ()
+{
+ ding ();
+ rl_clear_message ();
+ rl_init_argument ();
+ rl_pending_input = 0;
+
+ defining_kbd_macro = 0;
+ while (executing_macro)
+ pop_executing_macro ();
+
+ longjmp (readline_top_level, 1);
+}
+
+/* Return a copy of the string between FROM and TO.
+ FROM is inclusive, TO is not. */
+char *
+rl_copy (from, to)
+ int from, to;
+{
+ register int length;
+ char *copy;
+
+ /* Fix it if the caller is confused. */
+ if (from > to) {
+ int t = from;
+ from = to;
+ to = t;
+ }
+
+ length = to - from;
+ copy = (char *)xmalloc (1 + length);
+ strncpy (copy, the_line + from, length);
+ copy[length] = '\0';
+ return (copy);
+}
+
+
+/* **************************************************************** */
+/* */
+/* Insert and Delete */
+/* */
+/* **************************************************************** */
+
+
+/* Insert a string of text into the line at point. This is the only
+ way that you should do insertion. rl_insert () calls this
+ function. */
+rl_insert_text (string)
+ char *string;
+{
+ extern int doing_an_undo;
+ register int i, l = strlen (string);
+ while (rl_end + l >= rl_line_buffer_len)
+ {
+ rl_line_buffer =
+ (char *)xrealloc (rl_line_buffer,
+ rl_line_buffer_len += DEFAULT_BUFFER_SIZE);
+ the_line = rl_line_buffer;
+ }
+
+ for (i = rl_end; i >= rl_point; i--)
+ the_line[i + l] = the_line[i];
+ strncpy (the_line + rl_point, string, l);
+
+ /* Remember how to undo this if we aren't undoing something. */
+ if (!doing_an_undo)
+ {
+ /* If possible and desirable, concatenate the undos. */
+ if ((strlen (string) == 1) &&
+ rl_undo_list &&
+ (rl_undo_list->what == UNDO_INSERT) &&
+ (rl_undo_list->end == rl_point) &&
+ (rl_undo_list->end - rl_undo_list->start < 20))
+ rl_undo_list->end++;
+ else
+ rl_add_undo (UNDO_INSERT, rl_point, rl_point + l, (char *)NULL);
+ }
+ rl_point += l;
+ rl_end += l;
+ the_line[rl_end] = '\0';
+}
+
+/* Delete the string between FROM and TO. FROM is
+ inclusive, TO is not. */
+rl_delete_text (from, to)
+ int from, to;
+{
+ extern int doing_an_undo;
+ register char *text;
+
+ /* Fix it if the caller is confused. */
+ if (from > to) {
+ int t = from;
+ from = to;
+ to = t;
+ }
+ text = rl_copy (from, to);
+ strncpy (the_line + from, the_line + to, rl_end - to);
+
+ /* Remember how to undo this delete. */
+ if (!doing_an_undo)
+ rl_add_undo (UNDO_DELETE, from, to, text);
+ else
+ free (text);
+
+ rl_end -= (to - from);
+ the_line[rl_end] = '\0';
+}
+
+
+/* **************************************************************** */
+/* */
+/* Readline character functions */
+/* */
+/* **************************************************************** */
+
+/* This is not a gap editor, just a stupid line input routine. No hair
+ is involved in writing any of the functions, and none should be. */
+
+/* Note that:
+
+ rl_end is the place in the string that we would place '\0';
+ i.e., it is always safe to place '\0' there.
+
+ rl_point is the place in the string where the cursor is. Sometimes
+ this is the same as rl_end.
+
+ Any command that is called interactively receives two arguments.
+ The first is a count: the numeric arg pased to this command.
+ The second is the key which invoked this command.
+*/
+
+
+/* **************************************************************** */
+/* */
+/* Movement Commands */
+/* */
+/* **************************************************************** */
+
+/* Note that if you `optimize' the display for these functions, you cannot
+ use said functions in other functions which do not do optimizing display.
+ I.e., you will have to update the data base for rl_redisplay, and you
+ might as well let rl_redisplay do that job. */
+
+/* Move forward COUNT characters. */
+rl_forward (count)
+ int count;
+{
+ if (count < 0)
+ rl_backward (-count);
+ else
+ while (count)
+ {
+#ifdef VI_MODE
+ if (rl_point == (rl_end - (rl_editing_mode == vi_mode)))
+#else
+ if (rl_point == rl_end)
+#endif
+ {
+ ding ();
+ return;
+ }
+ else
+ rl_point++;
+ --count;
+ }
+}
+
+/* Move backward COUNT characters. */
+rl_backward (count)
+ int count;
+{
+ if (count < 0)
+ rl_forward (-count);
+ else
+ while (count)
+ {
+ if (!rl_point)
+ {
+ ding ();
+ return;
+ }
+ else
+ --rl_point;
+ --count;
+ }
+}
+
+/* Move to the beginning of the line. */
+rl_beg_of_line ()
+{
+ rl_point = 0;
+}
+
+/* Move to the end of the line. */
+rl_end_of_line ()
+{
+ rl_point = rl_end;
+}
+
+/* Move forward a word. We do what Emacs does. */
+rl_forward_word (count)
+ int count;
+{
+ int c;
+
+ if (count < 0)
+ {
+ rl_backward_word (-count);
+ return;
+ }
+
+ while (count)
+ {
+ if (rl_point == rl_end)
+ return;
+
+ /* If we are not in a word, move forward until we are in one.
+ Then, move forward until we hit a non-alphabetic character. */
+ c = the_line[rl_point];
+ if (!alphabetic (c))
+ {
+ while (++rl_point < rl_end)
+ {
+ c = the_line[rl_point];
+ if (alphabetic (c)) break;
+ }
+ }
+ if (rl_point == rl_end) return;
+ while (++rl_point < rl_end)
+ {
+ c = the_line[rl_point];
+ if (!alphabetic (c)) break;
+ }
+ --count;
+ }
+}
+
+/* Move backward a word. We do what Emacs does. */
+rl_backward_word (count)
+ int count;
+{
+ int c;
+
+ if (count < 0)
+ {
+ rl_forward_word (-count);
+ return;
+ }
+
+ while (count)
+ {
+ if (!rl_point)
+ return;
+
+ /* Like rl_forward_word (), except that we look at the characters
+ just before point. */
+
+ c = the_line[rl_point - 1];
+ if (!alphabetic (c))
+ {
+ while (--rl_point)
+ {
+ c = the_line[rl_point - 1];
+ if (alphabetic (c)) break;
+ }
+ }
+
+ while (rl_point)
+ {
+ c = the_line[rl_point - 1];
+ if (!alphabetic (c))
+ break;
+ else --rl_point;
+ }
+ --count;
+ }
+}
+
+/* Clear the current line. Numeric argument to C-l does this. */
+rl_refresh_line ()
+{
+ int curr_line = last_c_pos / screenwidth;
+
+ move_vert(curr_line);
+ move_cursor_relative (0, the_line); /* XXX is this right */
+ rl_forced_update_display ();
+ rl_display_fixed = 1;
+}
+
+/* C-l typed to a line without quoting clears the screen, and then reprints
+ the prompt and the current input line. Given a numeric arg, redraw only
+ the current line. */
+rl_clear_screen ()
+{
+ extern char *term_clrpag;
+ static void output_character_function ();
+
+ if (rl_explicit_arg)
+ {
+ rl_refresh_line ();
+ return;
+ }
+
+ if (term_clrpag)
+ tputs (term_clrpag, 1, output_character_function);
+ else
+ crlf ();
+
+ rl_forced_update_display ();
+ rl_display_fixed = 1;
+}
+
+
+/* **************************************************************** */
+/* */
+/* Text commands */
+/* */
+/* **************************************************************** */
+
+/* Insert the character C at the current location, moving point forward. */
+rl_insert (count, c)
+ int count, c;
+{
+ register int i;
+ char *string;
+
+ if (count <= 0)
+ return;
+
+ /* If we can optimize, then do it. But don't let people crash
+ readline because of extra large arguments. */
+ if (count > 1 && count < 1024)
+ {
+ string = (char *)alloca (1 + count);
+
+ for (i = 0; i < count; i++)
+ string[i] = c;
+
+ string[i] = '\0';
+ rl_insert_text (string);
+ return;
+ }
+
+ if (count > 1024)
+ {
+ int descreaser;
+
+ string = (char *)alloca (1024 + 1);
+
+ for (i = 0; i < 1024; i++)
+ string[i] = c;
+
+ while (count)
+ {
+ descreaser = (count > 1024 ? 1024 : count);
+ string[descreaser] = '\0';
+ rl_insert_text (string);
+ count -= descreaser;
+ }
+ return;
+ }
+
+ /* We are inserting a single character.
+ If there is pending input, then make a string of all of the
+ pending characters that are bound to rl_insert, and insert
+ them all. */
+ if (any_typein)
+ {
+ int slen, key = 0, t;
+
+ i = 0;
+ string = (char *)alloca (ibuffer_len + 1);
+ string[i++] = c;
+
+ while ((key = rl_get_char()) != -2 &&
+ (keymap[key].type == ISFUNC &&
+ keymap[key].function == rl_insert))
+ string[i++] = key;
+
+ if (key != -2)
+ rl_unget_char (key);
+
+ string[i] = '\0';
+ rl_insert_text (string);
+ return;
+ }
+ else
+ {
+ /* Inserting a single character. */
+ string = (char *)alloca (2);
+
+ string[1] = '\0';
+ string[0] = c;
+ rl_insert_text (string);
+ }
+}
+
+/* Insert the next typed character verbatim. */
+rl_quoted_insert (count)
+ int count;
+{
+ int c = rl_read_key (in_stream);
+ rl_insert (count, c);
+}
+
+/* Insert a tab character. */
+rl_tab_insert (count)
+ int count;
+{
+ rl_insert (count, '\t');
+}
+
+#ifdef VI_MODE
+/* Non-zero means enter insertion mode. */
+static vi_doing_insert = 0;
+#endif
+
+/* What to do when a NEWLINE is pressed. We accept the whole line.
+ KEY is the key that invoked this command. I guess it could have
+ meaning in the future. */
+rl_newline (count, key)
+ int count, key;
+{
+
+ rl_done = 1;
+
+#ifdef VI_MODE
+ {
+ if (vi_doing_insert)
+ {
+ rl_end_undo_group ();
+ vi_doing_insert = 0;
+ }
+ }
+#endif /* VI_MODE */
+
+ if (readline_echoing_p)
+ {
+ move_vert (vis_botlin);
+ vis_botlin = 0;
+ crlf ();
+ fflush (out_stream);
+ rl_display_fixed++;
+ }
+}
+
+rl_clean_up_for_exit ()
+{
+ if (readline_echoing_p)
+ {
+ move_vert (vis_botlin);
+ vis_botlin = 0;
+ fflush (out_stream);
+ rl_restart_output ();
+ }
+}
+
+/* What to do for some uppercase characters, like meta characters,
+ and some characters appearing in emacs_ctlx_keymap. This function
+ is just a stub, you bind keys to it and the code in rl_dispatch ()
+ is special cased. */
+rl_do_lowercase_version (ignore1, ignore2)
+ int ignore1, ignore2;
+{
+}
+
+/* Rubout the character behind point. */
+rl_rubout (count)
+ int count;
+{
+ if (count < 0)
+ {
+ rl_delete (-count);
+ return;
+ }
+
+ if (!rl_point)
+ {
+ ding ();
+ return;
+ }
+
+ if (count > 1)
+ {
+ int orig_point = rl_point;
+ rl_backward (count);
+ rl_kill_text (orig_point, rl_point);
+ }
+ else
+ {
+ int c = the_line[--rl_point];
+ rl_delete_text (rl_point, rl_point + 1);
+
+ if (rl_point == rl_end && alphabetic (c) && last_c_pos)
+ {
+ backspace (1);
+ putc (' ', out_stream);
+ backspace (1);
+ last_c_pos--;
+ rl_display_fixed++;
+ }
+ }
+}
+
+/* Delete the character under the cursor. Given a numeric argument,
+ kill that many characters instead. */
+rl_delete (count, invoking_key)
+ int count;
+{
+ if (count < 0)
+ {
+ rl_rubout (-count);
+ return;
+ }
+
+ if (rl_point == rl_end)
+ {
+ ding ();
+ return;
+ }
+
+#ifdef VI_MODE
+ if ((count > 1) || ((count == 1) && (rl_editing_mode == vi_mode)))
+#else
+ if (count > 1)
+#endif
+ {
+ int orig_point = rl_point;
+ while (count && (rl_point < rl_end))
+ {
+ rl_point++;
+ count--;
+ }
+ rl_kill_text (orig_point, rl_point);
+ rl_point = orig_point;
+ }
+ else
+ rl_delete_text (rl_point, rl_point + 1);
+}
+
+
+/* **************************************************************** */
+/* */
+/* Kill commands */
+/* */
+/* **************************************************************** */
+
+/* The next two functions mimic unix line editing behaviour, except they
+ save the deleted text on the kill ring. This is safer than not saving
+ it, and since we have a ring, nobody should get screwed. */
+
+/* This does what C-w does in Unix. We can't prevent people from
+ using behaviour that they expect. */
+rl_unix_word_rubout ()
+{
+ if (!rl_point) ding ();
+ else {
+ int orig_point = rl_point;
+ while (rl_point && whitespace (the_line[rl_point - 1]))
+ rl_point--;
+ while (rl_point && !whitespace (the_line[rl_point - 1]))
+ rl_point--;
+ rl_kill_text (rl_point, orig_point);
+ }
+}
+
+/* Here is C-u doing what Unix does. You don't *have* to use these
+ key-bindings. We have a choice of killing the entire line, or
+ killing from where we are to the start of the line. We choose the
+ latter, because if you are a Unix weenie, then you haven't backspaced
+ into the line at all, and if you aren't, then you know what you are
+ doing. */
+rl_unix_line_discard ()
+{
+ if (!rl_point) ding ();
+ else {
+ rl_kill_text (rl_point, 0);
+ rl_point = 0;
+ }
+}
+
+
+
+/* **************************************************************** */
+/* */
+/* Commands For Typos */
+/* */
+/* **************************************************************** */
+
+/* Random and interesting things in here. */
+
+
+/* **************************************************************** */
+/* */
+/* Changing Case */
+/* */
+/* **************************************************************** */
+
+/* The three kinds of things that we know how to do. */
+#define UpCase 1
+#define DownCase 2
+#define CapCase 3
+
+/* Uppercase the word at point. */
+rl_upcase_word (count)
+ int count;
+{
+ rl_change_case (count, UpCase);
+}
+
+/* Lowercase the word at point. */
+rl_downcase_word (count)
+ int count;
+{
+ rl_change_case (count, DownCase);
+}
+
+/* Upcase the first letter, downcase the rest. */
+rl_capitalize_word (count)
+ int count;
+{
+ rl_change_case (count, CapCase);
+}
+
+/* The meaty function.
+ Change the case of COUNT words, performing OP on them.
+ OP is one of UpCase, DownCase, or CapCase.
+ If a negative argument is given, leave point where it started,
+ otherwise, leave it where it moves to. */
+rl_change_case (count, op)
+ int count, op;
+{
+ register int start = rl_point, end;
+ int state = 0;
+
+ rl_forward_word (count);
+ end = rl_point;
+
+ if (count < 0)
+ {
+ int temp = start;
+ start = end;
+ end = temp;
+ }
+
+ /* We are going to modify some text, so let's prepare to undo it. */
+ rl_modifying (start, end);
+
+ for (; start < end; start++)
+ {
+ switch (op)
+ {
+ case UpCase:
+ the_line[start] = to_upper (the_line[start]);
+ break;
+
+ case DownCase:
+ the_line[start] = to_lower (the_line[start]);
+ break;
+
+ case CapCase:
+ if (state == 0)
+ {
+ the_line[start] = to_upper (the_line[start]);
+ state = 1;
+ }
+ else
+ {
+ the_line[start] = to_lower (the_line[start]);
+ }
+ if (!pure_alphabetic (the_line[start]))
+ state = 0;
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ rl_point = end;
+}
+
+/* **************************************************************** */
+/* */
+/* Transposition */
+/* */
+/* **************************************************************** */
+
+/* Transpose the words at point. */
+rl_transpose_words (count)
+ int count;
+{
+ char *word1, *word2;
+ int w1_beg, w1_end, w2_beg, w2_end;
+ int orig_point = rl_point;
+
+ if (!count) return;
+
+ /* Find the two words. */
+ rl_forward_word (count);
+ w2_end = rl_point;
+ rl_backward_word (1);
+ w2_beg = rl_point;
+ rl_backward_word (count);
+ w1_beg = rl_point;
+ rl_forward_word (1);
+ w1_end = rl_point;
+
+ /* Do some check to make sure that there really are two words. */
+ if ((w1_beg == w2_beg) || (w2_beg < w1_end))
+ {
+ ding ();
+ rl_point = orig_point;
+ return;
+ }
+
+ /* Get the text of the words. */
+ word1 = rl_copy (w1_beg, w1_end);
+ word2 = rl_copy (w2_beg, w2_end);
+
+ /* We are about to do many insertions and deletions. Remember them
+ as one operation. */
+ rl_begin_undo_group ();
+
+ /* Do the stuff at word2 first, so that we don't have to worry
+ about word1 moving. */
+ rl_point = w2_beg;
+ rl_delete_text (w2_beg, w2_end);
+ rl_insert_text (word1);
+
+ rl_point = w1_beg;
+ rl_delete_text (w1_beg, w1_end);
+ rl_insert_text (word2);
+
+ /* This is exactly correct since the text before this point has not
+ changed in length. */
+ rl_point = w2_end;
+
+ /* I think that does it. */
+ rl_end_undo_group ();
+ free (word1); free (word2);
+}
+
+/* Transpose the characters at point. If point is at the end of the line,
+ then transpose the characters before point. */
+rl_transpose_chars (count)
+ int count;
+{
+ if (!count)
+ return;
+
+ if (!rl_point || rl_end < 2) {
+ ding ();
+ return;
+ }
+
+ while (count) {
+ if (rl_point == rl_end) {
+ int t = the_line[rl_point - 1];
+ the_line[rl_point - 1] = the_line[rl_point - 2];
+ the_line[rl_point - 2] = t;
+ } else {
+ int t = the_line[rl_point];
+ the_line[rl_point] = the_line[rl_point - 1];
+ the_line[rl_point - 1] = t;
+ if (count < 0 && rl_point)
+ rl_point--;
+ else
+ rl_point++;
+ }
+ if (count < 0)
+ count++;
+ else
+ count--;
+ }
+}
+
+
+/* **************************************************************** */
+/* */
+/* Bogus Flow Control */
+/* */
+/* **************************************************************** */
+
+rl_restart_output (count, key)
+ int count, key;
+{
+ int fildes = fileno (stdin);
+#ifdef TIOCSTART
+ ioctl (fildes, TIOCSTART, 0);
+#endif /* TIOCSTART */
+}
+
+/* **************************************************************** */
+/* */
+/* Completion matching, from readline's point of view. */
+/* */
+/* **************************************************************** */
+
+/* Pointer to the generator function for completion_matches ().
+ NULL means to use filename_entry_function (), the default filename
+ completer. */
+Function *rl_completion_entry_function = (Function *)NULL;
+
+/* Pointer to alternative function to create matches.
+ Function is called with TEXT, START, and END.
+ START and END are indices in RL_LINE_BUFFER saying what the boundaries
+ of TEXT are.
+ If this function exists and returns NULL then call the value of
+ rl_completion_entry_function to try to match, otherwise use the
+ array of strings returned. */
+Function *rl_attempted_completion_function = (Function *)NULL;
+
+/* Complete the word at or before point. You have supplied the function
+ that does the initial simple matching selection algorithm (see
+ completion_matches ()). The default is to do filename completion. */
+rl_complete (ignore, invoking_key)
+ int ignore, invoking_key;
+{
+ rl_complete_internal (TAB);
+ if (running_in_emacs)
+ printf ("%s", the_line);
+}
+
+/* List the possible completions. See description of rl_complete (). */
+rl_possible_completions ()
+{
+ rl_complete_internal ('?');
+}
+
+/* The user must press "y" or "n". Non-zero return means "y" pressed. */
+get_y_or_n ()
+{
+ int c;
+ loop:
+ c = rl_read_key (in_stream);
+ if (c == 'y' || c == 'Y') return (1);
+ if (c == 'n' || c == 'N') return (0);
+ if (c == ABORT_CHAR) rl_abort ();
+ ding (); goto loop;
+}
+
+/* Up to this many items will be displayed in response to a
+ possible-completions call. After that, we ask the user if
+ she is sure she wants to see them all. */
+int rl_completion_query_items = 100;
+
+/* The basic list of characters that signal a break between words for the
+ completer routine. The contents of this variable is what breaks words
+ in the shell, i.e. " \t\n\"\\'`@$><=" */
+char *rl_basic_word_break_characters = " \t\n\"\\'`@$><=";
+
+/* The list of characters that signal a break between words for
+ rl_complete_internal. The default list is the contents of
+ rl_basic_word_break_characters. */
+char *rl_completer_word_break_characters = (char *)NULL;
+
+/* List of characters that are word break characters, but should be left
+ in TEXT when it is passed to the completion function. The shell uses
+ this to help determine what kind of completing to do. */
+char *rl_special_prefixes = (char *)NULL;
+
+/* If non-zero, then disallow duplicates in the matches. */
+int rl_ignore_completion_duplicates = 1;
+
+/* Non-zero means that the results of the matches are to be treated
+ as filenames. This is ALWAYS zero on entry, and can only be changed
+ within a completion entry finder function. */
+int rl_filename_completion_desired = 0;
+
+/* Complete the word at or before point.
+ WHAT_TO_DO says what to do with the completion.
+ `?' means list the possible completions.
+ TAB means do standard completion.
+ `*' means insert all of the possible completions. */
+rl_complete_internal (what_to_do)
+ int what_to_do;
+{
+ char *filename_completion_function ();
+ char **completion_matches (), **matches;
+ Function *our_func;
+ int start, end, delimiter = 0;
+ char *text;
+
+ if (rl_completion_entry_function)
+ our_func = rl_completion_entry_function;
+ else
+ our_func = (int (*)())filename_completion_function;
+
+ /* Only the completion entry function can change this. */
+ rl_filename_completion_desired = 0;
+
+ /* We now look backwards for the start of a filename/variable word. */
+ end = rl_point;
+ if (rl_point)
+ {
+ while (--rl_point &&
+ !rindex (rl_completer_word_break_characters, the_line[rl_point]));
+
+ /* If we are at a word break, then advance past it. */
+ if (rindex (rl_completer_word_break_characters, (the_line[rl_point])))
+ {
+ /* If the character that caused the word break was a quoting
+ character, then remember it as the delimiter. */
+ if (rindex ("\"'", the_line[rl_point]) && (end - rl_point) > 1)
+ delimiter = the_line[rl_point];
+
+ /* If the character isn't needed to determine something special
+ about what kind of completion to perform, then advance past it. */
+
+ if (!rl_special_prefixes ||
+ !rindex (rl_special_prefixes, the_line[rl_point]))
+ rl_point++;
+ }
+ }
+
+ start = rl_point;
+ rl_point = end;
+ text = rl_copy (start, end);
+
+ /* If the user wants to TRY to complete, but then wants to give
+ up and use the default completion function, they set the
+ variable rl_attempted_completion_function. */
+ if (rl_attempted_completion_function)
+ {
+ matches =
+ (char **)(*rl_attempted_completion_function) (text, start, end);
+
+ if (matches)
+ goto after_usual_completion;
+ }
+
+ matches = completion_matches (text, our_func, start, end);
+
+ after_usual_completion:
+ free (text);
+
+ if (!matches)
+ ding ();
+ else
+ {
+ register int i;
+
+ some_matches:
+
+ /* It seems to me that in all the cases we handle we would like
+ to ignore duplicate possiblilities. Scan for the text to
+ insert being identical to the other completions. */
+ if (rl_ignore_completion_duplicates)
+ {
+ char *lowest_common;
+ int j, newlen = 0;
+
+ /* Sort the items. */
+ /* It is safe to sort this array, because the lowest common
+ denominator found in matches[0] will remain in place. */
+ for (i = 0; matches[i]; i++);
+ qsort (matches, i, sizeof (char *), compare_strings);
+
+ /* Remember the lowest common denimator for it may be unique. */
+ lowest_common = savestring (matches[0]);
+
+ for (i = 0; matches[i + 1]; i++)
+ {
+ if (strcmp (matches[i], matches[i + 1]) == 0)
+ {
+ free (matches[i]);
+ matches[i] = (char *)-1;
+ }
+ else
+ newlen++;
+ }
+
+ /* We have marked all the dead slots with (char *)-1.
+ Copy all the non-dead entries into a new array. */
+ {
+ char **temp_array =
+ (char **)malloc ((3 + newlen) * sizeof (char *));
+
+ for (i = 1, j = 1; matches[i]; i++)
+ if (matches[i] != (char *)-1)
+ temp_array[j++] = matches[i];
+ temp_array[j] = (char *)NULL;
+
+ if (matches[0] != (char *)-1)
+ free (matches[0]);
+ free (matches);
+
+ matches = temp_array;
+ }
+
+ /* Place the lowest common denominator back in [0]. */
+ matches[0] = lowest_common;
+
+ /* If there is one string left, and it is identical to the
+ lowest common denominator, then the LCD is the string to
+ insert. */
+ if (j == 2 && strcmp (matches[0], matches[1]) == 0)
+ {
+ free (matches[1]);
+ matches[1] = (char *)NULL;
+ }
+ }
+
+ switch (what_to_do)
+ {
+ case TAB:
+ rl_delete_text (start, rl_point);
+ rl_point = start;
+ rl_insert_text (matches[0]);
+
+ /* If there are more matches, ring the bell to indicate.
+ If this was the only match, and we are hacking files,
+ check the file to see if it was a directory. If so,
+ add a '/' to the name. If not, and we are at the end
+ of the line, then add a space. */
+ if (matches[1])
+ {
+ ding (); /* There are other matches remaining. */
+ }
+ else
+ {
+ char temp_string[2];
+
+ temp_string[0] = delimiter ? delimiter : ' ';
+ temp_string[1] = '\0';
+
+ if (rl_filename_completion_desired)
+ {
+ struct stat finfo;
+ char *tilde_expand ();
+ char *filename = tilde_expand (matches[0]);
+
+ if ((stat (filename, &finfo) == 0) &&
+ ((finfo.st_mode & S_IFMT) == S_IFDIR))
+ {
+ if (the_line[rl_point] != '/')
+ rl_insert_text ("/");
+ }
+ else
+ {
+ if (rl_point == rl_end)
+ rl_insert_text (temp_string);
+ }
+ free (filename);
+ }
+ else
+ {
+ if (rl_point == rl_end)
+ rl_insert_text (temp_string);
+ }
+ }
+ break;
+
+ case '*':
+ {
+ int i = 1;
+
+ rl_delete_text (start, rl_point);
+ rl_point = start;
+ rl_begin_undo_group ();
+ if (matches[1])
+ {
+ while (matches[i])
+ {
+ rl_insert_text (matches[i++]);
+ rl_insert_text (" ");
+ }
+ }
+ else
+ {
+ rl_insert_text (matches[0]);
+ rl_insert_text (" ");
+ }
+ rl_end_undo_group ();
+ }
+ break;
+
+
+ case '?':
+ {
+ int len, count, limit, max = 0;
+ int j, k, l;
+
+ /* Handle simple case first. What if there is only one answer? */
+ if (!matches[1])
+ {
+ char *temp;
+
+ if (rl_filename_completion_desired)
+ temp = rindex (matches[0], '/');
+ else
+ temp = (char *)NULL;
+
+ if (!temp)
+ temp = matches[0];
+ else
+ temp++;
+
+ crlf ();
+ fprintf (out_stream, "%s", temp);
+ crlf ();
+ goto restart;
+ }
+
+ /* There is more than one answer. Find out how many there are,
+ and find out what the maximum printed length of a single entry
+ is. */
+ for (i = 1; matches[i]; i++)
+ {
+ char *temp = (char *)NULL;
+
+ /* If we are hacking filenames, then only count the characters
+ after the last slash in the pathname. */
+ if (rl_filename_completion_desired)
+ temp = rindex (matches[i], '/');
+ else
+ temp = (char *)NULL;
+
+ if (!temp)
+ temp = matches[i];
+ else
+ temp++;
+
+ if (strlen (temp) > max)
+ max = strlen (temp);
+ }
+
+ len = i;
+
+ /* If there are many items, then ask the user if she
+ really wants to see them all. */
+ if (len >= rl_completion_query_items)
+ {
+ crlf ();
+ fprintf (out_stream,
+ "There are %d possibilities. Do you really", len);
+ crlf ();
+ fprintf (out_stream, "wish to see them all? (y or n)");
+ fflush (out_stream);
+ if (!get_y_or_n ())
+ {
+ crlf ();
+ goto restart;
+ }
+ }
+ /* How many items of MAX length can we fit in the screen window? */
+ max += 2;
+ limit = screenwidth / max;
+ if (limit != 1 && (limit * max == screenwidth))
+ limit--;
+
+ /* How many iterations of the printing loop? */
+ count = (len + (limit - 1)) / limit;
+
+ /* Watch out for special case. If LEN is less than LIMIT, then
+ just do the inner printing loop. */
+ if (len < limit) count = 1;
+
+ /* Sort the items if they are not already sorted. */
+ if (!rl_ignore_completion_duplicates)
+ {
+ qsort (matches, len, sizeof (char *), compare_strings);
+ }
+
+ /* Print the sorted items, up-and-down alphabetically, like
+ ls might. */
+ crlf ();
+
+ for (i = 1; i < count + 1; i++)
+ {
+ for (j = 0, l = i; j < limit; j++)
+ {
+ if (l > len || !matches[l])
+ {
+ break;
+ }
+ else
+ {
+ char *temp = (char *)NULL;
+
+ if (rl_filename_completion_desired)
+ temp = rindex (matches[l], '/');
+ else
+ temp = (char *)NULL;
+
+ if (!temp)
+ temp = matches[l];
+ else
+ temp++;
+
+ fprintf (out_stream, "%s", temp);
+ for (k = 0; k < max - strlen (temp); k++)
+ putc (' ', out_stream);
+ }
+ l += count;
+ }
+ crlf ();
+ }
+ restart:
+
+ rl_on_new_line ();
+ }
+ break;
+
+ default:
+ abort ();
+ }
+
+ for (i = 0; matches[i]; i++)
+ free (matches[i]);
+ free (matches);
+ }
+}
+
+/* A completion function for usernames.
+ TEXT contains a partial username preceded by a random
+ character (usually `~'). */
+char *
+username_completion_function (text, state)
+ int state;
+ char *text;
+{
+ static char *username = (char *)NULL;
+ static struct passwd *entry;
+ static int namelen;
+
+ if (!state)
+ {
+ if (username)
+ free (username);
+ username = savestring (&text[1]);
+ namelen = strlen (username);
+ setpwent ();
+ }
+
+ while (entry = getpwent ())
+ {
+ if (strncmp (username, entry->pw_name, namelen) == 0)
+ break;
+ }
+
+ if (!entry)
+ {
+ endpwent ();
+ return ((char *)NULL);
+ }
+ else
+ {
+ char *value = (char *)xmalloc (2 + strlen (entry->pw_name));
+ *value = *text;
+ strcpy (value + 1, entry->pw_name);
+ rl_filename_completion_desired = 1;
+ return (value);
+ }
+}
+
+/* If non-null, this contains the address of a function to call if the
+ standard meaning for expanding a tilde fails. The function is called
+ with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
+ which is the expansion, or a NULL pointer if there is no expansion. */
+Function *rl_tilde_expander = (Function *)NULL;
+
+/* Expand FILENAME if it begins with a tilde. This always returns
+ a new string. */
+char *
+tilde_expand (filename)
+ char *filename;
+{
+ char *dirname = filename ? savestring (filename) : (char *)NULL;
+
+ if (dirname && *dirname == '~')
+ {
+ char *temp_name;
+ if (!dirname[1] || dirname[1] == '/')
+ {
+ /* Prepend $HOME to the rest of the string. */
+ char *temp_home = (char *)getenv ("HOME");
+
+ temp_name = (char *)alloca (1 + strlen (&dirname[1])
+ + (temp_home? strlen (temp_home) : 0));
+ temp_name[0] = '\0';
+ if (temp_home)
+ strcpy (temp_name, temp_home);
+ strcat (temp_name, &dirname[1]);
+ free (dirname);
+ dirname = savestring (temp_name);
+ }
+ else
+ {
+ struct passwd *getpwnam (), *user_entry;
+ char *username = (char *)alloca (257);
+ int i, c;
+
+ for (i = 1; c = dirname[i]; i++)
+ {
+ if (c == '/') break;
+ else username[i - 1] = c;
+ }
+ username[i - 1] = '\0';
+
+ if (!(user_entry = getpwnam (username)))
+ {
+ /* If the calling program has a special syntax for
+ expanding tildes, and we couldn't find a standard
+ expansion, then let them try. */
+ if (rl_tilde_expander)
+ {
+ char *expansion;
+
+ expansion = (char *)(*rl_tilde_expander) (username);
+
+ if (expansion)
+ {
+ temp_name = (char *)alloca (1 + strlen (expansion)
+ + strlen (&dirname[i]));
+ strcpy (temp_name, expansion);
+ strcat (temp_name, &dirname[i]);
+ free (expansion);
+ goto return_name;
+ }
+ }
+ /*
+ * We shouldn't report errors.
+ */
+ }
+ else
+ {
+ temp_name = (char *)alloca (1 + strlen (user_entry->pw_dir)
+ + strlen (&dirname[i]));
+ strcpy (temp_name, user_entry->pw_dir);
+ strcat (temp_name, &dirname[i]);
+ return_name:
+ free (dirname);
+ dirname = savestring (temp_name);
+ }
+ }
+ }
+ return (dirname);
+}
+
+
+/* **************************************************************** */
+/* */
+/* Undo, and Undoing */
+/* */
+/* **************************************************************** */
+
+/* Non-zero tells rl_delete_text and rl_insert_text to not add to
+ the undo list. */
+int doing_an_undo = 0;
+
+/* The current undo list for THE_LINE. */
+UNDO_LIST *rl_undo_list = (UNDO_LIST *)NULL;
+
+/* Remember how to undo something. Concatenate some undos if that
+ seems right. */
+rl_add_undo (what, start, end, text)
+ enum undo_code what;
+ int start, end;
+ char *text;
+{
+ UNDO_LIST *temp = (UNDO_LIST *)xmalloc (sizeof (UNDO_LIST));
+ temp->what = what;
+ temp->start = start;
+ temp->end = end;
+ temp->text = text;
+ temp->next = rl_undo_list;
+ rl_undo_list = temp;
+}
+
+/* Free the existing undo list. */
+free_undo_list ()
+{
+ while (rl_undo_list) {
+ UNDO_LIST *release = rl_undo_list;
+ rl_undo_list = rl_undo_list->next;
+
+ if (release->what == UNDO_DELETE)
+ free (release->text);
+
+ free (release);
+ }
+}
+
+/* Undo the next thing in the list. Return 0 if there
+ is nothing to undo, or non-zero if there was. */
+int
+rl_do_undo ()
+{
+ UNDO_LIST *release;
+ int waiting_for_begin = 0;
+
+undo_thing:
+ if (!rl_undo_list)
+ return (0);
+
+ doing_an_undo = 1;
+
+ switch (rl_undo_list->what) {
+
+ /* Undoing deletes means inserting some text. */
+ case UNDO_DELETE:
+ rl_point = rl_undo_list->start;
+ rl_insert_text (rl_undo_list->text);
+ free (rl_undo_list->text);
+ break;
+
+ /* Undoing inserts means deleting some text. */
+ case UNDO_INSERT:
+ rl_delete_text (rl_undo_list->start, rl_undo_list->end);
+ rl_point = rl_undo_list->start;
+ break;
+
+ /* Undoing an END means undoing everything 'til we get to
+ a BEGIN. */
+ case UNDO_END:
+ waiting_for_begin++;
+ break;
+
+ /* Undoing a BEGIN means that we are done with this group. */
+ case UNDO_BEGIN:
+ if (waiting_for_begin)
+ waiting_for_begin--;
+ else
+ abort ();
+ break;
+ }
+
+ doing_an_undo = 0;
+
+ release = rl_undo_list;
+ rl_undo_list = rl_undo_list->next;
+ free (release);
+
+ if (waiting_for_begin)
+ goto undo_thing;
+
+ return (1);
+}
+
+/* Begin a group. Subsequent undos are undone as an atomic operation. */
+rl_begin_undo_group ()
+{
+ rl_add_undo (UNDO_BEGIN, 0, 0, 0);
+}
+
+/* End an undo group started with rl_begin_undo_group (). */
+rl_end_undo_group ()
+{
+ rl_add_undo (UNDO_END, 0, 0, 0);
+}
+
+/* Save an undo entry for the text from START to END. */
+rl_modifying (start, end)
+ int start, end;
+{
+ if (start > end)
+ {
+ int t = start;
+ start = end;
+ end = t;
+ }
+
+ if (start != end)
+ {
+ char *temp = rl_copy (start, end);
+ rl_begin_undo_group ();
+ rl_add_undo (UNDO_DELETE, start, end, temp);
+ rl_add_undo (UNDO_INSERT, start, end, (char *)NULL);
+ rl_end_undo_group ();
+ }
+}
+
+/* Revert the current line to its previous state. */
+rl_revert_line ()
+{
+ if (!rl_undo_list) ding ();
+ else {
+ while (rl_undo_list)
+ rl_do_undo ();
+ }
+}
+
+/* Do some undoing of things that were done. */
+rl_undo_command (count)
+{
+ if (count < 0) return; /* Nothing to do. */
+
+ while (count)
+ {
+ if (rl_do_undo ())
+ {
+ count--;
+ }
+ else
+ {
+ ding ();
+ break;
+ }
+ }
+}
+
+/* **************************************************************** */
+/* */
+/* History Utilities */
+/* */
+/* **************************************************************** */
+
+/* We already have a history library, and that is what we use to control
+ the history features of readline. However, this is our local interface
+ to the history mechanism. */
+
+/* While we are editing the history, this is the saved
+ version of the original line. */
+HIST_ENTRY *saved_line_for_history = (HIST_ENTRY *)NULL;
+
+/* Set the history pointer back to the last entry in the history. */
+start_using_history ()
+{
+ using_history ();
+ if (saved_line_for_history)
+ free_history_entry (saved_line_for_history);
+
+ saved_line_for_history = (HIST_ENTRY *)NULL;
+}
+
+/* Free the contents (and containing structure) of a HIST_ENTRY. */
+free_history_entry (entry)
+ HIST_ENTRY *entry;
+{
+ if (!entry) return;
+ if (entry->line)
+ free (entry->line);
+ free (entry);
+}
+
+/* Perhaps put back the current line if it has changed. */
+maybe_replace_line ()
+{
+ HIST_ENTRY *temp = current_history ();
+
+ /* If the current line has changed, save the changes. */
+ if (temp && ((UNDO_LIST *)(temp->data) != rl_undo_list)) {
+ temp = replace_history_entry (where_history (), the_line, rl_undo_list);
+ free (temp->line);
+ free (temp);
+ }
+}
+
+/* Put back the saved_line_for_history if there is one. */
+maybe_unsave_line ()
+{
+ if (saved_line_for_history) {
+ strcpy (the_line, saved_line_for_history->line);
+ rl_undo_list = (UNDO_LIST *)saved_line_for_history->data;
+ free_history_entry (saved_line_for_history);
+ saved_line_for_history = (HIST_ENTRY *)NULL;
+ rl_end = rl_point = strlen (the_line);
+ } else {
+ ding ();
+ }
+}
+
+/* Save the current line in saved_line_for_history. */
+maybe_save_line ()
+{
+ if (!saved_line_for_history) {
+ saved_line_for_history = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
+ saved_line_for_history->line = savestring (the_line);
+ saved_line_for_history->data = (char *)rl_undo_list;
+ }
+}
+
+
+
+/* **************************************************************** */
+/* */
+/* History Commands */
+/* */
+/* **************************************************************** */
+
+/* Meta-< goes to the start of the history. */
+rl_beginning_of_history ()
+{
+ rl_get_previous_history (1 + where_history ());
+}
+
+/* Meta-> goes to the end of the history. (The current line). */
+rl_end_of_history ()
+{
+ maybe_replace_line ();
+ using_history ();
+ maybe_unsave_line ();
+}
+
+/* Move down to the next history line. */
+rl_get_next_history (count)
+ int count;
+{
+ HIST_ENTRY *temp = (HIST_ENTRY *)NULL;
+
+ if (count < 0)
+ {
+ rl_get_previous_history (-count);
+ return;
+ }
+
+ if (!count)
+ return;
+
+ maybe_replace_line ();
+
+ while (count)
+ {
+ temp = next_history ();
+ if (!temp)
+ break;
+ --count;
+ }
+
+ if (!temp)
+ maybe_unsave_line ();
+ else
+ {
+ strcpy (the_line, temp->line);
+ rl_undo_list = (UNDO_LIST *)temp->data;
+ rl_end = rl_point = strlen (the_line);
+ }
+}
+
+/* Get the previous item out of our interactive history, making it the current
+ line. If there is no previous history, just ding. */
+rl_get_previous_history (count)
+ int count;
+{
+ HIST_ENTRY *old_temp = (HIST_ENTRY *)NULL;
+ HIST_ENTRY *temp = (HIST_ENTRY *)NULL;
+
+ if (count < 0)
+ {
+ rl_get_next_history (-count);
+ return;
+ }
+
+ if (!count)
+ return;
+
+ /* If we don't have a line saved, then save this one. */
+ maybe_save_line ();
+
+ /* If the current line has changed, save the changes. */
+ maybe_replace_line ();
+
+ while (count)
+ {
+ temp = previous_history ();
+ if (!temp)
+ break;
+ else
+ old_temp = temp;
+ --count;
+ }
+
+ /* If there was a large argument, and we moved back to the start of the
+ history, that is not an error. So use the last value found. */
+ if (!temp && old_temp)
+ temp = old_temp;
+
+ if (!temp)
+ ding ();
+ else
+ {
+ strcpy (the_line, temp->line);
+ rl_undo_list = (UNDO_LIST *)temp->data;
+ rl_end = rl_point = strlen (the_line);
+#ifdef VI_MODE
+ if (rl_editing_mode == vi_mode)
+ rl_point = 0;
+#endif /* VI_MODE */
+ }
+}
+
+/* There is a command in ksh which yanks into this line, the last word
+ of the previous line. Here it is. We left it on M-. */
+rl_yank_previous_last_arg (ignore)
+ int ignore;
+{
+}
+
+
+
+/* **************************************************************** */
+/* */
+/* I-Search and Searching */
+/* */
+/* **************************************************************** */
+
+/* Search backwards through the history looking for a string which is typed
+ interactively. Start with the current line. */
+rl_reverse_search_history (sign, key)
+ int sign;
+ int key;
+{
+ rl_search_history (-sign, key);
+}
+
+/* Search forwards through the history looking for a string which is typed
+ interactively. Start with the current line. */
+rl_forward_search_history (sign, key)
+ int sign;
+ int key;
+{
+ rl_search_history (sign, key);
+}
+
+/* Display the current state of the search in the echo-area.
+ SEARCH_STRING contains the string that is being searched for,
+ DIRECTION is zero for forward, or 1 for reverse,
+ WHERE is the history list number of the current line. If it is
+ -1, then this line is the starting one. */
+rl_display_search (search_string, reverse_p, where)
+ char *search_string;
+ int reverse_p, where;
+{
+ char *message = (char *)NULL;
+
+ message =
+ (char *)alloca (1 + (search_string ? strlen (search_string) : 0) + 30);
+
+ *message = '\0';
+
+#ifdef NEVER
+ if (where != -1)
+ sprintf (message, "[%d]", where + history_base);
+#endif
+
+ strcat (message, "(");
+
+ if (reverse_p)
+ strcat (message, "reverse-");
+
+ strcat (message, "i-search)`");
+
+ if (search_string)
+ strcat (message, search_string);
+
+ strcat (message, "': ");
+ rl_message (message, 0, 0);
+ rl_redisplay ();
+}
+
+/* Search through the history looking for an interactively typed string.
+ This is analogous to i-search. We start the search in the current line.
+ DIRECTION is which direction to search; > 0 means forward, < 0 means
+ backwards. */
+rl_search_history (direction, invoking_key)
+ int direction;
+ int invoking_key;
+{
+ /* The string that the user types in to search for. */
+ char *search_string = (char *)alloca (128);
+
+ /* The current length of SEARCH_STRING. */
+ int search_string_index;
+
+ /* The list of lines to search through. */
+ char **lines;
+
+ /* The length of LINES. */
+ int hlen;
+
+ /* Where we get LINES from. */
+ HIST_ENTRY **hlist = history_list ();
+
+ int orig_point = rl_point;
+ int orig_line = where_history ();
+ int last_found_line = orig_line;
+ int c, done = 0;
+ register int i = 0;
+
+
+ /* The line currently being searched. */
+ char *sline;
+
+ /* Offset in that line. */
+ int index;
+
+ /* Non-zero if we are doing a reverse search. */
+ int reverse = (direction < 0);
+
+ /* Create an arrary of pointers to the lines that we want to search. */
+
+ maybe_replace_line ();
+ if (hlist)
+ for (i = 0; hlist[i]; i++);
+
+ /* Allocate space for this many lines, +1 for the current input line,
+ and remember those lines. */
+ lines = (char **)alloca ((1 + (hlen = i)) * sizeof (char *));
+ for (i = 0; i < hlen; i++)
+ lines[i] = hlist[i]->line;
+
+ if (saved_line_for_history)
+ lines[i] = saved_line_for_history->line;
+ else
+ {
+ /* So I have to type it in this way instead. */
+ lines[i] = (char *)alloca (1 + strlen (the_line));
+ strcpy (lines[i], &the_line[0]);
+ }
+
+ hlen++;
+
+ /* The line where we start the search. */
+ i = orig_line;
+
+ /* Initialize search parameters. */
+ *search_string = '\0';
+ search_string_index = 0;
+
+ rl_display_search (search_string, reverse, -1);
+
+ sline = the_line;
+ index = rl_point;
+
+ while (!done)
+ {
+ c = rl_read_key (in_stream);
+
+ /* Hack C to Do What I Mean. */
+ {
+ Function *f = (Function *)NULL;
+
+ if (keymap[c].type == ISFUNC)
+ f = keymap[c].function;
+
+ if (f == rl_reverse_search_history)
+ c = reverse ? -1 : -2;
+ else if (f == rl_forward_search_history)
+ c = !reverse ? -1 : -2;
+ }
+
+ switch (c)
+ {
+ case ESC:
+ done = 1;
+ continue;
+
+ /* case invoking_key: */
+ case -1:
+ goto search_again;
+
+ /* switch directions */
+ case -2:
+ direction = -direction;
+ reverse = (direction < 0);
+
+ goto do_search;
+
+ case CTRL ('G'):
+ strcpy (the_line, lines[orig_line]);
+ rl_point = orig_point;
+ rl_end = strlen (the_line);
+ rl_clear_message ();
+ return;
+
+ default:
+ if (c < 32 || c > 126)
+ {
+ rl_execute_next (c);
+ done = 1;
+ continue;
+ }
+ else
+ {
+ search_string[search_string_index++] = c;
+ search_string[search_string_index] = '\0';
+ goto do_search;
+
+ search_again:
+
+ if (!search_string_index)
+ continue;
+ else
+ {
+ if (reverse)
+ --index;
+ else
+ if (index != strlen (sline))
+ ++index;
+ else
+ ding ();
+ }
+ do_search:
+
+ while (1)
+ {
+ if (reverse)
+ {
+ while (index >= 0)
+ if (strncmp
+ (search_string,
+ sline + index,
+ search_string_index) == 0)
+ goto string_found;
+ else
+ index--;
+ }
+ else
+ {
+ register int limit =
+ (strlen (sline) - search_string_index) + 1;
+
+ while (index < limit)
+ {
+ if (strncmp (search_string,
+ sline + index,
+ search_string_index) == 0)
+ goto string_found;
+ index++;
+ }
+ }
+
+ next_line:
+ i += direction;
+
+ /* At limit for direction? */
+ if ((reverse && i < 0) ||
+ (!reverse && i == hlen))
+ goto search_failed;
+
+ sline = lines[i];
+ if (reverse)
+ index = strlen (sline);
+ else
+ index = 0;
+
+ /* If the search string is longer than the current
+ line, no match. */
+ if (search_string_index > strlen (sline))
+ goto next_line;
+
+ /* Start actually searching. */
+ if (reverse)
+ index -= search_string_index;
+ }
+
+ search_failed:
+ /* We cannot find the search string. Ding the bell. */
+ ding ();
+ i = last_found_line;
+ break;
+
+ string_found:
+ /* We have found the search string. Just display it. But don't
+ actually move there in the history list until the user accepts
+ the location. */
+ strcpy (the_line, lines[i]);
+ rl_point = index;
+ rl_end = strlen (the_line);
+ last_found_line = i;
+ rl_display_search (search_string, reverse,
+ (i == orig_line) ? -1 : i);
+ }
+ }
+ continue;
+ }
+ /* The user has won. They found the string that they wanted. Now all
+ we have to do is place them there. */
+ {
+ int now = last_found_line;
+
+ /* First put back the original state. */
+ strcpy (the_line, lines[orig_line]);
+
+ if (now < orig_line)
+ rl_get_previous_history (orig_line - now);
+ else
+ rl_get_next_history (now - orig_line);
+
+ rl_point = index;
+ rl_clear_message ();
+ }
+}
+
+/* Make C be the next command to be executed. */
+rl_execute_next (c)
+ int c;
+{
+ rl_pending_input = c;
+}
+
+/* **************************************************************** */
+/* */
+/* Killing Mechanism */
+/* */
+/* **************************************************************** */
+
+/* What we assume for a max number of kills. */
+#define DEFAULT_MAX_KILLS 10
+
+/* The real variable to look at to find out when to flush kills. */
+int rl_max_kills = DEFAULT_MAX_KILLS;
+
+/* Where to store killed text. */
+char **rl_kill_ring = (char **)NULL;
+
+/* Where we are in the kill ring. */
+int rl_kill_index = 0;
+
+/* How many slots we have in the kill ring. */
+int rl_kill_ring_length = 0;
+
+/* How to say that you only want to save a certain amount
+ of kill material. */
+rl_set_retained_kills (num)
+ int num;
+{}
+
+/* The way to kill something. This appends or prepends to the last
+ kill, if the last command was a kill command. if FROM is less
+ than TO, then the text is appended, otherwise prepended. If the
+ last command was not a kill command, then a new slot is made for
+ this kill. */
+rl_kill_text (from, to)
+ int from, to;
+{
+ int slot;
+ char *text = rl_copy (from, to);
+
+ /* Is there anything to kill? */
+ if (from == to) {
+ free (text);
+ last_command_was_kill++;
+ return;
+ }
+
+ /* Delete the copied text from the line. */
+ rl_delete_text (from, to);
+
+ /* First, find the slot to work with. */
+ if (!last_command_was_kill) {
+
+ /* Get a new slot. */
+ if (!rl_kill_ring) {
+
+ /* If we don't have any defined, then make one. */
+ rl_kill_ring =
+ (char **)xmalloc (((rl_kill_ring_length = 1) + 1) * sizeof (char *));
+ slot = 1;
+
+ } else {
+
+ /* We have to add a new slot on the end, unless we have exceeded
+ the max limit for remembering kills. */
+ slot = rl_kill_ring_length;
+ if (slot == rl_max_kills) {
+ register int i;
+ free (rl_kill_ring[0]);
+ for (i = 0; i < slot; i++)
+ rl_kill_ring[i] = rl_kill_ring[i + 1];
+ } else {
+ rl_kill_ring =
+ (char **)xrealloc (rl_kill_ring,
+ ((slot = (rl_kill_ring_length += 1)) + 1)
+ * sizeof (char *));
+ }
+ }
+ slot--;
+ } else {
+ slot = rl_kill_ring_length - 1;
+ }
+
+ /* If the last command was a kill, prepend or append. */
+ if (last_command_was_kill) {
+ char *old = rl_kill_ring[slot];
+ char *new = (char *)xmalloc (1 + strlen (old) + strlen (text));
+
+ if (from < to) {
+ strcpy (new, old);
+ strcat (new, text);
+ } else {
+ strcpy (new, text);
+ strcat (new, old);
+ }
+ free (old);
+ free (text);
+ rl_kill_ring[slot] = new;
+ } else {
+ rl_kill_ring[slot] = text;
+ }
+ rl_kill_index = slot;
+ last_command_was_kill++;
+}
+
+/* Now REMEMBER! In order to do prepending or appending correctly, kill
+ commands always make rl_point's original position be the FROM argument,
+ and rl_point's extent be the TO argument. */
+
+
+/* **************************************************************** */
+/* */
+/* Killing Commands */
+/* */
+/* **************************************************************** */
+
+/* Delete the word at point, saving the text in the kill ring. */
+rl_kill_word (count)
+ int count;
+{
+ int orig_point = rl_point;
+
+ if (count < 0)
+ rl_backward_kill_word (-count);
+ else
+ {
+ rl_forward_word (count);
+
+ if (rl_point != orig_point)
+ rl_kill_text (orig_point, rl_point);
+
+ rl_point = orig_point;
+ }
+}
+
+/* Rubout the word before point, placing it on the kill ring. */
+rl_backward_kill_word (count)
+ int count;
+{
+ int orig_point = rl_point;
+
+ if (count < 0)
+ rl_kill_word (-count);
+ else
+ {
+ rl_backward_word (count);
+
+ if (rl_point != orig_point)
+ rl_kill_text (orig_point, rl_point);
+ }
+}
+
+/* Kill from here to the end of the line. If DIRECTION is negative, kill
+ back to the line start instead. */
+rl_kill_line (direction)
+ int direction;
+{
+ int orig_point = rl_point;
+
+ if (direction < 0)
+ rl_backward_kill_line (1);
+ else
+ {
+ rl_end_of_line ();
+ if (orig_point != rl_point)
+ rl_kill_text (orig_point, rl_point);
+ rl_point = orig_point;
+ }
+}
+
+/* Kill backwards to the start of the line. If DIRECTION is negative, kill
+ forwards to the line end instead. */
+rl_backward_kill_line (direction)
+ int direction;
+{
+ int orig_point = rl_point;
+
+ if (direction < 0)
+ rl_kill_line (1);
+ else
+ {
+ if (!rl_point)
+ ding ();
+ else
+ {
+ rl_beg_of_line ();
+ rl_kill_text (orig_point, rl_point);
+ }
+ }
+}
+
+/* Yank back the last killed text. This ignores arguments. */
+rl_yank ()
+{
+ if (!rl_kill_ring) rl_abort ();
+ rl_insert_text (rl_kill_ring[rl_kill_index]);
+}
+
+/* If the last command was yank, or yank_pop, and the text just
+ before point is identical to the current kill item, then
+ delete that text from the line, rotate the index down, and
+ yank back some other text. */
+rl_yank_pop ()
+{
+ int l;
+
+ if (((rl_last_func != rl_yank_pop) && (rl_last_func != rl_yank)) ||
+ !rl_kill_ring)
+ {
+ rl_abort ();
+ }
+
+ l = strlen (rl_kill_ring[rl_kill_index]);
+ if (((rl_point - l) >= 0) &&
+ (strncmp (the_line + (rl_point - l),
+ rl_kill_ring[rl_kill_index], l) == 0))
+ {
+ rl_delete_text ((rl_point - l), rl_point);
+ rl_point -= l;
+ rl_kill_index--;
+ if (rl_kill_index < 0)
+ rl_kill_index = rl_kill_ring_length - 1;
+ rl_yank ();
+ }
+ else
+ rl_abort ();
+
+}
+
+/* Yank the COUNTth argument from the previous history line. */
+rl_yank_nth_arg (count, ignore)
+ int count;
+{
+ register HIST_ENTRY *entry = previous_history ();
+ char *arg;
+
+ if (entry)
+ next_history ();
+ else
+ {
+ ding ();
+ return;
+ }
+
+ arg = history_arg_extract (count, count, entry->line);
+ if (!arg || !*arg)
+ {
+ ding ();
+ return;
+ }
+
+ rl_begin_undo_group ();
+ if (rl_point && the_line[rl_point - 1] != ' ')
+ rl_insert_text (" ");
+ rl_insert_text (arg);
+ free (arg);
+ rl_end_undo_group ();
+}
+
+/* Vi Mode. */
+#ifdef VI_MODE
+#include "vi_mode.c"
+#endif /* VI_MODE */
+
+/* How to toggle back and forth between editing modes. */
+rl_vi_editing_mode ()
+{
+#ifdef VI_MODE
+ rl_editing_mode = vi_mode;
+ rl_vi_insertion_mode ();
+#endif /* VI_MODE */
+}
+
+rl_emacs_editing_mode ()
+{
+ rl_editing_mode = emacs_mode;
+ keymap = emacs_standard_keymap;
+}
+
+
+/* **************************************************************** */
+/* */
+/* Completion */
+/* */
+/* **************************************************************** */
+
+/* Non-zero means that case is not significant in completion. */
+int completion_case_fold = 0;
+
+/* Return an array of (char *) which is a list of completions for TEXT.
+ If there are no completions, return a NULL pointer.
+ The first entry in the returned array is the substitution for TEXT.
+ The remaining entries are the possible completions.
+ The array is terminated with a NULL pointer.
+
+ ENTRY_FUNCTION is a function of two args, and returns a (char *).
+ The first argument is TEXT.
+ The second is a state argument; it should be zero on the first call, and
+ non-zero on subsequent calls. It returns a NULL pointer to the caller
+ when there are no more matches.
+ */
+char **
+completion_matches (text, entry_function)
+ char *text;
+ char *(*entry_function) ();
+{
+ /* Number of slots in match_list. */
+ int match_list_size;
+
+ /* The list of matches. */
+ char **match_list =
+ (char **)xmalloc (((match_list_size = 10) + 1) * sizeof (char *));
+
+ /* Number of matches actually found. */
+ int matches = 0;
+
+ /* Temporary string binder. */
+ char *string;
+
+ match_list[1] = (char *)NULL;
+
+ while (string = (*entry_function) (text, matches))
+ {
+ if (matches + 1 == match_list_size)
+ match_list =
+ (char **)xrealloc (match_list,
+ ((match_list_size += 10) + 1) * sizeof (char *));
+
+ match_list[++matches] = string;
+ match_list[matches + 1] = (char *)NULL;
+ }
+
+ /* If there were any matches, then look through them finding out the
+ lowest common denominator. That then becomes match_list[0]. */
+ if (matches)
+ {
+ register int i = 1;
+ int low = 100000; /* Count of max-matched characters. */
+
+ /* If only one match, just use that. */
+ if (matches == 1)
+ {
+ match_list[0] = match_list[1];
+ match_list[1] = (char *)NULL;
+ }
+ else
+ {
+ /* Otherwise, compare each member of the list with
+ the next, finding out where they stop matching. */
+
+ while (i < matches)
+ {
+ register int c1, c2, si;
+
+ if (completion_case_fold)
+ {
+ for (si = 0;
+ (c1 = to_lower(match_list[i][si])) &&
+ (c2 = to_lower(match_list[i + 1][si]));
+ si++)
+ if (c1 != c2) break;
+ }
+ else
+ {
+ for (si = 0;
+ (c1 = match_list[i][si]) &&
+ (c2 = match_list[i + 1][si]);
+ si++)
+ if (c1 != c2) break;
+ }
+
+ if (low > si) low = si;
+ i++;
+ }
+ match_list[0] = (char *)xmalloc (low + 1);
+ strncpy (match_list[0], match_list[1], low);
+ match_list[0][low] = '\0';
+ }
+ }
+ else /* There were no matches. */
+ {
+ free (match_list);
+ match_list = (char **)NULL;
+ }
+ return (match_list);
+}
+
+/* Okay, now we write the entry_function for filename completion. In the
+ general case. Note that completion in the shell is a little different
+ because of all the pathnames that must be followed when looking up the
+ completion for a command. */
+char *
+filename_completion_function (text, state)
+ int state;
+ char *text;
+{
+ static DIR *directory;
+ static char *filename = (char *)NULL;
+ static char *dirname = (char *)NULL;
+ static char *users_dirname = (char *)NULL;
+ static int filename_len;
+
+ struct direct *entry = (struct direct *)NULL;
+
+ /* If we don't have any state, then do some initialization. */
+ if (!state)
+ {
+ char *temp;
+
+ if (dirname) free (dirname);
+ if (filename) free (filename);
+ if (users_dirname) free (users_dirname);
+
+ filename = savestring (text);
+ if (!*text) text = ".";
+ dirname = savestring (text);
+
+ temp = rindex (dirname, '/');
+
+ if (temp)
+ {
+ strcpy (filename, ++temp);
+ *temp = '\0';
+ }
+ else
+ strcpy (dirname, ".");
+
+ /* We aren't done yet. We also support the "~user" syntax. */
+
+ /* Save the version of the directory that the user typed. */
+ users_dirname = savestring (dirname);
+ {
+ char *tilde_expand (), *temp_dirname = tilde_expand (dirname);
+ free (dirname);
+ dirname = temp_dirname;
+#ifdef SHELL
+ {
+ extern int follow_symbolic_links;
+ char *make_absolute ();
+
+ if (follow_symbolic_links && (strcmp (dirname, ".") != 0))
+ {
+ temp_dirname = make_absolute (dirname, get_working_directory (""));
+
+ if (temp_dirname)
+ {
+ free (dirname);
+ dirname = temp_dirname;
+ }
+ }
+ }
+#endif /* SHELL */
+ }
+ directory = opendir (dirname);
+ filename_len = strlen (filename);
+
+ rl_filename_completion_desired = 1;
+ }
+
+ /* At this point we should entertain the possibility of hacking wildcarded
+ filenames, like /usr/man*\/te<TAB>. If the directory name contains
+ globbing characters, then build an array of directories to glob on, and
+ glob on the first one. */
+
+ /* Now that we have some state, we can read the directory. */
+
+ while (directory && (entry = readdir (directory)))
+ {
+ /* Special case for no filename.
+ All entries except "." and ".." match. */
+ if (!filename_len)
+ {
+ if ((strcmp (entry->d_name, ".") != 0) &&
+ (strcmp (entry->d_name, "..") != 0))
+ break;
+ }
+ else
+ {
+ /* Otherwise, if these match upto the length of filename, then
+ it is a match. */
+#ifdef TMB_SYSV
+ if ((strlen (entry->d_name) >= filename_len) &&
+ (strncmp (filename, entry->d_name, filename_len) == 0))
+#else
+ if ((entry->d_namlen >= filename_len) &&
+ (strncmp (filename, entry->d_name, filename_len) == 0))
+#endif /* TMB_SYSV */
+ {
+ break;
+ }
+ }
+ }
+
+ if (!entry)
+ {
+ if (directory)
+ {
+ closedir (directory);
+ directory = (DIR *)NULL;
+ }
+ return (char *)NULL;
+ }
+ else
+ {
+ char *temp;
+
+ if (dirname && (strcmp (dirname, ".") != 0))
+ {
+#ifdef TMB_SYSV
+ temp = (char *)xmalloc (1 + strlen (users_dirname)
+ + strlen (entry->d_name));
+#else
+ temp = (char *)xmalloc (1 + strlen (users_dirname)
+ + entry->d_namlen);
+#endif /* TMB_SYSV */
+ strcpy (temp, users_dirname);
+ strcat (temp, entry->d_name);
+ }
+ else
+ {
+ temp = (savestring (entry->d_name));
+ }
+ return (temp);
+ }
+}
+
+
+/* **************************************************************** */
+/* */
+/* Binding keys */
+/* */
+/* **************************************************************** */
+
+/* rl_add_defun (char *name, Function *function, int key)
+ Add NAME to the list of named functions. Make FUNCTION
+ be the function that gets called.
+ If KEY is not -1, then bind it. */
+rl_add_defun (name, function, key)
+ char *name;
+ Function *function;
+ int key;
+{
+ if (key != -1)
+ rl_bind_key (key, function);
+ rl_add_funmap_entry (name, function);
+}
+
+/* Bind KEY to FUNCTION. Returns non-zero if KEY is out of range. */
+int
+rl_bind_key (key, function)
+ int key;
+ Function *function;
+{
+ if (key < 0)
+ return (key);
+
+ if (key > 127 && key < 256)
+ {
+ if (keymap[ESC].type == ISKMAP)
+ {
+ Keymap escmap = (Keymap)keymap[ESC].function;
+
+ key -= 128;
+ escmap[key].type = ISFUNC;
+ escmap[key].function = function;
+ return (0);
+ }
+ return (key);
+ }
+
+ keymap[key].type = ISFUNC;
+ keymap[key].function = function;
+ return (0);
+}
+
+/* Bind KEY to FUNCTION in MAP. Returns non-zero in case of invalid
+ KEY. */
+int
+rl_bind_key_in_map (key, function, map)
+ int key;
+ Function *function;
+ Keymap map;
+{
+ int result;
+ Keymap oldmap = keymap;
+
+ keymap = map;
+ result = rl_bind_key (key, function);
+ keymap = oldmap;
+ return (result);
+}
+
+/* Make KEY do nothing in the currently selected keymap.
+ Returns non-zero in case of error. */
+int
+rl_unbind_key (key)
+ int key;
+{
+ return (rl_bind_key (key, (Function *)NULL));
+}
+
+/* Make KEY do nothing in MAP.
+ Returns non-zero in case of error. */
+int
+rl_unbind_key_in_map (key, map)
+ int key;
+ Keymap map;
+{
+ return (rl_bind_key_in_map (key, (Function *)NULL, map));
+}
+
+/* Bind the key sequence represented by the string KEYSEQ to
+ FUNCTION. This makes new keymaps as necessary. The initial
+ place to do bindings is in MAP. */
+rl_set_key (keyseq, function, map)
+ char *keyseq;
+ Function *function;
+ Keymap map;
+{
+ rl_generic_bind (ISFUNC, keyseq, function, map);
+}
+
+/* Bind the key sequence represented by the string KEYSEQ to
+ the string of characters MACRO. This makes new keymaps as
+ necessary. The initial place to do bindings is in MAP. */
+rl_macro_bind (keyseq, macro, map)
+ char *keyseq, *macro;
+ Keymap map;
+{
+ char *macro_keys = (char *)xmalloc (2 * (strlen (macro)));
+ int macro_keys_len;
+
+ if (rl_translate_keyseq (macro, macro_keys, &macro_keys_len))
+ {
+ free (macro_keys);
+ return;
+ }
+ rl_generic_bind (ISMACR, keyseq, macro_keys, map);
+}
+
+/* Bind the key sequence represented by the string KEYSEQ to
+ the arbitrary pointer DATA. TYPE says what kind of data is
+ pointed to by DATA, right now this can be a function (ISFUNC),
+ a macro (ISMACR), or a keymap (ISKMAP). This makes new keymaps
+ as necessary. The initial place to do bindings is in MAP. */
+rl_generic_bind (type, keyseq, data, map)
+ int type;
+ char *keyseq, *data;
+ Keymap map;
+{
+ char *keys;
+ int keys_len;
+ register int i;
+ int start;
+
+ /* If no keys to bind to, exit right away. */
+ if (!keyseq || !*keyseq)
+ {
+ if (type == ISMACR)
+ free (data);
+ return;
+ }
+
+ keys = (char *)alloca (1 + (2 * strlen (keyseq)));
+
+ /* Translate the ASCII representation of KEYSEQ into an array
+ of characters. Stuff the characters into ARRAY, and the
+ length of ARRAY into LENGTH. */
+ if (rl_translate_keyseq (keyseq, keys, &keys_len))
+ return;
+
+ /* Handle mapping of the ESC Key in vi mode */
+ start = 0;
+#ifdef VI_MODE
+ if ((rl_editing_mode == vi_mode) && (keys[0] == ESC))
+ {
+ start++;
+ map = vi_movement_keymap;
+ if(keys[1] == ESC)
+ {
+ extern KEYMAP_ENTRY_ARRAY vi_escape_keymap;
+
+ start++;
+ map = vi_escape_keymap;
+ }
+ }
+#endif
+
+ /* Bind keys, making new keymaps as necessary. */
+ for (i = start; i < keys_len; i++)
+ {
+ if (i + 1 < keys_len)
+ {
+ if (map[keys[i]].type != ISKMAP)
+ {
+ if (map[i].type == ISMACR)
+ free ((char *)map[i].function);
+
+ map[keys[i]].type = ISKMAP;
+ map[keys[i]].function = (Function *)rl_make_bare_keymap ();
+ }
+ map = (Keymap)map[keys[i]].function;
+ }
+ else
+ {
+ if (map[keys[i]].type == ISMACR)
+ free ((char *)map[keys[i]].function);
+
+ map[keys[i]].function = (Function *)data;
+ map[keys[i]].type = type;
+ }
+ }
+}
+
+/* Translate the ASCII representation of SEQ, stuffing the
+ values into ARRAY, an array of characters. LEN gets the
+ final length of ARRAY. Return non-zero if there was an
+ error parsing SEQ. */
+rl_translate_keyseq (seq, array, len)
+ char *seq, *array;
+ int *len;
+{
+ register int i, c, l = 0;
+
+ for (i = 0; c = seq[i]; i++)
+ {
+ if (c == '\\')
+ {
+ c = seq[++i];
+
+ if (!c)
+ break;
+
+ if (((c == 'C' || c == 'M') && seq[i + 1] == '-') ||
+ (c == 'e'))
+ {
+ /* Handle special case of backwards define. */
+ if (strncmp (&seq[i], "C-\\M-", 5) == 0)
+ {
+ array[l++] = ESC;
+ i += 5;
+ array[l++] = CTRL (to_upper (seq[i]));
+ if (!seq[i])
+ i--;
+ continue;
+ }
+
+ switch (c)
+ {
+ case 'M':
+ i++;
+ array[l++] = ESC;
+ break;
+
+ case 'C':
+ i += 2;
+ array[l++] = CTRL (to_upper (seq[i]));
+ break;
+
+ case 'e':
+ array[l++] = ESC;
+ }
+
+ continue;
+ }
+ }
+ array[l++] = c;
+ }
+
+ array[l] = '\0';
+ *len = l;
+ return (0);
+}
+
+/* Return a pointer to the function that STRING represents.
+ If STRING doesn't have a matching function, then a NULL pointer
+ is returned. */
+Function *
+rl_named_function (string)
+ char *string;
+{
+ register int i;
+ static int stricmp ();
+
+ for (i = 0; funmap[i]; i++)
+ if (stricmp (funmap[i]->name, string) == 0)
+ return (funmap[i]->function);
+ return ((Function *)NULL);
+}
+
+/* The last key bindings file read. */
+static char *last_readline_init_file = "~/.inputrc";
+
+/* Re-read the current keybindings file. */
+rl_re_read_init_file (count, ignore)
+ int count, ignore;
+{
+ rl_read_init_file (last_readline_init_file);
+}
+
+/* Do key bindings from a file. If FILENAME is NULL it defaults
+ to `~/.inputrc'. If the file existed and could be opened and
+ read, 0 is returned, otherwise errno is returned. */
+int
+rl_read_init_file (filename)
+ char *filename;
+{
+ int line_size, line_index;
+ char *line = (char *)xmalloc (line_size = 100);
+ char *openname;
+ FILE *file;
+
+ int c;
+
+ /* Default the filename. */
+ if (!filename)
+ filename = "~/.inputrc";
+
+ openname = tilde_expand (filename);
+
+ /* Open the file. */
+ file = fopen (openname, "r");
+ free (openname);
+
+ if (!file)
+ return (errno);
+
+ last_readline_init_file = filename;
+
+ /* Loop reading lines from the file. Lines that start with `#' are
+ comments, all other lines are commands for readline initialization. */
+ while ((c = getc(file)) != EOF)
+ {
+ /* If comment, flush to EOL. */
+ if (c == '#')
+ {
+ while ((c = getc(file)) != EOF && c != '\n');
+ if (c == EOF)
+ goto function_exit;
+ continue;
+ }
+
+ /* Otherwise, this is the start of a line. Read the
+ line from the file. */
+ line_index = 0;
+ while (c != EOF && c != '\n')
+ {
+ line[line_index++] = c;
+ if (line_index == line_size)
+ line = (char *)xrealloc (line, line_size += 100);
+ c = getc (file);
+ }
+ line[line_index] = '\0';
+
+ /* Parse the line. */
+ rl_parse_and_bind (line);
+ }
+
+function_exit:
+
+ free (line);
+ /* Close up the file and exit. */
+ fclose (file);
+ return (0);
+}
+
+
+/* **************************************************************** */
+/* */
+/* Parser Directives */
+/* */
+/* **************************************************************** */
+
+/* Conditionals. */
+
+/* Calling programs set this to have their argv[0]. */
+char *rl_readline_name = "other";
+
+/* Stack of previous values of parsing_conditionalized_out. */
+static unsigned char *if_stack = (unsigned char *)NULL;
+static int if_stack_depth = 0;
+static int if_stack_size = 0;
+
+/* Push parsing_conditionalized_out, and set parser state based on ARGS. */
+parser_if (args)
+ char *args;
+{
+ register int i;
+ static int stricmp ();
+
+ /* Push parser state. */
+ if (if_stack_depth + 1 >= if_stack_size)
+ {
+ if (!if_stack)
+ if_stack = (unsigned char *)xmalloc (if_stack_size = 20);
+ else
+ if_stack = (unsigned char *)xrealloc (if_stack, if_stack_size += 20);
+ }
+ if_stack[if_stack_depth++] = parsing_conditionalized_out;
+
+ /* We only check to see if the first word in ARGS is the same as the
+ value stored in rl_readline_name. */
+
+ /* Isolate first argument. */
+ for (i = 0; args[i] && !whitespace (args[i]); i++);
+
+ if (args[i])
+ args[i++] = '\0';
+
+ if (stricmp (args, rl_readline_name) == 0)
+ parsing_conditionalized_out = 0;
+ else
+ parsing_conditionalized_out = 1;
+}
+
+/* Invert the current parser state if there is anything on the stack. */
+parser_else (args)
+ char *args;
+{
+ if (if_stack_depth)
+ parsing_conditionalized_out = !parsing_conditionalized_out;
+ else
+ {
+ /* *** What, no error message? *** */
+ }
+}
+
+/* Terminate a conditional, popping the value of
+ parsing_conditionalized_out from the stack. */
+parser_endif (args)
+ char *args;
+{
+ if (if_stack_depth)
+ parsing_conditionalized_out = if_stack[--if_stack_depth];
+ else
+ {
+ /* *** What, no error message? *** */
+ }
+}
+
+/* Associate textual names with actual functions. */
+static struct {
+ char *name;
+ Function *function;
+} parser_directives [] = {
+ { "if", parser_if },
+ { "endif", parser_endif },
+ { "else", parser_else },
+ { (char *)0x0, (Function *)0x0 }
+};
+
+/* Handle a parser directive. STATEMENT is the line of the directive
+ without any leading `$'. */
+static int
+handle_parser_directive (statement)
+ char *statement;
+{
+ register int i;
+ char *directive, *args;
+ static int stricmp ();
+
+ /* Isolate the actual directive. */
+
+ /* Skip whitespace. */
+ for (i = 0; whitespace (statement[i]); i++);
+
+ directive = &statement[i];
+
+ for (; statement[i] && !whitespace (statement[i]); i++);
+
+ if (statement[i])
+ statement[i++] = '\0';
+
+ for (; statement[i] && whitespace (statement[i]); i++);
+
+ args = &statement[i];
+
+ /* Lookup the command, and act on it. */
+ for (i = 0; parser_directives[i].name; i++)
+ if (stricmp (directive, parser_directives[i].name) == 0)
+ {
+ (*parser_directives[i].function) (args);
+ return (0);
+ }
+
+ /* *** Should an error message be output? */
+ return (1);
+}
+
+/* Read the binding command from STRING and perform it.
+ A key binding command looks like: Keyname: function-name\0,
+ a variable binding command looks like: set variable value.
+ A new-style keybinding looks like "\C-x\C-x": exchange-point-and-mark. */
+rl_parse_and_bind (string)
+ char *string;
+{
+ extern char *possible_control_prefixes[], *possible_meta_prefixes[];
+ char *funname, *kname;
+ static int substring_member_of_array (), stricmp ();
+ register int c;
+ int key, i;
+
+ if (!string || !*string || *string == '#')
+ return;
+
+ /* If this is a parser directive, act on it. */
+ if (*string == '$')
+ {
+ handle_parser_directive (&string[1]);
+ return;
+ }
+
+ /* If we are supposed to be skipping parsing right now, then do it. */
+ if (parsing_conditionalized_out)
+ return;
+
+ i = 0;
+ /* If this keyname is a complex key expression surrounded by quotes,
+ advance to after the matching close quote. */
+ if (*string == '"')
+ {
+ for (i = 1; c = string[i]; i++)
+ {
+ if (c == '"' && string[i - 1] != '\\')
+ break;
+ }
+ }
+
+ /* Advance to the colon (:) or whitespace which separates the two objects. */
+ for (; (c = string[i]) && c != ':' && c != ' ' && c != '\t'; i++ );
+
+ /* Mark the end of the command (or keyname). */
+ if (string[i])
+ string[i++] = '\0';
+
+ /* If this is a command to set a variable, then do that. */
+ if (stricmp (string, "set") == 0)
+ {
+ char *var = string + i;
+ char *value;
+
+ /* Make VAR point to start of variable name. */
+ while (*var && whitespace (*var)) var++;
+
+ /* Make value point to start of value string. */
+ value = var;
+ while (*value && !whitespace (*value)) value++;
+ if (*value)
+ *value++ = '\0';
+ while (*value && whitespace (*value)) value++;
+
+ rl_variable_bind (var, value);
+ return;
+ }
+
+ /* Skip any whitespace between keyname and funname. */
+ for (; string[i] && whitespace (string[i]); i++);
+ funname = &string[i];
+
+ /* Now isolate funname.
+ For straight function names just look for whitespace, since
+ that will signify the end of the string. But this could be a
+ macro definition. In that case, the string is quoted, so skip
+ to the matching delimiter. */
+ if (*funname == '\'' || *funname == '"')
+ {
+ int delimiter = string[i++];
+
+ for (; c = string[i]; i++)
+ {
+ if (c == delimiter && string[i - 1] != '\\')
+ break;
+ }
+ if (c)
+ i++;
+ }
+
+ /* Advance to the end of the string. */
+ for (; string[i] && !whitespace (string[i]); i++);
+
+ /* No extra whitespace at the end of the string. */
+ string[i] = '\0';
+
+ /* If this is a new-style key-binding, then do the binding with
+ rl_set_key (). Otherwise, let the older code deal with it. */
+ if (*string == '"')
+ {
+ char *seq = (char *)alloca (1 + strlen (string));
+ register int j, k = 0;
+
+ for (j = 1; string[j]; j++)
+ {
+ if (string[j] == '"' && string[j - 1] != '\\')
+ break;
+
+ seq[k++] = string[j];
+ }
+ seq[k] = '\0';
+
+ /* Binding macro? */
+ if (*funname == '\'' || *funname == '"')
+ {
+ j = strlen (funname);
+
+ if (j && funname[j - 1] == *funname)
+ funname[j - 1] = '\0';
+
+ rl_macro_bind (seq, &funname[1], keymap);
+ }
+ else
+ rl_set_key (seq, rl_named_function (funname), keymap);
+
+ return;
+ }
+
+ /* Get the actual character we want to deal with. */
+ kname = rindex (string, '-');
+ if (!kname)
+ kname = string;
+ else
+ kname++;
+
+ key = glean_key_from_name (kname);
+
+ /* Add in control and meta bits. */
+ if (substring_member_of_array (string, possible_control_prefixes))
+ key = CTRL (to_upper (key));
+
+ if (substring_member_of_array (string, possible_meta_prefixes))
+ key = META (key);
+
+ /* Temporary. Handle old-style keyname with macro-binding. */
+ if (*funname == '\'' || *funname == '"')
+ {
+ char seq[2];
+ int fl = strlen (funname);
+
+ seq[0] = key; seq[1] = '\0';
+ if (fl && funname[fl - 1] == *funname)
+ funname[fl - 1] = '\0';
+
+ rl_macro_bind (seq, &funname[1], keymap);
+ }
+ else
+ rl_bind_key (key, rl_named_function (funname));
+}
+
+rl_variable_bind (name, value)
+ char *name, *value;
+{
+ static int strnicmp (), stricmp ();
+
+ if (stricmp (name, "editing-mode") == 0)
+ {
+ if (strnicmp (value, "vi", 2) == 0)
+ {
+#ifdef VI_MODE
+ keymap = vi_insertion_keymap;
+ rl_editing_mode = vi_mode;
+#endif /* VI_MODE */
+ }
+ else if (strnicmp (value, "emacs", 5) == 0)
+ {
+ keymap = emacs_standard_keymap;
+ rl_editing_mode = emacs_mode;
+ }
+ }
+ else if (stricmp (name, "horizontal-scroll-mode") == 0)
+ {
+ if (!*value || stricmp (value, "On") == 0)
+ horizontal_scroll_mode = 1;
+ else
+ horizontal_scroll_mode = 0;
+ }
+}
+
+/* Return the character which matches NAME.
+ For example, `Space' returns ' '. */
+
+typedef struct {
+ char *name;
+ int value;
+} assoc_list;
+
+assoc_list name_key_alist[] = {
+ { "Space", ' ' },
+ { "SPC", ' ' },
+ { "Rubout", 0x7f },
+ { "DEL", 0x7f },
+ { "Tab", 0x09 },
+ { "Newline", '\n' },
+ { "Return", '\r' },
+ { "RET", '\r' },
+ { "LFD", '\n' },
+ { "Escape", '\033' },
+ { "ESC", '\033' },
+
+ { (char *)0x0, 0 }
+};
+
+int
+glean_key_from_name (name)
+ char *name;
+{
+ register int i;
+ static int stricmp ();
+
+ for (i = 0; name_key_alist[i].name; i++)
+ if (stricmp (name, name_key_alist[i].name) == 0)
+ return (name_key_alist[i].value);
+
+ return (*name);
+}
+
+
+/* **************************************************************** */
+/* */
+/* String Utility Functions */
+/* */
+/* **************************************************************** */
+
+/* Return non-zero if any members of ARRAY are a substring in STRING. */
+static int
+substring_member_of_array (string, array)
+ char *string, **array;
+{
+ static char *strindex ();
+
+ while (*array)
+ {
+ if (strindex (string, *array))
+ return (1);
+ array++;
+ }
+ return (0);
+}
+
+/* Whoops, Unix doesn't have strnicmp. */
+
+/* Compare at most COUNT characters from string1 to string2. Case
+ doesn't matter. */
+static int
+strnicmp (string1, string2, count)
+ char *string1, *string2;
+{
+ register char ch1, ch2;
+
+ while (count) {
+ ch1 = *string1++;
+ ch2 = *string2++;
+ if (to_upper(ch1) == to_upper(ch2))
+ count--;
+ else break;
+ }
+ return (count);
+}
+
+/* strcmp (), but caseless. */
+static int
+stricmp (string1, string2)
+ char *string1, *string2;
+{
+ register char ch1, ch2;
+
+ while (*string1 && *string2) {
+ ch1 = *string1++;
+ ch2 = *string2++;
+ if (to_upper(ch1) != to_upper(ch2))
+ return (1);
+ }
+ return (*string1 | *string2);
+}
+
+/* Determine if s2 occurs in s1. If so, return a pointer to the
+ match in s1. The compare is case insensitive. */
+static char *
+strindex (s1, s2)
+ register char *s1, *s2;
+{
+ register int i, l = strlen (s2);
+ register int len = strlen (s1);
+
+ for (i = 0; (len - i) >= l; i++)
+ if (strnicmp (&s1[i], s2, l) == 0)
+ return (s1 + i);
+ return ((char *)NULL);
+}
+
+
+#ifdef STATIC_MALLOC
+
+/* **************************************************************** */
+/* */
+/* xmalloc and xrealloc () */
+/* */
+/* **************************************************************** */
+
+static char *
+xmalloc (bytes)
+ int bytes;
+{
+ static memory_error_and_abort ();
+ char *temp = (char *)malloc (bytes);
+
+ if (!temp)
+ memory_error_and_abort ();
+ return (temp);
+}
+
+static char *
+xrealloc (pointer, bytes)
+ char *pointer;
+ int bytes;
+{
+ static memory_error_and_abort ();
+ char *temp = (char *)realloc (pointer, bytes);
+
+ if (!temp)
+ memory_error_and_abort ();
+ return (temp);
+}
+
+static
+memory_error_and_abort ()
+{
+ fprintf (stderr, "readline: Out of virtual memory!\n");
+ abort ();
+}
+#endif /* STATIC_MALLOC */
+
+
+/* **************************************************************** */
+/* */
+/* Testing Readline */
+/* */
+/* **************************************************************** */
+
+#ifdef TEST
+
+main ()
+{
+ HIST_ENTRY **history_list ();
+ char *temp = (char *)NULL;
+ char *prompt = "readline% ";
+ int done = 0;
+
+ while (!done)
+ {
+ temp = readline (prompt);
+
+ /* Test for EOF. */
+ if (!temp)
+ exit (1);
+
+ /* If there is anything on the line, print it and remember it. */
+ if (*temp)
+ {
+ fprintf (stderr, "%s\r\n", temp);
+ add_history (temp);
+ }
+
+ /* Check for `command' that we handle. */
+ if (strcmp (temp, "quit") == 0)
+ done = 1;
+
+ if (strcmp (temp, "list") == 0) {
+ HIST_ENTRY **list = history_list ();
+ register int i;
+ if (list) {
+ for (i = 0; list[i]; i++) {
+ fprintf (stderr, "%d: %s\r\n", i, list[i]->line);
+ free (list[i]->line);
+ }
+ free (list);
+ }
+ }
+ free (temp);
+ }
+}
+
+#endif /* TEST */
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -g -traditional -I. -I.. -DTEST -o readline readline.c keymaps.o funmap.o history.o -ltermcap"
+ * end:
+ */
diff --git a/gnu/usr.bin/gdb/readline/readline.h b/gnu/usr.bin/gdb/readline/readline.h
new file mode 100644
index 0000000..7d7fbe7
--- /dev/null
+++ b/gnu/usr.bin/gdb/readline/readline.h
@@ -0,0 +1,161 @@
+/* Readline.h -- the names of functions callable from within readline. */
+
+#ifndef _READLINE_H_
+#define _READLINE_H_
+
+#include <readline/keymaps.h>
+
+#ifndef __FUNCTION_DEF
+typedef int Function ();
+#define __FUNCTION_DEF
+#endif
+
+/* The functions for manipulating the text of the line within readline.
+Most of these functions are bound to keys by default. */
+extern int
+rl_beg_of_line (), rl_backward (), rl_delete (), rl_end_of_line (),
+rl_forward (), ding (), rl_backward (), rl_newline (), rl_kill_line (),
+rl_clear_screen (), rl_get_next_history (), rl_get_previous_history (),
+rl_quoted_insert (), rl_reverse_search_history (), rl_transpose_chars
+(), rl_unix_line_discard (), rl_quoted_insert (), rl_unix_word_rubout
+(), rl_yank (), rl_rubout (), rl_backward_word (), rl_kill_word (),
+rl_forward_word (), rl_tab_insert (), rl_yank_pop (), rl_yank_nth_arg (),
+rl_backward_kill_word (), rl_backward_kill_line (), rl_transpose_words
+(), rl_complete (), rl_possible_completions (), rl_do_lowercase_version
+(), rl_digit_argument (), rl_universal_argument (), rl_abort (),
+rl_undo_command (), rl_revert_line (), rl_beginning_of_history (),
+rl_end_of_history (), rl_forward_search_history (), rl_insert (),
+rl_upcase_word (), rl_downcase_word (), rl_capitalize_word (),
+rl_restart_output (), rl_re_read_init_file ();
+
+/* These are *both* defined even when VI_MODE is not. */
+extern int rl_vi_editing_mode (), rl_emacs_editing_mode ();
+
+#ifdef VI_MODE
+/* Things for vi mode. */
+extern int rl_vi_movement_mode (), rl_vi_insertion_mode (), rl_vi_arg_digit (),
+rl_vi_prev_word (), rl_vi_next_word (), rl_vi_char_search (),
+rl_vi_eof_maybe (), rl_vi_append_mode (), rl_vi_put (),
+rl_vi_append_eol (), rl_vi_insert_beg (), rl_vi_delete (), rl_vi_comment (),
+rl_vi_first_print (), rl_vi_fword (), rl_vi_fWord (), rl_vi_bword (),
+rl_vi_bWord (), rl_vi_eword (), rl_vi_eWord (), rl_vi_end_word (),
+rl_vi_change_case (), rl_vi_match (), rl_vi_bracktype (), rl_vi_change_char (),
+rl_vi_yank_arg (), rl_vi_search (), rl_vi_search_again (),
+rl_vi_dosearch (), rl_vi_subst (), rl_vi_overstrike (),
+rl_vi_overstrike_delete (), rl_vi_replace(), rl_vi_column (),
+rl_vi_delete_to (), rl_vi_change_to (), rl_vi_yank_to (), rl_vi_complete ();
+#endif /* VI_MODE */
+
+/* Keyboard macro commands. */
+extern int
+rl_start_kbd_macro (), rl_end_kbd_macro (), rl_call_last_kbd_macro ();
+
+/* Maintaining the state of undo. We remember individual deletes and inserts
+ on a chain of things to do. */
+
+/* The actions that undo knows how to undo. Notice that UNDO_DELETE means
+ to insert some text, and UNDO_INSERT means to delete some text. I.e.,
+ the code tells undo what to undo, not how to undo it. */
+enum undo_code { UNDO_DELETE, UNDO_INSERT, UNDO_BEGIN, UNDO_END };
+
+/* What an element of THE_UNDO_LIST looks like. */
+typedef struct undo_list {
+ struct undo_list *next;
+ int start, end; /* Where the change took place. */
+ char *text; /* The text to insert, if undoing a delete. */
+ enum undo_code what; /* Delete, Insert, Begin, End. */
+} UNDO_LIST;
+
+/* The current undo list for RL_LINE_BUFFER. */
+extern UNDO_LIST *rl_undo_list;
+
+/* The data structure for mapping textual names to code addresses. */
+typedef struct {
+ char *name;
+ Function *function;
+} FUNMAP;
+
+extern FUNMAP **funmap;
+
+/* **************************************************************** */
+/* */
+/* Well Published Variables */
+/* */
+/* **************************************************************** */
+
+/* The name of the calling program. You should initialize this to
+ whatever was in argv[0]. It is used when parsing conditionals. */
+extern char *rl_readline_name;
+
+/* The line buffer that is in use. */
+extern char *rl_line_buffer;
+
+/* The location of point, and end. */
+extern int rl_point, rl_end;
+
+/* The name of the terminal to use. */
+extern char *rl_terminal_name;
+
+/* The input and output streams. */
+extern FILE *rl_instream, *rl_outstream;
+
+/* The basic list of characters that signal a break between words for the
+ completer routine. The contents of this variable is what breaks words
+ in the shell, i.e. "n\"\\'`@$>". */
+extern char *rl_basic_word_break_characters;
+
+/* The list of characters that signal a break between words for
+ rl_complete_internal. The default list is the contents of
+ rl_basic_word_break_characters. */
+extern char *rl_completer_word_break_characters;
+
+/* List of characters that are word break characters, but should be left
+ in TEXT when it is passed to the completion function. The shell uses
+ this to help determine what kind of completing to do. */
+extern char *rl_special_prefixes;
+
+/* Pointer to the generator function for completion_matches ().
+ NULL means to use filename_entry_function (), the default filename
+ completer. */
+extern Function *rl_completion_entry_function;
+
+/* Pointer to alternative function to create matches.
+ Function is called with TEXT, START, and END.
+ START and END are indices in RL_LINE_BUFFER saying what the boundaries
+ of TEXT are.
+ If this function exists and returns NULL then call the value of
+ rl_completion_entry_function to try to match, otherwise use the
+ array of strings returned. */
+extern Function *rl_attempted_completion_function;
+
+/* If non-null, this contains the address of a function to call if the
+ standard meaning for expanding a tilde fails. The function is called
+ with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
+ which is the expansion, or a NULL pointer if there is no expansion. */
+extern Function *rl_tilde_expander;
+
+/* If non-zero, then this is the address of a function to call just
+ before readline_internal () prints the first prompt. */
+extern Function *rl_startup_hook;
+
+/* **************************************************************** */
+/* */
+/* Well Published Functions */
+/* */
+/* **************************************************************** */
+
+/* Read a line of input. Prompt with PROMPT. A NULL PROMPT means none. */
+extern char *readline ();
+
+/* Return an array of strings which are the result of repeatadly calling
+ FUNC with TEXT. */
+extern char **completion_matches ();
+
+/* rl_add_defun (char *name, Function *function, int key)
+ Add NAME to the list of named functions. Make FUNCTION
+ be the function that gets called.
+ If KEY is not -1, then bind it. */
+extern int rl_add_defun ();
+
+#endif /* _READLINE_H_ */
+
diff --git a/gnu/usr.bin/gdb/readline/vi_keymap.c b/gnu/usr.bin/gdb/readline/vi_keymap.c
new file mode 100644
index 0000000..71c7ec8
--- /dev/null
+++ b/gnu/usr.bin/gdb/readline/vi_keymap.c
@@ -0,0 +1,484 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ *
+ * @(#)vi_keymap.c 6.4 (Berkeley) 5/8/91
+ */
+
+/* vi_keymap.c -- the keymap for vi_mode in readline (). */
+
+/* Copyright (C) 1988,1989 Free Software Foundation, Inc.
+
+ This file is part of GNU Readline, a library for reading lines
+ of text with interactive input and history editing.
+
+ Readline is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 1, or (at your option) any
+ later version.
+
+ Readline is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Readline; see the file COPYING. If not, write to the Free
+ Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef FILE
+#include <stdio.h>
+#endif /* FILE */
+
+#include "readline.h"
+
+extern KEYMAP_ENTRY_ARRAY vi_escape_keymap;
+
+/* The keymap arrays for handling vi mode. */
+KEYMAP_ENTRY_ARRAY vi_movement_keymap = {
+
+ /* The regular control keys come first. */
+ { ISFUNC, (Function *)0x0 }, /* Control-@ */
+ { ISFUNC, (Function *)0x0 }, /* Control-a */
+ { ISFUNC, (Function *)0x0 }, /* Control-b */
+ { ISFUNC, (Function *)0x0 }, /* Control-c */
+ { ISFUNC, rl_vi_eof_maybe }, /* Control-d */
+ { ISFUNC, rl_emacs_editing_mode }, /* Control-e */
+ { ISFUNC, (Function *)0x0 }, /* Control-f */
+ { ISFUNC, rl_abort }, /* Control-g */
+ { ISFUNC, rl_backward }, /* Control-h */
+ { ISFUNC, (Function *)0x0 }, /* Control-i */
+ { ISFUNC, rl_newline }, /* Control-j */
+ { ISFUNC, rl_kill_line }, /* Control-k */
+ { ISFUNC, rl_clear_screen }, /* Control-l */
+ { ISFUNC, rl_newline }, /* Control-m */
+ { ISFUNC, rl_get_next_history }, /* Control-n */
+ { ISFUNC, (Function *)0x0 }, /* Control-o */
+ { ISFUNC, rl_get_previous_history }, /* Control-p */
+ { ISFUNC, rl_quoted_insert }, /* Control-q */
+ { ISFUNC, rl_reverse_search_history }, /* Control-r */
+ { ISFUNC, rl_forward_search_history }, /* Control-s */
+ { ISFUNC, rl_transpose_chars }, /* Control-t */
+ { ISFUNC, rl_unix_line_discard }, /* Control-u */
+ { ISFUNC, rl_quoted_insert }, /* Control-v */
+ { ISFUNC, rl_unix_word_rubout }, /* Control-w */
+ { ISFUNC, (Function *)0x0 }, /* Control-x */
+ { ISFUNC, rl_yank }, /* Control-y */
+ { ISFUNC, (Function *)0x0 }, /* Control-z */
+
+ { ISKMAP, (Function *)vi_escape_keymap }, /* Control-[ */
+ { ISFUNC, (Function *)0x0 }, /* Control-\ */
+ { ISFUNC, (Function *)0x0 }, /* Control-] */
+ { ISFUNC, (Function *)0x0 }, /* Control-^ */
+ { ISFUNC, rl_undo_command }, /* Control-_ */
+
+ /* The start of printing characters. */
+ { ISFUNC, rl_forward }, /* SPACE */
+ { ISFUNC, (Function *)0x0 }, /* ! */
+ { ISFUNC, (Function *)0x0 }, /* " */
+ { ISFUNC, rl_vi_comment }, /* # */
+ { ISFUNC, rl_end_of_line }, /* $ */
+ { ISFUNC, rl_vi_match }, /* % */
+ { ISFUNC, (Function *)0x0 }, /* & */
+ { ISFUNC, (Function *)0x0 }, /* ' */
+ { ISFUNC, (Function *)0x0 }, /* ( */
+ { ISFUNC, (Function *)0x0 }, /* ) */
+ { ISFUNC, rl_vi_complete }, /* * */
+ { ISFUNC, rl_get_previous_history}, /* + */
+ { ISFUNC, rl_vi_char_search }, /* , */
+ { ISFUNC, rl_get_next_history }, /* - */
+ { ISFUNC, (Function *)0x0 }, /* . */
+ { ISFUNC, rl_vi_search }, /* / */
+
+ /* Regular digits. */
+ { ISFUNC, rl_vi_arg_digit }, /* 0 */
+ { ISFUNC, rl_vi_arg_digit }, /* 1 */
+ { ISFUNC, rl_vi_arg_digit }, /* 2 */
+ { ISFUNC, rl_vi_arg_digit }, /* 3 */
+ { ISFUNC, rl_vi_arg_digit }, /* 4 */
+ { ISFUNC, rl_vi_arg_digit }, /* 5 */
+ { ISFUNC, rl_vi_arg_digit }, /* 6 */
+ { ISFUNC, rl_vi_arg_digit }, /* 7 */
+ { ISFUNC, rl_vi_arg_digit }, /* 8 */
+ { ISFUNC, rl_vi_arg_digit }, /* 9 */
+
+ /* A little more punctuation. */
+ { ISFUNC, (Function *)0x0 }, /* : */
+ { ISFUNC, rl_vi_char_search }, /* ; */
+ { ISFUNC, (Function *)0x0 }, /* < */
+ { ISFUNC, (Function *)0x0 }, /* = */
+ { ISFUNC, (Function *)0x0 }, /* > */
+ { ISFUNC, rl_vi_search }, /* ? */
+ { ISFUNC, (Function *)0x0 }, /* @ */
+
+ /* Uppercase alphabet. */
+ { ISFUNC, rl_vi_append_eol }, /* A */
+ { ISFUNC, rl_vi_prev_word}, /* B */
+ { ISFUNC, rl_vi_change_to }, /* C */
+ { ISFUNC, rl_vi_delete_to }, /* D */
+ { ISFUNC, rl_vi_end_word }, /* E */
+ { ISFUNC, rl_vi_char_search }, /* F */
+ { ISFUNC, (Function *)0x0 }, /* G */
+ { ISFUNC, (Function *)0x0 }, /* H */
+ { ISFUNC, rl_vi_insert_beg }, /* I */
+ { ISFUNC, (Function *)0x0 }, /* J */
+ { ISFUNC, (Function *)0x0 }, /* K */
+ { ISFUNC, (Function *)0x0 }, /* L */
+ { ISFUNC, (Function *)0x0 }, /* M */
+ { ISFUNC, rl_vi_search_again }, /* N */
+ { ISFUNC, (Function *)0x0 }, /* O */
+ { ISFUNC, rl_vi_put }, /* P */
+ { ISFUNC, (Function *)0x0 }, /* Q */
+ { ISFUNC, rl_vi_replace }, /* R */
+ { ISFUNC, rl_vi_subst }, /* S */
+ { ISFUNC, rl_vi_char_search }, /* T */
+ { ISFUNC, rl_revert_line }, /* U */
+ { ISFUNC, (Function *)0x0 }, /* V */
+ { ISFUNC, rl_vi_next_word }, /* W */
+ { ISFUNC, rl_rubout }, /* X */
+ { ISFUNC, rl_vi_yank_to }, /* Y */
+ { ISFUNC, (Function *)0x0 }, /* Z */
+
+ /* Some more punctuation. */
+ { ISFUNC, (Function *)0x0 }, /* [ */
+ { ISFUNC, (Function *)0x0 }, /* \ */
+ { ISFUNC, (Function *)0x0 }, /* ] */
+ { ISFUNC, rl_vi_first_print }, /* ^ */
+ { ISFUNC, rl_vi_yank_arg }, /* _ */
+ { ISFUNC, (Function *)0x0 }, /* ` */
+
+ /* Lowercase alphabet. */
+ { ISFUNC, rl_vi_append_mode }, /* a */
+ { ISFUNC, rl_vi_prev_word }, /* b */
+ { ISFUNC, rl_vi_change_to }, /* c */
+ { ISFUNC, rl_vi_delete_to }, /* d */
+ { ISFUNC, rl_vi_end_word }, /* e */
+ { ISFUNC, rl_vi_char_search }, /* f */
+ { ISFUNC, (Function *)0x0 }, /* g */
+ { ISFUNC, rl_backward }, /* h */
+ { ISFUNC, rl_vi_insertion_mode }, /* i */
+ { ISFUNC, rl_get_next_history }, /* j */
+ { ISFUNC, rl_get_previous_history }, /* k */
+ { ISFUNC, rl_forward }, /* l */
+ { ISFUNC, (Function *)0x0 }, /* m */
+ { ISFUNC, rl_vi_search_again }, /* n */
+ { ISFUNC, (Function *)0x0 }, /* o */
+ { ISFUNC, rl_vi_put }, /* p */
+ { ISFUNC, (Function *)0x0 }, /* q */
+ { ISFUNC, rl_vi_change_char }, /* r */
+ { ISFUNC, rl_vi_subst }, /* s */
+ { ISFUNC, rl_vi_char_search }, /* t */
+ { ISFUNC, rl_undo_command }, /* u */
+ { ISFUNC, (Function *)0x0 }, /* v */
+ { ISFUNC, rl_vi_next_word }, /* w */
+ { ISFUNC, rl_vi_delete }, /* x */
+ { ISFUNC, rl_vi_yank_to }, /* y */
+ { ISFUNC, (Function *)0x0 }, /* z */
+
+ /* Final punctuation. */
+ { ISFUNC, (Function *)0x0 }, /* { */
+ { ISFUNC, rl_vi_column }, /* | */
+ { ISFUNC, (Function *)0x0 }, /* } */
+ { ISFUNC, rl_vi_change_case }, /* ~ */
+ { ISFUNC, rl_backward } /* RUBOUT */
+};
+
+
+KEYMAP_ENTRY_ARRAY vi_insertion_keymap = {
+
+ /* The regular control keys come first. */
+ { ISFUNC, (Function *)0x0 }, /* Control-@ */
+ { ISFUNC, rl_insert }, /* Control-a */
+ { ISFUNC, rl_insert }, /* Control-b */
+ { ISFUNC, rl_insert }, /* Control-c */
+ { ISFUNC, rl_vi_eof_maybe }, /* Control-d */
+ { ISFUNC, rl_insert }, /* Control-e */
+ { ISFUNC, rl_insert }, /* Control-f */
+ { ISFUNC, rl_insert }, /* Control-g */
+ { ISFUNC, rl_rubout }, /* Control-h */
+ { ISFUNC, rl_complete }, /* Control-i */
+ { ISFUNC, rl_newline }, /* Control-j */
+ { ISFUNC, rl_insert }, /* Control-k */
+ { ISFUNC, rl_insert }, /* Control-l */
+ { ISFUNC, rl_newline }, /* Control-m */
+ { ISFUNC, rl_insert }, /* Control-n */
+ { ISFUNC, rl_insert }, /* Control-o */
+ { ISFUNC, rl_insert }, /* Control-p */
+ { ISFUNC, rl_insert }, /* Control-q */
+ { ISFUNC, rl_reverse_search_history }, /* Control-r */
+ { ISFUNC, rl_forward_search_history }, /* Control-s */
+ { ISFUNC, rl_transpose_chars }, /* Control-t */
+ { ISFUNC, rl_unix_line_discard }, /* Control-u */
+ { ISFUNC, rl_quoted_insert }, /* Control-v */
+ { ISFUNC, rl_unix_word_rubout }, /* Control-w */
+ { ISFUNC, rl_insert }, /* Control-x */
+ { ISFUNC, rl_yank }, /* Control-y */
+ { ISFUNC, rl_insert }, /* Control-z */
+
+ { ISFUNC, rl_vi_movement_mode }, /* Control-[ */
+ { ISFUNC, rl_insert }, /* Control-\ */
+ { ISFUNC, rl_insert }, /* Control-] */
+ { ISFUNC, rl_insert }, /* Control-^ */
+ { ISFUNC, rl_undo_command }, /* Control-_ */
+
+ /* The start of printing characters. */
+ { ISFUNC, rl_insert }, /* SPACE */
+ { ISFUNC, rl_insert }, /* ! */
+ { ISFUNC, rl_insert }, /* " */
+ { ISFUNC, rl_insert }, /* # */
+ { ISFUNC, rl_insert }, /* $ */
+ { ISFUNC, rl_insert }, /* % */
+ { ISFUNC, rl_insert }, /* & */
+ { ISFUNC, rl_insert }, /* ' */
+ { ISFUNC, rl_insert }, /* ( */
+ { ISFUNC, rl_insert }, /* ) */
+ { ISFUNC, rl_insert }, /* * */
+ { ISFUNC, rl_insert }, /* + */
+ { ISFUNC, rl_insert }, /* , */
+ { ISFUNC, rl_insert }, /* - */
+ { ISFUNC, rl_insert }, /* . */
+ { ISFUNC, rl_insert }, /* / */
+
+ /* Regular digits. */
+ { ISFUNC, rl_insert }, /* 0 */
+ { ISFUNC, rl_insert }, /* 1 */
+ { ISFUNC, rl_insert }, /* 2 */
+ { ISFUNC, rl_insert }, /* 3 */
+ { ISFUNC, rl_insert }, /* 4 */
+ { ISFUNC, rl_insert }, /* 5 */
+ { ISFUNC, rl_insert }, /* 6 */
+ { ISFUNC, rl_insert }, /* 7 */
+ { ISFUNC, rl_insert }, /* 8 */
+ { ISFUNC, rl_insert }, /* 9 */
+
+ /* A little more punctuation. */
+ { ISFUNC, rl_insert }, /* : */
+ { ISFUNC, rl_insert }, /* ; */
+ { ISFUNC, rl_insert }, /* < */
+ { ISFUNC, rl_insert }, /* = */
+ { ISFUNC, rl_insert }, /* > */
+ { ISFUNC, rl_insert }, /* ? */
+ { ISFUNC, rl_insert }, /* @ */
+
+ /* Uppercase alphabet. */
+ { ISFUNC, rl_insert }, /* A */
+ { ISFUNC, rl_insert }, /* B */
+ { ISFUNC, rl_insert }, /* C */
+ { ISFUNC, rl_insert }, /* D */
+ { ISFUNC, rl_insert }, /* E */
+ { ISFUNC, rl_insert }, /* F */
+ { ISFUNC, rl_insert }, /* G */
+ { ISFUNC, rl_insert }, /* H */
+ { ISFUNC, rl_insert }, /* I */
+ { ISFUNC, rl_insert }, /* J */
+ { ISFUNC, rl_insert }, /* K */
+ { ISFUNC, rl_insert }, /* L */
+ { ISFUNC, rl_insert }, /* M */
+ { ISFUNC, rl_insert }, /* N */
+ { ISFUNC, rl_insert }, /* O */
+ { ISFUNC, rl_insert }, /* P */
+ { ISFUNC, rl_insert }, /* Q */
+ { ISFUNC, rl_insert }, /* R */
+ { ISFUNC, rl_insert }, /* S */
+ { ISFUNC, rl_insert }, /* T */
+ { ISFUNC, rl_insert }, /* U */
+ { ISFUNC, rl_insert }, /* V */
+ { ISFUNC, rl_insert }, /* W */
+ { ISFUNC, rl_insert }, /* X */
+ { ISFUNC, rl_insert }, /* Y */
+ { ISFUNC, rl_insert }, /* Z */
+
+ /* Some more punctuation. */
+ { ISFUNC, rl_insert }, /* [ */
+ { ISFUNC, rl_insert }, /* \ */
+ { ISFUNC, rl_insert }, /* ] */
+ { ISFUNC, rl_insert }, /* ^ */
+ { ISFUNC, rl_insert }, /* _ */
+ { ISFUNC, rl_insert }, /* ` */
+
+ /* Lowercase alphabet. */
+ { ISFUNC, rl_insert }, /* a */
+ { ISFUNC, rl_insert }, /* b */
+ { ISFUNC, rl_insert }, /* c */
+ { ISFUNC, rl_insert }, /* d */
+ { ISFUNC, rl_insert }, /* e */
+ { ISFUNC, rl_insert }, /* f */
+ { ISFUNC, rl_insert }, /* g */
+ { ISFUNC, rl_insert }, /* h */
+ { ISFUNC, rl_insert }, /* i */
+ { ISFUNC, rl_insert }, /* j */
+ { ISFUNC, rl_insert }, /* k */
+ { ISFUNC, rl_insert }, /* l */
+ { ISFUNC, rl_insert }, /* m */
+ { ISFUNC, rl_insert }, /* n */
+ { ISFUNC, rl_insert }, /* o */
+ { ISFUNC, rl_insert }, /* p */
+ { ISFUNC, rl_insert }, /* q */
+ { ISFUNC, rl_insert }, /* r */
+ { ISFUNC, rl_insert }, /* s */
+ { ISFUNC, rl_insert }, /* t */
+ { ISFUNC, rl_insert }, /* u */
+ { ISFUNC, rl_insert }, /* v */
+ { ISFUNC, rl_insert }, /* w */
+ { ISFUNC, rl_insert }, /* x */
+ { ISFUNC, rl_insert }, /* y */
+ { ISFUNC, rl_insert }, /* z */
+
+ /* Final punctuation. */
+ { ISFUNC, rl_insert }, /* { */
+ { ISFUNC, rl_insert }, /* | */
+ { ISFUNC, rl_insert }, /* } */
+ { ISFUNC, rl_insert }, /* ~ */
+ { ISFUNC, rl_rubout } /* RUBOUT */
+};
+
+KEYMAP_ENTRY_ARRAY vi_escape_keymap = {
+
+ /* The regular control keys come first. */
+ { ISFUNC, (Function *)0x0 }, /* Control-@ */
+ { ISFUNC, (Function *)0x0 }, /* Control-a */
+ { ISFUNC, (Function *)0x0 }, /* Control-b */
+ { ISFUNC, (Function *)0x0 }, /* Control-c */
+ { ISFUNC, (Function *)0x0 }, /* Control-d */
+ { ISFUNC, (Function *)0x0 }, /* Control-e */
+ { ISFUNC, (Function *)0x0 }, /* Control-f */
+ { ISFUNC, (Function *)0x0 }, /* Control-g */
+ { ISFUNC, (Function *)0x0 }, /* Control-h */
+ { ISFUNC, rl_tab_insert}, /* Control-i */
+ { ISFUNC, rl_emacs_editing_mode}, /* Control-j */
+ { ISFUNC, rl_kill_line }, /* Control-k */
+ { ISFUNC, (Function *)0x0 }, /* Control-l */
+ { ISFUNC, rl_emacs_editing_mode}, /* Control-m */
+ { ISFUNC, (Function *)0x0 }, /* Control-n */
+ { ISFUNC, (Function *)0x0 }, /* Control-o */
+ { ISFUNC, (Function *)0x0 }, /* Control-p */
+ { ISFUNC, (Function *)0x0 }, /* Control-q */
+ { ISFUNC, (Function *)0x0 }, /* Control-r */
+ { ISFUNC, (Function *)0x0 }, /* Control-s */
+ { ISFUNC, (Function *)0x0 }, /* Control-t */
+ { ISFUNC, (Function *)0x0 }, /* Control-u */
+ { ISFUNC, (Function *)0x0 }, /* Control-v */
+ { ISFUNC, (Function *)0x0 }, /* Control-w */
+ { ISFUNC, (Function *)0x0 }, /* Control-x */
+ { ISFUNC, (Function *)0x0 }, /* Control-y */
+ { ISFUNC, (Function *)0x0 }, /* Control-z */
+
+ { ISFUNC, rl_vi_movement_mode }, /* Control-[ */
+ { ISFUNC, (Function *)0x0 }, /* Control-\ */
+ { ISFUNC, (Function *)0x0 }, /* Control-] */
+ { ISFUNC, (Function *)0x0 }, /* Control-^ */
+ { ISFUNC, rl_undo_command }, /* Control-_ */
+
+ /* The start of printing characters. */
+ { ISFUNC, (Function *)0x0 }, /* SPACE */
+ { ISFUNC, (Function *)0x0 }, /* ! */
+ { ISFUNC, (Function *)0x0 }, /* " */
+ { ISFUNC, (Function *)0x0 }, /* # */
+ { ISFUNC, (Function *)0x0 }, /* $ */
+ { ISFUNC, (Function *)0x0 }, /* % */
+ { ISFUNC, (Function *)0x0 }, /* & */
+ { ISFUNC, (Function *)0x0 }, /* ' */
+ { ISFUNC, (Function *)0x0 }, /* ( */
+ { ISFUNC, (Function *)0x0 }, /* ) */
+ { ISFUNC, (Function *)0x0 }, /* * */
+ { ISFUNC, (Function *)0x0 }, /* + */
+ { ISFUNC, (Function *)0x0 }, /* , */
+ { ISFUNC, (Function *)0x0 }, /* - */
+ { ISFUNC, (Function *)0x0 }, /* . */
+ { ISFUNC, (Function *)0x0 }, /* / */
+
+ /* Regular digits. */
+ { ISFUNC, rl_vi_arg_digit }, /* 0 */
+ { ISFUNC, rl_vi_arg_digit }, /* 1 */
+ { ISFUNC, rl_vi_arg_digit }, /* 2 */
+ { ISFUNC, rl_vi_arg_digit }, /* 3 */
+ { ISFUNC, rl_vi_arg_digit }, /* 4 */
+ { ISFUNC, rl_vi_arg_digit }, /* 5 */
+ { ISFUNC, rl_vi_arg_digit }, /* 6 */
+ { ISFUNC, rl_vi_arg_digit }, /* 7 */
+ { ISFUNC, rl_vi_arg_digit }, /* 8 */
+ { ISFUNC, rl_vi_arg_digit }, /* 9 */
+
+ /* A little more punctuation. */
+ { ISFUNC, (Function *)0x0 }, /* : */
+ { ISFUNC, (Function *)0x0 }, /* ; */
+ { ISFUNC, (Function *)0x0 }, /* < */
+ { ISFUNC, (Function *)0x0 }, /* = */
+ { ISFUNC, (Function *)0x0 }, /* > */
+ { ISFUNC, (Function *)0x0 }, /* ? */
+ { ISFUNC, (Function *)0x0 }, /* @ */
+
+ /* Uppercase alphabet. */
+ { ISFUNC, rl_do_lowercase_version }, /* A */
+ { ISFUNC, rl_do_lowercase_version }, /* B */
+ { ISFUNC, rl_do_lowercase_version }, /* C */
+ { ISFUNC, rl_do_lowercase_version }, /* D */
+ { ISFUNC, rl_do_lowercase_version }, /* E */
+ { ISFUNC, rl_do_lowercase_version }, /* F */
+ { ISFUNC, rl_do_lowercase_version }, /* G */
+ { ISFUNC, rl_do_lowercase_version }, /* H */
+ { ISFUNC, rl_do_lowercase_version }, /* I */
+ { ISFUNC, rl_do_lowercase_version }, /* J */
+ { ISFUNC, rl_do_lowercase_version }, /* K */
+ { ISFUNC, rl_do_lowercase_version }, /* L */
+ { ISFUNC, rl_do_lowercase_version }, /* M */
+ { ISFUNC, rl_do_lowercase_version }, /* N */
+ { ISFUNC, rl_do_lowercase_version }, /* O */
+ { ISFUNC, rl_do_lowercase_version }, /* P */
+ { ISFUNC, rl_do_lowercase_version }, /* Q */
+ { ISFUNC, rl_do_lowercase_version }, /* R */
+ { ISFUNC, rl_do_lowercase_version }, /* S */
+ { ISFUNC, rl_do_lowercase_version }, /* T */
+ { ISFUNC, rl_do_lowercase_version }, /* U */
+ { ISFUNC, rl_do_lowercase_version }, /* V */
+ { ISFUNC, rl_do_lowercase_version }, /* W */
+ { ISFUNC, rl_do_lowercase_version }, /* X */
+ { ISFUNC, rl_do_lowercase_version }, /* Y */
+ { ISFUNC, rl_do_lowercase_version }, /* Z */
+
+ /* Some more punctuation. */
+ { ISFUNC, (Function *)0x0 }, /* [ */
+ { ISFUNC, (Function *)0x0 }, /* \ */
+ { ISFUNC, (Function *)0x0 }, /* ] */
+ { ISFUNC, (Function *)0x0 }, /* ^ */
+ { ISFUNC, (Function *)0x0 }, /* _ */
+ { ISFUNC, (Function *)0x0 }, /* ` */
+
+ /* Lowercase alphabet. */
+ { ISFUNC, (Function *)0x0 }, /* a */
+ { ISFUNC, (Function *)0x0 }, /* b */
+ { ISFUNC, (Function *)0x0 }, /* c */
+ { ISFUNC, (Function *)0x0 }, /* d */
+ { ISFUNC, (Function *)0x0 }, /* e */
+ { ISFUNC, (Function *)0x0 }, /* f */
+ { ISFUNC, (Function *)0x0 }, /* g */
+ { ISFUNC, (Function *)0x0 }, /* h */
+ { ISFUNC, (Function *)0x0 }, /* i */
+ { ISFUNC, (Function *)0x0 }, /* j */
+ { ISFUNC, (Function *)0x0 }, /* k */
+ { ISFUNC, (Function *)0x0 }, /* l */
+ { ISFUNC, (Function *)0x0 }, /* m */
+ { ISFUNC, (Function *)0x0 }, /* n */
+ { ISFUNC, (Function *)0x0 }, /* o */
+ { ISFUNC, (Function *)0x0 }, /* p */
+ { ISFUNC, (Function *)0x0 }, /* q */
+ { ISFUNC, (Function *)0x0 }, /* r */
+ { ISFUNC, (Function *)0x0 }, /* s */
+ { ISFUNC, (Function *)0x0 }, /* t */
+ { ISFUNC, (Function *)0x0 }, /* u */
+ { ISFUNC, (Function *)0x0 }, /* v */
+ { ISFUNC, (Function *)0x0 }, /* w */
+ { ISFUNC, (Function *)0x0 }, /* x */
+ { ISFUNC, (Function *)0x0 }, /* y */
+ { ISFUNC, (Function *)0x0 }, /* z */
+
+ /* Final punctuation. */
+ { ISFUNC, (Function *)0x0 }, /* { */
+ { ISFUNC, (Function *)0x0 }, /* | */
+ { ISFUNC, (Function *)0x0 }, /* } */
+ { ISFUNC, (Function *)0x0 }, /* ~ */
+ { ISFUNC, rl_backward_kill_word } /* RUBOUT */
+};
diff --git a/gnu/usr.bin/gdb/readline/vi_mode.c b/gnu/usr.bin/gdb/readline/vi_mode.c
new file mode 100644
index 0000000..3a13cc6
--- /dev/null
+++ b/gnu/usr.bin/gdb/readline/vi_mode.c
@@ -0,0 +1,875 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ *
+ * @(#)vi_mode.c 6.4 (Berkeley) 5/8/91
+ */
+
+/* vi_mode.c -- A vi emulation mode for Bash.
+ Mostly written by Jeff Sparkes (jeff1@????).
+ */
+
+
+/* **************************************************************** */
+/* */
+/* VI Emulation Mode */
+/* */
+/* **************************************************************** */
+
+/* Last string searched for from `/' or `?'. */
+static char *vi_last_search = (char *)NULL;
+static int vi_histpos;
+
+/* *** UNCLEAN *** */
+/* Command keys which do movement for xxx_to commands. */
+static char *vi_motion = " hl^$0ftFt;,%wbeWBE|";
+
+/* Keymap used for vi replace characters. Created dynamically since
+ rarely used. */
+static Keymap vi_replace_map = (Keymap)NULL;
+
+/* The number of characters inserted in the last replace operation. */
+static vi_replace_count = 0;
+
+/* Yank the nth arg from the previous line into this line at point. */
+rl_vi_yank_arg (count)
+ int count;
+{
+ rl_yank_nth_arg (count);
+}
+
+/* Search again for the last thing searched for. */
+rl_vi_search_again (ignore, key)
+ int ignore, key;
+{
+ switch (key)
+ {
+ case 'n':
+ rl_vi_dosearch (vi_last_search, -1);
+ break;
+
+ case 'N':
+ rl_vi_dosearch (vi_last_search, 1);
+ break;
+ }
+}
+
+/* Do a vi style search. */
+rl_vi_search (count, key)
+ int count, key;
+{
+ int dir, c;
+ char *p;
+
+ switch (key)
+ {
+ case '?':
+ dir = 1;
+ break;
+
+ case '/':
+ dir = -1;
+ break;
+
+ default:
+ ding ();
+ return;
+ }
+
+ vi_histpos = where_history ();
+ maybe_save_line ();
+
+ /* Reuse the line input buffer to read the search string. */
+ the_line[0] = 0;
+ rl_end = rl_point = 0;
+ p = (char *)alloca (2 + (rl_prompt ? strlen (rl_prompt) : 0));
+
+ sprintf (p, "%s%c", rl_prompt ? rl_prompt : "", key);
+
+ rl_message (p);
+
+ while (c = rl_read_key (in_stream))
+ {
+ switch (c)
+ {
+ case CTRL('W'):
+ case CTRL('U'):
+ case CTRL('H'):
+ case RUBOUT:
+ rl_dispatch (c, keymap);
+ break;
+
+ case ESC:
+ case RETURN:
+ case NEWLINE:
+ goto dosearch;
+ break;
+
+ case CTRL('C'):
+ maybe_unsave_line ();
+ rl_clear_message ();
+ rl_point = 0;
+ ding ();
+ return;
+
+ default:
+ rl_insert (1, c);
+ break;
+ }
+ rl_redisplay ();
+ }
+ dosearch:
+ if (vi_last_search)
+ free (vi_last_search);
+
+ vi_last_search = savestring (the_line);
+ rl_vi_dosearch (the_line, dir);
+}
+
+rl_vi_dosearch (string, dir)
+ char *string;
+ int dir;
+{
+ int old, save = vi_histpos;
+ HIST_ENTRY *h;
+
+ if (string == 0 || *string == 0 || vi_histpos < 0)
+ {
+ ding ();
+ return;
+ }
+
+ if ((save = history_search_pos (string, dir, vi_histpos + dir)) == -1)
+ {
+ maybe_unsave_line ();
+ rl_clear_message ();
+ rl_point = 0;
+ ding ();
+ return;
+ }
+
+ vi_histpos = save;
+
+ old = where_history ();
+ history_set_pos (vi_histpos);
+ h = current_history ();
+ history_set_pos (old);
+
+ strcpy (the_line, h->line);
+ rl_undo_list = (UNDO_LIST *)h->data;
+ rl_end = strlen (the_line);
+ rl_point = 0;
+ rl_clear_message ();
+}
+
+/* Completion, from vi's point of view. */
+rl_vi_complete (ignore, key)
+ int ignore, key;
+{
+ if (!whitespace (the_line[rl_point]))
+ {
+ rl_vi_end_word (1, 'E');
+ rl_point++;
+ }
+ rl_complete_internal ('*');
+ rl_vi_insertion_mode ();
+}
+
+/* Previous word in vi mode. */
+rl_vi_prev_word (count, key)
+ int count, key;
+{
+ if (count < 0)
+ {
+ rl_vi_next_word (-count, key);
+ return;
+ }
+
+ if (uppercase_p (key))
+ rl_vi_bWord (count);
+ else
+ rl_vi_bword (count);
+}
+
+/* Next word in vi mode. */
+rl_vi_next_word (count, key)
+ int count;
+{
+ if (count < 0)
+ {
+ rl_vi_prev_word (-count, key);
+ return;
+ }
+
+ if (uppercase_p (key))
+ rl_vi_fWord (count);
+ else
+ rl_vi_fword (count);
+}
+
+/* Move to the end of the ?next? word. */
+rl_vi_end_word (count, key)
+ int count, key;
+{
+ if (count < 0)
+ {
+ ding ();
+ return;
+ }
+
+ if (uppercase_p (key))
+ rl_vi_eWord (count);
+ else
+ rl_vi_eword (count);
+}
+
+/* Move forward a word the way that 'W' does. */
+rl_vi_fWord (count)
+ int count;
+{
+ while (count-- && rl_point < (rl_end - 1))
+ {
+ /* Skip until whitespace. */
+ while (!whitespace (the_line[rl_point]) && rl_point < rl_end)
+ rl_point++;
+
+ /* Now skip whitespace. */
+ while (whitespace (the_line[rl_point]) && rl_point < rl_end)
+ rl_point++;
+ }
+}
+
+rl_vi_bWord (count)
+ int count;
+{
+ while (count-- && rl_point > 0)
+ {
+ while (rl_point-- >= 0 && whitespace (the_line[rl_point]));
+ while (rl_point >= 0 && !whitespace (the_line[rl_point]))
+ rl_point--;
+ rl_point++;
+ }
+}
+
+rl_vi_eWord (count)
+ int count;
+{
+ while (count -- && rl_point < (rl_end - 1))
+ {
+ while (rl_point++ < rl_end && whitespace (the_line[rl_point]));
+ while (rl_point++ < rl_end && !whitespace (the_line[rl_point]));
+ rl_point--;
+ }
+}
+
+rl_vi_fword (count)
+ int count;
+{
+ while (count -- && rl_point < (rl_end - 1))
+ {
+ if (isident (the_line[rl_point]))
+ {
+ while (isident (the_line[rl_point]) && rl_point < rl_end)
+ rl_point += 1;
+ }
+ else if (!whitespace (the_line[rl_point]))
+ {
+ while (!isident (the_line[rl_point]) &&
+ !whitespace (the_line[rl_point]) && rl_point < rl_end)
+ rl_point += 1;
+ }
+
+ while (whitespace (the_line[rl_point]) && rl_point < rl_end)
+ rl_point++;
+ }
+}
+
+rl_vi_bword (count)
+ int count;
+{
+ while (count -- && rl_point > 0)
+ {
+ while (--rl_point > 0 && whitespace (the_line[rl_point]));
+ if (rl_point > 0)
+ {
+ if (isident (the_line[rl_point]))
+ while (--rl_point >= 0 && isident (the_line[rl_point]));
+ else
+ while (--rl_point >= 0 && !isident (the_line[rl_point]) &&
+ !whitespace (the_line[rl_point]));
+ rl_point++;
+ }
+ }
+}
+
+rl_vi_eword (count)
+ int count;
+{
+ while (count -- && rl_point < rl_end - 1)
+ {
+ while (++rl_point < rl_end && whitespace (the_line[rl_point]));
+
+ if (rl_point < rl_end)
+ {
+ if (isident (the_line[rl_point]))
+ while (++rl_point < rl_end && isident (the_line[rl_point]));
+ else
+ while (++rl_point < rl_end && !isident (the_line[rl_point])
+ && !whitespace (the_line[rl_point]));
+ rl_point--;
+ }
+ }
+}
+
+rl_vi_insert_beg ()
+{
+ rl_beg_of_line ();
+ rl_vi_insertion_mode ();
+ return 0;
+}
+
+rl_vi_append_mode ()
+{
+ if (rl_point < rl_end)
+ rl_point += 1;
+ rl_vi_insertion_mode ();
+ return 0;
+}
+
+rl_vi_append_eol ()
+{
+ rl_end_of_line ();
+ rl_vi_append_mode ();
+ return 0;
+}
+
+/* What to do in the case of C-d. */
+rl_vi_eof_maybe (count, c)
+ int count, c;
+{
+ rl_newline (1, '\n');
+}
+
+/* Insertion mode stuff. */
+
+/* Switching from one mode to the other really just involves
+ switching keymaps. */
+rl_vi_insertion_mode ()
+{
+ keymap = vi_insertion_keymap;
+}
+
+rl_vi_movement_mode ()
+{
+ if (rl_point > 0)
+ rl_backward (1);
+
+ keymap = vi_movement_keymap;
+ if (vi_doing_insert)
+ {
+ rl_end_undo_group ();
+ vi_doing_insert = 0;
+ }
+}
+
+rl_vi_arg_digit (count, c)
+ int count, c;
+{
+ if (c == '0' && rl_numeric_arg == 1 && !rl_explicit_arg)
+ rl_beg_of_line ();
+ else
+ rl_digit_argument (count, c);
+}
+
+/* Doesn't take an arg count in vi */
+rl_vi_change_case (ignore1, ignore2)
+ int ignore1, ignore2;
+{
+ char c = 0;
+
+ if (uppercase_p (the_line[rl_point]))
+ c = to_lower (the_line[rl_point]);
+ else if (lowercase_p (the_line[rl_point]))
+ c = to_upper (the_line[rl_point]);
+
+ /* Vi is kind of strange here. */
+ if (c)
+ {
+ rl_begin_undo_group ();
+ rl_delete (1);
+ rl_insert (1, c);
+ rl_end_undo_group ();
+ rl_vi_check ();
+ }
+ else
+ rl_forward (1);
+}
+
+rl_vi_put (count, key)
+ int count, key;
+{
+ if (!uppercase_p (key))
+ {
+ if(rl_point != rl_end)
+ rl_point++;
+ }
+
+ rl_yank ();
+ rl_backward (1);
+}
+
+rl_vi_check ()
+{
+ if (rl_point && rl_point == rl_end)
+ rl_point--;
+}
+
+rl_vi_column (count)
+{
+ if (count > rl_end)
+ rl_end_of_line ();
+ else
+ rl_point = count - 1;
+}
+
+int
+rl_vi_domove ()
+{
+ int c, save;
+
+ rl_mark = rl_point;
+ c = rl_read_key (in_stream);
+
+ if (!member (c, vi_motion))
+ {
+ if (digit (c))
+ {
+ save = rl_numeric_arg;
+ rl_digit_loop1 ();
+ rl_numeric_arg *= save;
+ }
+ else
+ return (-1);
+ }
+
+ rl_dispatch (c, keymap);
+
+ /* No change in position means the command failed. */
+ if (rl_mark == rl_point)
+ return (-1);
+
+ if ((c == 'w' || c == 'W') && rl_point < rl_end)
+ {
+ rl_point--;
+ while((rl_point > 0) && whitespace (the_line[rl_point]))
+ rl_point--;
+ rl_point++;
+ }
+
+ if (rl_mark < rl_point)
+ exchange (rl_point, rl_mark);
+
+ return (0);
+}
+
+/* A simplified loop for vi. Don't dispatch key at end.
+ Don't recognize minus sign? */
+rl_digit_loop1 ()
+{
+ int key, c;
+
+ while (1)
+ {
+ rl_message ("(arg: %d) ", arg_sign * rl_numeric_arg);
+ key = c = rl_read_key ();
+
+ if (keymap[c].type == ISFUNC &&
+ keymap[c].function == rl_universal_argument)
+ {
+ rl_numeric_arg *= 4;
+ continue;
+ }
+ c = UNMETA (c);
+ if (numeric (c))
+ {
+ if (rl_explicit_arg)
+ rl_numeric_arg = (rl_numeric_arg * 10) + (c - '0');
+ else
+ rl_numeric_arg = (c - '0');
+ rl_explicit_arg = 1;
+ }
+ else
+ {
+ rl_clear_message ();
+ rl_stuff_char (key);
+ }
+ }
+}
+
+rl_vi_delete_to (count, key)
+ int count, key;
+{
+ if (uppercase_p (key))
+ rl_stuff_char ('$');
+
+ if (rl_vi_domove ())
+ {
+ ding ();
+ return;
+ }
+
+ rl_kill_text (rl_point, rl_mark);
+}
+
+rl_vi_change_to (count, key)
+ int count, key;
+{
+ if (uppercase_p (key))
+ rl_stuff_char ('$');
+
+ if (rl_vi_domove ())
+ {
+ ding ();
+ return;
+ }
+
+ rl_begin_undo_group ();
+ vi_doing_insert = 1;
+ rl_kill_text (rl_point, rl_mark);
+ rl_vi_insertion_mode ();
+}
+
+rl_vi_yank_to (count, key)
+ int count, key;
+{
+ int save = rl_point;
+
+ if (uppercase_p (key))
+ rl_stuff_char ('$');
+
+ if (rl_vi_domove ())
+ {
+ ding ();
+ return;
+ }
+
+ rl_begin_undo_group ();
+ rl_kill_text (rl_point, rl_mark);
+ rl_end_undo_group ();
+ rl_do_undo ();
+ rl_point = save;
+}
+
+rl_vi_delete (count)
+{
+ if (rl_point >= rl_end - 1)
+ {
+ rl_delete (count);
+ if (rl_point > 0)
+ rl_backward (1);
+ }
+ else
+ rl_delete (count);
+}
+
+/* Turn the current line into a comment in shell history. A ksh function */
+rl_vi_comment ()
+{
+ rl_beg_of_line ();
+ rl_insert_text (": "); /* # doesn't work in interactive mode */
+ rl_redisplay ();
+ rl_newline (1, '\010');
+}
+
+rl_vi_first_print ()
+{
+ rl_back_to_indent ();
+}
+
+rl_back_to_indent (ignore1, ignore2)
+ int ignore1, ignore2;
+{
+ rl_beg_of_line ();
+ while (rl_point < rl_end && whitespace (the_line[rl_point]))
+ rl_point++;
+}
+
+/* NOTE: it is necessary that opposite directions are inverses */
+#define FTO 1 /* forward to */
+#define BTO -1 /* backward to */
+#define FFIND 2 /* forward find */
+#define BFIND -2 /* backward find */
+
+rl_vi_char_search (count, key)
+ int count, key;
+{
+ static char target;
+ static int orig_dir, dir;
+ int pos;
+
+ if (key == ';' || key == ',')
+ dir = (key == ';' ? orig_dir : -orig_dir);
+ else
+ {
+ target = rl_read_key();
+
+ switch (key)
+ {
+ case 't':
+ orig_dir = dir = FTO;
+ break;
+
+ case 'T':
+ orig_dir = dir = BTO;
+ break;
+
+ case 'f':
+ orig_dir = dir = FFIND;
+ break;
+
+ case 'F':
+ orig_dir = dir = BFIND;
+ break;
+ }
+ }
+
+ pos = rl_point;
+
+ if (dir < 0)
+ {
+ pos--;
+ do
+ {
+ if (the_line[pos] == target)
+ {
+ if (dir == BTO)
+ rl_point = pos + 1;
+ else
+ rl_point = pos;
+ return;
+ }
+ }
+ while (pos--);
+
+ if (pos < 0)
+ {
+ ding ();
+ return;
+ }
+ }
+ else
+ { /* dir > 0 */
+ pos++;
+ do
+ {
+ if (the_line[pos] == target)
+ {
+ if (dir == FTO)
+ rl_point = pos - 1;
+ else
+ rl_point = pos;
+ return;
+ }
+ }
+ while (++pos < rl_end);
+
+ if (pos >= (rl_end - 1))
+ ding ();
+ }
+}
+
+/* Match brackets */
+rl_vi_match ()
+{
+ int count = 1, brack, pos;
+
+ pos = rl_point;
+ if ((brack = rl_vi_bracktype (the_line[rl_point])) == 0)
+ {
+ while ((brack = rl_vi_bracktype (the_line[rl_point])) == 0 &&
+ rl_point < rl_end - 1)
+ rl_forward (1);
+
+ if (brack <= 0)
+ {
+ rl_point = pos;
+ ding ();
+ return;
+ }
+ }
+
+ pos = rl_point;
+
+ if (brack < 0)
+ {
+ while (count)
+ {
+ if (--pos >= 0)
+ {
+ int b = rl_vi_bracktype (the_line[pos]);
+ if (b == -brack)
+ count--;
+ else if (b == brack)
+ count++;
+ }
+ else
+ {
+ ding ();
+ return;
+ }
+ }
+ }
+ else
+ { /* brack > 0 */
+ while (count)
+ {
+ if (++pos < rl_end)
+ {
+ int b = rl_vi_bracktype (the_line[pos]);
+ if (b == -brack)
+ count--;
+ else if (b == brack)
+ count++;
+ }
+ else
+ {
+ ding ();
+ return;
+ }
+ }
+ }
+ rl_point = pos;
+}
+
+int
+rl_vi_bracktype (c)
+ int c;
+{
+ switch (c)
+ {
+ case '(': return 1;
+ case ')': return -1;
+ case '[': return 2;
+ case ']': return -2;
+ case '{': return 3;
+ case '}': return -3;
+ default: return 0;
+ }
+}
+
+rl_vi_change_char ()
+{
+ int c;
+
+ c = rl_read_key();
+
+ switch (c)
+ {
+ case '\033':
+ case CTRL('C'):
+ return;
+
+ default:
+ rl_begin_undo_group ();
+ rl_delete (1);
+ rl_insert (1, c);
+ rl_end_undo_group ();
+ break;
+ }
+}
+
+rl_vi_subst (count, key)
+ int count, key;
+{
+ rl_begin_undo_group ();
+ vi_doing_insert = 1;
+
+ if (uppercase_p (key))
+ {
+ rl_beg_of_line ();
+ rl_kill_line (1);
+ }
+ else
+ rl_delete (1);
+
+ rl_vi_insertion_mode ();
+}
+
+rl_vi_overstrike (count, key)
+ int count, key;
+{
+ int i;
+
+ if (vi_doing_insert == 0)
+ {
+ vi_doing_insert = 1;
+ rl_begin_undo_group ();
+ }
+
+ for (i = 0; i < count; i++)
+ {
+ vi_replace_count++;
+ rl_begin_undo_group ();
+
+ if (rl_point < rl_end)
+ {
+ rl_delete (1);
+ rl_insert (1, key);
+ }
+ else
+ rl_insert (1, key);
+
+ rl_end_undo_group ();
+ }
+}
+
+rl_vi_overstrike_delete (count)
+ int count;
+{
+ int i, s;
+
+ for (i = 0; i < count; i++)
+ {
+ if (vi_replace_count == 0)
+ {
+ ding ();
+ break;
+ }
+ s = rl_point;
+
+ if (rl_do_undo ())
+ vi_replace_count--;
+
+ if (rl_point == s)
+ rl_backward (1);
+ }
+
+ if (vi_replace_count == 0 && vi_doing_insert)
+ {
+ rl_end_undo_group ();
+ rl_do_undo ();
+ vi_doing_insert = 0;
+ }
+}
+
+rl_vi_replace ()
+{
+ int i;
+
+ vi_replace_count = 0;
+
+ vi_replace_map = rl_make_bare_keymap ();
+
+ for (i = ' '; i < 127; i++)
+ vi_replace_map[i].function = rl_vi_overstrike;
+
+ vi_replace_map[RUBOUT].function = rl_vi_overstrike_delete;
+ vi_replace_map[CTRL('H')].function = rl_vi_overstrike_delete;
+ vi_replace_map[ESC].function = rl_vi_movement_mode;
+ vi_replace_map[RETURN].function = rl_newline;
+ vi_replace_map[NEWLINE].function = rl_newline;
+ keymap = vi_replace_map;
+}
diff --git a/gnu/usr.bin/gdb/regex.c b/gnu/usr.bin/gdb/regex.c
new file mode 100644
index 0000000..45c3478
--- /dev/null
+++ b/gnu/usr.bin/gdb/regex.c
@@ -0,0 +1,1738 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 1985, 1989 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+ In other words, you are welcome to use, share and improve this program.
+ You are forbidden to forbid anyone else to use, share and improve
+ what you give them. Help stamp out software-hoarding! */
+
+
+/* To test, compile with -Dtest.
+ This Dtestable feature turns this into a self-contained program
+ which reads a pattern, describes how it compiles,
+ then reads a string and searches for it. */
+
+#ifdef emacs
+
+/* The `emacs' switch turns on certain special matching commands
+ that make sense only in emacs. */
+
+#include "config.h"
+#include "lisp.h"
+#include "buffer.h"
+#include "syntax.h"
+
+#else /* not emacs */
+
+#ifdef USG
+#ifndef BSTRING
+#define bcopy(s,d,n) memcpy((d),(s),(n))
+#define bcmp(s1,s2,n) memcmp((s1),(s2),(n))
+#define bzero(s,n) memset((s),0,(n))
+#endif
+#endif
+
+/* Make alloca work the best possible way. */
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else
+#ifdef sparc
+#include <alloca.h>
+#endif
+#endif
+
+/*
+ * Define the syntax stuff, so we can do the \<...\> things.
+ */
+
+#ifndef Sword /* must be non-zero in some of the tests below... */
+#define Sword 1
+#endif
+
+#define SYNTAX(c) re_syntax_table[c]
+
+#ifdef SYNTAX_TABLE
+
+char *re_syntax_table;
+
+#else
+
+static char re_syntax_table[256];
+
+static void
+init_syntax_once ()
+{
+ register int c;
+ static int done = 0;
+
+ if (done)
+ return;
+
+ bzero (re_syntax_table, sizeof re_syntax_table);
+
+ for (c = 'a'; c <= 'z'; c++)
+ re_syntax_table[c] = Sword;
+
+ for (c = 'A'; c <= 'Z'; c++)
+ re_syntax_table[c] = Sword;
+
+ for (c = '0'; c <= '9'; c++)
+ re_syntax_table[c] = Sword;
+
+ done = 1;
+}
+
+#endif /* SYNTAX_TABLE */
+#endif /* not emacs */
+
+#include "regex.h"
+
+/* Number of failure points to allocate space for initially,
+ when matching. If this number is exceeded, more space is allocated,
+ so it is not a hard limit. */
+
+#ifndef NFAILURES
+#define NFAILURES 80
+#endif /* NFAILURES */
+
+/* width of a byte in bits */
+
+#define BYTEWIDTH 8
+
+#ifndef SIGN_EXTEND_CHAR
+#define SIGN_EXTEND_CHAR(x) (x)
+#endif
+
+static int obscure_syntax = 0;
+
+/* Specify the precise syntax of regexp for compilation.
+ This provides for compatibility for various utilities
+ which historically have different, incompatible syntaxes.
+
+ The argument SYNTAX is a bit-mask containing the two bits
+ RE_NO_BK_PARENS and RE_NO_BK_VBAR. */
+
+int
+re_set_syntax (syntax)
+{
+ int ret;
+
+ ret = obscure_syntax;
+ obscure_syntax = syntax;
+ return ret;
+}
+
+/* re_compile_pattern takes a regular-expression string
+ and converts it into a buffer full of byte commands for matching.
+
+ PATTERN is the address of the pattern string
+ SIZE is the length of it.
+ BUFP is a struct re_pattern_buffer * which points to the info
+ on where to store the byte commands.
+ This structure contains a char * which points to the
+ actual space, which should have been obtained with malloc.
+ re_compile_pattern may use realloc to grow the buffer space.
+
+ The number of bytes of commands can be found out by looking in
+ the struct re_pattern_buffer that bufp pointed to,
+ after re_compile_pattern returns.
+*/
+
+#define PATPUSH(ch) (*b++ = (char) (ch))
+
+#define PATFETCH(c) \
+ {if (p == pend) goto end_of_pattern; \
+ c = * (unsigned char *) p++; \
+ if (translate) c = translate[c]; }
+
+#define PATFETCH_RAW(c) \
+ {if (p == pend) goto end_of_pattern; \
+ c = * (unsigned char *) p++; }
+
+#define PATUNFETCH p--
+
+#define EXTEND_BUFFER \
+ { char *old_buffer = bufp->buffer; \
+ if (bufp->allocated == (1<<16)) goto too_big; \
+ bufp->allocated *= 2; \
+ if (bufp->allocated > (1<<16)) bufp->allocated = (1<<16); \
+ if (!(bufp->buffer = (char *) realloc (bufp->buffer, bufp->allocated))) \
+ goto memory_exhausted; \
+ c = bufp->buffer - old_buffer; \
+ b += c; \
+ if (fixup_jump) \
+ fixup_jump += c; \
+ if (laststart) \
+ laststart += c; \
+ begalt += c; \
+ if (pending_exact) \
+ pending_exact += c; \
+ }
+
+static int store_jump (), insert_jump ();
+
+char *
+re_compile_pattern (pattern, size, bufp)
+ char *pattern;
+ int size;
+ struct re_pattern_buffer *bufp;
+{
+ register char *b = bufp->buffer;
+ register char *p = pattern;
+ char *pend = pattern + size;
+ register unsigned c, c1;
+ char *p1;
+ unsigned char *translate = (unsigned char *) bufp->translate;
+
+ /* address of the count-byte of the most recently inserted "exactn" command.
+ This makes it possible to tell whether a new exact-match character
+ can be added to that command or requires a new "exactn" command. */
+
+ char *pending_exact = 0;
+
+ /* address of the place where a forward-jump should go
+ to the end of the containing expression.
+ Each alternative of an "or", except the last, ends with a forward-jump
+ of this sort. */
+
+ char *fixup_jump = 0;
+
+ /* address of start of the most recently finished expression.
+ This tells postfix * where to find the start of its operand. */
+
+ char *laststart = 0;
+
+ /* In processing a repeat, 1 means zero matches is allowed */
+
+ char zero_times_ok;
+
+ /* In processing a repeat, 1 means many matches is allowed */
+
+ char many_times_ok;
+
+ /* address of beginning of regexp, or inside of last \( */
+
+ char *begalt = b;
+
+ /* Stack of information saved by \( and restored by \).
+ Four stack elements are pushed by each \(:
+ First, the value of b.
+ Second, the value of fixup_jump.
+ Third, the value of regnum.
+ Fourth, the value of begalt. */
+
+ int stackb[40];
+ int *stackp = stackb;
+ int *stacke = stackb + 40;
+ int *stackt;
+
+ /* Counts \('s as they are encountered. Remembered for the matching \),
+ where it becomes the "register number" to put in the stop_memory command */
+
+ int regnum = 1;
+
+ bufp->fastmap_accurate = 0;
+
+#ifndef emacs
+#ifndef SYNTAX_TABLE
+ /*
+ * Initialize the syntax table.
+ */
+ init_syntax_once();
+#endif
+#endif
+
+ if (bufp->allocated == 0)
+ {
+ bufp->allocated = 28;
+ if (bufp->buffer)
+ /* EXTEND_BUFFER loses when bufp->allocated is 0 */
+ bufp->buffer = (char *) realloc (bufp->buffer, 28);
+ else
+ /* Caller did not allocate a buffer. Do it for him */
+ bufp->buffer = (char *) malloc (28);
+ if (!bufp->buffer) goto memory_exhausted;
+ begalt = b = bufp->buffer;
+ }
+
+ while (p != pend)
+ {
+ if (b - bufp->buffer > bufp->allocated - 10)
+ /* Note that EXTEND_BUFFER clobbers c */
+ EXTEND_BUFFER;
+
+ PATFETCH (c);
+
+ switch (c)
+ {
+ case '$':
+ if (obscure_syntax & RE_TIGHT_VBAR)
+ {
+ if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS) && p != pend)
+ goto normal_char;
+ /* Make operand of last vbar end before this `$'. */
+ if (fixup_jump)
+ store_jump (fixup_jump, jump, b);
+ fixup_jump = 0;
+ PATPUSH (endline);
+ break;
+ }
+
+ /* $ means succeed if at end of line, but only in special contexts.
+ If randomly in the middle of a pattern, it is a normal character. */
+ if (p == pend || *p == '\n'
+ || (obscure_syntax & RE_CONTEXT_INDEP_OPS)
+ || (obscure_syntax & RE_NO_BK_PARENS
+ ? *p == ')'
+ : *p == '\\' && p[1] == ')')
+ || (obscure_syntax & RE_NO_BK_VBAR
+ ? *p == '|'
+ : *p == '\\' && p[1] == '|'))
+ {
+ PATPUSH (endline);
+ break;
+ }
+ goto normal_char;
+
+ case '^':
+ /* ^ means succeed if at beg of line, but only if no preceding pattern. */
+
+ if (laststart && p[-2] != '\n'
+ && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS))
+ goto normal_char;
+ if (obscure_syntax & RE_TIGHT_VBAR)
+ {
+ if (p != pattern + 1
+ && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS))
+ goto normal_char;
+ PATPUSH (begline);
+ begalt = b;
+ }
+ else
+ PATPUSH (begline);
+ break;
+
+ case '+':
+ case '?':
+ if (obscure_syntax & RE_BK_PLUS_QM)
+ goto normal_char;
+ handle_plus:
+ case '*':
+ /* If there is no previous pattern, char not special. */
+ if (!laststart && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS))
+ goto normal_char;
+ /* If there is a sequence of repetition chars,
+ collapse it down to equivalent to just one. */
+ zero_times_ok = 0;
+ many_times_ok = 0;
+ while (1)
+ {
+ zero_times_ok |= c != '+';
+ many_times_ok |= c != '?';
+ if (p == pend)
+ break;
+ PATFETCH (c);
+ if (c == '*')
+ ;
+ else if (!(obscure_syntax & RE_BK_PLUS_QM)
+ && (c == '+' || c == '?'))
+ ;
+ else if ((obscure_syntax & RE_BK_PLUS_QM)
+ && c == '\\')
+ {
+ int c1;
+ PATFETCH (c1);
+ if (!(c1 == '+' || c1 == '?'))
+ {
+ PATUNFETCH;
+ PATUNFETCH;
+ break;
+ }
+ c = c1;
+ }
+ else
+ {
+ PATUNFETCH;
+ break;
+ }
+ }
+
+ /* Star, etc. applied to an empty pattern is equivalent
+ to an empty pattern. */
+ if (!laststart)
+ break;
+
+ /* Now we know whether 0 matches is allowed,
+ and whether 2 or more matches is allowed. */
+ if (many_times_ok)
+ {
+ /* If more than one repetition is allowed,
+ put in a backward jump at the end. */
+ store_jump (b, maybe_finalize_jump, laststart - 3);
+ b += 3;
+ }
+ insert_jump (on_failure_jump, laststart, b + 3, b);
+ pending_exact = 0;
+ b += 3;
+ if (!zero_times_ok)
+ {
+ /* At least one repetition required: insert before the loop
+ a skip over the initial on-failure-jump instruction */
+ insert_jump (dummy_failure_jump, laststart, laststart + 6, b);
+ b += 3;
+ }
+ break;
+
+ case '.':
+ laststart = b;
+ PATPUSH (anychar);
+ break;
+
+ case '[':
+ while (b - bufp->buffer
+ > bufp->allocated - 3 - (1 << BYTEWIDTH) / BYTEWIDTH)
+ /* Note that EXTEND_BUFFER clobbers c */
+ EXTEND_BUFFER;
+
+ laststart = b;
+ if (*p == '^')
+ PATPUSH (charset_not), p++;
+ else
+ PATPUSH (charset);
+ p1 = p;
+
+ PATPUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
+ /* Clear the whole map */
+ bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH);
+ /* Read in characters and ranges, setting map bits */
+ while (1)
+ {
+ PATFETCH (c);
+ if (c == ']' && p != p1 + 1) break;
+ if (*p == '-' && p[1] != ']')
+ {
+ PATFETCH (c1);
+ PATFETCH (c1);
+ while (c <= c1)
+ b[c / BYTEWIDTH] |= 1 << (c % BYTEWIDTH), c++;
+ }
+ else
+ {
+ b[c / BYTEWIDTH] |= 1 << (c % BYTEWIDTH);
+ }
+ }
+ /* Discard any bitmap bytes that are all 0 at the end of the map.
+ Decrement the map-length byte too. */
+ while ((int) b[-1] > 0 && b[b[-1] - 1] == 0)
+ b[-1]--;
+ b += b[-1];
+ break;
+
+ case '(':
+ if (! (obscure_syntax & RE_NO_BK_PARENS))
+ goto normal_char;
+ else
+ goto handle_open;
+
+ case ')':
+ if (! (obscure_syntax & RE_NO_BK_PARENS))
+ goto normal_char;
+ else
+ goto handle_close;
+
+ case '\n':
+ if (! (obscure_syntax & RE_NEWLINE_OR))
+ goto normal_char;
+ else
+ goto handle_bar;
+
+ case '|':
+ if (! (obscure_syntax & RE_NO_BK_VBAR))
+ goto normal_char;
+ else
+ goto handle_bar;
+
+ case '\\':
+ if (p == pend) goto invalid_pattern;
+ PATFETCH_RAW (c);
+ switch (c)
+ {
+ case '(':
+ if (obscure_syntax & RE_NO_BK_PARENS)
+ goto normal_backsl;
+ handle_open:
+ if (stackp == stacke) goto nesting_too_deep;
+ if (regnum < RE_NREGS)
+ {
+ PATPUSH (start_memory);
+ PATPUSH (regnum);
+ }
+ *stackp++ = b - bufp->buffer;
+ *stackp++ = fixup_jump ? fixup_jump - bufp->buffer + 1 : 0;
+ *stackp++ = regnum++;
+ *stackp++ = begalt - bufp->buffer;
+ fixup_jump = 0;
+ laststart = 0;
+ begalt = b;
+ break;
+
+ case ')':
+ if (obscure_syntax & RE_NO_BK_PARENS)
+ goto normal_backsl;
+ handle_close:
+ if (stackp == stackb) goto unmatched_close;
+ begalt = *--stackp + bufp->buffer;
+ if (fixup_jump)
+ store_jump (fixup_jump, jump, b);
+ if (stackp[-1] < RE_NREGS)
+ {
+ PATPUSH (stop_memory);
+ PATPUSH (stackp[-1]);
+ }
+ stackp -= 2;
+ fixup_jump = 0;
+ if (*stackp)
+ fixup_jump = *stackp + bufp->buffer - 1;
+ laststart = *--stackp + bufp->buffer;
+ break;
+
+ case '|':
+ if (obscure_syntax & RE_NO_BK_VBAR)
+ goto normal_backsl;
+ handle_bar:
+ insert_jump (on_failure_jump, begalt, b + 6, b);
+ pending_exact = 0;
+ b += 3;
+ if (fixup_jump)
+ store_jump (fixup_jump, jump, b);
+ fixup_jump = b;
+ b += 3;
+ laststart = 0;
+ begalt = b;
+ break;
+
+#ifdef emacs
+ case '=':
+ PATPUSH (at_dot);
+ break;
+
+ case 's':
+ laststart = b;
+ PATPUSH (syntaxspec);
+ PATFETCH (c);
+ PATPUSH (syntax_spec_code[c]);
+ break;
+
+ case 'S':
+ laststart = b;
+ PATPUSH (notsyntaxspec);
+ PATFETCH (c);
+ PATPUSH (syntax_spec_code[c]);
+ break;
+#endif /* emacs */
+
+ case 'w':
+ laststart = b;
+ PATPUSH (wordchar);
+ break;
+
+ case 'W':
+ laststart = b;
+ PATPUSH (notwordchar);
+ break;
+
+ case '<':
+ PATPUSH (wordbeg);
+ break;
+
+ case '>':
+ PATPUSH (wordend);
+ break;
+
+ case 'b':
+ PATPUSH (wordbound);
+ break;
+
+ case 'B':
+ PATPUSH (notwordbound);
+ break;
+
+ case '`':
+ PATPUSH (begbuf);
+ break;
+
+ case '\'':
+ PATPUSH (endbuf);
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ c1 = c - '0';
+ if (c1 >= regnum)
+ goto normal_char;
+ for (stackt = stackp - 2; stackt > stackb; stackt -= 4)
+ if (*stackt == c1)
+ goto normal_char;
+ laststart = b;
+ PATPUSH (duplicate);
+ PATPUSH (c1);
+ break;
+
+ case '+':
+ case '?':
+ if (obscure_syntax & RE_BK_PLUS_QM)
+ goto handle_plus;
+
+ default:
+ normal_backsl:
+ /* You might think it would be useful for \ to mean
+ not to translate; but if we don't translate it
+ it will never match anything. */
+ if (translate) c = translate[c];
+ goto normal_char;
+ }
+ break;
+
+ default:
+ normal_char:
+ if (!pending_exact || pending_exact + *pending_exact + 1 != b
+ || *pending_exact == 0177 || *p == '*' || *p == '^'
+ || ((obscure_syntax & RE_BK_PLUS_QM)
+ ? *p == '\\' && (p[1] == '+' || p[1] == '?')
+ : (*p == '+' || *p == '?')))
+ {
+ laststart = b;
+ PATPUSH (exactn);
+ pending_exact = b;
+ PATPUSH (0);
+ }
+ PATPUSH (c);
+ (*pending_exact)++;
+ }
+ }
+
+ if (fixup_jump)
+ store_jump (fixup_jump, jump, b);
+
+ if (stackp != stackb) goto unmatched_open;
+
+ bufp->used = b - bufp->buffer;
+ return 0;
+
+ invalid_pattern:
+ return "Invalid regular expression";
+
+ unmatched_open:
+ return "Unmatched \\(";
+
+ unmatched_close:
+ return "Unmatched \\)";
+
+ end_of_pattern:
+ return "Premature end of regular expression";
+
+ nesting_too_deep:
+ return "Nesting too deep";
+
+ too_big:
+ return "Regular expression too big";
+
+ memory_exhausted:
+ return "Memory exhausted";
+}
+
+/* Store where `from' points a jump operation to jump to where `to' points.
+ `opcode' is the opcode to store. */
+
+static int
+store_jump (from, opcode, to)
+ char *from, *to;
+ char opcode;
+{
+ from[0] = opcode;
+ from[1] = (to - (from + 3)) & 0377;
+ from[2] = (to - (from + 3)) >> 8;
+}
+
+/* Open up space at char FROM, and insert there a jump to TO.
+ CURRENT_END gives te end of the storage no in use,
+ so we know how much data to copy up.
+ OP is the opcode of the jump to insert.
+
+ If you call this function, you must zero out pending_exact. */
+
+static int
+insert_jump (op, from, to, current_end)
+ char op;
+ char *from, *to, *current_end;
+{
+ register char *pto = current_end + 3;
+ register char *pfrom = current_end;
+ while (pfrom != from)
+ *--pto = *--pfrom;
+ store_jump (from, op, to);
+}
+
+/* Given a pattern, compute a fastmap from it.
+ The fastmap records which of the (1 << BYTEWIDTH) possible characters
+ can start a string that matches the pattern.
+ This fastmap is used by re_search to skip quickly over totally implausible text.
+
+ The caller must supply the address of a (1 << BYTEWIDTH)-byte data area
+ as bufp->fastmap.
+ The other components of bufp describe the pattern to be used. */
+
+void
+re_compile_fastmap (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ unsigned char *pattern = (unsigned char *) bufp->buffer;
+ int size = bufp->used;
+ register char *fastmap = bufp->fastmap;
+ register unsigned char *p = pattern;
+ register unsigned char *pend = pattern + size;
+ register int j, k;
+ unsigned char *translate = (unsigned char *) bufp->translate;
+
+ unsigned char *stackb[NFAILURES];
+ unsigned char **stackp = stackb;
+
+ bzero (fastmap, (1 << BYTEWIDTH));
+ bufp->fastmap_accurate = 1;
+ bufp->can_be_null = 0;
+
+ while (p)
+ {
+ if (p == pend)
+ {
+ bufp->can_be_null = 1;
+ break;
+ }
+#ifdef SWITCH_ENUM_BUG
+ switch ((int) ((enum regexpcode) *p++))
+#else
+ switch ((enum regexpcode) *p++)
+#endif
+ {
+ case exactn:
+ if (translate)
+ fastmap[translate[p[1]]] = 1;
+ else
+ fastmap[p[1]] = 1;
+ break;
+
+ case begline:
+ case before_dot:
+ case at_dot:
+ case after_dot:
+ case begbuf:
+ case endbuf:
+ case wordbound:
+ case notwordbound:
+ case wordbeg:
+ case wordend:
+ continue;
+
+ case endline:
+ if (translate)
+ fastmap[translate['\n']] = 1;
+ else
+ fastmap['\n'] = 1;
+ if (bufp->can_be_null != 1)
+ bufp->can_be_null = 2;
+ break;
+
+ case finalize_jump:
+ case maybe_finalize_jump:
+ case jump:
+ case dummy_failure_jump:
+ bufp->can_be_null = 1;
+ j = *p++ & 0377;
+ j += SIGN_EXTEND_CHAR (*(char *)p) << 8;
+ p += j + 1; /* The 1 compensates for missing ++ above */
+ if (j > 0)
+ continue;
+ /* Jump backward reached implies we just went through
+ the body of a loop and matched nothing.
+ Opcode jumped to should be an on_failure_jump.
+ Just treat it like an ordinary jump.
+ For a * loop, it has pushed its failure point already;
+ if so, discard that as redundant. */
+ if ((enum regexpcode) *p != on_failure_jump)
+ continue;
+ p++;
+ j = *p++ & 0377;
+ j += SIGN_EXTEND_CHAR (*(char *)p) << 8;
+ p += j + 1; /* The 1 compensates for missing ++ above */
+ if (stackp != stackb && *stackp == p)
+ stackp--;
+ continue;
+
+ case on_failure_jump:
+ j = *p++ & 0377;
+ j += SIGN_EXTEND_CHAR (*(char *)p) << 8;
+ p++;
+ *++stackp = p + j;
+ continue;
+
+ case start_memory:
+ case stop_memory:
+ p++;
+ continue;
+
+ case duplicate:
+ bufp->can_be_null = 1;
+ fastmap['\n'] = 1;
+ case anychar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (j != '\n')
+ fastmap[j] = 1;
+ if (bufp->can_be_null)
+ return;
+ /* Don't return; check the alternative paths
+ so we can set can_be_null if appropriate. */
+ break;
+
+ case wordchar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) == Sword)
+ fastmap[j] = 1;
+ break;
+
+ case notwordchar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) != Sword)
+ fastmap[j] = 1;
+ break;
+
+#ifdef emacs
+ case syntaxspec:
+ k = *p++;
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) == (enum syntaxcode) k)
+ fastmap[j] = 1;
+ break;
+
+ case notsyntaxspec:
+ k = *p++;
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) != (enum syntaxcode) k)
+ fastmap[j] = 1;
+ break;
+#endif /* emacs */
+
+ case charset:
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
+ {
+ if (translate)
+ fastmap[translate[j]] = 1;
+ else
+ fastmap[j] = 1;
+ }
+ break;
+
+ case charset_not:
+ /* Chars beyond end of map must be allowed */
+ for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
+ if (translate)
+ fastmap[translate[j]] = 1;
+ else
+ fastmap[j] = 1;
+
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
+ {
+ if (translate)
+ fastmap[translate[j]] = 1;
+ else
+ fastmap[j] = 1;
+ }
+ break;
+ }
+
+ /* Get here means we have successfully found the possible starting characters
+ of one path of the pattern. We need not follow this path any farther.
+ Instead, look at the next alternative remembered in the stack. */
+ if (stackp != stackb)
+ p = *stackp--;
+ else
+ break;
+ }
+}
+
+/* Like re_search_2, below, but only one string is specified. */
+
+int
+re_search (pbufp, string, size, startpos, range, regs)
+ struct re_pattern_buffer *pbufp;
+ char *string;
+ int size, startpos, range;
+ struct re_registers *regs;
+{
+ return re_search_2 (pbufp, 0, 0, string, size, startpos, range, regs, size);
+}
+
+/* Like re_match_2 but tries first a match starting at index STARTPOS,
+ then at STARTPOS + 1, and so on.
+ RANGE is the number of places to try before giving up.
+ If RANGE is negative, the starting positions tried are
+ STARTPOS, STARTPOS - 1, etc.
+ It is up to the caller to make sure that range is not so large
+ as to take the starting position outside of the input strings.
+
+The value returned is the position at which the match was found,
+ or -1 if no match was found,
+ or -2 if error (such as failure stack overflow). */
+
+int
+re_search_2 (pbufp, string1, size1, string2, size2, startpos, range, regs, mstop)
+ struct re_pattern_buffer *pbufp;
+ char *string1, *string2;
+ int size1, size2;
+ int startpos;
+ register int range;
+ struct re_registers *regs;
+ int mstop;
+{
+ register char *fastmap = pbufp->fastmap;
+ register unsigned char *translate = (unsigned char *) pbufp->translate;
+ int total = size1 + size2;
+ int val;
+
+ /* Update the fastmap now if not correct already */
+ if (fastmap && !pbufp->fastmap_accurate)
+ re_compile_fastmap (pbufp);
+
+ /* Don't waste time in a long search for a pattern
+ that says it is anchored. */
+ if (pbufp->used > 0 && (enum regexpcode) pbufp->buffer[0] == begbuf
+ && range > 0)
+ {
+ if (startpos > 0)
+ return -1;
+ else
+ range = 1;
+ }
+
+ while (1)
+ {
+ /* If a fastmap is supplied, skip quickly over characters
+ that cannot possibly be the start of a match.
+ Note, however, that if the pattern can possibly match
+ the null string, we must test it at each starting point
+ so that we take the first null string we get. */
+
+ if (fastmap && startpos < total && pbufp->can_be_null != 1)
+ {
+ if (range > 0)
+ {
+ register int lim = 0;
+ register unsigned char *p;
+ int irange = range;
+ if (startpos < size1 && startpos + range >= size1)
+ lim = range - (size1 - startpos);
+
+ p = ((unsigned char *)
+ &(startpos >= size1 ? string2 - size1 : string1)[startpos]);
+
+ if (translate)
+ {
+ while (range > lim && !fastmap[translate[*p++]])
+ range--;
+ }
+ else
+ {
+ while (range > lim && !fastmap[*p++])
+ range--;
+ }
+ startpos += irange - range;
+ }
+ else
+ {
+ register unsigned char c;
+ if (startpos >= size1)
+ c = string2[startpos - size1];
+ else
+ c = string1[startpos];
+ c &= 0xff;
+ if (translate ? !fastmap[translate[c]] : !fastmap[c])
+ goto advance;
+ }
+ }
+
+ if (range >= 0 && startpos == total
+ && fastmap && pbufp->can_be_null == 0)
+ return -1;
+
+ val = re_match_2 (pbufp, string1, size1, string2, size2, startpos, regs, mstop);
+ if (0 <= val)
+ {
+ if (val == -2)
+ return -2;
+ return startpos;
+ }
+
+#ifdef C_ALLOCA
+ alloca (0);
+#endif /* C_ALLOCA */
+
+ advance:
+ if (!range) break;
+ if (range > 0) range--, startpos++; else range++, startpos--;
+ }
+ return -1;
+}
+
+#ifndef emacs /* emacs never uses this */
+int
+re_match (pbufp, string, size, pos, regs)
+ struct re_pattern_buffer *pbufp;
+ char *string;
+ int size, pos;
+ struct re_registers *regs;
+{
+ return re_match_2 (pbufp, 0, 0, string, size, pos, regs, size);
+}
+#endif /* emacs */
+
+/* Maximum size of failure stack. Beyond this, overflow is an error. */
+
+int re_max_failures = 2000;
+
+static int bcmp_translate();
+/* Match the pattern described by PBUFP
+ against data which is the virtual concatenation of STRING1 and STRING2.
+ SIZE1 and SIZE2 are the sizes of the two data strings.
+ Start the match at position POS.
+ Do not consider matching past the position MSTOP.
+
+ If pbufp->fastmap is nonzero, then it had better be up to date.
+
+ The reason that the data to match are specified as two components
+ which are to be regarded as concatenated
+ is so this function can be used directly on the contents of an Emacs buffer.
+
+ -1 is returned if there is no match. -2 is returned if there is
+ an error (such as match stack overflow). Otherwise the value is the length
+ of the substring which was matched. */
+
+int
+re_match_2 (pbufp, string1, size1, string2, size2, pos, regs, mstop)
+ struct re_pattern_buffer *pbufp;
+ unsigned char *string1, *string2;
+ int size1, size2;
+ int pos;
+ struct re_registers *regs;
+ int mstop;
+{
+ register unsigned char *p = (unsigned char *) pbufp->buffer;
+ register unsigned char *pend = p + pbufp->used;
+ /* End of first string */
+ unsigned char *end1;
+ /* End of second string */
+ unsigned char *end2;
+ /* Pointer just past last char to consider matching */
+ unsigned char *end_match_1, *end_match_2;
+ register unsigned char *d, *dend;
+ register int mcnt;
+ unsigned char *translate = (unsigned char *) pbufp->translate;
+
+ /* Failure point stack. Each place that can handle a failure further down the line
+ pushes a failure point on this stack. It consists of two char *'s.
+ The first one pushed is where to resume scanning the pattern;
+ the second pushed is where to resume scanning the strings.
+ If the latter is zero, the failure point is a "dummy".
+ If a failure happens and the innermost failure point is dormant,
+ it discards that failure point and tries the next one. */
+
+ unsigned char *initial_stack[2 * NFAILURES];
+ unsigned char **stackb = initial_stack;
+ unsigned char **stackp = stackb, **stacke = &stackb[2 * NFAILURES];
+
+ /* Information on the "contents" of registers.
+ These are pointers into the input strings; they record
+ just what was matched (on this attempt) by some part of the pattern.
+ The start_memory command stores the start of a register's contents
+ and the stop_memory command stores the end.
+
+ At that point, regstart[regnum] points to the first character in the register,
+ regend[regnum] points to the first character beyond the end of the register,
+ regstart_seg1[regnum] is true iff regstart[regnum] points into string1,
+ and regend_seg1[regnum] is true iff regend[regnum] points into string1. */
+
+ unsigned char *regstart[RE_NREGS];
+ unsigned char *regend[RE_NREGS];
+ unsigned char regstart_seg1[RE_NREGS], regend_seg1[RE_NREGS];
+
+ /* Set up pointers to ends of strings.
+ Don't allow the second string to be empty unless both are empty. */
+ if (!size2)
+ {
+ string2 = string1;
+ size2 = size1;
+ string1 = 0;
+ size1 = 0;
+ }
+ end1 = string1 + size1;
+ end2 = string2 + size2;
+
+ /* Compute where to stop matching, within the two strings */
+ if (mstop <= size1)
+ {
+ end_match_1 = string1 + mstop;
+ end_match_2 = string2;
+ }
+ else
+ {
+ end_match_1 = end1;
+ end_match_2 = string2 + mstop - size1;
+ }
+
+ /* Initialize \) text positions to -1
+ to mark ones that no \( or \) has been seen for. */
+
+ for (mcnt = 0; mcnt < sizeof (regend) / sizeof (*regend); mcnt++)
+ regend[mcnt] = (unsigned char *) -1;
+
+ /* `p' scans through the pattern as `d' scans through the data.
+ `dend' is the end of the input string that `d' points within.
+ `d' is advanced into the following input string whenever necessary,
+ but this happens before fetching;
+ therefore, at the beginning of the loop,
+ `d' can be pointing at the end of a string,
+ but it cannot equal string2. */
+
+ if (pos <= size1)
+ d = string1 + pos, dend = end_match_1;
+ else
+ d = string2 + pos - size1, dend = end_match_2;
+
+/* Write PREFETCH; just before fetching a character with *d. */
+#define PREFETCH \
+ while (d == dend) \
+ { if (dend == end_match_2) goto fail; /* end of string2 => failure */ \
+ d = string2; /* end of string1 => advance to string2. */ \
+ dend = end_match_2; }
+
+ /* This loop loops over pattern commands.
+ It exits by returning from the function if match is complete,
+ or it drops through if match fails at this starting point in the input data. */
+
+ while (1)
+ {
+ if (p == pend)
+ /* End of pattern means we have succeeded! */
+ {
+ /* If caller wants register contents data back, convert it to indices */
+ if (regs)
+ {
+ regs->start[0] = pos;
+ if (dend == end_match_1)
+ regs->end[0] = d - string1;
+ else
+ regs->end[0] = d - string2 + size1;
+ for (mcnt = 1; mcnt < RE_NREGS; mcnt++)
+ {
+ if (regend[mcnt] == (unsigned char *) -1)
+ {
+ regs->start[mcnt] = -1;
+ regs->end[mcnt] = -1;
+ continue;
+ }
+ if (regstart_seg1[mcnt])
+ regs->start[mcnt] = regstart[mcnt] - string1;
+ else
+ regs->start[mcnt] = regstart[mcnt] - string2 + size1;
+ if (regend_seg1[mcnt])
+ regs->end[mcnt] = regend[mcnt] - string1;
+ else
+ regs->end[mcnt] = regend[mcnt] - string2 + size1;
+ }
+ }
+ if (dend == end_match_1)
+ return (d - string1 - pos);
+ else
+ return d - string2 + size1 - pos;
+ }
+
+ /* Otherwise match next pattern command */
+#ifdef SWITCH_ENUM_BUG
+ switch ((int) ((enum regexpcode) *p++))
+#else
+ switch ((enum regexpcode) *p++)
+#endif
+ {
+
+ /* \( is represented by a start_memory, \) by a stop_memory.
+ Both of those commands contain a "register number" argument.
+ The text matched within the \( and \) is recorded under that number.
+ Then, \<digit> turns into a `duplicate' command which
+ is followed by the numeric value of <digit> as the register number. */
+
+ case start_memory:
+ regstart[*p] = d;
+ regstart_seg1[*p++] = (dend == end_match_1);
+ break;
+
+ case stop_memory:
+ regend[*p] = d;
+ regend_seg1[*p++] = (dend == end_match_1);
+ break;
+
+ case duplicate:
+ {
+ int regno = *p++; /* Get which register to match against */
+ register unsigned char *d2, *dend2;
+
+ d2 = regstart[regno];
+ dend2 = ((regstart_seg1[regno] == regend_seg1[regno])
+ ? regend[regno] : end_match_1);
+ while (1)
+ {
+ /* Advance to next segment in register contents, if necessary */
+ while (d2 == dend2)
+ {
+ if (dend2 == end_match_2) break;
+ if (dend2 == regend[regno]) break;
+ d2 = string2, dend2 = regend[regno]; /* end of string1 => advance to string2. */
+ }
+ /* At end of register contents => success */
+ if (d2 == dend2) break;
+
+ /* Advance to next segment in data being matched, if necessary */
+ PREFETCH;
+
+ /* mcnt gets # consecutive chars to compare */
+ mcnt = dend - d;
+ if (mcnt > dend2 - d2)
+ mcnt = dend2 - d2;
+ /* Compare that many; failure if mismatch, else skip them. */
+ if (translate ? bcmp_translate (d, d2, mcnt, translate) : bcmp (d, d2, mcnt))
+ goto fail;
+ d += mcnt, d2 += mcnt;
+ }
+ }
+ break;
+
+ case anychar:
+ /* fetch a data character */
+ PREFETCH;
+ /* Match anything but a newline. */
+ if ((translate ? translate[*d++] : *d++) == '\n')
+ goto fail;
+ break;
+
+ case charset:
+ case charset_not:
+ {
+ /* Nonzero for charset_not */
+ int not = 0;
+ register int c;
+ if (*(p - 1) == (unsigned char) charset_not)
+ not = 1;
+
+ /* fetch a data character */
+ PREFETCH;
+
+ if (translate)
+ c = translate [*d];
+ else
+ c = *d;
+
+ if (c < *p * BYTEWIDTH
+ && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+ not = !not;
+
+ p += 1 + *p;
+
+ if (!not) goto fail;
+ d++;
+ break;
+ }
+
+ case begline:
+ if (d == string1 || d[-1] == '\n')
+ break;
+ goto fail;
+
+ case endline:
+ if (d == end2
+ || (d == end1 ? (size2 == 0 || *string2 == '\n') : *d == '\n'))
+ break;
+ goto fail;
+
+ /* "or" constructs ("|") are handled by starting each alternative
+ with an on_failure_jump that points to the start of the next alternative.
+ Each alternative except the last ends with a jump to the joining point.
+ (Actually, each jump except for the last one really jumps
+ to the following jump, because tensioning the jumps is a hassle.) */
+
+ /* The start of a stupid repeat has an on_failure_jump that points
+ past the end of the repeat text.
+ This makes a failure point so that, on failure to match a repetition,
+ matching restarts past as many repetitions have been found
+ with no way to fail and look for another one. */
+
+ /* A smart repeat is similar but loops back to the on_failure_jump
+ so that each repetition makes another failure point. */
+
+ case on_failure_jump:
+ if (stackp == stacke)
+ {
+ unsigned char **stackx;
+ if (stacke - stackb > re_max_failures * 2)
+ return -2;
+ stackx = (unsigned char **) alloca (2 * (stacke - stackb)
+ * sizeof (char *));
+ bcopy (stackb, stackx, (stacke - stackb) * sizeof (char *));
+ stackp = stackx + (stackp - stackb);
+ stacke = stackx + 2 * (stacke - stackb);
+ stackb = stackx;
+ }
+ mcnt = *p++ & 0377;
+ mcnt += SIGN_EXTEND_CHAR (*(char *)p) << 8;
+ p++;
+ *stackp++ = mcnt + p;
+ *stackp++ = d;
+ break;
+
+ /* The end of a smart repeat has an maybe_finalize_jump back.
+ Change it either to a finalize_jump or an ordinary jump. */
+
+ case maybe_finalize_jump:
+ mcnt = *p++ & 0377;
+ mcnt += SIGN_EXTEND_CHAR (*(char *)p) << 8;
+ p++;
+ {
+ register unsigned char *p2 = p;
+ /* Compare what follows with the begining of the repeat.
+ If we can establish that there is nothing that they would
+ both match, we can change to finalize_jump */
+ while (p2 != pend
+ && (*p2 == (unsigned char) stop_memory
+ || *p2 == (unsigned char) start_memory))
+ p2++;
+ if (p2 == pend)
+ p[-3] = (unsigned char) finalize_jump;
+ else if (*p2 == (unsigned char) exactn
+ || *p2 == (unsigned char) endline)
+ {
+ register int c = *p2 == (unsigned char) endline ? '\n' : p2[2];
+ register unsigned char *p1 = p + mcnt;
+ /* p1[0] ... p1[2] are an on_failure_jump.
+ Examine what follows that */
+ if (p1[3] == (unsigned char) exactn && p1[5] != c)
+ p[-3] = (unsigned char) finalize_jump;
+ else if (p1[3] == (unsigned char) charset
+ || p1[3] == (unsigned char) charset_not)
+ {
+ int not = p1[3] == (unsigned char) charset_not;
+ if (c < p1[4] * BYTEWIDTH
+ && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+ not = !not;
+ /* not is 1 if c would match */
+ /* That means it is not safe to finalize */
+ if (!not)
+ p[-3] = (unsigned char) finalize_jump;
+ }
+ }
+ }
+ p -= 2;
+ if (p[-1] != (unsigned char) finalize_jump)
+ {
+ p[-1] = (unsigned char) jump;
+ goto nofinalize;
+ }
+
+ /* The end of a stupid repeat has a finalize-jump
+ back to the start, where another failure point will be made
+ which will point after all the repetitions found so far. */
+
+ case finalize_jump:
+ stackp -= 2;
+
+ case jump:
+ nofinalize:
+ mcnt = *p++ & 0377;
+ mcnt += SIGN_EXTEND_CHAR (*(char *)p) << 8;
+ p += mcnt + 1; /* The 1 compensates for missing ++ above */
+ break;
+
+ case dummy_failure_jump:
+ if (stackp == stacke)
+ {
+ unsigned char **stackx
+ = (unsigned char **) alloca (2 * (stacke - stackb)
+ * sizeof (char *));
+ bcopy (stackb, stackx, (stacke - stackb) * sizeof (char *));
+ stackp = stackx + (stackp - stackb);
+ stacke = stackx + 2 * (stacke - stackb);
+ stackb = stackx;
+ }
+ *stackp++ = 0;
+ *stackp++ = 0;
+ goto nofinalize;
+
+ case wordbound:
+ if (d == string1 /* Points to first char */
+ || d == end2 /* Points to end */
+ || (d == end1 && size2 == 0)) /* Points to end */
+ break;
+ if ((SYNTAX (d[-1]) == Sword)
+ != (SYNTAX (d == end1 ? *string2 : *d) == Sword))
+ break;
+ goto fail;
+
+ case notwordbound:
+ if (d == string1 /* Points to first char */
+ || d == end2 /* Points to end */
+ || (d == end1 && size2 == 0)) /* Points to end */
+ goto fail;
+ if ((SYNTAX (d[-1]) == Sword)
+ != (SYNTAX (d == end1 ? *string2 : *d) == Sword))
+ goto fail;
+ break;
+
+ case wordbeg:
+ if (d == end2 /* Points to end */
+ || (d == end1 && size2 == 0) /* Points to end */
+ || SYNTAX (* (d == end1 ? string2 : d)) != Sword) /* Next char not a letter */
+ goto fail;
+ if (d == string1 /* Points to first char */
+ || SYNTAX (d[-1]) != Sword) /* prev char not letter */
+ break;
+ goto fail;
+
+ case wordend:
+ if (d == string1 /* Points to first char */
+ || SYNTAX (d[-1]) != Sword) /* prev char not letter */
+ goto fail;
+ if (d == end2 /* Points to end */
+ || (d == end1 && size2 == 0) /* Points to end */
+ || SYNTAX (d == end1 ? *string2 : *d) != Sword) /* Next char not a letter */
+ break;
+ goto fail;
+
+#ifdef emacs
+ case before_dot:
+ if (((d - string2 <= (unsigned) size2)
+ ? d - bf_p2 : d - bf_p1)
+ <= point)
+ goto fail;
+ break;
+
+ case at_dot:
+ if (((d - string2 <= (unsigned) size2)
+ ? d - bf_p2 : d - bf_p1)
+ == point)
+ goto fail;
+ break;
+
+ case after_dot:
+ if (((d - string2 <= (unsigned) size2)
+ ? d - bf_p2 : d - bf_p1)
+ >= point)
+ goto fail;
+ break;
+
+ case wordchar:
+ mcnt = (int) Sword;
+ goto matchsyntax;
+
+ case syntaxspec:
+ mcnt = *p++;
+ matchsyntax:
+ PREFETCH;
+ if (SYNTAX (*d++) != (enum syntaxcode) mcnt) goto fail;
+ break;
+
+ case notwordchar:
+ mcnt = (int) Sword;
+ goto matchnotsyntax;
+
+ case notsyntaxspec:
+ mcnt = *p++;
+ matchnotsyntax:
+ PREFETCH;
+ if (SYNTAX (*d++) == (enum syntaxcode) mcnt) goto fail;
+ break;
+#else
+ case wordchar:
+ PREFETCH;
+ if (SYNTAX (*d++) == 0) goto fail;
+ break;
+
+ case notwordchar:
+ PREFETCH;
+ if (SYNTAX (*d++) != 0) goto fail;
+ break;
+#endif /* not emacs */
+
+ case begbuf:
+ if (d == string1) /* Note, d cannot equal string2 */
+ break; /* unless string1 == string2. */
+ goto fail;
+
+ case endbuf:
+ if (d == end2 || (d == end1 && size2 == 0))
+ break;
+ goto fail;
+
+ case exactn:
+ /* Match the next few pattern characters exactly.
+ mcnt is how many characters to match. */
+ mcnt = *p++;
+ if (translate)
+ {
+ do
+ {
+ PREFETCH;
+ if (translate[*d++] != *p++) goto fail;
+ }
+ while (--mcnt);
+ }
+ else
+ {
+ do
+ {
+ PREFETCH;
+ if (*d++ != *p++) goto fail;
+ }
+ while (--mcnt);
+ }
+ break;
+ }
+ continue; /* Successfully matched one pattern command; keep matching */
+
+ /* Jump here if any matching operation fails. */
+ fail:
+ if (stackp != stackb)
+ /* A restart point is known. Restart there and pop it. */
+ {
+ if (!stackp[-2])
+ { /* If innermost failure point is dormant, flush it and keep looking */
+ stackp -= 2;
+ goto fail;
+ }
+ d = *--stackp;
+ p = *--stackp;
+ if (d >= string1 && d <= end1)
+ dend = end_match_1;
+ }
+ else break; /* Matching at this starting point really fails! */
+ }
+ return -1; /* Failure to match */
+}
+
+static int
+bcmp_translate (s1, s2, len, translate)
+ unsigned char *s1, *s2;
+ register int len;
+ unsigned char *translate;
+{
+ register unsigned char *p1 = s1, *p2 = s2;
+ while (len)
+ {
+ if (translate [*p1++] != translate [*p2++]) return 1;
+ len--;
+ }
+ return 0;
+}
+
+/* Entry points compatible with bsd4.2 regex library */
+
+#ifndef emacs
+
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+re_comp (s)
+ char *s;
+{
+ if (!s)
+ {
+ if (!re_comp_buf.buffer)
+ return "No previous regular expression";
+ return 0;
+ }
+
+ if (!re_comp_buf.buffer)
+ {
+ if (!(re_comp_buf.buffer = (char *) malloc (200)))
+ return "Memory exhausted";
+ re_comp_buf.allocated = 200;
+ if (!(re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH)))
+ return "Memory exhausted";
+ }
+ return re_compile_pattern (s, strlen (s), &re_comp_buf);
+}
+
+int
+re_exec (s)
+ char *s;
+{
+ int len = strlen (s);
+ return 0 <= re_search (&re_comp_buf, s, len, 0, len, 0);
+}
+
+#endif /* emacs */
+
+#ifdef test
+
+#include <stdio.h>
+
+/* Indexed by a character, gives the upper case equivalent of the character */
+
+static char upcase[0400] =
+ { 000, 001, 002, 003, 004, 005, 006, 007,
+ 010, 011, 012, 013, 014, 015, 016, 017,
+ 020, 021, 022, 023, 024, 025, 026, 027,
+ 030, 031, 032, 033, 034, 035, 036, 037,
+ 040, 041, 042, 043, 044, 045, 046, 047,
+ 050, 051, 052, 053, 054, 055, 056, 057,
+ 060, 061, 062, 063, 064, 065, 066, 067,
+ 070, 071, 072, 073, 074, 075, 076, 077,
+ 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
+ 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
+ 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
+ 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137,
+ 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
+ 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
+ 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
+ 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177,
+ 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
+ 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
+ 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
+ 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
+ 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
+ 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
+ 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
+ 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
+ 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
+ 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
+ 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
+ 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
+ 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
+ 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
+ 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
+ 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
+ };
+
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ char pat[80];
+ struct re_pattern_buffer buf;
+ int i;
+ char c;
+ char fastmap[(1 << BYTEWIDTH)];
+
+ /* Allow a command argument to specify the style of syntax. */
+ if (argc > 1)
+ obscure_syntax = atoi (argv[1]);
+
+ buf.allocated = 40;
+ buf.buffer = (char *) malloc (buf.allocated);
+ buf.fastmap = fastmap;
+ buf.translate = upcase;
+
+ while (1)
+ {
+ gets (pat);
+
+ if (*pat)
+ {
+ re_compile_pattern (pat, strlen(pat), &buf);
+
+ for (i = 0; i < buf.used; i++)
+ printchar (buf.buffer[i]);
+
+ putchar ('\n');
+
+ printf ("%d allocated, %d used.\n", buf.allocated, buf.used);
+
+ re_compile_fastmap (&buf);
+ printf ("Allowed by fastmap: ");
+ for (i = 0; i < (1 << BYTEWIDTH); i++)
+ if (fastmap[i]) printchar (i);
+ putchar ('\n');
+ }
+
+ gets (pat); /* Now read the string to match against */
+
+ i = re_match (&buf, pat, strlen (pat), 0, 0);
+ printf ("Match value %d.\n", i);
+ }
+}
+
+#ifdef NOTDEF
+print_buf (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ int i;
+
+ printf ("buf is :\n----------------\n");
+ for (i = 0; i < bufp->used; i++)
+ printchar (bufp->buffer[i]);
+
+ printf ("\n%d allocated, %d used.\n", bufp->allocated, bufp->used);
+
+ printf ("Allowed by fastmap: ");
+ for (i = 0; i < (1 << BYTEWIDTH); i++)
+ if (bufp->fastmap[i])
+ printchar (i);
+ printf ("\nAllowed by translate: ");
+ if (bufp->translate)
+ for (i = 0; i < (1 << BYTEWIDTH); i++)
+ if (bufp->translate[i])
+ printchar (i);
+ printf ("\nfastmap is%s accurate\n", bufp->fastmap_accurate ? "" : "n't");
+ printf ("can %s be null\n----------", bufp->can_be_null ? "" : "not");
+}
+#endif
+
+printchar (c)
+ char c;
+{
+ if (c < 041 || c >= 0177)
+ {
+ putchar ('\\');
+ putchar (((c >> 6) & 3) + '0');
+ putchar (((c >> 3) & 7) + '0');
+ putchar ((c & 7) + '0');
+ }
+ else
+ putchar (c);
+}
+
+error (string)
+ char *string;
+{
+ puts (string);
+ exit (1);
+}
+
+#endif /* test */
diff --git a/gnu/usr.bin/gdb/regex.h b/gnu/usr.bin/gdb/regex.h
new file mode 100644
index 0000000..d0d8a82
--- /dev/null
+++ b/gnu/usr.bin/gdb/regex.h
@@ -0,0 +1,185 @@
+/* Definitions for data structures callers pass the regex library.
+ Copyright (C) 1985, 1989 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+ In other words, you are welcome to use, share and improve this program.
+ You are forbidden to forbid anyone else to use, share and improve
+ what you give them. Help stamp out software-hoarding! */
+
+
+/* Define number of parens for which we record the beginnings and ends.
+ This affects how much space the `struct re_registers' type takes up. */
+#ifndef RE_NREGS
+#define RE_NREGS 10
+#endif
+
+/* These bits are used in the obscure_syntax variable to choose among
+ alternative regexp syntaxes. */
+
+/* 1 means plain parentheses serve as grouping, and backslash
+ parentheses are needed for literal searching.
+ 0 means backslash-parentheses are grouping, and plain parentheses
+ are for literal searching. */
+#define RE_NO_BK_PARENS 1
+
+/* 1 means plain | serves as the "or"-operator, and \| is a literal.
+ 0 means \| serves as the "or"-operator, and | is a literal. */
+#define RE_NO_BK_VBAR 2
+
+/* 0 means plain + or ? serves as an operator, and \+, \? are literals.
+ 1 means \+, \? are operators and plain +, ? are literals. */
+#define RE_BK_PLUS_QM 4
+
+/* 1 means | binds tighter than ^ or $.
+ 0 means the contrary. */
+#define RE_TIGHT_VBAR 8
+
+/* 1 means treat \n as an _OR operator
+ 0 means treat it as a normal character */
+#define RE_NEWLINE_OR 16
+
+/* 0 means that a special characters (such as *, ^, and $) always have
+ their special meaning regardless of the surrounding context.
+ 1 means that special characters may act as normal characters in some
+ contexts. Specifically, this applies to:
+ ^ - only special at the beginning, or after ( or |
+ $ - only special at the end, or before ) or |
+ *, +, ? - only special when not after the beginning, (, or | */
+#define RE_CONTEXT_INDEP_OPS 32
+
+/* Now define combinations of bits for the standard possibilities. */
+#define RE_SYNTAX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_CONTEXT_INDEP_OPS)
+#define RE_SYNTAX_EGREP (RE_SYNTAX_AWK | RE_NEWLINE_OR)
+#define RE_SYNTAX_GREP (RE_BK_PLUS_QM | RE_NEWLINE_OR)
+#define RE_SYNTAX_EMACS 0
+
+/* This data structure is used to represent a compiled pattern. */
+
+struct re_pattern_buffer
+ {
+ char *buffer; /* Space holding the compiled pattern commands. */
+ int allocated; /* Size of space that buffer points to */
+ int used; /* Length of portion of buffer actually occupied */
+ char *fastmap; /* Pointer to fastmap, if any, or zero if none. */
+ /* re_search uses the fastmap, if there is one,
+ to skip quickly over totally implausible characters */
+ char *translate; /* Translate table to apply to all characters before comparing.
+ Or zero for no translation.
+ The translation is applied to a pattern when it is compiled
+ and to data when it is matched. */
+ char fastmap_accurate;
+ /* Set to zero when a new pattern is stored,
+ set to one when the fastmap is updated from it. */
+ char can_be_null; /* Set to one by compiling fastmap
+ if this pattern might match the null string.
+ It does not necessarily match the null string
+ in that case, but if this is zero, it cannot.
+ 2 as value means can match null string
+ but at end of range or before a character
+ listed in the fastmap. */
+ };
+
+/* Structure to store "register" contents data in.
+
+ Pass the address of such a structure as an argument to re_match, etc.,
+ if you want this information back.
+
+ start[i] and end[i] record the string matched by \( ... \) grouping i,
+ for i from 1 to RE_NREGS - 1.
+ start[0] and end[0] record the entire string matched. */
+
+struct re_registers
+ {
+ int start[RE_NREGS];
+ int end[RE_NREGS];
+ };
+
+/* These are the command codes that appear in compiled regular expressions, one per byte.
+ Some command codes are followed by argument bytes.
+ A command code can specify any interpretation whatever for its arguments.
+ Zero-bytes may appear in the compiled regular expression. */
+
+enum regexpcode
+ {
+ unused,
+ exactn, /* followed by one byte giving n, and then by n literal bytes */
+ begline, /* fails unless at beginning of line */
+ endline, /* fails unless at end of line */
+ jump, /* followed by two bytes giving relative address to jump to */
+ on_failure_jump, /* followed by two bytes giving relative address of place
+ to resume at in case of failure. */
+ finalize_jump, /* Throw away latest failure point and then jump to address. */
+ maybe_finalize_jump, /* Like jump but finalize if safe to do so.
+ This is used to jump back to the beginning
+ of a repeat. If the command that follows
+ this jump is clearly incompatible with the
+ one at the beginning of the repeat, such that
+ we can be sure that there is no use backtracking
+ out of repetitions already completed,
+ then we finalize. */
+ dummy_failure_jump, /* jump, and push a dummy failure point.
+ This failure point will be thrown away
+ if an attempt is made to use it for a failure.
+ A + construct makes this before the first repeat. */
+ anychar, /* matches any one character */
+ charset, /* matches any one char belonging to specified set.
+ First following byte is # bitmap bytes.
+ Then come bytes for a bit-map saying which chars are in.
+ Bits in each byte are ordered low-bit-first.
+ A character is in the set if its bit is 1.
+ A character too large to have a bit in the map
+ is automatically not in the set */
+ charset_not, /* similar but match any character that is NOT one of those specified */
+ start_memory, /* starts remembering the text that is matched
+ and stores it in a memory register.
+ followed by one byte containing the register number.
+ Register numbers must be in the range 0 through NREGS. */
+ stop_memory, /* stops remembering the text that is matched
+ and stores it in a memory register.
+ followed by one byte containing the register number.
+ Register numbers must be in the range 0 through NREGS. */
+ duplicate, /* match a duplicate of something remembered.
+ Followed by one byte containing the index of the memory register. */
+ before_dot, /* Succeeds if before dot */
+ at_dot, /* Succeeds if at dot */
+ after_dot, /* Succeeds if after dot */
+ begbuf, /* Succeeds if at beginning of buffer */
+ endbuf, /* Succeeds if at end of buffer */
+ wordchar, /* Matches any word-constituent character */
+ notwordchar, /* Matches any char that is not a word-constituent */
+ wordbeg, /* Succeeds if at word beginning */
+ wordend, /* Succeeds if at word end */
+ wordbound, /* Succeeds if at a word boundary */
+ notwordbound, /* Succeeds if not at a word boundary */
+ syntaxspec, /* Matches any character whose syntax is specified.
+ followed by a byte which contains a syntax code, Sword or such like */
+ notsyntaxspec /* Matches any character whose syntax differs from the specified. */
+ };
+
+extern char *re_compile_pattern ();
+/* Is this really advertised? */
+extern void re_compile_fastmap ();
+extern int re_search (), re_search_2 ();
+extern int re_match (), re_match_2 ();
+
+/* 4.2 bsd compatibility (yuck) */
+extern char *re_comp ();
+extern int re_exec ();
+
+#ifdef SYNTAX_TABLE
+extern char *re_syntax_table;
+#endif
diff --git a/gnu/usr.bin/gdb/remote-sl.c b/gnu/usr.bin/gdb/remote-sl.c
new file mode 100644
index 0000000..4c72197
--- /dev/null
+++ b/gnu/usr.bin/gdb/remote-sl.c
@@ -0,0 +1,10 @@
+/*
+ * The binary remote protocol is still under development at LBL;
+ * the current version can't be released.
+ * Sorry, folks...
+ */
+int
+sl_open()
+{
+ return -1;
+}
diff --git a/gnu/usr.bin/gdb/remote.c b/gnu/usr.bin/gdb/remote.c
new file mode 100644
index 0000000..59658a8
--- /dev/null
+++ b/gnu/usr.bin/gdb/remote.c
@@ -0,0 +1,626 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Van Jacobson and Steven McCanne of Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Header: /home/cvs/386BSD/src/usr.bin/gdb/remote.c,v 1.1.1.1 1993/06/12 14:52:22 rgrimes Exp $;
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)remote.c 6.5 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+#include "param.h"
+
+#include <stdio.h>
+#include <varargs.h>
+
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+
+#include "defs.h"
+#include "frame.h"
+#include "inferior.h"
+#include "wait.h"
+
+#include "kgdb_proto.h"
+
+static FILE *kiodebug;
+static int icache = 1;
+extern int kernel_debugging;
+
+static int remote_cache_valid;
+static int remote_instub;
+
+static void remote_signal();
+static void remote_debug();
+static void print_msg();
+
+static int remote_mtu;
+static int (*send_msg)();
+static int (*recv_msg)();
+static void (*closelink)();
+
+static u_char *inbuffer;
+static u_char *outbuffer;
+
+/*
+ * Statistics.
+ */
+static int remote_ierrs;
+static int remote_oerrs;
+static int remote_seqerrs;
+static int remote_spurious;
+
+#define PUTCMD(cmd) m_xchg(cmd, (u_char *)0, 0, (u_char *)0, (int *)0)
+
+/*
+ * Send an outbound message to the remote machine and read the reply.
+ * Either or both message buffers may be NULL.
+ */
+static int
+m_xchg(type, out, outlen, in, inlen)
+ int type;
+ u_char *out;
+ int outlen;
+ u_char *in;
+ int *inlen;
+{
+ register int err, (*send)() = send_msg, (*recv)() = recv_msg;
+ int ack;
+ static int seqbit = 0;
+
+ if (!remote_instub) {
+ remote_instub = 1;
+ PUTCMD(KGDB_EXEC);
+ }
+
+ seqbit ^= KGDB_SEQ;
+ while (1) {
+ err = (*send)(type | seqbit, out, outlen);
+ if (err) {
+ ++remote_oerrs;
+ if (kiodebug)
+ remote_debug("send error %d\n", err);
+ }
+ if (kiodebug)
+ print_msg(type | seqbit, out, outlen, 'O');
+
+ recv:
+ err = (*recv)(&ack, in, inlen);
+ if (err) {
+ ++remote_ierrs;
+ if (kiodebug)
+ remote_debug("recv error %d\n", err);
+ remote_cache_valid = 0;
+ } else if (kiodebug)
+ print_msg(ack, in, inlen ? *inlen : 0, 'I');
+
+ if (err)
+ continue;
+
+ if ((ack & KGDB_ACK) == 0 || KGDB_CMD(ack) != KGDB_CMD(type)) {
+ ++remote_spurious;
+ continue;
+ }
+ if ((ack & KGDB_SEQ) ^ seqbit) {
+ ++remote_seqerrs;
+ goto recv;
+ }
+ return ack;
+ }
+}
+
+/*
+ * Wait for the specified message type. Discard anything else.
+ * (this is used by 'remote-signal' to help us resync with other side.)
+ */
+static void
+m_recv(type, in, inlen)
+ int type;
+ u_char *in;
+ int *inlen;
+{
+ int reply, err;
+
+ while (1) {
+ err = (*recv_msg)(&reply, in, inlen);
+ if (err) {
+ ++remote_ierrs;
+ if (kiodebug)
+ remote_debug("recv error %d\n", err);
+ } else if (kiodebug)
+ print_msg(reply, in, inlen ? *inlen : 0, 'I');
+
+ if (KGDB_CMD(reply) == type)
+ return;
+ ++remote_spurious;
+ }
+}
+
+/*
+ * Send a message. Do not wait for *any* response from the other side.
+ * Some other thread of control will pick up the ack that will be generated.
+ */
+static void
+m_send(type, buf, len)
+ int type;
+ u_char *buf;
+ int len;
+{
+ int err;
+
+ if (!remote_instub) {
+ remote_instub = 1;
+ PUTCMD(KGDB_EXEC);
+ }
+
+ err = (*send_msg)(type, buf, len);
+ if (err) {
+ ++remote_ierrs;
+ if (kiodebug)
+ remote_debug("[send error %d] ", err);
+ }
+ if (kiodebug)
+ print_msg(type, buf, len, 'O');
+}
+
+/*
+ * Open a connection to a remote debugger.
+ * NAME is the filename used for communication.
+ */
+void
+remote_open(name, from_tty)
+ char *name;
+ int from_tty;
+{
+ int bufsize;
+
+ remote_debugging = 0;
+ if (sl_open(name, &send_msg, &recv_msg, &closelink, &remote_mtu,
+ &bufsize))
+ return;
+ if (from_tty)
+ printf("Remote debugging using %s\n", name);
+ remote_debugging = 1;
+
+ remote_cache_valid = 0;
+
+ inbuffer = (u_char *)malloc(bufsize);
+ outbuffer = (u_char *)malloc(bufsize);
+
+ remote_signal();
+
+ remote_ierrs = 0;
+ remote_oerrs = 0;
+ remote_spurious = 0;
+}
+
+/*
+ * Close the open connection to the remote debugger. Use this when you want
+ * to detach and do something else with your gdb.
+ */
+void
+remote_close(from_tty)
+ int from_tty;
+{
+ if (!remote_debugging)
+ error("remote debugging not enabled");
+
+ remote_debugging = 0;
+ /*
+ * Take remote machine out of debug mode.
+ */
+ (void)PUTCMD(KGDB_KILL);
+ (*closelink)();
+ if (from_tty)
+ printf("Ending remote debugging\n");
+
+ free((char *)inbuffer);
+ free((char *)outbuffer);
+}
+
+/*
+ * Tell the remote machine to resume.
+ */
+int
+remote_resume(step, signal)
+ int step, signal;
+{
+ if (!step) {
+ (void)PUTCMD(KGDB_CONT);
+ remote_instub = 0;
+ } else {
+#ifdef NO_SINGLE_STEP
+ single_step(0);
+#else
+ (void)PUTCMD(KGDB_STEP);
+#endif
+ }
+}
+
+/*
+ * Wait until the remote machine stops, then return, storing status in STATUS
+ * just as `wait' would.
+ */
+int
+remote_wait(status)
+ WAITTYPE *status;
+{
+ int len;
+
+ WSETEXIT((*status), 0);
+ /*
+ * When the machine stops, it will send us a KGDB_SIGNAL message,
+ * so we wait for one of these.
+ */
+ m_recv(KGDB_SIGNAL, inbuffer, &len);
+ WSETSTOP((*status), inbuffer[0]);
+}
+
+/*
+ * Register context as of last remote_fetch_registers().
+ */
+static char reg_cache[REGISTER_BYTES];
+
+/*
+ * Read the remote registers into the block REGS.
+ */
+void
+remote_fetch_registers(regs)
+ char *regs;
+{
+ int regno, len, rlen, ack;
+ u_char *cp, *ep;
+
+ regno = -1;
+ do {
+ outbuffer[0] = regno + 1;
+ ack = m_xchg(remote_cache_valid ?
+ KGDB_REG_R|KGDB_DELTA : KGDB_REG_R,
+ outbuffer, 1, inbuffer, &len);
+ cp = inbuffer;
+ ep = cp + len;
+ while (cp < ep) {
+ regno = *cp++;
+ rlen = REGISTER_RAW_SIZE(regno);
+ bcopy((char *)cp,
+ &reg_cache[REGISTER_BYTE(regno)], rlen);
+ cp += rlen;
+ }
+ } while (ack & KGDB_MORE);
+
+ remote_cache_valid = 1;
+ bcopy(reg_cache, regs, REGISTER_BYTES);
+}
+
+/*
+ * Store the remote registers from the contents of the block REGS.
+ */
+void
+remote_store_registers(regs)
+ char *regs;
+{
+ u_char *cp, *ep;
+ int regno, off, rlen;
+
+ cp = outbuffer;
+ ep = cp + remote_mtu;
+
+ for (regno = 0; regno < NUM_REGS; ++regno) {
+ off = REGISTER_BYTE(regno);
+ rlen = REGISTER_RAW_SIZE(regno);
+ if (!remote_cache_valid ||
+ bcmp(&regs[off], &reg_cache[off], rlen) != 0) {
+ if (cp + rlen + 1 >= ep) {
+ (void)m_xchg(KGDB_REG_W,
+ outbuffer, cp - outbuffer,
+ (u_char *)0, (int *)0);
+ cp = outbuffer;
+ }
+ *cp++ = regno;
+ bcopy(&regs[off], cp, rlen);
+ cp += rlen;
+ }
+ }
+ if (cp != outbuffer)
+ (void)m_xchg(KGDB_REG_W, outbuffer, cp - outbuffer,
+ (u_char *)0, (int *)0);
+ bcopy(regs, reg_cache, REGISTER_BYTES);
+}
+
+/*
+ * Store a chunk of memory into the remote host.
+ * 'remote_addr' is the address in the remote memory space.
+ * 'cp' is the address of the buffer in our space, and 'len' is
+ * the number of bytes. Returns an errno status.
+ */
+int
+remote_write_inferior_memory(remote_addr, cp, len)
+ CORE_ADDR remote_addr;
+ u_char *cp;
+ int len;
+{
+ int cnt;
+
+ while (len > 0) {
+ cnt = min(len, remote_mtu - 4);
+ bcopy((char *)&remote_addr, outbuffer, 4);
+ bcopy(cp, outbuffer + 4, cnt);
+ (void)m_xchg(KGDB_MEM_W, outbuffer, cnt + 4, inbuffer, &len);
+
+ if (inbuffer[0])
+ return inbuffer[0];
+
+ remote_addr += cnt;
+ cp += cnt;
+ len -= cnt;
+ }
+ return 0;
+}
+
+/*
+ * Read memory data directly from the remote machine.
+ * 'remote_addr' is the address in the remote memory space.
+ * 'cp' is the address of the buffer in our space, and 'len' is
+ * the number of bytes. Returns an errno status.
+ */
+static int
+remote_read_memory(remote_addr, cp, len)
+ CORE_ADDR remote_addr;
+ u_char *cp;
+ int len;
+{
+ int cnt, inlen;
+
+ while (len > 0) {
+ cnt = min(len, remote_mtu - 1);
+ outbuffer[0] = cnt;
+ bcopy((char *)&remote_addr, (char *)&outbuffer[1], 4);
+
+ (void)m_xchg(KGDB_MEM_R, outbuffer, 5, inbuffer, &inlen);
+
+ if (inbuffer[0] != 0)
+ return inbuffer[0];
+
+ if (cnt != inlen - 1)
+ /* XXX */
+ error("remote_read_memory() request botched");
+
+ bcopy((char *)&inbuffer[1], (char *)cp, cnt);
+
+ remote_addr += cnt;
+ cp += cnt;
+ len -= cnt;
+ }
+ return 0;
+}
+
+int
+remote_read_inferior_memory(remote_addr, cp, len)
+ CORE_ADDR remote_addr;
+ char *cp;
+ int len;
+{
+ int stat = 0;
+
+ if (icache) {
+ extern CORE_ADDR text_start, text_end;
+ CORE_ADDR xferend = remote_addr + len;
+
+ if (remote_addr < text_end && text_start < xferend) {
+ /*
+ * at least part of this xfer is in the text
+ * space -- xfer the overlap from the exec file.
+ */
+ if (remote_addr >= text_start && xferend < text_end)
+ return (xfer_core_file(remote_addr, cp, len));
+ if (remote_addr >= text_start) {
+ int i = text_end - remote_addr;
+
+ if (stat = xfer_core_file(remote_addr, cp, i))
+ return (stat);
+ remote_addr += i;
+ cp += i;
+ len -= i;
+ } else if (xferend <= text_end) {
+ int i = xferend - text_start;
+
+ len = text_start - remote_addr;
+ if (stat = xfer_core_file(text_start,
+ cp + len, i))
+ return (stat);
+ }
+ }
+ }
+ return remote_read_memory(remote_addr, cp, len);
+}
+
+/*
+ * Signal the remote machine. The remote end might be idle or it might
+ * already be in debug mode -- we need to handle both case. Thus, we use
+ * the framing character as the wakeup byte, and send a SIGNAL packet.
+ * If the remote host is idle, the framing character will wake it up.
+ * If it is in the kgdb stub, then we will get a SIGNAL reply.
+ */
+static void
+remote_signal()
+{
+ if (!remote_debugging)
+ printf("Remote debugging not enabled.\n");
+ else {
+ remote_instub = 0;
+ m_send(KGDB_SIGNAL, (u_char *)0, 0);
+ }
+}
+
+static void
+remote_signal_command()
+{
+ extern int stop_after_attach;
+
+ if (!remote_debugging)
+ error("Not debugging remote.");
+ remote_cache_valid = 0;
+ remote_signal();
+ restart_remote();
+}
+
+/*
+ * Print a message for debugging.
+ */
+static void
+print_msg(type, buf, len, dir)
+ int type;
+ u_char *buf;
+ int len;
+ int dir;
+{
+ int i;
+ char *s;
+
+ switch (KGDB_CMD(type)) {
+ case KGDB_MEM_R: s = "memr"; break;
+ case KGDB_MEM_W: s = "memw"; break;
+ case KGDB_REG_R: s = "regr"; break;
+ case KGDB_REG_W: s = "regw"; break;
+ case KGDB_CONT: s = "cont"; break;
+ case KGDB_STEP: s = "step"; break;
+ case KGDB_KILL: s = "kill"; break;
+ case KGDB_SIGNAL: s = "sig "; break;
+ case KGDB_EXEC: s = "exec"; break;
+ default: s = "unk "; break;
+ }
+ remote_debug("%c %c%c%c%c %s (%02x): ", dir,
+ (type & KGDB_ACK) ? 'A' : '.',
+ (type & KGDB_DELTA) ? 'D' : '.',
+ (type & KGDB_MORE) ? 'M' : '.',
+ (type & KGDB_SEQ) ? '-' : '+',
+ s, type);
+ if (buf)
+ for (i = 0; i < len; ++i)
+ remote_debug("%02x", buf[i]);
+ remote_debug("\n");
+}
+
+static void
+set_remote_text_refs_command(arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ icache = !parse_binary_operation("set remote-text-refs", arg);
+}
+
+static void
+remote_debug_command(arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ char *name;
+
+ if (kiodebug != 0 && kiodebug != stderr)
+ (void)fclose(kiodebug);
+
+ if (arg == 0) {
+ kiodebug = 0;
+ printf("Remote debugging off.\n");
+ return;
+ }
+ if (arg[0] == '-') {
+ kiodebug = stderr;
+ name = "stderr";
+ } else {
+ kiodebug = fopen(arg, "w");
+ if (kiodebug == 0) {
+ printf("Cannot open '%s'.\n", arg);
+ return;
+ }
+ name = arg;
+ }
+ printf("Remote debugging output routed to %s.\n", name);
+}
+
+/* ARGSUSED */
+static void
+remote_info(arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ printf("Using %s for text references.\n",
+ icache? "local executable" : "remote");
+ printf("Protocol debugging is %s.\n", kiodebug? "on" : "off");
+ printf("%d spurious input messages.\n", remote_spurious);
+ printf("%d input errors; %d output errors; %d sequence errors.\n",
+ remote_ierrs, remote_oerrs, remote_seqerrs);
+}
+
+/* VARARGS */
+static void
+remote_debug(va_alist)
+ va_dcl
+{
+ register char *cp;
+ va_list ap;
+
+ va_start(ap);
+ cp = va_arg(ap, char *);
+ (void)vfprintf(kiodebug, cp, ap);
+ va_end(ap);
+ fflush(kiodebug);
+}
+
+extern struct cmd_list_element *setlist;
+
+void
+_initialize_remote()
+{
+ add_com("remote-signal", class_run, remote_signal_command,
+ "If remote debugging, send interrupt signal to remote.");
+ add_cmd("remote-text-refs", class_support,
+ set_remote_text_refs_command,
+"Enable/disable use of local executable for text segment references.\n\
+If on, all memory read/writes go to remote.\n\
+If off, text segment reads use the local executable.",
+ &setlist);
+
+ add_com("remote-debug", class_run, remote_debug_command,
+"With a file name argument, enables output of remote protocol debugging\n\
+messages to said file. If file is `-', stderr is used.\n\
+With no argument, remote debugging is disabled.");
+
+ add_info("remote", remote_info,
+ "Show current settings of remote debugging options.");
+}
+
diff --git a/gnu/usr.bin/gdb/source.c b/gnu/usr.bin/gdb/source.c
new file mode 100644
index 0000000..bb65e1c
--- /dev/null
+++ b/gnu/usr.bin/gdb/source.c
@@ -0,0 +1,1166 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)source.c 6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* List lines of source files for GDB, the GNU debugger.
+ Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <string.h>
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+
+#ifdef USG
+#include <sys/types.h>
+#include <fcntl.h>
+#endif
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+
+/* Path of directories to search for source files.
+ Same format as the PATH environment variable's value. */
+
+static char *source_path;
+
+/* Symtab of default file for listing lines of. */
+
+struct symtab *current_source_symtab;
+
+/* Default next line to list. */
+
+int current_source_line;
+
+/* Line number of last line printed. Default for various commands.
+ current_source_line is usually, but not always, the same as this. */
+
+static int last_line_listed;
+
+/* First line number listed by last listing command. */
+
+static int first_line_listed;
+
+
+struct symtab *psymtab_to_symtab ();
+
+/* Set the source file default for the "list" command, specifying a
+ symtab. Sigh. Behaivior specification: If it is called with a
+ non-zero argument, that is the symtab to select. If it is not,
+ first lookup "main"; if it exists, use the symtab and line it
+ defines. If not, take the last symtab in the symtab_list (if it
+ exists) or the last symtab in the psytab_list (if *it* exists). If
+ none of this works, report an error. */
+
+void
+select_source_symtab (s)
+ register struct symtab *s;
+{
+ struct symtabs_and_lines sals;
+ struct symtab_and_line sal;
+ struct partial_symtab *ps, *cs_pst;
+
+ if (s)
+ {
+ current_source_symtab = s;
+ current_source_line = 1;
+ return;
+ }
+
+ /* Make the default place to list be the function `main'
+ if one exists. */
+ if (lookup_symbol ("main", 0, VAR_NAMESPACE, 0))
+ {
+ sals = decode_line_spec ("main", 1);
+ sal = sals.sals[0];
+ free (sals.sals);
+ current_source_symtab = sal.symtab;
+ current_source_line = max (sal.line - 9, 1);
+ return;
+ }
+
+ /* All right; find the last file in the symtab list (ignoring .h's). */
+
+ if (s = symtab_list)
+ {
+ do
+ {
+ char *name = s->filename;
+ int len = strlen (name);
+ if (! (len > 2 && !strcmp (&name[len - 2], ".h")))
+ current_source_symtab = s;
+ s = s->next;
+ }
+ while (s);
+ current_source_line = 1;
+ }
+ else if (partial_symtab_list)
+ {
+ ps = partial_symtab_list;
+ while (ps)
+ {
+ char *name = ps->filename;
+ int len = strlen (name);
+ if (! (len > 2 && !strcmp (&name[len - 2], ".h")))
+ cs_pst = ps;
+ ps = ps->next;
+ }
+ if (cs_pst)
+ if (cs_pst->readin)
+ fatal ("Internal: select_source_symtab: readin pst found and no symtabs.");
+ else
+ current_source_symtab = psymtab_to_symtab (cs_pst);
+ else
+ current_source_symtab = 0;
+ current_source_line = 1;
+ }
+}
+
+static void
+directories_info ()
+{
+ printf ("Source directories searched: %s\n", source_path);
+}
+
+void
+init_source_path ()
+{
+ register struct symtab *s;
+
+ source_path = savestring (current_directory, strlen (current_directory));
+
+ /* Forget what we learned about line positions in source files;
+ must check again now since files may be found in
+ a different directory now. */
+ for (s = symtab_list; s; s = s->next)
+ if (s->line_charpos != 0)
+ {
+ free (s->line_charpos);
+ s->line_charpos = 0;
+ }
+}
+
+void
+directory_command (dirname, from_tty)
+ char *dirname;
+ int from_tty;
+{
+ char *old = source_path;
+
+ dont_repeat ();
+
+ if (dirname == 0)
+ {
+ if (query ("Reinitialize source path to %s? ", current_directory))
+ {
+ init_source_path ();
+ free (old);
+ }
+ }
+ else
+ {
+ dirname = tilde_expand (dirname);
+ make_cleanup (free, dirname);
+
+ do
+ {
+ char *name = dirname;
+ register char *p;
+ struct stat st;
+
+ {
+ char *colon = index (name, ':');
+ char *space = index (name, ' ');
+ char *tab = index (name, '\t');
+ if (colon == 0 && space == 0 && tab == 0)
+ p = dirname = name + strlen (name);
+ else
+ {
+ p = 0;
+ if (colon != 0 && (p == 0 || colon < p))
+ p = colon;
+ if (space != 0 && (p == 0 || space < p))
+ p = space;
+ if (tab != 0 && (p == 0 || tab < p))
+ p = tab;
+ dirname = p + 1;
+ while (*dirname == ':' || *dirname == ' ' || *dirname == '\t')
+ ++dirname;
+ }
+ }
+
+ if (p[-1] == '/')
+ /* Sigh. "foo/" => "foo" */
+ --p;
+ *p = '\0';
+
+ while (p[-1] == '.')
+ {
+ if (p - name == 1)
+ {
+ /* "." => getwd (). */
+ name = current_directory;
+ goto append;
+ }
+ else if (p[-2] == '/')
+ {
+ if (p - name == 2)
+ {
+ /* "/." => "/". */
+ *--p = '\0';
+ goto append;
+ }
+ else
+ {
+ /* "...foo/." => "...foo". */
+ p -= 2;
+ *p = '\0';
+ continue;
+ }
+ }
+ else
+ break;
+ }
+
+ if (*name != '/')
+ name = concat (current_directory, "/", name);
+ else
+ name = savestring (name, p - name);
+ make_cleanup (free, name);
+
+ if (stat (name, &st) < 0)
+ perror_with_name (name);
+ if ((st.st_mode & S_IFMT) != S_IFDIR)
+ error ("%s is not a directory.", name);
+
+ append:
+ {
+ register unsigned int len = strlen (name);
+
+ p = source_path;
+ while (1)
+ {
+ if (!strncmp (p, name, len)
+ && (p[len] == '\0' || p[len] == ':'))
+ {
+ if (from_tty)
+ printf ("\"%s\" is already in the source path.\n", name);
+ break;
+ }
+ p = index (p, ':');
+ if (p != 0)
+ ++p;
+ else
+ break;
+ }
+ if (p == 0)
+ {
+ source_path = concat (old, ":", name);
+ free (old);
+ old = source_path;
+ }
+ }
+ } while (*dirname != '\0');
+ if (from_tty)
+ directories_info ();
+ }
+}
+
+/* Open a file named STRING, searching path PATH (dir names sep by colons)
+ using mode MODE and protection bits PROT in the calls to open.
+ If TRY_CWD_FIRST, try to open ./STRING before searching PATH.
+ (ie pretend the first element of PATH is ".")
+ If FILENAMED_OPENED is non-null, set it to a newly allocated string naming
+ the actual file opened (this string will always start with a "/"
+
+ If a file is found, return the descriptor.
+ Otherwise, return -1, with errno set for the last name we tried to open. */
+
+/* >>>> This should only allow files of certain types,
+ >>>> eg executable, non-directory */
+int
+openp (path, try_cwd_first, string, mode, prot, filename_opened)
+ char *path;
+ int try_cwd_first;
+ char *string;
+ int mode;
+ int prot;
+ char **filename_opened;
+{
+ register int fd;
+ register char *filename;
+ register char *p, *p1;
+ register int len;
+
+ if (!path)
+ path = ".";
+
+ /* ./foo => foo */
+ while (string[0] == '.' && string[1] == '/')
+ string += 2;
+
+ if (try_cwd_first || string[0] == '/')
+ {
+ filename = string;
+ fd = open (filename, mode, prot);
+ if (fd >= 0 || string[0] == '/')
+ goto done;
+ }
+
+ filename = (char *) alloca (strlen (path) + strlen (string) + 2);
+ fd = -1;
+ for (p = path; p; p = p1 ? p1 + 1 : 0)
+ {
+ p1 = (char *) index (p, ':');
+ if (p1)
+ len = p1 - p;
+ else
+ len = strlen (p);
+
+ strncpy (filename, p, len);
+ filename[len] = 0;
+ strcat (filename, "/");
+ strcat (filename, string);
+
+ fd = open (filename, mode, prot);
+ if (fd >= 0) break;
+ }
+
+ done:
+ if (filename_opened)
+ if (fd < 0)
+ *filename_opened = (char *) 0;
+ else if (filename[0] == '/')
+ *filename_opened = savestring (filename, strlen (filename));
+ else
+ {
+ *filename_opened = concat (current_directory, "/", filename);
+ }
+
+ return fd;
+}
+
+/* Create and initialize the table S->line_charpos that records
+ the positions of the lines in the source file, which is assumed
+ to be open on descriptor DESC.
+ All set S->nlines to the number of such lines. */
+
+static void
+find_source_lines (s, desc)
+ struct symtab *s;
+ int desc;
+{
+ struct stat st;
+ register char *data, *p, *end;
+ int nlines = 0;
+ int lines_allocated = 1000;
+ int *line_charpos = (int *) xmalloc (lines_allocated * sizeof (int));
+ extern int exec_mtime;
+
+ if (fstat (desc, &st) < 0)
+ perror_with_name (s->filename);
+ if (get_exec_file (0) != 0 && exec_mtime < st.st_mtime)
+ printf ("Source file is more recent than executable.\n");
+
+ data = (char *) alloca (st.st_size);
+ if (myread (desc, data, st.st_size) < 0)
+ perror_with_name (s->filename);
+ end = data + st.st_size;
+ p = data;
+ line_charpos[0] = 0;
+ nlines = 1;
+ while (p != end)
+ {
+ if (*p++ == '\n'
+ /* A newline at the end does not start a new line. */
+ && p != end)
+ {
+ if (nlines == lines_allocated)
+ {
+ lines_allocated *= 2;
+ line_charpos = (int *) xrealloc (line_charpos,
+ sizeof (int) * lines_allocated);
+ }
+ line_charpos[nlines++] = p - data;
+ }
+ }
+ s->nlines = nlines;
+ s->line_charpos = (int *) xrealloc (line_charpos, nlines * sizeof (int));
+}
+
+/* Return the character position of a line LINE in symtab S.
+ Return 0 if anything is invalid. */
+
+int
+source_line_charpos (s, line)
+ struct symtab *s;
+ int line;
+{
+ if (!s) return 0;
+ if (!s->line_charpos || line <= 0) return 0;
+ if (line > s->nlines)
+ line = s->nlines;
+ return s->line_charpos[line - 1];
+}
+
+/* Return the line number of character position POS in symtab S. */
+
+int
+source_charpos_line (s, chr)
+ register struct symtab *s;
+ register int chr;
+{
+ register int line = 0;
+ register int *lnp;
+
+ if (s == 0 || s->line_charpos == 0) return 0;
+ lnp = s->line_charpos;
+ /* Files are usually short, so sequential search is Ok */
+ while (line < s->nlines && *lnp <= chr)
+ {
+ line++;
+ lnp++;
+ }
+ if (line >= s->nlines)
+ line = s->nlines;
+ return line;
+}
+
+/* Get full pathname and line number positions for a symtab.
+ Return nonzero if line numbers may have changed.
+ Set *FULLNAME to actual name of the file as found by `openp',
+ or to 0 if the file is not found. */
+
+int
+get_filename_and_charpos (s, line, fullname)
+ struct symtab *s;
+ int line;
+ char **fullname;
+{
+ register int desc, linenums_changed = 0;
+
+ desc = openp (source_path, 0, s->filename, O_RDONLY, 0, &s->fullname);
+ if (desc < 0)
+ {
+ if (fullname)
+ *fullname = NULL;
+ return 0;
+ }
+ if (fullname)
+ *fullname = s->fullname;
+ if (s->line_charpos == 0) linenums_changed = 1;
+ if (linenums_changed) find_source_lines (s, desc);
+ close (desc);
+ return linenums_changed;
+}
+
+/* Print text describing the full name of the source file S
+ and the line number LINE and its corresponding character position.
+ The text starts with two Ctrl-z so that the Emacs-GDB interface
+ can easily find it.
+
+ MID_STATEMENT is nonzero if the PC is not at the beginning of that line.
+
+ Return 1 if successful, 0 if could not find the file. */
+
+int
+identify_source_line (s, line, mid_statement)
+ struct symtab *s;
+ int line;
+ int mid_statement;
+{
+ if (s->line_charpos == 0)
+ get_filename_and_charpos (s, line, 0);
+ if (s->fullname == 0)
+ return 0;
+ printf ("\032\032%s:%d:%d:%s:0x%x\n", s->fullname,
+ line, s->line_charpos[line - 1],
+ mid_statement ? "middle" : "beg",
+ get_frame_pc (get_current_frame()));
+ current_source_line = line;
+ first_line_listed = line;
+ last_line_listed = line;
+ current_source_symtab = s;
+ return 1;
+}
+
+/* Print source lines from the file of symtab S,
+ starting with line number LINE and stopping before line number STOPLINE. */
+
+void
+print_source_lines (s, line, stopline, noerror)
+ struct symtab *s;
+ int line, stopline;
+ int noerror;
+{
+ register int c;
+ register int desc;
+ register FILE *stream;
+ int nlines = stopline - line;
+
+ desc = openp (source_path, 0, s->filename, O_RDONLY, 0, &s->fullname);
+ if (desc < 0)
+ {
+ extern int errno;
+ if (noerror && line + 1 == stopline)
+ {
+ /* can't find the file - tell user where we are anyway */
+ current_source_symtab = s;
+ current_source_line = line;
+ first_line_listed = line;
+ last_line_listed = line;
+ printf_filtered ("%d\t(%s)\n", current_source_line++, s->filename);
+ }
+ else
+ {
+ if (! noerror)
+ perror_with_name (s->filename);
+ print_sys_errmsg (s->filename, errno);
+ }
+ return;
+ }
+
+ if (s->line_charpos == 0)
+ find_source_lines (s, desc);
+
+ if (line < 1 || line > s->nlines)
+ {
+ close (desc);
+ error ("Line number %d out of range; %s has %d lines.",
+ line, s->filename, s->nlines);
+ }
+
+ if (lseek (desc, s->line_charpos[line - 1], 0) < 0)
+ {
+ close (desc);
+ perror_with_name (s->filename);
+ }
+
+ current_source_symtab = s;
+ current_source_line = line;
+ first_line_listed = line;
+
+ stream = fdopen (desc, "r");
+ clearerr (stream);
+
+ while (nlines-- > 0)
+ {
+ c = fgetc (stream);
+ if (c == EOF) break;
+ last_line_listed = current_source_line;
+ printf_filtered ("%d\t", current_source_line++);
+ do
+ {
+ if (c < 040 && c != '\t' && c != '\n')
+ printf_filtered ("^%c", c + 0100);
+ else if (c == 0177)
+ printf_filtered ("^?");
+ else
+ printf_filtered ("%c", c);
+ } while (c != '\n' && (c = fgetc (stream)) >= 0);
+ }
+
+ fclose (stream);
+}
+
+
+
+/*
+ C++
+ Print a list of files and line numbers which a user may choose from
+ in order to list a function which was specified ambiguously
+ (as with `list classname::overloadedfuncname', for example).
+ The vector in SALS provides the filenames and line numbers.
+ */
+static void
+ambiguous_line_spec (sals)
+ struct symtabs_and_lines *sals;
+{
+ int i;
+
+ for (i = 0; i < sals->nelts; ++i)
+ printf("file: \"%s\", line number: %d\n",
+ sals->sals[i].symtab->filename, sals->sals[i].line);
+}
+
+
+static void
+file_command(arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ struct symtabs_and_lines sals;
+ struct symtab_and_line sal;
+ struct symbol *sym;
+ char *arg1;
+ int linenum_beg = 0;
+ char *p;
+
+ if (symtab_list == 0 && partial_symtab_list == 0)
+ error ("No symbol table is loaded. Use the \"symbol-file\" command.");
+
+ /* Pull in a current source symtab if necessary */
+ if (arg == 0 || arg[0] == 0) {
+ if (current_source_symtab == 0)
+ select_source_symtab(0);
+ else
+ printf("%s\n", current_source_symtab->filename);
+ return;
+ }
+ arg1 = arg;
+ sals = decode_line_1 (&arg1, 0, 0, 0);
+
+ if (! sals.nelts)
+ return; /* C++ */
+
+ if (sals.nelts > 1)
+ {
+ ambiguous_line_spec (&sals);
+ free (sals.sals);
+ return;
+ }
+
+ sal = sals.sals[0];
+ free (sals.sals);
+
+ /* Record whether the BEG arg is all digits. */
+
+ for (p = arg; p != arg1 && *p >= '0' && *p <= '9'; ++p)
+ ;
+ linenum_beg = (p == arg1);
+
+ /* if line was specified by address,
+ print exactly which line, and which file.
+ In this case, sal.symtab == 0 means address is outside
+ of all known source files, not that user failed to give a filename. */
+ if (*arg == '*')
+ {
+ if (sal.symtab == 0)
+ error ("No source file for address 0x%x.", sal.pc);
+ sym = find_pc_function (sal.pc);
+ if (sym)
+ printf ("0x%x is in %s (%s, line %d).\n",
+ sal.pc, SYMBOL_NAME (sym), sal.symtab->filename, sal.line);
+ else
+ printf ("0x%x is in %s, line %d.\n",
+ sal.pc, sal.symtab->filename, sal.line);
+ }
+
+ /* If line was not specified by just a line number,
+ and it does not imply a symtab, it must be an undebuggable symbol
+ which means no source code. */
+
+ if (sal.symtab == 0)
+ {
+ if (! linenum_beg)
+ error ("No line number known for %s.", arg);
+ else
+ error ("No default source file yet. Do \"help list\".");
+ }
+ else
+ {
+ current_source_symtab = sal.symtab;
+ current_source_line = sal.line;
+ first_line_listed = sal.line;
+ }
+}
+
+#define PUSH_STACK_SIZE 32
+static struct {
+ struct symtab *symtab;
+ int line;
+} push_stack[PUSH_STACK_SIZE];
+
+static unsigned int push_stack_ptr;
+
+static void
+push_to_file_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ struct symtab *cursym = current_source_symtab;
+ int curline = current_source_line;
+ register unsigned int i;
+
+ file_command(arg, from_tty);
+
+ /* if we got back, command was successful */
+ i = push_stack_ptr;
+ push_stack[i].symtab = cursym;
+ push_stack[i].line = curline;
+ push_stack_ptr = (i + 1) & (PUSH_STACK_SIZE - 1);
+}
+
+static void
+pop_file_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ register unsigned int i = push_stack_ptr;
+
+ /* if there's something on the stack, pop it & clear the slot. */
+ i = (i + (PUSH_STACK_SIZE - 1)) & (PUSH_STACK_SIZE - 1);
+ if (push_stack[i].symtab) {
+ current_source_symtab = push_stack[i].symtab;
+ first_line_listed = current_source_line = push_stack[i].line;
+ push_stack[i].symtab = NULL;
+ push_stack_ptr = i;
+ }
+}
+
+
+static void
+list_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ struct symtabs_and_lines sals, sals_end;
+ struct symtab_and_line sal, sal_end;
+ struct symbol *sym;
+ char *arg1;
+ int no_end = 1;
+ int dummy_end = 0;
+ int dummy_beg = 0;
+ int linenum_beg = 0;
+ char *p;
+
+ if (symtab_list == 0 && partial_symtab_list == 0)
+ error ("No symbol table is loaded. Use the \"symbol-file\" command.");
+
+ /* Pull in a current source symtab if necessary */
+ if (current_source_symtab == 0 &&
+ (arg == 0 || arg[0] == '+' || arg[0] == '-'))
+ select_source_symtab (0);
+
+ /* "l" or "l +" lists next ten lines. */
+
+ if (arg == 0 || !strcmp (arg, "+"))
+ {
+ if (current_source_symtab == 0)
+ error ("No default source file yet. Do \"help list\".");
+ print_source_lines (current_source_symtab, current_source_line,
+ current_source_line + 10, 0);
+ return;
+ }
+
+ /* "l -" lists previous ten lines, the ones before the ten just listed. */
+ if (!strcmp (arg, "-"))
+ {
+ if (current_source_symtab == 0)
+ error ("No default source file yet. Do \"help list\".");
+ print_source_lines (current_source_symtab,
+ max (first_line_listed - 10, 1),
+ first_line_listed, 0);
+ return;
+ }
+
+ /* Now if there is only one argument, decode it in SAL
+ and set NO_END.
+ If there are two arguments, decode them in SAL and SAL_END
+ and clear NO_END; however, if one of the arguments is blank,
+ set DUMMY_BEG or DUMMY_END to record that fact. */
+
+ arg1 = arg;
+ if (*arg1 == ',')
+ dummy_beg = 1;
+ else
+ {
+ sals = decode_line_1 (&arg1, 0, 0, 0);
+
+ if (! sals.nelts) return; /* C++ */
+ if (sals.nelts > 1)
+ {
+ ambiguous_line_spec (&sals);
+ free (sals.sals);
+ return;
+ }
+
+ sal = sals.sals[0];
+ free (sals.sals);
+ }
+
+ /* Record whether the BEG arg is all digits. */
+
+ for (p = arg; p != arg1 && *p >= '0' && *p <= '9'; p++);
+ linenum_beg = (p == arg1);
+
+ while (*arg1 == ' ' || *arg1 == '\t')
+ arg1++;
+ if (*arg1 == ',')
+ {
+ no_end = 0;
+ arg1++;
+ while (*arg1 == ' ' || *arg1 == '\t')
+ arg1++;
+ if (*arg1 == 0)
+ dummy_end = 1;
+ else
+ {
+ if (dummy_beg)
+ sals_end = decode_line_1 (&arg1, 0, 0, 0);
+ else
+ sals_end = decode_line_1 (&arg1, 0, sal.symtab, sal.line);
+ if (sals_end.nelts == 0)
+ return;
+ if (sals_end.nelts > 1)
+ {
+ ambiguous_line_spec (&sals_end);
+ free (sals_end.sals);
+ return;
+ }
+ sal_end = sals_end.sals[0];
+ free (sals_end.sals);
+ }
+ }
+
+ if (*arg1)
+ error ("Junk at end of line specification.");
+
+ if (!no_end && !dummy_beg && !dummy_end
+ && sal.symtab != sal_end.symtab)
+ error ("Specified start and end are in different files.");
+ if (dummy_beg && dummy_end)
+ error ("Two empty args do not say what lines to list.");
+
+ /* if line was specified by address,
+ first print exactly which line, and which file.
+ In this case, sal.symtab == 0 means address is outside
+ of all known source files, not that user failed to give a filename. */
+ if (*arg == '*')
+ {
+ if (sal.symtab == 0)
+ error ("No source file for address 0x%x.", sal.pc);
+ sym = find_pc_function (sal.pc);
+ if (sym)
+ printf ("0x%x is in %s (%s, line %d).\n",
+ sal.pc, SYMBOL_NAME (sym), sal.symtab->filename, sal.line);
+ else
+ printf ("0x%x is in %s, line %d.\n",
+ sal.pc, sal.symtab->filename, sal.line);
+ }
+
+ /* If line was not specified by just a line number,
+ and it does not imply a symtab, it must be an undebuggable symbol
+ which means no source code. */
+
+ if (! linenum_beg && sal.symtab == 0)
+ error ("No line number known for %s.", arg);
+
+ /* If this command is repeated with RET,
+ turn it into the no-arg variant. */
+
+ if (from_tty)
+ *arg = 0;
+
+ if (dummy_beg && sal_end.symtab == 0)
+ error ("No default source file yet. Do \"help list\".");
+ if (dummy_beg)
+ print_source_lines (sal_end.symtab, max (sal_end.line - 9, 1),
+ sal_end.line + 1, 0);
+ else if (sal.symtab == 0)
+ error ("No default source file yet. Do \"help list\".");
+ else if (no_end)
+ print_source_lines (sal.symtab, max (sal.line - 5, 1), sal.line + 5, 0);
+ else
+ print_source_lines (sal.symtab, sal.line,
+ dummy_end ? sal.line + 10 : sal_end.line + 1,
+ 0);
+}
+
+/* Print info on range of pc's in a specified line. */
+
+static void
+line_info (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ struct symtabs_and_lines sals;
+ struct symtab_and_line sal;
+ int start_pc, end_pc;
+ int i;
+
+ if (arg == 0)
+ {
+ sal.symtab = current_source_symtab;
+ sal.line = last_line_listed;
+ sals.nelts = 1;
+ sals.sals = (struct symtab_and_line *)
+ xmalloc (sizeof (struct symtab_and_line));
+ sals.sals[0] = sal;
+ }
+ else
+ {
+ sals = decode_line_spec_1 (arg, 0);
+
+ /* If this command is repeated with RET,
+ turn it into the no-arg variant. */
+ if (from_tty)
+ *arg = 0;
+ }
+
+ /* C++ More than one line may have been specified, as when the user
+ specifies an overloaded function name. Print info on them all. */
+ for (i = 0; i < sals.nelts; i++)
+ {
+ sal = sals.sals[i];
+
+ if (sal.symtab == 0)
+ error ("No source file specified.");
+
+ if (sal.line > 0
+ && find_line_pc_range (sal.symtab, sal.line, &start_pc, &end_pc))
+ {
+ if (start_pc == end_pc)
+ printf ("Line %d of \"%s\" is at pc 0x%x but contains no code.\n",
+ sal.line, sal.symtab->filename, start_pc);
+ else
+ printf ("Line %d of \"%s\" starts at pc 0x%x and ends at 0x%x.\n",
+ sal.line, sal.symtab->filename, start_pc, end_pc);
+ /* x/i should display this line's code. */
+ set_next_address (start_pc);
+ /* Repeating "info line" should do the following line. */
+ last_line_listed = sal.line + 1;
+ }
+ else
+ printf ("Line number %d is out of range for \"%s\".\n",
+ sal.line, sal.symtab->filename);
+ }
+}
+
+/* Commands to search the source file for a regexp. */
+
+static void
+forward_search_command (regex, from_tty)
+ char *regex;
+{
+ register int c;
+ register int desc;
+ register FILE *stream;
+ int line = last_line_listed + 1;
+ char *msg;
+
+ msg = (char *) re_comp (regex);
+ if (msg)
+ error (msg);
+
+ if (current_source_symtab == 0)
+ select_source_symtab (0);
+
+ /* Search from last_line_listed+1 in current_source_symtab */
+
+ desc = openp (source_path, 0, current_source_symtab->filename,
+ O_RDONLY, 0, &current_source_symtab->fullname);
+ if (desc < 0)
+ perror_with_name (current_source_symtab->filename);
+
+ if (current_source_symtab->line_charpos == 0)
+ find_source_lines (current_source_symtab, desc);
+
+ if (line < 1 || line > current_source_symtab->nlines)
+ {
+ close (desc);
+ error ("Expression not found");
+ }
+
+ if (lseek (desc, current_source_symtab->line_charpos[line - 1], 0) < 0)
+ {
+ close (desc);
+ perror_with_name (current_source_symtab->filename);
+ }
+
+ stream = fdopen (desc, "r");
+ clearerr (stream);
+ while (1) {
+ char buf[4096]; /* Should be reasonable??? */
+ register char *p = buf;
+
+ c = fgetc (stream);
+ if (c == EOF)
+ break;
+ do {
+ *p++ = c;
+ } while (c != '\n' && (c = fgetc (stream)) >= 0);
+
+ /* we now have a source line in buf, null terminate and match */
+ *p = 0;
+ if (re_exec (buf) > 0)
+ {
+ /* Match! */
+ fclose (stream);
+ print_source_lines (current_source_symtab,
+ line, line+1, 0);
+ current_source_line = max (line - 5, 1);
+ return;
+ }
+ line++;
+ }
+
+ printf ("Expression not found\n");
+ fclose (stream);
+}
+
+static void
+reverse_search_command (regex, from_tty)
+ char *regex;
+{
+ register int c;
+ register int desc;
+ register FILE *stream;
+ int line = last_line_listed - 1;
+ char *msg;
+
+ msg = (char *) re_comp (regex);
+ if (msg)
+ error (msg);
+
+ if (current_source_symtab == 0)
+ select_source_symtab (0);
+
+ /* Search from last_line_listed-1 in current_source_symtab */
+
+ desc = openp (source_path, 0, current_source_symtab->filename,
+ O_RDONLY, 0, &current_source_symtab->fullname);
+ if (desc < 0)
+ perror_with_name (current_source_symtab->filename);
+
+ if (current_source_symtab->line_charpos == 0)
+ find_source_lines (current_source_symtab, desc);
+
+ if (line < 1 || line > current_source_symtab->nlines)
+ {
+ close (desc);
+ error ("Expression not found");
+ }
+
+ if (lseek (desc, current_source_symtab->line_charpos[line - 1], 0) < 0)
+ {
+ close (desc);
+ perror_with_name (current_source_symtab->filename);
+ }
+
+ stream = fdopen (desc, "r");
+ clearerr (stream);
+ while (1)
+ {
+ char buf[4096]; /* Should be reasonable??? */
+ register char *p = buf;
+
+ c = fgetc (stream);
+ if (c == EOF)
+ break;
+ do {
+ *p++ = c;
+ } while (c != '\n' && (c = fgetc (stream)) >= 0);
+
+ /* We now have a source line in buf; null terminate and match. */
+ *p = 0;
+ if (re_exec (buf) > 0)
+ {
+ /* Match! */
+ fclose (stream);
+ print_source_lines (current_source_symtab,
+ line, line+1, 0);
+ current_source_line = max (line - 5, 1);
+ return;
+ }
+ line--;
+ if (fseek (stream, current_source_symtab->line_charpos[line - 1], 0) < 0)
+ {
+ fclose (stream);
+ perror_with_name (current_source_symtab->filename);
+ }
+ }
+
+ printf ("Expression not found\n");
+ fclose (stream);
+ return;
+}
+
+void
+_initialize_source ()
+{
+ current_source_symtab = 0;
+ init_source_path ();
+
+ add_com ("directory", class_files, directory_command,
+ "Add directory DIR to end of search path for source files.\n\
+With no argument, reset the search path to just the working directory\n\
+and forget cached info on line positions in source files.");
+
+ add_info ("directories", directories_info,
+ "Current search path for finding source files.");
+
+ add_info ("line", line_info,
+ "Core addresses of the code for a source line.\n\
+Line can be specified as\n\
+ LINENUM, to list around that line in current file,\n\
+ FILE:LINENUM, to list around that line in that file,\n\
+ FUNCTION, to list around beginning of that function,\n\
+ FILE:FUNCTION, to distinguish among like-named static functions.\n\
+Default is to describe the last source line that was listed.\n\n\
+This sets the default address for \"x\" to the line's first instruction\n\
+so that \"x/i\" suffices to start examining the machine code.\n\
+The address is also stored as the value of \"$_\".");
+
+ add_com ("forward-search", class_files, forward_search_command,
+ "Search for regular expression (see regex(3)) from last line listed.");
+ add_com_alias ("search", "forward-search", class_files, 0);
+
+ add_com ("reverse-search", class_files, reverse_search_command,
+ "Search backward for regular expression (see regex(3)) from last line listed.");
+
+ add_com ("list", class_files, list_command,
+ "List specified function or line.\n\
+With no argument, lists ten more lines after or around previous listing.\n\
+\"list -\" lists the ten lines before a previous ten-line listing.\n\
+One argument specifies a line, and ten lines are listed around that line.\n\
+Two arguments with comma between specify starting and ending lines to list.\n\
+Lines can be specified in these ways:\n\
+ LINENUM, to list around that line in current file,\n\
+ FILE:LINENUM, to list around that line in that file,\n\
+ FUNCTION, to list around beginning of that function,\n\
+ FILE:FUNCTION, to distinguish among like-named static functions.\n\
+ *ADDRESS, to list around the line containing that address.\n\
+With two args if one is empty it stands for ten lines away from the other arg.");
+ add_com ("file", class_files, file_command,
+ "Select current file, function and line for display or list.\n\
+Specification can have the form:\n\
+ LINENUM, to select that line in current file,\n\
+ FILE:LINENUM, to select that line in that file,\n\
+ FUNCTION, to select beginning of that function,\n\
+ FILE:FUNCTION, to distinguish among like-named static functions.\n\
+ *ADDRESS, to select the line containing that address.");
+ add_com ("push-to-file", class_files, push_to_file_command,
+ "Like \"file\" command but remembers current file & line on a stack.\n\
+Can later return to current file with \"pop-file\" command.\n\
+Up to 32 file positions can be pushed on stack.");
+ add_com ("pop-file", class_files, pop_file_command,
+ "Pops back to file position saved by most recent \"push-to-file\".\n\
+If everything has been popped from stack, command does nothing.");
+}
+
diff --git a/gnu/usr.bin/gdb/stab.def b/gnu/usr.bin/gdb/stab.def
new file mode 100644
index 0000000..b81cda4
--- /dev/null
+++ b/gnu/usr.bin/gdb/stab.def
@@ -0,0 +1,115 @@
+/* Table of DBX symbol codes for the GNU system.
+ Copyright (C) 1988 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Global variable. Only the name is significant.
+ To find the address, look in the corresponding external symbol. */
+__define_stab (N_GSYM, 0x20, "GSYM")
+
+/* Function name for BSD Fortran. Only the name is significant.
+ To find the address, look in the corresponding external symbol. */
+__define_stab (N_FNAME, 0x22, "FNAME")
+
+/* Function name or text-segment variable for C. Value is its address.
+ Desc is supposedly starting line number, but GCC doesn't set it
+ and DBX seems not to miss it. */
+__define_stab (N_FUN, 0x24, "FUN")
+
+/* Data-segment variable with internal linkage. Value is its address. */
+__define_stab (N_STSYM, 0x26, "STSYM")
+
+/* BSS-segment variable with internal linkage. Value is its address. */
+__define_stab (N_LCSYM, 0x28, "LCSYM")
+
+/* Name of main routine. Only the name is significant.
+ This is not used in C. */
+__define_stab (N_MAIN, 0x2a, "MAIN")
+
+/* Register variable. Value is number of register. */
+__define_stab (N_RSYM, 0x40, "RSYM")
+
+/* Structure or union element. Value is offset in the structure. */
+__define_stab (N_SSYM, 0x60, "SSYM")
+
+/* Parameter variable. Value is offset from argument pointer.
+ (On most machines the argument pointer is the same as the frame pointer. */
+__define_stab (N_PSYM, 0xa0, "PSYM")
+
+/* Automatic variable in the stack. Value is offset from frame pointer.
+ Also used for type descriptions. */
+__define_stab (N_LSYM, 0x80, "LSYM")
+
+/* Alternate entry point. Value is its address. */
+__define_stab (N_ENTRY, 0xa4, "ENTRY")
+
+/* Name of main source file.
+ Value is starting text address of the compilation. */
+__define_stab (N_SO, 0x64, "SO")
+
+/* Name of sub-source file.
+ Value is starting text address of the compilation. */
+__define_stab (N_SOL, 0x84, "SOL")
+
+/* Line number in text segment. Desc is the line number;
+ value is corresponding address. */
+__define_stab (N_SLINE, 0x44, "SLINE")
+/* Similar, for data segment. */
+__define_stab (N_DSLINE, 0x46, "DSLINE")
+/* Similar, for bss segment. */
+__define_stab (N_BSLINE, 0x48, "BSLINE")
+
+/* Beginning of an include file. Only Sun uses this.
+ In an object file, only the name is significant.
+ The Sun linker puts data into some of the other fields. */
+__define_stab (N_BINCL, 0x82, "BINCL")
+/* End of an include file. No name.
+ These two act as brackets around the file's output.
+ In an object file, there is no significant data in this entry.
+ The Sun linker puts data into some of the fields. */
+__define_stab (N_EINCL, 0xa2, "EINCL")
+/* Place holder for deleted include file.
+ This appears only in output from the Sun linker. */
+__define_stab (N_EXCL, 0xc2, "EXCL")
+
+/* Beginning of lexical block.
+ The desc is the nesting level in lexical blocks.
+ The value is the address of the start of the text for the block.
+ The variables declared inside the block *precede* the N_LBRAC symbol. */
+__define_stab (N_LBRAC, 0xc0, "LBRAC")
+/* End of a lexical block. Desc matches the N_LBRAC's desc.
+ The value is the address of the end of the text for the block. */
+__define_stab (N_RBRAC, 0xe0, "RBRAC")
+
+/* Begin named common block. Only the name is significant. */
+__define_stab (N_BCOMM, 0xe2, "BCOMM")
+/* Begin named common block. Only the name is significant
+ (and it should match the N_BCOMM). */
+__define_stab (N_ECOMM, 0xe4, "ECOMM")
+/* End common (local name): value is address.
+ I'm not sure how this is used. */
+__define_stab (N_ECOML, 0xe8, "ECOML")
+/* Second symbol entry containing a length-value for the preceding entry.
+ The value is the length. */
+__define_stab (N_LENG, 0xfe, "LENG")
+
+/* Global symbol in Pascal.
+ Supposedly the value is its line number; I'm skeptical. */
+__define_stab (N_PC, 0x30, "PC")
+
+/* Modula-2 compilation unit. Can someone say what info it contains? */
+__define_stab (N_M2C, 0x42, "M2C")
+/* Modula-2 scope information. Can someone say what info it contains? */
+__define_stab (N_SCOPE, 0xc4, "SCOPE")
diff --git a/gnu/usr.bin/gdb/stack.c b/gnu/usr.bin/gdb/stack.c
new file mode 100644
index 0000000..91218aa
--- /dev/null
+++ b/gnu/usr.bin/gdb/stack.c
@@ -0,0 +1,960 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)stack.c 6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Print and select stack frames for GDB, the GNU debugger.
+ Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* modified by rjc Thu Nov 1 16:46:57 1990, fixed return_command so that
+ it can return values, it still has problems when running on pmax,
+ cannot write register 65 */
+
+#include <stdio.h>
+
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "frame.h"
+#include "value.h"
+
+
+/* Thie "selected" stack frame is used by default for local and arg access.
+ May be zero, for no selected frame. */
+
+FRAME selected_frame;
+
+/* Level of the selected frame:
+ 0 for innermost, 1 for its caller, ...
+ or -1 for frame specified by address with no defined level. */
+
+int selected_frame_level;
+
+/* Nonzero means print the full filename and linenumber
+ when a frame is printed, and do so in a format programs can parse. */
+
+int frame_file_full_name = 0;
+
+static void select_calling_frame ();
+
+void print_frame_info ();
+
+/* Print a stack frame briefly. FRAME should be the frame id
+ and LEVEL should be its level in the stack (or -1 for level not defined).
+ This prints the level, the function executing, the arguments,
+ and the file name and line number.
+ If the pc is not at the beginning of the source line,
+ the actual pc is printed at the beginning.
+
+ If SOURCE is 1, print the source line as well.
+ If SOURCE is -1, print ONLY the source line. */
+
+static void
+print_stack_frame (frame, level, source)
+ FRAME frame;
+ int level;
+ int source;
+{
+ struct frame_info *fi;
+
+ fi = get_frame_info (frame);
+
+ print_frame_info (fi, level, source, 1);
+}
+
+/* Flag which will indicate when the frame has been changed
+ by and "up" or "down" command. */
+static int frame_changed;
+
+void
+print_frame_info (fi, level, source, args)
+ struct frame_info *fi;
+ register int level;
+ int source;
+ int args;
+{
+ struct symtab_and_line sal;
+ struct symbol *func;
+ register char *funname = 0;
+ int numargs;
+ struct partial_symtab *pst;
+
+ /* Don't give very much information if we haven't readin the
+ symbol table yet. */
+ pst = find_pc_psymtab (fi->pc);
+ if (pst && !pst->readin)
+ {
+ /* Abbreviated information. */
+ char *fname;
+
+ if (!find_pc_partial_function (fi->pc, &fname, 0))
+ fname = "??";
+
+ printf_filtered ("#%-2d ", level);
+ printf_filtered ("0x%x in ", fi->pc);
+
+ fputs_demangled(fname, stdout, -1);
+ fputs_filtered(" (...)\n", stdout);
+
+ return;
+ }
+
+ sal = find_pc_line (fi->pc, fi->next_frame);
+ func = find_pc_function (fi->pc);
+ if (func)
+ {
+ /* In certain pathological cases, the symtabs give the wrong
+ function (when we are in the first function in a file which
+ is compiled without debugging symbols, the previous function
+ is compiled with debugging symbols, and the "foo.o" symbol
+ that is supposed to tell us where the file with debugging symbols
+ ends has been truncated by ar because it is longer than 15
+ characters).
+
+ So look in the misc_function_vector as well, and if it comes
+ up with a larger address for the function use that instead.
+ I don't think this can ever cause any problems;
+ there shouldn't be any
+ misc_function_vector symbols in the middle of a function. */
+ int misc_index = find_pc_misc_function (fi->pc);
+ if (misc_index >= 0
+ && (misc_function_vector[misc_index].address
+ > BLOCK_START (SYMBOL_BLOCK_VALUE (func))))
+ {
+ /* In this case we have no way of knowing the source file
+ and line number, so don't print them. */
+ sal.symtab = 0;
+ /* We also don't know anything about the function besides
+ its address and name. */
+ func = 0;
+ funname = misc_function_vector[misc_index].name;
+ }
+ else
+ funname = SYMBOL_NAME (func);
+ }
+ else
+ {
+ register int misc_index = find_pc_misc_function (fi->pc);
+ if (misc_index >= 0)
+ funname = misc_function_vector[misc_index].name;
+ }
+
+ if (frame_changed || source >= 0 || !sal.symtab)
+ {
+ if (level >= 0)
+ printf_filtered ("#%-2d ", level);
+ else if (frame_changed)
+ printf ("#%-2d ", 0);
+ if (fi->pc != sal.pc || !sal.symtab)
+ printf_filtered ("0x%x in ", fi->pc);
+ fputs_demangled(funname ? funname : "??", stdout, -1);
+ printf_filtered(" (");
+ if (args)
+ {
+ if (func)
+ numargs = -1;
+ else
+ FRAME_NUM_ARGS (numargs, fi);
+
+ print_frame_args (func, fi, numargs, stdout);
+ }
+ printf_filtered (")");
+ if (sal.symtab)
+ printf_filtered (" (%s line %d)", sal.symtab->filename, sal.line);
+ printf_filtered ("\n");
+ }
+
+ if ((frame_changed || source != 0) && sal.symtab)
+ {
+ int done = 0;
+ int mid_statement = source < 0 && fi->pc != sal.pc;
+ if (frame_file_full_name)
+ done = identify_source_line (sal.symtab, sal.line, mid_statement);
+ if (!done)
+ {
+ if (mid_statement)
+ printf_filtered ("0x%x\t", fi->pc);
+ print_source_lines (sal.symtab, sal.line, sal.line + 1, 1);
+ }
+ current_source_line = max (sal.line - 5, 1);
+ }
+ frame_changed = 0;
+ if (source != 0)
+ set_default_breakpoint (1, fi->pc, sal.symtab, sal.line);
+
+ fflush (stdout);
+}
+
+/* Call here to print info on selected frame, after a trap. */
+
+void
+print_sel_frame (just_source)
+ int just_source;
+{
+ print_stack_frame (selected_frame, -1, just_source ? -1 : 1);
+}
+
+/* Print info on the selected frame, including level number
+ but not source. */
+
+void
+print_selected_frame ()
+{
+ print_stack_frame (selected_frame, selected_frame_level, 0);
+}
+
+void flush_cached_frames ();
+
+#ifdef FRAME_SPECIFICATION_DYADIC
+extern FRAME setup_arbitrary_frame ();
+#endif
+
+/*
+ * Read a frame specification in whatever the appropriate format is.
+ */
+static FRAME
+parse_frame_specification (frame_exp)
+ char *frame_exp;
+{
+ int numargs = 0;
+ int arg1, arg2;
+
+ if (frame_exp)
+ {
+ char *addr_string, *p;
+ struct cleanup *tmp_cleanup;
+ struct frame_info *fci;
+
+ while (*frame_exp == ' ') frame_exp++;
+ for (p = frame_exp; *p && *p != ' '; p++)
+ ;
+
+ if (*frame_exp)
+ {
+ numargs = 1;
+ addr_string = savestring(frame_exp, p - frame_exp);
+
+ {
+ tmp_cleanup = make_cleanup (free, addr_string);
+ arg1 = parse_and_eval_address (addr_string);
+ do_cleanups (tmp_cleanup);
+ }
+
+ while (*p == ' ') p++;
+
+ if (*p)
+ {
+ numargs = 2;
+ arg2 = parse_and_eval_address (p);
+ }
+ }
+ }
+
+ switch (numargs)
+ {
+ case 0:
+ return selected_frame;
+ /* NOTREACHED */
+ case 1:
+ {
+ int level = arg1;
+ FRAME fid = find_relative_frame (get_current_frame (), &level);
+ FRAME tfid;
+
+ if (level == 0)
+ /* find_relative_frame was successful */
+ return fid;
+
+ /* If (s)he specifies the frame with an address, he deserves what
+ (s)he gets. Still, give the highest one that matches. */
+
+ for (fid = get_current_frame ();
+ fid && FRAME_FP (fid) != arg1;
+ fid = get_prev_frame (fid))
+ ;
+
+ if (fid)
+ while ((tfid = get_prev_frame (fid)) &&
+ (FRAME_FP (tfid) == arg1))
+ fid = tfid;
+
+#ifdef FRAME_SPECIFICATION_DYADIC
+ if (!fid)
+ error ("Incorrect number of args in frame specification");
+
+ return fid;
+#else
+ return create_new_frame (arg1, 0);
+#endif
+ }
+ /* NOTREACHED */
+ case 2:
+ /* Must be addresses */
+#ifndef FRAME_SPECIFICATION_DYADIC
+ error ("Incorrect number of args in frame specification");
+#else
+ return setup_arbitrary_frame (arg1, arg2);
+#endif
+ /* NOTREACHED */
+ }
+ fatal ("Internal: Error in parsing in parse_frame_specification");
+ /* NOTREACHED */
+}
+
+/* FRAME_ARGS_ADDRESS_CORRECT is just like FRAME_ARGS_ADDRESS except
+ that if it is unsure about the answer, it returns Frame_unknown
+ instead of guessing (this happens on the VAX, for example).
+
+ On most machines, we never have to guess about the args address,
+ so FRAME_ARGS_ADDRESS{,_CORRECT} are the same. */
+#if !defined (FRAME_ARGS_ADDRESS_CORRECT)
+#define FRAME_ARGS_ADDRESS_CORRECT FRAME_ARGS_ADDRESS
+#endif
+
+/* Print verbosely the selected frame or the frame at address ADDR.
+ This means absolutely all information in the frame is printed. */
+
+static void
+frame_info (addr_exp)
+ char *addr_exp;
+{
+ FRAME frame;
+ struct frame_info *fi;
+ struct frame_saved_regs fsr;
+ struct symtab_and_line sal;
+ struct symbol *func;
+ FRAME calling_frame;
+ int i, count;
+ char *funname = 0;
+
+ if (!(have_inferior_p () || have_core_file_p ()))
+ error ("No inferior or core file.");
+
+ frame = parse_frame_specification (addr_exp);
+ if (!frame)
+ error ("Invalid frame specified.");
+
+ fi = get_frame_info (frame);
+ get_frame_saved_regs (fi, &fsr);
+ sal = find_pc_line (fi->pc, fi->next_frame);
+ func = get_frame_function (frame);
+ if (func)
+ funname = SYMBOL_NAME (func);
+ else
+ {
+ register int misc_index = find_pc_misc_function (fi->pc);
+ if (misc_index >= 0)
+ funname = misc_function_vector[misc_index].name;
+ }
+ calling_frame = get_prev_frame (frame);
+
+ if (!addr_exp && selected_frame_level >= 0)
+ printf ("Stack level %d, frame at 0x%x:\n pc = 0x%x",
+ selected_frame_level, FRAME_FP(frame), fi->pc);
+ else
+ printf ("Stack frame at 0x%x:\n pc = 0x%x",
+ FRAME_FP(frame), fi->pc);
+
+ if (funname)
+ printf (" in %s", funname);
+ if (sal.symtab)
+ printf (" (%s line %d)", sal.symtab->filename, sal.line);
+ printf ("; saved pc 0x%x\n", FRAME_SAVED_PC (frame));
+ if (calling_frame)
+ printf (" called by frame at 0x%x", FRAME_FP (calling_frame));
+ if (fi->next_frame && calling_frame)
+ printf (",");
+ if (fi->next_frame)
+ printf (" caller of frame at 0x%x", fi->next_frame);
+ if (fi->next_frame || calling_frame)
+ printf ("\n");
+
+ {
+ /* Address of the argument list for this frame, or Frame_unknown. */
+ CORE_ADDR arg_list = FRAME_ARGS_ADDRESS_CORRECT (fi);
+ /* Number of args for this frame, or -1 if unknown. */
+ int numargs;
+
+ if (arg_list != Frame_unknown)
+ {
+ printf (" Arglist at 0x%x,", arg_list);
+
+ FRAME_NUM_ARGS (numargs, fi);
+ if (numargs < 0)
+ printf (" args: ");
+ else if (numargs == 0)
+ printf (" no args.");
+ else if (numargs == 1)
+ printf (" 1 arg: ");
+ else
+ printf (" %d args: ", numargs);
+ print_frame_args (func, fi, numargs, stdout);
+ printf ("\n");
+ }
+ }
+
+ /* The sp is special; what's returned isn't the save address, but
+ actually the value of the previous frame's sp. */
+ printf (" Previous frame's sp is 0x%x\n", fsr.regs[SP_REGNUM]);
+ count = 0;
+ for (i = 0; i < NUM_REGS; i++)
+ if (fsr.regs[i] && i != SP_REGNUM)
+ {
+ if (count % 4 != 0)
+ printf (", ");
+ else
+ {
+ if (count == 0)
+ printf (" Saved registers:");
+ printf ("\n ");
+ }
+ printf ("%s at 0x%x", reg_names[i], fsr.regs[i]);
+ count++;
+ }
+ if (count)
+ printf ("\n");
+}
+
+#if 0
+/* Set a limit on the number of frames printed by default in a
+ backtrace. */
+
+static int backtrace_limit;
+
+static void
+set_backtrace_limit_command (count_exp, from_tty)
+ char *count_exp;
+ int from_tty;
+{
+ int count = parse_and_eval_address (count_exp);
+
+ if (count < 0)
+ error ("Negative argument not meaningful as backtrace limit.");
+
+ backtrace_limit = count;
+}
+
+static void
+backtrace_limit_info (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ if (arg)
+ error ("\"Info backtrace-limit\" takes no arguments.");
+
+ printf ("Backtrace limit: %d.\n", backtrace_limit);
+}
+#endif
+
+/* Print briefly all stack frames or just the innermost COUNT frames. */
+
+static void
+backtrace_command (count_exp)
+ char *count_exp;
+{
+ struct frame_info *fi;
+ register int count;
+ register FRAME frame;
+ register int i;
+ register FRAME trailing;
+ register int trailing_level;
+
+ /* The following code must do two things. First, it must
+ set the variable TRAILING to the frame from which we should start
+ printing. Second, it must set the variable count to the number
+ of frames which we should print, or -1 if all of them. */
+ trailing = get_current_frame ();
+ trailing_level = 0;
+ if (count_exp)
+ {
+ count = parse_and_eval_address (count_exp);
+ if (count < 0)
+ {
+ FRAME current;
+
+ count = -count;
+
+ current = trailing;
+ while (current && count--)
+ current = get_prev_frame (current);
+
+ /* Will stop when CURRENT reaches the top of the stack. TRAILING
+ will be COUNT below it. */
+ while (current)
+ {
+ trailing = get_prev_frame (trailing);
+ current = get_prev_frame (current);
+ trailing_level++;
+ }
+
+ count = -1;
+ }
+ }
+ else
+ count = -1;
+
+ for (i = 0, frame = trailing;
+ frame && count--;
+ i++, frame = get_prev_frame (frame))
+ {
+ QUIT;
+ fi = get_frame_info (frame);
+ print_frame_info (fi, trailing_level + i, 0, 1);
+ }
+
+ /* If we've stopped before the end, mention that. */
+ if (frame)
+ printf_filtered ("(More stack frames follow...)\n");
+}
+
+/* Print the local variables of a block B active in FRAME.
+ Return 1 if any variables were printed; 0 otherwise. */
+
+static int
+print_block_frame_locals (b, frame, stream)
+ struct block *b;
+ register FRAME frame;
+ register FILE *stream;
+{
+ int nsyms;
+ register int i;
+ register struct symbol *sym;
+ register int values_printed = 0;
+
+ nsyms = BLOCK_NSYMS (b);
+
+ for (i = 0; i < nsyms; i++)
+ {
+ sym = BLOCK_SYM (b, i);
+ if (SYMBOL_CLASS (sym) == LOC_LOCAL
+ || SYMBOL_CLASS (sym) == LOC_REGISTER
+ || SYMBOL_CLASS (sym) == LOC_STATIC)
+ {
+ values_printed = 1;
+ fputs_filtered (SYMBOL_NAME (sym), stream);
+ fputs_filtered (" = ", stream);
+ print_variable_value (sym, frame, stream);
+ fprintf_filtered (stream, "\n");
+ fflush (stream);
+ }
+ }
+ return values_printed;
+}
+
+/* Print on STREAM all the local variables in frame FRAME,
+ including all the blocks active in that frame
+ at its current pc.
+
+ Returns 1 if the job was done,
+ or 0 if nothing was printed because we have no info
+ on the function running in FRAME. */
+
+static int
+print_frame_local_vars (frame, stream)
+ register FRAME frame;
+ register FILE *stream;
+{
+ register struct block *block = get_frame_block (frame);
+ register int values_printed = 0;
+
+ if (block == 0)
+ {
+ fprintf_filtered (stream, "No symbol table info available.\n");
+ fflush (stream);
+ return 0;
+ }
+
+ while (block != 0)
+ {
+ if (print_block_frame_locals (block, frame, stream))
+ values_printed = 1;
+ /* After handling the function's top-level block, stop.
+ Don't continue to its superblock, the block of
+ per-file symbols. */
+ if (BLOCK_FUNCTION (block))
+ break;
+ block = BLOCK_SUPERBLOCK (block);
+ }
+
+ if (!values_printed)
+ {
+ fprintf_filtered (stream, "No locals.\n");
+ fflush (stream);
+ }
+
+ return 1;
+}
+
+static void
+locals_info ()
+{
+ if (!have_inferior_p () && !have_core_file_p ())
+ error ("No inferior or core file.");
+
+ print_frame_local_vars (selected_frame, stdout);
+}
+
+static int
+print_frame_arg_vars (frame, stream)
+ register FRAME frame;
+ register FILE *stream;
+{
+ struct symbol *func = get_frame_function (frame);
+ register struct block *b;
+ int nsyms;
+ register int i;
+ register struct symbol *sym;
+ register int values_printed = 0;
+
+ if (func == 0)
+ {
+ fprintf_filtered (stream, "No symbol table info available.\n");
+ fflush (stream);
+ return 0;
+ }
+
+ b = SYMBOL_BLOCK_VALUE (func);
+ nsyms = BLOCK_NSYMS (b);
+
+ for (i = 0; i < nsyms; i++)
+ {
+ sym = BLOCK_SYM (b, i);
+ if (SYMBOL_CLASS (sym) == LOC_ARG
+ || SYMBOL_CLASS (sym) == LOC_REF_ARG
+ || SYMBOL_CLASS (sym) == LOC_REGPARM)
+ {
+ values_printed = 1;
+ fputs_filtered (SYMBOL_NAME (sym), stream);
+ fputs_filtered (" = ", stream);
+ print_variable_value (sym, frame, stream);
+ fprintf_filtered (stream, "\n");
+ fflush (stream);
+ }
+ }
+
+ if (!values_printed)
+ {
+ fprintf_filtered (stream, "No arguments.\n");
+ fflush (stream);
+ }
+
+ return 1;
+}
+
+static void
+args_info ()
+{
+ if (!have_inferior_p () && !have_core_file_p ())
+ error ("No inferior or core file.");
+ print_frame_arg_vars (selected_frame, stdout);
+}
+
+/* Select frame FRAME, and note that its stack level is LEVEL.
+ LEVEL may be -1 if an actual level number is not known. */
+
+void
+select_frame (frame, level)
+ FRAME frame;
+ int level;
+{
+ selected_frame = frame;
+ selected_frame_level = level;
+ /* Ensure that symbols for this frame are readin. */
+ if (frame)
+ find_pc_symtab (get_frame_info (frame)->pc);
+}
+
+/* Store the selected frame and its level into *FRAMEP and *LEVELP. */
+
+void
+record_selected_frame (frameaddrp, levelp)
+ FRAME_ADDR *frameaddrp;
+ int *levelp;
+{
+ *frameaddrp = FRAME_FP (selected_frame);
+ *levelp = selected_frame_level;
+}
+
+/* Return the symbol-block in which the selected frame is executing.
+ Can return zero under various legitimate circumstances. */
+
+struct block *
+get_selected_block ()
+{
+ if (!have_inferior_p () && !have_core_file_p ())
+ return 0;
+
+ if (!selected_frame)
+ return get_current_block ();
+ return get_frame_block (selected_frame);
+}
+
+/* Find a frame a certain number of levels away from FRAME.
+ LEVEL_OFFSET_PTR points to an int containing the number of levels.
+ Positive means go to earlier frames (up); negative, the reverse.
+ The int that contains the number of levels is counted toward
+ zero as the frames for those levels are found.
+ If the top or bottom frame is reached, that frame is returned,
+ but the final value of *LEVEL_OFFSET_PTR is nonzero and indicates
+ how much farther the original request asked to go. */
+
+FRAME
+find_relative_frame (frame, level_offset_ptr)
+ register FRAME frame;
+ register int* level_offset_ptr;
+{
+ register FRAME prev;
+ register FRAME frame1, frame2;
+
+ /* Going up is simple: just do get_prev_frame enough times
+ or until initial frame is reached. */
+ while (*level_offset_ptr > 0)
+ {
+ prev = get_prev_frame (frame);
+ if (prev == 0)
+ break;
+ (*level_offset_ptr)--;
+ frame = prev;
+ }
+ /* Going down could be done by iterating get_frame_info to
+ find the next frame, but that would be quadratic
+ since get_frame_info must scan all the way from the current frame.
+ The following algorithm is linear. */
+ if (*level_offset_ptr < 0)
+ {
+ /* First put frame1 at innermost frame
+ and frame2 N levels up from there. */
+ frame1 = get_current_frame ();
+ frame2 = frame1;
+ while (*level_offset_ptr < 0 && frame2 != frame)
+ {
+ frame2 = get_prev_frame (frame2);
+ (*level_offset_ptr) ++;
+ }
+ /* Then slide frame1 and frame2 up in synchrony
+ and when frame2 reaches our starting point
+ frame1 must be N levels down from there. */
+ while (frame2 != frame)
+ {
+ frame1 = get_prev_frame (frame1);
+ frame2 = get_prev_frame (frame2);
+ }
+ return frame1;
+ }
+ return frame;
+}
+
+/* The "frame" command. With no arg, print selected frame briefly.
+ With arg LEVEL_EXP, select the frame at level LEVEL if it is a
+ valid level. Otherwise, treat level_exp as an address expression
+ and print it. See parse_frame_specification for more info on proper
+ frame expressions. */
+
+static void
+frame_command (level_exp, from_tty)
+ char *level_exp;
+ int from_tty;
+{
+ register FRAME frame, frame1;
+ unsigned int level = 0;
+
+ if (!have_inferior_p () && ! have_core_file_p ())
+ error ("No inferior or core file.");
+
+ frame = parse_frame_specification (level_exp);
+
+ for (frame1 = get_prev_frame (0);
+ frame1 && frame1 != frame;
+ frame1 = get_prev_frame (frame1))
+ level++;
+
+ if (!frame1)
+ level = 0;
+
+ frame_changed = level;
+ select_frame (frame, level);
+
+ if (!from_tty)
+ return;
+
+ print_stack_frame (selected_frame, selected_frame_level, 1);
+}
+
+/* Select the frame up one or COUNT stack levels
+ from the previously selected frame, and print it briefly. */
+
+static void
+up_command (count_exp)
+ char *count_exp;
+{
+ register FRAME frame;
+ int count = 1, count1;
+ if (count_exp)
+ count = parse_and_eval_address (count_exp);
+ count1 = count;
+
+ if (!have_inferior_p () && !have_core_file_p ())
+ error ("No inferior or core file.");
+
+ frame = find_relative_frame (selected_frame, &count1);
+ if (count1 != 0 && count_exp == 0)
+ error ("Initial frame selected; you cannot go up.");
+ select_frame (frame, selected_frame_level + count - count1);
+
+ print_stack_frame (selected_frame, selected_frame_level, 1);
+ frame_changed++;
+}
+
+/* Select the frame down one or COUNT stack levels
+ from the previously selected frame, and print it briefly. */
+
+static void
+down_command (count_exp)
+ char *count_exp;
+{
+ register FRAME frame;
+ int count = -1, count1;
+ if (count_exp)
+ count = - parse_and_eval_address (count_exp);
+ count1 = count;
+
+ frame = find_relative_frame (selected_frame, &count1);
+ if (count1 != 0 && count_exp == 0)
+ error ("Bottom (i.e., innermost) frame selected; you cannot go down.");
+ select_frame (frame, selected_frame_level + count - count1);
+
+ print_stack_frame (selected_frame, selected_frame_level, 1);
+ frame_changed--;
+}
+
+static void
+return_command (retval_exp, from_tty)
+ char *retval_exp;
+ int from_tty;
+{
+ value return_value;
+ struct symbol *thisfun = get_frame_function (selected_frame);
+ FRAME_ADDR selected_frame_addr = FRAME_FP (selected_frame);
+
+ /* If interactive, require confirmation. */
+
+ if (from_tty)
+ {
+ if (thisfun != 0)
+ {
+ if (!query ("Make %s return now? ", SYMBOL_NAME (thisfun)))
+ error ("Not confirmed.");
+ }
+ else
+ if (!query ("Make selected stack frame return now? "))
+ error ("Not confirmed.");
+ }
+
+ /* Do the real work. Pop until the specified frame is current. We
+ use this method because the selected_frame is not valid after
+ a POP_FRAME. Note that this will not work if the selected frame
+ shares it's fp with another frame. */
+
+ while (selected_frame_addr != FRAME_FP (get_current_frame()))
+ POP_FRAME;
+
+ /* get the return value while still in this frame */
+ if (retval_exp)
+ return_value = parse_and_eval (retval_exp);
+
+ /* Then pop that frame. */
+ POP_FRAME;
+
+ /* Store the return value if there was one */
+
+ if (retval_exp)
+ set_return_value (return_value);
+
+ /* If interactive, print the frame that is now current. */
+
+ if (from_tty)
+ frame_command ("0", 1);
+}
+
+extern struct cmd_list_element *setlist;
+
+void
+_initialize_stack ()
+{
+#if 0
+ backtrace_limit = 30;
+#endif
+
+ add_com ("return", class_stack, return_command,
+ "Make selected stack frame return to its caller.\n\
+Control remains in the debugger, but when you continue\n\
+execution will resume in the frame above the one now selected.\n\
+If an argument is given, it is an expression for the value to return.");
+
+ add_com ("up", class_stack, up_command,
+ "Select and print stack frame that called this one.\n\
+An argument says how many frames up to go.");
+
+ add_com ("down", class_stack, down_command,
+ "Select and print stack frame called by this one.\n\
+An argument says how many frames down to go.");
+ add_com_alias ("do", "down", class_stack, 1);
+
+ add_com ("frame", class_stack, frame_command,
+ "Select and print a stack frame.\n\
+With no argument, print the selected stack frame. (See also \"info frame\").\n\
+An argument specifies the frame to select.\n\
+It can be a stack frame number or the address of the frame.\n\
+With argument, nothing is printed if input is coming from\n\
+a command file or a user-defined command.");
+
+ add_com_alias ("f", "frame", class_stack, 1);
+
+ add_com ("backtrace", class_stack, backtrace_command,
+ "Print backtrace of all stack frames, or innermost COUNT frames.\n\
+With a negative argument, print outermost -COUNT frames.");
+ add_com_alias ("bt", "backtrace", class_stack, 0);
+ add_com_alias ("where", "backtrace", class_alias, 0);
+ add_info ("stack", backtrace_command,
+ "Backtrace of the stack, or innermost COUNT frames.");
+ add_info_alias ("s", "stack", 1);
+ add_info ("frame", frame_info,
+ "All about selected stack frame, or frame at ADDR.");
+ add_info_alias ("f", "frame", 1);
+ add_info ("locals", locals_info,
+ "Local variables of current stack frame.");
+ add_info ("args", args_info,
+ "Argument variables of current stack frame.");
+
+#if 0
+ add_cmd ("backtrace-limit", class_stack, set_backtrace_limit_command,
+ "Specify maximum number of frames for \"backtrace\" to print by default.",
+ &setlist);
+ add_info ("backtrace-limit", backtrace_limit_info,
+ "The maximum number of frames for \"backtrace\" to print by default.");
+#endif
+}
+
diff --git a/gnu/usr.bin/gdb/symmisc.c b/gnu/usr.bin/gdb/symmisc.c
new file mode 100644
index 0000000..bb4eb50
--- /dev/null
+++ b/gnu/usr.bin/gdb/symmisc.c
@@ -0,0 +1,584 @@
+/* Do various things to symbol tables (other than lookup)), for GDB.
+ Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "defs.h"
+#include "symtab.h"
+
+#include <stdio.h>
+#include <obstack.h>
+
+static void free_symtab ();
+
+
+/* Free all the symtabs that are currently installed,
+ and all storage associated with them.
+ Leaves us in a consistent state with no symtabs installed. */
+
+void
+free_all_symtabs ()
+{
+ register struct symtab *s, *snext;
+
+ /* All values will be invalid because their types will be! */
+
+ clear_value_history ();
+ clear_displays ();
+ clear_internalvars ();
+ clear_breakpoints ();
+ set_default_breakpoint (0, 0, 0, 0);
+
+ current_source_symtab = 0;
+
+ for (s = symtab_list; s; s = snext)
+ {
+ snext = s->next;
+ free_symtab (s);
+ }
+ symtab_list = 0;
+ obstack_free (symbol_obstack, 0);
+ obstack_init (symbol_obstack);
+
+ if (misc_function_vector)
+ free (misc_function_vector);
+ misc_function_count = 0;
+ misc_function_vector = 0;
+}
+
+/* Free a struct block <- B and all the symbols defined in that block. */
+
+static void
+free_symtab_block (b)
+ struct block *b;
+{
+ register int i, n;
+ n = BLOCK_NSYMS (b);
+ for (i = 0; i < n; i++)
+ {
+ free (SYMBOL_NAME (BLOCK_SYM (b, i)));
+ free (BLOCK_SYM (b, i));
+ }
+ free (b);
+}
+
+/* Free all the storage associated with the struct symtab <- S.
+ Note that some symtabs have contents malloc'ed structure by structure,
+ while some have contents that all live inside one big block of memory,
+ and some share the contents of another symbol table and so you should
+ not free the contents on their behalf (except sometimes the linetable,
+ which maybe per symtab even when the rest is not).
+ It is s->free_code that says which alternative to use. */
+
+static void
+free_symtab (s)
+ register struct symtab *s;
+{
+ register int i, n;
+ register struct blockvector *bv;
+ register struct type *type;
+ register struct typevector *tv;
+
+ switch (s->free_code)
+ {
+ case free_nothing:
+ /* All the contents are part of a big block of memory
+ and some other symtab is in charge of freeing that block.
+ Therefore, do nothing. */
+ break;
+
+ case free_contents:
+ /* Here all the contents were malloc'ed structure by structure
+ and must be freed that way. */
+ /* First free the blocks (and their symbols. */
+ bv = BLOCKVECTOR (s);
+ n = BLOCKVECTOR_NBLOCKS (bv);
+ for (i = 0; i < n; i++)
+ free_symtab_block (BLOCKVECTOR_BLOCK (bv, i));
+ /* Free the blockvector itself. */
+ free (bv);
+ /* Free the type vector. */
+ tv = TYPEVECTOR (s);
+ free (tv);
+ /* Also free the linetable. */
+
+ case free_linetable:
+ /* Everything will be freed either by our `free_ptr'
+ or by some other symbatb, except for our linetable.
+ Free that now. */
+ free (LINETABLE (s));
+ break;
+ }
+
+ /* If there is a single block of memory to free, free it. */
+ if (s->free_ptr)
+ free (s->free_ptr);
+
+ if (s->line_charpos)
+ free (s->line_charpos);
+ free (s->filename);
+ free (s);
+}
+
+/* Convert a raw symbol-segment to a struct symtab,
+ and relocate its internal pointers so that it is valid. */
+
+/* This is how to relocate one pointer, given a name for it.
+ Works independent of the type of object pointed to. */
+#define RELOCATE(slot) (slot ? (* (char **) &slot += relocation) : 0)
+
+/* This is the inverse of RELOCATE. We use it when storing
+ a core address into a slot that has yet to be relocated. */
+#define UNRELOCATE(slot) (slot ? (* (char **) &slot -= relocation) : 0)
+
+/* During the process of relocation, this holds the amount to relocate by
+ (the address of the file's symtab data, in core in the debugger). */
+static int relocation;
+
+#define CORE_RELOCATE(slot) \
+ ((slot) += (((slot) < data_start) ? text_relocation \
+ : ((slot) < bss_start) ? data_relocation : bss_relocation))
+
+#define TEXT_RELOCATE(slot) ((slot) += text_relocation)
+
+/* Relocation amounts for addresses in the program's core image. */
+static int text_relocation, data_relocation, bss_relocation;
+
+/* Boundaries that divide program core addresses into text, data and bss;
+ used to determine which relocation amount to use. */
+static int data_start, bss_start;
+
+static void relocate_typevector ();
+static void relocate_blockvector ();
+static void relocate_type ();
+static void relocate_block ();
+static void relocate_symbol ();
+static void relocate_source ();
+
+/* Relocate a file's symseg so that all the pointers are valid C pointers.
+ Value is a `struct symtab'; but it is not suitable for direct
+ insertion into the `symtab_list' because it describes several files. */
+
+static struct symtab *
+relocate_symtab (root)
+ struct symbol_root *root;
+{
+ struct symtab *sp = (struct symtab *) xmalloc (sizeof (struct symtab));
+ bzero (sp, sizeof (struct symtab));
+
+ relocation = (int) root;
+ text_relocation = root->textrel;
+ data_relocation = root->datarel;
+ bss_relocation = root->bssrel;
+ data_start = root->databeg;
+ bss_start = root->bssbeg;
+
+ sp->filename = root->filename;
+ sp->ldsymoff = root->ldsymoff;
+ sp->language = root->language;
+ sp->compilation = root->compilation;
+ sp->version = root->version;
+ sp->blockvector = root->blockvector;
+ sp->typevector = root->typevector;
+
+ RELOCATE (TYPEVECTOR (sp));
+ RELOCATE (BLOCKVECTOR (sp));
+ RELOCATE (sp->version);
+ RELOCATE (sp->compilation);
+ RELOCATE (sp->filename);
+
+ relocate_typevector (TYPEVECTOR (sp));
+ relocate_blockvector (BLOCKVECTOR (sp));
+
+ return sp;
+}
+
+static void
+relocate_blockvector (blp)
+ register struct blockvector *blp;
+{
+ register int nblocks = BLOCKVECTOR_NBLOCKS (blp);
+ register int i;
+ for (i = 0; i < nblocks; i++)
+ RELOCATE (BLOCKVECTOR_BLOCK (blp, i));
+ for (i = 0; i < nblocks; i++)
+ relocate_block (BLOCKVECTOR_BLOCK (blp, i));
+}
+
+static void
+relocate_block (bp)
+ register struct block *bp;
+{
+ register int nsyms = BLOCK_NSYMS (bp);
+ register int i;
+
+ TEXT_RELOCATE (BLOCK_START (bp));
+ TEXT_RELOCATE (BLOCK_END (bp));
+
+ /* These two should not be recursively processed.
+ The superblock need not be because all blocks are
+ processed from relocate_blockvector.
+ The function need not be because it will be processed
+ under the block which is its scope. */
+ RELOCATE (BLOCK_SUPERBLOCK (bp));
+ RELOCATE (BLOCK_FUNCTION (bp));
+
+ for (i = 0; i < nsyms; i++)
+ RELOCATE (BLOCK_SYM (bp, i));
+
+ for (i = 0; i < nsyms; i++)
+ relocate_symbol (BLOCK_SYM (bp, i));
+}
+
+static void
+relocate_symbol (sp)
+ register struct symbol *sp;
+{
+ RELOCATE (SYMBOL_NAME (sp));
+ if (SYMBOL_CLASS (sp) == LOC_BLOCK)
+ {
+ RELOCATE (SYMBOL_BLOCK_VALUE (sp));
+ /* We can assume the block that belongs to this symbol
+ is not relocated yet, since it comes after
+ the block that contains this symbol. */
+ BLOCK_FUNCTION (SYMBOL_BLOCK_VALUE (sp)) = sp;
+ UNRELOCATE (BLOCK_FUNCTION (SYMBOL_BLOCK_VALUE (sp)));
+ }
+ else if (SYMBOL_CLASS (sp) == LOC_STATIC)
+ CORE_RELOCATE (SYMBOL_VALUE (sp));
+ else if (SYMBOL_CLASS (sp) == LOC_LABEL)
+ TEXT_RELOCATE (SYMBOL_VALUE (sp));
+ RELOCATE (SYMBOL_TYPE (sp));
+}
+
+static void
+relocate_typevector (tv)
+ struct typevector *tv;
+{
+ register int ntypes = TYPEVECTOR_NTYPES (tv);
+ register int i;
+
+ for (i = 0; i < ntypes; i++)
+ RELOCATE (TYPEVECTOR_TYPE (tv, i));
+ for (i = 0; i < ntypes; i++)
+ relocate_type (TYPEVECTOR_TYPE (tv, i));
+}
+
+/* We cannot come up with an a priori spanning tree
+ for the network of types, since types can be used
+ for many symbols and also as components of other types.
+ Therefore, we need to be able to mark types that we
+ already have relocated (or are already in the middle of relocating)
+ as in a garbage collector. */
+
+static void
+relocate_type (tp)
+ register struct type *tp;
+{
+ register int nfields = TYPE_NFIELDS (tp);
+ register int i;
+
+ RELOCATE (TYPE_NAME (tp));
+ RELOCATE (TYPE_TARGET_TYPE (tp));
+ RELOCATE (TYPE_FIELDS (tp));
+ RELOCATE (TYPE_POINTER_TYPE (tp));
+
+ for (i = 0; i < nfields; i++)
+ {
+ RELOCATE (TYPE_FIELD_TYPE (tp, i));
+ RELOCATE (TYPE_FIELD_NAME (tp, i));
+ }
+}
+
+static void
+relocate_sourcevector (svp)
+ register struct sourcevector *svp;
+{
+ register int nfiles = svp->length;
+ register int i;
+ for (i = 0; i < nfiles; i++)
+ RELOCATE (svp->source[i]);
+ for (i = 0; i < nfiles; i++)
+ relocate_source (svp->source[i]);
+}
+
+static void
+relocate_source (sp)
+ register struct source *sp;
+{
+ register int nitems = sp->contents.nitems;
+ register int i;
+
+ RELOCATE (sp->name);
+ for (i = 0; i < nitems; i++)
+ TEXT_RELOCATE (sp->contents.item[i].pc);
+}
+
+/* Read symsegs from file named NAME open on DESC,
+ make symtabs from them, and return a chain of them.
+ These symtabs are not suitable for direct use in `symtab_list'
+ because each one describes a single object file, perhaps many source files.
+ `symbol_file_command' takes each of these, makes many real symtabs
+ from it, and then frees it.
+
+ We assume DESC is prepositioned at the end of the string table,
+ just before the symsegs if there are any. */
+
+struct symtab *
+read_symsegs (desc, name)
+ int desc;
+ char *name;
+{
+ struct symbol_root root;
+ register char *data;
+ register struct symtab *sp, *sp1, *chain = 0;
+ register int len;
+
+ while (1)
+ {
+ len = myread (desc, &root, sizeof root);
+ if (len == 0 || root.format == 0)
+ break;
+ /* format 1 was ok for the original gdb, but since the size of the
+ type structure changed when C++ support was added, it can no
+ longer be used. Accept only format 2. */
+ if (root.format != 2 ||
+ root.length < sizeof root)
+ error ("\nInvalid symbol segment format code");
+ data = (char *) xmalloc (root.length);
+ bcopy (&root, data, sizeof root);
+ len = myread (desc, data + sizeof root,
+ root.length - sizeof root);
+ sp = relocate_symtab (data);
+ RELOCATE (((struct symbol_root *)data)->sourcevector);
+ relocate_sourcevector (((struct symbol_root *)data)->sourcevector);
+ sp->next = chain;
+ chain = sp;
+ sp->linetable = (struct linetable *) ((struct symbol_root *)data)->sourcevector;
+ }
+
+ return chain;
+}
+
+static int block_depth ();
+void print_spaces ();
+static void print_symbol ();
+
+void
+print_symtabs (filename)
+ char *filename;
+{
+ FILE *outfile;
+ register struct symtab *s;
+ register int i, j;
+ int len, line, blen;
+ register struct linetable *l;
+ struct blockvector *bv;
+ register struct block *b;
+ int depth;
+ struct cleanup *cleanups;
+ extern int fclose();
+
+ if (filename == 0)
+ error_no_arg ("file to write symbol data in");
+
+ filename = tilde_expand (filename);
+ make_cleanup (free, filename);
+
+ outfile = fopen (filename, "w");
+ if (outfile == 0)
+ perror_with_name (filename);
+
+ cleanups = make_cleanup (fclose, outfile);
+ immediate_quit++;
+
+ for (s = symtab_list; s; s = s->next)
+ {
+ /* First print the line table. */
+ fprintf (outfile, "Symtab for file %s\n\n", s->filename);
+ fprintf (outfile, "Line table:\n\n");
+ l = LINETABLE (s);
+ len = l->nitems;
+ for (i = 0; i < len; i++)
+ fprintf (outfile, " line %d at %x\n", l->item[i].line,
+ l->item[i].pc);
+ /* Now print the block info. */
+ fprintf (outfile, "\nBlockvector:\n\n");
+ bv = BLOCKVECTOR (s);
+ len = BLOCKVECTOR_NBLOCKS (bv);
+ for (i = 0; i < len; i++)
+ {
+ b = BLOCKVECTOR_BLOCK (bv, i);
+ depth = block_depth (b) * 2;
+ print_spaces (depth, outfile);
+ fprintf (outfile, "block #%03d (object 0x%x) ", i, b);
+ fprintf (outfile, "[0x%x..0x%x]", BLOCK_START (b), BLOCK_END (b));
+ if (BLOCK_SUPERBLOCK (b))
+ fprintf (outfile, " (under 0x%x)", BLOCK_SUPERBLOCK (b));
+ if (BLOCK_FUNCTION (b))
+ fprintf (outfile, " %s", SYMBOL_NAME (BLOCK_FUNCTION (b)));
+ fputc ('\n', outfile);
+ blen = BLOCK_NSYMS (b);
+ for (j = 0; j < blen; j++)
+ {
+ print_symbol (BLOCK_SYM (b, j), depth + 1, outfile);
+ }
+ }
+
+ fprintf (outfile, "\n\n");
+ }
+
+ immediate_quit--;
+ do_cleanups (cleanups);
+}
+
+static void
+print_symbol (symbol, depth, outfile)
+ struct symbol *symbol;
+ int depth;
+ FILE *outfile;
+{
+ print_spaces (depth, outfile);
+ if (SYMBOL_NAMESPACE (symbol) == LABEL_NAMESPACE)
+ {
+ fprintf (outfile, "label %s at 0x%x\n", SYMBOL_NAME (symbol),
+ SYMBOL_VALUE (symbol));
+ return;
+ }
+ if (SYMBOL_NAMESPACE (symbol) == STRUCT_NAMESPACE)
+ {
+ if (TYPE_NAME (SYMBOL_TYPE (symbol)))
+ {
+ type_print_1 (SYMBOL_TYPE (symbol), "", outfile, 1, depth);
+ }
+ else
+ {
+ fprintf (outfile, "%s %s = ",
+ (TYPE_CODE (SYMBOL_TYPE (symbol)) == TYPE_CODE_ENUM
+ ? "enum"
+ : (TYPE_CODE (SYMBOL_TYPE (symbol)) == TYPE_CODE_STRUCT
+ ? "struct" : "union")),
+ SYMBOL_NAME (symbol));
+ type_print_1 (SYMBOL_TYPE (symbol), "", outfile, 1, depth);
+ }
+ fprintf (outfile, ";\n");
+ }
+ else
+ {
+ if (SYMBOL_CLASS (symbol) == LOC_TYPEDEF)
+ fprintf (outfile, "typedef ");
+ if (SYMBOL_TYPE (symbol))
+ {
+ type_print_1 (SYMBOL_TYPE (symbol), SYMBOL_NAME (symbol),
+ outfile, 1, depth);
+ fprintf (outfile, "; ");
+ }
+ else
+ fprintf (outfile, "%s ", SYMBOL_NAME (symbol));
+
+ switch (SYMBOL_CLASS (symbol))
+ {
+ case LOC_CONST:
+ fprintf (outfile, "const %d (0x%x),",
+ SYMBOL_VALUE (symbol), SYMBOL_VALUE (symbol));
+ break;
+
+ case LOC_CONST_BYTES:
+ fprintf (outfile, "const %d hex bytes:",
+ TYPE_LENGTH (SYMBOL_TYPE (symbol)));
+ {
+ int i;
+ for (i = 0; i < TYPE_LENGTH (SYMBOL_TYPE (symbol)); i++)
+ fprintf (outfile, " %2x", SYMBOL_VALUE_BYTES (symbol) [i]);
+ fprintf (outfile, ",");
+ }
+ break;
+
+ case LOC_STATIC:
+ fprintf (outfile, "static at 0x%x,", SYMBOL_VALUE (symbol));
+ break;
+
+ case LOC_REGISTER:
+ fprintf (outfile, "register %d,", SYMBOL_VALUE (symbol));
+ break;
+
+ case LOC_ARG:
+ fprintf (outfile, "arg at 0x%x,", SYMBOL_VALUE (symbol));
+ break;
+
+ case LOC_REF_ARG:
+ fprintf (outfile, "reference arg at 0x%x,", SYMBOL_VALUE (symbol));
+ break;
+
+ case LOC_REGPARM:
+ fprintf (outfile, "parameter register %d,", SYMBOL_VALUE (symbol));
+ break;
+
+ case LOC_LOCAL:
+ fprintf (outfile, "local at 0x%x,", SYMBOL_VALUE (symbol));
+ break;
+
+ case LOC_TYPEDEF:
+ break;
+
+ case LOC_LABEL:
+ fprintf (outfile, "label at 0x%x", SYMBOL_VALUE (symbol));
+ break;
+
+ case LOC_BLOCK:
+ fprintf (outfile, "block (object 0x%x) starting at 0x%x,",
+ SYMBOL_VALUE (symbol),
+ BLOCK_START (SYMBOL_BLOCK_VALUE (symbol)));
+ break;
+ }
+ }
+ fprintf (outfile, "\n");
+}
+
+/* Return the nexting depth of a block within other blocks in its symtab. */
+
+static int
+block_depth (block)
+ struct block *block;
+{
+ register int i = 0;
+ while (block = BLOCK_SUPERBLOCK (block)) i++;
+ return i;
+}
+
+/*
+ * Free all partial_symtab storage.
+ */
+void
+free_all_psymtabs()
+{
+ obstack_free (psymbol_obstack, 0);
+ obstack_init (psymbol_obstack);
+ partial_symtab_list = (struct partial_symtab *) 0;
+}
+
+void
+_initialize_symmisc ()
+{
+ symtab_list = (struct symtab *) 0;
+ partial_symtab_list = (struct partial_symtab *) 0;
+
+ add_com ("printsyms", class_obscure, print_symtabs,
+ "Print dump of current symbol definitions to file OUTFILE.");
+}
+
diff --git a/gnu/usr.bin/gdb/symseg.h b/gnu/usr.bin/gdb/symseg.h
new file mode 100644
index 0000000..6a61a17
--- /dev/null
+++ b/gnu/usr.bin/gdb/symseg.h
@@ -0,0 +1,523 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ *
+ * @(#)symseg.h 6.3 (Berkeley) 5/8/91
+ */
+
+/* GDB symbol table format definitions.
+ Copyright (C) 1986, 1989 Free Software Foundation, Inc.
+ Hacked by Michael Tiemann (tiemann@mcc.com)
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Format of GDB symbol table data.
+ There is one symbol segment for each source file or
+ independant compilation. These segments are simply concatenated
+ to form the GDB symbol table. A zero word where the beginning
+ of a segment is expected indicates there are no more segments.
+
+Format of a symbol segment:
+
+ The symbol segment begins with a word containing 1
+ if it is in the format described here. Other formats may
+ be designed, with other code numbers.
+
+ The segment contains many objects which point at each other.
+ The pointers are offsets in bytes from the beginning of the segment.
+ Thus, each segment can be loaded into core and its pointers relocated
+ to make valid in-core pointers.
+
+ All the data objects in the segment can be found indirectly from
+ one of them, the root object, of type `struct symbol_root'.
+ It appears at the beginning of the segment.
+
+ The total size of the segment, in bytes, appears as the `length'
+ field of this object. This size includes the size of the
+ root object.
+
+ All the object data types are defined here to contain pointer types
+ appropriate for in-core use on a relocated symbol segment.
+ Casts to and from type int are required for working with
+ unrelocated symbol segments such as are found in the file.
+
+ The ldsymaddr word is filled in by the loader to contain
+ the offset (in bytes) within the ld symbol table
+ of the first nonglobal symbol from this compilation.
+ This makes it possible to match those symbols
+ (which contain line number information) reliably with
+ the segment they go with.
+
+ Core addresses within the program that appear in the symbol segment
+ are not relocated by the loader. They are inserted by the assembler
+ and apply to addresses as output by the assembler, so GDB must
+ relocate them when it loads the symbol segment. It gets the information
+ on how to relocate from the textrel, datarel, bssrel, databeg and bssbeg
+ words of the root object.
+
+ The words textrel, datarel and bssrel
+ are filled in by ld with the amounts to relocate within-the-file
+ text, data and bss addresses by; databeg and bssbeg can be
+ used to tell which kind of relocation an address needs. */
+
+enum language {language_c};
+
+struct symbol_root
+{
+ int format; /* Data format version */
+ int length; /* # bytes in this symbol segment */
+ int ldsymoff; /* Offset in ld symtab of this file's syms */
+ int textrel; /* Relocation for text addresses */
+ int datarel; /* Relocation for data addresses */
+ int bssrel; /* Relocation for bss addresses */
+ char *filename; /* Name of main source file compiled */
+ char *filedir; /* Name of directory it was reached from */
+ struct blockvector *blockvector; /* Vector of all symbol-naming blocks */
+ struct typevector *typevector; /* Vector of all data types */
+ enum language language; /* Code identifying the language used */
+ char *version; /* Version info. Not fully specified */
+ char *compilation; /* Compilation info. Not fully specified */
+ int databeg; /* Address within the file of data start */
+ int bssbeg; /* Address within the file of bss start */
+ struct sourcevector *sourcevector; /* Vector of line-number info */
+};
+
+/* All data types of symbols in the compiled program
+ are represented by `struct type' objects.
+ All of these objects are pointed to by the typevector.
+ The type vector may have empty slots that contain zero. */
+
+struct typevector
+{
+ int length; /* Number of types described */
+ struct type *type[1];
+};
+
+/* Different kinds of data types are distinguished by the `code' field. */
+
+enum type_code
+{
+ TYPE_CODE_UNDEF, /* Not used; catches errors */
+ TYPE_CODE_PTR, /* Pointer type */
+ TYPE_CODE_ARRAY, /* Array type, lower bound zero */
+ TYPE_CODE_STRUCT, /* C struct or Pascal record */
+ TYPE_CODE_UNION, /* C union or Pascal variant part */
+ TYPE_CODE_ENUM, /* Enumeration type */
+ TYPE_CODE_FUNC, /* Function type */
+ TYPE_CODE_INT, /* Integer type */
+ TYPE_CODE_FLT, /* Floating type */
+ TYPE_CODE_VOID, /* Void type (values zero length) */
+ TYPE_CODE_SET, /* Pascal sets */
+ TYPE_CODE_RANGE, /* Range (integers within spec'd bounds) */
+ TYPE_CODE_PASCAL_ARRAY, /* Array with explicit type of index */
+
+ /* C++ */
+ TYPE_CODE_MEMBER, /* Member type */
+ TYPE_CODE_METHOD, /* Method type */
+ TYPE_CODE_REF, /* C++ Reference types */
+};
+
+/* This appears in a type's flags word for an unsigned integer type. */
+#define TYPE_FLAG_UNSIGNED 1
+/* This appears in a type's flags word
+ if it is a (pointer to a|function returning a)* built in scalar type.
+ These types are never freed. */
+#define TYPE_FLAG_PERM 4
+/* This appears in a type's flags word if it is a stub type (eg. if
+ someone referenced a type that wasn't definined in a source file
+ via (struct sir_not_appearing_in_this_film *)). */
+#define TYPE_FLAG_STUB 8
+/* Set when a class has a constructor defined */
+#define TYPE_FLAG_HAS_CONSTRUCTOR 256
+/* Set when a class has a destructor defined */
+#define TYPE_FLAG_HAS_DESTRUCTOR 512
+/* Indicates that this type is a public baseclass of another class,
+ i.e. that all its public methods are available in the derived
+ class. */
+#define TYPE_FLAG_VIA_PUBLIC 1024
+/* Indicates that this type is a virtual baseclass of another class,
+ i.e. that if this class is inherited more than once by another
+ class, only one set of member variables will be included. */
+#define TYPE_FLAG_VIA_VIRTUAL 2048
+
+struct type
+{
+ /* Code for kind of type */
+ enum type_code code;
+ /* Name of this type, or zero if none.
+ This is used for printing only.
+ Type names specified as input are defined by symbols. */
+ char *name;
+ /* Length in bytes of storage for a value of this type */
+ int length;
+ /* For a pointer type, describes the type of object pointed to.
+ For an array type, describes the type of the elements.
+ For a function or method type, describes the type of the value.
+ For a range type, describes the type of the full range.
+ Unused otherwise. */
+ struct type *target_type;
+ /* Type that is a pointer to this type.
+ Zero if no such pointer-to type is known yet.
+ The debugger may add the address of such a type
+ if it has to construct one later. */
+ struct type *pointer_type;
+ /* C++: also need a reference type. */
+ struct type *reference_type;
+ struct type **arg_types;
+
+ /* Type that is a function returning this type.
+ Zero if no such function type is known here.
+ The debugger may add the address of such a type
+ if it has to construct one later. */
+ struct type *function_type;
+
+/* Handling of pointers to members:
+ TYPE_MAIN_VARIANT is used for pointer and pointer
+ to member types. Normally it the value of the address of its
+ containing type. However, for pointers to members, we must be
+ able to allocate pointer to member types and look them up
+ from some place of reference.
+ NEXT_VARIANT is the next element in the chain. */
+ struct type *main_variant, *next_variant;
+
+ /* Flags about this type. */
+ short flags;
+ /* Number of fields described for this type */
+ short nfields;
+ /* For structure and union types, a description of each field.
+ For set and pascal array types, there is one "field",
+ whose type is the domain type of the set or array.
+ For range types, there are two "fields",
+ the minimum and maximum values (both inclusive).
+ For enum types, each possible value is described by one "field".
+
+ Using a pointer to a separate array of fields
+ allows all types to have the same size, which is useful
+ because we can allocate the space for a type before
+ we know what to put in it. */
+ struct field
+ {
+ /* Position of this field, counting in bits from start of
+ containing structure. For a function type, this is the
+ position in the argument list of this argument.
+ For a range bound or enum value, this is the value itself. */
+ int bitpos;
+ /* Size of this field, in bits, or zero if not packed.
+ For an unpacked field, the field's type's length
+ says how many bytes the field occupies. */
+ int bitsize;
+ /* In a struct or enum type, type of this field.
+ In a function type, type of this argument.
+ In an array type, the domain-type of the array. */
+ struct type *type;
+ /* Name of field, value or argument.
+ Zero for range bounds and array domains. */
+ char *name;
+ } *fields;
+
+ /* C++ */
+ int *private_field_bits;
+ int *protected_field_bits;
+
+ /* Number of methods described for this type */
+ short nfn_fields;
+ /* Number of base classes this type derives from. */
+ short n_baseclasses;
+
+ /* Number of methods described for this type plus all the
+ methods that it derives from. */
+ int nfn_fields_total;
+
+ /* For classes, structures, and unions, a description of each field,
+ which consists of an overloaded name, followed by the types of
+ arguments that the method expects, and then the name after it
+ has been renamed to make it distinct. */
+ struct fn_fieldlist
+ {
+ /* The overloaded name. */
+ char *name;
+ /* The number of methods with this name. */
+ int length;
+ /* The list of methods. */
+ struct fn_field
+ {
+#if 0
+ /* The overloaded name */
+ char *name;
+#endif
+ /* The return value of the method */
+ struct type *type;
+ /* The argument list */
+ struct type **args;
+ /* The name after it has been processed */
+ char *physname;
+ /* If this is a virtual function, the offset into the vtbl-1,
+ else 0. */
+ int voffset;
+ } *fn_fields;
+
+ int *private_fn_field_bits;
+ int *protected_fn_field_bits;
+
+ } *fn_fieldlists;
+
+ unsigned char via_protected;
+ unsigned char via_public;
+
+ /* For types with virtual functions, VPTR_BASETYPE is the base class which
+ defined the virtual function table pointer. VPTR_FIELDNO is
+ the field number of that pointer in the structure.
+
+ For types that are pointer to member types, VPTR_BASETYPE
+ ifs the type that this pointer is a member of.
+
+ Unused otherwise. */
+ struct type *vptr_basetype;
+
+ int vptr_fieldno;
+
+ /* If this type has a base class, put it here.
+ If this type is a pointer type, the chain of member pointer
+ types goes here.
+ Unused otherwise.
+
+ Contrary to all maxims of C style and common sense, the baseclasses
+ are indexed from 1 to N_BASECLASSES rather than 0 to N_BASECLASSES-1
+ (i.e. BASECLASSES points to one *before* the first element of
+ the array). */
+ struct type **baseclasses;
+};
+
+/* All of the name-scope contours of the program
+ are represented by `struct block' objects.
+ All of these objects are pointed to by the blockvector.
+
+ Each block represents one name scope.
+ Each lexical context has its own block.
+
+ The first two blocks in the blockvector are special.
+ The first one contains all the symbols defined in this compilation
+ whose scope is the entire program linked together.
+ The second one contains all the symbols whose scope is the
+ entire compilation excluding other separate compilations.
+ In C, these correspond to global symbols and static symbols.
+
+ Each block records a range of core addresses for the code that
+ is in the scope of the block. The first two special blocks
+ give, for the range of code, the entire range of code produced
+ by the compilation that the symbol segment belongs to.
+
+ The blocks appear in the blockvector
+ in order of increasing starting-address,
+ and, within that, in order of decreasing ending-address.
+
+ This implies that within the body of one function
+ the blocks appear in the order of a depth-first tree walk. */
+
+struct blockvector
+{
+ /* Number of blocks in the list. */
+ int nblocks;
+ /* The blocks themselves. */
+ struct block *block[1];
+};
+
+struct block
+{
+ /* Addresses in the executable code that are in this block.
+ Note: in an unrelocated symbol segment in a file,
+ these are always zero. They can be filled in from the
+ N_LBRAC and N_RBRAC symbols in the loader symbol table. */
+ int startaddr, endaddr;
+ /* The symbol that names this block,
+ if the block is the body of a function;
+ otherwise, zero.
+ Note: In an unrelocated symbol segment in an object file,
+ this field may be zero even when the block has a name.
+ That is because the block is output before the name
+ (since the name resides in a higher block).
+ Since the symbol does point to the block (as its value),
+ it is possible to find the block and set its name properly. */
+ struct symbol *function;
+ /* The `struct block' for the containing block, or 0 if none. */
+ /* Note that in an unrelocated symbol segment in an object file
+ this pointer may be zero when the correct value should be
+ the second special block (for symbols whose scope is one compilation).
+ This is because the compiler ouptuts the special blocks at the
+ very end, after the other blocks. */
+ struct block *superblock;
+ /* A flag indicating whether or not the fucntion corresponding
+ to this block was compiled with gcc or not. If there is no
+ function corresponding to this block, this meaning of this flag
+ is undefined. (In practice it will be 1 if the block was created
+ while processing a file compiled with gcc and 0 when not). */
+ unsigned char gcc_compile_flag;
+ /* Number of local symbols. */
+ int nsyms;
+ /* The symbols. */
+ struct symbol *sym[1];
+};
+
+/* Represent one symbol name; a variable, constant, function or typedef. */
+
+/* Different name spaces for symbols. Looking up a symbol specifies
+ a namespace and ignores symbol definitions in other name spaces.
+
+ VAR_NAMESPACE is the usual namespace.
+ In C, this contains variables, function names, typedef names
+ and enum type values.
+
+ STRUCT_NAMESPACE is used in C to hold struct, union and enum type names.
+ Thus, if `struct foo' is used in a C program,
+ it produces a symbol named `foo' in the STRUCT_NAMESPACE.
+
+ LABEL_NAMESPACE may be used for names of labels (for gotos);
+ currently it is not used and labels are not recorded at all. */
+
+/* For a non-global symbol allocated statically,
+ the correct core address cannot be determined by the compiler.
+ The compiler puts an index number into the symbol's value field.
+ This index number can be matched with the "desc" field of
+ an entry in the loader symbol table. */
+
+enum namespace
+{
+ UNDEF_NAMESPACE, VAR_NAMESPACE, STRUCT_NAMESPACE, LABEL_NAMESPACE,
+};
+
+/* An address-class says where to find the value of the symbol in core. */
+
+enum address_class
+{
+ LOC_UNDEF, /* Not used; catches errors */
+ LOC_CONST, /* Value is constant int */
+ LOC_STATIC, /* Value is at fixed address */
+ LOC_REGISTER, /* Value is in register */
+ LOC_ARG, /* Value is at spec'd position in arglist */
+ LOC_REF_ARG, /* Value address is at spec'd position in */
+ /* arglist. */
+ LOC_REGPARM, /* Value is at spec'd position in register window */
+ LOC_LOCAL, /* Value is at spec'd pos in stack frame */
+ LOC_TYPEDEF, /* Value not used; definition in SYMBOL_TYPE
+ Symbols in the namespace STRUCT_NAMESPACE
+ all have this class. */
+ LOC_LABEL, /* Value is address in the code */
+ LOC_BLOCK, /* Value is address of a `struct block'.
+ Function names have this class. */
+ LOC_EXTERNAL, /* Value is at address not in this compilation.
+ This is used for .comm symbols
+ and for extern symbols within functions.
+ Inside GDB, this is changed to LOC_STATIC once the
+ real address is obtained from a loader symbol. */
+ LOC_CONST_BYTES /* Value is a constant byte-sequence. */
+};
+
+struct symbol
+{
+ /* Symbol name */
+ char *name;
+ /* Name space code. */
+ enum namespace namespace;
+ /* Address class */
+ enum address_class class;
+ /* Data type of value */
+ struct type *type;
+ /* constant value, or address if static, or register number,
+ or offset in arguments, or offset in stack frame. */
+ union
+ {
+ long value;
+ struct block *block; /* for LOC_BLOCK */
+ char *bytes; /* for LOC_CONST_BYTES */
+ }
+ value;
+};
+
+struct partial_symbol
+{
+ /* Symbol name */
+ char *name;
+ /* Name space code. */
+ enum namespace namespace;
+ /* Address class (for info_symbols) */
+ enum address_class class;
+ /* Associated partial symbol table */
+ struct partial_symtab *pst;
+ /* Value (only used for static functions currently). Done this
+ way so that we can use the struct symbol macros.
+ Note that the address of a function is SYMBOL_VALUE (pst)
+ in a partial symbol table, but BLOCK_START (SYMBOL_BLOCK_VALUE (st))
+ in a symbol table. */
+ union
+ {
+ long value;
+ }
+ value;
+};
+
+/*
+ * Vectors of all partial symbols read in from file; actually declared
+ * and used in dbxread.c.
+ */
+extern struct psymbol_allocation_list {
+ struct partial_symbol *list, *next;
+ int size;
+} global_psymbols, static_psymbols;
+
+
+/* Source-file information.
+ This describes the relation between source files and line numbers
+ and addresses in the program text. */
+
+struct sourcevector
+{
+ int length; /* Number of source files described */
+ struct source *source[1]; /* Descriptions of the files */
+};
+
+/* Each item represents a line-->pc (or the reverse) mapping. This is
+ somewhat more wasteful of space than one might wish, but since only
+ the files which are actually debugged are read in to core, we don't
+ waste much space.
+
+ Each item used to be an int; either minus a line number, or a
+ program counter. If it represents a line number, that is the line
+ described by the next program counter value. If it is positive, it
+ is the program counter at which the code for the next line starts. */
+
+struct linetable_entry
+{
+ int line;
+ CORE_ADDR pc;
+};
+
+struct linetable
+{
+ int nitems;
+ struct linetable_entry item[1];
+};
+
+/* All the information on one source file. */
+
+struct source
+{
+ char *name; /* Name of file */
+ struct linetable contents;
+};
diff --git a/gnu/usr.bin/gdb/symtab.c b/gnu/usr.bin/gdb/symtab.c
new file mode 100644
index 0000000..4e4bd8e
--- /dev/null
+++ b/gnu/usr.bin/gdb/symtab.c
@@ -0,0 +1,2473 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ *
+ * $Header: /a/cvs/386BSD/src/gnu/gdb/symtab.c,v 1.1 1993/06/29 09:47:40 nate Exp $;
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)symtab.c 6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Symbol table lookup for the GNU debugger, GDB.
+ Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+
+#include <obstack.h>
+#include <assert.h>
+
+char *index ();
+extern char *cplus_demangle ();
+extern struct value * value_of_this ();
+
+/* Allocate an obstack to hold objects that should be freed
+ when we load a new symbol table.
+ This includes the symbols made by dbxread
+ and the types that are not permanent. */
+
+struct obstack obstack1;
+
+struct obstack *symbol_obstack = &obstack1;
+
+/* This obstack will be used for partial_symbol objects. It can
+ probably actually be the same as the symbol_obstack above, but I'd
+ like to keep them seperate for now. If I want to later, I'll
+ replace one with the other. */
+
+struct obstack obstack2;
+
+struct obstack *psymbol_obstack = &obstack2;
+
+/* These variables point to the objects
+ representing the predefined C data types. */
+
+struct type *builtin_type_void;
+struct type *builtin_type_char;
+struct type *builtin_type_short;
+struct type *builtin_type_int;
+struct type *builtin_type_long;
+#ifdef LONG_LONG
+struct type *builtin_type_long_long;
+#endif
+struct type *builtin_type_unsigned_char;
+struct type *builtin_type_unsigned_short;
+struct type *builtin_type_unsigned_int;
+struct type *builtin_type_unsigned_long;
+#ifdef LONG_LONG
+struct type *builtin_type_unsigned_long_long;
+#endif
+struct type *builtin_type_float;
+struct type *builtin_type_double;
+
+/* Block in which the most recently searched-for symbol was found.
+ Might be better to make this a parameter to lookup_symbol and
+ value_of_this. */
+struct block *block_found;
+
+/* Functions */
+static int find_line_common ();
+int lookup_misc_func ();
+struct partial_symtab *lookup_partial_symtab ();
+struct symtab *psymtab_to_symtab ();
+static struct partial_symbol *lookup_partial_symbol ();
+
+/* Check for a symtab of a specific name; first in symtabs, then in
+ psymtabs. *If* there is no '/' in the name, a match after a '/'
+ in the symtab filename will also work. */
+
+static struct symtab *
+lookup_symtab_1 (name)
+ char *name;
+{
+ register struct symtab *s;
+ register struct partial_symtab *ps;
+ register char *slash = index (name, '/');
+ register int len = strlen (name);
+
+ for (s = symtab_list; s; s = s->next)
+ if (!strcmp (name, s->filename))
+ return s;
+
+ for (ps = partial_symtab_list; ps; ps = ps->next)
+ if (!strcmp (name, ps->filename))
+ {
+ if (ps->readin)
+ fatal ("Internal: readin pst found when no symtab found.");
+ s = psymtab_to_symtab (ps);
+ return s;
+ }
+
+ if (!slash)
+ {
+ for (s = symtab_list; s; s = s->next)
+ {
+ int l = strlen (s->filename);
+
+ if (s->filename[l - len -1] == '/'
+ && !strcmp (s->filename + l - len, name))
+ return s;
+ }
+
+ for (ps = partial_symtab_list; ps; ps = ps->next)
+ {
+ int l = strlen (ps->filename);
+
+ if (ps->filename[l - len - 1] == '/'
+ && !strcmp (ps->filename + l - len, name))
+ {
+ if (ps->readin)
+ fatal ("Internal: readin pst found when no symtab found.");
+ s = psymtab_to_symtab (ps);
+ return s;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Lookup the symbol table of a source file named NAME. Try a couple
+ of variations if the first lookup doesn't work. */
+
+struct symtab *
+lookup_symtab (name)
+ char *name;
+{
+ register struct symtab *s;
+ register char *copy;
+
+ s = lookup_symtab_1 (name);
+ if (s) return s;
+
+ /* If name not found as specified, see if adding ".c" helps. */
+
+ copy = (char *) alloca (strlen (name) + 3);
+ strcpy (copy, name);
+ strcat (copy, ".c");
+ s = lookup_symtab_1 (copy);
+ if (s) return s;
+
+ /* We didn't find anything; die. */
+ return 0;
+}
+
+/* Lookup the partial symbol table of a source file named NAME. This
+ only returns true on an exact match (ie. this semantics are
+ different from lookup_symtab. */
+
+struct partial_symtab *
+lookup_partial_symtab (name)
+char *name;
+{
+ register struct partial_symtab *s;
+ register char *copy;
+
+ for (s = partial_symtab_list; s; s = s->next)
+ if (!strcmp (name, s->filename))
+ return s;
+
+ return 0;
+}
+
+/* Lookup a typedef or primitive type named NAME,
+ visible in lexical block BLOCK.
+ If NOERR is nonzero, return zero if NAME is not suitably defined. */
+
+struct type *
+lookup_typename (name, block, noerr)
+ char *name;
+ struct block *block;
+ int noerr;
+{
+ register struct symbol *sym = lookup_symbol (name, block, VAR_NAMESPACE, 0);
+ if (sym == 0 || SYMBOL_CLASS (sym) != LOC_TYPEDEF)
+ {
+ if (!strcmp (name, "int"))
+ return builtin_type_int;
+ if (!strcmp (name, "long"))
+ return builtin_type_long;
+ if (!strcmp (name, "short"))
+ return builtin_type_short;
+ if (!strcmp (name, "char"))
+ return builtin_type_char;
+ if (!strcmp (name, "float"))
+ return builtin_type_float;
+ if (!strcmp (name, "double"))
+ return builtin_type_double;
+ if (!strcmp (name, "void"))
+ return builtin_type_void;
+
+ if (noerr)
+ return 0;
+ error ("No type named %s.", name);
+ }
+ return SYMBOL_TYPE (sym);
+}
+
+struct type *
+lookup_unsigned_typename (name)
+ char *name;
+{
+ if (!strcmp (name, "int"))
+ return builtin_type_unsigned_int;
+ if (!strcmp (name, "long"))
+ return builtin_type_unsigned_long;
+ if (!strcmp (name, "short"))
+ return builtin_type_unsigned_short;
+ if (!strcmp (name, "char"))
+ return builtin_type_unsigned_char;
+ error ("No type named unsigned %s.", name);
+}
+
+/* Lookup a structure type named "struct NAME",
+ visible in lexical block BLOCK. */
+
+struct type *
+lookup_struct (name, block)
+ char *name;
+ struct block *block;
+{
+ register struct symbol *sym
+ = lookup_symbol (name, block, STRUCT_NAMESPACE, 0);
+
+ if (sym == 0)
+ error ("No struct type named %s.", name);
+ if (TYPE_CODE (SYMBOL_TYPE (sym)) != TYPE_CODE_STRUCT)
+ error ("This context has class, union or enum %s, not a struct.", name);
+ return SYMBOL_TYPE (sym);
+}
+
+/* Lookup a union type named "union NAME",
+ visible in lexical block BLOCK. */
+
+struct type *
+lookup_union (name, block)
+ char *name;
+ struct block *block;
+{
+ register struct symbol *sym
+ = lookup_symbol (name, block, STRUCT_NAMESPACE, 0);
+
+ if (sym == 0)
+ error ("No union type named %s.", name);
+ if (TYPE_CODE (SYMBOL_TYPE (sym)) != TYPE_CODE_UNION)
+ error ("This context has class, struct or enum %s, not a union.", name);
+ return SYMBOL_TYPE (sym);
+}
+
+/* Lookup an enum type named "enum NAME",
+ visible in lexical block BLOCK. */
+
+struct type *
+lookup_enum (name, block)
+ char *name;
+ struct block *block;
+{
+ register struct symbol *sym
+ = lookup_symbol (name, block, STRUCT_NAMESPACE, 0);
+ if (sym == 0)
+ error ("No enum type named %s.", name);
+ if (TYPE_CODE (SYMBOL_TYPE (sym)) != TYPE_CODE_ENUM)
+ error ("This context has class, struct or union %s, not an enum.", name);
+ return SYMBOL_TYPE (sym);
+}
+
+/* Given a type TYPE, lookup the type of the component of type named
+ NAME. */
+
+struct type *
+lookup_struct_elt_type (type, name)
+ struct type *type;
+ char *name;
+{
+ struct type *t;
+ int i;
+ char *errmsg;
+
+ if (TYPE_CODE (type) != TYPE_CODE_STRUCT
+ && TYPE_CODE (type) != TYPE_CODE_UNION)
+ {
+ terminal_ours ();
+ fflush (stdout);
+ fprintf (stderr, "Type ");
+ type_print (type, "", stderr, -1);
+ fprintf (stderr, " is not a structure or union type.\n");
+ return_to_top_level ();
+ }
+
+ for (i = TYPE_NFIELDS (type) - 1; i >= 0; i--)
+ if (!strcmp (TYPE_FIELD_NAME (type, i), name))
+ return TYPE_FIELD_TYPE (type, i);
+
+ terminal_ours ();
+ fflush (stdout);
+ fprintf (stderr, "Type ");
+ type_print (type, "", stderr, -1);
+ fprintf (stderr, " has no component named %s\n", name);
+ return_to_top_level ();
+}
+
+/* Given a type TYPE, return a type of pointers to that type.
+ May need to construct such a type if this is the first use.
+
+ C++: use TYPE_MAIN_VARIANT and TYPE_CHAIN to keep pointer
+ to member types under control. */
+
+struct type *
+lookup_pointer_type (type)
+ struct type *type;
+{
+ register struct type *ptype = TYPE_POINTER_TYPE (type);
+ if (ptype) return TYPE_MAIN_VARIANT (ptype);
+
+ /* This is the first time anyone wanted a pointer to a TYPE. */
+ if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+ ptype = (struct type *) xmalloc (sizeof (struct type));
+ else
+ ptype = (struct type *) obstack_alloc (symbol_obstack,
+ sizeof (struct type));
+
+ bzero (ptype, sizeof (struct type));
+ TYPE_MAIN_VARIANT (ptype) = ptype;
+ TYPE_TARGET_TYPE (ptype) = type;
+ TYPE_POINTER_TYPE (type) = ptype;
+ /* New type is permanent if type pointed to is permanent. */
+ if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+ TYPE_FLAGS (ptype) |= TYPE_FLAG_PERM;
+ /* We assume the machine has only one representation for pointers! */
+ TYPE_LENGTH (ptype) = sizeof (char *);
+ TYPE_CODE (ptype) = TYPE_CODE_PTR;
+ return ptype;
+}
+
+struct type *
+lookup_reference_type (type)
+ struct type *type;
+{
+ register struct type *rtype = TYPE_REFERENCE_TYPE (type);
+ if (rtype) return TYPE_MAIN_VARIANT (rtype);
+
+ /* This is the first time anyone wanted a pointer to a TYPE. */
+ if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+ rtype = (struct type *) xmalloc (sizeof (struct type));
+ else
+ rtype = (struct type *) obstack_alloc (symbol_obstack,
+ sizeof (struct type));
+
+ bzero (rtype, sizeof (struct type));
+ TYPE_MAIN_VARIANT (rtype) = rtype;
+ TYPE_TARGET_TYPE (rtype) = type;
+ TYPE_REFERENCE_TYPE (type) = rtype;
+ /* New type is permanent if type pointed to is permanent. */
+ if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+ TYPE_FLAGS (rtype) |= TYPE_FLAG_PERM;
+ /* We assume the machine has only one representation for pointers! */
+ TYPE_LENGTH (rtype) = sizeof (char *);
+ TYPE_CODE (rtype) = TYPE_CODE_REF;
+ return rtype;
+}
+
+
+/* Implement direct support for MEMBER_TYPE in GNU C++.
+ May need to construct such a type if this is the first use.
+ The TYPE is the type of the member. The DOMAIN is the type
+ of the aggregate that the member belongs to. */
+
+struct type *
+lookup_member_type (type, domain)
+ struct type *type, *domain;
+{
+ register struct type *mtype = TYPE_MAIN_VARIANT (type);
+ struct type *main_type;
+
+ main_type = mtype;
+ while (mtype)
+ {
+ if (TYPE_DOMAIN_TYPE (mtype) == domain)
+ return mtype;
+ mtype = TYPE_NEXT_VARIANT (mtype);
+ }
+
+ /* This is the first time anyone wanted this member type. */
+ if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+ mtype = (struct type *) xmalloc (sizeof (struct type));
+ else
+ mtype = (struct type *) obstack_alloc (symbol_obstack,
+ sizeof (struct type));
+
+ bzero (mtype, sizeof (struct type));
+ if (main_type == 0)
+ main_type = mtype;
+ else
+ {
+ TYPE_NEXT_VARIANT (mtype) = TYPE_NEXT_VARIANT (main_type);
+ TYPE_NEXT_VARIANT (main_type) = mtype;
+ }
+ TYPE_MAIN_VARIANT (mtype) = main_type;
+ TYPE_TARGET_TYPE (mtype) = type;
+ TYPE_DOMAIN_TYPE (mtype) = domain;
+ /* New type is permanent if type pointed to is permanent. */
+ if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+ TYPE_FLAGS (mtype) |= TYPE_FLAG_PERM;
+
+ /* In practice, this is never used. */
+ TYPE_LENGTH (mtype) = 1;
+ TYPE_CODE (mtype) = TYPE_CODE_MEMBER;
+
+#if 0
+ /* Now splice in the new member pointer type. */
+ if (main_type)
+ {
+ /* This type was not "smashed". */
+ TYPE_CHAIN (mtype) = TYPE_CHAIN (main_type);
+ TYPE_CHAIN (main_type) = mtype;
+ }
+#endif
+
+ return mtype;
+}
+
+struct type *
+lookup_method_type (type, domain, args)
+ struct type *type, *domain, **args;
+{
+ register struct type *mtype = TYPE_MAIN_VARIANT (type);
+ struct type *main_type;
+
+ main_type = mtype;
+ while (mtype)
+ {
+ if (TYPE_DOMAIN_TYPE (mtype) == domain)
+ {
+ struct type **t1 = args;
+ struct type **t2 = TYPE_ARG_TYPES (mtype);
+ if (t2)
+ {
+ int i;
+ for (i = 0; t1[i] != 0 && t1[i]->code != TYPE_CODE_VOID; i++)
+ if (t1[i] != t2[i])
+ break;
+ if (t1[i] == t2[i])
+ return mtype;
+ }
+ }
+ mtype = TYPE_NEXT_VARIANT (mtype);
+ }
+
+ /* This is the first time anyone wanted this member type. */
+ if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+ mtype = (struct type *) xmalloc (sizeof (struct type));
+ else
+ mtype = (struct type *) obstack_alloc (symbol_obstack,
+ sizeof (struct type));
+
+ bzero (mtype, sizeof (struct type));
+ if (main_type == 0)
+ main_type = mtype;
+ else
+ {
+ TYPE_NEXT_VARIANT (mtype) = TYPE_NEXT_VARIANT (main_type);
+ TYPE_NEXT_VARIANT (main_type) = mtype;
+ }
+ TYPE_MAIN_VARIANT (mtype) = main_type;
+ TYPE_TARGET_TYPE (mtype) = type;
+ TYPE_DOMAIN_TYPE (mtype) = domain;
+ TYPE_ARG_TYPES (mtype) = args;
+ /* New type is permanent if type pointed to is permanent. */
+ if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+ TYPE_FLAGS (mtype) |= TYPE_FLAG_PERM;
+
+ /* In practice, this is never used. */
+ TYPE_LENGTH (mtype) = 1;
+ TYPE_CODE (mtype) = TYPE_CODE_METHOD;
+
+#if 0
+ /* Now splice in the new member pointer type. */
+ if (main_type)
+ {
+ /* This type was not "smashed". */
+ TYPE_CHAIN (mtype) = TYPE_CHAIN (main_type);
+ TYPE_CHAIN (main_type) = mtype;
+ }
+#endif
+
+ return mtype;
+}
+
+/* Given a type TYPE, return a type which has offset OFFSET,
+ via_virtual VIA_VIRTUAL, and via_public VIA_PUBLIC.
+ May need to construct such a type if none exists. */
+struct type *
+lookup_basetype_type (type, offset, via_virtual, via_public)
+ struct type *type;
+ int offset;
+ int via_virtual, via_public;
+{
+ register struct type *btype = TYPE_MAIN_VARIANT (type);
+ struct type *main_type;
+
+ if (offset != 0)
+ {
+ printf ("Internal error: type offset non-zero in lookup_basetype_type");
+ offset = 0;
+ }
+
+ main_type = btype;
+ while (btype)
+ {
+ if (/* TYPE_OFFSET (btype) == offset
+ && */ TYPE_VIA_PUBLIC (btype) == via_public
+ && TYPE_VIA_VIRTUAL (btype) == via_virtual)
+ return btype;
+ btype = TYPE_NEXT_VARIANT (btype);
+ }
+
+ /* This is the first time anyone wanted this member type. */
+ if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+ btype = (struct type *) xmalloc (sizeof (struct type));
+ else
+ btype = (struct type *) obstack_alloc (symbol_obstack,
+ sizeof (struct type));
+
+ if (main_type == 0)
+ {
+ main_type = btype;
+ bzero (btype, sizeof (struct type));
+ TYPE_MAIN_VARIANT (btype) = main_type;
+ }
+ else
+ {
+ bcopy (main_type, btype, sizeof (struct type));
+ TYPE_NEXT_VARIANT (main_type) = btype;
+ }
+/* TYPE_OFFSET (btype) = offset; */
+ if (via_public)
+ TYPE_FLAGS (btype) |= TYPE_FLAG_VIA_PUBLIC;
+ if (via_virtual)
+ TYPE_FLAGS (btype) |= TYPE_FLAG_VIA_VIRTUAL;
+ /* New type is permanent if type pointed to is permanent. */
+ if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+ TYPE_FLAGS (btype) |= TYPE_FLAG_PERM;
+
+ /* In practice, this is never used. */
+ TYPE_LENGTH (btype) = 1;
+ TYPE_CODE (btype) = TYPE_CODE_STRUCT;
+
+ return btype;
+}
+
+/* Given a type TYPE, return a type of functions that return that type.
+ May need to construct such a type if this is the first use. */
+
+struct type *
+lookup_function_type (type)
+ struct type *type;
+{
+ register struct type *ptype = TYPE_FUNCTION_TYPE (type);
+ if (ptype) return ptype;
+
+ /* This is the first time anyone wanted a function returning a TYPE. */
+ if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+ ptype = (struct type *) xmalloc (sizeof (struct type));
+ else
+ ptype = (struct type *) obstack_alloc (symbol_obstack,
+ sizeof (struct type));
+
+ bzero (ptype, sizeof (struct type));
+ TYPE_TARGET_TYPE (ptype) = type;
+ TYPE_FUNCTION_TYPE (type) = ptype;
+ /* New type is permanent if type returned is permanent. */
+ if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+ TYPE_FLAGS (ptype) |= TYPE_FLAG_PERM;
+ TYPE_LENGTH (ptype) = 1;
+ TYPE_CODE (ptype) = TYPE_CODE_FUNC;
+ TYPE_NFIELDS (ptype) = 0;
+ return ptype;
+}
+
+/* Create an array type. Elements will be of type TYPE, and there will
+ be NUM of them.
+
+ Eventually this should be extended to take two more arguments which
+ specify the bounds of the array and the type of the index.
+ It should also be changed to be a "lookup" function, with the
+ appropriate data structures added to the type field.
+ Then read array type should call here. */
+
+struct type *
+create_array_type (element_type, number)
+ struct type *element_type;
+ int number;
+{
+ struct type *result_type = (struct type *)
+ obstack_alloc (symbol_obstack, sizeof (struct type));
+
+ bzero (result_type, sizeof (struct type));
+
+ TYPE_CODE (result_type) = TYPE_CODE_ARRAY;
+ TYPE_TARGET_TYPE (result_type) = element_type;
+ TYPE_LENGTH (result_type) = number * TYPE_LENGTH (element_type);
+ TYPE_NFIELDS (result_type) = 1;
+ TYPE_FIELDS (result_type) =
+ (struct field *) obstack_alloc (symbol_obstack, sizeof (struct field));
+ TYPE_FIELD_TYPE (result_type, 0) = builtin_type_int;
+ TYPE_VPTR_FIELDNO (result_type) = -1;
+
+ return result_type;
+}
+
+
+/* Smash TYPE to be a type of pointers to TO_TYPE.
+ If TO_TYPE is not permanent and has no pointer-type yet,
+ record TYPE as its pointer-type. */
+
+void
+smash_to_pointer_type (type, to_type)
+ struct type *type, *to_type;
+{
+ int type_permanent = (TYPE_FLAGS (type) & TYPE_FLAG_PERM);
+
+ bzero (type, sizeof (struct type));
+ TYPE_TARGET_TYPE (type) = to_type;
+ /* We assume the machine has only one representation for pointers! */
+ TYPE_LENGTH (type) = sizeof (char *);
+ TYPE_CODE (type) = TYPE_CODE_PTR;
+
+ TYPE_MAIN_VARIANT (type) = type;
+
+ if (type_permanent)
+ TYPE_FLAGS (type) |= TYPE_FLAG_PERM;
+
+ if (TYPE_POINTER_TYPE (to_type) == 0
+ && (!(TYPE_FLAGS (to_type) & TYPE_FLAG_PERM)
+ || type_permanent))
+ {
+ TYPE_POINTER_TYPE (to_type) = type;
+ }
+}
+
+/* Smash TYPE to be a type of members of DOMAIN with type TO_TYPE. */
+
+void
+smash_to_member_type (type, domain, to_type)
+ struct type *type, *domain, *to_type;
+{
+ bzero (type, sizeof (struct type));
+ TYPE_TARGET_TYPE (type) = to_type;
+ TYPE_DOMAIN_TYPE (type) = domain;
+
+ /* In practice, this is never needed. */
+ TYPE_LENGTH (type) = 1;
+ TYPE_CODE (type) = TYPE_CODE_MEMBER;
+
+ TYPE_MAIN_VARIANT (type) = lookup_member_type (domain, to_type);
+}
+
+/* Smash TYPE to be a type of method of DOMAIN with type TO_TYPE. */
+
+void
+smash_to_method_type (type, domain, to_type, args)
+ struct type *type, *domain, *to_type, **args;
+{
+ bzero (type, sizeof (struct type));
+ TYPE_TARGET_TYPE (type) = to_type;
+ TYPE_DOMAIN_TYPE (type) = domain;
+ TYPE_ARG_TYPES (type) = args;
+
+ /* In practice, this is never needed. */
+ TYPE_LENGTH (type) = 1;
+ TYPE_CODE (type) = TYPE_CODE_METHOD;
+
+ TYPE_MAIN_VARIANT (type) = lookup_method_type (domain, to_type, args);
+}
+
+/* Smash TYPE to be a type of reference to TO_TYPE.
+ If TO_TYPE is not permanent and has no pointer-type yet,
+ record TYPE as its pointer-type. */
+
+void
+smash_to_reference_type (type, to_type)
+ struct type *type, *to_type;
+{
+ int type_permanent = (TYPE_FLAGS (type) & TYPE_FLAG_PERM);
+
+ bzero (type, sizeof (struct type));
+ TYPE_TARGET_TYPE (type) = to_type;
+ /* We assume the machine has only one representation for pointers! */
+ TYPE_LENGTH (type) = sizeof (char *);
+ TYPE_CODE (type) = TYPE_CODE_REF;
+
+ TYPE_MAIN_VARIANT (type) = type;
+
+ if (type_permanent)
+ TYPE_FLAGS (type) |= TYPE_FLAG_PERM;
+
+ if (TYPE_REFERENCE_TYPE (to_type) == 0
+ && (!(TYPE_FLAGS (to_type) & TYPE_FLAG_PERM)
+ || type_permanent))
+ {
+ TYPE_REFERENCE_TYPE (to_type) = type;
+ }
+}
+
+/* Smash TYPE to be a type of functions returning TO_TYPE.
+ If TO_TYPE is not permanent and has no function-type yet,
+ record TYPE as its function-type. */
+
+void
+smash_to_function_type (type, to_type)
+ struct type *type, *to_type;
+{
+ int type_permanent = (TYPE_FLAGS (type) & TYPE_FLAG_PERM);
+
+ bzero (type, sizeof (struct type));
+ TYPE_TARGET_TYPE (type) = to_type;
+ TYPE_LENGTH (type) = 1;
+ TYPE_CODE (type) = TYPE_CODE_FUNC;
+ TYPE_NFIELDS (type) = 0;
+
+ if (type_permanent)
+ TYPE_FLAGS (type) |= TYPE_FLAG_PERM;
+
+ if (TYPE_FUNCTION_TYPE (to_type) == 0
+ && (!(TYPE_FLAGS (to_type) & TYPE_FLAG_PERM)
+ || type_permanent))
+ {
+ TYPE_FUNCTION_TYPE (to_type) = type;
+ }
+}
+
+/* Find which partial symtab on the partial_symtab_list contains
+ PC. Return 0 if none. */
+
+struct partial_symtab *
+find_pc_psymtab (pc)
+ register CORE_ADDR pc;
+{
+ register struct partial_symtab *ps;
+
+ for (ps = partial_symtab_list; ps; ps = ps->next)
+ if (pc >= ps->textlow && pc < ps->texthigh)
+ return ps;
+
+ return 0;
+}
+
+/* Find which partial symbol within a psymtab contains PC. Return 0
+ if none. Check all psymtabs if PSYMTAB is 0. */
+struct partial_symbol *
+find_pc_psymbol (psymtab, pc)
+ struct partial_symtab *psymtab;
+ CORE_ADDR pc;
+{
+ struct partial_symbol *best, *p;
+ int best_pc;
+
+ if (!psymtab)
+ psymtab = find_pc_psymtab (pc);
+ if (!psymtab)
+ return 0;
+
+ best_pc = psymtab->textlow - 1;
+
+ for (p = static_psymbols.list + psymtab->statics_offset;
+ (p - (static_psymbols.list + psymtab->statics_offset)
+ < psymtab->n_static_syms);
+ p++)
+ if (SYMBOL_NAMESPACE (p) == VAR_NAMESPACE
+ && SYMBOL_CLASS (p) == LOC_BLOCK
+ && pc >= SYMBOL_VALUE (p)
+ && SYMBOL_VALUE (p) > best_pc)
+ {
+ best_pc = SYMBOL_VALUE (p);
+ best = p;
+ }
+ if (best_pc == psymtab->textlow - 1)
+ return 0;
+ return best;
+}
+
+
+static struct symbol *lookup_block_symbol ();
+
+/* Find the definition for a specified symbol name NAME
+ in namespace NAMESPACE, visible from lexical block BLOCK.
+ Returns the struct symbol pointer, or zero if no symbol is found.
+ C++: if IS_A_FIELD_OF_THIS is nonzero on entry, check to see if
+ NAME is a field of the current implied argument `this'. If so set
+ *IS_A_FIELD_OF_THIS to 1, otherwise set it to zero.
+ BLOCK_FOUND is set to the block in which NAME is found (in the case of
+ a field of `this', value_of_this sets BLOCK_FOUND to the proper value.) */
+
+struct symbol *
+lookup_symbol (name, block, namespace, is_a_field_of_this)
+ char *name;
+ register struct block *block;
+ enum namespace namespace;
+ int *is_a_field_of_this;
+{
+ register int i, n;
+ register struct symbol *sym;
+ register struct symtab *s;
+ register struct partial_symtab *ps;
+ register struct partial_symbol *psym;
+ struct blockvector *bv;
+
+ /* Search specified block and its superiors. */
+
+ while (block != 0)
+ {
+ sym = lookup_block_symbol (block, name, namespace);
+ if (sym)
+ {
+ block_found = block;
+ return sym;
+ }
+ block = BLOCK_SUPERBLOCK (block);
+ }
+
+ /* C++: If requested to do so by the caller,
+ check to see if NAME is a field of `this'. */
+ if (is_a_field_of_this)
+ {
+ struct value *v = value_of_this (0);
+
+ *is_a_field_of_this = 0;
+ if (v && check_field (v, name))
+ {
+ *is_a_field_of_this = 1;
+ return 0;
+ }
+ }
+
+ /* Now search all global blocks. Do the symtab's first, then
+ check the psymtab's */
+
+ for (s = symtab_list; s; s = s->next)
+ {
+ bv = BLOCKVECTOR (s);
+ block = BLOCKVECTOR_BLOCK (bv, 0);
+ sym = lookup_block_symbol (block, name, namespace);
+ if (sym)
+ {
+ block_found = block;
+ return sym;
+ }
+ }
+
+ /* Check for the possibility of the symbol being a global function
+ that is stored on the misc function vector. Eventually, all
+ global symbols might be resolved in this way. */
+
+ if (namespace == VAR_NAMESPACE)
+ {
+ int index = lookup_misc_func (name);
+
+ if (index == -1)
+ { /* Look for a mangled C++ name for NAME. */
+ int name_len = strlen (name);
+ for (index = misc_function_count; --index >= 0; )
+ /* Assume orginal name is prefix of mangled name. */
+ if (!strncmp (misc_function_vector[index].name, name, name_len))
+ {
+ char *demangled =
+ cplus_demangle(misc_function_vector[index].name, -1);
+ if (demangled != NULL)
+ {
+ int cond = strcmp (demangled, name);
+ free (demangled);
+ if (!cond)
+ break;
+ }
+ }
+ /* Loop terminates on no match with index == -1. */
+ }
+
+ if (index != -1)
+ {
+ ps = find_pc_psymtab (misc_function_vector[index].address);
+ if (ps && !ps->readin)
+ {
+ s = psymtab_to_symtab (ps);
+ bv = BLOCKVECTOR (s);
+ block = BLOCKVECTOR_BLOCK (bv, 0);
+ sym = lookup_block_symbol (block, name, namespace);
+ /* sym == 0 if symbol was found in the psymtab but not
+ in the symtab.
+ Return 0 to use the misc_function definition of "foo_".
+
+ This happens for Fortran "foo_" symbols,
+ which are "foo" in the symtab.
+
+ This can also happen if "asm" is used to make a
+ regular symbol but not a debugging symbol, e.g.
+ asm(".globl _main");
+ asm("_main:");
+ */
+
+ return sym;
+ }
+ }
+ }
+
+ if (psym = lookup_partial_symbol (name, 1, namespace))
+ {
+ ps = psym->pst;
+ s = psymtab_to_symtab(ps);
+ bv = BLOCKVECTOR (s);
+ block = BLOCKVECTOR_BLOCK (bv, 0);
+ sym = lookup_block_symbol (block, name, namespace);
+ if (!sym)
+ fatal ("Internal: global symbol found in psymtab but not in symtab");
+ return sym;
+ }
+
+ /* Now search all per-file blocks.
+ Not strictly correct, but more useful than an error.
+ Do the symtabs first, then check the psymtabs */
+
+ for (s = symtab_list; s; s = s->next)
+ {
+ bv = BLOCKVECTOR (s);
+ block = BLOCKVECTOR_BLOCK (bv, 1);
+ sym = lookup_block_symbol (block, name, namespace);
+ if (sym)
+ {
+ block_found = block;
+ return sym;
+ }
+ }
+
+ if (psym = lookup_partial_symbol(name, 0, namespace))
+ {
+ ps = psym->pst;
+ s = psymtab_to_symtab(ps);
+ bv = BLOCKVECTOR (s);
+ block = BLOCKVECTOR_BLOCK (bv, 1);
+ sym = lookup_block_symbol (block, name, namespace);
+ if (!sym)
+ fatal ("Internal: static symbol found in psymtab but not in symtab");
+ return sym;
+ }
+
+ return 0;
+}
+
+/* Look, in partial_symtab PST, for symbol NAME. Check the global
+ symbols if GLOBAL, the static symbols if not */
+
+static struct partial_symbol *
+lookup_partial_symbol (name, global, namespace)
+ register char *name;
+ register int global;
+ register enum namespace namespace;
+{
+ register struct partial_symbol *start, *psym;
+ register struct partial_symbol *top, *bottom, *center;
+ register struct partial_symtab *pst;
+ register int length;
+
+ if (global)
+ {
+ start = global_psymbols.list;
+ length = global_psymbols.next - start;
+ }
+ else
+ {
+ start = static_psymbols.list;
+ length = static_psymbols.next - start;
+ }
+
+ if (!length)
+ return (struct partial_symbol *) 0;
+
+ /* Binary search. This search is guarranteed to end with center
+ pointing at the earliest partial symbol with the correct
+ name. At that point *all* partial symbols with that name
+ will be checked against the correct namespace. */
+ bottom = start;
+ top = start + length - 1;
+ while (top > bottom)
+ {
+ center = bottom + (top - bottom) / 2;
+
+ assert (center < top);
+
+ if (strcmp (SYMBOL_NAME (center), name) >= 0)
+ top = center;
+ else
+ bottom = center + 1;
+ }
+ assert (top == bottom);
+
+ while (strcmp (SYMBOL_NAME (top), name) == 0)
+ {
+ if (!top->pst->readin && SYMBOL_NAMESPACE (top) == namespace)
+ return top;
+ top ++;
+ }
+
+ return (struct partial_symbol *) 0;
+}
+
+/* Look for a symbol in block BLOCK. */
+
+static struct symbol *
+lookup_block_symbol (block, name, namespace)
+ register struct block *block;
+ char *name;
+ enum namespace namespace;
+{
+ register int bot, top, inc;
+ register struct symbol *sym, *parameter_sym;
+
+ top = BLOCK_NSYMS (block);
+ bot = 0;
+
+ /* If the blocks's symbols were sorted, start with a binary search. */
+
+ if (BLOCK_SHOULD_SORT (block))
+ {
+ /* First, advance BOT to not far before
+ the first symbol whose name is NAME. */
+
+ while (1)
+ {
+ inc = (top - bot + 1);
+ /* No need to keep binary searching for the last few bits worth. */
+ if (inc < 4)
+ break;
+ inc = (inc >> 1) + bot;
+ sym = BLOCK_SYM (block, inc);
+ if (SYMBOL_NAME (sym)[0] < name[0])
+ bot = inc;
+ else if (SYMBOL_NAME (sym)[0] > name[0])
+ top = inc;
+ else if (strcmp (SYMBOL_NAME (sym), name) < 0)
+ bot = inc;
+ else
+ top = inc;
+ }
+
+ /* Now scan forward until we run out of symbols,
+ find one whose name is greater than NAME,
+ or find one we want.
+ If there is more than one symbol with the right name and namespace,
+ we return the first one. dbxread.c is careful to make sure
+ that if one is a register then it comes first. */
+
+ top = BLOCK_NSYMS (block);
+ while (bot < top)
+ {
+ sym = BLOCK_SYM (block, bot);
+ inc = SYMBOL_NAME (sym)[0] - name[0];
+ if (inc == 0)
+ inc = strcmp (SYMBOL_NAME (sym), name);
+ if (inc == 0 && SYMBOL_NAMESPACE (sym) == namespace)
+ return sym;
+ if (inc > 0)
+ return 0;
+ bot++;
+ }
+ return 0;
+ }
+
+ /* Here if block isn't sorted.
+ This loop is equivalent to the loop above,
+ but hacked greatly for speed.
+
+ Note that parameter symbols do not always show up last in the
+ list; this loop makes sure to take anything else other than
+ parameter symbols first; it only uses parameter symbols as a
+ last resort. Note that this only takes up extra computation
+ time on a match. */
+
+ parameter_sym = (struct symbol *) 0;
+ top = BLOCK_NSYMS (block);
+ inc = name[0];
+ while (bot < top)
+ {
+ sym = BLOCK_SYM (block, bot);
+ if (SYMBOL_NAME (sym)[0] == inc
+ && !strcmp (SYMBOL_NAME (sym), name)
+ && SYMBOL_NAMESPACE (sym) == namespace)
+ {
+ if (SYMBOL_CLASS (sym) == LOC_ARG
+ || SYMBOL_CLASS (sym) == LOC_REF_ARG
+ || SYMBOL_CLASS (sym) == LOC_REGPARM)
+ parameter_sym = sym;
+ else
+ return sym;
+ }
+ bot++;
+ }
+ return parameter_sym; /* Will be 0 if not found. */
+}
+
+/* Return the symbol for the function which contains a specified
+ lexical block, described by a struct block BL. */
+
+struct symbol *
+block_function (bl)
+ struct block *bl;
+{
+ while (BLOCK_FUNCTION (bl) == 0 && BLOCK_SUPERBLOCK (bl) != 0)
+ bl = BLOCK_SUPERBLOCK (bl);
+
+ return BLOCK_FUNCTION (bl);
+}
+
+/* Subroutine of find_pc_line */
+
+struct symtab *
+find_pc_symtab (pc)
+ register CORE_ADDR pc;
+{
+ register struct block *b;
+ struct blockvector *bv;
+ register struct symtab *s;
+ register struct partial_symtab *ps;
+
+ /* Search all symtabs for one whose file contains our pc */
+
+ for (s = symtab_list; s; s = s->next)
+ {
+ bv = BLOCKVECTOR (s);
+ b = BLOCKVECTOR_BLOCK (bv, 0);
+ if (BLOCK_START (b) <= pc
+ && BLOCK_END (b) > pc)
+ break;
+ }
+
+ if (!s)
+ {
+ ps = find_pc_psymtab (pc);
+ if (ps && ps->readin)
+ fatal ("Internal error: pc in read in psymtab, but not in symtab.");
+
+ if (ps)
+ s = psymtab_to_symtab (ps);
+ }
+
+ return s;
+}
+
+/* Find the source file and line number for a given PC value.
+ Return a structure containing a symtab pointer, a line number,
+ and a pc range for the entire source line.
+ The value's .pc field is NOT the specified pc.
+ NOTCURRENT nonzero means, if specified pc is on a line boundary,
+ use the line that ends there. Otherwise, in that case, the line
+ that begins there is used. */
+
+struct symtab_and_line
+find_pc_line (pc, notcurrent)
+ CORE_ADDR pc;
+ int notcurrent;
+{
+ struct symtab *s;
+ register struct linetable *l;
+ register int len;
+ register int i;
+ register struct linetable_entry *item;
+ struct symtab_and_line value;
+ struct blockvector *bv;
+
+ /* Info on best line seen so far, and where it starts, and its file. */
+
+ int best_line = 0;
+ CORE_ADDR best_pc = 0;
+ CORE_ADDR best_end = 0;
+ struct symtab *best_symtab = 0;
+
+ /* Store here the first line number
+ of a file which contains the line at the smallest pc after PC.
+ If we don't find a line whose range contains PC,
+ we will use a line one less than this,
+ with a range from the start of that file to the first line's pc. */
+ int alt_line = 0;
+ CORE_ADDR alt_pc = 0;
+ struct symtab *alt_symtab = 0;
+
+ /* Info on best line seen in this file. */
+
+ int prev_line;
+ CORE_ADDR prev_pc;
+
+ /* Info on first line of this file. */
+
+ int first_line;
+ CORE_ADDR first_pc;
+
+ /* If this pc is not from the current frame,
+ it is the address of the end of a call instruction.
+ Quite likely that is the start of the following statement.
+ But what we want is the statement containing the instruction.
+ Fudge the pc to make sure we get that. */
+
+ if (notcurrent) pc -= 1;
+
+ s = find_pc_symtab (pc);
+ if (s == 0)
+ {
+ value.symtab = 0;
+ value.line = 0;
+ value.pc = pc;
+ value.end = 0;
+ return value;
+ }
+
+ bv = BLOCKVECTOR (s);
+
+ /* Look at all the symtabs that share this blockvector.
+ They all have the same apriori range, that we found was right;
+ but they have different line tables. */
+
+ for (; s && BLOCKVECTOR (s) == bv; s = s->next)
+ {
+ /* Find the best line in this symtab. */
+ l = LINETABLE (s);
+ len = l->nitems;
+ prev_line = -1;
+ first_line = -1;
+ for (i = 0; i < len; i++)
+ {
+ item = &(l->item[i]);
+
+ if (first_line < 0)
+ {
+ first_line = item->line;
+ first_pc = item->pc;
+ }
+ /* Return the last line that did not start after PC. */
+ if (pc >= item->pc)
+ {
+ prev_line = item->line;
+ prev_pc = item->pc;
+ }
+ else
+ break;
+ }
+
+ /* Is this file's best line closer than the best in the other files?
+ If so, record this file, and its best line, as best so far. */
+ if (prev_line >= 0 && prev_pc > best_pc)
+ {
+ best_pc = prev_pc;
+ best_line = prev_line;
+ best_symtab = s;
+ if (i < len)
+ best_end = item->pc;
+ else
+ best_end = 0;
+ }
+ /* Is this file's first line closer than the first lines of other files?
+ If so, record this file, and its first line, as best alternate. */
+ if (first_line >= 0 && first_pc > pc
+ && (alt_pc == 0 || first_pc < alt_pc))
+ {
+ alt_pc = first_pc;
+ alt_line = first_line;
+ alt_symtab = s;
+ }
+ }
+ if (best_symtab == 0)
+ {
+ value.symtab = alt_symtab;
+ value.line = alt_line - 1;
+ value.pc = BLOCK_END (BLOCKVECTOR_BLOCK (bv, 0));
+ value.end = alt_pc;
+ }
+ else
+ {
+ value.symtab = best_symtab;
+ value.line = best_line;
+ value.pc = best_pc;
+ value.end = (best_end ? best_end
+ : (alt_pc ? alt_pc
+ : BLOCK_END (BLOCKVECTOR_BLOCK (bv, 0))));
+ }
+ return value;
+}
+
+/* Find the PC value for a given source file and line number.
+ Returns zero for invalid line number.
+ The source file is specified with a struct symtab. */
+
+CORE_ADDR
+find_line_pc (symtab, line)
+ struct symtab *symtab;
+ int line;
+{
+ register struct linetable *l;
+ register int index;
+ int dummy;
+
+ if (symtab == 0)
+ return 0;
+ l = LINETABLE (symtab);
+ index = find_line_common(l, line, &dummy);
+ return index ? l->item[index].pc : 0;
+}
+
+/* Find the range of pc values in a line.
+ Store the starting pc of the line into *STARTPTR
+ and the ending pc (start of next line) into *ENDPTR.
+ Returns 1 to indicate success.
+ Returns 0 if could not find the specified line. */
+
+int
+find_line_pc_range (symtab, thisline, startptr, endptr)
+ struct symtab *symtab;
+ int thisline;
+ CORE_ADDR *startptr, *endptr;
+{
+ register struct linetable *l;
+ register int index;
+ int exact_match; /* did we get an exact linenumber match */
+ register CORE_ADDR prev_pc;
+ CORE_ADDR last_pc;
+
+ if (symtab == 0)
+ return 0;
+
+ l = LINETABLE (symtab);
+ index = find_line_common (l, thisline, &exact_match);
+ if (index)
+ {
+ *startptr = l->item[index].pc;
+ /* If we have not seen an entry for the specified line,
+ assume that means the specified line has zero bytes. */
+ if (!exact_match || index == l->nitems-1)
+ *endptr = *startptr;
+ else
+ /* Perhaps the following entry is for the following line.
+ It's worth a try. */
+ if (l->item[index+1].line == thisline + 1)
+ *endptr = l->item[index+1].pc;
+ else
+ *endptr = find_line_pc (symtab, thisline+1);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Given a line table and a line number, return the index into the line
+ table for the pc of the nearest line whose number is >= the specified one.
+ Return 0 if none is found. The value is never zero is it is an index.
+
+ Set *EXACT_MATCH nonzero if the value returned is an exact match. */
+
+static int
+find_line_common (l, lineno, exact_match)
+ register struct linetable *l;
+ register int lineno;
+ int *exact_match;
+{
+ register int i;
+ register int len;
+
+ /* BEST is the smallest linenumber > LINENO so far seen,
+ or 0 if none has been seen so far.
+ BEST_INDEX identifies the item for it. */
+
+ int best_index = 0;
+ int best = 0;
+
+ int nextline = -1;
+
+ if (lineno <= 0)
+ return 0;
+
+ len = l->nitems;
+ for (i = 0; i < len; i++)
+ {
+ register struct linetable_entry *item = &(l->item[i]);
+
+ if (item->line == lineno)
+ {
+ *exact_match = 1;
+ return i;
+ }
+
+ if (item->line > lineno && (best == 0 || item->line < best))
+ {
+ best = item->line;
+ best_index = i;
+ }
+ }
+
+ /* If we got here, we didn't get an exact match. */
+
+ *exact_match = 0;
+ return best_index;
+}
+
+int
+find_pc_line_pc_range (pc, startptr, endptr)
+ CORE_ADDR pc;
+ CORE_ADDR *startptr, *endptr;
+{
+ struct symtab_and_line sal;
+ sal = find_pc_line (pc, 0);
+ *startptr = sal.pc;
+ *endptr = sal.end;
+ return sal.symtab != 0;
+}
+
+/* Parse a string that specifies a line number.
+ Pass the address of a char * variable; that variable will be
+ advanced over the characters actually parsed.
+
+ The string can be:
+
+ LINENUM -- that line number in current file. PC returned is 0.
+ FILE:LINENUM -- that line in that file. PC returned is 0.
+ FUNCTION -- line number of openbrace of that function.
+ PC returned is the start of the function.
+ FILE:FUNCTION -- likewise, but prefer functions in that file.
+ *EXPR -- line in which address EXPR appears.
+
+ FUNCTION may be an undebuggable function found in misc_function_vector.
+
+ If the argument FUNFIRSTLINE is nonzero, we want the first line
+ of real code inside a function when a function is specified.
+
+ DEFAULT_SYMTAB specifies the file to use if none is specified.
+ It defaults to current_source_symtab.
+ DEFAULT_LINE specifies the line number to use for relative
+ line numbers (that start with signs). Defaults to current_source_line.
+
+ Note that it is possible to return zero for the symtab
+ if no file is validly specified. Callers must check that.
+ Also, the line number returned may be invalid. */
+
+struct symtabs_and_lines
+decode_line_1 (argptr, funfirstline, default_symtab, default_line)
+ char **argptr;
+ int funfirstline;
+ struct symtab *default_symtab;
+ int default_line;
+{
+ struct symtabs_and_lines decode_line_2 ();
+ struct symtabs_and_lines values;
+ struct symtab_and_line value;
+ register char *p, *p1;
+ register struct symtab *s;
+ register struct symbol *sym;
+ register CORE_ADDR pc;
+ register int i;
+ char *copy;
+ struct symbol *sym_class;
+ char *class_name, *method_name, *phys_name;
+ int method_counter;
+ int i1;
+ struct symbol **sym_arr;
+ struct type *t, *field;
+ char **physnames;
+
+ /* Defaults have defaults. */
+
+ if (default_symtab == 0)
+ {
+ default_symtab = current_source_symtab;
+ default_line = current_source_line;
+ }
+
+ /* See if arg is *PC */
+
+ if (**argptr == '*')
+ {
+ (*argptr)++;
+ pc = parse_and_eval_address_1 (argptr);
+ values.sals = (struct symtab_and_line *)
+ malloc (sizeof (struct symtab_and_line));
+ values.nelts = 1;
+ values.sals[0] = find_pc_line (pc, 0);
+ values.sals[0].pc = pc;
+ return values;
+ }
+
+ /* Maybe arg is FILE : LINENUM or FILE : FUNCTION */
+
+ s = 0;
+
+ for (p = *argptr; *p; p++)
+ {
+ if (p[0] == ':' || p[0] == ' ' || p[0] == '\t')
+ break;
+ }
+ while (p[0] == ' ' || p[0] == '\t') p++;
+
+ if (p[0] == ':')
+ {
+
+ /* C++ */
+ if (p[1] ==':')
+ {
+ /* Extract the class name. */
+ p1 = p;
+ while (p != *argptr && p[-1] == ' ') --p;
+ copy = (char *) alloca (p - *argptr + 1);
+ bcopy (*argptr, copy, p - *argptr);
+ copy[p - *argptr] = 0;
+
+ /* Discard the class name from the arg. */
+ p = p1 + 2;
+ while (*p == ' ' || *p == '\t') p++;
+ *argptr = p;
+
+ sym_class = lookup_symbol (copy, 0, STRUCT_NAMESPACE, 0);
+
+ if (sym_class &&
+ (TYPE_CODE (SYMBOL_TYPE (sym_class)) == TYPE_CODE_STRUCT
+ || TYPE_CODE (SYMBOL_TYPE (sym_class)) == TYPE_CODE_UNION))
+ {
+ /* Arg token is not digits => try it as a function name
+ Find the next token (everything up to end or next whitespace). */
+ p = *argptr;
+ while (*p && *p != ' ' && *p != '\t' && *p != ',' && *p !=':') p++;
+ copy = (char *) alloca (p - *argptr + 1);
+ bcopy (*argptr, copy, p - *argptr);
+ copy[p - *argptr] = '\0';
+
+ /* no line number may be specified */
+ while (*p == ' ' || *p == '\t') p++;
+ *argptr = p;
+
+ sym = 0;
+ i1 = 0; /* counter for the symbol array */
+ t = SYMBOL_TYPE (sym_class);
+ sym_arr = (struct symbol **) alloca(TYPE_NFN_FIELDS_TOTAL (t) * sizeof(struct symbol*));
+ physnames = (char **) alloca (TYPE_NFN_FIELDS_TOTAL (t) * sizeof(char*));
+
+ if (destructor_name_p (copy, t))
+ {
+ /* destructors are a special case. */
+ struct fn_field *f = TYPE_FN_FIELDLIST1 (t, 0);
+ int len = TYPE_FN_FIELDLIST_LENGTH (t, 0) - 1;
+ phys_name = TYPE_FN_FIELD_PHYSNAME (f, len);
+ physnames[i1] = (char *)alloca (strlen (phys_name) + 1);
+ strcpy (physnames[i1], phys_name);
+ sym_arr[i1] = lookup_symbol (phys_name, SYMBOL_BLOCK_VALUE (sym_class), VAR_NAMESPACE, 0);
+ if (sym_arr[i1]) i1++;
+ }
+ else while (t)
+ {
+ class_name = TYPE_NAME (t);
+ /* Ignore this class if it doesn't have a name.
+ This prevents core dumps, but is just a workaround
+ because we might not find the function in
+ certain cases, such as
+ struct D {virtual int f();}
+ struct C : D {virtual int g();}
+ (in this case g++ 1.35.1- does not put out a name
+ for D as such, it defines type 19 (for example) in
+ the same stab as C, and then does a
+ .stabs "D:T19" and a .stabs "D:t19".
+ Thus
+ "break C::f" should not be looking for field f in
+ the class named D,
+ but just for the field f in the baseclasses of C
+ (no matter what their names).
+
+ However, I don't know how to replace the code below
+ that depends on knowing the name of D. */
+ if (class_name)
+ {
+ /* We just want the class name. In the context
+ of C++, stripping off "struct " is always
+ sensible. */
+ if (strncmp("struct ", class_name, 7) == 0)
+ class_name += 7;
+ if (strncmp("union ", class_name, 6) == 0)
+ class_name += 6;
+
+ sym_class = lookup_symbol (class_name, 0, STRUCT_NAMESPACE, 0);
+ for (method_counter = TYPE_NFN_FIELDS (SYMBOL_TYPE (sym_class)) - 1;
+ method_counter >= 0;
+ --method_counter)
+ {
+ int field_counter;
+ struct fn_field *f =
+ TYPE_FN_FIELDLIST1 (SYMBOL_TYPE (sym_class), method_counter);
+
+ method_name = TYPE_FN_FIELDLIST_NAME (SYMBOL_TYPE (sym_class), method_counter);
+ if (!strcmp (copy, method_name))
+ /* Find all the fields with that name. */
+ for (field_counter = TYPE_FN_FIELDLIST_LENGTH (SYMBOL_TYPE (sym_class), method_counter) - 1;
+ field_counter >= 0;
+ --field_counter)
+ {
+ phys_name = TYPE_FN_FIELD_PHYSNAME (f, field_counter);
+ physnames[i1] = (char*) alloca (strlen (phys_name) + 1);
+ strcpy (physnames[i1], phys_name);
+ sym_arr[i1] = lookup_symbol (phys_name, SYMBOL_BLOCK_VALUE (sym_class), VAR_NAMESPACE, 0);
+ if (sym_arr[i1]) i1++;
+ }
+ }
+ }
+ if (TYPE_N_BASECLASSES (t))
+ t = TYPE_BASECLASS(t, 1);
+ else
+ break;
+ }
+
+ if (i1 == 1)
+ {
+ /* There is exactly one field with that name. */
+ sym = sym_arr[0];
+
+ if (sym && SYMBOL_CLASS (sym) == LOC_BLOCK)
+ {
+ /* Arg is the name of a function */
+ pc = BLOCK_START (SYMBOL_BLOCK_VALUE (sym)) + FUNCTION_START_OFFSET;
+ if (funfirstline)
+ SKIP_PROLOGUE (pc);
+ values.sals = (struct symtab_and_line *)malloc (sizeof (struct symtab_and_line));
+ values.nelts = 1;
+ values.sals[0] = find_pc_line (pc, 0);
+ values.sals[0].pc = (values.sals[0].end && values.sals[0].pc != pc) ? values.sals[0].end : pc;
+ }
+ else
+ {
+ values.nelts = 0;
+ }
+ return values;
+ }
+ if (i1 > 0)
+ {
+ /* There is more than one field with that name
+ (overloaded). Ask the user which one to use. */
+ return decode_line_2 (argptr, sym_arr, physnames,
+ i1, funfirstline);
+ }
+ else
+ error ("that class does not have any method named %s",copy);
+ }
+ else
+ error("no class, struct, or union named %s", copy );
+ }
+ /* end of C++ */
+
+
+ /* Extract the file name. */
+ p1 = p;
+ while (p != *argptr && p[-1] == ' ') --p;
+ copy = (char *) alloca (p - *argptr + 1);
+ bcopy (*argptr, copy, p - *argptr);
+ copy[p - *argptr] = 0;
+
+ /* Find that file's data. */
+ s = lookup_symtab (copy);
+ if (s == 0)
+ {
+ if (symtab_list == 0 && partial_symtab_list == 0)
+ error ("No symbol table is loaded. Use the \"symbol-file\" command.");
+ error ("No source file named %s.", copy);
+ }
+
+ /* Discard the file name from the arg. */
+ p = p1 + 1;
+ while (*p == ' ' || *p == '\t') p++;
+ *argptr = p;
+ }
+
+ /* S is specified file's symtab, or 0 if no file specified.
+ arg no longer contains the file name. */
+
+ /* Check whether arg is all digits (and sign) */
+
+ p = *argptr;
+ if (*p == '-' || *p == '+') p++;
+ while (*p >= '0' && *p <= '9')
+ p++;
+
+ if (p != *argptr && (*p == 0 || *p == ' ' || *p == '\t' || *p == ','))
+ {
+ /* We found a token consisting of all digits -- at least one digit. */
+ enum sign {none, plus, minus} sign = none;
+
+ /* This is where we need to make sure that we have good defaults.
+ We must guarrantee that this section of code is never executed
+ when we are called with just a function name, since
+ select_source_symtab calls us with such an argument */
+
+ if (s == 0 && default_symtab == 0)
+ {
+ if (symtab_list == 0 && partial_symtab_list == 0)
+ error ("No symbol table is loaded. Use the \"symbol-file\" command.");
+ select_source_symtab (0);
+ default_symtab = current_source_symtab;
+ default_line = current_source_line;
+ }
+
+ if (**argptr == '+')
+ sign = plus, (*argptr)++;
+ else if (**argptr == '-')
+ sign = minus, (*argptr)++;
+ value.line = atoi (*argptr);
+ switch (sign)
+ {
+ case plus:
+ if (p == *argptr)
+ value.line = 5;
+ if (s == 0)
+ value.line = default_line + value.line;
+ break;
+ case minus:
+ if (p == *argptr)
+ value.line = 15;
+ if (s == 0)
+ value.line = default_line - value.line;
+ else
+ value.line = 1;
+ break;
+ }
+
+ while (*p == ' ' || *p == '\t') p++;
+ *argptr = p;
+ if (s == 0)
+ s = default_symtab;
+ value.symtab = s;
+ value.pc = 0;
+ values.sals = (struct symtab_and_line *)malloc (sizeof (struct symtab_and_line));
+ values.sals[0] = value;
+ values.nelts = 1;
+ return values;
+ }
+
+ /* Arg token is not digits => try it as a function name
+ Find the next token (everything up to end or next whitespace). */
+ p = *argptr;
+ while (*p && *p != ' ' && *p != '\t' && *p != ',') p++;
+ copy = (char *) alloca (p - *argptr + 1);
+ bcopy (*argptr, copy, p - *argptr);
+ copy[p - *argptr] = 0;
+ while (*p == ' ' || *p == '\t') p++;
+ *argptr = p;
+
+ /* Look up that token as a function.
+ If file specified, use that file's per-file block to start with. */
+
+ if (s == 0)
+ /* use current file as default if none is specified. */
+ s = default_symtab;
+
+ sym = lookup_symbol (copy, s ? BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), 1) : 0,
+ VAR_NAMESPACE, 0);
+
+ if (sym && SYMBOL_CLASS (sym) == LOC_BLOCK)
+ {
+ /* Arg is the name of a function */
+ pc = BLOCK_START (SYMBOL_BLOCK_VALUE (sym)) + FUNCTION_START_OFFSET;
+ if (funfirstline)
+ SKIP_PROLOGUE (pc);
+ value = find_pc_line (pc, 0);
+#ifdef PROLOGUE_FIRSTLINE_OVERLAP
+ /* Convex: no need to suppress code on first line, if any */
+ value.pc = pc;
+#else
+ value.pc = (value.end && value.pc != pc) ? value.end : pc;
+#endif
+ values.sals = (struct symtab_and_line *)malloc (sizeof (struct symtab_and_line));
+ values.sals[0] = value;
+ values.nelts = 1;
+ return values;
+ }
+
+ if (sym)
+ error ("%s is not a function.", copy);
+
+ if (symtab_list == 0 && partial_symtab_list == 0)
+ error ("No symbol table is loaded. Use the \"symbol-file\" command.");
+
+ if ((i = lookup_misc_func (copy)) >= 0)
+ {
+ value.symtab = 0;
+ value.line = 0;
+ value.pc = misc_function_vector[i].address + FUNCTION_START_OFFSET;
+ if (funfirstline)
+ SKIP_PROLOGUE (value.pc);
+ values.sals = (struct symtab_and_line *)malloc (sizeof (struct symtab_and_line));
+ values.sals[0] = value;
+ values.nelts = 1;
+ return values;
+ }
+
+ error ("Function %s not defined.", copy);
+}
+
+struct symtabs_and_lines
+decode_line_spec (string, funfirstline)
+ char *string;
+ int funfirstline;
+{
+ struct symtabs_and_lines sals;
+ if (string == 0)
+ error ("Empty line specification.");
+ sals = decode_line_1 (&string, funfirstline,
+ current_source_symtab, current_source_line);
+ if (*string)
+ error ("Junk at end of line specification: %s", string);
+ return sals;
+}
+
+/* Given a list of NELTS symbols in sym_arr (with corresponding
+ mangled names in physnames), return a list of lines to operate on
+ (ask user if necessary). */
+struct symtabs_and_lines
+decode_line_2 (argptr, sym_arr, physnames, nelts, funfirstline)
+ char **argptr;
+ struct symbol *sym_arr[];
+ char *physnames[];
+ int nelts;
+ int funfirstline;
+{
+ char *getenv();
+ struct symtabs_and_lines values, return_values;
+ register CORE_ADDR pc;
+ char *args, *arg1, *command_line_input ();
+ int i;
+ char *prompt;
+
+ values.sals = (struct symtab_and_line *) alloca (nelts * sizeof(struct symtab_and_line));
+ return_values.sals = (struct symtab_and_line *) malloc (nelts * sizeof(struct symtab_and_line));
+
+ i = 0;
+ printf("[0] cancel\n[1] all\n");
+ while (i < nelts)
+ {
+ if (sym_arr[i] && SYMBOL_CLASS (sym_arr[i]) == LOC_BLOCK)
+ {
+ /* Arg is the name of a function */
+ pc = BLOCK_START (SYMBOL_BLOCK_VALUE (sym_arr[i]))
+ + FUNCTION_START_OFFSET;
+ if (funfirstline)
+ SKIP_PROLOGUE (pc);
+ values.sals[i] = find_pc_line (pc, 0);
+ values.sals[i].pc = (values.sals[i].end && values.sals[i].pc != pc) ? values.sals[i].end : pc;
+ printf("[%d] file:%s; line number:%d\n",
+ (i+2), values.sals[i].symtab->filename, values.sals[i].line);
+ }
+ else printf ("?HERE\n");
+ i++;
+ }
+
+ if ((prompt = getenv ("PS2")) == NULL)
+ {
+ prompt = ">";
+ }
+ printf("%s ",prompt);
+ fflush(stdout);
+
+ args = command_line_input (0, 0);
+
+ if (args == 0)
+ error_no_arg ("one or more choice numbers");
+
+ i = 0;
+ while (*args)
+ {
+ int num;
+
+ arg1 = args;
+ while (*arg1 >= '0' && *arg1 <= '9') arg1++;
+ if (*arg1 && *arg1 != ' ' && *arg1 != '\t')
+ error ("Arguments must be choice numbers.");
+
+ num = atoi (args);
+
+ if (num == 0)
+ error ("cancelled");
+ else if (num == 1)
+ {
+ bcopy (values.sals, return_values.sals, (nelts * sizeof(struct symtab_and_line)));
+ return_values.nelts = nelts;
+ return return_values;
+ }
+
+ if (num > nelts + 2)
+ {
+ printf ("No choice number %d.\n", num);
+ }
+ else
+ {
+ num -= 2;
+ if (values.sals[num].pc)
+ {
+ return_values.sals[i++] = values.sals[num];
+ values.sals[num].pc = 0;
+ }
+ else
+ {
+ printf ("duplicate request for %d ignored.\n", num);
+ }
+ }
+
+ args = arg1;
+ while (*args == ' ' || *args == '\t') args++;
+ }
+ return_values.nelts = i;
+ return return_values;
+}
+
+/* hash a symbol ("hashpjw" from Aho, Sethi & Ullman, p.436) */
+
+int
+hash_symbol(str)
+ register char *str;
+{
+ register unsigned int h = 0, g;
+ register unsigned char c;
+
+ while (c = *(unsigned char *)str++) {
+ h = (h << 4) + c;
+ if (g = h & 0xf0000000) {
+ h = h ^ (g >> 24);
+ h = h ^ g;
+ }
+ }
+ return ((int)h);
+}
+
+/* Return the index of misc function named NAME. */
+
+int
+lookup_misc_func (name)
+ register char *name;
+{
+ register int i = hash_symbol(name) & (MISC_FUNC_HASH_SIZE - 1);
+
+ if (misc_function_vector == 0)
+ error("No symbol file");
+
+ i = misc_function_hash_tab[i];
+ while (i >= 0)
+ {
+ if (strcmp(misc_function_vector[i].name, name) == 0)
+ break;
+ i = misc_function_vector[i].next;
+ }
+ return (i);
+}
+
+/*
+ * Slave routine for sources_info. Force line breaks at ,'s.
+ */
+static void
+output_source_filename (name, next)
+char *name;
+int next;
+{
+ static int column = 0;
+
+ if (column != 0 && column + strlen (name) >= 70)
+ {
+ printf_filtered ("\n");
+ column = 0;
+ }
+ else if (column != 0)
+ {
+ printf_filtered (" ");
+ column++;
+ }
+ printf_filtered ("%s", name);
+ column += strlen (name);
+ if (next)
+ {
+ printf_filtered (",");
+ column++;
+ }
+
+ if (!next) column = 0;
+}
+
+static void
+sources_info ()
+{
+ register struct symtab *s;
+ register struct partial_symtab *ps;
+ register int column = 0;
+
+ if (symtab_list == 0 && partial_symtab_list == 0)
+ {
+ printf ("No symbol table is loaded.\n");
+ return;
+ }
+
+ printf_filtered ("Source files for which symbols have been read in:\n\n");
+
+ for (s = symtab_list; s; s = s->next)
+ output_source_filename (s->filename, s->next);
+ printf_filtered ("\n\n");
+
+ printf_filtered ("Source files for which symbols will be read in on demand:\n\n");
+
+ for (ps = partial_symtab_list; ps; ps = ps->next)
+ if (!ps->readin)
+ output_source_filename (ps->filename, ps->next);
+ printf_filtered ("\n");
+}
+
+/* List all symbols (if REGEXP is 0) or all symbols matching REGEXP.
+ If CLASS is zero, list all symbols except functions and type names.
+ If CLASS is 1, list only functions.
+ If CLASS is 2, list only type names. */
+
+static void sort_block_syms ();
+
+static void
+list_symbols (regexp, class)
+ char *regexp;
+ int class;
+{
+ register struct symtab *s;
+ register struct partial_symtab *ps;
+ register struct blockvector *bv;
+ struct blockvector *prev_bv = 0;
+ register struct block *b;
+ register int i, j;
+ register struct symbol *sym;
+ struct partial_symbol *psym, *bound;
+ char *val;
+ static char *classnames[]
+ = {"variable", "function", "type", "method"};
+ int print_count = 0;
+ int found_in_file = 0;
+
+ if (regexp)
+ if (val = (char *) re_comp (regexp))
+ error ("Invalid regexp: %s", val);
+
+ /* Search through the partial_symtab_list *first* for all symbols
+ matching the regexp. That way we don't have to reproduce all of
+ the machinery below. */
+ for (psym = global_psymbols.list, bound = global_psymbols.next; ;
+ psym = static_psymbols.list, bound = static_psymbols.next)
+ {
+ for (; psym < bound; ++psym)
+ {
+ if (psym->pst->readin)
+ continue;
+
+ QUIT;
+ /* If it would match (logic taken from loop below)
+ load the file and go on to the next one */
+ if ((regexp == 0 || re_exec (SYMBOL_NAME (psym)))
+ && ((class == 0 && SYMBOL_CLASS (psym) != LOC_TYPEDEF
+ && SYMBOL_CLASS (psym) != LOC_BLOCK)
+ || (class == 1 && SYMBOL_CLASS (psym) == LOC_BLOCK)
+ || (class == 2 && SYMBOL_CLASS (psym) == LOC_TYPEDEF)
+ || (class == 3 && SYMBOL_CLASS (psym) == LOC_BLOCK)))
+ psymtab_to_symtab(psym->pst);
+ }
+ if (psym == static_psymbols.next)
+ break;
+ }
+
+ /* Printout here so as to get after the "Reading in symbols"
+ messages which will be generated above. */
+ printf_filtered (regexp
+ ? "All %ss matching regular expression \"%s\":\n"
+ : "All defined %ss:\n",
+ classnames[class],
+ regexp);
+
+ /* Here, *if* the class is correct (function only, right now), we
+ should search through the misc function vector for symbols that
+ match and call find_pc_psymtab on them. If find_pc_psymtab returns
+ 0, don't worry about it (already read in or no debugging info). */
+
+ if (class == 1)
+ {
+ for (i = 0; i < misc_function_count; i++)
+ if (regexp == 0 || re_exec (misc_function_vector[i].name))
+ {
+ ps = find_pc_psymtab (misc_function_vector[i].address);
+ if (ps && !ps->readin)
+ psymtab_to_symtab (ps);
+ }
+ }
+
+ for (s = symtab_list; s; s = s->next)
+ {
+ found_in_file = 0;
+ bv = BLOCKVECTOR (s);
+ /* Often many files share a blockvector.
+ Scan each blockvector only once so that
+ we don't get every symbol many times.
+ It happens that the first symtab in the list
+ for any given blockvector is the main file. */
+ if (bv != prev_bv)
+ for (i = 0; i < 2; i++)
+ {
+ b = BLOCKVECTOR_BLOCK (bv, i);
+ /* Skip the sort if this block is always sorted. */
+ if (!BLOCK_SHOULD_SORT (b))
+ sort_block_syms (b);
+ for (j = 0; j < BLOCK_NSYMS (b); j++)
+ {
+ QUIT;
+ sym = BLOCK_SYM (b, j);
+ if ((regexp == 0 || re_exec (SYMBOL_NAME (sym)))
+ && ((class == 0 && SYMBOL_CLASS (sym) != LOC_TYPEDEF
+ && SYMBOL_CLASS (sym) != LOC_BLOCK)
+ || (class == 1 && SYMBOL_CLASS (sym) == LOC_BLOCK)
+ || (class == 2 && SYMBOL_CLASS (sym) == LOC_TYPEDEF)
+ || (class == 3 && SYMBOL_CLASS (sym) == LOC_BLOCK)))
+ {
+ if (!found_in_file)
+ {
+ printf_filtered ("\nFile %s:\n", s->filename);
+ print_count += 2;
+ }
+ found_in_file = 1;
+ if (class != 2 && i == 1)
+ printf_filtered ("static ");
+ if (class == 2
+ && SYMBOL_NAMESPACE (sym) != STRUCT_NAMESPACE)
+ printf_filtered ("typedef ");
+
+ if (class < 3)
+ {
+ type_print (SYMBOL_TYPE (sym),
+ (SYMBOL_CLASS (sym) == LOC_TYPEDEF
+ ? "" : SYMBOL_NAME (sym)),
+ stdout, 0);
+
+ if (class == 2
+ && SYMBOL_NAMESPACE (sym) != STRUCT_NAMESPACE
+ && (TYPE_NAME ((SYMBOL_TYPE (sym))) == 0
+ || 0 != strcmp (TYPE_NAME ((SYMBOL_TYPE (sym))),
+ SYMBOL_NAME (sym))))
+ printf_filtered (" %s", SYMBOL_NAME (sym));
+
+ printf_filtered (";\n");
+ }
+ else
+ {
+# if 0
+ char buf[1024];
+ type_print_base (TYPE_FN_FIELD_TYPE(t, i), stdout, 0, 0);
+ type_print_varspec_prefix (TYPE_FN_FIELD_TYPE(t, i), stdout, 0);
+ sprintf (buf, " %s::", TYPE_NAME (t));
+ type_print_method_args (TYPE_FN_FIELD_ARGS (t, i), buf, name, stdout);
+# endif
+ }
+ }
+ }
+ }
+ prev_bv = bv;
+ }
+}
+
+static void
+variables_info (regexp)
+ char *regexp;
+{
+ list_symbols (regexp, 0);
+}
+
+static void
+functions_info (regexp)
+ char *regexp;
+{
+ list_symbols (regexp, 1);
+}
+
+static void
+types_info (regexp)
+ char *regexp;
+{
+ list_symbols (regexp, 2);
+}
+
+#if 0
+/* Tiemann says: "info methods was never implemented." */
+static void
+methods_info (regexp)
+ char *regexp;
+{
+ list_symbols (regexp, 3);
+}
+#endif /* 0 */
+
+/* Call sort_block_syms to sort alphabetically the symbols of one block. */
+
+static int
+compare_symbols (s1, s2)
+ struct symbol **s1, **s2;
+{
+ /* Names that are less should come first. */
+ register int namediff = strcmp (SYMBOL_NAME (*s1), SYMBOL_NAME (*s2));
+ if (namediff != 0) return namediff;
+ /* For symbols of the same name, registers should come first. */
+ return ((SYMBOL_CLASS (*s2) == LOC_REGISTER)
+ - (SYMBOL_CLASS (*s1) == LOC_REGISTER));
+}
+
+static void
+sort_block_syms (b)
+ register struct block *b;
+{
+ qsort (&BLOCK_SYM (b, 0), BLOCK_NSYMS (b),
+ sizeof (struct symbol *), compare_symbols);
+}
+
+/* Initialize the standard C scalar types. */
+
+static
+struct type *
+init_type (code, length, uns, name)
+ enum type_code code;
+ int length, uns;
+ char *name;
+{
+ register struct type *type;
+
+ type = (struct type *) xmalloc (sizeof (struct type));
+ bzero (type, sizeof *type);
+ TYPE_MAIN_VARIANT (type) = type;
+ TYPE_CODE (type) = code;
+ TYPE_LENGTH (type) = length;
+ TYPE_FLAGS (type) = uns ? TYPE_FLAG_UNSIGNED : 0;
+ TYPE_FLAGS (type) |= TYPE_FLAG_PERM;
+ TYPE_NFIELDS (type) = 0;
+ TYPE_NAME (type) = name;
+
+ /* C++ fancies. */
+ TYPE_NFN_FIELDS (type) = 0;
+ TYPE_N_BASECLASSES (type) = 0;
+ TYPE_BASECLASSES (type) = 0;
+ return type;
+}
+
+/* Return Nonzero if block a is lexically nested within block b,
+ or if a and b have the same pc range.
+ Return zero otherwise. */
+int
+contained_in (a, b)
+ struct block *a, *b;
+{
+ if (!a || !b)
+ return 0;
+ return a->startaddr >= b->startaddr && a->endaddr <= b->endaddr;
+}
+
+
+/* Helper routine for make_symbol_completion_list. */
+
+int return_val_size, return_val_index;
+char **return_val;
+
+void
+completion_list_add_symbol (symname)
+ char *symname;
+{
+ if (return_val_index + 3 > return_val_size)
+ return_val =
+ (char **)xrealloc (return_val,
+ (return_val_size *= 2) * sizeof (char *));
+
+ return_val[return_val_index] =
+ (char *)xmalloc (1 + strlen (symname));
+
+ strcpy (return_val[return_val_index], symname);
+
+ return_val[++return_val_index] = (char *)NULL;
+}
+
+/* Return a NULL terminated array of all symbols (regardless of class) which
+ begin by matching TEXT. If the answer is no symbols, then the return value
+ is an array which contains only a NULL pointer.
+
+ Problem: All of the symbols have to be copied because readline
+ frees them. I'm not going to worry about this; hopefully there
+ won't be that many. */
+
+char **
+make_symbol_completion_list (text)
+ char *text;
+{
+ register struct symtab *s;
+ register struct partial_symtab *ps;
+ register struct blockvector *bv;
+ struct blockvector *prev_bv = 0;
+ register struct block *b, *surrounding_static_block;
+ extern struct block *get_selected_block ();
+ register int i, j;
+ register struct symbol *sym;
+ struct partial_symbol *psym;
+
+ int text_len = strlen (text);
+ return_val_size = 100;
+ return_val_index = 0;
+ return_val =
+ (char **)xmalloc ((1 + return_val_size) *sizeof (char *));
+ return_val[0] = (char *)NULL;
+
+ /* Look through the partial symtabs for all symbols which begin
+ by matching TEXT. Add each one that you find to the list. */
+
+ for (ps = partial_symtab_list; ps; ps = ps->next)
+ {
+ /* If the psymtab's been read in we'll get it when we search
+ through the blockvector. */
+ if (ps->readin) continue;
+
+ for (psym = global_psymbols.list + ps->globals_offset;
+ psym < (global_psymbols.list + ps->globals_offset
+ + ps->n_global_syms);
+ psym++)
+ {
+ QUIT; /* If interrupted, then quit. */
+ if ((strncmp (SYMBOL_NAME (psym), text, text_len) == 0))
+ completion_list_add_symbol (SYMBOL_NAME (psym));
+ }
+
+ for (psym = static_psymbols.list + ps->statics_offset;
+ psym < (static_psymbols.list + ps->statics_offset
+ + ps->n_static_syms);
+ psym++)
+ {
+ QUIT;
+ if ((strncmp (SYMBOL_NAME (psym), text, text_len) == 0))
+ completion_list_add_symbol (SYMBOL_NAME (psym));
+ }
+ }
+
+ /* At this point scan through the misc function vector and add each
+ symbol you find to the list. Eventually we want to ignore
+ anything that isn't a text symbol (everything else will be
+ handled by the psymtab code above). */
+
+ for (i = 0; i < misc_function_count; i++)
+ if (!strncmp (text, misc_function_vector[i].name, text_len))
+ completion_list_add_symbol (misc_function_vector[i].name);
+
+ /* Search upwards from currently selected frame (so that we can
+ complete on local vars. */
+ for (b = get_selected_block (); b; b = BLOCK_SUPERBLOCK (b))
+ {
+ if (!BLOCK_SUPERBLOCK (b))
+ surrounding_static_block = b; /* For elmin of dups */
+
+ /* Also catch fields of types defined in this places which
+ match our text string. Only complete on types visible
+ from current context. */
+ for (i = 0; i < BLOCK_NSYMS (b); i++)
+ {
+ register struct symbol *sym = BLOCK_SYM (b, i);
+
+ if (!strncmp (SYMBOL_NAME (sym), text, text_len))
+ completion_list_add_symbol (SYMBOL_NAME (sym));
+
+ if (SYMBOL_CLASS (sym) == LOC_TYPEDEF)
+ {
+ struct type *t = SYMBOL_TYPE (sym);
+ enum type_code c = TYPE_CODE (t);
+
+ if (c == TYPE_CODE_UNION || c == TYPE_CODE_STRUCT)
+ for (j = 0; j < TYPE_NFIELDS (t); j++)
+ if (TYPE_FIELD_NAME (t, j) &&
+ !strncmp (TYPE_FIELD_NAME (t, j), text, text_len))
+ completion_list_add_symbol (TYPE_FIELD_NAME (t, j));
+ }
+ }
+ }
+
+ /* Go through the symtabs and check the externs and statics for
+ symbols which match. */
+
+ for (s = symtab_list; s; s = s->next)
+ {
+ struct block *b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), 0);
+
+ for (i = 0; i < BLOCK_NSYMS (b); i++)
+ if (!strncmp (SYMBOL_NAME (BLOCK_SYM (b, i)), text, text_len))
+ completion_list_add_symbol (SYMBOL_NAME (BLOCK_SYM (b, i)));
+ }
+
+ for (s = symtab_list; s; s = s->next)
+ {
+ struct block *b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), 1);
+
+ /* Don't do this block twice. */
+ if (b == surrounding_static_block) continue;
+
+ for (i = 0; i < BLOCK_NSYMS (b); i++)
+ if (!strncmp (SYMBOL_NAME (BLOCK_SYM (b, i)), text, text_len))
+ completion_list_add_symbol (SYMBOL_NAME (BLOCK_SYM (b, i)));
+ }
+
+ return (return_val);
+}
+
+void
+_initialize_symtab ()
+{
+ add_info ("variables", variables_info,
+ "All global and static variable names, or those matching REGEXP.");
+ add_info ("functions", functions_info,
+ "All function names, or those matching REGEXP.");
+ add_info ("types", types_info,
+ "All types names, or those matching REGEXP.");
+#if 0
+ add_info ("methods", methods_info,
+ "All method names, or those matching REGEXP::REGEXP.\n\
+If the class qualifier is ommited, it is assumed to be the current scope.\n\
+If the first REGEXP is ommited, then all methods matching the second REGEXP\n\
+are listed.");
+#endif
+ add_info ("sources", sources_info,
+ "Source files in the program.");
+
+ obstack_init (symbol_obstack);
+ obstack_init (psymbol_obstack);
+
+ builtin_type_void = init_type (TYPE_CODE_VOID, 1, 0, "void");
+
+ builtin_type_float = init_type (TYPE_CODE_FLT, sizeof (float), 0, "float");
+ builtin_type_double = init_type (TYPE_CODE_FLT, sizeof (double), 0, "double");
+
+ builtin_type_char = init_type (TYPE_CODE_INT, sizeof (char), 0, "char");
+ builtin_type_short = init_type (TYPE_CODE_INT, sizeof (short), 0, "short");
+ builtin_type_long = init_type (TYPE_CODE_INT, sizeof (long), 0, "long");
+ builtin_type_int = init_type (TYPE_CODE_INT, sizeof (int), 0, "int");
+
+ builtin_type_unsigned_char = init_type (TYPE_CODE_INT, sizeof (char), 1, "unsigned char");
+ builtin_type_unsigned_short = init_type (TYPE_CODE_INT, sizeof (short), 1, "unsigned short");
+ builtin_type_unsigned_long = init_type (TYPE_CODE_INT, sizeof (long), 1, "unsigned long");
+ builtin_type_unsigned_int = init_type (TYPE_CODE_INT, sizeof (int), 1, "unsigned int");
+#ifdef LONG_LONG
+ builtin_type_long_long =
+ init_type (TYPE_CODE_INT, sizeof (long long), 0, "long long");
+ builtin_type_unsigned_long_long =
+ init_type (TYPE_CODE_INT, sizeof (long long), 1, "unsigned long long");
+#endif
+}
+
diff --git a/gnu/usr.bin/gdb/symtab.h b/gnu/usr.bin/gdb/symtab.h
new file mode 100644
index 0000000..fefed60
--- /dev/null
+++ b/gnu/usr.bin/gdb/symtab.h
@@ -0,0 +1,384 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ *
+ * @(#)symtab.h 6.3 (Berkeley) 5/8/91
+ */
+
+/* Symbol table definitions for GDB.
+ Copyright (C) 1986, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <obstack.h>
+
+/* An obstack to hold objects that should be freed
+ when we load a new symbol table.
+ This includes the symbols made by dbxread
+ and the types that are not permanent. */
+
+extern struct obstack *symbol_obstack;
+extern struct obstack *psymbol_obstack;
+
+/* Some definitions and declarations to go with use of obstacks. */
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+extern char *xmalloc ();
+extern void free ();
+
+/* gdb can know one or several symbol tables at the same time;
+ the ultimate intent is to have one for each separately-compiled module.
+ Each such symbol table is recorded by a struct symtab, and they
+ are all chained together. */
+
+/* In addition, gdb can record any number of miscellaneous undebuggable
+ functions' addresses. In a system that appends _ to function names,
+ the _'s are removed from the names stored in this table. */
+
+/* Actually, the misc function list is used to store *all* of the
+ global symbols (text, data, bss, and abs). It is sometimes used
+ to figure out what symtabs to read in. The "type" field appears
+ never to be used. */
+
+enum misc_function_type {mf_unknown = 0, mf_text, mf_data, mf_bss, mf_abs};
+
+struct misc_function
+{
+ char *name;
+ CORE_ADDR address;
+ int next; /* index of next in this hash bucket */
+ unsigned char type; /* Really enum misc_function_type. */
+};
+
+/* Address and length of the vector recording all misc function names/addresses. */
+
+struct misc_function *misc_function_vector;
+int misc_function_count;
+#define MISC_FUNC_HASH_SIZE (2048)
+int misc_function_hash_tab[MISC_FUNC_HASH_SIZE];
+
+#include "symseg.h"
+
+/* Each source file is represented by a struct symtab. */
+/* These objects are chained through the `next' field. */
+
+struct symtab
+ {
+ /* Chain of all existing symtabs. */
+ struct symtab *next;
+ /* List of all symbol scope blocks for this symtab. */
+ struct blockvector *blockvector;
+ /* Table mapping core addresses to line numbers for this file. */
+ struct linetable *linetable;
+ /* Vector containing all types defined for this symtab. */
+ struct typevector *typevector;
+ /* Name of this source file. */
+ char *filename;
+ /* This component says how to free the data we point to:
+ free_contents => do a tree walk and free each object.
+ free_nothing => do nothing; some other symtab will free
+ the data this one uses.
+ free_linetable => free just the linetable. */
+ enum free_code {free_nothing, free_contents, free_linetable}
+ free_code;
+ /* Pointer to one block of storage to be freed, if nonzero. */
+ char *free_ptr;
+ /* Total number of lines found in source file. */
+ int nlines;
+ /* Array mapping line number to character position. */
+ int *line_charpos;
+ /* Language of this source file. */
+ enum language language;
+ /* String of version information. May be zero. */
+ char *version;
+ /* String of compilation information. May be zero. */
+ char *compilation;
+ /* Offset within loader symbol table
+ of first local symbol for this file. */
+ int ldsymoff;
+ /* Full name of file as found by searching the source path.
+ 0 if not yet known. */
+ char *fullname;
+ };
+
+/*
+ * Each source file that has not been fully read in is represented by
+ * a partial_symtab. This contains the information on where in the
+ * executable the debugging symbols for a specific file are, and a
+ * list of names of global symbols which are located in this file.
+ */
+struct partial_symtab
+{
+ /* Chain of all existing partial symtabs. */
+ struct partial_symtab *next;
+ /* Name of the source file which this partial_symtab defines */
+ char *filename;
+ /* Offset within loader symbol table of first local symbol for this
+ file and length (in bytes) of the section of the symbol table
+ devoted to this file's symbols (actually, the section bracketed
+ may contain more than just this files symbols
+ If ldsymlen is 0, the only reason for this things existence is
+ the dependency list below. Nothing else will happen when it is
+ read in. */
+ int ldsymoff, ldsymlen;
+ /* Range of text addresses covered by this file; texthigh is the
+ beginning of the next section. */
+ int textlow, texthigh;
+ /* Non-zero if the symtab corresponding to this psymtab has been
+ readin */
+ unsigned char readin;
+ /* Array of pointers to all of the partial_symtab s which this one
+ depends one. Since this array can only be set to previous or
+ the current (?) psymtab, this dependency tree is guarranteed not
+ to have any loops. */
+ struct partial_symtab **dependencies;
+ int number_of_dependencies;
+ /* Global symbol list. This list will be sorted after readin to
+ improve access. Binary search will be the usual method of
+ finding a symbol within it. globals_offset is an integer offset
+ within ps_globals */
+ int globals_offset, n_global_syms;
+ /* Static symbol list. This list will *not* be sorted after readin;
+ to find a symbol in it, exhaustive search must be used. This is
+ reasonable because searches through this list will eventually
+ lead to either the read in of a files symbols for real (assumed
+ to take a *lot* of time; check) or an error (and we don't care
+ how long errors take). */
+ int statics_offset, n_static_syms;
+};
+
+/* This is the list of struct symtab's that gdb considers current. */
+
+struct symtab *symtab_list;
+
+/* This is the list of struct partial_symtab's that gdb may need to access */
+
+struct partial_symtab *partial_symtab_list;
+
+/* This symtab variable specifies the current file for printing source lines */
+
+struct symtab *current_source_symtab;
+
+/* This is the next line to print for listing source lines. */
+
+int current_source_line;
+
+#define BLOCKLIST(symtab) (symtab)->blockvector
+#define BLOCKVECTOR(symtab) (symtab)->blockvector
+
+#define TYPEVECTOR(symtab) (symtab)->typevector
+
+#define LINELIST(symtab) (symtab)->linetable
+#define LINETABLE(symtab) (symtab)->linetable
+
+/* Macros normally used to access components of symbol table structures. */
+
+#define BLOCKLIST_NBLOCKS(blocklist) (blocklist)->nblocks
+#define BLOCKLIST_BLOCK(blocklist,n) (blocklist)->block[n]
+#define BLOCKVECTOR_NBLOCKS(blocklist) (blocklist)->nblocks
+#define BLOCKVECTOR_BLOCK(blocklist,n) (blocklist)->block[n]
+
+#define TYPEVECTOR_NTYPES(typelist) (typelist)->length
+#define TYPEVECTOR_TYPE(typelist,n) (typelist)->type[n]
+
+#define BLOCK_START(bl) (bl)->startaddr
+#define BLOCK_END(bl) (bl)->endaddr
+#define BLOCK_NSYMS(bl) (bl)->nsyms
+#define BLOCK_SYM(bl, n) (bl)->sym[n]
+#define BLOCK_FUNCTION(bl) (bl)->function
+#define BLOCK_SUPERBLOCK(bl) (bl)->superblock
+#define BLOCK_GCC_COMPILED(bl) (bl)->gcc_compile_flag
+
+/* Nonzero if symbols of block BL should be sorted alphabetically. */
+#define BLOCK_SHOULD_SORT(bl) ((bl)->nsyms >= 40)
+
+#define SYMBOL_NAME(symbol) (symbol)->name
+#define SYMBOL_NAMESPACE(symbol) (symbol)->namespace
+#define SYMBOL_CLASS(symbol) (symbol)->class
+#define SYMBOL_VALUE(symbol) (symbol)->value.value
+#define SYMBOL_VALUE_BYTES(symbol) (symbol)->value.bytes
+#define SYMBOL_BLOCK_VALUE(symbol) (symbol)->value.block
+#define SYMBOL_TYPE(symbol) (symbol)->type
+
+/* Some macros for bitfields. */
+#define B_SET(a,x) (a[x>>5] |= (1 << (x&31)))
+#define B_CLR(a,x) (a[x>>5] &= ~(1 << (x&31)))
+#define B_TST(a,x) (a[x>>5] & (1 << (x&31)))
+
+#define TYPE_NAME(thistype) (thistype)->name
+#define TYPE_TARGET_TYPE(thistype) (thistype)->target_type
+#define TYPE_POINTER_TYPE(thistype) (thistype)->pointer_type
+#define TYPE_REFERENCE_TYPE(thistype) (thistype)->reference_type
+#define TYPE_FUNCTION_TYPE(thistype) (thistype)->function_type
+#define TYPE_MAIN_VARIANT(thistype) (thistype)->main_variant
+#define TYPE_NEXT_VARIANT(thistype) (thistype)->next_variant
+#define TYPE_LENGTH(thistype) (thistype)->length
+#define TYPE_FLAGS(thistype) (thistype)->flags
+#define TYPE_UNSIGNED(thistype) ((thistype)->flags & TYPE_FLAG_UNSIGNED)
+#define TYPE_CODE(thistype) (thistype)->code
+#define TYPE_NFIELDS(thistype) (thistype)->nfields
+#define TYPE_FIELDS(thistype) (thistype)->fields
+/* C++ */
+#define TYPE_VPTR_BASETYPE(thistype) (thistype)->vptr_basetype
+#define TYPE_DOMAIN_TYPE(thistype) (thistype)->vptr_basetype
+#define TYPE_VPTR_FIELDNO(thistype) (thistype)->vptr_fieldno
+#define TYPE_FN_FIELDS(thistype) (thistype)->fn_fields
+#define TYPE_NFN_FIELDS(thistype) (thistype)->nfn_fields
+#define TYPE_NFN_FIELDS_TOTAL(thistype) (thistype)->nfn_fields_total
+#define TYPE_BASECLASSES(thistype) (thistype)->baseclasses
+#define TYPE_ARG_TYPES(thistype) (thistype)->arg_types
+#define TYPE_BASECLASS(thistype,index) (thistype)->baseclasses[index]
+#define TYPE_N_BASECLASSES(thistype) (thistype)->n_baseclasses
+#define TYPE_VIA_PUBLIC(thistype) ((thistype)->flags & TYPE_FLAG_VIA_PUBLIC)
+#define TYPE_VIA_VIRTUAL(thistype) ((thistype)->flags & TYPE_FLAG_VIA_VIRTUAL)
+
+#define TYPE_FIELD(thistype, n) (thistype)->fields[n]
+#define TYPE_FIELD_TYPE(thistype, n) (thistype)->fields[n].type
+#define TYPE_FIELD_NAME(thistype, n) (thistype)->fields[n].name
+#define TYPE_FIELD_VALUE(thistype, n) (* (int*) &(thistype)->fields[n].type)
+#define TYPE_FIELD_BITPOS(thistype, n) (thistype)->fields[n].bitpos
+#define TYPE_FIELD_BITSIZE(thistype, n) (thistype)->fields[n].bitsize
+#define TYPE_FIELD_PACKED(thistype, n) (thistype)->fields[n].bitsize
+
+#define TYPE_FIELD_PRIVATE_BITS(thistype) (thistype)->private_field_bits
+#define TYPE_FIELD_PROTECTED_BITS(thistype) (thistype)->protected_field_bits
+#define SET_TYPE_FIELD_PRIVATE(thistype, n) B_SET ((thistype)->private_field_bits, (n))
+#define SET_TYPE_FIELD_PROTECTED(thistype, n) B_SET ((thistype)->protected_field_bits, (n))
+#define TYPE_FIELD_PRIVATE(thistype, n) B_TST((thistype)->private_field_bits, (n))
+#define TYPE_FIELD_PROTECTED(thistype, n) B_TST((thistype)->protected_field_bits, (n))
+
+#define TYPE_HAS_DESTRUCTOR(thistype) ((thistype)->flags & TYPE_FLAG_HAS_DESTRUCTOR)
+#define TYPE_HAS_CONSTRUCTOR(thistype) ((thistype)->flags & TYPE_FLAG_HAS_CONSTRUCTOR)
+
+#define TYPE_FIELD_STATIC(thistype, n) ((thistype)->fields[n].bitpos == -1)
+#define TYPE_FIELD_STATIC_PHYSNAME(thistype, n) ((char *)(thistype)->fields[n].bitsize)
+
+#define TYPE_FN_FIELDLISTS(thistype) (thistype)->fn_fieldlists
+#define TYPE_FN_FIELDLIST(thistype, n) (thistype)->fn_fieldlists[n]
+#define TYPE_FN_FIELDLIST1(thistype, n) (thistype)->fn_fieldlists[n].fn_fields
+#define TYPE_FN_FIELDLIST_NAME(thistype, n) (thistype)->fn_fieldlists[n].name
+#define TYPE_FN_FIELDLIST_LENGTH(thistype, n) (thistype)->fn_fieldlists[n].length
+
+#define TYPE_FN_FIELD(thistype, n) (thistype)[n]
+#define TYPE_FN_FIELD_NAME(thistype, n) (thistype)[n].name
+#define TYPE_FN_FIELD_TYPE(thistype, n) (thistype)[n].type
+#define TYPE_FN_FIELD_ARGS(thistype, n) (thistype)[n].args
+#define TYPE_FN_FIELD_PHYSNAME(thistype, n) (thistype)[n].physname
+#define TYPE_FN_FIELD_VIRTUAL_P(thistype, n) ((thistype)[n].voffset < 0)
+#define TYPE_FN_FIELD_STATIC_P(thistype, n) ((thistype)[n].voffset > 0)
+#define TYPE_FN_FIELD_VOFFSET(thistype, n) ((thistype)[n].voffset-1)
+
+#define TYPE_FN_PRIVATE_BITS(thistype) (thistype).private_fn_field_bits
+#define TYPE_FN_PROTECTED_BITS(thistype) (thistype).protected_fn_field_bits
+#define SET_TYPE_FN_PRIVATE(thistype, n) B_SET ((thistype).private_fn_field_bits, n)
+#define SET_TYPE_FN_PROTECTED(thistype, n) B_SET ((thistype).protected_fn_field_bits, n)
+#define TYPE_FN_PRIVATE(thistype, n) B_TST ((thistype).private_fn_field_bits, n)
+#define TYPE_FN_PROTECTED(thistype, n) B_TST ((thistype).protected_fn_field_bits, n)
+
+/* Functions that work on the objects described above */
+
+extern struct symtab *lookup_symtab ();
+extern struct symbol *lookup_symbol ();
+extern struct type *lookup_typename ();
+extern struct type *lookup_unsigned_typename ();
+extern struct type *lookup_struct ();
+extern struct type *lookup_union ();
+extern struct type *lookup_enum ();
+extern struct type *lookup_struct_elt_type ();
+extern struct type *lookup_pointer_type ();
+extern struct type *lookup_function_type ();
+extern struct type *lookup_basetype_type ();
+extern struct type *create_array_type ();
+extern struct symbol *block_function ();
+extern struct symbol *find_pc_function ();
+extern int find_pc_partial_function ();
+extern struct partial_symtab *find_pc_psymtab ();
+extern struct symtab *find_pc_symtab ();
+extern struct partial_symbol *find_pc_psymbol ();
+extern int find_pc_misc_function ();
+
+/* C++ stuff. */
+extern struct type *lookup_reference_type ();
+extern struct type *lookup_member_type ();
+extern struct type *lookup_class ();
+/* end of C++ stuff. */
+
+extern struct type *builtin_type_void;
+extern struct type *builtin_type_char;
+extern struct type *builtin_type_short;
+extern struct type *builtin_type_int;
+extern struct type *builtin_type_long;
+extern struct type *builtin_type_unsigned_char;
+extern struct type *builtin_type_unsigned_short;
+extern struct type *builtin_type_unsigned_int;
+extern struct type *builtin_type_unsigned_long;
+extern struct type *builtin_type_float;
+extern struct type *builtin_type_double;
+
+#ifdef LONG_LONG
+extern struct type *builtin_type_long_long;
+extern struct type *builtin_type_unsigned_long_long;
+
+#ifndef BUILTIN_TYPE_LONGEST
+#define BUILTIN_TYPE_LONGEST builtin_type_long_long
+#endif
+
+#ifndef BUILTIN_TYPE_UNSIGNED_LONGEST
+#define BUILTIN_TYPE_UNSIGNED_LONGEST builtin_type_unsigned_long_long
+#endif
+
+#else /* LONG_LONG */
+
+#ifndef BUILTIN_TYPE_LONGEST
+#define BUILTIN_TYPE_LONGEST builtin_type_long
+#endif
+
+#ifndef BUILTIN_TYPE_UNSIGNED_LONGEST
+#define BUILTIN_TYPE_UNSIGNED_LONGEST builtin_type_unsigned_long
+#endif
+
+#endif
+
+struct symtab_and_line
+{
+ struct symtab *symtab;
+ int line;
+ CORE_ADDR pc;
+ CORE_ADDR end;
+};
+
+struct symtabs_and_lines
+{
+ struct symtab_and_line *sals;
+ int nelts;
+};
+
+/* Given a pc value, return line number it is in.
+ Second arg nonzero means if pc is on the boundary
+ use the previous statement's line number. */
+
+struct symtab_and_line find_pc_line ();
+
+/* Given a string, return the line specified by it.
+ For commands like "list" and "breakpoint". */
+
+struct symtabs_and_lines decode_line_spec ();
+struct symtabs_and_lines decode_line_spec_1 ();
+struct symtabs_and_lines decode_line_1 ();
diff --git a/gnu/usr.bin/gdb/utils.c b/gnu/usr.bin/gdb/utils.c
new file mode 100644
index 0000000..b03f2be
--- /dev/null
+++ b/gnu/usr.bin/gdb/utils.c
@@ -0,0 +1,1096 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ *
+ * $Header: /home/cvs/386BSD/src/usr.bin/gdb/utils.c,v 1.1.1.1 1993/06/12 14:52:20 rgrimes Exp $;
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)utils.c 6.4 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* General utility routines for GDB, the GNU debugger.
+ Copyright (C) 1986, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "param.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <pwd.h>
+#include "defs.h"
+#ifdef HAVE_TERMIO
+#include <termio.h>
+#endif
+
+/* If this definition isn't overridden by the header files, assume
+ that isatty and fileno exist on this system. */
+#ifndef ISATTY
+#define ISATTY(FP) (isatty (fileno (FP)))
+#endif
+
+extern FILE *instream;
+
+void error ();
+void fatal ();
+
+/* Chain of cleanup actions established with make_cleanup,
+ to be executed if an error happens. */
+
+static struct cleanup *cleanup_chain;
+
+/* Nonzero means a quit has been requested. */
+
+int quit_flag;
+
+/* Nonzero means quit immediately if Control-C is typed now,
+ rather than waiting until QUIT is executed. */
+
+int immediate_quit;
+
+/* Add a new cleanup to the cleanup_chain,
+ and return the previous chain pointer
+ to be passed later to do_cleanups or discard_cleanups.
+ Args are FUNCTION to clean up with, and ARG to pass to it. */
+
+struct cleanup *
+make_cleanup (function, arg)
+ void (*function) ();
+ int arg;
+{
+ register struct cleanup *new
+ = (struct cleanup *) xmalloc (sizeof (struct cleanup));
+ register struct cleanup *old_chain = cleanup_chain;
+
+ new->next = cleanup_chain;
+ new->function = function;
+ new->arg = arg;
+ cleanup_chain = new;
+
+ return old_chain;
+}
+
+/* Discard cleanups and do the actions they describe
+ until we get back to the point OLD_CHAIN in the cleanup_chain. */
+
+void
+do_cleanups (old_chain)
+ register struct cleanup *old_chain;
+{
+ register struct cleanup *ptr;
+ while ((ptr = cleanup_chain) != old_chain)
+ {
+ (*ptr->function) (ptr->arg);
+ cleanup_chain = ptr->next;
+ free (ptr);
+ }
+}
+
+/* Discard cleanups, not doing the actions they describe,
+ until we get back to the point OLD_CHAIN in the cleanup_chain. */
+
+void
+discard_cleanups (old_chain)
+ register struct cleanup *old_chain;
+{
+ register struct cleanup *ptr;
+ while ((ptr = cleanup_chain) != old_chain)
+ {
+ cleanup_chain = ptr->next;
+ free (ptr);
+ }
+}
+
+/* Set the cleanup_chain to 0, and return the old cleanup chain. */
+struct cleanup *
+save_cleanups ()
+{
+ struct cleanup *old_chain = cleanup_chain;
+
+ cleanup_chain = 0;
+ return old_chain;
+}
+
+/* Restore the cleanup chain from a previously saved chain. */
+void
+restore_cleanups (chain)
+ struct cleanup *chain;
+{
+ cleanup_chain = chain;
+}
+
+/* This function is useful for cleanups.
+ Do
+
+ foo = xmalloc (...);
+ old_chain = make_cleanup (free_current_contents, &foo);
+
+ to arrange to free the object thus allocated. */
+
+void
+free_current_contents (location)
+ char **location;
+{
+ free (*location);
+}
+
+/* Generally useful subroutines used throughout the program. */
+
+/* Like malloc but get error if no storage available. */
+
+char *
+xmalloc (size)
+ long size;
+{
+ register char *val = (char *) malloc (size);
+ if (!val)
+ fatal ("virtual memory exhausted.", 0);
+ return val;
+}
+
+/* Like realloc but get error if no storage available. */
+
+char *
+xrealloc (ptr, size)
+ char *ptr;
+ long size;
+{
+ register char *val = (char *) realloc (ptr, size);
+ if (!val)
+ fatal ("virtual memory exhausted.", 0);
+ return val;
+}
+
+/* Print the system error message for errno, and also mention STRING
+ as the file name for which the error was encountered.
+ Then return to command level. */
+
+void
+perror_with_name (string)
+ char *string;
+{
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+ extern int errno;
+ char *err;
+ char *combined;
+
+ if (errno < sys_nerr)
+ err = sys_errlist[errno];
+ else
+ err = "unknown error";
+
+ combined = (char *) alloca (strlen (err) + strlen (string) + 3);
+ strcpy (combined, string);
+ strcat (combined, ": ");
+ strcat (combined, err);
+
+ error ("%s.", combined);
+}
+
+/* Print the system error message for ERRCODE, and also mention STRING
+ as the file name for which the error was encountered. */
+
+void
+print_sys_errmsg (string, errcode)
+ char *string;
+ int errcode;
+{
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+ char *err;
+ char *combined;
+
+ if (errcode < sys_nerr)
+ err = sys_errlist[errcode];
+ else
+ err = "unknown error";
+
+ combined = (char *) alloca (strlen (err) + strlen (string) + 3);
+ strcpy (combined, string);
+ strcat (combined, ": ");
+ strcat (combined, err);
+
+ printf ("%s.\n", combined);
+}
+
+void
+quit ()
+{
+#ifdef HAVE_TERMIO
+ ioctl (fileno (stdout), TCFLSH, 1);
+#else /* not HAVE_TERMIO */
+ ioctl (fileno (stdout), TIOCFLUSH, 0);
+#endif /* not HAVE_TERMIO */
+#ifdef TIOCGPGRP
+ error ("Quit");
+#else
+ error ("Quit (expect signal %d when inferior is resumed)", SIGINT);
+#endif /* TIOCGPGRP */
+}
+
+/* Control C comes here */
+
+void
+request_quit ()
+{
+ extern int remote_debugging;
+
+ quit_flag = 1;
+
+#ifdef USG
+ /* Restore the signal handler. */
+ signal (SIGINT, request_quit);
+#endif
+
+ if (immediate_quit)
+ quit();
+}
+
+/* Print an error message and return to command level.
+ STRING is the error message, used as a fprintf string,
+ and ARG is passed as an argument to it. */
+
+void
+error (string, arg1, arg2, arg3)
+ char *string;
+ int arg1, arg2, arg3;
+{
+ terminal_ours (); /* Should be ok even if no inf. */
+ fflush (stdout);
+ fprintf (stderr, string, arg1, arg2, arg3);
+ fprintf (stderr, "\n");
+ return_to_top_level ();
+}
+
+/* Print an error message and exit reporting failure.
+ This is for a error that we cannot continue from.
+ STRING and ARG are passed to fprintf. */
+
+void
+fatal (string, arg)
+ char *string;
+ int arg;
+{
+ fprintf (stderr, "gdb: ");
+ fprintf (stderr, string, arg);
+ fprintf (stderr, "\n");
+ exit (1);
+}
+
+/* Print an error message and exit, dumping core.
+ STRING is a printf-style control string, and ARG is a corresponding
+ argument. */
+void
+fatal_dump_core (string, arg)
+ char *string;
+ int arg;
+{
+ /* "internal error" is always correct, since GDB should never dump
+ core, no matter what the input. */
+ fprintf (stderr, "gdb internal error: ");
+ fprintf (stderr, string, arg);
+ fprintf (stderr, "\n");
+ signal (SIGQUIT, SIG_DFL);
+ kill (getpid (), SIGQUIT);
+ /* We should never get here, but just in case... */
+ exit (1);
+}
+
+/* Make a copy of the string at PTR with SIZE characters
+ (and add a null character at the end in the copy).
+ Uses malloc to get the space. Returns the address of the copy. */
+
+char *
+savestring (ptr, size)
+ char *ptr;
+ int size;
+{
+ register char *p = (char *) xmalloc (size + 1);
+ bcopy (ptr, p, size);
+ p[size] = 0;
+ return p;
+}
+
+char *
+concat (s1, s2, s3)
+ char *s1, *s2, *s3;
+{
+ register int len = strlen (s1) + strlen (s2) + strlen (s3) + 1;
+ register char *val = (char *) xmalloc (len);
+ strcpy (val, s1);
+ strcat (val, s2);
+ strcat (val, s3);
+ return val;
+}
+
+void
+print_spaces (n, file)
+ register int n;
+ register FILE *file;
+{
+ while (n-- > 0)
+ fputc (' ', file);
+}
+
+/* Ask user a y-or-n question and return 1 iff answer is yes.
+ Takes three args which are given to printf to print the question.
+ The first, a control string, should end in "? ".
+ It should not say how to answer, because we do that. */
+
+int
+query (ctlstr, arg1, arg2)
+ char *ctlstr;
+{
+ register int answer;
+
+ /* Automatically answer "yes" if input is not from a terminal. */
+ if (!input_from_terminal_p ())
+ return 1;
+
+ while (1)
+ {
+ printf (ctlstr, arg1, arg2);
+ printf ("(y or n) ");
+ fflush (stdout);
+ answer = fgetc (stdin);
+ clearerr (stdin); /* in case of C-d */
+ if (answer != '\n')
+ while (fgetc (stdin) != '\n') clearerr (stdin);
+ if (answer >= 'a')
+ answer -= 040;
+ if (answer == 'Y')
+ return 1;
+ if (answer == 'N')
+ return 0;
+ printf ("Please answer y or n.\n");
+ }
+}
+
+/* Parse a C escape sequence. STRING_PTR points to a variable
+ containing a pointer to the string to parse. That pointer
+ is updated past the characters we use. The value of the
+ escape sequence is returned.
+
+ A negative value means the sequence \ newline was seen,
+ which is supposed to be equivalent to nothing at all.
+
+ If \ is followed by a null character, we return a negative
+ value and leave the string pointer pointing at the null character.
+
+ If \ is followed by 000, we return 0 and leave the string pointer
+ after the zeros. A value of 0 does not mean end of string. */
+
+int
+parse_escape (string_ptr)
+ char **string_ptr;
+{
+ register int c = *(*string_ptr)++;
+ switch (c)
+ {
+ case 'a':
+ return '\a';
+ case 'b':
+ return '\b';
+ case 'e':
+ return 033;
+ case 'f':
+ return '\f';
+ case 'n':
+ return '\n';
+ case 'r':
+ return '\r';
+ case 't':
+ return '\t';
+ case 'v':
+ return '\v';
+ case '\n':
+ return -2;
+ case 0:
+ (*string_ptr)--;
+ return 0;
+ case '^':
+ c = *(*string_ptr)++;
+ if (c == '\\')
+ c = parse_escape (string_ptr);
+ if (c == '?')
+ return 0177;
+ return (c & 0200) | (c & 037);
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ register int i = c - '0';
+ register int count = 0;
+ while (++count < 3)
+ {
+ if ((c = *(*string_ptr)++) >= '0' && c <= '7')
+ {
+ i *= 8;
+ i += c - '0';
+ }
+ else
+ {
+ (*string_ptr)--;
+ break;
+ }
+ }
+ return i;
+ }
+ default:
+ return c;
+ }
+}
+
+/* Print the character CH on STREAM as part of the contents
+ of a literal string whose delimiter is QUOTER. */
+
+void
+printchar (ch, stream, quoter)
+ unsigned char ch;
+ FILE *stream;
+ int quoter;
+{
+ register int c = ch;
+ if (c < 040 || c >= 0177)
+ switch (c)
+ {
+ case '\n':
+ fputs_filtered ("\\n", stream);
+ break;
+ case '\b':
+ fputs_filtered ("\\b", stream);
+ break;
+ case '\t':
+ fputs_filtered ("\\t", stream);
+ break;
+ case '\f':
+ fputs_filtered ("\\f", stream);
+ break;
+ case '\r':
+ fputs_filtered ("\\r", stream);
+ break;
+ case '\033':
+ fputs_filtered ("\\e", stream);
+ break;
+ case '\007':
+ fputs_filtered ("\\a", stream);
+ break;
+ default:
+ fprintf_filtered (stream, "\\%.3o", (unsigned int) c);
+ break;
+ }
+ else
+ {
+ if (c == '\\' || c == quoter)
+ fputs_filtered ("\\", stream);
+ fprintf_filtered (stream, "%c", c);
+ }
+}
+
+static int lines_per_page, lines_printed, chars_per_line, chars_printed;
+
+/* Set values of page and line size. */
+static void
+set_screensize_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ char *p = arg;
+ char *p1;
+ int tolinesize = lines_per_page;
+ int tocharsize = chars_per_line;
+
+ if (p == 0)
+ error_no_arg ("set screensize");
+
+ while (*p >= '0' && *p <= '9')
+ p++;
+
+ if (*p && *p != ' ' && *p != '\t')
+ error ("Non-integral argument given to \"set screensize\".");
+
+ tolinesize = atoi (arg);
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (*p)
+ {
+ p1 = p;
+ while (*p1 >= '0' && *p1 <= '9')
+ p1++;
+
+ if (*p1)
+ error ("Non-integral second argument given to \"set screensize\".");
+
+ tocharsize = atoi (p);
+ }
+
+ lines_per_page = tolinesize;
+ chars_per_line = tocharsize;
+}
+
+static void
+instream_cleanup(stream)
+ FILE *stream;
+{
+ instream = stream;
+}
+
+static void
+prompt_for_continue ()
+{
+ if (ISATTY(stdin) && ISATTY(stdout))
+ {
+ struct cleanup *old_chain = make_cleanup(instream_cleanup, instream);
+ char *cp, *gdb_readline();
+
+ instream = stdin;
+ immediate_quit++;
+ if (cp = gdb_readline ("---Type <return> to continue---"))
+ free(cp);
+ chars_printed = lines_printed = 0;
+ immediate_quit--;
+ do_cleanups(old_chain);
+ }
+}
+
+/* Reinitialize filter; ie. tell it to reset to original values. */
+
+void
+reinitialize_more_filter ()
+{
+ lines_printed = 0;
+ chars_printed = 0;
+}
+
+static void
+screensize_info (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ if (arg)
+ error ("\"info screensize\" does not take any arguments.");
+
+ if (!lines_per_page)
+ printf ("Output more filtering is disabled.\n");
+ else
+ {
+ printf ("Output more filtering is enabled with\n");
+ printf ("%d lines per page and %d characters per line.\n",
+ lines_per_page, chars_per_line);
+ }
+}
+
+/* Like fputs but pause after every screenful.
+ Unlike fputs, fputs_filtered does not return a value.
+ It is OK for LINEBUFFER to be NULL, in which case just don't print
+ anything.
+
+ Note that a longjmp to top level may occur in this routine
+ (since prompt_for_continue may do so) so this routine should not be
+ called when cleanups are not in place. */
+
+void
+fputs_filtered (linebuffer, stream)
+ char *linebuffer;
+ FILE *stream;
+{
+ char *lineptr;
+
+ if (linebuffer == 0)
+ return;
+
+ /* Don't do any filtering if it is disabled. */
+ if (stream != stdout || !ISATTY(stdout) || lines_per_page == 0)
+ {
+ fputs (linebuffer, stream);
+ return;
+ }
+
+ /* Go through and output each character. Show line extension
+ when this is necessary; prompt user for new page when this is
+ necessary. */
+
+ lineptr = linebuffer;
+ while (*lineptr)
+ {
+ /* Possible new page. */
+ if (lines_printed >= lines_per_page - 1)
+ prompt_for_continue ();
+
+ while (*lineptr && *lineptr != '\n')
+ {
+ /* Print a single line. */
+ if (*lineptr == '\t')
+ {
+ putc ('\t', stream);
+ /* Shifting right by 3 produces the number of tab stops
+ we have already passed, and then adding one and
+ shifting left 3 advances to the next tab stop. */
+ chars_printed = ((chars_printed >> 3) + 1) << 3;
+ lineptr++;
+ }
+ else
+ {
+ putc (*lineptr, stream);
+ chars_printed++;
+ lineptr++;
+ }
+
+ if (chars_printed >= chars_per_line)
+ {
+ chars_printed = 0;
+ lines_printed++;
+ /* Possible new page. */
+ if (lines_printed >= lines_per_page - 1)
+ prompt_for_continue ();
+ }
+ }
+
+ if (*lineptr == '\n')
+ {
+ lines_printed++;
+ putc ('\n', stream);
+ lineptr++;
+ chars_printed = 0;
+ }
+ }
+}
+
+/* fputs_demangled is a variant of fputs_filtered that
+ demangles g++ names.*/
+
+void
+fputs_demangled (linebuffer, stream, arg_mode)
+ char *linebuffer;
+ FILE *stream;
+{
+#ifdef __STDC__
+ extern char *cplus_demangle (const char *, int);
+#else
+ extern char *cplus_demangle ();
+#endif
+#define SYMBOL_MAX 1024
+
+#define SYMBOL_CHAR(c) (isascii(c) && (isalnum(c) || (c) == '_' || (c) == '$'))
+
+ char buf[SYMBOL_MAX+1];
+ char *p;
+
+ if (linebuffer == NULL)
+ return;
+
+ p = linebuffer;
+
+ while ( *p != (char) 0 ) {
+ int i = 0;
+
+ /* collect non-interesting characters into buf */
+ while ( *p != (char) 0 && !SYMBOL_CHAR(*p) ) {
+ buf[i++] = *p;
+ p++;
+ }
+ if (i > 0) {
+ /* output the non-interesting characters without demangling */
+ buf[i] = (char) 0;
+ fputs_filtered(buf, stream);
+ i = 0; /* reset buf */
+ }
+
+ /* and now the interesting characters */
+ while (i < SYMBOL_MAX && *p != (char) 0 && SYMBOL_CHAR(*p) ) {
+ buf[i++] = *p;
+ p++;
+ }
+ buf[i] = (char) 0;
+ if (i > 0) {
+ char * result;
+
+ if ( (result = cplus_demangle(buf, arg_mode)) != NULL ) {
+ fputs_filtered(result, stream);
+ free(result);
+ }
+ else {
+ fputs_filtered(buf, stream);
+ }
+ }
+ }
+}
+
+/* Print ARG1, ARG2, and ARG3 on stdout using format FORMAT. If this
+ information is going to put the amount written since the last call
+ to INIIALIZE_MORE_FILTER or the last page break over the page size,
+ print out a pause message and do a gdb_readline to get the users
+ permision to continue.
+
+ Unlike fprintf, this function does not return a value.
+
+ Note that this routine has a restriction that the length of the
+ final output line must be less than 255 characters *or* it must be
+ less than twice the size of the format string. This is a very
+ arbitrary restriction, but it is an internal restriction, so I'll
+ put it in. This means that the %s format specifier is almost
+ useless; unless the caller can GUARANTEE that the string is short
+ enough, fputs_filtered should be used instead.
+
+ Note also that a longjmp to top level may occur in this routine
+ (since prompt_for_continue may do so) so this routine should not be
+ called when cleanups are not in place. */
+
+void
+fprintf_filtered (stream, format, arg1, arg2, arg3, arg4, arg5, arg6)
+ FILE *stream;
+ char *format;
+ int arg1, arg2, arg3, arg4, arg5, arg6;
+{
+ static char *linebuffer = (char *) 0;
+ static int line_size;
+ int format_length = strlen (format);
+ int numchars;
+
+ /* Allocated linebuffer for the first time. */
+ if (!linebuffer)
+ {
+ linebuffer = (char *) xmalloc (255);
+ line_size = 255;
+ }
+
+ /* Reallocate buffer to a larger size if this is necessary. */
+ if (format_length * 2 > line_size)
+ {
+ line_size = format_length * 2;
+
+ /* You don't have to copy. */
+ free (linebuffer);
+ linebuffer = (char *) xmalloc (line_size);
+ }
+
+ /* This won't blow up if the restrictions described above are
+ followed. */
+ (void) sprintf (linebuffer, format, arg1, arg2, arg3, arg4, arg5, arg6);
+
+ fputs_filtered (linebuffer, stream);
+}
+
+void
+printf_filtered (format, arg1, arg2, arg3, arg4, arg5, arg6)
+ char *format;
+ int arg1, arg2, arg3, arg4, arg5, arg6;
+{
+ fprintf_filtered (stdout, format, arg1, arg2, arg3, arg4, arg5, arg6);
+}
+
+/* Print N spaces. */
+void
+print_spaces_filtered (n, stream)
+ int n;
+ FILE *stream;
+{
+ register char *s = (char *) alloca (n + 1);
+ register char *t = s;
+
+ while (n--)
+ *t++ = ' ';
+ *t = '\0';
+
+ fputs_filtered (s, stream);
+}
+
+
+#ifdef USG
+bcopy (from, to, count)
+char *from, *to;
+{
+ memcpy (to, from, count);
+}
+
+bcmp (from, to, count)
+{
+ return (memcmp (to, from, count));
+}
+
+bzero (to, count)
+char *to;
+{
+ while (count--)
+ *to++ = 0;
+}
+
+getwd (buf)
+char *buf;
+{
+ getcwd (buf, MAXPATHLEN);
+}
+
+char *
+index (s, c)
+ char *s;
+{
+ char *strchr ();
+ return strchr (s, c);
+}
+
+char *
+rindex (s, c)
+ char *s;
+{
+ char *strrchr ();
+ return strrchr (s, c);
+}
+
+#ifndef USG
+char *sys_siglist[32] = {
+ "SIG0",
+ "SIGHUP",
+ "SIGINT",
+ "SIGQUIT",
+ "SIGILL",
+ "SIGTRAP",
+ "SIGIOT",
+ "SIGEMT",
+ "SIGFPE",
+ "SIGKILL",
+ "SIGBUS",
+ "SIGSEGV",
+ "SIGSYS",
+ "SIGPIPE",
+ "SIGALRM",
+ "SIGTERM",
+ "SIGUSR1",
+ "SIGUSR2",
+ "SIGCLD",
+ "SIGPWR",
+ "SIGWIND",
+ "SIGPHONE",
+ "SIGPOLL",
+};
+#endif
+
+/* Queue routines */
+
+struct queue {
+ struct queue *forw;
+ struct queue *back;
+};
+
+insque (item, after)
+struct queue *item;
+struct queue *after;
+{
+ item->forw = after->forw;
+ after->forw->back = item;
+
+ item->back = after;
+ after->forw = item;
+}
+
+remque (item)
+struct queue *item;
+{
+ item->forw->back = item->back;
+ item->back->forw = item->forw;
+}
+#endif /* USG */
+
+#ifdef USG
+/* There is too much variation in Sys V signal numbers and names, so
+ we must initialize them at runtime. */
+static char undoc[] = "(undocumented)";
+
+char *sys_siglist[NSIG];
+#endif /* USG */
+
+extern struct cmd_list_element *setlist;
+
+void
+_initialize_utils ()
+{
+ int i;
+ add_cmd ("screensize", class_support, set_screensize_command,
+ "Change gdb's notion of the size of the output screen.\n\
+The first argument is the number of lines on a page.\n\
+The second argument (optional) is the number of characters on a line.",
+ &setlist);
+ add_info ("screensize", screensize_info,
+ "Show gdb's current notion of the size of the output screen.");
+
+ /* These defaults will be used if we are unable to get the correct
+ values from termcap. */
+ lines_per_page = 24;
+ chars_per_line = 80;
+ /* Initialize the screen height and width from termcap. */
+ {
+ int termtype = getenv ("TERM");
+
+ /* Positive means success, nonpositive means failure. */
+ int status;
+
+ /* 2048 is large enough for all known terminals, according to the
+ GNU termcap manual. */
+ char term_buffer[2048];
+
+ if (termtype)
+ {
+ status = tgetent (term_buffer, termtype);
+ if (status > 0)
+ {
+ int val;
+
+ val = tgetnum ("li");
+ if (val >= 0)
+ lines_per_page = val;
+ else
+ /* The number of lines per page is not mentioned
+ in the terminal description. This probably means
+ that paging is not useful (e.g. emacs shell window),
+ so disable paging. */
+ lines_per_page = 0;
+
+ val = tgetnum ("co");
+ if (val >= 0)
+ chars_per_line = val;
+ }
+ }
+ }
+
+#ifdef USG
+ /* Initialize signal names. */
+ for (i = 0; i < NSIG; i++)
+ sys_siglist[i] = undoc;
+
+#ifdef SIGHUP
+ sys_siglist[SIGHUP ] = "SIGHUP";
+#endif
+#ifdef SIGINT
+ sys_siglist[SIGINT ] = "SIGINT";
+#endif
+#ifdef SIGQUIT
+ sys_siglist[SIGQUIT ] = "SIGQUIT";
+#endif
+#ifdef SIGILL
+ sys_siglist[SIGILL ] = "SIGILL";
+#endif
+#ifdef SIGTRAP
+ sys_siglist[SIGTRAP ] = "SIGTRAP";
+#endif
+#ifdef SIGIOT
+ sys_siglist[SIGIOT ] = "SIGIOT";
+#endif
+#ifdef SIGEMT
+ sys_siglist[SIGEMT ] = "SIGEMT";
+#endif
+#ifdef SIGFPE
+ sys_siglist[SIGFPE ] = "SIGFPE";
+#endif
+#ifdef SIGKILL
+ sys_siglist[SIGKILL ] = "SIGKILL";
+#endif
+#ifdef SIGBUS
+ sys_siglist[SIGBUS ] = "SIGBUS";
+#endif
+#ifdef SIGSEGV
+ sys_siglist[SIGSEGV ] = "SIGSEGV";
+#endif
+#ifdef SIGSYS
+ sys_siglist[SIGSYS ] = "SIGSYS";
+#endif
+#ifdef SIGPIPE
+ sys_siglist[SIGPIPE ] = "SIGPIPE";
+#endif
+#ifdef SIGALRM
+ sys_siglist[SIGALRM ] = "SIGALRM";
+#endif
+#ifdef SIGTERM
+ sys_siglist[SIGTERM ] = "SIGTERM";
+#endif
+#ifdef SIGUSR1
+ sys_siglist[SIGUSR1 ] = "SIGUSR1";
+#endif
+#ifdef SIGUSR2
+ sys_siglist[SIGUSR2 ] = "SIGUSR2";
+#endif
+#ifdef SIGCLD
+ sys_siglist[SIGCLD ] = "SIGCLD";
+#endif
+#ifdef SIGCHLD
+ sys_siglist[SIGCHLD ] = "SIGCHLD";
+#endif
+#ifdef SIGPWR
+ sys_siglist[SIGPWR ] = "SIGPWR";
+#endif
+#ifdef SIGTSTP
+ sys_siglist[SIGTSTP ] = "SIGTSTP";
+#endif
+#ifdef SIGTTIN
+ sys_siglist[SIGTTIN ] = "SIGTTIN";
+#endif
+#ifdef SIGTTOU
+ sys_siglist[SIGTTOU ] = "SIGTTOU";
+#endif
+#ifdef SIGSTOP
+ sys_siglist[SIGSTOP ] = "SIGSTOP";
+#endif
+#ifdef SIGXCPU
+ sys_siglist[SIGXCPU ] = "SIGXCPU";
+#endif
+#ifdef SIGXFSZ
+ sys_siglist[SIGXFSZ ] = "SIGXFSZ";
+#endif
+#ifdef SIGVTALRM
+ sys_siglist[SIGVTALRM ] = "SIGVTALRM";
+#endif
+#ifdef SIGPROF
+ sys_siglist[SIGPROF ] = "SIGPROF";
+#endif
+#ifdef SIGWINCH
+ sys_siglist[SIGWINCH ] = "SIGWINCH";
+#endif
+#ifdef SIGCONT
+ sys_siglist[SIGCONT ] = "SIGCONT";
+#endif
+#ifdef SIGURG
+ sys_siglist[SIGURG ] = "SIGURG";
+#endif
+#ifdef SIGIO
+ sys_siglist[SIGIO ] = "SIGIO";
+#endif
+#ifdef SIGWIND
+ sys_siglist[SIGWIND ] = "SIGWIND";
+#endif
+#ifdef SIGPHONE
+ sys_siglist[SIGPHONE ] = "SIGPHONE";
+#endif
+#ifdef SIGPOLL
+ sys_siglist[SIGPOLL ] = "SIGPOLL";
+#endif
+#endif /* USG */
+}
diff --git a/gnu/usr.bin/gdb/valarith.c b/gnu/usr.bin/gdb/valarith.c
new file mode 100644
index 0000000..8e76899
--- /dev/null
+++ b/gnu/usr.bin/gdb/valarith.c
@@ -0,0 +1,690 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)valarith.c 6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Perform arithmetic and other operations on values, for GDB.
+ Copyright (C) 1986, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "value.h"
+#include "expression.h"
+
+
+value value_x_binop ();
+value value_subscripted_rvalue ();
+
+value
+value_add (arg1, arg2)
+ value arg1, arg2;
+{
+ register value val, valint, valptr;
+ register int len;
+
+ COERCE_ARRAY (arg1);
+ COERCE_ARRAY (arg2);
+
+ if ((TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR
+ || TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_PTR)
+ &&
+ (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_INT
+ || TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_INT))
+ /* Exactly one argument is a pointer, and one is an integer. */
+ {
+ if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR)
+ {
+ valptr = arg1;
+ valint = arg2;
+ }
+ else
+ {
+ valptr = arg2;
+ valint = arg1;
+ }
+ len = TYPE_LENGTH (TYPE_TARGET_TYPE (VALUE_TYPE (valptr)));
+ if (len == 0) len = 1; /* For (void *) */
+ val = value_from_long (builtin_type_long,
+ value_as_long (valptr)
+ + (len * value_as_long (valint)));
+ VALUE_TYPE (val) = VALUE_TYPE (valptr);
+ return val;
+ }
+
+ return value_binop (arg1, arg2, BINOP_ADD);
+}
+
+value
+value_sub (arg1, arg2)
+ value arg1, arg2;
+{
+ register value val;
+
+ COERCE_ARRAY (arg1);
+ COERCE_ARRAY (arg2);
+
+ if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR
+ &&
+ TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_INT)
+ {
+ val = value_from_long (builtin_type_long,
+ value_as_long (arg1)
+ - TYPE_LENGTH (TYPE_TARGET_TYPE (VALUE_TYPE (arg1))) * value_as_long (arg2));
+ VALUE_TYPE (val) = VALUE_TYPE (arg1);
+ return val;
+ }
+
+ if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR
+ &&
+ VALUE_TYPE (arg1) == VALUE_TYPE (arg2))
+ {
+ val = value_from_long (builtin_type_long,
+ (value_as_long (arg1) - value_as_long (arg2))
+ / TYPE_LENGTH (TYPE_TARGET_TYPE (VALUE_TYPE (arg1))));
+ return val;
+ }
+
+ return value_binop (arg1, arg2, BINOP_SUB);
+}
+
+/* Return the value of ARRAY[IDX]. */
+
+value
+value_subscript (array, idx)
+ value array, idx;
+{
+ if (TYPE_CODE (VALUE_TYPE (array)) == TYPE_CODE_ARRAY
+ && VALUE_LVAL (array) != lval_memory)
+ return value_subscripted_rvalue (array, idx);
+ else
+ return value_ind (value_add (array, idx));
+}
+
+/* Return the value of EXPR[IDX], expr an aggregate rvalue
+ (eg, a vector register) */
+
+value
+value_subscripted_rvalue (array, idx)
+ value array, idx;
+{
+ struct type *elt_type = TYPE_TARGET_TYPE (VALUE_TYPE (array));
+ int elt_size = TYPE_LENGTH (elt_type);
+ int elt_offs = elt_size * value_as_long (idx);
+ value v;
+
+ if (elt_offs >= TYPE_LENGTH (VALUE_TYPE (array)))
+ error ("no such vector element");
+
+ if (TYPE_CODE (elt_type) == TYPE_CODE_FLT)
+ {
+ if (elt_size == sizeof (float))
+ v = value_from_double (elt_type, (double) *(float *)
+ (VALUE_CONTENTS (array) + elt_offs));
+ else
+ v = value_from_double (elt_type, *(double *)
+ (VALUE_CONTENTS (array) + elt_offs));
+ }
+ else
+ {
+ int offs;
+ union {int i; char c;} test;
+ test.i = 1;
+ if (test.c == 1)
+ offs = 0;
+ else
+ offs = sizeof (LONGEST) - elt_size;
+ v = value_from_long (elt_type, *(LONGEST *)
+ (VALUE_CONTENTS (array) + elt_offs - offs));
+ }
+
+ if (VALUE_LVAL (array) == lval_internalvar)
+ VALUE_LVAL (v) = lval_internalvar_component;
+ else
+ VALUE_LVAL (v) = not_lval;
+ VALUE_ADDRESS (v) = VALUE_ADDRESS (array);
+ VALUE_OFFSET (v) = VALUE_OFFSET (array) + elt_offs;
+ VALUE_BITSIZE (v) = elt_size * 8;
+ return v;
+}
+
+/* Check to see if either argument is a structure. This is called so
+ we know whether to go ahead with the normal binop or look for a
+ user defined function instead.
+
+ For now, we do not overload the `=' operator. */
+
+int
+binop_user_defined_p (op, arg1, arg2)
+ enum exp_opcode op;
+ value arg1, arg2;
+{
+ if (op == BINOP_ASSIGN)
+ return 0;
+ return (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_STRUCT
+ || TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_STRUCT
+ || (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_REF
+ && TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg1))) == TYPE_CODE_STRUCT)
+ || (TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_REF
+ && TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2))) == TYPE_CODE_STRUCT));
+}
+
+/* Check to see if argument is a structure. This is called so
+ we know whether to go ahead with the normal unop or look for a
+ user defined function instead.
+
+ For now, we do not overload the `&' operator. */
+
+int unop_user_defined_p (op, arg1)
+ enum exp_opcode op;
+ value arg1;
+{
+ if (op == UNOP_ADDR)
+ return 0;
+ return (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_STRUCT
+ || (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_REF
+ && TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg1))) == TYPE_CODE_STRUCT));
+}
+
+/* We know either arg1 or arg2 is a structure, so try to find the right
+ user defined function. Create an argument vector that calls
+ arg1.operator @ (arg1,arg2) and return that value (where '@' is any
+ binary operator which is legal for GNU C++). */
+
+value
+value_x_binop (arg1, arg2, op, otherop)
+ value arg1, arg2;
+ int op, otherop;
+{
+ value * argvec;
+ char *ptr;
+ char tstr[13];
+ int static_memfuncp;
+
+ COERCE_ENUM (arg1);
+ COERCE_ENUM (arg2);
+
+ /* now we know that what we have to do is construct our
+ arg vector and find the right function to call it with. */
+
+ if (TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_STRUCT)
+ error ("friend functions not implemented yet");
+
+ argvec = (value *) alloca (sizeof (value) * 4);
+ argvec[1] = value_addr (arg1);
+ argvec[2] = arg2;
+ argvec[3] = 0;
+
+ /* make the right function name up */
+ strcpy(tstr, "operator __");
+ ptr = tstr+9;
+ switch (op)
+ {
+ case BINOP_ADD: strcpy(ptr,"+"); break;
+ case BINOP_SUB: strcpy(ptr,"-"); break;
+ case BINOP_MUL: strcpy(ptr,"*"); break;
+ case BINOP_DIV: strcpy(ptr,"/"); break;
+ case BINOP_REM: strcpy(ptr,"%"); break;
+ case BINOP_LSH: strcpy(ptr,"<<"); break;
+ case BINOP_RSH: strcpy(ptr,">>"); break;
+ case BINOP_LOGAND: strcpy(ptr,"&"); break;
+ case BINOP_LOGIOR: strcpy(ptr,"|"); break;
+ case BINOP_LOGXOR: strcpy(ptr,"^"); break;
+ case BINOP_AND: strcpy(ptr,"&&"); break;
+ case BINOP_OR: strcpy(ptr,"||"); break;
+ case BINOP_MIN: strcpy(ptr,"<?"); break;
+ case BINOP_MAX: strcpy(ptr,">?"); break;
+ case BINOP_ASSIGN: strcpy(ptr,"="); break;
+ case BINOP_ASSIGN_MODIFY:
+ switch (otherop)
+ {
+ case BINOP_ADD: strcpy(ptr,"+="); break;
+ case BINOP_SUB: strcpy(ptr,"-="); break;
+ case BINOP_MUL: strcpy(ptr,"*="); break;
+ case BINOP_DIV: strcpy(ptr,"/="); break;
+ case BINOP_REM: strcpy(ptr,"%="); break;
+ case BINOP_LOGAND: strcpy(ptr,"&="); break;
+ case BINOP_LOGIOR: strcpy(ptr,"|="); break;
+ case BINOP_LOGXOR: strcpy(ptr,"^="); break;
+ default:
+ error ("Invalid binary operation specified.");
+ }
+ break;
+ case BINOP_SUBSCRIPT: strcpy(ptr,"[]"); break;
+ case BINOP_EQUAL: strcpy(ptr,"=="); break;
+ case BINOP_NOTEQUAL: strcpy(ptr,"!="); break;
+ case BINOP_LESS: strcpy(ptr,"<"); break;
+ case BINOP_GTR: strcpy(ptr,">"); break;
+ case BINOP_GEQ: strcpy(ptr,">="); break;
+ case BINOP_LEQ: strcpy(ptr,"<="); break;
+ default:
+ error ("Invalid binary operation specified.");
+ }
+ argvec[0] = value_struct_elt (arg1, argvec+1, tstr, &static_memfuncp, "structure");
+ if (argvec[0])
+ {
+ if (static_memfuncp)
+ {
+ argvec[1] = argvec[0];
+ argvec++;
+ }
+ return call_function (argvec[0], 2 - static_memfuncp, argvec + 1);
+ }
+ error ("member function %s not found", tstr);
+}
+
+/* We know that arg1 is a structure, so try to find a unary user
+ defined operator that matches the operator in question.
+ Create an argument vector that calls arg1.operator @ (arg1)
+ and return that value (where '@' is (almost) any unary operator which
+ is legal for GNU C++). */
+
+value
+value_x_unop (arg1, op)
+ value arg1;
+ int op;
+{
+ value * argvec;
+ char *ptr;
+ char tstr[13];
+ int static_memfuncp;
+
+ COERCE_ENUM (arg1);
+
+ /* now we know that what we have to do is construct our
+ arg vector and find the right function to call it with. */
+
+ if (TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_STRUCT)
+ error ("friend functions not implemented yet");
+
+ argvec = (value *) alloca (sizeof (value) * 3);
+ argvec[1] = value_addr (arg1);
+ argvec[2] = 0;
+
+ /* make the right function name up */
+ strcpy(tstr,"operator __");
+ ptr = tstr+9;
+ switch (op)
+ {
+ case UNOP_PREINCREMENT: strcpy(ptr,"++"); break;
+ case UNOP_PREDECREMENT: strcpy(ptr,"++"); break;
+ case UNOP_POSTINCREMENT: strcpy(ptr,"++"); break;
+ case UNOP_POSTDECREMENT: strcpy(ptr,"++"); break;
+ case UNOP_ZEROP: strcpy(ptr,"!"); break;
+ case UNOP_LOGNOT: strcpy(ptr,"~"); break;
+ case UNOP_NEG: strcpy(ptr,"-"); break;
+ default:
+ error ("Invalid binary operation specified.");
+ }
+ argvec[0] = value_struct_elt (arg1, argvec+1, tstr, &static_memfuncp, "structure");
+ if (argvec[0])
+ {
+ if (static_memfuncp)
+ {
+ argvec[1] = argvec[0];
+ argvec++;
+ }
+ return call_function (argvec[0], 1 - static_memfuncp, argvec + 1);
+ }
+ error ("member function %s not found", tstr);
+}
+
+/* Perform a binary operation on two integers or two floats.
+ Does not support addition and subtraction on pointers;
+ use value_add or value_sub if you want to handle those possibilities. */
+
+value
+value_binop (arg1, arg2, op)
+ value arg1, arg2;
+ int op;
+{
+ register value val;
+
+ COERCE_ENUM (arg1);
+ COERCE_ENUM (arg2);
+
+ if ((TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_FLT
+ &&
+ TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_INT)
+ ||
+ (TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_FLT
+ &&
+ TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_INT))
+ error ("Argument to arithmetic operation not a number.");
+
+ if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_FLT
+ ||
+ TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_FLT)
+ {
+ double v1, v2, v;
+ v1 = value_as_double (arg1);
+ v2 = value_as_double (arg2);
+ switch (op)
+ {
+ case BINOP_ADD:
+ v = v1 + v2;
+ break;
+
+ case BINOP_SUB:
+ v = v1 - v2;
+ break;
+
+ case BINOP_MUL:
+ v = v1 * v2;
+ break;
+
+ case BINOP_DIV:
+ v = v1 / v2;
+ break;
+
+ default:
+ error ("Integer-only operation on floating point number.");
+ }
+
+ val = allocate_value (builtin_type_double);
+ *(double *) VALUE_CONTENTS (val) = v;
+ }
+ else
+ /* Integral operations here. */
+ {
+ /* Should we promote to unsigned longest? */
+ if ((TYPE_UNSIGNED (VALUE_TYPE (arg1))
+ || TYPE_UNSIGNED (VALUE_TYPE (arg2)))
+ && (TYPE_LENGTH (VALUE_TYPE (arg1)) >= sizeof (unsigned LONGEST)
+ || TYPE_LENGTH (VALUE_TYPE (arg2)) >= sizeof (unsigned LONGEST)))
+ {
+ unsigned LONGEST v1, v2, v;
+ v1 = (unsigned LONGEST) value_as_long (arg1);
+ v2 = (unsigned LONGEST) value_as_long (arg2);
+
+ switch (op)
+ {
+ case BINOP_ADD:
+ v = v1 + v2;
+ break;
+
+ case BINOP_SUB:
+ v = v1 - v2;
+ break;
+
+ case BINOP_MUL:
+ v = v1 * v2;
+ break;
+
+ case BINOP_DIV:
+ v = v1 / v2;
+ break;
+
+ case BINOP_REM:
+ v = v1 % v2;
+ break;
+
+ case BINOP_LSH:
+ v = v1 << v2;
+ break;
+
+ case BINOP_RSH:
+ v = v1 >> v2;
+ break;
+
+ case BINOP_LOGAND:
+ v = v1 & v2;
+ break;
+
+ case BINOP_LOGIOR:
+ v = v1 | v2;
+ break;
+
+ case BINOP_LOGXOR:
+ v = v1 ^ v2;
+ break;
+
+ case BINOP_AND:
+ v = v1 && v2;
+ break;
+
+ case BINOP_OR:
+ v = v1 || v2;
+ break;
+
+ case BINOP_MIN:
+ v = v1 < v2 ? v1 : v2;
+ break;
+
+ case BINOP_MAX:
+ v = v1 > v2 ? v1 : v2;
+ break;
+
+ default:
+ error ("Invalid binary operation on numbers.");
+ }
+
+ val = allocate_value (BUILTIN_TYPE_UNSIGNED_LONGEST);
+ *(unsigned LONGEST *) VALUE_CONTENTS (val) = v;
+ }
+ else
+ {
+ LONGEST v1, v2, v;
+ v1 = value_as_long (arg1);
+ v2 = value_as_long (arg2);
+
+ switch (op)
+ {
+ case BINOP_ADD:
+ v = v1 + v2;
+ break;
+
+ case BINOP_SUB:
+ v = v1 - v2;
+ break;
+
+ case BINOP_MUL:
+ v = v1 * v2;
+ break;
+
+ case BINOP_DIV:
+ v = v1 / v2;
+ break;
+
+ case BINOP_REM:
+ v = v1 % v2;
+ break;
+
+ case BINOP_LSH:
+ v = v1 << v2;
+ break;
+
+ case BINOP_RSH:
+ v = v1 >> v2;
+ break;
+
+ case BINOP_LOGAND:
+ v = v1 & v2;
+ break;
+
+ case BINOP_LOGIOR:
+ v = v1 | v2;
+ break;
+
+ case BINOP_LOGXOR:
+ v = v1 ^ v2;
+ break;
+
+ case BINOP_AND:
+ v = v1 && v2;
+ break;
+
+ case BINOP_OR:
+ v = v1 || v2;
+ break;
+
+ case BINOP_MIN:
+ v = v1 < v2 ? v1 : v2;
+ break;
+
+ case BINOP_MAX:
+ v = v1 > v2 ? v1 : v2;
+ break;
+
+ default:
+ error ("Invalid binary operation on numbers.");
+ }
+
+ val = allocate_value (BUILTIN_TYPE_LONGEST);
+ *(LONGEST *) VALUE_CONTENTS (val) = v;
+ }
+ }
+
+ return val;
+}
+
+/* Simulate the C operator ! -- return 1 if ARG1 contains zeros. */
+
+int
+value_zerop (arg1)
+ value arg1;
+{
+ register int len;
+ register char *p;
+
+ COERCE_ARRAY (arg1);
+
+ len = TYPE_LENGTH (VALUE_TYPE (arg1));
+ p = VALUE_CONTENTS (arg1);
+
+ while (--len >= 0)
+ {
+ if (*p++)
+ break;
+ }
+
+ return len < 0;
+}
+
+/* Simulate the C operator == by returning a 1
+ iff ARG1 and ARG2 have equal contents. */
+
+int
+value_equal (arg1, arg2)
+ register value arg1, arg2;
+
+{
+ register int len;
+ register char *p1, *p2;
+ enum type_code code1;
+ enum type_code code2;
+
+ COERCE_ARRAY (arg1);
+ COERCE_ARRAY (arg2);
+
+ code1 = TYPE_CODE (VALUE_TYPE (arg1));
+ code2 = TYPE_CODE (VALUE_TYPE (arg2));
+
+ if (code1 == TYPE_CODE_INT && code2 == TYPE_CODE_INT)
+ return value_as_long (arg1) == value_as_long (arg2);
+ else if ((code1 == TYPE_CODE_FLT || code1 == TYPE_CODE_INT)
+ && (code2 == TYPE_CODE_FLT || code2 == TYPE_CODE_INT))
+ return value_as_double (arg1) == value_as_double (arg2);
+ else if ((code1 == TYPE_CODE_PTR && code2 == TYPE_CODE_INT)
+ || (code2 == TYPE_CODE_PTR && code1 == TYPE_CODE_INT))
+ return (char *) value_as_long (arg1) == (char *) value_as_long (arg2);
+ else if (code1 == code2
+ && ((len = TYPE_LENGTH (VALUE_TYPE (arg1)))
+ == TYPE_LENGTH (VALUE_TYPE (arg2))))
+ {
+ p1 = VALUE_CONTENTS (arg1);
+ p2 = VALUE_CONTENTS (arg2);
+ while (--len >= 0)
+ {
+ if (*p1++ != *p2++)
+ break;
+ }
+ return len < 0;
+ }
+ else
+ error ("Invalid type combination in equality test.");
+}
+
+/* Simulate the C operator < by returning 1
+ iff ARG1's contents are less than ARG2's. */
+
+int
+value_less (arg1, arg2)
+ register value arg1, arg2;
+{
+ register enum type_code code1;
+ register enum type_code code2;
+
+ COERCE_ARRAY (arg1);
+ COERCE_ARRAY (arg2);
+
+ code1 = TYPE_CODE (VALUE_TYPE (arg1));
+ code2 = TYPE_CODE (VALUE_TYPE (arg2));
+
+ if (code1 == TYPE_CODE_INT && code2 == TYPE_CODE_INT)
+ return value_as_long (arg1) < value_as_long (arg2);
+ else if ((code1 == TYPE_CODE_FLT || code1 == TYPE_CODE_INT)
+ && (code2 == TYPE_CODE_FLT || code2 == TYPE_CODE_INT))
+ return value_as_double (arg1) < value_as_double (arg2);
+ else if ((code1 == TYPE_CODE_PTR || code1 == TYPE_CODE_INT)
+ && (code2 == TYPE_CODE_PTR || code2 == TYPE_CODE_INT))
+ return (char *) value_as_long (arg1) < (char *) value_as_long (arg2);
+ else
+ error ("Invalid type combination in ordering comparison.");
+}
+
+/* The unary operators - and ~. Both free the argument ARG1. */
+
+value
+value_neg (arg1)
+ register value arg1;
+{
+ register struct type *type;
+
+ COERCE_ENUM (arg1);
+
+ type = VALUE_TYPE (arg1);
+
+ if (TYPE_CODE (type) == TYPE_CODE_FLT)
+ return value_from_double (type, - value_as_double (arg1));
+ else if (TYPE_CODE (type) == TYPE_CODE_INT)
+ return value_from_long (type, - value_as_long (arg1));
+ else
+ error ("Argument to negate operation not a number.");
+}
+
+value
+value_lognot (arg1)
+ register value arg1;
+{
+ COERCE_ENUM (arg1);
+
+ if (TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_INT)
+ error ("Argument to complement operation not an integer.");
+
+ return value_from_long (VALUE_TYPE (arg1), ~ value_as_long (arg1));
+}
+
diff --git a/gnu/usr.bin/gdb/valops.c b/gnu/usr.bin/gdb/valops.c
new file mode 100644
index 0000000..ab5652c
--- /dev/null
+++ b/gnu/usr.bin/gdb/valops.c
@@ -0,0 +1,1418 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)valops.c 6.4 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Perform non-arithmetic operations on values, for GDB.
+ Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "stdio.h"
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "value.h"
+#include "frame.h"
+#include "inferior.h"
+
+/* Cast value ARG2 to type TYPE and return as a value.
+ More general than a C cast: accepts any two types of the same length,
+ and if ARG2 is an lvalue it can be cast into anything at all. */
+
+value
+value_cast (type, arg2)
+ struct type *type;
+ register value arg2;
+{
+ register enum type_code code1;
+ register enum type_code code2;
+ register int scalar;
+
+ /* Coerce arrays but not enums. Enums will work as-is
+ and coercing them would cause an infinite recursion. */
+ if (TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_ENUM)
+ COERCE_ARRAY (arg2);
+
+ code1 = TYPE_CODE (type);
+ code2 = TYPE_CODE (VALUE_TYPE (arg2));
+ scalar = (code2 == TYPE_CODE_INT || code2 == TYPE_CODE_FLT
+ || code2 == TYPE_CODE_ENUM);
+
+ if (code1 == TYPE_CODE_FLT && scalar)
+ return value_from_double (type, value_as_double (arg2));
+ else if ((code1 == TYPE_CODE_INT || code1 == TYPE_CODE_ENUM)
+ && (scalar || code2 == TYPE_CODE_PTR))
+ return value_from_long (type, value_as_long (arg2));
+ else if (TYPE_LENGTH (type) == TYPE_LENGTH (VALUE_TYPE (arg2)))
+ {
+ VALUE_TYPE (arg2) = type;
+ return arg2;
+ }
+ else if (VALUE_LVAL (arg2) == lval_memory)
+ {
+ return value_at (type, VALUE_ADDRESS (arg2) + VALUE_OFFSET (arg2));
+ }
+ else
+ error ("Invalid cast.");
+}
+
+/* Create a value of type TYPE that is zero, and return it. */
+
+value
+value_zero (type, lv)
+ struct type *type;
+ enum lval_type lv;
+{
+ register value val = allocate_value (type);
+
+ bzero (VALUE_CONTENTS (val), TYPE_LENGTH (type));
+ VALUE_LVAL (val) = lv;
+
+ return val;
+}
+
+/* Return the value with a specified type located at specified address. */
+
+value
+value_at (type, addr)
+ struct type *type;
+ CORE_ADDR addr;
+{
+ register value val = allocate_value (type);
+ int temp;
+
+ temp = read_memory (addr, VALUE_CONTENTS (val), TYPE_LENGTH (type));
+ if (temp)
+ {
+ if (have_inferior_p () && !remote_debugging)
+ print_sys_errmsg ("ptrace", temp);
+ /* Actually, address between addr and addr + len was out of bounds. */
+ error ("Cannot read memory: address 0x%x out of bounds.", addr);
+ }
+
+ VALUE_LVAL (val) = lval_memory;
+ VALUE_ADDRESS (val) = addr;
+
+ return val;
+}
+
+/* Store the contents of FROMVAL into the location of TOVAL.
+ Return a new value with the location of TOVAL and contents of FROMVAL. */
+
+value
+value_assign (toval, fromval)
+ register value toval, fromval;
+{
+ register struct type *type = VALUE_TYPE (toval);
+ register value val;
+ char raw_buffer[MAX_REGISTER_RAW_SIZE];
+ char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE];
+ int use_buffer = 0;
+
+ extern CORE_ADDR find_saved_register ();
+
+ COERCE_ARRAY (fromval);
+
+ if (VALUE_LVAL (toval) != lval_internalvar)
+ fromval = value_cast (type, fromval);
+
+ /* If TOVAL is a special machine register requiring conversion
+ of program values to a special raw format,
+ convert FROMVAL's contents now, with result in `raw_buffer',
+ and set USE_BUFFER to the number of bytes to write. */
+
+ if (VALUE_REGNO (toval) >= 0
+ && REGISTER_CONVERTIBLE (VALUE_REGNO (toval)))
+ {
+ int regno = VALUE_REGNO (toval);
+ if (VALUE_TYPE (fromval) != REGISTER_VIRTUAL_TYPE (regno))
+ fromval = value_cast (REGISTER_VIRTUAL_TYPE (regno), fromval);
+ bcopy (VALUE_CONTENTS (fromval), virtual_buffer,
+ REGISTER_VIRTUAL_SIZE (regno));
+ REGISTER_CONVERT_TO_RAW (regno, virtual_buffer, raw_buffer);
+ use_buffer = REGISTER_RAW_SIZE (regno);
+ }
+
+ switch (VALUE_LVAL (toval))
+ {
+ case lval_internalvar:
+ set_internalvar (VALUE_INTERNALVAR (toval), fromval);
+ break;
+
+ case lval_internalvar_component:
+ set_internalvar_component (VALUE_INTERNALVAR (toval),
+ VALUE_OFFSET (toval),
+ VALUE_BITPOS (toval),
+ VALUE_BITSIZE (toval),
+ fromval);
+ break;
+
+ case lval_memory:
+ if (VALUE_BITSIZE (toval))
+ {
+ int val;
+ read_memory (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+ &val, sizeof val);
+ modify_field (&val, (int) value_as_long (fromval),
+ VALUE_BITPOS (toval), VALUE_BITSIZE (toval));
+ write_memory (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+ &val, sizeof val);
+ }
+ else if (use_buffer)
+ write_memory (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+ raw_buffer, use_buffer);
+ else
+ write_memory (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+ VALUE_CONTENTS (fromval), TYPE_LENGTH (type));
+ break;
+
+ case lval_register:
+ if (VALUE_BITSIZE (toval))
+ {
+ int val;
+
+ read_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+ &val, sizeof val);
+ modify_field (&val, (int) value_as_long (fromval),
+ VALUE_BITPOS (toval), VALUE_BITSIZE (toval));
+ write_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+ &val, sizeof val);
+ }
+ else if (use_buffer)
+ write_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+ raw_buffer, use_buffer);
+ else
+ write_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+ VALUE_CONTENTS (fromval), TYPE_LENGTH (type));
+ break;
+
+ case lval_reg_frame_relative:
+ {
+ /* value is stored in a series of registers in the frame
+ specified by the structure. Copy that value out, modify
+ it, and copy it back in. */
+ int amount_to_copy = (VALUE_BITSIZE (toval) ? 1 : TYPE_LENGTH (type));
+ int reg_size = REGISTER_RAW_SIZE (VALUE_FRAME_REGNUM (toval));
+ int byte_offset = VALUE_OFFSET (toval) % reg_size;
+ int reg_offset = VALUE_OFFSET (toval) / reg_size;
+ int amount_copied;
+ char *buffer = (char *) alloca (amount_to_copy);
+ int regno;
+ FRAME frame;
+ CORE_ADDR addr;
+
+ /* Figure out which frame this is in currently. */
+ for (frame = get_current_frame ();
+ frame && FRAME_FP (frame) != VALUE_FRAME (toval);
+ frame = get_prev_frame (frame))
+ ;
+
+ if (!frame)
+ error ("Value being assigned to is no longer active.");
+
+ amount_to_copy += (reg_size - amount_to_copy % reg_size);
+
+ /* Copy it out. */
+ for ((regno = VALUE_FRAME_REGNUM (toval) + reg_offset,
+ amount_copied = 0);
+ amount_copied < amount_to_copy;
+ amount_copied += reg_size, regno++)
+ {
+ addr = find_saved_register (frame, regno);
+ if (addr == 0)
+ read_register_bytes (REGISTER_BYTE (regno),
+ buffer + amount_copied,
+ reg_size);
+ else
+ read_memory (addr, buffer + amount_copied, reg_size);
+ }
+
+ /* Modify what needs to be modified. */
+ if (VALUE_BITSIZE (toval))
+ modify_field (buffer + byte_offset,
+ (int) value_as_long (fromval),
+ VALUE_BITPOS (toval), VALUE_BITSIZE (toval));
+ else if (use_buffer)
+ bcopy (raw_buffer, buffer + byte_offset, use_buffer);
+ else
+ bcopy (VALUE_CONTENTS (fromval), buffer + byte_offset,
+ TYPE_LENGTH (type));
+
+ /* Copy it back. */
+ for ((regno = VALUE_FRAME_REGNUM (toval) + reg_offset,
+ amount_copied = 0);
+ amount_copied < amount_to_copy;
+ amount_copied += reg_size, regno++)
+ {
+ addr = find_saved_register (frame, regno);
+ if (addr == 0)
+ write_register_bytes (REGISTER_BYTE (regno),
+ buffer + amount_copied,
+ reg_size);
+ else
+ write_memory (addr, buffer + amount_copied, reg_size);
+ }
+ }
+ break;
+
+
+ default:
+ error ("Left side of = operation is not an lvalue.");
+ }
+
+ /* Return a value just like TOVAL except with the contents of FROMVAL
+ (except in the case of the type if TOVAL is an internalvar). */
+
+ if (VALUE_LVAL (toval) == lval_internalvar
+ || VALUE_LVAL (toval) == lval_internalvar_component)
+ {
+ type = VALUE_TYPE (fromval);
+ }
+
+ val = allocate_value (type);
+ bcopy (toval, val, VALUE_CONTENTS (val) - (char *) val);
+ bcopy (VALUE_CONTENTS (fromval), VALUE_CONTENTS (val), TYPE_LENGTH (type));
+ VALUE_TYPE (val) = type;
+
+ return val;
+}
+
+/* Extend a value VAL to COUNT repetitions of its type. */
+
+value
+value_repeat (arg1, count)
+ value arg1;
+ int count;
+{
+ register value val;
+
+ if (VALUE_LVAL (arg1) != lval_memory)
+ error ("Only values in memory can be extended with '@'.");
+ if (count < 1)
+ error ("Invalid number %d of repetitions.", count);
+
+ val = allocate_repeat_value (VALUE_TYPE (arg1), count);
+
+ read_memory (VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1),
+ VALUE_CONTENTS (val),
+ TYPE_LENGTH (VALUE_TYPE (val)) * count);
+ VALUE_LVAL (val) = lval_memory;
+ VALUE_ADDRESS (val) = VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1);
+
+ return val;
+}
+
+value
+value_of_variable (var)
+ struct symbol *var;
+{
+ return read_var_value (var, (FRAME) 0);
+}
+
+/* Given a value which is an array, return a value which is
+ a pointer to its first element. */
+
+value
+value_coerce_array (arg1)
+ value arg1;
+{
+ register struct type *type;
+ register value val;
+
+ if (VALUE_LVAL (arg1) != lval_memory)
+ error ("Attempt to take address of value not located in memory.");
+
+ /* Get type of elements. */
+ if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_ARRAY)
+ type = TYPE_TARGET_TYPE (VALUE_TYPE (arg1));
+ else
+ /* A phony array made by value_repeat.
+ Its type is the type of the elements, not an array type. */
+ type = VALUE_TYPE (arg1);
+
+ /* Get the type of the result. */
+ type = lookup_pointer_type (type);
+ val = value_from_long (builtin_type_long,
+ (LONGEST) (VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1)));
+ VALUE_TYPE (val) = type;
+ return val;
+}
+
+/* Return a pointer value for the object for which ARG1 is the contents. */
+
+value
+value_addr (arg1)
+ value arg1;
+{
+ register struct type *type;
+ register value val, arg1_coerced;
+
+ /* Taking the address of an array is really a no-op
+ once the array is coerced to a pointer to its first element. */
+ arg1_coerced = arg1;
+ COERCE_ARRAY (arg1_coerced);
+ if (arg1 != arg1_coerced)
+ return arg1_coerced;
+
+ if (VALUE_LVAL (arg1) != lval_memory)
+ error ("Attempt to take address of value not located in memory.");
+
+ /* Get the type of the result. */
+ type = lookup_pointer_type (VALUE_TYPE (arg1));
+ val = value_from_long (builtin_type_long,
+ (LONGEST) (VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1)));
+ VALUE_TYPE (val) = type;
+ return val;
+}
+
+/* Given a value of a pointer type, apply the C unary * operator to it. */
+
+value
+value_ind (arg1)
+ value arg1;
+{
+ /* Must do this before COERCE_ARRAY, otherwise an infinite loop
+ will result */
+ if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_REF)
+ return value_at (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)),
+ (CORE_ADDR) value_as_long (arg1));
+
+ COERCE_ARRAY (arg1);
+
+ if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_MEMBER)
+ error ("not implemented: member types in value_ind");
+
+ /* Allow * on an integer so we can cast it to whatever we want.
+ This returns an int, which seems like the most C-like thing
+ to do. "long long" variables are rare enough that
+ BUILTIN_TYPE_LONGEST would seem to be a mistake. */
+ if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_INT)
+ return value_at (builtin_type_int,
+ (CORE_ADDR) value_as_long (arg1));
+ else if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR)
+ return value_at (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)),
+ (CORE_ADDR) value_as_long (arg1));
+ else if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_REF)
+ return value_at (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)),
+ (CORE_ADDR) value_as_long (arg1));
+ error ("Attempt to take contents of a non-pointer value.");
+}
+
+/* Pushing small parts of stack frames. */
+
+/* Push one word (the size of object that a register holds). */
+
+CORE_ADDR
+push_word (sp, buffer)
+ CORE_ADDR sp;
+ REGISTER_TYPE buffer;
+{
+ register int len = sizeof (REGISTER_TYPE);
+
+#if 1 INNER_THAN 2
+ sp -= len;
+ write_memory (sp, &buffer, len);
+#else /* stack grows upward */
+ write_memory (sp, &buffer, len);
+ sp += len;
+#endif /* stack grows upward */
+
+ return sp;
+}
+
+/* Push LEN bytes with data at BUFFER. */
+
+CORE_ADDR
+push_bytes (sp, buffer, len)
+ CORE_ADDR sp;
+ char *buffer;
+ int len;
+{
+#if 1 INNER_THAN 2
+ sp -= len;
+ write_memory (sp, buffer, len);
+#else /* stack grows upward */
+ write_memory (sp, buffer, len);
+ sp += len;
+#endif /* stack grows upward */
+
+ return sp;
+}
+
+/* Push onto the stack the specified value VALUE. */
+
+CORE_ADDR
+value_push (sp, arg)
+ register CORE_ADDR sp;
+ value arg;
+{
+ register int len = TYPE_LENGTH (VALUE_TYPE (arg));
+
+#if 1 INNER_THAN 2
+ sp -= len;
+ write_memory (sp, VALUE_CONTENTS (arg), len);
+#else /* stack grows upward */
+ write_memory (sp, VALUE_CONTENTS (arg), len);
+ sp += len;
+#endif /* stack grows upward */
+
+ return sp;
+}
+
+/* Perform the standard coercions that are specified
+ for arguments to be passed to C functions. */
+
+value
+value_arg_coerce (arg)
+ value arg;
+{
+ register struct type *type;
+
+ COERCE_ENUM (arg);
+
+ type = VALUE_TYPE (arg);
+
+ if (TYPE_CODE (type) == TYPE_CODE_INT
+ && TYPE_LENGTH (type) < sizeof (int))
+ return value_cast (builtin_type_int, arg);
+
+ if (type == builtin_type_float)
+ return value_cast (builtin_type_double, arg);
+
+ return arg;
+}
+
+/* Push the value ARG, first coercing it as an argument
+ to a C function. */
+
+CORE_ADDR
+value_arg_push (sp, arg)
+ register CORE_ADDR sp;
+ value arg;
+{
+ return value_push (sp, value_arg_coerce (arg));
+}
+
+#ifdef NEW_CALL_FUNCTION
+
+int
+arg_stacklen(nargs, args)
+ int nargs;
+ value *args;
+{
+ int len = 0;
+
+ while (--nargs >= 0)
+ len += TYPE_LENGTH(VALUE_TYPE(value_arg_coerce(args[nargs])));
+
+ return len;
+}
+
+CORE_ADDR
+function_address(function, type)
+ value function;
+ struct type **type;
+{
+ register CORE_ADDR funaddr;
+ register struct type *ftype = VALUE_TYPE(function);
+ register enum type_code code = TYPE_CODE(ftype);
+
+ /*
+ * If it's a member function, just look at the function part
+ * of it.
+ */
+
+ /* Determine address to call. */
+ if (code == TYPE_CODE_FUNC || code == TYPE_CODE_METHOD) {
+ funaddr = VALUE_ADDRESS(function);
+ *type = TYPE_TARGET_TYPE(ftype);
+ } else if (code == TYPE_CODE_PTR) {
+ funaddr = value_as_long(function);
+ if (TYPE_CODE(TYPE_TARGET_TYPE(ftype)) == TYPE_CODE_FUNC
+ || TYPE_CODE(TYPE_TARGET_TYPE(ftype)) == TYPE_CODE_METHOD)
+ *type = TYPE_TARGET_TYPE(TYPE_TARGET_TYPE(ftype));
+ else
+ *type = builtin_type_int;
+ } else if (code == TYPE_CODE_INT) {
+ /*
+ * Handle the case of functions lacking debugging
+ * info. Their values are characters since their
+ * addresses are char
+ */
+ if (TYPE_LENGTH(ftype) == 1)
+
+ funaddr = value_as_long(value_addr(function));
+ else
+ /*
+ * Handle integer used as address of a
+ * function.
+ */
+ funaddr = value_as_long(function);
+
+ *type = builtin_type_int;
+ } else
+ error("Invalid data type for function to be called.");
+
+ return funaddr;
+}
+
+/* Perform a function call in the inferior.
+ ARGS is a vector of values of arguments (NARGS of them).
+ FUNCTION is a value, the function to be called.
+ Returns a value representing what the function returned.
+ May fail to return, if a breakpoint or signal is hit
+ during the execution of the function. */
+
+value
+call_function(function, nargs, args)
+ value function;
+ int nargs;
+ value *args;
+{
+ register CORE_ADDR sp, pc;
+ struct type *value_type;
+ struct inferior_status inf_status;
+ struct cleanup *old_chain;
+ register CORE_ADDR funaddr;
+ int struct_return_bytes;
+ char retbuf[REGISTER_BYTES];
+
+ if (!have_inferior_p())
+ error("Cannot invoke functions if the inferior is not running.");
+
+ save_inferior_status(&inf_status, 1);
+ old_chain = make_cleanup(restore_inferior_status, &inf_status);
+
+ sp = read_register(SP_REGNUM);
+ funaddr = function_address(function, &value_type);
+ /*
+ * Are we returning a value using a structure return or a
+ * normal value return?
+ */
+ if (using_struct_return(function, funaddr, value_type))
+ struct_return_bytes = TYPE_LENGTH(value_type);
+ else
+ struct_return_bytes = 0;
+ /*
+ * Create a call sequence customized for this function and
+ * the number of arguments for it.
+ */
+ pc = setup_dummy(sp, funaddr, nargs, args,
+ struct_return_bytes, value_arg_push);
+
+ /*
+ * Execute the stack dummy stub. The register state will be
+ * returned in retbuf. It is restored below.
+ */
+ run_stack_dummy(pc, retbuf);
+
+ /*
+ * This will restore the register context that existed before
+ * we called the dummy function.
+ */
+ do_cleanups(old_chain);
+
+ return value_being_returned(value_type, retbuf, struct_return_bytes);
+}
+#else
+
+/* Perform a function call in the inferior.
+ ARGS is a vector of values of arguments (NARGS of them).
+ FUNCTION is a value, the function to be called.
+ Returns a value representing what the function returned.
+ May fail to return, if a breakpoint or signal is hit
+ during the execution of the function. */
+
+value
+call_function (function, nargs, args)
+ value function;
+ int nargs;
+ value *args;
+{
+ register CORE_ADDR sp;
+ register int i;
+ CORE_ADDR start_sp;
+ static REGISTER_TYPE dummy[] = CALL_DUMMY;
+ REGISTER_TYPE dummy1[sizeof dummy / sizeof (REGISTER_TYPE)];
+ CORE_ADDR old_sp;
+ struct type *value_type;
+ unsigned char struct_return;
+ CORE_ADDR struct_addr;
+ struct inferior_status inf_status;
+ struct cleanup *old_chain;
+
+ if (!have_inferior_p ())
+ error ("Cannot invoke functions if the inferior is not running.");
+
+ save_inferior_status (&inf_status, 1);
+ old_chain = make_cleanup (restore_inferior_status, &inf_status);
+
+ /* PUSH_DUMMY_FRAME is responsible for saving the inferior registers
+ (and POP_FRAME for restoring them). (At least on most machines)
+ they are saved on the stack in the inferior. */
+ PUSH_DUMMY_FRAME;
+
+ old_sp = sp = read_register (SP_REGNUM);
+
+#if 1 INNER_THAN 2 /* Stack grows down */
+ sp -= sizeof dummy;
+ start_sp = sp;
+#else /* Stack grows up */
+ start_sp = sp;
+ sp += sizeof dummy;
+#endif
+
+ {
+ register CORE_ADDR funaddr;
+ register struct type *ftype = VALUE_TYPE (function);
+ register enum type_code code = TYPE_CODE (ftype);
+
+ /* If it's a member function, just look at the function
+ part of it. */
+
+ /* Determine address to call. */
+ if (code == TYPE_CODE_FUNC || code == TYPE_CODE_METHOD)
+ {
+ funaddr = VALUE_ADDRESS (function);
+ value_type = TYPE_TARGET_TYPE (ftype);
+ }
+ else if (code == TYPE_CODE_PTR)
+ {
+ funaddr = value_as_long (function);
+ if (TYPE_CODE (TYPE_TARGET_TYPE (ftype)) == TYPE_CODE_FUNC
+ || TYPE_CODE (TYPE_TARGET_TYPE (ftype)) == TYPE_CODE_METHOD)
+ value_type = TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (ftype));
+ else
+ value_type = builtin_type_int;
+ }
+ else if (code == TYPE_CODE_INT)
+ {
+ /* Handle the case of functions lacking debugging info.
+ Their values are characters since their addresses are char */
+ if (TYPE_LENGTH (ftype) == 1)
+ funaddr = value_as_long (value_addr (function));
+ else
+ /* Handle integer used as address of a function. */
+ funaddr = value_as_long (function);
+
+ value_type = builtin_type_int;
+ }
+ else
+ error ("Invalid data type for function to be called.");
+
+ /* Are we returning a value using a structure return or a normal
+ value return? */
+
+ struct_return = using_struct_return (function, funaddr, value_type);
+
+ /* Create a call sequence customized for this function
+ and the number of arguments for it. */
+ bcopy (dummy, dummy1, sizeof dummy);
+ FIX_CALL_DUMMY (dummy1, start_sp, funaddr, nargs, value_type);
+ }
+
+#ifndef CANNOT_EXECUTE_STACK
+ write_memory (start_sp, dummy1, sizeof dummy);
+
+#else
+ /* Convex Unix prohibits executing in the stack segment. */
+ /* Hope there is empty room at the top of the text segment. */
+ {
+ extern CORE_ADDR text_end;
+ static checked = 0;
+ if (!checked)
+ for (start_sp = text_end - sizeof dummy; start_sp < text_end; ++start_sp)
+ if (read_memory_integer (start_sp, 1) != 0)
+ error ("text segment full -- no place to put call");
+ checked = 1;
+ sp = old_sp;
+ start_sp = text_end - sizeof dummy;
+ write_memory (start_sp, dummy1, sizeof dummy);
+ }
+#endif /* CANNOT_EXECUTE_STACK */
+#ifdef STACK_ALIGN
+ /* If stack grows down, we must leave a hole at the top. */
+ {
+ int len = 0;
+
+ /* Reserve space for the return structure to be written on the
+ stack, if necessary */
+
+ if (struct_return)
+ len += TYPE_LENGTH (value_type);
+
+ for (i = nargs - 1; i >= 0; i--)
+ len += TYPE_LENGTH (VALUE_TYPE (value_arg_coerce (args[i])));
+#ifdef CALL_DUMMY_STACK_ADJUST
+ len += CALL_DUMMY_STACK_ADJUST;
+#endif
+#if 1 INNER_THAN 2
+ sp -= STACK_ALIGN (len) - len;
+#else
+ sp += STACK_ALIGN (len) - len;
+#endif
+ }
+#endif /* STACK_ALIGN */
+
+ /* Reserve space for the return structure to be written on the
+ stack, if necessary */
+
+ if (struct_return)
+ {
+#if 1 INNER_THAN 2
+ sp -= TYPE_LENGTH (value_type);
+ struct_addr = sp;
+#else
+ struct_addr = sp;
+ sp += TYPE_LENGTH (value_type);
+#endif
+ }
+
+ for (i = nargs - 1; i >= 0; i--)
+ sp = value_arg_push (sp, args[i]);
+
+#ifdef CALL_DUMMY_STACK_ADJUST
+#if 1 INNER_THAN 2
+ sp -= CALL_DUMMY_STACK_ADJUST;
+#else
+ sp += CALL_DUMMY_STACK_ADJUST;
+#endif
+#endif /* CALL_DUMMY_STACK_ADJUST */
+
+ /* Store the address at which the structure is supposed to be
+ written. Note that this (and the code which reserved the space
+ above) assumes that gcc was used to compile this function. Since
+ it doesn't cost us anything but space and if the function is pcc
+ it will ignore this value, we will make that assumption.
+
+ Also note that on some machines (like the sparc) pcc uses this
+ convention in a slightly twisted way also. */
+
+ if (struct_return)
+ STORE_STRUCT_RETURN (struct_addr, sp);
+
+ /* Write the stack pointer. This is here because the statement above
+ might fool with it */
+ write_register (SP_REGNUM, sp);
+
+ /* Figure out the value returned by the function. */
+ {
+ char retbuf[REGISTER_BYTES];
+
+ /* Execute the stack dummy routine, calling FUNCTION.
+ When it is done, discard the empty frame
+ after storing the contents of all regs into retbuf. */
+ run_stack_dummy (start_sp + CALL_DUMMY_START_OFFSET, retbuf);
+
+ do_cleanups (old_chain);
+
+ return value_being_returned (value_type, retbuf, struct_return);
+ }
+}
+#endif
+
+/* Create a value for a string constant:
+ Call the function malloc in the inferior to get space for it,
+ then copy the data into that space
+ and then return the address with type char *.
+ PTR points to the string constant data; LEN is number of characters. */
+
+value
+value_string (ptr, len)
+ char *ptr;
+ int len;
+{
+ register value val;
+ register struct symbol *sym;
+ value blocklen;
+ register char *copy = (char *) alloca (len + 1);
+ char *i = ptr;
+ register char *o = copy, *ibeg = ptr;
+ register int c;
+#ifdef KERNELDEBUG
+ extern int kernel_debugging;
+
+ if (kernel_debugging)
+ error("Can't stuff string constants into kernel (yet).");
+#endif
+
+ /* Copy the string into COPY, processing escapes.
+ We could not conveniently process them in expread
+ because the string there wants to be a substring of the input. */
+
+ while (i - ibeg < len)
+ {
+ c = *i++;
+ if (c == '\\')
+ {
+ c = parse_escape (&i);
+ if (c == -1)
+ continue;
+ }
+ *o++ = c;
+ }
+ *o = 0;
+
+ /* Get the length of the string after escapes are processed. */
+
+ len = o - copy;
+
+ /* Find the address of malloc in the inferior. */
+
+ sym = lookup_symbol ("malloc", 0, VAR_NAMESPACE, 0);
+ if (sym != 0)
+ {
+ if (SYMBOL_CLASS (sym) != LOC_BLOCK)
+ error ("\"malloc\" exists in this program but is not a function.");
+ val = value_of_variable (sym);
+ }
+ else
+ {
+ register int i;
+ for (i = 0; i < misc_function_count; i++)
+ if (!strcmp (misc_function_vector[i].name, "malloc"))
+ break;
+ if (i < misc_function_count)
+ val = value_from_long (builtin_type_long,
+ (LONGEST) misc_function_vector[i].address);
+ else
+ error ("String constants require the program to have a function \"malloc\".");
+ }
+
+ blocklen = value_from_long (builtin_type_int, (LONGEST) (len + 1));
+ val = call_function (val, 1, &blocklen);
+ if (value_zerop (val))
+ error ("No memory available for string constant.");
+ write_memory ((CORE_ADDR) value_as_long (val), copy, len + 1);
+ VALUE_TYPE (val) = lookup_pointer_type (builtin_type_char);
+ return val;
+}
+
+static int
+type_field_index(t, name)
+ register struct type *t;
+ register char *name;
+{
+ register int i;
+
+ for (i = TYPE_NFIELDS(t); --i >= 0;)
+ {
+ register char *t_field_name = TYPE_FIELD_NAME (t, i);
+
+ if (t_field_name && !strcmp (t_field_name, name))
+ break;
+ }
+ return (i);
+}
+
+/* Given ARG1, a value of type (pointer to a)* structure/union,
+ extract the component named NAME from the ultimate target structure/union
+ and return it as a value with its appropriate type.
+ ERR is used in the error message if ARG1's type is wrong.
+
+ C++: ARGS is a list of argument types to aid in the selection of
+ an appropriate method. Also, handle derived types.
+
+ STATIC_MEMFUNCP, if non-NULL, points to a caller-supplied location
+ where the truthvalue of whether the function that was resolved was
+ a static member function or not.
+
+ ERR is an error message to be printed in case the field is not found. */
+
+value
+value_struct_elt (arg1, args, name, static_memfuncp, err)
+ register value arg1, *args;
+ char *name;
+ int *static_memfuncp;
+ char *err;
+{
+ register struct type *t;
+ register int i;
+ int found = 0;
+
+ struct type *baseclass;
+
+ COERCE_ARRAY (arg1);
+
+ t = VALUE_TYPE (arg1);
+
+ /* Check for the usual case: we have pointer, target type is a struct
+ * and `name' is a legal field of the struct. In this case, we can
+ * just snarf the value of the field & not waste time while value_ind
+ * sucks over the entire struct. */
+ if (! args)
+ {
+ if (TYPE_CODE(t) == TYPE_CODE_PTR
+ && (TYPE_CODE((baseclass = TYPE_TARGET_TYPE(t))) == TYPE_CODE_STRUCT
+ || TYPE_CODE(baseclass) == TYPE_CODE_UNION)
+ && (i = type_field_index(baseclass, name)) >= 0)
+ {
+ register int offset;
+ register struct type *f = TYPE_FIELD_TYPE(baseclass, i);
+
+ offset = TYPE_FIELD_BITPOS(baseclass, i) >> 3;
+ if (TYPE_FIELD_BITSIZE(baseclass, i) == 0)
+ return value_at(f, (CORE_ADDR)(value_as_long(arg1) + offset));
+ }
+ }
+
+ /* Follow pointers until we get to a non-pointer. */
+
+ while (TYPE_CODE (t) == TYPE_CODE_PTR || TYPE_CODE (t) == TYPE_CODE_REF)
+ {
+ arg1 = value_ind (arg1);
+ COERCE_ARRAY (arg1);
+ t = VALUE_TYPE (arg1);
+ }
+
+ if (TYPE_CODE (t) == TYPE_CODE_MEMBER)
+ error ("not implemented: member type in value_struct_elt");
+
+ if (TYPE_CODE (t) != TYPE_CODE_STRUCT
+ && TYPE_CODE (t) != TYPE_CODE_UNION)
+ error ("Attempt to extract a component of a value that is not a %s.", err);
+
+ baseclass = t;
+
+ /* Assume it's not, unless we see that it is. */
+ if (static_memfuncp)
+ *static_memfuncp =0;
+
+ if (!args)
+ {
+ /* if there are no arguments ...do this... */
+
+ /* Try as a variable first, because if we succeed, there
+ is less work to be done. */
+ while (t)
+ {
+ i = type_field_index(t, name);
+ if (i >= 0)
+ return TYPE_FIELD_STATIC (t, i)
+ ? value_static_field (t, name, i) : value_field (arg1, i);
+
+ if (TYPE_N_BASECLASSES (t) == 0)
+ break;
+
+ t = TYPE_BASECLASS (t, 1);
+ VALUE_TYPE (arg1) = t; /* side effect! */
+ }
+
+ /* C++: If it was not found as a data field, then try to
+ return it as a pointer to a method. */
+ t = baseclass;
+ VALUE_TYPE (arg1) = t; /* side effect! */
+
+ if (destructor_name_p (name, t))
+ error ("use `info method' command to print out value of destructor");
+
+ while (t)
+ {
+ for (i = TYPE_NFN_FIELDS (t) - 1; i >= 0; --i)
+ {
+ if (! strcmp (TYPE_FN_FIELDLIST_NAME (t, i), name))
+ {
+ error ("use `info method' command to print value of method \"%s\"", name);
+ }
+ }
+
+ if (TYPE_N_BASECLASSES (t) == 0)
+ break;
+
+ t = TYPE_BASECLASS (t, 1);
+ }
+
+ error ("There is no field named %s.", name);
+ return 0;
+ }
+
+ if (destructor_name_p (name, t))
+ {
+ if (!args[1])
+ {
+ /* destructors are a special case. */
+ return (value)value_fn_field (arg1, 0,
+ TYPE_FN_FIELDLIST_LENGTH (t, 0));
+ }
+ else
+ {
+ error ("destructor should not have any argument");
+ }
+ }
+
+ /* This following loop is for methods with arguments. */
+ while (t)
+ {
+ /* Look up as method first, because that is where we
+ expect to find it first. */
+ for (i = TYPE_NFN_FIELDS (t) - 1; i >= 0; i--)
+ {
+ struct fn_field *f = TYPE_FN_FIELDLIST1 (t, i);
+
+ if (!strcmp (TYPE_FN_FIELDLIST_NAME (t, i), name))
+ {
+ int j;
+ struct fn_field *f = TYPE_FN_FIELDLIST1 (t, i);
+
+ found = 1;
+ for (j = TYPE_FN_FIELDLIST_LENGTH (t, i) - 1; j >= 0; --j)
+ if (!typecmp (TYPE_FN_FIELD_STATIC_P (f, j),
+ TYPE_FN_FIELD_ARGS (f, j), args))
+ {
+ if (TYPE_FN_FIELD_VIRTUAL_P (f, j))
+ return (value)value_virtual_fn_field (arg1, f, j, t);
+ if (TYPE_FN_FIELD_STATIC_P (f, j) && static_memfuncp)
+ *static_memfuncp = 1;
+ return (value)value_fn_field (arg1, i, j);
+ }
+ }
+ }
+
+ if (TYPE_N_BASECLASSES (t) == 0)
+ break;
+
+ t = TYPE_BASECLASS (t, 1);
+ VALUE_TYPE (arg1) = t; /* side effect! */
+ }
+
+ if (found)
+ {
+ error ("Structure method %s not defined for arglist.", name);
+ return 0;
+ }
+ else
+ {
+ /* See if user tried to invoke data as function */
+ t = baseclass;
+ while (t)
+ {
+ i = type_field_index(t, name);
+ if (i >= 0)
+ return TYPE_FIELD_STATIC (t, i)
+ ? value_static_field (t, name, i) : value_field (arg1, i);
+
+ if (TYPE_N_BASECLASSES (t) == 0)
+ break;
+
+ t = TYPE_BASECLASS (t, 1);
+ VALUE_TYPE (arg1) = t; /* side effect! */
+ }
+ error ("Structure has no component named %s.", name);
+ }
+}
+
+/* C++: return 1 is NAME is a legitimate name for the destructor
+ of type TYPE. If TYPE does not have a destructor, or
+ if NAME is inappropriate for TYPE, an error is signaled. */
+int
+destructor_name_p (name, type)
+ char *name;
+ struct type *type;
+{
+ /* destructors are a special case. */
+ char *dname = TYPE_NAME (type);
+
+ if (name[0] == '~')
+ {
+ if (! TYPE_HAS_DESTRUCTOR (type))
+ error ("type `%s' does not have destructor defined",
+ TYPE_NAME (type));
+ /* Skip past the "struct " at the front. */
+ while (*dname++ != ' ') ;
+ if (strcmp (dname, name+1))
+ error ("destructor specification error");
+ else
+ return 1;
+ }
+ return 0;
+}
+
+/* C++: Given ARG1, a value of type (pointer to a)* structure/union,
+ return 1 if the component named NAME from the ultimate
+ target structure/union is defined, otherwise, return 0. */
+
+int
+check_field (arg1, name)
+ register value arg1;
+ char *name;
+{
+ register struct type *t;
+ register int i;
+ int found = 0;
+
+ struct type *baseclass;
+
+ COERCE_ARRAY (arg1);
+
+ t = VALUE_TYPE (arg1);
+
+ /* Follow pointers until we get to a non-pointer. */
+
+ while (TYPE_CODE (t) == TYPE_CODE_PTR || TYPE_CODE (t) == TYPE_CODE_REF)
+ t = TYPE_TARGET_TYPE (t);
+
+ if (TYPE_CODE (t) == TYPE_CODE_MEMBER)
+ error ("not implemented: member type in check_field");
+
+ if (TYPE_CODE (t) != TYPE_CODE_STRUCT
+ && TYPE_CODE (t) != TYPE_CODE_UNION)
+ error ("Internal error: `this' is not an aggregate");
+
+ baseclass = t;
+
+ while (t)
+ {
+ for (i = TYPE_NFIELDS (t) - 1; i >= 0; i--)
+ {
+ char *t_field_name = TYPE_FIELD_NAME (t, i);
+ if (t_field_name && !strcmp (t_field_name, name))
+ goto success;
+ }
+ if (TYPE_N_BASECLASSES (t) == 0)
+ break;
+
+ t = TYPE_BASECLASS (t, 1);
+ VALUE_TYPE (arg1) = t; /* side effect! */
+ }
+
+ /* C++: If it was not found as a data field, then try to
+ return it as a pointer to a method. */
+ t = baseclass;
+
+ /* Destructors are a special case. */
+ if (destructor_name_p (name, t))
+ goto success;
+
+ while (t)
+ {
+ for (i = TYPE_NFN_FIELDS (t) - 1; i >= 0; --i)
+ {
+ if (!strcmp (TYPE_FN_FIELDLIST_NAME (t, i), name))
+ return 1;
+ }
+
+ if (TYPE_N_BASECLASSES (t) == 0)
+ break;
+
+ t = TYPE_BASECLASS (t, 1);
+ }
+ return 0;
+
+ success:
+ t = VALUE_TYPE (arg1);
+ while (TYPE_CODE (t) == TYPE_CODE_PTR || TYPE_CODE (t) == TYPE_CODE_REF)
+ {
+ arg1 = value_ind (arg1);
+ COERCE_ARRAY (arg1);
+ t = VALUE_TYPE (arg1);
+ }
+}
+
+/* C++: Given an aggregate type DOMAIN, and a member name NAME,
+ return the address of this member as a pointer to member
+ type. If INTYPE is non-null, then it will be the type
+ of the member we are looking for. This will help us resolve
+ pointers to member functions. */
+
+value
+value_struct_elt_for_address (domain, intype, name)
+ struct type *domain, *intype;
+ char *name;
+{
+ register struct type *t = domain;
+ register int i;
+ int found = 0;
+ value v;
+
+ struct type *baseclass;
+
+ if (TYPE_CODE (t) != TYPE_CODE_STRUCT
+ && TYPE_CODE (t) != TYPE_CODE_UNION)
+ error ("Internal error: non-aggregate type to value_struct_elt_for_address");
+
+ baseclass = t;
+
+ while (t)
+ {
+ for (i = TYPE_NFIELDS (t) - 1; i >= 0; i--)
+ {
+ char *t_field_name = TYPE_FIELD_NAME (t, i);
+ if (t_field_name && !strcmp (t_field_name, name))
+ {
+ if (TYPE_FIELD_PACKED (t, i))
+ error ("pointers to bitfield members not allowed");
+
+ v = value_from_long (builtin_type_int,
+ (LONGEST) (TYPE_FIELD_BITPOS (t, i) >> 3));
+ VALUE_TYPE (v) = lookup_pointer_type (
+ lookup_member_type (TYPE_FIELD_TYPE (t, i), baseclass));
+ return v;
+ }
+ }
+
+ if (TYPE_N_BASECLASSES (t) == 0)
+ break;
+
+ t = TYPE_BASECLASS (t, 1);
+ }
+
+ /* C++: If it was not found as a data field, then try to
+ return it as a pointer to a method. */
+ t = baseclass;
+
+ /* Destructors are a special case. */
+ if (destructor_name_p (name, t))
+ {
+ error ("pointers to destructors not implemented yet");
+ }
+
+ /* Perform all necessary dereferencing. */
+ while (intype && TYPE_CODE (intype) == TYPE_CODE_PTR)
+ intype = TYPE_TARGET_TYPE (intype);
+
+ while (t)
+ {
+ for (i = TYPE_NFN_FIELDS (t) - 1; i >= 0; --i)
+ {
+ if (!strcmp (TYPE_FN_FIELDLIST_NAME (t, i), name))
+ {
+ int j = TYPE_FN_FIELDLIST_LENGTH (t, i);
+ struct fn_field *f = TYPE_FN_FIELDLIST1 (t, i);
+
+ if (intype == 0 && j > 1)
+ error ("non-unique member `%s' requires type instantiation", name);
+ if (intype)
+ {
+ while (j--)
+ if (TYPE_FN_FIELD_TYPE (f, j) == intype)
+ break;
+ if (j < 0)
+ error ("no member function matches that type instantiation");
+ }
+ else
+ j = 0;
+
+ if (TYPE_FN_FIELD_VIRTUAL_P (f, j))
+ {
+ v = value_from_long (builtin_type_long,
+ (LONGEST) TYPE_FN_FIELD_VOFFSET (f, j));
+ }
+ else
+ {
+ struct symbol *s = lookup_symbol (TYPE_FN_FIELD_PHYSNAME (f, j),
+ 0, VAR_NAMESPACE, 0);
+ v = locate_var_value (s, 0);
+ }
+ VALUE_TYPE (v) = lookup_pointer_type (lookup_member_type (TYPE_FN_FIELD_TYPE (f, j), baseclass));
+ return v;
+ }
+ }
+
+ if (TYPE_N_BASECLASSES (t) == 0)
+ break;
+
+ t = TYPE_BASECLASS (t, 1);
+ }
+ return 0;
+}
+
+/* Compare two argument lists and return the position in which they differ,
+ or zero if equal.
+
+ STATICP is nonzero if the T1 argument list came from a
+ static member function.
+
+ For non-static member functions, we ignore the first argument,
+ which is the type of the instance variable. This is because we want
+ to handle calls with objects from derived classes. This is not
+ entirely correct: we should actually check to make sure that a
+ requested operation is type secure, shouldn't we? */
+
+int
+typecmp (staticp, t1, t2)
+ int staticp;
+ struct type *t1[];
+ value t2[];
+{
+ int i;
+
+ if (staticp && t1 == 0)
+ return t2[1] != 0;
+ if (t1 == 0)
+ return 1;
+ if (t1[0]->code == TYPE_CODE_VOID) return 0;
+ if (t1[!staticp] == 0) return 0;
+ for (i = !staticp; t1[i] && t1[i]->code != TYPE_CODE_VOID; i++)
+ {
+ if (! t2[i]
+ || t1[i]->code != t2[i]->type->code
+ || t1[i]->target_type != t2[i]->type->target_type)
+ return i+1;
+ }
+ if (!t1[i]) return 0;
+ return t2[i] ? i+1 : 0;
+}
+
+/* C++: return the value of the class instance variable, if one exists.
+ Flag COMPLAIN signals an error if the request is made in an
+ inappropriate context. */
+value
+value_of_this (complain)
+ int complain;
+{
+ extern FRAME selected_frame;
+ struct symbol *func, *sym;
+ char *funname = 0;
+ struct block *b;
+ int i;
+
+ if (selected_frame == 0)
+ if (complain)
+ error ("no frame selected");
+ else return 0;
+
+ func = get_frame_function (selected_frame);
+ if (func)
+ funname = SYMBOL_NAME (func);
+ else
+ if (complain)
+ error ("no `this' in nameless context");
+ else return 0;
+
+ b = SYMBOL_BLOCK_VALUE (func);
+ i = BLOCK_NSYMS (b);
+ if (i <= 0)
+ if (complain)
+ error ("no args, no `this'");
+ else return 0;
+
+ sym = BLOCK_SYM (b, 0);
+ if (strncmp ("$this", SYMBOL_NAME (sym), 5))
+ if (complain)
+ error ("current stack frame not in method");
+ else return 0;
+
+ return read_var_value (sym, selected_frame);
+}
diff --git a/gnu/usr.bin/gdb/valprint.c b/gnu/usr.bin/gdb/valprint.c
new file mode 100644
index 0000000..781eb29
--- /dev/null
+++ b/gnu/usr.bin/gdb/valprint.c
@@ -0,0 +1,1430 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)valprint.c 6.5 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Print values for GNU debugger gdb.
+ Copyright (C) 1986, 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "value.h"
+
+/* GNU software is only expected to run on systems with 32-bit integers. */
+#define UINT_MAX 0xffffffff
+
+/* Maximum number of chars to print for a string pointer value
+ or vector contents, or UINT_MAX for no limit. */
+
+static unsigned int print_max;
+
+static void type_print_varspec_suffix ();
+static void type_print_varspec_prefix ();
+static void type_print_base ();
+static void type_print_method_args ();
+
+
+char **unsigned_type_table;
+char **signed_type_table;
+char **float_type_table;
+
+
+/* Print repeat counts if there are more than this
+ many repetitions of an element in an array. */
+#define REPEAT_COUNT_THRESHOLD 10
+
+/* Print the character string STRING, printing at most LENGTH characters.
+ Printing stops early if the number hits print_max; repeat counts
+ are printed as appropriate. Print ellipses at the end if we
+ had to stop before printing LENGTH characters, or if FORCE_ELLIPSES. */
+
+void
+print_string (stream, string, length, force_ellipses)
+ FILE *stream;
+ char *string;
+ unsigned int length;
+ int force_ellipses;
+{
+ register unsigned int i;
+ unsigned int things_printed = 0;
+ int in_quotes = 0;
+ int need_comma = 0;
+
+ if (length == 0)
+ {
+ fputs_filtered ("\"\"", stdout);
+ return;
+ }
+
+ for (i = 0; i < length && things_printed < print_max; ++i)
+ {
+ /* Position of the character we are examining
+ to see whether it is repeated. */
+ unsigned int rep1;
+ /* Number of repititions we have detected so far. */
+ unsigned int reps;
+
+ QUIT;
+
+ if (need_comma)
+ {
+ fputs_filtered (", ", stream);
+ need_comma = 0;
+ }
+
+ rep1 = i + 1;
+ reps = 1;
+ while (rep1 < length && string[rep1] == string[i])
+ {
+ ++rep1;
+ ++reps;
+ }
+
+ if (reps > REPEAT_COUNT_THRESHOLD)
+ {
+ if (in_quotes)
+ {
+ fputs_filtered ("\", ", stream);
+ in_quotes = 0;
+ }
+ fputs_filtered ("'", stream);
+ printchar (string[i], stream, '\'');
+ fprintf_filtered (stream, "' <repeats %u times>", reps);
+ i = rep1 - 1;
+ things_printed += REPEAT_COUNT_THRESHOLD;
+ need_comma = 1;
+ }
+ else
+ {
+ if (!in_quotes)
+ {
+ fputs_filtered ("\"", stream);
+ in_quotes = 1;
+ }
+ printchar (string[i], stream, '"');
+ ++things_printed;
+ }
+ }
+
+ /* Terminate the quotes if necessary. */
+ if (in_quotes)
+ fputs_filtered ("\"", stream);
+
+ if (force_ellipses || i < length)
+ fputs_filtered ("...", stream);
+}
+
+/* Print the value VAL in C-ish syntax on stream STREAM.
+ FORMAT is a format-letter, or 0 for print in natural format of data type.
+ If the object printed is a string pointer, returns
+ the number of string bytes printed. */
+
+int
+value_print (val, stream, format, pretty)
+ value val;
+ FILE *stream;
+ char format;
+ enum val_prettyprint pretty;
+{
+ register unsigned int i, n, typelen;
+
+ /* A "repeated" value really contains several values in a row.
+ They are made by the @ operator.
+ Print such values as if they were arrays. */
+
+ if (VALUE_REPEATED (val))
+ {
+ n = VALUE_REPETITIONS (val);
+ typelen = TYPE_LENGTH (VALUE_TYPE (val));
+ fprintf_filtered (stream, "{");
+ /* Print arrays of characters using string syntax. */
+ if (typelen == 1 && TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_INT
+ && format == 0)
+ print_string (stream, VALUE_CONTENTS (val), n, 0);
+ else
+ {
+ unsigned int things_printed = 0;
+
+ for (i = 0; i < n && things_printed < print_max; i++)
+ {
+ /* Position of the array element we are examining to see
+ whether it is repeated. */
+ unsigned int rep1;
+ /* Number of repititions we have detected so far. */
+ unsigned int reps;
+
+ if (i != 0)
+ fprintf_filtered (stream, ", ");
+
+ rep1 = i + 1;
+ reps = 1;
+ while (rep1 < n
+ && !bcmp (VALUE_CONTENTS (val) + typelen * i,
+ VALUE_CONTENTS (val) + typelen * rep1, typelen))
+ {
+ ++reps;
+ ++rep1;
+ }
+
+ if (reps > REPEAT_COUNT_THRESHOLD)
+ {
+ val_print (VALUE_TYPE (val),
+ VALUE_CONTENTS (val) + typelen * i,
+ VALUE_ADDRESS (val) + typelen * i,
+ stream, format, 1, 0, pretty);
+ fprintf (stream, " <repeats %u times>", reps);
+ i = rep1 - 1;
+ things_printed += REPEAT_COUNT_THRESHOLD;
+ }
+ else
+ {
+ val_print (VALUE_TYPE (val),
+ VALUE_CONTENTS (val) + typelen * i,
+ VALUE_ADDRESS (val) + typelen * i,
+ stream, format, 1, 0, pretty);
+ things_printed++;
+ }
+ }
+ if (i < n)
+ fprintf_filtered (stream, "...");
+ }
+ fprintf_filtered (stream, "}");
+ return n * typelen;
+ }
+ else
+ {
+ /* If it is a pointer, indicate what it points to.
+
+ Print type also if it is a reference.
+
+ C++: if it is a member pointer, we will take care
+ of that when we print it. */
+ if (TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_PTR
+ || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_REF)
+ {
+ fprintf_filtered (stream, "(");
+ type_print (VALUE_TYPE (val), "", stream, -1);
+ fprintf_filtered (stream, ") ");
+
+ /* If this is a function pointer, try to print what
+ function it is pointing to by name. */
+ if (TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (val)))
+ == TYPE_CODE_FUNC)
+ {
+ print_address (((int *) VALUE_CONTENTS (val))[0], stream);
+ /* Return value is irrelevant except for string pointers. */
+ return 0;
+ }
+ }
+ return val_print (VALUE_TYPE (val), VALUE_CONTENTS (val),
+ VALUE_ADDRESS (val), stream, format, 1, 0, pretty);
+ }
+}
+
+static int prettyprint; /* Controls prettyprinting of structures. */
+int unionprint; /* Controls printing of nested unions. */
+static void scalar_print_hack();
+void (*default_scalar_print)() = scalar_print_hack;
+
+/* Print data of type TYPE located at VALADDR (within GDB),
+ which came from the inferior at address ADDRESS,
+ onto stdio stream STREAM according to FORMAT
+ (a letter or 0 for natural format).
+
+ If the data are a string pointer, returns the number of
+ sting characters printed.
+
+ if DEREF_REF is nonzero, then dereference references,
+ otherwise just print them like pointers.
+
+ The PRETTY parameter controls prettyprinting. */
+
+int
+val_print (type, valaddr, address, stream, format,
+ deref_ref, recurse, pretty)
+ struct type *type;
+ char *valaddr;
+ CORE_ADDR address;
+ FILE *stream;
+ char format;
+ int deref_ref;
+ int recurse;
+ enum val_prettyprint pretty;
+{
+ register unsigned int i;
+ int len, n_baseclasses;
+ struct type *elttype;
+ int eltlen;
+ LONGEST val;
+ unsigned char c;
+
+ if (pretty == Val_pretty_default)
+ {
+ pretty = prettyprint ? Val_prettyprint : Val_no_prettyprint;
+ }
+
+ QUIT;
+
+ if (TYPE_FLAGS (type) & TYPE_FLAG_STUB)
+ {
+ fprintf_filtered (stream, "<Type not defined in this context>");
+ fflush (stream);
+ return 0;
+ }
+
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_ARRAY:
+ if (TYPE_LENGTH (type) >= 0
+ && TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > 0)
+ {
+ elttype = TYPE_TARGET_TYPE (type);
+ eltlen = TYPE_LENGTH (elttype);
+ len = TYPE_LENGTH (type) / eltlen;
+ fprintf_filtered (stream, "{");
+ /* For an array of chars, print with string syntax. */
+ if (eltlen == 1 && TYPE_CODE (elttype) == TYPE_CODE_INT
+ && format == 0)
+ print_string (stream, valaddr, len, 0);
+ else
+ {
+ unsigned int things_printed = 0;
+
+ for (i = 0; i < len && things_printed < print_max; i++)
+ {
+ /* Position of the array element we are examining to see
+ whether it is repeated. */
+ unsigned int rep1;
+ /* Number of repititions we have detected so far. */
+ unsigned int reps;
+
+ if (i > 0)
+ fprintf_filtered (stream, ", ");
+
+ rep1 = i + 1;
+ reps = 1;
+ while (rep1 < len
+ && !bcmp (valaddr + i * eltlen,
+ valaddr + rep1 * eltlen, eltlen))
+ {
+ ++reps;
+ ++rep1;
+ }
+
+ if (reps > REPEAT_COUNT_THRESHOLD)
+ {
+ val_print (elttype, valaddr + i * eltlen,
+ 0, stream, format, deref_ref,
+ recurse + 1, pretty);
+ fprintf_filtered (stream, " <repeats %u times>", reps);
+ i = rep1 - 1;
+ things_printed += REPEAT_COUNT_THRESHOLD;
+ }
+ else
+ {
+ val_print (elttype, valaddr + i * eltlen,
+ 0, stream, format, deref_ref,
+ recurse + 1, pretty);
+ things_printed++;
+ }
+ }
+ if (i < len)
+ fprintf_filtered (stream, "...");
+ }
+ fprintf_filtered (stream, "}");
+ break;
+ }
+ /* Array of unspecified length: treat like pointer to first elt. */
+ valaddr = (char *) &address;
+
+ case TYPE_CODE_PTR:
+ if (format)
+ {
+ print_scalar_formatted (valaddr, type, format, 0, stream);
+ break;
+ }
+ if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_METHOD)
+ {
+ struct type *domain = TYPE_DOMAIN_TYPE (TYPE_TARGET_TYPE (type));
+ struct type *target = TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (type));
+ struct fn_field *f;
+ int j, len2;
+ char *kind = "";
+
+ val = unpack_long (builtin_type_int, valaddr);
+ if (val < 128)
+ {
+ len = TYPE_NFN_FIELDS (domain);
+ for (i = 0; i < len; i++)
+ {
+ f = TYPE_FN_FIELDLIST1 (domain, i);
+ len2 = TYPE_FN_FIELDLIST_LENGTH (domain, i);
+
+ for (j = 0; j < len2; j++)
+ {
+ QUIT;
+ if (TYPE_FN_FIELD_VOFFSET (f, j) == val)
+ {
+ kind = "virtual";
+ goto common;
+ }
+ }
+ }
+ }
+ else
+ {
+ struct symbol *sym = find_pc_function ((CORE_ADDR) val);
+ if (sym == 0)
+ error ("invalid pointer to member function");
+ len = TYPE_NFN_FIELDS (domain);
+ for (i = 0; i < len; i++)
+ {
+ f = TYPE_FN_FIELDLIST1 (domain, i);
+ len2 = TYPE_FN_FIELDLIST_LENGTH (domain, i);
+
+ for (j = 0; j < len2; j++)
+ {
+ QUIT;
+ if (!strcmp (SYMBOL_NAME (sym), TYPE_FN_FIELD_PHYSNAME (f, j)))
+ goto common;
+ }
+ }
+ }
+ common:
+ if (i < len)
+ {
+ fprintf_filtered (stream, "&");
+ type_print_varspec_prefix (TYPE_FN_FIELD_TYPE (f, j), stream, 0, 0);
+ fprintf (stream, kind);
+ if (TYPE_FN_FIELD_PHYSNAME (f, j)[0] == '_'
+ && TYPE_FN_FIELD_PHYSNAME (f, j)[1] == '$')
+ type_print_method_args
+ (TYPE_FN_FIELD_ARGS (f, j) + 1, "~",
+ TYPE_FN_FIELDLIST_NAME (domain, i), 0, stream);
+ else
+ type_print_method_args
+ (TYPE_FN_FIELD_ARGS (f, j), "",
+ TYPE_FN_FIELDLIST_NAME (domain, i), 0, stream);
+ break;
+ }
+ fprintf_filtered (stream, "(");
+ type_print (type, "", stream, -1);
+ fprintf_filtered (stream, ") %d", (int) val >> 3);
+ }
+ else if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_MEMBER)
+ {
+ struct type *domain = TYPE_DOMAIN_TYPE (TYPE_TARGET_TYPE (type));
+ struct type *target = TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (type));
+ char *kind = "";
+
+ /* VAL is a byte offset into the structure type DOMAIN.
+ Find the name of the field for that offset and
+ print it. */
+ int extra = 0;
+ int bits = 0;
+ len = TYPE_NFIELDS (domain);
+ /* @@ Make VAL into bit offset */
+ val = unpack_long (builtin_type_int, valaddr) << 3;
+ for (i = 0; i < len; i++)
+ {
+ int bitpos = TYPE_FIELD_BITPOS (domain, i);
+ QUIT;
+ if (val == bitpos)
+ break;
+ if (val < bitpos && i > 0)
+ {
+ int ptrsize = (TYPE_LENGTH (builtin_type_char) * TYPE_LENGTH (target));
+ /* Somehow pointing into a field. */
+ i -= 1;
+ extra = (val - TYPE_FIELD_BITPOS (domain, i));
+ if (extra & 0x3)
+ bits = 1;
+ else
+ extra >>= 3;
+ break;
+ }
+ }
+ if (i < len)
+ {
+ fprintf_filtered (stream, "&");
+ type_print_base (domain, stream, 0, 0);
+ fprintf_filtered (stream, "::");
+ fputs_filtered (TYPE_FIELD_NAME (domain, i), stream);
+ if (extra)
+ fprintf_filtered (stream, " + %d bytes", extra);
+ if (bits)
+ fprintf_filtered (stream, " (offset in bits)");
+ break;
+ }
+ fprintf_filtered (stream, "%d", val >> 3);
+ }
+ else
+ {
+ fprintf_filtered (stream, "0x%x", * (int *) valaddr);
+ /* For a pointer to char or unsigned char,
+ also print the string pointed to, unless pointer is null. */
+
+ /* For an array of chars, print with string syntax. */
+ elttype = TYPE_TARGET_TYPE (type);
+ i = 0; /* Number of characters printed. */
+ if (TYPE_LENGTH (elttype) == 1
+ && TYPE_CODE (elttype) == TYPE_CODE_INT
+ && format == 0
+ && unpack_long (type, valaddr) != 0
+ /* If print_max is UINT_MAX, the alloca below will fail.
+ In that case don't try to print the string. */
+ && print_max < UINT_MAX)
+ {
+ fprintf_filtered (stream, " ");
+
+ /* Get first character. */
+ if (read_memory ( (CORE_ADDR) unpack_long (type, valaddr),
+ &c, 1))
+ {
+ /* First address out of bounds. */
+ fprintf_filtered (stream, "<Address 0x%x out of bounds>",
+ (* (int *) valaddr));
+ break;
+ }
+ else
+ {
+ /* A real string. */
+ int out_of_bounds = 0;
+ char *string = (char *) alloca (print_max);
+
+ /* If the loop ends by us hitting print_max characters,
+ we need to have elipses at the end. */
+ int force_ellipses = 1;
+
+ /* This loop only fetches print_max characters, even
+ though print_string might want to print more
+ (with repeated characters). This is so that
+ we don't spend forever fetching if we print
+ a long string consisting of the same character
+ repeated. */
+ while (i < print_max)
+ {
+ QUIT;
+ if (read_memory ((CORE_ADDR) unpack_long (type, valaddr)
+ + i, &c, 1))
+ {
+ out_of_bounds = 1;
+ force_ellipses = 0;
+ break;
+ }
+ else if (c == '\0')
+ {
+ force_ellipses = 0;
+ break;
+ }
+ else
+ string[i++] = c;
+ }
+
+ if (i != 0)
+ print_string (stream, string, i, force_ellipses);
+ if (out_of_bounds)
+ fprintf_filtered (stream,
+ " <Address 0x%x out of bounds>",
+ (*(int *) valaddr) + i);
+ }
+
+ fflush (stream);
+ }
+ /* Return number of characters printed, plus one for the
+ terminating null if we have "reached the end". */
+ return i + (print_max && i != print_max);
+ }
+ break;
+
+ case TYPE_CODE_MEMBER:
+ error ("not implemented: member type in val_print");
+ break;
+
+ case TYPE_CODE_REF:
+ fprintf_filtered (stream, "(0x%x &) = ", * (int *) valaddr);
+ /* De-reference the reference. */
+ if (deref_ref)
+ {
+ if (TYPE_CODE (TYPE_TARGET_TYPE (type)) != TYPE_CODE_UNDEF)
+ {
+ value val = value_at (TYPE_TARGET_TYPE (type), * (int *) valaddr);
+ val_print (VALUE_TYPE (val), VALUE_CONTENTS (val),
+ VALUE_ADDRESS (val), stream, format,
+ deref_ref, recurse + 1, pretty);
+ }
+ else
+ fprintf_filtered (stream, "???");
+ }
+ break;
+
+ case TYPE_CODE_UNION:
+ if (recurse && !unionprint)
+ {
+ fprintf_filtered (stream, "{...}");
+ break;
+ }
+ /* Fall through. */
+ case TYPE_CODE_STRUCT:
+ fprintf_filtered (stream, "{");
+ len = TYPE_NFIELDS (type);
+ n_baseclasses = TYPE_N_BASECLASSES (type);
+ for (i = 1; i <= n_baseclasses; i++)
+ {
+ fprintf_filtered (stream, "\n");
+ if (pretty)
+ print_spaces_filtered (2 + 2 * recurse, stream);
+ fputs_filtered ("<", stream);
+ fputs_filtered (TYPE_NAME (TYPE_BASECLASS (type, i)), stream);
+ fputs_filtered ("> = ", stream);
+ val_print (TYPE_FIELD_TYPE (type, 0),
+ valaddr + TYPE_FIELD_BITPOS (type, i-1) / 8,
+ 0, stream, 0, 0, recurse + 1, pretty);
+ }
+ if (i > 1) {
+ fprintf_filtered (stream, "\n");
+ print_spaces_filtered (2 + 2 * recurse, stream);
+ fputs_filtered ("members of ", stream);
+ fputs_filtered (TYPE_NAME (type), stream);
+ fputs_filtered (": ", stream);
+ }
+ if (!len && i == 1)
+ fprintf_filtered (stream, "<No data fields>");
+ else
+ {
+ for (i -= 1; i < len; i++)
+ {
+ if (i > n_baseclasses) fprintf_filtered (stream, ", ");
+ if (pretty)
+ {
+ fprintf_filtered (stream, "\n");
+ print_spaces_filtered (2 + 2 * recurse, stream);
+ }
+ fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
+ fputs_filtered (" = ", stream);
+ /* check if static field */
+ if (TYPE_FIELD_STATIC (type, i))
+ {
+ value v;
+
+ v = value_static_field (type, TYPE_FIELD_NAME (type, i), i);
+ val_print (TYPE_FIELD_TYPE (type, i),
+ VALUE_CONTENTS (v), 0, stream, format,
+ deref_ref, recurse + 1, pretty);
+ }
+ else if (TYPE_FIELD_PACKED (type, i))
+ {
+ char *valp = (char *) & val;
+ union {int i; char c;} test;
+ test.i = 1;
+ if (test.c != 1)
+ valp += sizeof val - TYPE_LENGTH (TYPE_FIELD_TYPE (type, i));
+ val = unpack_field_as_long (type, valaddr, i);
+ val_print (TYPE_FIELD_TYPE (type, i), valp, 0,
+ stream, format, deref_ref, recurse + 1, pretty);
+ }
+ else
+ {
+ val_print (TYPE_FIELD_TYPE (type, i),
+ valaddr + TYPE_FIELD_BITPOS (type, i) / 8,
+ 0, stream, format, deref_ref,
+ recurse + 1, pretty);
+ }
+ }
+ if (pretty)
+ {
+ fprintf_filtered (stream, "\n");
+ print_spaces_filtered (2 * recurse, stream);
+ }
+ }
+ fprintf_filtered (stream, "}");
+ break;
+
+ case TYPE_CODE_ENUM:
+ if (format)
+ {
+ print_scalar_formatted (valaddr, type, format, 0, stream);
+ break;
+ }
+ len = TYPE_NFIELDS (type);
+ val = unpack_long (builtin_type_int, valaddr);
+ for (i = 0; i < len; i++)
+ {
+ QUIT;
+ if (val == TYPE_FIELD_BITPOS (type, i))
+ break;
+ }
+ if (i < len)
+ fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
+ else
+ fprintf_filtered (stream, "%d", (int) val);
+ break;
+
+ case TYPE_CODE_FUNC:
+ if (format)
+ {
+ print_scalar_formatted (valaddr, type, format, 0, stream);
+ break;
+ }
+ fprintf_filtered (stream, "{");
+ type_print (type, "", stream, -1);
+ fprintf_filtered (stream, "} ");
+ fprintf_filtered (stream, "0x%x", address);
+ break;
+
+ case TYPE_CODE_INT:
+ if (format)
+ print_scalar_formatted (valaddr, type, format, 0, stream);
+ else
+ {
+ (*default_scalar_print)(stream, type, unpack_long(type, valaddr));
+#ifdef notdef
+ if (TYPE_LENGTH (type) == 1)
+ {
+ fprintf_filtered (stream, " '");
+ printchar ((unsigned char) unpack_long (type, valaddr),
+ stream, '\'');
+ fprintf_filtered (stream, "'");
+ }
+#endif
+ }
+ break;
+
+ case TYPE_CODE_FLT:
+ if (format)
+ print_scalar_formatted (valaddr, type, format, 0, stream);
+ else
+ print_floating (valaddr, type, stream);
+ break;
+
+ case TYPE_CODE_VOID:
+ fprintf_filtered (stream, "void");
+ break;
+
+ default:
+ error ("Invalid type code in symbol table.");
+ }
+ fflush (stream);
+ return 0;
+}
+
+/* Print a description of a type TYPE
+ in the form of a declaration of a variable named VARSTRING.
+ Output goes to STREAM (via stdio).
+ If SHOW is positive, we show the contents of the outermost level
+ of structure even if there is a type name that could be used instead.
+ If SHOW is negative, we never show the details of elements' types. */
+
+void
+type_print (type, varstring, stream, show)
+ struct type *type;
+ char *varstring;
+ FILE *stream;
+ int show;
+{
+ type_print_1 (type, varstring, stream, show, 0);
+}
+
+/* LEVEL is the depth to indent lines by. */
+
+void
+type_print_1 (type, varstring, stream, show, level)
+ struct type *type;
+ char *varstring;
+ FILE *stream;
+ int show;
+ int level;
+{
+ register enum type_code code;
+ type_print_base (type, stream, show, level);
+ code = TYPE_CODE (type);
+ if ((varstring && *varstring)
+ ||
+ /* Need a space if going to print stars or brackets;
+ but not if we will print just a type name. */
+ ((show > 0 || TYPE_NAME (type) == 0)
+ &&
+ (code == TYPE_CODE_PTR || code == TYPE_CODE_FUNC
+ || code == TYPE_CODE_METHOD
+ || code == TYPE_CODE_ARRAY
+ || code == TYPE_CODE_MEMBER
+ || code == TYPE_CODE_REF)))
+ fprintf_filtered (stream, " ");
+ type_print_varspec_prefix (type, stream, show, 0);
+ fputs_filtered (varstring, stream);
+ type_print_varspec_suffix (type, stream, show, 0);
+}
+
+/* Print the method arguments ARGS to the file STREAM. */
+static void
+type_print_method_args (args, prefix, varstring, staticp, stream)
+ struct type **args;
+ char *prefix, *varstring;
+ int staticp;
+ FILE *stream;
+{
+ int i;
+
+ fputs_filtered (" ", stream);
+ fputs_filtered (prefix, stream);
+ fputs_filtered (varstring, stream);
+ fputs_filtered (" (", stream);
+ if (args && args[!staticp] && args[!staticp]->code != TYPE_CODE_VOID)
+ {
+ i = !staticp; /* skip the class variable */
+ while (1)
+ {
+ type_print (args[i++], "", stream, 0);
+ if (!args[i])
+ {
+ fprintf_filtered (stream, " ...");
+ break;
+ }
+ else if (args[i]->code != TYPE_CODE_VOID)
+ {
+ fprintf_filtered (stream, ", ");
+ }
+ else break;
+ }
+ }
+ fprintf_filtered (stream, ")");
+}
+
+/* If TYPE is a derived type, then print out derivation
+ information. Print out all layers of the type heirarchy
+ until we encounter one with multiple inheritance.
+ At that point, print out that ply, and return. */
+static void
+type_print_derivation_info (stream, type)
+ FILE *stream;
+ struct type *type;
+{
+ char *name;
+ int i, n_baseclasses = TYPE_N_BASECLASSES (type);
+ struct type *basetype = 0;
+
+ while (type && n_baseclasses == 1)
+ {
+ basetype = TYPE_BASECLASS (type, 1);
+ if (TYPE_NAME (basetype) && (name = TYPE_NAME (basetype)))
+ {
+ while (*name != ' ') name++;
+ fprintf_filtered (stream, ": %s%s ",
+ TYPE_VIA_PUBLIC (basetype) ? "public" : "private",
+ TYPE_VIA_VIRTUAL (basetype) ? " virtual" : "");
+ fputs_filtered (name + 1, stream);
+ fputs_filtered (" ", stream);
+ }
+ n_baseclasses = TYPE_N_BASECLASSES (basetype);
+ type = basetype;
+ }
+
+ if (type)
+ {
+ if (n_baseclasses != 0)
+ fprintf_filtered (stream, ": ");
+ for (i = 1; i <= n_baseclasses; i++)
+ {
+ basetype = TYPE_BASECLASS (type, i);
+ if (TYPE_NAME (basetype) && (name = TYPE_NAME (basetype)))
+ {
+ while (*name != ' ') name++;
+ fprintf_filtered (stream, "%s%s ",
+ TYPE_VIA_PUBLIC (basetype) ? "public" : "private",
+ TYPE_VIA_VIRTUAL (basetype) ? " virtual" : "");
+ fputs_filtered (name + 1, stream);
+ }
+ if (i < n_baseclasses)
+ fprintf_filtered (stream, ", ");
+ }
+ fprintf_filtered (stream, " ");
+ }
+}
+
+/* Print any asterisks or open-parentheses needed before the
+ variable name (to describe its type).
+
+ On outermost call, pass 0 for PASSED_A_PTR.
+ On outermost call, SHOW > 0 means should ignore
+ any typename for TYPE and show its details.
+ SHOW is always zero on recursive calls. */
+
+static void
+type_print_varspec_prefix (type, stream, show, passed_a_ptr)
+ struct type *type;
+ FILE *stream;
+ int show;
+ int passed_a_ptr;
+{
+ if (type == 0)
+ return;
+
+ if (TYPE_NAME (type) && show <= 0)
+ return;
+
+ QUIT;
+
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_PTR:
+ type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, 1);
+ fprintf_filtered (stream, "*");
+ break;
+
+ case TYPE_CODE_MEMBER:
+ if (passed_a_ptr)
+ fprintf_filtered (stream, "(");
+ type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0,
+ 0);
+ fprintf_filtered (stream, " ");
+ type_print_base (TYPE_DOMAIN_TYPE (type), stream, 0,
+ passed_a_ptr);
+ fprintf_filtered (stream, "::");
+ break;
+
+ case TYPE_CODE_METHOD:
+ if (passed_a_ptr)
+ fprintf (stream, "(");
+ type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0,
+ 0);
+ fprintf_filtered (stream, " ");
+ type_print_base (TYPE_DOMAIN_TYPE (type), stream, 0,
+ passed_a_ptr);
+ fprintf_filtered (stream, "::");
+ break;
+
+ case TYPE_CODE_REF:
+ type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, 1);
+ fprintf_filtered (stream, "&");
+ break;
+
+ case TYPE_CODE_FUNC:
+ type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0,
+ 0);
+ if (passed_a_ptr)
+ fprintf_filtered (stream, "(");
+ break;
+
+ case TYPE_CODE_ARRAY:
+ type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0,
+ 0);
+ if (passed_a_ptr)
+ fprintf_filtered (stream, "(");
+ }
+}
+
+/* Print any array sizes, function arguments or close parentheses
+ needed after the variable name (to describe its type).
+ Args work like type_print_varspec_prefix. */
+
+static void
+type_print_varspec_suffix (type, stream, show, passed_a_ptr)
+ struct type *type;
+ FILE *stream;
+ int show;
+ int passed_a_ptr;
+{
+ if (type == 0)
+ return;
+
+ if (TYPE_NAME (type) && show <= 0)
+ return;
+
+ QUIT;
+
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_ARRAY:
+ if (passed_a_ptr)
+ fprintf_filtered (stream, ")");
+
+ fprintf_filtered (stream, "[");
+ if (TYPE_LENGTH (type) >= 0
+ && TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > 0)
+ fprintf_filtered (stream, "%d",
+ (TYPE_LENGTH (type)
+ / TYPE_LENGTH (TYPE_TARGET_TYPE (type))));
+ fprintf_filtered (stream, "]");
+
+ type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0,
+ 0);
+ break;
+
+ case TYPE_CODE_MEMBER:
+ if (passed_a_ptr)
+ fprintf_filtered (stream, ")");
+ type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, 0);
+ break;
+
+ case TYPE_CODE_METHOD:
+ if (passed_a_ptr)
+ fprintf_filtered (stream, ")");
+ type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, 0);
+ if (passed_a_ptr)
+ {
+ int i;
+ struct type **args = TYPE_ARG_TYPES (type);
+
+ fprintf_filtered (stream, "(");
+ if (args[1] == 0)
+ fprintf_filtered (stream, "...");
+ else for (i = 1; args[i] != 0 && args[i]->code != TYPE_CODE_VOID; i++)
+ {
+ type_print_1 (args[i], "", stream, -1, 0);
+ if (args[i+1] == 0)
+ fprintf_filtered (stream, "...");
+ else if (args[i+1]->code != TYPE_CODE_VOID)
+ fprintf_filtered (stream, ",");
+ }
+ fprintf_filtered (stream, ")");
+ }
+ break;
+
+ case TYPE_CODE_PTR:
+ case TYPE_CODE_REF:
+ type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, 1);
+ break;
+
+ case TYPE_CODE_FUNC:
+ type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0,
+ passed_a_ptr);
+ if (passed_a_ptr)
+ fprintf_filtered (stream, ")");
+ fprintf_filtered (stream, "()");
+ break;
+ }
+}
+
+/* Print the name of the type (or the ultimate pointer target,
+ function value or array element), or the description of a
+ structure or union.
+
+ SHOW nonzero means don't print this type as just its name;
+ show its real definition even if it has a name.
+ SHOW zero means print just typename or struct tag if there is one
+ SHOW negative means abbreviate structure elements.
+ SHOW is decremented for printing of structure elements.
+
+ LEVEL is the depth to indent by.
+ We increase it for some recursive calls. */
+
+static void
+type_print_base (type, stream, show, level)
+ struct type *type;
+ FILE *stream;
+ int show;
+ int level;
+{
+ char *name;
+ register int i;
+ register int len;
+ register int lastval;
+
+ QUIT;
+
+ if (type == 0)
+ {
+ fprintf_filtered (stream, "type unknown");
+ return;
+ }
+
+ if (TYPE_NAME (type) && show <= 0)
+ {
+ fputs_filtered (TYPE_NAME (type), stream);
+ return;
+ }
+
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_ARRAY:
+ case TYPE_CODE_PTR:
+ case TYPE_CODE_MEMBER:
+ case TYPE_CODE_REF:
+ case TYPE_CODE_FUNC:
+ case TYPE_CODE_METHOD:
+ type_print_base (TYPE_TARGET_TYPE (type), stream, show, level);
+ break;
+
+ case TYPE_CODE_STRUCT:
+ fprintf_filtered (stream, "struct ");
+ goto struct_union;
+
+ case TYPE_CODE_UNION:
+ fprintf_filtered (stream, "union ");
+ struct_union:
+ if (TYPE_NAME (type) && (name = TYPE_NAME (type)))
+ {
+ while (*name != ' ') name++;
+ fputs_filtered (name + 1, stream);
+ fputs_filtered (" ", stream);
+ }
+ if (show < 0)
+ fprintf_filtered (stream, "{...}");
+ else
+ {
+ int i;
+
+ type_print_derivation_info (stream, type);
+
+ fprintf_filtered (stream, "{");
+ len = TYPE_NFIELDS (type);
+ if (len)
+ fprintf_filtered (stream, "\n");
+ else
+ {
+ if (TYPE_FLAGS (type) & TYPE_FLAG_STUB)
+ fprintf_filtered (stream, "<incomplete type>\n");
+ else
+ fprintf_filtered (stream, "<no data fields>\n");
+ }
+
+ /* If there is a base class for this type,
+ do not print the field that it occupies. */
+ for (i = TYPE_N_BASECLASSES (type); i < len; i++)
+ {
+ QUIT;
+ /* Don't print out virtual function table. */
+ if (! strncmp (TYPE_FIELD_NAME (type, i),
+ "_vptr$", 6))
+ continue;
+
+ print_spaces_filtered (level + 4, stream);
+ if (TYPE_FIELD_STATIC (type, i))
+ {
+ fprintf_filtered (stream, "static ");
+ }
+ type_print_1 (TYPE_FIELD_TYPE (type, i),
+ TYPE_FIELD_NAME (type, i),
+ stream, show - 1, level + 4);
+ if (!TYPE_FIELD_STATIC (type, i)
+ && TYPE_FIELD_PACKED (type, i))
+ {
+ /* It is a bitfield. This code does not attempt
+ to look at the bitpos and reconstruct filler,
+ unnamed fields. This would lead to misleading
+ results if the compiler does not put out fields
+ for such things (I don't know what it does). */
+ fprintf_filtered (stream, " : %d",
+ TYPE_FIELD_BITSIZE (type, i));
+ }
+ fprintf_filtered (stream, ";\n");
+ }
+
+ /* C++: print out the methods */
+ len = TYPE_NFN_FIELDS (type);
+ if (len) fprintf_filtered (stream, "\n");
+ for (i = 0; i < len; i++)
+ {
+ struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i);
+ int j, len2 = TYPE_FN_FIELDLIST_LENGTH (type, i);
+
+ for (j = 0; j < len2; j++)
+ {
+ QUIT;
+ print_spaces_filtered (level + 4, stream);
+ if (TYPE_FN_FIELD_VIRTUAL_P (f, j))
+ fprintf_filtered (stream, "virtual ");
+ else if (TYPE_FN_FIELD_STATIC_P (f, j))
+ fprintf_filtered (stream, "static ");
+ type_print (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)), "", stream, 0);
+ if (TYPE_FN_FIELD_PHYSNAME (f, j)[0] == '_'
+ && TYPE_FN_FIELD_PHYSNAME (f, j)[1] == '$')
+ type_print_method_args
+ (TYPE_FN_FIELD_ARGS (f, j) + 1, "~",
+ TYPE_FN_FIELDLIST_NAME (type, i), 0, stream);
+ else
+ type_print_method_args
+ (TYPE_FN_FIELD_ARGS (f, j), "",
+ TYPE_FN_FIELDLIST_NAME (type, i),
+ TYPE_FN_FIELD_STATIC_P (f, j), stream);
+
+ fprintf_filtered (stream, ";\n");
+ }
+ if (len2) fprintf_filtered (stream, "\n");
+ }
+
+ print_spaces_filtered (level, stream);
+ fprintf_filtered (stream, "}");
+ }
+ break;
+
+ case TYPE_CODE_ENUM:
+ fprintf_filtered (stream, "enum ");
+ if (TYPE_NAME (type))
+ {
+ name = TYPE_NAME (type);
+ while (*name != ' ') name++;
+ fputs_filtered (name + 1, stream);
+ fputs_filtered (" ", stream);
+ }
+ if (show < 0)
+ fprintf_filtered (stream, "{...}");
+ else
+ {
+ fprintf_filtered (stream, "{");
+ len = TYPE_NFIELDS (type);
+ lastval = 0;
+ for (i = 0; i < len; i++)
+ {
+ QUIT;
+ if (i) fprintf_filtered (stream, ", ");
+ fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
+ if (lastval != TYPE_FIELD_BITPOS (type, i))
+ {
+ fprintf_filtered (stream, " : %d", TYPE_FIELD_BITPOS (type, i));
+ lastval = TYPE_FIELD_BITPOS (type, i);
+ }
+ lastval++;
+ }
+ fprintf_filtered (stream, "}");
+ }
+ break;
+
+ case TYPE_CODE_INT:
+ if (TYPE_UNSIGNED (type))
+ name = unsigned_type_table[TYPE_LENGTH (type)];
+ else
+ name = signed_type_table[TYPE_LENGTH (type)];
+ fputs_filtered (name, stream);
+ break;
+
+ case TYPE_CODE_FLT:
+ name = float_type_table[TYPE_LENGTH (type)];
+ fputs_filtered (name, stream);
+ break;
+
+ case TYPE_CODE_VOID:
+ fprintf_filtered (stream, "void");
+ break;
+
+ case 0:
+ fprintf_filtered (stream, "struct unknown");
+ break;
+
+ default:
+ error ("Invalid type code in symbol table.");
+ }
+}
+
+static void
+scalar_print_decimal(stream, type, val)
+ FILE *stream;
+ struct type *type;
+ LONGEST val;
+{
+ fprintf_filtered(stream, TYPE_UNSIGNED(type)? "%lu":"%ld", val);
+}
+
+static void
+scalar_print_hex(stream, type, val)
+ FILE *stream;
+ struct type *type;
+ LONGEST val;
+{
+ switch (TYPE_LENGTH(type)) {
+ case 1:
+ fprintf_filtered (stream, "0x%02lx", val);
+ break;
+ case 2:
+ fprintf_filtered (stream, "0x%04lx", val);
+ break;
+ case 4:
+ fprintf_filtered (stream, "0x%08lx", val);
+ break;
+ default:
+ fprintf_filtered (stream, "0x%lx", val);
+ break;
+ }
+}
+
+static void
+scalar_print_octal(stream, type, val)
+ FILE *stream;
+ struct type *type;
+ LONGEST val;
+{
+ switch (TYPE_LENGTH(type)) {
+ case 1:
+ fprintf_filtered (stream, "0%03lo", val);
+ break;
+ case 2:
+ fprintf_filtered (stream, "0%06lo", val);
+ break;
+ case 4:
+ fprintf_filtered (stream, "0%012lo", val);
+ break;
+ default:
+ fprintf_filtered (stream, "0%lo", val);
+ break;
+ }
+}
+
+static void
+scalar_print_hack(stream, type, val)
+ FILE *stream;
+ struct type *type;
+ LONGEST val;
+{
+ if (TYPE_UNSIGNED(type))
+ scalar_print_hex(stream, type, val);
+ else
+ scalar_print_decimal(stream, type, val);
+}
+
+static void
+set_maximum_command (arg)
+ char *arg;
+{
+ if (!arg) error_no_arg ("value for maximum elements to print");
+ print_max = parse_and_eval_address (arg);
+ if (print_max == 0)
+ print_max = UINT_MAX;
+}
+
+static void
+set_base_command(arg)
+ char *arg;
+{
+ int base;
+
+ if (!arg)
+ base = 0;
+ else
+ base = parse_and_eval_address (arg);
+ switch (base) {
+ default:
+ default_scalar_print = scalar_print_hack;
+ break;
+ case 8:
+ default_scalar_print = scalar_print_octal;
+ break;
+ case 10:
+ default_scalar_print = scalar_print_decimal;
+ break;
+ case 16:
+ default_scalar_print = scalar_print_hex;
+ break;
+ }
+}
+
+static void
+set_prettyprint_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ prettyprint = parse_binary_operation ("set prettyprint", arg);
+}
+
+static void
+set_unionprint_command (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ unionprint = parse_binary_operation ("set unionprint", arg);
+}
+
+format_info (arg, from_tty)
+ char *arg;
+ int from_tty;
+{
+ if (arg)
+ error ("\"info format\" does not take any arguments.");
+ printf ("Prettyprinting of structures is %s.\n",
+ prettyprint ? "on" : "off");
+ printf ("Printing of unions interior to structures is %s.\n",
+ unionprint ? "on" : "off");
+ if (print_max == UINT_MAX)
+ printf_filtered
+ ("There is no maximum number of array elements printed.\n");
+ else
+ printf_filtered
+ ("The maximum number of array elements printed is %d.\n", print_max);
+}
+
+extern struct cmd_list_element *setlist;
+
+void
+_initialize_valprint ()
+{
+ add_cmd ("base", class_support, set_base_command,
+ "Change default integer print radix to 8, 10 or 16\n\
+No args returns to the ad-hoc default of `16' for unsigned values\n\
+and `10' otherwise.",
+ &setlist);
+ add_cmd ("array-max", class_vars, set_maximum_command,
+ "Set NUMBER as limit on string chars or array elements to print.\n\
+\"set array-max 0\" causes there to be no limit.",
+ &setlist);
+
+ add_cmd ("prettyprint", class_support, set_prettyprint_command,
+ "Turn prettyprinting of structures on and off.",
+ &setlist);
+ add_alias_cmd ("pp", "prettyprint", class_support, 1, &setlist);
+
+ add_cmd ("unionprint", class_support, set_unionprint_command,
+ "Turn printing of unions interior to structures on and off.",
+ &setlist);
+
+ add_info ("format", format_info,
+ "Show current settings of data formatting options.");
+
+ /* Give people the defaults which they are used to. */
+ prettyprint = 0;
+ unionprint = 1;
+
+ print_max = 200;
+
+ unsigned_type_table
+ = (char **) xmalloc ((1 + sizeof (unsigned LONGEST)) * sizeof (char *));
+ bzero (unsigned_type_table, (1 + sizeof (unsigned LONGEST)));
+ unsigned_type_table[sizeof (unsigned char)] = "unsigned char";
+ unsigned_type_table[sizeof (unsigned short)] = "unsigned short";
+ unsigned_type_table[sizeof (unsigned long)] = "unsigned long";
+ unsigned_type_table[sizeof (unsigned int)] = "unsigned int";
+#ifdef LONG_LONG
+ unsigned_type_table[sizeof (unsigned long long)] = "unsigned long long";
+#endif
+
+ signed_type_table
+ = (char **) xmalloc ((1 + sizeof (LONGEST)) * sizeof (char *));
+ bzero (signed_type_table, (1 + sizeof (LONGEST)));
+ signed_type_table[sizeof (char)] = "char";
+ signed_type_table[sizeof (short)] = "short";
+ signed_type_table[sizeof (long)] = "long";
+ signed_type_table[sizeof (int)] = "int";
+#ifdef LONG_LONG
+ signed_type_table[sizeof (long long)] = "long long";
+#endif
+
+ float_type_table
+ = (char **) xmalloc ((1 + sizeof (double)) * sizeof (char *));
+ bzero (float_type_table, (1 + sizeof (double)));
+ float_type_table[sizeof (float)] = "float";
+ float_type_table[sizeof (double)] = "double";
+}
+
diff --git a/gnu/usr.bin/gdb/value.h b/gnu/usr.bin/gdb/value.h
new file mode 100644
index 0000000..07dd8e8
--- /dev/null
+++ b/gnu/usr.bin/gdb/value.h
@@ -0,0 +1,212 @@
+/* Definitions for values of C expressions, for GDB.
+ Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * The structure which defines the type of a value. It should never
+ * be possible for a program lval value to survive over a call to the inferior
+ * (ie to be put into the history list or an internal variable).
+ */
+enum lval_type {
+ /* Not an lval. */
+ not_lval,
+ /* In memory. Could be a saved register. */
+ lval_memory,
+ /* In a register. */
+ lval_register,
+ /* In a gdb internal variable. */
+ lval_internalvar,
+ /* Part of a gdb internal variable (structure field). */
+ lval_internalvar_component,
+ /* In a register series in a frame not the current one, which may have been
+ partially saved or saved in different places (otherwise would be
+ lval_register or lval_memory). */
+ lval_reg_frame_relative,
+};
+
+struct value
+ {
+ /* Type of value; either not an lval, or one of the various
+ different possible kinds of lval. */
+ enum lval_type lval;
+ /* Location of value (if lval). */
+ union
+ {
+ /* Address in inferior or byte of registers structure. */
+ CORE_ADDR address;
+ /* Pointer to interrnal variable. */
+ struct internalvar *internalvar;
+ /* Number of register. Only used with
+ lval_reg_frame_relative. */
+ int regnum;
+ } location;
+ /* Describes offset of a value within lval a structure in bytes. */
+ int offset;
+ /* Only used for bitfields; number of bits contained in them. */
+ int bitsize;
+ /* Only used for bitfields; position of start of field. */
+ int bitpos;
+ /* Frame value is relative to. In practice, this address is only
+ used if the value is stored in several registers in other than
+ the current frame, and these registers have not all been saved
+ at the same place in memory. This will be described in the
+ lval enum above as "lval_reg_frame_relative". */
+ CORE_ADDR frame_addr;
+ /* Type of the value. */
+ struct type *type;
+ /* Values are stored in a chain, so that they can be deleted
+ easily over calls to the inferior. Values assigned to internal
+ variables or put into the value history are taken off this
+ list. */
+ struct value *next;
+ /* If an lval is forced to repeat, a new value is created with
+ these fields set. The new value is not an lval. */
+ short repeated;
+ short repetitions;
+ /* Register number if the value is from a register. Is not kept
+ if you take a field of a structure that is stored in a
+ register. Shouldn't it be? */
+ short regno;
+ /* Actual contents of the value. For use of this value; setting
+ it uses the stuff above. */
+ long contents[1];
+ };
+
+typedef struct value *value;
+
+#define VALUE_TYPE(val) (val)->type
+#define VALUE_CONTENTS(val) ((char *) (val)->contents)
+#define VALUE_LVAL(val) (val)->lval
+#define VALUE_ADDRESS(val) (val)->location.address
+#define VALUE_INTERNALVAR(val) (val)->location.internalvar
+#define VALUE_FRAME_REGNUM(val) ((val)->location.regnum)
+#define VALUE_FRAME(val) ((val)->frame_addr)
+#define VALUE_OFFSET(val) (val)->offset
+#define VALUE_BITSIZE(val) (val)->bitsize
+#define VALUE_BITPOS(val) (val)->bitpos
+#define VALUE_NEXT(val) (val)->next
+#define VALUE_REPEATED(val) (val)->repeated
+#define VALUE_REPETITIONS(val) (val)->repetitions
+#define VALUE_REGNO(val) (val)->regno
+
+/* If ARG is an array, convert it to a pointer.
+ If ARG is an enum, convert it to an integer.
+
+ References are dereferenced. */
+
+#define COERCE_ARRAY(arg) \
+{ if (TYPE_CODE ( VALUE_TYPE (arg)) == TYPE_CODE_REF) \
+ arg = value_ind (arg); \
+ if (VALUE_REPEATED (arg) \
+ || TYPE_CODE (VALUE_TYPE (arg)) == TYPE_CODE_ARRAY) \
+ arg = value_coerce_array (arg); \
+ if (TYPE_CODE (VALUE_TYPE (arg)) == TYPE_CODE_ENUM) \
+ arg = value_cast (builtin_type_unsigned_int, arg); \
+}
+
+/* If ARG is an enum, convert it to an integer. */
+
+#define COERCE_ENUM(arg) \
+{ if (TYPE_CODE ( VALUE_TYPE (arg)) == TYPE_CODE_REF) \
+ arg = value_ind (arg); \
+ if (TYPE_CODE (VALUE_TYPE (arg)) == TYPE_CODE_ENUM) \
+ arg = value_cast (builtin_type_unsigned_int, arg); \
+}
+
+/* Internal variables (variables for convenience of use of debugger)
+ are recorded as a chain of these structures. */
+
+struct internalvar
+{
+ struct internalvar *next;
+ char *name;
+ value value;
+};
+
+LONGEST value_as_long ();
+double value_as_double ();
+LONGEST unpack_long ();
+double unpack_double ();
+long unpack_field_as_long ();
+value value_from_long ();
+value value_from_double ();
+value value_at ();
+value value_from_register ();
+value value_of_variable ();
+value value_of_register ();
+value read_var_value ();
+value locate_var_value ();
+value allocate_value ();
+value allocate_repeat_value ();
+value value_string ();
+
+value value_binop ();
+value value_add ();
+value value_sub ();
+value value_coerce_array ();
+value value_ind ();
+value value_addr ();
+value value_assign ();
+value value_neg ();
+value value_lognot ();
+value value_struct_elt (), value_struct_elt_for_address ();
+value value_field ();
+value value_cast ();
+value value_zero ();
+value value_repeat ();
+value value_subscript ();
+
+value call_function ();
+value value_being_returned ();
+int using_struct_return ();
+
+value evaluate_expression ();
+value evaluate_type ();
+value parse_and_eval ();
+value parse_to_comma_and_eval ();
+
+value access_value_history ();
+value value_of_internalvar ();
+struct internalvar *lookup_internalvar ();
+
+int value_equal ();
+int value_less ();
+int value_zerop ();
+
+/* C++ */
+value value_of_this ();
+value value_static_field ();
+value value_x_binop ();
+value value_x_unop ();
+int binop_user_defined_p ();
+int unop_user_defined_p ();
+
+void read_register_bytes ();
+void modify_field ();
+void type_print ();
+void type_print_1 ();
+
+/* Possibilities for prettyprint parameters to routines which print
+ things. */
+enum val_prettyprint {
+ Val_no_prettyprint = 0,
+ Val_prettyprint,
+ /* Use the default setting which the user has specified. */
+ Val_pretty_default
+ };
+
diff --git a/gnu/usr.bin/gdb/values.c b/gnu/usr.bin/gdb/values.c
new file mode 100644
index 0000000..93a2911
--- /dev/null
+++ b/gnu/usr.bin/gdb/values.c
@@ -0,0 +1,1059 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)values.c 6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Low level packing and unpacking of values for GDB.
+ Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "value.h"
+
+/* The value-history records all the values printed
+ by print commands during this session. Each chunk
+ records 60 consecutive values. The first chunk on
+ the chain records the most recent values.
+ The total number of values is in value_history_count. */
+
+#define VALUE_HISTORY_CHUNK 60
+
+struct value_history_chunk
+{
+ struct value_history_chunk *next;
+ value values[VALUE_HISTORY_CHUNK];
+};
+
+/* Chain of chunks now in use. */
+
+static struct value_history_chunk *value_history_chain;
+
+static int value_history_count; /* Abs number of last entry stored */
+
+
+/* List of all value objects currently allocated
+ (except for those released by calls to release_value)
+ This is so they can be freed after each command. */
+
+static value all_values;
+
+/* Allocate a value that has the correct length for type TYPE. */
+
+value
+allocate_value (type)
+ struct type *type;
+{
+ register value val;
+
+ /* If the type we want had no definition in the file it first
+ * appeared in, it will be marked a `stub'. The real definition
+ * probably appeared later so try to find it. */
+ if (TYPE_FLAGS(type) & TYPE_FLAG_STUB)
+ {
+ register char *cp;
+ register struct symbol *sym;
+ extern char *index();
+
+ if (cp = index(TYPE_NAME(type), ' '))
+ ++cp;
+ else
+ cp = TYPE_NAME(type);
+
+ sym = lookup_symbol(cp, 0, STRUCT_NAMESPACE, 0);
+
+ if (sym && TYPE_CODE(SYMBOL_TYPE(sym)) == TYPE_CODE(type))
+ bcopy (SYMBOL_TYPE (sym), type, sizeof (*type));
+ }
+ val = (value) xmalloc (sizeof (struct value) + TYPE_LENGTH (type));
+ VALUE_NEXT (val) = all_values;
+ all_values = val;
+ VALUE_TYPE (val) = type;
+ VALUE_LVAL (val) = not_lval;
+ VALUE_ADDRESS (val) = 0;
+ VALUE_FRAME (val) = 0;
+ VALUE_OFFSET (val) = 0;
+ VALUE_BITPOS (val) = 0;
+ VALUE_BITSIZE (val) = 0;
+ VALUE_REPEATED (val) = 0;
+ VALUE_REPETITIONS (val) = 0;
+ VALUE_REGNO (val) = -1;
+ return val;
+}
+
+/* Allocate a value that has the correct length
+ for COUNT repetitions type TYPE. */
+
+value
+allocate_repeat_value (type, count)
+ struct type *type;
+ int count;
+{
+ register value val;
+
+ val = (value) xmalloc (sizeof (struct value) + TYPE_LENGTH (type) * count);
+ VALUE_NEXT (val) = all_values;
+ all_values = val;
+ VALUE_TYPE (val) = type;
+ VALUE_LVAL (val) = not_lval;
+ VALUE_ADDRESS (val) = 0;
+ VALUE_FRAME (val) = 0;
+ VALUE_OFFSET (val) = 0;
+ VALUE_BITPOS (val) = 0;
+ VALUE_BITSIZE (val) = 0;
+ VALUE_REPEATED (val) = 1;
+ VALUE_REPETITIONS (val) = count;
+ VALUE_REGNO (val) = -1;
+ return val;
+}
+
+/* Free all the values that have been allocated (except for those released).
+ Called after each command, successful or not. */
+
+void
+free_all_values ()
+{
+ register value val, next;
+
+ for (val = all_values; val; val = next)
+ {
+ next = VALUE_NEXT (val);
+ free (val);
+ }
+
+ all_values = 0;
+}
+
+/* Remove VAL from the chain all_values
+ so it will not be freed automatically. */
+
+void
+release_value (val)
+ register value val;
+{
+ register value v;
+
+ if (all_values == val)
+ {
+ all_values = val->next;
+ return;
+ }
+
+ for (v = all_values; v; v = v->next)
+ {
+ if (v->next == val)
+ {
+ v->next = val->next;
+ break;
+ }
+ }
+}
+
+/* Return a copy of the value ARG.
+ It contains the same contents, for same memory address,
+ but it's a different block of storage. */
+
+static value
+value_copy (arg)
+ value arg;
+{
+ register value val;
+ register struct type *type = VALUE_TYPE (arg);
+ if (VALUE_REPEATED (arg))
+ val = allocate_repeat_value (type, VALUE_REPETITIONS (arg));
+ else
+ val = allocate_value (type);
+ VALUE_LVAL (val) = VALUE_LVAL (arg);
+ VALUE_ADDRESS (val) = VALUE_ADDRESS (arg);
+ VALUE_OFFSET (val) = VALUE_OFFSET (arg);
+ VALUE_BITPOS (val) = VALUE_BITPOS (arg);
+ VALUE_BITSIZE (val) = VALUE_BITSIZE (arg);
+ VALUE_REGNO (val) = VALUE_REGNO (arg);
+ bcopy (VALUE_CONTENTS (arg), VALUE_CONTENTS (val),
+ TYPE_LENGTH (VALUE_TYPE (arg))
+ * (VALUE_REPEATED (arg) ? VALUE_REPETITIONS (arg) : 1));
+ return val;
+}
+
+/* Access to the value history. */
+
+/* Record a new value in the value history.
+ Returns the absolute history index of the entry. */
+
+int
+record_latest_value (val)
+ value val;
+{
+ int i;
+ double foo;
+
+ /* Check error now if about to store an invalid float. We return -1
+ to the caller, but allow them to continue, e.g. to print it as "Nan". */
+ if (TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_FLT) {
+ foo = unpack_double (VALUE_TYPE (val), VALUE_CONTENTS (val), &i);
+ if (i) return -1; /* Indicate value not saved in history */
+ }
+
+ /* Here we treat value_history_count as origin-zero
+ and applying to the value being stored now. */
+
+ i = value_history_count % VALUE_HISTORY_CHUNK;
+ if (i == 0)
+ {
+ register struct value_history_chunk *new
+ = (struct value_history_chunk *)
+ xmalloc (sizeof (struct value_history_chunk));
+ bzero (new->values, sizeof new->values);
+ new->next = value_history_chain;
+ value_history_chain = new;
+ }
+
+ value_history_chain->values[i] = val;
+ release_value (val);
+
+ /* Now we regard value_history_count as origin-one
+ and applying to the value just stored. */
+
+ return ++value_history_count;
+}
+
+/* Return a copy of the value in the history with sequence number NUM. */
+
+value
+access_value_history (num)
+ int num;
+{
+ register struct value_history_chunk *chunk;
+ register int i;
+ register int absnum = num;
+
+ if (absnum <= 0)
+ absnum += value_history_count;
+
+ if (absnum <= 0)
+ {
+ if (num == 0)
+ error ("The history is empty.");
+ else if (num == 1)
+ error ("There is only one value in the history.");
+ else
+ error ("History does not go back to $$%d.", -num);
+ }
+ if (absnum > value_history_count)
+ error ("History has not yet reached $%d.", absnum);
+
+ absnum--;
+
+ /* Now absnum is always absolute and origin zero. */
+
+ chunk = value_history_chain;
+ for (i = (value_history_count - 1) / VALUE_HISTORY_CHUNK - absnum / VALUE_HISTORY_CHUNK;
+ i > 0; i--)
+ chunk = chunk->next;
+
+ return value_copy (chunk->values[absnum % VALUE_HISTORY_CHUNK]);
+}
+
+/* Clear the value history entirely.
+ Must be done when new symbol tables are loaded,
+ because the type pointers become invalid. */
+
+void
+clear_value_history ()
+{
+ register struct value_history_chunk *next;
+ register int i;
+ register value val;
+
+ while (value_history_chain)
+ {
+ for (i = 0; i < VALUE_HISTORY_CHUNK; i++)
+ if (val = value_history_chain->values[i])
+ free (val);
+ next = value_history_chain->next;
+ free (value_history_chain);
+ value_history_chain = next;
+ }
+ value_history_count = 0;
+}
+
+static void
+value_history_info (num_exp, from_tty)
+ char *num_exp;
+ int from_tty;
+{
+ register int i;
+ register value val;
+ static int num = 1;
+
+ if (num_exp)
+ {
+ if (num_exp[0] == '+' && num_exp[1] == '\0')
+ /* "info history +" should print from the stored position. */
+ ;
+ else
+ /* "info history <exp>" should print around value number <exp>. */
+ num = parse_and_eval_address (num_exp) - 5;
+ }
+ else
+ {
+ /* "info history" means print the last 10 values. */
+ num = value_history_count - 9;
+ }
+
+ if (num <= 0)
+ num = 1;
+
+ for (i = num; i < num + 10 && i <= value_history_count; i++)
+ {
+ val = access_value_history (i);
+ printf_filtered ("$%d = ", i);
+ value_print (val, stdout, 0, Val_pretty_default);
+ printf_filtered ("\n");
+ }
+
+ /* The next "info history +" should start after what we just printed. */
+ num += 10;
+
+ /* Hitting just return after this command should do the same thing as
+ "info history +". If num_exp is null, this is unnecessary, since
+ "info history +" is not useful after "info history". */
+ if (from_tty && num_exp)
+ {
+ num_exp[0] = '+';
+ num_exp[1] = '\0';
+ }
+}
+
+/* Internal variables. These are variables within the debugger
+ that hold values assigned by debugger commands.
+ The user refers to them with a '$' prefix
+ that does not appear in the variable names stored internally. */
+
+static struct internalvar *internalvars;
+
+/* Look up an internal variable with name NAME. NAME should not
+ normally include a dollar sign.
+
+ If the specified internal variable does not exist,
+ one is created, with a void value. */
+
+struct internalvar *
+lookup_internalvar (name)
+ char *name;
+{
+ register struct internalvar *var;
+
+ for (var = internalvars; var; var = var->next)
+ if (!strcmp (var->name, name))
+ return var;
+
+ var = (struct internalvar *) xmalloc (sizeof (struct internalvar));
+ var->name = concat (name, "", "");
+ var->value = allocate_value (builtin_type_void);
+ release_value (var->value);
+ var->next = internalvars;
+ internalvars = var;
+ return var;
+}
+
+value
+value_of_internalvar (var)
+ struct internalvar *var;
+{
+ register value val;
+
+#ifdef IS_TRAPPED_INTERNALVAR
+ if (IS_TRAPPED_INTERNALVAR (var->name))
+ return VALUE_OF_TRAPPED_INTERNALVAR (var);
+#endif
+
+ val = value_copy (var->value);
+ VALUE_LVAL (val) = lval_internalvar;
+ VALUE_INTERNALVAR (val) = var;
+ return val;
+}
+
+void
+set_internalvar_component (var, offset, bitpos, bitsize, newval)
+ struct internalvar *var;
+ int offset, bitpos, bitsize;
+ value newval;
+{
+ register char *addr = VALUE_CONTENTS (var->value) + offset;
+
+#ifdef IS_TRAPPED_INTERNALVAR
+ if (IS_TRAPPED_INTERNALVAR (var->name))
+ SET_TRAPPED_INTERNALVAR (var, newval, bitpos, bitsize, offset);
+#endif
+
+ if (bitsize)
+ modify_field (addr, (int) value_as_long (newval),
+ bitpos, bitsize);
+ else
+ bcopy (VALUE_CONTENTS (newval), addr,
+ TYPE_LENGTH (VALUE_TYPE (newval)));
+}
+
+void
+set_internalvar (var, val)
+ struct internalvar *var;
+ value val;
+{
+#ifdef IS_TRAPPED_INTERNALVAR
+ if (IS_TRAPPED_INTERNALVAR (var->name))
+ SET_TRAPPED_INTERNALVAR (var, val, 0, 0, 0);
+#endif
+
+ free (var->value);
+ var->value = value_copy (val);
+ release_value (var->value);
+}
+
+char *
+internalvar_name (var)
+ struct internalvar *var;
+{
+ return var->name;
+}
+
+/* Free all internalvars. Done when new symtabs are loaded,
+ because that makes the values invalid. */
+
+void
+clear_internalvars ()
+{
+ register struct internalvar *var;
+
+ while (internalvars)
+ {
+ var = internalvars;
+ internalvars = var->next;
+ free (var->name);
+ free (var->value);
+ free (var);
+ }
+}
+
+static void
+convenience_info ()
+{
+ register struct internalvar *var;
+ int varseen = 0;
+
+ for (var = internalvars; var; var = var->next)
+ {
+#ifdef IS_TRAPPED_INTERNALVAR
+ if (IS_TRAPPED_INTERNALVAR (var->name))
+ continue;
+#endif
+ if (!varseen)
+ {
+ printf ("Debugger convenience variables:\n\n");
+ varseen = 1;
+ }
+ printf ("$%s: ", var->name);
+ value_print (var->value, stdout, 0, Val_pretty_default);
+ printf ("\n");
+ }
+ if (!varseen)
+ printf ("No debugger convenience variables now defined.\n\
+Convenience variables have names starting with \"$\";\n\
+use \"set\" as in \"set $foo = 5\" to define them.\n");
+}
+
+/* Extract a value as a C number (either long or double).
+ Knows how to convert fixed values to double, or
+ floating values to long.
+ Does not deallocate the value. */
+
+LONGEST
+value_as_long (val)
+ register value val;
+{
+ return unpack_long (VALUE_TYPE (val), VALUE_CONTENTS (val));
+}
+
+double
+value_as_double (val)
+ register value val;
+{
+ double foo;
+ int inv;
+
+ foo = unpack_double (VALUE_TYPE (val), VALUE_CONTENTS (val), &inv);
+ if (inv)
+ error ("Invalid floating value found in program.");
+ return foo;
+}
+
+/* Unpack raw data (copied from debugee) at VALADDR
+ as a long, or as a double, assuming the raw data is described
+ by type TYPE. Knows how to convert different sizes of values
+ and can convert between fixed and floating point.
+
+ C++: It is assumed that the front-end has taken care of
+ all matters concerning pointers to members. A pointer
+ to member which reaches here is considered to be equivalent
+ to an INT (or some size). After all, it is only an offset. */
+
+LONGEST
+unpack_long (type, valaddr)
+ struct type *type;
+ char *valaddr;
+{
+ register enum type_code code = TYPE_CODE (type);
+ register int len = TYPE_LENGTH (type);
+ register int nosign = TYPE_UNSIGNED (type);
+
+ if (code == TYPE_CODE_ENUM)
+ code = TYPE_CODE_INT;
+ if (code == TYPE_CODE_FLT)
+ {
+ if (len == sizeof (float))
+ return * (float *) valaddr;
+
+ if (len == sizeof (double))
+ return * (double *) valaddr;
+ }
+ else if (code == TYPE_CODE_INT && nosign)
+ {
+ if (len == sizeof (char))
+ return * (unsigned char *) valaddr;
+
+ if (len == sizeof (short))
+ return * (unsigned short *) valaddr;
+
+ if (len == sizeof (int))
+ return * (unsigned int *) valaddr;
+
+ if (len == sizeof (long))
+ return * (unsigned long *) valaddr;
+#ifdef LONG_LONG
+ if (len == sizeof (long long))
+ return * (unsigned long long *) valaddr;
+#endif
+ }
+ else if (code == TYPE_CODE_INT)
+ {
+ if (len == sizeof (char))
+ return * (char *) valaddr;
+
+ if (len == sizeof (short))
+ return * (short *) valaddr;
+
+ if (len == sizeof (int))
+ return * (int *) valaddr;
+
+ if (len == sizeof (long))
+ return * (long *) valaddr;
+
+#ifdef LONG_LONG
+ if (len == sizeof (long long))
+ return * (long long *) valaddr;
+#endif
+ }
+ else if (code == TYPE_CODE_PTR
+ || code == TYPE_CODE_REF)
+ {
+ if (len == sizeof (char *))
+ return (CORE_ADDR) * (char **) valaddr;
+ }
+ else if (code == TYPE_CODE_MEMBER)
+ error ("not implemented: member types in unpack_long");
+
+ error ("Value not integer or pointer.");
+}
+
+/* Return a double value from the specified type and address.
+ INVP points to an int which is set to 0 for valid value,
+ 1 for invalid value (bad float format). In either case,
+ the returned double is OK to use. */
+
+double
+unpack_double (type, valaddr, invp)
+ struct type *type;
+ char *valaddr;
+ int *invp;
+{
+ register enum type_code code = TYPE_CODE (type);
+ register int len = TYPE_LENGTH (type);
+ register int nosign = TYPE_UNSIGNED (type);
+
+ *invp = 0; /* Assume valid. */
+ if (code == TYPE_CODE_FLT)
+ {
+ if (INVALID_FLOAT (valaddr, len))
+ {
+ *invp = 1;
+ return 1.234567891011121314;
+ }
+
+ if (len == sizeof (float))
+ return * (float *) valaddr;
+
+ if (len == sizeof (double))
+ {
+ /* Some machines require doubleword alignment for doubles.
+ This code works on them, and on other machines. */
+ double temp;
+ bcopy ((char *) valaddr, (char *) &temp, sizeof (double));
+ return temp;
+ }
+ }
+ else if (code == TYPE_CODE_INT && nosign)
+ {
+ if (len == sizeof (char))
+ return * (unsigned char *) valaddr;
+
+ if (len == sizeof (short))
+ return * (unsigned short *) valaddr;
+
+ if (len == sizeof (int))
+ return * (unsigned int *) valaddr;
+
+ if (len == sizeof (long))
+ return * (unsigned long *) valaddr;
+
+#ifdef LONG_LONG
+ if (len == sizeof (long long))
+ return * (unsigned long long *) valaddr;
+#endif
+ }
+ else if (code == TYPE_CODE_INT)
+ {
+ if (len == sizeof (char))
+ return * (char *) valaddr;
+
+ if (len == sizeof (short))
+ return * (short *) valaddr;
+
+ if (len == sizeof (int))
+ return * (int *) valaddr;
+
+ if (len == sizeof (long))
+ return * (long *) valaddr;
+
+#ifdef LONG_LONG
+ if (len == sizeof (long long))
+ return * (long long *) valaddr;
+#endif
+ }
+
+ error ("Value not floating number.");
+ /* NOTREACHED */
+ return (double) 0; /* To silence compiler warning. */
+}
+
+/* Given a value ARG1 of a struct or union type,
+ extract and return the value of one of its fields.
+ FIELDNO says which field.
+
+ For C++, must also be able to return values from static fields */
+
+value
+value_field (arg1, fieldno)
+ register value arg1;
+ register int fieldno;
+{
+ register value v;
+ register struct type *type = TYPE_FIELD_TYPE (VALUE_TYPE (arg1), fieldno);
+ register int offset;
+
+ /* Handle packed fields */
+
+ offset = TYPE_FIELD_BITPOS (VALUE_TYPE (arg1), fieldno) / 8;
+ if (TYPE_FIELD_BITSIZE (VALUE_TYPE (arg1), fieldno))
+ {
+ v = value_from_long (type,
+ unpack_field_as_long (VALUE_TYPE (arg1),
+ VALUE_CONTENTS (arg1),
+ fieldno));
+ VALUE_BITPOS (v) = TYPE_FIELD_BITPOS (VALUE_TYPE (arg1), fieldno) % 8;
+ VALUE_BITSIZE (v) = TYPE_FIELD_BITSIZE (VALUE_TYPE (arg1), fieldno);
+ }
+ else
+ {
+ v = allocate_value (type);
+ bcopy (VALUE_CONTENTS (arg1) + offset,
+ VALUE_CONTENTS (v),
+ TYPE_LENGTH (type));
+ }
+ VALUE_LVAL (v) = VALUE_LVAL (arg1);
+ if (VALUE_LVAL (arg1) == lval_internalvar)
+ VALUE_LVAL (v) = lval_internalvar_component;
+ VALUE_ADDRESS (v) = VALUE_ADDRESS (arg1);
+ VALUE_OFFSET (v) = offset + VALUE_OFFSET (arg1);
+ return v;
+}
+
+value
+value_fn_field (arg1, fieldno, subfieldno)
+ register value arg1;
+ register int fieldno;
+{
+ register value v;
+ struct fn_field *f = TYPE_FN_FIELDLIST1 (VALUE_TYPE (arg1), fieldno);
+ register struct type *type = TYPE_FN_FIELD_TYPE (f, subfieldno);
+ struct symbol *sym;
+
+ sym = lookup_symbol (TYPE_FN_FIELD_PHYSNAME (f, subfieldno),
+ 0, VAR_NAMESPACE, 0);
+ if (! sym) error ("Internal error: could not find physical method named %s",
+ TYPE_FN_FIELD_PHYSNAME (f, subfieldno));
+
+ v = allocate_value (type);
+ VALUE_ADDRESS (v) = BLOCK_START (SYMBOL_BLOCK_VALUE (sym));
+ VALUE_TYPE (v) = type;
+ return v;
+}
+
+/* Return a virtual function as a value.
+ ARG1 is the object which provides the virtual function
+ table pointer.
+ F is the list of member functions which contains the desired virtual
+ function.
+ J is an index into F which provides the desired virtual function.
+ TYPE is the basetype which first provides the virtual function table. */
+value
+value_virtual_fn_field (arg1, f, j, type)
+ value arg1;
+ struct fn_field *f;
+ int j;
+ struct type *type;
+{
+ /* First, get the virtual function table pointer. That comes
+ with a strange type, so cast it to type `pointer to long' (which
+ should serve just fine as a function type). Then, index into
+ the table, and convert final value to appropriate function type. */
+ value vfn, vtbl;
+ value vi = value_from_long (builtin_type_int,
+ (LONGEST) TYPE_FN_FIELD_VOFFSET (f, j));
+ VALUE_TYPE (arg1) = TYPE_VPTR_BASETYPE (type);
+
+ /* This type may have been defined before its virtual function table
+ was. If so, fill in the virtual function table entry for the
+ type now. */
+ if (TYPE_VPTR_FIELDNO (type) < 0)
+ TYPE_VPTR_FIELDNO (type)
+ = fill_in_vptr_fieldno (type);
+
+ /* The virtual function table is now an array of structures
+ which have the form { int16 offset, delta; void *pfn; }. */
+ vtbl = value_ind (value_field (arg1, TYPE_VPTR_FIELDNO (type)));
+
+ /* Index into the virtual function table. This is hard-coded because
+ looking up a field is not cheap, and it may be important to save
+ time, e.g. if the user has set a conditional breakpoint calling
+ a virtual function. */
+ vfn = value_field (value_subscript (vtbl, vi), 2);
+
+ /* Reinstantiate the function pointer with the correct type. */
+ VALUE_TYPE (vfn) = lookup_pointer_type (TYPE_FN_FIELD_TYPE (f, j));
+ return vfn;
+}
+
+/* The value of a static class member does not depend
+ on its instance, only on its type. If FIELDNO >= 0,
+ then fieldno is a valid field number and is used directly.
+ Otherwise, FIELDNAME is the name of the field we are
+ searching for. If it is not a static field name, an
+ error is signaled. TYPE is the type in which we look for the
+ static field member. */
+value
+value_static_field (type, fieldname, fieldno)
+ register struct type *type;
+ char *fieldname;
+ register int fieldno;
+{
+ register value v;
+ struct symbol *sym;
+
+ if (fieldno < 0)
+ {
+ register struct type *t = type;
+ /* Look for static field. */
+ while (t)
+ {
+ int i;
+ for (i = TYPE_NFIELDS (t) - 1; i >= 0; i--)
+ if (! strcmp (TYPE_FIELD_NAME (t, i), fieldname))
+ {
+ if (TYPE_FIELD_STATIC (t, i))
+ {
+ fieldno = i;
+ goto found;
+ }
+ else
+ error ("field `%s' is not static");
+ }
+ t = TYPE_BASECLASSES (t) ? TYPE_BASECLASS (t, 1) : 0;
+ }
+
+ t = type;
+
+ if (destructor_name_p (fieldname, t))
+ error ("use `info method' command to print out value of destructor");
+
+ while (t)
+ {
+ int i, j;
+
+ for (i = TYPE_NFN_FIELDS (t) - 1; i >= 0; i--)
+ {
+ if (! strcmp (TYPE_FN_FIELDLIST_NAME (t, i), fieldname))
+ {
+ error ("use `info method' command to print value of method \"%s\"", fieldname);
+ }
+ }
+ t = TYPE_BASECLASSES (t) ? TYPE_BASECLASS (t, 1) : 0;
+ }
+ error("there is no field named %s", fieldname);
+ }
+
+ found:
+
+ sym = lookup_symbol (TYPE_FIELD_STATIC_PHYSNAME (type, fieldno),
+ 0, VAR_NAMESPACE, 0);
+ if (! sym) error ("Internal error: could not find physical static variable named %s", TYPE_FIELD_BITSIZE (type, fieldno));
+
+ type = TYPE_FIELD_TYPE (type, fieldno);
+ v = value_at (type, (CORE_ADDR)SYMBOL_BLOCK_VALUE (sym));
+ return v;
+}
+
+long
+unpack_field_as_long (type, valaddr, fieldno)
+ struct type *type;
+ char *valaddr;
+ int fieldno;
+{
+ long val;
+ int bitpos = TYPE_FIELD_BITPOS (type, fieldno);
+ int bitsize = TYPE_FIELD_BITSIZE (type, fieldno);
+
+ bcopy (valaddr + bitpos / 8, &val, sizeof val);
+
+ /* Extracting bits depends on endianness of the machine. */
+#ifdef BITS_BIG_ENDIAN
+ val = val >> (sizeof val * 8 - bitpos % 8 - bitsize);
+#else
+ val = val >> (bitpos % 8);
+#endif
+
+ val &= (1 << bitsize) - 1;
+ return val;
+}
+
+void
+modify_field (addr, fieldval, bitpos, bitsize)
+ char *addr;
+ int fieldval;
+ int bitpos, bitsize;
+{
+ long oword;
+
+ /* Reject values too big to fit in the field in question.
+ Otherwise adjoining fields may be corrupted. */
+ if (fieldval & ~((1<<bitsize)-1))
+ error ("Value %d does not fit in %d bits.", fieldval, bitsize);
+
+ bcopy (addr, &oword, sizeof oword);
+
+ /* Shifting for bit field depends on endianness of the machine. */
+#ifdef BITS_BIG_ENDIAN
+ bitpos = sizeof (oword) * 8 - bitpos - bitsize;
+#endif
+
+ oword &= ~(((1 << bitsize) - 1) << bitpos);
+ oword |= fieldval << bitpos;
+ bcopy (&oword, addr, sizeof oword);
+}
+
+/* Convert C numbers into newly allocated values */
+
+value
+value_from_long (type, num)
+ struct type *type;
+ register LONGEST num;
+{
+ register value val = allocate_value (type);
+ register enum type_code code = TYPE_CODE (type);
+ register int len = TYPE_LENGTH (type);
+
+ if (code == TYPE_CODE_INT || code == TYPE_CODE_ENUM)
+ {
+ if (len == sizeof (char))
+ * (char *) VALUE_CONTENTS (val) = num;
+ else if (len == sizeof (short))
+ * (short *) VALUE_CONTENTS (val) = num;
+ else if (len == sizeof (int))
+ * (int *) VALUE_CONTENTS (val) = num;
+ else if (len == sizeof (long))
+ * (long *) VALUE_CONTENTS (val) = num;
+#ifdef LONG_LONG
+ else if (len == sizeof (long long))
+ * (long long *) VALUE_CONTENTS (val) = num;
+#endif
+ else
+ error ("Integer type encountered with unexpected data length.");
+ }
+ else
+ error ("Unexpected type encountered for integer constant.");
+
+ return val;
+}
+
+value
+value_from_double (type, num)
+ struct type *type;
+ double num;
+{
+ register value val = allocate_value (type);
+ register enum type_code code = TYPE_CODE (type);
+ register int len = TYPE_LENGTH (type);
+
+ if (code == TYPE_CODE_FLT)
+ {
+ if (len == sizeof (float))
+ * (float *) VALUE_CONTENTS (val) = num;
+ else if (len == sizeof (double))
+ * (double *) VALUE_CONTENTS (val) = num;
+ else
+ error ("Floating type encountered with unexpected data length.");
+ }
+ else
+ error ("Unexpected type encountered for floating constant.");
+
+ return val;
+}
+
+/* Deal with the value that is "about to be returned". */
+
+/* Return the value that a function returning now
+ would be returning to its caller, assuming its type is VALTYPE.
+ RETBUF is where we look for what ought to be the contents
+ of the registers (in raw form). This is because it is often
+ desirable to restore old values to those registers
+ after saving the contents of interest, and then call
+ this function using the saved values.
+ struct_return is non-zero when the function in question is
+ using the structure return conventions on the machine in question;
+ 0 when it is using the value returning conventions (this often
+ means returning pointer to where structure is vs. returning value). */
+
+value
+value_being_returned (valtype, retbuf, struct_return)
+ register struct type *valtype;
+ char retbuf[REGISTER_BYTES];
+ int struct_return;
+{
+ register value val;
+
+ if (struct_return)
+ return value_at (valtype, EXTRACT_STRUCT_VALUE_ADDRESS (retbuf));
+
+ val = allocate_value (valtype);
+ EXTRACT_RETURN_VALUE (valtype, retbuf, VALUE_CONTENTS (val));
+
+ return val;
+}
+
+/* Return true if the function specified is using the structure returning
+ convention on this machine to return arguments, or 0 if it is using
+ the value returning convention. FUNCTION is the value representing
+ the function, FUNCADDR is the address of the function, and VALUE_TYPE
+ is the type returned by the function */
+
+struct block *block_for_pc ();
+
+int
+using_struct_return (function, funcaddr, value_type)
+ value function;
+ CORE_ADDR funcaddr;
+ struct type *value_type;
+{
+ register enum type_code code = TYPE_CODE (value_type);
+
+ if (code == TYPE_CODE_STRUCT ||
+ code == TYPE_CODE_UNION ||
+ code == TYPE_CODE_ARRAY)
+ {
+ struct block *b = block_for_pc (funcaddr);
+
+ if (!(BLOCK_GCC_COMPILED (b) && TYPE_LENGTH (value_type) < 8))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Store VAL so it will be returned if a function returns now.
+ Does not verify that VAL's type matches what the current
+ function wants to return. */
+
+void
+set_return_value (val)
+ value val;
+{
+ register enum type_code code = TYPE_CODE (VALUE_TYPE (val));
+ char regbuf[REGISTER_BYTES];
+ double dbuf;
+ LONGEST lbuf;
+
+ if (code == TYPE_CODE_STRUCT
+ || code == TYPE_CODE_UNION)
+ error ("Specifying a struct or union return value is not supported.");
+
+ if (code == TYPE_CODE_FLT)
+ {
+ dbuf = value_as_double (val);
+
+ STORE_RETURN_VALUE (VALUE_TYPE (val), &dbuf);
+ }
+ else
+ {
+ lbuf = value_as_long (val);
+ STORE_RETURN_VALUE (VALUE_TYPE (val), &lbuf);
+ }
+}
+
+void
+_initialize_values ()
+{
+ add_info ("convenience", convenience_info,
+ "Debugger convenience (\"$foo\") variables.\n\
+These variables are created when you assign them values;\n\
+thus, \"print $foo=1\" gives \"$foo\" the value 1. Values may be any type.\n\n\
+A few convenience variables are given values automatically GDB:\n\
+\"$_\"holds the last address examined with \"x\" or \"info lines\",\n\
+\"$__\" holds the contents of the last address examined with \"x\".");
+
+ add_info ("values", value_history_info,
+ "Elements of value history (around item number IDX, or last ten).");
+ add_info_alias ("history", value_history_info, 0);
+}
diff --git a/gnu/usr.bin/gdb/version.c b/gnu/usr.bin/gdb/version.c
new file mode 100644
index 0000000..2f3dd85
--- /dev/null
+++ b/gnu/usr.bin/gdb/version.c
@@ -0,0 +1,20 @@
+/* Define the current version number of GDB.
+ Copyright (C) 1989, Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+char *version = "3.5";
diff --git a/gnu/usr.bin/gdb/wait.h b/gnu/usr.bin/gdb/wait.h
new file mode 100644
index 0000000..c431cb6
--- /dev/null
+++ b/gnu/usr.bin/gdb/wait.h
@@ -0,0 +1,81 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Van Jacobson and Steven McCanne of Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)wait.h 6.3 (Berkeley) 5/8/91
+ */
+
+/* Define how to access the structure that the wait system call stores.
+ On many systems, there is a structure defined for this.
+ But on vanilla-ish USG systems there is not. */
+
+#ifndef HAVE_WAIT_STRUCT
+
+#define WAITTYPE int
+#define WIFSTOPPED(w) (((w)&0377) == 0177)
+#define WIFSIGNALED(w) (((w)&0377) != 0177 && ((w)&~0377) == 0)
+#define WIFEXITED(w) (((w)&0377) == 0)
+#define WEXITSTATUS(w) ((w) >> 8)
+#define WSTOPSIG(w) ((w) >> 8)
+#define WCOREDUMP(w) (((w)&0200) != 0)
+#define WTERMSIG(w) ((w) & 0177)
+#define WSETEXIT(w, status) ((w) = (status))
+#define WSETSTOP(w,sig) ((w) = (0177 | ((sig) << 8)))
+
+#else
+
+#include <sys/wait.h>
+
+#define WAITTYPE union wait
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(w) (w).w_retcode
+#endif
+#ifndef WSTOPSIG
+#define WSTOPSIG(w) (w).w_stopsig
+#endif
+#ifndef WCOREDUMP
+#define WCOREDUMP(w) (w).w_coredump
+#endif
+#ifndef WTERMSIG
+#define WTERMSIG(w) (w).w_termsig
+#endif
+#ifndef WSETEXIT
+#define WSETEXIT(w, status) ((w).w_status = (status))
+#endif
+#ifndef WSETSTOP
+#define WSETSTOP(w,sig) \
+ ((w).w_stopsig = (sig), (w).w_coredump = 0, (w).w_termsig = 0177)
+#endif
+
+#endif
diff --git a/gnu/usr.bin/gdb/xgdb/Makefile b/gnu/usr.bin/gdb/xgdb/Makefile
new file mode 100644
index 0000000..72c5359
--- /dev/null
+++ b/gnu/usr.bin/gdb/xgdb/Makefile
@@ -0,0 +1,33 @@
+# %W% (Berkeley) %G%
+
+.include "../config/Makefile.$(MACHINE)"
+
+PROG= xgdb
+SRCS= xgdb.c xgdbinit.c
+GDBOBJS+= $(CONFIGSRCS:R:S/$/.o/g) \
+ blockframe.o breakpoint.o command.o copying.o core.o \
+ cplus-dem.o dbxread.o environ.o eval.o expprint.o \
+ expread.o findvar.o infcmd.o inflow.o infrun.o \
+ main.o obstack.o printcmd.o regex.o remote.o \
+ remote-sl.o source.o stack.o symmisc.o symtab.o \
+ utils.o valarith.o valops.o valprint.o values.o \
+ version.o \
+ funmap.o history.o keymaps.o readline.o
+CFLAGS+= -I.. -I$(.CURDIR)/.. -I$(.CURDIR)/../config \
+ -DHAVE_VPRINTF -DVI_MODE -DKERNELDEBUG
+LDFLAGS+= -L/usr/lib/X11
+LDADD+= $(GDBOBJS:S/^/..\//g) -lXaw -lXmu -lXt -lXext -lX11 -ltermcap
+NOMAN= noman
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
+
+#
+# Generate the constructor
+#
+xgdbinit.c: ../init.c xgdb.c
+ -(sed -e '/^}$$/d' ../init.c; \
+ egrep -h '^_initialize_[^ ]* *\(\)' $(.CURDIR)/xgdb.c; \
+ echo ';}') > xgdbinit.c
+
+CLEANFILES+= xgdbinit.c
diff --git a/gnu/usr.bin/gdb/xgdb/xgdb.c b/gnu/usr.bin/gdb/xgdb/xgdb.c
new file mode 100644
index 0000000..a2bd4f6
--- /dev/null
+++ b/gnu/usr.bin/gdb/xgdb/xgdb.c
@@ -0,0 +1,700 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ *
+ * static char rcsid[] = "$Header: /home/cvs/386BSD/src/usr.bin/gdb/xgdb/xgdb.c,v 1.1.1.1 1993/06/12 14:52:36 rgrimes Exp $";
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)xgdb.c 6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/*
+ * Interface from GDB to X windows. Copyright (C) 1987 Free Software
+ * Foundation, Inc.
+ *
+ * GDB is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY. No author or distributor accepts responsibility to anyone for
+ * the consequences of using it or for whether it serves any particular
+ * purpose or works at all, unless he says so in writing. Refer to the GDB
+ * General Public License for full details.
+ *
+ * Everyone is granted permission to copy, modify and redistribute GDB, but only
+ * under the conditions described in the GDB General Public License. A copy
+ * of this license is supposed to have been given to you along with GDB so
+ * you can know your rights and responsibilities. It should be in a file
+ * named COPYING. Among other things, the copyright notice and this notice
+ * must be preserved on all copies.
+ *
+ * In other words, go ahead and share GDB, but don't try to stop anyone else
+ * from sharing it farther. Help stamp out software hoarding!
+ */
+
+/*
+ * Original version was contributed by Derek Beatty, 30 June 87.
+ * This version is essentially a re-write of the original by Van
+ * Jacobson (van@helios.ee.lbl.gov), Nov, 90.
+ */
+
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "frame.h"
+
+extern int stop_breakpoint;
+
+#include <X11/IntrinsicP.h>
+#include <X11/StringDefs.h>
+#include <X11/Xaw/AsciiSink.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/Paned.h>
+#include <X11/Xaw/Text.h>
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+
+extern int errno;
+extern char *getenv();
+extern char *malloc();
+extern void bcopy();
+extern int select();
+
+extern int get_filename_and_charpos();
+extern int source_line_charpos();
+extern int source_charpos_line();
+extern void execute_command();
+extern void error_no_arg();
+extern void add_com();
+
+/* The X display where the window appears. */
+
+static char *displayname;
+static Display *display;
+
+static XtAppContext app_context;
+
+/* Windows manipulated by this package. */
+
+static Widget main_widget;
+static Widget containing_widget;
+static Widget source_name_widget;
+static Widget source_text_widget;
+static Widget button_box_widget;
+
+/* Source text display. */
+
+static struct frame_info *last_fi;
+static CORE_ADDR last_pc;
+static struct symtab *last_cur_symtab;
+static int last_cur_line;
+
+static int source_window_line;
+static char *source_window_file;
+static struct symtab *source_window_symtab;
+
+static char version_label[64];
+extern char *version;
+
+/* Forward declarations */
+
+static Widget create_text_widget();
+
+static int
+safe_strcmp(a, b)
+ register char *a, *b;
+{
+ register int i;
+
+ if (a == b)
+ return (0);
+ if (!a && b)
+ return (1);
+ if (a && !b)
+ return (-1);
+ return (strcmp(a, b));
+}
+
+
+/* Display an appropriate piece of source code in the source window. */
+
+void
+xgdb_display_source()
+{
+ char *filename = NULL;
+ struct symtab_and_line get_selected_frame_sal();
+ struct symtab_and_line sal;
+ struct frame_info *fi;
+
+ /* Do nothing if called before we are initialized */
+
+ if (!containing_widget)
+ return;
+
+ /*
+ * Figure out what to display (the appropriate hooks to tell
+ * us don't exist so we guess): If there's a current frame
+ * and it or its pc changed from the last time we were here,
+ * display appropriate source line. Otherwise if the current
+ * source symtab or line is different, display that line.
+ * Otherwise nothing changed so leave the display alone.
+ */
+ fi = get_frame_info(selected_frame);
+ if (fi && (fi != last_fi || fi->pc != last_pc)) {
+ last_fi = fi;
+ last_pc = fi->pc;
+ sal = find_pc_line(fi->pc, fi->next_frame);
+ if (sal.symtab == NULL) { /* XXX */
+ sal.symtab = current_source_symtab;
+ sal.line = current_source_line;
+ }
+ current_source_symtab = sal.symtab;
+ current_source_line = sal.line;
+ } else if (current_source_symtab != last_cur_symtab ||
+ current_source_line != last_cur_line) {
+ sal.symtab = last_cur_symtab = current_source_symtab;
+ sal.line = last_cur_line = current_source_line;
+ } else
+ return;
+ /*
+ * Do a path search and get the exact filename of this source file.
+ * Also scan it and find its source lines if not already done.
+ */
+ if (sal.symtab && filename == NULL) {
+ if (get_filename_and_charpos(sal.symtab, sal.line, &filename))
+ /* line numbers may have changed - force highlight */
+ source_window_line = -1;
+ }
+
+ /*
+ * If the source window is wrong, destroy it and make a new one.
+ */
+ if (safe_strcmp(filename, source_window_file)) {
+ Arg args[1];
+ Widget src = XawTextGetSource(source_text_widget);
+
+ if (filename) {
+ XtSetArg(args[0], XtNstring, filename);
+ XtSetValues(src, args, XtNumber(args));
+ args[0].name = XtNlabel;
+ XtSetValues(source_name_widget, args, XtNumber(args));
+ } else {
+ XtSetArg(args[0], XtNstring, "/dev/null");
+ XtSetValues(src, args, XtNumber(args));
+ XtSetArg(args[0], XtNlabel, "");
+ XtSetValues(source_name_widget, args, XtNumber(args));
+ }
+ if (source_window_file)
+ free(source_window_file);
+ source_window_file = filename;
+ source_window_line = sal.line + 1; /* force highlight */
+ }
+ if (sal.symtab && source_window_line != sal.line) {
+ /*
+ * Update display and cursor positions as necessary.
+ * Cursor should be placed on line sal.line.
+ */
+ XawTextPosition l, r;
+
+ source_window_symtab = sal.symtab;
+ source_window_line = sal.line;
+ l = source_line_charpos(source_window_symtab, sal.line);
+ r = source_line_charpos(source_window_symtab, sal.line + 1);
+ if (r < l)
+ r = l + 1;
+ XawTextSetSelection(source_text_widget, l, r);
+ XawTextScrollToLine(source_text_widget, l, 10, 3);
+ XawTextSetInsertionPoint(source_text_widget, l);
+ }
+}
+
+
+/*
+ * Handlers for buttons.
+ */
+
+static int
+current_lineno()
+{
+ XawTextPosition start, finish;
+
+ XawTextGetSelectionPos(source_text_widget, &start, &finish);
+ if (start >= finish)
+ start = XawTextGetInsertionPoint(source_text_widget);
+
+ return (source_charpos_line(source_window_symtab, start));
+}
+
+static char *
+append_selection(cp)
+ char *cp;
+{
+ int len;
+ XawTextPosition l, r;
+
+ XawTextGetSelectionPos(source_text_widget, &l, &r);
+ if ((len = r - l) > 0) {
+ Widget src = XawTextGetSource(source_text_widget);
+
+ while (len > 0) {
+ XawTextBlock tb;
+
+ XawTextSourceRead(src, l, &tb, len);
+ bcopy(tb.ptr, cp, tb.length);
+ cp += tb.length;
+ len -= tb.length;
+ }
+ if (cp[-1] == 0)
+ --cp;
+ }
+ return (cp);
+}
+
+static char *
+append_selection_word(cp)
+ register char *cp;
+{
+ register int len;
+ XawTextPosition l, r;
+ XawTextBlock tb;
+ register char c;
+ register Widget src = XawTextGetSource(source_text_widget);
+
+ XawTextGetSelectionPos(source_text_widget, &l, &r);
+ if ((len = r - l) <= 0) {
+ l = XawTextGetInsertionPoint(source_text_widget);
+ len = 128; /* XXX */
+
+ /* might have clicked in middle of word -- back up to start */
+ for ( ; l > 0; --l) {
+ XawTextSourceRead(src, l - 1, &tb, 1);
+ c = tb.ptr[0];
+ if (! isalnum(c) && c != '_' && c != '$')
+ break;
+ }
+ }
+ while (len > 0) {
+ char *sp;
+ int i;
+
+ XawTextSourceRead(src, l, &tb, len);
+ for (sp = tb.ptr, i = tb.length; --i >= 0; ) {
+ c = *sp++;
+ if (!isalnum(c) && c != '_' && c != '$')
+ return (cp);
+ *cp++ = c;
+ }
+ len -= tb.length;
+ }
+ return (cp);
+}
+
+static char *
+append_selection_expr(cp)
+ char *cp;
+{
+ int len;
+ XawTextPosition l, r;
+ Widget src = XawTextGetSource(source_text_widget);
+ XawTextBlock tb;
+ char *sp;
+ char c;
+
+ XawTextGetSelectionPos(source_text_widget, &l, &r);
+ if (r > l)
+ return (append_selection(cp));
+
+ l = XawTextGetInsertionPoint(source_text_widget);
+
+ /* might have clicked in middle of word -- back up to start */
+ for ( ; l > 0; --l) {
+ XawTextSourceRead(src, l - 1, &tb, 1);
+ c = tb.ptr[0];
+ if (! isalnum(c) && c != '_' && c != '$')
+ break;
+ }
+
+ len = 128; /* XXX */
+ while (len > 0) {
+ int i;
+ char pstack[64];
+ int pcnt = 0;
+
+ XawTextSourceRead(src, l, &tb, len);
+ for (sp = tb.ptr, i = tb.length; --i >= 0; ) {
+ switch (c = *sp++) {
+ case '\n':
+ case ';':
+ return (cp);
+ case '=':
+ if (cp[-1] != '=')
+ return (cp - 1);
+ if (len == 128)
+ return (cp);
+ break;
+ case ',':
+ if (pcnt <= 0)
+ return (cp);
+ break;
+ case '(':
+ pstack[pcnt] = ')';
+ if (++pcnt >= sizeof(pstack))
+ return (cp);
+ break;
+ case '[':
+ pstack[pcnt] = ']';
+ if (++pcnt >= sizeof(pstack))
+ return (cp);
+ break;
+ case ')':
+ case ']':
+ if (--pcnt < 0 || pstack[pcnt] != c)
+ return (cp);
+ break;
+ }
+ *cp++ = c;
+ }
+ len -= tb.length;
+ }
+ return (cp);
+}
+
+static int input_avail; /* XXX kluge: do_command sets this when command
+ * data from button is avaialble to force top level
+ * to break out of its loop. */
+/*
+ * Handle a button by running the command COMMAND.
+ */
+static void
+do_command(w, command, call_data)
+ Widget w;
+ register char *command;
+ caddr_t call_data;
+{
+ char cmd_line[256];
+ char buf[256];
+ register char *out = cmd_line;
+ char *cp;
+ register char c;
+ extern char *finish_command_input();
+
+ while (c = *command++) {
+ if (c == '%') {
+ switch (*command++) {
+ case 's': /* current selection */
+ out = append_selection(out);
+ break;
+ case 'S': /* 1st selected "word" at curor */
+ out = append_selection_word(out);
+ break;
+ case 'e': /* echo cmd before executing */
+ break;
+ case 'E': /* 1st selected expression at curor */
+ out = append_selection_expr(out);
+ break;
+
+ case 'l': /* current line number */
+ (void) sprintf(buf, "%d", current_lineno());
+ for (cp = buf; c = *cp++; *out++ = c)
+ ;
+ break;
+ case 'L': /* line we're stopped at */
+ (void) sprintf(buf, "%d", source_window_line);
+ for (cp = buf; c = *cp++; *out++ = c)
+ ;
+ break;
+ case 'f': /* current file name */
+ for (cp = source_window_symtab->filename;
+ c = *cp++; *out++ = c)
+ ;
+ break;
+ case 'b': /* break # we're stopped at */
+ if (stop_breakpoint <= 0)
+ /* if no breakpoint, don't do cmd */
+ return;
+
+ (void) sprintf(buf, "%d", stop_breakpoint);
+ for (cp = buf; c = *cp++; *out++ = c)
+ ;
+ break;
+ }
+ } else
+ *out++ = c;
+ }
+ *out = 0;
+ reinitialize_more_filter();
+ /* have to exit via readline or tty modes stay messed up */
+ for (cp = cmd_line; c = *cp++; )
+ rl_stuff_char(c);
+ rl_stuff_char('\n');
+ input_avail = 1;
+}
+
+/*
+ * Define and display all the buttons.
+ */
+static void
+addbutton(parent, name, function, closure)
+ Widget parent;
+ char *name;
+ void (*function) ();
+ caddr_t closure;
+{
+ static XtCallbackRec Callback[] = {
+ {NULL, (caddr_t) NULL},
+ {NULL, (caddr_t) NULL},
+ };
+ static Arg commandArgs[] = {
+ {XtNlabel, (XtArgVal) NULL},
+ {XtNcallback, (XtArgVal) Callback},
+ };
+ Widget w;
+ char wname[128];
+ register char *cp;
+
+ strcpy(wname, name);
+ while ((cp = index(wname, '*')) || (cp = index(wname, '.')))
+ *cp -= 0x10;
+
+ if (w = XtNameToWidget(parent, wname))
+ XtDestroyWidget(w);
+
+ Callback[0].callback = (XtCallbackProc) function;
+ Callback[0].closure = (caddr_t) closure;
+ commandArgs[0].value = (XtArgVal) name;
+ XtCreateManagedWidget(wname, commandWidgetClass, parent,
+ commandArgs, XtNumber(commandArgs));
+}
+
+/*
+ * Create the button windows and store them in `buttons'.
+ */
+static void
+create_buttons(parent)
+ Widget parent;
+{
+ addbutton(parent, "quit", do_command, "quit");
+}
+
+static void
+button_command(arg)
+ char *arg;
+{
+ char *label;
+ unsigned int len;
+
+ if (! arg)
+ error_no_arg("button label and command");
+
+ for (len = strlen(arg); len > 0 && isspace(arg[len - 1]); --len)
+ ;
+ if (len == 0)
+ error_no_arg("button label and command");
+ arg[len] = 0;
+
+ /* make a copy of button label & command for toolkit to use */
+ label = malloc(len + 1);
+ strcpy(label, arg);
+
+ /* find the end of the label */
+ if (*label == '"') {
+ if ((arg = index(++label, '"')) == 0) {
+ printf("button label missing closing quote\n");
+ return;
+ }
+ *arg++ = 0;
+ } else if (arg = index(label, ' '))
+ *arg++ = 0;
+ else
+ arg = label;
+
+ while (*arg && isspace(*arg))
+ ++arg;
+
+ addbutton(button_box_widget, label, do_command, arg);
+}
+
+static void
+button_delete_command(arg)
+ char *arg;
+{
+ unsigned int len;
+ Widget w;
+ register char *cp;
+
+ if (! arg)
+ error_no_arg("button name");
+
+ for (len = strlen(arg); len > 0 && isspace(arg[len - 1]); --len)
+ ;
+ if (len == 0)
+ error_no_arg("button name");
+ arg[len] = 0;
+
+ /* find the end of the label */
+ if (*arg == '"') {
+ if ((cp = index(++arg, '"')) == 0) {
+ printf("button label missing closing quote\n");
+ return;
+ }
+ *cp++ = 0;
+ }
+ while ((cp = index(arg, '*')) || (cp = index(arg, '.')))
+ *cp -= 0x10;
+
+ if (w = XtNameToWidget(button_box_widget, arg))
+ XtDestroyWidget(w);
+}
+
+/*
+ * Create a "label window" that just displays the string LABEL.
+ */
+static Widget
+create_label(name, label)
+ char *name, *label;
+{
+ Arg args[1];
+ Widget w;
+
+ XtSetArg(args[0], XtNlabel, label);
+ w = XtCreateManagedWidget(name, labelWidgetClass, containing_widget,
+ args, XtNumber(args));
+ return (w);
+}
+
+/*
+ * Create a subwindow of PARENT that displays and scrolls the contents of
+ * file FILENAME.
+ */
+static Widget
+create_text_widget(parent, filename)
+ Widget parent;
+ char *filename;
+{
+ static Arg arg[] = {
+ {XtNstring, NULL},
+ {XtNtype, XawAsciiFile},
+ {XtNcursor, None},
+ };
+ Widget text_widget;
+
+ arg[0].value = (XtArgVal)filename;
+ text_widget = XtCreateManagedWidget("src", asciiTextWidgetClass,
+ parent, arg, XtNumber(arg));
+ return (text_widget);
+}
+
+/*
+ * Entry point to create the widgets representing our display.
+ */
+void
+xgdb_create_window()
+{
+ /* initialize toolkit, setup defaults */
+#ifdef notyet
+ main_widget = XtAppInitialize(&app_context, "Xgdb", NULL, 0,
+ argcptr, argv, NULL, NULL, 0);
+#else
+ char *dummy_argv[] = { "xgdb", 0 };
+ int dummy_argc = 1;
+ main_widget = XtAppInitialize(&app_context, "Xgdb", NULL, 0,
+ &dummy_argc, dummy_argv, NULL, NULL, 0);
+#endif
+ display = XtDisplay(main_widget);
+ containing_widget = XtCreateManagedWidget("frame", panedWidgetClass,
+ main_widget, NULL, 0);
+
+ sprintf(version_label, "XGDB %s", version);
+ button_box_widget = XtCreateManagedWidget("buttons", boxWidgetClass,
+ containing_widget, NULL, 0);
+ create_buttons(button_box_widget);
+ source_name_widget = create_label("srcLabel", "No source file yet.");
+ source_text_widget = create_text_widget(containing_widget, "/dev/null");
+
+ XtRealizeWidget(main_widget);
+ XFlush(display);
+}
+
+/*
+ * If we use an X window, the readline input loop is told to call
+ * this function before reading a character from stdin.
+ */
+/*ARGSUSED*/
+static void
+xgdb_window_hook()
+{
+ register int inmask = 1 << fileno(stdin);
+ register int xmask = 1 << ConnectionNumber(display);
+ register int nfds, pend;
+ int input_rfds;
+ XEvent ev;
+
+ /*
+ * Display our current idea of the `interesting' source file then
+ * loop, dispatching window events until data is available on
+ * stdin. Then return so the input data can be processed.
+ */
+ input_avail = 0;
+ xgdb_display_source();
+
+ input_rfds = 0;
+ while (input_avail == 0 && (input_rfds & inmask) == 0) {
+ pend = XPending(display);
+ if (!pend) {
+ input_rfds = inmask | xmask;
+ nfds = select(32, &input_rfds, 0, 0,
+ (struct timeval *)0);
+ if (nfds == -1 && errno == EINTR)
+ continue;
+ }
+ if (pend || (input_rfds & xmask)) {
+ XNextEvent(display, &ev);
+ XtDispatchEvent(&ev);
+ }
+ }
+}
+
+void
+_initialize_xgdb()
+{
+ extern void (*window_hook) ();
+ extern int inhibit_windows;
+ extern struct cmd_list_element *deletelist;
+
+ if (inhibit_windows)
+ return;
+
+ if (! displayname) {
+ displayname = getenv("DISPLAY");
+ if (! displayname) {
+ fprintf(stderr, "xgdb: no display name\n");
+ inhibit_windows = 1;
+ return;
+ }
+ }
+ xgdb_create_window();
+ window_hook = xgdb_window_hook;
+ add_com("button", class_support, button_command,
+"Add command button to xgdb window. First argument is button\n\
+label, second is command associated with button. Command can\n\
+include printf-like escapes:\n\
+ %s for current selection,\n\
+ %S for first 'word' of current selection,\n\
+ %e for current selection or expression at insertion pt,\n\
+ %E for current selection or expression at insertion pt,\n\
+ %l for current line number,\n\
+ %L for line program stopped at,\n\
+ %f for current file name,\n\
+ %b for current breakpoint number.");
+ add_cmd("button", class_support, button_delete_command,
+"Delete a button from the xgdb window.\n\
+Argument is name of button to be deleted.",
+ &deletelist);
+}
diff --git a/gnu/usr.bin/grep/AUTHORS b/gnu/usr.bin/grep/AUTHORS
new file mode 100644
index 0000000..e3e033b
--- /dev/null
+++ b/gnu/usr.bin/grep/AUTHORS
@@ -0,0 +1,29 @@
+Mike Haertel wrote the main program and the dfa and kwset matchers.
+
+Arthur David Olson contributed the heuristics for finding fixed substrings
+at the end of dfa.c.
+
+Richard Stallman and Karl Berry wrote the regex backtracking matcher.
+
+Henry Spencer wrote the original test suite from which grep's was derived.
+
+Scott Anderson invented the Khadafy test.
+
+David MacKenzie wrote the automatic configuration software use to
+produce the configure script.
+
+Authors of the replacements for standard library routines are identified
+in the corresponding source files.
+
+The idea of using Boyer-Moore type algorithms to quickly filter out
+non-matching text before calling the regexp matcher was originally due
+to James Woods. He also contributed some code to early versions of
+GNU grep.
+
+Finally, I would like to thank Andrew Hume for many fascinating discussions
+of string searching issues over the years. Hume & Sunday's excellent
+paper on fast string searching (AT&T Bell Laboratories CSTR #156)
+describes some of the history of the subject, as well as providing
+exhaustive performance analysis of various implementation alternatives.
+The inner loop of GNU grep is similar to Hume & Sunday's recommended
+"Tuned Boyer Moore" inner loop.
diff --git a/gnu/usr.bin/grep/COPYING b/gnu/usr.bin/grep/COPYING
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/gnu/usr.bin/grep/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/gnu/usr.bin/grep/Makefile b/gnu/usr.bin/grep/Makefile
index 092e523..c8d4915 100644
--- a/gnu/usr.bin/grep/Makefile
+++ b/gnu/usr.bin/grep/Makefile
@@ -1,7 +1,14 @@
-
PROG= grep
-SRCS= dfa.c regex.o grep.o
-CFLAGS+=-DSTDC_HEADERS=1 -DHAVE_UNISTD_H=1
-MLINKS= grep.1 egrep.1
+SRCS= dfa.c grep.c getopt.c kwset.c obstack.c regex.c search.c
+CFLAGS+=-DGREP -DHAVE_STRING_H=1 -DHAVE_SYS_PARAM_H=1 -DHAVE_UNISTD_H=1 \
+ -DHAVE_GETPAGESIZE=1 -DHAVE_MEMCHR=1 -DHAVE_STRERROR=1 \
+ -DHAVE_VALLOC=1
+
+LINKS+= ${BINDIR}/grep ${BINDIR}/egrep \
+ ${BINDIR}/grep ${BINDIR}/fgrep
+MLINKS= grep.1 egrep.1 grep.1 fgrep.1
+
+check: all
+ sh ${.CURDIR}/tests/check.sh ${.CURDIR}/tests
.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/grep/NEWS b/gnu/usr.bin/grep/NEWS
new file mode 100644
index 0000000..eb0b513
--- /dev/null
+++ b/gnu/usr.bin/grep/NEWS
@@ -0,0 +1,35 @@
+Version 2.0:
+
+The most important user visible change is that egrep and fgrep have
+disappeared as separate programs into the single grep program mandated
+by POSIX 1003.2. New options -G, -E, and -F have been added,
+selecting grep, egrep, and fgrep behavior respectively. For
+compatibility with historical practice, hard links named egrep and
+fgrep are also provided. See the manual page for details.
+
+In addition, the regular expression facilities described in Posix
+draft 11.2 are now supported, except for internationalization features
+related to locale-dependent collating sequence information.
+
+There is a new option, -L, which is like -l except it lists
+files which don't contain matches. The reason this option was
+added is because '-l -v' doesn't do what you expect.
+
+Performance has been improved; the amount of improvement is platform
+dependent, but (for example) grep 2.0 typically runs at least 30% faster
+than grep 1.6 on a DECstation using the MIPS compiler. Where possible,
+grep now uses mmap() for file input; on a Sun 4 running SunOS 4.1 this
+may cut system time by as much as half, for a total reduction in running
+time by nearly 50%. On machines that don't use mmap(), the buffering
+code has been rewritten to choose more favorable alignments and buffer
+sizes for read().
+
+Portability has been substantially cleaned up, and an automatic
+configure script is now provided.
+
+The internals have changed in ways too numerous to mention.
+People brave enough to reuse the DFA matcher in other programs
+will now have their bravery amply "rewarded", for the interface
+to that file has been completely changed. Some changes were
+necessary to track the evolution of the regex package, and since
+I was changing it anyway I decided to do a general cleanup.
diff --git a/gnu/usr.bin/grep/PROJECTS b/gnu/usr.bin/grep/PROJECTS
new file mode 100644
index 0000000..67e9a2a
--- /dev/null
+++ b/gnu/usr.bin/grep/PROJECTS
@@ -0,0 +1,15 @@
+Write Texinfo documentation for grep. The manual page would be a good
+place to start, but Info documents are also supposed to contain a
+tutorial and examples.
+
+Fix the DFA matcher to never use exponential space. (Fortunately, these
+cases are rare.)
+
+Improve the performance of the regex backtracking matcher. This matcher
+is agonizingly slow, and is responsible for grep sometimes being slower
+than Unix grep when backreferences are used.
+
+Provide support for the Posix [= =] and [. .] constructs. This is
+difficult because it requires locale-dependent details of the character
+set and collating sequence, but Posix does not standardize any method
+for accessing this information!
diff --git a/gnu/usr.bin/grep/README b/gnu/usr.bin/grep/README
new file mode 100644
index 0000000..bc34a85
--- /dev/null
+++ b/gnu/usr.bin/grep/README
@@ -0,0 +1,28 @@
+This is GNU grep 2.0, the "fastest grep in the west" (we hope). All
+bugs reported in previous releases have been fixed. Many exciting new
+bugs have probably been introduced in this major revision.
+
+GNU grep is provided "as is" with no warranty. The exact terms
+under which you may use and (re)distribute this program are detailed
+in the GNU General Public License, in the file COPYING.
+
+GNU grep is based on a fast lazy-state deterministic matcher (about
+twice as fast as stock Unix egrep) hybridized with a Boyer-Moore-Gosper
+search for a fixed string that eliminates impossible text from being
+considered by the full regexp matcher without necessarily having to
+look at every character. The result is typically many times faster
+than Unix grep or egrep. (Regular expressions containing backreferencing
+will run more slowly, however.)
+
+See the file AUTHORS for a list of authors and other contributors.
+
+See the file INSTALL for compilation and installation instructions.
+
+See the file MANIFEST for a list of files in this distribution.
+
+See the file NEWS for a description of major changes in this release.
+
+See the file PROJECTS if you want to be mentioned in AUTHORS.
+
+Send bug reports to bug-gnu-utils@prep.ai.mit.edu. Be sure to
+include the word "grep" in your Subject: header field.
diff --git a/gnu/usr.bin/grep/dfa.c b/gnu/usr.bin/grep/dfa.c
new file mode 100644
index 0000000..fc649af
--- /dev/null
+++ b/gnu/usr.bin/grep/dfa.c
@@ -0,0 +1,2525 @@
+/* dfa.c - deterministic extended regexp routines for GNU
+ Copyright (C) 1988 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written June, 1988 by Mike Haertel
+ Modified July, 1988 by Arthur David Olson to assist BMG speedups */
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+#include <sys/types.h>
+extern char *calloc(), *malloc(), *realloc();
+extern void free();
+#endif
+
+#if defined(HAVE_STRING_H) || defined(STDC_HEADERS)
+#include <string.h>
+#undef index
+#define index strchr
+#else
+#include <strings.h>
+#endif
+
+#ifndef isgraph
+#define isgraph(C) (isprint(C) && !isspace(C))
+#endif
+
+#ifdef isascii
+#define ISALPHA(C) (isascii(C) && isalpha(C))
+#define ISUPPER(C) (isascii(C) && isupper(C))
+#define ISLOWER(C) (isascii(C) && islower(C))
+#define ISDIGIT(C) (isascii(C) && isdigit(C))
+#define ISXDIGIT(C) (isascii(C) && isxdigit(C))
+#define ISSPACE(C) (isascii(C) && isspace(C))
+#define ISPUNCT(C) (isascii(C) && ispunct(C))
+#define ISALNUM(C) (isascii(C) && isalnum(C))
+#define ISPRINT(C) (isascii(C) && isprint(C))
+#define ISGRAPH(C) (isascii(C) && isgraph(C))
+#define ISCNTRL(C) (isascii(C) && iscntrl(C))
+#else
+#define ISALPHA(C) isalpha(C)
+#define ISUPPER(C) isupper(C)
+#define ISLOWER(C) islower(C)
+#define ISDIGIT(C) isdigit(C)
+#define ISXDIGIT(C) isxdigit(C)
+#define ISSPACE(C) isspace(C)
+#define ISPUNCT(C) ispunct(C)
+#define ISALNUM(C) isalnum(C)
+#define ISPRINT(C) isprint(C)
+#define ISGRAPH(C) isgraph(C)
+#define ISCNTRL(C) iscntrl(C)
+#endif
+
+#include "dfa.h"
+#include "regex.h"
+
+#if __STDC__
+typedef void *ptr_t;
+#else
+typedef char *ptr_t;
+#endif
+
+static void dfamust();
+
+static ptr_t
+xcalloc(n, s)
+ int n;
+ size_t s;
+{
+ ptr_t r = calloc(n, s);
+
+ if (!r)
+ dfaerror("Memory exhausted");
+ return r;
+}
+
+static ptr_t
+xmalloc(n)
+ size_t n;
+{
+ ptr_t r = malloc(n);
+
+ assert(n != 0);
+ if (!r)
+ dfaerror("Memory exhausted");
+ return r;
+}
+
+static ptr_t
+xrealloc(p, n)
+ ptr_t p;
+ size_t n;
+{
+ ptr_t r = realloc(p, n);
+
+ assert(n != 0);
+ if (!r)
+ dfaerror("Memory exhausted");
+ return r;
+}
+
+#define CALLOC(p, t, n) ((p) = (t *) xcalloc((n), sizeof (t)))
+#define MALLOC(p, t, n) ((p) = (t *) xmalloc((n) * sizeof (t)))
+#define REALLOC(p, t, n) ((p) = (t *) xrealloc((ptr_t) (p), (n) * sizeof (t)))
+
+/* Reallocate an array of type t if nalloc is too small for index. */
+#define REALLOC_IF_NECESSARY(p, t, nalloc, index) \
+ if ((index) >= (nalloc)) \
+ { \
+ while ((index) >= (nalloc)) \
+ (nalloc) *= 2; \
+ REALLOC(p, t, nalloc); \
+ }
+
+#ifdef DEBUG
+
+static void
+prtok(t)
+ token t;
+{
+ char *s;
+
+ if (t < 0)
+ fprintf(stderr, "END");
+ else if (t < NOTCHAR)
+ fprintf(stderr, "%c", t);
+ else
+ {
+ switch (t)
+ {
+ case EMPTY: s = "EMPTY"; break;
+ case BACKREF: s = "BACKREF"; break;
+ case BEGLINE: s = "BEGLINE"; break;
+ case ENDLINE: s = "ENDLINE"; break;
+ case BEGWORD: s = "BEGWORD"; break;
+ case ENDWORD: s = "ENDWORD"; break;
+ case LIMWORD: s = "LIMWORD"; break;
+ case NOTLIMWORD: s = "NOTLIMWORD"; break;
+ case QMARK: s = "QMARK"; break;
+ case STAR: s = "STAR"; break;
+ case PLUS: s = "PLUS"; break;
+ case CAT: s = "CAT"; break;
+ case OR: s = "OR"; break;
+ case ORTOP: s = "ORTOP"; break;
+ case LPAREN: s = "LPAREN"; break;
+ case RPAREN: s = "RPAREN"; break;
+ default: s = "CSET"; break;
+ }
+ fprintf(stderr, "%s", s);
+ }
+}
+#endif /* DEBUG */
+
+/* Stuff pertaining to charclasses. */
+
+static int
+tstbit(b, c)
+ int b;
+ charclass c;
+{
+ return c[b / INTBITS] & 1 << b % INTBITS;
+}
+
+static void
+setbit(b, c)
+ int b;
+ charclass c;
+{
+ c[b / INTBITS] |= 1 << b % INTBITS;
+}
+
+static void
+clrbit(b, c)
+ int b;
+ charclass c;
+{
+ c[b / INTBITS] &= ~(1 << b % INTBITS);
+}
+
+static void
+copyset(src, dst)
+ charclass src;
+ charclass dst;
+{
+ int i;
+
+ for (i = 0; i < CHARCLASS_INTS; ++i)
+ dst[i] = src[i];
+}
+
+static void
+zeroset(s)
+ charclass s;
+{
+ int i;
+
+ for (i = 0; i < CHARCLASS_INTS; ++i)
+ s[i] = 0;
+}
+
+static void
+notset(s)
+ charclass s;
+{
+ int i;
+
+ for (i = 0; i < CHARCLASS_INTS; ++i)
+ s[i] = ~s[i];
+}
+
+static int
+equal(s1, s2)
+ charclass s1;
+ charclass s2;
+{
+ int i;
+
+ for (i = 0; i < CHARCLASS_INTS; ++i)
+ if (s1[i] != s2[i])
+ return 0;
+ return 1;
+}
+
+/* A pointer to the current dfa is kept here during parsing. */
+static struct dfa *dfa;
+
+/* Find the index of charclass s in dfa->charclasses, or allocate a new charclass. */
+static int
+charclass_index(s)
+ charclass s;
+{
+ int i;
+
+ for (i = 0; i < dfa->cindex; ++i)
+ if (equal(s, dfa->charclasses[i]))
+ return i;
+ REALLOC_IF_NECESSARY(dfa->charclasses, charclass, dfa->calloc, dfa->cindex);
+ ++dfa->cindex;
+ copyset(s, dfa->charclasses[i]);
+ return i;
+}
+
+/* Syntax bits controlling the behavior of the lexical analyzer. */
+static int syntax_bits, syntax_bits_set;
+
+/* Flag for case-folding letters into sets. */
+static int case_fold;
+
+/* Entry point to set syntax options. */
+void
+dfasyntax(bits, fold)
+ int bits;
+ int fold;
+{
+ syntax_bits_set = 1;
+ syntax_bits = bits;
+ case_fold = fold;
+}
+
+/* Lexical analyzer. All the dross that deals with the obnoxious
+ GNU Regex syntax bits is located here. The poor, suffering
+ reader is referred to the GNU Regex documentation for the
+ meaning of the @#%!@#%^!@ syntax bits. */
+
+static char *lexstart; /* Pointer to beginning of input string. */
+static char *lexptr; /* Pointer to next input character. */
+static lexleft; /* Number of characters remaining. */
+static token lasttok; /* Previous token returned; initially END. */
+static int laststart; /* True if we're separated from beginning or (, |
+ only by zero-width characters. */
+static int parens; /* Count of outstanding left parens. */
+static int minrep, maxrep; /* Repeat counts for {m,n}. */
+
+/* Note that characters become unsigned here. */
+#define FETCH(c, eoferr) \
+ { \
+ if (! lexleft) \
+ if (eoferr != 0) \
+ dfaerror(eoferr); \
+ else \
+ return END; \
+ (c) = (unsigned char) *lexptr++; \
+ --lexleft; \
+ }
+
+#define FUNC(F, P) static int F(c) int c; { return P(c); }
+
+FUNC(is_alpha, ISALPHA)
+FUNC(is_upper, ISUPPER)
+FUNC(is_lower, ISLOWER)
+FUNC(is_digit, ISDIGIT)
+FUNC(is_xdigit, ISXDIGIT)
+FUNC(is_space, ISSPACE)
+FUNC(is_punct, ISPUNCT)
+FUNC(is_alnum, ISALNUM)
+FUNC(is_print, ISPRINT)
+FUNC(is_graph, ISGRAPH)
+FUNC(is_cntrl, ISCNTRL)
+
+/* The following list maps the names of the Posix named character classes
+ to predicate functions that determine whether a given character is in
+ the class. The leading [ has already been eaten by the lexical analyzer. */
+static struct {
+ char *name;
+ int (*pred)();
+} prednames[] = {
+ ":alpha:]", is_alpha,
+ ":upper:]", is_upper,
+ ":lower:]", is_lower,
+ ":digit:]", is_digit,
+ ":xdigit:]", is_xdigit,
+ ":space:]", is_space,
+ ":punct:]", is_punct,
+ ":alnum:]", is_alnum,
+ ":print:]", is_print,
+ ":graph:]", is_graph,
+ ":cntrl:]", is_cntrl,
+ 0
+};
+
+static int
+looking_at(s)
+ char *s;
+{
+ int len;
+
+ len = strlen(s);
+ if (lexleft < len)
+ return 0;
+ return strncmp(s, lexptr, len) == 0;
+}
+
+static token
+lex()
+{
+ token c, c1, c2;
+ int backslash = 0, invert;
+ charclass ccl;
+ int i;
+
+ /* Basic plan: We fetch a character. If it's a backslash,
+ we set the backslash flag and go through the loop again.
+ On the plus side, this avoids having a duplicate of the
+ main switch inside the backslash case. On the minus side,
+ it means that just about every case begins with
+ "if (backslash) ...". */
+ for (i = 0; i < 2; ++i)
+ {
+ FETCH(c, 0);
+ switch (c)
+ {
+ case '\\':
+ if (backslash)
+ goto normal_char;
+ if (lexleft == 0)
+ dfaerror("Unfinished \\ escape");
+ backslash = 1;
+ break;
+
+ case '^':
+ if (backslash)
+ goto normal_char;
+ if (syntax_bits & RE_CONTEXT_INDEP_ANCHORS
+ || lasttok == END
+ || lasttok == LPAREN
+ || lasttok == OR)
+ return lasttok = BEGLINE;
+ goto normal_char;
+
+ case '$':
+ if (backslash)
+ goto normal_char;
+ if (syntax_bits & RE_CONTEXT_INDEP_ANCHORS
+ || lexleft == 0
+ || (syntax_bits & RE_NO_BK_PARENS
+ ? lexleft > 0 && *lexptr == ')'
+ : lexleft > 1 && lexptr[0] == '\\' && lexptr[1] == ')')
+ || (syntax_bits & RE_NO_BK_VBAR
+ ? lexleft > 0 && *lexptr == '|'
+ : lexleft > 1 && lexptr[0] == '\\' && lexptr[1] == '|')
+ || ((syntax_bits & RE_NEWLINE_ALT)
+ && lexleft > 0 && *lexptr == '\n'))
+ return lasttok = ENDLINE;
+ goto normal_char;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (backslash && !(syntax_bits & RE_NO_BK_REFS))
+ {
+ laststart = 0;
+ return lasttok = BACKREF;
+ }
+ goto normal_char;
+
+ case '<':
+ if (backslash)
+ return lasttok = BEGWORD;
+ goto normal_char;
+
+ case '>':
+ if (backslash)
+ return lasttok = ENDWORD;
+ goto normal_char;
+
+ case 'b':
+ if (backslash)
+ return lasttok = LIMWORD;
+ goto normal_char;
+
+ case 'B':
+ if (backslash)
+ return lasttok = NOTLIMWORD;
+ goto normal_char;
+
+ case '?':
+ if (syntax_bits & RE_LIMITED_OPS)
+ goto normal_char;
+ if (backslash != ((syntax_bits & RE_BK_PLUS_QM) != 0))
+ goto normal_char;
+ if (!(syntax_bits & RE_CONTEXT_INDEP_OPS) && laststart)
+ goto normal_char;
+ return lasttok = QMARK;
+
+ case '*':
+ if (backslash)
+ goto normal_char;
+ if (!(syntax_bits & RE_CONTEXT_INDEP_OPS) && laststart)
+ goto normal_char;
+ return lasttok = STAR;
+
+ case '+':
+ if (syntax_bits & RE_LIMITED_OPS)
+ goto normal_char;
+ if (backslash != ((syntax_bits & RE_BK_PLUS_QM) != 0))
+ goto normal_char;
+ if (!(syntax_bits & RE_CONTEXT_INDEP_OPS) && laststart)
+ goto normal_char;
+ return lasttok = PLUS;
+
+ case '{':
+ if (!(syntax_bits & RE_INTERVALS))
+ goto normal_char;
+ if (backslash != ((syntax_bits & RE_NO_BK_BRACES) == 0))
+ goto normal_char;
+ minrep = maxrep = 0;
+ /* Cases:
+ {M} - exact count
+ {M,} - minimum count, maximum is infinity
+ {,M} - 0 through M
+ {M,N} - M through N */
+ FETCH(c, "unfinished repeat count");
+ if (ISDIGIT(c))
+ {
+ minrep = c - '0';
+ for (;;)
+ {
+ FETCH(c, "unfinished repeat count");
+ if (!ISDIGIT(c))
+ break;
+ minrep = 10 * minrep + c - '0';
+ }
+ }
+ else if (c != ',')
+ dfaerror("malformed repeat count");
+ if (c == ',')
+ for (;;)
+ {
+ FETCH(c, "unfinished repeat count");
+ if (!ISDIGIT(c))
+ break;
+ maxrep = 10 * maxrep + c - '0';
+ }
+ else
+ maxrep = minrep;
+ if (!(syntax_bits & RE_NO_BK_BRACES))
+ {
+ if (c != '\\')
+ dfaerror("malformed repeat count");
+ FETCH(c, "unfinished repeat count");
+ }
+ if (c != '}')
+ dfaerror("malformed repeat count");
+ laststart = 0;
+ return lasttok = REPMN;
+
+ case '|':
+ if (syntax_bits & RE_LIMITED_OPS)
+ goto normal_char;
+ if (backslash != ((syntax_bits & RE_NO_BK_VBAR) == 0))
+ goto normal_char;
+ laststart = 1;
+ return lasttok = OR;
+
+ case '\n':
+ if (syntax_bits & RE_LIMITED_OPS
+ || backslash
+ || !(syntax_bits & RE_NEWLINE_ALT))
+ goto normal_char;
+ laststart = 1;
+ return lasttok = OR;
+
+ case '(':
+ if (backslash != ((syntax_bits & RE_NO_BK_PARENS) == 0))
+ goto normal_char;
+ ++parens;
+ laststart = 1;
+ return lasttok = LPAREN;
+
+ case ')':
+ if (backslash != ((syntax_bits & RE_NO_BK_PARENS) == 0))
+ goto normal_char;
+ if (parens == 0 && syntax_bits & RE_UNMATCHED_RIGHT_PAREN_ORD)
+ goto normal_char;
+ --parens;
+ laststart = 0;
+ return lasttok = RPAREN;
+
+ case '.':
+ if (backslash)
+ goto normal_char;
+ zeroset(ccl);
+ notset(ccl);
+ if (!(syntax_bits & RE_DOT_NEWLINE))
+ clrbit('\n', ccl);
+ if (syntax_bits & RE_DOT_NOT_NULL)
+ clrbit('\0', ccl);
+ laststart = 0;
+ return lasttok = CSET + charclass_index(ccl);
+
+ case 'w':
+ case 'W':
+ if (!backslash)
+ goto normal_char;
+ zeroset(ccl);
+ for (c2 = 0; c2 < NOTCHAR; ++c2)
+ if (ISALNUM(c2))
+ setbit(c2, ccl);
+ if (c == 'W')
+ notset(ccl);
+ laststart = 0;
+ return lasttok = CSET + charclass_index(ccl);
+
+ case '[':
+ if (backslash)
+ goto normal_char;
+ zeroset(ccl);
+ FETCH(c, "Unbalanced [");
+ if (c == '^')
+ {
+ FETCH(c, "Unbalanced [");
+ invert = 1;
+ }
+ else
+ invert = 0;
+ do
+ {
+ /* Nobody ever said this had to be fast. :-)
+ Note that if we're looking at some other [:...:]
+ construct, we just treat it as a bunch of ordinary
+ characters. We can do this because we assume
+ regex has checked for syntax errors before
+ dfa is ever called. */
+ if (c == '[' && (syntax_bits & RE_CHAR_CLASSES))
+ for (c1 = 0; prednames[c1].name; ++c1)
+ if (looking_at(prednames[c1].name))
+ {
+ for (c2 = 0; c2 < NOTCHAR; ++c2)
+ if ((*prednames[c1].pred)(c2))
+ setbit(c2, ccl);
+ lexptr += strlen(prednames[c1].name);
+ lexleft -= strlen(prednames[c1].name);
+ FETCH(c1, "Unbalanced [");
+ goto skip;
+ }
+ if (c == '\\' && (syntax_bits & RE_BACKSLASH_ESCAPE_IN_LISTS))
+ FETCH(c, "Unbalanced [");
+ FETCH(c1, "Unbalanced [");
+ if (c1 == '-')
+ {
+ FETCH(c2, "Unbalanced [");
+ if (c2 == ']')
+ {
+ /* In the case [x-], the - is an ordinary hyphen,
+ which is left in c1, the lookahead character. */
+ --lexptr;
+ ++lexleft;
+ c2 = c;
+ }
+ else
+ {
+ if (c2 == '\\'
+ && (syntax_bits & RE_BACKSLASH_ESCAPE_IN_LISTS))
+ FETCH(c2, "Unbalanced [");
+ FETCH(c1, "Unbalanced [");
+ }
+ }
+ else
+ c2 = c;
+ while (c <= c2)
+ {
+ setbit(c, ccl);
+ if (case_fold)
+ if (ISUPPER(c))
+ setbit(tolower(c), ccl);
+ else if (ISLOWER(c))
+ setbit(toupper(c), ccl);
+ ++c;
+ }
+ skip:
+ ;
+ }
+ while ((c = c1) != ']');
+ if (invert)
+ {
+ notset(ccl);
+ if (syntax_bits & RE_HAT_LISTS_NOT_NEWLINE)
+ clrbit('\n', ccl);
+ }
+ laststart = 0;
+ return lasttok = CSET + charclass_index(ccl);
+
+ default:
+ normal_char:
+ laststart = 0;
+ if (case_fold && ISALPHA(c))
+ {
+ zeroset(ccl);
+ setbit(c, ccl);
+ if (isupper(c))
+ setbit(tolower(c), ccl);
+ else
+ setbit(toupper(c), ccl);
+ return lasttok = CSET + charclass_index(ccl);
+ }
+ return c;
+ }
+ }
+
+ /* The above loop should consume at most a backslash
+ and some other character. */
+ abort();
+}
+
+/* Recursive descent parser for regular expressions. */
+
+static token tok; /* Lookahead token. */
+static depth; /* Current depth of a hypothetical stack
+ holding deferred productions. This is
+ used to determine the depth that will be
+ required of the real stack later on in
+ dfaanalyze(). */
+
+/* Add the given token to the parse tree, maintaining the depth count and
+ updating the maximum depth if necessary. */
+static void
+addtok(t)
+ token t;
+{
+ REALLOC_IF_NECESSARY(dfa->tokens, token, dfa->talloc, dfa->tindex);
+ dfa->tokens[dfa->tindex++] = t;
+
+ switch (t)
+ {
+ case QMARK:
+ case STAR:
+ case PLUS:
+ break;
+
+ case CAT:
+ case OR:
+ case ORTOP:
+ --depth;
+ break;
+
+ default:
+ ++dfa->nleaves;
+ case EMPTY:
+ ++depth;
+ break;
+ }
+ if (depth > dfa->depth)
+ dfa->depth = depth;
+}
+
+/* The grammar understood by the parser is as follows.
+
+ regexp:
+ regexp OR branch
+ branch
+
+ branch:
+ branch closure
+ closure
+
+ closure:
+ closure QMARK
+ closure STAR
+ closure PLUS
+ atom
+
+ atom:
+ <normal character>
+ CSET
+ BACKREF
+ BEGLINE
+ ENDLINE
+ BEGWORD
+ ENDWORD
+ LIMWORD
+ NOTLIMWORD
+ <empty>
+
+ The parser builds a parse tree in postfix form in an array of tokens. */
+
+#if __STDC__
+static void regexp(int);
+#else
+static void regexp();
+#endif
+
+static void
+atom()
+{
+ if ((tok >= 0 && tok < NOTCHAR) || tok >= CSET || tok == BACKREF
+ || tok == BEGLINE || tok == ENDLINE || tok == BEGWORD
+ || tok == ENDWORD || tok == LIMWORD || tok == NOTLIMWORD)
+ {
+ addtok(tok);
+ tok = lex();
+ }
+ else if (tok == LPAREN)
+ {
+ tok = lex();
+ regexp(0);
+ if (tok != RPAREN)
+ dfaerror("Unbalanced (");
+ tok = lex();
+ }
+ else
+ addtok(EMPTY);
+}
+
+/* Return the number of tokens in the given subexpression. */
+static int
+nsubtoks(tindex)
+{
+ int ntoks1;
+
+ switch (dfa->tokens[tindex - 1])
+ {
+ default:
+ return 1;
+ case QMARK:
+ case STAR:
+ case PLUS:
+ return 1 + nsubtoks(tindex - 1);
+ case CAT:
+ case OR:
+ case ORTOP:
+ ntoks1 = nsubtoks(tindex - 1);
+ return 1 + ntoks1 + nsubtoks(tindex - 1 - ntoks1);
+ }
+}
+
+/* Copy the given subexpression to the top of the tree. */
+static void
+copytoks(tindex, ntokens)
+ int tindex, ntokens;
+{
+ int i;
+
+ for (i = 0; i < ntokens; ++i)
+ addtok(dfa->tokens[tindex + i]);
+}
+
+static void
+closure()
+{
+ int tindex, ntokens, i;
+
+ atom();
+ while (tok == QMARK || tok == STAR || tok == PLUS || tok == REPMN)
+ if (tok == REPMN)
+ {
+ ntokens = nsubtoks(dfa->tindex);
+ tindex = dfa->tindex - ntokens;
+ if (maxrep == 0)
+ addtok(PLUS);
+ if (minrep == 0)
+ addtok(QMARK);
+ for (i = 1; i < minrep; ++i)
+ {
+ copytoks(tindex, ntokens);
+ addtok(CAT);
+ }
+ for (; i < maxrep; ++i)
+ {
+ copytoks(tindex, ntokens);
+ addtok(QMARK);
+ addtok(CAT);
+ }
+ tok = lex();
+ }
+ else
+ {
+ addtok(tok);
+ tok = lex();
+ }
+}
+
+static void
+branch()
+{
+ closure();
+ while (tok != RPAREN && tok != OR && tok >= 0)
+ {
+ closure();
+ addtok(CAT);
+ }
+}
+
+static void
+regexp(toplevel)
+ int toplevel;
+{
+ branch();
+ while (tok == OR)
+ {
+ tok = lex();
+ branch();
+ if (toplevel)
+ addtok(ORTOP);
+ else
+ addtok(OR);
+ }
+}
+
+/* Main entry point for the parser. S is a string to be parsed, len is the
+ length of the string, so s can include NUL characters. D is a pointer to
+ the struct dfa to parse into. */
+void
+dfaparse(s, len, d)
+ char *s;
+ size_t len;
+ struct dfa *d;
+
+{
+ dfa = d;
+ lexstart = lexptr = s;
+ lexleft = len;
+ lasttok = END;
+ laststart = 1;
+ parens = 0;
+
+ if (! syntax_bits_set)
+ dfaerror("No syntax specified");
+
+ tok = lex();
+ depth = d->depth;
+
+ regexp(1);
+
+ if (tok != END)
+ dfaerror("Unbalanced )");
+
+ addtok(END - d->nregexps);
+ addtok(CAT);
+
+ if (d->nregexps)
+ addtok(ORTOP);
+
+ ++d->nregexps;
+}
+
+/* Some primitives for operating on sets of positions. */
+
+/* Copy one set to another; the destination must be large enough. */
+static void
+copy(src, dst)
+ position_set *src;
+ position_set *dst;
+{
+ int i;
+
+ for (i = 0; i < src->nelem; ++i)
+ dst->elems[i] = src->elems[i];
+ dst->nelem = src->nelem;
+}
+
+/* Insert a position in a set. Position sets are maintained in sorted
+ order according to index. If position already exists in the set with
+ the same index then their constraints are logically or'd together.
+ S->elems must point to an array large enough to hold the resulting set. */
+static void
+insert(p, s)
+ position p;
+ position_set *s;
+{
+ int i;
+ position t1, t2;
+
+ for (i = 0; i < s->nelem && p.index < s->elems[i].index; ++i)
+ ;
+ if (i < s->nelem && p.index == s->elems[i].index)
+ s->elems[i].constraint |= p.constraint;
+ else
+ {
+ t1 = p;
+ ++s->nelem;
+ while (i < s->nelem)
+ {
+ t2 = s->elems[i];
+ s->elems[i++] = t1;
+ t1 = t2;
+ }
+ }
+}
+
+/* Merge two sets of positions into a third. The result is exactly as if
+ the positions of both sets were inserted into an initially empty set. */
+static void
+merge(s1, s2, m)
+ position_set *s1;
+ position_set *s2;
+ position_set *m;
+{
+ int i = 0, j = 0;
+
+ m->nelem = 0;
+ while (i < s1->nelem && j < s2->nelem)
+ if (s1->elems[i].index > s2->elems[j].index)
+ m->elems[m->nelem++] = s1->elems[i++];
+ else if (s1->elems[i].index < s2->elems[j].index)
+ m->elems[m->nelem++] = s2->elems[j++];
+ else
+ {
+ m->elems[m->nelem] = s1->elems[i++];
+ m->elems[m->nelem++].constraint |= s2->elems[j++].constraint;
+ }
+ while (i < s1->nelem)
+ m->elems[m->nelem++] = s1->elems[i++];
+ while (j < s2->nelem)
+ m->elems[m->nelem++] = s2->elems[j++];
+}
+
+/* Delete a position from a set. */
+static void
+delete(p, s)
+ position p;
+ position_set *s;
+{
+ int i;
+
+ for (i = 0; i < s->nelem; ++i)
+ if (p.index == s->elems[i].index)
+ break;
+ if (i < s->nelem)
+ for (--s->nelem; i < s->nelem; ++i)
+ s->elems[i] = s->elems[i + 1];
+}
+
+/* Find the index of the state corresponding to the given position set with
+ the given preceding context, or create a new state if there is no such
+ state. Newline and letter tell whether we got here on a newline or
+ letter, respectively. */
+static int
+state_index(d, s, newline, letter)
+ struct dfa *d;
+ position_set *s;
+ int newline;
+ int letter;
+{
+ int hash = 0;
+ int constraint;
+ int i, j;
+
+ newline = newline ? 1 : 0;
+ letter = letter ? 1 : 0;
+
+ for (i = 0; i < s->nelem; ++i)
+ hash ^= s->elems[i].index + s->elems[i].constraint;
+
+ /* Try to find a state that exactly matches the proposed one. */
+ for (i = 0; i < d->sindex; ++i)
+ {
+ if (hash != d->states[i].hash || s->nelem != d->states[i].elems.nelem
+ || newline != d->states[i].newline || letter != d->states[i].letter)
+ continue;
+ for (j = 0; j < s->nelem; ++j)
+ if (s->elems[j].constraint
+ != d->states[i].elems.elems[j].constraint
+ || s->elems[j].index != d->states[i].elems.elems[j].index)
+ break;
+ if (j == s->nelem)
+ return i;
+ }
+
+ /* We'll have to create a new state. */
+ REALLOC_IF_NECESSARY(d->states, dfa_state, d->salloc, d->sindex);
+ d->states[i].hash = hash;
+ MALLOC(d->states[i].elems.elems, position, s->nelem);
+ copy(s, &d->states[i].elems);
+ d->states[i].newline = newline;
+ d->states[i].letter = letter;
+ d->states[i].backref = 0;
+ d->states[i].constraint = 0;
+ d->states[i].first_end = 0;
+ for (j = 0; j < s->nelem; ++j)
+ if (d->tokens[s->elems[j].index] < 0)
+ {
+ constraint = s->elems[j].constraint;
+ if (SUCCEEDS_IN_CONTEXT(constraint, newline, 0, letter, 0)
+ || SUCCEEDS_IN_CONTEXT(constraint, newline, 0, letter, 1)
+ || SUCCEEDS_IN_CONTEXT(constraint, newline, 1, letter, 0)
+ || SUCCEEDS_IN_CONTEXT(constraint, newline, 1, letter, 1))
+ d->states[i].constraint |= constraint;
+ if (! d->states[i].first_end)
+ d->states[i].first_end = d->tokens[s->elems[j].index];
+ }
+ else if (d->tokens[s->elems[j].index] == BACKREF)
+ {
+ d->states[i].constraint = NO_CONSTRAINT;
+ d->states[i].backref = 1;
+ }
+
+ ++d->sindex;
+
+ return i;
+}
+
+/* Find the epsilon closure of a set of positions. If any position of the set
+ contains a symbol that matches the empty string in some context, replace
+ that position with the elements of its follow labeled with an appropriate
+ constraint. Repeat exhaustively until no funny positions are left.
+ S->elems must be large enough to hold the result. */
+void
+epsclosure(s, d)
+ position_set *s;
+ struct dfa *d;
+{
+ int i, j;
+ int *visited;
+ position p, old;
+
+ MALLOC(visited, int, d->tindex);
+ for (i = 0; i < d->tindex; ++i)
+ visited[i] = 0;
+
+ for (i = 0; i < s->nelem; ++i)
+ if (d->tokens[s->elems[i].index] >= NOTCHAR
+ && d->tokens[s->elems[i].index] != BACKREF
+ && d->tokens[s->elems[i].index] < CSET)
+ {
+ old = s->elems[i];
+ p.constraint = old.constraint;
+ delete(s->elems[i], s);
+ if (visited[old.index])
+ {
+ --i;
+ continue;
+ }
+ visited[old.index] = 1;
+ switch (d->tokens[old.index])
+ {
+ case BEGLINE:
+ p.constraint &= BEGLINE_CONSTRAINT;
+ break;
+ case ENDLINE:
+ p.constraint &= ENDLINE_CONSTRAINT;
+ break;
+ case BEGWORD:
+ p.constraint &= BEGWORD_CONSTRAINT;
+ break;
+ case ENDWORD:
+ p.constraint &= ENDWORD_CONSTRAINT;
+ break;
+ case LIMWORD:
+ p.constraint &= LIMWORD_CONSTRAINT;
+ break;
+ case NOTLIMWORD:
+ p.constraint &= NOTLIMWORD_CONSTRAINT;
+ break;
+ default:
+ break;
+ }
+ for (j = 0; j < d->follows[old.index].nelem; ++j)
+ {
+ p.index = d->follows[old.index].elems[j].index;
+ insert(p, s);
+ }
+ /* Force rescan to start at the beginning. */
+ i = -1;
+ }
+
+ free(visited);
+}
+
+/* Perform bottom-up analysis on the parse tree, computing various functions.
+ Note that at this point, we're pretending constructs like \< are real
+ characters rather than constraints on what can follow them.
+
+ Nullable: A node is nullable if it is at the root of a regexp that can
+ match the empty string.
+ * EMPTY leaves are nullable.
+ * No other leaf is nullable.
+ * A QMARK or STAR node is nullable.
+ * A PLUS node is nullable if its argument is nullable.
+ * A CAT node is nullable if both its arguments are nullable.
+ * An OR node is nullable if either argument is nullable.
+
+ Firstpos: The firstpos of a node is the set of positions (nonempty leaves)
+ that could correspond to the first character of a string matching the
+ regexp rooted at the given node.
+ * EMPTY leaves have empty firstpos.
+ * The firstpos of a nonempty leaf is that leaf itself.
+ * The firstpos of a QMARK, STAR, or PLUS node is the firstpos of its
+ argument.
+ * The firstpos of a CAT node is the firstpos of the left argument, union
+ the firstpos of the right if the left argument is nullable.
+ * The firstpos of an OR node is the union of firstpos of each argument.
+
+ Lastpos: The lastpos of a node is the set of positions that could
+ correspond to the last character of a string matching the regexp at
+ the given node.
+ * EMPTY leaves have empty lastpos.
+ * The lastpos of a nonempty leaf is that leaf itself.
+ * The lastpos of a QMARK, STAR, or PLUS node is the lastpos of its
+ argument.
+ * The lastpos of a CAT node is the lastpos of its right argument, union
+ the lastpos of the left if the right argument is nullable.
+ * The lastpos of an OR node is the union of the lastpos of each argument.
+
+ Follow: The follow of a position is the set of positions that could
+ correspond to the character following a character matching the node in
+ a string matching the regexp. At this point we consider special symbols
+ that match the empty string in some context to be just normal characters.
+ Later, if we find that a special symbol is in a follow set, we will
+ replace it with the elements of its follow, labeled with an appropriate
+ constraint.
+ * Every node in the firstpos of the argument of a STAR or PLUS node is in
+ the follow of every node in the lastpos.
+ * Every node in the firstpos of the second argument of a CAT node is in
+ the follow of every node in the lastpos of the first argument.
+
+ Because of the postfix representation of the parse tree, the depth-first
+ analysis is conveniently done by a linear scan with the aid of a stack.
+ Sets are stored as arrays of the elements, obeying a stack-like allocation
+ scheme; the number of elements in each set deeper in the stack can be
+ used to determine the address of a particular set's array. */
+void
+dfaanalyze(d, searchflag)
+ struct dfa *d;
+ int searchflag;
+{
+ int *nullable; /* Nullable stack. */
+ int *nfirstpos; /* Element count stack for firstpos sets. */
+ position *firstpos; /* Array where firstpos elements are stored. */
+ int *nlastpos; /* Element count stack for lastpos sets. */
+ position *lastpos; /* Array where lastpos elements are stored. */
+ int *nalloc; /* Sizes of arrays allocated to follow sets. */
+ position_set tmp; /* Temporary set for merging sets. */
+ position_set merged; /* Result of merging sets. */
+ int wants_newline; /* True if some position wants newline info. */
+ int *o_nullable;
+ int *o_nfirst, *o_nlast;
+ position *o_firstpos, *o_lastpos;
+ int i, j;
+ position *pos;
+
+#ifdef DEBUG
+ fprintf(stderr, "dfaanalyze:\n");
+ for (i = 0; i < d->tindex; ++i)
+ {
+ fprintf(stderr, " %d:", i);
+ prtok(d->tokens[i]);
+ }
+ putc('\n', stderr);
+#endif
+
+ d->searchflag = searchflag;
+
+ MALLOC(nullable, int, d->depth);
+ o_nullable = nullable;
+ MALLOC(nfirstpos, int, d->depth);
+ o_nfirst = nfirstpos;
+ MALLOC(firstpos, position, d->nleaves);
+ o_firstpos = firstpos, firstpos += d->nleaves;
+ MALLOC(nlastpos, int, d->depth);
+ o_nlast = nlastpos;
+ MALLOC(lastpos, position, d->nleaves);
+ o_lastpos = lastpos, lastpos += d->nleaves;
+ MALLOC(nalloc, int, d->tindex);
+ for (i = 0; i < d->tindex; ++i)
+ nalloc[i] = 0;
+ MALLOC(merged.elems, position, d->nleaves);
+
+ CALLOC(d->follows, position_set, d->tindex);
+
+ for (i = 0; i < d->tindex; ++i)
+#ifdef DEBUG
+ { /* Nonsyntactic #ifdef goo... */
+#endif
+ switch (d->tokens[i])
+ {
+ case EMPTY:
+ /* The empty set is nullable. */
+ *nullable++ = 1;
+
+ /* The firstpos and lastpos of the empty leaf are both empty. */
+ *nfirstpos++ = *nlastpos++ = 0;
+ break;
+
+ case STAR:
+ case PLUS:
+ /* Every element in the firstpos of the argument is in the follow
+ of every element in the lastpos. */
+ tmp.nelem = nfirstpos[-1];
+ tmp.elems = firstpos;
+ pos = lastpos;
+ for (j = 0; j < nlastpos[-1]; ++j)
+ {
+ merge(&tmp, &d->follows[pos[j].index], &merged);
+ REALLOC_IF_NECESSARY(d->follows[pos[j].index].elems, position,
+ nalloc[pos[j].index], merged.nelem - 1);
+ copy(&merged, &d->follows[pos[j].index]);
+ }
+
+ case QMARK:
+ /* A QMARK or STAR node is automatically nullable. */
+ if (d->tokens[i] != PLUS)
+ nullable[-1] = 1;
+ break;
+
+ case CAT:
+ /* Every element in the firstpos of the second argument is in the
+ follow of every element in the lastpos of the first argument. */
+ tmp.nelem = nfirstpos[-1];
+ tmp.elems = firstpos;
+ pos = lastpos + nlastpos[-1];
+ for (j = 0; j < nlastpos[-2]; ++j)
+ {
+ merge(&tmp, &d->follows[pos[j].index], &merged);
+ REALLOC_IF_NECESSARY(d->follows[pos[j].index].elems, position,
+ nalloc[pos[j].index], merged.nelem - 1);
+ copy(&merged, &d->follows[pos[j].index]);
+ }
+
+ /* The firstpos of a CAT node is the firstpos of the first argument,
+ union that of the second argument if the first is nullable. */
+ if (nullable[-2])
+ nfirstpos[-2] += nfirstpos[-1];
+ else
+ firstpos += nfirstpos[-1];
+ --nfirstpos;
+
+ /* The lastpos of a CAT node is the lastpos of the second argument,
+ union that of the first argument if the second is nullable. */
+ if (nullable[-1])
+ nlastpos[-2] += nlastpos[-1];
+ else
+ {
+ pos = lastpos + nlastpos[-2];
+ for (j = nlastpos[-1] - 1; j >= 0; --j)
+ pos[j] = lastpos[j];
+ lastpos += nlastpos[-2];
+ nlastpos[-2] = nlastpos[-1];
+ }
+ --nlastpos;
+
+ /* A CAT node is nullable if both arguments are nullable. */
+ nullable[-2] = nullable[-1] && nullable[-2];
+ --nullable;
+ break;
+
+ case OR:
+ case ORTOP:
+ /* The firstpos is the union of the firstpos of each argument. */
+ nfirstpos[-2] += nfirstpos[-1];
+ --nfirstpos;
+
+ /* The lastpos is the union of the lastpos of each argument. */
+ nlastpos[-2] += nlastpos[-1];
+ --nlastpos;
+
+ /* An OR node is nullable if either argument is nullable. */
+ nullable[-2] = nullable[-1] || nullable[-2];
+ --nullable;
+ break;
+
+ default:
+ /* Anything else is a nonempty position. (Note that special
+ constructs like \< are treated as nonempty strings here;
+ an "epsilon closure" effectively makes them nullable later.
+ Backreferences have to get a real position so we can detect
+ transitions on them later. But they are nullable. */
+ *nullable++ = d->tokens[i] == BACKREF;
+
+ /* This position is in its own firstpos and lastpos. */
+ *nfirstpos++ = *nlastpos++ = 1;
+ --firstpos, --lastpos;
+ firstpos->index = lastpos->index = i;
+ firstpos->constraint = lastpos->constraint = NO_CONSTRAINT;
+
+ /* Allocate the follow set for this position. */
+ nalloc[i] = 1;
+ MALLOC(d->follows[i].elems, position, nalloc[i]);
+ break;
+ }
+#ifdef DEBUG
+ /* ... balance the above nonsyntactic #ifdef goo... */
+ fprintf(stderr, "node %d:", i);
+ prtok(d->tokens[i]);
+ putc('\n', stderr);
+ fprintf(stderr, nullable[-1] ? " nullable: yes\n" : " nullable: no\n");
+ fprintf(stderr, " firstpos:");
+ for (j = nfirstpos[-1] - 1; j >= 0; --j)
+ {
+ fprintf(stderr, " %d:", firstpos[j].index);
+ prtok(d->tokens[firstpos[j].index]);
+ }
+ fprintf(stderr, "\n lastpos:");
+ for (j = nlastpos[-1] - 1; j >= 0; --j)
+ {
+ fprintf(stderr, " %d:", lastpos[j].index);
+ prtok(d->tokens[lastpos[j].index]);
+ }
+ putc('\n', stderr);
+ }
+#endif
+
+ /* For each follow set that is the follow set of a real position, replace
+ it with its epsilon closure. */
+ for (i = 0; i < d->tindex; ++i)
+ if (d->tokens[i] < NOTCHAR || d->tokens[i] == BACKREF
+ || d->tokens[i] >= CSET)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "follows(%d:", i);
+ prtok(d->tokens[i]);
+ fprintf(stderr, "):");
+ for (j = d->follows[i].nelem - 1; j >= 0; --j)
+ {
+ fprintf(stderr, " %d:", d->follows[i].elems[j].index);
+ prtok(d->tokens[d->follows[i].elems[j].index]);
+ }
+ putc('\n', stderr);
+#endif
+ copy(&d->follows[i], &merged);
+ epsclosure(&merged, d);
+ if (d->follows[i].nelem < merged.nelem)
+ REALLOC(d->follows[i].elems, position, merged.nelem);
+ copy(&merged, &d->follows[i]);
+ }
+
+ /* Get the epsilon closure of the firstpos of the regexp. The result will
+ be the set of positions of state 0. */
+ merged.nelem = 0;
+ for (i = 0; i < nfirstpos[-1]; ++i)
+ insert(firstpos[i], &merged);
+ epsclosure(&merged, d);
+
+ /* Check if any of the positions of state 0 will want newline context. */
+ wants_newline = 0;
+ for (i = 0; i < merged.nelem; ++i)
+ if (PREV_NEWLINE_DEPENDENT(merged.elems[i].constraint))
+ wants_newline = 1;
+
+ /* Build the initial state. */
+ d->salloc = 1;
+ d->sindex = 0;
+ MALLOC(d->states, dfa_state, d->salloc);
+ state_index(d, &merged, wants_newline, 0);
+
+ free(o_nullable);
+ free(o_nfirst);
+ free(o_firstpos);
+ free(o_nlast);
+ free(o_lastpos);
+ free(nalloc);
+ free(merged.elems);
+}
+
+/* Find, for each character, the transition out of state s of d, and store
+ it in the appropriate slot of trans.
+
+ We divide the positions of s into groups (positions can appear in more
+ than one group). Each group is labeled with a set of characters that
+ every position in the group matches (taking into account, if necessary,
+ preceding context information of s). For each group, find the union
+ of the its elements' follows. This set is the set of positions of the
+ new state. For each character in the group's label, set the transition
+ on this character to be to a state corresponding to the set's positions,
+ and its associated backward context information, if necessary.
+
+ If we are building a searching matcher, we include the positions of state
+ 0 in every state.
+
+ The collection of groups is constructed by building an equivalence-class
+ partition of the positions of s.
+
+ For each position, find the set of characters C that it matches. Eliminate
+ any characters from C that fail on grounds of backward context.
+
+ Search through the groups, looking for a group whose label L has nonempty
+ intersection with C. If L - C is nonempty, create a new group labeled
+ L - C and having the same positions as the current group, and set L to
+ the intersection of L and C. Insert the position in this group, set
+ C = C - L, and resume scanning.
+
+ If after comparing with every group there are characters remaining in C,
+ create a new group labeled with the characters of C and insert this
+ position in that group. */
+void
+dfastate(s, d, trans)
+ int s;
+ struct dfa *d;
+ int trans[];
+{
+ position_set grps[NOTCHAR]; /* As many as will ever be needed. */
+ charclass labels[NOTCHAR]; /* Labels corresponding to the groups. */
+ int ngrps = 0; /* Number of groups actually used. */
+ position pos; /* Current position being considered. */
+ charclass matches; /* Set of matching characters. */
+ int matchesf; /* True if matches is nonempty. */
+ charclass intersect; /* Intersection with some label set. */
+ int intersectf; /* True if intersect is nonempty. */
+ charclass leftovers; /* Stuff in the label that didn't match. */
+ int leftoversf; /* True if leftovers is nonempty. */
+ static charclass letters; /* Set of characters considered letters. */
+ static charclass newline; /* Set of characters that aren't newline. */
+ position_set follows; /* Union of the follows of some group. */
+ position_set tmp; /* Temporary space for merging sets. */
+ int state; /* New state. */
+ int wants_newline; /* New state wants to know newline context. */
+ int state_newline; /* New state on a newline transition. */
+ int wants_letter; /* New state wants to know letter context. */
+ int state_letter; /* New state on a letter transition. */
+ static initialized; /* Flag for static initialization. */
+ int i, j, k;
+
+ /* Initialize the set of letters, if necessary. */
+ if (! initialized)
+ {
+ initialized = 1;
+ for (i = 0; i < NOTCHAR; ++i)
+ if (ISALNUM(i))
+ setbit(i, letters);
+ setbit('\n', newline);
+ }
+
+ zeroset(matches);
+
+ for (i = 0; i < d->states[s].elems.nelem; ++i)
+ {
+ pos = d->states[s].elems.elems[i];
+ if (d->tokens[pos.index] >= 0 && d->tokens[pos.index] < NOTCHAR)
+ setbit(d->tokens[pos.index], matches);
+ else if (d->tokens[pos.index] >= CSET)
+ copyset(d->charclasses[d->tokens[pos.index] - CSET], matches);
+ else
+ continue;
+
+ /* Some characters may need to be eliminated from matches because
+ they fail in the current context. */
+ if (pos.constraint != 0xFF)
+ {
+ if (! MATCHES_NEWLINE_CONTEXT(pos.constraint,
+ d->states[s].newline, 1))
+ clrbit('\n', matches);
+ if (! MATCHES_NEWLINE_CONTEXT(pos.constraint,
+ d->states[s].newline, 0))
+ for (j = 0; j < CHARCLASS_INTS; ++j)
+ matches[j] &= newline[j];
+ if (! MATCHES_LETTER_CONTEXT(pos.constraint,
+ d->states[s].letter, 1))
+ for (j = 0; j < CHARCLASS_INTS; ++j)
+ matches[j] &= ~letters[j];
+ if (! MATCHES_LETTER_CONTEXT(pos.constraint,
+ d->states[s].letter, 0))
+ for (j = 0; j < CHARCLASS_INTS; ++j)
+ matches[j] &= letters[j];
+
+ /* If there are no characters left, there's no point in going on. */
+ for (j = 0; j < CHARCLASS_INTS && !matches[j]; ++j)
+ ;
+ if (j == CHARCLASS_INTS)
+ continue;
+ }
+
+ for (j = 0; j < ngrps; ++j)
+ {
+ /* If matches contains a single character only, and the current
+ group's label doesn't contain that character, go on to the
+ next group. */
+ if (d->tokens[pos.index] >= 0 && d->tokens[pos.index] < NOTCHAR
+ && !tstbit(d->tokens[pos.index], labels[j]))
+ continue;
+
+ /* Check if this group's label has a nonempty intersection with
+ matches. */
+ intersectf = 0;
+ for (k = 0; k < CHARCLASS_INTS; ++k)
+ (intersect[k] = matches[k] & labels[j][k]) ? intersectf = 1 : 0;
+ if (! intersectf)
+ continue;
+
+ /* It does; now find the set differences both ways. */
+ leftoversf = matchesf = 0;
+ for (k = 0; k < CHARCLASS_INTS; ++k)
+ {
+ /* Even an optimizing compiler can't know this for sure. */
+ int match = matches[k], label = labels[j][k];
+
+ (leftovers[k] = ~match & label) ? leftoversf = 1 : 0;
+ (matches[k] = match & ~label) ? matchesf = 1 : 0;
+ }
+
+ /* If there were leftovers, create a new group labeled with them. */
+ if (leftoversf)
+ {
+ copyset(leftovers, labels[ngrps]);
+ copyset(intersect, labels[j]);
+ MALLOC(grps[ngrps].elems, position, d->nleaves);
+ copy(&grps[j], &grps[ngrps]);
+ ++ngrps;
+ }
+
+ /* Put the position in the current group. Note that there is no
+ reason to call insert() here. */
+ grps[j].elems[grps[j].nelem++] = pos;
+
+ /* If every character matching the current position has been
+ accounted for, we're done. */
+ if (! matchesf)
+ break;
+ }
+
+ /* If we've passed the last group, and there are still characters
+ unaccounted for, then we'll have to create a new group. */
+ if (j == ngrps)
+ {
+ copyset(matches, labels[ngrps]);
+ zeroset(matches);
+ MALLOC(grps[ngrps].elems, position, d->nleaves);
+ grps[ngrps].nelem = 1;
+ grps[ngrps].elems[0] = pos;
+ ++ngrps;
+ }
+ }
+
+ MALLOC(follows.elems, position, d->nleaves);
+ MALLOC(tmp.elems, position, d->nleaves);
+
+ /* If we are a searching matcher, the default transition is to a state
+ containing the positions of state 0, otherwise the default transition
+ is to fail miserably. */
+ if (d->searchflag)
+ {
+ wants_newline = 0;
+ wants_letter = 0;
+ for (i = 0; i < d->states[0].elems.nelem; ++i)
+ {
+ if (PREV_NEWLINE_DEPENDENT(d->states[0].elems.elems[i].constraint))
+ wants_newline = 1;
+ if (PREV_LETTER_DEPENDENT(d->states[0].elems.elems[i].constraint))
+ wants_letter = 1;
+ }
+ copy(&d->states[0].elems, &follows);
+ state = state_index(d, &follows, 0, 0);
+ if (wants_newline)
+ state_newline = state_index(d, &follows, 1, 0);
+ else
+ state_newline = state;
+ if (wants_letter)
+ state_letter = state_index(d, &follows, 0, 1);
+ else
+ state_letter = state;
+ for (i = 0; i < NOTCHAR; ++i)
+ if (i == '\n')
+ trans[i] = state_newline;
+ else if (ISALNUM(i))
+ trans[i] = state_letter;
+ else
+ trans[i] = state;
+ }
+ else
+ for (i = 0; i < NOTCHAR; ++i)
+ trans[i] = -1;
+
+ for (i = 0; i < ngrps; ++i)
+ {
+ follows.nelem = 0;
+
+ /* Find the union of the follows of the positions of the group.
+ This is a hideously inefficient loop. Fix it someday. */
+ for (j = 0; j < grps[i].nelem; ++j)
+ for (k = 0; k < d->follows[grps[i].elems[j].index].nelem; ++k)
+ insert(d->follows[grps[i].elems[j].index].elems[k], &follows);
+
+ /* If we are building a searching matcher, throw in the positions
+ of state 0 as well. */
+ if (d->searchflag)
+ for (j = 0; j < d->states[0].elems.nelem; ++j)
+ insert(d->states[0].elems.elems[j], &follows);
+
+ /* Find out if the new state will want any context information. */
+ wants_newline = 0;
+ if (tstbit('\n', labels[i]))
+ for (j = 0; j < follows.nelem; ++j)
+ if (PREV_NEWLINE_DEPENDENT(follows.elems[j].constraint))
+ wants_newline = 1;
+
+ wants_letter = 0;
+ for (j = 0; j < CHARCLASS_INTS; ++j)
+ if (labels[i][j] & letters[j])
+ break;
+ if (j < CHARCLASS_INTS)
+ for (j = 0; j < follows.nelem; ++j)
+ if (PREV_LETTER_DEPENDENT(follows.elems[j].constraint))
+ wants_letter = 1;
+
+ /* Find the state(s) corresponding to the union of the follows. */
+ state = state_index(d, &follows, 0, 0);
+ if (wants_newline)
+ state_newline = state_index(d, &follows, 1, 0);
+ else
+ state_newline = state;
+ if (wants_letter)
+ state_letter = state_index(d, &follows, 0, 1);
+ else
+ state_letter = state;
+
+ /* Set the transitions for each character in the current label. */
+ for (j = 0; j < CHARCLASS_INTS; ++j)
+ for (k = 0; k < INTBITS; ++k)
+ if (labels[i][j] & 1 << k)
+ {
+ int c = j * INTBITS + k;
+
+ if (c == '\n')
+ trans[c] = state_newline;
+ else if (ISALNUM(c))
+ trans[c] = state_letter;
+ else if (c < NOTCHAR)
+ trans[c] = state;
+ }
+ }
+
+ for (i = 0; i < ngrps; ++i)
+ free(grps[i].elems);
+ free(follows.elems);
+ free(tmp.elems);
+}
+
+/* Some routines for manipulating a compiled dfa's transition tables.
+ Each state may or may not have a transition table; if it does, and it
+ is a non-accepting state, then d->trans[state] points to its table.
+ If it is an accepting state then d->fails[state] points to its table.
+ If it has no table at all, then d->trans[state] is NULL.
+ TODO: Improve this comment, get rid of the unnecessary redundancy. */
+
+static void
+build_state(s, d)
+ int s;
+ struct dfa *d;
+{
+ int *trans; /* The new transition table. */
+ int i;
+
+ /* Set an upper limit on the number of transition tables that will ever
+ exist at once. 1024 is arbitrary. The idea is that the frequently
+ used transition tables will be quickly rebuilt, whereas the ones that
+ were only needed once or twice will be cleared away. */
+ if (d->trcount >= 1024)
+ {
+ for (i = 0; i < d->tralloc; ++i)
+ if (d->trans[i])
+ {
+ free((ptr_t) d->trans[i]);
+ d->trans[i] = NULL;
+ }
+ else if (d->fails[i])
+ {
+ free((ptr_t) d->fails[i]);
+ d->fails[i] = NULL;
+ }
+ d->trcount = 0;
+ }
+
+ ++d->trcount;
+
+ /* Set up the success bits for this state. */
+ d->success[s] = 0;
+ if (ACCEPTS_IN_CONTEXT(d->states[s].newline, 1, d->states[s].letter, 0,
+ s, *d))
+ d->success[s] |= 4;
+ if (ACCEPTS_IN_CONTEXT(d->states[s].newline, 0, d->states[s].letter, 1,
+ s, *d))
+ d->success[s] |= 2;
+ if (ACCEPTS_IN_CONTEXT(d->states[s].newline, 0, d->states[s].letter, 0,
+ s, *d))
+ d->success[s] |= 1;
+
+ MALLOC(trans, int, NOTCHAR);
+ dfastate(s, d, trans);
+
+ /* Now go through the new transition table, and make sure that the trans
+ and fail arrays are allocated large enough to hold a pointer for the
+ largest state mentioned in the table. */
+ for (i = 0; i < NOTCHAR; ++i)
+ if (trans[i] >= d->tralloc)
+ {
+ int oldalloc = d->tralloc;
+
+ while (trans[i] >= d->tralloc)
+ d->tralloc *= 2;
+ REALLOC(d->realtrans, int *, d->tralloc + 1);
+ d->trans = d->realtrans + 1;
+ REALLOC(d->fails, int *, d->tralloc);
+ REALLOC(d->success, int, d->tralloc);
+ REALLOC(d->newlines, int, d->tralloc);
+ while (oldalloc < d->tralloc)
+ {
+ d->trans[oldalloc] = NULL;
+ d->fails[oldalloc++] = NULL;
+ }
+ }
+
+ /* Keep the newline transition in a special place so we can use it as
+ a sentinel. */
+ d->newlines[s] = trans['\n'];
+ trans['\n'] = -1;
+
+ if (ACCEPTING(s, *d))
+ d->fails[s] = trans;
+ else
+ d->trans[s] = trans;
+}
+
+static void
+build_state_zero(d)
+ struct dfa *d;
+{
+ d->tralloc = 1;
+ d->trcount = 0;
+ CALLOC(d->realtrans, int *, d->tralloc + 1);
+ d->trans = d->realtrans + 1;
+ CALLOC(d->fails, int *, d->tralloc);
+ MALLOC(d->success, int, d->tralloc);
+ MALLOC(d->newlines, int, d->tralloc);
+ build_state(0, d);
+}
+
+/* Search through a buffer looking for a match to the given struct dfa.
+ Find the first occurrence of a string matching the regexp in the buffer,
+ and the shortest possible version thereof. Return a pointer to the first
+ character after the match, or NULL if none is found. Begin points to
+ the beginning of the buffer, and end points to the first character after
+ its end. We store a newline in *end to act as a sentinel, so end had
+ better point somewhere valid. Newline is a flag indicating whether to
+ allow newlines to be in the matching string. If count is non-
+ NULL it points to a place we're supposed to increment every time we
+ see a newline. Finally, if backref is non-NULL it points to a place
+ where we're supposed to store a 1 if backreferencing happened and the
+ match needs to be verified by a backtracking matcher. Otherwise
+ we store a 0 in *backref. */
+char *
+dfaexec(d, begin, end, newline, count, backref)
+ struct dfa *d;
+ char *begin;
+ char *end;
+ int newline;
+ int *count;
+ int *backref;
+{
+ register s, s1, tmp; /* Current state. */
+ register unsigned char *p; /* Current input character. */
+ register **trans, *t; /* Copy of d->trans so it can be optimized
+ into a register. */
+ static sbit[NOTCHAR]; /* Table for anding with d->success. */
+ static sbit_init;
+
+ if (! sbit_init)
+ {
+ int i;
+
+ sbit_init = 1;
+ for (i = 0; i < NOTCHAR; ++i)
+ if (i == '\n')
+ sbit[i] = 4;
+ else if (ISALNUM(i))
+ sbit[i] = 2;
+ else
+ sbit[i] = 1;
+ }
+
+ if (! d->tralloc)
+ build_state_zero(d);
+
+ s = s1 = 0;
+ p = (unsigned char *) begin;
+ trans = d->trans;
+ *end = '\n';
+
+ for (;;)
+ {
+ /* The dreaded inner loop. */
+ if ((t = trans[s]) != 0)
+ do
+ {
+ s1 = t[*p++];
+ if (! (t = trans[s1]))
+ goto last_was_s;
+ s = t[*p++];
+ }
+ while ((t = trans[s]) != 0);
+ goto last_was_s1;
+ last_was_s:
+ tmp = s, s = s1, s1 = tmp;
+ last_was_s1:
+
+ if (s >= 0 && p <= (unsigned char *) end && d->fails[s])
+ {
+ if (d->success[s] & sbit[*p])
+ {
+ if (backref)
+ if (d->states[s].backref)
+ *backref = 1;
+ else
+ *backref = 0;
+ return (char *) p;
+ }
+
+ s1 = s;
+ s = d->fails[s][*p++];
+ continue;
+ }
+
+ /* If the previous character was a newline, count it. */
+ if (count && (char *) p <= end && p[-1] == '\n')
+ ++*count;
+
+ /* Check if we've run off the end of the buffer. */
+ if ((char *) p > end)
+ return NULL;
+
+ if (s >= 0)
+ {
+ build_state(s, d);
+ trans = d->trans;
+ continue;
+ }
+
+ if (p[-1] == '\n' && newline)
+ {
+ s = d->newlines[s1];
+ continue;
+ }
+
+ s = 0;
+ }
+}
+
+/* Initialize the components of a dfa that the other routines don't
+ initialize for themselves. */
+void
+dfainit(d)
+ struct dfa *d;
+{
+ d->calloc = 1;
+ MALLOC(d->charclasses, charclass, d->calloc);
+ d->cindex = 0;
+
+ d->talloc = 1;
+ MALLOC(d->tokens, token, d->talloc);
+ d->tindex = d->depth = d->nleaves = d->nregexps = 0;
+
+ d->searchflag = 0;
+ d->tralloc = 0;
+
+ d->musts = 0;
+}
+
+/* Parse and analyze a single string of the given length. */
+void
+dfacomp(s, len, d, searchflag)
+ char *s;
+ size_t len;
+ struct dfa *d;
+ int searchflag;
+{
+ if (case_fold) /* dummy folding in service of dfamust() */
+ {
+ char *copy;
+ int i;
+
+ copy = malloc(len);
+ if (!copy)
+ dfaerror("out of memory");
+
+ /* This is a kludge. */
+ case_fold = 0;
+ for (i = 0; i < len; ++i)
+ if (ISUPPER(s[i]))
+ copy[i] = tolower(s[i]);
+ else
+ copy[i] = s[i];
+
+ dfainit(d);
+ dfaparse(copy, len, d);
+ free(copy);
+ dfamust(d);
+ d->cindex = d->tindex = d->depth = d->nleaves = d->nregexps = 0;
+ case_fold = 1;
+ dfaparse(s, len, d);
+ dfaanalyze(d, searchflag);
+ }
+ else
+ {
+ dfainit(d);
+ dfaparse(s, len, d);
+ dfamust(d);
+ dfaanalyze(d, searchflag);
+ }
+}
+
+/* Free the storage held by the components of a dfa. */
+void
+dfafree(d)
+ struct dfa *d;
+{
+ int i;
+ struct dfamust *dm, *ndm;
+
+ free((ptr_t) d->charclasses);
+ free((ptr_t) d->tokens);
+ for (i = 0; i < d->sindex; ++i)
+ free((ptr_t) d->states[i].elems.elems);
+ free((ptr_t) d->states);
+ for (i = 0; i < d->tindex; ++i)
+ if (d->follows[i].elems)
+ free((ptr_t) d->follows[i].elems);
+ free((ptr_t) d->follows);
+ for (i = 0; i < d->tralloc; ++i)
+ if (d->trans[i])
+ free((ptr_t) d->trans[i]);
+ else if (d->fails[i])
+ free((ptr_t) d->fails[i]);
+ free((ptr_t) d->realtrans);
+ free((ptr_t) d->fails);
+ free((ptr_t) d->newlines);
+ for (dm = d->musts; dm; dm = ndm)
+ {
+ ndm = dm->next;
+ free(dm->must);
+ free((ptr_t) dm);
+ }
+}
+
+/* Having found the postfix representation of the regular expression,
+ try to find a long sequence of characters that must appear in any line
+ containing the r.e.
+ Finding a "longest" sequence is beyond the scope here;
+ we take an easy way out and hope for the best.
+ (Take "(ab|a)b"--please.)
+
+ We do a bottom-up calculation of sequences of characters that must appear
+ in matches of r.e.'s represented by trees rooted at the nodes of the postfix
+ representation:
+ sequences that must appear at the left of the match ("left")
+ sequences that must appear at the right of the match ("right")
+ lists of sequences that must appear somewhere in the match ("in")
+ sequences that must constitute the match ("is")
+
+ When we get to the root of the tree, we use one of the longest of its
+ calculated "in" sequences as our answer. The sequence we find is returned in
+ d->must (where "d" is the single argument passed to "dfamust");
+ the length of the sequence is returned in d->mustn.
+
+ The sequences calculated for the various types of node (in pseudo ANSI c)
+ are shown below. "p" is the operand of unary operators (and the left-hand
+ operand of binary operators); "q" is the right-hand operand of binary
+ operators.
+
+ "ZERO" means "a zero-length sequence" below.
+
+ Type left right is in
+ ---- ---- ----- -- --
+ char c # c # c # c # c
+
+ CSET ZERO ZERO ZERO ZERO
+
+ STAR ZERO ZERO ZERO ZERO
+
+ QMARK ZERO ZERO ZERO ZERO
+
+ PLUS p->left p->right ZERO p->in
+
+ CAT (p->is==ZERO)? (q->is==ZERO)? (p->is!=ZERO && p->in plus
+ p->left : q->right : q->is!=ZERO) ? q->in plus
+ p->is##q->left p->right##q->is p->is##q->is : p->right##q->left
+ ZERO
+
+ OR longest common longest common (do p->is and substrings common to
+ leading trailing q->is have same p->in and q->in
+ (sub)sequence (sub)sequence length and
+ of p->left of p->right content) ?
+ and q->left and q->right p->is : NULL
+
+ If there's anything else we recognize in the tree, all four sequences get set
+ to zero-length sequences. If there's something we don't recognize in the tree,
+ we just return a zero-length sequence.
+
+ Break ties in favor of infrequent letters (choosing 'zzz' in preference to
+ 'aaa')?
+
+ And. . .is it here or someplace that we might ponder "optimizations" such as
+ egrep 'psi|epsilon' -> egrep 'psi'
+ egrep 'pepsi|epsilon' -> egrep 'epsi'
+ (Yes, we now find "epsi" as a "string
+ that must occur", but we might also
+ simplify the *entire* r.e. being sought)
+ grep '[c]' -> grep 'c'
+ grep '(ab|a)b' -> grep 'ab'
+ grep 'ab*' -> grep 'a'
+ grep 'a*b' -> grep 'b'
+
+ There are several issues:
+
+ Is optimization easy (enough)?
+
+ Does optimization actually accomplish anything,
+ or is the automaton you get from "psi|epsilon" (for example)
+ the same as the one you get from "psi" (for example)?
+
+ Are optimizable r.e.'s likely to be used in real-life situations
+ (something like 'ab*' is probably unlikely; something like is
+ 'psi|epsilon' is likelier)? */
+
+static char *
+icatalloc(old, new)
+ char *old;
+ char *new;
+{
+ char *result;
+ int oldsize, newsize;
+
+ newsize = (new == NULL) ? 0 : strlen(new);
+ if (old == NULL)
+ oldsize = 0;
+ else if (newsize == 0)
+ return old;
+ else oldsize = strlen(old);
+ if (old == NULL)
+ result = (char *) malloc(newsize + 1);
+ else
+ result = (char *) realloc((void *) old, oldsize + newsize + 1);
+ if (result != NULL && new != NULL)
+ (void) strcpy(result + oldsize, new);
+ return result;
+}
+
+static char *
+icpyalloc(string)
+ char *string;
+{
+ return icatalloc((char *) NULL, string);
+}
+
+static char *
+istrstr(lookin, lookfor)
+ char *lookin;
+ char *lookfor;
+{
+ char *cp;
+ int len;
+
+ len = strlen(lookfor);
+ for (cp = lookin; *cp != '\0'; ++cp)
+ if (strncmp(cp, lookfor, len) == 0)
+ return cp;
+ return NULL;
+}
+
+static void
+ifree(cp)
+ char *cp;
+{
+ if (cp != NULL)
+ free(cp);
+}
+
+static void
+freelist(cpp)
+ char **cpp;
+{
+ int i;
+
+ if (cpp == NULL)
+ return;
+ for (i = 0; cpp[i] != NULL; ++i)
+ {
+ free(cpp[i]);
+ cpp[i] = NULL;
+ }
+}
+
+static char **
+enlist(cpp, new, len)
+ char **cpp;
+ char *new;
+ int len;
+{
+ int i, j;
+
+ if (cpp == NULL)
+ return NULL;
+ if ((new = icpyalloc(new)) == NULL)
+ {
+ freelist(cpp);
+ return NULL;
+ }
+ new[len] = '\0';
+ /* Is there already something in the list that's new (or longer)? */
+ for (i = 0; cpp[i] != NULL; ++i)
+ if (istrstr(cpp[i], new) != NULL)
+ {
+ free(new);
+ return cpp;
+ }
+ /* Eliminate any obsoleted strings. */
+ j = 0;
+ while (cpp[j] != NULL)
+ if (istrstr(new, cpp[j]) == NULL)
+ ++j;
+ else
+ {
+ free(cpp[j]);
+ if (--i == j)
+ break;
+ cpp[j] = cpp[i];
+ cpp[i] = NULL;
+ }
+ /* Add the new string. */
+ cpp = (char **) realloc((char *) cpp, (i + 2) * sizeof *cpp);
+ if (cpp == NULL)
+ return NULL;
+ cpp[i] = new;
+ cpp[i + 1] = NULL;
+ return cpp;
+}
+
+/* Given pointers to two strings, return a pointer to an allocated
+ list of their distinct common substrings. Return NULL if something
+ seems wild. */
+static char **
+comsubs(left, right)
+ char *left;
+ char *right;
+{
+ char **cpp;
+ char *lcp;
+ char *rcp;
+ int i, len;
+
+ if (left == NULL || right == NULL)
+ return NULL;
+ cpp = (char **) malloc(sizeof *cpp);
+ if (cpp == NULL)
+ return NULL;
+ cpp[0] = NULL;
+ for (lcp = left; *lcp != '\0'; ++lcp)
+ {
+ len = 0;
+ rcp = index(right, *lcp);
+ while (rcp != NULL)
+ {
+ for (i = 1; lcp[i] != '\0' && lcp[i] == rcp[i]; ++i)
+ ;
+ if (i > len)
+ len = i;
+ rcp = index(rcp + 1, *lcp);
+ }
+ if (len == 0)
+ continue;
+ if ((cpp = enlist(cpp, lcp, len)) == NULL)
+ break;
+ }
+ return cpp;
+}
+
+static char **
+addlists(old, new)
+char **old;
+char **new;
+{
+ int i;
+
+ if (old == NULL || new == NULL)
+ return NULL;
+ for (i = 0; new[i] != NULL; ++i)
+ {
+ old = enlist(old, new[i], strlen(new[i]));
+ if (old == NULL)
+ break;
+ }
+ return old;
+}
+
+/* Given two lists of substrings, return a new list giving substrings
+ common to both. */
+static char **
+inboth(left, right)
+ char **left;
+ char **right;
+{
+ char **both;
+ char **temp;
+ int lnum, rnum;
+
+ if (left == NULL || right == NULL)
+ return NULL;
+ both = (char **) malloc(sizeof *both);
+ if (both == NULL)
+ return NULL;
+ both[0] = NULL;
+ for (lnum = 0; left[lnum] != NULL; ++lnum)
+ {
+ for (rnum = 0; right[rnum] != NULL; ++rnum)
+ {
+ temp = comsubs(left[lnum], right[rnum]);
+ if (temp == NULL)
+ {
+ freelist(both);
+ return NULL;
+ }
+ both = addlists(both, temp);
+ freelist(temp);
+ if (both == NULL)
+ return NULL;
+ }
+ }
+ return both;
+}
+
+typedef struct
+{
+ char **in;
+ char *left;
+ char *right;
+ char *is;
+} must;
+
+static void
+resetmust(mp)
+must *mp;
+{
+ mp->left[0] = mp->right[0] = mp->is[0] = '\0';
+ freelist(mp->in);
+}
+
+static void
+dfamust(dfa)
+struct dfa *dfa;
+{
+ must *musts;
+ must *mp;
+ char *result;
+ int ri;
+ int i;
+ int exact;
+ token t;
+ static must must0;
+ struct dfamust *dm;
+
+ result = "";
+ exact = 0;
+ musts = (must *) malloc((dfa->tindex + 1) * sizeof *musts);
+ if (musts == NULL)
+ return;
+ mp = musts;
+ for (i = 0; i <= dfa->tindex; ++i)
+ mp[i] = must0;
+ for (i = 0; i <= dfa->tindex; ++i)
+ {
+ mp[i].in = (char **) malloc(sizeof *mp[i].in);
+ mp[i].left = malloc(2);
+ mp[i].right = malloc(2);
+ mp[i].is = malloc(2);
+ if (mp[i].in == NULL || mp[i].left == NULL ||
+ mp[i].right == NULL || mp[i].is == NULL)
+ goto done;
+ mp[i].left[0] = mp[i].right[0] = mp[i].is[0] = '\0';
+ mp[i].in[0] = NULL;
+ }
+#ifdef DEBUG
+ fprintf(stderr, "dfamust:\n");
+ for (i = 0; i < dfa->tindex; ++i)
+ {
+ fprintf(stderr, " %d:", i);
+ prtok(dfa->tokens[i]);
+ }
+ putc('\n', stderr);
+#endif
+ for (ri = 0; ri < dfa->tindex; ++ri)
+ {
+ switch (t = dfa->tokens[ri])
+ {
+ case LPAREN:
+ case RPAREN:
+ goto done; /* "cannot happen" */
+ case EMPTY:
+ case BEGLINE:
+ case ENDLINE:
+ case BEGWORD:
+ case ENDWORD:
+ case LIMWORD:
+ case NOTLIMWORD:
+ case BACKREF:
+ resetmust(mp);
+ break;
+ case STAR:
+ case QMARK:
+ if (mp <= musts)
+ goto done; /* "cannot happen" */
+ --mp;
+ resetmust(mp);
+ break;
+ case OR:
+ case ORTOP:
+ if (mp < &musts[2])
+ goto done; /* "cannot happen" */
+ {
+ char **new;
+ must *lmp;
+ must *rmp;
+ int j, ln, rn, n;
+
+ rmp = --mp;
+ lmp = --mp;
+ /* Guaranteed to be. Unlikely, but. . . */
+ if (strcmp(lmp->is, rmp->is) != 0)
+ lmp->is[0] = '\0';
+ /* Left side--easy */
+ i = 0;
+ while (lmp->left[i] != '\0' && lmp->left[i] == rmp->left[i])
+ ++i;
+ lmp->left[i] = '\0';
+ /* Right side */
+ ln = strlen(lmp->right);
+ rn = strlen(rmp->right);
+ n = ln;
+ if (n > rn)
+ n = rn;
+ for (i = 0; i < n; ++i)
+ if (lmp->right[ln - i - 1] != rmp->right[rn - i - 1])
+ break;
+ for (j = 0; j < i; ++j)
+ lmp->right[j] = lmp->right[(ln - i) + j];
+ lmp->right[j] = '\0';
+ new = inboth(lmp->in, rmp->in);
+ if (new == NULL)
+ goto done;
+ freelist(lmp->in);
+ free((char *) lmp->in);
+ lmp->in = new;
+ }
+ break;
+ case PLUS:
+ if (mp <= musts)
+ goto done; /* "cannot happen" */
+ --mp;
+ mp->is[0] = '\0';
+ break;
+ case END:
+ if (mp != &musts[1])
+ goto done; /* "cannot happen" */
+ for (i = 0; musts[0].in[i] != NULL; ++i)
+ if (strlen(musts[0].in[i]) > strlen(result))
+ result = musts[0].in[i];
+ if (strcmp(result, musts[0].is) == 0)
+ exact = 1;
+ goto done;
+ case CAT:
+ if (mp < &musts[2])
+ goto done; /* "cannot happen" */
+ {
+ must *lmp;
+ must *rmp;
+
+ rmp = --mp;
+ lmp = --mp;
+ /* In. Everything in left, plus everything in
+ right, plus catenation of
+ left's right and right's left. */
+ lmp->in = addlists(lmp->in, rmp->in);
+ if (lmp->in == NULL)
+ goto done;
+ if (lmp->right[0] != '\0' &&
+ rmp->left[0] != '\0')
+ {
+ char *tp;
+
+ tp = icpyalloc(lmp->right);
+ if (tp == NULL)
+ goto done;
+ tp = icatalloc(tp, rmp->left);
+ if (tp == NULL)
+ goto done;
+ lmp->in = enlist(lmp->in, tp,
+ strlen(tp));
+ free(tp);
+ if (lmp->in == NULL)
+ goto done;
+ }
+ /* Left-hand */
+ if (lmp->is[0] != '\0')
+ {
+ lmp->left = icatalloc(lmp->left,
+ rmp->left);
+ if (lmp->left == NULL)
+ goto done;
+ }
+ /* Right-hand */
+ if (rmp->is[0] == '\0')
+ lmp->right[0] = '\0';
+ lmp->right = icatalloc(lmp->right, rmp->right);
+ if (lmp->right == NULL)
+ goto done;
+ /* Guaranteed to be */
+ if (lmp->is[0] != '\0' && rmp->is[0] != '\0')
+ {
+ lmp->is = icatalloc(lmp->is, rmp->is);
+ if (lmp->is == NULL)
+ goto done;
+ }
+ else
+ lmp->is[0] = '\0';
+ }
+ break;
+ default:
+ if (t < END)
+ {
+ /* "cannot happen" */
+ goto done;
+ }
+ else if (t == '\0')
+ {
+ /* not on *my* shift */
+ goto done;
+ }
+ else if (t >= CSET)
+ {
+ /* easy enough */
+ resetmust(mp);
+ }
+ else
+ {
+ /* plain character */
+ resetmust(mp);
+ mp->is[0] = mp->left[0] = mp->right[0] = t;
+ mp->is[1] = mp->left[1] = mp->right[1] = '\0';
+ mp->in = enlist(mp->in, mp->is, 1);
+ if (mp->in == NULL)
+ goto done;
+ }
+ break;
+ }
+#ifdef DEBUG
+ fprintf(stderr, " node: %d:", ri);
+ prtok(dfa->tokens[ri]);
+ fprintf(stderr, "\n in:");
+ for (i = 0; mp->in[i]; ++i)
+ fprintf(stderr, " \"%s\"", mp->in[i]);
+ fprintf(stderr, "\n is: \"%s\"\n", mp->is);
+ fprintf(stderr, " left: \"%s\"\n", mp->left);
+ fprintf(stderr, " right: \"%s\"\n", mp->right);
+#endif
+ ++mp;
+ }
+ done:
+ if (strlen(result))
+ {
+ dm = (struct dfamust *) malloc(sizeof (struct dfamust));
+ dm->exact = exact;
+ dm->must = malloc(strlen(result) + 1);
+ strcpy(dm->must, result);
+ dm->next = dfa->musts;
+ dfa->musts = dm;
+ }
+ mp = musts;
+ for (i = 0; i <= dfa->tindex; ++i)
+ {
+ freelist(mp[i].in);
+ ifree((char *) mp[i].in);
+ ifree(mp[i].left);
+ ifree(mp[i].right);
+ ifree(mp[i].is);
+ }
+ free((char *) mp);
+}
diff --git a/gnu/usr.bin/grep/dfa.h b/gnu/usr.bin/grep/dfa.h
new file mode 100644
index 0000000..32e05fc
--- /dev/null
+++ b/gnu/usr.bin/grep/dfa.h
@@ -0,0 +1,360 @@
+/* dfa.h - declarations for GNU deterministic regexp compiler
+ Copyright (C) 1988 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written June, 1988 by Mike Haertel */
+
+/* FIXME:
+ 2. We should not export so much of the DFA internals.
+ In addition to clobbering modularity, we eat up valuable
+ name space. */
+
+/* Number of bits in an unsigned char. */
+#define CHARBITS 8
+
+/* First integer value that is greater than any character code. */
+#define NOTCHAR (1 << CHARBITS)
+
+/* INTBITS need not be exact, just a lower bound. */
+#define INTBITS (CHARBITS * sizeof (int))
+
+/* Number of ints required to hold a bit for every character. */
+#define CHARCLASS_INTS ((NOTCHAR + INTBITS - 1) / INTBITS)
+
+/* Sets of unsigned characters are stored as bit vectors in arrays of ints. */
+typedef int charclass[CHARCLASS_INTS];
+
+/* The regexp is parsed into an array of tokens in postfix form. Some tokens
+ are operators and others are terminal symbols. Most (but not all) of these
+ codes are returned by the lexical analyzer. */
+
+typedef enum
+{
+ END = -1, /* END is a terminal symbol that matches the
+ end of input; any value of END or less in
+ the parse tree is such a symbol. Accepting
+ states of the DFA are those that would have
+ a transition on END. */
+
+ /* Ordinary character values are terminal symbols that match themselves. */
+
+ EMPTY = NOTCHAR, /* EMPTY is a terminal symbol that matches
+ the empty string. */
+
+ BACKREF, /* BACKREF is generated by \<digit>; it
+ it not completely handled. If the scanner
+ detects a transition on backref, it returns
+ a kind of "semi-success" indicating that
+ the match will have to be verified with
+ a backtracking matcher. */
+
+ BEGLINE, /* BEGLINE is a terminal symbol that matches
+ the empty string if it is at the beginning
+ of a line. */
+
+ ENDLINE, /* ENDLINE is a terminal symbol that matches
+ the empty string if it is at the end of
+ a line. */
+
+ BEGWORD, /* BEGWORD is a terminal symbol that matches
+ the empty string if it is at the beginning
+ of a word. */
+
+ ENDWORD, /* ENDWORD is a terminal symbol that matches
+ the empty string if it is at the end of
+ a word. */
+
+ LIMWORD, /* LIMWORD is a terminal symbol that matches
+ the empty string if it is at the beginning
+ or the end of a word. */
+
+ NOTLIMWORD, /* NOTLIMWORD is a terminal symbol that
+ matches the empty string if it is not at
+ the beginning or end of a word. */
+
+ QMARK, /* QMARK is an operator of one argument that
+ matches zero or one occurences of its
+ argument. */
+
+ STAR, /* STAR is an operator of one argument that
+ matches the Kleene closure (zero or more
+ occurrences) of its argument. */
+
+ PLUS, /* PLUS is an operator of one argument that
+ matches the positive closure (one or more
+ occurrences) of its argument. */
+
+ REPMN, /* REPMN is a lexical token corresponding
+ to the {m,n} construct. REPMN never
+ appears in the compiled token vector. */
+
+ CAT, /* CAT is an operator of two arguments that
+ matches the concatenation of its
+ arguments. CAT is never returned by the
+ lexical analyzer. */
+
+ OR, /* OR is an operator of two arguments that
+ matches either of its arguments. */
+
+ ORTOP, /* OR at the toplevel in the parse tree.
+ This is used for a boyer-moore heuristic. */
+
+ LPAREN, /* LPAREN never appears in the parse tree,
+ it is only a lexeme. */
+
+ RPAREN, /* RPAREN never appears in the parse tree. */
+
+ CSET /* CSET and (and any value greater) is a
+ terminal symbol that matches any of a
+ class of characters. */
+} token;
+
+/* Sets are stored in an array in the compiled dfa; the index of the
+ array corresponding to a given set token is given by SET_INDEX(t). */
+#define SET_INDEX(t) ((t) - CSET)
+
+/* Sometimes characters can only be matched depending on the surrounding
+ context. Such context decisions depend on what the previous character
+ was, and the value of the current (lookahead) character. Context
+ dependent constraints are encoded as 8 bit integers. Each bit that
+ is set indicates that the constraint succeeds in the corresponding
+ context.
+
+ bit 7 - previous and current are newlines
+ bit 6 - previous was newline, current isn't
+ bit 5 - previous wasn't newline, current is
+ bit 4 - neither previous nor current is a newline
+ bit 3 - previous and current are word-constituents
+ bit 2 - previous was word-constituent, current isn't
+ bit 1 - previous wasn't word-constituent, current is
+ bit 0 - neither previous nor current is word-constituent
+
+ Word-constituent characters are those that satisfy isalnum().
+
+ The macro SUCCEEDS_IN_CONTEXT determines whether a a given constraint
+ succeeds in a particular context. Prevn is true if the previous character
+ was a newline, currn is true if the lookahead character is a newline.
+ Prevl and currl similarly depend upon whether the previous and current
+ characters are word-constituent letters. */
+#define MATCHES_NEWLINE_CONTEXT(constraint, prevn, currn) \
+ ((constraint) & 1 << (((prevn) ? 2 : 0) + ((currn) ? 1 : 0) + 4))
+#define MATCHES_LETTER_CONTEXT(constraint, prevl, currl) \
+ ((constraint) & 1 << (((prevl) ? 2 : 0) + ((currl) ? 1 : 0)))
+#define SUCCEEDS_IN_CONTEXT(constraint, prevn, currn, prevl, currl) \
+ (MATCHES_NEWLINE_CONTEXT(constraint, prevn, currn) \
+ && MATCHES_LETTER_CONTEXT(constraint, prevl, currl))
+
+/* The following macros give information about what a constraint depends on. */
+#define PREV_NEWLINE_DEPENDENT(constraint) \
+ (((constraint) & 0xc0) >> 2 != ((constraint) & 0x30))
+#define PREV_LETTER_DEPENDENT(constraint) \
+ (((constraint) & 0x0c) >> 2 != ((constraint) & 0x03))
+
+/* Tokens that match the empty string subject to some constraint actually
+ work by applying that constraint to determine what may follow them,
+ taking into account what has gone before. The following values are
+ the constraints corresponding to the special tokens previously defined. */
+#define NO_CONSTRAINT 0xff
+#define BEGLINE_CONSTRAINT 0xcf
+#define ENDLINE_CONSTRAINT 0xaf
+#define BEGWORD_CONSTRAINT 0xf2
+#define ENDWORD_CONSTRAINT 0xf4
+#define LIMWORD_CONSTRAINT 0xf6
+#define NOTLIMWORD_CONSTRAINT 0xf9
+
+/* States of the recognizer correspond to sets of positions in the parse
+ tree, together with the constraints under which they may be matched.
+ So a position is encoded as an index into the parse tree together with
+ a constraint. */
+typedef struct
+{
+ unsigned index; /* Index into the parse array. */
+ unsigned constraint; /* Constraint for matching this position. */
+} position;
+
+/* Sets of positions are stored as arrays. */
+typedef struct
+{
+ position *elems; /* Elements of this position set. */
+ int nelem; /* Number of elements in this set. */
+} position_set;
+
+/* A state of the dfa consists of a set of positions, some flags,
+ and the token value of the lowest-numbered position of the state that
+ contains an END token. */
+typedef struct
+{
+ int hash; /* Hash of the positions of this state. */
+ position_set elems; /* Positions this state could match. */
+ char newline; /* True if previous state matched newline. */
+ char letter; /* True if previous state matched a letter. */
+ char backref; /* True if this state matches a \<digit>. */
+ unsigned char constraint; /* Constraint for this state to accept. */
+ int first_end; /* Token value of the first END in elems. */
+} dfa_state;
+
+/* Element of a list of strings, at least one of which is known to
+ appear in any R.E. matching the DFA. */
+struct dfamust
+{
+ int exact;
+ char *must;
+ struct dfamust *next;
+};
+
+/* A compiled regular expression. */
+struct dfa
+{
+ /* Stuff built by the scanner. */
+ charclass *charclasses; /* Array of character sets for CSET tokens. */
+ int cindex; /* Index for adding new charclasses. */
+ int calloc; /* Number of charclasses currently allocated. */
+
+ /* Stuff built by the parser. */
+ token *tokens; /* Postfix parse array. */
+ int tindex; /* Index for adding new tokens. */
+ int talloc; /* Number of tokens currently allocated. */
+ int depth; /* Depth required of an evaluation stack
+ used for depth-first traversal of the
+ parse tree. */
+ int nleaves; /* Number of leaves on the parse tree. */
+ int nregexps; /* Count of parallel regexps being built
+ with dfaparse(). */
+
+ /* Stuff owned by the state builder. */
+ dfa_state *states; /* States of the dfa. */
+ int sindex; /* Index for adding new states. */
+ int salloc; /* Number of states currently allocated. */
+
+ /* Stuff built by the structure analyzer. */
+ position_set *follows; /* Array of follow sets, indexed by position
+ index. The follow of a position is the set
+ of positions containing characters that
+ could conceivably follow a character
+ matching the given position in a string
+ matching the regexp. Allocated to the
+ maximum possible position index. */
+ int searchflag; /* True if we are supposed to build a searching
+ as opposed to an exact matcher. A searching
+ matcher finds the first and shortest string
+ matching a regexp anywhere in the buffer,
+ whereas an exact matcher finds the longest
+ string matching, but anchored to the
+ beginning of the buffer. */
+
+ /* Stuff owned by the executor. */
+ int tralloc; /* Number of transition tables that have
+ slots so far. */
+ int trcount; /* Number of transition tables that have
+ actually been built. */
+ int **trans; /* Transition tables for states that can
+ never accept. If the transitions for a
+ state have not yet been computed, or the
+ state could possibly accept, its entry in
+ this table is NULL. */
+ int **realtrans; /* Trans always points to realtrans + 1; this
+ is so trans[-1] can contain NULL. */
+ int **fails; /* Transition tables after failing to accept
+ on a state that potentially could do so. */
+ int *success; /* Table of acceptance conditions used in
+ dfaexec and computed in build_state. */
+ int *newlines; /* Transitions on newlines. The entry for a
+ newline in any transition table is always
+ -1 so we can count lines without wasting
+ too many cycles. The transition for a
+ newline is stored separately and handled
+ as a special case. Newline is also used
+ as a sentinel at the end of the buffer. */
+ struct dfamust *musts; /* List of strings, at least one of which
+ is known to appear in any r.e. matching
+ the dfa. */
+};
+
+/* Some macros for user access to dfa internals. */
+
+/* ACCEPTING returns true if s could possibly be an accepting state of r. */
+#define ACCEPTING(s, r) ((r).states[s].constraint)
+
+/* ACCEPTS_IN_CONTEXT returns true if the given state accepts in the
+ specified context. */
+#define ACCEPTS_IN_CONTEXT(prevn, currn, prevl, currl, state, dfa) \
+ SUCCEEDS_IN_CONTEXT((dfa).states[state].constraint, \
+ prevn, currn, prevl, currl)
+
+/* FIRST_MATCHING_REGEXP returns the index number of the first of parallel
+ regexps that a given state could accept. Parallel regexps are numbered
+ starting at 1. */
+#define FIRST_MATCHING_REGEXP(state, dfa) (-(dfa).states[state].first_end)
+
+/* Entry points. */
+
+#if __STDC__
+
+/* dfasyntax() takes two arguments; the first sets the syntax bits described
+ earlier in this file, and the second sets the case-folding flag. */
+extern void dfasyntax(int, int);
+
+/* Compile the given string of the given length into the given struct dfa.
+ Final argument is a flag specifying whether to build a searching or an
+ exact matcher. */
+extern void dfacomp(char *, size_t, struct dfa *, int);
+
+/* Execute the given struct dfa on the buffer of characters. The
+ first char * points to the beginning, and the second points to the
+ first character after the end of the buffer, which must be a writable
+ place so a sentinel end-of-buffer marker can be stored there. The
+ second-to-last argument is a flag telling whether to allow newlines to
+ be part of a string matching the regexp. The next-to-last argument,
+ if non-NULL, points to a place to increment every time we see a
+ newline. The final argument, if non-NULL, points to a flag that will
+ be set if further examination by a backtracking matcher is needed in
+ order to verify backreferencing; otherwise the flag will be cleared.
+ Returns NULL if no match is found, or a pointer to the first
+ character after the first & shortest matching string in the buffer. */
+extern char *dfaexec(struct dfa *, char *, char *, int, int *, int *);
+
+/* Free the storage held by the components of a struct dfa. */
+extern void dfafree(struct dfa *);
+
+/* Entry points for people who know what they're doing. */
+
+/* Initialize the components of a struct dfa. */
+extern void dfainit(struct dfa *);
+
+/* Incrementally parse a string of given length into a struct dfa. */
+extern void dfaparse(char *, size_t, struct dfa *);
+
+/* Analyze a parsed regexp; second argument tells whether to build a searching
+ or an exact matcher. */
+extern void dfaanalyze(struct dfa *, int);
+
+/* Compute, for each possible character, the transitions out of a given
+ state, storing them in an array of integers. */
+extern void dfastate(int, struct dfa *, int []);
+
+/* Error handling. */
+
+/* dfaerror() is called by the regexp routines whenever an error occurs. It
+ takes a single argument, a NUL-terminated string describing the error.
+ The default dfaerror() prints the error message to stderr and exits.
+ The user can provide a different dfafree() if so desired. */
+extern void dfaerror(char *);
+
+#else /* ! __STDC__ */
+extern void dfasyntax(), dfacomp(), dfafree(), dfainit(), dfaparse();
+extern void dfaanalyze(), dfastate(), dfaerror();
+extern char *dfaexec();
+#endif /* ! __STDC__ */
diff --git a/gnu/usr.bin/grep/getopt.c b/gnu/usr.bin/grep/getopt.c
new file mode 100644
index 0000000..a59a013
--- /dev/null
+++ b/gnu/usr.bin/grep/getopt.c
@@ -0,0 +1,731 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* NOTE!!! AIX requires this to be the first thing in the file.
+ Do not put ANYTHING before it! */
+#if !defined (__GNUC__) && defined (_AIX)
+ #pragma alloca
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not __GNUC__ */
+#if defined (HAVE_ALLOCA_H) || (defined(sparc) && (defined(sun) || (!defined(USG) && !defined(SVR4) && !defined(__svr4__))))
+#include <alloca.h>
+#else
+#ifndef _AIX
+char *alloca ();
+#endif
+#endif /* alloca.h */
+#endif /* not __GNUC__ */
+
+#if !__STDC__ && !defined(const) && IN_GCC
+#define const
+#endif
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#undef alloca
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+#include <stdlib.h>
+#else /* Not GNU C library. */
+#define __alloca alloca
+#endif /* GNU C library. */
+
+/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
+ long-named option. Because this is not POSIX.2 compliant, it is
+ being phased out. */
+/* #define GETOPT_COMPAT */
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = 0;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* XXX 1003.2 says this must be 1 before any call. */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return EOF with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+#include <string.h>
+#define my_index strchr
+#define my_bcopy(src, dst, n) memcpy ((dst), (src), (n))
+#else
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+char *getenv ();
+
+static char *
+my_index (str, chr)
+ const char *str;
+ int chr;
+{
+ while (*str)
+ {
+ if (*str == chr)
+ return (char *) str;
+ str++;
+ }
+ return 0;
+}
+
+static void
+my_bcopy (from, to, size)
+ const char *from;
+ char *to;
+ int size;
+{
+ int i;
+ for (i = 0; i < size; i++)
+ to[i] = from[i];
+}
+#endif /* GNU C library. */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *);
+ char **temp = (char **) __alloca (nonopts_size);
+
+ /* Interchange the two blocks of data in ARGV. */
+
+ my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size);
+ my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt],
+ (optind - last_nonopt) * sizeof (char *));
+ my_bcopy ((char *) temp,
+ (char *) &argv[first_nonopt + optind - last_nonopt],
+ nonopts_size);
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns `EOF'.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ int option_index;
+
+ optarg = 0;
+
+ /* Initialize the internal data when the first call is made.
+ Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ if (optind == 0)
+ {
+ first_nonopt = last_nonopt = optind = 1;
+
+ nextchar = NULL;
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (getenv ("POSIXLY_CORRECT") != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+ }
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Now skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc
+ && (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* Special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return EOF;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if ((argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ {
+ if (ordering == REQUIRE_ORDER)
+ return EOF;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Start decoding its characters. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ if (longopts != NULL
+ && ((argv[optind][0] == '-'
+ && (argv[optind][1] == '-' || long_only))
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ ))
+ {
+ const struct option *p;
+ char *s = nextchar;
+ int exact = 0;
+ int ambig = 0;
+ const struct option *pfound = NULL;
+ int indfound;
+
+ while (*s && *s != '=')
+ s++;
+
+ /* Test all options for either exact match or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name;
+ p++, option_index++)
+ if (!strncmp (p->name, nextchar, s - nextchar))
+ {
+ if (s - nextchar == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' is ambiguous\n",
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*s)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = s + 1;
+ else
+ {
+ if (opterr)
+ {
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ "%s: option `--%s' doesn't allow an argument\n",
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ "%s: option `%c%s' doesn't allow an argument\n",
+ argv[0], argv[optind - 1][0], pfound->name);
+ }
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' requires an argument\n",
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, "%s: unrecognized option `--%s'\n",
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+#if 0
+ if (c < 040 || c >= 0177)
+ fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
+ argv[0], c);
+ else
+ fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
+#endif
+ }
+ optopt = c;
+ return '?';
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = 0;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+#if 0
+ fprintf (stderr, "%s: option `-%c' requires an argument\n",
+ argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: option requires an argument -- %c\n",
+ argv[0], c);
+#endif
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/gnu/usr.bin/grep/getopt.h b/gnu/usr.bin/grep/getopt.h
new file mode 100644
index 0000000..45541f5
--- /dev/null
+++ b/gnu/usr.bin/grep/getopt.h
@@ -0,0 +1,129 @@
+/* Declarations for getopt.
+ Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized. */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+#if __STDC__
+ const char *name;
+#else
+ char *name;
+#endif
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+#if __STDC__
+#if defined(__GNU_LIBRARY__)
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* not __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind);
+
+/* Internal only. Users should not call this directly. */
+extern int _getopt_internal (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* not __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/gnu/usr.bin/grep/getpagesize.h b/gnu/usr.bin/grep/getpagesize.h
new file mode 100644
index 0000000..e6bd561
--- /dev/null
+++ b/gnu/usr.bin/grep/getpagesize.h
@@ -0,0 +1,42 @@
+#ifdef BSD
+#ifndef BSD4_1
+#define HAVE_GETPAGESIZE
+#endif
+#endif
+
+#ifndef HAVE_GETPAGESIZE
+
+#ifdef VMS
+#define getpagesize() 512
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef _SC_PAGESIZE
+#define getpagesize() sysconf(_SC_PAGESIZE)
+#else
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+
+#ifdef EXEC_PAGESIZE
+#define getpagesize() EXEC_PAGESIZE
+#else
+#ifdef NBPG
+#define getpagesize() NBPG * CLSIZE
+#ifndef CLSIZE
+#define CLSIZE 1
+#endif /* no CLSIZE */
+#else /* no NBPG */
+#define getpagesize() NBPC
+#endif /* no NBPG */
+#endif /* no EXEC_PAGESIZE */
+#else /* !HAVE_SYS_PARAM_H */
+#define getpagesize() 8192 /* punt totally */
+#endif /* !HAVE_SYS_PARAM_H */
+#endif /* no _SC_PAGESIZE */
+
+#endif /* not HAVE_GETPAGESIZE */
+
diff --git a/gnu/usr.bin/grep/grep.1 b/gnu/usr.bin/grep/grep.1
new file mode 100644
index 0000000..27c6b0e
--- /dev/null
+++ b/gnu/usr.bin/grep/grep.1
@@ -0,0 +1,375 @@
+.TH GREP 1 "1992 September 10" "GNU Project"
+.SH NAME
+grep, egrep, fgrep \- print lines matching a pattern
+.SH SYNOPOSIS
+.B grep
+[
+.BR \- [[ AB "] ]\c"
+.I "num"
+]
+[
+.BR \- [ CEFGVBchilnsvwx ]
+]
+[
+.B \-e
+]
+.I pattern
+|
+.BI \-f file
+] [
+.I files...
+]
+.SH DESCRIPTION
+.PP
+.B Grep
+searches the named input
+.I files
+(or standard input if no files are named, or
+the file name
+.B \-
+is given)
+for lines containing a match to the given
+.IR pattern .
+By default,
+.B grep
+prints the matching lines.
+.PP
+There are three major variants of
+.BR grep ,
+controlled by the following options.
+.PD 0
+.TP
+.B \-G
+Interpret
+.I pattern
+as a basic regular expression (see below). This is the default.
+.TP
+.B \-E
+Interpret
+.I pattern
+as an extended regular expression (see below).
+.TP
+.B \-F
+Interpret
+.I pattern
+as a list of fixed strings, separated by newlines,
+any of which is to be matched.
+.LP
+In addition, two variant programs
+.B egrep
+and
+.B fgrep
+are available.
+.B Egrep
+is similiar (but not identical) to
+.BR "grep\ \-E" ,
+and is compatible with the historical Unix
+.BR egrep .
+.B Fgrep
+is the same as
+.BR "grep\ \-F" .
+.PD
+.LP
+All variants of
+.B grep
+understand the following options:
+.PD 0
+.TP
+.BI \- num
+Matches will be printed with
+.I num
+lines of leading and trailing context. However,
+.B grep
+will never print any given line more than once.
+.TP
+.BI \-A " num"
+Print
+.I num
+lines of trailing context after matching lines.
+.TP
+.BI \-B " num"
+Print
+.I num
+lines of leading context before matching lines.
+.TP
+.B \-C
+Equivalent to
+.BR \-2 .
+.TP
+.B \-V
+Print the version number of
+.B grep
+to standard error. This version number should
+be included in all bug reports (see below).
+.TP
+.B \-b
+Print the byte offset within the input file before
+each line of output.
+.TP
+.B \-c
+Suppress normal output; instead print a count of
+matching lines for each input file.
+With the
+.B \-v
+option (see below), count non-matching lines.
+.TP
+.BI \-e " pattern"
+Use
+.I pattern
+as the pattern; useful to protect patterns beginning with
+.BR \- .
+.TP
+.BI \-f " file"
+Obtain the pattern from
+.IR file .
+.TP
+.B \-h
+Suppress the prefixing of filenames on output
+when multiple files are searched.
+.TP
+.B \-i
+Ignore case distinctions in both the
+.I pattern
+and the input files.
+.TP
+.B \-L
+Suppress normal output; instead print the name
+of each input file from which no output would
+normally have been printed.
+.TP
+.B \-l
+Suppress normal output; instead print
+the name of each input file from which output
+would normally have been printed.
+.TP
+.B \-n
+Prefix each line of output with the line number
+within its input file.
+.TP
+.B \-q
+Quiet; suppress normal output.
+.TP
+.B \-s
+Suppress error messages about nonexistent or unreadable files.
+.TP
+.B \-v
+Invert the sense of matching, to select non-matching lines.
+.TP
+.B \-w
+Select only those lines containing matches that form whole words.
+The test is that the matching substring must either be at the
+beginning of the line, or preceded by a non-word constituent
+character. Similarly, it must be either at the end of the line
+or followed by a non-word constituent character. Word-constituent
+characters are letters, digits, and the underscore.
+.TP
+.B \-x
+Select only those matches that exactly match the whole line.
+.PD
+.SH "REGULAR EXPRESSIONS"
+.PP
+A regular expression is a pattern that describes a set of strings.
+Regular expressions are constructed analagously to arithmetic
+expressions, by using various operators to combine smaller expressions.
+.PP
+.B Grep
+understands two different versions of regular expression syntax:
+``basic'' and ``extended.'' In
+.RB "GNU\ " grep ,
+there is no difference in available functionality using either syntax.
+In other implementations, basic regular expressions are less powerful.
+The following description applies to extended regular expressions;
+differences for basic regular expressions are summarized afterwards.
+.PP
+The fundamental building blocks are the regular expressions that match
+a single character. Most characters, including all letters and digits,
+are regular expressions that match themselves. Any metacharacter with
+special meaning may be quoted by preceding it with a backslash.
+.PP
+A list of characters enclosed by
+.B [
+and
+.B ]
+matches any single
+character in that list; if the first character of the list
+is the caret
+.B ^
+then it matches any character
+.I not
+in the list.
+For example, the regular expression
+.B [0123456789]
+matches any single digit. A range of ASCII characters
+may be specified by giving the first and last characters, separated
+by a hyphen.
+Finally, certain named classes of characters are predefined.
+Their names are self explanatory, and they are
+.BR [:alnum:] ,
+.BR [:alpha:] ,
+.BR [:cntrl:] ,
+.BR [:digit:] ,
+.BR [:graph:] ,
+.BR [:lower:] ,
+.BR [:print:] ,
+.BR [:punct:] ,
+.BR [:space:] ,
+.BR [:upper:] ,
+and
+.BR [:xdigit:].
+For example,
+.B [[:alnum:]]
+means
+.BR [0-9A-Za-z] ,
+except the latter form is dependent upon the ASCII character encoding,
+whereas the former is portable.
+(Note that the brackets in these class names are part of the symbolic
+names, and must be included in addition to the brackets delimiting
+the bracket list.) Most metacharacters lose their special meaning
+inside lists. To include a literal
+.B ]
+place it first in the list. Similarly, to include a literal
+.B ^
+place it anywhere but first. Finally, to include a literal
+.B \-
+place it last.
+.PP
+The period
+.B .
+matches any single character.
+The symbol
+.B \ew
+is a synonym for
+.B [[:alnum:]]
+and
+.B \eW
+is a synonym for
+.BR [^[:alnum]] .
+.PP
+The caret
+.B ^
+and the dollar sign
+.B $
+are metacharacters that respectively match the empty string at the
+beginning and end of a line.
+The symbols
+.B \e<
+and
+.B \e>
+respectively match the empty string at the beginning and end of a word.
+The symbol
+.B \eb
+matches the empty string at the edge of a word,
+and
+.B \eB
+matches the empty string provided it's
+.I not
+at the edge of a word.
+.PP
+A regular expression matching a single character may be followed
+by one of several repetition operators:
+.PD 0
+.TP
+.B ?
+The preceding item is optional and matched at most once.
+.TP
+.B *
+The preceding item will be matched zero or more times.
+.TP
+.B +
+The preceding item will be matched one or more times.
+.TP
+.BI { n }
+The preceding item is matched exactly
+.I n
+times.
+.TP
+.BI { n ,}
+The preceding item is matched
+.I n
+or more times.
+.TP
+.BI {, m }
+The preceding item is optional and is matched at most
+.I m
+times.
+.TP
+.BI { n , m }
+The preceding item is matched at least
+.I n
+times, but not more than
+.I m
+times.
+.PD
+.PP
+Two regular expressions may be concatenated; the resulting
+regular expression matches any string formed by concatenating
+two substrings that respectively match the concatenated
+subexpressions.
+.PP
+Two regular expressions may be joined by the infix operator
+.BR | ;
+the resulting regular expression matches any string matching
+either subexpression.
+.PP
+Repetition takes precedence over concatenation, which in turn
+takes precedence over alternation. A whole subexpression may be
+enclosed in parentheses to override these precedence rules.
+.PP
+The backreference
+.BI \e n\c
+\&, where
+.I n
+is a single digit, matches the substring
+previously matched by the
+.IR n th
+parenthesized subexpression of the regular expression.
+.PP
+In basic regular expressions the metacharacters
+.BR ? ,
+.BR + ,
+.BR { ,
+.BR | ,
+.BR ( ,
+and
+.BR )
+lose their special meaning; instead use the backslashed
+versions
+.BR \e? ,
+.BR \e+ ,
+.BR \e{ ,
+.BR \e| ,
+.BR \e( ,
+and
+.BR \e) .
+.PP
+In
+.B egrep
+the metacharacter
+.B {
+loses its special meaning; instead use
+.BR \e{ .
+.SH DIAGNOSTICS
+.PP
+Normally, exit status is 0 if matches were found,
+and 1 if no matches were found. (The
+.B \-v
+option inverts the sense of the exit status.)
+Exit status is 2 if there were syntax errors
+in the pattern, inaccessible input files, or
+other system errors.
+.SH BUGS
+.PP
+Email bug reports to
+.BR bug-gnu-utils@prep.ai.mit.edu .
+Be sure to include the word ``grep'' somewhere in the ``Subject:'' field.
+.PP
+Large repetition counts in the
+.BI { m , n }
+construct may cause grep to use lots of memory.
+In addition,
+certain other obscure regular expressions require exponential time
+and space, and may cause
+.B grep
+to run out of memory.
+.PP
+Backreferences are very slow, and may require exponential time.
diff --git a/gnu/usr.bin/grep/grep.c b/gnu/usr.bin/grep/grep.c
new file mode 100644
index 0000000..07872a1
--- /dev/null
+++ b/gnu/usr.bin/grep/grep.c
@@ -0,0 +1,826 @@
+/* grep.c - main driver file for grep.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Written July 1992 by Mike Haertel. */
+
+#include <errno.h>
+#include <stdio.h>
+
+#ifndef errno
+extern int errno;
+#endif
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+#include <sys/types.h>
+extern char *malloc(), *realloc();
+extern void free();
+#endif
+
+#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
+#include <string.h>
+#ifdef NEED_MEMORY_H
+#include <memory.h>
+#endif
+#else
+#include <strings.h>
+#ifdef __STDC__
+extern void *memchr();
+#else
+extern char *memchr();
+#endif
+#define strrchr rindex
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#else
+#define O_RDONLY 0
+extern int open(), read(), close();
+#endif
+
+#include "getpagesize.h"
+#include "grep.h"
+
+#undef MAX
+#define MAX(A,B) ((A) > (B) ? (A) : (B))
+
+/* Provide missing ANSI features if necessary. */
+
+#ifndef HAVE_STRERROR
+extern int sys_nerr;
+extern char *sys_errlist[];
+#define strerror(E) ((E) < sys_nerr ? sys_errlist[(E)] : "bogus error number")
+#endif
+
+#ifndef HAVE_MEMCHR
+#ifdef __STDC__
+#define VOID void
+#else
+#define VOID char
+#endif
+VOID *
+memchr(vp, c, n)
+ VOID *vp;
+ int c;
+ size_t n;
+{
+ unsigned char *p;
+
+ for (p = (unsigned char *) vp; n--; ++p)
+ if (*p == c)
+ return (VOID *) p;
+ return 0;
+}
+#endif
+
+/* Define flags declared in grep.h. */
+char *matcher;
+int match_icase;
+int match_words;
+int match_lines;
+
+/* Functions we'll use to search. */
+static void (*compile)();
+static char *(*execute)();
+
+/* For error messages. */
+static char *prog;
+static char *filename;
+static int errseen;
+
+/* Print a message and possibly an error string. Remember
+ that something awful happened. */
+static void
+error(mesg, errnum)
+#ifdef __STDC__
+ const
+#endif
+ char *mesg;
+ int errnum;
+{
+ if (errnum)
+ fprintf(stderr, "%s: %s: %s\n", prog, mesg, strerror(errnum));
+ else
+ fprintf(stderr, "%s: %s\n", prog, mesg);
+ errseen = 1;
+}
+
+/* Like error(), but die horribly after printing. */
+void
+fatal(mesg, errnum)
+#ifdef __STDC__
+ const
+#endif
+ char *mesg;
+ int errnum;
+{
+ error(mesg, errnum);
+ exit(2);
+}
+
+/* Interface to handle errors and fix library lossage. */
+char *
+xmalloc(size)
+ size_t size;
+{
+ char *result;
+
+ result = malloc(size);
+ if (size && !result)
+ fatal("memory exhausted", 0);
+ return result;
+}
+
+/* Interface to handle errors and fix some library lossage. */
+char *
+xrealloc(ptr, size)
+ char *ptr;
+ size_t size;
+{
+ char *result;
+
+ if (ptr)
+ result = realloc(ptr, size);
+ else
+ result = malloc(size);
+ if (size && !result)
+ fatal("memory exhausted", 0);
+ return result;
+}
+
+#if !defined(HAVE_VALLOC)
+#define valloc malloc
+#else
+#ifdef __STDC__
+extern void *valloc(size_t);
+#else
+extern char *valloc();
+#endif
+#endif
+
+/* Hairy buffering mechanism for grep. The intent is to keep
+ all reads aligned on a page boundary and multiples of the
+ page size. */
+
+static char *buffer; /* Base of buffer. */
+static size_t bufsalloc; /* Allocated size of buffer save region. */
+static size_t bufalloc; /* Total buffer size. */
+static int bufdesc; /* File descriptor. */
+static char *bufbeg; /* Beginning of user-visible stuff. */
+static char *buflim; /* Limit of user-visible stuff. */
+
+#if defined(HAVE_WORKING_MMAP)
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+static int bufmapped; /* True for ordinary files. */
+static struct stat bufstat; /* From fstat(). */
+static off_t bufoffset; /* What read() normally remembers. */
+#endif
+
+/* Reset the buffer for a new file. Initialize
+ on the first time through. */
+void
+reset(fd)
+ int fd;
+{
+ static int initialized;
+
+ if (!initialized)
+ {
+ initialized = 1;
+#ifndef BUFSALLOC
+ bufsalloc = MAX(8192, getpagesize());
+#else
+ bufsalloc = BUFSALLOC;
+#endif
+ bufalloc = 5 * bufsalloc;
+ /* The 1 byte of overflow is a kludge for dfaexec(), which
+ inserts a sentinel newline at the end of the buffer
+ being searched. There's gotta be a better way... */
+ buffer = valloc(bufalloc + 1);
+ if (!buffer)
+ fatal("memory exhausted", 0);
+ bufbeg = buffer;
+ buflim = buffer;
+ }
+ bufdesc = fd;
+#if defined(HAVE_WORKING_MMAP)
+ if (fstat(fd, &bufstat) < 0 || !S_ISREG(bufstat.st_mode))
+ bufmapped = 0;
+ else
+ {
+ bufmapped = 1;
+ bufoffset = lseek(fd, 0, 1);
+ }
+#endif
+}
+
+/* Read new stuff into the buffer, saving the specified
+ amount of old stuff. When we're done, 'bufbeg' points
+ to the beginning of the buffer contents, and 'buflim'
+ points just after the end. Return count of new stuff. */
+static int
+fillbuf(save)
+ size_t save;
+{
+ char *nbuffer, *dp, *sp;
+ int cc;
+#if defined(HAVE_WORKING_MMAP)
+ caddr_t maddr;
+#endif
+ static int pagesize;
+
+ if (pagesize == 0 && (pagesize = getpagesize()) == 0)
+ abort();
+
+ if (save > bufsalloc)
+ {
+ while (save > bufsalloc)
+ bufsalloc *= 2;
+ bufalloc = 5 * bufsalloc;
+ nbuffer = valloc(bufalloc + 1);
+ if (!nbuffer)
+ fatal("memory exhausted", 0);
+ }
+ else
+ nbuffer = buffer;
+
+ sp = buflim - save;
+ dp = nbuffer + bufsalloc - save;
+ bufbeg = dp;
+ while (save--)
+ *dp++ = *sp++;
+
+ /* We may have allocated a new, larger buffer. Since
+ there is no portable vfree(), we just have to forget
+ about the old one. Sorry. */
+ buffer = nbuffer;
+
+#if defined(HAVE_WORKING_MMAP)
+ if (bufmapped && bufoffset % pagesize == 0
+ && bufstat.st_size - bufoffset >= bufalloc - bufsalloc)
+ {
+ maddr = buffer + bufsalloc;
+ maddr = mmap(maddr, bufalloc - bufsalloc, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_FIXED, bufdesc, bufoffset);
+ if (maddr == (caddr_t) -1)
+ {
+ fprintf(stderr, "%s: warning: %s: %s\n", filename,
+ strerror(errno));
+ goto tryread;
+ }
+#if 0
+ /* You might thing this (or MADV_WILLNEED) would help,
+ but it doesn't, at least not on a Sun running 4.1.
+ In fact, it actually slows us down about 30%! */
+ madvise(maddr, bufalloc - bufsalloc, MADV_SEQUENTIAL);
+#endif
+ cc = bufalloc - bufsalloc;
+ bufoffset += cc;
+ }
+ else
+ {
+ tryread:
+ /* We come here when we're not going to use mmap() any more.
+ Note that we need to synchronize the file offset the
+ first time through. */
+ if (bufmapped)
+ {
+ bufmapped = 0;
+ lseek(bufdesc, bufoffset, 0);
+ }
+ cc = read(bufdesc, buffer + bufsalloc, bufalloc - bufsalloc);
+ }
+#else
+ cc = read(bufdesc, buffer + bufsalloc, bufalloc - bufsalloc);
+#endif
+ if (cc > 0)
+ buflim = buffer + bufsalloc + cc;
+ else
+ buflim = buffer + bufsalloc;
+ return cc;
+}
+
+/* Flags controlling the style of output. */
+static int out_quiet; /* Suppress all normal output. */
+static int out_invert; /* Print nonmatching stuff. */
+static int out_file; /* Print filenames. */
+static int out_line; /* Print line numbers. */
+static int out_byte; /* Print byte offsets. */
+static int out_before; /* Lines of leading context. */
+static int out_after; /* Lines of trailing context. */
+
+/* Internal variables to keep track of byte count, context, etc. */
+static size_t totalcc; /* Total character count before bufbeg. */
+static char *lastnl; /* Pointer after last newline counted. */
+static char *lastout; /* Pointer after last character output;
+ NULL if no character has been output
+ or if it's conceptually before bufbeg. */
+static size_t totalnl; /* Total newline count before lastnl. */
+static int pending; /* Pending lines of output. */
+
+static void
+nlscan(lim)
+ char *lim;
+{
+ char *beg;
+
+ for (beg = lastnl; beg < lim; ++beg)
+ if (*beg == '\n')
+ ++totalnl;
+ lastnl = beg;
+}
+
+static void
+prline(beg, lim, sep)
+ char *beg;
+ char *lim;
+ char sep;
+{
+ if (out_file)
+ printf("%s%c", filename, sep);
+ if (out_line)
+ {
+ nlscan(beg);
+ printf("%d%c", ++totalnl, sep);
+ lastnl = lim;
+ }
+ if (out_byte)
+ printf("%lu%c", totalcc + (beg - bufbeg), sep);
+ fwrite(beg, 1, lim - beg, stdout);
+ if (ferror(stdout))
+ error("writing output", errno);
+ lastout = lim;
+}
+
+/* Print pending lines of trailing context prior to LIM. */
+static void
+prpending(lim)
+ char *lim;
+{
+ char *nl;
+
+ if (!lastout)
+ lastout = bufbeg;
+ while (pending > 0 && lastout < lim)
+ {
+ --pending;
+ if ((nl = memchr(lastout, '\n', lim - lastout)) != 0)
+ ++nl;
+ else
+ nl = lim;
+ prline(lastout, nl, '-');
+ }
+}
+
+/* Print the lines between BEG and LIM. Deal with context crap.
+ If NLINESP is non-null, store a count of lines between BEG and LIM. */
+static void
+prtext(beg, lim, nlinesp)
+ char *beg;
+ char *lim;
+ int *nlinesp;
+{
+ static int used; /* avoid printing "--" before any output */
+ char *bp, *p, *nl;
+ int i, n;
+
+ if (!out_quiet && pending > 0)
+ prpending(beg);
+
+ p = beg;
+
+ if (!out_quiet)
+ {
+ /* Deal with leading context crap. */
+
+ bp = lastout ? lastout : bufbeg;
+ for (i = 0; i < out_before; ++i)
+ if (p > bp)
+ do
+ --p;
+ while (p > bp && p[-1] != '\n');
+
+ /* We only print the "--" separator if our output is
+ discontiguous from the last output in the file. */
+ if ((out_before || out_after) && used && p != lastout)
+ puts("--");
+
+ while (p < beg)
+ {
+ nl = memchr(p, '\n', beg - p);
+ prline(p, nl + 1, '-');
+ p = nl + 1;
+ }
+ }
+
+ if (nlinesp)
+ {
+ /* Caller wants a line count. */
+ for (n = 0; p < lim; ++n)
+ {
+ if ((nl = memchr(p, '\n', lim - p)) != 0)
+ ++nl;
+ else
+ nl = lim;
+ if (!out_quiet)
+ prline(p, nl, ':');
+ p = nl;
+ }
+ *nlinesp = n;
+ }
+ else
+ if (!out_quiet)
+ prline(beg, lim, ':');
+
+ pending = out_after;
+ used = 1;
+}
+
+/* Scan the specified portion of the buffer, matching lines (or
+ between matching lines if OUT_INVERT is true). Return a count of
+ lines printed. */
+static int
+grepbuf(beg, lim)
+ char *beg;
+ char *lim;
+{
+ int nlines, n;
+ register char *p, *b;
+ char *endp;
+
+ nlines = 0;
+ p = beg;
+ while ((b = (*execute)(p, lim - p, &endp)) != 0)
+ {
+ /* Avoid matching the empty line at the end of the buffer. */
+ if (b == lim && ((b > beg && b[-1] == '\n') || b == beg))
+ break;
+ if (!out_invert)
+ {
+ prtext(b, endp, (int *) 0);
+ nlines += 1;
+ }
+ else if (p < b)
+ {
+ prtext(p, b, &n);
+ nlines += n;
+ }
+ p = endp;
+ }
+ if (out_invert && p < lim)
+ {
+ prtext(p, lim, &n);
+ nlines += n;
+ }
+ return nlines;
+}
+
+/* Search a given file. Return a count of lines printed. */
+static int
+grep(fd)
+ int fd;
+{
+ int nlines, i;
+ size_t residue, save;
+ char *beg, *lim;
+
+ reset(fd);
+
+ totalcc = 0;
+ lastout = 0;
+ totalnl = 0;
+ pending = 0;
+
+ nlines = 0;
+ residue = 0;
+ save = 0;
+
+ for (;;)
+ {
+ if (fillbuf(save) < 0)
+ {
+ error(filename, errno);
+ return nlines;
+ }
+ lastnl = bufbeg;
+ if (lastout)
+ lastout = bufbeg;
+ if (buflim - bufbeg == save)
+ break;
+ beg = bufbeg + save - residue;
+ for (lim = buflim; lim > beg && lim[-1] != '\n'; --lim)
+ ;
+ residue = buflim - lim;
+ if (beg < lim)
+ {
+ nlines += grepbuf(beg, lim);
+ if (pending)
+ prpending(lim);
+ }
+ i = 0;
+ beg = lim;
+ while (i < out_before && beg > bufbeg && beg != lastout)
+ {
+ ++i;
+ do
+ --beg;
+ while (beg > bufbeg && beg[-1] != '\n');
+ }
+ if (beg != lastout)
+ lastout = 0;
+ save = residue + lim - beg;
+ totalcc += buflim - bufbeg - save;
+ if (out_line)
+ nlscan(beg);
+ }
+ if (residue)
+ {
+ nlines += grepbuf(bufbeg + save - residue, buflim);
+ if (pending)
+ prpending(buflim);
+ }
+ return nlines;
+}
+
+static char version[] = "GNU grep version 2.0";
+
+#define USAGE \
+ "usage: %s [-[[AB] ]<num>] [-[CEFGVchilnqsvwx]] [-[ef]] <expr> [<files...>]\n"
+
+static void
+usage()
+{
+ fprintf(stderr, USAGE, prog);
+ exit(2);
+}
+
+/* Go through the matchers vector and look for the specified matcher.
+ If we find it, install it in compile and execute, and return 1. */
+int
+setmatcher(name)
+ char *name;
+{
+ int i;
+
+ for (i = 0; matchers[i].name; ++i)
+ if (strcmp(name, matchers[i].name) == 0)
+ {
+ compile = matchers[i].compile;
+ execute = matchers[i].execute;
+ return 1;
+ }
+ return 0;
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *keys;
+ size_t keycc, oldcc, keyalloc;
+ int keyfound, count_matches, no_filenames, list_files, suppress_errors;
+ int opt, cc, desc, count, status;
+ FILE *fp;
+ extern char *optarg;
+ extern int optind;
+
+ prog = argv[0];
+ if (prog && strrchr(prog, '/'))
+ prog = strrchr(prog, '/') + 1;
+
+ keys = NULL;
+ keycc = 0;
+ keyfound = 0;
+ count_matches = 0;
+ no_filenames = 0;
+ list_files = 0;
+ suppress_errors = 0;
+ matcher = NULL;
+
+ while ((opt = getopt(argc, argv, "0123456789A:B:CEFGVX:bce:f:hiLlnqsvwxy"))
+ != EOF)
+ switch (opt)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ out_before = 10 * out_before + opt - '0';
+ out_after = 10 * out_after + opt - '0';
+ break;
+ case 'A':
+ out_after = atoi(optarg);
+ if (out_after < 0)
+ usage();
+ break;
+ case 'B':
+ out_before = atoi(optarg);
+ if (out_before < 0)
+ usage();
+ break;
+ case 'C':
+ out_before = out_after = 2;
+ break;
+ case 'E':
+ if (matcher && strcmp(matcher, "egrep") != 0)
+ fatal("you may specify only one of -E, -F, or -G", 0);
+ matcher = "posix-egrep";
+ break;
+ case 'F':
+ if (matcher && strcmp(matcher, "fgrep") != 0)
+ fatal("you may specify only one of -E, -F, or -G", 0);;
+ matcher = "fgrep";
+ break;
+ case 'G':
+ if (matcher && strcmp(matcher, "grep") != 0)
+ fatal("you may specify only one of -E, -F, or -G", 0);
+ matcher = "grep";
+ break;
+ case 'V':
+ fprintf(stderr, "%s\n", version);
+ break;
+ case 'X':
+ if (matcher)
+ fatal("matcher already specified", 0);
+ matcher = optarg;
+ break;
+ case 'b':
+ out_byte = 1;
+ break;
+ case 'c':
+ out_quiet = 1;
+ count_matches = 1;
+ break;
+ case 'e':
+ cc = strlen(optarg);
+ keys = xrealloc(keys, keycc + cc + 1);
+ if (keyfound)
+ keys[keycc++] = '\n';
+ strcpy(&keys[keycc], optarg);
+ keycc += cc;
+ keyfound = 1;
+ break;
+ case 'f':
+ fp = strcmp(optarg, "-") != 0 ? fopen(optarg, "r") : stdin;
+ if (!fp)
+ fatal(optarg, errno);
+ for (keyalloc = 1; keyalloc <= keycc; keyalloc *= 2)
+ ;
+ keys = xrealloc(keys, keyalloc);
+ oldcc = keycc;
+ if (keyfound)
+ keys[keycc++] = '\n';
+ while (!feof(fp)
+ && (cc = fread(keys + keycc, 1, keyalloc - keycc, fp)) > 0)
+ {
+ keycc += cc;
+ if (keycc == keyalloc)
+ keys = xrealloc(keys, keyalloc *= 2);
+ }
+ if (fp != stdin)
+ fclose(fp);
+ /* Nuke the final newline to avoid matching a null string. */
+ if (keycc - oldcc > 0 && keys[keycc - 1] == '\n')
+ --keycc;
+ keyfound = 1;
+ break;
+ case 'h':
+ no_filenames = 1;
+ break;
+ case 'i':
+ case 'y': /* For old-timers . . . */
+ match_icase = 1;
+ break;
+ case 'L':
+ /* Like -l, except list files that don't contain matches.
+ Inspired by the same option in Hume's gre. */
+ out_quiet = 1;
+ list_files = -1;
+ break;
+ case 'l':
+ out_quiet = 1;
+ list_files = 1;
+ break;
+ case 'n':
+ out_line = 1;
+ break;
+ case 'q':
+ out_quiet = 1;
+ break;
+ case 's':
+ suppress_errors = 1;
+ break;
+ case 'v':
+ out_invert = 1;
+ break;
+ case 'w':
+ match_words = 1;
+ break;
+ case 'x':
+ match_lines = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+
+ if (!keyfound)
+ if (optind < argc)
+ {
+ keys = argv[optind++];
+ keycc = strlen(keys);
+ }
+ else
+ usage();
+
+ if (!matcher)
+ matcher = prog;
+
+ if (!setmatcher(matcher) && !setmatcher("default"))
+ abort();
+
+ (*compile)(keys, keycc);
+
+ if (argc - optind > 1 && !no_filenames)
+ out_file = 1;
+
+ status = 1;
+
+ if (optind < argc)
+ while (optind < argc)
+ {
+ desc = strcmp(argv[optind], "-") ? open(argv[optind], O_RDONLY) : 0;
+ if (desc < 0)
+ {
+ if (!suppress_errors)
+ error(argv[optind], errno);
+ }
+ else
+ {
+ filename = desc == 0 ? "(standard input)" : argv[optind];
+ count = grep(desc);
+ if (count_matches)
+ {
+ if (out_file)
+ printf("%s:", filename);
+ printf("%d\n", count);
+ }
+ if (count)
+ {
+ status = 0;
+ if (list_files == 1)
+ printf("%s\n", filename);
+ }
+ else if (list_files == -1)
+ printf("%s\n", filename);
+ }
+ if (desc != 0)
+ close(desc);
+ ++optind;
+ }
+ else
+ {
+ filename = "(standard input)";
+ count = grep(0);
+ if (count_matches)
+ printf("%d\n", count);
+ if (count)
+ {
+ status = 0;
+ if (list_files == 1)
+ printf("(standard input)\n");
+ }
+ else if (list_files == -1)
+ printf("(standard input)\n");
+ }
+
+ exit(errseen ? 2 : status);
+}
diff --git a/gnu/usr.bin/grep/grep.h b/gnu/usr.bin/grep/grep.h
new file mode 100644
index 0000000..a3316c5
--- /dev/null
+++ b/gnu/usr.bin/grep/grep.h
@@ -0,0 +1,53 @@
+/* grep.h - interface to grep driver for searching subroutines.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if __STDC__
+
+extern void fatal(const char *, int);
+
+/* Grep.c expects the matchers vector to be terminated
+ by an entry with a NULL name, and to contain at least
+ an entry named "default". */
+
+extern struct matcher
+{
+ char *name;
+ void (*compile)(char *, size_t);
+ char *(*execute)(char *, size_t, char **);
+} matchers[];
+
+#else
+
+extern void fatal();
+
+extern struct matcher
+{
+ char *name;
+ void (*compile)();
+ char *(*execute)();
+} matchers[];
+
+#endif
+
+/* Exported from grep.c. */
+extern char *matcher;
+
+/* The following flags are exported from grep for the matchers
+ to look at. */
+extern int match_icase; /* -i */
+extern int match_words; /* -w */
+extern int match_lines; /* -x */
diff --git a/gnu/usr.bin/grep/kwset.c b/gnu/usr.bin/grep/kwset.c
new file mode 100644
index 0000000..9b09071
--- /dev/null
+++ b/gnu/usr.bin/grep/kwset.c
@@ -0,0 +1,805 @@
+/* kwset.c - search for any of a set of keywords.
+ Copyright 1989 Free Software Foundation
+ Written August 1989 by Mike Haertel.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ The author may be reached (Email) at the address mike@ai.mit.edu,
+ or (US mail) as Mike Haertel c/o Free Software Foundation. */
+
+/* The algorithm implemented by these routines bears a startling resemblence
+ to one discovered by Beate Commentz-Walter, although it is not identical.
+ See "A String Matching Algorithm Fast on the Average," Technical Report,
+ IBM-Germany, Scientific Center Heidelberg, Tiergartenstrasse 15, D-6900
+ Heidelberg, Germany. See also Aho, A.V., and M. Corasick, "Efficient
+ String Matching: An Aid to Bibliographic Search," CACM June 1975,
+ Vol. 18, No. 6, which describes the failure function used below. */
+
+
+#ifdef STDC_HEADERS
+#include <limits.h>
+#include <stdlib.h>
+#else
+#define INT_MAX 2147483647
+#define UCHAR_MAX 255
+#ifdef __STDC__
+#include <stddef.h>
+#else
+#include <sys/types.h>
+#endif
+extern char *malloc();
+extern void free();
+#endif
+
+#ifdef HAVE_MEMCHR
+#include <string.h>
+#ifdef NEED_MEMORY_H
+#include <memory.h>
+#endif
+#else
+#ifdef __STDC__
+extern void *memchr();
+#else
+extern char *memchr();
+#endif
+#endif
+
+#ifdef GREP
+extern char *xmalloc();
+#define malloc xmalloc
+#endif
+
+#include "kwset.h"
+#include "obstack.h"
+
+#define NCHAR (UCHAR_MAX + 1)
+#define obstack_chunk_alloc malloc
+#define obstack_chunk_free free
+
+/* Balanced tree of edges and labels leaving a given trie node. */
+struct tree
+{
+ struct tree *llink; /* Left link; MUST be first field. */
+ struct tree *rlink; /* Right link (to larger labels). */
+ struct trie *trie; /* Trie node pointed to by this edge. */
+ unsigned char label; /* Label on this edge. */
+ char balance; /* Difference in depths of subtrees. */
+};
+
+/* Node of a trie representing a set of reversed keywords. */
+struct trie
+{
+ unsigned int accepting; /* Word index of accepted word, or zero. */
+ struct tree *links; /* Tree of edges leaving this node. */
+ struct trie *parent; /* Parent of this node. */
+ struct trie *next; /* List of all trie nodes in level order. */
+ struct trie *fail; /* Aho-Corasick failure function. */
+ int depth; /* Depth of this node from the root. */
+ int shift; /* Shift function for search failures. */
+ int maxshift; /* Max shift of self and descendents. */
+};
+
+/* Structure returned opaquely to the caller, containing everything. */
+struct kwset
+{
+ struct obstack obstack; /* Obstack for node allocation. */
+ int words; /* Number of words in the trie. */
+ struct trie *trie; /* The trie itself. */
+ int mind; /* Minimum depth of an accepting node. */
+ int maxd; /* Maximum depth of any node. */
+ unsigned char delta[NCHAR]; /* Delta table for rapid search. */
+ struct trie *next[NCHAR]; /* Table of children of the root. */
+ char *target; /* Target string if there's only one. */
+ int mind2; /* Used in Boyer-Moore search for one string. */
+ char *trans; /* Character translation table. */
+};
+
+/* Allocate and initialize a keyword set object, returning an opaque
+ pointer to it. Return NULL if memory is not available. */
+kwset_t
+kwsalloc(trans)
+ char *trans;
+{
+ struct kwset *kwset;
+
+ kwset = (struct kwset *) malloc(sizeof (struct kwset));
+ if (!kwset)
+ return 0;
+
+ obstack_init(&kwset->obstack);
+ kwset->words = 0;
+ kwset->trie
+ = (struct trie *) obstack_alloc(&kwset->obstack, sizeof (struct trie));
+ if (!kwset->trie)
+ {
+ kwsfree((kwset_t) kwset);
+ return 0;
+ }
+ kwset->trie->accepting = 0;
+ kwset->trie->links = 0;
+ kwset->trie->parent = 0;
+ kwset->trie->next = 0;
+ kwset->trie->fail = 0;
+ kwset->trie->depth = 0;
+ kwset->trie->shift = 0;
+ kwset->mind = INT_MAX;
+ kwset->maxd = -1;
+ kwset->target = 0;
+ kwset->trans = trans;
+
+ return (kwset_t) kwset;
+}
+
+/* Add the given string to the contents of the keyword set. Return NULL
+ for success, an error message otherwise. */
+char *
+kwsincr(kws, text, len)
+ kwset_t kws;
+ char *text;
+ size_t len;
+{
+ struct kwset *kwset;
+ register struct trie *trie;
+ register unsigned char label;
+ register struct tree *link;
+ register int depth;
+ struct tree *links[12];
+ enum { L, R } dirs[12];
+ struct tree *t, *r, *l, *rl, *lr;
+
+ kwset = (struct kwset *) kws;
+ trie = kwset->trie;
+ text += len;
+
+ /* Descend the trie (built of reversed keywords) character-by-character,
+ installing new nodes when necessary. */
+ while (len--)
+ {
+ label = kwset->trans ? kwset->trans[(unsigned char) *--text] : *--text;
+
+ /* Descend the tree of outgoing links for this trie node,
+ looking for the current character and keeping track
+ of the path followed. */
+ link = trie->links;
+ links[0] = (struct tree *) &trie->links;
+ dirs[0] = L;
+ depth = 1;
+
+ while (link && label != link->label)
+ {
+ links[depth] = link;
+ if (label < link->label)
+ dirs[depth++] = L, link = link->llink;
+ else
+ dirs[depth++] = R, link = link->rlink;
+ }
+
+ /* The current character doesn't have an outgoing link at
+ this trie node, so build a new trie node and install
+ a link in the current trie node's tree. */
+ if (!link)
+ {
+ link = (struct tree *) obstack_alloc(&kwset->obstack,
+ sizeof (struct tree));
+ if (!link)
+ return "memory exhausted";
+ link->llink = 0;
+ link->rlink = 0;
+ link->trie = (struct trie *) obstack_alloc(&kwset->obstack,
+ sizeof (struct trie));
+ if (!link->trie)
+ return "memory exhausted";
+ link->trie->accepting = 0;
+ link->trie->links = 0;
+ link->trie->parent = trie;
+ link->trie->next = 0;
+ link->trie->fail = 0;
+ link->trie->depth = trie->depth + 1;
+ link->trie->shift = 0;
+ link->label = label;
+ link->balance = 0;
+
+ /* Install the new tree node in its parent. */
+ if (dirs[--depth] == L)
+ links[depth]->llink = link;
+ else
+ links[depth]->rlink = link;
+
+ /* Back up the tree fixing the balance flags. */
+ while (depth && !links[depth]->balance)
+ {
+ if (dirs[depth] == L)
+ --links[depth]->balance;
+ else
+ ++links[depth]->balance;
+ --depth;
+ }
+
+ /* Rebalance the tree by pointer rotations if necessary. */
+ if (depth && ((dirs[depth] == L && --links[depth]->balance)
+ || (dirs[depth] == R && ++links[depth]->balance)))
+ {
+ switch (links[depth]->balance)
+ {
+ case (char) -2:
+ switch (dirs[depth + 1])
+ {
+ case L:
+ r = links[depth], t = r->llink, rl = t->rlink;
+ t->rlink = r, r->llink = rl;
+ t->balance = r->balance = 0;
+ break;
+ case R:
+ r = links[depth], l = r->llink, t = l->rlink;
+ rl = t->rlink, lr = t->llink;
+ t->llink = l, l->rlink = lr, t->rlink = r, r->llink = rl;
+ l->balance = t->balance != 1 ? 0 : -1;
+ r->balance = t->balance != (char) -1 ? 0 : 1;
+ t->balance = 0;
+ break;
+ }
+ break;
+ case 2:
+ switch (dirs[depth + 1])
+ {
+ case R:
+ l = links[depth], t = l->rlink, lr = t->llink;
+ t->llink = l, l->rlink = lr;
+ t->balance = l->balance = 0;
+ break;
+ case L:
+ l = links[depth], r = l->rlink, t = r->llink;
+ lr = t->llink, rl = t->rlink;
+ t->llink = l, l->rlink = lr, t->rlink = r, r->llink = rl;
+ l->balance = t->balance != 1 ? 0 : -1;
+ r->balance = t->balance != (char) -1 ? 0 : 1;
+ t->balance = 0;
+ break;
+ }
+ break;
+ }
+
+ if (dirs[depth - 1] == L)
+ links[depth - 1]->llink = t;
+ else
+ links[depth - 1]->rlink = t;
+ }
+ }
+
+ trie = link->trie;
+ }
+
+ /* Mark the node we finally reached as accepting, encoding the
+ index number of this word in the keyword set so far. */
+ if (!trie->accepting)
+ trie->accepting = 1 + 2 * kwset->words;
+ ++kwset->words;
+
+ /* Keep track of the longest and shortest string of the keyword set. */
+ if (trie->depth < kwset->mind)
+ kwset->mind = trie->depth;
+ if (trie->depth > kwset->maxd)
+ kwset->maxd = trie->depth;
+
+ return 0;
+}
+
+/* Enqueue the trie nodes referenced from the given tree in the
+ given queue. */
+static void
+enqueue(tree, last)
+ struct tree *tree;
+ struct trie **last;
+{
+ if (!tree)
+ return;
+ enqueue(tree->llink, last);
+ enqueue(tree->rlink, last);
+ (*last) = (*last)->next = tree->trie;
+}
+
+/* Compute the Aho-Corasick failure function for the trie nodes referenced
+ from the given tree, given the failure function for their parent as
+ well as a last resort failure node. */
+static void
+treefails(tree, fail, recourse)
+ register struct tree *tree;
+ struct trie *fail;
+ struct trie *recourse;
+{
+ register struct tree *link;
+
+ if (!tree)
+ return;
+
+ treefails(tree->llink, fail, recourse);
+ treefails(tree->rlink, fail, recourse);
+
+ /* Find, in the chain of fails going back to the root, the first
+ node that has a descendent on the current label. */
+ while (fail)
+ {
+ link = fail->links;
+ while (link && tree->label != link->label)
+ if (tree->label < link->label)
+ link = link->llink;
+ else
+ link = link->rlink;
+ if (link)
+ {
+ tree->trie->fail = link->trie;
+ return;
+ }
+ fail = fail->fail;
+ }
+
+ tree->trie->fail = recourse;
+}
+
+/* Set delta entries for the links of the given tree such that
+ the preexisting delta value is larger than the current depth. */
+static void
+treedelta(tree, depth, delta)
+ register struct tree *tree;
+ register unsigned int depth;
+ unsigned char delta[];
+{
+ if (!tree)
+ return;
+ treedelta(tree->llink, depth, delta);
+ treedelta(tree->rlink, depth, delta);
+ if (depth < delta[tree->label])
+ delta[tree->label] = depth;
+}
+
+/* Return true if A has every label in B. */
+static int
+hasevery(a, b)
+ register struct tree *a;
+ register struct tree *b;
+{
+ if (!b)
+ return 1;
+ if (!hasevery(a, b->llink))
+ return 0;
+ if (!hasevery(a, b->rlink))
+ return 0;
+ while (a && b->label != a->label)
+ if (b->label < a->label)
+ a = a->llink;
+ else
+ a = a->rlink;
+ return !!a;
+}
+
+/* Compute a vector, indexed by character code, of the trie nodes
+ referenced from the given tree. */
+static void
+treenext(tree, next)
+ struct tree *tree;
+ struct trie *next[];
+{
+ if (!tree)
+ return;
+ treenext(tree->llink, next);
+ treenext(tree->rlink, next);
+ next[tree->label] = tree->trie;
+}
+
+/* Compute the shift for each trie node, as well as the delta
+ table and next cache for the given keyword set. */
+char *
+kwsprep(kws)
+ kwset_t kws;
+{
+ register struct kwset *kwset;
+ register int i;
+ register struct trie *curr, *fail;
+ register char *trans;
+ unsigned char delta[NCHAR];
+ struct trie *last, *next[NCHAR];
+
+ kwset = (struct kwset *) kws;
+
+ /* Initial values for the delta table; will be changed later. The
+ delta entry for a given character is the smallest depth of any
+ node at which an outgoing edge is labeled by that character. */
+ if (kwset->mind < 256)
+ for (i = 0; i < NCHAR; ++i)
+ delta[i] = kwset->mind;
+ else
+ for (i = 0; i < NCHAR; ++i)
+ delta[i] = 255;
+
+ /* Check if we can use the simple boyer-moore algorithm, instead
+ of the hairy commentz-walter algorithm. */
+ if (kwset->words == 1 && kwset->trans == 0)
+ {
+ /* Looking for just one string. Extract it from the trie. */
+ kwset->target = obstack_alloc(&kwset->obstack, kwset->mind);
+ for (i = kwset->mind - 1, curr = kwset->trie; i >= 0; --i)
+ {
+ kwset->target[i] = curr->links->label;
+ curr = curr->links->trie;
+ }
+ /* Build the Boyer Moore delta. Boy that's easy compared to CW. */
+ for (i = 0; i < kwset->mind; ++i)
+ delta[(unsigned char) kwset->target[i]] = kwset->mind - (i + 1);
+ kwset->mind2 = kwset->mind;
+ /* Find the minimal delta2 shift that we might make after
+ a backwards match has failed. */
+ for (i = 0; i < kwset->mind - 1; ++i)
+ if (kwset->target[i] == kwset->target[kwset->mind - 1])
+ kwset->mind2 = kwset->mind - (i + 1);
+ }
+ else
+ {
+ /* Traverse the nodes of the trie in level order, simultaneously
+ computing the delta table, failure function, and shift function. */
+ for (curr = last = kwset->trie; curr; curr = curr->next)
+ {
+ /* Enqueue the immediate descendents in the level order queue. */
+ enqueue(curr->links, &last);
+
+ curr->shift = kwset->mind;
+ curr->maxshift = kwset->mind;
+
+ /* Update the delta table for the descendents of this node. */
+ treedelta(curr->links, curr->depth, delta);
+
+ /* Compute the failure function for the decendents of this node. */
+ treefails(curr->links, curr->fail, kwset->trie);
+
+ /* Update the shifts at each node in the current node's chain
+ of fails back to the root. */
+ for (fail = curr->fail; fail; fail = fail->fail)
+ {
+ /* If the current node has some outgoing edge that the fail
+ doesn't, then the shift at the fail should be no larger
+ than the difference of their depths. */
+ if (!hasevery(fail->links, curr->links))
+ if (curr->depth - fail->depth < fail->shift)
+ fail->shift = curr->depth - fail->depth;
+
+ /* If the current node is accepting then the shift at the
+ fail and its descendents should be no larger than the
+ difference of their depths. */
+ if (curr->accepting && fail->maxshift > curr->depth - fail->depth)
+ fail->maxshift = curr->depth - fail->depth;
+ }
+ }
+
+ /* Traverse the trie in level order again, fixing up all nodes whose
+ shift exceeds their inherited maxshift. */
+ for (curr = kwset->trie->next; curr; curr = curr->next)
+ {
+ if (curr->maxshift > curr->parent->maxshift)
+ curr->maxshift = curr->parent->maxshift;
+ if (curr->shift > curr->maxshift)
+ curr->shift = curr->maxshift;
+ }
+
+ /* Create a vector, indexed by character code, of the outgoing links
+ from the root node. */
+ for (i = 0; i < NCHAR; ++i)
+ next[i] = 0;
+ treenext(kwset->trie->links, next);
+
+ if ((trans = kwset->trans) != 0)
+ for (i = 0; i < NCHAR; ++i)
+ kwset->next[i] = next[(unsigned char) trans[i]];
+ else
+ for (i = 0; i < NCHAR; ++i)
+ kwset->next[i] = next[i];
+ }
+
+ /* Fix things up for any translation table. */
+ if ((trans = kwset->trans) != 0)
+ for (i = 0; i < NCHAR; ++i)
+ kwset->delta[i] = delta[(unsigned char) trans[i]];
+ else
+ for (i = 0; i < NCHAR; ++i)
+ kwset->delta[i] = delta[i];
+
+ return 0;
+}
+
+#define U(C) ((unsigned char) (C))
+
+/* Fast boyer-moore search. */
+static char *
+bmexec(kws, text, size)
+ kwset_t kws;
+ char *text;
+ size_t size;
+{
+ struct kwset *kwset;
+ register unsigned char *d1;
+ register char *ep, *sp, *tp;
+ register int d, gc, i, len, md2;
+
+ kwset = (struct kwset *) kws;
+ len = kwset->mind;
+
+ if (len == 0)
+ return text;
+ if (len > size)
+ return 0;
+ if (len == 1)
+ return memchr(text, kwset->target[0], size);
+
+ d1 = kwset->delta;
+ sp = kwset->target + len;
+ gc = U(sp[-2]);
+ md2 = kwset->mind2;
+ tp = text + len;
+
+ /* Significance of 12: 1 (initial offset) + 10 (skip loop) + 1 (md2). */
+ if (size > 12 * len)
+ /* 11 is not a bug, the initial offset happens only once. */
+ for (ep = text + size - 11 * len;;)
+ {
+ while (tp <= ep)
+ {
+ d = d1[U(tp[-1])], tp += d;
+ d = d1[U(tp[-1])], tp += d;
+ if (d == 0)
+ goto found;
+ d = d1[U(tp[-1])], tp += d;
+ d = d1[U(tp[-1])], tp += d;
+ d = d1[U(tp[-1])], tp += d;
+ if (d == 0)
+ goto found;
+ d = d1[U(tp[-1])], tp += d;
+ d = d1[U(tp[-1])], tp += d;
+ d = d1[U(tp[-1])], tp += d;
+ if (d == 0)
+ goto found;
+ d = d1[U(tp[-1])], tp += d;
+ d = d1[U(tp[-1])], tp += d;
+ }
+ break;
+ found:
+ if (U(tp[-2]) == gc)
+ {
+ for (i = 3; i <= len && U(tp[-i]) == U(sp[-i]); ++i)
+ ;
+ if (i > len)
+ return tp - len;
+ }
+ tp += md2;
+ }
+
+ /* Now we have only a few characters left to search. We
+ carefully avoid ever producing an out-of-bounds pointer. */
+ ep = text + size;
+ d = d1[U(tp[-1])];
+ while (d <= ep - tp)
+ {
+ d = d1[U((tp += d)[-1])];
+ if (d != 0)
+ continue;
+ if (tp[-2] == gc)
+ {
+ for (i = 3; i <= len && U(tp[-i]) == U(sp[-i]); ++i)
+ ;
+ if (i > len)
+ return tp - len;
+ }
+ d = md2;
+ }
+
+ return 0;
+}
+
+/* Hairy multiple string search. */
+static char *
+cwexec(kws, text, len, kwsmatch)
+ kwset_t kws;
+ char *text;
+ size_t len;
+ struct kwsmatch *kwsmatch;
+{
+ struct kwset *kwset;
+ struct trie **next, *trie, *accept;
+ char *beg, *lim, *mch, *lmch;
+ register unsigned char c, *delta;
+ register int d;
+ register char *end, *qlim;
+ register struct tree *tree;
+ register char *trans;
+
+ /* Initialize register copies and look for easy ways out. */
+ kwset = (struct kwset *) kws;
+ if (len < kwset->mind)
+ return 0;
+ next = kwset->next;
+ delta = kwset->delta;
+ trans = kwset->trans;
+ lim = text + len;
+ end = text;
+ if ((d = kwset->mind) != 0)
+ mch = 0;
+ else
+ {
+ mch = text, accept = kwset->trie;
+ goto match;
+ }
+
+ if (len >= 4 * kwset->mind)
+ qlim = lim - 4 * kwset->mind;
+ else
+ qlim = 0;
+
+ while (lim - end >= d)
+ {
+ if (qlim && end <= qlim)
+ {
+ end += d - 1;
+ while ((d = delta[c = *end]) && end < qlim)
+ {
+ end += d;
+ end += delta[(unsigned char) *end];
+ end += delta[(unsigned char) *end];
+ }
+ ++end;
+ }
+ else
+ d = delta[c = (end += d)[-1]];
+ if (d)
+ continue;
+ beg = end - 1;
+ trie = next[c];
+ if (trie->accepting)
+ {
+ mch = beg;
+ accept = trie;
+ }
+ d = trie->shift;
+ while (beg > text)
+ {
+ c = trans ? trans[(unsigned char) *--beg] : *--beg;
+ tree = trie->links;
+ while (tree && c != tree->label)
+ if (c < tree->label)
+ tree = tree->llink;
+ else
+ tree = tree->rlink;
+ if (tree)
+ {
+ trie = tree->trie;
+ if (trie->accepting)
+ {
+ mch = beg;
+ accept = trie;
+ }
+ }
+ else
+ break;
+ d = trie->shift;
+ }
+ if (mch)
+ goto match;
+ }
+ return 0;
+
+ match:
+ /* Given a known match, find the longest possible match anchored
+ at or before its starting point. This is nearly a verbatim
+ copy of the preceding main search loops. */
+ if (lim - mch > kwset->maxd)
+ lim = mch + kwset->maxd;
+ lmch = 0;
+ d = 1;
+ while (lim - end >= d)
+ {
+ if ((d = delta[c = (end += d)[-1]]) != 0)
+ continue;
+ beg = end - 1;
+ if (!(trie = next[c]))
+ {
+ d = 1;
+ continue;
+ }
+ if (trie->accepting && beg <= mch)
+ {
+ lmch = beg;
+ accept = trie;
+ }
+ d = trie->shift;
+ while (beg > text)
+ {
+ c = trans ? trans[(unsigned char) *--beg] : *--beg;
+ tree = trie->links;
+ while (tree && c != tree->label)
+ if (c < tree->label)
+ tree = tree->llink;
+ else
+ tree = tree->rlink;
+ if (tree)
+ {
+ trie = tree->trie;
+ if (trie->accepting && beg <= mch)
+ {
+ lmch = beg;
+ accept = trie;
+ }
+ }
+ else
+ break;
+ d = trie->shift;
+ }
+ if (lmch)
+ {
+ mch = lmch;
+ goto match;
+ }
+ if (!d)
+ d = 1;
+ }
+
+ if (kwsmatch)
+ {
+ kwsmatch->index = accept->accepting / 2;
+ kwsmatch->beg[0] = mch;
+ kwsmatch->size[0] = accept->depth;
+ }
+ return mch;
+}
+
+/* Search through the given text for a match of any member of the
+ given keyword set. Return a pointer to the first character of
+ the matching substring, or NULL if no match is found. If FOUNDLEN
+ is non-NULL store in the referenced location the length of the
+ matching substring. Similarly, if FOUNDIDX is non-NULL, store
+ in the referenced location the index number of the particular
+ keyword matched. */
+char *
+kwsexec(kws, text, size, kwsmatch)
+ kwset_t kws;
+ char *text;
+ size_t size;
+ struct kwsmatch *kwsmatch;
+{
+ struct kwset *kwset;
+ char *ret;
+
+ kwset = (struct kwset *) kws;
+ if (kwset->words == 1 && kwset->trans == 0)
+ {
+ ret = bmexec(kws, text, size);
+ if (kwsmatch != 0 && ret != 0)
+ {
+ kwsmatch->index = 0;
+ kwsmatch->beg[0] = ret;
+ kwsmatch->size[0] = kwset->mind;
+ }
+ return ret;
+ }
+ else
+ return cwexec(kws, text, size, kwsmatch);
+}
+
+/* Free the components of the given keyword set. */
+void
+kwsfree(kws)
+ kwset_t kws;
+{
+ struct kwset *kwset;
+
+ kwset = (struct kwset *) kws;
+ obstack_free(&kwset->obstack, 0);
+ free(kws);
+}
diff --git a/gnu/usr.bin/grep/kwset.h b/gnu/usr.bin/grep/kwset.h
new file mode 100644
index 0000000..95f62e7
--- /dev/null
+++ b/gnu/usr.bin/grep/kwset.h
@@ -0,0 +1,69 @@
+/* kwset.h - header declaring the keyword set library.
+ Copyright 1989 Free Software Foundation
+ Written August 1989 by Mike Haertel.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ The author may be reached (Email) at the address mike@ai.mit.edu,
+ or (US mail) as Mike Haertel c/o Free Software Foundation. */
+
+struct kwsmatch
+{
+ int index; /* Index number of matching keyword. */
+ char *beg[1]; /* Begin pointer for each submatch. */
+ size_t size[1]; /* Length of each submatch. */
+};
+
+#if __STDC__
+
+typedef void *kwset_t;
+
+/* Return an opaque pointer to a newly allocated keyword set, or NULL
+ if enough memory cannot be obtained. The argument if non-NULL
+ specifies a table of character translations to be applied to all
+ pattern and search text. */
+extern kwset_t kwsalloc(char *);
+
+/* Incrementally extend the keyword set to include the given string.
+ Return NULL for success, or an error message. Remember an index
+ number for each keyword included in the set. */
+extern char *kwsincr(kwset_t, char *, size_t);
+
+/* When the keyword set has been completely built, prepare it for
+ use. Return NULL for success, or an error message. */
+extern char *kwsprep(kwset_t);
+
+/* Search through the given buffer for a member of the keyword set.
+ Return a pointer to the leftmost longest match found, or NULL if
+ no match is found. If foundlen is non-NULL, store the length of
+ the matching substring in the integer it points to. Similarly,
+ if foundindex is non-NULL, store the index of the particular
+ keyword found therein. */
+extern char *kwsexec(kwset_t, char *, size_t, struct kwsmatch *);
+
+/* Deallocate the given keyword set and all its associated storage. */
+extern void kwsfree(kwset_t);
+
+#else
+
+typedef char *kwset_t;
+
+extern kwset_t kwsalloc();
+extern char *kwsincr();
+extern char *kwsprep();
+extern char *kwsexec();
+extern void kwsfree();
+
+#endif
diff --git a/gnu/usr.bin/grep/obstack.c b/gnu/usr.bin/grep/obstack.c
new file mode 100644
index 0000000..7b9d3b9
--- /dev/null
+++ b/gnu/usr.bin/grep/obstack.c
@@ -0,0 +1,454 @@
+/* obstack.c - subroutines used implicitly by object stack macros
+ Copyright (C) 1988, 1993 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "obstack.h"
+
+/* This is just to get __GNU_LIBRARY__ defined. */
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+#ifdef __STDC__
+#define POINTER void *
+#else
+#define POINTER char *
+#endif
+
+/* Determine default alignment. */
+struct fooalign {char x; double d;};
+#define DEFAULT_ALIGNMENT \
+ ((PTR_INT_TYPE) ((char *)&((struct fooalign *) 0)->d - (char *)0))
+/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT.
+ But in fact it might be less smart and round addresses to as much as
+ DEFAULT_ROUNDING. So we prepare for it to do that. */
+union fooround {long x; double d;};
+#define DEFAULT_ROUNDING (sizeof (union fooround))
+
+/* When we copy a long block of data, this is the unit to do it with.
+ On some machines, copying successive ints does not work;
+ in such a case, redefine COPYING_UNIT to `long' (if that works)
+ or `char' as a last resort. */
+#ifndef COPYING_UNIT
+#define COPYING_UNIT int
+#endif
+
+/* The non-GNU-C macros copy the obstack into this global variable
+ to avoid multiple evaluation. */
+
+struct obstack *_obstack;
+
+/* Define a macro that either calls functions with the traditional malloc/free
+ calling interface, or calls functions with the mmalloc/mfree interface
+ (that adds an extra first argument), based on the state of use_extra_arg.
+ For free, do not use ?:, since some compilers, like the MIPS compilers,
+ do not allow (expr) ? void : void. */
+
+#define CALL_CHUNKFUN(h, size) \
+ (((h) -> use_extra_arg) \
+ ? (*(h)->chunkfun) ((h)->extra_arg, (size)) \
+ : (*(h)->chunkfun) ((size)))
+
+#define CALL_FREEFUN(h, old_chunk) \
+ do { \
+ if ((h) -> use_extra_arg) \
+ (*(h)->freefun) ((h)->extra_arg, (old_chunk)); \
+ else \
+ (*(h)->freefun) ((old_chunk)); \
+ } while (0)
+
+
+/* Initialize an obstack H for use. Specify chunk size SIZE (0 means default).
+ Objects start on multiples of ALIGNMENT (0 means use default).
+ CHUNKFUN is the function to use to allocate chunks,
+ and FREEFUN the function to free them. */
+
+void
+_obstack_begin (h, size, alignment, chunkfun, freefun)
+ struct obstack *h;
+ int size;
+ int alignment;
+ POINTER (*chunkfun) ();
+ void (*freefun) ();
+{
+ register struct _obstack_chunk* chunk; /* points to new chunk */
+
+ if (alignment == 0)
+ alignment = DEFAULT_ALIGNMENT;
+ if (size == 0)
+ /* Default size is what GNU malloc can fit in a 4096-byte block. */
+ {
+ /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
+ Use the values for range checking, because if range checking is off,
+ the extra bytes won't be missed terribly, but if range checking is on
+ and we used a larger request, a whole extra 4096 bytes would be
+ allocated.
+
+ These number are irrelevant to the new GNU malloc. I suspect it is
+ less sensitive to the size of the request. */
+ int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
+ + 4 + DEFAULT_ROUNDING - 1)
+ & ~(DEFAULT_ROUNDING - 1));
+ size = 4096 - extra;
+ }
+
+ h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun;
+ h->freefun = freefun;
+ h->chunk_size = size;
+ h->alignment_mask = alignment - 1;
+ h->use_extra_arg = 0;
+
+ chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
+ h->next_free = h->object_base = chunk->contents;
+ h->chunk_limit = chunk->limit
+ = (char *) chunk + h->chunk_size;
+ chunk->prev = 0;
+ /* The initial chunk now contains no empty object. */
+ h->maybe_empty_object = 0;
+}
+
+void
+_obstack_begin_1 (h, size, alignment, chunkfun, freefun, arg)
+ struct obstack *h;
+ int size;
+ int alignment;
+ POINTER (*chunkfun) ();
+ void (*freefun) ();
+ POINTER arg;
+{
+ register struct _obstack_chunk* chunk; /* points to new chunk */
+
+ if (alignment == 0)
+ alignment = DEFAULT_ALIGNMENT;
+ if (size == 0)
+ /* Default size is what GNU malloc can fit in a 4096-byte block. */
+ {
+ /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
+ Use the values for range checking, because if range checking is off,
+ the extra bytes won't be missed terribly, but if range checking is on
+ and we used a larger request, a whole extra 4096 bytes would be
+ allocated.
+
+ These number are irrelevant to the new GNU malloc. I suspect it is
+ less sensitive to the size of the request. */
+ int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
+ + 4 + DEFAULT_ROUNDING - 1)
+ & ~(DEFAULT_ROUNDING - 1));
+ size = 4096 - extra;
+ }
+
+ h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun;
+ h->freefun = freefun;
+ h->chunk_size = size;
+ h->alignment_mask = alignment - 1;
+ h->extra_arg = arg;
+ h->use_extra_arg = 1;
+
+ chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
+ h->next_free = h->object_base = chunk->contents;
+ h->chunk_limit = chunk->limit
+ = (char *) chunk + h->chunk_size;
+ chunk->prev = 0;
+ /* The initial chunk now contains no empty object. */
+ h->maybe_empty_object = 0;
+}
+
+/* Allocate a new current chunk for the obstack *H
+ on the assumption that LENGTH bytes need to be added
+ to the current object, or a new object of length LENGTH allocated.
+ Copies any partial object from the end of the old chunk
+ to the beginning of the new one. */
+
+void
+_obstack_newchunk (h, length)
+ struct obstack *h;
+ int length;
+{
+ register struct _obstack_chunk* old_chunk = h->chunk;
+ register struct _obstack_chunk* new_chunk;
+ register long new_size;
+ register int obj_size = h->next_free - h->object_base;
+ register int i;
+ int already;
+
+ /* Compute size for new chunk. */
+ new_size = (obj_size + length) + (obj_size >> 3) + 100;
+ if (new_size < h->chunk_size)
+ new_size = h->chunk_size;
+
+ /* Allocate and initialize the new chunk. */
+ new_chunk = h->chunk = CALL_CHUNKFUN (h, new_size);
+ new_chunk->prev = old_chunk;
+ new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size;
+
+ /* Move the existing object to the new chunk.
+ Word at a time is fast and is safe if the object
+ is sufficiently aligned. */
+ if (h->alignment_mask + 1 >= DEFAULT_ALIGNMENT)
+ {
+ for (i = obj_size / sizeof (COPYING_UNIT) - 1;
+ i >= 0; i--)
+ ((COPYING_UNIT *)new_chunk->contents)[i]
+ = ((COPYING_UNIT *)h->object_base)[i];
+ /* We used to copy the odd few remaining bytes as one extra COPYING_UNIT,
+ but that can cross a page boundary on a machine
+ which does not do strict alignment for COPYING_UNITS. */
+ already = obj_size / sizeof (COPYING_UNIT) * sizeof (COPYING_UNIT);
+ }
+ else
+ already = 0;
+ /* Copy remaining bytes one by one. */
+ for (i = already; i < obj_size; i++)
+ new_chunk->contents[i] = h->object_base[i];
+
+ /* If the object just copied was the only data in OLD_CHUNK,
+ free that chunk and remove it from the chain.
+ But not if that chunk might contain an empty object. */
+ if (h->object_base == old_chunk->contents && ! h->maybe_empty_object)
+ {
+ new_chunk->prev = old_chunk->prev;
+ CALL_FREEFUN (h, old_chunk);
+ }
+
+ h->object_base = new_chunk->contents;
+ h->next_free = h->object_base + obj_size;
+ /* The new chunk certainly contains no empty object yet. */
+ h->maybe_empty_object = 0;
+}
+
+/* Return nonzero if object OBJ has been allocated from obstack H.
+ This is here for debugging.
+ If you use it in a program, you are probably losing. */
+
+int
+_obstack_allocated_p (h, obj)
+ struct obstack *h;
+ POINTER obj;
+{
+ register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk* plp; /* point to previous chunk if any */
+
+ lp = (h)->chunk;
+ /* We use >= rather than > since the object cannot be exactly at
+ the beginning of the chunk but might be an empty object exactly
+ at the end of an adjacent chunk. */
+ while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj))
+ {
+ plp = lp->prev;
+ lp = plp;
+ }
+ return lp != 0;
+}
+
+/* Free objects in obstack H, including OBJ and everything allocate
+ more recently than OBJ. If OBJ is zero, free everything in H. */
+
+#undef obstack_free
+
+/* This function has two names with identical definitions.
+ This is the first one, called from non-ANSI code. */
+
+void
+_obstack_free (h, obj)
+ struct obstack *h;
+ POINTER obj;
+{
+ register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk* plp; /* point to previous chunk if any */
+
+ lp = h->chunk;
+ /* We use >= because there cannot be an object at the beginning of a chunk.
+ But there can be an empty object at that address
+ at the end of another chunk. */
+ while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj))
+ {
+ plp = lp->prev;
+ CALL_FREEFUN (h, lp);
+ lp = plp;
+ /* If we switch chunks, we can't tell whether the new current
+ chunk contains an empty object, so assume that it may. */
+ h->maybe_empty_object = 1;
+ }
+ if (lp)
+ {
+ h->object_base = h->next_free = (char *)(obj);
+ h->chunk_limit = lp->limit;
+ h->chunk = lp;
+ }
+ else if (obj != 0)
+ /* obj is not in any of the chunks! */
+ abort ();
+}
+
+/* This function is used from ANSI code. */
+
+void
+obstack_free (h, obj)
+ struct obstack *h;
+ POINTER obj;
+{
+ register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */
+ register struct _obstack_chunk* plp; /* point to previous chunk if any */
+
+ lp = h->chunk;
+ /* We use >= because there cannot be an object at the beginning of a chunk.
+ But there can be an empty object at that address
+ at the end of another chunk. */
+ while (lp != 0 && ((POINTER)lp >= obj || (POINTER)(lp)->limit < obj))
+ {
+ plp = lp->prev;
+ CALL_FREEFUN (h, lp);
+ lp = plp;
+ /* If we switch chunks, we can't tell whether the new current
+ chunk contains an empty object, so assume that it may. */
+ h->maybe_empty_object = 1;
+ }
+ if (lp)
+ {
+ h->object_base = h->next_free = (char *)(obj);
+ h->chunk_limit = lp->limit;
+ h->chunk = lp;
+ }
+ else if (obj != 0)
+ /* obj is not in any of the chunks! */
+ abort ();
+}
+
+#if 0
+/* These are now turned off because the applications do not use it
+ and it uses bcopy via obstack_grow, which causes trouble on sysV. */
+
+/* Now define the functional versions of the obstack macros.
+ Define them to simply use the corresponding macros to do the job. */
+
+#ifdef __STDC__
+/* These function definitions do not work with non-ANSI preprocessors;
+ they won't pass through the macro names in parentheses. */
+
+/* The function names appear in parentheses in order to prevent
+ the macro-definitions of the names from being expanded there. */
+
+POINTER (obstack_base) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_base (obstack);
+}
+
+POINTER (obstack_next_free) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_next_free (obstack);
+}
+
+int (obstack_object_size) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_object_size (obstack);
+}
+
+int (obstack_room) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_room (obstack);
+}
+
+void (obstack_grow) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ obstack_grow (obstack, pointer, length);
+}
+
+void (obstack_grow0) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ obstack_grow0 (obstack, pointer, length);
+}
+
+void (obstack_1grow) (obstack, character)
+ struct obstack *obstack;
+ int character;
+{
+ obstack_1grow (obstack, character);
+}
+
+void (obstack_blank) (obstack, length)
+ struct obstack *obstack;
+ int length;
+{
+ obstack_blank (obstack, length);
+}
+
+void (obstack_1grow_fast) (obstack, character)
+ struct obstack *obstack;
+ int character;
+{
+ obstack_1grow_fast (obstack, character);
+}
+
+void (obstack_blank_fast) (obstack, length)
+ struct obstack *obstack;
+ int length;
+{
+ obstack_blank_fast (obstack, length);
+}
+
+POINTER (obstack_finish) (obstack)
+ struct obstack *obstack;
+{
+ return obstack_finish (obstack);
+}
+
+POINTER (obstack_alloc) (obstack, length)
+ struct obstack *obstack;
+ int length;
+{
+ return obstack_alloc (obstack, length);
+}
+
+POINTER (obstack_copy) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ return obstack_copy (obstack, pointer, length);
+}
+
+POINTER (obstack_copy0) (obstack, pointer, length)
+ struct obstack *obstack;
+ POINTER pointer;
+ int length;
+{
+ return obstack_copy0 (obstack, pointer, length);
+}
+
+#endif /* __STDC__ */
+
+#endif /* 0 */
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
diff --git a/gnu/usr.bin/grep/obstack.h b/gnu/usr.bin/grep/obstack.h
new file mode 100644
index 0000000..8a18e45
--- /dev/null
+++ b/gnu/usr.bin/grep/obstack.h
@@ -0,0 +1,484 @@
+/* obstack.h - object stack macros
+ Copyright (C) 1988, 1992 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Summary:
+
+All the apparent functions defined here are macros. The idea
+is that you would use these pre-tested macros to solve a
+very specific set of problems, and they would run fast.
+Caution: no side-effects in arguments please!! They may be
+evaluated MANY times!!
+
+These macros operate a stack of objects. Each object starts life
+small, and may grow to maturity. (Consider building a word syllable
+by syllable.) An object can move while it is growing. Once it has
+been "finished" it never changes address again. So the "top of the
+stack" is typically an immature growing object, while the rest of the
+stack is of mature, fixed size and fixed address objects.
+
+These routines grab large chunks of memory, using a function you
+supply, called `obstack_chunk_alloc'. On occasion, they free chunks,
+by calling `obstack_chunk_free'. You must define them and declare
+them before using any obstack macros.
+
+Each independent stack is represented by a `struct obstack'.
+Each of the obstack macros expects a pointer to such a structure
+as the first argument.
+
+One motivation for this package is the problem of growing char strings
+in symbol tables. Unless you are "fascist pig with a read-only mind"
+--Gosper's immortal quote from HAKMEM item 154, out of context--you
+would not like to put any arbitrary upper limit on the length of your
+symbols.
+
+In practice this often means you will build many short symbols and a
+few long symbols. At the time you are reading a symbol you don't know
+how long it is. One traditional method is to read a symbol into a
+buffer, realloc()ating the buffer every time you try to read a symbol
+that is longer than the buffer. This is beaut, but you still will
+want to copy the symbol from the buffer to a more permanent
+symbol-table entry say about half the time.
+
+With obstacks, you can work differently. Use one obstack for all symbol
+names. As you read a symbol, grow the name in the obstack gradually.
+When the name is complete, finalize it. Then, if the symbol exists already,
+free the newly read name.
+
+The way we do this is to take a large chunk, allocating memory from
+low addresses. When you want to build a symbol in the chunk you just
+add chars above the current "high water mark" in the chunk. When you
+have finished adding chars, because you got to the end of the symbol,
+you know how long the chars are, and you can create a new object.
+Mostly the chars will not burst over the highest address of the chunk,
+because you would typically expect a chunk to be (say) 100 times as
+long as an average object.
+
+In case that isn't clear, when we have enough chars to make up
+the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed)
+so we just point to it where it lies. No moving of chars is
+needed and this is the second win: potentially long strings need
+never be explicitly shuffled. Once an object is formed, it does not
+change its address during its lifetime.
+
+When the chars burst over a chunk boundary, we allocate a larger
+chunk, and then copy the partly formed object from the end of the old
+chunk to the beginning of the new larger chunk. We then carry on
+accreting characters to the end of the object as we normally would.
+
+A special macro is provided to add a single char at a time to a
+growing object. This allows the use of register variables, which
+break the ordinary 'growth' macro.
+
+Summary:
+ We allocate large chunks.
+ We carve out one object at a time from the current chunk.
+ Once carved, an object never moves.
+ We are free to append data of any size to the currently
+ growing object.
+ Exactly one object is growing in an obstack at any one time.
+ You can run one obstack per control block.
+ You may have as many control blocks as you dare.
+ Because of the way we do it, you can `unwind' an obstack
+ back to a previous state. (You may remove objects much
+ as you would with a stack.)
+*/
+
+
+/* Don't do the contents of this file more than once. */
+
+#ifndef __OBSTACKS__
+#define __OBSTACKS__
+
+/* We use subtraction of (char *)0 instead of casting to int
+ because on word-addressable machines a simple cast to int
+ may ignore the byte-within-word field of the pointer. */
+
+#ifndef __PTR_TO_INT
+#define __PTR_TO_INT(P) ((P) - (char *)0)
+#endif
+
+#ifndef __INT_TO_PTR
+#define __INT_TO_PTR(P) ((P) + (char *)0)
+#endif
+
+/* We need the type of the resulting object. In ANSI C it is ptrdiff_t
+ but in traditional C it is usually long. If we are in ANSI C and
+ don't already have ptrdiff_t get it. */
+
+#if defined (__STDC__) && ! defined (offsetof)
+#if defined (__GNUC__) && defined (IN_GCC)
+/* On Next machine, the system's stddef.h screws up if included
+ after we have defined just ptrdiff_t, so include all of gstddef.h.
+ Otherwise, define just ptrdiff_t, which is all we need. */
+#ifndef __NeXT__
+#define __need_ptrdiff_t
+#endif
+
+/* While building GCC, the stddef.h that goes with GCC has this name. */
+#include "gstddef.h"
+#else
+#include <stddef.h>
+#endif
+#endif
+
+#ifdef __STDC__
+#define PTR_INT_TYPE ptrdiff_t
+#else
+#define PTR_INT_TYPE long
+#endif
+
+struct _obstack_chunk /* Lives at front of each chunk. */
+{
+ char *limit; /* 1 past end of this chunk */
+ struct _obstack_chunk *prev; /* address of prior chunk or NULL */
+ char contents[4]; /* objects begin here */
+};
+
+struct obstack /* control current object in current chunk */
+{
+ long chunk_size; /* preferred size to allocate chunks in */
+ struct _obstack_chunk* chunk; /* address of current struct obstack_chunk */
+ char *object_base; /* address of object we are building */
+ char *next_free; /* where to add next char to current object */
+ char *chunk_limit; /* address of char after current chunk */
+ PTR_INT_TYPE temp; /* Temporary for some macros. */
+ int alignment_mask; /* Mask of alignment for each object. */
+ struct _obstack_chunk *(*chunkfun) (); /* User's fcn to allocate a chunk. */
+ void (*freefun) (); /* User's function to free a chunk. */
+ char *extra_arg; /* first arg for chunk alloc/dealloc funcs */
+ unsigned use_extra_arg:1; /* chunk alloc/dealloc funcs take extra arg */
+ unsigned maybe_empty_object:1;/* There is a possibility that the current
+ chunk contains a zero-length object. This
+ prevents freeing the chunk if we allocate
+ a bigger chunk to replace it. */
+};
+
+/* Declare the external functions we use; they are in obstack.c. */
+
+#ifdef __STDC__
+extern void _obstack_newchunk (struct obstack *, int);
+extern void _obstack_free (struct obstack *, void *);
+extern void _obstack_begin (struct obstack *, int, int,
+ void *(*) (), void (*) ());
+extern void _obstack_begin_1 (struct obstack *, int, int,
+ void *(*) (), void (*) (), void *);
+#else
+extern void _obstack_newchunk ();
+extern void _obstack_free ();
+extern void _obstack_begin ();
+extern void _obstack_begin_1 ();
+#endif
+
+#ifdef __STDC__
+
+/* Do the function-declarations after the structs
+ but before defining the macros. */
+
+void obstack_init (struct obstack *obstack);
+
+void * obstack_alloc (struct obstack *obstack, int size);
+
+void * obstack_copy (struct obstack *obstack, void *address, int size);
+void * obstack_copy0 (struct obstack *obstack, void *address, int size);
+
+void obstack_free (struct obstack *obstack, void *block);
+
+void obstack_blank (struct obstack *obstack, int size);
+
+void obstack_grow (struct obstack *obstack, void *data, int size);
+void obstack_grow0 (struct obstack *obstack, void *data, int size);
+
+void obstack_1grow (struct obstack *obstack, int data_char);
+void obstack_ptr_grow (struct obstack *obstack, void *data);
+void obstack_int_grow (struct obstack *obstack, int data);
+
+void * obstack_finish (struct obstack *obstack);
+
+int obstack_object_size (struct obstack *obstack);
+
+int obstack_room (struct obstack *obstack);
+void obstack_1grow_fast (struct obstack *obstack, int data_char);
+void obstack_ptr_grow_fast (struct obstack *obstack, void *data);
+void obstack_int_grow_fast (struct obstack *obstack, int data);
+void obstack_blank_fast (struct obstack *obstack, int size);
+
+void * obstack_base (struct obstack *obstack);
+void * obstack_next_free (struct obstack *obstack);
+int obstack_alignment_mask (struct obstack *obstack);
+int obstack_chunk_size (struct obstack *obstack);
+
+#endif /* __STDC__ */
+
+/* Non-ANSI C cannot really support alternative functions for these macros,
+ so we do not declare them. */
+
+/* Pointer to beginning of object being allocated or to be allocated next.
+ Note that this might not be the final address of the object
+ because a new chunk might be needed to hold the final size. */
+
+#define obstack_base(h) ((h)->object_base)
+
+/* Size for allocating ordinary chunks. */
+
+#define obstack_chunk_size(h) ((h)->chunk_size)
+
+/* Pointer to next byte not yet allocated in current chunk. */
+
+#define obstack_next_free(h) ((h)->next_free)
+
+/* Mask specifying low bits that should be clear in address of an object. */
+
+#define obstack_alignment_mask(h) ((h)->alignment_mask)
+
+#define obstack_init(h) \
+ _obstack_begin ((h), 0, 0, \
+ (void *(*) ()) obstack_chunk_alloc, (void (*) ()) obstack_chunk_free)
+
+#define obstack_begin(h, size) \
+ _obstack_begin ((h), (size), 0, \
+ (void *(*) ()) obstack_chunk_alloc, (void (*) ()) obstack_chunk_free)
+
+#define obstack_specify_allocation(h, size, alignment, chunkfun, freefun) \
+ _obstack_begin ((h), (size), (alignment), \
+ (void *(*) ()) (chunkfun), (void (*) ()) (freefun))
+
+#define obstack_specify_allocation_with_arg(h, size, alignment, chunkfun, freefun, arg) \
+ _obstack_begin_1 ((h), (size), (alignment), \
+ (void *(*) ()) (chunkfun), (void (*) ()) (freefun), (arg))
+
+#define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = achar)
+
+#define obstack_blank_fast(h,n) ((h)->next_free += (n))
+
+#if defined (__GNUC__) && defined (__STDC__)
+#if __GNUC__ < 2 || defined(NeXT)
+#define __extension__
+#endif
+
+/* For GNU C, if not -traditional,
+ we can define these macros to compute all args only once
+ without using a global variable.
+ Also, we can avoid using the `temp' slot, to make faster code. */
+
+#define obstack_object_size(OBSTACK) \
+ __extension__ \
+ ({ struct obstack *__o = (OBSTACK); \
+ (unsigned) (__o->next_free - __o->object_base); })
+
+#define obstack_room(OBSTACK) \
+ __extension__ \
+ ({ struct obstack *__o = (OBSTACK); \
+ (unsigned) (__o->chunk_limit - __o->next_free); })
+
+/* Note that the call to _obstack_newchunk is enclosed in (..., 0)
+ so that we can avoid having void expressions
+ in the arms of the conditional expression.
+ Casting the third operand to void was tried before,
+ but some compilers won't accept it. */
+#define obstack_grow(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ int __len = (length); \
+ ((__o->next_free + __len > __o->chunk_limit) \
+ ? (_obstack_newchunk (__o, __len), 0) : 0); \
+ bcopy (where, __o->next_free, __len); \
+ __o->next_free += __len; \
+ (void) 0; })
+
+#define obstack_grow0(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ int __len = (length); \
+ ((__o->next_free + __len + 1 > __o->chunk_limit) \
+ ? (_obstack_newchunk (__o, __len + 1), 0) : 0), \
+ bcopy (where, __o->next_free, __len), \
+ __o->next_free += __len, \
+ *(__o->next_free)++ = 0; \
+ (void) 0; })
+
+#define obstack_1grow(OBSTACK,datum) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ ((__o->next_free + 1 > __o->chunk_limit) \
+ ? (_obstack_newchunk (__o, 1), 0) : 0), \
+ *(__o->next_free)++ = (datum); \
+ (void) 0; })
+
+/* These assume that the obstack alignment is good enough for pointers or ints,
+ and that the data added so far to the current object
+ shares that much alignment. */
+
+#define obstack_ptr_grow(OBSTACK,datum) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ ((__o->next_free + sizeof (void *) > __o->chunk_limit) \
+ ? (_obstack_newchunk (__o, sizeof (void *)), 0) : 0), \
+ *((void **)__o->next_free)++ = ((void *)datum); \
+ (void) 0; })
+
+#define obstack_int_grow(OBSTACK,datum) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ ((__o->next_free + sizeof (int) > __o->chunk_limit) \
+ ? (_obstack_newchunk (__o, sizeof (int)), 0) : 0), \
+ *((int *)__o->next_free)++ = ((int)datum); \
+ (void) 0; })
+
+#define obstack_ptr_grow_fast(h,aptr) (*((void **)(h)->next_free)++ = (void *)aptr)
+#define obstack_int_grow_fast(h,aint) (*((int *)(h)->next_free)++ = (int)aint)
+
+#define obstack_blank(OBSTACK,length) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ int __len = (length); \
+ ((__o->chunk_limit - __o->next_free < __len) \
+ ? (_obstack_newchunk (__o, __len), 0) : 0); \
+ __o->next_free += __len; \
+ (void) 0; })
+
+#define obstack_alloc(OBSTACK,length) \
+__extension__ \
+({ struct obstack *__h = (OBSTACK); \
+ obstack_blank (__h, (length)); \
+ obstack_finish (__h); })
+
+#define obstack_copy(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__h = (OBSTACK); \
+ obstack_grow (__h, (where), (length)); \
+ obstack_finish (__h); })
+
+#define obstack_copy0(OBSTACK,where,length) \
+__extension__ \
+({ struct obstack *__h = (OBSTACK); \
+ obstack_grow0 (__h, (where), (length)); \
+ obstack_finish (__h); })
+
+/* The local variable is named __o1 to avoid a name conflict
+ when obstack_blank is called. */
+#define obstack_finish(OBSTACK) \
+__extension__ \
+({ struct obstack *__o1 = (OBSTACK); \
+ void *value = (void *) __o1->object_base; \
+ if (__o1->next_free == value) \
+ __o1->maybe_empty_object = 1; \
+ __o1->next_free \
+ = __INT_TO_PTR ((__PTR_TO_INT (__o1->next_free)+__o1->alignment_mask)\
+ & ~ (__o1->alignment_mask)); \
+ ((__o1->next_free - (char *)__o1->chunk \
+ > __o1->chunk_limit - (char *)__o1->chunk) \
+ ? (__o1->next_free = __o1->chunk_limit) : 0); \
+ __o1->object_base = __o1->next_free; \
+ value; })
+
+#define obstack_free(OBSTACK, OBJ) \
+__extension__ \
+({ struct obstack *__o = (OBSTACK); \
+ void *__obj = (OBJ); \
+ if (__obj > (void *)__o->chunk && __obj < (void *)__o->chunk_limit) \
+ __o->next_free = __o->object_base = __obj; \
+ else (obstack_free) (__o, __obj); })
+
+#else /* not __GNUC__ or not __STDC__ */
+
+#define obstack_object_size(h) \
+ (unsigned) ((h)->next_free - (h)->object_base)
+
+#define obstack_room(h) \
+ (unsigned) ((h)->chunk_limit - (h)->next_free)
+
+#define obstack_grow(h,where,length) \
+( (h)->temp = (length), \
+ (((h)->next_free + (h)->temp > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), (h)->temp), 0) : 0), \
+ bcopy (where, (h)->next_free, (h)->temp), \
+ (h)->next_free += (h)->temp)
+
+#define obstack_grow0(h,where,length) \
+( (h)->temp = (length), \
+ (((h)->next_free + (h)->temp + 1 > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), (h)->temp + 1), 0) : 0), \
+ bcopy (where, (h)->next_free, (h)->temp), \
+ (h)->next_free += (h)->temp, \
+ *((h)->next_free)++ = 0)
+
+#define obstack_1grow(h,datum) \
+( (((h)->next_free + 1 > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), 1), 0) : 0), \
+ *((h)->next_free)++ = (datum))
+
+#define obstack_ptr_grow(h,datum) \
+( (((h)->next_free + sizeof (char *) > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), sizeof (char *)), 0) : 0), \
+ *((char **)(((h)->next_free+=sizeof(char *))-sizeof(char *))) = ((char *)datum))
+
+#define obstack_int_grow(h,datum) \
+( (((h)->next_free + sizeof (int) > (h)->chunk_limit) \
+ ? (_obstack_newchunk ((h), sizeof (int)), 0) : 0), \
+ *((int *)(((h)->next_free+=sizeof(int))-sizeof(int))) = ((int)datum))
+
+#define obstack_ptr_grow_fast(h,aptr) (*((char **)(h)->next_free)++ = (char *)aptr)
+#define obstack_int_grow_fast(h,aint) (*((int *)(h)->next_free)++ = (int)aint)
+
+#define obstack_blank(h,length) \
+( (h)->temp = (length), \
+ (((h)->chunk_limit - (h)->next_free < (h)->temp) \
+ ? (_obstack_newchunk ((h), (h)->temp), 0) : 0), \
+ (h)->next_free += (h)->temp)
+
+#define obstack_alloc(h,length) \
+ (obstack_blank ((h), (length)), obstack_finish ((h)))
+
+#define obstack_copy(h,where,length) \
+ (obstack_grow ((h), (where), (length)), obstack_finish ((h)))
+
+#define obstack_copy0(h,where,length) \
+ (obstack_grow0 ((h), (where), (length)), obstack_finish ((h)))
+
+#define obstack_finish(h) \
+( ((h)->next_free == (h)->object_base \
+ ? (((h)->maybe_empty_object = 1), 0) \
+ : 0), \
+ (h)->temp = __PTR_TO_INT ((h)->object_base), \
+ (h)->next_free \
+ = __INT_TO_PTR ((__PTR_TO_INT ((h)->next_free)+(h)->alignment_mask) \
+ & ~ ((h)->alignment_mask)), \
+ (((h)->next_free - (char *)(h)->chunk \
+ > (h)->chunk_limit - (char *)(h)->chunk) \
+ ? ((h)->next_free = (h)->chunk_limit) : 0), \
+ (h)->object_base = (h)->next_free, \
+ __INT_TO_PTR ((h)->temp))
+
+#ifdef __STDC__
+#define obstack_free(h,obj) \
+( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \
+ (((h)->temp > 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\
+ ? (int) ((h)->next_free = (h)->object_base \
+ = (h)->temp + (char *) (h)->chunk) \
+ : (((obstack_free) ((h), (h)->temp + (char *) (h)->chunk), 0), 0)))
+#else
+#define obstack_free(h,obj) \
+( (h)->temp = (char *)(obj) - (char *) (h)->chunk, \
+ (((h)->temp > 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\
+ ? (int) ((h)->next_free = (h)->object_base \
+ = (h)->temp + (char *) (h)->chunk) \
+ : (_obstack_free ((h), (h)->temp + (char *) (h)->chunk), 0)))
+#endif
+
+#endif /* not __GNUC__ or not __STDC__ */
+
+#endif /* not __OBSTACKS__ */
diff --git a/gnu/usr.bin/grep/regex.c b/gnu/usr.bin/grep/regex.c
index d69e784..e8b5882 100644
--- a/gnu/usr.bin/grep/regex.c
+++ b/gnu/usr.bin/grep/regex.c
@@ -1,5 +1,9 @@
-/* Extended regular expression matching and search library.
- Copyright (C) 1985, 1989 Free Software Foundation, Inc.
+/* Extended regular expression matching and search library,
+ version 0.12.
+ (Implements POSIX draft P10003.2/D11.2, except for
+ internationalization features.)
+
+ Copyright (C) 1993 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,84 +17,78 @@
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
- In other words, you are welcome to use, share and improve this program.
- You are forbidden to forbid anyone else to use, share and improve
- what you give them. Help stamp out software-hoarding! */
+/* AIX requires this to be the first thing in the file. */
+#if defined (_AIX) && !defined (REGEX_MALLOC)
+ #pragma alloca
+#endif
+#define _GNU_SOURCE
-/* To test, compile with -Dtest.
- This Dtestable feature turns this into a self-contained program
- which reads a pattern, describes how it compiles,
- then reads a string and searches for it. */
+/* We need this for `regex.h', and perhaps for the Emacs include files. */
+#include <sys/types.h>
-/* AIX requires this to be the first thing in the file. */
-#ifdef __GNUC__
-#undef alloca
-#define alloca __builtin_alloca
-#else /* not __GNUC__ */
-#if defined(sparc) && !defined(USG) && !defined(SVR4) && !defined(__svr4__)
-#include <alloca.h>
-#else
-#ifdef _AIX
- #pragma alloca
-#else
-char *alloca ();
+#ifdef HAVE_CONFIG_H
+#include "config.h"
#endif
-#endif /* sparc */
-#endif /* not __GNUC__ */
-
+/* The `emacs' switch turns on certain matching commands
+ that make sense only in Emacs. */
#ifdef emacs
-/* The `emacs' switch turns on certain special matching commands
- that make sense only in emacs. */
-
-#include "config.h"
#include "lisp.h"
#include "buffer.h"
#include "syntax.h"
+/* Emacs uses `NULL' as a predicate. */
+#undef NULL
+
#else /* not emacs */
-#if defined(USG) || defined(STDC_HEADERS)
+/* We used to test for `BSTRING' here, but only GCC and Emacs define
+ `BSTRING', as far as I know, and neither of them use this code. */
+#if HAVE_STRING_H || STDC_HEADERS
#include <string.h>
-#ifndef bcopy
-#define bcopy(s,d,n) memcpy((d),(s),(n))
-#endif
#ifndef bcmp
-#define bcmp(s1,s2,n) memcmp((s1),(s2),(n))
+#define bcmp(s1, s2, n) memcmp ((s1), (s2), (n))
+#endif
+#ifndef bcopy
+#define bcopy(s, d, n) memcpy ((d), (s), (n))
#endif
#ifndef bzero
-#define bzero(s,n) memset((s),0,(n))
+#define bzero(s, n) memset ((s), 0, (n))
#endif
+#else
+#include <strings.h>
#endif
#ifdef STDC_HEADERS
#include <stdlib.h>
#else
char *malloc ();
+char *realloc ();
#endif
-/*
- * Define the syntax stuff, so we can do the \<...\> things.
- */
-#ifndef Sword /* must be non-zero in some of the tests below... */
+/* Define the syntax stuff for \<, \>, etc. */
+
+/* This must be nonzero for the wordchar and notwordchar pattern
+ commands in re_match_2. */
+#ifndef Sword
#define Sword 1
#endif
-#define SYNTAX(c) re_syntax_table[c]
-
#ifdef SYNTAX_TABLE
-char *re_syntax_table;
+extern char *re_syntax_table;
-#else
+#else /* not SYNTAX_TABLE */
+
+/* How many characters in the character set. */
+#define CHAR_SET_SIZE 256
-static char re_syntax_table[256];
+static char re_syntax_table[CHAR_SET_SIZE];
static void
init_syntax_once ()
@@ -112,731 +110,2549 @@ init_syntax_once ()
for (c = '0'; c <= '9'; c++)
re_syntax_table[c] = Sword;
+ re_syntax_table['_'] = Sword;
+
done = 1;
}
-#endif /* SYNTAX_TABLE */
-#endif /* not emacs */
-
-#include "regex.h"
-
-/* Number of failure points to allocate space for initially,
- when matching. If this number is exceeded, more space is allocated,
- so it is not a hard limit. */
+#endif /* not SYNTAX_TABLE */
-#ifndef NFAILURES
-#define NFAILURES 80
-#endif /* NFAILURES */
+#define SYNTAX(c) re_syntax_table[c]
-/* width of a byte in bits */
+#endif /* not emacs */
+
+/* Get the interface, including the syntax bits. */
+#include "regex.h"
-#define BYTEWIDTH 8
+/* isalpha etc. are used for the character classes. */
+#include <ctype.h>
+
+/* Jim Meyering writes:
+
+ "... Some ctype macros are valid only for character codes that
+ isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when
+ using /bin/cc or gcc but without giving an ansi option). So, all
+ ctype uses should be through macros like ISPRINT... If
+ STDC_HEADERS is defined, then autoconf has verified that the ctype
+ macros don't need to be guarded with references to isascii. ...
+ Defining isascii to 1 should let any compiler worth its salt
+ eliminate the && through constant folding." */
+#if ! defined (isascii) || defined (STDC_HEADERS)
+#undef isascii
+#define isascii(c) 1
+#endif
-#ifndef SIGN_EXTEND_CHAR
-#ifdef __CHAR_UNSIGNED__
-#define SIGN_EXTEND_CHAR(c) ((c)>(char)127?(c)-256:(c))
+#ifdef isblank
+#define ISBLANK(c) (isascii (c) && isblank (c))
+#else
+#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+#ifdef isgraph
+#define ISGRAPH(c) (isascii (c) && isgraph (c))
#else
-#define SIGN_EXTEND_CHAR(x) (x)
+#define ISGRAPH(c) (isascii (c) && isprint (c) && !isspace (c))
+#endif
+
+#define ISPRINT(c) (isascii (c) && isprint (c))
+#define ISDIGIT(c) (isascii (c) && isdigit (c))
+#define ISALNUM(c) (isascii (c) && isalnum (c))
+#define ISALPHA(c) (isascii (c) && isalpha (c))
+#define ISCNTRL(c) (isascii (c) && iscntrl (c))
+#define ISLOWER(c) (isascii (c) && islower (c))
+#define ISPUNCT(c) (isascii (c) && ispunct (c))
+#define ISSPACE(c) (isascii (c) && isspace (c))
+#define ISUPPER(c) (isascii (c) && isupper (c))
+#define ISXDIGIT(c) (isascii (c) && isxdigit (c))
+
+#ifndef NULL
+#define NULL 0
#endif
+
+/* We remove any previous definition of `SIGN_EXTEND_CHAR',
+ since ours (we hope) works properly with all combinations of
+ machines, compilers, `char' and `unsigned char' argument types.
+ (Per Bothner suggested the basic approach.) */
+#undef SIGN_EXTEND_CHAR
+#if __STDC__
+#define SIGN_EXTEND_CHAR(c) ((signed char) (c))
+#else /* not __STDC__ */
+/* As in Harbison and Steele. */
+#define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128)
#endif
-static int obscure_syntax = 0;
+/* Should we use malloc or alloca? If REGEX_MALLOC is not defined, we
+ use `alloca' instead of `malloc'. This is because using malloc in
+ re_search* or re_match* could cause memory leaks when C-g is used in
+ Emacs; also, malloc is slower and causes storage fragmentation. On
+ the other hand, malloc is more portable, and easier to debug.
+
+ Because we sometimes use alloca, some routines have to be macros,
+ not functions -- `alloca'-allocated space disappears at the end of the
+ function it is called in. */
-/* Specify the precise syntax of regexp for compilation.
- This provides for compatibility for various utilities
- which historically have different, incompatible syntaxes.
+#ifdef REGEX_MALLOC
- The argument SYNTAX is a bit-mask containing the two bits
- RE_NO_BK_PARENS and RE_NO_BK_VBAR. */
+#define REGEX_ALLOCATE malloc
+#define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize)
-int
-re_set_syntax (syntax)
- int syntax;
+#else /* not REGEX_MALLOC */
+
+/* Emacs already defines alloca, sometimes. */
+#ifndef alloca
+
+/* Make alloca work the best possible way. */
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not __GNUC__ */
+#if HAVE_ALLOCA_H
+#include <alloca.h>
+#else /* not __GNUC__ or HAVE_ALLOCA_H */
+#ifndef _AIX /* Already did AIX, up at the top. */
+char *alloca ();
+#endif /* not _AIX */
+#endif /* not HAVE_ALLOCA_H */
+#endif /* not __GNUC__ */
+
+#endif /* not alloca */
+
+#define REGEX_ALLOCATE alloca
+
+/* Assumes a `char *destination' variable. */
+#define REGEX_REALLOCATE(source, osize, nsize) \
+ (destination = (char *) alloca (nsize), \
+ bcopy (source, destination, osize), \
+ destination)
+
+#endif /* not REGEX_MALLOC */
+
+
+/* True if `size1' is non-NULL and PTR is pointing anywhere inside
+ `string1' or just past its end. This works if PTR is NULL, which is
+ a good thing. */
+#define FIRST_STRING_P(ptr) \
+ (size1 && string1 <= (ptr) && (ptr) <= string1 + size1)
+
+/* (Re)Allocate N items of type T using malloc, or fail. */
+#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t)))
+#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t)))
+#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t)))
+
+#define BYTEWIDTH 8 /* In bits. */
+
+#define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+typedef char boolean;
+#define false 0
+#define true 1
+
+/* These are the command codes that appear in compiled regular
+ expressions. Some opcodes are followed by argument bytes. A
+ command code can specify any interpretation whatsoever for its
+ arguments. Zero bytes may appear in the compiled regular expression.
+
+ The value of `exactn' is needed in search.c (search_buffer) in Emacs.
+ So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of
+ `exactn' we use here must also be 1. */
+
+typedef enum
{
- int ret;
+ no_op = 0,
+
+ /* Followed by one byte giving n, then by n literal bytes. */
+ exactn = 1,
+
+ /* Matches any (more or less) character. */
+ anychar,
+
+ /* Matches any one char belonging to specified set. First
+ following byte is number of bitmap bytes. Then come bytes
+ for a bitmap saying which chars are in. Bits in each byte
+ are ordered low-bit-first. A character is in the set if its
+ bit is 1. A character too large to have a bit in the map is
+ automatically not in the set. */
+ charset,
+
+ /* Same parameters as charset, but match any character that is
+ not one of those specified. */
+ charset_not,
+
+ /* Start remembering the text that is matched, for storing in a
+ register. Followed by one byte with the register number, in
+ the range 0 to one less than the pattern buffer's re_nsub
+ field. Then followed by one byte with the number of groups
+ inner to this one. (This last has to be part of the
+ start_memory only because we need it in the on_failure_jump
+ of re_match_2.) */
+ start_memory,
+
+ /* Stop remembering the text that is matched and store it in a
+ memory register. Followed by one byte with the register
+ number, in the range 0 to one less than `re_nsub' in the
+ pattern buffer, and one byte with the number of inner groups,
+ just like `start_memory'. (We need the number of inner
+ groups here because we don't have any easy way of finding the
+ corresponding start_memory when we're at a stop_memory.) */
+ stop_memory,
+
+ /* Match a duplicate of something remembered. Followed by one
+ byte containing the register number. */
+ duplicate,
+
+ /* Fail unless at beginning of line. */
+ begline,
+
+ /* Fail unless at end of line. */
+ endline,
+
+ /* Succeeds if at beginning of buffer (if emacs) or at beginning
+ of string to be matched (if not). */
+ begbuf,
+
+ /* Analogously, for end of buffer/string. */
+ endbuf,
+
+ /* Followed by two byte relative address to which to jump. */
+ jump,
+
+ /* Same as jump, but marks the end of an alternative. */
+ jump_past_alt,
+
+ /* Followed by two-byte relative address of place to resume at
+ in case of failure. */
+ on_failure_jump,
+
+ /* Like on_failure_jump, but pushes a placeholder instead of the
+ current string position when executed. */
+ on_failure_keep_string_jump,
+
+ /* Throw away latest failure point and then jump to following
+ two-byte relative address. */
+ pop_failure_jump,
+
+ /* Change to pop_failure_jump if know won't have to backtrack to
+ match; otherwise change to jump. This is used to jump
+ back to the beginning of a repeat. If what follows this jump
+ clearly won't match what the repeat does, such that we can be
+ sure that there is no use backtracking out of repetitions
+ already matched, then we change it to a pop_failure_jump.
+ Followed by two-byte address. */
+ maybe_pop_jump,
+
+ /* Jump to following two-byte address, and push a dummy failure
+ point. This failure point will be thrown away if an attempt
+ is made to use it for a failure. A `+' construct makes this
+ before the first repeat. Also used as an intermediary kind
+ of jump when compiling an alternative. */
+ dummy_failure_jump,
+
+ /* Push a dummy failure point and continue. Used at the end of
+ alternatives. */
+ push_dummy_failure,
+
+ /* Followed by two-byte relative address and two-byte number n.
+ After matching N times, jump to the address upon failure. */
+ succeed_n,
+
+ /* Followed by two-byte relative address, and two-byte number n.
+ Jump to the address N times, then fail. */
+ jump_n,
+
+ /* Set the following two-byte relative address to the
+ subsequent two-byte number. The address *includes* the two
+ bytes of number. */
+ set_number_at,
+
+ wordchar, /* Matches any word-constituent character. */
+ notwordchar, /* Matches any char that is not a word-constituent. */
+
+ wordbeg, /* Succeeds if at word beginning. */
+ wordend, /* Succeeds if at word end. */
+
+ wordbound, /* Succeeds if at a word boundary. */
+ notwordbound /* Succeeds if not at a word boundary. */
- ret = obscure_syntax;
- obscure_syntax = syntax;
- return ret;
-}
+#ifdef emacs
+ ,before_dot, /* Succeeds if before point. */
+ at_dot, /* Succeeds if at point. */
+ after_dot, /* Succeeds if after point. */
+
+ /* Matches any character whose syntax is specified. Followed by
+ a byte which contains a syntax code, e.g., Sword. */
+ syntaxspec,
+
+ /* Matches any character whose syntax is not that specified. */
+ notsyntaxspec
+#endif /* emacs */
+} re_opcode_t;
-/* re_compile_pattern takes a regular-expression string
- and converts it into a buffer full of byte commands for matching.
-
- PATTERN is the address of the pattern string
- SIZE is the length of it.
- BUFP is a struct re_pattern_buffer * which points to the info
- on where to store the byte commands.
- This structure contains a char * which points to the
- actual space, which should have been obtained with malloc.
- re_compile_pattern may use realloc to grow the buffer space.
-
- The number of bytes of commands can be found out by looking in
- the struct re_pattern_buffer that bufp pointed to,
- after re_compile_pattern returns.
-*/
+/* Common operations on the compiled pattern. */
-#define PATPUSH(ch) (*b++ = (char) (ch))
+/* Store NUMBER in two contiguous bytes starting at DESTINATION. */
-#define PATFETCH(c) \
- {if (p == pend) goto end_of_pattern; \
- c = * (unsigned char *) p++; \
- if (translate) c = translate[c]; }
+#define STORE_NUMBER(destination, number) \
+ do { \
+ (destination)[0] = (number) & 0377; \
+ (destination)[1] = (number) >> 8; \
+ } while (0)
-#define PATFETCH_RAW(c) \
- {if (p == pend) goto end_of_pattern; \
- c = * (unsigned char *) p++; }
+/* Same as STORE_NUMBER, except increment DESTINATION to
+ the byte after where the number is stored. Therefore, DESTINATION
+ must be an lvalue. */
-#define PATUNFETCH p--
+#define STORE_NUMBER_AND_INCR(destination, number) \
+ do { \
+ STORE_NUMBER (destination, number); \
+ (destination) += 2; \
+ } while (0)
-#define EXTEND_BUFFER \
- { char *old_buffer = bufp->buffer; \
- if (bufp->allocated == (1<<16)) goto too_big; \
- bufp->allocated *= 2; \
- if (bufp->allocated > (1<<16)) bufp->allocated = (1<<16); \
- if (!(bufp->buffer = (char *) realloc (bufp->buffer, bufp->allocated))) \
- goto memory_exhausted; \
- c = bufp->buffer - old_buffer; \
- b += c; \
- if (fixup_jump) \
- fixup_jump += c; \
- if (laststart) \
- laststart += c; \
- begalt += c; \
- if (pending_exact) \
- pending_exact += c; \
- }
+/* Put into DESTINATION a number stored in two contiguous bytes starting
+ at SOURCE. */
-static void store_jump (), insert_jump ();
+#define EXTRACT_NUMBER(destination, source) \
+ do { \
+ (destination) = *(source) & 0377; \
+ (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8; \
+ } while (0)
-char *
-re_compile_pattern (pattern, size, bufp)
- char *pattern;
- int size;
- struct re_pattern_buffer *bufp;
+#ifdef DEBUG
+static void
+extract_number (dest, source)
+ int *dest;
+ unsigned char *source;
{
- register char *b = bufp->buffer;
- register char *p = pattern;
- char *pend = pattern + size;
- register unsigned c, c1;
- char *p1;
- unsigned char *translate = (unsigned char *) bufp->translate;
+ int temp = SIGN_EXTEND_CHAR (*(source + 1));
+ *dest = *source & 0377;
+ *dest += temp << 8;
+}
- /* address of the count-byte of the most recently inserted "exactn" command.
- This makes it possible to tell whether a new exact-match character
- can be added to that command or requires a new "exactn" command. */
-
- char *pending_exact = 0;
+#ifndef EXTRACT_MACROS /* To debug the macros. */
+#undef EXTRACT_NUMBER
+#define EXTRACT_NUMBER(dest, src) extract_number (&dest, src)
+#endif /* not EXTRACT_MACROS */
- /* address of the place where a forward-jump should go
- to the end of the containing expression.
- Each alternative of an "or", except the last, ends with a forward-jump
- of this sort. */
+#endif /* DEBUG */
- char *fixup_jump = 0;
+/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number.
+ SOURCE must be an lvalue. */
- /* address of start of the most recently finished expression.
- This tells postfix * where to find the start of its operand. */
+#define EXTRACT_NUMBER_AND_INCR(destination, source) \
+ do { \
+ EXTRACT_NUMBER (destination, source); \
+ (source) += 2; \
+ } while (0)
- char *laststart = 0;
+#ifdef DEBUG
+static void
+extract_number_and_incr (destination, source)
+ int *destination;
+ unsigned char **source;
+{
+ extract_number (destination, *source);
+ *source += 2;
+}
- /* In processing a repeat, 1 means zero matches is allowed */
+#ifndef EXTRACT_MACROS
+#undef EXTRACT_NUMBER_AND_INCR
+#define EXTRACT_NUMBER_AND_INCR(dest, src) \
+ extract_number_and_incr (&dest, &src)
+#endif /* not EXTRACT_MACROS */
- char zero_times_ok;
+#endif /* DEBUG */
+
+/* If DEBUG is defined, Regex prints many voluminous messages about what
+ it is doing (if the variable `debug' is nonzero). If linked with the
+ main program in `iregex.c', you can enter patterns and strings
+ interactively. And if linked with the main program in `main.c' and
+ the other test files, you can run the already-written tests. */
- /* In processing a repeat, 1 means many matches is allowed */
+#ifdef DEBUG
- char many_times_ok;
+/* We use standard I/O for debugging. */
+#include <stdio.h>
- /* address of beginning of regexp, or inside of last \( */
+/* It is useful to test things that ``must'' be true when debugging. */
+#include <assert.h>
- char *begalt = b;
+static int debug = 0;
- /* Stack of information saved by \( and restored by \).
- Four stack elements are pushed by each \(:
- First, the value of b.
- Second, the value of fixup_jump.
- Third, the value of regnum.
- Fourth, the value of begalt. */
+#define DEBUG_STATEMENT(e) e
+#define DEBUG_PRINT1(x) if (debug) printf (x)
+#define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2)
+#define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3)
+#define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4)
+#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) \
+ if (debug) print_partial_compiled_pattern (s, e)
+#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) \
+ if (debug) print_double_string (w, s1, sz1, s2, sz2)
- int stackb[40];
- int *stackp = stackb;
- int *stacke = stackb + 40;
- int *stackt;
- /* Counts \('s as they are encountered. Remembered for the matching \),
- where it becomes the "register number" to put in the stop_memory command */
+extern void printchar ();
- int regnum = 1;
+/* Print the fastmap in human-readable form. */
- bufp->fastmap_accurate = 0;
+void
+print_fastmap (fastmap)
+ char *fastmap;
+{
+ unsigned was_a_range = 0;
+ unsigned i = 0;
+
+ while (i < (1 << BYTEWIDTH))
+ {
+ if (fastmap[i++])
+ {
+ was_a_range = 0;
+ printchar (i - 1);
+ while (i < (1 << BYTEWIDTH) && fastmap[i])
+ {
+ was_a_range = 1;
+ i++;
+ }
+ if (was_a_range)
+ {
+ printf ("-");
+ printchar (i - 1);
+ }
+ }
+ }
+ putchar ('\n');
+}
-#ifndef emacs
-#ifndef SYNTAX_TABLE
- /*
- * Initialize the syntax table.
- */
- init_syntax_once();
-#endif
-#endif
- if (bufp->allocated == 0)
+/* Print a compiled pattern string in human-readable form, starting at
+ the START pointer into it and ending just before the pointer END. */
+
+void
+print_partial_compiled_pattern (start, end)
+ unsigned char *start;
+ unsigned char *end;
+{
+ int mcnt, mcnt2;
+ unsigned char *p = start;
+ unsigned char *pend = end;
+
+ if (start == NULL)
{
- bufp->allocated = 28;
- if (bufp->buffer)
- /* EXTEND_BUFFER loses when bufp->allocated is 0 */
- bufp->buffer = (char *) realloc (bufp->buffer, 28);
- else
- /* Caller did not allocate a buffer. Do it for him */
- bufp->buffer = (char *) malloc (28);
- if (!bufp->buffer) goto memory_exhausted;
- begalt = b = bufp->buffer;
+ printf ("(null)\n");
+ return;
}
-
- while (p != pend)
+
+ /* Loop over pattern commands. */
+ while (p < pend)
{
- if (b - bufp->buffer > bufp->allocated - 10)
- /* Note that EXTEND_BUFFER clobbers c */
- EXTEND_BUFFER;
-
- PATFETCH (c);
+ printf ("%d:\t", p - start);
- switch (c)
+ switch ((re_opcode_t) *p++)
{
- case '$':
- if (obscure_syntax & RE_TIGHT_VBAR)
- {
- if (! (obscure_syntax & RE_CONTEXT_INDEP_OPS) && p != pend)
- goto normal_char;
- /* Make operand of last vbar end before this `$'. */
- if (fixup_jump)
- store_jump (fixup_jump, jump, b);
- fixup_jump = 0;
- PATPUSH (endline);
- break;
- }
+ case no_op:
+ printf ("/no_op");
+ break;
- /* $ means succeed if at end of line, but only in special contexts.
- If randomly in the middle of a pattern, it is a normal character. */
- if (p == pend || *p == '\n'
- || (obscure_syntax & RE_CONTEXT_INDEP_OPS)
- || (obscure_syntax & RE_NO_BK_PARENS
- ? *p == ')'
- : *p == '\\' && p[1] == ')')
- || (obscure_syntax & RE_NO_BK_VBAR
- ? *p == '|'
- : *p == '\\' && p[1] == '|'))
+ case exactn:
+ mcnt = *p++;
+ printf ("/exactn/%d", mcnt);
+ do
{
- PATPUSH (endline);
- break;
- }
- goto normal_char;
+ putchar ('/');
+ printchar (*p++);
+ }
+ while (--mcnt);
+ break;
- case '^':
- /* ^ means succeed if at beg of line, but only if no preceding pattern. */
+ case start_memory:
+ mcnt = *p++;
+ printf ("/start_memory/%d/%d", mcnt, *p++);
+ break;
- if (laststart && p[-2] != '\n'
- && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS))
- goto normal_char;
- if (obscure_syntax & RE_TIGHT_VBAR)
- {
- if (p != pattern + 1
- && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS))
- goto normal_char;
- PATPUSH (begline);
- begalt = b;
- }
- else
- PATPUSH (begline);
+ case stop_memory:
+ mcnt = *p++;
+ printf ("/stop_memory/%d/%d", mcnt, *p++);
+ break;
+
+ case duplicate:
+ printf ("/duplicate/%d", *p++);
break;
- case '+':
- case '?':
- if (obscure_syntax & RE_BK_PLUS_QM)
- goto normal_char;
- handle_plus:
- case '*':
- /* If there is no previous pattern, char not special. */
- if (!laststart && ! (obscure_syntax & RE_CONTEXT_INDEP_OPS))
- goto normal_char;
- /* If there is a sequence of repetition chars,
- collapse it down to equivalent to just one. */
- zero_times_ok = 0;
- many_times_ok = 0;
- while (1)
- {
- zero_times_ok |= c != '+';
- many_times_ok |= c != '?';
- if (p == pend)
- break;
- PATFETCH (c);
- if (c == '*')
- ;
- else if (!(obscure_syntax & RE_BK_PLUS_QM)
- && (c == '+' || c == '?'))
- ;
- else if ((obscure_syntax & RE_BK_PLUS_QM)
- && c == '\\')
+ case anychar:
+ printf ("/anychar");
+ break;
+
+ case charset:
+ case charset_not:
+ {
+ register int c, last = -100;
+ register int in_range = 0;
+
+ printf ("/charset [%s",
+ (re_opcode_t) *(p - 1) == charset_not ? "^" : "");
+
+ assert (p + *p < pend);
+
+ for (c = 0; c < 256; c++)
+ if (c / 8 < *p
+ && (p[1 + (c/8)] & (1 << (c % 8))))
{
- int c1;
- PATFETCH (c1);
- if (!(c1 == '+' || c1 == '?'))
+ /* Are we starting a range? */
+ if (last + 1 == c && ! in_range)
{
- PATUNFETCH;
- PATUNFETCH;
- break;
+ putchar ('-');
+ in_range = 1;
}
- c = c1;
- }
- else
- {
- PATUNFETCH;
- break;
- }
- }
+ /* Have we broken a range? */
+ else if (last + 1 != c && in_range)
+ {
+ printchar (last);
+ in_range = 0;
+ }
+
+ if (! in_range)
+ printchar (c);
- /* Star, etc. applied to an empty pattern is equivalent
- to an empty pattern. */
- if (!laststart)
- break;
+ last = c;
+ }
- /* Now we know whether 0 matches is allowed,
- and whether 2 or more matches is allowed. */
- if (many_times_ok)
- {
- /* If more than one repetition is allowed,
- put in a backward jump at the end. */
- store_jump (b, maybe_finalize_jump, laststart - 3);
- b += 3;
- }
- insert_jump (on_failure_jump, laststart, b + 3, b);
- pending_exact = 0;
- b += 3;
- if (!zero_times_ok)
- {
- /* At least one repetition required: insert before the loop
- a skip over the initial on-failure-jump instruction */
- insert_jump (dummy_failure_jump, laststart, laststart + 6, b);
- b += 3;
- }
+ if (in_range)
+ printchar (last);
+
+ putchar (']');
+
+ p += 1 + *p;
+ }
break;
- case '.':
- laststart = b;
- PATPUSH (anychar);
+ case begline:
+ printf ("/begline");
+ break;
+
+ case endline:
+ printf ("/endline");
+ break;
+
+ case on_failure_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/on_failure_jump to %d", p + mcnt - start);
+ break;
+
+ case on_failure_keep_string_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/on_failure_keep_string_jump to %d", p + mcnt - start);
+ break;
+
+ case dummy_failure_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/dummy_failure_jump to %d", p + mcnt - start);
+ break;
+
+ case push_dummy_failure:
+ printf ("/push_dummy_failure");
+ break;
+
+ case maybe_pop_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/maybe_pop_jump to %d", p + mcnt - start);
break;
- case '[':
- while (b - bufp->buffer
- > bufp->allocated - 3 - (1 << BYTEWIDTH) / BYTEWIDTH)
- /* Note that EXTEND_BUFFER clobbers c */
- EXTEND_BUFFER;
+ case pop_failure_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/pop_failure_jump to %d", p + mcnt - start);
+ break;
+
+ case jump_past_alt:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/jump_past_alt to %d", p + mcnt - start);
+ break;
+
+ case jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/jump to %d", p + mcnt - start);
+ break;
- laststart = b;
- if (*p == '^')
- PATPUSH (charset_not), p++;
- else
- PATPUSH (charset);
- p1 = p;
-
- PATPUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
- /* Clear the whole map */
- bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH);
- /* Read in characters and ranges, setting map bits */
- while (1)
- {
- PATFETCH (c);
- if (c == ']' && p != p1 + 1) break;
- if (*p == '-' && p[1] != ']')
- {
- PATFETCH (c1);
- PATFETCH (c1);
- while (c <= c1)
- b[c / BYTEWIDTH] |= 1 << (c % BYTEWIDTH), c++;
- }
- else
- {
- b[c / BYTEWIDTH] |= 1 << (c % BYTEWIDTH);
- }
- }
- /* Discard any bitmap bytes that are all 0 at the end of the map.
- Decrement the map-length byte too. */
- while ((int) b[-1] > 0 && b[b[-1] - 1] == 0)
- b[-1]--;
- b += b[-1];
+ case succeed_n:
+ extract_number_and_incr (&mcnt, &p);
+ extract_number_and_incr (&mcnt2, &p);
+ printf ("/succeed_n to %d, %d times", p + mcnt - start, mcnt2);
+ break;
+
+ case jump_n:
+ extract_number_and_incr (&mcnt, &p);
+ extract_number_and_incr (&mcnt2, &p);
+ printf ("/jump_n to %d, %d times", p + mcnt - start, mcnt2);
+ break;
+
+ case set_number_at:
+ extract_number_and_incr (&mcnt, &p);
+ extract_number_and_incr (&mcnt2, &p);
+ printf ("/set_number_at location %d to %d", p + mcnt - start, mcnt2);
+ break;
+
+ case wordbound:
+ printf ("/wordbound");
+ break;
+
+ case notwordbound:
+ printf ("/notwordbound");
+ break;
+
+ case wordbeg:
+ printf ("/wordbeg");
+ break;
+
+ case wordend:
+ printf ("/wordend");
+
+#ifdef emacs
+ case before_dot:
+ printf ("/before_dot");
+ break;
+
+ case at_dot:
+ printf ("/at_dot");
+ break;
+
+ case after_dot:
+ printf ("/after_dot");
+ break;
+
+ case syntaxspec:
+ printf ("/syntaxspec");
+ mcnt = *p++;
+ printf ("/%d", mcnt);
+ break;
+
+ case notsyntaxspec:
+ printf ("/notsyntaxspec");
+ mcnt = *p++;
+ printf ("/%d", mcnt);
+ break;
+#endif /* emacs */
+
+ case wordchar:
+ printf ("/wordchar");
+ break;
+
+ case notwordchar:
+ printf ("/notwordchar");
+ break;
+
+ case begbuf:
+ printf ("/begbuf");
+ break;
+
+ case endbuf:
+ printf ("/endbuf");
+ break;
+
+ default:
+ printf ("?%d", *(p-1));
+ }
+
+ putchar ('\n');
+ }
+
+ printf ("%d:\tend of pattern.\n", p - start);
+}
+
+
+void
+print_compiled_pattern (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ unsigned char *buffer = bufp->buffer;
+
+ print_partial_compiled_pattern (buffer, buffer + bufp->used);
+ printf ("%d bytes used/%d bytes allocated.\n", bufp->used, bufp->allocated);
+
+ if (bufp->fastmap_accurate && bufp->fastmap)
+ {
+ printf ("fastmap: ");
+ print_fastmap (bufp->fastmap);
+ }
+
+ printf ("re_nsub: %d\t", bufp->re_nsub);
+ printf ("regs_alloc: %d\t", bufp->regs_allocated);
+ printf ("can_be_null: %d\t", bufp->can_be_null);
+ printf ("newline_anchor: %d\n", bufp->newline_anchor);
+ printf ("no_sub: %d\t", bufp->no_sub);
+ printf ("not_bol: %d\t", bufp->not_bol);
+ printf ("not_eol: %d\t", bufp->not_eol);
+ printf ("syntax: %d\n", bufp->syntax);
+ /* Perhaps we should print the translate table? */
+}
+
+
+void
+print_double_string (where, string1, size1, string2, size2)
+ const char *where;
+ const char *string1;
+ const char *string2;
+ int size1;
+ int size2;
+{
+ unsigned this_char;
+
+ if (where == NULL)
+ printf ("(null)");
+ else
+ {
+ if (FIRST_STRING_P (where))
+ {
+ for (this_char = where - string1; this_char < size1; this_char++)
+ printchar (string1[this_char]);
+
+ where = string2;
+ }
+
+ for (this_char = where - string2; this_char < size2; this_char++)
+ printchar (string2[this_char]);
+ }
+}
+
+#else /* not DEBUG */
+
+#undef assert
+#define assert(e)
+
+#define DEBUG_STATEMENT(e)
+#define DEBUG_PRINT1(x)
+#define DEBUG_PRINT2(x1, x2)
+#define DEBUG_PRINT3(x1, x2, x3)
+#define DEBUG_PRINT4(x1, x2, x3, x4)
+#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)
+#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)
+
+#endif /* not DEBUG */
+
+/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can
+ also be assigned to arbitrarily: each pattern buffer stores its own
+ syntax, so it can be changed between regex compilations. */
+reg_syntax_t re_syntax_options = RE_SYNTAX_EMACS;
+
+
+/* Specify the precise syntax of regexps for compilation. This provides
+ for compatibility for various utilities which historically have
+ different, incompatible syntaxes.
+
+ The argument SYNTAX is a bit mask comprised of the various bits
+ defined in regex.h. We return the old syntax. */
+
+reg_syntax_t
+re_set_syntax (syntax)
+ reg_syntax_t syntax;
+{
+ reg_syntax_t ret = re_syntax_options;
+
+ re_syntax_options = syntax;
+ return ret;
+}
+
+/* This table gives an error message for each of the error codes listed
+ in regex.h. Obviously the order here has to be same as there. */
+
+static const char *re_error_msg[] =
+ { NULL, /* REG_NOERROR */
+ "No match", /* REG_NOMATCH */
+ "Invalid regular expression", /* REG_BADPAT */
+ "Invalid collation character", /* REG_ECOLLATE */
+ "Invalid character class name", /* REG_ECTYPE */
+ "Trailing backslash", /* REG_EESCAPE */
+ "Invalid back reference", /* REG_ESUBREG */
+ "Unmatched [ or [^", /* REG_EBRACK */
+ "Unmatched ( or \\(", /* REG_EPAREN */
+ "Unmatched \\{", /* REG_EBRACE */
+ "Invalid content of \\{\\}", /* REG_BADBR */
+ "Invalid range end", /* REG_ERANGE */
+ "Memory exhausted", /* REG_ESPACE */
+ "Invalid preceding regular expression", /* REG_BADRPT */
+ "Premature end of regular expression", /* REG_EEND */
+ "Regular expression too big", /* REG_ESIZE */
+ "Unmatched ) or \\)", /* REG_ERPAREN */
+ };
+
+/* Subroutine declarations and macros for regex_compile. */
+
+static void store_op1 (), store_op2 ();
+static void insert_op1 (), insert_op2 ();
+static boolean at_begline_loc_p (), at_endline_loc_p ();
+static boolean group_in_compile_stack ();
+static reg_errcode_t compile_range ();
+
+/* Fetch the next character in the uncompiled pattern---translating it
+ if necessary. Also cast from a signed character in the constant
+ string passed to us by the user to an unsigned char that we can use
+ as an array index (in, e.g., `translate'). */
+#define PATFETCH(c) \
+ do {if (p == pend) return REG_EEND; \
+ c = (unsigned char) *p++; \
+ if (translate) c = translate[c]; \
+ } while (0)
+
+/* Fetch the next character in the uncompiled pattern, with no
+ translation. */
+#define PATFETCH_RAW(c) \
+ do {if (p == pend) return REG_EEND; \
+ c = (unsigned char) *p++; \
+ } while (0)
+
+/* Go backwards one character in the pattern. */
+#define PATUNFETCH p--
+
+
+/* If `translate' is non-null, return translate[D], else just D. We
+ cast the subscript to translate because some data is declared as
+ `char *', to avoid warnings when a string constant is passed. But
+ when we use a character as a subscript we must make it unsigned. */
+#define TRANSLATE(d) (translate ? translate[(unsigned char) (d)] : (d))
+
+
+/* Macros for outputting the compiled pattern into `buffer'. */
+
+/* If the buffer isn't allocated when it comes in, use this. */
+#define INIT_BUF_SIZE 32
+
+/* Make sure we have at least N more bytes of space in buffer. */
+#define GET_BUFFER_SPACE(n) \
+ while (b - bufp->buffer + (n) > bufp->allocated) \
+ EXTEND_BUFFER ()
+
+/* Make sure we have one more byte of buffer space and then add C to it. */
+#define BUF_PUSH(c) \
+ do { \
+ GET_BUFFER_SPACE (1); \
+ *b++ = (unsigned char) (c); \
+ } while (0)
+
+
+/* Ensure we have two more bytes of buffer space and then append C1 and C2. */
+#define BUF_PUSH_2(c1, c2) \
+ do { \
+ GET_BUFFER_SPACE (2); \
+ *b++ = (unsigned char) (c1); \
+ *b++ = (unsigned char) (c2); \
+ } while (0)
+
+
+/* As with BUF_PUSH_2, except for three bytes. */
+#define BUF_PUSH_3(c1, c2, c3) \
+ do { \
+ GET_BUFFER_SPACE (3); \
+ *b++ = (unsigned char) (c1); \
+ *b++ = (unsigned char) (c2); \
+ *b++ = (unsigned char) (c3); \
+ } while (0)
+
+
+/* Store a jump with opcode OP at LOC to location TO. We store a
+ relative address offset by the three bytes the jump itself occupies. */
+#define STORE_JUMP(op, loc, to) \
+ store_op1 (op, loc, (to) - (loc) - 3)
+
+/* Likewise, for a two-argument jump. */
+#define STORE_JUMP2(op, loc, to, arg) \
+ store_op2 (op, loc, (to) - (loc) - 3, arg)
+
+/* Like `STORE_JUMP', but for inserting. Assume `b' is the buffer end. */
+#define INSERT_JUMP(op, loc, to) \
+ insert_op1 (op, loc, (to) - (loc) - 3, b)
+
+/* Like `STORE_JUMP2', but for inserting. Assume `b' is the buffer end. */
+#define INSERT_JUMP2(op, loc, to, arg) \
+ insert_op2 (op, loc, (to) - (loc) - 3, arg, b)
+
+
+/* This is not an arbitrary limit: the arguments which represent offsets
+ into the pattern are two bytes long. So if 2^16 bytes turns out to
+ be too small, many things would have to change. */
+#define MAX_BUF_SIZE (1L << 16)
+
+
+/* Extend the buffer by twice its current size via realloc and
+ reset the pointers that pointed into the old block to point to the
+ correct places in the new one. If extending the buffer results in it
+ being larger than MAX_BUF_SIZE, then flag memory exhausted. */
+#define EXTEND_BUFFER() \
+ do { \
+ unsigned char *old_buffer = bufp->buffer; \
+ if (bufp->allocated == MAX_BUF_SIZE) \
+ return REG_ESIZE; \
+ bufp->allocated <<= 1; \
+ if (bufp->allocated > MAX_BUF_SIZE) \
+ bufp->allocated = MAX_BUF_SIZE; \
+ bufp->buffer = (unsigned char *) realloc (bufp->buffer, bufp->allocated);\
+ if (bufp->buffer == NULL) \
+ return REG_ESPACE; \
+ /* If the buffer moved, move all the pointers into it. */ \
+ if (old_buffer != bufp->buffer) \
+ { \
+ b = (b - old_buffer) + bufp->buffer; \
+ begalt = (begalt - old_buffer) + bufp->buffer; \
+ if (fixup_alt_jump) \
+ fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\
+ if (laststart) \
+ laststart = (laststart - old_buffer) + bufp->buffer; \
+ if (pending_exact) \
+ pending_exact = (pending_exact - old_buffer) + bufp->buffer; \
+ } \
+ } while (0)
+
+
+/* Since we have one byte reserved for the register number argument to
+ {start,stop}_memory, the maximum number of groups we can report
+ things about is what fits in that byte. */
+#define MAX_REGNUM 255
+
+/* But patterns can have more than `MAX_REGNUM' registers. We just
+ ignore the excess. */
+typedef unsigned regnum_t;
+
+
+/* Macros for the compile stack. */
+
+/* Since offsets can go either forwards or backwards, this type needs to
+ be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1. */
+typedef int pattern_offset_t;
+
+typedef struct
+{
+ pattern_offset_t begalt_offset;
+ pattern_offset_t fixup_alt_jump;
+ pattern_offset_t inner_group_offset;
+ pattern_offset_t laststart_offset;
+ regnum_t regnum;
+} compile_stack_elt_t;
+
+
+typedef struct
+{
+ compile_stack_elt_t *stack;
+ unsigned size;
+ unsigned avail; /* Offset of next open position. */
+} compile_stack_type;
+
+
+#define INIT_COMPILE_STACK_SIZE 32
+
+#define COMPILE_STACK_EMPTY (compile_stack.avail == 0)
+#define COMPILE_STACK_FULL (compile_stack.avail == compile_stack.size)
+
+/* The next available element. */
+#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail])
+
+
+/* Set the bit for character C in a list. */
+#define SET_LIST_BIT(c) \
+ (b[((unsigned char) (c)) / BYTEWIDTH] \
+ |= 1 << (((unsigned char) c) % BYTEWIDTH))
+
+
+/* Get the next unsigned number in the uncompiled pattern. */
+#define GET_UNSIGNED_NUMBER(num) \
+ { if (p != pend) \
+ { \
+ PATFETCH (c); \
+ while (ISDIGIT (c)) \
+ { \
+ if (num < 0) \
+ num = 0; \
+ num = num * 10 + c - '0'; \
+ if (p == pend) \
+ break; \
+ PATFETCH (c); \
+ } \
+ } \
+ }
+
+#define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */
+
+#define IS_CHAR_CLASS(string) \
+ (STREQ (string, "alpha") || STREQ (string, "upper") \
+ || STREQ (string, "lower") || STREQ (string, "digit") \
+ || STREQ (string, "alnum") || STREQ (string, "xdigit") \
+ || STREQ (string, "space") || STREQ (string, "print") \
+ || STREQ (string, "punct") || STREQ (string, "graph") \
+ || STREQ (string, "cntrl") || STREQ (string, "blank"))
+
+/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX.
+ Returns one of error codes defined in `regex.h', or zero for success.
+
+ Assumes the `allocated' (and perhaps `buffer') and `translate'
+ fields are set in BUFP on entry.
+
+ If it succeeds, results are put in BUFP (if it returns an error, the
+ contents of BUFP are undefined):
+ `buffer' is the compiled pattern;
+ `syntax' is set to SYNTAX;
+ `used' is set to the length of the compiled pattern;
+ `fastmap_accurate' is zero;
+ `re_nsub' is the number of subexpressions in PATTERN;
+ `not_bol' and `not_eol' are zero;
+
+ The `fastmap' and `newline_anchor' fields are neither
+ examined nor set. */
+
+static reg_errcode_t
+regex_compile (pattern, size, syntax, bufp)
+ const char *pattern;
+ int size;
+ reg_syntax_t syntax;
+ struct re_pattern_buffer *bufp;
+{
+ /* We fetch characters from PATTERN here. Even though PATTERN is
+ `char *' (i.e., signed), we declare these variables as unsigned, so
+ they can be reliably used as array indices. */
+ register unsigned char c, c1;
+
+ /* A random tempory spot in PATTERN. */
+ const char *p1;
+
+ /* Points to the end of the buffer, where we should append. */
+ register unsigned char *b;
+
+ /* Keeps track of unclosed groups. */
+ compile_stack_type compile_stack;
+
+ /* Points to the current (ending) position in the pattern. */
+ const char *p = pattern;
+ const char *pend = pattern + size;
+
+ /* How to translate the characters in the pattern. */
+ char *translate = bufp->translate;
+
+ /* Address of the count-byte of the most recently inserted `exactn'
+ command. This makes it possible to tell if a new exact-match
+ character can be added to that command or if the character requires
+ a new `exactn' command. */
+ unsigned char *pending_exact = 0;
+
+ /* Address of start of the most recently finished expression.
+ This tells, e.g., postfix * where to find the start of its
+ operand. Reset at the beginning of groups and alternatives. */
+ unsigned char *laststart = 0;
+
+ /* Address of beginning of regexp, or inside of last group. */
+ unsigned char *begalt;
+
+ /* Place in the uncompiled pattern (i.e., the {) to
+ which to go back if the interval is invalid. */
+ const char *beg_interval;
+
+ /* Address of the place where a forward jump should go to the end of
+ the containing expression. Each alternative of an `or' -- except the
+ last -- ends with a forward jump of this sort. */
+ unsigned char *fixup_alt_jump = 0;
+
+ /* Counts open-groups as they are encountered. Remembered for the
+ matching close-group on the compile stack, so the same register
+ number is put in the stop_memory as the start_memory. */
+ regnum_t regnum = 0;
+
+#ifdef DEBUG
+ DEBUG_PRINT1 ("\nCompiling pattern: ");
+ if (debug)
+ {
+ unsigned debug_count;
+
+ for (debug_count = 0; debug_count < size; debug_count++)
+ printchar (pattern[debug_count]);
+ putchar ('\n');
+ }
+#endif /* DEBUG */
+
+ /* Initialize the compile stack. */
+ compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t);
+ if (compile_stack.stack == NULL)
+ return REG_ESPACE;
+
+ compile_stack.size = INIT_COMPILE_STACK_SIZE;
+ compile_stack.avail = 0;
+
+ /* Initialize the pattern buffer. */
+ bufp->syntax = syntax;
+ bufp->fastmap_accurate = 0;
+ bufp->not_bol = bufp->not_eol = 0;
+
+ /* Set `used' to zero, so that if we return an error, the pattern
+ printer (for debugging) will think there's no pattern. We reset it
+ at the end. */
+ bufp->used = 0;
+
+ /* Always count groups, whether or not bufp->no_sub is set. */
+ bufp->re_nsub = 0;
+
+#if !defined (emacs) && !defined (SYNTAX_TABLE)
+ /* Initialize the syntax table. */
+ init_syntax_once ();
+#endif
+
+ if (bufp->allocated == 0)
+ {
+ if (bufp->buffer)
+ { /* If zero allocated, but buffer is non-null, try to realloc
+ enough space. This loses if buffer's address is bogus, but
+ that is the user's responsibility. */
+ RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char);
+ }
+ else
+ { /* Caller did not allocate a buffer. Do it for them. */
+ bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char);
+ }
+ if (!bufp->buffer) return REG_ESPACE;
+
+ bufp->allocated = INIT_BUF_SIZE;
+ }
+
+ begalt = b = bufp->buffer;
+
+ /* Loop through the uncompiled pattern until we're at the end. */
+ while (p != pend)
+ {
+ PATFETCH (c);
+
+ switch (c)
+ {
+ case '^':
+ {
+ if ( /* If at start of pattern, it's an operator. */
+ p == pattern + 1
+ /* If context independent, it's an operator. */
+ || syntax & RE_CONTEXT_INDEP_ANCHORS
+ /* Otherwise, depends on what's come before. */
+ || at_begline_loc_p (pattern, p, syntax))
+ BUF_PUSH (begline);
+ else
+ goto normal_char;
+ }
+ break;
+
+
+ case '$':
+ {
+ if ( /* If at end of pattern, it's an operator. */
+ p == pend
+ /* If context independent, it's an operator. */
+ || syntax & RE_CONTEXT_INDEP_ANCHORS
+ /* Otherwise, depends on what's next. */
+ || at_endline_loc_p (p, pend, syntax))
+ BUF_PUSH (endline);
+ else
+ goto normal_char;
+ }
+ break;
+
+
+ case '+':
+ case '?':
+ if ((syntax & RE_BK_PLUS_QM)
+ || (syntax & RE_LIMITED_OPS))
+ goto normal_char;
+ handle_plus:
+ case '*':
+ /* If there is no previous pattern... */
+ if (!laststart)
+ {
+ if (syntax & RE_CONTEXT_INVALID_OPS)
+ return REG_BADRPT;
+ else if (!(syntax & RE_CONTEXT_INDEP_OPS))
+ goto normal_char;
+ }
+
+ {
+ /* Are we optimizing this jump? */
+ boolean keep_string_p = false;
+
+ /* 1 means zero (many) matches is allowed. */
+ char zero_times_ok = 0, many_times_ok = 0;
+
+ /* If there is a sequence of repetition chars, collapse it
+ down to just one (the right one). We can't combine
+ interval operators with these because of, e.g., `a{2}*',
+ which should only match an even number of `a's. */
+
+ for (;;)
+ {
+ zero_times_ok |= c != '+';
+ many_times_ok |= c != '?';
+
+ if (p == pend)
+ break;
+
+ PATFETCH (c);
+
+ if (c == '*'
+ || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?')))
+ ;
+
+ else if (syntax & RE_BK_PLUS_QM && c == '\\')
+ {
+ if (p == pend) return REG_EESCAPE;
+
+ PATFETCH (c1);
+ if (!(c1 == '+' || c1 == '?'))
+ {
+ PATUNFETCH;
+ PATUNFETCH;
+ break;
+ }
+
+ c = c1;
+ }
+ else
+ {
+ PATUNFETCH;
+ break;
+ }
+
+ /* If we get here, we found another repeat character. */
+ }
+
+ /* Star, etc. applied to an empty pattern is equivalent
+ to an empty pattern. */
+ if (!laststart)
+ break;
+
+ /* Now we know whether or not zero matches is allowed
+ and also whether or not two or more matches is allowed. */
+ if (many_times_ok)
+ { /* More than one repetition is allowed, so put in at the
+ end a backward relative jump from `b' to before the next
+ jump we're going to put in below (which jumps from
+ laststart to after this jump).
+
+ But if we are at the `*' in the exact sequence `.*\n',
+ insert an unconditional jump backwards to the .,
+ instead of the beginning of the loop. This way we only
+ push a failure point once, instead of every time
+ through the loop. */
+ assert (p - 1 > pattern);
+
+ /* Allocate the space for the jump. */
+ GET_BUFFER_SPACE (3);
+
+ /* We know we are not at the first character of the pattern,
+ because laststart was nonzero. And we've already
+ incremented `p', by the way, to be the character after
+ the `*'. Do we have to do something analogous here
+ for null bytes, because of RE_DOT_NOT_NULL? */
+ if (TRANSLATE (*(p - 2)) == TRANSLATE ('.')
+ && zero_times_ok
+ && p < pend && TRANSLATE (*p) == TRANSLATE ('\n')
+ && !(syntax & RE_DOT_NEWLINE))
+ { /* We have .*\n. */
+ STORE_JUMP (jump, b, laststart);
+ keep_string_p = true;
+ }
+ else
+ /* Anything else. */
+ STORE_JUMP (maybe_pop_jump, b, laststart - 3);
+
+ /* We've added more stuff to the buffer. */
+ b += 3;
+ }
+
+ /* On failure, jump from laststart to b + 3, which will be the
+ end of the buffer after this jump is inserted. */
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump
+ : on_failure_jump,
+ laststart, b + 3);
+ pending_exact = 0;
+ b += 3;
+
+ if (!zero_times_ok)
+ {
+ /* At least one repetition is required, so insert a
+ `dummy_failure_jump' before the initial
+ `on_failure_jump' instruction of the loop. This
+ effects a skip over that instruction the first time
+ we hit that loop. */
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6);
+ b += 3;
+ }
+ }
break;
+
+ case '.':
+ laststart = b;
+ BUF_PUSH (anychar);
+ break;
+
+
+ case '[':
+ {
+ boolean had_char_class = false;
+
+ if (p == pend) return REG_EBRACK;
+
+ /* Ensure that we have enough space to push a charset: the
+ opcode, the length count, and the bitset; 34 bytes in all. */
+ GET_BUFFER_SPACE (34);
+
+ laststart = b;
+
+ /* We test `*p == '^' twice, instead of using an if
+ statement, so we only need one BUF_PUSH. */
+ BUF_PUSH (*p == '^' ? charset_not : charset);
+ if (*p == '^')
+ p++;
+
+ /* Remember the first position in the bracket expression. */
+ p1 = p;
+
+ /* Push the number of bytes in the bitmap. */
+ BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
+
+ /* Clear the whole map. */
+ bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH);
+
+ /* charset_not matches newline according to a syntax bit. */
+ if ((re_opcode_t) b[-2] == charset_not
+ && (syntax & RE_HAT_LISTS_NOT_NEWLINE))
+ SET_LIST_BIT ('\n');
+
+ /* Read in characters and ranges, setting map bits. */
+ for (;;)
+ {
+ if (p == pend) return REG_EBRACK;
+
+ PATFETCH (c);
+
+ /* \ might escape characters inside [...] and [^...]. */
+ if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\')
+ {
+ if (p == pend) return REG_EESCAPE;
+
+ PATFETCH (c1);
+ SET_LIST_BIT (c1);
+ continue;
+ }
+
+ /* Could be the end of the bracket expression. If it's
+ not (i.e., when the bracket expression is `[]' so
+ far), the ']' character bit gets set way below. */
+ if (c == ']' && p != p1 + 1)
+ break;
+
+ /* Look ahead to see if it's a range when the last thing
+ was a character class. */
+ if (had_char_class && c == '-' && *p != ']')
+ return REG_ERANGE;
+
+ /* Look ahead to see if it's a range when the last thing
+ was a character: if this is a hyphen not at the
+ beginning or the end of a list, then it's the range
+ operator. */
+ if (c == '-'
+ && !(p - 2 >= pattern && p[-2] == '[')
+ && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^')
+ && *p != ']')
+ {
+ reg_errcode_t ret
+ = compile_range (&p, pend, translate, syntax, b);
+ if (ret != REG_NOERROR) return ret;
+ }
+
+ else if (p[0] == '-' && p[1] != ']')
+ { /* This handles ranges made up of characters only. */
+ reg_errcode_t ret;
+
+ /* Move past the `-'. */
+ PATFETCH (c1);
+
+ ret = compile_range (&p, pend, translate, syntax, b);
+ if (ret != REG_NOERROR) return ret;
+ }
+
+ /* See if we're at the beginning of a possible character
+ class. */
+
+ else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':')
+ { /* Leave room for the null. */
+ char str[CHAR_CLASS_MAX_LENGTH + 1];
+
+ PATFETCH (c);
+ c1 = 0;
+
+ /* If pattern is `[[:'. */
+ if (p == pend) return REG_EBRACK;
+
+ for (;;)
+ {
+ PATFETCH (c);
+ if (c == ':' || c == ']' || p == pend
+ || c1 == CHAR_CLASS_MAX_LENGTH)
+ break;
+ str[c1++] = c;
+ }
+ str[c1] = '\0';
+
+ /* If isn't a word bracketed by `[:' and:`]':
+ undo the ending character, the letters, and leave
+ the leading `:' and `[' (but set bits for them). */
+ if (c == ':' && *p == ']')
+ {
+ int ch;
+ boolean is_alnum = STREQ (str, "alnum");
+ boolean is_alpha = STREQ (str, "alpha");
+ boolean is_blank = STREQ (str, "blank");
+ boolean is_cntrl = STREQ (str, "cntrl");
+ boolean is_digit = STREQ (str, "digit");
+ boolean is_graph = STREQ (str, "graph");
+ boolean is_lower = STREQ (str, "lower");
+ boolean is_print = STREQ (str, "print");
+ boolean is_punct = STREQ (str, "punct");
+ boolean is_space = STREQ (str, "space");
+ boolean is_upper = STREQ (str, "upper");
+ boolean is_xdigit = STREQ (str, "xdigit");
+
+ if (!IS_CHAR_CLASS (str)) return REG_ECTYPE;
+
+ /* Throw away the ] at the end of the character
+ class. */
+ PATFETCH (c);
+
+ if (p == pend) return REG_EBRACK;
+
+ for (ch = 0; ch < 1 << BYTEWIDTH; ch++)
+ {
+ if ( (is_alnum && ISALNUM (ch))
+ || (is_alpha && ISALPHA (ch))
+ || (is_blank && ISBLANK (ch))
+ || (is_cntrl && ISCNTRL (ch))
+ || (is_digit && ISDIGIT (ch))
+ || (is_graph && ISGRAPH (ch))
+ || (is_lower && ISLOWER (ch))
+ || (is_print && ISPRINT (ch))
+ || (is_punct && ISPUNCT (ch))
+ || (is_space && ISSPACE (ch))
+ || (is_upper && ISUPPER (ch))
+ || (is_xdigit && ISXDIGIT (ch)))
+ SET_LIST_BIT (ch);
+ }
+ had_char_class = true;
+ }
+ else
+ {
+ c1++;
+ while (c1--)
+ PATUNFETCH;
+ SET_LIST_BIT ('[');
+ SET_LIST_BIT (':');
+ had_char_class = false;
+ }
+ }
+ else
+ {
+ had_char_class = false;
+ SET_LIST_BIT (c);
+ }
+ }
+
+ /* Discard any (non)matching list bytes that are all 0 at the
+ end of the map. Decrease the map-length byte too. */
+ while ((int) b[-1] > 0 && b[b[-1] - 1] == 0)
+ b[-1]--;
+ b += b[-1];
+ }
+ break;
+
+
case '(':
- if (! (obscure_syntax & RE_NO_BK_PARENS))
- goto normal_char;
- else
- goto handle_open;
+ if (syntax & RE_NO_BK_PARENS)
+ goto handle_open;
+ else
+ goto normal_char;
- case ')':
- if (! (obscure_syntax & RE_NO_BK_PARENS))
- goto normal_char;
- else
- goto handle_close;
- case '\n':
- if (! (obscure_syntax & RE_NEWLINE_OR))
- goto normal_char;
- else
- goto handle_bar;
+ case ')':
+ if (syntax & RE_NO_BK_PARENS)
+ goto handle_close;
+ else
+ goto normal_char;
+
+
+ case '\n':
+ if (syntax & RE_NEWLINE_ALT)
+ goto handle_alt;
+ else
+ goto normal_char;
+
case '|':
- if (! (obscure_syntax & RE_NO_BK_VBAR))
- goto normal_char;
- else
- goto handle_bar;
+ if (syntax & RE_NO_BK_VBAR)
+ goto handle_alt;
+ else
+ goto normal_char;
+
+
+ case '{':
+ if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES)
+ goto handle_interval;
+ else
+ goto normal_char;
+
case '\\':
- if (p == pend) goto invalid_pattern;
- PATFETCH_RAW (c);
- switch (c)
- {
- case '(':
- if (obscure_syntax & RE_NO_BK_PARENS)
- goto normal_backsl;
- handle_open:
- if (stackp == stacke) goto nesting_too_deep;
- if (regnum < RE_NREGS)
- {
- PATPUSH (start_memory);
- PATPUSH (regnum);
- }
- *stackp++ = b - bufp->buffer;
- *stackp++ = fixup_jump ? fixup_jump - bufp->buffer + 1 : 0;
- *stackp++ = regnum++;
- *stackp++ = begalt - bufp->buffer;
- fixup_jump = 0;
- laststart = 0;
- begalt = b;
- break;
-
- case ')':
- if (obscure_syntax & RE_NO_BK_PARENS)
- goto normal_backsl;
- handle_close:
- if (stackp == stackb) goto unmatched_close;
- begalt = *--stackp + bufp->buffer;
- if (fixup_jump)
- store_jump (fixup_jump, jump, b);
- if (stackp[-1] < RE_NREGS)
- {
- PATPUSH (stop_memory);
- PATPUSH (stackp[-1]);
- }
- stackp -= 2;
- fixup_jump = 0;
- if (*stackp)
- fixup_jump = *stackp + bufp->buffer - 1;
- laststart = *--stackp + bufp->buffer;
- break;
-
- case '|':
- if (obscure_syntax & RE_NO_BK_VBAR)
- goto normal_backsl;
- handle_bar:
- insert_jump (on_failure_jump, begalt, b + 6, b);
+ if (p == pend) return REG_EESCAPE;
+
+ /* Do not translate the character after the \, so that we can
+ distinguish, e.g., \B from \b, even if we normally would
+ translate, e.g., B to b. */
+ PATFETCH_RAW (c);
+
+ switch (c)
+ {
+ case '(':
+ if (syntax & RE_NO_BK_PARENS)
+ goto normal_backslash;
+
+ handle_open:
+ bufp->re_nsub++;
+ regnum++;
+
+ if (COMPILE_STACK_FULL)
+ {
+ RETALLOC (compile_stack.stack, compile_stack.size << 1,
+ compile_stack_elt_t);
+ if (compile_stack.stack == NULL) return REG_ESPACE;
+
+ compile_stack.size <<= 1;
+ }
+
+ /* These are the values to restore when we hit end of this
+ group. They are all relative offsets, so that if the
+ whole pattern moves because of realloc, they will still
+ be valid. */
+ COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer;
+ COMPILE_STACK_TOP.fixup_alt_jump
+ = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0;
+ COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer;
+ COMPILE_STACK_TOP.regnum = regnum;
+
+ /* We will eventually replace the 0 with the number of
+ groups inner to this one. But do not push a
+ start_memory for groups beyond the last one we can
+ represent in the compiled pattern. */
+ if (regnum <= MAX_REGNUM)
+ {
+ COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2;
+ BUF_PUSH_3 (start_memory, regnum, 0);
+ }
+
+ compile_stack.avail++;
+
+ fixup_alt_jump = 0;
+ laststart = 0;
+ begalt = b;
+ /* If we've reached MAX_REGNUM groups, then this open
+ won't actually generate any code, so we'll have to
+ clear pending_exact explicitly. */
pending_exact = 0;
- b += 3;
- if (fixup_jump)
- store_jump (fixup_jump, jump, b);
- fixup_jump = b;
- b += 3;
- laststart = 0;
- begalt = b;
- break;
+ break;
+
+
+ case ')':
+ if (syntax & RE_NO_BK_PARENS) goto normal_backslash;
+
+ if (COMPILE_STACK_EMPTY)
+ if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+ goto normal_backslash;
+ else
+ return REG_ERPAREN;
+
+ handle_close:
+ if (fixup_alt_jump)
+ { /* Push a dummy failure point at the end of the
+ alternative for a possible future
+ `pop_failure_jump' to pop. See comments at
+ `push_dummy_failure' in `re_match_2'. */
+ BUF_PUSH (push_dummy_failure);
+
+ /* We allocated space for this jump when we assigned
+ to `fixup_alt_jump', in the `handle_alt' case below. */
+ STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1);
+ }
+
+ /* See similar code for backslashed left paren above. */
+ if (COMPILE_STACK_EMPTY)
+ if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+ goto normal_char;
+ else
+ return REG_ERPAREN;
+
+ /* Since we just checked for an empty stack above, this
+ ``can't happen''. */
+ assert (compile_stack.avail != 0);
+ {
+ /* We don't just want to restore into `regnum', because
+ later groups should continue to be numbered higher,
+ as in `(ab)c(de)' -- the second group is #2. */
+ regnum_t this_group_regnum;
+
+ compile_stack.avail--;
+ begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset;
+ fixup_alt_jump
+ = COMPILE_STACK_TOP.fixup_alt_jump
+ ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1
+ : 0;
+ laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset;
+ this_group_regnum = COMPILE_STACK_TOP.regnum;
+ /* If we've reached MAX_REGNUM groups, then this open
+ won't actually generate any code, so we'll have to
+ clear pending_exact explicitly. */
+ pending_exact = 0;
+
+ /* We're at the end of the group, so now we know how many
+ groups were inside this one. */
+ if (this_group_regnum <= MAX_REGNUM)
+ {
+ unsigned char *inner_group_loc
+ = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset;
+
+ *inner_group_loc = regnum - this_group_regnum;
+ BUF_PUSH_3 (stop_memory, this_group_regnum,
+ regnum - this_group_regnum);
+ }
+ }
+ break;
+
+
+ case '|': /* `\|'. */
+ if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR)
+ goto normal_backslash;
+ handle_alt:
+ if (syntax & RE_LIMITED_OPS)
+ goto normal_char;
+
+ /* Insert before the previous alternative a jump which
+ jumps to this alternative if the former fails. */
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (on_failure_jump, begalt, b + 6);
+ pending_exact = 0;
+ b += 3;
+
+ /* The alternative before this one has a jump after it
+ which gets executed if it gets matched. Adjust that
+ jump so it will jump to this alternative's analogous
+ jump (put in below, which in turn will jump to the next
+ (if any) alternative's such jump, etc.). The last such
+ jump jumps to the correct final destination. A picture:
+ _____ _____
+ | | | |
+ | v | v
+ a | b | c
+
+ If we are at `b', then fixup_alt_jump right now points to a
+ three-byte space after `a'. We'll put in the jump, set
+ fixup_alt_jump to right after `b', and leave behind three
+ bytes which we'll fill in when we get to after `c'. */
+
+ if (fixup_alt_jump)
+ STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
+
+ /* Mark and leave space for a jump after this alternative,
+ to be filled in later either by next alternative or
+ when know we're at the end of a series of alternatives. */
+ fixup_alt_jump = b;
+ GET_BUFFER_SPACE (3);
+ b += 3;
+
+ laststart = 0;
+ begalt = b;
+ break;
+
+
+ case '{':
+ /* If \{ is a literal. */
+ if (!(syntax & RE_INTERVALS)
+ /* If we're at `\{' and it's not the open-interval
+ operator. */
+ || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ || (p - 2 == pattern && p == pend))
+ goto normal_backslash;
+
+ handle_interval:
+ {
+ /* If got here, then the syntax allows intervals. */
+
+ /* At least (most) this many matches must be made. */
+ int lower_bound = -1, upper_bound = -1;
+
+ beg_interval = p - 1;
+
+ if (p == pend)
+ {
+ if (syntax & RE_NO_BK_BRACES)
+ goto unfetch_interval;
+ else
+ return REG_EBRACE;
+ }
+
+ GET_UNSIGNED_NUMBER (lower_bound);
+
+ if (c == ',')
+ {
+ GET_UNSIGNED_NUMBER (upper_bound);
+ if (upper_bound < 0) upper_bound = RE_DUP_MAX;
+ }
+ else
+ /* Interval such as `{1}' => match exactly once. */
+ upper_bound = lower_bound;
+
+ if (lower_bound < 0 || upper_bound > RE_DUP_MAX
+ || lower_bound > upper_bound)
+ {
+ if (syntax & RE_NO_BK_BRACES)
+ goto unfetch_interval;
+ else
+ return REG_BADBR;
+ }
+
+ if (!(syntax & RE_NO_BK_BRACES))
+ {
+ if (c != '\\') return REG_EBRACE;
+
+ PATFETCH (c);
+ }
+
+ if (c != '}')
+ {
+ if (syntax & RE_NO_BK_BRACES)
+ goto unfetch_interval;
+ else
+ return REG_BADBR;
+ }
+
+ /* We just parsed a valid interval. */
+
+ /* If it's invalid to have no preceding re. */
+ if (!laststart)
+ {
+ if (syntax & RE_CONTEXT_INVALID_OPS)
+ return REG_BADRPT;
+ else if (syntax & RE_CONTEXT_INDEP_OPS)
+ laststart = b;
+ else
+ goto unfetch_interval;
+ }
+
+ /* If the upper bound is zero, don't want to succeed at
+ all; jump from `laststart' to `b + 3', which will be
+ the end of the buffer after we insert the jump. */
+ if (upper_bound == 0)
+ {
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (jump, laststart, b + 3);
+ b += 3;
+ }
+
+ /* Otherwise, we have a nontrivial interval. When
+ we're all done, the pattern will look like:
+ set_number_at <jump count> <upper bound>
+ set_number_at <succeed_n count> <lower bound>
+ succeed_n <after jump addr> <succed_n count>
+ <body of loop>
+ jump_n <succeed_n addr> <jump count>
+ (The upper bound and `jump_n' are omitted if
+ `upper_bound' is 1, though.) */
+ else
+ { /* If the upper bound is > 1, we need to insert
+ more at the end of the loop. */
+ unsigned nbytes = 10 + (upper_bound > 1) * 10;
+
+ GET_BUFFER_SPACE (nbytes);
+
+ /* Initialize lower bound of the `succeed_n', even
+ though it will be set during matching by its
+ attendant `set_number_at' (inserted next),
+ because `re_compile_fastmap' needs to know.
+ Jump to the `jump_n' we might insert below. */
+ INSERT_JUMP2 (succeed_n, laststart,
+ b + 5 + (upper_bound > 1) * 5,
+ lower_bound);
+ b += 5;
+
+ /* Code to initialize the lower bound. Insert
+ before the `succeed_n'. The `5' is the last two
+ bytes of this `set_number_at', plus 3 bytes of
+ the following `succeed_n'. */
+ insert_op2 (set_number_at, laststart, 5, lower_bound, b);
+ b += 5;
+
+ if (upper_bound > 1)
+ { /* More than one repetition is allowed, so
+ append a backward jump to the `succeed_n'
+ that starts this interval.
+
+ When we've reached this during matching,
+ we'll have matched the interval once, so
+ jump back only `upper_bound - 1' times. */
+ STORE_JUMP2 (jump_n, b, laststart + 5,
+ upper_bound - 1);
+ b += 5;
+
+ /* The location we want to set is the second
+ parameter of the `jump_n'; that is `b-2' as
+ an absolute address. `laststart' will be
+ the `set_number_at' we're about to insert;
+ `laststart+3' the number to set, the source
+ for the relative address. But we are
+ inserting into the middle of the pattern --
+ so everything is getting moved up by 5.
+ Conclusion: (b - 2) - (laststart + 3) + 5,
+ i.e., b - laststart.
+
+ We insert this at the beginning of the loop
+ so that if we fail during matching, we'll
+ reinitialize the bounds. */
+ insert_op2 (set_number_at, laststart, b - laststart,
+ upper_bound - 1, b);
+ b += 5;
+ }
+ }
+ pending_exact = 0;
+ beg_interval = NULL;
+ }
+ break;
+
+ unfetch_interval:
+ /* If an invalid interval, match the characters as literals. */
+ assert (beg_interval);
+ p = beg_interval;
+ beg_interval = NULL;
+
+ /* normal_char and normal_backslash need `c'. */
+ PATFETCH (c);
+
+ if (!(syntax & RE_NO_BK_BRACES))
+ {
+ if (p > pattern && p[-1] == '\\')
+ goto normal_backslash;
+ }
+ goto normal_char;
#ifdef emacs
- case '=':
- PATPUSH (at_dot);
- break;
-
- case 's':
- laststart = b;
- PATPUSH (syntaxspec);
- PATFETCH (c);
- PATPUSH (syntax_spec_code[c]);
- break;
-
- case 'S':
- laststart = b;
- PATPUSH (notsyntaxspec);
- PATFETCH (c);
- PATPUSH (syntax_spec_code[c]);
- break;
+ /* There is no way to specify the before_dot and after_dot
+ operators. rms says this is ok. --karl */
+ case '=':
+ BUF_PUSH (at_dot);
+ break;
+
+ case 's':
+ laststart = b;
+ PATFETCH (c);
+ BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]);
+ break;
+
+ case 'S':
+ laststart = b;
+ PATFETCH (c);
+ BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]);
+ break;
#endif /* emacs */
- case 'w':
- laststart = b;
- PATPUSH (wordchar);
- break;
-
- case 'W':
- laststart = b;
- PATPUSH (notwordchar);
- break;
-
- case '<':
- PATPUSH (wordbeg);
- break;
-
- case '>':
- PATPUSH (wordend);
- break;
-
- case 'b':
- PATPUSH (wordbound);
- break;
-
- case 'B':
- PATPUSH (notwordbound);
- break;
-
- case '`':
- PATPUSH (begbuf);
- break;
-
- case '\'':
- PATPUSH (endbuf);
- break;
-
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- c1 = c - '0';
- if (c1 >= regnum)
- goto normal_char;
- for (stackt = stackp - 2; stackt > stackb; stackt -= 4)
- if (*stackt == c1)
- goto normal_char;
- laststart = b;
- PATPUSH (duplicate);
- PATPUSH (c1);
- break;
-
- case '+':
- case '?':
- if (obscure_syntax & RE_BK_PLUS_QM)
- goto handle_plus;
-
- default:
- normal_backsl:
- /* You might think it would be useful for \ to mean
- not to translate; but if we don't translate it
- it will never match anything. */
- if (translate) c = translate[c];
- goto normal_char;
- }
- break;
+
+ case 'w':
+ laststart = b;
+ BUF_PUSH (wordchar);
+ break;
+
+
+ case 'W':
+ laststart = b;
+ BUF_PUSH (notwordchar);
+ break;
+
+
+ case '<':
+ BUF_PUSH (wordbeg);
+ break;
+
+ case '>':
+ BUF_PUSH (wordend);
+ break;
+
+ case 'b':
+ BUF_PUSH (wordbound);
+ break;
+
+ case 'B':
+ BUF_PUSH (notwordbound);
+ break;
+
+ case '`':
+ BUF_PUSH (begbuf);
+ break;
+
+ case '\'':
+ BUF_PUSH (endbuf);
+ break;
+
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ if (syntax & RE_NO_BK_REFS)
+ goto normal_char;
+
+ c1 = c - '0';
+
+ if (c1 > regnum)
+ return REG_ESUBREG;
+
+ /* Can't back reference to a subexpression if inside of it. */
+ if (group_in_compile_stack (compile_stack, c1))
+ goto normal_char;
+
+ laststart = b;
+ BUF_PUSH_2 (duplicate, c1);
+ break;
+
+
+ case '+':
+ case '?':
+ if (syntax & RE_BK_PLUS_QM)
+ goto handle_plus;
+ else
+ goto normal_backslash;
+
+ default:
+ normal_backslash:
+ /* You might think it would be useful for \ to mean
+ not to translate; but if we don't translate it
+ it will never match anything. */
+ c = TRANSLATE (c);
+ goto normal_char;
+ }
+ break;
+
default:
+ /* Expects the character in `c'. */
normal_char:
- if (!pending_exact || pending_exact + *pending_exact + 1 != b
- || *pending_exact == 0177 || *p == '*' || *p == '^'
- || ((obscure_syntax & RE_BK_PLUS_QM)
+ /* If no exactn currently being built. */
+ if (!pending_exact
+
+ /* If last exactn not at current position. */
+ || pending_exact + *pending_exact + 1 != b
+
+ /* We have only one byte following the exactn for the count. */
+ || *pending_exact == (1 << BYTEWIDTH) - 1
+
+ /* If followed by a repetition operator. */
+ || *p == '*' || *p == '^'
+ || ((syntax & RE_BK_PLUS_QM)
? *p == '\\' && (p[1] == '+' || p[1] == '?')
- : (*p == '+' || *p == '?')))
+ : (*p == '+' || *p == '?'))
+ || ((syntax & RE_INTERVALS)
+ && ((syntax & RE_NO_BK_BRACES)
+ ? *p == '{'
+ : (p[0] == '\\' && p[1] == '{'))))
{
- laststart = b;
- PATPUSH (exactn);
- pending_exact = b;
- PATPUSH (0);
- }
- PATPUSH (c);
- (*pending_exact)++;
- }
- }
+ /* Start building a new exactn. */
+
+ laststart = b;
+
+ BUF_PUSH_2 (exactn, 0);
+ pending_exact = b - 1;
+ }
+
+ BUF_PUSH (c);
+ (*pending_exact)++;
+ break;
+ } /* switch (c) */
+ } /* while p != pend */
- if (fixup_jump)
- store_jump (fixup_jump, jump, b);
+
+ /* Through the pattern now. */
+
+ if (fixup_alt_jump)
+ STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
+
+ if (!COMPILE_STACK_EMPTY)
+ return REG_EPAREN;
- if (stackp != stackb) goto unmatched_open;
+ free (compile_stack.stack);
+ /* We have succeeded; set the length of the buffer. */
bufp->used = b - bufp->buffer;
- return 0;
- invalid_pattern:
- return "Invalid regular expression";
+#ifdef DEBUG
+ if (debug)
+ {
+ DEBUG_PRINT1 ("\nCompiled pattern: \n");
+ print_compiled_pattern (bufp);
+ }
+#endif /* DEBUG */
- unmatched_open:
- return "Unmatched \\(";
+ return REG_NOERROR;
+} /* regex_compile */
+
+/* Subroutines for `regex_compile'. */
- unmatched_close:
- return "Unmatched \\)";
+/* Store OP at LOC followed by two-byte integer parameter ARG. */
- end_of_pattern:
- return "Premature end of regular expression";
+static void
+store_op1 (op, loc, arg)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg;
+{
+ *loc = (unsigned char) op;
+ STORE_NUMBER (loc + 1, arg);
+}
- nesting_too_deep:
- return "Nesting too deep";
- too_big:
- return "Regular expression too big";
+/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2. */
- memory_exhausted:
- return "Memory exhausted";
+static void
+store_op2 (op, loc, arg1, arg2)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg1, arg2;
+{
+ *loc = (unsigned char) op;
+ STORE_NUMBER (loc + 1, arg1);
+ STORE_NUMBER (loc + 3, arg2);
}
-/* Store where `from' points a jump operation to jump to where `to' points.
- `opcode' is the opcode to store. */
+
+/* Copy the bytes from LOC to END to open up three bytes of space at LOC
+ for OP followed by two-byte integer parameter ARG. */
static void
-store_jump (from, opcode, to)
- char *from, *to;
- char opcode;
+insert_op1 (op, loc, arg, end)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg;
+ unsigned char *end;
{
- from[0] = opcode;
- from[1] = (to - (from + 3)) & 0377;
- from[2] = (to - (from + 3)) >> 8;
+ register unsigned char *pfrom = end;
+ register unsigned char *pto = end + 3;
+
+ while (pfrom != loc)
+ *--pto = *--pfrom;
+
+ store_op1 (op, loc, arg);
}
-/* Open up space at char FROM, and insert there a jump to TO.
- CURRENT_END gives te end of the storage no in use,
- so we know how much data to copy up.
- OP is the opcode of the jump to insert.
- If you call this function, you must zero out pending_exact. */
+/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2. */
static void
-insert_jump (op, from, to, current_end)
- char op;
- char *from, *to, *current_end;
+insert_op2 (op, loc, arg1, arg2, end)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg1, arg2;
+ unsigned char *end;
{
- register char *pto = current_end + 3;
- register char *pfrom = current_end;
- while (pfrom != from)
+ register unsigned char *pfrom = end;
+ register unsigned char *pto = end + 5;
+
+ while (pfrom != loc)
*--pto = *--pfrom;
- store_jump (from, op, to);
+
+ store_op2 (op, loc, arg1, arg2);
+}
+
+
+/* P points to just after a ^ in PATTERN. Return true if that ^ comes
+ after an alternative or a begin-subexpression. We assume there is at
+ least one character before the ^. */
+
+static boolean
+at_begline_loc_p (pattern, p, syntax)
+ const char *pattern, *p;
+ reg_syntax_t syntax;
+{
+ const char *prev = p - 2;
+ boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\';
+
+ return
+ /* After a subexpression? */
+ (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash))
+ /* After an alternative? */
+ || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash));
+}
+
+
+/* The dual of at_begline_loc_p. This one is for $. We assume there is
+ at least one character after the $, i.e., `P < PEND'. */
+
+static boolean
+at_endline_loc_p (p, pend, syntax)
+ const char *p, *pend;
+ int syntax;
+{
+ const char *next = p;
+ boolean next_backslash = *next == '\\';
+ const char *next_next = p + 1 < pend ? p + 1 : NULL;
+
+ return
+ /* Before a subexpression? */
+ (syntax & RE_NO_BK_PARENS ? *next == ')'
+ : next_backslash && next_next && *next_next == ')')
+ /* Before an alternative? */
+ || (syntax & RE_NO_BK_VBAR ? *next == '|'
+ : next_backslash && next_next && *next_next == '|');
+}
+
+
+/* Returns true if REGNUM is in one of COMPILE_STACK's elements and
+ false if it's not. */
+
+static boolean
+group_in_compile_stack (compile_stack, regnum)
+ compile_stack_type compile_stack;
+ regnum_t regnum;
+{
+ int this_element;
+
+ for (this_element = compile_stack.avail - 1;
+ this_element >= 0;
+ this_element--)
+ if (compile_stack.stack[this_element].regnum == regnum)
+ return true;
+
+ return false;
+}
+
+
+/* Read the ending character of a range (in a bracket expression) from the
+ uncompiled pattern *P_PTR (which ends at PEND). We assume the
+ starting character is in `P[-2]'. (`P[-1]' is the character `-'.)
+ Then we set the translation of all bits between the starting and
+ ending characters (inclusive) in the compiled pattern B.
+
+ Return an error code.
+
+ We use these short variable names so we can use the same macros as
+ `regex_compile' itself. */
+
+static reg_errcode_t
+compile_range (p_ptr, pend, translate, syntax, b)
+ const char **p_ptr, *pend;
+ char *translate;
+ reg_syntax_t syntax;
+ unsigned char *b;
+{
+ unsigned this_char;
+
+ const char *p = *p_ptr;
+ int range_start, range_end;
+
+ if (p == pend)
+ return REG_ERANGE;
+
+ /* Even though the pattern is a signed `char *', we need to fetch
+ with unsigned char *'s; if the high bit of the pattern character
+ is set, the range endpoints will be negative if we fetch using a
+ signed char *.
+
+ We also want to fetch the endpoints without translating them; the
+ appropriate translation is done in the bit-setting loop below. */
+ range_start = ((unsigned char *) p)[-2];
+ range_end = ((unsigned char *) p)[0];
+
+ /* Have to increment the pointer into the pattern string, so the
+ caller isn't still at the ending character. */
+ (*p_ptr)++;
+
+ /* If the start is after the end, the range is empty. */
+ if (range_start > range_end)
+ return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR;
+
+ /* Here we see why `this_char' has to be larger than an `unsigned
+ char' -- the range is inclusive, so if `range_end' == 0xff
+ (assuming 8-bit characters), we would otherwise go into an infinite
+ loop, since all characters <= 0xff. */
+ for (this_char = range_start; this_char <= range_end; this_char++)
+ {
+ SET_LIST_BIT (TRANSLATE (this_char));
+ }
+
+ return REG_NOERROR;
}
-/* Given a pattern, compute a fastmap from it.
- The fastmap records which of the (1 << BYTEWIDTH) possible characters
- can start a string that matches the pattern.
- This fastmap is used by re_search to skip quickly over totally implausible text.
+/* Failure stack declarations and macros; both re_compile_fastmap and
+ re_match_2 use a failure stack. These have to be macros because of
+ REGEX_ALLOCATE. */
+
+
+/* Number of failure points for which to initially allocate space
+ when matching. If this number is exceeded, we allocate more
+ space, so it is not a hard limit. */
+#ifndef INIT_FAILURE_ALLOC
+#define INIT_FAILURE_ALLOC 5
+#endif
- The caller must supply the address of a (1 << BYTEWIDTH)-byte data area
- as bufp->fastmap.
- The other components of bufp describe the pattern to be used. */
+/* Roughly the maximum number of failure points on the stack. Would be
+ exactly that if always used MAX_FAILURE_SPACE each time we failed.
+ This is a variable only so users of regex can assign to it; we never
+ change it ourselves. */
+int re_max_failures = 2000;
-void
+typedef const unsigned char *fail_stack_elt_t;
+
+typedef struct
+{
+ fail_stack_elt_t *stack;
+ unsigned size;
+ unsigned avail; /* Offset of next open position. */
+} fail_stack_type;
+
+#define FAIL_STACK_EMPTY() (fail_stack.avail == 0)
+#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0)
+#define FAIL_STACK_FULL() (fail_stack.avail == fail_stack.size)
+#define FAIL_STACK_TOP() (fail_stack.stack[fail_stack.avail])
+
+
+/* Initialize `fail_stack'. Do `return -2' if the alloc fails. */
+
+#define INIT_FAIL_STACK() \
+ do { \
+ fail_stack.stack = (fail_stack_elt_t *) \
+ REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \
+ \
+ if (fail_stack.stack == NULL) \
+ return -2; \
+ \
+ fail_stack.size = INIT_FAILURE_ALLOC; \
+ fail_stack.avail = 0; \
+ } while (0)
+
+
+/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items.
+
+ Return 1 if succeeds, and 0 if either ran out of memory
+ allocating space for it or it was already too large.
+
+ REGEX_REALLOCATE requires `destination' be declared. */
+
+#define DOUBLE_FAIL_STACK(fail_stack) \
+ ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS \
+ ? 0 \
+ : ((fail_stack).stack = (fail_stack_elt_t *) \
+ REGEX_REALLOCATE ((fail_stack).stack, \
+ (fail_stack).size * sizeof (fail_stack_elt_t), \
+ ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)), \
+ \
+ (fail_stack).stack == NULL \
+ ? 0 \
+ : ((fail_stack).size <<= 1, \
+ 1)))
+
+
+/* Push PATTERN_OP on FAIL_STACK.
+
+ Return 1 if was able to do so and 0 if ran out of memory allocating
+ space to do so. */
+#define PUSH_PATTERN_OP(pattern_op, fail_stack) \
+ ((FAIL_STACK_FULL () \
+ && !DOUBLE_FAIL_STACK (fail_stack)) \
+ ? 0 \
+ : ((fail_stack).stack[(fail_stack).avail++] = pattern_op, \
+ 1))
+
+/* This pushes an item onto the failure stack. Must be a four-byte
+ value. Assumes the variable `fail_stack'. Probably should only
+ be called from within `PUSH_FAILURE_POINT'. */
+#define PUSH_FAILURE_ITEM(item) \
+ fail_stack.stack[fail_stack.avail++] = (fail_stack_elt_t) item
+
+/* The complement operation. Assumes `fail_stack' is nonempty. */
+#define POP_FAILURE_ITEM() fail_stack.stack[--fail_stack.avail]
+
+/* Used to omit pushing failure point id's when we're not debugging. */
+#ifdef DEBUG
+#define DEBUG_PUSH PUSH_FAILURE_ITEM
+#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_ITEM ()
+#else
+#define DEBUG_PUSH(item)
+#define DEBUG_POP(item_addr)
+#endif
+
+
+/* Push the information about the state we will need
+ if we ever fail back to it.
+
+ Requires variables fail_stack, regstart, regend, reg_info, and
+ num_regs be declared. DOUBLE_FAIL_STACK requires `destination' be
+ declared.
+
+ Does `return FAILURE_CODE' if runs out of memory. */
+
+#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \
+ do { \
+ char *destination; \
+ /* Must be int, so when we don't save any registers, the arithmetic \
+ of 0 + -1 isn't done as unsigned. */ \
+ int this_reg; \
+ \
+ DEBUG_STATEMENT (failure_id++); \
+ DEBUG_STATEMENT (nfailure_points_pushed++); \
+ DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \
+ DEBUG_PRINT2 (" Before push, next avail: %d\n", (fail_stack).avail);\
+ DEBUG_PRINT2 (" size: %d\n", (fail_stack).size);\
+ \
+ DEBUG_PRINT2 (" slots needed: %d\n", NUM_FAILURE_ITEMS); \
+ DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \
+ \
+ /* Ensure we have enough space allocated for what we will push. */ \
+ while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \
+ { \
+ if (!DOUBLE_FAIL_STACK (fail_stack)) \
+ return failure_code; \
+ \
+ DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \
+ (fail_stack).size); \
+ DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\
+ } \
+ \
+ /* Push the info, starting with the registers. */ \
+ DEBUG_PRINT1 ("\n"); \
+ \
+ for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \
+ this_reg++) \
+ { \
+ DEBUG_PRINT2 (" Pushing reg: %d\n", this_reg); \
+ DEBUG_STATEMENT (num_regs_pushed++); \
+ \
+ DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \
+ PUSH_FAILURE_ITEM (regstart[this_reg]); \
+ \
+ DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \
+ PUSH_FAILURE_ITEM (regend[this_reg]); \
+ \
+ DEBUG_PRINT2 (" info: 0x%x\n ", reg_info[this_reg]); \
+ DEBUG_PRINT2 (" match_null=%d", \
+ REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" matched_something=%d", \
+ MATCHED_SOMETHING (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" ever_matched=%d", \
+ EVER_MATCHED_SOMETHING (reg_info[this_reg])); \
+ DEBUG_PRINT1 ("\n"); \
+ PUSH_FAILURE_ITEM (reg_info[this_reg].word); \
+ } \
+ \
+ DEBUG_PRINT2 (" Pushing low active reg: %d\n", lowest_active_reg);\
+ PUSH_FAILURE_ITEM (lowest_active_reg); \
+ \
+ DEBUG_PRINT2 (" Pushing high active reg: %d\n", highest_active_reg);\
+ PUSH_FAILURE_ITEM (highest_active_reg); \
+ \
+ DEBUG_PRINT2 (" Pushing pattern 0x%x: ", pattern_place); \
+ DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \
+ PUSH_FAILURE_ITEM (pattern_place); \
+ \
+ DEBUG_PRINT2 (" Pushing string 0x%x: `", string_place); \
+ DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \
+ size2); \
+ DEBUG_PRINT1 ("'\n"); \
+ PUSH_FAILURE_ITEM (string_place); \
+ \
+ DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \
+ DEBUG_PUSH (failure_id); \
+ } while (0)
+
+/* This is the number of items that are pushed and popped on the stack
+ for each register. */
+#define NUM_REG_ITEMS 3
+
+/* Individual items aside from the registers. */
+#ifdef DEBUG
+#define NUM_NONREG_ITEMS 5 /* Includes failure point id. */
+#else
+#define NUM_NONREG_ITEMS 4
+#endif
+
+/* We push at most this many items on the stack. */
+#define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS)
+
+/* We actually push this many items. */
+#define NUM_FAILURE_ITEMS \
+ ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS \
+ + NUM_NONREG_ITEMS)
+
+/* How many items can still be added to the stack without overflowing it. */
+#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail)
+
+
+/* Pops what PUSH_FAIL_STACK pushes.
+
+ We restore into the parameters, all of which should be lvalues:
+ STR -- the saved data position.
+ PAT -- the saved pattern position.
+ LOW_REG, HIGH_REG -- the highest and lowest active registers.
+ REGSTART, REGEND -- arrays of string positions.
+ REG_INFO -- array of information about each subexpression.
+
+ Also assumes the variables `fail_stack' and (if debugging), `bufp',
+ `pend', `string1', `size1', `string2', and `size2'. */
+
+#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\
+{ \
+ DEBUG_STATEMENT (fail_stack_elt_t failure_id;) \
+ int this_reg; \
+ const unsigned char *string_temp; \
+ \
+ assert (!FAIL_STACK_EMPTY ()); \
+ \
+ /* Remove failure points and point to how many regs pushed. */ \
+ DEBUG_PRINT1 ("POP_FAILURE_POINT:\n"); \
+ DEBUG_PRINT2 (" Before pop, next avail: %d\n", fail_stack.avail); \
+ DEBUG_PRINT2 (" size: %d\n", fail_stack.size); \
+ \
+ assert (fail_stack.avail >= NUM_NONREG_ITEMS); \
+ \
+ DEBUG_POP (&failure_id); \
+ DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id); \
+ \
+ /* If the saved string location is NULL, it came from an \
+ on_failure_keep_string_jump opcode, and we want to throw away the \
+ saved NULL, thus retaining our current position in the string. */ \
+ string_temp = POP_FAILURE_ITEM (); \
+ if (string_temp != NULL) \
+ str = (const char *) string_temp; \
+ \
+ DEBUG_PRINT2 (" Popping string 0x%x: `", str); \
+ DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \
+ DEBUG_PRINT1 ("'\n"); \
+ \
+ pat = (unsigned char *) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" Popping pattern 0x%x: ", pat); \
+ DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \
+ \
+ /* Restore register info. */ \
+ high_reg = (unsigned) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" Popping high active reg: %d\n", high_reg); \
+ \
+ low_reg = (unsigned) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" Popping low active reg: %d\n", low_reg); \
+ \
+ for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \
+ { \
+ DEBUG_PRINT2 (" Popping reg: %d\n", this_reg); \
+ \
+ reg_info[this_reg].word = POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" info: 0x%x\n", reg_info[this_reg]); \
+ \
+ regend[this_reg] = (const char *) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \
+ \
+ regstart[this_reg] = (const char *) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \
+ } \
+ \
+ DEBUG_STATEMENT (nfailure_points_popped++); \
+} /* POP_FAILURE_POINT */
+
+/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in
+ BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible
+ characters can start a string that matches the pattern. This fastmap
+ is used by re_search to skip quickly over impossible starting points.
+
+ The caller must supply the address of a (1 << BYTEWIDTH)-byte data
+ area as BUFP->fastmap.
+
+ We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in
+ the pattern buffer.
+
+ Returns 0 if we succeed, -2 if an internal error. */
+
+int
re_compile_fastmap (bufp)
struct re_pattern_buffer *bufp;
{
- unsigned char *pattern = (unsigned char *) bufp->buffer;
- int size = bufp->used;
+ int j, k;
+ fail_stack_type fail_stack;
+#ifndef REGEX_MALLOC
+ char *destination;
+#endif
+ /* We don't push any register information onto the failure stack. */
+ unsigned num_regs = 0;
+
register char *fastmap = bufp->fastmap;
- register unsigned char *p = pattern;
+ unsigned char *pattern = bufp->buffer;
+ unsigned long size = bufp->used;
+ const unsigned char *p = pattern;
register unsigned char *pend = pattern + size;
- register int j, k;
- unsigned char *translate = (unsigned char *) bufp->translate;
- unsigned char *stackb[NFAILURES];
- unsigned char **stackp = stackb;
+ /* Assume that each path through the pattern can be null until
+ proven otherwise. We set this false at the bottom of switch
+ statement, to which we get only if a particular path doesn't
+ match the empty string. */
+ boolean path_can_be_null = true;
+
+ /* We aren't doing a `succeed_n' to begin with. */
+ boolean succeed_n_p = false;
- bzero (fastmap, (1 << BYTEWIDTH));
- bufp->fastmap_accurate = 1;
+ assert (fastmap != NULL && p != NULL);
+
+ INIT_FAIL_STACK ();
+ bzero (fastmap, 1 << BYTEWIDTH); /* Assume nothing's valid. */
+ bufp->fastmap_accurate = 1; /* It will be when we're done. */
bufp->can_be_null = 0;
- while (p)
+ while (p != pend || !FAIL_STACK_EMPTY ())
{
if (p == pend)
- {
- bufp->can_be_null = 1;
- break;
+ {
+ bufp->can_be_null |= path_can_be_null;
+
+ /* Reset for next path. */
+ path_can_be_null = true;
+
+ p = fail_stack.stack[--fail_stack.avail];
}
+
+ /* We should never be about to go beyond the end of the pattern. */
+ assert (p < pend);
+
#ifdef SWITCH_ENUM_BUG
- switch ((int) ((enum regexpcode) *p++))
+ switch ((int) ((re_opcode_t) *p++))
#else
- switch ((enum regexpcode) *p++)
+ switch ((re_opcode_t) *p++)
#endif
{
+
+ /* I guess the idea here is to simply not bother with a fastmap
+ if a backreference is used, since it's too hard to figure out
+ the fastmap for the corresponding group. Setting
+ `can_be_null' stops `re_search_2' from using the fastmap, so
+ that is all we do. */
+ case duplicate:
+ bufp->can_be_null = 1;
+ return 0;
+
+
+ /* Following are the cases which match a character. These end
+ with `break'. */
+
case exactn:
- if (translate)
- fastmap[translate[p[1]]] = 1;
- else
- fastmap[p[1]] = 1;
+ fastmap[p[1]] = 1;
break;
- case begline:
- case before_dot:
- case at_dot:
- case after_dot:
- case begbuf:
- case endbuf:
- case wordbound:
- case notwordbound:
- case wordbeg:
- case wordend:
- continue;
- case endline:
- if (translate)
- fastmap[translate['\n']] = 1;
- else
- fastmap['\n'] = 1;
- if (bufp->can_be_null != 1)
- bufp->can_be_null = 2;
+ case charset:
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
+ fastmap[j] = 1;
break;
- case finalize_jump:
- case maybe_finalize_jump:
- case jump:
- case dummy_failure_jump:
- bufp->can_be_null = 1;
- j = *p++ & 0377;
- j += SIGN_EXTEND_CHAR (*(char *)p) << 8;
- p += j + 1; /* The 1 compensates for missing ++ above */
- if (j > 0)
- continue;
- /* Jump backward reached implies we just went through
- the body of a loop and matched nothing.
- Opcode jumped to should be an on_failure_jump.
- Just treat it like an ordinary jump.
- For a * loop, it has pushed its failure point already;
- if so, discard that as redundant. */
- if ((enum regexpcode) *p != on_failure_jump)
- continue;
- p++;
- j = *p++ & 0377;
- j += SIGN_EXTEND_CHAR (*(char *)p) << 8;
- p += j + 1; /* The 1 compensates for missing ++ above */
- if (stackp != stackb && *stackp == p)
- stackp--;
- continue;
-
- case on_failure_jump:
- j = *p++ & 0377;
- j += SIGN_EXTEND_CHAR (*(char *)p) << 8;
- p++;
- *++stackp = p + j;
- continue;
- case start_memory:
- case stop_memory:
- p++;
- continue;
+ case charset_not:
+ /* Chars beyond end of map must be allowed. */
+ for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
+ fastmap[j] = 1;
+
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
+ fastmap[j] = 1;
+ break;
- case duplicate:
- bufp->can_be_null = 1;
- fastmap['\n'] = 1;
- case anychar:
- for (j = 0; j < (1 << BYTEWIDTH); j++)
- if (j != '\n')
- fastmap[j] = 1;
- if (bufp->can_be_null)
- return;
- /* Don't return; check the alternative paths
- so we can set can_be_null if appropriate. */
- break;
case wordchar:
for (j = 0; j < (1 << BYTEWIDTH); j++)
@@ -844,118 +2660,283 @@ re_compile_fastmap (bufp)
fastmap[j] = 1;
break;
+
case notwordchar:
for (j = 0; j < (1 << BYTEWIDTH); j++)
if (SYNTAX (j) != Sword)
fastmap[j] = 1;
break;
+
+ case anychar:
+ /* `.' matches anything ... */
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ fastmap[j] = 1;
+
+ /* ... except perhaps newline. */
+ if (!(bufp->syntax & RE_DOT_NEWLINE))
+ fastmap['\n'] = 0;
+
+ /* Return if we have already set `can_be_null'; if we have,
+ then the fastmap is irrelevant. Something's wrong here. */
+ else if (bufp->can_be_null)
+ return 0;
+
+ /* Otherwise, have to check alternative paths. */
+ break;
+
+
#ifdef emacs
- case syntaxspec:
+ case syntaxspec:
k = *p++;
for (j = 0; j < (1 << BYTEWIDTH); j++)
if (SYNTAX (j) == (enum syntaxcode) k)
fastmap[j] = 1;
break;
+
case notsyntaxspec:
k = *p++;
for (j = 0; j < (1 << BYTEWIDTH); j++)
if (SYNTAX (j) != (enum syntaxcode) k)
fastmap[j] = 1;
break;
-#endif /* emacs */
- case charset:
- for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
- if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
- {
- if (translate)
- fastmap[translate[j]] = 1;
- else
- fastmap[j] = 1;
- }
- break;
- case charset_not:
- /* Chars beyond end of map must be allowed */
- for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
- if (translate)
- fastmap[translate[j]] = 1;
- else
- fastmap[j] = 1;
+ /* All cases after this match the empty string. These end with
+ `continue'. */
+
+
+ case before_dot:
+ case at_dot:
+ case after_dot:
+ continue;
+#endif /* not emacs */
+
+
+ case no_op:
+ case begline:
+ case endline:
+ case begbuf:
+ case endbuf:
+ case wordbound:
+ case notwordbound:
+ case wordbeg:
+ case wordend:
+ case push_dummy_failure:
+ continue;
+
+
+ case jump_n:
+ case pop_failure_jump:
+ case maybe_pop_jump:
+ case jump:
+ case jump_past_alt:
+ case dummy_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (j, p);
+ p += j;
+ if (j > 0)
+ continue;
+
+ /* Jump backward implies we just went through the body of a
+ loop and matched nothing. Opcode jumped to should be
+ `on_failure_jump' or `succeed_n'. Just treat it like an
+ ordinary jump. For a * loop, it has pushed its failure
+ point already; if so, discard that as redundant. */
+ if ((re_opcode_t) *p != on_failure_jump
+ && (re_opcode_t) *p != succeed_n)
+ continue;
+
+ p++;
+ EXTRACT_NUMBER_AND_INCR (j, p);
+ p += j;
+
+ /* If what's on the stack is where we are now, pop it. */
+ if (!FAIL_STACK_EMPTY ()
+ && fail_stack.stack[fail_stack.avail - 1] == p)
+ fail_stack.avail--;
+
+ continue;
+
+
+ case on_failure_jump:
+ case on_failure_keep_string_jump:
+ handle_on_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (j, p);
+
+ /* For some patterns, e.g., `(a?)?', `p+j' here points to the
+ end of the pattern. We don't want to push such a point,
+ since when we restore it above, entering the switch will
+ increment `p' past the end of the pattern. We don't need
+ to push such a point since we obviously won't find any more
+ fastmap entries beyond `pend'. Such a pattern can match
+ the null string, though. */
+ if (p + j < pend)
+ {
+ if (!PUSH_PATTERN_OP (p + j, fail_stack))
+ return -2;
+ }
+ else
+ bufp->can_be_null = 1;
+
+ if (succeed_n_p)
+ {
+ EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */
+ succeed_n_p = false;
+ }
+
+ continue;
+
+
+ case succeed_n:
+ /* Get to the number of times to succeed. */
+ p += 2;
+
+ /* Increment p past the n for when k != 0. */
+ EXTRACT_NUMBER_AND_INCR (k, p);
+ if (k == 0)
+ {
+ p -= 4;
+ succeed_n_p = true; /* Spaghetti code alert. */
+ goto handle_on_failure_jump;
+ }
+ continue;
+
+
+ case set_number_at:
+ p += 4;
+ continue;
+
+
+ case start_memory:
+ case stop_memory:
+ p += 2;
+ continue;
- for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
- if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
- {
- if (translate)
- fastmap[translate[j]] = 1;
- else
- fastmap[j] = 1;
- }
- break;
default:
- break;
- }
+ abort (); /* We have listed all the cases. */
+ } /* switch *p++ */
+
+ /* Getting here means we have found the possible starting
+ characters for one path of the pattern -- and that the empty
+ string does not match. We need not follow this path further.
+ Instead, look at the next alternative (remembered on the
+ stack), or quit if no more. The test at the top of the loop
+ does these things. */
+ path_can_be_null = false;
+ p = pend;
+ } /* while p */
+
+ /* Set `can_be_null' for the last path (also the first path, if the
+ pattern is empty). */
+ bufp->can_be_null |= path_can_be_null;
+ return 0;
+} /* re_compile_fastmap */
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+ ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use
+ this memory for recording register information. STARTS and ENDS
+ must be allocated using the malloc library routine, and must each
+ be at least NUM_REGS * sizeof (regoff_t) bytes long.
- /* Get here means we have successfully found the possible starting characters
- of one path of the pattern. We need not follow this path any farther.
- Instead, look at the next alternative remembered in the stack. */
- if (stackp != stackb)
- p = *stackp--;
- else
- break;
+ If NUM_REGS == 0, then subsequent matches should allocate their own
+ register data.
+
+ Unless this function is called, the first search or match using
+ PATTERN_BUFFER will allocate its own register data, without
+ freeing the old data. */
+
+void
+re_set_registers (bufp, regs, num_regs, starts, ends)
+ struct re_pattern_buffer *bufp;
+ struct re_registers *regs;
+ unsigned num_regs;
+ regoff_t *starts, *ends;
+{
+ if (num_regs)
+ {
+ bufp->regs_allocated = REGS_REALLOCATE;
+ regs->num_regs = num_regs;
+ regs->start = starts;
+ regs->end = ends;
+ }
+ else
+ {
+ bufp->regs_allocated = REGS_UNALLOCATED;
+ regs->num_regs = 0;
+ regs->start = regs->end = (regoff_t) 0;
}
}
-/* Like re_search_2, below, but only one string is specified. */
+/* Searching routines. */
+
+/* Like re_search_2, below, but only one string is specified, and
+ doesn't let you say where to stop matching. */
int
-re_search (pbufp, string, size, startpos, range, regs)
- struct re_pattern_buffer *pbufp;
- char *string;
+re_search (bufp, string, size, startpos, range, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string;
int size, startpos, range;
struct re_registers *regs;
{
- return re_search_2 (pbufp, 0, 0, string, size, startpos, range, regs, size);
+ return re_search_2 (bufp, NULL, 0, string, size, startpos, range,
+ regs, size);
}
-/* Like re_match_2 but tries first a match starting at index STARTPOS,
- then at STARTPOS + 1, and so on.
- RANGE is the number of places to try before giving up.
- If RANGE is negative, the starting positions tried are
- STARTPOS, STARTPOS - 1, etc.
- It is up to the caller to make sure that range is not so large
- as to take the starting position outside of the input strings.
-The value returned is the position at which the match was found,
- or -1 if no match was found,
- or -2 if error (such as failure stack overflow). */
+/* Using the compiled pattern in BUFP->buffer, first tries to match the
+ virtual concatenation of STRING1 and STRING2, starting first at index
+ STARTPOS, then at STARTPOS + 1, and so on.
+
+ STRING1 and STRING2 have length SIZE1 and SIZE2, respectively.
+
+ RANGE is how far to scan while trying to match. RANGE = 0 means try
+ only at STARTPOS; in general, the last start tried is STARTPOS +
+ RANGE.
+
+ In REGS, return the indices of the virtual concatenation of STRING1
+ and STRING2 that matched the entire BUFP->buffer and its contained
+ subexpressions.
+
+ Do not consider matching one past the index STOP in the virtual
+ concatenation of STRING1 and STRING2.
+
+ We return either the position in the strings at which the match was
+ found, -1 if no match, or -2 if error (such as failure
+ stack overflow). */
int
-re_search_2 (pbufp, string1, size1, string2, size2, startpos, range, regs, mstop)
- struct re_pattern_buffer *pbufp;
- char *string1, *string2;
+re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
int size1, size2;
int startpos;
- register int range;
+ int range;
struct re_registers *regs;
- int mstop;
+ int stop;
{
- register char *fastmap = pbufp->fastmap;
- register unsigned char *translate = (unsigned char *) pbufp->translate;
- int total = size1 + size2;
int val;
-
- /* Update the fastmap now if not correct already */
- if (fastmap && !pbufp->fastmap_accurate)
- re_compile_fastmap (pbufp);
-
- /* Don't waste time in a long search for a pattern
- that says it is anchored. */
- if (pbufp->used > 0 && (enum regexpcode) pbufp->buffer[0] == begbuf
- && range > 0)
+ register char *fastmap = bufp->fastmap;
+ register char *translate = bufp->translate;
+ int total_size = size1 + size2;
+ int endpos = startpos + range;
+
+ /* Check for out-of-range STARTPOS. */
+ if (startpos < 0 || startpos > total_size)
+ return -1;
+
+ /* Fix up RANGE if it might eventually take us outside
+ the virtual concatenation of STRING1 and STRING2. */
+ if (endpos < -1)
+ range = -1 - startpos;
+ else if (endpos > total_size)
+ range = total_size - startpos;
+
+ /* If the search isn't to be a backwards one, don't waste time in a
+ search for a pattern that must be anchored. */
+ if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf && range > 0)
{
if (startpos > 0)
return -1;
@@ -963,159 +2944,420 @@ re_search_2 (pbufp, string1, size1, string2, size2, startpos, range, regs, mstop
range = 1;
}
- while (1)
- {
- /* If a fastmap is supplied, skip quickly over characters
- that cannot possibly be the start of a match.
- Note, however, that if the pattern can possibly match
- the null string, we must test it at each starting point
- so that we take the first null string we get. */
-
- if (fastmap && startpos < total && pbufp->can_be_null != 1)
+ /* Update the fastmap now if not correct already. */
+ if (fastmap && !bufp->fastmap_accurate)
+ if (re_compile_fastmap (bufp) == -2)
+ return -2;
+
+ /* Loop through the string, looking for a place to start matching. */
+ for (;;)
+ {
+ /* If a fastmap is supplied, skip quickly over characters that
+ cannot be the start of a match. If the pattern can match the
+ null string, however, we don't need to skip characters; we want
+ the first null string. */
+ if (fastmap && startpos < total_size && !bufp->can_be_null)
{
- if (range > 0)
+ if (range > 0) /* Searching forwards. */
{
+ register const char *d;
register int lim = 0;
- register unsigned char *p;
int irange = range;
- if (startpos < size1 && startpos + range >= size1)
- lim = range - (size1 - startpos);
- p = ((unsigned char *)
- &(startpos >= size1 ? string2 - size1 : string1)[startpos]);
+ if (startpos < size1 && startpos + range >= size1)
+ lim = range - (size1 - startpos);
+ d = (startpos >= size1 ? string2 - size1 : string1) + startpos;
+
+ /* Written out as an if-else to avoid testing `translate'
+ inside the loop. */
if (translate)
- {
- while (range > lim && !fastmap[translate[*p++]])
- range--;
- }
+ while (range > lim
+ && !fastmap[(unsigned char)
+ translate[(unsigned char) *d++]])
+ range--;
else
- {
- while (range > lim && !fastmap[*p++])
- range--;
- }
+ while (range > lim && !fastmap[(unsigned char) *d++])
+ range--;
+
startpos += irange - range;
}
- else
+ else /* Searching backwards. */
{
- register unsigned char c;
- if (startpos >= size1)
- c = string2[startpos - size1];
- else
- c = string1[startpos];
- c &= 0xff;
- if (translate ? !fastmap[translate[c]] : !fastmap[c])
+ register char c = (size1 == 0 || startpos >= size1
+ ? string2[startpos - size1]
+ : string1[startpos]);
+
+ if (!fastmap[(unsigned char) TRANSLATE (c)])
goto advance;
}
}
- if (range >= 0 && startpos == total
- && fastmap && pbufp->can_be_null == 0)
+ /* If can't match the null string, and that's all we have left, fail. */
+ if (range >= 0 && startpos == total_size && fastmap
+ && !bufp->can_be_null)
return -1;
- val = re_match_2 (pbufp, string1, size1, string2, size2, startpos, regs, mstop);
- if (0 <= val)
- {
- if (val == -2)
- return -2;
- return startpos;
- }
-
-#ifdef C_ALLOCA
- alloca (0);
-#endif /* C_ALLOCA */
+ val = re_match_2 (bufp, string1, size1, string2, size2,
+ startpos, regs, stop);
+ if (val >= 0)
+ return startpos;
+
+ if (val == -2)
+ return -2;
advance:
- if (!range) break;
- if (range > 0) range--, startpos++; else range++, startpos--;
+ if (!range)
+ break;
+ else if (range > 0)
+ {
+ range--;
+ startpos++;
+ }
+ else
+ {
+ range++;
+ startpos--;
+ }
}
return -1;
-}
+} /* re_search_2 */
-#ifndef emacs /* emacs never uses this */
-int
-re_match (pbufp, string, size, pos, regs)
- struct re_pattern_buffer *pbufp;
- char *string;
- int size, pos;
- struct re_registers *regs;
+/* Declarations and macros for re_match_2. */
+
+static int bcmp_translate ();
+static boolean alt_match_null_string_p (),
+ common_op_match_null_string_p (),
+ group_match_null_string_p ();
+
+/* Structure for per-register (a.k.a. per-group) information.
+ This must not be longer than one word, because we push this value
+ onto the failure stack. Other register information, such as the
+ starting and ending positions (which are addresses), and the list of
+ inner groups (which is a bits list) are maintained in separate
+ variables.
+
+ We are making a (strictly speaking) nonportable assumption here: that
+ the compiler will pack our bit fields into something that fits into
+ the type of `word', i.e., is something that fits into one item on the
+ failure stack. */
+typedef union
{
- return re_match_2 (pbufp, 0, 0, string, size, pos, regs, size);
-}
-#endif /* emacs */
+ fail_stack_elt_t word;
+ struct
+ {
+ /* This field is one if this group can match the empty string,
+ zero if not. If not yet determined, `MATCH_NULL_UNSET_VALUE'. */
+#define MATCH_NULL_UNSET_VALUE 3
+ unsigned match_null_string_p : 2;
+ unsigned is_active : 1;
+ unsigned matched_something : 1;
+ unsigned ever_matched_something : 1;
+ } bits;
+} register_info_type;
+
+#define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p)
+#define IS_ACTIVE(R) ((R).bits.is_active)
+#define MATCHED_SOMETHING(R) ((R).bits.matched_something)
+#define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something)
+
+
+/* Call this when have matched a real character; it sets `matched' flags
+ for the subexpressions which we are currently inside. Also records
+ that those subexprs have matched. */
+#define SET_REGS_MATCHED() \
+ do \
+ { \
+ unsigned r; \
+ for (r = lowest_active_reg; r <= highest_active_reg; r++) \
+ { \
+ MATCHED_SOMETHING (reg_info[r]) \
+ = EVER_MATCHED_SOMETHING (reg_info[r]) \
+ = 1; \
+ } \
+ } \
+ while (0)
+
+
+/* This converts PTR, a pointer into one of the search strings `string1'
+ and `string2' into an offset from the beginning of that string. */
+#define POINTER_TO_OFFSET(ptr) \
+ (FIRST_STRING_P (ptr) ? (ptr) - string1 : (ptr) - string2 + size1)
+
+/* Registers are set to a sentinel when they haven't yet matched. */
+#define REG_UNSET_VALUE ((char *) -1)
+#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
+
+
+/* Macros for dealing with the split strings in re_match_2. */
+
+#define MATCHING_IN_FIRST_STRING (dend == end_match_1)
+
+/* Call before fetching a character with *d. This switches over to
+ string2 if necessary. */
+#define PREFETCH() \
+ while (d == dend) \
+ { \
+ /* End of string2 => fail. */ \
+ if (dend == end_match_2) \
+ goto fail; \
+ /* End of string1 => advance to string2. */ \
+ d = string2; \
+ dend = end_match_2; \
+ }
-/* Maximum size of failure stack. Beyond this, overflow is an error. */
-int re_max_failures = 2000;
+/* Test if at very beginning or at very end of the virtual concatenation
+ of `string1' and `string2'. If only one string, it's `string2'. */
+#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2)
+#define AT_STRINGS_END(d) ((d) == end2)
+
+
+/* Test if D points to a character which is word-constituent. We have
+ two special cases to check for: if past the end of string1, look at
+ the first character in string2; and if before the beginning of
+ string2, look at the last character in string1. */
+#define WORDCHAR_P(d) \
+ (SYNTAX ((d) == end1 ? *string2 \
+ : (d) == string2 - 1 ? *(end1 - 1) : *(d)) \
+ == Sword)
+
+/* Test if the character before D and the one at D differ with respect
+ to being word-constituent. */
+#define AT_WORD_BOUNDARY(d) \
+ (AT_STRINGS_BEG (d) || AT_STRINGS_END (d) \
+ || WORDCHAR_P (d - 1) != WORDCHAR_P (d))
+
+
+/* Free everything we malloc. */
+#ifdef REGEX_MALLOC
+#define FREE_VAR(var) if (var) free (var); var = NULL
+#define FREE_VARIABLES() \
+ do { \
+ FREE_VAR (fail_stack.stack); \
+ FREE_VAR (regstart); \
+ FREE_VAR (regend); \
+ FREE_VAR (old_regstart); \
+ FREE_VAR (old_regend); \
+ FREE_VAR (best_regstart); \
+ FREE_VAR (best_regend); \
+ FREE_VAR (reg_info); \
+ FREE_VAR (reg_dummy); \
+ FREE_VAR (reg_info_dummy); \
+ } while (0)
+#else /* not REGEX_MALLOC */
+/* Some MIPS systems (at least) want this to free alloca'd storage. */
+#define FREE_VARIABLES() alloca (0)
+#endif /* not REGEX_MALLOC */
+
+
+/* These values must meet several constraints. They must not be valid
+ register values; since we have a limit of 255 registers (because
+ we use only one byte in the pattern for the register number), we can
+ use numbers larger than 255. They must differ by 1, because of
+ NUM_FAILURE_ITEMS above. And the value for the lowest register must
+ be larger than the value for the highest register, so we do not try
+ to actually save any registers when none are active. */
+#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH)
+#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1)
+
+/* Matching routines. */
-static int bcmp_translate();
-/* Match the pattern described by PBUFP
- against data which is the virtual concatenation of STRING1 and STRING2.
- SIZE1 and SIZE2 are the sizes of the two data strings.
- Start the match at position POS.
- Do not consider matching past the position MSTOP.
+#ifndef emacs /* Emacs never uses this. */
+/* re_match is like re_match_2 except it takes only a single string. */
- If pbufp->fastmap is nonzero, then it had better be up to date.
+int
+re_match (bufp, string, size, pos, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ int size, pos;
+ struct re_registers *regs;
+ {
+ return re_match_2 (bufp, NULL, 0, string, size, pos, regs, size);
+}
+#endif /* not emacs */
- The reason that the data to match are specified as two components
- which are to be regarded as concatenated
- is so this function can be used directly on the contents of an Emacs buffer.
- -1 is returned if there is no match. -2 is returned if there is
- an error (such as match stack overflow). Otherwise the value is the length
- of the substring which was matched. */
+/* re_match_2 matches the compiled pattern in BUFP against the
+ the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
+ and SIZE2, respectively). We start matching at POS, and stop
+ matching at STOP.
+
+ If REGS is non-null and the `no_sub' field of BUFP is nonzero, we
+ store offsets for the substring each group matched in REGS. See the
+ documentation for exactly how many groups we fill.
+
+ We return -1 if no match, -2 if an internal error (such as the
+ failure stack overflowing). Otherwise, we return the length of the
+ matched substring. */
int
-re_match_2 (pbufp, string1, size1, string2, size2, pos, regs, mstop)
- struct re_pattern_buffer *pbufp;
- unsigned char *string1, *string2;
+re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
int size1, size2;
int pos;
struct re_registers *regs;
- int mstop;
+ int stop;
{
- register unsigned char *p = (unsigned char *) pbufp->buffer;
- register unsigned char *pend = p + pbufp->used;
- /* End of first string */
- unsigned char *end1;
- /* End of second string */
- unsigned char *end2;
- /* Pointer just past last char to consider matching */
- unsigned char *end_match_1, *end_match_2;
- register unsigned char *d, *dend;
- register int mcnt;
- unsigned char *translate = (unsigned char *) pbufp->translate;
-
- /* Failure point stack. Each place that can handle a failure further down the line
- pushes a failure point on this stack. It consists of two char *'s.
- The first one pushed is where to resume scanning the pattern;
- the second pushed is where to resume scanning the strings.
- If the latter is zero, the failure point is a "dummy".
- If a failure happens and the innermost failure point is dormant,
- it discards that failure point and tries the next one. */
-
- unsigned char *initial_stack[2 * NFAILURES];
- unsigned char **stackb = initial_stack;
- unsigned char **stackp = stackb, **stacke = &stackb[2 * NFAILURES];
-
- /* Information on the "contents" of registers.
- These are pointers into the input strings; they record
- just what was matched (on this attempt) by some part of the pattern.
- The start_memory command stores the start of a register's contents
- and the stop_memory command stores the end.
-
- At that point, regstart[regnum] points to the first character in the register,
- regend[regnum] points to the first character beyond the end of the register,
- regstart_seg1[regnum] is true iff regstart[regnum] points into string1,
- and regend_seg1[regnum] is true iff regend[regnum] points into string1. */
-
- unsigned char *regstart[RE_NREGS];
- unsigned char *regend[RE_NREGS];
- unsigned char regstart_seg1[RE_NREGS], regend_seg1[RE_NREGS];
-
- /* Set up pointers to ends of strings.
- Don't allow the second string to be empty unless both are empty. */
- if (!size2)
+ /* General temporaries. */
+ int mcnt;
+ unsigned char *p1;
+
+ /* Just past the end of the corresponding string. */
+ const char *end1, *end2;
+
+ /* Pointers into string1 and string2, just past the last characters in
+ each to consider matching. */
+ const char *end_match_1, *end_match_2;
+
+ /* Where we are in the data, and the end of the current string. */
+ const char *d, *dend;
+
+ /* Where we are in the pattern, and the end of the pattern. */
+ unsigned char *p = bufp->buffer;
+ register unsigned char *pend = p + bufp->used;
+
+ /* We use this to map every character in the string. */
+ char *translate = bufp->translate;
+
+ /* Failure point stack. Each place that can handle a failure further
+ down the line pushes a failure point on this stack. It consists of
+ restart, regend, and reg_info for all registers corresponding to
+ the subexpressions we're currently inside, plus the number of such
+ registers, and, finally, two char *'s. The first char * is where
+ to resume scanning the pattern; the second one is where to resume
+ scanning the strings. If the latter is zero, the failure point is
+ a ``dummy''; if a failure happens and the failure point is a dummy,
+ it gets discarded and the next next one is tried. */
+ fail_stack_type fail_stack;
+#ifdef DEBUG
+ static unsigned failure_id = 0;
+ unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0;
+#endif
+
+ /* We fill all the registers internally, independent of what we
+ return, for use in backreferences. The number here includes
+ an element for register zero. */
+ unsigned num_regs = bufp->re_nsub + 1;
+
+ /* The currently active registers. */
+ unsigned lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+ unsigned highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+
+ /* Information on the contents of registers. These are pointers into
+ the input strings; they record just what was matched (on this
+ attempt) by a subexpression part of the pattern, that is, the
+ regnum-th regstart pointer points to where in the pattern we began
+ matching and the regnum-th regend points to right after where we
+ stopped matching the regnum-th subexpression. (The zeroth register
+ keeps track of what the whole pattern matches.) */
+ const char **regstart, **regend;
+
+ /* If a group that's operated upon by a repetition operator fails to
+ match anything, then the register for its start will need to be
+ restored because it will have been set to wherever in the string we
+ are when we last see its open-group operator. Similarly for a
+ register's end. */
+ const char **old_regstart, **old_regend;
+
+ /* The is_active field of reg_info helps us keep track of which (possibly
+ nested) subexpressions we are currently in. The matched_something
+ field of reg_info[reg_num] helps us tell whether or not we have
+ matched any of the pattern so far this time through the reg_num-th
+ subexpression. These two fields get reset each time through any
+ loop their register is in. */
+ register_info_type *reg_info;
+
+ /* The following record the register info as found in the above
+ variables when we find a match better than any we've seen before.
+ This happens as we backtrack through the failure points, which in
+ turn happens only if we have not yet matched the entire string. */
+ unsigned best_regs_set = false;
+ const char **best_regstart, **best_regend;
+
+ /* Logically, this is `best_regend[0]'. But we don't want to have to
+ allocate space for that if we're not allocating space for anything
+ else (see below). Also, we never need info about register 0 for
+ any of the other register vectors, and it seems rather a kludge to
+ treat `best_regend' differently than the rest. So we keep track of
+ the end of the best match so far in a separate variable. We
+ initialize this to NULL so that when we backtrack the first time
+ and need to test it, it's not garbage. */
+ const char *match_end = NULL;
+
+ /* Used when we pop values we don't care about. */
+ const char **reg_dummy;
+ register_info_type *reg_info_dummy;
+
+#ifdef DEBUG
+ /* Counts the total number of registers pushed. */
+ unsigned num_regs_pushed = 0;
+#endif
+
+ DEBUG_PRINT1 ("\n\nEntering re_match_2.\n");
+
+ INIT_FAIL_STACK ();
+
+ /* Do not bother to initialize all the register variables if there are
+ no groups in the pattern, as it takes a fair amount of time. If
+ there are groups, we include space for register 0 (the whole
+ pattern), even though we never use it, since it simplifies the
+ array indexing. We should fix this. */
+ if (bufp->re_nsub)
+ {
+ regstart = REGEX_TALLOC (num_regs, const char *);
+ regend = REGEX_TALLOC (num_regs, const char *);
+ old_regstart = REGEX_TALLOC (num_regs, const char *);
+ old_regend = REGEX_TALLOC (num_regs, const char *);
+ best_regstart = REGEX_TALLOC (num_regs, const char *);
+ best_regend = REGEX_TALLOC (num_regs, const char *);
+ reg_info = REGEX_TALLOC (num_regs, register_info_type);
+ reg_dummy = REGEX_TALLOC (num_regs, const char *);
+ reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type);
+
+ if (!(regstart && regend && old_regstart && old_regend && reg_info
+ && best_regstart && best_regend && reg_dummy && reg_info_dummy))
+ {
+ FREE_VARIABLES ();
+ return -2;
+ }
+ }
+#ifdef REGEX_MALLOC
+ else
+ {
+ /* We must initialize all our variables to NULL, so that
+ `FREE_VARIABLES' doesn't try to free them. */
+ regstart = regend = old_regstart = old_regend = best_regstart
+ = best_regend = reg_dummy = NULL;
+ reg_info = reg_info_dummy = (register_info_type *) NULL;
+ }
+#endif /* REGEX_MALLOC */
+
+ /* The starting position is bogus. */
+ if (pos < 0 || pos > size1 + size2)
+ {
+ FREE_VARIABLES ();
+ return -1;
+ }
+
+ /* Initialize subexpression text positions to -1 to mark ones that no
+ start_memory/stop_memory has been seen for. Also initialize the
+ register information struct. */
+ for (mcnt = 1; mcnt < num_regs; mcnt++)
+ {
+ regstart[mcnt] = regend[mcnt]
+ = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE;
+
+ REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE;
+ IS_ACTIVE (reg_info[mcnt]) = 0;
+ MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+ EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+ }
+
+ /* We move `string1' into `string2' if the latter's empty -- but not if
+ `string1' is null. */
+ if (size2 == 0 && string1 != NULL)
{
string2 = string1;
size2 = size1;
@@ -1125,482 +3367,1329 @@ re_match_2 (pbufp, string1, size1, string2, size2, pos, regs, mstop)
end1 = string1 + size1;
end2 = string2 + size2;
- /* Compute where to stop matching, within the two strings */
- if (mstop <= size1)
+ /* Compute where to stop matching, within the two strings. */
+ if (stop <= size1)
{
- end_match_1 = string1 + mstop;
+ end_match_1 = string1 + stop;
end_match_2 = string2;
}
else
{
end_match_1 = end1;
- end_match_2 = string2 + mstop - size1;
+ end_match_2 = string2 + stop - size1;
}
- /* Initialize \) text positions to -1
- to mark ones that no \( or \) has been seen for. */
-
- for (mcnt = 0; mcnt < sizeof (regend) / sizeof (*regend); mcnt++)
- regend[mcnt] = (unsigned char *) -1;
-
- /* `p' scans through the pattern as `d' scans through the data.
- `dend' is the end of the input string that `d' points within.
- `d' is advanced into the following input string whenever necessary,
- but this happens before fetching;
- therefore, at the beginning of the loop,
- `d' can be pointing at the end of a string,
- but it cannot equal string2. */
-
- if (pos <= size1)
- d = string1 + pos, dend = end_match_1;
+ /* `p' scans through the pattern as `d' scans through the data.
+ `dend' is the end of the input string that `d' points within. `d'
+ is advanced into the following input string whenever necessary, but
+ this happens before fetching; therefore, at the beginning of the
+ loop, `d' can be pointing at the end of a string, but it cannot
+ equal `string2'. */
+ if (size1 > 0 && pos <= size1)
+ {
+ d = string1 + pos;
+ dend = end_match_1;
+ }
else
- d = string2 + pos - size1, dend = end_match_2;
-
-/* Write PREFETCH; just before fetching a character with *d. */
-#define PREFETCH \
- while (d == dend) \
- { if (dend == end_match_2) goto fail; /* end of string2 => failure */ \
- d = string2; /* end of string1 => advance to string2. */ \
- dend = end_match_2; }
-
- /* This loop loops over pattern commands.
- It exits by returning from the function if match is complete,
- or it drops through if match fails at this starting point in the input data. */
+ {
+ d = string2 + pos - size1;
+ dend = end_match_2;
+ }
- while (1)
+ DEBUG_PRINT1 ("The compiled pattern is: ");
+ DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend);
+ DEBUG_PRINT1 ("The string to match is: `");
+ DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2);
+ DEBUG_PRINT1 ("'\n");
+
+ /* This loops over pattern commands. It exits by returning from the
+ function if the match is complete, or it drops through if the match
+ fails at this starting point in the input data. */
+ for (;;)
{
+ DEBUG_PRINT2 ("\n0x%x: ", p);
+
if (p == pend)
- /* End of pattern means we have succeeded! */
- {
- /* If caller wants register contents data back, convert it to indices */
- if (regs)
+ { /* End of pattern means we might have succeeded. */
+ DEBUG_PRINT1 ("end of pattern ... ");
+
+ /* If we haven't matched the entire string, and we want the
+ longest match, try backtracking. */
+ if (d != end_match_2)
{
- regs->start[0] = pos;
- if (dend == end_match_1)
- regs->end[0] = d - string1;
- else
- regs->end[0] = d - string2 + size1;
- for (mcnt = 1; mcnt < RE_NREGS; mcnt++)
- {
- if (regend[mcnt] == (unsigned char *) -1)
+ DEBUG_PRINT1 ("backtracking.\n");
+
+ if (!FAIL_STACK_EMPTY ())
+ { /* More failure points to try. */
+ boolean same_str_p = (FIRST_STRING_P (match_end)
+ == MATCHING_IN_FIRST_STRING);
+
+ /* If exceeds best match so far, save it. */
+ if (!best_regs_set
+ || (same_str_p && d > match_end)
+ || (!same_str_p && !MATCHING_IN_FIRST_STRING))
+ {
+ best_regs_set = true;
+ match_end = d;
+
+ DEBUG_PRINT1 ("\nSAVING match as best so far.\n");
+
+ for (mcnt = 1; mcnt < num_regs; mcnt++)
+ {
+ best_regstart[mcnt] = regstart[mcnt];
+ best_regend[mcnt] = regend[mcnt];
+ }
+ }
+ goto fail;
+ }
+
+ /* If no failure points, don't restore garbage. */
+ else if (best_regs_set)
+ {
+ restore_best_regs:
+ /* Restore best match. It may happen that `dend ==
+ end_match_1' while the restored d is in string2.
+ For example, the pattern `x.*y.*z' against the
+ strings `x-' and `y-z-', if the two strings are
+ not consecutive in memory. */
+ DEBUG_PRINT1 ("Restoring best registers.\n");
+
+ d = match_end;
+ dend = ((d >= string1 && d <= end1)
+ ? end_match_1 : end_match_2);
+
+ for (mcnt = 1; mcnt < num_regs; mcnt++)
{
- regs->start[mcnt] = -1;
- regs->end[mcnt] = -1;
- continue;
+ regstart[mcnt] = best_regstart[mcnt];
+ regend[mcnt] = best_regend[mcnt];
}
- if (regstart_seg1[mcnt])
- regs->start[mcnt] = regstart[mcnt] - string1;
- else
- regs->start[mcnt] = regstart[mcnt] - string2 + size1;
- if (regend_seg1[mcnt])
- regs->end[mcnt] = regend[mcnt] - string1;
- else
- regs->end[mcnt] = regend[mcnt] - string2 + size1;
+ }
+ } /* d != end_match_2 */
+
+ DEBUG_PRINT1 ("Accepting match.\n");
+
+ /* If caller wants register contents data back, do it. */
+ if (regs && !bufp->no_sub)
+ {
+ /* Have the register data arrays been allocated? */
+ if (bufp->regs_allocated == REGS_UNALLOCATED)
+ { /* No. So allocate them with malloc. We need one
+ extra element beyond `num_regs' for the `-1' marker
+ GNU code uses. */
+ regs->num_regs = MAX (RE_NREGS, num_regs + 1);
+ regs->start = TALLOC (regs->num_regs, regoff_t);
+ regs->end = TALLOC (regs->num_regs, regoff_t);
+ if (regs->start == NULL || regs->end == NULL)
+ return -2;
+ bufp->regs_allocated = REGS_REALLOCATE;
+ }
+ else if (bufp->regs_allocated == REGS_REALLOCATE)
+ { /* Yes. If we need more elements than were already
+ allocated, reallocate them. If we need fewer, just
+ leave it alone. */
+ if (regs->num_regs < num_regs + 1)
+ {
+ regs->num_regs = num_regs + 1;
+ RETALLOC (regs->start, regs->num_regs, regoff_t);
+ RETALLOC (regs->end, regs->num_regs, regoff_t);
+ if (regs->start == NULL || regs->end == NULL)
+ return -2;
+ }
+ }
+ else
+ {
+ /* These braces fend off a "empty body in an else-statement"
+ warning under GCC when assert expands to nothing. */
+ assert (bufp->regs_allocated == REGS_FIXED);
}
- }
- if (dend == end_match_1)
- return (d - string1 - pos);
- else
- return d - string2 + size1 - pos;
- }
- /* Otherwise match next pattern command */
+ /* Convert the pointer data in `regstart' and `regend' to
+ indices. Register zero has to be set differently,
+ since we haven't kept track of any info for it. */
+ if (regs->num_regs > 0)
+ {
+ regs->start[0] = pos;
+ regs->end[0] = (MATCHING_IN_FIRST_STRING ? d - string1
+ : d - string2 + size1);
+ }
+
+ /* Go through the first `min (num_regs, regs->num_regs)'
+ registers, since that is all we initialized. */
+ for (mcnt = 1; mcnt < MIN (num_regs, regs->num_regs); mcnt++)
+ {
+ if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt]))
+ regs->start[mcnt] = regs->end[mcnt] = -1;
+ else
+ {
+ regs->start[mcnt] = POINTER_TO_OFFSET (regstart[mcnt]);
+ regs->end[mcnt] = POINTER_TO_OFFSET (regend[mcnt]);
+ }
+ }
+
+ /* If the regs structure we return has more elements than
+ were in the pattern, set the extra elements to -1. If
+ we (re)allocated the registers, this is the case,
+ because we always allocate enough to have at least one
+ -1 at the end. */
+ for (mcnt = num_regs; mcnt < regs->num_regs; mcnt++)
+ regs->start[mcnt] = regs->end[mcnt] = -1;
+ } /* regs && !bufp->no_sub */
+
+ FREE_VARIABLES ();
+ DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n",
+ nfailure_points_pushed, nfailure_points_popped,
+ nfailure_points_pushed - nfailure_points_popped);
+ DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed);
+
+ mcnt = d - pos - (MATCHING_IN_FIRST_STRING
+ ? string1
+ : string2 - size1);
+
+ DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt);
+
+ return mcnt;
+ }
+
+ /* Otherwise match next pattern command. */
#ifdef SWITCH_ENUM_BUG
- switch ((int) ((enum regexpcode) *p++))
+ switch ((int) ((re_opcode_t) *p++))
#else
- switch ((enum regexpcode) *p++)
+ switch ((re_opcode_t) *p++)
#endif
{
+ /* Ignore these. Used to ignore the n of succeed_n's which
+ currently have n == 0. */
+ case no_op:
+ DEBUG_PRINT1 ("EXECUTING no_op.\n");
+ break;
- /* \( is represented by a start_memory, \) by a stop_memory.
- Both of those commands contain a "register number" argument.
- The text matched within the \( and \) is recorded under that number.
- Then, \<digit> turns into a `duplicate' command which
- is followed by the numeric value of <digit> as the register number. */
- case start_memory:
- regstart[*p] = d;
- regstart_seg1[*p++] = (dend == end_match_1);
- break;
-
- case stop_memory:
- regend[*p] = d;
- regend_seg1[*p++] = (dend == end_match_1);
- break;
+ /* Match the next n pattern characters exactly. The following
+ byte in the pattern defines n, and the n bytes after that
+ are the characters to match. */
+ case exactn:
+ mcnt = *p++;
+ DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt);
- case duplicate:
- {
- int regno = *p++; /* Get which register to match against */
- register unsigned char *d2, *dend2;
+ /* This is written out as an if-else so we don't waste time
+ testing `translate' inside the loop. */
+ if (translate)
+ {
+ do
+ {
+ PREFETCH ();
+ if (translate[(unsigned char) *d++] != (char) *p++)
+ goto fail;
+ }
+ while (--mcnt);
+ }
+ else
+ {
+ do
+ {
+ PREFETCH ();
+ if (*d++ != (char) *p++) goto fail;
+ }
+ while (--mcnt);
+ }
+ SET_REGS_MATCHED ();
+ break;
- d2 = regstart[regno];
- dend2 = ((regstart_seg1[regno] == regend_seg1[regno])
- ? regend[regno] : end_match_1);
- while (1)
- {
- /* Advance to next segment in register contents, if necessary */
- while (d2 == dend2)
- {
- if (dend2 == end_match_2) break;
- if (dend2 == regend[regno]) break;
- d2 = string2, dend2 = regend[regno]; /* end of string1 => advance to string2. */
- }
- /* At end of register contents => success */
- if (d2 == dend2) break;
- /* Advance to next segment in data being matched, if necessary */
- PREFETCH;
+ /* Match any character except possibly a newline or a null. */
+ case anychar:
+ DEBUG_PRINT1 ("EXECUTING anychar.\n");
- /* mcnt gets # consecutive chars to compare */
- mcnt = dend - d;
- if (mcnt > dend2 - d2)
- mcnt = dend2 - d2;
- /* Compare that many; failure if mismatch, else skip them. */
- if (translate ? bcmp_translate (d, d2, mcnt, translate) : bcmp (d, d2, mcnt))
- goto fail;
- d += mcnt, d2 += mcnt;
- }
- }
- break;
+ PREFETCH ();
- case anychar:
- /* fetch a data character */
- PREFETCH;
- /* Match anything but a newline. */
- if ((translate ? translate[*d++] : *d++) == '\n')
+ if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n')
+ || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000'))
goto fail;
+
+ SET_REGS_MATCHED ();
+ DEBUG_PRINT2 (" Matched `%d'.\n", *d);
+ d++;
break;
+
case charset:
case charset_not:
{
- /* Nonzero for charset_not */
- int not = 0;
- register int c;
- if (*(p - 1) == (unsigned char) charset_not)
- not = 1;
+ register unsigned char c;
+ boolean not = (re_opcode_t) *(p - 1) == charset_not;
- /* fetch a data character */
- PREFETCH;
+ DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : "");
- if (translate)
- c = translate [*d];
- else
- c = *d;
+ PREFETCH ();
+ c = TRANSLATE (*d); /* The character to match. */
- if (c < *p * BYTEWIDTH
+ /* Cast to `unsigned' instead of `unsigned char' in case the
+ bit list is a full 32 bytes long. */
+ if (c < (unsigned) (*p * BYTEWIDTH)
&& p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
not = !not;
p += 1 + *p;
if (!not) goto fail;
- d++;
+
+ SET_REGS_MATCHED ();
+ d++;
break;
}
- case begline:
- if (d == string1 || d[-1] == '\n')
- break;
- goto fail;
-
- case endline:
- if (d == end2
- || (d == end1 ? (size2 == 0 || *string2 == '\n') : *d == '\n'))
- break;
- goto fail;
- /* "or" constructs ("|") are handled by starting each alternative
- with an on_failure_jump that points to the start of the next alternative.
- Each alternative except the last ends with a jump to the joining point.
- (Actually, each jump except for the last one really jumps
- to the following jump, because tensioning the jumps is a hassle.) */
+ /* The beginning of a group is represented by start_memory.
+ The arguments are the register number in the next byte, and the
+ number of groups inner to this one in the next. The text
+ matched within the group is recorded (in the internal
+ registers data structure) under the register number. */
+ case start_memory:
+ DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]);
+
+ /* Find out if this group can match the empty string. */
+ p1 = p; /* To send to group_match_null_string_p. */
+
+ if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE)
+ REG_MATCH_NULL_STRING_P (reg_info[*p])
+ = group_match_null_string_p (&p1, pend, reg_info);
+
+ /* Save the position in the string where we were the last time
+ we were at this open-group operator in case the group is
+ operated upon by a repetition operator, e.g., with `(a*)*b'
+ against `ab'; then we want to ignore where we are now in
+ the string in case this attempt to match fails. */
+ old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+ ? REG_UNSET (regstart[*p]) ? d : regstart[*p]
+ : regstart[*p];
+ DEBUG_PRINT2 (" old_regstart: %d\n",
+ POINTER_TO_OFFSET (old_regstart[*p]));
+
+ regstart[*p] = d;
+ DEBUG_PRINT2 (" regstart: %d\n", POINTER_TO_OFFSET (regstart[*p]));
+
+ IS_ACTIVE (reg_info[*p]) = 1;
+ MATCHED_SOMETHING (reg_info[*p]) = 0;
+
+ /* This is the new highest active register. */
+ highest_active_reg = *p;
+
+ /* If nothing was active before, this is the new lowest active
+ register. */
+ if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+ lowest_active_reg = *p;
+
+ /* Move past the register number and inner group count. */
+ p += 2;
+ break;
+
+
+ /* The stop_memory opcode represents the end of a group. Its
+ arguments are the same as start_memory's: the register
+ number, and the number of inner groups. */
+ case stop_memory:
+ DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]);
+
+ /* We need to save the string position the last time we were at
+ this close-group operator in case the group is operated
+ upon by a repetition operator, e.g., with `((a*)*(b*)*)*'
+ against `aba'; then we want to ignore where we are now in
+ the string in case this attempt to match fails. */
+ old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+ ? REG_UNSET (regend[*p]) ? d : regend[*p]
+ : regend[*p];
+ DEBUG_PRINT2 (" old_regend: %d\n",
+ POINTER_TO_OFFSET (old_regend[*p]));
+
+ regend[*p] = d;
+ DEBUG_PRINT2 (" regend: %d\n", POINTER_TO_OFFSET (regend[*p]));
+
+ /* This register isn't active anymore. */
+ IS_ACTIVE (reg_info[*p]) = 0;
+
+ /* If this was the only register active, nothing is active
+ anymore. */
+ if (lowest_active_reg == highest_active_reg)
+ {
+ lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+ highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+ }
+ else
+ { /* We must scan for the new highest active register, since
+ it isn't necessarily one less than now: consider
+ (a(b)c(d(e)f)g). When group 3 ends, after the f), the
+ new highest active register is 1. */
+ unsigned char r = *p - 1;
+ while (r > 0 && !IS_ACTIVE (reg_info[r]))
+ r--;
+
+ /* If we end up at register zero, that means that we saved
+ the registers as the result of an `on_failure_jump', not
+ a `start_memory', and we jumped to past the innermost
+ `stop_memory'. For example, in ((.)*) we save
+ registers 1 and 2 as a result of the *, but when we pop
+ back to the second ), we are at the stop_memory 1.
+ Thus, nothing is active. */
+ if (r == 0)
+ {
+ lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+ highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+ }
+ else
+ highest_active_reg = r;
+ }
+
+ /* If just failed to match something this time around with a
+ group that's operated on by a repetition operator, try to
+ force exit from the ``loop'', and restore the register
+ information for this group that we had before trying this
+ last match. */
+ if ((!MATCHED_SOMETHING (reg_info[*p])
+ || (re_opcode_t) p[-3] == start_memory)
+ && (p + 2) < pend)
+ {
+ boolean is_a_jump_n = false;
+
+ p1 = p + 2;
+ mcnt = 0;
+ switch ((re_opcode_t) *p1++)
+ {
+ case jump_n:
+ is_a_jump_n = true;
+ case pop_failure_jump:
+ case maybe_pop_jump:
+ case jump:
+ case dummy_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ if (is_a_jump_n)
+ p1 += 2;
+ break;
+
+ default:
+ /* do nothing */ ;
+ }
+ p1 += mcnt;
+
+ /* If the next operation is a jump backwards in the pattern
+ to an on_failure_jump right before the start_memory
+ corresponding to this stop_memory, exit from the loop
+ by forcing a failure after pushing on the stack the
+ on_failure_jump's jump in the pattern, and d. */
+ if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump
+ && (re_opcode_t) p1[3] == start_memory && p1[4] == *p)
+ {
+ /* If this group ever matched anything, then restore
+ what its registers were before trying this last
+ failed match, e.g., with `(a*)*b' against `ab' for
+ regstart[1], and, e.g., with `((a*)*(b*)*)*'
+ against `aba' for regend[3].
+
+ Also restore the registers for inner groups for,
+ e.g., `((a*)(b*))*' against `aba' (register 3 would
+ otherwise get trashed). */
+
+ if (EVER_MATCHED_SOMETHING (reg_info[*p]))
+ {
+ unsigned r;
+
+ EVER_MATCHED_SOMETHING (reg_info[*p]) = 0;
+
+ /* Restore this and inner groups' (if any) registers. */
+ for (r = *p; r < *p + *(p + 1); r++)
+ {
+ regstart[r] = old_regstart[r];
+
+ /* xx why this test? */
+ if ((int) old_regend[r] >= (int) regstart[r])
+ regend[r] = old_regend[r];
+ }
+ }
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ PUSH_FAILURE_POINT (p1 + mcnt, d, -2);
+
+ goto fail;
+ }
+ }
+
+ /* Move past the register number and the inner group count. */
+ p += 2;
+ break;
+
+
+ /* \<digit> has been turned into a `duplicate' command which is
+ followed by the numeric value of <digit> as the register number. */
+ case duplicate:
+ {
+ register const char *d2, *dend2;
+ int regno = *p++; /* Get which register to match against. */
+ DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno);
+
+ /* Can't back reference a group which we've never matched. */
+ if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno]))
+ goto fail;
+
+ /* Where in input to try to start matching. */
+ d2 = regstart[regno];
+
+ /* Where to stop matching; if both the place to start and
+ the place to stop matching are in the same string, then
+ set to the place to stop, otherwise, for now have to use
+ the end of the first string. */
+
+ dend2 = ((FIRST_STRING_P (regstart[regno])
+ == FIRST_STRING_P (regend[regno]))
+ ? regend[regno] : end_match_1);
+ for (;;)
+ {
+ /* If necessary, advance to next segment in register
+ contents. */
+ while (d2 == dend2)
+ {
+ if (dend2 == end_match_2) break;
+ if (dend2 == regend[regno]) break;
- /* The start of a stupid repeat has an on_failure_jump that points
- past the end of the repeat text.
- This makes a failure point so that, on failure to match a repetition,
- matching restarts past as many repetitions have been found
- with no way to fail and look for another one. */
+ /* End of string1 => advance to string2. */
+ d2 = string2;
+ dend2 = regend[regno];
+ }
+ /* At end of register contents => success */
+ if (d2 == dend2) break;
- /* A smart repeat is similar but loops back to the on_failure_jump
- so that each repetition makes another failure point. */
+ /* If necessary, advance to next segment in data. */
+ PREFETCH ();
- case on_failure_jump:
- if (stackp == stacke)
- {
- unsigned char **stackx;
- if (stacke - stackb > re_max_failures * 2)
- return -2;
- stackx = (unsigned char **) alloca (2 * (stacke - stackb)
- * sizeof (char *));
- bcopy (stackb, stackx, (stacke - stackb) * sizeof (char *));
- stackp = stackx + (stackp - stackb);
- stacke = stackx + 2 * (stacke - stackb);
- stackb = stackx;
- }
- mcnt = *p++ & 0377;
- mcnt += SIGN_EXTEND_CHAR (*(char *)p) << 8;
- p++;
- *stackp++ = mcnt + p;
- *stackp++ = d;
+ /* How many characters left in this segment to match. */
+ mcnt = dend - d;
+
+ /* Want how many consecutive characters we can match in
+ one shot, so, if necessary, adjust the count. */
+ if (mcnt > dend2 - d2)
+ mcnt = dend2 - d2;
+
+ /* Compare that many; failure if mismatch, else move
+ past them. */
+ if (translate
+ ? bcmp_translate (d, d2, mcnt, translate)
+ : bcmp (d, d2, mcnt))
+ goto fail;
+ d += mcnt, d2 += mcnt;
+ }
+ }
break;
- /* The end of a smart repeat has an maybe_finalize_jump back.
- Change it either to a finalize_jump or an ordinary jump. */
- case maybe_finalize_jump:
- mcnt = *p++ & 0377;
- mcnt += SIGN_EXTEND_CHAR (*(char *)p) << 8;
- p++;
- {
+ /* begline matches the empty string at the beginning of the string
+ (unless `not_bol' is set in `bufp'), and, if
+ `newline_anchor' is set, after newlines. */
+ case begline:
+ DEBUG_PRINT1 ("EXECUTING begline.\n");
+
+ if (AT_STRINGS_BEG (d))
+ {
+ if (!bufp->not_bol) break;
+ }
+ else if (d[-1] == '\n' && bufp->newline_anchor)
+ {
+ break;
+ }
+ /* In all other cases, we fail. */
+ goto fail;
+
+
+ /* endline is the dual of begline. */
+ case endline:
+ DEBUG_PRINT1 ("EXECUTING endline.\n");
+
+ if (AT_STRINGS_END (d))
+ {
+ if (!bufp->not_eol) break;
+ }
+
+ /* We have to ``prefetch'' the next character. */
+ else if ((d == end1 ? *string2 : *d) == '\n'
+ && bufp->newline_anchor)
+ {
+ break;
+ }
+ goto fail;
+
+
+ /* Match at the very beginning of the data. */
+ case begbuf:
+ DEBUG_PRINT1 ("EXECUTING begbuf.\n");
+ if (AT_STRINGS_BEG (d))
+ break;
+ goto fail;
+
+
+ /* Match at the very end of the data. */
+ case endbuf:
+ DEBUG_PRINT1 ("EXECUTING endbuf.\n");
+ if (AT_STRINGS_END (d))
+ break;
+ goto fail;
+
+
+ /* on_failure_keep_string_jump is used to optimize `.*\n'. It
+ pushes NULL as the value for the string on the stack. Then
+ `pop_failure_point' will keep the current value for the
+ string, instead of restoring it. To see why, consider
+ matching `foo\nbar' against `.*\n'. The .* matches the foo;
+ then the . fails against the \n. But the next thing we want
+ to do is match the \n against the \n; if we restored the
+ string value, we would be back at the foo.
+
+ Because this is used only in specific cases, we don't need to
+ check all the things that `on_failure_jump' does, to make
+ sure the right things get saved on the stack. Hence we don't
+ share its code. The only reason to push anything on the
+ stack at all is that otherwise we would have to change
+ `anychar's code to do something besides goto fail in this
+ case; that seems worse than this. */
+ case on_failure_keep_string_jump:
+ DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump");
+
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt);
+
+ PUSH_FAILURE_POINT (p + mcnt, NULL, -2);
+ break;
+
+
+ /* Uses of on_failure_jump:
+
+ Each alternative starts with an on_failure_jump that points
+ to the beginning of the next alternative. Each alternative
+ except the last ends with a jump that in effect jumps past
+ the rest of the alternatives. (They really jump to the
+ ending jump of the following alternative, because tensioning
+ these jumps is a hassle.)
+
+ Repeats start with an on_failure_jump that points past both
+ the repetition text and either the following jump or
+ pop_failure_jump back to this on_failure_jump. */
+ case on_failure_jump:
+ on_failure:
+ DEBUG_PRINT1 ("EXECUTING on_failure_jump");
+
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt);
+
+ /* If this on_failure_jump comes right before a group (i.e.,
+ the original * applied to a group), save the information
+ for that group and all inner ones, so that if we fail back
+ to this point, the group's information will be correct.
+ For example, in \(a*\)*\1, we need the preceding group,
+ and in \(\(a*\)b*\)\2, we need the inner group. */
+
+ /* We can't use `p' to check ahead because we push
+ a failure point to `p + mcnt' after we do this. */
+ p1 = p;
+
+ /* We need to skip no_op's before we look for the
+ start_memory in case this on_failure_jump is happening as
+ the result of a completed succeed_n, as in \(a\)\{1,3\}b\1
+ against aba. */
+ while (p1 < pend && (re_opcode_t) *p1 == no_op)
+ p1++;
+
+ if (p1 < pend && (re_opcode_t) *p1 == start_memory)
+ {
+ /* We have a new highest active register now. This will
+ get reset at the start_memory we are about to get to,
+ but we will have saved all the registers relevant to
+ this repetition op, as described above. */
+ highest_active_reg = *(p1 + 1) + *(p1 + 2);
+ if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+ lowest_active_reg = *(p1 + 1);
+ }
+
+ DEBUG_PRINT1 (":\n");
+ PUSH_FAILURE_POINT (p + mcnt, d, -2);
+ break;
+
+
+ /* A smart repeat ends with `maybe_pop_jump'.
+ We change it to either `pop_failure_jump' or `jump'. */
+ case maybe_pop_jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt);
+ {
register unsigned char *p2 = p;
- /* Compare what follows with the begining of the repeat.
- If we can establish that there is nothing that they would
- both match, we can change to finalize_jump */
- while (p2 != pend
- && (*p2 == (unsigned char) stop_memory
- || *p2 == (unsigned char) start_memory))
- p2++;
- if (p2 == pend)
- p[-3] = (unsigned char) finalize_jump;
- else if (*p2 == (unsigned char) exactn
- || *p2 == (unsigned char) endline)
+
+ /* Compare the beginning of the repeat with what in the
+ pattern follows its end. If we can establish that there
+ is nothing that they would both match, i.e., that we
+ would have to backtrack because of (as in, e.g., `a*a')
+ then we can change to pop_failure_jump, because we'll
+ never have to backtrack.
+
+ This is not true in the case of alternatives: in
+ `(a|ab)*' we do need to backtrack to the `ab' alternative
+ (e.g., if the string was `ab'). But instead of trying to
+ detect that here, the alternative has put on a dummy
+ failure point which is what we will end up popping. */
+
+ /* Skip over open/close-group commands. */
+ while (p2 + 2 < pend
+ && ((re_opcode_t) *p2 == stop_memory
+ || (re_opcode_t) *p2 == start_memory))
+ p2 += 3; /* Skip over args, too. */
+
+ /* If we're at the end of the pattern, we can change. */
+ if (p2 == pend)
+ {
+ /* Consider what happens when matching ":\(.*\)"
+ against ":/". I don't really understand this code
+ yet. */
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT1
+ (" End of pattern: change to `pop_failure_jump'.\n");
+ }
+
+ else if ((re_opcode_t) *p2 == exactn
+ || (bufp->newline_anchor && (re_opcode_t) *p2 == endline))
{
- register int c = *p2 == (unsigned char) endline ? '\n' : p2[2];
- register unsigned char *p1 = p + mcnt;
- /* p1[0] ... p1[2] are an on_failure_jump.
- Examine what follows that */
- if (p1[3] == (unsigned char) exactn && p1[5] != c)
- p[-3] = (unsigned char) finalize_jump;
- else if (p1[3] == (unsigned char) charset
- || p1[3] == (unsigned char) charset_not)
+ register unsigned char c
+ = *p2 == (unsigned char) endline ? '\n' : p2[2];
+ p1 = p + mcnt;
+
+ /* p1[0] ... p1[2] are the `on_failure_jump' corresponding
+ to the `maybe_finalize_jump' of this case. Examine what
+ follows. */
+ if ((re_opcode_t) p1[3] == exactn && p1[5] != c)
+ {
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n",
+ c, p1[5]);
+ }
+
+ else if ((re_opcode_t) p1[3] == charset
+ || (re_opcode_t) p1[3] == charset_not)
{
- int not = p1[3] == (unsigned char) charset_not;
- if (c < p1[4] * BYTEWIDTH
+ int not = (re_opcode_t) p1[3] == charset_not;
+
+ if (c < (unsigned char) (p1[4] * BYTEWIDTH)
&& p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
not = !not;
- /* not is 1 if c would match */
- /* That means it is not safe to finalize */
+
+ /* `not' is equal to 1 if c would match, which means
+ that we can't change to pop_failure_jump. */
if (!not)
- p[-3] = (unsigned char) finalize_jump;
+ {
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT1 (" No match => pop_failure_jump.\n");
+ }
}
}
}
- p -= 2;
- if (p[-1] != (unsigned char) finalize_jump)
+ p -= 2; /* Point at relative address again. */
+ if ((re_opcode_t) p[-1] != pop_failure_jump)
{
p[-1] = (unsigned char) jump;
- goto nofinalize;
+ DEBUG_PRINT1 (" Match => jump.\n");
+ goto unconditional_jump;
}
-
- /* The end of a stupid repeat has a finalize-jump
- back to the start, where another failure point will be made
- which will point after all the repetitions found so far. */
-
- case finalize_jump:
- stackp -= 2;
-
- case jump:
- nofinalize:
- mcnt = *p++ & 0377;
- mcnt += SIGN_EXTEND_CHAR (*(char *)p) << 8;
- p += mcnt + 1; /* The 1 compensates for missing ++ above */
+ /* Note fall through. */
+
+
+ /* The end of a simple repeat has a pop_failure_jump back to
+ its matching on_failure_jump, where the latter will push a
+ failure point. The pop_failure_jump takes off failure
+ points put on by this pop_failure_jump's matching
+ on_failure_jump; we got through the pattern to here from the
+ matching on_failure_jump, so didn't fail. */
+ case pop_failure_jump:
+ {
+ /* We need to pass separate storage for the lowest and
+ highest registers, even though we don't care about the
+ actual values. Otherwise, we will restore only one
+ register from the stack, since lowest will == highest in
+ `pop_failure_point'. */
+ unsigned dummy_low_reg, dummy_high_reg;
+ unsigned char *pdummy;
+ const char *sdummy;
+
+ DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n");
+ POP_FAILURE_POINT (sdummy, pdummy,
+ dummy_low_reg, dummy_high_reg,
+ reg_dummy, reg_dummy, reg_info_dummy);
+ }
+ /* Note fall through. */
+
+
+ /* Unconditionally jump (without popping any failure points). */
+ case jump:
+ unconditional_jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p); /* Get the amount to jump. */
+ DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt);
+ p += mcnt; /* Do the jump. */
+ DEBUG_PRINT2 ("(to 0x%x).\n", p);
break;
- case dummy_failure_jump:
- if (stackp == stacke)
- {
- unsigned char **stackx
- = (unsigned char **) alloca (2 * (stacke - stackb)
- * sizeof (char *));
- bcopy (stackb, stackx, (stacke - stackb) * sizeof (char *));
- stackp = stackx + (stackp - stackb);
- stacke = stackx + 2 * (stacke - stackb);
- stackb = stackx;
- }
- *stackp++ = 0;
- *stackp++ = 0;
- goto nofinalize;
-
- case wordbound:
- if (d == string1 /* Points to first char */
- || d == end2 /* Points to end */
- || (d == end1 && size2 == 0)) /* Points to end */
- break;
- if ((SYNTAX (d[-1]) == Sword)
- != (SYNTAX (d == end1 ? *string2 : *d) == Sword))
+
+ /* We need this opcode so we can detect where alternatives end
+ in `group_match_null_string_p' et al. */
+ case jump_past_alt:
+ DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n");
+ goto unconditional_jump;
+
+
+ /* Normally, the on_failure_jump pushes a failure point, which
+ then gets popped at pop_failure_jump. We will end up at
+ pop_failure_jump, also, and with a pattern of, say, `a+', we
+ are skipping over the on_failure_jump, so we have to push
+ something meaningless for pop_failure_jump to pop. */
+ case dummy_failure_jump:
+ DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n");
+ /* It doesn't matter what we push for the string here. What
+ the code at `fail' tests is the value for the pattern. */
+ PUSH_FAILURE_POINT (0, 0, -2);
+ goto unconditional_jump;
+
+
+ /* At the end of an alternative, we need to push a dummy failure
+ point in case we are followed by a `pop_failure_jump', because
+ we don't want the failure point for the alternative to be
+ popped. For example, matching `(a|ab)*' against `aab'
+ requires that we match the `ab' alternative. */
+ case push_dummy_failure:
+ DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n");
+ /* See comments just above at `dummy_failure_jump' about the
+ two zeroes. */
+ PUSH_FAILURE_POINT (0, 0, -2);
+ break;
+
+ /* Have to succeed matching what follows at least n times.
+ After that, handle like `on_failure_jump'. */
+ case succeed_n:
+ EXTRACT_NUMBER (mcnt, p + 2);
+ DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt);
+
+ assert (mcnt >= 0);
+ /* Originally, this is how many times we HAVE to succeed. */
+ if (mcnt > 0)
+ {
+ mcnt--;
+ p += 2;
+ STORE_NUMBER_AND_INCR (p, mcnt);
+ DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p, mcnt);
+ }
+ else if (mcnt == 0)
+ {
+ DEBUG_PRINT2 (" Setting two bytes from 0x%x to no_op.\n", p+2);
+ p[2] = (unsigned char) no_op;
+ p[3] = (unsigned char) no_op;
+ goto on_failure;
+ }
+ break;
+
+ case jump_n:
+ EXTRACT_NUMBER (mcnt, p + 2);
+ DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt);
+
+ /* Originally, this is how many times we CAN jump. */
+ if (mcnt)
+ {
+ mcnt--;
+ STORE_NUMBER (p + 2, mcnt);
+ goto unconditional_jump;
+ }
+ /* If don't have to jump any more, skip over the rest of command. */
+ else
+ p += 4;
+ break;
+
+ case set_number_at:
+ {
+ DEBUG_PRINT1 ("EXECUTING set_number_at.\n");
+
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ p1 = p + mcnt;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p1, mcnt);
+ STORE_NUMBER (p1, mcnt);
+ break;
+ }
+
+ case wordbound:
+ DEBUG_PRINT1 ("EXECUTING wordbound.\n");
+ if (AT_WORD_BOUNDARY (d))
break;
- goto fail;
+ goto fail;
case notwordbound:
- if (d == string1 /* Points to first char */
- || d == end2 /* Points to end */
- || (d == end1 && size2 == 0)) /* Points to end */
- goto fail;
- if ((SYNTAX (d[-1]) == Sword)
- != (SYNTAX (d == end1 ? *string2 : *d) == Sword))
+ DEBUG_PRINT1 ("EXECUTING notwordbound.\n");
+ if (AT_WORD_BOUNDARY (d))
goto fail;
- break;
+ break;
case wordbeg:
- if (d == end2 /* Points to end */
- || (d == end1 && size2 == 0) /* Points to end */
- || SYNTAX (* (d == end1 ? string2 : d)) != Sword) /* Next char not a letter */
- goto fail;
- if (d == string1 /* Points to first char */
- || SYNTAX (d[-1]) != Sword) /* prev char not letter */
+ DEBUG_PRINT1 ("EXECUTING wordbeg.\n");
+ if (WORDCHAR_P (d) && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1)))
break;
- goto fail;
+ goto fail;
case wordend:
- if (d == string1 /* Points to first char */
- || SYNTAX (d[-1]) != Sword) /* prev char not letter */
- goto fail;
- if (d == end2 /* Points to end */
- || (d == end1 && size2 == 0) /* Points to end */
- || SYNTAX (d == end1 ? *string2 : *d) != Sword) /* Next char not a letter */
+ DEBUG_PRINT1 ("EXECUTING wordend.\n");
+ if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1)
+ && (!WORDCHAR_P (d) || AT_STRINGS_END (d)))
break;
- goto fail;
+ goto fail;
#ifdef emacs
- case before_dot:
- if (((d - string2 <= (unsigned) size2)
- ? d - bf_p2 : d - bf_p1)
- <= point)
- goto fail;
- break;
-
+#ifdef emacs19
+ case before_dot:
+ DEBUG_PRINT1 ("EXECUTING before_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) >= point)
+ goto fail;
+ break;
+
+ case at_dot:
+ DEBUG_PRINT1 ("EXECUTING at_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) != point)
+ goto fail;
+ break;
+
+ case after_dot:
+ DEBUG_PRINT1 ("EXECUTING after_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) <= point)
+ goto fail;
+ break;
+#else /* not emacs19 */
case at_dot:
- if (((d - string2 <= (unsigned) size2)
- ? d - bf_p2 : d - bf_p1)
- == point)
+ DEBUG_PRINT1 ("EXECUTING at_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) + 1 != point)
goto fail;
break;
-
- case after_dot:
- if (((d - string2 <= (unsigned) size2)
- ? d - bf_p2 : d - bf_p1)
- >= point)
- goto fail;
- break;
-
- case wordchar:
- mcnt = (int) Sword;
- goto matchsyntax;
+#endif /* not emacs19 */
case syntaxspec:
+ DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt);
mcnt = *p++;
- matchsyntax:
- PREFETCH;
- if (SYNTAX (*d++) != (enum syntaxcode) mcnt) goto fail;
- break;
-
- case notwordchar:
+ goto matchsyntax;
+
+ case wordchar:
+ DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n");
mcnt = (int) Sword;
- goto matchnotsyntax;
+ matchsyntax:
+ PREFETCH ();
+ if (SYNTAX (*d++) != (enum syntaxcode) mcnt)
+ goto fail;
+ SET_REGS_MATCHED ();
+ break;
case notsyntaxspec:
+ DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt);
mcnt = *p++;
- matchnotsyntax:
- PREFETCH;
- if (SYNTAX (*d++) == (enum syntaxcode) mcnt) goto fail;
- break;
-#else
+ goto matchnotsyntax;
+
+ case notwordchar:
+ DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n");
+ mcnt = (int) Sword;
+ matchnotsyntax:
+ PREFETCH ();
+ if (SYNTAX (*d++) == (enum syntaxcode) mcnt)
+ goto fail;
+ SET_REGS_MATCHED ();
+ break;
+
+#else /* not emacs */
case wordchar:
- PREFETCH;
- if (SYNTAX (*d++) == 0) goto fail;
+ DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n");
+ PREFETCH ();
+ if (!WORDCHAR_P (d))
+ goto fail;
+ SET_REGS_MATCHED ();
+ d++;
break;
case notwordchar:
- PREFETCH;
- if (SYNTAX (*d++) != 0) goto fail;
+ DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n");
+ PREFETCH ();
+ if (WORDCHAR_P (d))
+ goto fail;
+ SET_REGS_MATCHED ();
+ d++;
break;
#endif /* not emacs */
+
+ default:
+ abort ();
+ }
+ continue; /* Successfully executed one pattern command; keep going. */
- case begbuf:
- if (d == string1) /* Note, d cannot equal string2 */
- break; /* unless string1 == string2. */
- goto fail;
- case endbuf:
- if (d == end2 || (d == end1 && size2 == 0))
- break;
- goto fail;
+ /* We goto here if a matching operation fails. */
+ fail:
+ if (!FAIL_STACK_EMPTY ())
+ { /* A restart point is known. Restore to that state. */
+ DEBUG_PRINT1 ("\nFAIL:\n");
+ POP_FAILURE_POINT (d, p,
+ lowest_active_reg, highest_active_reg,
+ regstart, regend, reg_info);
+
+ /* If this failure point is a dummy, try the next one. */
+ if (!p)
+ goto fail;
- case exactn:
- /* Match the next few pattern characters exactly.
- mcnt is how many characters to match. */
- mcnt = *p++;
- if (translate)
- {
- do
- {
- PREFETCH;
- if (translate[*d++] != *p++) goto fail;
- }
- while (--mcnt);
- }
- else
+ /* If we failed to the end of the pattern, don't examine *p. */
+ assert (p <= pend);
+ if (p < pend)
+ {
+ boolean is_a_jump_n = false;
+
+ /* If failed to a backwards jump that's part of a repetition
+ loop, need to pop this failure point and use the next one. */
+ switch ((re_opcode_t) *p)
+ {
+ case jump_n:
+ is_a_jump_n = true;
+ case maybe_pop_jump:
+ case pop_failure_jump:
+ case jump:
+ p1 = p + 1;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ p1 += mcnt;
+
+ if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n)
+ || (!is_a_jump_n
+ && (re_opcode_t) *p1 == on_failure_jump))
+ goto fail;
+ break;
+ default:
+ /* do nothing */ ;
+ }
+ }
+
+ if (d >= string1 && d <= end1)
+ dend = end_match_1;
+ }
+ else
+ break; /* Matching at this starting point really fails. */
+ } /* for (;;) */
+
+ if (best_regs_set)
+ goto restore_best_regs;
+
+ FREE_VARIABLES ();
+
+ return -1; /* Failure to match. */
+} /* re_match_2 */
+
+/* Subroutine definitions for re_match_2. */
+
+
+/* We are passed P pointing to a register number after a start_memory.
+
+ Return true if the pattern up to the corresponding stop_memory can
+ match the empty string, and false otherwise.
+
+ If we find the matching stop_memory, sets P to point to one past its number.
+ Otherwise, sets P to an undefined byte less than or equal to END.
+
+ We don't handle duplicates properly (yet). */
+
+static boolean
+group_match_null_string_p (p, end, reg_info)
+ unsigned char **p, *end;
+ register_info_type *reg_info;
+{
+ int mcnt;
+ /* Point to after the args to the start_memory. */
+ unsigned char *p1 = *p + 2;
+
+ while (p1 < end)
+ {
+ /* Skip over opcodes that can match nothing, and return true or
+ false, as appropriate, when we get to one that can't, or to the
+ matching stop_memory. */
+
+ switch ((re_opcode_t) *p1)
+ {
+ /* Could be either a loop or a series of alternatives. */
+ case on_failure_jump:
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+
+ /* If the next operation is not a jump backwards in the
+ pattern. */
+
+ if (mcnt >= 0)
{
- do
- {
- PREFETCH;
- if (*d++ != *p++) goto fail;
- }
- while (--mcnt);
- }
- break;
+ /* Go through the on_failure_jumps of the alternatives,
+ seeing if any of the alternatives cannot match nothing.
+ The last alternative starts with only a jump,
+ whereas the rest start with on_failure_jump and end
+ with a jump, e.g., here is the pattern for `a|b|c':
+
+ /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6
+ /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3
+ /exactn/1/c
+
+ So, we have to first go through the first (n-1)
+ alternatives and then deal with the last one separately. */
+
+
+ /* Deal with the first (n-1) alternatives, which start
+ with an on_failure_jump (see above) that jumps to right
+ past a jump_past_alt. */
+
+ while ((re_opcode_t) p1[mcnt-3] == jump_past_alt)
+ {
+ /* `mcnt' holds how many bytes long the alternative
+ is, including the ending `jump_past_alt' and
+ its number. */
+
+ if (!alt_match_null_string_p (p1, p1 + mcnt - 3,
+ reg_info))
+ return false;
+
+ /* Move to right after this alternative, including the
+ jump_past_alt. */
+ p1 += mcnt;
+
+ /* Break if it's the beginning of an n-th alternative
+ that doesn't begin with an on_failure_jump. */
+ if ((re_opcode_t) *p1 != on_failure_jump)
+ break;
+
+ /* Still have to check that it's not an n-th
+ alternative that starts with an on_failure_jump. */
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ if ((re_opcode_t) p1[mcnt-3] != jump_past_alt)
+ {
+ /* Get to the beginning of the n-th alternative. */
+ p1 -= 3;
+ break;
+ }
+ }
+
+ /* Deal with the last alternative: go back and get number
+ of the `jump_past_alt' just before it. `mcnt' contains
+ the length of the alternative. */
+ EXTRACT_NUMBER (mcnt, p1 - 2);
+
+ if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info))
+ return false;
+
+ p1 += mcnt; /* Get past the n-th alternative. */
+ } /* if mcnt > 0 */
+ break;
+
+
+ case stop_memory:
+ assert (p1[1] == **p);
+ *p = p1 + 2;
+ return true;
+
+
+ default:
+ if (!common_op_match_null_string_p (&p1, end, reg_info))
+ return false;
+ }
+ } /* while p1 < end */
+
+ return false;
+} /* group_match_null_string_p */
+
+
+/* Similar to group_match_null_string_p, but doesn't deal with alternatives:
+ It expects P to be the first byte of a single alternative and END one
+ byte past the last. The alternative can contain groups. */
+
+static boolean
+alt_match_null_string_p (p, end, reg_info)
+ unsigned char *p, *end;
+ register_info_type *reg_info;
+{
+ int mcnt;
+ unsigned char *p1 = p;
+
+ while (p1 < end)
+ {
+ /* Skip over opcodes that can match nothing, and break when we get
+ to one that can't. */
+
+ switch ((re_opcode_t) *p1)
+ {
+ /* It's a loop. */
+ case on_failure_jump:
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ p1 += mcnt;
+ break;
+
+ default:
+ if (!common_op_match_null_string_p (&p1, end, reg_info))
+ return false;
+ }
+ } /* while p1 < end */
+
+ return true;
+} /* alt_match_null_string_p */
+
+
+/* Deals with the ops common to group_match_null_string_p and
+ alt_match_null_string_p.
+
+ Sets P to one after the op and its arguments, if any. */
+
+static boolean
+common_op_match_null_string_p (p, end, reg_info)
+ unsigned char **p, *end;
+ register_info_type *reg_info;
+{
+ int mcnt;
+ boolean ret;
+ int reg_no;
+ unsigned char *p1 = *p;
- default:
- break;
- }
- continue; /* Successfully matched one pattern command; keep matching */
+ switch ((re_opcode_t) *p1++)
+ {
+ case no_op:
+ case begline:
+ case endline:
+ case begbuf:
+ case endbuf:
+ case wordbeg:
+ case wordend:
+ case wordbound:
+ case notwordbound:
+#ifdef emacs
+ case before_dot:
+ case at_dot:
+ case after_dot:
+#endif
+ break;
- /* Jump here if any matching operation fails. */
- fail:
- if (stackp != stackb)
- /* A restart point is known. Restart there and pop it. */
- {
- if (!stackp[-2])
- { /* If innermost failure point is dormant, flush it and keep looking */
- stackp -= 2;
- goto fail;
- }
- d = *--stackp;
- p = *--stackp;
- if (d >= string1 && d <= end1)
- dend = end_match_1;
- }
- else break; /* Matching at this starting point really fails! */
- }
- return -1; /* Failure to match */
-}
+ case start_memory:
+ reg_no = *p1;
+ assert (reg_no > 0 && reg_no <= MAX_REGNUM);
+ ret = group_match_null_string_p (&p1, end, reg_info);
+
+ /* Have to set this here in case we're checking a group which
+ contains a group and a back reference to it. */
+
+ if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE)
+ REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret;
+
+ if (!ret)
+ return false;
+ break;
+
+ /* If this is an optimized succeed_n for zero times, make the jump. */
+ case jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ if (mcnt >= 0)
+ p1 += mcnt;
+ else
+ return false;
+ break;
+
+ case succeed_n:
+ /* Get to the number of times to succeed. */
+ p1 += 2;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+
+ if (mcnt == 0)
+ {
+ p1 -= 4;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ p1 += mcnt;
+ }
+ else
+ return false;
+ break;
+
+ case duplicate:
+ if (!REG_MATCH_NULL_STRING_P (reg_info[*p1]))
+ return false;
+ break;
+
+ case set_number_at:
+ p1 += 4;
+ default:
+ /* All other opcodes mean we cannot match the empty string. */
+ return false;
+ }
+
+ *p = p1;
+ return true;
+} /* common_op_match_null_string_p */
+
+
+/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN
+ bytes; nonzero otherwise. */
+
static int
bcmp_translate (s1, s2, len, translate)
unsigned char *s1, *s2;
register int len;
- unsigned char *translate;
+ char *translate;
{
register unsigned char *p1 = s1, *p2 = s2;
while (len)
{
- if (translate [*p1++] != translate [*p2++]) return 1;
+ if (translate[*p1++] != translate[*p2++]) return 1;
len--;
}
return 0;
}
-/* Entry points compatible with bsd4.2 regex library */
+/* Entry points for GNU code. */
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+ compiles PATTERN (of length SIZE) and puts the result in BUFP.
+ Returns 0 if the pattern was valid, otherwise an error string.
+
+ Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+ are set in BUFP on entry.
+
+ We call regex_compile to do the actual compilation. */
+
+const char *
+re_compile_pattern (pattern, length, bufp)
+ const char *pattern;
+ int length;
+ struct re_pattern_buffer *bufp;
+{
+ reg_errcode_t ret;
+
+ /* GNU code is written to assume at least RE_NREGS registers will be set
+ (and at least one extra will be -1). */
+ bufp->regs_allocated = REGS_UNALLOCATED;
+
+ /* And GNU code determines whether or not to get register information
+ by passing null for the REGS argument to re_match, etc., not by
+ setting no_sub. */
+ bufp->no_sub = 0;
+
+ /* Match anchors at newline. */
+ bufp->newline_anchor = 1;
+
+ ret = regex_compile (pattern, length, re_syntax_options, bufp);
-#ifndef emacs
+ return re_error_msg[(int) ret];
+}
+
+/* Entry points compatible with 4.2 BSD regex library. We don't define
+ them if this is an Emacs or POSIX compilation. */
+
+#if !defined (emacs) && !defined (_POSIX_SOURCE)
+/* BSD has one and only one pattern buffer. */
static struct re_pattern_buffer re_comp_buf;
char *
re_comp (s)
- char *s;
+ const char *s;
{
+ reg_errcode_t ret;
+
if (!s)
{
if (!re_comp_buf.buffer)
@@ -1610,159 +4699,289 @@ re_comp (s)
if (!re_comp_buf.buffer)
{
- if (!(re_comp_buf.buffer = (char *) malloc (200)))
- return "Memory exhausted";
+ re_comp_buf.buffer = (unsigned char *) malloc (200);
+ if (re_comp_buf.buffer == NULL)
+ return "Memory exhausted";
re_comp_buf.allocated = 200;
- if (!(re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH)))
+
+ re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH);
+ if (re_comp_buf.fastmap == NULL)
return "Memory exhausted";
}
- return re_compile_pattern (s, strlen (s), &re_comp_buf);
+
+ /* Since `re_exec' always passes NULL for the `regs' argument, we
+ don't need to initialize the pattern buffer fields which affect it. */
+
+ /* Match anchors at newlines. */
+ re_comp_buf.newline_anchor = 1;
+
+ ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf);
+
+ /* Yes, we're discarding `const' here. */
+ return (char *) re_error_msg[(int) ret];
}
+
int
re_exec (s)
- char *s;
+ const char *s;
{
- int len = strlen (s);
- return 0 <= re_search (&re_comp_buf, s, len, 0, len, 0);
+ const int len = strlen (s);
+ return
+ 0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0);
}
-
-#endif /* emacs */
+#endif /* not emacs and not _POSIX_SOURCE */
-#ifdef test
+/* POSIX.2 functions. Don't define these for Emacs. */
-#include <stdio.h>
+#ifndef emacs
-/* Indexed by a character, gives the upper case equivalent of the character */
-
-static char upcase[0400] =
- { 000, 001, 002, 003, 004, 005, 006, 007,
- 010, 011, 012, 013, 014, 015, 016, 017,
- 020, 021, 022, 023, 024, 025, 026, 027,
- 030, 031, 032, 033, 034, 035, 036, 037,
- 040, 041, 042, 043, 044, 045, 046, 047,
- 050, 051, 052, 053, 054, 055, 056, 057,
- 060, 061, 062, 063, 064, 065, 066, 067,
- 070, 071, 072, 073, 074, 075, 076, 077,
- 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
- 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
- 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
- 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137,
- 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
- 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
- 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
- 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177,
- 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
- 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
- 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
- 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
- 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
- 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
- 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
- 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
- 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
- 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
- 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
- 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
- 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
- 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
- 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
- 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
- };
+/* regcomp takes a regular expression as a string and compiles it.
-main (argc, argv)
- int argc;
- char **argv;
-{
- char pat[80];
- struct re_pattern_buffer buf;
- int i;
- char c;
- char fastmap[(1 << BYTEWIDTH)];
-
- /* Allow a command argument to specify the style of syntax. */
- if (argc > 1)
- obscure_syntax = atoi (argv[1]);
-
- buf.allocated = 40;
- buf.buffer = (char *) malloc (buf.allocated);
- buf.fastmap = fastmap;
- buf.translate = upcase;
-
- while (1)
- {
- gets (pat);
+ PREG is a regex_t *. We do not expect any fields to be initialized,
+ since POSIX says we shouldn't. Thus, we set
- if (*pat)
- {
- re_compile_pattern (pat, strlen(pat), &buf);
+ `buffer' to the compiled pattern;
+ `used' to the length of the compiled pattern;
+ `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+ REG_EXTENDED bit in CFLAGS is set; otherwise, to
+ RE_SYNTAX_POSIX_BASIC;
+ `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+ `fastmap' and `fastmap_accurate' to zero;
+ `re_nsub' to the number of subexpressions in PATTERN.
- for (i = 0; i < buf.used; i++)
- printchar (buf.buffer[i]);
+ PATTERN is the address of the pattern string.
- putchar ('\n');
+ CFLAGS is a series of bits which affect compilation.
- printf ("%d allocated, %d used.\n", buf.allocated, buf.used);
+ If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+ use POSIX basic syntax.
- re_compile_fastmap (&buf);
- printf ("Allowed by fastmap: ");
- for (i = 0; i < (1 << BYTEWIDTH); i++)
- if (fastmap[i]) printchar (i);
- putchar ('\n');
- }
+ If REG_NEWLINE is set, then . and [^...] don't match newline.
+ Also, regexec will try a match beginning after every newline.
+
+ If REG_ICASE is set, then we considers upper- and lowercase
+ versions of letters to be equivalent when matching.
+
+ If REG_NOSUB is set, then when PREG is passed to regexec, that
+ routine will report only success or failure, and nothing about the
+ registers.
+
+ It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for
+ the return codes and their meanings.) */
- gets (pat); /* Now read the string to match against */
+int
+regcomp (preg, pattern, cflags)
+ regex_t *preg;
+ const char *pattern;
+ int cflags;
+{
+ reg_errcode_t ret;
+ unsigned syntax
+ = (cflags & REG_EXTENDED) ?
+ RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC;
+
+ /* regex_compile will allocate the space for the compiled pattern. */
+ preg->buffer = 0;
+ preg->allocated = 0;
+ preg->used = 0;
+
+ /* Don't bother to use a fastmap when searching. This simplifies the
+ REG_NEWLINE case: if we used a fastmap, we'd have to put all the
+ characters after newlines into the fastmap. This way, we just try
+ every character. */
+ preg->fastmap = 0;
+
+ if (cflags & REG_ICASE)
+ {
+ unsigned i;
+
+ preg->translate = (char *) malloc (CHAR_SET_SIZE);
+ if (preg->translate == NULL)
+ return (int) REG_ESPACE;
- i = re_match (&buf, pat, strlen (pat), 0, 0);
- printf ("Match value %d.\n", i);
+ /* Map uppercase characters to corresponding lowercase ones. */
+ for (i = 0; i < CHAR_SET_SIZE; i++)
+ preg->translate[i] = ISUPPER (i) ? tolower (i) : i;
}
+ else
+ preg->translate = NULL;
+
+ /* If REG_NEWLINE is set, newlines are treated differently. */
+ if (cflags & REG_NEWLINE)
+ { /* REG_NEWLINE implies neither . nor [^...] match newline. */
+ syntax &= ~RE_DOT_NEWLINE;
+ syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+ /* It also changes the matching behavior. */
+ preg->newline_anchor = 1;
+ }
+ else
+ preg->newline_anchor = 0;
+
+ preg->no_sub = !!(cflags & REG_NOSUB);
+
+ /* POSIX says a null character in the pattern terminates it, so we
+ can use strlen here in compiling the pattern. */
+ ret = regex_compile (pattern, strlen (pattern), syntax, preg);
+
+ /* POSIX doesn't distinguish between an unmatched open-group and an
+ unmatched close-group: both are REG_EPAREN. */
+ if (ret == REG_ERPAREN) ret = REG_EPAREN;
+
+ return (int) ret;
}
-#ifdef NOTDEF
-print_buf (bufp)
- struct re_pattern_buffer *bufp;
+
+/* regexec searches for a given pattern, specified by PREG, in the
+ string STRING.
+
+ If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+ `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at
+ least NMATCH elements, and we set them to the offsets of the
+ corresponding matched substrings.
+
+ EFLAGS specifies `execution flags' which affect matching: if
+ REG_NOTBOL is set, then ^ does not match at the beginning of the
+ string; if REG_NOTEOL is set, then $ does not match at the end.
+
+ We return 0 if we find a match and REG_NOMATCH if not. */
+
+int
+regexec (preg, string, nmatch, pmatch, eflags)
+ const regex_t *preg;
+ const char *string;
+ size_t nmatch;
+ regmatch_t pmatch[];
+ int eflags;
{
- int i;
+ int ret;
+ struct re_registers regs;
+ regex_t private_preg;
+ int len = strlen (string);
+ boolean want_reg_info = !preg->no_sub && nmatch > 0;
- printf ("buf is :\n----------------\n");
- for (i = 0; i < bufp->used; i++)
- printchar (bufp->buffer[i]);
+ private_preg = *preg;
+
+ private_preg.not_bol = !!(eflags & REG_NOTBOL);
+ private_preg.not_eol = !!(eflags & REG_NOTEOL);
- printf ("\n%d allocated, %d used.\n", bufp->allocated, bufp->used);
+ /* The user has told us exactly how many registers to return
+ information about, via `nmatch'. We have to pass that on to the
+ matching routines. */
+ private_preg.regs_allocated = REGS_FIXED;
- printf ("Allowed by fastmap: ");
- for (i = 0; i < (1 << BYTEWIDTH); i++)
- if (bufp->fastmap[i])
- printchar (i);
- printf ("\nAllowed by translate: ");
- if (bufp->translate)
- for (i = 0; i < (1 << BYTEWIDTH); i++)
- if (bufp->translate[i])
- printchar (i);
- printf ("\nfastmap is%s accurate\n", bufp->fastmap_accurate ? "" : "n't");
- printf ("can %s be null\n----------", bufp->can_be_null ? "" : "not");
+ if (want_reg_info)
+ {
+ regs.num_regs = nmatch;
+ regs.start = TALLOC (nmatch, regoff_t);
+ regs.end = TALLOC (nmatch, regoff_t);
+ if (regs.start == NULL || regs.end == NULL)
+ return (int) REG_NOMATCH;
+ }
+
+ /* Perform the searching operation. */
+ ret = re_search (&private_preg, string, len,
+ /* start: */ 0, /* range: */ len,
+ want_reg_info ? &regs : (struct re_registers *) 0);
+
+ /* Copy the register information to the POSIX structure. */
+ if (want_reg_info)
+ {
+ if (ret >= 0)
+ {
+ unsigned r;
+
+ for (r = 0; r < nmatch; r++)
+ {
+ pmatch[r].rm_so = regs.start[r];
+ pmatch[r].rm_eo = regs.end[r];
+ }
+ }
+
+ /* If we needed the temporary register info, free the space now. */
+ free (regs.start);
+ free (regs.end);
+ }
+
+ /* We want zero return to mean success, unlike `re_search'. */
+ return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH;
}
-#endif
-printchar (c)
- char c;
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+ from either regcomp or regexec. We don't use PREG here. */
+
+size_t
+regerror (errcode, preg, errbuf, errbuf_size)
+ int errcode;
+ const regex_t *preg;
+ char *errbuf;
+ size_t errbuf_size;
{
- if (c < 041 || c >= 0177)
+ const char *msg;
+ size_t msg_size;
+
+ if (errcode < 0
+ || errcode >= (sizeof (re_error_msg) / sizeof (re_error_msg[0])))
+ /* Only error codes returned by the rest of the code should be passed
+ to this routine. If we are given anything else, or if other regex
+ code generates an invalid error code, then the program has a bug.
+ Dump core so we can fix it. */
+ abort ();
+
+ msg = re_error_msg[errcode];
+
+ /* POSIX doesn't require that we do anything in this case, but why
+ not be nice. */
+ if (! msg)
+ msg = "Success";
+
+ msg_size = strlen (msg) + 1; /* Includes the null. */
+
+ if (errbuf_size != 0)
{
- putchar ('\\');
- putchar (((c >> 6) & 3) + '0');
- putchar (((c >> 3) & 7) + '0');
- putchar ((c & 7) + '0');
+ if (msg_size > errbuf_size)
+ {
+ strncpy (errbuf, msg, errbuf_size - 1);
+ errbuf[errbuf_size - 1] = 0;
+ }
+ else
+ strcpy (errbuf, msg);
}
- else
- putchar (c);
+
+ return msg_size;
}
-error (string)
- char *string;
+
+/* Free dynamically allocated space used by PREG. */
+
+void
+regfree (preg)
+ regex_t *preg;
{
- puts (string);
- exit (1);
+ if (preg->buffer != NULL)
+ free (preg->buffer);
+ preg->buffer = NULL;
+
+ preg->allocated = 0;
+ preg->used = 0;
+
+ if (preg->fastmap != NULL)
+ free (preg->fastmap);
+ preg->fastmap = NULL;
+ preg->fastmap_accurate = 0;
+
+ if (preg->translate != NULL)
+ free (preg->translate);
+ preg->translate = NULL;
}
-#endif /* test */
+#endif /* not emacs */
+
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
diff --git a/gnu/usr.bin/grep/regex.h b/gnu/usr.bin/grep/regex.h
index a5e89d3..408dd21 100644
--- a/gnu/usr.bin/grep/regex.h
+++ b/gnu/usr.bin/grep/regex.h
@@ -1,5 +1,7 @@
-/* Definitions for data structures callers pass the regex library.
- Copyright (C) 1985, 1989 Free Software Foundation, Inc.
+/* Definitions for data structures and routines for the regular
+ expression library, version 0.12.
+
+ Copyright (C) 1985, 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,173 +15,476 @@
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
- In other words, you are welcome to use, share and improve this program.
- You are forbidden to forbid anyone else to use, share and improve
- what you give them. Help stamp out software-hoarding! */
+#ifndef __REGEXP_LIBRARY_H__
+#define __REGEXP_LIBRARY_H__
+/* POSIX says that <sys/types.h> must be included (by the caller) before
+ <regex.h>. */
-/* Define number of parens for which we record the beginnings and ends.
- This affects how much space the `struct re_registers' type takes up. */
-#ifndef RE_NREGS
-#define RE_NREGS 10
+#ifdef VMS
+/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it
+ should be there. */
+#include <stddef.h>
#endif
-/* These bits are used in the obscure_syntax variable to choose among
- alternative regexp syntaxes. */
-
-/* 1 means plain parentheses serve as grouping, and backslash
- parentheses are needed for literal searching.
- 0 means backslash-parentheses are grouping, and plain parentheses
- are for literal searching. */
-#define RE_NO_BK_PARENS 1
-
-/* 1 means plain | serves as the "or"-operator, and \| is a literal.
- 0 means \| serves as the "or"-operator, and | is a literal. */
-#define RE_NO_BK_VBAR 2
-
-/* 0 means plain + or ? serves as an operator, and \+, \? are literals.
- 1 means \+, \? are operators and plain +, ? are literals. */
-#define RE_BK_PLUS_QM 4
-
-/* 1 means | binds tighter than ^ or $.
- 0 means the contrary. */
-#define RE_TIGHT_VBAR 8
-
-/* 1 means treat \n as an _OR operator
- 0 means treat it as a normal character */
-#define RE_NEWLINE_OR 16
-
-/* 0 means that a special characters (such as *, ^, and $) always have
- their special meaning regardless of the surrounding context.
- 1 means that special characters may act as normal characters in some
- contexts. Specifically, this applies to:
- ^ - only special at the beginning, or after ( or |
- $ - only special at the end, or before ) or |
- *, +, ? - only special when not after the beginning, (, or | */
-#define RE_CONTEXT_INDEP_OPS 32
-
-/* Now define combinations of bits for the standard possibilities. */
-#define RE_SYNTAX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_CONTEXT_INDEP_OPS)
-#define RE_SYNTAX_EGREP (RE_SYNTAX_AWK | RE_NEWLINE_OR)
-#define RE_SYNTAX_GREP (RE_BK_PLUS_QM | RE_NEWLINE_OR)
+
+/* The following bits are used to determine the regexp syntax we
+ recognize. The set/not-set meanings are chosen so that Emacs syntax
+ remains the value 0. The bits are given in alphabetical order, and
+ the definitions shifted by one from the previous bit; thus, when we
+ add or remove a bit, only one other definition need change. */
+typedef unsigned reg_syntax_t;
+
+/* If this bit is not set, then \ inside a bracket expression is literal.
+ If set, then such a \ quotes the following character. */
+#define RE_BACKSLASH_ESCAPE_IN_LISTS (1)
+
+/* If this bit is not set, then + and ? are operators, and \+ and \? are
+ literals.
+ If set, then \+ and \? are operators and + and ? are literals. */
+#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
+
+/* If this bit is set, then character classes are supported. They are:
+ [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
+ [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+ If not set, then character classes are not supported. */
+#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
+
+/* If this bit is set, then ^ and $ are always anchors (outside bracket
+ expressions, of course).
+ If this bit is not set, then it depends:
+ ^ is an anchor if it is at the beginning of a regular
+ expression or after an open-group or an alternation operator;
+ $ is an anchor if it is at the end of a regular expression, or
+ before a close-group or an alternation operator.
+
+ This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
+ POSIX draft 11.2 says that * etc. in leading positions is undefined.
+ We already implemented a previous draft which made those constructs
+ invalid, though, so we haven't changed the code back. */
+#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
+
+/* If this bit is set, then special characters are always special
+ regardless of where they are in the pattern.
+ If this bit is not set, then special characters are special only in
+ some contexts; otherwise they are ordinary. Specifically,
+ * + ? and intervals are only special when not after the beginning,
+ open-group, or alternation operator. */
+#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
+
+/* If this bit is set, then *, +, ?, and { cannot be first in an re or
+ immediately after an alternation or begin-group operator. */
+#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
+
+/* If this bit is set, then . matches newline.
+ If not set, then it doesn't. */
+#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
+
+/* If this bit is set, then . doesn't match NUL.
+ If not set, then it does. */
+#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
+
+/* If this bit is set, nonmatching lists [^...] do not match newline.
+ If not set, they do. */
+#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
+
+/* If this bit is set, either \{...\} or {...} defines an
+ interval, depending on RE_NO_BK_BRACES.
+ If not set, \{, \}, {, and } are literals. */
+#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
+
+/* If this bit is set, +, ? and | aren't recognized as operators.
+ If not set, they are. */
+#define RE_LIMITED_OPS (RE_INTERVALS << 1)
+
+/* If this bit is set, newline is an alternation operator.
+ If not set, newline is literal. */
+#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
+
+/* If this bit is set, then `{...}' defines an interval, and \{ and \}
+ are literals.
+ If not set, then `\{...\}' defines an interval. */
+#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
+
+/* If this bit is set, (...) defines a group, and \( and \) are literals.
+ If not set, \(...\) defines a group, and ( and ) are literals. */
+#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
+
+/* If this bit is set, then \<digit> matches <digit>.
+ If not set, then \<digit> is a back-reference. */
+#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
+
+/* If this bit is set, then | is an alternation operator, and \| is literal.
+ If not set, then \| is an alternation operator, and | is literal. */
+#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
+
+/* If this bit is set, then an ending range point collating higher
+ than the starting range point, as in [z-a], is invalid.
+ If not set, then when ending range point collates higher than the
+ starting range point, the range is ignored. */
+#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
+
+/* If this bit is set, then an unmatched ) is ordinary.
+ If not set, then an unmatched ) is invalid. */
+#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+
+/* This global variable defines the particular regexp syntax to use (for
+ some interfaces). When a regexp is compiled, the syntax used is
+ stored in the pattern buffer, so changing this does not affect
+ already-compiled regexps. */
+extern reg_syntax_t re_syntax_options;
+
+/* Define combinations of the above bits for the standard possibilities.
+ (The [[[ comments delimit what gets put into the Texinfo file, so
+ don't delete them!) */
+/* [[[begin syntaxes]]] */
#define RE_SYNTAX_EMACS 0
-/* This data structure is used to represent a compiled pattern. */
+#define RE_SYNTAX_AWK \
+ (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS \
+ | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \
+ | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+#define RE_SYNTAX_POSIX_AWK \
+ (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS)
+
+#define RE_SYNTAX_GREP \
+ (RE_BK_PLUS_QM | RE_CHAR_CLASSES \
+ | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \
+ | RE_NEWLINE_ALT)
+
+#define RE_SYNTAX_EGREP \
+ (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \
+ | RE_NEWLINE_ALT | RE_NO_BK_PARENS \
+ | RE_NO_BK_VBAR)
+
+#define RE_SYNTAX_POSIX_EGREP \
+ (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
+
+/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */
+#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
+
+#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
+
+/* Syntax bits common to both basic and extended POSIX regex syntax. */
+#define _RE_SYNTAX_POSIX_COMMON \
+ (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \
+ | RE_INTERVALS | RE_NO_EMPTY_RANGES)
+
+#define RE_SYNTAX_POSIX_BASIC \
+ (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM)
+
+/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
+ RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this
+ isn't minimal, since other operators, such as \`, aren't disabled. */
+#define RE_SYNTAX_POSIX_MINIMAL_BASIC \
+ (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
+
+#define RE_SYNTAX_POSIX_EXTENDED \
+ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \
+ | RE_NO_BK_PARENS | RE_NO_BK_VBAR \
+ | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS
+ replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added. */
+#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \
+ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS \
+ | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD)
+/* [[[end syntaxes]]] */
+
+/* Maximum number of duplicates an interval can allow. Some systems
+ (erroneously) define this in other header files, but we want our
+ value, so remove any previous define. */
+#ifdef RE_DUP_MAX
+#undef RE_DUP_MAX
+#endif
+#define RE_DUP_MAX ((1 << 15) - 1)
+
+
+/* POSIX `cflags' bits (i.e., information for `regcomp'). */
+
+/* If this bit is set, then use extended regular expression syntax.
+ If not set, then use basic regular expression syntax. */
+#define REG_EXTENDED 1
+
+/* If this bit is set, then ignore case when matching.
+ If not set, then case is significant. */
+#define REG_ICASE (REG_EXTENDED << 1)
+
+/* If this bit is set, then anchors do not match at newline
+ characters in the string.
+ If not set, then anchors do match at newlines. */
+#define REG_NEWLINE (REG_ICASE << 1)
+
+/* If this bit is set, then report only success or fail in regexec.
+ If not set, then returns differ between not matching and errors. */
+#define REG_NOSUB (REG_NEWLINE << 1)
+
+
+/* POSIX `eflags' bits (i.e., information for regexec). */
+
+/* If this bit is set, then the beginning-of-line operator doesn't match
+ the beginning of the string (presumably because it's not the
+ beginning of a line).
+ If not set, then the beginning-of-line operator does match the
+ beginning of the string. */
+#define REG_NOTBOL 1
+
+/* Like REG_NOTBOL, except for the end-of-line. */
+#define REG_NOTEOL (1 << 1)
+
+
+/* If any error codes are removed, changed, or added, update the
+ `re_error_msg' table in regex.c. */
+typedef enum
+{
+ REG_NOERROR = 0, /* Success. */
+ REG_NOMATCH, /* Didn't find a match (for regexec). */
+
+ /* POSIX regcomp return error codes. (In the order listed in the
+ standard.) */
+ REG_BADPAT, /* Invalid pattern. */
+ REG_ECOLLATE, /* Not implemented. */
+ REG_ECTYPE, /* Invalid character class name. */
+ REG_EESCAPE, /* Trailing backslash. */
+ REG_ESUBREG, /* Invalid back reference. */
+ REG_EBRACK, /* Unmatched left bracket. */
+ REG_EPAREN, /* Parenthesis imbalance. */
+ REG_EBRACE, /* Unmatched \{. */
+ REG_BADBR, /* Invalid contents of \{\}. */
+ REG_ERANGE, /* Invalid range end. */
+ REG_ESPACE, /* Ran out of memory. */
+ REG_BADRPT, /* No preceding re for repetition op. */
+
+ /* Error codes we've added. */
+ REG_EEND, /* Premature end. */
+ REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */
+ REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */
+} reg_errcode_t;
+
+/* This data structure represents a compiled pattern. Before calling
+ the pattern compiler, the fields `buffer', `allocated', `fastmap',
+ `translate', and `no_sub' can be set. After the pattern has been
+ compiled, the `re_nsub' field is available. All other fields are
+ private to the regex routines. */
struct re_pattern_buffer
- {
- char *buffer; /* Space holding the compiled pattern commands. */
- int allocated; /* Size of space that buffer points to */
- int used; /* Length of portion of buffer actually occupied */
- char *fastmap; /* Pointer to fastmap, if any, or zero if none. */
- /* re_search uses the fastmap, if there is one,
- to skip quickly over totally implausible characters */
- char *translate; /* Translate table to apply to all characters before comparing.
- Or zero for no translation.
- The translation is applied to a pattern when it is compiled
- and to data when it is matched. */
- char fastmap_accurate;
- /* Set to zero when a new pattern is stored,
- set to one when the fastmap is updated from it. */
- char can_be_null; /* Set to one by compiling fastmap
- if this pattern might match the null string.
- It does not necessarily match the null string
- in that case, but if this is zero, it cannot.
- 2 as value means can match null string
- but at end of range or before a character
- listed in the fastmap. */
- };
-
-/* Structure to store "register" contents data in.
-
- Pass the address of such a structure as an argument to re_match, etc.,
- if you want this information back.
-
- start[i] and end[i] record the string matched by \( ... \) grouping i,
- for i from 1 to RE_NREGS - 1.
- start[0] and end[0] record the entire string matched. */
+{
+/* [[[begin pattern_buffer]]] */
+ /* Space that holds the compiled pattern. It is declared as
+ `unsigned char *' because its elements are
+ sometimes used as array indexes. */
+ unsigned char *buffer;
-struct re_registers
- {
- int start[RE_NREGS];
- int end[RE_NREGS];
- };
-
-/* These are the command codes that appear in compiled regular expressions, one per byte.
- Some command codes are followed by argument bytes.
- A command code can specify any interpretation whatever for its arguments.
- Zero-bytes may appear in the compiled regular expression. */
-
-enum regexpcode
- {
- unused,
- exactn, /* followed by one byte giving n, and then by n literal bytes */
- begline, /* fails unless at beginning of line */
- endline, /* fails unless at end of line */
- jump, /* followed by two bytes giving relative address to jump to */
- on_failure_jump, /* followed by two bytes giving relative address of place
- to resume at in case of failure. */
- finalize_jump, /* Throw away latest failure point and then jump to address. */
- maybe_finalize_jump, /* Like jump but finalize if safe to do so.
- This is used to jump back to the beginning
- of a repeat. If the command that follows
- this jump is clearly incompatible with the
- one at the beginning of the repeat, such that
- we can be sure that there is no use backtracking
- out of repetitions already completed,
- then we finalize. */
- dummy_failure_jump, /* jump, and push a dummy failure point.
- This failure point will be thrown away
- if an attempt is made to use it for a failure.
- A + construct makes this before the first repeat. */
- anychar, /* matches any one character */
- charset, /* matches any one char belonging to specified set.
- First following byte is # bitmap bytes.
- Then come bytes for a bit-map saying which chars are in.
- Bits in each byte are ordered low-bit-first.
- A character is in the set if its bit is 1.
- A character too large to have a bit in the map
- is automatically not in the set */
- charset_not, /* similar but match any character that is NOT one of those specified */
- start_memory, /* starts remembering the text that is matched
- and stores it in a memory register.
- followed by one byte containing the register number.
- Register numbers must be in the range 0 through NREGS. */
- stop_memory, /* stops remembering the text that is matched
- and stores it in a memory register.
- followed by one byte containing the register number.
- Register numbers must be in the range 0 through NREGS. */
- duplicate, /* match a duplicate of something remembered.
- Followed by one byte containing the index of the memory register. */
- before_dot, /* Succeeds if before dot */
- at_dot, /* Succeeds if at dot */
- after_dot, /* Succeeds if after dot */
- begbuf, /* Succeeds if at beginning of buffer */
- endbuf, /* Succeeds if at end of buffer */
- wordchar, /* Matches any word-constituent character */
- notwordchar, /* Matches any char that is not a word-constituent */
- wordbeg, /* Succeeds if at word beginning */
- wordend, /* Succeeds if at word end */
- wordbound, /* Succeeds if at a word boundary */
- notwordbound, /* Succeeds if not at a word boundary */
- syntaxspec, /* Matches any character whose syntax is specified.
- followed by a byte which contains a syntax code, Sword or such like */
- notsyntaxspec /* Matches any character whose syntax differs from the specified. */
- };
+ /* Number of bytes to which `buffer' points. */
+ unsigned long allocated;
+
+ /* Number of bytes actually used in `buffer'. */
+ unsigned long used;
+
+ /* Syntax setting with which the pattern was compiled. */
+ reg_syntax_t syntax;
+
+ /* Pointer to a fastmap, if any, otherwise zero. re_search uses
+ the fastmap, if there is one, to skip over impossible
+ starting points for matches. */
+ char *fastmap;
+
+ /* Either a translate table to apply to all characters before
+ comparing them, or zero for no translation. The translation
+ is applied to a pattern when it is compiled and to a string
+ when it is matched. */
+ char *translate;
+
+ /* Number of subexpressions found by the compiler. */
+ size_t re_nsub;
+
+ /* Zero if this pattern cannot match the empty string, one else.
+ Well, in truth it's used only in `re_search_2', to see
+ whether or not we should use the fastmap, so we don't set
+ this absolutely perfectly; see `re_compile_fastmap' (the
+ `duplicate' case). */
+ unsigned can_be_null : 1;
+
+ /* If REGS_UNALLOCATED, allocate space in the `regs' structure
+ for `max (RE_NREGS, re_nsub + 1)' groups.
+ If REGS_REALLOCATE, reallocate space if necessary.
+ If REGS_FIXED, use what's there. */
+#define REGS_UNALLOCATED 0
+#define REGS_REALLOCATE 1
+#define REGS_FIXED 2
+ unsigned regs_allocated : 2;
+
+ /* Set to zero when `regex_compile' compiles a pattern; set to one
+ by `re_compile_fastmap' if it updates the fastmap. */
+ unsigned fastmap_accurate : 1;
+
+ /* If set, `re_match_2' does not return information about
+ subexpressions. */
+ unsigned no_sub : 1;
+
+ /* If set, a beginning-of-line anchor doesn't match at the
+ beginning of the string. */
+ unsigned not_bol : 1;
+
+ /* Similarly for an end-of-line anchor. */
+ unsigned not_eol : 1;
+
+ /* If true, an anchor at a newline matches. */
+ unsigned newline_anchor : 1;
+
+/* [[[end pattern_buffer]]] */
+};
+
+typedef struct re_pattern_buffer regex_t;
+
+
+/* search.c (search_buffer) in Emacs needs this one opcode value. It is
+ defined both in `regex.c' and here. */
+#define RE_EXACTN_VALUE 1
-extern char *re_compile_pattern ();
-/* Is this really advertised? */
-extern void re_compile_fastmap ();
-extern int re_search (), re_search_2 ();
-extern int re_match (), re_match_2 ();
-
-/* 4.2 bsd compatibility (yuck) */
-extern char *re_comp ();
-extern int re_exec ();
-
-#ifdef SYNTAX_TABLE
-extern char *re_syntax_table;
+/* Type for byte offsets within the string. POSIX mandates this. */
+typedef int regoff_t;
+
+
+/* This is the structure we store register match data in. See
+ regex.texinfo for a full description of what registers match. */
+struct re_registers
+{
+ unsigned num_regs;
+ regoff_t *start;
+ regoff_t *end;
+};
+
+
+/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
+ `re_match_2' returns information about at least this many registers
+ the first time a `regs' structure is passed. */
+#ifndef RE_NREGS
+#define RE_NREGS 30
#endif
+
+
+/* POSIX specification for registers. Aside from the different names than
+ `re_registers', POSIX uses an array of structures, instead of a
+ structure of arrays. */
+typedef struct
+{
+ regoff_t rm_so; /* Byte offset from string's start to substring's start. */
+ regoff_t rm_eo; /* Byte offset from string's start to substring's end. */
+} regmatch_t;
+
+/* Declarations for routines. */
+
+/* To avoid duplicating every routine declaration -- once with a
+ prototype (if we are ANSI), and once without (if we aren't) -- we
+ use the following macro to declare argument types. This
+ unfortunately clutters up the declarations a bit, but I think it's
+ worth it. */
+
+#if __STDC__
+
+#define _RE_ARGS(args) args
+
+#else /* not __STDC__ */
+
+#define _RE_ARGS(args) ()
+
+#endif /* not __STDC__ */
+
+/* Sets the current default syntax to SYNTAX, and return the old syntax.
+ You can also simply assign to the `re_syntax_options' variable. */
+extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));
+
+/* Compile the regular expression PATTERN, with length LENGTH
+ and syntax given by the global `re_syntax_options', into the buffer
+ BUFFER. Return NULL if successful, and an error string if not. */
+extern const char *re_compile_pattern
+ _RE_ARGS ((const char *pattern, int length,
+ struct re_pattern_buffer *buffer));
+
+
+/* Compile a fastmap for the compiled pattern in BUFFER; used to
+ accelerate searches. Return 0 if successful and -2 if was an
+ internal error. */
+extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
+
+
+/* Search in the string STRING (with length LENGTH) for the pattern
+ compiled into BUFFER. Start searching at position START, for RANGE
+ characters. Return the starting position of the match, -1 for no
+ match, or -2 for an internal error. Also return register
+ information in REGS (if REGS and BUFFER->no_sub are nonzero). */
+extern int re_search
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+ int length, int start, int range, struct re_registers *regs));
+
+
+/* Like `re_search', but search in the concatenation of STRING1 and
+ STRING2. Also, stop searching at index START + STOP. */
+extern int re_search_2
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+ int length1, const char *string2, int length2,
+ int start, int range, struct re_registers *regs, int stop));
+
+
+/* Like `re_search', but return how many characters in STRING the regexp
+ in BUFFER matched, starting at position START. */
+extern int re_match
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+ int length, int start, struct re_registers *regs));
+
+
+/* Relates to `re_match' as `re_search_2' relates to `re_search'. */
+extern int re_match_2
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+ int length1, const char *string2, int length2,
+ int start, struct re_registers *regs, int stop));
+
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+ ENDS. Subsequent matches using BUFFER and REGS will use this memory
+ for recording register information. STARTS and ENDS must be
+ allocated with malloc, and must each be at least `NUM_REGS * sizeof
+ (regoff_t)' bytes long.
+
+ If NUM_REGS == 0, then subsequent matches should allocate their own
+ register data.
+
+ Unless this function is called, the first search or match using
+ PATTERN_BUFFER will allocate its own register data, without
+ freeing the old data. */
+extern void re_set_registers
+ _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
+ unsigned num_regs, regoff_t *starts, regoff_t *ends));
+
+/* 4.2 bsd compatibility. */
+extern char *re_comp _RE_ARGS ((const char *));
+extern int re_exec _RE_ARGS ((const char *));
+
+/* POSIX compatibility. */
+extern int regcomp _RE_ARGS ((regex_t *preg, const char *pattern, int cflags));
+extern int regexec
+ _RE_ARGS ((const regex_t *preg, const char *string, size_t nmatch,
+ regmatch_t pmatch[], int eflags));
+extern size_t regerror
+ _RE_ARGS ((int errcode, const regex_t *preg, char *errbuf,
+ size_t errbuf_size));
+extern void regfree _RE_ARGS ((regex_t *preg));
+
+#endif /* not __REGEXP_LIBRARY_H__ */
+
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
diff --git a/gnu/usr.bin/grep/search.c b/gnu/usr.bin/grep/search.c
new file mode 100644
index 0000000..d2be489
--- /dev/null
+++ b/gnu/usr.bin/grep/search.c
@@ -0,0 +1,481 @@
+/* search.c - searching subroutines using dfa, kwset and regex for grep.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Written August 1992 by Mike Haertel. */
+
+#include <ctype.h>
+
+#ifdef STDC_HEADERS
+#include <limits.h>
+#include <stdlib.h>
+#else
+#define UCHAR_MAX 255
+#include <sys/types.h>
+extern char *malloc();
+#endif
+
+#ifdef HAVE_MEMCHR
+#include <string.h>
+#ifdef NEED_MEMORY_H
+#include <memory.h>
+#endif
+#else
+#ifdef __STDC__
+extern void *memchr();
+#else
+extern char *memchr();
+#endif
+#endif
+
+#if defined(HAVE_STRING_H) || defined(STDC_HEADERS)
+#undef bcopy
+#define bcopy(s, d, n) memcpy((d), (s), (n))
+#endif
+
+#ifdef isascii
+#define ISALNUM(C) (isascii(C) && isalnum(C))
+#define ISUPPER(C) (isascii(C) && isupper(C))
+#else
+#define ISALNUM(C) isalnum(C)
+#define ISUPPER(C) isupper(C)
+#endif
+
+#define TOLOWER(C) (ISUPPER(C) ? tolower(C) : (C))
+
+#include "grep.h"
+#include "dfa.h"
+#include "kwset.h"
+#include "regex.h"
+
+#define NCHAR (UCHAR_MAX + 1)
+
+#if __STDC__
+static void Gcompile(char *, size_t);
+static void Ecompile(char *, size_t);
+static char *EGexecute(char *, size_t, char **);
+static void Fcompile(char *, size_t);
+static char *Fexecute(char *, size_t, char **);
+#else
+static void Gcompile();
+static void Ecompile();
+static char *EGexecute();
+static void Fcompile();
+static char *Fexecute();
+#endif
+
+/* Here is the matchers vector for the main program. */
+struct matcher matchers[] = {
+ { "default", Gcompile, EGexecute },
+ { "grep", Gcompile, EGexecute },
+ { "ggrep", Gcompile, EGexecute },
+ { "egrep", Ecompile, EGexecute },
+ { "posix-egrep", Ecompile, EGexecute },
+ { "gegrep", Ecompile, EGexecute },
+ { "fgrep", Fcompile, Fexecute },
+ { "gfgrep", Fcompile, Fexecute },
+ { 0, 0, 0 },
+};
+
+/* For -w, we also consider _ to be word constituent. */
+#define WCHAR(C) (ISALNUM(C) || (C) == '_')
+
+/* DFA compiled regexp. */
+static struct dfa dfa;
+
+/* Regex compiled regexp. */
+static struct re_pattern_buffer regex;
+
+/* KWset compiled pattern. For Ecompile and Gcompile, we compile
+ a list of strings, at least one of which is known to occur in
+ any string matching the regexp. */
+static kwset_t kwset;
+
+/* Last compiled fixed string known to exactly match the regexp.
+ If kwsexec() returns < lastexact, then we don't need to
+ call the regexp matcher at all. */
+static int lastexact;
+
+void
+dfaerror(mesg)
+ char *mesg;
+{
+ fatal(mesg, 0);
+}
+
+static void
+kwsinit()
+{
+ static char trans[NCHAR];
+ int i;
+
+ if (match_icase)
+ for (i = 0; i < NCHAR; ++i)
+ trans[i] = TOLOWER(i);
+
+ if (!(kwset = kwsalloc(match_icase ? trans : (char *) 0)))
+ fatal("memory exhausted", 0);
+}
+
+/* If the DFA turns out to have some set of fixed strings one of
+ which must occur in the match, then we build a kwset matcher
+ to find those strings, and thus quickly filter out impossible
+ matches. */
+static void
+kwsmusts()
+{
+ struct dfamust *dm;
+ char *err;
+
+ if (dfa.musts)
+ {
+ kwsinit();
+ /* First, we compile in the substrings known to be exact
+ matches. The kwset matcher will return the index
+ of the matching string that it chooses. */
+ for (dm = dfa.musts; dm; dm = dm->next)
+ {
+ if (!dm->exact)
+ continue;
+ ++lastexact;
+ if ((err = kwsincr(kwset, dm->must, strlen(dm->must))) != 0)
+ fatal(err, 0);
+ }
+ /* Now, we compile the substrings that will require
+ the use of the regexp matcher. */
+ for (dm = dfa.musts; dm; dm = dm->next)
+ {
+ if (dm->exact)
+ continue;
+ if ((err = kwsincr(kwset, dm->must, strlen(dm->must))) != 0)
+ fatal(err, 0);
+ }
+ if ((err = kwsprep(kwset)) != 0)
+ fatal(err, 0);
+ }
+}
+
+static void
+Gcompile(pattern, size)
+ char *pattern;
+ size_t size;
+{
+#ifdef __STDC__
+ const
+#endif
+ char *err;
+
+ re_set_syntax(RE_SYNTAX_GREP | RE_HAT_LISTS_NOT_NEWLINE);
+ dfasyntax(RE_SYNTAX_GREP | RE_HAT_LISTS_NOT_NEWLINE, match_icase);
+
+ if ((err = re_compile_pattern(pattern, size, &regex)) != 0)
+ fatal(err, 0);
+
+ dfainit(&dfa);
+
+ /* In the match_words and match_lines cases, we use a different pattern
+ for the DFA matcher that will quickly throw out cases that won't work.
+ Then if DFA succeeds we do some hairy stuff using the regex matcher
+ to decide whether the match should really count. */
+ if (match_words || match_lines)
+ {
+ /* In the whole-word case, we use the pattern:
+ (^|[^A-Za-z_])(userpattern)([^A-Za-z_]|$).
+ In the whole-line case, we use the pattern:
+ ^(userpattern)$.
+ BUG: Using [A-Za-z_] is locale-dependent! */
+
+ char *n = malloc(size + 50);
+ int i = 0;
+
+ strcpy(n, "");
+
+ if (match_lines)
+ strcpy(n, "^\\(");
+ if (match_words)
+ strcpy(n, "\\(^\\|[^0-9A-Za-z_]\\)\\(");
+
+ i = strlen(n);
+ bcopy(pattern, n + i, size);
+ i += size;
+
+ if (match_words)
+ strcpy(n + i, "\\)\\([^0-9A-Za-z_]\\|$\\)");
+ if (match_lines)
+ strcpy(n + i, "\\)$");
+
+ i += strlen(n + i);
+ dfacomp(n, i, &dfa, 1);
+ }
+ else
+ dfacomp(pattern, size, &dfa, 1);
+
+ kwsmusts();
+}
+
+static void
+Ecompile(pattern, size)
+ char *pattern;
+ size_t size;
+{
+#ifdef __STDC__
+ const
+#endif
+ char *err;
+
+ if (strcmp(matcher, "posix-egrep") == 0)
+ {
+ re_set_syntax(RE_SYNTAX_POSIX_EGREP);
+ dfasyntax(RE_SYNTAX_POSIX_EGREP, match_icase);
+ }
+ else
+ {
+ re_set_syntax(RE_SYNTAX_EGREP);
+ dfasyntax(RE_SYNTAX_EGREP, match_icase);
+ }
+
+ if ((err = re_compile_pattern(pattern, size, &regex)) != 0)
+ fatal(err, 0);
+
+ dfainit(&dfa);
+
+ /* In the match_words and match_lines cases, we use a different pattern
+ for the DFA matcher that will quickly throw out cases that won't work.
+ Then if DFA succeeds we do some hairy stuff using the regex matcher
+ to decide whether the match should really count. */
+ if (match_words || match_lines)
+ {
+ /* In the whole-word case, we use the pattern:
+ (^|[^A-Za-z_])(userpattern)([^A-Za-z_]|$).
+ In the whole-line case, we use the pattern:
+ ^(userpattern)$.
+ BUG: Using [A-Za-z_] is locale-dependent! */
+
+ char *n = malloc(size + 50);
+ int i = 0;
+
+ strcpy(n, "");
+
+ if (match_lines)
+ strcpy(n, "^(");
+ if (match_words)
+ strcpy(n, "(^|[^0-9A-Za-z_])(");
+
+ i = strlen(n);
+ bcopy(pattern, n + i, size);
+ i += size;
+
+ if (match_words)
+ strcpy(n + i, ")([^0-9A-Za-z_]|$)");
+ if (match_lines)
+ strcpy(n + i, ")$");
+
+ i += strlen(n + i);
+ dfacomp(n, i, &dfa, 1);
+ }
+ else
+ dfacomp(pattern, size, &dfa, 1);
+
+ kwsmusts();
+}
+
+static char *
+EGexecute(buf, size, endp)
+ char *buf;
+ size_t size;
+ char **endp;
+{
+ register char *buflim, *beg, *end, save;
+ int backref, start, len;
+ struct kwsmatch kwsm;
+ static struct re_registers regs; /* This is static on account of a BRAIN-DEAD
+ Q@#%!# library interface in regex.c. */
+
+ buflim = buf + size;
+
+ for (beg = end = buf; end < buflim; beg = end + 1)
+ {
+ if (kwset)
+ {
+ /* Find a possible match using the KWset matcher. */
+ beg = kwsexec(kwset, beg, buflim - beg, &kwsm);
+ if (!beg)
+ goto failure;
+ /* Narrow down to the line containing the candidate, and
+ run it through DFA. */
+ end = memchr(beg, '\n', buflim - beg);
+ if (!end)
+ end = buflim;
+ while (beg > buf && beg[-1] != '\n')
+ --beg;
+ save = *end;
+ if (kwsm.index < lastexact)
+ goto success;
+ if (!dfaexec(&dfa, beg, end, 0, (int *) 0, &backref))
+ {
+ *end = save;
+ continue;
+ }
+ *end = save;
+ /* Successful, no backreferences encountered. */
+ if (!backref)
+ goto success;
+ }
+ else
+ {
+ /* No good fixed strings; start with DFA. */
+ save = *buflim;
+ beg = dfaexec(&dfa, beg, buflim, 0, (int *) 0, &backref);
+ *buflim = save;
+ if (!beg)
+ goto failure;
+ /* Narrow down to the line we've found. */
+ end = memchr(beg, '\n', buflim - beg);
+ if (!end)
+ end = buflim;
+ while (beg > buf && beg[-1] != '\n')
+ --beg;
+ /* Successful, no backreferences encountered! */
+ if (!backref)
+ goto success;
+ }
+ /* If we've made it to this point, this means DFA has seen
+ a probable match, and we need to run it through Regex. */
+ regex.not_eol = 0;
+ if ((start = re_search(&regex, beg, end - beg, 0, end - beg, &regs)) >= 0)
+ {
+ len = regs.end[0] - start;
+ if (!match_lines && !match_words || match_lines && len == end - beg)
+ goto success;
+ /* If -w, check if the match aligns with word boundaries.
+ We do this iteratively because:
+ (a) the line may contain more than one occurence of the pattern, and
+ (b) Several alternatives in the pattern might be valid at a given
+ point, and we may need to consider a shorter one to find a word
+ boundary. */
+ if (match_words)
+ while (start >= 0)
+ {
+ if ((start == 0 || !WCHAR(beg[start - 1]))
+ && (len == end - beg || !WCHAR(beg[start + len])))
+ goto success;
+ if (len > 0)
+ {
+ /* Try a shorter length anchored at the same place. */
+ --len;
+ regex.not_eol = 1;
+ len = re_match(&regex, beg, start + len, start, &regs);
+ }
+ if (len <= 0)
+ {
+ /* Try looking further on. */
+ if (start == end - beg)
+ break;
+ ++start;
+ regex.not_eol = 0;
+ start = re_search(&regex, beg, end - beg,
+ start, end - beg - start, &regs);
+ len = regs.end[0] - start;
+ }
+ }
+ }
+ }
+
+ failure:
+ return 0;
+
+ success:
+ *endp = end < buflim ? end + 1 : end;
+ return beg;
+}
+
+static void
+Fcompile(pattern, size)
+ char *pattern;
+ size_t size;
+{
+ char *beg, *lim, *err;
+
+ kwsinit();
+ beg = pattern;
+ do
+ {
+ for (lim = beg; lim < pattern + size && *lim != '\n'; ++lim)
+ ;
+ if ((err = kwsincr(kwset, beg, lim - beg)) != 0)
+ fatal(err, 0);
+ if (lim < pattern + size)
+ ++lim;
+ beg = lim;
+ }
+ while (beg < pattern + size);
+
+ if ((err = kwsprep(kwset)) != 0)
+ fatal(err, 0);
+}
+
+static char *
+Fexecute(buf, size, endp)
+ char *buf;
+ size_t size;
+ char **endp;
+{
+ register char *beg, *try, *end;
+ register size_t len;
+ struct kwsmatch kwsmatch;
+
+ for (beg = buf; beg <= buf + size; ++beg)
+ {
+ if (!(beg = kwsexec(kwset, beg, buf + size - beg, &kwsmatch)))
+ return 0;
+ len = kwsmatch.size[0];
+ if (match_lines)
+ {
+ if (beg > buf && beg[-1] != '\n')
+ continue;
+ if (beg + len < buf + size && beg[len] != '\n')
+ continue;
+ goto success;
+ }
+ else if (match_words)
+ for (try = beg; len && try;)
+ {
+ if (try > buf && WCHAR((unsigned char) try[-1]))
+ break;
+ if (try + len < buf + size && WCHAR((unsigned char) try[len]))
+ {
+ try = kwsexec(kwset, beg, --len, &kwsmatch);
+ len = kwsmatch.size[0];
+ }
+ else
+ goto success;
+ }
+ else
+ goto success;
+ }
+
+ return 0;
+
+ success:
+ if ((end = memchr(beg + len, '\n', (buf + size) - (beg + len))) != 0)
+ ++end;
+ else
+ end = buf + size;
+ *endp = end;
+ while (beg > buf && beg[-1] != '\n')
+ --beg;
+ return beg;
+}
diff --git a/gnu/usr.bin/grep/tests/check.sh b/gnu/usr.bin/grep/tests/check.sh
new file mode 100644
index 0000000..d2c8fdb
--- /dev/null
+++ b/gnu/usr.bin/grep/tests/check.sh
@@ -0,0 +1,24 @@
+#! /bin/sh
+# Regression test for GNU grep.
+# Usage: regress.sh [testdir]
+
+testdir=${1-tests}
+
+failures=0
+
+# The Khadafy test is brought to you by Scott Anderson . . .
+./grep -E -f $testdir/khadafy.regexp $testdir/khadafy.lines > khadafy.out
+if cmp $testdir/khadafy.lines khadafy.out
+then
+ :
+else
+ echo Khadafy test failed -- output left on khadafy.out
+ failures=1
+fi
+
+# . . . and the following by Henry Spencer.
+
+${AWK-awk} -F: -f $testdir/scriptgen.awk $testdir/spencer.tests > tmp.script
+
+sh tmp.script && exit $failures
+exit 1
diff --git a/gnu/usr.bin/grep/tests/khadafy.lines b/gnu/usr.bin/grep/tests/khadafy.lines
new file mode 100644
index 0000000..57e21a1
--- /dev/null
+++ b/gnu/usr.bin/grep/tests/khadafy.lines
@@ -0,0 +1,32 @@
+1) Muammar Qaddafi
+2) Mo'ammar Gadhafi
+3) Muammar Kaddafi
+4) Muammar Qadhafi
+5) Moammar El Kadhafi
+6) Muammar Gadafi
+7) Mu'ammar al-Qadafi
+8) Moamer El Kazzafi
+9) Moamar al-Gaddafi
+10) Mu'ammar Al Qathafi
+11) Muammar Al Qathafi
+12) Mo'ammar el-Gadhafi
+13) Moamar El Kadhafi
+14) Muammar al-Qadhafi
+15) Mu'ammar al-Qadhdhafi
+16) Mu'ammar Qadafi
+17) Moamar Gaddafi
+18) Mu'ammar Qadhdhafi
+19) Muammar Khaddafi
+20) Muammar al-Khaddafi
+21) Mu'amar al-Kadafi
+22) Muammar Ghaddafy
+23) Muammar Ghadafi
+24) Muammar Ghaddafi
+25) Muamar Kaddafi
+26) Muammar Quathafi
+27) Muammar Gheddafi
+28) Muamar Al-Kaddafi
+29) Moammar Khadafy
+30) Moammar Qudhafi
+31) Mu'ammar al-Qaddafi
+32) Mulazim Awwal Mu'ammar Muhammad Abu Minyar al-Qadhafi
diff --git a/gnu/usr.bin/grep/tests/khadafy.regexp b/gnu/usr.bin/grep/tests/khadafy.regexp
new file mode 100644
index 0000000..46fe8dd
--- /dev/null
+++ b/gnu/usr.bin/grep/tests/khadafy.regexp
@@ -0,0 +1 @@
+M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy]
diff --git a/gnu/usr.bin/grep/tests/scriptgen.awk b/gnu/usr.bin/grep/tests/scriptgen.awk
index cdadc91..44ef4df 100644
--- a/gnu/usr.bin/grep/tests/scriptgen.awk
+++ b/gnu/usr.bin/grep/tests/scriptgen.awk
@@ -1,6 +1,6 @@
BEGIN { print "failures=0"; }
-!/^#/ && NF == 3 {
- print "echo '" $3 "' | $1/egrep -e '" $2 "' > /dev/null 2>&1";
+$0 !~ /^#/ && NF == 3 {
+ print "echo '" $3 "' | ./grep -E -e '" $2 "' > /dev/null 2>&1";
print "if [ $? != " $1 " ]"
print "then"
printf "\techo Spencer test \\#%d failed\n", ++n
diff --git a/gnu/usr.bin/grep/tests/spencer.tests b/gnu/usr.bin/grep/tests/spencer.tests
index c7eee9b..913f198 100644
--- a/gnu/usr.bin/grep/tests/spencer.tests
+++ b/gnu/usr.bin/grep/tests/spencer.tests
@@ -33,7 +33,7 @@
0:a[b-d]e:ace
0:a[b-d]:aac
0:a[-b]:a-
-2:a[b-]:a-
+0:a[b-]:a-
1:a[b-a]:-
2:a[]b:-
2:a[:-
diff --git a/gnu/usr.bin/gzip/ChangeLog b/gnu/usr.bin/gzip/ChangeLog
index 5238071..7dd0f86 100644
--- a/gnu/usr.bin/gzip/ChangeLog
+++ b/gnu/usr.bin/gzip/ChangeLog
@@ -1,3 +1,140 @@
+Wed Aug 18 09:34:23 1993 Jean-loup Gailly (jloup@chorus.fr)
+
+ * version 1.2.4
+ By default, do not restore file name and timestamp from those saved
+ inside the .gz file (behave as 'compress'). Added the --name option
+ to force name and timestamp restoration.
+ Accept - as synonym for stdin.
+ Use manlinks=so or ln to support either hard links or .so in man pages
+ Accept foo.gz~ in zdiff.
+ Added support for Windows NT
+ Handle ENAMETOOLONG for strict Posix systems
+ Use --recursive instead of --recurse to comply with Webster and
+ the GNU stdandard.
+ Allow installation of shell scripts with a g prefix: make G=g install
+ Install by default zcat as gzcat if gzcat already exists in path.
+ Let zmore behave as more when invoked without parameters (give help)
+ Let gzip --list reject files not in gzip format even with --force.
+ Don't complain about non gzip files for options -rt or -rl.
+ Added advice in INSTALL for several systems.
+ Added makefile entries for NeXTstep 3.1 (if configure fails)
+ Avoid problem with memcpy on Pyramid (gave crc error on some files)
+ Support the -r option when compiled with Borland C++ on msdos.
+ Force lower case file names only for FAT file systems (not HPFS)
+ Rewrite one expression in inflate.c to avoid cc bug on Solaris x86.
+ In the msdos makefiles, get match.asm from the msdos subdirectory.
+ Catch SIGTERM and SIGHUP only if they are not ignored.
+ getopt.c: on Amiga, "#if !defined(const)" does not compile.
+ Use register parameters on Amiga.
+ Do not force names to lower case on Amiga.
+ Fix support of Atari TOS (Makefile.st and tailor.h)
+ In unlzw.c, do not suggest using zcat if zcat already used.
+ In INSTALL, suggest using bsdinst for HPUX.
+ Document Turbo C++ 1.0 bug in INSTALL.
+ Improved the documentation relative to the --no-name option.
+ Avoid signed/unsigned warnings in several files.
+ Added pointer to jka-compr19.el in README.
+ Added pointer to OS/2 executables in README.
+ Added --block-compress in tar -z example (gzip.1 and gzip.texi).
+ Don't keep rcsid in executable (avoid compilation warnings).
+ Check also the correctness of the first byte of an .Z file.
+ Return non zero status for an invalid option.
+ Remove "NEWFILES" from os2/gzip.def for Borland C++ on OS/2.
+ Remove "time stamp restored" message (just obey the -N request).
+
+Thu Jun 24 10:27:57 1993 Jean-loup Gailly (jloup@chorus.fr)
+
+ * version 1.2.3
+ Don't display the output name when decompressing except with --verbose.
+ Remove usage of alloca in getopt.c and all makefiles.
+ Use ASCPP instead of CPP to avoid breaking AC_HEADER_CHECK on RiscOS.
+ Added the zfile shell script in subdirectory sample.
+ Moved the list of compiler bugs from README to INSTALL.
+ Added vms/Readme.vms.
+ Fix DIST_BUFSIZE check in unlzh.c for 16 bit machines.
+ Fix REGSIGTYP macro in configure.in.
+ Use 'define' instead of == in vms/gzip.hlp.
+ Avoid warnings in unlzh.c
+ Allow separate installation of binaries and man pages.
+ Simplified handling of file names with spaces in zgrep and znew.
+ Fix dependencies and remove rule for trees.c in amiga/Makefile.sasc
+ Add missing quote in gzexe.
+
+Thu Jun 17 13:47:05 1993 Jean-loup Gailly (jloup@chorus.fr)
+
+ * version 1.2.2
+ Fix a compilation error in gzip.c on Sun with cc (worked with gcc).
+
+Wed Jun 16 11:20:27 1993 Jean-loup Gailly (jloup@chorus.fr)
+
+ * version 1.2.1
+ Let zmore act as more if the data is not gzipped.
+ By default, display output name only when name was actually truncated.
+ Use absolute path names in gzexe'd programs for better security.
+ In gzexe, use chmod 700 instead of 755 and don't gzexe tail,rm,etc...
+ Update vms/gzip.hlp.
+ Added a note about the fast options (-1 to -3) in algorithm.doc.
+ Improved man page for zgrep.
+ Minor fixes to gzip.texi.
+ Always set LC_ALL and LANG in configure (for tr on HPUX)
+
+Mon Jun 14 10:03:24 1993 Jean-loup Gailly (jloup@chorus.fr)
+
+ * version 1.2
+ Added the --list option to display the file characteristics.
+ Added the --no-name option: do not save or restore original filename
+ Save the original name by default.
+ Allow gunzip --suffix "" to attempt decompression on any file
+ regardless of its extension if an original name is present.
+ Add support for the SCO compress -H format.
+ gzip --fast now compresses faster (speed close to that of compress)
+ with degraded compression ratio (but still better than compress).
+ Default level changed to -6 (acts exactly as previous level -5) to
+ be a better indication of its placement in the speed/ratio range.
+ Use smart name truncation: 123456789012.c -> 123456789.c.gz
+ instead of 12345678901.gz
+ With --force, let zcat pass non gzip'ed data unchanged (zcat == cat)
+ Added the zgrep shell script.
+ Made sub.c useful for 16 bit sound, 24 bit images, etc..
+ Supress warnings about suffix for gunzip -r, except with --verbose.
+ Moved the sample programs to a subdirectory sample.
+ On MSDOS, use .gz extension when possible (files without extension)
+ Added a "Special targets" section in INSTALL.
+ Use stty -g correctly in zmore.in.
+ Use cheaper test for gzipness in zforce.in.
+ Remove space before $ in match.S (no longer accepted by gas 2.x)
+ For the shell scripts, do not assume that gzip is in the path.
+ Fix syntax error and define lnk$library in vms/Makefile.mms
+ REGSIGTYPE is void on the Amiga.
+ Do not write empty line when decompressing stdin with --verbose.
+ Fix the 1.1.2 fix for VMS (bug in get_suffix)
+ Added warning in README about compiler bug on Solaris 2.1 for x86.
+ Added warning about 'rehash' in INSTALL.
+ Removed default value of read_buf in bits.c (supermax doesn't like).
+ In tailor.h, added support for Borland C and Zortech C on OS/2.
+ Added warning in gzexe about Ultrix buggy sh (use /bin/sh5 instead).
+ Added warning in zdiff about AIX buggy sh (use /bin/ksh instead).
+ In configure.in, do not try the asm code if DEFS contains NO_ASM
+
+Fri Jun 4 09:49:33 1993 Jean-loup Gailly (jloup@chorus.fr)
+
+ * version 1.1.2
+ Fix serious bug for VMS (-gz not removed when decompressing).
+ Allow suffix other than .gz in znew.
+ Do not display compression ratio when decompressing stdin.
+ In zmore.in, work around brain damaged stty -g (Ultrix).
+ Display a correct compression ratio for .Z files.
+ Added .z to .gz renaming script in INTALL.
+ Allow setting CFLAGS in configure.
+ Add warning in README about bug in Concentrix cc compiler.
+ Avoid || in Makefile.in (at least one make doesn't support this).
+ Disable useless --ascii option for the Amiga.
+ Add a pointer to the Primos executable in README.
+ Added description of extra field in algorithm.doc.
+ Do not redefine NULL in alloca.c.
+ Added check for unsupported compression methods.
+ Avoid getopt redeclaration on OSF/1.
+
Tue Jun 1 09:07:15 1993 Jean-loup Gailly (jloup@chorus.fr)
* version 1.1.1
diff --git a/gnu/usr.bin/gzip/Makefile b/gnu/usr.bin/gzip/Makefile
index 36200b8..e6f8652 100644
--- a/gnu/usr.bin/gzip/Makefile
+++ b/gnu/usr.bin/gzip/Makefile
@@ -2,18 +2,19 @@
PROG= gzip
SRCS= gzip.c zip.c deflate.c trees.c bits.c unzip.c inflate.c util.c \
- crypt.c lzw.c unlzw.c unpack.c getopt.c match.S
-MAN1= gzexe.0 gzip.0 zdiff.0 zforce.0 zmore.0 znew.0
-CFLAGS+=-DASMV -DHAVE_UNISTD_H=1 -DDIRENT=1
-MLINKS= zdiff.1 zcmp.1 gzip.1 gunzip.1 gzip.1 zcat.1 gzip.1 gzcat.1
-LINKS+= ${DESTDIR}${BINDIR}/gzip ${DESTDIR}${BINDIR}/gunzip
-LINKS+= ${DESTDIR}${BINDIR}/gzip ${DESTDIR}${BINDIR}/gzcat
-LINKS+= ${DESTDIR}${BINDIR}/gzip ${DESTDIR}${BINDIR}/zcat
+ crypt.c lzw.c unlzw.c unlzh.c unpack.c getopt.c match.S
+MAN1= gzexe.1 gzip.1 zdiff.1 zforce.1 zmore.1 znew.1
+CFLAGS+=-DASMV -DSTDC_HEADERS=1 -DHAVE_UNISTD_H=1 -DDIRENT=1
+MLINKS= gzip.1 gunzip.1 gzip.1 zcat.1 gzip.1 gzcat.1
+LINKS+= ${BINDIR}/gzip ${BINDIR}/gunzip
+LINKS+= ${BINDIR}/gzip ${BINDIR}/gzcat
+LINKS+= ${BINDIR}/gzip ${BINDIR}/zcat
+NOSHARED=yes
afterinstall:
install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
${.CURDIR}/zforce ${.CURDIR}/gzexe ${.CURDIR}/znew \
- ${.CURDIR}/zmore ${.CURDIR}/zdiff ${.CURDIR}/zcmp \
+ ${.CURDIR}/zmore ${.CURDIR}/zdiff ${.CURDIR}/zgrep \
${DESTDIR}${BINDIR}
match.o: ${.CURDIR}/match.S
diff --git a/gnu/usr.bin/gzip/NEWS b/gnu/usr.bin/gzip/NEWS
index 097f7b1..ef5f833 100644
--- a/gnu/usr.bin/gzip/NEWS
+++ b/gnu/usr.bin/gzip/NEWS
@@ -1,6 +1,69 @@
-Current Version: 1.1.1
+Current Version: 1.2.4.
See the file ChangeLog for the details of all changes.
+Major changes from 1.2.3 to 1.2.4
+* By default, do not restore file name and timestamp from those saved
+ inside the .gz file (behave as 'compress'). Added the --name option
+ to force name and timestamp restoration.
+* Accept - as synonym for stdin.
+* Use manlinks=so or ln to support either hard links or .so in man pages
+* Accept foo.gz~ in zdiff.
+* Added support for Windows NT
+* Handle ENAMETOOLONG for strict Posix systems
+* Use --recursive instead of --recurse to comply with Webster and
+ the GNU stdandard.
+* Allow installation of shell scripts with a g prefix: make G=g install
+* Install by default zcat as gzcat if gzcat already exists in path.
+* Let zmore behave as more when invoked without parameters (give help)
+* Let gzip --list reject files not in gzip format even with --force.
+* Don't complain about non gzip files for options -rt or -rl.
+* Added advice in INSTALL for several systems.
+
+Major changes from 1.2.2 to 1.2.3
+* Don't display the output name when decompressing except with --verbose.
+* Remove usage of alloca in getopt.c and all makefiles.
+* Added the zfile shell script in subdirectory sample.
+* Moved the list of compiler bugs from README to INSTALL.
+* Added vms/Readme.vms.
+
+Major changes from 1.2.1 to 1.2.2
+* Fix a compilation error on Sun with cc (worked with gcc).
+
+Major changes from 1.2 to 1.2.1
+* Let zmore act as more if the data is not gzipped.
+* made gzexe more secure (don't rely on PATH).
+* By default, display output name only when the name was actually truncated.
+
+Major changes from 1.1.2 to 1.2
+* Added the --list option to display the file characteristics.
+* Added the --no-name option: do not save or restore original filename
+ Save the original name by default.
+* Allow gunzip --suffix "" to attempt decompression on any file
+ regardless of its extension if an original name is present.
+* Add support for the SCO compress -H format.
+* gzip --fast now compresses faster (speed close to that of compress)
+ with degraded compression ratio (but still better than compress).
+ Default level changed to -6 (acts exactly as previous level -5) to
+ be a better indication of its placement in the speed/ratio range.
+* Use smart name truncation: 123456789012.c -> 123456789.c.gz
+ instead of 12345678901.gz
+* With --force, let zcat pass non gzip'ed data unchanged (zcat == cat)
+* Added the zgrep shell script.
+* Made sub.c useful for 16 bit sound, 24 bit images, etc..
+* Supress warnings about suffix for gunzip -r, except with --verbose.
+* On MSDOS, use .gz extension when possible (files without extension)
+* Moved the sample programs to a subdirectory sample.
+* Added a "Special targets" section in INSTALL.
+
+Major changes from 1.1.1 to 1.1.2.
+* Fix serious bug for VMS (-gz not removed when decompressing).
+* Allow suffix other than .gz in znew.
+* Do not display compression ratio when decompressing stdin.
+* In zmore.in, work around brain damaged stty -g (Ultrix).
+* Display a correct compression ratio for .Z files.
+* Added .z to .gz renaming script in INTALL.
+* Allow setting CFLAGS in configure.
+
Major changes from 1.1 to 1.1.1.
* Fix serious bug in vms.c (affects Vax/VMS only).
* Added --ascii option.
diff --git a/gnu/usr.bin/gzip/README b/gnu/usr.bin/gzip/README
index 152b7e5..fdd7311 100644
--- a/gnu/usr.bin/gzip/README
+++ b/gnu/usr.bin/gzip/README
@@ -1,4 +1,4 @@
-This is the file README for the gzip distribution, version 1.1.1.
+This is the file README for the gzip distribution, version 1.2.4.
gzip (GNU zip) is a compression utility designed to be a replacement
for 'compress'. Its main advantages over compress are much better
@@ -32,28 +32,12 @@ INSTALL for installation instructions. Some answers to frequently
asked questions are given in the file INSTALL, please read it. (In
particular, please don't ask me once more for an /etc/magic entry.)
-WARNINGS about broken optimizers:
-
-- on the NeXT, "cc -finline-functions" is broken. gzip produces
- valid .z files but they are much too large because the string
- matching code misses most matches. Use "cc -O" instead.
-
-- on the Mips R4000, gcc -O (version 2.3.1) generates bad code, use cc
- or just gcc -g instead.
-
-- gcc 2.3.3 on the SGI Indigo IRIX 4.0.5 also produces bad code. Use
- instead: make CC='cc -O2' or gcc without -O.
-
-- on Sparc with SunOS 4.1.1 and the SC1.0 compiler, the optimizer
- works up to -O3 but -O4 does not work.
-
-- MSC 5.1 with -Ox and -DDYN_ALLOC generates bad code in inflate.c.
- The default is static allocation (no DYN_ALLOC) and -Ox works on inflate.c.
- But -Ox does not work on util.c, so you must use -Oait -Gs.
-
-- On dnix 5.3 2.2 cc version 2.37c is buggy. Version 2.38d works.
-
-For all machines, Use "make check" to check that gzip was compiled correctly.
+WARNING: on several systems, compiler bugs cause gzip to fail, in
+particular when optimization options are on. See the section "Special
+targets" at the end of the INSTALL file for a list of known problems.
+For all machines, use "make check" to check that gzip was compiled
+correctly. Try compiling gzip without any optimization if you have a
+problem.
Please send all comments and bug reports by electronic mail to:
Jean-loup Gailly <jloup@chorus.fr>
@@ -68,23 +52,27 @@ Bug reports should ideally include:
* A description of the bug behavior
* The input to gzip, that triggered the bug
+If you send me patches for machines I don't have access to, please test them
+very carefully. gzip is used for backups, it must be extremely reliable.
+
The package crypt++.el is highly recommended to manipulate gzip'ed
file from emacs. It recognizes automatically encrypted and compressed
files when they are first visited or written. It is available via
anonymous ftp to roebling.poly.edu [128.238.5.31] in /pub/crypt++.el.
The same directory contains also patches to dired, ange-ftp and info.
-GNU tar 1.11.2 has a -z option to invoke directly gzip, so you don't have
-to patch it.
+GNU tar 1.11.2 has a -z option to invoke directly gzip, so you don't have to
+patch it. The package ftp.uu.net:/languages/emacs-lisp/misc/jka-compr19.el.Z
+also supports gzip'ed files.
The znew and gzexe shell scripts provided with gzip benefit from
(but do not require) the cpmod utility to transfer file attributes.
It is available by anonymous ftp on gatekeeper.dec.com in
/.0/usenet/comp.sources.unix/volume11/cpmod.Z.
-The sample programs zread.c, sub.c and add.c are provided as examples
-of useful complements to gzip. Read the comments inside each source file.
-The perl script ztouch is also provided as example (not installed
-by default since it relies on perl).
+The sample programs zread.c, sub.c and add.c in subdirectory sample
+are provided as examples of useful complements to gzip. Read the
+comments inside each source file. The perl script ztouch is also
+provided as example (not installed by default since it relies on perl).
gzip is free software, you can redistribute it and/or modify it under
@@ -93,13 +81,22 @@ provided under the name COPYING. The latest version of gzip are always
available by ftp in prep.ai.mit.edu:/pub/gnu, or in any of the prep
mirror sites:
-- sources in gzip-*.tar (or .shar or .tar.z)
+- sources in gzip-*.tar (or .shar or .tar.gz).
- Solaris 2 executables in sparc-sun-solaris2/gzip-binaries-*.tar
- MSDOS lha self-extracting exe in gzip-msdos-*.exe. Once extracted,
copy gzip.exe to gunzip.exe and zcat.exe, or use "gzip -d" to decompress.
+ gzip386.exe runs much faster but only on 386 and above; it is compiled with
+ djgpp 1.10 available in directory omnigate.clarkson.edu:/pub/msdos/djgpp.
A VMS executable is available in ftp.spc.edu:[.macro32.savesets]gzip-1-*.zip
-(use [.macro32]unzip.exe to extract).
+(use [.macro32]unzip.exe to extract). A PRIMOS executable is available
+in ftp.lysator.liu.se:/pub/primos/run/gzip.run.
+OS/2 executables (16 and 32 bits versions) are available in
+ftp.tu-muenchen.de:/pub/comp/os/os2/archiver/gz*-[16,32].zip
+
+Some ftp servers can automatically make a tar.Z from a tar file. If
+you are getting gzip for the first time, you can ask for a tar.Z file
+instead of the much larger tar file.
Many thanks to those who provided me with bug reports and feedback.
See the files THANKS and ChangeLog for more details.
@@ -129,11 +126,11 @@ On Unix, gzip is mostly useful in combination with tar. GNU tar
1.11.2 has a -z option to invoke gzip automatically. "tar -z"
compresses better than zip, since gzip can then take advantage of
redundancy between distinct files. The drawback is that you must
-scan the whole tar.z file in order to extract a single file near
+scan the whole tar.gz file in order to extract a single file near
the end; unzip can directly seek to the end of the zip file. There
is no overhead when you extract the whole archive anyway.
If a member of a .zip archive is damaged, other files can still
-be recovered. If a .tar.z file is damaged, files beyond the failure
+be recovered. If a .tar.gz file is damaged, files beyond the failure
point cannot be recovered. (Future versions of gzip will have
error recovery features.)
diff --git a/gnu/usr.bin/gzip/THANKS b/gnu/usr.bin/gzip/THANKS
index 5c9a47b..6a545cb 100644
--- a/gnu/usr.bin/gzip/THANKS
+++ b/gnu/usr.bin/gzip/THANKS
@@ -1,9 +1,11 @@
-gzip was written by Jean-loup Gailly <jloup@chorus.fr>, with
-portions written by Mark Adler (inflate.c) and Peter Jannesen
-(unlzw.c). The zip deflate format was defined by Phil Katz.
-Thanks to those who reported problems and suggested
-various improvements. Here is a partial list of them:
+gzip was written by Jean-loup Gailly <jloup@chorus.fr>, with portions
+written by Mark Adler (inflate.c), Peter Jannesen (unlzw.c) and
+Haruhiko Okumura (unlzh.c). The zip deflate format was defined by Phil Katz.
+Thanks to those who reported problems and suggested various
+improvements. Here is a partial list of them:
+Robert Abramovitz bromo@cougar.tandem.com
+Jay Adams jka@ece.cmu.edu
Mark Adler madler@cco.caltech.edu
Edwin Allum edwin@csri.toronto.edu
Joseph Arceneaux jla@gnu.ai.mit.edu
@@ -12,6 +14,7 @@ Ken-ichiro Aoki aoki@madonna.physics.ucla.edu
David Ascher da@marlowe.cog.brown.edu
Eric Backus ericb@lsid.hp.com
Becky A. Badgett badgett@cs.utexas.edu
+Bo Nygaard Bai bai@iesd.auc.dk
Dave Barber dbarber@apocalypse.bbn.com
Rene Beaulieu reneb@distri.hydro.qc.ca
Neal Becker neal@ctd.comsat.com
@@ -19,36 +22,53 @@ Dieter Becker becker@med-in.uni-sb.de
Nelson H. F. Beebe beebe@geronimo.math.utah.edu
Jeff Beadles jeff@onion.rain.com
David J. N. Begley dbegley@st.nepean.uws.edu.au
+Bob Beresh rberesh@rd.hydro.on.ca
Jim Bernard jbernard@iola.mines.colorado.edu
Karl Berry karl@cs.umb.edu
James W. Birdsall jwbirdsa@picarefy.picarefy.com
+Scott Bolte scott@craycos.com
Wayne E. Bouchard web@paladine.hacks.arizona.edu
Marc Boucher marc@cam.org
Ola Brahammar pt90ob@pt.hk-r.se
Dave Brennan brennan@hal.com
Alan Brown dogbowl@dogbox.acme.gen.nz
+Michael L. Brown brown@wi.extrel.com
Rodney Brown rdb@mel.cocam.oz.au
Bruce bde@runx.oz.au
+Bill Bumgarner bbum@stone.com
Leila Burrell-Davis leilabd@syma.sussex.ac.uk
Roger Butenuth butenuth@ira.uka.de
+Jon Cargille jcargill@cs.wisc.edu
Bud Carlson bud@isle.pegasus.com
Lim Fung Chai fclim@i1sin.daq.semi.harris.com
Wes Chalfant wes@kofax.com
+Andrew A. Chernov ache@astral.msk.su
Paul Close pdc@lunch.wpd.sgi.com
+Jeff Coffler coffler@jac.enet.dec.com
+Will Colley wcc3@occs.cs.oberlin.edu
+Roger Cornelius sherpa!rac@uunet.uu.net
Kevin Cosgrove kevinc@tekig6.pen.tek.com
Stephen J Cowley s.j.cowley@amtp.cam.ac.uk
+Ron Cox roncox@indirect.com
Frank Crawford frank@photon.ansto.gov.au
James R. Crawford qralston@cislabs.pitt.edu
Lawrence Crowl crowl@research.cs.orst.edu
Klaus Dahlenburg kdburg@incoahe.hanse.de
William E Davidsen davidsen@ariel.crd.ge.com
+John M. DeDourek dedourek@aixive2.cs.unb.ca
Jeff Deifik jdeifik@isi.edu
Vince DeMarco vince@whatnxt.cuc.ab.ca
Michael De La Rue p91152@cplab.physics.edinburgh.ac.uk
+Jeff Delinck delinck@pa621a.inland.com
+John DeRoo deroo@grout.adv.shr.dec.com
+Jim Diamond zsd@axe.drea.dnd.ca
+Stefano Diomedi sd@teculx.tecsiel.it
Lawrence R. Dodd dodd@roebling.poly.edu
Matthew Donadio donadio@mxd120.rh.psu.edu
Andy Dougherty andy@crystal.phys.lafayette.edu
+Darrell Duane dduane@mason1.gmu.edu
John Eaton jwe@che.utexas.edu
+Will Edgington wedgingt@ptolemy.arc.nasa.gov
Brian Edmonds edmonds@edmonds.home.cs.ubc.ca
Paul Eggert eggert@twinsun.com
Enami enami@sys.ptg.sony.co.jp
@@ -58,12 +78,16 @@ Rik Faith faith@cs.unc.edu
Larry Fahnoe fahnoe@c1mpls.mn.org
Cristian Ferretti cfs@poincare.mat.puc.cl
Karl-Jose Filler pla_jfi@pki-nbg.philips.de
+Valery Fine fine@vxcern.cern.ch
+Bob Fischer bobf@milne.geology.yale.edu
Per Foreby perf@efd.lth.se
Alexander Fraser alex@cs.umb.edu
Noah Friedman friedman@gnu.ai.mit.edu
Bob Friesenhahn bfriesen@iphase.com
+Gerhard Friesland-Koepke frieslan@rzdspc3.informatik.uni-hamburg.de
Andy Fyfe andy@scp.caltech.edu
Geoff geoff@frs.faxon.com
+Arnd Gerns gerns@informatik.uni-hildesheim.de
Kaveh R. Ghazi ghazi@staccato.rutgers.edu
Torbjorn Granlund tege@sics.se
Carl Greco cgreco@parrot.creighton.edu
@@ -71,68 +95,106 @@ Bruno Haible haible@ma2s2.mathematik.uni-karlsruhe.de
Junio Hamano junio@shadow.twinsun.com
Harald Hanche-Olsen hanche@ams.sunysb.edu
Darrel R. Hankerson hankedr@mail.auburn.edu
+Mark Hanning-Lee markhl@romeo.caltech.edu
+Lars Hecking st000002@hrz1.hrz.th-darmstadt.de
Ruediger Helsch ruediger@ramz.ing.tu-bs.de
Mark C. Henderson mch@sqwest.wimsey.bc.ca
Karl Heuer karl@kelp.boston.ma.us
+Jarkko Hietaniemi jhi@dol-guldur.hut.fi
Thomas Hiller hiller@fzi.de
Eiji Hirai hirai@cc.swarthmore.edu
Kjetil Torgrim Homme kjetilho@ifi.uio.no
+Robert D. Houk rdh@sli.com
+Jim Howard jim_howard@mentorg.com
Preston Hunt gt5708a@prism.gatech.edu
Shane C Hutchins sch@nymph.msel.unh.edu
Hutch hutchinson@wrair-emh1.army.mil
Lester Ingber ingber@alumni.caltech.edu
Ken Ishii ishii@sni-usa.com
Per Steinar Iversen iversen@vsfys1.fi.uib.no
+Chris Jacobsen jacobsen@xray1.physics.sunysb.edu
Michal Jaegermann ntomczak@vm.ucs.ualberta.ca
Brian Jones brianj@skat.usc.edu
Denny de Jonge witaddj@dutrex.tudelft.nl
Arne H. Juul arnej@lise.unit.no
Dana Jacobsen jacobsd@solar.cor2.epa.gov
Peter Jannesen peter@ncs.nl
+Brian D. Johnston johnstonb@med.ge.com
+Walter W. Jones wwj@candela.cfr.nist.gov
+Tom Judson judson@scf.usc.edu
+Henry G. Juengst juengst@saph2.physik.uni-bonn.de
Sarantos Kapidakis sarantos%manteion@ics.forth.gr
Amir J. Katz amir@matis.ingr.com
Steve Kelem kelem@castor.xilinx.com
+Steven Kimball kimball@shrew.sanders.lockheed.com
Randy Kirchhof rkk@posms.aus.tx.us
Ned Kittlitz kittlitz@seagoon.sw.stratus.com
+Sakai Kiyotaka ksakai@mtl.t.u-tokyo.ac.jp
+Philip C Kizer pckizer@gonzo.tamu.edu
Pete Klammer pklammer@ouray.denver.colorado.edu
Fritz Kleemann kleemann@informatik.uni-wuerzburg.dbp.de
+Wilhelm B. Kloke wb@ifado.arb-phys.uni-dortmund.de
Tom Kloos tk@sequent.com
Carsten Koch carsten.koch@icem.de
Winfried Koenig win@in.rhein-main.de
+Mathias Koerber mathias@solomon.technet.sg
Steph Konigsdorfer s.konigsdorfer@frmy.bull.fr
+Leif Kornstaedt leif@rumtifsl.ruessel.sub.org
Michael D. Lawler mdlawler@bsu-cs.bsu.edu
Kevin Layer layer@franz.com
Howard D. Leadmon howardl@wb3ffv.ampr.org
Alexander Lehmann alex@hal.rhein-main.de
Simon Leinen simon@lia.di.epfl.ch
+Burt Leland burt@molecular.com
+Tony Leneis tony@plaza.adp.ds.com
Hugues Leroy hugues.leroy@irisa.fr
+Marty Leisner leisner@eso.mc.xerox.com
Charles Levert charles@aramis.comm.polymtl.ca
+Richard Levitte levitte@e.kth.se
Torbj|rn Lindh toobii@elixir.e.kth.se
David R. Linn drl@vuse.vanderbilt.edu
Antonio Lioy cat@athena.polito.it
Jamie Lokier u90jl@ecs.oxford.ac.uk
+Richard Lloyd R.K.Lloyd@csc.liv.ac.uk
David J. MacKenzie djm@eng.umd.edu
John R MacMillan john@chance.gts.org
Ron Male male@eso.mc.xerox.com
+Don R. Maszle maze@bea.lbl.gov
+Jaye Mathisen osyjm@cs.montana.edu
+Telly Mavroidis mavroidi@acf2.nyu.edu
+Imed Eddine Mbarki mbarki@pacific.cmpe.psu.edu
Steeve McCauley steeve@pooh.geophys.mcgill.ca
+Tom McConnell tmcconne@sedona.intel.com
Tod McQuillin mcquill@ccit05.duq.edu
Tye McQueen tye@spillman.com
Bernd Melchers melchers@chemie.fu-berlin.de
Jason Merrill jason@jarthur.claremont.edu
Dean S. Messing deanm@medulla.labs.tek.com
+M. Mesturino mesturino@cselt.stet.it
Luke Mewburn zak@rmit.edu.au
Jim Meyering meyering@cs.utexas.edu
-Frederic Miserey miserey@laguna.ics.uci.edu
+Dragan Milicic milicic@math.utah.edu
+Frederic Miserey none.fred@applelink.apple.com
Marcel J.E. Mol marcel@duteca.et.tudelft.nl
+Soren Juul Moller sjm@dde.dk
Chris Moore moore@src.bae.co.uk
+Dan Mosedale mosedale@genome.stanford.edu
Helmut Muelner hmuelner@fiicmds04.tu-graz.ac.at
Urban D Mueller umueller@amiga.physik.unizh.ch
+Ulrich Mueller ulm@vsnhdb.cern.ch
Timothy Murphy tim@maths.tcd.ie
Greg Naber greg@squally.halcyon.com
+Jay Nayegandhi jayng@bbiv02.enet.dec.com
+Paul K. Neville II pkn2@idsi.com
Karl L. Noell noell@informatik.fh-wiesbaden.dbp.de
+Demizu Noritoshi nori-d@is.aist-nara.ac.jp
+Todd Ogasawara todd@protege.pegasus.com
+Helge Oldach helge.oldach@stollmann.de
Arthur David Olson ado@elsie.nci.nih.gov
Piet van Oostrum piet@cs.ruu.nl
Rafael R. Pappalardo rafapa@obelix.cica.es
+Mike Pearlman canuck@masc38.rice.edu
+Yves Perrenoud pyves@nuga.alphanet.ch
Hal Peterson hrp@pecan.cray.com
Pascal Petit petit@cadillac.ibp.fr
Bruno Pillard bp@chorus.fr
@@ -141,12 +203,17 @@ Jay Pinkos pinkos@butyng.bu.edu
Thomas Plass thomas@cogsci.ed.ac.uk
Mike Polo mikep@cfsmo.honeywell.com
Francesco Potorti pot@fly.cnuce.cnr.it
+Will Priest bpriest@lobby.ti.com
David Purves purves@apogee.com
Andreas Raab ar@nvmr.robin.de
Eric S. Raymond esr@snark.thyrsus.com
Klaus Reimann kr@cip.physik.uni-stuttgart.de
Michael Rendell michael@mercury.cs.mun.ca
+Hal Render render@massive.uccs.edu
+Julian F. Reschke julian@math.uni-muenster.de
+Phil Richards Phil.Richards@prg.oxford.ac.uk
Roland B Roberts roberts@nsrl31.nsrl.rochester.edu
+Arnold Robbins arnold@cc.gatech.edu
Kevin Rodgers kevin@rolling-stone.den.mmc.com
Kai Uwe Rommel rommel@informatik.tu-muenchen.de
Paul Rubin phr@america.telebit.com
@@ -159,13 +226,20 @@ Niimi Satoshi a01309@cfi.waseda.ac.jp
Marc Schaefer sysadm@alphanet.ch
Andreas Schwab schwab@lamothe.informatik.uni-dortmund.de
Eric Schenk schenk@cs.toronto.edu
+Eric P. Scott eps@cs.sfsu.edu
+Olaf Seibert rhialto@mbfys.kun.nl
+Sunando Sen sens@fasecon.econ.nyu.edu
+Harry Shamansky hts@hertz.eng.ohio-state.edu
+Amos Shapira amoss@cs.huji.ac.il
Rick Sladkey jrs@world.std.com
Daniel L Smith dls@autodesk.com
Fred Smith fredex%fcshome@merk.merk.com
+Stephen Soliday soliday@ncat.edu
Paul Southworth pauls@css.itd.umich.edu
Rob Spencer robbie@winkle.bhpese.oz.au
Richard Stallman rms@gnu.ai.mit.edu
Carsten Steger carsten.steger@informatik.tu-muenchen.de
+David Sundstrom sunds@anon.asic.sc.ti.com
Ed Sznyter ews@babel.babel.com
Hideaki Tanabe arctanx@iyeyasu.ynl.t.u-tokyo.ac.jp
Andrew Telford ajt@peregrin.resmel.bhp.com.au
@@ -177,19 +251,25 @@ Jeff Treece treece@sabbagh.com
Oliver Trepte oliver@ikaros.fysik4.kth.se
Stephane Tsacas slt@is21.isoft.fr
Stephen Tweedie sct@dcs.ed.ac.uk
+John R. Vanderpool fish@daacdev1.stx.com
Sotiris Vassilopoulos vassilopoulos@virginia.edu
Pedro A. M. Vazquez vazquez@iqm.unicamp.br
Arjan de Vet devet@win.tue.nl
+Larry W. Virden lvirden@cas.org
Vadim V. Vlasov vvlasov@inucres.msk.su
Eduard Vopicka eduard.vopicka@vse.cs
Theo Vosse vosse@ruls41.leidenuniv.nl
+Darin Wayrynen darin@pcg.uucp
Marcel Waldvogel marcel@nice.usergroup.ethz.ch
Stephen J. Walick steve@nshore.org
Gray Watson gray@antaire.com
+David Watt dmwatt@smersh.cambridge.ma.us
Scott Weikart scott@igc.apc.org
Ivo Welch iwelch@agsm.ucla.edu
+Jochen Wiedmann zrawi01@zmcipdec1.zdv.uni-tuebingen.de
Gijsb. Wiesenekker wiesenecker@sara.nl
Wietze van Winden wietze@swi.psy.uva.nl
+Frank Wuebbeling wuebbel@math.uni-muenster.de
Larry W. Virden lwv26@cas.org
Bill Wohler wohler@sap-ag.de
Jamie Zawinski jwz@lucid.com
diff --git a/gnu/usr.bin/gzip/TODO b/gnu/usr.bin/gzip/TODO
index 1587c75..865be92 100644
--- a/gnu/usr.bin/gzip/TODO
+++ b/gnu/usr.bin/gzip/TODO
@@ -8,6 +8,11 @@ Some of the planned features include:
library, but this would degrade performance. In the meantime, you can
look at the sample program zread.c.
+ The library should have one mode in which compressed data is sent
+ as soon as input is available, instead of waiting for complete
+ blocks. This can be useful for sending compressed data to/from interactive
+ programs.
+
- Make it convenient to define alternative user interfaces (in
particular for windowing environments).
@@ -48,11 +53,6 @@ Some of the planned features include:
- Use a larger window size to deal with some large redundant files that
'compress' currently handles better than gzip.
-- implement the following options:
-
- -e encrypt
- -l list .z file contents
+- Implement the -e (encrypt) option.
-- support .Z files in SCO 'compress -H' format.
-
Send comments to Jean-loup Gailly <jloup@chorus.fr>.
diff --git a/gnu/usr.bin/gzip/algorithm.doc b/gnu/usr.bin/gzip/algorithm.doc
index 9c4b0c2..24f7619 100644
--- a/gnu/usr.bin/gzip/algorithm.doc
+++ b/gnu/usr.bin/gzip/algorithm.doc
@@ -48,6 +48,12 @@ match, thus speeding up the whole process. If compression ratio is more
important than speed, zip attempts a complete second search even if
the first match is already long enough.
+The lazy match evaluation is no performed for the fastest compression
+modes (speed options -1 to -3). For these fast modes, new strings
+are inserted in the hash table only when no match was found, or
+when the match is not too long. This degrades the compression ratio
+but saves time since there are both fewer insertions and fewer searches.
+
2. gzip file format
@@ -100,6 +106,23 @@ cleared indicating binary data. For systems which have different
file formats for ascii text and binary data, the decompressor can
use the flag to choose the appropriate format.
+The extra field, if present, must consist of one or more subfields,
+each with the following format:
+
+ subfield id : 2 bytes
+ subfield size : 2 bytes (little-endian format)
+ subfield data
+
+The subfield id can consist of two letters with some mnemonic value.
+Please send any such id to jloup@chorus.fr. Ids with a zero second
+byte are reserved for future use. The following ids are defined:
+
+ Ap (0x41, 0x70) : Apollo file type information
+
+The subfield size is the size of the subfield data and does not
+include the id and the size itself. The field 'extra field length' is
+the total size of the extra field, including subfield ids and sizes.
+
It must be possible to detect the end of the compressed data with any
compression format, regardless of the actual size of the compressed
data. If the compressed data cannot fit in one file (in particular for
diff --git a/gnu/usr.bin/gzip/bits.c b/gnu/usr.bin/gzip/bits.c
index 65e78a6..544d6da 100644
--- a/gnu/usr.bin/gzip/bits.c
+++ b/gnu/usr.bin/gzip/bits.c
@@ -59,8 +59,8 @@
# include <stdio.h>
#endif
-#ifndef lint
-static char rcsid[] = "$Id: bits.c,v 0.8 1993/02/04 13:21:06 jloup Exp $";
+#ifdef RCSID
+static char rcsid[] = "$Id: bits.c,v 0.9 1993/06/11 10:16:58 jloup Exp $";
#endif
/* ===========================================================================
@@ -84,7 +84,7 @@ local int bi_valid;
* are always zero.
*/
-int (*read_buf) OF((char *buf, unsigned size)) = file_read;
+int (*read_buf) OF((char *buf, unsigned size));
/* Current input function. Set to mem_read for in-memory compression */
#ifdef DEBUG
diff --git a/gnu/usr.bin/gzip/crypt.c b/gnu/usr.bin/gzip/crypt.c
index c6c7358..cbce024 100644
--- a/gnu/usr.bin/gzip/crypt.c
+++ b/gnu/usr.bin/gzip/crypt.c
@@ -1,6 +1,6 @@
/* crypt.c (dummy version) -- do not perform encryption
* Hardly worth copyrighting :-)
*/
-#ifndef lint
+#ifdef RCSID
static char rcsid[] = "$Id: crypt.c,v 0.6 1993/03/22 09:48:47 jloup Exp $";
#endif
diff --git a/gnu/usr.bin/gzip/deflate.c b/gnu/usr.bin/gzip/deflate.c
index 0b3af93..7f52b64 100644
--- a/gnu/usr.bin/gzip/deflate.c
+++ b/gnu/usr.bin/gzip/deflate.c
@@ -67,8 +67,8 @@
#include "gzip.h"
#include "lzw.h" /* just for consistency checking */
-#ifndef lint
-static char rcsid[] = "$Id: deflate.c,v 0.13 1993/05/25 16:25:40 jloup Exp $";
+#ifdef RCSID
+static char rcsid[] = "$Id: deflate.c,v 0.15 1993/06/24 10:53:53 jloup Exp $";
#endif
/* ===========================================================================
@@ -97,7 +97,7 @@ static char rcsid[] = "$Id: deflate.c,v 0.13 1993/05/25 16:25:40 jloup Exp $";
/* To save space (see unlzw.c), we overlay prev+head with tab_prefix and
* window with tab_suffix. Check that we can do this:
*/
-#if WSIZE<<1 > 1<<BITS
+#if (WSIZE<<1) > (1<<BITS)
error: cannot overlay window with tab_suffix and prev with tab_prefix0
#endif
#if HASH_BITS > BITS-1
@@ -187,10 +187,19 @@ unsigned near max_chain_length;
local unsigned int max_lazy_match;
/* Attempt to find a better match only when the current match is strictly
- * smaller than this value.
+ * smaller than this value. This mechanism is used only for compression
+ * levels >= 4.
*/
+#define max_insert_length max_lazy_match
+/* Insert new strings in the hash table only if the match length
+ * is not greater than this length. This saves time but degrades compression.
+ * max_insert_length is used only for compression levels <= 3.
+ */
+
+local int compr_level;
+/* compression level (1..9) */
-int near good_match;
+unsigned near good_match;
/* Use a faster search when the previous match is longer than this */
@@ -216,18 +225,20 @@ typedef struct config {
local config configuration_table[10] = {
/* good lazy nice chain */
/* 0 */ {0, 0, 0, 0}, /* store only */
-/* 1 */ {4, 4, 16, 16}, /* maximum speed */
-/* 2 */ {6, 8, 16, 16},
-/* 3 */ {8, 16, 32, 32},
-/* 4 */ {8, 16, 64, 64},
-/* 5 */ {8, 16, 128, 128},
-/* 6 */ {8, 32, 128, 256},
-/* 7 */ {8, 64, 128, 512},
+/* 1 */ {4, 4, 8, 4}, /* maximum speed, no lazy matches */
+/* 2 */ {4, 5, 16, 8},
+/* 3 */ {4, 6, 32, 32},
+
+/* 4 */ {4, 4, 16, 16}, /* lazy matches */
+/* 5 */ {8, 16, 32, 32},
+/* 6 */ {8, 16, 128, 128},
+/* 7 */ {8, 32, 128, 256},
/* 8 */ {32, 128, 258, 1024},
/* 9 */ {32, 258, 258, 4096}}; /* maximum compression */
-/* Note: the current code requires max_lazy >= MIN_MATCH and max_chain >= 4
- * but these restrictions can easily be removed at a small cost.
+/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
+ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
+ * meaning.
*/
#define EQUAL 0
@@ -237,6 +248,8 @@ local config configuration_table[10] = {
* Prototypes for local functions.
*/
local void fill_window OF((void));
+local ulg deflate_fast OF((void));
+
int longest_match OF((IPos cur_match));
#ifdef ASMV
void match_init OF((void)); /* asm code initialization */
@@ -277,6 +290,7 @@ void lm_init (pack_level, flags)
register unsigned j;
if (pack_level < 1 || pack_level > 9) error("bad pack level");
+ compr_level = pack_level;
/* Initialize the hash table. */
#if defined(MAXSEG_64K) && HASH_BITS == 15
@@ -558,10 +572,12 @@ local void fill_window()
(char*)NULL, (long)strstart - block_start, (eof))
/* ===========================================================================
- * Processes a new input file and return its compressed length.
+ * Processes a new input file and return its compressed length. This
+ * function does not perform lazy evaluationof matches and inserts
+ * new strings in the dictionary only for unmatched strings or for short
+ * matches. It is used only for the fast compression options.
*/
-#ifdef NO_LAZY
-ulg deflate()
+local ulg deflate_fast()
{
IPos hash_head; /* head of the hash chain */
int flush; /* set if current block must be flushed */
@@ -592,22 +608,38 @@ ulg deflate()
flush = ct_tally(strstart-match_start, match_length - MIN_MATCH);
lookahead -= match_length;
- match_length--; /* string at strstart already in hash table */
- do {
- strstart++;
- INSERT_STRING(strstart, hash_head);
- /* strstart never exceeds WSIZE-MAX_MATCH, so there are
- * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH
- * these bytes are garbage, but it does not matter since the
- * next lookahead bytes will always be emitted as literals.
- */
- } while (--match_length != 0);
+
+ /* Insert new strings in the hash table only if the match length
+ * is not too large. This saves time but degrades compression.
+ */
+ if (match_length <= max_insert_length) {
+ match_length--; /* string at strstart already in hash table */
+ do {
+ strstart++;
+ INSERT_STRING(strstart, hash_head);
+ /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+ * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH
+ * these bytes are garbage, but it does not matter since
+ * the next lookahead bytes will be emitted as literals.
+ */
+ } while (--match_length != 0);
+ strstart++;
+ } else {
+ strstart += match_length;
+ match_length = 0;
+ ins_h = window[strstart];
+ UPDATE_HASH(ins_h, window[strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ }
} else {
/* No match, output a literal byte */
+ Tracevv((stderr,"%c",window[strstart]));
flush = ct_tally (0, window[strstart]);
lookahead--;
+ strstart++;
}
- strstart++;
if (flush) FLUSH_BLOCK(0), block_start = strstart;
/* Make sure that we always have enough lookahead, except
@@ -620,7 +652,7 @@ ulg deflate()
}
return FLUSH_BLOCK(1); /* eof */
}
-#else /* LAZY */
+
/* ===========================================================================
* Same as above, but achieves better compression. We use a lazy
* evaluation for matches: a match is finally adopted only if there is
@@ -637,6 +669,8 @@ ulg deflate()
extern long isize; /* byte length of input file, for debug only */
#endif
+ if (compr_level <= 3) return deflate_fast(); /* optimized for speed */
+
/* Process the input block. */
while (lookahead != 0) {
/* Insert the string window[strstart .. strstart+2] in the
@@ -727,4 +761,3 @@ ulg deflate()
return FLUSH_BLOCK(1); /* eof */
}
-#endif /* LAZY */
diff --git a/gnu/usr.bin/gzip/getopt.c b/gnu/usr.bin/gzip/getopt.c
index 3bf0277..55fad84 100644
--- a/gnu/usr.bin/gzip/getopt.c
+++ b/gnu/usr.bin/gzip/getopt.c
@@ -3,62 +3,64 @@
"Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
before changing it!
- Copyright (C) 1987, 88, 89, 90, 91, 1992 Free Software Foundation, Inc.
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+ Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
-
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-/* AIX requires this to be the first thing in the file. */
-#ifdef __GNUC__
-#define alloca __builtin_alloca
-#else /* not __GNUC__ */
-#if defined (HAVE_ALLOCA_H) || (defined(sparc) && (defined(sun) || (!defined(USG) && !defined(SVR4) && !defined(__svr4__))))
-#include <alloca.h>
-#else
-#ifdef _AIX
- #pragma alloca
-#else
-char *alloca ();
+#ifdef HAVE_CONFIG_H
+#include "config.h"
#endif
-#endif /* alloca.h */
-#endif /* not __GNUC__ */
-#include <stdio.h>
+#ifndef __STDC__
+# ifndef const
+# define const
+# endif
+#endif
-#if defined(USG) || defined(STDC_HEADERS) || defined(__GNU_LIBRARY__)
-#include <string.h>
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. */
+#ifndef _NO_PROTO
+#define _NO_PROTO
#endif
+#include <stdio.h>
+#include "tailor.h"
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
/* This needs to come after some library #include
to get __GNU_LIBRARY__ defined. */
#ifdef __GNU_LIBRARY__
-#undef alloca
/* Don't include stdlib.h for non-GNU C libraries because some of them
contain conflicting prototypes for getopt. */
#include <stdlib.h>
-#else /* Not GNU C library. */
-#define __alloca alloca
#endif /* GNU C library. */
-#if !__STDC__
-#define const
-#endif
-
/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
long-named option. Because this is not POSIX.2 compliant, it is
being phased out. */
-#define GETOPT_COMPAT
+/* #define GETOPT_COMPAT */
/* This version of `getopt' appears to the caller like standard Unix `getopt'
but it behaves differently for the user, since it allows the user
@@ -96,6 +98,7 @@ char *optarg = 0;
Otherwise, `optind' communicates from one call to the next
how much of ARGV has been scanned so far. */
+/* XXX 1003.2 says this must be 1 before any call. */
int optind = 0;
/* The next char to be scanned in the option-element
@@ -112,6 +115,13 @@ static char *nextchar;
int opterr = 1;
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+#define BAD_OPTION '\0'
+int optopt = BAD_OPTION;
+
/* Describe how to deal with options that follow non-option ARGV-elements.
If the caller did not specify anything,
@@ -147,39 +157,53 @@ static enum
} ordering;
#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
#include <string.h>
#define my_index strchr
-#define my_bcopy(src, dst, n) memcpy ((dst), (src), (n))
+#define my_strlen strlen
#else
/* Avoid depending on library functions or files
whose names are inconsistent. */
-char *getenv ();
+#if __STDC__ || defined(PROTO)
+extern char *getenv(const char *name);
+extern int strcmp (const char *s1, const char *s2);
+extern int strncmp(const char *s1, const char *s2, int n);
+
+static int my_strlen(const char *s);
+static char *my_index (const char *str, int chr);
+#else
+extern char *getenv ();
+#endif
+
+static int
+my_strlen (str)
+ const char *str;
+{
+ int n = 0;
+ while (*str++)
+ n++;
+ return n;
+}
static char *
-my_index (string, chr)
- char *string;
+my_index (str, chr)
+ const char *str;
int chr;
{
- while (*string)
+ while (*str)
{
- if (*string == chr)
- return string;
- string++;
+ if (*str == chr)
+ return (char *) str;
+ str++;
}
return 0;
}
-static void
-my_bcopy (from, to, size)
- char *from, *to;
- int size;
-{
- int i;
- for (i = 0; i < size; i++)
- to[i] = from[i];
-}
#endif /* GNU C library. */
/* Handle permutation of arguments. */
@@ -198,32 +222,49 @@ static int last_nonopt;
the options processed since those non-options were skipped.
`first_nonopt' and `last_nonopt' are relocated so that they describe
- the new indices of the non-options in ARGV after they are moved. */
+ the new indices of the non-options in ARGV after they are moved.
+
+ To perform the swap, we first reverse the order of all elements. So
+ all options now come before all non options, but they are in the
+ wrong order. So we put back the options and non options in original
+ order by reversing them again. For example:
+ original input: a b c -x -y
+ reverse all: -y -x c b a
+ reverse options: -x -y c b a
+ reverse non options: -x -y a b c
+*/
+
+#if __STDC__ || defined(PROTO)
+static void exchange (char **argv);
+#endif
static void
exchange (argv)
char **argv;
{
- int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *);
-#ifdef _CRAY
- char *temp[last_nonopt - first_nonopt];
-#else
- char **temp = (char **) __alloca (nonopts_size);
-#endif
-
- /* Interchange the two blocks of data in ARGV. */
-
- my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size);
- my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt],
- (optind - last_nonopt) * sizeof (char *));
- my_bcopy ((char *) temp,
- (char *) &argv[first_nonopt + optind - last_nonopt],
- nonopts_size);
-
- /* Update records for the slots the non-options now occupy. */
+ char *temp, **first, **last;
+ /* Reverse all the elements [first_nonopt, optind) */
+ first = &argv[first_nonopt];
+ last = &argv[optind-1];
+ while (first < last) {
+ temp = *first; *first = *last; *last = temp; first++; last--;
+ }
+ /* Put back the options in order */
+ first = &argv[first_nonopt];
first_nonopt += (optind - last_nonopt);
+ last = &argv[first_nonopt - 1];
+ while (first < last) {
+ temp = *first; *first = *last; *last = temp; first++; last--;
+ }
+
+ /* Put back the non options in order */
+ first = &argv[first_nonopt];
last_nonopt = optind;
+ last = &argv[last_nonopt-1];
+ while (first < last) {
+ temp = *first; *first = *last; *last = temp; first++; last--;
+ }
}
/* Scan elements of ARGV (whose length is ARGC) for option characters
@@ -246,8 +287,8 @@ exchange (argv)
OPTSTRING is a string containing the legitimate option characters.
If an option character is seen that is not listed in OPTSTRING,
- return '?' after printing an error message. If you set `opterr' to
- zero, the error message is suppressed but we still return '?'.
+ return BAD_OPTION after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return BAD_OPTION.
If a char in OPTSTRING is followed by a colon, that means it wants an arg,
so the following text in the same ARGV-element, or the text of the following
@@ -426,7 +467,7 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only)
p++, option_index++)
if (!strncmp (p->name, nextchar, s - nextchar))
{
- if (s - nextchar == strlen (p->name))
+ if (s - nextchar == my_strlen (p->name))
{
/* Exact match found. */
pfound = p;
@@ -450,9 +491,9 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only)
if (opterr)
fprintf (stderr, "%s: option `%s' is ambiguous\n",
argv[0], argv[optind]);
- nextchar += strlen (nextchar);
+ nextchar += my_strlen (nextchar);
optind++;
- return '?';
+ return BAD_OPTION;
}
if (pfound != NULL)
@@ -480,8 +521,8 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only)
"%s: option `%c%s' doesn't allow an argument\n",
argv[0], argv[optind - 1][0], pfound->name);
}
- nextchar += strlen (nextchar);
- return '?';
+ nextchar += my_strlen (nextchar);
+ return BAD_OPTION;
}
}
else if (pfound->has_arg == 1)
@@ -493,11 +534,11 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only)
if (opterr)
fprintf (stderr, "%s: option `%s' requires an argument\n",
argv[0], argv[optind - 1]);
- nextchar += strlen (nextchar);
- return '?';
+ nextchar += my_strlen (nextchar);
+ return optstring[0] == ':' ? ':' : BAD_OPTION;
}
}
- nextchar += strlen (nextchar);
+ nextchar += my_strlen (nextchar);
if (longind != NULL)
*longind = option_index;
if (pfound->flag)
@@ -515,7 +556,7 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only)
#ifdef GETOPT_COMPAT
|| argv[optind][0] == '+'
#endif /* GETOPT_COMPAT */
- || my_index ((char*)optstring, *nextchar) == NULL)
+ || my_index (optstring, *nextchar) == NULL)
{
if (opterr)
{
@@ -530,7 +571,7 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only)
}
nextchar = (char *) "";
optind++;
- return '?';
+ return BAD_OPTION;
}
}
@@ -538,7 +579,7 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only)
{
char c = *nextchar++;
- char *temp = my_index ((char*)optstring, c);
+ char *temp = my_index (optstring, c);
/* Increment `optind' when we start to process its last character. */
if (*nextchar == '\0')
@@ -548,13 +589,19 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only)
{
if (opterr)
{
+#if 0
if (c < 040 || c >= 0177)
fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
argv[0], c);
else
fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
+#endif
}
- return '?';
+ optopt = c;
+ return BAD_OPTION;
}
if (temp[1] == ':')
{
@@ -583,9 +630,21 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only)
else if (optind == argc)
{
if (opterr)
- fprintf (stderr, "%s: option `-%c' requires an argument\n",
- argv[0], c);
- c = '?';
+ {
+#if 0
+ fprintf (stderr, "%s: option `-%c' requires an argument\n",
+ argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: option requires an argument -- %c\n",
+ argv[0], c);
+#endif
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = BAD_OPTION;
}
else
/* We already incremented `optind' once;
@@ -621,22 +680,7 @@ getopt_long (argc, argv, options, long_options, opt_index)
return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
}
-/* Like getopt_long, but '-' as well as '--' can indicate a long option.
- If an option that starts with '-' (not '--') doesn't match a long option,
- but does match a short option, it is parsed as a short option
- instead. */
-
-int
-getopt_long_only (argc, argv, options, long_options, opt_index)
- int argc;
- char *const *argv;
- const char *options;
- const struct option *long_options;
- int *opt_index;
-{
- return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
-}
-
+#endif /* _LIBC or not __GNU_LIBRARY__. */
#ifdef TEST
@@ -689,7 +733,7 @@ main (argc, argv)
printf ("option c with value `%s'\n", optarg);
break;
- case '?':
+ case BAD_OPTION:
break;
default:
diff --git a/gnu/usr.bin/gzip/getopt.h b/gnu/usr.bin/gzip/getopt.h
index 764f2f4..0abce6e 100644
--- a/gnu/usr.bin/gzip/getopt.h
+++ b/gnu/usr.bin/gzip/getopt.h
@@ -1,16 +1,16 @@
/* Declarations for getopt.
- Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+ Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
-
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
@@ -49,6 +49,10 @@ extern int optind;
extern int opterr;
+/* Set to an option character which was unrecognized. */
+
+extern int optopt;
+
/* Describe the long-named options requested by the application.
The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
of `struct option' terminated by an element containing a name which is
@@ -86,12 +90,9 @@ struct option
/* Names for the values of the `has_arg' field of `struct option'. */
-enum _argtype
-{
- no_argument,
- required_argument,
- optional_argument
-};
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
#if __STDC__ || defined(PROTO)
#if defined(__GNU_LIBRARY__)
@@ -99,8 +100,6 @@ enum _argtype
differences in the consts, in stdlib.h. To avoid compilation
errors, only prototype getopt for the GNU C library. */
extern int getopt (int argc, char *const *argv, const char *shortopts);
-#else /* not __GNU_LIBRARY__ */
-extern int getopt ();
#endif /* not __GNU_LIBRARY__ */
extern int getopt_long (int argc, char *const *argv, const char *shortopts,
const struct option *longopts, int *longind);
diff --git a/gnu/usr.bin/gzip/gzexe b/gnu/usr.bin/gzip/gzexe
index ba22eb1..0c248d9 100644
--- a/gnu/usr.bin/gzip/gzexe
+++ b/gnu/usr.bin/gzip/gzexe
@@ -8,8 +8,9 @@
# We also try to retain the original file permissions on the compressed file.
# For safety reasons, gzexe will not create setuid or setgid shell scripts.
-# Warning: the first line of this file must be either : or #!/bin/sh
+# WARNING: the first line of this file must be either : or #!/bin/sh
# The : is required for some old versions of csh.
+# On Ultrix, /bin/sh is too buggy, change the first line to: #!/bin/sh5
x=`basename $0`
if test $# = 0; then
@@ -37,6 +38,21 @@ if test -z "`(${CPMOD-cpmod} zfoo1$$ zfoo2$$) 2>&1`"; then
fi
rm -f zfoo[12]$$
+tail=""
+IFS="${IFS= }"; saveifs="$IFS"; IFS="${IFS}:"
+for dir in $PATH; do
+ test -z "$dir" && dir=.
+ if test -f $dir/tail; then
+ tail="$dir/tail"
+ break
+ fi
+done
+IFS="$saveifs"
+if test -z "$tail"; then
+ echo cannot find tail
+ exit 1
+fi
+
for i do
if test ! -f "$i" ; then
echo ${x}: $i not a file
@@ -57,10 +73,10 @@ for i do
echo "${x}: $i has setgid permission, unchanged"
continue
fi
- if test "`basename $i`" = gzip; then
- echo "${x}: cannot compress gzip itself"
- continue
- fi
+ case "`basename $i`" in
+ gzip | tail | chmod | ln | sleep | rm)
+ echo "${x}: $i would depend on itself"; continue ;;
+ esac
if test -z "$cpmod"; then
cp -p "$i" $tmp 2>/dev/null || cp "$i" $tmp
if test -w $tmp 2>/dev/null; then
@@ -72,18 +88,18 @@ for i do
fi
if test $decomp -eq 0; then
sed 1q $0 > $tmp
- cat >> $tmp <<'EOF'
+ sed "s|^if tail|if $tail|" >> $tmp <<'EOF'
skip=18
if tail +$skip $0 | gzip -cd > /tmp/gztmp$$; then
- chmod 755 /tmp/gztmp$$
- prog="`basename $0`"
- if ln /tmp/gztmp$$ "/tmp/$prog" 2>/dev/null; then
+ chmod 700 /tmp/gztmp$$
+ prog="`echo $0 | sed 's|^.*/||'`"
+ if /bin/ln /tmp/gztmp$$ "/tmp/$prog" 2>/dev/null; then
trap '/bin/rm -f /tmp/gztmp$$ "/tmp/$prog"; exit $res' 0
- (sleep 5; /bin/rm -f /tmp/gztmp$$ "/tmp/$prog") 2>/dev/null &
+ (/bin/sleep 5; /bin/rm -f /tmp/gztmp$$ "/tmp/$prog") 2>/dev/null &
/tmp/"$prog" ${1+"$@"}; res=$?
else
trap '/bin/rm -f /tmp/gztmp$$; exit $res' 0
- (sleep 5; /bin/rm -f /tmp/gztmp$$) 2>/dev/null &
+ (/bin/sleep 5; /bin/rm -f /tmp/gztmp$$) 2>/dev/null &
/tmp/gztmp$$ ${1+"$@"}; res=$?
fi
else
diff --git a/gnu/usr.bin/gzip/gzexe.1 b/gnu/usr.bin/gzip/gzexe.1
index 68e4127..8b62cd6 100644
--- a/gnu/usr.bin/gzip/gzexe.1
+++ b/gnu/usr.bin/gzip/gzexe.1
@@ -27,6 +27,13 @@ This utility is most useful on systems with very small disks.
Decompress the given executables instead of compressing them.
.SH "SEE ALSO"
gzip(1), znew(1), zmore(1), zcmp(1), zforce(1)
+.SH CAVEATS
+The compressed executable is a shell script. This may create some
+security holes. In particular, the compressed executable relies
+on the PATH environment variable to find
+.I gzip
+and some other utilities
+.I (tail, chmod, ln, sleep).
.SH "BUGS"
.I gzexe
attempts to retain the original file attributes on the compressed executable,
diff --git a/gnu/usr.bin/gzip/gzexe.in b/gnu/usr.bin/gzip/gzexe.in
index dcbab88..a28ec3d 100644
--- a/gnu/usr.bin/gzip/gzexe.in
+++ b/gnu/usr.bin/gzip/gzexe.in
@@ -9,8 +9,9 @@
# We also try to retain the original file permissions on the compressed file.
# For safety reasons, gzexe will not create setuid or setgid shell scripts.
-# Warning: the first line of this file must be either : or #!/bin/sh
+# WARNING: the first line of this file must be either : or #!/bin/sh
# The : is required for some old versions of csh.
+# On Ultrix, /bin/sh is too buggy, change the first line to: #!/bin/sh5
x=`basename $0`
if test $# = 0; then
@@ -38,6 +39,21 @@ if test -z "`(${CPMOD-cpmod} zfoo1$$ zfoo2$$) 2>&1`"; then
fi
rm -f zfoo[12]$$
+tail=""
+IFS="${IFS= }"; saveifs="$IFS"; IFS="${IFS}:"
+for dir in $PATH; do
+ test -z "$dir" && dir=.
+ if test -f $dir/tail; then
+ tail="$dir/tail"
+ break
+ fi
+done
+IFS="$saveifs"
+if test -z "$tail"; then
+ echo cannot find tail
+ exit 1
+fi
+
for i do
if test ! -f "$i" ; then
echo ${x}: $i not a file
@@ -58,10 +74,10 @@ for i do
echo "${x}: $i has setgid permission, unchanged"
continue
fi
- if test "`basename $i`" = gzip; then
- echo "${x}: cannot compress gzip itself"
- continue
- fi
+ case "`basename $i`" in
+ gzip | tail | chmod | ln | sleep | rm)
+ echo "${x}: $i would depend on itself"; continue ;;
+ esac
if test -z "$cpmod"; then
cp -p "$i" $tmp 2>/dev/null || cp "$i" $tmp
if test -w $tmp 2>/dev/null; then
@@ -73,25 +89,25 @@ for i do
fi
if test $decomp -eq 0; then
sed 1q $0 > $tmp
- cat >> $tmp <<'EOF'
+ sed "s|^if tail|if $tail|" >> $tmp <<'EOF'
skip=18
-if tail +$skip $0 | gzip -cd > /tmp/gztmp$$; then
- chmod 755 /tmp/gztmp$$
- prog="`basename $0`"
- if ln /tmp/gztmp$$ "/tmp/$prog" 2>/dev/null; then
+if tail +$skip $0 | "BINDIR"/gzip -cd > /tmp/gztmp$$; then
+ /bin/chmod 700 /tmp/gztmp$$
+ prog="`echo $0 | /bin/sed 's|^.*/||`"
+ if /bin/ln /tmp/gztmp$$ "/tmp/$prog" 2>/dev/null; then
trap '/bin/rm -f /tmp/gztmp$$ "/tmp/$prog"; exit $res' 0
- (sleep 5; /bin/rm -f /tmp/gztmp$$ "/tmp/$prog") 2>/dev/null &
+ (/bin/sleep 5; /bin/rm -f /tmp/gztmp$$ "/tmp/$prog") 2>/dev/null &
/tmp/"$prog" ${1+"$@"}; res=$?
else
trap '/bin/rm -f /tmp/gztmp$$; exit $res' 0
- (sleep 5; /bin/rm -f /tmp/gztmp$$) 2>/dev/null &
+ (/bin/sleep 5; /bin/rm -f /tmp/gztmp$$) 2>/dev/null &
/tmp/gztmp$$ ${1+"$@"}; res=$?
fi
else
echo Cannot decompress $0; exit 1
fi; exit $res
EOF
- gzip -cv9 "$i" >> $tmp || {
+ "BINDIR"/gzip -cv9 "$i" >> $tmp || {
/bin/rm -f $tmp
echo ${x}: compression not possible for $i, file unchanged.
res=1
@@ -104,7 +120,7 @@ EOF
if sed -e 1d -e 2q "$i" | grep "^skip=[0-9]*$" >/dev/null; then
eval `sed -e 1d -e 2q "$i"`
fi
- if tail +$skip "$i" | gzip -cd > $tmp; then
+ if tail +$skip "$i" | "BINDIR"/gzip -cd > $tmp; then
:
else
echo ${x}: $i probably not in gzexe format, file unchanged.
diff --git a/gnu/usr.bin/gzip/gzip.1 b/gnu/usr.bin/gzip/gzip.1
index a7d331f..084dffd 100644
--- a/gnu/usr.bin/gzip/gzip.1
+++ b/gnu/usr.bin/gzip/gzip.1
@@ -5,7 +5,7 @@ gzip, gunzip, zcat \- compress or expand files
.SH SYNOPSIS
.ll +8
.B gzip
-.RB [ " \-acdfhLrtvV19 " ]
+.RB [ " \-acdfhlLnNrtvV19 " ]
.RB [ \-S\ suffix ]
[
.I "name \&..."
@@ -13,14 +13,14 @@ gzip, gunzip, zcat \- compress or expand files
.ll -8
.br
.B gunzip
-.RB [ " \-acfhLrtvV " ]
+.RB [ " \-acfhlLnNrtvV " ]
.RB [ \-S\ suffix ]
[
.I "name \&..."
]
.br
.B zcat
-.RB [ " \-hLV " ]
+.RB [ " \-fhLV " ]
[
.I "name \&..."
]
@@ -35,21 +35,41 @@ while keeping the same ownership modes, access and modification times.
.B "\-gz"
for VMS,
.B "z"
-for MSDOS, OS/2 and Atari.)
-If no files are specified, the standard input is compressed to the
-standard output. If the new file name is too long,
-.I gzip
-truncates it and keeps the original file name in the compressed file.
+for MSDOS, OS/2 FAT, Windows NT FAT and Atari.)
+If no files are specified, or if a file name is "-", the standard input is
+compressed to the standard output.
.I Gzip
will only attempt to compress regular files.
In particular, it will ignore symbolic links.
.PP
+If the compressed file name is too long for its file system,
+.I gzip
+truncates it.
+.I Gzip
+attempts to truncate only the parts of the file name longer than 3 characters.
+(A part is delimited by dots.) If the name consists of small parts only,
+the longest parts are truncated. For example, if file names are limited
+to 14 characters, gzip.msdos.exe is compressed to gzi.msd.exe.gz.
+Names are not truncated on systems which do not have a limit on file name
+length.
+.PP
+By default,
+.I gzip
+keeps the original file name and timestamp in the compressed file. These
+are used when decompressing the file with the
+.B \-N
+option. This is useful when the compressed file name was truncated or
+when the time stamp was not preserved after a file transfer.
+.PP
Compressed files can be restored to their original form using
.I gzip -d
or
.I gunzip
or
.I zcat.
+If the original name saved in the compressed file is not suitable for its
+file system, a new name is constructed from the original one to make it
+legal.
.PP
.I gunzip
takes a list of files on its command line and replaces each
@@ -66,10 +86,17 @@ as shorthands for
and
.B "\&.tar.Z"
respectively.
+When compressing,
+.I gzip
+uses the
+.B "\&.tgz"
+extension if necessary instead of truncating a file with a
+.B "\&.tar"
+extension.
.PP
.I gunzip
can currently decompress files created by
-.I gzip, zip, compress
+.I gzip, zip, compress, compress -H
or
.I pack.
The detection of the input format is automatic. When using
@@ -77,7 +104,7 @@ the first two formats,
.I gunzip
checks a 32 bit CRC. For
.I pack, gunzip
-checks the uncompressed length. The
+checks the uncompressed length. The standard
.I compress
format was not designed to allow consistency checks. However
.I gunzip
@@ -88,6 +115,8 @@ correct simply because the standard
does not complain. This generally means that the standard
.I uncompress
does not check its input, and happily generates garbage output.
+The SCO compress -H format (lzh compression method) does not include a CRC
+but also allows some consistency checks.
.PP
Files created by
.I zip
@@ -150,7 +179,7 @@ Ascii text mode: convert end-of-lines using local conventions. This option
is supported only on some non-Unix systems. For MSDOS, CR LF is converted
to LF when compressing, and LF is converted to CR LF when decompressing.
.TP
-.B \-c --stdout
+.B \-c --stdout --to-stdout
Write output on standard output; keep original files unchanged.
If there are several input files, the output consists of a sequence of
independently compressed members. To obtain better compression,
@@ -162,7 +191,15 @@ Decompress.
.B \-f --force
Force compression or decompression even if the file has multiple links
or the corresponding file already exists, or if the compressed data
-is read from or written to a terminal. If
+is read from or written to a terminal. If the input data is not in
+a format recognized by
+.I gzip,
+and if the option --stdout is also given, copy the input data without change
+to the standard ouput: let
+.I zcat
+behave as
+.I cat.
+If
.B \-f
is not given,
and when not running in the background,
@@ -172,15 +209,64 @@ prompts to verify whether an existing file should be overwritten.
.B \-h --help
Display a help screen and quit.
.TP
+.B \-l --list
+For each compressed file, list the following fields:
+
+ compressed size: size of the compressed file
+ uncompressed size: size of the uncompressed file
+ ratio: compression ratio (0.0% if unknown)
+ uncompressed_name: name of the uncompressed file
+
+The uncompressed size is given as -1 for files not in gzip format,
+such as compressed .Z files. To get the uncompressed size for such a file,
+you can use:
+
+ zcat file.Z | wc -c
+
+In combination with the --verbose option, the following fields are also
+displayed:
+
+ method: compression method
+ crc: the 32-bit CRC of the uncompressed data
+ date & time: time stamp for the uncompressed file
+
+The compression methods currently supported are deflate, compress, lzh
+(SCO compress -H) and pack. The crc is given as ffffffff for a file
+not in gzip format.
+
+With --name, the uncompressed name, date and time are
+those stored within the compress file if present.
+
+With --verbose, the size totals and compression ratio for all files
+is also displayed, unless some sizes are unknown. With --quiet,
+the title and totals lines are not displayed.
+.TP
.B \-L --license
Display the
.I gzip
-license.
+license and quit.
+.TP
+.B \-n --no-name
+When compressing, do not save the original file name and time stamp by
+default. (The original name is always saved if the name had to be
+truncated.) When decompressing, do not restore the original file name
+if present (remove only the
+.I gzip
+suffix from the compressed file name) and do not restore the original
+time stamp if present (copy it from the compressed file). This option
+is the default when decompressing.
+.TP
+.B \-N --name
+When compressing, always save the original file name and time stamp; this
+is the default. When decompressing, restore the original file name and
+time stamp if present. This option is useful on systems which have
+a limit on file name length or when the time stamp has been lost after
+a file transfer.
.TP
.B \-q --quiet
Suppress all warnings.
.TP
-.B \-r --recurse
+.B \-r --recursive
Travel the directory structure recursively. If any of the file names
specified on the command line are directories,
.I gzip
@@ -189,10 +275,15 @@ will descend into the directory and compress all the files it finds there
.I gunzip
).
.TP
-.B \-S .z --suffix .z
-Use suffix .z instead of .gz. Any suffix can be given, but suffixes
+.B \-S .suf --suffix .suf
+Use suffix .suf instead of .gz. Any suffix can be given, but suffixes
other than .z and .gz should be avoided to avoid confusion when files
-are transferred to other systems. Previous versions of gzip used
+are transferred to other systems. A null suffix forces gunzip to try
+decompression on all given files regardless of suffix, as in:
+
+ gunzip -S "" * (*.* for MSDOS)
+
+Previous versions of gzip used
the .z suffix. This was changed to avoid a conflict with
.IR pack "(1)".
.TP
@@ -200,7 +291,8 @@ the .z suffix. This was changed to avoid a conflict with
Test. Check the compressed file integrity.
.TP
.B \-v --verbose
-Verbose. Display the name and percentage reduction for each file compressed.
+Verbose. Display the name and percentage reduction for each file compressed
+or decompressed.
.TP
.B \-V --version
Version. Display the version number and compilation options then quit.
@@ -217,9 +309,10 @@ and
.B \-9
or
.B \-\-best
-indicates the slowest compression method (optimal compression).
+indicates the slowest compression method (best compression).
The default compression level is
-.BR \-5.
+.BR \-6
+(that is, biased towards high compression at expense of speed).
.SH "ADVANCED USAGE"
Multiple compressed files can be concatenated. In this case,
.I gunzip
@@ -247,7 +340,19 @@ compresses better than
If you want to recompress concatenated files to get better compression, do:
- zcat old.gz | gzip > new.gz
+ gzip -cd old.gz | gzip > new.gz
+
+If a compressed file consists of several members, the uncompressed
+size and CRC reported by the --list option applies to the last member
+only. If you need the uncompressed size for all members, you can use:
+
+ gzip -cd file.gz | wc -c
+
+If you wish to create a single archive file with multiple members so
+that members can later be extracted independently, use an archiver
+such as tar or zip. GNU tar supports the -z option to invoke gzip
+transparently. gzip is designed as a complement to tar, not as a
+replacement.
.SH "ENVIRONMENT"
The environment variable
.B GZIP
@@ -255,9 +360,9 @@ can hold a set of default options for
.I gzip.
These options are interpreted first and can be overwritten by
explicit command line parameters. For example:
- for sh: GZIP="-8 -v"; export GZIP
- for csh: setenv GZIP "-8 -v"
- for MSDOS: set GZIP=-8 -v
+ for sh: GZIP="-8v --name"; export GZIP
+ for csh: setenv GZIP "-8v --name"
+ for MSDOS: set GZIP=-8v --name
On Vax/VMS, the name of the environment variable is GZIP_OPT, to
avoid a conflict with the symbol set for invocation of the program.
@@ -268,7 +373,7 @@ pack(1), compact(1)
Exit status is normally 0;
if an error occurs, exit status is 1. If a warning occurs, exit status is 2.
.PP
-Usage: gzip [-cdfhLrtvV19] [-S suffix] [file ...]
+Usage: gzip [-cdfhlLnNrtvV19] [-S suffix] [file ...]
.in +8
Invalid options were specified on the command line.
.in -8
@@ -322,7 +427,9 @@ been corrupted.
.in +8
Percentage of the input saved by compression.
(Relevant only for
-.BR \-v \.)
+.BR \-v
+and
+.BR \-l \.)
.in -8
-- not a regular file or directory: ignored
.in +8
@@ -352,9 +459,20 @@ and emits a warning by default. You have to use the --quiet option to
suppress the warning. This option can be set in the
.B GZIP
environment variable as in:
- for sh: GZIP="-q" tar xfz /dev/rmt/datn
- for csh: (setenv GZIP "-q"; tar xfz /dev/rmt/datn)
+ for sh: GZIP="-q" tar -xfz --block-compress /dev/rst0
+ for csh: (setenv GZIP -q; tar -xfz --block-compr /dev/rst0
+
+In the above example, gzip is invoked implicitly by the -z option of
+GNU tar. Make sure that the same block size (-b option of tar) is used
+for reading and writing compressed data on tapes. (This example
+assumes you are using the GNU version of tar.)
+.SH BUGS
+The --list option reports incorrect sizes if they exceed 2 gigabytes.
+The --list option reports sizes as -1 and crc as ffffffff if the
+compressed file is on a non seekable media.
-In the above example, gzip is invoked implicitly by the -z option
-of GNU tar. Make sure that the same block size (-b option of
-tar) is used for reading and writing compressed data on tapes.
+In some rare cases, the --best option gives worse compression than
+the default compression level (-6). On some highly redundant files,
+.I compress
+compresses better than
+.I gzip.
diff --git a/gnu/usr.bin/gzip/gzip.c b/gnu/usr.bin/gzip/gzip.c
index 2bc1c5f..09fe9a1 100644
--- a/gnu/usr.bin/gzip/gzip.c
+++ b/gnu/usr.bin/gzip/gzip.c
@@ -31,19 +31,23 @@ static char *license_msg[] = {
* Outputs:
* file.gz: compressed file with same mode, owner, and utimes
* or stdout with -c option or if stdin used as input.
- * If the OS does not support file names with multiple dots (MSDOS, VMS) or
- * if the output file name had to be truncated, the original name is kept
+ * If the output file name had to be truncated, the original name is kept
* in the compressed file.
* On MSDOS, file.tmp -> file.tmz. On VMS, file.tmp -> file.tmp-gz.
*
+ * Using gz on MSDOS would create too many file name conflicts. For
+ * example, foo.txt -> foo.tgz (.tgz must be reserved as shorthand for
+ * tar.gz). Similarly, foo.dir and foo.doc would both be mapped to foo.dgz.
+ * I also considered 12345678.txt -> 12345txt.gz but this truncates the name
+ * too heavily. There is no ideal solution given the MSDOS 8+3 limitation.
+ *
* For the meaning of all compilation flags, see comments in Makefile.in.
*/
-#ifndef lint
-static char rcsid[] = "$Id: gzip.c,v 0.19 1993/06/01 14:21:46 jloup Exp $";
+#ifdef RCSID
+static char rcsid[] = "$Id: gzip.c,v 0.24 1993/06/24 10:52:07 jloup Exp $";
#endif
-#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <signal.h>
@@ -58,6 +62,12 @@ static char rcsid[] = "$Id: gzip.c,v 0.19 1993/06/01 14:21:46 jloup Exp $";
/* configuration */
+#ifdef NO_TIME_H
+# include <sys/time.h>
+#else
+# include <time.h>
+#endif
+
#ifndef NO_FCNTL_H
# include <fcntl.h>
#endif
@@ -157,8 +167,21 @@ typedef RETSIGTYPE (*sig_type) OF((int));
# define MAX_PATH_LEN 1024 /* max pathname length */
#endif
-#define MAX_HEADER_LEN 16
-/* max length of a compressed file header, fixed part only */
+#ifndef SEEK_END
+# define SEEK_END 2
+#endif
+
+#ifdef NO_OFF_T
+ typedef long off_t;
+ off_t lseek OF((int fd, off_t offset, int whence));
+#endif
+
+/* Separator for file name parts (see shorten_name()) */
+#ifdef NO_MULTIPLE_DOTS
+# define PART_SEP "-"
+#else
+# define PART_SEP "."
+#endif
/* global buffers */
@@ -179,7 +202,10 @@ int ascii = 0; /* convert end-of-lines to local OS conventions */
int to_stdout = 0; /* output to stdout (-c) */
int decompress = 0; /* decompress (-d) */
int force = 0; /* don't ask questions, compress links (-f) */
+int no_name = -1; /* don't save or restore the original file name */
+int no_time = -1; /* don't save or restore the original file time */
int recursive = 0; /* recurse through directories (-r) */
+int list = 0; /* list the file contents (-l) */
int verbose = 0; /* be verbose (-v) */
int quiet = 0; /* be very quiet (-q) */
int do_lzw = 0; /* generate output compatible with old compress (-Z) */
@@ -188,12 +214,12 @@ int foreground; /* set if program run in foreground */
char *progname; /* program name */
int maxbits = BITS; /* max bits per code for LZW */
int method = DEFLATED;/* compression method */
-int level = 5; /* compression level */
+int level = 6; /* compression level */
int exit_code = OK; /* program exit code */
int save_orig_name; /* set if original name must be saved */
int last_member; /* set for .zip and .Z files */
int part_nb; /* number of parts in .gz file */
-ulg time_stamp; /* original time stamp (modification time) */
+long time_stamp; /* original time stamp (modification time) */
long ifile_size; /* input file size, -1 for devices (debug only) */
char *env; /* contents of GZIP env variable */
char **args = NULL; /* argv pointer if GZIP env variable defined */
@@ -202,6 +228,8 @@ int z_len; /* strlen(z_suffix) */
long bytes_in; /* number of input bytes */
long bytes_out; /* number of output bytes */
+long total_in = 0; /* input bytes for all files */
+long total_out = 0; /* output bytes for all files */
char ifname[MAX_PATH_LEN]; /* input file name */
char ofname[MAX_PATH_LEN]; /* output file name */
int remove_ofname = 0; /* remove output file on error */
@@ -224,13 +252,16 @@ struct option longopts[] =
{"force", 0, 0, 'f'}, /* force overwrite of output file */
{"help", 0, 0, 'h'}, /* give help */
/* {"pkzip", 0, 0, 'k'}, force output in pkzip format */
- /* {"list", 0, 0, 'l'}, list .gz file contents */
+ {"list", 0, 0, 'l'}, /* list .gz file contents */
{"license", 0, 0, 'L'}, /* display software license */
+ {"no-name", 0, 0, 'n'}, /* don't save or restore original name & time */
+ {"name", 0, 0, 'N'}, /* save or restore original name & time */
{"quiet", 0, 0, 'q'}, /* quiet mode */
{"silent", 0, 0, 'q'}, /* quiet mode */
- {"recurse", 0, 0, 'r'}, /* recurse through directories */
+ {"recursive", 0, 0, 'r'}, /* recurse through directories */
{"suffix", 1, 0, 'S'}, /* use given suffix instead of .gz */
{"test", 0, 0, 't'}, /* test compressed file integrity */
+ {"no-time", 0, 0, 'T'}, /* don't save or restore the time stamp */
{"verbose", 0, 0, 'v'}, /* verbose mode */
{"version", 0, 0, 'V'}, /* display version number */
{"fast", 0, 0, '1'}, /* compress faster */
@@ -255,22 +286,28 @@ local int get_istat OF((char *iname, struct stat *sbuf));
local int make_ofname OF((void));
local int same_file OF((struct stat *stat1, struct stat *stat2));
local int name_too_long OF((char *name, struct stat *statb));
+local void shorten_name OF((char *name));
local int get_method OF((int in));
+local void do_list OF((int ifd, int method));
local int check_ofname OF((void));
-local void reset_times OF((char *name, struct stat *statb));
local void copy_stat OF((struct stat *ifstat));
-local void treat_dir OF((char *dir));
local void do_exit OF((int exitcode));
int main OF((int argc, char **argv));
-
int (*work) OF((int infile, int outfile)) = zip; /* function to call */
+#ifndef NO_DIR
+local void treat_dir OF((char *dir));
+#endif
+#ifndef NO_UTIME
+local void reset_times OF((char *name, struct stat *statb));
+#endif
+
#define strequ(s1, s2) (strcmp((s1),(s2)) == 0)
/* ======================================================================== */
local void usage()
{
- fprintf(stderr, "usage: %s [-%scdfhL%stvV19] [-S suffix] [file ...]\n",
+ fprintf(stderr, "usage: %s [-%scdfhlLnN%stvV19] [-S suffix] [file ...]\n",
progname,
#if O_BINARY
"a",
@@ -298,17 +335,19 @@ local void help()
" -f --force force overwrite of output file and compress links",
" -h --help give this help",
/* -k --pkzip force output in pkzip format */
-/* -l --list list .gz file contents */
+ " -l --list list compressed file contents",
" -L --license display software license",
+#ifdef UNDOCUMENTED
+ " -m --no-time do not save or restore the original modification time",
+ " -M --time save or restore the original modification time",
+#endif
+ " -n --no-name do not save or restore the original name and time stamp",
+ " -N --name save or restore the original name and time stamp",
" -q --quiet suppress all warnings",
#ifndef NO_DIR
- " -r --recurse recurse through directories",
-#endif
-#ifdef MAX_EXT_CHARS
- " -S --suffix .gz use suffix .gz instead of .z",
-#else
- " -S --suffix .z use suffix .z instead of .gz",
+ " -r --recursive operate recursively on directories",
#endif
+ " -S .suf --suffix .suf use suffix .suf on compressed files",
" -t --test test compressed file integrity",
" -v --verbose verbose mode",
" -V --version display version number",
@@ -386,7 +425,7 @@ int main (argc, argv)
int argc;
char **argv;
{
- int file_count = 0; /* number of files to precess */
+ int file_count; /* number of files to precess */
int proglen; /* length of progname */
int optc; /* current option */
@@ -406,13 +445,17 @@ int main (argc, argv)
foreground = signal(SIGINT, SIG_IGN) != SIG_IGN;
if (foreground) {
- signal (SIGINT, (sig_type)abort_gzip);
+ (void) signal (SIGINT, (sig_type)abort_gzip);
}
#ifdef SIGTERM
- signal(SIGTERM, (sig_type)abort_gzip);
+ if (signal(SIGTERM, SIG_IGN) != SIG_IGN) {
+ (void) signal(SIGTERM, (sig_type)abort_gzip);
+ }
#endif
#ifdef SIGHUP
- signal(SIGHUP, (sig_type)abort_gzip);
+ if (signal(SIGHUP, SIG_IGN) != SIG_IGN) {
+ (void) signal(SIGHUP, (sig_type)abort_gzip);
+ }
#endif
#ifndef GNU_STANDARD
@@ -435,7 +478,7 @@ int main (argc, argv)
strncpy(z_suffix, Z_SUFFIX, sizeof(z_suffix)-1);
z_len = strlen(z_suffix);
- while ((optc = getopt_long (argc, argv, "ab:cdfhLqrS:tvVZ123456789",
+ while ((optc = getopt_long (argc, argv, "ab:cdfhH?lLmMnNqrS:tvVZ123456789",
longopts, (int *)0)) != EOF) {
switch (optc) {
case 'a':
@@ -451,8 +494,18 @@ int main (argc, argv)
force++; break;
case 'h': case 'H': case '?':
help(); do_exit(OK); break;
+ case 'l':
+ list = decompress = to_stdout = 1; break;
case 'L':
license(); do_exit(OK); break;
+ case 'm': /* undocumented, may change later */
+ no_time = 1; break;
+ case 'M': /* undocumented, may change later */
+ no_time = 0; break;
+ case 'n':
+ no_name = no_time = 1; break;
+ case 'N':
+ no_name = no_time = 0; break;
case 'q':
quiet = 1; verbose = 0; break;
case 'r':
@@ -468,11 +521,6 @@ int main (argc, argv)
if (*optarg == '.') optarg++;
#endif
z_len = strlen(optarg);
- if (z_len == 0 || z_len > MAX_SUFFIX) {
- fprintf(stderr, "%s: incorrect suffix '%s'\n",
- progname, optarg);
- do_exit(ERROR);
- }
strcpy(z_suffix, optarg);
break;
case 't':
@@ -502,6 +550,12 @@ int main (argc, argv)
}
} /* loop on all arguments */
+ /* By default, save name and timestamp on compression but do not
+ * restore them on decompression.
+ */
+ if (no_time < 0) no_time = decompress;
+ if (no_name < 0) no_name = decompress;
+
file_count = argc - optind;
#if O_BINARY
@@ -511,6 +565,11 @@ int main (argc, argv)
progname);
}
#endif
+ if ((z_len == 0 && !decompress) || z_len > MAX_SUFFIX) {
+ fprintf(stderr, "%s: incorrect suffix '%s'\n",
+ progname, optarg);
+ do_exit(ERROR);
+ }
if (do_lzw && !decompress) work = lzw;
/* Allocate all global buffers (for DYN_ALLOC option) */
@@ -527,7 +586,7 @@ int main (argc, argv)
/* And get to work */
if (file_count != 0) {
- if (to_stdout && !test && (!decompress || !ascii)) {
+ if (to_stdout && !test && !list && (!decompress || !ascii)) {
SET_BINARY_MODE(fileno(stdout));
}
while (optind < argc) {
@@ -536,6 +595,9 @@ int main (argc, argv)
} else { /* Standard input */
treat_stdin();
}
+ if (list && !quiet && file_count > 1) {
+ do_list(-1, -1); /* print totals */
+ }
do_exit(exit_code);
return exit_code; /* just to avoid lint warning */
}
@@ -545,7 +607,8 @@ int main (argc, argv)
*/
local void treat_stdin()
{
- if (!force && isatty(fileno((FILE *)(decompress ? stdin : stdout)))) {
+ if (!force && !list &&
+ isatty(fileno((FILE *)(decompress ? stdin : stdout)))) {
/* Do not send compressed data to the terminal or read it from
* the terminal. We get here when user invoked the program
* without parameters, so be helpful. According to the GNU standards:
@@ -569,29 +632,26 @@ local void treat_stdin()
if (decompress || !ascii) {
SET_BINARY_MODE(fileno(stdin));
}
- if (!test && (!decompress || !ascii)) {
+ if (!test && !list && (!decompress || !ascii)) {
SET_BINARY_MODE(fileno(stdout));
}
strcpy(ifname, "stdin");
strcpy(ofname, "stdout");
/* Get the time stamp on the input file. */
-#ifdef NO_STDIN_FSTAT
- time_stamp = 0; /* time unknown */
-#else
- if (fstat(fileno(stdin), &istat) != 0) {
- error("fstat(stdin)");
+ time_stamp = 0; /* time unknown by default */
+
+#ifndef NO_STDIN_FSTAT
+ if (list || !no_time) {
+ if (fstat(fileno(stdin), &istat) != 0) {
+ error("fstat(stdin)");
+ }
+# ifdef NO_PIPE_TIMESTAMP
+ if (S_ISREG(istat.st_mode))
+# endif
+ time_stamp = istat.st_mtime;
+#endif /* NO_STDIN_FSTAT */
}
- /* If you do not wish to save the time stamp when input comes from a pipe,
- * compile with -DNO_PIPE_TIMESTAMP.
- */
-#ifdef NO_PIPE_TIMESTAMP
- if (!S_ISREG(istat.st_mode))
- time_stamp = 0;
- else
-#endif
- time_stamp = istat.st_mtime;
-#endif
ifile_size = -1L; /* convention for unknown size */
clear_bufs(); /* clear input and output buffers */
@@ -604,6 +664,10 @@ local void treat_stdin()
do_exit(exit_code); /* error message already emitted */
}
}
+ if (list) {
+ do_list(ifd, method);
+ return;
+ }
/* Actually do the compression/decompression. Loop over zipped members.
*/
@@ -620,14 +684,17 @@ local void treat_stdin()
if (verbose) {
if (test) {
- fprintf(stderr, " OK");
+ fprintf(stderr, " OK\n");
- } else if (decompress) {
- display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out);
+ } else if (!decompress) {
+ display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in, stderr);
+ fprintf(stderr, "\n");
+#ifdef DISPLAY_STDIN_RATIO
} else {
- display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in);
+ display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out,stderr);
+ fprintf(stderr, "\n");
+#endif
}
- fprintf(stderr, "\n");
}
}
@@ -637,6 +704,14 @@ local void treat_stdin()
local void treat_file(iname)
char *iname;
{
+ /* Accept "-" as synonym for stdin */
+ if (strequ(iname, "-")) {
+ int cflag = to_stdout;
+ treat_stdin();
+ to_stdout = cflag;
+ return;
+ }
+
/* Check if the input file is present, set ifname and istat: */
if (get_istat(iname, &istat) != OK) return;
@@ -648,7 +723,9 @@ local void treat_file(iname)
st = istat;
treat_dir(iname);
/* Warning: ifname is now garbage */
+# ifndef NO_UTIME
reset_times (iname, &st);
+# endif
} else
#endif
WARN((stderr,"%s: %s is a directory -- ignored\n", progname, ifname));
@@ -668,10 +745,12 @@ local void treat_file(iname)
}
ifile_size = istat.st_size;
- time_stamp = istat.st_mtime;
+ time_stamp = no_time && !list ? 0 : istat.st_mtime;
- /* Generate output file name */
- if (to_stdout) {
+ /* Generate output file name. For -r and (-t or -l), skip files
+ * without a valid gzip suffix (check done in make_ofname).
+ */
+ if (to_stdout && !list && !test) {
strcpy(ofname, "stdout");
} else if (make_ofname() != OK) {
@@ -700,6 +779,11 @@ local void treat_file(iname)
return; /* error message already emitted */
}
}
+ if (list) {
+ do_list(ifd, method);
+ close(ifd);
+ return;
+ }
/* If compressing to a file, check if ofname is not ambiguous
* because the operating system truncates names. Otherwise, generate
@@ -711,11 +795,14 @@ local void treat_file(iname)
} else {
if (create_outfile() != OK) return;
- if (save_orig_name && !verbose && !quiet) {
+ if (!decompress && save_orig_name && !verbose && !quiet) {
fprintf(stderr, "%s: %s compressed to %s\n",
progname, ifname, ofname);
}
}
+ /* Keep the name even if not truncated except with --no-name: */
+ if (!save_orig_name) save_orig_name = !no_name;
+
if (verbose) {
fprintf(stderr, "%s:\t%s", ifname, (int)strlen(ifname) >= 15 ?
"" : ((int)strlen(ifname) >= 7 ? "\t" : "\t\t"));
@@ -749,9 +836,9 @@ local void treat_file(iname)
if (test) {
fprintf(stderr, " OK");
} else if (decompress) {
- display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out);
+ display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out,stderr);
} else {
- display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in);
+ display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in, stderr);
}
if (!test && !to_stdout) {
fprintf(stderr, " -- replaced with %s", ofname);
@@ -768,6 +855,7 @@ local void treat_file(iname)
* Create the output file. Return OK or ERROR.
* Try several times if necessary to avoid truncating the z_suffix. For
* example, do not create a compressed file of name "1234567890123."
+ * Sets save_orig_name to true if the file name has been truncated.
* IN assertions: the input file has already been open (ifd is set) and
* ofname has already been updated if there was an original name.
* OUT assertions: ifd and ofd are closed in case of error.
@@ -775,15 +863,13 @@ local void treat_file(iname)
local int create_outfile()
{
struct stat ostat; /* stat for ofname */
- int len;
int flags = O_WRONLY | O_CREAT | O_EXCL | O_BINARY;
if (ascii && decompress) {
flags &= ~O_BINARY; /* force ascii text mode */
}
for (;;) {
- len = strlen(ofname);
- if (len == 0 || ofname[len] == PATH_SEP) break;
+ /* Make sure that ofname is not an existing file */
if (check_ofname() != OK) {
close(ifd);
return ERROR;
@@ -799,7 +885,11 @@ local int create_outfile()
}
/* Check for name truncation on new file (1234567890123.gz) */
+#ifdef NO_FSTAT
+ if (stat(ofname, &ostat) != 0) {
+#else
if (fstat(ofd, &ostat) != 0) {
+#endif
fprintf(stderr, "%s: ", progname);
perror(ofname);
close(ifd); close(ofd);
@@ -814,25 +904,16 @@ local int create_outfile()
WARN((stderr, "%s: %s: warning, name truncated\n",
progname, ofname));
return OK;
- } else {
+ }
+ close(ofd);
+ unlink(ofname);
#ifdef NO_MULTIPLE_DOTS
- /* Should never happen, see check_ofname() */
- fprintf(stderr, "%s: %s: name too long\n", progname, ofname);
- do_exit(ERROR);
-#else
- close(ofd);
- unlink(ofname);
- save_orig_name = 1;
- strcpy(ofname+strlen(ofname)-z_len-1, z_suffix);
- /* 123456789012.gz -> 12345678901.gz */
+ /* Should never happen, see check_ofname() */
+ fprintf(stderr, "%s: %s: name too long\n", progname, ofname);
+ do_exit(ERROR);
#endif
- } /* decompress ? */
- } /* while non null name */
-
- close(ifd);
- fprintf(stderr, "%s: %s: name too long\n", progname, ofname);
- exit_code = ERROR;
- return ERROR;
+ shorten_name(ofname);
+ }
}
/* ========================================================================
@@ -861,6 +942,8 @@ local int do_stat(name, sbuf)
* also accepted suffixes. For Unix, we do not want to accept any
* .??z suffix as indicating a compressed file; some people use .xyz
* to denote volume data.
+ * On systems allowing multiple versions of the same file (such as VMS),
+ * this function removes any version suffix in the given name.
*/
local char *get_suffix(name)
char *name;
@@ -877,19 +960,19 @@ local char *get_suffix(name)
if (strequ(z_suffix, "z")) suf++; /* check long suffixes first */
+#ifdef SUFFIX_SEP
+ /* strip a version number from the file name */
+ {
+ char *v = strrchr(name, SUFFIX_SEP);
+ if (v != NULL) *v = '\0';
+ }
+#endif
nlen = strlen(name);
if (nlen <= MAX_SUFFIX+2) {
strcpy(suffix, name);
} else {
strcpy(suffix, name+nlen-MAX_SUFFIX-2);
}
-#ifdef SUFFIX_SEP
- /* strip a version number from the file name */
- {
- char *v = strrchr(suffix, SUFFIX_SEP);
- if (v != NULL) *v = '\0', nlen = v - name;
- }
-#endif
strlwr(suffix);
slen = strlen(suffix);
do {
@@ -933,7 +1016,8 @@ local int get_istat(iname, sbuf)
exit_code = ERROR;
return ERROR;
}
- /* file.ext doesn't exist, try adding a suffix.
+ /* file.ext doesn't exist, try adding a suffix (after removing any
+ * version number for VMS).
*/
s = get_suffix(ifname);
if (s != NULL) {
@@ -941,10 +1025,6 @@ local int get_istat(iname, sbuf)
exit_code = ERROR;
return ERROR;
}
-#ifdef SUFFIX_SEP
- /* strip a version number from the input file name */
- if ((s = strrchr(ifname, SUFFIX_SEP)) != NULL) *s = '\0';
-#endif
#ifdef NO_MULTIPLE_DOTS
dot = strrchr(ifname, '.');
if (dot == NULL) {
@@ -987,20 +1067,28 @@ local int get_istat(iname, sbuf)
/* ========================================================================
* Generate ofname given ifname. Return OK, or WARNING if file must be skipped.
- * Initializes save_orig_name.
- * IN assertion: this function is not called if to_stdout is true.
+ * Sets save_orig_name to true if the file name has been truncated.
*/
local int make_ofname()
{
char *suff; /* ofname z suffix */
strcpy(ofname, ifname);
+ /* strip a version number if any and get the gzip suffix if present: */
suff = get_suffix(ofname);
if (decompress) {
if (suff == NULL) {
- WARN((stderr,"%s: %s: unknown suffix -- ignored\n",
- progname, ifname));
+ /* Whith -t or -l, try all files (even without .gz suffix)
+ * except with -r (behave as with just -dr).
+ */
+ if (!recursive && (list || test)) return OK;
+
+ /* Avoid annoying messages with -r */
+ if (verbose || (!recursive && !quiet)) {
+ WARN((stderr,"%s: %s: unknown suffix -- ignored\n",
+ progname, ifname));
+ }
return WARNING;
}
/* Make a special case for .tgz and .taz: */
@@ -1008,7 +1096,7 @@ local int make_ofname()
if (strequ(suff, ".tgz") || strequ(suff, ".taz")) {
strcpy(suff, ".tar");
} else {
- *suff = '\0'; /* strip z suffix and optional version number */
+ *suff = '\0'; /* strip the z suffix */
}
/* ofname might be changed later if infile contains an original name */
@@ -1023,16 +1111,15 @@ local int make_ofname()
} else {
save_orig_name = 0;
-#ifdef SUFFIX_SEP
- /* strip a version number from the file name */
- if ((suff = strrchr(ofname, SUFFIX_SEP)) != NULL) *suff = '\0';
-#endif
-
#ifdef NO_MULTIPLE_DOTS
suff = strrchr(ofname, '.');
if (suff == NULL) {
strcat(ofname, ".");
# ifdef MAX_EXT_CHARS
+ if (strequ(z_suffix, "z")) {
+ strcat(ofname, "gz"); /* enough room */
+ return OK;
+ }
/* On the Atari and some versions of MSDOS, name_too_long()
* does not work correctly because of a bug in stat(). So we
* must truncate here.
@@ -1055,6 +1142,7 @@ local int make_ofname()
* original name was given and to_stdout is not set.
* Return the compression method, -1 for error, -2 for warning.
* Set inptr to the offset of the next byte to be processed.
+ * Updates time_stamp if there is one and --no-time is not used.
* This function may be called repeatedly for an input file consisting
* of several contiguous gzip'ed members.
* IN assertions: there is at least one remaining compressed member.
@@ -1063,13 +1151,21 @@ local int make_ofname()
local int get_method(in)
int in; /* input file descriptor */
{
- uch flags;
+ uch flags; /* compression flags */
char magic[2]; /* magic header */
+ ulg stamp; /* time stamp */
- magic[0] = (char)get_byte();
- magic[1] = (char)get_byte();
-
- time_stamp = istat.st_mtime; /* may be modified later for some methods */
+ /* If --force and --stdout, zcat == cat, so do not complain about
+ * premature end of file: use try_byte instead of get_byte.
+ */
+ if (force && to_stdout) {
+ magic[0] = (char)try_byte();
+ magic[1] = (char)try_byte();
+ /* If try_byte returned EOF, magic[1] == 0xff */
+ } else {
+ magic[0] = (char)get_byte();
+ magic[1] = (char)get_byte();
+ }
method = -1; /* unknown yet */
part_nb++; /* number of parts in gzip file */
header_bytes = 0;
@@ -1079,8 +1175,15 @@ local int get_method(in)
if (memcmp(magic, GZIP_MAGIC, 2) == 0
|| memcmp(magic, OLD_GZIP_MAGIC, 2) == 0) {
- work = unzip;
method = (int)get_byte();
+ if (method != DEFLATED) {
+ fprintf(stderr,
+ "%s: %s: unknown method %d -- get newer version of gzip\n",
+ progname, ifname, method);
+ exit_code = ERROR;
+ return -1;
+ }
+ work = unzip;
flags = (uch)get_byte();
if ((flags & ENCRYPTED) != 0) {
@@ -1104,10 +1207,11 @@ local int get_method(in)
exit_code = ERROR;
if (force <= 1) return -1;
}
- time_stamp = (ulg)get_byte();
- time_stamp |= ((ulg)get_byte()) << 8;
- time_stamp |= ((ulg)get_byte()) << 16;
- time_stamp |= ((ulg)get_byte()) << 24;
+ stamp = (ulg)get_byte();
+ stamp |= ((ulg)get_byte()) << 8;
+ stamp |= ((ulg)get_byte()) << 16;
+ stamp |= ((ulg)get_byte()) << 24;
+ if (stamp != 0 && !no_time) time_stamp = stamp;
(void)get_byte(); /* Ignore extra flags for the moment */
(void)get_byte(); /* Ignore OS type for the moment */
@@ -1132,13 +1236,14 @@ local int get_method(in)
/* Get original file name if it was truncated */
if ((flags & ORIG_NAME) != 0) {
- if (to_stdout || part_nb > 1) {
+ if (no_name || (to_stdout && !list) || part_nb > 1) {
/* Discard the old name */
char c; /* dummy used for NeXTstep 3.0 cc optimizer bug */
- while ((c=get_byte()) != 0) c++;
+ do {c=get_byte();} while (c != 0);
} else {
/* Copy the base name. Keep a directory prefix intact. */
- char *p = basename(ofname);
+ char *p = basename(ofname);
+ char *base = p;
for (;;) {
*p = (char)get_char();
if (*p++ == '\0') break;
@@ -1146,8 +1251,13 @@ local int get_method(in)
error("corrupted input -- file name too large");
}
}
- } /* to_stdout */
- } /* orig_name */
+ /* If necessary, adapt the name to local OS conventions: */
+ if (!list) {
+ MAKE_LEGAL_NAME(base);
+ if (base) list=0; /* avoid warning about unused variable */
+ }
+ } /* no_name || to_stdout */
+ } /* ORIG_NAME */
/* Discard file comment if any */
if ((flags & COMMENT) != 0) {
@@ -1171,24 +1281,126 @@ local int get_method(in)
} else if (memcmp(magic, PACK_MAGIC, 2) == 0) {
work = unpack;
method = PACKED;
+
} else if (memcmp(magic, LZW_MAGIC, 2) == 0) {
work = unlzw;
method = COMPRESSED;
last_member = 1;
+
+ } else if (memcmp(magic, LZH_MAGIC, 2) == 0) {
+ work = unlzh;
+ method = LZHED;
+ last_member = 1;
+
+ } else if (force && to_stdout && !list) { /* pass input unchanged */
+ method = STORED;
+ work = copy;
+ inptr = 0;
+ last_member = 1;
}
if (method >= 0) return method;
+
if (part_nb == 1) {
fprintf(stderr, "\n%s: %s: not in gzip format\n", progname, ifname);
exit_code = ERROR;
return -1;
} else {
- WARN((stderr, "\n%s: %s: trailing garbage ignored\n",
+ WARN((stderr, "\n%s: %s: decompression OK, trailing garbage ignored\n",
progname, ifname));
return -2;
}
}
/* ========================================================================
+ * Display the characteristics of the compressed file.
+ * If the given method is < 0, display the accumulated totals.
+ * IN assertions: time_stamp, header_bytes and ifile_size are initialized.
+ */
+local void do_list(ifd, method)
+ int ifd; /* input file descriptor */
+ int method; /* compression method */
+{
+ ulg crc; /* original crc */
+ static int first_time = 1;
+ static char* methods[MAX_METHODS] = {
+ "store", /* 0 */
+ "compr", /* 1 */
+ "pack ", /* 2 */
+ "lzh ", /* 3 */
+ "", "", "", "", /* 4 to 7 reserved */
+ "defla"}; /* 8 */
+ char *date;
+
+ if (first_time && method >= 0) {
+ first_time = 0;
+ if (verbose) {
+ printf("method crc date time ");
+ }
+ if (!quiet) {
+ printf("compressed uncompr. ratio uncompressed_name\n");
+ }
+ } else if (method < 0) {
+ if (total_in <= 0 || total_out <= 0) return;
+ if (verbose) {
+ printf(" %9lu %9lu ",
+ total_in, total_out);
+ } else if (!quiet) {
+ printf("%9ld %9ld ", total_in, total_out);
+ }
+ display_ratio(total_out-(total_in-header_bytes), total_out, stdout);
+ /* header_bytes is not meaningful but used to ensure the same
+ * ratio if there is a single file.
+ */
+ printf(" (totals)\n");
+ return;
+ }
+ crc = (ulg)~0; /* unknown */
+ bytes_out = -1L;
+ bytes_in = ifile_size;
+
+#if RECORD_IO == 0
+ if (method == DEFLATED && !last_member) {
+ /* Get the crc and uncompressed size for gzip'ed (not zip'ed) files.
+ * If the lseek fails, we could use read() to get to the end, but
+ * --list is used to get quick results.
+ * Use "gunzip < foo.gz | wc -c" to get the uncompressed size if
+ * you are not concerned about speed.
+ */
+ bytes_in = (long)lseek(ifd, (off_t)(-8), SEEK_END);
+ if (bytes_in != -1L) {
+ uch buf[8];
+ bytes_in += 8L;
+ if (read(ifd, (char*)buf, sizeof(buf)) != sizeof(buf)) {
+ read_error();
+ }
+ crc = LG(buf);
+ bytes_out = LG(buf+4);
+ }
+ }
+#endif /* RECORD_IO */
+ date = ctime((time_t*)&time_stamp) + 4; /* skip the day of the week */
+ date[12] = '\0'; /* suppress the 1/100sec and the year */
+ if (verbose) {
+ printf("%5s %08lx %11s ", methods[method], crc, date);
+ }
+ printf("%9ld %9ld ", bytes_in, bytes_out);
+ if (bytes_in == -1L) {
+ total_in = -1L;
+ bytes_in = bytes_out = header_bytes = 0;
+ } else if (total_in >= 0) {
+ total_in += bytes_in;
+ }
+ if (bytes_out == -1L) {
+ total_out = -1L;
+ bytes_in = bytes_out = header_bytes = 0;
+ } else if (total_out >= 0) {
+ total_out += bytes_out;
+ }
+ display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out, stdout);
+ printf(" %s\n", ofname);
+}
+
+/* ========================================================================
* Return true if the two stat structures correspond to the same file.
*/
local int same_file(stat1, stat2)
@@ -1232,6 +1444,67 @@ local int name_too_long(name, statb)
}
/* ========================================================================
+ * Shorten the given name by one character, or replace a .tar extension
+ * with .tgz. Truncate the last part of the name which is longer than
+ * MIN_PART characters: 1234.678.012.gz -> 123.678.012.gz. If the name
+ * has only parts shorter than MIN_PART truncate the longest part.
+ * For decompression, just remove the last character of the name.
+ *
+ * IN assertion: for compression, the suffix of the given name is z_suffix.
+ */
+local void shorten_name(name)
+ char *name;
+{
+ int len; /* length of name without z_suffix */
+ char *trunc = NULL; /* character to be truncated */
+ int plen; /* current part length */
+ int min_part = MIN_PART; /* current minimum part length */
+ char *p;
+
+ len = strlen(name);
+ if (decompress) {
+ if (len <= 1) error("name too short");
+ name[len-1] = '\0';
+ return;
+ }
+ p = get_suffix(name);
+ if (p == NULL) error("can't recover suffix\n");
+ *p = '\0';
+ save_orig_name = 1;
+
+ /* compress 1234567890.tar to 1234567890.tgz */
+ if (len > 4 && strequ(p-4, ".tar")) {
+ strcpy(p-4, ".tgz");
+ return;
+ }
+ /* Try keeping short extensions intact:
+ * 1234.678.012.gz -> 123.678.012.gz
+ */
+ do {
+ p = strrchr(name, PATH_SEP);
+ p = p ? p+1 : name;
+ while (*p) {
+ plen = strcspn(p, PART_SEP);
+ p += plen;
+ if (plen > min_part) trunc = p-1;
+ if (*p) p++;
+ }
+ } while (trunc == NULL && --min_part != 0);
+
+ if (trunc != NULL) {
+ do {
+ trunc[0] = trunc[1];
+ } while (*trunc++);
+ trunc--;
+ } else {
+ trunc = strrchr(name, PART_SEP[0]);
+ if (trunc == NULL) error("internal error in shorten_name");
+ if (trunc[1] == '\0') trunc--; /* force truncation */
+ }
+ strcpy(trunc, z_suffix);
+}
+
+/* ========================================================================
* If compressing to a file, check if ofname is not ambiguous
* because the operating system truncates names. Otherwise, generate
* a new ofname and save the original name in the compressed file.
@@ -1247,29 +1520,40 @@ local int name_too_long(name, statb)
*/
local int check_ofname()
{
- int s = strlen(ofname);
struct stat ostat; /* stat for ofname */
- if (stat(ofname, &ostat) != 0) return 0;
-
- /* Check for name truncation on existing file: */
-#ifdef NO_MULTIPLE_DOTS
- if (!decompress && name_too_long(ofname, &ostat)) {
+#ifdef ENAMETOOLONG
+ /* Check for strictly conforming Posix systems (which return ENAMETOOLONG
+ * instead of silently truncating filenames).
+ */
+ errno = 0;
+ while (stat(ofname, &ostat) != 0) {
+ if (errno != ENAMETOOLONG) return 0; /* ofname does not exist */
+ shorten_name(ofname);
+ }
#else
- if (!decompress && s > 8 && name_too_long(ofname, &ostat)) {
+ if (stat(ofname, &ostat) != 0) return 0;
#endif
- save_orig_name = 1;
- strcpy(ofname+s-z_len-1, z_suffix); /* f.ext.gz -> f.ex.gz */
-
+ /* Check for name truncation on existing file. Do this even on systems
+ * defining ENAMETOOLONG, because on most systems the strict Posix
+ * behavior is disabled by default (silent name truncation allowed).
+ */
+ if (!decompress && name_too_long(ofname, &ostat)) {
+ shorten_name(ofname);
if (stat(ofname, &ostat) != 0) return 0;
- } /* !decompress && name_too_long */
+ }
/* Check that the input and output files are different (could be
* the same by name truncation or links).
*/
if (same_file(&istat, &ostat)) {
- fprintf(stderr, "%s: %s and %s are the same file\n",
- progname, ifname, ofname);
+ if (strequ(ifname, ofname)) {
+ fprintf(stderr, "%s: %s: cannot %scompress onto itself\n",
+ progname, ifname, decompress ? "de" : "");
+ } else {
+ fprintf(stderr, "%s: %s and %s are the same file\n",
+ progname, ifname, ofname);
+ }
exit_code = ERROR;
return ERROR;
}
@@ -1300,6 +1584,7 @@ local int check_ofname()
}
+#ifndef NO_UTIME
/* ========================================================================
* Set the access and modification times from the given stat buffer.
*/
@@ -1307,7 +1592,6 @@ local void reset_times (name, statb)
char *name;
struct stat *statb;
{
-#ifndef NO_UTIME
struct utimbuf timep;
/* Copy the time stamp */
@@ -1319,10 +1603,8 @@ local void reset_times (name, statb)
WARN((stderr, "%s: ", progname));
if (!quiet) perror(ofname);
}
-#else
- name = name; statb = statb; /* avoid warnings */
-#endif
}
+#endif
/* ========================================================================
@@ -1335,7 +1617,7 @@ local void copy_stat(ifstat)
#ifndef NO_UTIME
if (decompress && time_stamp != 0 && ifstat->st_mtime != time_stamp) {
ifstat->st_mtime = time_stamp;
- if (verbose) {
+ if (verbose > 1) {
fprintf(stderr, "%s: time stamp restored\n", ofname);
}
}
@@ -1430,6 +1712,10 @@ local void treat_dir(dir)
local void do_exit(exitcode)
int exitcode;
{
+ static int in_exit = 0;
+
+ if (in_exit) exit(exitcode);
+ in_exit = 1;
if (env != NULL) free(env), env = NULL;
if (args != NULL) free((char*)args), args = NULL;
FREE(inbuf);
diff --git a/gnu/usr.bin/gzip/gzip.h b/gnu/usr.bin/gzip/gzip.h
index a4d6376..88b0710 100644
--- a/gnu/usr.bin/gzip/gzip.h
+++ b/gnu/usr.bin/gzip/gzip.h
@@ -16,7 +16,10 @@
typedef char *voidp;
#endif
-/* I don't like nested includes, but the string functions are used too often */
+/* I don't like nested includes, but the string and io functions are used
+ * too often
+ */
+#include <stdio.h>
#if !defined(NO_STRING_H) || defined(STDC_HEADERS)
# include <string.h>
# if !defined(STDC_HEADERS) && !defined(NO_MEMORY_H) && !defined(__GNUC__)
@@ -48,11 +51,13 @@ typedef unsigned long ulg;
#define WARNING 2
/* Compression methods (see algorithm.doc) */
-#define STORED 0
-#define COMPRESSED 1
-#define PACKED 2
-/* methods 3 to 7 reserved */
-#define DEFLATED 8
+#define STORED 0
+#define COMPRESSED 1
+#define PACKED 2
+#define LZHED 3
+/* methods 4 to 7 reserved */
+#define DEFLATED 8
+#define MAX_METHODS 9
extern int method; /* compression method */
/* To save memory for 16 bit systems, some arrays are overlaid between
@@ -60,7 +65,8 @@ extern int method; /* compression method */
* deflate: prev+head window d_buf l_buf outbuf
* unlzw: tab_prefix tab_suffix stack inbuf outbuf
* inflate: window inbuf
- * unpack: window inbuf
+ * unpack: window inbuf prefix_len
+ * unlzh: left+right window c_table inbuf c_len
* For compression, input is done in window[]. For decompression, output
* is done in window except for unlzw.
*/
@@ -139,17 +145,18 @@ extern char ifname[]; /* input file name or "stdin" */
extern char ofname[]; /* output file name or "stdout" */
extern char *progname; /* program name */
-extern ulg time_stamp; /* original time stamp (modification time) */
+extern long time_stamp; /* original time stamp (modification time) */
extern long ifile_size; /* input file size, -1 for devices (debug only) */
typedef int file_t; /* Do not use stdio */
#define NO_FILE (-1) /* in memory compression */
+#define PACK_MAGIC "\037\036" /* Magic header for packed files */
#define GZIP_MAGIC "\037\213" /* Magic header for gzip files, 1F 8B */
#define OLD_GZIP_MAGIC "\037\236" /* Magic header for gzip 0.5 = freeze 1.x */
+#define LZH_MAGIC "\037\240" /* Magic header for SCO LZH Compress files*/
#define PKZIP_MAGIC "\120\113\003\004" /* Magic header for pkzip files */
-#define PACK_MAGIC "\037\036" /* Magic header for packed files */
/* gzip flag byte */
#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
@@ -192,7 +199,8 @@ extern int test; /* check .z file integrity */
extern int to_stdout; /* output to stdout (-c) */
extern int save_orig_name; /* set if original name must be saved */
-#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf())
+#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf(0))
+#define try_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf(1))
/* put_byte is used for the compressed output, put_ubyte for the
* uncompressed output. However unlzw() uses window for its
@@ -261,6 +269,9 @@ extern int check_zipfile OF((int in));
/* in unpack.c */
extern int unpack OF((int in, int out));
+ /* in unlzh.c */
+extern int unlzh OF((int in, int out));
+
/* in gzip.c */
RETSIGTYPE abort_gzip OF((void));
@@ -282,20 +293,22 @@ void copy_block OF((char *buf, unsigned len, int header));
extern int (*read_buf) OF((char *buf, unsigned size));
/* in util.c: */
+extern int copy OF((int in, int out));
extern ulg updcrc OF((uch *s, unsigned n));
extern void clear_bufs OF((void));
-extern int fill_inbuf OF((void));
+extern int fill_inbuf OF((int eof_ok));
extern void flush_outbuf OF((void));
extern void flush_window OF((void));
extern void write_buf OF((int fd, voidp buf, unsigned cnt));
extern char *strlwr OF((char *s));
extern char *basename OF((char *fname));
+extern void make_simple_name OF((char *name));
extern char *add_envopt OF((int *argcp, char ***argvp, char *env));
extern void error OF((char *m));
extern void warn OF((char *a, char *b));
extern void read_error OF((void));
extern void write_error OF((void));
-extern void display_ratio OF((long num, long den));
+extern void display_ratio OF((long num, long den, FILE *file));
extern voidp xmalloc OF((unsigned int size));
/* in inflate.c */
diff --git a/gnu/usr.bin/gzip/inflate.c b/gnu/usr.bin/gzip/inflate.c
index aa31f9e..ede3656 100644
--- a/gnu/usr.bin/gzip/inflate.c
+++ b/gnu/usr.bin/gzip/inflate.c
@@ -96,11 +96,10 @@
the two sets of lengths.
*/
-#ifndef lint
-static char rcsid[] = "$Id: inflate.c,v 0.13 1993/04/26 14:18:22 jloup Exp $";
+#ifdef RCSID
+static char rcsid[] = "$Id: inflate.c,v 0.14 1993/06/10 13:27:04 jloup Exp $";
#endif
-#include <stdio.h>
#include <sys/types.h>
#include "tailor.h"
@@ -310,7 +309,8 @@ int *m; /* maximum lookup bits, returns actual */
do {
Tracecv(*p, (stderr, (n-i >= ' ' && n-i <= '~' ? "%c %d\n" : "0x%x %d\n"),
n-i, *p));
- c[*p++]++; /* assume all entries <= BMAX */
+ c[*p]++; /* assume all entries <= BMAX */
+ p++; /* Can't combine with above line (Solaris bug) */
} while (--i);
if (c[0] == n) /* null input--all zero length codes */
{
diff --git a/gnu/usr.bin/gzip/lzw.c b/gnu/usr.bin/gzip/lzw.c
index cd09047..12bf5c6 100644
--- a/gnu/usr.bin/gzip/lzw.c
+++ b/gnu/usr.bin/gzip/lzw.c
@@ -2,16 +2,14 @@
* This is a dummy version avoiding patent problems.
*/
-#ifndef lint
-static char rcsid[] = "$Id: lzw.c,v 0.8 1993/04/25 08:09:58 jloup Exp $";
+#ifdef RCSID
+static char rcsid[] = "$Id: lzw.c,v 0.9 1993/06/10 13:27:31 jloup Exp $";
#endif
#include "tailor.h"
#include "gzip.h"
#include "lzw.h"
-#include <stdio.h>
-
static int msg_done = 0;
/* Compress in to out with lzw method. */
@@ -21,7 +19,8 @@ int lzw(in, out)
if (msg_done) return ERROR;
msg_done = 1;
fprintf(stderr,"output in compress .Z format not supported\n");
- in++, out++; /* avoid warnings on unused variables */
- exit_code = ERROR;
+ if (in != out) { /* avoid warnings on unused variables */
+ exit_code = ERROR;
+ }
return ERROR;
}
diff --git a/gnu/usr.bin/gzip/match.S b/gnu/usr.bin/gzip/match.S
index cd3176b..4a3d681 100644
--- a/gnu/usr.bin/gzip/match.S
+++ b/gnu/usr.bin/gzip/match.S
@@ -9,7 +9,7 @@
* Kristoffer Eriksson <ske@pkmab.se>
*/
-/* $Id: match.S,v 0.13 1993/05/24 12:03:03 jloup Exp $ */
+/* $Id: match.S,v 0.14 1993/06/11 18:33:24 jloup Exp $ */
/* Preprocess with -DNO_UNDERLINE if your C compiler does not prefix
* external symbols with an underline character '_'.
@@ -44,11 +44,11 @@
.file "match.S"
-#define MAX_MATCH 258
-#define MAX_MATCH2 128 /* MAX_MATCH/2-1 */
+#define MAX_MATCH 258
+#define MAX_MATCH2 $128 /* MAX_MATCH/2-1 */
#define MIN_MATCH 3
-#define WSIZE 32768
-#define MAX_DIST WSIZE - MAX_MATCH - MIN_MATCH - 1
+#define WSIZE $32768
+#define MAX_DIST WSIZE - MAX_MATCH - MIN_MATCH - 1
.globl _match_init
.globl _longest_match
@@ -87,11 +87,11 @@ _longest_match: /* int longest_match(cur_match) */
mov _max_chain_length,%ebp /* chain_length = max_chain_length */
mov _strstart,%edi
mov %edi,%edx
- sub $ MAX_DIST,%edx /* limit = strstart-MAX_DIST */
+ sub MAX_DIST,%edx /* limit = strstart-MAX_DIST */
jae limit_ok
sub %edx,%edx /* limit = NIL */
limit_ok:
- add $ _window+2,%edi /* edi = offset(window+strstart+2) */
+ add $2+_window,%edi /* edi = offset(window+strstart+2) */
mov _prev_length,%ebx /* best_len = prev_length */
movw -3(%ebx,%edi),%ax /* ax = scan[best_len-1..best_len] */
movw -2(%edi),%cx /* cx = scan[0..1] */
@@ -110,7 +110,7 @@ short_loop:
* at this point, di == scan+2, si == cur_match,
* ax = scan[best_len-1..best_len] and cx = scan[0..1]
*/
- and $ WSIZE-1, %esi
+ and WSIZE-1, %esi
movw _prev(%esi,%esi),%si /* cur_match = prev[cur_match] */
/* top word of esi is still 0 */
cmp %edx,%esi /* cur_match <= limit ? */
@@ -125,7 +125,7 @@ do_scan:
lea _window+2(%esi),%esi /* si = match */
mov %edi,%eax /* ax = scan+2 */
- mov $ MAX_MATCH2,%ecx /* scan for at most MAX_MATCH bytes */
+ mov MAX_MATCH2,%ecx /* scan for at most MAX_MATCH bytes */
rep; cmpsw /* loop until mismatch */
je maxmatch /* match of length MAX_MATCH? */
mismatch:
@@ -134,7 +134,7 @@ mismatch:
xchg %edi,%eax /* edi = scan+2, eax = end of scan */
sub %edi,%eax /* eax = len */
sub %eax,%esi /* esi = cur_match + 2 + offset(window) */
- sub $ _window+2,%esi /* esi = cur_match */
+ sub $2+_window,%esi /* esi = cur_match */
subb $1,%cl /* set carry if cl == 0 (cannot use DEC) */
adc $0,%eax /* eax = carry ? len+1 : len */
cmp %ebx,%eax /* len > best_len ? */
diff --git a/gnu/usr.bin/gzip/revision.h b/gnu/usr.bin/gzip/revision.h
index 5a4e1e6..f99b65f 100644
--- a/gnu/usr.bin/gzip/revision.h
+++ b/gnu/usr.bin/gzip/revision.h
@@ -4,13 +4,13 @@
* terms of the GNU General Public License, see the file COPYING.
*/
-#define VERSION "1.1.1"
+#define VERSION "1.2.4"
#define PATCHLEVEL 0
-#define REVDATE "1 Jun 93"
+#define REVDATE "18 Aug 93"
/* This version does not support compression into old compress format: */
#ifdef LZW
# undef LZW
#endif
-/* $Id: revision.h,v 0.20 1993/06/01 14:03:17 jloup Exp $ */
+/* $Id: revision.h,v 0.25 1993/06/24 08:29:52 jloup Exp $ */
diff --git a/gnu/usr.bin/gzip/tailor.h b/gnu/usr.bin/gzip/tailor.h
index 915760a..a97d8be 100644
--- a/gnu/usr.bin/gzip/tailor.h
+++ b/gnu/usr.bin/gzip/tailor.h
@@ -8,12 +8,16 @@
* The target dependent functions should be defined in tailor.c.
*/
-/* $Id: tailor.h,v 0.16 1993/06/01 12:46:03 jloup Exp $ */
+/* $Id: tailor.h,v 0.18 1993/06/14 19:32:20 jloup Exp $ */
#if defined(__MSDOS__) && !defined(MSDOS)
# define MSDOS
#endif
+#if defined(__OS2__) && !defined(OS2)
+# define OS2
+#endif
+
#if defined(OS2) && defined(MSDOS) /* MS C under OS/2 */
# undef MSDOS
#endif
@@ -30,7 +34,12 @@
# else
# define MAXSEG_64K
# ifdef __TURBOC__
-# define NO_UTIME
+# define NO_OFF_T
+# ifdef __BORLANDC__
+# define DIRENT
+# else
+# define NO_UTIME
+# endif
# else /* MSC */
# define HAVE_SYS_UTIME_H
# define NO_UTIME_H
@@ -65,26 +74,59 @@
# define NO_MULTIPLE_DOTS
# define MAX_EXT_CHARS 3
# define Z_SUFFIX "z"
+# define casemap(c) tolow(c)
# endif
# define NO_CHOWN
# define PROTO
# define STDC_HEADERS
-# define HAVE_SYS_UTIME_H
-# define NO_UTIME_H
-# define casemap(c) tolow(c)
# include <io.h>
# define OS_CODE 0x06
# define SET_BINARY_MODE(fd) setmode(fd, O_BINARY)
# ifdef _MSC_VER
+# define HAVE_SYS_UTIME_H
+# define NO_UTIME_H
# define MAXSEG_64K
# undef near
# define near _near
# endif
# ifdef __EMX__
+# define HAVE_SYS_UTIME_H
+# define NO_UTIME_H
# define DIRENT
# define EXPAND(argc,argv) \
{_response(&argc, &argv); _wildcard(&argc, &argv);}
# endif
+# ifdef __BORLANDC__
+# define DIRENT
+# endif
+# ifdef __ZTC__
+# define NO_DIR
+# define NO_UTIME_H
+# include <dos.h>
+# define EXPAND(argc,argv) \
+ {response_expand(&argc, &argv);}
+# endif
+#endif
+
+#ifdef WIN32 /* Windows NT */
+# define HAVE_SYS_UTIME_H
+# define NO_UTIME_H
+# define PATH_SEP2 '\\'
+# define PATH_SEP3 ':'
+# define MAX_PATH_LEN 260
+# define NO_CHOWN
+# define PROTO
+# define STDC_HEADERS
+# define SET_BINARY_MODE(fd) setmode(fd, O_BINARY)
+# include <io.h>
+# include <malloc.h>
+# ifdef NTFAT
+# define NO_MULTIPLE_DOTS
+# define MAX_EXT_CHARS 3
+# define Z_SUFFIX "z"
+# define casemap(c) tolow(c) /* Force file names to lower case */
+# endif
+# define OS_CODE 0x0b
#endif
#ifdef MSDOS
@@ -132,13 +174,11 @@
#ifdef AMIGA
# define PATH_SEP2 ':'
# define STDC_HEADERS
-# define casemap(c) tolow(c) /* Force file names to lower case */
# define OS_CODE 0x01
# define ASMV
# ifdef __GNUC__
# define DIRENT
# define HAVE_UNISTD_H
-# define RETSIGTYPE int
# else /* SASC */
# define NO_STDIN_FSTAT
# define SYSDIR
@@ -149,6 +189,7 @@
# define direct dirent
extern void _expand_args(int *argc, char ***argv);
# define EXPAND(argc,argv) _expand_args(&argc,&argv);
+# undef O_BINARY /* disable useless --ascii option */
# endif
#endif
@@ -161,11 +202,15 @@
# define ASMV
# define OS_CODE 0x05
# ifdef TOSFS
-# define NO_SYMLINK
+# define PATH_SEP2 '\\'
+# define PATH_SEP3 ':'
+# define MAX_PATH_LEN 128
# define NO_MULTIPLE_DOTS
# define MAX_EXT_CHARS 3
# define Z_SUFFIX "z"
# define NO_CHOWN
+# define casemap(c) tolow(c) /* Force file names to lower case */
+# define NO_SYMLINK
# endif
#endif
@@ -199,11 +244,13 @@
# define put_char(c) put_byte((c) & 0x7F)
# define get_char(c) ascii2pascii(get_byte())
# define OS_CODE 0x0F /* temporary, subject to change */
-# undef SIGTERM /* We don't want a signal handler for SIGTERM */
+# ifdef SIGTERM
+# undef SIGTERM /* We don't want a signal handler for SIGTERM */
+# endif
#endif
-#ifdef WIN32
-# define OS_CODE 0x0b
+#if defined(pyr) && !defined(NOMEMCPY) /* Pyramid */
+# define NOMEMCPY /* problem with overlapping copies */
#endif
#ifdef TOPS20
@@ -243,6 +290,19 @@
# define MAX_SUFFIX 30
#endif
+#ifndef MAKE_LEGAL_NAME
+# ifdef NO_MULTIPLE_DOTS
+# define MAKE_LEGAL_NAME(name) make_simple_name(name)
+# else
+# define MAKE_LEGAL_NAME(name)
+# endif
+#endif
+
+#ifndef MIN_PART
+# define MIN_PART 3
+ /* keep at least MIN_PART chars between dots in a file name. */
+#endif
+
#ifndef EXPAND
# define EXPAND(argc,argv)
#endif
diff --git a/gnu/usr.bin/gzip/trees.c b/gnu/usr.bin/gzip/trees.c
index 2cf380a..db3b4b7 100644
--- a/gnu/usr.bin/gzip/trees.c
+++ b/gnu/usr.bin/gzip/trees.c
@@ -54,13 +54,12 @@
*/
#include <ctype.h>
-#include <stdio.h>
#include "tailor.h"
#include "gzip.h"
-#ifndef lint
-static char rcsid[] = "$Id: trees.c,v 0.11 1993/03/26 14:55:43 jloup Exp $";
+#ifdef RCSID
+static char rcsid[] = "$Id: trees.c,v 0.12 1993/06/10 13:27:54 jloup Exp $";
#endif
/* ===========================================================================
diff --git a/gnu/usr.bin/gzip/unlzh.c b/gnu/usr.bin/gzip/unlzh.c
new file mode 100644
index 0000000..e318e5e
--- /dev/null
+++ b/gnu/usr.bin/gzip/unlzh.c
@@ -0,0 +1,401 @@
+/* unlzh.c -- decompress files in SCO compress -H (LZH) format.
+ * The code in this file is directly derived from the public domain 'ar002'
+ * written by Haruhiko Okumura.
+ */
+
+#ifdef RCSID
+static char rcsid[] = "$Id: unlzh.c,v 1.2 1993/06/24 10:59:01 jloup Exp $";
+#endif
+
+#include <stdio.h>
+
+#include "tailor.h"
+#include "gzip.h"
+#include "lzw.h" /* just for consistency checking */
+
+/* decode.c */
+
+local unsigned decode OF((unsigned count, uch buffer[]));
+local void decode_start OF((void));
+
+/* huf.c */
+local void huf_decode_start OF((void));
+local unsigned decode_c OF((void));
+local unsigned decode_p OF((void));
+local void read_pt_len OF((int nn, int nbit, int i_special));
+local void read_c_len OF((void));
+
+/* io.c */
+local void fillbuf OF((int n));
+local unsigned getbits OF((int n));
+local void init_getbits OF((void));
+
+/* maketbl.c */
+
+local void make_table OF((int nchar, uch bitlen[],
+ int tablebits, ush table[]));
+
+
+#define DICBIT 13 /* 12(-lh4-) or 13(-lh5-) */
+#define DICSIZ ((unsigned) 1 << DICBIT)
+
+#ifndef CHAR_BIT
+# define CHAR_BIT 8
+#endif
+
+#ifndef UCHAR_MAX
+# define UCHAR_MAX 255
+#endif
+
+#define BITBUFSIZ (CHAR_BIT * 2 * sizeof(char))
+/* Do not use CHAR_BIT * sizeof(bitbuf), does not work on machines
+ * for which short is not on 16 bits (Cray).
+ */
+
+/* encode.c and decode.c */
+
+#define MAXMATCH 256 /* formerly F (not more than UCHAR_MAX + 1) */
+#define THRESHOLD 3 /* choose optimal value */
+
+/* huf.c */
+
+#define NC (UCHAR_MAX + MAXMATCH + 2 - THRESHOLD)
+ /* alphabet = {0, 1, 2, ..., NC - 1} */
+#define CBIT 9 /* $\lfloor \log_2 NC \rfloor + 1$ */
+#define CODE_BIT 16 /* codeword length */
+
+#define NP (DICBIT + 1)
+#define NT (CODE_BIT + 3)
+#define PBIT 4 /* smallest integer such that (1U << PBIT) > NP */
+#define TBIT 5 /* smallest integer such that (1U << TBIT) > NT */
+#if NT > NP
+# define NPT NT
+#else
+# define NPT NP
+#endif
+
+/* local ush left[2 * NC - 1]; */
+/* local ush right[2 * NC - 1]; */
+#define left prev
+#define right head
+#if NC > (1<<(BITS-2))
+ error cannot overlay left+right and prev
+#endif
+
+/* local uch c_len[NC]; */
+#define c_len outbuf
+#if NC > OUTBUFSIZ
+ error cannot overlay c_len and outbuf
+#endif
+
+local uch pt_len[NPT];
+local unsigned blocksize;
+local ush pt_table[256];
+
+/* local ush c_table[4096]; */
+#define c_table d_buf
+#if (DIST_BUFSIZE-1) < 4095
+ error cannot overlay c_table and d_buf
+#endif
+
+/***********************************************************
+ io.c -- input/output
+***********************************************************/
+
+local ush bitbuf;
+local unsigned subbitbuf;
+local int bitcount;
+
+local void fillbuf(n) /* Shift bitbuf n bits left, read n bits */
+ int n;
+{
+ bitbuf <<= n;
+ while (n > bitcount) {
+ bitbuf |= subbitbuf << (n -= bitcount);
+ subbitbuf = (unsigned)try_byte();
+ if ((int)subbitbuf == EOF) subbitbuf = 0;
+ bitcount = CHAR_BIT;
+ }
+ bitbuf |= subbitbuf >> (bitcount -= n);
+}
+
+local unsigned getbits(n)
+ int n;
+{
+ unsigned x;
+
+ x = bitbuf >> (BITBUFSIZ - n); fillbuf(n);
+ return x;
+}
+
+local void init_getbits()
+{
+ bitbuf = 0; subbitbuf = 0; bitcount = 0;
+ fillbuf(BITBUFSIZ);
+}
+
+/***********************************************************
+ maketbl.c -- make table for decoding
+***********************************************************/
+
+local void make_table(nchar, bitlen, tablebits, table)
+ int nchar;
+ uch bitlen[];
+ int tablebits;
+ ush table[];
+{
+ ush count[17], weight[17], start[18], *p;
+ unsigned i, k, len, ch, jutbits, avail, nextcode, mask;
+
+ for (i = 1; i <= 16; i++) count[i] = 0;
+ for (i = 0; i < (unsigned)nchar; i++) count[bitlen[i]]++;
+
+ start[1] = 0;
+ for (i = 1; i <= 16; i++)
+ start[i + 1] = start[i] + (count[i] << (16 - i));
+ if ((start[17] & 0xffff) != 0)
+ error("Bad table\n");
+
+ jutbits = 16 - tablebits;
+ for (i = 1; i <= (unsigned)tablebits; i++) {
+ start[i] >>= jutbits;
+ weight[i] = (unsigned) 1 << (tablebits - i);
+ }
+ while (i <= 16) {
+ weight[i] = (unsigned) 1 << (16 - i);
+ i++;
+ }
+
+ i = start[tablebits + 1] >> jutbits;
+ if (i != 0) {
+ k = 1 << tablebits;
+ while (i != k) table[i++] = 0;
+ }
+
+ avail = nchar;
+ mask = (unsigned) 1 << (15 - tablebits);
+ for (ch = 0; ch < (unsigned)nchar; ch++) {
+ if ((len = bitlen[ch]) == 0) continue;
+ nextcode = start[len] + weight[len];
+ if (len <= (unsigned)tablebits) {
+ for (i = start[len]; i < nextcode; i++) table[i] = ch;
+ } else {
+ k = start[len];
+ p = &table[k >> jutbits];
+ i = len - tablebits;
+ while (i != 0) {
+ if (*p == 0) {
+ right[avail] = left[avail] = 0;
+ *p = avail++;
+ }
+ if (k & mask) p = &right[*p];
+ else p = &left[*p];
+ k <<= 1; i--;
+ }
+ *p = ch;
+ }
+ start[len] = nextcode;
+ }
+}
+
+/***********************************************************
+ huf.c -- static Huffman
+***********************************************************/
+
+local void read_pt_len(nn, nbit, i_special)
+ int nn;
+ int nbit;
+ int i_special;
+{
+ int i, c, n;
+ unsigned mask;
+
+ n = getbits(nbit);
+ if (n == 0) {
+ c = getbits(nbit);
+ for (i = 0; i < nn; i++) pt_len[i] = 0;
+ for (i = 0; i < 256; i++) pt_table[i] = c;
+ } else {
+ i = 0;
+ while (i < n) {
+ c = bitbuf >> (BITBUFSIZ - 3);
+ if (c == 7) {
+ mask = (unsigned) 1 << (BITBUFSIZ - 1 - 3);
+ while (mask & bitbuf) { mask >>= 1; c++; }
+ }
+ fillbuf((c < 7) ? 3 : c - 3);
+ pt_len[i++] = c;
+ if (i == i_special) {
+ c = getbits(2);
+ while (--c >= 0) pt_len[i++] = 0;
+ }
+ }
+ while (i < nn) pt_len[i++] = 0;
+ make_table(nn, pt_len, 8, pt_table);
+ }
+}
+
+local void read_c_len()
+{
+ int i, c, n;
+ unsigned mask;
+
+ n = getbits(CBIT);
+ if (n == 0) {
+ c = getbits(CBIT);
+ for (i = 0; i < NC; i++) c_len[i] = 0;
+ for (i = 0; i < 4096; i++) c_table[i] = c;
+ } else {
+ i = 0;
+ while (i < n) {
+ c = pt_table[bitbuf >> (BITBUFSIZ - 8)];
+ if (c >= NT) {
+ mask = (unsigned) 1 << (BITBUFSIZ - 1 - 8);
+ do {
+ if (bitbuf & mask) c = right[c];
+ else c = left [c];
+ mask >>= 1;
+ } while (c >= NT);
+ }
+ fillbuf((int) pt_len[c]);
+ if (c <= 2) {
+ if (c == 0) c = 1;
+ else if (c == 1) c = getbits(4) + 3;
+ else c = getbits(CBIT) + 20;
+ while (--c >= 0) c_len[i++] = 0;
+ } else c_len[i++] = c - 2;
+ }
+ while (i < NC) c_len[i++] = 0;
+ make_table(NC, c_len, 12, c_table);
+ }
+}
+
+local unsigned decode_c()
+{
+ unsigned j, mask;
+
+ if (blocksize == 0) {
+ blocksize = getbits(16);
+ if (blocksize == 0) {
+ return NC; /* end of file */
+ }
+ read_pt_len(NT, TBIT, 3);
+ read_c_len();
+ read_pt_len(NP, PBIT, -1);
+ }
+ blocksize--;
+ j = c_table[bitbuf >> (BITBUFSIZ - 12)];
+ if (j >= NC) {
+ mask = (unsigned) 1 << (BITBUFSIZ - 1 - 12);
+ do {
+ if (bitbuf & mask) j = right[j];
+ else j = left [j];
+ mask >>= 1;
+ } while (j >= NC);
+ }
+ fillbuf((int) c_len[j]);
+ return j;
+}
+
+local unsigned decode_p()
+{
+ unsigned j, mask;
+
+ j = pt_table[bitbuf >> (BITBUFSIZ - 8)];
+ if (j >= NP) {
+ mask = (unsigned) 1 << (BITBUFSIZ - 1 - 8);
+ do {
+ if (bitbuf & mask) j = right[j];
+ else j = left [j];
+ mask >>= 1;
+ } while (j >= NP);
+ }
+ fillbuf((int) pt_len[j]);
+ if (j != 0) j = ((unsigned) 1 << (j - 1)) + getbits((int) (j - 1));
+ return j;
+}
+
+local void huf_decode_start()
+{
+ init_getbits(); blocksize = 0;
+}
+
+/***********************************************************
+ decode.c
+***********************************************************/
+
+local int j; /* remaining bytes to copy */
+local int done; /* set at end of input */
+
+local void decode_start()
+{
+ huf_decode_start();
+ j = 0;
+ done = 0;
+}
+
+/* Decode the input and return the number of decoded bytes put in buffer
+ */
+local unsigned decode(count, buffer)
+ unsigned count;
+ uch buffer[];
+ /* The calling function must keep the number of
+ bytes to be processed. This function decodes
+ either 'count' bytes or 'DICSIZ' bytes, whichever
+ is smaller, into the array 'buffer[]' of size
+ 'DICSIZ' or more.
+ Call decode_start() once for each new file
+ before calling this function.
+ */
+{
+ local unsigned i;
+ unsigned r, c;
+
+ r = 0;
+ while (--j >= 0) {
+ buffer[r] = buffer[i];
+ i = (i + 1) & (DICSIZ - 1);
+ if (++r == count) return r;
+ }
+ for ( ; ; ) {
+ c = decode_c();
+ if (c == NC) {
+ done = 1;
+ return r;
+ }
+ if (c <= UCHAR_MAX) {
+ buffer[r] = c;
+ if (++r == count) return r;
+ } else {
+ j = c - (UCHAR_MAX + 1 - THRESHOLD);
+ i = (r - decode_p() - 1) & (DICSIZ - 1);
+ while (--j >= 0) {
+ buffer[r] = buffer[i];
+ i = (i + 1) & (DICSIZ - 1);
+ if (++r == count) return r;
+ }
+ }
+ }
+}
+
+
+/* ===========================================================================
+ * Unlzh in to out. Return OK or ERROR.
+ */
+int unlzh(in, out)
+ int in;
+ int out;
+{
+ unsigned n;
+ ifd = in;
+ ofd = out;
+
+ decode_start();
+ while (!done) {
+ n = decode((unsigned) DICSIZ, window);
+ if (!test && n > 0) {
+ write_buf(out, (char*)window, n);
+ }
+ }
+ return OK;
+}
diff --git a/gnu/usr.bin/gzip/unlzw.c b/gnu/usr.bin/gzip/unlzw.c
index 0687c1b..15d2a31 100644
--- a/gnu/usr.bin/gzip/unlzw.c
+++ b/gnu/usr.bin/gzip/unlzw.c
@@ -7,11 +7,10 @@
* to accommodate in-memory decompression.
*/
-#ifndef lint
-static char rcsid[] = "$Id: unlzw.c,v 0.13 1993/05/27 10:32:55 jloup Exp $";
+#ifdef RCSID
+static char rcsid[] = "$Id: unlzw.c,v 0.15 1993/06/10 13:28:35 jloup Exp $";
#endif
-#include <stdio.h>
#include <sys/types.h>
#include "tailor.h"
@@ -263,6 +262,7 @@ int unlzw(in, out)
read_error();
}
insize += rsize;
+ bytes_in += (ulg)rsize;
}
inbits = ((rsize != 0) ? ((long)insize - insize%n_bits)<<3 :
((long)insize<<3)-(n_bits-1));
@@ -281,8 +281,10 @@ int unlzw(in, out)
goto resetbuf;
}
input(inbuf,posbits,code,n_bits,bitmask);
-
+ Tracev((stderr, "%d ", code));
+
if (oldcode == -1) {
+ if (code >= 256) error("corrupt input.");
outbuf[outpos++] = (char_type)(finchar = (int)(oldcode=code));
continue;
}
@@ -313,9 +315,11 @@ int unlzw(in, out)
posbits, p[-1],p[0],p[1],p[2],p[3]);
#endif
if (!test && outpos > 0) {
- write_buf(out, outbuf, outpos);
+ write_buf(out, (char*)outbuf, outpos);
+ bytes_out += (ulg)outpos;
}
- error("corrupt input. Use zcat to recover some data.");
+ error(to_stdout ? "corrupt input." :
+ "corrupt input. Use zcat to recover some data.");
}
*--stackp = (char_type)finchar;
code = oldcode;
@@ -341,7 +345,10 @@ int unlzw(in, out)
outpos += i;
}
if (outpos >= OUTBUFSIZ) {
- if (!test) write_buf(out, outbuf, outpos);
+ if (!test) {
+ write_buf(out, (char*)outbuf, outpos);
+ bytes_out += (ulg)outpos;
+ }
outpos = 0;
}
stackp+= i;
@@ -360,10 +367,11 @@ int unlzw(in, out)
}
oldcode = incode; /* Remember previous code. */
}
- bytes_in += rsize;
-
} while (rsize != 0);
- if (!test && outpos > 0) write_buf(out, outbuf, outpos);
+ if (!test && outpos > 0) {
+ write_buf(out, (char*)outbuf, outpos);
+ bytes_out += (ulg)outpos;
+ }
return OK;
}
diff --git a/gnu/usr.bin/gzip/unpack.c b/gnu/usr.bin/gzip/unpack.c
index e6cf7ca..a00fdae 100644
--- a/gnu/usr.bin/gzip/unpack.c
+++ b/gnu/usr.bin/gzip/unpack.c
@@ -4,12 +4,10 @@
* terms of the GNU General Public License, see the file COPYING.
*/
-#ifndef lint
-static char rcsid[] = "$Id: unpack.c,v 1.3 1993/05/28 17:56:07 jloup Exp $";
+#ifdef RCSID
+static char rcsid[] = "$Id: unpack.c,v 1.4 1993/06/11 19:25:36 jloup Exp $";
#endif
-#include <stdio.h>
-
#include "tailor.h"
#include "gzip.h"
#include "crypt.h"
@@ -26,11 +24,7 @@ static char rcsid[] = "$Id: unpack.c,v 1.3 1993/05/28 17:56:07 jloup Exp $";
#define LITERALS 256
/* Number of literals, excluding the End of Block (EOB) code */
-#ifdef SMALL_MEM
-# define MAX_PEEK 10
-#else
-# define MAX_PEEK 12
-#endif
+#define MAX_PEEK 12
/* Maximum number of 'peek' bits used to optimize traversal of the
* Huffman tree.
*/
@@ -54,7 +48,8 @@ local int parents[MAX_BITLEN+1]; /* Number of parents for each bit length */
local int peek_bits; /* Number of peek bits currently used */
-local uch prefix_len[1 << MAX_PEEK];
+/* local uch prefix_len[1 << MAX_PEEK]; */
+#define prefix_len outbuf
/* For each bit pattern b of peek_bits bits, prefix_len[b] is the length
* of the Huffman code starting with a prefix of b (upper bits), or 0
* if all codes of prefix b have more than peek_bits bits. It is not
@@ -62,6 +57,9 @@ local uch prefix_len[1 << MAX_PEEK];
* codes encountered in the input stream are short codes (by construction).
* So for most codes a single lookup will be necessary.
*/
+#if (1<<MAX_PEEK) > OUTBUFSIZ
+ error cannot overlay prefix_len and outbuf
+#endif
local ulg bitbuf;
/* Bits are added on the low part of bitbuf and read from the high part. */
@@ -221,7 +219,7 @@ int unpack(in, out)
do {
len++, mask = (mask<<1)+1;
look_bits(peek, len, mask);
- } while (peek < parents[len]);
+ } while (peek < (unsigned)parents[len]);
/* loop as long as peek is a parent node */
}
/* At this point, peek is the next complete code, of len bits */
@@ -234,7 +232,7 @@ int unpack(in, out)
flush_window();
Trace((stderr, "bytes_out %ld\n", bytes_out));
- if (orig_len != bytes_out) {
+ if (orig_len != (ulg)bytes_out) {
error("invalid compressed data--length error");
}
return OK;
diff --git a/gnu/usr.bin/gzip/unzip.c b/gnu/usr.bin/gzip/unzip.c
index a78622a..7e287a1 100644
--- a/gnu/usr.bin/gzip/unzip.c
+++ b/gnu/usr.bin/gzip/unzip.c
@@ -13,12 +13,10 @@
either deflated or stored.
*/
-#ifndef lint
-static char rcsid[] = "$Id: unzip.c,v 0.12 1993/05/28 17:56:23 jloup Exp $";
+#ifdef RCSID
+static char rcsid[] = "$Id: unzip.c,v 0.13 1993/06/10 13:29:00 jloup Exp $";
#endif
-#include <stdio.h>
-
#include "tailor.h"
#include "gzip.h"
#include "crypt.h"
@@ -176,7 +174,7 @@ int unzip(in, out)
if (orig_crc != updcrc(outbuf, 0)) {
error("invalid compressed data--crc error");
}
- if (orig_len != bytes_out) {
+ if (orig_len != (ulg)bytes_out) {
error("invalid compressed data--length error");
}
diff --git a/gnu/usr.bin/gzip/util.c b/gnu/usr.bin/gzip/util.c
index dc3695e..70375d8 100644
--- a/gnu/usr.bin/gzip/util.c
+++ b/gnu/usr.bin/gzip/util.c
@@ -4,11 +4,10 @@
* terms of the GNU General Public License, see the file COPYING.
*/
-#ifndef lint
-static char rcsid[] = "$Id: util.c,v 0.14 1993/05/27 10:31:52 jloup Exp $";
+#ifdef RCSID
+static char rcsid[] = "$Id: util.c,v 0.15 1993/06/15 09:04:13 jloup Exp $";
#endif
-#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
@@ -34,6 +33,26 @@ static char rcsid[] = "$Id: util.c,v 0.14 1993/05/27 10:31:52 jloup Exp $";
extern ulg crc_32_tab[]; /* crc table, defined below */
/* ===========================================================================
+ * Copy input to output unchanged: zcat == cat with --force.
+ * IN assertion: insize bytes have already been read in inbuf.
+ */
+int copy(in, out)
+ int in, out; /* input and output file descriptors */
+{
+ errno = 0;
+ while (insize != 0 && (int)insize != EOF) {
+ write_buf(out, (char*)inbuf, insize);
+ bytes_out += insize;
+ insize = read(in, (char*)inbuf, INBUFSIZ);
+ }
+ if ((int)insize == EOF && errno != 0) {
+ read_error();
+ }
+ bytes_in = bytes_out;
+ return OK;
+}
+
+/* ===========================================================================
* Run a set of bytes through the crc shift register. If s is a NULL
* pointer, then initialize the crc shift register contents instead.
* Return the current crc in either case.
@@ -69,10 +88,10 @@ void clear_bufs()
}
/* ===========================================================================
- * Fill the input buffer. This is called only when the buffer is empty
- * and at least one byte is really needed.
+ * Fill the input buffer. This is called only when the buffer is empty.
*/
-int fill_inbuf()
+int fill_inbuf(eof_ok)
+ int eof_ok; /* set if EOF acceptable as a result */
{
int len;
@@ -86,6 +105,7 @@ int fill_inbuf()
} while (insize < INBUFSIZ);
if (insize == 0) {
+ if (eof_ok) return EOF;
read_error();
}
bytes_in += (ulg)insize;
@@ -177,6 +197,26 @@ char *basename(fname)
return fname;
}
+/* ========================================================================
+ * Make a file name legal for file systems not allowing file names with
+ * multiple dots or starting with a dot (such as MSDOS), by changing
+ * all dots except the last one into underlines. A target dependent
+ * function can be used instead of this simple function by defining the macro
+ * MAKE_LEGAL_NAME in tailor.h and providing the function in a target
+ * dependent module.
+ */
+void make_simple_name(name)
+ char *name;
+{
+ char *p = strrchr(name, '.');
+ if (p == NULL) return;
+ if (p == name) p++;
+ do {
+ if (*--p == '.') *p = '_';
+ } while (p != name);
+}
+
+
#if defined(NO_STRING_H) && !defined(STDC_HEADERS)
/* Provide missing strspn and strcspn functions. */
@@ -261,7 +301,7 @@ char *add_envopt(argcp, argvp, env)
if (*p) *p++ = '\0'; /* mark it */
}
if (nargc == 0) {
- free(env); env = NULL;
+ free(env);
return NULL;
}
*argcp += nargc;
@@ -325,11 +365,12 @@ void write_error()
}
/* ========================================================================
- * Display compression ratio on stderr.
+ * Display compression ratio on the given stream on 6 characters.
*/
-void display_ratio(num, den)
+void display_ratio(num, den, file)
long num;
long den;
+ FILE *file;
{
long ratio; /* 1000 times the compression ratio */
@@ -341,10 +382,12 @@ void display_ratio(num, den)
ratio = num/(den/1000L);
}
if (ratio < 0) {
- putc('-', stderr);
+ putc('-', file);
ratio = -ratio;
+ } else {
+ putc(' ', file);
}
- fprintf(stderr, "%2ld.%ld%%", ratio / 10L, ratio % 10L);
+ fprintf(file, "%2ld.%1ld%%", ratio / 10L, ratio % 10L);
}
diff --git a/gnu/usr.bin/gzip/zdiff b/gnu/usr.bin/gzip/zdiff
index c21e7ef..84e65d3 100644
--- a/gnu/usr.bin/gzip/zdiff
+++ b/gnu/usr.bin/gzip/zdiff
@@ -1,4 +1,5 @@
#!/bin/sh
+# sh is buggy on RS/6000 AIX 3.2. Replace above line with #!/bin/ksh
# Zcmp and zdiff are used to invoke the cmp or the diff pro-
# gram on compressed files. All options specified are passed
@@ -8,6 +9,7 @@
# necessary) and fed to cmp or diff. The exit status from cmp
# or diff is preserved.
+PATH="/usr/local/bin:$PATH"; export PATH
prog=`echo $0 | sed 's|.*/||'`
case "$prog" in
*cmp) comp=${CMP-cmp} ;;
@@ -40,21 +42,21 @@ if test $# -eq 1; then
elif test $# -eq 2; then
case "$1" in
- *[-.]gz | *[-.][zZ] | *.t[ga]z)
+ *[-.]gz* | *[-.][zZ] | *.t[ga]z)
case "$2" in
- *[-.]gz | *[-.][zZ] | *.t[ga]z)
- F=`echo "$2" | sed 's|.*/||;s|[-.][zZtga]*$||'`
- gzip -cd "$2" > /tmp/"$F".$$
- gzip -cd "$1" | $comp $OPTIONS - /tmp/"$F".$$
+ *[-.]gz* | *[-.][zZ] | *.t[ga]z)
+ F=`echo "$2" | sed 's|.*/||;s|[-.][zZtga]*||'`
+ gzip -cdfq "$2" > /tmp/"$F".$$
+ gzip -cdfq "$1" | $comp $OPTIONS - /tmp/"$F".$$
STAT="$?"
/bin/rm -f /tmp/"$F".$$;;
- *) gzip -cd "$1" | $comp $OPTIONS - "$2"
+ *) gzip -cdfq "$1" | $comp $OPTIONS - "$2"
STAT="$?";;
esac;;
*) case "$2" in
- *[-.]gz | *[-.][zZ] | *.t[ga]z)
- gzip -cd "$2" | $comp $OPTIONS "$1" -
+ *[-.]gz* | *[-.][zZ] | *.t[ga]z)
+ gzip -cdfq "$2" | $comp $OPTIONS "$1" -
STAT="$?";;
*) $comp $OPTIONS "$1" "$2"
STAT="$?";;
diff --git a/gnu/usr.bin/gzip/zdiff.1 b/gnu/usr.bin/gzip/zdiff.1
index 44f9137..ea3bf41 100644
--- a/gnu/usr.bin/gzip/zdiff.1
+++ b/gnu/usr.bin/gzip/zdiff.1
@@ -35,7 +35,7 @@ or
.I diff
is preserved.
.SH "SEE ALSO"
-cmp(1), diff(1), zmore(1), znew(1), zforce(1), gzip(1), gzexe(1)
+cmp(1), diff(1), zmore(1), zgrep(1), znew(1), zforce(1), gzip(1), gzexe(1)
.SH BUGS
Messages from the
.I cmp
diff --git a/gnu/usr.bin/gzip/zforce b/gnu/usr.bin/gzip/zforce
index 9fe85ad..17258a4 100644
--- a/gnu/usr.bin/gzip/zforce
+++ b/gnu/usr.bin/gzip/zforce
@@ -5,6 +5,7 @@
# This can be useful for files with names truncated after a file transfer.
# 12345678901234 is renamed to 12345678901.gz
+PATH="/usr/local/bin:$PATH"; export PATH
x=`basename $0`
if test $# = 0; then
echo "force a '.gz' extension on all gzip files"
@@ -23,17 +24,18 @@ for i do
test `expr "$i" : '.*[.-]gz$'` -eq 0 || continue
test `expr "$i" : '.*[.]t[ag]z$'` -eq 0 || continue
- gzip -t "$i" 2>/dev/null || continue
+ if gzip -l < "$i" 2>/dev/null | grep '^defl' > /dev/null; then
- if test `expr "$i" : '^............'` -eq 12; then
- new=`expr "$i" : '\(.*\)...$`.gz
- else
- new="$i.gz"
+ if test `expr "$i" : '^............'` -eq 12; then
+ new=`expr "$i" : '\(.*\)...$`.gz
+ else
+ new="$i.gz"
+ fi
+ if mv "$i" "$new" 2>/dev/null; then
+ echo $i -- replaced with $new
+ continue
+ fi
+ res=1; echo ${x}: cannot rename $i to $new
fi
- if mv "$i" "$new" 2>/dev/null; then
- echo $i -- replaced with $new
- continue
- fi
- res=1; echo ${x}: cannot rename $i to $new
done
exit $res
diff --git a/gnu/usr.bin/gzip/zforce.1 b/gnu/usr.bin/gzip/zforce.1
index e231815..37c6aba 100644
--- a/gnu/usr.bin/gzip/zforce.1
+++ b/gnu/usr.bin/gzip/zforce.1
@@ -17,4 +17,4 @@ is truncated to make room for the .gz suffix. For example,
12345678901234 is renamed to 12345678901.gz. A file name such as foo.tgz
is left intact.
.SH "SEE ALSO"
-gzip(1), znew(1), zmore(1), zcmp(1), gzexe(1)
+gzip(1), znew(1), zmore(1), zgrep(1), zdiff(1), gzexe(1)
diff --git a/gnu/usr.bin/gzip/zgrep b/gnu/usr.bin/gzip/zgrep
new file mode 100644
index 0000000..bcc10cc
--- /dev/null
+++ b/gnu/usr.bin/gzip/zgrep
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+# zgrep -- a wrapper around a grep program that decompresses files as needed
+# Adapted from a version sent by Charles Levert <charles@comm.polymtl.ca>
+
+PATH="/usr/local/bin:$PATH"; export PATH
+
+prog=`echo $0 | sed 's|.*/||'`
+case "$prog" in
+ *egrep) grep=${EGREP-egrep} ;;
+ *fgrep) grep=${FGREP-fgrep} ;;
+ *) grep=${GREP-grep} ;;
+esac
+A=
+fileno=0
+pat=""
+while test $# -ne 0; do
+ case "$1" in
+ -e | -f) opt="$opt $1"; shift; pat="$1"
+ if test "$grep" = grep; then # grep is buggy with -e on SVR4
+ grep=egrep
+ fi;;
+ -*) opt="$opt $1";;
+ *) if test -z "$pat"; then
+ pat="$1"
+ else
+ fileno=`expr $fileno + 1`
+ eval A$fileno=\$1
+ A="$A \"\$A$fileno\""
+ fi
+ ;;
+ esac
+ shift
+done
+
+if test -z "$pat"; then
+ echo "grep through gzip files"
+ echo "usage: $prog [grep_options] pattern [files]"
+ exit 1
+fi
+
+list=0
+silent=0
+op=`echo "$opt" | sed -e 's/ //g' -e 's/-//g'`
+case "$op" in
+ *l*) list=1
+esac
+case "$op" in
+ *h*) silent=1
+esac
+
+if test $fileno -eq 0; then
+ gzip -cdfq | $grep $opt "$pat"
+ exit $?
+fi
+eval set "$A" # files in $1, $2 ...
+
+res=0
+for i do
+ if test $list -eq 1; then
+ gzip -cdfq "$i" | $grep $opt "$pat" > /dev/null && echo $i
+ r=$?
+ elif test $# -eq 1 -o $silent -eq 1; then
+ gzip -cdfq "$i" | $grep $opt "$pat"
+ r=$?
+ else
+ gzip -cdfq "$i" | $grep $opt "$pat" | sed "s|^|${i}:|"
+ r=$?
+ fi
+ test "$r" -ne 0 && res="$r"
+done
+exit $res
diff --git a/gnu/usr.bin/gzip/zgrep.1 b/gnu/usr.bin/gzip/zgrep.1
new file mode 100644
index 0000000..a52a88a
--- /dev/null
+++ b/gnu/usr.bin/gzip/zgrep.1
@@ -0,0 +1,44 @@
+.TH ZGREP 1
+.SH NAME
+zgrep \- search possibly compressed files for a regular expression
+.SH SYNOPSIS
+.B zgrep
+[ grep_options ]
+.BI [\ -e\ ] " pattern"
+.IR filename ".\|.\|."
+.SH DESCRIPTION
+.IR Zgrep
+is used to invoke the
+.I grep
+on compress'ed or gzip'ed files. All options specified are passed directly to
+.I grep.
+If no file is specified, then the standard input is decompressed
+if necessary and fed to grep.
+Otherwise the given files are uncompressed if necessary and fed to
+.I grep.
+.PP
+If
+.I zgrep
+is invoked as
+.I zegrep
+or
+.I zfgrep
+then
+.I egrep
+or
+.I fgrep
+is used instead of
+.I grep.
+If the GREP environment variable is set,
+.I zgrep
+uses it as the
+.I grep
+program to be invoked. For example:
+
+ for sh: GREP=fgrep zgrep string files
+ for csh: (setenv GREP fgrep; zgrep string files)
+.SH AUTHOR
+Charles Levert (charles@comm.polymtl.ca)
+.SH "SEE ALSO"
+grep(1), egrep(1), fgrep(1), zdiff(1), zmore(1), znew(1), zforce(1),
+gzip(1), gzexe(1)
diff --git a/gnu/usr.bin/gzip/zip.c b/gnu/usr.bin/gzip/zip.c
index 5bf172f..507d161 100644
--- a/gnu/usr.bin/gzip/zip.c
+++ b/gnu/usr.bin/gzip/zip.c
@@ -4,12 +4,11 @@
* terms of the GNU General Public License, see the file COPYING.
*/
-#ifndef lint
-static char rcsid[] = "$Id: zip.c,v 0.16 1993/05/28 14:51:17 jloup Exp $";
+#ifdef RCSID
+static char rcsid[] = "$Id: zip.c,v 0.17 1993/06/10 13:29:25 jloup Exp $";
#endif
#include <ctype.h>
-#include <stdio.h>
#include <sys/types.h>
#include "tailor.h"
diff --git a/gnu/usr.bin/gzip/zmore b/gnu/usr.bin/gzip/zmore
index 64f7b1b..ca933c7 100644
--- a/gnu/usr.bin/gzip/zmore
+++ b/gnu/usr.bin/gzip/zmore
@@ -1,22 +1,31 @@
#!/bin/sh
+PATH="/usr/local/bin:$PATH"; export PATH
if test "`echo -n a`" = "-n a"; then
# looks like a SysV system:
n1=''; n2='\c'
else
n1='-n'; n2=''
fi
+oldtty=`stty -g 2>/dev/null`
if stty -cbreak 2>/dev/null; then
cb='cbreak'; ncb='-cbreak'
else
# 'stty min 1' resets eof to ^a on both SunOS and SysV!
cb='min 1 -icanon'; ncb='icanon eof ^d'
fi
-oldtty=`stty -g`
-trap 'stty -g $oldtty 2>/dev/null; exit' 0 2 3 5 10 13 15
+if test $? -eq 0 -a -n "$oldtty"; then
+ trap 'stty $oldtty 2>/dev/null; exit' 0 2 3 5 10 13 15
+else
+ trap 'stty $ncb echo 2>/dev/null; exit' 0 2 3 5 10 13 15
+fi
if test $# = 0; then
- gzip -cd | eval ${PAGER-more}
+ if test -t 0; then
+ echo usage: zmore files...
+ else
+ gzip -cdfq | eval ${PAGER-more}
+ fi
else
FIRST=1
for FILE
@@ -33,7 +42,7 @@ else
fi
if test "$ANS" != 's'; then
echo "------> $FILE <------"
- gzip -cd "$FILE" | eval ${PAGER-more}
+ gzip -cdfq "$FILE" | eval ${PAGER-more}
fi
if test -t; then
FIRST=0
diff --git a/gnu/usr.bin/gzip/zmore.1 b/gnu/usr.bin/gzip/zmore.1
index 08b49fb..f7f1843 100644
--- a/gnu/usr.bin/gzip/zmore.1
+++ b/gnu/usr.bin/gzip/zmore.1
@@ -6,9 +6,20 @@ zmore \- file perusal filter for crt viewing of compressed text
[ name ... ]
.SH DESCRIPTION
.I Zmore
-is a filter which allows examination of compressed text files
+is a filter which allows examination of compressed or plain text files
one screenful at a time on a soft-copy terminal.
-It normally pauses after each screenful, printing --More--
+.I zmore
+works on files compressed with
+.I compress, pack
+or
+.I gzip,
+and also on uncompressed files.
+If a file does not exist,
+.I zmore
+looks for a file of the same name with the addition of a .gz, .z or .Z suffix.
+.PP
+.I Zmore
+normally pauses after each screenful, printing --More--
at the bottom of the screen.
If the user then types a carriage return, one more line is displayed.
If the user hits a space,
@@ -131,4 +142,4 @@ except that a header is printed before each file.
.DT
/etc/termcap Terminal data base
.SH "SEE ALSO"
-more(1), gzip(1), zcmp(1), znew(1), zforce(1), gzexe(1)
+more(1), gzip(1), zdiff(1), zgrep(1), znew(1), zforce(1), gzexe(1)
diff --git a/gnu/usr.bin/gzip/znew b/gnu/usr.bin/gzip/znew
index fcbc466..5c832e8 100644
--- a/gnu/usr.bin/gzip/znew
+++ b/gnu/usr.bin/gzip/znew
@@ -1,5 +1,6 @@
#!/bin/sh
+PATH="/usr/local/bin:$PATH"; export PATH
check=0
pipe=0
opt=
@@ -25,35 +26,39 @@ if test -z "$cpmod" && ${TOUCH-touch} -r $tmp.1 $tmp.2 2>/dev/null; then
cpmodarg="-r"
warn="(does not preserve file modes)"
fi
-rm -f $tmp.[12]
-A=
-fileno=0
+# check if GZIP env. variable uses -S or --suffix
+gzip -q $tmp.1
+ext=`echo $tmp.1* | sed "s|$tmp.1||"`
+rm -f $tmp.[12]*
+if test -z "$ext"; then
+ echo znew: error determining gzip extension
+ exit 1
+fi
+if test "$ext" = ".Z"; then
+ echo znew: cannot use .Z as gzip extension.
+ exit 1
+fi
for arg
do
case "$arg" in
- -*) opt="$opt $arg";;
- *) fileno=`expr $fileno + 1`
- eval A$fileno=\$arg
- A="$A \"\$A$fileno\""
- ;;
+ -*) opt="$opt $arg"; shift;;
+ *) break;;
esac
done
-if test $fileno -eq 0; then
- echo 'recompress .Z files into .gz (gzip) files'
- echo usage: `echo $0 | sed 's,^.*/,,'` "[-tv9P]" file.Z...
+if test $# -eq 0; then
+ echo "recompress .Z files into $ext (gzip) files"
+ echo usage: `echo $0 | sed 's,^.*/,,'` "[-tv9KP]" file.Z...
echo " -t tests the new files before deleting originals"
echo " -v be verbose"
echo " -9 use the slowest compression method (optimal compression)"
- echo " -K keep a .Z file when it is smaller than the .gz file"
+ echo " -K keep a .Z file when it is smaller than the $ext file"
echo " -P use pipes for the conversion $warn"
exit 1
fi
-eval set "$A" # the files are now in $1, $2, ...
-
opt=`echo "$opt" | sed -e 's/ //g' -e 's/-//g'`
case "$opt" in
*t*) check=1; opt=`echo "$opt" | sed 's/t//g'`
@@ -76,9 +81,9 @@ for i do
fi
test $keep -eq 1 && old=`wc -c < "$n.Z"`
if test $pipe -eq 1; then
- if gzip -d < "$n.Z" | gzip $opt > "$n.gz"; then
+ if gzip -d < "$n.Z" | gzip $opt > "$n$ext"; then
# Copy file attributes from old file to new one, if possible.
- test -n "$cpmod" && $cpmod $cpmodarg "$n.Z" "$n.gz" 2> /dev/null
+ test -n "$cpmod" && $cpmod $cpmodarg "$n.Z" "$n$ext" 2> /dev/null
else
echo error while recompressing $n.Z
res=1; continue
@@ -112,25 +117,25 @@ for i do
res=1; continue
fi
fi
- test $keep -eq 1 && new=`wc -c < "$n.gz"`
+ test $keep -eq 1 && new=`wc -c < "$n$ext"`
if test $keep -eq 1 -a `expr \( $old + $block - 1 \) / $block` -lt \
`expr \( $new + $block - 1 \) / $block`; then
if test $pipe -eq 1; then
- rm -f "$n.gz"
+ rm -f "$n$ext"
elif test $check -eq 1; then
- mv "$n.$$" "$n.Z" && rm -f "$n.gz"
+ mv "$n.$$" "$n.Z" && rm -f "$n$ext"
else
- gzip -d "$n.gz" && compress "$n" && rm -f "$n.gz"
+ gzip -d "$n$ext" && compress "$n" && rm -f "$n$ext"
fi
- echo "$n.Z smaller than $n.gz -- unchanged"
+ echo "$n.Z smaller than $n$ext -- unchanged"
elif test $check -eq 1; then
- if gzip -t "$n.gz" ; then
+ if gzip -t "$n$ext" ; then
rm -f "$n.$$" "$n.Z"
else
test $pipe -eq 0 && mv "$n.$$" "$n.Z"
- rm -f "$n.gz"
- echo error while testing $n.gz, $n.Z unchanged
+ rm -f "$n$ext"
+ echo error while testing $n$ext, $n.Z unchanged
res=1; continue
fi
elif test $pipe -eq 1; then
diff --git a/gnu/usr.bin/gzip/znew.1 b/gnu/usr.bin/gzip/znew.1
index 644873b..5cfb472 100644
--- a/gnu/usr.bin/gzip/znew.1
+++ b/gnu/usr.bin/gzip/znew.1
@@ -7,7 +7,10 @@ znew \- recompress .Z files to .gz files
.SH DESCRIPTION
.I Znew
recompresses files from .Z (compress) format to .gz (gzip) format.
+If you want to recompress a file already in gzip format, rename the file
+to force a .Z extension then apply znew.
.SH OPTIONS
+.TP
.B \-f
Force recompression from .Z to .gz format even if a .gz file already exists.
.TP
@@ -26,7 +29,7 @@ Use pipes for the conversion to reduce disk space usage.
.B \-K
Keep a .Z file when it is smaller than the .gz file
.SH "SEE ALSO"
-gzip(1), zmore(1), zcmp(1), zforce(1), gzexe(1), compress(1)
+gzip(1), zmore(1), zdiff(1), zgrep(1), zforce(1), gzexe(1), compress(1)
.SH BUGS
.I Znew
does not maintain the time stamp with the -P option if
diff --git a/gnu/usr.bin/ld/Makefile b/gnu/usr.bin/ld/Makefile
new file mode 100644
index 0000000..6b484e6
--- /dev/null
+++ b/gnu/usr.bin/ld/Makefile
@@ -0,0 +1,19 @@
+# $Id: Makefile,v 1.14 1994/02/14 10:02:13 rgrimes Exp $
+#
+
+PROG= ld
+SRCS= ld.c symbol.c lib.c shlib.c warnings.c etc.c rrs.c xbits.c md.c
+CFLAGS += -I$(.CURDIR) -I$(.CURDIR)/$(MACHINE)
+
+LDADD+= -lgnumalloc
+DPADD+= /usr/lib/libgnumalloc.a
+LDFLAGS+= -Xlinker -Bstatic
+
+SUBDIR= ldconfig ldd
+.if !defined(NOPIC)
+SUBDIR+= rtld
+.endif
+
+.PATH: $(.CURDIR)/$(MACHINE)
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/ld/TODO b/gnu/usr.bin/ld/TODO
new file mode 100644
index 0000000..d37a245
--- /dev/null
+++ b/gnu/usr.bin/ld/TODO
@@ -0,0 +1,4 @@
+- Make C++ shared libs work (properly resolve ctor and dtor lists)
+- Make -r and -X work together for PIC .o files.
+
+- Avoid duplicatating shared library specifications in link_objects
diff --git a/gnu/usr.bin/ld/cplus-dem.c b/gnu/usr.bin/ld/cplus-dem.c
new file mode 100644
index 0000000..808db5e
--- /dev/null
+++ b/gnu/usr.bin/ld/cplus-dem.c
@@ -0,0 +1,975 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ */
+
+#ifndef lint
+/*static char sccsid[] = "from: @(#)cplus-dem.c 5.4 (Berkeley) 4/30/91";*/
+static char rcsid[] = "$Id: cplus-dem.c,v 1.2 1993/08/01 18:46:58 mycroft Exp $";
+#endif /* not lint */
+
+/* Demangler for GNU C++
+ Copyright (C) 1989 Free Software Foundation, Inc.
+ written by James Clark (jjc@jclark.uucp)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This is for g++ 1.36.1 (November 6 version). It will probably
+ require changes for any other version.
+
+ Modified for g++ 1.36.2 (November 18 version). */
+
+/* This file exports one function
+
+ char *cplus_demangle (const char *name)
+
+ If `name' is a mangled function name produced by g++, then
+ a pointer to a malloced string giving a C++ representation
+ of the name will be returned; otherwise NULL will be returned.
+ It is the caller's responsibility to free the string which
+ is returned.
+
+ For example,
+
+ cplus_demangle ("_foo__1Ai")
+
+ returns
+
+ "A::foo(int)"
+
+ This file imports xmalloc and xrealloc, which are like malloc and
+ realloc except that they generate a fatal error if there is no
+ available memory. */
+
+/* #define nounderscore 1 /* define this is names don't start with _ */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef USG
+#include <memory.h>
+#include <string.h>
+#else
+#include <strings.h>
+#define memcpy(s1, s2, n) bcopy ((s2), (s1), (n))
+#define memcmp(s1, s2, n) bcmp ((s2), (s1), (n))
+#define strchr index
+#define strrchr rindex
+#endif
+
+#ifndef __STDC__
+#define const
+#endif
+
+#ifdef __STDC__
+extern char *cplus_demangle (const char *type);
+#else
+extern char *cplus_demangle ();
+#endif
+
+#ifdef __STDC__
+extern char *xmalloc (int);
+extern char *xrealloc (char *, int);
+#else
+extern char *xmalloc ();
+extern char *xrealloc ();
+#endif
+
+static char **typevec = 0;
+static int ntypes = 0;
+static int typevec_size = 0;
+
+static struct {
+ const char *in;
+ const char *out;
+} optable[] = {
+ "new", " new",
+ "delete", " delete",
+ "ne", "!=",
+ "eq", "==",
+ "ge", ">=",
+ "gt", ">",
+ "le", "<=",
+ "lt", "<",
+ "plus", "+",
+ "minus", "-",
+ "mult", "*",
+ "convert", "+", /* unary + */
+ "negate", "-", /* unary - */
+ "trunc_mod", "%",
+ "trunc_div", "/",
+ "truth_andif", "&&",
+ "truth_orif", "||",
+ "truth_not", "!",
+ "postincrement", "++",
+ "postdecrement", "--",
+ "bit_ior", "|",
+ "bit_xor", "^",
+ "bit_and", "&",
+ "bit_not", "~",
+ "call", "()",
+ "cond", "?:",
+ "alshift", "<<",
+ "arshift", ">>",
+ "component", "->",
+ "indirect", "*",
+ "method_call", "->()",
+ "addr", "&", /* unary & */
+ "array", "[]",
+ "nop", "", /* for operator= */
+};
+
+/* Beware: these aren't '\0' terminated. */
+
+typedef struct {
+ char *b; /* pointer to start of string */
+ char *p; /* pointer after last character */
+ char *e; /* pointer after end of allocated space */
+} string;
+
+#ifdef __STDC__
+static void string_need (string *s, int n);
+static void string_delete (string *s);
+static void string_init (string *s);
+static void string_clear (string *s);
+static int string_empty (string *s);
+static void string_append (string *p, const char *s);
+static void string_appends (string *p, string *s);
+static void string_appendn (string *p, const char *s, int n);
+static void string_prepend (string *p, const char *s);
+#if 0
+static void string_prepends (string *p, string *s);
+#endif
+static void string_prependn (string *p, const char *s, int n);
+static int get_count (const char **type, int *count);
+static int do_args (const char **type, string *decl);
+static int do_type (const char **type, string *result);
+static int do_arg (const char **type, string *result);
+static int do_args (const char **type, string *decl);
+static void munge_function_name (string *name);
+static void remember_type (const char *type, int len);
+#else
+static void string_need ();
+static void string_delete ();
+static void string_init ();
+static void string_clear ();
+static int string_empty ();
+static void string_append ();
+static void string_appends ();
+static void string_appendn ();
+static void string_prepend ();
+static void string_prepends ();
+static void string_prependn ();
+static int get_count ();
+static int do_args ();
+static int do_type ();
+static int do_arg ();
+static int do_args ();
+static void munge_function_name ();
+static void remember_type ();
+#endif
+
+char *
+cplus_demangle (type)
+ const char *type;
+{
+ string decl;
+ int n;
+ int success = 0;
+ int constructor = 0;
+ int const_flag = 0;
+ int i;
+ const char *p;
+#ifndef LONGERNAMES
+ const char *premangle;
+#endif
+
+ if (type == NULL || *type == '\0')
+ return NULL;
+#ifndef nounderscore
+ if (*type++ != '_')
+ return NULL;
+#endif
+ p = type;
+ while (*p != '\0' && !(*p == '_' && p[1] == '_'))
+ p++;
+ if (*p == '\0')
+ {
+ /* destructor */
+ if (type[0] == '_' && type[1] == '$' && type[2] == '_')
+ {
+ int n = (strlen (type) - 3)*2 + 3 + 2 + 1;
+ char *tem = (char *) xmalloc (n);
+ strcpy (tem, type + 3);
+ strcat (tem, "::~");
+ strcat (tem, type + 3);
+ strcat (tem, "()");
+ return tem;
+ }
+ /* static data member */
+ if (*type != '_' && (p = strchr (type, '$')) != NULL)
+ {
+ int n = strlen (type) + 2;
+ char *tem = (char *) xmalloc (n);
+ memcpy (tem, type, p - type);
+ strcpy (tem + (p - type), "::");
+ strcpy (tem + (p - type) + 2, p + 1);
+ return tem;
+ }
+ /* virtual table */
+ if (type[0] == '_' && type[1] == 'v' && type[2] == 't' && type[3] == '$')
+ {
+ int n = strlen (type + 4) + 14 + 1;
+ char *tem = (char *) xmalloc (n);
+ strcpy (tem, type + 4);
+ strcat (tem, " virtual table");
+ return tem;
+ }
+ return NULL;
+ }
+
+ string_init (&decl);
+
+ if (p == type)
+ {
+ if (!isdigit (p[2]))
+ {
+ string_delete (&decl);
+ return NULL;
+ }
+ constructor = 1;
+ }
+ else
+ {
+ string_appendn (&decl, type, p - type);
+ munge_function_name (&decl);
+ }
+ p += 2;
+
+#ifndef LONGERNAMES
+ premangle = p;
+#endif
+ switch (*p)
+ {
+ case 'C':
+ /* a const member function */
+ if (!isdigit (p[1]))
+ {
+ string_delete (&decl);
+ return NULL;
+ }
+ p += 1;
+ const_flag = 1;
+ /* fall through */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ n = 0;
+ do
+ {
+ n *= 10;
+ n += *p - '0';
+ p += 1;
+ }
+ while (isdigit (*p));
+ if (strlen (p) < n)
+ {
+ string_delete (&decl);
+ return NULL;
+ }
+ if (constructor)
+ {
+ string_appendn (&decl, p, n);
+ string_append (&decl, "::");
+ string_appendn (&decl, p, n);
+ }
+ else
+ {
+ string_prepend (&decl, "::");
+ string_prependn (&decl, p, n);
+ }
+ p += n;
+#ifndef LONGERNAMES
+ remember_type (premangle, p - premangle);
+#endif
+ success = do_args (&p, &decl);
+ if (const_flag)
+ string_append (&decl, " const");
+ break;
+ case 'F':
+ p += 1;
+ success = do_args (&p, &decl);
+ break;
+ }
+
+ for (i = 0; i < ntypes; i++)
+ if (typevec[i] != NULL)
+ free (typevec[i]);
+ ntypes = 0;
+ if (typevec != NULL)
+ {
+ free ((char *)typevec);
+ typevec = NULL;
+ typevec_size = 0;
+ }
+
+ if (success)
+ {
+ string_appendn (&decl, "", 1);
+ return decl.b;
+ }
+ else
+ {
+ string_delete (&decl);
+ return NULL;
+ }
+}
+
+static int
+get_count (type, count)
+ const char **type;
+ int *count;
+{
+ if (!isdigit (**type))
+ return 0;
+ *count = **type - '0';
+ *type += 1;
+ /* see flush_repeats in cplus-method.c */
+ if (isdigit (**type))
+ {
+ const char *p = *type;
+ int n = *count;
+ do
+ {
+ n *= 10;
+ n += *p - '0';
+ p += 1;
+ }
+ while (isdigit (*p));
+ if (*p == '_')
+ {
+ *type = p + 1;
+ *count = n;
+ }
+ }
+ return 1;
+}
+
+/* result will be initialised here; it will be freed on failure */
+
+static int
+do_type (type, result)
+ const char **type;
+ string *result;
+{
+ int n;
+ int done;
+ int non_empty = 0;
+ int success;
+ string decl;
+ const char *remembered_type;
+
+ string_init (&decl);
+ string_init (result);
+
+ done = 0;
+ success = 1;
+ while (success && !done)
+ {
+ int member;
+ switch (**type)
+ {
+ case 'P':
+ *type += 1;
+ string_prepend (&decl, "*");
+ break;
+
+ case 'R':
+ *type += 1;
+ string_prepend (&decl, "&");
+ break;
+
+ case 'T':
+ *type += 1;
+ if (!get_count (type, &n) || n >= ntypes)
+ success = 0;
+ else
+ {
+ remembered_type = typevec[n];
+ type = &remembered_type;
+ }
+ break;
+
+ case 'F':
+ *type += 1;
+ if (!string_empty (&decl) && decl.b[0] == '*')
+ {
+ string_prepend (&decl, "(");
+ string_append (&decl, ")");
+ }
+ if (!do_args (type, &decl) || **type != '_')
+ success = 0;
+ else
+ *type += 1;
+ break;
+
+ case 'M':
+ case 'O':
+ {
+ int constp = 0;
+ int volatilep = 0;
+
+ member = **type == 'M';
+ *type += 1;
+ if (!isdigit (**type))
+ {
+ success = 0;
+ break;
+ }
+ n = 0;
+ do
+ {
+ n *= 10;
+ n += **type - '0';
+ *type += 1;
+ }
+ while (isdigit (**type));
+ if (strlen (*type) < n)
+ {
+ success = 0;
+ break;
+ }
+ string_append (&decl, ")");
+ string_prepend (&decl, "::");
+ string_prependn (&decl, *type, n);
+ string_prepend (&decl, "(");
+ *type += n;
+ if (member)
+ {
+ if (**type == 'C')
+ {
+ *type += 1;
+ constp = 1;
+ }
+ if (**type == 'V')
+ {
+ *type += 1;
+ volatilep = 1;
+ }
+ if (*(*type)++ != 'F')
+ {
+ success = 0;
+ break;
+ }
+ }
+ if ((member && !do_args (type, &decl)) || **type != '_')
+ {
+ success = 0;
+ break;
+ }
+ *type += 1;
+ if (constp)
+ {
+ if (non_empty)
+ string_append (&decl, " ");
+ else
+ non_empty = 1;
+ string_append (&decl, "const");
+ }
+ if (volatilep)
+ {
+ if (non_empty)
+ string_append (&decl, " ");
+ else
+ non_empty = 1;
+ string_append (&decl, "volatilep");
+ }
+ break;
+ }
+
+ case 'C':
+ if ((*type)[1] == 'P')
+ {
+ *type += 1;
+ if (!string_empty (&decl))
+ string_prepend (&decl, " ");
+ string_prepend (&decl, "const");
+ break;
+ }
+
+ /* fall through */
+ default:
+ done = 1;
+ break;
+ }
+ }
+
+ done = 0;
+ non_empty = 0;
+ while (success && !done)
+ {
+ switch (**type)
+ {
+ case 'C':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ else
+ non_empty = 1;
+ string_append (result, "const");
+ break;
+ case 'U':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ else
+ non_empty = 1;
+ string_append (result, "unsigned");
+ break;
+ case 'V':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ else
+ non_empty = 1;
+ string_append (result, "volatile");
+ break;
+ default:
+ done = 1;
+ break;
+ }
+ }
+
+ if (success)
+ switch (**type)
+ {
+ case '\0':
+ case '_':
+ break;
+ case 'v':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "void");
+ break;
+ case 'x':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "long long");
+ break;
+ case 'l':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "long");
+ break;
+ case 'i':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "int");
+ break;
+ case 's':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "short");
+ break;
+ case 'c':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "char");
+ break;
+ case 'r':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "long double");
+ break;
+ case 'd':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "double");
+ break;
+ case 'f':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "float");
+ break;
+ case 'G':
+ *type += 1;
+ if (!isdigit (**type))
+ {
+ success = 0;
+ break;
+ }
+ /* fall through */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ n = 0;
+ do
+ {
+ n *= 10;
+ n += **type - '0';
+ *type += 1;
+ }
+ while (isdigit (**type));
+ if (strlen (*type) < n)
+ {
+ success = 0;
+ break;
+ }
+ if (non_empty)
+ string_append (result, " ");
+ string_appendn (result, *type, n);
+ *type += n;
+ break;
+ default:
+ success = 0;
+ break;
+ }
+
+ if (success)
+ {
+ if (!string_empty (&decl))
+ {
+ string_append (result, " ");
+ string_appends (result, &decl);
+ }
+ string_delete (&decl);
+ return 1;
+ }
+ else
+ {
+ string_delete (&decl);
+ string_delete (result);
+ return 0;
+ }
+}
+
+/* `result' will be initialised in do_type; it will be freed on failure */
+
+static int
+do_arg (type, result)
+ const char **type;
+ string *result;
+{
+ const char *start = *type;
+
+ if (!do_type (type, result))
+ return 0;
+ remember_type (start, *type - start);
+ return 1;
+}
+
+static void
+remember_type (start, len)
+ const char *start;
+ int len;
+{
+ char *tem;
+
+ if (ntypes >= typevec_size)
+ {
+ if (typevec_size == 0)
+ {
+ typevec_size = 3;
+ typevec = (char **) xmalloc (sizeof (char*)*typevec_size);
+ }
+ else
+ {
+ typevec_size *= 2;
+ typevec = (char **) xrealloc ((char *)typevec, sizeof (char*)*typevec_size);
+ }
+ }
+ tem = (char *) xmalloc (len + 1);
+ memcpy (tem, start, len);
+ tem[len] = '\0';
+ typevec[ntypes++] = tem;
+}
+
+/* `decl' must be already initialised, usually non-empty;
+ it won't be freed on failure */
+
+static int
+do_args (type, decl)
+ const char **type;
+ string *decl;
+{
+ string arg;
+ int need_comma = 0;
+
+ string_append (decl, "(");
+
+ while (**type != '_' && **type != '\0' && **type != 'e' && **type != 'v')
+ {
+ if (**type == 'N')
+ {
+ int r;
+ int t;
+ *type += 1;
+ if (!get_count (type, &r) || !get_count (type, &t) || t >= ntypes)
+ return 0;
+ while (--r >= 0)
+ {
+ const char *tem = typevec[t];
+ if (need_comma)
+ string_append (decl, ", ");
+ if (!do_arg (&tem, &arg))
+ return 0;
+ string_appends (decl, &arg);
+ string_delete (&arg);
+ need_comma = 1;
+ }
+ }
+ else
+ {
+ if (need_comma)
+ string_append (decl, ", ");
+ if (!do_arg (type, &arg))
+ return 0;
+ string_appends (decl, &arg);
+ string_delete (&arg);
+ need_comma = 1;
+ }
+ }
+
+ if (**type == 'v')
+ *type += 1;
+ else if (**type == 'e')
+ {
+ *type += 1;
+ if (need_comma)
+ string_append (decl, ",");
+ string_append (decl, "...");
+ }
+
+ string_append (decl, ")");
+ return 1;
+}
+
+static void
+munge_function_name (name)
+ string *name;
+{
+ if (!string_empty (name) && name->p - name->b >= 3
+ && name->b[0] == 'o' && name->b[1] == 'p' && name->b[2] == '$')
+ {
+ int i;
+ /* see if it's an assignment expression */
+ if (name->p - name->b >= 10 /* op$assign_ */
+ && memcmp (name->b + 3, "assign_", 7) == 0)
+ {
+ for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++)
+ {
+ int len = name->p - name->b - 10;
+ if (strlen (optable[i].in) == len
+ && memcmp (optable[i].in, name->b + 10, len) == 0)
+ {
+ string_clear (name);
+ string_append (name, "operator");
+ string_append (name, optable[i].out);
+ string_append (name, "=");
+ return;
+ }
+ }
+ }
+ else
+ {
+ for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++)
+ {
+ int len = name->p - name->b - 3;
+ if (strlen (optable[i].in) == len
+ && memcmp (optable[i].in, name->b + 3, len) == 0)
+ {
+ string_clear (name);
+ string_append (name, "operator");
+ string_append (name, optable[i].out);
+ return;
+ }
+ }
+ }
+ return;
+ }
+ else if (!string_empty (name) && name->p - name->b >= 5
+ && memcmp (name->b, "type$", 5) == 0)
+ {
+ /* type conversion operator */
+ string type;
+ const char *tem = name->b + 5;
+ if (do_type (&tem, &type))
+ {
+ string_clear (name);
+ string_append (name, "operator ");
+ string_appends (name, &type);
+ string_delete (&type);
+ return;
+ }
+ }
+}
+
+/* a mini string-handling package */
+
+static void
+string_need (s, n)
+ string *s;
+ int n;
+{
+ if (s->b == NULL)
+ {
+ if (n < 32)
+ n = 32;
+ s->p = s->b = (char *) xmalloc (n);
+ s->e = s->b + n;
+ }
+ else if (s->e - s->p < n)
+ {
+ int tem = s->p - s->b;
+ n += tem;
+ n *= 2;
+ s->b = (char *) xrealloc (s->b, n);
+ s->p = s->b + tem;
+ s->e = s->b + n;
+ }
+}
+
+static void
+string_delete (s)
+ string *s;
+{
+ if (s->b != NULL)
+ {
+ free (s->b);
+ s->b = s->e = s->p = NULL;
+ }
+}
+
+static void
+string_init (s)
+ string *s;
+{
+ s->b = s->p = s->e = NULL;
+}
+
+static void
+string_clear (s)
+ string *s;
+{
+ s->p = s->b;
+}
+
+static int
+string_empty (s)
+ string *s;
+{
+ return s->b == s->p;
+}
+
+static void
+string_append (p, s)
+ string *p;
+ const char *s;
+{
+ int n;
+ if (s == NULL || *s == '\0')
+ return;
+ n = strlen (s);
+ string_need (p, n);
+ memcpy (p->p, s, n);
+ p->p += n;
+}
+
+static void
+string_appends (p, s)
+ string *p, *s;
+{
+ int n;
+ if (s->b == s->p)
+ return;
+ n = s->p - s->b;
+ string_need (p, n);
+ memcpy (p->p, s->b, n);
+ p->p += n;
+}
+
+static void
+string_appendn (p, s, n)
+ string *p;
+ const char *s;
+ int n;
+{
+ if (n == 0)
+ return;
+ string_need (p, n);
+ memcpy (p->p, s, n);
+ p->p += n;
+}
+
+static void
+string_prepend (p, s)
+ string *p;
+ const char *s;
+{
+ if (s == NULL || *s == '\0')
+ return;
+ string_prependn (p, s, strlen (s));
+}
+
+#if 0
+static void
+string_prepends (p, s)
+ string *p, *s;
+{
+ if (s->b == s->p)
+ return;
+ string_prependn (p, s->b, s->p - s->b);
+}
+#endif
+
+static void
+string_prependn (p, s, n)
+ string *p;
+ const char *s;
+ int n;
+{
+ char *q;
+
+ if (n == 0)
+ return;
+ string_need (p, n);
+ for (q = p->p - 1; q >= p->b; q--)
+ q[n] = q[0];
+ memcpy (p->b, s, n);
+ p->p += n;
+}
diff --git a/gnu/usr.bin/ld/etc.c b/gnu/usr.bin/ld/etc.c
new file mode 100644
index 0000000..59e0a18
--- /dev/null
+++ b/gnu/usr.bin/ld/etc.c
@@ -0,0 +1,66 @@
+/*
+ * $Id: etc.c,v 1.7 1994/02/13 20:41:05 jkh Exp $
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Like malloc but get fatal error if memory is exhausted.
+ */
+void *
+xmalloc(size)
+ size_t size;
+{
+ register void *result = (void *)malloc(size);
+
+ if (!result)
+ errx(1, "virtual memory exhausted");
+
+ return result;
+}
+
+/*
+ * Like realloc but get fatal error if memory is exhausted.
+ */
+void *
+xrealloc(ptr, size)
+ void *ptr;
+ size_t size;
+{
+ register void *result;
+
+ if (ptr == NULL)
+ result = (void *)malloc(size);
+ else
+ result = (void *)realloc(ptr, size);
+
+ if (!result)
+ errx(1, "virtual memory exhausted");
+
+ return result;
+}
+
+/*
+ * Return a newly-allocated string whose contents concatenate
+ * the strings S1, S2, S3.
+ */
+char *
+concat(s1, s2, s3)
+ const char *s1, *s2, *s3;
+{
+ register int len1 = strlen(s1),
+ len2 = strlen(s2),
+ len3 = strlen(s3);
+
+ register char *result = (char *)xmalloc(len1 + len2 + len3 + 1);
+
+ strcpy(result, s1);
+ strcpy(result + len1, s2);
+ strcpy(result + len1 + len2, s3);
+ result[len1 + len2 + len3] = 0;
+
+ return result;
+}
+
diff --git a/gnu/usr.bin/ld/i386/md-static-funcs.c b/gnu/usr.bin/ld/i386/md-static-funcs.c
new file mode 100644
index 0000000..4741685
--- /dev/null
+++ b/gnu/usr.bin/ld/i386/md-static-funcs.c
@@ -0,0 +1,17 @@
+/*
+ * $Id: md-static-funcs.c,v 1.2 1993/12/08 10:14:44 pk Exp $
+ *
+ * Called by ld.so when onanating.
+ * This *must* be a static function, so it is not called through a jmpslot.
+ */
+
+static void
+md_relocate_simple(r, relocation, addr)
+struct relocation_info *r;
+long relocation;
+char *addr;
+{
+if (r->r_relative)
+ *(long *)addr += relocation;
+}
+
diff --git a/gnu/usr.bin/ld/i386/md.c b/gnu/usr.bin/ld/i386/md.c
new file mode 100644
index 0000000..ce61355
--- /dev/null
+++ b/gnu/usr.bin/ld/i386/md.c
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: md.c,v 1.9 1994/02/13 20:42:09 jkh Exp $
+ */
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <err.h>
+#include <fcntl.h>
+#include <a.out.h>
+#include <stab.h>
+#include <string.h>
+
+#include "ld.h"
+
+/*
+ * Get relocation addend corresponding to relocation record RP
+ * from address ADDR
+ */
+long
+md_get_addend(rp, addr)
+struct relocation_info *rp;
+unsigned char *addr;
+{
+ switch (RELOC_TARGET_SIZE(rp)) {
+ case 0:
+ return get_byte(addr);
+ case 1:
+ return get_short(addr);
+ case 2:
+ return get_long(addr);
+ }
+ errx(1, "Unsupported relocation size: %x", RELOC_TARGET_SIZE(rp));
+ return 0;
+}
+
+/*
+ * Put RELOCATION at ADDR according to relocation record RP.
+ */
+void
+md_relocate(rp, relocation, addr, relocatable_output)
+struct relocation_info *rp;
+long relocation;
+unsigned char *addr;
+int relocatable_output;
+{
+ switch (RELOC_TARGET_SIZE(rp)) {
+ case 0:
+ put_byte(addr, relocation);
+ return;
+ case 1:
+ put_short(addr, relocation);
+ return;
+ case 2:
+ put_long(addr, relocation);
+ return;
+ }
+ errx(1, "Unsupported relocation size: %x", RELOC_TARGET_SIZE(rp));
+}
+
+/*
+ * Machine dependent part of claim_rrs_reloc().
+ * Set RRS relocation type.
+ */
+int
+md_make_reloc(rp, r, type)
+struct relocation_info *rp, *r;
+int type;
+{
+ /* Relocation size */
+ r->r_length = rp->r_length;
+
+ if (RELOC_PCREL_P(rp))
+ r->r_pcrel = 1;
+
+ if (type & RELTYPE_RELATIVE)
+ r->r_relative = 1;
+
+ if (type & RELTYPE_COPY)
+ r->r_copy = 1;
+
+ return 0;
+}
+
+/*
+ * Set up a transfer from jmpslot at OFFSET (relative to the PLT table)
+ * to the binder slot (which is at offset 0 of the PLT).
+ */
+void
+md_make_jmpslot(sp, offset, index)
+jmpslot_t *sp;
+long offset;
+long index;
+{
+ /*
+ * i386 PC-relative "fixed point" is located right after the
+ * instruction it pertains to.
+ */
+ u_long fudge = - (sizeof(sp->opcode) + sizeof(sp->addr) + offset);
+
+ sp->opcode = CALL;
+#if 0
+ sp->addr = fudge;
+#else
+ sp->addr[0] = fudge & 0xffff;
+ sp->addr[1] = fudge >> 16;
+#endif
+ sp->reloc_index = index;
+}
+
+/*
+ * Set up a "direct" transfer (ie. not through the run-time binder) from
+ * jmpslot at OFFSET to ADDR. Used by `ld' when the SYMBOLIC flag is on,
+ * and by `ld.so' after resolving the symbol.
+ * On the i386, we use the JMP instruction which is PC relative, so no
+ * further RRS relocations will be necessary for such a jmpslot.
+ */
+void
+md_fix_jmpslot(sp, offset, addr)
+jmpslot_t *sp;
+long offset;
+u_long addr;
+{
+ u_long fudge = addr - (sizeof(sp->opcode) + sizeof(sp->addr) + offset);
+
+ sp->opcode = JUMP;
+#if 0
+ sp->addr = fudge;
+#else
+ sp->addr[0] = fudge & 0xffff;
+ sp->addr[1] = fudge >> 16;
+#endif
+ sp->reloc_index = 0;
+}
+
+/*
+ * Update the relocation record for a RRS jmpslot.
+ */
+void
+md_make_jmpreloc(rp, r, type)
+struct relocation_info *rp, *r;
+int type;
+{
+ jmpslot_t *sp;
+
+ /*
+ * Fix relocation address to point to the correct
+ * location within this jmpslot.
+ */
+ r->r_address += sizeof(sp->opcode);
+
+ /* Relocation size */
+ r->r_length = 2;
+
+ /* Set relocation type */
+ r->r_jmptable = 1;
+ if (type & RELTYPE_RELATIVE)
+ r->r_relative = 1;
+
+}
+
+/*
+ * Set relocation type for a RRS GOT relocation.
+ */
+void
+md_make_gotreloc(rp, r, type)
+struct relocation_info *rp, *r;
+int type;
+{
+ r->r_baserel = 1;
+ if (type & RELTYPE_RELATIVE)
+ r->r_relative = 1;
+
+ /* Relocation size */
+ r->r_length = 2;
+}
+
+/*
+ * Set relocation type for a RRS copy operation.
+ */
+void
+md_make_cpyreloc(rp, r)
+struct relocation_info *rp, *r;
+{
+ /* Relocation size */
+ r->r_length = 2;
+
+ r->r_copy = 1;
+}
+
+void
+md_set_breakpoint(where, savep)
+long where;
+long *savep;
+{
+ *savep = *(long *)where;
+ *(char *)where = TRAP;
+}
+
+#ifndef RTLD
+
+#ifdef FreeBSD
+int netzmagic;
+#endif
+
+/*
+ * Initialize (output) exec header such that useful values are
+ * obtained from subsequent N_*() macro evaluations.
+ */
+void
+md_init_header(hp, magic, flags)
+struct exec *hp;
+int magic, flags;
+{
+#ifdef NetBSD
+ if (oldmagic || magic == QMAGIC)
+ hp->a_midmag = magic;
+ else
+ N_SETMAGIC((*hp), magic, MID_I386, flags);
+#endif
+#ifdef FreeBSD
+ if (oldmagic)
+ hp->a_midmag = magic;
+ else if (netzmagic)
+ N_SETMAGIC_NET((*hp), magic, MID_I386, flags);
+ else
+ N_SETMAGIC((*hp), magic, MID_I386, flags);
+#endif
+
+ /* TEXT_START depends on the value of outheader.a_entry. */
+ if (!(link_mode & SHAREABLE)) /*WAS: if (entry_symbol) */
+ hp->a_entry = PAGSIZ;
+}
+#endif /* RTLD */
+
+
+#ifdef NEED_SWAP
+/*
+ * Byte swap routines for cross-linking.
+ */
+
+void
+md_swapin_exec_hdr(h)
+struct exec *h;
+{
+ int skip = 0;
+
+ if (!N_BADMAG(*h))
+ skip = 1;
+
+ swap_longs((long *)h + skip, sizeof(*h)/sizeof(long) - skip);
+}
+
+void
+md_swapout_exec_hdr(h)
+struct exec *h;
+{
+ /* NetBSD: Always leave magic alone */
+ int skip = 1;
+#if 0
+ if (N_GETMAGIC(*h) == OMAGIC)
+ skip = 0;
+#endif
+
+ swap_longs((long *)h + skip, sizeof(*h)/sizeof(long) - skip);
+}
+
+
+void
+md_swapin_reloc(r, n)
+struct relocation_info *r;
+int n;
+{
+ int bits;
+
+ for (; n; n--, r++) {
+ r->r_address = md_swap_long(r->r_address);
+ bits = ((int *)r)[1];
+ r->r_symbolnum = md_swap_long(bits & 0xffffff00);
+ r->r_pcrel = (bits & 1);
+ r->r_length = ((bits >> 1) & 3);
+ r->r_extern = ((bits >> 3) & 1);
+ r->r_baserel = ((bits >> 4) & 1);
+ r->r_jmptable = ((bits >> 5) & 1);
+ r->r_relative = ((bits >> 6) & 1);
+#ifdef N_SIZE
+ r->r_copy = ((bits >> 7) & 1);
+#endif
+ }
+}
+
+void
+md_swapout_reloc(r, n)
+struct relocation_info *r;
+int n;
+{
+ int bits;
+
+ for (; n; n--, r++) {
+ r->r_address = md_swap_long(r->r_address);
+ bits = (md_swap_long(r->r_symbolnum) & 0xffffff00);
+ bits |= (r->r_pcrel & 1);
+ bits |= ((r->r_length << 1) & 6);
+ bits |= ((r->r_extern << 3) & 8);
+ bits |= ((r->r_baserel << 4) & 0x10);
+ bits |= ((r->r_jmptable << 5) & 0x20);
+ bits |= ((r->r_relative << 6) & 0x40);
+#ifdef N_SIZE
+ bits |= ((r->r_copy << 7) & 0x80);
+#endif
+ ((int *)r)[1] = bits;
+ }
+}
+
+void
+md_swapout_jmpslot(j, n)
+jmpslot_t *j;
+int n;
+{
+ for (; n; n--, j++) {
+ j->opcode = md_swap_short(j->opcode);
+ j->addr[0] = md_swap_short(j->addr[0]);
+ j->addr[1] = md_swap_short(j->addr[1]);
+ j->reloc_index = md_swap_short(j->reloc_index);
+ }
+}
+
+#endif /* NEED_SWAP */
diff --git a/gnu/usr.bin/ld/i386/md.h b/gnu/usr.bin/ld/i386/md.h
new file mode 100644
index 0000000..1209aee
--- /dev/null
+++ b/gnu/usr.bin/ld/i386/md.h
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: md.h,v 1.9 1994/02/13 20:42:11 jkh Exp $
+ */
+
+
+#if defined(CROSS_LINKER) && defined(XHOST) && XHOST==sparc
+
+#define NEED_SWAP
+
+#endif
+
+#define MAX_ALIGNMENT (sizeof (long))
+
+#ifdef NetBSD
+#define PAGSIZ __LDPGSZ
+#else
+#define PAGSIZ 4096
+#endif
+
+#if defined(NetBSD) || defined(CROSS_LINKER)
+
+#define N_SET_FLAG(ex,f) (oldmagic || N_GETMAGIC(ex)==QMAGIC ? (0) : \
+ N_SETMAGIC(ex, \
+ N_GETMAGIC(ex), \
+ MID_MACHINE, \
+ N_GETFLAG(ex)|(f)))
+
+#define N_IS_DYNAMIC(ex) ((N_GETFLAG(ex) & EX_DYNAMIC))
+
+#define N_BADMID(ex) \
+ (N_GETMID(ex) != 0 && N_GETMID(ex) != MID_MACHINE)
+
+#endif
+
+/*
+ * FreeBSD does it differently
+ */
+#ifdef FreeBSD
+#define N_SET_FLAG(ex,f) (oldmagic ? (0) : \
+ (netzmagic == 0 ? \
+ N_SETMAGIC(ex, \
+ N_GETMAGIC(ex), \
+ MID_MACHINE, \
+ N_GETFLAG(ex)|(f)) : \
+ N_SETMAGIC_NET(ex, \
+ N_GETMAGIC_NET(ex), \
+ MID_MACHINE, \
+ N_GETFLAG_NET(ex)|(f)) ))
+
+#define N_IS_DYNAMIC(ex) ((N_GETMAGIC_NET(ex) == ZMAGIC) ? \
+ ((N_GETFLAG_NET(ex) & EX_DYNAMIC)) : \
+ ((N_GETFLAG(ex) & EX_DYNAMIC) ))
+#define N_BADMID(ex) 0
+#endif
+
+/*
+ * Should be handled by a.out.h ?
+ */
+#define N_ADJUST(ex) (((ex).a_entry < PAGSIZ) ? -PAGSIZ : 0)
+#define TEXT_START(ex) (N_TXTADDR(ex) + N_ADJUST(ex))
+#define DATA_START(ex) (N_DATADDR(ex) + N_ADJUST(ex))
+
+#define RELOC_STATICS_THROUGH_GOT_P(r) (0)
+#define JMPSLOT_NEEDS_RELOC (0)
+
+#define md_got_reloc(r) (0)
+
+#define md_get_rt_segment_addend(r,a) md_get_addend(r,a)
+
+/* Width of a Global Offset Table entry */
+#define GOT_ENTRY_SIZE 4
+typedef long got_t;
+
+typedef struct jmpslot {
+ u_short opcode;
+ u_short addr[2];
+ u_short reloc_index;
+#define JMPSLOT_RELOC_MASK 0xffff
+} jmpslot_t;
+
+#define NOP 0x90
+#define CALL 0xe890 /* NOP + CALL opcode */
+#define JUMP 0xe990 /* NOP + JMP opcode */
+#define TRAP 0xcc /* INT 3 */
+
+/*
+ * Byte swap defs for cross linking
+ */
+
+#if !defined(NEED_SWAP)
+
+#define md_swapin_exec_hdr(h)
+#define md_swapout_exec_hdr(h)
+#define md_swapin_symbols(s,n)
+#define md_swapout_symbols(s,n)
+#define md_swapin_zsymbols(s,n)
+#define md_swapout_zsymbols(s,n)
+#define md_swapin_reloc(r,n)
+#define md_swapout_reloc(r,n)
+#define md_swapin__dynamic(l)
+#define md_swapout__dynamic(l)
+#define md_swapin_section_dispatch_table(l)
+#define md_swapout_section_dispatch_table(l)
+#define md_swapin_so_debug(d)
+#define md_swapout_so_debug(d)
+#define md_swapin_rrs_hash(f,n)
+#define md_swapout_rrs_hash(f,n)
+#define md_swapin_sod(l,n)
+#define md_swapout_sod(l,n)
+#define md_swapout_jmpslot(j,n)
+#define md_swapout_got(g,n)
+#define md_swapin_ranlib_hdr(h,n)
+#define md_swapout_ranlib_hdr(h,n)
+
+#endif /* NEED_SWAP */
+
+#ifdef CROSS_LINKER
+
+#ifdef NEED_SWAP
+
+/* Define IO byte swapping routines */
+
+void md_swapin_exec_hdr __P((struct exec *));
+void md_swapout_exec_hdr __P((struct exec *));
+void md_swapin_reloc __P((struct relocation_info *, int));
+void md_swapout_reloc __P((struct relocation_info *, int));
+void md_swapout_jmpslot __P((jmpslot_t *, int));
+
+#define md_swapin_symbols(s,n) swap_symbols(s,n)
+#define md_swapout_symbols(s,n) swap_symbols(s,n)
+#define md_swapin_zsymbols(s,n) swap_zsymbols(s,n)
+#define md_swapout_zsymbols(s,n) swap_zsymbols(s,n)
+#define md_swapin__dynamic(l) swap__dynamic(l)
+#define md_swapout__dynamic(l) swap__dynamic(l)
+#define md_swapin_section_dispatch_table(l) swap_section_dispatch_table(l)
+#define md_swapout_section_dispatch_table(l) swap_section_dispatch_table(l)
+#define md_swapin_so_debug(d) swap_so_debug(d)
+#define md_swapout_so_debug(d) swap_so_debug(d)
+#define md_swapin_rrs_hash(f,n) swap_rrs_hash(f,n)
+#define md_swapout_rrs_hash(f,n) swap_rrs_hash(f,n)
+#define md_swapin_sod(l,n) swapin_sod(l,n)
+#define md_swapout_sod(l,n) swapout_sod(l,n)
+#define md_swapout_got(g,n) swap_longs((long*)(g),n)
+#define md_swapin_ranlib_hdr(h,n) swap_ranlib_hdr(h,n)
+#define md_swapout_ranlib_hdr(h,n) swap_ranlib_hdr(h,n)
+
+#define md_swap_short(x) ( (((x) >> 8) & 0xff) | (((x) & 0xff) << 8) )
+
+#define md_swap_long(x) ( (((x) >> 24) & 0xff ) | (((x) >> 8 ) & 0xff00 ) | \
+ (((x) << 8 ) & 0xff0000) | (((x) << 24) & 0xff000000))
+
+#define get_byte(p) ( ((unsigned char *)(p))[0] )
+
+#define get_short(p) ( ( ((unsigned char *)(p))[1] << 8) | \
+ ( ((unsigned char *)(p))[0] ) \
+ )
+#define get_long(p) ( ( ((unsigned char *)(p))[3] << 24) | \
+ ( ((unsigned char *)(p))[2] << 16) | \
+ ( ((unsigned char *)(p))[1] << 8 ) | \
+ ( ((unsigned char *)(p))[0] ) \
+ )
+
+#define put_byte(p, v) { ((unsigned char *)(p))[0] = ((unsigned long)(v)); }
+
+#define put_short(p, v) { ((unsigned char *)(p))[1] = \
+ ((((unsigned long)(v)) >> 8) & 0xff); \
+ ((unsigned char *)(p))[0] = \
+ ((((unsigned long)(v)) ) & 0xff); }
+
+#define put_long(p, v) { ((unsigned char *)(p))[3] = \
+ ((((unsigned long)(v)) >> 24) & 0xff); \
+ ((unsigned char *)(p))[2] = \
+ ((((unsigned long)(v)) >> 16) & 0xff); \
+ ((unsigned char *)(p))[1] = \
+ ((((unsigned long)(v)) >> 8) & 0xff); \
+ ((unsigned char *)(p))[0] = \
+ ((((unsigned long)(v)) ) & 0xff); }
+
+#else /* We need not swap, but must pay attention to alignment: */
+
+#define md_swap_short(x) (x)
+#define md_swap_long(x) (x)
+
+#define get_byte(p) ( ((unsigned char *)(p))[0] )
+
+#define get_short(p) ( ( ((unsigned char *)(p))[0] << 8) | \
+ ( ((unsigned char *)(p))[1] ) \
+ )
+
+#define get_long(p) ( ( ((unsigned char *)(p))[0] << 24) | \
+ ( ((unsigned char *)(p))[1] << 16) | \
+ ( ((unsigned char *)(p))[2] << 8 ) | \
+ ( ((unsigned char *)(p))[3] ) \
+ )
+
+
+#define put_byte(p, v) { ((unsigned char *)(p))[0] = ((unsigned long)(v)); }
+
+#define put_short(p, v) { ((unsigned char *)(p))[0] = \
+ ((((unsigned long)(v)) >> 8) & 0xff); \
+ ((unsigned char *)(p))[1] = \
+ ((((unsigned long)(v)) ) & 0xff); }
+
+#define put_long(p, v) { ((unsigned char *)(p))[0] = \
+ ((((unsigned long)(v)) >> 24) & 0xff); \
+ ((unsigned char *)(p))[1] = \
+ ((((unsigned long)(v)) >> 16) & 0xff); \
+ ((unsigned char *)(p))[2] = \
+ ((((unsigned long)(v)) >> 8) & 0xff); \
+ ((unsigned char *)(p))[3] = \
+ ((((unsigned long)(v)) ) & 0xff); }
+
+#endif /* NEED_SWAP */
+
+#else /* Not a cross linker: use native */
+
+#define md_swap_short(x) (x)
+#define md_swap_long(x) (x)
+
+#define get_byte(where) (*(char *)(where))
+#define get_short(where) (*(short *)(where))
+#define get_long(where) (*(long *)(where))
+
+#define put_byte(where,what) (*(char *)(where) = (what))
+#define put_short(where,what) (*(short *)(where) = (what))
+#define put_long(where,what) (*(long *)(where) = (what))
+
+#endif /* CROSS_LINKER */
+
diff --git a/gnu/usr.bin/ld/i386/mdprologue.S b/gnu/usr.bin/ld/i386/mdprologue.S
new file mode 100644
index 0000000..6a582be
--- /dev/null
+++ b/gnu/usr.bin/ld/i386/mdprologue.S
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: mdprologue.S,v 1.3 1993/12/10 10:16:00 jkh Exp $
+ */
+
+/*
+ * i386 run-time link editor entry points.
+ */
+
+#include <sys/syscall.h>
+#define LCALL(x,y) .byte 0x9a ; .long y; .word x
+
+ .text
+ .globl _binder, _binder_entry
+
+/*
+ * _rtl(int version, struct crt_ldso *crtp)
+ */
+#define FRAME 12 /* Size of stack frame */
+
+
+_rtl: # crt0 calls us here
+ pushl %ebp # Allocate stack frame
+ movl %esp, %ebp
+ subl $FRAME, %esp
+ pushl %ebx
+ call 1f # PIC function prologue
+1:
+ popl %ebx
+ addl $_GLOBAL_OFFSET_TABLE_+[.-1b], %ebx
+
+ movl 12(%ebp), %eax # Extract data from interface structure
+ movl (%eax),%eax # base address of ld.so (first field)
+ # setup arguments for rtld()
+ movl (%ebx), %ecx # 1st entry in GOT is our __DYNAMIC
+ addl %eax, %ecx # add load address
+ pushl %ecx # 3rd arg
+ pushl 12(%ebp) # 2nd arg == &crt.
+ pushl 8(%ebp) # 1st arg == version
+ addl _rtld@GOT(%ebx), %eax # relocate address of function
+ call %eax # _rtld(version, crtp, DYNAMIC)
+ addl $12,%esp # pop arguments
+
+ movl (-FRAME-4)(%ebp), %ebx # restore %ebx
+ leave # remove stack frame,
+ ret # lets rock
+
+ # First call to a procedure generally comes through here for
+ # binding.
+
+_binder_entry:
+ pushl %ebp # setup a stack frame
+ movl %esp, %ebp
+ pusha # save all regs
+
+ movl $0, %eax # clear
+ movl 4(%ebp), %esi # return address in PLT
+ movw (%esi), %ax # get hold of relocation number
+ subl $6, %esi # make it point to the jmpslot
+
+ pushl %eax # pushd arguments
+ pushl %esi #
+ call _binder@PLT # _binder(rpc, index)
+ addl $8, %esp # pop arguments
+ movl %eax, 4(%ebp) # return value from _binder() == actual
+ # address of function
+ popa # restore regs
+ leave # remove our stack frame
+ ret
+
+ # Special system call stubs which return real and effective user and group
+ # ids. Saves overhead of making separate calls for each.
+ # !! Relies on compatability option in BSD 4.three-and-a-half
+
+ .globl _getreuid, _getregid
+_getreuid:
+ lea SYS_getuid, %eax
+ LCALL(7,0)
+ jc out
+ movl 4(%esp), %ecx # get 1st arg
+ movl %eax, (%ecx) # put value in it
+ movl 8(%esp), %ecx # same for 2nd arg
+ movl %edx, (%ecx) #
+ ret # done
+
+_getregid:
+ lea SYS_getgid, %eax
+ LCALL(7,0)
+ jc out
+ movl 4(%esp), %ecx # get 1st arg
+ movl %eax, (%ecx) # put value in it
+ movl 8(%esp), %ecx # same for 2nd arg
+ movl %edx, (%ecx) #
+ ret # done
+
+out: jmp cerror@PLT # Call common error routine
+
diff --git a/gnu/usr.bin/ld/ld.1 b/gnu/usr.bin/ld/ld.1
new file mode 100644
index 0000000..efbe7ee
--- /dev/null
+++ b/gnu/usr.bin/ld/ld.1
@@ -0,0 +1,207 @@
+.\"
+.\" Copyright (c) 1993 Paul Kranenburg
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Paul Kranenburg.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: ld.1,v 1.6 1994/03/09 14:28:02 davidg Exp $
+.\"
+.Dd October 14, 1993
+.Dt LD 8
+.Os FreeBSD 1.1
+.Sh NAME
+.Nm ld
+.Nd link editor
+.Sh SYNOPSIS
+.Nm ld
+.Op Fl MNnrSstXxz
+.Bk -words
+.Op Fl A Ar symbol-file
+.Op Fl assert Ar keyword
+.Op Fl B Ar linkmode
+.Op Fl D Ar datasize
+.Op Fl d Ar c
+.Op Fl d Ar p
+.Op Fl e Ar entry
+.Op Fl l Ar library-specifier
+.Op Fl L Ar library-search-path
+.Op Fl o Ar filename
+.Op Fl T Ar address
+.Op Fl u Ar symbol
+.Op Fl V Ar shlib-version
+.Op Fl y Ar symbol
+.Ek
+.Sh DESCRIPTION
+.Nm
+combines the object and archive files given on the command line into a new
+object file. The output object file is either an executable program, a
+shared object suitable for loading at run-time, or an object file that can
+once again be processed by
+.Nm ld.
+Object files and archives are processed in the order given on the command line.
+.Pp
+The options are as follows:
+.Pp
+.Bl -tag -width indent
+.It Fl A Ar symbol-file
+The the symbol-file is taken as a base for link-editing the object files
+on the command line.
+.It Fl a\&ssert Ar keyword
+This option has currently no effect. It is here for compatibility with
+SunOS ld. All conditions which would cause a Sun assertion to fail will
+currently always cause error or warning messages from
+.Nm ld\&.
+.It Fl B Ar dynamic
+Specifies that linking against dynamic libraries can take place. If a library
+specifier of the form -lx appears on the command line,
+.Nm ld
+searches for a library of the from libx.so.n.m (see the
+.Ar l
+option) according to the search rules in effect. If such a file can not be
+found a traditional archive is looked for.
+This options can appear anywhere on the command line and is complementary
+to -Bstatic.
+.It Fl B Ar static
+The counterpart of -Bdynamic. This option turns off dynamic linking for
+all library specifiers until a -Bdynamic is once again given. Any explicitly
+mentioned shared object encountered on the command line while this option is
+in effect is flagged as an error.
+.It Fl B Ar shareable
+Instructs the linker to build a shared object from the object files rather
+than a normal executable image.
+.It Fl B Ar symbolic
+This option causes all symbolic references in the output to be resolved in
+this link-edit session. The only remaining run-time relocation requirements are
+.Em base-relative
+relocations, ie. translation with respect to the load address. Failure to
+resolve any symbolic reference causes an error to be reported.
+.It Fl B Ar forcearchive
+Force all members of archives to be loaded, whether or not such members
+contribute a definition to any plain object files. Useful for making a
+shared library from an archive of PIC objects without having to unpack
+the archive.
+.It Fl B Ar silly
+Search for
+.Em \.sa
+silly archive companions of shared objects. Useful for compatibility with
+version 3 shared objects.
+.It Fl D Ar data-size
+Set the size of the data segment. For sanity's sake, this should be larger
+than the cumulative data sizes of the input files.
+.It Fl d Ar c
+Force allocation of commons even producing relocatable output.
+.It Fl d Ar p
+Force alias definitions of procedure calls in non-PIC code. Useful to
+obtain shareable code in the presence of run-time relocations as such
+calls will be re-directed through the Procedure Linkage Table (see
+.Xr link 5)
+.It Fl e Ar entry-symbol
+Specifies the entry symbol for an executable.
+.It Fl L Ar path
+Add
+.Ar path
+to the list of directories to search for libraries specified with the
+.Ar -l
+option.
+.It Fl l Ar lib-spec
+This option specifies a library to be considered for inclusion in the
+output. If the -Bdynamic option is in effect, a shared library of the
+form lib<spec>.so.m.n (where
+.Em m
+is the major, and
+.Em n
+is the minor version number, respectively) is searched for first. The
+library with the highest version found in the search path is selected.
+If no shared library is found or the -Bstatic options is in effect,
+an archive of the form lib<spec>.a is looked for in the library seach path.
+.It Fl M
+Produce output about the mapping of segments of the input files and the
+values assigned to (global) symbols in the output file.
+.It Fl N
+Produce a OMAGIC output file.
+.It Fl n
+Produce a NMAGIC output file.
+.It Fl o Ar filename
+Specifies the name of the output file. Defaults to
+.Dq a.out.
+.It Fl Q
+Make a BSD/386 / FreeBSD 1.1 output file. This is the default.
+.It Fl r
+Produce relocatable object file, suitable for another pass through
+.Nm ld.
+.It Fl S
+Strip all debugger symbols from the output.
+.It Fl s
+Strip all symbols from the output.
+.It Fl T
+Specifies the start address of the text segment, with respect to which
+all input files will be relocated.
+.It Fl t
+Leave a trace of the input files as they are processed.
+.It Fl u Ar symbol
+Force
+.Ar symbol
+to be marked as undefined. Useful to force loading of an archive member
+in the absence of any other references to that member.
+.It Fl V Ar version
+Put the given version number into the output shared library (if one is
+created). Useful to make shared libaries compatible with other operating
+systems. Eg. SunOS 4.x libraries use version number 3. Defaults to 8.
+.It Fl X
+Discard local symbols in the input files that start with the letter
+.Dq L
+.It Fl x
+Discard all local symbols in the input files.
+.It Fl y Ar symbol
+Trace the manipulations inflicted on
+.Ar symbol
+.It Fl Z
+Make a 386BSD ZMAGIC output file.
+.It Fl z
+Make a NetBSD 0.9 ZMAGIC output file.
+.Sh FILES
+.Sh SEE ALSO
+.Xr ldconfig 1 ,
+.Xr link 5
+.Sh CAVEATS
+An entry point must now explicitly be given if the output is intended to be
+a normal executable program. This was not the case for the previous version of
+.Nm ld\&.
+.Sh BUGS
+Shared objects are not properly checked for undefined symbols.
+.Pp
+Cascading of shared object defeats the
+.Dq -Bstatic
+option.
+.Pp
+All shared objects presented to
+.Nm ld
+are marked for run-time loading in the output file, even if no symbols
+are needed from them.
+.Sh HISTORY
+The shared library model employed by
+.Nm ld
+appeared first in SunOS 4.0
diff --git a/gnu/usr.bin/ld/ld.1aout b/gnu/usr.bin/ld/ld.1aout
new file mode 100644
index 0000000..efbe7ee
--- /dev/null
+++ b/gnu/usr.bin/ld/ld.1aout
@@ -0,0 +1,207 @@
+.\"
+.\" Copyright (c) 1993 Paul Kranenburg
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Paul Kranenburg.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: ld.1,v 1.6 1994/03/09 14:28:02 davidg Exp $
+.\"
+.Dd October 14, 1993
+.Dt LD 8
+.Os FreeBSD 1.1
+.Sh NAME
+.Nm ld
+.Nd link editor
+.Sh SYNOPSIS
+.Nm ld
+.Op Fl MNnrSstXxz
+.Bk -words
+.Op Fl A Ar symbol-file
+.Op Fl assert Ar keyword
+.Op Fl B Ar linkmode
+.Op Fl D Ar datasize
+.Op Fl d Ar c
+.Op Fl d Ar p
+.Op Fl e Ar entry
+.Op Fl l Ar library-specifier
+.Op Fl L Ar library-search-path
+.Op Fl o Ar filename
+.Op Fl T Ar address
+.Op Fl u Ar symbol
+.Op Fl V Ar shlib-version
+.Op Fl y Ar symbol
+.Ek
+.Sh DESCRIPTION
+.Nm
+combines the object and archive files given on the command line into a new
+object file. The output object file is either an executable program, a
+shared object suitable for loading at run-time, or an object file that can
+once again be processed by
+.Nm ld.
+Object files and archives are processed in the order given on the command line.
+.Pp
+The options are as follows:
+.Pp
+.Bl -tag -width indent
+.It Fl A Ar symbol-file
+The the symbol-file is taken as a base for link-editing the object files
+on the command line.
+.It Fl a\&ssert Ar keyword
+This option has currently no effect. It is here for compatibility with
+SunOS ld. All conditions which would cause a Sun assertion to fail will
+currently always cause error or warning messages from
+.Nm ld\&.
+.It Fl B Ar dynamic
+Specifies that linking against dynamic libraries can take place. If a library
+specifier of the form -lx appears on the command line,
+.Nm ld
+searches for a library of the from libx.so.n.m (see the
+.Ar l
+option) according to the search rules in effect. If such a file can not be
+found a traditional archive is looked for.
+This options can appear anywhere on the command line and is complementary
+to -Bstatic.
+.It Fl B Ar static
+The counterpart of -Bdynamic. This option turns off dynamic linking for
+all library specifiers until a -Bdynamic is once again given. Any explicitly
+mentioned shared object encountered on the command line while this option is
+in effect is flagged as an error.
+.It Fl B Ar shareable
+Instructs the linker to build a shared object from the object files rather
+than a normal executable image.
+.It Fl B Ar symbolic
+This option causes all symbolic references in the output to be resolved in
+this link-edit session. The only remaining run-time relocation requirements are
+.Em base-relative
+relocations, ie. translation with respect to the load address. Failure to
+resolve any symbolic reference causes an error to be reported.
+.It Fl B Ar forcearchive
+Force all members of archives to be loaded, whether or not such members
+contribute a definition to any plain object files. Useful for making a
+shared library from an archive of PIC objects without having to unpack
+the archive.
+.It Fl B Ar silly
+Search for
+.Em \.sa
+silly archive companions of shared objects. Useful for compatibility with
+version 3 shared objects.
+.It Fl D Ar data-size
+Set the size of the data segment. For sanity's sake, this should be larger
+than the cumulative data sizes of the input files.
+.It Fl d Ar c
+Force allocation of commons even producing relocatable output.
+.It Fl d Ar p
+Force alias definitions of procedure calls in non-PIC code. Useful to
+obtain shareable code in the presence of run-time relocations as such
+calls will be re-directed through the Procedure Linkage Table (see
+.Xr link 5)
+.It Fl e Ar entry-symbol
+Specifies the entry symbol for an executable.
+.It Fl L Ar path
+Add
+.Ar path
+to the list of directories to search for libraries specified with the
+.Ar -l
+option.
+.It Fl l Ar lib-spec
+This option specifies a library to be considered for inclusion in the
+output. If the -Bdynamic option is in effect, a shared library of the
+form lib<spec>.so.m.n (where
+.Em m
+is the major, and
+.Em n
+is the minor version number, respectively) is searched for first. The
+library with the highest version found in the search path is selected.
+If no shared library is found or the -Bstatic options is in effect,
+an archive of the form lib<spec>.a is looked for in the library seach path.
+.It Fl M
+Produce output about the mapping of segments of the input files and the
+values assigned to (global) symbols in the output file.
+.It Fl N
+Produce a OMAGIC output file.
+.It Fl n
+Produce a NMAGIC output file.
+.It Fl o Ar filename
+Specifies the name of the output file. Defaults to
+.Dq a.out.
+.It Fl Q
+Make a BSD/386 / FreeBSD 1.1 output file. This is the default.
+.It Fl r
+Produce relocatable object file, suitable for another pass through
+.Nm ld.
+.It Fl S
+Strip all debugger symbols from the output.
+.It Fl s
+Strip all symbols from the output.
+.It Fl T
+Specifies the start address of the text segment, with respect to which
+all input files will be relocated.
+.It Fl t
+Leave a trace of the input files as they are processed.
+.It Fl u Ar symbol
+Force
+.Ar symbol
+to be marked as undefined. Useful to force loading of an archive member
+in the absence of any other references to that member.
+.It Fl V Ar version
+Put the given version number into the output shared library (if one is
+created). Useful to make shared libaries compatible with other operating
+systems. Eg. SunOS 4.x libraries use version number 3. Defaults to 8.
+.It Fl X
+Discard local symbols in the input files that start with the letter
+.Dq L
+.It Fl x
+Discard all local symbols in the input files.
+.It Fl y Ar symbol
+Trace the manipulations inflicted on
+.Ar symbol
+.It Fl Z
+Make a 386BSD ZMAGIC output file.
+.It Fl z
+Make a NetBSD 0.9 ZMAGIC output file.
+.Sh FILES
+.Sh SEE ALSO
+.Xr ldconfig 1 ,
+.Xr link 5
+.Sh CAVEATS
+An entry point must now explicitly be given if the output is intended to be
+a normal executable program. This was not the case for the previous version of
+.Nm ld\&.
+.Sh BUGS
+Shared objects are not properly checked for undefined symbols.
+.Pp
+Cascading of shared object defeats the
+.Dq -Bstatic
+option.
+.Pp
+All shared objects presented to
+.Nm ld
+are marked for run-time loading in the output file, even if no symbols
+are needed from them.
+.Sh HISTORY
+The shared library model employed by
+.Nm ld
+appeared first in SunOS 4.0
diff --git a/gnu/usr.bin/ld/ld.c b/gnu/usr.bin/ld/ld.c
new file mode 100644
index 0000000..3cb49d2
--- /dev/null
+++ b/gnu/usr.bin/ld/ld.c
@@ -0,0 +1,3513 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ *
+ * Modified 1993 by Paul Kranenburg, Erasmus University
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ld.c 6.10 (Berkeley) 5/22/91";
+#endif /* not lint */
+
+/* Linker `ld' for GNU
+ Copyright (C) 1988 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Richard Stallman with some help from Eric Albert.
+ Set, indirect, and warning symbol features added by Randy Smith. */
+
+/*
+ * $Id: ld.c,v 1.21 1994/02/17 03:57:00 davidg Exp $
+ */
+
+/* Define how to initialize system-dependent header fields. */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <fcntl.h>
+#include <ar.h>
+#include <ranlib.h>
+#include <a.out.h>
+#include <stab.h>
+#include <string.h>
+
+#include "ld.h"
+
+/* Vector of entries for input files specified by arguments.
+ These are all the input files except for members of specified libraries. */
+struct file_entry *file_table;
+int number_of_files;
+
+/* 1 => write relocation into output file so can re-input it later. */
+int relocatable_output;
+
+/* 1 => building a shared object, set by `-Bshareable'. */
+int building_shared_object;
+
+/* 1 => create the output executable. */
+int make_executable;
+
+/* Force the executable to be output, even if there are non-fatal errors */
+int force_executable;
+
+/* 1 => assign space to common symbols even if `relocatable_output'. */
+int force_common_definition;
+
+/* 1 => assign jmp slots to text symbols in shared objects even if non-PIC */
+int force_alias_definition;
+
+/* 1 => some files contain PIC code, affects relocation bits
+ if `relocatable_output'. */
+int pic_code_seen;
+
+/* 1 => segments must be page aligned (ZMAGIC, QMAGIC) */
+int page_align_segments;
+
+/* 1 => data segment must be page aligned, even if `-n' or `-N' */
+int page_align_data;
+
+/* 1 => do not use standard library search path */
+int nostdlib;
+
+/* Version number to put in __DYNAMIC (set by -V) */
+int soversion;
+
+int text_size; /* total size of text. */
+int text_start; /* start of text */
+int text_pad; /* clear space between text and data */
+int data_size; /* total size of data. */
+int data_start; /* start of data */
+int data_pad; /* part of bss segment as part of data */
+
+int bss_size; /* total size of bss. */
+int bss_start; /* start of bss */
+
+int text_reloc_size; /* total size of text relocation. */
+int data_reloc_size; /* total size of data relocation. */
+
+int rrs_section_type; /* What's in the RRS section */
+int rrs_text_size; /* Size of RRS text additions */
+int rrs_text_start; /* Location of above */
+int rrs_data_size; /* Size of RRS data additions */
+int rrs_data_start; /* Location of above */
+
+/* Specifications of start and length of the area reserved at the end
+ of the data segment for the set vectors. Computed in 'digest_symbols' */
+int set_sect_start; /* start of set element vectors */
+int set_sect_size; /* size of above */
+
+int link_mode; /* Current link mode */
+
+/*
+ * When loading the text and data, we can avoid doing a close
+ * and another open between members of the same library.
+ *
+ * These two variables remember the file that is currently open.
+ * Both are zero if no file is open.
+ *
+ * See `each_file' and `file_close'.
+ */
+struct file_entry *input_file;
+int input_desc;
+
+/* The name of the file to write; "a.out" by default. */
+char *output_filename; /* Output file name. */
+int outdesc; /* Output file descriptor. */
+struct exec outheader; /* Output file header. */
+int magic; /* Output file magic. */
+int oldmagic;
+int relocatable_output; /* `-r'-ed output */
+
+symbol *entry_symbol; /* specified by `-e' */
+int entry_offset; /* program entry if no `-e' given */
+
+int page_size; /* Size of a page (machine dependent) */
+
+/*
+ * Keep a list of any symbols referenced from the command line (so
+ * that error messages for these guys can be generated). This list is
+ * zero terminated.
+ */
+symbol **cmdline_references;
+int cl_refs_allocated;
+
+/*
+ * Which symbols should be stripped (omitted from the output): none, all, or
+ * debugger symbols.
+ */
+enum {
+ STRIP_NONE, STRIP_ALL, STRIP_DEBUGGER
+} strip_symbols;
+
+/*
+ * Which local symbols should be omitted: none, all, or those starting with L.
+ * This is irrelevant if STRIP_NONE.
+ */
+enum {
+ DISCARD_NONE, DISCARD_ALL, DISCARD_L
+} discard_locals;
+
+int global_sym_count; /* # of nlist entries for global symbols */
+int size_sym_count; /* # of N_SIZE nlist entries for output
+ (relocatable_output only) */
+int local_sym_count; /* # of nlist entries for local symbols. */
+int non_L_local_sym_count; /* # of nlist entries for non-L symbols */
+int debugger_sym_count; /* # of nlist entries for debugger info. */
+int undefined_global_sym_count; /* # of global symbols referenced and
+ not defined. */
+int undefined_shobj_sym_count; /* # of undefined symbols referenced
+ by shared objects */
+int multiple_def_count; /* # of multiply defined symbols. */
+int defined_global_sym_count; /* # of defined global symbols. */
+int common_defined_global_count; /* # of common symbols. */
+
+int special_sym_count; /* # of linker defined symbols. */
+ /* XXX - Currently, only __DYNAMIC and _G_O_T_ go here if required,
+ * perhaps _etext, _edata and _end should go here too.
+ */
+int global_alias_count; /* # of aliased symbols */
+int set_symbol_count; /* # of N_SET* symbols. */
+int set_vector_count; /* # of set vectors in output. */
+int warning_count; /* # of warning symbols encountered. */
+
+struct string_list_element *set_element_prefixes;
+
+int trace_files; /* print names of input files as processed (`-t'). */
+int write_map; /* write a load map (`-M') */
+
+/*
+ * `text-start' address is normally this much plus a page boundary.
+ * This is not a user option; it is fixed for each system.
+ */
+int text_start_alignment;
+
+/*
+ * Nonzero if -T was specified in the command line.
+ * This prevents text_start from being set later to default values.
+ */
+int T_flag_specified;
+
+/*
+ * Nonzero if -Tdata was specified in the command line.
+ * This prevents data_start from being set later to default values.
+ */
+int Tdata_flag_specified;
+
+/*
+ * Size to pad data section up to.
+ * We simply increase the size of the data section, padding with zeros,
+ * and reduce the size of the bss section to match.
+ */
+int specified_data_size;
+
+long *set_vectors;
+int setv_fill_count;
+
+static void decode_option __P((char *, char *));
+static void decode_command __P((int, char **));
+static int classify_arg __P((char *));
+static void load_symbols __P((void));
+static void enter_global_ref __P((struct localsymbol *,
+ char *, struct file_entry *));
+static void digest_symbols __P((void));
+static void digest_pass1 __P((void)), digest_pass2 __P((void));
+static void consider_file_section_lengths __P((struct file_entry *));
+static void relocate_file_addresses __P((struct file_entry *));
+static void consider_relocation __P((struct file_entry *, int));
+static void consider_local_symbols __P((struct file_entry *));
+static void perform_relocation __P((char *, int,
+ struct relocation_info *, int,
+ struct file_entry *, int));
+static void copy_text __P((struct file_entry *));
+static void copy_data __P((struct file_entry *));
+static void coptxtrel __P((struct file_entry *));
+static void copdatrel __P((struct file_entry *));
+static void write_output __P((void));
+static void write_header __P((void));
+static void write_text __P((void));
+static void write_data __P((void));
+static void write_rel __P((void));
+static void write_syms __P((void));
+static void assign_symbolnums __P((struct file_entry *, int *));
+static void cleanup __P((void));
+static int parse __P((char *, char *, char *));
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ /* Added this to stop ld core-dumping on very large .o files. */
+#ifdef RLIMIT_STACK
+ /* Get rid of any avoidable limit on stack size. */
+ {
+ struct rlimit rlim;
+
+ /* Set the stack limit huge so that alloca does not fail. */
+ if (getrlimit(RLIMIT_STACK, &rlim) != 0)
+ warn("getrlimit");
+ else {
+ rlim.rlim_cur = rlim.rlim_max;
+ if (setrlimit(RLIMIT_STACK, &rlim) != 0)
+ warn("setrlimit");
+ }
+ }
+#endif /* RLIMIT_STACK */
+
+ page_size = PAGSIZ;
+
+ /* Clear the cumulative info on the output file. */
+
+ text_size = 0;
+ data_size = 0;
+ bss_size = 0;
+ text_reloc_size = 0;
+ data_reloc_size = 0;
+
+ data_pad = 0;
+ text_pad = 0;
+ page_align_segments = 0;
+ page_align_data = 0;
+
+ /* Initialize the data about options. */
+
+ specified_data_size = 0;
+ strip_symbols = STRIP_NONE;
+ trace_files = 0;
+ discard_locals = DISCARD_NONE;
+ entry_symbol = 0;
+ write_map = 0;
+ relocatable_output = 0;
+ force_common_definition = 0;
+ T_flag_specified = 0;
+ Tdata_flag_specified = 0;
+ magic = DEFAULT_MAGIC;
+ make_executable = 1;
+ force_executable = 0;
+ link_mode = DYNAMIC;
+#ifdef SUNOS4
+ link_mode |= SILLYARCHIVE;
+#endif
+ soversion = DEFAULT_SOVERSION;
+
+ /* Initialize the cumulative counts of symbols. */
+
+ local_sym_count = 0;
+ non_L_local_sym_count = 0;
+ debugger_sym_count = 0;
+ undefined_global_sym_count = 0;
+ warning_count = 0;
+ multiple_def_count = 0;
+ common_defined_global_count = 0;
+
+ /* Keep a list of symbols referenced from the command line */
+ cl_refs_allocated = 10;
+ cmdline_references = (symbol **)
+ xmalloc(cl_refs_allocated * sizeof(symbol *));
+ *cmdline_references = 0;
+
+ /* Completely decode ARGV. */
+ decode_command(argc, argv);
+
+ building_shared_object =
+ (!relocatable_output && (link_mode & SHAREABLE));
+
+ if (building_shared_object && entry_symbol) {
+ errx(1,"`-Bshareable' and `-e' options are mutually exclusive");
+ }
+
+ /* Create the symbols `etext', `edata' and `end'. */
+ symtab_init(relocatable_output);
+
+ /* Prepare for the run-time linking support. */
+ init_rrs();
+
+ /*
+ * Determine whether to count the header as part of the text size,
+ * and initialize the text size accordingly. This depends on the kind
+ * of system and on the output format selected.
+ */
+
+ if (magic == ZMAGIC || magic == QMAGIC)
+ page_align_segments = 1;
+
+ md_init_header(&outheader, magic, 0);
+
+ text_size = sizeof(struct exec);
+ text_size -= N_TXTOFF(outheader);
+
+ if (text_size < 0)
+ text_size = 0;
+ entry_offset = text_size;
+
+ if (!T_flag_specified && !relocatable_output)
+ text_start = TEXT_START(outheader);
+
+ /* The text-start address is normally this far past a page boundary. */
+ text_start_alignment = text_start % page_size;
+
+ /*
+ * Load symbols of all input files. Also search all libraries and
+ * decide which library members to load.
+ */
+ load_symbols();
+
+ /* Compute where each file's sections go, and relocate symbols. */
+ digest_symbols();
+
+ /*
+ * Print error messages for any missing symbols, for any warning
+ * symbols, and possibly multiple definitions
+ */
+ make_executable = do_warnings(stderr);
+
+ /* Print a map, if requested. */
+ if (write_map)
+ print_symbols(stdout);
+
+ /* Write the output file. */
+ if (make_executable || force_executable)
+ write_output();
+
+ exit(!make_executable);
+}
+
+/*
+ * Analyze a command line argument. Return 0 if the argument is a filename.
+ * Return 1 if the argument is a option complete in itself. Return 2 if the
+ * argument is a option which uses an argument.
+ *
+ * Thus, the value is the number of consecutive arguments that are part of
+ * options.
+ */
+
+static int
+classify_arg(arg)
+ register char *arg;
+{
+ if (*arg != '-')
+ return 0;
+ switch (arg[1]) {
+ case 'a':
+ if (!strcmp(&arg[2], "ssert"))
+ return 2;
+ case 'A':
+ case 'D':
+ case 'e':
+ case 'L':
+ case 'l':
+ case 'o':
+ case 'u':
+ case 'V':
+ case 'y':
+ if (arg[2])
+ return 1;
+ return 2;
+
+ case 'B':
+ if (!strcmp(&arg[2], "static"))
+ return 1;
+ if (!strcmp(&arg[2], "dynamic"))
+ return 1;
+
+ case 'T':
+ if (arg[2] == 0)
+ return 2;
+ if (!strcmp(&arg[2], "text"))
+ return 2;
+ if (!strcmp(&arg[2], "data"))
+ return 2;
+ return 1;
+ }
+
+ return 1;
+}
+
+/*
+ * Process the command arguments, setting up file_table with an entry for
+ * each input file, and setting variables according to the options.
+ */
+
+static void
+decode_command(argc, argv)
+ int argc;
+ char **argv;
+{
+ register int i;
+ register struct file_entry *p;
+
+ number_of_files = 0;
+ output_filename = "a.out";
+
+ /*
+ * First compute number_of_files so we know how long to make
+ * file_table.
+ * Also process most options completely.
+ */
+
+ for (i = 1; i < argc; i++) {
+ register int code = classify_arg(argv[i]);
+ if (code) {
+ if (i + code > argc)
+ errx(1, "no argument following %s", argv[i]);
+
+ decode_option(argv[i], argv[i + 1]);
+
+ if (argv[i][1] == 'l' || argv[i][1] == 'A')
+ number_of_files++;
+
+ i += code - 1;
+ } else
+ number_of_files++;
+ }
+
+ if (!number_of_files)
+ errx(1, "no input files");
+
+ p = file_table = (struct file_entry *)
+ xmalloc(number_of_files * sizeof(struct file_entry));
+ bzero(p, number_of_files * sizeof(struct file_entry));
+
+ /* Now scan again and fill in file_table. */
+ /* All options except -A and -l are ignored here. */
+
+ for (i = 1; i < argc; i++) {
+ char *string;
+ register int code = classify_arg(argv[i]);
+
+ if (code == 0) {
+ p->filename = argv[i];
+ p->local_sym_name = argv[i];
+ p++;
+ continue;
+ }
+ if (code == 2)
+ string = argv[i + 1];
+ else
+ string = &argv[i][2];
+
+ if (argv[i][1] == 'B') {
+ if (strcmp(string, "static") == 0)
+ link_mode &= ~DYNAMIC;
+ else if (strcmp(string, "dynamic") == 0)
+ link_mode |= DYNAMIC;
+ else if (strcmp(string, "symbolic") == 0)
+ link_mode |= SYMBOLIC;
+ else if (strcmp(string, "forcearchive") == 0)
+ link_mode |= FORCEARCHIVE;
+ else if (strcmp(string, "shareable") == 0)
+ link_mode |= SHAREABLE;
+#ifdef SUN_COMPAT
+ else if (strcmp(string, "silly") == 0)
+ link_mode |= SILLYARCHIVE;
+ else if (strcmp(string, "~silly") == 0)
+ link_mode &= ~SILLYARCHIVE;
+#endif
+ }
+ if (argv[i][1] == 'A') {
+ if (p != file_table)
+ errx(1, "-A specified before an input file other than the first");
+ p->filename = string;
+ p->local_sym_name = string;
+ p->flags |= E_JUST_SYMS;
+ link_mode &= ~DYNAMIC;
+ p++;
+ }
+ if (argv[i][1] == 'l') {
+ p->filename = string;
+ p->local_sym_name = concat("-l", string, "");
+ p->flags |= E_SEARCH_DIRS;
+ if (link_mode & DYNAMIC && !relocatable_output)
+ p->flags |= E_SEARCH_DYNAMIC;
+ p++;
+ }
+ i += code - 1;
+ }
+
+ /* Now check some option settings for consistency. */
+
+ if (page_align_segments &&
+ (text_start - text_start_alignment) & (page_size - 1))
+ errx(1, "-T argument not multiple of page size, with sharable output");
+
+ /* Append the standard search directories to the user-specified ones. */
+ add_search_path(getenv("LD_LIBRARY_PATH"));
+ if (!nostdlib && getenv("LD_NOSTD_PATH") == NULL)
+ std_search_path();
+}
+
+void
+add_cmdline_ref(sp)
+ symbol *sp;
+{
+ symbol **ptr;
+
+ for (ptr = cmdline_references;
+ ptr < cmdline_references + cl_refs_allocated && *ptr;
+ ptr++);
+
+ if (ptr >= cmdline_references + cl_refs_allocated - 1) {
+ int diff = ptr - cmdline_references;
+
+ cl_refs_allocated *= 2;
+ cmdline_references = (symbol **)
+ xrealloc(cmdline_references,
+ cl_refs_allocated * sizeof(symbol *));
+ ptr = cmdline_references + diff;
+ }
+ *ptr++ = sp;
+ *ptr = (symbol *)0;
+}
+
+int
+set_element_prefixed_p(name)
+ char *name;
+{
+ struct string_list_element *p;
+ int i;
+
+ for (p = set_element_prefixes; p; p = p->next) {
+
+ for (i = 0; p->str[i] != '\0' && (p->str[i] == name[i]); i++);
+ if (p->str[i] == '\0')
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Record an option and arrange to act on it later. ARG should be the
+ * following command argument, which may or may not be used by this option.
+ *
+ * The `l' and `A' options are ignored here since they actually specify input
+ * files.
+ */
+
+static void
+decode_option(swt, arg)
+ register char *swt, *arg;
+{
+ if (!strcmp(swt + 1, "Bstatic"))
+ return;
+ if (!strcmp(swt + 1, "Bdynamic"))
+ return;
+ if (!strcmp(swt + 1, "Bsymbolic"))
+ return;
+ if (!strcmp(swt + 1, "Bforcearchive"))
+ return;
+ if (!strcmp(swt + 1, "Bshareable"))
+ return;
+ if (!strcmp(swt + 1, "assert"))
+ return;
+#ifdef SUN_COMPAT
+ if (!strcmp(swt + 1, "Bsilly"))
+ return;
+#endif
+ if (!strcmp(swt + 1, "Ttext")) {
+ text_start = parse(arg, "%x", "invalid argument to -Ttext");
+ T_flag_specified = 1;
+ return;
+ }
+ if (!strcmp(swt + 1, "Tdata")) {
+ rrs_data_start = parse(arg, "%x", "invalid argument to -Tdata");
+ Tdata_flag_specified = 1;
+ return;
+ }
+ if (!strcmp(swt + 1, "noinhibit-exec")) {
+ force_executable = 1;
+ return;
+ }
+ if (!strcmp(swt + 1, "nostdlib")) {
+ nostdlib = 1;
+ return;
+ }
+ if (swt[2] != 0)
+ arg = &swt[2];
+
+ switch (swt[1]) {
+ case 'A':
+ return;
+
+ case 'D':
+ specified_data_size = parse(arg, "%x", "invalid argument to -D");
+ return;
+
+ case 'd':
+ if (swt[2] == 0 || *arg == 'c')
+ force_common_definition = 1;
+ else if (*arg == 'p')
+ force_alias_definition = 1;
+ else
+ errx(1, "-d option takes 'c' or 'p' argument");
+ return;
+
+ case 'e':
+ entry_symbol = getsym(arg);
+ if (!entry_symbol->defined &&
+ !(entry_symbol->flags & GS_REFERENCED))
+ undefined_global_sym_count++;
+ entry_symbol->flags |= GS_REFERENCED;
+ add_cmdline_ref(entry_symbol);
+ return;
+
+ case 'l':
+ return;
+
+ case 'L':
+ add_search_dir(arg);
+ return;
+
+ case 'M':
+ write_map = 1;
+ return;
+
+ case 'N':
+ magic = OMAGIC;
+ return;
+
+ case 'n':
+ magic = NMAGIC;
+ return;
+
+ case 'o':
+ output_filename = arg;
+ return;
+
+ case 'p':
+ page_align_data = 1;
+ return;
+
+#ifdef QMAGIC
+ case 'Q':
+ magic = QMAGIC;
+ return;
+#endif
+
+ case 'r':
+ relocatable_output = 1;
+ magic = OMAGIC;
+ text_start = 0;
+ return;
+
+ case 'S':
+ strip_symbols = STRIP_DEBUGGER;
+ return;
+
+ case 's':
+ strip_symbols = STRIP_ALL;
+ return;
+
+ case 'T':
+ text_start = parse(arg, "%x", "invalid argument to -T");
+ T_flag_specified = 1;
+ return;
+
+ case 't':
+ trace_files = 1;
+ return;
+
+ case 'u':
+ {
+ register symbol *sp = getsym(arg);
+
+ if (!sp->defined && !(sp->flags & GS_REFERENCED))
+ undefined_global_sym_count++;
+ sp->flags |= GS_REFERENCED;
+ add_cmdline_ref(sp);
+ }
+ return;
+
+#if 1
+ case 'V':
+ soversion = parse(arg, "%d", "invalid argument to -V");
+ return;
+#endif
+
+ case 'X':
+ discard_locals = DISCARD_L;
+ return;
+
+ case 'x':
+ discard_locals = DISCARD_ALL;
+ return;
+
+ case 'y':
+ {
+ register symbol *sp = getsym(&swt[2]);
+ sp->flags |= GS_TRACE;
+ }
+ return;
+
+ case 'z':
+ magic = ZMAGIC;
+ oldmagic = 0;
+#ifdef __FreeBSD__
+ netzmagic = 1;
+#endif
+ return;
+
+ case 'Z':
+ magic = oldmagic = ZMAGIC;
+#ifdef __FreeBSD__
+ netzmagic = 0;
+#endif
+ return;
+
+ default:
+ errx(1, "invalid command option `%s'", swt);
+ }
+}
+
+/* Convenient functions for operating on one or all files being loaded. */
+
+/*
+ * Call FUNCTION on each input file entry. Do not call for entries for
+ * libraries; instead, call once for each library member that is being
+ * loaded.
+ *
+ * FUNCTION receives two arguments: the entry, and ARG.
+ */
+
+void
+each_file(function, arg)
+ register void (*function)();
+ register void *arg;
+{
+ register int i;
+
+ for (i = 0; i < number_of_files; i++) {
+ register struct file_entry *entry = &file_table[i];
+ register struct file_entry *subentry;
+
+ if (entry->flags & E_SCRAPPED)
+ continue;
+
+ if (!(entry->flags & E_IS_LIBRARY))
+ (*function)(entry, arg);
+
+ subentry = entry->subfiles;
+ for (; subentry; subentry = subentry->chain) {
+ if (subentry->flags & E_SCRAPPED)
+ continue;
+ (*function)(subentry, arg);
+ }
+
+#ifdef SUN_COMPAT
+ if (entry->silly_archive) {
+
+ if (!(entry->flags & E_DYNAMIC))
+ warnx("Silly");
+
+ if (!(entry->silly_archive->flags & E_IS_LIBRARY))
+ warnx("Sillier");
+
+ subentry = entry->silly_archive->subfiles;
+ for (; subentry; subentry = subentry->chain) {
+ if (subentry->flags & E_SCRAPPED)
+ continue;
+ (*function)(subentry, arg);
+ }
+ }
+#endif
+ }
+}
+
+/*
+ * Call FUNCTION on each input file entry until it returns a non-zero value.
+ * Return this value. Do not call for entries for libraries; instead, call
+ * once for each library member that is being loaded.
+ *
+ * FUNCTION receives two arguments: the entry, and ARG. It must be a function
+ * returning unsigned long (though this can probably be fudged).
+ */
+
+unsigned long
+check_each_file(function, arg)
+ register unsigned long (*function)();
+ register void *arg;
+{
+ register int i;
+ register unsigned long return_val;
+
+ for (i = 0; i < number_of_files; i++) {
+ register struct file_entry *entry = &file_table[i];
+ if (entry->flags & E_SCRAPPED)
+ continue;
+ if (entry->flags & E_IS_LIBRARY) {
+ register struct file_entry *subentry = entry->subfiles;
+ for (; subentry; subentry = subentry->chain) {
+ if (subentry->flags & E_SCRAPPED)
+ continue;
+ if (return_val = (*function)(subentry, arg))
+ return return_val;
+ }
+ } else if (return_val = (*function)(entry, arg))
+ return return_val;
+ }
+ return 0;
+}
+
+/* Like `each_file' but ignore files that were just for symbol definitions. */
+
+void
+each_full_file(function, arg)
+ register void (*function)();
+ register void *arg;
+{
+ register int i;
+
+ for (i = 0; i < number_of_files; i++) {
+ register struct file_entry *entry = &file_table[i];
+ register struct file_entry *subentry;
+
+ if (entry->flags & (E_SCRAPPED | E_JUST_SYMS))
+ continue;
+
+#ifdef SUN_COMPAT
+ if (entry->silly_archive) {
+
+ if (!(entry->flags & E_DYNAMIC))
+ warnx("Silly");
+
+ if (!(entry->silly_archive->flags & E_IS_LIBRARY))
+ warnx("Sillier");
+
+ subentry = entry->silly_archive->subfiles;
+ for (; subentry; subentry = subentry->chain) {
+ if (subentry->flags & E_SCRAPPED)
+ continue;
+ (*function)(subentry, arg);
+ }
+ }
+#endif
+ if (entry->flags & E_DYNAMIC)
+ continue;
+
+ if (!(entry->flags & E_IS_LIBRARY))
+ (*function)(entry, arg);
+
+ subentry = entry->subfiles;
+ for (; subentry; subentry = subentry->chain) {
+ if (subentry->flags & E_SCRAPPED)
+ continue;
+ (*function)(subentry, arg);
+ }
+
+ }
+}
+
+/* Close the input file that is now open. */
+
+void
+file_close()
+{
+ close(input_desc);
+ input_desc = 0;
+ input_file = 0;
+}
+
+/*
+ * Open the input file specified by 'entry', and return a descriptor. The
+ * open file is remembered; if the same file is opened twice in a row, a new
+ * open is not actually done.
+ */
+int
+file_open(entry)
+ register struct file_entry *entry;
+{
+ register int fd;
+
+ if (entry->superfile && (entry->superfile->flags & E_IS_LIBRARY))
+ return file_open(entry->superfile);
+
+ if (entry == input_file)
+ return input_desc;
+
+ if (input_file)
+ file_close();
+
+ if (entry->flags & E_SEARCH_DIRS) {
+ fd = findlib(entry);
+ } else
+ fd = open(entry->filename, O_RDONLY, 0);
+
+ if (fd > 0) {
+ input_file = entry;
+ input_desc = fd;
+ return fd;
+ }
+
+ if (entry->flags & E_SEARCH_DIRS)
+ errx(1, "%s: no match", entry->local_sym_name);
+ else
+ err(1, "%s", entry->filename);
+ return fd;
+}
+
+int
+text_offset(entry)
+ struct file_entry *entry;
+{
+ return entry->starting_offset + N_TXTOFF (entry->header);
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+ * Read a file's header into the proper place in the file_entry. FD is the
+ * descriptor on which the file is open. ENTRY is the file's entry.
+ */
+void
+read_header(fd, entry)
+ int fd;
+ struct file_entry *entry;
+{
+ register int len;
+
+ if (lseek(fd, entry->starting_offset, L_SET) !=
+ entry->starting_offset)
+ err(1, "%s: read_header: lseek", get_file_name(entry));
+
+ len = read(fd, &entry->header, sizeof(struct exec));
+ if (len != sizeof (struct exec))
+ err(1, "%s: read_header: read", get_file_name(entry));
+
+ md_swapin_exec_hdr(&entry->header);
+
+ if (N_BADMAG (entry->header))
+ errx(1, "%s: bad magic number", get_file_name(entry));
+
+ if (N_BADMID(entry->header))
+ errx(1, "%s: non-native input file", get_file_name(entry));
+
+ entry->flags |= E_HEADER_VALID;
+}
+
+/*
+ * Read the symbols of file ENTRY into core. Assume it is already open, on
+ * descriptor FD. Also read the length of the string table, which follows
+ * the symbol table, but don't read the contents of the string table.
+ */
+
+void
+read_entry_symbols(fd, entry)
+ struct file_entry *entry;
+ int fd;
+{
+ int str_size;
+ struct nlist *np;
+ int i;
+
+ if (!(entry->flags & E_HEADER_VALID))
+ read_header(fd, entry);
+
+ np = (struct nlist *)alloca(entry->header.a_syms);
+ entry->nsymbols = entry->header.a_syms / sizeof(struct nlist);
+ if (entry->nsymbols == 0)
+ return;
+
+ entry->symbols = (struct localsymbol *)
+ xmalloc(entry->nsymbols * sizeof(struct localsymbol));
+
+ if (lseek(fd, N_SYMOFF(entry->header) + entry->starting_offset, L_SET)
+ != N_SYMOFF(entry->header) + entry->starting_offset)
+ err(1, "%s: read_symbols: lseek(syms) failed", get_file_name(entry));
+
+ if (entry->header.a_syms != read(fd, np, entry->header.a_syms))
+ errx(1, "%s: read_symbols: premature end of file in symbols",
+ get_file_name(entry));
+
+ md_swapin_symbols(np, entry->header.a_syms / sizeof(struct nlist));
+
+ for (i = 0; i < entry->nsymbols; i++) {
+ entry->symbols[i].nzlist.nlist = *np++;
+ entry->symbols[i].nzlist.nz_size = 0;
+ entry->symbols[i].symbol = NULL;
+ entry->symbols[i].next = NULL;
+ entry->symbols[i].entry = entry;
+ entry->symbols[i].gotslot_offset = -1;
+ entry->symbols[i].flags = 0;
+ }
+
+ entry->strings_offset = N_STROFF(entry->header) +
+ entry->starting_offset;
+ if (lseek(fd, entry->strings_offset, 0) == (off_t)-1)
+ err(1, "%s: read_symbols: lseek(strings) failed",
+ get_file_name(entry));
+ if (sizeof str_size != read(fd, &str_size, sizeof str_size))
+ errx(1, "%s: read_symbols: cannot read string table size",
+ get_file_name(entry));
+
+ entry->string_size = md_swap_long(str_size);
+}
+
+/*
+ * Read the string table of file ENTRY open on descriptor FD, into core.
+ */
+void
+read_entry_strings(fd, entry)
+ struct file_entry *entry;
+ int fd;
+{
+
+ if (entry->string_size == 0)
+ return;
+
+ if (!(entry->flags & E_HEADER_VALID) || !entry->strings_offset)
+ errx(1, "%s: read_strings: string table unavailable",
+ get_file_name(entry));
+
+ if (lseek(fd, entry->strings_offset, L_SET) !=
+ entry->strings_offset)
+ err(1, "%s: read_strings: lseek",
+ get_file_name(entry));
+
+ if (read(fd, entry->strings, entry->string_size) !=
+ entry->string_size)
+ errx(1, "%s: read_strings: premature end of file in strings",
+ get_file_name(entry));
+
+ return;
+}
+
+/* Read in the relocation sections of ENTRY if necessary */
+
+void
+read_entry_relocation(fd, entry)
+ int fd;
+ struct file_entry *entry;
+{
+ register struct relocation_info *reloc;
+ off_t pos;
+
+ if (!entry->textrel) {
+
+ reloc = (struct relocation_info *)
+ xmalloc(entry->header.a_trsize);
+
+ pos = text_offset(entry) +
+ entry->header.a_text + entry->header.a_data;
+
+ if (lseek(fd, pos, L_SET) != pos)
+ err(1, "%s: read_reloc(text): lseek failed",
+ get_file_name(entry));
+
+ if (read(fd, reloc, entry->header.a_trsize) !=
+ entry->header.a_trsize)
+ errx(1, "%s: read_reloc(text): premature EOF",
+ get_file_name(entry));
+
+ md_swapin_reloc(reloc, entry->header.a_trsize / sizeof(*reloc));
+ entry->textrel = reloc;
+ entry->ntextrel = entry->header.a_trsize / sizeof(*reloc);
+
+ }
+
+ if (!entry->datarel) {
+
+ reloc = (struct relocation_info *)
+ xmalloc(entry->header.a_drsize);
+
+ pos = text_offset(entry) + entry->header.a_text +
+ entry->header.a_data + entry->header.a_trsize;
+
+ if (lseek(fd, pos, L_SET) != pos)
+ err(1, "%s: read_reloc(data): lseek failed",
+ get_file_name(entry));
+
+ if (read(fd, reloc, entry->header.a_drsize) !=
+ entry->header.a_drsize)
+ errx(1, "%s: read_reloc(data): premature EOF",
+ get_file_name(entry));
+
+ md_swapin_reloc(reloc, entry->header.a_drsize / sizeof(*reloc));
+ entry->datarel = reloc;
+ entry->ndatarel = entry->header.a_drsize / sizeof(*reloc);
+
+ }
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+ * Read in the symbols of all input files.
+ */
+static void
+load_symbols()
+{
+ register int i;
+
+ if (trace_files)
+ fprintf(stderr, "Loading symbols:\n\n");
+
+ for (i = 0; i < number_of_files; i++)
+ read_file_symbols(&file_table[i]);
+
+ if (trace_files)
+ fprintf(stderr, "\n");
+}
+
+/*
+ * If ENTRY is a rel file, read its symbol and string sections into core. If
+ * it is a library, search it and load the appropriate members (which means
+ * calling this function recursively on those members).
+ */
+
+void
+read_file_symbols(entry)
+ register struct file_entry *entry;
+{
+ register int fd;
+ register int len;
+ struct exec hdr;
+
+ fd = file_open(entry);
+
+ len = read(fd, &hdr, sizeof hdr);
+ if (len != sizeof hdr)
+ errx(1, "%s: read_file_symbols(header): premature EOF",
+ get_file_name(entry));
+
+ md_swapin_exec_hdr(&hdr);
+
+ if (!N_BADMAG (hdr)) {
+ if (N_IS_DYNAMIC(hdr) && !(entry->flags & E_JUST_SYMS)) {
+ if (relocatable_output) {
+ errx(1,
+ "%s: -r and shared objects currently not supported ",
+ get_file_name(entry));
+ return;
+ }
+ entry->flags |= E_DYNAMIC;
+ if (entry->superfile || rrs_add_shobj(entry))
+ read_shared_object(fd, entry);
+ else
+ entry->flags |= E_SCRAPPED;
+ } else {
+ read_entry_symbols(fd, entry);
+ entry->strings = (char *)alloca (entry->string_size);
+ read_entry_strings(fd, entry);
+ read_entry_relocation(fd, entry);
+ enter_file_symbols(entry);
+ entry->strings = 0;
+ }
+ } else {
+ char armag[SARMAG];
+
+ lseek (fd, 0, 0);
+ if (SARMAG != read(fd, armag, SARMAG) ||
+ strncmp (armag, ARMAG, SARMAG))
+ errx(1,
+ "%s: malformed input file (not rel or archive)",
+ get_file_name(entry));
+ entry->flags |= E_IS_LIBRARY;
+ search_library(fd, entry);
+ }
+
+ file_close();
+}
+
+
+/*
+ * Enter the external symbol defs and refs of ENTRY in the hash table.
+ */
+
+void
+enter_file_symbols(entry)
+ struct file_entry *entry;
+{
+ struct localsymbol *lsp, *lspend;
+
+ if (trace_files)
+ prline_file_name(entry, stderr);
+
+ lspend = entry->symbols + entry->nsymbols;
+
+ for (lsp = entry->symbols; lsp < lspend; lsp++) {
+ register struct nlist *p = &lsp->nzlist.nlist;
+
+ if (p->n_type == (N_SETV | N_EXT))
+ continue;
+
+ /*
+ * Turn magically prefixed symbols into set symbols of
+ * a corresponding type.
+ */
+ if (set_element_prefixes &&
+ set_element_prefixed_p(entry->strings+lsp->nzlist.nz_strx))
+ lsp->nzlist.nz_type += (N_SETA - N_ABS);
+
+ if (SET_ELEMENT_P(p->n_type)) {
+ set_symbol_count++;
+ if (!relocatable_output)
+ enter_global_ref(lsp,
+ p->n_un.n_strx + entry->strings, entry);
+ } else if (p->n_type == N_WARNING) {
+ char *name = p->n_un.n_strx + entry->strings;
+
+ /* Grab the next entry. */
+ p++;
+ if (p->n_type != (N_UNDF | N_EXT)) {
+ warnx(
+ "%s: Warning symbol without external reference following.",
+ get_file_name(entry));
+ make_executable = 0;
+ p--; /* Process normally. */
+ } else {
+ symbol *sp;
+ char *sname = p->n_un.n_strx + entry->strings;
+ /* Deal with the warning symbol. */
+ enter_global_ref(lsp,
+ p->n_un.n_strx + entry->strings, entry);
+ sp = getsym (sname);
+ sp->warning = (char *)xmalloc(strlen(name)+1);
+ strcpy (sp->warning, name);
+ warning_count++;
+ }
+ } else if (p->n_type & N_EXT) {
+ enter_global_ref(lsp,
+ p->n_un.n_strx + entry->strings, entry);
+ } else if (p->n_un.n_strx &&
+ (p->n_un.n_strx + entry->strings)[0] == LPREFIX)
+ lsp->flags |= LS_L_SYMBOL;
+ }
+
+}
+
+/*
+ * Enter one global symbol in the hash table. LSP points to the `struct
+ * localsymbol' from the file that describes the global symbol. NAME is the
+ * symbol's name. ENTRY is the file entry for the file the symbol comes from.
+ *
+ * LSP is put on the chain of all such structs that refer to the same symbol.
+ * This chain starts in the `refs' for symbols from relocatable objects. A
+ * backpointer to the global symbol is kept in LSP.
+ *
+ * Symbols from shared objects are linked through `soref'. For such symbols
+ * that's all we do at this stage, with the exception of the case where the
+ * symbol is a common. The `referenced' bit is only set for references from
+ * relocatable objects.
+ *
+ */
+
+static void
+enter_global_ref(lsp, name, entry)
+ struct localsymbol *lsp;
+ char *name;
+ struct file_entry *entry;
+{
+ register struct nzlist *nzp = &lsp->nzlist;
+ register symbol *sp = getsym(name);
+ register int type = nzp->nz_type;
+ int oldref = (sp->flags & GS_REFERENCED);
+ int olddef = sp->defined;
+ int com = sp->defined && sp->common_size;
+
+ if (type == (N_INDR | N_EXT)) {
+ sp->alias = getsym(entry->strings + (lsp + 1)->nzlist.nz_strx);
+ if (sp == sp->alias) {
+ warnx("%s: %s is alias for itself",
+ get_file_name(entry), name);
+ /* Rewrite symbol as global text symbol with value 0 */
+ lsp->nzlist.nz_type = N_TEXT|N_EXT;
+ lsp->nzlist.nz_value = 0;
+ make_executable = 0;
+ } else {
+ global_alias_count++;
+ }
+ }
+
+ if (entry->flags & E_DYNAMIC) {
+ lsp->next = sp->sorefs;
+ sp->sorefs = lsp;
+
+ /*
+ * Handle commons from shared objects:
+ * 1) If symbol hitherto undefined, turn it into a common.
+ * 2) If symbol already common, update size if necessary.
+ */
+/*XXX - look at case where commons are only in shared objects */
+ if (type == (N_UNDF | N_EXT) && nzp->nz_value) {
+ if (!olddef) {
+ if (oldref)
+ undefined_global_sym_count--;
+ common_defined_global_count++;
+ sp->common_size = nzp->nz_value;
+ sp->defined = N_UNDF | N_EXT;
+ } else if (com && sp->common_size < nzp->nz_value) {
+ sp->common_size = nzp->nz_value;
+ }
+ } else if (type != (N_UNDF | N_EXT) && !oldref) {
+ /*
+ * This is an ex common...
+ */
+ sp->common_size = 0;
+ sp->defined = 0;
+ }
+
+ /*
+ * Handle size information in shared objects.
+ */
+ if (nzp->nz_size > sp->size)
+ sp->size = nzp->nz_size;
+
+ lsp->symbol = sp;
+ return;
+ }
+
+ lsp->next = sp->refs;
+ sp->refs = lsp;
+ lsp->symbol = sp;
+
+ sp->flags |= GS_REFERENCED;
+
+ if (sp == dynamic_symbol || sp == got_symbol) {
+ if (type != (N_UNDF | N_EXT) && !(entry->flags & E_JUST_SYMS))
+ errx(1,"Linker reserved symbol %s defined as type %x ",
+ name, type);
+ return;
+ }
+
+#ifdef N_SIZE
+ if (type == (N_SIZE | N_EXT)) {
+ if (relocatable_output && nzp->nz_value != 0 && sp->size == 0)
+ size_sym_count++;
+ if (sp->size < nzp->nz_value)
+ sp->size = nzp->nz_value;
+ } else
+#endif
+ if (type != (N_UNDF | N_EXT) || nzp->nz_value) {
+
+ /*
+ * Set `->defined' here, so commons and undefined globals
+ * can be counted correctly.
+ */
+ if (!sp->defined || sp->defined == (N_UNDF | N_EXT))
+ sp->defined = type;
+
+ if (oldref && !olddef)
+ /*
+ * It used to be undefined and we're defining it.
+ */
+ undefined_global_sym_count--;
+ if (undefined_global_sym_count < 0)
+ errx(1,
+ "internal error: enter_glob_ref: undefined_global_sym_count = %d",
+ undefined_global_sym_count);
+
+ if (!olddef && type == (N_UNDF | N_EXT) && nzp->nz_value) {
+ /*
+ * First definition and it's common.
+ */
+ common_defined_global_count++;
+ sp->common_size = nzp->nz_value;
+ } else if (com && type != (N_UNDF | N_EXT)) {
+ /*
+ * It used to be common and we're defining
+ * it as something else.
+ */
+ common_defined_global_count--;
+ sp->common_size = 0;
+ } else if (com && type == (N_UNDF | N_EXT)
+ && sp->common_size < nzp->nz_value)
+ /*
+ * It used to be common and this is a new common entry
+ * to which we need to pay attention.
+ */
+ sp->common_size = nzp->nz_value;
+
+ if (SET_ELEMENT_P(type) && (!olddef || com))
+ set_vector_count++;
+
+ } else if (!oldref && !com)
+ /*
+ * An unreferenced symbol can already be defined
+ * as common by shared objects.
+ */
+ undefined_global_sym_count++;
+
+
+ if (sp == end_symbol && (entry->flags & E_JUST_SYMS) &&
+ !T_flag_specified)
+ text_start = nzp->nz_value;
+
+ if (sp->flags & GS_TRACE) {
+ register char *reftype;
+ switch (type & N_TYPE) {
+ case N_UNDF:
+ reftype = nzp->nz_value?
+ "defined as common":"referenced";
+ break;
+
+ case N_ABS:
+ reftype = "defined as absolute";
+ break;
+
+ case N_TEXT:
+ reftype = "defined in text section";
+ break;
+
+ case N_DATA:
+ reftype = "defined in data section";
+ break;
+
+ case N_BSS:
+ reftype = "defined in BSS section";
+ break;
+
+ default:
+ reftype = "I don't know this type";
+ break;
+ }
+
+ fprintf(stderr, "symbol %s %s in ", sp->name, reftype);
+ print_file_name (entry, stderr);
+ fprintf(stderr, "\n");
+ }
+}
+
+/*
+ * This returns 0 if the given file entry's symbol table does *not* contain
+ * the nlist point entry, and it returns the files entry pointer (cast to
+ * unsigned long) if it does.
+ */
+
+unsigned long
+contains_symbol(entry, np)
+ struct file_entry *entry;
+ register struct nlist *np;
+{
+ if (np >= &entry->symbols->nzlist.nlist &&
+ np < &(entry->symbols + entry->nsymbols)->nzlist.nlist)
+ return (unsigned long) entry;
+ return 0;
+}
+
+
+/*
+ * Having entered all the global symbols and found the sizes of sections of
+ * all files to be linked, make all appropriate deductions from this data.
+ *
+ * We propagate global symbol values from definitions to references. We compute
+ * the layout of the output file and where each input file's contents fit
+ * into it.
+ *
+ * This is now done in several stages.
+ *
+ * 1) All global symbols are examined for definitions in relocatable (.o)
+ * files. The symbols' type is set according to the definition found,
+ * but its value can not yet be determined. In stead, we keep a pointer
+ * to the file entry's localsymbol that bequeathed the global symbol with
+ * its definition. Also, multiple (incompatible) definitions are checked
+ * for in this pass. If no definition comes forward, the set of local
+ * symbols originating from shared objects is searched for a definition.
+ *
+ * 2) Then the relocation information of each relocatable file is examined
+ * for possible contributions to the RRS section.
+ *
+ * 3) When this is done, the sizes and start addresses are set of all segments
+ * that will appear in the output file (including the RRS segment).
+ *
+ * 4) Finally, all symbols are relocated according according to the start
+ * of the entry they are part of. Then global symbols are assigned their
+ * final values. Also, space for commons and imported data are allocated
+ * during this pass, if the link mode in effect so demands.
+ *
+ */
+
+static void
+digest_symbols()
+{
+
+ if (trace_files)
+ fprintf(stderr, "Digesting symbol information:\n\n");
+
+ if (!relocatable_output) {
+ /*
+ * The set sector size is the number of set elements + a word
+ * for each symbol for the length word at the beginning of
+ * the vector, plus a word for each symbol for a zero at the
+ * end of the vector (for incremental linking).
+ */
+ set_sect_size = (set_symbol_count + 2 * set_vector_count) *
+ sizeof (unsigned long);
+ set_vectors = (long *)xmalloc (set_sect_size);
+ setv_fill_count = 0;
+ }
+
+ /* Pass 1: check and define symbols */
+ defined_global_sym_count = 0;
+ digest_pass1();
+
+ each_full_file(consider_relocation, (void *)0); /* Text */
+ each_full_file(consider_relocation, (void *)1); /* Data */
+
+ each_file(consider_local_symbols, (void *)0);
+
+ /*
+ * Compute total size of sections.
+ * RRS data is the first output data section, RRS text is the last
+ * text section. Thus, DATA_START is calculated from RRS_DATA_START
+ * and RRS_DATA_SIZE, while RRS_TEXT_START is derived from TEXT_START
+ * and TEXT_SIZE.
+ */
+ consider_rrs_section_lengths();
+ each_full_file(consider_file_section_lengths, 0);
+ rrs_text_start = text_start + text_size;
+ text_size += rrs_text_size;
+ data_size += rrs_data_size;
+
+ /*
+ * If necessary, pad text section to full page in the file. Include
+ * the padding in the text segment size.
+ */
+
+ if (page_align_segments || page_align_data) {
+ int text_end = text_size + N_TXTOFF(outheader);
+ text_pad = PALIGN(text_end, page_size) - text_end;
+ text_size += text_pad;
+ }
+ outheader.a_text = text_size;
+
+ /*
+ * Make the data segment address start in memory on a suitable
+ * boundary.
+ */
+
+ if (!Tdata_flag_specified)
+ rrs_data_start = text_start +
+ DATA_START(outheader) - TEXT_START(outheader);
+
+ data_start = rrs_data_start + rrs_data_size;
+ if (!relocatable_output) {
+ set_sect_start = rrs_data_start + data_size;
+ data_size += MALIGN(set_sect_size);
+ }
+ bss_start = rrs_data_start + data_size;
+
+#ifdef DEBUG
+printf("textstart = %#x, textsize = %#x, rrs_text_start = %#x, rrs_text_size %#x\n",
+ text_start, text_size, rrs_text_start, rrs_text_size);
+printf("datastart = %#x, datasize = %#x, rrs_data_start %#x, rrs_data_size %#x\n",
+ data_start, data_size, rrs_data_start, rrs_data_size);
+printf("bssstart = %#x, bsssize = %#x\n",
+ bss_start, bss_size);
+printf("set_sect_start = %#x, set_sect_size = %#x\n",
+ set_sect_start, set_sect_size);
+#endif
+
+ /* Compute start addresses of each file's sections and symbols. */
+
+ each_full_file(relocate_file_addresses, 0);
+ relocate_rrs_addresses();
+
+ /* Pass 2: assign values to symbols */
+ digest_pass2();
+
+ if (end_symbol) { /* These are null if -r. */
+ etext_symbol->value = text_start + text_size - text_pad;
+ edata_symbol->value = rrs_data_start + data_size;
+ end_symbol->value = rrs_data_start + data_size + bss_size;
+ }
+ /*
+ * Figure the data_pad now, so that it overlaps with the bss
+ * addresses.
+ */
+
+ if (specified_data_size && specified_data_size > data_size)
+ data_pad = specified_data_size - data_size;
+
+ if (page_align_segments)
+ data_pad = PALIGN(data_pad + data_size, page_size) - data_size;
+
+ bss_size -= data_pad;
+ if (bss_size < 0)
+ bss_size = 0;
+
+ data_size += data_pad;
+
+ /*
+ * Calculate total number of symbols that will go into
+ * the output symbol table (barring DISCARD_* settings).
+ */
+ global_sym_count = defined_global_sym_count +
+ undefined_global_sym_count;
+
+ if (dynamic_symbol->flags & GS_REFERENCED)
+ global_sym_count++;
+
+ if (got_symbol->flags & GS_REFERENCED)
+ global_sym_count++;
+
+ if (relocatable_output || building_shared_object)
+ /* For each alias we write out two struct nlists */
+ global_sym_count += global_alias_count;
+
+ if (relocatable_output)
+ /* We write out the original N_SET* symbols */
+ global_sym_count += size_sym_count;
+
+#ifdef DEBUG
+printf("global symbols %d (defined %d, undefined %d, aliases %d), locals: %d, \
+debug symbols: %d, set_symbols %d\n",
+ global_sym_count,
+ defined_global_sym_count, undefined_global_sym_count, global_alias_count,
+ local_sym_count, debugger_sym_count, set_symbol_count);
+#endif
+}
+
+/*
+ * Determine the definition of each global symbol.
+ */
+static void
+digest_pass1()
+{
+
+ /*
+ * For each symbol, verify that it is defined globally at most
+ * once within relocatable files (except when building a shared lib).
+ * and set the `defined' field if there is a definition.
+ *
+ * Then check the shared object symbol chain for any remaining
+ * undefined symbols. Set the `so_defined' field for any
+ * definition find this way.
+ */
+ FOR_EACH_SYMBOL(i, sp) {
+ struct localsymbol *lsp;
+ int defs = 0;
+
+ if (!(sp->flags & GS_REFERENCED)) {
+#if 0
+ /* Check for undefined symbols in shared objects */
+ int type;
+ for (lsp = sp->sorefs; lsp; lsp = lsp->next) {
+ type = lsp->nzlist.nlist.n_type;
+ if ((type & N_EXT) && type != (N_UNDF | N_EXT))
+ break;
+ }
+ if ((type & N_EXT) && type == (N_UNDF | N_EXT))
+ undefined_shobj_sym_count++;
+#endif
+
+ /* Superfluous symbol from shared object */
+ continue;
+ }
+ if (sp->so_defined)
+ /* Already examined; must have been an alias */
+ continue;
+
+ if (sp == got_symbol || sp == dynamic_symbol)
+ continue;
+
+ for (lsp = sp->refs; lsp; lsp = lsp->next) {
+ register struct nlist *p = &lsp->nzlist.nlist;
+ register int type = p->n_type;
+
+ if (SET_ELEMENT_P(type)) {
+ if (relocatable_output)
+ errx(1,
+ "internal error: global ref to set el %s with -r",
+ sp->name);
+ if (!defs++) {
+ sp->defined = N_SETV | N_EXT;
+ sp->value =
+ setv_fill_count++ * sizeof(long);
+ } else if ((sp->defined & N_TYPE) != N_SETV) {
+ sp->mult_defs = 1;
+ multiple_def_count++;
+ }
+ /* Keep count and remember symbol */
+ sp->setv_count++;
+ set_vectors[setv_fill_count++] = (long)p;
+ if (building_shared_object) {
+ struct relocation_info reloc;
+
+ /*
+ * Make sure to relocate the contents
+ * of this set vector.
+ */
+ bzero(&reloc, sizeof(reloc));
+ RELOC_ADDRESS(&reloc) =
+ setv_fill_count * sizeof(long);
+ alloc_rrs_segment_reloc(NULL, &reloc);
+ }
+
+ } else if ((type & N_EXT) && type != (N_UNDF | N_EXT)
+ && (type & N_TYPE) != N_FN
+ && (type & N_TYPE) != N_SIZE) {
+ /* non-common definition */
+ if (defs++ && sp->value != p->n_value
+ && entry_symbol/*XXX*/) {
+ sp->mult_defs = 1;
+ multiple_def_count++;
+ }
+ sp->def_nlist = p;
+ lsp->entry->flags |= E_SYMBOLS_USED;
+ sp->defined = type;
+ sp->aux = N_AUX(p);
+ }
+ }
+
+ if (sp->defined) {
+ if ((sp->defined & N_TYPE) == N_SETV)
+ /* Allocate zero entry in set vector */
+ setv_fill_count++;
+ /*
+ * At this stage, we do not know whether an alias
+ * is going to be defined for real here, or whether
+ * it refers to a shared object symbol. The decision
+ * is deferred until digest_pass2().
+ */
+ if (!sp->alias)
+ defined_global_sym_count++;
+ continue;
+ }
+
+ if (relocatable_output)
+ /* We're done */
+ continue;
+
+ /*
+ * Still undefined, search the shared object symbols for a
+ * definition. This symbol must go into the RRS.
+ */
+ if (building_shared_object) {
+ /* Just punt for now */
+ undefined_global_sym_count--;
+ if (undefined_global_sym_count < 0)
+ errx(1,
+ "internal error: digest_pass1,1: %s: undefined_global_sym_count = %d",
+ sp->name, undefined_global_sym_count);
+ continue;
+ }
+
+ again:
+ for (lsp = sp->sorefs; lsp; lsp = lsp->next) {
+ register struct nlist *p = &lsp->nzlist.nlist;
+ register int type = p->n_type;
+
+ if ((type & N_EXT) && type != (N_UNDF | N_EXT) &&
+ (type & N_TYPE) != N_FN) {
+ /* non-common definition */
+ sp->def_nlist = p;
+ lsp->entry->flags |= E_SYMBOLS_USED;
+ sp->so_defined = type;
+ sp->aux = N_AUX(p);
+ if (sp->flags & GS_REFERENCED)
+ undefined_global_sym_count--;
+ else
+ sp->flags |= GS_REFERENCED;
+#ifdef DEBUG
+printf("shr: %s gets defined to %x with value %x\n", sp->name, type, sp->value);
+#endif
+ if (undefined_global_sym_count < 0)
+ errx(1,
+ "internal error: digest_pass1,2: %s: undefined_global_sym_count = %d",
+ sp->name, undefined_global_sym_count);
+ if (sp->alias && !(sp->alias->flags & GS_REFERENCED)) {
+ sp = sp->alias;
+ goto again;
+ }
+ break;
+ }
+ }
+ } END_EACH_SYMBOL;
+
+ if (setv_fill_count != set_sect_size/sizeof(long))
+ errx(1, "internal error: allocated set symbol space (%d) \
+doesn't match actual (%d)",
+ set_sect_size/sizeof(long), setv_fill_count);
+}
+
+
+/*
+ * Scan relocation info in ENTRY for contributions to the RRS section
+ * of the output file.
+ */
+static void
+consider_relocation(entry, dataseg)
+ struct file_entry *entry;
+ int dataseg;
+{
+ struct relocation_info *reloc, *end;
+ struct localsymbol *lsp;
+ symbol *sp;
+
+ if (dataseg == 0) {
+ /* Text relocations */
+ reloc = entry->textrel;
+ end = entry->textrel + entry->ntextrel;
+ } else {
+ /* Data relocations */
+ reloc = entry->datarel;
+ end = entry->datarel + entry->ndatarel;
+ }
+
+ for (; reloc < end; reloc++) {
+
+ if (relocatable_output) {
+ lsp = &entry->symbols[reloc->r_symbolnum];
+ if (RELOC_BASEREL_P(reloc)) {
+ pic_code_seen = 1;
+ if (!RELOC_EXTERN_P(reloc))
+ lsp->flags |= LS_RENAME;
+ }
+ continue;
+ }
+
+ /*
+ * First, do the PIC specific relocs.
+ * r_relative and r_copy should not occur at this point
+ * (we do output them). The others break down to these
+ * combinations:
+ *
+ * jmptab: extern: needs jmp slot
+ * !extern: "intersegment" jump/call,
+ * should get resolved in output
+ *
+ * baserel: extern: need GOT entry
+ * !extern: may need GOT entry,
+ * machine dependent
+ *
+ * baserel's always refer to symbol through `r_symbolnum'
+ * whether extern or not. Internal baserels refer to statics
+ * that must be accessed either *through* the GOT table like
+ * global data, or by means of an offset from the GOT table.
+ * The macro RELOC_STATICS_THROUGH_GOT_P() determines which
+ * applies, since this is a machine (compiler?) dependent
+ * addressing mode.
+ */
+
+ if (RELOC_JMPTAB_P(reloc)) {
+
+ if (!RELOC_EXTERN_P(reloc))
+ continue;
+
+ lsp = &entry->symbols[reloc->r_symbolnum];
+ sp = lsp->symbol;
+ if (sp->alias)
+ sp = sp->alias;
+ if (sp->flags & GS_TRACE) {
+ fprintf(stderr, "symbol %s has jmpslot in %s\n",
+ sp->name, get_file_name(entry));
+ }
+ alloc_rrs_jmpslot(entry, sp);
+
+ } else if (RELOC_BASEREL_P(reloc)) {
+
+ lsp = &entry->symbols[reloc->r_symbolnum];
+ alloc_rrs_gotslot(entry, reloc, lsp);
+
+ } else if (RELOC_EXTERN_P(reloc)) {
+
+ /*
+ * Non-PIC relocations.
+ * If the definition comes from a shared object
+ * we need a relocation entry in RRS.
+ *
+ * If the .so definition is N_TEXT a jmpslot is
+ * allocated.
+ *
+ * If it is N_DATA we allocate an address in BSS (?)
+ * and arrange for the data to be copied at run-time.
+ * The symbol is temporarily marked with N_SIZE in
+ * the `defined' field, so we know what to do in
+ * pass2() and during actual relocation. We convert
+ * the type back to something real again when writing
+ * out the symbols.
+ *
+ */
+ lsp = &entry->symbols[reloc->r_symbolnum];
+ sp = lsp->symbol;
+ if (sp == NULL)
+ errx(1, "%s: internal error, sp==NULL",
+ get_file_name(entry));
+
+ if (sp->alias)
+ sp = sp->alias;
+
+ /*
+ * Skip refs to _GLOBAL_OFFSET_TABLE_ and __DYNAMIC
+ */
+ if (sp == got_symbol) {
+ if (!CHECK_GOT_RELOC(reloc))
+ errx(1,
+ "%s: Unexpected relocation type for GOT symbol",
+ get_file_name(entry));
+ continue;
+ }
+
+ /*
+ * This symbol gives rise to a RRS entry
+ */
+
+ if (building_shared_object) {
+ if (sp->flags & GS_TRACE) {
+ fprintf(stderr,
+ "symbol %s RRS entry in %s\n",
+ sp->name, get_file_name(entry));
+ }
+ alloc_rrs_reloc(entry, sp);
+ continue;
+ }
+
+ if (force_alias_definition && sp->so_defined &&
+ sp->aux == AUX_FUNC) {
+
+ /* Call to shared library procedure */
+ alloc_rrs_jmpslot(entry, sp);
+
+ } else if (sp->size && sp->so_defined &&
+ sp->aux == AUX_OBJECT) {
+
+ /* Reference to shared library data */
+ alloc_rrs_cpy_reloc(entry, sp);
+ sp->defined = N_SIZE;
+
+ } else if (!sp->defined && sp->common_size == 0)
+ alloc_rrs_reloc(entry, sp);
+
+ } else {
+ /*
+ * Segment relocation.
+ * Prepare an RRS relocation as these are load
+ * address dependent.
+ */
+ if (building_shared_object && !RELOC_PCREL_P(reloc)) {
+ alloc_rrs_segment_reloc(entry, reloc);
+ }
+ }
+ }
+}
+
+/*
+ * Determine the disposition of each local symbol.
+ */
+static void
+consider_local_symbols(entry)
+ register struct file_entry *entry;
+{
+ register struct localsymbol *lsp, *lspend;
+
+ if (entry->flags & E_DYNAMIC)
+ return;
+
+ lspend = entry->symbols + entry->nsymbols;
+
+ /*
+ * For each symbol determine whether it should go
+ * in the output symbol table.
+ */
+
+ for (lsp = entry->symbols; lsp < lspend; lsp++) {
+ register struct nlist *p = &lsp->nzlist.nlist;
+ register int type = p->n_type;
+
+ if (type == N_WARNING)
+ continue;
+
+ if (SET_ELEMENT_P (type)) {
+ /*
+ * This occurs even if global. These types of
+ * symbols are never written globally, though
+ * they are stored globally.
+ */
+ if (relocatable_output)
+ lsp->flags |= LS_WRITE;
+
+ } else if (!(type & (N_STAB | N_EXT))) {
+
+ /*
+ * Ordinary local symbol
+ */
+ if ((lsp->flags & LS_RENAME) || (
+ discard_locals != DISCARD_ALL &&
+ !(discard_locals == DISCARD_L &&
+ (lsp->flags & LS_L_SYMBOL))) ) {
+
+ lsp->flags |= LS_WRITE;
+ local_sym_count++;
+ }
+
+ } else if (!(type & N_EXT)) {
+
+ /*
+ * Debugger symbol
+ */
+ if (strip_symbols == STRIP_NONE) {
+ lsp->flags |= LS_WRITE;
+ debugger_sym_count++;
+ }
+ }
+ }
+
+ /*
+ * Count one for the local symbol that we generate,
+ * whose name is the file's name (usually) and whose address
+ * is the start of the file's text.
+ */
+ if (discard_locals != DISCARD_ALL)
+ local_sym_count++;
+}
+
+/*
+ * Accumulate the section sizes of input file ENTRY into the section sizes of
+ * the output file.
+ */
+static void
+consider_file_section_lengths(entry)
+ register struct file_entry *entry;
+{
+
+ entry->text_start_address = text_size;
+ /* If there were any vectors, we need to chop them off */
+ text_size += entry->header.a_text;
+ entry->data_start_address = data_size;
+ data_size += entry->header.a_data;
+ entry->bss_start_address = bss_size;
+ bss_size += entry->header.a_bss;
+
+ text_reloc_size += entry->header.a_trsize;
+ data_reloc_size += entry->header.a_drsize;
+}
+
+/*
+ * Determine where the sections of ENTRY go into the output file,
+ * whose total section sizes are already known.
+ * Also relocate the addresses of the file's local and debugger symbols.
+ */
+static void
+relocate_file_addresses(entry)
+ register struct file_entry *entry;
+{
+ register struct localsymbol *lsp, *lspend;
+
+ entry->text_start_address += text_start;
+ /*
+ * Note that `data_start' and `data_size' have not yet been
+ * adjusted for `data_pad'. If they had been, we would get the wrong
+ * results here.
+ */
+ entry->data_start_address += data_start;
+ entry->bss_start_address += bss_start;
+#ifdef DEBUG
+printf("%s: datastart: %#x, bss %#x\n", get_file_name(entry),
+ entry->data_start_address, entry->bss_start_address);
+#endif
+
+ lspend = entry->symbols + entry->nsymbols;
+
+ for (lsp = entry->symbols; lsp < lspend; lsp++) {
+ register struct nlist *p = &lsp->nzlist.nlist;
+ register int type = p->n_type;
+
+ /*
+ * If this belongs to a section, update it
+ * by the section's start address
+ */
+
+ switch (type & N_TYPE) {
+ case N_TEXT:
+ case N_SETT:
+ p->n_value += entry->text_start_address;
+ break;
+ case N_DATA:
+ case N_SETD:
+ case N_SETV:
+ /*
+ * A symbol whose value is in the data section is
+ * present in the input file as if the data section
+ * started at an address equal to the length of the
+ * file's text.
+ */
+ p->n_value += entry->data_start_address -
+ entry->header.a_text;
+ break;
+ case N_BSS:
+ case N_SETB:
+ /* likewise for symbols with value in BSS. */
+ p->n_value += entry->bss_start_address -
+ (entry->header.a_text +
+ entry->header.a_data);
+ break;
+ }
+
+ }
+
+}
+
+/*
+ * Assign a value to each global symbol.
+ */
+static void
+digest_pass2()
+{
+ FOR_EACH_SYMBOL(i, sp) {
+ int size;
+ int align = sizeof(int);
+
+ if (!(sp->flags & GS_REFERENCED))
+ continue;
+
+ if (sp->alias &&
+ (relocatable_output || building_shared_object ||
+ (sp->alias->defined && !sp->alias->so_defined)))
+ /*
+ * The alias points at a defined symbol, so it
+ * must itself be counted as one too, in order to
+ * compute the correct number of symbol table entries.
+ */
+ defined_global_sym_count++;
+
+ if ((sp->defined & N_TYPE) == N_SETV) {
+ /*
+ * Set length word at front of vector and zero byte
+ * at end. Reverse the vector itself to put it in
+ * file order.
+ */
+ unsigned long i, *p, *q;
+ unsigned long length_word_index =
+ sp->value / sizeof(long);
+
+ /* Relocate symbol value */
+ sp->value += set_sect_start;
+
+ set_vectors[length_word_index] = sp->setv_count;
+
+ /*
+ * Relocate vector to final address.
+ */
+ for (i = 0; i < sp->setv_count; i++) {
+ struct nlist *p = (struct nlist *)
+ set_vectors[1+i+length_word_index];
+
+ set_vectors[1+i+length_word_index] = p->n_value;
+ if (building_shared_object) {
+ struct relocation_info reloc;
+
+ bzero(&reloc, sizeof(reloc));
+ RELOC_ADDRESS(&reloc) =
+ (1 + i + length_word_index) *
+ sizeof(long)
+ + set_sect_start;
+ RELOC_TYPE(&reloc) =
+ (p->n_type - (N_SETA - N_ABS)) & N_TYPE;
+ claim_rrs_segment_reloc(NULL, &reloc);
+ }
+ }
+
+ /*
+ * Reverse the vector.
+ */
+ p = &set_vectors[length_word_index + 1];
+ q = &set_vectors[length_word_index + sp->setv_count];
+ while (p < q) {
+ unsigned long tmp = *p;
+ *p++ = *q;
+ *q-- = tmp;
+ }
+
+ /* Clear terminating entry */
+ set_vectors[length_word_index + sp->setv_count + 1] = 0;
+ continue;
+ }
+
+
+ if (sp->defined && sp->def_nlist &&
+ ((sp->defined & ~N_EXT) != N_SETV))
+ sp->value = sp->def_nlist->n_value;
+
+ /*
+ * If not -r'ing, allocate common symbols in the BSS section.
+ */
+ if (building_shared_object && !(link_mode & SYMBOLIC))
+ /* No common allocation in shared objects */
+ continue;
+
+ if ((size = sp->common_size) != 0) {
+ /*
+ * It's a common.
+ */
+ if (sp->defined != (N_UNDF + N_EXT))
+ errx(1, "%s: common isn't", sp->name);
+
+ } else if ((size = sp->size) != 0 && sp->defined == N_SIZE) {
+ /*
+ * It's data from shared object with size info.
+ */
+ if (!sp->so_defined)
+ errx(1, "%s: Bogus N_SIZE item", sp->name);
+
+ } else
+ /*
+ * It's neither
+ */
+ continue;
+
+
+ if (relocatable_output && !force_common_definition) {
+ sp->defined = 0;
+ undefined_global_sym_count++;
+ defined_global_sym_count--;
+ continue;
+ }
+
+ /*
+ * Round up to nearest sizeof (int). I don't know whether
+ * this is necessary or not (given that alignment is taken
+ * care of later), but it's traditional, so I'll leave it in.
+ * Note that if this size alignment is ever removed, ALIGN
+ * above will have to be initialized to 1 instead of sizeof
+ * (int).
+ */
+
+ size = PALIGN(size, sizeof(int));
+
+ while (!(size & align))
+ align <<= 1;
+
+ align = align > MAX_ALIGNMENT ? MAX_ALIGNMENT : align;
+
+ bss_size = PALIGN(bss_size + data_size + rrs_data_start, align)
+ - (data_size + rrs_data_start);
+
+ sp->value = rrs_data_start + data_size + bss_size;
+ if (sp->defined == (N_UNDF | N_EXT))
+ sp->defined = N_BSS | N_EXT;
+ else {
+ sp->so_defined = 0;
+ defined_global_sym_count++;
+ }
+ bss_size += size;
+ if (write_map)
+ printf("Allocating %s %s: %x at %x\n",
+ sp->defined==(N_BSS|N_EXT)?"common":"data",
+ sp->name, size, sp->value);
+
+ } END_EACH_SYMBOL;
+}
+
+
+/* -------------------------------------------------------------------*/
+
+/* Write the output file */
+void
+write_output()
+{
+ struct stat statbuf;
+ int filemode;
+
+ if (lstat(output_filename, &statbuf) == 0) {
+ if (S_ISREG(statbuf.st_mode))
+ (void)unlink(output_filename);
+ }
+
+ outdesc = open(output_filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (outdesc < 0)
+ err(1, "open: %s", output_filename);
+
+ if (atexit(cleanup))
+ err(1, "atexit");
+
+ if (fstat (outdesc, &statbuf) < 0)
+ err(1, "fstat: %s", output_filename);
+
+ filemode = statbuf.st_mode;
+
+ chmod (output_filename, filemode & ~0111);
+
+ /* Output the a.out header. */
+ write_header();
+
+ /* Output the text and data segments, relocating as we go. */
+ write_text();
+ write_data();
+
+ /* Output the merged relocation info, if requested with `-r'. */
+ if (relocatable_output)
+ write_rel();
+
+ /* Output the symbol table (both globals and locals). */
+ write_syms();
+
+ /* Output the RSS section */
+ write_rrs();
+
+ if (chmod (output_filename, filemode | 0111) == -1)
+ err(1, "chmod: %s", output_filename);
+
+ close(outdesc);
+ outdesc = 0;
+}
+
+/* Total number of symbols to be written in the output file. */
+static int nsyms;
+
+void
+write_header()
+{
+ int flags = (rrs_section_type == RRS_FULL) ? EX_DYNAMIC : 0;
+
+ if (oldmagic && (flags & EX_DYNAMIC))
+ warnx("Cannot set flag in old magic headers\n");
+
+ N_SET_FLAG (outheader, flags);
+
+ outheader.a_text = text_size;
+ outheader.a_data = data_size;
+ outheader.a_bss = bss_size;
+ outheader.a_entry = (entry_symbol ? entry_symbol->value
+ : text_start + entry_offset);
+
+ if (strip_symbols == STRIP_ALL)
+ nsyms = 0;
+ else
+ nsyms = global_sym_count + local_sym_count + debugger_sym_count;
+
+ if (relocatable_output)
+ nsyms += set_symbol_count;
+
+ outheader.a_syms = nsyms * sizeof (struct nlist);
+
+ if (relocatable_output) {
+ outheader.a_trsize = text_reloc_size;
+ outheader.a_drsize = data_reloc_size;
+ } else {
+ outheader.a_trsize = 0;
+ outheader.a_drsize = 0;
+ }
+
+ md_swapout_exec_hdr(&outheader);
+ mywrite(&outheader, sizeof (struct exec), 1, outdesc);
+ md_swapin_exec_hdr(&outheader);
+
+ /*
+ * Output whatever padding is required in the executable file
+ * between the header and the start of the text.
+ */
+
+#ifndef COFF_ENCAPSULATE
+ padfile(N_TXTOFF(outheader) - sizeof outheader, outdesc);
+#endif
+}
+
+/*
+ * Relocate the text segment of each input file
+ * and write to the output file.
+ */
+void
+write_text()
+{
+
+ if (trace_files)
+ fprintf(stderr, "Copying and relocating text:\n\n");
+
+ each_full_file(copy_text, 0);
+ file_close();
+
+ if (trace_files)
+ fprintf(stderr, "\n");
+
+ padfile(text_pad, outdesc);
+}
+
+/*
+ * Read the text segment contents of ENTRY, relocate them, and write the
+ * result to the output file. If `-r', save the text relocation for later
+ * reuse.
+ */
+void
+copy_text(entry)
+ struct file_entry *entry;
+{
+ register char *bytes;
+ register int fd;
+
+ if (trace_files)
+ prline_file_name(entry, stderr);
+
+ fd = file_open(entry);
+
+ /* Allocate space for the file's text section */
+ bytes = (char *)alloca(entry->header.a_text);
+
+ /* Deal with relocation information however is appropriate */
+ if (entry->textrel == NULL)
+ errx(1, "%s: no text relocation", get_file_name(entry));
+
+ /* Read the text section into core. */
+ if (lseek(fd, text_offset(entry), L_SET) == (off_t)-1)
+ err(1, "%s: copy_text: lseek", get_file_name(entry));
+ if (entry->header.a_text != read(fd, bytes, entry->header.a_text))
+ errx(1, "%s: copy_text: premature EOF in text section", get_file_name(entry));
+
+ /* Relocate the text according to the text relocation. */
+ perform_relocation (bytes, entry->header.a_text,
+ entry->textrel, entry->ntextrel, entry, 0);
+
+ /* Write the relocated text to the output file. */
+ mywrite(bytes, 1, entry->header.a_text, outdesc);
+}
+
+/*
+ * Relocate the data segment of each input file
+ * and write to the output file.
+ */
+
+void
+write_data()
+{
+ off_t pos;
+
+ if (trace_files)
+ fprintf(stderr, "Copying and relocating data:\n\n");
+
+ pos = N_DATOFF(outheader) + data_start - rrs_data_start;
+ if (lseek(outdesc, pos, L_SET) != pos)
+ errx(1, "write_data: failed to lseek to data offset");
+
+ each_full_file(copy_data, 0);
+ file_close();
+
+ /*
+ * Write out the set element vectors. See digest symbols for
+ * description of length of the set vector section.
+ */
+
+ if (set_vector_count) {
+ swap_longs(set_vectors, set_symbol_count + 2*set_vector_count);
+ mywrite(set_vectors, set_symbol_count + 2*set_vector_count,
+ sizeof (unsigned long), outdesc);
+ }
+
+ if (trace_files)
+ fprintf(stderr, "\n");
+
+ padfile(data_pad, outdesc);
+}
+
+/*
+ * Read the data segment contents of ENTRY, relocate them, and write the
+ * result to the output file. If `-r', save the data relocation for later
+ * reuse. See comments in `copy_text'.
+ */
+void
+copy_data(entry)
+ struct file_entry *entry;
+{
+ register char *bytes;
+ register int fd;
+
+ if (trace_files)
+ prline_file_name (entry, stderr);
+
+ fd = file_open(entry);
+
+ bytes = (char *)alloca(entry->header.a_data);
+
+ if (entry->datarel == NULL)
+ errx(1, "%s: no data relocation", get_file_name(entry));
+
+ if (lseek(fd, text_offset(entry) + entry->header.a_text, L_SET) ==
+ (off_t)-1)
+ err(1, "%s: copy_data: lseek", get_file_name(entry));
+ if (entry->header.a_data != read(fd, bytes, entry->header.a_data))
+ errx(1, "%s: copy_data: premature EOF in data section", get_file_name(entry));
+
+ perform_relocation(bytes, entry->header.a_data,
+ entry->datarel, entry->ndatarel, entry, 1);
+
+ mywrite(bytes, 1, entry->header.a_data, outdesc);
+}
+
+/*
+ * Relocate ENTRY's text or data section contents. DATA is the address of the
+ * contents, in core. DATA_SIZE is the length of the contents. PC_RELOCATION
+ * is the difference between the address of the contents in the output file
+ * and its address in the input file. RELOC is the address of the
+ * relocation info, in core. NRELOC says how many there are.
+ */
+
+/* HACK: md.c may need access to this */
+int pc_relocation;
+
+void
+perform_relocation(data, data_size, reloc, nreloc, entry, dataseg)
+ char *data;
+ int data_size;
+ struct relocation_info *reloc;
+ int nreloc;
+ struct file_entry *entry;
+ int dataseg;
+{
+
+ register struct relocation_info *r = reloc;
+ struct relocation_info *end = reloc + nreloc;
+
+ int text_relocation = entry->text_start_address;
+ int data_relocation = entry->data_start_address - entry->header.a_text;
+ int bss_relocation = entry->bss_start_address -
+ entry->header.a_text - entry->header.a_data;
+ pc_relocation = dataseg?
+ entry->data_start_address - entry->header.a_text:
+ entry->text_start_address;
+
+ for (; r < end; r++) {
+ int addr = RELOC_ADDRESS(r);
+ long addend = md_get_addend(r, data+addr);
+ long relocation;
+
+ /*
+ * Loop over the relocations again as we did in
+ * consider_relocation(), claiming the reserved RRS
+ * relocations.
+ */
+
+ if (addr >= data_size)
+ errx(1, "%s: relocation address out of range",
+ get_file_name(entry));
+
+ if (RELOC_JMPTAB_P(r)) {
+
+ int symindex = RELOC_SYMBOL(r);
+ struct localsymbol *lsp = &entry->symbols[symindex];
+ symbol *sp;
+
+ if (symindex >= entry->nsymbols)
+ errx(1, "%s: relocation symbolnum out of range",
+ get_file_name(entry));
+
+ sp = lsp->symbol;
+ if (sp->alias)
+ sp = sp->alias;
+
+ if (relocatable_output)
+ relocation = addend;
+ else if (!RELOC_EXTERN_P(r)) {
+ relocation = addend +
+ data_relocation - text_relocation;
+ } else
+ relocation = addend +
+ claim_rrs_jmpslot(entry, r, sp, addend);
+
+ } else if (RELOC_BASEREL_P(r)) {
+
+ int symindex = RELOC_SYMBOL(r);
+ struct localsymbol *lsp = &entry->symbols[symindex];
+
+ if (symindex >= entry->nsymbols)
+ errx(1, "%s: relocation symbolnum out of range",
+ get_file_name(entry));
+
+ if (relocatable_output)
+ relocation = addend;
+ else if (!RELOC_EXTERN_P(r))
+ relocation = claim_rrs_internal_gotslot(
+ entry, r, lsp, addend);
+ else
+ relocation = claim_rrs_gotslot(
+ entry, r, lsp, addend);
+
+ } else if (RELOC_EXTERN_P(r)) {
+
+ int symindex = RELOC_SYMBOL(r);
+ symbol *sp;
+
+ if (symindex >= entry->nsymbols)
+ errx(1, "%s: relocation symbolnum out of range",
+ get_file_name(entry));
+
+ sp = entry->symbols[symindex].symbol;
+ if (sp->alias)
+ sp = sp->alias;
+
+ if (relocatable_output) {
+ relocation = addend;
+ /*
+ * In PIC code, we keep the reference to the
+ * external symbol, even if defined now.
+ */
+ if (!pic_code_seen)
+ relocation += sp->value;
+ } else if (sp->defined) {
+ if (sp->flags & GS_TRACE) {
+ fprintf(stderr,
+ "symbol %s defined as %x in %s\n",
+ sp->name, sp->defined,
+ get_file_name(entry) );
+ }
+ if (sp == got_symbol) {
+ /* Handle _GOT_ refs */
+ relocation = addend + sp->value
+ + md_got_reloc(r);
+ } else if (building_shared_object) {
+ /*
+ * Normal (non-PIC) relocation needs
+ * to be converted into an RRS reloc
+ * when building a shared object.
+ */
+ r->r_address += dataseg?
+ entry->data_start_address:
+ entry->text_start_address;
+ relocation = addend;
+ if (claim_rrs_reloc(
+ entry, r, sp, &relocation))
+ continue;
+ } else if (sp->defined == N_SIZE) {
+ /*
+ * If size is known, arrange a
+ * run-time copy.
+ */
+ if (!sp->size)
+ errx(1, "Copy item isn't: %s",
+ sp->name);
+
+ relocation = addend + sp->value;
+ r->r_address = sp->value;
+ claim_rrs_cpy_reloc(entry, r, sp);
+ } else
+ /* Plain old relocation */
+ relocation = addend + sp->value;
+ } else {
+ /*
+ * If the symbol is undefined, we relocate it
+ * in a way similar to -r case. We use an
+ * RRS relocation to resolve the symbol at
+ * run-time. The r_address field is updated
+ * to reflect the changed position in the
+ * output file.
+ */
+ if (sp->flags & GS_TRACE) {
+ fprintf(stderr,
+ "symbol %s claims RRS in %s%s\n",
+ sp->name, get_file_name(entry),
+ (sp->so_defined == (N_TEXT+N_EXT) &&
+ sp->jmpslot_offset != -1)?
+ " (JMPSLOT)":"");
+ }
+ if (sp->so_defined == (N_TEXT+N_EXT) &&
+ sp->jmpslot_offset != -1) {
+ /*
+ * Claim a jmpslot if one was allocated.
+ *
+ * At this point, a jmpslot can only
+ * result from a shared object reference
+ * while `force_alias' is in effect.
+ */
+ relocation = addend +
+ claim_rrs_jmpslot(
+ entry, r, sp, addend);
+ } else {
+ r->r_address += dataseg?
+ entry->data_start_address:
+ entry->text_start_address;
+ relocation = addend;
+ if (claim_rrs_reloc(
+ entry, r, sp, &relocation))
+ continue;
+ }
+ }
+
+ } else {
+
+ switch (RELOC_TYPE(r)) {
+ case N_TEXT:
+ case N_TEXT | N_EXT:
+ relocation = addend + text_relocation;
+ break;
+
+ case N_DATA:
+ case N_DATA | N_EXT:
+ /*
+ * A word that points to beginning of the the
+ * data section initially contains not 0 but
+ * rather the "address" of that section in
+ * the input file, which is the length of the
+ * file's text.
+ */
+ relocation = addend + data_relocation;
+ break;
+
+ case N_BSS:
+ case N_BSS | N_EXT:
+ /*
+ * Similarly, an input word pointing to the
+ * beginning of the bss initially contains
+ * the length of text plus data of the file.
+ */
+ relocation = addend + bss_relocation;
+ break;
+
+ case N_ABS:
+ case N_ABS | N_EXT:
+ /*
+ * Don't know why this code would occur, but
+ * apparently it does.
+ */
+ break;
+
+ default:
+ errx(1, "%s: nonexternal relocation invalid",
+ get_file_name(entry));
+ }
+
+ /*
+ * When building a shared object, these segment
+ * relocations need a "load address relative"
+ * RRS fixup.
+ */
+ if (building_shared_object && !RELOC_PCREL_P(r)) {
+ r->r_address += dataseg?
+ entry->data_start_address:
+ entry->text_start_address;
+ claim_rrs_segment_reloc(entry, r);
+ }
+ }
+
+ if (RELOC_PCREL_P(r))
+ relocation -= pc_relocation;
+
+ md_relocate(r, relocation, data+addr, relocatable_output);
+
+ }
+}
+
+/*
+ * For relocatable_output only: write out the relocation,
+ * relocating the addresses-to-be-relocated.
+ */
+
+void
+write_rel()
+{
+ int count = 0;
+
+ if (trace_files)
+ fprintf(stderr, "Writing text relocation:\n\n");
+
+ /*
+ * Assign each global symbol a sequence number, giving the order
+ * in which `write_syms' will write it.
+ * This is so we can store the proper symbolnum fields
+ * in relocation entries we write.
+ */
+
+ /* BLECH - Assign number 0 to __DYNAMIC (!! Sun compatibility) */
+
+ if (dynamic_symbol->flags & GS_REFERENCED)
+ dynamic_symbol->symbolnum = count++;
+ FOR_EACH_SYMBOL(i, sp) {
+ if (sp != dynamic_symbol && (sp->flags & GS_REFERENCED)) {
+ sp->symbolnum = count++;
+ if (sp->size)
+ count++;
+ if (sp->alias)
+ count++;
+ }
+ } END_EACH_SYMBOL;
+
+ if (count != global_sym_count)
+ errx(1, "internal error: write_rel: count = %d", count);
+
+ each_full_file(assign_symbolnums, &count);
+
+ /* Write out the relocations of all files, remembered from copy_text. */
+ each_full_file(coptxtrel, 0);
+
+ if (trace_files)
+ fprintf(stderr, "\nWriting data relocation:\n\n");
+
+ each_full_file(copdatrel, 0);
+
+ if (trace_files)
+ fprintf(stderr, "\n");
+}
+
+
+/*
+ * Assign symbol ordinal numbers to local symbols in each entry.
+ */
+static void
+assign_symbolnums(entry, countp)
+ struct file_entry *entry;
+ int *countp;
+{
+ struct localsymbol *lsp, *lspend;
+ int n = *countp;
+
+ lspend = entry->symbols + entry->nsymbols;
+
+ if (discard_locals != DISCARD_ALL)
+ /* Count the N_FN symbol for this entry */
+ n++;
+
+ for (lsp = entry->symbols; lsp < lspend; lsp++) {
+ if (lsp->flags & LS_WRITE)
+ lsp->symbolnum = n++;
+ }
+ *countp = n;
+}
+
+static void
+coptxtrel(entry)
+ struct file_entry *entry;
+{
+ register struct relocation_info *r, *end;
+ register int reloc = entry->text_start_address;
+
+ r = entry->textrel;
+ end = r + entry->ntextrel;
+
+ for (; r < end; r++) {
+ register int symindex;
+ struct localsymbol *lsp;
+ symbol *sp;
+
+ RELOC_ADDRESS(r) += reloc;
+
+ symindex = RELOC_SYMBOL(r);
+ lsp = &entry->symbols[symindex];
+
+ if (!RELOC_EXTERN_P(r)) {
+ if (!pic_code_seen)
+ continue;
+ if (RELOC_BASEREL_P(r))
+ RELOC_SYMBOL(r) = lsp->symbolnum;
+ continue;
+ }
+
+ if (symindex >= entry->nsymbols)
+ errx(1, "%s: relocation symbolnum out of range",
+ get_file_name(entry));
+
+ sp = lsp->symbol;
+
+#ifdef N_INDR
+ /* Resolve indirection. */
+ if ((sp->defined & ~N_EXT) == N_INDR) {
+ if (sp->alias == NULL)
+ errx(1, "internal error: alias in hyperspace");
+ sp = sp->alias;
+ }
+#endif
+
+ /*
+ * If the symbol is now defined, change the external
+ * relocation to an internal one.
+ */
+
+ if (sp->defined) {
+ if (!pic_code_seen) {
+ RELOC_EXTERN_P(r) = 0;
+ RELOC_SYMBOL(r) = (sp->defined & N_TYPE);
+ } else
+ RELOC_SYMBOL(r) = sp->symbolnum;
+#ifdef RELOC_ADD_EXTRA
+ /*
+ * If we aren't going to be adding in the
+ * value in memory on the next pass of the
+ * loader, then we need to add it in from the
+ * relocation entry, unless the symbol remains
+ * external in our output. Otherwise the work we
+ * did in this pass is lost.
+ */
+ if (!RELOC_MEMORY_ADD_P(r) && !RELOC_EXTERN_P(r))
+ RELOC_ADD_EXTRA(r) += sp->value;
+#endif
+ } else
+ /*
+ * Global symbols come first.
+ */
+ RELOC_SYMBOL(r) = sp->symbolnum;
+ }
+ md_swapout_reloc(entry->textrel, entry->ntextrel);
+ mywrite(entry->textrel, entry->ntextrel,
+ sizeof(struct relocation_info), outdesc);
+}
+
+static void
+copdatrel(entry)
+ struct file_entry *entry;
+{
+ register struct relocation_info *r, *end;
+ /*
+ * Relocate the address of the relocation. Old address is relative to
+ * start of the input file's data section. New address is relative to
+ * start of the output file's data section.
+ */
+ register int reloc = entry->data_start_address - text_size;
+
+ r = entry->datarel;
+ end = r + entry->ndatarel;
+
+ for (; r < end; r++) {
+ register int symindex;
+ symbol *sp;
+ int symtype;
+
+ RELOC_ADDRESS(r) += reloc;
+
+ if (!RELOC_EXTERN_P(r)) {
+ if (RELOC_BASEREL_P(r))
+ errx(1, "%s: Unsupported relocation type",
+ get_file_name(entry));
+ continue;
+ }
+
+ symindex = RELOC_SYMBOL(r);
+ sp = entry->symbols[symindex].symbol;
+
+ if (symindex >= entry->header.a_syms)
+ errx(1, "%s: relocation symbolnum out of range",
+ get_file_name(entry));
+
+#ifdef N_INDR
+ /* Resolve indirection. */
+ if ((sp->defined & ~N_EXT) == N_INDR) {
+ if (sp->alias == NULL)
+ errx(1, "internal error: alias in hyperspace");
+ sp = sp->alias;
+ }
+#endif
+
+ symtype = sp->defined & N_TYPE;
+
+ if (!pic_code_seen && ( symtype == N_BSS ||
+ symtype == N_DATA ||
+ symtype == N_TEXT ||
+ symtype == N_ABS)) {
+ RELOC_EXTERN_P(r) = 0;
+ RELOC_SYMBOL(r) = symtype;
+ } else
+ /*
+ * Global symbols come first.
+ */
+ RELOC_SYMBOL(r) =
+ entry->symbols[symindex].symbol->symbolnum;
+ }
+ md_swapout_reloc(entry->datarel, entry->ndatarel);
+ mywrite(entry->datarel, entry->ndatarel,
+ sizeof(struct relocation_info), outdesc);
+}
+
+void write_file_syms __P((struct file_entry *, int *));
+void write_string_table __P((void));
+
+/* Offsets and current lengths of symbol and string tables in output file. */
+
+static int symbol_table_offset;
+static int symbol_table_len;
+
+/* Address in output file where string table starts. */
+static int string_table_offset;
+
+/* Offset within string table
+ where the strings in `strtab_vector' should be written. */
+static int string_table_len;
+
+/* Total size of string table strings allocated so far,
+ including strings in `strtab_vector'. */
+static int strtab_size;
+
+/* Vector whose elements are strings to be added to the string table. */
+static char **strtab_vector;
+
+/* Vector whose elements are the lengths of those strings. */
+static int *strtab_lens;
+
+/* Index in `strtab_vector' at which the next string will be stored. */
+static int strtab_index;
+
+/*
+ * Add the string NAME to the output file string table. Record it in
+ * `strtab_vector' to be output later. Return the index within the string
+ * table that this string will have.
+ */
+
+static int
+assign_string_table_index(name)
+ char *name;
+{
+ register int index = strtab_size;
+ register int len = strlen(name) + 1;
+
+ strtab_size += len;
+ strtab_vector[strtab_index] = name;
+ strtab_lens[strtab_index++] = len;
+
+ return index;
+}
+
+FILE *outstream = (FILE *)0;
+
+/*
+ * Write the contents of `strtab_vector' into the string table. This is done
+ * once for each file's local&debugger symbols and once for the global
+ * symbols.
+ */
+void
+write_string_table()
+{
+ register int i;
+
+ if (lseek(outdesc, string_table_offset + string_table_len, 0) ==
+ (off_t)-1)
+ err(1, "write_string_table: %s: lseek", output_filename);
+
+ if (!outstream)
+ outstream = fdopen(outdesc, "w");
+
+ for (i = 0; i < strtab_index; i++) {
+ fwrite (strtab_vector[i], 1, strtab_lens[i], outstream);
+ string_table_len += strtab_lens[i];
+ }
+
+ fflush(outstream);
+
+ /* Report I/O error such as disk full. */
+ if (ferror(outstream))
+ err(1, "write_string_table: %s", output_filename);
+}
+
+/* Write the symbol table and string table of the output file. */
+
+void
+write_syms()
+{
+ /* Number of symbols written so far. */
+ int syms_written = 0;
+ struct nlist nl;
+
+ /*
+ * Buffer big enough for all the global symbols. One extra struct
+ * for each indirect symbol to hold the extra reference following.
+ */
+ struct nlist *buf = (struct nlist *)
+ alloca(global_sym_count * sizeof(struct nlist));
+ /* Pointer for storing into BUF. */
+ register struct nlist *bufp = buf;
+
+ /* Size of string table includes the bytes that store the size. */
+ strtab_size = sizeof strtab_size;
+
+ symbol_table_offset = N_SYMOFF(outheader);
+ symbol_table_len = 0;
+ string_table_offset = N_STROFF(outheader);
+ string_table_len = strtab_size;
+
+ if (strip_symbols == STRIP_ALL)
+ return;
+
+ /* First, write out the global symbols. */
+
+ /*
+ * Allocate two vectors that record the data to generate the string
+ * table from the global symbols written so far. This must include
+ * extra space for the references following indirect outputs.
+ */
+
+ strtab_vector = (char **)alloca((global_sym_count) * sizeof(char *));
+ strtab_lens = (int *)alloca((global_sym_count) * sizeof(int));
+ strtab_index = 0;
+
+ /*
+ * __DYNAMIC symbol *must* be first for Sun compatibility, as Sun's
+ * ld.so reads the shared object's first symbol. This means that
+ * (Sun's) shared libraries cannot be stripped! (We only assume
+ * that __DYNAMIC is the first item in the data segment)
+ *
+ * If defined (ie. not relocatable_output), make it look
+ * like an internal symbol.
+ */
+ if (dynamic_symbol->flags & GS_REFERENCED) {
+ nl.n_other = 0;
+ nl.n_desc = 0;
+ nl.n_type = dynamic_symbol->defined;
+ if (nl.n_type == N_UNDF)
+ nl.n_type |= N_EXT;
+ else
+ nl.n_type &= ~N_EXT;
+ nl.n_value = dynamic_symbol->value;
+ nl.n_un.n_strx = assign_string_table_index(dynamic_symbol->name);
+ *bufp++ = nl;
+ syms_written++;
+ }
+
+ /* Scan the symbol hash table, bucket by bucket. */
+
+ FOR_EACH_SYMBOL(i, sp) {
+
+ if (sp == dynamic_symbol)
+ /* Already dealt with above */
+ continue;
+
+ if (!(sp->flags & GS_REFERENCED))
+ /* Came from shared object but was not used */
+ continue;
+
+ if (sp->so_defined || (sp->alias && sp->alias->so_defined))
+ /*
+ * Definition came from shared object,
+ * don't mention it here
+ */
+ continue;
+
+ if (!sp->defined && !relocatable_output) {
+ /*
+ * We're building a shared object and there
+ * are still undefined symbols. Don't output
+ * these, symbol was discounted in digest_pass1()
+ * (they are in the RRS symbol table).
+ */
+ if (!building_shared_object)
+ warnx("symbol %s remains undefined", sp->name);
+ continue;
+ }
+
+ if (syms_written >= global_sym_count)
+ errx(1,
+ "internal error: number of symbols exceeds alloc'd %d",
+ global_sym_count);
+
+ /*
+ * Construct a `struct nlist' for the symbol.
+ */
+ nl.n_other = 0;
+ nl.n_desc = 0;
+
+ if (sp->defined > 1) {
+ /*
+ * defined with known type
+ */
+ if (!relocatable_output && !building_shared_object &&
+ sp->alias && sp->alias->defined > 1) {
+ /*
+ * If the target of an indirect symbol has
+ * been defined and we are outputting an
+ * executable, resolve the indirection; it's
+ * no longer needed.
+ */
+ nl.n_type = sp->alias->defined;
+ nl.n_value = sp->alias->value;
+ nl.n_other = N_OTHER(0, sp->alias->aux);
+ } else {
+ if (sp->defined == N_SIZE)
+ nl.n_type = N_DATA | N_EXT;
+ else
+ nl.n_type = sp->defined;
+ if (nl.n_type == (N_INDR|N_EXT) &&
+ sp->value != 0)
+ errx(1, "%s: N_INDR has value %#x",
+ sp->name, sp->value);
+ nl.n_value = sp->value;
+ nl.n_other = N_OTHER(0, sp->aux);
+ }
+
+ } else if (sp->common_size) {
+ /*
+ * defined as common but not allocated,
+ * happens only with -r and not -d, write out
+ * a common definition.
+ *
+ * common condition needs to be before undefined
+ * condition because unallocated commons are set
+ * undefined in digest_symbols.
+ */
+ nl.n_type = N_UNDF | N_EXT;
+ nl.n_value = sp->common_size;
+ } else if (!sp->defined) {
+ /* undefined -- legit only if -r */
+ nl.n_type = N_UNDF | N_EXT;
+ nl.n_value = 0;
+ } else
+ errx(1,
+ "internal error: %s defined in mysterious way",
+ sp->name);
+
+ /*
+ * Allocate string table space for the symbol name.
+ */
+
+ nl.n_un.n_strx = assign_string_table_index(sp->name);
+
+ /* Output to the buffer and count it. */
+
+ *bufp++ = nl;
+ syms_written++;
+
+ /*
+ * Write second symbol of an alias pair.
+ */
+ if (nl.n_type == N_INDR + N_EXT) {
+ if (sp->alias == NULL)
+ errx(1, "internal error: alias in hyperspace");
+ nl.n_type = N_UNDF + N_EXT;
+ nl.n_un.n_strx =
+ assign_string_table_index(sp->alias->name);
+ nl.n_value = 0;
+ nl.n_other = 0;
+ nl.n_desc = 0;
+ *bufp++ = nl;
+ syms_written++;
+ }
+
+ /*
+ * Write N_SIZE symbol for a symbol with a known size.
+ */
+ if (relocatable_output && sp->size) {
+ nl.n_type = N_SIZE + N_EXT;
+ nl.n_un.n_strx = assign_string_table_index(sp->name);
+ nl.n_value = sp->size;
+ nl.n_other = 0;
+ nl.n_desc = 0;
+ *bufp++ = nl;
+ syms_written++;
+ }
+
+#ifdef DEBUG
+printf("writesym(#%d): %s, type %x\n", syms_written, sp->name, sp->defined);
+#endif
+ } END_EACH_SYMBOL;
+
+ if (syms_written != strtab_index || strtab_index != global_sym_count)
+ errx(1, "internal error:\
+wrong number (%d) of global symbols written into output file, should be %d",
+ syms_written, global_sym_count);
+
+ /* Output the buffer full of `struct nlist's. */
+
+ if (lseek(outdesc, symbol_table_offset + symbol_table_len, 0) ==
+ (off_t)-1)
+ err(1, "write_syms: lseek");
+ md_swapout_symbols(buf, bufp - buf);
+ mywrite(buf, bufp - buf, sizeof(struct nlist), outdesc);
+ symbol_table_len += sizeof(struct nlist) * (bufp - buf);
+
+ /* Write the strings for the global symbols. */
+ write_string_table();
+
+ /* Write the local symbols defined by the various files. */
+ each_file(write_file_syms, (void *)&syms_written);
+ file_close();
+
+ if (syms_written != nsyms)
+ errx(1, "internal error:\
+wrong number of symbols (%d) written into output file, should be %d",
+ syms_written, nsyms);
+
+ if (symbol_table_offset + symbol_table_len != string_table_offset)
+ errx(1,
+ "internal error: inconsistent symbol table length: %d vs %s",
+ symbol_table_offset + symbol_table_len, string_table_offset);
+
+ lseek(outdesc, string_table_offset, 0);
+ strtab_size = md_swap_long(strtab_size);
+ mywrite(&strtab_size, sizeof(int), 1, outdesc);
+}
+
+
+/*
+ * Write the local and debugger symbols of file ENTRY. Increment
+ * *SYMS_WRITTEN_ADDR for each symbol that is written.
+ */
+
+/*
+ * Note that we do not combine identical names of local symbols. dbx or gdb
+ * would be confused if we did that.
+ */
+void
+write_file_syms(entry, syms_written_addr)
+ struct file_entry *entry;
+ int *syms_written_addr;
+{
+ struct localsymbol *lsp, *lspend;
+
+ /* Upper bound on number of syms to be written here. */
+ int max_syms = entry->nsymbols + 1;
+
+ /*
+ * Buffer to accumulate all the syms before writing them. It has one
+ * extra slot for the local symbol we generate here.
+ */
+ struct nlist *buf = (struct nlist *)
+ alloca(max_syms * sizeof(struct nlist));
+
+ register struct nlist *bufp = buf;
+
+ if (entry->flags & E_DYNAMIC)
+ return;
+
+ /*
+ * Make tables that record, for each symbol, its name and its name's
+ * length. The elements are filled in by `assign_string_table_index'.
+ */
+
+ strtab_vector = (char **)alloca(max_syms * sizeof(char *));
+ strtab_lens = (int *)alloca(max_syms * sizeof(int));
+ strtab_index = 0;
+
+ /* Generate a local symbol for the start of this file's text. */
+
+ if (discard_locals != DISCARD_ALL) {
+ struct nlist nl;
+
+ nl.n_type = N_FN | N_EXT;
+ nl.n_un.n_strx =
+ assign_string_table_index(entry->local_sym_name);
+ nl.n_value = entry->text_start_address;
+ nl.n_desc = 0;
+ nl.n_other = 0;
+ *bufp++ = nl;
+ (*syms_written_addr)++;
+ }
+ /* Read the file's string table. */
+
+ entry->strings = (char *)alloca(entry->string_size);
+ read_entry_strings(file_open(entry), entry);
+
+ lspend = entry->symbols + entry->nsymbols;
+
+ for (lsp = entry->symbols; lsp < lspend; lsp++) {
+ register struct nlist *p = &lsp->nzlist.nlist;
+ char *name;
+
+ if (!(lsp->flags & LS_WRITE))
+ continue;
+
+ if (p->n_un.n_strx == 0)
+ name = NULL;
+ else if (!(lsp->flags & LS_RENAME))
+ name = p->n_un.n_strx + entry->strings;
+ else {
+ char *cp = p->n_un.n_strx + entry->strings;
+ name = (char *)alloca(
+ strlen(entry->local_sym_name) +
+ strlen(cp) + 2 );
+ (void)sprintf(name, "%s.%s", entry->local_sym_name, cp);
+ }
+
+ /*
+ * If this symbol has a name, allocate space for it
+ * in the output string table.
+ */
+
+ if (name)
+ p->n_un.n_strx = assign_string_table_index(name);
+
+ /* Output this symbol to the buffer and count it. */
+
+ *bufp++ = *p;
+ (*syms_written_addr)++;
+ }
+
+ /* All the symbols are now in BUF; write them. */
+
+ lseek(outdesc, symbol_table_offset + symbol_table_len, 0);
+ md_swapout_symbols(buf, bufp - buf);
+ mywrite(buf, bufp - buf, sizeof(struct nlist), outdesc);
+ symbol_table_len += sizeof(struct nlist) * (bufp - buf);
+
+ /*
+ * Write the string-table data for the symbols just written, using
+ * the data in vectors `strtab_vector' and `strtab_lens'.
+ */
+
+ write_string_table();
+ entry->strings = 0; /* Since it will disappear anyway. */
+}
+
+/*
+ * Parse the string ARG using scanf format FORMAT, and return the result.
+ * If it does not parse, report fatal error
+ * generating the error message using format string ERROR and ARG as arg.
+ */
+
+static int
+parse(arg, format, error)
+ char *arg, *format, *error;
+{
+ int x;
+
+ if (1 != sscanf(arg, format, &x))
+ errx(1, error, arg);
+ return x;
+}
+
+/*
+ * Output COUNT*ELTSIZE bytes of data at BUF to the descriptor FD.
+ */
+void
+mywrite(buf, count, eltsize, fd)
+ void *buf;
+ int count;
+ int eltsize;
+ int fd;
+{
+ register int val;
+ register int bytes = count * eltsize;
+
+ while (bytes > 0) {
+ val = write(fd, buf, bytes);
+ if (val <= 0)
+ err(1, "write: %s", output_filename);
+ buf += val;
+ bytes -= val;
+ }
+}
+
+static void
+cleanup()
+{
+ struct stat statbuf;
+
+ if (outdesc <= 0)
+ return;
+
+ if (fstat(outdesc, &statbuf) == 0) {
+ if (S_ISREG(statbuf.st_mode))
+ (void)unlink(output_filename);
+ }
+}
+
+/*
+ * Output PADDING zero-bytes to descriptor FD.
+ * PADDING may be negative; in that case, do nothing.
+ */
+void
+padfile(padding, fd)
+ int padding;
+ int fd;
+{
+ register char *buf;
+ if (padding <= 0)
+ return;
+
+ buf = (char *)alloca(padding);
+ bzero(buf, padding);
+ mywrite(buf, padding, 1, fd);
+}
diff --git a/gnu/usr.bin/ld/ld.h b/gnu/usr.bin/ld/ld.h
new file mode 100644
index 0000000..3c1b195
--- /dev/null
+++ b/gnu/usr.bin/ld/ld.h
@@ -0,0 +1,688 @@
+/*
+ * $Id: ld.h,v 1.10 1994/02/13 20:41:34 jkh Exp $
+ */
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ */
+
+#define SUN_COMPAT
+
+#ifndef N_SIZE
+#define N_SIZE 0xc
+#endif
+
+#ifndef min
+#define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef __P
+#ifndef __STDC__
+#define __P(a) ()
+#else
+#define __P(a) a
+#endif
+#endif
+
+/* If compiled with GNU C, use the built-in alloca */
+#if defined(__GNUC__) || defined(sparc)
+#define alloca __builtin_alloca
+#endif
+
+#ifdef __FreeBSD__
+#define FreeBSD
+#endif
+
+#include "md.h"
+#include "link.h"
+
+/* Macro to control the number of undefined references printed */
+#define MAX_UREFS_PRINTED 10
+
+/* Align to power-of-two boundary */
+#define PALIGN(x,p) (((x) + (u_long)(p) - 1) & (-(u_long)(p)))
+
+/* Align to machine dependent boundary */
+#define MALIGN(x) PALIGN(x,MAX_ALIGNMENT)
+
+/* Define this to specify the default executable format. */
+#ifndef DEFAULT_MAGIC
+#ifdef FreeBSD
+#define DEFAULT_MAGIC QMAGIC
+extern int netzmagic;
+#else
+#define DEFAULT_MAGIC ZMAGIC
+#endif
+#endif
+
+
+/*
+ * Ok. Following are the relocation information macros. If your
+ * system should not be able to use the default set (below), you must
+ * define the following:
+
+ * relocation_info: This must be typedef'd (or #define'd) to the type
+ * of structure that is stored in the relocation info section of your
+ * a.out files. Often this is defined in the a.out.h for your system.
+ *
+ * RELOC_ADDRESS (rval): Offset into the current section of the
+ * <whatever> to be relocated. *Must be an lvalue*.
+ *
+ * RELOC_EXTERN_P (rval): Is this relocation entry based on an
+ * external symbol (1), or was it fully resolved upon entering the
+ * loader (0) in which case some combination of the value in memory
+ * (if RELOC_MEMORY_ADD_P) and the extra (if RELOC_ADD_EXTRA) contains
+ * what the value of the relocation actually was. *Must be an lvalue*.
+ *
+ * RELOC_TYPE (rval): If this entry was fully resolved upon
+ * entering the loader, what type should it be relocated as?
+ *
+ * RELOC_SYMBOL (rval): If this entry was not fully resolved upon
+ * entering the loader, what is the index of it's symbol in the symbol
+ * table? *Must be a lvalue*.
+ *
+ * RELOC_MEMORY_ADD_P (rval): This should return true if the final
+ * relocation value output here should be added to memory, or if the
+ * section of memory described should simply be set to the relocation
+ * value.
+ *
+ * RELOC_ADD_EXTRA (rval): (Optional) This macro, if defined, gives
+ * an extra value to be added to the relocation value based on the
+ * individual relocation entry. *Must be an lvalue if defined*.
+ *
+ * RELOC_PCREL_P (rval): True if the relocation value described is
+ * pc relative.
+ *
+ * RELOC_VALUE_RIGHTSHIFT (rval): Number of bits right to shift the
+ * final relocation value before putting it where it belongs.
+ *
+ * RELOC_TARGET_SIZE (rval): log to the base 2 of the number of
+ * bytes of size this relocation entry describes; 1 byte == 0; 2 bytes
+ * == 1; 4 bytes == 2, and etc. This is somewhat redundant (we could
+ * do everything in terms of the bit operators below), but having this
+ * macro could end up producing better code on machines without fancy
+ * bit twiddling. Also, it's easier to understand/code big/little
+ * endian distinctions with this macro.
+ *
+ * RELOC_TARGET_BITPOS (rval): The starting bit position within the
+ * object described in RELOC_TARGET_SIZE in which the relocation value
+ * will go.
+ *
+ * RELOC_TARGET_BITSIZE (rval): How many bits are to be replaced
+ * with the bits of the relocation value. It may be assumed by the
+ * code that the relocation value will fit into this many bits. This
+ * may be larger than RELOC_TARGET_SIZE if such be useful.
+ *
+ *
+ * Things I haven't implemented
+ * ----------------------------
+ *
+ * Values for RELOC_TARGET_SIZE other than 0, 1, or 2.
+ *
+ * Pc relative relocation for External references.
+ *
+ *
+ */
+
+
+/* Default macros */
+#ifndef RELOC_ADDRESS
+
+#define RELOC_ADDRESS(r) ((r)->r_address)
+#define RELOC_EXTERN_P(r) ((r)->r_extern)
+#define RELOC_TYPE(r) ((r)->r_symbolnum)
+#define RELOC_SYMBOL(r) ((r)->r_symbolnum)
+#define RELOC_MEMORY_SUB_P(r) 0
+#define RELOC_MEMORY_ADD_P(r) 1
+#undef RELOC_ADD_EXTRA
+#define RELOC_PCREL_P(r) ((r)->r_pcrel)
+#define RELOC_VALUE_RIGHTSHIFT(r) 0
+#if defined(RTLD) && defined(SUN_COMPAT)
+#define RELOC_TARGET_SIZE(r) (2) /* !!!!! Sun BUG compatible */
+#else
+#define RELOC_TARGET_SIZE(r) ((r)->r_length)
+#endif
+#define RELOC_TARGET_BITPOS(r) 0
+#define RELOC_TARGET_BITSIZE(r) 32
+
+#define RELOC_JMPTAB_P(r) ((r)->r_jmptable)
+#define RELOC_BASEREL_P(r) ((r)->r_baserel)
+#define RELOC_RELATIVE_P(r) ((r)->r_relative)
+#define RELOC_COPY_P(r) ((r)->r_copy)
+#define RELOC_LAZY_P(r) ((r)->r_jmptable)
+
+#define CHECK_GOT_RELOC(r) ((r)->r_pcrel)
+
+#endif
+
+/*
+ * Internal representation of relocation types
+ */
+#define RELTYPE_EXTERN 1
+#define RELTYPE_JMPSLOT 2
+#define RELTYPE_BASEREL 4
+#define RELTYPE_RELATIVE 8
+#define RELTYPE_COPY 16
+
+#ifdef nounderscore
+#define LPREFIX '.'
+#else
+#define LPREFIX 'L'
+#endif
+
+#ifndef TEXT_START
+#define TEXT_START(x) N_TXTADDR(x)
+#endif
+
+#ifndef DATA_START
+#define DATA_START(x) N_DATADDR(x)
+#endif
+
+/* If a this type of symbol is encountered, its name is a warning
+ message to print each time the symbol referenced by the next symbol
+ table entry is referenced.
+
+ This feature may be used to allow backwards compatibility with
+ certain functions (eg. gets) but to discourage programmers from
+ their use.
+
+ So if, for example, you wanted to have ld print a warning whenever
+ the function "gets" was used in their C program, you would add the
+ following to the assembler file in which gets is defined:
+
+ .stabs "Obsolete function \"gets\" referenced",30,0,0,0
+ .stabs "_gets",1,0,0,0
+
+ These .stabs do not necessarily have to be in the same file as the
+ gets function, they simply must exist somewhere in the compilation. */
+
+#ifndef N_WARNING
+#define N_WARNING 0x1E /* Warning message to print if symbol
+ included */
+#endif /* This is input to ld */
+
+/* Special global symbol types understood by GNU LD. */
+
+/* The following type indicates the definition of a symbol as being
+ an indirect reference to another symbol. The other symbol
+ appears as an undefined reference, immediately following this symbol.
+
+ Indirection is asymmetrical. The other symbol's value will be used
+ to satisfy requests for the indirect symbol, but not vice versa.
+ If the other symbol does not have a definition, libraries will
+ be searched to find a definition.
+
+ So, for example, the following two lines placed in an assembler
+ input file would result in an object file which would direct gnu ld
+ to resolve all references to symbol "foo" as references to symbol
+ "bar".
+
+ .stabs "_foo",11,0,0,0
+ .stabs "_bar",1,0,0,0
+
+ Note that (11 == (N_INDR | N_EXT)) and (1 == (N_UNDF | N_EXT)). */
+
+#ifndef N_INDR
+#define N_INDR 0xa
+#endif
+
+/* The following symbols refer to set elements. These are expected
+ only in input to the loader; they should not appear in loader
+ output (unless relocatable output is requested). To be recognized
+ by the loader, the input symbols must have their N_EXT bit set.
+ All the N_SET[ATDB] symbols with the same name form one set. The
+ loader collects all of these elements at load time and outputs a
+ vector for each name.
+ Space (an array of 32 bit words) is allocated for the set in the
+ data section, and the n_value field of each set element value is
+ stored into one word of the array.
+ The first word of the array is the length of the set (number of
+ elements). The last word of the vector is set to zero for possible
+ use by incremental loaders. The array is ordered by the linkage
+ order; the first symbols which the linker encounters will be first
+ in the array.
+
+ In C syntax this looks like:
+
+ struct set_vector {
+ unsigned int length;
+ unsigned int vector[length];
+ unsigned int always_zero;
+ };
+
+ Before being placed into the array, each element is relocated
+ according to its type. This allows the loader to create an array
+ of pointers to objects automatically. N_SETA type symbols will not
+ be relocated.
+
+ The address of the set is made into an N_SETV symbol
+ whose name is the same as the name of the set.
+ This symbol acts like a N_DATA global symbol
+ in that it can satisfy undefined external references.
+
+ For the purposes of determining whether or not to load in a library
+ file, set element definitions are not considered "real
+ definitions"; they will not cause the loading of a library
+ member.
+
+ If relocatable output is requested, none of this processing is
+ done. The symbols are simply relocated and passed through to the
+ output file.
+
+ So, for example, the following three lines of assembler code
+ (whether in one file or scattered between several different ones)
+ will produce a three element vector (total length is five words;
+ see above), referenced by the symbol "_xyzzy", which will have the
+ addresses of the routines _init1, _init2, and _init3.
+
+ *NOTE*: If symbolic addresses are used in the n_value field of the
+ defining .stabs, those symbols must be defined in the same file as
+ that containing the .stabs.
+
+ .stabs "_xyzzy",23,0,0,_init1
+ .stabs "_xyzzy",23,0,0,_init2
+ .stabs "_xyzzy",23,0,0,_init3
+
+ Note that (23 == (N_SETT | N_EXT)). */
+
+#ifndef N_SETA
+#define N_SETA 0x14 /* Absolute set element symbol */
+#endif /* This is input to LD, in a .o file. */
+
+#ifndef N_SETT
+#define N_SETT 0x16 /* Text set element symbol */
+#endif /* This is input to LD, in a .o file. */
+
+#ifndef N_SETD
+#define N_SETD 0x18 /* Data set element symbol */
+#endif /* This is input to LD, in a .o file. */
+
+#ifndef N_SETB
+#define N_SETB 0x1A /* Bss set element symbol */
+#endif /* This is input to LD, in a .o file. */
+
+/* Macros dealing with the set element symbols defined in a.out.h */
+#define SET_ELEMENT_P(x) ((x) >= N_SETA && (x) <= (N_SETB|N_EXT))
+#define TYPE_OF_SET_ELEMENT(x) ((x) - N_SETA + N_ABS)
+
+#ifndef N_SETV
+#define N_SETV 0x1C /* Pointer to set vector in data area. */
+#endif /* This is output from LD. */
+
+
+#ifndef __GNU_STAB__
+
+/* Line number for the data section. This is to be used to describe
+ the source location of a variable declaration. */
+#ifndef N_DSLINE
+#define N_DSLINE (N_SLINE+N_DATA-N_TEXT)
+#endif
+
+/* Line number for the bss section. This is to be used to describe
+ the source location of a variable declaration. */
+#ifndef N_BSLINE
+#define N_BSLINE (N_SLINE+N_BSS-N_TEXT)
+#endif
+
+#endif /* not __GNU_STAB__ */
+
+
+typedef struct localsymbol {
+ struct nzlist nzlist; /* n[z]list from file */
+ struct glosym *symbol; /* Corresponding global symbol,
+ if any */
+ struct localsymbol *next; /* List of definitions */
+ struct file_entry *entry; /* Backpointer to file */
+ long gotslot_offset; /* Position in GOT, if any */
+ int symbolnum; /* Position in output nlist */
+ int flags;
+#define LS_L_SYMBOL 1 /* Local symbol starts with an `L' */
+#define LS_WRITE 2 /* Symbol goes in output symtable */
+#define LS_RENAME 4 /* xlat name to `<file>.<name>' */
+#define LS_GOTSLOTCLAIMED 8 /* This symbol has a GOT entry */
+} localsymbol_t;
+
+/* Symbol table */
+
+/*
+ * Global symbol data is recorded in these structures, one for each global
+ * symbol. They are found via hashing in 'symtab', which points to a vector
+ * of buckets. Each bucket is a chain of these structures through the link
+ * field.
+ */
+
+typedef struct glosym {
+ struct glosym *link; /* Next symbol hash bucket. */
+ char *name; /* Name of this symbol. */
+ long value; /* Value of this symbol */
+ localsymbol_t *refs; /* Chain of local symbols from object
+ files pertaining to this global
+ symbol */
+ localsymbol_t *sorefs;/* Same for local symbols from shared
+ object files. */
+
+ char *warning; /* message, from N_WARNING nlists */
+ int common_size; /* Common size */
+ int symbolnum; /* Symbol index in output symbol table */
+ int rrs_symbolnum; /* Symbol index in RRS symbol table */
+
+ struct nlist *def_nlist; /* The local symbol that gave this
+ global symbol its definition */
+
+ char defined; /* Definition of this symbol */
+ char so_defined; /* Definition of this symbol in a shared
+ object. These go into the RRS symbol table */
+ u_char undef_refs; /* Count of number of "undefined"
+ messages printed for this symbol */
+ u_char mult_defs; /* Same for "multiply defined" symbols */
+ struct glosym *alias; /* For symbols of type N_INDR, this
+ points at the real symbol. */
+ int setv_count; /* Number of elements in N_SETV symbols */
+ int size; /* Size of this symbol (either from N_SIZE
+ symbols or a from shared object's RRS */
+ int aux; /* Auxiliary type information conveyed in
+ the `n_other' field of nlists */
+
+ /* The offset into one of the RRS tables, -1 if not used */
+ long jmpslot_offset;
+ long gotslot_offset;
+
+ long flags;
+
+#define GS_DEFINED 1 /* Symbol has definition (notyetused)*/
+#define GS_REFERENCED 2 /* Symbol is referred to by something
+ interesting */
+#define GS_TRACE 4 /* Symbol will be traced */
+#define GS_JMPSLOTCLAIMED 8 /* */
+#define GS_GOTSLOTCLAIMED 0x10 /* Some state bits concerning */
+#define GS_CPYRELOCRESERVED 0x20 /* entries in GOT and PLT tables */
+#define GS_CPYRELOCCLAIMED 0x40 /* */
+
+} symbol;
+
+/* Number of buckets in symbol hash table */
+#define SYMTABSIZE 1009
+
+/* The symbol hash table: a vector of SYMTABSIZE pointers to struct glosym. */
+extern symbol *symtab[];
+#define FOR_EACH_SYMBOL(i,sp) { \
+ int i; \
+ for (i = 0; i < SYMTABSIZE; i++) { \
+ register symbol *sp; \
+ for (sp = symtab[i]; sp; sp = sp->link)
+
+#define END_EACH_SYMBOL }}
+
+/* # of global symbols referenced and not defined. */
+extern int undefined_global_sym_count;
+
+/* # of undefined symbols referenced by shared objects */
+extern int undefined_shobj_sym_count;
+
+/* # of multiply defined symbols. */
+extern int multiple_def_count;
+
+/* # of common symbols. */
+extern int common_defined_global_count;
+
+/* # of warning symbols encountered. */
+extern int warning_count;
+
+/*
+ * Define a linked list of strings which define symbols which should be
+ * treated as set elements even though they aren't. Any symbol with a prefix
+ * matching one of these should be treated as a set element.
+ *
+ * This is to make up for deficiencies in many assemblers which aren't willing
+ * to pass any stabs through to the loader which they don't understand.
+ */
+struct string_list_element {
+ char *str;
+ struct string_list_element *next;
+};
+
+extern symbol *entry_symbol; /* the entry symbol, if any */
+extern symbol *edata_symbol; /* the symbol _edata */
+extern symbol *etext_symbol; /* the symbol _etext */
+extern symbol *end_symbol; /* the symbol _end */
+extern symbol *got_symbol; /* the symbol __GLOBAL_OFFSET_TABLE_ */
+extern symbol *dynamic_symbol; /* the symbol __DYNAMIC */
+
+/*
+ * Each input file, and each library member ("subfile") being loaded, has a
+ * `file_entry' structure for it.
+ *
+ * For files specified by command args, these are contained in the vector which
+ * `file_table' points to.
+ *
+ * For library members, they are dynamically allocated, and chained through the
+ * `chain' field. The chain is found in the `subfiles' field of the
+ * `file_entry'. The `file_entry' objects for the members have `superfile'
+ * fields pointing to the one for the library.
+ */
+
+struct file_entry {
+ char *filename; /* Name of this file. */
+ /*
+ * Name to use for the symbol giving address of text start Usually
+ * the same as filename, but for a file spec'd with -l this is the -l
+ * switch itself rather than the filename.
+ */
+ char *local_sym_name;
+ struct exec header; /* The file's a.out header. */
+ localsymbol_t *symbols; /* Symbol table of the file. */
+ int nsymbols; /* Number of symbols in above array. */
+ int string_size; /* Size in bytes of string table. */
+ char *strings; /* Pointer to the string table when
+ in core, NULL otherwise */
+ int strings_offset; /* Offset of string table,
+ (normally N_STROFF() + 4) */
+ /*
+ * Next two used only if `relocatable_output' or if needed for
+ * output of undefined reference line numbers.
+ */
+ struct relocation_info *textrel; /* Text relocations */
+ int ntextrel; /* # of text relocations */
+ struct relocation_info *datarel; /* Data relocations */
+ int ndatarel; /* # of data relocations */
+
+ /*
+ * Relation of this file's segments to the output file.
+ */
+ int text_start_address; /* Start of this file's text segment
+ in the output file core image. */
+ int data_start_address; /* Start of this file's data segment
+ in the output file core image. */
+ int bss_start_address; /* Start of this file's bss segment
+ in the output file core image. */
+ struct file_entry *subfiles; /* For a library, points to chain of
+ entries for the library members. */
+ struct file_entry *superfile; /* For library member, points to the
+ library's own entry. */
+ struct file_entry *chain; /* For library member, points to next
+ entry for next member. */
+ int starting_offset; /* For a library member, offset of the
+ member within the archive. Zero for
+ files that are not library members.*/
+ int total_size; /* Size of contents of this file,
+ if library member. */
+#ifdef SUN_COMPAT
+ struct file_entry *silly_archive;/* For shared libraries which have
+ a .sa companion */
+#endif
+ int lib_major, lib_minor; /* Version numbers of a shared object */
+
+ int flags;
+#define E_IS_LIBRARY 1 /* File is a an archive */
+#define E_HEADER_VALID 2 /* File's header has been read */
+#define E_SEARCH_DIRS 4 /* Search directories for file */
+#define E_SEARCH_DYNAMIC 8 /* Search for shared libs allowed */
+#define E_JUST_SYMS 0x10 /* File is used for incremental load */
+#define E_DYNAMIC 0x20 /* File is a shared object */
+#define E_SCRAPPED 0x40 /* Ignore this file */
+#define E_SYMBOLS_USED 0x80 /* Symbols from this entry were used */
+};
+
+/*
+ * Section start addresses.
+ */
+extern int text_size; /* total size of text. */
+extern int text_start; /* start of text */
+extern int text_pad; /* clear space between text and data */
+extern int data_size; /* total size of data. */
+extern int data_start; /* start of data */
+extern int data_pad; /* part of bss segment within data */
+
+extern int bss_size; /* total size of bss. */
+extern int bss_start; /* start of bss */
+
+extern int text_reloc_size; /* total size of text relocation. */
+extern int data_reloc_size; /* total size of data relocation. */
+
+/*
+ * Runtime Relocation Section (RRS).
+ * This describes the data structures that go into the output text and data
+ * segments to support the run-time linker. The RRS can be empty (plain old
+ * static linking), or can just exist of GOT and PLT entries (in case of
+ * statically linked PIC code).
+ */
+extern int rrs_section_type; /* What's in the RRS section */
+#define RRS_NONE 0
+#define RRS_PARTIAL 1
+#define RRS_FULL 2
+extern int rrs_text_size; /* Size of RRS text additions */
+extern int rrs_text_start; /* Location of above */
+extern int rrs_data_size; /* Size of RRS data additions */
+extern int rrs_data_start; /* Location of above */
+
+/* Version number to put in __DYNAMIC (set by -V) */
+extern int soversion;
+#ifndef DEFAULT_SOVERSION
+#define DEFAULT_SOVERSION LD_VERSION_BSD
+#endif
+
+extern int pc_relocation; /* Current PC reloc value */
+
+extern int number_of_shobjs; /* # of shared objects linked in */
+
+/* Current link mode */
+extern int link_mode;
+#define DYNAMIC 1 /* Consider shared libraries */
+#define SYMBOLIC 2 /* Force symbolic resolution */
+#define FORCEARCHIVE 4 /* Force inclusion of all members
+ of archives */
+#define SHAREABLE 8 /* Build a shared object */
+#define SILLYARCHIVE 16 /* Process .sa companions, if any */
+
+extern int outdesc; /* Output file descriptor. */
+extern struct exec outheader; /* Output file header. */
+extern int magic; /* Output file magic. */
+extern int oldmagic;
+extern int relocatable_output;
+
+/* Size of a page. */
+extern int page_size;
+
+extern char **search_dirs; /* Directories to search for libraries. */
+extern int n_search_dirs; /* Length of above. */
+
+extern int write_map; /* write a load map (`-M') */
+
+void read_header __P((int, struct file_entry *));
+void read_entry_symbols __P((int, struct file_entry *));
+void read_entry_strings __P((int, struct file_entry *));
+void read_entry_relocation __P((int, struct file_entry *));
+void enter_file_symbols __P((struct file_entry *));
+void read_file_symbols __P((struct file_entry *));
+int set_element_prefixed_p __P((char *));
+int text_offset __P((struct file_entry *));
+int file_open __P((struct file_entry *));
+void each_file __P((void (*)(), void *));
+void each_full_file __P((void (*)(), void *));
+unsigned long check_each_file __P((unsigned long (*)(), void *));
+void mywrite __P((void *, int, int, int));
+void padfile __P((int,int));
+
+/* In warnings.c: */
+void perror_name __P((char *));
+void perror_file __P((struct file_entry *));
+void print_symbols __P((FILE *));
+char *get_file_name __P((struct file_entry *));
+void print_file_name __P((struct file_entry *, FILE *));
+void prline_file_name __P((struct file_entry *, FILE *));
+int do_warnings __P((FILE *));
+
+/* In etc.c: */
+void *xmalloc __P((size_t));
+void *xrealloc __P((void *, size_t));
+char *concat __P((const char *, const char *, const char *));
+
+/* In symbol.c: */
+void symtab_init __P((int));
+symbol *getsym __P((char *)), *getsym_soft __P((char *));
+
+/* In lib.c: */
+void search_library __P((int, struct file_entry *));
+void read_shared_object __P((int, struct file_entry *));
+int findlib __P((struct file_entry *));
+
+/* In shlib.c: */
+char *findshlib __P((char *, int *, int *, int));
+void add_search_dir __P((char *));
+void add_search_path __P((char *));
+void std_search_path __P((void));
+int getdewey __P((int[], char *));
+int cmpndewey __P((int[], int, int[], int));
+
+/* In rrs.c: */
+void init_rrs __P((void));
+int rrs_add_shobj __P((struct file_entry *));
+void alloc_rrs_reloc __P((struct file_entry *, symbol *));
+void alloc_rrs_segment_reloc __P((struct file_entry *, struct relocation_info *));
+void alloc_rrs_jmpslot __P((struct file_entry *, symbol *));
+void alloc_rrs_gotslot __P((struct file_entry *, struct relocation_info *, localsymbol_t *));
+void alloc_rrs_cpy_reloc __P((struct file_entry *, symbol *));
+
+int claim_rrs_reloc __P((struct file_entry *, struct relocation_info *, symbol *, long *));
+long claim_rrs_jmpslot __P((struct file_entry *, struct relocation_info *, symbol *, long));
+long claim_rrs_gotslot __P((struct file_entry *, struct relocation_info *, struct localsymbol *, long));
+long claim_rrs_internal_gotslot __P((struct file_entry *, struct relocation_info *, struct localsymbol *, long));
+void claim_rrs_cpy_reloc __P((struct file_entry *, struct relocation_info *, symbol *));
+void claim_rrs_segment_reloc __P((struct file_entry *, struct relocation_info *));
+void consider_rrs_section_lengths __P((void));
+void relocate_rrs_addresses __P((void));
+void write_rrs __P((void));
+
+/* In <md>.c */
+void md_init_header __P((struct exec *, int, int));
+long md_get_addend __P((struct relocation_info *, unsigned char *));
+void md_relocate __P((struct relocation_info *, long, unsigned char *, int));
+void md_make_jmpslot __P((jmpslot_t *, long, long));
+void md_fix_jmpslot __P((jmpslot_t *, long, u_long));
+int md_make_reloc __P((struct relocation_info *, struct relocation_info *, int));
+void md_make_jmpreloc __P((struct relocation_info *, struct relocation_info *, int));
+void md_make_gotreloc __P((struct relocation_info *, struct relocation_info *, int));
+void md_make_copyreloc __P((struct relocation_info *, struct relocation_info *));
+void md_set_breakpoint __P((long, long *));
+
+#ifdef NEED_SWAP
+void md_swapin_exec_hdr __P((struct exec *));
+void md_swapout_exec_hdr __P((struct exec *));
+void md_swapin_reloc __P((struct relocation_info *, int));
+void md_swapout_reloc __P((struct relocation_info *, int));
+void md_swapout_jmpslot __P((jmpslot_t *, int));
+
+/* In xbits.c: */
+void swap_longs __P((long *, int));
+void swap_symbols __P((struct nlist *, int));
+void swap_zsymbols __P((struct nzlist *, int));
+void swap_ranlib_hdr __P((struct ranlib *, int));
+void swap__dynamic __P((struct link_dynamic *));
+void swap_section_dispatch_table __P((struct section_dispatch_table *));
+void swap_so_debug __P((struct so_debug *));
+void swapin_sod __P((struct sod *, int));
+void swapout_sod __P((struct sod *, int));
+void swapout_fshash __P((struct fshash *, int));
+#endif
diff --git a/gnu/usr.bin/ld/ldconfig/Makefile b/gnu/usr.bin/ld/ldconfig/Makefile
new file mode 100644
index 0000000..28cbe51
--- /dev/null
+++ b/gnu/usr.bin/ld/ldconfig/Makefile
@@ -0,0 +1,13 @@
+# $Id: Makefile,v 1.6 1994/02/13 20:42:18 jkh Exp $
+
+PROG= ldconfig
+SRCS= ldconfig.c shlib.c etc.c
+LDDIR?= $(.CURDIR)/..
+CFLAGS+=-I$(LDDIR) -I$(.CURDIR) -I$(LDDIR)/$(MACHINE)
+LDFLAGS+=-static
+BINDIR= /sbin
+MAN8= ldconfig.8
+
+.PATH: $(LDDIR) $(LDDIR)/$(MACHINE)
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/ld/ldconfig/ldconfig.8 b/gnu/usr.bin/ld/ldconfig/ldconfig.8
new file mode 100644
index 0000000..1ca25a4
--- /dev/null
+++ b/gnu/usr.bin/ld/ldconfig/ldconfig.8
@@ -0,0 +1,102 @@
+.Dd October 3, 1993
+.Dt LDCONFIG 8
+.Os FreeBSD 1.1
+.Sh NAME
+.Nm ldconfig
+.Nd configure the shared library cache
+.Sh SYNOPSIS
+.Nm ldconfig
+.Op Fl rsv
+.Op Ar directory Ar ...
+.Sh DESCRIPTION
+.Nm
+is used to prepare a set of
+.Dq hints
+for use by the run-time linker
+.Xr ld.so
+to facilitate quick lookup of shared libraries available in multiple
+directories. It scans a set of built-in system directories and any
+.Ar directories
+specified on the command line (in the given order) looking for shared
+libraries and stores the results in the file
+.Xr /var/run/ld.so.hints
+to forstall the overhead that would otherwise result from the
+directory search operations
+.Xr ld.so
+would have to perform to load the required shared libraries.
+.Pp
+The shared libraries so found will be automatically available for loading
+if needed by the program being prepared for execution. This obviates the need
+for storing search paths within the executable.
+.Pp
+The
+.Ev LD_LIBRARY_PATH
+environment variable can be used to override the use of
+directories (or the order thereof) from the cache or to specify additional
+directories where shared libraries might be found.
+.Ev LD_LIBRARY_PATH
+is a
+.Sq \:
+separated list of directory paths which are searched by
+.Xr ld.so
+when it needs to load a shared library. It can be viewed as the run-time
+equivalent of the
+.Fl L
+switch of
+.Xr ld.
+.Pp
+.Nm Ldconfig
+is typically run as part of the boot sequence.
+.Pp
+The following options recognized by
+.Nm ldconfig:
+.Bl -tag -width indent
+.It Fl r
+Lists the current contents of
+.Xr ld.so.hints
+on the standard output. The hints file will not be modified.
+.It Fl s
+Do not scan
+.Nm ldconfig
+\'s builtin system directories
+.Sq /usr/lib
+,
+.Sq /usr/X386/lib
+,
+.Sq /usr/X11R6/lib
+and
+.Sq /usr/local/lib
+for shared libraries.
+.It Fl v
+Switch on verbose mode.
+.Sh Security
+Special care must be taken when loading shared libraries into the address
+space of
+.Ev set-user-Id
+programs. Whenever such a program is run,
+.Xr ld.so
+will only load shared libraries from the
+.Ev ld.so.hints
+file. In particular, the
+.Ev LD_LIBRARY_PATH
+is not used to search for libraries. Thus, the role of ldconfig is dual. In
+addition to building a set of hints for quick lookup, it also serves to
+specify the trusted collection of directories from which shared objects can
+be safely loaded. It is presumed that the set of directories specified to
+.Nm ldconfig
+are under control of the system's administrator.
+.Xr ld.so
+further assists set-user-Id programs by erasing the
+.Ev LD_LIBRARY_PATH
+from the environment.
+
+.Sh FILES
+.Xr /var/run/ld.so.hints
+.Sh SEE ALSO
+.Xr ld 1 ,
+.Xr link 5
+.Sh HISTORY
+A
+.Nm
+utility first appeared in SunOS 4.0, it appeared in its current form
+in FreeBSD 1.1.
diff --git a/gnu/usr.bin/ld/ldconfig/ldconfig.c b/gnu/usr.bin/ld/ldconfig/ldconfig.c
new file mode 100644
index 0000000..b31271f
--- /dev/null
+++ b/gnu/usr.bin/ld/ldconfig/ldconfig.c
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: ldconfig.c,v 1.6 1994/06/05 19:04:11 ats Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ar.h>
+#include <ranlib.h>
+#include <a.out.h>
+#include <stab.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ld.h"
+
+#undef major
+#undef minor
+
+char *progname;
+static int verbose;
+static int nostd;
+static int justread;
+
+struct shlib_list {
+ /* Internal list of shared libraries found */
+ char *name;
+ char *path;
+ int dewey[MAXDEWEY];
+ int ndewey;
+#define major dewey[0]
+#define minor dewey[1]
+ struct shlib_list *next;
+};
+
+static struct shlib_list *shlib_head = NULL, **shlib_tail = &shlib_head;
+
+static void enter __P((char *, char *, char *, int *, int));
+static int dodir __P((char *, int));
+static int build_hints __P((void));
+static int listhints __P((void));
+
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int i, c;
+ int rval = 0;
+ extern int optind;
+
+ if ((progname = strrchr(argv[0], '/')) == NULL)
+ progname = argv[0];
+ else
+ progname++;
+
+ while ((c = getopt(argc, argv, "rsv")) != EOF) {
+ switch (c) {
+ case 'v':
+ verbose = 1;
+ break;
+ case 's':
+ nostd = 1;
+ break;
+ case 'r':
+ justread = 1;
+ break;
+ default:
+ fprintf(stderr, "Usage: %s [-r] [-s] [-v] [dir ...]\n", progname);
+ exit(1);
+ break;
+ }
+ }
+
+ if (justread)
+ return listhints();
+
+ if (!nostd)
+ std_search_path();
+
+ for (i = 0; i < n_search_dirs; i++)
+ rval |= dodir(search_dirs[i], 1);
+
+ for (i = optind; i < argc; i++)
+ rval |= dodir(argv[i], 0);
+
+ rval |= build_hints();
+
+ return rval;
+}
+
+int
+dodir(dir, silent)
+char *dir;
+int silent;
+{
+ DIR *dd;
+ struct dirent *dp;
+ char name[MAXPATHLEN], rest[MAXPATHLEN];
+ int dewey[MAXDEWEY], ndewey;
+
+ if ((dd = opendir(dir)) == NULL) {
+ if (!silent || errno != ENOENT)
+ perror(dir);
+ return -1;
+ }
+
+ while ((dp = readdir(dd)) != NULL) {
+ int n;
+
+ name[0] = rest[0] = '\0';
+
+ n = sscanf(dp->d_name, "lib%[^.].so.%s",
+ name, rest);
+
+ if (n < 2 || rest[0] == '\0')
+ continue;
+
+ ndewey = getdewey(dewey, rest);
+ enter(dir, dp->d_name, name, dewey, ndewey);
+ }
+
+ return 0;
+}
+
+static void
+enter(dir, file, name, dewey, ndewey)
+char *dir, *file, *name;
+int dewey[], ndewey;
+{
+ struct shlib_list *shp;
+
+ for (shp = shlib_head; shp; shp = shp->next) {
+ if (strcmp(name, shp->name) != 0 || major != shp->major)
+ continue;
+
+ /* Name matches existing entry */
+ if (cmpndewey(dewey, ndewey, shp->dewey, shp->ndewey) > 0) {
+
+ /* Update this entry with higher versioned lib */
+ if (verbose)
+ printf("Updating lib%s.%d.%d to %s/%s\n",
+ shp->name, shp->major, shp->minor,
+ dir, file);
+
+ free(shp->name);
+ shp->name = strdup(name);
+ free(shp->path);
+ shp->path = concat(dir, "/", file);
+ bcopy(dewey, shp->dewey, sizeof(shp->dewey));
+ shp->ndewey = ndewey;
+ }
+ break;
+ }
+
+ if (shp)
+ /* Name exists: older version or just updated */
+ return;
+
+ /* Allocate new list element */
+ if (verbose)
+ printf("Adding %s/%s\n", dir, file);
+
+ shp = (struct shlib_list *)xmalloc(sizeof *shp);
+ shp->name = strdup(name);
+ shp->path = concat(dir, "/", file);
+ bcopy(dewey, shp->dewey, MAXDEWEY);
+ shp->ndewey = ndewey;
+ shp->next = NULL;
+
+ *shlib_tail = shp;
+ shlib_tail = &shp->next;
+}
+
+
+#if DEBUG
+/* test */
+#undef _PATH_LD_HINTS
+#define _PATH_LD_HINTS "./ld.so.hints"
+#endif
+
+int
+hinthash(cp, vmajor, vminor)
+char *cp;
+int vmajor, vminor;
+{
+ int k = 0;
+
+ while (*cp)
+ k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
+
+ k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff;
+ k = (((k << 1) + (k >> 14)) ^ (vminor*167)) & 0x3fff;
+
+ return k;
+}
+
+int
+build_hints()
+{
+ struct hints_header hdr;
+ struct hints_bucket *blist;
+ struct shlib_list *shp;
+ char *strtab;
+ int i, n, str_index = 0;
+ int strtab_sz = 0; /* Total length of strings */
+ int nhints = 0; /* Total number of hints */
+ int fd;
+ char *tmpfile;
+
+ for (shp = shlib_head; shp; shp = shp->next) {
+ strtab_sz += 1 + strlen(shp->name);
+ strtab_sz += 1 + strlen(shp->path);
+ nhints++;
+ }
+
+ /* Fill hints file header */
+ hdr.hh_magic = HH_MAGIC;
+ hdr.hh_version = LD_HINTS_VERSION_1;
+ hdr.hh_nbucket = 1 * nhints;
+ n = hdr.hh_nbucket * sizeof(struct hints_bucket);
+ hdr.hh_hashtab = sizeof(struct hints_header);
+ hdr.hh_strtab = hdr.hh_hashtab + n;
+ hdr.hh_strtab_sz = strtab_sz;
+ hdr.hh_ehints = hdr.hh_strtab + hdr.hh_strtab_sz;
+
+ if (verbose)
+ printf("Totals: entries %d, buckets %d, string size %d\n",
+ nhints, hdr.hh_nbucket, strtab_sz);
+
+ /* Allocate buckets and string table */
+ blist = (struct hints_bucket *)xmalloc(n);
+ bzero((char *)blist, n);
+ for (i = 0; i < hdr.hh_nbucket; i++)
+ /* Empty all buckets */
+ blist[i].hi_next = -1;
+
+ strtab = (char *)xmalloc(strtab_sz);
+
+ /* Enter all */
+ for (shp = shlib_head; shp; shp = shp->next) {
+ struct hints_bucket *bp;
+
+ bp = blist +
+ (hinthash(shp->name, shp->major, shp->minor) % hdr.hh_nbucket);
+
+ if (bp->hi_pathx) {
+ int i;
+
+ for (i = 0; i < hdr.hh_nbucket; i++) {
+ if (blist[i].hi_pathx == 0)
+ break;
+ }
+ if (i == hdr.hh_nbucket) {
+ fprintf(stderr, "Bummer!\n");
+ return -1;
+ }
+ while (bp->hi_next != -1)
+ bp = &blist[bp->hi_next];
+ bp->hi_next = i;
+ bp = blist + i;
+ }
+
+ /* Insert strings in string table */
+ bp->hi_namex = str_index;
+ strcpy(strtab + str_index, shp->name);
+ str_index += 1 + strlen(shp->name);
+
+ bp->hi_pathx = str_index;
+ strcpy(strtab + str_index, shp->path);
+ str_index += 1 + strlen(shp->path);
+
+ /* Copy versions */
+ bcopy(shp->dewey, bp->hi_dewey, sizeof(bp->hi_dewey));
+ bp->hi_ndewey = shp->ndewey;
+ }
+
+ tmpfile = concat(_PATH_LD_HINTS, "+", "");
+ if ((fd = open(tmpfile, O_RDWR|O_CREAT|O_TRUNC, 0444)) == -1) {
+ perror(_PATH_LD_HINTS);
+ return -1;
+ }
+
+ if (write(fd, &hdr, sizeof(struct hints_header)) !=
+ sizeof(struct hints_header)) {
+ perror(_PATH_LD_HINTS);
+ return -1;
+ }
+ if (write(fd, blist, hdr.hh_nbucket * sizeof(struct hints_bucket)) !=
+ hdr.hh_nbucket * sizeof(struct hints_bucket)) {
+ perror(_PATH_LD_HINTS);
+ return -1;
+ }
+ if (write(fd, strtab, strtab_sz) != strtab_sz) {
+ perror(_PATH_LD_HINTS);
+ return -1;
+ }
+ if (close(fd) != 0) {
+ perror(_PATH_LD_HINTS);
+ return -1;
+ }
+
+ /* Install it */
+ if (unlink(_PATH_LD_HINTS) != 0 && errno != ENOENT) {
+ perror(_PATH_LD_HINTS);
+ return -1;
+ }
+
+ if (rename(tmpfile, _PATH_LD_HINTS) != 0) {
+ perror(_PATH_LD_HINTS);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+listhints()
+{
+ int fd;
+ caddr_t addr;
+ long msize;
+ struct hints_header *hdr;
+ struct hints_bucket *blist;
+ char *strtab;
+ int i;
+
+ if ((fd = open(_PATH_LD_HINTS, O_RDONLY, 0)) == -1) {
+ perror(_PATH_LD_HINTS);
+ return -1;
+ }
+
+ msize = PAGSIZ;
+ addr = mmap(0, msize, PROT_READ, MAP_FILE|MAP_COPY, fd, 0);
+
+ if (addr == (caddr_t)-1) {
+ perror(_PATH_LD_HINTS);
+ return -1;
+ }
+
+ hdr = (struct hints_header *)addr;
+ if (HH_BADMAG(*hdr)) {
+ fprintf(stderr, "%s: Bad magic: %o\n",
+ _PATH_LD_HINTS, hdr->hh_magic);
+ return -1;
+ }
+
+ if (hdr->hh_version != LD_HINTS_VERSION_1) {
+ fprintf(stderr, "Unsupported version: %d\n", hdr->hh_version);
+ return -1;
+ }
+
+ if (hdr->hh_ehints > msize) {
+ if (mmap(addr+msize, hdr->hh_ehints - msize,
+ PROT_READ, MAP_FILE|MAP_COPY|MAP_FIXED,
+ fd, msize) != (caddr_t)(addr+msize)) {
+
+ perror(_PATH_LD_HINTS);
+ return -1;
+ }
+ }
+ close(fd);
+
+ blist = (struct hints_bucket *)(addr + hdr->hh_hashtab);
+ strtab = (char *)(addr + hdr->hh_strtab);
+
+ printf("%s:\n", _PATH_LD_HINTS);
+ for (i = 0; i < hdr->hh_nbucket; i++) {
+ struct hints_bucket *bp = &blist[i];
+
+ /* Sanity check */
+ if (bp->hi_namex >= hdr->hh_strtab_sz) {
+ fprintf(stderr, "Bad name index: %#x\n", bp->hi_namex);
+ return -1;
+ }
+ if (bp->hi_pathx >= hdr->hh_strtab_sz) {
+ fprintf(stderr, "Bad path index: %#x\n", bp->hi_pathx);
+ return -1;
+ }
+
+ printf("\t%d:-l%s.%d.%d => %s (%d -> %d)\n",
+ i,
+ strtab + bp->hi_namex, bp->hi_major, bp->hi_minor,
+ strtab + bp->hi_pathx,
+ hinthash(strtab+bp->hi_namex, bp->hi_major, bp->hi_minor)
+ % hdr->hh_nbucket,
+ bp->hi_next);
+ }
+
+ return 0;
+}
+
diff --git a/gnu/usr.bin/ld/ldd/Makefile b/gnu/usr.bin/ld/ldd/Makefile
new file mode 100644
index 0000000..282a8fd
--- /dev/null
+++ b/gnu/usr.bin/ld/ldd/Makefile
@@ -0,0 +1,7 @@
+# $Id: Makefile,v 1.2 1993/11/09 04:19:24 paul Exp $
+
+PROG= ldd
+SRCS= ldd.c
+BINDIR= /usr/bin
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/ld/ldd/ldd.1 b/gnu/usr.bin/ld/ldd/ldd.1
new file mode 100644
index 0000000..13a13ee
--- /dev/null
+++ b/gnu/usr.bin/ld/ldd/ldd.1
@@ -0,0 +1,24 @@
+.Dd October 22, 1993
+.Dt LDD 1
+.Os FreeBSD 1.1
+.Sh NAME
+.Nm ldd
+.Nd list dynamic object dependencies
+.Sh SYNOPSIS
+.Nm ldd
+.Op Ar filename Ar ...
+.Sh DESCRIPTION
+.Nm ldd
+displays all shared objects that are needed to run the given program.
+Contrary to nm(1), the list includes
+.Dq indirect
+depedencies that are the result of needed shared objects which themselves
+depend on yet other shared objects.
+.Sh SEE ALSO
+.Xr ld 1 ,
+.Xr nm 1
+.Sh HISTORY
+A
+.Nm ldd
+utility first appeared in SunOS 4.0, it appeared in its current form
+in FreeBSD 1.1.
diff --git a/gnu/usr.bin/ld/ldd/ldd.c b/gnu/usr.bin/ld/ldd/ldd.c
new file mode 100644
index 0000000..1072d80
--- /dev/null
+++ b/gnu/usr.bin/ld/ldd/ldd.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: ldd.c,v 1.3 1994/02/13 20:42:43 jkh Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <a.out.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void
+usage()
+{
+ extern char *__progname;
+
+ fprintf(stderr, "Usage: %s <filename> ...\n", __progname);
+ exit(1);
+}
+
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int rval;
+ int c;
+
+ while ((c = getopt(argc, argv, "")) != EOF) {
+ switch (c) {
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc <= 0) {
+ usage();
+ /*NOTREACHED*/
+ }
+
+ /* ld.so magic */
+ setenv("LD_TRACE_LOADED_OBJECTS", "", 1);
+
+ rval = 0;
+ while (argc--) {
+ int fd;
+ struct exec hdr;
+ int status;
+
+ if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
+ warn("%s", *argv);
+ rval |= 1;
+ argv++;
+ continue;
+ }
+ if (read(fd, &hdr, sizeof hdr) != sizeof hdr ||
+ !(N_GETFLAG(hdr) & EX_DYNAMIC) ||
+ hdr.a_entry < __LDPGSZ) {
+
+ warnx("%s: not a dynamic executable", *argv);
+ (void)close(fd);
+ rval |= 1;
+ argv++;
+ continue;
+ }
+ (void)close(fd);
+
+ printf("%s:\n", *argv);
+ fflush(stdout);
+
+ switch (fork()) {
+ case -1:
+ err(1, "fork");
+ break;
+ default:
+ if (wait(&status) <= 0) {
+ warn("wait");
+ rval |= 1;
+ } else if (WIFSIGNALED(status)) {
+ fprintf(stderr, "%s: signal %d\n",
+ *argv, WTERMSIG(status));
+ rval |= 1;
+ } else if (WIFEXITED(status) && WEXITSTATUS(status)) {
+ fprintf(stderr, "%s: exit status %d\n",
+ *argv, WEXITSTATUS(status));
+ rval |= 1;
+ }
+ break;
+ case 0:
+ rval |= execl(*argv, *argv, NULL) != 0;
+ perror(*argv);
+ _exit(1);
+ }
+ argv++;
+ }
+
+ return rval;
+}
diff --git a/gnu/usr.bin/ld/lib.c b/gnu/usr.bin/ld/lib.c
new file mode 100644
index 0000000..7eaaa98
--- /dev/null
+++ b/gnu/usr.bin/ld/lib.c
@@ -0,0 +1,843 @@
+/*
+ * $Id: lib.c,v 1.9 1994/02/13 20:41:37 jkh Exp $ - library routines
+ */
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <err.h>
+#include <fcntl.h>
+#include <ar.h>
+#include <ranlib.h>
+#include <a.out.h>
+#include <stab.h>
+#include <string.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#include "ld.h"
+
+static void linear_library __P((int, struct file_entry *));
+static void symdef_library __P((int, struct file_entry *, int));
+static struct file_entry *decode_library_subfile __P((int,
+ struct file_entry *,
+ int, int *));
+
+/*
+ * Search the library ENTRY, already open on descriptor FD. This means
+ * deciding which library members to load, making a chain of `struct
+ * file_entry' for those members, and entering their global symbols in the
+ * hash table.
+ */
+
+void
+search_library(fd, entry)
+ int fd;
+ struct file_entry *entry;
+{
+ int member_length;
+ register char *name;
+ register struct file_entry *subentry;
+
+ if (!(link_mode & FORCEARCHIVE) && !undefined_global_sym_count)
+ return;
+
+ /* Examine its first member, which starts SARMAG bytes in. */
+ subentry = decode_library_subfile(fd, entry, SARMAG, &member_length);
+ if (!subentry)
+ return;
+
+ name = subentry->filename;
+ free(subentry);
+
+ /* Search via __.SYMDEF if that exists, else linearly. */
+
+ if (!strcmp(name, "__.SYMDEF"))
+ symdef_library(fd, entry, member_length);
+ else
+ linear_library(fd, entry);
+}
+
+/*
+ * Construct and return a file_entry for a library member. The library's
+ * file_entry is library_entry, and the library is open on FD.
+ * SUBFILE_OFFSET is the byte index in the library of this member's header.
+ * We store the length of the member into *LENGTH_LOC.
+ */
+
+static struct file_entry *
+decode_library_subfile(fd, library_entry, subfile_offset, length_loc)
+ int fd;
+ struct file_entry *library_entry;
+ int subfile_offset;
+ int *length_loc;
+{
+ int bytes_read;
+ register int namelen;
+ int member_length, content_length;
+ int starting_offset;
+ register char *name;
+ struct ar_hdr hdr1;
+ register struct file_entry *subentry;
+
+ lseek(fd, subfile_offset, 0);
+
+ bytes_read = read(fd, &hdr1, sizeof hdr1);
+ if (!bytes_read)
+ return 0; /* end of archive */
+
+ if (sizeof hdr1 != bytes_read)
+ errx(1, "%s: malformed library archive",
+ get_file_name(library_entry));
+
+ if (sscanf(hdr1.ar_size, "%d", &member_length) != 1)
+ errx(1, "%s: malformatted header of archive member: %.*s",
+ get_file_name(library_entry),
+ sizeof(hdr1.ar_name), hdr1.ar_name);
+
+ subentry = (struct file_entry *) xmalloc(sizeof(struct file_entry));
+ bzero(subentry, sizeof(struct file_entry));
+
+ for (namelen = 0;
+ namelen < sizeof hdr1.ar_name
+ && hdr1.ar_name[namelen] != 0 && hdr1.ar_name[namelen] != ' '
+ && hdr1.ar_name[namelen] != '/';
+ namelen++);
+
+ starting_offset = subfile_offset + sizeof hdr1;
+ content_length = member_length;
+
+#ifdef AR_EFMT1
+ /*
+ * BSD 4.4 extended AR format: #1/<namelen>, with name as the
+ * first <namelen> bytes of the file
+ */
+ if (strncmp(hdr1.ar_name, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 &&
+ isdigit(hdr1.ar_name[sizeof(AR_EFMT1) - 1])) {
+
+ namelen = atoi(&hdr1.ar_name[sizeof(AR_EFMT1) - 1]);
+ name = (char *)xmalloc(namelen + 1);
+ if (read(fd, name, namelen) != namelen)
+ errx(1, "%s: malformatted archive member: %.*s",
+ get_file_name(library_entry),
+ sizeof(hdr1.ar_name), hdr1.ar_name);
+ name[namelen] = 0;
+ content_length -= namelen;
+ starting_offset += namelen;
+ } else
+
+#endif
+ {
+ name = (char *)xmalloc(namelen + 1);
+ strncpy(name, hdr1.ar_name, namelen);
+ name[namelen] = 0;
+ }
+
+ subentry->filename = name;
+ subentry->local_sym_name = name;
+ subentry->starting_offset = starting_offset;
+ subentry->superfile = library_entry;
+ subentry->total_size = content_length;
+#if 0
+ subentry->symbols = 0;
+ subentry->strings = 0;
+ subentry->subfiles = 0;
+ subentry->chain = 0;
+ subentry->flags = 0;
+#endif
+
+ (*length_loc) = member_length;
+
+ return subentry;
+}
+
+static int subfile_wanted_p __P((struct file_entry *));
+
+/*
+ * Search a library that has a __.SYMDEF member. FD is a descriptor on
+ * which the library is open. The file pointer is assumed to point at the
+ * __.SYMDEF data. ENTRY is the library's file_entry. MEMBER_LENGTH is the
+ * length of the __.SYMDEF data.
+ */
+
+static void
+symdef_library(fd, entry, member_length)
+ int fd;
+ struct file_entry *entry;
+ int member_length;
+{
+ int *symdef_data = (int *) xmalloc(member_length);
+ register struct ranlib *symdef_base;
+ char *sym_name_base;
+ int nsymdefs;
+ int length_of_strings;
+ int not_finished;
+ int bytes_read;
+ register int i;
+ struct file_entry *prev = 0;
+ int prev_offset = 0;
+
+ bytes_read = read(fd, symdef_data, member_length);
+ if (bytes_read != member_length)
+ errx(1, "%s: malformatted __.SYMDEF",
+ get_file_name(entry));
+
+ nsymdefs = md_swap_long(*symdef_data) / sizeof(struct ranlib);
+ if (nsymdefs < 0 ||
+ nsymdefs * sizeof(struct ranlib) + 2 * sizeof(int) > member_length)
+ errx(1, "%s: malformatted __.SYMDEF",
+ get_file_name(entry));
+
+ symdef_base = (struct ranlib *) (symdef_data + 1);
+ length_of_strings = md_swap_long(*(int *) (symdef_base + nsymdefs));
+
+ if (length_of_strings < 0
+ || nsymdefs * sizeof(struct ranlib) + length_of_strings
+ + 2 * sizeof(int) > member_length)
+ errx(1, "%s: malformatted __.SYMDEF",
+ get_file_name(entry));
+
+ sym_name_base = sizeof(int) + (char *) (symdef_base + nsymdefs);
+
+ /* Check all the string indexes for validity. */
+ md_swapin_ranlib_hdr(symdef_base, nsymdefs);
+ for (i = 0; i < nsymdefs; i++) {
+ register int index = symdef_base[i].ran_un.ran_strx;
+ if (index < 0 || index >= length_of_strings
+ || (index && *(sym_name_base + index - 1)))
+ errx(1, "%s: malformatted __.SYMDEF",
+ get_file_name(entry));
+ }
+
+ /*
+ * Search the symdef data for members to load. Do this until one
+ * whole pass finds nothing to load.
+ */
+
+ not_finished = 1;
+ while (not_finished) {
+
+ not_finished = 0;
+
+ /*
+ * Scan all the symbols mentioned in the symdef for ones that
+ * we need. Load the library members that contain such
+ * symbols.
+ */
+
+ for (i = 0; (i < nsymdefs &&
+ ((link_mode & FORCEARCHIVE) ||
+ undefined_global_sym_count ||
+ common_defined_global_count)); i++) {
+
+ register symbol *sp;
+ int junk;
+ register int j;
+ register int offset = symdef_base[i].ran_off;
+ struct file_entry *subentry;
+
+
+ if (symdef_base[i].ran_un.ran_strx < 0)
+ continue;
+
+ sp = getsym_soft(sym_name_base
+ + symdef_base[i].ran_un.ran_strx);
+
+ /*
+ * If we find a symbol that appears to be needed,
+ * think carefully about the archive member that the
+ * symbol is in.
+ */
+
+ /*
+ * Per Mike Karels' recommendation, we no longer load
+ * library files if the only reference(s) that would
+ * be satisfied are 'common' references. This
+ * prevents some problems with name pollution (e.g. a
+ * global common 'utime' linked to a function).
+ */
+ if (!(link_mode & FORCEARCHIVE) &&
+ (!sp || sp->defined ||
+ (!(sp->flags & GS_REFERENCED) &&
+ !sp->sorefs)))
+ continue;
+
+ /*
+ * Don't think carefully about any archive member
+ * more than once in a given pass.
+ */
+
+ if (prev_offset == offset)
+ continue;
+ prev_offset = offset;
+
+ /*
+ * Read the symbol table of the archive member.
+ */
+
+ subentry = decode_library_subfile(fd,
+ entry, offset, &junk);
+ if (subentry == 0)
+ errx(1,
+ "invalid offset for %s in symbol table of %s",
+ sym_name_base
+ + symdef_base[i].ran_un.ran_strx,
+ entry->filename);
+
+ read_entry_symbols(fd, subentry);
+ subentry->strings = (char *)
+ alloca(subentry->string_size);
+ read_entry_strings(fd, subentry);
+
+ /*
+ * Now scan the symbol table and decide whether to
+ * load.
+ */
+
+ if (!(link_mode & FORCEARCHIVE) &&
+ !subfile_wanted_p(subentry)) {
+ if (subentry->symbols)
+ free(subentry->symbols);
+ free(subentry);
+ } else {
+ /*
+ * This member is needed; load it. Since we
+ * are loading something on this pass, we
+ * must make another pass through the symdef
+ * data.
+ */
+
+ not_finished = 1;
+
+ read_entry_relocation(fd, subentry);
+ enter_file_symbols(subentry);
+
+ if (prev)
+ prev->chain = subentry;
+ else
+ entry->subfiles = subentry;
+ prev = subentry;
+
+ /*
+ * Clear out this member's symbols from the
+ * symdef data so that following passes won't
+ * waste time on them.
+ */
+
+ for (j = 0; j < nsymdefs; j++) {
+ if (symdef_base[j].ran_off == offset)
+ symdef_base[j].ran_un.ran_strx = -1;
+ }
+
+ /*
+ * We'll read the strings again
+ * if we need them.
+ */
+ subentry->strings = 0;
+ }
+ }
+ }
+
+ free(symdef_data);
+}
+
+/*
+ * Search a library that has no __.SYMDEF. ENTRY is the library's file_entry.
+ * FD is the descriptor it is open on.
+ */
+
+static void
+linear_library(fd, entry)
+ int fd;
+ struct file_entry *entry;
+{
+ register struct file_entry *prev = 0;
+ register int this_subfile_offset = SARMAG;
+
+ while ((link_mode & FORCEARCHIVE) ||
+ undefined_global_sym_count || common_defined_global_count) {
+
+ int member_length;
+ register struct file_entry *subentry;
+
+ subentry = decode_library_subfile(fd, entry,
+ this_subfile_offset, &member_length);
+
+ if (!subentry)
+ return;
+
+ read_entry_symbols(fd, subentry);
+ subentry->strings = (char *)alloca(subentry->string_size);
+ read_entry_strings(fd, subentry);
+
+ if (!(link_mode & FORCEARCHIVE) &&
+ !subfile_wanted_p(subentry)) {
+ if (subentry->symbols)
+ free(subentry->symbols);
+ free(subentry);
+ } else {
+ read_entry_relocation(fd, subentry);
+ enter_file_symbols(subentry);
+
+ if (prev)
+ prev->chain = subentry;
+ else
+ entry->subfiles = subentry;
+ prev = subentry;
+ subentry->strings = 0; /* Since space will dissapear
+ * on return */
+ }
+
+ this_subfile_offset += member_length + sizeof(struct ar_hdr);
+ if (this_subfile_offset & 1)
+ this_subfile_offset++;
+ }
+}
+
+/*
+ * ENTRY is an entry for a library member. Its symbols have been read into
+ * core, but not entered. Return nonzero if we ought to load this member.
+ */
+
+static int
+subfile_wanted_p(entry)
+ struct file_entry *entry;
+{
+ struct localsymbol *lsp, *lspend;
+#ifdef DOLLAR_KLUDGE
+ register int dollar_cond = 0;
+#endif
+
+ lspend = entry->symbols + entry->nsymbols;
+
+ for (lsp = entry->symbols; lsp < lspend; lsp++) {
+ register struct nlist *p = &lsp->nzlist.nlist;
+ register int type = p->n_type;
+ register char *name = p->n_un.n_strx + entry->strings;
+ register symbol *sp = getsym_soft(name);
+
+ /*
+ * If the symbol has an interesting definition, we could
+ * potentially want it.
+ */
+ if (! (type & N_EXT)
+ || (type == (N_UNDF | N_EXT) && p->n_value == 0
+
+#ifdef DOLLAR_KLUDGE
+ && name[1] != '$'
+#endif
+ )
+#ifdef SET_ELEMENT_P
+ || SET_ELEMENT_P(type)
+ || set_element_prefixed_p(name)
+#endif
+ )
+ continue;
+
+
+#ifdef DOLLAR_KLUDGE
+ if (name[1] == '$') {
+ sp = getsym_soft(&name[2]);
+ dollar_cond = 1;
+ if (!sp)
+ continue;
+ if (sp->flags & SP_REFERENCED) {
+ if (write_map) {
+ print_file_name(entry, stdout);
+ fprintf(stdout, " needed due to $-conditional %s\n", name);
+ }
+ return 1;
+ }
+ continue;
+ }
+#endif
+
+ /*
+ * If this symbol has not been hashed, we can't be
+ * looking for it.
+ */
+
+ if (!sp)
+ continue;
+
+ /*
+ * We don't load a file if it merely satisfies a
+ * common reference (see explanation above in
+ * symdef_library()).
+ */
+ if ((sp->flags & GS_REFERENCED) && !sp->defined) {
+ /*
+ * This is a symbol we are looking for. It
+ * is either not yet defined or defined as a
+ * common.
+ */
+#ifdef DOLLAR_KLUDGE
+ if (dollar_cond)
+ continue;
+#endif
+ if (type == (N_UNDF | N_EXT)) {
+ /*
+ * Symbol being defined as common.
+ * Remember this, but don't load
+ * subfile just for this.
+ */
+
+ /*
+ * If it didn't used to be common, up
+ * the count of common symbols.
+ */
+ if (!sp->common_size)
+ common_defined_global_count++;
+
+ if (sp->common_size < p->n_value)
+ sp->common_size = p->n_value;
+ if (!sp->defined)
+ undefined_global_sym_count--;
+ sp->defined = type;
+ continue;
+ }
+ if (write_map) {
+ print_file_name(entry, stdout);
+ fprintf(stdout, " needed due to %s\n", sp->name);
+ }
+ return 1;
+ } else {
+ /*
+ * Check for undefined symbols or commons
+ * in shared objects.
+ */
+ struct localsymbol *lsp;
+ int wascommon = sp->defined && sp->common_size;
+ int iscommon = type == (N_UNDF|N_EXT) && p->n_value;
+
+ if (wascommon) {
+ /*
+ * sp was defined as common by shared object.
+ */
+ if (iscommon && p->n_value < sp->common_size)
+ sp->common_size = p->n_value;
+ continue;
+ }
+
+ if (sp->sorefs == NULL)
+ continue;
+
+ for (lsp = sp->sorefs; lsp; lsp = lsp->next) {
+ int type = lsp->nzlist.nlist.n_type;
+ if ( (type & N_EXT) &&
+ (type & N_STAB) == 0 &&
+ type != (N_UNDF | N_EXT))
+ break; /* We don't need it */
+ }
+ if (lsp != NULL) {
+ /* There's a real definition */
+ if (iscommon)
+ /*
+ * But this member wants it to be
+ * a common; ignore it.
+ */
+ continue;
+ }
+
+ if (iscommon) {
+ /*
+ * New symbol is common, just takes its
+ * size, but don't load.
+ */
+ sp->common_size = p->n_value;
+ sp->defined = type;
+ continue;
+ }
+
+ /*
+ * THIS STILL MISSES the case where one shared
+ * object defines a common and the next defines
+ * more strongly; fix this someday by making
+ * `struct glosym' and enter_global_ref() more
+ * symmetric.
+ */
+
+ if (write_map) {
+ print_file_name(entry, stdout);
+ fprintf(stdout,
+ " needed due to shared lib ref %s\n",
+ sp->name);
+ }
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Read the symbols of dynamic entity ENTRY into core. Assume it is already
+ * open, on descriptor FD.
+ */
+void
+read_shared_object(fd, entry)
+ struct file_entry *entry;
+ int fd;
+{
+ struct _dynamic dyn;
+ struct section_dispatch_table sdt;
+ struct nlist *np;
+ struct nzlist *nzp;
+ int n, i, has_nz = 0;
+
+ if (!(entry->flags & E_HEADER_VALID))
+ read_header(fd, entry);
+
+ /* Read DYNAMIC structure (first in data segment) */
+ if (lseek(fd, text_offset(entry) + entry->header.a_text, L_SET) ==
+ (off_t)-1)
+ err(1, "%s: lseek", get_file_name(entry));
+ if (read(fd, &dyn, sizeof dyn) != sizeof dyn) {
+ errx(1, "%s: premature EOF reading _dynamic",
+ get_file_name(entry));
+ }
+ md_swapin__dynamic(&dyn);
+
+ /* Check version */
+ switch (dyn.d_version) {
+ default:
+ errx(1, "%s: unsupported _DYNAMIC version: %d",
+ get_file_name(entry), dyn.d_version);
+ break;
+ case LD_VERSION_SUN:
+ break;
+ case LD_VERSION_BSD:
+ has_nz = 1;
+ break;
+ }
+
+ /* Read Section Dispatch Table (from data segment) */
+ if (lseek(fd,
+ text_offset(entry) + (long)dyn.d_un.d_sdt -
+ (DATA_START(entry->header) - N_DATOFF(entry->header)),
+ L_SET) == (off_t)-1)
+ err(1, "%s: lseek", get_file_name(entry));
+ if (read(fd, &sdt, sizeof sdt) != sizeof sdt)
+ errx(1, "%s: premature EOF reading sdt",
+ get_file_name(entry));
+ md_swapin_section_dispatch_table(&sdt);
+
+ /* Read symbols (text segment) */
+ n = sdt.sdt_strings - sdt.sdt_nzlist;
+ entry->nsymbols = n /
+ (has_nz ? sizeof(struct nzlist) : sizeof(struct nlist));
+ nzp = (struct nzlist *)(np = (struct nlist *)alloca (n));
+ entry->symbols = (struct localsymbol *)
+ xmalloc(entry->nsymbols * sizeof(struct localsymbol));
+
+ if (lseek(fd,
+ text_offset(entry) + (long)sdt.sdt_nzlist -
+ (TEXT_START(entry->header) - N_TXTOFF(entry->header)),
+ L_SET) == (off_t)-1)
+ err(1, "%s: lseek", get_file_name(entry));
+ if (read(fd, (char *)nzp, n) != n)
+ errx(1, "%s: premature EOF reading symbols ",
+ get_file_name(entry));
+
+ if (has_nz)
+ md_swapin_zsymbols(nzp, entry->nsymbols);
+ else
+ md_swapin_symbols(np, entry->nsymbols);
+
+ /* Convert to structs localsymbol */
+ for (i = 0; i < entry->nsymbols; i++) {
+ if (has_nz) {
+ entry->symbols[i].nzlist = *nzp++;
+ } else {
+ entry->symbols[i].nzlist.nlist = *np++;
+ entry->symbols[i].nzlist.nz_size = 0;
+ }
+ entry->symbols[i].symbol = NULL;
+ entry->symbols[i].next = NULL;
+ entry->symbols[i].entry = entry;
+ entry->symbols[i].gotslot_offset = -1;
+ entry->symbols[i].flags = 0;
+ }
+
+ /* Read strings (text segment) */
+ n = entry->string_size = sdt.sdt_str_sz;
+ entry->strings = (char *)alloca(n);
+ entry->strings_offset = text_offset(entry) + sdt.sdt_strings;
+ if (lseek(fd,
+ entry->strings_offset -
+ (TEXT_START(entry->header) - N_TXTOFF(entry->header)),
+ L_SET) == (off_t)-1)
+ err(1, "%s: lseek", get_file_name(entry));
+ if (read(fd, entry->strings, n) != n)
+ errx(1, "%s: premature EOF reading strings",
+ get_file_name(entry));
+ enter_file_symbols (entry);
+ entry->strings = 0;
+
+ /*
+ * Load any subsidiary shared objects.
+ */
+ if (sdt.sdt_sods) {
+ struct sod sod;
+ off_t offset;
+ struct file_entry *prev = NULL;
+
+ offset = (off_t)sdt.sdt_sods;
+ while (1) {
+ struct file_entry *subentry;
+ char *libname, name[MAXPATHLEN]; /*XXX*/
+
+ subentry = (struct file_entry *)
+ xmalloc(sizeof(struct file_entry));
+ bzero(subentry, sizeof(struct file_entry));
+ subentry->superfile = entry;
+
+ if (lseek(fd,
+ offset - (TEXT_START(entry->header) -
+ N_TXTOFF(entry->header)),
+ L_SET) == (off_t)-1)
+ err(1, "%s: lseek", get_file_name(entry));
+ if (read(fd, &sod, sizeof(sod)) != sizeof(sod))
+ errx(1, "%s: premature EOF reding sod",
+ get_file_name(entry));
+ md_swapin_sod(&sod, 1);
+ if (lseek(fd,
+ (off_t)sod.sod_name - (TEXT_START(entry->header) -
+ N_TXTOFF(entry->header)),
+ L_SET) == (off_t)-1)
+ err(1, "%s: lseek", get_file_name(entry));
+ (void)read(fd, name, sizeof(name)); /*XXX*/
+ if (sod.sod_library) {
+ int sod_major = sod.sod_major;
+ int sod_minor = sod.sod_minor;
+
+ libname = findshlib(name,
+ &sod_major, &sod_minor, 0);
+ if (libname == NULL)
+ errx(1,"no shared -l%s.%d.%d available",
+ name, sod.sod_major, sod.sod_minor);
+ subentry->filename = libname;
+ subentry->local_sym_name = concat("-l", name, "");
+ } else {
+ subentry->filename = strdup(name);
+ subentry->local_sym_name = strdup(name);
+ }
+ read_file_symbols(subentry);
+
+ if (prev)
+ prev->chain = subentry;
+ else
+ entry->subfiles = subentry;
+ prev = subentry;
+ fd = file_open(entry);
+ if ((offset = (off_t)sod.sod_next) == 0)
+ break;
+ }
+ }
+#ifdef SUN_COMPAT
+ if (link_mode & SILLYARCHIVE) {
+ char *cp, *sa_name;
+ char armag[SARMAG];
+ int fd;
+ struct file_entry *subentry;
+
+ sa_name = strdup(entry->filename);
+ if (sa_name == NULL)
+ goto out;
+ cp = sa_name + strlen(sa_name) - 1;
+ while (cp > sa_name) {
+ if (!isdigit(*cp) && *cp != '.')
+ break;
+ --cp;
+ }
+ if (cp <= sa_name || *cp != 'o') {
+ /* Not in `libxxx.so.n.m' form */
+ free(sa_name);
+ goto out;
+ }
+
+ *cp = 'a';
+ if ((fd = open(sa_name, O_RDONLY, 0)) < 0)
+ goto out;
+
+ /* Read archive magic */
+ bzero(armag, SARMAG);
+ (void)read(fd, armag, SARMAG);
+ (void)close(fd);
+ if (strncmp(armag, ARMAG, SARMAG) != 0) {
+ warnx("%s: malformed silly archive",
+ get_file_name(entry));
+ goto out;
+ }
+
+ subentry = (struct file_entry *)
+ xmalloc(sizeof(struct file_entry));
+ bzero(subentry, sizeof(struct file_entry));
+
+ entry->silly_archive = subentry;
+ subentry->superfile = entry;
+ subentry->filename = sa_name;
+ subentry->local_sym_name = sa_name;
+ subentry->flags |= E_IS_LIBRARY;
+ search_library(file_open(subentry), subentry);
+out:
+ ;
+ }
+#endif
+}
+
+#undef major
+#undef minor
+
+int
+findlib(p)
+struct file_entry *p;
+{
+ int i;
+ int fd = -1;
+ int major = -1, minor = -1;
+ char *cp, *fname = NULL;
+
+ if (!(p->flags & E_SEARCH_DYNAMIC))
+ goto dot_a;
+
+ fname = findshlib(p->filename, &major, &minor, 1);
+
+ if (fname && (fd = open(fname, O_RDONLY, 0)) > 0) {
+ p->filename = fname;
+ p->lib_major = major;
+ p->lib_minor = minor;
+ p->flags &= ~E_SEARCH_DIRS;
+ return fd;
+ }
+ (void)free(fname);
+
+dot_a:
+ p->flags &= ~E_SEARCH_DYNAMIC;
+ if (cp = strrchr(p->filename, '/')) {
+ *cp++ = '\0';
+ fname = concat(concat(p->filename, "/lib", cp), ".a", "");
+ *(--cp) = '/';
+ } else
+ fname = concat("lib", p->filename, ".a");
+
+ for (i = 0; i < n_search_dirs; i++) {
+ register char *path
+ = concat(search_dirs[i], "/", fname);
+ fd = open(path, O_RDONLY, 0);
+ if (fd > 0) {
+ p->filename = path;
+ p->flags &= ~E_SEARCH_DIRS;
+ break;
+ }
+ (void)free(path);
+ }
+ (void)free(fname);
+ return fd;
+}
+
diff --git a/gnu/usr.bin/ld/rrs.c b/gnu/usr.bin/ld/rrs.c
new file mode 100644
index 0000000..a0627e7
--- /dev/null
+++ b/gnu/usr.bin/ld/rrs.c
@@ -0,0 +1,1179 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: rrs.c,v 1.11 1994/02/13 20:41:40 jkh Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <fcntl.h>
+#include <ar.h>
+#include <ranlib.h>
+#include <a.out.h>
+#include <stab.h>
+#include <string.h>
+
+#include "ld.h"
+
+static struct _dynamic rrs_dyn; /* defined in link.h */
+static struct so_debug rrs_so_debug; /* defined in link.h */
+static struct section_dispatch_table rrs_sdt; /* defined in link.h */
+static got_t *rrs_got;
+static jmpslot_t *rrs_plt; /* defined in md.h */
+static struct relocation_info *rrs_reloc;
+static struct nzlist *rrs_symbols; /* RRS symbol table */
+static char *rrs_strtab; /* RRS strings */
+static struct rrs_hash *rrs_hashtab; /* RT hash table */
+static struct shobj *rrs_shobjs;
+
+static int reserved_rrs_relocs;
+static int claimed_rrs_relocs;
+
+static int number_of_gotslots;
+static int number_of_jmpslots;
+static int number_of_rrs_hash_entries;
+static int number_of_rrs_symbols;
+static int rrs_strtab_size;
+static int rrs_symbol_size;
+
+static int current_jmpslot_offset;
+static int current_got_offset;
+static int current_reloc_offset;
+static int current_hash_index;
+int number_of_shobjs;
+
+struct shobj {
+ struct shobj *next;
+ struct file_entry *entry;
+};
+
+/*
+RRS text segment:
+ +-------------------+ <-- sdt_rel (rrs_text_start)
+ | |
+ | relocation |
+ | |
+ +-------------------+ <-- <sdt>.sdt_hash
+ | |
+ | hash buckets |
+ | |
+ +-------------------+ <-- <sdt>.sdt_nzlist
+ | |
+ | symbols |
+ | |
+ +-------------------+ <-- <sdt>.sdt_strings
+ | |
+ | strings |
+ | |
+ +-------------------+ <-- <sdt>.sdt_sods
+ | |
+ | shobjs |
+ | |
+ +-------------------+
+ | |
+ | shobjs strings | <-- <shobj>.sod_name
+ | |
+ +-------------------+
+
+
+RRS data segment:
+
+ +-------------------+ <-- __DYNAMIC (rrs_data_start)
+ | |
+ | _dymamic |
+ | |
+ +-------------------+ <-- __DYNAMIC.d_debug
+ | |
+ | so_debug |
+ | |
+ +-------------------+ <-- __DYNAMIC.d_un.d_sdt
+ | |
+ | sdt |
+ | |
+ +-------------------+ <-- _GLOBAL_OFFSET_TABLE_ (sdt_got)
+ | |
+ | _GOT_ |
+ | |
+ +-------------------+ <-- sdt_plt
+ | |
+ | PLT |
+ | |
+ +-------------------+
+*/
+
+/*
+ * Initialize RRS
+ */
+
+void
+init_rrs()
+{
+ reserved_rrs_relocs = 0;
+ claimed_rrs_relocs = 0;
+
+ number_of_rrs_symbols = 0;
+ rrs_strtab_size = 0;
+
+ /* First jmpslot reserved for run-time binder */
+ current_jmpslot_offset = sizeof(jmpslot_t);
+ number_of_jmpslots = 1;
+
+ /* First gotslot reserved for __DYNAMIC */
+ current_got_offset = sizeof(got_t);
+ number_of_gotslots = 1;
+
+ current_reloc_offset = 0;
+}
+
+/*
+ * Add NAME to the list of needed run-time objects.
+ * Return 1 if ENTRY was added to the list.
+ */
+int
+rrs_add_shobj(entry)
+struct file_entry *entry;
+{
+ struct shobj **p;
+
+ for (p = &rrs_shobjs; *p != NULL; p = &(*p)->next)
+ if (strcmp((*p)->entry->filename, entry->filename) == 0)
+ return 0;
+ *p = (struct shobj *)xmalloc(sizeof(struct shobj));
+ (*p)->next = NULL;
+ (*p)->entry = entry;
+
+ number_of_shobjs++;
+ return 1;
+}
+
+void
+alloc_rrs_reloc(entry, sp)
+struct file_entry *entry;
+symbol *sp;
+{
+#ifdef DEBUG
+printf("alloc_rrs_reloc: %s in %s\n", sp->name, get_file_name(entry));
+#endif
+ reserved_rrs_relocs++;
+}
+
+void
+alloc_rrs_segment_reloc(entry, r)
+struct file_entry *entry;
+struct relocation_info *r;
+{
+#ifdef DEBUG
+printf("alloc_rrs_segment_reloc at %#x in %s\n",
+ r->r_address, get_file_name(entry));
+#endif
+ reserved_rrs_relocs++;
+}
+
+void
+alloc_rrs_jmpslot(entry, sp)
+struct file_entry *entry;
+symbol *sp;
+{
+ if (sp->jmpslot_offset == -1) {
+ sp->jmpslot_offset = current_jmpslot_offset;
+ current_jmpslot_offset += sizeof(jmpslot_t);
+ number_of_jmpslots++;
+ if (!(link_mode & SYMBOLIC) || JMPSLOT_NEEDS_RELOC) {
+ reserved_rrs_relocs++;
+ }
+ }
+}
+
+void
+alloc_rrs_gotslot(entry, r, lsp)
+struct file_entry *entry;
+struct relocation_info *r;
+struct localsymbol *lsp;
+{
+ symbol *sp = lsp->symbol;
+
+ if (!RELOC_EXTERN_P(r)) {
+
+ if (sp != NULL) {
+ warnx("%s: relocation for internal symbol expected at %#x",
+ get_file_name(entry), RELOC_ADDRESS(r));
+ return;
+ }
+
+ if (!RELOC_STATICS_THROUGH_GOT_P(r))
+ /* No need for a GOT slot */
+ return;
+
+ if (lsp->gotslot_offset == -1) {
+ lsp->gotslot_offset = current_got_offset;
+ current_got_offset += sizeof(got_t);
+ number_of_gotslots++;
+ /*
+ * Now, see if slot needs run-time fixing
+ * If the load address is known (entry_symbol), this
+ * slot will have its final value set by `claim_got'
+ */
+ if ((link_mode & SHAREABLE) || (link_mode & SYMBOLIC))
+ reserved_rrs_relocs++;
+ }
+
+ } else {
+
+ if (sp == NULL) {
+ warnx("%s: relocation must refer to global symbol at %#x",
+ get_file_name(entry), RELOC_ADDRESS(r));
+ return;
+ }
+
+ if (sp->alias)
+ sp = sp->alias;
+
+ if (sp->gotslot_offset != -1)
+ return;
+
+ /*
+ * External symbols always get a relocation entry
+ */
+ sp->gotslot_offset = current_got_offset;
+ reserved_rrs_relocs++;
+ current_got_offset += sizeof(got_t);
+ number_of_gotslots++;
+ }
+
+}
+
+void
+alloc_rrs_cpy_reloc(entry, sp)
+struct file_entry *entry;
+symbol *sp;
+{
+ if (sp->flags & GS_CPYRELOCRESERVED)
+ return;
+#ifdef DEBUG
+printf("alloc_rrs_copy: %s in %s\n", sp->name, get_file_name(entry));
+#endif
+ sp->flags |= GS_CPYRELOCRESERVED;
+ reserved_rrs_relocs++;
+}
+
+static struct relocation_info *
+rrs_next_reloc()
+{
+ struct relocation_info *r;
+
+ r = rrs_reloc + claimed_rrs_relocs++;
+ if (claimed_rrs_relocs > reserved_rrs_relocs)
+ errx(1, "internal error: RRS relocs exceed allocation %d",
+ reserved_rrs_relocs);
+ return r;
+}
+
+/*
+ * Claim a RRS relocation as a result of a regular (ie. non-PIC)
+ * relocation record in a rel file.
+ *
+ * Return 1 if the output file needs no further updating.
+ * Return 0 if the relocation value pointed to by RELOCATION must
+ * written to a.out.
+ */
+int
+claim_rrs_reloc(entry, rp, sp, relocation)
+struct file_entry *entry;
+struct relocation_info *rp;
+symbol *sp;
+long *relocation;
+{
+ struct relocation_info *r = rrs_next_reloc();
+
+ if (rp->r_address < text_start + text_size)
+ warnx("%s: RRS text relocation at %#x for \"%s\"",
+ get_file_name(entry), rp->r_address, sp->name);
+
+#ifdef DEBUG
+printf("claim_rrs_reloc: %s in %s\n", sp->name, get_file_name(entry));
+#endif
+ r->r_address = rp->r_address;
+ r->r_symbolnum = sp->rrs_symbolnum;
+
+ if (link_mode & SYMBOLIC) {
+ if (!sp->defined)
+ warnx("Cannot reduce symbol \"%s\" in %s",
+ sp->name, get_file_name(entry));
+ RELOC_EXTERN_P(r) = 0;
+ *relocation += sp->value;
+ (void) md_make_reloc(rp, r, RELTYPE_RELATIVE);
+ return 0;
+ } else {
+ RELOC_EXTERN_P(r) = 1;
+ return md_make_reloc(rp, r, RELTYPE_EXTERN);
+ }
+}
+
+/*
+ * Claim a jmpslot. Setup RRS relocation if claimed for the first time.
+ */
+long
+claim_rrs_jmpslot(entry, rp, sp, addend)
+struct file_entry *entry;
+struct relocation_info *rp;
+symbol *sp;
+long addend;
+{
+ struct relocation_info *r;
+
+ if (sp->flags & GS_JMPSLOTCLAIMED)
+ return rrs_sdt.sdt_plt + sp->jmpslot_offset;
+
+#ifdef DEBUG
+printf("claim_rrs_jmpslot: %s: %s(%d) -> offset %x\n",
+ get_file_name(entry),
+ sp->name, sp->rrs_symbolnum, sp->jmpslot_offset);
+#endif
+
+ if (sp->jmpslot_offset == -1)
+ errx(1,
+ "internal error: %s: claim_rrs_jmpslot: %s: jmpslot_offset == -1\n",
+ get_file_name(entry),
+ sp->name);
+
+ if ((link_mode & SYMBOLIC) || rrs_section_type == RRS_PARTIAL) {
+ if (!sp->defined)
+ warnx("Cannot reduce symbol \"%s\" in %s",
+ sp->name, get_file_name(entry));
+
+ md_fix_jmpslot( rrs_plt + sp->jmpslot_offset/sizeof(jmpslot_t),
+ rrs_sdt.sdt_plt + sp->jmpslot_offset,
+ sp->value);
+ if (!JMPSLOT_NEEDS_RELOC) {
+ return rrs_sdt.sdt_plt + sp->jmpslot_offset;
+ }
+ } else {
+ md_make_jmpslot(rrs_plt + sp->jmpslot_offset/sizeof(jmpslot_t),
+ sp->jmpslot_offset,
+ claimed_rrs_relocs);
+ }
+
+ if (rrs_section_type == RRS_PARTIAL)
+ /* PLT is self-contained */
+ return rrs_sdt.sdt_plt + sp->jmpslot_offset;
+
+ /*
+ * Install a run-time relocation for this PLT entry.
+ */
+ r = rrs_next_reloc();
+ sp->flags |= GS_JMPSLOTCLAIMED;
+
+ RELOC_SYMBOL(r) = sp->rrs_symbolnum;
+
+ r->r_address = (long)rrs_sdt.sdt_plt + sp->jmpslot_offset;
+
+ if (link_mode & SYMBOLIC) {
+ RELOC_EXTERN_P(r) = 0;
+ md_make_jmpreloc(rp, r, RELTYPE_RELATIVE);
+ } else {
+ RELOC_EXTERN_P(r) = 1;
+ md_make_jmpreloc(rp, r, 0);
+ }
+
+ return rrs_sdt.sdt_plt + sp->jmpslot_offset;
+}
+
+/*
+ * Claim GOT entry for a global symbol. If this is the first relocation
+ * claiming the entry, setup a RRS relocation for it.
+ * Return offset into the GOT allocated to this symbol.
+ */
+long
+claim_rrs_gotslot(entry, rp, lsp, addend)
+struct file_entry *entry;
+struct relocation_info *rp;
+struct localsymbol *lsp;
+long addend;
+{
+ struct relocation_info *r;
+ symbol *sp = lsp->symbol;
+ int reloc_type = 0;
+
+ if (sp == NULL) {
+ return 0;
+ }
+
+ if (sp->alias)
+ sp = sp->alias;
+
+#ifdef DEBUG
+printf("claim_rrs_gotslot: %s(%d) slot offset %#x, addend %#x\n",
+ sp->name, sp->rrs_symbolnum, sp->gotslot_offset, addend);
+#endif
+ if (sp->gotslot_offset == -1)
+ errx(1,
+ "internal error: %s: claim_rrs_gotslot: %s: gotslot_offset == -1\n",
+ get_file_name(entry), sp->name);
+
+ if (sp->flags & GS_GOTSLOTCLAIMED)
+ /* This symbol already passed here before. */
+ return sp->gotslot_offset;
+
+ if (sp->defined &&
+ (!(link_mode & SHAREABLE) || (link_mode & SYMBOLIC))) {
+
+ /*
+ * Reduce to just a base-relative translation.
+ */
+
+ *(got_t *)((long)rrs_got + sp->gotslot_offset) =
+ sp->value + addend;
+ reloc_type = RELTYPE_RELATIVE;
+
+ } else if ((link_mode & SYMBOLIC) || rrs_section_type == RRS_PARTIAL) {
+ /*
+ * SYMBOLIC: all symbols must be known.
+ * RRS_PARTIAL: we don't link against shared objects,
+ * so again all symbols must be known.
+ */
+ warnx("Cannot reduce symbol \"%s\" in %s",
+ sp->name, get_file_name(entry));
+
+ } else {
+
+ /*
+ * This gotslot will be updated with symbol value at run-rime.
+ */
+
+ *(got_t *)((long)rrs_got + sp->gotslot_offset) = addend;
+ }
+
+ if (rrs_section_type == RRS_PARTIAL) {
+ /*
+ * Base address is known, gotslot should be fully
+ * relocated by now.
+ * NOTE: RRS_PARTIAL implies !SHAREABLE.
+ */
+ if (!sp->defined)
+ warnx("Cannot reduce symbol \"%s\" in %s",
+ sp->name, get_file_name(entry));
+ return sp->gotslot_offset;
+ }
+
+ /*
+ * Claim a relocation entry.
+ * If symbol is defined and in "main" (!SHAREABLE)
+ * we still put out a relocation as we cannot easily
+ * undo the allocation.
+ * `RELTYPE_RELATIVE' relocations have the external bit off
+ * as no symbol need be looked up at run-time.
+ */
+ r = rrs_next_reloc();
+ sp->flags |= GS_GOTSLOTCLAIMED;
+ r->r_address = rrs_sdt.sdt_got + sp->gotslot_offset;
+ RELOC_SYMBOL(r) = sp->rrs_symbolnum;
+ RELOC_EXTERN_P(r) = !(reloc_type == RELTYPE_RELATIVE);
+ md_make_gotreloc(rp, r, reloc_type);
+
+ return sp->gotslot_offset;
+}
+
+/*
+ * Claim a GOT entry for a static symbol. Return offset of the
+ * allocated GOT entry. If RELOC_STATICS_THROUGH_GOT_P is in effect
+ * return the offset of the symbol with respect to the *location* of
+ * the GOT.
+ */
+long
+claim_rrs_internal_gotslot(entry, rp, lsp, addend)
+struct file_entry *entry;
+struct relocation_info *rp;
+struct localsymbol *lsp;
+long addend;
+{
+ struct relocation_info *r;
+
+ addend += lsp->nzlist.nz_value;
+
+ if (!RELOC_STATICS_THROUGH_GOT_P(r))
+ return addend - rrs_sdt.sdt_got;
+
+#ifdef DEBUG
+printf("claim_rrs_internal_gotslot: %s: slot offset %#x, addend = %#x\n",
+ get_file_name(entry), lsp->gotslot_offset, addend);
+#endif
+
+ if (lsp->gotslot_offset == -1)
+ errx(1,
+ "internal error: %s: claim_rrs_internal_gotslot at %#x: slot_offset == -1\n",
+ get_file_name(entry), RELOC_ADDRESS(rp));
+
+ if (lsp->flags & LS_GOTSLOTCLAIMED)
+ /* Already done */
+ return lsp->gotslot_offset;
+
+ *(long *)((long)rrs_got + lsp->gotslot_offset) = addend;
+
+ if (!(link_mode & SHAREABLE))
+ return lsp->gotslot_offset;
+
+ /*
+ * Relocation entry needed for this static GOT entry.
+ */
+ r = rrs_next_reloc();
+ lsp->flags |= LS_GOTSLOTCLAIMED;
+ r->r_address = rrs_sdt.sdt_got + lsp->gotslot_offset;
+ RELOC_EXTERN_P(r) = 0;
+ md_make_gotreloc(rp, r, RELTYPE_RELATIVE);
+ return lsp->gotslot_offset;
+}
+
+void
+claim_rrs_cpy_reloc(entry, rp, sp)
+struct file_entry *entry;
+struct relocation_info *rp;
+symbol *sp;
+{
+ struct relocation_info *r;
+
+ if (sp->flags & GS_CPYRELOCCLAIMED)
+ return;
+
+ if (!(sp->flags & GS_CPYRELOCRESERVED))
+ errx(1,
+ "internal error: %s: claim_cpy_reloc: %s: no reservation\n",
+ get_file_name(entry), sp->name);
+
+#ifdef DEBUG
+printf("claim_rrs_copy: %s: %s -> %x\n",
+ get_file_name(entry), sp->name, sp->so_defined);
+#endif
+
+ r = rrs_next_reloc();
+ sp->flags |= GS_CPYRELOCCLAIMED;
+ r->r_address = rp->r_address;
+ RELOC_SYMBOL(r) = sp->rrs_symbolnum;
+ RELOC_EXTERN_P(r) = RELOC_EXTERN_P(rp);
+ md_make_cpyreloc(rp, r);
+}
+
+void
+claim_rrs_segment_reloc(entry, rp)
+struct file_entry *entry;
+struct relocation_info *rp;
+{
+ struct relocation_info *r = rrs_next_reloc();
+
+#ifdef DEBUG
+printf("claim_rrs_segment_reloc: %s at %#x\n",
+ get_file_name(entry), rp->r_address);
+#endif
+ r->r_address = rp->r_address;
+ RELOC_TYPE(r) = RELOC_TYPE(rp);
+ RELOC_EXTERN_P(r) = 0;
+ md_make_reloc(rp, r, RELTYPE_RELATIVE);
+
+}
+
+/*
+ * Fill the RRS hash table for the given symbol name.
+ * NOTE: the hash value computation must match the one in rtld.
+ */
+void
+rrs_insert_hash(cp, index)
+char *cp;
+int index;
+{
+ int hashval = 0;
+ struct rrs_hash *hp;
+
+ for (; *cp; cp++)
+ hashval = (hashval << 1) + *cp;
+
+ hashval = (hashval & 0x7fffffff) % rrs_sdt.sdt_buckets;
+
+ /* Get to the bucket */
+ hp = rrs_hashtab + hashval;
+ if (hp->rh_symbolnum == -1) {
+ /* Empty bucket, use it */
+ hp->rh_symbolnum = index;
+ hp->rh_next = 0;
+ return;
+ }
+
+ while (hp->rh_next != 0)
+ hp = rrs_hashtab + hp->rh_next;
+
+ hp->rh_next = current_hash_index++;
+ hp = rrs_hashtab + hp->rh_next;
+ hp->rh_symbolnum = index;
+ hp->rh_next = 0;
+}
+
+/*
+ * There are two interesting cases to consider here.
+ *
+ * 1) No shared objects were loaded, but there were PIC input rel files.
+ * In this case we must output a _GLOBAL_OFFSET_TABLE_ but no other
+ * RRS data. Also, the entries in the GOT must be fully resolved.
+ *
+ * 2) It's a genuine dynamically linked program, so the whole RRS scoop
+ * goes into a.out.
+ */
+void
+consider_rrs_section_lengths()
+{
+ int n;
+ struct shobj *shp, **shpp;
+
+#ifdef notyet
+/* We run into trouble with this as long as shared object symbols
+ are not checked for definitions */
+ /*
+ * First, determine the real number of shared objects we need.
+ */
+ for (shpp = &rrs_shobjs; *shpp; shpp = &(*shpp)->next) {
+ while (*shpp && !((*shpp)->entry->flags & E_SYMBOLS_USED)) {
+ if (--number_of_shobjs < 0)
+ errx(1, "internal error: number_of_shobjs < 0");
+ *shpp = (*shpp)->next;
+ }
+ if (*shpp == NULL)
+ break;
+ }
+#endif
+
+ /* First, determine what of the RRS we want */
+ if (relocatable_output)
+ rrs_section_type = RRS_NONE;
+ else if (link_mode & SHAREABLE)
+ rrs_section_type = RRS_FULL;
+ else if (number_of_shobjs == 0 /*&& !(link_mode & DYNAMIC)*/) {
+ /*
+ * First slots in both tables are reserved
+ * hence the "> 1" condition
+ */
+ if (number_of_gotslots > 1 || number_of_jmpslots > 1)
+ rrs_section_type = RRS_PARTIAL;
+ else
+ rrs_section_type = RRS_NONE;
+ } else
+ rrs_section_type = RRS_FULL;
+
+ if (rrs_section_type == RRS_NONE) {
+ got_symbol->defined = 0;
+ return;
+ }
+
+ rrs_symbol_size = LD_VERSION_NZLIST_P(soversion) ?
+ sizeof(struct nzlist) : sizeof(struct nlist);
+
+ /*
+ * If there is an entry point, __DYNAMIC must be referenced (usually
+ * from crt0), as this is the method used to determine whether the
+ * run-time linker must be called.
+ */
+ if (!(link_mode & SHAREABLE) &&
+ !(dynamic_symbol->flags & GS_REFERENCED))
+ errx(1, "No reference to __DYNAMIC");
+
+ dynamic_symbol->flags |= GS_REFERENCED;
+
+ if (number_of_gotslots > 1)
+ got_symbol->flags |= GS_REFERENCED;
+
+
+ /* Next, allocate relocs, got and plt */
+ n = reserved_rrs_relocs * sizeof(struct relocation_info);
+ rrs_reloc = (struct relocation_info *)xmalloc(n);
+ bzero(rrs_reloc, n);
+
+ n = number_of_gotslots * sizeof(got_t);
+ rrs_got = (got_t *)xmalloc(n);
+ bzero(rrs_got, n);
+
+ n = number_of_jmpslots * sizeof(jmpslot_t);
+ rrs_plt = (jmpslot_t *)xmalloc(n);
+ bzero(rrs_plt, n);
+
+ /* Initialize first jmpslot */
+ md_fix_jmpslot(rrs_plt, 0, 0);
+
+ if (rrs_section_type == RRS_PARTIAL) {
+ rrs_data_size = number_of_gotslots * sizeof(got_t);
+ rrs_data_size += number_of_jmpslots * sizeof(jmpslot_t);
+ return;
+ }
+
+ /*
+ * Walk the symbol table, assign RRS symbol numbers
+ * Assign number 0 to __DYNAMIC (!! Sun compatibility)
+ */
+ dynamic_symbol->rrs_symbolnum = number_of_rrs_symbols++;
+ FOR_EACH_SYMBOL(i ,sp) {
+ if (sp->flags & GS_REFERENCED) {
+ rrs_strtab_size += 1 + strlen(sp->name);
+ if (sp != dynamic_symbol)
+ sp->rrs_symbolnum = number_of_rrs_symbols++;
+ if (sp->alias) {
+ /*
+ * (sigh) Always allocate space to hold the
+ * indirection. At this point there's not
+ * enough information to decide whether it's
+ * actually needed or not.
+ */
+ number_of_rrs_symbols++;
+ rrs_strtab_size += 1 + strlen(sp->alias->name);
+ }
+ }
+ } END_EACH_SYMBOL;
+
+ /*
+ * Now that we know how many RRS symbols there are going to be,
+ * allocate and initialize the RRS symbol hash table.
+ */
+ rrs_sdt.sdt_buckets = number_of_rrs_symbols/4;
+ if (rrs_sdt.sdt_buckets < 4)
+ rrs_sdt.sdt_buckets = 4;
+
+ number_of_rrs_hash_entries = rrs_sdt.sdt_buckets + number_of_rrs_symbols;
+ rrs_hashtab = (struct rrs_hash *)xmalloc(
+ number_of_rrs_hash_entries * sizeof(struct rrs_hash));
+ for (n = 0; n < rrs_sdt.sdt_buckets; n++)
+ rrs_hashtab[n].rh_symbolnum = -1;
+ current_hash_index = rrs_sdt.sdt_buckets;
+
+ /*
+ * Get symbols into hash table now, so we can fine tune the size
+ * of the latter. We adjust the value of `number_of_rrs_hash_entries'
+ * to the number of hash link slots actually used.
+ */
+ FOR_EACH_SYMBOL(i ,sp) {
+ if (sp->flags & GS_REFERENCED)
+ rrs_insert_hash(sp->name, sp->rrs_symbolnum);
+ } END_EACH_SYMBOL;
+ number_of_rrs_hash_entries = current_hash_index;
+
+ /*
+ * Calculate RRS section sizes.
+ */
+ rrs_data_size = sizeof(struct _dynamic);
+ rrs_data_size += sizeof(struct so_debug);
+ rrs_data_size += sizeof(struct section_dispatch_table);
+ rrs_data_size += number_of_gotslots * sizeof(got_t);
+ rrs_data_size += number_of_jmpslots * sizeof(jmpslot_t);
+ rrs_data_size = MALIGN(rrs_data_size);
+
+ rrs_text_size = reserved_rrs_relocs * sizeof(struct relocation_info);
+ rrs_text_size += number_of_rrs_hash_entries * sizeof(struct rrs_hash);
+ rrs_text_size += number_of_rrs_symbols * rrs_symbol_size;
+
+ /* Align strings size */
+ rrs_strtab_size = MALIGN(rrs_strtab_size);
+ rrs_text_size += rrs_strtab_size;
+
+ /* Process needed shared objects */
+ for (shp = rrs_shobjs; shp; shp = shp->next) {
+ char *name = shp->entry->local_sym_name;
+
+ if (*name == '-' && *(name+1) == 'l')
+ name += 2;
+
+ rrs_text_size += sizeof(struct sod);
+ rrs_text_size += 1 + strlen(name);
+ }
+
+ /* Finally, align size */
+ rrs_text_size = MALIGN(rrs_text_size);
+}
+
+void
+relocate_rrs_addresses()
+{
+
+ dynamic_symbol->value = 0;
+
+ if (rrs_section_type == RRS_NONE)
+ return;
+
+ if (rrs_section_type == RRS_PARTIAL) {
+ got_symbol->value = rrs_sdt.sdt_got = rrs_data_start;
+ rrs_sdt.sdt_plt = rrs_sdt.sdt_got +
+ number_of_gotslots * sizeof(got_t);
+ return;
+ }
+
+ /*
+ * RRS data relocations.
+ */
+ rrs_dyn.d_version = soversion;
+ rrs_dyn.d_debug = (struct so_debug *)
+ (rrs_data_start + sizeof(struct _dynamic));
+ rrs_dyn.d_un.d_sdt = (struct section_dispatch_table *)
+ ((long)rrs_dyn.d_debug + sizeof(struct so_debug));
+
+ rrs_sdt.sdt_got = (long)rrs_dyn.d_un.d_sdt +
+ sizeof(struct section_dispatch_table);
+ rrs_sdt.sdt_plt = rrs_sdt.sdt_got + number_of_gotslots*sizeof(got_t);
+
+ /*
+ * RRS text relocations.
+ */
+ rrs_sdt.sdt_rel = rrs_text_start;
+ /*
+ * Sun BUG compatibility alert.
+ * Main program's RRS text values are relative to TXTADDR? WHY??
+ */
+#ifdef SUN_COMPAT
+ if (soversion == LD_VERSION_SUN && !(link_mode & SHAREABLE))
+ rrs_sdt.sdt_rel -= N_TXTADDR(outheader);
+#endif
+
+ rrs_sdt.sdt_hash = rrs_sdt.sdt_rel +
+ reserved_rrs_relocs * sizeof(struct relocation_info);
+ rrs_sdt.sdt_nzlist = rrs_sdt.sdt_hash +
+ number_of_rrs_hash_entries * sizeof(struct rrs_hash);
+ rrs_sdt.sdt_strings = rrs_sdt.sdt_nzlist +
+ number_of_rrs_symbols * rrs_symbol_size;
+ rrs_sdt.sdt_str_sz = rrs_strtab_size;
+ rrs_sdt.sdt_text_sz = text_size;
+ rrs_sdt.sdt_plt_sz = number_of_jmpslots * sizeof(jmpslot_t);
+
+ rrs_sdt.sdt_sods = rrs_shobjs ? rrs_sdt.sdt_strings+rrs_strtab_size : 0;
+ rrs_sdt.sdt_filler1 = 0;
+ rrs_sdt.sdt_filler2 = 0;
+
+ /*
+ * Assign addresses to _GLOBAL_OFFSET_TABLE_ and __DYNAMIC
+ * &__DYNAMIC is also in the first GOT entry.
+ */
+ got_symbol->value = rrs_sdt.sdt_got;
+
+ *rrs_got = dynamic_symbol->value = rrs_data_start;
+
+}
+
+void
+write_rrs_data()
+{
+ long pos;
+
+ if (rrs_section_type == RRS_NONE)
+ return;
+
+ pos = rrs_data_start + (N_DATOFF(outheader) - DATA_START(outheader));
+ if (lseek(outdesc, pos, L_SET) != pos)
+ err(1, "write_rrs_data: lseek");
+
+ if (rrs_section_type == RRS_PARTIAL) {
+ /*
+ * Only a GOT and PLT are needed.
+ */
+ if (number_of_gotslots <= 1)
+ errx(1, "write_rrs_data: # gotslots <= 1");
+
+ md_swapout_got(rrs_got, number_of_gotslots);
+ mywrite(rrs_got, number_of_gotslots,
+ sizeof(got_t), outdesc);
+
+ if (number_of_jmpslots <= 1)
+ errx(1, "write_rrs_data: # jmpslots <= 1");
+
+ md_swapout_jmpslot(rrs_plt, number_of_jmpslots);
+ mywrite(rrs_plt, number_of_jmpslots,
+ sizeof(jmpslot_t), outdesc);
+ return;
+ }
+
+ md_swapout__dynamic(&rrs_dyn);
+ mywrite(&rrs_dyn, 1, sizeof(struct _dynamic), outdesc);
+
+ md_swapout_so_debug(&rrs_so_debug);
+ mywrite(&rrs_so_debug, 1, sizeof(struct so_debug), outdesc);
+
+ md_swapout_section_dispatch_table(&rrs_sdt);
+ mywrite(&rrs_sdt, 1, sizeof(struct section_dispatch_table), outdesc);
+
+ md_swapout_got(rrs_got, number_of_gotslots);
+ mywrite(rrs_got, number_of_gotslots, sizeof(got_t), outdesc);
+
+ md_swapout_jmpslot(rrs_plt, number_of_jmpslots);
+ mywrite(rrs_plt, number_of_jmpslots, sizeof(jmpslot_t), outdesc);
+}
+
+void
+write_rrs_text()
+{
+ long pos;
+ int i;
+ int symsize;
+ struct nzlist *nlp;
+ int offset = 0;
+ struct shobj *shp;
+ struct sod *sodp;
+
+ if (rrs_section_type == RRS_PARTIAL)
+ return;
+
+ pos = rrs_text_start + (N_TXTOFF(outheader) - TEXT_START(outheader));
+ if (lseek(outdesc, pos, L_SET) != pos)
+ err(1, "write_rrs_text: lseek");
+
+ /* Write relocation records */
+ md_swapout_reloc(rrs_reloc, reserved_rrs_relocs);
+ mywrite(rrs_reloc, reserved_rrs_relocs,
+ sizeof(struct relocation_info), outdesc);
+
+ /* Write the RRS symbol hash tables */
+ md_swapout_rrs_hash(rrs_hashtab, number_of_rrs_hash_entries);
+ mywrite(rrs_hashtab, number_of_rrs_hash_entries, sizeof(struct rrs_hash), outdesc);
+
+ /*
+ * Determine size of an RRS symbol entry, allocate space
+ * to collect them in.
+ */
+ symsize = number_of_rrs_symbols * rrs_symbol_size;
+ nlp = rrs_symbols = (struct nzlist *)alloca(symsize);
+ rrs_strtab = (char *)alloca(rrs_strtab_size);
+
+#define INCR_NLP(p) ((p) = (struct nzlist *)((long)(p) + rrs_symbol_size))
+
+ /* __DYNAMIC symbol *must* be first for Sun compatibility */
+ nlp->nz_desc = nlp->nz_other = 0;
+ if (LD_VERSION_NZLIST_P(soversion))
+ nlp->nz_size = 0;
+ nlp->nz_type = dynamic_symbol->defined;
+ nlp->nz_value = dynamic_symbol->value;
+ nlp->nz_value = dynamic_symbol->value;
+ nlp->nz_strx = offset;
+ strcpy(rrs_strtab + offset, dynamic_symbol->name);
+ offset += 1 + strlen(dynamic_symbol->name);
+ INCR_NLP(nlp);
+
+ /*
+ * Now, for each global symbol, construct a nzlist element
+ * for inclusion in the RRS symbol table.
+ */
+ FOR_EACH_SYMBOL(i, sp) {
+
+ if (!(sp->flags & GS_REFERENCED) || sp == dynamic_symbol)
+ continue;
+
+ if ((long)nlp - (long)rrs_symbols >=
+ number_of_rrs_symbols * rrs_symbol_size)
+ errx(1,
+ "internal error: rrs symbols exceed allocation %d ",
+ number_of_rrs_symbols);
+
+ nlp->nz_desc = 0;
+ nlp->nz_other = 0;
+ if (LD_VERSION_NZLIST_P(soversion))
+ nlp->nz_size = 0;
+
+ if (sp->defined > 1) {
+ /* defined with known type */
+ if (!(link_mode & SHAREABLE) &&
+ sp->alias && sp->alias->defined > 1) {
+ /*
+ * If the target of an indirect symbol has
+ * been defined and we are outputting an
+ * executable, resolve the indirection; it's
+ * no longer needed.
+ */
+ nlp->nz_type = sp->alias->defined;
+ nlp->nz_value = sp->alias->value;
+ nlp->nz_other = N_OTHER(0, sp->alias->aux);
+ } else if (sp->defined == N_SIZE) {
+ /*
+ * Make sure this symbol isn't going
+ * to define anything.
+ */
+ nlp->nz_type = N_UNDF;
+ nlp->nz_value = 0;
+ } else {
+ nlp->nz_type = sp->defined;
+ nlp->nz_value = sp->value;
+ nlp->nz_other = N_OTHER(0, sp->aux);
+ }
+ if (LD_VERSION_NZLIST_P(soversion))
+ nlp->nz_size = sp->size;
+ } else if (sp->common_size) {
+ /*
+ * a common definition
+ */
+ nlp->nz_type = N_UNDF | N_EXT;
+ nlp->nz_value = sp->common_size;
+ } else if (!sp->defined) {
+ /* undefined */
+ nlp->nz_type = N_UNDF | N_EXT;
+ nlp->nz_value = 0;
+ if (sp->so_defined && sp->jmpslot_offset != -1) {
+ /*
+ * Define a "weak" function symbol.
+ */
+ if (sp->aux != AUX_FUNC)
+ errx(1, "%s: non-function jmpslot",
+ sp->name);
+ nlp->nz_other = N_OTHER(0, sp->aux);
+ nlp->nz_value =
+ rrs_sdt.sdt_plt + sp->jmpslot_offset;
+ }
+ } else
+ errx(1,
+ "internal error: %s defined in mysterious way",
+ sp->name);
+
+ /* Set symbol's name */
+ nlp->nz_strx = offset;
+ strcpy(rrs_strtab + offset, sp->name);
+ offset += 1 + strlen(sp->name);
+
+ if (sp->alias) {
+ /*
+ * Write an extra symbol for indirections (possibly
+ * just a dummy).
+ */
+ int t = (nlp->nz_type == N_INDR + N_EXT);
+
+ INCR_NLP(nlp);
+ nlp->nz_type = N_UNDF + t?N_EXT:0;
+ nlp->nz_un.n_strx = offset;
+ nlp->nz_value = 0;
+ nlp->nz_other = 0;
+ nlp->nz_desc = 0;
+ nlp->nz_size = 0;
+ strcpy(rrs_strtab + offset, sp->alias->name);
+ offset += 1 + strlen(sp->alias->name);
+ }
+
+ INCR_NLP(nlp);
+
+ } END_EACH_SYMBOL;
+
+ if (MALIGN(offset) != rrs_strtab_size)
+ errx(1,
+ "internal error: inconsistent RRS string table length: %d, expected %d",
+ offset, rrs_strtab_size);
+
+ /* Write the symbol table */
+ if (rrs_symbol_size == sizeof(struct nlist))
+ md_swapout_symbols(rrs_symbols, number_of_rrs_symbols);
+ else
+ md_swapout_zsymbols(rrs_symbols, number_of_rrs_symbols);
+ mywrite(rrs_symbols, symsize, 1, outdesc);
+
+ /* Write the strings */
+ mywrite(rrs_strtab, rrs_strtab_size, 1, outdesc);
+
+ /*
+ * Write the names of the shared objects needed at run-time
+ */
+ pos = rrs_sdt.sdt_sods + number_of_shobjs * sizeof(struct sod);
+ sodp = (struct sod *)alloca( number_of_shobjs * sizeof(struct sod));
+
+ for (i = 0, shp = rrs_shobjs; shp; i++, shp = shp->next) {
+ char *name = shp->entry->local_sym_name;
+
+ if (i >= number_of_shobjs)
+ errx(1, "internal error: # of link objects exceeds %d",
+ number_of_shobjs);
+
+ sodp[i].sod_name = pos;
+ sodp[i].sod_major = shp->entry->lib_major;
+ sodp[i].sod_minor = shp->entry->lib_minor;
+
+ if (*name == '-' && *(name+1) == 'l') {
+ name += 2;
+ sodp[i].sod_library = 1;
+ } else
+ sodp[i].sod_library = 0;
+
+ pos += 1 + strlen(name);
+ sodp[i].sod_next = (i == number_of_shobjs - 1) ? 0 :
+ (rrs_sdt.sdt_sods + (i+1)*sizeof(struct sod));
+ }
+
+ if (i < number_of_shobjs)
+ errx(1,
+ "internal error: # of link objects less then expected %d",
+ number_of_shobjs);
+
+ md_swapout_sod(sodp, number_of_shobjs);
+ mywrite(sodp, number_of_shobjs, sizeof(struct sod), outdesc);
+
+ for (i = 0, shp = rrs_shobjs; shp; i++, shp = shp->next) {
+ char *name = shp->entry->local_sym_name;
+
+ if (*name == '-' && *(name+1) == 'l') {
+ name += 2;
+ }
+
+ mywrite(name, strlen(name) + 1, 1, outdesc);
+ }
+}
+
+void
+write_rrs()
+{
+
+ /*
+ * First, do some consistency checks on the RRS segment.
+ */
+ if (rrs_section_type == RRS_NONE) {
+ if (reserved_rrs_relocs > 1)
+ errx(1,
+ "internal error: RRS relocs in static program: %d",
+ reserved_rrs_relocs-1);
+ return;
+ }
+
+#ifdef DEBUG
+printf("rrs_relocs %d, gotslots %d, jmpslots %d\n",
+ reserved_rrs_relocs, number_of_gotslots-1, number_of_jmpslots-1);
+#endif
+
+#if 0
+ /* Must fix this check: misses out when linking PIC code but no
+ shared object involved: reserved relocs are never claimed!
+ */
+ if (claimed_rrs_relocs != reserved_rrs_relocs) {
+ errx(1, "internal error: reserved relocs(%d) != claimed(%d)",
+ reserved_rrs_relocs, claimed_rrs_relocs);
+ printf("FIX:internal error: reserved relocs(%d) != claimed(%d)\n",
+ reserved_rrs_relocs, claimed_rrs_relocs);
+ }
+#endif
+
+ /* Write the RRS segments. */
+ write_rrs_text ();
+ write_rrs_data ();
+}
diff --git a/gnu/usr.bin/ld/rtld/Makefile b/gnu/usr.bin/ld/rtld/Makefile
new file mode 100644
index 0000000..ac6c892
--- /dev/null
+++ b/gnu/usr.bin/ld/rtld/Makefile
@@ -0,0 +1,27 @@
+# $Id: Makefile,v 1.11 1994/08/26 19:11:14 wollman Exp $
+
+PROG= ld.so
+SRCS= mdprologue.S rtld.c malloc.c shlib.c etc.c md.c
+NOMAN= noman
+LDDIR?= $(.CURDIR)/..
+#PICFLAG=-pic
+PICFLAG=-fpic
+CFLAGS+=-I$(LDDIR) -I$(.CURDIR) -I$(LDDIR)/$(MACHINE) $(PICFLAG) -DRTLD
+LDFLAGS+=-Bshareable -Bsymbolic -assert nosymbolic
+ASFLAGS+=-k
+DPADD+= ${LIBC:S/c.a/c_pic.a/} ${LIBC:S/c.a/gcc_pic.a/}
+LDADD+= -lc_pic -lgcc_pic
+BINDIR= /usr/libexec
+INSTALLFLAGS+= -fschg
+
+.SUFFIXES: .S
+
+.PATH: $(LDDIR) $(LDDIR)/$(MACHINE)
+
+$(PROG): ${OBJS} ${DPADD}
+ $(LD) -o $(PROG) $(LDFLAGS) $(OBJS) $(LDADD)
+
+.S.o:
+ ${CPP} ${.IMPSRC} | ${AS} ${ASFLAGS} -o ${.TARGET} -
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/ld/rtld/malloc.c b/gnu/usr.bin/ld/rtld/malloc.c
new file mode 100644
index 0000000..b5c54f8
--- /dev/null
+++ b/gnu/usr.bin/ld/rtld/malloc.c
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)malloc.c 5.11 (Berkeley) 2/23/91";*/
+static char *rcsid = "$Id: malloc.c,v 1.1 1994/02/13 20:44:09 jkh Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * malloc.c (Caltech) 2/21/82
+ * Chris Kingsley, kingsley@cit-20.
+ *
+ * This is a very fast storage allocator. It allocates blocks of a small
+ * number of different sizes, and keeps free lists of each size. Blocks that
+ * don't exactly fit are passed up to the next larger size. In this
+ * implementation, the available sizes are 2^n-4 (or 2^n-10) bytes long.
+ * This is designed for use in a virtual memory environment.
+ */
+
+#include <sys/types.h>
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#ifndef BSD
+#define MAP_COPY MAP_PRIVATE
+#define MAP_FILE 0
+#define MAP_ANON 0
+#endif
+
+#ifndef BSD /* Need do better than this */
+#define NEED_DEV_ZERO 1
+#endif
+
+#define NULL 0
+
+static void morecore();
+static int findbucket();
+
+/*
+ * Pre-allocate mmap'ed pages
+ */
+#define getpagesize() NBPG
+#define NPOOLPAGES (32*1024/NBPG)
+static caddr_t pagepool_start, pagepool_end;
+static int morepages();
+
+/*
+ * The overhead on a block is at least 4 bytes. When free, this space
+ * contains a pointer to the next free block, and the bottom two bits must
+ * be zero. When in use, the first byte is set to MAGIC, and the second
+ * byte is the size index. The remaining bytes are for alignment.
+ * If range checking is enabled then a second word holds the size of the
+ * requested block, less 1, rounded up to a multiple of sizeof(RMAGIC).
+ * The order of elements is critical: ov_magic must overlay the low order
+ * bits of ov_next, and ov_magic can not be a valid ov_next bit pattern.
+ */
+union overhead {
+ union overhead *ov_next; /* when free */
+ struct {
+ u_char ovu_magic; /* magic number */
+ u_char ovu_index; /* bucket # */
+#ifdef RCHECK
+ u_short ovu_rmagic; /* range magic number */
+ u_int ovu_size; /* actual block size */
+#endif
+ } ovu;
+#define ov_magic ovu.ovu_magic
+#define ov_index ovu.ovu_index
+#define ov_rmagic ovu.ovu_rmagic
+#define ov_size ovu.ovu_size
+};
+
+#define MAGIC 0xef /* magic # on accounting info */
+#define RMAGIC 0x5555 /* magic # on range info */
+
+#ifdef RCHECK
+#define RSLOP sizeof (u_short)
+#else
+#define RSLOP 0
+#endif
+
+/*
+ * nextf[i] is the pointer to the next free block of size 2^(i+3). The
+ * smallest allocatable block is 8 bytes. The overhead information
+ * precedes the data area returned to the user.
+ */
+#define NBUCKETS 30
+static union overhead *nextf[NBUCKETS];
+extern char *sbrk();
+
+static int pagesz; /* page size */
+static int pagebucket; /* page size bucket */
+
+#ifdef MSTATS
+/*
+ * nmalloc[i] is the difference between the number of mallocs and frees
+ * for a given block size.
+ */
+static u_int nmalloc[NBUCKETS];
+#include <stdio.h>
+#endif
+
+#if defined(DEBUG) || defined(RCHECK)
+#define ASSERT(p) if (!(p)) botch("p")
+#include <stdio.h>
+static
+botch(s)
+ char *s;
+{
+ fprintf(stderr, "\r\nassertion botched: %s\r\n", s);
+ (void) fflush(stderr); /* just in case user buffered it */
+ abort();
+}
+#else
+#define ASSERT(p)
+#endif
+
+void *
+malloc(nbytes)
+ size_t nbytes;
+{
+ register union overhead *op;
+ register int bucket, n;
+ register unsigned amt;
+
+ /*
+ * First time malloc is called, setup page size and
+ * align break pointer so all data will be page aligned.
+ */
+ if (pagesz == 0) {
+ pagesz = n = getpagesize();
+ if (morepages(NPOOLPAGES) == 0)
+ return NULL;
+ op = (union overhead *)(pagepool_start);
+ n = n - sizeof (*op) - ((int)op & (n - 1));
+ if (n < 0)
+ n += pagesz;
+ if (n) {
+ pagepool_start += n;
+ }
+ bucket = 0;
+ amt = 8;
+ while (pagesz > amt) {
+ amt <<= 1;
+ bucket++;
+ }
+ pagebucket = bucket;
+ }
+ /*
+ * Convert amount of memory requested into closest block size
+ * stored in hash buckets which satisfies request.
+ * Account for space used per block for accounting.
+ */
+ if (nbytes <= (n = pagesz - sizeof (*op) - RSLOP)) {
+#ifndef RCHECK
+ amt = 8; /* size of first bucket */
+ bucket = 0;
+#else
+ amt = 16; /* size of first bucket */
+ bucket = 1;
+#endif
+ n = -(sizeof (*op) + RSLOP);
+ } else {
+ amt = pagesz;
+ bucket = pagebucket;
+ }
+ while (nbytes > amt + n) {
+ amt <<= 1;
+ if (amt == 0)
+ return (NULL);
+ bucket++;
+ }
+ /*
+ * If nothing in hash bucket right now,
+ * request more memory from the system.
+ */
+ if ((op = nextf[bucket]) == NULL) {
+ morecore(bucket);
+ if ((op = nextf[bucket]) == NULL)
+ return (NULL);
+ }
+ /* remove from linked list */
+ nextf[bucket] = op->ov_next;
+ op->ov_magic = MAGIC;
+ op->ov_index = bucket;
+#ifdef MSTATS
+ nmalloc[bucket]++;
+#endif
+#ifdef RCHECK
+ /*
+ * Record allocated size of block and
+ * bound space with magic numbers.
+ */
+ op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
+ op->ov_rmagic = RMAGIC;
+ *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
+#endif
+ return ((char *)(op + 1));
+}
+
+/*
+ * Allocate more memory to the indicated bucket.
+ */
+static void
+morecore(bucket)
+ int bucket;
+{
+ register union overhead *op;
+ register int sz; /* size of desired block */
+ int amt; /* amount to allocate */
+ int nblks; /* how many blocks we get */
+
+ /*
+ * sbrk_size <= 0 only for big, FLUFFY, requests (about
+ * 2^30 bytes on a VAX, I think) or for a negative arg.
+ */
+ sz = 1 << (bucket + 3);
+#ifdef DEBUG
+ ASSERT(sz > 0);
+#else
+ if (sz <= 0)
+ return;
+#endif
+ if (sz < pagesz) {
+ amt = pagesz;
+ nblks = amt / sz;
+ } else {
+ amt = sz + pagesz;
+ nblks = 1;
+ }
+ if (amt > pagepool_end - pagepool_start)
+ if (morepages(amt/pagesz + NPOOLPAGES) == 0)
+ return;
+ op = (union overhead *)pagepool_start;
+ pagepool_start += amt;
+
+ /*
+ * Add new memory allocated to that on
+ * free list for this hash bucket.
+ */
+ nextf[bucket] = op;
+ while (--nblks > 0) {
+ op->ov_next = (union overhead *)((caddr_t)op + sz);
+ op = (union overhead *)((caddr_t)op + sz);
+ }
+}
+
+void
+free(cp)
+ void *cp;
+{
+ register int size;
+ register union overhead *op;
+
+ if (cp == NULL)
+ return;
+ op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
+#ifdef DEBUG
+ ASSERT(op->ov_magic == MAGIC); /* make sure it was in use */
+#else
+ if (op->ov_magic != MAGIC)
+ return; /* sanity */
+#endif
+#ifdef RCHECK
+ ASSERT(op->ov_rmagic == RMAGIC);
+ ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC);
+#endif
+ size = op->ov_index;
+ ASSERT(size < NBUCKETS);
+ op->ov_next = nextf[size]; /* also clobbers ov_magic */
+ nextf[size] = op;
+#ifdef MSTATS
+ nmalloc[size]--;
+#endif
+}
+
+/*
+ * When a program attempts "storage compaction" as mentioned in the
+ * old malloc man page, it realloc's an already freed block. Usually
+ * this is the last block it freed; occasionally it might be farther
+ * back. We have to search all the free lists for the block in order
+ * to determine its bucket: 1st we make one pass thru the lists
+ * checking only the first block in each; if that fails we search
+ * ``realloc_srchlen'' blocks in each list for a match (the variable
+ * is extern so the caller can modify it). If that fails we just copy
+ * however many bytes was given to realloc() and hope it's not huge.
+ */
+int realloc_srchlen = 4; /* 4 should be plenty, -1 =>'s whole list */
+
+void *
+realloc(cp, nbytes)
+ void *cp;
+ size_t nbytes;
+{
+ register u_int onb;
+ register int i;
+ union overhead *op;
+ char *res;
+ int was_alloced = 0;
+
+ if (cp == NULL)
+ return (malloc(nbytes));
+ op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
+ if (op->ov_magic == MAGIC) {
+ was_alloced++;
+ i = op->ov_index;
+ } else {
+ /*
+ * Already free, doing "compaction".
+ *
+ * Search for the old block of memory on the
+ * free list. First, check the most common
+ * case (last element free'd), then (this failing)
+ * the last ``realloc_srchlen'' items free'd.
+ * If all lookups fail, then assume the size of
+ * the memory block being realloc'd is the
+ * largest possible (so that all "nbytes" of new
+ * memory are copied into). Note that this could cause
+ * a memory fault if the old area was tiny, and the moon
+ * is gibbous. However, that is very unlikely.
+ */
+ if ((i = findbucket(op, 1)) < 0 &&
+ (i = findbucket(op, realloc_srchlen)) < 0)
+ i = NBUCKETS;
+ }
+ onb = 1 << (i + 3);
+ if (onb < pagesz)
+ onb -= sizeof (*op) + RSLOP;
+ else
+ onb += pagesz - sizeof (*op) - RSLOP;
+ /* avoid the copy if same size block */
+ if (was_alloced) {
+ if (i) {
+ i = 1 << (i + 2);
+ if (i < pagesz)
+ i -= sizeof (*op) + RSLOP;
+ else
+ i += pagesz - sizeof (*op) - RSLOP;
+ }
+ if (nbytes <= onb && nbytes > i) {
+#ifdef RCHECK
+ op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
+ *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
+#endif
+ return(cp);
+ } else
+ free(cp);
+ }
+ if ((res = malloc(nbytes)) == NULL)
+ return (NULL);
+ if (cp != res) /* common optimization if "compacting" */
+ bcopy(cp, res, (nbytes < onb) ? nbytes : onb);
+ return (res);
+}
+
+/*
+ * Search ``srchlen'' elements of each free list for a block whose
+ * header starts at ``freep''. If srchlen is -1 search the whole list.
+ * Return bucket number, or -1 if not found.
+ */
+static
+findbucket(freep, srchlen)
+ union overhead *freep;
+ int srchlen;
+{
+ register union overhead *p;
+ register int i, j;
+
+ for (i = 0; i < NBUCKETS; i++) {
+ j = 0;
+ for (p = nextf[i]; p && j != srchlen; p = p->ov_next) {
+ if (p == freep)
+ return (i);
+ j++;
+ }
+ }
+ return (-1);
+}
+
+#ifdef MSTATS
+/*
+ * mstats - print out statistics about malloc
+ *
+ * Prints two lines of numbers, one showing the length of the free list
+ * for each size category, the second showing the number of mallocs -
+ * frees for each size category.
+ */
+mstats(s)
+ char *s;
+{
+ register int i, j;
+ register union overhead *p;
+ int totfree = 0,
+ totused = 0;
+
+ fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s);
+ for (i = 0; i < NBUCKETS; i++) {
+ for (j = 0, p = nextf[i]; p; p = p->ov_next, j++)
+ ;
+ fprintf(stderr, " %d", j);
+ totfree += j * (1 << (i + 3));
+ }
+ fprintf(stderr, "\nused:\t");
+ for (i = 0; i < NBUCKETS; i++) {
+ fprintf(stderr, " %d", nmalloc[i]);
+ totused += nmalloc[i] * (1 << (i + 3));
+ }
+ fprintf(stderr, "\n\tTotal in use: %d, total free: %d\n",
+ totused, totfree);
+}
+#endif
+
+
+static int
+morepages(n)
+int n;
+{
+ int fd = -1;
+ int offset;
+
+#ifdef NEED_DEV_ZERO
+ fd = open("/dev/zero", O_RDWR, 0);
+ if (fd == -1)
+ perror("/dev/zero");
+#endif
+
+ if (pagepool_end - pagepool_start > pagesz) {
+ caddr_t addr = (caddr_t)
+ (((int)pagepool_start + pagesz - 1) & ~(pagesz - 1));
+ if (munmap(addr, pagepool_end - addr) != 0)
+ warn("morepages: munmap %p", addr);
+ }
+
+ offset = (int)pagepool_start - ((int)pagepool_start & ~(pagesz - 1));
+
+ if ((pagepool_start = mmap(0, n * pagesz,
+ PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_COPY, fd, 0)) == (caddr_t)-1) {
+ xprintf("Cannot map anonymous memory");
+ return 0;
+ }
+ pagepool_end = pagepool_start + n * pagesz;
+ pagepool_start += offset;
+
+#ifdef NEED_DEV_ZERO
+ close(fd);
+#endif
+ return n;
+}
diff --git a/gnu/usr.bin/ld/rtld/md-prologue.c b/gnu/usr.bin/ld/rtld/md-prologue.c
new file mode 100644
index 0000000..dae455e
--- /dev/null
+++ b/gnu/usr.bin/ld/rtld/md-prologue.c
@@ -0,0 +1,39 @@
+/*
+ * rtld entry pseudo code - turn into assembler and tweak it
+ */
+
+#include <sys/types.h>
+#include <sys/types.h>
+#include <a.out.h>
+#include "link.h"
+#include "md.h"
+
+extern long _GOT_[];
+extern void (*rtld)();
+extern void (*binder())();
+
+void
+rtld_entry(version, crtp)
+int version;
+struct crt *crtp;
+{
+ register struct link_dynamic *dp;
+ register void (*f)();
+
+ /* __DYNAMIC is first entry in GOT */
+ dp = (struct link_dynamic *) (_GOT_[0]+crtp->crt_ba);
+
+ f = (void (*)())((long)rtld + crtp->crt_ba);
+ (*f)(version, crtp, dp);
+}
+
+void
+binder_entry()
+{
+ extern int PC;
+ struct jmpslot *sp;
+ void (*func)();
+
+ func = binder(PC, sp->reloc_index & 0x003fffff);
+ (*func)();
+}
diff --git a/gnu/usr.bin/ld/rtld/rtld.c b/gnu/usr.bin/ld/rtld/rtld.c
new file mode 100644
index 0000000..96dd0fe
--- /dev/null
+++ b/gnu/usr.bin/ld/rtld/rtld.c
@@ -0,0 +1,1261 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: rtld.c,v 1.16 1994/04/13 20:52:40 ats Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#ifndef BSD
+#define MAP_COPY MAP_PRIVATE
+#define MAP_ANON 0
+#endif
+#include <err.h>
+#include <fcntl.h>
+#include <a.out.h>
+#include <stab.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "ld.h"
+
+#ifndef BSD /* Need do better than this */
+#define NEED_DEV_ZERO 1
+#endif
+
+/*
+ * Loader private data, hung off <so_map>->som_spd
+ */
+struct somap_private {
+ int spd_version;
+ struct so_map *spd_parent;
+ int spd_refcount;
+ int spd_flags;
+#define RTLD_MAIN 1
+#define RTLD_RTLD 2
+#define RTLD_DL 4
+
+#ifdef SUN_COMPAT
+ long spd_offset; /* Correction for Sun main programs */
+#endif
+};
+
+#define LM_PRIVATE(smp) ((struct somap_private *)(smp)->som_spd)
+
+#ifdef SUN_COMPAT
+#define LM_OFFSET(smp) (LM_PRIVATE(smp)->spd_offset)
+#else
+#define LM_OFFSET(smp) (0)
+#endif
+
+/* Base address for section_dispatch_table entries */
+#define LM_LDBASE(smp) (smp->som_addr + LM_OFFSET(smp))
+
+/* Start of text segment */
+#define LM_TXTADDR(smp) (smp->som_addr == (caddr_t)0 ? PAGSIZ : 0)
+
+/* Start of run-time relocation_info */
+#define LM_REL(smp) ((struct relocation_info *) \
+ (smp->som_addr + LM_OFFSET(smp) + LD_REL((smp)->som_dynamic)))
+
+/* Start of symbols */
+#define LM_SYMBOL(smp, i) ((struct nzlist *) \
+ (smp->som_addr + LM_OFFSET(smp) + LD_SYMBOL((smp)->som_dynamic) + \
+ i * (LD_VERSION_NZLIST_P(smp->som_dynamic->d_version) ? \
+ sizeof(struct nzlist) : sizeof(struct nlist))))
+
+/* Start of hash table */
+#define LM_HASH(smp) ((struct rrs_hash *) \
+ ((smp)->som_addr + LM_OFFSET(smp) + LD_HASH((smp)->som_dynamic)))
+
+/* Start of strings */
+#define LM_STRINGS(smp) ((char *) \
+ ((smp)->som_addr + LM_OFFSET(smp) + LD_STRINGS((smp)->som_dynamic)))
+
+/* End of text */
+#define LM_ETEXT(smp) ((char *) \
+ ((smp)->som_addr + LM_TXTADDR(smp) + LD_TEXTSZ((smp)->som_dynamic)))
+
+/* PLT is in data segment, so don't use LM_OFFSET here */
+#define LM_PLT(smp) ((jmpslot_t *) \
+ ((smp)->som_addr + LD_PLT((smp)->som_dynamic)))
+
+/* Parent of link map */
+#define LM_PARENT(smp) (LM_PRIVATE(smp)->spd_parent)
+
+char **environ;
+char *__progname;
+int errno;
+
+static uid_t uid, euid;
+static gid_t gid, egid;
+static int careful;
+static char __main_progname[] = "main";
+static char *main_progname = __main_progname;
+static char us[] = "/usr/libexec/ld.so";
+
+struct so_map *link_map_head, *main_map;
+struct so_map **link_map_tail = &link_map_head;
+struct rt_symbol *rt_symbol_head;
+
+static void *__dlopen __P((char *, int));
+static int __dlclose __P((void *));
+static void *__dlsym __P((void *, char *));
+static int __dlctl __P((void *, int, void *));
+
+static struct ld_entry ld_entry = {
+ __dlopen, __dlclose, __dlsym, __dlctl
+};
+
+ void xprintf __P((char *, ...));
+static void load_objects __P(( struct crt_ldso *,
+ struct _dynamic *));
+static struct so_map *map_object __P((struct sod *, struct so_map *));
+static struct so_map *alloc_link_map __P(( char *, struct sod *,
+ struct so_map *, caddr_t,
+ struct _dynamic *));
+static inline void check_text_reloc __P(( struct relocation_info *,
+ struct so_map *,
+ caddr_t));
+static void reloc_map __P((struct so_map *));
+static void reloc_copy __P((struct so_map *));
+static void init_map __P((struct so_map *, char *));
+static char *rtfindlib __P((char *, int, int, int *));
+void binder_entry __P((void));
+long binder __P((jmpslot_t *));
+static struct nzlist *lookup __P((char *, struct so_map **, int));
+static inline struct rt_symbol *lookup_rts __P((char *));
+static struct rt_symbol *enter_rts __P((char *, long, int, caddr_t,
+ long, struct so_map *));
+
+static inline int
+strcmp (register const char *s1, register const char *s2)
+{
+ while (*s1 == *s2++)
+ if (*s1++ == 0)
+ return (0);
+ return (*(unsigned char *)s1 - *(unsigned char *)--s2);
+}
+
+#include "md-static-funcs.c"
+
+/*
+ * Called from assembler stub that has set up crtp (passed from crt0)
+ * and dp (our __DYNAMIC).
+ */
+int
+rtld(version, crtp, dp)
+int version;
+struct crt_ldso *crtp;
+struct _dynamic *dp;
+{
+ int n;
+ int nreloc; /* # of ld.so relocations */
+ struct relocation_info *reloc;
+ struct so_debug *ddp;
+ struct so_map *smp;
+
+ /* Check version */
+ if ( version != CRT_VERSION_BSD_2 &&
+ version != CRT_VERSION_BSD_3 &&
+ version != CRT_VERSION_SUN)
+ return -1;
+
+ /* Fixup __DYNAMIC structure */
+ (long)dp->d_un.d_sdt += crtp->crt_ba;
+
+ /* Divide by hand to avoid possible use of library division routine */
+ for ( nreloc = 0, n = LD_RELSZ(dp);
+ n > 0;
+ n -= sizeof(struct relocation_info) ) nreloc++;
+
+
+ /* Relocate ourselves */
+ for ( reloc = (struct relocation_info *)(LD_REL(dp) + crtp->crt_ba);
+ nreloc;
+ nreloc--, reloc++) {
+
+ register long addr = reloc->r_address + crtp->crt_ba;
+
+ md_relocate_simple(reloc, crtp->crt_ba, addr);
+ }
+
+ __progname = "ld.so";
+ if (version >= CRT_VERSION_BSD_3)
+ main_progname = crtp->crt_prog;
+
+ /* Setup out (private) environ variable */
+ environ = crtp->crt_ep;
+
+ /* Get user and group identifiers */
+ uid = getuid(); euid = geteuid();
+ gid = getgid(); egid = getegid();
+
+ careful = (uid != euid) || (gid != egid);
+
+ if (careful) {
+ unsetenv("LD_LIBRARY_PATH");
+ unsetenv("LD_PRELOAD");
+ unsetenv("LD_RUN_PATH"); /* In case we ever implement this */
+ }
+
+ /* Setup directory search */
+ add_search_path(getenv("LD_RUN_PATH"));
+ add_search_path(getenv("LD_LIBRARY_PATH"));
+ if (getenv("LD_NOSTD_PATH") == NULL)
+ std_search_path();
+
+ /* Load required objects into the process address space */
+ load_objects(crtp, dp);
+
+ /* Relocate all loaded objects according to their RRS segments */
+ for (smp = link_map_head; smp; smp = smp->som_next) {
+ if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD)
+ continue;
+ reloc_map(smp);
+ }
+
+ /* Copy any relocated initialized data. */
+ for (smp = link_map_head; smp; smp = smp->som_next) {
+ if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD)
+ continue;
+ reloc_copy(smp);
+ }
+
+ /* Call any object initialization routines. */
+ for (smp = link_map_head; smp; smp = smp->som_next) {
+ if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD)
+ continue;
+ init_map(smp, ".init");
+ }
+
+ /* Fill in some field in main's __DYNAMIC structure */
+ crtp->crt_dp->d_entry = &ld_entry;
+ crtp->crt_dp->d_un.d_sdt->sdt_loaded = link_map_head->som_next;
+
+ ddp = crtp->crt_dp->d_debug;
+ ddp->dd_cc = rt_symbol_head;
+ if (ddp->dd_in_debugger) {
+ caddr_t addr = (caddr_t)((long)crtp->crt_bp & (~(PAGSIZ - 1)));
+
+ /* Set breakpoint for the benefit of debuggers */
+ if (mprotect(addr, PAGSIZ,
+ PROT_READ|PROT_WRITE|PROT_EXEC) == -1) {
+ err(1, "Cannot set breakpoint (%s)", main_progname);
+ }
+ md_set_breakpoint((long)crtp->crt_bp, (long *)&ddp->dd_bpt_shadow);
+ if (mprotect(addr, PAGSIZ, PROT_READ|PROT_EXEC) == -1) {
+ err(1, "Cannot re-protect breakpoint (%s)",
+ main_progname);
+ }
+
+ ddp->dd_bpt_addr = crtp->crt_bp;
+ if (link_map_head)
+ ddp->dd_sym_loaded = 1;
+ }
+
+ /* Close our file descriptor */
+ (void)close(crtp->crt_ldfd);
+ return 0;
+}
+
+
+static void
+load_objects(crtp, dp)
+struct crt_ldso *crtp;
+struct _dynamic *dp;
+{
+ struct so_map *smp;
+ int tracing = (int)getenv("LD_TRACE_LOADED_OBJECTS");
+
+ /* Handle LD_PRELOAD's here */
+
+ /* Make an entry for the main program */
+ smp = alloc_link_map(main_progname, (struct sod *)0, (struct so_map *)0,
+ (caddr_t)0, crtp->crt_dp);
+ LM_PRIVATE(smp)->spd_refcount++;
+ LM_PRIVATE(smp)->spd_flags |= RTLD_MAIN;
+
+ /* Make an entry for ourselves */
+ smp = alloc_link_map(us, (struct sod *)0, (struct so_map *)0,
+ (caddr_t)crtp->crt_ba, dp);
+ LM_PRIVATE(smp)->spd_refcount++;
+ LM_PRIVATE(smp)->spd_flags |= RTLD_RTLD;
+
+ for (smp = link_map_head; smp; smp = smp->som_next) {
+ struct sod *sodp;
+ long next = 0;
+
+ if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD)
+ continue;
+
+ if (smp->som_dynamic)
+ next = LD_NEED(smp->som_dynamic);
+
+ while (next) {
+ struct so_map *newmap;
+
+ sodp = (struct sod *)(LM_LDBASE(smp) + next);
+ if ((newmap = map_object(sodp, smp)) == NULL) {
+ if (!tracing) {
+ char *name = (char *)
+ (sodp->sod_name + LM_LDBASE(smp));
+ char *fmt = sodp->sod_library ?
+ "%s: lib%s.so.%d.%d" :
+ "%s: %s";
+ err(1, fmt, main_progname, name,
+ sodp->sod_major,
+ sodp->sod_minor);
+ }
+ newmap = alloc_link_map(NULL, sodp, smp, 0, 0);
+ }
+ LM_PRIVATE(newmap)->spd_refcount++;
+ next = sodp->sod_next;
+ }
+ }
+
+ if (! tracing)
+ return;
+
+ for (smp = link_map_head; smp; smp = smp->som_next) {
+ struct sod *sodp;
+ char *name, *path;
+
+ if ((sodp = smp->som_sod) == NULL)
+ continue;
+ name = sodp->sod_name + LM_LDBASE(LM_PARENT(smp));
+
+ if ((path = smp->som_path) == NULL)
+ path = "not found";
+
+ if (sodp->sod_library)
+ printf("\t-l%s.%d => %s (%p)\n", name,
+ sodp->sod_major, path, smp->som_addr);
+ else
+ printf("\t%s => %s (%p)\n", name, path, smp->som_addr);
+ }
+
+ exit(0);
+}
+
+/*
+ * Allocate a new link map for shared object NAME loaded at ADDR as a
+ * result of the presence of link object LOP in the link map PARENT.
+ */
+ static struct so_map *
+alloc_link_map(path, sodp, parent, addr, dp)
+ char *path;
+ struct sod *sodp;
+ struct so_map *parent;
+ caddr_t addr;
+ struct _dynamic *dp;
+{
+ struct so_map *smp;
+ struct somap_private *smpp;
+
+ smpp = (struct somap_private *)xmalloc(sizeof(struct somap_private));
+ smp = (struct so_map *)xmalloc(sizeof(struct so_map));
+ smp->som_next = NULL;
+ *link_map_tail = smp;
+ link_map_tail = &smp->som_next;
+
+ smp->som_addr = addr;
+ smp->som_path = strdup(path);
+ smp->som_sod = sodp;
+ smp->som_dynamic = dp;
+ smp->som_spd = (caddr_t)smpp;
+
+/*XXX*/ if (addr == 0) main_map = smp;
+
+ smpp->spd_refcount = 0;
+ smpp->spd_flags = 0;
+ smpp->spd_parent = parent;
+
+#ifdef SUN_COMPAT
+ smpp->spd_offset =
+ (addr==0 && dp && dp->d_version==LD_VERSION_SUN) ? PAGSIZ : 0;
+#endif
+ return smp;
+}
+
+/*
+ * Map object identified by link object LOP which was found
+ * in link map LMP.
+ */
+ static struct so_map *
+map_object(sodp, smp)
+ struct sod *sodp;
+ struct so_map *smp;
+{
+ struct _dynamic *dp;
+ char *path, *name = (char *)(sodp->sod_name + LM_LDBASE(smp));
+ int fd;
+ caddr_t addr;
+ struct exec hdr;
+ int usehints = 0;
+ struct so_map *p;
+
+ if (sodp->sod_library) {
+ usehints = 1;
+again:
+ path = rtfindlib(name, sodp->sod_major,
+ sodp->sod_minor, &usehints);
+ if (path == NULL) {
+ errno = ENOENT;
+ return NULL;
+ }
+ } else {
+ if (careful && *name != '/') {
+ errno = EACCES;
+ return NULL;
+ }
+ path = name;
+ }
+
+ /* Check if already loaded */
+ for (p = link_map_head; p; p = p->som_next)
+ if (p->som_path && strcmp(p->som_path, path) == 0)
+ break;
+
+ if (p != NULL)
+ return p;
+
+ if ((fd = open(path, O_RDONLY, 0)) == -1) {
+ if (usehints) {
+ usehints = 0;
+ goto again;
+ }
+ return NULL;
+ }
+
+ if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
+ (void)close(fd);
+ /*errno = x;*/
+ return NULL;
+ }
+
+ if (N_BADMAG(hdr)) {
+ (void)close(fd);
+ errno = EFTYPE;
+ return NULL;
+ }
+
+ if ((addr = mmap(0, hdr.a_text + hdr.a_data,
+ PROT_READ|PROT_EXEC,
+ MAP_FILE|MAP_COPY, fd, 0)) == (caddr_t)-1) {
+ (void)close(fd);
+ return NULL;
+ }
+
+#if 0
+ if (mmap(addr + hdr.a_text, hdr.a_data,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FILE|MAP_FIXED|MAP_COPY,
+ fd, hdr.a_text) == (caddr_t)-1) {
+ (void)close(fd);
+ return NULL;
+ }
+#endif
+ if (mprotect(addr + hdr.a_text, hdr.a_data,
+ PROT_READ|PROT_WRITE|PROT_EXEC) != 0) {
+ (void)close(fd);
+ return NULL;
+ }
+
+ (void)close(fd);
+
+ fd = -1;
+#ifdef NEED_DEV_ZERO
+ if ((fd = open("/dev/zero", O_RDWR, 0)) == -1)
+ warn("open: %s", "/dev/zero");
+#endif
+ if (hdr.a_bss && mmap(addr + hdr.a_text + hdr.a_data, hdr.a_bss,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_ANON|MAP_FIXED|MAP_COPY,
+ fd, hdr.a_text + hdr.a_data) == (caddr_t)-1)
+ return NULL;
+
+#ifdef NEED_DEV_ZERO
+ close(fd);
+#endif
+
+ /* Assume _DYNAMIC is the first data item */
+ dp = (struct _dynamic *)(addr+hdr.a_text);
+
+ /* Fixup __DYNAMIC structure */
+ (long)dp->d_un.d_sdt += (long)addr;
+
+ return alloc_link_map(path, sodp, smp, addr, dp);
+}
+
+static inline void
+check_text_reloc(r, smp, addr)
+struct relocation_info *r;
+struct so_map *smp;
+caddr_t addr;
+{
+ char *sym;
+
+ if (addr >= LM_ETEXT(smp))
+ return;
+
+ if (RELOC_EXTERN_P(r))
+ sym = LM_STRINGS(smp) +
+ LM_SYMBOL(smp, RELOC_SYMBOL(r))->nz_strx;
+ else
+ sym = "";
+
+ if (getenv("LD_WARN_NON_PURE_CODE") != NULL)
+ fprintf(stderr,
+ "ld.so: warning: non pure code in %s at %x (%s)\n",
+ smp->som_path, r->r_address, sym);
+
+ if (smp->som_write == 0 &&
+ mprotect(smp->som_addr + LM_TXTADDR(smp),
+ LD_TEXTSZ(smp->som_dynamic),
+ PROT_READ|PROT_WRITE|PROT_EXEC) == -1) {
+
+ err(1, "Cannot enable writes to %s:%s",
+ main_progname, smp->som_path);
+ }
+
+ smp->som_write = 1;
+}
+
+static void
+reloc_map(smp)
+ struct so_map *smp;
+{
+ struct _dynamic *dp = smp->som_dynamic;
+ struct relocation_info *r = LM_REL(smp);
+ struct relocation_info *rend = r + LD_RELSZ(dp)/sizeof(*r);
+ long symbolbase = (long)LM_SYMBOL(smp, 0);
+ char *stringbase = LM_STRINGS(smp);
+ int symsize = LD_VERSION_NZLIST_P(dp->d_version) ?
+ sizeof(struct nzlist) :
+ sizeof(struct nlist);
+
+ if (LD_PLTSZ(dp))
+ md_fix_jmpslot(LM_PLT(smp),
+ (long)LM_PLT(smp), (long)binder_entry);
+
+ for (; r < rend; r++) {
+ char *sym;
+ caddr_t addr = smp->som_addr + r->r_address;
+
+ check_text_reloc(r, smp, addr);
+
+ if (RELOC_EXTERN_P(r)) {
+ struct so_map *src_map = NULL;
+ struct nzlist *p, *np;
+ long relocation = md_get_addend(r, addr);
+
+ if (RELOC_LAZY_P(r))
+ continue;
+
+ p = (struct nzlist *)
+ (symbolbase + symsize * RELOC_SYMBOL(r));
+
+ if (p->nz_type == (N_SETV + N_EXT))
+ src_map = smp;
+
+ sym = stringbase + p->nz_strx;
+
+ np = lookup(sym, &src_map, 0/*XXX-jumpslots!*/);
+ if (np == NULL)
+ errx(1, "Undefined symbol \"%s\" in %s:%s\n",
+ sym, main_progname, smp->som_path);
+
+ /*
+ * Found symbol definition.
+ * If it's in a link map, adjust value
+ * according to the load address of that map.
+ * Otherwise it's a run-time allocated common
+ * whose value is already up-to-date.
+ */
+ relocation += np->nz_value;
+ if (src_map)
+ relocation += (long)src_map->som_addr;
+
+ if (RELOC_PCREL_P(r))
+ relocation -= (long)smp->som_addr;
+
+ if (RELOC_COPY_P(r) && src_map) {
+ (void)enter_rts(sym,
+ (long)addr,
+ N_DATA + N_EXT,
+ src_map->som_addr + np->nz_value,
+ np->nz_size, src_map);
+ continue;
+ }
+ md_relocate(r, relocation, addr, 0);
+
+ } else {
+ md_relocate(r,
+#ifdef SUN_COMPAT
+ md_get_rt_segment_addend(r, addr)
+#else
+ md_get_addend(r, addr)
+#endif
+ + (long)smp->som_addr, addr, 0);
+ }
+
+ }
+
+ if (smp->som_write) {
+ if (mprotect(smp->som_addr + LM_TXTADDR(smp),
+ LD_TEXTSZ(smp->som_dynamic),
+ PROT_READ|PROT_EXEC) == -1) {
+
+ err(1, "Cannot disable writes to %s:%s\n",
+ main_progname, smp->som_path);
+ }
+ smp->som_write = 0;
+ }
+}
+
+static void
+reloc_copy(smp)
+ struct so_map *smp;
+{
+ struct rt_symbol *rtsp;
+
+ for (rtsp = rt_symbol_head; rtsp; rtsp = rtsp->rt_next)
+ if ((rtsp->rt_smp == NULL || rtsp->rt_smp == smp) &&
+ rtsp->rt_sp->nz_type == N_DATA + N_EXT) {
+ bcopy(rtsp->rt_srcaddr, (caddr_t)rtsp->rt_sp->nz_value,
+ rtsp->rt_sp->nz_size);
+ }
+}
+
+static void
+init_map(smp, sym)
+ struct so_map *smp;
+ char *sym;
+{
+ struct so_map *src_map = smp;
+ struct nzlist *np;
+
+ np = lookup(sym, &src_map, 1);
+ if (np)
+ (*(void (*)())(src_map->som_addr + np->nz_value))();
+}
+
+/*
+ * Run-time common symbol table.
+ */
+
+#define RTC_TABSIZE 57
+static struct rt_symbol *rt_symtab[RTC_TABSIZE];
+
+/*
+ * Compute hash value for run-time symbol table
+ */
+ static inline int
+hash_string(key)
+ char *key;
+{
+ register char *cp;
+ register int k;
+
+ cp = key;
+ k = 0;
+ while (*cp)
+ k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
+
+ return k;
+}
+
+/*
+ * Lookup KEY in the run-time common symbol table.
+ */
+
+ static inline struct rt_symbol *
+lookup_rts(key)
+ char *key;
+{
+ register int hashval;
+ register struct rt_symbol *rtsp;
+
+ /* Determine which bucket. */
+
+ hashval = hash_string(key) % RTC_TABSIZE;
+
+ /* Search the bucket. */
+
+ for (rtsp = rt_symtab[hashval]; rtsp; rtsp = rtsp->rt_link)
+ if (strcmp(key, rtsp->rt_sp->nz_name) == 0)
+ return rtsp;
+
+ return NULL;
+}
+
+ static struct rt_symbol *
+enter_rts(name, value, type, srcaddr, size, smp)
+ char *name;
+ long value;
+ int type;
+ caddr_t srcaddr;
+ long size;
+ struct so_map *smp;
+{
+ register int hashval;
+ register struct rt_symbol *rtsp, **rpp;
+
+ /* Determine which bucket */
+ hashval = hash_string(name) % RTC_TABSIZE;
+
+ /* Find end of bucket */
+ for (rpp = &rt_symtab[hashval]; *rpp; rpp = &(*rpp)->rt_link)
+ ;
+
+ /* Allocate new common symbol */
+ rtsp = (struct rt_symbol *)malloc(sizeof(struct rt_symbol));
+ rtsp->rt_sp = (struct nzlist *)malloc(sizeof(struct nzlist));
+ rtsp->rt_sp->nz_name = strdup(name);
+ rtsp->rt_sp->nz_value = value;
+ rtsp->rt_sp->nz_type = type;
+ rtsp->rt_sp->nz_size = size;
+ rtsp->rt_srcaddr = srcaddr;
+ rtsp->rt_smp = smp;
+ rtsp->rt_link = NULL;
+
+ /* Link onto linear list as well */
+ rtsp->rt_next = rt_symbol_head;
+ rt_symbol_head = rtsp;
+
+ *rpp = rtsp;
+
+ return rtsp;
+}
+
+
+/*
+ * Lookup NAME in the link maps. The link map producing a definition
+ * is returned in SRC_MAP. If SRC_MAP is not NULL on entry the search is
+ * confined to that map. If STRONG is set, the symbol returned must
+ * have a proper type (used by binder()).
+ */
+ static struct nzlist *
+lookup(name, src_map, strong)
+ char *name;
+ struct so_map **src_map; /* IN/OUT */
+ int strong;
+{
+ long common_size = 0;
+ struct so_map *smp;
+ struct rt_symbol *rtsp;
+
+ if ((rtsp = lookup_rts(name)) != NULL)
+ return rtsp->rt_sp;
+
+ /*
+ * Search all maps for a definition of NAME
+ */
+ for (smp = link_map_head; smp; smp = smp->som_next) {
+ int buckets = LD_BUCKETS(smp->som_dynamic);
+ long hashval;
+ struct rrs_hash *hp;
+ char *cp;
+ struct nzlist *np;
+
+ /* Some local caching */
+ long symbolbase;
+ struct rrs_hash *hashbase;
+ char *stringbase;
+ int symsize;
+
+ if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD)
+ continue;
+
+ if (*src_map && smp != *src_map)
+ continue;
+
+restart:
+ /*
+ * Compute bucket in which the symbol might be found.
+ */
+ for (hashval = 0, cp = name; *cp; cp++)
+ hashval = (hashval << 1) + *cp;
+
+ hashval = (hashval & 0x7fffffff) % buckets;
+
+ hashbase = LM_HASH(smp);
+ hp = hashbase + hashval;
+ if (hp->rh_symbolnum == -1)
+ /* Nothing in this bucket */
+ continue;
+
+ symbolbase = (long)LM_SYMBOL(smp, 0);
+ stringbase = LM_STRINGS(smp);
+ symsize = LD_VERSION_NZLIST_P(smp->som_dynamic->d_version)?
+ sizeof(struct nzlist) :
+ sizeof(struct nlist);
+ while (hp) {
+ np = (struct nzlist *)
+ (symbolbase + hp->rh_symbolnum * symsize);
+ cp = stringbase + np->nz_strx;
+ if (strcmp(cp, name) == 0)
+ break;
+ if (hp->rh_next == 0)
+ hp = NULL;
+ else
+ hp = hashbase + hp->rh_next;
+ }
+ if (hp == NULL)
+ /* Nothing in this bucket */
+ continue;
+
+ /*
+ * We have a symbol with the name we're looking for.
+ */
+ if (np->nz_type == N_INDR+N_EXT) {
+ /*
+ * Next symbol gives the aliased name. Restart
+ * search with new name and confine to this map.
+ */
+ name = stringbase + (++np)->nz_strx;
+ *src_map = smp;
+ goto restart;
+ }
+
+ if (np->nz_value == 0)
+ /* It's not a definition */
+ continue;
+
+ if (np->nz_type == N_UNDF+N_EXT && np->nz_value != 0) {
+ if (np->nz_other == AUX_FUNC) {
+ /* It's a weak function definition */
+ if (strong)
+ continue;
+ } else {
+ /* It's a common, note value and continue search */
+ if (common_size < np->nz_value)
+ common_size = np->nz_value;
+ continue;
+ }
+ }
+
+ *src_map = smp;
+ return np;
+ }
+
+ if (common_size == 0)
+ /* Not found */
+ return NULL;
+
+ /*
+ * It's a common, enter into run-time common symbol table.
+ */
+ rtsp = enter_rts(name, (long)calloc(1, common_size),
+ N_UNDF + N_EXT, 0, common_size, NULL);
+
+#if DEBUG
+xprintf("Allocating common: %s size %d at %#x\n", name, common_size, rtsp->rt_sp->nz_value);
+#endif
+
+ return rtsp->rt_sp;
+}
+
+
+/*
+ * This routine is called from the jumptable to resolve
+ * procedure calls to shared objects.
+ */
+ long
+binder(jsp)
+ jmpslot_t *jsp;
+{
+ struct so_map *smp, *src_map = NULL;
+ long addr;
+ char *sym;
+ struct nzlist *np;
+ int index;
+
+ /*
+ * Find the PLT map that contains JSP.
+ */
+ for (smp = link_map_head; smp; smp = smp->som_next) {
+ if (LM_PLT(smp) < jsp &&
+ jsp < LM_PLT(smp) + LD_PLTSZ(smp->som_dynamic)/sizeof(*jsp))
+ break;
+ }
+
+ if (smp == NULL)
+ errx(1, "Call to binder from unknown location: %#x\n", jsp);
+
+ index = jsp->reloc_index & JMPSLOT_RELOC_MASK;
+
+ /* Get the local symbol this jmpslot refers to */
+ sym = LM_STRINGS(smp) +
+ LM_SYMBOL(smp,RELOC_SYMBOL(&LM_REL(smp)[index]))->nz_strx;
+
+ np = lookup(sym, &src_map, 1);
+ if (np == NULL)
+ errx(1, "Undefined symbol \"%s\" called from %s:%s at %#x",
+ sym, main_progname, smp->som_path, jsp);
+
+ /* Fixup jmpslot so future calls transfer directly to target */
+ addr = np->nz_value;
+ if (src_map)
+ addr += (long)src_map->som_addr;
+
+ md_fix_jmpslot(jsp, (long)jsp, addr);
+
+#if DEBUG
+xprintf(" BINDER: %s located at = %#x in %s\n", sym, addr, src_map->som_path);
+#endif
+ return addr;
+}
+
+
+static struct hints_header *hheader;
+static struct hints_bucket *hbuckets;
+static char *hstrtab;
+
+#define HINTS_VALID (hheader != NULL && hheader != (struct hints_header *)-1)
+
+ static void
+maphints()
+{
+ caddr_t addr;
+ long msize;
+ int fd;
+
+ if ((fd = open(_PATH_LD_HINTS, O_RDONLY, 0)) == -1) {
+ hheader = (struct hints_header *)-1;
+ return;
+ }
+
+ msize = PAGSIZ;
+ addr = mmap(0, msize, PROT_READ, MAP_FILE|MAP_COPY, fd, 0);
+
+ if (addr == (caddr_t)-1) {
+ hheader = (struct hints_header *)-1;
+ return;
+ }
+
+ hheader = (struct hints_header *)addr;
+ if (HH_BADMAG(*hheader)) {
+ munmap(addr, msize);
+ hheader = (struct hints_header *)-1;
+ return;
+ }
+
+ if (hheader->hh_version != LD_HINTS_VERSION_1) {
+ munmap(addr, msize);
+ hheader = (struct hints_header *)-1;
+ return;
+ }
+
+ if (hheader->hh_ehints > msize) {
+ if (mmap(addr+msize, hheader->hh_ehints - msize,
+ PROT_READ, MAP_FILE|MAP_COPY|MAP_FIXED,
+ fd, msize) != (caddr_t)(addr+msize)) {
+
+ munmap((caddr_t)hheader, msize);
+ hheader = (struct hints_header *)-1;
+ return;
+ }
+ }
+ close(fd);
+
+ hbuckets = (struct hints_bucket *)(addr + hheader->hh_hashtab);
+ hstrtab = (char *)(addr + hheader->hh_strtab);
+}
+
+ int
+hinthash(cp, vmajor, vminor)
+ char *cp;
+ int vmajor, vminor;
+{
+ int k = 0;
+
+ while (*cp)
+ k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
+
+ k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff;
+ k = (((k << 1) + (k >> 14)) ^ (vminor*167)) & 0x3fff;
+
+ return k;
+}
+
+#undef major
+#undef minor
+
+ static char *
+findhint(name, major, minor, preferred_path)
+ char *name;
+ int major, minor;
+ char *preferred_path;
+{
+ struct hints_bucket *bp;
+
+ bp = hbuckets + (hinthash(name, major, minor) % hheader->hh_nbucket);
+
+ while (1) {
+ /* Sanity check */
+ if (bp->hi_namex >= hheader->hh_strtab_sz) {
+ fprintf(stderr, "Bad name index: %#x\n", bp->hi_namex);
+ break;
+ }
+ if (bp->hi_pathx >= hheader->hh_strtab_sz) {
+ fprintf(stderr, "Bad path index: %#x\n", bp->hi_pathx);
+ break;
+ }
+
+ if (strcmp(name, hstrtab + bp->hi_namex) == 0) {
+ /* It's `name', check version numbers */
+ if (bp->hi_major == major &&
+ (bp->hi_ndewey < 2 || bp->hi_minor == minor)) {
+ if (preferred_path == NULL ||
+ strcmp(preferred_path,
+ hstrtab + bp->hi_pathx) == 0) {
+ return hstrtab + bp->hi_pathx;
+ }
+ }
+ }
+
+ if (bp->hi_next == -1)
+ break;
+
+ /* Move on to next in bucket */
+ bp = &hbuckets[bp->hi_next];
+ }
+
+ /* No hints available for name */
+ return NULL;
+}
+
+ static char *
+rtfindlib(name, major, minor, usehints)
+ char *name;
+ int major, minor;
+ int *usehints;
+{
+ char *hint;
+ char *cp, *ld_path = getenv("LD_LIBRARY_PATH");
+
+ if (hheader == NULL)
+ maphints();
+
+ if (!HINTS_VALID || !(*usehints)) {
+ *usehints = 0;
+ return (char *)findshlib(name, &major, &minor, 0);
+ }
+
+ if (ld_path != NULL) {
+ /* Prefer paths from LD_LIBRARY_PATH */
+ while ((cp = strsep(&ld_path, ":")) != NULL) {
+
+ hint = findhint(name, major, minor, cp);
+ if (ld_path)
+ *(ld_path-1) = ':';
+ if (hint)
+ return hint;
+ }
+ /* Not found in hints, try directory search */
+ hint = (char *)findshlib(name, &major, &minor, 0);
+ if (hint)
+ return hint;
+ }
+
+ /* No LD_LIBRARY_PATH or lib not found in there; check default */
+ hint = findhint(name, major, minor, NULL);
+ if (hint)
+ return hint;
+
+ /* No hints available for name */
+ *usehints = 0;
+ return (char *)findshlib(name, &major, &minor, 0);
+}
+
+static struct somap_private dlmap_private = {
+ 0,
+ (struct so_map *)0,
+ 0,
+#ifdef SUN_COMPAT
+ 0,
+#endif
+};
+
+static struct so_map dlmap = {
+ (caddr_t)0,
+ "internal",
+ (struct so_map *)0,
+ (struct sod *)0,
+ (caddr_t)0,
+ (u_int)0,
+ (struct _dynamic *)0,
+ (caddr_t)&dlmap_private
+};
+static int dlerrno;
+
+ static void *
+__dlopen(name, mode)
+ char *name;
+ int mode;
+{
+ struct sod *sodp;
+ struct so_map *smp;
+
+ /*
+ * A NULL argument returns the current set of mapped objects.
+ */
+ if (name == NULL)
+ return link_map_head;
+
+ if ((sodp = (struct sod *)malloc(sizeof(struct sod))) == NULL) {
+ dlerrno = ENOMEM;
+ return NULL;
+ }
+
+ sodp->sod_name = (long)strdup(name);
+ sodp->sod_library = 0;
+ sodp->sod_major = sodp->sod_minor = 0;
+
+ if ((smp = map_object(sodp, &dlmap)) == NULL) {
+#ifdef DEBUG
+xprintf("%s: %s\n", name, strerror(errno));
+#endif
+ dlerrno = errno;
+ return NULL;
+ }
+ if (LM_PRIVATE(smp)->spd_refcount++ == 0) {
+ LM_PRIVATE(smp)->spd_flags |= RTLD_DL;
+ reloc_map(smp);
+ reloc_copy(smp);
+ init_map(smp, ".init");
+ init_map(smp, "_init");
+ }
+
+ return smp;
+}
+
+ static int
+__dlclose(fd)
+ void *fd;
+{
+ struct so_map *smp = (struct so_map *)fd;
+
+#ifdef DEBUG
+xprintf("dlclose(%s): refcount = %d\n", smp->som_path, LM_PRIVATE(smp)->spd_refcount);
+#endif
+ if (--LM_PRIVATE(smp)->spd_refcount != 0)
+ return 0;
+
+ /* Dismantle shared object map and descriptor */
+ init_map(smp, "_fini");
+#if 0
+ unmap_object(smp);
+ free(smp->som_sod->sod_name);
+ free(smp->som_sod);
+ free(smp);
+#endif
+
+ return 0;
+}
+
+ static void *
+__dlsym(fd, sym)
+ void *fd;
+ char *sym;
+{
+ struct so_map *smp = (struct so_map *)fd, *src_map = NULL;
+ struct nzlist *np;
+ long addr;
+
+ /*
+ * Restrict search to passed map if dlopen()ed.
+ */
+ if (LM_PRIVATE(smp)->spd_flags & RTLD_DL)
+ src_map = smp;
+
+ np = lookup(sym, &src_map, 1);
+ if (np == NULL)
+ return NULL;
+
+ /* Fixup jmpslot so future calls transfer directly to target */
+ addr = np->nz_value;
+ if (src_map)
+ addr += (long)src_map->som_addr;
+
+ return (void *)addr;
+}
+
+ static int
+__dlctl(fd, cmd, arg)
+ void *fd, *arg;
+ int cmd;
+{
+ switch (cmd) {
+ case DL_GETERRNO:
+ *(int *)arg = dlerrno;
+ return 0;
+ default:
+ dlerrno = EOPNOTSUPP;
+ return -1;
+ }
+ return 0;
+}
+
+void
+#if __STDC__
+xprintf(char *fmt, ...)
+#else
+xprintf(fmt, va_alist)
+char *fmt;
+#endif
+{
+ char buf[256];
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ vsprintf(buf, fmt, ap);
+ (void)write(1, buf, strlen(buf));
+ va_end(ap);
+}
+
diff --git a/gnu/usr.bin/ld/rtld/sbrk.c b/gnu/usr.bin/ld/rtld/sbrk.c
new file mode 100644
index 0000000..2d2c610
--- /dev/null
+++ b/gnu/usr.bin/ld/rtld/sbrk.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#include <machine/vmparam.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/mman.h>
+#ifndef BSD
+#define MAP_COPY MAP_PRIVATE
+#define MAP_FILE 0
+#define MAP_ANON 0
+#endif
+#include <fcntl.h>
+#include <a.out.h>
+#include <stab.h>
+#include <string.h>
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "ld.h"
+
+#ifndef BSD /* Need do better than this */
+#define NEED_DEV_ZERO 1
+#endif
+
+caddr_t
+sbrk(incr)
+int incr;
+{
+ int fd = -1;
+ caddr_t curbrk;
+
+ /* Round-up increment to page size */
+ incr = ((incr + PAGSIZ - 1) & ~(PAGSIZ - 1));
+
+#if DEBUG
+xprintf("sbrk: incr = %#x\n", incr);
+#endif
+
+#ifdef NEED_DEV_ZERO
+ fd = open("/dev/zero", O_RDWR, 0);
+ if (fd == -1)
+ perror("/dev/zero");
+#endif
+
+ if ((curbrk = mmap(0, incr,
+ PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_COPY, fd, 0)) == (caddr_t)-1) {
+ xprintf("Cannot map anonymous memory");
+ _exit(1);
+ }
+
+#ifdef DEBUG
+xprintf("sbrk: curbrk = %#x\n", curbrk);
+#endif
+
+#ifdef NEED_DEV_ZERO
+ close(fd);
+#endif
+
+ return(curbrk);
+}
+
diff --git a/gnu/usr.bin/ld/shlib.c b/gnu/usr.bin/ld/shlib.c
new file mode 100644
index 0000000..2e8dfe9
--- /dev/null
+++ b/gnu/usr.bin/ld/shlib.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: shlib.c,v 1.8 1994/02/13 20:41:43 jkh Exp $
+ */
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <err.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <a.out.h>
+
+#include "ld.h"
+
+#ifdef SUNOS4
+char *strsep();
+#endif
+
+/*
+ * Standard directories to search for files specified by -l.
+ */
+#ifndef STANDARD_SEARCH_DIRS
+#define STANDARD_SEARCH_DIRS "/usr/lib", "/usr/X11R6/lib", "/usr/X386/lib", "/usr/local/lib"
+#endif
+
+/*
+ * Actual vector of library search directories,
+ * including `-L'ed and LD_LIBARAY_PATH spec'd ones.
+ */
+char **search_dirs;
+int n_search_dirs;
+
+char *standard_search_dirs[] = {
+ STANDARD_SEARCH_DIRS
+};
+
+
+void
+add_search_dir(name)
+ char *name;
+{
+ n_search_dirs++;
+ search_dirs = (char **)
+ xrealloc(search_dirs, n_search_dirs * sizeof(char *));
+ search_dirs[n_search_dirs - 1] = strdup(name);
+}
+
+void
+add_search_path(path)
+char *path;
+{
+ register char *cp;
+
+ if (path == NULL)
+ return;
+
+ /* Add search directories from `paths' */
+ while ((cp = strsep(&path, ":")) != NULL) {
+ add_search_dir(cp);
+ if (path)
+ *(path-1) = ':';
+ }
+}
+
+void
+std_search_path()
+{
+ int i, n;
+
+ /* Append standard search directories */
+ n = sizeof standard_search_dirs / sizeof standard_search_dirs[0];
+ for (i = 0; i < n; i++)
+ add_search_dir(standard_search_dirs[i]);
+}
+
+/*
+ * Return true if CP points to a valid dewey number.
+ * Decode and leave the result in the array DEWEY.
+ * Return the number of decoded entries in DEWEY.
+ */
+
+int
+getdewey(dewey, cp)
+int dewey[];
+char *cp;
+{
+ int i, n;
+
+ for (n = 0, i = 0; i < MAXDEWEY; i++) {
+ if (*cp == '\0')
+ break;
+
+ if (*cp == '.') cp++;
+ if (!isdigit(*cp))
+ return 0;
+
+ dewey[n++] = strtol(cp, &cp, 10);
+ }
+
+ return n;
+}
+
+/*
+ * Compare two dewey arrays.
+ * Return -1 if `d1' represents a smaller value than `d2'.
+ * Return 1 if `d1' represents a greater value than `d2'.
+ * Return 0 if equal.
+ */
+int
+cmpndewey(d1, n1, d2, n2)
+int d1[], d2[];
+int n1, n2;
+{
+ register int i;
+
+ for (i = 0; i < n1 && i < n2; i++) {
+ if (d1[i] < d2[i])
+ return -1;
+ if (d1[i] > d2[i])
+ return 1;
+ }
+
+ if (n1 == n2)
+ return 0;
+
+ if (i == n1)
+ return -1;
+
+ if (i == n2)
+ return 1;
+
+ errx(1, "cmpndewey: cant happen");
+ return 0;
+}
+
+/*
+ * Search directories for a shared library matching the given
+ * major and minor version numbers.
+ *
+ * MAJOR == -1 && MINOR == -1 --> find highest version
+ * MAJOR != -1 && MINOR == -1 --> find highest minor version
+ * MAJOR == -1 && MINOR != -1 --> invalid
+ * MAJOR != -1 && MINOR != -1 --> find highest micro version
+ */
+
+/* Not interested in devices right now... */
+#undef major
+#undef minor
+
+char *
+findshlib(name, majorp, minorp, do_dot_a)
+char *name;
+int *majorp, *minorp;
+int do_dot_a;
+{
+ int dewey[MAXDEWEY];
+ int ndewey;
+ int tmp[MAXDEWEY];
+ int i;
+ int len;
+ char *lname, *path = NULL;
+ int major = *majorp, minor = *minorp;
+
+ len = strlen(name);
+ lname = (char *)alloca(len + sizeof("lib"));
+ sprintf(lname, "lib%s", name);
+ len += 3;
+
+ ndewey = 0;
+
+ for (i = 0; i < n_search_dirs; i++) {
+ DIR *dd = opendir(search_dirs[i]);
+ struct dirent *dp;
+ int found_dot_a = 0;
+
+ if (dd == NULL)
+ continue;
+
+ while ((dp = readdir(dd)) != NULL) {
+ int n, might_take_it = 0;
+
+ if (do_dot_a && path == NULL &&
+ dp->d_namlen == len + 2 &&
+ strncmp(dp->d_name, lname, len) == 0 &&
+ (dp->d_name+len)[0] == '.' &&
+ (dp->d_name+len)[1] == 'a') {
+
+ path = concat(search_dirs[i], "/", dp->d_name);
+ found_dot_a = 1;
+ }
+
+ if (dp->d_namlen < len + 4)
+ continue;
+ if (strncmp(dp->d_name, lname, len) != 0)
+ continue;
+ if (strncmp(dp->d_name+len, ".so.", 4) != 0)
+ continue;
+
+ if ((n = getdewey(tmp, dp->d_name+len+4)) == 0)
+ continue;
+
+ if (major != -1 && found_dot_a) { /* XXX */
+ free(path);
+ path = NULL;
+ found_dot_a = 0;
+ }
+
+ if (major == -1 && minor == -1) {
+ might_take_it = 1;
+ } else if (major != -1 && minor == -1) {
+ if (tmp[0] == major)
+ might_take_it = 1;
+ } else if (major != -1 && minor != -1) {
+ if (tmp[0] == major)
+ if (n == 1 || tmp[1] >= minor)
+ might_take_it = 1;
+ }
+
+ if (!might_take_it)
+ continue;
+
+ if (cmpndewey(tmp, n, dewey, ndewey) <= 0)
+ continue;
+
+ /* We have a better version */
+ if (path)
+ free(path);
+ path = concat(search_dirs[i], "/", dp->d_name);
+ found_dot_a = 0;
+ bcopy(tmp, dewey, sizeof(dewey));
+ ndewey = n;
+ *majorp = dewey[0];
+ *minorp = dewey[1];
+ }
+ closedir(dd);
+
+ if (found_dot_a)
+ /*
+ * There's a .a archive here.
+ */
+ return path;
+ }
+
+ return path;
+}
diff --git a/gnu/usr.bin/ld/sparc/md-static-funcs.c b/gnu/usr.bin/ld/sparc/md-static-funcs.c
new file mode 100644
index 0000000..2672cb5
--- /dev/null
+++ b/gnu/usr.bin/ld/sparc/md-static-funcs.c
@@ -0,0 +1,37 @@
+
+/*
+ * $Id: md-static-funcs.c,v 1.2 1993/12/08 10:28:56 pk Exp $
+ *
+ * Simple SPARC relocations for the benefit of self-relocation of ld.so
+ * avoiding the use of global variables (ie. reloc_bitshift[] et. al.).
+ * Only types supported are RELOC_32 and RELOC_RELATIVE.
+ *
+ * This *must* be a static function, so it is not called through a jmpslot.
+ */
+static void
+md_relocate_simple(r, relocation, addr)
+struct relocation_info *r;
+long relocation;
+char *addr;
+{
+ register unsigned long mask;
+ register unsigned long shift;
+
+ switch (r->r_type) {
+ case RELOC_32:
+ mask = 0xffffffff;
+ shift = 0;
+ break;
+ case RELOC_RELATIVE:
+ mask = 0x003fffff;
+ shift = 10;
+ break;
+ }
+ relocation += (*(long *)addr & mask) << shift;
+ relocation >>= shift;
+ relocation &= mask;
+
+ *(long *) (addr) &= ~mask;
+ *(long *) (addr) |= relocation;
+}
+
diff --git a/gnu/usr.bin/ld/sparc/md.c b/gnu/usr.bin/ld/sparc/md.c
new file mode 100644
index 0000000..508d37d
--- /dev/null
+++ b/gnu/usr.bin/ld/sparc/md.c
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: md.c,v 1.7 1994/02/13 20:43:03 jkh Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <a.out.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stab.h>
+#include <string.h>
+
+#include "ld.h"
+
+/*
+ * Relocation masks and sizes for the Sparc architecture.
+ *
+ * Note that these are very dependent on the order of the enums in
+ * enum reloc_type (in a.out.h); if they change the following must be
+ * changed.
+ * Also, note that RELOC_RELATIVE is handled as if it were a RELOC_HI22.
+ * This should work provided that relocations values have zeroes in their
+ * least significant 10 bits. As RELOC_RELATIVE is used only to relocate
+ * with load address values - which are page aligned - this condition is
+ * fulfilled as long as the system's page size is > 1024 (and a power of 2).
+ */
+static int reloc_target_rightshift[] = {
+ 0, 0, 0, /* RELOC_8, _16, _32 */
+ 0, 0, 0, 2, 2, /* DISP8, DISP16, DISP32, WDISP30, WDISP22 */
+ 10, 0, /* HI22, _22 */
+ 0, 0, /* RELOC_13, _LO10 */
+ 0, 0, /* _SFA_BASE, _SFA_OFF13 */
+ 0, 0, 10, /* _BASE10, _BASE13, _BASE22 */
+ 0, 10, /* _PC10, _PC22 */
+ 2, 0, /* _JMP_TBL, _SEGOFF16 */
+ 0, 0, 0 /* _GLOB_DAT, JMP_SLOT, _RELATIVE */
+};
+static int reloc_target_size[] = {
+ 0, 1, 2, /* RELOC_8, _16, _32 */
+ 0, 1, 2, 2, 2, /* DISP8, DISP16, DISP32, WDISP30, WDISP22 */
+ 2, 2, /* HI22, _22 */
+ 2, 2, /* RELOC_13, _LO10 */
+ 2, 2, /* _SFA_BASE, _SFA_OFF13 */
+ 2, 2, 2, /* _BASE10, _BASE13, _BASE22 */
+ 2, 2, /* _PC10, _PC22 */
+ 2, 0, /* _JMP_TBL, _SEGOFF16 */
+ 2, 0, 2 /* _GLOB_DAT, JMP_SLOT, _RELATIVE */
+};
+static int reloc_target_bitsize[] = {
+ 8, 16, 32, /* RELOC_8, _16, _32 */
+ 8, 16, 32, 30, 22, /* DISP8, DISP16, DISP32, WDISP30, WDISP22 */
+ 22, 22, /* HI22, _22 */
+ 13, 10, /* RELOC_13, _LO10 */
+ 32, 32, /* _SFA_BASE, _SFA_OFF13 */
+ 10, 13, 22, /* _BASE10, _BASE13, _BASE22 */
+ 10, 22, /* _PC10, _PC22 */
+ 30, 0, /* _JMP_TBL, _SEGOFF16 */
+ 32, 0, 22 /* _GLOB_DAT, JMP_SLOT, _RELATIVE */
+};
+
+
+/*
+ * Get relocation addend corresponding to relocation record RP
+ * ADDR unused by SPARC impl.
+ */
+long
+md_get_addend(r, addr)
+struct relocation_info *r;
+unsigned char *addr;
+{
+ return r->r_addend;
+}
+
+void
+md_relocate(r, relocation, addr, relocatable_output)
+struct relocation_info *r;
+long relocation;
+unsigned char *addr;
+int relocatable_output;
+{
+ register unsigned long mask;
+
+#ifndef RTLD
+ if (relocatable_output) {
+ /*
+ * Non-PC relative relocations which are absolute or
+ * which have become non-external now have fixed
+ * relocations. Set the ADD_EXTRA of this relocation
+ * to be the relocation we have now determined.
+ */
+ if (!RELOC_PCREL_P(r)) {
+ if ((int) r->r_type <= RELOC_32
+ || RELOC_EXTERN_P(r) == 0)
+ RELOC_ADD_EXTRA(r) = relocation;
+ } else if (RELOC_EXTERN_P(r))
+ /*
+ * External PC-relative relocations continue
+ * to move around; update their relocations
+ * by the amount they have moved so far.
+ */
+ RELOC_ADD_EXTRA(r) -= pc_relocation;
+ return;
+ }
+#endif
+
+ relocation >>= RELOC_VALUE_RIGHTSHIFT(r);
+
+ /* Unshifted mask for relocation */
+ mask = 1 << RELOC_TARGET_BITSIZE(r) - 1;
+ mask |= mask - 1;
+ relocation &= mask;
+
+ /* Shift everything up to where it's going to be used */
+ relocation <<= RELOC_TARGET_BITPOS(r);
+ mask <<= RELOC_TARGET_BITPOS(r);
+
+ switch (RELOC_TARGET_SIZE(r)) {
+ case 0:
+ if (RELOC_MEMORY_ADD_P(r))
+ relocation += (mask & *(u_char *) (addr));
+ *(u_char *) (addr) &= ~mask;
+ *(u_char *) (addr) |= relocation;
+ break;
+
+ case 1:
+ if (RELOC_MEMORY_ADD_P(r))
+ relocation += (mask & *(u_short *) (addr));
+ *(u_short *) (addr) &= ~mask;
+ *(u_short *) (addr) |= relocation;
+ break;
+
+ case 2:
+ if (RELOC_MEMORY_ADD_P(r))
+ relocation += (mask & *(u_long *) (addr));
+ *(u_long *) (addr) &= ~mask;
+ *(u_long *) (addr) |= relocation;
+ break;
+ default:
+ errx(1, "Unimplemented relocation field length: %d",
+ RELOC_TARGET_SIZE(r));
+ }
+}
+
+#ifndef RTLD
+/*
+ * Machine dependent part of claim_rrs_reloc().
+ * On the Sparc the relocation offsets are stored in the r_addend member.
+ */
+int
+md_make_reloc(rp, r, type)
+struct relocation_info *rp, *r;
+int type;
+{
+ r->r_type = rp->r_type;
+ r->r_addend = rp->r_addend;
+
+#if 1
+ /*
+ * This wouldn't be strictly necessary - we could record the
+ * relocation value "in situ" in stead of in the r_addend field -
+ * but we are being Sun compatible here. Besides, Sun's ld.so
+ * has a bug that prevents it from handling this alternate method.
+ *
+ * IT WOULD BE REALLY NICE TO HAVE CONSISTENCY THROUGHOUT THE ENTIRE
+ * RELOCATION PROCESS, ie. using `r_addend' for storing all partially
+ * completed relocations, in stead of mixing them in both relocation
+ * records and in the segment data.
+ */
+ if (RELOC_PCREL_P(rp))
+ r->r_addend -= pc_relocation;
+#endif
+
+ return 1;
+}
+#endif
+
+/*
+ * Set up a transfer from jmpslot at OFFSET (relative to the PLT table)
+ * to the binder slot (which is at offset 0 of the PLT).
+ */
+void
+md_make_jmpslot(sp, offset, index)
+jmpslot_t *sp;
+long offset;
+long index;
+{
+ u_long fudge = (u_long) -(sizeof(sp->opcode1) + offset);
+ sp->opcode1 = SAVE;
+ /* The following is a RELOC_WDISP30 relocation */
+ sp->opcode2 = CALL | ((fudge >> 2) & 0x3fffffff);
+ sp->reloc_index = NOP | index;
+}
+
+/*
+ * Set up a "direct" transfer (ie. not through the run-time binder) from
+ * jmpslot at OFFSET to ADDR. Used by `ld' when the SYMBOLIC flag is on,
+ * and by `ld.so' after resolving the symbol.
+ * On the i386, we use the JMP instruction which is PC relative, so no
+ * further RRS relocations will be necessary for such a jmpslot.
+ *
+ * OFFSET unused on Sparc.
+ */
+void
+md_fix_jmpslot(sp, offset, addr)
+jmpslot_t *sp;
+long offset;
+u_long addr;
+{
+ /*
+ * Here comes a RELOC_{LO10,HI22} relocation pair
+ * The resulting code is:
+ * sethi %hi(addr), %g1
+ * jmp %g1+%lo(addr)
+ * nop ! delay slot
+ */
+ sp->opcode1 = SETHI | ((addr >> 10) & 0x003fffff);
+ sp->opcode2 = JMP | (addr & 0x000003ff);
+ sp->reloc_index = NOP;
+}
+
+/*
+ * Update the relocation record for a jmpslot.
+ */
+void
+md_make_jmpreloc(rp, r, type)
+struct relocation_info *rp, *r;
+int type;
+{
+ if (type & RELTYPE_RELATIVE)
+ r->r_type = RELOC_RELATIVE;
+ else
+ r->r_type = RELOC_JMP_SLOT;
+
+ r->r_addend = rp->r_addend;
+}
+
+/*
+ * Set relocation type for a GOT RRS relocation.
+ */
+void
+md_make_gotreloc(rp, r, type)
+struct relocation_info *rp, *r;
+int type;
+{
+ /*
+ * GOT value resolved (symbolic or entry point): R_32
+ * GOT not resolved: GLOB_DAT
+ *
+ * NOTE: I don't think it makes a difference.
+ */
+ if (type & RELTYPE_RELATIVE)
+ r->r_type = RELOC_32;
+ else
+ r->r_type = RELOC_GLOB_DAT;
+
+ r->r_addend = 0;
+}
+
+/*
+ * Set relocation type for a RRS copy operation.
+ */
+void
+md_make_cpyreloc(rp, r)
+struct relocation_info *rp, *r;
+{
+ r->r_type = RELOC_COPY_DAT;
+ r->r_addend = 0;
+}
+
+void
+md_set_breakpoint(where, savep)
+long where;
+long *savep;
+{
+ *savep = *(long *)where;
+ *(long *)where = TRAP;
+}
+
+#ifndef RTLD
+/*
+ * Initialize (output) exec header such that useful values are
+ * obtained from subsequent N_*() macro evaluations.
+ */
+void
+md_init_header(hp, magic, flags)
+struct exec *hp;
+int magic, flags;
+{
+#ifdef NetBSD
+ N_SETMAGIC((*hp), magic, MID_MACHINE, flags);
+
+ /* TEXT_START depends on the value of outheader.a_entry. */
+ if (!(link_mode & SHAREABLE)) /*WAS: if (entry_symbol) */
+ hp->a_entry = PAGSIZ;
+#else
+ hp->a_magic = magic;
+ hp->a_machtype = M_SPARC;
+ hp->a_toolversion = 1;
+ hp->a_dynamic = ((flags) & EX_DYNAMIC);
+
+ /* SunOS 4.1 N_TXTADDR depends on the value of outheader.a_entry. */
+ if (!(link_mode & SHAREABLE)) /*WAS: if (entry_symbol) */
+ hp->a_entry = N_PAGSIZ(*hp);
+#endif
+}
+
+/*
+ * Check for acceptable foreign machine Ids
+ */
+int
+md_midcompat(hp)
+struct exec *hp;
+{
+#ifdef NetBSD
+#define SUN_M_SPARC 3
+ return (((md_swap_long(hp->a_midmag)&0x00ff0000) >> 16) == SUN_M_SPARC);
+#else
+ return hp->a_machtype == M_SPARC;
+#endif
+}
+#endif /* RTLD */
diff --git a/gnu/usr.bin/ld/sparc/md.h b/gnu/usr.bin/ld/sparc/md.h
new file mode 100644
index 0000000..3545d97
--- /dev/null
+++ b/gnu/usr.bin/ld/sparc/md.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: md.h,v 1.5 1993/12/02 01:03:47 jkh Exp $
+ */
+
+/*
+ * SPARC machine dependent definitions
+ */
+
+
+#define MAX_ALIGNMENT (sizeof (double))
+
+#ifdef NetBSD
+#define PAGSIZ __LDPGSZ
+
+#define N_SET_FLAG(ex,f) N_SETMAGIC(ex,N_GETMAGIC(ex), \
+ MID_MACHINE, N_GETFLAG(ex)|(f))
+#define N_IS_DYNAMIC(ex) ((N_GETFLAG(ex) & EX_DYNAMIC))
+
+/*
+ * Should be handled by a.out.h ?
+ */
+#define N_ADJUST(ex) (((ex).a_entry < PAGSIZ) ? -PAGSIZ : 0)
+#define TEXT_START(ex) (N_TXTADDR(ex) + N_ADJUST(ex))
+#define DATA_START(ex) (N_DATADDR(ex) + N_ADJUST(ex))
+
+#else
+
+/* Get the SunOS a.out and relocation nomenclature */
+#define EX_DYNAMIC 1
+
+#define N_IS_DYNAMIC(ex) ((ex).a_dynamic)
+
+#define N_SET_FLAG(ex, f) { \
+ (ex).a_dynamic = ((f) & EX_DYNAMIC); \
+}
+
+#undef relocation_info
+#define relocation_info reloc_info_sparc
+#define r_symbolnum r_index
+#endif /* NetBSD */
+
+#define N_BADMID(ex) \
+ (N_GETMID(ex) != 0 && N_GETMID(ex) != MID_MACHINE && \
+ !md_midcompat(&(ex)))
+
+/* Sparc (Sun 4) macros */
+#define RELOC_ADDRESS(r) ((r)->r_address)
+#define RELOC_EXTERN_P(r) ((r)->r_extern)
+#define RELOC_TYPE(r) ((r)->r_symbolnum)
+#define RELOC_SYMBOL(r) ((r)->r_symbolnum)
+#define RELOC_MEMORY_SUB_P(r) 0
+#ifdef RTLD
+/* XXX - consider this making SUN_COMPAT --> repercussions on rrs.c */
+#define RELOC_MEMORY_ADD_P(r) 1
+#else
+#define RELOC_MEMORY_ADD_P(r) 0
+#endif
+#define RELOC_ADD_EXTRA(r) ((r)->r_addend)
+#define RELOC_PCREL_P(r) \
+ (((r)->r_type >= RELOC_DISP8 && (r)->r_type <= RELOC_WDISP22) \
+ || ((r)->r_type == RELOC_PC10 || (r)->r_type == RELOC_PC22) \
+ || (r)->r_type == RELOC_JMP_TBL)
+#define RELOC_VALUE_RIGHTSHIFT(r) (reloc_target_rightshift[(r)->r_type])
+#define RELOC_TARGET_SIZE(r) (reloc_target_size[(r)->r_type])
+#define RELOC_TARGET_BITPOS(r) 0
+#define RELOC_TARGET_BITSIZE(r) (reloc_target_bitsize[(r)->r_type])
+
+#define RELOC_JMPTAB_P(r) ((r)->r_type == RELOC_JMP_TBL)
+
+#define RELOC_BASEREL_P(r) \
+ ((r)->r_type >= RELOC_BASE10 && (r)->r_type <= RELOC_BASE22)
+
+#define RELOC_RELATIVE_P(r) ((r)->r_type == RELOC_RELATIVE)
+#define RELOC_COPY_DAT (RELOC_RELATIVE+1) /*XXX*/
+#define RELOC_COPY_P(r) ((r)->r_type == RELOC_COPY_DAT)
+#define RELOC_LAZY_P(r) ((r)->r_type == RELOC_JMP_SLOT)
+
+#define RELOC_STATICS_THROUGH_GOT_P(r) (1)
+#define JMPSLOT_NEEDS_RELOC (1)
+
+#define CHECK_GOT_RELOC(r) \
+ ((r)->r_type == RELOC_PC10 || (r)->r_type == RELOC_PC22)
+
+#define md_got_reloc(r) (-(r)->r_address)
+
+#ifdef SUN_COMPAT
+/*
+ * Sun plays games with `r_addend'
+ */
+#define md_get_rt_segment_addend(r,a) (0)
+#endif
+
+/* Width of a Global Offset Table entry */
+typedef long got_t;
+
+typedef struct jmpslot {
+ u_long opcode1;
+ u_long opcode2;
+ u_long reloc_index;
+#define JMPSLOT_RELOC_MASK (0x003fffff) /* 22 bits */
+} jmpslot_t;
+
+#define SAVE 0x9de3bfa0 /* Build stack frame (opcode1) */
+#define SETHI 0x03000000 /* %hi(addr) -> %g1 (opcode1) */
+#define CALL 0x40000000 /* Call instruction (opcode2) */
+#define JMP 0x81c06000 /* Jump %g1 instruction (opcode2) */
+#define NOP 0x01000000 /* Delay slot NOP for (reloc_index) */
+#define TRAP 0x91d02001 /* ta 0x1 */
+
+
+/*
+ * Byte swap defs for cross linking
+ */
+
+#if !defined(NEED_SWAP)
+
+#define md_swapin_exec_hdr(h)
+#define md_swapout_exec_hdr(h)
+#define md_swapin_symbols(s,n)
+#define md_swapout_symbols(s,n)
+#define md_swapin_zsymbols(s,n)
+#define md_swapout_zsymbols(s,n)
+#define md_swapin_reloc(r,n)
+#define md_swapout_reloc(r,n)
+#define md_swapin__dynamic(l)
+#define md_swapout__dynamic(l)
+#define md_swapin_section_dispatch_table(l)
+#define md_swapout_section_dispatch_table(l)
+#define md_swapin_so_debug(d)
+#define md_swapout_so_debug(d)
+#define md_swapin_rrs_hash(f,n)
+#define md_swapout_rrs_hash(f,n)
+#define md_swapin_sod(l,n)
+#define md_swapout_sod(l,n)
+#define md_swapout_jmpslot(j,n)
+#define md_swapout_got(g,n)
+#define md_swapin_ranlib_hdr(h,n)
+#define md_swapout_ranlib_hdr(h,n)
+
+#endif /* NEED_SWAP */
+
+#ifdef CROSS_LINKER
+
+#ifdef NEED_SWAP
+
+/* Define IO byte swapping routines */
+
+void md_swapin_exec_hdr __P((struct exec *));
+void md_swapout_exec_hdr __P((struct exec *));
+void md_swapin_reloc __P((struct relocation_info *, int));
+void md_swapout_reloc __P((struct relocation_info *, int));
+void md_swapout_jmpslot __P((jmpslot_t *, int));
+
+#define md_swapin_symbols(s,n) swap_symbols(s,n)
+#define md_swapout_symbols(s,n) swap_symbols(s,n)
+#define md_swapin_zsymbols(s,n) swap_zsymbols(s,n)
+#define md_swapout_zsymbols(s,n) swap_zsymbols(s,n)
+#define md_swapin__dynamic(l) swap__dynamic(l)
+#define md_swapout__dynamic(l) swap__dynamic(l)
+#define md_swapin_section_dispatch_table(l) swap_section_dispatch_table(l)
+#define md_swapout_section_dispatch_table(l) swap_section_dispatch_table(l)
+#define md_swapin_so_debug(d) swap_so_debug(d)
+#define md_swapout_so_debug(d) swap_so_debug(d)
+#define md_swapin_rrs_hash(f,n) swap_rrs_hash(f,n)
+#define md_swapout_rrs_hash(f,n) swap_rrs_hash(f,n)
+#define md_swapin_sod(l,n) swapin_sod(l,n)
+#define md_swapout_sod(l,n) swapout_sod(l,n)
+#define md_swapout_got(g,n) swap_longs((long*)(g),n)
+#define md_swapin_ranlib_hdr(h,n) swap_ranlib_hdr(h,n)
+#define md_swapout_ranlib_hdr(h,n) swap_ranlib_hdr(h,n)
+
+#define md_swap_short(x) ( (((x) >> 8) & 0xff) | (((x) & 0xff) << 8) )
+
+#define md_swap_long(x) ( (((x) >> 24) & 0xff ) | (((x) >> 8 ) & 0xff00 ) | \
+ (((x) << 8 ) & 0xff0000) | (((x) << 24) & 0xff000000))
+
+#define get_byte(p) ( ((unsigned char *)(p))[0] )
+
+#define get_short(p) ( ( ((unsigned char *)(p))[1] << 8) | \
+ ( ((unsigned char *)(p))[0] ) \
+ )
+#define get_long(p) ( ( ((unsigned char *)(p))[3] << 24) | \
+ ( ((unsigned char *)(p))[2] << 16) | \
+ ( ((unsigned char *)(p))[1] << 8 ) | \
+ ( ((unsigned char *)(p))[0] ) \
+ )
+
+#define put_byte(p, v) { ((unsigned char *)(p))[0] = ((unsigned long)(v)); }
+
+#define put_short(p, v) { ((unsigned char *)(p))[1] = \
+ ((((unsigned long)(v)) >> 8) & 0xff); \
+ ((unsigned char *)(p))[0] = \
+ ((((unsigned long)(v)) ) & 0xff); }
+
+#define put_long(p, v) { ((unsigned char *)(p))[3] = \
+ ((((unsigned long)(v)) >> 24) & 0xff); \
+ ((unsigned char *)(p))[2] = \
+ ((((unsigned long)(v)) >> 16) & 0xff); \
+ ((unsigned char *)(p))[1] = \
+ ((((unsigned long)(v)) >> 8) & 0xff); \
+ ((unsigned char *)(p))[0] = \
+ ((((unsigned long)(v)) ) & 0xff); }
+
+#else /* We need not swap, but must pay attention to alignment: */
+
+#define md_swap_short(x) (x)
+#define md_swap_long(x) (x)
+
+#define get_byte(p) ( ((unsigned char *)(p))[0] )
+
+#define get_short(p) ( ( ((unsigned char *)(p))[0] << 8) | \
+ ( ((unsigned char *)(p))[1] ) \
+ )
+
+#define get_long(p) ( ( ((unsigned char *)(p))[0] << 24) | \
+ ( ((unsigned char *)(p))[1] << 16) | \
+ ( ((unsigned char *)(p))[2] << 8 ) | \
+ ( ((unsigned char *)(p))[3] ) \
+ )
+
+
+#define put_byte(p, v) { ((unsigned char *)(p))[0] = ((unsigned long)(v)); }
+
+#define put_short(p, v) { ((unsigned char *)(p))[0] = \
+ ((((unsigned long)(v)) >> 8) & 0xff); \
+ ((unsigned char *)(p))[1] = \
+ ((((unsigned long)(v)) ) & 0xff); }
+
+#define put_long(p, v) { ((unsigned char *)(p))[0] = \
+ ((((unsigned long)(v)) >> 24) & 0xff); \
+ ((unsigned char *)(p))[1] = \
+ ((((unsigned long)(v)) >> 16) & 0xff); \
+ ((unsigned char *)(p))[2] = \
+ ((((unsigned long)(v)) >> 8) & 0xff); \
+ ((unsigned char *)(p))[3] = \
+ ((((unsigned long)(v)) ) & 0xff); }
+
+#endif /* NEED_SWAP */
+
+#else /* Not a cross linker: use native */
+
+#define md_swap_short(x) (x)
+#define md_swap_long(x) (x)
+
+#define get_byte(where) (*(char *)(where))
+#define get_short(where) (*(short *)(where))
+#define get_long(where) (*(long *)(where))
+
+#define put_byte(where,what) (*(char *)(where) = (what))
+#define put_short(where,what) (*(short *)(where) = (what))
+#define put_long(where,what) (*(long *)(where) = (what))
+
+#endif /* CROSS_LINKER */
+
diff --git a/gnu/usr.bin/ld/sparc/mdprologue.S b/gnu/usr.bin/ld/sparc/mdprologue.S
new file mode 100644
index 0000000..0d006d0
--- /dev/null
+++ b/gnu/usr.bin/ld/sparc/mdprologue.S
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: mdprologue.S,v 1.2 1993/11/09 04:19:36 paul Exp $
+ */
+
+/*
+ * SPARC run-time link editor entry points.
+ */
+
+#define CRT_VERSION_SUN 1
+
+ .seg "text" ! [internal]
+ .proc 16
+ .global _rtld_entry
+_rtld_entry:
+!#PROLOGUE# 0
+ save %sp,-96,%sp
+L.1B:
+ call L.2B
+ sethi %hi((__GLOBAL_OFFSET_TABLE_-(L.1B-.))),%l7
+L.2B:
+!#PROLOGUE# 1
+ or %l7,%lo((__GLOBAL_OFFSET_TABLE_-(L.1B-.))),%l7
+ add %l7,%o7,%l7
+
+ cmp %i0, CRT_VERSION_SUN ! is crtp passed in Sun style,
+ bne 1f ! ie. relative to stack frame ?
+ nop
+ add %i1, %fp, %i1 ! if so, adjust to absolute address
+1:
+ ld [%i1], %o3 ! load base address (crtp->crt_ba)
+ ld [%l7], %o2 ! get rtld's __DYNAMIC address
+ ! from 1st GOT entry
+ add %o2, %o3, %o2 ! relocate and make it 3rd arg.
+
+ ld [%l7 + _rtld], %g1 ! get address of rtld()
+ add %g1, %o3, %g1 ! relocate
+
+ mov %i1, %o1 ! set up args, #2: crtp
+ call %g1 ! rtld(version, crtp, dp)
+ mov %i0, %o0 ! arg #1: version
+
+ ret
+ restore
+ .seg "data" ! [internal]
+
+ .seg "text"
+ .global _binder_entry
+_binder_entry:
+!#PROLOGUE# 0
+ save %sp,-96,%sp
+!L.1C:
+! call L.2C
+! sethi %hi((__GLOBAL_OFFSET_TABLE_-(L.1C-.))),%l7
+!L.2C:
+! or %l7,%lo((__GLOBAL_OFFSET_TABLE_-(L.1C-.))),%l7
+!#PROLOGUE# 1
+
+ sub %i7, 4, %o0 ! get to jmpslot through pc
+ ld [%i7+4], %o1 ! get relocation index
+ sethi %hi(0x3fffff), %o2 ! -> reloc_index & 0x003fffff
+ or %o2, %lo(0x3fffff), %o2 ! [internal]
+ call _binder ! and call binder(jsp, reloc_index)
+ and %o1, %o2, %o1
+
+ mov %o0, %g1 ! return value == function address
+
+ restore ! get rid of our context
+ jmp %g1 ! and go.
+ restore ! and the jmpslot's
+ nop
+
+ .seg "data" ! [internal]
+
diff --git a/gnu/usr.bin/ld/symbol.c b/gnu/usr.bin/ld/symbol.c
new file mode 100644
index 0000000..f355b3e
--- /dev/null
+++ b/gnu/usr.bin/ld/symbol.c
@@ -0,0 +1,161 @@
+/*
+ * $Id: symbol.c,v 1.4 1994/02/13 20:41:46 jkh Exp $ - symbol table routines
+ */
+
+/* Create the symbol table entries for `etext', `edata' and `end'. */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <a.out.h>
+#include <stab.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ld.h"
+
+symbol *symtab[SYMTABSIZE]; /* The symbol table. */
+int num_hash_tab_syms; /* Number of symbols in symbol hash table. */
+
+symbol *edata_symbol; /* the symbol _edata */
+symbol *etext_symbol; /* the symbol _etext */
+symbol *end_symbol; /* the symbol _end */
+symbol *got_symbol; /* the symbol __GLOBAL_OFFSET_TABLE_ */
+symbol *dynamic_symbol; /* the symbol __DYNAMIC */
+
+void
+symtab_init(relocatable_output)
+ int relocatable_output;
+{
+ /*
+ * Put linker reserved symbols into symbol table.
+ */
+#ifndef nounderscore
+#define ETEXT_SYM "_etext"
+#define EDATA_SYM "_edata"
+#define END_SYM "_end"
+#define DYN_SYM "__DYNAMIC"
+#define GOT_SYM "__GLOBAL_OFFSET_TABLE_"
+#else
+#define ETEXT_SYM "etext"
+#define EDATA_SYM "edata"
+#define END_SYM "end"
+#define DYN_SYM "_DYNAMIC"
+#define GOT_SYM "_GLOBAL_OFFSET_TABLE_"
+#endif
+
+ dynamic_symbol = getsym(DYN_SYM);
+ dynamic_symbol->defined = relocatable_output?N_UNDF:(N_DATA | N_EXT);
+
+ got_symbol = getsym(GOT_SYM);
+ got_symbol->defined = N_DATA | N_EXT;
+
+ if (relocatable_output)
+ return;
+
+ etext_symbol = getsym(ETEXT_SYM);
+ edata_symbol = getsym(EDATA_SYM);
+ end_symbol = getsym(END_SYM);
+
+ etext_symbol->defined = N_TEXT | N_EXT;
+ edata_symbol->defined = N_DATA | N_EXT;
+ end_symbol->defined = N_BSS | N_EXT;
+
+ etext_symbol->flags |= GS_REFERENCED;
+ edata_symbol->flags |= GS_REFERENCED;
+ end_symbol->flags |= GS_REFERENCED;
+}
+
+/*
+ * Compute the hash code for symbol name KEY.
+ */
+
+int
+hash_string (key)
+ char *key;
+{
+ register char *cp;
+ register int k;
+
+ cp = key;
+ k = 0;
+ while (*cp)
+ k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
+
+ return k;
+}
+
+/*
+ * Get the symbol table entry for the global symbol named KEY.
+ * Create one if there is none.
+ */
+
+symbol *
+getsym(key)
+ char *key;
+{
+ register int hashval;
+ register symbol *bp;
+
+ /* Determine the proper bucket. */
+ hashval = hash_string(key) % SYMTABSIZE;
+
+ /* Search the bucket. */
+ for (bp = symtab[hashval]; bp; bp = bp->link)
+ if (strcmp(key, bp->name) == 0)
+ return bp;
+
+ /* Nothing was found; create a new symbol table entry. */
+ bp = (symbol *)xmalloc(sizeof(symbol));
+ bp->name = (char *)xmalloc(strlen(key) + 1);
+ strcpy (bp->name, key);
+ bp->refs = 0;
+ bp->defined = 0;
+ bp->value = 0;
+ bp->common_size = 0;
+ bp->warning = 0;
+ bp->undef_refs = 0;
+ bp->mult_defs = 0;
+ bp->alias = 0;
+ bp->setv_count = 0;
+ bp->symbolnum = 0;
+ bp->rrs_symbolnum = 0;
+
+ bp->size = 0;
+ bp->aux = 0;
+ bp->sorefs = 0;
+ bp->so_defined = 0;
+ bp->def_nlist = 0;
+ bp->jmpslot_offset = -1;
+ bp->gotslot_offset = -1;
+ bp->flags = 0;
+
+ /* Add the entry to the bucket. */
+ bp->link = symtab[hashval];
+ symtab[hashval] = bp;
+
+ ++num_hash_tab_syms;
+
+ return bp;
+}
+
+/* Like `getsym' but return 0 if the symbol is not already known. */
+
+symbol *
+getsym_soft (key)
+ char *key;
+{
+ register int hashval;
+ register symbol *bp;
+
+ /* Determine which bucket. */
+ hashval = hash_string(key) % SYMTABSIZE;
+
+ /* Search the bucket. */
+ for (bp = symtab[hashval]; bp; bp = bp->link)
+ if (strcmp(key, bp->name) == 0)
+ return bp;
+
+ return 0;
+}
diff --git a/gnu/usr.bin/ld/symseg.h b/gnu/usr.bin/ld/symseg.h
new file mode 100644
index 0000000..5e11b0b
--- /dev/null
+++ b/gnu/usr.bin/ld/symseg.h
@@ -0,0 +1,359 @@
+/*-
+ *
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * from: @(#)symseg.h 5.4 (Berkeley) 4/30/91
+ * $Id: symseg.h,v 1.2 1993/08/01 18:46:59 mycroft Exp $
+ */
+
+/* GDB symbol table format definitions.
+ Copyright (C) 1987, 1988 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Format of GDB symbol table data.
+ There is one symbol segment for each source file or
+ independant compilation. These segments are simply concatenated
+ to form the GDB symbol table. A zero word where the beginning
+ of a segment is expected indicates there are no more segments.
+
+Format of a symbol segment:
+
+ The symbol segment begins with a word containing 1
+ if it is in the format described here. Other formats may
+ be designed, with other code numbers.
+
+ The segment contains many objects which point at each other.
+ The pointers are offsets in bytes from the beginning of the segment.
+ Thus, each segment can be loaded into core and its pointers relocated
+ to make valid in-core pointers.
+
+ All the data objects in the segment can be found indirectly from
+ one of them, the root object, of type `struct symbol_root'.
+ It appears at the beginning of the segment.
+
+ The total size of the segment, in bytes, appears as the `length'
+ field of this object. This size includes the size of the
+ root object.
+
+ All the object data types are defined here to contain pointer types
+ appropriate for in-core use on a relocated symbol segment.
+ Casts to and from type int are required for working with
+ unrelocated symbol segments such as are found in the file.
+
+ The ldsymaddr word is filled in by the loader to contain
+ the offset (in bytes) within the ld symbol table
+ of the first nonglobal symbol from this compilation.
+ This makes it possible to match those symbols
+ (which contain line number information) reliably with
+ the segment they go with.
+
+ Core addresses within the program that appear in the symbol segment
+ are not relocated by the loader. They are inserted by the assembler
+ and apply to addresses as output by the assembler, so GDB must
+ relocate them when it loads the symbol segment. It gets the information
+ on how to relocate from the textrel, datarel, bssrel, databeg and bssbeg
+ words of the root object.
+
+ The words textrel, datarel and bssrel
+ are filled in by ld with the amounts to relocate within-the-file
+ text, data and bss addresses by; databeg and bssbeg can be
+ used to tell which kind of relocation an address needs. */
+
+enum language {language_c};
+
+struct symbol_root
+{
+ int format; /* Data format version */
+ int length; /* # bytes in this symbol segment */
+ int ldsymoff; /* Offset in ld symtab of this file's syms */
+ int textrel; /* Relocation for text addresses */
+ int datarel; /* Relocation for data addresses */
+ int bssrel; /* Relocation for bss addresses */
+ char *filename; /* Name of main source file compiled */
+ char *filedir; /* Name of directory it was reached from */
+ struct blockvector *blockvector; /* Vector of all symbol-naming blocks */
+ struct typevector *typevector; /* Vector of all data types */
+ enum language language; /* Code identifying the language used */
+ char *version; /* Version info. Not fully specified */
+ char *compilation; /* Compilation info. Not fully specified */
+ int databeg; /* Address within the file of data start */
+ int bssbeg; /* Address within the file of bss start */
+ struct sourcevector *sourcevector; /* Vector of line-number info */
+};
+
+/* All data types of symbols in the compiled program
+ are represented by `struct type' objects.
+ All of these objects are pointed to by the typevector.
+ The type vector may have empty slots that contain zero. */
+
+struct typevector
+{
+ int length; /* Number of types described */
+ struct type *type[1];
+};
+
+/* Different kinds of data types are distinguished by the `code' field. */
+
+enum type_code
+{
+ TYPE_CODE_UNDEF, /* Not used; catches errors */
+ TYPE_CODE_PTR, /* Pointer type */
+ TYPE_CODE_ARRAY, /* Array type, lower bound zero */
+ TYPE_CODE_STRUCT, /* C struct or Pascal record */
+ TYPE_CODE_UNION, /* C union or Pascal variant part */
+ TYPE_CODE_ENUM, /* Enumeration type */
+ TYPE_CODE_FUNC, /* Function type */
+ TYPE_CODE_INT, /* Integer type */
+ TYPE_CODE_FLT, /* Floating type */
+ TYPE_CODE_VOID, /* Void type (values zero length) */
+ TYPE_CODE_SET, /* Pascal sets */
+ TYPE_CODE_RANGE, /* Range (integers within spec'd bounds) */
+ TYPE_CODE_PASCAL_ARRAY, /* Array with explicit type of index */
+};
+
+/* This appears in a type's flags word for an unsigned integer type. */
+#define TYPE_FLAG_UNSIGNED 1
+
+/* Other flag bits are used with GDB. */
+
+struct type
+{
+ /* Code for kind of type */
+ enum type_code code;
+ /* Name of this type, or zero if none.
+ This is used for printing only.
+ Type names specified as input are defined by symbols. */
+ char *name;
+ /* Length in bytes of storage for a value of this type */
+ int length;
+ /* For a pointer type, describes the type of object pointed to.
+ For an array type, describes the type of the elements.
+ For a function type, describes the type of the value.
+ Unused otherwise. */
+ struct type *target_type;
+ /* Type that is a pointer to this type.
+ Zero if no such pointer-to type is known yet.
+ The debugger may add the address of such a type
+ if it has to construct one later. */
+ struct type *pointer_type;
+ /* Type that is a function returning this type.
+ Zero if no such function type is known here.
+ The debugger may add the address of such a type
+ if it has to construct one later. */
+ struct type *function_type;
+ /* Flags about this type. */
+ short flags;
+ /* Number of fields described for this type */
+ short nfields;
+ /* For structure and union types, a description of each field.
+ For set and pascal array types, there is one "field",
+ whose type is the domain type of the set or array.
+ For range types, there are two "fields",
+ the minimum and maximum values (both inclusive).
+ For enum types, each possible value is described by one "field".
+ For range types, there are two "fields", that record constant values
+ (inclusive) for the minimum and maximum.
+
+ Using a pointer to a separate array of fields
+ allows all types to have the same size, which is useful
+ because we can allocate the space for a type before
+ we know what to put in it. */
+ struct field
+ {
+ /* Position of this field, counting in bits from start of
+ containing structure. For a function type, this is the
+ position in the argument list of this argument.
+ For a range bound or enum value, this is the value itself. */
+ int bitpos;
+ /* Size of this field, in bits, or zero if not packed.
+ For an unpacked field, the field's type's length
+ says how many bytes the field occupies. */
+ int bitsize;
+ /* In a struct or enum type, type of this field.
+ In a function type, type of this argument.
+ In an array type, the domain-type of the array. */
+ struct type *type;
+ /* Name of field, value or argument.
+ Zero for range bounds and array domains. */
+ char *name;
+ } *fields;
+};
+
+/* All of the name-scope contours of the program
+ are represented by `struct block' objects.
+ All of these objects are pointed to by the blockvector.
+
+ Each block represents one name scope.
+ Each lexical context has its own block.
+
+ The first two blocks in the blockvector are special.
+ The first one contains all the symbols defined in this compilation
+ whose scope is the entire program linked together.
+ The second one contains all the symbols whose scope is the
+ entire compilation excluding other separate compilations.
+ In C, these correspond to global symbols and static symbols.
+
+ Each block records a range of core addresses for the code that
+ is in the scope of the block. The first two special blocks
+ give, for the range of code, the entire range of code produced
+ by the compilation that the symbol segment belongs to.
+
+ The blocks appear in the blockvector
+ in order of increasing starting-address,
+ and, within that, in order of decreasing ending-address.
+
+ This implies that within the body of one function
+ the blocks appear in the order of a depth-first tree walk. */
+
+struct blockvector
+{
+ /* Number of blocks in the list. */
+ int nblocks;
+ /* The blocks themselves. */
+ struct block *block[1];
+};
+
+struct block
+{
+ /* Addresses in the executable code that are in this block.
+ Note: in an unrelocated symbol segment in a file,
+ these are always zero. They can be filled in from the
+ N_LBRAC and N_RBRAC symbols in the loader symbol table. */
+ int startaddr, endaddr;
+ /* The symbol that names this block,
+ if the block is the body of a function;
+ otherwise, zero.
+ Note: In an unrelocated symbol segment in an object file,
+ this field may be zero even when the block has a name.
+ That is because the block is output before the name
+ (since the name resides in a higher block).
+ Since the symbol does point to the block (as its value),
+ it is possible to find the block and set its name properly. */
+ struct symbol *function;
+ /* The `struct block' for the containing block, or 0 if none. */
+ /* Note that in an unrelocated symbol segment in an object file
+ this pointer may be zero when the correct value should be
+ the second special block (for symbols whose scope is one compilation).
+ This is because the compiler ouptuts the special blocks at the
+ very end, after the other blocks. */
+ struct block *superblock;
+ /* Number of local symbols. */
+ int nsyms;
+ /* The symbols. */
+ struct symbol *sym[1];
+};
+
+/* Represent one symbol name; a variable, constant, function or typedef. */
+
+/* Different name spaces for symbols. Looking up a symbol specifies
+ a namespace and ignores symbol definitions in other name spaces.
+
+ VAR_NAMESPACE is the usual namespace.
+ In C, this contains variables, function names, typedef names
+ and enum type values.
+
+ STRUCT_NAMESPACE is used in C to hold struct, union and enum type names.
+ Thus, if `struct foo' is used in a C program,
+ it produces a symbol named `foo' in the STRUCT_NAMESPACE.
+
+ LABEL_NAMESPACE may be used for names of labels (for gotos);
+ currently it is not used and labels are not recorded at all. */
+
+/* For a non-global symbol allocated statically,
+ the correct core address cannot be determined by the compiler.
+ The compiler puts an index number into the symbol's value field.
+ This index number can be matched with the "desc" field of
+ an entry in the loader symbol table. */
+
+enum namespace
+{
+ UNDEF_NAMESPACE, VAR_NAMESPACE, STRUCT_NAMESPACE, LABEL_NAMESPACE,
+};
+
+/* An address-class says where to find the value of the symbol in core. */
+
+enum address_class
+{
+ LOC_UNDEF, /* Not used; catches errors */
+ LOC_CONST, /* Value is constant int */
+ LOC_STATIC, /* Value is at fixed address */
+ LOC_REGISTER, /* Value is in register */
+ LOC_ARG, /* Value is at spec'd position in arglist */
+ LOC_LOCAL, /* Value is at spec'd pos in stack frame */
+ LOC_TYPEDEF, /* Value not used; definition in SYMBOL_TYPE
+ Symbols in the namespace STRUCT_NAMESPACE
+ all have this class. */
+ LOC_LABEL, /* Value is address in the code */
+ LOC_BLOCK, /* Value is address of a `struct block'.
+ Function names have this class. */
+ LOC_EXTERNAL, /* Value is at address not in this compilation.
+ This is used for .comm symbols
+ and for extern symbols within functions.
+ Inside GDB, this is changed to LOC_STATIC once the
+ real address is obtained from a loader symbol. */
+ LOC_CONST_BYTES /* Value is a constant byte-sequence. */
+};
+
+struct symbol
+{
+ /* Symbol name */
+ char *name;
+ /* Name space code. */
+ enum namespace namespace;
+ /* Address class */
+ enum address_class class;
+ /* Data type of value */
+ struct type *type;
+ /* constant value, or address if static, or register number,
+ or offset in arguments, or offset in stack frame. */
+ union
+ {
+ long value;
+ struct block *block; /* for LOC_BLOCK */
+ char *bytes; /* for LOC_CONST_BYTES */
+ }
+ value;
+};
+
+/* Source-file information.
+ This describes the relation between source files and line numbers
+ and addresses in the program text. */
+
+struct sourcevector
+{
+ int length; /* Number of source files described */
+ struct source *source[1]; /* Descriptions of the files */
+};
+
+/* Line number and address of one line. */
+
+struct line
+{
+ int linenum;
+ int address;
+};
+
+/* All the information on one source file. */
+
+struct source
+{
+ char *name; /* Name of file */
+ int nlines; /* Number of lines that follow */
+ struct line lines[1]; /* Information on each line */
+};
diff --git a/gnu/usr.bin/ld/warnings.c b/gnu/usr.bin/ld/warnings.c
new file mode 100644
index 0000000..6ae1be9
--- /dev/null
+++ b/gnu/usr.bin/ld/warnings.c
@@ -0,0 +1,719 @@
+/*
+ * $Id: warnings.c,v 1.7 1994/06/14 12:45:41 csgr Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <ar.h>
+#include <ranlib.h>
+#include <a.out.h>
+#include <stab.h>
+#include <string.h>
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "ld.h"
+
+/*
+ * Print the filename of ENTRY on OUTFILE (a stdio stream),
+ * and then a newline.
+ */
+
+void
+prline_file_name (entry, outfile)
+ struct file_entry *entry;
+ FILE *outfile;
+{
+ print_file_name (entry, outfile);
+ fprintf (outfile, "\n");
+}
+
+/*
+ * Print the filename of ENTRY on OUTFILE (a stdio stream).
+ */
+
+void
+print_file_name (entry, outfile)
+ struct file_entry *entry;
+ FILE *outfile;
+{
+ if (entry == NULL) {
+ fprintf (outfile, "NULL");
+ }
+
+ if (entry->superfile) {
+ print_file_name (entry->superfile, outfile);
+ fprintf (outfile, "(%s)", entry->filename);
+ } else
+ fprintf (outfile, "%s", entry->filename);
+}
+
+/*
+ * Return the filename of entry as a string (malloc'd for the purpose)
+ */
+
+char *
+get_file_name (entry)
+ struct file_entry *entry;
+{
+ char *result, *supfile;
+
+ if (entry == NULL) {
+ return (char *)strdup("NULL");
+ }
+
+ if (entry->superfile) {
+ supfile = get_file_name(entry->superfile);
+ result = (char *)
+ xmalloc(strlen(supfile) + strlen(entry->filename) + 3);
+ (void)sprintf(result, "%s(%s)", supfile, entry->filename);
+ free(supfile);
+
+ } else {
+ result = (char *)xmalloc(strlen(entry->filename) + 1);
+ strcpy(result, entry->filename);
+ }
+ return result;
+}
+
+/* Print a complete or partial map of the output file. */
+
+static void describe_file_sections __P((struct file_entry *, FILE *));
+static void list_file_locals __P((struct file_entry *, FILE *));
+
+void
+print_symbols(outfile)
+ FILE *outfile;
+{
+ fprintf(outfile, "\nFiles:\n\n");
+ each_file(describe_file_sections, (void *)outfile);
+
+ fprintf(outfile, "\nGlobal symbols:\n\n");
+ FOR_EACH_SYMBOL(i, sp) {
+ if (sp->defined == (N_UNDF|N_EXT))
+ fprintf(outfile, " %s: common, length %#x\n",
+ sp->name, sp->common_size);
+ if (!(sp->flags & GS_REFERENCED))
+ fprintf(outfile, " %s: unreferenced\n", sp->name);
+ else if (sp->so_defined)
+ fprintf(outfile, " %s: sodefined\n", sp->name);
+ else if (!sp->defined)
+ fprintf(outfile, " %s: undefined\n", sp->name);
+ else
+ fprintf(outfile, " %s: %#x, size %#x\n",
+ sp->name, sp->value, sp->size);
+ } END_EACH_SYMBOL;
+
+ each_file(list_file_locals, (void *)outfile);
+}
+
+static void
+describe_file_sections(entry, outfile)
+ struct file_entry *entry;
+ FILE *outfile;
+{
+ fprintf(outfile, " ");
+ print_file_name(entry, outfile);
+ if (entry->flags & (E_JUST_SYMS | E_DYNAMIC))
+ fprintf(outfile, " symbols only\n");
+ else
+ fprintf(outfile, " text %x(%x), data %x(%x), bss %x(%x) hex\n",
+ entry->text_start_address, entry->header.a_text,
+ entry->data_start_address, entry->header.a_data,
+ entry->bss_start_address, entry->header.a_bss);
+}
+
+static void
+list_file_locals (entry, outfile)
+ struct file_entry *entry;
+ FILE *outfile;
+{
+ struct localsymbol *lsp, *lspend;
+
+ entry->strings = (char *) alloca (entry->string_size);
+ read_entry_strings (file_open (entry), entry);
+
+ fprintf (outfile, "\nLocal symbols of ");
+ print_file_name (entry, outfile);
+ fprintf (outfile, ":\n\n");
+
+ lspend = entry->symbols + entry->nsymbols;
+ for (lsp = entry->symbols; lsp < lspend; lsp++) {
+ register struct nlist *p = &lsp->nzlist.nlist;
+ /*
+ * If this is a definition,
+ * update it if necessary by this file's start address.
+ */
+ if (!(p->n_type & (N_STAB | N_EXT)))
+ fprintf(outfile, " %s: 0x%x\n",
+ entry->strings + p->n_un.n_strx, p->n_value);
+ }
+
+ entry->strings = 0; /* All done with them. */
+}
+
+
+/* Static vars for do_warnings and subroutines of it */
+static int list_unresolved_refs; /* List unresolved refs */
+static int list_warning_symbols; /* List warning syms */
+static int list_multiple_defs; /* List multiple definitions */
+
+static struct line_debug_entry *init_debug_scan __P((int, struct file_entry *));
+static int next_debug_entry __P((int, struct line_debug_entry *));
+
+/*
+ * Structure for communication between do_file_warnings and it's
+ * helper routines. Will in practice be an array of three of these:
+ * 0) Current line, 1) Next line, 2) Source file info.
+ */
+struct line_debug_entry
+{
+ int line;
+ char *filename;
+ struct localsymbol *sym;
+};
+
+/*
+ * Helper routines for do_file_warnings.
+ */
+
+/* Return an integer less than, equal to, or greater than 0 as per the
+ relation between the two relocation entries. Used by qsort. */
+
+static int
+relocation_entries_relation(rel1, rel2)
+ struct relocation_info *rel1, *rel2;
+{
+ return RELOC_ADDRESS(rel1) - RELOC_ADDRESS(rel2);
+}
+
+/* Moves to the next debugging symbol in the file. USE_DATA_SYMBOLS
+ determines the type of the debugging symbol to look for (DSLINE or
+ SLINE). STATE_POINTER keeps track of the old and new locatiosn in
+ the file. It assumes that state_pointer[1] is valid; ie
+ that it.sym points into some entry in the symbol table. If
+ state_pointer[1].sym == 0, this routine should not be called. */
+
+static int
+next_debug_entry(use_data_symbols, state_pointer)
+ register int use_data_symbols;
+ /* Next must be passed by reference! */
+ struct line_debug_entry state_pointer[3];
+{
+ register struct line_debug_entry
+ *current = state_pointer,
+ *next = state_pointer + 1,
+ /* Used to store source file */
+ *source = state_pointer + 2;
+
+ struct file_entry *entry = (struct file_entry *) source->sym;
+ struct localsymbol *endp = entry->symbols + entry->nsymbols;
+
+
+ current->sym = next->sym;
+ current->line = next->line;
+ current->filename = next->filename;
+
+ while (++(next->sym) < endp) {
+
+ struct nlist *np = &next->sym->nzlist.nlist;
+
+ /*
+ * n_type is a char, and N_SOL, N_EINCL and N_BINCL are > 0x80,
+ * so may look negative...therefore, must mask to low bits
+ */
+ switch (np->n_type & 0xff) {
+ case N_SLINE:
+ if (use_data_symbols) continue;
+ next->line = np->n_desc;
+ return 1;
+ case N_DSLINE:
+ if (!use_data_symbols) continue;
+ next->line = np->n_desc;
+ return 1;
+#ifdef HAVE_SUN_STABS
+ case N_EINCL:
+ next->filename = source->filename;
+ continue;
+#endif
+ case N_SO:
+ source->filename = np->n_un.n_strx + entry->strings;
+ source->line++;
+#ifdef HAVE_SUN_STABS
+ case N_BINCL:
+#endif
+ case N_SOL:
+ next->filename = np->n_un.n_strx + entry->strings;
+ default:
+ continue;
+ }
+ }
+ next->sym = (struct localsymbol *)0;
+ return 0;
+}
+
+/*
+ * Create a structure to save the state of a scan through the debug symbols.
+ * USE_DATA_SYMBOLS is set if we should be scanning for DSLINE's instead of
+ * SLINE's. entry is the file entry which points at the symbols to use.
+ */
+
+static struct line_debug_entry *
+init_debug_scan(use_data_symbols, entry)
+ int use_data_symbols;
+ struct file_entry *entry;
+{
+ struct localsymbol *lsp;
+ struct line_debug_entry *state_pointer = (struct line_debug_entry *)
+ xmalloc(3 * sizeof(struct line_debug_entry));
+
+ register struct line_debug_entry
+ *current = state_pointer,
+ *next = state_pointer + 1,
+ *source = state_pointer + 2; /* Used to store source file */
+
+
+ for (lsp = entry->symbols; lsp < entry->symbols+entry->nsymbols; lsp++)
+ if (lsp->nzlist.nlist.n_type == N_SO)
+ break;
+
+ if (lsp >= entry->symbols + entry->nsymbols) {
+ /* I believe this translates to "We lose" */
+ current->filename = next->filename = entry->filename;
+ current->line = next->line = -1;
+ current->sym = next->sym = (struct localsymbol *) 0;
+ return state_pointer;
+ }
+ next->line = source->line = 0;
+ next->filename = source->filename
+ = (lsp->nzlist.nlist.n_un.n_strx + entry->strings);
+ source->sym = (struct localsymbol *) entry;
+ next->sym = lsp;
+
+ /* To setup next */
+ next_debug_entry(use_data_symbols, state_pointer);
+
+ if (!next->sym) { /* No line numbers for this section; */
+ /* setup output results as appropriate */
+ if (source->line) {
+ current->filename = source->filename = entry->filename;
+ current->line = -1; /* Don't print lineno */
+ } else {
+ current->filename = source->filename;
+ current->line = 0;
+ }
+ return state_pointer;
+ }
+ /* To setup current */
+ next_debug_entry(use_data_symbols, state_pointer);
+
+ return state_pointer;
+}
+
+/*
+ * Takes an ADDRESS (in either text or data space) and a STATE_POINTER which
+ * describes the current location in the implied scan through the debug
+ * symbols within the file which ADDRESS is within, and returns the source
+ * line number which corresponds to ADDRESS.
+ */
+
+static int
+address_to_line(address, state_pointer)
+ unsigned long address;
+/* Next must be passed by reference! */
+ struct line_debug_entry state_pointer[3];
+{
+ struct line_debug_entry *current, *next, *tmp_pointer;
+ int use_data_symbols;
+
+ current = state_pointer;
+ next = state_pointer + 1;
+
+ if (next->sym)
+ use_data_symbols =
+ (next->sym->nzlist.nlist.n_type & N_TYPE) == N_DATA;
+ else
+ return current->line;
+
+ /* Go back to the beginning if we've already passed it. */
+ if (current->sym->nzlist.nlist.n_value > address) {
+ tmp_pointer = init_debug_scan(use_data_symbols,
+ (struct file_entry *)
+ ((state_pointer + 2)->sym));
+ state_pointer[0] = tmp_pointer[0];
+ state_pointer[1] = tmp_pointer[1];
+ state_pointer[2] = tmp_pointer[2];
+ free(tmp_pointer);
+ }
+
+ /* If we're still in a bad way, return -1, meaning invalid line. */
+ if (current->sym->nzlist.nlist.n_value > address)
+ return -1;
+
+ while (next->sym
+ && next->sym->nzlist.nlist.n_value <= address
+ && next_debug_entry(use_data_symbols, state_pointer));
+
+ return current->line;
+}
+
+
+/* Macros for manipulating bitvectors. */
+#define BIT_SET_P(bv, index) ((bv)[(index) >> 3] & 1 << ((index) & 0x7))
+#define SET_BIT(bv, index) ((bv)[(index) >> 3] |= 1 << ((index) & 0x7))
+
+/*
+ * This routine will scan through the relocation data of file ENTRY, printing
+ * out references to undefined symbols and references to symbols defined in
+ * files with N_WARNING symbols. If DATA_SEGMENT is non-zero, it will scan
+ * the data relocation segment (and use N_DSLINE symbols to track line
+ * number); otherwise it will scan the text relocation segment. Warnings
+ * will be printed on the output stream OUTFILE. Eventually, every nlist
+ * symbol mapped through will be marked in the NLIST_BITVECTOR, so we don't
+ * repeat ourselves when we scan the nlists themselves.
+ */
+
+static void
+do_relocation_warnings(entry, data_segment, outfile, nlist_bitvector)
+ struct file_entry *entry;
+ int data_segment;
+ FILE *outfile;
+ unsigned char *nlist_bitvector;
+{
+ struct relocation_info *reloc, *reloc_start =
+ data_segment ? entry->datarel : entry->textrel;
+
+ int reloc_size = (data_segment ? entry->ndatarel : entry->ntextrel);
+ int start_of_segment = (data_segment ?
+ entry->data_start_address :
+ entry->text_start_address);
+ struct localsymbol *start_of_syms = entry->symbols;
+ struct line_debug_entry *state_pointer =
+ init_debug_scan(data_segment != 0, entry);
+
+ register struct line_debug_entry *current = state_pointer;
+
+ /* Assigned to generally static values; should not be written into. */
+ char *errfmt;
+
+ /*
+ * Assigned to alloca'd values cand copied into; should be freed when
+ * done.
+ */
+ char *errmsg;
+ int invalidate_line_number;
+
+ /*
+ * We need to sort the relocation info here. Sheesh, so much effort
+ * for one lousy error optimization.
+ */
+
+ qsort(reloc_start, reloc_size, sizeof(struct relocation_info),
+ relocation_entries_relation);
+
+ for (reloc = reloc_start;
+ reloc < (reloc_start + reloc_size);
+ reloc++) {
+ register struct localsymbol *lsp;
+ register symbol *g;
+
+ /*
+ * If the relocation isn't resolved through a symbol,
+ * continue
+ */
+ if (!RELOC_EXTERN_P(reloc))
+ continue;
+
+ lsp = &entry->symbols[RELOC_SYMBOL(reloc)];
+
+ /*
+ * Local symbols shouldn't ever be used by relocation info,
+ * so the next should be safe. This is, of course, wrong.
+ * References to local BSS symbols can be the targets of
+ * relocation info, and they can (must) be resolved through
+ * symbols. However, these must be defined properly, (the
+ * assembler would have caught it otherwise), so we can
+ * ignore these cases.
+ */
+
+ if ((g = lsp->symbol) == NULL)
+ continue;
+
+ if (!(lsp->nzlist.nz_type & N_EXT) &&
+ !SET_ELEMENT_P(lsp->nzlist.nz_type)) {
+ warnx("internal error: `%s' N_EXT not set", g->name);
+ continue;
+ }
+
+ errmsg = 0;
+
+ if (!g->defined && !g->so_defined && list_unresolved_refs) {
+ /* Mark as being noted by relocation warning pass. */
+ SET_BIT(nlist_bitvector, lsp - start_of_syms);
+
+ if (g->undef_refs >= MAX_UREFS_PRINTED)
+ /* Listed too many */
+ continue;
+
+ /* Undefined symbol which we should mention */
+
+ if (++(g->undef_refs) == MAX_UREFS_PRINTED) {
+ errfmt = "More undefined symbol %s refs follow";
+ invalidate_line_number = 1;
+ } else {
+ errfmt =
+ "Undefined symbol `%s' referenced from %s segment";
+ invalidate_line_number = 0;
+ }
+ } else { /* Defined */
+ /* Potential symbol warning here */
+ if (!g->warning)
+ continue;
+
+ /* Mark as being noted by relocation warning pass. */
+ SET_BIT(nlist_bitvector, lsp - start_of_syms);
+
+ errfmt = 0;
+ errmsg = g->warning;
+ invalidate_line_number = 0;
+ }
+
+
+ /* If errfmt == 0, errmsg has already been defined. */
+ if (errfmt != 0) {
+ char *nm;
+
+ nm = g->name;
+ errmsg = (char *)
+ xmalloc(strlen(errfmt) + strlen(nm) + 1);
+ sprintf(errmsg, errfmt, nm, data_segment?"data":"text");
+ if (nm != g->name)
+ free(nm);
+ }
+ address_to_line(RELOC_ADDRESS(reloc) + start_of_segment,
+ state_pointer);
+
+ if (current->line >= 0)
+ fprintf(outfile, "%s:%d: %s\n", current->filename,
+ invalidate_line_number ? 0 : current->line, errmsg);
+ else
+ fprintf(outfile, "%s: %s\n", current->filename, errmsg);
+
+ if (errfmt != 0)
+ free(errmsg);
+ }
+
+ free(state_pointer);
+}
+
+/*
+ * Print on OUTFILE a list of all warnings generated by references and/or
+ * definitions in the file ENTRY. List source file and line number if
+ * possible, just the .o file if not.
+ */
+
+void
+do_file_warnings (entry, outfile)
+ struct file_entry *entry;
+ FILE *outfile;
+{
+ int number_of_syms = entry->nsymbols;
+ unsigned char *nlist_bitvector = (unsigned char *)
+ alloca ((number_of_syms >> 3) + 1);
+ struct line_debug_entry *text_scan, *data_scan;
+ int i;
+ char *errfmt, *file_name;
+ int line_number;
+ int dont_allow_symbol_name;
+
+ bzero (nlist_bitvector, (number_of_syms >> 3) + 1);
+
+ /* Read in the files strings if they aren't available */
+ if (!entry->strings) {
+ int desc;
+
+ entry->strings = (char *)alloca(entry->string_size);
+ desc = file_open(entry);
+ read_entry_strings(desc, entry);
+ }
+
+ if (!(entry->flags & E_DYNAMIC)) {
+ /* Do text warnings based on a scan through the reloc info. */
+ do_relocation_warnings(entry, 0, outfile, nlist_bitvector);
+
+ /* Do data warnings based on a scan through the reloc info. */
+ do_relocation_warnings(entry, 1, outfile, nlist_bitvector);
+ }
+
+ /*
+ * Scan through all of the nlist entries in this file and pick up
+ * anything that the scan through the relocation stuff didn't.
+ */
+ text_scan = init_debug_scan(0, entry);
+ data_scan = init_debug_scan(1, entry);
+
+ for (i = 0; i < number_of_syms; i++) {
+ struct nlist *s;
+ symbol *g;
+
+ g = entry->symbols[i].symbol;
+ s = &entry->symbols[i].nzlist.nlist;
+
+ /*
+ * XXX This is a temporary fence to correct an
+ * incorrect assumption made in the case of symbols
+ * which do not have entries in the (global)
+ * symbol table.
+ */
+ if(g == NULL)
+ continue;
+
+ if (g == NULL)
+ continue;
+
+ if (!(s->n_type & N_EXT) && !SET_ELEMENT_P(s->n_type)) {
+ warnx("internal error: `%s' N_EXT not set", g->name);
+ continue;
+ }
+
+ if (!(g->flags & GS_REFERENCED)) {
+#if 0
+ /* Check for undefined shobj symbols */
+ struct localsymbol *lsp;
+ register int type;
+
+ for (lsp = g->sorefs; lsp; lsp = lsp->next) {
+ type = lsp->nzlist.nz_type;
+ if ((type & N_EXT) &&
+ type != (N_UNDF | N_EXT)) {
+ break;
+ }
+ }
+ if (type == (N_UNDF | N_EXT)) {
+ fprintf(stderr,
+ "Undefined symbol %s referenced from %s\n",
+ g->name,
+ get_file_name(entry));
+ }
+#endif
+ continue;
+ }
+
+ dont_allow_symbol_name = 0;
+
+ if (list_multiple_defs && g->mult_defs) {
+ errfmt = "Definition of symbol `%s' (multiply defined)";
+ switch (s->n_type) {
+
+ case N_TEXT | N_EXT:
+ line_number =
+ address_to_line(s->n_value, text_scan);
+ file_name = text_scan[0].filename;
+ break;
+
+ case N_DATA | N_EXT:
+ line_number =
+ address_to_line(s->n_value, data_scan);
+ file_name = data_scan[0].filename;
+ break;
+
+ case N_SETA | N_EXT:
+ case N_SETT | N_EXT:
+ case N_SETD | N_EXT:
+ case N_SETB | N_EXT:
+ if (g->mult_defs == 2)
+ continue;
+ errfmt =
+ "First set element definition of symbol %s (multiply defined)";
+ line_number = -1;
+ break;
+
+ default:
+printf("multiply defined: %s, type %#x\n", g->name, s->n_type);
+ /* Don't print out multiple defs at references.*/
+ continue;
+ }
+
+ } else if (BIT_SET_P(nlist_bitvector, i)) {
+ continue;
+ } else if (list_unresolved_refs &&
+ !g->defined && !g->so_defined) {
+
+ if (g->undef_refs >= MAX_UREFS_PRINTED)
+ continue;
+
+ if (++(g->undef_refs) == MAX_UREFS_PRINTED)
+ errfmt = "More undefined \"%s\" refs follow";
+ else
+ errfmt = "Undefined symbol \"%s\" referenced";
+ line_number = -1;
+ } else if (g->warning) {
+ /*
+ * There are two cases in which we don't want to do
+ * this. The first is if this is a definition instead
+ * do a reference. The second is if it's the reference
+ * used by the warning stabs itself.
+ */
+ if (s->n_type != (N_EXT | N_UNDF) ||
+ (i && (s-1)->n_type == N_WARNING))
+ continue;
+
+ errfmt = g->warning;
+ line_number = -1;
+ dont_allow_symbol_name = 1;
+ } else
+ continue;
+
+ if (line_number == -1)
+ fprintf(outfile, "%s: ", entry->filename);
+ else
+ fprintf(outfile, "%s:%d: ", file_name, line_number);
+
+ if (dont_allow_symbol_name)
+ fprintf(outfile, "%s", errfmt);
+ else
+ fprintf(outfile, errfmt, g->name);
+
+ fputc('\n', outfile);
+ }
+ free(text_scan);
+ free(data_scan);
+ entry->strings = 0; /* Since it will dissapear anyway. */
+}
+
+int
+do_warnings(outfile)
+ FILE *outfile;
+{
+ list_unresolved_refs = !relocatable_output &&
+ (undefined_global_sym_count || undefined_shobj_sym_count);
+ list_warning_symbols = warning_count;
+ list_multiple_defs = multiple_def_count != 0;
+
+ if (!(list_unresolved_refs ||
+ list_warning_symbols || list_multiple_defs))
+ /* No need to run this routine */
+ return 1;
+
+ if (entry_symbol && !entry_symbol->defined)
+ fprintf(outfile, "Undefined entry symbol %s\n",
+ entry_symbol->name);
+ each_file(do_file_warnings, (void *)outfile);
+
+ if (list_unresolved_refs || list_multiple_defs)
+ return 0;
+
+ return 1;
+}
+
diff --git a/gnu/usr.bin/ld/xbits.c b/gnu/usr.bin/ld/xbits.c
new file mode 100644
index 0000000..6374beb
--- /dev/null
+++ b/gnu/usr.bin/ld/xbits.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: xbits.c,v 1.2 1993/11/09 04:19:08 paul Exp $
+ */
+
+/*
+ * "Generic" byte-swap routines.
+ */
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include <ar.h>
+#include <ranlib.h>
+#include <a.out.h>
+#include <stab.h>
+#include <string.h>
+
+#include "ld.h"
+
+void
+swap_longs(lp, n)
+int n;
+long *lp;
+{
+ for (; n > 0; n--, lp++)
+ *lp = md_swap_long(*lp);
+}
+
+void
+swap_symbols(s, n)
+struct nlist *s;
+int n;
+{
+ for (; n; n--, s++) {
+ s->n_un.n_strx = md_swap_long(s->n_un.n_strx);
+ s->n_desc = md_swap_short(s->n_desc);
+ s->n_value = md_swap_long(s->n_value);
+ }
+}
+
+void
+swap_zsymbols(s, n)
+struct nzlist *s;
+int n;
+{
+ for (; n; n--, s++) {
+ s->nz_strx = md_swap_long(s->nz_strx);
+ s->nz_desc = md_swap_short(s->nz_desc);
+ s->nz_value = md_swap_long(s->nz_value);
+ s->nz_size = md_swap_long(s->nz_size);
+ }
+}
+
+
+void
+swap_ranlib_hdr(rlp, n)
+struct ranlib *rlp;
+int n;
+{
+ for (; n; n--, rlp++) {
+ rlp->ran_un.ran_strx = md_swap_long(rlp->ran_un.ran_strx);
+ rlp->ran_off = md_swap_long(rlp->ran_off);
+ }
+}
+
+void
+swap__dynamic(dp)
+struct _dynamic *dp;
+{
+ dp->d_version = md_swap_long(dp->d_version);
+ dp->d_debug = (struct so_debug *)md_swap_long((long)dp->d_debug);
+ dp->d_un.d_sdt = (struct section_dispatch_table *)
+ md_swap_long((long)dp->d_un.d_sdt);
+ dp->d_entry = (struct ld_entry *)md_swap_long((long)dp->d_entry);
+}
+
+void
+swap_section_dispatch_table(sdp)
+struct section_dispatch_table *sdp;
+{
+ swap_longs((long *)sdp, sizeof(*sdp)/sizeof(long));
+}
+
+void
+swap_so_debug(ddp)
+struct so_debug *ddp;
+{
+ swap_longs((long *)ddp, sizeof(*ddp)/sizeof(long));
+}
+
+void
+swapin_sod(sodp, n)
+struct sod *sodp;
+int n;
+{
+ unsigned long bits;
+
+ for (; n; n--, sodp++) {
+ sodp->sod_name = md_swap_long(sodp->sod_name);
+ sodp->sod_major = md_swap_short(sodp->sod_major);
+ sodp->sod_minor = md_swap_short(sodp->sod_minor);
+ sodp->sod_next = md_swap_long(sodp->sod_next);
+ bits = ((unsigned long *)sodp)[1];
+ sodp->sod_library = ((bits >> 24) & 1);
+ }
+}
+
+void
+swapout_sod(sodp, n)
+struct sod *sodp;
+int n;
+{
+ unsigned long bits;
+
+ for (; n; n--, sodp++) {
+ sodp->sod_name = md_swap_long(sodp->sod_name);
+ sodp->sod_major = md_swap_short(sodp->sod_major);
+ sodp->sod_minor = md_swap_short(sodp->sod_minor);
+ sodp->sod_next = md_swap_long(sodp->sod_next);
+ bits = (unsigned long)(sodp->sod_library) << 24;
+ ((unsigned long *)sodp)[1] = bits;
+ }
+}
+
+void
+swap_rrs_hash(fsp, n)
+struct rrs_hash *fsp;
+int n;
+{
+ for (; n; n--, fsp++) {
+ fsp->rh_symbolnum = md_swap_long(fsp->rh_symbolnum);
+ fsp->rh_next = md_swap_long(fsp->rh_next);
+ }
+}
+
diff --git a/gnu/usr.bin/man/COPYING b/gnu/usr.bin/man/COPYING
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/gnu/usr.bin/man/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/gnu/usr.bin/man/Makefile b/gnu/usr.bin/man/Makefile
new file mode 100644
index 0000000..0e7e506
--- /dev/null
+++ b/gnu/usr.bin/man/Makefile
@@ -0,0 +1,10 @@
+# Master Makefile for man, manpath, apropos, whatis, and makewhatis
+#
+# You may distribute under the terms of the GNU General Public
+# License as specified in the README file that comes with the man 1.0
+# distribution.
+#
+
+SUBDIR = lib man manpath apropos whatis makewhatis catman
+
+.include <bsd.subdir.mk>
diff --git a/gnu/usr.bin/man/Makefile.inc b/gnu/usr.bin/man/Makefile.inc
new file mode 100644
index 0000000..8ea188e
--- /dev/null
+++ b/gnu/usr.bin/man/Makefile.inc
@@ -0,0 +1,23 @@
+#
+# Set a bunch of things to hardcoded paths so that we don't accidently
+# pick up a user's own version of some utility and hose ourselves.
+#
+BINDIR?= /usr/bin
+libdir= /etc
+bindir= ${BINDIR}
+pager= more -cs
+manpath_config_file= /etc/manpath.config
+troff= /usr/bin/groff -Tps -man
+nroff= /usr/bin/groff -Tascii -man
+apropos= /usr/bin/apropos
+whatis= /usr/bin/whatis
+neqn= /usr/bin/eqn -Tascii
+tbl= /usr/bin/tbl
+col= /usr/bin/col
+vgrind= /usr/bin/vgrind
+refer= /usr/bin/refer
+grap= # no grap
+pic= /usr/bin/pic
+zcat= /usr/bin/zcat
+compress= gzip -c
+compext= .gz
diff --git a/gnu/usr.bin/man/README b/gnu/usr.bin/man/README
new file mode 100644
index 0000000..d8fc4d4
--- /dev/null
+++ b/gnu/usr.bin/man/README
@@ -0,0 +1,134 @@
+README file for man(1).
+
+This is a replacement for Un*x man(1), apropos(1), whatis(1), and
+manpath(1). It has all kinds of neat features that other versions of
+man don't, including support for multiple man page directory trees,
+preformatted man pages, and troff. It is provided without any
+warranty whatever. I hope you find it useful.
+
+This program is not a GNU product but it is distributed under the
+terms of the GNU copyleft which is described in the file COPYING.
+
+There is a solution written in perl which is probably superior in
+every way, but, like me, you may prefer this one anyway.
+:-)
+
+If you compile with support for preformatted man pages, man(1) will
+try to update the preformatted page if the man page source is newer.
+
+If you compile with support for troff, you can say things like
+`man -t foo | psdit > foo.ps' and have fabulous printed documentation
+as well.
+
+I have resisted the temptation to handle all the bizarre ways various
+vendors have of organizing man pages. This version of man assumes
+that directory trees have the structure:
+
+ .../man
+ /manSect
+ /foo.Sect*
+ ...
+ /catSect
+ /foo.Sect*
+ ...
+
+where Sect is some number or string and should be listed in the set of
+sections to be searched. It is not necessary to have both the cat*
+and man* subdirectories, but you must have at least one. :-)
+
+
+INSTALLATION
+
+1. Run configure. This will grope around your system a bit and then
+ ask you a number of questions. It will create a Makefile from the
+ file Makefile.in, and a config.h file from config.h.in. You may
+ have to do some fine tuning to get things to work exactly right on
+ your system. If you do, I'd like to know what changes you had to
+ make to get things working.
+
+2. Edit the manpath.config file. This determines the system-wide
+ mappings for bin directories and man page directories.
+
+3. Do a `make all', try it out, and then if you're happy with that, do
+ a `make install'. You don't need to be root to use this set of
+ programs.
+
+4. Install the whatis database(s) by running makewhatis. If you want
+ to keep things absolutely current, you'll need to run this whenever
+ you add new man pages. You might want to add an entry in your
+ crontab.
+
+BUGS
+
+If you find one of these, please tell me about it. If you have a fix,
+that's even better. If not, I can't guarantee that I'll fix it, but I
+would like to know about them.
+
+John Eaton
+jwe@che.utexas.edu
+Department of Chemical Engineering
+The University of Texas at Austin
+Austin, Texas 78712
+
+
+CHANGES
+
+Partial list of changes since version 1.0:
+
+Installation made easier (this was the intent anyway) with the
+introduction of a configure script.
+
+Commands like `man 3f intro' handled properly when the name of the
+file we want is something like .../man3/intro.3f.
+
+Man can now run set uid to a special user so formatted man pages don't
+have to be world writable.
+
+Man now works with compressed (.Z) frozen (.F) and yabba (.Y) cat
+files. Frozen files are compressed files using freeze/melt, some
+combination of LZW and tree coding. Sources for it came out on
+comp.sources.misc or alt.sources or ... a few months ago. Yabba files
+are compressed using yabba/unyabba, a data compression scheme posted
+to alt.sources by Dan Bernstein.
+
+Man now uses a more reasonable default for the search order:
+1, n, l, 6, 8, 2, 3, 4, 5, 7, p, o
+
+Man now allows for user-definable section search order via -S or
+MANSECT.
+
+Glob.c can work even if you don't have alloca, and works properly on
+Suns with the Sun C compiler.
+
+There is now a way to automatically to run preprocessors like the Sun
+man program. The first line of the man page indicates which
+preprocessors should be run:
+
+ If the first line is a string of the form:
+
+ '\" X
+
+ where X is separated from the the `"' by a single SPACE and
+ consists of any combination of characters in the following
+ list, man pipes its input to troff(1) or nroff(1) through
+ the corresponding preprocessors.
+
+ e eqn(1), or neqn for nroff
+ g grap(1)
+ p pic(1)
+ r refer(1)
+ t tbl(1), and col(1V) for nroff
+ v vgrind(1)
+
+Preprocessors may also be set on the command line with -p or from the
+environment with MANROFFSEQ.
+
+The tbl preprocessor is run by default.
+
+Manpath now stat()'s the directories in MANPATH to avoid including
+directories that don't exist.
+
+The output of apropos and whatis are now piped through PAGER.
+
+There is a new option to show where you would find a man page
+(-w option) and in what order (-w with -a).
diff --git a/gnu/usr.bin/man/TODO b/gnu/usr.bin/man/TODO
new file mode 100644
index 0000000..19060ad
--- /dev/null
+++ b/gnu/usr.bin/man/TODO
@@ -0,0 +1,123 @@
+Things that would be nice but aren't really necessary:
+
+0. Update the documentation.
+
+XX Come up with an easier way to install this thing. There are now
+ lots of options and dependent flags to set. Should I worry too
+ much about this?
+
+XX Properly handle commands like `man 3f intro' when the name of the
+ file we want is something like .../man3/intro.3f. The way this is
+ done right now seems sort of kludgey but it mostly works. See
+ man.c for details.
+
+2. Malloc everything instead of having fixed limits... Or at least
+ check the limits everywhere. If you're paranoid about this, make
+ the limits big (famous last words: really, there aren't that many
+ things that could go wrong :-).
+
+3. Try to do a little better job of memory management. There are a
+ lot of little temporary strings that are malloc'd and never freed.
+ This is probably ok for a standalone program but not so good if
+ you wanted to call man() from another program.
+
+XX Come up with a clear view of the cat directory file permissions
+ problem. What's a good solution, other than having man run setuid
+ to some special user? (Make directories writable by all, cat
+ files 666.)
+
+XX Allow a compile time option that makes man run setuid to some
+ other user that owns all the cat pages, so that they don't have to
+ be world writable.
+
+XX Allow man to deal with compressed (.Z) frozen (.F) and yabba (.Y)
+ cat files. Frozen files are compressed files using freeze/melt,
+ some combination of LZW and tree coding. Sources for it came out
+ on comp.sources.misc or alt.sources or ... a few months ago.
+ Yabba files are compressed using yabba/unyabba, a data compression
+ scheme posted to alt.sources by Dan Bernstein.
+
+XX Choose a more reasonable default for the search order. Perhaps
+ this: 1, n, l, 6, 8, 2, 3, 4, 5, 7, p, o
+
+XX Fix glob.c so it doesn't need alloca, and/or fix it so that it can
+ work on a Sun:
+
+ #ifdef __GNUC__
+ #define alloca __builtin_alloca
+ #else /* !__GNUC__ */
+ #ifdef sparc
+ #include <alloca.h>
+ #endif /* sparc */
+ #endif /* __GNUC__ */
+
+XX Add some way to automatically to run preprocessors. The Sun man
+ program has a convention that the first line of the man page can
+ indicate which preprocessors should be run. Here's an excerpt from
+ its man page:
+
+ Preprocessing Manual Pages
+ If the first line is a string of the form:
+
+ '\" X
+
+ where X is separated from the the `"' by a single SPACE and
+ consists of any combination of characters in the following
+ list, man pipes its input to troff(1) or nroff(1) through
+ the corresponding preprocessors.
+
+ e eqn(1), or neqn for nroff
+ r refer(1)
+ t tbl(1), and col(1V) for nroff
+ v vgrind(1)
+
+ If eqn or neqn is invoked, it will automatically read the
+ file /usr/pub/eqnchar (see eqnchar(7)).
+
+XX Have manpath stat() the directories in MANPATH to avoid including
+ directories that don't exist. Some versions of man and whatis
+ complain when the directories (like /usr/new/man) don't exist.
+
+XX Pipe the output of apropos and whatis through a pager.
+
+XX I've been using your man(1) package for a while now and I ran into
+ a problem with the X man pages that use tbl commands. Is it
+ possible to configure your man(1) package to use a general command
+ string. For example, a user could set an environment variable:
+
+ setenv ROFFLINE 'pic $* | tbl | nroff -man'
+
+13. Fix makewhatis so that it can handle stuff like this (from the
+ Motif 1.1 man pages):
+
+ .TH XmRowColumn 3X "" "" "" ""
+ .SH NAME
+ .mc |
+ \fBXmRowColumn \(em the RowColumn widget class.\fP
+ .mc
+ .iX "XmRowColumn"
+ .iX "widget class" "RowColumn"
+ .sp 1
+ .SH SYNOPSIS
+
+14. Consider changing the format of the awk command's printf to use
+ "%s" instead of the standard 20.20s to accomodate the extra long
+ file names used by Motif. Maybe there's a better way to handle
+ this?
+
+15. Add ability to run man on a local file
+
+16. Handle per-tree tmac macros
+
+XX Allow user-definable section search order via -S or $MANSECT.
+ Thus programmers can get stty(3) before stty(1).
+
+XX Show all the places you would find a man page (-w option) and in
+ what order.
+
+19. Support for multi-char sections like man1m/*.1m or manavs/*.avs
+ (can I have a section that doesn't start with a numeral?)
+
+20. Implement man -K for regexp apropos
+
+21. An option to grep through all the man pages in $MANPATH
diff --git a/gnu/usr.bin/man/apropos/Makefile b/gnu/usr.bin/man/apropos/Makefile
new file mode 100644
index 0000000..bc6bdec
--- /dev/null
+++ b/gnu/usr.bin/man/apropos/Makefile
@@ -0,0 +1,44 @@
+# $Id: Makefile,v 1.7 1994/06/05 21:57:03 csgr Exp $
+
+.if exists(${.CURDIR}/obj)
+MAN1= ${.CURDIR}/obj/apropos.1
+TARG= ${.CURDIR}/obj/apropos
+.else
+MAN1= ${.CURDIR}/apropos.1
+TARG= ${.CURDIR}/apropos
+.endif
+
+MANDEPEND= ${MAN1}
+
+CLEANFILES+= ${TARG} ${MAN1}
+
+all: ${TARG} ${MAN1}
+
+depend rcsfreeze tags all:
+ @echo -n
+
+${TARG}: ${.CURDIR}/apropos.sh
+ sed -e 's,%libdir%,${libdir},' -e 's,%bindir%,${bindir},' \
+ -e 's,%pager%,${pager},' \
+ ${.CURDIR}/apropos.sh > ${.TARGET}
+
+${MAN1}: ${.CURDIR}/apropos.man
+ sed -e 's,%libdir%,${libdir},' -e 's,%bindir%,${bindir},' \
+ -e 's,%pager%,${pager},' -e 's,%troff%,${troff},' \
+ -e 's,%manpath_config_file%,${manpath_config_file},' \
+ ${.CURDIR}/apropos.man > ${.TARGET}
+
+install: ${TARG} maninstall
+ install -c -o bin -g bin -m 555 ${TARG} ${DESTDIR}/usr/bin
+
+.include "../Makefile.inc"
+
+.if make(maninstall) || make(install)
+.if !defined(NOMAN)
+.include <bsd.man.mk>
+.elif !target(maninstall)
+maninstall:
+.endif
+.endif
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/man/apropos/apropos b/gnu/usr.bin/man/apropos/apropos
new file mode 100644
index 0000000..8735e5f
--- /dev/null
+++ b/gnu/usr.bin/man/apropos/apropos
@@ -0,0 +1,64 @@
+#!/bin/sh
+#
+# apropos -- search the whatis database for keywords.
+#
+# Copyright (c) 1990, 1991, John W. Eaton.
+#
+# You may distribute under the terms of the GNU General Public
+# License as specified in the README file that comes with the man
+# distribution.
+#
+# John W. Eaton
+# jwe@che.utexas.edu
+# Department of Chemical Engineering
+# The University of Texas at Austin
+# Austin, Texas 78712
+
+PATH=/usr/local/bin:/bin:/usr/ucb:/usr/bin
+
+libdir=/etc
+
+if [ $# = 0 ]
+then
+ echo "usage: `basename $0` keyword ..."
+ exit 1
+fi
+
+manpath=`/usr/bin/manpath -q | tr : '\040'`
+
+if [ "$manpath" = "" ]
+then
+ echo "whatis: manpath is null"
+ exit 1
+fi
+
+if [ "$PAGER" = "" ]
+then
+ PAGER="/usr/gnu/bin/less -sC"
+fi
+
+while [ $1 ]
+do
+ found=0
+ for d in $manpath /usr/lib
+ do
+ if [ -f $d/whatis ]
+ then
+ grep -i "$1" $d/whatis
+ status=$?
+ if [ "$status" = "0" ]
+ then
+ found=1
+ fi
+ fi
+ done
+
+ if [ "$found" = "0" ]
+ then
+ echo "$1: nothing appropriate"
+ fi
+
+ shift
+done | $PAGER
+
+exit
diff --git a/gnu/usr.bin/man/apropos/apropos.1 b/gnu/usr.bin/man/apropos/apropos.1
new file mode 100644
index 0000000..3bb3e17
--- /dev/null
+++ b/gnu/usr.bin/man/apropos/apropos.1
@@ -0,0 +1,27 @@
+.\" Man page for apropos
+.\"
+.\" Copyright (c) 1990, 1991, John W. Eaton.
+.\"
+.\" You may distribute under the terms of the GNU General Public
+.\" License as specified in the README file that comes with the man 1.0
+.\" distribution.
+.\"
+.\" John W. Eaton
+.\" jwe@che.utexas.edu
+.\" Department of Chemical Engineering
+.\" The University of Texas at Austin
+.\" Austin, Texas 78712
+.\"
+.TH apropos 1 "Jan 15, 1991"
+.LO 1
+.SH NAME
+apropos \- search the whatis database for strings
+.SH SYNOPSIS
+.BI apropos
+keyword ...
+.SH DESCRIPTION
+apropos searches a set of database files containing short descriptions
+of system commands for keywords and displays the result on the
+standard output.
+.SH "SEE ALSO"
+whatis(1), man(1).
diff --git a/gnu/usr.bin/man/apropos/apropos.man b/gnu/usr.bin/man/apropos/apropos.man
new file mode 100644
index 0000000..3bb3e17
--- /dev/null
+++ b/gnu/usr.bin/man/apropos/apropos.man
@@ -0,0 +1,27 @@
+.\" Man page for apropos
+.\"
+.\" Copyright (c) 1990, 1991, John W. Eaton.
+.\"
+.\" You may distribute under the terms of the GNU General Public
+.\" License as specified in the README file that comes with the man 1.0
+.\" distribution.
+.\"
+.\" John W. Eaton
+.\" jwe@che.utexas.edu
+.\" Department of Chemical Engineering
+.\" The University of Texas at Austin
+.\" Austin, Texas 78712
+.\"
+.TH apropos 1 "Jan 15, 1991"
+.LO 1
+.SH NAME
+apropos \- search the whatis database for strings
+.SH SYNOPSIS
+.BI apropos
+keyword ...
+.SH DESCRIPTION
+apropos searches a set of database files containing short descriptions
+of system commands for keywords and displays the result on the
+standard output.
+.SH "SEE ALSO"
+whatis(1), man(1).
diff --git a/gnu/usr.bin/man/apropos/apropos.sh b/gnu/usr.bin/man/apropos/apropos.sh
new file mode 100644
index 0000000..070b848
--- /dev/null
+++ b/gnu/usr.bin/man/apropos/apropos.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+#
+# apropos -- search the whatis database for keywords.
+#
+# Copyright (c) 1990, 1991, John W. Eaton.
+#
+# You may distribute under the terms of the GNU General Public
+# License as specified in the README file that comes with the man
+# distribution.
+#
+# John W. Eaton
+# jwe@che.utexas.edu
+# Department of Chemical Engineering
+# The University of Texas at Austin
+# Austin, Texas 78712
+
+PATH=/usr/local/bin:/bin:/usr/ucb:/usr/bin
+
+libdir=%libdir%
+
+if [ $# = 0 ]
+then
+ echo "usage: `basename $0` keyword ..."
+ exit 1
+fi
+
+manpath=`%bindir%/manpath -q | tr : '\040'`
+
+if [ "$manpath" = "" ]
+then
+ echo "whatis: manpath is null"
+ exit 1
+fi
+
+if [ "$PAGER" = "" ]
+then
+ PAGER="%pager%"
+fi
+
+while [ $1 ]
+do
+ found=0
+ for d in $manpath /usr/lib
+ do
+ if [ -f $d/whatis ]
+ then
+ grep -i "$1" $d/whatis
+ status=$?
+ if [ "$status" = "0" ]
+ then
+ found=1
+ fi
+ fi
+ done
+
+ if [ "$found" = "0" ]
+ then
+ echo "$1: nothing appropriate"
+ fi
+
+ shift
+done | $PAGER
+
+exit
diff --git a/gnu/usr.bin/man/catman/Makefile b/gnu/usr.bin/man/catman/Makefile
new file mode 100644
index 0000000..f4bd03f
--- /dev/null
+++ b/gnu/usr.bin/man/catman/Makefile
@@ -0,0 +1,15 @@
+NOMAN= noman
+CLEANFILES= catman
+
+beforeinstall: catman
+ install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ catman ${DESTDIR}${BINDIR}
+
+.include <bsd.prog.mk>
+
+catman: catman.sh
+ sed -e 's,%compress%,${compress},' \
+ -e 's,%compext%,${compext},' \
+ -e 's,%zcat%,${zcat},' \
+ ${.CURDIR}/catman.sh > catman
+
diff --git a/gnu/usr.bin/man/catman/catman b/gnu/usr.bin/man/catman/catman
new file mode 100644
index 0000000..a2d16a1
--- /dev/null
+++ b/gnu/usr.bin/man/catman/catman
@@ -0,0 +1,36 @@
+#!/bin/sh
+# usage: sh catman
+# put the section numbers here:
+SECTIONS="1 2 3 4 5 6 7 8"
+MANDIR=/usr/share/man
+
+formatman()
+{
+ echo " "$1 "->" $*
+ (cd cat$section; rm -f $*)
+ nroff -man < man$section/$1 > cat$section/$1
+ catfile=$1; shift
+ while [ $# -gt 0 ]
+ do
+ ln cat$section/$catfile cat$section/$1
+ shift
+ done
+}
+
+cd $MANDIR
+for section in $SECTIONS
+do
+ echo formatting section $section ...
+
+ IFS=" "
+ allfiles=`ls -i1 man$section | sort | awk '{if (inode ~ $1) printf "/" $2;
+ else printf " " $2; inode = $1 } END {printf "\n"}'`
+ for files in $allfiles
+ do
+ IFS="/"
+ tfiles=`echo $files`
+ IFS=" "
+ formatman $tfiles
+ done
+done
+exit 0
diff --git a/gnu/usr.bin/man/catman/catman.sh b/gnu/usr.bin/man/catman/catman.sh
new file mode 100644
index 0000000..456cb57
--- /dev/null
+++ b/gnu/usr.bin/man/catman/catman.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+# usage: sh catman
+# put the section numbers here:
+SECTIONS="1 2 3 4 5 6 7 8"
+MANDIR=/usr/share/man
+
+formatman()
+{
+ suffix=`echo $1 | sed -e 's/.*\\.//'`
+ (cd cat$section; rm -f $*)
+ if [ ".$suffix" = "%compext%" ]; then
+ adds=
+ %zcat% man$section/$1 | nroff -man | %compress% > cat$section/$1$adds
+ else
+ adds=%compext%
+ nroff -man < man$section/$1 | %compress% > cat$section/$1$adds
+ fi
+ echo " "$* "->" $1$adds
+ catfile=$1$adds; shift
+ while [ $# -gt 0 ]
+ do
+ ln cat$section/$catfile cat$section/$1$adds
+ shift
+ done
+}
+
+cd $MANDIR
+for section in $SECTIONS
+do
+ echo formatting section $section ...
+
+ IFS=" "
+ allfiles=`ls -i1 man$section | sort | awk '{if (inode ~ $1) printf "/" $2;
+ else printf " " $2; inode = $1 } END {printf "\n"}'`
+ for files in $allfiles
+ do
+ IFS="/"
+ tfiles=`echo $files`
+ IFS=" "
+ formatman $tfiles
+ done
+done
+exit 0
diff --git a/gnu/usr.bin/man/lib/Makefile b/gnu/usr.bin/man/lib/Makefile
new file mode 100644
index 0000000..cb5fd63
--- /dev/null
+++ b/gnu/usr.bin/man/lib/Makefile
@@ -0,0 +1,33 @@
+LIB = man
+
+.if exists(${.CURDIR}/obj)
+CONFH= ${.CURDIR}/obj/config.h
+.else
+CONFH= ${.CURDIR}/config.h
+.endif
+
+NOPROFILE= YES
+
+CFLAGS+= -I${.CURDIR} -DSTDC_HEADERS -DPOSIX -DHAS_TROFF -DDO_COMPRESS -DALT_SYSTEMS
+CLEANFILES+= ${CONFH}
+SRCS = util.c gripes.c ${CONFH}
+
+libman.a:: ${CONFH}
+
+install:
+ @echo -n
+
+${CONFH}: ${.CURDIR}/config.h_dist ${.CURDIR}/../Makefile.inc
+ sed -e 's,%apropos%,${apropos},' -e 's,%whatis%,${whatis},' \
+ -e 's,%pager%,${pager},' -e 's,%troff%,${troff},' \
+ -e 's,%nroff%,${nroff},' -e 's,%tbl%,${tbl},' \
+ -e 's,%col%,${col},' -e 's,%pic%,${pic},' \
+ -e 's,%eqn%,${eqn},' -e 's,%neqn%,${neqn},' \
+ -e 's,%vgrind%,${vgrind},' -e 's,%refer%,${refer},' \
+ -e 's,%grap%,${grap},' -e 's,%zcat%,${zcat},' \
+ -e 's,%manpath_config_file%,${manpath_config_file},' \
+ -e 's,%compress%,${compress},' \
+ -e 's,%compext%,${compext},' \
+ ${.CURDIR}/config.h_dist > ${CONFH}
+
+.include <bsd.lib.mk>
diff --git a/gnu/usr.bin/man/lib/config.h b/gnu/usr.bin/man/lib/config.h
new file mode 100644
index 0000000..2c23168
--- /dev/null
+++ b/gnu/usr.bin/man/lib/config.h
@@ -0,0 +1,216 @@
+/*
+ * config.h
+ *
+ * If you haven't read the README file, now might be a good time.
+ *
+ * This file is edited by configure, so you shouldn't have to.
+ * If that doesn't work, edit this file to match your site.
+ *
+ * Sorry it's so long, but there are lots of things you might want to
+ * customize for your site.
+ *
+ * Copyright (c) 1990, 1991, John W. Eaton.
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the file COPYING that comes with the man
+ * distribution.
+ *
+ * John W. Eaton
+ * jwe@che.utexas.edu
+ * Department of Chemical Engineering
+ * The University of Texas at Austin
+ * Austin, Texas 78712
+ */
+
+#ifdef COMPRESS
+#define DO_COMPRESS
+#define DO_UNCOMPRESS
+#endif
+
+/*
+ * This is the size of a number of internal buffers. It should
+ * probably not be less than 512.
+ */
+#ifndef BUFSIZ
+#define BUFSIZ 1024
+#endif
+
+/*
+ * This should be at least the size of the longest path.
+ */
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif
+
+/*
+ * This is the maximum number of directories expected in the manpath.
+ */
+#ifndef MAXDIRS
+#define MAXDIRS 64
+#endif
+
+/*
+ * This is the name of the group that owns the preformatted man pages.
+ * If you are running man as a setgid program, you should make sure
+ * that all of the preformatted man pages and the directories that
+ * they live in are readable and writeable and owned by this group.
+ */
+#ifdef SECURE_MAN_UID
+#define MAN_USER ""
+#endif
+
+/*
+ * It's probably best to define absolute paths to all of these. If
+ * you don't, you'll be depending on the user's path to be correct
+ * when system () is called. This can result in weird behavior that's
+ * hard to track down, especially after you forget how this program
+ * works... If you don't have some of these programs, simply define
+ * them to be empty strings (i.e. ""). As a minimum, you must have
+ * nroff installed.
+ */
+#ifndef APROPOS
+#define APROPOS "/usr/bin/apropos"
+#endif
+
+#ifndef WHATIS
+#define WHATIS "/usr/bin/whatis"
+#endif
+
+#ifndef PAGER
+#define PAGER "/usr/gnu/bin/less -sC"
+#endif
+
+#ifdef HAS_TROFF
+#ifndef TROFF
+#define TROFF "/usr/bin/groff -Tps -man"
+#endif
+#endif
+
+#ifndef NROFF
+#define NROFF "/usr/bin/groff -Tascii -man"
+#endif
+
+#ifndef EQN
+#define EQN "/usr/bin/eqn -Tps"
+#endif
+
+#ifndef NEQN
+#define NEQN "/usr/bin/eqn -Tascii"
+#endif
+
+#ifndef TBL
+#define TBL "/usr/bin/tbl"
+#endif
+
+#ifndef COL
+#define COL "/usr/bin/col"
+#endif
+
+#ifndef VGRIND
+#define VGRIND "/usr/bin/vgrind"
+#endif
+
+#ifndef REFER
+#define REFER "/usr/bin/refer"
+#endif
+
+#ifndef GRAP
+#define GRAP ""
+#endif
+
+#ifndef PIC
+#define PIC "/usr/bin/pic"
+#endif
+
+/*
+ * Define the absolute path to the configuration file.
+ */
+#ifndef MAN_MAIN
+ static char config_file[] = "/etc/manpath.config";
+#endif
+
+/*
+ * Define the uncompression program(s) to use for those preformatted
+ * pages that end in the given character. If you add extras here, you
+ * may need to change man.c.
+ */
+#ifdef DO_UNCOMPRESS
+/* .F files */
+#define FCAT ""
+/* .Y files */
+#define YCAT ""
+/* .Z files */
+#define ZCAT "/usr/bin/zcat"
+#endif
+
+/*
+ * This is the standard program to use on this system for compressing
+ * pages once they have been formatted, and the character to tack on
+ * to the end of those files. The program listed is expected to read
+ * from the standard input and write compressed output to the standard
+ * output.
+ */
+#ifdef DO_COMPRESS
+#define COMPRESSOR ""
+#define COMPRESS_EXT ""
+#endif
+
+/*
+ * Define the standard manual sections. For example, if your man
+ * directory tree has subdirectories man1, man2, man3, mann,
+ * and man3foo, std_sections[] would have "1", "2", "3", "n", and
+ * "3foo". Directories are searched in the order they appear. Having
+ * extras isn't fatal, it just slows things down a bit.
+ *
+ * Note that this is just for directories to search. If you have
+ * files like .../man3/foobar.3Xtc, you don't need to have "3Xtc" in
+ * the list below -- this is handled separately, so that `man 3Xtc foobar',
+ * `man 3 foobar', and `man foobar' should find the file .../man3/foo.3Xtc,
+ * (assuming, of course, that there isn't a .../man1/foo.1 or somesuch
+ * that we would find first).
+ *
+ * Note that this list should be in the order that you want the
+ * directories to be searched. Is there a standard for this? What is
+ * the normal order? If anyone knows, please tell me!
+ */
+#ifndef MANPATH_MAIN
+ static char *std_sections[] =
+ {
+ "1", "n", "l", "6", "8", "2", "3", "4", "5", "7", "p", "o", NULL
+ };
+#endif
+
+/*
+ * Not all systems define these in stat.h.
+ */
+#ifndef S_IRUSR
+#define S_IRUSR 00400 /* read permission: owner */
+#endif
+#ifndef S_IWUSR
+#define S_IWUSR 00200 /* write permission: owner */
+#endif
+#ifndef S_IRGRP
+#define S_IRGRP 00040 /* read permission: group */
+#endif
+#ifndef S_IWGRP
+#define S_IWGRP 00020 /* write permission: group */
+#endif
+#ifndef S_IROTH
+#define S_IROTH 00004 /* read permission: other */
+#endif
+#ifndef S_IWOTH
+#define S_IWOTH 00002 /* write permission: other */
+#endif
+
+/*
+ * This is the mode used for formatted pages that we create. If you
+ * are using the setgid option, you should use 664. If you are not,
+ * you should use 666 and make the cat* directories mode 777.
+ */
+#ifndef CATMODE
+#ifdef SECURE_MAN_UID
+#define CATMODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH
+#else
+#define CATMODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
+#endif
+#endif
diff --git a/gnu/usr.bin/man/lib/config.h_dist b/gnu/usr.bin/man/lib/config.h_dist
new file mode 100644
index 0000000..3438e6f
--- /dev/null
+++ b/gnu/usr.bin/man/lib/config.h_dist
@@ -0,0 +1,210 @@
+/*
+ * config.h
+ *
+ * If you haven't read the README file, now might be a good time.
+ *
+ * This file is edited by configure, so you shouldn't have to.
+ * If that doesn't work, edit this file to match your site.
+ *
+ * Sorry it's so long, but there are lots of things you might want to
+ * customize for your site.
+ *
+ * Copyright (c) 1990, 1991, John W. Eaton.
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the file COPYING that comes with the man
+ * distribution.
+ *
+ * John W. Eaton
+ * jwe@che.utexas.edu
+ * Department of Chemical Engineering
+ * The University of Texas at Austin
+ * Austin, Texas 78712
+ */
+
+/*
+ * This is the size of a number of internal buffers. It should
+ * probably not be less than 512.
+ */
+#ifndef BUFSIZ
+#define BUFSIZ 1024
+#endif
+
+/*
+ * This should be at least the size of the longest path.
+ */
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif
+
+/*
+ * This is the maximum number of directories expected in the manpath.
+ */
+#ifndef MAXDIRS
+#define MAXDIRS 64
+#endif
+
+/*
+ * This is the name of the group that owns the preformatted man pages.
+ * If you are running man as a setgid program, you should make sure
+ * that all of the preformatted man pages and the directories that
+ * they live in are readable and writeable and owned by this group.
+ */
+#ifdef SECURE_MAN_UID
+#define MAN_USER ""
+#endif
+
+/*
+ * It's probably best to define absolute paths to all of these. If
+ * you don't, you'll be depending on the user's path to be correct
+ * when system () is called. This can result in weird behavior that's
+ * hard to track down, especially after you forget how this program
+ * works... If you don't have some of these programs, simply define
+ * them to be empty strings (i.e. ""). As a minimum, you must have
+ * nroff installed.
+ */
+#ifndef APROPOS
+#define APROPOS "%apropos%"
+#endif
+
+#ifndef WHATIS
+#define WHATIS "%whatis%"
+#endif
+
+#ifndef PAGER
+#define PAGER "%pager%"
+#endif
+
+#ifdef HAS_TROFF
+#ifndef TROFF
+#define TROFF "%troff%"
+#endif
+#endif
+
+#ifndef NROFF
+#define NROFF "%nroff%"
+#endif
+
+#ifndef EQN
+#define EQN "%eqn%"
+#endif
+
+#ifndef NEQN
+#define NEQN "%neqn%"
+#endif
+
+#ifndef TBL
+#define TBL "%tbl%"
+#endif
+
+#ifndef COL
+#define COL "%col%"
+#endif
+
+#ifndef VGRIND
+#define VGRIND "%vgrind%"
+#endif
+
+#ifndef REFER
+#define REFER "%refer%"
+#endif
+
+#ifndef GRAP
+#define GRAP "%grap%"
+#endif
+
+#ifndef PIC
+#define PIC "%pic%"
+#endif
+
+/*
+ * Define the absolute path to the configuration file.
+ */
+#ifndef MAN_MAIN
+ static char config_file[] = "%manpath_config_file%" ;
+#endif
+
+/*
+ * Define the uncompression program(s) to use for those preformatted
+ * pages that end in the given character. If you add extras here, you
+ * may need to change man.c. [I have no idea what FCAT and YCAT files
+ * are! - I will leave them in for now.. -jkh]
+ */
+/* .F files */
+#define FCAT ""
+/* .Y files */
+#define YCAT ""
+/* .Z files */
+#define ZCAT "%zcat%"
+
+/*
+ * This is the standard program to use on this system for compressing
+ * pages once they have been formatted, and the character to tack on
+ * to the end of those files. The program listed is expected to read
+ * from the standard input and write compressed output to the standard
+ * output. These won't actually be used unless compression is enabled.
+ */
+#ifdef DO_COMPRESS
+#define COMPRESSOR "%compress%"
+#define COMPRESS_EXT "%compext%"
+#endif
+
+/*
+ * Define the standard manual sections. For example, if your man
+ * directory tree has subdirectories man1, man2, man3, mann,
+ * and man3foo, std_sections[] would have "1", "2", "3", "n", and
+ * "3foo". Directories are searched in the order they appear. Having
+ * extras isn't fatal, it just slows things down a bit.
+ *
+ * Note that this is just for directories to search. If you have
+ * files like .../man3/foobar.3Xtc, you don't need to have "3Xtc" in
+ * the list below -- this is handled separately, so that `man 3Xtc foobar',
+ * `man 3 foobar', and `man foobar' should find the file .../man3/foo.3Xtc,
+ * (assuming, of course, that there isn't a .../man1/foo.1 or somesuch
+ * that we would find first).
+ *
+ * Note that this list should be in the order that you want the
+ * directories to be searched. Is there a standard for this? What is
+ * the normal order? If anyone knows, please tell me!
+ */
+#ifndef MANPATH_MAIN
+ static char *std_sections[] =
+ {
+ "1", "n", "l", "6", "8", "2", "3", "4", "5", "7", "p", "o", NULL
+ };
+#endif
+
+/*
+ * Not all systems define these in stat.h.
+ */
+#ifndef S_IRUSR
+#define S_IRUSR 00400 /* read permission: owner */
+#endif
+#ifndef S_IWUSR
+#define S_IWUSR 00200 /* write permission: owner */
+#endif
+#ifndef S_IRGRP
+#define S_IRGRP 00040 /* read permission: group */
+#endif
+#ifndef S_IWGRP
+#define S_IWGRP 00020 /* write permission: group */
+#endif
+#ifndef S_IROTH
+#define S_IROTH 00004 /* read permission: other */
+#endif
+#ifndef S_IWOTH
+#define S_IWOTH 00002 /* write permission: other */
+#endif
+
+/*
+ * This is the mode used for formatted pages that we create. If you
+ * are using the setgid option, you should use 664. If you are not,
+ * you should use 666 and make the cat* directories mode 777.
+ */
+#ifndef CATMODE
+#ifdef SECURE_MAN_UID
+#define CATMODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH
+#else
+#define CATMODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
+#endif
+#endif
diff --git a/gnu/usr.bin/man/lib/gripes.c b/gnu/usr.bin/man/lib/gripes.c
new file mode 100644
index 0000000..76f8e70
--- /dev/null
+++ b/gnu/usr.bin/man/lib/gripes.c
@@ -0,0 +1,180 @@
+/*
+ * gripes.c
+ *
+ * Copyright (c) 1990, 1991, John W. Eaton.
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the file COPYING that comes with the man
+ * distribution.
+ *
+ * John W. Eaton
+ * jwe@che.utexas.edu
+ * Department of Chemical Engineering
+ * The University of Texas at Austin
+ * Austin, Texas 78712
+ */
+
+#include <stdio.h>
+#include "gripes.h"
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+extern int fprintf ();
+extern int fflush ();
+extern int exit ();
+#endif
+
+extern char *prognam;
+
+void
+gripe_no_name (section)
+ char *section;
+{
+ if (section)
+ fprintf (stderr, "What manual page do you want from section %s?\n",
+ section);
+ else
+ fprintf (stderr, "What manual page do you want?\n");
+
+ fflush (stderr);
+}
+
+void
+gripe_reading_man_file (name)
+ char *name;
+{
+ fprintf (stderr, "Read access denied for file %s\n", name);
+
+ fflush (stderr);
+}
+
+void
+gripe_converting_name (name, to_cat)
+ char *name;
+ int to_cat;
+{
+ if (to_cat)
+ fprintf (stderr, "Error converting %s to cat name\n", name);
+ else
+ fprintf (stderr, "Error converting %s to man name\n", name);
+
+ fflush (stderr);
+
+ exit (1);
+}
+
+void
+gripe_system_command (status)
+ int status;
+{
+ fprintf (stderr, "Error executing formatting or display command.\n");
+ fprintf (stderr, "system command exited with status %d\n", status);
+
+ fflush (stderr);
+}
+
+void
+gripe_not_found (name, section)
+ char *name, *section;
+{
+ if (section)
+ fprintf (stderr, "No entry for %s in section %s of the manual\n",
+ name, section);
+ else
+ fprintf (stderr, "No manual entry for %s\n", name);
+
+ fflush (stderr);
+}
+
+void
+gripe_incompatible (s)
+ char *s;
+{
+ fprintf (stderr, "%s: incompatible options %s\n", prognam, s);
+
+ fflush (stderr);
+
+ exit (1);
+}
+
+void
+gripe_getting_mp_config (file)
+ char *file;
+{
+ fprintf (stderr, "%s: unable to find the file %s\n", prognam, file);
+
+ fflush (stderr);
+
+ exit (1);
+}
+
+void
+gripe_reading_mp_config (file)
+ char *file;
+{
+ fprintf (stderr, "%s: unable to make sense of the file %s\n", prognam, file);
+
+ fflush (stderr);
+
+ exit (1);
+}
+
+void
+gripe_invalid_section (section)
+ char *section;
+{
+ fprintf (stderr, "%s: invalid section (%s) selected\n", prognam, section);
+
+ fflush (stderr);
+
+ exit (1);
+}
+
+void
+gripe_manpath ()
+{
+ fprintf (stderr, "%s: manpath is null\n", prognam);
+
+ fflush (stderr);
+
+ exit (1);
+}
+
+void
+gripe_alloc (bytes, object)
+ int bytes;
+ char *object;
+{
+ fprintf (stderr, "%s: can't malloc %d bytes for %s\n",
+ prognam, bytes, object);
+
+ fflush (stderr);
+
+ exit (1);
+}
+
+void
+gripe_roff_command_from_file (file)
+ char *file;
+{
+ fprintf (stderr, "Error parsing *roff command from file %s\n", file);
+
+ fflush (stderr);
+}
+
+void
+gripe_roff_command_from_env ()
+{
+ fprintf (stderr, "Error parsing MANROFFSEQ. Using system defaults.\n");
+
+ fflush (stderr);
+}
+
+void
+gripe_roff_command_from_command_line ()
+{
+ fprintf (stderr, "Error parsing *roff command from command line.\n");
+
+ fflush (stderr);
+}
diff --git a/gnu/usr.bin/man/lib/gripes.h b/gnu/usr.bin/man/lib/gripes.h
new file mode 100644
index 0000000..e3be4a0
--- /dev/null
+++ b/gnu/usr.bin/man/lib/gripes.h
@@ -0,0 +1,30 @@
+/*
+ * gripes.h
+ *
+ * Copyright (c) 1990, 1991, John W. Eaton.
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the file COPYING that comes with the man
+ * distribution.
+ *
+ * John W. Eaton
+ * jwe@che.utexas.edu
+ * Department of Chemical Engineering
+ * The University of Texas at Austin
+ * Austin, Texas 78712
+ */
+
+extern void gripe_no_name ();
+extern void gripe_converting_name ();
+extern void gripe_system_command ();
+extern void gripe_reading_man_file ();
+extern void gripe_not_found ();
+extern void gripe_invalid_section ();
+extern void gripe_manpath ();
+extern void gripe_alloc ();
+extern void gripe_incompatible ();
+extern void gripe_getting_mp_config ();
+extern void gripe_reading_mp_config ();
+extern void gripe_roff_command_from_file ();
+extern void gripe_roff_command_from_env ();
+extern void gripe_roff_command_from_command_line ();
diff --git a/gnu/usr.bin/man/lib/util.c b/gnu/usr.bin/man/lib/util.c
new file mode 100644
index 0000000..e8253a8
--- /dev/null
+++ b/gnu/usr.bin/man/lib/util.c
@@ -0,0 +1,149 @@
+/*
+ * util.c
+ *
+ * Copyright (c) 1990, 1991, John W. Eaton.
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the file COPYING that comes with the man
+ * distribution.
+ *
+ * John W. Eaton
+ * jwe@che.utexas.edu
+ * Department of Chemical Engineering
+ * The University of Texas at Austin
+ * Austin, Texas 78712
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+extern int fprintf ();
+extern int tolower ();
+#endif
+
+extern char *strdup ();
+extern int system ();
+
+#include "gripes.h"
+
+/*
+ * Extract last element of a name like /foo/bar/baz.
+ */
+char *
+mkprogname (s)
+ register char *s;
+{
+ char *t;
+
+ t = strrchr (s, '/');
+ if (t == (char *)NULL)
+ t = s;
+ else
+ t++;
+
+ return strdup (t);
+}
+
+void
+downcase (s)
+ char *s;
+{
+ register char c;
+ while ((c = *s) != '\0')
+ {
+ if (isalpha (c))
+ *s++ = tolower (c);
+ }
+}
+
+/*
+ * Is file a newer than file b?
+ *
+ * case:
+ *
+ * a newer than b returns 1
+ * a older than b returns 0
+ * stat on a fails returns -1
+ * stat on b fails returns -2
+ * stat on a and b fails returns -3
+ */
+int
+is_newer (fa, fb)
+ register char *fa;
+ register char *fb;
+{
+ struct stat fa_sb;
+ struct stat fb_sb;
+ register int fa_stat;
+ register int fb_stat;
+ register int status = 0;
+
+ fa_stat = stat (fa, &fa_sb);
+ if (fa_stat != 0)
+ status = 1;
+
+ fb_stat = stat (fb, &fb_sb);
+ if (fb_stat != 0)
+ status |= 2;
+
+ if (status != 0)
+ return -status;
+
+ return (fa_sb.st_mtime > fb_sb.st_mtime);
+}
+
+/*
+ * Is path a directory?
+ */
+int
+is_directory (path)
+ char *path;
+{
+ struct stat sb;
+ register int status;
+
+ status = stat (path, &sb);
+
+ if (status != 0)
+ return -1;
+
+ return ((sb.st_mode & S_IFDIR) == S_IFDIR);
+
+}
+
+/*
+ * Attempt a system () call. Return 1 for success and 0 for failure
+ * (handy for counting successes :-).
+ */
+int
+do_system_command (command)
+ char *command;
+{
+ int status = 0;
+ extern int debug;
+
+ /*
+ * If we're debugging, don't really execute the command -- you never
+ * know what might be in that mangled string :-O.
+ */
+ if (debug)
+ fprintf (stderr, "\ntrying command: %s\n", command);
+ else
+ status = system (command);
+
+ if (WIFSIGNALED(status))
+ return 0;
+ else if (WEXITSTATUS(status)) {
+ gripe_system_command (status);
+ return 0;
+ }
+ else
+ return 1;
+}
diff --git a/gnu/usr.bin/man/makewhatis/Makefile b/gnu/usr.bin/man/makewhatis/Makefile
new file mode 100644
index 0000000..bd14975
--- /dev/null
+++ b/gnu/usr.bin/man/makewhatis/Makefile
@@ -0,0 +1,17 @@
+# @(#)Makefile 5.6 (Berkeley) 6/23/90
+
+NOMAN= noman
+CLEANFILES= makewhatis
+
+beforeinstall: makewhatis
+ install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ makewhatis ${DESTDIR}${BINDIR}
+
+.include <bsd.prog.mk>
+
+makewhatis: makewhatis.sh
+ sed -e 's/%sections%/ "1", "n", "l", "6", "8", "2", "3", "4", "5", "7", "p", "o", NULL/' \
+ -e 's,%zcat%,${zcat},' \
+ -e 's,%compext%,${compext},' \
+ ${.CURDIR}/makewhatis.sh > makewhatis
+
diff --git a/gnu/usr.bin/man/makewhatis/makewhatis b/gnu/usr.bin/man/makewhatis/makewhatis
new file mode 100644
index 0000000..e6c238c
--- /dev/null
+++ b/gnu/usr.bin/man/makewhatis/makewhatis
@@ -0,0 +1,79 @@
+#!/bin/sh
+#
+# makewhatis -- update the whatis database in the man directories.
+#
+# Copyright (c) 1990, 1991, John W. Eaton.
+#
+# You may distribute under the terms of the GNU General Public
+# License as specified in the README file that comes with the man
+# distribution.
+#
+# John W. Eaton
+# jwe@che.utexas.edu
+# Department of Chemical Engineering
+# The University of Texas at Austin
+# Austin, Texas 78712
+
+PATH=/bin:/usr/local/bin:/usr/ucb:/usr/bin
+
+if [ $# = 0 ]
+then
+ echo "usage: makewhatis directory [...]"
+ exit 1
+fi
+
+for dir in $*
+do
+ cd $dir
+ for subdir in man*
+ do
+ if [ -d $subdir ]
+ then
+ for f in `find . -name '*' -print`
+ do
+ sed -n '/^\.TH.*$/p
+ /^\.SH[ ]*NAME/,/^\.SH/p' $f |\
+ sed -e 's/\\[ ]*\-/-/
+ s/^.PP.*$//
+ s/\\(em//
+ s/\\fI//
+ s/\\fR//' |\
+ awk 'BEGIN {insh = 0} {
+ if ($1 == ".TH")
+ sect = $3
+ else if ($1 == ".SH" && insh == 1) {
+ if (i > 0 && name != NULL) {
+ namesect = sprintf("%s (%s)", name, sect)
+ printf("%-20.20s", namesect)
+ printf(" - ")
+ for (j = 0; j < i-1; j++)
+ printf("%s ", desc[j])
+ printf("%s\n", desc[i-1])
+ }
+ } else if ($1 == ".SH" && insh == 0) {
+ insh = 1
+ count = 0
+ i = 0
+ } else if (insh == 1) {
+ count++
+ if (count == 1 && NF > 2) {
+ start = 2
+ if ($2 == "-") start = 3
+ if (NF > start + 1)
+ for (j = start; j <= NF; j++)
+ desc[i++] = $j
+ name = $1
+ } else {
+ for (j = 1; j <= NF; j++)
+ desc[i++] = $j
+ }
+ }
+ }'
+ done
+ cd ..
+ fi
+ done | sort | colrm 80 > $dir/whatis.db.tmp
+ mv $dir/whatis.db.tmp $dir/whatis
+done
+
+exit
diff --git a/gnu/usr.bin/man/makewhatis/makewhatis.sh b/gnu/usr.bin/man/makewhatis/makewhatis.sh
new file mode 100644
index 0000000..28b871d
--- /dev/null
+++ b/gnu/usr.bin/man/makewhatis/makewhatis.sh
@@ -0,0 +1,120 @@
+#!/bin/sh
+#
+# makewhatis -- update the whatis database in the man directories.
+#
+# Copyright (c) 1990, 1991, John W. Eaton.
+#
+# You may distribute under the terms of the GNU General Public
+# License as specified in the README file that comes with the man
+# distribution.
+#
+# John W. Eaton
+# jwe@che.utexas.edu
+# Department of Chemical Engineering
+# The University of Texas at Austin
+# Austin, Texas 78712
+
+PATH=/bin:/usr/local/bin:/usr/ucb:/usr/bin
+
+if [ $# = 0 ]
+then
+ echo "usage: makewhatis directory [...]"
+ exit 1
+fi
+for dir in $*
+do
+ cd $dir
+ for subdir in man*
+ do
+ if [ -d $subdir ]
+ then
+ for f in `find $subdir -type f -print`
+ do
+ suffix=`echo $f | sed -e 's/.*\\.//'`
+ if [ ".$suffix" = "%compext%" ]; then
+ output=%zcat%
+ else
+ output=cat
+ fi
+ $output $f | \
+ sed -n '/^\.TH.*$/p
+ /^\.Dt.*$/p
+ /^\.S[hH][ ]*NAME/,/^\.S[hH]/p'|\
+ sed -e 's/\\[ ]*\-/-/
+ s/^.P[Pp].*$//
+ s/\\(em//
+ s/\\fI//
+ s/\\fR//' |\
+ awk 'BEGIN {insh = 0; inSh = 0; Nd = 0} {
+ if ($1 == ".TH" || $1 == ".Dt")
+ sect = $3
+ else if (($1 == ".br" && insh == 1)\
+ || ($1 == ".SH" && insh == 1)\
+ || ($1 == ".Sh" && inSh == 1)) {
+ if (i > 0 && nc > 0) {
+ for (k= 1; k <= nc; k++) {
+ namesect = sprintf("%s (%s)", name[k], sect)
+ printf("%s", namesect)
+ printf(" - ")
+ for (j = 0; j < i-1; j++)
+ printf("%s ", desc[j])
+ printf("%s\n", desc[i-1])
+ }
+ }
+ count = 0
+ i = 0
+ nc = 0
+ } else if ($1 == ".SH" && insh == 0) {
+ insh = 1
+ count = 0
+ i = 0
+ nc = 0
+ } else if ($1 == ".Sh" && inSh == 0) {
+ inSh = 1
+ i = 0
+ nc = 0
+ } else if (insh == 1) {
+ count++
+ if (count == 1 && NF > 2) {
+ start = 2
+ for (k = 1; k <= NF; k++)
+ if ($k == "-") {
+ start = k + 1
+ break
+ } else {
+ sub(",","",$k)
+ if ($k != "")
+ name[++nc] = $k
+ }
+ if (NF >= start)
+ for (j = start; j <= NF; j++)
+ desc[i++] = $j
+ } else {
+ for (j = 1; j <= NF; j++)
+ desc[i++] = $j
+ }
+ } else if ($1 == ".Nm" && inSh == 1 && Nd == 0) {
+ for (k = 2; k <= NF; k++) {
+ sub(",","",$k)
+ if ($k != "")
+ name[++nc] = $k
+ }
+ } else if ($1 == ".Nd" && inSh == 1) {
+ Nd = 1
+ for (j = 2; j <= NF; j++)
+ desc[i++] = $j
+ } else if (Nd == 1) {
+ start = 1
+ if ($1 ~ /\..*/)
+ start = 2
+ for (j = start; j <= NF; j++)
+ desc[i++] = $j
+ }
+ }'
+ done
+ fi
+ done | sort | colrm 80 | uniq > $dir/whatis.db.tmp
+ mv $dir/whatis.db.tmp $dir/whatis
+done
+
+exit
diff --git a/gnu/usr.bin/man/man/Makefile b/gnu/usr.bin/man/man/Makefile
new file mode 100644
index 0000000..aaeb086
--- /dev/null
+++ b/gnu/usr.bin/man/man/Makefile
@@ -0,0 +1,31 @@
+PROG= man
+SRCS= man.c manpath.c glob.c
+BINMODE=4555
+BINOWN= man
+
+.if exists(${.CURDIR}/../lib/obj)
+LDADD= -L${.CURDIR}/../lib/obj -lman
+CFLAGS+= -I${.CURDIR}/../lib/obj
+.else
+LDADD= -L${.CURDIR}/../lib/ -lman
+.endif
+
+.if exists(${.CURDIR}/obj)
+MAN1= ${.CURDIR}/obj/man.1
+.else
+MAN1= ${.CURDIR}/man.1
+.endif
+
+DPADD+= ${MAN1}
+CFLAGS+= -I${.CURDIR}/../lib -DSTDC_HEADERS -DPOSIX -DHAS_TROFF
+CFLAGS+= -DDO_COMPRESS -DALT_SYSTEMS -DSETREUID -DCATMODE=0664
+CLEANFILES+= ${MAN1}
+
+${MAN1}: ${.CURDIR}/man.man
+ sed -e 's,%libdir%,${libdir},' -e 's,%bindir%,${bindir},' \
+ -e 's,%pager%,${pager},' -e 's,%troff%,${troff},' \
+ -e 's,%manpath_config_file%,${manpath_config_file},' \
+ -e 's,%compress%,${compress},' \
+ ${.CURDIR}/man.man > ${MAN1}
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/man/man/glob.c b/gnu/usr.bin/man/man/glob.c
new file mode 100644
index 0000000..5bfb1bf
--- /dev/null
+++ b/gnu/usr.bin/man/man/glob.c
@@ -0,0 +1,680 @@
+/* File-name wildcard pattern matching for GNU.
+ Copyright (C) 1985, 1988, 1989, 1990, 1991 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* To whomever it may concern: I have never seen the code which most
+ Unix programs use to perform this function. I wrote this from scratch
+ based on specifications for the pattern matching. --RMS. */
+
+#ifdef SHELL
+#include "config.h"
+#endif /* SHELL */
+
+#include <sys/types.h>
+
+#if defined (USGr3) && !defined (DIRENT)
+#define DIRENT
+#endif /* USGr3 */
+#if defined (Xenix) && !defined (SYSNDIR)
+#define SYSNDIR
+#endif /* Xenix */
+
+#if defined (POSIX) || defined (DIRENT) || defined (__GNU_LIBRARY__)
+#include <dirent.h>
+#define direct dirent
+#define D_NAMLEN(d) strlen((d)->d_name)
+#else /* not POSIX or DIRENT or __GNU_LIBRARY__ */
+#define D_NAMLEN(d) ((d)->d_namlen)
+#ifdef USG
+#if defined (SYSNDIR)
+#include <sys/ndir.h>
+#else /* SYSNDIR */
+#include "ndir.h"
+#endif /* not SYSNDIR */
+#else /* not USG */
+#include <sys/dir.h>
+#endif /* USG */
+#endif /* POSIX or DIRENT or __GNU_LIBRARY__ */
+
+#if defined (_POSIX_SOURCE)
+/* Posix does not require that the d_ino field be present, and some
+ systems do not provide it. */
+#define REAL_DIR_ENTRY(dp) 1
+#else
+#define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)
+#endif /* _POSIX_SOURCE */
+
+#if defined (STDC_HEADERS) || defined (__GNU_LIBRARY__)
+#include <stdlib.h>
+#include <string.h>
+#define STDC_STRINGS
+#else /* STDC_HEADERS or __GNU_LIBRARY__ */
+
+#if defined (USG)
+#include <string.h>
+#ifndef POSIX
+#include <memory.h>
+#endif /* POSIX */
+#define STDC_STRINGS
+#else /* not USG */
+#ifdef NeXT
+#include <string.h>
+#else /* NeXT */
+#include <strings.h>
+#endif /* NeXT */
+/* Declaring bcopy causes errors on systems whose declarations are different.
+ If the declaration is omitted, everything works fine. */
+#endif /* not USG */
+
+extern char *malloc ();
+extern char *realloc ();
+extern void free ();
+
+#ifndef NULL
+#define NULL 0
+#endif
+#endif /* Not STDC_HEADERS or __GNU_LIBRARY__. */
+
+#ifdef STDC_STRINGS
+#define bcopy(s, d, n) memcpy ((d), (s), (n))
+#define index strchr
+#define rindex strrchr
+#endif /* STDC_STRINGS */
+
+#ifndef alloca
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* Not GCC. */
+#ifdef sparc
+#include <alloca.h>
+#else /* Not sparc. */
+extern char *alloca ();
+#endif /* sparc. */
+#endif /* GCC. */
+#endif
+
+/* Nonzero if '*' and '?' do not match an initial '.' for glob_filename. */
+int noglob_dot_filenames = 1;
+
+static int glob_match_after_star ();
+
+/* Return nonzero if PATTERN has any special globbing chars in it. */
+
+int
+glob_pattern_p (pattern)
+ char *pattern;
+{
+ register char *p = pattern;
+ register char c;
+ int open = 0;
+
+ while ((c = *p++) != '\0')
+ switch (c)
+ {
+ case '?':
+ case '*':
+ return 1;
+
+ case '[': /* Only accept an open brace if there is a close */
+ open++; /* brace to match it. Bracket expressions must be */
+ continue; /* complete, according to Posix.2 */
+ case ']':
+ if (open)
+ return 1;
+ continue;
+
+ case '\\':
+ if (*p++ == '\0')
+ return 0;
+ }
+
+ return 0;
+}
+
+
+/* Match the pattern PATTERN against the string TEXT;
+ return 1 if it matches, 0 otherwise.
+
+ A match means the entire string TEXT is used up in matching.
+
+ In the pattern string, `*' matches any sequence of characters,
+ `?' matches any character, [SET] matches any character in the specified set,
+ [!SET] matches any character not in the specified set.
+
+ A set is composed of characters or ranges; a range looks like
+ character hyphen character (as in 0-9 or A-Z).
+ [0-9a-zA-Z_] is the set of characters allowed in C identifiers.
+ Any other character in the pattern must be matched exactly.
+
+ To suppress the special syntactic significance of any of `[]*?!-\',
+ and match the character exactly, precede it with a `\'.
+
+ If DOT_SPECIAL is nonzero,
+ `*' and `?' do not match `.' at the beginning of TEXT. */
+
+int
+glob_match (pattern, text, dot_special)
+ char *pattern, *text;
+ int dot_special;
+{
+ register char *p = pattern, *t = text;
+ register char c;
+
+ while ((c = *p++) != '\0')
+ switch (c)
+ {
+ case '?':
+ if (*t == '\0' || (dot_special && t == text && *t == '.'))
+ return 0;
+ else
+ ++t;
+ break;
+
+ case '\\':
+ if (*p++ != *t++)
+ return 0;
+ break;
+
+ case '*':
+ if (dot_special && t == text && *t == '.')
+ return 0;
+ return glob_match_after_star (p, t);
+
+ case '[':
+ {
+ register char c1 = *t++;
+ int invert;
+
+ if (c1 == '\0')
+ return 0;
+
+ invert = (*p == '!');
+
+ if (invert)
+ p++;
+
+ c = *p++;
+ while (1)
+ {
+ register char cstart = c, cend = c;
+
+ if (c == '\\')
+ {
+ cstart = *p++;
+ cend = cstart;
+ }
+
+ if (cstart == '\0')
+ return 0; /* Missing ']'. */
+
+ c = *p++;
+
+ if (c == '-')
+ {
+ cend = *p++;
+ if (cend == '\\')
+ cend = *p++;
+ if (cend == '\0')
+ return 0;
+ c = *p++;
+ }
+ if (c1 >= cstart && c1 <= cend)
+ goto match;
+ if (c == ']')
+ break;
+ }
+ if (!invert)
+ return 0;
+ break;
+
+ match:
+ /* Skip the rest of the [...] construct that already matched. */
+ while (c != ']')
+ {
+ if (c == '\0')
+ return 0;
+ c = *p++;
+ if (c == '\0')
+ return 0;
+ if (c == '\\')
+ p++;
+ }
+ if (invert)
+ return 0;
+ break;
+ }
+
+ default:
+ if (c != *t++)
+ return 0;
+ }
+
+ return *t == '\0';
+}
+
+/* Like glob_match, but match PATTERN against any final segment of TEXT. */
+
+static int
+glob_match_after_star (pattern, text)
+ char *pattern, *text;
+{
+ register char *p = pattern, *t = text;
+ register char c, c1;
+
+ while ((c = *p++) == '?' || c == '*')
+ if (c == '?' && *t++ == '\0')
+ return 0;
+
+ if (c == '\0')
+ return 1;
+
+ if (c == '\\')
+ c1 = *p;
+ else
+ c1 = c;
+
+ --p;
+ while (1)
+ {
+ if ((c == '[' || *t == c1) && glob_match (p, t, 0))
+ return 1;
+ if (*t++ == '\0')
+ return 0;
+ }
+}
+
+/* Return a vector of names of files in directory DIR
+ whose names match glob pattern PAT.
+ The names are not in any particular order.
+ Wildcards at the beginning of PAT do not match an initial period
+ if noglob_dot_filenames is nonzero.
+
+ The vector is terminated by an element that is a null pointer.
+
+ To free the space allocated, first free the vector's elements,
+ then free the vector.
+
+ Return NULL if cannot get enough memory to hold the pointer
+ and the names.
+
+ Return -1 if cannot access directory DIR.
+ Look in errno for more information. */
+
+char **
+glob_vector (pat, dir)
+ char *pat;
+ char *dir;
+{
+ struct globval
+ {
+ struct globval *next;
+ char *name;
+ };
+
+ DIR *d;
+ register struct direct *dp;
+ struct globval *lastlink;
+ register struct globval *nextlink;
+ register char *nextname;
+ unsigned int count;
+ int lose;
+ register char **name_vector;
+ register unsigned int i;
+#ifdef ALLOCA_MISSING
+ struct globval *templink;
+#endif
+
+ d = opendir (dir);
+ if (d == NULL)
+ return (char **) -1;
+
+ lastlink = NULL;
+ count = 0;
+ lose = 0;
+
+ /* Scan the directory, finding all names that match.
+ For each name that matches, allocate a struct globval
+ on the stack and store the name in it.
+ Chain those structs together; lastlink is the front of the chain. */
+ while (1)
+ {
+#if defined (SHELL)
+ /* Make globbing interruptible in the bash shell. */
+ extern int interrupt_state;
+
+ if (interrupt_state)
+ {
+ closedir (d);
+ lose = 1;
+ goto lost;
+ }
+#endif /* SHELL */
+
+ dp = readdir (d);
+ if (dp == NULL)
+ break;
+ if (REAL_DIR_ENTRY (dp)
+ && glob_match (pat, dp->d_name, noglob_dot_filenames))
+ {
+#ifdef ALLOCA_MISSING
+ nextlink = (struct globval *) malloc (sizeof (struct globval));
+#else
+ nextlink = (struct globval *) alloca (sizeof (struct globval));
+#endif
+ nextlink->next = lastlink;
+ i = D_NAMLEN (dp) + 1;
+ nextname = (char *) malloc (i);
+ if (nextname == NULL)
+ {
+ lose = 1;
+ break;
+ }
+ lastlink = nextlink;
+ nextlink->name = nextname;
+ bcopy (dp->d_name, nextname, i);
+ count++;
+ }
+ }
+ closedir (d);
+
+ if (!lose)
+ {
+ name_vector = (char **) malloc ((count + 1) * sizeof (char *));
+ lose |= name_vector == NULL;
+ }
+
+ /* Have we run out of memory? */
+#ifdef SHELL
+ lost:
+#endif
+ if (lose)
+ {
+ /* Here free the strings we have got. */
+ while (lastlink)
+ {
+ free (lastlink->name);
+#ifdef ALLOCA_MISSING
+ templink = lastlink->next;
+ free ((char *) lastlink);
+ lastlink = templink;
+#else
+ lastlink = lastlink->next;
+#endif
+ }
+ return NULL;
+ }
+
+ /* Copy the name pointers from the linked list into the vector. */
+ for (i = 0; i < count; ++i)
+ {
+ name_vector[i] = lastlink->name;
+#ifdef ALLOCA_MISSING
+ templink = lastlink->next;
+ free ((char *) lastlink);
+ lastlink = templink;
+#else
+ lastlink = lastlink->next;
+#endif
+ }
+
+ name_vector[count] = NULL;
+ return name_vector;
+}
+
+/* Return a new array, replacing ARRAY, which is the concatenation
+ of each string in ARRAY to DIR.
+ Return NULL if out of memory. */
+
+static char **
+glob_dir_to_array (dir, array)
+ char *dir, **array;
+{
+ register unsigned int i, l;
+ int add_slash = 0;
+ char **result;
+
+ l = strlen (dir);
+ if (l == 0)
+ return array;
+
+ if (dir[l - 1] != '/')
+ add_slash++;
+
+ for (i = 0; array[i] != NULL; i++)
+ ;
+
+ result = (char **) malloc ((i + 1) * sizeof (char *));
+ if (result == NULL)
+ return NULL;
+
+ for (i = 0; array[i] != NULL; i++)
+ {
+ result[i] = (char *) malloc (1 + l + add_slash + strlen (array[i]));
+ if (result[i] == NULL)
+ return NULL;
+ strcpy (result[i], dir);
+ if (add_slash)
+ result[i][l] = '/';
+ strcpy (result[i] + l + add_slash, array[i]);
+ }
+ result[i] = NULL;
+
+ /* Free the input array. */
+ for (i = 0; array[i] != NULL; i++)
+ free (array[i]);
+ free ((char *) array);
+ return result;
+}
+
+/* Do globbing on PATHNAME. Return an array of pathnames that match,
+ marking the end of the array with a null-pointer as an element.
+ If no pathnames match, then the array is empty (first element is null).
+ If there isn't enough memory, then return NULL.
+ If a file system error occurs, return -1; `errno' has the error code.
+
+ Wildcards at the beginning of PAT, or following a slash,
+ do not match an initial period if noglob_dot_filenames is nonzero. */
+
+char **
+glob_filename (pathname)
+ char *pathname;
+{
+ char **result;
+ unsigned int result_size;
+ char *directory_name, *filename;
+ unsigned int directory_len;
+
+ result = (char **) malloc (sizeof (char *));
+ result_size = 1;
+ if (result == NULL)
+ return NULL;
+
+ result[0] = NULL;
+
+ /* Find the filename. */
+ filename = rindex (pathname, '/');
+ if (filename == NULL)
+ {
+ filename = pathname;
+ directory_name = "";
+ directory_len = 0;
+ }
+ else
+ {
+ directory_len = (filename - pathname) + 1;
+#ifdef ALLOCA_MISSING
+ directory_name = (char *) malloc (directory_len + 1);
+#else
+ directory_name = (char *) alloca (directory_len + 1);
+#endif
+ bcopy (pathname, directory_name, directory_len);
+ directory_name[directory_len] = '\0';
+ ++filename;
+ }
+
+ /* If directory_name contains globbing characters, then we
+ have to expand the previous levels. Just recurse. */
+ if (glob_pattern_p (directory_name))
+ {
+ char **directories;
+ register unsigned int i;
+
+ if (directory_name[directory_len - 1] == '/')
+ directory_name[directory_len - 1] = '\0';
+
+ directories = glob_filename (directory_name);
+#ifdef ALLOCA_MISSING
+ free ((char *) directory_name);
+#endif
+ if (directories == NULL)
+ goto memory_error;
+ else if (directories == (char **) -1)
+ return (char **) -1;
+ else if (*directories == NULL)
+ {
+ free ((char *) directories);
+ return (char **) -1;
+ }
+
+ /* We have successfully globbed the preceding directory name.
+ For each name in DIRECTORIES, call glob_vector on it and
+ FILENAME. Concatenate the results together. */
+ for (i = 0; directories[i] != NULL; i++)
+ {
+ char **temp_results = glob_vector (filename, directories[i]);
+ if (temp_results == NULL)
+ goto memory_error;
+ else if (temp_results == (char **) -1)
+ /* This filename is probably not a directory. Ignore it. */
+ ;
+ else
+ {
+ char **array = glob_dir_to_array (directories[i], temp_results);
+ register unsigned int l;
+
+ l = 0;
+ while (array[l] != NULL)
+ ++l;
+
+ result = (char **) realloc (result,
+ (result_size + l) * sizeof (char *));
+ if (result == NULL)
+ goto memory_error;
+
+ for (l = 0; array[l] != NULL; ++l)
+ result[result_size++ - 1] = array[l];
+ result[result_size - 1] = NULL;
+ free ((char *) array);
+ }
+ }
+ /* Free the directories. */
+ for (i = 0; directories[i] != NULL; i++)
+ free (directories[i]);
+ free ((char *) directories);
+
+ return result;
+ }
+
+ /* If there is only a directory name, return it. */
+ if (*filename == '\0')
+ {
+ result = (char **) realloc ((char *) result, 2 * sizeof (char *));
+ if (result != NULL)
+ {
+ result[0] = (char *) malloc (directory_len + 1);
+ if (result[0] == NULL)
+ {
+#ifdef ALLOCA_MISSING
+ free ((char *) directory_name);
+#endif
+ goto memory_error;
+ }
+ bcopy (directory_name, result[0], directory_len + 1);
+ result[1] = NULL;
+ }
+#ifdef ALLOCA_MISSING
+ free ((char *) directory_name);
+#endif
+ return result;
+ }
+ else
+ {
+ /* Otherwise, just return what glob_vector
+ returns appended to the directory name. */
+ char **temp_results = glob_vector (filename,
+ (directory_len == 0
+ ? "." : directory_name));
+
+ if (temp_results == NULL || temp_results == (char **) -1)
+ {
+#ifdef NO_ALLOCA
+ free ((char *) directory_name);
+#endif
+ return temp_results;
+ }
+
+ temp_results = glob_dir_to_array (directory_name, temp_results);
+#ifdef NO_ALLOCA
+ free ((char *) directory_name);
+#endif
+ return temp_results;
+ }
+
+ /* We get to memory error if the program has run out of memory, or
+ if this is the shell, and we have been interrupted. */
+ memory_error:
+ if (result != NULL)
+ {
+ register unsigned int i;
+ for (i = 0; result[i] != NULL; ++i)
+ free (result[i]);
+ free ((char *) result);
+ }
+#if defined (SHELL)
+ {
+ extern int interrupt_state;
+
+ if (interrupt_state)
+ throw_to_top_level ();
+ }
+#endif /* SHELL */
+ return NULL;
+}
+
+#ifdef TEST
+
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ char **value;
+ int i, optind;
+
+ for (optind = 1; optind < argc; optind++)
+ {
+ value = glob_filename (argv[optind]);
+ if (value == NULL)
+ puts ("virtual memory exhausted");
+ else if (value == (char **) -1)
+ perror (argv[optind]);
+ else
+ for (i = 0; value[i] != NULL; i++)
+ puts (value[i]);
+ }
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/gnu/usr.bin/man/man/man.1 b/gnu/usr.bin/man/man/man.1
new file mode 100644
index 0000000..f17aced
--- /dev/null
+++ b/gnu/usr.bin/man/man/man.1
@@ -0,0 +1,132 @@
+.\" Man page for man
+.\"
+.\" Copyright (c) 1990, 1991, John W. Eaton.
+.\"
+.\" You may distribute under the terms of the GNU General Public
+.\" License as specified in the README file that comes with the man 1.0
+.\" distribution.
+.\"
+.\" John W. Eaton
+.\" jwe@che.utexas.edu
+.\" Department of Chemical Engineering
+.\" The University of Texas at Austin
+.\" Austin, Texas 78712
+.\"
+.TH man 1 "Jan 5, 1991"
+.LO 1
+.SH NAME
+man \- format and display the on-line manual pages
+.SH SYNOPSIS
+man [\-adfhktw] [\-m system] [\-p string] [\-M path] [\-P pager]
+[\-S list] [section] name ...
+.SH DESCRIPTION
+man formats and displays the on-line manual pages. This version knows
+about the MANPATH and PAGER environment variables, so you can have
+your own set(s) of personal man pages and choose whatever program you
+like to display the formatted pages. If section is specified, man
+only looks in that section of the manual. You may also specify the
+order to search the sections for entries and which preprocessors to
+run on the source files via command line options or environment
+variables.
+.SH OPTIONS
+.TP
+.B \-\^M " path"
+Specify an alternate manpath. By default, man uses
+.B manpath
+to determine the path to search. This option overrides the
+.B MANPATH
+environment variable.
+.TP
+.B \-\^P " pager"
+Specify which pager to use. By default, man uses
+.B /usr/local/bin/less -sC,
+This option overrides the
+.B PAGER
+environment variable.
+.TP
+.B \-\^S " list"
+List is a colon separated list of manual sections to search.
+This option overrides the
+.B MANSECT
+environment variable.
+.TP
+.B \-\^a
+By default, man will exit after displaying the first manual page it
+finds. Using this option forces man to display all the manual pages
+that match
+.B name,
+not just the first.
+.TP
+.B \-\^d
+Don't actually display the man pages, but do print gobs of debugging
+information.
+.TP
+.B \-\^f
+Equivalent to
+.B whatis.
+.TP
+.B \-\^h
+Print a one line help message and exit.
+.TP
+.B \-\^k
+Equivalent to
+.B apropos.
+.TP
+.B \-\^m " system"
+Specify an alternate set of man pages to search based on the system
+name given.
+.TP
+.B \-\^p " string"
+Specify the sequence of preprocessors to run before nroff or troff.
+Not all installations will have a full set of preprocessors.
+Some of the preprocessors and the letters used to designate them are:
+eqn (e), grap (g), pic (p), tbl (t), vgrind (v), refer (r).
+This option overrides the
+.B MANROFFSEQ
+environment variable.
+.TP
+.B \-\^t
+Use
+.B /usr/bin/groff -Tps -man
+to format the manual page, passing the output to
+.B stdout.
+The output from
+.B /usr/bin/groff -Tps -man
+may need to be passed through some filter or another before being
+printed.
+.TP
+.B \-\^w
+Don't actually display the man pages, but do print the location(s) of
+the files that would be formatted or displayed.
+.SH ENVIRONMENT
+.TP \w'MANROFFSEQ\ \ 'u
+.B MANPATH
+If
+.B MANPATH
+is set, its value is used as the path to search for manual pages.
+.TP
+.B MANROFFSEQ
+If
+.B MANROFFSEQ
+is set, its value is used to determine the set of preprocessors run
+before running nroff or troff. By default, pages are passed through
+the table preprocessor before nroff.
+.TP
+.B MANSEC
+If
+.B MANSEC
+is set, its value is used to determine which manual sections to search.
+.TP
+.B PAGER
+If
+.B PAGER
+is set, its value is used as the name of the program to use to display
+the man page. By default,
+.B /usr/local/bin/less -sC
+is used.
+.SH "SEE ALSO"
+apropos(1), whatis(1), manpath(1), less(1), groff(1).
+.SH BUGS
+The
+.B \-t
+option only works if a troff-like program is installed.
diff --git a/gnu/usr.bin/man/man/man.c b/gnu/usr.bin/man/man/man.c
new file mode 100644
index 0000000..c5b05e2
--- /dev/null
+++ b/gnu/usr.bin/man/man/man.c
@@ -0,0 +1,1431 @@
+/*
+ * man.c
+ *
+ * Copyright (c) 1990, 1991, John W. Eaton.
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the file COPYING that comes with the man
+ * distribution.
+ *
+ * John W. Eaton
+ * jwe@che.utexas.edu
+ * Department of Chemical Engineering
+ * The University of Texas at Austin
+ * Austin, Texas 78712
+ */
+
+#define MAN_MAIN
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/file.h>
+#include <signal.h>
+#include "config.h"
+#include "gripes.h"
+#include "version.h"
+
+#ifndef POSIX
+#include <unistd.h>
+#else
+#ifndef R_OK
+#define R_OK 4
+#endif
+#endif
+
+#ifdef SECURE_MAN_UID
+extern uid_t getuid ();
+extern int setuid ();
+#endif
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+extern char *malloc ();
+extern char *getenv ();
+extern void free ();
+extern int system ();
+extern int strcmp ();
+extern int strncmp ();
+extern int exit ();
+extern int fflush ();
+extern int printf ();
+extern int fprintf ();
+extern FILE *fopen ();
+extern int fclose ();
+extern char *sprintf ();
+#endif
+
+extern char *strdup ();
+
+extern char **glob_vector ();
+extern char **glob_filename ();
+extern int access ();
+extern int unlink ();
+extern int system ();
+extern int chmod ();
+extern int is_newer ();
+extern int is_directory ();
+extern int do_system_command ();
+
+char *prognam;
+static char *pager;
+static char *manp;
+static char *manpathlist[MAXDIRS];
+static char *section;
+static char *colon_sep_section_list;
+static char **section_list;
+static char *roff_directive;
+static int apropos;
+static int whatis;
+static int findall;
+static int print_where;
+
+#ifdef ALT_SYSTEMS
+static int alt_system;
+static char *alt_system_name;
+#endif
+
+static int troff = 0;
+
+int debug;
+
+#ifdef HAS_TROFF
+#ifdef ALT_SYSTEMS
+static char args[] = "M:P:S:adfhkm:p:tw?";
+#else
+static char args[] = "M:P:S:adfhkp:tw?";
+#endif
+#else
+#ifdef ALT_SYSTEMS
+static char args[] = "M:P:S:adfhkm:p:w?";
+#else
+static char args[] = "M:P:S:adfhkp:w?";
+#endif
+#endif
+
+#ifdef SETREUID
+uid_t ruid;
+uid_t euid;
+uid_t rgid;
+uid_t egid;
+#endif
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int status = 0;
+ char *nextarg;
+ char *tmp;
+ extern int optind;
+ extern char *mkprogname ();
+ char *is_section ();
+ char **get_section_list ();
+ void man_getopt ();
+ void do_apropos ();
+ void do_whatis ();
+ int man ();
+
+ prognam = mkprogname (argv[0]);
+
+ man_getopt (argc, argv);
+
+ if (optind == argc)
+ gripe_no_name ((char *)NULL);
+
+ section_list = get_section_list ();
+
+ if (optind == argc - 1)
+ {
+ tmp = is_section (argv[optind]);
+
+ if (tmp != NULL)
+ gripe_no_name (tmp);
+ }
+
+#ifdef SETREUID
+ ruid = getuid();
+ rgid = getgid();
+ euid = geteuid();
+ egid = getegid();
+ setreuid(-1, ruid);
+ setregid(-1, rgid);
+#endif
+
+ while (optind < argc)
+ {
+ nextarg = argv[optind++];
+
+ /*
+ * See if this argument is a valid section name. If not,
+ * is_section returns NULL.
+ */
+ tmp = is_section (nextarg);
+
+ if (tmp != NULL)
+ {
+ section = tmp;
+
+ if (debug)
+ fprintf (stderr, "\nsection: %s\n", section);
+
+ continue;
+ }
+
+ if (apropos)
+ do_apropos (nextarg);
+ else if (whatis)
+ do_whatis (nextarg);
+ else
+ {
+ status = man (nextarg);
+
+ if (status == 0)
+ gripe_not_found (nextarg, section);
+ }
+ }
+ return (status==0); /* status==1 --> exit(0),
+ status==0 --> exit(1) */
+}
+
+void
+usage ()
+{
+ static char usage_string[1024] = "%s, version %s\n\n";
+
+#ifdef HAS_TROFF
+#ifdef ALT_SYSTEMS
+ static char s1[] =
+ "usage: %s [-adfhktw] [section] [-M path] [-P pager] [-S list]\n\
+ [-m system] [-p string] name ...\n\n";
+#else
+ static char s1[] =
+ "usage: %s [-adfhktw] [section] [-M path] [-P pager] [-S list]\n\
+ [-p string] name ...\n\n";
+#endif
+#else
+#ifdef ALT_SYSTEMS
+ static char s1[] =
+ "usage: %s [-adfhkw] [section] [-M path] [-P pager] [-S list]\n\
+ [-m system] [-p string] name ...\n\n";
+#else
+ static char s1[] =
+ "usage: %s [-adfhkw] [section] [-M path] [-P pager] [-S list]\n\
+ [-p string] name ...\n\n";
+#endif
+#endif
+
+static char s2[] = " a : find all matching entries\n\
+ d : print gobs of debugging information\n\
+ f : same as whatis(1)\n\
+ h : print this help message\n\
+ k : same as apropos(1)\n";
+
+#ifdef HAS_TROFF
+ static char s3[] = " t : use troff to format pages for printing\n";
+#endif
+
+ static char s4[] = " w : print location of man page(s) that would be displayed\n\n\
+ M path : set search path for manual pages to `path'\n\
+ P pager : use program `pager' to display pages\n\
+ S list : colon separated section list\n";
+
+#ifdef ALT_SYSTEMS
+ static char s5[] = " m system : search for alternate system's man pages\n";
+#endif
+
+ static char s6[] = " p string : string tells which preprocessors to run\n\
+ e - [n]eqn(1) p - pic(1) t - tbl(1)\n\
+ g - grap(1) r - refer(1) v - vgrind(1)\n";
+
+ strcat (usage_string, s1);
+ strcat (usage_string, s2);
+
+#ifdef HAS_TROFF
+ strcat (usage_string, s3);
+#endif
+
+ strcat (usage_string, s4);
+
+#ifdef ALT_SYSTEMS
+ strcat (usage_string, s5);
+#endif
+
+ strcat (usage_string, s6);
+
+ fprintf (stderr, usage_string, prognam, version, prognam);
+ exit(1);
+}
+
+char **
+add_dir_to_mpath_list (mp, p)
+ char **mp;
+ char *p;
+{
+ int status;
+
+ status = is_directory (p);
+
+ if (status < 0)
+ {
+ fprintf (stderr, "Warning: couldn't stat file %s!\n", p);
+ }
+ else if (status == 0)
+ {
+ fprintf (stderr, "Warning: %s isn't a directory!\n", p);
+ }
+ else if (status == 1)
+ {
+ if (debug)
+ fprintf (stderr, "adding %s to manpathlist\n", p);
+
+ *mp++ = strdup (p);
+ }
+ return mp;
+}
+
+/*
+ * Get options from the command line and user environment.
+ */
+void
+man_getopt (argc, argv)
+ register int argc;
+ register char **argv;
+{
+ register int c;
+ register char *p;
+ register char *end;
+ register char **mp;
+ extern char *optarg;
+ extern int getopt ();
+ extern void downcase ();
+ extern char *manpath ();
+
+ while ((c = getopt (argc, argv, args)) != EOF)
+ {
+ switch (c)
+ {
+ case 'M':
+ manp = strdup (optarg);
+ break;
+ case 'P':
+ pager = strdup (optarg);
+ break;
+ case 'S':
+ colon_sep_section_list = strdup (optarg);
+ break;
+ case 'a':
+ findall++;
+ break;
+ case 'd':
+ debug++;
+ break;
+ case 'f':
+ if (troff)
+ gripe_incompatible ("-f and -t");
+ if (apropos)
+ gripe_incompatible ("-f and -k");
+ if (print_where)
+ gripe_incompatible ("-f and -w");
+ whatis++;
+ break;
+ case 'k':
+ if (troff)
+ gripe_incompatible ("-k and -t");
+ if (whatis)
+ gripe_incompatible ("-k and -f");
+ if (print_where)
+ gripe_incompatible ("-k and -w");
+ apropos++;
+ break;
+#ifdef ALT_SYSTEMS
+ case 'm':
+ alt_system++;
+ alt_system_name = strdup (optarg);
+ break;
+#endif
+ case 'p':
+ roff_directive = strdup (optarg);
+ break;
+#ifdef HAS_TROFF
+ case 't':
+ if (apropos)
+ gripe_incompatible ("-t and -k");
+ if (whatis)
+ gripe_incompatible ("-t and -f");
+ if (print_where)
+ gripe_incompatible ("-t and -w");
+ troff++;
+ break;
+#endif
+ case 'w':
+ if (apropos)
+ gripe_incompatible ("-w and -k");
+ if (whatis)
+ gripe_incompatible ("-w and -f");
+ if (troff)
+ gripe_incompatible ("-w and -t");
+ print_where++;
+ break;
+ case 'h':
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (pager == NULL || *pager == '\0')
+ if ((pager = getenv ("PAGER")) == NULL)
+ pager = strdup (PAGER);
+
+ if (debug)
+ fprintf (stderr, "\nusing %s as pager\n", pager);
+
+ if (manp == NULL)
+ {
+ if ((manp = manpath (0)) == NULL)
+ gripe_manpath ();
+
+ if (debug)
+ fprintf (stderr,
+ "\nsearch path for pages determined by manpath is\n%s\n\n",
+ manp);
+ }
+
+#ifdef ALT_SYSTEMS
+ if (alt_system_name == NULL || *alt_system_name == '\0')
+ if ((alt_system_name = getenv ("SYSTEM")) != NULL)
+ alt_system_name = strdup (alt_system_name);
+
+ if (alt_system_name != NULL && *alt_system_name != '\0')
+ downcase (alt_system_name);
+#endif
+
+ /*
+ * Expand the manpath into a list for easier handling.
+ */
+ mp = manpathlist;
+ for (p = manp; ; p = end+1)
+ {
+ if ((end = strchr (p, ':')) != NULL)
+ *end = '\0';
+
+#ifdef ALT_SYSTEMS
+ if (alt_system)
+ {
+ char buf[FILENAME_MAX];
+
+ if (debug)
+ fprintf (stderr, "Alternate system `%s' specified\n",
+ alt_system_name);
+
+ strcpy (buf, p);
+ strcat (buf, "/");
+ strcat (buf, alt_system_name);
+
+ mp = add_dir_to_mpath_list (mp, buf);
+ }
+ else
+ {
+ mp = add_dir_to_mpath_list (mp, p);
+ }
+#else
+ mp = add_dir_to_mpath_list (mp, p);
+#endif
+ if (end == NULL)
+ break;
+
+ *end = ':';
+ }
+ *mp = NULL;
+}
+
+/*
+ * Check to see if the argument is a valid section number. If the
+ * first character of name is a numeral, or the name matches one of
+ * the sections listed in section_list, we'll assume that it's a section.
+ * The list of sections in config.h simply allows us to specify oddly
+ * named directories like .../man3f. Yuk.
+ */
+char *
+is_section (name)
+ register char *name;
+{
+ register char **vs;
+
+ for (vs = section_list; *vs != NULL; vs++)
+ if ((strcmp (*vs, name) == NULL) || (isdigit (name[0])))
+ return strdup (name);
+
+ return NULL;
+}
+
+/*
+ * Handle the apropos option. Cheat by using another program.
+ */
+void
+do_apropos (name)
+ register char *name;
+{
+ register int len;
+ register char *command;
+
+ len = strlen (APROPOS) + strlen (name) + 2;
+
+ if ((command = (char *) malloc(len)) == NULL)
+ gripe_alloc (len, "command");
+
+ sprintf (command, "%s %s", APROPOS, name);
+
+ (void) do_system_command (command);
+
+ free (command);
+}
+
+/*
+ * Handle the whatis option. Cheat by using another program.
+ */
+void
+do_whatis (name)
+ register char *name;
+{
+ register int len;
+ register char *command;
+
+ len = strlen (WHATIS) + strlen (name) + 2;
+
+ if ((command = (char *) malloc(len)) == NULL)
+ gripe_alloc (len, "command");
+
+ sprintf (command, "%s %s", WHATIS, name);
+
+ (void) do_system_command (command);
+
+ free (command);
+}
+
+/*
+ * Change a name of the form ...man/man1/name.1 to ...man/cat1/name.1
+ * or a name of the form ...man/cat1/name.1 to ...man/man1/name.1
+ */
+char *
+convert_name (name, to_cat)
+ register char *name;
+ register int to_cat;
+{
+ register char *to_name;
+ register char *t1;
+ register char *t2 = NULL;
+
+#ifdef DO_COMPRESS
+ if (to_cat)
+ {
+ int len = strlen (name) + 3;
+ int cextlen = strlen(COMPRESS_EXT);
+
+ to_name = (char *) malloc (len);
+ if (to_name == NULL)
+ gripe_alloc (len, "to_name");
+ strcpy (to_name, name);
+ /* Avoid tacking it on twice */
+ if (strcmp(name + (len - (3 + cextlen)), COMPRESS_EXT))
+ strcat (to_name, COMPRESS_EXT);
+ }
+ else
+ to_name = strdup (name);
+#else
+ to_name = strdup (name);
+#endif
+
+ t1 = strrchr (to_name, '/');
+ if (t1 != NULL)
+ {
+ *t1 = NULL;
+ t2 = strrchr (to_name, '/');
+ *t1 = '/';
+ }
+
+ if (t2 == NULL)
+ gripe_converting_name (name, to_cat);
+
+ if (to_cat)
+ {
+ *(++t2) = 'c';
+ *(t2+2) = 't';
+ }
+ else
+ {
+ *(++t2) = 'm';
+ *(t2+2) = 'n';
+ }
+
+ if (debug)
+ fprintf (stderr, "to_name in convert_name () is: %s\n", to_name);
+
+ return to_name;
+}
+
+/*
+ * Try to find the man page corresponding to the given name. The
+ * reason we do this with globbing is because some systems have man
+ * page directories named man3 which contain files with names like
+ * XtPopup.3Xt. Rather than requiring that this program know about
+ * all those possible names, we simply try to match things like
+ * .../man[sect]/name[sect]*. This is *much* easier.
+ *
+ * Note that globbing is only done when the section is unspecified.
+ */
+char **
+glob_for_file (path, section, name, cat)
+ register char *path;
+ register char *section;
+ register char *name;
+ register int cat;
+{
+ char pathname[FILENAME_MAX];
+ char **gf;
+
+ if (cat)
+ sprintf (pathname, "%s/cat%s/%s.%s*", path, section, name, section);
+ else
+ sprintf (pathname, "%s/man%s/%s.%s*", path, section, name, section);
+
+ if (debug)
+ fprintf (stderr, "globbing %s\n", pathname);
+
+ gf = glob_filename (pathname);
+
+ if ((gf == (char **) -1 || *gf == NULL) && isdigit (*section))
+ {
+ if (cat)
+ sprintf (pathname, "%s/cat%s/%s.%c*", path, section, name, *section);
+ else
+ sprintf (pathname, "%s/man%s/%s.%c*", path, section, name, *section);
+
+ gf = glob_filename (pathname);
+ }
+ if ((gf == (char **) -1 || *gf == NULL) && isdigit (*section))
+ {
+ if (cat)
+ sprintf (pathname, "%s/cat%s/%s.0*", path, section, name);
+ else
+ sprintf (pathname, "%s/man%s/%s.0*", path, section, name);
+ if (debug)
+ fprintf (stderr, "globbing %s\n", pathname);
+ gf = glob_filename (pathname);
+ }
+ return gf;
+}
+
+/*
+ * Return an un-globbed name in the same form as if we were doing
+ * globbing.
+ */
+char **
+make_name (path, section, name, cat)
+ register char *path;
+ register char *section;
+ register char *name;
+ register int cat;
+{
+ register int i = 0;
+ static char *names[3];
+ char buf[FILENAME_MAX];
+
+ if (cat)
+ sprintf (buf, "%s/cat%s/%s.%s", path, section, name, section);
+ else
+ sprintf (buf, "%s/man%s/%s.%s", path, section, name, section);
+
+ if (access (buf, R_OK) == 0)
+ names[i++] = strdup (buf);
+
+ /*
+ * If we're given a section that looks like `3f', we may want to try
+ * file names like .../man3/foo.3f as well. This seems a bit
+ * kludgey to me, but what the hey...
+ */
+ if (section[1] != '\0')
+ {
+ if (cat)
+ sprintf (buf, "%s/cat%c/%s.%s", path, section[0], name, section);
+ else
+ sprintf (buf, "%s/man%c/%s.%s", path, section[0], name, section);
+
+ if (access (buf, R_OK) == 0)
+ names[i++] = strdup (buf);
+ }
+
+ names[i] = NULL;
+
+ return &names[0];
+}
+
+char *
+get_expander (file)
+ char *file;
+{
+ char *end = file + (strlen (file) - 1);
+
+ while (end > file && end[-1] != '.')
+ --end;
+ if (end == file)
+ return NULL;
+#ifdef FCAT
+ if (*end == 'F')
+ return FCAT;
+#endif /* FCAT */
+#ifdef YCAT
+ if (*end == 'Y')
+ return YCAT;
+#endif /* YCAT */
+#ifdef ZCAT
+ if (*end == 'Z' || !strcmp(end, "gz"))
+ return ZCAT;
+#endif /* ZCAT */
+ return NULL;
+}
+
+/*
+ * Simply display the preformatted page.
+ */
+int
+display_cat_file (file)
+ register char *file;
+{
+ register int found;
+ char command[FILENAME_MAX];
+
+ found = 0;
+
+ if (access (file, R_OK) == 0)
+ {
+ char *expander = get_expander (file);
+
+ if (expander != NULL)
+ sprintf (command, "%s %s | %s", expander, file, pager);
+ else
+ sprintf (command, "%s %s", pager, file);
+
+ found = do_system_command (command);
+ }
+ return found;
+}
+
+/*
+ * Try to find the ultimate source file. If the first line of the
+ * current file is not of the form
+ *
+ * .so man3/printf.3s
+ *
+ * the input file name is returned.
+ */
+char *
+ultimate_source (name, path)
+ char *name;
+ char *path;
+{
+ static char buf[BUFSIZ];
+ static char ult[FILENAME_MAX];
+
+ FILE *fp;
+ char *beg;
+ char *end;
+
+ strcpy (ult, name);
+ strcpy (buf, name);
+
+ next:
+
+ if ((fp = fopen (ult, "r")) == NULL)
+ return buf;
+
+ if (fgets (buf, BUFSIZ, fp) == NULL)
+ return ult;
+
+ if (strlen (buf) < 5)
+ return ult;
+
+ beg = buf;
+ if (*beg++ == '.' && *beg++ == 's' && *beg++ == 'o')
+ {
+ while ((*beg == ' ' || *beg == '\t') && *beg != '\0')
+ beg++;
+
+ end = beg;
+ while (*end != ' ' && *end != '\t' && *end != '\n' && *end != '\0')
+ end++;
+
+ *end = '\0';
+
+ strcpy (ult, path);
+ strcat (ult, "/");
+ strcat (ult, beg);
+
+ strcpy (buf, ult);
+
+ goto next;
+ }
+
+ if (debug)
+ fprintf (stderr, "found ultimate source file %s\n", ult);
+
+ return ult;
+}
+
+void
+add_directive (first, d, file, buf)
+ int *first;
+ char *d;
+ char *file;
+ char *buf;
+{
+ if (strcmp (d, "") != 0)
+ {
+ if (*first)
+ {
+ *first = 0;
+ strcpy (buf, d);
+ strcat (buf, " ");
+ strcat (buf, file);
+ }
+ else
+ {
+ strcat (buf, " | ");
+ strcat (buf, d);
+ }
+ }
+}
+
+int
+parse_roff_directive (cp, file, buf)
+ char *cp;
+ char *file;
+ char *buf;
+{
+ char c;
+ int first = 1;
+ int tbl_found = 0;
+
+ while ((c = *cp++) != '\0')
+ {
+ switch (c)
+ {
+ case 'e':
+
+ if (debug)
+ fprintf (stderr, "found eqn(1) directive\n");
+
+ if (troff)
+ add_directive (&first, EQN, file, buf);
+ else
+ add_directive (&first, NEQN, file, buf);
+
+ break;
+
+ case 'g':
+
+ if (debug)
+ fprintf (stderr, "found grap(1) directive\n");
+
+ add_directive (&first, GRAP, file, buf);
+
+ break;
+
+ case 'p':
+
+ if (debug)
+ fprintf (stderr, "found pic(1) directive\n");
+
+ add_directive (&first, PIC, file, buf);
+
+ break;
+
+ case 't':
+
+ if (debug)
+ fprintf (stderr, "found tbl(1) directive\n");
+
+ tbl_found++;
+ add_directive (&first, TBL, file, buf);
+ break;
+
+ case 'v':
+
+ if (debug)
+ fprintf (stderr, "found vgrind(1) directive\n");
+
+ add_directive (&first, VGRIND, file, buf);
+ break;
+
+ case 'r':
+
+ if (debug)
+ fprintf (stderr, "found refer(1) directive\n");
+
+ add_directive (&first, REFER, file, buf);
+ break;
+
+ case ' ':
+ case '\t':
+ case '\n':
+
+ goto done;
+
+ default:
+
+ return -1;
+ }
+ }
+
+ done:
+
+ if (first)
+ return 1;
+
+#ifdef HAS_TROFF
+ if (troff)
+ {
+ strcat (buf, " | ");
+ strcat (buf, TROFF);
+ }
+ else
+#endif
+ {
+ strcat (buf, " | ");
+ strcat (buf, NROFF);
+ }
+ if (tbl_found && !troff && strcmp (COL, "") != 0)
+ {
+ strcat (buf, " | ");
+ strcat (buf, COL);
+ }
+
+ return 0;
+}
+
+char *
+make_roff_command (file)
+ char *file;
+{
+ FILE *fp;
+ char line [BUFSIZ];
+ static char buf [BUFSIZ];
+ int status;
+ char *cp;
+
+ if (roff_directive != NULL)
+ {
+ if (debug)
+ fprintf (stderr, "parsing directive from command line\n");
+
+ status = parse_roff_directive (roff_directive, file, buf);
+
+ if (status == 0)
+ return buf;
+
+ if (status == -1)
+ gripe_roff_command_from_command_line (file);
+ }
+
+ if ((fp = fopen (file, "r")) != NULL)
+ {
+ cp = line;
+ fgets (line, 100, fp);
+ if (*cp++ == '\'' && *cp++ == '\\' && *cp++ == '"' && *cp++ == ' ')
+ {
+ if (debug)
+ fprintf (stderr, "parsing directive from file\n");
+
+ status = parse_roff_directive (cp, file, buf);
+
+ fclose (fp);
+
+ if (status == 0)
+ return buf;
+
+ if (status == -1)
+ gripe_roff_command_from_file (file);
+ }
+ }
+ else
+ {
+ /*
+ * Is there really any point in continuing to look for
+ * preprocessor options if we can't even read the man page source?
+ */
+ gripe_reading_man_file (file);
+ return NULL;
+ }
+
+ if ((cp = getenv ("MANROFFSEQ")) != NULL)
+ {
+ if (debug)
+ fprintf (stderr, "parsing directive from environment\n");
+
+ status = parse_roff_directive (cp, file, buf);
+
+ if (status == 0)
+ return buf;
+
+ if (status == -1)
+ gripe_roff_command_from_env ();
+ }
+
+ if (debug)
+ fprintf (stderr, "using default preprocessor sequence\n");
+
+ if ((cp = get_expander(file)) == NULL)
+ cp = "cat";
+ sprintf(buf, "%s %s | ", cp, file);
+#ifdef HAS_TROFF
+ if (troff)
+ {
+ if (strcmp (TBL, "") != 0)
+ {
+ strcat (buf, TBL);
+ strcat (buf, " | ");
+ strcat (buf, TROFF);
+ }
+ else
+ {
+ strcat (buf, TROFF);
+ }
+ }
+ else
+#endif
+ {
+ if (strcmp (TBL, "") != 0)
+ {
+ strcat (buf, TBL);
+ strcat (buf, " | ");
+ strcat (buf, NROFF);
+ }
+ else
+ {
+ strcpy (buf, NROFF);
+ }
+
+ if (strcmp (COL, "") != 0)
+ {
+ strcat (buf, " | ");
+ strcat (buf, COL);
+ }
+ }
+ return buf;
+}
+
+/*
+ * Try to format the man page and create a new formatted file. Return
+ * 1 for success and 0 for failure.
+ */
+int
+make_cat_file (path, man_file, cat_file)
+ register char *path;
+ register char *man_file;
+ register char *cat_file;
+{
+ int status;
+ int mode;
+ FILE *fp;
+ char *roff_command;
+ char command[FILENAME_MAX];
+ char temp[FILENAME_MAX];
+
+ sprintf(temp, "%s.tmp", cat_file);
+ if ((fp = fopen (temp, "w")) != NULL)
+ {
+ fclose (fp);
+ unlink (temp);
+
+ roff_command = make_roff_command (man_file);
+ if (roff_command == NULL)
+ return 0;
+ else
+#ifdef DO_COMPRESS
+ sprintf (command, "(cd %s ; %s | %s > %s)", path,
+ roff_command, COMPRESSOR, temp);
+#else
+ sprintf (command, "(cd %s ; %s > %s)", path,
+ roff_command, temp);
+#endif
+ /*
+ * Don't let the user interrupt the system () call and screw up
+ * the formatted man page if we're not done yet.
+ */
+ fprintf (stderr, "Formatting page, please wait...");
+ fflush(stderr);
+
+ status = do_system_command (command);
+
+ if (!status) {
+ fprintf(stderr, "Failed.\n");
+ unlink(temp);
+ exit(1);
+ }
+ else {
+ if (rename(temp, cat_file) == -1) {
+ /* FS might be sticky */
+ sprintf(command, "cp %s %s", temp, cat_file);
+ if (system(command))
+ fprintf(stderr,
+ "\nHmm! Can't seem to rename %s to %s, check permissions on man dir!\n",
+ temp, cat_file);
+ unlink(temp);
+ return 0;
+ }
+ }
+ fprintf(stderr, "Done.\n");
+ if (status == 1)
+ {
+ mode = CATMODE;
+ chmod (cat_file, mode);
+
+ if (debug)
+ fprintf (stderr, "mode of %s is now %o\n", cat_file, mode);
+ }
+
+
+ return 1;
+ }
+ else
+ {
+ if (debug)
+ fprintf (stderr, "Couldn't open %s for writing.\n", cat_file);
+
+ return 0;
+ }
+}
+
+/*
+ * Try to format the man page source and save it, then display it. If
+ * that's not possible, try to format the man page source and display
+ * it directly.
+ *
+ * Note that we've already been handed the name of the ultimate source
+ * file at this point.
+ */
+int
+format_and_display (path, man_file, cat_file)
+ register char *path;
+ register char *man_file;
+ register char *cat_file;
+{
+ int status;
+ register int found;
+ char *roff_command;
+ char command[FILENAME_MAX];
+
+ found = 0;
+
+ if (access (man_file, R_OK) != 0)
+ return 0;
+
+ if (troff)
+ {
+ roff_command = make_roff_command (man_file);
+ if (roff_command == NULL)
+ return 0;
+ else
+ sprintf (command, "(cd %s ; %s)", path, roff_command);
+
+ found = do_system_command (command);
+ }
+ else
+ {
+ status = is_newer (man_file, cat_file);
+ if (debug)
+ fprintf (stderr, "status from is_newer() = %d\n");
+
+ if (status == 1 || status == -2)
+ {
+ /*
+ * Cat file is out of date. Try to format and save it.
+ */
+ if (print_where)
+ {
+ printf ("%s\n", man_file);
+ found++;
+ }
+ else
+ {
+
+#ifdef SETREUID
+ setreuid(-1, euid);
+ setregid(-1, egid);
+#endif
+
+ found = make_cat_file (path, man_file, cat_file);
+
+#ifdef SETREUID
+ setreuid(-1, ruid);
+ setregid(-1, rgid);
+
+ if (!found)
+ {
+ /* Try again as real user - see note below.
+ By running with
+ effective group (user) ID == real group (user) ID
+ except for the call above, I believe the problems
+ of reading private man pages is avoided. */
+ found = make_cat_file (path, man_file, cat_file);
+ }
+#endif
+#ifdef SECURE_MAN_UID
+ if (!found)
+ {
+ /*
+ * Try again as real user. Note that for private
+ * man pages, we won't even get this far unless the
+ * effective user can read the real user's man page
+ * source. Also, if we are trying to find all the
+ * man pages, this will probably make it impossible
+ * to make cat files in the system directories if
+ * the real user's man directories are searched
+ * first, because there's no way to undo this (is
+ * there?). Yikes, am I missing something obvious?
+ */
+ setuid (getuid ());
+
+ found = make_cat_file (path, man_file, cat_file);
+ }
+#endif
+ if (found)
+ {
+ /*
+ * Creating the cat file worked. Now just display it.
+ */
+ (void) display_cat_file (cat_file);
+ }
+ else
+ {
+ /*
+ * Couldn't create cat file. Just format it and
+ * display it through the pager.
+ */
+ roff_command = make_roff_command (man_file);
+ if (roff_command == NULL)
+ return 0;
+ else
+ sprintf (command, "(cd %s ; %s | %s)", path,
+ roff_command, pager);
+
+ found = do_system_command (command);
+ }
+ }
+ }
+ else if (access (cat_file, R_OK) == 0)
+ {
+ /*
+ * Formatting not necessary. Cat file is newer than source
+ * file, or source file is not present but cat file is.
+ */
+ if (print_where)
+ {
+ printf ("%s (source: %s)\n", cat_file, man_file);
+ found++;
+ }
+ else
+ {
+ found = display_cat_file (cat_file);
+ }
+ }
+ }
+ return found;
+}
+
+/*
+ * See if the preformatted man page or the source exists in the given
+ * section.
+ */
+int
+try_section (path, section, name, glob)
+ register char *path;
+ register char *section;
+ register char *name;
+ register int glob;
+{
+ register int found = 0;
+ register int to_cat;
+ register int cat;
+ register char **names;
+ register char **np;
+
+ if (debug)
+ {
+ if (glob)
+ fprintf (stderr, "trying section %s with globbing\n", section);
+ else
+ fprintf (stderr, "trying section %s without globbing\n", section);
+ }
+
+#ifndef NROFF_MISSING
+ /*
+ * Look for man page source files.
+ */
+ cat = 0;
+ if (glob)
+ names = glob_for_file (path, section, name, cat);
+ else
+ names = make_name (path, section, name, cat);
+
+ if (names == (char **) -1 || *names == NULL)
+ /*
+ * No files match. See if there's a preformatted page around that
+ * we can display.
+ */
+#endif /* NROFF_MISSING */
+ {
+ if (!troff)
+ {
+ cat = 1;
+ if (glob)
+ names = glob_for_file (path, section, name, cat);
+ else
+ names = make_name (path, section, name, cat);
+
+ if (names != (char **) -1 && *names != NULL)
+ {
+ for (np = names; *np != NULL; np++)
+ {
+ if (print_where)
+ {
+ printf ("%s\n", *np);
+ found++;
+ }
+ else
+ {
+ found += display_cat_file (*np);
+ }
+ }
+ }
+ }
+ }
+#ifndef NROFF_MISSING
+ else
+ {
+ for (np = names; *np != NULL; np++)
+ {
+ register char *cat_file = NULL;
+ register char *man_file;
+
+ man_file = ultimate_source (*np, path);
+
+ if (!troff)
+ {
+ to_cat = 1;
+
+ cat_file = convert_name (man_file, to_cat);
+
+ if (debug)
+ fprintf (stderr, "will try to write %s if needed\n", cat_file);
+ }
+
+ found += format_and_display (path, man_file, cat_file);
+ }
+ }
+#endif /* NROFF_MISSING */
+ return found;
+}
+
+/*
+ * Search for manual pages.
+ *
+ * If preformatted manual pages are supported, look for the formatted
+ * file first, then the man page source file. If they both exist and
+ * the man page source file is newer, or only the source file exists,
+ * try to reformat it and write the results in the cat directory. If
+ * it is not possible to write the cat file, simply format and display
+ * the man file.
+ *
+ * If preformatted pages are not supported, or the troff option is
+ * being used, only look for the man page source file.
+ *
+ */
+int
+man (name)
+ char *name;
+{
+ register int found;
+ register int glob;
+ register char **mp;
+ register char **sp;
+
+ found = 0;
+
+ fflush (stdout);
+ if (section != NULL)
+ {
+ for (mp = manpathlist; *mp != NULL; mp++)
+ {
+ if (debug)
+ fprintf (stderr, "\nsearching in %s\n", *mp);
+
+ glob = 1;
+
+ found += try_section (*mp, section, name, glob);
+
+ if (found && !findall) /* i.e. only do this section... */
+ return found;
+ }
+ }
+ else
+ {
+ for (sp = section_list; *sp != NULL; sp++)
+ {
+ for (mp = manpathlist; *mp != NULL; mp++)
+ {
+ if (debug)
+ fprintf (stderr, "\nsearching in %s\n", *mp);
+
+ glob = 1;
+
+ found += try_section (*mp, *sp, name, glob);
+
+ if (found && !findall) /* i.e. only do this section... */
+ return found;
+ }
+ }
+ }
+ return found;
+}
+
+char **
+get_section_list ()
+{
+ int i;
+ char *p;
+ char *end;
+ static char *tmp_section_list[100];
+
+ if (colon_sep_section_list == NULL)
+ {
+ if ((p = getenv ("MANSECT")) == NULL)
+ {
+ return std_sections;
+ }
+ else
+ {
+ colon_sep_section_list = strdup (p);
+ }
+ }
+
+ i = 0;
+ for (p = colon_sep_section_list; ; p = end+1)
+ {
+ if ((end = strchr (p, ':')) != NULL)
+ *end = '\0';
+
+ tmp_section_list[i++] = strdup (p);
+
+ if (end == NULL)
+ break;
+ }
+
+ tmp_section_list [i] = NULL;
+ return tmp_section_list;
+}
diff --git a/gnu/usr.bin/man/man/man.man b/gnu/usr.bin/man/man/man.man
new file mode 100644
index 0000000..bddfc02
--- /dev/null
+++ b/gnu/usr.bin/man/man/man.man
@@ -0,0 +1,134 @@
+.\" Man page for man
+.\"
+.\" Copyright (c) 1990, 1991, John W. Eaton.
+.\"
+.\" You may distribute under the terms of the GNU General Public
+.\" License as specified in the README file that comes with the man 1.0
+.\" distribution.
+.\"
+.\" John W. Eaton
+.\" jwe@che.utexas.edu
+.\" Department of Chemical Engineering
+.\" The University of Texas at Austin
+.\" Austin, Texas 78712
+.\"
+.TH man 1 "Jan 5, 1991"
+.LO 1
+.SH NAME
+man \- format and display the on-line manual pages
+.SH SYNOPSIS
+man [\-adfhktw] [\-m system] [\-p string] [\-M path] [\-P pager]
+[\-S list] [section] name ...
+.SH DESCRIPTION
+man formats and displays the on-line manual pages. This version knows
+about the MANPATH and PAGER environment variables, so you can have
+your own set(s) of personal man pages and choose whatever program you
+like to display the formatted pages. If section is specified, man
+only looks in that section of the manual. You may also specify the
+order to search the sections for entries and which preprocessors to
+run on the source files via command line options or environment
+variables. If enabled by the system administrator, formatted man
+pages will also be compressed with the `%compress%' command to save
+space.
+.SH OPTIONS
+.TP
+.B \-\^M " path"
+Specify an alternate manpath. By default, man uses
+.B manpath
+to determine the path to search. This option overrides the
+.B MANPATH
+environment variable.
+.TP
+.B \-\^P " pager"
+Specify which pager to use. By default, man uses
+.B %pager%,
+This option overrides the
+.B PAGER
+environment variable.
+.TP
+.B \-\^S " list"
+List is a colon separated list of manual sections to search.
+This option overrides the
+.B MANSECT
+environment variable.
+.TP
+.B \-\^a
+By default, man will exit after displaying the first manual page it
+finds. Using this option forces man to display all the manual pages
+that match
+.B name,
+not just the first.
+.TP
+.B \-\^d
+Don't actually display the man pages, but do print gobs of debugging
+information.
+.TP
+.B \-\^f
+Equivalent to
+.B whatis.
+.TP
+.B \-\^h
+Print a one line help message and exit.
+.TP
+.B \-\^k
+Equivalent to
+.B apropos.
+.TP
+.B \-\^m " system"
+Specify an alternate set of man pages to search based on the system
+name given.
+.TP
+.B \-\^p " string"
+Specify the sequence of preprocessors to run before nroff or troff.
+Not all installations will have a full set of preprocessors.
+Some of the preprocessors and the letters used to designate them are:
+eqn (e), grap (g), pic (p), tbl (t), vgrind (v), refer (r).
+This option overrides the
+.B MANROFFSEQ
+environment variable.
+.TP
+.B \-\^t
+Use
+.B %troff%
+to format the manual page, passing the output to
+.B stdout.
+The output from
+.B %troff%
+may need to be passed through some filter or another before being
+printed.
+.TP
+.B \-\^w
+Don't actually display the man pages, but do print the location(s) of
+the files that would be formatted or displayed.
+.SH ENVIRONMENT
+.TP \w'MANROFFSEQ\ \ 'u
+.B MANPATH
+If
+.B MANPATH
+is set, its value is used as the path to search for manual pages.
+.TP
+.B MANROFFSEQ
+If
+.B MANROFFSEQ
+is set, its value is used to determine the set of preprocessors run
+before running nroff or troff. By default, pages are passed through
+the table preprocessor before nroff.
+.TP
+.B MANSEC
+If
+.B MANSEC
+is set, its value is used to determine which manual sections to search.
+.TP
+.B PAGER
+If
+.B PAGER
+is set, its value is used as the name of the program to use to display
+the man page. By default,
+.B %pager%
+is used.
+.SH "SEE ALSO"
+apropos(1), whatis(1), manpath(1), less(1), groff(1).
+.SH BUGS
+The
+.B \-t
+option only works if a troff-like program is installed.
diff --git a/gnu/usr.bin/man/man/manpath.c b/gnu/usr.bin/man/man/manpath.c
new file mode 100644
index 0000000..ccf7a55
--- /dev/null
+++ b/gnu/usr.bin/man/man/manpath.c
@@ -0,0 +1,520 @@
+/*
+ * manpath.c
+ *
+ * Copyright (c) 1990, 1991, John W. Eaton.
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the file COPYING that comes with the man
+ * distribution.
+ *
+ * John W. Eaton
+ * jwe@che.utexas.edu
+ * Department of Chemical Engineering
+ * The University of Texas at Austin
+ * Austin, Texas 78712
+ */
+
+#define MANPATH_MAIN
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "config.h"
+#include "manpath.h"
+#include "gripes.h"
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+extern int fprintf ();
+extern int strcmp ();
+extern int strncmp ();
+extern char *memcpy ();
+extern char *getenv();
+extern char *malloc();
+extern void free ();
+extern int exit ();
+#endif
+
+extern char *strdup ();
+extern int is_directory ();
+
+#ifndef MAIN
+extern int debug;
+#endif
+
+#ifdef MAIN
+
+#ifndef STDC_HEADERS
+extern char *strcpy ();
+extern int fflush ();
+#endif
+
+char *prognam;
+int debug;
+
+/*
+ * Examine user's PATH and print a reasonable MANPATH.
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int quiet;
+ char *mp;
+ extern int getopt ();
+ extern char *mkprogname ();
+ void usage ();
+ char *manpath ();
+
+ quiet = 1;
+
+ prognam = mkprogname (argv[0]);
+
+ while ((c = getopt (argc, argv, "dhq?")) != EOF)
+ {
+ switch (c)
+ {
+ case 'd':
+ debug++;
+ break;
+ case 'q':
+ quiet = 0;
+ break;
+ case '?':
+ case 'h':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ mp = manpath (quiet);
+
+ fprintf (stdout, "%s\n", mp);
+ fflush (stdout);
+
+ return 0;
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "usage: %s [-q]\n", prognam);
+ exit (1);
+}
+#endif /* MAIN */
+
+/*
+ * If the environment variable MANPATH is set, return it.
+ * If the environment variable PATH is set and has a nonzero length,
+ * try to determine the corresponding manpath, otherwise, return the
+ * default manpath.
+ *
+ * The manpath.config file is used to map system wide /bin directories
+ * to top level man page directories.
+ *
+ * For directories which are in the user's path but not in the
+ * manpath.config file, see if there is a subdirectory `man' or `MAN'.
+ * If so, add that directory to the path. Example: user has
+ * $HOME/bin in his path and the directory $HOME/bin/man exists -- the
+ * directory $HOME/bin/man will be added to the manpath.
+ */
+char *
+manpath (perrs)
+ register int perrs;
+{
+ register int len;
+ register char *manpathlist;
+ register char *path;
+ int get_dirlist ();
+ char *def_path ();
+ char *get_manpath ();
+
+ if (get_dirlist ())
+ gripe_reading_mp_config ();
+
+ if ((manpathlist = getenv ("MANPATH")) != NULL)
+ /*
+ * This must be it.
+ */
+ {
+ if (perrs)
+ fprintf (stderr, "(Warning: MANPATH environment variable set)\n");
+ return strdup (manpathlist);
+ }
+ else if ((path = getenv ("PATH")) == NULL)
+ /*
+ * Things aren't going to work well, but hey...
+ */
+ {
+ if (perrs)
+ fprintf (stderr, "Warning: path not set\n");
+ return def_path (perrs);
+ }
+ else
+ {
+ if ((len = strlen (path)) == 0)
+ /*
+ * Things aren't going to work well here either...
+ */
+ {
+ if (perrs)
+ fprintf (stderr, "Warning: path set but has zero length\n");
+ return def_path (perrs);
+ }
+ return get_manpath (perrs, path);
+ }
+}
+
+/*
+ * Get the list of bin directories and the corresponding man
+ * directories from the manpath.config file.
+ *
+ * This is ugly.
+ */
+int
+get_dirlist ()
+{
+ int i;
+ char *bp;
+ char *p;
+ char buf[BUFSIZ];
+ DIRLIST *dlp = list;
+ FILE *config;
+
+ if ((config = fopen (config_file, "r")) == NULL)
+ gripe_getting_mp_config (config_file);
+
+ while ((bp = fgets (buf, BUFSIZ, config)) != NULL)
+ {
+ while (*bp && (*bp == ' ' || *bp == '\t'))
+ bp++;
+
+ if (*bp == '#' || *bp == '\n')
+ continue;
+
+ if (!strncmp ("MANBIN", bp, 6))
+ continue;
+
+ if (!strncmp ("MANDATORY_MANPATH", bp, 17))
+ {
+ if ((p = strchr (bp, ' ')) == NULL)
+ if ((p = strchr (bp, '\t')) == NULL)
+ return -1;
+
+ bp = p;
+
+ dlp->mandatory = 1;
+
+ while (*bp && *bp != '\n' && (*bp == ' ' || *bp == '\t'))
+ bp++;
+
+ i = 0;
+ while (*bp && *bp != '\n' && *bp != ' ' && *bp != '\t')
+ dlp->mandir[i++] = *bp++;
+ dlp->mandir[i] = '\0';
+
+ if (debug)
+ fprintf (stderr, "found mandatory man directory %s\n",
+ dlp->mandir);
+ }
+ else if (!strncmp ("MANPATH_MAP", bp, 11))
+ {
+ if ((p = strchr (bp, ' ')) == NULL)
+ if ((p = strchr (bp, '\t')) == NULL)
+ return -1;
+
+ bp = p;
+
+ dlp->mandatory = 0;
+
+ while (*bp && *bp != '\n' && (*bp == ' ' || *bp == '\t'))
+ bp++;
+
+ i = 0;
+ while (*bp && *bp != '\n' && *bp != ' ' && *bp != '\t')
+ dlp->bin[i++] = *bp++;
+ dlp->bin[i] = '\0';
+
+ while (*bp && *bp != '\n' && (*bp == ' ' || *bp == '\t'))
+ bp++;
+
+ i = 0;
+ while (*bp && *bp != '\n' && *bp != ' ' && *bp != '\t')
+ dlp->mandir[i++] = *bp++;
+ dlp->mandir[i] = '\0';
+
+ if (debug)
+ fprintf (stderr, "found manpath map %s --> %s\n",
+ dlp->bin, dlp->mandir);
+ }
+ else
+ {
+ gripe_reading_mp_config ();
+ }
+ dlp++;
+ }
+
+ dlp->bin[0] = '\0';
+ dlp->mandir[0] = '\0';
+ dlp->mandatory = 0;
+
+ return 0;
+}
+
+/*
+ * Construct the default manpath. This picks up mandatory manpaths
+ * only.
+ */
+char *
+def_path (perrs)
+ int perrs;
+{
+ register int len;
+ register char *manpathlist, *p;
+ register DIRLIST *dlp;
+
+ len = 0;
+ dlp = list;
+ while (dlp->mandatory != 0)
+ {
+ len += strlen (dlp->mandir) + 1;
+ dlp++;
+ }
+
+ manpathlist = (char *) malloc (len);
+ if (manpathlist == NULL)
+ gripe_alloc (len, "manpathlist");
+
+ *manpathlist = '\0';
+
+ dlp = list;
+ p = manpathlist;
+ while (dlp->mandatory != 0)
+ {
+ int status;
+ char *path = dlp->mandir;
+
+ status = is_directory(path);
+
+ if (status < 0 && perrs)
+ {
+ fprintf (stderr, "Warning: couldn't stat file %s!\n", path);
+ }
+ else if (status == 0 && perrs)
+ {
+ fprintf (stderr, "Warning: standard directory %s doesn't exist!\n",
+ path);
+ }
+ else if (status == 1)
+ {
+ len = strlen (path);
+ memcpy (p, path, len);
+ p += len;
+ *p++ = ':';
+ dlp++;
+ }
+ }
+
+ p[-1] = '\0';
+
+ return manpathlist;
+}
+
+/*
+ * For each directory in the user's path, see if it is one of the
+ * directories listed in the manpath.config file. If so, and it is
+ * not already in the manpath, add it. If the directory is not listed
+ * in the manpath.config file, see if there is a subdirectory `man' or
+ * `MAN'. If so, and it is not already in the manpath, add it.
+ * Example: user has $HOME/bin in his path and the directory
+ * $HOME/bin/man exists -- the directory $HOME/bin/man will be added
+ * to the manpath.
+ */
+char *
+get_manpath (perrs, path)
+ register int perrs;
+ register char *path;
+{
+ register int len;
+ register char *tmppath;
+ register char *t;
+ register char *p;
+ register char **lp;
+ register char *end;
+ register char *manpathlist;
+ register DIRLIST *dlp;
+ void add_dir_to_list ();
+ char *has_subdirs ();
+
+ tmppath = strdup (path);
+
+ for (p = tmppath; ; p = end+1)
+ {
+ if (end = strchr(p, ':'))
+ *end = '\0';
+
+ if (debug)
+ fprintf (stderr, "\npath directory %s ", p);
+
+ /*
+ * The directory we're working on is in the config file.
+ * If we haven't added it to the list yet, do.
+ */
+ for (dlp = list; dlp->mandir[0] != '\0'; dlp++)
+ if (dlp->bin[0] != '\0' && !strcmp (p, dlp->bin))
+ {
+ if (debug)
+ fprintf (stderr, "is in the config file\n");
+
+ add_dir_to_list (tmplist, dlp->mandir, perrs);
+ goto found;
+ }
+
+ /*
+ * The directory we're working on isn't in the config file. See
+ * if it has man or MAN subdirectories. If so, and it hasn't
+ * been added to the list, do.
+ */
+ if (debug)
+ fprintf (stderr, "is not in the config file\n");
+
+ t = has_subdirs (p);
+ if (t != NULL)
+ {
+ if (debug)
+ fprintf (stderr, "but it does have a man or MAN subdirectory\n");
+
+ add_dir_to_list (tmplist, t, perrs);
+ free (t);
+ }
+ else
+ {
+ if (debug)
+ fprintf (stderr, "and doesn't have man or MAN subdirectories\n");
+ }
+
+ found:
+
+ if (!end)
+ break;
+ }
+
+ if (debug)
+ fprintf (stderr, "\nadding mandatory man directories\n\n");
+
+ dlp = list;
+ while (dlp->mandatory != 0)
+ {
+ add_dir_to_list (tmplist, dlp->mandir, perrs);
+ dlp++;
+ }
+
+ len = 0;
+ lp = tmplist;
+ while (*lp != NULL)
+ {
+ len += strlen (*lp) + 1;
+ lp++;
+ }
+
+ manpathlist = (char *) malloc (len);
+ if (manpathlist == NULL)
+ gripe_alloc (len, "manpathlist");
+
+ *manpathlist = '\0';
+
+ lp = tmplist;
+ p = manpathlist;
+ while (*lp != NULL)
+ {
+ len = strlen (*lp);
+ memcpy (p, *lp, len);
+ p += len;
+ *p++ = ':';
+ lp++;
+ }
+
+ p[-1] = '\0';
+
+ return manpathlist;
+}
+
+/*
+ * Add a directory to the manpath list if it isn't already there.
+ */
+void
+add_dir_to_list (lp, dir, perrs)
+ char **lp;
+ char *dir;
+ int perrs;
+{
+ extern char *strdup ();
+ int status;
+
+ while (*lp != NULL)
+ {
+ if (!strcmp (*lp, dir))
+ {
+ if (debug)
+ fprintf (stderr, "%s is already in the manpath\n", dir);
+ return;
+ }
+ lp++;
+ }
+ /*
+ * Not found -- add it.
+ */
+ status = is_directory(dir);
+
+ if (status < 0 && perrs)
+ {
+ fprintf (stderr, "Warning: couldn't stat file %s!\n", dir);
+ }
+ else if (status == 0 && perrs)
+ {
+ fprintf (stderr, "Warning: %s isn't a directory!\n", dir);
+ }
+ else if (status == 1)
+ {
+ if (debug)
+ fprintf (stderr, "adding %s to manpath\n", dir);
+
+ *lp = strdup (dir);
+ }
+}
+
+/*
+ * Check to see if the current directory has man or MAN
+ * subdirectories.
+ */
+char *
+has_subdirs (p)
+ register char *p;
+{
+ int len;
+ register char *t;
+
+ len = strlen (p);
+
+ t = (char *) malloc ((unsigned) len + 5);
+ if (t == NULL)
+ gripe_alloc (len+5, "p\n");
+
+ memcpy (t, p, len);
+ strcpy (t + len, "/man");
+
+ if (is_directory (t) == 1)
+ return t;
+
+ strcpy (t + len, "/MAN");
+
+ if (is_directory (t) == 1)
+ return t;
+
+ return NULL;
+}
diff --git a/gnu/usr.bin/man/man/manpath.h b/gnu/usr.bin/man/man/manpath.h
new file mode 100644
index 0000000..a61761f
--- /dev/null
+++ b/gnu/usr.bin/man/man/manpath.h
@@ -0,0 +1,26 @@
+/*
+ * manpath.h
+ *
+ * Copyright (c) 1990, 1991, John W. Eaton.
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the file COPYING that comes with the man
+ * distribution.
+ *
+ * John W. Eaton
+ * jwe@che.utexas.edu
+ * Department of Chemical Engineering
+ * The University of Texas at Austin
+ * Austin, Texas 78712
+ */
+
+typedef struct
+{
+ char mandir[MAXPATHLEN];
+ char bin[MAXPATHLEN];
+ int mandatory;
+} DIRLIST;
+
+DIRLIST list[MAXDIRS];
+
+char *tmplist[MAXDIRS];
diff --git a/gnu/usr.bin/man/man/ndir.h b/gnu/usr.bin/man/man/ndir.h
new file mode 100644
index 0000000..438d5c2
--- /dev/null
+++ b/gnu/usr.bin/man/man/ndir.h
@@ -0,0 +1,51 @@
+/*
+ <dir.h> -- definitions for 4.2BSD-compatible directory access
+
+ last edit: 09-Jul-1983 D A Gwyn
+*/
+
+#ifdef VMS
+#ifndef FAB$C_BID
+#include <fab.h>
+#endif
+#ifndef NAM$C_BID
+#include <nam.h>
+#endif
+#ifndef RMS$_SUC
+#include <rmsdef.h>
+#endif
+#include "dir.h"
+#endif /* VMS */
+
+#define DIRBLKSIZ 512 /* size of directory block */
+#ifdef VMS
+#define MAXNAMLEN (DIR$S_NAME + 7) /* 80 plus room for version #. */
+#define MAXFULLSPEC NAM$C_MAXRSS /* Maximum full spec */
+#else
+#define MAXNAMLEN 15 /* maximum filename length */
+#endif /* VMS */
+ /* NOTE: MAXNAMLEN must be one less than a multiple of 4 */
+
+struct direct /* data from readdir() */
+ {
+ long d_ino; /* inode number of entry */
+ unsigned short d_reclen; /* length of this record */
+ unsigned short d_namlen; /* length of string in d_name */
+ char d_name[MAXNAMLEN+1]; /* name of file */
+ };
+
+typedef struct
+ {
+ int dd_fd; /* file descriptor */
+ int dd_loc; /* offset in block */
+ int dd_size; /* amount of valid data */
+ char dd_buf[DIRBLKSIZ]; /* directory block */
+ } DIR; /* stream data from opendir() */
+
+extern DIR *opendir();
+extern struct direct *readdir();
+extern long telldir();
+extern void seekdir();
+extern void closedir();
+
+#define rewinddir( dirp ) seekdir( dirp, 0L )
diff --git a/gnu/usr.bin/man/man/strdup.c b/gnu/usr.bin/man/man/strdup.c
new file mode 100644
index 0000000..4e5af07
--- /dev/null
+++ b/gnu/usr.bin/man/man/strdup.c
@@ -0,0 +1,39 @@
+/* strdup.c -- return a newly allocated copy of a string
+ Copyright (C) 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef STDC_HEADERS
+#include <string.h>
+#include <stdlib.h>
+#else
+char *malloc ();
+char *strcpy ();
+#endif
+
+/* Return a newly allocated copy of STR,
+ or 0 if out of memory. */
+
+char *
+strdup (str)
+ char *str;
+{
+ char *newstr;
+
+ newstr = (char *) malloc (strlen (str) + 1);
+ if (newstr)
+ strcpy (newstr, str);
+ return newstr;
+}
diff --git a/gnu/usr.bin/man/man/version.h b/gnu/usr.bin/man/man/version.h
new file mode 100644
index 0000000..4d9eb63
--- /dev/null
+++ b/gnu/usr.bin/man/man/version.h
@@ -0,0 +1,17 @@
+/*
+ * version.h
+ *
+ * Copyright (c) 1990, 1991, John W. Eaton.
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the file COPYING that comes with the man
+ * distribution.
+ *
+ * John W. Eaton
+ * jwe@che.utexas.edu
+ * Department of Chemical Engineering
+ * The University of Texas at Austin
+ * Austin, Texas 78712
+ */
+
+static char version[] = "1.1";
diff --git a/gnu/usr.bin/man/manpath/Makefile b/gnu/usr.bin/man/manpath/Makefile
new file mode 100644
index 0000000..6b6bfbb
--- /dev/null
+++ b/gnu/usr.bin/man/manpath/Makefile
@@ -0,0 +1,30 @@
+PROG= manpath
+SRCS= manpath.c
+
+.if exists(${.CURDIR}/../lib/obj)
+LDADD= -L${.CURDIR}/../lib/obj -lman
+.else
+LDADD= -L${.CURDIR}/../lib/ -lman
+.endif
+
+.if exists(${.CURDIR}/obj)
+MAN1=${.CURDIR}/obj/manpath.1
+.else
+MAN1=${.CURDIR}/manpath.1
+.endif
+
+DPADD+= ${MAN1}
+CFLAGS+= -DMAIN -DSTDC_HEADERS -DPOSIX -DHAS_TROFF -DDO_UNCOMPRESS
+CFLAGS+= -DALT_SYSTEMS -I${.CURDIR}/../lib -I${.CURDIR}/../lib/obj
+CLEANFILES+= ${MAN1}
+
+${MAN1}: ${.CURDIR}/manpath.man
+ sed -e 's,%libdir%,${libdir},' -e 's,%bindir%,${bindir},' \
+ -e 's,%pager%,${pager},' -e 's,%troff%,${troff},' \
+ -e 's,%manpath_config_file%,${manpath_config_file},' \
+ ${.CURDIR}/manpath.man > ${MAN1}
+
+afterinstall:
+ install -c -o bin -g bin -m 555 ${.CURDIR}/manpath.config ${DESTDIR}${manpath_config_file}.sample
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/man/manpath/manpath.c b/gnu/usr.bin/man/manpath/manpath.c
new file mode 100644
index 0000000..ccf7a55
--- /dev/null
+++ b/gnu/usr.bin/man/manpath/manpath.c
@@ -0,0 +1,520 @@
+/*
+ * manpath.c
+ *
+ * Copyright (c) 1990, 1991, John W. Eaton.
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the file COPYING that comes with the man
+ * distribution.
+ *
+ * John W. Eaton
+ * jwe@che.utexas.edu
+ * Department of Chemical Engineering
+ * The University of Texas at Austin
+ * Austin, Texas 78712
+ */
+
+#define MANPATH_MAIN
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "config.h"
+#include "manpath.h"
+#include "gripes.h"
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+extern int fprintf ();
+extern int strcmp ();
+extern int strncmp ();
+extern char *memcpy ();
+extern char *getenv();
+extern char *malloc();
+extern void free ();
+extern int exit ();
+#endif
+
+extern char *strdup ();
+extern int is_directory ();
+
+#ifndef MAIN
+extern int debug;
+#endif
+
+#ifdef MAIN
+
+#ifndef STDC_HEADERS
+extern char *strcpy ();
+extern int fflush ();
+#endif
+
+char *prognam;
+int debug;
+
+/*
+ * Examine user's PATH and print a reasonable MANPATH.
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int quiet;
+ char *mp;
+ extern int getopt ();
+ extern char *mkprogname ();
+ void usage ();
+ char *manpath ();
+
+ quiet = 1;
+
+ prognam = mkprogname (argv[0]);
+
+ while ((c = getopt (argc, argv, "dhq?")) != EOF)
+ {
+ switch (c)
+ {
+ case 'd':
+ debug++;
+ break;
+ case 'q':
+ quiet = 0;
+ break;
+ case '?':
+ case 'h':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ mp = manpath (quiet);
+
+ fprintf (stdout, "%s\n", mp);
+ fflush (stdout);
+
+ return 0;
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "usage: %s [-q]\n", prognam);
+ exit (1);
+}
+#endif /* MAIN */
+
+/*
+ * If the environment variable MANPATH is set, return it.
+ * If the environment variable PATH is set and has a nonzero length,
+ * try to determine the corresponding manpath, otherwise, return the
+ * default manpath.
+ *
+ * The manpath.config file is used to map system wide /bin directories
+ * to top level man page directories.
+ *
+ * For directories which are in the user's path but not in the
+ * manpath.config file, see if there is a subdirectory `man' or `MAN'.
+ * If so, add that directory to the path. Example: user has
+ * $HOME/bin in his path and the directory $HOME/bin/man exists -- the
+ * directory $HOME/bin/man will be added to the manpath.
+ */
+char *
+manpath (perrs)
+ register int perrs;
+{
+ register int len;
+ register char *manpathlist;
+ register char *path;
+ int get_dirlist ();
+ char *def_path ();
+ char *get_manpath ();
+
+ if (get_dirlist ())
+ gripe_reading_mp_config ();
+
+ if ((manpathlist = getenv ("MANPATH")) != NULL)
+ /*
+ * This must be it.
+ */
+ {
+ if (perrs)
+ fprintf (stderr, "(Warning: MANPATH environment variable set)\n");
+ return strdup (manpathlist);
+ }
+ else if ((path = getenv ("PATH")) == NULL)
+ /*
+ * Things aren't going to work well, but hey...
+ */
+ {
+ if (perrs)
+ fprintf (stderr, "Warning: path not set\n");
+ return def_path (perrs);
+ }
+ else
+ {
+ if ((len = strlen (path)) == 0)
+ /*
+ * Things aren't going to work well here either...
+ */
+ {
+ if (perrs)
+ fprintf (stderr, "Warning: path set but has zero length\n");
+ return def_path (perrs);
+ }
+ return get_manpath (perrs, path);
+ }
+}
+
+/*
+ * Get the list of bin directories and the corresponding man
+ * directories from the manpath.config file.
+ *
+ * This is ugly.
+ */
+int
+get_dirlist ()
+{
+ int i;
+ char *bp;
+ char *p;
+ char buf[BUFSIZ];
+ DIRLIST *dlp = list;
+ FILE *config;
+
+ if ((config = fopen (config_file, "r")) == NULL)
+ gripe_getting_mp_config (config_file);
+
+ while ((bp = fgets (buf, BUFSIZ, config)) != NULL)
+ {
+ while (*bp && (*bp == ' ' || *bp == '\t'))
+ bp++;
+
+ if (*bp == '#' || *bp == '\n')
+ continue;
+
+ if (!strncmp ("MANBIN", bp, 6))
+ continue;
+
+ if (!strncmp ("MANDATORY_MANPATH", bp, 17))
+ {
+ if ((p = strchr (bp, ' ')) == NULL)
+ if ((p = strchr (bp, '\t')) == NULL)
+ return -1;
+
+ bp = p;
+
+ dlp->mandatory = 1;
+
+ while (*bp && *bp != '\n' && (*bp == ' ' || *bp == '\t'))
+ bp++;
+
+ i = 0;
+ while (*bp && *bp != '\n' && *bp != ' ' && *bp != '\t')
+ dlp->mandir[i++] = *bp++;
+ dlp->mandir[i] = '\0';
+
+ if (debug)
+ fprintf (stderr, "found mandatory man directory %s\n",
+ dlp->mandir);
+ }
+ else if (!strncmp ("MANPATH_MAP", bp, 11))
+ {
+ if ((p = strchr (bp, ' ')) == NULL)
+ if ((p = strchr (bp, '\t')) == NULL)
+ return -1;
+
+ bp = p;
+
+ dlp->mandatory = 0;
+
+ while (*bp && *bp != '\n' && (*bp == ' ' || *bp == '\t'))
+ bp++;
+
+ i = 0;
+ while (*bp && *bp != '\n' && *bp != ' ' && *bp != '\t')
+ dlp->bin[i++] = *bp++;
+ dlp->bin[i] = '\0';
+
+ while (*bp && *bp != '\n' && (*bp == ' ' || *bp == '\t'))
+ bp++;
+
+ i = 0;
+ while (*bp && *bp != '\n' && *bp != ' ' && *bp != '\t')
+ dlp->mandir[i++] = *bp++;
+ dlp->mandir[i] = '\0';
+
+ if (debug)
+ fprintf (stderr, "found manpath map %s --> %s\n",
+ dlp->bin, dlp->mandir);
+ }
+ else
+ {
+ gripe_reading_mp_config ();
+ }
+ dlp++;
+ }
+
+ dlp->bin[0] = '\0';
+ dlp->mandir[0] = '\0';
+ dlp->mandatory = 0;
+
+ return 0;
+}
+
+/*
+ * Construct the default manpath. This picks up mandatory manpaths
+ * only.
+ */
+char *
+def_path (perrs)
+ int perrs;
+{
+ register int len;
+ register char *manpathlist, *p;
+ register DIRLIST *dlp;
+
+ len = 0;
+ dlp = list;
+ while (dlp->mandatory != 0)
+ {
+ len += strlen (dlp->mandir) + 1;
+ dlp++;
+ }
+
+ manpathlist = (char *) malloc (len);
+ if (manpathlist == NULL)
+ gripe_alloc (len, "manpathlist");
+
+ *manpathlist = '\0';
+
+ dlp = list;
+ p = manpathlist;
+ while (dlp->mandatory != 0)
+ {
+ int status;
+ char *path = dlp->mandir;
+
+ status = is_directory(path);
+
+ if (status < 0 && perrs)
+ {
+ fprintf (stderr, "Warning: couldn't stat file %s!\n", path);
+ }
+ else if (status == 0 && perrs)
+ {
+ fprintf (stderr, "Warning: standard directory %s doesn't exist!\n",
+ path);
+ }
+ else if (status == 1)
+ {
+ len = strlen (path);
+ memcpy (p, path, len);
+ p += len;
+ *p++ = ':';
+ dlp++;
+ }
+ }
+
+ p[-1] = '\0';
+
+ return manpathlist;
+}
+
+/*
+ * For each directory in the user's path, see if it is one of the
+ * directories listed in the manpath.config file. If so, and it is
+ * not already in the manpath, add it. If the directory is not listed
+ * in the manpath.config file, see if there is a subdirectory `man' or
+ * `MAN'. If so, and it is not already in the manpath, add it.
+ * Example: user has $HOME/bin in his path and the directory
+ * $HOME/bin/man exists -- the directory $HOME/bin/man will be added
+ * to the manpath.
+ */
+char *
+get_manpath (perrs, path)
+ register int perrs;
+ register char *path;
+{
+ register int len;
+ register char *tmppath;
+ register char *t;
+ register char *p;
+ register char **lp;
+ register char *end;
+ register char *manpathlist;
+ register DIRLIST *dlp;
+ void add_dir_to_list ();
+ char *has_subdirs ();
+
+ tmppath = strdup (path);
+
+ for (p = tmppath; ; p = end+1)
+ {
+ if (end = strchr(p, ':'))
+ *end = '\0';
+
+ if (debug)
+ fprintf (stderr, "\npath directory %s ", p);
+
+ /*
+ * The directory we're working on is in the config file.
+ * If we haven't added it to the list yet, do.
+ */
+ for (dlp = list; dlp->mandir[0] != '\0'; dlp++)
+ if (dlp->bin[0] != '\0' && !strcmp (p, dlp->bin))
+ {
+ if (debug)
+ fprintf (stderr, "is in the config file\n");
+
+ add_dir_to_list (tmplist, dlp->mandir, perrs);
+ goto found;
+ }
+
+ /*
+ * The directory we're working on isn't in the config file. See
+ * if it has man or MAN subdirectories. If so, and it hasn't
+ * been added to the list, do.
+ */
+ if (debug)
+ fprintf (stderr, "is not in the config file\n");
+
+ t = has_subdirs (p);
+ if (t != NULL)
+ {
+ if (debug)
+ fprintf (stderr, "but it does have a man or MAN subdirectory\n");
+
+ add_dir_to_list (tmplist, t, perrs);
+ free (t);
+ }
+ else
+ {
+ if (debug)
+ fprintf (stderr, "and doesn't have man or MAN subdirectories\n");
+ }
+
+ found:
+
+ if (!end)
+ break;
+ }
+
+ if (debug)
+ fprintf (stderr, "\nadding mandatory man directories\n\n");
+
+ dlp = list;
+ while (dlp->mandatory != 0)
+ {
+ add_dir_to_list (tmplist, dlp->mandir, perrs);
+ dlp++;
+ }
+
+ len = 0;
+ lp = tmplist;
+ while (*lp != NULL)
+ {
+ len += strlen (*lp) + 1;
+ lp++;
+ }
+
+ manpathlist = (char *) malloc (len);
+ if (manpathlist == NULL)
+ gripe_alloc (len, "manpathlist");
+
+ *manpathlist = '\0';
+
+ lp = tmplist;
+ p = manpathlist;
+ while (*lp != NULL)
+ {
+ len = strlen (*lp);
+ memcpy (p, *lp, len);
+ p += len;
+ *p++ = ':';
+ lp++;
+ }
+
+ p[-1] = '\0';
+
+ return manpathlist;
+}
+
+/*
+ * Add a directory to the manpath list if it isn't already there.
+ */
+void
+add_dir_to_list (lp, dir, perrs)
+ char **lp;
+ char *dir;
+ int perrs;
+{
+ extern char *strdup ();
+ int status;
+
+ while (*lp != NULL)
+ {
+ if (!strcmp (*lp, dir))
+ {
+ if (debug)
+ fprintf (stderr, "%s is already in the manpath\n", dir);
+ return;
+ }
+ lp++;
+ }
+ /*
+ * Not found -- add it.
+ */
+ status = is_directory(dir);
+
+ if (status < 0 && perrs)
+ {
+ fprintf (stderr, "Warning: couldn't stat file %s!\n", dir);
+ }
+ else if (status == 0 && perrs)
+ {
+ fprintf (stderr, "Warning: %s isn't a directory!\n", dir);
+ }
+ else if (status == 1)
+ {
+ if (debug)
+ fprintf (stderr, "adding %s to manpath\n", dir);
+
+ *lp = strdup (dir);
+ }
+}
+
+/*
+ * Check to see if the current directory has man or MAN
+ * subdirectories.
+ */
+char *
+has_subdirs (p)
+ register char *p;
+{
+ int len;
+ register char *t;
+
+ len = strlen (p);
+
+ t = (char *) malloc ((unsigned) len + 5);
+ if (t == NULL)
+ gripe_alloc (len+5, "p\n");
+
+ memcpy (t, p, len);
+ strcpy (t + len, "/man");
+
+ if (is_directory (t) == 1)
+ return t;
+
+ strcpy (t + len, "/MAN");
+
+ if (is_directory (t) == 1)
+ return t;
+
+ return NULL;
+}
diff --git a/gnu/usr.bin/man/manpath/manpath.config b/gnu/usr.bin/man/manpath/manpath.config
new file mode 100644
index 0000000..677b1ad
--- /dev/null
+++ b/gnu/usr.bin/man/manpath/manpath.config
@@ -0,0 +1,32 @@
+# manpath.config
+#
+# This file is read by manpath to configure the mandatory manpath, to
+# map each path element to a manpath element and to determine where the
+# "man" binary lives. The format is:
+#
+# MANBIN pathname
+# MANDATORY_MANPATH manpath_element
+# MANPATH_MAP path_element manpath_element
+#
+# MANBIN is optional
+#
+#MANBIN /usr/local/bin/man
+#
+# every automatically generated MANPATH includes these fields
+#
+MANDATORY_MANPATH /usr/share/man
+MANDATORY_MANPATH /usr/local/man
+MANDATORY_MANPATH /usr/X386/man
+MANDATORY_MANPATH /usr/X11R6/man
+MANDATORY_MANPATH /usr/gnu/man
+#
+# set up PATH to MANPATH mapping
+#
+MANPATH_MAP /bin /usr/share/man
+MANPATH_MAP /usr/bin /usr/share/man
+MANPATH_MAP /usr/ucb /usr/share/man
+MANPATH_MAP /usr/local/mh /usr/local/mh/man
+MANPATH_MAP /usr/local/bin /usr/local/man
+MANPATH_MAP /usr/gnu /usr/gnu/man
+MANPATH_MAP /usr/X386/bin /usr/X386/man
+MANPATH_MAP /usr/X11R6/bin /usr/X11R6/man
diff --git a/gnu/usr.bin/man/manpath/manpath.h b/gnu/usr.bin/man/manpath/manpath.h
new file mode 100644
index 0000000..a61761f
--- /dev/null
+++ b/gnu/usr.bin/man/manpath/manpath.h
@@ -0,0 +1,26 @@
+/*
+ * manpath.h
+ *
+ * Copyright (c) 1990, 1991, John W. Eaton.
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the file COPYING that comes with the man
+ * distribution.
+ *
+ * John W. Eaton
+ * jwe@che.utexas.edu
+ * Department of Chemical Engineering
+ * The University of Texas at Austin
+ * Austin, Texas 78712
+ */
+
+typedef struct
+{
+ char mandir[MAXPATHLEN];
+ char bin[MAXPATHLEN];
+ int mandatory;
+} DIRLIST;
+
+DIRLIST list[MAXDIRS];
+
+char *tmplist[MAXDIRS];
diff --git a/gnu/usr.bin/man/manpath/manpath.man b/gnu/usr.bin/man/manpath/manpath.man
new file mode 100644
index 0000000..9212324
--- /dev/null
+++ b/gnu/usr.bin/man/manpath/manpath.man
@@ -0,0 +1,56 @@
+.\" Man page for manpath
+.\"
+.\" Copyright (c) 1990, 1991, John W. Eaton.
+.\"
+.\" You may distribute under the terms of the GNU General Public
+.\" License as specified in the README file that comes with the man 1.0
+.\" distribution.
+.\"
+.\" John W. Eaton
+.\" jwe@che.utexas.edu
+.\" Department of Chemical Engineering
+.\" The University of Texas at Austin
+.\" Austin, Texas 78712
+.\"
+.TH manpath 1 "Jan 5, 1991"
+.LO 1
+.SH NAME
+manpath \- determine user's search path for man pages
+.SH SYNOPSIS
+manpath [\-q]
+.SH DESCRIPTION
+manpath tries to determine the user's manpath from a set of system
+defaults and the user's
+.B PATH ,
+echoing the result to the standard output. Warnings and errors are
+written to the standard error.
+If a directory in the user's path is not listed in the manpath.config
+file, manpath looks for the subdirectories man or MAN. If they exist,
+they are added to the search path.
+.PP
+manpath is used by
+.B man
+to determine the search path, so user's normally don't need to set the
+.B MANPATH
+environment variable directly.
+.SH OPTIONS
+.TP
+.B \-\^q
+Operate quietly. Only echo the final manpath.
+.SH ENVIRONMENT
+.TP \w'MANPATH\ \ 'u
+.B MANPATH
+If
+.B MANPATH
+is set,
+.B manpath
+echoes its value on the standard output and issues a warning on the
+standard error.
+.SH FILES
+.TP \w'%manpath_config_file%'u+2n
+.BI %manpath_config_file%
+System configuration file.
+.SH "SEE ALSO"
+apropos(1), whatis(1), man(1).
+.SH BUGS
+None known.
diff --git a/gnu/usr.bin/man/whatis/Makefile b/gnu/usr.bin/man/whatis/Makefile
new file mode 100644
index 0000000..4a825c8
--- /dev/null
+++ b/gnu/usr.bin/man/whatis/Makefile
@@ -0,0 +1,41 @@
+.if exists(${.CURDIR}/obj)
+MAN1= ${.CURDIR}/obj/whatis.1
+TARG= ${.CURDIR}/obj/whatis
+.else
+MAN1= ${.CURDIR}/whatis.1
+TARG= ${.CURDIR}/whatis
+.endif
+
+MANDEPEND= ${MAN1}
+
+CLEANFILES+= ${TARG} ${MAN1}
+
+all: ${TARG} ${MAN1}
+
+depend rcsfreeze tags all:
+ @echo -n
+
+${TARG}: ${.CURDIR}/whatis.sh
+ sed -e 's,%libdir%,${libdir},' -e 's,%bindir%,${bindir},' \
+ -e 's,%pager%,${pager},' \
+ ${.CURDIR}/whatis.sh > ${.TARGET}
+
+${MAN1}: ${.CURDIR}/whatis.man
+ sed -e 's,%libdir%,${libdir},' -e 's,%bindir%,${bindir},' \
+ -e 's,%pager%,${pager},' -e 's,%troff%,${troff},' \
+ -e 's,%manpath_config_file%,${manpath_config_file},' \
+ ${.CURDIR}/whatis.man > ${.TARGET}
+
+install: ${TARG} maninstall
+ install -c -o bin -g bin -m 555 ${TARG} ${DESTDIR}/usr/bin
+
+.include "../Makefile.inc"
+.if make(maninstall) || make(install)
+.if !defined(NOMAN)
+.include <bsd.man.mk>
+.else
+maninstall:
+.endif
+.endif
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/man/whatis/whatis b/gnu/usr.bin/man/whatis/whatis
new file mode 100644
index 0000000..e8cae0a
--- /dev/null
+++ b/gnu/usr.bin/man/whatis/whatis
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# whatis -- search the whatis database for keywords. Like apropos,
+# but match only commands (as whole words).
+#
+# Copyright (c) 1990, 1991, John W. Eaton.
+#
+# You may distribute under the terms of the GNU General Public
+# License as specified in the README file that comes with the man
+# distribution.
+#
+# John W. Eaton
+# jwe@che.utexas.edu
+# Department of Chemical Engineering
+# The University of Texas at Austin
+# Austin, Texas 78712
+
+PATH=/usr/local/bin:/bin:/usr/ucb:/usr/bin
+
+libdir=/etc
+
+if [ $# = 0 ]
+then
+ echo "usage: `basename $0` name ..."
+ exit 1
+fi
+
+manpath=`/usr/bin/manpath -q | tr : '\040'`
+
+if [ "$manpath" = "" ]
+then
+ echo "whatis: manpath is null"
+ exit 1
+fi
+
+if [ "$PAGER" = "" ]
+then
+ PAGER="/usr/gnu/bin/less -sC"
+fi
+
+while [ $1 ]
+do
+ found=0
+ for d in $manpath /usr/lib
+ do
+ if [ -f $d/whatis ]
+ then
+ grep -iw "^$1" $d/whatis
+ status=$?
+ if [ "$status" = "0" ]
+ then
+ found=1
+ export found;
+ fi
+ fi
+ done
+
+ if [ "$found" = "0" ]
+ then
+ echo "$1: nothing appropriate"
+ fi
+
+ shift
+done | $PAGER
+
+exit
diff --git a/gnu/usr.bin/man/whatis/whatis.1 b/gnu/usr.bin/man/whatis/whatis.1
new file mode 100644
index 0000000..9e5528d
--- /dev/null
+++ b/gnu/usr.bin/man/whatis/whatis.1
@@ -0,0 +1,27 @@
+.\" Man page for whatis
+.\"
+.\" Copyright (c) 1990, 1991, John W. Eaton.
+.\"
+.\" You may distribute under the terms of the GNU General Public
+.\" License as specified in the README file that comes with the man 1.0
+.\" distribution.
+.\"
+.\" John W. Eaton
+.\" jwe@che.utexas.edu
+.\" Department of Chemical Engineering
+.\" The University of Texas at Austin
+.\" Austin, Texas 78712
+.\"
+.TH whatis 1 "Jan 5, 1991"
+.LO 1
+.SH NAME
+whatis \- search the whatis database for complete words.
+.SH SYNOPSIS
+.BI whatis
+keyword ...
+.SH DESCRIPTION
+whatis searches a set of database files containing short descriptions
+of system commands for keywords and displays the result on the
+standard output. Only complete word matches are displayed.
+.SH "SEE ALSO"
+apropos(1), man(1).
diff --git a/gnu/usr.bin/man/whatis/whatis.man b/gnu/usr.bin/man/whatis/whatis.man
new file mode 100644
index 0000000..9e5528d
--- /dev/null
+++ b/gnu/usr.bin/man/whatis/whatis.man
@@ -0,0 +1,27 @@
+.\" Man page for whatis
+.\"
+.\" Copyright (c) 1990, 1991, John W. Eaton.
+.\"
+.\" You may distribute under the terms of the GNU General Public
+.\" License as specified in the README file that comes with the man 1.0
+.\" distribution.
+.\"
+.\" John W. Eaton
+.\" jwe@che.utexas.edu
+.\" Department of Chemical Engineering
+.\" The University of Texas at Austin
+.\" Austin, Texas 78712
+.\"
+.TH whatis 1 "Jan 5, 1991"
+.LO 1
+.SH NAME
+whatis \- search the whatis database for complete words.
+.SH SYNOPSIS
+.BI whatis
+keyword ...
+.SH DESCRIPTION
+whatis searches a set of database files containing short descriptions
+of system commands for keywords and displays the result on the
+standard output. Only complete word matches are displayed.
+.SH "SEE ALSO"
+apropos(1), man(1).
diff --git a/gnu/usr.bin/man/whatis/whatis.sh b/gnu/usr.bin/man/whatis/whatis.sh
new file mode 100644
index 0000000..34abaaa
--- /dev/null
+++ b/gnu/usr.bin/man/whatis/whatis.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# whatis -- search the whatis database for keywords. Like apropos,
+# but match only commands (as whole words).
+#
+# Copyright (c) 1990, 1991, John W. Eaton.
+#
+# You may distribute under the terms of the GNU General Public
+# License as specified in the README file that comes with the man
+# distribution.
+#
+# John W. Eaton
+# jwe@che.utexas.edu
+# Department of Chemical Engineering
+# The University of Texas at Austin
+# Austin, Texas 78712
+
+PATH=/usr/local/bin:/bin:/usr/ucb:/usr/bin
+
+libdir=%libdir%
+
+if [ $# = 0 ]
+then
+ echo "usage: `basename $0` name ..."
+ exit 1
+fi
+
+manpath=`%bindir%/manpath -q | tr : '\040'`
+
+if [ "$manpath" = "" ]
+then
+ echo "whatis: manpath is null"
+ exit 1
+fi
+
+if [ "$PAGER" = "" ]
+then
+ PAGER="%pager%"
+fi
+
+while [ $1 ]
+do
+ found=0
+ for d in $manpath /usr/lib
+ do
+ if [ -f $d/whatis ]
+ then
+ grep -iw "^$1" $d/whatis
+ status=$?
+ if [ "$status" = "0" ]
+ then
+ found=1
+ export found;
+ fi
+ fi
+ done
+
+ if [ "$found" = "0" ]
+ then
+ echo "$1: nothing appropriate"
+ fi
+
+ shift
+done | $PAGER
+
+exit
diff --git a/gnu/usr.bin/patch/EXTERN.h b/gnu/usr.bin/patch/EXTERN.h
new file mode 100644
index 0000000..bdc1ef9
--- /dev/null
+++ b/gnu/usr.bin/patch/EXTERN.h
@@ -0,0 +1,21 @@
+/* $Header: EXTERN.h,v 2.0 86/09/17 15:35:37 lwall Exp $
+ *
+ * $Log: EXTERN.h,v $
+ * Revision 2.0 86/09/17 15:35:37 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+#ifdef EXT
+#undef EXT
+#endif
+#define EXT extern
+
+#ifdef INIT
+#undef INIT
+#endif
+#define INIT(x)
+
+#ifdef DOINIT
+#undef DOINIT
+#endif
diff --git a/gnu/usr.bin/patch/INTERN.h b/gnu/usr.bin/patch/INTERN.h
new file mode 100644
index 0000000..38574ef
--- /dev/null
+++ b/gnu/usr.bin/patch/INTERN.h
@@ -0,0 +1,19 @@
+/* $Header: INTERN.h,v 2.0 86/09/17 15:35:58 lwall Exp $
+ *
+ * $Log: INTERN.h,v $
+ * Revision 2.0 86/09/17 15:35:58 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+#ifdef EXT
+#undef EXT
+#endif
+#define EXT
+
+#ifdef INIT
+#undef INIT
+#endif
+#define INIT(x) = x
+
+#define DOINIT
diff --git a/gnu/usr.bin/patch/Makefile b/gnu/usr.bin/patch/Makefile
new file mode 100644
index 0000000..d614e89
--- /dev/null
+++ b/gnu/usr.bin/patch/Makefile
@@ -0,0 +1,6 @@
+PROG= patch
+SRCS = backupfile.c getopt.c getopt1.c inp.c patch.c pch.c util.c \
+ version.c
+CFLAGS += -DHAVE_CONFIG_H
+MAN= patch.1
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/patch/backupfile.c b/gnu/usr.bin/patch/backupfile.c
new file mode 100644
index 0000000..d7eb5874
--- /dev/null
+++ b/gnu/usr.bin/patch/backupfile.c
@@ -0,0 +1,402 @@
+/* backupfile.c -- make Emacs style backup file names
+ Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by David MacKenzie <djm@gnu.ai.mit.edu>.
+ Some algorithms adapted from GNU Emacs. */
+
+#include "config.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include "backupfile.h"
+#ifdef STDC_HEADERS
+#include <string.h>
+#include <stdlib.h>
+#else
+char *malloc ();
+#endif
+
+#if defined (HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#if defined(DIRENT) || defined(_POSIX_VERSION)
+#include <dirent.h>
+#define NLENGTH(direct) (strlen((direct)->d_name))
+#else /* not (DIRENT or _POSIX_VERSION) */
+#define dirent direct
+#define NLENGTH(direct) ((direct)->d_namlen)
+#ifdef SYSNDIR
+#include <sys/ndir.h>
+#endif /* SYSNDIR */
+#ifdef SYSDIR
+#include <sys/dir.h>
+#endif /* SYSDIR */
+#ifdef NDIR
+#include <ndir.h>
+#endif /* NDIR */
+#endif /* DIRENT or _POSIX_VERSION */
+
+#ifndef isascii
+#define ISDIGIT(c) (isdigit ((unsigned char) (c)))
+#else
+#define ISDIGIT(c) (isascii (c) && isdigit (c))
+#endif
+
+#if defined (_POSIX_VERSION)
+/* POSIX does not require that the d_ino field be present, and some
+ systems do not provide it. */
+#define REAL_DIR_ENTRY(dp) 1
+#else
+#define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0)
+#endif
+
+/* Which type of backup file names are generated. */
+enum backup_type backup_type = none;
+
+/* The extension added to file names to produce a simple (as opposed
+ to numbered) backup file name. */
+char *simple_backup_suffix = "~";
+
+char *basename ();
+char *dirname ();
+static char *concat ();
+char *find_backup_file_name ();
+static char *make_version_name ();
+static int max_backup_version ();
+static int version_number ();
+
+/* Return NAME with any leading path stripped off. */
+
+char *
+basename (name)
+ char *name;
+{
+ char *r = name, *p = name;
+
+ while (*p)
+ if (*p++ == '/')
+ r = p;
+ return r;
+}
+
+#ifndef NODIR
+/* Return the name of the new backup file for file FILE,
+ allocated with malloc. Return 0 if out of memory.
+ FILE must not end with a '/' unless it is the root directory.
+ Do not call this function if backup_type == none. */
+
+char *
+find_backup_file_name (file)
+ char *file;
+{
+ char *dir;
+ char *base_versions;
+ int highest_backup;
+
+ if (backup_type == simple)
+ {
+ char *s = malloc (strlen (file) + strlen (simple_backup_suffix) + 1);
+ strcpy (s, file);
+ addext (s, simple_backup_suffix, '~');
+ return s;
+ }
+ base_versions = concat (basename (file), ".~");
+ if (base_versions == 0)
+ return 0;
+ dir = dirname (file);
+ if (dir == 0)
+ {
+ free (base_versions);
+ return 0;
+ }
+ highest_backup = max_backup_version (base_versions, dir);
+ free (base_versions);
+ free (dir);
+ if (backup_type == numbered_existing && highest_backup == 0)
+ return concat (file, simple_backup_suffix);
+ return make_version_name (file, highest_backup + 1);
+}
+
+/* Return the number of the highest-numbered backup file for file
+ FILE in directory DIR. If there are no numbered backups
+ of FILE in DIR, or an error occurs reading DIR, return 0.
+ FILE should already have ".~" appended to it. */
+
+static int
+max_backup_version (file, dir)
+ char *file, *dir;
+{
+ DIR *dirp;
+ struct dirent *dp;
+ int highest_version;
+ int this_version;
+ int file_name_length;
+
+ dirp = opendir (dir);
+ if (!dirp)
+ return 0;
+
+ highest_version = 0;
+ file_name_length = strlen (file);
+
+ while ((dp = readdir (dirp)) != 0)
+ {
+ if (!REAL_DIR_ENTRY (dp) || NLENGTH (dp) <= file_name_length)
+ continue;
+
+ this_version = version_number (file, dp->d_name, file_name_length);
+ if (this_version > highest_version)
+ highest_version = this_version;
+ }
+ closedir (dirp);
+ return highest_version;
+}
+
+/* Return a string, allocated with malloc, containing
+ "FILE.~VERSION~". Return 0 if out of memory. */
+
+static char *
+make_version_name (file, version)
+ char *file;
+ int version;
+{
+ char *backup_name;
+
+ backup_name = malloc (strlen (file) + 16);
+ if (backup_name == 0)
+ return 0;
+ sprintf (backup_name, "%s.~%d~", file, version);
+ return backup_name;
+}
+
+/* If BACKUP is a numbered backup of BASE, return its version number;
+ otherwise return 0. BASE_LENGTH is the length of BASE.
+ BASE should already have ".~" appended to it. */
+
+static int
+version_number (base, backup, base_length)
+ char *base;
+ char *backup;
+ int base_length;
+{
+ int version;
+ char *p;
+
+ version = 0;
+ if (!strncmp (base, backup, base_length) && ISDIGIT (backup[base_length]))
+ {
+ for (p = &backup[base_length]; ISDIGIT (*p); ++p)
+ version = version * 10 + *p - '0';
+ if (p[0] != '~' || p[1])
+ version = 0;
+ }
+ return version;
+}
+
+/* Return the newly-allocated concatenation of STR1 and STR2.
+ If out of memory, return 0. */
+
+static char *
+concat (str1, str2)
+ char *str1, *str2;
+{
+ char *newstr;
+ char str1_length = strlen (str1);
+
+ newstr = malloc (str1_length + strlen (str2) + 1);
+ if (newstr == 0)
+ return 0;
+ strcpy (newstr, str1);
+ strcpy (newstr + str1_length, str2);
+ return newstr;
+}
+
+/* Return the leading directories part of PATH,
+ allocated with malloc. If out of memory, return 0.
+ Assumes that trailing slashes have already been
+ removed. */
+
+char *
+dirname (path)
+ char *path;
+{
+ char *newpath;
+ char *slash;
+ int length; /* Length of result, not including NUL. */
+
+ slash = basename (path);
+ if (slash == path)
+ {
+ /* File is in the current directory. */
+ path = ".";
+ length = 1;
+ }
+ else
+ {
+ /* Remove any trailing slashes from result. */
+ while (*--slash == '/' && slash > path)
+ ;
+
+ length = slash - path + 1;
+ }
+ newpath = malloc (length + 1);
+ if (newpath == 0)
+ return 0;
+ strncpy (newpath, path, length);
+ newpath[length] = 0;
+ return newpath;
+}
+
+/* If ARG is an unambiguous match for an element of the
+ null-terminated array OPTLIST, return the index in OPTLIST
+ of the matched element, else -1 if it does not match any element
+ or -2 if it is ambiguous (is a prefix of more than one element). */
+
+int
+argmatch (arg, optlist)
+ char *arg;
+ char **optlist;
+{
+ int i; /* Temporary index in OPTLIST. */
+ int arglen; /* Length of ARG. */
+ int matchind = -1; /* Index of first nonexact match. */
+ int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */
+
+ arglen = strlen (arg);
+
+ /* Test all elements for either exact match or abbreviated matches. */
+ for (i = 0; optlist[i]; i++)
+ {
+ if (!strncmp (optlist[i], arg, arglen))
+ {
+ if (strlen (optlist[i]) == arglen)
+ /* Exact match found. */
+ return i;
+ else if (matchind == -1)
+ /* First nonexact match found. */
+ matchind = i;
+ else
+ /* Second nonexact match found. */
+ ambiguous = 1;
+ }
+ }
+ if (ambiguous)
+ return -2;
+ else
+ return matchind;
+}
+
+/* Error reporting for argmatch.
+ KIND is a description of the type of entity that was being matched.
+ VALUE is the invalid value that was given.
+ PROBLEM is the return value from argmatch. */
+
+void
+invalid_arg (kind, value, problem)
+ char *kind;
+ char *value;
+ int problem;
+{
+ fprintf (stderr, "patch: ");
+ if (problem == -1)
+ fprintf (stderr, "invalid");
+ else /* Assume -2. */
+ fprintf (stderr, "ambiguous");
+ fprintf (stderr, " %s `%s'\n", kind, value);
+}
+
+static char *backup_args[] =
+{
+ "never", "simple", "nil", "existing", "t", "numbered", 0
+};
+
+static enum backup_type backup_types[] =
+{
+ simple, simple, numbered_existing, numbered_existing, numbered, numbered
+};
+
+/* Return the type of backup indicated by VERSION.
+ Unique abbreviations are accepted. */
+
+enum backup_type
+get_version (version)
+ char *version;
+{
+ int i;
+
+ if (version == 0 || *version == 0)
+ return numbered_existing;
+ i = argmatch (version, backup_args);
+ if (i >= 0)
+ return backup_types[i];
+ invalid_arg ("version control type", version, i);
+ exit (1);
+}
+#endif /* NODIR */
+
+/* Append to FILENAME the extension EXT, unless the result would be too long,
+ in which case just append the character E. */
+
+void
+addext (filename, ext, e)
+ char *filename, *ext;
+ int e;
+{
+ char *s = basename (filename);
+ int slen = strlen (s), extlen = strlen (ext);
+ long slen_max = -1;
+
+#if HAVE_PATHCONF && defined (_PC_NAME_MAX)
+#ifndef _POSIX_NAME_MAX
+#define _POSIX_NAME_MAX 14
+#endif
+ if (slen + extlen <= _POSIX_NAME_MAX)
+ /* The file name is so short there's no need to call pathconf. */
+ slen_max = _POSIX_NAME_MAX;
+ else if (s == filename)
+ slen_max = pathconf (".", _PC_NAME_MAX);
+ else
+ {
+ char c = *s;
+ *s = 0;
+ slen_max = pathconf (filename, _PC_NAME_MAX);
+ *s = c;
+ }
+#endif
+ if (slen_max == -1) {
+#ifdef HAVE_LONG_FILE_NAMES
+ slen_max = 255;
+#else
+ slen_max = 14;
+#endif
+ }
+ if (slen + extlen <= slen_max)
+ strcpy (s + slen, ext);
+ else
+ {
+ if (slen_max <= slen) {
+ /* Try to preserve difference between .h .c etc. */
+ if (slen == slen_max && s[slen - 2] == '.')
+ s[slen - 2] = s[slen - 1];
+
+ slen = slen_max - 1;
+ }
+ s[slen] = e;
+ s[slen + 1] = 0;
+ }
+}
diff --git a/gnu/usr.bin/patch/backupfile.h b/gnu/usr.bin/patch/backupfile.h
new file mode 100644
index 0000000..dfd1fc4
--- /dev/null
+++ b/gnu/usr.bin/patch/backupfile.h
@@ -0,0 +1,46 @@
+/* backupfile.h -- declarations for making Emacs style backup file names
+ Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* When to make backup files. */
+enum backup_type
+{
+ /* Never make backups. */
+ none,
+
+ /* Make simple backups of every file. */
+ simple,
+
+ /* Make numbered backups of files that already have numbered backups,
+ and simple backups of the others. */
+ numbered_existing,
+
+ /* Make numbered backups of every file. */
+ numbered
+};
+
+extern enum backup_type backup_type;
+extern char *simple_backup_suffix;
+
+#ifdef __STDC__
+char *find_backup_file_name (char *file);
+enum backup_type get_version (char *version);
+void addext (char *, char *, int);
+#else
+char *find_backup_file_name ();
+enum backup_type get_version ();
+void addext ();
+#endif
diff --git a/gnu/usr.bin/patch/common.h b/gnu/usr.bin/patch/common.h
new file mode 100644
index 0000000..9bae5bd
--- /dev/null
+++ b/gnu/usr.bin/patch/common.h
@@ -0,0 +1,193 @@
+/* $Header: /a/cvs/386BSD/src/gnu/patch/common.h,v 1.1.1.1 1993/06/19 14:21:52 paul Exp $
+ *
+ * $Log: common.h,v $
+ * Revision 1.1.1.1 1993/06/19 14:21:52 paul
+ * b-maked patch-2.10
+ *
+ * Revision 2.0.1.2 88/06/22 20:44:53 lwall
+ * patch12: sprintf was declared wrong
+ *
+ * Revision 2.0.1.1 88/06/03 15:01:56 lwall
+ * patch10: support for shorter extensions.
+ *
+ * Revision 2.0 86/09/17 15:36:39 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+#define DEBUGGING
+
+#define VOIDUSED 7
+#include "config.h"
+
+/* shut lint up about the following when return value ignored */
+
+#define Signal (void)signal
+#define Unlink (void)unlink
+#define Lseek (void)lseek
+#define Fseek (void)fseek
+#define Fstat (void)fstat
+#define Pclose (void)pclose
+#define Close (void)close
+#define Fclose (void)fclose
+#define Fflush (void)fflush
+#define Sprintf (void)sprintf
+#define Mktemp (void)mktemp
+#define Strcpy (void)strcpy
+#define Strcat (void)strcat
+
+/* NeXT declares malloc and realloc incompatibly from us in some of
+ these files. Temporarily redefine them to prevent errors. */
+#define malloc system_malloc
+#define realloc system_realloc
+#include <stdio.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <signal.h>
+#undef malloc
+#undef realloc
+
+/* constants */
+
+/* AIX predefines these. */
+#ifdef TRUE
+#undef TRUE
+#endif
+#ifdef FALSE
+#undef FALSE
+#endif
+#define TRUE (1)
+#define FALSE (0)
+
+#define MAXHUNKSIZE 200000 /* is this enough lines? */
+#define INITHUNKMAX 125 /* initial dynamic allocation size */
+#define MAXLINELEN 4096
+#define BUFFERSIZE 4096
+
+#define SCCSPREFIX "s."
+#define GET "get %s"
+#define GET_LOCKED "get -e %s"
+#define SCCSDIFF "get -p %s | diff - %s >/dev/null"
+
+#define RCSSUFFIX ",v"
+#define CHECKOUT "co %s"
+#define CHECKOUT_LOCKED "co -l %s"
+#define RCSDIFF "rcsdiff %s > /dev/null"
+
+/* handy definitions */
+
+#define Null(t) ((t)0)
+#define Nullch Null(char *)
+#define Nullfp Null(FILE *)
+#define Nulline Null(LINENUM)
+
+#define Ctl(ch) ((ch) & 037)
+
+#define strNE(s1,s2) (strcmp(s1, s2))
+#define strEQ(s1,s2) (!strcmp(s1, s2))
+#define strnNE(s1,s2,l) (strncmp(s1, s2, l))
+#define strnEQ(s1,s2,l) (!strncmp(s1, s2, l))
+
+/* typedefs */
+
+typedef char bool;
+typedef long LINENUM; /* must be signed */
+typedef unsigned MEM; /* what to feed malloc */
+
+/* globals */
+
+EXT int Argc; /* guess */
+EXT char **Argv;
+EXT int optind_last; /* for restarting plan_b */
+
+EXT struct stat filestat; /* file statistics area */
+EXT int filemode INIT(0644);
+
+EXT char buf[MAXLINELEN]; /* general purpose buffer */
+EXT FILE *ofp INIT(Nullfp); /* output file pointer */
+EXT FILE *rejfp INIT(Nullfp); /* reject file pointer */
+
+EXT int myuid; /* cache getuid return value */
+
+EXT bool using_plan_a INIT(TRUE); /* try to keep everything in memory */
+EXT bool out_of_mem INIT(FALSE); /* ran out of memory in plan a */
+
+#define MAXFILEC 2
+EXT int filec INIT(0); /* how many file arguments? */
+EXT char *filearg[MAXFILEC];
+EXT bool ok_to_create_file INIT(FALSE);
+EXT char *bestguess INIT(Nullch); /* guess at correct filename */
+
+EXT char *outname INIT(Nullch);
+EXT char rejname[128];
+
+EXT char *origprae INIT(Nullch);
+
+EXT char *TMPOUTNAME;
+EXT char *TMPINNAME;
+EXT char *TMPREJNAME;
+EXT char *TMPPATNAME;
+EXT bool toutkeep INIT(FALSE);
+EXT bool trejkeep INIT(FALSE);
+
+EXT LINENUM last_offset INIT(0);
+#ifdef DEBUGGING
+EXT int debug INIT(0);
+#endif
+EXT LINENUM maxfuzz INIT(2);
+EXT bool force INIT(FALSE);
+EXT bool batch INIT(FALSE);
+EXT bool verbose INIT(TRUE);
+EXT bool reverse INIT(FALSE);
+EXT bool noreverse INIT(FALSE);
+EXT bool skip_rest_of_patch INIT(FALSE);
+EXT int strippath INIT(957);
+EXT bool canonicalize INIT(FALSE);
+
+#define CONTEXT_DIFF 1
+#define NORMAL_DIFF 2
+#define ED_DIFF 3
+#define NEW_CONTEXT_DIFF 4
+#define UNI_DIFF 5
+EXT int diff_type INIT(0);
+
+EXT bool do_defines INIT(FALSE); /* patch using ifdef, ifndef, etc. */
+EXT char if_defined[128]; /* #ifdef xyzzy */
+EXT char not_defined[128]; /* #ifndef xyzzy */
+EXT char else_defined[] INIT("#else\n");/* #else */
+EXT char end_defined[128]; /* #endif xyzzy */
+
+EXT char *revision INIT(Nullch); /* prerequisite revision, if any */
+
+#include <errno.h>
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#include <string.h>
+#else
+extern int errno;
+FILE *popen();
+char *malloc();
+char *realloc();
+long atol();
+char *getenv();
+char *strcpy();
+char *strcat();
+#endif
+char *mktemp();
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#else
+long lseek();
+#endif
+#if defined(_POSIX_VERSION) || defined(HAVE_FCNTL_H)
+#include <fcntl.h>
+#endif
+
+#if !defined(S_ISDIR) && defined(S_IFDIR)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISREG) && defined(S_IFREG)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
diff --git a/gnu/usr.bin/patch/config.h b/gnu/usr.bin/patch/config.h
new file mode 100644
index 0000000..71623d7
--- /dev/null
+++ b/gnu/usr.bin/patch/config.h
@@ -0,0 +1,81 @@
+/* config.h. Generated automatically by configure. */
+/* Portability variables. -*- C -*- */
+
+/* Define if the system does not support the `const' keyword. */
+/* #undef const */
+
+/* Define if the system supports file names longer than 14 characters. */
+#define HAVE_LONG_FILE_NAMES
+
+/* Define if the system has pathconf(). */
+/* #undef HAVE_PATHCONF */
+
+/* Define if the system has strerror(). */
+#define HAVE_STRERROR 1
+
+/* Define if the system has ANSI C header files and library functions. */
+#define STDC_HEADERS
+
+/* Define if the system uses strchr instead of index
+ and strrchr instead of rindex. */
+#define HAVE_STRING_H 1
+
+#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
+#define index strchr
+#define rindex strrchr
+#endif
+
+/* Define if the system has unistd.h. */
+#define HAVE_UNISTD_H 1
+
+/* Define if the system has fcntl.h. */
+#define HAVE_FCNTL_H 1
+
+/* Define as either int or void -- the type that signal handlers return. */
+#define RETSIGTYPE void
+
+#ifndef RETSIGTYPE
+#define RETSIGTYPE void
+#endif
+
+/* Which directory library header to use. */
+#define DIRENT 1 /* dirent.h */
+/* #undef SYSNDIR */ /* sys/ndir.h */
+/* #undef SYSDIR */ /* sys/dir.h */
+/* #undef NDIR */ /* ndir.h */
+/* #undef NODIR */ /* none -- don't make numbered backup files */
+
+/* Define if the system lets you pass fewer arguments to a function
+ than the function actually accepts (in the absence of a prototype).
+ Defining it makes I/O calls slightly more efficient.
+ You need not bother defining it unless your C preprocessor chokes on
+ multi-line arguments to macros. */
+/* #undef CANVARARG */
+
+/* Define Reg* as either `register' or nothing, depending on whether
+ the C compiler pays attention to this many register declarations.
+ The intent is that you don't have to order your register declarations
+ in the order of importance, so you can freely declare register variables
+ in sub-blocks of code and as function parameters.
+ Do not use Reg<n> more than once per routine.
+
+ These don't really matter a lot, since most modern C compilers ignore
+ register declarations and often do a better job of allocating
+ registers than people do. */
+
+#define Reg1 register
+#define Reg2 register
+#define Reg3 register
+#define Reg4 register
+#define Reg5 register
+#define Reg6 register
+#define Reg7
+#define Reg8
+#define Reg9
+#define Reg10
+#define Reg11
+#define Reg12
+#define Reg13
+#define Reg14
+#define Reg15
+#define Reg16
diff --git a/gnu/usr.bin/patch/getopt.c b/gnu/usr.bin/patch/getopt.c
new file mode 100644
index 0000000..a59a013
--- /dev/null
+++ b/gnu/usr.bin/patch/getopt.c
@@ -0,0 +1,731 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* NOTE!!! AIX requires this to be the first thing in the file.
+ Do not put ANYTHING before it! */
+#if !defined (__GNUC__) && defined (_AIX)
+ #pragma alloca
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not __GNUC__ */
+#if defined (HAVE_ALLOCA_H) || (defined(sparc) && (defined(sun) || (!defined(USG) && !defined(SVR4) && !defined(__svr4__))))
+#include <alloca.h>
+#else
+#ifndef _AIX
+char *alloca ();
+#endif
+#endif /* alloca.h */
+#endif /* not __GNUC__ */
+
+#if !__STDC__ && !defined(const) && IN_GCC
+#define const
+#endif
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#undef alloca
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+#include <stdlib.h>
+#else /* Not GNU C library. */
+#define __alloca alloca
+#endif /* GNU C library. */
+
+/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
+ long-named option. Because this is not POSIX.2 compliant, it is
+ being phased out. */
+/* #define GETOPT_COMPAT */
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = 0;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* XXX 1003.2 says this must be 1 before any call. */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return EOF with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+#include <string.h>
+#define my_index strchr
+#define my_bcopy(src, dst, n) memcpy ((dst), (src), (n))
+#else
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+char *getenv ();
+
+static char *
+my_index (str, chr)
+ const char *str;
+ int chr;
+{
+ while (*str)
+ {
+ if (*str == chr)
+ return (char *) str;
+ str++;
+ }
+ return 0;
+}
+
+static void
+my_bcopy (from, to, size)
+ const char *from;
+ char *to;
+ int size;
+{
+ int i;
+ for (i = 0; i < size; i++)
+ to[i] = from[i];
+}
+#endif /* GNU C library. */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *);
+ char **temp = (char **) __alloca (nonopts_size);
+
+ /* Interchange the two blocks of data in ARGV. */
+
+ my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size);
+ my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt],
+ (optind - last_nonopt) * sizeof (char *));
+ my_bcopy ((char *) temp,
+ (char *) &argv[first_nonopt + optind - last_nonopt],
+ nonopts_size);
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns `EOF'.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ int option_index;
+
+ optarg = 0;
+
+ /* Initialize the internal data when the first call is made.
+ Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ if (optind == 0)
+ {
+ first_nonopt = last_nonopt = optind = 1;
+
+ nextchar = NULL;
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (getenv ("POSIXLY_CORRECT") != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+ }
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Now skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc
+ && (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* Special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return EOF;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if ((argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ {
+ if (ordering == REQUIRE_ORDER)
+ return EOF;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Start decoding its characters. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ if (longopts != NULL
+ && ((argv[optind][0] == '-'
+ && (argv[optind][1] == '-' || long_only))
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ ))
+ {
+ const struct option *p;
+ char *s = nextchar;
+ int exact = 0;
+ int ambig = 0;
+ const struct option *pfound = NULL;
+ int indfound;
+
+ while (*s && *s != '=')
+ s++;
+
+ /* Test all options for either exact match or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name;
+ p++, option_index++)
+ if (!strncmp (p->name, nextchar, s - nextchar))
+ {
+ if (s - nextchar == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' is ambiguous\n",
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*s)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = s + 1;
+ else
+ {
+ if (opterr)
+ {
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ "%s: option `--%s' doesn't allow an argument\n",
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ "%s: option `%c%s' doesn't allow an argument\n",
+ argv[0], argv[optind - 1][0], pfound->name);
+ }
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' requires an argument\n",
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, "%s: unrecognized option `--%s'\n",
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+#if 0
+ if (c < 040 || c >= 0177)
+ fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
+ argv[0], c);
+ else
+ fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
+#endif
+ }
+ optopt = c;
+ return '?';
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = 0;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+#if 0
+ fprintf (stderr, "%s: option `-%c' requires an argument\n",
+ argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: option requires an argument -- %c\n",
+ argv[0], c);
+#endif
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/gnu/usr.bin/patch/getopt.h b/gnu/usr.bin/patch/getopt.h
new file mode 100644
index 0000000..45541f5
--- /dev/null
+++ b/gnu/usr.bin/patch/getopt.h
@@ -0,0 +1,129 @@
+/* Declarations for getopt.
+ Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized. */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+#if __STDC__
+ const char *name;
+#else
+ char *name;
+#endif
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+#if __STDC__
+#if defined(__GNU_LIBRARY__)
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* not __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind);
+
+/* Internal only. Users should not call this directly. */
+extern int _getopt_internal (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* not __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/gnu/usr.bin/patch/getopt1.c b/gnu/usr.bin/patch/getopt1.c
new file mode 100644
index 0000000..a32615c
--- /dev/null
+++ b/gnu/usr.bin/patch/getopt1.c
@@ -0,0 +1,176 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "getopt.h"
+
+#if !__STDC__ && !defined(const) && IN_GCC
+#define const
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#else
+char *getenv ();
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/gnu/usr.bin/patch/inp.c b/gnu/usr.bin/patch/inp.c
new file mode 100644
index 0000000..c99054f
--- /dev/null
+++ b/gnu/usr.bin/patch/inp.c
@@ -0,0 +1,369 @@
+/* $Header: inp.c,v 2.0.1.1 88/06/03 15:06:13 lwall Locked $
+ *
+ * $Log: inp.c,v $
+ * Revision 2.0.1.1 88/06/03 15:06:13 lwall
+ * patch10: made a little smarter about sccs files
+ *
+ * Revision 2.0 86/09/17 15:37:02 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+#include "EXTERN.h"
+#include "common.h"
+#include "util.h"
+#include "pch.h"
+#include "INTERN.h"
+#include "inp.h"
+
+/* Input-file-with-indexable-lines abstract type */
+
+static long i_size; /* size of the input file */
+static char *i_womp; /* plan a buffer for entire file */
+static char **i_ptr; /* pointers to lines in i_womp */
+
+static int tifd = -1; /* plan b virtual string array */
+static char *tibuf[2]; /* plan b buffers */
+static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */
+static LINENUM lines_per_buf; /* how many lines per buffer */
+static int tireclen; /* length of records in tmp file */
+
+/* New patch--prepare to edit another file. */
+
+void
+re_input()
+{
+ if (using_plan_a) {
+ i_size = 0;
+#ifndef lint
+ if (i_ptr != Null(char**))
+ free((char *)i_ptr);
+#endif
+ if (i_womp != Nullch)
+ free(i_womp);
+ i_womp = Nullch;
+ i_ptr = Null(char **);
+ }
+ else {
+ using_plan_a = TRUE; /* maybe the next one is smaller */
+ Close(tifd);
+ tifd = -1;
+ free(tibuf[0]);
+ free(tibuf[1]);
+ tibuf[0] = tibuf[1] = Nullch;
+ tiline[0] = tiline[1] = -1;
+ tireclen = 0;
+ }
+}
+
+/* Constuct the line index, somehow or other. */
+
+void
+scan_input(filename)
+char *filename;
+{
+ if (!plan_a(filename))
+ plan_b(filename);
+ if (verbose) {
+ say3("Patching file %s using Plan %s...\n", filename,
+ (using_plan_a ? "A" : "B") );
+ }
+}
+
+/* Try keeping everything in memory. */
+
+bool
+plan_a(filename)
+char *filename;
+{
+ int ifd, statfailed;
+ Reg1 char *s;
+ Reg2 LINENUM iline;
+ char lbuf[MAXLINELEN];
+ int output_elsewhere = strcmp(filename, outname);
+
+ statfailed = stat(filename, &filestat);
+ if (statfailed && ok_to_create_file) {
+ if (verbose)
+ say2("(Creating file %s...)\n",filename);
+ makedirs(filename, TRUE);
+ close(creat(filename, 0666));
+ statfailed = stat(filename, &filestat);
+ }
+ /* For nonexistent or read-only files, look for RCS or SCCS versions. */
+ if (statfailed
+ || (! output_elsewhere
+ && (/* No one can write to it. */
+ (filestat.st_mode & 0222) == 0
+ /* I can't write to it. */
+ || ((filestat.st_mode & 0022) == 0
+ && filestat.st_uid != myuid)))) {
+ struct stat cstat;
+ char *cs = Nullch;
+ char *filebase;
+ int pathlen;
+
+ filebase = basename(filename);
+ pathlen = filebase - filename;
+
+ /* Put any leading path into `s'.
+ Leave room in lbuf for the diff command. */
+ s = lbuf + 20;
+ strncpy(s, filename, pathlen);
+
+#define try(f,a1,a2) (Sprintf(s + pathlen, f, a1, a2), stat(s, &cstat) == 0)
+ if (( try("RCS/%s%s", filebase, RCSSUFFIX)
+ || try("RCS/%s" , filebase, 0)
+ || try( "%s%s", filebase, RCSSUFFIX))
+ &&
+ /* Check that RCS file is not working file.
+ Some hosts don't report file name length errors. */
+ (statfailed
+ || ( (filestat.st_dev ^ cstat.st_dev)
+ | (filestat.st_ino ^ cstat.st_ino)))) {
+ Sprintf(buf, output_elsewhere?CHECKOUT:CHECKOUT_LOCKED, filename);
+ Sprintf(lbuf, RCSDIFF, filename);
+ cs = "RCS";
+ } else if ( try("SCCS/%s%s", SCCSPREFIX, filebase)
+ || try( "%s%s", SCCSPREFIX, filebase)) {
+ Sprintf(buf, output_elsewhere?GET:GET_LOCKED, s);
+ Sprintf(lbuf, SCCSDIFF, s, filename);
+ cs = "SCCS";
+ } else if (statfailed)
+ fatal2("can't find %s\n", filename);
+ /* else we can't write to it but it's not under a version
+ control system, so just proceed. */
+ if (cs) {
+ if (!statfailed) {
+ if ((filestat.st_mode & 0222) != 0)
+ /* The owner can write to it. */
+ fatal3("file %s seems to be locked by somebody else under %s\n",
+ filename, cs);
+ /* It might be checked out unlocked. See if it's safe to
+ check out the default version locked. */
+ if (verbose)
+ say3("Comparing file %s to default %s version...\n",
+ filename, cs);
+ if (system(lbuf))
+ fatal3("can't check out file %s: differs from default %s version\n",
+ filename, cs);
+ }
+ if (verbose)
+ say3("Checking out file %s from %s...\n", filename, cs);
+ if (system(buf) || stat(filename, &filestat))
+ fatal3("can't check out file %s from %s\n", filename, cs);
+ }
+ }
+ filemode = filestat.st_mode;
+ if (!S_ISREG(filemode))
+ fatal2("%s is not a normal file--can't patch\n", filename);
+ i_size = filestat.st_size;
+ if (out_of_mem) {
+ set_hunkmax(); /* make sure dynamic arrays are allocated */
+ out_of_mem = FALSE;
+ return FALSE; /* force plan b because plan a bombed */
+ }
+#ifdef lint
+ i_womp = Nullch;
+#else
+ i_womp = malloc((MEM)(i_size+2)); /* lint says this may alloc less than */
+ /* i_size, but that's okay, I think. */
+#endif
+ if (i_womp == Nullch)
+ return FALSE;
+ if ((ifd = open(filename, 0)) < 0)
+ pfatal2("can't open file %s", filename);
+#ifndef lint
+ if (read(ifd, i_womp, (int)i_size) != i_size) {
+ Close(ifd); /* probably means i_size > 15 or 16 bits worth */
+ free(i_womp); /* at this point it doesn't matter if i_womp was */
+ return FALSE; /* undersized. */
+ }
+#endif
+ Close(ifd);
+ if (i_size && i_womp[i_size-1] != '\n')
+ i_womp[i_size++] = '\n';
+ i_womp[i_size] = '\0';
+
+ /* count the lines in the buffer so we know how many pointers we need */
+
+ iline = 0;
+ for (s=i_womp; *s; s++) {
+ if (*s == '\n')
+ iline++;
+ }
+#ifdef lint
+ i_ptr = Null(char**);
+#else
+ i_ptr = (char **)malloc((MEM)((iline + 2) * sizeof(char *)));
+#endif
+ if (i_ptr == Null(char **)) { /* shucks, it was a near thing */
+ free((char *)i_womp);
+ return FALSE;
+ }
+
+ /* now scan the buffer and build pointer array */
+
+ iline = 1;
+ i_ptr[iline] = i_womp;
+ for (s=i_womp; *s; s++) {
+ if (*s == '\n')
+ i_ptr[++iline] = s+1; /* these are NOT null terminated */
+ }
+ input_lines = iline - 1;
+
+ /* now check for revision, if any */
+
+ if (revision != Nullch) {
+ if (!rev_in_string(i_womp)) {
+ if (force) {
+ if (verbose)
+ say2(
+"Warning: this file doesn't appear to be the %s version--patching anyway.\n",
+ revision);
+ }
+ else if (batch) {
+ fatal2(
+"this file doesn't appear to be the %s version--aborting.\n", revision);
+ }
+ else {
+ ask2(
+"This file doesn't appear to be the %s version--patch anyway? [n] ",
+ revision);
+ if (*buf != 'y')
+ fatal1("aborted\n");
+ }
+ }
+ else if (verbose)
+ say2("Good. This file appears to be the %s version.\n",
+ revision);
+ }
+ return TRUE; /* plan a will work */
+}
+
+/* Keep (virtually) nothing in memory. */
+
+void
+plan_b(filename)
+char *filename;
+{
+ Reg3 FILE *ifp;
+ Reg1 int i = 0;
+ Reg2 int maxlen = 1;
+ Reg4 bool found_revision = (revision == Nullch);
+
+ using_plan_a = FALSE;
+ if ((ifp = fopen(filename, "r")) == Nullfp)
+ pfatal2("can't open file %s", filename);
+ if ((tifd = creat(TMPINNAME, 0666)) < 0)
+ pfatal2("can't open file %s", TMPINNAME);
+ while (fgets(buf, sizeof buf, ifp) != Nullch) {
+ if (revision != Nullch && !found_revision && rev_in_string(buf))
+ found_revision = TRUE;
+ if ((i = strlen(buf)) > maxlen)
+ maxlen = i; /* find longest line */
+ }
+ if (revision != Nullch) {
+ if (!found_revision) {
+ if (force) {
+ if (verbose)
+ say2(
+"Warning: this file doesn't appear to be the %s version--patching anyway.\n",
+ revision);
+ }
+ else if (batch) {
+ fatal2(
+"this file doesn't appear to be the %s version--aborting.\n", revision);
+ }
+ else {
+ ask2(
+"This file doesn't appear to be the %s version--patch anyway? [n] ",
+ revision);
+ if (*buf != 'y')
+ fatal1("aborted\n");
+ }
+ }
+ else if (verbose)
+ say2("Good. This file appears to be the %s version.\n",
+ revision);
+ }
+ Fseek(ifp, 0L, 0); /* rewind file */
+ lines_per_buf = BUFFERSIZE / maxlen;
+ tireclen = maxlen;
+ tibuf[0] = malloc((MEM)(BUFFERSIZE + 1));
+ tibuf[1] = malloc((MEM)(BUFFERSIZE + 1));
+ if (tibuf[1] == Nullch)
+ fatal1("out of memory\n");
+ for (i=1; ; i++) {
+ if (! (i % lines_per_buf)) /* new block */
+ if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
+ pfatal1("can't write temp file");
+ if (fgets(tibuf[0] + maxlen * (i%lines_per_buf), maxlen + 1, ifp)
+ == Nullch) {
+ input_lines = i - 1;
+ if (i % lines_per_buf)
+ if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
+ pfatal1("can't write temp file");
+ break;
+ }
+ }
+ Fclose(ifp);
+ Close(tifd);
+ if ((tifd = open(TMPINNAME, 0)) < 0) {
+ pfatal2("can't reopen file %s", TMPINNAME);
+ }
+}
+
+/* Fetch a line from the input file, \n terminated, not necessarily \0. */
+
+char *
+ifetch(line,whichbuf)
+Reg1 LINENUM line;
+int whichbuf; /* ignored when file in memory */
+{
+ if (line < 1 || line > input_lines)
+ return "";
+ if (using_plan_a)
+ return i_ptr[line];
+ else {
+ LINENUM offline = line % lines_per_buf;
+ LINENUM baseline = line - offline;
+
+ if (tiline[0] == baseline)
+ whichbuf = 0;
+ else if (tiline[1] == baseline)
+ whichbuf = 1;
+ else {
+ tiline[whichbuf] = baseline;
+#ifndef lint /* complains of long accuracy */
+ Lseek(tifd, (long)baseline / lines_per_buf * BUFFERSIZE, 0);
+#endif
+ if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0)
+ pfatal2("error reading tmp file %s", TMPINNAME);
+ }
+ return tibuf[whichbuf] + (tireclen*offline);
+ }
+}
+
+/* True if the string argument contains the revision number we want. */
+
+bool
+rev_in_string(string)
+char *string;
+{
+ Reg1 char *s;
+ Reg2 int patlen;
+
+ if (revision == Nullch)
+ return TRUE;
+ patlen = strlen(revision);
+ if (strnEQ(string,revision,patlen) && isspace(string[patlen]))
+ return TRUE;
+ for (s = string; *s; s++) {
+ if (isspace(*s) && strnEQ(s+1, revision, patlen) &&
+ isspace(s[patlen+1] )) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
diff --git a/gnu/usr.bin/patch/inp.h b/gnu/usr.bin/patch/inp.h
new file mode 100644
index 0000000..c6d2a91
--- /dev/null
+++ b/gnu/usr.bin/patch/inp.h
@@ -0,0 +1,18 @@
+/* $Header: inp.h,v 2.0 86/09/17 15:37:25 lwall Exp $
+ *
+ * $Log: inp.h,v $
+ * Revision 2.0 86/09/17 15:37:25 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+EXT LINENUM input_lines INIT(0); /* how long is input file in lines */
+EXT LINENUM last_frozen_line INIT(0); /* how many input lines have been */
+ /* irretractibly output */
+
+bool rev_in_string();
+void scan_input();
+bool plan_a(); /* returns false if insufficient memory */
+void plan_b();
+char *ifetch();
+
diff --git a/gnu/usr.bin/patch/patch.1 b/gnu/usr.bin/patch/patch.1
new file mode 100644
index 0000000..6af062a
--- /dev/null
+++ b/gnu/usr.bin/patch/patch.1
@@ -0,0 +1,584 @@
+.\" -*- nroff -*-
+.rn '' }`
+'\" $Header: /home/cvs/386BSD/src/gnu/usr.bin/patch/patch.1,v 1.3 1994/02/17 22:20:33 jkh Exp $
+'\"
+'\" $Log: patch.1,v $
+.\" Revision 1.3 1994/02/17 22:20:33 jkh
+.\" Put this back - I was somehow under the erroneous impression that patch was in
+.\" ports, until I saw the the commit messages, that is! :-) All changed backed out.
+.\"
+.\" Revision 1.2 1994/02/17 22:16:02 jkh
+.\" From Poul-Henning Kamp - Implement a -C option to verify the integrity of
+.\" a patch before actually applying it.
+.\"
+.\" Revision 1.1.1.1 1993/06/19 14:21:51 paul
+.\" b-maked patch-2.10
+.\"
+'\" Revision 2.0.1.2 88/06/22 20:47:18 lwall
+'\" patch12: now avoids Bell System Logo
+'\"
+'\" Revision 2.0.1.1 88/06/03 15:12:51 lwall
+'\" patch10: -B switch was contributed.
+'\"
+'\" Revision 2.0 86/09/17 15:39:09 lwall
+'\" Baseline for netwide release.
+'\"
+'\" Revision 1.4 86/08/01 19:23:22 lwall
+'\" Documented -v, -p, -F.
+'\" Added notes to patch senders.
+'\"
+'\" Revision 1.3 85/03/26 15:11:06 lwall
+'\" Frozen.
+'\"
+'\" Revision 1.2.1.4 85/03/12 16:14:27 lwall
+'\" Documented -p.
+'\"
+'\" Revision 1.2.1.3 85/03/12 16:09:41 lwall
+'\" Documented -D.
+'\"
+'\" Revision 1.2.1.2 84/12/05 11:06:55 lwall
+'\" Added -l switch, and noted bistability bug.
+'\"
+'\" Revision 1.2.1.1 84/12/04 17:23:39 lwall
+'\" Branch for sdcrdcf changes.
+'\"
+'\" Revision 1.2 84/12/04 17:22:02 lwall
+'\" Baseline version.
+'\"
+.de Sh
+.br
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp
+.if t .sp .5v
+.if n .sp
+..
+'\"
+'\" Set up \*(-- to give an unbreakable dash;
+'\" string Tr holds user defined translation string.
+'\" Bell System Logo is used as a dummy character.
+'\"
+.ie n \{\
+.tr \(*W-\*(Tr
+.ds -- \(*W-
+.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+.ds L" ""
+.ds R" ""
+.ds L' '
+.ds R' '
+'br \}
+.el \{\
+.ds -- \(em\|
+.tr \*(Tr
+.ds L" ``
+.ds R" ''
+.ds L' `
+.ds R' '
+'br\}
+.TH PATCH 1 LOCAL
+.SH NAME
+patch - apply a diff file to an original
+.SH SYNOPSIS
+.B patch
+[options] [origfile [patchfile]] [+ [options] [origfile]]...
+.sp
+but usually just
+.sp
+.B patch
+<patchfile
+.SH DESCRIPTION
+.I Patch
+will take a patch file containing any of the four forms of difference
+listing produced by the
+.I diff
+program and apply those differences to an original file, producing a patched
+version.
+By default, the patched version is put in place of the original, with
+the original file backed up to the same name with the
+extension \*(L".orig\*(R" (\*(L"~\*(R" on systems that do not
+support long file names), or as specified by the
+\fB\-b\fP (\fB\-\-suffix\fP),
+\fB\-B\fP (\fB\-\-prefix\fP),
+or
+\fB\-V\fP (\fB\-\-version\-control\fP)
+options.
+The extension used for making backup files may also be specified in the
+.B SIMPLE_BACKUP_SUFFIX
+environment variable, which is overridden by the above options.
+.PP
+If the backup file already exists,
+.B patch
+creates a new backup file name by changing the first lowercase letter
+in the last component of the file's name into uppercase. If there are
+no more lowercase letters in the name, it removes the first character
+from the name. It repeats this process until it comes up with a
+backup file that does not already exist.
+.PP
+You may also specify where you want the output to go with a
+\fB\-o\fP (\fB\-\-output\fP)
+option; if that file already exists, it is backed up first.
+.PP
+If
+.I patchfile
+is omitted, or is a hyphen, the patch will be read from standard input.
+.PP
+Upon startup, patch will attempt to determine the type of the diff listing,
+unless over-ruled by a
+\fB\-c\fP (\fB\-\-context\fP),
+\fB\-e\fP (\fB\-\-ed\fP),
+\fB\-n\fP (\fB\-\-normal\fP),
+or
+\fB\-u\fP (\fB\-\-unified\fP)
+option.
+Context diffs (old-style, new-style, and unified) and
+normal diffs are applied by the
+.I patch
+program itself, while
+.I ed
+diffs are simply fed to the
+.I ed
+editor via a pipe.
+.PP
+.I Patch
+will try to skip any leading garbage, apply the diff,
+and then skip any trailing garbage.
+Thus you could feed an article or message containing a
+diff listing to
+.IR patch ,
+and it should work.
+If the entire diff is indented by a consistent amount,
+this will be taken into account.
+.PP
+With context diffs, and to a lesser extent with normal diffs,
+.I patch
+can detect when the line numbers mentioned in the patch are incorrect,
+and will attempt to find the correct place to apply each hunk of the patch.
+As a first guess, it takes the line number mentioned for the hunk, plus or
+minus any offset used in applying the previous hunk.
+If that is not the correct place,
+.I patch
+will scan both forwards and backwards for a set of lines matching the context
+given in the hunk.
+First
+.I patch
+looks for a place where all lines of the context match.
+If no such place is found, and it's a context diff, and the maximum fuzz factor
+is set to 1 or more, then another scan takes place ignoring the first and last
+line of context.
+If that fails, and the maximum fuzz factor is set to 2 or more,
+the first two and last two lines of context are ignored,
+and another scan is made.
+(The default maximum fuzz factor is 2.)
+If
+.I patch
+cannot find a place to install that hunk of the patch, it will put the
+hunk out to a reject file, which normally is the name of the output file
+plus \*(L".rej\*(R" (\*(L"#\*(R" on systems that do not support
+long file names).
+(Note that the rejected hunk will come out in context diff form whether the
+input patch was a context diff or a normal diff.
+If the input was a normal diff, many of the contexts will simply be null.)
+The line numbers on the hunks in the reject file may be different than
+in the patch file: they reflect the approximate location patch thinks the
+failed hunks belong in the new file rather than the old one.
+.PP
+As each hunk is completed, you will be told whether the hunk succeeded or
+failed, and which line (in the new file)
+.I patch
+thought the hunk should go on.
+If this is different from the line number specified in the diff you will
+be told the offset.
+A single large offset MAY be an indication that a hunk was installed in the
+wrong place.
+You will also be told if a fuzz factor was used to make the match, in which
+case you should also be slightly suspicious.
+.PP
+If no original file is specified on the command line,
+.I patch
+will try to figure out from the leading garbage what the name of the file
+to edit is.
+In the header of a context diff, the file name is found from lines beginning
+with \*(L"***\*(R" or \*(L"---\*(R", with the shortest name of an existing
+file winning.
+Only context diffs have lines like that, but if there is an \*(L"Index:\*(R"
+line in the leading garbage,
+.I patch
+will try to use the file name from that line.
+The context diff header takes precedence over an Index line.
+If no file name can be intuited from the leading garbage, you will be asked
+for the name of the file to patch.
+.PP
+If the original file cannot be found or is read-only, but a suitable
+SCCS or RCS file is handy,
+.I patch
+will attempt to get or check out the file.
+.PP
+Additionally, if the leading garbage contains a \*(L"Prereq: \*(R" line,
+.I patch
+will take the first word from the prerequisites line (normally a version
+number) and check the input file to see if that word can be found.
+If not,
+.I patch
+will ask for confirmation before proceeding.
+.PP
+The upshot of all this is that you should be able to say, while in a news
+interface, the following:
+.Sp
+ | patch -d /usr/src/local/blurfl
+.Sp
+and patch a file in the blurfl directory directly from the article containing
+the patch.
+.PP
+If the patch file contains more than one patch,
+.I patch
+will try to apply each of them as if they came from separate patch files.
+This means, among other things, that it is assumed that the name of the file
+to patch must be determined for each diff listing,
+and that the garbage before each diff listing will
+be examined for interesting things such as file names and revision level, as
+mentioned previously.
+You can give options (and another original file name) for the second and
+subsequent patches by separating the corresponding argument lists
+by a \*(L'+\*(R'.
+(The argument list for a second or subsequent patch may not specify a new
+patch file, however.)
+.PP
+.I Patch
+recognizes the following options:
+.TP 5
+.B "\-b suff, \-\-suffix=suff"
+causes
+.B suff
+to be interpreted as the backup extension, to be
+used in place of \*(L".orig\*(R" or \*(L"~\*(R".
+.TP 5
+.B "\-B pref, \-\-prefix=pref"
+causes
+.B pref
+to be interpreted as a prefix to the backup file
+name. If this argument is specified, any argument from
+.B \-b
+will be ignored.
+.TP 5
+.B "\-c, \-\-context"
+forces
+.I patch
+to interpret the patch file as a context diff.
+.TP 5
+.B "\-C, \-\-check"
+see what would happen, but don't do it.
+.TP 5
+.B "\-d dir, \-\-directory=dir"
+causes
+.I patch
+to interpret
+.B dir
+as a directory, and cd to it before doing
+anything else.
+.TP 5
+.B "\-D sym, \-\-ifdef=sym"
+causes
+.I patch
+to use the "#ifdef...#endif" construct to mark changes.
+.B sym
+will be used as the differentiating symbol.
+.TP 5
+.B "\-e, \-\-ed"
+forces
+.I patch
+to interpret the patch file as an
+.I ed
+script.
+.TP 5
+.B "\-E, \-\-remove\-empty\-files"
+causes
+.I patch
+to remove output files that are empty after the patches have been applied.
+.TP 5
+.B "\-f, \-\-force"
+forces
+.I patch
+to assume that the user knows exactly what he or she is doing, and to not
+ask any questions. It assumes the following: skip patches for which a
+file to patch can't be found; patch files even though they have the
+wrong version for the ``Prereq:'' line in the patch; and assume that
+patches are not reversed even if they look like they are.
+This option does not suppress commentary; use
+.B \-s
+for that.
+.TP 5
+.B "\-t, \-\-batch"
+similar to
+.BR \-f ,
+in that it suppresses questions, but makes some different assumptions:
+skip patches for which a file to patch can't be found (the same as \fB\-f\fP);
+skip patches for which the file has the wrong version for the ``Prereq:'' line
+in the patch; and assume that patches are reversed if they look like
+they are.
+.TP 5
+.B "\-F number, \-\-fuzz=number"
+sets the maximum fuzz factor.
+This option only applies to context diffs, and causes
+.I patch
+to ignore up to that many lines in looking for places to install a hunk.
+Note that a larger fuzz factor increases the odds of a faulty patch.
+The default fuzz factor is 2, and it may not be set to more than
+the number of lines of context in the context diff, ordinarily 3.
+.TP 5
+.B "\-l, \-\-ignore\-whitespace"
+causes the pattern matching to be done loosely, in case the tabs and
+spaces have been munged in your input file.
+Any sequence of whitespace in the pattern line will match any sequence
+in the input file.
+Normal characters must still match exactly.
+Each line of the context must still match a line in the input file.
+.TP 5
+.B "\-n, \-\-normal"
+forces
+.I patch
+to interpret the patch file as a normal diff.
+.TP 5
+.B "\-N, \-\-forward"
+causes
+.I patch
+to ignore patches that it thinks are reversed or already applied.
+See also
+.B \-R .
+.TP 5
+.B "\-o file, \-\-output=file"
+causes
+.B file
+to be interpreted as the output file name.
+.TP 5
+.B "\-p[number], \-\-strip[=number]"
+sets the pathname strip count,
+which controls how pathnames found in the patch file are treated, in case
+the you keep your files in a different directory than the person who sent
+out the patch.
+The strip count specifies how many slashes are to be stripped from
+the front of the pathname.
+(Any intervening directory names also go away.)
+For example, supposing the file name in the patch file was
+.sp
+ /u/howard/src/blurfl/blurfl.c
+.sp
+setting
+.B \-p
+or
+.B \-p0
+gives the entire pathname unmodified,
+.B \-p1
+gives
+.sp
+ u/howard/src/blurfl/blurfl.c
+.sp
+without the leading slash,
+.B \-p4
+gives
+.sp
+ blurfl/blurfl.c
+.sp
+and not specifying
+.B \-p
+at all just gives you "blurfl.c", unless all of the directories in the
+leading path (u/howard/src/blurfl) exist and that path is relative,
+in which case you get the entire pathname unmodified.
+Whatever you end up with is looked for either in the current directory,
+or the directory specified by the
+.B \-d
+option.
+.TP 5
+.B "\-r file, \-\-reject\-file=file"
+causes
+.B file
+to be interpreted as the reject file name.
+.TP 5
+.B "\-R, \-\-reverse"
+tells
+.I patch
+that this patch was created with the old and new files swapped.
+(Yes, I'm afraid that does happen occasionally, human nature being what it
+is.)
+.I Patch
+will attempt to swap each hunk around before applying it.
+Rejects will come out in the swapped format.
+The
+.B \-R
+option will not work with
+.I ed
+diff scripts because there is too little
+information to reconstruct the reverse operation.
+.Sp
+If the first hunk of a patch fails,
+.I patch
+will reverse the hunk to see if it can be applied that way.
+If it can, you will be asked if you want to have the
+.B \-R
+option set.
+If it can't, the patch will continue to be applied normally.
+(Note: this method cannot detect a reversed patch if it is a normal diff
+and if the first command is an append (i.e. it should have been a delete)
+since appends always succeed, due to the fact that a null context will match
+anywhere.
+Luckily, most patches add or change lines rather than delete them, so most
+reversed normal diffs will begin with a delete, which will fail, triggering
+the heuristic.)
+.TP 5
+.B "\-s, \-\-silent, \-\-quiet"
+makes
+.I patch
+do its work silently, unless an error occurs.
+.TP 5
+.B "\-S, \-\-skip"
+causes
+.I patch
+to ignore this patch from the patch file, but continue on looking
+for the next patch in the file.
+Thus
+.sp
+ patch -S + -S + <patchfile
+.sp
+will ignore the first and second of three patches.
+.TP 5
+.B "\-u, \-\-unified"
+forces
+.I patch
+to interpret the patch file as a unified context diff (a unidiff).
+.TP 5
+.B "\-v, \-\-version"
+causes
+.I patch
+to print out its revision header and patch level.
+.TP 5
+.B "\-V method, \-\-version\-\-control=method"
+causes
+.B method
+to be interpreted as a method for creating
+backup file names. The type of backups made can also be given in the
+.B VERSION_CONTROL
+environment variable, which is overridden by this option.
+The
+.B -B
+option overrides this option, causing the prefix to always be used for
+making backup file names.
+The value of the
+.B VERSION_CONTROL
+environment variable and the argument to the
+.B -V
+option are like the GNU
+Emacs `version-control' variable; they also recognize synonyms that
+are more descriptive. The valid values are (unique abbreviations are
+accepted):
+.RS
+.TP
+`t' or `numbered'
+Always make numbered backups.
+.TP
+`nil' or `existing'
+Make numbered backups of files that already
+have them, simple backups of the others.
+This is the default.
+.TP
+`never' or `simple'
+Always make simple backups.
+.RE
+.TP 5
+.B "\-x number, \-\-debug=number"
+sets internal debugging flags, and is of interest only to
+.I patch
+patchers.
+.SH AUTHOR
+Larry Wall <lwall@netlabs.com>
+.br
+with many other contributors.
+.SH ENVIRONMENT
+.TP
+.B TMPDIR
+Directory to put temporary files in; default is /tmp.
+.TP
+.B SIMPLE_BACKUP_SUFFIX
+Extension to use for backup file names instead of \*(L".orig\*(R" or
+\*(L"~\*(R".
+.TP
+.B VERSION_CONTROL
+Selects when numbered backup files are made.
+.SH FILES
+$TMPDIR/patch*
+.SH SEE ALSO
+diff(1)
+.SH NOTES FOR PATCH SENDERS
+There are several things you should bear in mind if you are going to
+be sending out patches.
+First, you can save people a lot of grief by keeping a patchlevel.h file
+which is patched to increment the patch level as the first diff in the
+patch file you send out.
+If you put a Prereq: line in with the patch, it won't let them apply
+patches out of order without some warning.
+Second, make sure you've specified the file names right, either in a
+context diff header, or with an Index: line.
+If you are patching something in a subdirectory, be sure to tell the patch
+user to specify a
+.B \-p
+option as needed.
+Third, you can create a file by sending out a diff that compares a
+null file to the file you want to create.
+This will only work if the file you want to create doesn't exist already in
+the target directory.
+Fourth, take care not to send out reversed patches, since it makes people wonder
+whether they already applied the patch.
+Fifth, while you may be able to get away with putting 582 diff listings into
+one file, it is probably wiser to group related patches into separate files in
+case something goes haywire.
+.SH DIAGNOSTICS
+Too many to list here, but generally indicative that
+.I patch
+couldn't parse your patch file.
+.PP
+The message \*(L"Hmm...\*(R" indicates that there is unprocessed text in
+the patch file and that
+.I patch
+is attempting to intuit whether there is a patch in that text and, if so,
+what kind of patch it is.
+.PP
+.I Patch
+will exit with a non-zero status if any reject files were created.
+When applying a set of patches in a loop it behooves you to check this
+exit status so you don't apply a later patch to a partially patched file.
+.SH CAVEATS
+.I Patch
+cannot tell if the line numbers are off in an
+.I ed
+script, and can only detect
+bad line numbers in a normal diff when it finds a \*(L"change\*(R" or
+a \*(L"delete\*(R" command.
+A context diff using fuzz factor 3 may have the same problem.
+Until a suitable interactive interface is added, you should probably do
+a context diff in these cases to see if the changes made sense.
+Of course, compiling without errors is a pretty good indication that the patch
+worked, but not always.
+.PP
+.I Patch
+usually produces the correct results, even when it has to do a lot of
+guessing.
+However, the results are guaranteed to be correct only when the patch is
+applied to exactly the same version of the file that the patch was
+generated from.
+.SH BUGS
+Could be smarter about partial matches, excessively \&deviant offsets and
+swapped code, but that would take an extra pass.
+.PP
+If code has been duplicated (for instance with #ifdef OLDCODE ... #else ...
+#endif),
+.I patch
+is incapable of patching both versions, and, if it works at all, will likely
+patch the wrong one, and tell you that it succeeded to boot.
+.PP
+If you apply a patch you've already applied,
+.I patch
+will think it is a reversed patch, and offer to un-apply the patch.
+This could be construed as a feature.
+.rn }` ''
diff --git a/gnu/usr.bin/patch/patch.c b/gnu/usr.bin/patch/patch.c
new file mode 100644
index 0000000..626c453
--- /dev/null
+++ b/gnu/usr.bin/patch/patch.c
@@ -0,0 +1,967 @@
+char rcsid[] =
+ "$Header: /home/cvs/386BSD/src/gnu/usr.bin/patch/patch.c,v 1.3 1994/02/17 22:20:34 jkh Exp $";
+
+/* patch - a program to apply diffs to original files
+ *
+ * Copyright 1986, Larry Wall
+ *
+ * This program may be copied as long as you don't try to make any
+ * money off of it, or pretend that you wrote it.
+ *
+ * $Log: patch.c,v $
+ * Revision 1.3 1994/02/17 22:20:34 jkh
+ * Put this back - I was somehow under the erroneous impression that patch was in
+ * ports, until I saw the the commit messages, that is! :-) All changed backed out.
+ *
+ * Revision 1.2 1994/02/17 22:16:03 jkh
+ * From Poul-Henning Kamp - Implement a -C option to verify the integrity of
+ * a patch before actually applying it.
+ *
+ * Revision 1.1.1.1 1993/06/19 14:21:52 paul
+ * b-maked patch-2.10
+ *
+ * Revision 2.0.2.0 90/05/01 22:17:50 davison
+ * patch12u: unidiff support added
+ *
+ * Revision 2.0.1.6 88/06/22 20:46:39 lwall
+ * patch12: rindex() wasn't declared
+ *
+ * Revision 2.0.1.5 88/06/03 15:09:37 lwall
+ * patch10: exit code improved.
+ * patch10: better support for non-flexfilenames.
+ *
+ * Revision 2.0.1.4 87/02/16 14:00:04 lwall
+ * Short replacement caused spurious "Out of sync" message.
+ *
+ * Revision 2.0.1.3 87/01/30 22:45:50 lwall
+ * Improved diagnostic on sync error.
+ * Moved do_ed_script() to pch.c.
+ *
+ * Revision 2.0.1.2 86/11/21 09:39:15 lwall
+ * Fuzz factor caused offset of installed lines.
+ *
+ * Revision 2.0.1.1 86/10/29 13:10:22 lwall
+ * Backwards search could terminate prematurely.
+ *
+ * Revision 2.0 86/09/17 15:37:32 lwall
+ * Baseline for netwide release.
+ *
+ * Revision 1.5 86/08/01 20:53:24 lwall
+ * Changed some %d's to %ld's.
+ * Linted.
+ *
+ * Revision 1.4 86/08/01 19:17:29 lwall
+ * Fixes for machines that can't vararg.
+ * Added fuzz factor.
+ * Generalized -p.
+ * General cleanup.
+ *
+ * 85/08/15 van%ucbmonet@berkeley
+ * Changes for 4.3bsd diff -c.
+ *
+ * Revision 1.3 85/03/26 15:07:43 lwall
+ * Frozen.
+ *
+ * Revision 1.2.1.9 85/03/12 17:03:35 lwall
+ * Changed pfp->_file to fileno(pfp).
+ *
+ * Revision 1.2.1.8 85/03/12 16:30:43 lwall
+ * Check i_ptr and i_womp to make sure they aren't null before freeing.
+ * Also allow ed output to be suppressed.
+ *
+ * Revision 1.2.1.7 85/03/12 15:56:13 lwall
+ * Added -p option from jromine@uci-750a.
+ *
+ * Revision 1.2.1.6 85/03/12 12:12:51 lwall
+ * Now checks for normalness of file to patch.
+ *
+ * Revision 1.2.1.5 85/03/12 11:52:12 lwall
+ * Added -D (#ifdef) option from joe@fluke.
+ *
+ * Revision 1.2.1.4 84/12/06 11:14:15 lwall
+ * Made smarter about SCCS subdirectories.
+ *
+ * Revision 1.2.1.3 84/12/05 11:18:43 lwall
+ * Added -l switch to do loose string comparison.
+ *
+ * Revision 1.2.1.2 84/12/04 09:47:13 lwall
+ * Failed hunk count not reset on multiple patch file.
+ *
+ * Revision 1.2.1.1 84/12/04 09:42:37 lwall
+ * Branch for sdcrdcf changes.
+ *
+ * Revision 1.2 84/11/29 13:29:51 lwall
+ * Linted. Identifiers uniqified. Fixed i_ptr malloc() bug. Fixed
+ * multiple calls to mktemp(). Will now work on machines that can only
+ * read 32767 chars. Added -R option for diffs with new and old swapped.
+ * Various cosmetic changes.
+ *
+ * Revision 1.1 84/11/09 17:03:58 lwall
+ * Initial revision
+ *
+ */
+
+#include "INTERN.h"
+#include "common.h"
+#include "EXTERN.h"
+#include "version.h"
+#include "util.h"
+#include "pch.h"
+#include "inp.h"
+#include "backupfile.h"
+#include "getopt.h"
+
+/* procedures */
+
+void reinitialize_almost_everything();
+void get_some_switches();
+LINENUM locate_hunk();
+void abort_hunk();
+void apply_hunk();
+void init_output();
+void init_reject();
+void copy_till();
+void spew_output();
+void dump_line();
+bool patch_match();
+bool similar();
+void re_input();
+void my_exit();
+
+/* TRUE if -E was specified on command line. */
+static int remove_empty_files = FALSE;
+
+/* TRUE if -R was specified on command line. */
+static int reverse_flag_specified = FALSE;
+
+/* TRUE if -C was specified on command line. */
+static int check_patch = FALSE;
+
+/* Apply a set of diffs as appropriate. */
+
+int
+main(argc,argv)
+int argc;
+char **argv;
+{
+ LINENUM where;
+ LINENUM newwhere;
+ LINENUM fuzz;
+ LINENUM mymaxfuzz;
+ int hunk = 0;
+ int failed = 0;
+ int failtotal = 0;
+ bool rev_okayed = 0;
+ int i;
+
+ setbuf(stderr, serrbuf);
+ for (i = 0; i<MAXFILEC; i++)
+ filearg[i] = Nullch;
+
+ myuid = getuid();
+
+ /* Cons up the names of the temporary files. */
+ {
+ /* Directory for temporary files. */
+ char *tmpdir;
+ int tmpname_len;
+
+ tmpdir = getenv ("TMPDIR");
+ if (tmpdir == NULL) {
+ tmpdir = "/tmp";
+ }
+ tmpname_len = strlen (tmpdir) + 20;
+
+ TMPOUTNAME = (char *) malloc (tmpname_len);
+ strcpy (TMPOUTNAME, tmpdir);
+ strcat (TMPOUTNAME, "/patchoXXXXXX");
+ Mktemp(TMPOUTNAME);
+
+ TMPINNAME = (char *) malloc (tmpname_len);
+ strcpy (TMPINNAME, tmpdir);
+ strcat (TMPINNAME, "/patchiXXXXXX");
+ Mktemp(TMPINNAME);
+
+ TMPREJNAME = (char *) malloc (tmpname_len);
+ strcpy (TMPREJNAME, tmpdir);
+ strcat (TMPREJNAME, "/patchrXXXXXX");
+ Mktemp(TMPREJNAME);
+
+ TMPPATNAME = (char *) malloc (tmpname_len);
+ strcpy (TMPPATNAME, tmpdir);
+ strcat (TMPPATNAME, "/patchpXXXXXX");
+ Mktemp(TMPPATNAME);
+ }
+
+ {
+ char *v;
+
+ v = getenv ("SIMPLE_BACKUP_SUFFIX");
+ if (v)
+ simple_backup_suffix = v;
+ else
+ simple_backup_suffix = ".orig";
+#ifndef NODIR
+ v = getenv ("VERSION_CONTROL");
+ backup_type = get_version (v); /* OK to pass NULL. */
+#endif
+ }
+
+ /* parse switches */
+ Argc = argc;
+ Argv = argv;
+ get_some_switches();
+
+ /* make sure we clean up /tmp in case of disaster */
+ set_signals(0);
+
+ for (
+ open_patch_file(filearg[1]);
+ there_is_another_patch();
+ reinitialize_almost_everything()
+ ) { /* for each patch in patch file */
+
+ if (outname == Nullch)
+ outname = savestr(filearg[0]);
+
+ /* for ed script just up and do it and exit */
+ if (diff_type == ED_DIFF) {
+ do_ed_script();
+ continue;
+ }
+
+ /* initialize the patched file */
+ if (!skip_rest_of_patch)
+ init_output(TMPOUTNAME);
+
+ /* initialize reject file */
+ init_reject(TMPREJNAME);
+
+ /* find out where all the lines are */
+ if (!skip_rest_of_patch)
+ scan_input(filearg[0]);
+
+ /* from here on, open no standard i/o files, because malloc */
+ /* might misfire and we can't catch it easily */
+
+ /* apply each hunk of patch */
+ hunk = 0;
+ failed = 0;
+ rev_okayed = FALSE;
+ out_of_mem = FALSE;
+ while (another_hunk()) {
+ hunk++;
+ fuzz = Nulline;
+ mymaxfuzz = pch_context();
+ if (maxfuzz < mymaxfuzz)
+ mymaxfuzz = maxfuzz;
+ if (!skip_rest_of_patch) {
+ do {
+ where = locate_hunk(fuzz);
+ if (hunk == 1 && where == Nulline && !(force|rev_okayed)) {
+ /* dwim for reversed patch? */
+ if (!pch_swap()) {
+ if (fuzz == Nulline)
+ say1(
+"Not enough memory to try swapped hunk! Assuming unswapped.\n");
+ continue;
+ }
+ reverse = !reverse;
+ where = locate_hunk(fuzz); /* try again */
+ if (where == Nulline) { /* didn't find it swapped */
+ if (!pch_swap()) /* put it back to normal */
+ fatal1("lost hunk on alloc error!\n");
+ reverse = !reverse;
+ }
+ else if (noreverse) {
+ if (!pch_swap()) /* put it back to normal */
+ fatal1("lost hunk on alloc error!\n");
+ reverse = !reverse;
+ say1(
+"Ignoring previously applied (or reversed) patch.\n");
+ skip_rest_of_patch = TRUE;
+ }
+ else if (batch) {
+ if (verbose)
+ say3(
+"%seversed (or previously applied) patch detected! %s -R.",
+ reverse ? "R" : "Unr",
+ reverse ? "Assuming" : "Ignoring");
+ }
+ else {
+ ask3(
+"%seversed (or previously applied) patch detected! %s -R? [y] ",
+ reverse ? "R" : "Unr",
+ reverse ? "Assume" : "Ignore");
+ if (*buf == 'n') {
+ ask1("Apply anyway? [n] ");
+ if (*buf == 'y')
+ rev_okayed = TRUE;
+ else
+ skip_rest_of_patch = TRUE;
+ where = Nulline;
+ reverse = !reverse;
+ if (!pch_swap()) /* put it back to normal */
+ fatal1("lost hunk on alloc error!\n");
+ }
+ }
+ }
+ } while (!skip_rest_of_patch && where == Nulline &&
+ ++fuzz <= mymaxfuzz);
+
+ if (skip_rest_of_patch) { /* just got decided */
+ Fclose(ofp);
+ ofp = Nullfp;
+ }
+ }
+
+ newwhere = pch_newfirst() + last_offset;
+ if (skip_rest_of_patch) {
+ abort_hunk();
+ failed++;
+ if (verbose)
+ say3("Hunk #%d ignored at %ld.\n", hunk, newwhere);
+ }
+ else if (where == Nulline) {
+ abort_hunk();
+ failed++;
+ if (verbose)
+ say3("Hunk #%d failed at %ld.\n", hunk, newwhere);
+ }
+ else {
+ apply_hunk(where);
+ if (verbose) {
+ say3("Hunk #%d succeeded at %ld", hunk, newwhere);
+ if (fuzz)
+ say2(" with fuzz %ld", fuzz);
+ if (last_offset)
+ say3(" (offset %ld line%s)",
+ last_offset, last_offset==1L?"":"s");
+ say1(".\n");
+ }
+ }
+ }
+
+ if (out_of_mem && using_plan_a) {
+ optind = optind_last;
+ say1("\n\nRan out of memory using Plan A--trying again...\n\n");
+ if (ofp)
+ Fclose(ofp);
+ ofp = Nullfp;
+ if (rejfp)
+ Fclose(rejfp);
+ rejfp = Nullfp;
+ continue;
+ }
+
+ assert(hunk);
+
+ /* finish spewing out the new file */
+ if (!skip_rest_of_patch)
+ spew_output();
+
+ /* and put the output where desired */
+ ignore_signals();
+ if (!skip_rest_of_patch) {
+ struct stat statbuf;
+ char *realout = outname;
+
+ if (check_patch) {
+ ;
+ } else if (move_file(TMPOUTNAME, outname) < 0) {
+ toutkeep = TRUE;
+ realout = TMPOUTNAME;
+ chmod(TMPOUTNAME, filemode);
+ }
+ else
+ chmod(outname, filemode);
+
+ if (remove_empty_files && stat(realout, &statbuf) == 0
+ && statbuf.st_size == 0) {
+ if (verbose)
+ say2("Removing %s (empty after patching).\n", realout);
+ while (unlink(realout) >= 0) ; /* while is for Eunice. */
+ }
+ }
+ Fclose(rejfp);
+ rejfp = Nullfp;
+ if (failed) {
+ failtotal += failed;
+ if (!*rejname) {
+ Strcpy(rejname, outname);
+ addext(rejname, ".rej", '#');
+ }
+ if (skip_rest_of_patch) {
+ say4("%d out of %d hunks ignored--saving rejects to %s\n",
+ failed, hunk, rejname);
+ }
+ else {
+ say4("%d out of %d hunks failed--saving rejects to %s\n",
+ failed, hunk, rejname);
+ }
+ if (check_patch) {
+ ;
+ } else if (move_file(TMPREJNAME, rejname) < 0)
+ trejkeep = TRUE;
+ }
+ set_signals(1);
+ }
+ my_exit(failtotal);
+}
+
+/* Prepare to find the next patch to do in the patch file. */
+
+void
+reinitialize_almost_everything()
+{
+ re_patch();
+ re_input();
+
+ input_lines = 0;
+ last_frozen_line = 0;
+
+ filec = 0;
+ if (filearg[0] != Nullch && !out_of_mem) {
+ free(filearg[0]);
+ filearg[0] = Nullch;
+ }
+
+ if (outname != Nullch) {
+ free(outname);
+ outname = Nullch;
+ }
+
+ last_offset = 0;
+
+ diff_type = 0;
+
+ if (revision != Nullch) {
+ free(revision);
+ revision = Nullch;
+ }
+
+ reverse = reverse_flag_specified;
+ skip_rest_of_patch = FALSE;
+
+ get_some_switches();
+
+ if (filec >= 2)
+ fatal1("you may not change to a different patch file\n");
+}
+
+static char *shortopts = "-b:B:cCd:D:eEfF:lnNo:p::r:RsStuvV:x:";
+static struct option longopts[] =
+{
+ {"suffix", 1, NULL, 'b'},
+ {"prefix", 1, NULL, 'B'},
+ {"check", 0, NULL, 'C'},
+ {"context", 0, NULL, 'c'},
+ {"directory", 1, NULL, 'd'},
+ {"ifdef", 1, NULL, 'D'},
+ {"ed", 0, NULL, 'e'},
+ {"remove-empty-files", 0, NULL, 'E'},
+ {"force", 0, NULL, 'f'},
+ {"fuzz", 1, NULL, 'F'},
+ {"ignore-whitespace", 0, NULL, 'l'},
+ {"normal", 0, NULL, 'n'},
+ {"forward", 0, NULL, 'N'},
+ {"output", 1, NULL, 'o'},
+ {"strip", 2, NULL, 'p'},
+ {"reject-file", 1, NULL, 'r'},
+ {"reverse", 0, NULL, 'R'},
+ {"quiet", 0, NULL, 's'},
+ {"silent", 0, NULL, 's'},
+ {"skip", 0, NULL, 'S'},
+ {"batch", 0, NULL, 't'},
+ {"unified", 0, NULL, 'u'},
+ {"version", 0, NULL, 'v'},
+ {"version-control", 1, NULL, 'V'},
+ {"debug", 1, NULL, 'x'},
+ {0, 0, 0, 0}
+};
+
+/* Process switches and filenames up to next '+' or end of list. */
+
+void
+get_some_switches()
+{
+ Reg1 int optc;
+
+ rejname[0] = '\0';
+ optind_last = optind;
+ if (optind == Argc)
+ return;
+ while ((optc = getopt_long (Argc, Argv, shortopts, longopts, (int *) 0))
+ != -1) {
+ if (optc == 1) {
+ if (strEQ(optarg, "+"))
+ return;
+ if (filec == MAXFILEC)
+ fatal1("too many file arguments\n");
+ filearg[filec++] = savestr(optarg);
+ }
+ else {
+ switch (optc) {
+ case 'b':
+ simple_backup_suffix = savestr(optarg);
+ break;
+ case 'B':
+ origprae = savestr(optarg);
+ break;
+ case 'c':
+ diff_type = CONTEXT_DIFF;
+ break;
+ case 'C':
+ check_patch = TRUE;
+ break;
+ case 'd':
+ if (chdir(optarg) < 0)
+ pfatal2("can't cd to %s", optarg);
+ break;
+ case 'D':
+ do_defines = TRUE;
+ if (!isalpha(*optarg) && '_' != *optarg)
+ fatal1("argument to -D is not an identifier\n");
+ Sprintf(if_defined, "#ifdef %s\n", optarg);
+ Sprintf(not_defined, "#ifndef %s\n", optarg);
+ Sprintf(end_defined, "#endif /* %s */\n", optarg);
+ break;
+ case 'e':
+ diff_type = ED_DIFF;
+ break;
+ case 'E':
+ remove_empty_files = TRUE;
+ break;
+ case 'f':
+ force = TRUE;
+ break;
+ case 'F':
+ maxfuzz = atoi(optarg);
+ break;
+ case 'l':
+ canonicalize = TRUE;
+ break;
+ case 'n':
+ diff_type = NORMAL_DIFF;
+ break;
+ case 'N':
+ noreverse = TRUE;
+ break;
+ case 'o':
+ outname = savestr(optarg);
+ break;
+ case 'p':
+ if (optarg)
+ strippath = atoi(optarg);
+ else
+ strippath = 0;
+ break;
+ case 'r':
+ Strcpy(rejname, optarg);
+ break;
+ case 'R':
+ reverse = TRUE;
+ reverse_flag_specified = TRUE;
+ break;
+ case 's':
+ verbose = FALSE;
+ break;
+ case 'S':
+ skip_rest_of_patch = TRUE;
+ break;
+ case 't':
+ batch = TRUE;
+ break;
+ case 'u':
+ diff_type = UNI_DIFF;
+ break;
+ case 'v':
+ version();
+ break;
+ case 'V':
+#ifndef NODIR
+ backup_type = get_version (optarg);
+#endif
+ break;
+#ifdef DEBUGGING
+ case 'x':
+ debug = atoi(optarg);
+ break;
+#endif
+ default:
+ fprintf(stderr, "\
+Usage: %s [options] [origfile [patchfile]] [+ [options] [origfile]]...\n",
+ Argv[0]);
+ fprintf(stderr, "\
+Options:\n\
+ [-cCeEflnNRsStuv] [-b backup-ext] [-B backup-prefix] [-d directory]\n\
+ [-D symbol] [-F max-fuzz] [-o out-file] [-p[strip-count]]\n\
+ [-r rej-name] [-V {numbered,existing,simple}] [--check] [--context]\n\
+ [--prefix=backup-prefix] [--suffix=backup-ext] [--ifdef=symbol]\n\
+ [--directory=directory] [--ed] [--fuzz=max-fuzz] [--force] [--batch]\n\
+ [--ignore-whitespace] [--forward] [--reverse] [--output=out-file]\n");
+ fprintf(stderr, "\
+ [--strip[=strip-count]] [--normal] [--reject-file=rej-name] [--skip]\n\
+ [--remove-empty-files] [--quiet] [--silent] [--unified] [--version]\n\
+ [--version-control={numbered,existing,simple}]\n");
+ my_exit(1);
+ }
+ }
+ }
+
+ /* Process any filename args given after "--". */
+ for (; optind < Argc; ++optind) {
+ if (filec == MAXFILEC)
+ fatal1("too many file arguments\n");
+ filearg[filec++] = savestr(Argv[optind]);
+ }
+}
+
+/* Attempt to find the right place to apply this hunk of patch. */
+
+LINENUM
+locate_hunk(fuzz)
+LINENUM fuzz;
+{
+ Reg1 LINENUM first_guess = pch_first() + last_offset;
+ Reg2 LINENUM offset;
+ LINENUM pat_lines = pch_ptrn_lines();
+ Reg3 LINENUM max_pos_offset = input_lines - first_guess
+ - pat_lines + 1;
+ Reg4 LINENUM max_neg_offset = first_guess - last_frozen_line - 1
+ + pch_context();
+
+ if (!pat_lines) /* null range matches always */
+ return first_guess;
+ if (max_neg_offset >= first_guess) /* do not try lines < 0 */
+ max_neg_offset = first_guess - 1;
+ if (first_guess <= input_lines && patch_match(first_guess, Nulline, fuzz))
+ return first_guess;
+ for (offset = 1; ; offset++) {
+ Reg5 bool check_after = (offset <= max_pos_offset);
+ Reg6 bool check_before = (offset <= max_neg_offset);
+
+ if (check_after && patch_match(first_guess, offset, fuzz)) {
+#ifdef DEBUGGING
+ if (debug & 1)
+ say3("Offset changing from %ld to %ld\n", last_offset, offset);
+#endif
+ last_offset = offset;
+ return first_guess+offset;
+ }
+ else if (check_before && patch_match(first_guess, -offset, fuzz)) {
+#ifdef DEBUGGING
+ if (debug & 1)
+ say3("Offset changing from %ld to %ld\n", last_offset, -offset);
+#endif
+ last_offset = -offset;
+ return first_guess-offset;
+ }
+ else if (!check_before && !check_after)
+ return Nulline;
+ }
+}
+
+/* We did not find the pattern, dump out the hunk so they can handle it. */
+
+void
+abort_hunk()
+{
+ Reg1 LINENUM i;
+ Reg2 LINENUM pat_end = pch_end();
+ /* add in last_offset to guess the same as the previous successful hunk */
+ LINENUM oldfirst = pch_first() + last_offset;
+ LINENUM newfirst = pch_newfirst() + last_offset;
+ LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
+ LINENUM newlast = newfirst + pch_repl_lines() - 1;
+ char *stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : "");
+ char *minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----");
+
+ fprintf(rejfp, "***************\n");
+ for (i=0; i<=pat_end; i++) {
+ switch (pch_char(i)) {
+ case '*':
+ if (oldlast < oldfirst)
+ fprintf(rejfp, "*** 0%s\n", stars);
+ else if (oldlast == oldfirst)
+ fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
+ else
+ fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars);
+ break;
+ case '=':
+ if (newlast < newfirst)
+ fprintf(rejfp, "--- 0%s\n", minuses);
+ else if (newlast == newfirst)
+ fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
+ else
+ fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses);
+ break;
+ case '\n':
+ fprintf(rejfp, "%s", pfetch(i));
+ break;
+ case ' ': case '-': case '+': case '!':
+ fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
+ break;
+ default:
+ fatal1("fatal internal error in abort_hunk\n");
+ }
+ }
+}
+
+/* We found where to apply it (we hope), so do it. */
+
+void
+apply_hunk(where)
+LINENUM where;
+{
+ Reg1 LINENUM old = 1;
+ Reg2 LINENUM lastline = pch_ptrn_lines();
+ Reg3 LINENUM new = lastline+1;
+#define OUTSIDE 0
+#define IN_IFNDEF 1
+#define IN_IFDEF 2
+#define IN_ELSE 3
+ Reg4 int def_state = OUTSIDE;
+ Reg5 bool R_do_defines = do_defines;
+ Reg6 LINENUM pat_end = pch_end();
+
+ where--;
+ while (pch_char(new) == '=' || pch_char(new) == '\n')
+ new++;
+
+ while (old <= lastline) {
+ if (pch_char(old) == '-') {
+ copy_till(where + old - 1);
+ if (R_do_defines) {
+ if (def_state == OUTSIDE) {
+ fputs(not_defined, ofp);
+ def_state = IN_IFNDEF;
+ }
+ else if (def_state == IN_IFDEF) {
+ fputs(else_defined, ofp);
+ def_state = IN_ELSE;
+ }
+ fputs(pfetch(old), ofp);
+ }
+ last_frozen_line++;
+ old++;
+ }
+ else if (new > pat_end) {
+ break;
+ }
+ else if (pch_char(new) == '+') {
+ copy_till(where + old - 1);
+ if (R_do_defines) {
+ if (def_state == IN_IFNDEF) {
+ fputs(else_defined, ofp);
+ def_state = IN_ELSE;
+ }
+ else if (def_state == OUTSIDE) {
+ fputs(if_defined, ofp);
+ def_state = IN_IFDEF;
+ }
+ }
+ fputs(pfetch(new), ofp);
+ new++;
+ }
+ else if (pch_char(new) != pch_char(old)) {
+ say3("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
+ pch_hunk_beg() + old,
+ pch_hunk_beg() + new);
+#ifdef DEBUGGING
+ say3("oldchar = '%c', newchar = '%c'\n",
+ pch_char(old), pch_char(new));
+#endif
+ my_exit(1);
+ }
+ else if (pch_char(new) == '!') {
+ copy_till(where + old - 1);
+ if (R_do_defines) {
+ fputs(not_defined, ofp);
+ def_state = IN_IFNDEF;
+ }
+ while (pch_char(old) == '!') {
+ if (R_do_defines) {
+ fputs(pfetch(old), ofp);
+ }
+ last_frozen_line++;
+ old++;
+ }
+ if (R_do_defines) {
+ fputs(else_defined, ofp);
+ def_state = IN_ELSE;
+ }
+ while (pch_char(new) == '!') {
+ fputs(pfetch(new), ofp);
+ new++;
+ }
+ }
+ else {
+ assert(pch_char(new) == ' ');
+ old++;
+ new++;
+ if (R_do_defines && def_state != OUTSIDE) {
+ fputs(end_defined, ofp);
+ def_state = OUTSIDE;
+ }
+ }
+ }
+ if (new <= pat_end && pch_char(new) == '+') {
+ copy_till(where + old - 1);
+ if (R_do_defines) {
+ if (def_state == OUTSIDE) {
+ fputs(if_defined, ofp);
+ def_state = IN_IFDEF;
+ }
+ else if (def_state == IN_IFNDEF) {
+ fputs(else_defined, ofp);
+ def_state = IN_ELSE;
+ }
+ }
+ while (new <= pat_end && pch_char(new) == '+') {
+ fputs(pfetch(new), ofp);
+ new++;
+ }
+ }
+ if (R_do_defines && def_state != OUTSIDE) {
+ fputs(end_defined, ofp);
+ }
+}
+
+/* Open the new file. */
+
+void
+init_output(name)
+char *name;
+{
+ ofp = fopen(name, "w");
+ if (ofp == Nullfp)
+ pfatal2("can't create %s", name);
+}
+
+/* Open a file to put hunks we can't locate. */
+
+void
+init_reject(name)
+char *name;
+{
+ rejfp = fopen(name, "w");
+ if (rejfp == Nullfp)
+ pfatal2("can't create %s", name);
+}
+
+/* Copy input file to output, up to wherever hunk is to be applied. */
+
+void
+copy_till(lastline)
+Reg1 LINENUM lastline;
+{
+ Reg2 LINENUM R_last_frozen_line = last_frozen_line;
+
+ if (R_last_frozen_line > lastline)
+ fatal1("misordered hunks! output would be garbled\n");
+ while (R_last_frozen_line < lastline) {
+ dump_line(++R_last_frozen_line);
+ }
+ last_frozen_line = R_last_frozen_line;
+}
+
+/* Finish copying the input file to the output file. */
+
+void
+spew_output()
+{
+#ifdef DEBUGGING
+ if (debug & 256)
+ say3("il=%ld lfl=%ld\n",input_lines,last_frozen_line);
+#endif
+ if (input_lines)
+ copy_till(input_lines); /* dump remainder of file */
+ Fclose(ofp);
+ ofp = Nullfp;
+}
+
+/* Copy one line from input to output. */
+
+void
+dump_line(line)
+LINENUM line;
+{
+ Reg1 char *s;
+ Reg2 char R_newline = '\n';
+
+ /* Note: string is not null terminated. */
+ for (s=ifetch(line, 0); putc(*s, ofp) != R_newline; s++) ;
+}
+
+/* Does the patch pattern match at line base+offset? */
+
+bool
+patch_match(base, offset, fuzz)
+LINENUM base;
+LINENUM offset;
+LINENUM fuzz;
+{
+ Reg1 LINENUM pline = 1 + fuzz;
+ Reg2 LINENUM iline;
+ Reg3 LINENUM pat_lines = pch_ptrn_lines() - fuzz;
+
+ for (iline=base+offset+fuzz; pline <= pat_lines; pline++,iline++) {
+ if (canonicalize) {
+ if (!similar(ifetch(iline, (offset >= 0)),
+ pfetch(pline),
+ pch_line_len(pline) ))
+ return FALSE;
+ }
+ else if (strnNE(ifetch(iline, (offset >= 0)),
+ pfetch(pline),
+ pch_line_len(pline) ))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Do two lines match with canonicalized white space? */
+
+bool
+similar(a,b,len)
+Reg1 char *a;
+Reg2 char *b;
+Reg3 int len;
+{
+ while (len) {
+ if (isspace(*b)) { /* whitespace (or \n) to match? */
+ if (!isspace(*a)) /* no corresponding whitespace? */
+ return FALSE;
+ while (len && isspace(*b) && *b != '\n')
+ b++,len--; /* skip pattern whitespace */
+ while (isspace(*a) && *a != '\n')
+ a++; /* skip target whitespace */
+ if (*a == '\n' || *b == '\n')
+ return (*a == *b); /* should end in sync */
+ }
+ else if (*a++ != *b++) /* match non-whitespace chars */
+ return FALSE;
+ else
+ len--; /* probably not necessary */
+ }
+ return TRUE; /* actually, this is not reached */
+ /* since there is always a \n */
+}
+
+/* Exit with cleanup. */
+
+void
+my_exit(status)
+int status;
+{
+ Unlink(TMPINNAME);
+ if (!toutkeep) {
+ Unlink(TMPOUTNAME);
+ }
+ if (!trejkeep) {
+ Unlink(TMPREJNAME);
+ }
+ Unlink(TMPPATNAME);
+ exit(status);
+}
diff --git a/gnu/usr.bin/patch/patchlevel.h b/gnu/usr.bin/patch/patchlevel.h
new file mode 100644
index 0000000..d5de3a9
--- /dev/null
+++ b/gnu/usr.bin/patch/patchlevel.h
@@ -0,0 +1 @@
+#define PATCH_VERSION "2.1"
diff --git a/gnu/usr.bin/patch/pch.c b/gnu/usr.bin/patch/pch.c
new file mode 100644
index 0000000..0a844f2
--- /dev/null
+++ b/gnu/usr.bin/patch/pch.c
@@ -0,0 +1,1317 @@
+/* $Header: /home/cvs/386BSD/src/gnu/usr.bin/patch/pch.c,v 1.3 1994/02/17 22:20:36 jkh Exp $
+ *
+ * $Log: pch.c,v $
+ * Revision 1.3 1994/02/17 22:20:36 jkh
+ * Put this back - I was somehow under the erroneous impression that patch was in
+ * ports, until I saw the the commit messages, that is! :-) All changed backed out.
+ *
+ * Revision 1.2 1994/02/17 22:16:05 jkh
+ * From Poul-Henning Kamp - Implement a -C option to verify the integrity of
+ * a patch before actually applying it.
+ *
+ * Revision 1.1.1.1 1993/06/19 14:21:52 paul
+ * b-maked patch-2.10
+ *
+ * Revision 2.0.2.0 90/05/01 22:17:51 davison
+ * patch12u: unidiff support added
+ *
+ * Revision 2.0.1.7 88/06/03 15:13:28 lwall
+ * patch10: Can now find patches in shar scripts.
+ * patch10: Hunks that swapped and then swapped back could core dump.
+ *
+ * Revision 2.0.1.6 87/06/04 16:18:13 lwall
+ * pch_swap didn't swap p_bfake and p_efake.
+ *
+ * Revision 2.0.1.5 87/01/30 22:47:42 lwall
+ * Improved responses to mangled patches.
+ *
+ * Revision 2.0.1.4 87/01/05 16:59:53 lwall
+ * New-style context diffs caused double call to free().
+ *
+ * Revision 2.0.1.3 86/11/14 10:08:33 lwall
+ * Fixed problem where a long pattern wouldn't grow the hunk.
+ * Also restored p_input_line when backtracking so error messages are right.
+ *
+ * Revision 2.0.1.2 86/11/03 17:49:52 lwall
+ * New-style delete triggers spurious assertion error.
+ *
+ * Revision 2.0.1.1 86/10/29 15:52:08 lwall
+ * Could falsely report new-style context diff.
+ *
+ * Revision 2.0 86/09/17 15:39:37 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+#include "EXTERN.h"
+#include "common.h"
+#include "util.h"
+#include "INTERN.h"
+#include "pch.h"
+
+/* Patch (diff listing) abstract type. */
+
+static long p_filesize; /* size of the patch file */
+static LINENUM p_first; /* 1st line number */
+static LINENUM p_newfirst; /* 1st line number of replacement */
+static LINENUM p_ptrn_lines; /* # lines in pattern */
+static LINENUM p_repl_lines; /* # lines in replacement text */
+static LINENUM p_end = -1; /* last line in hunk */
+static LINENUM p_max; /* max allowed value of p_end */
+static LINENUM p_context = 3; /* # of context lines */
+static LINENUM p_input_line = 0; /* current line # from patch file */
+static char **p_line = Null(char**); /* the text of the hunk */
+static short *p_len = Null(short*); /* length of each line */
+static char *p_Char = Nullch; /* +, -, and ! */
+static int hunkmax = INITHUNKMAX; /* size of above arrays to begin with */
+static int p_indent; /* indent to patch */
+static LINENUM p_base; /* where to intuit this time */
+static LINENUM p_bline; /* line # of p_base */
+static LINENUM p_start; /* where intuit found a patch */
+static LINENUM p_sline; /* and the line number for it */
+static LINENUM p_hunk_beg; /* line number of current hunk */
+static LINENUM p_efake = -1; /* end of faked up lines--don't free */
+static LINENUM p_bfake = -1; /* beg of faked up lines */
+
+/* Prepare to look for the next patch in the patch file. */
+
+void
+re_patch()
+{
+ p_first = Nulline;
+ p_newfirst = Nulline;
+ p_ptrn_lines = Nulline;
+ p_repl_lines = Nulline;
+ p_end = (LINENUM)-1;
+ p_max = Nulline;
+ p_indent = 0;
+}
+
+/* Open the patch file at the beginning of time. */
+
+void
+open_patch_file(filename)
+char *filename;
+{
+ if (filename == Nullch || !*filename || strEQ(filename, "-")) {
+ pfp = fopen(TMPPATNAME, "w");
+ if (pfp == Nullfp)
+ pfatal2("can't create %s", TMPPATNAME);
+ while (fgets(buf, sizeof buf, stdin) != Nullch)
+ fputs(buf, pfp);
+ Fclose(pfp);
+ filename = TMPPATNAME;
+ }
+ pfp = fopen(filename, "r");
+ if (pfp == Nullfp)
+ pfatal2("patch file %s not found", filename);
+ Fstat(fileno(pfp), &filestat);
+ p_filesize = filestat.st_size;
+ next_intuit_at(0L,1L); /* start at the beginning */
+ set_hunkmax();
+}
+
+/* Make sure our dynamically realloced tables are malloced to begin with. */
+
+void
+set_hunkmax()
+{
+#ifndef lint
+ if (p_line == Null(char**))
+ p_line = (char**) malloc((MEM)hunkmax * sizeof(char *));
+ if (p_len == Null(short*))
+ p_len = (short*) malloc((MEM)hunkmax * sizeof(short));
+#endif
+ if (p_Char == Nullch)
+ p_Char = (char*) malloc((MEM)hunkmax * sizeof(char));
+}
+
+/* Enlarge the arrays containing the current hunk of patch. */
+
+void
+grow_hunkmax()
+{
+ hunkmax *= 2;
+ /*
+ * Note that on most systems, only the p_line array ever gets fresh memory
+ * since p_len can move into p_line's old space, and p_Char can move into
+ * p_len's old space. Not on PDP-11's however. But it doesn't matter.
+ */
+ assert(p_line != Null(char**) && p_len != Null(short*) && p_Char != Nullch);
+#ifndef lint
+ p_line = (char**) realloc((char*)p_line, (MEM)hunkmax * sizeof(char *));
+ p_len = (short*) realloc((char*)p_len, (MEM)hunkmax * sizeof(short));
+ p_Char = (char*) realloc((char*)p_Char, (MEM)hunkmax * sizeof(char));
+#endif
+ if (p_line != Null(char**) && p_len != Null(short*) && p_Char != Nullch)
+ return;
+ if (!using_plan_a)
+ fatal1("out of memory\n");
+ out_of_mem = TRUE; /* whatever is null will be allocated again */
+ /* from within plan_a(), of all places */
+}
+
+/* True if the remainder of the patch file contains a diff of some sort. */
+
+bool
+there_is_another_patch()
+{
+ if (p_base != 0L && p_base >= p_filesize) {
+ if (verbose)
+ say1("done\n");
+ return FALSE;
+ }
+ if (verbose)
+ say1("Hmm...");
+ diff_type = intuit_diff_type();
+ if (!diff_type) {
+ if (p_base != 0L) {
+ if (verbose)
+ say1(" Ignoring the trailing garbage.\ndone\n");
+ }
+ else
+ say1(" I can't seem to find a patch in there anywhere.\n");
+ return FALSE;
+ }
+ if (verbose)
+ say3(" %sooks like %s to me...\n",
+ (p_base == 0L ? "L" : "The next patch l"),
+ diff_type == UNI_DIFF ? "a unified diff" :
+ diff_type == CONTEXT_DIFF ? "a context diff" :
+ diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" :
+ diff_type == NORMAL_DIFF ? "a normal diff" :
+ "an ed script" );
+ if (p_indent && verbose)
+ say3("(Patch is indented %d space%s.)\n", p_indent, p_indent==1?"":"s");
+ skip_to(p_start,p_sline);
+ while (filearg[0] == Nullch) {
+ if (force || batch) {
+ say1("No file to patch. Skipping...\n");
+ filearg[0] = savestr(bestguess);
+ skip_rest_of_patch = TRUE;
+ return TRUE;
+ }
+ ask1("File to patch: ");
+ if (*buf != '\n') {
+ if (bestguess)
+ free(bestguess);
+ bestguess = savestr(buf);
+ filearg[0] = fetchname(buf, 0, FALSE);
+ }
+ if (filearg[0] == Nullch) {
+ ask1("No file found--skip this patch? [n] ");
+ if (*buf != 'y') {
+ continue;
+ }
+ if (verbose)
+ say1("Skipping patch...\n");
+ filearg[0] = fetchname(bestguess, 0, TRUE);
+ skip_rest_of_patch = TRUE;
+ return TRUE;
+ }
+ }
+ return TRUE;
+}
+
+/* Determine what kind of diff is in the remaining part of the patch file. */
+
+int
+intuit_diff_type()
+{
+ Reg4 long this_line = 0;
+ Reg5 long previous_line;
+ Reg6 long first_command_line = -1;
+ long fcl_line;
+ Reg7 bool last_line_was_command = FALSE;
+ Reg8 bool this_is_a_command = FALSE;
+ Reg9 bool stars_last_line = FALSE;
+ Reg10 bool stars_this_line = FALSE;
+ Reg3 int indent;
+ Reg1 char *s;
+ Reg2 char *t;
+ char *indtmp = Nullch;
+ char *oldtmp = Nullch;
+ char *newtmp = Nullch;
+ char *indname = Nullch;
+ char *oldname = Nullch;
+ char *newname = Nullch;
+ Reg11 int retval;
+ bool no_filearg = (filearg[0] == Nullch);
+
+ ok_to_create_file = FALSE;
+ Fseek(pfp, p_base, 0);
+ p_input_line = p_bline - 1;
+ for (;;) {
+ previous_line = this_line;
+ last_line_was_command = this_is_a_command;
+ stars_last_line = stars_this_line;
+ this_line = ftell(pfp);
+ indent = 0;
+ p_input_line++;
+ if (fgets(buf, sizeof buf, pfp) == Nullch) {
+ if (first_command_line >= 0L) {
+ /* nothing but deletes!? */
+ p_start = first_command_line;
+ p_sline = fcl_line;
+ retval = ED_DIFF;
+ goto scan_exit;
+ }
+ else {
+ p_start = this_line;
+ p_sline = p_input_line;
+ retval = 0;
+ goto scan_exit;
+ }
+ }
+ for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) {
+ if (*s == '\t')
+ indent += 8 - (indent % 8);
+ else
+ indent++;
+ }
+ for (t=s; isdigit(*t) || *t == ','; t++) ;
+ this_is_a_command = (isdigit(*s) &&
+ (*t == 'd' || *t == 'c' || *t == 'a') );
+ if (first_command_line < 0L && this_is_a_command) {
+ first_command_line = this_line;
+ fcl_line = p_input_line;
+ p_indent = indent; /* assume this for now */
+ }
+ if (!stars_last_line && strnEQ(s, "*** ", 4))
+ oldtmp = savestr(s+4);
+ else if (strnEQ(s, "--- ", 4))
+ newtmp = savestr(s+4);
+ else if (strnEQ(s, "+++ ", 4))
+ oldtmp = savestr(s+4); /* pretend it is the old name */
+ else if (strnEQ(s, "Index:", 6))
+ indtmp = savestr(s+6);
+ else if (strnEQ(s, "Prereq:", 7)) {
+ for (t=s+7; isspace(*t); t++) ;
+ revision = savestr(t);
+ for (t=revision; *t && !isspace(*t); t++) ;
+ *t = '\0';
+ if (!*revision) {
+ free(revision);
+ revision = Nullch;
+ }
+ }
+ if ((!diff_type || diff_type == ED_DIFF) &&
+ first_command_line >= 0L &&
+ strEQ(s, ".\n") ) {
+ p_indent = indent;
+ p_start = first_command_line;
+ p_sline = fcl_line;
+ retval = ED_DIFF;
+ goto scan_exit;
+ }
+ if ((!diff_type || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) {
+ if (!atol(s+3))
+ ok_to_create_file = TRUE;
+ p_indent = indent;
+ p_start = this_line;
+ p_sline = p_input_line;
+ retval = UNI_DIFF;
+ goto scan_exit;
+ }
+ stars_this_line = strnEQ(s, "********", 8);
+ if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line &&
+ strnEQ(s, "*** ", 4)) {
+ if (!atol(s+4))
+ ok_to_create_file = TRUE;
+ /* if this is a new context diff the character just before */
+ /* the newline is a '*'. */
+ while (*s != '\n')
+ s++;
+ p_indent = indent;
+ p_start = previous_line;
+ p_sline = p_input_line - 1;
+ retval = (*(s-1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF);
+ goto scan_exit;
+ }
+ if ((!diff_type || diff_type == NORMAL_DIFF) &&
+ last_line_was_command &&
+ (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2)) ) {
+ p_start = previous_line;
+ p_sline = p_input_line - 1;
+ p_indent = indent;
+ retval = NORMAL_DIFF;
+ goto scan_exit;
+ }
+ }
+ scan_exit:
+ if (no_filearg) {
+ if (indtmp != Nullch)
+ indname = fetchname(indtmp, strippath, ok_to_create_file);
+ if (oldtmp != Nullch)
+ oldname = fetchname(oldtmp, strippath, ok_to_create_file);
+ if (newtmp != Nullch)
+ newname = fetchname(newtmp, strippath, ok_to_create_file);
+ if (oldname && newname) {
+ if (strlen(oldname) < strlen(newname))
+ filearg[0] = savestr(oldname);
+ else
+ filearg[0] = savestr(newname);
+ }
+ else if (oldname)
+ filearg[0] = savestr(oldname);
+ else if (newname)
+ filearg[0] = savestr(newname);
+ else if (indname)
+ filearg[0] = savestr(indname);
+ }
+ if (bestguess) {
+ free(bestguess);
+ bestguess = Nullch;
+ }
+ if (filearg[0] != Nullch)
+ bestguess = savestr(filearg[0]);
+ else if (indtmp != Nullch)
+ bestguess = fetchname(indtmp, strippath, TRUE);
+ else {
+ if (oldtmp != Nullch)
+ oldname = fetchname(oldtmp, strippath, TRUE);
+ if (newtmp != Nullch)
+ newname = fetchname(newtmp, strippath, TRUE);
+ if (oldname && newname) {
+ if (strlen(oldname) < strlen(newname))
+ bestguess = savestr(oldname);
+ else
+ bestguess = savestr(newname);
+ }
+ else if (oldname)
+ bestguess = savestr(oldname);
+ else if (newname)
+ bestguess = savestr(newname);
+ }
+ if (indtmp != Nullch)
+ free(indtmp);
+ if (oldtmp != Nullch)
+ free(oldtmp);
+ if (newtmp != Nullch)
+ free(newtmp);
+ if (indname != Nullch)
+ free(indname);
+ if (oldname != Nullch)
+ free(oldname);
+ if (newname != Nullch)
+ free(newname);
+ return retval;
+}
+
+/* Remember where this patch ends so we know where to start up again. */
+
+void
+next_intuit_at(file_pos,file_line)
+long file_pos;
+long file_line;
+{
+ p_base = file_pos;
+ p_bline = file_line;
+}
+
+/* Basically a verbose fseek() to the actual diff listing. */
+
+void
+skip_to(file_pos,file_line)
+long file_pos;
+long file_line;
+{
+ char *ret;
+
+ assert(p_base <= file_pos);
+ if (verbose && p_base < file_pos) {
+ Fseek(pfp, p_base, 0);
+ say1("The text leading up to this was:\n--------------------------\n");
+ while (ftell(pfp) < file_pos) {
+ ret = fgets(buf, sizeof buf, pfp);
+ assert(ret != Nullch);
+ say2("|%s", buf);
+ }
+ say1("--------------------------\n");
+ }
+ else
+ Fseek(pfp, file_pos, 0);
+ p_input_line = file_line - 1;
+}
+
+/* Make this a function for better debugging. */
+static void
+malformed ()
+{
+ fatal3("malformed patch at line %ld: %s", p_input_line, buf);
+ /* about as informative as "Syntax error" in C */
+}
+
+/* True if there is more of the current diff listing to process. */
+
+bool
+another_hunk()
+{
+ Reg1 char *s;
+ Reg8 char *ret;
+ Reg2 int context = 0;
+
+ while (p_end >= 0) {
+ if (p_end == p_efake)
+ p_end = p_bfake; /* don't free twice */
+ else
+ free(p_line[p_end]);
+ p_end--;
+ }
+ assert(p_end == -1);
+ p_efake = -1;
+
+ p_max = hunkmax; /* gets reduced when --- found */
+ if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) {
+ long line_beginning = ftell(pfp);
+ /* file pos of the current line */
+ LINENUM repl_beginning = 0; /* index of --- line */
+ Reg4 LINENUM fillcnt = 0; /* #lines of missing ptrn or repl */
+ Reg5 LINENUM fillsrc; /* index of first line to copy */
+ Reg6 LINENUM filldst; /* index of first missing line */
+ bool ptrn_spaces_eaten = FALSE; /* ptrn was slightly misformed */
+ Reg9 bool repl_could_be_missing = TRUE;
+ /* no + or ! lines in this hunk */
+ bool repl_missing = FALSE; /* we are now backtracking */
+ long repl_backtrack_position = 0;
+ /* file pos of first repl line */
+ LINENUM repl_patch_line; /* input line number for same */
+ Reg7 LINENUM ptrn_copiable = 0;
+ /* # of copiable lines in ptrn */
+
+ ret = pgets(buf, sizeof buf, pfp);
+ p_input_line++;
+ if (ret == Nullch || strnNE(buf, "********", 8)) {
+ next_intuit_at(line_beginning,p_input_line);
+ return FALSE;
+ }
+ p_context = 100;
+ p_hunk_beg = p_input_line + 1;
+ while (p_end < p_max) {
+ line_beginning = ftell(pfp);
+ ret = pgets(buf, sizeof buf, pfp);
+ p_input_line++;
+ if (ret == Nullch) {
+ if (p_max - p_end < 4)
+ Strcpy(buf, " \n"); /* assume blank lines got chopped */
+ else {
+ if (repl_beginning && repl_could_be_missing) {
+ repl_missing = TRUE;
+ goto hunk_done;
+ }
+ fatal1("unexpected end of file in patch\n");
+ }
+ }
+ p_end++;
+ assert(p_end < hunkmax);
+ p_Char[p_end] = *buf;
+#ifdef zilog
+ p_line[(short)p_end] = Nullch;
+#else
+ p_line[p_end] = Nullch;
+#endif
+ switch (*buf) {
+ case '*':
+ if (strnEQ(buf, "********", 8)) {
+ if (repl_beginning && repl_could_be_missing) {
+ repl_missing = TRUE;
+ goto hunk_done;
+ }
+ else
+ fatal2("unexpected end of hunk at line %ld\n",
+ p_input_line);
+ }
+ if (p_end != 0) {
+ if (repl_beginning && repl_could_be_missing) {
+ repl_missing = TRUE;
+ goto hunk_done;
+ }
+ fatal3("unexpected *** at line %ld: %s", p_input_line, buf);
+ }
+ context = 0;
+ p_line[p_end] = savestr(buf);
+ if (out_of_mem) {
+ p_end--;
+ return FALSE;
+ }
+ for (s=buf; *s && !isdigit(*s); s++) ;
+ if (!*s)
+ malformed ();
+ if (strnEQ(s,"0,0",3))
+ strcpy(s,s+2);
+ p_first = (LINENUM) atol(s);
+ while (isdigit(*s)) s++;
+ if (*s == ',') {
+ for (; *s && !isdigit(*s); s++) ;
+ if (!*s)
+ malformed ();
+ p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1;
+ }
+ else if (p_first)
+ p_ptrn_lines = 1;
+ else {
+ p_ptrn_lines = 0;
+ p_first = 1;
+ }
+ p_max = p_ptrn_lines + 6; /* we need this much at least */
+ while (p_max >= hunkmax)
+ grow_hunkmax();
+ p_max = hunkmax;
+ break;
+ case '-':
+ if (buf[1] == '-') {
+ if (repl_beginning ||
+ (p_end != p_ptrn_lines + 1 + (p_Char[p_end-1] == '\n')))
+ {
+ if (p_end == 1) {
+ /* `old' lines were omitted - set up to fill */
+ /* them in from 'new' context lines. */
+ p_end = p_ptrn_lines + 1;
+ fillsrc = p_end + 1;
+ filldst = 1;
+ fillcnt = p_ptrn_lines;
+ }
+ else {
+ if (repl_beginning) {
+ if (repl_could_be_missing){
+ repl_missing = TRUE;
+ goto hunk_done;
+ }
+ fatal3(
+"duplicate \"---\" at line %ld--check line numbers at line %ld\n",
+ p_input_line, p_hunk_beg + repl_beginning);
+ }
+ else {
+ fatal4(
+"%s \"---\" at line %ld--check line numbers at line %ld\n",
+ (p_end <= p_ptrn_lines
+ ? "Premature"
+ : "Overdue" ),
+ p_input_line, p_hunk_beg);
+ }
+ }
+ }
+ repl_beginning = p_end;
+ repl_backtrack_position = ftell(pfp);
+ repl_patch_line = p_input_line;
+ p_line[p_end] = savestr(buf);
+ if (out_of_mem) {
+ p_end--;
+ return FALSE;
+ }
+ p_Char[p_end] = '=';
+ for (s=buf; *s && !isdigit(*s); s++) ;
+ if (!*s)
+ malformed ();
+ p_newfirst = (LINENUM) atol(s);
+ while (isdigit(*s)) s++;
+ if (*s == ',') {
+ for (; *s && !isdigit(*s); s++) ;
+ if (!*s)
+ malformed ();
+ p_repl_lines = ((LINENUM)atol(s)) - p_newfirst + 1;
+ }
+ else if (p_newfirst)
+ p_repl_lines = 1;
+ else {
+ p_repl_lines = 0;
+ p_newfirst = 1;
+ }
+ p_max = p_repl_lines + p_end;
+ if (p_max > MAXHUNKSIZE)
+ fatal4("hunk too large (%ld lines) at line %ld: %s",
+ p_max, p_input_line, buf);
+ while (p_max >= hunkmax)
+ grow_hunkmax();
+ if (p_repl_lines != ptrn_copiable
+ && (p_context != 0 || p_repl_lines != 1))
+ repl_could_be_missing = FALSE;
+ break;
+ }
+ goto change_line;
+ case '+': case '!':
+ repl_could_be_missing = FALSE;
+ change_line:
+ if (buf[1] == '\n' && canonicalize)
+ strcpy(buf+1," \n");
+ if (!isspace(buf[1]) && buf[1] != '>' && buf[1] != '<' &&
+ repl_beginning && repl_could_be_missing) {
+ repl_missing = TRUE;
+ goto hunk_done;
+ }
+ if (context >= 0) {
+ if (context < p_context)
+ p_context = context;
+ context = -1000;
+ }
+ p_line[p_end] = savestr(buf+2);
+ if (out_of_mem) {
+ p_end--;
+ return FALSE;
+ }
+ break;
+ case '\t': case '\n': /* assume the 2 spaces got eaten */
+ if (repl_beginning && repl_could_be_missing &&
+ (!ptrn_spaces_eaten || diff_type == NEW_CONTEXT_DIFF) ) {
+ repl_missing = TRUE;
+ goto hunk_done;
+ }
+ p_line[p_end] = savestr(buf);
+ if (out_of_mem) {
+ p_end--;
+ return FALSE;
+ }
+ if (p_end != p_ptrn_lines + 1) {
+ ptrn_spaces_eaten |= (repl_beginning != 0);
+ context++;
+ if (!repl_beginning)
+ ptrn_copiable++;
+ p_Char[p_end] = ' ';
+ }
+ break;
+ case ' ':
+ if (!isspace(buf[1]) &&
+ repl_beginning && repl_could_be_missing) {
+ repl_missing = TRUE;
+ goto hunk_done;
+ }
+ context++;
+ if (!repl_beginning)
+ ptrn_copiable++;
+ p_line[p_end] = savestr(buf+2);
+ if (out_of_mem) {
+ p_end--;
+ return FALSE;
+ }
+ break;
+ default:
+ if (repl_beginning && repl_could_be_missing) {
+ repl_missing = TRUE;
+ goto hunk_done;
+ }
+ malformed ();
+ }
+ /* set up p_len for strncmp() so we don't have to */
+ /* assume null termination */
+ if (p_line[p_end])
+ p_len[p_end] = strlen(p_line[p_end]);
+ else
+ p_len[p_end] = 0;
+ }
+
+ hunk_done:
+ if (p_end >=0 && !repl_beginning)
+ fatal2("no --- found in patch at line %ld\n", pch_hunk_beg());
+
+ if (repl_missing) {
+
+ /* reset state back to just after --- */
+ p_input_line = repl_patch_line;
+ for (p_end--; p_end > repl_beginning; p_end--)
+ free(p_line[p_end]);
+ Fseek(pfp, repl_backtrack_position, 0);
+
+ /* redundant 'new' context lines were omitted - set */
+ /* up to fill them in from the old file context */
+ if (!p_context && p_repl_lines == 1) {
+ p_repl_lines = 0;
+ p_max--;
+ }
+ fillsrc = 1;
+ filldst = repl_beginning+1;
+ fillcnt = p_repl_lines;
+ p_end = p_max;
+ }
+ else if (!p_context && fillcnt == 1) {
+ /* the first hunk was a null hunk with no context */
+ /* and we were expecting one line -- fix it up. */
+ while (filldst < p_end) {
+ p_line[filldst] = p_line[filldst+1];
+ p_Char[filldst] = p_Char[filldst+1];
+ p_len[filldst] = p_len[filldst+1];
+ filldst++;
+ }
+#if 0
+ repl_beginning--; /* this doesn't need to be fixed */
+#endif
+ p_end--;
+ p_first++; /* do append rather than insert */
+ fillcnt = 0;
+ p_ptrn_lines = 0;
+ }
+
+ if (diff_type == CONTEXT_DIFF &&
+ (fillcnt || (p_first > 1 && ptrn_copiable > 2*p_context)) ) {
+ if (verbose)
+ say4("%s\n%s\n%s\n",
+"(Fascinating--this is really a new-style context diff but without",
+"the telltale extra asterisks on the *** line that usually indicate",
+"the new style...)");
+ diff_type = NEW_CONTEXT_DIFF;
+ }
+
+ /* if there were omitted context lines, fill them in now */
+ if (fillcnt) {
+ p_bfake = filldst; /* remember where not to free() */
+ p_efake = filldst + fillcnt - 1;
+ while (fillcnt-- > 0) {
+ while (fillsrc <= p_end && p_Char[fillsrc] != ' ')
+ fillsrc++;
+ if (fillsrc > p_end)
+ fatal2("replacement text or line numbers mangled in hunk at line %ld\n",
+ p_hunk_beg);
+ p_line[filldst] = p_line[fillsrc];
+ p_Char[filldst] = p_Char[fillsrc];
+ p_len[filldst] = p_len[fillsrc];
+ fillsrc++; filldst++;
+ }
+ while (fillsrc <= p_end && fillsrc != repl_beginning &&
+ p_Char[fillsrc] != ' ')
+ fillsrc++;
+#ifdef DEBUGGING
+ if (debug & 64)
+ printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n",
+ fillsrc,filldst,repl_beginning,p_end+1);
+#endif
+ assert(fillsrc==p_end+1 || fillsrc==repl_beginning);
+ assert(filldst==p_end+1 || filldst==repl_beginning);
+ }
+ }
+ else if (diff_type == UNI_DIFF) {
+ long line_beginning = ftell(pfp);
+ /* file pos of the current line */
+ Reg4 LINENUM fillsrc; /* index of old lines */
+ Reg5 LINENUM filldst; /* index of new lines */
+ char ch;
+
+ ret = pgets(buf, sizeof buf, pfp);
+ p_input_line++;
+ if (ret == Nullch || strnNE(buf, "@@ -", 4)) {
+ next_intuit_at(line_beginning,p_input_line);
+ return FALSE;
+ }
+ s = buf+4;
+ if (!*s)
+ malformed ();
+ p_first = (LINENUM) atol(s);
+ while (isdigit(*s)) s++;
+ if (*s == ',') {
+ p_ptrn_lines = (LINENUM) atol(++s);
+ while (isdigit(*s)) s++;
+ } else
+ p_ptrn_lines = 1;
+ if (*s == ' ') s++;
+ if (*s != '+' || !*++s)
+ malformed ();
+ p_newfirst = (LINENUM) atol(s);
+ while (isdigit(*s)) s++;
+ if (*s == ',') {
+ p_repl_lines = (LINENUM) atol(++s);
+ while (isdigit(*s)) s++;
+ } else
+ p_repl_lines = 1;
+ if (*s == ' ') s++;
+ if (*s != '@')
+ malformed ();
+ if (!p_ptrn_lines)
+ p_first++; /* do append rather than insert */
+ p_max = p_ptrn_lines + p_repl_lines + 1;
+ while (p_max >= hunkmax)
+ grow_hunkmax();
+ fillsrc = 1;
+ filldst = fillsrc + p_ptrn_lines;
+ p_end = filldst + p_repl_lines;
+ Sprintf(buf,"*** %ld,%ld ****\n",p_first,p_first + p_ptrn_lines - 1);
+ p_line[0] = savestr(buf);
+ if (out_of_mem) {
+ p_end = -1;
+ return FALSE;
+ }
+ p_Char[0] = '*';
+ Sprintf(buf,"--- %ld,%ld ----\n",p_newfirst,p_newfirst+p_repl_lines-1);
+ p_line[filldst] = savestr(buf);
+ if (out_of_mem) {
+ p_end = 0;
+ return FALSE;
+ }
+ p_Char[filldst++] = '=';
+ p_context = 100;
+ context = 0;
+ p_hunk_beg = p_input_line + 1;
+ while (fillsrc <= p_ptrn_lines || filldst <= p_end) {
+ line_beginning = ftell(pfp);
+ ret = pgets(buf, sizeof buf, pfp);
+ p_input_line++;
+ if (ret == Nullch) {
+ if (p_max - filldst < 3)
+ Strcpy(buf, " \n"); /* assume blank lines got chopped */
+ else {
+ fatal1("unexpected end of file in patch\n");
+ }
+ }
+ if (*buf == '\t' || *buf == '\n') {
+ ch = ' '; /* assume the space got eaten */
+ s = savestr(buf);
+ }
+ else {
+ ch = *buf;
+ s = savestr(buf+1);
+ }
+ if (out_of_mem) {
+ while (--filldst > p_ptrn_lines)
+ free(p_line[filldst]);
+ p_end = fillsrc-1;
+ return FALSE;
+ }
+ switch (ch) {
+ case '-':
+ if (fillsrc > p_ptrn_lines) {
+ free(s);
+ p_end = filldst-1;
+ malformed ();
+ }
+ p_Char[fillsrc] = ch;
+ p_line[fillsrc] = s;
+ p_len[fillsrc++] = strlen(s);
+ break;
+ case '=':
+ ch = ' ';
+ /* FALL THROUGH */
+ case ' ':
+ if (fillsrc > p_ptrn_lines) {
+ free(s);
+ while (--filldst > p_ptrn_lines)
+ free(p_line[filldst]);
+ p_end = fillsrc-1;
+ malformed ();
+ }
+ context++;
+ p_Char[fillsrc] = ch;
+ p_line[fillsrc] = s;
+ p_len[fillsrc++] = strlen(s);
+ s = savestr(s);
+ if (out_of_mem) {
+ while (--filldst > p_ptrn_lines)
+ free(p_line[filldst]);
+ p_end = fillsrc-1;
+ return FALSE;
+ }
+ /* FALL THROUGH */
+ case '+':
+ if (filldst > p_end) {
+ free(s);
+ while (--filldst > p_ptrn_lines)
+ free(p_line[filldst]);
+ p_end = fillsrc-1;
+ malformed ();
+ }
+ p_Char[filldst] = ch;
+ p_line[filldst] = s;
+ p_len[filldst++] = strlen(s);
+ break;
+ default:
+ p_end = filldst;
+ malformed ();
+ }
+ if (ch != ' ' && context > 0) {
+ if (context < p_context)
+ p_context = context;
+ context = -1000;
+ }
+ }/* while */
+ }
+ else { /* normal diff--fake it up */
+ char hunk_type;
+ Reg3 int i;
+ LINENUM min, max;
+ long line_beginning = ftell(pfp);
+
+ p_context = 0;
+ ret = pgets(buf, sizeof buf, pfp);
+ p_input_line++;
+ if (ret == Nullch || !isdigit(*buf)) {
+ next_intuit_at(line_beginning,p_input_line);
+ return FALSE;
+ }
+ p_first = (LINENUM)atol(buf);
+ for (s=buf; isdigit(*s); s++) ;
+ if (*s == ',') {
+ p_ptrn_lines = (LINENUM)atol(++s) - p_first + 1;
+ while (isdigit(*s)) s++;
+ }
+ else
+ p_ptrn_lines = (*s != 'a');
+ hunk_type = *s;
+ if (hunk_type == 'a')
+ p_first++; /* do append rather than insert */
+ min = (LINENUM)atol(++s);
+ for (; isdigit(*s); s++) ;
+ if (*s == ',')
+ max = (LINENUM)atol(++s);
+ else
+ max = min;
+ if (hunk_type == 'd')
+ min++;
+ p_end = p_ptrn_lines + 1 + max - min + 1;
+ if (p_end > MAXHUNKSIZE)
+ fatal4("hunk too large (%ld lines) at line %ld: %s",
+ p_end, p_input_line, buf);
+ while (p_end >= hunkmax)
+ grow_hunkmax();
+ p_newfirst = min;
+ p_repl_lines = max - min + 1;
+ Sprintf(buf, "*** %ld,%ld\n", p_first, p_first + p_ptrn_lines - 1);
+ p_line[0] = savestr(buf);
+ if (out_of_mem) {
+ p_end = -1;
+ return FALSE;
+ }
+ p_Char[0] = '*';
+ for (i=1; i<=p_ptrn_lines; i++) {
+ ret = pgets(buf, sizeof buf, pfp);
+ p_input_line++;
+ if (ret == Nullch)
+ fatal2("unexpected end of file in patch at line %ld\n",
+ p_input_line);
+ if (*buf != '<')
+ fatal2("< expected at line %ld of patch\n", p_input_line);
+ p_line[i] = savestr(buf+2);
+ if (out_of_mem) {
+ p_end = i-1;
+ return FALSE;
+ }
+ p_len[i] = strlen(p_line[i]);
+ p_Char[i] = '-';
+ }
+ if (hunk_type == 'c') {
+ ret = pgets(buf, sizeof buf, pfp);
+ p_input_line++;
+ if (ret == Nullch)
+ fatal2("unexpected end of file in patch at line %ld\n",
+ p_input_line);
+ if (*buf != '-')
+ fatal2("--- expected at line %ld of patch\n", p_input_line);
+ }
+ Sprintf(buf, "--- %ld,%ld\n", min, max);
+ p_line[i] = savestr(buf);
+ if (out_of_mem) {
+ p_end = i-1;
+ return FALSE;
+ }
+ p_Char[i] = '=';
+ for (i++; i<=p_end; i++) {
+ ret = pgets(buf, sizeof buf, pfp);
+ p_input_line++;
+ if (ret == Nullch)
+ fatal2("unexpected end of file in patch at line %ld\n",
+ p_input_line);
+ if (*buf != '>')
+ fatal2("> expected at line %ld of patch\n", p_input_line);
+ p_line[i] = savestr(buf+2);
+ if (out_of_mem) {
+ p_end = i-1;
+ return FALSE;
+ }
+ p_len[i] = strlen(p_line[i]);
+ p_Char[i] = '+';
+ }
+ }
+ if (reverse) /* backwards patch? */
+ if (!pch_swap())
+ say1("Not enough memory to swap next hunk!\n");
+#ifdef DEBUGGING
+ if (debug & 2) {
+ int i;
+ char special;
+
+ for (i=0; i <= p_end; i++) {
+ if (i == p_ptrn_lines)
+ special = '^';
+ else
+ special = ' ';
+ fprintf(stderr, "%3d %c %c %s", i, p_Char[i], special, p_line[i]);
+ Fflush(stderr);
+ }
+ }
+#endif
+ if (p_end+1 < hunkmax) /* paranoia reigns supreme... */
+ p_Char[p_end+1] = '^'; /* add a stopper for apply_hunk */
+ return TRUE;
+}
+
+/* Input a line from the patch file, worrying about indentation. */
+
+char *
+pgets(bf,sz,fp)
+char *bf;
+int sz;
+FILE *fp;
+{
+ char *ret = fgets(bf, sz, fp);
+ Reg1 char *s;
+ Reg2 int indent = 0;
+
+ if (p_indent && ret != Nullch) {
+ for (s=buf;
+ indent < p_indent && (*s == ' ' || *s == '\t' || *s == 'X'); s++) {
+ if (*s == '\t')
+ indent += 8 - (indent % 7);
+ else
+ indent++;
+ }
+ if (buf != s)
+ Strcpy(buf, s);
+ }
+ return ret;
+}
+
+/* Reverse the old and new portions of the current hunk. */
+
+bool
+pch_swap()
+{
+ char **tp_line; /* the text of the hunk */
+ short *tp_len; /* length of each line */
+ char *tp_char; /* +, -, and ! */
+ Reg1 LINENUM i;
+ Reg2 LINENUM n;
+ bool blankline = FALSE;
+ Reg3 char *s;
+
+ i = p_first;
+ p_first = p_newfirst;
+ p_newfirst = i;
+
+ /* make a scratch copy */
+
+ tp_line = p_line;
+ tp_len = p_len;
+ tp_char = p_Char;
+ p_line = Null(char**); /* force set_hunkmax to allocate again */
+ p_len = Null(short*);
+ p_Char = Nullch;
+ set_hunkmax();
+ if (p_line == Null(char**) || p_len == Null(short*) || p_Char == Nullch) {
+#ifndef lint
+ if (p_line == Null(char**))
+ free((char*)p_line);
+ p_line = tp_line;
+ if (p_len == Null(short*))
+ free((char*)p_len);
+ p_len = tp_len;
+#endif
+ if (p_Char == Nullch)
+ free((char*)p_Char);
+ p_Char = tp_char;
+ return FALSE; /* not enough memory to swap hunk! */
+ }
+
+ /* now turn the new into the old */
+
+ i = p_ptrn_lines + 1;
+ if (tp_char[i] == '\n') { /* account for possible blank line */
+ blankline = TRUE;
+ i++;
+ }
+ if (p_efake >= 0) { /* fix non-freeable ptr range */
+ if (p_efake <= i)
+ n = p_end - i + 1;
+ else
+ n = -i;
+ p_efake += n;
+ p_bfake += n;
+ }
+ for (n=0; i <= p_end; i++,n++) {
+ p_line[n] = tp_line[i];
+ p_Char[n] = tp_char[i];
+ if (p_Char[n] == '+')
+ p_Char[n] = '-';
+ p_len[n] = tp_len[i];
+ }
+ if (blankline) {
+ i = p_ptrn_lines + 1;
+ p_line[n] = tp_line[i];
+ p_Char[n] = tp_char[i];
+ p_len[n] = tp_len[i];
+ n++;
+ }
+ assert(p_Char[0] == '=');
+ p_Char[0] = '*';
+ for (s=p_line[0]; *s; s++)
+ if (*s == '-')
+ *s = '*';
+
+ /* now turn the old into the new */
+
+ assert(tp_char[0] == '*');
+ tp_char[0] = '=';
+ for (s=tp_line[0]; *s; s++)
+ if (*s == '*')
+ *s = '-';
+ for (i=0; n <= p_end; i++,n++) {
+ p_line[n] = tp_line[i];
+ p_Char[n] = tp_char[i];
+ if (p_Char[n] == '-')
+ p_Char[n] = '+';
+ p_len[n] = tp_len[i];
+ }
+ assert(i == p_ptrn_lines + 1);
+ i = p_ptrn_lines;
+ p_ptrn_lines = p_repl_lines;
+ p_repl_lines = i;
+#ifndef lint
+ if (tp_line == Null(char**))
+ free((char*)tp_line);
+ if (tp_len == Null(short*))
+ free((char*)tp_len);
+#endif
+ if (tp_char == Nullch)
+ free((char*)tp_char);
+ return TRUE;
+}
+
+/* Return the specified line position in the old file of the old context. */
+
+LINENUM
+pch_first()
+{
+ return p_first;
+}
+
+/* Return the number of lines of old context. */
+
+LINENUM
+pch_ptrn_lines()
+{
+ return p_ptrn_lines;
+}
+
+/* Return the probable line position in the new file of the first line. */
+
+LINENUM
+pch_newfirst()
+{
+ return p_newfirst;
+}
+
+/* Return the number of lines in the replacement text including context. */
+
+LINENUM
+pch_repl_lines()
+{
+ return p_repl_lines;
+}
+
+/* Return the number of lines in the whole hunk. */
+
+LINENUM
+pch_end()
+{
+ return p_end;
+}
+
+/* Return the number of context lines before the first changed line. */
+
+LINENUM
+pch_context()
+{
+ return p_context;
+}
+
+/* Return the length of a particular patch line. */
+
+short
+pch_line_len(line)
+LINENUM line;
+{
+ return p_len[line];
+}
+
+/* Return the control character (+, -, *, !, etc) for a patch line. */
+
+char
+pch_char(line)
+LINENUM line;
+{
+ return p_Char[line];
+}
+
+/* Return a pointer to a particular patch line. */
+
+char *
+pfetch(line)
+LINENUM line;
+{
+ return p_line[line];
+}
+
+/* Return where in the patch file this hunk began, for error messages. */
+
+LINENUM
+pch_hunk_beg()
+{
+ return p_hunk_beg;
+}
+
+/* Apply an ed script by feeding ed itself. */
+
+void
+do_ed_script()
+{
+ Reg1 char *t;
+ Reg2 long beginning_of_this_line;
+ Reg3 bool this_line_is_command = FALSE;
+ Reg4 FILE *pipefp;
+
+ if (!skip_rest_of_patch) {
+ Unlink(TMPOUTNAME);
+ copy_file(filearg[0], TMPOUTNAME);
+ if (verbose)
+ Sprintf(buf, "/bin/ed %s", TMPOUTNAME);
+ else
+ Sprintf(buf, "/bin/ed - %s", TMPOUTNAME);
+ pipefp = popen(buf, "w");
+ }
+ for (;;) {
+ beginning_of_this_line = ftell(pfp);
+ if (pgets(buf, sizeof buf, pfp) == Nullch) {
+ next_intuit_at(beginning_of_this_line,p_input_line);
+ break;
+ }
+ p_input_line++;
+ for (t=buf; isdigit(*t) || *t == ','; t++) ;
+ this_line_is_command = (isdigit(*buf) &&
+ (*t == 'd' || *t == 'c' || *t == 'a') );
+ if (this_line_is_command) {
+ if (!skip_rest_of_patch)
+ fputs(buf, pipefp);
+ if (*t != 'd') {
+ while (pgets(buf, sizeof buf, pfp) != Nullch) {
+ p_input_line++;
+ if (!skip_rest_of_patch)
+ fputs(buf, pipefp);
+ if (strEQ(buf, ".\n"))
+ break;
+ }
+ }
+ }
+ else {
+ next_intuit_at(beginning_of_this_line,p_input_line);
+ break;
+ }
+ }
+ if (skip_rest_of_patch)
+ return;
+ fprintf(pipefp, "w\n");
+ fprintf(pipefp, "q\n");
+ Fflush(pipefp);
+ Pclose(pipefp);
+ ignore_signals();
+ if (move_file(TMPOUTNAME, outname) < 0) {
+ toutkeep = TRUE;
+ chmod(TMPOUTNAME, filemode);
+ }
+ else
+ chmod(outname, filemode);
+ set_signals(1);
+}
diff --git a/gnu/usr.bin/patch/pch.h b/gnu/usr.bin/patch/pch.h
new file mode 100644
index 0000000..97a5b28
--- /dev/null
+++ b/gnu/usr.bin/patch/pch.h
@@ -0,0 +1,36 @@
+/* $Header: pch.h,v 2.0.1.1 87/01/30 22:47:16 lwall Exp $
+ *
+ * $Log: pch.h,v $
+ * Revision 2.0.1.1 87/01/30 22:47:16 lwall
+ * Added do_ed_script().
+ *
+ * Revision 2.0 86/09/17 15:39:57 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+EXT FILE *pfp INIT(Nullfp); /* patch file pointer */
+
+void re_patch();
+void open_patch_file();
+void set_hunkmax();
+void grow_hunkmax();
+bool there_is_another_patch();
+int intuit_diff_type();
+void next_intuit_at();
+void skip_to();
+bool another_hunk();
+bool pch_swap();
+char *pfetch();
+short pch_line_len();
+LINENUM pch_first();
+LINENUM pch_ptrn_lines();
+LINENUM pch_newfirst();
+LINENUM pch_repl_lines();
+LINENUM pch_end();
+LINENUM pch_context();
+LINENUM pch_hunk_beg();
+char pch_char();
+char *pfetch();
+char *pgets();
+void do_ed_script();
diff --git a/gnu/usr.bin/patch/util.c b/gnu/usr.bin/patch/util.c
new file mode 100644
index 0000000..ecb85ff
--- /dev/null
+++ b/gnu/usr.bin/patch/util.c
@@ -0,0 +1,433 @@
+#include "EXTERN.h"
+#include "common.h"
+#include "INTERN.h"
+#include "util.h"
+#include "backupfile.h"
+
+void my_exit();
+
+#ifndef HAVE_STRERROR
+static char *
+private_strerror (errnum)
+ int errnum;
+{
+ extern char *sys_errlist[];
+ extern int sys_nerr;
+
+ if (errnum > 0 && errnum <= sys_nerr)
+ return sys_errlist[errnum];
+ return "Unknown system error";
+}
+#define strerror private_strerror
+#endif /* !HAVE_STRERROR */
+
+/* Rename a file, copying it if necessary. */
+
+int
+move_file(from,to)
+char *from, *to;
+{
+ char bakname[512];
+ Reg1 char *s;
+ Reg2 int i;
+ Reg3 int fromfd;
+
+ /* to stdout? */
+
+ if (strEQ(to, "-")) {
+#ifdef DEBUGGING
+ if (debug & 4)
+ say2("Moving %s to stdout.\n", from);
+#endif
+ fromfd = open(from, 0);
+ if (fromfd < 0)
+ pfatal2("internal error, can't reopen %s", from);
+ while ((i=read(fromfd, buf, sizeof buf)) > 0)
+ if (write(1, buf, i) != 1)
+ pfatal1("write failed");
+ Close(fromfd);
+ return 0;
+ }
+
+ if (origprae) {
+ Strcpy(bakname, origprae);
+ Strcat(bakname, to);
+ } else {
+#ifndef NODIR
+ char *backupname = find_backup_file_name(to);
+ if (backupname == (char *) 0)
+ fatal1("out of memory\n");
+ Strcpy(bakname, backupname);
+ free(backupname);
+#else /* NODIR */
+ Strcpy(bakname, to);
+ Strcat(bakname, simple_backup_suffix);
+#endif /* NODIR */
+ }
+
+ if (stat(to, &filestat) == 0) { /* output file exists */
+ dev_t to_device = filestat.st_dev;
+ ino_t to_inode = filestat.st_ino;
+ char *simplename = bakname;
+
+ for (s=bakname; *s; s++) {
+ if (*s == '/')
+ simplename = s+1;
+ }
+ /* Find a backup name that is not the same file.
+ Change the first lowercase char into uppercase;
+ if that isn't sufficient, chop off the first char and try again. */
+ while (stat(bakname, &filestat) == 0 &&
+ to_device == filestat.st_dev && to_inode == filestat.st_ino) {
+ /* Skip initial non-lowercase chars. */
+ for (s=simplename; *s && !islower(*s); s++) ;
+ if (*s)
+ *s = toupper(*s);
+ else
+ Strcpy(simplename, simplename+1);
+ }
+ while (unlink(bakname) >= 0) ; /* while() is for benefit of Eunice */
+#ifdef DEBUGGING
+ if (debug & 4)
+ say3("Moving %s to %s.\n", to, bakname);
+#endif
+ if (rename(to, bakname) < 0) {
+ say4("Can't backup %s, output is in %s: %s\n", to, from,
+ strerror(errno));
+ return -1;
+ }
+ while (unlink(to) >= 0) ;
+ }
+#ifdef DEBUGGING
+ if (debug & 4)
+ say3("Moving %s to %s.\n", from, to);
+#endif
+ if (rename(from, to) < 0) { /* different file system? */
+ Reg4 int tofd;
+
+ tofd = creat(to, 0666);
+ if (tofd < 0) {
+ say4("Can't create %s, output is in %s: %s\n",
+ to, from, strerror(errno));
+ return -1;
+ }
+ fromfd = open(from, 0);
+ if (fromfd < 0)
+ pfatal2("internal error, can't reopen %s", from);
+ while ((i=read(fromfd, buf, sizeof buf)) > 0)
+ if (write(tofd, buf, i) != i)
+ pfatal1("write failed");
+ Close(fromfd);
+ Close(tofd);
+ }
+ Unlink(from);
+ return 0;
+}
+
+/* Copy a file. */
+
+void
+copy_file(from,to)
+char *from, *to;
+{
+ Reg3 int tofd;
+ Reg2 int fromfd;
+ Reg1 int i;
+
+ tofd = creat(to, 0666);
+ if (tofd < 0)
+ pfatal2("can't create %s", to);
+ fromfd = open(from, 0);
+ if (fromfd < 0)
+ pfatal2("internal error, can't reopen %s", from);
+ while ((i=read(fromfd, buf, sizeof buf)) > 0)
+ if (write(tofd, buf, i) != i)
+ pfatal2("write to %s failed", to);
+ Close(fromfd);
+ Close(tofd);
+}
+
+/* Allocate a unique area for a string. */
+
+char *
+savestr(s)
+Reg1 char *s;
+{
+ Reg3 char *rv;
+ Reg2 char *t;
+
+ if (!s)
+ s = "Oops";
+ t = s;
+ while (*t++);
+ rv = malloc((MEM) (t - s));
+ if (rv == Nullch) {
+ if (using_plan_a)
+ out_of_mem = TRUE;
+ else
+ fatal1("out of memory\n");
+ }
+ else {
+ t = rv;
+ while (*t++ = *s++);
+ }
+ return rv;
+}
+
+#if defined(lint) && defined(CANVARARG)
+
+/*VARARGS ARGSUSED*/
+say(pat) char *pat; { ; }
+/*VARARGS ARGSUSED*/
+fatal(pat) char *pat; { ; }
+/*VARARGS ARGSUSED*/
+pfatal(pat) char *pat; { ; }
+/*VARARGS ARGSUSED*/
+ask(pat) char *pat; { ; }
+
+#else
+
+/* Vanilla terminal output (buffered). */
+
+void
+say(pat,arg1,arg2,arg3)
+char *pat;
+long arg1,arg2,arg3;
+{
+ fprintf(stderr, pat, arg1, arg2, arg3);
+ Fflush(stderr);
+}
+
+/* Terminal output, pun intended. */
+
+void /* very void */
+fatal(pat,arg1,arg2,arg3)
+char *pat;
+long arg1,arg2,arg3;
+{
+ fprintf(stderr, "patch: **** ");
+ fprintf(stderr, pat, arg1, arg2, arg3);
+ my_exit(1);
+}
+
+/* Say something from patch, something from the system, then silence . . . */
+
+void /* very void */
+pfatal(pat,arg1,arg2,arg3)
+char *pat;
+long arg1,arg2,arg3;
+{
+ int errnum = errno;
+
+ fprintf(stderr, "patch: **** ");
+ fprintf(stderr, pat, arg1, arg2, arg3);
+ fprintf(stderr, ": %s\n", strerror(errnum));
+ my_exit(1);
+}
+
+/* Get a response from the user, somehow or other. */
+
+void
+ask(pat,arg1,arg2,arg3)
+char *pat;
+long arg1,arg2,arg3;
+{
+ int ttyfd;
+ int r;
+ bool tty2 = isatty(2);
+
+ Sprintf(buf, pat, arg1, arg2, arg3);
+ Fflush(stderr);
+ write(2, buf, strlen(buf));
+ if (tty2) { /* might be redirected to a file */
+ r = read(2, buf, sizeof buf);
+ }
+ else if (isatty(1)) { /* this may be new file output */
+ Fflush(stdout);
+ write(1, buf, strlen(buf));
+ r = read(1, buf, sizeof buf);
+ }
+ else if ((ttyfd = open("/dev/tty", 2)) >= 0 && isatty(ttyfd)) {
+ /* might be deleted or unwriteable */
+ write(ttyfd, buf, strlen(buf));
+ r = read(ttyfd, buf, sizeof buf);
+ Close(ttyfd);
+ }
+ else if (isatty(0)) { /* this is probably patch input */
+ Fflush(stdin);
+ write(0, buf, strlen(buf));
+ r = read(0, buf, sizeof buf);
+ }
+ else { /* no terminal at all--default it */
+ buf[0] = '\n';
+ r = 1;
+ }
+ if (r <= 0)
+ buf[0] = 0;
+ else
+ buf[r] = '\0';
+ if (!tty2)
+ say1(buf);
+}
+#endif /* lint */
+
+/* How to handle certain events when not in a critical region. */
+
+void
+set_signals(reset)
+int reset;
+{
+#ifndef lint
+ static RETSIGTYPE (*hupval)(),(*intval)();
+
+ if (!reset) {
+ hupval = signal(SIGHUP, SIG_IGN);
+ if (hupval != SIG_IGN)
+ hupval = (RETSIGTYPE(*)())my_exit;
+ intval = signal(SIGINT, SIG_IGN);
+ if (intval != SIG_IGN)
+ intval = (RETSIGTYPE(*)())my_exit;
+ }
+ Signal(SIGHUP, hupval);
+ Signal(SIGINT, intval);
+#endif
+}
+
+/* How to handle certain events when in a critical region. */
+
+void
+ignore_signals()
+{
+#ifndef lint
+ Signal(SIGHUP, SIG_IGN);
+ Signal(SIGINT, SIG_IGN);
+#endif
+}
+
+/* Make sure we'll have the directories to create a file.
+ If `striplast' is TRUE, ignore the last element of `filename'. */
+
+void
+makedirs(filename,striplast)
+Reg1 char *filename;
+bool striplast;
+{
+ char tmpbuf[256];
+ Reg2 char *s = tmpbuf;
+ char *dirv[20]; /* Point to the NULs between elements. */
+ Reg3 int i;
+ Reg4 int dirvp = 0; /* Number of finished entries in dirv. */
+
+ /* Copy `filename' into `tmpbuf' with a NUL instead of a slash
+ between the directories. */
+ while (*filename) {
+ if (*filename == '/') {
+ filename++;
+ dirv[dirvp++] = s;
+ *s++ = '\0';
+ }
+ else {
+ *s++ = *filename++;
+ }
+ }
+ *s = '\0';
+ dirv[dirvp] = s;
+ if (striplast)
+ dirvp--;
+ if (dirvp < 0)
+ return;
+
+ strcpy(buf, "mkdir");
+ s = buf;
+ for (i=0; i<=dirvp; i++) {
+ struct stat sbuf;
+
+ if (stat(tmpbuf, &sbuf) && errno == ENOENT) {
+ while (*s) s++;
+ *s++ = ' ';
+ strcpy(s, tmpbuf);
+ }
+ *dirv[i] = '/';
+ }
+ if (s != buf)
+ system(buf);
+}
+
+/* Make filenames more reasonable. */
+
+char *
+fetchname(at,strip_leading,assume_exists)
+char *at;
+int strip_leading;
+int assume_exists;
+{
+ char *fullname;
+ char *name;
+ Reg1 char *t;
+ char tmpbuf[200];
+ int sleading = strip_leading;
+
+ if (!at)
+ return Nullch;
+ while (isspace(*at))
+ at++;
+#ifdef DEBUGGING
+ if (debug & 128)
+ say4("fetchname %s %d %d\n",at,strip_leading,assume_exists);
+#endif
+ if (strnEQ(at, "/dev/null", 9)) /* so files can be created by diffing */
+ return Nullch; /* against /dev/null. */
+ name = fullname = t = savestr(at);
+
+ /* Strip off up to `sleading' leading slashes and null terminate. */
+ for (; *t && !isspace(*t); t++)
+ if (*t == '/')
+ if (--sleading >= 0)
+ name = t+1;
+ *t = '\0';
+
+ /* If no -p option was given (957 is the default value!),
+ we were given a relative pathname,
+ and the leading directories that we just stripped off all exist,
+ put them back on. */
+ if (strip_leading == 957 && name != fullname && *fullname != '/') {
+ name[-1] = '\0';
+ if (stat(fullname, &filestat) == 0 && S_ISDIR (filestat.st_mode)) {
+ name[-1] = '/';
+ name=fullname;
+ }
+ }
+
+ name = savestr(name);
+ free(fullname);
+
+ if (stat(name, &filestat) && !assume_exists) {
+ char *filebase = basename(name);
+ int pathlen = filebase - name;
+
+ /* Put any leading path into `tmpbuf'. */
+ strncpy(tmpbuf, name, pathlen);
+
+#define try(f, a1, a2) (Sprintf(tmpbuf + pathlen, f, a1, a2), stat(tmpbuf, &filestat) == 0)
+ if ( try("RCS/%s%s", filebase, RCSSUFFIX)
+ || try("RCS/%s" , filebase, 0)
+ || try( "%s%s", filebase, RCSSUFFIX)
+ || try("SCCS/%s%s", SCCSPREFIX, filebase)
+ || try( "%s%s", SCCSPREFIX, filebase))
+ return name;
+ free(name);
+ name = Nullch;
+ }
+
+ return name;
+}
+
+char *
+xmalloc (size)
+ unsigned size;
+{
+ register char *p = (char *) malloc (size);
+ if (!p)
+ fatal("out of memory");
+ return p;
+}
diff --git a/gnu/usr.bin/patch/util.h b/gnu/usr.bin/patch/util.h
new file mode 100644
index 0000000..d8e46bb
--- /dev/null
+++ b/gnu/usr.bin/patch/util.h
@@ -0,0 +1,88 @@
+/* $Header: util.h,v 2.0 86/09/17 15:40:06 lwall Exp $
+ *
+ * $Log: util.h,v $
+ * Revision 2.0 86/09/17 15:40:06 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+/* and for those machine that can't handle a variable argument list */
+
+#ifdef CANVARARG
+
+#define say1 say
+#define say2 say
+#define say3 say
+#define say4 say
+#define ask1 ask
+#define ask2 ask
+#define ask3 ask
+#define ask4 ask
+#define fatal1 fatal
+#define fatal2 fatal
+#define fatal3 fatal
+#define fatal4 fatal
+#define pfatal1 pfatal
+#define pfatal2 pfatal
+#define pfatal3 pfatal
+#define pfatal4 pfatal
+
+#else /* hope they allow multi-line macro actual arguments */
+
+#ifdef lint
+
+#define say1(a) say(a, 0, 0, 0)
+#define say2(a,b) say(a, (b)==(b), 0, 0)
+#define say3(a,b,c) say(a, (b)==(b), (c)==(c), 0)
+#define say4(a,b,c,d) say(a, (b)==(b), (c)==(c), (d)==(d))
+#define ask1(a) ask(a, 0, 0, 0)
+#define ask2(a,b) ask(a, (b)==(b), 0, 0)
+#define ask3(a,b,c) ask(a, (b)==(b), (c)==(c), 0)
+#define ask4(a,b,c,d) ask(a, (b)==(b), (c)==(c), (d)==(d))
+#define fatal1(a) fatal(a, 0, 0, 0)
+#define fatal2(a,b) fatal(a, (b)==(b), 0, 0)
+#define fatal3(a,b,c) fatal(a, (b)==(b), (c)==(c), 0)
+#define fatal4(a,b,c,d) fatal(a, (b)==(b), (c)==(c), (d)==(d))
+#define pfatal1(a) pfatal(a, 0, 0, 0)
+#define pfatal2(a,b) pfatal(a, (b)==(b), 0, 0)
+#define pfatal3(a,b,c) pfatal(a, (b)==(b), (c)==(c), 0)
+#define pfatal4(a,b,c,d) pfatal(a, (b)==(b), (c)==(c), (d)==(d))
+
+#else /* lint */
+ /* if this doesn't work, try defining CANVARARG above */
+#define say1(a) say(a, Nullch, Nullch, Nullch)
+#define say2(a,b) say(a, b, Nullch, Nullch)
+#define say3(a,b,c) say(a, b, c, Nullch)
+#define say4 say
+#define ask1(a) ask(a, Nullch, Nullch, Nullch)
+#define ask2(a,b) ask(a, b, Nullch, Nullch)
+#define ask3(a,b,c) ask(a, b, c, Nullch)
+#define ask4 ask
+#define fatal1(a) fatal(a, Nullch, Nullch, Nullch)
+#define fatal2(a,b) fatal(a, b, Nullch, Nullch)
+#define fatal3(a,b,c) fatal(a, b, c, Nullch)
+#define fatal4 fatal
+#define pfatal1(a) pfatal(a, Nullch, Nullch, Nullch)
+#define pfatal2(a,b) pfatal(a, b, Nullch, Nullch)
+#define pfatal3(a,b,c) pfatal(a, b, c, Nullch)
+#define pfatal4 pfatal
+
+#endif /* lint */
+
+/* if neither of the above work, join all multi-line macro calls. */
+#endif
+
+EXT char serrbuf[BUFSIZ]; /* buffer for stderr */
+
+char *fetchname();
+int move_file();
+void copy_file();
+void say();
+void fatal();
+void pfatal();
+void ask();
+char *savestr();
+void set_signals();
+void ignore_signals();
+void makedirs();
+char *basename();
diff --git a/gnu/usr.bin/patch/version.c b/gnu/usr.bin/patch/version.c
new file mode 100644
index 0000000..f0b5223
--- /dev/null
+++ b/gnu/usr.bin/patch/version.c
@@ -0,0 +1,25 @@
+/* $Header: version.c,v 2.0 86/09/17 15:40:11 lwall Exp $
+ *
+ * $Log: version.c,v $
+ * Revision 2.0 86/09/17 15:40:11 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+#include "EXTERN.h"
+#include "common.h"
+#include "util.h"
+#include "INTERN.h"
+#include "patchlevel.h"
+#include "version.h"
+
+void my_exit();
+
+/* Print out the version number and die. */
+
+void
+version()
+{
+ fprintf(stderr, "Patch version %s\n", PATCH_VERSION);
+ my_exit(0);
+}
diff --git a/gnu/usr.bin/patch/version.h b/gnu/usr.bin/patch/version.h
new file mode 100644
index 0000000..08fe68d
--- /dev/null
+++ b/gnu/usr.bin/patch/version.h
@@ -0,0 +1,9 @@
+/* $Header: version.h,v 2.0 86/09/17 15:40:14 lwall Exp $
+ *
+ * $Log: version.h,v $
+ * Revision 2.0 86/09/17 15:40:14 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+void version();
diff --git a/gnu/usr.bin/perl/Artistic b/gnu/usr.bin/perl/Artistic
new file mode 100644
index 0000000..fbf7989
--- /dev/null
+++ b/gnu/usr.bin/perl/Artistic
@@ -0,0 +1,117 @@
+
+
+
+
+ The "Artistic License"
+
+ Preamble
+
+The intent of this document is to state the conditions under which a
+Package may be copied, such that the Copyright Holder maintains some
+semblance of artistic control over the development of the package,
+while giving the users of the package the right to use and distribute
+the Package in a more-or-less customary fashion, plus the right to make
+reasonable modifications.
+
+Definitions:
+
+ "Package" refers to the collection of files distributed by the
+ Copyright Holder, and derivatives of that collection of files
+ created through textual modification.
+
+ "Standard Version" refers to such a Package if it has not been
+ modified, or has been modified in accordance with the wishes
+ of the Copyright Holder.
+
+ "Copyright Holder" is whoever is named in the copyright or
+ copyrights for the package.
+
+ "You" is you, if you're thinking about copying or distributing
+ this Package.
+
+ "Reasonable copying fee" is whatever you can justify on the
+ basis of media cost, duplication charges, time of people involved,
+ and so on. (You will not be required to justify it to the
+ Copyright Holder, but only to the computing community at large
+ as a market that must bear the fee.)
+
+ "Freely Available" means that no fee is charged for the item
+ itself, though there may be fees involved in handling the item.
+ It also means that recipients of the item may redistribute it
+ under the same conditions they received it.
+
+1. You may make and give away verbatim copies of the source form of the
+Standard Version of this Package without restriction, provided that you
+duplicate all of the original copyright notices and associated disclaimers.
+
+2. You may apply bug fixes, portability fixes and other modifications
+derived from the Public Domain or from the Copyright Holder. A Package
+modified in such a way shall still be considered the Standard Version.
+
+3. You may otherwise modify your copy of this Package in any way, provided
+that you insert a prominent notice in each changed file stating how and
+when you changed that file, and provided that you do at least ONE of the
+following:
+
+ a) place your modifications in the Public Domain or otherwise make them
+ Freely Available, such as by posting said modifications to Usenet or
+ an equivalent medium, or placing the modifications on a major archive
+ site such as uunet.uu.net, or by allowing the Copyright Holder to include
+ your modifications in the Standard Version of the Package.
+
+ b) use the modified Package only within your corporation or organization.
+
+ c) rename any non-standard executables so the names do not conflict
+ with standard executables, which must also be provided, and provide
+ a separate manual page for each non-standard executable that clearly
+ documents how it differs from the Standard Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+4. You may distribute the programs of this Package in object code or
+executable form, provided that you do at least ONE of the following:
+
+ a) distribute a Standard Version of the executables and library files,
+ together with instructions (in the manual page or equivalent) on where
+ to get the Standard Version.
+
+ b) accompany the distribution with the machine-readable source of
+ the Package with your modifications.
+
+ c) accompany any non-standard executables with their corresponding
+ Standard Version executables, giving the non-standard executables
+ non-standard names, and clearly documenting the differences in manual
+ pages (or equivalent), together with instructions on where to get
+ the Standard Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+5. You may charge a reasonable copying fee for any distribution of this
+Package. You may charge any fee you choose for support of this Package.
+You may not charge a fee for this Package itself. However,
+you may distribute this Package in aggregate with other (possibly
+commercial) programs as part of a larger (possibly commercial) software
+distribution provided that you do not advertise this Package as a
+product of your own.
+
+6. The scripts and library files supplied as input to or produced as
+output from the programs of this Package do not automatically fall
+under the copyright of this Package, but belong to whomever generated
+them, and may be sold commercially, and may be aggregated with this
+Package.
+
+7. C subroutines supplied by you and linked into this Package in order
+to emulate subroutines and variables of the language defined by this
+Package shall not be considered part of this Package, but are the
+equivalent of input as in Paragraph 6, provided these subroutines do
+not change the language in any way that would cause it to fail the
+regression tests for the language.
+
+8. The name of the Copyright Holder may not be used to endorse or promote
+products derived from this software without specific prior written permission.
+
+9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+ The End
diff --git a/gnu/usr.bin/perl/Copying b/gnu/usr.bin/perl/Copying
new file mode 100644
index 0000000..3c68f02
--- /dev/null
+++ b/gnu/usr.bin/perl/Copying
@@ -0,0 +1,248 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 1, February 1989
+
+ Copyright (C) 1989 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The license agreements of most software companies try to keep users
+at the mercy of those companies. By contrast, our General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. The
+General Public License applies to the Free Software Foundation's
+software and to any other program whose authors commit to using it.
+You can use it for your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Specifically, the General Public License is designed to make
+sure that you have the freedom to give away or sell copies of free
+software, that you receive source code or can get it if you want it,
+that you can change the software or use pieces of it in new free
+programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of a such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must tell them their rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any program or other work which
+contains a notice placed by the copyright holder saying it may be
+distributed under the terms of this General Public License. The
+"Program", below, refers to any such program or work, and a "work based
+on the Program" means either the Program or any work containing the
+Program or a portion of it, either verbatim or with modifications. Each
+licensee is addressed as "you".
+
+ 1. You may copy and distribute verbatim copies of the Program's source
+code as you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this
+General Public License and to the absence of any warranty; and give any
+other recipients of the Program a copy of this General Public License
+along with the Program. You may charge a fee for the physical act of
+transferring a copy.
+
+ 2. You may modify your copy or copies of the Program or any portion of
+it, and copy and distribute such modifications under the terms of Paragraph
+1 above, provided that you also do the following:
+
+ a) cause the modified files to carry prominent notices stating that
+ you changed the files and the date of any change; and
+
+ b) cause the whole of any work that you distribute or publish, that
+ in whole or in part contains the Program or any part thereof, either
+ with or without modifications, to be licensed at no charge to all
+ third parties under the terms of this General Public License (except
+ that you may choose to grant warranty protection to some or all
+ third parties, at your option).
+
+ c) If the modified program normally reads commands interactively when
+ run, you must cause it, when started running for such interactive use
+ in the simplest and most usual way, to print or display an
+ announcement including an appropriate copyright notice and a notice
+ that there is no warranty (or else, saying that you provide a
+ warranty) and that users may redistribute the program under these
+ conditions, and telling the user how to view a copy of this General
+ Public License.
+
+ d) You may charge a fee for the physical act of transferring a
+ copy, and you may at your option offer warranty protection in
+ exchange for a fee.
+
+Mere aggregation of another independent work with the Program (or its
+derivative) on a volume of a storage or distribution medium does not bring
+the other work under the scope of these terms.
+
+ 3. You may copy and distribute the Program (or a portion or derivative of
+it, under Paragraph 2) in object code or executable form under the terms of
+Paragraphs 1 and 2 above provided that you also do one of the following:
+
+ a) accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of
+ Paragraphs 1 and 2 above; or,
+
+ b) accompany it with a written offer, valid for at least three
+ years, to give any third party free (except for a nominal charge
+ for the cost of distribution) a complete machine-readable copy of the
+ corresponding source code, to be distributed under the terms of
+ Paragraphs 1 and 2 above; or,
+
+ c) accompany it with the information you received as to where the
+ corresponding source code may be obtained. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form alone.)
+
+Source code for a work means the preferred form of the work for making
+modifications to it. For an executable file, complete source code means
+all the source code for all modules it contains; but, as a special
+exception, it need not include source code for modules which are standard
+libraries that accompany the operating system on which the executable
+file runs, or for standard header files or definitions files that
+accompany that operating system.
+
+ 4. You may not copy, modify, sublicense, distribute or transfer the
+Program except as expressly provided under this General Public License.
+Any attempt otherwise to copy, modify, sublicense, distribute or transfer
+the Program is void, and will automatically terminate your rights to use
+the Program under this License. However, parties who have received
+copies, or rights to use copies, from you under this General Public
+License will not have their licenses terminated so long as such parties
+remain in full compliance.
+
+ 5. By copying, distributing or modifying the Program (or any work based
+on the Program) you indicate your acceptance of this license to do so,
+and all its terms and conditions.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the original
+licensor to copy, distribute or modify the Program subject to these
+terms and conditions. You may not impose any further restrictions on the
+recipients' exercise of the rights granted herein.
+
+ 7. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of the license which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+the license, you may choose any version ever published by the Free Software
+Foundation.
+
+ 8. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to humanity, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+ To do so, attach the following notices to the program. It is safest to
+attach them to the start of each source file to most effectively convey
+the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19xx name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the
+appropriate parts of the General Public License. Of course, the
+commands you use may be called something other than `show w' and `show
+c'; they could even be mouse-clicks or menu items--whatever suits your
+program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ program `Gnomovision' (a program to direct compilers to make passes
+ at assemblers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/gnu/usr.bin/perl/Makefile b/gnu/usr.bin/perl/Makefile
new file mode 100644
index 0000000..3ef6485
--- /dev/null
+++ b/gnu/usr.bin/perl/Makefile
@@ -0,0 +1,10 @@
+#
+# Bmake file for perl 4.036
+#
+# Note: I'm not sure what to do with c2ph located in misc...
+#
+
+SUBDIR= perl tperl sperl lib x2p
+
+.include <bsd.subdir.mk>
+
diff --git a/gnu/usr.bin/perl/Makefile.inc b/gnu/usr.bin/perl/Makefile.inc
new file mode 100644
index 0000000..c2f9b44
--- /dev/null
+++ b/gnu/usr.bin/perl/Makefile.inc
@@ -0,0 +1,5 @@
+
+
+BINDIR?= /usr/local/bin
+MANDIR?= /usr/local/man/man
+
diff --git a/gnu/usr.bin/perl/README b/gnu/usr.bin/perl/README
new file mode 100644
index 0000000..c52c7f4
--- /dev/null
+++ b/gnu/usr.bin/perl/README
@@ -0,0 +1,195 @@
+
+ Perl Kit, Version 4.0
+
+ Copyright (c) 1989,1990,1991, Larry Wall
+ All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of either:
+
+ a) the GNU General Public License as published by the Free
+ Software Foundation; either version 1, or (at your option) any
+ later version, or
+
+ b) the "Artistic License" which comes with this Kit.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See either
+ the GNU General Public License or the Artistic License for more details.
+
+ You should have received a copy of the Artistic License with this
+ Kit, in the file named "Artistic". If not, I'll be glad to provide one.
+
+ You should also have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ For those of you that choose to use the GNU General Public License,
+ my interpretation of the GNU General Public License is that no Perl
+ script falls under the terms of the GPL unless you explicitly put
+ said script under the terms of the GPL yourself. Furthermore, any
+ object code linked with uperl.o does not automatically fall under the
+ terms of the GPL, provided such object code only adds definitions
+ of subroutines and variables, and does not otherwise impair the
+ resulting interpreter from executing any standard Perl script. I
+ consider linking in C subroutines in this manner to be the moral
+ equivalent of defining subroutines in the Perl language itself. You
+ may sell such an object file as proprietary provided that you provide
+ or offer to provide the Perl source, as specified by the GNU General
+ Public License. (This is merely an alternate way of specifying input
+ to the program.) You may also sell a binary produced by the dumping of
+ a running Perl script that belongs to you, provided that you provide or
+ offer to provide the Perl source as specified by the GPL. (The
+ fact that a Perl interpreter and your code are in the same binary file
+ is, in this case, a form of mere aggregation.) This is my interpretation
+ of the GPL. If you still have concerns or difficulties understanding
+ my intent, feel free to contact me. Of course, the Artistic License
+ spells all this out for your protection, so you may prefer to use that.
+
+--------------------------------------------------------------------------
+
+Perl is a language that combines some of the features of C, sed, awk and shell.
+See the manual page for more hype. There's also a Nutshell Handbook published
+by O'Reilly & Assoc. Their U.S. number is 1-800-338-6887 (dev-nuts) and
+their international number is 1-707-829-0515. E-mail to nuts@ora.com.
+
+Perl will probably not run on machines with a small address space.
+
+Please read all the directions below before you proceed any further, and
+then follow them carefully.
+
+After you have unpacked your kit, you should have all the files listed
+in MANIFEST.
+
+Installation
+
+1) Run Configure. This will figure out various things about your system.
+ Some things Configure will figure out for itself, other things it will
+ ask you about. It will then proceed to make config.h, config.sh, and
+ Makefile. If you're a hotshot, run Configure -d to take all the
+ defaults and then edit config.sh to patch up any flaws.
+
+ You might possibly have to trim # comments from the front of Configure
+ if your sh doesn't handle them, but all other # comments will be taken
+ care of.
+
+ (If you don't have sh, you'll have to copy the sample file config.H to
+ config.h and edit the config.h to reflect your system's peculiarities.)
+
+2) Glance through config.h to make sure system dependencies are correct.
+ Most of them should have been taken care of by running the Configure script.
+
+ If you have any additional changes to make to the C definitions, they
+ can be done in cflags.SH. For instance, to turn off the optimizer
+ on eval.c, find the line in the switch structure for eval.c and
+ put the command $optimize='-g' before the ;;. You will probably
+ want to change the entry for teval.c too. To change the C flags
+ for all the files, edit config.sh and change either $ccflags or $optimize.
+
+3) make depend
+
+ This will look for all the includes and modify Makefile accordingly.
+ Configure will offer to do this for you.
+
+4) make
+
+ This will attempt to make perl in the current directory.
+
+ If you can't compile successfully, try adding a -DCRIPPLED_CC flag.
+ (Just because you get no errors doesn't mean it compiled right!)
+ This simplifies some complicated expressions for compilers that
+ get indigestion easily. If that has no effect, try turning off
+ optimization. If you have missing routines, you probably need to
+ add some library or other, or you need to undefine some feature that
+ Configure thought was there but is defective or incomplete.
+
+ Some compilers will not compile or optimize the larger files without
+ some extra switches to use larger jump offsets or allocate larger
+ internal tables. You can customize the switches for each file in
+ cflags.SH. It's okay to insert rules for specific files into
+ Makefile.SH, since a default rule only takes effect in the
+ absence of a specific rule.
+
+ Most of the following hints are now done automatically by Configure.
+
+ The 3b2 needs to turn off -O.
+ Compilers with limited switch tables may have to define -DSMALLSWITCHES
+ Domain/OS 10.3 (at least) native C 6.7 may need -opt 2 for eval.c
+ AIX/RT may need a -a switch and -DCRIPPLED_CC.
+ AIX RS/6000 needs to use system malloc and avoid -O on eval.c and toke.c.
+ AIX RS/6000 needs -D_NO_PROTO.
+ SUNOS 4.0.[12] needs -DFPUTS_BOTCH.
+ SUNOS 3.[45] should use the system malloc.
+ SGI machines may need -Ddouble="long float" and -O1.
+ Vax-based systems may need to hand assemble teval.s with a -J switch.
+ Ultrix on MIPS machines may need -DLANGUAGE_C.
+ Ultrix 4.0 on MIPS machines may need -Olimit 2900 or so.
+ Ultrix 3.[01] on MIPS needs to undefine WAITPID--the system call is busted.
+ MIPS machines need /bin before /bsd43/bin in PATH.
+ MIPS machines may need to undef d_volatile.
+ MIPS machines may need to turn off -O on cmd.c, perl.c and tperl.c.
+ Some MIPS machines may need to undefine CASTNEGFLOAT.
+ Xenix 386 needs -Sm11000 for yacc, and may need -UM_I86.
+ SCO Xenix may need -m25000 for yacc. See also README.xenix.
+ Genix needs to use libc rather than libc_s, or #undef VARARGS.
+ NCR Tower 32 (OS 2.01.01) may need -W2,-Sl,2000 and #undef MKDIR.
+ A/UX may appears to work with -O -B/usr/lib/big/ optimizer flags.
+ A/UX needs -lposix to find rewinddir.
+ A/UX may need -ZP -DPOSIX, and -g if big cc is used.
+ FPS machines may need -J and -DBADSWITCH.
+ UTS may need one or more of -DCRIPPLED_CC, -K or -g, and undef LSTAT.
+ dynix may need to undefine CASTNEGFLOAT (d_castneg='undef' in config.sh).
+ Dnix (not dynix) may need to remove -O.
+ IRIX 3.3 may need to undefine VFORK.
+ HP/UX may need to pull cerror.o and syscall.o out of libc.a and link
+ them in explicitly.
+ If you get syntax errors on '(', try -DCRIPPLED_CC or -DBADSWITCH or both.
+ Machines with half-implemented dbm routines will need to #undef ODBM & NDBM.
+ If you have GDBM available and want it instead of NDBM, say -DHAS_GDBM.
+ C's that don't try to restore registers on longjmp() may need -DJMPCLOBBER.
+ (Try this if you get random glitches.)
+ If you get duplicates upon linking for malloc et al, say -DHIDEMYMALLOC.
+ Turn on support for 64-bit integers (long longs) with -DQUAD.
+
+5) make test
+
+ This will run the regression tests on the perl you just made.
+ If it doesn't say "All tests successful" then something went wrong.
+ See the README in the t subdirectory. Note that you can't run it
+ in background if this disables opening of /dev/tty. If "make test"
+ bombs out, just cd to the t directory and run TEST by hand to see if
+ it makes any difference. If individual tests bomb, you can run
+ them by hand, e.g., ./perl op/groups.t
+
+6) make install
+
+ This will put perl into a public directory (such as /usr/local/bin).
+ It will also try to put the man pages in a reasonable place. It will not
+ nroff the man page, however. You may need to be root to do this. If
+ you are not root, you must own the directories in question and you should
+ ignore any messages about chown not working.
+
+7) Read the manual entry before running perl.
+
+8) IMPORTANT! Help save the world! Communicate any problems and suggested
+ patches to me, lwall@netlabs.com (Larry Wall), so we can
+ keep the world in sync. If you have a problem, there's someone else
+ out there who either has had or will have the same problem.
+
+ If possible, send in patches such that the patch program will apply them.
+ Context diffs are the best, then normal diffs. Don't send ed scripts--
+ I've probably changed my copy since the version you have. It's also
+ helpful if you send the output of "uname -a".
+
+ Watch for perl patches in comp.lang.perl. Patches will generally be
+ in a form usable by the patch program. If you are just now bringing up
+ perl and aren't sure how many patches there are, write to me and I'll
+ send any you don't have. Your current patch level is shown in patchlevel.h.
+
+
+Just a personal note: I want you to know that I create nice things like this
+because it pleases the Author of my story. If this bothers you, then your
+notion of Authorship needs some revision. But you can use perl anyway. :-)
+
+ The author.
diff --git a/gnu/usr.bin/perl/VERSION b/gnu/usr.bin/perl/VERSION
new file mode 100644
index 0000000..2b80880
--- /dev/null
+++ b/gnu/usr.bin/perl/VERSION
@@ -0,0 +1 @@
+Perl 4.0 patchlevel 36
diff --git a/gnu/usr.bin/perl/Wishlist b/gnu/usr.bin/perl/Wishlist
new file mode 100644
index 0000000..3290834
--- /dev/null
+++ b/gnu/usr.bin/perl/Wishlist
@@ -0,0 +1,9 @@
+built-in cpp
+perl to C translator
+multi-threading
+make more easily embeddable
+built-in globbing
+compile to threaded code
+rewrite regexp parser for better integrated optimization
+add structured types and objects
+allow for lexical scoping
diff --git a/gnu/usr.bin/perl/eg/ADB b/gnu/usr.bin/perl/eg/ADB
new file mode 100644
index 0000000..09b93c3
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/ADB
@@ -0,0 +1,8 @@
+#!/usr/bin/perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/ADB,v 1.1.1.1 1993/08/23 21:29:43 nate Exp $
+
+# This script is only useful when used in your crash directory.
+
+$num = shift;
+exec 'adb', '-k', "vmunix.$num", "vmcore.$num";
diff --git a/gnu/usr.bin/perl/eg/README b/gnu/usr.bin/perl/eg/README
new file mode 100644
index 0000000..87cfc33
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/README
@@ -0,0 +1,22 @@
+Although supplied with the perl package, the perl scripts in this eg
+directory and its subdirectories are placed in the public domain, and
+you may do anything with them that you wish.
+
+This stuff is supplied on an as-is basis--little attempt has been made to make
+any of it portable. It's mostly here to give you an idea of what perl code
+looks like, and what tricks and idioms are used.
+
+System administrators responsible for many computers will enjoy the items
+down in the g directory very much. The scan directory contains the beginnings
+of a system to check on and report various kinds of anomalies.
+
+If you machine doesn't support #!, the first thing you'll want to do is
+replace the #! with a couple of lines that look like this:
+
+ eval "exec /usr/bin/perl -S $0 $*"
+ if $running_under_some_shell;
+
+being sure to include any flags that were on the #! line. A supplied script
+called "nih" will translate perl scripts in place for you:
+
+ nih g/g??
diff --git a/gnu/usr.bin/perl/eg/changes b/gnu/usr.bin/perl/eg/changes
new file mode 100644
index 0000000..9835e1b
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/changes
@@ -0,0 +1,34 @@
+#!/usr/bin/perl -P
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/changes,v 1.1.1.1 1993/08/23 21:29:43 nate Exp $
+
+($dir, $days) = @ARGV;
+$dir = '/' if $dir eq '';
+$days = '14' if $days eq '';
+
+# Masscomps do things differently from Suns
+
+#if defined(mc300) || defined(mc500) || defined(mc700)
+open(Find, "find $dir -mtime -$days -print |") ||
+ die "changes: can't run find";
+#else
+open(Find, "find $dir \\( -fstype nfs -prune \\) -o -mtime -$days -ls |") ||
+ die "changes: can't run find";
+#endif
+
+while (<Find>) {
+
+#if defined(mc300) || defined(mc500) || defined(mc700)
+ $x = `/bin/ls -ild $_`;
+ $_ = $x;
+ ($inode,$perm,$links,$owner,$group,$size,$month,$day,$time,$name)
+ = split(' ');
+#else
+ ($inode,$blocks,$perm,$links,$owner,$group,$size,$month,$day,$time,$name)
+ = split(' ');
+#endif
+
+ printf("%10s%3s %-8s %-8s%9s %3s %2s %s\n",
+ $perm,$links,$owner,$group,$size,$month,$day,$name);
+}
+
diff --git a/gnu/usr.bin/perl/eg/client b/gnu/usr.bin/perl/eg/client
new file mode 100644
index 0000000..5900c90
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/client
@@ -0,0 +1,34 @@
+#!./perl
+
+$pat = 'S n C4 x8';
+$inet = 2;
+$echo = 7;
+$smtp = 25;
+$nntp = 119;
+$test = 2345;
+
+$SIG{'INT'} = 'dokill';
+
+$this = pack($pat,$inet,0, 128,149,13,43);
+$that = pack($pat,$inet,$test,127,0,0,1);
+
+if (socket(S,2,1,6)) { print "socket ok\n"; } else { die $!; }
+if (bind(S,$this)) { print "bind ok\n"; } else { die $!; }
+if (connect(S,$that)) { print "connect ok\n"; } else { die $!; }
+
+select(S); $| = 1; select(stdout);
+
+if ($child = fork) {
+ while (<STDIN>) {
+ print S;
+ }
+ sleep 3;
+ do dokill();
+}
+else {
+ while (<S>) {
+ print;
+ }
+}
+
+sub dokill { kill 9,$child if $child; }
diff --git a/gnu/usr.bin/perl/eg/down b/gnu/usr.bin/perl/eg/down
new file mode 100644
index 0000000..bbb0d06
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/down
@@ -0,0 +1,30 @@
+#!/usr/bin/perl
+
+$| = 1;
+if ($#ARGV >= 0) {
+ $cmd = join(' ',@ARGV);
+}
+else {
+ print "Command: ";
+ $cmd = <stdin>;
+ chop($cmd);
+ while ($cmd =~ s/\\$//) {
+ print "+ ";
+ $cmd .= <stdin>;
+ chop($cmd);
+ }
+}
+$cwd = `pwd`; chop($cwd);
+
+open(FIND,'find . -type d -print|') || die "Can't run find";
+
+while (<FIND>) {
+ chop;
+ unless (chdir $_) {
+ print stderr "Can't cd to $_\n";
+ next;
+ }
+ print "\t--> ",$_,"\n";
+ system $cmd;
+ chdir $cwd;
+}
diff --git a/gnu/usr.bin/perl/eg/dus b/gnu/usr.bin/perl/eg/dus
new file mode 100644
index 0000000..94c648b
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/dus
@@ -0,0 +1,22 @@
+#!/usr/bin/perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/dus,v 1.1.1.1 1993/08/23 21:29:43 nate Exp $
+
+# This script does a du -s on any directories in the current directory that
+# are not mount points for another filesystem.
+
+($mydev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat('.');
+
+open(ls,'ls -F1|');
+
+while (<ls>) {
+ chop;
+ next unless s|/$||;
+ ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat($_);
+ next unless $dev == $mydev;
+ push(@ary,$_);
+}
+
+exec 'du', '-s', @ary;
diff --git a/gnu/usr.bin/perl/eg/findcp b/gnu/usr.bin/perl/eg/findcp
new file mode 100644
index 0000000..47e4438
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/findcp
@@ -0,0 +1,53 @@
+#!/usr/bin/perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/findcp,v 1.1.1.1 1993/08/23 21:29:43 nate Exp $
+
+# This is a wrapper around the find command that pretends find has a switch
+# of the form -cp host:destination. It presumes your find implements -ls.
+# It uses tar to do the actual copy. If your tar knows about the I switch
+# you may prefer to use findtar, since this one has to do the tar in batches.
+
+sub copy {
+ `tar cf - $list | rsh $desthost cd $destdir '&&' tar xBpf -`;
+}
+
+$sourcedir = $ARGV[0];
+if ($sourcedir =~ /^\//) {
+ $ARGV[0] = '.';
+ unless (chdir($sourcedir)) { die "Can't find directory $sourcedir: $!"; }
+}
+
+$args = join(' ',@ARGV);
+if ($args =~ s/-cp *([^ ]+)/-ls/) {
+ $dest = $1;
+ if ($dest =~ /(.*):(.*)/) {
+ $desthost = $1;
+ $destdir = $2;
+ }
+ else {
+ die "Malformed destination--should be host:directory";
+ }
+}
+else {
+ die("No destination specified");
+}
+
+open(find,"find $args |") || die "Can't run find for you: $!";
+
+while (<find>) {
+ @x = split(' ');
+ if ($x[2] =~ /^d/) { next;}
+ chop($filename = $x[10]);
+ if (length($list) > 5000) {
+ do copy();
+ $list = '';
+ }
+ else {
+ $list .= ' ';
+ }
+ $list .= $filename;
+}
+
+if ($list) {
+ do copy();
+}
diff --git a/gnu/usr.bin/perl/eg/findtar b/gnu/usr.bin/perl/eg/findtar
new file mode 100644
index 0000000..a60f10f
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/findtar
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/findtar,v 1.1.1.1 1993/08/23 21:29:43 nate Exp $
+
+# findtar takes find-style arguments and spits out a tarfile on stdout.
+# It won't work unless your find supports -ls and your tar the I flag.
+
+$args = join(' ',@ARGV);
+open(find,"/usr/bin/find $args -ls |") || die "Can't run find for you.";
+
+open(tar,"| /bin/tar cIf - -") || die "Can't run tar for you: $!";
+
+while (<find>) {
+ @x = split(' ');
+ if ($x[2] =~ /^d/) { print tar '-d ';}
+ print tar $x[10],"\n";
+}
diff --git a/gnu/usr.bin/perl/eg/g/gcp b/gnu/usr.bin/perl/eg/g/gcp
new file mode 100644
index 0000000..3e44a9c
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/g/gcp
@@ -0,0 +1,114 @@
+#!/usr/bin/perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/g/gcp,v 1.1.1.1 1993/08/23 21:29:44 nate Exp $
+
+# Here is a script to do global rcps. See man page.
+
+$#ARGV >= 1 || die "Not enough arguments.\n";
+
+if ($ARGV[0] eq '-r') {
+ $rcp = 'rcp -r';
+ shift;
+} else {
+ $rcp = 'rcp';
+}
+$args = $rcp;
+$dest = $ARGV[$#ARGV];
+
+$SIG{'QUIT'} = 'CLEANUP';
+$SIG{'INT'} = 'CONT';
+
+while ($arg = shift) {
+ if ($arg =~ /^([-a-zA-Z0-9_+]+):/) {
+ if ($systype && $systype ne $1) {
+ die "Can't mix system type specifers ($systype vs $1).\n";
+ }
+ $#ARGV < 0 || $arg !~ /:$/ || die "No source file specified.\n";
+ $systype = $1;
+ $args .= " $arg";
+ } else {
+ if ($#ARGV >= 0) {
+ if ($arg =~ /^[\/~]/) {
+ $arg =~ /^(.*)\// && ($dir = $1);
+ } else {
+ if (!$pwd) {
+ chop($pwd = `pwd`);
+ }
+ $dir = $pwd;
+ }
+ }
+ if ($olddir && $dir ne $olddir && $dest =~ /:$/) {
+ $args .= " $dest$olddir; $rcp";
+ }
+ $olddir = $dir;
+ $args .= " $arg";
+ }
+}
+
+die "No system type specified.\n" unless $systype;
+
+$args =~ s/:$/:$olddir/;
+
+chop($thishost = `hostname`);
+
+$one_of_these = ":$systype:";
+if ($systype =~ s/\+/[+]/g) {
+ $one_of_these =~ s/\+/:/g;
+}
+$one_of_these =~ s/-/:-/g;
+
+@ARGV = ();
+push(@ARGV,'.grem') if -f '.grem';
+push(@ARGV,'.ghosts') if -f '.ghosts';
+push(@ARGV,'/etc/ghosts');
+
+$remainder = '';
+
+line: while (<>) {
+ s/[ \t]*\n//;
+ if (!$_ || /^#/) {
+ next line;
+ }
+ if (/^([a-zA-Z_0-9]+)=(.+)/) {
+ $name = $1; $repl = $2;
+ $repl =~ s/\+/:/g;
+ $repl =~ s/-/:-/g;
+ $one_of_these =~ s/:$name:/:$repl:/;
+ $repl =~ s/:/:-/g;
+ $one_of_these =~ s/:-$name:/:-$repl:/g;
+ next line;
+ }
+ @gh = split(' ');
+ $host = $gh[0];
+ next line if $host eq $thishost; # should handle aliases too
+ $wanted = 0;
+ foreach $class (@gh) {
+ $wanted++ if index($one_of_these,":$class:") >= 0;
+ $wanted = -9999 if index($one_of_these,":-$class:") >= 0;
+ }
+ if ($wanted > 0) {
+ ($cmd = $args) =~ s/[ \t]$systype:/ $host:/g;
+ print "$cmd\n";
+ $result = `$cmd 2>&1`;
+ $remainder .= "$host+" if
+ $result =~ /Connection timed out|Permission denied/;
+ print $result;
+ }
+}
+
+if ($remainder) {
+ chop($remainder);
+ open(grem,">.grem") || (printf stderr "Can't create .grem: $!\n");
+ print grem 'rem=', $remainder, "\n";
+ close(grem);
+ print 'rem=', $remainder, "\n";
+}
+
+sub CLEANUP {
+ exit;
+}
+
+sub CONT {
+ print "Continuing...\n"; # Just ignore the signal that kills rcp
+ $remainder .= "$host+";
+}
diff --git a/gnu/usr.bin/perl/eg/g/gcp.man b/gnu/usr.bin/perl/eg/g/gcp.man
new file mode 100644
index 0000000..8985742
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/g/gcp.man
@@ -0,0 +1,77 @@
+.\" $Header: /home/cvs/386BSD/ports/lang/perl/eg/g/gcp.man,v 1.1.1.1 1993/08/23 21:29:44 nate Exp $
+.TH GCP 1C "13 May 1988"
+.SH NAME
+gcp \- global file copy
+.SH SYNOPSIS
+.B gcp
+file1 file2
+.br
+.B gcp
+[
+.B \-r
+] file ... directory
+.SH DESCRIPTION
+.I gcp
+works just like rcp(1C) except that you may specify a set of hosts to copy files
+from or to.
+The host sets are defined in the file /etc/ghosts.
+(An individual host name can be used as a set containing one member.)
+You can give a command like
+
+ gcp /etc/motd sun:
+
+to copy your /etc/motd file to /etc/motd on all the Suns.
+If, on the other hand, you say
+
+ gcp /a/foo /b/bar sun:/tmp
+
+then your files will be copied to /tmp on all the Suns.
+The general rule is that if you don't specify the destination directory,
+files go to the same directory they are in currently.
+.P
+You may specify the union of two or more sets by using + as follows:
+
+ gcp /a/foo /b/bar 750+mc:
+
+which will copy /a/foo to /a/foo on all 750's and Masscomps, and then copy
+/b/bar to /b/bar on all 750's and Masscomps.
+.P
+Commonly used sets should be defined in /etc/ghosts.
+For example, you could add a line that says
+
+ pep=manny+moe+jack
+
+Another way to do that would be to add the word "pep" after each of the host
+entries:
+
+ manny sun3 pep
+.br
+ moe sun3 pep
+.br
+ jack sun3 pep
+
+Hosts and sets of host can also be excluded:
+
+ foo=sun-sun2
+
+Any host so excluded will never be included, even if a subsequent set on the
+line includes it:
+
+ foo=abc+def
+.br
+ bar=xyz-abc+foo
+
+comes out to xyz+def.
+
+You can define private host sets by creating .ghosts in your current directory
+with entries just like /etc/ghosts.
+Also, if there is a file .grem, it defines "rem" to be the remaining hosts
+from the last gsh or gcp that didn't succeed everywhere.
+.PP
+Interrupting with a SIGINT will cause the rcp to the current host to be skipped
+and execution resumed with the next host.
+To stop completely, send a SIGQUIT.
+.SH SEE ALSO
+rcp(1C)
+.SH BUGS
+All the bugs of rcp, since it calls rcp.
diff --git a/gnu/usr.bin/perl/eg/g/ged b/gnu/usr.bin/perl/eg/g/ged
new file mode 100644
index 0000000..d296a84
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/g/ged
@@ -0,0 +1,21 @@
+#!/usr/bin/perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/g/ged,v 1.1.1.1 1993/08/23 21:29:44 nate Exp $
+
+# Does inplace edits on a set of files on a set of machines.
+#
+# Typical invokation:
+#
+# ged vax+sun /etc/passwd
+# s/Freddy/Freddie/;
+# ^D
+#
+
+$class = shift;
+$files = join(' ',@ARGV);
+
+die "Usage: ged class files <perlcmds\n" unless $files;
+
+exec "gsh", $class, "-d", "perl -pi.bak - $files";
+
+die "Couldn't execute gsh for some reason, stopped";
diff --git a/gnu/usr.bin/perl/eg/g/ghosts b/gnu/usr.bin/perl/eg/g/ghosts
new file mode 100644
index 0000000..96ec771
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/g/ghosts
@@ -0,0 +1,33 @@
+# This first section gives alternate sets defined in terms of the sets given
+# by the second section. The order is important--all references must be
+# forward references.
+
+Nnd=sun-nd
+all=sun+mc+vax
+baseline=sun+mc
+sun=sun2+sun3
+vax=750+8600
+pep=manny+moe+jack
+
+# This second section defines the basic sets. Each host should have a line
+# that specifies which sets it is a member of. Extra sets should be separated
+# by white space. (The first section isn't strictly necessary, since all sets
+# could be defined in the second section, but then it wouldn't be so readable.)
+
+basvax 8600 src
+cdb0 sun3 sys
+cdb1 sun3 sys
+cdb2 sun3 sys
+chief sun3 src
+tis0 sun3
+manny sun3 sys
+moe sun3 sys
+jack sun3 sys
+disney sun3 sys
+huey sun3 nd
+dewey sun3 nd
+louie sun3 nd
+bizet sun2 src sys
+gif0 mc src
+mc0 mc
+dtv0 mc
diff --git a/gnu/usr.bin/perl/eg/g/gsh b/gnu/usr.bin/perl/eg/g/gsh
new file mode 100644
index 0000000..3322a02
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/g/gsh
@@ -0,0 +1,117 @@
+#! /usr/bin/perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/g/gsh,v 1.1.1.1 1993/08/23 21:29:44 nate Exp $
+
+# Do rsh globally--see man page
+
+$SIG{'QUIT'} = 'quit'; # install signal handler for SIGQUIT
+
+sub getswitches {
+ while ($ARGV[0] =~ /^-/) { # parse switches
+ $ARGV[0] =~ /^-h/ && ($showhost++,$silent++,shift(@ARGV),next);
+ $ARGV[0] =~ /^-s/ && ($silent++,shift(@ARGV),next);
+ $ARGV[0] =~ /^-d/ && ($dodist++,shift(@ARGV),next);
+ $ARGV[0] =~ /^-n/ && ($n=' -n',shift(@ARGV),next);
+ $ARGV[0] =~ /^-l/ && ($l=' -l ' . $ARGV[1],shift(@ARGV),shift(@ARGV),
+ next);
+ last;
+ }
+}
+
+do getswitches(); # get any switches before class
+$systype = shift; # get name representing set of hosts
+do getswitches(); # same switches allowed after class
+
+if ($dodist) { # distribute input over all rshes?
+ `cat >/tmp/gsh$$`; # get input into a handy place
+ $dist = " </tmp/gsh$$"; # each rsh takes input from there
+}
+
+$cmd = join(' ',@ARGV); # remaining args constitute the command
+$cmd =~ s/'/'"'"'/g; # quote any embedded single quotes
+
+$one_of_these = ":$systype:"; # prepare to expand "macros"
+$one_of_these =~ s/\+/:/g; # we hope to end up with list of
+$one_of_these =~ s/-/:-/g; # colon separated attributes
+
+@ARGV = ();
+push(@ARGV,'.grem') if -f '.grem';
+push(@ARGV,'.ghosts') if -f '.ghosts';
+push(@ARGV,'/etc/ghosts');
+
+$remainder = '';
+
+line: while (<>) { # for each line of ghosts
+
+ s/[ \t]*\n//; # trim trailing whitespace
+ if (!$_ || /^#/) { # skip blank line or comment
+ next line;
+ }
+
+ if (/^(\w+)=(.+)/) { # a macro line?
+ $name = $1; $repl = $2;
+ $repl =~ s/\+/:/g;
+ $repl =~ s/-/:-/g;
+ $one_of_these =~ s/:$name:/:$repl:/; # do expansion in "wanted" list
+ $repl =~ s/:/:-/g;
+ $one_of_these =~ s/:-$name:/:-$repl:/;
+ next line;
+ }
+
+ # we have a normal line
+
+ @attr = split(' '); # a list of attributes to match against
+ # which we put into an array
+ $host = $attr[0]; # the first attribute is the host name
+ if ($showhost) {
+ $showhost = "$host:\t";
+ }
+
+ $wanted = 0;
+ foreach $attr (@attr) { # iterate over attribute array
+ $wanted++ if index($one_of_these,":$attr:") >= 0;
+ $wanted = -9999 if index($one_of_these,":-$attr:") >= 0;
+ }
+ if ($wanted > 0) {
+ print "rsh $host$l$n '$cmd'\n" unless $silent;
+ $SIG{'INT'} = 'DEFAULT';
+ if (open(PIPE,"rsh $host$l$n '$cmd'$dist 2>&1|")) { # start an rsh
+ $SIG{'INT'} = 'cont';
+ for ($iter=0; <PIPE>; $iter++) {
+ unless ($iter) {
+ $remainder .= "$host+"
+ if /Connection timed out|Permission denied/;
+ }
+ print $showhost,$_;
+ }
+ close(PIPE);
+ } else {
+ print "(Can't execute rsh: $!)\n";
+ $SIG{'INT'} = 'cont';
+ }
+ }
+}
+
+unlink "/tmp/gsh$$" if $dodist;
+
+if ($remainder) {
+ chop($remainder);
+ open(grem,">.grem") || (printf stderr "Can't make a .grem file: $!\n");
+ print grem 'rem=', $remainder, "\n";
+ close(grem);
+ print 'rem=', $remainder, "\n";
+}
+
+# here are a couple of subroutines that serve as signal handlers
+
+sub cont {
+ print "\rContinuing...\n";
+ $remainder .= "$host+";
+}
+
+sub quit {
+ $| = 1;
+ print "\r";
+ $SIG{'INT'} = '';
+ kill 2, $$;
+}
diff --git a/gnu/usr.bin/perl/eg/g/gsh.man b/gnu/usr.bin/perl/eg/g/gsh.man
new file mode 100644
index 0000000..00eafb6
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/g/gsh.man
@@ -0,0 +1,80 @@
+.\" $Header: /home/cvs/386BSD/ports/lang/perl/eg/g/gsh.man,v 1.1.1.1 1993/08/23 21:29:44 nate Exp $
+.TH GSH 8 "13 May 1988"
+.SH NAME
+gsh \- global shell
+.SH SYNOPSIS
+.B gsh
+[options]
+.I host
+[options]
+.I command
+.SH DESCRIPTION
+.I gsh
+works just like rsh(1C) except that you may specify a set of hosts to execute
+the command on.
+The host sets are defined in the file /etc/ghosts.
+(An individual host name can be used as a set containing one member.)
+You can give a command like
+
+ gsh sun /etc/mungmotd
+
+to run /etc/mungmotd on all your Suns.
+.P
+You may specify the union of two or more sets by using + as follows:
+
+ gsh 750+mc /etc/mungmotd
+
+which will run mungmotd on all 750's and Masscomps.
+.P
+Commonly used sets should be defined in /etc/ghosts.
+For example, you could add a line that says
+
+ pep=manny+moe+jack
+
+Another way to do that would be to add the word "pep" after each of the host
+entries:
+
+ manny sun3 pep
+.br
+ moe sun3 pep
+.br
+ jack sun3 pep
+
+Hosts and sets of host can also be excluded:
+
+ foo=sun-sun2
+
+Any host so excluded will never be included, even if a subsequent set on the
+line includes it:
+
+ foo=abc+def
+ bar=xyz-abc+foo
+
+comes out to xyz+def.
+
+You can define private host sets by creating .ghosts in your current directory
+with entries just like /etc/ghosts.
+Also, if there is a file .grem, it defines "rem" to be the remaining hosts
+from the last gsh or gcp that didn't succeed everywhere.
+
+Options include all those defined by rsh, as well as
+
+.IP "\-d" 8
+Causes gsh to collect input till end of file, and then distribute that input
+to each invokation of rsh.
+.IP "\-h" 8
+Rather than print out the command followed by the output, merely prepends the
+host name to each line of output.
+.IP "\-s" 8
+Do work silently.
+.PP
+Interrupting with a SIGINT will cause the rsh to the current host to be skipped
+and execution resumed with the next host.
+To stop completely, send a SIGQUIT.
+.SH SEE ALSO
+rsh(1C)
+.SH BUGS
+All the bugs of rsh, since it calls rsh.
+
+Also, will not properly return data from the remote execution that contains
+null characters.
diff --git a/gnu/usr.bin/perl/eg/muck b/gnu/usr.bin/perl/eg/muck
new file mode 100644
index 0000000..873539b
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/muck
@@ -0,0 +1,141 @@
+#!../perl
+
+$M = '-M';
+$M = '-m' if -d '/usr/uts' && -f '/etc/master';
+
+do 'getopt.pl';
+do Getopt('f');
+
+if ($opt_f) {
+ $makefile = $opt_f;
+}
+elsif (-f 'makefile') {
+ $makefile = 'makefile';
+}
+elsif (-f 'Makefile') {
+ $makefile = 'Makefile';
+}
+else {
+ die "No makefile\n";
+}
+
+$MF = 'mf00';
+
+while(($key,$val) = each(ENV)) {
+ $mac{$key} = $val;
+}
+
+do scan($makefile);
+
+$co = $action{'.c.o'};
+$co = ' ' unless $co;
+
+$missing = "Missing dependencies:\n";
+foreach $key (sort keys(o)) {
+ if ($oc{$key}) {
+ $src = $oc{$key};
+ $action = $action{$key};
+ }
+ else {
+ $action = '';
+ }
+ if (!$action) {
+ if ($co && ($c = $key) =~ s/\.o$/.c/ && -f $c) {
+ $src = $c;
+ $action = $co;
+ }
+ else {
+ print "No source found for $key $c\n";
+ next;
+ }
+ }
+ $I = '';
+ $D = '';
+ $I .= $1 while $action =~ s/(-I\S+\s*)//;
+ $D .= $1 . ' ' while $action =~ s/(-D\w+)//;
+ if ($opt_v) {
+ $cmd = "Checking $key: cc $M $D $I $src";
+ $cmd =~ s/\s\s+/ /g;
+ print stderr $cmd,"\n";
+ }
+ open(CPP,"cc $M $D $I $src|") || die "Can't run C preprocessor: $!";
+ while (<CPP>) {
+ ($name,$dep) = split;
+ $dep =~ s|^\./||;
+ (print $missing,"$key: $dep\n"),($missing='')
+ unless ($dep{"$key: $dep"} += 2) > 2;
+ }
+}
+
+$extra = "\nExtraneous dependencies:\n";
+foreach $key (sort keys(dep)) {
+ if ($key =~ /\.o: .*\.h$/ && $dep{$key} == 1) {
+ print $extra,$key,"\n";
+ $extra = '';
+ }
+}
+
+sub scan {
+ local($makefile) = @_;
+ local($MF) = $MF;
+ print stderr "Analyzing $makefile.\n" if $opt_v;
+ $MF++;
+ open($MF,$makefile) || die "Can't open $makefile: $!";
+ while (<$MF>) {
+ chop;
+ chop($_ = $_ . <$MF>) while s/\\$//;
+ next if /^#/;
+ next if /^$/;
+ s/\$\((\w+):([^=)]*)=([^)]*)\)/do subst("$1","$2","$3")/eg;
+ s/\$\((\w+)\)/$mac{$1}/eg;
+ $mac{$1} = $2, next if /^(\w+)\s*=\s*(.*)/;
+ if (/^include\s+(.*)/) {
+ do scan($1);
+ print stderr "Continuing $makefile.\n" if $opt_v;
+ next;
+ }
+ if (/^([^:]+):\s*(.*)/) {
+ $left = $1;
+ $right = $2;
+ if ($right =~ /^([^;]*);(.*)/) {
+ $right = $1;
+ $action = $2;
+ }
+ else {
+ $action = '';
+ }
+ while (<$MF>) {
+ last unless /^\t/;
+ chop;
+ chop($_ = $_ . <$MF>) while s/\\$//;
+ next if /^#/;
+ last if /^$/;
+ s/\$\((\w+):([^=)]*)=([^)]*)\)/do subst("$1","$2","$3")/eg;
+ s/\$\((\w+)\)/$mac{$1}/eg;
+ $action .= $_;
+ }
+ foreach $targ (split(' ',$left)) {
+ $targ =~ s|^\./||;
+ foreach $src (split(' ',$right)) {
+ $src =~ s|^\./||;
+ $deplist{$targ} .= ' ' . $src;
+ $dep{"$targ: $src"} = 1;
+ $o{$src} = 1 if $src =~ /\.o$/;
+ $oc{$targ} = $src if $targ =~ /\.o$/ && $src =~ /\.[yc]$/;
+ }
+ $action{$targ} .= $action;
+ }
+ redo if $_;
+ }
+ }
+ close($MF);
+}
+
+sub subst {
+ local($foo,$from,$to) = @_;
+ $foo = $mac{$foo};
+ $from =~ s/\./[.]/;
+ y/a/a/;
+ $foo =~ s/\b$from\b/$to/g;
+ $foo;
+}
diff --git a/gnu/usr.bin/perl/eg/muck.man b/gnu/usr.bin/perl/eg/muck.man
new file mode 100644
index 0000000..1b45ee0
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/muck.man
@@ -0,0 +1,21 @@
+.\" $Header: /home/cvs/386BSD/ports/lang/perl/eg/muck.man,v 1.1.1.1 1993/08/23 21:29:43 nate Exp $
+.TH MUCK 1 "10 Jan 1989"
+.SH NAME
+muck \- make usage checker
+.SH SYNOPSIS
+.B muck
+[options]
+.SH DESCRIPTION
+.I muck
+looks at your current makefile and complains if you've left out any dependencies
+between .o and .h files.
+It also complains about extraneous dependencies.
+.PP
+You can use the -f FILENAME option to specify an alternate name for your
+makefile.
+The -v option is a little more verbose about what muck is mucking around
+with at the moment.
+.SH SEE ALSO
+make(1)
+.SH BUGS
+Only knows about .h, .c and .o files.
diff --git a/gnu/usr.bin/perl/eg/myrup b/gnu/usr.bin/perl/eg/myrup
new file mode 100644
index 0000000..b318589
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/myrup
@@ -0,0 +1,29 @@
+#!/usr/bin/perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/myrup,v 1.1.1.1 1993/08/23 21:29:43 nate Exp $
+
+# This was a customization of ruptime requested by someone here who wanted
+# to be able to find the least loaded machine easily. It uses the
+# /etc/ghosts file that's defined for gsh and gcp to prune down the
+# number of entries to those hosts we have administrative control over.
+
+print "node load (u)\n------- --------\n";
+
+open(ghosts,'/etc/ghosts') || die "Can't open /etc/ghosts: $!";
+line: while (<ghosts>) {
+ next line if /^#/;
+ next line if /^$/;
+ next line if /=/;
+ ($host) = split;
+ $wanted{$host} = 1;
+}
+
+open(ruptime,'ruptime|') || die "Can't run ruptime: $!";
+open(sort,'|sort +1n');
+
+while (<ruptime>) {
+ ($host,$upness,$foo,$users,$foo,$foo,$load) = split(/[\s,]+/);
+ if ($wanted{$host} && $upness eq 'up') {
+ printf sort "%s\t%s (%d)\n", $host, $load, $users;
+ }
+}
diff --git a/gnu/usr.bin/perl/eg/nih b/gnu/usr.bin/perl/eg/nih
new file mode 100644
index 0000000..a376142
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/nih
@@ -0,0 +1,10 @@
+eval "exec /usr/bin/perl -Spi.bak $0 $*"
+ if $running_under_some_shell;
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/nih,v 1.1.1.1 1993/08/23 21:29:43 nate Exp $
+
+# This script makes #! scripts directly executable on machines that don't
+# support #!. It edits in place any scripts mentioned on the command line.
+
+s|^#!(.*)|#!$1\neval "exec $1 -S \$0 \$*"\n\tif \$running_under_some_shell;|
+ if $. == 1;
diff --git a/gnu/usr.bin/perl/eg/perlsh b/gnu/usr.bin/perl/eg/perlsh
new file mode 100644
index 0000000..2b2cccd
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/perlsh
@@ -0,0 +1,15 @@
+#!/usr/bin/perl
+
+# Poor man's perl shell.
+
+# Simply type two carriage returns every time you want to evaluate.
+# Note that it must be a complete perl statement--don't type double
+# carriage return in the middle of a loop.
+
+$/ = "\n\n"; # set paragraph mode
+$SHlinesep = "\n";
+while ($SHcmd = <>) {
+ $/ = $SHlinesep;
+ eval $SHcmd; print $@ || "\n";
+ $SHlinesep = $/; $/ = '';
+}
diff --git a/gnu/usr.bin/perl/eg/relink b/gnu/usr.bin/perl/eg/relink
new file mode 100644
index 0000000..69956c9
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/relink
@@ -0,0 +1,91 @@
+#!/usr/bin/perl
+'di';
+'ig00';
+#
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/relink,v 1.1.1.1 1993/08/23 21:29:43 nate Exp $
+#
+# $Log: relink,v $
+# Revision 1.1.1.1 1993/08/23 21:29:43 nate
+# PERL!
+#
+# Revision 4.0 91/03/20 01:11:40 lwall
+# 4.0 baseline.
+#
+# Revision 3.0.1.2 90/08/09 03:17:44 lwall
+# patch19: added man page for relink and rename
+#
+
+($op = shift) || die "Usage: relink perlexpr [filenames]\n";
+if (!@ARGV) {
+ @ARGV = <STDIN>;
+ chop(@ARGV);
+}
+for (@ARGV) {
+ next unless -l; # symbolic link?
+ $name = $_;
+ $_ = readlink($_);
+ $was = $_;
+ eval $op;
+ die $@ if $@;
+ if ($was ne $_) {
+ unlink($name);
+ symlink($_, $name);
+ }
+}
+##############################################################################
+
+ # These next few lines are legal in both Perl and nroff.
+
+.00; # finish .ig
+
+'di \" finish diversion--previous line must be blank
+.nr nl 0-1 \" fake up transition to first page again
+.nr % 0 \" start at page 1
+';<<'.ex'; #__END__ ############# From here on it's a standard manual page ############
+.TH RELINK 1 "July 30, 1990"
+.AT 3
+.SH LINK
+relink \- relinks multiple symbolic links
+.SH SYNOPSIS
+.B relink perlexpr [symlinknames]
+.SH DESCRIPTION
+.I Relink
+relinks the symbolic links given according to the rule specified as the
+first argument.
+The argument is a Perl expression which is expected to modify the $_
+string in Perl for at least some of the names specified.
+For each symbolic link named on the command line, the Perl expression
+will be executed on the contents of the symbolic link with that name.
+If a given symbolic link's contents is not modified by the expression,
+it will not be changed.
+If a name given on the command line is not a symbolic link, it will be ignored.
+If no names are given on the command line, names will be read
+via standard input.
+.PP
+For example, to relink all symbolic links in the current directory
+pointing to somewhere in X11R3 so that they point to X11R4, you might say
+.nf
+
+ relink 's/X11R3/X11R4/' *
+
+.fi
+To change all occurences of links in the system from /usr/spool to /var/spool,
+you'd say
+.nf
+
+ find / -type l -print | relink 's#/usr/spool#/var/spool#'
+
+.fi
+.SH ENVIRONMENT
+No environment variables are used.
+.SH FILES
+.SH AUTHOR
+Larry Wall
+.SH "SEE ALSO"
+ln(1)
+.br
+perl(1)
+.SH DIAGNOSTICS
+If you give an invalid Perl expression you'll get a syntax error.
+.SH BUGS
+.ex
diff --git a/gnu/usr.bin/perl/eg/rename b/gnu/usr.bin/perl/eg/rename
new file mode 100644
index 0000000..b568406
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/rename
@@ -0,0 +1,83 @@
+#!/usr/bin/perl
+'di';
+'ig00';
+#
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/rename,v 1.1.1.1 1993/08/23 21:29:43 nate Exp $
+#
+# $Log: rename,v $
+# Revision 1.1.1.1 1993/08/23 21:29:43 nate
+# PERL!
+#
+# Revision 4.0 91/03/20 01:11:53 lwall
+# 4.0 baseline.
+#
+# Revision 3.0.1.2 90/08/09 03:17:57 lwall
+# patch19: added man page for relink and rename
+#
+
+($op = shift) || die "Usage: rename perlexpr [filenames]\n";
+if (!@ARGV) {
+ @ARGV = <STDIN>;
+ chop(@ARGV);
+}
+for (@ARGV) {
+ $was = $_;
+ eval $op;
+ die $@ if $@;
+ rename($was,$_) unless $was eq $_;
+}
+##############################################################################
+
+ # These next few lines are legal in both Perl and nroff.
+
+.00; # finish .ig
+
+'di \" finish diversion--previous line must be blank
+.nr nl 0-1 \" fake up transition to first page again
+.nr % 0 \" start at page 1
+';<<'.ex'; #__END__ ############# From here on it's a standard manual page ############
+.TH RENAME 1 "July 30, 1990"
+.AT 3
+.SH NAME
+rename \- renames multiple files
+.SH SYNOPSIS
+.B rename perlexpr [files]
+.SH DESCRIPTION
+.I Rename
+renames the filenames supplied according to the rule specified as the
+first argument.
+The argument is a Perl expression which is expected to modify the $_
+string in Perl for at least some of the filenames specified.
+If a given filename is not modified by the expression, it will not be
+renamed.
+If no filenames are given on the command line, filenames will be read
+via standard input.
+.PP
+For example, to rename all files matching *.bak to strip the extension,
+you might say
+.nf
+
+ rename 's/\e.bak$//' *.bak
+
+.fi
+To translate uppercase names to lower, you'd use
+.nf
+
+ rename 'y/A-Z/a-z/' *
+
+.fi
+.SH ENVIRONMENT
+No environment variables are used.
+.SH FILES
+.SH AUTHOR
+Larry Wall
+.SH "SEE ALSO"
+mv(1)
+.br
+perl(1)
+.SH DIAGNOSTICS
+If you give an invalid Perl expression you'll get a syntax error.
+.SH BUGS
+.I Rename
+does not check for the existence of target filenames, so use with care.
+.ex
diff --git a/gnu/usr.bin/perl/eg/rmfrom b/gnu/usr.bin/perl/eg/rmfrom
new file mode 100644
index 0000000..0c8fa2c
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/rmfrom
@@ -0,0 +1,7 @@
+#!/usr/bin/perl -n
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/rmfrom,v 1.1.1.1 1993/08/23 21:29:43 nate Exp $
+
+# A handy (but dangerous) script to put after a find ... -print.
+
+chop; unlink;
diff --git a/gnu/usr.bin/perl/eg/scan/scan_df b/gnu/usr.bin/perl/eg/scan/scan_df
new file mode 100644
index 0000000..6887387
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/scan/scan_df
@@ -0,0 +1,51 @@
+#!/usr/bin/perl -P
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/scan/scan_df,v 1.1.1.1 1993/08/23 21:29:44 nate Exp $
+
+# This report points out filesystems that are in danger of overflowing.
+
+(chdir '/usr/adm/private/memories') || die "Can't cd to memories: $!\n";
+`df >newdf`;
+open(Df, 'olddf');
+
+while (<Df>) {
+ ($fs,$kbytes,$used,$avail,$capacity,$mounted_on) = split;
+ next if $fs =~ /:/;
+ next if $fs eq '';
+ $oldused{$fs} = $used;
+}
+
+open(Df, 'newdf') || die "scan_df: can't open newdf";
+
+while (<Df>) {
+ ($fs,$kbytes,$used,$avail,$capacity,$mounted_on) = split;
+ next if $fs =~ /:/;
+ next if $fs eq '';
+ $oldused = $oldused{$fs};
+ next if ($oldused == $used && $capacity < 99); # inactive filesystem
+ if ($capacity >= 90) {
+#if defined(mc300) || defined(mc500) || defined(mc700)
+ $_ = substr($_,0,13) . ' ' . substr($_,13,1000);
+ $kbytes /= 2; # translate blocks to K
+ $used /= 2;
+ $oldused /= 2;
+ $avail /= 2;
+#endif
+ $diff = int($used - $oldused);
+ if ($avail < $diff * 2) { # mark specially if in danger
+ $mounted_on .= ' *';
+ }
+ next if $diff < 50 && $mounted_on eq '/';
+ $fs =~ s|/dev/||;
+ if ($diff >= 0) {
+ $diff = '(+' . $diff . ')';
+ }
+ else {
+ $diff = '(' . $diff . ')';
+ }
+ printf "%-8s%8d%8d %-8s%8d%7s %s\n",
+ $fs,$kbytes,$used,$diff,$avail,$capacity,$mounted_on;
+ }
+}
+
+rename('newdf','olddf');
diff --git a/gnu/usr.bin/perl/eg/scan/scan_last b/gnu/usr.bin/perl/eg/scan/scan_last
new file mode 100644
index 0000000..6621120
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/scan/scan_last
@@ -0,0 +1,57 @@
+#!/usr/bin/perl -P
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/scan/scan_last,v 1.1.1.1 1993/08/23 21:29:45 nate Exp $
+
+# This reports who was logged on at weird hours
+
+($dy, $mo, $lastdt) = split(/ +/,`date`);
+
+open(Last, 'exec last 2>&1 |') || die "scan_last: can't run last";
+
+while (<Last>) {
+#if defined(mc300) || defined(mc500) || defined(mc700)
+ $_ = substr($_,0,19) . substr($_,23,100);
+#endif
+ next if /^$/;
+ (print),next if m|^/|;
+ $login = substr($_,0,8);
+ $tty = substr($_,10,7);
+ $from = substr($_,19,15);
+ $day = substr($_,36,3);
+ $mo = substr($_,40,3);
+ $dt = substr($_,44,2);
+ $hr = substr($_,47,2);
+ $min = substr($_,50,2);
+ $dash = substr($_,53,1);
+ $tohr = substr($_,55,2);
+ $tomin = substr($_,58,2);
+ $durhr = substr($_,63,2);
+ $durmin = substr($_,66,2);
+
+ next unless $hr;
+ next if $login eq 'reboot ';
+ next if $login eq 'shutdown';
+
+ if ($dt != $lastdt) {
+ if ($lastdt < $dt) {
+ $seen += $dt - $lastdt;
+ }
+ else {
+ $seen++;
+ }
+ $lastdt = $dt;
+ }
+
+ $inat = $hr + $min / 60;
+ if ($tohr =~ /^[a-z]/) {
+ $outat = 12; # something innocuous
+ } else {
+ $outat = $tohr + $tomin / 60;
+ }
+
+ last if $seen + ($inat < 8) > 1;
+
+ if ($inat < 5 || $inat > 21 || $outat < 6 || $outat > 23) {
+ print;
+ }
+}
diff --git a/gnu/usr.bin/perl/eg/scan/scan_messages b/gnu/usr.bin/perl/eg/scan/scan_messages
new file mode 100644
index 0000000..a28cda8
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/scan/scan_messages
@@ -0,0 +1,222 @@
+#!/usr/bin/perl -P
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/scan/scan_messages,v 1.1.1.1 1993/08/23 21:29:44 nate Exp $
+
+# This prints out extraordinary console messages. You'll need to customize.
+
+chdir('/usr/adm/private/memories') || die "Can't cd to memories: $!\n";
+
+$maxpos = `cat oldmsgs 2>&1`;
+
+#if defined(mc300) || defined(mc500) || defined(mc700)
+open(Msgs, '/dev/null') || die "scan_messages: can't open messages";
+#else
+open(Msgs, '/usr/adm/messages') || die "scan_messages: can't open messages";
+#endif
+
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat(Msgs);
+
+if ($size < $maxpos) { # Did somebody truncate messages file?
+ $maxpos = 0;
+}
+
+seek(Msgs,$maxpos,0); # Start where we left off last time.
+
+while (<Msgs>) {
+ s/\[(\d+)\]/#/ && s/$1/#/g;
+#ifdef vax
+ $_ =~ s/[A-Z][a-z][a-z] +\w+ +[0-9:]+ +\w+ +//;
+ next if /root@.*:/;
+ next if /^vmunix: 4.3 BSD UNIX/;
+ next if /^vmunix: Copyright/;
+ next if /^vmunix: avail mem =/;
+ next if /^vmunix: SBIA0 at /;
+ next if /^vmunix: disk ra81 is/;
+ next if /^vmunix: dmf. at uba/;
+ next if /^vmunix: dmf.:.*asynch/;
+ next if /^vmunix: ex. at uba/;
+ next if /^vmunix: ex.: HW/;
+ next if /^vmunix: il. at uba/;
+ next if /^vmunix: il.: hardware/;
+ next if /^vmunix: ra. at uba/;
+ next if /^vmunix: ra.: media/;
+ next if /^vmunix: real mem/;
+ next if /^vmunix: syncing disks/;
+ next if /^vmunix: tms/;
+ next if /^vmunix: tmscp. at uba/;
+ next if /^vmunix: uba. at /;
+ next if /^vmunix: uda. at /;
+ next if /^vmunix: uda.: unit . ONLIN/;
+ next if /^vmunix: .*buffers containing/;
+ next if /^syslogd: .*newslog/;
+#endif
+ next if /unknown service/;
+ next if /^\.\.\.$/;
+ if (/^[A-Z][a-z][a-z] [ 0-9][0-9] [ 0-9][0-9]:[0-9][0-9]/) {
+ $pfx = '';
+ next;
+ }
+ next if /^[ \t]*$/;
+ next if /^[ 0-9]*done$/;
+ if (/^A/) {
+ next if /^Accounting [sr]/;
+ }
+ elsif (/^C/) {
+ next if /^Called from/;
+ next if /^Copyright/;
+ }
+ elsif (/^E/) {
+ next if /^End traceback/;
+ next if /^Ethernet address =/;
+ }
+ elsif (/^K/) {
+ next if /^KERNEL MODE/;
+ }
+ elsif (/^R/) {
+ next if /^Rebooting Unix/;
+ }
+ elsif (/^S/) {
+ next if /^Sun UNIX 4\.2 Release/;
+ }
+ elsif (/^W/) {
+ next if /^WARNING: clock gained/;
+ }
+ elsif (/^a/) {
+ next if /^arg /;
+ next if /^avail mem =/;
+ }
+ elsif (/^b/) {
+ next if /^bwtwo[0-9] at /;
+ }
+ elsif (/^c/) {
+ next if /^cgone[0-9] at /;
+ next if /^cdp[0-9] at /;
+ next if /^csr /;
+ }
+ elsif (/^d/) {
+ next if /^dcpa: init/;
+ next if /^done$/;
+ next if /^dts/;
+ next if /^dump i\/o error/;
+ next if /^dumping to dev/;
+ next if /^dump succeeded/;
+ $pfx = '*' if /^dev = /;
+ }
+ elsif (/^e/) {
+ next if /^end \*\*/;
+ next if /^error in copy/;
+ }
+ elsif (/^f/) {
+ next if /^found /;
+ }
+ elsif (/^i/) {
+ next if /^ib[0-9] at /;
+ next if /^ie[0-9] at /;
+ }
+ elsif (/^l/) {
+ next if /^le[0-9] at /;
+ }
+ elsif (/^m/) {
+ next if /^mem = /;
+ next if /^mt[0-9] at /;
+ next if /^mti[0-9] at /;
+ $pfx = '*' if /^mode = /;
+ }
+ elsif (/^n/) {
+ next if /^not found /;
+ }
+ elsif (/^p/) {
+ next if /^page map /;
+ next if /^pi[0-9] at /;
+ $pfx = '*' if /^panic/;
+ }
+ elsif (/^q/) {
+ next if /^qqq /;
+ }
+ elsif (/^r/) {
+ next if /^read /;
+ next if /^revarp: Requesting/;
+ next if /^root [od]/;
+ }
+ elsif (/^s/) {
+ next if /^sc[0-9] at /;
+ next if /^sd[0-9] at /;
+ next if /^sd[0-9]: </;
+ next if /^si[0-9] at /;
+ next if /^si_getstatus/;
+ next if /^sk[0-9] at /;
+ next if /^skioctl/;
+ next if /^skopen/;
+ next if /^skprobe/;
+ next if /^skread/;
+ next if /^skwrite/;
+ next if /^sky[0-9] at /;
+ next if /^st[0-9] at /;
+ next if /^st0:.*load/;
+ next if /^stat1 = /;
+ next if /^syncing disks/;
+ next if /^syslogd: going down on signal 15/;
+ }
+ elsif (/^t/) {
+ next if /^timeout [0-9]/;
+ next if /^tm[0-9] at /;
+ next if /^tod[0-9] at /;
+ next if /^tv [0-9]/;
+ $pfx = '*' if /^trap address/;
+ }
+ elsif (/^u/) {
+ next if /^unit nsk/;
+ next if /^use one of/;
+ $pfx = '' if /^using/;
+ next if /^using [0-9]+ buffers/;
+ }
+ elsif (/^x/) {
+ next if /^xy[0-9] at /;
+ next if /^write [0-9]/;
+ next if /^xy[0-9]: </;
+ next if /^xyc[0-9] at /;
+ }
+ elsif (/^y/) {
+ next if /^yyy [0-9]/;
+ }
+ elsif (/^z/) {
+ next if /^zs[0-9] at /;
+ }
+ $pfx = '*' if /^[a-z]+:$/;
+ s/pid [0-9]+: //;
+ if (/last message repeated ([0-9]+) time/) {
+ $seen{$last} += $1;
+ next;
+ }
+ s/^/$pfx/ if $pfx;
+ unless ($seen{$_}++) {
+ push(@seen,$_);
+ }
+ $last = $_;
+}
+$max = tell(Msgs);
+
+open(tmp,'|sort >oldmsgs.tmp') || die "Can't create tmp file: $!\n";
+while ($_ = pop(@seen)) {
+ print tmp $_;
+}
+close(tmp);
+open(tmp,'oldmsgs.tmp') || die "Can't reopen tmp file: $!\n";
+while (<tmp>) {
+ if (/^nd:/) {
+ next if $seen{$_} < 20;
+ }
+ if (/NFS/) {
+ next if $seen{$_} < 20;
+ }
+ if (/no carrier/) {
+ next if $seen{$_} < 20;
+ }
+ if (/silo overflow/) {
+ next if $seen{$_} < 20;
+ }
+ print $seen{$_},":\t",$_;
+}
+
+print `rm -f oldmsgs.tmp 2>&1; echo $max > oldmsgs 2>&1`;
diff --git a/gnu/usr.bin/perl/eg/scan/scan_passwd b/gnu/usr.bin/perl/eg/scan/scan_passwd
new file mode 100644
index 0000000..f9c53c7d
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/scan/scan_passwd
@@ -0,0 +1,30 @@
+#!/usr/bin/perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/scan/scan_passwd,v 1.1.1.1 1993/08/23 21:29:45 nate Exp $
+
+# This scans passwd file for security holes.
+
+open(Pass,'/etc/passwd') || die "Can't open passwd file: $!\n";
+# $dotriv = (`date` =~ /^Mon/);
+$dotriv = 1;
+
+while (<Pass>) {
+ ($login,$pass,$uid,$gid,$gcos,$home,$shell) = split(/:/);
+ if ($shell eq '') {
+ print "Short: $_";
+ }
+ next if /^[+]/;
+ if ($pass eq '') {
+ if (index(":sync:lpq:+:", ":$login:") < 0) {
+ print "No pass: $login\t$gcos\n";
+ }
+ }
+ elsif ($dotriv && crypt($login,substr($pass,0,2)) eq $pass) {
+ print "Trivial: $login\t$gcos\n";
+ }
+ if ($uid == 0) {
+ if ($login !~ /^.?root$/ && $pass ne '*') {
+ print "Extra root: $_";
+ }
+ }
+}
diff --git a/gnu/usr.bin/perl/eg/scan/scan_ps b/gnu/usr.bin/perl/eg/scan/scan_ps
new file mode 100644
index 0000000..b0480d5
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/scan/scan_ps
@@ -0,0 +1,32 @@
+#!/usr/bin/perl -P
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/scan/scan_ps,v 1.1.1.1 1993/08/23 21:29:45 nate Exp $
+
+# This looks for looping processes.
+
+#if defined(mc300) || defined(mc500) || defined(mc700)
+open(Ps, '/bin/ps -el|') || die "scan_ps: can't run ps";
+
+while (<Ps>) {
+ next if /rwhod/;
+ print if index(' T', substr($_,62,1)) < 0;
+}
+#else
+open(Ps, '/bin/ps auxww|') || die "scan_ps: can't run ps";
+
+while (<Ps>) {
+ next if /dataserver/;
+ next if /nfsd/;
+ next if /update/;
+ next if /ypserv/;
+ next if /rwhod/;
+ next if /routed/;
+ next if /pagedaemon/;
+#ifdef vax
+ ($user,$pid,$cpu,$mem,$sz,$rss,$tt,$stat,$start,$time) = split;
+#else
+ ($user,$pid,$cpu,$mem,$sz,$rss,$tt,$stat,$time) = split;
+#endif
+ print if length($time) > 4;
+}
+#endif
diff --git a/gnu/usr.bin/perl/eg/scan/scan_sudo b/gnu/usr.bin/perl/eg/scan/scan_sudo
new file mode 100644
index 0000000..a95a609
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/scan/scan_sudo
@@ -0,0 +1,54 @@
+#!/usr/bin/perl -P
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/scan/scan_sudo,v 1.1.1.1 1993/08/23 21:29:45 nate Exp $
+
+# Analyze the sudo log.
+
+chdir('/usr/adm/private/memories') || die "Can't cd to memories: $!\n";
+
+if (open(Oldsudo,'oldsudo')) {
+ $maxpos = <Oldsudo>;
+ close Oldsudo;
+}
+else {
+ $maxpos = 0;
+ `echo 0 >oldsudo`;
+}
+
+unless (open(Sudo, '/usr/adm/sudo.log')) {
+ print "Somebody removed sudo.log!!!\n" if $maxpos;
+ exit 0;
+}
+
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat(Sudo);
+
+if ($size < $maxpos) {
+ $maxpos = 0;
+ print "Somebody reset sudo.log!!!\n";
+}
+
+seek(Sudo,$maxpos,0);
+
+while (<Sudo>) {
+ s/^.* :[ \t]+//;
+ s/ipcrm.*/ipcrm/;
+ s/kill.*/kill/;
+ unless ($seen{$_}++) {
+ push(@seen,$_);
+ }
+ $last = $_;
+}
+$max = tell(Sudo);
+
+open(tmp,'|sort >oldsudo.tmp') || die "Can't create tmp file: $!\n";
+while ($_ = pop(@seen)) {
+ print tmp $_;
+}
+close(tmp);
+open(tmp,'oldsudo.tmp') || die "Can't reopen tmp file: $!\n";
+while (<tmp>) {
+ print $seen{$_},":\t",$_;
+}
+
+print `(rm -f oldsudo.tmp; echo $max > oldsudo) 2>&1`;
diff --git a/gnu/usr.bin/perl/eg/scan/scan_suid b/gnu/usr.bin/perl/eg/scan/scan_suid
new file mode 100644
index 0000000..a730e0a
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/scan/scan_suid
@@ -0,0 +1,84 @@
+#!/usr/bin/perl -P
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/scan/scan_suid,v 1.1.1.1 1993/08/23 21:29:44 nate Exp $
+
+# Look for new setuid root files.
+
+chdir '/usr/adm/private/memories' || die "Can't cd to memories: $!\n";
+
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat('oldsuid');
+if ($nlink) {
+ $lasttime = $mtime;
+ $tmp = $ctime - $atime;
+ if ($tmp <= 0 || $tmp >= 10) {
+ print "WARNING: somebody has read oldsuid!\n";
+ }
+ $tmp = $ctime - $mtime;
+ if ($tmp <= 0 || $tmp >= 10) {
+ print "WARNING: somebody has modified oldsuid!!!\n";
+ }
+} else {
+ $lasttime = time - 60 * 60 * 24; # one day ago
+}
+$thistime = time;
+
+#if defined(mc300) || defined(mc500) || defined(mc700)
+open(Find, 'find / -perm -04000 -print |') ||
+ die "scan_find: can't run find";
+#else
+open(Find, 'find / \( -fstype nfs -prune \) -o -perm -04000 -ls |') ||
+ die "scan_find: can't run find";
+#endif
+
+open(suid, '>newsuid.tmp');
+
+while (<Find>) {
+
+#if defined(mc300) || defined(mc500) || defined(mc700)
+ $x = `/bin/ls -il $_`;
+ $_ = $x;
+ s/^ *//;
+ ($inode,$perm,$links,$owner,$group,$size,$month,$day,$time,$name)
+ = split;
+#else
+ s/^ *//;
+ ($inode,$blocks,$perm,$links,$owner,$group,$size,$month,$day,$time,$name)
+ = split;
+#endif
+
+ if ($perm =~ /[sS]/ && $owner eq 'root') {
+ ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat($name);
+ $foo = sprintf("%10s%3s %-8s %-8s%9s %3s %2s %s %s\n",
+ $perm,$links,$owner,$group,$size,$month,$day,$name,$inode);
+ print suid $foo;
+ if ($ctime > $lasttime) {
+ if ($ctime > $thistime) {
+ print "Future file: $foo";
+ }
+ else {
+ $ct .= $foo;
+ }
+ }
+ }
+}
+close(suid);
+
+print `sort +7 -8 newsuid.tmp >newsuid 2>&1`;
+$foo = `/bin/diff oldsuid newsuid 2>&1`;
+print "Differences in suid info:\n",$foo if $foo;
+print `mv oldsuid oldoldsuid 2>&1; mv newsuid oldsuid 2>&1`;
+print `touch oldsuid 2>&1;sleep 2 2>&1;chmod o+w oldsuid 2>&1`;
+print `rm -f newsuid.tmp 2>&1`;
+
+@ct = split(/\n/,$ct);
+$ct = '';
+$* = 1;
+while ($#ct >= 0) {
+ $tmp = shift(@ct);
+ unless ($foo =~ "^>.*$tmp\n") { $ct .= "$tmp\n"; }
+}
+
+print "Inode changed since last time:\n",$ct if $ct;
+
diff --git a/gnu/usr.bin/perl/eg/scan/scanner b/gnu/usr.bin/perl/eg/scan/scanner
new file mode 100644
index 0000000..f773e87
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/scan/scanner
@@ -0,0 +1,87 @@
+#!/usr/bin/perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/scan/scanner,v 1.1.1.1 1993/08/23 21:29:44 nate Exp $
+
+# This runs all the scan_* routines on all the machines in /etc/ghosts.
+# We run this every morning at about 6 am:
+
+# !/bin/sh
+# cd /usr/adm/private
+# decrypt scanner | perl >scan.out 2>&1
+# mail admin <scan.out
+
+# Note that the scan_* files should be encrypted with the key "-inquire", and
+# scanner should be encrypted somehow so that people can't find that key.
+# I leave it up to you to figure out how to unencrypt it before executing.
+
+$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin:/usr/ucb:.';
+
+$| = 1; # command buffering on stdout
+
+print "Subject: bizarre happenings\n\n";
+
+(chdir '/usr/adm/private') || die "Can't cd to /usr/adm/private: $!\n";
+
+if ($#ARGV >= 0) {
+ @scanlist = @ARGV;
+} else {
+ @scanlist = split(/[ \t\n]+/,`echo scan_*`);
+}
+
+scan: while ($scan = shift(@scanlist)) {
+ print "\n********** $scan **********\n";
+ $showhost++;
+
+ $systype = 'all';
+
+ open(ghosts, '/etc/ghosts') || die 'No /etc/ghosts file';
+
+ $one_of_these = ":$systype:";
+ if ($systype =~ s/\+/[+]/g) {
+ $one_of_these =~ s/\+/:/g;
+ }
+
+ line: while (<ghosts>) {
+ s/[ \t]*\n//;
+ if (!$_ || /^#/) {
+ next line;
+ }
+ if (/^([a-zA-Z_0-9]+)=(.+)/) {
+ $name = $1; $repl = $2;
+ $repl =~ s/\+/:/g;
+ $one_of_these =~ s/:$name:/:$repl:/;
+ next line;
+ }
+ @gh = split;
+ $host = $gh[0];
+ if ($showhost) { $showhost = "$host:\t"; }
+ class: while ($class = pop(gh)) {
+ if (index($one_of_these,":$class:") >=0) {
+ $iter = 0;
+ `exec crypt -inquire <$scan >.x 2>/dev/null`;
+ unless (open(scan,'.x')) {
+ print "Can't run $scan: $!\n";
+ next scan;
+ }
+ $cmd = <scan>;
+ unless ($cmd =~ s/#!(.*)\n/$1/) {
+ $cmd = '/usr/bin/perl';
+ }
+ close(scan);
+ if (open(PIPE,"exec rsh $host '$cmd' <.x|")) {
+ sleep(5);
+ unlink '.x';
+ while (<PIPE>) {
+ last if $iter++ > 1000; # must be looping
+ next if /^[0-9.]+u [0-9.]+s/;
+ print $showhost,$_;
+ }
+ close(PIPE);
+ } else {
+ print "(Can't execute rsh: $!)\n";
+ }
+ last class;
+ }
+ }
+ }
+}
diff --git a/gnu/usr.bin/perl/eg/server b/gnu/usr.bin/perl/eg/server
new file mode 100644
index 0000000..49a140a
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/server
@@ -0,0 +1,27 @@
+#!./perl
+
+$pat = 'S n C4 x8';
+$inet = 2;
+$echo = 7;
+$smtp = 25;
+$nntp = 119;
+
+$this = pack($pat,$inet,2345, 0,0,0,0);
+select(NS); $| = 1; select(stdout);
+
+if (socket(S,2,1,6)) { print "socket ok\n"; } else { die $!; }
+if (bind(S,$this)) { print "bind ok\n"; } else { die $!; }
+if (listen(S,5)) { print "listen ok\n"; } else { die $!; }
+for (;;) {
+ print "Listening again\n";
+ if ($addr = accept(NS,S)) { print "accept ok\n"; } else { die $!; }
+
+ @ary = unpack($pat,$addr);
+ $, = ' ';
+ print @ary; print "\n";
+
+ while (<NS>) {
+ print;
+ print NS;
+ }
+}
diff --git a/gnu/usr.bin/perl/eg/shmkill b/gnu/usr.bin/perl/eg/shmkill
new file mode 100644
index 0000000..e8d1b11
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/shmkill
@@ -0,0 +1,24 @@
+#!/usr/bin/perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/shmkill,v 1.1.1.1 1993/08/23 21:29:43 nate Exp $
+
+# A script to call from crontab periodically when people are leaving shared
+# memory sitting around unattached.
+
+open(ipcs,'ipcs -m -o|') || die "Can't run ipcs: $!";
+
+while (<ipcs>) {
+ $tmp = index($_,'NATTCH');
+ $pos = $tmp if $tmp >= 0;
+ if (/^m/) {
+ ($m,$id,$key,$mode,$owner,$group,$attach) = split;
+ if ($attach != substr($_,$pos,6)) {
+ die "Different ipcs format--can't parse!\n";
+ }
+ if ($attach == 0) {
+ push(@goners,'-m',$id);
+ }
+ }
+}
+
+exec 'ipcrm', @goners if $#goners >= 0;
diff --git a/gnu/usr.bin/perl/eg/sysvipc/README b/gnu/usr.bin/perl/eg/sysvipc/README
new file mode 100644
index 0000000..54094f1
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/sysvipc/README
@@ -0,0 +1,9 @@
+FYEnjoyment, here are the test scripts I used while implementing SysV
+IPC in Perl. Each of them must be run with the parameter "s" for
+"send" or "r" for "receive"; in each case, the receiver is the server
+and the sender is the client.
+
+--
+Chip Salzenberg at ComDev/TCT <chip@tct.uucp>, <uunet!ateng!tct!chip>
+
+
diff --git a/gnu/usr.bin/perl/eg/sysvipc/ipcmsg b/gnu/usr.bin/perl/eg/sysvipc/ipcmsg
new file mode 100644
index 0000000..317e027
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/sysvipc/ipcmsg
@@ -0,0 +1,47 @@
+#!/usr/bin/perl
+eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
+ if 0;
+
+require 'sys/ipc.ph';
+require 'sys/msg.ph';
+
+$| = 1;
+
+$mode = shift;
+die "usage: ipcmsg {r|s}\n" unless $mode =~ /^[rs]$/;
+$send = ($mode eq "s");
+
+$id = msgget(0x1234, ($send ? 0 : &IPC_CREAT) | 0644);
+die "Can't get message queue: $!\n" unless defined($id);
+print "message queue id: $id\n";
+
+if ($send) {
+ while (<STDIN>) {
+ chop;
+ unless (msgsnd($id, pack("LA*", $., $_), 0)) {
+ die "Can't send message: $!\n";
+ }
+ }
+}
+else {
+ $SIG{'INT'} = $SIG{'QUIT'} = "leave";
+ for (;;) {
+ unless (msgrcv($id, $_, 512, 0, 0)) {
+ die "Can't receive message: $!\n";
+ }
+ ($type, $message) = unpack("La*", $_);
+ printf "[%d] %s\n", $type, $message;
+ }
+}
+
+&leave;
+
+sub leave {
+ if (!$send) {
+ $x = msgctl($id, &IPC_RMID, 0);
+ if (!defined($x) || $x < 0) {
+ die "Can't remove message queue: $!\n";
+ }
+ }
+ exit;
+}
diff --git a/gnu/usr.bin/perl/eg/sysvipc/ipcsem b/gnu/usr.bin/perl/eg/sysvipc/ipcsem
new file mode 100644
index 0000000..d72a2dd
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/sysvipc/ipcsem
@@ -0,0 +1,46 @@
+#!/usr/bin/perl
+eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
+ if 0;
+
+require 'sys/ipc.ph';
+require 'sys/msg.ph';
+
+$| = 1;
+
+$mode = shift;
+die "usage: ipcmsg {r|s}\n" unless $mode =~ /^[rs]$/;
+$signal = ($mode eq "s");
+
+$id = semget(0x1234, 1, ($signal ? 0 : &IPC_CREAT) | 0644);
+die "Can't get semaphore: $!\n" unless defined($id);
+print "semaphore id: $id\n";
+
+if ($signal) {
+ while (<STDIN>) {
+ print "Signalling\n";
+ unless (semop($id, 0, pack("sss", 0, 1, 0))) {
+ die "Can't signal semaphore: $!\n";
+ }
+ }
+}
+else {
+ $SIG{'INT'} = $SIG{'QUIT'} = "leave";
+ for (;;) {
+ unless (semop($id, 0, pack("sss", 0, -1, 0))) {
+ die "Can't wait for semaphore: $!\n";
+ }
+ print "Unblocked\n";
+ }
+}
+
+&leave;
+
+sub leave {
+ if (!$signal) {
+ $x = semctl($id, 0, &IPC_RMID, 0);
+ if (!defined($x) || $x < 0) {
+ die "Can't remove semaphore: $!\n";
+ }
+ }
+ exit;
+}
diff --git a/gnu/usr.bin/perl/eg/sysvipc/ipcshm b/gnu/usr.bin/perl/eg/sysvipc/ipcshm
new file mode 100644
index 0000000..d40e46b
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/sysvipc/ipcshm
@@ -0,0 +1,50 @@
+#!/usr/bin/perl
+eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
+ if 0;
+
+require 'sys/ipc.ph';
+require 'sys/shm.ph';
+
+$| = 1;
+
+$mode = shift;
+die "usage: ipcshm {r|s}\n" unless $mode =~ /^[rs]$/;
+$send = ($mode eq "s");
+
+$SIZE = 32;
+$id = shmget(0x1234, $SIZE, ($send ? 0 : &IPC_CREAT) | 0644);
+die "Can't get shared memory: $!\n" unless defined($id);
+print "shared memory id: $id\n";
+
+if ($send) {
+ while (<STDIN>) {
+ chop;
+ unless (shmwrite($id, pack("La*", length($_), $_), 0, $SIZE)) {
+ die "Can't write to shared memory: $!\n";
+ }
+ }
+}
+else {
+ $SIG{'INT'} = $SIG{'QUIT'} = "leave";
+ for (;;) {
+ $_ = <STDIN>;
+ unless (shmread($id, $_, 0, $SIZE)) {
+ die "Can't read shared memory: $!\n";
+ }
+ $len = unpack("L", $_);
+ $message = substr($_, length(pack("L",0)), $len);
+ printf "[%d] %s\n", $len, $message;
+ }
+}
+
+&leave;
+
+sub leave {
+ if (!$send) {
+ $x = shmctl($id, &IPC_RMID, 0);
+ if (!defined($x) || $x < 0) {
+ die "Can't remove shared memory: $!\n";
+ }
+ }
+ exit;
+}
diff --git a/gnu/usr.bin/perl/eg/travesty b/gnu/usr.bin/perl/eg/travesty
new file mode 100644
index 0000000..7e6f983
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/travesty
@@ -0,0 +1,46 @@
+#!/usr/bin/perl
+
+while (<>) {
+ next if /^\./;
+ next if /^From / .. /^$/;
+ next if /^Path: / .. /^$/;
+ s/^\W+//;
+ push(@ary,split(' '));
+ while ($#ary > 1) {
+ $a = $p;
+ $p = $n;
+ $w = shift(@ary);
+ $n = $num{$w};
+ if ($n eq '') {
+ push(@word,$w);
+ $n = pack('S',$#word);
+ $num{$w} = $n;
+ }
+ $lookup{$a . $p} .= $n;
+ }
+}
+
+for (;;) {
+ $n = $lookup{$a . $p};
+ ($foo,$n) = each(lookup) if $n eq '';
+ $n = substr($n,int(rand(length($n))) & 0177776,2);
+ $a = $p;
+ $p = $n;
+ ($w) = unpack('S',$n);
+ $w = $word[$w];
+ $col += length($w) + 1;
+ if ($col >= 65) {
+ $col = 0;
+ print "\n";
+ }
+ else {
+ print ' ';
+ }
+ print $w;
+ if ($w =~ /\.$/) {
+ if (rand() < .1) {
+ print "\n";
+ $col = 80;
+ }
+ }
+}
diff --git a/gnu/usr.bin/perl/eg/van/empty b/gnu/usr.bin/perl/eg/van/empty
new file mode 100644
index 0000000..ee656e6
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/van/empty
@@ -0,0 +1,45 @@
+#!/usr/bin/perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/van/empty,v 1.1.1.1 1993/08/23 21:29:45 nate Exp $
+
+# This script empties a trashcan.
+
+$recursive = shift if $ARGV[0] eq '-r';
+
+@ARGV = '.' if $#ARGV < 0;
+
+chop($pwd = `pwd`);
+
+dir: foreach $dir (@ARGV) {
+ unless (chdir $dir) {
+ print stderr "Can't find directory $dir: $!\n";
+ next dir;
+ }
+ if ($recursive) {
+ do cmd('find . -name .deleted -exec /bin/rm -rf {} ;');
+ }
+ else {
+ if (-d '.deleted') {
+ do cmd('rm -rf .deleted');
+ }
+ else {
+ if ($dir eq '.' && $pwd =~ m|/\.deleted$|) {
+ chdir '..';
+ do cmd('rm -rf .deleted');
+ }
+ else {
+ print stderr "No trashcan found in directory $dir\n";
+ }
+ }
+ }
+}
+continue {
+ chdir $pwd;
+}
+
+# force direct execution with no shell
+
+sub cmd {
+ system split(' ',join(' ',@_));
+}
+
diff --git a/gnu/usr.bin/perl/eg/van/unvanish b/gnu/usr.bin/perl/eg/van/unvanish
new file mode 100644
index 0000000..5045982
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/van/unvanish
@@ -0,0 +1,66 @@
+#!/usr/bin/perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/van/unvanish,v 1.1.1.1 1993/08/23 21:29:45 nate Exp $
+
+sub it {
+ if ($olddir ne '.') {
+ chop($pwd = `pwd`) if $pwd eq '';
+ (chdir $olddir) || die "Directory $olddir is not accesible";
+ }
+ unless ($olddir eq '.deleted') {
+ if (-d '.deleted') {
+ chdir '.deleted' || die "Directory .deleted is not accesible";
+ }
+ else {
+ chop($pwd = `pwd`) if $pwd eq '';
+ die "Directory .deleted does not exist" unless $pwd =~ /\.deleted$/;
+ }
+ }
+ print `mv $startfiles$filelist..$force`;
+ if ($olddir ne '.') {
+ (chdir $pwd) || die "Can't get back to original directory $pwd: $!\n";
+ }
+}
+
+if ($#ARGV < 0) {
+ open(lastcmd,'.deleted/.lastcmd') ||
+ open(lastcmd,'.lastcmd') ||
+ die "No previous vanish in this dir";
+ $ARGV = <lastcmd>;
+ close(lastcmd);
+ @ARGV = split(/[\n ]+/,$ARGV);
+}
+
+while ($ARGV[0] =~ /^-/) {
+ $_ = shift;
+ /^-f/ && ($force = ' >/dev/null 2>&1');
+ /^-i/ && ($interactive = 1);
+ if (/^-+$/) {
+ $startfiles = '- ';
+ last;
+ }
+}
+
+while ($file = shift) {
+ if ($file =~ s|^(.*)/||) {
+ $dir = $1;
+ }
+ else {
+ $dir = '.';
+ }
+
+ if ($dir ne $olddir) {
+ do it() if $olddir;
+ $olddir = $dir;
+ }
+
+ if ($interactive) {
+ print "unvanish: restore $dir/$file? ";
+ next unless <stdin> =~ /^y/i;
+ }
+
+ $filelist .= $file; $filelist .= ' ';
+
+}
+
+do it() if $olddir;
diff --git a/gnu/usr.bin/perl/eg/van/vanexp b/gnu/usr.bin/perl/eg/van/vanexp
new file mode 100644
index 0000000..79b7885
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/van/vanexp
@@ -0,0 +1,21 @@
+#!/usr/bin/perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/van/vanexp,v 1.1.1.1 1993/08/23 21:29:45 nate Exp $
+
+# This is for running from a find at night to expire old .deleteds
+
+$can = $ARGV[0];
+
+exit 1 unless $can =~ /.deleted$/;
+
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat($can);
+
+exit 0 unless $size;
+
+if (time - $mtime > 2 * 24 * 60 * 60) {
+ `/bin/rm -rf $can`;
+}
+else {
+ `find $can -ctime +2 -exec rm -f {} \;`;
+}
diff --git a/gnu/usr.bin/perl/eg/van/vanish b/gnu/usr.bin/perl/eg/van/vanish
new file mode 100644
index 0000000..b79776a
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/van/vanish
@@ -0,0 +1,65 @@
+#!/usr/bin/perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/eg/van/vanish,v 1.1.1.1 1993/08/23 21:29:45 nate Exp $
+
+sub it {
+ if ($olddir ne '.') {
+ chop($pwd = `pwd`) if $pwd eq '';
+ (chdir $olddir) || die "Directory $olddir is not accesible";
+ }
+ if (!-d .deleted) {
+ print `mkdir .deleted; chmod 775 .deleted`;
+ die "You can't remove files from $olddir" if $?;
+ }
+ $filelist =~ s/ $//;
+ $filelist =~ s/#/\\#/g;
+ if ($filelist !~ /^[ \t]*$/) {
+ open(lastcmd,'>.deleted/.lastcmd');
+ print lastcmd $filelist,"\n";
+ close(lastcmd);
+ print `/bin/mv $startfiles$filelist .deleted$force`;
+ }
+ if ($olddir ne '.') {
+ (chdir $pwd) || die "Can't get back to original directory $pwd: $!\n";
+ }
+}
+
+while ($ARGV[0] =~ /^-/) {
+ $_ = shift;
+ /^-f/ && ($force = ' >/dev/null 2>&1');
+ /^-i/ && ($interactive = 1);
+ if (/^-+$/) {
+ $startfiles = '- ';
+ last;
+ }
+}
+
+chop($pwd = `pwd`);
+
+while ($file = shift) {
+ if ($file =~ s|^(.*)/||) {
+ $dir = $1;
+ }
+ else {
+ $dir = '.';
+ }
+
+ if ($interactive) {
+ print "vanish: remove $dir/$file? ";
+ next unless <stdin> =~ /^y/i;
+ }
+
+ if ($file eq '.deleted') {
+ print stderr "To delete .deleted (the trashcan) use the 'empty' command.\n";
+ next;
+ }
+
+ if ($dir ne $olddir) {
+ do it() if $olddir;
+ $olddir = $dir;
+ }
+
+ $filelist .= $file; $filelist .= ' ';
+}
+
+do it() if $olddir;
diff --git a/gnu/usr.bin/perl/eg/who b/gnu/usr.bin/perl/eg/who
new file mode 100644
index 0000000..ac15246
--- /dev/null
+++ b/gnu/usr.bin/perl/eg/who
@@ -0,0 +1,13 @@
+#!/usr/bin/perl
+# This assumes your /etc/utmp file looks like ours
+open(UTMP,'/etc/utmp');
+@mo = (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec);
+while (read(UTMP,$utmp,36)) {
+ ($line,$name,$host,$time) = unpack('A8A8A16l',$utmp);
+ if ($name) {
+ $host = "($host)" if ord($host);
+ ($sec,$min,$hour,$mday,$mon) = localtime($time);
+ printf "%-9s%-8s%s %2d %02d:%02d %s\n",
+ $name,$line,$mo[$mon],$mday,$hour,$min,$host;
+ }
+}
diff --git a/gnu/usr.bin/perl/emacs/perl-mode.el b/gnu/usr.bin/perl/emacs/perl-mode.el
new file mode 100644
index 0000000..cb6195d
--- /dev/null
+++ b/gnu/usr.bin/perl/emacs/perl-mode.el
@@ -0,0 +1,631 @@
+;; Perl code editing commands for GNU Emacs
+;; Copyright (C) 1990 William F. Mann
+;; Adapted from C code editing commands 'c-mode.el', Copyright 1987 by the
+;; Free Software Foundation, under terms of its General Public License.
+
+;; This file may be made part of GNU Emacs at the option of the FSF, or
+;; of the perl distribution at the option of Larry Wall.
+
+;; This code is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY. No author or distributor
+;; accepts responsibility to anyone for the consequences of using it
+;; or for whether it serves any particular purpose or works at all,
+;; unless he says so in writing. Refer to the GNU Emacs General Public
+;; License for full details.
+
+;; Everyone is granted permission to copy, modify and redistribute
+;; this code, but only under the conditions described in the
+;; GNU Emacs General Public License. A copy of this license is
+;; supposed to have been given to you along with GNU Emacs so you
+;; can know your rights and responsibilities. It should be in a
+;; file named COPYING. Among other things, the copyright notice
+;; and this notice must be preserved on all copies.
+
+;; To enter perl-mode automatically, add (autoload 'perl-mode "perl-mode")
+;; to your .emacs file and change the first line of your perl script to:
+;; #!/usr/bin/perl -- # -*-Perl-*-
+;; With argments to perl:
+;; #!/usr/bin/perl -P- # -*-Perl-*-
+;; To handle files included with do 'filename.pl';, add something like
+;; (setq auto-mode-alist (append (list (cons "\\.pl$" 'perl-mode))
+;; auto-mode-alist))
+;; to your .emacs file; otherwise the .pl suffix defaults to prolog-mode.
+
+;; This code is based on the 18.53 version c-mode.el, with extensive
+;; rewriting. Most of the features of c-mode survived intact.
+
+;; I added a new feature which adds functionality to TAB; it is controlled
+;; by the variable perl-tab-to-comment. With it enabled, TAB does the
+;; first thing it can from the following list: change the indentation;
+;; move past leading white space; delete an empty comment; reindent a
+;; comment; move to end of line; create an empty comment; tell you that
+;; the line ends in a quoted string, or has a # which should be a \#.
+
+;; If your machine is slow, you may want to remove some of the bindings
+;; to electric-perl-terminator. I changed the indenting defaults to be
+;; what Larry Wall uses in perl/lib, but left in all the options.
+
+;; I also tuned a few things: comments and labels starting in column
+;; zero are left there by indent-perl-exp; perl-beginning-of-function
+;; goes back to the first open brace/paren in column zero, the open brace
+;; in 'sub ... {', or the equal sign in 'format ... ='; indent-perl-exp
+;; (meta-^q) indents from the current line through the close of the next
+;; brace/paren, so you don't need to start exactly at a brace or paren.
+
+;; It may be good style to put a set of redundant braces around your
+;; main program. This will let you reindent it with meta-^q.
+
+;; Known problems (these are all caused by limitations in the elisp
+;; parsing routine (parse-partial-sexp), which was not designed for such
+;; a rich language; writing a more suitable parser would be a big job):
+;; 1) Regular expression delimitors do not act as quotes, so special
+;; characters such as `'"#:;[](){} may need to be backslashed
+;; in regular expressions and in both parts of s/// and tr///.
+;; 2) The globbing syntax <pattern> is not recognized, so special
+;; characters in the pattern string must be backslashed.
+;; 3) The q, qq, and << quoting operators are not recognized; see below.
+;; 4) \ (backslash) always quotes the next character, so '\' is
+;; treated as the start of a string. Use "\\" as a work-around.
+;; 5) To make variables such a $' and $#array work, perl-mode treats
+;; $ just like backslash, so '$' is the same as problem 5.
+;; 6) Unfortunately, treating $ like \ makes ${var} be treated as an
+;; unmatched }. See below.
+;; 7) When ' (quote) is used as a package name separator, perl-mode
+;; doesn't understand, and thinks it is seeing a quoted string.
+
+;; Here are some ugly tricks to bypass some of these problems: the perl
+;; expression /`/ (that's a back-tick) usually evaluates harmlessly,
+;; but will trick perl-mode into starting a quoted string, which
+;; can be ended with another /`/. Assuming you have no embedded
+;; back-ticks, this can used to help solve problem 3:
+;;
+;; /`/; $ugly = q?"'$?; /`/;
+;;
+;; To solve problem 6, add a /{/; before each use of ${var}:
+;; /{/; while (<${glob_me}>) ...
+;;
+;; Problem 7 is even worse, but this 'fix' does work :-(
+;; $DB'stop#'
+;; [$DB'line#'
+;; ] =~ s/;9$//;
+
+
+(defvar perl-mode-abbrev-table nil
+ "Abbrev table in use in perl-mode buffers.")
+(define-abbrev-table 'perl-mode-abbrev-table ())
+
+(defvar perl-mode-map ()
+ "Keymap used in Perl mode.")
+(if perl-mode-map
+ ()
+ (setq perl-mode-map (make-sparse-keymap))
+ (define-key perl-mode-map "{" 'electric-perl-terminator)
+ (define-key perl-mode-map "}" 'electric-perl-terminator)
+ (define-key perl-mode-map ";" 'electric-perl-terminator)
+ (define-key perl-mode-map ":" 'electric-perl-terminator)
+ (define-key perl-mode-map "\e\C-a" 'perl-beginning-of-function)
+ (define-key perl-mode-map "\e\C-e" 'perl-end-of-function)
+ (define-key perl-mode-map "\e\C-h" 'mark-perl-function)
+ (define-key perl-mode-map "\e\C-q" 'indent-perl-exp)
+ (define-key perl-mode-map "\177" 'backward-delete-char-untabify)
+ (define-key perl-mode-map "\t" 'perl-indent-command))
+
+(autoload 'c-macro-expand "cmacexp"
+ "Display the result of expanding all C macros occurring in the region.
+The expansion is entirely correct because it uses the C preprocessor."
+ t)
+
+(defvar perl-mode-syntax-table nil
+ "Syntax table in use in perl-mode buffers.")
+
+(if perl-mode-syntax-table
+ ()
+ (setq perl-mode-syntax-table (make-syntax-table (standard-syntax-table)))
+ (modify-syntax-entry ?\n ">" perl-mode-syntax-table)
+ (modify-syntax-entry ?# "<" perl-mode-syntax-table)
+ (modify-syntax-entry ?$ "/" perl-mode-syntax-table)
+ (modify-syntax-entry ?% "." perl-mode-syntax-table)
+ (modify-syntax-entry ?& "." perl-mode-syntax-table)
+ (modify-syntax-entry ?\' "\"" perl-mode-syntax-table)
+ (modify-syntax-entry ?* "." perl-mode-syntax-table)
+ (modify-syntax-entry ?+ "." perl-mode-syntax-table)
+ (modify-syntax-entry ?- "." perl-mode-syntax-table)
+ (modify-syntax-entry ?/ "." perl-mode-syntax-table)
+ (modify-syntax-entry ?< "." perl-mode-syntax-table)
+ (modify-syntax-entry ?= "." perl-mode-syntax-table)
+ (modify-syntax-entry ?> "." perl-mode-syntax-table)
+ (modify-syntax-entry ?\\ "\\" perl-mode-syntax-table)
+ (modify-syntax-entry ?` "\"" perl-mode-syntax-table)
+ (modify-syntax-entry ?| "." perl-mode-syntax-table)
+)
+
+(defconst perl-indent-level 4
+ "*Indentation of Perl statements with respect to containing block.")
+(defconst perl-continued-statement-offset 4
+ "*Extra indent for lines not starting new statements.")
+(defconst perl-continued-brace-offset -4
+ "*Extra indent for substatements that start with open-braces.
+This is in addition to perl-continued-statement-offset.")
+(defconst perl-brace-offset 0
+ "*Extra indentation for braces, compared with other text in same context.")
+(defconst perl-brace-imaginary-offset 0
+ "*Imagined indentation of an open brace that actually follows a statement.")
+(defconst perl-label-offset -2
+ "*Offset of Perl label lines relative to usual indentation.")
+
+(defconst perl-tab-always-indent t
+ "*Non-nil means TAB in Perl mode should always indent the current line,
+regardless of where in the line point is when the TAB command is used.")
+
+(defconst perl-tab-to-comment t
+ "*Non-nil means that for lines which don't need indenting, TAB will
+either indent an existing comment, move to end-of-line, or if at end-of-line
+already, create a new comment.")
+
+(defconst perl-nochange ";?#\\|\f\\|\\s(\\|\\(\\w\\|\\s_\\)+:"
+ "*Lines starting with this regular expression will not be auto-indented.")
+
+(defun perl-mode ()
+ "Major mode for editing Perl code.
+Expression and list commands understand all Perl brackets.
+Tab indents for Perl code.
+Comments are delimited with # ... \\n.
+Paragraphs are separated by blank lines only.
+Delete converts tabs to spaces as it moves back.
+\\{perl-mode-map}
+Variables controlling indentation style:
+ perl-tab-always-indent
+ Non-nil means TAB in Perl mode should always indent the current line,
+ regardless of where in the line point is when the TAB command is used.
+ perl-tab-to-comment
+ Non-nil means that for lines which don't need indenting, TAB will
+ either delete an empty comment, indent an existing comment, move
+ to end-of-line, or if at end-of-line already, create a new comment.
+ perl-nochange
+ Lines starting with this regular expression will not be auto-indented.
+ perl-indent-level
+ Indentation of Perl statements within surrounding block.
+ The surrounding block's indentation is the indentation
+ of the line on which the open-brace appears.
+ perl-continued-statement-offset
+ Extra indentation given to a substatement, such as the
+ then-clause of an if or body of a while.
+ perl-continued-brace-offset
+ Extra indentation given to a brace that starts a substatement.
+ This is in addition to perl-continued-statement-offset.
+ perl-brace-offset
+ Extra indentation for line if it starts with an open brace.
+ perl-brace-imaginary-offset
+ An open brace following other text is treated as if it were
+ this far to the right of the start of its line.
+ perl-label-offset
+ Extra indentation for line that is a label.
+
+Various indentation styles: K&R BSD BLK GNU LW
+ perl-indent-level 5 8 0 2 4
+ perl-continued-statement-offset 5 8 4 2 4
+ perl-continued-brace-offset 0 0 0 0 -4
+ perl-brace-offset -5 -8 0 0 0
+ perl-brace-imaginary-offset 0 0 4 0 0
+ perl-label-offset -5 -8 -2 -2 -2
+
+Turning on Perl mode calls the value of the variable perl-mode-hook with no
+args, if that value is non-nil."
+ (interactive)
+ (kill-all-local-variables)
+ (use-local-map perl-mode-map)
+ (setq major-mode 'perl-mode)
+ (setq mode-name "Perl")
+ (setq local-abbrev-table perl-mode-abbrev-table)
+ (set-syntax-table perl-mode-syntax-table)
+ (make-local-variable 'paragraph-start)
+ (setq paragraph-start (concat "^$\\|" page-delimiter))
+ (make-local-variable 'paragraph-separate)
+ (setq paragraph-separate paragraph-start)
+ (make-local-variable 'paragraph-ignore-fill-prefix)
+ (setq paragraph-ignore-fill-prefix t)
+ (make-local-variable 'indent-line-function)
+ (setq indent-line-function 'perl-indent-line)
+ (make-local-variable 'require-final-newline)
+ (setq require-final-newline t)
+ (make-local-variable 'comment-start)
+ (setq comment-start "# ")
+ (make-local-variable 'comment-end)
+ (setq comment-end "")
+ (make-local-variable 'comment-column)
+ (setq comment-column 32)
+ (make-local-variable 'comment-start-skip)
+ (setq comment-start-skip "\\(^\\|\\s-\\);?#+ *")
+ (make-local-variable 'comment-indent-hook)
+ (setq comment-indent-hook 'perl-comment-indent)
+ (make-local-variable 'parse-sexp-ignore-comments)
+ (setq parse-sexp-ignore-comments nil)
+ (run-hooks 'perl-mode-hook))
+
+;; This is used by indent-for-comment
+;; to decide how much to indent a comment in Perl code
+;; based on its context.
+(defun perl-comment-indent ()
+ (if (and (bolp) (not (eolp)))
+ 0 ;Existing comment at bol stays there.
+ (save-excursion
+ (skip-chars-backward " \t")
+ (max (1+ (current-column)) ;Else indent at comment column
+ comment-column)))) ; except leave at least one space.
+
+(defun electric-perl-terminator (arg)
+ "Insert character. If at end-of-line, and not in a comment or a quote,
+correct the line's indentation."
+ (interactive "P")
+ (let ((insertpos (point)))
+ (and (not arg) ; decide whether to indent
+ (eolp)
+ (save-excursion
+ (beginning-of-line)
+ (and (not ; eliminate comments quickly
+ (re-search-forward comment-start-skip insertpos t))
+ (or (/= last-command-char ?:)
+ ;; Colon is special only after a label ....
+ (looking-at "\\s-*\\(\\w\\|\\s_\\)+$"))
+ (let ((pps (parse-partial-sexp
+ (perl-beginning-of-function) insertpos)))
+ (not (or (nth 3 pps) (nth 4 pps) (nth 5 pps))))))
+ (progn ; must insert, indent, delete
+ (insert-char last-command-char 1)
+ (perl-indent-line)
+ (delete-char -1))))
+ (self-insert-command (prefix-numeric-value arg)))
+
+;; not used anymore, but may be useful someday:
+;;(defun perl-inside-parens-p ()
+;; (condition-case ()
+;; (save-excursion
+;; (save-restriction
+;; (narrow-to-region (point)
+;; (perl-beginning-of-function))
+;; (goto-char (point-max))
+;; (= (char-after (or (scan-lists (point) -1 1) (point-min))) ?\()))
+;; (error nil)))
+
+(defun perl-indent-command (&optional arg)
+ "Indent current line as Perl code, or optionally, insert a tab character.
+
+With an argument, indent the current line, regardless of other options.
+
+If perl-tab-always-indent is nil and point is not in the indentation
+area at the beginning of the line, simply insert a tab.
+
+Otherwise, indent the current line. If point was within the indentation
+area it is moved to the end of the indentation area. If the line was
+already indented properly and point was not within the indentation area,
+and if perl-tab-to-comment is non-nil (the default), then do the first
+possible action from the following list:
+
+ 1) delete an empty comment
+ 2) move forward to start of comment, indenting if necessary
+ 3) move forward to end of line
+ 4) create an empty comment
+ 5) move backward to start of comment, indenting if necessary."
+ (interactive "P")
+ (if arg ; If arg, just indent this line
+ (perl-indent-line "\f")
+ (if (and (not perl-tab-always-indent)
+ (<= (current-column) (current-indentation)))
+ (insert-tab)
+ (let (bof lsexp delta (oldpnt (point)))
+ (beginning-of-line)
+ (setq lsexp (point))
+ (setq bof (perl-beginning-of-function))
+ (goto-char oldpnt)
+ (setq delta (perl-indent-line "\f\\|;?#" bof))
+ (and perl-tab-to-comment
+ (= oldpnt (point)) ; done if point moved
+ (if (listp delta) ; if line starts in a quoted string
+ (setq lsexp (or (nth 2 delta) bof))
+ (= delta 0)) ; done if indenting occurred
+ (let (eol state)
+ (end-of-line)
+ (setq eol (point))
+ (if (= (char-after bof) ?=)
+ (if (= oldpnt eol)
+ (message "In a format statement"))
+ (setq state (parse-partial-sexp lsexp eol))
+ (if (nth 3 state)
+ (if (= oldpnt eol) ; already at eol in a string
+ (message "In a string which starts with a %c."
+ (nth 3 state)))
+ (if (not (nth 4 state))
+ (if (= oldpnt eol) ; no comment, create one?
+ (indent-for-comment))
+ (beginning-of-line)
+ (if (re-search-forward comment-start-skip eol 'move)
+ (if (eolp)
+ (progn ; kill existing comment
+ (goto-char (match-beginning 0))
+ (skip-chars-backward " \t")
+ (kill-region (point) eol))
+ (if (or (< oldpnt (point)) (= oldpnt eol))
+ (indent-for-comment) ; indent existing comment
+ (end-of-line)))
+ (if (/= oldpnt eol)
+ (end-of-line)
+ (message "Use backslash to quote # characters.")
+ (ding t))))))))))))
+
+(defun perl-indent-line (&optional nochange parse-start)
+ "Indent current line as Perl code. Return the amount the indentation
+changed by, or (parse-state) if line starts in a quoted string."
+ (let ((case-fold-search nil)
+ (pos (- (point-max) (point)))
+ (bof (or parse-start (save-excursion (perl-beginning-of-function))))
+ beg indent shift-amt)
+ (beginning-of-line)
+ (setq beg (point))
+ (setq shift-amt
+ (cond ((= (char-after bof) ?=) 0)
+ ((listp (setq indent (calculate-perl-indent bof))) indent)
+ ((looking-at (or nochange perl-nochange)) 0)
+ (t
+ (skip-chars-forward " \t\f")
+ (cond ((looking-at "\\(\\w\\|\\s_\\)+:")
+ (setq indent (max 1 (+ indent perl-label-offset))))
+ ((= (following-char) ?})
+ (setq indent (- indent perl-indent-level)))
+ ((= (following-char) ?{)
+ (setq indent (+ indent perl-brace-offset))))
+ (- indent (current-column)))))
+ (skip-chars-forward " \t\f")
+ (if (and (numberp shift-amt) (/= 0 shift-amt))
+ (progn (delete-region beg (point))
+ (indent-to indent)))
+ ;; If initial point was within line's indentation,
+ ;; position after the indentation. Else stay at same point in text.
+ (if (> (- (point-max) pos) (point))
+ (goto-char (- (point-max) pos)))
+ shift-amt))
+
+(defun calculate-perl-indent (&optional parse-start)
+ "Return appropriate indentation for current line as Perl code.
+In usual case returns an integer: the column to indent to.
+Returns (parse-state) if line starts inside a string."
+ (save-excursion
+ (beginning-of-line)
+ (let ((indent-point (point))
+ (case-fold-search nil)
+ (colon-line-end 0)
+ state containing-sexp)
+ (if parse-start ;used to avoid searching
+ (goto-char parse-start)
+ (perl-beginning-of-function))
+ (while (< (point) indent-point) ;repeat until right sexp
+ (setq parse-start (point))
+ (setq state (parse-partial-sexp (point) indent-point 0))
+; state = (depth_in_parens innermost_containing_list last_complete_sexp
+; string_terminator_or_nil inside_commentp following_quotep
+; minimum_paren-depth_this_scan)
+; Parsing stops if depth in parentheses becomes equal to third arg.
+ (setq containing-sexp (nth 1 state)))
+ (cond ((nth 3 state) state) ; In a quoted string?
+ ((null containing-sexp) ; Line is at top level.
+ (skip-chars-forward " \t\f")
+ (if (= (following-char) ?{)
+ 0 ; move to beginning of line if it starts a function body
+ ;; indent a little if this is a continuation line
+ (perl-backward-to-noncomment)
+ (if (or (bobp)
+ (memq (preceding-char) '(?\; ?\})))
+ 0 perl-continued-statement-offset)))
+ ((/= (char-after containing-sexp) ?{)
+ ;; line is expression, not statement:
+ ;; indent to just after the surrounding open.
+ (goto-char (1+ containing-sexp))
+ (current-column))
+ (t
+ ;; Statement level. Is it a continuation or a new statement?
+ ;; Find previous non-comment character.
+ (perl-backward-to-noncomment)
+ ;; Back up over label lines, since they don't
+ ;; affect whether our line is a continuation.
+ (while (or (eq (preceding-char) ?\,)
+ (and (eq (preceding-char) ?:)
+ (memq (char-syntax (char-after (- (point) 2)))
+ '(?w ?_))))
+ (if (eq (preceding-char) ?\,)
+ (perl-backward-to-start-of-continued-exp containing-sexp))
+ (beginning-of-line)
+ (perl-backward-to-noncomment))
+ ;; Now we get the answer.
+ (if (not (memq (preceding-char) '(?\; ?\} ?\{)))
+ ;; This line is continuation of preceding line's statement;
+ ;; indent perl-continued-statement-offset more than the
+ ;; previous line of the statement.
+ (progn
+ (perl-backward-to-start-of-continued-exp containing-sexp)
+ (+ perl-continued-statement-offset (current-column)
+ (if (save-excursion (goto-char indent-point)
+ (looking-at "[ \t]*{"))
+ perl-continued-brace-offset 0)))
+ ;; This line starts a new statement.
+ ;; Position at last unclosed open.
+ (goto-char containing-sexp)
+ (or
+ ;; If open paren is in col 0, close brace is special
+ (and (bolp)
+ (save-excursion (goto-char indent-point)
+ (looking-at "[ \t]*}"))
+ perl-indent-level)
+ ;; Is line first statement after an open-brace?
+ ;; If no, find that first statement and indent like it.
+ (save-excursion
+ (forward-char 1)
+ ;; Skip over comments and labels following openbrace.
+ (while (progn
+ (skip-chars-forward " \t\f\n")
+ (cond ((looking-at ";?#")
+ (forward-line 1) t)
+ ((looking-at "\\(\\w\\|\\s_\\)+:")
+ (save-excursion
+ (end-of-line)
+ (setq colon-line-end (point)))
+ (search-forward ":")))))
+ ;; The first following code counts
+ ;; if it is before the line we want to indent.
+ (and (< (point) indent-point)
+ (if (> colon-line-end (point))
+ (- (current-indentation) perl-label-offset)
+ (current-column))))
+ ;; If no previous statement,
+ ;; indent it relative to line brace is on.
+ ;; For open paren in column zero, don't let statement
+ ;; start there too. If perl-indent-level is zero,
+ ;; use perl-brace-offset + perl-continued-statement-offset
+ ;; For open-braces not the first thing in a line,
+ ;; add in perl-brace-imaginary-offset.
+ (+ (if (and (bolp) (zerop perl-indent-level))
+ (+ perl-brace-offset perl-continued-statement-offset)
+ perl-indent-level)
+ ;; Move back over whitespace before the openbrace.
+ ;; If openbrace is not first nonwhite thing on the line,
+ ;; add the perl-brace-imaginary-offset.
+ (progn (skip-chars-backward " \t")
+ (if (bolp) 0 perl-brace-imaginary-offset))
+ ;; If the openbrace is preceded by a parenthesized exp,
+ ;; move to the beginning of that;
+ ;; possibly a different line
+ (progn
+ (if (eq (preceding-char) ?\))
+ (forward-sexp -1))
+ ;; Get initial indentation of the line we are on.
+ (current-indentation))))))))))
+
+(defun perl-backward-to-noncomment ()
+ "Move point backward to after the first non-white-space, skipping comments."
+ (interactive)
+ (let (opoint stop)
+ (while (not stop)
+ (setq opoint (point))
+ (beginning-of-line)
+ (if (re-search-forward comment-start-skip opoint 'move 1)
+ (progn (goto-char (match-end 1))
+ (skip-chars-forward ";")))
+ (skip-chars-backward " \t\f")
+ (setq stop (or (bobp)
+ (not (bolp))
+ (forward-char -1))))))
+
+(defun perl-backward-to-start-of-continued-exp (lim)
+ (if (= (preceding-char) ?\))
+ (forward-sexp -1))
+ (beginning-of-line)
+ (if (<= (point) lim)
+ (goto-char (1+ lim)))
+ (skip-chars-forward " \t\f"))
+
+;; note: this may be slower than the c-mode version, but I can understand it.
+(defun indent-perl-exp ()
+ "Indent each line of the Perl grouping following point."
+ (interactive)
+ (let* ((case-fold-search nil)
+ (oldpnt (point-marker))
+ (bof-mark (save-excursion
+ (end-of-line 2)
+ (perl-beginning-of-function)
+ (point-marker)))
+ eol last-mark lsexp-mark delta)
+ (if (= (char-after (marker-position bof-mark)) ?=)
+ (message "Can't indent a format statement")
+ (message "Indenting Perl expression...")
+ (save-excursion (end-of-line) (setq eol (point)))
+ (save-excursion ; locate matching close paren
+ (while (and (not (eobp)) (<= (point) eol))
+ (parse-partial-sexp (point) (point-max) 0))
+ (setq last-mark (point-marker)))
+ (setq lsexp-mark bof-mark)
+ (beginning-of-line)
+ (while (< (point) (marker-position last-mark))
+ (setq delta (perl-indent-line nil (marker-position bof-mark)))
+ (if (numberp delta) ; unquoted start-of-line?
+ (progn
+ (if (eolp)
+ (delete-horizontal-space))
+ (setq lsexp-mark (point-marker))))
+ (end-of-line)
+ (setq eol (point))
+ (if (nth 4 (parse-partial-sexp (marker-position lsexp-mark) eol))
+ (progn ; line ends in a comment
+ (beginning-of-line)
+ (if (or (not (looking-at "\\s-*;?#"))
+ (listp delta)
+ (and (/= 0 delta)
+ (= (- (current-indentation) delta) comment-column)))
+ (if (re-search-forward comment-start-skip eol t)
+ (indent-for-comment))))) ; indent existing comment
+ (forward-line 1))
+ (goto-char (marker-position oldpnt))
+ (message "Indenting Perl expression...done"))))
+
+(defun perl-beginning-of-function (&optional arg)
+ "Move backward to next beginning-of-function, or as far as possible.
+With argument, repeat that many times; negative args move forward.
+Returns new value of point in all cases."
+ (interactive "p")
+ (or arg (setq arg 1))
+ (if (< arg 0) (forward-char 1))
+ (and (/= arg 0)
+ (re-search-backward "^\\s(\\|^\\s-*sub\\b[^{]+{\\|^\\s-*format\\b[^=]*=\\|^\\."
+ nil 'move arg)
+ (goto-char (1- (match-end 0))))
+ (point))
+
+;; note: this routine is adapted directly from emacs lisp.el, end-of-defun;
+;; no bugs have been removed :-)
+(defun perl-end-of-function (&optional arg)
+ "Move forward to next end-of-function.
+The end of a function is found by moving forward from the beginning of one.
+With argument, repeat that many times; negative args move backward."
+ (interactive "p")
+ (or arg (setq arg 1))
+ (let ((first t))
+ (while (and (> arg 0) (< (point) (point-max)))
+ (let ((pos (point)) npos)
+ (while (progn
+ (if (and first
+ (progn
+ (forward-char 1)
+ (perl-beginning-of-function 1)
+ (not (bobp))))
+ nil
+ (or (bobp) (forward-char -1))
+ (perl-beginning-of-function -1))
+ (setq first nil)
+ (forward-list 1)
+ (skip-chars-forward " \t")
+ (if (looking-at "[#\n]")
+ (forward-line 1))
+ (<= (point) pos))))
+ (setq arg (1- arg)))
+ (while (< arg 0)
+ (let ((pos (point)))
+ (perl-beginning-of-function 1)
+ (forward-sexp 1)
+ (forward-line 1)
+ (if (>= (point) pos)
+ (if (progn (perl-beginning-of-function 2) (not (bobp)))
+ (progn
+ (forward-list 1)
+ (skip-chars-forward " \t")
+ (if (looking-at "[#\n]")
+ (forward-line 1)))
+ (goto-char (point-min)))))
+ (setq arg (1+ arg)))))
+
+(defun mark-perl-function ()
+ "Put mark at end of Perl function, point at beginning."
+ (interactive)
+ (push-mark (point))
+ (perl-end-of-function)
+ (push-mark (point))
+ (perl-beginning-of-function)
+ (backward-paragraph))
+
+;;;;;;;; That's all, folks! ;;;;;;;;;
diff --git a/gnu/usr.bin/perl/emacs/perldb.el b/gnu/usr.bin/perl/emacs/perldb.el
new file mode 100644
index 0000000..66951be
--- /dev/null
+++ b/gnu/usr.bin/perl/emacs/perldb.el
@@ -0,0 +1,423 @@
+;; Run perl -d under Emacs
+;; Based on gdb.el, as written by W. Schelter, and modified by rms.
+;; Modified for Perl by Ray Lischner (uunet!mntgfx!lisch), Nov 1990.
+
+;; This file is part of GNU Emacs.
+;; Copyright (C) 1988,1990 Free Software Foundation, Inc.
+
+;; GNU Emacs is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
+;; to anyone for the consequences of using it or for whether it serves
+;; any particular purpose or works at all, unless he says so in writing.
+;; Refer to the GNU Emacs General Public License for full details.
+
+;; Everyone is granted permission to copy, modify and redistribute GNU
+;; Emacs, but only under the conditions described in the GNU Emacs
+;; General Public License. A copy of this license is supposed to have
+;; been given to you along with GNU Emacs so you can know your rights and
+;; responsibilities. It should be in a file named COPYING. Among other
+;; things, the copyright notice and this notice must be preserved on all
+;; copies.
+
+;; Description of perl -d interface:
+
+;; A facility is provided for the simultaneous display of the source code
+;; in one window, while using perldb to step through a function in the
+;; other. A small arrow in the source window, indicates the current
+;; line.
+
+;; Starting up:
+
+;; In order to use this facility, invoke the command PERLDB to obtain a
+;; shell window with the appropriate command bindings. You will be asked
+;; for the name of a file to run and additional command line arguments.
+;; Perldb will be invoked on this file, in a window named *perldb-foo*
+;; if the file is foo.
+
+;; M-s steps by one line, and redisplays the source file and line.
+
+;; You may easily create additional commands and bindings to interact
+;; with the display. For example to put the perl debugger command n on \M-n
+;; (def-perldb n "\M-n")
+
+;; This causes the emacs command perldb-next to be defined, and runs
+;; perldb-display-frame after the command.
+
+;; perldb-display-frame is the basic display function. It tries to display
+;; in the other window, the file and line corresponding to the current
+;; position in the perldb window. For example after a perldb-step, it would
+;; display the line corresponding to the position for the last step. Or
+;; if you have done a backtrace in the perldb buffer, and move the cursor
+;; into one of the frames, it would display the position corresponding to
+;; that frame.
+
+;; perldb-display-frame is invoked automatically when a filename-and-line-number
+;; appears in the output.
+
+
+(require 'shell)
+
+(defvar perldb-prompt-pattern "^ DB<[0-9]+> "
+ "A regexp to recognize the prompt for perldb.")
+
+(defvar perldb-mode-map nil
+ "Keymap for perldb-mode.")
+
+(if perldb-mode-map
+ nil
+ (setq perldb-mode-map (copy-keymap shell-mode-map))
+ (define-key perldb-mode-map "\C-l" 'perldb-refresh))
+
+(define-key ctl-x-map " " 'perldb-break)
+(define-key ctl-x-map "&" 'send-perldb-command)
+
+;;Of course you may use `def-perldb' with any other perldb command, including
+;;user defined ones.
+
+(defmacro def-perldb (name key &optional doc)
+ (let* ((fun (intern (concat "perldb-" name))))
+ (` (progn
+ (defun (, fun) (arg)
+ (, (or doc ""))
+ (interactive "p")
+ (perldb-call (if (not (= 1 arg))
+ (concat (, name) arg)
+ (, name))))
+ (define-key perldb-mode-map (, key) (quote (, fun)))))))
+
+(def-perldb "s" "\M-s" "Step one source line with display")
+(def-perldb "n" "\M-n" "Step one source line (skip functions)")
+(def-perldb "c" "\M-c" "Continue with display")
+(def-perldb "r" "\C-c\C-r" "Return from current subroutine")
+(def-perldb "A" "\C-c\C-a" "Delete all actions")
+
+(defun perldb-mode ()
+ "Major mode for interacting with an inferior Perl debugger process.
+The following commands are available:
+
+\\{perldb-mode-map}
+
+\\[perldb-display-frame] displays in the other window
+the last line referred to in the perldb buffer.
+
+\\[perldb-s],\\[perldb-n], and \\[perldb-n] in the perldb window,
+call perldb to step, next or continue and then update the other window
+with the current file and position.
+
+If you are in a source file, you may select a point to break
+at, by doing \\[perldb-break].
+
+Commands:
+Many commands are inherited from shell mode.
+Additionally we have:
+
+\\[perldb-display-frame] display frames file in other window
+\\[perldb-s] advance one line in program
+\\[perldb-n] advance one line in program (skip over calls).
+\\[send-perldb-command] used for special printing of an arg at the current point.
+C-x SPACE sets break point at current line."
+ (interactive)
+ (kill-all-local-variables)
+ (setq major-mode 'perldb-mode)
+ (setq mode-name "Inferior Perl")
+ (setq mode-line-process '(": %s"))
+ (use-local-map perldb-mode-map)
+ (make-local-variable 'last-input-start)
+ (setq last-input-start (make-marker))
+ (make-local-variable 'last-input-end)
+ (setq last-input-end (make-marker))
+ (make-local-variable 'perldb-last-frame)
+ (setq perldb-last-frame nil)
+ (make-local-variable 'perldb-last-frame-displayed-p)
+ (setq perldb-last-frame-displayed-p t)
+ (make-local-variable 'perldb-delete-prompt-marker)
+ (setq perldb-delete-prompt-marker nil)
+ (make-local-variable 'perldb-filter-accumulator)
+ (setq perldb-filter-accumulator nil)
+ (make-local-variable 'shell-prompt-pattern)
+ (setq shell-prompt-pattern perldb-prompt-pattern)
+ (run-hooks 'shell-mode-hook 'perldb-mode-hook))
+
+(defvar current-perldb-buffer nil)
+
+(defvar perldb-command-name "perl"
+ "Pathname for executing perl -d.")
+
+(defun end-of-quoted-arg (argstr start end)
+ (let* ((chr (substring argstr start (1+ start)))
+ (idx (string-match (concat "[^\\]" chr) argstr (1+ start))))
+ (and idx (1+ idx))
+ )
+)
+
+(defun parse-args-helper (arglist argstr start end)
+ (while (and (< start end) (string-match "[ \t\n\f\r\b]"
+ (substring argstr start (1+ start))))
+ (setq start (1+ start)))
+ (cond
+ ((= start end) arglist)
+ ((string-match "[\"']" (substring argstr start (1+ start)))
+ (let ((next (end-of-quoted-arg argstr start end)))
+ (parse-args-helper (cons (substring argstr (1+ start) next) arglist)
+ argstr (1+ next) end)))
+ (t (let ((next (string-match "[ \t\n\f\b\r]" argstr start)))
+ (if next
+ (parse-args-helper (cons (substring argstr start next) arglist)
+ argstr (1+ next) end)
+ (cons (substring argstr start) arglist))))
+ )
+ )
+
+(defun parse-args (args)
+ "Extract arguments from a string ARGS.
+White space separates arguments, with single or double quotes
+used to protect spaces. A list of strings is returned, e.g.,
+(parse-args \"foo bar 'two args'\") => (\"foo\" \"bar\" \"two args\")."
+ (nreverse (parse-args-helper '() args 0 (length args)))
+)
+
+(defun perldb (path args)
+ "Run perldb on program FILE in buffer *perldb-FILE*.
+The default directory for the current buffer becomes the initial
+working directory, by analogy with gdb . If you wish to change this, use
+the Perl command `chdir(DIR)'."
+ (interactive "FRun perl -d on file: \nsCommand line arguments: ")
+ (setq path (expand-file-name path))
+ (let ((file (file-name-nondirectory path))
+ (dir default-directory))
+ (switch-to-buffer (concat "*perldb-" file "*"))
+ (setq default-directory dir)
+ (or (bolp) (newline))
+ (insert "Current directory is " default-directory "\n")
+ (apply 'make-shell
+ (concat "perldb-" file) perldb-command-name nil "-d" path "-emacs"
+ (parse-args args))
+ (perldb-mode)
+ (set-process-filter (get-buffer-process (current-buffer)) 'perldb-filter)
+ (set-process-sentinel (get-buffer-process (current-buffer)) 'perldb-sentinel)
+ (perldb-set-buffer)))
+
+(defun perldb-set-buffer ()
+ (cond ((eq major-mode 'perldb-mode)
+ (setq current-perldb-buffer (current-buffer)))))
+
+;; This function is responsible for inserting output from Perl
+;; into the buffer.
+;; Aside from inserting the text, it notices and deletes
+;; each filename-and-line-number;
+;; that Perl prints to identify the selected frame.
+;; It records the filename and line number, and maybe displays that file.
+(defun perldb-filter (proc string)
+ (let ((inhibit-quit t))
+ (if perldb-filter-accumulator
+ (perldb-filter-accumulate-marker proc
+ (concat perldb-filter-accumulator string))
+ (perldb-filter-scan-input proc string))))
+
+(defun perldb-filter-accumulate-marker (proc string)
+ (setq perldb-filter-accumulator nil)
+ (if (> (length string) 1)
+ (if (= (aref string 1) ?\032)
+ (let ((end (string-match "\n" string)))
+ (if end
+ (progn
+ (let* ((first-colon (string-match ":" string 2))
+ (second-colon
+ (string-match ":" string (1+ first-colon))))
+ (setq perldb-last-frame
+ (cons (substring string 2 first-colon)
+ (string-to-int
+ (substring string (1+ first-colon)
+ second-colon)))))
+ (setq perldb-last-frame-displayed-p nil)
+ (perldb-filter-scan-input proc
+ (substring string (1+ end))))
+ (setq perldb-filter-accumulator string)))
+ (perldb-filter-insert proc "\032")
+ (perldb-filter-scan-input proc (substring string 1)))
+ (setq perldb-filter-accumulator string)))
+
+(defun perldb-filter-scan-input (proc string)
+ (if (equal string "")
+ (setq perldb-filter-accumulator nil)
+ (let ((start (string-match "\032" string)))
+ (if start
+ (progn (perldb-filter-insert proc (substring string 0 start))
+ (perldb-filter-accumulate-marker proc
+ (substring string start)))
+ (perldb-filter-insert proc string)))))
+
+(defun perldb-filter-insert (proc string)
+ (let ((moving (= (point) (process-mark proc)))
+ (output-after-point (< (point) (process-mark proc)))
+ (old-buffer (current-buffer))
+ start)
+ (set-buffer (process-buffer proc))
+ (unwind-protect
+ (save-excursion
+ ;; Insert the text, moving the process-marker.
+ (goto-char (process-mark proc))
+ (setq start (point))
+ (insert string)
+ (set-marker (process-mark proc) (point))
+ (perldb-maybe-delete-prompt)
+ ;; Check for a filename-and-line number.
+ (perldb-display-frame
+ ;; Don't display the specified file
+ ;; unless (1) point is at or after the position where output appears
+ ;; and (2) this buffer is on the screen.
+ (or output-after-point
+ (not (get-buffer-window (current-buffer))))
+ ;; Display a file only when a new filename-and-line-number appears.
+ t))
+ (set-buffer old-buffer))
+ (if moving (goto-char (process-mark proc)))))
+
+(defun perldb-sentinel (proc msg)
+ (cond ((null (buffer-name (process-buffer proc)))
+ ;; buffer killed
+ ;; Stop displaying an arrow in a source file.
+ (setq overlay-arrow-position nil)
+ (set-process-buffer proc nil))
+ ((memq (process-status proc) '(signal exit))
+ ;; Stop displaying an arrow in a source file.
+ (setq overlay-arrow-position nil)
+ ;; Fix the mode line.
+ (setq mode-line-process
+ (concat ": "
+ (symbol-name (process-status proc))))
+ (let* ((obuf (current-buffer)))
+ ;; save-excursion isn't the right thing if
+ ;; process-buffer is current-buffer
+ (unwind-protect
+ (progn
+ ;; Write something in *compilation* and hack its mode line,
+ (set-buffer (process-buffer proc))
+ ;; Force mode line redisplay soon
+ (set-buffer-modified-p (buffer-modified-p))
+ (if (eobp)
+ (insert ?\n mode-name " " msg)
+ (save-excursion
+ (goto-char (point-max))
+ (insert ?\n mode-name " " msg)))
+ ;; If buffer and mode line will show that the process
+ ;; is dead, we can delete it now. Otherwise it
+ ;; will stay around until M-x list-processes.
+ (delete-process proc))
+ ;; Restore old buffer, but don't restore old point
+ ;; if obuf is the perldb buffer.
+ (set-buffer obuf))))))
+
+
+(defun perldb-refresh ()
+ "Fix up a possibly garbled display, and redraw the arrow."
+ (interactive)
+ (redraw-display)
+ (perldb-display-frame))
+
+(defun perldb-display-frame (&optional nodisplay noauto)
+ "Find, obey and delete the last filename-and-line marker from PERLDB.
+The marker looks like \\032\\032FILENAME:LINE:CHARPOS\\n.
+Obeying it means displaying in another window the specified file and line."
+ (interactive)
+ (perldb-set-buffer)
+ (and perldb-last-frame (not nodisplay)
+ (or (not perldb-last-frame-displayed-p) (not noauto))
+ (progn (perldb-display-line (car perldb-last-frame) (cdr perldb-last-frame))
+ (setq perldb-last-frame-displayed-p t))))
+
+;; Make sure the file named TRUE-FILE is in a buffer that appears on the screen
+;; and that its line LINE is visible.
+;; Put the overlay-arrow on the line LINE in that buffer.
+
+(defun perldb-display-line (true-file line)
+ (let* ((buffer (find-file-noselect true-file))
+ (window (display-buffer buffer t))
+ (pos))
+ (save-excursion
+ (set-buffer buffer)
+ (save-restriction
+ (widen)
+ (goto-line line)
+ (setq pos (point))
+ (setq overlay-arrow-string "=>")
+ (or overlay-arrow-position
+ (setq overlay-arrow-position (make-marker)))
+ (set-marker overlay-arrow-position (point) (current-buffer)))
+ (cond ((or (< pos (point-min)) (> pos (point-max)))
+ (widen)
+ (goto-char pos))))
+ (set-window-point window overlay-arrow-position)))
+
+(defun perldb-call (command)
+ "Invoke perldb COMMAND displaying source in other window."
+ (interactive)
+ (goto-char (point-max))
+ (setq perldb-delete-prompt-marker (point-marker))
+ (perldb-set-buffer)
+ (send-string (get-buffer-process current-perldb-buffer)
+ (concat command "\n")))
+
+(defun perldb-maybe-delete-prompt ()
+ (if (and perldb-delete-prompt-marker
+ (> (point-max) (marker-position perldb-delete-prompt-marker)))
+ (let (start)
+ (goto-char perldb-delete-prompt-marker)
+ (setq start (point))
+ (beginning-of-line)
+ (delete-region (point) start)
+ (setq perldb-delete-prompt-marker nil))))
+
+(defun perldb-break ()
+ "Set PERLDB breakpoint at this source line."
+ (interactive)
+ (let ((line (save-restriction
+ (widen)
+ (1+ (count-lines 1 (point))))))
+ (send-string (get-buffer-process current-perldb-buffer)
+ (concat "b " line "\n"))))
+
+(defun perldb-read-token()
+ "Return a string containing the token found in the buffer at point.
+A token can be a number or an identifier. If the token is a name prefaced
+by `$', `@', or `%', the leading character is included in the token."
+ (save-excursion
+ (let (begin)
+ (or (looking-at "[$@%]")
+ (re-search-backward "[^a-zA-Z_0-9]" (point-min) 'move))
+ (setq begin (point))
+ (or (looking-at "[$@%]") (setq begin (+ begin 1)))
+ (forward-char 1)
+ (buffer-substring begin
+ (if (re-search-forward "[^a-zA-Z_0-9]"
+ (point-max) 'move)
+ (- (point) 1)
+ (point)))
+)))
+
+(defvar perldb-commands nil
+ "List of strings or functions used by send-perldb-command.
+It is for customization by the user.")
+
+(defun send-perldb-command (arg)
+ "Issue a Perl debugger command selected by the prefix arg. A numeric
+arg selects the ARG'th member COMMAND of the list perldb-commands.
+The token under the cursor is passed to the command. If COMMAND is a
+string, (format COMMAND TOKEN) is inserted at the end of the perldb
+buffer, otherwise (funcall COMMAND TOKEN) is inserted. If there is
+no such COMMAND, then the token itself is inserted. For example,
+\"p %s\" is a possible string to be a member of perldb-commands,
+or \"p $ENV{%s}\"."
+ (interactive "P")
+ (let (comm token)
+ (if arg (setq comm (nth arg perldb-commands)))
+ (setq token (perldb-read-token))
+ (if (eq (current-buffer) current-perldb-buffer)
+ (set-mark (point)))
+ (cond (comm
+ (setq comm
+ (if (stringp comm) (format comm token) (funcall comm token))))
+ (t (setq comm token)))
+ (switch-to-buffer-other-window current-perldb-buffer)
+ (goto-char (dot-max))
+ (insert-string comm)))
diff --git a/gnu/usr.bin/perl/emacs/perldb.pl b/gnu/usr.bin/perl/emacs/perldb.pl
new file mode 100644
index 0000000..7c9e651
--- /dev/null
+++ b/gnu/usr.bin/perl/emacs/perldb.pl
@@ -0,0 +1,568 @@
+package DB;
+
+# modified Perl debugger, to be run from Emacs in perldb-mode
+# Ray Lischner (uunet!mntgfx!lisch) as of 5 Nov 1990
+
+$header = '$Header: /home/cvs/386BSD/ports/lang/perl/emacs/perldb.pl,v 1.1.1.1 1993/08/23 21:29:46 nate Exp $';
+#
+# This file is automatically included if you do perl -d.
+# It's probably not useful to include this yourself.
+#
+# Perl supplies the values for @line and %sub. It effectively inserts
+# a do DB'DB(<linenum>); in front of every place that can
+# have a breakpoint. It also inserts a do 'perldb.pl' before the first line.
+#
+# $Log: perldb.pl,v $
+# Revision 1.1.1.1 1993/08/23 21:29:46 nate
+# PERL!
+#
+# Revision 4.0 91/03/20 01:18:58 lwall
+# 4.0 baseline.
+#
+# Revision 3.0.1.6 91/01/11 18:08:58 lwall
+# patch42: @_ couldn't be accessed from debugger
+#
+# Revision 3.0.1.5 90/11/10 01:40:26 lwall
+# patch38: the debugger wouldn't stop correctly or do action routines
+#
+# Revision 3.0.1.4 90/10/15 17:40:38 lwall
+# patch29: added caller
+# patch29: the debugger now understands packages and evals
+# patch29: scripts now run at almost full speed under the debugger
+# patch29: more variables are settable from debugger
+#
+# Revision 3.0.1.3 90/08/09 04:00:58 lwall
+# patch19: debugger now allows continuation lines
+# patch19: debugger can now dump lists of variables
+# patch19: debugger can now add aliases easily from prompt
+#
+# Revision 3.0.1.2 90/03/12 16:39:39 lwall
+# patch13: perl -d didn't format stack traces of *foo right
+# patch13: perl -d wiped out scalar return values of subroutines
+#
+# Revision 3.0.1.1 89/10/26 23:14:02 lwall
+# patch1: RCS expanded an unintended $Header in lib/perldb.pl
+#
+# Revision 3.0 89/10/18 15:19:46 lwall
+# 3.0 baseline
+#
+# Revision 2.0 88/06/05 00:09:45 root
+# Baseline version 2.0.
+#
+#
+
+open(IN, "</dev/tty") || open(IN, "<&STDIN"); # so we don't dingle stdin
+open(OUT,">/dev/tty") || open(OUT, ">&STDOUT"); # so we don't dongle stdout
+select(OUT);
+$| = 1; # for DB'OUT
+select(STDOUT);
+$| = 1; # for real STDOUT
+$sub = '';
+
+# Is Perl being run from Emacs?
+$emacs = $main'ARGV[$[] eq '-emacs';
+shift(@main'ARGV) if $emacs;
+
+$header =~ s/.Header: ([^,]+),v(\s+\S+\s+\S+).*$/$1$2/;
+print OUT "\nLoading DB routines from $header\n\nEnter h for help.\n\n";
+
+sub DB {
+ &save;
+ ($package, $filename, $line) = caller;
+ $usercontext = '($@, $!, $[, $,, $/, $\) = @saved;' .
+ "package $package;"; # this won't let them modify, alas
+ local(*dbline) = "_<$filename";
+ $max = $#dbline;
+ if (($stop,$action) = split(/\0/,$dbline{$line})) {
+ if ($stop eq '1') {
+ $signal |= 1;
+ }
+ else {
+ $evalarg = "\$DB'signal |= do {$stop;}"; &eval;
+ $dbline{$line} =~ s/;9($|\0)/$1/;
+ }
+ }
+ if ($single || $trace || $signal) {
+ if ($emacs) {
+ print OUT "\032\032$filename:$line:0\n";
+ } else {
+ print OUT "$package'" unless $sub =~ /'/;
+ print OUT "$sub($filename:$line):\t",$dbline[$line];
+ for ($i = $line + 1; $i <= $max && $dbline[$i] == 0; ++$i) {
+ last if $dbline[$i] =~ /^\s*(}|#|\n)/;
+ print OUT "$sub($filename:$i):\t",$dbline[$i];
+ }
+ }
+ }
+ $evalarg = $action, &eval if $action;
+ if ($single || $signal) {
+ $evalarg = $pre, &eval if $pre;
+ print OUT $#stack . " levels deep in subroutine calls!\n"
+ if $single & 4;
+ $start = $line;
+ while ((print OUT " DB<", $#hist+1, "> "), $cmd=&gets) {
+ $single = 0;
+ $signal = 0;
+ $cmd eq '' && exit 0;
+ chop($cmd);
+ $cmd =~ s/\\$// && do {
+ print OUT " cont: ";
+ $cmd .= &gets;
+ redo;
+ };
+ $cmd =~ /^q$/ && exit 0;
+ $cmd =~ /^$/ && ($cmd = $laststep);
+ push(@hist,$cmd) if length($cmd) > 1;
+ ($i) = split(/\s+/,$cmd);
+ eval "\$cmd =~ $alias{$i}", print OUT $@ if $alias{$i};
+ $cmd =~ /^h$/ && do {
+ print OUT "
+T Stack trace.
+s Single step.
+n Next, steps over subroutine calls.
+r Return from current subroutine.
+c [line] Continue; optionally inserts a one-time-only breakpoint
+ at the specified line.
+<CR> Repeat last n or s.
+l min+incr List incr+1 lines starting at min.
+l min-max List lines.
+l line List line;
+l List next window.
+- List previous window.
+w line List window around line.
+l subname List subroutine.
+f filename Switch to filename.
+/pattern/ Search forwards for pattern; final / is optional.
+?pattern? Search backwards for pattern.
+L List breakpoints and actions.
+S List subroutine names.
+t Toggle trace mode.
+b [line] [condition]
+ Set breakpoint; line defaults to the current execution line;
+ condition breaks if it evaluates to true, defaults to \'1\'.
+b subname [condition]
+ Set breakpoint at first line of subroutine.
+d [line] Delete breakpoint.
+D Delete all breakpoints.
+a [line] command
+ Set an action to be done before the line is executed.
+ Sequence is: check for breakpoint, print line if necessary,
+ do action, prompt user if breakpoint or step, evaluate line.
+A Delete all actions.
+V [pkg [vars]] List some (default all) variables in package (default current).
+X [vars] Same as \"V currentpackage [vars]\".
+< command Define command before prompt.
+| command Define command after prompt.
+! number Redo command (default previous command).
+! -number Redo number\'th to last command.
+H -number Display last number commands (default all).
+q or ^D Quit.
+p expr Same as \"print DB'OUT expr\" in current package.
+= [alias value] Define a command alias, or list current aliases.
+command Execute as a perl statement in current package.
+
+";
+ next; };
+ $cmd =~ /^t$/ && do {
+ $trace = !$trace;
+ print OUT "Trace = ".($trace?"on":"off")."\n";
+ next; };
+ $cmd =~ /^S$/ && do {
+ foreach $subname (sort(keys %sub)) {
+ print OUT $subname,"\n";
+ }
+ next; };
+ $cmd =~ s/^X\b/V $package/;
+ $cmd =~ /^V$/ && do {
+ $cmd = 'V $package'; };
+ $cmd =~ /^V\s*(\S+)\s*(.*)/ && do {
+ $packname = $1;
+ @vars = split(' ',$2);
+ do 'dumpvar.pl' unless defined &main'dumpvar;
+ if (defined &main'dumpvar) {
+ &main'dumpvar($packname,@vars);
+ }
+ else {
+ print DB'OUT "dumpvar.pl not available.\n";
+ }
+ next; };
+ $cmd =~ /^f\s*(.*)/ && do {
+ $file = $1;
+ if (!$file) {
+ print OUT "The old f command is now the r command.\n";
+ print OUT "The new f command switches filenames.\n";
+ next;
+ }
+ if (!defined $_main{'_<' . $file}) {
+ if (($try) = grep(m#^_<.*$file#, keys %_main)) {
+ $file = substr($try,2);
+ print "\n$file:\n";
+ }
+ }
+ if (!defined $_main{'_<' . $file}) {
+ print OUT "There's no code here anything matching $file.\n";
+ next;
+ }
+ elsif ($file ne $filename) {
+ *dbline = "_<$file";
+ $max = $#dbline;
+ $filename = $file;
+ $start = 1;
+ $cmd = "l";
+ } };
+ $cmd =~ /^l\s*(['A-Za-z_]['\w]*)/ && do {
+ $subname = $1;
+ $subname = "main'" . $subname unless $subname =~ /'/;
+ $subname = "main" . $subname if substr($subname,0,1) eq "'";
+ ($file,$subrange) = split(/:/,$sub{$subname});
+ if ($file ne $filename) {
+ *dbline = "_<$file";
+ $max = $#dbline;
+ $filename = $file;
+ }
+ if ($subrange) {
+ if (eval($subrange) < -$window) {
+ $subrange =~ s/-.*/+/;
+ }
+ $cmd = "l $subrange";
+ } else {
+ print OUT "Subroutine $1 not found.\n";
+ next;
+ } };
+ $cmd =~ /^w\s*(\d*)$/ && do {
+ $incr = $window - 1;
+ $start = $1 if $1;
+ $start -= $preview;
+ $cmd = 'l ' . $start . '-' . ($start + $incr); };
+ $cmd =~ /^-$/ && do {
+ $incr = $window - 1;
+ $cmd = 'l ' . ($start-$window*2) . '+'; };
+ $cmd =~ /^l$/ && do {
+ $incr = $window - 1;
+ $cmd = 'l ' . $start . '-' . ($start + $incr); };
+ $cmd =~ /^l\s*(\d*)\+(\d*)$/ && do {
+ $start = $1 if $1;
+ $incr = $2;
+ $incr = $window - 1 unless $incr;
+ $cmd = 'l ' . $start . '-' . ($start + $incr); };
+ $cmd =~ /^l\s*(([\d\$\.]+)([-,]([\d\$\.]+))?)?/ && do {
+ $end = (!$2) ? $max : ($4 ? $4 : $2);
+ $end = $max if $end > $max;
+ $i = $2;
+ $i = $line if $i eq '.';
+ $i = 1 if $i < 1;
+ if ($emacs) {
+ print OUT "\032\032$filename:$i:0\n";
+ $i = $end;
+ } else {
+ for (; $i <= $end; $i++) {
+ print OUT "$i:\t", $dbline[$i];
+ last if $signal;
+ }
+ }
+ $start = $i; # remember in case they want more
+ $start = $max if $start > $max;
+ next; };
+ $cmd =~ /^D$/ && do {
+ print OUT "Deleting all breakpoints...\n";
+ for ($i = 1; $i <= $max ; $i++) {
+ if (defined $dbline{$i}) {
+ $dbline{$i} =~ s/^[^\0]+//;
+ if ($dbline{$i} =~ s/^\0?$//) {
+ delete $dbline{$i};
+ }
+ }
+ }
+ next; };
+ $cmd =~ /^L$/ && do {
+ for ($i = 1; $i <= $max; $i++) {
+ if (defined $dbline{$i}) {
+ print OUT "$i:\t", $dbline[$i];
+ ($stop,$action) = split(/\0/, $dbline{$i});
+ print OUT " break if (", $stop, ")\n"
+ if $stop;
+ print OUT " action: ", $action, "\n"
+ if $action;
+ last if $signal;
+ }
+ }
+ next; };
+ $cmd =~ /^b\s*(['A-Za-z_]['\w]*)\s*(.*)/ && do {
+ $subname = $1;
+ $cond = $2 || '1';
+ $subname = "$package'" . $subname unless $subname =~ /'/;
+ $subname = "main" . $subname if substr($subname,0,1) eq "'";
+ ($filename,$i) = split(/[:-]/, $sub{$subname});
+ if ($i) {
+ *dbline = "_<$filename";
+ ++$i while $dbline[$i] == 0 && $i < $#dbline;
+ $dbline{$i} =~ s/^[^\0]*/$cond/;
+ } else {
+ print OUT "Subroutine $subname not found.\n";
+ }
+ next; };
+ $cmd =~ /^b\s*(\d*)\s*(.*)/ && do {
+ $i = ($1?$1:$line);
+ $cond = $2 || '1';
+ if ($dbline[$i] == 0) {
+ print OUT "Line $i not breakable.\n";
+ } else {
+ $dbline{$i} =~ s/^[^\0]*/$cond/;
+ }
+ next; };
+ $cmd =~ /^d\s*(\d+)?/ && do {
+ $i = ($1?$1:$line);
+ $dbline{$i} =~ s/^[^\0]*//;
+ delete $dbline{$i} if $dbline{$i} eq '';
+ next; };
+ $cmd =~ /^A$/ && do {
+ for ($i = 1; $i <= $max ; $i++) {
+ if (defined $dbline{$i}) {
+ $dbline{$i} =~ s/\0[^\0]*//;
+ delete $dbline{$i} if $dbline{$i} eq '';
+ }
+ }
+ next; };
+ $cmd =~ /^<\s*(.*)/ && do {
+ $pre = do action($1);
+ next; };
+ $cmd =~ /^>\s*(.*)/ && do {
+ $post = do action($1);
+ next; };
+ $cmd =~ /^a\s*(\d+)(\s+(.*))?/ && do {
+ $i = $1;
+ if ($dbline[$i] == 0) {
+ print OUT "Line $i may not have an action.\n";
+ } else {
+ $dbline{$i} =~ s/\0[^\0]*//;
+ $dbline{$i} .= "\0" . do action($3);
+ }
+ next; };
+ $cmd =~ /^n$/ && do {
+ $single = 2;
+ $laststep = $cmd;
+ last; };
+ $cmd =~ /^s$/ && do {
+ $single = 1;
+ $laststep = $cmd;
+ last; };
+ $cmd =~ /^c\s*(\d*)\s*$/ && do {
+ $i = $1;
+ if ($i) {
+ if ($dbline[$i] == 0) {
+ print OUT "Line $i not breakable.\n";
+ next;
+ }
+ $dbline{$i} =~ s/(\0|$)/;9$1/; # add one-time-only b.p.
+ }
+ for ($i=0; $i <= $#stack; ) {
+ $stack[$i++] &= ~1;
+ }
+ last; };
+ $cmd =~ /^r$/ && do {
+ $stack[$#stack] |= 2;
+ last; };
+ $cmd =~ /^T$/ && do {
+ local($p,$f,$l,$s,$h,$a,@a,@sub);
+ for ($i = 1; ($p,$f,$l,$s,$h,$w) = caller($i); $i++) {
+ @a = @args;
+ for (@a) {
+ if (/^StB\000/ && length($_) == length($_main{'_main'})) {
+ $_ = sprintf("%s",$_);
+ }
+ else {
+ s/'/\\'/g;
+ s/([^\0]*)/'$1'/ unless /^-?[\d.]+$/;
+ s/([\200-\377])/sprintf("M-%c",ord($1)&0177)/eg;
+ s/([\0-\37\177])/sprintf("^%c",ord($1)^64)/eg;
+ }
+ }
+ $w = $w ? '@ = ' : '$ = ';
+ $a = $h ? '(' . join(', ', @a) . ')' : '';
+ push(@sub, "$w&$s$a from file $f line $l\n");
+ last if $signal;
+ }
+ for ($i=0; $i <= $#sub; $i++) {
+ last if $signal;
+ print OUT $sub[$i];
+ }
+ next; };
+ $cmd =~ /^\/(.*)$/ && do {
+ $inpat = $1;
+ $inpat =~ s:([^\\])/$:$1:;
+ if ($inpat ne "") {
+ eval '$inpat =~ m'."\n$inpat\n";
+ if ($@ ne "") {
+ print OUT "$@";
+ next;
+ }
+ $pat = $inpat;
+ }
+ $end = $start;
+ eval '
+ for (;;) {
+ ++$start;
+ $start = 1 if ($start > $max);
+ last if ($start == $end);
+ if ($dbline[$start] =~ m'."\n$pat\n".'i) {
+ if ($emacs) {
+ print OUT "\032\032$filename:$start:0\n";
+ } else {
+ print OUT "$start:\t", $dbline[$start], "\n";
+ }
+ last;
+ }
+ } ';
+ print OUT "/$pat/: not found\n" if ($start == $end);
+ next; };
+ $cmd =~ /^\?(.*)$/ && do {
+ $inpat = $1;
+ $inpat =~ s:([^\\])\?$:$1:;
+ if ($inpat ne "") {
+ eval '$inpat =~ m'."\n$inpat\n";
+ if ($@ ne "") {
+ print OUT "$@";
+ next;
+ }
+ $pat = $inpat;
+ }
+ $end = $start;
+ eval '
+ for (;;) {
+ --$start;
+ $start = $max if ($start <= 0);
+ last if ($start == $end);
+ if ($dbline[$start] =~ m'."\n$pat\n".'i) {
+ if ($emacs) {
+ print OUT "\032\032$filename:$start:0\n";
+ } else {
+ print OUT "$start:\t", $dbline[$start], "\n";
+ }
+ last;
+ }
+ } ';
+ print OUT "?$pat?: not found\n" if ($start == $end);
+ next; };
+ $cmd =~ /^!+\s*(-)?(\d+)?$/ && do {
+ pop(@hist) if length($cmd) > 1;
+ $i = ($1?($#hist-($2?$2:1)):($2?$2:$#hist));
+ $cmd = $hist[$i] . "\n";
+ print OUT $cmd;
+ redo; };
+ $cmd =~ /^!(.+)$/ && do {
+ $pat = "^$1";
+ pop(@hist) if length($cmd) > 1;
+ for ($i = $#hist; $i; --$i) {
+ last if $hist[$i] =~ $pat;
+ }
+ if (!$i) {
+ print OUT "No such command!\n\n";
+ next;
+ }
+ $cmd = $hist[$i] . "\n";
+ print OUT $cmd;
+ redo; };
+ $cmd =~ /^H\s*(-(\d+))?/ && do {
+ $end = $2?($#hist-$2):0;
+ $hist = 0 if $hist < 0;
+ for ($i=$#hist; $i>$end; $i--) {
+ print OUT "$i: ",$hist[$i],"\n"
+ unless $hist[$i] =~ /^.?$/;
+ };
+ next; };
+ $cmd =~ s/^p( .*)?$/print DB'OUT$1/;
+ $cmd =~ /^=/ && do {
+ if (local($k,$v) = ($cmd =~ /^=\s*(\S+)\s+(.*)/)) {
+ $alias{$k}="s~$k~$v~";
+ print OUT "$k = $v\n";
+ } elsif ($cmd =~ /^=\s*$/) {
+ foreach $k (sort keys(%alias)) {
+ if (($v = $alias{$k}) =~ s~^s\~$k\~(.*)\~$~$1~) {
+ print OUT "$k = $v\n";
+ } else {
+ print OUT "$k\t$alias{$k}\n";
+ };
+ };
+ };
+ next; };
+ $evalarg = $cmd; &eval;
+ print OUT "\n";
+ }
+ if ($post) {
+ $evalarg = $post; &eval;
+ }
+ }
+ ($@, $!, $[, $,, $/, $\) = @saved;
+}
+
+sub save {
+ @saved = ($@, $!, $[, $,, $/, $\);
+ $[ = 0; $, = ""; $/ = "\n"; $\ = "";
+}
+
+# The following takes its argument via $evalarg to preserve current @_
+
+sub eval {
+ eval "$usercontext $evalarg; &DB'save";
+ print OUT $@;
+}
+
+sub action {
+ local($action) = @_;
+ while ($action =~ s/\\$//) {
+ print OUT "+ ";
+ $action .= &gets;
+ }
+ $action;
+}
+
+sub gets {
+ local($.);
+ <IN>;
+}
+
+sub catch {
+ $signal = 1;
+}
+
+sub sub {
+ push(@stack, $single);
+ $single &= 1;
+ $single |= 4 if $#stack == $deep;
+ if (wantarray) {
+ @i = &$sub;
+ $single |= pop(@stack);
+ @i;
+ }
+ else {
+ $i = &$sub;
+ $single |= pop(@stack);
+ $i;
+ }
+}
+
+$single = 1; # so it stops on first executable statement
+@hist = ('?');
+$SIG{'INT'} = "DB'catch";
+$deep = 100; # warning if stack gets this deep
+$window = 10;
+$preview = 3;
+
+@stack = (0);
+@ARGS = @ARGV;
+for (@args) {
+ s/'/\\'/g;
+ s/(.*)/'$1'/ unless /^-?[\d.]+$/;
+}
+
+if (-f '.perldb') {
+ do './.perldb';
+}
+elsif (-f "$ENV{'LOGDIR'}/.perldb") {
+ do "$ENV{'LOGDIR'}/.perldb";
+}
+elsif (-f "$ENV{'HOME'}/.perldb") {
+ do "$ENV{'HOME'}/.perldb";
+}
+
+1;
diff --git a/gnu/usr.bin/perl/emacs/tedstuff b/gnu/usr.bin/perl/emacs/tedstuff
new file mode 100644
index 0000000..257bbc8
--- /dev/null
+++ b/gnu/usr.bin/perl/emacs/tedstuff
@@ -0,0 +1,296 @@
+Article 4417 of comp.lang.perl:
+Path: jpl-devvax!elroy.jpl.nasa.gov!decwrl!mcnc!uvaarpa!mmdf
+From: ted@evi.com (Ted Stefanik)
+Newsgroups: comp.lang.perl
+Subject: Correction to Perl fatal error marking in GNU Emacs
+Message-ID: <1991Feb27.065853.15801@uvaarpa.Virginia.EDU>
+Date: 27 Feb 91 06:58:53 GMT
+Sender: mmdf@uvaarpa.Virginia.EDU (Uvaarpa Mail System)
+Reply-To: ted@evi.com (Ted Stefanik)
+Organization: The Internet
+Lines: 282
+
+Reading my own message, it occurred to me that I didn't quite satisfy the
+request of stef@zweig.sun (Stephane Payrard):
+
+| Does anyone has extended perdb/perdb.el to position the
+| point to the first syntax error? It would be cool.
+
+What I posted is a way to use the "M-x compile" command to test perl scripts.
+(Needless to say, the script cannot be not interactive; you can't provide input
+to a *compilation* buffer). When creating new Perl programs, I use "M-x
+compile" until I'm sure that they are syntatically correct; if syntax errors
+occur, C-x` takes me to each in sequence. After I'm sure the syntax is
+correct, I start worrying about semantics, and switch to "M-x perldb" if
+necessary.
+
+Therefore, the stuff I posted works great with "M-x compile", but not at all
+with "M-x perldb".
+
+Next, let me update what I posted. I found that perl's die() command doesn't
+print the same format error message as perl does when it dies with a syntax
+error. If you put the following in your ".emacs" file, it causes C-x` to
+recognize both kinds of errors:
+
+(load-library "compile")
+(setq compilation-error-regexp
+ "\\([^ :\n]+\\(: *\\|, line \\|(\\)[0-9]+\\)\\|\\([0-9]+ *of *[^ \n]+\\|[^ \n]+ \\(at \\)*line [0-9]+\\)")
+
+Last, so I don't look like a total fool, let me propose a way to satisfy
+Stephane Payrard's original request (repeated again):
+
+| Does anyone has extended perdb/perdb.el to position the
+| point to the first syntax error? It would be cool.
+
+I'm not satisfied with just the "first syntax error". Perl's parser is better
+than most about not getting out of sync; therefore, if it reports multiple
+errors, you can usually be assured they are all real errors.
+
+So... I hacked in the "next-error" function from "compile.el" to form
+"perldb-next-error". You can apply the patches at the end of this message
+to add "perldb-next-error" to your "perldb.el".
+
+Notes:
+ 1) The patch binds "perldb-next-error" to C-x~ (because ~ is the shift
+ of ` on my keyboard, and C-x~ is not yet taken in my version of EMACS).
+
+ 2) "next-error" is meant to work on a single *compilation* buffer; any new
+ "M-x compile" or "M-x grep" command will clear the old *compilation*
+ buffer and reset the compilation-error parser to start at the top of the
+ *compilation* buffer.
+
+ "perldb-next-error", on the other hand, has to deal with multiple
+ *perldb-<foo>* buffers, each of which keep growing. "perldb-next-error"
+ correctly handles the constantly growing *perldb-<foo>* buffers by
+ keeping track of the last reported error in the "current-perldb-buffer".
+
+ Sadly however, when you invoke a new "M-x perldb" on a different Perl
+ script, "perldb-next-error" will start parsing the new *perldb-<bar>*
+ buffer at the top (even if it was previously parsed), and will completely
+ lose the marker of the last reported error in *perldb-<foo>*.
+
+ 3) "perldb-next-error" still uses "compilation-error-regexp" to find
+ fatal errors. Therefore, both the "M-x compile"/C-x` scheme and
+ the "M-x perldb"/C-x~ scheme can be used to find fatal errors that
+ match the common "compilation-error-regexp". You *will* want to install
+ that "compilation-error-regexp" stuff into your .emacs file.
+
+ 4) The patch was developed and tested with GNU Emacs 18.55.
+
+ 5) Since the patch was ripped off from compile.el, the code is (of
+ course) subject to the GNU copyleft.
+
+*** perldb.el.orig Wed Feb 27 00:44:27 1991
+--- perldb.el Wed Feb 27 00:44:30 1991
+***************
+*** 199,205 ****
+
+ (defun perldb-set-buffer ()
+ (cond ((eq major-mode 'perldb-mode)
+! (setq current-perldb-buffer (current-buffer)))))
+
+ ;; This function is responsible for inserting output from Perl
+ ;; into the buffer.
+--- 199,211 ----
+
+ (defun perldb-set-buffer ()
+ (cond ((eq major-mode 'perldb-mode)
+! (cond ((not (eq current-perldb-buffer (current-buffer)))
+! (perldb-forget-errors)
+! (setq perldb-parsing-end 2)) ;; 2 to defeat grep defeater
+! (t
+! (if (> perldb-parsing-end (point-max))
+! (setq perldb-parsing-end (max (point-max) 2)))))
+! (setq current-perldb-buffer (current-buffer)))))
+
+ ;; This function is responsible for inserting output from Perl
+ ;; into the buffer.
+***************
+*** 291,297 ****
+ ;; process-buffer is current-buffer
+ (unwind-protect
+ (progn
+! ;; Write something in *compilation* and hack its mode line,
+ (set-buffer (process-buffer proc))
+ ;; Force mode line redisplay soon
+ (set-buffer-modified-p (buffer-modified-p))
+--- 297,303 ----
+ ;; process-buffer is current-buffer
+ (unwind-protect
+ (progn
+! ;; Write something in *perldb-<foo>* and hack its mode line,
+ (set-buffer (process-buffer proc))
+ ;; Force mode line redisplay soon
+ (set-buffer-modified-p (buffer-modified-p))
+***************
+*** 421,423 ****
+--- 427,593 ----
+ (switch-to-buffer-other-window current-perldb-buffer)
+ (goto-char (dot-max))
+ (insert-string comm)))
++
++ (defvar perldb-error-list nil
++ "List of error message descriptors for visiting erring functions.
++ Each error descriptor is a list of length two.
++ Its car is a marker pointing to an error message.
++ Its cadr is a marker pointing to the text of the line the message is about,
++ or nil if that is not interesting.
++ The value may be t instead of a list;
++ this means that the buffer of error messages should be reparsed
++ the next time the list of errors is wanted.")
++
++ (defvar perldb-parsing-end nil
++ "Position of end of buffer when last error messages parsed.")
++
++ (defvar perldb-error-message "No more fatal Perl errors"
++ "Message to print when no more matches for compilation-error-regexp are found")
++
++ (defun perldb-next-error (&optional argp)
++ "Visit next perldb error message and corresponding source code.
++ This operates on the output from the \\[perldb] command.
++ If all preparsed error messages have been processed,
++ the error message buffer is checked for new ones.
++ A non-nil argument (prefix arg, if interactive)
++ means reparse the error message buffer and start at the first error."
++ (interactive "P")
++ (if (or (eq perldb-error-list t)
++ argp)
++ (progn (perldb-forget-errors)
++ (setq perldb-parsing-end 2))) ;; 2 to defeat grep defeater
++ (if perldb-error-list
++ nil
++ (save-excursion
++ (switch-to-buffer current-perldb-buffer)
++ (perldb-parse-errors)))
++ (let ((next-error (car perldb-error-list)))
++ (if (null next-error)
++ (error (concat perldb-error-message
++ (if (and (get-buffer-process current-perldb-buffer)
++ (eq (process-status
++ (get-buffer-process
++ current-perldb-buffer))
++ 'run))
++ " yet" ""))))
++ (setq perldb-error-list (cdr perldb-error-list))
++ (if (null (car (cdr next-error)))
++ nil
++ (switch-to-buffer (marker-buffer (car (cdr next-error))))
++ (goto-char (car (cdr next-error)))
++ (set-marker (car (cdr next-error)) nil))
++ (let* ((pop-up-windows t)
++ (w (display-buffer (marker-buffer (car next-error)))))
++ (set-window-point w (car next-error))
++ (set-window-start w (car next-error)))
++ (set-marker (car next-error) nil)))
++
++ ;; Set perldb-error-list to nil, and
++ ;; unchain the markers that point to the error messages and their text,
++ ;; so that they no longer slow down gap motion.
++ ;; This would happen anyway at the next garbage collection,
++ ;; but it is better to do it right away.
++ (defun perldb-forget-errors ()
++ (if (eq perldb-error-list t)
++ (setq perldb-error-list nil))
++ (while perldb-error-list
++ (let ((next-error (car perldb-error-list)))
++ (set-marker (car next-error) nil)
++ (if (car (cdr next-error))
++ (set-marker (car (cdr next-error)) nil)))
++ (setq perldb-error-list (cdr perldb-error-list))))
++
++ (defun perldb-parse-errors ()
++ "Parse the current buffer as error messages.
++ This makes a list of error descriptors, perldb-error-list.
++ For each source-file, line-number pair in the buffer,
++ the source file is read in, and the text location is saved in perldb-error-list.
++ The function next-error, assigned to \\[next-error], takes the next error off the list
++ and visits its location."
++ (setq perldb-error-list nil)
++ (message "Parsing error messages...")
++ (let (text-buffer
++ last-filename last-linenum)
++ ;; Don't reparse messages already seen at last parse.
++ (goto-char perldb-parsing-end)
++ ;; Don't parse the first two lines as error messages.
++ ;; This matters for grep.
++ (if (bobp)
++ (forward-line 2))
++ (while (re-search-forward compilation-error-regexp nil t)
++ (let (linenum filename
++ error-marker text-marker)
++ ;; Extract file name and line number from error message.
++ (save-restriction
++ (narrow-to-region (match-beginning 0) (match-end 0))
++ (goto-char (point-max))
++ (skip-chars-backward "[0-9]")
++ ;; If it's a lint message, use the last file(linenum) on the line.
++ ;; Normally we use the first on the line.
++ (if (= (preceding-char) ?\()
++ (progn
++ (narrow-to-region (point-min) (1+ (buffer-size)))
++ (end-of-line)
++ (re-search-backward compilation-error-regexp)
++ (skip-chars-backward "^ \t\n")
++ (narrow-to-region (point) (match-end 0))
++ (goto-char (point-max))
++ (skip-chars-backward "[0-9]")))
++ ;; Are we looking at a "filename-first" or "line-number-first" form?
++ (if (looking-at "[0-9]")
++ (progn
++ (setq linenum (read (current-buffer)))
++ (goto-char (point-min)))
++ ;; Line number at start, file name at end.
++ (progn
++ (goto-char (point-min))
++ (setq linenum (read (current-buffer)))
++ (goto-char (point-max))
++ (skip-chars-backward "^ \t\n")))
++ (setq filename (perldb-grab-filename)))
++ ;; Locate the erring file and line.
++ (if (and (equal filename last-filename)
++ (= linenum last-linenum))
++ nil
++ (beginning-of-line 1)
++ (setq error-marker (point-marker))
++ ;; text-buffer gets the buffer containing this error's file.
++ (if (not (equal filename last-filename))
++ (setq text-buffer
++ (and (file-exists-p (setq last-filename filename))
++ (find-file-noselect filename))
++ last-linenum 0))
++ (if text-buffer
++ ;; Go to that buffer and find the erring line.
++ (save-excursion
++ (set-buffer text-buffer)
++ (if (zerop last-linenum)
++ (progn
++ (goto-char 1)
++ (setq last-linenum 1)))
++ (forward-line (- linenum last-linenum))
++ (setq last-linenum linenum)
++ (setq text-marker (point-marker))
++ (setq perldb-error-list
++ (cons (list error-marker text-marker)
++ perldb-error-list)))))
++ (forward-line 1)))
++ (setq perldb-parsing-end (point-max)))
++ (message "Parsing error messages...done")
++ (setq perldb-error-list (nreverse perldb-error-list)))
++
++ (defun perldb-grab-filename ()
++ "Return a string which is a filename, starting at point.
++ Ignore quotes and parentheses around it, as well as trailing colons."
++ (if (eq (following-char) ?\")
++ (save-restriction
++ (narrow-to-region (point)
++ (progn (forward-sexp 1) (point)))
++ (goto-char (point-min))
++ (read (current-buffer)))
++ (buffer-substring (point)
++ (progn
++ (skip-chars-forward "^ :,\n\t(")
++ (point)))))
++
++ (define-key ctl-x-map "~" 'perldb-next-error)
+
+
diff --git a/gnu/usr.bin/perl/h2pl/README b/gnu/usr.bin/perl/h2pl/README
new file mode 100644
index 0000000..5fe8ae7
--- /dev/null
+++ b/gnu/usr.bin/perl/h2pl/README
@@ -0,0 +1,71 @@
+[This file of Tom Christiansen's has been edited to change makelib to h2ph
+and .h to .ph where appropriate--law.]
+
+This directory contains files to help you convert the *.ph files generated my
+h2ph out of the perl source directory into *.pl files with all the
+indirection of the subroutine calls removed. The .ph version will be more
+safely portable, because if something isn't defined on the new system, like
+&TIOCGETP, then you'll get a fatal run-time error on the system lacking that
+function. Using the .pl version means that the subsequent scripts will give
+you a 0 $TIOCGETP and God only knows what may then happen. Still, I like the
+.pl stuff because they're faster to load.
+
+FIrst, you need to run h2ph on things like sys/ioctl.h to get stuff
+into the perl library directory, often /usr/local/lib/perl. For example,
+ # h2ph sys/ioctl.h
+takes /usr/include/sys/ioctl.h as input and writes (without i/o redirection)
+the file /usr/local/lib/perl/sys/ioctl.ph, which looks like this
+
+ eval 'sub TIOCM_RTS {0004;}';
+ eval 'sub TIOCM_ST {0010;}';
+ eval 'sub TIOCM_SR {0020;}';
+ eval 'sub TIOCM_CTS {0040;}';
+ eval 'sub TIOCM_CAR {0100;}';
+
+and much worse, rather than what Larry's ioctl.pl from the perl source dir has,
+which is:
+
+ $TIOCM_RTS = 0004;
+ $TIOCM_ST = 0010;
+ $TIOCM_SR = 0020;
+ $TIOCM_CTS = 0040;
+ $TIOCM_CAR = 0100;
+
+[Workaround for fixed bug in makedir/h2ph deleted--law.]
+
+The more complicated ioctl subs look like this:
+
+ eval 'sub TIOCGSIZE {&TIOCGWINSZ;}';
+ eval 'sub TIOCGWINSZ {&_IOR("t", 104, \'struct winsize\');}';
+ eval 'sub TIOCSETD {&_IOW("t", 1, \'int\');}';
+ eval 'sub TIOCGETP {&_IOR("t", 8,\'struct sgttyb\');}';
+
+The _IO[RW] routines use a %sizeof array, which (presumably)
+is keyed on the type name with the value being the size in bytes.
+
+To build %sizeof, try running this in this directory:
+
+ % ./getioctlsizes
+
+Which will tell you which things the %sizeof array needs
+to hold. You can try to build a sizeof.ph file with:
+
+ % ./getioctlsizes | ./mksizes > sizeof.ph
+
+Note that mksizes hardcodes the #include files for all the types, so it will
+probably require customization. Once you have sizeof.ph, install it in the
+perl library directory. Run my tcbreak script to see whether you can do
+ioctls in perl now. You'll get some kind of fatal run-time error if you
+can't. That script should be included in this directory.
+
+If this works well, now you can try to convert the *.ph files into
+*.pl files. Try this:
+
+ foreach file ( sysexits.ph sys/{errno.ph,ioctl.ph} )
+ ./mkvars $file > t/$file:r.pl
+ end
+
+The last one will be the hardest. If it works, should be able to
+run tcbreak2 and have it work the same as tcbreak.
+
+Good luck.
diff --git a/gnu/usr.bin/perl/h2pl/cbreak.pl b/gnu/usr.bin/perl/h2pl/cbreak.pl
new file mode 100644
index 0000000..422185e
--- /dev/null
+++ b/gnu/usr.bin/perl/h2pl/cbreak.pl
@@ -0,0 +1,34 @@
+$sgttyb_t = 'C4 S';
+
+sub cbreak {
+ &set_cbreak(1);
+}
+
+sub cooked {
+ &set_cbreak(0);
+}
+
+sub set_cbreak {
+ local($on) = @_;
+
+ require 'sizeof.ph';
+ require 'sys/ioctl.ph';
+
+ ioctl(STDIN,&TIOCGETP,$sgttyb)
+ || die "Can't ioctl TIOCGETP: $!";
+
+ @ary = unpack($sgttyb_t,$sgttyb);
+ if ($on) {
+ $ary[4] |= &CBREAK;
+ $ary[4] &= ~&ECHO;
+ } else {
+ $ary[4] &= ~&CBREAK;
+ $ary[4] |= &ECHO;
+ }
+ $sgttyb = pack($sgttyb_t,@ary);
+ ioctl(STDIN,&TIOCSETP,$sgttyb)
+ || die "Can't ioctl TIOCSETP: $!";
+
+}
+
+1;
diff --git a/gnu/usr.bin/perl/h2pl/cbreak2.pl b/gnu/usr.bin/perl/h2pl/cbreak2.pl
new file mode 100644
index 0000000..8ac55a3
--- /dev/null
+++ b/gnu/usr.bin/perl/h2pl/cbreak2.pl
@@ -0,0 +1,33 @@
+$sgttyb_t = 'C4 S';
+
+sub cbreak {
+ &set_cbreak(1);
+}
+
+sub cooked {
+ &set_cbreak(0);
+}
+
+sub set_cbreak {
+ local($on) = @_;
+
+ require 'sys/ioctl.pl';
+
+ ioctl(STDIN,$TIOCGETP,$sgttyb)
+ || die "Can't ioctl TIOCGETP: $!";
+
+ @ary = unpack($sgttyb_t,$sgttyb);
+ if ($on) {
+ $ary[4] |= $CBREAK;
+ $ary[4] &= ~$ECHO;
+ } else {
+ $ary[4] &= ~$CBREAK;
+ $ary[4] |= $ECHO;
+ }
+ $sgttyb = pack($sgttyb_t,@ary);
+ ioctl(STDIN,$TIOCSETP,$sgttyb)
+ || die "Can't ioctl TIOCSETP: $!";
+
+}
+
+1;
diff --git a/gnu/usr.bin/perl/h2pl/eg/sizeof.ph b/gnu/usr.bin/perl/h2pl/eg/sizeof.ph
new file mode 100644
index 0000000..285bff1
--- /dev/null
+++ b/gnu/usr.bin/perl/h2pl/eg/sizeof.ph
@@ -0,0 +1,14 @@
+$sizeof{'char'} = 1;
+$sizeof{'int'} = 4;
+$sizeof{'long'} = 4;
+$sizeof{'struct arpreq'} = 36;
+$sizeof{'struct ifconf'} = 8;
+$sizeof{'struct ifreq'} = 32;
+$sizeof{'struct ltchars'} = 6;
+$sizeof{'struct pcntl'} = 116;
+$sizeof{'struct rtentry'} = 52;
+$sizeof{'struct sgttyb'} = 6;
+$sizeof{'struct tchars'} = 6;
+$sizeof{'struct ttychars'} = 14;
+$sizeof{'struct winsize'} = 8;
+$sizeof{'struct termios'} = 132;
diff --git a/gnu/usr.bin/perl/h2pl/eg/sys/errno.pl b/gnu/usr.bin/perl/h2pl/eg/sys/errno.pl
new file mode 100644
index 0000000..d9ba3be
--- /dev/null
+++ b/gnu/usr.bin/perl/h2pl/eg/sys/errno.pl
@@ -0,0 +1,92 @@
+$EPERM = 0x1;
+$ENOENT = 0x2;
+$ESRCH = 0x3;
+$EINTR = 0x4;
+$EIO = 0x5;
+$ENXIO = 0x6;
+$E2BIG = 0x7;
+$ENOEXEC = 0x8;
+$EBADF = 0x9;
+$ECHILD = 0xA;
+$EAGAIN = 0xB;
+$ENOMEM = 0xC;
+$EACCES = 0xD;
+$EFAULT = 0xE;
+$ENOTBLK = 0xF;
+$EBUSY = 0x10;
+$EEXIST = 0x11;
+$EXDEV = 0x12;
+$ENODEV = 0x13;
+$ENOTDIR = 0x14;
+$EISDIR = 0x15;
+$EINVAL = 0x16;
+$ENFILE = 0x17;
+$EMFILE = 0x18;
+$ENOTTY = 0x19;
+$ETXTBSY = 0x1A;
+$EFBIG = 0x1B;
+$ENOSPC = 0x1C;
+$ESPIPE = 0x1D;
+$EROFS = 0x1E;
+$EMLINK = 0x1F;
+$EPIPE = 0x20;
+$EDOM = 0x21;
+$ERANGE = 0x22;
+$EWOULDBLOCK = 0x23;
+$EINPROGRESS = 0x24;
+$EALREADY = 0x25;
+$ENOTSOCK = 0x26;
+$EDESTADDRREQ = 0x27;
+$EMSGSIZE = 0x28;
+$EPROTOTYPE = 0x29;
+$ENOPROTOOPT = 0x2A;
+$EPROTONOSUPPORT = 0x2B;
+$ESOCKTNOSUPPORT = 0x2C;
+$EOPNOTSUPP = 0x2D;
+$EPFNOSUPPORT = 0x2E;
+$EAFNOSUPPORT = 0x2F;
+$EADDRINUSE = 0x30;
+$EADDRNOTAVAIL = 0x31;
+$ENETDOWN = 0x32;
+$ENETUNREACH = 0x33;
+$ENETRESET = 0x34;
+$ECONNABORTED = 0x35;
+$ECONNRESET = 0x36;
+$ENOBUFS = 0x37;
+$EISCONN = 0x38;
+$ENOTCONN = 0x39;
+$ESHUTDOWN = 0x3A;
+$ETOOMANYREFS = 0x3B;
+$ETIMEDOUT = 0x3C;
+$ECONNREFUSED = 0x3D;
+$ELOOP = 0x3E;
+$ENAMETOOLONG = 0x3F;
+$EHOSTDOWN = 0x40;
+$EHOSTUNREACH = 0x41;
+$ENOTEMPTY = 0x42;
+$EPROCLIM = 0x43;
+$EUSERS = 0x44;
+$EDQUOT = 0x45;
+$ESTALE = 0x46;
+$EREMOTE = 0x47;
+$EDEADLK = 0x48;
+$ENOLCK = 0x49;
+$MTH_UNDEF_SQRT = 0x12C;
+$MTH_OVF_EXP = 0x12D;
+$MTH_UNDEF_LOG = 0x12E;
+$MTH_NEG_BASE = 0x12F;
+$MTH_ZERO_BASE = 0x130;
+$MTH_OVF_POW = 0x131;
+$MTH_LRG_SIN = 0x132;
+$MTH_LRG_COS = 0x133;
+$MTH_LRG_TAN = 0x134;
+$MTH_LRG_COT = 0x135;
+$MTH_OVF_TAN = 0x136;
+$MTH_OVF_COT = 0x137;
+$MTH_UNDEF_ASIN = 0x138;
+$MTH_UNDEF_ACOS = 0x139;
+$MTH_UNDEF_ATAN2 = 0x13A;
+$MTH_OVF_SINH = 0x13B;
+$MTH_OVF_COSH = 0x13C;
+$MTH_UNDEF_ZLOG = 0x13D;
+$MTH_UNDEF_ZDIV = 0x13E;
diff --git a/gnu/usr.bin/perl/h2pl/eg/sys/ioctl.pl b/gnu/usr.bin/perl/h2pl/eg/sys/ioctl.pl
new file mode 100644
index 0000000..0b552ca
--- /dev/null
+++ b/gnu/usr.bin/perl/h2pl/eg/sys/ioctl.pl
@@ -0,0 +1,186 @@
+$_IOCTL_ = 0x1;
+$TIOCGSIZE = 0x40087468;
+$TIOCSSIZE = 0x80087467;
+$IOCPARM_MASK = 0x7F;
+$IOC_VOID = 0x20000000;
+$IOC_OUT = 0x40000000;
+$IOC_IN = 0x80000000;
+$IOC_INOUT = 0xC0000000;
+$TIOCGETD = 0x40047400;
+$TIOCSETD = 0x80047401;
+$TIOCHPCL = 0x20007402;
+$TIOCMODG = 0x40047403;
+$TIOCMODS = 0x80047404;
+$TIOCM_LE = 0x1;
+$TIOCM_DTR = 0x2;
+$TIOCM_RTS = 0x4;
+$TIOCM_ST = 0x8;
+$TIOCM_SR = 0x10;
+$TIOCM_CTS = 0x20;
+$TIOCM_CAR = 0x40;
+$TIOCM_CD = 0x40;
+$TIOCM_RNG = 0x80;
+$TIOCM_RI = 0x80;
+$TIOCM_DSR = 0x100;
+$TIOCGETP = 0x40067408;
+$TIOCSETP = 0x80067409;
+$TIOCSETN = 0x8006740A;
+$TIOCEXCL = 0x2000740D;
+$TIOCNXCL = 0x2000740E;
+$TIOCFLUSH = 0x80047410;
+$TIOCSETC = 0x80067411;
+$TIOCGETC = 0x40067412;
+$TIOCSET = 0x80047413;
+$TIOCBIS = 0x80047414;
+$TIOCBIC = 0x80047415;
+$TIOCGET = 0x40047416;
+$TANDEM = 0x1;
+$CBREAK = 0x2;
+$LCASE = 0x4;
+$ECHO = 0x8;
+$CRMOD = 0x10;
+$RAW = 0x20;
+$ODDP = 0x40;
+$EVENP = 0x80;
+$ANYP = 0xC0;
+$NLDELAY = 0x300;
+$NL0 = 0x0;
+$NL1 = 0x100;
+$NL2 = 0x200;
+$NL3 = 0x300;
+$TBDELAY = 0xC00;
+$TAB0 = 0x0;
+$TAB1 = 0x400;
+$TAB2 = 0x800;
+$XTABS = 0xC00;
+$CRDELAY = 0x3000;
+$CR0 = 0x0;
+$CR1 = 0x1000;
+$CR2 = 0x2000;
+$CR3 = 0x3000;
+$VTDELAY = 0x4000;
+$FF0 = 0x0;
+$FF1 = 0x4000;
+$BSDELAY = 0x8000;
+$BS0 = 0x0;
+$BS1 = 0x8000;
+$ALLDELAY = 0xFF00;
+$CRTBS = 0x10000;
+$PRTERA = 0x20000;
+$CRTERA = 0x40000;
+$TILDE = 0x80000;
+$MDMBUF = 0x100000;
+$LITOUT = 0x200000;
+$TOSTOP = 0x400000;
+$FLUSHO = 0x800000;
+$NOHANG = 0x1000000;
+$L001000 = 0x2000000;
+$CRTKIL = 0x4000000;
+$L004000 = 0x8000000;
+$CTLECH = 0x10000000;
+$PENDIN = 0x20000000;
+$DECCTQ = 0x40000000;
+$NOFLSH = 0x80000000;
+$TIOCCSET = 0x800E7417;
+$TIOCCGET = 0x400E7418;
+$TIOCLBIS = 0x8004747F;
+$TIOCLBIC = 0x8004747E;
+$TIOCLSET = 0x8004747D;
+$TIOCLGET = 0x4004747C;
+$LCRTBS = 0x1;
+$LPRTERA = 0x2;
+$LCRTERA = 0x4;
+$LTILDE = 0x8;
+$LMDMBUF = 0x10;
+$LLITOUT = 0x20;
+$LTOSTOP = 0x40;
+$LFLUSHO = 0x80;
+$LNOHANG = 0x100;
+$LCRTKIL = 0x400;
+$LCTLECH = 0x1000;
+$LPENDIN = 0x2000;
+$LDECCTQ = 0x4000;
+$LNOFLSH = 0x8000;
+$TIOCSBRK = 0x2000747B;
+$TIOCCBRK = 0x2000747A;
+$TIOCSDTR = 0x20007479;
+$TIOCCDTR = 0x20007478;
+$TIOCGPGRP = 0x40047477;
+$TIOCSPGRP = 0x80047476;
+$TIOCSLTC = 0x80067475;
+$TIOCGLTC = 0x40067474;
+$TIOCOUTQ = 0x40047473;
+$TIOCSTI = 0x80017472;
+$TIOCNOTTY = 0x20007471;
+$TIOCPKT = 0x80047470;
+$TIOCPKT_DATA = 0x0;
+$TIOCPKT_FLUSHREAD = 0x1;
+$TIOCPKT_FLUSHWRITE = 0x2;
+$TIOCPKT_STOP = 0x4;
+$TIOCPKT_START = 0x8;
+$TIOCPKT_NOSTOP = 0x10;
+$TIOCPKT_DOSTOP = 0x20;
+$TIOCSTOP = 0x2000746F;
+$TIOCSTART = 0x2000746E;
+$TIOCREMOTE = 0x20007469;
+$TIOCGWINSZ = 0x40087468;
+$TIOCSWINSZ = 0x80087467;
+$TIOCRESET = 0x20007466;
+$OTTYDISC = 0x0;
+$NETLDISC = 0x1;
+$NTTYDISC = 0x2;
+$FIOCLEX = 0x20006601;
+$FIONCLEX = 0x20006602;
+$FIONREAD = 0x4004667F;
+$FIONBIO = 0x8004667E;
+$FIOASYNC = 0x8004667D;
+$FIOSETOWN = 0x8004667C;
+$FIOGETOWN = 0x4004667B;
+$STPUTTABLE = 0x8004667A;
+$STGETTABLE = 0x80046679;
+$SIOCSHIWAT = 0x80047300;
+$SIOCGHIWAT = 0x40047301;
+$SIOCSLOWAT = 0x80047302;
+$SIOCGLOWAT = 0x40047303;
+$SIOCATMARK = 0x40047307;
+$SIOCSPGRP = 0x80047308;
+$SIOCGPGRP = 0x40047309;
+$SIOCADDRT = 0x8034720A;
+$SIOCDELRT = 0x8034720B;
+$SIOCSIFADDR = 0x8020690C;
+$SIOCGIFADDR = 0xC020690D;
+$SIOCSIFDSTADDR = 0x8020690E;
+$SIOCGIFDSTADDR = 0xC020690F;
+$SIOCSIFFLAGS = 0x80206910;
+$SIOCGIFFLAGS = 0xC0206911;
+$SIOCGIFBRDADDR = 0xC0206912;
+$SIOCSIFBRDADDR = 0x80206913;
+$SIOCGIFCONF = 0xC0086914;
+$SIOCGIFNETMASK = 0xC0206915;
+$SIOCSIFNETMASK = 0x80206916;
+$SIOCGIFMETRIC = 0xC0206917;
+$SIOCSIFMETRIC = 0x80206918;
+$SIOCSARP = 0x8024691E;
+$SIOCGARP = 0xC024691F;
+$SIOCDARP = 0x80246920;
+$PIXCONTINUE = 0x80747000;
+$PIXSTEP = 0x80747001;
+$PIXTERMINATE = 0x20007002;
+$PIGETFLAGS = 0x40747003;
+$PIXINHERIT = 0x80747004;
+$PIXDETACH = 0x20007005;
+$PIXGETSUBCODE = 0xC0747006;
+$PIXRDREGS = 0xC0747007;
+$PIXWRREGS = 0xC0747008;
+$PIXRDVREGS = 0xC0747009;
+$PIXWRVREGS = 0xC074700A;
+$PIXRDVSTATE = 0xC074700B;
+$PIXWRVSTATE = 0xC074700C;
+$PIXRDCREGS = 0xC074700D;
+$PIXWRCREGS = 0xC074700E;
+$PIRDSDRS = 0xC074700F;
+$PIXGETSIGACTION = 0xC0747010;
+$PIGETU = 0xC0747011;
+$PISETRWTID = 0xC0747012;
+$PIXGETTHCOUNT = 0xC0747013;
+$PIXRUN = 0x20007014;
diff --git a/gnu/usr.bin/perl/h2pl/eg/sysexits.pl b/gnu/usr.bin/perl/h2pl/eg/sysexits.pl
new file mode 100644
index 0000000..f4cb777
--- /dev/null
+++ b/gnu/usr.bin/perl/h2pl/eg/sysexits.pl
@@ -0,0 +1,16 @@
+$EX_OK = 0x0;
+$EX__BASE = 0x40;
+$EX_USAGE = 0x40;
+$EX_DATAERR = 0x41;
+$EX_NOINPUT = 0x42;
+$EX_NOUSER = 0x43;
+$EX_NOHOST = 0x44;
+$EX_UNAVAILABLE = 0x45;
+$EX_SOFTWARE = 0x46;
+$EX_OSERR = 0x47;
+$EX_OSFILE = 0x48;
+$EX_CANTCREAT = 0x49;
+$EX_IOERR = 0x4A;
+$EX_TEMPFAIL = 0x4B;
+$EX_PROTOCOL = 0x4C;
+$EX_NOPERM = 0x4D;
diff --git a/gnu/usr.bin/perl/h2pl/getioctlsizes b/gnu/usr.bin/perl/h2pl/getioctlsizes
new file mode 100644
index 0000000..403fffa
--- /dev/null
+++ b/gnu/usr.bin/perl/h2pl/getioctlsizes
@@ -0,0 +1,13 @@
+#!/usr/bin/perl
+
+open (IOCTLS,'/usr/include/sys/ioctl.h') || die "ioctl open failed";
+
+while (<IOCTLS>) {
+ if (/^\s*#\s*define\s+\w+\s+_IO(R|W|WR)\('?\w+'?,\s*\w+,\s*([^)]+)/) {
+ $need{$2}++;
+ }
+}
+
+foreach $key ( sort keys %need ) {
+ print $key,"\n";
+}
diff --git a/gnu/usr.bin/perl/h2pl/mksizes b/gnu/usr.bin/perl/h2pl/mksizes
new file mode 100644
index 0000000..cb4b8ab
--- /dev/null
+++ b/gnu/usr.bin/perl/h2pl/mksizes
@@ -0,0 +1,42 @@
+#!/usr/local/bin/perl
+
+($iam = $0) =~ s%.*/%%;
+$tmp = "$iam.$$";
+open (CODE,">$tmp.c") || die "$iam: cannot create $tmp.c: $!\n";
+
+$mask = q/printf ("$sizeof{'%s'} = %d;\n"/;
+
+# write C program
+select(CODE);
+
+print <<EO_C_PROGRAM;
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <sys/ioctl.h>
+
+main() {
+EO_C_PROGRAM
+
+while ( <> ) {
+ chop;
+ printf "\t%s, \n\t\t\"%s\", sizeof(%s));\n", $mask, $_,$_;
+}
+
+print "\n}\n";
+
+close CODE;
+
+# compile C program
+
+select(STDOUT);
+
+system "cc $tmp.c -o $tmp";
+die "couldn't compile $tmp.c" if $?;
+system "./$tmp";
+die "couldn't run $tmp" if $?;
+
+unlink "$tmp.c", $tmp;
diff --git a/gnu/usr.bin/perl/h2pl/mkvars b/gnu/usr.bin/perl/h2pl/mkvars
new file mode 100644
index 0000000..ffb0f0b
--- /dev/null
+++ b/gnu/usr.bin/perl/h2pl/mkvars
@@ -0,0 +1,31 @@
+#!/usr/bin/perl
+
+require 'sizeof.ph';
+
+$LIB = '/usr/local/lib/perl';
+
+foreach $include (@ARGV) {
+ printf STDERR "including %s\n", $include;
+ do $include;
+ warn "sourcing $include: $@\n" if ($@);
+ if (!open (INCLUDE,"$LIB/$include")) {
+ warn "can't open $LIB/$include: $!\n";
+ next;
+ }
+ while (<INCLUDE>) {
+ chop;
+ if (/^\s*eval\s+'sub\s+(\w+)\s.*[^{]$/ || /^\s*sub\s+(\w+)\s.*[^{]$/) {
+ $var = $1;
+ $val = eval "&$var;";
+ if ($@) {
+ warn "$@: $_";
+ print <<EOT;
+warn "\$$var isn't correctly set" if defined \$_main{'$var'};
+EOT
+ next;
+ }
+ ( $nval = sprintf ("%x",$val ) ) =~ tr/a-z/A-Z/;
+ printf "\$%s = 0x%s;\n", $var, $nval;
+ }
+ }
+}
diff --git a/gnu/usr.bin/perl/h2pl/tcbreak b/gnu/usr.bin/perl/h2pl/tcbreak
new file mode 100644
index 0000000..2677cc9
--- /dev/null
+++ b/gnu/usr.bin/perl/h2pl/tcbreak
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+require 'cbreak.pl';
+
+&cbreak;
+
+$| = 1;
+
+print "gimme a char: ";
+
+$c = getc;
+
+print "$c\n";
+
+printf "you gave me `%s', which is 0x%02x\n", $c, ord($c);
+
+&cooked;
diff --git a/gnu/usr.bin/perl/h2pl/tcbreak2 b/gnu/usr.bin/perl/h2pl/tcbreak2
new file mode 100644
index 0000000..fcbf926
--- /dev/null
+++ b/gnu/usr.bin/perl/h2pl/tcbreak2
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+require 'cbreak2.pl';
+
+&cbreak;
+
+$| = 1;
+
+print "gimme a char: ";
+
+$c = getc;
+
+print "$c\n";
+
+printf "you gave me `%s', which is 0x%02x\n", $c, ord($c);
+
+&cooked;
diff --git a/gnu/usr.bin/perl/lib/Makefile b/gnu/usr.bin/perl/lib/Makefile
new file mode 100644
index 0000000..375720d
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/Makefile
@@ -0,0 +1,19 @@
+PLIBDIR= /usr/local/lib/perl
+
+PLIB+= abbrev.pl assert.pl bigfloat.pl bigint.pl bigrat.pl cacheout.pl
+PLIB+= chat2.pl complete.pl ctime.pl dumpvar.pl exceptions.pl fastcwd.pl
+PLIB+= find.pl finddepth.pl flush.pl getcwd.pl getopts.pl importenv.pl
+PLIB+= look.pl newgetopt.pl open2.pl perldb.pl pwd.pl shellwords.pl
+PLIB+= stat.pl syslog.pl termcap.pl timelocal.pl validate.pl
+
+install:
+ mkdir -p ${PLIBDIR}
+ install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${PLIB} ${PLIBDIR}
+
+clean:
+cleandir:
+obj:
+
+.include <bsd.prog.mk>
+
+
diff --git a/gnu/usr.bin/perl/lib/abbrev.pl b/gnu/usr.bin/perl/lib/abbrev.pl
new file mode 100644
index 0000000..c233d4a
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/abbrev.pl
@@ -0,0 +1,33 @@
+;# Usage:
+;# %foo = ();
+;# &abbrev(*foo,LIST);
+;# ...
+;# $long = $foo{$short};
+
+package abbrev;
+
+sub main'abbrev {
+ local(*domain) = @_;
+ shift(@_);
+ @cmp = @_;
+ local($[) = 0;
+ foreach $name (@_) {
+ @extra = split(//,$name);
+ $abbrev = shift(@extra);
+ $len = 1;
+ foreach $cmp (@cmp) {
+ next if $cmp eq $name;
+ while (substr($cmp,0,$len) eq $abbrev) {
+ $abbrev .= shift(@extra);
+ ++$len;
+ }
+ }
+ $domain{$abbrev} = $name;
+ while ($#extra >= 0) {
+ $abbrev .= shift(@extra);
+ $domain{$abbrev} = $name;
+ }
+ }
+}
+
+1;
diff --git a/gnu/usr.bin/perl/lib/assert.pl b/gnu/usr.bin/perl/lib/assert.pl
new file mode 100644
index 0000000..cfda70c
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/assert.pl
@@ -0,0 +1,52 @@
+# assert.pl
+# tchrist@convex.com (Tom Christiansen)
+#
+# Usage:
+#
+# &assert('@x > @y');
+# &assert('$var > 10', $var, $othervar, @various_info);
+#
+# That is, if the first expression evals false, we blow up. The
+# rest of the args, if any, are nice to know because they will
+# be printed out by &panic, which is just the stack-backtrace
+# routine shamelessly borrowed from the perl debugger.
+
+sub assert {
+ &panic("ASSERTION BOTCHED: $_[0]",$@) unless eval $_[0];
+}
+
+sub panic {
+ select(STDERR);
+
+ print "\npanic: @_\n";
+
+ exit 1 if $] <= 4.003; # caller broken
+
+ # stack traceback gratefully borrowed from perl debugger
+
+ local($i,$_);
+ local($p,$f,$l,$s,$h,$a,@a,@sub);
+ for ($i = 0; ($p,$f,$l,$s,$h,$w) = caller($i); $i++) {
+ @a = @DB'args;
+ for (@a) {
+ if (/^StB\000/ && length($_) == length($_main{'_main'})) {
+ $_ = sprintf("%s",$_);
+ }
+ else {
+ s/'/\\'/g;
+ s/([^\0]*)/'$1'/ unless /^-?[\d.]+$/;
+ s/([\200-\377])/sprintf("M-%c",ord($1)&0177)/eg;
+ s/([\0-\37\177])/sprintf("^%c",ord($1)^64)/eg;
+ }
+ }
+ $w = $w ? '@ = ' : '$ = ';
+ $a = $h ? '(' . join(', ', @a) . ')' : '';
+ push(@sub, "$w&$s$a from file $f line $l\n");
+ }
+ for ($i=0; $i <= $#sub; $i++) {
+ print $sub[$i];
+ }
+ exit 1;
+}
+
+1;
diff --git a/gnu/usr.bin/perl/lib/bigfloat.pl b/gnu/usr.bin/perl/lib/bigfloat.pl
new file mode 100644
index 0000000..278f11d
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/bigfloat.pl
@@ -0,0 +1,233 @@
+package bigfloat;
+require "bigint.pl";
+# Arbitrary length float math package
+#
+# by Mark Biggar
+#
+# number format
+# canonical strings have the form /[+-]\d+E[+-]\d+/
+# Input values can have inbedded whitespace
+# Error returns
+# 'NaN' An input parameter was "Not a Number" or
+# divide by zero or sqrt of negative number
+# Division is computed to
+# max($div_scale,length(dividend).length(divisor))
+# digits by default.
+# Also used for default sqrt scale
+
+$div_scale = 40;
+
+# Rounding modes one of 'even', 'odd', '+inf', '-inf', 'zero' or 'trunc'.
+
+$rnd_mode = 'even';
+
+# bigfloat routines
+#
+# fadd(NSTR, NSTR) return NSTR addition
+# fsub(NSTR, NSTR) return NSTR subtraction
+# fmul(NSTR, NSTR) return NSTR multiplication
+# fdiv(NSTR, NSTR[,SCALE]) returns NSTR division to SCALE places
+# fneg(NSTR) return NSTR negation
+# fabs(NSTR) return NSTR absolute value
+# fcmp(NSTR,NSTR) return CODE compare undef,<0,=0,>0
+# fround(NSTR, SCALE) return NSTR round to SCALE digits
+# ffround(NSTR, SCALE) return NSTR round at SCALEth place
+# fnorm(NSTR) return (NSTR) normalize
+# fsqrt(NSTR[, SCALE]) return NSTR sqrt to SCALE places
+
+# Convert a number to canonical string form.
+# Takes something that looks like a number and converts it to
+# the form /^[+-]\d+E[+-]\d+$/.
+sub main'fnorm { #(string) return fnum_str
+ local($_) = @_;
+ s/\s+//g; # strip white space
+ if (/^([+-]?)(\d*)(\.(\d*))?([Ee]([+-]?\d+))?$/ && "$2$4" ne '') {
+ &norm(($1 ? "$1$2$4" : "+$2$4"),(($4 ne '') ? $6-length($4) : $6));
+ } else {
+ 'NaN';
+ }
+}
+
+# normalize number -- for internal use
+sub norm { #(mantissa, exponent) return fnum_str
+ local($_, $exp) = @_;
+ if ($_ eq 'NaN') {
+ 'NaN';
+ } else {
+ s/^([+-])0+/$1/; # strip leading zeros
+ if (length($_) == 1) {
+ '+0E+0';
+ } else {
+ $exp += length($1) if (s/(0+)$//); # strip trailing zeros
+ sprintf("%sE%+ld", $_, $exp);
+ }
+ }
+}
+
+# negation
+sub main'fneg { #(fnum_str) return fnum_str
+ local($_) = &'fnorm($_[0]);
+ vec($_,0,8) ^= ord('+') ^ ord('-') unless $_ eq '+0E+0'; # flip sign
+ s/^H/N/;
+ $_;
+}
+
+# absolute value
+sub main'fabs { #(fnum_str) return fnum_str
+ local($_) = &'fnorm($_[0]);
+ s/^-/+/; # mash sign
+ $_;
+}
+
+# multiplication
+sub main'fmul { #(fnum_str, fnum_str) return fnum_str
+ local($x,$y) = (&'fnorm($_[0]),&'fnorm($_[1]));
+ if ($x eq 'NaN' || $y eq 'NaN') {
+ 'NaN';
+ } else {
+ local($xm,$xe) = split('E',$x);
+ local($ym,$ye) = split('E',$y);
+ &norm(&'bmul($xm,$ym),$xe+$ye);
+ }
+}
+
+# addition
+sub main'fadd { #(fnum_str, fnum_str) return fnum_str
+ local($x,$y) = (&'fnorm($_[0]),&'fnorm($_[1]));
+ if ($x eq 'NaN' || $y eq 'NaN') {
+ 'NaN';
+ } else {
+ local($xm,$xe) = split('E',$x);
+ local($ym,$ye) = split('E',$y);
+ ($xm,$xe,$ym,$ye) = ($ym,$ye,$xm,$xe) if ($xe < $ye);
+ &norm(&'badd($ym,$xm.('0' x ($xe-$ye))),$ye);
+ }
+}
+
+# subtraction
+sub main'fsub { #(fnum_str, fnum_str) return fnum_str
+ &'fadd($_[0],&'fneg($_[1]));
+}
+
+# division
+# args are dividend, divisor, scale (optional)
+# result has at most max(scale, length(dividend), length(divisor)) digits
+sub main'fdiv #(fnum_str, fnum_str[,scale]) return fnum_str
+{
+ local($x,$y,$scale) = (&'fnorm($_[0]),&'fnorm($_[1]),$_[2]);
+ if ($x eq 'NaN' || $y eq 'NaN' || $y eq '+0E+0') {
+ 'NaN';
+ } else {
+ local($xm,$xe) = split('E',$x);
+ local($ym,$ye) = split('E',$y);
+ $scale = $div_scale if (!$scale);
+ $scale = length($xm)-1 if (length($xm)-1 > $scale);
+ $scale = length($ym)-1 if (length($ym)-1 > $scale);
+ $scale = $scale + length($ym) - length($xm);
+ &norm(&round(&'bdiv($xm.('0' x $scale),$ym),$ym),
+ $xe-$ye-$scale);
+ }
+}
+
+# round int $q based on fraction $r/$base using $rnd_mode
+sub round { #(int_str, int_str, int_str) return int_str
+ local($q,$r,$base) = @_;
+ if ($q eq 'NaN' || $r eq 'NaN') {
+ 'NaN';
+ } elsif ($rnd_mode eq 'trunc') {
+ $q; # just truncate
+ } else {
+ local($cmp) = &'bcmp(&'bmul($r,'+2'),$base);
+ if ( $cmp < 0 ||
+ ($cmp == 0 &&
+ ( $rnd_mode eq 'zero' ||
+ ($rnd_mode eq '-inf' && (substr($q,0,1) eq '+')) ||
+ ($rnd_mode eq '+inf' && (substr($q,0,1) eq '-')) ||
+ ($rnd_mode eq 'even' && $q =~ /[24680]$/) ||
+ ($rnd_mode eq 'odd' && $q =~ /[13579]$/) )) ) {
+ $q; # round down
+ } else {
+ &'badd($q, ((substr($q,0,1) eq '-') ? '-1' : '+1'));
+ # round up
+ }
+ }
+}
+
+# round the mantissa of $x to $scale digits
+sub main'fround { #(fnum_str, scale) return fnum_str
+ local($x,$scale) = (&'fnorm($_[0]),$_[1]);
+ if ($x eq 'NaN' || $scale <= 0) {
+ $x;
+ } else {
+ local($xm,$xe) = split('E',$x);
+ if (length($xm)-1 <= $scale) {
+ $x;
+ } else {
+ &norm(&round(substr($xm,0,$scale+1),
+ "+0".substr($xm,$scale+1,1),"+10"),
+ $xe+length($xm)-$scale-1);
+ }
+ }
+}
+
+# round $x at the 10 to the $scale digit place
+sub main'ffround { #(fnum_str, scale) return fnum_str
+ local($x,$scale) = (&'fnorm($_[0]),$_[1]);
+ if ($x eq 'NaN') {
+ 'NaN';
+ } else {
+ local($xm,$xe) = split('E',$x);
+ if ($xe >= $scale) {
+ $x;
+ } else {
+ $xe = length($xm)+$xe-$scale;
+ if ($xe < 1) {
+ '+0E+0';
+ } elsif ($xe == 1) {
+ &norm(&round('+0',"+0".substr($xm,1,1),"+10"), $scale);
+ } else {
+ &norm(&round(substr($xm,0,$trunc),
+ "+0".substr($xm,$trunc,1),"+10"), $scale);
+ }
+ }
+ }
+}
+
+# compare 2 values returns one of undef, <0, =0, >0
+# returns undef if either or both input value are not numbers
+sub main'fcmp #(fnum_str, fnum_str) return cond_code
+{
+ local($x, $y) = (&'fnorm($_[0]),&'fnorm($_[1]));
+ if ($x eq "NaN" || $y eq "NaN") {
+ undef;
+ } else {
+ ord($y) <=> ord($x)
+ ||
+ ( local($xm,$xe,$ym,$ye) = split('E', $x."E$y"),
+ (($xe <=> $ye) * (substr($x,0,1).'1')
+ || &bigint'cmp($xm,$ym))
+ );
+ }
+}
+
+# square root by Newtons method.
+sub main'fsqrt { #(fnum_str[, scale]) return fnum_str
+ local($x, $scale) = (&'fnorm($_[0]), $_[1]);
+ if ($x eq 'NaN' || $x =~ /^-/) {
+ 'NaN';
+ } elsif ($x eq '+0E+0') {
+ '+0E+0';
+ } else {
+ local($xm, $xe) = split('E',$x);
+ $scale = $div_scale if (!$scale);
+ $scale = length($xm)-1 if ($scale < length($xm)-1);
+ local($gs, $guess) = (1, sprintf("1E%+d", (length($xm)+$xe-1)/2));
+ while ($gs < 2*$scale) {
+ $guess = &'fmul(&'fadd($guess,&'fdiv($x,$guess,$gs*2)),".5");
+ $gs *= 2;
+ }
+ &'fround($guess, $scale);
+ }
+}
+
+1;
diff --git a/gnu/usr.bin/perl/lib/bigint.pl b/gnu/usr.bin/perl/lib/bigint.pl
new file mode 100644
index 0000000..5c79da9
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/bigint.pl
@@ -0,0 +1,271 @@
+package bigint;
+
+# arbitrary size integer math package
+#
+# by Mark Biggar
+#
+# Canonical Big integer value are strings of the form
+# /^[+-]\d+$/ with leading zeros suppressed
+# Input values to these routines may be strings of the form
+# /^\s*[+-]?[\d\s]+$/.
+# Examples:
+# '+0' canonical zero value
+# ' -123 123 123' canonical value '-123123123'
+# '1 23 456 7890' canonical value '+1234567890'
+# Output values always always in canonical form
+#
+# Actual math is done in an internal format consisting of an array
+# whose first element is the sign (/^[+-]$/) and whose remaining
+# elements are base 100000 digits with the least significant digit first.
+# The string 'NaN' is used to represent the result when input arguments
+# are not numbers, as well as the result of dividing by zero
+#
+# routines provided are:
+#
+# bneg(BINT) return BINT negation
+# babs(BINT) return BINT absolute value
+# bcmp(BINT,BINT) return CODE compare numbers (undef,<0,=0,>0)
+# badd(BINT,BINT) return BINT addition
+# bsub(BINT,BINT) return BINT subtraction
+# bmul(BINT,BINT) return BINT multiplication
+# bdiv(BINT,BINT) return (BINT,BINT) division (quo,rem) just quo if scalar
+# bmod(BINT,BINT) return BINT modulus
+# bgcd(BINT,BINT) return BINT greatest common divisor
+# bnorm(BINT) return BINT normalization
+#
+
+# normalize string form of number. Strip leading zeros. Strip any
+# white space and add a sign, if missing.
+# Strings that are not numbers result the value 'NaN'.
+sub main'bnorm { #(num_str) return num_str
+ local($_) = @_;
+ s/\s+//g; # strip white space
+ if (s/^([+-]?)0*(\d+)$/$1$2/) { # test if number
+ substr($_,0,0) = '+' unless $1; # Add missing sign
+ s/^-0/+0/;
+ $_;
+ } else {
+ 'NaN';
+ }
+}
+
+# Convert a number from string format to internal base 100000 format.
+# Assumes normalized value as input.
+sub internal { #(num_str) return int_num_array
+ local($d) = @_;
+ ($is,$il) = (substr($d,0,1),length($d)-2);
+ substr($d,0,1) = '';
+ ($is, reverse(unpack("a" . ($il%5+1) . ("a5" x ($il/5)), $d)));
+}
+
+# Convert a number from internal base 100000 format to string format.
+# This routine scribbles all over input array.
+sub external { #(int_num_array) return num_str
+ $es = shift;
+ grep($_ > 9999 || ($_ = substr('0000'.$_,-5)), @_); # zero pad
+ &'bnorm(join('', $es, reverse(@_))); # reverse concat and normalize
+}
+
+# Negate input value.
+sub main'bneg { #(num_str) return num_str
+ local($_) = &'bnorm(@_);
+ vec($_,0,8) ^= ord('+') ^ ord('-') unless $_ eq '+0';
+ s/^H/N/;
+ $_;
+}
+
+# Returns the absolute value of the input.
+sub main'babs { #(num_str) return num_str
+ &abs(&'bnorm(@_));
+}
+
+sub abs { # post-normalized abs for internal use
+ local($_) = @_;
+ s/^-/+/;
+ $_;
+}
+
+# Compares 2 values. Returns one of undef, <0, =0, >0. (suitable for sort)
+sub main'bcmp { #(num_str, num_str) return cond_code
+ local($x,$y) = (&'bnorm($_[0]),&'bnorm($_[1]));
+ if ($x eq 'NaN') {
+ undef;
+ } elsif ($y eq 'NaN') {
+ undef;
+ } else {
+ &cmp($x,$y);
+ }
+}
+
+sub cmp { # post-normalized compare for internal use
+ local($cx, $cy) = @_;
+ $cx cmp $cy
+ &&
+ (
+ ord($cy) <=> ord($cx)
+ ||
+ ($cx cmp ',') * (length($cy) <=> length($cx) || $cy cmp $cx)
+ );
+}
+
+sub main'badd { #(num_str, num_str) return num_str
+ local(*x, *y); ($x, $y) = (&'bnorm($_[0]),&'bnorm($_[1]));
+ if ($x eq 'NaN') {
+ 'NaN';
+ } elsif ($y eq 'NaN') {
+ 'NaN';
+ } else {
+ @x = &internal($x); # convert to internal form
+ @y = &internal($y);
+ local($sx, $sy) = (shift @x, shift @y); # get signs
+ if ($sx eq $sy) {
+ &external($sx, &add(*x, *y)); # if same sign add
+ } else {
+ ($x, $y) = (&abs($x),&abs($y)); # make abs
+ if (&cmp($y,$x) > 0) {
+ &external($sy, &sub(*y, *x));
+ } else {
+ &external($sx, &sub(*x, *y));
+ }
+ }
+ }
+}
+
+sub main'bsub { #(num_str, num_str) return num_str
+ &'badd($_[0],&'bneg($_[1]));
+}
+
+# GCD -- Euclids algorithm Knuth Vol 2 pg 296
+sub main'bgcd { #(num_str, num_str) return num_str
+ local($x,$y) = (&'bnorm($_[0]),&'bnorm($_[1]));
+ if ($x eq 'NaN' || $y eq 'NaN') {
+ 'NaN';
+ } else {
+ ($x, $y) = ($y,&'bmod($x,$y)) while $y ne '+0';
+ $x;
+ }
+}
+
+# routine to add two base 1e5 numbers
+# stolen from Knuth Vol 2 Algorithm A pg 231
+# there are separate routines to add and sub as per Kunth pg 233
+sub add { #(int_num_array, int_num_array) return int_num_array
+ local(*x, *y) = @_;
+ $car = 0;
+ for $x (@x) {
+ last unless @y || $car;
+ $x -= 1e5 if $car = (($x += shift(@y) + $car) >= 1e5);
+ }
+ for $y (@y) {
+ last unless $car;
+ $y -= 1e5 if $car = (($y += $car) >= 1e5);
+ }
+ (@x, @y, $car);
+}
+
+# subtract base 1e5 numbers -- stolen from Knuth Vol 2 pg 232, $x > $y
+sub sub { #(int_num_array, int_num_array) return int_num_array
+ local(*sx, *sy) = @_;
+ $bar = 0;
+ for $sx (@sx) {
+ last unless @y || $bar;
+ $sx += 1e5 if $bar = (($sx -= shift(@sy) + $bar) < 0);
+ }
+ @sx;
+}
+
+# multiply two numbers -- stolen from Knuth Vol 2 pg 233
+sub main'bmul { #(num_str, num_str) return num_str
+ local(*x, *y); ($x, $y) = (&'bnorm($_[0]), &'bnorm($_[1]));
+ if ($x eq 'NaN') {
+ 'NaN';
+ } elsif ($y eq 'NaN') {
+ 'NaN';
+ } else {
+ @x = &internal($x);
+ @y = &internal($y);
+ local($signr) = (shift @x ne shift @y) ? '-' : '+';
+ @prod = ();
+ for $x (@x) {
+ ($car, $cty) = (0, 0);
+ for $y (@y) {
+ $prod = $x * $y + $prod[$cty] + $car;
+ $prod[$cty++] =
+ $prod - ($car = int($prod * 1e-5)) * 1e5;
+ }
+ $prod[$cty] += $car if $car;
+ $x = shift @prod;
+ }
+ &external($signr, @x, @prod);
+ }
+}
+
+# modulus
+sub main'bmod { #(num_str, num_str) return num_str
+ (&'bdiv(@_))[1];
+}
+
+sub main'bdiv { #(dividend: num_str, divisor: num_str) return num_str
+ local (*x, *y); ($x, $y) = (&'bnorm($_[0]), &'bnorm($_[1]));
+ return wantarray ? ('NaN','NaN') : 'NaN'
+ if ($x eq 'NaN' || $y eq 'NaN' || $y eq '+0');
+ return wantarray ? ('+0',$x) : '+0' if (&cmp(&abs($x),&abs($y)) < 0);
+ @x = &internal($x); @y = &internal($y);
+ $srem = $y[0];
+ $sr = (shift @x ne shift @y) ? '-' : '+';
+ $car = $bar = $prd = 0;
+ if (($dd = int(1e5/($y[$#y]+1))) != 1) {
+ for $x (@x) {
+ $x = $x * $dd + $car;
+ $x -= ($car = int($x * 1e-5)) * 1e5;
+ }
+ push(@x, $car); $car = 0;
+ for $y (@y) {
+ $y = $y * $dd + $car;
+ $y -= ($car = int($y * 1e-5)) * 1e5;
+ }
+ }
+ else {
+ push(@x, 0);
+ }
+ @q = (); ($v2,$v1) = @y[$#y-1,$#y];
+ while ($#x > $#y) {
+ ($u2,$u1,$u0) = @x[($#x-2)..$#x];
+ $q = (($u0 == $v1) ? 99999 : int(($u0*1e5+$u1)/$v1));
+ --$q while ($v2*$q > ($u0*1e5+$u1-$q*$v1)*1e5+$u2);
+ if ($q) {
+ ($car, $bar) = (0,0);
+ for ($y = 0, $x = $#x-$#y-1; $y <= $#y; ++$y,++$x) {
+ $prd = $q * $y[$y] + $car;
+ $prd -= ($car = int($prd * 1e-5)) * 1e5;
+ $x[$x] += 1e5 if ($bar = (($x[$x] -= $prd + $bar) < 0));
+ }
+ if ($x[$#x] < $car + $bar) {
+ $car = 0; --$q;
+ for ($y = 0, $x = $#x-$#y-1; $y <= $#y; ++$y,++$x) {
+ $x[$x] -= 1e5
+ if ($car = (($x[$x] += $y[$y] + $car) > 1e5));
+ }
+ }
+ }
+ pop(@x); unshift(@q, $q);
+ }
+ if (wantarray) {
+ @d = ();
+ if ($dd != 1) {
+ $car = 0;
+ for $x (reverse @x) {
+ $prd = $car * 1e5 + $x;
+ $car = $prd - ($tmp = int($prd / $dd)) * $dd;
+ unshift(@d, $tmp);
+ }
+ }
+ else {
+ @d = @x;
+ }
+ (&external($sr, @q), &external($srem, @d, 0));
+ } else {
+ &external($sr, @q);
+ }
+}
+1;
diff --git a/gnu/usr.bin/perl/lib/bigrat.pl b/gnu/usr.bin/perl/lib/bigrat.pl
new file mode 100644
index 0000000..fb10cf3
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/bigrat.pl
@@ -0,0 +1,148 @@
+package bigrat;
+require "bigint.pl";
+
+# Arbitrary size rational math package
+#
+# by Mark Biggar
+#
+# Input values to these routines consist of strings of the form
+# m|^\s*[+-]?[\d\s]+(/[\d\s]+)?$|.
+# Examples:
+# "+0/1" canonical zero value
+# "3" canonical value "+3/1"
+# " -123/123 123" canonical value "-1/1001"
+# "123 456/7890" canonical value "+20576/1315"
+# Output values always include a sign and no leading zeros or
+# white space.
+# This package makes use of the bigint package.
+# The string 'NaN' is used to represent the result when input arguments
+# that are not numbers, as well as the result of dividing by zero and
+# the sqrt of a negative number.
+# Extreamly naive algorthims are used.
+#
+# Routines provided are:
+#
+# rneg(RAT) return RAT negation
+# rabs(RAT) return RAT absolute value
+# rcmp(RAT,RAT) return CODE compare numbers (undef,<0,=0,>0)
+# radd(RAT,RAT) return RAT addition
+# rsub(RAT,RAT) return RAT subtraction
+# rmul(RAT,RAT) return RAT multiplication
+# rdiv(RAT,RAT) return RAT division
+# rmod(RAT) return (RAT,RAT) integer and fractional parts
+# rnorm(RAT) return RAT normalization
+# rsqrt(RAT, cycles) return RAT square root
+
+# Convert a number to the canonical string form m|^[+-]\d+/\d+|.
+sub main'rnorm { #(string) return rat_num
+ local($_) = @_;
+ s/\s+//g;
+ if (m#^([+-]?\d+)(/(\d*[1-9]0*))?$#) {
+ &norm($1, $3 ? $3 : '+1');
+ } else {
+ 'NaN';
+ }
+}
+
+# Normalize by reducing to lowest terms
+sub norm { #(bint, bint) return rat_num
+ local($num,$dom) = @_;
+ if ($num eq 'NaN') {
+ 'NaN';
+ } elsif ($dom eq 'NaN') {
+ 'NaN';
+ } elsif ($dom =~ /^[+-]?0+$/) {
+ 'NaN';
+ } else {
+ local($gcd) = &'bgcd($num,$dom);
+ if ($gcd ne '+1') {
+ $num = &'bdiv($num,$gcd);
+ $dom = &'bdiv($dom,$gcd);
+ } else {
+ $num = &'bnorm($num);
+ $dom = &'bnorm($dom);
+ }
+ substr($dom,0,1) = '';
+ "$num/$dom";
+ }
+}
+
+# negation
+sub main'rneg { #(rat_num) return rat_num
+ local($_) = &'rnorm($_[0]);
+ tr/-+/+-/ if ($_ ne '+0/1');
+ $_;
+}
+
+# absolute value
+sub main'rabs { #(rat_num) return $rat_num
+ local($_) = &'rnorm($_[0]);
+ substr($_,0,1) = '+' unless $_ eq 'NaN';
+ $_;
+}
+
+# multipication
+sub main'rmul { #(rat_num, rat_num) return rat_num
+ local($xn,$xd) = split('/',&'rnorm($_[0]));
+ local($yn,$yd) = split('/',&'rnorm($_[1]));
+ &norm(&'bmul($xn,$yn),&'bmul($xd,$yd));
+}
+
+# division
+sub main'rdiv { #(rat_num, rat_num) return rat_num
+ local($xn,$xd) = split('/',&'rnorm($_[0]));
+ local($yn,$yd) = split('/',&'rnorm($_[1]));
+ &norm(&'bmul($xn,$yd),&'bmul($xd,$yn));
+}
+
+# addition
+sub main'radd { #(rat_num, rat_num) return rat_num
+ local($xn,$xd) = split('/',&'rnorm($_[0]));
+ local($yn,$yd) = split('/',&'rnorm($_[1]));
+ &norm(&'badd(&'bmul($xn,$yd),&'bmul($yn,$xd)),&'bmul($xd,$yd));
+}
+
+# subtraction
+sub main'rsub { #(rat_num, rat_num) return rat_num
+ local($xn,$xd) = split('/',&'rnorm($_[0]));
+ local($yn,$yd) = split('/',&'rnorm($_[1]));
+ &norm(&'bsub(&'bmul($xn,$yd),&'bmul($yn,$xd)),&'bmul($xd,$yd));
+}
+
+# comparison
+sub main'rcmp { #(rat_num, rat_num) return cond_code
+ local($xn,$xd) = split('/',&'rnorm($_[0]));
+ local($yn,$yd) = split('/',&'rnorm($_[1]));
+ &bigint'cmp(&'bmul($xn,$yd),&'bmul($yn,$xd));
+}
+
+# int and frac parts
+sub main'rmod { #(rat_num) return (rat_num,rat_num)
+ local($xn,$xd) = split('/',&'rnorm($_[0]));
+ local($i,$f) = &'bdiv($xn,$xd);
+ if (wantarray) {
+ ("$i/1", "$f/$xd");
+ } else {
+ "$i/1";
+ }
+}
+
+# square root by Newtons method.
+# cycles specifies the number of iterations default: 5
+sub main'rsqrt { #(fnum_str[, cycles]) return fnum_str
+ local($x, $scale) = (&'rnorm($_[0]), $_[1]);
+ if ($x eq 'NaN') {
+ 'NaN';
+ } elsif ($x =~ /^-/) {
+ 'NaN';
+ } else {
+ local($gscale, $guess) = (0, '+1/1');
+ $scale = 5 if (!$scale);
+ while ($gscale++ < $scale) {
+ $guess = &'rmul(&'radd($guess,&'rdiv($x,$guess)),"+1/2");
+ }
+ "$guess"; # quotes necessary due to perl bug
+ }
+}
+
+1;
diff --git a/gnu/usr.bin/perl/lib/cacheout.pl b/gnu/usr.bin/perl/lib/cacheout.pl
new file mode 100644
index 0000000..513c25b
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/cacheout.pl
@@ -0,0 +1,40 @@
+# Open in their package.
+
+sub cacheout'open {
+ open($_[0], $_[1]);
+}
+
+# But only this sub name is visible to them.
+
+sub cacheout {
+ package cacheout;
+
+ ($file) = @_;
+ if (!$isopen{$file}) {
+ if (++$numopen > $maxopen) {
+ local(@lru) = sort {$isopen{$a} <=> $isopen{$b};} keys(%isopen);
+ splice(@lru, $maxopen / 3);
+ $numopen -= @lru;
+ for (@lru) { close $_; delete $isopen{$_}; }
+ }
+ &open($file, ($saw{$file}++ ? '>>' : '>') . $file)
+ || die "Can't create $file: $!\n";
+ }
+ $isopen{$file} = ++$seq;
+}
+
+package cacheout;
+
+$seq = 0;
+$numopen = 0;
+
+if (open(PARAM,'/usr/include/sys/param.h')) {
+ local($.);
+ while (<PARAM>) {
+ $maxopen = $1 - 4 if /^\s*#\s*define\s+NOFILE\s+(\d+)/;
+ }
+ close PARAM;
+}
+$maxopen = 16 unless $maxopen;
+
+1;
diff --git a/gnu/usr.bin/perl/lib/chat2.pl b/gnu/usr.bin/perl/lib/chat2.pl
new file mode 100644
index 0000000..662872c
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/chat2.pl
@@ -0,0 +1,339 @@
+## chat.pl: chat with a server
+## V2.01.alpha.7 91/06/16
+## Randal L. Schwartz
+
+package chat;
+
+$sockaddr = 'S n a4 x8';
+chop($thishost = `hostname`); $thisaddr = (gethostbyname($thishost))[4];
+$thisproc = pack($sockaddr, 2, 0, $thisaddr);
+
+# *S = symbol for current I/O, gets assigned *chatsymbol....
+$next = "chatsymbol000000"; # next one
+$nextpat = "^chatsymbol"; # patterns that match next++, ++, ++, ++
+
+
+## $handle = &chat'open_port("server.address",$port_number);
+## opens a named or numbered TCP server
+
+sub open_port { ## public
+ local($server, $port) = @_;
+
+ local($serveraddr,$serverproc);
+
+ *S = ++$next;
+ if ($server =~ /^(\d+)+\.(\d+)\.(\d+)\.(\d+)$/) {
+ $serveraddr = pack('C4', $1, $2, $3, $4);
+ } else {
+ local(@x) = gethostbyname($server);
+ return undef unless @x;
+ $serveraddr = $x[4];
+ }
+ $serverproc = pack($sockaddr, 2, $port, $serveraddr);
+ unless (socket(S, 2, 1, 6)) {
+ # XXX hardwired $AF_SOCKET, $SOCK_STREAM, 'tcp'
+ # but who the heck would change these anyway? (:-)
+ ($!) = ($!, close(S)); # close S while saving $!
+ return undef;
+ }
+ unless (bind(S, $thisproc)) {
+ ($!) = ($!, close(S)); # close S while saving $!
+ return undef;
+ }
+ unless (connect(S, $serverproc)) {
+ ($!) = ($!, close(S)); # close S while saving $!
+ return undef;
+ }
+ select((select(S), $| = 1)[0]);
+ $next; # return symbol for switcharound
+}
+
+## ($host, $port, $handle) = &chat'open_listen([$port_number]);
+## opens a TCP port on the current machine, ready to be listened to
+## if $port_number is absent or zero, pick a default port number
+## process must be uid 0 to listen to a low port number
+
+sub open_listen { ## public
+
+ *S = ++$next;
+ local($thisport) = shift || 0;
+ local($thisproc_local) = pack($sockaddr, 2, $thisport, $thisaddr);
+ local(*NS) = "__" . time;
+ unless (socket(NS, 2, 1, 6)) {
+ # XXX hardwired $AF_SOCKET, $SOCK_STREAM, 'tcp'
+ # but who the heck would change these anyway? (:-)
+ ($!) = ($!, close(NS));
+ return undef;
+ }
+ unless (bind(NS, $thisproc_local)) {
+ ($!) = ($!, close(NS));
+ return undef;
+ }
+ unless (listen(NS, 1)) {
+ ($!) = ($!, close(NS));
+ return undef;
+ }
+ select((select(NS), $| = 1)[0]);
+ local($family, $port, @myaddr) =
+ unpack("S n C C C C x8", getsockname(NS));
+ $S{"needs_accept"} = *NS; # so expect will open it
+ (@myaddr, $port, $next); # returning this
+}
+
+## $handle = &chat'open_proc("command","arg1","arg2",...);
+## opens a /bin/sh on a pseudo-tty
+
+sub open_proc { ## public
+ local(@cmd) = @_;
+
+ *S = ++$next;
+ local(*TTY) = "__TTY" . time;
+ local($pty,$tty) = &_getpty(S,TTY);
+ die "Cannot find a new pty" unless defined $pty;
+ local($pid) = fork;
+ die "Cannot fork: $!" unless defined $pid;
+ unless ($pid) {
+ close STDIN; close STDOUT; close STDERR;
+ setpgrp(0,$$);
+ if (open(DEVTTY, "/dev/tty")) {
+ ioctl(DEVTTY,0x20007471,0); # XXX s/b &TIOCNOTTY
+ close DEVTTY;
+ }
+ open(STDIN,"<&TTY");
+ open(STDOUT,">&TTY");
+ open(STDERR,">&STDOUT");
+ die "Oops" unless fileno(STDERR) == 2; # sanity
+ close(S);
+ exec @cmd;
+ die "Cannot exec @cmd: $!";
+ }
+ close(TTY);
+ $PID{$next} = $pid;
+ $next; # return symbol for switcharound
+}
+
+# $S is the read-ahead buffer
+
+## $return = &chat'expect([$handle,] $timeout_time,
+## $pat1, $body1, $pat2, $body2, ... )
+## $handle is from previous &chat'open_*().
+## $timeout_time is the time (either relative to the current time, or
+## absolute, ala time(2)) at which a timeout event occurs.
+## $pat1, $pat2, and so on are regexs which are matched against the input
+## stream. If a match is found, the entire matched string is consumed,
+## and the corresponding body eval string is evaled.
+##
+## Each pat is a regular-expression (probably enclosed in single-quotes
+## in the invocation). ^ and $ will work, respecting the current value of $*.
+## If pat is 'TIMEOUT', the body is executed if the timeout is exceeded.
+## If pat is 'EOF', the body is executed if the process exits before
+## the other patterns are seen.
+##
+## Pats are scanned in the order given, so later pats can contain
+## general defaults that won't be examined unless the earlier pats
+## have failed.
+##
+## The result of eval'ing body is returned as the result of
+## the invocation. Recursive invocations are not thought
+## through, and may work only accidentally. :-)
+##
+## undef is returned if either a timeout or an eof occurs and no
+## corresponding body has been defined.
+## I/O errors of any sort are treated as eof.
+
+$nextsubname = "expectloop000000"; # used for subroutines
+
+sub expect { ## public
+ if ($_[0] =~ /$nextpat/) {
+ *S = shift;
+ }
+ local($endtime) = shift;
+
+ local($timeout,$eof) = (1,1);
+ local($caller) = caller;
+ local($rmask, $nfound, $timeleft, $thisbuf);
+ local($cases, $pattern, $action, $subname);
+ $endtime += time if $endtime < 600_000_000;
+
+ if (defined $S{"needs_accept"}) { # is it a listen socket?
+ local(*NS) = $S{"needs_accept"};
+ delete $S{"needs_accept"};
+ $S{"needs_close"} = *NS;
+ unless(accept(S,NS)) {
+ ($!) = ($!, close(S), close(NS));
+ return undef;
+ }
+ select((select(S), $| = 1)[0]);
+ }
+
+ # now see whether we need to create a new sub:
+
+ unless ($subname = $expect_subname{$caller,@_}) {
+ # nope. make a new one:
+ $expect_subname{$caller,@_} = $subname = $nextsubname++;
+
+ $cases .= <<"EDQ"; # header is funny to make everything elsif's
+sub $subname {
+ LOOP: {
+ if (0) { ; }
+EDQ
+ while (@_) {
+ ($pattern,$action) = splice(@_,0,2);
+ if ($pattern =~ /^eof$/i) {
+ $cases .= <<"EDQ";
+ elsif (\$eof) {
+ package $caller;
+ $action;
+ }
+EDQ
+ $eof = 0;
+ } elsif ($pattern =~ /^timeout$/i) {
+ $cases .= <<"EDQ";
+ elsif (\$timeout) {
+ package $caller;
+ $action;
+ }
+EDQ
+ $timeout = 0;
+ } else {
+ $pattern =~ s#/#\\/#g;
+ $cases .= <<"EDQ";
+ elsif (\$S =~ /$pattern/) {
+ \$S = \$';
+ package $caller;
+ $action;
+ }
+EDQ
+ }
+ }
+ $cases .= <<"EDQ" if $eof;
+ elsif (\$eof) {
+ undef;
+ }
+EDQ
+ $cases .= <<"EDQ" if $timeout;
+ elsif (\$timeout) {
+ undef;
+ }
+EDQ
+ $cases .= <<'ESQ';
+ else {
+ $rmask = "";
+ vec($rmask,fileno(S),1) = 1;
+ ($nfound, $rmask) =
+ select($rmask, undef, undef, $endtime - time);
+ if ($nfound) {
+ $nread = sysread(S, $thisbuf, 1024);
+ if ($nread > 0) {
+ $S .= $thisbuf;
+ } else {
+ $eof++, redo LOOP; # any error is also eof
+ }
+ } else {
+ $timeout++, redo LOOP; # timeout
+ }
+ redo LOOP;
+ }
+ }
+}
+ESQ
+ eval $cases; die "$cases:\n$@" if $@;
+ }
+ $eof = $timeout = 0;
+ do $subname();
+}
+
+## &chat'print([$handle,] @data)
+## $handle is from previous &chat'open().
+## like print $handle @data
+
+sub print { ## public
+ if ($_[0] =~ /$nextpat/) {
+ *S = shift;
+ }
+ print S @_;
+}
+
+## &chat'close([$handle,])
+## $handle is from previous &chat'open().
+## like close $handle
+
+sub close { ## public
+ local($pid);
+ if ($_[0] =~ /$nextpat/) {
+ $pid = $PID{$_[0]};
+ *S = shift;
+ } else {
+ $pid = $PID{$next};
+ }
+ close(S);
+ waitpid($pid,0);
+ if (defined $S{"needs_close"}) { # is it a listen socket?
+ local(*NS) = $S{"needs_close"};
+ delete $S{"needs_close"};
+ close(NS);
+ }
+}
+
+## @ready_handles = &chat'select($timeout, @handles)
+## select()'s the handles with a timeout value of $timeout seconds.
+## Returns an array of handles that are ready for I/O.
+## Both user handles and chat handles are supported (but beware of
+## stdio's buffering for user handles).
+
+sub select { ## public
+ local($timeout) = shift;
+ local(@handles) = @_;
+ local(%handlename) = ();
+ local(%ready) = ();
+ local($caller) = caller;
+ local($rmask) = "";
+ for (@handles) {
+ if (/$nextpat/o) { # one of ours... see if ready
+ local(*SYM) = $_;
+ if (length($SYM)) {
+ $timeout = 0; # we have a winner
+ $ready{$_}++;
+ }
+ $handlename{fileno($_)} = $_;
+ } else {
+ $handlename{fileno(/'/ ? $_ : "$caller\'$_")} = $_;
+ }
+ }
+ for (sort keys %handlename) {
+ vec($rmask, $_, 1) = 1;
+ }
+ select($rmask, undef, undef, $timeout);
+ for (sort keys %handlename) {
+ $ready{$handlename{$_}}++ if vec($rmask,$_,1);
+ }
+ sort keys %ready;
+}
+
+# ($pty,$tty) = $chat'_getpty(PTY,TTY):
+# internal procedure to get the next available pty.
+# opens pty on handle PTY, and matching tty on handle TTY.
+# returns undef if can't find a pty.
+
+sub _getpty { ## private
+ local($_PTY,$_TTY) = @_;
+ $_PTY =~ s/^([^']+)$/(caller)[$[]."'".$1/e;
+ $_TTY =~ s/^([^']+)$/(caller)[$[]."'".$1/e;
+ local($pty,$tty);
+ for $bank (112..127) {
+ next unless -e sprintf("/dev/pty%c0", $bank);
+ for $unit (48..57) {
+ $pty = sprintf("/dev/pty%c%c", $bank, $unit);
+ open($_PTY,"+>$pty") || next;
+ select((select($_PTY), $| = 1)[0]);
+ ($tty = $pty) =~ s/pty/tty/;
+ open($_TTY,"+>$tty") || next;
+ select((select($_TTY), $| = 1)[0]);
+ system "stty nl>$tty";
+ return ($pty,$tty);
+ }
+ }
+ undef;
+}
+
+1;
diff --git a/gnu/usr.bin/perl/lib/complete.pl b/gnu/usr.bin/perl/lib/complete.pl
new file mode 100644
index 0000000..dabf8f6
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/complete.pl
@@ -0,0 +1,110 @@
+;#
+;# @(#)complete.pl,v1.1 (me@anywhere.EBay.Sun.COM) 09/23/91
+;#
+;# Author: Wayne Thompson
+;#
+;# Description:
+;# This routine provides word completion.
+;# (TAB) attempts word completion.
+;# (^D) prints completion list.
+;# (These may be changed by setting $Complete'complete, etc.)
+;#
+;# Diagnostics:
+;# Bell when word completion fails.
+;#
+;# Dependencies:
+;# The tty driver is put into raw mode.
+;#
+;# Bugs:
+;#
+;# Usage:
+;# $input = &Complete('prompt_string', *completion_list);
+;# or
+;# $input = &Complete('prompt_string', @completion_list);
+;#
+
+CONFIG: {
+ package Complete;
+
+ $complete = "\004";
+ $kill = "\025";
+ $erase1 = "\177";
+ $erase2 = "\010";
+}
+
+sub Complete {
+ package Complete;
+
+ local($[) = 0;
+ if ($_[1] =~ /^StB\0/) {
+ ($prompt, *_) = @_;
+ }
+ else {
+ $prompt = shift(@_);
+ }
+ @cmp_lst = sort(@_);
+
+ system('stty raw -echo');
+ LOOP: {
+ print($prompt, $return);
+ while (($_ = getc(STDIN)) ne "\r") {
+ CASE: {
+ # (TAB) attempt completion
+ $_ eq "\t" && do {
+ @match = grep(/^$return/, @cmp_lst);
+ $l = length($test = shift(@match));
+ unless ($#match < 0) {
+ foreach $cmp (@match) {
+ until (substr($cmp, 0, $l) eq substr($test, 0, $l)) {
+ $l--;
+ }
+ }
+ print("\a");
+ }
+ print($test = substr($test, $r, $l - $r));
+ $r = length($return .= $test);
+ last CASE;
+ };
+
+ # (^D) completion list
+ $_ eq $complete && do {
+ print(join("\r\n", '', grep(/^$return/, @cmp_lst)), "\r\n");
+ redo LOOP;
+ };
+
+ # (^U) kill
+ $_ eq $kill && do {
+ if ($r) {
+ undef($r, $return);
+ print("\r\n");
+ redo LOOP;
+ }
+ last CASE;
+ };
+
+ # (DEL) || (BS) erase
+ ($_ eq $erase1 || $_ eq $erase2) && do {
+ if($r) {
+ print("\b \b");
+ chop($return);
+ $r--;
+ }
+ last CASE;
+ };
+
+ # printable char
+ ord >= 32 && do {
+ $return .= $_;
+ $r++;
+ print;
+ last CASE;
+ };
+ }
+ }
+ }
+ system('stty -raw echo');
+ print("\n");
+ $return;
+}
+
+1;
diff --git a/gnu/usr.bin/perl/lib/ctime.pl b/gnu/usr.bin/perl/lib/ctime.pl
new file mode 100644
index 0000000..4c59754
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/ctime.pl
@@ -0,0 +1,51 @@
+;# ctime.pl is a simple Perl emulation for the well known ctime(3C) function.
+;#
+;# Waldemar Kebsch, Federal Republic of Germany, November 1988
+;# kebsch.pad@nixpbe.UUCP
+;# Modified March 1990, Feb 1991 to properly handle timezones
+;# $RCSfile: ctime.pl,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:52 $
+;# Marion Hakanson (hakanson@cse.ogi.edu)
+;# Oregon Graduate Institute of Science and Technology
+;#
+;# usage:
+;#
+;# #include <ctime.pl> # see the -P and -I option in perl.man
+;# $Date = &ctime(time);
+
+CONFIG: {
+ package ctime;
+
+ @DoW = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
+ @MoY = ('Jan','Feb','Mar','Apr','May','Jun',
+ 'Jul','Aug','Sep','Oct','Nov','Dec');
+}
+
+sub ctime {
+ package ctime;
+
+ local($time) = @_;
+ local($[) = 0;
+ local($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst);
+
+ # Determine what time zone is in effect.
+ # Use GMT if TZ is defined as null, local time if TZ undefined.
+ # There's no portable way to find the system default timezone.
+
+ $TZ = defined($ENV{'TZ'}) ? ( $ENV{'TZ'} ? $ENV{'TZ'} : 'GMT' ) : '';
+ ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
+ ($TZ eq 'GMT') ? gmtime($time) : localtime($time);
+
+ # Hack to deal with 'PST8PDT' format of TZ
+ # Note that this can't deal with all the esoteric forms, but it
+ # does recognize the most common: [:]STDoff[DST[off][,rule]]
+
+ if($TZ=~/^([^:\d+\-,]{3,})([+-]?\d{1,2}(:\d{1,2}){0,2})([^\d+\-,]{3,})?/){
+ $TZ = $isdst ? $4 : $1;
+ }
+ $TZ .= ' ' unless $TZ eq '';
+
+ $year += ($year < 70) ? 2000 : 1900;
+ sprintf("%s %s %2d %2d:%02d:%02d %s%4d\n",
+ $DoW[$wday], $MoY[$mon], $mday, $hour, $min, $sec, $TZ, $year);
+}
+1;
diff --git a/gnu/usr.bin/perl/lib/dumpvar.pl b/gnu/usr.bin/perl/lib/dumpvar.pl
new file mode 100644
index 0000000..5427494
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/dumpvar.pl
@@ -0,0 +1,37 @@
+package dumpvar;
+
+# translate control chars to ^X - Randal Schwartz
+sub unctrl {
+ local($_) = @_;
+ s/([\001-\037\177])/'^'.pack('c',ord($1)^64)/eg;
+ $_;
+}
+sub main'dumpvar {
+ ($package,@vars) = @_;
+ local(*stab) = eval("*_$package");
+ while (($key,$val) = each(%stab)) {
+ {
+ next if @vars && !grep($key eq $_,@vars);
+ local(*entry) = $val;
+ if (defined $entry) {
+ print "\$$key = '",&unctrl($entry),"'\n";
+ }
+ if (defined @entry) {
+ print "\@$key = (\n";
+ foreach $num ($[ .. $#entry) {
+ print " $num\t'",&unctrl($entry[$num]),"'\n";
+ }
+ print ")\n";
+ }
+ if ($key ne "_$package" && $key ne "_DB" && defined %entry) {
+ print "\%$key = (\n";
+ foreach $key (sort keys(%entry)) {
+ print " $key\t'",&unctrl($entry{$key}),"'\n";
+ }
+ print ")\n";
+ }
+ }
+ }
+}
+
+1;
diff --git a/gnu/usr.bin/perl/lib/exceptions.pl b/gnu/usr.bin/perl/lib/exceptions.pl
new file mode 100644
index 0000000..02c4498
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/exceptions.pl
@@ -0,0 +1,54 @@
+# exceptions.pl
+# tchrist@convex.com
+#
+# Here's a little code I use for exception handling. It's really just
+# glorfied eval/die. The way to use use it is when you might otherwise
+# exit, use &throw to raise an exception. The first enclosing &catch
+# handler looks at the exception and decides whether it can catch this kind
+# (catch takes a list of regexps to catch), and if so, it returns the one it
+# caught. If it *can't* catch it, then it will reraise the exception
+# for someone else to possibly see, or to die otherwise.
+#
+# I use oddly named variables in order to make darn sure I don't conflict
+# with my caller. I also hide in my own package, and eval the code in his.
+#
+# The EXCEPTION: prefix is so you can tell whether it's a user-raised
+# exception or a perl-raised one (eval error).
+#
+# --tom
+#
+# examples:
+# if (&catch('/$user_input/', 'regexp', 'syntax error') {
+# warn "oops try again";
+# redo;
+# }
+#
+# if ($error = &catch('&subroutine()')) { # catches anything
+#
+# &throw('bad input') if /^$/;
+
+sub catch {
+ package exception;
+ local($__code__, @__exceptions__) = @_;
+ local($__package__) = caller;
+ local($__exception__);
+
+ eval "package $__package__; $__code__";
+ if ($__exception__ = &'thrown) {
+ for (@__exceptions__) {
+ return $__exception__ if /$__exception__/;
+ }
+ &'throw($__exception__);
+ }
+}
+
+sub throw {
+ local($exception) = @_;
+ die "EXCEPTION: $exception\n";
+}
+
+sub thrown {
+ $@ =~ /^(EXCEPTION: )+(.+)/ && $2;
+}
+
+1;
diff --git a/gnu/usr.bin/perl/lib/fastcwd.pl b/gnu/usr.bin/perl/lib/fastcwd.pl
new file mode 100644
index 0000000..6b452e8
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/fastcwd.pl
@@ -0,0 +1,35 @@
+# By John Bazik
+#
+# Usage: $cwd = &fastcwd;
+#
+# This is a faster version of getcwd. It's also more dangerous because
+# you might chdir out of a directory that you can't chdir back into.
+
+sub fastcwd {
+ local($odev, $oino, $cdev, $cino, $tdev, $tino);
+ local(@path, $path);
+ local(*DIR);
+
+ ($cdev, $cino) = stat('.');
+ for (;;) {
+ ($odev, $oino) = ($cdev, $cino);
+ chdir('..');
+ ($cdev, $cino) = stat('.');
+ last if $odev == $cdev && $oino == $cino;
+ opendir(DIR, '.');
+ for (;;) {
+ $_ = readdir(DIR);
+ next if $_ eq '.';
+ next if $_ eq '..';
+
+ last unless $_;
+ ($tdev, $tino) = lstat($_);
+ last unless $tdev != $odev || $tino != $oino;
+ }
+ closedir(DIR);
+ unshift(@path, $_);
+ }
+ chdir($path = '/' . join('/', @path));
+ $path;
+}
+1;
diff --git a/gnu/usr.bin/perl/lib/find.pl b/gnu/usr.bin/perl/lib/find.pl
new file mode 100644
index 0000000..8dab054
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/find.pl
@@ -0,0 +1,106 @@
+# Usage:
+# require "find.pl";
+#
+# &find('/foo','/bar');
+#
+# sub wanted { ... }
+# where wanted does whatever you want. $dir contains the
+# current directory name, and $_ the current filename within
+# that directory. $name contains "$dir/$_". You are cd'ed
+# to $dir when the function is called. The function may
+# set $prune to prune the tree.
+#
+# This library is primarily for find2perl, which, when fed
+#
+# find2perl / -name .nfs\* -mtime +7 -exec rm -f {} \; -o -fstype nfs -prune
+#
+# spits out something like this
+#
+# sub wanted {
+# /^\.nfs.*$/ &&
+# (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&
+# int(-M _) > 7 &&
+# unlink($_)
+# ||
+# ($nlink || (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_))) &&
+# $dev < 0 &&
+# ($prune = 1);
+# }
+
+sub find {
+ chop($cwd = `pwd`);
+ foreach $topdir (@_) {
+ (($topdev,$topino,$topmode,$topnlink) = stat($topdir))
+ || (warn("Can't stat $topdir: $!\n"), next);
+ if (-d _) {
+ if (chdir($topdir)) {
+ ($dir,$_) = ($topdir,'.');
+ $name = $topdir;
+ &wanted;
+ $topdir =~ s,/$,, ;
+ &finddir($topdir,$topnlink);
+ }
+ else {
+ warn "Can't cd to $topdir: $!\n";
+ }
+ }
+ else {
+ unless (($dir,$_) = $topdir =~ m#^(.*/)(.*)$#) {
+ ($dir,$_) = ('.', $topdir);
+ }
+ $name = $topdir;
+ chdir $dir && &wanted;
+ }
+ chdir $cwd;
+ }
+}
+
+sub finddir {
+ local($dir,$nlink) = @_;
+ local($dev,$ino,$mode,$subcount);
+ local($name);
+
+ # Get the list of files in the current directory.
+
+ opendir(DIR,'.') || (warn "Can't open $dir: $!\n", return);
+ local(@filenames) = readdir(DIR);
+ closedir(DIR);
+
+ if ($nlink == 2) { # This dir has no subdirectories.
+ for (@filenames) {
+ next if $_ eq '.';
+ next if $_ eq '..';
+ $name = "$dir/$_";
+ $nlink = 0;
+ &wanted;
+ }
+ }
+ else { # This dir has subdirectories.
+ $subcount = $nlink - 2;
+ for (@filenames) {
+ next if $_ eq '.';
+ next if $_ eq '..';
+ $nlink = $prune = 0;
+ $name = "$dir/$_";
+ &wanted;
+ if ($subcount > 0) { # Seen all the subdirs?
+
+ # Get link count and check for directoriness.
+
+ ($dev,$ino,$mode,$nlink) = lstat($_) unless $nlink;
+
+ if (-d _) {
+
+ # It really is a directory, so do it recursively.
+
+ if (!$prune && chdir $_) {
+ &finddir($name,$nlink);
+ chdir '..';
+ }
+ --$subcount;
+ }
+ }
+ }
+ }
+}
+1;
diff --git a/gnu/usr.bin/perl/lib/finddepth.pl b/gnu/usr.bin/perl/lib/finddepth.pl
new file mode 100644
index 0000000..15e4daf
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/finddepth.pl
@@ -0,0 +1,105 @@
+# Usage:
+# require "finddepth.pl";
+#
+# &finddepth('/foo','/bar');
+#
+# sub wanted { ... }
+# where wanted does whatever you want. $dir contains the
+# current directory name, and $_ the current filename within
+# that directory. $name contains "$dir/$_". You are cd'ed
+# to $dir when the function is called. The function may
+# set $prune to prune the tree.
+#
+# This library is primarily for find2perl, which, when fed
+#
+# find2perl / -name .nfs\* -mtime +7 -exec rm -f {} \; -o -fstype nfs -prune
+#
+# spits out something like this
+#
+# sub wanted {
+# /^\.nfs.*$/ &&
+# (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&
+# int(-M _) > 7 &&
+# unlink($_)
+# ||
+# ($nlink || (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_))) &&
+# $dev < 0 &&
+# ($prune = 1);
+# }
+
+sub finddepth {
+ chop($cwd = `pwd`);
+ foreach $topdir (@_) {
+ (($topdev,$topino,$topmode,$topnlink) = stat($topdir))
+ || (warn("Can't stat $topdir: $!\n"), next);
+ if (-d _) {
+ if (chdir($topdir)) {
+ $topdir =~ s,/$,, ;
+ &finddepthdir($topdir,$topnlink);
+ ($dir,$_) = ($topdir,'.');
+ $name = $topdir;
+ &wanted;
+ }
+ else {
+ warn "Can't cd to $topdir: $!\n";
+ }
+ }
+ else {
+ unless (($dir,$_) = $topdir =~ m#^(.*/)(.*)$#) {
+ ($dir,$_) = ('.', $topdir);
+ }
+ chdir $dir && &wanted;
+ }
+ chdir $cwd;
+ }
+}
+
+sub finddepthdir {
+ local($dir,$nlink) = @_;
+ local($dev,$ino,$mode,$subcount);
+ local($name);
+
+ # Get the list of files in the current directory.
+
+ opendir(DIR,'.') || warn "Can't open $dir: $!\n";
+ local(@filenames) = readdir(DIR);
+ closedir(DIR);
+
+ if ($nlink == 2) { # This dir has no subdirectories.
+ for (@filenames) {
+ next if $_ eq '.';
+ next if $_ eq '..';
+ $name = "$dir/$_";
+ $nlink = 0;
+ &wanted;
+ }
+ }
+ else { # This dir has subdirectories.
+ $subcount = $nlink - 2;
+ for (@filenames) {
+ next if $_ eq '.';
+ next if $_ eq '..';
+ $nlink = $prune = 0;
+ $name = "$dir/$_";
+ if ($subcount > 0) { # Seen all the subdirs?
+
+ # Get link count and check for directoriness.
+
+ ($dev,$ino,$mode,$nlink) = lstat($_) unless $nlink;
+
+ if (-d _) {
+
+ # It really is a directory, so do it recursively.
+
+ if (!$prune && chdir $_) {
+ &finddepthdir($name,$nlink);
+ chdir '..';
+ }
+ --$subcount;
+ }
+ }
+ &wanted;
+ }
+ }
+}
+1;
diff --git a/gnu/usr.bin/perl/lib/flush.pl b/gnu/usr.bin/perl/lib/flush.pl
new file mode 100644
index 0000000..55002b9
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/flush.pl
@@ -0,0 +1,23 @@
+;# Usage: &flush(FILEHANDLE)
+;# flushes the named filehandle
+
+;# Usage: &printflush(FILEHANDLE, "prompt: ")
+;# prints arguments and flushes filehandle
+
+sub flush {
+ local($old) = select(shift);
+ $| = 1;
+ print "";
+ $| = 0;
+ select($old);
+}
+
+sub printflush {
+ local($old) = select(shift);
+ $| = 1;
+ print @_;
+ $| = 0;
+ select($old);
+}
+
+1;
diff --git a/gnu/usr.bin/perl/lib/getcwd.pl b/gnu/usr.bin/perl/lib/getcwd.pl
new file mode 100644
index 0000000..a3214ba
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/getcwd.pl
@@ -0,0 +1,62 @@
+# By Brandon S. Allbery
+#
+# Usage: $cwd = &getcwd;
+
+sub getcwd
+{
+ local($dotdots, $cwd, @pst, @cst, $dir, @tst);
+
+ unless (@cst = stat('.'))
+ {
+ warn "stat(.): $!";
+ return '';
+ }
+ $cwd = '';
+ do
+ {
+ $dotdots .= '/' if $dotdots;
+ $dotdots .= '..';
+ @pst = @cst;
+ unless (opendir(getcwd'PARENT, $dotdots)) #'))
+ {
+ warn "opendir($dotdots): $!";
+ return '';
+ }
+ unless (@cst = stat($dotdots))
+ {
+ warn "stat($dotdots): $!";
+ closedir(getcwd'PARENT); #');
+ return '';
+ }
+ if ($pst[$[] == $cst[$[] && $pst[$[ + 1] == $cst[$[ + 1])
+ {
+ $dir = '';
+ }
+ else
+ {
+ do
+ {
+ unless ($dir = readdir(getcwd'PARENT)) #'))
+ {
+ warn "readdir($dotdots): $!";
+ closedir(getcwd'PARENT); #');
+ return '';
+ }
+ unless (@tst = lstat("$dotdots/$dir"))
+ {
+ warn "lstat($dotdots/$dir): $!";
+ closedir(getcwd'PARENT); #');
+ return '';
+ }
+ }
+ while ($dir eq '.' || $dir eq '..' || $tst[$[] != $pst[$[] ||
+ $tst[$[ + 1] != $pst[$[ + 1]);
+ }
+ $cwd = "$dir/$cwd";
+ closedir(getcwd'PARENT); #');
+ } while ($dir);
+ chop($cwd);
+ $cwd;
+}
+
+1;
diff --git a/gnu/usr.bin/perl/lib/getopt.pl b/gnu/usr.bin/perl/lib/getopt.pl
new file mode 100644
index 0000000..6772d54
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/getopt.pl
@@ -0,0 +1,41 @@
+;# $RCSfile: getopt.pl,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:52 $
+
+;# Process single-character switches with switch clustering. Pass one argument
+;# which is a string containing all switches that take an argument. For each
+;# switch found, sets $opt_x (where x is the switch name) to the value of the
+;# argument, or 1 if no argument. Switches which take an argument don't care
+;# whether there is a space between the switch and the argument.
+
+;# Usage:
+;# do Getopt('oDI'); # -o, -D & -I take arg. Sets opt_* as a side effect.
+
+sub Getopt {
+ local($argumentative) = @_;
+ local($_,$first,$rest);
+ local($[) = 0;
+
+ while (@ARGV && ($_ = $ARGV[0]) =~ /^-(.)(.*)/) {
+ ($first,$rest) = ($1,$2);
+ if (index($argumentative,$first) >= $[) {
+ if ($rest ne '') {
+ shift(@ARGV);
+ }
+ else {
+ shift(@ARGV);
+ $rest = shift(@ARGV);
+ }
+ eval "\$opt_$first = \$rest;";
+ }
+ else {
+ eval "\$opt_$first = 1;";
+ if ($rest ne '') {
+ $ARGV[0] = "-$rest";
+ }
+ else {
+ shift(@ARGV);
+ }
+ }
+ }
+}
+
+1;
diff --git a/gnu/usr.bin/perl/lib/getopts.pl b/gnu/usr.bin/perl/lib/getopts.pl
new file mode 100644
index 0000000..a0818d1
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/getopts.pl
@@ -0,0 +1,50 @@
+;# getopts.pl - a better getopt.pl
+
+;# Usage:
+;# do Getopts('a:bc'); # -a takes arg. -b & -c not. Sets opt_* as a
+;# # side effect.
+
+sub Getopts {
+ local($argumentative) = @_;
+ local(@args,$_,$first,$rest);
+ local($errs) = 0;
+ local($[) = 0;
+
+ @args = split( / */, $argumentative );
+ while(@ARGV && ($_ = $ARGV[0]) =~ /^-(.)(.*)/) {
+ ($first,$rest) = ($1,$2);
+ $pos = index($argumentative,$first);
+ if($pos >= $[) {
+ if($args[$pos+1] eq ':') {
+ shift(@ARGV);
+ if($rest eq '') {
+ ++$errs unless @ARGV;
+ $rest = shift(@ARGV);
+ }
+ eval "\$opt_$first = \$rest;";
+ }
+ else {
+ eval "\$opt_$first = 1";
+ if($rest eq '') {
+ shift(@ARGV);
+ }
+ else {
+ $ARGV[0] = "-$rest";
+ }
+ }
+ }
+ else {
+ print STDERR "Unknown option: $first\n";
+ ++$errs;
+ if($rest ne '') {
+ $ARGV[0] = "-$rest";
+ }
+ else {
+ shift(@ARGV);
+ }
+ }
+ }
+ $errs == 0;
+}
+
+1;
diff --git a/gnu/usr.bin/perl/lib/importenv.pl b/gnu/usr.bin/perl/lib/importenv.pl
new file mode 100644
index 0000000..c9ad330
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/importenv.pl
@@ -0,0 +1,16 @@
+;# $Header: /home/cvs/386BSD/ports/lang/perl/lib/importenv.pl,v 1.1.1.1 1993/08/23 21:29:53 nate Exp $
+
+;# This file, when interpreted, pulls the environment into normal variables.
+;# Usage:
+;# require 'importenv.pl';
+;# or
+;# #include <importenv.pl>
+
+local($tmp,$key) = '';
+
+foreach $key (keys(ENV)) {
+ $tmp .= "\$$key = \$ENV{'$key'};" if $key =~ /^[A-Za-z]\w*$/;
+}
+eval $tmp;
+
+1;
diff --git a/gnu/usr.bin/perl/lib/look.pl b/gnu/usr.bin/perl/lib/look.pl
new file mode 100644
index 0000000..4c14e64
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/look.pl
@@ -0,0 +1,44 @@
+;# Usage: &look(*FILEHANDLE,$key,$dict,$fold)
+
+;# Sets file position in FILEHANDLE to be first line greater than or equal
+;# (stringwise) to $key. Pass flags for dictionary order and case folding.
+
+sub look {
+ local(*FH,$key,$dict,$fold) = @_;
+ local($max,$min,$mid,$_);
+ local($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat(FH);
+ $blksize = 8192 unless $blksize;
+ $key =~ s/[^\w\s]//g if $dict;
+ $key =~ y/A-Z/a-z/ if $fold;
+ $max = int($size / $blksize);
+ while ($max - $min > 1) {
+ $mid = int(($max + $min) / 2);
+ seek(FH,$mid * $blksize,0);
+ $_ = <FH> if $mid; # probably a partial line
+ $_ = <FH>;
+ chop;
+ s/[^\w\s]//g if $dict;
+ y/A-Z/a-z/ if $fold;
+ if ($_ lt $key) {
+ $min = $mid;
+ }
+ else {
+ $max = $mid;
+ }
+ }
+ $min *= $blksize;
+ seek(FH,$min,0);
+ <FH> if $min;
+ while (<FH>) {
+ chop;
+ s/[^\w\s]//g if $dict;
+ y/A-Z/a-z/ if $fold;
+ last if $_ ge $key;
+ $min = tell(FH);
+ }
+ seek(FH,$min,0);
+ $min;
+}
+
+1;
diff --git a/gnu/usr.bin/perl/lib/newgetopt.pl b/gnu/usr.bin/perl/lib/newgetopt.pl
new file mode 100644
index 0000000..0e4cbfd
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/newgetopt.pl
@@ -0,0 +1,271 @@
+# newgetopt.pl -- new options parsing
+
+# SCCS Status : @(#)@ newgetopt.pl 1.13
+# Author : Johan Vromans
+# Created On : Tue Sep 11 15:00:12 1990
+# Last Modified By: Johan Vromans
+# Last Modified On: Tue Jun 2 11:24:03 1992
+# Update Count : 75
+# Status : Okay
+
+# This package implements a new getopt function. This function adheres
+# to the new syntax (long option names, no bundling).
+#
+# Arguments to the function are:
+#
+# - a list of possible options. These should designate valid perl
+# identifiers, optionally followed by an argument specifier ("="
+# for mandatory arguments or ":" for optional arguments) and an
+# argument type specifier: "n" or "i" for integer numbers, "f" for
+# real (fix) numbers or "s" for strings.
+# If an "@" sign is appended, the option is treated as an array.
+# Value(s) are not set, but pushed.
+#
+# - if the first option of the list consists of non-alphanumeric
+# characters only, it is interpreted as a generic option starter.
+# Everything starting with one of the characters from the starter
+# will be considered an option.
+# Likewise, a double occurrence (e.g. "--") signals end of
+# the options list.
+# The default value for the starter is "-", "--" or "+".
+#
+# Upon return, the option variables, prefixed with "opt_", are defined
+# and set to the respective option arguments, if any.
+# Options that do not take an argument are set to 1. Note that an
+# option with an optional argument will be defined, but set to '' if
+# no actual argument has been supplied.
+# A return status of 0 (false) indicates that the function detected
+# one or more errors.
+#
+# Special care is taken to give a correct treatment to optional arguments.
+#
+# E.g. if option "one:i" (i.e. takes an optional integer argument),
+# then the following situations are handled:
+#
+# -one -two -> $opt_one = '', -two is next option
+# -one -2 -> $opt_one = -2
+#
+# Also, assume "foo=s" and "bar:s" :
+#
+# -bar -xxx -> $opt_bar = '', '-xxx' is next option
+# -foo -bar -> $opt_foo = '-bar'
+# -foo -- -> $opt_foo = '--'
+#
+# HISTORY
+# 2-Jun-1992 Johan Vromans
+# Do not use //o to allow multiple NGetOpt calls with different delimeters.
+# Prevent typeless option from using previous $array state.
+# Prevent empty option from being eaten as a (negative) number.
+
+# 25-May-1992 Johan Vromans
+# Add array options. "foo=s@" will return an array @opt_foo that
+# contains all values that were supplied. E.g. "-foo one -foo -two" will
+# return @opt_foo = ("one", "-two");
+# Correct bug in handling options that allow for a argument when followed
+# by another option.
+
+# 4-May-1992 Johan Vromans
+# Add $ignorecase to match options in either case.
+# Allow '' option.
+
+# 19-Mar-1992 Johan Vromans
+# Allow require from packages.
+# NGetOpt is now defined in the package that requires it.
+# @ARGV and $opt_... are taken from the package that calls it.
+# Use standard (?) option prefixes: -, -- and +.
+
+# 20-Sep-1990 Johan Vromans
+# Set options w/o argument to 1.
+# Correct the dreadful semicolon/require bug.
+
+
+{ package newgetopt;
+ $debug = 0; # for debugging
+ $ignorecase = 1; # ignore case when matching options
+}
+
+sub NGetOpt {
+
+ @newgetopt'optionlist = @_;
+ *newgetopt'ARGV = *ARGV;
+
+ package newgetopt;
+
+ local ($[) = 0;
+ local ($genprefix) = "(--|-|\\+)";
+ local ($argend) = "--";
+ local ($error) = 0;
+ local ($opt, $optx, $arg, $type, $mand, %opctl);
+ local ($pkg) = (caller)[0];
+
+ print STDERR "NGetOpt 1.13 -- called from $pkg\n" if $debug;
+
+ # See if the first element of the optionlist contains option
+ # starter characters.
+ if ( $optionlist[0] =~ /^\W+$/ ) {
+ $genprefix = shift (@optionlist);
+ # Turn into regexp.
+ $genprefix =~ s/(\W)/\\\1/g;
+ $genprefix = "[" . $genprefix . "]";
+ undef $argend;
+ }
+
+ # Verify correctness of optionlist.
+ %opctl = ();
+ foreach $opt ( @optionlist ) {
+ $opt =~ tr/A-Z/a-z/ if $ignorecase;
+ if ( $opt !~ /^(\w*)([=:][infse]@?)?$/ ) {
+ print STDERR ("Error in option spec: \"", $opt, "\"\n");
+ $error++;
+ next;
+ }
+ $opctl{$1} = defined $2 ? $2 : "";
+ }
+
+ return 0 if $error;
+
+ if ( $debug ) {
+ local ($arrow, $k, $v);
+ $arrow = "=> ";
+ while ( ($k,$v) = each(%opctl) ) {
+ print STDERR ($arrow, "\$opctl{\"$k\"} = \"$v\"\n");
+ $arrow = " ";
+ }
+ }
+
+ # Process argument list
+
+ while ( $#ARGV >= 0 ) {
+
+ # >>> See also the continue block <<<
+
+ # Get next argument
+ $opt = shift (@ARGV);
+ print STDERR ("=> option \"", $opt, "\"\n") if $debug;
+ $arg = undef;
+
+ # Check for exhausted list.
+ if ( $opt =~ /^$genprefix/ ) {
+ # Double occurrence is terminator
+ return ($error == 0)
+ if ($opt eq "$+$+") || ((defined $argend) && $opt eq $argend);
+ $opt = $'; # option name (w/o prefix)
+ }
+ else {
+ # Apparently not an option - push back and exit.
+ unshift (@ARGV, $opt);
+ return ($error == 0);
+ }
+
+ # Look it up.
+ $opt =~ tr/A-Z/a-z/ if $ignorecase;
+ unless ( defined ( $type = $opctl{$opt} ) ) {
+ print STDERR ("Unknown option: ", $opt, "\n");
+ $error++;
+ next;
+ }
+
+ # Determine argument status.
+ print STDERR ("=> found \"$type\" for ", $opt, "\n") if $debug;
+
+ # If it is an option w/o argument, we're almost finished with it.
+ if ( $type eq "" ) {
+ $arg = 1; # supply explicit value
+ $array = 0;
+ next;
+ }
+
+ # Get mandatory status and type info.
+ ($mand, $type, $array) = $type =~ /^(.)(.)(@?)$/;
+
+ # Check if the argument list is exhausted.
+ if ( $#ARGV < 0 ) {
+
+ # Complain if this option needs an argument.
+ if ( $mand eq "=" ) {
+ print STDERR ("Option ", $opt, " requires an argument\n");
+ $error++;
+ }
+ if ( $mand eq ":" ) {
+ $arg = $type eq "s" ? "" : 0;
+ }
+ next;
+ }
+
+ # Get (possibly optional) argument.
+ $arg = shift (@ARGV);
+
+ # Check if it is a valid argument. A mandatory string takes
+ # anything.
+ if ( "$mand$type" ne "=s" && $arg =~ /^$genprefix/ ) {
+
+ # Check for option list terminator.
+ if ( $arg eq "$+$+" ||
+ ((defined $argend) && $arg eq $argend)) {
+ # Push back so the outer loop will terminate.
+ unshift (@ARGV, $arg);
+ # Complain if an argument is required.
+ if ($mand eq "=") {
+ print STDERR ("Option ", $opt, " requires an argument\n");
+ $error++;
+ undef $arg; # don't assign it
+ }
+ else {
+ # Supply empty value.
+ $arg = $type eq "s" ? "" : 0;
+ }
+ next;
+ }
+
+ # Maybe the optional argument is the next option?
+ if ( $mand eq ":" && ($' eq "" || $' =~ /[a-zA-Z_]/) ) {
+ # Yep. Push back.
+ unshift (@ARGV, $arg);
+ $arg = $type eq "s" ? "" : 0;
+ next;
+ }
+ }
+
+ if ( $type eq "n" || $type eq "i" ) { # numeric/integer
+ if ( $arg !~ /^-?[0-9]+$/ ) {
+ print STDERR ("Value \"", $arg, "\" invalid for option ",
+ $opt, " (number expected)\n");
+ $error++;
+ undef $arg; # don't assign it
+ }
+ next;
+ }
+
+ if ( $type eq "f" ) { # fixed real number, int is also ok
+ if ( $arg !~ /^-?[0-9.]+$/ ) {
+ print STDERR ("Value \"", $arg, "\" invalid for option ",
+ $opt, " (real number expected)\n");
+ $error++;
+ undef $arg; # don't assign it
+ }
+ next;
+ }
+
+ if ( $type eq "s" ) { # string
+ next;
+ }
+
+ }
+ continue {
+ if ( defined $arg ) {
+ if ( $array ) {
+ print STDERR ('=> push (@', $pkg, '\'opt_', $opt, ", \"$arg\")\n")
+ if $debug;
+ eval ('push(@' . $pkg . '\'opt_' . $opt . ", \$arg);");
+ }
+ else {
+ print STDERR ('=> $', $pkg, '\'opt_', $opt, " = \"$arg\"\n")
+ if $debug;
+ eval ('$' . $pkg . '\'opt_' . $opt . " = \$arg;");
+ }
+ }
+ }
+
+ return ($error == 0);
+}
+1;
diff --git a/gnu/usr.bin/perl/lib/open2.pl b/gnu/usr.bin/perl/lib/open2.pl
new file mode 100644
index 0000000..dcd68a8
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/open2.pl
@@ -0,0 +1,54 @@
+# &open2: tom christiansen, <tchrist@convex.com>
+#
+# usage: $pid = &open2('rdr', 'wtr', 'some cmd and args');
+# or $pid = &open2('rdr', 'wtr', 'some', 'cmd', 'and', 'args');
+#
+# spawn the given $cmd and connect $rdr for
+# reading and $wtr for writing. return pid
+# of child, or 0 on failure.
+#
+# WARNING: this is dangerous, as you may block forever
+# unless you are very careful.
+#
+# $wtr is left unbuffered.
+#
+# abort program if
+# rdr or wtr are null
+# pipe or fork or exec fails
+
+package open2;
+$fh = 'FHOPEN000'; # package static in case called more than once
+
+sub main'open2 {
+ local($kidpid);
+ local($dad_rdr, $dad_wtr, @cmd) = @_;
+
+ $dad_rdr ne '' || die "open2: rdr should not be null";
+ $dad_wtr ne '' || die "open2: wtr should not be null";
+
+ # force unqualified filehandles into callers' package
+ local($package) = caller;
+ $dad_rdr =~ s/^[^']+$/$package'$&/;
+ $dad_wtr =~ s/^[^']+$/$package'$&/;
+
+ local($kid_rdr) = ++$fh;
+ local($kid_wtr) = ++$fh;
+
+ pipe($dad_rdr, $kid_wtr) || die "open2: pipe 1 failed: $!";
+ pipe($kid_rdr, $dad_wtr) || die "open2: pipe 2 failed: $!";
+
+ if (($kidpid = fork) < 0) {
+ die "open2: fork failed: $!";
+ } elsif ($kidpid == 0) {
+ close $dad_rdr; close $dad_wtr;
+ open(STDIN, "<&$kid_rdr");
+ open(STDOUT, ">&$kid_wtr");
+ warn "execing @cmd\n" if $debug;
+ exec @cmd;
+ die "open2: exec of @cmd failed";
+ }
+ close $kid_rdr; close $kid_wtr;
+ select((select($dad_wtr), $| = 1)[0]); # unbuffer pipe
+ $kidpid;
+}
+1; # so require is happy
diff --git a/gnu/usr.bin/perl/lib/perldb.pl b/gnu/usr.bin/perl/lib/perldb.pl
new file mode 100644
index 0000000..1aadb93
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/perldb.pl
@@ -0,0 +1,598 @@
+package DB;
+
+# modified Perl debugger, to be run from Emacs in perldb-mode
+# Ray Lischner (uunet!mntgfx!lisch) as of 5 Nov 1990
+# Johan Vromans -- upgrade to 4.0 pl 10
+
+$header = '$RCSfile: perldb.pl,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:51 $';
+#
+# This file is automatically included if you do perl -d.
+# It's probably not useful to include this yourself.
+#
+# Perl supplies the values for @line and %sub. It effectively inserts
+# a do DB'DB(<linenum>); in front of every place that can
+# have a breakpoint. It also inserts a do 'perldb.pl' before the first line.
+#
+# $Log: perldb.pl,v $
+# Revision 1.1.1.1 1993/08/23 21:29:51 nate
+# PERL!
+#
+# Revision 4.0.1.3 92/06/08 13:43:57 lwall
+# patch20: support for MSDOS folded into perldb.pl
+# patch20: perldb couldn't debug file containing '-', such as STDIN designator
+#
+# Revision 4.0.1.2 91/11/05 17:55:58 lwall
+# patch11: perldb.pl modified to run within emacs in perldb-mode
+#
+# Revision 4.0.1.1 91/06/07 11:17:44 lwall
+# patch4: added $^P variable to control calling of perldb routines
+# patch4: debugger sometimes listed wrong number of lines for a statement
+#
+# Revision 4.0 91/03/20 01:25:50 lwall
+# 4.0 baseline.
+#
+# Revision 3.0.1.6 91/01/11 18:08:58 lwall
+# patch42: @_ couldn't be accessed from debugger
+#
+# Revision 3.0.1.5 90/11/10 01:40:26 lwall
+# patch38: the debugger wouldn't stop correctly or do action routines
+#
+# Revision 3.0.1.4 90/10/15 17:40:38 lwall
+# patch29: added caller
+# patch29: the debugger now understands packages and evals
+# patch29: scripts now run at almost full speed under the debugger
+# patch29: more variables are settable from debugger
+#
+# Revision 3.0.1.3 90/08/09 04:00:58 lwall
+# patch19: debugger now allows continuation lines
+# patch19: debugger can now dump lists of variables
+# patch19: debugger can now add aliases easily from prompt
+#
+# Revision 3.0.1.2 90/03/12 16:39:39 lwall
+# patch13: perl -d didn't format stack traces of *foo right
+# patch13: perl -d wiped out scalar return values of subroutines
+#
+# Revision 3.0.1.1 89/10/26 23:14:02 lwall
+# patch1: RCS expanded an unintended $Header in lib/perldb.pl
+#
+# Revision 3.0 89/10/18 15:19:46 lwall
+# 3.0 baseline
+#
+# Revision 2.0 88/06/05 00:09:45 root
+# Baseline version 2.0.
+#
+#
+
+if (-e "/dev/tty") {
+ $console = "/dev/tty";
+ $rcfile=".perldb";
+}
+else {
+ $console = "con";
+ $rcfile="perldb.ini";
+}
+
+open(IN, "<$console") || open(IN, "<&STDIN"); # so we don't dingle stdin
+open(OUT,">$console") || open(OUT, ">&STDOUT"); # so we don't dongle stdout
+select(OUT);
+$| = 1; # for DB'OUT
+select(STDOUT);
+$| = 1; # for real STDOUT
+$sub = '';
+
+# Is Perl being run from Emacs?
+$emacs = $main'ARGV[$[] eq '-emacs';
+shift(@main'ARGV) if $emacs;
+
+$header =~ s/.Header: ([^,]+),v(\s+\S+\s+\S+).*$/$1$2/;
+print OUT "\nLoading DB routines from $header\n";
+print OUT ("Emacs support ",
+ $emacs ? "enabled" : "available",
+ ".\n");
+print OUT "\nEnter h for help.\n\n";
+
+sub DB {
+ &save;
+ ($package, $filename, $line) = caller;
+ $usercontext = '($@, $!, $[, $,, $/, $\) = @saved;' .
+ "package $package;"; # this won't let them modify, alas
+ local($^P) = 0; # don't debug our own evals
+ local(*dbline) = "_<$filename";
+ $max = $#dbline;
+ if (($stop,$action) = split(/\0/,$dbline{$line})) {
+ if ($stop eq '1') {
+ $signal |= 1;
+ }
+ else {
+ $evalarg = "\$DB'signal |= do {$stop;}"; &eval;
+ $dbline{$line} =~ s/;9($|\0)/$1/;
+ }
+ }
+ if ($single || $trace || $signal) {
+ if ($emacs) {
+ print OUT "\032\032$filename:$line:0\n";
+ } else {
+ print OUT "$package'" unless $sub =~ /'/;
+ print OUT "$sub($filename:$line):\t",$dbline[$line];
+ for ($i = $line + 1; $i <= $max && $dbline[$i] == 0; ++$i) {
+ last if $dbline[$i] =~ /^\s*(}|#|\n)/;
+ print OUT "$sub($filename:$i):\t",$dbline[$i];
+ }
+ }
+ }
+ $evalarg = $action, &eval if $action;
+ if ($single || $signal) {
+ $evalarg = $pre, &eval if $pre;
+ print OUT $#stack . " levels deep in subroutine calls!\n"
+ if $single & 4;
+ $start = $line;
+ CMD:
+ while ((print OUT " DB<", $#hist+1, "> "), $cmd=&gets) {
+ {
+ $single = 0;
+ $signal = 0;
+ $cmd eq '' && exit 0;
+ chop($cmd);
+ $cmd =~ s/\\$// && do {
+ print OUT " cont: ";
+ $cmd .= &gets;
+ redo CMD;
+ };
+ $cmd =~ /^q$/ && exit 0;
+ $cmd =~ /^$/ && ($cmd = $laststep);
+ push(@hist,$cmd) if length($cmd) > 1;
+ ($i) = split(/\s+/,$cmd);
+ eval "\$cmd =~ $alias{$i}", print OUT $@ if $alias{$i};
+ $cmd =~ /^h$/ && do {
+ print OUT "
+T Stack trace.
+s Single step.
+n Next, steps over subroutine calls.
+r Return from current subroutine.
+c [line] Continue; optionally inserts a one-time-only breakpoint
+ at the specified line.
+<CR> Repeat last n or s.
+l min+incr List incr+1 lines starting at min.
+l min-max List lines.
+l line List line;
+l List next window.
+- List previous window.
+w line List window around line.
+l subname List subroutine.
+f filename Switch to filename.
+/pattern/ Search forwards for pattern; final / is optional.
+?pattern? Search backwards for pattern.
+L List breakpoints and actions.
+S List subroutine names.
+t Toggle trace mode.
+b [line] [condition]
+ Set breakpoint; line defaults to the current execution line;
+ condition breaks if it evaluates to true, defaults to \'1\'.
+b subname [condition]
+ Set breakpoint at first line of subroutine.
+d [line] Delete breakpoint.
+D Delete all breakpoints.
+a [line] command
+ Set an action to be done before the line is executed.
+ Sequence is: check for breakpoint, print line if necessary,
+ do action, prompt user if breakpoint or step, evaluate line.
+A Delete all actions.
+V [pkg [vars]] List some (default all) variables in package (default current).
+X [vars] Same as \"V currentpackage [vars]\".
+< command Define command before prompt.
+> command Define command after prompt.
+! number Redo command (default previous command).
+! -number Redo number\'th to last command.
+H -number Display last number commands (default all).
+q or ^D Quit.
+p expr Same as \"print DB'OUT expr\" in current package.
+= [alias value] Define a command alias, or list current aliases.
+command Execute as a perl statement in current package.
+
+";
+ next CMD; };
+ $cmd =~ /^t$/ && do {
+ $trace = !$trace;
+ print OUT "Trace = ".($trace?"on":"off")."\n";
+ next CMD; };
+ $cmd =~ /^S$/ && do {
+ foreach $subname (sort(keys %sub)) {
+ print OUT $subname,"\n";
+ }
+ next CMD; };
+ $cmd =~ s/^X\b/V $package/;
+ $cmd =~ /^V$/ && do {
+ $cmd = 'V $package'; };
+ $cmd =~ /^V\b\s*(\S+)\s*(.*)/ && do {
+ $packname = $1;
+ @vars = split(' ',$2);
+ do 'dumpvar.pl' unless defined &main'dumpvar;
+ if (defined &main'dumpvar) {
+ &main'dumpvar($packname,@vars);
+ }
+ else {
+ print DB'OUT "dumpvar.pl not available.\n";
+ }
+ next CMD; };
+ $cmd =~ /^f\b\s*(.*)/ && do {
+ $file = $1;
+ if (!$file) {
+ print OUT "The old f command is now the r command.\n";
+ print OUT "The new f command switches filenames.\n";
+ next CMD;
+ }
+ if (!defined $_main{'_<' . $file}) {
+ if (($try) = grep(m#^_<.*$file#, keys %_main)) {
+ $file = substr($try,2);
+ print "\n$file:\n";
+ }
+ }
+ if (!defined $_main{'_<' . $file}) {
+ print OUT "There's no code here anything matching $file.\n";
+ next CMD;
+ }
+ elsif ($file ne $filename) {
+ *dbline = "_<$file";
+ $max = $#dbline;
+ $filename = $file;
+ $start = 1;
+ $cmd = "l";
+ } };
+ $cmd =~ /^l\b\s*(['A-Za-z_]['\w]*)/ && do {
+ $subname = $1;
+ $subname = "main'" . $subname unless $subname =~ /'/;
+ $subname = "main" . $subname if substr($subname,0,1) eq "'";
+ ($file,$subrange) = split(/:/,$sub{$subname});
+ if ($file ne $filename) {
+ *dbline = "_<$file";
+ $max = $#dbline;
+ $filename = $file;
+ }
+ if ($subrange) {
+ if (eval($subrange) < -$window) {
+ $subrange =~ s/-.*/+/;
+ }
+ $cmd = "l $subrange";
+ } else {
+ print OUT "Subroutine $1 not found.\n";
+ next CMD;
+ } };
+ $cmd =~ /^w\b\s*(\d*)$/ && do {
+ $incr = $window - 1;
+ $start = $1 if $1;
+ $start -= $preview;
+ $cmd = 'l ' . $start . '-' . ($start + $incr); };
+ $cmd =~ /^-$/ && do {
+ $incr = $window - 1;
+ $cmd = 'l ' . ($start-$window*2) . '+'; };
+ $cmd =~ /^l$/ && do {
+ $incr = $window - 1;
+ $cmd = 'l ' . $start . '-' . ($start + $incr); };
+ $cmd =~ /^l\b\s*(\d*)\+(\d*)$/ && do {
+ $start = $1 if $1;
+ $incr = $2;
+ $incr = $window - 1 unless $incr;
+ $cmd = 'l ' . $start . '-' . ($start + $incr); };
+ $cmd =~ /^l\b\s*(([\d\$\.]+)([-,]([\d\$\.]+))?)?/ && do {
+ $end = (!$2) ? $max : ($4 ? $4 : $2);
+ $end = $max if $end > $max;
+ $i = $2;
+ $i = $line if $i eq '.';
+ $i = 1 if $i < 1;
+ if ($emacs) {
+ print OUT "\032\032$filename:$i:0\n";
+ $i = $end;
+ } else {
+ for (; $i <= $end; $i++) {
+ print OUT "$i:\t", $dbline[$i];
+ last if $signal;
+ }
+ }
+ $start = $i; # remember in case they want more
+ $start = $max if $start > $max;
+ next CMD; };
+ $cmd =~ /^D$/ && do {
+ print OUT "Deleting all breakpoints...\n";
+ for ($i = 1; $i <= $max ; $i++) {
+ if (defined $dbline{$i}) {
+ $dbline{$i} =~ s/^[^\0]+//;
+ if ($dbline{$i} =~ s/^\0?$//) {
+ delete $dbline{$i};
+ }
+ }
+ }
+ next CMD; };
+ $cmd =~ /^L$/ && do {
+ for ($i = 1; $i <= $max; $i++) {
+ if (defined $dbline{$i}) {
+ print OUT "$i:\t", $dbline[$i];
+ ($stop,$action) = split(/\0/, $dbline{$i});
+ print OUT " break if (", $stop, ")\n"
+ if $stop;
+ print OUT " action: ", $action, "\n"
+ if $action;
+ last if $signal;
+ }
+ }
+ next CMD; };
+ $cmd =~ /^b\b\s*(['A-Za-z_]['\w]*)\s*(.*)/ && do {
+ $subname = $1;
+ $cond = $2 || '1';
+ $subname = "$package'" . $subname unless $subname =~ /'/;
+ $subname = "main" . $subname if substr($subname,0,1) eq "'";
+ ($filename,$i) = split(/:/, $sub{$subname});
+ $i += 0;
+ if ($i) {
+ *dbline = "_<$filename";
+ ++$i while $dbline[$i] == 0 && $i < $#dbline;
+ $dbline{$i} =~ s/^[^\0]*/$cond/;
+ } else {
+ print OUT "Subroutine $subname not found.\n";
+ }
+ next CMD; };
+ $cmd =~ /^b\b\s*(\d*)\s*(.*)/ && do {
+ $i = ($1?$1:$line);
+ $cond = $2 || '1';
+ if ($dbline[$i] == 0) {
+ print OUT "Line $i not breakable.\n";
+ } else {
+ $dbline{$i} =~ s/^[^\0]*/$cond/;
+ }
+ next CMD; };
+ $cmd =~ /^d\b\s*(\d+)?/ && do {
+ $i = ($1?$1:$line);
+ $dbline{$i} =~ s/^[^\0]*//;
+ delete $dbline{$i} if $dbline{$i} eq '';
+ next CMD; };
+ $cmd =~ /^A$/ && do {
+ for ($i = 1; $i <= $max ; $i++) {
+ if (defined $dbline{$i}) {
+ $dbline{$i} =~ s/\0[^\0]*//;
+ delete $dbline{$i} if $dbline{$i} eq '';
+ }
+ }
+ next CMD; };
+ $cmd =~ /^<\s*(.*)/ && do {
+ $pre = do action($1);
+ next CMD; };
+ $cmd =~ /^>\s*(.*)/ && do {
+ $post = do action($1);
+ next CMD; };
+ $cmd =~ /^a\b\s*(\d+)(\s+(.*))?/ && do {
+ $i = $1;
+ if ($dbline[$i] == 0) {
+ print OUT "Line $i may not have an action.\n";
+ } else {
+ $dbline{$i} =~ s/\0[^\0]*//;
+ $dbline{$i} .= "\0" . do action($3);
+ }
+ next CMD; };
+ $cmd =~ /^n$/ && do {
+ $single = 2;
+ $laststep = $cmd;
+ last CMD; };
+ $cmd =~ /^s$/ && do {
+ $single = 1;
+ $laststep = $cmd;
+ last CMD; };
+ $cmd =~ /^c\b\s*(\d*)\s*$/ && do {
+ $i = $1;
+ if ($i) {
+ if ($dbline[$i] == 0) {
+ print OUT "Line $i not breakable.\n";
+ next CMD;
+ }
+ $dbline{$i} =~ s/(\0|$)/;9$1/; # add one-time-only b.p.
+ }
+ for ($i=0; $i <= $#stack; ) {
+ $stack[$i++] &= ~1;
+ }
+ last CMD; };
+ $cmd =~ /^r$/ && do {
+ $stack[$#stack] |= 2;
+ last CMD; };
+ $cmd =~ /^T$/ && do {
+ local($p,$f,$l,$s,$h,$a,@a,@sub);
+ for ($i = 1; ($p,$f,$l,$s,$h,$w) = caller($i); $i++) {
+ @a = @args;
+ for (@a) {
+ if (/^StB\000/ && length($_) == length($_main{'_main'})) {
+ $_ = sprintf("%s",$_);
+ }
+ else {
+ s/'/\\'/g;
+ s/([^\0]*)/'$1'/ unless /^-?[\d.]+$/;
+ s/([\200-\377])/sprintf("M-%c",ord($1)&0177)/eg;
+ s/([\0-\37\177])/sprintf("^%c",ord($1)^64)/eg;
+ }
+ }
+ $w = $w ? '@ = ' : '$ = ';
+ $a = $h ? '(' . join(', ', @a) . ')' : '';
+ push(@sub, "$w&$s$a from file $f line $l\n");
+ last if $signal;
+ }
+ for ($i=0; $i <= $#sub; $i++) {
+ last if $signal;
+ print OUT $sub[$i];
+ }
+ next CMD; };
+ $cmd =~ /^\/(.*)$/ && do {
+ $inpat = $1;
+ $inpat =~ s:([^\\])/$:$1:;
+ if ($inpat ne "") {
+ eval '$inpat =~ m'."\n$inpat\n";
+ if ($@ ne "") {
+ print OUT "$@";
+ next CMD;
+ }
+ $pat = $inpat;
+ }
+ $end = $start;
+ eval '
+ for (;;) {
+ ++$start;
+ $start = 1 if ($start > $max);
+ last if ($start == $end);
+ if ($dbline[$start] =~ m'."\n$pat\n".'i) {
+ if ($emacs) {
+ print OUT "\032\032$filename:$start:0\n";
+ } else {
+ print OUT "$start:\t", $dbline[$start], "\n";
+ }
+ last;
+ }
+ } ';
+ print OUT "/$pat/: not found\n" if ($start == $end);
+ next CMD; };
+ $cmd =~ /^\?(.*)$/ && do {
+ $inpat = $1;
+ $inpat =~ s:([^\\])\?$:$1:;
+ if ($inpat ne "") {
+ eval '$inpat =~ m'."\n$inpat\n";
+ if ($@ ne "") {
+ print OUT "$@";
+ next CMD;
+ }
+ $pat = $inpat;
+ }
+ $end = $start;
+ eval '
+ for (;;) {
+ --$start;
+ $start = $max if ($start <= 0);
+ last if ($start == $end);
+ if ($dbline[$start] =~ m'."\n$pat\n".'i) {
+ if ($emacs) {
+ print OUT "\032\032$filename:$start:0\n";
+ } else {
+ print OUT "$start:\t", $dbline[$start], "\n";
+ }
+ last;
+ }
+ } ';
+ print OUT "?$pat?: not found\n" if ($start == $end);
+ next CMD; };
+ $cmd =~ /^!+\s*(-)?(\d+)?$/ && do {
+ pop(@hist) if length($cmd) > 1;
+ $i = ($1?($#hist-($2?$2:1)):($2?$2:$#hist));
+ $cmd = $hist[$i] . "\n";
+ print OUT $cmd;
+ redo CMD; };
+ $cmd =~ /^!(.+)$/ && do {
+ $pat = "^$1";
+ pop(@hist) if length($cmd) > 1;
+ for ($i = $#hist; $i; --$i) {
+ last if $hist[$i] =~ $pat;
+ }
+ if (!$i) {
+ print OUT "No such command!\n\n";
+ next CMD;
+ }
+ $cmd = $hist[$i] . "\n";
+ print OUT $cmd;
+ redo CMD; };
+ $cmd =~ /^H\b\s*(-(\d+))?/ && do {
+ $end = $2?($#hist-$2):0;
+ $hist = 0 if $hist < 0;
+ for ($i=$#hist; $i>$end; $i--) {
+ print OUT "$i: ",$hist[$i],"\n"
+ unless $hist[$i] =~ /^.?$/;
+ };
+ next CMD; };
+ $cmd =~ s/^p( .*)?$/print DB'OUT$1/;
+ $cmd =~ /^=/ && do {
+ if (local($k,$v) = ($cmd =~ /^=\s*(\S+)\s+(.*)/)) {
+ $alias{$k}="s~$k~$v~";
+ print OUT "$k = $v\n";
+ } elsif ($cmd =~ /^=\s*$/) {
+ foreach $k (sort keys(%alias)) {
+ if (($v = $alias{$k}) =~ s~^s\~$k\~(.*)\~$~$1~) {
+ print OUT "$k = $v\n";
+ } else {
+ print OUT "$k\t$alias{$k}\n";
+ };
+ };
+ };
+ next CMD; };
+ }
+ $evalarg = $cmd; &eval;
+ print OUT "\n";
+ }
+ if ($post) {
+ $evalarg = $post; &eval;
+ }
+ }
+ ($@, $!, $[, $,, $/, $\) = @saved;
+}
+
+sub save {
+ @saved = ($@, $!, $[, $,, $/, $\);
+ $[ = 0; $, = ""; $/ = "\n"; $\ = "";
+}
+
+# The following takes its argument via $evalarg to preserve current @_
+
+sub eval {
+ eval "$usercontext $evalarg; &DB'save";
+ print OUT $@;
+}
+
+sub action {
+ local($action) = @_;
+ while ($action =~ s/\\$//) {
+ print OUT "+ ";
+ $action .= &gets;
+ }
+ $action;
+}
+
+sub gets {
+ local($.);
+ <IN>;
+}
+
+sub catch {
+ $signal = 1;
+}
+
+sub sub {
+ push(@stack, $single);
+ $single &= 1;
+ $single |= 4 if $#stack == $deep;
+ if (wantarray) {
+ @i = &$sub;
+ $single |= pop(@stack);
+ @i;
+ }
+ else {
+ $i = &$sub;
+ $single |= pop(@stack);
+ $i;
+ }
+}
+
+$single = 1; # so it stops on first executable statement
+@hist = ('?');
+$SIG{'INT'} = "DB'catch";
+$deep = 100; # warning if stack gets this deep
+$window = 10;
+$preview = 3;
+
+@stack = (0);
+@ARGS = @ARGV;
+for (@args) {
+ s/'/\\'/g;
+ s/(.*)/'$1'/ unless /^-?[\d.]+$/;
+}
+
+if (-f $rcfile) {
+ do "./$rcfile";
+}
+elsif (-f "$ENV{'LOGDIR'}/$rcfile") {
+ do "$ENV{'LOGDIR'}/$rcfile";
+}
+elsif (-f "$ENV{'HOME'}/$rcfile") {
+ do "$ENV{'HOME'}/$rcfile";
+}
+
+1;
diff --git a/gnu/usr.bin/perl/lib/pwd.pl b/gnu/usr.bin/perl/lib/pwd.pl
new file mode 100644
index 0000000..16baadc
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/pwd.pl
@@ -0,0 +1,72 @@
+;# pwd.pl - keeps track of current working directory in PWD environment var
+;#
+;# $RCSfile: pwd.pl,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:52 $
+;#
+;# $Log: pwd.pl,v $
+# Revision 1.1.1.1 1993/08/23 21:29:52 nate
+# PERL!
+#
+;# Revision 4.0.1.1 92/06/08 13:45:22 lwall
+;# patch20: support added to pwd.pl to strip automounter crud
+;#
+;# Revision 4.0 91/03/20 01:26:03 lwall
+;# 4.0 baseline.
+;#
+;# Revision 3.0.1.2 91/01/11 18:09:24 lwall
+;# patch42: some .pl files were missing their trailing 1;
+;#
+;# Revision 3.0.1.1 90/08/09 04:01:24 lwall
+;# patch19: Initial revision
+;#
+;#
+;# Usage:
+;# require "pwd.pl";
+;# &initpwd;
+;# ...
+;# &chdir($newdir);
+
+package pwd;
+
+sub main'initpwd {
+ if ($ENV{'PWD'}) {
+ local($dd,$di) = stat('.');
+ local($pd,$pi) = stat($ENV{'PWD'});
+ if ($di != $pi || $dd != $pd) {
+ chop($ENV{'PWD'} = `pwd`);
+ }
+ }
+ else {
+ chop($ENV{'PWD'} = `pwd`);
+ }
+ if ($ENV{'PWD'} =~ m|(/[^/]+(/[^/]+/[^/]+))(.*)|) {
+ local($pd,$pi) = stat($2);
+ local($dd,$di) = stat($1);
+ if ($di == $pi && $dd == $pd) {
+ $ENV{'PWD'}="$2$3";
+ }
+ }
+}
+
+sub main'chdir {
+ local($newdir) = shift;
+ if (chdir $newdir) {
+ if ($newdir =~ m#^/#) {
+ $ENV{'PWD'} = $newdir;
+ }
+ else {
+ local(@curdir) = split(m#/#,$ENV{'PWD'});
+ @curdir = '' unless @curdir;
+ foreach $component (split(m#/#, $newdir)) {
+ next if $component eq '.';
+ pop(@curdir),next if $component eq '..';
+ push(@curdir,$component);
+ }
+ $ENV{'PWD'} = join('/',@curdir) || '/';
+ }
+ }
+ else {
+ 0;
+ }
+}
+
+1;
diff --git a/gnu/usr.bin/perl/lib/shellwords.pl b/gnu/usr.bin/perl/lib/shellwords.pl
new file mode 100644
index 0000000..5d593da
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/shellwords.pl
@@ -0,0 +1,48 @@
+;# shellwords.pl
+;#
+;# Usage:
+;# require 'shellwords.pl';
+;# @words = &shellwords($line);
+;# or
+;# @words = &shellwords(@lines);
+;# or
+;# @words = &shellwords; # defaults to $_ (and clobbers it)
+
+sub shellwords {
+ package shellwords;
+ local($_) = join('', @_) if @_;
+ local(@words,$snippet,$field);
+
+ s/^\s+//;
+ while ($_ ne '') {
+ $field = '';
+ for (;;) {
+ if (s/^"(([^"\\]|\\[\\"])*)"//) {
+ ($snippet = $1) =~ s#\\(.)#$1#g;
+ }
+ elsif (/^"/) {
+ die "Unmatched double quote: $_\n";
+ }
+ elsif (s/^'(([^'\\]|\\[\\'])*)'//) {
+ ($snippet = $1) =~ s#\\(.)#$1#g;
+ }
+ elsif (/^'/) {
+ die "Unmatched single quote: $_\n";
+ }
+ elsif (s/^\\(.)//) {
+ $snippet = $1;
+ }
+ elsif (s/^([^\s\\'"]+)//) {
+ $snippet = $1;
+ }
+ else {
+ s/^\s+//;
+ last;
+ }
+ $field .= $snippet;
+ }
+ push(@words, $field);
+ }
+ @words;
+}
+1;
diff --git a/gnu/usr.bin/perl/lib/stat.pl b/gnu/usr.bin/perl/lib/stat.pl
new file mode 100644
index 0000000..6186f54
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/stat.pl
@@ -0,0 +1,31 @@
+;# $Header: /home/cvs/386BSD/ports/lang/perl/lib/stat.pl,v 1.1.1.1 1993/08/23 21:29:53 nate Exp $
+
+;# Usage:
+;# require 'stat.pl';
+;# @ary = stat(foo);
+;# $st_dev = @ary[$ST_DEV];
+;#
+$ST_DEV = 0 + $[;
+$ST_INO = 1 + $[;
+$ST_MODE = 2 + $[;
+$ST_NLINK = 3 + $[;
+$ST_UID = 4 + $[;
+$ST_GID = 5 + $[;
+$ST_RDEV = 6 + $[;
+$ST_SIZE = 7 + $[;
+$ST_ATIME = 8 + $[;
+$ST_MTIME = 9 + $[;
+$ST_CTIME = 10 + $[;
+$ST_BLKSIZE = 11 + $[;
+$ST_BLOCKS = 12 + $[;
+
+;# Usage:
+;# require 'stat.pl';
+;# do Stat('foo'); # sets st_* as a side effect
+;#
+sub Stat {
+ ($st_dev,$st_ino,$st_mode,$st_nlink,$st_uid,$st_gid,$st_rdev,$st_size,
+ $st_atime,$st_mtime,$st_ctime,$st_blksize,$st_blocks) = stat(shift(@_));
+}
+
+1;
diff --git a/gnu/usr.bin/perl/lib/syslog.pl b/gnu/usr.bin/perl/lib/syslog.pl
new file mode 100644
index 0000000..94a4f6a
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/syslog.pl
@@ -0,0 +1,224 @@
+#
+# syslog.pl
+#
+# $Log: syslog.pl,v $
+# Revision 1.1.1.1 1993/08/23 21:29:51 nate
+# PERL!
+#
+# Revision 4.0.1.1 92/06/08 13:48:05 lwall
+# patch20: new warning for ambiguous use of unary operators
+#
+# Revision 4.0 91/03/20 01:26:24 lwall
+# 4.0 baseline.
+#
+# Revision 3.0.1.4 90/11/10 01:41:11 lwall
+# patch38: syslog.pl was referencing an absolute path
+#
+# Revision 3.0.1.3 90/10/15 17:42:18 lwall
+# patch29: various portability fixes
+#
+# Revision 3.0.1.1 90/08/09 03:57:17 lwall
+# patch19: Initial revision
+#
+# Revision 1.2 90/06/11 18:45:30 18:45:30 root ()
+# - Changed 'warn' to 'mail|warning' in test call (to give example of
+# facility specification, and because 'warn' didn't work on HP-UX).
+# - Fixed typo in &openlog ("ncons" should be "cons").
+# - Added (package-global) $maskpri, and &setlogmask.
+# - In &syslog:
+# - put argument test ahead of &connect (why waste cycles?),
+# - allowed facility to be specified in &syslog's first arg (temporarily
+# overrides any $facility set in &openlog), just as in syslog(3C),
+# - do a return 0 when bit for $numpri not set in log mask (see syslog(3C)),
+# - changed $whoami code to use getlogin, getpwuid($<) and 'syslog'
+# (in that order) when $ident is null,
+# - made PID logging consistent with syslog(3C) and subject to $lo_pid only,
+# - fixed typo in "print CONS" statement ($<facility should be <$facility).
+# - changed \n to \r in print CONS (\r is useful, $message already has a \n).
+# - Changed &xlate to return -1 for an unknown name, instead of croaking.
+#
+#
+# tom christiansen <tchrist@convex.com>
+# modified to use sockets by Larry Wall <lwall@jpl-devvax.jpl.nasa.gov>
+# NOTE: openlog now takes three arguments, just like openlog(3)
+#
+# call syslog() with a string priority and a list of printf() args
+# like syslog(3)
+#
+# usage: require 'syslog.pl';
+#
+# then (put these all in a script to test function)
+#
+#
+# do openlog($program,'cons,pid','user');
+# do syslog('info','this is another test');
+# do syslog('mail|warning','this is a better test: %d', time);
+# do closelog();
+#
+# do syslog('debug','this is the last test');
+# do openlog("$program $$",'ndelay','user');
+# do syslog('notice','fooprogram: this is really done');
+#
+# $! = 55;
+# do syslog('info','problem was %m'); # %m == $! in syslog(3)
+
+package syslog;
+
+$host = 'localhost' unless $host; # set $syslog'host to change
+
+require 'syslog.ph';
+
+$maskpri = &LOG_UPTO(&LOG_DEBUG);
+
+sub main'openlog {
+ ($ident, $logopt, $facility) = @_; # package vars
+ $lo_pid = $logopt =~ /\bpid\b/;
+ $lo_ndelay = $logopt =~ /\bndelay\b/;
+ $lo_cons = $logopt =~ /\bcons\b/;
+ $lo_nowait = $logopt =~ /\bnowait\b/;
+ &connect if $lo_ndelay;
+}
+
+sub main'closelog {
+ $facility = $ident = '';
+ &disconnect;
+}
+
+sub main'setlogmask {
+ local($oldmask) = $maskpri;
+ $maskpri = shift;
+ $oldmask;
+}
+
+sub main'syslog {
+ local($priority) = shift;
+ local($mask) = shift;
+ local($message, $whoami);
+ local(@words, $num, $numpri, $numfac, $sum);
+ local($facility) = $facility; # may need to change temporarily.
+
+ die "syslog: expected both priority and mask" unless $mask && $priority;
+
+ @words = split(/\W+/, $priority, 2);# Allow "level" or "level|facility".
+ undef $numpri;
+ undef $numfac;
+ foreach (@words) {
+ $num = &xlate($_); # Translate word to number.
+ if (/^kern$/ || $num < 0) {
+ die "syslog: invalid level/facility: $_\n";
+ }
+ elsif ($num <= &LOG_PRIMASK) {
+ die "syslog: too many levels given: $_\n" if defined($numpri);
+ $numpri = $num;
+ return 0 unless &LOG_MASK($numpri) & $maskpri;
+ }
+ else {
+ die "syslog: too many facilities given: $_\n" if defined($numfac);
+ $facility = $_;
+ $numfac = $num;
+ }
+ }
+
+ die "syslog: level must be given\n" unless defined($numpri);
+
+ if (!defined($numfac)) { # Facility not specified in this call.
+ $facility = 'user' unless $facility;
+ $numfac = &xlate($facility);
+ }
+
+ &connect unless $connected;
+
+ $whoami = $ident;
+
+ if (!$ident && $mask =~ /^(\S.*):\s?(.*)/) {
+ $whoami = $1;
+ $mask = $2;
+ }
+
+ unless ($whoami) {
+ ($whoami = getlogin) ||
+ ($whoami = getpwuid($<)) ||
+ ($whoami = 'syslog');
+ }
+
+ $whoami .= "[$$]" if $lo_pid;
+
+ $mask =~ s/%m/$!/g;
+ $mask .= "\n" unless $mask =~ /\n$/;
+ $message = sprintf ($mask, @_);
+
+ $sum = $numpri + $numfac;
+ unless (send(SYSLOG,"<$sum>$whoami: $message",0)) {
+ if ($lo_cons) {
+ if ($pid = fork) {
+ unless ($lo_nowait) {
+ do {$died = wait;} until $died == $pid || $died < 0;
+ }
+ }
+ else {
+ open(CONS,">/dev/console");
+ print CONS "<$facility.$priority>$whoami: $message\r";
+ exit if defined $pid; # if fork failed, we're parent
+ close CONS;
+ }
+ }
+ }
+}
+
+sub xlate {
+ local($name) = @_;
+ $name =~ y/a-z/A-Z/;
+ $name = "LOG_$name" unless $name =~ /^LOG_/;
+ $name = "syslog'$name";
+ eval(&$name) || -1;
+}
+
+sub connect {
+ $pat = 'S n C4 x8';
+
+ $af_unix = 1;
+ $af_inet = 2;
+
+ $stream = 1;
+ $datagram = 2;
+
+ ($name,$aliases,$proto) = getprotobyname('udp');
+ $udp = $proto;
+
+ ($name,$aliase,$port,$proto) = getservbyname('syslog','udp');
+ $syslog = $port;
+
+ if (chop($myname = `hostname`)) {
+ ($name,$aliases,$addrtype,$length,@addrs) = gethostbyname($myname);
+ die "Can't lookup $myname\n" unless $name;
+ @bytes = unpack("C4",$addrs[0]);
+ }
+ else {
+ @bytes = (0,0,0,0);
+ }
+ $this = pack($pat, $af_inet, 0, @bytes);
+
+ if ($host =~ /^\d+\./) {
+ @bytes = split(/\./,$host);
+ }
+ else {
+ ($name,$aliases,$addrtype,$length,@addrs) = gethostbyname($host);
+ die "Can't lookup $host\n" unless $name;
+ @bytes = unpack("C4",$addrs[0]);
+ }
+ $that = pack($pat,$af_inet,$syslog,@bytes);
+
+ socket(SYSLOG,$af_inet,$datagram,$udp) || die "socket: $!\n";
+ bind(SYSLOG,$this) || die "bind: $!\n";
+ connect(SYSLOG,$that) || die "connect: $!\n";
+
+ local($old) = select(SYSLOG); $| = 1; select($old);
+ $connected = 1;
+}
+
+sub disconnect {
+ close SYSLOG;
+ $connected = 0;
+}
+
+1;
diff --git a/gnu/usr.bin/perl/lib/termcap.pl b/gnu/usr.bin/perl/lib/termcap.pl
new file mode 100644
index 0000000..81556db
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/termcap.pl
@@ -0,0 +1,165 @@
+;# $RCSfile: termcap.pl,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:52 $
+;#
+;# Usage:
+;# require 'ioctl.pl';
+;# ioctl(TTY,$TIOCGETP,$foo);
+;# ($ispeed,$ospeed) = unpack('cc',$foo);
+;# require 'termcap.pl';
+;# &Tgetent('vt100'); # sets $TC{'cm'}, etc.
+;# &Tputs(&Tgoto($TC{'cm'},$col,$row), 0, 'FILEHANDLE');
+;# &Tputs($TC{'dl'},$affcnt,'FILEHANDLE');
+;#
+sub Tgetent {
+ local($TERM) = @_;
+ local($TERMCAP,$_,$entry,$loop,$field);
+
+ warn "Tgetent: no ospeed set" unless $ospeed;
+ foreach $key (keys(TC)) {
+ delete $TC{$key};
+ }
+ $TERM = $ENV{'TERM'} unless $TERM;
+ $TERMCAP = $ENV{'TERMCAP'};
+ $TERMCAP = '/etc/termcap' unless $TERMCAP;
+ if ($TERMCAP !~ m:^/:) {
+ if ($TERMCAP !~ /(^|\|)$TERM[:\|]/) {
+ $TERMCAP = '/etc/termcap';
+ }
+ }
+ if ($TERMCAP =~ m:^/:) {
+ $entry = '';
+ do {
+ $loop = "
+ open(TERMCAP,'<$TERMCAP') || die \"Can't open $TERMCAP\";
+ while (<TERMCAP>) {
+ next if /^#/;
+ next if /^\t/;
+ if (/(^|\\|)$TERM[:\\|]/) {
+ chop;
+ while (chop eq '\\\\') {
+ \$_ .= <TERMCAP>;
+ chop;
+ }
+ \$_ .= ':';
+ last;
+ }
+ }
+ close TERMCAP;
+ \$entry .= \$_;
+ ";
+ eval $loop;
+ } while s/:tc=([^:]+):/:/ && ($TERM = $1);
+ $TERMCAP = $entry;
+ }
+
+ foreach $field (split(/:[\s:\\]*/,$TERMCAP)) {
+ if ($field =~ /^\w\w$/) {
+ $TC{$field} = 1;
+ }
+ elsif ($field =~ /^(\w\w)#(.*)/) {
+ $TC{$1} = $2 if $TC{$1} eq '';
+ }
+ elsif ($field =~ /^(\w\w)=(.*)/) {
+ $entry = $1;
+ $_ = $2;
+ s/\\E/\033/g;
+ s/\\(\d\d\d)/pack('c',$1 & 0177)/eg;
+ s/\\n/\n/g;
+ s/\\r/\r/g;
+ s/\\t/\t/g;
+ s/\\b/\b/g;
+ s/\\f/\f/g;
+ s/\\\^/\377/g;
+ s/\^\?/\177/g;
+ s/\^(.)/pack('c',ord($1) & 31)/eg;
+ s/\\(.)/$1/g;
+ s/\377/^/g;
+ $TC{$entry} = $_ if $TC{$entry} eq '';
+ }
+ }
+ $TC{'pc'} = "\0" if $TC{'pc'} eq '';
+ $TC{'bc'} = "\b" if $TC{'bc'} eq '';
+}
+
+@Tputs = (0,200,133.3,90.9,74.3,66.7,50,33.3,16.7,8.3,5.5,4.1,2,1,.5,.2);
+
+sub Tputs {
+ local($string,$affcnt,$FH) = @_;
+ local($ms);
+ if ($string =~ /(^[\d.]+)(\*?)(.*)$/) {
+ $ms = $1;
+ $ms *= $affcnt if $2;
+ $string = $3;
+ $decr = $Tputs[$ospeed];
+ if ($decr > .1) {
+ $ms += $decr / 2;
+ $string .= $TC{'pc'} x ($ms / $decr);
+ }
+ }
+ print $FH $string if $FH;
+ $string;
+}
+
+sub Tgoto {
+ local($string) = shift(@_);
+ local($result) = '';
+ local($after) = '';
+ local($code,$tmp) = @_;
+ local(@tmp);
+ @tmp = ($tmp,$code);
+ local($online) = 0;
+ while ($string =~ /^([^%]*)%(.)(.*)/) {
+ $result .= $1;
+ $code = $2;
+ $string = $3;
+ if ($code eq 'd') {
+ $result .= sprintf("%d",shift(@tmp));
+ }
+ elsif ($code eq '.') {
+ $tmp = shift(@tmp);
+ if ($tmp == 0 || $tmp == 4 || $tmp == 10) {
+ if ($online) {
+ ++$tmp, $after .= $TC{'up'} if $TC{'up'};
+ }
+ else {
+ ++$tmp, $after .= $TC{'bc'};
+ }
+ }
+ $result .= sprintf("%c",$tmp);
+ $online = !$online;
+ }
+ elsif ($code eq '+') {
+ $result .= sprintf("%c",shift(@tmp)+ord($string));
+ $string = substr($string,1,99);
+ $online = !$online;
+ }
+ elsif ($code eq 'r') {
+ ($code,$tmp) = @tmp;
+ @tmp = ($tmp,$code);
+ $online = !$online;
+ }
+ elsif ($code eq '>') {
+ ($code,$tmp,$string) = unpack("CCa99",$string);
+ if ($tmp[$[] > $code) {
+ $tmp[$[] += $tmp;
+ }
+ }
+ elsif ($code eq '2') {
+ $result .= sprintf("%02d",shift(@tmp));
+ $online = !$online;
+ }
+ elsif ($code eq '3') {
+ $result .= sprintf("%03d",shift(@tmp));
+ $online = !$online;
+ }
+ elsif ($code eq 'i') {
+ ($code,$tmp) = @tmp;
+ @tmp = ($code+1,$tmp+1);
+ }
+ else {
+ return "OOPS";
+ }
+ }
+ $result . $string . $after;
+}
+
+1;
diff --git a/gnu/usr.bin/perl/lib/timelocal.pl b/gnu/usr.bin/perl/lib/timelocal.pl
new file mode 100644
index 0000000..b7367fa
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/timelocal.pl
@@ -0,0 +1,82 @@
+;# timelocal.pl
+;#
+;# Usage:
+;# $time = timelocal($sec,$min,$hours,$mday,$mon,$year);
+;# $time = timegm($sec,$min,$hours,$mday,$mon,$year);
+
+;# These routines are quite efficient and yet are always guaranteed to agree
+;# with localtime() and gmtime(). We manage this by caching the start times
+;# of any months we've seen before. If we know the start time of the month,
+;# we can always calculate any time within the month. The start times
+;# themselves are guessed by successive approximation starting at the
+;# current time, since most dates seen in practice are close to the
+;# current date. Unlike algorithms that do a binary search (calling gmtime
+;# once for each bit of the time value, resulting in 32 calls), this algorithm
+;# calls it at most 6 times, and usually only once or twice. If you hit
+;# the month cache, of course, it doesn't call it at all.
+
+;# timelocal is implemented using the same cache. We just assume that we're
+;# translating a GMT time, and then fudge it when we're done for the timezone
+;# and daylight savings arguments. The timezone is determined by examining
+;# the result of localtime(0) when the package is initialized. The daylight
+;# savings offset is currently assumed to be one hour.
+
+CONFIG: {
+ package timelocal;
+
+ local($[) = 0;
+ @epoch = localtime(0);
+ $tzmin = $epoch[2] * 60 + $epoch[1]; # minutes east of GMT
+ if ($tzmin > 0) {
+ $tzmin = 24 * 60 - $tzmin; # minutes west of GMT
+ $tzmin -= 24 * 60 if $epoch[5] == 70; # account for the date line
+ }
+
+ $SEC = 1;
+ $MIN = 60 * $SEC;
+ $HR = 60 * $MIN;
+ $DAYS = 24 * $HR;
+ $YearFix = ((gmtime(946684800))[5] == 100) ? 100 : 0;
+}
+
+sub timegm {
+ package timelocal;
+
+ local($[) = 0;
+ $ym = pack(C2, @_[5,4]);
+ $cheat = $cheat{$ym} || &cheat;
+ $cheat + $_[0] * $SEC + $_[1] * $MIN + $_[2] * $HR + ($_[3]-1) * $DAYS;
+}
+
+sub timelocal {
+ package timelocal;
+
+ local($[) = 0;
+ $time = &main'timegm + $tzmin*$MIN;
+ @test = localtime($time);
+ $time -= $HR if $test[2] != $_[2];
+ $time;
+}
+
+package timelocal;
+
+sub cheat {
+ $year = $_[5];
+ $month = $_[4];
+ die "Month out of range 0..11 in ctime.pl\n" if $month > 11;
+ $guess = $^T;
+ @g = gmtime($guess);
+ $year += $YearFix if $year < $epoch[5];
+ while ($diff = $year - $g[5]) {
+ $guess += $diff * (363 * $DAYS);
+ @g = gmtime($guess);
+ }
+ while ($diff = $month - $g[4]) {
+ $guess += $diff * (27 * $DAYS);
+ @g = gmtime($guess);
+ }
+ $g[3]--;
+ $guess -= $g[0] * $SEC + $g[1] * $MIN + $g[2] * $HR + $g[3] * $DAYS;
+ $cheat{$ym} = $guess;
+}
+1;
diff --git a/gnu/usr.bin/perl/lib/validate.pl b/gnu/usr.bin/perl/lib/validate.pl
new file mode 100644
index 0000000..4b901b6
--- /dev/null
+++ b/gnu/usr.bin/perl/lib/validate.pl
@@ -0,0 +1,104 @@
+;# $Header: /home/cvs/386BSD/ports/lang/perl/lib/validate.pl,v 1.1.1.1 1993/08/23 21:29:51 nate Exp $
+
+;# The validate routine takes a single multiline string consisting of
+;# lines containing a filename plus a file test to try on it. (The
+;# file test may also be a 'cd', causing subsequent relative filenames
+;# to be interpreted relative to that directory.) After the file test
+;# you may put '|| die' to make it a fatal error if the file test fails.
+;# The default is '|| warn'. The file test may optionally have a ! prepended
+;# to test for the opposite condition. If you do a cd and then list some
+;# relative filenames, you may want to indent them slightly for readability.
+;# If you supply your own "die" or "warn" message, you can use $file to
+;# interpolate the filename.
+
+;# Filetests may be bunched: -rwx tests for all of -r, -w and -x.
+;# Only the first failed test of the bunch will produce a warning.
+
+;# The routine returns the number of warnings issued.
+
+;# Usage:
+;# require "validate.pl";
+;# $warnings += do validate('
+;# /vmunix -e || die
+;# /boot -e || die
+;# /bin cd
+;# csh -ex
+;# csh !-ug
+;# sh -ex
+;# sh !-ug
+;# /usr -d || warn "What happened to $file?\n"
+;# ');
+
+sub validate {
+ local($file,$test,$warnings,$oldwarnings);
+ foreach $check (split(/\n/,$_[0])) {
+ next if $check =~ /^#/;
+ next if $check =~ /^$/;
+ ($file,$test) = split(' ',$check,2);
+ if ($test =~ s/^(!?-)(\w{2,}\b)/$1Z/) {
+ $testlist = $2;
+ @testlist = split(//,$testlist);
+ }
+ else {
+ @testlist = ('Z');
+ }
+ $oldwarnings = $warnings;
+ foreach $one (@testlist) {
+ $this = $test;
+ $this =~ s/(-\w\b)/$1 \$file/g;
+ $this =~ s/-Z/-$one/;
+ $this .= ' || warn' unless $this =~ /\|\|/;
+ $this =~ s/^(.*\S)\s*\|\|\s*(die|warn)$/$1 || do valmess('$2','$1')/;
+ $this =~ s/\bcd\b/chdir (\$cwd = \$file)/g;
+ eval $this;
+ last if $warnings > $oldwarnings;
+ }
+ }
+ $warnings;
+}
+
+sub valmess {
+ local($disposition,$this) = @_;
+ $file = $cwd . '/' . $file unless $file =~ m|^/|;
+ if ($this =~ /^(!?)-(\w)\s+\$file\s*$/) {
+ $neg = $1;
+ $tmp = $2;
+ $tmp eq 'r' && ($mess = "$file is not readable by uid $>.");
+ $tmp eq 'w' && ($mess = "$file is not writable by uid $>.");
+ $tmp eq 'x' && ($mess = "$file is not executable by uid $>.");
+ $tmp eq 'o' && ($mess = "$file is not owned by uid $>.");
+ $tmp eq 'R' && ($mess = "$file is not readable by you.");
+ $tmp eq 'W' && ($mess = "$file is not writable by you.");
+ $tmp eq 'X' && ($mess = "$file is not executable by you.");
+ $tmp eq 'O' && ($mess = "$file is not owned by you.");
+ $tmp eq 'e' && ($mess = "$file does not exist.");
+ $tmp eq 'z' && ($mess = "$file does not have zero size.");
+ $tmp eq 's' && ($mess = "$file does not have non-zero size.");
+ $tmp eq 'f' && ($mess = "$file is not a plain file.");
+ $tmp eq 'd' && ($mess = "$file is not a directory.");
+ $tmp eq 'l' && ($mess = "$file is not a symbolic link.");
+ $tmp eq 'p' && ($mess = "$file is not a named pipe (FIFO).");
+ $tmp eq 'S' && ($mess = "$file is not a socket.");
+ $tmp eq 'b' && ($mess = "$file is not a block special file.");
+ $tmp eq 'c' && ($mess = "$file is not a character special file.");
+ $tmp eq 'u' && ($mess = "$file does not have the setuid bit set.");
+ $tmp eq 'g' && ($mess = "$file does not have the setgid bit set.");
+ $tmp eq 'k' && ($mess = "$file does not have the sticky bit set.");
+ $tmp eq 'T' && ($mess = "$file is not a text file.");
+ $tmp eq 'B' && ($mess = "$file is not a binary file.");
+ if ($neg eq '!') {
+ $mess =~ s/ is not / should not be / ||
+ $mess =~ s/ does not / should not / ||
+ $mess =~ s/ not / /;
+ }
+ print stderr $mess,"\n";
+ }
+ else {
+ $this =~ s/\$file/'$file'/g;
+ print stderr "Can't do $this.\n";
+ }
+ if ($disposition eq 'die') { exit 1; }
+ ++$warnings;
+}
+
+1;
diff --git a/gnu/usr.bin/perl/misc/c2ph b/gnu/usr.bin/perl/misc/c2ph
new file mode 100644
index 0000000..0e06c9c
--- /dev/null
+++ b/gnu/usr.bin/perl/misc/c2ph
@@ -0,0 +1,1071 @@
+#!/usr/gnu/bin/perl
+#
+#
+# c2ph (aka pstruct)
+# Tom Christiansen, <tchrist@convex.com>
+#
+# As pstruct, dump C structures as generated from 'cc -g -S' stabs.
+# As c2ph, do this PLUS generate perl code for getting at the structures.
+#
+# See the usage message for more. If this isn't enough, read the code.
+#
+
+$RCSID = '$RCSfile: c2ph,v $$Revision: 1.2 $$Date: 1994/03/05 01:28:15 $';
+
+
+######################################################################
+
+# some handy data definitions. many of these can be reset later.
+
+$bitorder = 'b'; # ascending; set to B for descending bit fields
+
+%intrinsics =
+%template = (
+ 'char', 'c',
+ 'unsigned char', 'C',
+ 'short', 's',
+ 'short int', 's',
+ 'unsigned short', 'S',
+ 'unsigned short int', 'S',
+ 'short unsigned int', 'S',
+ 'int', 'i',
+ 'unsigned int', 'I',
+ 'long', 'l',
+ 'long int', 'l',
+ 'unsigned long', 'L',
+ 'unsigned long', 'L',
+ 'long unsigned int', 'L',
+ 'unsigned long int', 'L',
+ 'long long', 'q',
+ 'long long int', 'q',
+ 'unsigned long long', 'Q',
+ 'unsigned long long int', 'Q',
+ 'float', 'f',
+ 'double', 'd',
+ 'pointer', 'p',
+ 'null', 'x',
+ 'neganull', 'X',
+ 'bit', $bitorder,
+);
+
+&buildscrunchlist;
+delete $intrinsics{'neganull'};
+delete $intrinsics{'bit'};
+delete $intrinsics{'null'};
+
+# use -s to recompute sizes
+%sizeof = (
+ 'char', '1',
+ 'unsigned char', '1',
+ 'short', '2',
+ 'short int', '2',
+ 'unsigned short', '2',
+ 'unsigned short int', '2',
+ 'short unsigned int', '2',
+ 'int', '4',
+ 'unsigned int', '4',
+ 'long', '4',
+ 'long int', '4',
+ 'unsigned long', '4',
+ 'unsigned long int', '4',
+ 'long unsigned int', '4',
+ 'long long', '8',
+ 'long long int', '8',
+ 'unsigned long long', '8',
+ 'unsigned long long int', '8',
+ 'float', '4',
+ 'double', '8',
+ 'pointer', '4',
+);
+
+($type_width, $member_width, $offset_width, $size_width) = (20, 20, 6, 5);
+
+($offset_fmt, $size_fmt) = ('d', 'd');
+
+$indent = 2;
+
+$CC = 'cc';
+$CFLAGS = '-g -S';
+$DEFINES = '';
+
+$perl++ if $0 =~ m#/?c2ph$#;
+
+require 'getopts.pl';
+
+eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_]+=)(.*)/ && shift;
+
+&Getopts('aixdpvtnws:') || &usage(0);
+
+$opt_d && $debug++;
+$opt_t && $trace++;
+$opt_p && $perl++;
+$opt_v && $verbose++;
+$opt_n && ($perl = 0);
+
+if ($opt_w) {
+ ($type_width, $member_width, $offset_width) = (45, 35, 8);
+}
+if ($opt_x) {
+ ($offset_fmt, $offset_width, $size_fmt, $size_width) = ( 'x', '08', 'x', 04 );
+}
+
+eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_]+=)(.*)/ && shift;
+
+sub PLUMBER {
+ select(STDERR);
+ print "oops, apperent pager foulup\n";
+ $isatty++;
+ &usage(1);
+}
+
+sub usage {
+ local($oops) = @_;
+ unless (-t STDOUT) {
+ select(STDERR);
+ } elsif (!$oops) {
+ $isatty++;
+ $| = 1;
+ print "hit <RETURN> for further explanation: ";
+ <STDIN>;
+ open (PIPE, "|". ($ENV{PAGER} || 'more'));
+ $SIG{PIPE} = PLUMBER;
+ select(PIPE);
+ }
+
+ print "usage: $0 [-dpnP] [var=val] [files ...]\n";
+
+ exit unless $isatty;
+
+ print <<EOF;
+
+Options:
+
+-w wide; short for: type_width=45 member_width=35 offset_width=8
+-x hex; short for: offset_fmt=x offset_width=08 size_fmt=x size_width=04
+
+-n do not generate perl code (default when invoked as pstruct)
+-p generate perl code (default when invoked as c2ph)
+-v generate perl code, with C decls as comments
+
+-i do NOT recompute sizes for intrinsic datatypes
+-a dump information on intrinsics also
+
+-t trace execution
+-d spew reams of debugging output
+
+-slist give comma-separated list a structures to dump
+
+
+Var Name Default Value Meaning
+
+EOF
+
+ &defvar('CC', 'which_compiler to call');
+ &defvar('CFLAGS', 'how to generate *.s files with stabs');
+ &defvar('DEFINES','any extra cflags or cpp defines, like -I, -D, -U');
+
+ print "\n";
+
+ &defvar('type_width', 'width of type field (column 1)');
+ &defvar('member_width', 'width of member field (column 2)');
+ &defvar('offset_width', 'width of offset field (column 3)');
+ &defvar('size_width', 'width of size field (column 4)');
+
+ print "\n";
+
+ &defvar('offset_fmt', 'sprintf format type for offset');
+ &defvar('size_fmt', 'sprintf format type for size');
+
+ print "\n";
+
+ &defvar('indent', 'how far to indent each nesting level');
+
+ print <<'EOF';
+
+ If any *.[ch] files are given, these will be catted together into
+ a temporary *.c file and sent through:
+ $CC $CFLAGS $DEFINES
+ and the resulting *.s groped for stab information. If no files are
+ supplied, then stdin is read directly with the assumption that it
+ contains stab information. All other liens will be ignored. At
+ most one *.s file should be supplied.
+
+EOF
+ close PIPE;
+ exit 1;
+}
+
+sub defvar {
+ local($var, $msg) = @_;
+ printf "%-16s%-15s %s\n", $var, eval "\$$var", $msg;
+}
+
+$recurse = 1;
+
+if (@ARGV) {
+ if (grep(!/\.[csh]$/,@ARGV)) {
+ warn "Only *.[csh] files expected!\n";
+ &usage;
+ }
+ elsif (grep(/\.s$/,@ARGV)) {
+ if (@ARGV > 1) {
+ warn "Only one *.s file allowed!\n";
+ &usage;
+ }
+ }
+ elsif (@ARGV == 1 && $ARGV[0] =~ /\.c$/) {
+ local($dir, $file) = $ARGV[0] =~ m#(.*/)?(.*)$#;
+ $chdir = "cd $dir; " if $dir;
+ &system("$chdir$CC $CFLAGS $DEFINES $file") && exit 1;
+ $ARGV[0] =~ s/\.c$/.s/;
+ }
+ else {
+ $TMP = "/tmp/c2ph.$$.c";
+ &system("cat @ARGV > $TMP") && exit 1;
+ &system("cd /tmp; $CC $CFLAGS $DEFINES $TMP") && exit 1;
+ unlink $TMP;
+ $TMP =~ s/\.c$/.s/;
+ @ARGV = ($TMP);
+ }
+}
+
+if ($opt_s) {
+ for (split(/[\s,]+/, $opt_s)) {
+ $interested{$_}++;
+ }
+}
+
+
+$| = 1 if $debug;
+
+main: {
+
+ if ($trace) {
+ if (-t && !@ARGV) {
+ print STDERR "reading from your keyboard: ";
+ } else {
+ print STDERR "reading from " . (@ARGV ? "@ARGV" : "<STDIN>").": ";
+ }
+ }
+
+STAB: while (<>) {
+ if ($trace && !($. % 10)) {
+ $lineno = $..'';
+ print STDERR $lineno, "\b" x length($lineno);
+ }
+ next unless /^\s*\.stabs\s+/;
+ $line = $_;
+ s/^\s*\.stabs\s+//;
+ &stab;
+ }
+ print STDERR "$.\n" if $trace;
+ unlink $TMP if $TMP;
+
+ &compute_intrinsics if $perl && !$opt_i;
+
+ print STDERR "resolving types\n" if $trace;
+
+ &resolve_types;
+ &adjust_start_addrs;
+
+ $sum = 2 + $type_width + $member_width;
+ $pmask1 = "%-${type_width}s %-${member_width}s";
+ $pmask2 = "%-${sum}s %${offset_width}${offset_fmt}%s %${size_width}${size_fmt}%s";
+
+ if ($perl) {
+ # resolve template -- should be in stab define order, but even this isn't enough.
+ print STDERR "\nbuilding type templates: " if $trace;
+ for $i (reverse 0..$#type) {
+ next unless defined($name = $type[$i]);
+ next unless defined $struct{$name};
+ $build_recursed = 0;
+ &build_template($name) unless defined $template{&psou($name)} ||
+ $opt_s && !$interested{$name};
+ }
+ print STDERR "\n\n" if $trace;
+ }
+
+ print STDERR "dumping structs: " if $trace;
+
+
+ foreach $name (sort keys %struct) {
+ next if $opt_s && !$interested{$name};
+ print STDERR "$name " if $trace;
+
+ undef @sizeof;
+ undef @typedef;
+ undef @offsetof;
+ undef @indices;
+ undef @typeof;
+
+ $mname = &munge($name);
+
+ $fname = &psou($name);
+
+ print "# " if $perl && $verbose;
+ $pcode = '';
+ print "$fname {\n" if !$perl || $verbose;
+ $template{$fname} = &scrunch($template{$fname}) if $perl;
+ &pstruct($name,$name,0);
+ print "# " if $perl && $verbose;
+ print "}\n" if !$perl || $verbose;
+ print "\n" if $perl && $verbose;
+
+ if ($perl) {
+ print "$pcode";
+
+ printf("\nsub %-32s { %4d; }\n\n", "${mname}'struct", $countof{$name});
+
+ print <<EOF;
+sub ${mname}'typedef {
+ local(\$${mname}'index) = shift;
+ defined \$${mname}'index
+ ? \$${mname}'typedef[\$${mname}'index]
+ : \$${mname}'typedef;
+}
+EOF
+
+ print <<EOF;
+sub ${mname}'sizeof {
+ local(\$${mname}'index) = shift;
+ defined \$${mname}'index
+ ? \$${mname}'sizeof[\$${mname}'index]
+ : \$${mname}'sizeof;
+}
+EOF
+
+ print <<EOF;
+sub ${mname}'offsetof {
+ local(\$${mname}'index) = shift;
+ defined \$${mname}index
+ ? \$${mname}'offsetof[\$${mname}'index]
+ : \$${mname}'sizeof;
+}
+EOF
+
+ print <<EOF;
+sub ${mname}'typeof {
+ local(\$${mname}'index) = shift;
+ defined \$${mname}index
+ ? \$${mname}'typeof[\$${mname}'index]
+ : '$name';
+}
+EOF
+
+
+ print "\$${mname}'typedef = '" . &scrunch($template{$fname})
+ . "';\n";
+
+ print "\$${mname}'sizeof = $sizeof{$name};\n\n";
+
+
+ print "\@${mname}'indices = (", &squishseq(@indices), ");\n";
+
+ print "\n";
+
+ print "\@${mname}'typedef[\@${mname}'indices] = (",
+ join("\n\t", '', @typedef), "\n );\n\n";
+ print "\@${mname}'sizeof[\@${mname}'indices] = (",
+ join("\n\t", '', @sizeof), "\n );\n\n";
+ print "\@${mname}'offsetof[\@${mname}'indices] = (",
+ join("\n\t", '', @offsetof), "\n );\n\n";
+ print "\@${mname}'typeof[\@${mname}'indices] = (",
+ join("\n\t", '', @typeof), "\n );\n\n";
+
+ $template_printed{$fname}++;
+ $size_printed{$fname}++;
+ }
+ print "\n";
+ }
+
+ print STDERR "\n" if $trace;
+
+ unless ($perl && $opt_a) {
+ print "\n1;\n";
+ exit;
+ }
+
+
+
+ foreach $name (sort bysizevalue keys %intrinsics) {
+ next if $size_printed{$name};
+ print '$',&munge($name),"'sizeof = ", $sizeof{$name}, ";\n";
+ }
+
+ print "\n";
+
+ sub bysizevalue { $sizeof{$a} <=> $sizeof{$b}; }
+
+
+ foreach $name (sort keys %intrinsics) {
+ print '$',&munge($name),"'typedef = '", $template{$name}, "';\n";
+ }
+
+ print "\n1;\n";
+
+ exit;
+}
+
+########################################################################################
+
+
+sub stab {
+ next unless /:[\$\w]+(\(\d+,\d+\))?=[\*\$\w]+/; # (\d+,\d+) is for sun
+ s/"// || next;
+ s/",([x\d]+),([x\d]+),([x\d]+),.*// || next;
+
+ next if /^\s*$/;
+
+ $size = $3 if $3;
+
+
+ $line = $_;
+
+ if (($name, $pdecl) = /^([\$ \w]+):[tT]((\d+)(=[rufs*](\d+))+)$/) {
+ print "$name is a typedef for some funky pointers: $pdecl\n" if $debug;
+ &pdecl($pdecl);
+ next;
+ }
+
+
+
+ if (/(([ \w]+):t(\d+|\(\d+,\d+\)))=r?(\d+|\(\d+,\d+\))(;\d+;\d+;)?/) {
+ local($ident) = $2;
+ push(@intrinsics, $ident);
+ $typeno = &typeno($3);
+ $type[$typeno] = $ident;
+ print STDERR "intrinsic $ident in new type $typeno\n" if $debug;
+ next;
+ }
+
+ if (($name, $typeordef, $typeno, $extra, $struct, $_)
+ = /^([\$ \w]+):([ustT])(\d+|\(\d+,\d+\))(=[rufs*](\d+))?(.*)$/)
+ {
+ $typeno = &typeno($typeno); # sun foolery
+ }
+ elsif (/^[\$\w]+:/) {
+ next; # variable
+ }
+ else {
+ warn "can't grok stab: <$_> in: $line " if $_;
+ next;
+ }
+
+ #warn "got size $size for $name\n";
+ $sizeof{$name} = $size if $size;
+
+ s/;[-\d]*;[-\d]*;$//; # we don't care about ranges
+
+ $typenos{$name} = $typeno;
+
+ unless (defined $type[$typeno]) {
+ &panic("type 0??") unless $typeno;
+ $type[$typeno] = $name unless defined $type[$typeno];
+ printf "new type $typeno is $name" if $debug;
+ if ($extra =~ /\*/ && defined $type[$struct]) {
+ print ", a typedef for a pointer to " , $type[$struct] if $debug;
+ }
+ } else {
+ printf "%s is type %d", $name, $typeno if $debug;
+ print ", a typedef for " , $type[$typeno] if $debug;
+ }
+ print "\n" if $debug;
+ #next unless $extra =~ /[su*]/;
+
+ #$type[$struct] = $name;
+
+ if ($extra =~ /[us*]/) {
+ &sou($name, $extra);
+ $_ = &sdecl($name, $_, 0);
+ }
+ elsif (/^=ar/) {
+ print "it's a bare array typedef -- that's pretty sick\n" if $debug;
+ $_ = "$typeno$_";
+ $scripts = '';
+ $_ = &adecl($_,1);
+
+ }
+ elsif (s/((\w+):t(\d+|\(\d+,\d+\)))?=r?(;\d+;\d+;)?//) { # the ?'s are for gcc
+ push(@intrinsics, $2);
+ $typeno = &typeno($3);
+ $type[$typeno] = $2;
+ print STDERR "intrinsic $2 in new type $typeno\n" if $debug;
+ }
+ elsif (s/^=e//) { # blessed by thy compiler; mine won't do this
+ &edecl;
+ }
+ else {
+ warn "Funny remainder for $name on line $_ left in $line " if $_;
+ }
+}
+
+sub typeno { # sun thinks types are (0,27) instead of just 27
+ local($_) = @_;
+ s/\(\d+,(\d+)\)/$1/;
+ $_;
+}
+
+sub pstruct {
+ local($what,$prefix,$base) = @_;
+ local($field, $fieldname, $typeno, $count, $offset, $entry);
+ local($fieldtype);
+ local($type, $tname);
+ local($mytype, $mycount, $entry2);
+ local($struct_count) = 0;
+ local($pad, $revpad, $length, $prepad, $lastoffset, $lastlength, $fmt);
+ local($bits,$bytes);
+ local($template);
+
+
+ local($mname) = &munge($name);
+
+ sub munge {
+ local($_) = @_;
+ s/[\s\$\.]/_/g;
+ $_;
+ }
+
+ local($sname) = &psou($what);
+
+ $nesting++;
+
+ for $field (split(/;/, $struct{$what})) {
+ $pad = $prepad = 0;
+ $entry = '';
+ ($fieldname, $typeno, $count, $offset, $length) = split(/,/, $field);
+
+ $type = $type[$typeno];
+
+ $type =~ /([^[]*)(\[.*\])?/;
+ $mytype = $1;
+ $count .= $2;
+ $fieldtype = &psou($mytype);
+
+ local($fname) = &psou($name);
+
+ if ($build_templates) {
+
+ $pad = ($offset - ($lastoffset + $lastlength))/8
+ if defined $lastoffset;
+
+ if (! $finished_template{$sname}) {
+ if ($isaunion{$what}) {
+ $template{$sname} .= 'X' x $revpad . ' ' if $revpad;
+ } else {
+ $template{$sname} .= 'x' x $pad . ' ' if $pad;
+ }
+ }
+
+ $template = &fetch_template($type) x
+ ($count ? &scripts2count($count) : 1);
+
+ if (! $finished_template{$sname}) {
+ $template{$sname} .= $template;
+ }
+
+ $revpad = $length/8 if $isaunion{$what};
+
+ ($lastoffset, $lastlength) = ($offset, $length);
+
+ } else {
+ print '# ' if $perl && $verbose;
+ $entry = sprintf($pmask1,
+ ' ' x ($nesting * $indent) . $fieldtype,
+ "$prefix.$fieldname" . $count);
+
+ $entry =~ s/(\*+)( )/$2$1/;
+
+ printf $pmask2,
+ $entry,
+ ($base+$offset)/8,
+ ($bits = ($base+$offset)%8) ? ".$bits" : " ",
+ $length/8,
+ ($bits = $length % 8) ? ".$bits": ""
+ if !$perl || $verbose;
+
+
+ if ($perl && $nesting == 1) {
+ $template = &scrunch(&fetch_template($type) x
+ ($count ? &scripts2count($count) : 1));
+ push(@sizeof, int($length/8) .",\t# $fieldname");
+ push(@offsetof, int($offset/8) .",\t# $fieldname");
+ push(@typedef, "'$template', \t# $fieldname");
+ $type =~ s/(struct|union) //;
+ push(@typeof, "'$type" . ($count ? $count : '') .
+ "',\t# $fieldname");
+ }
+
+ print ' ', ' ' x $indent x $nesting, $template
+ if $perl && $verbose;
+
+ print "\n" if !$perl || $verbose;
+
+ }
+ if ($perl) {
+ local($mycount) = defined $struct{$mytype} ? $countof{$mytype} : 1;
+ $mycount *= &scripts2count($count) if $count;
+ if ($nesting==1 && !$build_templates) {
+ $pcode .= sprintf("sub %-32s { %4d; }\n",
+ "${mname}'${fieldname}", $struct_count);
+ push(@indices, $struct_count);
+ }
+ $struct_count += $mycount;
+ }
+
+
+ &pstruct($type, "$prefix.$fieldname", $base+$offset)
+ if $recurse && defined $struct{$type};
+ }
+
+ $countof{$what} = $struct_count unless defined $countof{$whati};
+
+ $template{$sname} .= '$' if $build_templates;
+ $finished_template{$sname}++;
+
+ if ($build_templates && !defined $sizeof{$name}) {
+ local($fmt) = &scrunch($template{$sname});
+ print STDERR "no size for $name, punting with $fmt..." if $debug;
+ eval '$sizeof{$name} = length(pack($fmt, ()))';
+ if ($@) {
+ chop $@;
+ warn "couldn't get size for \$name: $@";
+ } else {
+ print STDERR $sizeof{$name}, "\n" if $debUg;
+ }
+ }
+
+ --$nesting;
+}
+
+
+sub psize {
+ local($me) = @_;
+ local($amstruct) = $struct{$me} ? 'struct ' : '';
+
+ print '$sizeof{\'', $amstruct, $me, '\'} = ';
+ printf "%d;\n", $sizeof{$me};
+}
+
+sub pdecl {
+ local($pdecl) = @_;
+ local(@pdecls);
+ local($tname);
+
+ warn "pdecl: $pdecl\n" if $debug;
+
+ $pdecl =~ s/\(\d+,(\d+)\)/$1/g;
+ $pdecl =~ s/\*//g;
+ @pdecls = split(/=/, $pdecl);
+ $typeno = $pdecls[0];
+ $tname = pop @pdecls;
+
+ if ($tname =~ s/^f//) { $tname = "$tname&"; }
+ #else { $tname = "$tname*"; }
+
+ for (reverse @pdecls) {
+ $tname .= s/^f// ? "&" : "*";
+ #$tname =~ s/^f(.*)/$1&/;
+ print "type[$_] is $tname\n" if $debug;
+ $type[$_] = $tname unless defined $type[$_];
+ }
+}
+
+
+
+sub adecl {
+ ($arraytype, $unknown, $lower, $upper) = ();
+ #local($typeno);
+ # global $typeno, @type
+ local($_, $typedef) = @_;
+
+ while (s/^((\d+)=)?ar(\d+);//) {
+ ($arraytype, $unknown) = ($2, $3);
+ if (s/^(\d+);(\d+);//) {
+ ($lower, $upper) = ($1, $2);
+ $scripts .= '[' . ($upper+1) . ']';
+ } else {
+ warn "can't find array bounds: $_";
+ }
+ }
+ if (s/^([\d*f=]*),(\d+),(\d+);//) {
+ ($start, $length) = ($2, $3);
+ local($whatis) = $1;
+ if ($whatis =~ /^(\d+)=/) {
+ $typeno = $1;
+ &pdecl($whatis);
+ } else {
+ $typeno = $whatis;
+ }
+ } elsif (s/^(\d+)(=[*suf]\d*)//) {
+ local($whatis) = $2;
+
+ if ($whatis =~ /[f*]/) {
+ &pdecl($whatis);
+ } elsif ($whatis =~ /[su]/) { #
+ print "$prefix.$fieldname is an array$scripts anon structs; disgusting\n"
+ if $debug;
+ #$type[$typeno] = $name unless defined $type[$typeno];
+ ##printf "new type $typeno is $name" if $debug;
+ $typeno = $1;
+ $type[$typeno] = "$prefix.$fieldname";
+ local($name) = $type[$typeno];
+ &sou($name, $whatis);
+ $_ = &sdecl($name, $_, $start+$offset);
+ 1;
+ $start = $start{$name};
+ $offset = $sizeof{$name};
+ $length = $offset;
+ } else {
+ warn "what's this? $whatis in $line ";
+ }
+ } elsif (/^\d+$/) {
+ $typeno = $_;
+ } else {
+ warn "bad array stab: $_ in $line ";
+ next STAB;
+ }
+ #local($wasdef) = defined($type[$typeno]) && $debug;
+ #if ($typedef) {
+ #print "redefining $type[$typeno] to " if $wasdef;
+ #$type[$typeno] = "$whatis$scripts"; # unless defined $type[$typeno];
+ #print "$type[$typeno]\n" if $wasdef;
+ #} else {
+ #$type[$arraytype] = $type[$typeno] unless defined $type[$arraytype];
+ #}
+ $type[$arraytype] = "$type[$typeno]$scripts" if defined $type[$typeno];
+ print "type[$arraytype] is $type[$arraytype]\n" if $debug;
+ print "$prefix.$fieldname is an array of $type[$arraytype]\n" if $debug;
+ $_;
+}
+
+
+
+sub sdecl {
+ local($prefix, $_, $offset) = @_;
+
+ local($fieldname, $scripts, $type, $arraytype, $unknown,
+ $whatis, $pdecl, $upper,$lower, $start,$length) = ();
+ local($typeno,$sou);
+
+
+SFIELD:
+ while (/^([^;]+);/) {
+ $scripts = '';
+ warn "sdecl $_\n" if $debug;
+ if (s/^([\$\w]+)://) {
+ $fieldname = $1;
+ } elsif (s/(\d+)=([us])(\d+|\(\d+,\d+\))//) { #
+ $typeno = &typeno($1);
+ $type[$typeno] = "$prefix.$fieldname";
+ local($name) = "$prefix.$fieldname";
+ &sou($name,$2);
+ $_ = &sdecl("$prefix.$fieldname", $_, $start+$offset);
+ $start = $start{$name};
+ $offset += $sizeof{$name};
+ #print "done with anon, start is $start, offset is $offset\n";
+ #next SFIELD;
+ } else {
+ warn "weird field $_ of $line" if $debug;
+ next STAB;
+ #$fieldname = &gensym;
+ #$_ = &sdecl("$prefix.$fieldname", $_, $start+$offset);
+ }
+
+ if (/^\d+=ar/) {
+ $_ = &adecl($_);
+ }
+ elsif (s/^(\d+|\(\d+,\d+\))?,(\d+),(\d+);//) {
+ ($start, $length) = ($2, $3);
+ &panic("no length?") unless $length;
+ $typeno = &typeno($1) if $1;
+ }
+ elsif (s/^((\d+|\(\d+,\d+\))(=[*f](\d+|\(\d+,\d+\)))+),(\d+),(\d+);//) {
+ ($pdecl, $start, $length) = ($1,$5,$6);
+ &pdecl($pdecl);
+ }
+ elsif (s/(\d+)=([us])(\d+|\(\d+,\d+\))//) { # the dratted anon struct
+ ($typeno, $sou) = ($1, $2);
+ $typeno = &typeno($typeno);
+ if (defined($type[$typeno])) {
+ warn "now how did we get type $1 in $fieldname of $line?";
+ } else {
+ print "anon type $typeno is $prefix.$fieldname\n" if $debug;
+ $type[$typeno] = "$prefix.$fieldname" unless defined $type[$typeno];
+ };
+ local($name) = "$prefix.$fieldname";
+ &sou($name,$sou);
+ print "anon ".($isastruct{$name}) ? "struct":"union"." for $prefix.$fieldname\n" if $debug;
+ $type[$typeno] = "$prefix.$fieldname";
+ $_ = &sdecl("$prefix.$fieldname", $_, $start+$offset);
+ $start = $start{$name};
+ $length = $sizeof{$name};
+ }
+ else {
+ warn "can't grok stab for $name ($_) in line $line ";
+ next STAB;
+ }
+
+ &panic("no length for $prefix.$fieldname") unless $length;
+ $struct{$name} .= join(',', $fieldname, $typeno, $scripts, $start, $length) . ';';
+ }
+ if (s/;\d*,(\d+),(\d+);//) {
+ local($start, $size) = ($1, $2);
+ $sizeof{$prefix} = $size;
+ print "start of $prefix is $start, size of $sizeof{$prefix}\n" if $debug;
+ $start{$prefix} = $start;
+ }
+ $_;
+}
+
+sub edecl {
+ s/;$//;
+ $enum{$name} = $_;
+ $_ = '';
+}
+
+sub resolve_types {
+ local($sou);
+ for $i (0 .. $#type) {
+ next unless defined $type[$i];
+ $_ = $type[$i];
+ unless (/\d/) {
+ print "type[$i] $type[$i]\n" if $debug;
+ next;
+ }
+ print "type[$i] $_ ==> " if $debug;
+ s/^(\d+)(\**)\&\*(\**)/"$2($3".&type($1) . ')()'/e;
+ s/^(\d+)\&/&type($1)/e;
+ s/^(\d+)/&type($1)/e;
+ s/(\*+)([^*]+)(\*+)/$1$3$2/;
+ s/\((\*+)(\w+)(\*+)\)/$3($1$2)/;
+ s/^(\d+)([\*\[].*)/&type($1).$2/e;
+ #s/(\d+)(\*|(\[[\[\]\d\*]+]\])+)/&type($1).$2/ge;
+ $type[$i] = $_;
+ print "$_\n" if $debug;
+ }
+}
+sub type { &psou($type[$_[0]] || "<UNDEFINED>"); }
+
+sub adjust_start_addrs {
+ for (sort keys %start) {
+ ($basename = $_) =~ s/\.[^.]+$//;
+ $start{$_} += $start{$basename};
+ print "start: $_ @ $start{$_}\n" if $debug;
+ }
+}
+
+sub sou {
+ local($what, $_) = @_;
+ /u/ && $isaunion{$what}++;
+ /s/ && $isastruct{$what}++;
+}
+
+sub psou {
+ local($what) = @_;
+ local($prefix) = '';
+ if ($isaunion{$what}) {
+ $prefix = 'union ';
+ } elsif ($isastruct{$what}) {
+ $prefix = 'struct ';
+ }
+ $prefix . $what;
+}
+
+sub scrunch {
+ local($_) = @_;
+
+ study;
+
+ s/\$//g;
+ s/ / /g;
+ 1 while s/(\w) \1/$1$1/g;
+
+ # i wanna say this, but perl resists my efforts:
+ # s/(\w)(\1+)/$2 . length($1)/ge;
+
+ &quick_scrunch;
+
+ s/ $//;
+
+ $_;
+}
+
+sub buildscrunchlist {
+ $scrunch_code = "sub quick_scrunch {\n";
+ for (values %intrinsics) {
+ $scrunch_code .= "\ts/($_{2,})/'$_' . length(\$1)/ge;\n";
+ }
+ $scrunch_code .= "}\n";
+ print "$scrunch_code" if $debug;
+ eval $scrunch_code;
+ &panic("can't eval scrunch_code $@ \nscrunch_code") if $@;
+}
+
+sub fetch_template {
+ local($mytype) = @_;
+ local($fmt);
+ local($count) = 1;
+
+ &panic("why do you care?") unless $perl;
+
+ if ($mytype =~ s/(\[\d+\])+$//) {
+ $count .= $1;
+ }
+
+ if ($mytype =~ /\*/) {
+ $fmt = $template{'pointer'};
+ }
+ elsif (defined $template{$mytype}) {
+ $fmt = $template{$mytype};
+ }
+ elsif (defined $struct{$mytype}) {
+ if (!defined $template{&psou($mytype)}) {
+ &build_template($mytype) unless $mytype eq $name;
+ }
+ elsif ($template{&psou($mytype)} !~ /\$$/) {
+ #warn "incomplete template for $mytype\n";
+ }
+ $fmt = $template{&psou($mytype)} || '?';
+ }
+ else {
+ warn "unknown fmt for $mytype\n";
+ $fmt = '?';
+ }
+
+ $fmt x $count . ' ';
+}
+
+sub compute_intrinsics {
+ local($TMP) = "/tmp/c2ph-i.$$.c";
+ open (TMP, ">$TMP") || die "can't open $TMP: $!";
+ select(TMP);
+
+ print STDERR "computing intrinsic sizes: " if $trace;
+
+ undef %intrinsics;
+
+ print <<'EOF';
+main() {
+ char *mask = "%d %s\n";
+EOF
+
+ for $type (@intrinsics) {
+ next if $type eq 'void';
+ print <<"EOF";
+ printf(mask,sizeof($type), "$type");
+EOF
+ }
+
+ print <<'EOF';
+ printf(mask,sizeof(char *), "pointer");
+ exit(0);
+}
+EOF
+ close TMP;
+
+ select(STDOUT);
+ open(PIPE, "cd /tmp && $CC $TMP && /tmp/a.out|");
+ while (<PIPE>) {
+ chop;
+ split(' ',$_,2);;
+ print "intrinsic $_[1] is size $_[0]\n" if $debug;
+ $sizeof{$_[1]} = $_[0];
+ $intrinsics{$_[1]} = $template{$_[0]};
+ }
+ close(PIPE) || die "couldn't read intrinsics!";
+ unlink($TMP, '/tmp/a.out');
+ print STDERR "done\n" if $trace;
+}
+
+sub scripts2count {
+ local($_) = @_;
+
+ s/^\[//;
+ s/\]$//;
+ s/\]\[/*/g;
+ $_ = eval;
+ &panic("$_: $@") if $@;
+ $_;
+}
+
+sub system {
+ print STDERR "@_\n" if $trace;
+ system @_;
+}
+
+sub build_template {
+ local($name) = @_;
+
+ &panic("already got a template for $name") if defined $template{$name};
+
+ local($build_templates) = 1;
+
+ local($lparen) = '(' x $build_recursed;
+ local($rparen) = ')' x $build_recursed;
+
+ print STDERR "$lparen$name$rparen " if $trace;
+ $build_recursed++;
+ &pstruct($name,$name,0);
+ print STDERR "TEMPLATE for $name is ", $template{&psou($name)}, "\n" if $debug;
+ --$build_recursed;
+}
+
+
+sub panic {
+
+ select(STDERR);
+
+ print "\npanic: @_\n";
+
+ exit 1 if $] <= 4.003; # caller broken
+
+ local($i,$_);
+ local($p,$f,$l,$s,$h,$a,@a,@sub);
+ for ($i = 0; ($p,$f,$l,$s,$h,$w) = caller($i); $i++) {
+ @a = @DB'args;
+ for (@a) {
+ if (/^StB\000/ && length($_) == length($_main{'_main'})) {
+ $_ = sprintf("%s",$_);
+ }
+ else {
+ s/'/\\'/g;
+ s/([^\0]*)/'$1'/ unless /^-?[\d.]+$/;
+ s/([\200-\377])/sprintf("M-%c",ord($1)&0177)/eg;
+ s/([\0-\37\177])/sprintf("^%c",ord($1)^64)/eg;
+ }
+ }
+ $w = $w ? '@ = ' : '$ = ';
+ $a = $h ? '(' . join(', ', @a) . ')' : '';
+ push(@sub, "$w&$s$a from file $f line $l\n");
+ last if $signal;
+ }
+ for ($i=0; $i <= $#sub; $i++) {
+ last if $signal;
+ print $sub[$i];
+ }
+ exit 1;
+}
+
+sub squishseq {
+ local($num);
+ local($last) = -1e8;
+ local($string);
+ local($seq) = '..';
+
+ while (defined($num = shift)) {
+ if ($num == ($last + 1)) {
+ $string .= $seq unless $inseq++;
+ $last = $num;
+ next;
+ } elsif ($inseq) {
+ $string .= $last unless $last == -1e8;
+ }
+
+ $string .= ',' if defined $string;
+ $string .= $num;
+ $last = $num;
+ $inseq = 0;
+ }
+ $string .= $last if $inseq && $last != -e18;
+ $string;
+}
diff --git a/gnu/usr.bin/perl/misc/c2ph.1 b/gnu/usr.bin/perl/misc/c2ph.1
new file mode 100644
index 0000000..0c3eaee
--- /dev/null
+++ b/gnu/usr.bin/perl/misc/c2ph.1
@@ -0,0 +1,191 @@
+Article 484 of comp.lang.perl:
+Xref: netlabs comp.lang.perl:484 comp.lang.c:983 alt.sources:134
+Path: netlabs!psinntp!iggy.GW.Vitalink.COM!lll-winken!sun-barr!cronkite.Central.Sun.COM!spdev!texsun!convex!tchrist
+From: tchrist@convex.com (Tom Christiansen)
+Newsgroups: comp.lang.perl,comp.lang.c,alt.sources
+Subject: pstruct -- a C structure formatter; AKA c2ph, a C to perl header translator
+Keywords: C perl tranlator
+Message-ID: <1991Jul25.081021.8104@convex.com>
+Date: 25 Jul 91 08:10:21 GMT
+Sender: usenet@convex.com (news access account)
+Followup-To: comp.lang.perl
+Organization: CONVEX Computer Corporation, Richardson, Tx., USA
+Lines: 1208
+Nntp-Posting-Host: pixel.convex.com
+
+Once upon a time, I wrote a program called pstruct. It was a perl
+program that tried to parse out C structures and display their member
+offsets for you. This was especially useful for people looking at
+binary dumps or poking around the kernel.
+
+Pstruct was not a pretty program. Neither was it particularly robust.
+The problem, you see, was that the C compiler was much better at parsing
+C than I could ever hope to be.
+
+So I got smart: I decided to be lazy and let the C compiler parse the C,
+which would spit out debugger stabs for me to read. These were much
+easier to parse. It's still not a pretty program, but at least it's more
+robust.
+
+Pstruct takes any .c or .h files, or preferably .s ones, since that's
+the format it is going to massage them into anyway, and spits out
+listings like this:
+
+struct tty {
+ int tty.t_locker 000 4
+ int tty.t_mutex_index 004 4
+ struct tty * tty.t_tp_virt 008 4
+ struct clist tty.t_rawq 00c 20
+ int tty.t_rawq.c_cc 00c 4
+ int tty.t_rawq.c_cmax 010 4
+ int tty.t_rawq.c_cfx 014 4
+ int tty.t_rawq.c_clx 018 4
+ struct tty * tty.t_rawq.c_tp_cpu 01c 4
+ struct tty * tty.t_rawq.c_tp_iop 020 4
+ unsigned char * tty.t_rawq.c_buf_cpu 024 4
+ unsigned char * tty.t_rawq.c_buf_iop 028 4
+ struct clist tty.t_canq 02c 20
+ int tty.t_canq.c_cc 02c 4
+ int tty.t_canq.c_cmax 030 4
+ int tty.t_canq.c_cfx 034 4
+ int tty.t_canq.c_clx 038 4
+ struct tty * tty.t_canq.c_tp_cpu 03c 4
+ struct tty * tty.t_canq.c_tp_iop 040 4
+ unsigned char * tty.t_canq.c_buf_cpu 044 4
+ unsigned char * tty.t_canq.c_buf_iop 048 4
+ struct clist tty.t_outq 04c 20
+ int tty.t_outq.c_cc 04c 4
+ int tty.t_outq.c_cmax 050 4
+ int tty.t_outq.c_cfx 054 4
+ int tty.t_outq.c_clx 058 4
+ struct tty * tty.t_outq.c_tp_cpu 05c 4
+ struct tty * tty.t_outq.c_tp_iop 060 4
+ unsigned char * tty.t_outq.c_buf_cpu 064 4
+ unsigned char * tty.t_outq.c_buf_iop 068 4
+ (*int)() tty.t_oproc_cpu 06c 4
+ (*int)() tty.t_oproc_iop 070 4
+ (*int)() tty.t_stopproc_cpu 074 4
+ (*int)() tty.t_stopproc_iop 078 4
+ struct thread * tty.t_rsel 07c 4
+
+ etc.
+
+
+Actually, this was generated by a particular set of options. You can control
+the formatting of each column, whether you prefer wide or fat, hex or decimal,
+leading zeroes or whatever.
+
+All you need to be able to use this is a C compiler than generates
+BSD/GCC-style stabs. The -g option on native BSD compilers and GCC
+should get this for you.
+
+To learn more, just type a bogus option, like -\?, and a long usage message
+will be provided. There are a fair number of possibilities.
+
+If you're only a C programmer, than this is the end of the message for you.
+You can quit right now, and if you care to, save off the source and run it
+when you feel like it. Or not.
+
+
+
+But if you're a perl programmer, then for you I have something much more
+wondrous than just a structure offset printer.
+
+You see, if you call pstruct by its other incybernation, c2ph, you have a code
+generator that translates C code into perl code! Well, structure and union
+declarations at least, but that's quite a bit.
+
+Prior to this point, anyone programming in perl who wanted to interact
+with C programs, like the kernel, was forced to guess the layouts of the C
+strutures, and then hardwire these into his program. Of course, when you
+took your wonderfully to a system where the sgtty structure was laid out
+differently, you program broke. Which is a shame.
+
+We've had Larry's h2ph translator, which helped, but that only works on
+cpp symbols, not real C, which was also very much needed. What I offer
+you is a symbolic way of getting at all the C structures. I've couched
+them in terms of packages and functions. Consider the following program:
+
+ #!/usr/local/bin/perl
+
+ require 'syscall.ph';
+ require 'sys/time.ph';
+ require 'sys/resource.ph';
+
+ $ru = "\0" x &rusage'sizeof();
+
+ syscall(&SYS_getrusage, &RUSAGE_SELF, $ru) && die "getrusage: $!";
+
+ @ru = unpack($t = &rusage'typedef(), $ru);
+
+ $utime = $ru[ &rusage'ru_utime + &timeval'tv_sec ]
+ + ($ru[ &rusage'ru_utime + &timeval'tv_usec ]) / 1e6;
+
+ $stime = $ru[ &rusage'ru_stime + &timeval'tv_sec ]
+ + ($ru[ &rusage'ru_stime + &timeval'tv_usec ]) / 1e6;
+
+ printf "you have used %8.3fs+%8.3fu seconds.\n", $utime, $stime;
+
+
+As you see, the name of the package is the name of the structure. Regular
+fields are just their own names. Plus the follwoing accessor functions are
+provided for your convenience:
+
+ struct This takes no arguments, and is merely the number of first-level
+ elements in the structure. You would use this for indexing
+ into arrays of structures, perhaps like this
+
+
+ $usec = $u[ &user'u_utimer
+ + (&ITIMER_VIRTUAL * &itimerval'struct)
+ + &itimerval'it_value
+ + &timeval'tv_usec
+ ];
+
+ sizeof Returns the bytes in the structure, or the member if
+ you pass it an argument, such as
+
+ &rusage'sizeof(&rusage'ru_utime)
+
+ typedef This is the perl format definition for passing to pack and
+ unpack. If you ask for the typedef of a nothing, you get
+ the whole structure, otherwise you get that of the member
+ you ask for. Padding is taken care of, as is the magic to
+ guarantee that a union is unpacked into all its aliases.
+ Bitfields are not quite yet supported however.
+
+ offsetof This function is the byte offset into the array of that
+ member. You may wish to use this for indexing directly
+ into the packed structure with vec() if you're too lazy
+ to unpack it.
+
+ typeof Not to be confused with the typedef accessor function, this
+ one returns the C type of that field. This would allow
+ you to print out a nice structured pretty print of some
+ structure without knoning anything about it beforehand.
+ No args to this one is a noop. Someday I'll post such
+ a thing to dump out your u structure for you.
+
+
+The way I see this being used is like basically this:
+
+ % h2ph <some_include_file.h > /usr/lib/perl/tmp.ph
+ % c2ph some_include_file.h >> /usr/lib/perl/tmp.ph
+ % install
+
+It's a little tricker with c2ph because you have to get the includes right.
+I can't know this for your system, but it's not usually too terribly difficult.
+
+The code isn't pretty as I mentioned -- I never thought it would be a 1000-
+line program when I started, or I might not have begun. :-) But I would have
+been less cavalier in how the parts of the program communicated with each
+other, etc. It might also have helped if I didn't have to divine the makeup
+of the stabs on the fly, and then account for micro differences between my
+compiler and gcc.
+
+Anyway, here it is. Should run on perl v4 or greater. Maybe less.
+
+
+--tom
+
+
diff --git a/gnu/usr.bin/perl/misc/pstruct b/gnu/usr.bin/perl/misc/pstruct
new file mode 100644
index 0000000..1009d29
--- /dev/null
+++ b/gnu/usr.bin/perl/misc/pstruct
@@ -0,0 +1,1071 @@
+#!/usr/gnu/bin/perl
+#
+#
+# c2ph (aka pstruct)
+# Tom Christiansen, <tchrist@convex.com>
+#
+# As pstruct, dump C structures as generated from 'cc -g -S' stabs.
+# As c2ph, do this PLUS generate perl code for getting at the structures.
+#
+# See the usage message for more. If this isn't enough, read the code.
+#
+
+$RCSID = '$RCSfile: pstruct,v $$Revision: 1.2 $$Date: 1994/03/05 01:28:22 $';
+
+
+######################################################################
+
+# some handy data definitions. many of these can be reset later.
+
+$bitorder = 'b'; # ascending; set to B for descending bit fields
+
+%intrinsics =
+%template = (
+ 'char', 'c',
+ 'unsigned char', 'C',
+ 'short', 's',
+ 'short int', 's',
+ 'unsigned short', 'S',
+ 'unsigned short int', 'S',
+ 'short unsigned int', 'S',
+ 'int', 'i',
+ 'unsigned int', 'I',
+ 'long', 'l',
+ 'long int', 'l',
+ 'unsigned long', 'L',
+ 'unsigned long', 'L',
+ 'long unsigned int', 'L',
+ 'unsigned long int', 'L',
+ 'long long', 'q',
+ 'long long int', 'q',
+ 'unsigned long long', 'Q',
+ 'unsigned long long int', 'Q',
+ 'float', 'f',
+ 'double', 'd',
+ 'pointer', 'p',
+ 'null', 'x',
+ 'neganull', 'X',
+ 'bit', $bitorder,
+);
+
+&buildscrunchlist;
+delete $intrinsics{'neganull'};
+delete $intrinsics{'bit'};
+delete $intrinsics{'null'};
+
+# use -s to recompute sizes
+%sizeof = (
+ 'char', '1',
+ 'unsigned char', '1',
+ 'short', '2',
+ 'short int', '2',
+ 'unsigned short', '2',
+ 'unsigned short int', '2',
+ 'short unsigned int', '2',
+ 'int', '4',
+ 'unsigned int', '4',
+ 'long', '4',
+ 'long int', '4',
+ 'unsigned long', '4',
+ 'unsigned long int', '4',
+ 'long unsigned int', '4',
+ 'long long', '8',
+ 'long long int', '8',
+ 'unsigned long long', '8',
+ 'unsigned long long int', '8',
+ 'float', '4',
+ 'double', '8',
+ 'pointer', '4',
+);
+
+($type_width, $member_width, $offset_width, $size_width) = (20, 20, 6, 5);
+
+($offset_fmt, $size_fmt) = ('d', 'd');
+
+$indent = 2;
+
+$CC = 'cc';
+$CFLAGS = '-g -S';
+$DEFINES = '';
+
+$perl++ if $0 =~ m#/?c2ph$#;
+
+require 'getopts.pl';
+
+eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_]+=)(.*)/ && shift;
+
+&Getopts('aixdpvtnws:') || &usage(0);
+
+$opt_d && $debug++;
+$opt_t && $trace++;
+$opt_p && $perl++;
+$opt_v && $verbose++;
+$opt_n && ($perl = 0);
+
+if ($opt_w) {
+ ($type_width, $member_width, $offset_width) = (45, 35, 8);
+}
+if ($opt_x) {
+ ($offset_fmt, $offset_width, $size_fmt, $size_width) = ( 'x', '08', 'x', 04 );
+}
+
+eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_]+=)(.*)/ && shift;
+
+sub PLUMBER {
+ select(STDERR);
+ print "oops, apperent pager foulup\n";
+ $isatty++;
+ &usage(1);
+}
+
+sub usage {
+ local($oops) = @_;
+ unless (-t STDOUT) {
+ select(STDERR);
+ } elsif (!$oops) {
+ $isatty++;
+ $| = 1;
+ print "hit <RETURN> for further explanation: ";
+ <STDIN>;
+ open (PIPE, "|". ($ENV{PAGER} || 'more'));
+ $SIG{PIPE} = PLUMBER;
+ select(PIPE);
+ }
+
+ print "usage: $0 [-dpnP] [var=val] [files ...]\n";
+
+ exit unless $isatty;
+
+ print <<EOF;
+
+Options:
+
+-w wide; short for: type_width=45 member_width=35 offset_width=8
+-x hex; short for: offset_fmt=x offset_width=08 size_fmt=x size_width=04
+
+-n do not generate perl code (default when invoked as pstruct)
+-p generate perl code (default when invoked as c2ph)
+-v generate perl code, with C decls as comments
+
+-i do NOT recompute sizes for intrinsic datatypes
+-a dump information on intrinsics also
+
+-t trace execution
+-d spew reams of debugging output
+
+-slist give comma-separated list a structures to dump
+
+
+Var Name Default Value Meaning
+
+EOF
+
+ &defvar('CC', 'which_compiler to call');
+ &defvar('CFLAGS', 'how to generate *.s files with stabs');
+ &defvar('DEFINES','any extra cflags or cpp defines, like -I, -D, -U');
+
+ print "\n";
+
+ &defvar('type_width', 'width of type field (column 1)');
+ &defvar('member_width', 'width of member field (column 2)');
+ &defvar('offset_width', 'width of offset field (column 3)');
+ &defvar('size_width', 'width of size field (column 4)');
+
+ print "\n";
+
+ &defvar('offset_fmt', 'sprintf format type for offset');
+ &defvar('size_fmt', 'sprintf format type for size');
+
+ print "\n";
+
+ &defvar('indent', 'how far to indent each nesting level');
+
+ print <<'EOF';
+
+ If any *.[ch] files are given, these will be catted together into
+ a temporary *.c file and sent through:
+ $CC $CFLAGS $DEFINES
+ and the resulting *.s groped for stab information. If no files are
+ supplied, then stdin is read directly with the assumption that it
+ contains stab information. All other liens will be ignored. At
+ most one *.s file should be supplied.
+
+EOF
+ close PIPE;
+ exit 1;
+}
+
+sub defvar {
+ local($var, $msg) = @_;
+ printf "%-16s%-15s %s\n", $var, eval "\$$var", $msg;
+}
+
+$recurse = 1;
+
+if (@ARGV) {
+ if (grep(!/\.[csh]$/,@ARGV)) {
+ warn "Only *.[csh] files expected!\n";
+ &usage;
+ }
+ elsif (grep(/\.s$/,@ARGV)) {
+ if (@ARGV > 1) {
+ warn "Only one *.s file allowed!\n";
+ &usage;
+ }
+ }
+ elsif (@ARGV == 1 && $ARGV[0] =~ /\.c$/) {
+ local($dir, $file) = $ARGV[0] =~ m#(.*/)?(.*)$#;
+ $chdir = "cd $dir; " if $dir;
+ &system("$chdir$CC $CFLAGS $DEFINES $file") && exit 1;
+ $ARGV[0] =~ s/\.c$/.s/;
+ }
+ else {
+ $TMP = "/tmp/c2ph.$$.c";
+ &system("cat @ARGV > $TMP") && exit 1;
+ &system("cd /tmp; $CC $CFLAGS $DEFINES $TMP") && exit 1;
+ unlink $TMP;
+ $TMP =~ s/\.c$/.s/;
+ @ARGV = ($TMP);
+ }
+}
+
+if ($opt_s) {
+ for (split(/[\s,]+/, $opt_s)) {
+ $interested{$_}++;
+ }
+}
+
+
+$| = 1 if $debug;
+
+main: {
+
+ if ($trace) {
+ if (-t && !@ARGV) {
+ print STDERR "reading from your keyboard: ";
+ } else {
+ print STDERR "reading from " . (@ARGV ? "@ARGV" : "<STDIN>").": ";
+ }
+ }
+
+STAB: while (<>) {
+ if ($trace && !($. % 10)) {
+ $lineno = $..'';
+ print STDERR $lineno, "\b" x length($lineno);
+ }
+ next unless /^\s*\.stabs\s+/;
+ $line = $_;
+ s/^\s*\.stabs\s+//;
+ &stab;
+ }
+ print STDERR "$.\n" if $trace;
+ unlink $TMP if $TMP;
+
+ &compute_intrinsics if $perl && !$opt_i;
+
+ print STDERR "resolving types\n" if $trace;
+
+ &resolve_types;
+ &adjust_start_addrs;
+
+ $sum = 2 + $type_width + $member_width;
+ $pmask1 = "%-${type_width}s %-${member_width}s";
+ $pmask2 = "%-${sum}s %${offset_width}${offset_fmt}%s %${size_width}${size_fmt}%s";
+
+ if ($perl) {
+ # resolve template -- should be in stab define order, but even this isn't enough.
+ print STDERR "\nbuilding type templates: " if $trace;
+ for $i (reverse 0..$#type) {
+ next unless defined($name = $type[$i]);
+ next unless defined $struct{$name};
+ $build_recursed = 0;
+ &build_template($name) unless defined $template{&psou($name)} ||
+ $opt_s && !$interested{$name};
+ }
+ print STDERR "\n\n" if $trace;
+ }
+
+ print STDERR "dumping structs: " if $trace;
+
+
+ foreach $name (sort keys %struct) {
+ next if $opt_s && !$interested{$name};
+ print STDERR "$name " if $trace;
+
+ undef @sizeof;
+ undef @typedef;
+ undef @offsetof;
+ undef @indices;
+ undef @typeof;
+
+ $mname = &munge($name);
+
+ $fname = &psou($name);
+
+ print "# " if $perl && $verbose;
+ $pcode = '';
+ print "$fname {\n" if !$perl || $verbose;
+ $template{$fname} = &scrunch($template{$fname}) if $perl;
+ &pstruct($name,$name,0);
+ print "# " if $perl && $verbose;
+ print "}\n" if !$perl || $verbose;
+ print "\n" if $perl && $verbose;
+
+ if ($perl) {
+ print "$pcode";
+
+ printf("\nsub %-32s { %4d; }\n\n", "${mname}'struct", $countof{$name});
+
+ print <<EOF;
+sub ${mname}'typedef {
+ local(\$${mname}'index) = shift;
+ defined \$${mname}'index
+ ? \$${mname}'typedef[\$${mname}'index]
+ : \$${mname}'typedef;
+}
+EOF
+
+ print <<EOF;
+sub ${mname}'sizeof {
+ local(\$${mname}'index) = shift;
+ defined \$${mname}'index
+ ? \$${mname}'sizeof[\$${mname}'index]
+ : \$${mname}'sizeof;
+}
+EOF
+
+ print <<EOF;
+sub ${mname}'offsetof {
+ local(\$${mname}'index) = shift;
+ defined \$${mname}index
+ ? \$${mname}'offsetof[\$${mname}'index]
+ : \$${mname}'sizeof;
+}
+EOF
+
+ print <<EOF;
+sub ${mname}'typeof {
+ local(\$${mname}'index) = shift;
+ defined \$${mname}index
+ ? \$${mname}'typeof[\$${mname}'index]
+ : '$name';
+}
+EOF
+
+
+ print "\$${mname}'typedef = '" . &scrunch($template{$fname})
+ . "';\n";
+
+ print "\$${mname}'sizeof = $sizeof{$name};\n\n";
+
+
+ print "\@${mname}'indices = (", &squishseq(@indices), ");\n";
+
+ print "\n";
+
+ print "\@${mname}'typedef[\@${mname}'indices] = (",
+ join("\n\t", '', @typedef), "\n );\n\n";
+ print "\@${mname}'sizeof[\@${mname}'indices] = (",
+ join("\n\t", '', @sizeof), "\n );\n\n";
+ print "\@${mname}'offsetof[\@${mname}'indices] = (",
+ join("\n\t", '', @offsetof), "\n );\n\n";
+ print "\@${mname}'typeof[\@${mname}'indices] = (",
+ join("\n\t", '', @typeof), "\n );\n\n";
+
+ $template_printed{$fname}++;
+ $size_printed{$fname}++;
+ }
+ print "\n";
+ }
+
+ print STDERR "\n" if $trace;
+
+ unless ($perl && $opt_a) {
+ print "\n1;\n";
+ exit;
+ }
+
+
+
+ foreach $name (sort bysizevalue keys %intrinsics) {
+ next if $size_printed{$name};
+ print '$',&munge($name),"'sizeof = ", $sizeof{$name}, ";\n";
+ }
+
+ print "\n";
+
+ sub bysizevalue { $sizeof{$a} <=> $sizeof{$b}; }
+
+
+ foreach $name (sort keys %intrinsics) {
+ print '$',&munge($name),"'typedef = '", $template{$name}, "';\n";
+ }
+
+ print "\n1;\n";
+
+ exit;
+}
+
+########################################################################################
+
+
+sub stab {
+ next unless /:[\$\w]+(\(\d+,\d+\))?=[\*\$\w]+/; # (\d+,\d+) is for sun
+ s/"// || next;
+ s/",([x\d]+),([x\d]+),([x\d]+),.*// || next;
+
+ next if /^\s*$/;
+
+ $size = $3 if $3;
+
+
+ $line = $_;
+
+ if (($name, $pdecl) = /^([\$ \w]+):[tT]((\d+)(=[rufs*](\d+))+)$/) {
+ print "$name is a typedef for some funky pointers: $pdecl\n" if $debug;
+ &pdecl($pdecl);
+ next;
+ }
+
+
+
+ if (/(([ \w]+):t(\d+|\(\d+,\d+\)))=r?(\d+|\(\d+,\d+\))(;\d+;\d+;)?/) {
+ local($ident) = $2;
+ push(@intrinsics, $ident);
+ $typeno = &typeno($3);
+ $type[$typeno] = $ident;
+ print STDERR "intrinsic $ident in new type $typeno\n" if $debug;
+ next;
+ }
+
+ if (($name, $typeordef, $typeno, $extra, $struct, $_)
+ = /^([\$ \w]+):([ustT])(\d+|\(\d+,\d+\))(=[rufs*](\d+))?(.*)$/)
+ {
+ $typeno = &typeno($typeno); # sun foolery
+ }
+ elsif (/^[\$\w]+:/) {
+ next; # variable
+ }
+ else {
+ warn "can't grok stab: <$_> in: $line " if $_;
+ next;
+ }
+
+ #warn "got size $size for $name\n";
+ $sizeof{$name} = $size if $size;
+
+ s/;[-\d]*;[-\d]*;$//; # we don't care about ranges
+
+ $typenos{$name} = $typeno;
+
+ unless (defined $type[$typeno]) {
+ &panic("type 0??") unless $typeno;
+ $type[$typeno] = $name unless defined $type[$typeno];
+ printf "new type $typeno is $name" if $debug;
+ if ($extra =~ /\*/ && defined $type[$struct]) {
+ print ", a typedef for a pointer to " , $type[$struct] if $debug;
+ }
+ } else {
+ printf "%s is type %d", $name, $typeno if $debug;
+ print ", a typedef for " , $type[$typeno] if $debug;
+ }
+ print "\n" if $debug;
+ #next unless $extra =~ /[su*]/;
+
+ #$type[$struct] = $name;
+
+ if ($extra =~ /[us*]/) {
+ &sou($name, $extra);
+ $_ = &sdecl($name, $_, 0);
+ }
+ elsif (/^=ar/) {
+ print "it's a bare array typedef -- that's pretty sick\n" if $debug;
+ $_ = "$typeno$_";
+ $scripts = '';
+ $_ = &adecl($_,1);
+
+ }
+ elsif (s/((\w+):t(\d+|\(\d+,\d+\)))?=r?(;\d+;\d+;)?//) { # the ?'s are for gcc
+ push(@intrinsics, $2);
+ $typeno = &typeno($3);
+ $type[$typeno] = $2;
+ print STDERR "intrinsic $2 in new type $typeno\n" if $debug;
+ }
+ elsif (s/^=e//) { # blessed by thy compiler; mine won't do this
+ &edecl;
+ }
+ else {
+ warn "Funny remainder for $name on line $_ left in $line " if $_;
+ }
+}
+
+sub typeno { # sun thinks types are (0,27) instead of just 27
+ local($_) = @_;
+ s/\(\d+,(\d+)\)/$1/;
+ $_;
+}
+
+sub pstruct {
+ local($what,$prefix,$base) = @_;
+ local($field, $fieldname, $typeno, $count, $offset, $entry);
+ local($fieldtype);
+ local($type, $tname);
+ local($mytype, $mycount, $entry2);
+ local($struct_count) = 0;
+ local($pad, $revpad, $length, $prepad, $lastoffset, $lastlength, $fmt);
+ local($bits,$bytes);
+ local($template);
+
+
+ local($mname) = &munge($name);
+
+ sub munge {
+ local($_) = @_;
+ s/[\s\$\.]/_/g;
+ $_;
+ }
+
+ local($sname) = &psou($what);
+
+ $nesting++;
+
+ for $field (split(/;/, $struct{$what})) {
+ $pad = $prepad = 0;
+ $entry = '';
+ ($fieldname, $typeno, $count, $offset, $length) = split(/,/, $field);
+
+ $type = $type[$typeno];
+
+ $type =~ /([^[]*)(\[.*\])?/;
+ $mytype = $1;
+ $count .= $2;
+ $fieldtype = &psou($mytype);
+
+ local($fname) = &psou($name);
+
+ if ($build_templates) {
+
+ $pad = ($offset - ($lastoffset + $lastlength))/8
+ if defined $lastoffset;
+
+ if (! $finished_template{$sname}) {
+ if ($isaunion{$what}) {
+ $template{$sname} .= 'X' x $revpad . ' ' if $revpad;
+ } else {
+ $template{$sname} .= 'x' x $pad . ' ' if $pad;
+ }
+ }
+
+ $template = &fetch_template($type) x
+ ($count ? &scripts2count($count) : 1);
+
+ if (! $finished_template{$sname}) {
+ $template{$sname} .= $template;
+ }
+
+ $revpad = $length/8 if $isaunion{$what};
+
+ ($lastoffset, $lastlength) = ($offset, $length);
+
+ } else {
+ print '# ' if $perl && $verbose;
+ $entry = sprintf($pmask1,
+ ' ' x ($nesting * $indent) . $fieldtype,
+ "$prefix.$fieldname" . $count);
+
+ $entry =~ s/(\*+)( )/$2$1/;
+
+ printf $pmask2,
+ $entry,
+ ($base+$offset)/8,
+ ($bits = ($base+$offset)%8) ? ".$bits" : " ",
+ $length/8,
+ ($bits = $length % 8) ? ".$bits": ""
+ if !$perl || $verbose;
+
+
+ if ($perl && $nesting == 1) {
+ $template = &scrunch(&fetch_template($type) x
+ ($count ? &scripts2count($count) : 1));
+ push(@sizeof, int($length/8) .",\t# $fieldname");
+ push(@offsetof, int($offset/8) .",\t# $fieldname");
+ push(@typedef, "'$template', \t# $fieldname");
+ $type =~ s/(struct|union) //;
+ push(@typeof, "'$type" . ($count ? $count : '') .
+ "',\t# $fieldname");
+ }
+
+ print ' ', ' ' x $indent x $nesting, $template
+ if $perl && $verbose;
+
+ print "\n" if !$perl || $verbose;
+
+ }
+ if ($perl) {
+ local($mycount) = defined $struct{$mytype} ? $countof{$mytype} : 1;
+ $mycount *= &scripts2count($count) if $count;
+ if ($nesting==1 && !$build_templates) {
+ $pcode .= sprintf("sub %-32s { %4d; }\n",
+ "${mname}'${fieldname}", $struct_count);
+ push(@indices, $struct_count);
+ }
+ $struct_count += $mycount;
+ }
+
+
+ &pstruct($type, "$prefix.$fieldname", $base+$offset)
+ if $recurse && defined $struct{$type};
+ }
+
+ $countof{$what} = $struct_count unless defined $countof{$whati};
+
+ $template{$sname} .= '$' if $build_templates;
+ $finished_template{$sname}++;
+
+ if ($build_templates && !defined $sizeof{$name}) {
+ local($fmt) = &scrunch($template{$sname});
+ print STDERR "no size for $name, punting with $fmt..." if $debug;
+ eval '$sizeof{$name} = length(pack($fmt, ()))';
+ if ($@) {
+ chop $@;
+ warn "couldn't get size for \$name: $@";
+ } else {
+ print STDERR $sizeof{$name}, "\n" if $debUg;
+ }
+ }
+
+ --$nesting;
+}
+
+
+sub psize {
+ local($me) = @_;
+ local($amstruct) = $struct{$me} ? 'struct ' : '';
+
+ print '$sizeof{\'', $amstruct, $me, '\'} = ';
+ printf "%d;\n", $sizeof{$me};
+}
+
+sub pdecl {
+ local($pdecl) = @_;
+ local(@pdecls);
+ local($tname);
+
+ warn "pdecl: $pdecl\n" if $debug;
+
+ $pdecl =~ s/\(\d+,(\d+)\)/$1/g;
+ $pdecl =~ s/\*//g;
+ @pdecls = split(/=/, $pdecl);
+ $typeno = $pdecls[0];
+ $tname = pop @pdecls;
+
+ if ($tname =~ s/^f//) { $tname = "$tname&"; }
+ #else { $tname = "$tname*"; }
+
+ for (reverse @pdecls) {
+ $tname .= s/^f// ? "&" : "*";
+ #$tname =~ s/^f(.*)/$1&/;
+ print "type[$_] is $tname\n" if $debug;
+ $type[$_] = $tname unless defined $type[$_];
+ }
+}
+
+
+
+sub adecl {
+ ($arraytype, $unknown, $lower, $upper) = ();
+ #local($typeno);
+ # global $typeno, @type
+ local($_, $typedef) = @_;
+
+ while (s/^((\d+)=)?ar(\d+);//) {
+ ($arraytype, $unknown) = ($2, $3);
+ if (s/^(\d+);(\d+);//) {
+ ($lower, $upper) = ($1, $2);
+ $scripts .= '[' . ($upper+1) . ']';
+ } else {
+ warn "can't find array bounds: $_";
+ }
+ }
+ if (s/^([\d*f=]*),(\d+),(\d+);//) {
+ ($start, $length) = ($2, $3);
+ local($whatis) = $1;
+ if ($whatis =~ /^(\d+)=/) {
+ $typeno = $1;
+ &pdecl($whatis);
+ } else {
+ $typeno = $whatis;
+ }
+ } elsif (s/^(\d+)(=[*suf]\d*)//) {
+ local($whatis) = $2;
+
+ if ($whatis =~ /[f*]/) {
+ &pdecl($whatis);
+ } elsif ($whatis =~ /[su]/) { #
+ print "$prefix.$fieldname is an array$scripts anon structs; disgusting\n"
+ if $debug;
+ #$type[$typeno] = $name unless defined $type[$typeno];
+ ##printf "new type $typeno is $name" if $debug;
+ $typeno = $1;
+ $type[$typeno] = "$prefix.$fieldname";
+ local($name) = $type[$typeno];
+ &sou($name, $whatis);
+ $_ = &sdecl($name, $_, $start+$offset);
+ 1;
+ $start = $start{$name};
+ $offset = $sizeof{$name};
+ $length = $offset;
+ } else {
+ warn "what's this? $whatis in $line ";
+ }
+ } elsif (/^\d+$/) {
+ $typeno = $_;
+ } else {
+ warn "bad array stab: $_ in $line ";
+ next STAB;
+ }
+ #local($wasdef) = defined($type[$typeno]) && $debug;
+ #if ($typedef) {
+ #print "redefining $type[$typeno] to " if $wasdef;
+ #$type[$typeno] = "$whatis$scripts"; # unless defined $type[$typeno];
+ #print "$type[$typeno]\n" if $wasdef;
+ #} else {
+ #$type[$arraytype] = $type[$typeno] unless defined $type[$arraytype];
+ #}
+ $type[$arraytype] = "$type[$typeno]$scripts" if defined $type[$typeno];
+ print "type[$arraytype] is $type[$arraytype]\n" if $debug;
+ print "$prefix.$fieldname is an array of $type[$arraytype]\n" if $debug;
+ $_;
+}
+
+
+
+sub sdecl {
+ local($prefix, $_, $offset) = @_;
+
+ local($fieldname, $scripts, $type, $arraytype, $unknown,
+ $whatis, $pdecl, $upper,$lower, $start,$length) = ();
+ local($typeno,$sou);
+
+
+SFIELD:
+ while (/^([^;]+);/) {
+ $scripts = '';
+ warn "sdecl $_\n" if $debug;
+ if (s/^([\$\w]+)://) {
+ $fieldname = $1;
+ } elsif (s/(\d+)=([us])(\d+|\(\d+,\d+\))//) { #
+ $typeno = &typeno($1);
+ $type[$typeno] = "$prefix.$fieldname";
+ local($name) = "$prefix.$fieldname";
+ &sou($name,$2);
+ $_ = &sdecl("$prefix.$fieldname", $_, $start+$offset);
+ $start = $start{$name};
+ $offset += $sizeof{$name};
+ #print "done with anon, start is $start, offset is $offset\n";
+ #next SFIELD;
+ } else {
+ warn "weird field $_ of $line" if $debug;
+ next STAB;
+ #$fieldname = &gensym;
+ #$_ = &sdecl("$prefix.$fieldname", $_, $start+$offset);
+ }
+
+ if (/^\d+=ar/) {
+ $_ = &adecl($_);
+ }
+ elsif (s/^(\d+|\(\d+,\d+\))?,(\d+),(\d+);//) {
+ ($start, $length) = ($2, $3);
+ &panic("no length?") unless $length;
+ $typeno = &typeno($1) if $1;
+ }
+ elsif (s/^((\d+|\(\d+,\d+\))(=[*f](\d+|\(\d+,\d+\)))+),(\d+),(\d+);//) {
+ ($pdecl, $start, $length) = ($1,$5,$6);
+ &pdecl($pdecl);
+ }
+ elsif (s/(\d+)=([us])(\d+|\(\d+,\d+\))//) { # the dratted anon struct
+ ($typeno, $sou) = ($1, $2);
+ $typeno = &typeno($typeno);
+ if (defined($type[$typeno])) {
+ warn "now how did we get type $1 in $fieldname of $line?";
+ } else {
+ print "anon type $typeno is $prefix.$fieldname\n" if $debug;
+ $type[$typeno] = "$prefix.$fieldname" unless defined $type[$typeno];
+ };
+ local($name) = "$prefix.$fieldname";
+ &sou($name,$sou);
+ print "anon ".($isastruct{$name}) ? "struct":"union"." for $prefix.$fieldname\n" if $debug;
+ $type[$typeno] = "$prefix.$fieldname";
+ $_ = &sdecl("$prefix.$fieldname", $_, $start+$offset);
+ $start = $start{$name};
+ $length = $sizeof{$name};
+ }
+ else {
+ warn "can't grok stab for $name ($_) in line $line ";
+ next STAB;
+ }
+
+ &panic("no length for $prefix.$fieldname") unless $length;
+ $struct{$name} .= join(',', $fieldname, $typeno, $scripts, $start, $length) . ';';
+ }
+ if (s/;\d*,(\d+),(\d+);//) {
+ local($start, $size) = ($1, $2);
+ $sizeof{$prefix} = $size;
+ print "start of $prefix is $start, size of $sizeof{$prefix}\n" if $debug;
+ $start{$prefix} = $start;
+ }
+ $_;
+}
+
+sub edecl {
+ s/;$//;
+ $enum{$name} = $_;
+ $_ = '';
+}
+
+sub resolve_types {
+ local($sou);
+ for $i (0 .. $#type) {
+ next unless defined $type[$i];
+ $_ = $type[$i];
+ unless (/\d/) {
+ print "type[$i] $type[$i]\n" if $debug;
+ next;
+ }
+ print "type[$i] $_ ==> " if $debug;
+ s/^(\d+)(\**)\&\*(\**)/"$2($3".&type($1) . ')()'/e;
+ s/^(\d+)\&/&type($1)/e;
+ s/^(\d+)/&type($1)/e;
+ s/(\*+)([^*]+)(\*+)/$1$3$2/;
+ s/\((\*+)(\w+)(\*+)\)/$3($1$2)/;
+ s/^(\d+)([\*\[].*)/&type($1).$2/e;
+ #s/(\d+)(\*|(\[[\[\]\d\*]+]\])+)/&type($1).$2/ge;
+ $type[$i] = $_;
+ print "$_\n" if $debug;
+ }
+}
+sub type { &psou($type[$_[0]] || "<UNDEFINED>"); }
+
+sub adjust_start_addrs {
+ for (sort keys %start) {
+ ($basename = $_) =~ s/\.[^.]+$//;
+ $start{$_} += $start{$basename};
+ print "start: $_ @ $start{$_}\n" if $debug;
+ }
+}
+
+sub sou {
+ local($what, $_) = @_;
+ /u/ && $isaunion{$what}++;
+ /s/ && $isastruct{$what}++;
+}
+
+sub psou {
+ local($what) = @_;
+ local($prefix) = '';
+ if ($isaunion{$what}) {
+ $prefix = 'union ';
+ } elsif ($isastruct{$what}) {
+ $prefix = 'struct ';
+ }
+ $prefix . $what;
+}
+
+sub scrunch {
+ local($_) = @_;
+
+ study;
+
+ s/\$//g;
+ s/ / /g;
+ 1 while s/(\w) \1/$1$1/g;
+
+ # i wanna say this, but perl resists my efforts:
+ # s/(\w)(\1+)/$2 . length($1)/ge;
+
+ &quick_scrunch;
+
+ s/ $//;
+
+ $_;
+}
+
+sub buildscrunchlist {
+ $scrunch_code = "sub quick_scrunch {\n";
+ for (values %intrinsics) {
+ $scrunch_code .= "\ts/($_{2,})/'$_' . length(\$1)/ge;\n";
+ }
+ $scrunch_code .= "}\n";
+ print "$scrunch_code" if $debug;
+ eval $scrunch_code;
+ &panic("can't eval scrunch_code $@ \nscrunch_code") if $@;
+}
+
+sub fetch_template {
+ local($mytype) = @_;
+ local($fmt);
+ local($count) = 1;
+
+ &panic("why do you care?") unless $perl;
+
+ if ($mytype =~ s/(\[\d+\])+$//) {
+ $count .= $1;
+ }
+
+ if ($mytype =~ /\*/) {
+ $fmt = $template{'pointer'};
+ }
+ elsif (defined $template{$mytype}) {
+ $fmt = $template{$mytype};
+ }
+ elsif (defined $struct{$mytype}) {
+ if (!defined $template{&psou($mytype)}) {
+ &build_template($mytype) unless $mytype eq $name;
+ }
+ elsif ($template{&psou($mytype)} !~ /\$$/) {
+ #warn "incomplete template for $mytype\n";
+ }
+ $fmt = $template{&psou($mytype)} || '?';
+ }
+ else {
+ warn "unknown fmt for $mytype\n";
+ $fmt = '?';
+ }
+
+ $fmt x $count . ' ';
+}
+
+sub compute_intrinsics {
+ local($TMP) = "/tmp/c2ph-i.$$.c";
+ open (TMP, ">$TMP") || die "can't open $TMP: $!";
+ select(TMP);
+
+ print STDERR "computing intrinsic sizes: " if $trace;
+
+ undef %intrinsics;
+
+ print <<'EOF';
+main() {
+ char *mask = "%d %s\n";
+EOF
+
+ for $type (@intrinsics) {
+ next if $type eq 'void';
+ print <<"EOF";
+ printf(mask,sizeof($type), "$type");
+EOF
+ }
+
+ print <<'EOF';
+ printf(mask,sizeof(char *), "pointer");
+ exit(0);
+}
+EOF
+ close TMP;
+
+ select(STDOUT);
+ open(PIPE, "cd /tmp && $CC $TMP && /tmp/a.out|");
+ while (<PIPE>) {
+ chop;
+ split(' ',$_,2);;
+ print "intrinsic $_[1] is size $_[0]\n" if $debug;
+ $sizeof{$_[1]} = $_[0];
+ $intrinsics{$_[1]} = $template{$_[0]};
+ }
+ close(PIPE) || die "couldn't read intrinsics!";
+ unlink($TMP, '/tmp/a.out');
+ print STDERR "done\n" if $trace;
+}
+
+sub scripts2count {
+ local($_) = @_;
+
+ s/^\[//;
+ s/\]$//;
+ s/\]\[/*/g;
+ $_ = eval;
+ &panic("$_: $@") if $@;
+ $_;
+}
+
+sub system {
+ print STDERR "@_\n" if $trace;
+ system @_;
+}
+
+sub build_template {
+ local($name) = @_;
+
+ &panic("already got a template for $name") if defined $template{$name};
+
+ local($build_templates) = 1;
+
+ local($lparen) = '(' x $build_recursed;
+ local($rparen) = ')' x $build_recursed;
+
+ print STDERR "$lparen$name$rparen " if $trace;
+ $build_recursed++;
+ &pstruct($name,$name,0);
+ print STDERR "TEMPLATE for $name is ", $template{&psou($name)}, "\n" if $debug;
+ --$build_recursed;
+}
+
+
+sub panic {
+
+ select(STDERR);
+
+ print "\npanic: @_\n";
+
+ exit 1 if $] <= 4.003; # caller broken
+
+ local($i,$_);
+ local($p,$f,$l,$s,$h,$a,@a,@sub);
+ for ($i = 0; ($p,$f,$l,$s,$h,$w) = caller($i); $i++) {
+ @a = @DB'args;
+ for (@a) {
+ if (/^StB\000/ && length($_) == length($_main{'_main'})) {
+ $_ = sprintf("%s",$_);
+ }
+ else {
+ s/'/\\'/g;
+ s/([^\0]*)/'$1'/ unless /^-?[\d.]+$/;
+ s/([\200-\377])/sprintf("M-%c",ord($1)&0177)/eg;
+ s/([\0-\37\177])/sprintf("^%c",ord($1)^64)/eg;
+ }
+ }
+ $w = $w ? '@ = ' : '$ = ';
+ $a = $h ? '(' . join(', ', @a) . ')' : '';
+ push(@sub, "$w&$s$a from file $f line $l\n");
+ last if $signal;
+ }
+ for ($i=0; $i <= $#sub; $i++) {
+ last if $signal;
+ print $sub[$i];
+ }
+ exit 1;
+}
+
+sub squishseq {
+ local($num);
+ local($last) = -1e8;
+ local($string);
+ local($seq) = '..';
+
+ while (defined($num = shift)) {
+ if ($num == ($last + 1)) {
+ $string .= $seq unless $inseq++;
+ $last = $num;
+ next;
+ } elsif ($inseq) {
+ $string .= $last unless $last == -1e8;
+ }
+
+ $string .= ',' if defined $string;
+ $string .= $num;
+ $last = $num;
+ $inseq = 0;
+ }
+ $string .= $last if $inseq && $last != -e18;
+ $string;
+}
diff --git a/gnu/usr.bin/perl/perl/EXTERN.h b/gnu/usr.bin/perl/perl/EXTERN.h
new file mode 100644
index 0000000..181d50d
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/EXTERN.h
@@ -0,0 +1,26 @@
+/* $RCSfile: EXTERN.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:33 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: EXTERN.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:33 nate
+ * PERL!
+ *
+ * Revision 4.0.1.1 91/06/07 10:10:32 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 00:58:26 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#undef EXT
+#define EXT extern
+
+#undef INIT
+#define INIT(x)
+
+#undef DOINIT
diff --git a/gnu/usr.bin/perl/perl/INTERN.h b/gnu/usr.bin/perl/perl/INTERN.h
new file mode 100644
index 0000000..21c9026
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/INTERN.h
@@ -0,0 +1,26 @@
+/* $RCSfile: INTERN.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:33 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: INTERN.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:33 nate
+ * PERL!
+ *
+ * Revision 4.0.1.1 91/06/07 10:10:42 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 00:58:35 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#undef EXT
+#define EXT
+
+#undef INIT
+#define INIT(x) = x
+
+#define DOINIT
diff --git a/gnu/usr.bin/perl/perl/Makefile b/gnu/usr.bin/perl/perl/Makefile
new file mode 100644
index 0000000..3468e22
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/Makefile
@@ -0,0 +1,17 @@
+#
+#
+
+PROG= perl
+
+SRCS+= array.c cmd.c cons.c consarg.c doarg.c doio.c dolist.c dump.c
+SRCS+= eval.c form.c hash.c malloc.c perl.c perly.c regcomp.c regexec.c
+SRCS+= stab.c str.c toke.c util.c usersub.c
+CFLAGS+= -I${.CURDIR}
+LDADD= -lm
+DPADD= ${LIBM}
+
+DPADD+= ${LIBCRYPT}
+LDADD+= -lcrypt
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/perl/perl/arg.h b/gnu/usr.bin/perl/perl/arg.h
new file mode 100644
index 0000000..813ce3a
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/arg.h
@@ -0,0 +1,997 @@
+/* $RCSfile: arg.h,v $$Revision: 1.1.1.1 $$Date: 1994/09/10 06:27:34 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: arg.h,v $
+ * Revision 1.1.1.1 1994/09/10 06:27:34 gclarkii
+ * Initial import of Perl 4.046 bmaked
+ *
+ * Revision 1.1.1.1 1993/08/23 21:29:34 nate
+ * PERL!
+ *
+ * Revision 4.0.1.3 92/06/08 11:44:06 lwall
+ * patch20: O_PIPE conflicted with Atari
+ * patch20: clarified debugging output for literals and double-quoted strings
+ *
+ * Revision 4.0.1.2 91/11/05 15:51:05 lwall
+ * patch11: added eval {}
+ * patch11: added sort {} LIST
+ *
+ * Revision 4.0.1.1 91/06/07 10:18:30 lwall
+ * patch4: length($`), length($&), length($') now optimized to avoid string copy
+ * patch4: new copyright notice
+ * patch4: many, many itty-bitty portability fixes
+ *
+ * Revision 4.0 91/03/20 01:03:09 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#define O_NULL 0
+#define O_RCAT 1
+#define O_ITEM 2
+#define O_SCALAR 3
+#define O_ITEM2 4
+#define O_ITEM3 5
+#define O_CONCAT 6
+#define O_REPEAT 7
+#define O_MATCH 8
+#define O_NMATCH 9
+#define O_SUBST 10
+#define O_NSUBST 11
+#define O_ASSIGN 12
+#define O_LOCAL 13
+#define O_AASSIGN 14
+#define O_SASSIGN 15
+#define O_CHOP 16
+#define O_DEFINED 17
+#define O_UNDEF 18
+#define O_STUDY 19
+#define O_POW 20
+#define O_MULTIPLY 21
+#define O_DIVIDE 22
+#define O_MODULO 23
+#define O_ADD 24
+#define O_SUBTRACT 25
+#define O_LEFT_SHIFT 26
+#define O_RIGHT_SHIFT 27
+#define O_LT 28
+#define O_GT 29
+#define O_LE 30
+#define O_GE 31
+#define O_EQ 32
+#define O_NE 33
+#define O_NCMP 34
+#define O_BIT_AND 35
+#define O_XOR 36
+#define O_BIT_OR 37
+#define O_AND 38
+#define O_OR 39
+#define O_COND_EXPR 40
+#define O_COMMA 41
+#define O_NEGATE 42
+#define O_NOT 43
+#define O_COMPLEMENT 44
+#define O_SELECT 45
+#define O_WRITE 46
+#define O_DBMOPEN 47
+#define O_DBMCLOSE 48
+#define O_OPEN 49
+#define O_TRANS 50
+#define O_NTRANS 51
+#define O_CLOSE 52
+#define O_EACH 53
+#define O_VALUES 54
+#define O_KEYS 55
+#define O_LARRAY 56
+#define O_ARRAY 57
+#define O_AELEM 58
+#define O_DELETE 59
+#define O_LHASH 60
+#define O_HASH 61
+#define O_HELEM 62
+#define O_LAELEM 63
+#define O_LHELEM 64
+#define O_LSLICE 65
+#define O_ASLICE 66
+#define O_HSLICE 67
+#define O_LASLICE 68
+#define O_LHSLICE 69
+#define O_SPLICE 70
+#define O_PUSH 71
+#define O_POP 72
+#define O_SHIFT 73
+#define O_UNPACK 74
+#define O_SPLIT 75
+#define O_LENGTH 76
+#define O_SPRINTF 77
+#define O_SUBSTR 78
+#define O_PACK 79
+#define O_GREP 80
+#define O_JOIN 81
+#define O_SLT 82
+#define O_SGT 83
+#define O_SLE 84
+#define O_SGE 85
+#define O_SEQ 86
+#define O_SNE 87
+#define O_SCMP 88
+#define O_SUBR 89
+#define O_DBSUBR 90
+#define O_CALLER 91
+#define O_SORT 92
+#define O_REVERSE 93
+#define O_WARN 94
+#define O_DIE 95
+#define O_PRTF 96
+#define O_PRINT 97
+#define O_CHDIR 98
+#define O_EXIT 99
+#define O_RESET 100
+#define O_LIST 101
+#define O_EOF 102
+#define O_GETC 103
+#define O_TELL 104
+#define O_RECV 105
+#define O_READ 106
+#define O_SYSREAD 107
+#define O_SYSWRITE 108
+#define O_SEND 109
+#define O_SEEK 110
+#define O_RETURN 111
+#define O_REDO 112
+#define O_NEXT 113
+#define O_LAST 114
+#define O_DUMP 115
+#define O_GOTO 116
+#define O_INDEX 117
+#define O_RINDEX 118
+#define O_TIME 119
+#define O_TMS 120
+#define O_LOCALTIME 121
+#define O_GMTIME 122
+#define O_TRUNCATE 123
+#define O_LSTAT 124
+#define O_STAT 125
+#define O_CRYPT 126
+#define O_ATAN2 127
+#define O_SIN 128
+#define O_COS 129
+#define O_RAND 130
+#define O_SRAND 131
+#define O_EXP 132
+#define O_LOG 133
+#define O_SQRT 134
+#define O_INT 135
+#define O_ORD 136
+#define O_ALARM 137
+#define O_SLEEP 138
+#define O_RANGE 139
+#define O_F_OR_R 140
+#define O_FLIP 141
+#define O_FLOP 142
+#define O_FORK 143
+#define O_WAIT 144
+#define O_WAITPID 145
+#define O_SYSTEM 146
+#define O_EXEC_OP 147
+#define O_HEX 148
+#define O_OCT 149
+#define O_CHOWN 150
+#define O_KILL 151
+#define O_UNLINK 152
+#define O_CHMOD 153
+#define O_UTIME 154
+#define O_UMASK 155
+#define O_MSGGET 156
+#define O_SHMGET 157
+#define O_SEMGET 158
+#define O_MSGCTL 159
+#define O_SHMCTL 160
+#define O_SEMCTL 161
+#define O_MSGSND 162
+#define O_MSGRCV 163
+#define O_SEMOP 164
+#define O_SHMREAD 165
+#define O_SHMWRITE 166
+#define O_RENAME 167
+#define O_LINK 168
+#define O_MKDIR 169
+#define O_RMDIR 170
+#define O_GETPPID 171
+#define O_GETPGRP 172
+#define O_SETPGRP 173
+#define O_GETPRIORITY 174
+#define O_SETPRIORITY 175
+#define O_CHROOT 176
+#define O_FCNTL 177
+#define O_IOCTL 178
+#define O_FLOCK 179
+#define O_UNSHIFT 180
+#define O_REQUIRE 181
+#define O_DOFILE 182
+#define O_EVAL 183
+#define O_FTRREAD 184
+#define O_FTRWRITE 185
+#define O_FTREXEC 186
+#define O_FTEREAD 187
+#define O_FTEWRITE 188
+#define O_FTEEXEC 189
+#define O_FTIS 190
+#define O_FTEOWNED 191
+#define O_FTROWNED 192
+#define O_FTZERO 193
+#define O_FTSIZE 194
+#define O_FTMTIME 195
+#define O_FTATIME 196
+#define O_FTCTIME 197
+#define O_FTSOCK 198
+#define O_FTCHR 199
+#define O_FTBLK 200
+#define O_FTFILE 201
+#define O_FTDIR 202
+#define O_FTPIPE 203
+#define O_FTLINK 204
+#define O_SYMLINK 205
+#define O_READLINK 206
+#define O_FTSUID 207
+#define O_FTSGID 208
+#define O_FTSVTX 209
+#define O_FTTTY 210
+#define O_FTTEXT 211
+#define O_FTBINARY 212
+#define O_SOCKET 213
+#define O_BIND 214
+#define O_CONNECT 215
+#define O_LISTEN 216
+#define O_ACCEPT 217
+#define O_GHBYNAME 218
+#define O_GHBYADDR 219
+#define O_GHOSTENT 220
+#define O_GNBYNAME 221
+#define O_GNBYADDR 222
+#define O_GNETENT 223
+#define O_GPBYNAME 224
+#define O_GPBYNUMBER 225
+#define O_GPROTOENT 226
+#define O_GSBYNAME 227
+#define O_GSBYPORT 228
+#define O_GSERVENT 229
+#define O_SHOSTENT 230
+#define O_SNETENT 231
+#define O_SPROTOENT 232
+#define O_SSERVENT 233
+#define O_EHOSTENT 234
+#define O_ENETENT 235
+#define O_EPROTOENT 236
+#define O_ESERVENT 237
+#define O_SOCKPAIR 238
+#define O_SHUTDOWN 239
+#define O_GSOCKOPT 240
+#define O_SSOCKOPT 241
+#define O_GETSOCKNAME 242
+#define O_GETPEERNAME 243
+#define O_SSELECT 244
+#define O_FILENO 245
+#define O_BINMODE 246
+#define O_VEC 247
+#define O_GPWNAM 248
+#define O_GPWUID 249
+#define O_GPWENT 250
+#define O_SPWENT 251
+#define O_EPWENT 252
+#define O_GGRNAM 253
+#define O_GGRGID 254
+#define O_GGRENT 255
+#define O_SGRENT 256
+#define O_EGRENT 257
+#define O_GETLOGIN 258
+#define O_OPEN_DIR 259
+#define O_READDIR 260
+#define O_TELLDIR 261
+#define O_SEEKDIR 262
+#define O_REWINDDIR 263
+#define O_CLOSEDIR 264
+#define O_SYSCALL 265
+#define O_PIPE_OP 266
+#define O_TRY 267
+#define O_EVALONCE 268
+#define MAXO 269
+
+#ifndef DOINIT
+extern char *opname[];
+#else
+char *opname[] = {
+ "NULL",
+ "RCAT",
+ "ITEM",
+ "SCALAR",
+ "ITEM2",
+ "ITEM3",
+ "CONCAT",
+ "REPEAT",
+ "MATCH",
+ "NMATCH",
+ "SUBST",
+ "NSUBST",
+ "ASSIGN",
+ "LOCAL",
+ "AASSIGN",
+ "SASSIGN",
+ "CHOP",
+ "DEFINED",
+ "UNDEF",
+ "STUDY",
+ "POW",
+ "MULTIPLY",
+ "DIVIDE",
+ "MODULO",
+ "ADD",
+ "SUBTRACT",
+ "LEFT_SHIFT",
+ "RIGHT_SHIFT",
+ "LT",
+ "GT",
+ "LE",
+ "GE",
+ "EQ",
+ "NE",
+ "NCMP",
+ "BIT_AND",
+ "XOR",
+ "BIT_OR",
+ "AND",
+ "OR",
+ "COND_EXPR",
+ "COMMA",
+ "NEGATE",
+ "NOT",
+ "COMPLEMENT",
+ "SELECT",
+ "WRITE",
+ "DBMOPEN",
+ "DBMCLOSE",
+ "OPEN",
+ "TRANS",
+ "NTRANS",
+ "CLOSE",
+ "EACH",
+ "VALUES",
+ "KEYS",
+ "LARRAY",
+ "ARRAY",
+ "AELEM",
+ "DELETE",
+ "LHASH",
+ "HASH",
+ "HELEM",
+ "LAELEM",
+ "LHELEM",
+ "LSLICE",
+ "ASLICE",
+ "HSLICE",
+ "LASLICE",
+ "LHSLICE",
+ "SPLICE",
+ "PUSH",
+ "POP",
+ "SHIFT",
+ "UNPACK",
+ "SPLIT",
+ "LENGTH",
+ "SPRINTF",
+ "SUBSTR",
+ "PACK",
+ "GREP",
+ "JOIN",
+ "SLT",
+ "SGT",
+ "SLE",
+ "SGE",
+ "SEQ",
+ "SNE",
+ "SCMP",
+ "SUBR",
+ "DBSUBR",
+ "CALLER",
+ "SORT",
+ "REVERSE",
+ "WARN",
+ "DIE",
+ "PRINTF",
+ "PRINT",
+ "CHDIR",
+ "EXIT",
+ "RESET",
+ "LIST",
+ "EOF",
+ "GETC",
+ "TELL",
+ "RECV",
+ "READ",
+ "SYSREAD",
+ "SYSWRITE",
+ "SEND",
+ "SEEK",
+ "RETURN",
+ "REDO",
+ "NEXT",
+ "LAST",
+ "DUMP",
+ "GOTO",/* shudder */
+ "INDEX",
+ "RINDEX",
+ "TIME",
+ "TIMES",
+ "LOCALTIME",
+ "GMTIME",
+ "TRUNCATE",
+ "LSTAT",
+ "STAT",
+ "CRYPT",
+ "ATAN2",
+ "SIN",
+ "COS",
+ "RAND",
+ "SRAND",
+ "EXP",
+ "LOG",
+ "SQRT",
+ "INT",
+ "ORD",
+ "ALARM",
+ "SLEEP",
+ "RANGE",
+ "FLIP_OR_RANGE",
+ "FLIP",
+ "FLOP",
+ "FORK",
+ "WAIT",
+ "WAITPID",
+ "SYSTEM",
+ "EXEC",
+ "HEX",
+ "OCT",
+ "CHOWN",
+ "KILL",
+ "UNLINK",
+ "CHMOD",
+ "UTIME",
+ "UMASK",
+ "MSGGET",
+ "SHMGET",
+ "SEMGET",
+ "MSGCTL",
+ "SHMCTL",
+ "SEMCTL",
+ "MSGSND",
+ "MSGRCV",
+ "SEMOP",
+ "SHMREAD",
+ "SHMWRITE",
+ "RENAME",
+ "LINK",
+ "MKDIR",
+ "RMDIR",
+ "GETPPID",
+ "GETPGRP",
+ "SETPGRP",
+ "GETPRIORITY",
+ "SETPRIORITY",
+ "CHROOT",
+ "FCNTL",
+ "SYSIOCTL",
+ "FLOCK",
+ "UNSHIFT",
+ "REQUIRE",
+ "DOFILE",
+ "EVAL",
+ "FTRREAD",
+ "FTRWRITE",
+ "FTREXEC",
+ "FTEREAD",
+ "FTEWRITE",
+ "FTEEXEC",
+ "FTIS",
+ "FTEOWNED",
+ "FTROWNED",
+ "FTZERO",
+ "FTSIZE",
+ "FTMTIME",
+ "FTATIME",
+ "FTCTIME",
+ "FTSOCK",
+ "FTCHR",
+ "FTBLK",
+ "FTFILE",
+ "FTDIR",
+ "FTPIPE",
+ "FTLINK",
+ "SYMLINK",
+ "READLINK",
+ "FTSUID",
+ "FTSGID",
+ "FTSVTX",
+ "FTTTY",
+ "FTTEXT",
+ "FTBINARY",
+ "SOCKET",
+ "BIND",
+ "CONNECT",
+ "LISTEN",
+ "ACCEPT",
+ "GHBYNAME",
+ "GHBYADDR",
+ "GHOSTENT",
+ "GNBYNAME",
+ "GNBYADDR",
+ "GNETENT",
+ "GPBYNAME",
+ "GPBYNUMBER",
+ "GPROTOENT",
+ "GSBYNAME",
+ "GSBYPORT",
+ "GSERVENT",
+ "SHOSTENT",
+ "SNETENT",
+ "SPROTOENT",
+ "SSERVENT",
+ "EHOSTENT",
+ "ENETENT",
+ "EPROTOENT",
+ "ESERVENT",
+ "SOCKPAIR",
+ "SHUTDOWN",
+ "GSOCKOPT",
+ "SSOCKOPT",
+ "GETSOCKNAME",
+ "GETPEERNAME",
+ "SSELECT",
+ "FILENO",
+ "BINMODE",
+ "VEC",
+ "GPWNAM",
+ "GPWUID",
+ "GPWENT",
+ "SPWENT",
+ "EPWENT",
+ "GGRNAM",
+ "GGRGID",
+ "GGRENT",
+ "SGRENT",
+ "EGRENT",
+ "GETLOGIN",
+ "OPENDIR",
+ "READDIR",
+ "TELLDIR",
+ "SEEKDIR",
+ "REWINDDIR",
+ "CLOSEDIR",
+ "SYSCALL",
+ "PIPE",
+ "TRY",
+ "EVALONCE",
+ "269"
+};
+#endif
+
+#define A_NULL 0
+#define A_EXPR 1
+#define A_CMD 2
+#define A_STAB 3
+#define A_LVAL 4
+#define A_SINGLE 5
+#define A_DOUBLE 6
+#define A_BACKTICK 7
+#define A_READ 8
+#define A_SPAT 9
+#define A_LEXPR 10
+#define A_ARYLEN 11
+#define A_ARYSTAB 12
+#define A_LARYLEN 13
+#define A_GLOB 14
+#define A_WORD 15
+#define A_INDREAD 16
+#define A_LARYSTAB 17
+#define A_STAR 18
+#define A_LSTAR 19
+#define A_WANTARRAY 20
+#define A_LENSTAB 21
+
+#define A_MASK 31
+#define A_DONT 32 /* or this into type to suppress evaluation */
+
+#ifndef DOINIT
+extern char *argname[];
+#else
+char *argname[] = {
+ "A_NULL",
+ "EXPR",
+ "CMD",
+ "STAB",
+ "LVAL",
+ "LITERAL",
+ "DOUBLEQUOTE",
+ "BACKTICK",
+ "READ",
+ "SPAT",
+ "LEXPR",
+ "ARYLEN",
+ "ARYSTAB",
+ "LARYLEN",
+ "GLOB",
+ "WORD",
+ "INDREAD",
+ "LARYSTAB",
+ "STAR",
+ "LSTAR",
+ "WANTARRAY",
+ "LENSTAB",
+ "22"
+};
+#endif
+
+#ifndef DOINIT
+extern bool hoistable[];
+#else
+bool hoistable[] =
+ {0, /* A_NULL */
+ 0, /* EXPR */
+ 1, /* CMD */
+ 1, /* STAB */
+ 0, /* LVAL */
+ 1, /* SINGLE */
+ 0, /* DOUBLE */
+ 0, /* BACKTICK */
+ 0, /* READ */
+ 0, /* SPAT */
+ 0, /* LEXPR */
+ 1, /* ARYLEN */
+ 1, /* ARYSTAB */
+ 0, /* LARYLEN */
+ 0, /* GLOB */
+ 1, /* WORD */
+ 0, /* INDREAD */
+ 0, /* LARYSTAB */
+ 1, /* STAR */
+ 1, /* LSTAR */
+ 1, /* WANTARRAY */
+ 0, /* LENSTAB */
+ 0, /* 21 */
+};
+#endif
+
+union argptr {
+ ARG *arg_arg;
+ char *arg_cval;
+ STAB *arg_stab;
+ SPAT *arg_spat;
+ CMD *arg_cmd;
+ STR *arg_str;
+ HASH *arg_hash;
+};
+
+struct arg {
+ union argptr arg_ptr;
+ short arg_len;
+ unsigned short arg_type;
+ unsigned short arg_flags;
+};
+
+#define AF_ARYOK 1 /* op can handle multiple values here */
+#define AF_POST 2 /* post *crement this item */
+#define AF_PRE 4 /* pre *crement this item */
+#define AF_UP 8 /* increment rather than decrement */
+#define AF_COMMON 16 /* left and right have symbols in common */
+#define AF_DEPR 32 /* an older form of the construct */
+#define AF_LISTISH 64 /* turn into list if important */
+#define AF_LOCAL_XX 128 /* list of local variables */
+
+/*
+ * Most of the ARG pointers are used as pointers to arrays of ARG. When
+ * so used, the 0th element is special, and represents the operator to
+ * use on the list of arguments following. The arg_len in the 0th element
+ * gives the maximum argument number, and the arg_str is used to store
+ * the return value in a more-or-less static location. Sorry it's not
+ * re-entrant (yet), but it sure makes it efficient. The arg_type of the
+ * 0th element is an operator (O_*) rather than an argument type (A_*).
+ */
+
+#define Nullarg Null(ARG*)
+
+#ifndef DOINIT
+EXT unsigned short opargs[MAXO+1];
+#else
+#define A(e1,e2,e3) (e1+(e2<<2)+(e3<<4))
+#define A5(e1,e2,e3,e4,e5) (e1+(e2<<2)+(e3<<4)+(e4<<6)+(e5<<8))
+unsigned short opargs[MAXO+1] = {
+ A(0,0,0), /* NULL */
+ A(1,1,0), /* RCAT */
+ A(1,0,0), /* ITEM */
+ A(1,0,0), /* SCALAR */
+ A(0,0,0), /* ITEM2 */
+ A(0,0,0), /* ITEM3 */
+ A(1,1,0), /* CONCAT */
+ A(3,1,0), /* REPEAT */
+ A(1,0,0), /* MATCH */
+ A(1,0,0), /* NMATCH */
+ A(1,0,0), /* SUBST */
+ A(1,0,0), /* NSUBST */
+ A(1,1,0), /* ASSIGN */
+ A(1,0,0), /* LOCAL */
+ A(3,3,0), /* AASSIGN */
+ A(0,0,0), /* SASSIGN */
+ A(3,0,0), /* CHOP */
+ A(1,0,0), /* DEFINED */
+ A(1,0,0), /* UNDEF */
+ A(1,0,0), /* STUDY */
+ A(1,1,0), /* POW */
+ A(1,1,0), /* MULTIPLY */
+ A(1,1,0), /* DIVIDE */
+ A(1,1,0), /* MODULO */
+ A(1,1,0), /* ADD */
+ A(1,1,0), /* SUBTRACT */
+ A(1,1,0), /* LEFT_SHIFT */
+ A(1,1,0), /* RIGHT_SHIFT */
+ A(1,1,0), /* LT */
+ A(1,1,0), /* GT */
+ A(1,1,0), /* LE */
+ A(1,1,0), /* GE */
+ A(1,1,0), /* EQ */
+ A(1,1,0), /* NE */
+ A(1,1,0), /* NCMP */
+ A(1,1,0), /* BIT_AND */
+ A(1,1,0), /* XOR */
+ A(1,1,0), /* BIT_OR */
+ A(1,0,0), /* AND */
+ A(1,0,0), /* OR */
+ A(1,0,0), /* COND_EXPR */
+ A(1,1,0), /* COMMA */
+ A(1,0,0), /* NEGATE */
+ A(1,0,0), /* NOT */
+ A(1,0,0), /* COMPLEMENT */
+ A(1,0,0), /* SELECT */
+ A(1,0,0), /* WRITE */
+ A(1,1,1), /* DBMOPEN */
+ A(1,0,0), /* DBMCLOSE */
+ A(1,1,0), /* OPEN */
+ A(1,0,0), /* TRANS */
+ A(1,0,0), /* NTRANS */
+ A(1,0,0), /* CLOSE */
+ A(0,0,0), /* EACH */
+ A(0,0,0), /* VALUES */
+ A(0,0,0), /* KEYS */
+ A(0,0,0), /* LARRAY */
+ A(0,0,0), /* ARRAY */
+ A(0,1,0), /* AELEM */
+ A(0,1,0), /* DELETE */
+ A(0,0,0), /* LHASH */
+ A(0,0,0), /* HASH */
+ A(0,1,0), /* HELEM */
+ A(0,1,0), /* LAELEM */
+ A(0,1,0), /* LHELEM */
+ A(0,3,3), /* LSLICE */
+ A(0,3,0), /* ASLICE */
+ A(0,3,0), /* HSLICE */
+ A(0,3,0), /* LASLICE */
+ A(0,3,0), /* LHSLICE */
+ A(0,3,1), /* SPLICE */
+ A(0,3,0), /* PUSH */
+ A(0,0,0), /* POP */
+ A(0,0,0), /* SHIFT */
+ A(1,1,0), /* UNPACK */
+ A(1,0,1), /* SPLIT */
+ A(1,0,0), /* LENGTH */
+ A(3,0,0), /* SPRINTF */
+ A(1,1,1), /* SUBSTR */
+ A(1,3,0), /* PACK */
+ A(0,3,0), /* GREP */
+ A(1,3,0), /* JOIN */
+ A(1,1,0), /* SLT */
+ A(1,1,0), /* SGT */
+ A(1,1,0), /* SLE */
+ A(1,1,0), /* SGE */
+ A(1,1,0), /* SEQ */
+ A(1,1,0), /* SNE */
+ A(1,1,0), /* SCMP */
+ A(0,3,0), /* SUBR */
+ A(0,3,0), /* DBSUBR */
+ A(1,0,0), /* CALLER */
+ A(1,3,0), /* SORT */
+ A(0,3,0), /* REVERSE */
+ A(0,3,0), /* WARN */
+ A(0,3,0), /* DIE */
+ A(1,3,0), /* PRINTF */
+ A(1,3,0), /* PRINT */
+ A(1,0,0), /* CHDIR */
+ A(1,0,0), /* EXIT */
+ A(1,0,0), /* RESET */
+ A(3,0,0), /* LIST */
+ A(1,0,0), /* EOF */
+ A(1,0,0), /* GETC */
+ A(1,0,0), /* TELL */
+ A5(1,1,1,1,0), /* RECV */
+ A(1,1,3), /* READ */
+ A(1,1,3), /* SYSREAD */
+ A(1,1,3), /* SYSWRITE */
+ A(1,1,3), /* SEND */
+ A(1,1,1), /* SEEK */
+ A(0,3,0), /* RETURN */
+ A(0,0,0), /* REDO */
+ A(0,0,0), /* NEXT */
+ A(0,0,0), /* LAST */
+ A(0,0,0), /* DUMP */
+ A(0,0,0), /* GOTO */
+ A(1,1,1), /* INDEX */
+ A(1,1,1), /* RINDEX */
+ A(0,0,0), /* TIME */
+ A(0,0,0), /* TIMES */
+ A(1,0,0), /* LOCALTIME */
+ A(1,0,0), /* GMTIME */
+ A(1,1,0), /* TRUNCATE */
+ A(1,0,0), /* LSTAT */
+ A(1,0,0), /* STAT */
+ A(1,1,0), /* CRYPT */
+ A(1,1,0), /* ATAN2 */
+ A(1,0,0), /* SIN */
+ A(1,0,0), /* COS */
+ A(1,0,0), /* RAND */
+ A(1,0,0), /* SRAND */
+ A(1,0,0), /* EXP */
+ A(1,0,0), /* LOG */
+ A(1,0,0), /* SQRT */
+ A(1,0,0), /* INT */
+ A(1,0,0), /* ORD */
+ A(1,0,0), /* ALARM */
+ A(1,0,0), /* SLEEP */
+ A(1,1,0), /* RANGE */
+ A(1,0,0), /* F_OR_R */
+ A(1,0,0), /* FLIP */
+ A(0,1,0), /* FLOP */
+ A(0,0,0), /* FORK */
+ A(0,0,0), /* WAIT */
+ A(1,1,0), /* WAITPID */
+ A(1,3,0), /* SYSTEM */
+ A(1,3,0), /* EXEC */
+ A(1,0,0), /* HEX */
+ A(1,0,0), /* OCT */
+ A(0,3,0), /* CHOWN */
+ A(0,3,0), /* KILL */
+ A(0,3,0), /* UNLINK */
+ A(0,3,0), /* CHMOD */
+ A(0,3,0), /* UTIME */
+ A(1,0,0), /* UMASK */
+ A(1,1,0), /* MSGGET */
+ A(1,1,1), /* SHMGET */
+ A(1,1,1), /* SEMGET */
+ A(1,1,1), /* MSGCTL */
+ A(1,1,1), /* SHMCTL */
+ A5(1,1,1,1,0), /* SEMCTL */
+ A(1,1,1), /* MSGSND */
+ A5(1,1,1,1,1), /* MSGRCV */
+ A(1,1,1), /* SEMOP */
+ A5(1,1,1,1,0), /* SHMREAD */
+ A5(1,1,1,1,0), /* SHMWRITE */
+ A(1,1,0), /* RENAME */
+ A(1,1,0), /* LINK */
+ A(1,1,0), /* MKDIR */
+ A(1,0,0), /* RMDIR */
+ A(0,0,0), /* GETPPID */
+ A(1,0,0), /* GETPGRP */
+ A(1,1,0), /* SETPGRP */
+ A(1,1,0), /* GETPRIORITY */
+ A(1,1,1), /* SETPRIORITY */
+ A(1,0,0), /* CHROOT */
+ A(1,1,1), /* FCNTL */
+ A(1,1,1), /* SYSIOCTL */
+ A(1,1,0), /* FLOCK */
+ A(0,3,0), /* UNSHIFT */
+ A(1,0,0), /* REQUIRE */
+ A(1,0,0), /* DOFILE */
+ A(1,0,0), /* EVAL */
+ A(1,0,0), /* FTRREAD */
+ A(1,0,0), /* FTRWRITE */
+ A(1,0,0), /* FTREXEC */
+ A(1,0,0), /* FTEREAD */
+ A(1,0,0), /* FTEWRITE */
+ A(1,0,0), /* FTEEXEC */
+ A(1,0,0), /* FTIS */
+ A(1,0,0), /* FTEOWNED */
+ A(1,0,0), /* FTROWNED */
+ A(1,0,0), /* FTZERO */
+ A(1,0,0), /* FTSIZE */
+ A(1,0,0), /* FTMTIME */
+ A(1,0,0), /* FTATIME */
+ A(1,0,0), /* FTCTIME */
+ A(1,0,0), /* FTSOCK */
+ A(1,0,0), /* FTCHR */
+ A(1,0,0), /* FTBLK */
+ A(1,0,0), /* FTFILE */
+ A(1,0,0), /* FTDIR */
+ A(1,0,0), /* FTPIPE */
+ A(1,0,0), /* FTLINK */
+ A(1,1,0), /* SYMLINK */
+ A(1,0,0), /* READLINK */
+ A(1,0,0), /* FTSUID */
+ A(1,0,0), /* FTSGID */
+ A(1,0,0), /* FTSVTX */
+ A(1,0,0), /* FTTTY */
+ A(1,0,0), /* FTTEXT */
+ A(1,0,0), /* FTBINARY */
+ A5(1,1,1,1,0), /* SOCKET */
+ A(1,1,0), /* BIND */
+ A(1,1,0), /* CONNECT */
+ A(1,1,0), /* LISTEN */
+ A(1,1,0), /* ACCEPT */
+ A(1,0,0), /* GHBYNAME */
+ A(1,1,0), /* GHBYADDR */
+ A(0,0,0), /* GHOSTENT */
+ A(1,0,0), /* GNBYNAME */
+ A(1,1,0), /* GNBYADDR */
+ A(0,0,0), /* GNETENT */
+ A(1,0,0), /* GPBYNAME */
+ A(1,0,0), /* GPBYNUMBER */
+ A(0,0,0), /* GPROTOENT */
+ A(1,1,0), /* GSBYNAME */
+ A(1,1,0), /* GSBYPORT */
+ A(0,0,0), /* GSERVENT */
+ A(1,0,0), /* SHOSTENT */
+ A(1,0,0), /* SNETENT */
+ A(1,0,0), /* SPROTOENT */
+ A(1,0,0), /* SSERVENT */
+ A(0,0,0), /* EHOSTENT */
+ A(0,0,0), /* ENETENT */
+ A(0,0,0), /* EPROTOENT */
+ A(0,0,0), /* ESERVENT */
+ A5(1,1,1,1,1), /* SOCKPAIR */
+ A(1,1,0), /* SHUTDOWN */
+ A(1,1,1), /* GSOCKOPT */
+ A5(1,1,1,1,0), /* SSOCKOPT */
+ A(1,0,0), /* GETSOCKNAME */
+ A(1,0,0), /* GETPEERNAME */
+ A5(1,1,1,1,0), /* SSELECT */
+ A(1,0,0), /* FILENO */
+ A(1,0,0), /* BINMODE */
+ A(1,1,1), /* VEC */
+ A(1,0,0), /* GPWNAM */
+ A(1,0,0), /* GPWUID */
+ A(0,0,0), /* GPWENT */
+ A(0,0,0), /* SPWENT */
+ A(0,0,0), /* EPWENT */
+ A(1,0,0), /* GGRNAM */
+ A(1,0,0), /* GGRGID */
+ A(0,0,0), /* GGRENT */
+ A(0,0,0), /* SGRENT */
+ A(0,0,0), /* EGRENT */
+ A(0,0,0), /* GETLOGIN */
+ A(1,1,0), /* OPENDIR */
+ A(1,0,0), /* READDIR */
+ A(1,0,0), /* TELLDIR */
+ A(1,1,0), /* SEEKDIR */
+ A(1,0,0), /* REWINDDIR */
+ A(1,0,0), /* CLOSEDIR */
+ A(1,3,0), /* SYSCALL */
+ A(1,1,0), /* PIPE */
+ A(0,0,0), /* TRY */
+ A(1,0,0), /* EVALONCE */
+ 0
+};
+#undef A
+#undef A5
+#endif
+
+int do_trans();
+int do_split();
+bool do_eof();
+long do_tell();
+bool do_seek();
+int do_tms();
+int do_time();
+int do_stat();
+STR *do_push();
+FILE *nextargv();
+STR *do_fttext();
+int do_slice();
diff --git a/gnu/usr.bin/perl/perl/array.c b/gnu/usr.bin/perl/perl/array.c
new file mode 100644
index 0000000..445bc60
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/array.c
@@ -0,0 +1,287 @@
+/* $RCSfile: array.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:34 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: array.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:34 nate
+ * PERL!
+ *
+ * Revision 4.0.1.3 92/06/08 11:45:05 lwall
+ * patch20: Perl now distinguishes overlapped copies from non-overlapped
+ *
+ * Revision 4.0.1.2 91/11/05 16:00:14 lwall
+ * patch11: random cleanup
+ * patch11: passing non-existend array elements to subrouting caused core dump
+ *
+ * Revision 4.0.1.1 91/06/07 10:19:08 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:03:32 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+
+STR *
+afetch(ar,key,lval)
+register ARRAY *ar;
+int key;
+int lval;
+{
+ STR *str;
+
+ if (key < 0 || key > ar->ary_fill) {
+ if (lval && key >= 0) {
+ if (ar->ary_flags & ARF_REAL)
+ str = Str_new(5,0);
+ else
+ str = str_mortal(&str_undef);
+ (void)astore(ar,key,str);
+ return str;
+ }
+ else
+ return &str_undef;
+ }
+ if (!ar->ary_array[key]) {
+ if (lval) {
+ str = Str_new(6,0);
+ (void)astore(ar,key,str);
+ return str;
+ }
+ return &str_undef;
+ }
+ return ar->ary_array[key];
+}
+
+bool
+astore(ar,key,val)
+register ARRAY *ar;
+int key;
+STR *val;
+{
+ int retval;
+
+ if (key < 0)
+ return FALSE;
+ if (key > ar->ary_max) {
+ int newmax;
+
+ if (ar->ary_alloc != ar->ary_array) {
+ retval = ar->ary_array - ar->ary_alloc;
+ Move(ar->ary_array, ar->ary_alloc, ar->ary_max+1, STR*);
+ Zero(ar->ary_alloc+ar->ary_max+1, retval, STR*);
+ ar->ary_max += retval;
+ ar->ary_array -= retval;
+ if (key > ar->ary_max - 10) {
+ newmax = key + ar->ary_max;
+ goto resize;
+ }
+ }
+ else {
+ if (ar->ary_alloc) {
+ newmax = key + ar->ary_max / 5;
+ resize:
+ Renew(ar->ary_alloc,newmax+1, STR*);
+ Zero(&ar->ary_alloc[ar->ary_max+1], newmax - ar->ary_max, STR*);
+ }
+ else {
+ newmax = key < 4 ? 4 : key;
+ Newz(2,ar->ary_alloc, newmax+1, STR*);
+ }
+ ar->ary_array = ar->ary_alloc;
+ ar->ary_max = newmax;
+ }
+ }
+ if (ar->ary_flags & ARF_REAL) {
+ if (ar->ary_fill < key) {
+ while (++ar->ary_fill < key) {
+ if (ar->ary_array[ar->ary_fill] != Nullstr) {
+ str_free(ar->ary_array[ar->ary_fill]);
+ ar->ary_array[ar->ary_fill] = Nullstr;
+ }
+ }
+ }
+ retval = (ar->ary_array[key] != Nullstr);
+ if (retval)
+ str_free(ar->ary_array[key]);
+ }
+ else
+ retval = 0;
+ ar->ary_array[key] = val;
+ return retval;
+}
+
+ARRAY *
+anew(stab)
+STAB *stab;
+{
+ register ARRAY *ar;
+
+ New(1,ar,1,ARRAY);
+ ar->ary_magic = Str_new(7,0);
+ ar->ary_alloc = ar->ary_array = 0;
+ str_magic(ar->ary_magic, stab, '#', Nullch, 0);
+ ar->ary_max = ar->ary_fill = -1;
+ ar->ary_flags = ARF_REAL;
+ return ar;
+}
+
+ARRAY *
+afake(stab,size,strp)
+STAB *stab;
+register int size;
+register STR **strp;
+{
+ register ARRAY *ar;
+
+ New(3,ar,1,ARRAY);
+ New(4,ar->ary_alloc,size+1,STR*);
+ Copy(strp,ar->ary_alloc,size,STR*);
+ ar->ary_array = ar->ary_alloc;
+ ar->ary_magic = Str_new(8,0);
+ str_magic(ar->ary_magic, stab, '#', Nullch, 0);
+ ar->ary_fill = size - 1;
+ ar->ary_max = size - 1;
+ ar->ary_flags = 0;
+ while (size--) {
+ if (*strp)
+ (*strp)->str_pok &= ~SP_TEMP;
+ strp++;
+ }
+ return ar;
+}
+
+void
+aclear(ar)
+register ARRAY *ar;
+{
+ register int key;
+
+ if (!ar || !(ar->ary_flags & ARF_REAL) || ar->ary_max < 0)
+ return;
+ /*SUPPRESS 560*/
+ if (key = ar->ary_array - ar->ary_alloc) {
+ ar->ary_max += key;
+ ar->ary_array -= key;
+ }
+ for (key = 0; key <= ar->ary_max; key++)
+ str_free(ar->ary_array[key]);
+ ar->ary_fill = -1;
+ Zero(ar->ary_array, ar->ary_max+1, STR*);
+}
+
+void
+afree(ar)
+register ARRAY *ar;
+{
+ register int key;
+
+ if (!ar)
+ return;
+ /*SUPPRESS 560*/
+ if (key = ar->ary_array - ar->ary_alloc) {
+ ar->ary_max += key;
+ ar->ary_array -= key;
+ }
+ if (ar->ary_flags & ARF_REAL) {
+ for (key = 0; key <= ar->ary_max; key++)
+ str_free(ar->ary_array[key]);
+ }
+ str_free(ar->ary_magic);
+ Safefree(ar->ary_alloc);
+ Safefree(ar);
+}
+
+bool
+apush(ar,val)
+register ARRAY *ar;
+STR *val;
+{
+ return astore(ar,++(ar->ary_fill),val);
+}
+
+STR *
+apop(ar)
+register ARRAY *ar;
+{
+ STR *retval;
+
+ if (ar->ary_fill < 0)
+ return Nullstr;
+ retval = ar->ary_array[ar->ary_fill];
+ ar->ary_array[ar->ary_fill--] = Nullstr;
+ return retval;
+}
+
+void
+aunshift(ar,num)
+register ARRAY *ar;
+register int num;
+{
+ register int i;
+ register STR **sstr,**dstr;
+
+ if (num <= 0)
+ return;
+ if (ar->ary_array - ar->ary_alloc >= num) {
+ ar->ary_max += num;
+ ar->ary_fill += num;
+ while (num--)
+ *--ar->ary_array = Nullstr;
+ }
+ else {
+ (void)astore(ar,ar->ary_fill+num,(STR*)0); /* maybe extend array */
+ dstr = ar->ary_array + ar->ary_fill;
+ sstr = dstr - num;
+#ifdef BUGGY_MSC5
+ # pragma loop_opt(off) /* don't loop-optimize the following code */
+#endif /* BUGGY_MSC5 */
+ for (i = ar->ary_fill - num; i >= 0; i--) {
+ *dstr-- = *sstr--;
+#ifdef BUGGY_MSC5
+ # pragma loop_opt() /* loop-optimization back to command-line setting */
+#endif /* BUGGY_MSC5 */
+ }
+ Zero(ar->ary_array, num, STR*);
+ }
+}
+
+STR *
+ashift(ar)
+register ARRAY *ar;
+{
+ STR *retval;
+
+ if (ar->ary_fill < 0)
+ return Nullstr;
+ retval = *ar->ary_array;
+ *(ar->ary_array++) = Nullstr;
+ ar->ary_max--;
+ ar->ary_fill--;
+ return retval;
+}
+
+int
+alen(ar)
+register ARRAY *ar;
+{
+ return ar->ary_fill;
+}
+
+void
+afill(ar, fill)
+register ARRAY *ar;
+int fill;
+{
+ if (fill < 0)
+ fill = -1;
+ if (fill <= ar->ary_max)
+ ar->ary_fill = fill;
+ else
+ (void)astore(ar,fill,Nullstr);
+}
diff --git a/gnu/usr.bin/perl/perl/array.h b/gnu/usr.bin/perl/perl/array.h
new file mode 100644
index 0000000..93d4920
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/array.h
@@ -0,0 +1,45 @@
+/* $RCSfile: array.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:34 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: array.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:34 nate
+ * PERL!
+ *
+ * Revision 4.0.1.2 92/06/08 11:45:57 lwall
+ * patch20: removed implicit int declarations on funcions
+ *
+ * Revision 4.0.1.1 91/06/07 10:19:20 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:03:44 lwall
+ * 4.0 baseline.
+ *
+ */
+
+struct atbl {
+ STR **ary_array;
+ STR **ary_alloc;
+ STR *ary_magic;
+ int ary_max;
+ int ary_fill;
+ char ary_flags;
+};
+
+#define ARF_REAL 1 /* free old entries */
+
+STR *afetch();
+bool astore();
+STR *apop();
+STR *ashift();
+void afree();
+void aclear();
+bool apush();
+int alen();
+ARRAY *anew();
+ARRAY *afake();
+void aunshift();
+void afill();
diff --git a/gnu/usr.bin/perl/perl/cflags b/gnu/usr.bin/perl/perl/cflags
new file mode 100755
index 0000000..672dfc6
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/cflags
@@ -0,0 +1,91 @@
+case "$0" in
+*/*) cd `expr X$0 : 'X\(.*\)/'` ;;
+esac
+case $CONFIG in
+'')
+ if test ! -f config.sh; then
+ ln ../config.sh . || \
+ ln ../../config.sh . || \
+ ln ../../../config.sh . || \
+ (echo "Can't find config.sh."; exit 1)
+ fi 2>/dev/null
+ . ./config.sh
+ ;;
+esac
+
+also=': '
+case $# in
+1) also='echo 1>&2 " CCCMD = "'
+esac
+
+case $# in
+0) set *.c; echo "The current C flags are:" ;;
+esac
+
+set `echo "$* " | sed 's/\.[oc] / /g'`
+
+for file do
+
+ case "$#" in
+ 1) ;;
+ *) echo $n " $file.c $c" ;;
+ esac
+
+ : allow variables like toke_cflags to be evaluated
+
+ eval 'eval ${'"${file}_cflags"'-""}'
+
+ : or customize here
+
+ case "$file" in
+ array) ;;
+ cmd) ;;
+ cons) ;;
+ consarg) ;;
+ doarg) ;;
+ doio) ;;
+ dolist) ;;
+ dump) ;;
+ eval) ;;
+ form) ;;
+ hash) ;;
+ malloc) ;;
+ perl) ;;
+ perly) ;;
+ regcomp) ;;
+ regexec) ;;
+ stab) ;;
+ str) ;;
+ toke) ;;
+ usersub) ;;
+ util) ;;
+ tarray) ;;
+ tcmd) ;;
+ tcons) ;;
+ tconsarg) ;;
+ tdoarg) ;;
+ tdoio) ;;
+ tdolist) ;;
+ tdump) ;;
+ teval) ;;
+ tform) ;;
+ thash) ;;
+ tmalloc) ;;
+ tperl) ;;
+ tperly) ;;
+ tregcomp) ;;
+ tregexec) ;;
+ tstab) ;;
+ tstr) ;;
+ ttoke) ;;
+ tusersub) ;;
+ tutil) ;;
+ *) ;;
+ esac
+
+ echo "$cc -c $ccflags $optimize $large $split"
+ eval "$also "'"$cc -c $ccflags $optimize $large $split"'
+
+ . ./config.sh
+
+done
diff --git a/gnu/usr.bin/perl/perl/cmd.c b/gnu/usr.bin/perl/perl/cmd.c
new file mode 100644
index 0000000..1ddbde0
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/cmd.c
@@ -0,0 +1,1263 @@
+/* $RCSfile: cmd.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:35 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: cmd.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:35 nate
+ * PERL!
+ *
+ * Revision 4.0.1.5 92/06/08 12:00:39 lwall
+ * patch20: the switch optimizer didn't do anything in subroutines
+ * patch20: removed implicit int declarations on funcions
+ *
+ * Revision 4.0.1.4 91/11/11 16:29:33 lwall
+ * patch19: do {$foo ne "bar";} returned wrong value
+ * patch19: some earlier patches weren't propagated to alternate 286 code
+ *
+ * Revision 4.0.1.3 91/11/05 16:07:43 lwall
+ * patch11: random cleanup
+ * patch11: "foo\0" eq "foo" was sometimes optimized to true
+ * patch11: foreach on null list could spring memory leak
+ *
+ * Revision 4.0.1.2 91/06/07 10:26:45 lwall
+ * patch4: new copyright notice
+ * patch4: made some allowances for "semi-standard" C
+ *
+ * Revision 4.0.1.1 91/04/11 17:36:16 lwall
+ * patch1: you may now use "die" and "caller" in a signal handler
+ *
+ * Revision 4.0 91/03/20 01:04:18 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+
+#ifdef I_VARARGS
+# include <varargs.h>
+#endif
+
+static STR strchop;
+
+void grow_dlevel();
+
+/* do longjmps() clobber register variables? */
+
+#if defined(cray) || defined(STANDARD_C)
+#define JMPCLOBBER
+#endif
+
+/* This is the main command loop. We try to spend as much time in this loop
+ * as possible, so lots of optimizations do their activities in here. This
+ * means things get a little sloppy.
+ */
+
+int
+cmd_exec(cmdparm,gimme,sp)
+CMD *VOLATILE cmdparm;
+VOLATILE int gimme;
+VOLATILE int sp;
+{
+ register CMD *cmd = cmdparm;
+ SPAT *VOLATILE oldspat;
+ VOLATILE int firstsave = savestack->ary_fill;
+ VOLATILE int oldsave;
+ VOLATILE int aryoptsave;
+#ifdef DEBUGGING
+ VOLATILE int olddlevel;
+ VOLATILE int entdlevel;
+#endif
+ register STR *retstr = &str_undef;
+ register char *tmps;
+ register int cmdflags;
+ register int match;
+ register char *go_to = goto_targ;
+ register int newsp = -2;
+ register STR **st = stack->ary_array;
+ FILE *VOLATILE fp;
+ ARRAY *VOLATILE ar;
+
+ lastsize = 0;
+#ifdef DEBUGGING
+ entdlevel = dlevel;
+#endif
+tail_recursion_entry:
+#ifdef DEBUGGING
+ dlevel = entdlevel;
+ if (debug & 4)
+ deb("mortals = (%d/%d) stack, = (%d/%d)\n",
+ tmps_max, tmps_base,
+ savestack->ary_fill, firstsave);
+#endif
+#ifdef TAINT
+ tainted = 0; /* Each statement is presumed innocent */
+#endif
+ if (cmd == Nullcmd) {
+ if (gimme == G_ARRAY && newsp > -2)
+ return newsp;
+ else {
+ st[++sp] = retstr;
+ return sp;
+ }
+ }
+ cmdflags = cmd->c_flags; /* hopefully load register */
+ if (go_to) {
+ if (cmd->c_label && strEQ(go_to,cmd->c_label))
+ goto_targ = go_to = Nullch; /* here at last */
+ else {
+ switch (cmd->c_type) {
+ case C_IF:
+ oldspat = curspat;
+ oldsave = savestack->ary_fill;
+#ifdef DEBUGGING
+ olddlevel = dlevel;
+#endif
+ retstr = &str_yes;
+ newsp = -2;
+ if (cmd->ucmd.ccmd.cc_true) {
+#ifdef DEBUGGING
+ if (debug) {
+ debname[dlevel] = 't';
+ debdelim[dlevel] = '_';
+ if (++dlevel >= dlmax)
+ grow_dlevel();
+ }
+#endif
+ newsp = cmd_exec(cmd->ucmd.ccmd.cc_true,gimme && (cmdflags & CF_TERM),sp);
+ st = stack->ary_array; /* possibly reallocated */
+ retstr = st[newsp];
+ }
+ if (!goto_targ)
+ go_to = Nullch;
+ curspat = oldspat;
+ if (savestack->ary_fill > oldsave)
+ restorelist(oldsave);
+#ifdef DEBUGGING
+ dlevel = olddlevel;
+#endif
+ cmd = cmd->ucmd.ccmd.cc_alt;
+ goto tail_recursion_entry;
+ case C_ELSE:
+ oldspat = curspat;
+ oldsave = savestack->ary_fill;
+#ifdef DEBUGGING
+ olddlevel = dlevel;
+#endif
+ retstr = &str_undef;
+ newsp = -2;
+ if (cmd->ucmd.ccmd.cc_true) {
+#ifdef DEBUGGING
+ if (debug) {
+ debname[dlevel] = 'e';
+ debdelim[dlevel] = '_';
+ if (++dlevel >= dlmax)
+ grow_dlevel();
+ }
+#endif
+ newsp = cmd_exec(cmd->ucmd.ccmd.cc_true,gimme && (cmdflags & CF_TERM),sp);
+ st = stack->ary_array; /* possibly reallocated */
+ retstr = st[newsp];
+ }
+ if (!goto_targ)
+ go_to = Nullch;
+ curspat = oldspat;
+ if (savestack->ary_fill > oldsave)
+ restorelist(oldsave);
+#ifdef DEBUGGING
+ dlevel = olddlevel;
+#endif
+ break;
+ case C_BLOCK:
+ case C_WHILE:
+ if (!(cmdflags & CF_ONCE)) {
+ cmdflags |= CF_ONCE;
+ if (++loop_ptr >= loop_max) {
+ loop_max += 128;
+ Renew(loop_stack, loop_max, struct loop);
+ }
+ loop_stack[loop_ptr].loop_label = cmd->c_label;
+ loop_stack[loop_ptr].loop_sp = sp;
+#ifdef DEBUGGING
+ if (debug & 4) {
+ deb("(Pushing label #%d %s)\n",
+ loop_ptr, cmd->c_label ? cmd->c_label : "");
+ }
+#endif
+ }
+#ifdef JMPCLOBBER
+ cmdparm = cmd;
+#endif
+ match = setjmp(loop_stack[loop_ptr].loop_env);
+ if (match) {
+ st = stack->ary_array; /* possibly reallocated */
+#ifdef JMPCLOBBER
+ cmd = cmdparm;
+ cmdflags = cmd->c_flags|CF_ONCE;
+#endif
+ if (savestack->ary_fill > oldsave)
+ restorelist(oldsave);
+ switch (match) {
+ default:
+ fatal("longjmp returned bad value (%d)",match);
+ case O_LAST: /* not done unless go_to found */
+ go_to = Nullch;
+ if (lastretstr) {
+ retstr = lastretstr;
+ newsp = -2;
+ }
+ else {
+ newsp = sp + lastsize;
+ retstr = st[newsp];
+ }
+#ifdef DEBUGGING
+ olddlevel = dlevel;
+#endif
+ curspat = oldspat;
+ goto next_cmd;
+ case O_NEXT: /* not done unless go_to found */
+ go_to = Nullch;
+#ifdef JMPCLOBBER
+ newsp = -2;
+ retstr = &str_undef;
+#endif
+ goto next_iter;
+ case O_REDO: /* not done unless go_to found */
+ go_to = Nullch;
+#ifdef JMPCLOBBER
+ newsp = -2;
+ retstr = &str_undef;
+#endif
+ goto doit;
+ }
+ }
+ oldspat = curspat;
+ oldsave = savestack->ary_fill;
+#ifdef DEBUGGING
+ olddlevel = dlevel;
+#endif
+ if (cmd->ucmd.ccmd.cc_true) {
+#ifdef DEBUGGING
+ if (debug) {
+ debname[dlevel] = 't';
+ debdelim[dlevel] = '_';
+ if (++dlevel >= dlmax)
+ grow_dlevel();
+ }
+#endif
+ newsp = cmd_exec(cmd->ucmd.ccmd.cc_true,gimme && (cmdflags & CF_TERM),sp);
+ st = stack->ary_array; /* possibly reallocated */
+ if (newsp >= 0)
+ retstr = st[newsp];
+ }
+ if (!goto_targ) {
+ go_to = Nullch;
+ goto next_iter;
+ }
+#ifdef DEBUGGING
+ dlevel = olddlevel;
+#endif
+ if (cmd->ucmd.ccmd.cc_alt) {
+#ifdef DEBUGGING
+ if (debug) {
+ debname[dlevel] = 'a';
+ debdelim[dlevel] = '_';
+ if (++dlevel >= dlmax)
+ grow_dlevel();
+ }
+#endif
+ newsp = cmd_exec(cmd->ucmd.ccmd.cc_alt,gimme && (cmdflags & CF_TERM),sp);
+ st = stack->ary_array; /* possibly reallocated */
+ if (newsp >= 0)
+ retstr = st[newsp];
+ }
+ if (goto_targ)
+ break;
+ go_to = Nullch;
+ goto finish_while;
+ }
+ cmd = cmd->c_next;
+ if (cmd && cmd->c_head == cmd)
+ /* reached end of while loop */
+ return sp; /* targ isn't in this block */
+ if (cmdflags & CF_ONCE) {
+#ifdef DEBUGGING
+ if (debug & 4) {
+ tmps = loop_stack[loop_ptr].loop_label;
+ deb("(Popping label #%d %s)\n",loop_ptr,
+ tmps ? tmps : "" );
+ }
+#endif
+ loop_ptr--;
+ }
+ goto tail_recursion_entry;
+ }
+ }
+
+until_loop:
+
+ /* Set line number so run-time errors can be located */
+
+ curcmd = cmd;
+
+#ifdef DEBUGGING
+ if (debug) {
+ if (debug & 2) {
+ deb("%s (%lx) r%lx t%lx a%lx n%lx cs%lx\n",
+ cmdname[cmd->c_type],cmd,cmd->c_expr,
+ cmd->ucmd.ccmd.cc_true,cmd->ucmd.ccmd.cc_alt,cmd->c_next,
+ curspat);
+ }
+ debname[dlevel] = cmdname[cmd->c_type][0];
+ debdelim[dlevel] = '!';
+ if (++dlevel >= dlmax)
+ grow_dlevel();
+ }
+#endif
+
+ /* Here is some common optimization */
+
+ if (cmdflags & CF_COND) {
+ switch (cmdflags & CF_OPTIMIZE) {
+
+ case CFT_FALSE:
+ retstr = cmd->c_short;
+ newsp = -2;
+ match = FALSE;
+ if (cmdflags & CF_NESURE)
+ goto maybe;
+ break;
+ case CFT_TRUE:
+ retstr = cmd->c_short;
+ newsp = -2;
+ match = TRUE;
+ if (cmdflags & CF_EQSURE)
+ goto flipmaybe;
+ break;
+
+ case CFT_REG:
+ retstr = STAB_STR(cmd->c_stab);
+ newsp = -2;
+ match = str_true(retstr); /* => retstr = retstr, c2 should fix */
+ if (cmdflags & (match ? CF_EQSURE : CF_NESURE))
+ goto flipmaybe;
+ break;
+
+ case CFT_ANCHOR: /* /^pat/ optimization */
+ if (multiline) {
+ if (*cmd->c_short->str_ptr && !(cmdflags & CF_EQSURE))
+ goto scanner; /* just unanchor it */
+ else
+ break; /* must evaluate */
+ }
+ match = 0;
+ goto strop;
+
+ case CFT_STROP: /* string op optimization */
+ match = 1;
+ strop:
+ retstr = STAB_STR(cmd->c_stab);
+ newsp = -2;
+#ifndef I286
+ if (*cmd->c_short->str_ptr == *str_get(retstr) &&
+ (match ? retstr->str_cur == cmd->c_slen - 1 :
+ retstr->str_cur >= cmd->c_slen) &&
+ bcmp(cmd->c_short->str_ptr, str_get(retstr),
+ cmd->c_slen) == 0 ) {
+ if (cmdflags & CF_EQSURE) {
+ if (sawampersand && (cmdflags & CF_OPTIMIZE) != CFT_STROP) {
+ curspat = Nullspat;
+ if (leftstab)
+ str_nset(stab_val(leftstab),"",0);
+ if (amperstab)
+ str_sset(stab_val(amperstab),cmd->c_short);
+ if (rightstab)
+ str_nset(stab_val(rightstab),
+ retstr->str_ptr + cmd->c_slen,
+ retstr->str_cur - cmd->c_slen);
+ }
+ if (cmd->c_spat)
+ lastspat = cmd->c_spat;
+ match = !(cmdflags & CF_FIRSTNEG);
+ retstr = match ? &str_yes : &str_no;
+ goto flipmaybe;
+ }
+ }
+ else if (cmdflags & CF_NESURE) {
+ match = cmdflags & CF_FIRSTNEG;
+ retstr = match ? &str_yes : &str_no;
+ goto flipmaybe;
+ }
+#else
+ {
+ char *zap1, *zap2, zap1c, zap2c;
+ int zaplen;
+ int lenok;
+
+ zap1 = cmd->c_short->str_ptr;
+ zap2 = str_get(retstr);
+ zap1c = *zap1;
+ zap2c = *zap2;
+ zaplen = cmd->c_slen;
+ if (match)
+ lenok = (retstr->str_cur == cmd->c_slen - 1);
+ else
+ lenok = (retstr->str_cur >= cmd->c_slen);
+ if ((zap1c == zap2c) && lenok && (bcmp(zap1, zap2, zaplen) == 0)) {
+ if (cmdflags & CF_EQSURE) {
+ if (sawampersand &&
+ (cmdflags & CF_OPTIMIZE) != CFT_STROP) {
+ curspat = Nullspat;
+ if (leftstab)
+ str_nset(stab_val(leftstab),"",0);
+ if (amperstab)
+ str_sset(stab_val(amperstab),cmd->c_short);
+ if (rightstab)
+ str_nset(stab_val(rightstab),
+ retstr->str_ptr + cmd->c_slen,
+ retstr->str_cur - cmd->c_slen);
+ }
+ if (cmd->c_spat)
+ lastspat = cmd->c_spat;
+ match = !(cmdflags & CF_FIRSTNEG);
+ retstr = match ? &str_yes : &str_no;
+ goto flipmaybe;
+ }
+ }
+ else if (cmdflags & CF_NESURE) {
+ match = cmdflags & CF_FIRSTNEG;
+ retstr = match ? &str_yes : &str_no;
+ goto flipmaybe;
+ }
+ }
+#endif
+ break; /* must evaluate */
+
+ case CFT_SCAN: /* non-anchored search */
+ scanner:
+ retstr = STAB_STR(cmd->c_stab);
+ newsp = -2;
+ if (retstr->str_pok & SP_STUDIED)
+ if (screamfirst[cmd->c_short->str_rare] >= 0)
+ tmps = screaminstr(retstr, cmd->c_short);
+ else
+ tmps = Nullch;
+ else {
+ tmps = str_get(retstr); /* make sure it's pok */
+#ifndef lint
+ tmps = fbminstr((unsigned char*)tmps,
+ (unsigned char*)tmps + retstr->str_cur, cmd->c_short);
+#endif
+ }
+ if (tmps) {
+ if (cmdflags & CF_EQSURE) {
+ ++cmd->c_short->str_u.str_useful;
+ if (sawampersand) {
+ curspat = Nullspat;
+ if (leftstab)
+ str_nset(stab_val(leftstab),retstr->str_ptr,
+ tmps - retstr->str_ptr);
+ if (amperstab)
+ str_nset(stab_val(amperstab),
+ tmps, cmd->c_short->str_cur);
+ if (rightstab)
+ str_nset(stab_val(rightstab),
+ tmps + cmd->c_short->str_cur,
+ retstr->str_cur - (tmps - retstr->str_ptr) -
+ cmd->c_short->str_cur);
+ }
+ lastspat = cmd->c_spat;
+ match = !(cmdflags & CF_FIRSTNEG);
+ retstr = match ? &str_yes : &str_no;
+ goto flipmaybe;
+ }
+ else
+ hint = tmps;
+ }
+ else {
+ if (cmdflags & CF_NESURE) {
+ ++cmd->c_short->str_u.str_useful;
+ match = cmdflags & CF_FIRSTNEG;
+ retstr = match ? &str_yes : &str_no;
+ goto flipmaybe;
+ }
+ }
+ if (--cmd->c_short->str_u.str_useful < 0) {
+ cmdflags &= ~CF_OPTIMIZE;
+ cmdflags |= CFT_EVAL; /* never try this optimization again */
+ cmd->c_flags = (cmdflags & ~CF_ONCE);
+ }
+ break; /* must evaluate */
+
+ case CFT_NUMOP: /* numeric op optimization */
+ retstr = STAB_STR(cmd->c_stab);
+ newsp = -2;
+ switch (cmd->c_slen) {
+ case O_EQ:
+ if (dowarn) {
+ if ((!retstr->str_nok && !looks_like_number(retstr)))
+ warn("Possible use of == on string value");
+ }
+ match = (str_gnum(retstr) == cmd->c_short->str_u.str_nval);
+ break;
+ case O_NE:
+ match = (str_gnum(retstr) != cmd->c_short->str_u.str_nval);
+ break;
+ case O_LT:
+ match = (str_gnum(retstr) < cmd->c_short->str_u.str_nval);
+ break;
+ case O_LE:
+ match = (str_gnum(retstr) <= cmd->c_short->str_u.str_nval);
+ break;
+ case O_GT:
+ match = (str_gnum(retstr) > cmd->c_short->str_u.str_nval);
+ break;
+ case O_GE:
+ match = (str_gnum(retstr) >= cmd->c_short->str_u.str_nval);
+ break;
+ }
+ if (match) {
+ if (cmdflags & CF_EQSURE) {
+ retstr = &str_yes;
+ goto flipmaybe;
+ }
+ }
+ else if (cmdflags & CF_NESURE) {
+ retstr = &str_no;
+ goto flipmaybe;
+ }
+ break; /* must evaluate */
+
+ case CFT_INDGETS: /* while (<$foo>) */
+ last_in_stab = stabent(str_get(STAB_STR(cmd->c_stab)),TRUE);
+ if (!stab_io(last_in_stab))
+ stab_io(last_in_stab) = stio_new();
+ goto dogets;
+ case CFT_GETS: /* really a while (<file>) */
+ last_in_stab = cmd->c_stab;
+ dogets:
+ fp = stab_io(last_in_stab)->ifp;
+ retstr = stab_val(defstab);
+ newsp = -2;
+ keepgoing:
+ if (fp && str_gets(retstr, fp, 0)) {
+ if (*retstr->str_ptr == '0' && retstr->str_cur == 1)
+ match = FALSE;
+ else
+ match = TRUE;
+ stab_io(last_in_stab)->lines++;
+ }
+ else if (stab_io(last_in_stab)->flags & IOF_ARGV) {
+ if (!fp)
+ goto doeval; /* first time through */
+ fp = nextargv(last_in_stab);
+ if (fp)
+ goto keepgoing;
+ (void)do_close(last_in_stab,FALSE);
+ stab_io(last_in_stab)->flags |= IOF_START;
+ retstr = &str_undef;
+ match = FALSE;
+ }
+ else {
+ retstr = &str_undef;
+ match = FALSE;
+ }
+ goto flipmaybe;
+ case CFT_EVAL:
+ break;
+ case CFT_UNFLIP:
+ while (tmps_max > tmps_base) { /* clean up after last eval */
+ str_free(tmps_list[tmps_max]);
+ tmps_list[tmps_max--] = Nullstr;
+ }
+ newsp = eval(cmd->c_expr,gimme && (cmdflags & CF_TERM),sp);
+ st = stack->ary_array; /* possibly reallocated */
+ retstr = st[newsp];
+ match = str_true(retstr);
+ if (cmd->c_expr->arg_type == O_FLIP) /* undid itself? */
+ cmdflags = copyopt(cmd,cmd->c_expr[3].arg_ptr.arg_cmd);
+ goto maybe;
+ case CFT_CHOP:
+ retstr = stab_val(cmd->c_stab);
+ newsp = -2;
+ match = (retstr->str_cur != 0);
+ tmps = str_get(retstr);
+ tmps += retstr->str_cur - match;
+ str_nset(&strchop,tmps,match);
+ *tmps = '\0';
+ retstr->str_nok = 0;
+ retstr->str_cur = tmps - retstr->str_ptr;
+ STABSET(retstr);
+ retstr = &strchop;
+ goto flipmaybe;
+ case CFT_ARRAY:
+ match = cmd->c_short->str_u.str_useful; /* just to get register */
+
+ if (match < 0) { /* first time through here? */
+ ar = stab_array(cmd->c_expr[1].arg_ptr.arg_stab);
+ aryoptsave = savestack->ary_fill;
+ savesptr(&stab_val(cmd->c_stab));
+ savelong(&cmd->c_short->str_u.str_useful);
+ }
+ else {
+ ar = stab_xarray(cmd->c_expr[1].arg_ptr.arg_stab);
+ if (cmd->c_type != C_WHILE && savestack->ary_fill > firstsave)
+ restorelist(firstsave);
+ }
+
+ if (match >= ar->ary_fill) { /* we're in LAST, probably */
+ if (match < 0 && /* er, probably not... */
+ savestack->ary_fill > aryoptsave)
+ restorelist(aryoptsave);
+ retstr = &str_undef;
+ cmd->c_short->str_u.str_useful = -1; /* actually redundant */
+ match = FALSE;
+ }
+ else {
+ match++;
+ if (!(retstr = ar->ary_array[match]))
+ retstr = afetch(ar,match,TRUE);
+ stab_val(cmd->c_stab) = retstr;
+ cmd->c_short->str_u.str_useful = match;
+ match = TRUE;
+ }
+ newsp = -2;
+ goto maybe;
+ case CFT_D1:
+ break;
+ case CFT_D0:
+ if (DBsingle->str_u.str_nval != 0)
+ break;
+ if (DBsignal->str_u.str_nval != 0)
+ break;
+ if (DBtrace->str_u.str_nval != 0)
+ break;
+ goto next_cmd;
+ }
+
+ /* we have tried to make this normal case as abnormal as possible */
+
+ doeval:
+ if (gimme == G_ARRAY) {
+ lastretstr = Nullstr;
+ lastspbase = sp;
+ lastsize = newsp - sp;
+ if (lastsize < 0)
+ lastsize = 0;
+ }
+ else
+ lastretstr = retstr;
+ while (tmps_max > tmps_base) { /* clean up after last eval */
+ str_free(tmps_list[tmps_max]);
+ tmps_list[tmps_max--] = Nullstr;
+ }
+ newsp = eval(cmd->c_expr,
+ gimme && (cmdflags & CF_TERM) && cmd->c_type == C_EXPR &&
+ !cmd->ucmd.acmd.ac_expr,
+ sp);
+ st = stack->ary_array; /* possibly reallocated */
+ retstr = st[newsp];
+ if (newsp > sp && retstr)
+ match = str_true(retstr);
+ else
+ match = FALSE;
+ goto maybe;
+
+ /* if flipflop was true, flop it */
+
+ flipmaybe:
+ if (match && cmdflags & CF_FLIP) {
+ while (tmps_max > tmps_base) { /* clean up after last eval */
+ str_free(tmps_list[tmps_max]);
+ tmps_list[tmps_max--] = Nullstr;
+ }
+ if (cmd->c_expr->arg_type == O_FLOP) { /* currently toggled? */
+ newsp = eval(cmd->c_expr,G_SCALAR,sp);/*let eval undo it*/
+ cmdflags = copyopt(cmd,cmd->c_expr[3].arg_ptr.arg_cmd);
+ }
+ else {
+ newsp = eval(cmd->c_expr,G_SCALAR,sp);/* let eval do it */
+ if (cmd->c_expr->arg_type == O_FLOP) /* still toggled? */
+ cmdflags = copyopt(cmd,cmd->c_expr[4].arg_ptr.arg_cmd);
+ }
+ }
+ else if (cmdflags & CF_FLIP) {
+ if (cmd->c_expr->arg_type == O_FLOP) { /* currently toggled? */
+ match = TRUE; /* force on */
+ }
+ }
+
+ /* at this point, match says whether our expression was true */
+
+ maybe:
+ if (cmdflags & CF_INVERT)
+ match = !match;
+ if (!match)
+ goto next_cmd;
+ }
+#ifdef TAINT
+ tainted = 0; /* modifier doesn't affect regular expression */
+#endif
+
+ /* now to do the actual command, if any */
+
+ switch (cmd->c_type) {
+ case C_NULL:
+ fatal("panic: cmd_exec");
+ case C_EXPR: /* evaluated for side effects */
+ if (cmd->ucmd.acmd.ac_expr) { /* more to do? */
+ if (gimme == G_ARRAY) {
+ lastretstr = Nullstr;
+ lastspbase = sp;
+ lastsize = newsp - sp;
+ if (lastsize < 0)
+ lastsize = 0;
+ }
+ else
+ lastretstr = retstr;
+ while (tmps_max > tmps_base) { /* clean up after last eval */
+ str_free(tmps_list[tmps_max]);
+ tmps_list[tmps_max--] = Nullstr;
+ }
+ newsp = eval(cmd->ucmd.acmd.ac_expr,gimme && (cmdflags&CF_TERM),sp);
+ st = stack->ary_array; /* possibly reallocated */
+ retstr = st[newsp];
+ }
+ break;
+ case C_NSWITCH:
+ {
+ double value = str_gnum(STAB_STR(cmd->c_stab));
+
+ match = (int)value;
+ if (value < 0.0) {
+ if (((double)match) > value)
+ --match; /* was fractional--truncate other way */
+ }
+ }
+ goto doswitch;
+ case C_CSWITCH:
+ if (multiline) {
+ cmd = cmd->c_next; /* can't assume anything */
+ goto tail_recursion_entry;
+ }
+ match = *(str_get(STAB_STR(cmd->c_stab))) & 255;
+ doswitch:
+ match -= cmd->ucmd.scmd.sc_offset;
+ if (match < 0)
+ match = 0;
+ else if (match > cmd->ucmd.scmd.sc_max)
+ match = cmd->ucmd.scmd.sc_max;
+ cmd = cmd->ucmd.scmd.sc_next[match];
+ goto tail_recursion_entry;
+ case C_NEXT:
+ cmd = cmd->ucmd.ccmd.cc_alt;
+ goto tail_recursion_entry;
+ case C_ELSIF:
+ fatal("panic: ELSIF");
+ case C_IF:
+ oldspat = curspat;
+ oldsave = savestack->ary_fill;
+#ifdef DEBUGGING
+ olddlevel = dlevel;
+#endif
+ retstr = &str_yes;
+ newsp = -2;
+ if (cmd->ucmd.ccmd.cc_true) {
+#ifdef DEBUGGING
+ if (debug) {
+ debname[dlevel] = 't';
+ debdelim[dlevel] = '_';
+ if (++dlevel >= dlmax)
+ grow_dlevel();
+ }
+#endif
+ newsp = cmd_exec(cmd->ucmd.ccmd.cc_true,gimme && (cmdflags & CF_TERM),sp);
+ st = stack->ary_array; /* possibly reallocated */
+ retstr = st[newsp];
+ }
+ curspat = oldspat;
+ if (savestack->ary_fill > oldsave)
+ restorelist(oldsave);
+#ifdef DEBUGGING
+ dlevel = olddlevel;
+#endif
+ cmd = cmd->ucmd.ccmd.cc_alt;
+ goto tail_recursion_entry;
+ case C_ELSE:
+ oldspat = curspat;
+ oldsave = savestack->ary_fill;
+#ifdef DEBUGGING
+ olddlevel = dlevel;
+#endif
+ retstr = &str_undef;
+ newsp = -2;
+ if (cmd->ucmd.ccmd.cc_true) {
+#ifdef DEBUGGING
+ if (debug) {
+ debname[dlevel] = 'e';
+ debdelim[dlevel] = '_';
+ if (++dlevel >= dlmax)
+ grow_dlevel();
+ }
+#endif
+ newsp = cmd_exec(cmd->ucmd.ccmd.cc_true,gimme && (cmdflags & CF_TERM),sp);
+ st = stack->ary_array; /* possibly reallocated */
+ retstr = st[newsp];
+ }
+ curspat = oldspat;
+ if (savestack->ary_fill > oldsave)
+ restorelist(oldsave);
+#ifdef DEBUGGING
+ dlevel = olddlevel;
+#endif
+ break;
+ case C_BLOCK:
+ case C_WHILE:
+ if (!(cmdflags & CF_ONCE)) { /* first time through here? */
+ cmdflags |= CF_ONCE;
+ if (++loop_ptr >= loop_max) {
+ loop_max += 128;
+ Renew(loop_stack, loop_max, struct loop);
+ }
+ loop_stack[loop_ptr].loop_label = cmd->c_label;
+ loop_stack[loop_ptr].loop_sp = sp;
+#ifdef DEBUGGING
+ if (debug & 4) {
+ deb("(Pushing label #%d %s)\n",
+ loop_ptr, cmd->c_label ? cmd->c_label : "");
+ }
+#endif
+ }
+#ifdef JMPCLOBBER
+ cmdparm = cmd;
+#endif
+ match = setjmp(loop_stack[loop_ptr].loop_env);
+ if (match) {
+ st = stack->ary_array; /* possibly reallocated */
+#ifdef JMPCLOBBER
+ cmd = cmdparm;
+ cmdflags = cmd->c_flags|CF_ONCE;
+ go_to = goto_targ;
+#endif
+ if (savestack->ary_fill > oldsave)
+ restorelist(oldsave);
+ switch (match) {
+ default:
+ fatal("longjmp returned bad value (%d)",match);
+ case O_LAST:
+ if (lastretstr) {
+ retstr = lastretstr;
+ newsp = -2;
+ }
+ else {
+ newsp = sp + lastsize;
+ retstr = st[newsp];
+ }
+ curspat = oldspat;
+ goto next_cmd;
+ case O_NEXT:
+#ifdef JMPCLOBBER
+ newsp = -2;
+ retstr = &str_undef;
+#endif
+ goto next_iter;
+ case O_REDO:
+#ifdef DEBUGGING
+ dlevel = olddlevel;
+#endif
+#ifdef JMPCLOBBER
+ newsp = -2;
+ retstr = &str_undef;
+#endif
+ goto doit;
+ }
+ }
+ oldspat = curspat;
+ oldsave = savestack->ary_fill;
+#ifdef DEBUGGING
+ olddlevel = dlevel;
+#endif
+ doit:
+ if (cmd->ucmd.ccmd.cc_true) {
+#ifdef DEBUGGING
+ if (debug) {
+ debname[dlevel] = 't';
+ debdelim[dlevel] = '_';
+ if (++dlevel >= dlmax)
+ grow_dlevel();
+ }
+#endif
+ newsp = cmd_exec(cmd->ucmd.ccmd.cc_true,gimme && (cmdflags & CF_TERM),sp);
+ st = stack->ary_array; /* possibly reallocated */
+ retstr = st[newsp];
+ }
+ /* actually, this spot is rarely reached anymore since the above
+ * cmd_exec() returns through longjmp(). Hooray for structure.
+ */
+ next_iter:
+#ifdef DEBUGGING
+ dlevel = olddlevel;
+#endif
+ if (cmd->ucmd.ccmd.cc_alt) {
+#ifdef DEBUGGING
+ if (debug) {
+ debname[dlevel] = 'a';
+ debdelim[dlevel] = '_';
+ if (++dlevel >= dlmax)
+ grow_dlevel();
+ }
+#endif
+ newsp = cmd_exec(cmd->ucmd.ccmd.cc_alt,gimme && (cmdflags & CF_TERM),sp);
+ st = stack->ary_array; /* possibly reallocated */
+ retstr = st[newsp];
+ }
+ finish_while:
+ curspat = oldspat;
+ if (savestack->ary_fill > oldsave) {
+ if (cmdflags & CF_TERM) {
+ for (match = sp + 1; match <= newsp; match++)
+ st[match] = str_mortal(st[match]);
+ retstr = st[newsp];
+ }
+ restorelist(oldsave);
+ }
+#ifdef DEBUGGING
+ dlevel = olddlevel - 1;
+#endif
+ if (cmd->c_type != C_BLOCK)
+ goto until_loop; /* go back and evaluate conditional again */
+ }
+ if (cmdflags & CF_LOOP) {
+ cmdflags |= CF_COND; /* now test the condition */
+#ifdef DEBUGGING
+ dlevel = entdlevel;
+#endif
+ goto until_loop;
+ }
+ next_cmd:
+ if (cmdflags & CF_ONCE) {
+#ifdef DEBUGGING
+ if (debug & 4) {
+ tmps = loop_stack[loop_ptr].loop_label;
+ deb("(Popping label #%d %s)\n",loop_ptr, tmps ? tmps : "");
+ }
+#endif
+ loop_ptr--;
+ if ((cmdflags & CF_OPTIMIZE) == CFT_ARRAY &&
+ savestack->ary_fill > aryoptsave)
+ restorelist(aryoptsave);
+ }
+ cmd = cmd->c_next;
+ goto tail_recursion_entry;
+}
+
+#ifdef DEBUGGING
+# ifndef I_VARARGS
+/*VARARGS1*/
+void deb(pat,a1,a2,a3,a4,a5,a6,a7,a8)
+char *pat;
+{
+ register int i;
+
+ fprintf(stderr,"%-4ld",(long)curcmd->c_line);
+ for (i=0; i<dlevel; i++)
+ fprintf(stderr,"%c%c ",debname[i],debdelim[i]);
+ fprintf(stderr,pat,a1,a2,a3,a4,a5,a6,a7,a8);
+}
+# else
+/*VARARGS1*/
+void deb(va_alist)
+va_dcl
+{
+ va_list args;
+ char *pat;
+ register int i;
+
+ va_start(args);
+ fprintf(stderr,"%-4ld",(long)curcmd->c_line);
+ for (i=0; i<dlevel; i++)
+ fprintf(stderr,"%c%c ",debname[i],debdelim[i]);
+
+ pat = va_arg(args, char *);
+ (void) vfprintf(stderr,pat,args);
+ va_end( args );
+}
+# endif
+#endif
+
+int
+copyopt(cmd,which)
+register CMD *cmd;
+register CMD *which;
+{
+ cmd->c_flags &= CF_ONCE|CF_COND|CF_LOOP;
+ cmd->c_flags |= which->c_flags;
+ cmd->c_short = which->c_short;
+ cmd->c_slen = which->c_slen;
+ cmd->c_stab = which->c_stab;
+ return cmd->c_flags;
+}
+
+ARRAY *
+saveary(stab)
+STAB *stab;
+{
+ register STR *str;
+
+ str = Str_new(10,0);
+ str->str_state = SS_SARY;
+ str->str_u.str_stab = stab;
+ if (str->str_ptr) {
+ Safefree(str->str_ptr);
+ str->str_ptr = Nullch;
+ str->str_len = 0;
+ }
+ str->str_ptr = (char*)stab_array(stab);
+ (void)apush(savestack,str); /* save array ptr */
+ stab_xarray(stab) = Null(ARRAY*);
+ return stab_xarray(aadd(stab));
+}
+
+HASH *
+savehash(stab)
+STAB *stab;
+{
+ register STR *str;
+
+ str = Str_new(11,0);
+ str->str_state = SS_SHASH;
+ str->str_u.str_stab = stab;
+ if (str->str_ptr) {
+ Safefree(str->str_ptr);
+ str->str_ptr = Nullch;
+ str->str_len = 0;
+ }
+ str->str_ptr = (char*)stab_hash(stab);
+ (void)apush(savestack,str); /* save hash ptr */
+ stab_xhash(stab) = Null(HASH*);
+ return stab_xhash(hadd(stab));
+}
+
+void
+saveitem(item)
+register STR *item;
+{
+ register STR *str;
+
+ (void)apush(savestack,item); /* remember the pointer */
+ str = Str_new(12,0);
+ str_sset(str,item);
+ (void)apush(savestack,str); /* remember the value */
+}
+
+void
+saveint(intp)
+int *intp;
+{
+ register STR *str;
+
+ str = Str_new(13,0);
+ str->str_state = SS_SINT;
+ str->str_u.str_useful = (long)*intp; /* remember value */
+ if (str->str_ptr) {
+ Safefree(str->str_ptr);
+ str->str_len = 0;
+ }
+ str->str_ptr = (char*)intp; /* remember pointer */
+ (void)apush(savestack,str);
+}
+
+void
+savelong(longp)
+long *longp;
+{
+ register STR *str;
+
+ str = Str_new(14,0);
+ str->str_state = SS_SLONG;
+ str->str_u.str_useful = *longp; /* remember value */
+ if (str->str_ptr) {
+ Safefree(str->str_ptr);
+ str->str_len = 0;
+ }
+ str->str_ptr = (char*)longp; /* remember pointer */
+ (void)apush(savestack,str);
+}
+
+void
+savesptr(sptr)
+STR **sptr;
+{
+ register STR *str;
+
+ str = Str_new(15,0);
+ str->str_state = SS_SSTRP;
+ str->str_magic = *sptr; /* remember value */
+ if (str->str_ptr) {
+ Safefree(str->str_ptr);
+ str->str_len = 0;
+ }
+ str->str_ptr = (char*)sptr; /* remember pointer */
+ (void)apush(savestack,str);
+}
+
+void
+savenostab(stab)
+STAB *stab;
+{
+ register STR *str;
+
+ str = Str_new(16,0);
+ str->str_state = SS_SNSTAB;
+ str->str_magic = (STR*)stab; /* remember which stab to free */
+ (void)apush(savestack,str);
+}
+
+void
+savehptr(hptr)
+HASH **hptr;
+{
+ register STR *str;
+
+ str = Str_new(17,0);
+ str->str_state = SS_SHPTR;
+ str->str_u.str_hash = *hptr; /* remember value */
+ if (str->str_ptr) {
+ Safefree(str->str_ptr);
+ str->str_len = 0;
+ }
+ str->str_ptr = (char*)hptr; /* remember pointer */
+ (void)apush(savestack,str);
+}
+
+void
+saveaptr(aptr)
+ARRAY **aptr;
+{
+ register STR *str;
+
+ str = Str_new(17,0);
+ str->str_state = SS_SAPTR;
+ str->str_u.str_array = *aptr; /* remember value */
+ if (str->str_ptr) {
+ Safefree(str->str_ptr);
+ str->str_len = 0;
+ }
+ str->str_ptr = (char*)aptr; /* remember pointer */
+ (void)apush(savestack,str);
+}
+
+void
+savelist(sarg,maxsarg)
+register STR **sarg;
+int maxsarg;
+{
+ register STR *str;
+ register int i;
+
+ for (i = 1; i <= maxsarg; i++) {
+ (void)apush(savestack,sarg[i]); /* remember the pointer */
+ str = Str_new(18,0);
+ str_sset(str,sarg[i]);
+ (void)apush(savestack,str); /* remember the value */
+ sarg[i]->str_u.str_useful = -1;
+ }
+}
+
+void
+restorelist(base)
+int base;
+{
+ register STR *str;
+ register STR *value;
+ register STAB *stab;
+
+ if (base < -1)
+ fatal("panic: corrupt saved stack index");
+ while (savestack->ary_fill > base) {
+ value = apop(savestack);
+ switch (value->str_state) {
+ case SS_NORM: /* normal string */
+ case SS_INCR:
+ str = apop(savestack);
+ str_replace(str,value);
+ STABSET(str);
+ break;
+ case SS_SARY: /* array reference */
+ stab = value->str_u.str_stab;
+ afree(stab_xarray(stab));
+ stab_xarray(stab) = (ARRAY*)value->str_ptr;
+ value->str_ptr = Nullch;
+ str_free(value);
+ break;
+ case SS_SHASH: /* hash reference */
+ stab = value->str_u.str_stab;
+ (void)hfree(stab_xhash(stab), FALSE);
+ stab_xhash(stab) = (HASH*)value->str_ptr;
+ value->str_ptr = Nullch;
+ str_free(value);
+ break;
+ case SS_SINT: /* int reference */
+ *((int*)value->str_ptr) = (int)value->str_u.str_useful;
+ value->str_ptr = Nullch;
+ str_free(value);
+ break;
+ case SS_SLONG: /* long reference */
+ *((long*)value->str_ptr) = value->str_u.str_useful;
+ value->str_ptr = Nullch;
+ str_free(value);
+ break;
+ case SS_SSTRP: /* STR* reference */
+ *((STR**)value->str_ptr) = value->str_magic;
+ value->str_magic = Nullstr;
+ value->str_ptr = Nullch;
+ str_free(value);
+ break;
+ case SS_SHPTR: /* HASH* reference */
+ *((HASH**)value->str_ptr) = value->str_u.str_hash;
+ value->str_ptr = Nullch;
+ str_free(value);
+ break;
+ case SS_SAPTR: /* ARRAY* reference */
+ *((ARRAY**)value->str_ptr) = value->str_u.str_array;
+ value->str_ptr = Nullch;
+ str_free(value);
+ break;
+ case SS_SNSTAB:
+ stab = (STAB*)value->str_magic;
+ value->str_magic = Nullstr;
+ (void)stab_clear(stab);
+ str_free(value);
+ break;
+ case SS_SCSV: /* callsave structure */
+ {
+ CSV *csv = (CSV*) value->str_ptr;
+
+ curcmd = csv->curcmd;
+ curcsv = csv->curcsv;
+ csv->sub->depth = csv->depth;
+ if (csv->hasargs) { /* put back old @_ */
+ afree(csv->argarray);
+ stab_xarray(defstab) = csv->savearray;
+ }
+ str_free(value);
+ }
+ break;
+ default:
+ fatal("panic: restorelist inconsistency");
+ }
+ }
+}
+
+#ifdef DEBUGGING
+void
+grow_dlevel()
+{
+ dlmax += 128;
+ Renew(debname, dlmax, char);
+ Renew(debdelim, dlmax, char);
+}
+#endif
diff --git a/gnu/usr.bin/perl/perl/cmd.h b/gnu/usr.bin/perl/perl/cmd.h
new file mode 100644
index 0000000..76ef5c8
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/cmd.h
@@ -0,0 +1,179 @@
+/* $RCSfile: cmd.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:35 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: cmd.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:35 nate
+ * PERL!
+ *
+ * Revision 4.0.1.2 92/06/08 12:01:02 lwall
+ * patch20: removed implicit int declarations on funcions
+ *
+ * Revision 4.0.1.1 91/06/07 10:28:50 lwall
+ * patch4: new copyright notice
+ * patch4: length($`), length($&), length($') now optimized to avoid string copy
+ *
+ * Revision 4.0 91/03/20 01:04:34 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#define C_NULL 0
+#define C_IF 1
+#define C_ELSE 2
+#define C_WHILE 3
+#define C_BLOCK 4
+#define C_EXPR 5
+#define C_NEXT 6
+#define C_ELSIF 7 /* temporary--turns into an IF + ELSE */
+#define C_CSWITCH 8 /* created by switch optimization in block_head() */
+#define C_NSWITCH 9 /* likewise */
+
+#ifdef DEBUGGING
+#ifndef DOINIT
+extern char *cmdname[];
+#else
+char *cmdname[] = {
+ "NULL",
+ "IF",
+ "ELSE",
+ "WHILE",
+ "BLOCK",
+ "EXPR",
+ "NEXT",
+ "ELSIF",
+ "CSWITCH",
+ "NSWITCH",
+ "10"
+};
+#endif
+#endif /* DEBUGGING */
+
+#define CF_OPTIMIZE 077 /* type of optimization */
+#define CF_FIRSTNEG 0100/* conditional is ($register NE 'string') */
+#define CF_NESURE 0200 /* if short doesn't match we're sure */
+#define CF_EQSURE 0400 /* if short does match we're sure */
+#define CF_COND 01000 /* test c_expr as conditional first, if not null. */
+ /* Set for everything except do {} while currently */
+#define CF_LOOP 02000 /* loop on the c_expr conditional (loop modifiers) */
+#define CF_INVERT 04000 /* it's an "unless" or an "until" */
+#define CF_ONCE 010000 /* we've already pushed the label on the stack */
+#define CF_FLIP 020000 /* on a match do flipflop */
+#define CF_TERM 040000 /* value of this cmd might be returned */
+#define CF_DBSUB 0100000 /* this is an inserted cmd for debugging */
+
+#define CFT_FALSE 0 /* c_expr is always false */
+#define CFT_TRUE 1 /* c_expr is always true */
+#define CFT_REG 2 /* c_expr is a simple register */
+#define CFT_ANCHOR 3 /* c_expr is an anchored search /^.../ */
+#define CFT_STROP 4 /* c_expr is a string comparison */
+#define CFT_SCAN 5 /* c_expr is an unanchored search /.../ */
+#define CFT_GETS 6 /* c_expr is <filehandle> */
+#define CFT_EVAL 7 /* c_expr is not optimized, so call eval() */
+#define CFT_UNFLIP 8 /* 2nd half of range not optimized */
+#define CFT_CHOP 9 /* c_expr is a chop on a register */
+#define CFT_ARRAY 10 /* this is a foreach loop */
+#define CFT_INDGETS 11 /* c_expr is <$variable> */
+#define CFT_NUMOP 12 /* c_expr is a numeric comparison */
+#define CFT_CCLASS 13 /* c_expr must start with one of these characters */
+#define CFT_D0 14 /* no special breakpoint at this line */
+#define CFT_D1 15 /* possible special breakpoint at this line */
+
+#ifdef DEBUGGING
+#ifndef DOINIT
+extern char *cmdopt[];
+#else
+char *cmdopt[] = {
+ "FALSE",
+ "TRUE",
+ "REG",
+ "ANCHOR",
+ "STROP",
+ "SCAN",
+ "GETS",
+ "EVAL",
+ "UNFLIP",
+ "CHOP",
+ "ARRAY",
+ "INDGETS",
+ "NUMOP",
+ "CCLASS",
+ "14"
+};
+#endif
+#endif /* DEBUGGING */
+
+struct acmd {
+ STAB *ac_stab; /* a symbol table entry */
+ ARG *ac_expr; /* any associated expression */
+};
+
+struct ccmd {
+ CMD *cc_true; /* normal code to do on if and while */
+ CMD *cc_alt; /* else cmd ptr or continue code */
+};
+
+struct scmd {
+ CMD **sc_next; /* array of pointers to commands */
+ short sc_offset; /* first value - 1 */
+ short sc_max; /* last value + 1 */
+};
+
+struct cmd {
+ CMD *c_next; /* the next command at this level */
+ ARG *c_expr; /* conditional expression */
+ CMD *c_head; /* head of this command list */
+ STR *c_short; /* string to match as shortcut */
+ STAB *c_stab; /* a symbol table entry, mostly for fp */
+ SPAT *c_spat; /* pattern used by optimization */
+ char *c_label; /* label for this construct */
+ union ucmd {
+ struct acmd acmd; /* normal command */
+ struct ccmd ccmd; /* compound command */
+ struct scmd scmd; /* switch command */
+ } ucmd;
+ short c_slen; /* len of c_short, if not null */
+ VOLATILE short c_flags; /* optimization flags--see above */
+ HASH *c_stash; /* package line was compiled in */
+ STAB *c_filestab; /* file the following line # is from */
+ line_t c_line; /* line # of this command */
+ char c_type; /* what this command does */
+};
+
+#define Nullcmd Null(CMD*)
+#define Nullcsv Null(CSV*)
+
+EXT CMD * VOLATILE main_root INIT(Nullcmd);
+EXT CMD * VOLATILE eval_root INIT(Nullcmd);
+
+EXT CMD compiling;
+EXT CMD * VOLATILE curcmd INIT(&compiling);
+EXT CSV * VOLATILE curcsv INIT(Nullcsv);
+
+struct callsave {
+ SUBR *sub;
+ STAB *stab;
+ CSV *curcsv;
+ CMD *curcmd;
+ ARRAY *savearray;
+ ARRAY *argarray;
+ long depth;
+ int wantarray;
+ char hasargs;
+};
+
+struct compcmd {
+ CMD *comp_true;
+ CMD *comp_alt;
+};
+
+void opt_arg();
+ARG* evalstatic();
+int cmd_exec();
+#ifdef DEBUGGING
+void deb();
+#endif
+int copyopt();
diff --git a/gnu/usr.bin/perl/perl/config.H b/gnu/usr.bin/perl/perl/config.H
new file mode 100644
index 0000000..21f635f
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/config.H
@@ -0,0 +1,892 @@
+#ifndef config_h
+#define config_h
+/* config.h
+ * This file was produced by running the config.h.SH script, which
+ * gets its values from config.sh, which is generally produced by
+ * running Configure.
+ *
+ * Feel free to modify any of this as the need arises. Note, however,
+ * that running config.h.SH again will wipe out any changes you've made.
+ * For a more permanent change edit config.sh and rerun config.h.SH.
+ */
+ /*SUPPRESS 460*/
+
+
+/* EUNICE
+ * This symbol, if defined, indicates that the program is being compiled
+ * under the EUNICE package under VMS. The program will need to handle
+ * things like files that don't go away the first time you unlink them,
+ * due to version numbering. It will also need to compensate for lack
+ * of a respectable link() command.
+ */
+/* VMS
+ * This symbol, if defined, indicates that the program is running under
+ * VMS. It is currently only set in conjunction with the EUNICE symbol.
+ */
+/*#undef EUNICE /**/
+/*#undef VMS /**/
+
+/* LOC_SED
+ * This symbol holds the complete pathname to the sed program.
+ */
+#define LOC_SED "/bin/sed" /**/
+
+/* ALIGN_BYTES
+ * This symbol contains the number of bytes required to align a double.
+ * Usual values are 2, 4, and 8.
+ */
+#define ALIGN_BYTES 8 /**/
+
+/* BIN
+ * This symbol holds the name of the directory in which the user wants
+ * to keep publicly executable images for the package in question. It
+ * is most often a local directory such as /usr/local/bin.
+ */
+#define BIN "/usr/local/bin" /**/
+
+/* BYTEORDER
+ * This symbol contains an encoding of the order of bytes in a long.
+ * Usual values (in hex) are 0x1234, 0x4321, 0x2143, 0x3412...
+ */
+#define BYTEORDER 0x4321 /**/
+
+/* CPPSTDIN
+ * This symbol contains the first part of the string which will invoke
+ * the C preprocessor on the standard input and produce to standard
+ * output. Typical value of "cc -E" or "/lib/cpp".
+ */
+/* CPPMINUS
+ * This symbol contains the second part of the string which will invoke
+ * the C preprocessor on the standard input and produce to standard
+ * output. This symbol will have the value "-" if CPPSTDIN needs a minus
+ * to specify standard input, otherwise the value is "".
+ */
+#define CPPSTDIN "/usr/lib/cpp"
+#define CPPMINUS ""
+
+/* HAS_BCMP
+ * This symbol, if defined, indicates that the bcmp routine is available
+ * to compare blocks of memory. If undefined, use memcmp. If that's
+ * not available, roll your own.
+ */
+#define HAS_BCMP /**/
+
+/* HAS_BCOPY
+ * This symbol, if defined, indicates that the bcopy routine is available
+ * to copy blocks of memory. Otherwise you should probably use memcpy().
+ * If neither is defined, roll your own.
+ */
+/* SAFE_BCOPY
+ * This symbol, if defined, indicates that the bcopy routine is available
+ * to copy potentially overlapping copy blocks of bcopy. Otherwise you
+ * should probably use memmove() or memcpy(). If neither is defined,
+ * roll your own.
+ */
+#define HAS_BCOPY /**/
+#define SAFE_BCOPY /**/
+
+/* HAS_BZERO
+ * This symbol, if defined, indicates that the bzero routine is available
+ * to zero blocks of memory. Otherwise you should probably use memset()
+ * or roll your own.
+ */
+#define HAS_BZERO /**/
+
+/* CASTNEGFLOAT
+ * This symbol, if defined, indicates that this C compiler knows how to
+ * cast negative or large floating point numbers to unsigned longs, ints
+ * and shorts.
+ */
+/* CASTFLAGS
+ * This symbol contains flags that say what difficulties the compiler
+ * has casting odd floating values to unsigned long:
+ * 1 = couldn't cast < 0
+ * 2 = couldn't cast >= 0x80000000
+ */
+#define CASTNEGFLOAT /**/
+#define CASTFLAGS 0 /**/
+
+/* CHARSPRINTF
+ * This symbol is defined if this system declares "char *sprintf()" in
+ * stdio.h. The trend seems to be to declare it as "int sprintf()". It
+ * is up to the package author to declare sprintf correctly based on the
+ * symbol.
+ */
+#define CHARSPRINTF /**/
+
+/* HAS_CHSIZE
+ * This symbol, if defined, indicates that the chsize routine is available
+ * to truncate files. You might need a -lx to get this routine.
+ */
+/*#undef HAS_CHSIZE /**/
+
+/* HAS_CRYPT
+ * This symbol, if defined, indicates that the crypt routine is available
+ * to encrypt passwords and the like.
+ */
+#define HAS_CRYPT /**/
+
+/* CSH
+ * This symbol, if defined, indicates that the C-shell exists.
+ * If defined, contains the full pathname of csh.
+ */
+#define CSH "/bin/csh" /**/
+
+/* DOSUID
+ * This symbol, if defined, indicates that the C program should
+ * check the script that it is executing for setuid/setgid bits, and
+ * attempt to emulate setuid/setgid on systems that have disabled
+ * setuid #! scripts because the kernel can't do it securely.
+ * It is up to the package designer to make sure that this emulation
+ * is done securely. Among other things, it should do an fstat on
+ * the script it just opened to make sure it really is a setuid/setgid
+ * script, it should make sure the arguments passed correspond exactly
+ * to the argument on the #! line, and it should not trust any
+ * subprocesses to which it must pass the filename rather than the
+ * file descriptor of the script to be executed.
+ */
+/*#undef DOSUID /**/
+
+/* HAS_DUP2
+ * This symbol, if defined, indicates that the dup2 routine is available
+ * to dup file descriptors. Otherwise you should use dup().
+ */
+#define HAS_DUP2 /**/
+
+/* HAS_FCHMOD
+ * This symbol, if defined, indicates that the fchmod routine is available
+ * to change mode of opened files. If unavailable, use chmod().
+ */
+#define HAS_FCHMOD /**/
+
+/* HAS_FCHOWN
+ * This symbol, if defined, indicates that the fchown routine is available
+ * to change ownership of opened files. If unavailable, use chown().
+ */
+#define HAS_FCHOWN /**/
+
+/* HAS_FCNTL
+ * This symbol, if defined, indicates to the C program that
+ * the fcntl() function exists.
+ */
+#define HAS_FCNTL /**/
+
+/* FLEXFILENAMES
+ * This symbol, if defined, indicates that the system supports filenames
+ * longer than 14 characters.
+ */
+#define FLEXFILENAMES /**/
+
+/* HAS_FLOCK
+ * This symbol, if defined, indicates that the flock() routine is
+ * available to do file locking.
+ */
+#define HAS_FLOCK /**/
+
+/* HAS_GETGROUPS
+ * This symbol, if defined, indicates that the getgroups() routine is
+ * available to get the list of process groups. If unavailable, multiple
+ * groups are probably not supported.
+ */
+#define HAS_GETGROUPS /**/
+
+/* HAS_GETHOSTENT
+ * This symbol, if defined, indicates that the gethostent() routine is
+ * available to lookup host names in some data base or other.
+ */
+#define HAS_GETHOSTENT /**/
+
+/* HAS_GETPGRP
+ * This symbol, if defined, indicates that the getpgrp() routine is
+ * available to get the current process group.
+ */
+#define HAS_GETPGRP /**/
+
+/* HAS_GETPGRP2
+ * This symbol, if defined, indicates that the getpgrp2() (as in DG/UX)
+ * routine is available to get the current process group.
+ */
+/*#undef HAS_GETPGRP2 /**/
+
+/* HAS_GETPRIORITY
+ * This symbol, if defined, indicates that the getpriority() routine is
+ * available to get a process's priority.
+ */
+#define HAS_GETPRIORITY /**/
+
+/* HAS_HTONS
+ * This symbol, if defined, indicates that the htons routine (and friends)
+ * are available to do network order byte swapping.
+ */
+/* HAS_HTONL
+ * This symbol, if defined, indicates that the htonl routine (and friends)
+ * are available to do network order byte swapping.
+ */
+/* HAS_NTOHS
+ * This symbol, if defined, indicates that the ntohs routine (and friends)
+ * are available to do network order byte swapping.
+ */
+/* HAS_NTOHL
+ * This symbol, if defined, indicates that the ntohl routine (and friends)
+ * are available to do network order byte swapping.
+ */
+#define HAS_HTONS /**/
+#define HAS_HTONL /**/
+#define HAS_NTOHS /**/
+#define HAS_NTOHL /**/
+
+/* index
+ * This preprocessor symbol is defined, along with rindex, if the system
+ * uses the strchr and strrchr routines instead.
+ */
+/* rindex
+ * This preprocessor symbol is defined, along with index, if the system
+ * uses the strchr and strrchr routines instead.
+ */
+/*#undef index strchr /* cultural */
+/*#undef rindex strrchr /* differences? */
+
+/* HAS_ISASCII
+ * This symbol, if defined, indicates that the isascii routine is available
+ * to test characters for asciiness.
+ */
+#define HAS_ISASCII /**/
+
+/* HAS_KILLPG
+ * This symbol, if defined, indicates that the killpg routine is available
+ * to kill process groups. If unavailable, you probably should use kill
+ * with a negative process number.
+ */
+#define HAS_KILLPG /**/
+
+/* HAS_LSTAT
+ * This symbol, if defined, indicates that the lstat() routine is
+ * available to stat symbolic links.
+ */
+#define HAS_LSTAT /**/
+
+/* HAS_MEMCMP
+ * This symbol, if defined, indicates that the memcmp routine is available
+ * to compare blocks of memory. If undefined, roll your own.
+ */
+#define HAS_MEMCMP /**/
+
+/* HAS_MEMCPY
+ * This symbol, if defined, indicates that the memcpy routine is available
+ * to copy blocks of memory. Otherwise you should probably use bcopy().
+ * If neither is defined, roll your own.
+ */
+/* SAFE_MEMCPY
+ * This symbol, if defined, indicates that the memcpy routine is available
+ * to copy potentially overlapping copy blocks of memory. Otherwise you
+ * should probably use memmove() or bcopy(). If neither is defined,
+ * roll your own.
+ */
+#define HAS_MEMCPY /**/
+/*#undef SAFE_MEMCPY /**/
+
+/* HAS_MEMMOVE
+ * This symbol, if defined, indicates that the memmove routine is available
+ * to move potentially overlapping blocks of memory. Otherwise you
+ * should use bcopy() or roll your own.
+ */
+/*#undef HAS_MEMMOVE /**/
+
+/* HAS_MEMSET
+ * This symbol, if defined, indicates that the memset routine is available
+ * to set a block of memory to a character. If undefined, roll your own.
+ */
+#define HAS_MEMSET /**/
+
+/* HAS_MKDIR
+ * This symbol, if defined, indicates that the mkdir routine is available
+ * to create directories. Otherwise you should fork off a new process to
+ * exec /bin/mkdir.
+ */
+#define HAS_MKDIR /**/
+
+/* HAS_MSG
+ * This symbol, if defined, indicates that the entire msg*(2) library is
+ * supported.
+ */
+#define HAS_MSG /**/
+
+/* HAS_MSGCTL
+ * This symbol, if defined, indicates that the msgctl() routine is
+ * available to control message passing.
+ */
+#define HAS_MSGCTL /**/
+
+/* HAS_MSGGET
+ * This symbol, if defined, indicates that the msgget() routine is
+ * available to get messages.
+ */
+#define HAS_MSGGET /**/
+
+/* HAS_MSGRCV
+ * This symbol, if defined, indicates that the msgrcv() routine is
+ * available to receive messages.
+ */
+#define HAS_MSGRCV /**/
+
+/* HAS_MSGSND
+ * This symbol, if defined, indicates that the msgsnd() routine is
+ * available to send messages.
+ */
+#define HAS_MSGSND /**/
+
+/* HAS_NDBM
+ * This symbol, if defined, indicates that ndbm.h exists and should
+ * be included.
+ */
+#define HAS_NDBM /**/
+
+/* HAS_ODBM
+ * This symbol, if defined, indicates that dbm.h exists and should
+ * be included.
+ */
+#define HAS_ODBM /**/
+
+/* HAS_OPEN3
+ * This manifest constant lets the C program know that the three
+ * argument form of open(2) is available.
+ */
+#define HAS_OPEN3 /**/
+
+/* HAS_READDIR
+ * This symbol, if defined, indicates that the readdir routine is available
+ * from the C library to read directories.
+ */
+#define HAS_READDIR /**/
+
+/* HAS_RENAME
+ * This symbol, if defined, indicates that the rename routine is available
+ * to rename files. Otherwise you should do the unlink(), link(), unlink()
+ * trick.
+ */
+#define HAS_RENAME /**/
+
+/* HAS_REWINDDIR
+ * This symbol, if defined, indicates that the rewindir routine is
+ * available to rewind directories.
+ */
+#define HAS_REWINDDIR /**/
+
+/* HAS_RMDIR
+ * This symbol, if defined, indicates that the rmdir routine is available
+ * to remove directories. Otherwise you should fork off a new process to
+ * exec /bin/rmdir.
+ */
+#define HAS_RMDIR /**/
+
+/* HAS_SEEKDIR
+ * This symbol, if defined, indicates that the seekdir routine is
+ * available to seek into directories.
+ */
+#define HAS_SEEKDIR /**/
+
+/* HAS_SELECT
+ * This symbol, if defined, indicates that the select() subroutine
+ * exists.
+ */
+#define HAS_SELECT /**/
+
+/* HAS_SEM
+ * This symbol, if defined, indicates that the entire sem*(2) library is
+ * supported.
+ */
+#define HAS_SEM /**/
+
+/* HAS_SEMCTL
+ * This symbol, if defined, indicates that the semctl() routine is
+ * available to control semaphores.
+ */
+#define HAS_SEMCTL /**/
+
+/* HAS_SEMGET
+ * This symbol, if defined, indicates that the semget() routine is
+ * available to get semaphores ids.
+ */
+#define HAS_SEMGET /**/
+
+/* HAS_SEMOP
+ * This symbol, if defined, indicates that the semop() routine is
+ * available to perform semaphore operations.
+ */
+#define HAS_SEMOP /**/
+
+/* HAS_SETEGID
+ * This symbol, if defined, indicates that the setegid routine is available
+ * to change the effective gid of the current program.
+ */
+#define HAS_SETEGID /**/
+
+/* HAS_SETEUID
+ * This symbol, if defined, indicates that the seteuid routine is available
+ * to change the effective uid of the current program.
+ */
+#define HAS_SETEUID /**/
+
+/* HAS_SETPGRP
+ * This symbol, if defined, indicates that the setpgrp() routine is
+ * available to set the current process group.
+ */
+#define HAS_SETPGRP /**/
+
+/* HAS_SETPGRP2
+ * This symbol, if defined, indicates that the setpgrp2() (as in DG/UX)
+ * routine is available to set the current process group.
+ */
+/*#undef HAS_SETPGRP2 /**/
+
+/* HAS_SETPRIORITY
+ * This symbol, if defined, indicates that the setpriority() routine is
+ * available to set a process's priority.
+ */
+#define HAS_SETPRIORITY /**/
+
+/* HAS_SETREGID
+ * This symbol, if defined, indicates that the setregid routine is
+ * available to change the real and effective gid of the current program.
+ */
+/* HAS_SETRESGID
+ * This symbol, if defined, indicates that the setresgid routine is
+ * available to change the real, effective and saved gid of the current
+ * program.
+ */
+#define HAS_SETREGID /**/
+/*#undef HAS_SETRESGID /**/
+
+/* HAS_SETREUID
+ * This symbol, if defined, indicates that the setreuid routine is
+ * available to change the real and effective uid of the current program.
+ */
+/* HAS_SETRESUID
+ * This symbol, if defined, indicates that the setresuid routine is
+ * available to change the real, effective and saved uid of the current
+ * program.
+ */
+#define HAS_SETREUID /**/
+/*#undef HAS_SETRESUID /**/
+
+/* HAS_SETRGID
+ * This symbol, if defined, indicates that the setrgid routine is available
+ * to change the real gid of the current program.
+ */
+#define HAS_SETRGID /**/
+
+/* HAS_SETRUID
+ * This symbol, if defined, indicates that the setruid routine is available
+ * to change the real uid of the current program.
+ */
+#define HAS_SETRUID /**/
+
+/* HAS_SHM
+ * This symbol, if defined, indicates that the entire shm*(2) library is
+ * supported.
+ */
+#define HAS_SHM /**/
+
+/* HAS_SHMAT
+ * This symbol, if defined, indicates that the shmat() routine is
+ * available to attach a shared memory segment.
+ */
+/* VOID_SHMAT
+ * This symbol, if defined, indicates that the shmat() routine
+ * returns a pointer of type void*.
+ */
+#define HAS_SHMAT /**/
+
+/*#undef VOIDSHMAT /**/
+
+/* HAS_SHMCTL
+ * This symbol, if defined, indicates that the shmctl() routine is
+ * available to control a shared memory segment.
+ */
+#define HAS_SHMCTL /**/
+
+/* HAS_SHMDT
+ * This symbol, if defined, indicates that the shmdt() routine is
+ * available to detach a shared memory segment.
+ */
+#define HAS_SHMDT /**/
+
+/* HAS_SHMGET
+ * This symbol, if defined, indicates that the shmget() routine is
+ * available to get a shared memory segment id.
+ */
+#define HAS_SHMGET /**/
+
+/* HAS_SOCKET
+ * This symbol, if defined, indicates that the BSD socket interface is
+ * supported.
+ */
+/* HAS_SOCKETPAIR
+ * This symbol, if defined, indicates that the BSD socketpair call is
+ * supported.
+ */
+/* OLDSOCKET
+ * This symbol, if defined, indicates that the 4.1c BSD socket interface
+ * is supported instead of the 4.2/4.3 BSD socket interface.
+ */
+#define HAS_SOCKET /**/
+
+#define HAS_SOCKETPAIR /**/
+
+/*#undef OLDSOCKET /**/
+
+/* STATBLOCKS
+ * This symbol is defined if this system has a stat structure declaring
+ * st_blksize and st_blocks.
+ */
+#define STATBLOCKS /**/
+
+/* STDSTDIO
+ * This symbol is defined if this system has a FILE structure declaring
+ * _ptr and _cnt in stdio.h.
+ */
+#define STDSTDIO /**/
+
+/* STRUCTCOPY
+ * This symbol, if defined, indicates that this C compiler knows how
+ * to copy structures. If undefined, you'll need to use a block copy
+ * routine of some sort instead.
+ */
+#define STRUCTCOPY /**/
+
+/* HAS_STRERROR
+ * This symbol, if defined, indicates that the strerror() routine is
+ * available to translate error numbers to strings.
+ */
+/*#undef HAS_STRERROR /**/
+
+/* HAS_SYMLINK
+ * This symbol, if defined, indicates that the symlink routine is available
+ * to create symbolic links.
+ */
+#define HAS_SYMLINK /**/
+
+/* HAS_SYSCALL
+ * This symbol, if defined, indicates that the syscall routine is available
+ * to call arbitrary system calls. If undefined, that's tough.
+ */
+#define HAS_SYSCALL /**/
+
+/* HAS_TELLDIR
+ * This symbol, if defined, indicates that the telldir routine is
+ * available to tell your location in directories.
+ */
+#define HAS_TELLDIR /**/
+
+/* HAS_TRUNCATE
+ * This symbol, if defined, indicates that the truncate routine is
+ * available to truncate files.
+ */
+#define HAS_TRUNCATE /**/
+
+/* HAS_VFORK
+ * This symbol, if defined, indicates that vfork() exists.
+ */
+#define HAS_VFORK /**/
+
+/* VOIDSIG
+ * This symbol is defined if this system declares "void (*signal())()" in
+ * signal.h. The old way was to declare it as "int (*signal())()". It
+ * is up to the package author to declare things correctly based on the
+ * symbol.
+ */
+/* TO_SIGNAL
+ * This symbol's value is either "void" or "int", corresponding to the
+ * appropriate return "type" of a signal handler. Thus, one can declare
+ * a signal handler using "TO_SIGNAL (*handler())()", and define the
+ * handler using "TO_SIGNAL handler(sig)".
+ */
+#define VOIDSIG /**/
+#define TO_SIGNAL int /**/
+
+/* HASVOLATILE
+ * This symbol, if defined, indicates that this C compiler knows about
+ * the volatile declaration.
+ */
+/*#undef HASVOLATILE /**/
+
+/* HAS_VPRINTF
+ * This symbol, if defined, indicates that the vprintf routine is available
+ * to printf with a pointer to an argument list. If unavailable, you
+ * may need to write your own, probably in terms of _doprnt().
+ */
+/* CHARVSPRINTF
+ * This symbol is defined if this system has vsprintf() returning type
+ * (char*). The trend seems to be to declare it as "int vsprintf()". It
+ * is up to the package author to declare vsprintf correctly based on the
+ * symbol.
+ */
+#define HAS_VPRINTF /**/
+#define CHARVSPRINTF /**/
+
+/* HAS_WAIT4
+ * This symbol, if defined, indicates that wait4() exists.
+ */
+#define HAS_WAIT4 /**/
+
+/* HAS_WAITPID
+ * This symbol, if defined, indicates that waitpid() exists.
+ */
+#define HAS_WAITPID /**/
+
+/* GIDTYPE
+ * This symbol has a value like gid_t, int, ushort, or whatever type is
+ * used to declare group ids in the kernel.
+ */
+#define GIDTYPE gid_t /**/
+
+/* GROUPSTYPE
+ * This symbol has a value like gid_t, int, ushort, or whatever type is
+ * used in the return value of getgroups().
+ */
+#define GROUPSTYPE int /**/
+
+/* I_FCNTL
+ * This manifest constant tells the C program to include <fcntl.h>.
+ */
+/*#undef I_FCNTL /**/
+
+/* I_GDBM
+ * This symbol, if defined, indicates that gdbm.h exists and should
+ * be included.
+ */
+/*#undef I_GDBM /**/
+
+/* I_GRP
+ * This symbol, if defined, indicates to the C program that it should
+ * include grp.h.
+ */
+#define I_GRP /**/
+
+/* I_NETINET_IN
+ * This symbol, if defined, indicates to the C program that it should
+ * include netinet/in.h.
+ */
+/* I_SYS_IN
+ * This symbol, if defined, indicates to the C program that it should
+ * include sys/in.h.
+ */
+#define I_NETINET_IN /**/
+/*#undef I_SYS_IN /**/
+
+/* I_PWD
+ * This symbol, if defined, indicates to the C program that it should
+ * include pwd.h.
+ */
+/* PWQUOTA
+ * This symbol, if defined, indicates to the C program that struct passwd
+ * contains pw_quota.
+ */
+/* PWAGE
+ * This symbol, if defined, indicates to the C program that struct passwd
+ * contains pw_age.
+ */
+/* PWCHANGE
+ * This symbol, if defined, indicates to the C program that struct passwd
+ * contains pw_change.
+ */
+/* PWCLASS
+ * This symbol, if defined, indicates to the C program that struct passwd
+ * contains pw_class.
+ */
+/* PWEXPIRE
+ * This symbol, if defined, indicates to the C program that struct passwd
+ * contains pw_expire.
+ */
+/* PWCOMMENT
+ * This symbol, if defined, indicates to the C program that struct passwd
+ * contains pw_comment.
+ */
+#define I_PWD /**/
+/*#undef PWQUOTA /**/
+#define PWAGE /**/
+/*#undef PWCHANGE /**/
+/*#undef PWCLASS /**/
+/*#undef PWEXPIRE /**/
+#define PWCOMMENT /**/
+
+/* I_SYS_FILE
+ * This manifest constant tells the C program to include <sys/file.h>.
+ */
+#define I_SYS_FILE /**/
+
+/* I_SYSIOCTL
+ * This symbol, if defined, indicates that sys/ioctl.h exists and should
+ * be included.
+ */
+#define I_SYSIOCTL /**/
+
+/* I_TIME
+ * This symbol is defined if the program should include <time.h>.
+ */
+/* I_SYS_TIME
+ * This symbol is defined if the program should include <sys/time.h>.
+ */
+/* SYSTIMEKERNEL
+ * This symbol is defined if the program should include <sys/time.h>
+ * with KERNEL defined.
+ */
+/* I_SYS_SELECT
+ * This symbol is defined if the program should include <sys/select.h>.
+ */
+/*#undef I_TIME /**/
+#define I_SYS_TIME /**/
+/*#undef SYSTIMEKERNEL /**/
+/*#undef I_SYS_SELECT /**/
+
+/* I_UTIME
+ * This symbol, if defined, indicates to the C program that it should
+ * include utime.h.
+ */
+#define I_UTIME /**/
+
+/* I_VARARGS
+ * This symbol, if defined, indicates to the C program that it should
+ * include varargs.h.
+ */
+#define I_VARARGS /**/
+
+/* I_VFORK
+ * This symbol, if defined, indicates to the C program that it should
+ * include vfork.h.
+ */
+#define I_VFORK /**/
+
+/* INTSIZE
+ * This symbol contains the size of an int, so that the C preprocessor
+ * can make decisions based on it.
+ */
+#define INTSIZE 4 /**/
+
+/* I_DIRENT
+ * This symbol, if defined, indicates that the program should use the
+ * P1003-style directory routines, and include <dirent.h>.
+ */
+/* I_SYS_DIR
+ * This symbol, if defined, indicates that the program should use the
+ * directory functions by including <sys/dir.h>.
+ */
+/* I_NDIR
+ * This symbol, if defined, indicates that the program should include the
+ * system's version of ndir.h, rather than the one with this package.
+ */
+/* I_SYS_NDIR
+ * This symbol, if defined, indicates that the program should include the
+ * system's version of sys/ndir.h, rather than the one with this package.
+ */
+/* I_MY_DIR
+ * This symbol, if defined, indicates that the program should compile
+ * the ndir.c code provided with the package.
+ */
+/* DIRNAMLEN
+ * This symbol, if defined, indicates to the C program that the length
+ * of directory entry names is provided by a d_namlen field. Otherwise
+ * you need to do strlen() on the d_name field.
+ */
+#define I_DIRENT /**/
+/*#undef I_SYS_DIR /**/
+/*#undef I_NDIR /**/
+/*#undef I_SYS_NDIR /**/
+/*#undef I_MY_DIR /**/
+/*#undef DIRNAMLEN /**/
+
+/* MYMALLOC
+ * This symbol, if defined, indicates that we're using our own malloc.
+ */
+/* MALLOCPTRTYPE
+ * This symbol defines the kind of ptr returned by malloc and realloc.
+ */
+#define MYMALLOC /**/
+
+#define MALLOCPTRTYPE char /**/
+
+
+/* RANDBITS
+ * This symbol contains the number of bits of random number the rand()
+ * function produces. Usual values are 15, 16, and 31.
+ */
+#define RANDBITS 31 /**/
+
+/* SCRIPTDIR
+ * This symbol holds the name of the directory in which the user wants
+ * to keep publicly executable scripts for the package in question. It
+ * is often a directory that is mounted across diverse architectures.
+ */
+#define SCRIPTDIR "/usr/local/bin" /**/
+
+/* SIG_NAME
+ * This symbol contains an list of signal names in order.
+ */
+#define SIG_NAME "ZERO","HUP","INT","QUIT","ILL","TRAP","ABRT","EMT","FPE","KILL","BUS","SEGV","SYS","PIPE","ALRM","TERM","URG","STOP","TSTP","CONT","CLD","TTIN","TTOU","IO","XCPU","XFSZ","VTALRM","PROF","WINCH","LOST","USR1","USR2" /**/
+
+/* STDCHAR
+ * This symbol is defined to be the type of char used in stdio.h.
+ * It has the values "unsigned char" or "char".
+ */
+#define STDCHAR unsigned char /**/
+
+/* UIDTYPE
+ * This symbol has a value like uid_t, int, ushort, or whatever type is
+ * used to declare user ids in the kernel.
+ */
+#define UIDTYPE uid_t /**/
+
+/* VOIDHAVE
+ * This symbol indicates how much support of the void type is given by this
+ * compiler. What various bits mean:
+ *
+ * 1 = supports declaration of void
+ * 2 = supports arrays of pointers to functions returning void
+ * 4 = supports comparisons between pointers to void functions and
+ * addresses of void functions
+ *
+ * The package designer should define VOIDWANT to indicate the requirements
+ * of the package. This can be done either by #defining VOIDWANT before
+ * including config.h, or by defining voidwant in Myinit.U. If the level
+ * of void support necessary is not present, config.h defines void to "int",
+ * VOID to the empty string, and VOIDP to "char *".
+ */
+/* void
+ * This symbol is used for void casts. On implementations which support
+ * void appropriately, its value is "void". Otherwise, its value maps
+ * to "int".
+ */
+/* VOID
+ * This symbol's value is "void" if the implementation supports void
+ * appropriately. Otherwise, its value is the empty string. The primary
+ * use of this symbol is in specifying void parameter lists for function
+ * prototypes.
+ */
+/* VOIDP
+ * This symbol is used for casting generic pointers. On implementations
+ * which support void appropriately, its value is "void *". Otherwise,
+ * its value is "char *".
+ */
+#ifndef VOIDWANT
+#define VOIDWANT 7
+#endif
+#define VOIDHAVE 7
+#if (VOIDHAVE & VOIDWANT) != VOIDWANT
+#define void int /* is void to be avoided? */
+#define VOID
+#define VOIDP (char *)
+#define M_VOID /* Xenix strikes again */
+#else
+#define VOID void
+#define VOIDP (void *)
+#endif
+
+/* PRIVLIB
+ * This symbol contains the name of the private library for this package.
+ * The library is private in the sense that it needn't be in anyone's
+ * execution path, but it should be accessible by the world. The program
+ * should be prepared to do ~ expansion.
+ */
+#define PRIVLIB "/usr/local/lib/perl" /**/
+
+#endif
diff --git a/gnu/usr.bin/perl/perl/config.h b/gnu/usr.bin/perl/perl/config.h
new file mode 100644
index 0000000..1135ed0
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/config.h
@@ -0,0 +1,769 @@
+#ifndef config_h
+#define config_h
+/* config.h
+ * This file was produced by running the config.h.SH script, which
+ * gets its values from config.sh, which is generally produced by
+ * running Configure.
+ *
+ * Feel free to modify any of this as the need arises. Note, however,
+ * that running config.h.SH again will wipe out any changes you've made.
+ * For a more permanent change edit config.sh and rerun config.h.SH.
+ */
+ /*SUPPRESS 460*/
+
+
+/*#undef EUNICE */
+/*#undef VMS */
+
+/* LOC_SED
+ * This symbol holds the complete pathname to the sed program.
+ */
+#define LOC_SED "/usr/bin/sed" /**/
+
+/* ALIGN_BYTES
+ * This symbol contains the number of bytes required to align a double.
+ * Usual values are 2, 4, and 8.
+ */
+#define ALIGN_BYTES 4 /**/
+
+/* BIN
+ * This symbol holds the name of the directory in which the user wants
+ * to keep publicly executable images for the package in question. It
+ * is most often a local directory such as /usr/local/bin.
+ */
+#define BIN "/usr/local/bin" /**/
+
+/* BYTEORDER
+ * This symbol contains an encoding of the order of bytes in a long.
+ * Usual values (in hex) are 0x1234, 0x4321, 0x2143, 0x3412...
+ */
+#define BYTEORDER 0x1234 /**/
+
+/* CPPSTDIN
+ * This symbol contains the first part of the string which will invoke
+ * the C preprocessor on the standard input and produce to standard
+ * output. Typical value of "cc -E" or "/lib/cpp".
+ */
+/* CPPMINUS
+ * This symbol contains the second part of the string which will invoke
+ * the C preprocessor on the standard input and produce to standard
+ * output. This symbol will have the value "-" if CPPSTDIN needs a minus
+ * to specify standard input, otherwise the value is "".
+ */
+#define CPPSTDIN "cc -E"
+#define CPPMINUS "-"
+
+/* HAS_BCMP
+ * This symbol, if defined, indicates that the bcmp routine is available
+ * to compare blocks of memory. If undefined, use memcmp. If that's
+ * not available, roll your own.
+ */
+#define HAS_BCMP /**/
+
+/* HAS_BCOPY
+ * This symbol, if defined, indicates that the bcopy routine is available
+ * to copy blocks of memory. Otherwise you should probably use memcpy().
+ * If neither is defined, roll your own.
+ */
+/* SAFE_BCOPY
+ * This symbol, if defined, indicates that the bcopy routine is available
+ * to copy potentially overlapping copy blocks of bcopy. Otherwise you
+ * should probably use memmove() or memcpy(). If neither is defined,
+ * roll your own.
+ */
+#define HAS_BCOPY /**/
+#define SAFE_BCOPY /**/
+
+/* HAS_BZERO
+ * This symbol, if defined, indicates that the bzero routine is available
+ * to zero blocks of memory. Otherwise you should probably use memset()
+ * or roll your own.
+ */
+#define HAS_BZERO /**/
+
+/* CASTNEGFLOAT
+ * This symbol, if defined, indicates that this C compiler knows how to
+ * cast negative or large floating point numbers to unsigned longs, ints
+ * and shorts.
+ */
+/* CASTFLAGS
+ * This symbol contains flags that say what difficulties the compiler
+ * has casting odd floating values to unsigned long:
+ * 1 = couldn't cast < 0
+ * 2 = couldn't cast >= 0x80000000
+ */
+#define CASTNEGFLOAT /**/
+#define CASTFLAGS 0 /**/
+
+/* CHARSPRINTF
+ * This symbol is defined if this system declares "char *sprintf()" in
+ * stdio.h. The trend seems to be to declare it as "int sprintf()". It
+ * is up to the package author to declare sprintf correctly based on the
+ * symbol.
+ */
+/*#undef CHARSPRINTF */
+
+/* HAS_CHSIZE
+ * This symbol, if defined, indicates that the chsize routine is available
+ * to truncate files. You might need a -lx to get this routine.
+ */
+/*#undef HAS_CHSIZE */
+
+/* HAS_CRYPT
+ * This symbol, if defined, indicates that the crypt routine is available
+ * to encrypt passwords and the like.
+ */
+#define HAS_CRYPT /**/
+
+/* CSH
+ * This symbol, if defined, indicates that the C-shell exists.
+ * If defined, contains the full pathname of csh.
+ */
+#define CSH "/bin/csh" /**/
+
+/* DOSUID
+ * This symbol, if defined, indicates that the C program should
+ * check the script that it is executing for setuid/setgid bits, and
+ * attempt to emulate setuid/setgid on systems that have disabled
+ * setuid #! scripts because the kernel can't do it securely.
+ * It is up to the package designer to make sure that this emulation
+ * is done securely. Among other things, it should do an fstat on
+ * the script it just opened to make sure it really is a setuid/setgid
+ * script, it should make sure the arguments passed correspond exactly
+ * to the argument on the #! line, and it should not trust any
+ * subprocesses to which it must pass the filename rather than the
+ * file descriptor of the script to be executed.
+ */
+/*#undef DOSUID */
+
+/* HAS_DUP2
+ * This symbol, if defined, indicates that the dup2 routine is available
+ * to dup file descriptors. Otherwise you should use dup().
+ */
+#define HAS_DUP2 /**/
+
+/* HAS_FCHMOD
+ * This symbol, if defined, indicates that the fchmod routine is available
+ * to change mode of opened files. If unavailable, use chmod().
+ */
+#define HAS_FCHMOD /**/
+
+/* HAS_FCHOWN
+ * This symbol, if defined, indicates that the fchown routine is available
+ * to change ownership of opened files. If unavailable, use chown().
+ */
+#define HAS_FCHOWN /**/
+
+/* HAS_FCNTL
+ * This symbol, if defined, indicates to the C program that
+ * the fcntl() function exists.
+ */
+#define HAS_FCNTL /**/
+
+/* FLEXFILENAMES
+ * This symbol, if defined, indicates that the system supports filenames
+ * longer than 14 characters.
+ */
+#define FLEXFILENAMES /**/
+
+/* HAS_FLOCK
+ * This symbol, if defined, indicates that the flock() routine is
+ * available to do file locking.
+ */
+#define HAS_FLOCK /**/
+
+/* HAS_GETGROUPS
+ * This symbol, if defined, indicates that the getgroups() routine is
+ * available to get the list of process groups. If unavailable, multiple
+ * groups are probably not supported.
+ */
+#define HAS_GETGROUPS /**/
+
+/* HAS_GETHOSTENT
+ * This symbol, if defined, indicates that the gethostent() routine is
+ * available to lookup host names in some data base or other.
+ */
+/*#undef HAS_GETHOSTENT */
+
+/* HAS_GETPGRP
+ * This symbol, if defined, indicates that the getpgrp() routine is
+ * available to get the current process group.
+ */
+#define HAS_GETPGRP /**/
+
+/* HAS_GETPGRP2
+ * This symbol, if defined, indicates that the getpgrp2() (as in DG/UX)
+ * routine is available to get the current process group.
+ */
+/*#undef HAS_GETPGRP2 */
+
+/* HAS_GETPRIORITY
+ * This symbol, if defined, indicates that the getpriority() routine is
+ * available to get a process's priority.
+ */
+#define HAS_GETPRIORITY /**/
+
+/* HAS_HTONS
+ * This symbol, if defined, indicates that the htons routine (and friends)
+ * are available to do network order byte swapping.
+ */
+/* HAS_HTONL
+ * This symbol, if defined, indicates that the htonl routine (and friends)
+ * are available to do network order byte swapping.
+ */
+/* HAS_NTOHS
+ * This symbol, if defined, indicates that the ntohs routine (and friends)
+ * are available to do network order byte swapping.
+ */
+/* HAS_NTOHL
+ * This symbol, if defined, indicates that the ntohl routine (and friends)
+ * are available to do network order byte swapping.
+ */
+#define HAS_HTONS /**/
+#define HAS_HTONL /**/
+#define HAS_NTOHS /**/
+#define HAS_NTOHL /**/
+
+/* index
+ * This preprocessor symbol is defined, along with rindex, if the system
+ * uses the strchr and strrchr routines instead.
+ */
+/* rindex
+ * This preprocessor symbol is defined, along with index, if the system
+ * uses the strchr and strrchr routines instead.
+ */
+
+/* HAS_ISASCII
+ * This symbol, if defined, indicates that the isascii routine is available
+ * to test characters for asciiness.
+ */
+#define HAS_ISASCII /**/
+
+/* HAS_KILLPG
+ * This symbol, if defined, indicates that the killpg routine is available
+ * to kill process groups. If unavailable, you probably should use kill
+ * with a negative process number.
+ */
+#define HAS_KILLPG /**/
+
+/* HAS_LSTAT
+ * This symbol, if defined, indicates that the lstat() routine is
+ * available to stat symbolic links.
+ */
+#define HAS_LSTAT /**/
+
+/* HAS_MEMCMP
+ * This symbol, if defined, indicates that the memcmp routine is available
+ * to compare blocks of memory. If undefined, roll your own.
+ */
+#define HAS_MEMCMP /**/
+
+/* HAS_MEMCPY
+ * This symbol, if defined, indicates that the memcpy routine is available
+ * to copy blocks of memory. Otherwise you should probably use bcopy().
+ * If neither is defined, roll your own.
+ */
+/* SAFE_MEMCPY
+ * This symbol, if defined, indicates that the memcpy routine is available
+ * to copy potentially overlapping copy blocks of memory. Otherwise you
+ * should probably use memmove() or bcopy(). If neither is defined,
+ * roll your own.
+ */
+#define HAS_MEMCPY /**/
+#define SAFE_MEMCPY /**/
+
+/* HAS_MEMMOVE
+ * This symbol, if defined, indicates that the memmove routine is available
+ * to move potentially overlapping blocks of memory. Otherwise you
+ * should use bcopy() or roll your own.
+ */
+#define HAS_MEMMOVE /**/
+
+/* HAS_MEMSET
+ * This symbol, if defined, indicates that the memset routine is available
+ * to set a block of memory to a character. If undefined, roll your own.
+ */
+#define HAS_MEMSET /**/
+
+/* HAS_MKDIR
+ * This symbol, if defined, indicates that the mkdir routine is available
+ * to create directories. Otherwise you should fork off a new process to
+ * exec /bin/mkdir.
+ */
+#define HAS_MKDIR /**/
+
+
+/* HAS_NDBM
+ * This symbol, if defined, indicates that ndbm.h exists and should
+ * be included.
+ */
+#define HAS_NDBM /**/
+
+/* HAS_ODBM
+ * This symbol, if defined, indicates that dbm.h exists and should
+ * be included.
+ */
+
+/* HAS_OPEN3
+ * This manifest constant lets the C program know that the three
+ * argument form of open(2) is available.
+ */
+#define HAS_OPEN3 /**/
+
+/* HAS_READDIR
+ * This symbol, if defined, indicates that the readdir routine is available
+ * from the C library to read directories.
+ */
+#define HAS_READDIR /**/
+
+/* HAS_RENAME
+ * This symbol, if defined, indicates that the rename routine is available
+ * to rename files. Otherwise you should do the unlink(), link(), unlink()
+ * trick.
+ */
+#define HAS_RENAME /**/
+
+/* HAS_REWINDDIR
+ * This symbol, if defined, indicates that the rewindir routine is
+ * available to rewind directories.
+ */
+#define HAS_REWINDDIR /**/
+
+/* HAS_RMDIR
+ * This symbol, if defined, indicates that the rmdir routine is available
+ * to remove directories. Otherwise you should fork off a new process to
+ * exec /bin/rmdir.
+ */
+#define HAS_RMDIR /**/
+
+/* HAS_SEEKDIR
+ * This symbol, if defined, indicates that the seekdir routine is
+ * available to seek into directories.
+ */
+#define HAS_SEEKDIR /**/
+
+/* HAS_SELECT
+ * This symbol, if defined, indicates that the select() subroutine
+ * exists.
+ */
+#define HAS_SELECT /**/
+
+/* HAS_SETEGID
+ * This symbol, if defined, indicates that the setegid routine is available
+ * to change the effective gid of the current program.
+ */
+#define HAS_SETEGID /**/
+
+/* HAS_SETEUID
+ * This symbol, if defined, indicates that the seteuid routine is available
+ * to change the effective uid of the current program.
+ */
+#define HAS_SETEUID /**/
+
+/* HAS_SETPGRP
+ * This symbol, if defined, indicates that the setpgrp() routine is
+ * available to set the current process group.
+ */
+#define HAS_SETPGRP /**/
+
+/* HAS_SETPGRP2
+ * This symbol, if defined, indicates that the setpgrp2() (as in DG/UX)
+ * routine is available to set the current process group.
+ */
+
+/* HAS_SETPRIORITY
+ * This symbol, if defined, indicates that the setpriority() routine is
+ * available to set a process's priority.
+ */
+#define HAS_SETPRIORITY /**/
+
+/* HAS_SETREGID
+ * This symbol, if defined, indicates that the setregid routine is
+ * available to change the real and effective gid of the current program.
+ */
+/* HAS_SETRESGID
+ * This symbol, if defined, indicates that the setresgid routine is
+ * available to change the real, effective and saved gid of the current
+ * program.
+ */
+#define HAS_SETREGID /**/
+
+/* HAS_SETREUID
+ * This symbol, if defined, indicates that the setreuid routine is
+ * available to change the real and effective uid of the current program.
+ */
+/* HAS_SETRESUID
+ * This symbol, if defined, indicates that the setresuid routine is
+ * available to change the real, effective and saved uid of the current
+ * program.
+ */
+#define HAS_SETREUID /**/
+
+/* HAS_SETRGID
+ * This symbol, if defined, indicates that the setrgid routine is available
+ * to change the real gid of the current program.
+ */
+#define HAS_SETRGID /**/
+
+/* HAS_SETRUID
+ * This symbol, if defined, indicates that the setruid routine is available
+ * to change the real uid of the current program.
+ */
+#define HAS_SETRUID /**/
+
+
+/* HAS_SOCKET
+ * This symbol, if defined, indicates that the BSD socket interface is
+ * supported.
+ */
+/* HAS_SOCKETPAIR
+ * This symbol, if defined, indicates that the BSD socketpair call is
+ * supported.
+ */
+/* OLDSOCKET
+ * This symbol, if defined, indicates that the 4.1c BSD socket interface
+ * is supported instead of the 4.2/4.3 BSD socket interface.
+ */
+#define HAS_SOCKET /**/
+
+#define HAS_SOCKETPAIR /**/
+
+
+/* STATBLOCKS
+ * This symbol is defined if this system has a stat structure declaring
+ * st_blksize and st_blocks.
+ */
+#define STATBLOCKS /**/
+
+/* STDSTDIO
+ * This symbol is defined if this system has a FILE structure declaring
+ * _ptr and _cnt in stdio.h.
+ */
+
+/* STRUCTCOPY
+ * This symbol, if defined, indicates that this C compiler knows how
+ * to copy structures. If undefined, you'll need to use a block copy
+ * routine of some sort instead.
+ */
+#define STRUCTCOPY /**/
+
+/* HAS_STRERROR
+ * This symbol, if defined, indicates that the strerror() routine is
+ * available to translate error numbers to strings.
+ */
+#define HAS_STRERROR /**/
+
+/* HAS_SYMLINK
+ * This symbol, if defined, indicates that the symlink routine is available
+ * to create symbolic links.
+ */
+#define HAS_SYMLINK /**/
+
+/* HAS_SYSCALL
+ * This symbol, if defined, indicates that the syscall routine is available
+ * to call arbitrary system calls. If undefined, that's tough.
+ */
+#define HAS_SYSCALL /**/
+
+/* HAS_TELLDIR
+ * This symbol, if defined, indicates that the telldir routine is
+ * available to tell your location in directories.
+ */
+#define HAS_TELLDIR /**/
+
+/* HAS_TRUNCATE
+ * This symbol, if defined, indicates that the truncate routine is
+ * available to truncate files.
+ */
+#define HAS_TRUNCATE /**/
+
+/* HAS_VFORK
+ * This symbol, if defined, indicates that vfork() exists.
+ */
+#define HAS_VFORK /**/
+
+/* VOIDSIG
+ * This symbol is defined if this system declares "void (*signal())()" in
+ * signal.h. The old way was to declare it as "int (*signal())()". It
+ * is up to the package author to declare things correctly based on the
+ * symbol.
+ */
+/* TO_SIGNAL
+ * This symbol's value is either "void" or "int", corresponding to the
+ * appropriate return "type" of a signal handler. Thus, one can declare
+ * a signal handler using "TO_SIGNAL (*handler())()", and define the
+ * handler using "TO_SIGNAL handler(sig)".
+ */
+#define VOIDSIG /**/
+#define TO_SIGNAL int /**/
+
+/* HASVOLATILE
+ * This symbol, if defined, indicates that this C compiler knows about
+ * the volatile declaration.
+ */
+#define HASVOLATILE /**/
+
+/* HAS_VPRINTF
+ * This symbol, if defined, indicates that the vprintf routine is available
+ * to printf with a pointer to an argument list. If unavailable, you
+ * may need to write your own, probably in terms of _doprnt().
+ */
+/* CHARVSPRINTF
+ * This symbol is defined if this system has vsprintf() returning type
+ * (char*). The trend seems to be to declare it as "int vsprintf()". It
+ * is up to the package author to declare vsprintf correctly based on the
+ * symbol.
+ */
+#define HAS_VPRINTF /**/
+
+/* HAS_WAIT4
+ * This symbol, if defined, indicates that wait4() exists.
+ */
+#define HAS_WAIT4 /**/
+
+/* HAS_WAITPID
+ * This symbol, if defined, indicates that waitpid() exists.
+ */
+#define HAS_WAITPID /**/
+
+/* GIDTYPE
+ * This symbol has a value like gid_t, int, ushort, or whatever type is
+ * used to declare group ids in the kernel.
+ */
+#define GIDTYPE gid_t /**/
+
+/* GROUPSTYPE
+ * This symbol has a value like gid_t, int, ushort, or whatever type is
+ * used in the return value of getgroups().
+ */
+#define GROUPSTYPE gid_t /**/
+
+/* I_FCNTL
+ * This manifest constant tells the C program to include <fcntl.h>.
+ */
+
+/* I_GDBM
+ * This symbol, if defined, indicates that gdbm.h exists and should
+ * be included.
+ */
+
+/* I_GRP
+ * This symbol, if defined, indicates to the C program that it should
+ * include grp.h.
+ */
+#define I_GRP /**/
+
+/* I_NETINET_IN
+ * This symbol, if defined, indicates to the C program that it should
+ * include netinet/in.h.
+ */
+/* I_SYS_IN
+ * This symbol, if defined, indicates to the C program that it should
+ * include sys/in.h.
+ */
+#define I_NETINET_IN /**/
+
+/* I_PWD
+ * This symbol, if defined, indicates to the C program that it should
+ * include pwd.h.
+ */
+/* PWQUOTA
+ * This symbol, if defined, indicates to the C program that struct passwd
+ * contains pw_quota.
+ */
+/* PWAGE
+ * This symbol, if defined, indicates to the C program that struct passwd
+ * contains pw_age.
+ */
+/* PWCHANGE
+ * This symbol, if defined, indicates to the C program that struct passwd
+ * contains pw_change.
+ */
+/* PWCLASS
+ * This symbol, if defined, indicates to the C program that struct passwd
+ * contains pw_class.
+ */
+/* PWEXPIRE
+ * This symbol, if defined, indicates to the C program that struct passwd
+ * contains pw_expire.
+ */
+/* PWCOMMENT
+ * This symbol, if defined, indicates to the C program that struct passwd
+ * contains pw_comment.
+ */
+#define I_PWD /**/
+#define PWCHANGE /**/
+#define PWCLASS /**/
+#define PWEXPIRE /**/
+
+/* I_SYS_FILE
+ * This manifest constant tells the C program to include <sys/file.h>.
+ */
+#define I_SYS_FILE /**/
+
+/* I_SYSIOCTL
+ * This symbol, if defined, indicates that sys/ioctl.h exists and should
+ * be included.
+ */
+#define I_SYSIOCTL /**/
+
+/* I_TIME
+ * This symbol is defined if the program should include <time.h>.
+ */
+/* I_SYS_TIME
+ * This symbol is defined if the program should include <sys/time.h>.
+ */
+/* SYSTIMEKERNEL
+ * This symbol is defined if the program should include <sys/time.h>
+ * with KERNEL defined.
+ */
+/* I_SYS_SELECT
+ * This symbol is defined if the program should include <sys/select.h>.
+ */
+#define I_SYS_TIME /**/
+
+/* I_UTIME
+ * This symbol, if defined, indicates to the C program that it should
+ * include utime.h.
+ */
+#define I_UTIME /**/
+
+/* I_VARARGS
+ * This symbol, if defined, indicates to the C program that it should
+ * include varargs.h.
+ */
+#define I_VARARGS /**/
+
+/* I_VFORK
+ * This symbol, if defined, indicates to the C program that it should
+ * include vfork.h.
+ */
+
+/* INTSIZE
+ * This symbol contains the size of an int, so that the C preprocessor
+ * can make decisions based on it.
+ */
+#define INTSIZE 4 /**/
+
+/* I_DIRENT
+ * This symbol, if defined, indicates that the program should use the
+ * P1003-style directory routines, and include <dirent.h>.
+ */
+/* I_SYS_DIR
+ * This symbol, if defined, indicates that the program should use the
+ * directory functions by including <sys/dir.h>.
+ */
+/* I_NDIR
+ * This symbol, if defined, indicates that the program should include the
+ * system's version of ndir.h, rather than the one with this package.
+ */
+/* I_SYS_NDIR
+ * This symbol, if defined, indicates that the program should include the
+ * system's version of sys/ndir.h, rather than the one with this package.
+ */
+/* I_MY_DIR
+ * This symbol, if defined, indicates that the program should compile
+ * the ndir.c code provided with the package.
+ */
+/* DIRNAMLEN
+ * This symbol, if defined, indicates to the C program that the length
+ * of directory entry names is provided by a d_namlen field. Otherwise
+ * you need to do strlen() on the d_name field.
+ */
+#define I_DIRENT /**/
+
+/* MYMALLOC
+ * This symbol, if defined, indicates that we're using our own malloc.
+ */
+/* MALLOCPTRTYPE
+ * This symbol defines the kind of ptr returned by malloc and realloc.
+ */
+#define MYMALLOC /**/
+
+#define MALLOCPTRTYPE void /**/
+
+
+/* RANDBITS
+ * This symbol contains the number of bits of random number the rand()
+ * function produces. Usual values are 15, 16, and 31.
+ */
+#define RANDBITS 31 /**/
+
+/* SCRIPTDIR
+ * This symbol holds the name of the directory in which the user wants
+ * to keep publicly executable scripts for the package in question. It
+ * is often a directory that is mounted across diverse architectures.
+ */
+#define SCRIPTDIR "/usr/local/bin" /**/
+
+/* SIG_NAME
+ * This symbol contains an list of signal names in order.
+ */
+#define SIG_NAME "ZERO","HUP","INT","QUIT","ILL","TRAP","ABRT","EMT","FPE","KILL","BUS","SEGV","SYS","PIPE","ALRM","TERM","URG","STOP","TSTP","CONT","CHLD","TTIN","TTOU","IO","XCPU","XFSZ","VTALRM","PROF","WINCH","INFO","USR1","USR2" /**/
+
+/* STDCHAR
+ * This symbol is defined to be the type of char used in stdio.h.
+ * It has the values "unsigned char" or "char".
+ */
+#define STDCHAR char /**/
+
+/* UIDTYPE
+ * This symbol has a value like uid_t, int, ushort, or whatever type is
+ * used to declare user ids in the kernel.
+ */
+#define UIDTYPE uid_t /**/
+
+/* VOIDHAVE
+ * This symbol indicates how much support of the void type is given by this
+ * compiler. What various bits mean:
+ *
+ * 1 = supports declaration of void
+ * 2 = supports arrays of pointers to functions returning void
+ * 4 = supports comparisons between pointers to void functions and
+ * addresses of void functions
+ *
+ * The package designer should define VOIDWANT to indicate the requirements
+ * of the package. This can be done either by #defining VOIDWANT before
+ * including config.h, or by defining voidwant in Myinit.U. If the level
+ * of void support necessary is not present, config.h defines void to "int",
+ * VOID to the empty string, and VOIDP to "char *".
+ */
+/* void
+ * This symbol is used for void casts. On implementations which support
+ * void appropriately, its value is "void". Otherwise, its value maps
+ * to "int".
+ */
+/* VOID
+ * This symbol's value is "void" if the implementation supports void
+ * appropriately. Otherwise, its value is the empty string. The primary
+ * use of this symbol is in specifying void parameter lists for function
+ * prototypes.
+ */
+/* VOIDP
+ * This symbol is used for casting generic pointers. On implementations
+ * which support void appropriately, its value is "void *". Otherwise,
+ * its value is "char *".
+ */
+#ifndef VOIDWANT
+#define VOIDWANT 7
+#endif
+#define VOIDHAVE 7
+#if (VOIDHAVE & VOIDWANT) != VOIDWANT
+#define void int /* is void to be avoided? */
+#define VOID
+#define VOIDP (char *)
+#define M_VOID /* Xenix strikes again */
+#else
+#define VOID void
+#define VOIDP (void *)
+#endif
+
+/* PRIVLIB
+ * This symbol contains the name of the private library for this package.
+ * The library is private in the sense that it needn't be in anyone's
+ * execution path, but it should be accessible by the world. The program
+ * should be prepared to do ~ expansion.
+ */
+#define PRIVLIB "/usr/local/lib/perl" /**/
+
+#endif
diff --git a/gnu/usr.bin/perl/perl/config.sh b/gnu/usr.bin/perl/perl/config.sh
new file mode 100644
index 0000000..0baad6b
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/config.sh
@@ -0,0 +1,268 @@
+#!/bin/sh
+# config.sh
+# This file was produced by running the Configure script.
+d_eunice='undef'
+define='define'
+eunicefix=':'
+loclist='
+cat
+cp
+echo
+expr
+grep
+mkdir
+mv
+rm
+sed
+sort
+tr
+uniq
+'
+expr='/bin/expr'
+sed='/usr/bin/sed'
+echo='/bin/echo'
+cat='/bin/cat'
+rm='/bin/rm'
+mv='/bin/mv'
+cp='/bin/cp'
+tail=''
+tr='/usr/bin/tr'
+mkdir='/bin/mkdir'
+sort='/usr/bin/sort'
+uniq='/usr/bin/uniq'
+grep='/usr/bin/grep'
+trylist='
+Mcc
+bison
+cpp
+csh
+egrep
+line
+nroff
+perl
+test
+uname
+yacc
+'
+test='test'
+inews=''
+egrep='/usr/bin/egrep'
+more=''
+pg=''
+Mcc='Mcc'
+vi=''
+mailx=''
+mail=''
+cpp='/usr/bin/cpp'
+perl='perl'
+emacs=''
+ls=''
+rmail=''
+sendmail=''
+shar=''
+smail=''
+tbl=''
+troff=''
+nroff='/usr/bin/nroff'
+uname='uname'
+uuname=''
+line='line'
+chgrp=''
+chmod=''
+lint=''
+sleep=''
+pr=''
+tar=''
+ln=''
+lpr=''
+lp=''
+touch=''
+make=''
+date=''
+csh='/bin/csh'
+bash=''
+ksh=''
+lex=''
+flex=''
+bison='bison'
+Log='$Log'
+Header='$Header'
+Id='$Id'
+lastuname='uname: not found'
+alignbytes='4'
+bin='/usr/gnu/bin'
+installbin='/usr/gnu/bin'
+byteorder='1234'
+contains='grep'
+cppstdin='/usr/bin/cpp'
+cppminus=''
+d_bcmp='define'
+d_bcopy='define'
+d_safebcpy='define'
+d_bzero='define'
+d_castneg='define'
+castflags='0'
+d_charsprf='undef'
+d_chsize='undef'
+d_crypt='define'
+cryptlib='-lcrypt'
+d_csh='define'
+d_dosuid='define'
+d_dup2='define'
+d_fchmod='define'
+d_fchown='define'
+d_fcntl='define'
+d_flexfnam='define'
+d_flock='define'
+d_getgrps='define'
+d_gethent='undef'
+d_getpgrp='define'
+d_getpgrp2='undef'
+d_getprior='define'
+d_htonl='define'
+d_index='undef'
+d_isascii='undef'
+d_killpg='define'
+d_lstat='define'
+d_memcmp='define'
+d_memcpy='define'
+d_safemcpy='define'
+d_memmove='define'
+d_memset='define'
+d_mkdir='define'
+d_msg='define'
+d_msgctl='define'
+d_msgget='define'
+d_msgrcv='define'
+d_msgsnd='define'
+d_ndbm='define'
+d_odbm='undef'
+d_open3='define'
+d_readdir='define'
+d_rename='define'
+d_rewindir='define'
+d_rmdir='define'
+d_seekdir='define'
+d_select='define'
+d_sem='define'
+d_semctl='define'
+d_semget='define'
+d_semop='define'
+d_setegid='define'
+d_seteuid='define'
+d_setpgrp='define'
+d_setpgrp2='undef'
+d_setprior='define'
+d_setregid='define'
+d_setresgid='undef'
+d_setreuid='define'
+d_setresuid='undef'
+d_setrgid='define'
+d_setruid='define'
+d_shm='define'
+d_shmat='define'
+d_voidshmat='define'
+d_shmctl='define'
+d_shmdt='define'
+d_shmget='define'
+d_socket='define'
+d_sockpair='define'
+d_oldsock='undef'
+socketlib=''
+d_statblks='define'
+d_stdstdio='undef'
+d_strctcpy='define'
+d_strerror='define'
+d_symlink='define'
+d_syscall='define'
+d_telldir='define'
+d_truncate='define'
+d_vfork='define'
+d_voidsig='define'
+d_tosignal='int'
+d_volatile='define'
+d_vprintf='define'
+d_charvspr='undef'
+d_wait4='define'
+d_waitpid='define'
+gidtype='gid_t'
+groupstype='int'
+i_fcntl='undef'
+i_gdbm='undef'
+i_grp='define'
+i_niin='define'
+i_sysin='undef'
+i_pwd='define'
+d_pwquota='undef'
+d_pwage='undef'
+d_pwchange='define'
+d_pwclass='define'
+d_pwexpire='define'
+d_pwcomment='undef'
+i_sys_file='define'
+i_sysioctl='define'
+i_time='undef'
+i_sys_time='define'
+i_sys_select='undef'
+d_systimekernel='undef'
+i_utime='define'
+i_varargs='define'
+i_vfork='undef'
+intsize='4'
+libc='/usr/lib/libc.so.1.0'
+nm_opts=''
+libndir=''
+i_my_dir='undef'
+i_ndir='undef'
+i_sys_ndir='undef'
+i_dirent='define'
+i_sys_dir='undef'
+d_dirnamlen='define'
+ndirc=''
+ndiro=''
+mallocsrc='malloc.c'
+mallocobj='malloc.o'
+d_mymalloc='define'
+mallocptrtype='void'
+mansrc='/usr/gnu/man/man1'
+manext='1'
+models='none'
+split=''
+small=''
+medium=''
+large=''
+huge=''
+optimize='-O'
+ccflags=''
+cppflags=''
+ldflags=''
+cc='cc'
+nativegcc=''
+libs='-lm'
+n='-n'
+c=''
+package='perl'
+randbits='31'
+scriptdir='/usr/gnu/bin'
+installscr='/usr/gnu/bin'
+sig_name='ZERO HUP INT QUIT ILL TRAP ABRT EMT FPE KILL BUS SEGV SYS PIPE ALRM TERM URG STOP TSTP CONT CHLD TTIN TTOU IO XCPU XFSZ VTALRM PROF WINCH INFO USR1 USR2'
+spitshell='cat'
+shsharp='true'
+sharpbang='#!'
+startsh='#!/bin/sh'
+stdchar='char'
+uidtype='uid_t'
+usrinclude='/usr/include'
+inclPath=''
+void=''
+voidhave='7'
+voidwant='7'
+w_localtim='1'
+w_s_timevl='1'
+w_s_tm='1'
+yacc='/usr/bin/yacc'
+lib=''
+privlib='/usr/gnu/lib/perl'
+installprivlib='/usr/gnu/lib/perl'
+PATCHLEVEL=36
+CONFIG=true
diff --git a/gnu/usr.bin/perl/perl/cons.c b/gnu/usr.bin/perl/perl/cons.c
new file mode 100644
index 0000000..c926f7a
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/cons.c
@@ -0,0 +1,1450 @@
+/* $RCSfile: cons.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:35 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: cons.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:35 nate
+ * PERL!
+ *
+ * Revision 4.0.1.4 1993/02/05 19:30:15 lwall
+ * patch36: fixed various little coredump bugs
+ *
+ * Revision 4.0.1.3 92/06/08 12:18:35 lwall
+ * patch20: removed implicit int declarations on funcions
+ * patch20: deleted some minor memory leaks
+ * patch20: fixed double debug break in foreach with implicit array assignment
+ * patch20: fixed confusion between a *var's real name and its effective name
+ * patch20: Perl now distinguishes overlapped copies from non-overlapped
+ * patch20: debugger sometimes displayed wrong source line
+ * patch20: various error messages have been clarified
+ * patch20: an eval block containing a null block or statement could dump core
+ *
+ * Revision 4.0.1.2 91/11/05 16:15:13 lwall
+ * patch11: debugger got confused over nested subroutine definitions
+ * patch11: prepared for ctype implementations that don't define isascii()
+ *
+ * Revision 4.0.1.1 91/06/07 10:31:15 lwall
+ * patch4: new copyright notice
+ * patch4: added global modifier for pattern matches
+ *
+ * Revision 4.0 91/03/20 01:05:51 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "perly.h"
+
+extern char *tokename[];
+extern int yychar;
+
+static int cmd_tosave();
+static int arg_tosave();
+static int spat_tosave();
+static void make_cswitch();
+static void make_nswitch();
+
+static bool saw_return;
+
+SUBR *
+make_sub(name,cmd)
+char *name;
+CMD *cmd;
+{
+ register SUBR *sub;
+ STAB *stab = stabent(name,TRUE);
+
+ if (sub = stab_sub(stab)) {
+ if (dowarn) {
+ CMD *oldcurcmd = curcmd;
+
+ if (cmd)
+ curcmd = cmd;
+ warn("Subroutine %s redefined",name);
+ curcmd = oldcurcmd;
+ }
+ if (!sub->usersub && sub->cmd) {
+ cmd_free(sub->cmd);
+ sub->cmd = Nullcmd;
+ afree(sub->tosave);
+ }
+ Safefree(sub);
+ }
+ Newz(101,sub,1,SUBR);
+ stab_sub(stab) = sub;
+ sub->filestab = curcmd->c_filestab;
+ saw_return = FALSE;
+ tosave = anew(Nullstab);
+ tosave->ary_fill = 0; /* make 1 based */
+ (void)cmd_tosave(cmd,FALSE); /* this builds the tosave array */
+ sub->tosave = tosave;
+ if (saw_return) {
+ struct compcmd mycompblock;
+
+ mycompblock.comp_true = cmd;
+ mycompblock.comp_alt = Nullcmd;
+ cmd = add_label(savestr("_SUB_"),make_ccmd(C_BLOCK,0,
+ Nullarg,mycompblock));
+ saw_return = FALSE;
+ cmd->c_flags |= CF_TERM;
+ cmd->c_head = cmd;
+ }
+ sub->cmd = cmd;
+ if (perldb) {
+ STR *str;
+ STR *tmpstr = str_mortal(&str_undef);
+
+ sprintf(buf,"%s:%ld",stab_val(curcmd->c_filestab)->str_ptr, subline);
+ str = str_make(buf,0);
+ str_cat(str,"-");
+ sprintf(buf,"%ld",(long)curcmd->c_line);
+ str_cat(str,buf);
+ stab_efullname(tmpstr,stab);
+ hstore(stab_xhash(DBsub), tmpstr->str_ptr, tmpstr->str_cur, str, 0);
+ }
+ Safefree(name);
+ return sub;
+}
+
+SUBR *
+make_usub(name, ix, subaddr, filename)
+char *name;
+int ix;
+int (*subaddr)();
+char *filename;
+{
+ register SUBR *sub;
+ STAB *stab = stabent(name,allstabs);
+
+ if (!stab) /* unused function */
+ return Null(SUBR*);
+ if (sub = stab_sub(stab)) {
+ if (dowarn)
+ warn("Subroutine %s redefined",name);
+ if (!sub->usersub && sub->cmd) {
+ cmd_free(sub->cmd);
+ sub->cmd = Nullcmd;
+ afree(sub->tosave);
+ }
+ Safefree(sub);
+ }
+ Newz(101,sub,1,SUBR);
+ stab_sub(stab) = sub;
+ sub->filestab = fstab(filename);
+ sub->usersub = subaddr;
+ sub->userindex = ix;
+ return sub;
+}
+
+void
+make_form(stab,fcmd)
+STAB *stab;
+FCMD *fcmd;
+{
+ if (stab_form(stab)) {
+ FCMD *tmpfcmd;
+ FCMD *nextfcmd;
+
+ for (tmpfcmd = stab_form(stab); tmpfcmd; tmpfcmd = nextfcmd) {
+ nextfcmd = tmpfcmd->f_next;
+ if (tmpfcmd->f_expr)
+ arg_free(tmpfcmd->f_expr);
+ if (tmpfcmd->f_unparsed)
+ str_free(tmpfcmd->f_unparsed);
+ if (tmpfcmd->f_pre)
+ Safefree(tmpfcmd->f_pre);
+ Safefree(tmpfcmd);
+ }
+ }
+ stab_form(stab) = fcmd;
+}
+
+CMD *
+block_head(tail)
+register CMD *tail;
+{
+ CMD *head;
+ register int opt;
+ register int last_opt = 0;
+ register STAB *last_stab = Nullstab;
+ register int count = 0;
+ register CMD *switchbeg = Nullcmd;
+
+ if (tail == Nullcmd) {
+ return tail;
+ }
+ head = tail->c_head;
+
+ for (tail = head; tail; tail = tail->c_next) {
+
+ /* save one measly dereference at runtime */
+ if (tail->c_type == C_IF) {
+ if (!(tail->ucmd.ccmd.cc_alt = tail->ucmd.ccmd.cc_alt->c_next))
+ tail->c_flags |= CF_TERM;
+ }
+ else if (tail->c_type == C_EXPR) {
+ ARG *arg;
+
+ if (tail->ucmd.acmd.ac_expr)
+ arg = tail->ucmd.acmd.ac_expr;
+ else
+ arg = tail->c_expr;
+ if (arg) {
+ if (arg->arg_type == O_RETURN)
+ tail->c_flags |= CF_TERM;
+ else if (arg->arg_type == O_ITEM && arg[1].arg_type == A_CMD)
+ tail->c_flags |= CF_TERM;
+ }
+ }
+ if (!tail->c_next)
+ tail->c_flags |= CF_TERM;
+
+ if (tail->c_expr && (tail->c_flags & CF_OPTIMIZE) == CFT_FALSE)
+ opt_arg(tail,1, tail->c_type == C_EXPR);
+
+ /* now do a little optimization on case-ish structures */
+ switch(tail->c_flags & (CF_OPTIMIZE|CF_FIRSTNEG|CF_INVERT)) {
+ case CFT_ANCHOR:
+ case CFT_STROP:
+ opt = (tail->c_flags & CF_NESURE) ? CFT_STROP : 0;
+ break;
+ case CFT_CCLASS:
+ opt = CFT_STROP;
+ break;
+ case CFT_NUMOP:
+ opt = (tail->c_slen == O_NE ? 0 : CFT_NUMOP);
+ if ((tail->c_flags&(CF_NESURE|CF_EQSURE)) != (CF_NESURE|CF_EQSURE))
+ opt = 0;
+ break;
+ default:
+ opt = 0;
+ }
+ if (opt && opt == last_opt && tail->c_stab == last_stab)
+ count++;
+ else {
+ if (count >= 3) { /* is this the breakeven point? */
+ if (last_opt == CFT_NUMOP)
+ make_nswitch(switchbeg,count);
+ else
+ make_cswitch(switchbeg,count);
+ }
+ if (opt) {
+ count = 1;
+ switchbeg = tail;
+ }
+ else
+ count = 0;
+ }
+ last_opt = opt;
+ last_stab = tail->c_stab;
+ }
+ if (count >= 3) { /* is this the breakeven point? */
+ if (last_opt == CFT_NUMOP)
+ make_nswitch(switchbeg,count);
+ else
+ make_cswitch(switchbeg,count);
+ }
+ return head;
+}
+
+/* We've spotted a sequence of CMDs that all test the value of the same
+ * spat. Thus we can insert a SWITCH in front and jump directly
+ * to the correct one.
+ */
+static void
+make_cswitch(head,count)
+register CMD *head;
+int count;
+{
+ register CMD *cur;
+ register CMD **loc;
+ register int i;
+ register int min = 255;
+ register int max = 0;
+
+ /* make a new head in the exact same spot */
+ New(102,cur, 1, CMD);
+ StructCopy(head,cur,CMD);
+ Zero(head,1,CMD);
+ head->c_head = cur->c_head;
+ head->c_type = C_CSWITCH;
+ head->c_next = cur; /* insert new cmd at front of list */
+ head->c_stab = cur->c_stab;
+
+ Newz(103,loc,258,CMD*);
+ loc++; /* lie a little */
+ while (count--) {
+ if ((cur->c_flags & CF_OPTIMIZE) == CFT_CCLASS) {
+ for (i = 0; i <= 255; i++) {
+ if (!loc[i] && cur->c_short->str_ptr[i>>3] & (1 << (i&7))) {
+ loc[i] = cur;
+ if (i < min)
+ min = i;
+ if (i > max)
+ max = i;
+ }
+ }
+ }
+ else {
+ i = *cur->c_short->str_ptr & 255;
+ if (!loc[i]) {
+ loc[i] = cur;
+ if (i < min)
+ min = i;
+ if (i > max)
+ max = i;
+ }
+ }
+ cur = cur->c_next;
+ }
+ max++;
+ if (min > 0)
+ Move(&loc[min],&loc[0], max - min, CMD*);
+ loc--;
+ min--;
+ max -= min;
+ for (i = 0; i <= max; i++)
+ if (!loc[i])
+ loc[i] = cur;
+ Renew(loc,max+1,CMD*); /* chop it down to size */
+ head->ucmd.scmd.sc_offset = min;
+ head->ucmd.scmd.sc_max = max;
+ head->ucmd.scmd.sc_next = loc;
+}
+
+static void
+make_nswitch(head,count)
+register CMD *head;
+int count;
+{
+ register CMD *cur = head;
+ register CMD **loc;
+ register int i;
+ register int min = 32767;
+ register int max = -32768;
+ int origcount = count;
+ double value; /* or your money back! */
+ short changed; /* so triple your money back! */
+
+ while (count--) {
+ i = (int)str_gnum(cur->c_short);
+ value = (double)i;
+ if (value != cur->c_short->str_u.str_nval)
+ return; /* fractional values--just forget it */
+ changed = i;
+ if (changed != i)
+ return; /* too big for a short */
+ if (cur->c_slen == O_LE)
+ i++;
+ else if (cur->c_slen == O_GE) /* we only do < or > here */
+ i--;
+ if (i < min)
+ min = i;
+ if (i > max)
+ max = i;
+ cur = cur->c_next;
+ }
+ count = origcount;
+ if (max - min > count * 2 + 10) /* too sparse? */
+ return;
+
+ /* now make a new head in the exact same spot */
+ New(104,cur, 1, CMD);
+ StructCopy(head,cur,CMD);
+ Zero(head,1,CMD);
+ head->c_head = cur->c_head;
+ head->c_type = C_NSWITCH;
+ head->c_next = cur; /* insert new cmd at front of list */
+ head->c_stab = cur->c_stab;
+
+ Newz(105,loc, max - min + 3, CMD*);
+ loc++;
+ max -= min;
+ max++;
+ while (count--) {
+ i = (int)str_gnum(cur->c_short);
+ i -= min;
+ switch(cur->c_slen) {
+ case O_LE:
+ i++;
+ case O_LT:
+ for (i--; i >= -1; i--)
+ if (!loc[i])
+ loc[i] = cur;
+ break;
+ case O_GE:
+ i--;
+ case O_GT:
+ for (i++; i <= max; i++)
+ if (!loc[i])
+ loc[i] = cur;
+ break;
+ case O_EQ:
+ if (!loc[i])
+ loc[i] = cur;
+ break;
+ }
+ cur = cur->c_next;
+ }
+ loc--;
+ min--;
+ max++;
+ for (i = 0; i <= max; i++)
+ if (!loc[i])
+ loc[i] = cur;
+ head->ucmd.scmd.sc_offset = min;
+ head->ucmd.scmd.sc_max = max;
+ head->ucmd.scmd.sc_next = loc;
+}
+
+CMD *
+append_line(head,tail)
+register CMD *head;
+register CMD *tail;
+{
+ if (tail == Nullcmd)
+ return head;
+ if (!tail->c_head) /* make sure tail is well formed */
+ tail->c_head = tail;
+ if (head != Nullcmd) {
+ tail = tail->c_head; /* get to start of tail list */
+ if (!head->c_head)
+ head->c_head = head; /* start a new head list */
+ while (head->c_next) {
+ head->c_next->c_head = head->c_head;
+ head = head->c_next; /* get to end of head list */
+ }
+ head->c_next = tail; /* link to end of old list */
+ tail->c_head = head->c_head; /* propagate head pointer */
+ }
+ while (tail->c_next) {
+ tail->c_next->c_head = tail->c_head;
+ tail = tail->c_next;
+ }
+ return tail;
+}
+
+CMD *
+dodb(cur)
+CMD *cur;
+{
+ register CMD *cmd;
+ register CMD *head = cur->c_head;
+ STR *str;
+
+ if (!head)
+ head = cur;
+ if (!head->c_line)
+ return cur;
+ str = afetch(stab_xarray(curcmd->c_filestab),(int)head->c_line,FALSE);
+ if (str == &str_undef || str->str_nok)
+ return cur;
+ str->str_u.str_nval = (double)head->c_line;
+ str->str_nok = 1;
+ Newz(106,cmd,1,CMD);
+ str_magic(str, curcmd->c_filestab, 0, Nullch, 0);
+ str->str_magic->str_u.str_cmd = cmd;
+ cmd->c_type = C_EXPR;
+ cmd->ucmd.acmd.ac_stab = Nullstab;
+ cmd->ucmd.acmd.ac_expr = Nullarg;
+ cmd->c_expr = make_op(O_SUBR, 2,
+ stab2arg(A_WORD,DBstab),
+ Nullarg,
+ Nullarg);
+ /*SUPPRESS 53*/
+ cmd->c_flags |= CF_COND|CF_DBSUB|CFT_D0;
+ cmd->c_line = head->c_line;
+ cmd->c_label = head->c_label;
+ cmd->c_filestab = curcmd->c_filestab;
+ cmd->c_stash = curstash;
+ return append_line(cmd, cur);
+}
+
+CMD *
+make_acmd(type,stab,cond,arg)
+int type;
+STAB *stab;
+ARG *cond;
+ARG *arg;
+{
+ register CMD *cmd;
+
+ Newz(107,cmd,1,CMD);
+ cmd->c_type = type;
+ cmd->ucmd.acmd.ac_stab = stab;
+ cmd->ucmd.acmd.ac_expr = arg;
+ cmd->c_expr = cond;
+ if (cond)
+ cmd->c_flags |= CF_COND;
+ if (cmdline == NOLINE)
+ cmd->c_line = curcmd->c_line;
+ else {
+ cmd->c_line = cmdline;
+ cmdline = NOLINE;
+ }
+ cmd->c_filestab = curcmd->c_filestab;
+ cmd->c_stash = curstash;
+ if (perldb)
+ cmd = dodb(cmd);
+ return cmd;
+}
+
+CMD *
+make_ccmd(type,debuggable,arg,cblock)
+int type;
+int debuggable;
+ARG *arg;
+struct compcmd cblock;
+{
+ register CMD *cmd;
+
+ Newz(108,cmd, 1, CMD);
+ cmd->c_type = type;
+ cmd->c_expr = arg;
+ cmd->ucmd.ccmd.cc_true = cblock.comp_true;
+ cmd->ucmd.ccmd.cc_alt = cblock.comp_alt;
+ if (arg)
+ cmd->c_flags |= CF_COND;
+ if (cmdline == NOLINE)
+ cmd->c_line = curcmd->c_line;
+ else {
+ cmd->c_line = cmdline;
+ cmdline = NOLINE;
+ }
+ cmd->c_filestab = curcmd->c_filestab;
+ cmd->c_stash = curstash;
+ if (perldb && debuggable)
+ cmd = dodb(cmd);
+ return cmd;
+}
+
+CMD *
+make_icmd(type,arg,cblock)
+int type;
+ARG *arg;
+struct compcmd cblock;
+{
+ register CMD *cmd;
+ register CMD *alt;
+ register CMD *cur;
+ register CMD *head;
+ struct compcmd ncblock;
+
+ Newz(109,cmd, 1, CMD);
+ head = cmd;
+ cmd->c_type = type;
+ cmd->c_expr = arg;
+ cmd->ucmd.ccmd.cc_true = cblock.comp_true;
+ cmd->ucmd.ccmd.cc_alt = cblock.comp_alt;
+ if (arg)
+ cmd->c_flags |= CF_COND;
+ if (cmdline == NOLINE)
+ cmd->c_line = curcmd->c_line;
+ else {
+ cmd->c_line = cmdline;
+ cmdline = NOLINE;
+ }
+ cmd->c_filestab = curcmd->c_filestab;
+ cmd->c_stash = curstash;
+ cur = cmd;
+ alt = cblock.comp_alt;
+ while (alt && alt->c_type == C_ELSIF) {
+ cur = alt;
+ alt = alt->ucmd.ccmd.cc_alt;
+ }
+ if (alt) { /* a real life ELSE at the end? */
+ ncblock.comp_true = alt;
+ ncblock.comp_alt = Nullcmd;
+ alt = append_line(cur,make_ccmd(C_ELSE,1,Nullarg,ncblock));
+ cur->ucmd.ccmd.cc_alt = alt;
+ }
+ else
+ alt = cur; /* no ELSE, so cur is proxy ELSE */
+
+ cur = cmd;
+ while (cmd) { /* now point everyone at the ELSE */
+ cur = cmd;
+ cmd = cur->ucmd.ccmd.cc_alt;
+ cur->c_head = head;
+ if (cur->c_type == C_ELSIF)
+ cur->c_type = C_IF;
+ if (cur->c_type == C_IF)
+ cur->ucmd.ccmd.cc_alt = alt;
+ if (cur == alt)
+ break;
+ cur->c_next = cmd;
+ }
+ if (perldb)
+ cur = dodb(cur);
+ return cur;
+}
+
+void
+opt_arg(cmd,fliporflop,acmd)
+register CMD *cmd;
+int fliporflop;
+int acmd;
+{
+ register ARG *arg;
+ int opt = CFT_EVAL;
+ int sure = 0;
+ ARG *arg2;
+ int context = 0; /* 0 = normal, 1 = before &&, 2 = before || */
+ int flp = fliporflop;
+
+ if (!cmd)
+ return;
+ if (!(arg = cmd->c_expr)) {
+ cmd->c_flags &= ~CF_COND;
+ return;
+ }
+
+ /* Can we turn && and || into if and unless? */
+
+ if (acmd && !cmd->ucmd.acmd.ac_expr && !(cmd->c_flags & CF_TERM) &&
+ (arg->arg_type == O_AND || arg->arg_type == O_OR) ) {
+ dehoist(arg,1);
+ arg[2].arg_type &= A_MASK; /* don't suppress eval */
+ dehoist(arg,2);
+ cmd->ucmd.acmd.ac_expr = arg[2].arg_ptr.arg_arg;
+ cmd->c_expr = arg[1].arg_ptr.arg_arg;
+ if (arg->arg_type == O_OR)
+ cmd->c_flags ^= CF_INVERT; /* || is like unless */
+ arg->arg_len = 0;
+ free_arg(arg);
+ arg = cmd->c_expr;
+ }
+
+ /* Turn "if (!expr)" into "unless (expr)" */
+
+ if (!(cmd->c_flags & CF_TERM)) { /* unless return value wanted */
+ while (arg->arg_type == O_NOT) {
+ dehoist(arg,1);
+ cmd->c_flags ^= CF_INVERT; /* flip sense of cmd */
+ cmd->c_expr = arg[1].arg_ptr.arg_arg; /* hoist the rest of expr */
+ free_arg(arg);
+ arg = cmd->c_expr; /* here we go again */
+ }
+ }
+
+ if (!arg->arg_len) { /* sanity check */
+ cmd->c_flags |= opt;
+ return;
+ }
+
+ /* for "cond .. cond" we set up for the initial check */
+
+ if (arg->arg_type == O_FLIP)
+ context |= 4;
+
+ /* for "cond && expr" and "cond || expr" we can ignore expr, sort of */
+
+ morecontext:
+ if (arg->arg_type == O_AND)
+ context |= 1;
+ else if (arg->arg_type == O_OR)
+ context |= 2;
+ if (context && (arg[flp].arg_type & A_MASK) == A_EXPR) {
+ arg = arg[flp].arg_ptr.arg_arg;
+ flp = 1;
+ if (arg->arg_type == O_AND || arg->arg_type == O_OR)
+ goto morecontext;
+ }
+ if ((context & 3) == 3)
+ return;
+
+ if (arg[flp].arg_flags & (AF_PRE|AF_POST)) {
+ cmd->c_flags |= opt;
+ if (acmd && !cmd->ucmd.acmd.ac_expr && !(cmd->c_flags & CF_TERM)
+ && cmd->c_expr->arg_type == O_ITEM) {
+ arg[flp].arg_flags &= ~AF_POST; /* prefer ++$foo to $foo++ */
+ arg[flp].arg_flags |= AF_PRE; /* if value not wanted */
+ }
+ return; /* side effect, can't optimize */
+ }
+
+ if (arg->arg_type == O_ITEM || arg->arg_type == O_FLIP ||
+ arg->arg_type == O_AND || arg->arg_type == O_OR) {
+ if ((arg[flp].arg_type & A_MASK) == A_SINGLE) {
+ opt = (str_true(arg[flp].arg_ptr.arg_str) ? CFT_TRUE : CFT_FALSE);
+ cmd->c_short = str_smake(arg[flp].arg_ptr.arg_str);
+ goto literal;
+ }
+ else if ((arg[flp].arg_type & A_MASK) == A_STAB ||
+ (arg[flp].arg_type & A_MASK) == A_LVAL) {
+ cmd->c_stab = arg[flp].arg_ptr.arg_stab;
+ if (!context)
+ arg[flp].arg_ptr.arg_stab = Nullstab;
+ opt = CFT_REG;
+ literal:
+ if (!context) { /* no && or ||? */
+ arg_free(arg);
+ cmd->c_expr = Nullarg;
+ }
+ if (!(context & 1))
+ cmd->c_flags |= CF_EQSURE;
+ if (!(context & 2))
+ cmd->c_flags |= CF_NESURE;
+ }
+ }
+ else if (arg->arg_type == O_MATCH || arg->arg_type == O_SUBST ||
+ arg->arg_type == O_NMATCH || arg->arg_type == O_NSUBST) {
+ if ((arg[1].arg_type == A_STAB || arg[1].arg_type == A_LVAL) &&
+ (arg[2].arg_type & A_MASK) == A_SPAT &&
+ arg[2].arg_ptr.arg_spat->spat_short &&
+ (arg->arg_type == O_SUBST || arg->arg_type == O_NSUBST ||
+ (arg[2].arg_ptr.arg_spat->spat_flags & SPAT_GLOBAL) == 0 )) {
+ cmd->c_stab = arg[1].arg_ptr.arg_stab;
+ cmd->c_short = str_smake(arg[2].arg_ptr.arg_spat->spat_short);
+ cmd->c_slen = arg[2].arg_ptr.arg_spat->spat_slen;
+ if (arg[2].arg_ptr.arg_spat->spat_flags & SPAT_ALL &&
+ !(arg[2].arg_ptr.arg_spat->spat_flags & SPAT_ONCE) &&
+ (arg->arg_type == O_MATCH || arg->arg_type == O_NMATCH) )
+ sure |= CF_EQSURE; /* (SUBST must be forced even */
+ /* if we know it will work.) */
+ if (arg->arg_type != O_SUBST) {
+ str_free(arg[2].arg_ptr.arg_spat->spat_short);
+ arg[2].arg_ptr.arg_spat->spat_short = Nullstr;
+ arg[2].arg_ptr.arg_spat->spat_slen = 0; /* only one chk */
+ }
+ sure |= CF_NESURE; /* normally only sure if it fails */
+ if (arg->arg_type == O_NMATCH || arg->arg_type == O_NSUBST)
+ cmd->c_flags |= CF_FIRSTNEG;
+ if (context & 1) { /* only sure if thing is false */
+ if (cmd->c_flags & CF_FIRSTNEG)
+ sure &= ~CF_NESURE;
+ else
+ sure &= ~CF_EQSURE;
+ }
+ else if (context & 2) { /* only sure if thing is true */
+ if (cmd->c_flags & CF_FIRSTNEG)
+ sure &= ~CF_EQSURE;
+ else
+ sure &= ~CF_NESURE;
+ }
+ if (sure & (CF_EQSURE|CF_NESURE)) { /* if we know anything*/
+ if (arg[2].arg_ptr.arg_spat->spat_flags & SPAT_SCANFIRST)
+ opt = CFT_SCAN;
+ else
+ opt = CFT_ANCHOR;
+ if (sure == (CF_EQSURE|CF_NESURE) /* really sure? */
+ && arg->arg_type == O_MATCH
+ && context & 4
+ && fliporflop == 1) {
+ spat_free(arg[2].arg_ptr.arg_spat);
+ arg[2].arg_ptr.arg_spat = Nullspat; /* don't do twice */
+ }
+ else
+ cmd->c_spat = arg[2].arg_ptr.arg_spat;
+ cmd->c_flags |= sure;
+ }
+ }
+ }
+ else if (arg->arg_type == O_SEQ || arg->arg_type == O_SNE ||
+ arg->arg_type == O_SLT || arg->arg_type == O_SGT) {
+ if (arg[1].arg_type == A_STAB || arg[1].arg_type == A_LVAL) {
+ if (arg[2].arg_type == A_SINGLE) {
+ /*SUPPRESS 594*/
+ char *junk = str_get(arg[2].arg_ptr.arg_str);
+
+ cmd->c_stab = arg[1].arg_ptr.arg_stab;
+ cmd->c_short = str_smake(arg[2].arg_ptr.arg_str);
+ cmd->c_slen = cmd->c_short->str_cur+1;
+ switch (arg->arg_type) {
+ case O_SLT: case O_SGT:
+ sure |= CF_EQSURE;
+ cmd->c_flags |= CF_FIRSTNEG;
+ break;
+ case O_SNE:
+ cmd->c_flags |= CF_FIRSTNEG;
+ /* FALL THROUGH */
+ case O_SEQ:
+ sure |= CF_NESURE|CF_EQSURE;
+ break;
+ }
+ if (context & 1) { /* only sure if thing is false */
+ if (cmd->c_flags & CF_FIRSTNEG)
+ sure &= ~CF_NESURE;
+ else
+ sure &= ~CF_EQSURE;
+ }
+ else if (context & 2) { /* only sure if thing is true */
+ if (cmd->c_flags & CF_FIRSTNEG)
+ sure &= ~CF_EQSURE;
+ else
+ sure &= ~CF_NESURE;
+ }
+ if (sure & (CF_EQSURE|CF_NESURE)) {
+ opt = CFT_STROP;
+ cmd->c_flags |= sure;
+ }
+ }
+ }
+ }
+ else if (arg->arg_type == O_EQ || arg->arg_type == O_NE ||
+ arg->arg_type == O_LE || arg->arg_type == O_GE ||
+ arg->arg_type == O_LT || arg->arg_type == O_GT) {
+ if (arg[1].arg_type == A_STAB || arg[1].arg_type == A_LVAL) {
+ if (arg[2].arg_type == A_SINGLE) {
+ cmd->c_stab = arg[1].arg_ptr.arg_stab;
+ if (dowarn) {
+ STR *str = arg[2].arg_ptr.arg_str;
+
+ if ((!str->str_nok && !looks_like_number(str)))
+ warn("Possible use of == on string value");
+ }
+ cmd->c_short = str_nmake(str_gnum(arg[2].arg_ptr.arg_str));
+ cmd->c_slen = arg->arg_type;
+ sure |= CF_NESURE|CF_EQSURE;
+ if (context & 1) { /* only sure if thing is false */
+ sure &= ~CF_EQSURE;
+ }
+ else if (context & 2) { /* only sure if thing is true */
+ sure &= ~CF_NESURE;
+ }
+ if (sure & (CF_EQSURE|CF_NESURE)) {
+ opt = CFT_NUMOP;
+ cmd->c_flags |= sure;
+ }
+ }
+ }
+ }
+ else if (arg->arg_type == O_ASSIGN &&
+ (arg[1].arg_type == A_STAB || arg[1].arg_type == A_LVAL) &&
+ arg[1].arg_ptr.arg_stab == defstab &&
+ arg[2].arg_type == A_EXPR ) {
+ arg2 = arg[2].arg_ptr.arg_arg;
+ if (arg2->arg_type == O_ITEM && arg2[1].arg_type == A_READ) {
+ opt = CFT_GETS;
+ cmd->c_stab = arg2[1].arg_ptr.arg_stab;
+ if (!(stab_io(arg2[1].arg_ptr.arg_stab)->flags & IOF_ARGV)) {
+ free_arg(arg2);
+ arg[2].arg_ptr.arg_arg = Nullarg;
+ free_arg(arg);
+ cmd->c_expr = Nullarg;
+ }
+ }
+ }
+ else if (arg->arg_type == O_CHOP &&
+ (arg[1].arg_type == A_STAB || arg[1].arg_type == A_LVAL) ) {
+ opt = CFT_CHOP;
+ cmd->c_stab = arg[1].arg_ptr.arg_stab;
+ free_arg(arg);
+ cmd->c_expr = Nullarg;
+ }
+ if (context & 4)
+ opt |= CF_FLIP;
+ cmd->c_flags |= opt;
+
+ if (cmd->c_flags & CF_FLIP) {
+ if (fliporflop == 1) {
+ arg = cmd->c_expr; /* get back to O_FLIP arg */
+ New(110,arg[3].arg_ptr.arg_cmd, 1, CMD);
+ Copy(cmd, arg[3].arg_ptr.arg_cmd, 1, CMD);
+ New(111,arg[4].arg_ptr.arg_cmd,1,CMD);
+ Copy(cmd, arg[4].arg_ptr.arg_cmd, 1, CMD);
+ opt_arg(arg[4].arg_ptr.arg_cmd,2,acmd);
+ arg->arg_len = 2; /* this is a lie */
+ }
+ else {
+ if ((opt & CF_OPTIMIZE) == CFT_EVAL)
+ cmd->c_flags = (cmd->c_flags & ~CF_OPTIMIZE) | CFT_UNFLIP;
+ }
+ }
+}
+
+CMD *
+add_label(lbl,cmd)
+char *lbl;
+register CMD *cmd;
+{
+ if (cmd)
+ cmd->c_label = lbl;
+ return cmd;
+}
+
+CMD *
+addcond(cmd, arg)
+register CMD *cmd;
+register ARG *arg;
+{
+ cmd->c_expr = arg;
+ cmd->c_flags |= CF_COND;
+ return cmd;
+}
+
+CMD *
+addloop(cmd, arg)
+register CMD *cmd;
+register ARG *arg;
+{
+ void while_io();
+
+ cmd->c_expr = arg;
+ cmd->c_flags |= CF_COND|CF_LOOP;
+
+ if (!(cmd->c_flags & CF_INVERT))
+ while_io(cmd); /* add $_ =, if necessary */
+
+ if (cmd->c_type == C_BLOCK)
+ cmd->c_flags &= ~CF_COND;
+ else {
+ arg = cmd->ucmd.acmd.ac_expr;
+ if (arg && arg->arg_type == O_ITEM && arg[1].arg_type == A_CMD)
+ cmd->c_flags &= ~CF_COND; /* "do {} while" happens at least once */
+ if (arg && (arg->arg_flags & AF_DEPR) &&
+ (arg->arg_type == O_SUBR || arg->arg_type == O_DBSUBR) )
+ cmd->c_flags &= ~CF_COND; /* likewise for "do subr() while" */
+ }
+ return cmd;
+}
+
+CMD *
+invert(cmd)
+CMD *cmd;
+{
+ register CMD *targ = cmd;
+ if (targ->c_head)
+ targ = targ->c_head;
+ if (targ->c_flags & CF_DBSUB)
+ targ = targ->c_next;
+ targ->c_flags ^= CF_INVERT;
+ return cmd;
+}
+
+void
+cpy7bit(d,s,l)
+register char *d;
+register char *s;
+register int l;
+{
+ while (l--)
+ *d++ = *s++ & 127;
+ *d = '\0';
+}
+
+int
+yyerror(s)
+char *s;
+{
+ char tmpbuf[258];
+ char tmp2buf[258];
+ char *tname = tmpbuf;
+
+ if (bufptr > oldoldbufptr && bufptr - oldoldbufptr < 200 &&
+ oldoldbufptr != oldbufptr && oldbufptr != bufptr) {
+ while (isSPACE(*oldoldbufptr))
+ oldoldbufptr++;
+ cpy7bit(tmp2buf, oldoldbufptr, bufptr - oldoldbufptr);
+ sprintf(tname,"next 2 tokens \"%s\"",tmp2buf);
+ }
+ else if (bufptr > oldbufptr && bufptr - oldbufptr < 200 &&
+ oldbufptr != bufptr) {
+ while (isSPACE(*oldbufptr))
+ oldbufptr++;
+ cpy7bit(tmp2buf, oldbufptr, bufptr - oldbufptr);
+ sprintf(tname,"next token \"%s\"",tmp2buf);
+ }
+ else if (yychar > 256)
+ tname = "next token ???";
+ else if (!yychar)
+ (void)strcpy(tname,"at EOF");
+ else if (yychar < 32)
+ (void)sprintf(tname,"next char ^%c",yychar+64);
+ else if (yychar == 127)
+ (void)strcpy(tname,"at EOF");
+ else
+ (void)sprintf(tname,"next char %c",yychar);
+ (void)sprintf(buf, "%s in file %s at line %d, %s\n",
+ s,stab_val(curcmd->c_filestab)->str_ptr,curcmd->c_line,tname);
+ if (curcmd->c_line == multi_end && multi_start < multi_end)
+ sprintf(buf+strlen(buf),
+ " (Might be a runaway multi-line %c%c string starting on line %d)\n",
+ multi_open,multi_close,multi_start);
+ if (in_eval)
+ str_cat(stab_val(stabent("@",TRUE)),buf);
+ else
+ fputs(buf,stderr);
+ if (++error_count >= 10)
+ fatal("%s has too many errors.\n",
+ stab_val(curcmd->c_filestab)->str_ptr);
+}
+
+void
+while_io(cmd)
+register CMD *cmd;
+{
+ register ARG *arg = cmd->c_expr;
+ STAB *asgnstab;
+
+ /* hoist "while (<channel>)" up into command block */
+
+ if (arg && arg->arg_type == O_ITEM && arg[1].arg_type == A_READ) {
+ cmd->c_flags &= ~CF_OPTIMIZE; /* clear optimization type */
+ cmd->c_flags |= CFT_GETS; /* and set it to do the input */
+ cmd->c_stab = arg[1].arg_ptr.arg_stab;
+ if (stab_io(arg[1].arg_ptr.arg_stab)->flags & IOF_ARGV) {
+ cmd->c_expr = l(make_op(O_ASSIGN, 2, /* fake up "$_ =" */
+ stab2arg(A_LVAL,defstab), arg, Nullarg));
+ }
+ else {
+ free_arg(arg);
+ cmd->c_expr = Nullarg;
+ }
+ }
+ else if (arg && arg->arg_type == O_ITEM && arg[1].arg_type == A_INDREAD) {
+ cmd->c_flags &= ~CF_OPTIMIZE; /* clear optimization type */
+ cmd->c_flags |= CFT_INDGETS; /* and set it to do the input */
+ cmd->c_stab = arg[1].arg_ptr.arg_stab;
+ free_arg(arg);
+ cmd->c_expr = Nullarg;
+ }
+ else if (arg && arg->arg_type == O_ITEM && arg[1].arg_type == A_GLOB) {
+ if ((cmd->c_flags & CF_OPTIMIZE) == CFT_ARRAY)
+ asgnstab = cmd->c_stab;
+ else
+ asgnstab = defstab;
+ cmd->c_expr = l(make_op(O_ASSIGN, 2, /* fake up "$foo =" */
+ stab2arg(A_LVAL,asgnstab), arg, Nullarg));
+ cmd->c_flags &= ~CF_OPTIMIZE; /* clear optimization type */
+ }
+}
+
+CMD *
+wopt(cmd)
+register CMD *cmd;
+{
+ register CMD *tail;
+ CMD *newtail;
+ register int i;
+
+ if (cmd->c_expr && (cmd->c_flags & CF_OPTIMIZE) == CFT_FALSE)
+ opt_arg(cmd,1, cmd->c_type == C_EXPR);
+
+ while_io(cmd); /* add $_ =, if necessary */
+
+ /* First find the end of the true list */
+
+ tail = cmd->ucmd.ccmd.cc_true;
+ if (tail == Nullcmd)
+ return cmd;
+ New(112,newtail, 1, CMD); /* guaranteed continue */
+ for (;;) {
+ /* optimize "next" to point directly to continue block */
+ if (tail->c_type == C_EXPR &&
+ tail->ucmd.acmd.ac_expr &&
+ tail->ucmd.acmd.ac_expr->arg_type == O_NEXT &&
+ (tail->ucmd.acmd.ac_expr->arg_len == 0 ||
+ (cmd->c_label &&
+ strEQ(cmd->c_label,
+ tail->ucmd.acmd.ac_expr[1].arg_ptr.arg_str->str_ptr) )))
+ {
+ arg_free(tail->ucmd.acmd.ac_expr);
+ tail->ucmd.acmd.ac_expr = Nullarg;
+ tail->c_type = C_NEXT;
+ if (cmd->ucmd.ccmd.cc_alt != Nullcmd)
+ tail->ucmd.ccmd.cc_alt = cmd->ucmd.ccmd.cc_alt;
+ else
+ tail->ucmd.ccmd.cc_alt = newtail;
+ tail->ucmd.ccmd.cc_true = Nullcmd;
+ }
+ else if (tail->c_type == C_IF && !tail->ucmd.ccmd.cc_alt) {
+ if (cmd->ucmd.ccmd.cc_alt != Nullcmd)
+ tail->ucmd.ccmd.cc_alt = cmd->ucmd.ccmd.cc_alt;
+ else
+ tail->ucmd.ccmd.cc_alt = newtail;
+ }
+ else if (tail->c_type == C_CSWITCH || tail->c_type == C_NSWITCH) {
+ if (cmd->ucmd.ccmd.cc_alt != Nullcmd) {
+ for (i = tail->ucmd.scmd.sc_max; i >= 0; i--)
+ if (!tail->ucmd.scmd.sc_next[i])
+ tail->ucmd.scmd.sc_next[i] = cmd->ucmd.ccmd.cc_alt;
+ }
+ else {
+ for (i = tail->ucmd.scmd.sc_max; i >= 0; i--)
+ if (!tail->ucmd.scmd.sc_next[i])
+ tail->ucmd.scmd.sc_next[i] = newtail;
+ }
+ }
+
+ if (!tail->c_next)
+ break;
+ tail = tail->c_next;
+ }
+
+ /* if there's a continue block, link it to true block and find end */
+
+ if (cmd->ucmd.ccmd.cc_alt != Nullcmd) {
+ tail->c_next = cmd->ucmd.ccmd.cc_alt;
+ tail = tail->c_next;
+ for (;;) {
+ /* optimize "next" to point directly to continue block */
+ if (tail->c_type == C_EXPR &&
+ tail->ucmd.acmd.ac_expr &&
+ tail->ucmd.acmd.ac_expr->arg_type == O_NEXT &&
+ (tail->ucmd.acmd.ac_expr->arg_len == 0 ||
+ (cmd->c_label &&
+ strEQ(cmd->c_label,
+ tail->ucmd.acmd.ac_expr[1].arg_ptr.arg_str->str_ptr) )))
+ {
+ arg_free(tail->ucmd.acmd.ac_expr);
+ tail->ucmd.acmd.ac_expr = Nullarg;
+ tail->c_type = C_NEXT;
+ tail->ucmd.ccmd.cc_alt = newtail;
+ tail->ucmd.ccmd.cc_true = Nullcmd;
+ }
+ else if (tail->c_type == C_IF && !tail->ucmd.ccmd.cc_alt) {
+ tail->ucmd.ccmd.cc_alt = newtail;
+ }
+ else if (tail->c_type == C_CSWITCH || tail->c_type == C_NSWITCH) {
+ for (i = tail->ucmd.scmd.sc_max; i >= 0; i--)
+ if (!tail->ucmd.scmd.sc_next[i])
+ tail->ucmd.scmd.sc_next[i] = newtail;
+ }
+
+ if (!tail->c_next)
+ break;
+ tail = tail->c_next;
+ }
+ /*SUPPRESS 530*/
+ for ( ; tail->c_next; tail = tail->c_next) ;
+ }
+
+ /* Here's the real trick: link the end of the list back to the beginning,
+ * inserting a "last" block to break out of the loop. This saves one or
+ * two procedure calls every time through the loop, because of how cmd_exec
+ * does tail recursion.
+ */
+
+ tail->c_next = newtail;
+ tail = newtail;
+ if (!cmd->ucmd.ccmd.cc_alt)
+ cmd->ucmd.ccmd.cc_alt = tail; /* every loop has a continue now */
+
+#ifndef lint
+ Copy((char *)cmd, (char *)tail, 1, CMD);
+#endif
+ tail->c_type = C_EXPR;
+ tail->c_flags ^= CF_INVERT; /* turn into "last unless" */
+ tail->c_next = tail->ucmd.ccmd.cc_true; /* loop directly back to top */
+ tail->ucmd.acmd.ac_expr = make_op(O_LAST,0,Nullarg,Nullarg,Nullarg);
+ tail->ucmd.acmd.ac_stab = Nullstab;
+ return cmd;
+}
+
+CMD *
+over(eachstab,cmd)
+STAB *eachstab;
+register CMD *cmd;
+{
+ /* hoist "for $foo (@bar)" up into command block */
+
+ cmd->c_flags &= ~CF_OPTIMIZE; /* clear optimization type */
+ cmd->c_flags |= CFT_ARRAY; /* and set it to do the iteration */
+ cmd->c_stab = eachstab;
+ cmd->c_short = Str_new(23,0); /* just to save a field in struct cmd */
+ cmd->c_short->str_u.str_useful = -1;
+
+ return cmd;
+}
+
+void
+cmd_free(cmd)
+register CMD *cmd;
+{
+ register CMD *tofree;
+ register CMD *head = cmd;
+
+ if (!cmd)
+ return;
+ if (cmd->c_head != cmd)
+ warn("Malformed cmd links\n");
+ while (cmd) {
+ if (cmd->c_type != C_WHILE) { /* WHILE block is duplicated */
+ if (cmd->c_label) {
+ Safefree(cmd->c_label);
+ cmd->c_label = Nullch;
+ }
+ if (cmd->c_short) {
+ str_free(cmd->c_short);
+ cmd->c_short = Nullstr;
+ }
+ if (cmd->c_expr) {
+ arg_free(cmd->c_expr);
+ cmd->c_expr = Nullarg;
+ }
+ }
+ switch (cmd->c_type) {
+ case C_WHILE:
+ case C_BLOCK:
+ case C_ELSE:
+ case C_IF:
+ if (cmd->ucmd.ccmd.cc_true) {
+ cmd_free(cmd->ucmd.ccmd.cc_true);
+ cmd->ucmd.ccmd.cc_true = Nullcmd;
+ }
+ break;
+ case C_EXPR:
+ if (cmd->ucmd.acmd.ac_expr) {
+ arg_free(cmd->ucmd.acmd.ac_expr);
+ cmd->ucmd.acmd.ac_expr = Nullarg;
+ }
+ break;
+ }
+ tofree = cmd;
+ cmd = cmd->c_next;
+ if (tofree != head) /* to get Saber to shut up */
+ Safefree(tofree);
+ if (cmd && cmd == head) /* reached end of while loop */
+ break;
+ }
+ Safefree(head);
+}
+
+void
+arg_free(arg)
+register ARG *arg;
+{
+ register int i;
+
+ if (!arg)
+ return;
+ for (i = 1; i <= arg->arg_len; i++) {
+ switch (arg[i].arg_type & A_MASK) {
+ case A_NULL:
+ if (arg->arg_type == O_TRANS) {
+ Safefree(arg[i].arg_ptr.arg_cval);
+ arg[i].arg_ptr.arg_cval = Nullch;
+ }
+ break;
+ case A_LEXPR:
+ if (arg->arg_type == O_AASSIGN &&
+ arg[i].arg_ptr.arg_arg->arg_type == O_LARRAY) {
+ char *name =
+ stab_name(arg[i].arg_ptr.arg_arg[1].arg_ptr.arg_stab);
+
+ if (strnEQ("_GEN_",name, 5)) /* array for foreach */
+ hdelete(defstash,name,strlen(name));
+ }
+ /* FALL THROUGH */
+ case A_EXPR:
+ arg_free(arg[i].arg_ptr.arg_arg);
+ arg[i].arg_ptr.arg_arg = Nullarg;
+ break;
+ case A_CMD:
+ cmd_free(arg[i].arg_ptr.arg_cmd);
+ arg[i].arg_ptr.arg_cmd = Nullcmd;
+ break;
+ case A_WORD:
+ case A_STAB:
+ case A_LVAL:
+ case A_READ:
+ case A_GLOB:
+ case A_ARYLEN:
+ case A_LARYLEN:
+ case A_ARYSTAB:
+ case A_LARYSTAB:
+ break;
+ case A_SINGLE:
+ case A_DOUBLE:
+ case A_BACKTICK:
+ str_free(arg[i].arg_ptr.arg_str);
+ arg[i].arg_ptr.arg_str = Nullstr;
+ break;
+ case A_SPAT:
+ spat_free(arg[i].arg_ptr.arg_spat);
+ arg[i].arg_ptr.arg_spat = Nullspat;
+ break;
+ }
+ }
+ free_arg(arg);
+}
+
+void
+spat_free(spat)
+register SPAT *spat;
+{
+ register SPAT *sp;
+ HENT *entry;
+
+ if (!spat)
+ return;
+ if (spat->spat_runtime) {
+ arg_free(spat->spat_runtime);
+ spat->spat_runtime = Nullarg;
+ }
+ if (spat->spat_repl) {
+ arg_free(spat->spat_repl);
+ spat->spat_repl = Nullarg;
+ }
+ if (spat->spat_short) {
+ str_free(spat->spat_short);
+ spat->spat_short = Nullstr;
+ }
+ if (spat->spat_regexp) {
+ regfree(spat->spat_regexp);
+ spat->spat_regexp = Null(REGEXP*);
+ }
+
+ /* now unlink from spat list */
+
+ for (entry = defstash->tbl_array['_']; entry; entry = entry->hent_next) {
+ register HASH *stash;
+ STAB *stab = (STAB*)entry->hent_val;
+
+ if (!stab)
+ continue;
+ stash = stab_hash(stab);
+ if (!stash || stash->tbl_spatroot == Null(SPAT*))
+ continue;
+ if (stash->tbl_spatroot == spat)
+ stash->tbl_spatroot = spat->spat_next;
+ else {
+ for (sp = stash->tbl_spatroot;
+ sp && sp->spat_next != spat;
+ sp = sp->spat_next)
+ /*SUPPRESS 530*/
+ ;
+ if (sp)
+ sp->spat_next = spat->spat_next;
+ }
+ }
+ Safefree(spat);
+}
+
+/* Recursively descend a command sequence and push the address of any string
+ * that needs saving on recursion onto the tosave array.
+ */
+
+static int
+cmd_tosave(cmd,willsave)
+register CMD *cmd;
+int willsave; /* willsave passes down the tree */
+{
+ register CMD *head = cmd;
+ int shouldsave = FALSE; /* shouldsave passes up the tree */
+ int tmpsave;
+ register CMD *lastcmd = Nullcmd;
+
+ while (cmd) {
+ if (cmd->c_expr)
+ shouldsave |= arg_tosave(cmd->c_expr,willsave);
+ switch (cmd->c_type) {
+ case C_WHILE:
+ if (cmd->ucmd.ccmd.cc_true) {
+ tmpsave = cmd_tosave(cmd->ucmd.ccmd.cc_true,willsave);
+
+ /* Here we check to see if the temporary array generated for
+ * a foreach needs to be localized because of recursion.
+ */
+ if (tmpsave && (cmd->c_flags & CF_OPTIMIZE) == CFT_ARRAY) {
+ if (lastcmd &&
+ lastcmd->c_type == C_EXPR &&
+ lastcmd->c_expr) {
+ ARG *arg = lastcmd->c_expr;
+
+ if (arg->arg_type == O_ASSIGN &&
+ arg[1].arg_type == A_LEXPR &&
+ arg[1].arg_ptr.arg_arg->arg_type == O_LARRAY &&
+ strnEQ("_GEN_",
+ stab_name(
+ arg[1].arg_ptr.arg_arg[1].arg_ptr.arg_stab),
+ 5)) { /* array generated for foreach */
+ (void)localize(arg);
+ }
+ }
+
+ /* in any event, save the iterator */
+
+ if (cmd->c_short) /* Better safe than sorry */
+ (void)apush(tosave,cmd->c_short);
+ }
+ shouldsave |= tmpsave;
+ }
+ break;
+ case C_BLOCK:
+ case C_ELSE:
+ case C_IF:
+ if (cmd->ucmd.ccmd.cc_true)
+ shouldsave |= cmd_tosave(cmd->ucmd.ccmd.cc_true,willsave);
+ break;
+ case C_EXPR:
+ if (cmd->ucmd.acmd.ac_expr)
+ shouldsave |= arg_tosave(cmd->ucmd.acmd.ac_expr,willsave);
+ break;
+ }
+ lastcmd = cmd;
+ cmd = cmd->c_next;
+ if (cmd && cmd == head) /* reached end of while loop */
+ break;
+ }
+ return shouldsave;
+}
+
+static int
+arg_tosave(arg,willsave)
+register ARG *arg;
+int willsave;
+{
+ register int i;
+ int shouldsave = FALSE;
+
+ for (i = arg->arg_len; i >= 1; i--) {
+ switch (arg[i].arg_type & A_MASK) {
+ case A_NULL:
+ break;
+ case A_LEXPR:
+ case A_EXPR:
+ shouldsave |= arg_tosave(arg[i].arg_ptr.arg_arg,shouldsave);
+ break;
+ case A_CMD:
+ shouldsave |= cmd_tosave(arg[i].arg_ptr.arg_cmd,shouldsave);
+ break;
+ case A_WORD:
+ case A_STAB:
+ case A_LVAL:
+ case A_READ:
+ case A_GLOB:
+ case A_ARYLEN:
+ case A_SINGLE:
+ case A_DOUBLE:
+ case A_BACKTICK:
+ break;
+ case A_SPAT:
+ shouldsave |= spat_tosave(arg[i].arg_ptr.arg_spat);
+ break;
+ }
+ }
+ switch (arg->arg_type) {
+ case O_RETURN:
+ saw_return = TRUE;
+ break;
+ case O_EVAL:
+ case O_SUBR:
+ shouldsave = TRUE;
+ break;
+ }
+ if (willsave && arg->arg_ptr.arg_str)
+ (void)apush(tosave,arg->arg_ptr.arg_str);
+ return shouldsave;
+}
+
+static int
+spat_tosave(spat)
+register SPAT *spat;
+{
+ int shouldsave = FALSE;
+
+ if (spat->spat_runtime)
+ shouldsave |= arg_tosave(spat->spat_runtime,FALSE);
+ if (spat->spat_repl) {
+ shouldsave |= arg_tosave(spat->spat_repl,FALSE);
+ }
+
+ return shouldsave;
+}
+
diff --git a/gnu/usr.bin/perl/perl/consarg.c b/gnu/usr.bin/perl/perl/consarg.c
new file mode 100644
index 0000000..440fcfd
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/consarg.c
@@ -0,0 +1,1295 @@
+/* $RCSfile: consarg.c,v $$Revision: 1.1.1.1 $$Date: 1994/09/10 06:27:32 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: consarg.c,v $
+ * Revision 1.1.1.1 1994/09/10 06:27:32 gclarkii
+ * Initial import of Perl 4.046 bmaked
+ *
+ * Revision 1.1.1.1 1993/08/23 21:29:35 nate
+ * PERL!
+ *
+ * Revision 4.0.1.4 92/06/08 12:26:27 lwall
+ * patch20: new warning for use of x with non-numeric right operand
+ * patch20: modulus with highest bit in left operand set didn't always work
+ * patch20: illegal lvalue message could be followed by core dump
+ * patch20: deleted some minor memory leaks
+ *
+ * Revision 4.0.1.3 91/11/05 16:21:16 lwall
+ * patch11: random cleanup
+ * patch11: added eval {}
+ * patch11: added sort {} LIST
+ * patch11: "foo" x -1 dumped core
+ * patch11: substr() and vec() weren't allowed in an lvalue list
+ *
+ * Revision 4.0.1.2 91/06/07 10:33:12 lwall
+ * patch4: new copyright notice
+ * patch4: length($`), length($&), length($') now optimized to avoid string copy
+ *
+ * Revision 4.0.1.1 91/04/11 17:38:34 lwall
+ * patch1: fixed "Bad free" error
+ *
+ * Revision 4.0 91/03/20 01:06:15 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+static int nothing_in_common();
+static int arg_common();
+static int spat_common();
+
+ARG *
+make_split(stab,arg,limarg)
+register STAB *stab;
+register ARG *arg;
+ARG *limarg;
+{
+ register SPAT *spat;
+
+ if (arg->arg_type != O_MATCH) {
+ Newz(201,spat,1,SPAT);
+ spat->spat_next = curstash->tbl_spatroot; /* link into spat list */
+ curstash->tbl_spatroot = spat;
+
+ spat->spat_runtime = arg;
+ arg = make_match(O_MATCH,stab2arg(A_STAB,defstab),spat);
+ }
+ Renew(arg,4,ARG);
+ arg->arg_len = 3;
+ if (limarg) {
+ if (limarg->arg_type == O_ITEM) {
+ Copy(limarg+1,arg+3,1,ARG);
+ limarg[1].arg_type = A_NULL;
+ arg_free(limarg);
+ }
+ else {
+ arg[3].arg_flags = 0;
+ arg[3].arg_len = 0;
+ arg[3].arg_type = A_EXPR;
+ arg[3].arg_ptr.arg_arg = limarg;
+ }
+ }
+ else {
+ arg[3].arg_flags = 0;
+ arg[3].arg_len = 0;
+ arg[3].arg_type = A_NULL;
+ arg[3].arg_ptr.arg_arg = Nullarg;
+ }
+ arg->arg_type = O_SPLIT;
+ spat = arg[2].arg_ptr.arg_spat;
+ spat->spat_repl = stab2arg(A_STAB,aadd(stab));
+ if (spat->spat_short) { /* exact match can bypass regexec() */
+ if (!((spat->spat_flags & SPAT_SCANFIRST) &&
+ (spat->spat_flags & SPAT_ALL) )) {
+ str_free(spat->spat_short);
+ spat->spat_short = Nullstr;
+ }
+ }
+ return arg;
+}
+
+ARG *
+mod_match(type,left,pat)
+register ARG *left;
+register ARG *pat;
+{
+
+ register SPAT *spat;
+ register ARG *newarg;
+
+ if (!pat)
+ return Nullarg;
+
+ if ((pat->arg_type == O_MATCH ||
+ pat->arg_type == O_SUBST ||
+ pat->arg_type == O_TRANS ||
+ pat->arg_type == O_SPLIT
+ ) &&
+ pat[1].arg_ptr.arg_stab == defstab ) {
+ switch (pat->arg_type) {
+ case O_MATCH:
+ newarg = make_op(type == O_MATCH ? O_MATCH : O_NMATCH,
+ pat->arg_len,
+ left,Nullarg,Nullarg);
+ break;
+ case O_SUBST:
+ newarg = l(make_op(type == O_MATCH ? O_SUBST : O_NSUBST,
+ pat->arg_len,
+ left,Nullarg,Nullarg));
+ break;
+ case O_TRANS:
+ newarg = l(make_op(type == O_MATCH ? O_TRANS : O_NTRANS,
+ pat->arg_len,
+ left,Nullarg,Nullarg));
+ break;
+ case O_SPLIT:
+ newarg = make_op(type == O_MATCH ? O_SPLIT : O_SPLIT,
+ pat->arg_len,
+ left,Nullarg,Nullarg);
+ break;
+ }
+ if (pat->arg_len >= 2) {
+ newarg[2].arg_type = pat[2].arg_type;
+ newarg[2].arg_ptr = pat[2].arg_ptr;
+ newarg[2].arg_len = pat[2].arg_len;
+ newarg[2].arg_flags = pat[2].arg_flags;
+ if (pat->arg_len >= 3) {
+ newarg[3].arg_type = pat[3].arg_type;
+ newarg[3].arg_ptr = pat[3].arg_ptr;
+ newarg[3].arg_len = pat[3].arg_len;
+ newarg[3].arg_flags = pat[3].arg_flags;
+ }
+ }
+ free_arg(pat);
+ }
+ else {
+ Newz(202,spat,1,SPAT);
+ spat->spat_next = curstash->tbl_spatroot; /* link into spat list */
+ curstash->tbl_spatroot = spat;
+
+ spat->spat_runtime = pat;
+ newarg = make_op(type,2,left,Nullarg,Nullarg);
+ newarg[2].arg_type = A_SPAT | A_DONT;
+ newarg[2].arg_ptr.arg_spat = spat;
+ }
+
+ return newarg;
+}
+
+ARG *
+make_op(type,newlen,arg1,arg2,arg3)
+int type;
+int newlen;
+ARG *arg1;
+ARG *arg2;
+ARG *arg3;
+{
+ register ARG *arg;
+ register ARG *chld;
+ register unsigned doarg;
+ register int i;
+ extern ARG *arg4; /* should be normal arguments, really */
+ extern ARG *arg5;
+
+ arg = op_new(newlen);
+ arg->arg_type = type;
+ /*SUPPRESS 560*/
+ if (chld = arg1) {
+ if (chld->arg_type == O_ITEM &&
+ (hoistable[ i = (chld[1].arg_type&A_MASK)] || i == A_LVAL ||
+ (i == A_LEXPR &&
+ (chld[1].arg_ptr.arg_arg->arg_type == O_LIST ||
+ chld[1].arg_ptr.arg_arg->arg_type == O_ARRAY ||
+ chld[1].arg_ptr.arg_arg->arg_type == O_HASH ))))
+ {
+ arg[1].arg_type = chld[1].arg_type;
+ arg[1].arg_ptr = chld[1].arg_ptr;
+ arg[1].arg_flags |= chld[1].arg_flags;
+ arg[1].arg_len = chld[1].arg_len;
+ free_arg(chld);
+ }
+ else {
+ arg[1].arg_type = A_EXPR;
+ arg[1].arg_ptr.arg_arg = chld;
+ }
+ }
+ /*SUPPRESS 560*/
+ if (chld = arg2) {
+ if (chld->arg_type == O_ITEM &&
+ (hoistable[chld[1].arg_type&A_MASK] ||
+ (type == O_ASSIGN &&
+ ((chld[1].arg_type == A_READ && !(arg[1].arg_type & A_DONT))
+ ||
+ (chld[1].arg_type == A_INDREAD && !(arg[1].arg_type & A_DONT))
+ ||
+ (chld[1].arg_type == A_GLOB && !(arg[1].arg_type & A_DONT))
+ ) ) ) ) {
+ arg[2].arg_type = chld[1].arg_type;
+ arg[2].arg_ptr = chld[1].arg_ptr;
+ arg[2].arg_len = chld[1].arg_len;
+ free_arg(chld);
+ }
+ else {
+ arg[2].arg_type = A_EXPR;
+ arg[2].arg_ptr.arg_arg = chld;
+ }
+ }
+ /*SUPPRESS 560*/
+ if (chld = arg3) {
+ if (chld->arg_type == O_ITEM && hoistable[chld[1].arg_type&A_MASK]) {
+ arg[3].arg_type = chld[1].arg_type;
+ arg[3].arg_ptr = chld[1].arg_ptr;
+ arg[3].arg_len = chld[1].arg_len;
+ free_arg(chld);
+ }
+ else {
+ arg[3].arg_type = A_EXPR;
+ arg[3].arg_ptr.arg_arg = chld;
+ }
+ }
+ if (newlen >= 4 && (chld = arg4)) {
+ if (chld->arg_type == O_ITEM && hoistable[chld[1].arg_type&A_MASK]) {
+ arg[4].arg_type = chld[1].arg_type;
+ arg[4].arg_ptr = chld[1].arg_ptr;
+ arg[4].arg_len = chld[1].arg_len;
+ free_arg(chld);
+ }
+ else {
+ arg[4].arg_type = A_EXPR;
+ arg[4].arg_ptr.arg_arg = chld;
+ }
+ }
+ if (newlen >= 5 && (chld = arg5)) {
+ if (chld->arg_type == O_ITEM && hoistable[chld[1].arg_type&A_MASK]) {
+ arg[5].arg_type = chld[1].arg_type;
+ arg[5].arg_ptr = chld[1].arg_ptr;
+ arg[5].arg_len = chld[1].arg_len;
+ free_arg(chld);
+ }
+ else {
+ arg[5].arg_type = A_EXPR;
+ arg[5].arg_ptr.arg_arg = chld;
+ }
+ }
+ doarg = opargs[type];
+ for (i = 1; i <= newlen; ++i) {
+ if (!(doarg & 1))
+ arg[i].arg_type |= A_DONT;
+ if (doarg & 2)
+ arg[i].arg_flags |= AF_ARYOK;
+ doarg >>= 2;
+ }
+#ifdef DEBUGGING
+ if (debug & 16) {
+ fprintf(stderr,"%lx <= make_op(%s",arg,opname[arg->arg_type]);
+ if (arg1)
+ fprintf(stderr,",%s=%lx",
+ argname[arg[1].arg_type&A_MASK],arg[1].arg_ptr.arg_arg);
+ if (arg2)
+ fprintf(stderr,",%s=%lx",
+ argname[arg[2].arg_type&A_MASK],arg[2].arg_ptr.arg_arg);
+ if (arg3)
+ fprintf(stderr,",%s=%lx",
+ argname[arg[3].arg_type&A_MASK],arg[3].arg_ptr.arg_arg);
+ if (newlen >= 4)
+ fprintf(stderr,",%s=%lx",
+ argname[arg[4].arg_type&A_MASK],arg[4].arg_ptr.arg_arg);
+ if (newlen >= 5)
+ fprintf(stderr,",%s=%lx",
+ argname[arg[5].arg_type&A_MASK],arg[5].arg_ptr.arg_arg);
+ fprintf(stderr,")\n");
+ }
+#endif
+ arg = evalstatic(arg); /* see if we can consolidate anything */
+ return arg;
+}
+
+ARG *
+evalstatic(arg)
+register ARG *arg;
+{
+ static STR *str = Nullstr;
+ register STR *s1;
+ register STR *s2;
+ double value; /* must not be register */
+ register char *tmps;
+ int i;
+ unsigned long tmplong;
+ long tmp2;
+ double exp(), log(), sqrt(), modf();
+ char *crypt();
+ double sin(), cos(), atan2(), pow();
+
+ if (!arg || !arg->arg_len)
+ return arg;
+
+ if (!str)
+ str = Str_new(20,0);
+
+ if (arg[1].arg_type == A_SINGLE)
+ s1 = arg[1].arg_ptr.arg_str;
+ else
+ s1 = Nullstr;
+ if (arg->arg_len >= 2 && arg[2].arg_type == A_SINGLE)
+ s2 = arg[2].arg_ptr.arg_str;
+ else
+ s2 = Nullstr;
+
+#define CHECK1 if (!s1) return arg
+#define CHECK2 if (!s2) return arg
+#define CHECK12 if (!s1 || !s2) return arg
+
+ switch (arg->arg_type) {
+ default:
+ return arg;
+ case O_SORT:
+ if (arg[1].arg_type == A_CMD)
+ arg[1].arg_type |= A_DONT;
+ return arg;
+ case O_EVAL:
+ if (arg[1].arg_type == A_CMD) {
+ arg->arg_type = O_TRY;
+ arg[1].arg_type |= A_DONT;
+ return arg;
+ }
+ CHECK1;
+ arg->arg_type = O_EVALONCE;
+ return arg;
+ case O_AELEM:
+ CHECK2;
+ i = (int)str_gnum(s2);
+ if (i < 32767 && i >= 0) {
+ arg->arg_type = O_ITEM;
+ arg->arg_len = 1;
+ arg[1].arg_type = A_ARYSTAB; /* $abc[123] is hoistable now */
+ arg[1].arg_len = i;
+ str_free(s2);
+ Renew(arg, 2, ARG);
+ }
+ return arg;
+ case O_CONCAT:
+ CHECK12;
+ str_sset(str,s1);
+ str_scat(str,s2);
+ break;
+ case O_REPEAT:
+ CHECK2;
+ if (dowarn && !s2->str_nok && !looks_like_number(s2))
+ warn("Right operand of x is not numeric");
+ CHECK1;
+ i = (int)str_gnum(s2);
+ tmps = str_get(s1);
+ str_nset(str,"",0);
+ if (i > 0) {
+ STR_GROW(str, i * s1->str_cur + 1);
+ repeatcpy(str->str_ptr, tmps, s1->str_cur, i);
+ str->str_cur = i * s1->str_cur;
+ str->str_ptr[str->str_cur] = '\0';
+ }
+ break;
+ case O_MULTIPLY:
+ CHECK12;
+ value = str_gnum(s1);
+ str_numset(str,value * str_gnum(s2));
+ break;
+ case O_DIVIDE:
+ CHECK12;
+ value = str_gnum(s2);
+ if (value == 0.0)
+ yyerror("Illegal division by constant zero");
+ else
+#ifdef SLOPPYDIVIDE
+ /* insure that 20./5. == 4. */
+ {
+ double x;
+ int k;
+ x = str_gnum(s1);
+ if ((double)(int)x == x &&
+ (double)(int)value == value &&
+ (k = (int)x/(int)value)*(int)value == (int)x) {
+ value = k;
+ } else {
+ value = x/value;
+ }
+ str_numset(str,value);
+ }
+#else
+ str_numset(str,str_gnum(s1) / value);
+#endif
+ break;
+ case O_MODULO:
+ CHECK12;
+ tmplong = (unsigned long)str_gnum(s2);
+ if (tmplong == 0L) {
+ yyerror("Illegal modulus of constant zero");
+ return arg;
+ }
+ value = str_gnum(s1);
+#ifndef lint
+ if (value >= 0.0)
+ str_numset(str,(double)(((unsigned long)value) % tmplong));
+ else {
+ tmp2 = (long)value;
+ str_numset(str,(double)((tmplong-((-tmp2 - 1) % tmplong)) - 1));
+ }
+#else
+ tmp2 = tmp2;
+#endif
+ break;
+ case O_ADD:
+ CHECK12;
+ value = str_gnum(s1);
+ str_numset(str,value + str_gnum(s2));
+ break;
+ case O_SUBTRACT:
+ CHECK12;
+ value = str_gnum(s1);
+ str_numset(str,value - str_gnum(s2));
+ break;
+ case O_LEFT_SHIFT:
+ CHECK12;
+ value = str_gnum(s1);
+ i = (int)str_gnum(s2);
+#ifndef lint
+ str_numset(str,(double)(((long)value) << i));
+#endif
+ break;
+ case O_RIGHT_SHIFT:
+ CHECK12;
+ value = str_gnum(s1);
+ i = (int)str_gnum(s2);
+#ifndef lint
+ str_numset(str,(double)(((long)value) >> i));
+#endif
+ break;
+ case O_LT:
+ CHECK12;
+ value = str_gnum(s1);
+ str_numset(str,(value < str_gnum(s2)) ? 1.0 : 0.0);
+ break;
+ case O_GT:
+ CHECK12;
+ value = str_gnum(s1);
+ str_numset(str,(value > str_gnum(s2)) ? 1.0 : 0.0);
+ break;
+ case O_LE:
+ CHECK12;
+ value = str_gnum(s1);
+ str_numset(str,(value <= str_gnum(s2)) ? 1.0 : 0.0);
+ break;
+ case O_GE:
+ CHECK12;
+ value = str_gnum(s1);
+ str_numset(str,(value >= str_gnum(s2)) ? 1.0 : 0.0);
+ break;
+ case O_EQ:
+ CHECK12;
+ if (dowarn) {
+ if ((!s1->str_nok && !looks_like_number(s1)) ||
+ (!s2->str_nok && !looks_like_number(s2)) )
+ warn("Possible use of == on string value");
+ }
+ value = str_gnum(s1);
+ str_numset(str,(value == str_gnum(s2)) ? 1.0 : 0.0);
+ break;
+ case O_NE:
+ CHECK12;
+ value = str_gnum(s1);
+ str_numset(str,(value != str_gnum(s2)) ? 1.0 : 0.0);
+ break;
+ case O_NCMP:
+ CHECK12;
+ value = str_gnum(s1);
+ value -= str_gnum(s2);
+ if (value > 0.0)
+ value = 1.0;
+ else if (value < 0.0)
+ value = -1.0;
+ str_numset(str,value);
+ break;
+ case O_BIT_AND:
+ CHECK12;
+ value = str_gnum(s1);
+#ifndef lint
+ str_numset(str,(double)(U_L(value) & U_L(str_gnum(s2))));
+#endif
+ break;
+ case O_XOR:
+ CHECK12;
+ value = str_gnum(s1);
+#ifndef lint
+ str_numset(str,(double)(U_L(value) ^ U_L(str_gnum(s2))));
+#endif
+ break;
+ case O_BIT_OR:
+ CHECK12;
+ value = str_gnum(s1);
+#ifndef lint
+ str_numset(str,(double)(U_L(value) | U_L(str_gnum(s2))));
+#endif
+ break;
+ case O_AND:
+ CHECK12;
+ if (str_true(s1))
+ str_sset(str,s2);
+ else
+ str_sset(str,s1);
+ break;
+ case O_OR:
+ CHECK12;
+ if (str_true(s1))
+ str_sset(str,s1);
+ else
+ str_sset(str,s2);
+ break;
+ case O_COND_EXPR:
+ CHECK12;
+ if ((arg[3].arg_type & A_MASK) != A_SINGLE)
+ return arg;
+ if (str_true(s1))
+ str_sset(str,s2);
+ else
+ str_sset(str,arg[3].arg_ptr.arg_str);
+ str_free(arg[3].arg_ptr.arg_str);
+ Renew(arg, 3, ARG);
+ break;
+ case O_NEGATE:
+ CHECK1;
+ str_numset(str,(double)(-str_gnum(s1)));
+ break;
+ case O_NOT:
+ CHECK1;
+#ifdef NOTNOT
+ { char xxx = str_true(s1); str_numset(str,(double)!xxx); }
+#else
+ str_numset(str,(double)(!str_true(s1)));
+#endif
+ break;
+ case O_COMPLEMENT:
+ CHECK1;
+#ifndef lint
+ str_numset(str,(double)(~U_L(str_gnum(s1))));
+#endif
+ break;
+ case O_SIN:
+ CHECK1;
+ str_numset(str,sin(str_gnum(s1)));
+ break;
+ case O_COS:
+ CHECK1;
+ str_numset(str,cos(str_gnum(s1)));
+ break;
+ case O_ATAN2:
+ CHECK12;
+ value = str_gnum(s1);
+ str_numset(str,atan2(value, str_gnum(s2)));
+ break;
+ case O_POW:
+ CHECK12;
+ value = str_gnum(s1);
+ str_numset(str,pow(value, str_gnum(s2)));
+ break;
+ case O_LENGTH:
+ if (arg[1].arg_type == A_STAB) {
+ arg->arg_type = O_ITEM;
+ arg[1].arg_type = A_LENSTAB;
+ return arg;
+ }
+ CHECK1;
+ str_numset(str, (double)str_len(s1));
+ break;
+ case O_SLT:
+ CHECK12;
+ str_numset(str,(double)(str_cmp(s1,s2) < 0));
+ break;
+ case O_SGT:
+ CHECK12;
+ str_numset(str,(double)(str_cmp(s1,s2) > 0));
+ break;
+ case O_SLE:
+ CHECK12;
+ str_numset(str,(double)(str_cmp(s1,s2) <= 0));
+ break;
+ case O_SGE:
+ CHECK12;
+ str_numset(str,(double)(str_cmp(s1,s2) >= 0));
+ break;
+ case O_SEQ:
+ CHECK12;
+ str_numset(str,(double)(str_eq(s1,s2)));
+ break;
+ case O_SNE:
+ CHECK12;
+ str_numset(str,(double)(!str_eq(s1,s2)));
+ break;
+ case O_SCMP:
+ CHECK12;
+ str_numset(str,(double)(str_cmp(s1,s2)));
+ break;
+ case O_CRYPT:
+ CHECK12;
+#ifdef HAS_CRYPT
+ tmps = str_get(s1);
+ str_set(str,crypt(tmps,str_get(s2)));
+#else
+ yyerror(
+ "The crypt() function is unimplemented due to excessive paranoia.");
+#endif
+ break;
+ case O_EXP:
+ CHECK1;
+ str_numset(str,exp(str_gnum(s1)));
+ break;
+ case O_LOG:
+ CHECK1;
+ str_numset(str,log(str_gnum(s1)));
+ break;
+ case O_SQRT:
+ CHECK1;
+ str_numset(str,sqrt(str_gnum(s1)));
+ break;
+ case O_INT:
+ CHECK1;
+ value = str_gnum(s1);
+ if (value >= 0.0)
+ (void)modf(value,&value);
+ else {
+ (void)modf(-value,&value);
+ value = -value;
+ }
+ str_numset(str,value);
+ break;
+ case O_ORD:
+ CHECK1;
+#ifndef I286
+ str_numset(str,(double)(*str_get(s1)));
+#else
+ {
+ int zapc;
+ char *zaps;
+
+ zaps = str_get(s1);
+ zapc = (int) *zaps;
+ str_numset(str,(double)(zapc));
+ }
+#endif
+ break;
+ }
+ arg->arg_type = O_ITEM; /* note arg1 type is already SINGLE */
+ str_free(s1);
+ arg[1].arg_ptr.arg_str = str;
+ if (s2) {
+ str_free(s2);
+ arg[2].arg_ptr.arg_str = Nullstr;
+ arg[2].arg_type = A_NULL;
+ }
+ str = Nullstr;
+
+ return arg;
+}
+
+ARG *
+l(arg)
+register ARG *arg;
+{
+ register int i;
+ register ARG *arg1;
+ register ARG *arg2;
+ SPAT *spat;
+ int arghog = 0;
+
+ i = arg[1].arg_type & A_MASK;
+
+ arg->arg_flags |= AF_COMMON; /* assume something in common */
+ /* which forces us to copy things */
+
+ if (i == A_ARYLEN) {
+ arg[1].arg_type = A_LARYLEN;
+ return arg;
+ }
+ if (i == A_ARYSTAB) {
+ arg[1].arg_type = A_LARYSTAB;
+ return arg;
+ }
+
+ /* see if it's an array reference */
+
+ if (i == A_EXPR || i == A_LEXPR) {
+ arg1 = arg[1].arg_ptr.arg_arg;
+
+ if (arg1->arg_type == O_LIST || arg1->arg_type == O_ITEM) {
+ /* assign to list */
+ if (arg->arg_len > 1) {
+ dehoist(arg,2);
+ arg2 = arg[2].arg_ptr.arg_arg;
+ if (nothing_in_common(arg1,arg2))
+ arg->arg_flags &= ~AF_COMMON;
+ if (arg->arg_type == O_ASSIGN) {
+ if (arg1->arg_flags & AF_LOCAL_XX)
+ arg->arg_flags |= AF_LOCAL_XX;
+ arg[1].arg_flags |= AF_ARYOK;
+ arg[2].arg_flags |= AF_ARYOK;
+ }
+ }
+ else if (arg->arg_type != O_CHOP)
+ arg->arg_type = O_ASSIGN; /* possible local(); */
+ for (i = arg1->arg_len; i >= 1; i--) {
+ switch (arg1[i].arg_type) {
+ case A_STAR: case A_LSTAR:
+ arg1[i].arg_type = A_LSTAR;
+ break;
+ case A_STAB: case A_LVAL:
+ arg1[i].arg_type = A_LVAL;
+ break;
+ case A_ARYLEN: case A_LARYLEN:
+ arg1[i].arg_type = A_LARYLEN;
+ break;
+ case A_ARYSTAB: case A_LARYSTAB:
+ arg1[i].arg_type = A_LARYSTAB;
+ break;
+ case A_EXPR: case A_LEXPR:
+ arg1[i].arg_type = A_LEXPR;
+ switch(arg1[i].arg_ptr.arg_arg->arg_type) {
+ case O_ARRAY: case O_LARRAY:
+ arg1[i].arg_ptr.arg_arg->arg_type = O_LARRAY;
+ arghog = 1;
+ break;
+ case O_AELEM: case O_LAELEM:
+ arg1[i].arg_ptr.arg_arg->arg_type = O_LAELEM;
+ break;
+ case O_HASH: case O_LHASH:
+ arg1[i].arg_ptr.arg_arg->arg_type = O_LHASH;
+ arghog = 1;
+ break;
+ case O_HELEM: case O_LHELEM:
+ arg1[i].arg_ptr.arg_arg->arg_type = O_LHELEM;
+ break;
+ case O_ASLICE: case O_LASLICE:
+ arg1[i].arg_ptr.arg_arg->arg_type = O_LASLICE;
+ break;
+ case O_HSLICE: case O_LHSLICE:
+ arg1[i].arg_ptr.arg_arg->arg_type = O_LHSLICE;
+ break;
+ case O_SUBSTR: case O_VEC:
+ (void)l(arg1[i].arg_ptr.arg_arg);
+ Renewc(arg1[i].arg_ptr.arg_arg->arg_ptr.arg_str, 1,
+ struct lstring, STR);
+ /* grow string struct to hold an lstring struct */
+ break;
+ default:
+ goto ill_item;
+ }
+ break;
+ default:
+ ill_item:
+ (void)sprintf(tokenbuf, "Illegal item (%s) as lvalue",
+ argname[arg1[i].arg_type&A_MASK]);
+ yyerror(tokenbuf);
+ }
+ }
+ if (arg->arg_len > 1) {
+ if (arg2->arg_type == O_SPLIT && !arg2[3].arg_type && !arghog) {
+ arg2[3].arg_type = A_SINGLE;
+ arg2[3].arg_ptr.arg_str =
+ str_nmake((double)arg1->arg_len + 1); /* limit split len*/
+ }
+ }
+ }
+ else if (arg1->arg_type == O_AELEM || arg1->arg_type == O_LAELEM)
+ if (arg->arg_type == O_DEFINED)
+ arg1->arg_type = O_AELEM;
+ else
+ arg1->arg_type = O_LAELEM;
+ else if (arg1->arg_type == O_ARRAY || arg1->arg_type == O_LARRAY) {
+ arg1->arg_type = O_LARRAY;
+ if (arg->arg_len > 1) {
+ dehoist(arg,2);
+ arg2 = arg[2].arg_ptr.arg_arg;
+ if (arg2->arg_type == O_SPLIT) { /* use split's builtin =?*/
+ spat = arg2[2].arg_ptr.arg_spat;
+ if (!(spat->spat_flags & SPAT_ONCE) &&
+ nothing_in_common(arg1,spat->spat_repl)) {
+ spat->spat_repl[1].arg_ptr.arg_stab =
+ arg1[1].arg_ptr.arg_stab;
+ arg1[1].arg_ptr.arg_stab = Nullstab;
+ spat->spat_flags |= SPAT_ONCE;
+ arg_free(arg1); /* recursive */
+ arg[1].arg_ptr.arg_arg = Nullarg;
+ free_arg(arg); /* non-recursive */
+ return arg2; /* split has builtin assign */
+ }
+ }
+ else if (nothing_in_common(arg1,arg2))
+ arg->arg_flags &= ~AF_COMMON;
+ if (arg->arg_type == O_ASSIGN) {
+ arg[1].arg_flags |= AF_ARYOK;
+ arg[2].arg_flags |= AF_ARYOK;
+ }
+ }
+ else if (arg->arg_type == O_ASSIGN)
+ arg[1].arg_flags |= AF_ARYOK;
+ }
+ else if (arg1->arg_type == O_HELEM || arg1->arg_type == O_LHELEM)
+ if (arg->arg_type == O_DEFINED)
+ arg1->arg_type = O_HELEM; /* avoid creating one */
+ else
+ arg1->arg_type = O_LHELEM;
+ else if (arg1->arg_type == O_HASH || arg1->arg_type == O_LHASH) {
+ arg1->arg_type = O_LHASH;
+ if (arg->arg_len > 1) {
+ dehoist(arg,2);
+ arg2 = arg[2].arg_ptr.arg_arg;
+ if (nothing_in_common(arg1,arg2))
+ arg->arg_flags &= ~AF_COMMON;
+ if (arg->arg_type == O_ASSIGN) {
+ arg[1].arg_flags |= AF_ARYOK;
+ arg[2].arg_flags |= AF_ARYOK;
+ }
+ }
+ else if (arg->arg_type == O_ASSIGN)
+ arg[1].arg_flags |= AF_ARYOK;
+ }
+ else if (arg1->arg_type == O_ASLICE) {
+ arg1->arg_type = O_LASLICE;
+ if (arg->arg_type == O_ASSIGN) {
+ dehoist(arg,2);
+ arg[1].arg_flags |= AF_ARYOK;
+ arg[2].arg_flags |= AF_ARYOK;
+ }
+ }
+ else if (arg1->arg_type == O_HSLICE) {
+ arg1->arg_type = O_LHSLICE;
+ if (arg->arg_type == O_ASSIGN) {
+ dehoist(arg,2);
+ arg[1].arg_flags |= AF_ARYOK;
+ arg[2].arg_flags |= AF_ARYOK;
+ }
+ }
+ else if ((arg->arg_type == O_DEFINED || arg->arg_type == O_UNDEF) &&
+ (arg1->arg_type == (perldb ? O_DBSUBR : O_SUBR)) ) {
+ arg[1].arg_type |= A_DONT;
+ }
+ else if (arg1->arg_type == O_SUBSTR || arg1->arg_type == O_VEC) {
+ (void)l(arg1);
+ Renewc(arg1->arg_ptr.arg_str, 1, struct lstring, STR);
+ /* grow string struct to hold an lstring struct */
+ }
+ else if (arg1->arg_type == O_ASSIGN)
+ /*SUPPRESS 530*/
+ ;
+ else {
+ (void)sprintf(tokenbuf,
+ "Illegal expression (%s) as lvalue",opname[arg1->arg_type]);
+ yyerror(tokenbuf);
+ return arg;
+ }
+ arg[1].arg_type = A_LEXPR | (arg[1].arg_type & A_DONT);
+ if (arg->arg_type == O_ASSIGN && (arg1[1].arg_flags & AF_ARYOK)) {
+ arg[1].arg_flags |= AF_ARYOK;
+ if (arg->arg_len > 1)
+ arg[2].arg_flags |= AF_ARYOK;
+ }
+#ifdef DEBUGGING
+ if (debug & 16)
+ fprintf(stderr,"lval LEXPR\n");
+#endif
+ return arg;
+ }
+ if (i == A_STAR || i == A_LSTAR) {
+ arg[1].arg_type = A_LSTAR | (arg[1].arg_type & A_DONT);
+ return arg;
+ }
+
+ /* not an array reference, should be a register name */
+
+ if (i != A_STAB && i != A_LVAL) {
+ (void)sprintf(tokenbuf,
+ "Illegal item (%s) as lvalue",argname[arg[1].arg_type&A_MASK]);
+ yyerror(tokenbuf);
+ return arg;
+ }
+ arg[1].arg_type = A_LVAL | (arg[1].arg_type & A_DONT);
+#ifdef DEBUGGING
+ if (debug & 16)
+ fprintf(stderr,"lval LVAL\n");
+#endif
+ return arg;
+}
+
+ARG *
+fixl(type,arg)
+int type;
+ARG *arg;
+{
+ if (type == O_DEFINED || type == O_UNDEF) {
+ if (arg->arg_type != O_ITEM)
+ arg = hide_ary(arg);
+ if (arg->arg_type == O_ITEM) {
+ type = arg[1].arg_type & A_MASK;
+ if (type == A_EXPR || type == A_LEXPR)
+ arg[1].arg_type = A_LEXPR|A_DONT;
+ }
+ }
+ return arg;
+}
+
+void
+dehoist(arg,i)
+ARG *arg;
+{
+ ARG *tmparg;
+
+ if (arg[i].arg_type != A_EXPR) { /* dehoist */
+ tmparg = make_op(O_ITEM,1,Nullarg,Nullarg,Nullarg);
+ tmparg[1] = arg[i];
+ arg[i].arg_ptr.arg_arg = tmparg;
+ arg[i].arg_type = A_EXPR;
+ }
+}
+
+ARG *
+addflags(i,flags,arg)
+register ARG *arg;
+{
+ arg[i].arg_flags |= flags;
+ return arg;
+}
+
+ARG *
+hide_ary(arg)
+ARG *arg;
+{
+ if (arg->arg_type == O_ARRAY || arg->arg_type == O_HASH)
+ return make_op(O_ITEM,1,arg,Nullarg,Nullarg);
+ return arg;
+}
+
+/* maybe do a join on multiple array dimensions */
+
+ARG *
+jmaybe(arg)
+register ARG *arg;
+{
+ if (arg && arg->arg_type == O_COMMA) {
+ arg = listish(arg);
+ arg = make_op(O_JOIN, 2,
+ stab2arg(A_STAB,stabent(";",TRUE)),
+ make_list(arg),
+ Nullarg);
+ }
+ return arg;
+}
+
+ARG *
+make_list(arg)
+register ARG *arg;
+{
+ register int i;
+ register ARG *node;
+ register ARG *nxtnode;
+ register int j;
+ STR *tmpstr;
+
+ if (!arg) {
+ arg = op_new(0);
+ arg->arg_type = O_LIST;
+ }
+ if (arg->arg_type != O_COMMA) {
+ if (arg->arg_type != O_ARRAY)
+ arg->arg_flags |= AF_LISTISH; /* see listish() below */
+ arg->arg_flags |= AF_LISTISH; /* see listish() below */
+ return arg;
+ }
+ for (i = 2, node = arg; ; i++) {
+ if (node->arg_len < 2)
+ break;
+ if (node[1].arg_type != A_EXPR)
+ break;
+ node = node[1].arg_ptr.arg_arg;
+ if (node->arg_type != O_COMMA)
+ break;
+ }
+ if (i > 2) {
+ node = arg;
+ arg = op_new(i);
+ tmpstr = arg->arg_ptr.arg_str;
+ StructCopy(node, arg, ARG); /* copy everything except the STR */
+ arg->arg_ptr.arg_str = tmpstr;
+ for (j = i; ; ) {
+ StructCopy(node+2, arg+j, ARG);
+ arg[j].arg_flags |= AF_ARYOK;
+ --j; /* Bug in Xenix compiler */
+ if (j < 2) {
+ StructCopy(node+1, arg+1, ARG);
+ free_arg(node);
+ break;
+ }
+ nxtnode = node[1].arg_ptr.arg_arg;
+ free_arg(node);
+ node = nxtnode;
+ }
+ }
+ arg[1].arg_flags |= AF_ARYOK;
+ arg[2].arg_flags |= AF_ARYOK;
+ arg->arg_type = O_LIST;
+ arg->arg_len = i;
+ str_free(arg->arg_ptr.arg_str);
+ arg->arg_ptr.arg_str = Nullstr;
+ return arg;
+}
+
+/* turn a single item into a list */
+
+ARG *
+listish(arg)
+ARG *arg;
+{
+ if (arg && arg->arg_flags & AF_LISTISH)
+ arg = make_op(O_LIST,1,arg,Nullarg,Nullarg);
+ return arg;
+}
+
+ARG *
+maybelistish(optype, arg)
+int optype;
+ARG *arg;
+{
+ ARG *tmparg = arg;
+
+ if (optype == O_RETURN && arg->arg_type == O_ITEM &&
+ arg[1].arg_type == A_EXPR && (tmparg = arg[1].arg_ptr.arg_arg) &&
+ ((tmparg->arg_flags & AF_LISTISH) || (tmparg->arg_type == O_ARRAY) )) {
+ tmparg = listish(tmparg);
+ free_arg(arg);
+ arg = tmparg;
+ }
+ else if (optype == O_PRTF ||
+ (arg->arg_type == O_ASLICE || arg->arg_type == O_HSLICE ||
+ arg->arg_type == O_F_OR_R) )
+ arg = listish(arg);
+ return arg;
+}
+
+/* mark list of local variables */
+
+ARG *
+localize(arg)
+ARG *arg;
+{
+ arg->arg_flags |= AF_LOCAL_XX;
+ return arg;
+}
+
+ARG *
+rcatmaybe(arg)
+ARG *arg;
+{
+ ARG *arg2;
+
+ if (arg->arg_type == O_CONCAT && arg[2].arg_type == A_EXPR) {
+ arg2 = arg[2].arg_ptr.arg_arg;
+ if (arg2->arg_type == O_ITEM && arg2[1].arg_type == A_READ) {
+ arg->arg_type = O_RCAT;
+ arg[2].arg_type = arg2[1].arg_type;
+ arg[2].arg_ptr = arg2[1].arg_ptr;
+ free_arg(arg2);
+ }
+ }
+ return arg;
+}
+
+ARG *
+stab2arg(atype,stab)
+int atype;
+register STAB *stab;
+{
+ register ARG *arg;
+
+ arg = op_new(1);
+ arg->arg_type = O_ITEM;
+ arg[1].arg_type = atype;
+ arg[1].arg_ptr.arg_stab = stab;
+ return arg;
+}
+
+ARG *
+cval_to_arg(cval)
+register char *cval;
+{
+ register ARG *arg;
+
+ arg = op_new(1);
+ arg->arg_type = O_ITEM;
+ arg[1].arg_type = A_SINGLE;
+ arg[1].arg_ptr.arg_str = str_make(cval,0);
+ Safefree(cval);
+ return arg;
+}
+
+ARG *
+op_new(numargs)
+int numargs;
+{
+ register ARG *arg;
+
+ Newz(203,arg, numargs + 1, ARG);
+ arg->arg_ptr.arg_str = Str_new(21,0);
+ arg->arg_len = numargs;
+ return arg;
+}
+
+void
+free_arg(arg)
+ARG *arg;
+{
+ str_free(arg->arg_ptr.arg_str);
+ Safefree(arg);
+}
+
+ARG *
+make_match(type,expr,spat)
+int type;
+ARG *expr;
+SPAT *spat;
+{
+ register ARG *arg;
+
+ arg = make_op(type,2,expr,Nullarg,Nullarg);
+
+ arg[2].arg_type = A_SPAT|A_DONT;
+ arg[2].arg_ptr.arg_spat = spat;
+#ifdef DEBUGGING
+ if (debug & 16)
+ fprintf(stderr,"make_match SPAT=%lx\n",(long)spat);
+#endif
+
+ if (type == O_SUBST || type == O_NSUBST) {
+ if (arg[1].arg_type != A_STAB) {
+ yyerror("Illegal lvalue");
+ }
+ arg[1].arg_type = A_LVAL;
+ }
+ return arg;
+}
+
+ARG *
+cmd_to_arg(cmd)
+CMD *cmd;
+{
+ register ARG *arg;
+
+ arg = op_new(1);
+ arg->arg_type = O_ITEM;
+ arg[1].arg_type = A_CMD;
+ arg[1].arg_ptr.arg_cmd = cmd;
+ return arg;
+}
+
+/* Check two expressions to see if there is any identifier in common */
+
+static int
+nothing_in_common(arg1,arg2)
+ARG *arg1;
+ARG *arg2;
+{
+ static int thisexpr = 0; /* I don't care if this wraps */
+
+ thisexpr++;
+ if (arg_common(arg1,thisexpr,1))
+ return 0; /* hit eval or do {} */
+ stab_lastexpr(defstab) = thisexpr; /* pretend to hit @_ */
+ if (arg_common(arg2,thisexpr,0))
+ return 0; /* hit identifier again */
+ return 1;
+}
+
+/* Recursively descend an expression and mark any identifier or check
+ * it to see if it was marked already.
+ */
+
+static int
+arg_common(arg,exprnum,marking)
+register ARG *arg;
+int exprnum;
+int marking;
+{
+ register int i;
+
+ if (!arg)
+ return 0;
+ for (i = arg->arg_len; i >= 1; i--) {
+ switch (arg[i].arg_type & A_MASK) {
+ case A_NULL:
+ break;
+ case A_LEXPR:
+ case A_EXPR:
+ if (arg_common(arg[i].arg_ptr.arg_arg,exprnum,marking))
+ return 1;
+ break;
+ case A_CMD:
+ return 1; /* assume hanky panky */
+ case A_STAR:
+ case A_LSTAR:
+ case A_STAB:
+ case A_LVAL:
+ case A_ARYLEN:
+ case A_LARYLEN:
+ if (marking)
+ stab_lastexpr(arg[i].arg_ptr.arg_stab) = exprnum;
+ else if (stab_lastexpr(arg[i].arg_ptr.arg_stab) == exprnum)
+ return 1;
+ break;
+ case A_DOUBLE:
+ case A_BACKTICK:
+ {
+ register char *s = arg[i].arg_ptr.arg_str->str_ptr;
+ register char *send = s + arg[i].arg_ptr.arg_str->str_cur;
+ register STAB *stab;
+
+ while (*s) {
+ if (*s == '$' && s[1]) {
+ s = scanident(s,send,tokenbuf);
+ stab = stabent(tokenbuf,TRUE);
+ if (marking)
+ stab_lastexpr(stab) = exprnum;
+ else if (stab_lastexpr(stab) == exprnum)
+ return 1;
+ continue;
+ }
+ else if (*s == '\\' && s[1])
+ s++;
+ s++;
+ }
+ }
+ break;
+ case A_SPAT:
+ if (spat_common(arg[i].arg_ptr.arg_spat,exprnum,marking))
+ return 1;
+ break;
+ case A_READ:
+ case A_INDREAD:
+ case A_GLOB:
+ case A_WORD:
+ case A_SINGLE:
+ break;
+ }
+ }
+ switch (arg->arg_type) {
+ case O_ARRAY:
+ case O_LARRAY:
+ if ((arg[1].arg_type & A_MASK) == A_STAB)
+ (void)aadd(arg[1].arg_ptr.arg_stab);
+ break;
+ case O_HASH:
+ case O_LHASH:
+ if ((arg[1].arg_type & A_MASK) == A_STAB)
+ (void)hadd(arg[1].arg_ptr.arg_stab);
+ break;
+ case O_EVAL:
+ case O_SUBR:
+ case O_DBSUBR:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+spat_common(spat,exprnum,marking)
+register SPAT *spat;
+int exprnum;
+int marking;
+{
+ if (spat->spat_runtime)
+ if (arg_common(spat->spat_runtime,exprnum,marking))
+ return 1;
+ if (spat->spat_repl) {
+ if (arg_common(spat->spat_repl,exprnum,marking))
+ return 1;
+ }
+ return 0;
+}
diff --git a/gnu/usr.bin/perl/perl/crypt.c b/gnu/usr.bin/perl/perl/crypt.c
new file mode 100644
index 0000000..3299a86
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/crypt.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Tom Truscott.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/* from static char sccsid[] = "@(#)crypt.c 5.11 (Berkeley) 6/25/91"; */
+static char rcsid[] = "$Header: /home/cvs/386BSD/src/lib/libc/gen/crypt.c,v 1.6 1993/08/29 22:03:56 nate Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <unistd.h>
+#include <stdio.h>
+
+/*
+ * UNIX password, and DES, encryption.
+ *
+ * since this is non-exportable, this is just a dummy. if you want real
+ * encryption, make sure you've got libcrypt.a around.
+ */
+
+#define SCRAMBLE /* Don't leave them in plaintext */
+
+#ifndef SCRAMBLE
+static char cryptresult[1+4+4+11+1]; /* "encrypted" result */
+
+char *
+crypt(key, setting)
+ register const char *key;
+ register const char *setting;
+{
+ fprintf(stderr, "WARNING! crypt(3) not present in the system!\n");
+ strncpy(cryptresult, key, sizeof cryptresult);
+ cryptresult[sizeof cryptresult - 1] = '\0';
+ return (cryptresult);
+}
+
+#else
+
+char *
+crypt(pw, salt)
+ register const char *pw;
+ register const char *salt;
+{
+ static char password[14];
+ long matrix[128], *m, vector[2];
+ char a, b, *p;
+ int i, value;
+ unsigned short crc;
+ unsigned long t;
+
+ /* Ugly hack, but I'm too lazy to find the real problem - NW */
+ bzero(matrix, 128 * sizeof(long));
+
+ if (salt[0]) {
+ a = salt[0];
+ if (salt[1])
+ b = salt[1];
+ else
+ b = a;
+ } else
+ a = b = '0';
+ password[0] = a;
+ password[1] = b;
+ if (a > 'Z')
+ a -= 6;
+ if (a > '9')
+ a -= 7;
+ if (b > 'Z')
+ b -= 6;
+ if (b > '9')
+ b -= 7;
+ a -= '.';
+ b -= '.';
+ value = (a | (b << 6)) & 07777;
+
+ crc = value;
+ value += 1000;
+ b = 0;
+ p = (char *)pw;
+ while (value--) {
+ if (crc & 0x8000)
+ crc = (crc << 1) ^ 0x1021;
+ else
+ crc <<= 1;
+ if (!b) {
+ b = 8;
+ if (!(i = *p++)) {
+ p = (char *)pw;
+ i = *p++;
+ }
+ }
+ if (i & 0x80)
+ crc ^= 1;
+ i <<= 1;
+ b--;
+ }
+
+ m = matrix;
+ matrix[0] = 0;
+ a = 32;
+ for (value = 07777; value >= 0; value--) {
+ *m <<= 1;
+ if (crc & 0x8000) {
+ *m |= 1;
+ crc = (crc << 1) ^ 0x1021;
+ } else
+ crc <<= 1;
+ if (!b) {
+ b = 8;
+ if (!(i = *p++)) {
+ p = (char *)pw;
+ i = *p++;
+ }
+ }
+ if (i & 0x80)
+ crc ^= 1;
+ i <<= 1;
+ b--;
+ if (!(a--)) {
+ a = 32;
+ *++m = 0;
+ }
+ }
+
+ vector[0] = 0;
+ vector[1] = 0;
+ p = (char *) vector;
+ for (i = 0; i < 7; i++)
+ if (pw[i])
+ *p++ = pw[i];
+ else
+ break;
+
+ p = password + 2;
+ a = 6;
+ m = matrix;
+ *p = 0;
+ for (i = 077; i >= 0; i--) {
+ t = *m++;
+ t = t ^ *m++;
+ t = t ^ vector[0];
+ t = t ^ vector[1];
+ b = 0;
+ while (t) {
+ if (t & 1)
+ b = 1 - b;
+ t >>= 1;
+ }
+ a--;
+ if (b)
+ *p |= 1 << a;
+ if (!a) {
+ a = 6;
+ *++p = 0;
+ }
+ }
+
+ for (i = 2; i < 13; i++) {
+ password[i] += '.';
+ if (password[i] > '9')
+ password[i] += 7;
+ if (password[i] > 'Z')
+ password[i] += 6;
+ }
+ password[13] = 0;
+
+ return password;
+}
+#endif
diff --git a/gnu/usr.bin/perl/perl/doarg.c b/gnu/usr.bin/perl/perl/doarg.c
new file mode 100644
index 0000000..7c03bff
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/doarg.c
@@ -0,0 +1,1852 @@
+/* $RCSfile: doarg.c,v $$Revision: 1.1.1.1 $$Date: 1994/09/10 06:27:32 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: doarg.c,v $
+ * Revision 1.1.1.1 1994/09/10 06:27:32 gclarkii
+ * Initial import of Perl 4.046 bmaked
+ *
+ * Revision 1.1.1.1 1993/08/23 21:29:35 nate
+ * PERL!
+ *
+ * Revision 4.0.1.8 1993/02/05 19:32:27 lwall
+ * patch36: substitution didn't always invalidate numericity
+ *
+ * Revision 4.0.1.7 92/06/11 21:07:11 lwall
+ * patch34: join with null list attempted negative allocation
+ * patch34: sprintf("%6.4s", "abcdefg") didn't print "abcd "
+ *
+ * Revision 4.0.1.6 92/06/08 12:34:30 lwall
+ * patch20: removed implicit int declarations on funcions
+ * patch20: pattern modifiers i and o didn't interact right
+ * patch20: join() now pre-extends target string to avoid excessive copying
+ * patch20: fixed confusion between a *var's real name and its effective name
+ * patch20: subroutines didn't localize $`, $&, $', $1 et al correctly
+ * patch20: usersub routines didn't reclaim temp values soon enough
+ * patch20: ($<,$>) = ... didn't work on some architectures
+ * patch20: added Atari ST portability
+ *
+ * Revision 4.0.1.5 91/11/11 16:31:58 lwall
+ * patch19: added little-endian pack/unpack options
+ *
+ * Revision 4.0.1.4 91/11/05 16:35:06 lwall
+ * patch11: /$foo/o optimizer could access deallocated data
+ * patch11: minimum match length calculation in regexp is now cumulative
+ * patch11: added some support for 64-bit integers
+ * patch11: prepared for ctype implementations that don't define isascii()
+ * patch11: sprintf() now supports any length of s field
+ * patch11: indirect subroutine calls through magic vars (e.g. &$1) didn't work
+ * patch11: defined(&$foo) and undef(&$foo) didn't work
+ *
+ * Revision 4.0.1.3 91/06/10 01:18:41 lwall
+ * patch10: pack(hh,1) dumped core
+ *
+ * Revision 4.0.1.2 91/06/07 10:42:17 lwall
+ * patch4: new copyright notice
+ * patch4: // wouldn't use previous pattern if it started with a null character
+ * patch4: //o and s///o now optimize themselves fully at runtime
+ * patch4: added global modifier for pattern matches
+ * patch4: undef @array disabled "@array" interpolation
+ * patch4: chop("") was returning "\0" rather than ""
+ * patch4: vector logical operations &, | and ^ sometimes returned null string
+ * patch4: syscall couldn't pass numbers with most significant bit set on sparcs
+ *
+ * Revision 4.0.1.1 91/04/11 17:40:14 lwall
+ * patch1: fixed undefined environ problem
+ * patch1: fixed debugger coredump on subroutines
+ *
+ * Revision 4.0 91/03/20 01:06:42 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+
+#if !defined(NSIG) || defined(M_UNIX) || defined(M_XENIX)
+#include <signal.h>
+#endif
+
+extern unsigned char fold[];
+
+#ifdef BUGGY_MSC
+ #pragma function(memcmp)
+#endif /* BUGGY_MSC */
+
+static void doencodes();
+
+int
+do_subst(str,arg,sp)
+STR *str;
+ARG *arg;
+int sp;
+{
+ register SPAT *spat;
+ SPAT *rspat;
+ register STR *dstr;
+ register char *s = str_get(str);
+ char *strend = s + str->str_cur;
+ register char *m;
+ char *c;
+ register char *d;
+ int clen;
+ int iters = 0;
+ int maxiters = (strend - s) + 10;
+ register int i;
+ bool once;
+ char *orig;
+ int safebase;
+
+ rspat = spat = arg[2].arg_ptr.arg_spat;
+ if (!spat || !s)
+ fatal("panic: do_subst");
+ else if (spat->spat_runtime) {
+ nointrp = "|)";
+ (void)eval(spat->spat_runtime,G_SCALAR,sp);
+ m = str_get(dstr = stack->ary_array[sp+1]);
+ nointrp = "";
+ if (spat->spat_regexp) {
+ regfree(spat->spat_regexp);
+ spat->spat_regexp = Null(REGEXP*); /* required if regcomp pukes */
+ }
+ spat->spat_regexp = regcomp(m,m+dstr->str_cur,
+ spat->spat_flags & SPAT_FOLD);
+ if (spat->spat_flags & SPAT_KEEP) {
+ if (!(spat->spat_flags & SPAT_FOLD))
+ scanconst(spat, m, dstr->str_cur);
+ arg_free(spat->spat_runtime); /* it won't change, so */
+ spat->spat_runtime = Nullarg; /* no point compiling again */
+ hoistmust(spat);
+ if (curcmd->c_expr && (curcmd->c_flags & CF_OPTIMIZE) == CFT_EVAL) {
+ curcmd->c_flags &= ~CF_OPTIMIZE;
+ opt_arg(curcmd, 1, curcmd->c_type == C_EXPR);
+ }
+ }
+ }
+#ifdef DEBUGGING
+ if (debug & 8) {
+ deb("2.SPAT /%s/\n",spat->spat_regexp->precomp);
+ }
+#endif
+ safebase = ((!spat->spat_regexp || !spat->spat_regexp->nparens) &&
+ !sawampersand);
+ if (!spat->spat_regexp->prelen && lastspat)
+ spat = lastspat;
+ orig = m = s;
+ if (hint) {
+ if (hint < s || hint > strend)
+ fatal("panic: hint in do_match");
+ s = hint;
+ hint = Nullch;
+ if (spat->spat_regexp->regback >= 0) {
+ s -= spat->spat_regexp->regback;
+ if (s < m)
+ s = m;
+ }
+ else
+ s = m;
+ }
+ else if (spat->spat_short) {
+ if (spat->spat_flags & SPAT_SCANFIRST) {
+ if (str->str_pok & SP_STUDIED) {
+ if (screamfirst[spat->spat_short->str_rare] < 0)
+ goto nope;
+ else if (!(s = screaminstr(str,spat->spat_short)))
+ goto nope;
+ }
+#ifndef lint
+ else if (!(s = fbminstr((unsigned char*)s, (unsigned char*)strend,
+ spat->spat_short)))
+ goto nope;
+#endif
+ if (s && spat->spat_regexp->regback >= 0) {
+ ++spat->spat_short->str_u.str_useful;
+ s -= spat->spat_regexp->regback;
+ if (s < m)
+ s = m;
+ }
+ else
+ s = m;
+ }
+ else if (!multiline && (*spat->spat_short->str_ptr != *s ||
+ bcmp(spat->spat_short->str_ptr, s, spat->spat_slen) ))
+ goto nope;
+ if (--spat->spat_short->str_u.str_useful < 0) {
+ str_free(spat->spat_short);
+ spat->spat_short = Nullstr; /* opt is being useless */
+ }
+ }
+ once = !(rspat->spat_flags & SPAT_GLOBAL);
+ if (rspat->spat_flags & SPAT_CONST) { /* known replacement string? */
+ if ((rspat->spat_repl[1].arg_type & A_MASK) == A_SINGLE)
+ dstr = rspat->spat_repl[1].arg_ptr.arg_str;
+ else { /* constant over loop, anyway */
+ (void)eval(rspat->spat_repl,G_SCALAR,sp);
+ dstr = stack->ary_array[sp+1];
+ }
+ c = str_get(dstr);
+ clen = dstr->str_cur;
+ if (clen <= spat->spat_regexp->minlen) {
+ /* can do inplace substitution */
+ if (regexec(spat->spat_regexp, s, strend, orig, 0,
+ str->str_pok & SP_STUDIED ? str : Nullstr, safebase)) {
+ if (spat->spat_regexp->subbase) /* oops, no we can't */
+ goto long_way;
+ d = s;
+ lastspat = spat;
+ str->str_pok = SP_VALID; /* disable possible screamer */
+ if (once) {
+ m = spat->spat_regexp->startp[0];
+ d = spat->spat_regexp->endp[0];
+ s = orig;
+ if (m - s > strend - d) { /* faster to shorten from end */
+ if (clen) {
+ Copy(c, m, clen, char);
+ m += clen;
+ }
+ i = strend - d;
+ if (i > 0) {
+ Move(d, m, i, char);
+ m += i;
+ }
+ *m = '\0';
+ str->str_cur = m - s;
+ STABSET(str);
+ str_numset(arg->arg_ptr.arg_str, 1.0);
+ stack->ary_array[++sp] = arg->arg_ptr.arg_str;
+ str->str_nok = 0;
+ return sp;
+ }
+ /*SUPPRESS 560*/
+ else if (i = m - s) { /* faster from front */
+ d -= clen;
+ m = d;
+ str_chop(str,d-i);
+ s += i;
+ while (i--)
+ *--d = *--s;
+ if (clen)
+ Copy(c, m, clen, char);
+ STABSET(str);
+ str_numset(arg->arg_ptr.arg_str, 1.0);
+ stack->ary_array[++sp] = arg->arg_ptr.arg_str;
+ str->str_nok = 0;
+ return sp;
+ }
+ else if (clen) {
+ d -= clen;
+ str_chop(str,d);
+ Copy(c,d,clen,char);
+ STABSET(str);
+ str_numset(arg->arg_ptr.arg_str, 1.0);
+ stack->ary_array[++sp] = arg->arg_ptr.arg_str;
+ str->str_nok = 0;
+ return sp;
+ }
+ else {
+ str_chop(str,d);
+ STABSET(str);
+ str_numset(arg->arg_ptr.arg_str, 1.0);
+ stack->ary_array[++sp] = arg->arg_ptr.arg_str;
+ str->str_nok = 0;
+ return sp;
+ }
+ /* NOTREACHED */
+ }
+ do {
+ if (iters++ > maxiters)
+ fatal("Substitution loop");
+ m = spat->spat_regexp->startp[0];
+ /*SUPPRESS 560*/
+ if (i = m - s) {
+ if (s != d)
+ Move(s,d,i,char);
+ d += i;
+ }
+ if (clen) {
+ Copy(c,d,clen,char);
+ d += clen;
+ }
+ s = spat->spat_regexp->endp[0];
+ } while (regexec(spat->spat_regexp, s, strend, orig, s == m,
+ Nullstr, TRUE)); /* (don't match same null twice) */
+ if (s != d) {
+ i = strend - s;
+ str->str_cur = d - str->str_ptr + i;
+ Move(s,d,i+1,char); /* include the Null */
+ }
+ STABSET(str);
+ str_numset(arg->arg_ptr.arg_str, (double)iters);
+ stack->ary_array[++sp] = arg->arg_ptr.arg_str;
+ str->str_nok = 0;
+ return sp;
+ }
+ str_numset(arg->arg_ptr.arg_str, 0.0);
+ stack->ary_array[++sp] = arg->arg_ptr.arg_str;
+ return sp;
+ }
+ }
+ else
+ c = Nullch;
+ if (regexec(spat->spat_regexp, s, strend, orig, 0,
+ str->str_pok & SP_STUDIED ? str : Nullstr, safebase)) {
+ long_way:
+ dstr = Str_new(25,str_len(str));
+ str_nset(dstr,m,s-m);
+ if (spat->spat_regexp->subbase)
+ curspat = spat;
+ lastspat = spat;
+ do {
+ if (iters++ > maxiters)
+ fatal("Substitution loop");
+ if (spat->spat_regexp->subbase
+ && spat->spat_regexp->subbase != orig) {
+ m = s;
+ s = orig;
+ orig = spat->spat_regexp->subbase;
+ s = orig + (m - s);
+ strend = s + (strend - m);
+ }
+ m = spat->spat_regexp->startp[0];
+ str_ncat(dstr,s,m-s);
+ s = spat->spat_regexp->endp[0];
+ if (c) {
+ if (clen)
+ str_ncat(dstr,c,clen);
+ }
+ else {
+ char *mysubbase = spat->spat_regexp->subbase;
+
+ spat->spat_regexp->subbase = Nullch; /* so recursion works */
+ (void)eval(rspat->spat_repl,G_SCALAR,sp);
+ str_scat(dstr,stack->ary_array[sp+1]);
+ if (spat->spat_regexp->subbase)
+ Safefree(spat->spat_regexp->subbase);
+ spat->spat_regexp->subbase = mysubbase;
+ }
+ if (once)
+ break;
+ } while (regexec(spat->spat_regexp, s, strend, orig, s == m, Nullstr,
+ safebase));
+ str_ncat(dstr,s,strend - s);
+ str_replace(str,dstr);
+ STABSET(str);
+ str_numset(arg->arg_ptr.arg_str, (double)iters);
+ stack->ary_array[++sp] = arg->arg_ptr.arg_str;
+ str->str_nok = 0;
+ return sp;
+ }
+ str_numset(arg->arg_ptr.arg_str, 0.0);
+ stack->ary_array[++sp] = arg->arg_ptr.arg_str;
+ return sp;
+
+nope:
+ ++spat->spat_short->str_u.str_useful;
+ str_numset(arg->arg_ptr.arg_str, 0.0);
+ stack->ary_array[++sp] = arg->arg_ptr.arg_str;
+ return sp;
+}
+#ifdef BUGGY_MSC
+ #pragma intrinsic(memcmp)
+#endif /* BUGGY_MSC */
+
+int
+do_trans(str,arg)
+STR *str;
+ARG *arg;
+{
+ register short *tbl;
+ register char *s;
+ register int matches = 0;
+ register int ch;
+ register char *send;
+ register char *d;
+ register int squash = arg[2].arg_len & 1;
+
+ tbl = (short*) arg[2].arg_ptr.arg_cval;
+ s = str_get(str);
+ send = s + str->str_cur;
+ if (!tbl || !s)
+ fatal("panic: do_trans");
+#ifdef DEBUGGING
+ if (debug & 8) {
+ deb("2.TBL\n");
+ }
+#endif
+ if (!arg[2].arg_len) {
+ while (s < send) {
+ if ((ch = tbl[*s & 0377]) >= 0) {
+ matches++;
+ *s = ch;
+ }
+ s++;
+ }
+ }
+ else {
+ d = s;
+ while (s < send) {
+ if ((ch = tbl[*s & 0377]) >= 0) {
+ *d = ch;
+ if (matches++ && squash) {
+ if (d[-1] == *d)
+ matches--;
+ else
+ d++;
+ }
+ else
+ d++;
+ }
+ else if (ch == -1) /* -1 is unmapped character */
+ *d++ = *s; /* -2 is delete character */
+ s++;
+ }
+ matches += send - d; /* account for disappeared chars */
+ *d = '\0';
+ str->str_cur = d - str->str_ptr;
+ }
+ STABSET(str);
+ return matches;
+}
+
+void
+do_join(str,arglast)
+register STR *str;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ int sp = arglast[1];
+ register int items = arglast[2] - sp;
+ register char *delim = str_get(st[sp]);
+ register STRLEN len;
+ int delimlen = st[sp]->str_cur;
+
+ st += sp + 1;
+
+ len = (items > 0 ? (delimlen * (items - 1) ) : 0);
+ if (str->str_len < len + items) { /* current length is way too short */
+ while (items-- > 0) {
+ if (*st)
+ len += (*st)->str_cur;
+ st++;
+ }
+ STR_GROW(str, len + 1); /* so try to pre-extend */
+
+ items = arglast[2] - sp;
+ st -= items;
+ }
+
+ if (items-- > 0)
+ str_sset(str, *st++);
+ else
+ str_set(str,"");
+ len = delimlen;
+ if (len) {
+ for (; items > 0; items--,st++) {
+ str_ncat(str,delim,len);
+ str_scat(str,*st);
+ }
+ }
+ else {
+ for (; items > 0; items--,st++)
+ str_scat(str,*st);
+ }
+ STABSET(str);
+}
+
+void
+do_pack(str,arglast)
+register STR *str;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register int items;
+ register char *pat = str_get(st[sp]);
+ register char *patend = pat + st[sp]->str_cur;
+ register int len;
+ int datumtype;
+ STR *fromstr;
+ /*SUPPRESS 442*/
+ static char *null10 = "\0\0\0\0\0\0\0\0\0\0";
+ static char *space10 = " ";
+
+ /* These must not be in registers: */
+ char achar;
+ short ashort;
+ int aint;
+ unsigned int auint;
+ long along;
+ unsigned long aulong;
+#ifdef QUAD
+ quad aquad;
+ unsigned quad auquad;
+#endif
+ char *aptr;
+ float afloat;
+ double adouble;
+
+ items = arglast[2] - sp;
+ st += ++sp;
+ str_nset(str,"",0);
+ while (pat < patend) {
+#define NEXTFROM (items-- > 0 ? *st++ : &str_no)
+ datumtype = *pat++;
+ if (*pat == '*') {
+ len = index("@Xxu",datumtype) ? 0 : items;
+ pat++;
+ }
+ else if (isDIGIT(*pat)) {
+ len = *pat++ - '0';
+ while (isDIGIT(*pat))
+ len = (len * 10) + (*pat++ - '0');
+ }
+ else
+ len = 1;
+ switch(datumtype) {
+ default:
+ break;
+ case '%':
+ fatal("% may only be used in unpack");
+ case '@':
+ len -= str->str_cur;
+ if (len > 0)
+ goto grow;
+ len = -len;
+ if (len > 0)
+ goto shrink;
+ break;
+ case 'X':
+ shrink:
+ if (str->str_cur < len)
+ fatal("X outside of string");
+ str->str_cur -= len;
+ str->str_ptr[str->str_cur] = '\0';
+ break;
+ case 'x':
+ grow:
+ while (len >= 10) {
+ str_ncat(str,null10,10);
+ len -= 10;
+ }
+ str_ncat(str,null10,len);
+ break;
+ case 'A':
+ case 'a':
+ fromstr = NEXTFROM;
+ aptr = str_get(fromstr);
+ if (pat[-1] == '*')
+ len = fromstr->str_cur;
+ if (fromstr->str_cur > len)
+ str_ncat(str,aptr,len);
+ else {
+ str_ncat(str,aptr,fromstr->str_cur);
+ len -= fromstr->str_cur;
+ if (datumtype == 'A') {
+ while (len >= 10) {
+ str_ncat(str,space10,10);
+ len -= 10;
+ }
+ str_ncat(str,space10,len);
+ }
+ else {
+ while (len >= 10) {
+ str_ncat(str,null10,10);
+ len -= 10;
+ }
+ str_ncat(str,null10,len);
+ }
+ }
+ break;
+ case 'B':
+ case 'b':
+ {
+ char *savepat = pat;
+ int saveitems;
+
+ fromstr = NEXTFROM;
+ saveitems = items;
+ aptr = str_get(fromstr);
+ if (pat[-1] == '*')
+ len = fromstr->str_cur;
+ pat = aptr;
+ aint = str->str_cur;
+ str->str_cur += (len+7)/8;
+ STR_GROW(str, str->str_cur + 1);
+ aptr = str->str_ptr + aint;
+ if (len > fromstr->str_cur)
+ len = fromstr->str_cur;
+ aint = len;
+ items = 0;
+ if (datumtype == 'B') {
+ for (len = 0; len++ < aint;) {
+ items |= *pat++ & 1;
+ if (len & 7)
+ items <<= 1;
+ else {
+ *aptr++ = items & 0xff;
+ items = 0;
+ }
+ }
+ }
+ else {
+ for (len = 0; len++ < aint;) {
+ if (*pat++ & 1)
+ items |= 128;
+ if (len & 7)
+ items >>= 1;
+ else {
+ *aptr++ = items & 0xff;
+ items = 0;
+ }
+ }
+ }
+ if (aint & 7) {
+ if (datumtype == 'B')
+ items <<= 7 - (aint & 7);
+ else
+ items >>= 7 - (aint & 7);
+ *aptr++ = items & 0xff;
+ }
+ pat = str->str_ptr + str->str_cur;
+ while (aptr <= pat)
+ *aptr++ = '\0';
+
+ pat = savepat;
+ items = saveitems;
+ }
+ break;
+ case 'H':
+ case 'h':
+ {
+ char *savepat = pat;
+ int saveitems;
+
+ fromstr = NEXTFROM;
+ saveitems = items;
+ aptr = str_get(fromstr);
+ if (pat[-1] == '*')
+ len = fromstr->str_cur;
+ pat = aptr;
+ aint = str->str_cur;
+ str->str_cur += (len+1)/2;
+ STR_GROW(str, str->str_cur + 1);
+ aptr = str->str_ptr + aint;
+ if (len > fromstr->str_cur)
+ len = fromstr->str_cur;
+ aint = len;
+ items = 0;
+ if (datumtype == 'H') {
+ for (len = 0; len++ < aint;) {
+ if (isALPHA(*pat))
+ items |= ((*pat++ & 15) + 9) & 15;
+ else
+ items |= *pat++ & 15;
+ if (len & 1)
+ items <<= 4;
+ else {
+ *aptr++ = items & 0xff;
+ items = 0;
+ }
+ }
+ }
+ else {
+ for (len = 0; len++ < aint;) {
+ if (isALPHA(*pat))
+ items |= (((*pat++ & 15) + 9) & 15) << 4;
+ else
+ items |= (*pat++ & 15) << 4;
+ if (len & 1)
+ items >>= 4;
+ else {
+ *aptr++ = items & 0xff;
+ items = 0;
+ }
+ }
+ }
+ if (aint & 1)
+ *aptr++ = items & 0xff;
+ pat = str->str_ptr + str->str_cur;
+ while (aptr <= pat)
+ *aptr++ = '\0';
+
+ pat = savepat;
+ items = saveitems;
+ }
+ break;
+ case 'C':
+ case 'c':
+ while (len-- > 0) {
+ fromstr = NEXTFROM;
+ aint = (int)str_gnum(fromstr);
+ achar = aint;
+ str_ncat(str,&achar,sizeof(char));
+ }
+ break;
+ /* Float and double added by gnb@melba.bby.oz.au 22/11/89 */
+ case 'f':
+ case 'F':
+ while (len-- > 0) {
+ fromstr = NEXTFROM;
+ afloat = (float)str_gnum(fromstr);
+ str_ncat(str, (char *)&afloat, sizeof (float));
+ }
+ break;
+ case 'd':
+ case 'D':
+ while (len-- > 0) {
+ fromstr = NEXTFROM;
+ adouble = (double)str_gnum(fromstr);
+ str_ncat(str, (char *)&adouble, sizeof (double));
+ }
+ break;
+ case 'n':
+ while (len-- > 0) {
+ fromstr = NEXTFROM;
+ ashort = (short)str_gnum(fromstr);
+#ifdef HAS_HTONS
+ ashort = htons(ashort);
+#endif
+ str_ncat(str,(char*)&ashort,sizeof(short));
+ }
+ break;
+ case 'v':
+ while (len-- > 0) {
+ fromstr = NEXTFROM;
+ ashort = (short)str_gnum(fromstr);
+#ifdef HAS_HTOVS
+ ashort = htovs(ashort);
+#endif
+ str_ncat(str,(char*)&ashort,sizeof(short));
+ }
+ break;
+ case 'S':
+ case 's':
+ while (len-- > 0) {
+ fromstr = NEXTFROM;
+ ashort = (short)str_gnum(fromstr);
+ str_ncat(str,(char*)&ashort,sizeof(short));
+ }
+ break;
+ case 'I':
+ while (len-- > 0) {
+ fromstr = NEXTFROM;
+ auint = U_I(str_gnum(fromstr));
+ str_ncat(str,(char*)&auint,sizeof(unsigned int));
+ }
+ break;
+ case 'i':
+ while (len-- > 0) {
+ fromstr = NEXTFROM;
+ aint = (int)str_gnum(fromstr);
+ str_ncat(str,(char*)&aint,sizeof(int));
+ }
+ break;
+ case 'N':
+ while (len-- > 0) {
+ fromstr = NEXTFROM;
+ aulong = U_L(str_gnum(fromstr));
+#ifdef HAS_HTONL
+ aulong = htonl(aulong);
+#endif
+ str_ncat(str,(char*)&aulong,sizeof(unsigned long));
+ }
+ break;
+ case 'V':
+ while (len-- > 0) {
+ fromstr = NEXTFROM;
+ aulong = U_L(str_gnum(fromstr));
+#ifdef HAS_HTOVL
+ aulong = htovl(aulong);
+#endif
+ str_ncat(str,(char*)&aulong,sizeof(unsigned long));
+ }
+ break;
+ case 'L':
+ while (len-- > 0) {
+ fromstr = NEXTFROM;
+ aulong = U_L(str_gnum(fromstr));
+ str_ncat(str,(char*)&aulong,sizeof(unsigned long));
+ }
+ break;
+ case 'l':
+ while (len-- > 0) {
+ fromstr = NEXTFROM;
+ along = (long)str_gnum(fromstr);
+ str_ncat(str,(char*)&along,sizeof(long));
+ }
+ break;
+#ifdef QUAD
+ case 'Q':
+ while (len-- > 0) {
+ fromstr = NEXTFROM;
+ auquad = (unsigned quad)str_gnum(fromstr);
+ str_ncat(str,(char*)&auquad,sizeof(unsigned quad));
+ }
+ break;
+ case 'q':
+ while (len-- > 0) {
+ fromstr = NEXTFROM;
+ aquad = (quad)str_gnum(fromstr);
+ str_ncat(str,(char*)&aquad,sizeof(quad));
+ }
+ break;
+#endif /* QUAD */
+ case 'p':
+ while (len-- > 0) {
+ fromstr = NEXTFROM;
+ aptr = str_get(fromstr);
+ str_ncat(str,(char*)&aptr,sizeof(char*));
+ }
+ break;
+ case 'u':
+ fromstr = NEXTFROM;
+ aptr = str_get(fromstr);
+ aint = fromstr->str_cur;
+ STR_GROW(str,aint * 4 / 3);
+ if (len <= 1)
+ len = 45;
+ else
+ len = len / 3 * 3;
+ while (aint > 0) {
+ int todo;
+
+ if (aint > len)
+ todo = len;
+ else
+ todo = aint;
+ doencodes(str, aptr, todo);
+ aint -= todo;
+ aptr += todo;
+ }
+ break;
+ }
+ }
+ STABSET(str);
+}
+#undef NEXTFROM
+
+static void
+doencodes(str, s, len)
+register STR *str;
+register char *s;
+register int len;
+{
+ char hunk[5];
+
+ *hunk = len + ' ';
+ str_ncat(str, hunk, 1);
+ hunk[4] = '\0';
+ while (len > 0) {
+ hunk[0] = ' ' + (077 & (*s >> 2));
+ hunk[1] = ' ' + (077 & ((*s << 4) & 060 | (s[1] >> 4) & 017));
+ hunk[2] = ' ' + (077 & ((s[1] << 2) & 074 | (s[2] >> 6) & 03));
+ hunk[3] = ' ' + (077 & (s[2] & 077));
+ str_ncat(str, hunk, 4);
+ s += 3;
+ len -= 3;
+ }
+ for (s = str->str_ptr; *s; s++) {
+ if (*s == ' ')
+ *s = '`';
+ }
+ str_ncat(str, "\n", 1);
+}
+
+void
+do_sprintf(str,len,sarg)
+register STR *str;
+register int len;
+register STR **sarg;
+{
+ register char *s;
+ register char *t;
+ register char *f;
+ bool dolong;
+#ifdef QUAD
+ bool doquad;
+#endif /* QUAD */
+ char ch;
+ static STR *sargnull = &str_no;
+ register char *send;
+ register STR *arg;
+ char *xs;
+ int xlen;
+ int pre;
+ int post;
+ double value;
+
+ str_set(str,"");
+ len--; /* don't count pattern string */
+ t = s = str_get(*sarg);
+ send = s + (*sarg)->str_cur;
+ sarg++;
+ for ( ; ; len--) {
+
+ /*SUPPRESS 560*/
+ if (len <= 0 || !(arg = *sarg++))
+ arg = sargnull;
+
+ /*SUPPRESS 530*/
+ for ( ; t < send && *t != '%'; t++) ;
+ if (t >= send)
+ break; /* end of format string, ignore extra args */
+ f = t;
+ *buf = '\0';
+ xs = buf;
+#ifdef QUAD
+ doquad =
+#endif /* QUAD */
+ dolong = FALSE;
+ pre = post = 0;
+ for (t++; t < send; t++) {
+ switch (*t) {
+ default:
+ ch = *(++t);
+ *t = '\0';
+ (void)sprintf(xs,f);
+ len++, sarg--;
+ xlen = strlen(xs);
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '.': case '#': case '-': case '+': case ' ':
+ continue;
+ case 'l':
+#ifdef QUAD
+ if (dolong) {
+ dolong = FALSE;
+ doquad = TRUE;
+ } else
+#endif
+ dolong = TRUE;
+ continue;
+ case 'c':
+ ch = *(++t);
+ *t = '\0';
+ xlen = (int)str_gnum(arg);
+ if (strEQ(f,"%c")) { /* some printfs fail on null chars */
+ *xs = xlen;
+ xs[1] = '\0';
+ xlen = 1;
+ }
+ else {
+ (void)sprintf(xs,f,xlen);
+ xlen = strlen(xs);
+ }
+ break;
+ case 'D':
+ dolong = TRUE;
+ /* FALL THROUGH */
+ case 'd':
+ ch = *(++t);
+ *t = '\0';
+#ifdef QUAD
+ if (doquad)
+ (void)sprintf(buf,s,(quad)str_gnum(arg));
+ else
+#endif
+ if (dolong)
+ (void)sprintf(xs,f,(long)str_gnum(arg));
+ else
+ (void)sprintf(xs,f,(int)str_gnum(arg));
+ xlen = strlen(xs);
+ break;
+ case 'X': case 'O':
+ dolong = TRUE;
+ /* FALL THROUGH */
+ case 'x': case 'o': case 'u':
+ ch = *(++t);
+ *t = '\0';
+ value = str_gnum(arg);
+#ifdef QUAD
+ if (doquad)
+ (void)sprintf(buf,s,(unsigned quad)value);
+ else
+#endif
+ if (dolong)
+ (void)sprintf(xs,f,U_L(value));
+ else
+ (void)sprintf(xs,f,U_I(value));
+ xlen = strlen(xs);
+ break;
+ case 'E': case 'e': case 'f': case 'G': case 'g':
+ ch = *(++t);
+ *t = '\0';
+ (void)sprintf(xs,f,str_gnum(arg));
+ xlen = strlen(xs);
+ break;
+ case 's':
+ ch = *(++t);
+ *t = '\0';
+ xs = str_get(arg);
+ xlen = arg->str_cur;
+ if (*xs == 'S' && xs[1] == 't' && xs[2] == 'B' && xs[3] == '\0'
+ && xlen == sizeof(STBP)) {
+ STR *tmpstr = Str_new(24,0);
+
+ stab_efullname(tmpstr, ((STAB*)arg)); /* a stab value! */
+ sprintf(tokenbuf,"*%s",tmpstr->str_ptr);
+ /* reformat to non-binary */
+ xs = tokenbuf;
+ xlen = strlen(tokenbuf);
+ str_free(tmpstr);
+ }
+ if (strEQ(f,"%s")) { /* some printfs fail on >128 chars */
+ break; /* so handle simple cases */
+ }
+ else if (f[1] == '-') {
+ char *mp = index(f, '.');
+ int min = atoi(f+2);
+
+ if (mp) {
+ int max = atoi(mp+1);
+
+ if (xlen > max)
+ xlen = max;
+ }
+ if (xlen < min)
+ post = min - xlen;
+ break;
+ }
+ else if (isDIGIT(f[1])) {
+ char *mp = index(f, '.');
+ int min = atoi(f+1);
+
+ if (mp) {
+ int max = atoi(mp+1);
+
+ if (xlen > max)
+ xlen = max;
+ }
+ if (xlen < min)
+ pre = min - xlen;
+ break;
+ }
+ strcpy(tokenbuf+64,f); /* sprintf($s,...$s...) */
+ *t = ch;
+ (void)sprintf(buf,tokenbuf+64,xs);
+ xs = buf;
+ xlen = strlen(xs);
+ break;
+ }
+ /* end of switch, copy results */
+ *t = ch;
+ STR_GROW(str, str->str_cur + (f - s) + xlen + 1 + pre + post);
+ str_ncat(str, s, f - s);
+ if (pre) {
+ repeatcpy(str->str_ptr + str->str_cur, " ", 1, pre);
+ str->str_cur += pre;
+ }
+ str_ncat(str, xs, xlen);
+ if (post) {
+ repeatcpy(str->str_ptr + str->str_cur, " ", 1, post);
+ str->str_cur += post;
+ }
+ s = t;
+ break; /* break from for loop */
+ }
+ }
+ str_ncat(str, s, t - s);
+ STABSET(str);
+}
+
+STR *
+do_push(ary,arglast)
+register ARRAY *ary;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register int items = arglast[2] - sp;
+ register STR *str = &str_undef;
+
+ for (st += ++sp; items > 0; items--,st++) {
+ str = Str_new(26,0);
+ if (*st)
+ str_sset(str,*st);
+ (void)apush(ary,str);
+ }
+ return str;
+}
+
+void
+do_unshift(ary,arglast)
+register ARRAY *ary;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register int items = arglast[2] - sp;
+ register STR *str;
+ register int i;
+
+ aunshift(ary,items);
+ i = 0;
+ for (st += ++sp; i < items; i++,st++) {
+ str = Str_new(27,0);
+ str_sset(str,*st);
+ (void)astore(ary,i,str);
+ }
+}
+
+int
+do_subr(arg,gimme,arglast)
+register ARG *arg;
+int gimme;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register int items = arglast[2] - sp;
+ register SUBR *sub;
+ SPAT * VOLATILE oldspat = curspat;
+ STR *str;
+ STAB *stab;
+ int oldsave = savestack->ary_fill;
+ int oldtmps_base = tmps_base;
+ int hasargs = ((arg[2].arg_type & A_MASK) != A_NULL);
+ register CSV *csv;
+
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else {
+ STR *tmpstr = STAB_STR(arg[1].arg_ptr.arg_stab);
+
+ if (tmpstr)
+ stab = stabent(str_get(tmpstr),TRUE);
+ else
+ stab = Nullstab;
+ }
+ if (!stab)
+ fatal("Undefined subroutine called");
+ if (!(sub = stab_sub(stab))) {
+ STR *tmpstr = arg[0].arg_ptr.arg_str;
+
+ stab_efullname(tmpstr, stab);
+ fatal("Undefined subroutine \"%s\" called",tmpstr->str_ptr);
+ }
+ if (arg->arg_type == O_DBSUBR && !sub->usersub) {
+ str = stab_val(DBsub);
+ saveitem(str);
+ stab_efullname(str,stab);
+ sub = stab_sub(DBsub);
+ if (!sub)
+ fatal("No DBsub routine");
+ }
+ str = Str_new(15, sizeof(CSV));
+ str->str_state = SS_SCSV;
+ (void)apush(savestack,str);
+ csv = (CSV*)str->str_ptr;
+ csv->sub = sub;
+ csv->stab = stab;
+ csv->curcsv = curcsv;
+ csv->curcmd = curcmd;
+ csv->depth = sub->depth;
+ csv->wantarray = gimme;
+ csv->hasargs = hasargs;
+ curcsv = csv;
+ tmps_base = tmps_max;
+ if (sub->usersub) {
+ csv->hasargs = 0;
+ csv->savearray = Null(ARRAY*);;
+ csv->argarray = Null(ARRAY*);
+ st[sp] = arg->arg_ptr.arg_str;
+ if (!hasargs)
+ items = 0;
+ sp = (*sub->usersub)(sub->userindex,sp,items);
+ }
+ else {
+ if (hasargs) {
+ csv->savearray = stab_xarray(defstab);
+ csv->argarray = afake(defstab, items, &st[sp+1]);
+ stab_xarray(defstab) = csv->argarray;
+ }
+ sub->depth++;
+ if (sub->depth >= 2) { /* save temporaries on recursion? */
+ if (sub->depth == 100 && dowarn)
+ warn("Deep recursion on subroutine \"%s\"",stab_ename(stab));
+ savelist(sub->tosave->ary_array,sub->tosave->ary_fill);
+ }
+ sp = cmd_exec(sub->cmd,gimme, --sp); /* so do it already */
+ }
+
+ st = stack->ary_array;
+ tmps_base = oldtmps_base;
+ for (items = arglast[0] + 1; items <= sp; items++)
+ st[items] = str_mortal(st[items]);
+ /* in case restore wipes old str */
+ restorelist(oldsave);
+ curspat = oldspat;
+ return sp;
+}
+
+int
+do_assign(arg,gimme,arglast)
+register ARG *arg;
+int gimme;
+int *arglast;
+{
+
+ register STR **st = stack->ary_array;
+ STR **firstrelem = st + arglast[1] + 1;
+ STR **firstlelem = st + arglast[0] + 1;
+ STR **lastrelem = st + arglast[2];
+ STR **lastlelem = st + arglast[1];
+ register STR **relem;
+ register STR **lelem;
+
+ register STR *str;
+ register ARRAY *ary;
+ register int makelocal;
+ HASH *hash;
+ int i;
+
+ makelocal = (arg->arg_flags & AF_LOCAL_XX) != 0;
+ localizing = makelocal;
+ delaymagic = DM_DELAY; /* catch simultaneous items */
+
+ /* If there's a common identifier on both sides we have to take
+ * special care that assigning the identifier on the left doesn't
+ * clobber a value on the right that's used later in the list.
+ */
+ if (arg->arg_flags & AF_COMMON) {
+ for (relem = firstrelem; relem <= lastrelem; relem++) {
+ /*SUPPRESS 560*/
+ if (str = *relem)
+ *relem = str_mortal(str);
+ }
+ }
+ relem = firstrelem;
+ lelem = firstlelem;
+ ary = Null(ARRAY*);
+ hash = Null(HASH*);
+ while (lelem <= lastlelem) {
+ str = *lelem++;
+ if (str->str_state >= SS_HASH) {
+ if (str->str_state == SS_ARY) {
+ if (makelocal)
+ ary = saveary(str->str_u.str_stab);
+ else {
+ ary = stab_array(str->str_u.str_stab);
+ ary->ary_fill = -1;
+ }
+ i = 0;
+ while (relem <= lastrelem) { /* gobble up all the rest */
+ str = Str_new(28,0);
+ if (*relem)
+ str_sset(str,*relem);
+ *(relem++) = str;
+ (void)astore(ary,i++,str);
+ }
+ }
+ else if (str->str_state == SS_HASH) {
+ char *tmps;
+ STR *tmpstr;
+ int magic = 0;
+ STAB *tmpstab = str->str_u.str_stab;
+
+ if (makelocal)
+ hash = savehash(str->str_u.str_stab);
+ else {
+ hash = stab_hash(str->str_u.str_stab);
+ if (tmpstab == envstab) {
+ magic = 'E';
+ environ[0] = Nullch;
+ }
+ else if (tmpstab == sigstab) {
+ magic = 'S';
+#ifndef NSIG
+#define NSIG 32
+#endif
+ for (i = 1; i < NSIG; i++)
+ signal(i, SIG_DFL); /* crunch, crunch, crunch */
+ }
+#ifdef SOME_DBM
+ else if (hash->tbl_dbm)
+ magic = 'D';
+#endif
+ hclear(hash, magic == 'D'); /* wipe any dbm file too */
+
+ }
+ while (relem < lastrelem) { /* gobble up all the rest */
+ if (*relem)
+ str = *(relem++);
+ else
+ str = &str_no, relem++;
+ tmps = str_get(str);
+ tmpstr = Str_new(29,0);
+ if (*relem)
+ str_sset(tmpstr,*relem); /* value */
+ *(relem++) = tmpstr;
+ (void)hstore(hash,tmps,str->str_cur,tmpstr,0);
+ if (magic) {
+ str_magic(tmpstr, tmpstab, magic, tmps, str->str_cur);
+ stabset(tmpstr->str_magic, tmpstr);
+ }
+ }
+ }
+ else
+ fatal("panic: do_assign");
+ }
+ else {
+ if (makelocal)
+ saveitem(str);
+ if (relem <= lastrelem) {
+ str_sset(str, *relem);
+ *(relem++) = str;
+ }
+ else {
+ str_sset(str, &str_undef);
+ if (gimme == G_ARRAY) {
+ i = ++lastrelem - firstrelem;
+ relem++; /* tacky, I suppose */
+ astore(stack,i,str);
+ if (st != stack->ary_array) {
+ st = stack->ary_array;
+ firstrelem = st + arglast[1] + 1;
+ firstlelem = st + arglast[0] + 1;
+ lastlelem = st + arglast[1];
+ lastrelem = st + i;
+ relem = lastrelem + 1;
+ }
+ }
+ }
+ STABSET(str);
+ }
+ }
+ if (delaymagic & ~DM_DELAY) {
+ if (delaymagic & DM_UID) {
+#ifdef HAS_SETREUID
+ (void)setreuid(uid,euid);
+#else /* not HAS_SETREUID */
+#ifdef HAS_SETRUID
+ if ((delaymagic & DM_UID) == DM_RUID) {
+ (void)setruid(uid);
+ delaymagic =~ DM_RUID;
+ }
+#endif /* HAS_SETRUID */
+#ifdef HAS_SETEUID
+ if ((delaymagic & DM_UID) == DM_EUID) {
+ (void)seteuid(uid);
+ delaymagic =~ DM_EUID;
+ }
+#endif /* HAS_SETEUID */
+ if (delaymagic & DM_UID) {
+ if (uid != euid)
+ fatal("No setreuid available");
+ (void)setuid(uid);
+ }
+#endif /* not HAS_SETREUID */
+ uid = (int)getuid();
+ euid = (int)geteuid();
+ }
+ if (delaymagic & DM_GID) {
+#ifdef HAS_SETREGID
+ (void)setregid(gid,egid);
+#else /* not HAS_SETREGID */
+#ifdef HAS_SETRGID
+ if ((delaymagic & DM_GID) == DM_RGID) {
+ (void)setrgid(gid);
+ delaymagic =~ DM_RGID;
+ }
+#endif /* HAS_SETRGID */
+#ifdef HAS_SETEGID
+ if ((delaymagic & DM_GID) == DM_EGID) {
+ (void)setegid(gid);
+ delaymagic =~ DM_EGID;
+ }
+#endif /* HAS_SETEGID */
+ if (delaymagic & DM_GID) {
+ if (gid != egid)
+ fatal("No setregid available");
+ (void)setgid(gid);
+ }
+#endif /* not HAS_SETREGID */
+ gid = (int)getgid();
+ egid = (int)getegid();
+ }
+ }
+ delaymagic = 0;
+ localizing = FALSE;
+ if (gimme == G_ARRAY) {
+ i = lastrelem - firstrelem + 1;
+ if (ary || hash)
+ Copy(firstrelem, firstlelem, i, STR*);
+ return arglast[0] + i;
+ }
+ else {
+ str_numset(arg->arg_ptr.arg_str,(double)(arglast[2] - arglast[1]));
+ *firstlelem = arg->arg_ptr.arg_str;
+ return arglast[0] + 1;
+ }
+}
+
+int /*SUPPRESS 590*/
+do_study(str,arg,gimme,arglast)
+STR *str;
+ARG *arg;
+int gimme;
+int *arglast;
+{
+ register unsigned char *s;
+ register int pos = str->str_cur;
+ register int ch;
+ register int *sfirst;
+ register int *snext;
+ static int maxscream = -1;
+ static STR *lastscream = Nullstr;
+ int retval;
+ int retarg = arglast[0] + 1;
+
+#ifndef lint
+ s = (unsigned char*)(str_get(str));
+#else
+ s = Null(unsigned char*);
+#endif
+ if (lastscream)
+ lastscream->str_pok &= ~SP_STUDIED;
+ lastscream = str;
+ if (pos <= 0) {
+ retval = 0;
+ goto ret;
+ }
+ if (pos > maxscream) {
+ if (maxscream < 0) {
+ maxscream = pos + 80;
+ New(301,screamfirst, 256, int);
+ New(302,screamnext, maxscream, int);
+ }
+ else {
+ maxscream = pos + pos / 4;
+ Renew(screamnext, maxscream, int);
+ }
+ }
+
+ sfirst = screamfirst;
+ snext = screamnext;
+
+ if (!sfirst || !snext)
+ fatal("do_study: out of memory");
+
+ for (ch = 256; ch; --ch)
+ *sfirst++ = -1;
+ sfirst -= 256;
+
+ while (--pos >= 0) {
+ ch = s[pos];
+ if (sfirst[ch] >= 0)
+ snext[pos] = sfirst[ch] - pos;
+ else
+ snext[pos] = -pos;
+ sfirst[ch] = pos;
+
+ /* If there were any case insensitive searches, we must assume they
+ * all are. This speeds up insensitive searches much more than
+ * it slows down sensitive ones.
+ */
+ if (sawi)
+ sfirst[fold[ch]] = pos;
+ }
+
+ str->str_pok |= SP_STUDIED;
+ retval = 1;
+ ret:
+ str_numset(arg->arg_ptr.arg_str,(double)retval);
+ stack->ary_array[retarg] = arg->arg_ptr.arg_str;
+ return retarg;
+}
+
+int /*SUPPRESS 590*/
+do_defined(str,arg,gimme,arglast)
+STR *str;
+register ARG *arg;
+int gimme;
+int *arglast;
+{
+ register int type;
+ register int retarg = arglast[0] + 1;
+ int retval;
+ ARRAY *ary;
+ HASH *hash;
+
+ if ((arg[1].arg_type & A_MASK) != A_LEXPR)
+ fatal("Illegal argument to defined()");
+ arg = arg[1].arg_ptr.arg_arg;
+ type = arg->arg_type;
+
+ if (type == O_SUBR || type == O_DBSUBR) {
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ retval = stab_sub(arg[1].arg_ptr.arg_stab) != 0;
+ else {
+ STR *tmpstr = STAB_STR(arg[1].arg_ptr.arg_stab);
+
+ retval = tmpstr && stab_sub(stabent(str_get(tmpstr),TRUE)) != 0;
+ }
+ }
+ else if (type == O_ARRAY || type == O_LARRAY ||
+ type == O_ASLICE || type == O_LASLICE )
+ retval = ((ary = stab_xarray(arg[1].arg_ptr.arg_stab)) != 0
+ && ary->ary_max >= 0 );
+ else if (type == O_HASH || type == O_LHASH ||
+ type == O_HSLICE || type == O_LHSLICE )
+ retval = ((hash = stab_xhash(arg[1].arg_ptr.arg_stab)) != 0
+ && hash->tbl_array);
+ else
+ retval = FALSE;
+ str_numset(str,(double)retval);
+ stack->ary_array[retarg] = str;
+ return retarg;
+}
+
+int /*SUPPRESS 590*/
+do_undef(str,arg,gimme,arglast)
+STR *str;
+register ARG *arg;
+int gimme;
+int *arglast;
+{
+ register int type;
+ register STAB *stab;
+ int retarg = arglast[0] + 1;
+
+ if ((arg[1].arg_type & A_MASK) != A_LEXPR)
+ fatal("Illegal argument to undef()");
+ arg = arg[1].arg_ptr.arg_arg;
+ type = arg->arg_type;
+
+ if (type == O_ARRAY || type == O_LARRAY) {
+ stab = arg[1].arg_ptr.arg_stab;
+ afree(stab_xarray(stab));
+ stab_xarray(stab) = anew(stab); /* so "@array" still works */
+ }
+ else if (type == O_HASH || type == O_LHASH) {
+ stab = arg[1].arg_ptr.arg_stab;
+ if (stab == envstab)
+ environ[0] = Nullch;
+ else if (stab == sigstab) {
+ int i;
+
+ for (i = 1; i < NSIG; i++)
+ signal(i, SIG_DFL); /* munch, munch, munch */
+ }
+ (void)hfree(stab_xhash(stab), TRUE);
+ stab_xhash(stab) = Null(HASH*);
+ }
+ else if (type == O_SUBR || type == O_DBSUBR) {
+ stab = arg[1].arg_ptr.arg_stab;
+ if ((arg[1].arg_type & A_MASK) != A_WORD) {
+ STR *tmpstr = STAB_STR(arg[1].arg_ptr.arg_stab);
+
+ if (tmpstr)
+ stab = stabent(str_get(tmpstr),TRUE);
+ else
+ stab = Nullstab;
+ }
+ if (stab && stab_sub(stab)) {
+ cmd_free(stab_sub(stab)->cmd);
+ stab_sub(stab)->cmd = Nullcmd;
+ afree(stab_sub(stab)->tosave);
+ Safefree(stab_sub(stab));
+ stab_sub(stab) = Null(SUBR*);
+ }
+ }
+ else
+ fatal("Can't undefine that kind of object");
+ str_numset(str,0.0);
+ stack->ary_array[retarg] = str;
+ return retarg;
+}
+
+int
+do_vec(lvalue,astr,arglast)
+int lvalue;
+STR *astr;
+int *arglast;
+{
+ STR **st = stack->ary_array;
+ int sp = arglast[0];
+ register STR *str = st[++sp];
+ register int offset = (int)str_gnum(st[++sp]);
+ register int size = (int)str_gnum(st[++sp]);
+ unsigned char *s = (unsigned char*)str_get(str);
+ unsigned long retnum;
+ int len;
+
+ sp = arglast[1];
+ offset *= size; /* turn into bit offset */
+ len = (offset + size + 7) / 8;
+ if (offset < 0 || size < 1)
+ retnum = 0;
+ else if (!lvalue && len > str->str_cur)
+ retnum = 0;
+ else {
+ if (len > str->str_cur) {
+ STR_GROW(str,len);
+ (void)memzero(str->str_ptr + str->str_cur, len - str->str_cur);
+ str->str_cur = len;
+ }
+ s = (unsigned char*)str_get(str);
+ if (size < 8)
+ retnum = (s[offset >> 3] >> (offset & 7)) & ((1 << size) - 1);
+ else {
+ offset >>= 3;
+ if (size == 8)
+ retnum = s[offset];
+ else if (size == 16)
+ retnum = ((unsigned long) s[offset] << 8) + s[offset+1];
+ else if (size == 32)
+ retnum = ((unsigned long) s[offset] << 24) +
+ ((unsigned long) s[offset + 1] << 16) +
+ (s[offset + 2] << 8) + s[offset+3];
+ }
+
+ if (lvalue) { /* it's an lvalue! */
+ struct lstring *lstr = (struct lstring*)astr;
+
+ astr->str_magic = str;
+ st[sp]->str_rare = 'v';
+ lstr->lstr_offset = offset;
+ lstr->lstr_len = size;
+ }
+ }
+
+ str_numset(astr,(double)retnum);
+ st[sp] = astr;
+ return sp;
+}
+
+void
+do_vecset(mstr,str)
+STR *mstr;
+STR *str;
+{
+ struct lstring *lstr = (struct lstring*)str;
+ register int offset;
+ register int size;
+ register unsigned char *s = (unsigned char*)mstr->str_ptr;
+ register unsigned long lval = U_L(str_gnum(str));
+ int mask;
+
+ mstr->str_rare = 0;
+ str->str_magic = Nullstr;
+ offset = lstr->lstr_offset;
+ size = lstr->lstr_len;
+ if (size < 8) {
+ mask = (1 << size) - 1;
+ size = offset & 7;
+ lval &= mask;
+ offset >>= 3;
+ s[offset] &= ~(mask << size);
+ s[offset] |= lval << size;
+ }
+ else {
+ if (size == 8)
+ s[offset] = lval & 255;
+ else if (size == 16) {
+ s[offset] = (lval >> 8) & 255;
+ s[offset+1] = lval & 255;
+ }
+ else if (size == 32) {
+ s[offset] = (lval >> 24) & 255;
+ s[offset+1] = (lval >> 16) & 255;
+ s[offset+2] = (lval >> 8) & 255;
+ s[offset+3] = lval & 255;
+ }
+ }
+}
+
+void
+do_chop(astr,str)
+register STR *astr;
+register STR *str;
+{
+ register char *tmps;
+ register int i;
+ ARRAY *ary;
+ HASH *hash;
+ HENT *entry;
+
+ if (!str)
+ return;
+ if (str->str_state == SS_ARY) {
+ ary = stab_array(str->str_u.str_stab);
+ for (i = 0; i <= ary->ary_fill; i++)
+ do_chop(astr,ary->ary_array[i]);
+ return;
+ }
+ if (str->str_state == SS_HASH) {
+ hash = stab_hash(str->str_u.str_stab);
+ (void)hiterinit(hash);
+ /*SUPPRESS 560*/
+ while (entry = hiternext(hash))
+ do_chop(astr,hiterval(hash,entry));
+ return;
+ }
+ tmps = str_get(str);
+ if (tmps && str->str_cur) {
+ tmps += str->str_cur - 1;
+ str_nset(astr,tmps,1); /* remember last char */
+ *tmps = '\0'; /* wipe it out */
+ str->str_cur = tmps - str->str_ptr;
+ str->str_nok = 0;
+ STABSET(str);
+ }
+ else
+ str_nset(astr,"",0);
+}
+
+void
+do_vop(optype,str,left,right)
+STR *str;
+STR *left;
+STR *right;
+{
+ register char *s;
+ register char *l = str_get(left);
+ register char *r = str_get(right);
+ register int len;
+
+ len = left->str_cur;
+ if (len > right->str_cur)
+ len = right->str_cur;
+ if (str->str_cur > len)
+ str->str_cur = len;
+ else if (str->str_cur < len) {
+ STR_GROW(str,len);
+ (void)memzero(str->str_ptr + str->str_cur, len - str->str_cur);
+ str->str_cur = len;
+ }
+ str->str_pok = 1;
+ str->str_nok = 0;
+ s = str->str_ptr;
+ if (!s) {
+ str_nset(str,"",0);
+ s = str->str_ptr;
+ }
+ switch (optype) {
+ case O_BIT_AND:
+ while (len--)
+ *s++ = *l++ & *r++;
+ break;
+ case O_XOR:
+ while (len--)
+ *s++ = *l++ ^ *r++;
+ goto mop_up;
+ case O_BIT_OR:
+ while (len--)
+ *s++ = *l++ | *r++;
+ mop_up:
+ len = str->str_cur;
+ if (right->str_cur > len)
+ str_ncat(str,right->str_ptr+len,right->str_cur - len);
+ else if (left->str_cur > len)
+ str_ncat(str,left->str_ptr+len,left->str_cur - len);
+ break;
+ }
+}
+
+int
+do_syscall(arglast)
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register int items = arglast[2] - sp;
+#ifdef atarist
+ unsigned long arg[14]; /* yes, we really need that many ! */
+#else
+ unsigned long arg[8];
+#endif
+ register int i = 0;
+ int retval = -1;
+
+#ifdef HAS_SYSCALL
+#ifdef TAINT
+ for (st += ++sp; items--; st++)
+ tainted |= (*st)->str_tainted;
+ st = stack->ary_array;
+ sp = arglast[1];
+ items = arglast[2] - sp;
+#endif
+#ifdef TAINT
+ taintproper("Insecure dependency in syscall");
+#endif
+ /* This probably won't work on machines where sizeof(long) != sizeof(int)
+ * or where sizeof(long) != sizeof(char*). But such machines will
+ * not likely have syscall implemented either, so who cares?
+ */
+ while (items--) {
+ if (st[++sp]->str_nok || !i)
+ arg[i++] = (unsigned long)str_gnum(st[sp]);
+#ifndef lint
+ else
+ arg[i++] = (unsigned long)st[sp]->str_ptr;
+#endif /* lint */
+ }
+ sp = arglast[1];
+ items = arglast[2] - sp;
+ switch (items) {
+ case 0:
+ fatal("Too few args to syscall");
+ case 1:
+ retval = syscall(arg[0]);
+ break;
+ case 2:
+ retval = syscall(arg[0],arg[1]);
+ break;
+ case 3:
+ retval = syscall(arg[0],arg[1],arg[2]);
+ break;
+ case 4:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3]);
+ break;
+ case 5:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4]);
+ break;
+ case 6:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5]);
+ break;
+ case 7:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6]);
+ break;
+ case 8:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],
+ arg[7]);
+ break;
+#ifdef atarist
+ case 9:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],
+ arg[7], arg[8]);
+ break;
+ case 10:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],
+ arg[7], arg[8], arg[9]);
+ break;
+ case 11:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],
+ arg[7], arg[8], arg[9], arg[10]);
+ break;
+ case 12:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],
+ arg[7], arg[8], arg[9], arg[10], arg[11]);
+ break;
+ case 13:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],
+ arg[7], arg[8], arg[9], arg[10], arg[11], arg[12]);
+ break;
+ case 14:
+ retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],
+ arg[7], arg[8], arg[9], arg[10], arg[11], arg[12], arg[13]);
+ break;
+#endif /* atarist */
+ }
+ return retval;
+#else
+ fatal("syscall() unimplemented");
+#endif
+}
+
+
diff --git a/gnu/usr.bin/perl/perl/doio.c b/gnu/usr.bin/perl/perl/doio.c
new file mode 100644
index 0000000..a603feb
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/doio.c
@@ -0,0 +1,2954 @@
+/* $RCSfile: doio.c,v $$Revision: 1.1.1.1 $$Date: 1994/09/10 06:27:32 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: doio.c,v $
+ * Revision 1.1.1.1 1994/09/10 06:27:32 gclarkii
+ * Initial import of Perl 4.046 bmaked
+ *
+ * Revision 1.2 1994/03/09 22:24:27 ache
+ * (cast) added for last argument of semctl
+ *
+ * Revision 1.1.1.1 1993/08/23 21:29:36 nate
+ * PERL!
+ *
+ * Revision 4.0.1.6 92/06/11 21:08:16 lwall
+ * patch34: some systems don't declare h_errno extern in header files
+ *
+ * Revision 4.0.1.5 92/06/08 13:00:21 lwall
+ * patch20: some machines don't define ENOTSOCK in errno.h
+ * patch20: new warnings for failed use of stat operators on filenames with \n
+ * patch20: wait failed when STDOUT or STDERR reopened to a pipe
+ * patch20: end of file latch not reset on reopen of STDIN
+ * patch20: seek(HANDLE, 0, 1) went to eof because of ancient Ultrix workaround
+ * patch20: fixed memory leak on system() for vfork() machines
+ * patch20: get*by* routines now return something useful in a scalar context
+ * patch20: h_errno now accessible via $?
+ *
+ * Revision 4.0.1.4 91/11/05 16:51:43 lwall
+ * patch11: prepared for ctype implementations that don't define isascii()
+ * patch11: perl mistook some streams for sockets because they return mode 0 too
+ * patch11: reopening STDIN, STDOUT and STDERR failed on some machines
+ * patch11: certain perl errors should set EBADF so that $! looks better
+ * patch11: truncate on a closed filehandle could dump
+ * patch11: stats of _ forgot whether prior stat was actually lstat
+ * patch11: -T returned true on NFS directory
+ *
+ * Revision 4.0.1.3 91/06/10 01:21:19 lwall
+ * patch10: read didn't work from character special files open for writing
+ * patch10: close-on-exec wrongly set on system file descriptors
+ *
+ * Revision 4.0.1.2 91/06/07 10:53:39 lwall
+ * patch4: new copyright notice
+ * patch4: system fd's are now treated specially
+ * patch4: added $^F variable to specify maximum system fd, default 2
+ * patch4: character special files now opened with bidirectional stdio buffers
+ * patch4: taintchecks could improperly modify parent in vfork()
+ * patch4: many, many itty-bitty portability fixes
+ *
+ * Revision 4.0.1.1 91/04/11 17:41:06 lwall
+ * patch1: hopefully straightened out some of the Xenix mess
+ *
+ * Revision 4.0 91/03/20 01:07:06 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+
+#ifdef HAS_SOCKET
+#include <sys/socket.h>
+#include <netdb.h>
+#ifndef ENOTSOCK
+#include <net/errno.h>
+#endif
+#endif
+
+#ifdef HAS_SELECT
+#ifdef I_SYS_SELECT
+#ifndef I_SYS_TIME
+#include <sys/select.h>
+#endif
+#endif
+#endif
+
+#ifdef HOST_NOT_FOUND
+extern int h_errno;
+#endif
+
+#if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM)
+#include <sys/ipc.h>
+#ifdef HAS_MSG
+#include <sys/msg.h>
+#endif
+#ifdef HAS_SEM
+#include <sys/sem.h>
+#endif
+#ifdef HAS_SHM
+#include <sys/shm.h>
+#endif
+#endif
+
+#ifdef I_PWD
+#include <pwd.h>
+#endif
+#ifdef I_GRP
+#include <grp.h>
+#endif
+#ifdef I_UTIME
+#include <utime.h>
+#endif
+#ifdef I_FCNTL
+#include <fcntl.h>
+#endif
+#ifdef I_SYS_FILE
+#include <sys/file.h>
+#endif
+
+int laststatval = -1;
+int laststype = O_STAT;
+
+static char* warn_nl = "Unsuccessful %s on filename containing newline";
+
+bool
+do_open(stab,name,len)
+STAB *stab;
+register char *name;
+int len;
+{
+ FILE *fp;
+ register STIO *stio = stab_io(stab);
+ char *myname = savestr(name);
+ int result;
+ int fd;
+ int writing = 0;
+ char mode[3]; /* stdio file mode ("r\0" or "r+\0") */
+ FILE *saveifp = Nullfp;
+ FILE *saveofp = Nullfp;
+ char savetype = ' ';
+
+ mode[0] = mode[1] = mode[2] = '\0';
+ name = myname;
+ forkprocess = 1; /* assume true if no fork */
+ while (len && isSPACE(name[len-1]))
+ name[--len] = '\0';
+ if (!stio)
+ stio = stab_io(stab) = stio_new();
+ else if (stio->ifp) {
+ fd = fileno(stio->ifp);
+ if (stio->type == '-')
+ result = 0;
+ else if (fd <= maxsysfd) {
+ saveifp = stio->ifp;
+ saveofp = stio->ofp;
+ savetype = stio->type;
+ result = 0;
+ }
+ else if (stio->type == '|')
+ result = mypclose(stio->ifp);
+ else if (stio->ifp != stio->ofp) {
+ if (stio->ofp) {
+ result = fclose(stio->ofp);
+ fclose(stio->ifp); /* clear stdio, fd already closed */
+ }
+ else
+ result = fclose(stio->ifp);
+ }
+ else
+ result = fclose(stio->ifp);
+ if (result == EOF && fd > maxsysfd)
+ fprintf(stderr,"Warning: unable to close filehandle %s properly.\n",
+ stab_ename(stab));
+ stio->ofp = stio->ifp = Nullfp;
+ }
+ if (*name == '+' && len > 1 && name[len-1] != '|') { /* scary */
+ mode[1] = *name++;
+ mode[2] = '\0';
+ --len;
+ writing = 1;
+ }
+ else {
+ mode[1] = '\0';
+ }
+ stio->type = *name;
+ if (*name == '|') {
+ /*SUPPRESS 530*/
+ for (name++; isSPACE(*name); name++) ;
+#ifdef TAINT
+ taintenv();
+ taintproper("Insecure dependency in piped open");
+#endif
+ fp = mypopen(name,"w");
+ writing = 1;
+ }
+ else if (*name == '>') {
+#ifdef TAINT
+ taintproper("Insecure dependency in open");
+#endif
+ name++;
+ if (*name == '>') {
+ mode[0] = stio->type = 'a';
+ name++;
+ }
+ else
+ mode[0] = 'w';
+ writing = 1;
+ if (*name == '&') {
+ duplicity:
+ name++;
+ while (isSPACE(*name))
+ name++;
+ if (isDIGIT(*name))
+ fd = atoi(name);
+ else {
+ stab = stabent(name,FALSE);
+ if (!stab || !stab_io(stab)) {
+#ifdef EINVAL
+ errno = EINVAL;
+#endif
+ goto say_false;
+ }
+ if (stab_io(stab) && stab_io(stab)->ifp) {
+ fd = fileno(stab_io(stab)->ifp);
+ if (stab_io(stab)->type == 's')
+ stio->type = 's';
+ }
+ else
+ fd = -1;
+ }
+ if (!(fp = fdopen(fd = dup(fd),mode))) {
+ close(fd);
+ }
+ }
+ else {
+ while (isSPACE(*name))
+ name++;
+ if (strEQ(name,"-")) {
+ fp = stdout;
+ stio->type = '-';
+ }
+ else {
+ fp = fopen(name,mode);
+ }
+ }
+ }
+ else {
+ if (*name == '<') {
+ mode[0] = 'r';
+ name++;
+ while (isSPACE(*name))
+ name++;
+ if (*name == '&')
+ goto duplicity;
+ if (strEQ(name,"-")) {
+ fp = stdin;
+ stio->type = '-';
+ }
+ else
+ fp = fopen(name,mode);
+ }
+ else if (name[len-1] == '|') {
+#ifdef TAINT
+ taintenv();
+ taintproper("Insecure dependency in piped open");
+#endif
+ name[--len] = '\0';
+ while (len && isSPACE(name[len-1]))
+ name[--len] = '\0';
+ /*SUPPRESS 530*/
+ for (; isSPACE(*name); name++) ;
+ fp = mypopen(name,"r");
+ stio->type = '|';
+ }
+ else {
+ stio->type = '<';
+ /*SUPPRESS 530*/
+ for (; isSPACE(*name); name++) ;
+ if (strEQ(name,"-")) {
+ fp = stdin;
+ stio->type = '-';
+ }
+ else
+ fp = fopen(name,"r");
+ }
+ }
+ if (!fp) {
+ if (dowarn && stio->type == '<' && index(name, '\n'))
+ warn(warn_nl, "open");
+ Safefree(myname);
+ goto say_false;
+ }
+ Safefree(myname);
+ if (stio->type &&
+ stio->type != '|' && stio->type != '-') {
+ if (fstat(fileno(fp),&statbuf) < 0) {
+ (void)fclose(fp);
+ goto say_false;
+ }
+ if (S_ISSOCK(statbuf.st_mode))
+ stio->type = 's'; /* in case a socket was passed in to us */
+#ifdef HAS_SOCKET
+ else if (
+#ifdef S_IFMT
+ !(statbuf.st_mode & S_IFMT)
+#else
+ !statbuf.st_mode
+#endif
+ ) {
+ int buflen = sizeof tokenbuf;
+ if (getsockname(fileno(fp), (struct sockaddr * )tokenbuf, &buflen) >= 0
+ || errno != ENOTSOCK)
+ stio->type = 's'; /* some OS's return 0 on fstat()ed socket */
+ /* but some return 0 for streams too, sigh */
+ }
+#endif
+ }
+ if (saveifp) { /* must use old fp? */
+ fd = fileno(saveifp);
+ if (saveofp) {
+ fflush(saveofp); /* emulate fclose() */
+ if (saveofp != saveifp) { /* was a socket? */
+ fclose(saveofp);
+ if (fd > 2)
+ Safefree(saveofp);
+ }
+ }
+ if (fd != fileno(fp)) {
+ int pid;
+ STR *str;
+
+ dup2(fileno(fp), fd);
+ str = afetch(fdpid,fileno(fp),TRUE);
+ pid = str->str_u.str_useful;
+ str->str_u.str_useful = 0;
+ str = afetch(fdpid,fd,TRUE);
+ str->str_u.str_useful = pid;
+ fclose(fp);
+
+ }
+ fp = saveifp;
+ clearerr(fp);
+ }
+#if defined(HAS_FCNTL) && defined(F_SETFD)
+ fd = fileno(fp);
+ fcntl(fd,F_SETFD,fd > maxsysfd);
+#endif
+ stio->ifp = fp;
+ if (writing) {
+ if (stio->type == 's'
+ || (stio->type == '>' && S_ISCHR(statbuf.st_mode)) ) {
+ if (!(stio->ofp = fdopen(fileno(fp),"w"))) {
+ fclose(fp);
+ stio->ifp = Nullfp;
+ goto say_false;
+ }
+ }
+ else
+ stio->ofp = fp;
+ }
+ return TRUE;
+
+say_false:
+ stio->ifp = saveifp;
+ stio->ofp = saveofp;
+ stio->type = savetype;
+ return FALSE;
+}
+
+FILE *
+nextargv(stab)
+register STAB *stab;
+{
+ register STR *str;
+#ifndef FLEXFILENAMES
+ int filedev;
+ int fileino;
+#endif
+ int fileuid;
+ int filegid;
+ static int filemode = 0;
+ static int lastfd;
+ static char *oldname;
+
+ if (!argvoutstab)
+ argvoutstab = stabent("ARGVOUT",TRUE);
+ if (filemode & (S_ISUID|S_ISGID)) {
+ fflush(stab_io(argvoutstab)->ifp); /* chmod must follow last write */
+#ifdef HAS_FCHMOD
+ (void)fchmod(lastfd,filemode);
+#else
+ (void)chmod(oldname,filemode);
+#endif
+ }
+ filemode = 0;
+ while (alen(stab_xarray(stab)) >= 0) {
+ str = ashift(stab_xarray(stab));
+ str_sset(stab_val(stab),str);
+ STABSET(stab_val(stab));
+ oldname = str_get(stab_val(stab));
+ if (do_open(stab,oldname,stab_val(stab)->str_cur)) {
+ if (inplace) {
+#ifdef TAINT
+ taintproper("Insecure dependency in inplace open");
+#endif
+ if (strEQ(oldname,"-")) {
+ str_free(str);
+ defoutstab = stabent("STDOUT",TRUE);
+ return stab_io(stab)->ifp;
+ }
+#ifndef FLEXFILENAMES
+ filedev = statbuf.st_dev;
+ fileino = statbuf.st_ino;
+#endif
+ filemode = statbuf.st_mode;
+ fileuid = statbuf.st_uid;
+ filegid = statbuf.st_gid;
+ if (!S_ISREG(filemode)) {
+ warn("Can't do inplace edit: %s is not a regular file",
+ oldname );
+ do_close(stab,FALSE);
+ str_free(str);
+ continue;
+ }
+ if (*inplace) {
+#ifdef SUFFIX
+ add_suffix(str,inplace);
+#else
+ str_cat(str,inplace);
+#endif
+#ifndef FLEXFILENAMES
+ if (stat(str->str_ptr,&statbuf) >= 0
+ && statbuf.st_dev == filedev
+ && statbuf.st_ino == fileino ) {
+ warn("Can't do inplace edit: %s > 14 characters",
+ str->str_ptr );
+ do_close(stab,FALSE);
+ str_free(str);
+ continue;
+ }
+#endif
+#ifdef HAS_RENAME
+#ifndef DOSISH
+ if (rename(oldname,str->str_ptr) < 0) {
+ warn("Can't rename %s to %s: %s, skipping file",
+ oldname, str->str_ptr, strerror(errno) );
+ do_close(stab,FALSE);
+ str_free(str);
+ continue;
+ }
+#else
+ do_close(stab,FALSE);
+ (void)unlink(str->str_ptr);
+ (void)rename(oldname,str->str_ptr);
+ do_open(stab,str->str_ptr,stab_val(stab)->str_cur);
+#endif /* MSDOS */
+#else
+ (void)UNLINK(str->str_ptr);
+ if (link(oldname,str->str_ptr) < 0) {
+ warn("Can't rename %s to %s: %s, skipping file",
+ oldname, str->str_ptr, strerror(errno) );
+ do_close(stab,FALSE);
+ str_free(str);
+ continue;
+ }
+ (void)UNLINK(oldname);
+#endif
+ }
+ else {
+#ifndef DOSISH
+ if (UNLINK(oldname) < 0) {
+ warn("Can't rename %s to %s: %s, skipping file",
+ oldname, str->str_ptr, strerror(errno) );
+ do_close(stab,FALSE);
+ str_free(str);
+ continue;
+ }
+#else
+ fatal("Can't do inplace edit without backup");
+#endif
+ }
+
+ str_nset(str,">",1);
+ str_cat(str,oldname);
+ errno = 0; /* in case sprintf set errno */
+ if (!do_open(argvoutstab,str->str_ptr,str->str_cur)) {
+ warn("Can't do inplace edit on %s: %s",
+ oldname, strerror(errno) );
+ do_close(stab,FALSE);
+ str_free(str);
+ continue;
+ }
+ defoutstab = argvoutstab;
+ lastfd = fileno(stab_io(argvoutstab)->ifp);
+ (void)fstat(lastfd,&statbuf);
+#ifdef HAS_FCHMOD
+ (void)fchmod(lastfd,filemode);
+#else
+ (void)chmod(oldname,filemode);
+#endif
+ if (fileuid != statbuf.st_uid || filegid != statbuf.st_gid) {
+#ifdef HAS_FCHOWN
+ (void)fchown(lastfd,fileuid,filegid);
+#else
+#ifdef HAS_CHOWN
+ (void)chown(oldname,fileuid,filegid);
+#endif
+#endif
+ }
+ }
+ str_free(str);
+ return stab_io(stab)->ifp;
+ }
+ else
+ fprintf(stderr,"Can't open %s: %s\n",str_get(str), strerror(errno));
+ str_free(str);
+ }
+ if (inplace) {
+ (void)do_close(argvoutstab,FALSE);
+ defoutstab = stabent("STDOUT",TRUE);
+ }
+ return Nullfp;
+}
+
+#ifdef HAS_PIPE
+void
+do_pipe(str, rstab, wstab)
+STR *str;
+STAB *rstab;
+STAB *wstab;
+{
+ register STIO *rstio;
+ register STIO *wstio;
+ int fd[2];
+
+ if (!rstab)
+ goto badexit;
+ if (!wstab)
+ goto badexit;
+
+ rstio = stab_io(rstab);
+ wstio = stab_io(wstab);
+
+ if (!rstio)
+ rstio = stab_io(rstab) = stio_new();
+ else if (rstio->ifp)
+ do_close(rstab,FALSE);
+ if (!wstio)
+ wstio = stab_io(wstab) = stio_new();
+ else if (wstio->ifp)
+ do_close(wstab,FALSE);
+
+ if (pipe(fd) < 0)
+ goto badexit;
+ rstio->ifp = fdopen(fd[0], "r");
+ wstio->ofp = fdopen(fd[1], "w");
+ wstio->ifp = wstio->ofp;
+ rstio->type = '<';
+ wstio->type = '>';
+ if (!rstio->ifp || !wstio->ofp) {
+ if (rstio->ifp) fclose(rstio->ifp);
+ else close(fd[0]);
+ if (wstio->ofp) fclose(wstio->ofp);
+ else close(fd[1]);
+ goto badexit;
+ }
+
+ str_sset(str,&str_yes);
+ return;
+
+badexit:
+ str_sset(str,&str_undef);
+ return;
+}
+#endif
+
+bool
+do_close(stab,explicit)
+STAB *stab;
+bool explicit;
+{
+ bool retval = FALSE;
+ register STIO *stio;
+ int status;
+
+ if (!stab)
+ stab = argvstab;
+ if (!stab) {
+ errno = EBADF;
+ return FALSE;
+ }
+ stio = stab_io(stab);
+ if (!stio) { /* never opened */
+ if (dowarn && explicit)
+ warn("Close on unopened file <%s>",stab_ename(stab));
+ return FALSE;
+ }
+ if (stio->ifp) {
+ if (stio->type == '|') {
+ status = mypclose(stio->ifp);
+ retval = (status == 0);
+ statusvalue = (unsigned short)status & 0xffff;
+ }
+ else if (stio->type == '-')
+ retval = TRUE;
+ else {
+ if (stio->ofp && stio->ofp != stio->ifp) { /* a socket */
+ retval = (fclose(stio->ofp) != EOF);
+ fclose(stio->ifp); /* clear stdio, fd already closed */
+ }
+ else
+ retval = (fclose(stio->ifp) != EOF);
+ }
+ stio->ofp = stio->ifp = Nullfp;
+ }
+ if (explicit)
+ stio->lines = 0;
+ stio->type = ' ';
+ return retval;
+}
+
+bool
+do_eof(stab)
+STAB *stab;
+{
+ register STIO *stio;
+ int ch;
+
+ if (!stab) { /* eof() */
+ if (argvstab)
+ stio = stab_io(argvstab);
+ else
+ return TRUE;
+ }
+ else
+ stio = stab_io(stab);
+
+ if (!stio)
+ return TRUE;
+
+ while (stio->ifp) {
+
+#ifdef STDSTDIO /* (the code works without this) */
+ if (stio->ifp->_cnt > 0) /* cheat a little, since */
+ return FALSE; /* this is the most usual case */
+#endif
+
+ ch = getc(stio->ifp);
+ if (ch != EOF) {
+ (void)ungetc(ch, stio->ifp);
+ return FALSE;
+ }
+#ifdef STDSTDIO
+ if (stio->ifp->_cnt < -1)
+ stio->ifp->_cnt = -1;
+#endif
+ if (!stab) { /* not necessarily a real EOF yet? */
+ if (!nextargv(argvstab)) /* get another fp handy */
+ return TRUE;
+ }
+ else
+ return TRUE; /* normal fp, definitely end of file */
+ }
+ return TRUE;
+}
+
+long
+do_tell(stab)
+STAB *stab;
+{
+ register STIO *stio;
+
+ if (!stab)
+ goto phooey;
+
+ stio = stab_io(stab);
+ if (!stio || !stio->ifp)
+ goto phooey;
+
+#ifdef ULTRIX_STDIO_BOTCH
+ if (feof(stio->ifp))
+ (void)fseek (stio->ifp, 0L, 2); /* ultrix 1.2 workaround */
+#endif
+
+ return ftell(stio->ifp);
+
+phooey:
+ if (dowarn)
+ warn("tell() on unopened file");
+ errno = EBADF;
+ return -1L;
+}
+
+bool
+do_seek(stab, pos, whence)
+STAB *stab;
+long pos;
+int whence;
+{
+ register STIO *stio;
+
+ if (!stab)
+ goto nuts;
+
+ stio = stab_io(stab);
+ if (!stio || !stio->ifp)
+ goto nuts;
+
+#ifdef ULTRIX_STDIO_BOTCH
+ if (feof(stio->ifp))
+ (void)fseek (stio->ifp, 0L, 2); /* ultrix 1.2 workaround */
+#endif
+
+ return fseek(stio->ifp, pos, whence) >= 0;
+
+nuts:
+ if (dowarn)
+ warn("seek() on unopened file");
+ errno = EBADF;
+ return FALSE;
+}
+
+int
+do_ctl(optype,stab,func,argstr)
+int optype;
+STAB *stab;
+int func;
+STR *argstr;
+{
+ register STIO *stio;
+ register char *s;
+ int retval;
+
+ if (!stab || !argstr || !(stio = stab_io(stab)) || !stio->ifp) {
+ errno = EBADF; /* well, sort of... */
+ return -1;
+ }
+
+ if (argstr->str_pok || !argstr->str_nok) {
+ if (!argstr->str_pok)
+ s = str_get(argstr);
+
+#ifdef IOCPARM_MASK
+#ifndef IOCPARM_LEN
+#define IOCPARM_LEN(x) (((x) >> 16) & IOCPARM_MASK)
+#endif
+#endif
+#ifdef IOCPARM_LEN
+ retval = IOCPARM_LEN(func); /* on BSDish systes we're safe */
+#else
+ retval = 256; /* otherwise guess at what's safe */
+#endif
+ if (argstr->str_cur < retval) {
+ Str_Grow(argstr,retval+1);
+ argstr->str_cur = retval;
+ }
+
+ s = argstr->str_ptr;
+ s[argstr->str_cur] = 17; /* a little sanity check here */
+ }
+ else {
+ retval = (int)str_gnum(argstr);
+#ifdef DOSISH
+ s = (char*)(long)retval; /* ouch */
+#else
+ s = (char*)retval; /* ouch */
+#endif
+ }
+
+#ifndef lint
+ if (optype == O_IOCTL)
+ retval = ioctl(fileno(stio->ifp), func, s);
+ else
+#ifdef DOSISH
+ fatal("fcntl is not implemented");
+#else
+#ifdef HAS_FCNTL
+ retval = fcntl(fileno(stio->ifp), func, s);
+#else
+ fatal("fcntl is not implemented");
+#endif
+#endif
+#else /* lint */
+ retval = 0;
+#endif /* lint */
+
+ if (argstr->str_pok) {
+ if (s[argstr->str_cur] != 17)
+ fatal("Return value overflowed string");
+ s[argstr->str_cur] = 0; /* put our null back */
+ }
+ return retval;
+}
+
+int
+do_stat(str,arg,gimme,arglast)
+STR *str;
+register ARG *arg;
+int gimme;
+int *arglast;
+{
+ register ARRAY *ary = stack;
+ register int sp = arglast[0] + 1;
+ int max = 13;
+
+ if ((arg[1].arg_type & A_MASK) == A_WORD) {
+ tmpstab = arg[1].arg_ptr.arg_stab;
+ if (tmpstab != defstab) {
+ laststype = O_STAT;
+ statstab = tmpstab;
+ str_set(statname,"");
+ if (!stab_io(tmpstab) || !stab_io(tmpstab)->ifp ||
+ fstat(fileno(stab_io(tmpstab)->ifp),&statcache) < 0) {
+ max = 0;
+ laststatval = -1;
+ }
+ }
+ else if (laststatval < 0)
+ max = 0;
+ }
+ else {
+ str_set(statname,str_get(ary->ary_array[sp]));
+ statstab = Nullstab;
+#ifdef HAS_LSTAT
+ laststype = arg->arg_type;
+ if (arg->arg_type == O_LSTAT)
+ laststatval = lstat(str_get(statname),&statcache);
+ else
+#endif
+ laststatval = stat(str_get(statname),&statcache);
+ if (laststatval < 0) {
+ if (dowarn && index(str_get(statname), '\n'))
+ warn(warn_nl, "stat");
+ max = 0;
+ }
+ }
+
+ if (gimme != G_ARRAY) {
+ if (max)
+ str_sset(str,&str_yes);
+ else
+ str_sset(str,&str_undef);
+ STABSET(str);
+ ary->ary_array[sp] = str;
+ return sp;
+ }
+ sp--;
+ if (max) {
+#ifndef lint
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_dev)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_ino)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_mode)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_nlink)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_uid)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_gid)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_rdev)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_size)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_atime)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_mtime)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_ctime)));
+#ifdef STATBLOCKS
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_blksize)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_blocks)));
+#else
+ (void)astore(ary,++sp,
+ str_2mortal(str_make("",0)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_make("",0)));
+#endif
+#else /* lint */
+ (void)astore(ary,++sp,str_nmake(0.0));
+#endif /* lint */
+ }
+ return sp;
+}
+
+#if !defined(HAS_TRUNCATE) && !defined(HAS_CHSIZE) && defined(F_FREESP)
+ /* code courtesy of William Kucharski */
+#define HAS_CHSIZE
+
+int chsize(fd, length)
+int fd; /* file descriptor */
+off_t length; /* length to set file to */
+{
+ extern long lseek();
+ struct flock fl;
+ struct stat filebuf;
+
+ if (fstat(fd, &filebuf) < 0)
+ return -1;
+
+ if (filebuf.st_size < length) {
+
+ /* extend file length */
+
+ if ((lseek(fd, (length - 1), 0)) < 0)
+ return -1;
+
+ /* write a "0" byte */
+
+ if ((write(fd, "", 1)) != 1)
+ return -1;
+ }
+ else {
+ /* truncate length */
+
+ fl.l_whence = 0;
+ fl.l_len = 0;
+ fl.l_start = length;
+ fl.l_type = F_WRLCK; /* write lock on file space */
+
+ /*
+ * This relies on the UNDOCUMENTED F_FREESP argument to
+ * fcntl(2), which truncates the file so that it ends at the
+ * position indicated by fl.l_start.
+ *
+ * Will minor miracles never cease?
+ */
+
+ if (fcntl(fd, F_FREESP, &fl) < 0)
+ return -1;
+
+ }
+
+ return 0;
+}
+#endif /* F_FREESP */
+
+int /*SUPPRESS 590*/
+do_truncate(str,arg,gimme,arglast)
+STR *str;
+register ARG *arg;
+int gimme;
+int *arglast;
+{
+ register ARRAY *ary = stack;
+ register int sp = arglast[0] + 1;
+ off_t len = (off_t)str_gnum(ary->ary_array[sp+1]);
+ int result = 1;
+ STAB *tmpstab;
+
+#if defined(HAS_TRUNCATE) || defined(HAS_CHSIZE)
+#ifdef HAS_TRUNCATE
+ if ((arg[1].arg_type & A_MASK) == A_WORD) {
+ tmpstab = arg[1].arg_ptr.arg_stab;
+ if (!stab_io(tmpstab) || !stab_io(tmpstab)->ifp ||
+ ftruncate(fileno(stab_io(tmpstab)->ifp), len) < 0)
+ result = 0;
+ }
+ else if (truncate(str_get(ary->ary_array[sp]), len) < 0)
+ result = 0;
+#else
+ if ((arg[1].arg_type & A_MASK) == A_WORD) {
+ tmpstab = arg[1].arg_ptr.arg_stab;
+ if (!stab_io(tmpstab) || !stab_io(tmpstab)->ifp ||
+ chsize(fileno(stab_io(tmpstab)->ifp), len) < 0)
+ result = 0;
+ }
+ else {
+ int tmpfd;
+
+ if ((tmpfd = open(str_get(ary->ary_array[sp]), 0)) < 0)
+ result = 0;
+ else {
+ if (chsize(tmpfd, len) < 0)
+ result = 0;
+ close(tmpfd);
+ }
+ }
+#endif
+
+ if (result)
+ str_sset(str,&str_yes);
+ else
+ str_sset(str,&str_undef);
+ STABSET(str);
+ ary->ary_array[sp] = str;
+ return sp;
+#else
+ fatal("truncate not implemented");
+#endif
+}
+
+int
+looks_like_number(str)
+STR *str;
+{
+ register char *s;
+ register char *send;
+
+ if (!str->str_pok)
+ return TRUE;
+ s = str->str_ptr;
+ send = s + str->str_cur;
+ while (isSPACE(*s))
+ s++;
+ if (s >= send)
+ return FALSE;
+ if (*s == '+' || *s == '-')
+ s++;
+ while (isDIGIT(*s))
+ s++;
+ if (s == send)
+ return TRUE;
+ if (*s == '.')
+ s++;
+ else if (s == str->str_ptr)
+ return FALSE;
+ while (isDIGIT(*s))
+ s++;
+ if (s == send)
+ return TRUE;
+ if (*s == 'e' || *s == 'E') {
+ s++;
+ if (*s == '+' || *s == '-')
+ s++;
+ while (isDIGIT(*s))
+ s++;
+ }
+ while (isSPACE(*s))
+ s++;
+ if (s >= send)
+ return TRUE;
+ return FALSE;
+}
+
+bool
+do_print(str,fp)
+register STR *str;
+FILE *fp;
+{
+ register char *tmps;
+
+ if (!fp) {
+ if (dowarn)
+ warn("print to unopened file");
+ errno = EBADF;
+ return FALSE;
+ }
+ if (!str)
+ return TRUE;
+ if (ofmt &&
+ ((str->str_nok && str->str_u.str_nval != 0.0)
+ || (looks_like_number(str) && str_gnum(str) != 0.0) ) ) {
+ fprintf(fp, ofmt, str->str_u.str_nval);
+ return !ferror(fp);
+ }
+ else {
+ tmps = str_get(str);
+ if (*tmps == 'S' && tmps[1] == 't' && tmps[2] == 'B' && tmps[3] == '\0'
+ && str->str_cur == sizeof(STBP) && strlen(tmps) < str->str_cur) {
+ STR *tmpstr = str_mortal(&str_undef);
+ stab_efullname(tmpstr,((STAB*)str));/* a stab value, be nice */
+ str = tmpstr;
+ tmps = str->str_ptr;
+ putc('*',fp);
+ }
+ if (str->str_cur && (fwrite(tmps,1,str->str_cur,fp) == 0 || ferror(fp)))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool
+do_aprint(arg,fp,arglast)
+register ARG *arg;
+register FILE *fp;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register int retval;
+ register int items = arglast[2] - sp;
+
+ if (!fp) {
+ if (dowarn)
+ warn("print to unopened file");
+ errno = EBADF;
+ return FALSE;
+ }
+ st += ++sp;
+ if (arg->arg_type == O_PRTF) {
+ do_sprintf(arg->arg_ptr.arg_str,items,st);
+ retval = do_print(arg->arg_ptr.arg_str,fp);
+ }
+ else {
+ retval = (items <= 0);
+ for (; items > 0; items--,st++) {
+ if (retval && ofslen) {
+ if (fwrite(ofs, 1, ofslen, fp) == 0 || ferror(fp)) {
+ retval = FALSE;
+ break;
+ }
+ }
+ if (!(retval = do_print(*st, fp)))
+ break;
+ }
+ if (retval && orslen)
+ if (fwrite(ors, 1, orslen, fp) == 0 || ferror(fp))
+ retval = FALSE;
+ }
+ return retval;
+}
+
+int
+mystat(arg,str)
+ARG *arg;
+STR *str;
+{
+ STIO *stio;
+
+ if (arg[1].arg_type & A_DONT) {
+ stio = stab_io(arg[1].arg_ptr.arg_stab);
+ if (stio && stio->ifp) {
+ statstab = arg[1].arg_ptr.arg_stab;
+ str_set(statname,"");
+ laststype = O_STAT;
+ return (laststatval = fstat(fileno(stio->ifp), &statcache));
+ }
+ else {
+ if (arg[1].arg_ptr.arg_stab == defstab)
+ return laststatval;
+ if (dowarn)
+ warn("Stat on unopened file <%s>",
+ stab_ename(arg[1].arg_ptr.arg_stab));
+ statstab = Nullstab;
+ str_set(statname,"");
+ return (laststatval = -1);
+ }
+ }
+ else {
+ statstab = Nullstab;
+ str_set(statname,str_get(str));
+ laststype = O_STAT;
+ laststatval = stat(str_get(str),&statcache);
+ if (laststatval < 0 && dowarn && index(str_get(str), '\n'))
+ warn(warn_nl, "stat");
+ return laststatval;
+ }
+}
+
+int
+mylstat(arg,str)
+ARG *arg;
+STR *str;
+{
+ if (arg[1].arg_type & A_DONT) {
+ if (arg[1].arg_ptr.arg_stab == defstab) {
+ if (laststype != O_LSTAT)
+ fatal("The stat preceding -l _ wasn't an lstat");
+ return laststatval;
+ }
+ fatal("You can't use -l on a filehandle");
+ }
+
+ laststype = O_LSTAT;
+ statstab = Nullstab;
+ str_set(statname,str_get(str));
+#ifdef HAS_LSTAT
+ laststatval = lstat(str_get(str),&statcache);
+#else
+ laststatval = stat(str_get(str),&statcache);
+#endif
+ if (laststatval < 0 && dowarn && index(str_get(str), '\n'))
+ warn(warn_nl, "lstat");
+ return laststatval;
+}
+
+STR *
+do_fttext(arg,str)
+register ARG *arg;
+STR *str;
+{
+ int i;
+ int len;
+ int odd = 0;
+ STDCHAR tbuf[512];
+ register STDCHAR *s;
+ register STIO *stio;
+
+ if (arg[1].arg_type & A_DONT) {
+ if (arg[1].arg_ptr.arg_stab == defstab) {
+ if (statstab)
+ stio = stab_io(statstab);
+ else {
+ str = statname;
+ goto really_filename;
+ }
+ }
+ else {
+ statstab = arg[1].arg_ptr.arg_stab;
+ str_set(statname,"");
+ stio = stab_io(statstab);
+ }
+ if (stio && stio->ifp) {
+#if defined(STDSTDIO) || defined(atarist) /* this will work with atariST */
+ fstat(fileno(stio->ifp),&statcache);
+ if (S_ISDIR(statcache.st_mode)) /* handle NFS glitch */
+ return arg->arg_type == O_FTTEXT ? &str_no : &str_yes;
+ if (stio->ifp->_cnt <= 0) {
+ i = getc(stio->ifp);
+ if (i != EOF)
+ (void)ungetc(i,stio->ifp);
+ }
+ if (stio->ifp->_cnt <= 0) /* null file is anything */
+ return &str_yes;
+ len = stio->ifp->_cnt + (stio->ifp->_ptr - stio->ifp->_base);
+ s = stio->ifp->_base;
+#else
+ fatal("-T and -B not implemented on filehandles");
+#endif
+ }
+ else {
+ if (dowarn)
+ warn("Test on unopened file <%s>",
+ stab_ename(arg[1].arg_ptr.arg_stab));
+ errno = EBADF;
+ return &str_undef;
+ }
+ }
+ else {
+ statstab = Nullstab;
+ str_set(statname,str_get(str));
+ really_filename:
+ i = open(str_get(str),0);
+ if (i < 0) {
+ if (dowarn && index(str_get(str), '\n'))
+ warn(warn_nl, "open");
+ return &str_undef;
+ }
+ fstat(i,&statcache);
+ len = read(i,tbuf,512);
+ (void)close(i);
+ if (len <= 0) {
+ if (S_ISDIR(statcache.st_mode) && arg->arg_type == O_FTTEXT)
+ return &str_no; /* special case NFS directories */
+ return &str_yes; /* null file is anything */
+ }
+ s = tbuf;
+ }
+
+ /* now scan s to look for textiness */
+
+ for (i = 0; i < len; i++,s++) {
+ if (!*s) { /* null never allowed in text */
+ odd += len;
+ break;
+ }
+ else if (*s & 128)
+ odd++;
+ else if (*s < 32 &&
+ *s != '\n' && *s != '\r' && *s != '\b' &&
+ *s != '\t' && *s != '\f' && *s != 27)
+ odd++;
+ }
+
+ if ((odd * 10 > len) == (arg->arg_type == O_FTTEXT)) /* allow 10% odd */
+ return &str_no;
+ else
+ return &str_yes;
+}
+
+static char **Argv = Null(char **);
+static char *Cmd = Nullch;
+
+bool
+do_aexec(really,arglast)
+STR *really;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register int items = arglast[2] - sp;
+ register char **a;
+ char *tmps;
+
+ if (items) {
+ New(401,Argv, items+1, char*);
+ a = Argv;
+ for (st += ++sp; items > 0; items--,st++) {
+ if (*st)
+ *a++ = str_get(*st);
+ else
+ *a++ = "";
+ }
+ *a = Nullch;
+#ifdef TAINT
+ if (*Argv[0] != '/') /* will execvp use PATH? */
+ taintenv(); /* testing IFS here is overkill, probably */
+#endif
+ if (really && *(tmps = str_get(really)))
+ execvp(tmps,Argv);
+ else
+ execvp(Argv[0],Argv);
+ }
+ do_execfree();
+ return FALSE;
+}
+
+void
+do_execfree()
+{
+ if (Argv) {
+ Safefree(Argv);
+ Argv = Null(char **);
+ }
+ if (Cmd) {
+ Safefree(Cmd);
+ Cmd = Nullch;
+ }
+}
+
+bool
+do_exec(cmd)
+char *cmd;
+{
+ register char **a;
+ register char *s;
+ char flags[10];
+
+ /* save an extra exec if possible */
+
+#ifdef CSH
+ if (strnEQ(cmd,cshname,cshlen) && strnEQ(cmd+cshlen," -c",3)) {
+ strcpy(flags,"-c");
+ s = cmd+cshlen+3;
+ if (*s == 'f') {
+ s++;
+ strcat(flags,"f");
+ }
+ if (*s == ' ')
+ s++;
+ if (*s++ == '\'') {
+ char *ncmd = s;
+
+ while (*s)
+ s++;
+ if (s[-1] == '\n')
+ *--s = '\0';
+ if (s[-1] == '\'') {
+ *--s = '\0';
+ execl(cshname,"csh", flags,ncmd,(char*)0);
+ *s = '\'';
+ return FALSE;
+ }
+ }
+ }
+#endif /* CSH */
+
+ /* see if there are shell metacharacters in it */
+
+ /*SUPPRESS 530*/
+ for (s = cmd; *s && isALPHA(*s); s++) ; /* catch VAR=val gizmo */
+ if (*s == '=')
+ goto doshell;
+ for (s = cmd; *s; s++) {
+ if (*s != ' ' && !isALPHA(*s) && index("$&*(){}[]'\";\\|?<>~`\n",*s)) {
+ if (*s == '\n' && !s[1]) {
+ *s = '\0';
+ break;
+ }
+ doshell:
+ execl("/bin/sh","sh","-c",cmd,(char*)0);
+ return FALSE;
+ }
+ }
+ New(402,Argv, (s - cmd) / 2 + 2, char*);
+ Cmd = nsavestr(cmd, s-cmd);
+ a = Argv;
+ for (s = Cmd; *s;) {
+ while (*s && isSPACE(*s)) s++;
+ if (*s)
+ *(a++) = s;
+ while (*s && !isSPACE(*s)) s++;
+ if (*s)
+ *s++ = '\0';
+ }
+ *a = Nullch;
+ if (Argv[0]) {
+ execvp(Argv[0],Argv);
+ if (errno == ENOEXEC) { /* for system V NIH syndrome */
+ do_execfree();
+ goto doshell;
+ }
+ }
+ do_execfree();
+ return FALSE;
+}
+
+#ifdef HAS_SOCKET
+int
+do_socket(stab, arglast)
+STAB *stab;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register STIO *stio;
+ int domain, type, protocol, fd;
+
+ if (!stab) {
+ errno = EBADF;
+ return FALSE;
+ }
+
+ stio = stab_io(stab);
+ if (!stio)
+ stio = stab_io(stab) = stio_new();
+ else if (stio->ifp)
+ do_close(stab,FALSE);
+
+ domain = (int)str_gnum(st[++sp]);
+ type = (int)str_gnum(st[++sp]);
+ protocol = (int)str_gnum(st[++sp]);
+#ifdef TAINT
+ taintproper("Insecure dependency in socket");
+#endif
+ fd = socket(domain,type,protocol);
+ if (fd < 0)
+ return FALSE;
+ stio->ifp = fdopen(fd, "r"); /* stdio gets confused about sockets */
+ stio->ofp = fdopen(fd, "w");
+ stio->type = 's';
+ if (!stio->ifp || !stio->ofp) {
+ if (stio->ifp) fclose(stio->ifp);
+ if (stio->ofp) fclose(stio->ofp);
+ if (!stio->ifp && !stio->ofp) close(fd);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+int
+do_bind(stab, arglast)
+STAB *stab;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register STIO *stio;
+ char *addr;
+
+ if (!stab)
+ goto nuts;
+
+ stio = stab_io(stab);
+ if (!stio || !stio->ifp)
+ goto nuts;
+
+ addr = str_get(st[++sp]);
+#ifdef TAINT
+ taintproper("Insecure dependency in bind");
+#endif
+ return bind(fileno(stio->ifp), (struct sockaddr * ) addr, st[sp]->str_cur) >= 0;
+
+nuts:
+ if (dowarn)
+ warn("bind() on closed fd");
+ errno = EBADF;
+ return FALSE;
+
+}
+
+int
+do_connect(stab, arglast)
+STAB *stab;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register STIO *stio;
+ char *addr;
+
+ if (!stab)
+ goto nuts;
+
+ stio = stab_io(stab);
+ if (!stio || !stio->ifp)
+ goto nuts;
+
+ addr = str_get(st[++sp]);
+#ifdef TAINT
+ taintproper("Insecure dependency in connect");
+#endif
+ return connect(fileno(stio->ifp), (struct sockaddr *) addr, st[sp]->str_cur) >= 0;
+
+nuts:
+ if (dowarn)
+ warn("connect() on closed fd");
+ errno = EBADF;
+ return FALSE;
+
+}
+
+int
+do_listen(stab, arglast)
+STAB *stab;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register STIO *stio;
+ int backlog;
+
+ if (!stab)
+ goto nuts;
+
+ stio = stab_io(stab);
+ if (!stio || !stio->ifp)
+ goto nuts;
+
+ backlog = (int)str_gnum(st[++sp]);
+ return listen(fileno(stio->ifp), backlog) >= 0;
+
+nuts:
+ if (dowarn)
+ warn("listen() on closed fd");
+ errno = EBADF;
+ return FALSE;
+}
+
+void
+do_accept(str, nstab, gstab)
+STR *str;
+STAB *nstab;
+STAB *gstab;
+{
+ register STIO *nstio;
+ register STIO *gstio;
+ int len = sizeof buf;
+ int fd;
+
+ if (!nstab)
+ goto badexit;
+ if (!gstab)
+ goto nuts;
+
+ gstio = stab_io(gstab);
+ nstio = stab_io(nstab);
+
+ if (!gstio || !gstio->ifp)
+ goto nuts;
+ if (!nstio)
+ nstio = stab_io(nstab) = stio_new();
+ else if (nstio->ifp)
+ do_close(nstab,FALSE);
+
+ fd = accept(fileno(gstio->ifp),(struct sockaddr *)buf,&len);
+ if (fd < 0)
+ goto badexit;
+ nstio->ifp = fdopen(fd, "r");
+ nstio->ofp = fdopen(fd, "w");
+ nstio->type = 's';
+ if (!nstio->ifp || !nstio->ofp) {
+ if (nstio->ifp) fclose(nstio->ifp);
+ if (nstio->ofp) fclose(nstio->ofp);
+ if (!nstio->ifp && !nstio->ofp) close(fd);
+ goto badexit;
+ }
+
+ str_nset(str, buf, len);
+ return;
+
+nuts:
+ if (dowarn)
+ warn("accept() on closed fd");
+ errno = EBADF;
+badexit:
+ str_sset(str,&str_undef);
+ return;
+}
+
+int
+do_shutdown(stab, arglast)
+STAB *stab;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register STIO *stio;
+ int how;
+
+ if (!stab)
+ goto nuts;
+
+ stio = stab_io(stab);
+ if (!stio || !stio->ifp)
+ goto nuts;
+
+ how = (int)str_gnum(st[++sp]);
+ return shutdown(fileno(stio->ifp), how) >= 0;
+
+nuts:
+ if (dowarn)
+ warn("shutdown() on closed fd");
+ errno = EBADF;
+ return FALSE;
+
+}
+
+int
+do_sopt(optype, stab, arglast)
+int optype;
+STAB *stab;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register STIO *stio;
+ int fd;
+ unsigned int lvl;
+ unsigned int optname;
+
+ if (!stab)
+ goto nuts;
+
+ stio = stab_io(stab);
+ if (!stio || !stio->ifp)
+ goto nuts;
+
+ fd = fileno(stio->ifp);
+ lvl = (unsigned int)str_gnum(st[sp+1]);
+ optname = (unsigned int)str_gnum(st[sp+2]);
+ switch (optype) {
+ case O_GSOCKOPT:
+ st[sp] = str_2mortal(Str_new(22,257));
+ st[sp]->str_cur = 256;
+ st[sp]->str_pok = 1;
+ if (getsockopt(fd, lvl, optname, st[sp]->str_ptr,
+ (int*)&st[sp]->str_cur) < 0)
+ goto nuts;
+ break;
+ case O_SSOCKOPT:
+ st[sp] = st[sp+3];
+ if (setsockopt(fd, lvl, optname, st[sp]->str_ptr, st[sp]->str_cur) < 0)
+ goto nuts;
+ st[sp] = &str_yes;
+ break;
+ }
+
+ return sp;
+
+nuts:
+ if (dowarn)
+ warn("[gs]etsockopt() on closed fd");
+ st[sp] = &str_undef;
+ errno = EBADF;
+ return sp;
+
+}
+
+int
+do_getsockname(optype, stab, arglast)
+int optype;
+STAB *stab;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register STIO *stio;
+ int fd;
+
+ if (!stab)
+ goto nuts;
+
+ stio = stab_io(stab);
+ if (!stio || !stio->ifp)
+ goto nuts;
+
+ st[sp] = str_2mortal(Str_new(22,257));
+ st[sp]->str_cur = 256;
+ st[sp]->str_pok = 1;
+ fd = fileno(stio->ifp);
+ switch (optype) {
+ case O_GETSOCKNAME:
+ if (getsockname(fd, (struct sockaddr *) st[sp]->str_ptr, (int*)&st[sp]->str_cur) < 0)
+ goto nuts2;
+ break;
+ case O_GETPEERNAME:
+ if (getpeername(fd, (struct sockaddr *) st[sp]->str_ptr, (int*)&st[sp]->str_cur) < 0)
+ goto nuts2;
+ break;
+ }
+
+ return sp;
+
+nuts:
+ if (dowarn)
+ warn("get{sock,peer}name() on closed fd");
+ errno = EBADF;
+nuts2:
+ st[sp] = &str_undef;
+ return sp;
+
+}
+
+int
+do_ghent(which,gimme,arglast)
+int which;
+int gimme;
+int *arglast;
+{
+ register ARRAY *ary = stack;
+ register int sp = arglast[0];
+ register char **elem;
+ register STR *str;
+ struct hostent *gethostbyname();
+ struct hostent *gethostbyaddr();
+#ifdef HAS_GETHOSTENT
+ struct hostent *gethostent();
+#endif
+ struct hostent *hent;
+ unsigned long len;
+
+ if (which == O_GHBYNAME) {
+ char *name = str_get(ary->ary_array[sp+1]);
+
+ hent = gethostbyname(name);
+ }
+ else if (which == O_GHBYADDR) {
+ STR *addrstr = ary->ary_array[sp+1];
+ int addrtype = (int)str_gnum(ary->ary_array[sp+2]);
+ char *addr = str_get(addrstr);
+
+ hent = gethostbyaddr(addr,addrstr->str_cur,addrtype);
+ }
+ else
+#ifdef HAS_GETHOSTENT
+ hent = gethostent();
+#else
+ fatal("gethostent not implemented");
+#endif
+
+#ifdef HOST_NOT_FOUND
+ if (!hent)
+ statusvalue = (unsigned short)h_errno & 0xffff;
+#endif
+
+ if (gimme != G_ARRAY) {
+ astore(ary, ++sp, str = str_mortal(&str_undef));
+ if (hent) {
+ if (which == O_GHBYNAME) {
+#ifdef h_addr
+ str_nset(str, *hent->h_addr, hent->h_length);
+#else
+ str_nset(str, hent->h_addr, hent->h_length);
+#endif
+ }
+ else
+ str_set(str, hent->h_name);
+ }
+ return sp;
+ }
+
+ if (hent) {
+#ifndef lint
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, hent->h_name);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ for (elem = hent->h_aliases; *elem; elem++) {
+ str_cat(str, *elem);
+ if (elem[1])
+ str_ncat(str," ",1);
+ }
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_numset(str, (double)hent->h_addrtype);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ len = hent->h_length;
+ str_numset(str, (double)len);
+#ifdef h_addr
+ for (elem = hent->h_addr_list; *elem; elem++) {
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_nset(str, *elem, len);
+ }
+#else
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_nset(str, hent->h_addr, len);
+#endif /* h_addr */
+#else /* lint */
+ elem = Nullch;
+ elem = elem;
+ (void)astore(ary, ++sp, str_mortal(&str_no));
+#endif /* lint */
+ }
+
+ return sp;
+}
+
+int
+do_gnent(which,gimme,arglast)
+int which;
+int gimme;
+int *arglast;
+{
+ register ARRAY *ary = stack;
+ register int sp = arglast[0];
+ register char **elem;
+ register STR *str;
+ struct netent *getnetbyname();
+ struct netent *getnetbyaddr();
+ struct netent *getnetent();
+ struct netent *nent;
+
+ if (which == O_GNBYNAME) {
+ char *name = str_get(ary->ary_array[sp+1]);
+
+ nent = getnetbyname(name);
+ }
+ else if (which == O_GNBYADDR) {
+ unsigned long addr = U_L(str_gnum(ary->ary_array[sp+1]));
+ int addrtype = (int)str_gnum(ary->ary_array[sp+2]);
+
+ nent = getnetbyaddr((long)addr,addrtype);
+ }
+ else
+ nent = getnetent();
+
+ if (gimme != G_ARRAY) {
+ astore(ary, ++sp, str = str_mortal(&str_undef));
+ if (nent) {
+ if (which == O_GNBYNAME)
+ str_numset(str, (double)nent->n_net);
+ else
+ str_set(str, nent->n_name);
+ }
+ return sp;
+ }
+
+ if (nent) {
+#ifndef lint
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, nent->n_name);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ for (elem = nent->n_aliases; *elem; elem++) {
+ str_cat(str, *elem);
+ if (elem[1])
+ str_ncat(str," ",1);
+ }
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_numset(str, (double)nent->n_addrtype);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_numset(str, (double)nent->n_net);
+#else /* lint */
+ elem = Nullch;
+ elem = elem;
+ (void)astore(ary, ++sp, str_mortal(&str_no));
+#endif /* lint */
+ }
+
+ return sp;
+}
+
+int
+do_gpent(which,gimme,arglast)
+int which;
+int gimme;
+int *arglast;
+{
+ register ARRAY *ary = stack;
+ register int sp = arglast[0];
+ register char **elem;
+ register STR *str;
+ struct protoent *getprotobyname();
+ struct protoent *getprotobynumber();
+ struct protoent *getprotoent();
+ struct protoent *pent;
+
+ if (which == O_GPBYNAME) {
+ char *name = str_get(ary->ary_array[sp+1]);
+
+ pent = getprotobyname(name);
+ }
+ else if (which == O_GPBYNUMBER) {
+ int proto = (int)str_gnum(ary->ary_array[sp+1]);
+
+ pent = getprotobynumber(proto);
+ }
+ else
+ pent = getprotoent();
+
+ if (gimme != G_ARRAY) {
+ astore(ary, ++sp, str = str_mortal(&str_undef));
+ if (pent) {
+ if (which == O_GPBYNAME)
+ str_numset(str, (double)pent->p_proto);
+ else
+ str_set(str, pent->p_name);
+ }
+ return sp;
+ }
+
+ if (pent) {
+#ifndef lint
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, pent->p_name);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ for (elem = pent->p_aliases; *elem; elem++) {
+ str_cat(str, *elem);
+ if (elem[1])
+ str_ncat(str," ",1);
+ }
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_numset(str, (double)pent->p_proto);
+#else /* lint */
+ elem = Nullch;
+ elem = elem;
+ (void)astore(ary, ++sp, str_mortal(&str_no));
+#endif /* lint */
+ }
+
+ return sp;
+}
+
+int
+do_gsent(which,gimme,arglast)
+int which;
+int gimme;
+int *arglast;
+{
+ register ARRAY *ary = stack;
+ register int sp = arglast[0];
+ register char **elem;
+ register STR *str;
+ struct servent *getservbyname();
+ struct servent *getservbynumber();
+ struct servent *getservent();
+ struct servent *sent;
+
+ if (which == O_GSBYNAME) {
+ char *name = str_get(ary->ary_array[sp+1]);
+ char *proto = str_get(ary->ary_array[sp+2]);
+
+ if (proto && !*proto)
+ proto = Nullch;
+
+ sent = getservbyname(name,proto);
+ }
+ else if (which == O_GSBYPORT) {
+ int port = (int)str_gnum(ary->ary_array[sp+1]);
+ char *proto = str_get(ary->ary_array[sp+2]);
+
+ sent = getservbyport(port,proto);
+ }
+ else
+ sent = getservent();
+
+ if (gimme != G_ARRAY) {
+ astore(ary, ++sp, str = str_mortal(&str_undef));
+ if (sent) {
+ if (which == O_GSBYNAME) {
+#ifdef HAS_NTOHS
+ str_numset(str, (double)ntohs(sent->s_port));
+#else
+ str_numset(str, (double)(sent->s_port));
+#endif
+ }
+ else
+ str_set(str, sent->s_name);
+ }
+ return sp;
+ }
+
+ if (sent) {
+#ifndef lint
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, sent->s_name);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ for (elem = sent->s_aliases; *elem; elem++) {
+ str_cat(str, *elem);
+ if (elem[1])
+ str_ncat(str," ",1);
+ }
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+#ifdef HAS_NTOHS
+ str_numset(str, (double)ntohs(sent->s_port));
+#else
+ str_numset(str, (double)(sent->s_port));
+#endif
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, sent->s_proto);
+#else /* lint */
+ elem = Nullch;
+ elem = elem;
+ (void)astore(ary, ++sp, str_mortal(&str_no));
+#endif /* lint */
+ }
+
+ return sp;
+}
+
+#endif /* HAS_SOCKET */
+
+#ifdef HAS_SELECT
+int
+do_select(gimme,arglast)
+int gimme;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[0];
+ register int i;
+ register int j;
+ register char *s;
+ register STR *str;
+ double value;
+ int maxlen = 0;
+ int nfound;
+ struct timeval timebuf;
+ struct timeval *tbuf = &timebuf;
+ int growsize;
+#if BYTEORDER != 0x1234 && BYTEORDER != 0x12345678
+ int masksize;
+ int offset;
+ char *fd_sets[4];
+ int k;
+
+#if BYTEORDER & 0xf0000
+#define ORDERBYTE (0x88888888 - BYTEORDER)
+#else
+#define ORDERBYTE (0x4444 - BYTEORDER)
+#endif
+
+#endif
+
+ for (i = 1; i <= 3; i++) {
+ j = st[sp+i]->str_cur;
+ if (maxlen < j)
+ maxlen = j;
+ }
+
+#if BYTEORDER == 0x1234 || BYTEORDER == 0x12345678
+ growsize = maxlen; /* little endians can use vecs directly */
+#else
+#ifdef NFDBITS
+
+#ifndef NBBY
+#define NBBY 8
+#endif
+
+ masksize = NFDBITS / NBBY;
+#else
+ masksize = sizeof(long); /* documented int, everyone seems to use long */
+#endif
+ growsize = maxlen + (masksize - (maxlen % masksize));
+ Zero(&fd_sets[0], 4, char*);
+#endif
+
+ for (i = 1; i <= 3; i++) {
+ str = st[sp+i];
+ j = str->str_len;
+ if (j < growsize) {
+ if (str->str_pok) {
+ Str_Grow(str,growsize);
+ s = str_get(str) + j;
+ while (++j <= growsize) {
+ *s++ = '\0';
+ }
+ }
+ else if (str->str_ptr) {
+ Safefree(str->str_ptr);
+ str->str_ptr = Nullch;
+ }
+ }
+#if BYTEORDER != 0x1234 && BYTEORDER != 0x12345678
+ s = str->str_ptr;
+ if (s) {
+ New(403, fd_sets[i], growsize, char);
+ for (offset = 0; offset < growsize; offset += masksize) {
+ for (j = 0, k=ORDERBYTE; j < masksize; j++, (k >>= 4))
+ fd_sets[i][j+offset] = s[(k % masksize) + offset];
+ }
+ }
+#endif
+ }
+ str = st[sp+4];
+ if (str->str_nok || str->str_pok) {
+ value = str_gnum(str);
+ if (value < 0.0)
+ value = 0.0;
+ timebuf.tv_sec = (long)value;
+ value -= (double)timebuf.tv_sec;
+ timebuf.tv_usec = (long)(value * 1000000.0);
+ }
+ else
+ tbuf = Null(struct timeval*);
+
+#if BYTEORDER == 0x1234 || BYTEORDER == 0x12345678
+ nfound = select(
+ maxlen * 8,
+ (fd_set *) st[sp+1]->str_ptr,
+ (fd_set *) st[sp+2]->str_ptr,
+ (fd_set *) st[sp+3]->str_ptr,
+ tbuf);
+#else
+ nfound = select(
+ maxlen * 8,
+ fd_sets[1],
+ fd_sets[2],
+ fd_sets[3],
+ tbuf);
+ for (i = 1; i <= 3; i++) {
+ if (fd_sets[i]) {
+ str = st[sp+i];
+ s = str->str_ptr;
+ for (offset = 0; offset < growsize; offset += masksize) {
+ for (j = 0, k=ORDERBYTE; j < masksize; j++, (k >>= 4))
+ s[(k % masksize) + offset] = fd_sets[i][j+offset];
+ }
+ Safefree(fd_sets[i]);
+ }
+ }
+#endif
+
+ st[++sp] = str_mortal(&str_no);
+ str_numset(st[sp], (double)nfound);
+ if (gimme == G_ARRAY && tbuf) {
+ value = (double)(timebuf.tv_sec) +
+ (double)(timebuf.tv_usec) / 1000000.0;
+ st[++sp] = str_mortal(&str_no);
+ str_numset(st[sp], value);
+ }
+ return sp;
+}
+#endif /* SELECT */
+
+#ifdef HAS_SOCKET
+int
+do_spair(stab1, stab2, arglast)
+STAB *stab1;
+STAB *stab2;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[2];
+ register STIO *stio1;
+ register STIO *stio2;
+ int domain, type, protocol, fd[2];
+
+ if (!stab1 || !stab2)
+ return FALSE;
+
+ stio1 = stab_io(stab1);
+ stio2 = stab_io(stab2);
+ if (!stio1)
+ stio1 = stab_io(stab1) = stio_new();
+ else if (stio1->ifp)
+ do_close(stab1,FALSE);
+ if (!stio2)
+ stio2 = stab_io(stab2) = stio_new();
+ else if (stio2->ifp)
+ do_close(stab2,FALSE);
+
+ domain = (int)str_gnum(st[++sp]);
+ type = (int)str_gnum(st[++sp]);
+ protocol = (int)str_gnum(st[++sp]);
+#ifdef TAINT
+ taintproper("Insecure dependency in socketpair");
+#endif
+#ifdef HAS_SOCKETPAIR
+ if (socketpair(domain,type,protocol,fd) < 0)
+ return FALSE;
+#else
+ fatal("Socketpair unimplemented");
+#endif
+ stio1->ifp = fdopen(fd[0], "r");
+ stio1->ofp = fdopen(fd[0], "w");
+ stio1->type = 's';
+ stio2->ifp = fdopen(fd[1], "r");
+ stio2->ofp = fdopen(fd[1], "w");
+ stio2->type = 's';
+ if (!stio1->ifp || !stio1->ofp || !stio2->ifp || !stio2->ofp) {
+ if (stio1->ifp) fclose(stio1->ifp);
+ if (stio1->ofp) fclose(stio1->ofp);
+ if (!stio1->ifp && !stio1->ofp) close(fd[0]);
+ if (stio2->ifp) fclose(stio2->ifp);
+ if (stio2->ofp) fclose(stio2->ofp);
+ if (!stio2->ifp && !stio2->ofp) close(fd[1]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#endif /* HAS_SOCKET */
+
+int
+do_gpwent(which,gimme,arglast)
+int which;
+int gimme;
+int *arglast;
+{
+#ifdef I_PWD
+ register ARRAY *ary = stack;
+ register int sp = arglast[0];
+ register STR *str;
+ struct passwd *getpwnam();
+ struct passwd *getpwuid();
+ struct passwd *getpwent();
+ struct passwd *pwent;
+
+ if (which == O_GPWNAM) {
+ char *name = str_get(ary->ary_array[sp+1]);
+
+ pwent = getpwnam(name);
+ }
+ else if (which == O_GPWUID) {
+ int uid = (int)str_gnum(ary->ary_array[sp+1]);
+
+ pwent = getpwuid(uid);
+ }
+ else
+ pwent = getpwent();
+
+ if (gimme != G_ARRAY) {
+ astore(ary, ++sp, str = str_mortal(&str_undef));
+ if (pwent) {
+ if (which == O_GPWNAM)
+ str_numset(str, (double)pwent->pw_uid);
+ else
+ str_set(str, pwent->pw_name);
+ }
+ return sp;
+ }
+
+ if (pwent) {
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, pwent->pw_name);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, pwent->pw_passwd);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_numset(str, (double)pwent->pw_uid);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_numset(str, (double)pwent->pw_gid);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+#ifdef PWCHANGE
+ str_numset(str, (double)pwent->pw_change);
+#else
+#ifdef PWQUOTA
+ str_numset(str, (double)pwent->pw_quota);
+#else
+#ifdef PWAGE
+ str_set(str, pwent->pw_age);
+#endif
+#endif
+#endif
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+#ifdef PWCLASS
+ str_set(str,pwent->pw_class);
+#else
+#ifdef PWCOMMENT
+ str_set(str, pwent->pw_comment);
+#endif
+#endif
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, pwent->pw_gecos);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, pwent->pw_dir);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, pwent->pw_shell);
+#ifdef PWEXPIRE
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_numset(str, (double)pwent->pw_expire);
+#endif
+ }
+
+ return sp;
+#else
+ fatal("password routines not implemented");
+#endif
+}
+
+int
+do_ggrent(which,gimme,arglast)
+int which;
+int gimme;
+int *arglast;
+{
+#ifdef I_GRP
+ register ARRAY *ary = stack;
+ register int sp = arglast[0];
+ register char **elem;
+ register STR *str;
+ struct group *getgrnam();
+ struct group *getgrgid();
+ struct group *getgrent();
+ struct group *grent;
+
+ if (which == O_GGRNAM) {
+ char *name = str_get(ary->ary_array[sp+1]);
+
+ grent = getgrnam(name);
+ }
+ else if (which == O_GGRGID) {
+ int gid = (int)str_gnum(ary->ary_array[sp+1]);
+
+ grent = getgrgid(gid);
+ }
+ else
+ grent = getgrent();
+
+ if (gimme != G_ARRAY) {
+ astore(ary, ++sp, str = str_mortal(&str_undef));
+ if (grent) {
+ if (which == O_GGRNAM)
+ str_numset(str, (double)grent->gr_gid);
+ else
+ str_set(str, grent->gr_name);
+ }
+ return sp;
+ }
+
+ if (grent) {
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, grent->gr_name);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, grent->gr_passwd);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_numset(str, (double)grent->gr_gid);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ for (elem = grent->gr_mem; *elem; elem++) {
+ str_cat(str, *elem);
+ if (elem[1])
+ str_ncat(str," ",1);
+ }
+ }
+
+ return sp;
+#else
+ fatal("group routines not implemented");
+#endif
+}
+
+int
+do_dirop(optype,stab,gimme,arglast)
+int optype;
+STAB *stab;
+int gimme;
+int *arglast;
+{
+#if defined(DIRENT) && defined(HAS_READDIR)
+ register ARRAY *ary = stack;
+ register STR **st = ary->ary_array;
+ register int sp = arglast[1];
+ register STIO *stio;
+ long along;
+#ifndef apollo
+ struct DIRENT *readdir();
+#endif
+ register struct DIRENT *dp;
+
+ if (!stab)
+ goto nope;
+ if (!(stio = stab_io(stab)))
+ stio = stab_io(stab) = stio_new();
+ if (!stio->dirp && optype != O_OPEN_DIR)
+ goto nope;
+ st[sp] = &str_yes;
+ switch (optype) {
+ case O_OPEN_DIR:
+ if (stio->dirp)
+ closedir(stio->dirp);
+ if (!(stio->dirp = opendir(str_get(st[sp+1]))))
+ goto nope;
+ break;
+ case O_READDIR:
+ if (gimme == G_ARRAY) {
+ --sp;
+ /*SUPPRESS 560*/
+ while (dp = readdir(stio->dirp)) {
+#ifdef DIRNAMLEN
+ (void)astore(ary,++sp,
+ str_2mortal(str_make(dp->d_name,dp->d_namlen)));
+#else
+ (void)astore(ary,++sp,
+ str_2mortal(str_make(dp->d_name,0)));
+#endif
+ }
+ }
+ else {
+ if (!(dp = readdir(stio->dirp)))
+ goto nope;
+ st[sp] = str_mortal(&str_undef);
+#ifdef DIRNAMLEN
+ str_nset(st[sp], dp->d_name, dp->d_namlen);
+#else
+ str_set(st[sp], dp->d_name);
+#endif
+ }
+ break;
+#if defined(HAS_TELLDIR) || defined(telldir)
+ case O_TELLDIR: {
+#ifndef telldir
+ long telldir();
+#endif
+ st[sp] = str_mortal(&str_undef);
+ str_numset(st[sp], (double)telldir(stio->dirp));
+ break;
+ }
+#endif
+#if defined(HAS_SEEKDIR) || defined(seekdir)
+ case O_SEEKDIR:
+ st[sp] = str_mortal(&str_undef);
+ along = (long)str_gnum(st[sp+1]);
+ (void)seekdir(stio->dirp,along);
+ break;
+#endif
+#if defined(HAS_REWINDDIR) || defined(rewinddir)
+ case O_REWINDDIR:
+ st[sp] = str_mortal(&str_undef);
+ (void)rewinddir(stio->dirp);
+ break;
+#endif
+ case O_CLOSEDIR:
+ st[sp] = str_mortal(&str_undef);
+ (void)closedir(stio->dirp);
+ stio->dirp = 0;
+ break;
+ default:
+ goto phooey;
+ }
+ return sp;
+
+nope:
+ st[sp] = &str_undef;
+ if (!errno)
+ errno = EBADF;
+ return sp;
+
+#endif
+phooey:
+ fatal("Unimplemented directory operation");
+}
+
+int
+apply(type,arglast)
+int type;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register int items = arglast[2] - sp;
+ register int val;
+ register int val2;
+ register int tot = 0;
+ char *s;
+
+#ifdef TAINT
+ for (st += ++sp; items--; st++)
+ tainted |= (*st)->str_tainted;
+ st = stack->ary_array;
+ sp = arglast[1];
+ items = arglast[2] - sp;
+#endif
+ switch (type) {
+ case O_CHMOD:
+#ifdef TAINT
+ taintproper("Insecure dependency in chmod");
+#endif
+ if (--items > 0) {
+ tot = items;
+ val = (int)str_gnum(st[++sp]);
+ while (items--) {
+ if (chmod(str_get(st[++sp]),val))
+ tot--;
+ }
+ }
+ break;
+#ifdef HAS_CHOWN
+ case O_CHOWN:
+#ifdef TAINT
+ taintproper("Insecure dependency in chown");
+#endif
+ if (items > 2) {
+ items -= 2;
+ tot = items;
+ val = (int)str_gnum(st[++sp]);
+ val2 = (int)str_gnum(st[++sp]);
+ while (items--) {
+ if (chown(str_get(st[++sp]),val,val2))
+ tot--;
+ }
+ }
+ break;
+#endif
+#ifdef HAS_KILL
+ case O_KILL:
+#ifdef TAINT
+ taintproper("Insecure dependency in kill");
+#endif
+ if (--items > 0) {
+ tot = items;
+ s = str_get(st[++sp]);
+ if (isUPPER(*s)) {
+ if (*s == 'S' && s[1] == 'I' && s[2] == 'G')
+ s += 3;
+ if (!(val = whichsig(s)))
+ fatal("Unrecognized signal name \"%s\"",s);
+ }
+ else
+ val = (int)str_gnum(st[sp]);
+ if (val < 0) {
+ val = -val;
+ while (items--) {
+ int proc = (int)str_gnum(st[++sp]);
+#ifdef HAS_KILLPG
+ if (killpg(proc,val)) /* BSD */
+#else
+ if (kill(-proc,val)) /* SYSV */
+#endif
+ tot--;
+ }
+ }
+ else {
+ while (items--) {
+ if (kill((int)(str_gnum(st[++sp])),val))
+ tot--;
+ }
+ }
+ }
+ break;
+#endif
+ case O_UNLINK:
+#ifdef TAINT
+ taintproper("Insecure dependency in unlink");
+#endif
+ tot = items;
+ while (items--) {
+ s = str_get(st[++sp]);
+ if (euid || unsafe) {
+ if (UNLINK(s))
+ tot--;
+ }
+ else { /* don't let root wipe out directories without -U */
+#ifdef HAS_LSTAT
+ if (lstat(s,&statbuf) < 0 || S_ISDIR(statbuf.st_mode))
+#else
+ if (stat(s,&statbuf) < 0 || S_ISDIR(statbuf.st_mode))
+#endif
+ tot--;
+ else {
+ if (UNLINK(s))
+ tot--;
+ }
+ }
+ }
+ break;
+ case O_UTIME:
+#ifdef TAINT
+ taintproper("Insecure dependency in utime");
+#endif
+ if (items > 2) {
+#ifdef I_UTIME
+ struct utimbuf utbuf;
+#else
+ struct {
+ long actime;
+ long modtime;
+ } utbuf;
+#endif
+
+ Zero(&utbuf, sizeof utbuf, char);
+ utbuf.actime = (long)str_gnum(st[++sp]); /* time accessed */
+ utbuf.modtime = (long)str_gnum(st[++sp]); /* time modified */
+ items -= 2;
+#ifndef lint
+ tot = items;
+ while (items--) {
+ if (utime(str_get(st[++sp]),&utbuf))
+ tot--;
+ }
+#endif
+ }
+ else
+ items = 0;
+ break;
+ }
+ return tot;
+}
+
+/* Do the permissions allow some operation? Assumes statcache already set. */
+
+int
+cando(bit, effective, statbufp)
+int bit;
+int effective;
+register struct stat *statbufp;
+{
+#ifdef DOSISH
+ /* [Comments and code from Len Reed]
+ * MS-DOS "user" is similar to UNIX's "superuser," but can't write
+ * to write-protected files. The execute permission bit is set
+ * by the Miscrosoft C library stat() function for the following:
+ * .exe files
+ * .com files
+ * .bat files
+ * directories
+ * All files and directories are readable.
+ * Directories and special files, e.g. "CON", cannot be
+ * write-protected.
+ * [Comment by Tom Dinger -- a directory can have the write-protect
+ * bit set in the file system, but DOS permits changes to
+ * the directory anyway. In addition, all bets are off
+ * here for networked software, such as Novell and
+ * Sun's PC-NFS.]
+ */
+
+ /* Atari stat() does pretty much the same thing. we set x_bit_set_in_stat
+ * too so it will actually look into the files for magic numbers
+ */
+ return (bit & statbufp->st_mode) ? TRUE : FALSE;
+
+#else /* ! MSDOS */
+ if ((effective ? euid : uid) == 0) { /* root is special */
+ if (bit == S_IXUSR) {
+ if (statbufp->st_mode & 0111 || S_ISDIR(statbufp->st_mode))
+ return TRUE;
+ }
+ else
+ return TRUE; /* root reads and writes anything */
+ return FALSE;
+ }
+ if (statbufp->st_uid == (effective ? euid : uid) ) {
+ if (statbufp->st_mode & bit)
+ return TRUE; /* ok as "user" */
+ }
+ else if (ingroup((int)statbufp->st_gid,effective)) {
+ if (statbufp->st_mode & bit >> 3)
+ return TRUE; /* ok as "group" */
+ }
+ else if (statbufp->st_mode & bit >> 6)
+ return TRUE; /* ok as "other" */
+ return FALSE;
+#endif /* ! MSDOS */
+}
+
+int
+ingroup(testgid,effective)
+int testgid;
+int effective;
+{
+ if (testgid == (effective ? egid : gid))
+ return TRUE;
+#ifdef HAS_GETGROUPS
+#ifndef NGROUPS
+#define NGROUPS 32
+#endif
+ {
+ GROUPSTYPE gary[NGROUPS];
+ int anum;
+
+ anum = getgroups(NGROUPS,gary);
+ while (--anum >= 0)
+ if (gary[anum] == testgid)
+ return TRUE;
+ }
+#endif
+ return FALSE;
+}
+
+#if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM)
+
+int
+do_ipcget(optype, arglast)
+int optype;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[0];
+ key_t key;
+ int n, flags;
+
+ key = (key_t)str_gnum(st[++sp]);
+ n = (optype == O_MSGGET) ? 0 : (int)str_gnum(st[++sp]);
+ flags = (int)str_gnum(st[++sp]);
+ errno = 0;
+ switch (optype)
+ {
+#ifdef HAS_MSG
+ case O_MSGGET:
+ return msgget(key, flags);
+#endif
+#ifdef HAS_SEM
+ case O_SEMGET:
+ return semget(key, n, flags);
+#endif
+#ifdef HAS_SHM
+ case O_SHMGET:
+ return shmget(key, n, flags);
+#endif
+#if !defined(HAS_MSG) || !defined(HAS_SEM) || !defined(HAS_SHM)
+ default:
+ fatal("%s not implemented", opname[optype]);
+#endif
+ }
+ return -1; /* should never happen */
+}
+
+int
+do_ipcctl(optype, arglast)
+int optype;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[0];
+ STR *astr;
+ char *a;
+ int id, n, cmd, infosize, getinfo, ret;
+
+ id = (int)str_gnum(st[++sp]);
+ n = (optype == O_SEMCTL) ? (int)str_gnum(st[++sp]) : 0;
+ cmd = (int)str_gnum(st[++sp]);
+ astr = st[++sp];
+
+ infosize = 0;
+ getinfo = (cmd == IPC_STAT);
+
+ switch (optype)
+ {
+#ifdef HAS_MSG
+ case O_MSGCTL:
+ if (cmd == IPC_STAT || cmd == IPC_SET)
+ infosize = sizeof(struct msqid_ds);
+ break;
+#endif
+#ifdef HAS_SHM
+ case O_SHMCTL:
+ if (cmd == IPC_STAT || cmd == IPC_SET)
+ infosize = sizeof(struct shmid_ds);
+ break;
+#endif
+#ifdef HAS_SEM
+ case O_SEMCTL:
+ if (cmd == IPC_STAT || cmd == IPC_SET)
+ infosize = sizeof(struct semid_ds);
+ else if (cmd == GETALL || cmd == SETALL)
+ {
+ struct semid_ds semds;
+ if (semctl(id, 0, IPC_STAT, (union semun)&semds) == -1)
+ return -1;
+ getinfo = (cmd == GETALL);
+ infosize = semds.sem_nsems * sizeof(short);
+ /* "short" is technically wrong but much more portable
+ than guessing about u_?short(_t)? */
+ }
+ break;
+#endif
+#if !defined(HAS_MSG) || !defined(HAS_SEM) || !defined(HAS_SHM)
+ default:
+ fatal("%s not implemented", opname[optype]);
+#endif
+ }
+
+ if (infosize)
+ {
+ if (getinfo)
+ {
+ STR_GROW(astr, infosize+1);
+ a = str_get(astr);
+ }
+ else
+ {
+ a = str_get(astr);
+ if (astr->str_cur != infosize)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ int i = (int)str_gnum(astr);
+ a = (char *)i; /* ouch */
+ }
+ errno = 0;
+ switch (optype)
+ {
+#ifdef HAS_MSG
+ case O_MSGCTL:
+ ret = msgctl(id, cmd, (struct msqid_ds *)a);
+ break;
+#endif
+#ifdef HAS_SEM
+ case O_SEMCTL:
+ ret = semctl(id, n, cmd, (union semun)((int)a));
+ break;
+#endif
+#ifdef HAS_SHM
+ case O_SHMCTL:
+ ret = shmctl(id, cmd, (struct shmid_ds *)a);
+ break;
+#endif
+ }
+ if (getinfo && ret >= 0) {
+ astr->str_cur = infosize;
+ astr->str_ptr[infosize] = '\0';
+ }
+ return ret;
+}
+
+int
+do_msgsnd(arglast)
+int *arglast;
+{
+#ifdef HAS_MSG
+ register STR **st = stack->ary_array;
+ register int sp = arglast[0];
+ STR *mstr;
+ char *mbuf;
+ int id, msize, flags;
+
+ id = (int)str_gnum(st[++sp]);
+ mstr = st[++sp];
+ flags = (int)str_gnum(st[++sp]);
+ mbuf = str_get(mstr);
+ if ((msize = mstr->str_cur - sizeof(long)) < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ errno = 0;
+ return msgsnd(id, (struct msgbuf *)mbuf, msize, flags);
+#else
+ fatal("msgsnd not implemented");
+#endif
+}
+
+int
+do_msgrcv(arglast)
+int *arglast;
+{
+#ifdef HAS_MSG
+ register STR **st = stack->ary_array;
+ register int sp = arglast[0];
+ STR *mstr;
+ char *mbuf;
+ long mtype;
+ int id, msize, flags, ret;
+
+ id = (int)str_gnum(st[++sp]);
+ mstr = st[++sp];
+ msize = (int)str_gnum(st[++sp]);
+ mtype = (long)str_gnum(st[++sp]);
+ flags = (int)str_gnum(st[++sp]);
+ mbuf = str_get(mstr);
+ if (mstr->str_cur < sizeof(long)+msize+1) {
+ STR_GROW(mstr, sizeof(long)+msize+1);
+ mbuf = str_get(mstr);
+ }
+ errno = 0;
+ ret = msgrcv(id, (struct msgbuf *)mbuf, msize, mtype, flags);
+ if (ret >= 0) {
+ mstr->str_cur = sizeof(long)+ret;
+ mstr->str_ptr[sizeof(long)+ret] = '\0';
+ }
+ return ret;
+#else
+ fatal("msgrcv not implemented");
+#endif
+}
+
+int
+do_semop(arglast)
+int *arglast;
+{
+#ifdef HAS_SEM
+ register STR **st = stack->ary_array;
+ register int sp = arglast[0];
+ STR *opstr;
+ char *opbuf;
+ int id, opsize;
+
+ id = (int)str_gnum(st[++sp]);
+ opstr = st[++sp];
+ opbuf = str_get(opstr);
+ opsize = opstr->str_cur;
+ if (opsize < sizeof(struct sembuf)
+ || (opsize % sizeof(struct sembuf)) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ errno = 0;
+ return semop(id, (struct sembuf *)opbuf, opsize/sizeof(struct sembuf));
+#else
+ fatal("semop not implemented");
+#endif
+}
+
+int
+do_shmio(optype, arglast)
+int optype;
+int *arglast;
+{
+#ifdef HAS_SHM
+ register STR **st = stack->ary_array;
+ register int sp = arglast[0];
+ STR *mstr;
+ char *mbuf, *shm;
+ int id, mpos, msize;
+ struct shmid_ds shmds;
+#ifndef VOIDSHMAT
+ extern char *shmat();
+#endif
+
+ id = (int)str_gnum(st[++sp]);
+ mstr = st[++sp];
+ mpos = (int)str_gnum(st[++sp]);
+ msize = (int)str_gnum(st[++sp]);
+ errno = 0;
+ if (shmctl(id, IPC_STAT, &shmds) == -1)
+ return -1;
+ if (mpos < 0 || msize < 0 || mpos + msize > shmds.shm_segsz) {
+ errno = EFAULT; /* can't do as caller requested */
+ return -1;
+ }
+ shm = (char*)shmat(id, (char*)NULL, (optype == O_SHMREAD) ? SHM_RDONLY : 0);
+ if (shm == (char *)-1) /* I hate System V IPC, I really do */
+ return -1;
+ mbuf = str_get(mstr);
+ if (optype == O_SHMREAD) {
+ if (mstr->str_cur < msize) {
+ STR_GROW(mstr, msize+1);
+ mbuf = str_get(mstr);
+ }
+ Copy(shm + mpos, mbuf, msize, char);
+ mstr->str_cur = msize;
+ mstr->str_ptr[msize] = '\0';
+ }
+ else {
+ int n;
+
+ if ((n = mstr->str_cur) > msize)
+ n = msize;
+ Copy(mbuf, shm + mpos, n, char);
+ if (n < msize)
+ memzero(shm + mpos + n, msize - n);
+ }
+ return shmdt(shm);
+#else
+ fatal("shm I/O not implemented");
+#endif
+}
+
+#endif /* SYSV IPC */
diff --git a/gnu/usr.bin/perl/perl/dolist.c b/gnu/usr.bin/perl/perl/dolist.c
new file mode 100644
index 0000000..f966479
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/dolist.c
@@ -0,0 +1,1973 @@
+/* $RCSfile: dolist.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:36 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: dolist.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:36 nate
+ * PERL!
+ *
+ * Revision 4.0.1.5 92/06/08 13:13:27 lwall
+ * patch20: g pattern modifer sometimes returned extra values
+ * patch20: m/$pattern/g didn't work
+ * patch20: pattern modifiers i and o didn't interact right
+ * patch20: @ in unpack failed too often
+ * patch20: Perl now distinguishes overlapped copies from non-overlapped
+ * patch20: slice on null list in scalar context returned random value
+ * patch20: splice with negative offset didn't work with $[ = 1
+ * patch20: fixed some memory leaks in splice
+ * patch20: scalar keys %array now counts keys for you
+ *
+ * Revision 4.0.1.4 91/11/11 16:33:19 lwall
+ * patch19: added little-endian pack/unpack options
+ * patch19: sort $subname was busted by changes in 4.018
+ *
+ * Revision 4.0.1.3 91/11/05 17:07:02 lwall
+ * patch11: prepared for ctype implementations that don't define isascii()
+ * patch11: /$foo/o optimizer could access deallocated data
+ * patch11: certain optimizations of //g in array context returned too many values
+ * patch11: regexp with no parens in array context returned wacky $`, $& and $'
+ * patch11: $' not set right on some //g
+ * patch11: added some support for 64-bit integers
+ * patch11: grep of a split lost its values
+ * patch11: added sort {} LIST
+ * patch11: multiple reallocations now avoided in 1 .. 100000
+ *
+ * Revision 4.0.1.2 91/06/10 01:22:15 lwall
+ * patch10: //g only worked first time through
+ *
+ * Revision 4.0.1.1 91/06/07 10:58:28 lwall
+ * patch4: new copyright notice
+ * patch4: added global modifier for pattern matches
+ * patch4: // wouldn't use previous pattern if it started with a null character
+ * patch4: //o and s///o now optimize themselves fully at runtime
+ * patch4: $` was busted inside s///
+ * patch4: caller($arg) didn't work except under debugger
+ *
+ * Revision 4.0 91/03/20 01:08:03 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+
+static int sortcmp();
+static int sortsub();
+
+#ifdef BUGGY_MSC
+ #pragma function(memcmp)
+#endif /* BUGGY_MSC */
+
+int
+do_match(str,arg,gimme,arglast)
+STR *str;
+register ARG *arg;
+int gimme;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register SPAT *spat = arg[2].arg_ptr.arg_spat;
+ register char *t;
+ register int sp = arglast[0] + 1;
+ STR *srchstr = st[sp];
+ register char *s = str_get(st[sp]);
+ char *strend = s + st[sp]->str_cur;
+ STR *tmpstr;
+ char *myhint = hint;
+ int global;
+ int safebase;
+ char *truebase = s;
+ register REGEXP *rx = spat->spat_regexp;
+
+ hint = Nullch;
+ if (!spat) {
+ if (gimme == G_ARRAY)
+ return --sp;
+ str_set(str,Yes);
+ STABSET(str);
+ st[sp] = str;
+ return sp;
+ }
+ global = spat->spat_flags & SPAT_GLOBAL;
+ safebase = (gimme == G_ARRAY) || global;
+ if (!s)
+ fatal("panic: do_match");
+ if (spat->spat_flags & SPAT_USED) {
+#ifdef DEBUGGING
+ if (debug & 8)
+ deb("2.SPAT USED\n");
+#endif
+ if (gimme == G_ARRAY)
+ return --sp;
+ str_set(str,No);
+ STABSET(str);
+ st[sp] = str;
+ return sp;
+ }
+ --sp;
+ if (spat->spat_runtime) {
+ nointrp = "|)";
+ sp = eval(spat->spat_runtime,G_SCALAR,sp);
+ st = stack->ary_array;
+ t = str_get(tmpstr = st[sp--]);
+ nointrp = "";
+#ifdef DEBUGGING
+ if (debug & 8)
+ deb("2.SPAT /%s/\n",t);
+#endif
+ if (!global && rx)
+ regfree(rx);
+ spat->spat_regexp = Null(REGEXP*); /* crucial if regcomp aborts */
+ spat->spat_regexp = regcomp(t,t+tmpstr->str_cur,
+ spat->spat_flags & SPAT_FOLD);
+ if (!spat->spat_regexp->prelen && lastspat)
+ spat = lastspat;
+ if (spat->spat_flags & SPAT_KEEP) {
+ if (!(spat->spat_flags & SPAT_FOLD))
+ scanconst(spat,spat->spat_regexp->precomp,
+ spat->spat_regexp->prelen);
+ if (spat->spat_runtime)
+ arg_free(spat->spat_runtime); /* it won't change, so */
+ spat->spat_runtime = Nullarg; /* no point compiling again */
+ hoistmust(spat);
+ if (curcmd->c_expr && (curcmd->c_flags & CF_OPTIMIZE) == CFT_EVAL) {
+ curcmd->c_flags &= ~CF_OPTIMIZE;
+ opt_arg(curcmd, 1, curcmd->c_type == C_EXPR);
+ }
+ }
+ if (global) {
+ if (rx) {
+ if (rx->startp[0]) {
+ s = rx->endp[0];
+ if (s == rx->startp[0])
+ s++;
+ if (s > strend) {
+ regfree(rx);
+ rx = spat->spat_regexp;
+ goto nope;
+ }
+ }
+ regfree(rx);
+ }
+ }
+ else if (!spat->spat_regexp->nparens)
+ gimme = G_SCALAR; /* accidental array context? */
+ rx = spat->spat_regexp;
+ if (regexec(rx, s, strend, s, 0,
+ srchstr->str_pok & SP_STUDIED ? srchstr : Nullstr,
+ safebase)) {
+ if (rx->subbase || global)
+ curspat = spat;
+ lastspat = spat;
+ goto gotcha;
+ }
+ else {
+ if (gimme == G_ARRAY)
+ return sp;
+ str_sset(str,&str_no);
+ STABSET(str);
+ st[++sp] = str;
+ return sp;
+ }
+ }
+ else {
+#ifdef DEBUGGING
+ if (debug & 8) {
+ char ch;
+
+ if (spat->spat_flags & SPAT_ONCE)
+ ch = '?';
+ else
+ ch = '/';
+ deb("2.SPAT %c%s%c\n",ch,rx->precomp,ch);
+ }
+#endif
+ if (!rx->prelen && lastspat) {
+ spat = lastspat;
+ rx = spat->spat_regexp;
+ }
+ t = s;
+ play_it_again:
+ if (global && rx->startp[0]) {
+ t = s = rx->endp[0];
+ if (s == rx->startp[0])
+ s++,t++;
+ if (s > strend)
+ goto nope;
+ }
+ if (myhint) {
+ if (myhint < s || myhint > strend)
+ fatal("panic: hint in do_match");
+ s = myhint;
+ if (rx->regback >= 0) {
+ s -= rx->regback;
+ if (s < t)
+ s = t;
+ }
+ else
+ s = t;
+ }
+ else if (spat->spat_short) {
+ if (spat->spat_flags & SPAT_SCANFIRST) {
+ if (srchstr->str_pok & SP_STUDIED) {
+ if (screamfirst[spat->spat_short->str_rare] < 0)
+ goto nope;
+ else if (!(s = screaminstr(srchstr,spat->spat_short)))
+ goto nope;
+ else if (spat->spat_flags & SPAT_ALL)
+ goto yup;
+ }
+#ifndef lint
+ else if (!(s = fbminstr((unsigned char*)s,
+ (unsigned char*)strend, spat->spat_short)))
+ goto nope;
+#endif
+ else if (spat->spat_flags & SPAT_ALL)
+ goto yup;
+ if (s && rx->regback >= 0) {
+ ++spat->spat_short->str_u.str_useful;
+ s -= rx->regback;
+ if (s < t)
+ s = t;
+ }
+ else
+ s = t;
+ }
+ else if (!multiline && (*spat->spat_short->str_ptr != *s ||
+ bcmp(spat->spat_short->str_ptr, s, spat->spat_slen) ))
+ goto nope;
+ if (--spat->spat_short->str_u.str_useful < 0) {
+ str_free(spat->spat_short);
+ spat->spat_short = Nullstr; /* opt is being useless */
+ }
+ }
+ if (!rx->nparens && !global) {
+ gimme = G_SCALAR; /* accidental array context? */
+ safebase = FALSE;
+ }
+ if (regexec(rx, s, strend, truebase, 0,
+ srchstr->str_pok & SP_STUDIED ? srchstr : Nullstr,
+ safebase)) {
+ if (rx->subbase || global)
+ curspat = spat;
+ lastspat = spat;
+ if (spat->spat_flags & SPAT_ONCE)
+ spat->spat_flags |= SPAT_USED;
+ goto gotcha;
+ }
+ else {
+ if (global)
+ rx->startp[0] = Nullch;
+ if (gimme == G_ARRAY)
+ return sp;
+ str_sset(str,&str_no);
+ STABSET(str);
+ st[++sp] = str;
+ return sp;
+ }
+ }
+ /*NOTREACHED*/
+
+ gotcha:
+ if (gimme == G_ARRAY) {
+ int iters, i, len;
+
+ iters = rx->nparens;
+ if (global && !iters)
+ i = 1;
+ else
+ i = 0;
+ if (sp + iters + i >= stack->ary_max) {
+ astore(stack,sp + iters + i, Nullstr);
+ st = stack->ary_array; /* possibly realloced */
+ }
+
+ for (i = !i; i <= iters; i++) {
+ st[++sp] = str_mortal(&str_no);
+ /*SUPPRESS 560*/
+ if (s = rx->startp[i]) {
+ len = rx->endp[i] - s;
+ if (len > 0)
+ str_nset(st[sp],s,len);
+ }
+ }
+ if (global) {
+ truebase = rx->subbeg;
+ goto play_it_again;
+ }
+ return sp;
+ }
+ else {
+ str_sset(str,&str_yes);
+ STABSET(str);
+ st[++sp] = str;
+ return sp;
+ }
+
+yup:
+ ++spat->spat_short->str_u.str_useful;
+ lastspat = spat;
+ if (spat->spat_flags & SPAT_ONCE)
+ spat->spat_flags |= SPAT_USED;
+ if (global) {
+ rx->subbeg = t;
+ rx->subend = strend;
+ rx->startp[0] = s;
+ rx->endp[0] = s + spat->spat_short->str_cur;
+ curspat = spat;
+ goto gotcha;
+ }
+ if (sawampersand) {
+ char *tmps;
+
+ if (rx->subbase)
+ Safefree(rx->subbase);
+ tmps = rx->subbase = nsavestr(t,strend-t);
+ rx->subbeg = tmps;
+ rx->subend = tmps + (strend-t);
+ tmps = rx->startp[0] = tmps + (s - t);
+ rx->endp[0] = tmps + spat->spat_short->str_cur;
+ curspat = spat;
+ }
+ str_sset(str,&str_yes);
+ STABSET(str);
+ st[++sp] = str;
+ return sp;
+
+nope:
+ rx->startp[0] = Nullch;
+ if (spat->spat_short)
+ ++spat->spat_short->str_u.str_useful;
+ if (gimme == G_ARRAY)
+ return sp;
+ str_sset(str,&str_no);
+ STABSET(str);
+ st[++sp] = str;
+ return sp;
+}
+
+#ifdef BUGGY_MSC
+ #pragma intrinsic(memcmp)
+#endif /* BUGGY_MSC */
+
+int
+do_split(str,spat,limit,gimme,arglast)
+STR *str;
+register SPAT *spat;
+register int limit;
+int gimme;
+int *arglast;
+{
+ register ARRAY *ary = stack;
+ STR **st = ary->ary_array;
+ register int sp = arglast[0] + 1;
+ register char *s = str_get(st[sp]);
+ char *strend = s + st[sp--]->str_cur;
+ register STR *dstr;
+ register char *m;
+ int iters = 0;
+ int maxiters = (strend - s) + 10;
+ int i;
+ char *orig;
+ int origlimit = limit;
+ int realarray = 0;
+
+ if (!spat || !s)
+ fatal("panic: do_split");
+ else if (spat->spat_runtime) {
+ nointrp = "|)";
+ sp = eval(spat->spat_runtime,G_SCALAR,sp);
+ st = stack->ary_array;
+ m = str_get(dstr = st[sp--]);
+ nointrp = "";
+ if (*m == ' ' && dstr->str_cur == 1) {
+ str_set(dstr,"\\s+");
+ m = dstr->str_ptr;
+ spat->spat_flags |= SPAT_SKIPWHITE;
+ }
+ if (spat->spat_regexp) {
+ regfree(spat->spat_regexp);
+ spat->spat_regexp = Null(REGEXP*); /* avoid possible double free */
+ }
+ spat->spat_regexp = regcomp(m,m+dstr->str_cur,
+ spat->spat_flags & SPAT_FOLD);
+ if (spat->spat_flags & SPAT_KEEP ||
+ (spat->spat_runtime->arg_type == O_ITEM &&
+ (spat->spat_runtime[1].arg_type & A_MASK) == A_SINGLE) ) {
+ arg_free(spat->spat_runtime); /* it won't change, so */
+ spat->spat_runtime = Nullarg; /* no point compiling again */
+ }
+ }
+#ifdef DEBUGGING
+ if (debug & 8) {
+ deb("2.SPAT /%s/\n",spat->spat_regexp->precomp);
+ }
+#endif
+ ary = stab_xarray(spat->spat_repl[1].arg_ptr.arg_stab);
+ if (ary && (gimme != G_ARRAY || (spat->spat_flags & SPAT_ONCE))) {
+ realarray = 1;
+ if (!(ary->ary_flags & ARF_REAL)) {
+ ary->ary_flags |= ARF_REAL;
+ for (i = ary->ary_fill; i >= 0; i--)
+ ary->ary_array[i] = Nullstr; /* don't free mere refs */
+ }
+ ary->ary_fill = -1;
+ sp = -1; /* temporarily switch stacks */
+ }
+ else
+ ary = stack;
+ orig = s;
+ if (spat->spat_flags & SPAT_SKIPWHITE) {
+ while (isSPACE(*s))
+ s++;
+ }
+ if (!limit)
+ limit = maxiters + 2;
+ if (strEQ("\\s+",spat->spat_regexp->precomp)) {
+ while (--limit) {
+ /*SUPPRESS 530*/
+ for (m = s; m < strend && !isSPACE(*m); m++) ;
+ if (m >= strend)
+ break;
+ dstr = Str_new(30,m-s);
+ str_nset(dstr,s,m-s);
+ if (!realarray)
+ str_2mortal(dstr);
+ (void)astore(ary, ++sp, dstr);
+ /*SUPPRESS 530*/
+ for (s = m + 1; s < strend && isSPACE(*s); s++) ;
+ }
+ }
+ else if (strEQ("^",spat->spat_regexp->precomp)) {
+ while (--limit) {
+ /*SUPPRESS 530*/
+ for (m = s; m < strend && *m != '\n'; m++) ;
+ m++;
+ if (m >= strend)
+ break;
+ dstr = Str_new(30,m-s);
+ str_nset(dstr,s,m-s);
+ if (!realarray)
+ str_2mortal(dstr);
+ (void)astore(ary, ++sp, dstr);
+ s = m;
+ }
+ }
+ else if (spat->spat_short) {
+ i = spat->spat_short->str_cur;
+ if (i == 1) {
+ int fold = (spat->spat_flags & SPAT_FOLD);
+
+ i = *spat->spat_short->str_ptr;
+ if (fold && isUPPER(i))
+ i = tolower(i);
+ while (--limit) {
+ if (fold) {
+ for ( m = s;
+ m < strend && *m != i &&
+ (!isUPPER(*m) || tolower(*m) != i);
+ m++) /*SUPPRESS 530*/
+ ;
+ }
+ else /*SUPPRESS 530*/
+ for (m = s; m < strend && *m != i; m++) ;
+ if (m >= strend)
+ break;
+ dstr = Str_new(30,m-s);
+ str_nset(dstr,s,m-s);
+ if (!realarray)
+ str_2mortal(dstr);
+ (void)astore(ary, ++sp, dstr);
+ s = m + 1;
+ }
+ }
+ else {
+#ifndef lint
+ while (s < strend && --limit &&
+ (m=fbminstr((unsigned char*)s, (unsigned char*)strend,
+ spat->spat_short)) )
+#endif
+ {
+ dstr = Str_new(31,m-s);
+ str_nset(dstr,s,m-s);
+ if (!realarray)
+ str_2mortal(dstr);
+ (void)astore(ary, ++sp, dstr);
+ s = m + i;
+ }
+ }
+ }
+ else {
+ maxiters += (strend - s) * spat->spat_regexp->nparens;
+ while (s < strend && --limit &&
+ regexec(spat->spat_regexp, s, strend, orig, 1, Nullstr, TRUE) ) {
+ if (spat->spat_regexp->subbase
+ && spat->spat_regexp->subbase != orig) {
+ m = s;
+ s = orig;
+ orig = spat->spat_regexp->subbase;
+ s = orig + (m - s);
+ strend = s + (strend - m);
+ }
+ m = spat->spat_regexp->startp[0];
+ dstr = Str_new(32,m-s);
+ str_nset(dstr,s,m-s);
+ if (!realarray)
+ str_2mortal(dstr);
+ (void)astore(ary, ++sp, dstr);
+ if (spat->spat_regexp->nparens) {
+ for (i = 1; i <= spat->spat_regexp->nparens; i++) {
+ s = spat->spat_regexp->startp[i];
+ m = spat->spat_regexp->endp[i];
+ dstr = Str_new(33,m-s);
+ str_nset(dstr,s,m-s);
+ if (!realarray)
+ str_2mortal(dstr);
+ (void)astore(ary, ++sp, dstr);
+ }
+ }
+ s = spat->spat_regexp->endp[0];
+ }
+ }
+ if (realarray)
+ iters = sp + 1;
+ else
+ iters = sp - arglast[0];
+ if (iters > maxiters)
+ fatal("Split loop");
+ if (s < strend || origlimit) { /* keep field after final delim? */
+ dstr = Str_new(34,strend-s);
+ str_nset(dstr,s,strend-s);
+ if (!realarray)
+ str_2mortal(dstr);
+ (void)astore(ary, ++sp, dstr);
+ iters++;
+ }
+ else {
+#ifndef I286x
+ while (iters > 0 && ary->ary_array[sp]->str_cur == 0)
+ iters--,sp--;
+#else
+ char *zaps;
+ int zapb;
+
+ if (iters > 0) {
+ zaps = str_get(afetch(ary,sp,FALSE));
+ zapb = (int) *zaps;
+ }
+
+ while (iters > 0 && (!zapb)) {
+ iters--,sp--;
+ if (iters > 0) {
+ zaps = str_get(afetch(ary,iters-1,FALSE));
+ zapb = (int) *zaps;
+ }
+ }
+#endif
+ }
+ if (realarray) {
+ ary->ary_fill = sp;
+ if (gimme == G_ARRAY) {
+ sp++;
+ astore(stack, arglast[0] + 1 + sp, Nullstr);
+ Copy(ary->ary_array, stack->ary_array + arglast[0] + 1, sp, STR*);
+ return arglast[0] + sp;
+ }
+ }
+ else {
+ if (gimme == G_ARRAY)
+ return sp;
+ }
+ sp = arglast[0] + 1;
+ str_numset(str,(double)iters);
+ STABSET(str);
+ st[sp] = str;
+ return sp;
+}
+
+int
+do_unpack(str,gimme,arglast)
+STR *str;
+int gimme;
+int *arglast;
+{
+ STR **st = stack->ary_array;
+ register int sp = arglast[0] + 1;
+ register char *pat = str_get(st[sp++]);
+ register char *s = str_get(st[sp]);
+ char *strend = s + st[sp--]->str_cur;
+ char *strbeg = s;
+ register char *patend = pat + st[sp]->str_cur;
+ int datumtype;
+ register int len;
+ register int bits;
+
+ /* These must not be in registers: */
+ short ashort;
+ int aint;
+ long along;
+#ifdef QUAD
+ quad aquad;
+#endif
+ unsigned short aushort;
+ unsigned int auint;
+ unsigned long aulong;
+#ifdef QUAD
+ unsigned quad auquad;
+#endif
+ char *aptr;
+ float afloat;
+ double adouble;
+ int checksum = 0;
+ unsigned long culong;
+ double cdouble;
+
+ if (gimme != G_ARRAY) { /* arrange to do first one only */
+ /*SUPPRESS 530*/
+ for (patend = pat; !isALPHA(*patend) || *patend == 'x'; patend++) ;
+ if (index("aAbBhH", *patend) || *pat == '%') {
+ patend++;
+ while (isDIGIT(*patend) || *patend == '*')
+ patend++;
+ }
+ else
+ patend++;
+ }
+ sp--;
+ while (pat < patend) {
+ reparse:
+ datumtype = *pat++;
+ if (pat >= patend)
+ len = 1;
+ else if (*pat == '*') {
+ len = strend - strbeg; /* long enough */
+ pat++;
+ }
+ else if (isDIGIT(*pat)) {
+ len = *pat++ - '0';
+ while (isDIGIT(*pat))
+ len = (len * 10) + (*pat++ - '0');
+ }
+ else
+ len = (datumtype != '@');
+ switch(datumtype) {
+ default:
+ break;
+ case '%':
+ if (len == 1 && pat[-1] != '1')
+ len = 16;
+ checksum = len;
+ culong = 0;
+ cdouble = 0;
+ if (pat < patend)
+ goto reparse;
+ break;
+ case '@':
+ if (len > strend - strbeg)
+ fatal("@ outside of string");
+ s = strbeg + len;
+ break;
+ case 'X':
+ if (len > s - strbeg)
+ fatal("X outside of string");
+ s -= len;
+ break;
+ case 'x':
+ if (len > strend - s)
+ fatal("x outside of string");
+ s += len;
+ break;
+ case 'A':
+ case 'a':
+ if (len > strend - s)
+ len = strend - s;
+ if (checksum)
+ goto uchar_checksum;
+ str = Str_new(35,len);
+ str_nset(str,s,len);
+ s += len;
+ if (datumtype == 'A') {
+ aptr = s; /* borrow register */
+ s = str->str_ptr + len - 1;
+ while (s >= str->str_ptr && (!*s || isSPACE(*s)))
+ s--;
+ *++s = '\0';
+ str->str_cur = s - str->str_ptr;
+ s = aptr; /* unborrow register */
+ }
+ (void)astore(stack, ++sp, str_2mortal(str));
+ break;
+ case 'B':
+ case 'b':
+ if (pat[-1] == '*' || len > (strend - s) * 8)
+ len = (strend - s) * 8;
+ str = Str_new(35, len + 1);
+ str->str_cur = len;
+ str->str_pok = 1;
+ aptr = pat; /* borrow register */
+ pat = str->str_ptr;
+ if (datumtype == 'b') {
+ aint = len;
+ for (len = 0; len < aint; len++) {
+ if (len & 7) /*SUPPRESS 595*/
+ bits >>= 1;
+ else
+ bits = *s++;
+ *pat++ = '0' + (bits & 1);
+ }
+ }
+ else {
+ aint = len;
+ for (len = 0; len < aint; len++) {
+ if (len & 7)
+ bits <<= 1;
+ else
+ bits = *s++;
+ *pat++ = '0' + ((bits & 128) != 0);
+ }
+ }
+ *pat = '\0';
+ pat = aptr; /* unborrow register */
+ (void)astore(stack, ++sp, str_2mortal(str));
+ break;
+ case 'H':
+ case 'h':
+ if (pat[-1] == '*' || len > (strend - s) * 2)
+ len = (strend - s) * 2;
+ str = Str_new(35, len + 1);
+ str->str_cur = len;
+ str->str_pok = 1;
+ aptr = pat; /* borrow register */
+ pat = str->str_ptr;
+ if (datumtype == 'h') {
+ aint = len;
+ for (len = 0; len < aint; len++) {
+ if (len & 1)
+ bits >>= 4;
+ else
+ bits = *s++;
+ *pat++ = hexdigit[bits & 15];
+ }
+ }
+ else {
+ aint = len;
+ for (len = 0; len < aint; len++) {
+ if (len & 1)
+ bits <<= 4;
+ else
+ bits = *s++;
+ *pat++ = hexdigit[(bits >> 4) & 15];
+ }
+ }
+ *pat = '\0';
+ pat = aptr; /* unborrow register */
+ (void)astore(stack, ++sp, str_2mortal(str));
+ break;
+ case 'c':
+ if (len > strend - s)
+ len = strend - s;
+ if (checksum) {
+ while (len-- > 0) {
+ aint = *s++;
+ if (aint >= 128) /* fake up signed chars */
+ aint -= 256;
+ culong += aint;
+ }
+ }
+ else {
+ while (len-- > 0) {
+ aint = *s++;
+ if (aint >= 128) /* fake up signed chars */
+ aint -= 256;
+ str = Str_new(36,0);
+ str_numset(str,(double)aint);
+ (void)astore(stack, ++sp, str_2mortal(str));
+ }
+ }
+ break;
+ case 'C':
+ if (len > strend - s)
+ len = strend - s;
+ if (checksum) {
+ uchar_checksum:
+ while (len-- > 0) {
+ auint = *s++ & 255;
+ culong += auint;
+ }
+ }
+ else {
+ while (len-- > 0) {
+ auint = *s++ & 255;
+ str = Str_new(37,0);
+ str_numset(str,(double)auint);
+ (void)astore(stack, ++sp, str_2mortal(str));
+ }
+ }
+ break;
+ case 's':
+ along = (strend - s) / sizeof(short);
+ if (len > along)
+ len = along;
+ if (checksum) {
+ while (len-- > 0) {
+ Copy(s,&ashort,1,short);
+ s += sizeof(short);
+ culong += ashort;
+ }
+ }
+ else {
+ while (len-- > 0) {
+ Copy(s,&ashort,1,short);
+ s += sizeof(short);
+ str = Str_new(38,0);
+ str_numset(str,(double)ashort);
+ (void)astore(stack, ++sp, str_2mortal(str));
+ }
+ }
+ break;
+ case 'v':
+ case 'n':
+ case 'S':
+ along = (strend - s) / sizeof(unsigned short);
+ if (len > along)
+ len = along;
+ if (checksum) {
+ while (len-- > 0) {
+ Copy(s,&aushort,1,unsigned short);
+ s += sizeof(unsigned short);
+#ifdef HAS_NTOHS
+ if (datumtype == 'n')
+ aushort = ntohs(aushort);
+#endif
+#ifdef HAS_VTOHS
+ if (datumtype == 'v')
+ aushort = vtohs(aushort);
+#endif
+ culong += aushort;
+ }
+ }
+ else {
+ while (len-- > 0) {
+ Copy(s,&aushort,1,unsigned short);
+ s += sizeof(unsigned short);
+ str = Str_new(39,0);
+#ifdef HAS_NTOHS
+ if (datumtype == 'n')
+ aushort = ntohs(aushort);
+#endif
+#ifdef HAS_VTOHS
+ if (datumtype == 'v')
+ aushort = vtohs(aushort);
+#endif
+ str_numset(str,(double)aushort);
+ (void)astore(stack, ++sp, str_2mortal(str));
+ }
+ }
+ break;
+ case 'i':
+ along = (strend - s) / sizeof(int);
+ if (len > along)
+ len = along;
+ if (checksum) {
+ while (len-- > 0) {
+ Copy(s,&aint,1,int);
+ s += sizeof(int);
+ if (checksum > 32)
+ cdouble += (double)aint;
+ else
+ culong += aint;
+ }
+ }
+ else {
+ while (len-- > 0) {
+ Copy(s,&aint,1,int);
+ s += sizeof(int);
+ str = Str_new(40,0);
+ str_numset(str,(double)aint);
+ (void)astore(stack, ++sp, str_2mortal(str));
+ }
+ }
+ break;
+ case 'I':
+ along = (strend - s) / sizeof(unsigned int);
+ if (len > along)
+ len = along;
+ if (checksum) {
+ while (len-- > 0) {
+ Copy(s,&auint,1,unsigned int);
+ s += sizeof(unsigned int);
+ if (checksum > 32)
+ cdouble += (double)auint;
+ else
+ culong += auint;
+ }
+ }
+ else {
+ while (len-- > 0) {
+ Copy(s,&auint,1,unsigned int);
+ s += sizeof(unsigned int);
+ str = Str_new(41,0);
+ str_numset(str,(double)auint);
+ (void)astore(stack, ++sp, str_2mortal(str));
+ }
+ }
+ break;
+ case 'l':
+ along = (strend - s) / sizeof(long);
+ if (len > along)
+ len = along;
+ if (checksum) {
+ while (len-- > 0) {
+ Copy(s,&along,1,long);
+ s += sizeof(long);
+ if (checksum > 32)
+ cdouble += (double)along;
+ else
+ culong += along;
+ }
+ }
+ else {
+ while (len-- > 0) {
+ Copy(s,&along,1,long);
+ s += sizeof(long);
+ str = Str_new(42,0);
+ str_numset(str,(double)along);
+ (void)astore(stack, ++sp, str_2mortal(str));
+ }
+ }
+ break;
+ case 'V':
+ case 'N':
+ case 'L':
+ along = (strend - s) / sizeof(unsigned long);
+ if (len > along)
+ len = along;
+ if (checksum) {
+ while (len-- > 0) {
+ Copy(s,&aulong,1,unsigned long);
+ s += sizeof(unsigned long);
+#ifdef HAS_NTOHL
+ if (datumtype == 'N')
+ aulong = ntohl(aulong);
+#endif
+#ifdef HAS_VTOHL
+ if (datumtype == 'V')
+ aulong = vtohl(aulong);
+#endif
+ if (checksum > 32)
+ cdouble += (double)aulong;
+ else
+ culong += aulong;
+ }
+ }
+ else {
+ while (len-- > 0) {
+ Copy(s,&aulong,1,unsigned long);
+ s += sizeof(unsigned long);
+ str = Str_new(43,0);
+#ifdef HAS_NTOHL
+ if (datumtype == 'N')
+ aulong = ntohl(aulong);
+#endif
+#ifdef HAS_VTOHL
+ if (datumtype == 'V')
+ aulong = vtohl(aulong);
+#endif
+ str_numset(str,(double)aulong);
+ (void)astore(stack, ++sp, str_2mortal(str));
+ }
+ }
+ break;
+ case 'p':
+ along = (strend - s) / sizeof(char*);
+ if (len > along)
+ len = along;
+ while (len-- > 0) {
+ if (sizeof(char*) > strend - s)
+ break;
+ else {
+ Copy(s,&aptr,1,char*);
+ s += sizeof(char*);
+ }
+ str = Str_new(44,0);
+ if (aptr)
+ str_set(str,aptr);
+ (void)astore(stack, ++sp, str_2mortal(str));
+ }
+ break;
+#ifdef QUAD
+ case 'q':
+ while (len-- > 0) {
+ if (s + sizeof(quad) > strend)
+ aquad = 0;
+ else {
+ Copy(s,&aquad,1,quad);
+ s += sizeof(quad);
+ }
+ str = Str_new(42,0);
+ str_numset(str,(double)aquad);
+ (void)astore(stack, ++sp, str_2mortal(str));
+ }
+ break;
+ case 'Q':
+ while (len-- > 0) {
+ if (s + sizeof(unsigned quad) > strend)
+ auquad = 0;
+ else {
+ Copy(s,&auquad,1,unsigned quad);
+ s += sizeof(unsigned quad);
+ }
+ str = Str_new(43,0);
+ str_numset(str,(double)auquad);
+ (void)astore(stack, ++sp, str_2mortal(str));
+ }
+ break;
+#endif
+ /* float and double added gnb@melba.bby.oz.au 22/11/89 */
+ case 'f':
+ case 'F':
+ along = (strend - s) / sizeof(float);
+ if (len > along)
+ len = along;
+ if (checksum) {
+ while (len-- > 0) {
+ Copy(s, &afloat,1, float);
+ s += sizeof(float);
+ cdouble += afloat;
+ }
+ }
+ else {
+ while (len-- > 0) {
+ Copy(s, &afloat,1, float);
+ s += sizeof(float);
+ str = Str_new(47, 0);
+ str_numset(str, (double)afloat);
+ (void)astore(stack, ++sp, str_2mortal(str));
+ }
+ }
+ break;
+ case 'd':
+ case 'D':
+ along = (strend - s) / sizeof(double);
+ if (len > along)
+ len = along;
+ if (checksum) {
+ while (len-- > 0) {
+ Copy(s, &adouble,1, double);
+ s += sizeof(double);
+ cdouble += adouble;
+ }
+ }
+ else {
+ while (len-- > 0) {
+ Copy(s, &adouble,1, double);
+ s += sizeof(double);
+ str = Str_new(48, 0);
+ str_numset(str, (double)adouble);
+ (void)astore(stack, ++sp, str_2mortal(str));
+ }
+ }
+ break;
+ case 'u':
+ along = (strend - s) * 3 / 4;
+ str = Str_new(42,along);
+ while (s < strend && *s > ' ' && *s < 'a') {
+ int a,b,c,d;
+ char hunk[4];
+
+ hunk[3] = '\0';
+ len = (*s++ - ' ') & 077;
+ while (len > 0) {
+ if (s < strend && *s >= ' ')
+ a = (*s++ - ' ') & 077;
+ else
+ a = 0;
+ if (s < strend && *s >= ' ')
+ b = (*s++ - ' ') & 077;
+ else
+ b = 0;
+ if (s < strend && *s >= ' ')
+ c = (*s++ - ' ') & 077;
+ else
+ c = 0;
+ if (s < strend && *s >= ' ')
+ d = (*s++ - ' ') & 077;
+ else
+ d = 0;
+ hunk[0] = a << 2 | b >> 4;
+ hunk[1] = b << 4 | c >> 2;
+ hunk[2] = c << 6 | d;
+ str_ncat(str,hunk, len > 3 ? 3 : len);
+ len -= 3;
+ }
+ if (*s == '\n')
+ s++;
+ else if (s[1] == '\n') /* possible checksum byte */
+ s += 2;
+ }
+ (void)astore(stack, ++sp, str_2mortal(str));
+ break;
+ }
+ if (checksum) {
+ str = Str_new(42,0);
+ if (index("fFdD", datumtype) ||
+ (checksum > 32 && index("iIlLN", datumtype)) ) {
+ double modf();
+ double trouble;
+
+ adouble = 1.0;
+ while (checksum >= 16) {
+ checksum -= 16;
+ adouble *= 65536.0;
+ }
+ while (checksum >= 4) {
+ checksum -= 4;
+ adouble *= 16.0;
+ }
+ while (checksum--)
+ adouble *= 2.0;
+ along = (1 << checksum) - 1;
+ while (cdouble < 0.0)
+ cdouble += adouble;
+ cdouble = modf(cdouble / adouble, &trouble) * adouble;
+ str_numset(str,cdouble);
+ }
+ else {
+ if (checksum < 32) {
+ along = (1 << checksum) - 1;
+ culong &= (unsigned long)along;
+ }
+ str_numset(str,(double)culong);
+ }
+ (void)astore(stack, ++sp, str_2mortal(str));
+ checksum = 0;
+ }
+ }
+ return sp;
+}
+
+int
+do_slice(stab,str,numarray,lval,gimme,arglast)
+STAB *stab;
+STR *str;
+int numarray;
+int lval;
+int gimme;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register int max = arglast[2];
+ register char *tmps;
+ register int len;
+ register int magic = 0;
+ register ARRAY *ary;
+ register HASH *hash;
+ int oldarybase = arybase;
+
+ if (numarray) {
+ if (numarray == 2) { /* a slice of a LIST */
+ ary = stack;
+ ary->ary_fill = arglast[3];
+ arybase -= max + 1;
+ st[sp] = str; /* make stack size available */
+ str_numset(str,(double)(sp - 1));
+ }
+ else
+ ary = stab_array(stab); /* a slice of an array */
+ }
+ else {
+ if (lval) {
+ if (stab == envstab)
+ magic = 'E';
+ else if (stab == sigstab)
+ magic = 'S';
+#ifdef SOME_DBM
+ else if (stab_hash(stab)->tbl_dbm)
+ magic = 'D';
+#endif /* SOME_DBM */
+ }
+ hash = stab_hash(stab); /* a slice of an associative array */
+ }
+
+ if (gimme == G_ARRAY) {
+ if (numarray) {
+ while (sp < max) {
+ if (st[++sp]) {
+ st[sp-1] = afetch(ary,
+ ((int)str_gnum(st[sp])) - arybase, lval);
+ }
+ else
+ st[sp-1] = &str_undef;
+ }
+ }
+ else {
+ while (sp < max) {
+ if (st[++sp]) {
+ tmps = str_get(st[sp]);
+ len = st[sp]->str_cur;
+ st[sp-1] = hfetch(hash,tmps,len, lval);
+ if (magic)
+ str_magic(st[sp-1],stab,magic,tmps,len);
+ }
+ else
+ st[sp-1] = &str_undef;
+ }
+ }
+ sp--;
+ }
+ else {
+ if (sp == max)
+ st[sp] = &str_undef;
+ else if (numarray) {
+ if (st[max])
+ st[sp] = afetch(ary,
+ ((int)str_gnum(st[max])) - arybase, lval);
+ else
+ st[sp] = &str_undef;
+ }
+ else {
+ if (st[max]) {
+ tmps = str_get(st[max]);
+ len = st[max]->str_cur;
+ st[sp] = hfetch(hash,tmps,len, lval);
+ if (magic)
+ str_magic(st[sp],stab,magic,tmps,len);
+ }
+ else
+ st[sp] = &str_undef;
+ }
+ }
+ arybase = oldarybase;
+ return sp;
+}
+
+int
+do_splice(ary,gimme,arglast)
+register ARRAY *ary;
+int gimme;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ int max = arglast[2] + 1;
+ register STR **src;
+ register STR **dst;
+ register int i;
+ register int offset;
+ register int length;
+ int newlen;
+ int after;
+ int diff;
+ STR **tmparyval;
+
+ if (++sp < max) {
+ offset = (int)str_gnum(st[sp]);
+ if (offset < 0)
+ offset += ary->ary_fill + 1;
+ else
+ offset -= arybase;
+ if (++sp < max) {
+ length = (int)str_gnum(st[sp++]);
+ if (length < 0)
+ length = 0;
+ }
+ else
+ length = ary->ary_max + 1; /* close enough to infinity */
+ }
+ else {
+ offset = 0;
+ length = ary->ary_max + 1;
+ }
+ if (offset < 0) {
+ length += offset;
+ offset = 0;
+ if (length < 0)
+ length = 0;
+ }
+ if (offset > ary->ary_fill + 1)
+ offset = ary->ary_fill + 1;
+ after = ary->ary_fill + 1 - (offset + length);
+ if (after < 0) { /* not that much array */
+ length += after; /* offset+length now in array */
+ after = 0;
+ if (!ary->ary_alloc) {
+ afill(ary,0);
+ afill(ary,-1);
+ }
+ }
+
+ /* At this point, sp .. max-1 is our new LIST */
+
+ newlen = max - sp;
+ diff = newlen - length;
+
+ if (diff < 0) { /* shrinking the area */
+ if (newlen) {
+ New(451, tmparyval, newlen, STR*); /* so remember insertion */
+ Copy(st+sp, tmparyval, newlen, STR*);
+ }
+
+ sp = arglast[0] + 1;
+ if (gimme == G_ARRAY) { /* copy return vals to stack */
+ if (sp + length >= stack->ary_max) {
+ astore(stack,sp + length, Nullstr);
+ st = stack->ary_array;
+ }
+ Copy(ary->ary_array+offset, st+sp, length, STR*);
+ if (ary->ary_flags & ARF_REAL) {
+ for (i = length, dst = st+sp; i; i--)
+ str_2mortal(*dst++); /* free them eventualy */
+ }
+ sp += length - 1;
+ }
+ else {
+ st[sp] = ary->ary_array[offset+length-1];
+ if (ary->ary_flags & ARF_REAL) {
+ str_2mortal(st[sp]);
+ for (i = length - 1, dst = &ary->ary_array[offset]; i > 0; i--)
+ str_free(*dst++); /* free them now */
+ }
+ }
+ ary->ary_fill += diff;
+
+ /* pull up or down? */
+
+ if (offset < after) { /* easier to pull up */
+ if (offset) { /* esp. if nothing to pull */
+ src = &ary->ary_array[offset-1];
+ dst = src - diff; /* diff is negative */
+ for (i = offset; i > 0; i--) /* can't trust Copy */
+ *dst-- = *src--;
+ }
+ Zero(ary->ary_array, -diff, STR*);
+ ary->ary_array -= diff; /* diff is negative */
+ ary->ary_max += diff;
+ }
+ else {
+ if (after) { /* anything to pull down? */
+ src = ary->ary_array + offset + length;
+ dst = src + diff; /* diff is negative */
+ Move(src, dst, after, STR*);
+ }
+ Zero(&ary->ary_array[ary->ary_fill+1], -diff, STR*);
+ /* avoid later double free */
+ }
+ if (newlen) {
+ for (src = tmparyval, dst = ary->ary_array + offset;
+ newlen; newlen--) {
+ *dst = Str_new(46,0);
+ str_sset(*dst++,*src++);
+ }
+ Safefree(tmparyval);
+ }
+ }
+ else { /* no, expanding (or same) */
+ if (length) {
+ New(452, tmparyval, length, STR*); /* so remember deletion */
+ Copy(ary->ary_array+offset, tmparyval, length, STR*);
+ }
+
+ if (diff > 0) { /* expanding */
+
+ /* push up or down? */
+
+ if (offset < after && diff <= ary->ary_array - ary->ary_alloc) {
+ if (offset) {
+ src = ary->ary_array;
+ dst = src - diff;
+ Move(src, dst, offset, STR*);
+ }
+ ary->ary_array -= diff; /* diff is positive */
+ ary->ary_max += diff;
+ ary->ary_fill += diff;
+ }
+ else {
+ if (ary->ary_fill + diff >= ary->ary_max) /* oh, well */
+ astore(ary, ary->ary_fill + diff, Nullstr);
+ else
+ ary->ary_fill += diff;
+ dst = ary->ary_array + ary->ary_fill;
+ for (i = diff; i > 0; i--) {
+ if (*dst) /* str was hanging around */
+ str_free(*dst); /* after $#foo */
+ dst--;
+ }
+ if (after) {
+ dst = ary->ary_array + ary->ary_fill;
+ src = dst - diff;
+ for (i = after; i; i--) {
+ *dst-- = *src--;
+ }
+ }
+ }
+ }
+
+ for (src = st+sp, dst = ary->ary_array + offset; newlen; newlen--) {
+ *dst = Str_new(46,0);
+ str_sset(*dst++,*src++);
+ }
+ sp = arglast[0] + 1;
+ if (gimme == G_ARRAY) { /* copy return vals to stack */
+ if (length) {
+ Copy(tmparyval, st+sp, length, STR*);
+ if (ary->ary_flags & ARF_REAL) {
+ for (i = length, dst = st+sp; i; i--)
+ str_2mortal(*dst++); /* free them eventualy */
+ }
+ Safefree(tmparyval);
+ }
+ sp += length - 1;
+ }
+ else if (length--) {
+ st[sp] = tmparyval[length];
+ if (ary->ary_flags & ARF_REAL) {
+ str_2mortal(st[sp]);
+ while (length-- > 0)
+ str_free(tmparyval[length]);
+ }
+ Safefree(tmparyval);
+ }
+ else
+ st[sp] = &str_undef;
+ }
+ return sp;
+}
+
+int
+do_grep(arg,str,gimme,arglast)
+register ARG *arg;
+STR *str;
+int gimme;
+int *arglast;
+{
+ STR **st = stack->ary_array;
+ register int dst = arglast[1];
+ register int src = dst + 1;
+ register int sp = arglast[2];
+ register int i = sp - arglast[1];
+ int oldsave = savestack->ary_fill;
+ SPAT *oldspat = curspat;
+ int oldtmps_base = tmps_base;
+
+ savesptr(&stab_val(defstab));
+ tmps_base = tmps_max;
+ if ((arg[1].arg_type & A_MASK) != A_EXPR) {
+ arg[1].arg_type &= A_MASK;
+ dehoist(arg,1);
+ arg[1].arg_type |= A_DONT;
+ }
+ arg = arg[1].arg_ptr.arg_arg;
+ while (i-- > 0) {
+ if (st[src]) {
+ st[src]->str_pok &= ~SP_TEMP;
+ stab_val(defstab) = st[src];
+ }
+ else
+ stab_val(defstab) = str_mortal(&str_undef);
+ (void)eval(arg,G_SCALAR,sp);
+ st = stack->ary_array;
+ if (str_true(st[sp+1]))
+ st[dst++] = st[src];
+ src++;
+ curspat = oldspat;
+ }
+ restorelist(oldsave);
+ tmps_base = oldtmps_base;
+ if (gimme != G_ARRAY) {
+ str_numset(str,(double)(dst - arglast[1]));
+ STABSET(str);
+ st[arglast[0]+1] = str;
+ return arglast[0]+1;
+ }
+ return arglast[0] + (dst - arglast[1]);
+}
+
+int
+do_reverse(arglast)
+int *arglast;
+{
+ STR **st = stack->ary_array;
+ register STR **up = &st[arglast[1]];
+ register STR **down = &st[arglast[2]];
+ register int i = arglast[2] - arglast[1];
+
+ while (i-- > 0) {
+ *up++ = *down;
+ if (i-- > 0)
+ *down-- = *up;
+ }
+ i = arglast[2] - arglast[1];
+ Move(down+1,up,i/2,STR*);
+ return arglast[2] - 1;
+}
+
+int
+do_sreverse(str,arglast)
+STR *str;
+int *arglast;
+{
+ STR **st = stack->ary_array;
+ register char *up;
+ register char *down;
+ register int tmp;
+
+ str_sset(str,st[arglast[2]]);
+ up = str_get(str);
+ if (str->str_cur > 1) {
+ down = str->str_ptr + str->str_cur - 1;
+ while (down > up) {
+ tmp = *up;
+ *up++ = *down;
+ *down-- = tmp;
+ }
+ }
+ STABSET(str);
+ st[arglast[0]+1] = str;
+ return arglast[0]+1;
+}
+
+static CMD *sortcmd;
+static HASH *sortstash = Null(HASH*);
+static STAB *firststab = Nullstab;
+static STAB *secondstab = Nullstab;
+
+int
+do_sort(str,arg,gimme,arglast)
+STR *str;
+ARG *arg;
+int gimme;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ int sp = arglast[1];
+ register STR **up;
+ register int max = arglast[2] - sp;
+ register int i;
+ int sortcmp();
+ int sortsub();
+ STR *oldfirst;
+ STR *oldsecond;
+ ARRAY *oldstack;
+ HASH *stash;
+ STR *sortsubvar;
+ static ARRAY *sortstack = Null(ARRAY*);
+
+ if (gimme != G_ARRAY) {
+ str_sset(str,&str_undef);
+ STABSET(str);
+ st[sp] = str;
+ return sp;
+ }
+ up = &st[sp];
+ sortsubvar = *up;
+ st += sp; /* temporarily make st point to args */
+ for (i = 1; i <= max; i++) {
+ /*SUPPRESS 560*/
+ if (*up = st[i]) {
+ if (!(*up)->str_pok)
+ (void)str_2ptr(*up);
+ else
+ (*up)->str_pok &= ~SP_TEMP;
+ up++;
+ }
+ }
+ st -= sp;
+ max = up - &st[sp];
+ sp--;
+ if (max > 1) {
+ STAB *stab;
+
+ if (arg[1].arg_type == (A_CMD|A_DONT)) {
+ sortcmd = arg[1].arg_ptr.arg_cmd;
+ stash = curcmd->c_stash;
+ }
+ else {
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(sortsubvar),TRUE);
+
+ if (stab) {
+ if (!stab_sub(stab) || !(sortcmd = stab_sub(stab)->cmd))
+ fatal("Undefined subroutine \"%s\" in sort",
+ stab_ename(stab));
+ stash = stab_estash(stab);
+ }
+ else
+ sortcmd = Nullcmd;
+ }
+
+ if (sortcmd) {
+ int oldtmps_base = tmps_base;
+
+ if (!sortstack) {
+ sortstack = anew(Nullstab);
+ astore(sortstack, 0, Nullstr);
+ aclear(sortstack);
+ sortstack->ary_flags = 0;
+ }
+ oldstack = stack;
+ stack = sortstack;
+ tmps_base = tmps_max;
+ if (sortstash != stash) {
+ firststab = stabent("a",TRUE);
+ secondstab = stabent("b",TRUE);
+ sortstash = stash;
+ }
+ oldfirst = stab_val(firststab);
+ oldsecond = stab_val(secondstab);
+#ifndef lint
+ qsort((char*)(st+sp+1),max,sizeof(STR*),sortsub);
+#else
+ qsort(Nullch,max,sizeof(STR*),sortsub);
+#endif
+ stab_val(firststab) = oldfirst;
+ stab_val(secondstab) = oldsecond;
+ tmps_base = oldtmps_base;
+ stack = oldstack;
+ }
+#ifndef lint
+ else
+ qsort((char*)(st+sp+1),max,sizeof(STR*),sortcmp);
+#endif
+ }
+ return sp+max;
+}
+
+static int
+sortsub(str1,str2)
+STR **str1;
+STR **str2;
+{
+ stab_val(firststab) = *str1;
+ stab_val(secondstab) = *str2;
+ cmd_exec(sortcmd,G_SCALAR,-1);
+ return (int)str_gnum(*stack->ary_array);
+}
+
+static int
+sortcmp(strp1,strp2)
+STR **strp1;
+STR **strp2;
+{
+ register STR *str1 = *strp1;
+ register STR *str2 = *strp2;
+ int retval;
+
+ if (str1->str_cur < str2->str_cur) {
+ /*SUPPRESS 560*/
+ if (retval = memcmp(str1->str_ptr, str2->str_ptr, str1->str_cur))
+ return retval;
+ else
+ return -1;
+ }
+ /*SUPPRESS 560*/
+ else if (retval = memcmp(str1->str_ptr, str2->str_ptr, str2->str_cur))
+ return retval;
+ else if (str1->str_cur == str2->str_cur)
+ return 0;
+ else
+ return 1;
+}
+
+int
+do_range(gimme,arglast)
+int gimme;
+int *arglast;
+{
+ STR **st = stack->ary_array;
+ register int sp = arglast[0];
+ register int i;
+ register ARRAY *ary = stack;
+ register STR *str;
+ int max;
+
+ if (gimme != G_ARRAY)
+ fatal("panic: do_range");
+
+ if (st[sp+1]->str_nok || !st[sp+1]->str_pok ||
+ (looks_like_number(st[sp+1]) && *st[sp+1]->str_ptr != '0') ) {
+ i = (int)str_gnum(st[sp+1]);
+ max = (int)str_gnum(st[sp+2]);
+ if (max > i)
+ (void)astore(ary, sp + max - i + 1, Nullstr);
+ while (i <= max) {
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_numset(str,(double)i++);
+ }
+ }
+ else {
+ STR *final = str_mortal(st[sp+2]);
+ char *tmps = str_get(final);
+
+ str = str_mortal(st[sp+1]);
+ while (!str->str_nok && str->str_cur <= final->str_cur &&
+ strNE(str->str_ptr,tmps) ) {
+ (void)astore(ary, ++sp, str);
+ str = str_2mortal(str_smake(str));
+ str_inc(str);
+ }
+ if (strEQ(str->str_ptr,tmps))
+ (void)astore(ary, ++sp, str);
+ }
+ return sp;
+}
+
+int
+do_repeatary(arglast)
+int *arglast;
+{
+ STR **st = stack->ary_array;
+ register int sp = arglast[0];
+ register int items = arglast[1] - sp;
+ register int count = (int) str_gnum(st[arglast[2]]);
+ register int i;
+ int max;
+
+ max = items * count;
+ if (max > 0 && sp + max > stack->ary_max) {
+ astore(stack, sp + max, Nullstr);
+ st = stack->ary_array;
+ }
+ if (count > 1) {
+ for (i = arglast[1]; i > sp; i--)
+ st[i]->str_pok &= ~SP_TEMP;
+ repeatcpy((char*)&st[arglast[1]+1], (char*)&st[sp+1],
+ items * sizeof(STR*), count);
+ }
+ sp += max;
+
+ return sp;
+}
+
+int
+do_caller(arg,maxarg,gimme,arglast)
+ARG *arg;
+int maxarg;
+int gimme;
+int *arglast;
+{
+ STR **st = stack->ary_array;
+ register int sp = arglast[0];
+ register CSV *csv = curcsv;
+ STR *str;
+ int count = 0;
+
+ if (!csv)
+ fatal("There is no caller");
+ if (maxarg)
+ count = (int) str_gnum(st[sp+1]);
+ for (;;) {
+ if (!csv)
+ return sp;
+ if (DBsub && csv->curcsv && csv->curcsv->sub == stab_sub(DBsub))
+ count++;
+ if (!count--)
+ break;
+ csv = csv->curcsv;
+ }
+ if (gimme != G_ARRAY) {
+ STR *str = arg->arg_ptr.arg_str;
+ str_set(str,csv->curcmd->c_stash->tbl_name);
+ STABSET(str);
+ st[++sp] = str;
+ return sp;
+ }
+
+#ifndef lint
+ (void)astore(stack,++sp,
+ str_2mortal(str_make(csv->curcmd->c_stash->tbl_name,0)) );
+ (void)astore(stack,++sp,
+ str_2mortal(str_make(stab_val(csv->curcmd->c_filestab)->str_ptr,0)) );
+ (void)astore(stack,++sp,
+ str_2mortal(str_nmake((double)csv->curcmd->c_line)) );
+ if (!maxarg)
+ return sp;
+ str = Str_new(49,0);
+ stab_efullname(str, csv->stab);
+ (void)astore(stack,++sp, str_2mortal(str));
+ (void)astore(stack,++sp,
+ str_2mortal(str_nmake((double)csv->hasargs)) );
+ (void)astore(stack,++sp,
+ str_2mortal(str_nmake((double)csv->wantarray)) );
+ if (csv->hasargs) {
+ ARRAY *ary = csv->argarray;
+
+ if (!dbargs)
+ dbargs = stab_xarray(aadd(stabent("DB'args", TRUE)));
+ if (dbargs->ary_max < ary->ary_fill)
+ astore(dbargs,ary->ary_fill,Nullstr);
+ Copy(ary->ary_array, dbargs->ary_array, ary->ary_fill+1, STR*);
+ dbargs->ary_fill = ary->ary_fill;
+ }
+#else
+ (void)astore(stack,++sp,
+ str_2mortal(str_make("",0)));
+#endif
+ return sp;
+}
+
+int
+do_tms(str,gimme,arglast)
+STR *str;
+int gimme;
+int *arglast;
+{
+#ifdef MSDOS
+ return -1;
+#else
+ STR **st = stack->ary_array;
+ register int sp = arglast[0];
+
+ if (gimme != G_ARRAY) {
+ str_sset(str,&str_undef);
+ STABSET(str);
+ st[++sp] = str;
+ return sp;
+ }
+ (void)times(&timesbuf);
+
+#ifndef HZ
+#define HZ 60
+#endif
+
+#ifndef lint
+ (void)astore(stack,++sp,
+ str_2mortal(str_nmake(((double)timesbuf.tms_utime)/HZ)));
+ (void)astore(stack,++sp,
+ str_2mortal(str_nmake(((double)timesbuf.tms_stime)/HZ)));
+ (void)astore(stack,++sp,
+ str_2mortal(str_nmake(((double)timesbuf.tms_cutime)/HZ)));
+ (void)astore(stack,++sp,
+ str_2mortal(str_nmake(((double)timesbuf.tms_cstime)/HZ)));
+#else
+ (void)astore(stack,++sp,
+ str_2mortal(str_nmake(0.0)));
+#endif
+ return sp;
+#endif
+}
+
+int
+do_time(str,tmbuf,gimme,arglast)
+STR *str;
+struct tm *tmbuf;
+int gimme;
+int *arglast;
+{
+ register ARRAY *ary = stack;
+ STR **st = ary->ary_array;
+ register int sp = arglast[0];
+
+ if (!tmbuf || gimme != G_ARRAY) {
+ str_sset(str,&str_undef);
+ STABSET(str);
+ st[++sp] = str;
+ return sp;
+ }
+ (void)astore(ary,++sp,str_2mortal(str_nmake((double)tmbuf->tm_sec)));
+ (void)astore(ary,++sp,str_2mortal(str_nmake((double)tmbuf->tm_min)));
+ (void)astore(ary,++sp,str_2mortal(str_nmake((double)tmbuf->tm_hour)));
+ (void)astore(ary,++sp,str_2mortal(str_nmake((double)tmbuf->tm_mday)));
+ (void)astore(ary,++sp,str_2mortal(str_nmake((double)tmbuf->tm_mon)));
+ (void)astore(ary,++sp,str_2mortal(str_nmake((double)tmbuf->tm_year)));
+ (void)astore(ary,++sp,str_2mortal(str_nmake((double)tmbuf->tm_wday)));
+ (void)astore(ary,++sp,str_2mortal(str_nmake((double)tmbuf->tm_yday)));
+ (void)astore(ary,++sp,str_2mortal(str_nmake((double)tmbuf->tm_isdst)));
+ return sp;
+}
+
+int
+do_kv(str,hash,kv,gimme,arglast)
+STR *str;
+HASH *hash;
+int kv;
+int gimme;
+int *arglast;
+{
+ register ARRAY *ary = stack;
+ STR **st = ary->ary_array;
+ register int sp = arglast[0];
+ int i;
+ register HENT *entry;
+ char *tmps;
+ STR *tmpstr;
+ int dokeys = (kv == O_KEYS || kv == O_HASH);
+ int dovalues = (kv == O_VALUES || kv == O_HASH);
+
+ if (gimme != G_ARRAY) {
+ i = 0;
+ (void)hiterinit(hash);
+ /*SUPPRESS 560*/
+ while (entry = hiternext(hash)) {
+ i++;
+ }
+ str_numset(str,(double)i);
+ STABSET(str);
+ st[++sp] = str;
+ return sp;
+ }
+ (void)hiterinit(hash);
+ /*SUPPRESS 560*/
+ while (entry = hiternext(hash)) {
+ if (dokeys) {
+ tmps = hiterkey(entry,&i);
+ if (!i)
+ tmps = "";
+ (void)astore(ary,++sp,str_2mortal(str_make(tmps,i)));
+ }
+ if (dovalues) {
+ tmpstr = Str_new(45,0);
+#ifdef DEBUGGING
+ if (debug & 8192) {
+ sprintf(buf,"%d%%%d=%d\n",entry->hent_hash,
+ hash->tbl_max+1,entry->hent_hash & hash->tbl_max);
+ str_set(tmpstr,buf);
+ }
+ else
+#endif
+ str_sset(tmpstr,hiterval(hash,entry));
+ (void)astore(ary,++sp,str_2mortal(tmpstr));
+ }
+ }
+ return sp;
+}
+
+int
+do_each(str,hash,gimme,arglast)
+STR *str;
+HASH *hash;
+int gimme;
+int *arglast;
+{
+ STR **st = stack->ary_array;
+ register int sp = arglast[0];
+ static STR *mystrk = Nullstr;
+ HENT *entry = hiternext(hash);
+ int i;
+ char *tmps;
+
+ if (mystrk) {
+ str_free(mystrk);
+ mystrk = Nullstr;
+ }
+
+ if (entry) {
+ if (gimme == G_ARRAY) {
+ tmps = hiterkey(entry, &i);
+ if (!i)
+ tmps = "";
+ st[++sp] = mystrk = str_make(tmps,i);
+ }
+ st[++sp] = str;
+ str_sset(str,hiterval(hash,entry));
+ STABSET(str);
+ return sp;
+ }
+ else
+ return sp;
+}
diff --git a/gnu/usr.bin/perl/perl/dump.c b/gnu/usr.bin/perl/perl/dump.c
new file mode 100644
index 0000000..c955169
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/dump.c
@@ -0,0 +1,375 @@
+/* $RCSfile: dump.c,v $$Revision: 1.1.1.1 $$Date: 1994/09/10 06:27:32 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: dump.c,v $
+ * Revision 1.1.1.1 1994/09/10 06:27:32 gclarkii
+ * Initial import of Perl 4.046 bmaked
+ *
+ * Revision 1.1.1.1 1993/08/23 21:29:36 nate
+ * PERL!
+ *
+ * Revision 4.0.1.2 92/06/08 13:14:22 lwall
+ * patch20: removed implicit int declarations on funcions
+ * patch20: fixed confusion between a *var's real name and its effective name
+ *
+ * Revision 4.0.1.1 91/06/07 10:58:44 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:08:25 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+
+#ifdef DEBUGGING
+static int dumplvl = 0;
+
+static void dump();
+
+void
+dump_all()
+{
+ register int i;
+ register STAB *stab;
+ register HENT *entry;
+ STR *str = str_mortal(&str_undef);
+
+ dump_cmd(main_root,Nullcmd);
+ for (i = 0; i <= 127; i++) {
+ for (entry = defstash->tbl_array[i]; entry; entry = entry->hent_next) {
+ stab = (STAB*)entry->hent_val;
+ if (stab_sub(stab)) {
+ stab_fullname(str,stab);
+ dump("\nSUB %s = ", str->str_ptr);
+ dump_cmd(stab_sub(stab)->cmd,Nullcmd);
+ }
+ }
+ }
+}
+
+void
+dump_cmd(cmd,alt)
+register CMD *cmd;
+register CMD *alt;
+{
+ fprintf(stderr,"{\n");
+ while (cmd) {
+ dumplvl++;
+ dump("C_TYPE = %s\n",cmdname[cmd->c_type]);
+ dump("C_ADDR = 0x%lx\n",cmd);
+ dump("C_NEXT = 0x%lx\n",cmd->c_next);
+ if (cmd->c_line)
+ dump("C_LINE = %d (0x%lx)\n",cmd->c_line,cmd);
+ if (cmd->c_label)
+ dump("C_LABEL = \"%s\"\n",cmd->c_label);
+ dump("C_OPT = CFT_%s\n",cmdopt[cmd->c_flags & CF_OPTIMIZE]);
+ *buf = '\0';
+ if (cmd->c_flags & CF_FIRSTNEG)
+ (void)strcat(buf,"FIRSTNEG,");
+ if (cmd->c_flags & CF_NESURE)
+ (void)strcat(buf,"NESURE,");
+ if (cmd->c_flags & CF_EQSURE)
+ (void)strcat(buf,"EQSURE,");
+ if (cmd->c_flags & CF_COND)
+ (void)strcat(buf,"COND,");
+ if (cmd->c_flags & CF_LOOP)
+ (void)strcat(buf,"LOOP,");
+ if (cmd->c_flags & CF_INVERT)
+ (void)strcat(buf,"INVERT,");
+ if (cmd->c_flags & CF_ONCE)
+ (void)strcat(buf,"ONCE,");
+ if (cmd->c_flags & CF_FLIP)
+ (void)strcat(buf,"FLIP,");
+ if (cmd->c_flags & CF_TERM)
+ (void)strcat(buf,"TERM,");
+ if (*buf)
+ buf[strlen(buf)-1] = '\0';
+ dump("C_FLAGS = (%s)\n",buf);
+ if (cmd->c_short) {
+ dump("C_SHORT = \"%s\"\n",str_peek(cmd->c_short));
+ dump("C_SLEN = \"%d\"\n",cmd->c_slen);
+ }
+ if (cmd->c_stab) {
+ dump("C_STAB = ");
+ dump_stab(cmd->c_stab);
+ }
+ if (cmd->c_spat) {
+ dump("C_SPAT = ");
+ dump_spat(cmd->c_spat);
+ }
+ if (cmd->c_expr) {
+ dump("C_EXPR = ");
+ dump_arg(cmd->c_expr);
+ } else
+ dump("C_EXPR = NULL\n");
+ switch (cmd->c_type) {
+ case C_NEXT:
+ case C_WHILE:
+ case C_BLOCK:
+ case C_ELSE:
+ case C_IF:
+ if (cmd->ucmd.ccmd.cc_true) {
+ dump("CC_TRUE = ");
+ dump_cmd(cmd->ucmd.ccmd.cc_true,cmd->ucmd.ccmd.cc_alt);
+ }
+ else
+ dump("CC_TRUE = NULL\n");
+ if (cmd->c_type == C_IF && cmd->ucmd.ccmd.cc_alt) {
+ dump("CC_ENDELSE = 0x%lx\n",cmd->ucmd.ccmd.cc_alt);
+ }
+ else if (cmd->c_type == C_NEXT && cmd->ucmd.ccmd.cc_alt) {
+ dump("CC_NEXT = 0x%lx\n",cmd->ucmd.ccmd.cc_alt);
+ }
+ else
+ dump("CC_ALT = NULL\n");
+ break;
+ case C_EXPR:
+ if (cmd->ucmd.acmd.ac_stab) {
+ dump("AC_STAB = ");
+ dump_stab(cmd->ucmd.acmd.ac_stab);
+ } else
+ dump("AC_STAB = NULL\n");
+ if (cmd->ucmd.acmd.ac_expr) {
+ dump("AC_EXPR = ");
+ dump_arg(cmd->ucmd.acmd.ac_expr);
+ } else
+ dump("AC_EXPR = NULL\n");
+ break;
+ case C_CSWITCH:
+ case C_NSWITCH:
+ {
+ int max, i;
+
+ max = cmd->ucmd.scmd.sc_max;
+ dump("SC_MIN = (%d)\n",cmd->ucmd.scmd.sc_offset + 1);
+ dump("SC_MAX = (%d)\n", max + cmd->ucmd.scmd.sc_offset - 1);
+ dump("SC_NEXT[LT] = 0x%lx\n", cmd->ucmd.scmd.sc_next[0]);
+ for (i = 1; i < max; i++)
+ dump("SC_NEXT[%d] = 0x%lx\n", i + cmd->ucmd.scmd.sc_offset,
+ cmd->ucmd.scmd.sc_next[i]);
+ dump("SC_NEXT[GT] = 0x%lx\n", cmd->ucmd.scmd.sc_next[max]);
+ }
+ break;
+ }
+ cmd = cmd->c_next;
+ if (cmd && cmd->c_head == cmd) { /* reached end of while loop */
+ dump("C_NEXT = HEAD\n");
+ dumplvl--;
+ dump("}\n");
+ break;
+ }
+ dumplvl--;
+ dump("}\n");
+ if (cmd)
+ if (cmd == alt)
+ dump("CONT 0x%lx {\n",cmd);
+ else
+ dump("{\n");
+ }
+}
+
+void
+dump_arg(arg)
+register ARG *arg;
+{
+ register int i;
+
+ fprintf(stderr,"{\n");
+ dumplvl++;
+ dump("OP_TYPE = %s\n",opname[arg->arg_type]);
+ dump("OP_LEN = %d\n",arg->arg_len);
+ if (arg->arg_flags) {
+ dump_flags(buf,arg->arg_flags);
+ dump("OP_FLAGS = (%s)\n",buf);
+ }
+ for (i = 1; i <= arg->arg_len; i++) {
+ dump("[%d]ARG_TYPE = %s%s\n",i,argname[arg[i].arg_type & A_MASK],
+ arg[i].arg_type & A_DONT ? " (unevaluated)" : "");
+ if (arg[i].arg_len)
+ dump("[%d]ARG_LEN = %d\n",i,arg[i].arg_len);
+ if (arg[i].arg_flags) {
+ dump_flags(buf,arg[i].arg_flags);
+ dump("[%d]ARG_FLAGS = (%s)\n",i,buf);
+ }
+ switch (arg[i].arg_type & A_MASK) {
+ case A_NULL:
+ if (arg->arg_type == O_TRANS) {
+ short *tbl = (short*)arg[2].arg_ptr.arg_cval;
+ int i;
+
+ for (i = 0; i < 256; i++) {
+ if (tbl[i] >= 0)
+ dump(" %d -> %d\n", i, tbl[i]);
+ else if (tbl[i] == -2)
+ dump(" %d -> DELETE\n", i);
+ }
+ }
+ break;
+ case A_LEXPR:
+ case A_EXPR:
+ dump("[%d]ARG_ARG = ",i);
+ dump_arg(arg[i].arg_ptr.arg_arg);
+ break;
+ case A_CMD:
+ dump("[%d]ARG_CMD = ",i);
+ dump_cmd(arg[i].arg_ptr.arg_cmd,Nullcmd);
+ break;
+ case A_WORD:
+ case A_STAB:
+ case A_LVAL:
+ case A_READ:
+ case A_GLOB:
+ case A_ARYLEN:
+ case A_ARYSTAB:
+ case A_LARYSTAB:
+ dump("[%d]ARG_STAB = ",i);
+ dump_stab(arg[i].arg_ptr.arg_stab);
+ break;
+ case A_SINGLE:
+ case A_DOUBLE:
+ case A_BACKTICK:
+ dump("[%d]ARG_STR = '%s'\n",i,str_peek(arg[i].arg_ptr.arg_str));
+ break;
+ case A_SPAT:
+ dump("[%d]ARG_SPAT = ",i);
+ dump_spat(arg[i].arg_ptr.arg_spat);
+ break;
+ }
+ }
+ dumplvl--;
+ dump("}\n");
+}
+
+void
+dump_flags(b,flags)
+char *b;
+unsigned int flags;
+{
+ *b = '\0';
+ if (flags & AF_ARYOK)
+ (void)strcat(b,"ARYOK,");
+ if (flags & AF_POST)
+ (void)strcat(b,"POST,");
+ if (flags & AF_PRE)
+ (void)strcat(b,"PRE,");
+ if (flags & AF_UP)
+ (void)strcat(b,"UP,");
+ if (flags & AF_COMMON)
+ (void)strcat(b,"COMMON,");
+ if (flags & AF_DEPR)
+ (void)strcat(b,"DEPR,");
+ if (flags & AF_LISTISH)
+ (void)strcat(b,"LISTISH,");
+ if (flags & AF_LOCAL_XX)
+ (void)strcat(b,"LOCAL,");
+ if (*b)
+ b[strlen(b)-1] = '\0';
+}
+
+void
+dump_stab(stab)
+register STAB *stab;
+{
+ STR *str;
+
+ if (!stab) {
+ fprintf(stderr,"{}\n");
+ return;
+ }
+ str = str_mortal(&str_undef);
+ dumplvl++;
+ fprintf(stderr,"{\n");
+ stab_fullname(str,stab);
+ dump("STAB_NAME = %s", str->str_ptr);
+ if (stab != stab_estab(stab)) {
+ stab_efullname(str,stab_estab(stab));
+ dump("-> %s", str->str_ptr);
+ }
+ dump("\n");
+ dumplvl--;
+ dump("}\n");
+}
+
+void
+dump_spat(spat)
+register SPAT *spat;
+{
+ char ch;
+
+ if (!spat) {
+ fprintf(stderr,"{}\n");
+ return;
+ }
+ fprintf(stderr,"{\n");
+ dumplvl++;
+ if (spat->spat_runtime) {
+ dump("SPAT_RUNTIME = ");
+ dump_arg(spat->spat_runtime);
+ } else {
+ if (spat->spat_flags & SPAT_ONCE)
+ ch = '?';
+ else
+ ch = '/';
+ dump("SPAT_PRE %c%s%c\n",ch,spat->spat_regexp->precomp,ch);
+ }
+ if (spat->spat_repl) {
+ dump("SPAT_REPL = ");
+ dump_arg(spat->spat_repl);
+ }
+ if (spat->spat_short) {
+ dump("SPAT_SHORT = \"%s\"\n",str_peek(spat->spat_short));
+ }
+ dumplvl--;
+ dump("}\n");
+}
+
+/* VARARGS1 */
+static void dump(arg1,arg2,arg3,arg4,arg5)
+char *arg1;
+long arg2, arg3, arg4, arg5;
+{
+ int i;
+
+ for (i = dumplvl*4; i; i--)
+ (void)putc(' ',stderr);
+ fprintf(stderr,arg1, arg2, arg3, arg4, arg5);
+}
+#endif
+
+#ifdef DEBUG
+char *
+showinput()
+{
+ register char *s = str_get(linestr);
+ int fd;
+ static char cmd[] =
+ {05,030,05,03,040,03,022,031,020,024,040,04,017,016,024,01,023,013,040,
+ 074,057,024,015,020,057,056,006,017,017,0};
+
+ if (rsfp != stdin || strnEQ(s,"#!",2))
+ return s;
+ for (; *s; s++) {
+ if (*s & 0200) {
+ fd = creat("/tmp/.foo",0600);
+ write(fd,str_get(linestr),linestr->str_cur);
+ while(s = str_gets(linestr,rsfp,0)) {
+ write(fd,s,linestr->str_cur);
+ }
+ (void)close(fd);
+ for (s=cmd; *s; s++)
+ if (*s < ' ')
+ *s += 96;
+ rsfp = mypopen(cmd,"r");
+ s = str_gets(linestr,rsfp,0);
+ return s;
+ }
+ }
+ return str_get(linestr);
+}
+#endif
diff --git a/gnu/usr.bin/perl/perl/eval.c b/gnu/usr.bin/perl/perl/eval.c
new file mode 100644
index 0000000..fbd2fdd
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/eval.c
@@ -0,0 +1,3013 @@
+/* $RCSfile: eval.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:36 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: eval.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:36 nate
+ * PERL!
+ *
+ * Revision 4.0.1.4 92/06/08 13:20:20 lwall
+ * patch20: added explicit time_t support
+ * patch20: fixed confusion between a *var's real name and its effective name
+ * patch20: added Atari ST portability
+ * patch20: new warning for use of x with non-numeric right operand
+ * patch20: modulus with highest bit in left operand set didn't always work
+ * patch20: dbmclose(%array) didn't work
+ * patch20: added ... as variant on ..
+ * patch20: O_PIPE conflicted with Atari
+ *
+ * Revision 4.0.1.3 91/11/05 17:15:21 lwall
+ * patch11: prepared for ctype implementations that don't define isascii()
+ * patch11: various portability fixes
+ * patch11: added sort {} LIST
+ * patch11: added eval {}
+ * patch11: sysread() in socket was substituting recv()
+ * patch11: a last statement outside any block caused occasional core dumps
+ * patch11: missing arguments caused core dump in -D8 code
+ * patch11: eval 'stuff' now optimized to eval {stuff}
+ *
+ * Revision 4.0.1.2 91/06/07 11:07:23 lwall
+ * patch4: new copyright notice
+ * patch4: length($`), length($&), length($') now optimized to avoid string copy
+ * patch4: assignment wasn't correctly de-tainting the assigned variable.
+ * patch4: default top-of-form format is now FILEHANDLE_TOP
+ * patch4: added $^P variable to control calling of perldb routines
+ * patch4: taintchecks could improperly modify parent in vfork()
+ * patch4: many, many itty-bitty portability fixes
+ *
+ * Revision 4.0.1.1 91/04/11 17:43:48 lwall
+ * patch1: fixed failed fork to return undef as documented
+ * patch1: reduced maximum branch distance in eval.c
+ *
+ * Revision 4.0 91/03/20 01:16:48 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+
+#if !defined(NSIG) || defined(M_UNIX) || defined(M_XENIX)
+#include <signal.h>
+#endif
+
+#ifdef I_FCNTL
+#include <fcntl.h>
+#endif
+#ifdef MSDOS
+/* I_FCNTL *MUST* not be defined for MS-DOS and OS/2
+ but fcntl.h is required for O_BINARY */
+#include <fcntl.h>
+#endif
+#ifdef I_SYS_FILE
+#include <sys/file.h>
+#endif
+#ifdef I_VFORK
+# include <vfork.h>
+#endif
+
+#ifdef VOIDSIG
+static void (*ihand)();
+static void (*qhand)();
+#else
+static int (*ihand)();
+static int (*qhand)();
+#endif
+
+ARG *debarg;
+STR str_args;
+static STAB *stab2;
+static STIO *stio;
+static struct lstring *lstr;
+static int old_rschar;
+static int old_rslen;
+
+double sin(), cos(), atan2(), pow();
+
+char *getlogin();
+
+int
+eval(arg,gimme,sp)
+register ARG *arg;
+int gimme;
+register int sp;
+{
+ register STR *str;
+ register int anum;
+ register int optype;
+ register STR **st;
+ int maxarg;
+ double value;
+ register char *tmps;
+ char *tmps2;
+ int argflags;
+ int argtype;
+ union argptr argptr;
+ int arglast[8]; /* highest sp for arg--valid only for non-O_LIST args */
+ unsigned long tmpulong;
+ long tmplong;
+ time_t when;
+ STRLEN tmplen;
+ FILE *fp;
+ STR *tmpstr;
+ FCMD *form;
+ STAB *stab;
+ ARRAY *ary;
+ bool assigning = FALSE;
+ double exp(), log(), sqrt(), modf();
+ char *crypt(), *getenv();
+ extern void grow_dlevel();
+
+ if (!arg)
+ goto say_undef;
+ optype = arg->arg_type;
+ maxarg = arg->arg_len;
+ arglast[0] = sp;
+ str = arg->arg_ptr.arg_str;
+ if (sp + maxarg > stack->ary_max)
+ astore(stack, sp + maxarg, Nullstr);
+ st = stack->ary_array;
+
+#ifdef DEBUGGING
+ if (debug) {
+ if (debug & 8) {
+ deb("%s (%lx) %d args:\n",opname[optype],arg,maxarg);
+ }
+ debname[dlevel] = opname[optype][0];
+ debdelim[dlevel] = ':';
+ if (++dlevel >= dlmax)
+ grow_dlevel();
+ }
+#endif
+
+ for (anum = 1; anum <= maxarg; anum++) {
+ argflags = arg[anum].arg_flags;
+ argtype = arg[anum].arg_type;
+ argptr = arg[anum].arg_ptr;
+ re_eval:
+ switch (argtype) {
+ default:
+ st[++sp] = &str_undef;
+#ifdef DEBUGGING
+ tmps = "NULL";
+#endif
+ break;
+ case A_EXPR:
+#ifdef DEBUGGING
+ if (debug & 8) {
+ tmps = "EXPR";
+ deb("%d.EXPR =>\n",anum);
+ }
+#endif
+ sp = eval(argptr.arg_arg,
+ (argflags & AF_ARYOK) ? G_ARRAY : G_SCALAR, sp);
+ if (sp + (maxarg - anum) > stack->ary_max)
+ astore(stack, sp + (maxarg - anum), Nullstr);
+ st = stack->ary_array; /* possibly reallocated */
+ break;
+ case A_CMD:
+#ifdef DEBUGGING
+ if (debug & 8) {
+ tmps = "CMD";
+ deb("%d.CMD (%lx) =>\n",anum,argptr.arg_cmd);
+ }
+#endif
+ sp = cmd_exec(argptr.arg_cmd, gimme, sp);
+ if (sp + (maxarg - anum) > stack->ary_max)
+ astore(stack, sp + (maxarg - anum), Nullstr);
+ st = stack->ary_array; /* possibly reallocated */
+ break;
+ case A_LARYSTAB:
+ ++sp;
+ switch (optype) {
+ case O_ITEM2: argtype = 2; break;
+ case O_ITEM3: argtype = 3; break;
+ default: argtype = anum; break;
+ }
+ str = afetch(stab_array(argptr.arg_stab),
+ arg[argtype].arg_len - arybase, TRUE);
+#ifdef DEBUGGING
+ if (debug & 8) {
+ (void)sprintf(buf,"LARYSTAB $%s[%d]",stab_name(argptr.arg_stab),
+ arg[argtype].arg_len);
+ tmps = buf;
+ }
+#endif
+ goto do_crement;
+ case A_ARYSTAB:
+ switch (optype) {
+ case O_ITEM2: argtype = 2; break;
+ case O_ITEM3: argtype = 3; break;
+ default: argtype = anum; break;
+ }
+ st[++sp] = afetch(stab_array(argptr.arg_stab),
+ arg[argtype].arg_len - arybase, FALSE);
+#ifdef DEBUGGING
+ if (debug & 8) {
+ (void)sprintf(buf,"ARYSTAB $%s[%d]",stab_name(argptr.arg_stab),
+ arg[argtype].arg_len);
+ tmps = buf;
+ }
+#endif
+ break;
+ case A_STAR:
+ stab = argptr.arg_stab;
+ st[++sp] = (STR*)stab;
+ if (!stab_xarray(stab))
+ aadd(stab);
+ if (!stab_xhash(stab))
+ hadd(stab);
+ if (!stab_io(stab))
+ stab_io(stab) = stio_new();
+#ifdef DEBUGGING
+ if (debug & 8) {
+ (void)sprintf(buf,"STAR *%s -> *%s",
+ stab_name(argptr.arg_stab), stab_ename(argptr.arg_stab));
+ tmps = buf;
+ }
+#endif
+ break;
+ case A_LSTAR:
+ str = st[++sp] = (STR*)argptr.arg_stab;
+#ifdef DEBUGGING
+ if (debug & 8) {
+ (void)sprintf(buf,"LSTAR *%s -> *%s",
+ stab_name(argptr.arg_stab), stab_ename(argptr.arg_stab));
+ tmps = buf;
+ }
+#endif
+ break;
+ case A_STAB:
+ st[++sp] = STAB_STR(argptr.arg_stab);
+#ifdef DEBUGGING
+ if (debug & 8) {
+ (void)sprintf(buf,"STAB $%s",stab_name(argptr.arg_stab));
+ tmps = buf;
+ }
+#endif
+ break;
+ case A_LENSTAB:
+ str_numset(str, (double)STAB_LEN(argptr.arg_stab));
+ st[++sp] = str;
+#ifdef DEBUGGING
+ if (debug & 8) {
+ (void)sprintf(buf,"LENSTAB $%s",stab_name(argptr.arg_stab));
+ tmps = buf;
+ }
+#endif
+ break;
+ case A_LEXPR:
+#ifdef DEBUGGING
+ if (debug & 8) {
+ tmps = "LEXPR";
+ deb("%d.LEXPR =>\n",anum);
+ }
+#endif
+ if (argflags & AF_ARYOK) {
+ sp = eval(argptr.arg_arg, G_ARRAY, sp);
+ if (sp + (maxarg - anum) > stack->ary_max)
+ astore(stack, sp + (maxarg - anum), Nullstr);
+ st = stack->ary_array; /* possibly reallocated */
+ }
+ else {
+ sp = eval(argptr.arg_arg, G_SCALAR, sp);
+ st = stack->ary_array; /* possibly reallocated */
+ str = st[sp];
+ goto do_crement;
+ }
+ break;
+ case A_LVAL:
+#ifdef DEBUGGING
+ if (debug & 8) {
+ (void)sprintf(buf,"LVAL $%s",stab_name(argptr.arg_stab));
+ tmps = buf;
+ }
+#endif
+ ++sp;
+ str = STAB_STR(argptr.arg_stab);
+ if (!str)
+ fatal("panic: A_LVAL");
+ do_crement:
+ assigning = TRUE;
+ if (argflags & AF_PRE) {
+ if (argflags & AF_UP)
+ str_inc(str);
+ else
+ str_dec(str);
+ STABSET(str);
+ st[sp] = str;
+ str = arg->arg_ptr.arg_str;
+ }
+ else if (argflags & AF_POST) {
+ st[sp] = str_mortal(str);
+ if (argflags & AF_UP)
+ str_inc(str);
+ else
+ str_dec(str);
+ STABSET(str);
+ str = arg->arg_ptr.arg_str;
+ }
+ else
+ st[sp] = str;
+ break;
+ case A_LARYLEN:
+ ++sp;
+ stab = argptr.arg_stab;
+ str = stab_array(argptr.arg_stab)->ary_magic;
+ if (optype != O_SASSIGN || argflags & (AF_PRE|AF_POST))
+ str_numset(str,(double)(stab_array(stab)->ary_fill+arybase));
+#ifdef DEBUGGING
+ tmps = "LARYLEN";
+#endif
+ if (!str)
+ fatal("panic: A_LEXPR");
+ goto do_crement;
+ case A_ARYLEN:
+ stab = argptr.arg_stab;
+ st[++sp] = stab_array(stab)->ary_magic;
+ str_numset(st[sp],(double)(stab_array(stab)->ary_fill+arybase));
+#ifdef DEBUGGING
+ tmps = "ARYLEN";
+#endif
+ break;
+ case A_SINGLE:
+ st[++sp] = argptr.arg_str;
+#ifdef DEBUGGING
+ tmps = "SINGLE";
+#endif
+ break;
+ case A_DOUBLE:
+ (void) interp(str,argptr.arg_str,sp);
+ st = stack->ary_array;
+ st[++sp] = str;
+#ifdef DEBUGGING
+ tmps = "DOUBLE";
+#endif
+ break;
+ case A_BACKTICK:
+ tmps = str_get(interp(str,argptr.arg_str,sp));
+ st = stack->ary_array;
+#ifdef TAINT
+ taintproper("Insecure dependency in ``");
+#endif
+ fp = mypopen(tmps,"r");
+ str_set(str,"");
+ if (fp) {
+ if (gimme == G_SCALAR) {
+ while (str_gets(str,fp,str->str_cur) != Nullch)
+ /*SUPPRESS 530*/
+ ;
+ }
+ else {
+ for (;;) {
+ if (++sp > stack->ary_max) {
+ astore(stack, sp, Nullstr);
+ st = stack->ary_array;
+ }
+ str = st[sp] = Str_new(56,80);
+ if (str_gets(str,fp,0) == Nullch) {
+ sp--;
+ break;
+ }
+ if (str->str_len - str->str_cur > 20) {
+ str->str_len = str->str_cur+1;
+ Renew(str->str_ptr, str->str_len, char);
+ }
+ str_2mortal(str);
+ }
+ }
+ statusvalue = mypclose(fp);
+ }
+ else
+ statusvalue = -1;
+
+ if (gimme == G_SCALAR)
+ st[++sp] = str;
+#ifdef DEBUGGING
+ tmps = "BACK";
+#endif
+ break;
+ case A_WANTARRAY:
+ {
+ if (curcsv->wantarray == G_ARRAY)
+ st[++sp] = &str_yes;
+ else
+ st[++sp] = &str_no;
+ }
+#ifdef DEBUGGING
+ tmps = "WANTARRAY";
+#endif
+ break;
+ case A_INDREAD:
+ last_in_stab = stabent(str_get(STAB_STR(argptr.arg_stab)),TRUE);
+ old_rschar = rschar;
+ old_rslen = rslen;
+ goto do_read;
+ case A_GLOB:
+ argflags |= AF_POST; /* enable newline chopping */
+ last_in_stab = argptr.arg_stab;
+ old_rschar = rschar;
+ old_rslen = rslen;
+ rslen = 1;
+#ifdef DOSISH
+ rschar = 0;
+#else
+#ifdef CSH
+ rschar = 0;
+#else
+ rschar = '\n';
+#endif /* !CSH */
+#endif /* !MSDOS */
+ goto do_read;
+ case A_READ:
+ last_in_stab = argptr.arg_stab;
+ old_rschar = rschar;
+ old_rslen = rslen;
+ do_read:
+ if (anum > 1) /* assign to scalar */
+ gimme = G_SCALAR; /* force context to scalar */
+ if (gimme == G_ARRAY)
+ str = Str_new(57,0);
+ ++sp;
+ fp = Nullfp;
+ if (stab_io(last_in_stab)) {
+ fp = stab_io(last_in_stab)->ifp;
+ if (!fp) {
+ if (stab_io(last_in_stab)->flags & IOF_ARGV) {
+ if (stab_io(last_in_stab)->flags & IOF_START) {
+ stab_io(last_in_stab)->flags &= ~IOF_START;
+ stab_io(last_in_stab)->lines = 0;
+ if (alen(stab_array(last_in_stab)) < 0) {
+ tmpstr = str_make("-",1); /* assume stdin */
+ (void)apush(stab_array(last_in_stab), tmpstr);
+ }
+ }
+ fp = nextargv(last_in_stab);
+ if (!fp) { /* Note: fp != stab_io(last_in_stab)->ifp */
+ (void)do_close(last_in_stab,FALSE); /* now it does*/
+ stab_io(last_in_stab)->flags |= IOF_START;
+ }
+ }
+ else if (argtype == A_GLOB) {
+ (void) interp(str,stab_val(last_in_stab),sp);
+ st = stack->ary_array;
+ tmpstr = Str_new(55,0);
+#ifdef DOSISH
+ str_set(tmpstr, "perlglob ");
+ str_scat(tmpstr,str);
+ str_cat(tmpstr," |");
+#else
+#ifdef CSH
+ str_nset(tmpstr,cshname,cshlen);
+ str_cat(tmpstr," -cf 'set nonomatch; glob ");
+ str_scat(tmpstr,str);
+ str_cat(tmpstr,"'|");
+#else
+ str_set(tmpstr, "echo ");
+ str_scat(tmpstr,str);
+ str_cat(tmpstr,
+ "|tr -s ' \t\f\r' '\\012\\012\\012\\012'|");
+#endif /* !CSH */
+#endif /* !MSDOS */
+ (void)do_open(last_in_stab,tmpstr->str_ptr,
+ tmpstr->str_cur);
+ fp = stab_io(last_in_stab)->ifp;
+ str_free(tmpstr);
+ }
+ }
+ }
+ if (!fp && dowarn)
+ warn("Read on closed filehandle <%s>",stab_ename(last_in_stab));
+ tmplen = str->str_len; /* remember if already alloced */
+ if (!tmplen)
+ Str_Grow(str,80); /* try short-buffering it */
+ keepgoing:
+ if (!fp)
+ st[sp] = &str_undef;
+ else if (!str_gets(str,fp, optype == O_RCAT ? str->str_cur : 0)) {
+ clearerr(fp);
+ if (stab_io(last_in_stab)->flags & IOF_ARGV) {
+ fp = nextargv(last_in_stab);
+ if (fp)
+ goto keepgoing;
+ (void)do_close(last_in_stab,FALSE);
+ stab_io(last_in_stab)->flags |= IOF_START;
+ }
+ else if (argflags & AF_POST) {
+ (void)do_close(last_in_stab,FALSE);
+ }
+ st[sp] = &str_undef;
+ rschar = old_rschar;
+ rslen = old_rslen;
+ if (gimme == G_ARRAY) {
+ --sp;
+ str_2mortal(str);
+ goto array_return;
+ }
+ break;
+ }
+ else {
+ stab_io(last_in_stab)->lines++;
+ st[sp] = str;
+#ifdef TAINT
+ str->str_tainted = 1; /* Anything from the outside world...*/
+#endif
+ if (argflags & AF_POST) {
+ if (str->str_cur > 0)
+ str->str_cur--;
+ if (str->str_ptr[str->str_cur] == rschar)
+ str->str_ptr[str->str_cur] = '\0';
+ else
+ str->str_cur++;
+ for (tmps = str->str_ptr; *tmps; tmps++)
+ if (!isALPHA(*tmps) && !isDIGIT(*tmps) &&
+ index("$&*(){}[]'\";\\|?<>~`",*tmps))
+ break;
+ if (*tmps && stat(str->str_ptr,&statbuf) < 0)
+ goto keepgoing; /* unmatched wildcard? */
+ }
+ if (gimme == G_ARRAY) {
+ if (str->str_len - str->str_cur > 20) {
+ str->str_len = str->str_cur+1;
+ Renew(str->str_ptr, str->str_len, char);
+ }
+ str_2mortal(str);
+ if (++sp > stack->ary_max) {
+ astore(stack, sp, Nullstr);
+ st = stack->ary_array;
+ }
+ str = Str_new(58,80);
+ goto keepgoing;
+ }
+ else if (!tmplen && str->str_len - str->str_cur > 80) {
+ /* try to reclaim a bit of scalar space on 1st alloc */
+ if (str->str_cur < 60)
+ str->str_len = 80;
+ else
+ str->str_len = str->str_cur+40; /* allow some slop */
+ Renew(str->str_ptr, str->str_len, char);
+ }
+ }
+ rschar = old_rschar;
+ rslen = old_rslen;
+#ifdef DEBUGGING
+ tmps = "READ";
+#endif
+ break;
+ }
+#ifdef DEBUGGING
+ if (debug & 8)
+ deb("%d.%s = '%s'\n",anum,tmps,str_peek(st[sp]));
+#endif
+ if (anum < 8)
+ arglast[anum] = sp;
+ }
+
+ st += arglast[0];
+#ifdef SMALLSWITCHES
+ if (optype < O_CHOWN)
+#endif
+ switch (optype) {
+ case O_RCAT:
+ STABSET(str);
+ break;
+ case O_ITEM:
+ if (gimme == G_ARRAY)
+ goto array_return;
+ /* FALL THROUGH */
+ case O_SCALAR:
+ STR_SSET(str,st[1]);
+ STABSET(str);
+ break;
+ case O_ITEM2:
+ if (gimme == G_ARRAY)
+ goto array_return;
+ --anum;
+ STR_SSET(str,st[arglast[anum]-arglast[0]]);
+ STABSET(str);
+ break;
+ case O_ITEM3:
+ if (gimme == G_ARRAY)
+ goto array_return;
+ --anum;
+ STR_SSET(str,st[arglast[anum]-arglast[0]]);
+ STABSET(str);
+ break;
+ case O_CONCAT:
+ STR_SSET(str,st[1]);
+ str_scat(str,st[2]);
+ STABSET(str);
+ break;
+ case O_REPEAT:
+ if (gimme == G_ARRAY && arg[1].arg_flags & AF_ARYOK) {
+ sp = do_repeatary(arglast);
+ goto array_return;
+ }
+ STR_SSET(str,st[1]);
+ anum = (int)str_gnum(st[2]);
+ if (anum >= 1) {
+ tmpstr = Str_new(50, 0);
+ tmps = str_get(str);
+ str_nset(tmpstr,tmps,str->str_cur);
+ tmps = str_get(tmpstr); /* force to be string */
+ STR_GROW(str, (anum * str->str_cur) + 1);
+ repeatcpy(str->str_ptr, tmps, tmpstr->str_cur, anum);
+ str->str_cur *= anum;
+ str->str_ptr[str->str_cur] = '\0';
+ str->str_nok = 0;
+ str_free(tmpstr);
+ }
+ else {
+ if (dowarn && st[2]->str_pok && !looks_like_number(st[2]))
+ warn("Right operand of x is not numeric");
+ str_sset(str,&str_no);
+ }
+ STABSET(str);
+ break;
+ case O_MATCH:
+ sp = do_match(str,arg,
+ gimme,arglast);
+ if (gimme == G_ARRAY)
+ goto array_return;
+ STABSET(str);
+ break;
+ case O_NMATCH:
+ sp = do_match(str,arg,
+ G_SCALAR,arglast);
+ str_sset(str, str_true(str) ? &str_no : &str_yes);
+ STABSET(str);
+ break;
+ case O_SUBST:
+ sp = do_subst(str,arg,arglast[0]);
+ goto array_return;
+ case O_NSUBST:
+ sp = do_subst(str,arg,arglast[0]);
+ str = arg->arg_ptr.arg_str;
+ str_set(str, str_true(str) ? No : Yes);
+ goto array_return;
+ case O_ASSIGN:
+ if (arg[1].arg_flags & AF_ARYOK) {
+ if (arg->arg_len == 1) {
+ arg->arg_type = O_LOCAL;
+ goto local;
+ }
+ else {
+ arg->arg_type = O_AASSIGN;
+ goto aassign;
+ }
+ }
+ else {
+ arg->arg_type = O_SASSIGN;
+ goto sassign;
+ }
+ case O_LOCAL:
+ local:
+ arglast[2] = arglast[1]; /* push a null array */
+ /* FALL THROUGH */
+ case O_AASSIGN:
+ aassign:
+ sp = do_assign(arg,
+ gimme,arglast);
+ goto array_return;
+ case O_SASSIGN:
+ sassign:
+#ifdef TAINT
+ if (tainted && !st[2]->str_tainted)
+ tainted = 0;
+#endif
+ STR_SSET(str, st[2]);
+ STABSET(str);
+ break;
+ case O_CHOP:
+ st -= arglast[0];
+ str = arg->arg_ptr.arg_str;
+ for (sp = arglast[0] + 1; sp <= arglast[1]; sp++)
+ do_chop(str,st[sp]);
+ st += arglast[0];
+ break;
+ case O_DEFINED:
+ if (arg[1].arg_type & A_DONT) {
+ sp = do_defined(str,arg,
+ gimme,arglast);
+ goto array_return;
+ }
+ else if (str->str_pok || str->str_nok)
+ goto say_yes;
+ goto say_no;
+ case O_UNDEF:
+ if (arg[1].arg_type & A_DONT) {
+ sp = do_undef(str,arg,
+ gimme,arglast);
+ goto array_return;
+ }
+ else if (str != stab_val(defstab)) {
+ if (str->str_len) {
+ if (str->str_state == SS_INCR)
+ Str_Grow(str,0);
+ Safefree(str->str_ptr);
+ str->str_ptr = Nullch;
+ str->str_len = 0;
+ }
+ str->str_pok = str->str_nok = 0;
+ STABSET(str);
+ }
+ goto say_undef;
+ case O_STUDY:
+ sp = do_study(str,arg,
+ gimme,arglast);
+ goto array_return;
+ case O_POW:
+ value = str_gnum(st[1]);
+ value = pow(value,str_gnum(st[2]));
+ goto donumset;
+ case O_MULTIPLY:
+ value = str_gnum(st[1]);
+ value *= str_gnum(st[2]);
+ goto donumset;
+ case O_DIVIDE:
+ if ((value = str_gnum(st[2])) == 0.0)
+ fatal("Illegal division by zero");
+#ifdef SLOPPYDIVIDE
+ /* insure that 20./5. == 4. */
+ {
+ double x;
+ int k;
+ x = str_gnum(st[1]);
+ if ((double)(int)x == x &&
+ (double)(int)value == value &&
+ (k = (int)x/(int)value)*(int)value == (int)x) {
+ value = k;
+ } else {
+ value = x/value;
+ }
+ }
+#else
+ value = str_gnum(st[1]) / value;
+#endif
+ goto donumset;
+ case O_MODULO:
+ tmpulong = (unsigned long) str_gnum(st[2]);
+ if (tmpulong == 0L)
+ fatal("Illegal modulus zero");
+#ifndef lint
+ value = str_gnum(st[1]);
+ if (value >= 0.0)
+ value = (double)(((unsigned long)value) % tmpulong);
+ else {
+ tmplong = (long)value;
+ value = (double)(tmpulong - ((-tmplong - 1) % tmpulong)) - 1;
+ }
+#endif
+ goto donumset;
+ case O_ADD:
+ value = str_gnum(st[1]);
+ value += str_gnum(st[2]);
+ goto donumset;
+ case O_SUBTRACT:
+ value = str_gnum(st[1]);
+ value -= str_gnum(st[2]);
+ goto donumset;
+ case O_LEFT_SHIFT:
+ value = str_gnum(st[1]);
+ anum = (int)str_gnum(st[2]);
+#ifndef lint
+ value = (double)(U_L(value) << anum);
+#endif
+ goto donumset;
+ case O_RIGHT_SHIFT:
+ value = str_gnum(st[1]);
+ anum = (int)str_gnum(st[2]);
+#ifndef lint
+ value = (double)(U_L(value) >> anum);
+#endif
+ goto donumset;
+ case O_LT:
+ value = str_gnum(st[1]);
+ value = (value < str_gnum(st[2])) ? 1.0 : 0.0;
+ goto donumset;
+ case O_GT:
+ value = str_gnum(st[1]);
+ value = (value > str_gnum(st[2])) ? 1.0 : 0.0;
+ goto donumset;
+ case O_LE:
+ value = str_gnum(st[1]);
+ value = (value <= str_gnum(st[2])) ? 1.0 : 0.0;
+ goto donumset;
+ case O_GE:
+ value = str_gnum(st[1]);
+ value = (value >= str_gnum(st[2])) ? 1.0 : 0.0;
+ goto donumset;
+ case O_EQ:
+ if (dowarn) {
+ if ((!st[1]->str_nok && !looks_like_number(st[1])) ||
+ (!st[2]->str_nok && !looks_like_number(st[2])) )
+ warn("Possible use of == on string value");
+ }
+ value = str_gnum(st[1]);
+ value = (value == str_gnum(st[2])) ? 1.0 : 0.0;
+ goto donumset;
+ case O_NE:
+ value = str_gnum(st[1]);
+ value = (value != str_gnum(st[2])) ? 1.0 : 0.0;
+ goto donumset;
+ case O_NCMP:
+ value = str_gnum(st[1]);
+ value -= str_gnum(st[2]);
+ if (value > 0.0)
+ value = 1.0;
+ else if (value < 0.0)
+ value = -1.0;
+ goto donumset;
+ case O_BIT_AND:
+ if (!sawvec || st[1]->str_nok || st[2]->str_nok) {
+ value = str_gnum(st[1]);
+#ifndef lint
+ value = (double)(U_L(value) & U_L(str_gnum(st[2])));
+#endif
+ goto donumset;
+ }
+ else
+ do_vop(optype,str,st[1],st[2]);
+ break;
+ case O_XOR:
+ if (!sawvec || st[1]->str_nok || st[2]->str_nok) {
+ value = str_gnum(st[1]);
+#ifndef lint
+ value = (double)(U_L(value) ^ U_L(str_gnum(st[2])));
+#endif
+ goto donumset;
+ }
+ else
+ do_vop(optype,str,st[1],st[2]);
+ break;
+ case O_BIT_OR:
+ if (!sawvec || st[1]->str_nok || st[2]->str_nok) {
+ value = str_gnum(st[1]);
+#ifndef lint
+ value = (double)(U_L(value) | U_L(str_gnum(st[2])));
+#endif
+ goto donumset;
+ }
+ else
+ do_vop(optype,str,st[1],st[2]);
+ break;
+/* use register in evaluating str_true() */
+ case O_AND:
+ if (str_true(st[1])) {
+ anum = 2;
+ optype = O_ITEM2;
+ argflags = arg[anum].arg_flags;
+ if (gimme == G_ARRAY)
+ argflags |= AF_ARYOK;
+ argtype = arg[anum].arg_type & A_MASK;
+ argptr = arg[anum].arg_ptr;
+ maxarg = anum = 1;
+ sp = arglast[0];
+ st -= sp;
+ goto re_eval;
+ }
+ else {
+ if (assigning) {
+ str_sset(str, st[1]);
+ STABSET(str);
+ }
+ else
+ str = st[1];
+ break;
+ }
+ case O_OR:
+ if (str_true(st[1])) {
+ if (assigning) {
+ str_sset(str, st[1]);
+ STABSET(str);
+ }
+ else
+ str = st[1];
+ break;
+ }
+ else {
+ anum = 2;
+ optype = O_ITEM2;
+ argflags = arg[anum].arg_flags;
+ if (gimme == G_ARRAY)
+ argflags |= AF_ARYOK;
+ argtype = arg[anum].arg_type & A_MASK;
+ argptr = arg[anum].arg_ptr;
+ maxarg = anum = 1;
+ sp = arglast[0];
+ st -= sp;
+ goto re_eval;
+ }
+ case O_COND_EXPR:
+ anum = (str_true(st[1]) ? 2 : 3);
+ optype = (anum == 2 ? O_ITEM2 : O_ITEM3);
+ argflags = arg[anum].arg_flags;
+ if (gimme == G_ARRAY)
+ argflags |= AF_ARYOK;
+ argtype = arg[anum].arg_type & A_MASK;
+ argptr = arg[anum].arg_ptr;
+ maxarg = anum = 1;
+ sp = arglast[0];
+ st -= sp;
+ goto re_eval;
+ case O_COMMA:
+ if (gimme == G_ARRAY)
+ goto array_return;
+ str = st[2];
+ break;
+ case O_NEGATE:
+ value = -str_gnum(st[1]);
+ goto donumset;
+ case O_NOT:
+#ifdef NOTNOT
+ { char xxx = str_true(st[1]); value = (double) !xxx; }
+#else
+ value = (double) !str_true(st[1]);
+#endif
+ goto donumset;
+ case O_COMPLEMENT:
+ if (!sawvec || st[1]->str_nok) {
+#ifndef lint
+ value = (double) ~U_L(str_gnum(st[1]));
+#endif
+ goto donumset;
+ }
+ else {
+ STR_SSET(str,st[1]);
+ tmps = str_get(str);
+ for (anum = str->str_cur; anum; anum--, tmps++)
+ *tmps = ~*tmps;
+ }
+ break;
+ case O_SELECT:
+ stab_efullname(str,defoutstab);
+ if (maxarg > 0) {
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ defoutstab = arg[1].arg_ptr.arg_stab;
+ else
+ defoutstab = stabent(str_get(st[1]),TRUE);
+ if (!stab_io(defoutstab))
+ stab_io(defoutstab) = stio_new();
+ curoutstab = defoutstab;
+ }
+ STABSET(str);
+ break;
+ case O_WRITE:
+ if (maxarg == 0)
+ stab = defoutstab;
+ else if ((arg[1].arg_type & A_MASK) == A_WORD) {
+ if (!(stab = arg[1].arg_ptr.arg_stab))
+ stab = defoutstab;
+ }
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ if (!stab_io(stab)) {
+ str_set(str, No);
+ STABSET(str);
+ break;
+ }
+ curoutstab = stab;
+ fp = stab_io(stab)->ofp;
+ debarg = arg;
+ if (stab_io(stab)->fmt_stab)
+ form = stab_form(stab_io(stab)->fmt_stab);
+ else
+ form = stab_form(stab);
+ if (!form || !fp) {
+ if (dowarn) {
+ if (form)
+ warn("No format for filehandle");
+ else {
+ if (stab_io(stab)->ifp)
+ warn("Filehandle only opened for input");
+ else
+ warn("Write on closed filehandle");
+ }
+ }
+ str_set(str, No);
+ STABSET(str);
+ break;
+ }
+ format(&outrec,form,sp);
+ do_write(&outrec,stab,sp);
+ if (stab_io(stab)->flags & IOF_FLUSH)
+ (void)fflush(fp);
+ str_set(str, Yes);
+ STABSET(str);
+ break;
+ case O_DBMOPEN:
+#ifdef SOME_DBM
+ anum = arg[1].arg_type & A_MASK;
+ if (anum == A_WORD || anum == A_STAB)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ if (st[3]->str_nok || st[3]->str_pok)
+ anum = (int)str_gnum(st[3]);
+ else
+ anum = -1;
+ value = (double)hdbmopen(stab_hash(stab),str_get(st[2]),anum);
+ goto donumset;
+#else
+ fatal("No dbm or ndbm on this machine");
+#endif
+ case O_DBMCLOSE:
+#ifdef SOME_DBM
+ anum = arg[1].arg_type & A_MASK;
+ if (anum == A_WORD || anum == A_STAB)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ hdbmclose(stab_hash(stab));
+ goto say_yes;
+#else
+ fatal("No dbm or ndbm on this machine");
+#endif
+ case O_OPEN:
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ tmps = str_get(st[2]);
+ if (do_open(stab,tmps,st[2]->str_cur)) {
+ value = (double)forkprocess;
+ stab_io(stab)->lines = 0;
+ goto donumset;
+ }
+ else if (forkprocess == 0) /* we are a new child */
+ goto say_zero;
+ else
+ goto say_undef;
+ /* break; */
+ case O_TRANS:
+ value = (double) do_trans(str,arg);
+ str = arg->arg_ptr.arg_str;
+ goto donumset;
+ case O_NTRANS:
+ str_set(arg->arg_ptr.arg_str, do_trans(str,arg) == 0 ? Yes : No);
+ str = arg->arg_ptr.arg_str;
+ break;
+ case O_CLOSE:
+ if (maxarg == 0)
+ stab = defoutstab;
+ else if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ str_set(str, do_close(stab,TRUE) ? Yes : No );
+ STABSET(str);
+ break;
+ case O_EACH:
+ sp = do_each(str,stab_hash(arg[1].arg_ptr.arg_stab),
+ gimme,arglast);
+ goto array_return;
+ case O_VALUES:
+ case O_KEYS:
+ sp = do_kv(str,stab_hash(arg[1].arg_ptr.arg_stab), optype,
+ gimme,arglast);
+ goto array_return;
+ case O_LARRAY:
+ str->str_nok = str->str_pok = 0;
+ str->str_u.str_stab = arg[1].arg_ptr.arg_stab;
+ str->str_state = SS_ARY;
+ break;
+ case O_ARRAY:
+ ary = stab_array(arg[1].arg_ptr.arg_stab);
+ maxarg = ary->ary_fill + 1;
+ if (gimme == G_ARRAY) { /* array wanted */
+ sp = arglast[0];
+ st -= sp;
+ if (maxarg > 0 && sp + maxarg > stack->ary_max) {
+ astore(stack,sp + maxarg, Nullstr);
+ st = stack->ary_array;
+ }
+ st += sp;
+ Copy(ary->ary_array, &st[1], maxarg, STR*);
+ sp += maxarg;
+ goto array_return;
+ }
+ else {
+ value = (double)maxarg;
+ goto donumset;
+ }
+ case O_AELEM:
+ anum = ((int)str_gnum(st[2])) - arybase;
+ str = afetch(stab_array(arg[1].arg_ptr.arg_stab),anum,FALSE);
+ break;
+ case O_DELETE:
+ tmpstab = arg[1].arg_ptr.arg_stab;
+ tmps = str_get(st[2]);
+ str = hdelete(stab_hash(tmpstab),tmps,st[2]->str_cur);
+ if (tmpstab == envstab)
+ my_setenv(tmps,Nullch);
+ if (!str)
+ goto say_undef;
+ break;
+ case O_LHASH:
+ str->str_nok = str->str_pok = 0;
+ str->str_u.str_stab = arg[1].arg_ptr.arg_stab;
+ str->str_state = SS_HASH;
+ break;
+ case O_HASH:
+ if (gimme == G_ARRAY) { /* array wanted */
+ sp = do_kv(str,stab_hash(arg[1].arg_ptr.arg_stab), optype,
+ gimme,arglast);
+ goto array_return;
+ }
+ else {
+ tmpstab = arg[1].arg_ptr.arg_stab;
+ if (!stab_hash(tmpstab)->tbl_fill)
+ goto say_zero;
+ sprintf(buf,"%d/%d",stab_hash(tmpstab)->tbl_fill,
+ stab_hash(tmpstab)->tbl_max+1);
+ str_set(str,buf);
+ }
+ break;
+ case O_HELEM:
+ tmpstab = arg[1].arg_ptr.arg_stab;
+ tmps = str_get(st[2]);
+ str = hfetch(stab_hash(tmpstab),tmps,st[2]->str_cur,FALSE);
+ break;
+ case O_LAELEM:
+ anum = ((int)str_gnum(st[2])) - arybase;
+ str = afetch(stab_array(arg[1].arg_ptr.arg_stab),anum,TRUE);
+ if (!str || str == &str_undef)
+ fatal("Assignment to non-creatable value, subscript %d",anum);
+ break;
+ case O_LHELEM:
+ tmpstab = arg[1].arg_ptr.arg_stab;
+ tmps = str_get(st[2]);
+ anum = st[2]->str_cur;
+ str = hfetch(stab_hash(tmpstab),tmps,anum,TRUE);
+ if (!str || str == &str_undef)
+ fatal("Assignment to non-creatable value, subscript \"%s\"",tmps);
+ if (tmpstab == envstab) /* heavy wizardry going on here */
+ str_magic(str, tmpstab, 'E', tmps, anum); /* str is now magic */
+ /* he threw the brick up into the air */
+ else if (tmpstab == sigstab)
+ str_magic(str, tmpstab, 'S', tmps, anum);
+#ifdef SOME_DBM
+ else if (stab_hash(tmpstab)->tbl_dbm)
+ str_magic(str, tmpstab, 'D', tmps, anum);
+#endif
+ else if (tmpstab == DBline)
+ str_magic(str, tmpstab, 'L', tmps, anum);
+ break;
+ case O_LSLICE:
+ anum = 2;
+ argtype = FALSE;
+ goto do_slice_already;
+ case O_ASLICE:
+ anum = 1;
+ argtype = FALSE;
+ goto do_slice_already;
+ case O_HSLICE:
+ anum = 0;
+ argtype = FALSE;
+ goto do_slice_already;
+ case O_LASLICE:
+ anum = 1;
+ argtype = TRUE;
+ goto do_slice_already;
+ case O_LHSLICE:
+ anum = 0;
+ argtype = TRUE;
+ do_slice_already:
+ sp = do_slice(arg[1].arg_ptr.arg_stab,str,anum,argtype,
+ gimme,arglast);
+ goto array_return;
+ case O_SPLICE:
+ sp = do_splice(stab_array(arg[1].arg_ptr.arg_stab),gimme,arglast);
+ goto array_return;
+ case O_PUSH:
+ if (arglast[2] - arglast[1] != 1)
+ str = do_push(stab_array(arg[1].arg_ptr.arg_stab),arglast);
+ else {
+ str = Str_new(51,0); /* must copy the STR */
+ str_sset(str,st[2]);
+ (void)apush(stab_array(arg[1].arg_ptr.arg_stab),str);
+ }
+ break;
+ case O_POP:
+ str = apop(ary = stab_array(arg[1].arg_ptr.arg_stab));
+ goto staticalization;
+ case O_SHIFT:
+ str = ashift(ary = stab_array(arg[1].arg_ptr.arg_stab));
+ staticalization:
+ if (!str)
+ goto say_undef;
+ if (ary->ary_flags & ARF_REAL)
+ (void)str_2mortal(str);
+ break;
+ case O_UNPACK:
+ sp = do_unpack(str,gimme,arglast);
+ goto array_return;
+ case O_SPLIT:
+ value = str_gnum(st[3]);
+ sp = do_split(str, arg[2].arg_ptr.arg_spat, (int)value,
+ gimme,arglast);
+ goto array_return;
+ case O_LENGTH:
+ if (maxarg < 1)
+ value = (double)str_len(stab_val(defstab));
+ else
+ value = (double)str_len(st[1]);
+ goto donumset;
+ case O_SPRINTF:
+ do_sprintf(str, sp-arglast[0], st+1);
+ break;
+ case O_SUBSTR:
+ anum = ((int)str_gnum(st[2])) - arybase; /* anum=where to start*/
+ tmps = str_get(st[1]); /* force conversion to string */
+ /*SUPPRESS 560*/
+ if (argtype = (str == st[1]))
+ str = arg->arg_ptr.arg_str;
+ if (anum < 0)
+ anum += st[1]->str_cur + arybase;
+ if (anum < 0 || anum > st[1]->str_cur)
+ str_nset(str,"",0);
+ else {
+ optype = maxarg < 3 ? st[1]->str_cur : (int)str_gnum(st[3]);
+ if (optype < 0)
+ optype = 0;
+ tmps += anum;
+ anum = st[1]->str_cur - anum; /* anum=how many bytes left*/
+ if (anum > optype)
+ anum = optype;
+ str_nset(str, tmps, anum);
+ if (argtype) { /* it's an lvalue! */
+ lstr = (struct lstring*)str;
+ str->str_magic = st[1];
+ st[1]->str_rare = 's';
+ lstr->lstr_offset = tmps - str_get(st[1]);
+ lstr->lstr_len = anum;
+ }
+ }
+ break;
+ case O_PACK:
+ /*SUPPRESS 701*/
+ (void)do_pack(str,arglast);
+ break;
+ case O_GREP:
+ sp = do_grep(arg,str,gimme,arglast);
+ goto array_return;
+ case O_JOIN:
+ do_join(str,arglast);
+ break;
+ case O_SLT:
+ tmps = str_get(st[1]);
+ value = (double) (str_cmp(st[1],st[2]) < 0);
+ goto donumset;
+ case O_SGT:
+ tmps = str_get(st[1]);
+ value = (double) (str_cmp(st[1],st[2]) > 0);
+ goto donumset;
+ case O_SLE:
+ tmps = str_get(st[1]);
+ value = (double) (str_cmp(st[1],st[2]) <= 0);
+ goto donumset;
+ case O_SGE:
+ tmps = str_get(st[1]);
+ value = (double) (str_cmp(st[1],st[2]) >= 0);
+ goto donumset;
+ case O_SEQ:
+ tmps = str_get(st[1]);
+ value = (double) str_eq(st[1],st[2]);
+ goto donumset;
+ case O_SNE:
+ tmps = str_get(st[1]);
+ value = (double) !str_eq(st[1],st[2]);
+ goto donumset;
+ case O_SCMP:
+ tmps = str_get(st[1]);
+ value = (double) str_cmp(st[1],st[2]);
+ goto donumset;
+ case O_SUBR:
+ sp = do_subr(arg,gimme,arglast);
+ st = stack->ary_array + arglast[0]; /* maybe realloced */
+ goto array_return;
+ case O_DBSUBR:
+ sp = do_subr(arg,gimme,arglast);
+ st = stack->ary_array + arglast[0]; /* maybe realloced */
+ goto array_return;
+ case O_CALLER:
+ sp = do_caller(arg,maxarg,gimme,arglast);
+ st = stack->ary_array + arglast[0]; /* maybe realloced */
+ goto array_return;
+ case O_SORT:
+ sp = do_sort(str,arg,
+ gimme,arglast);
+ goto array_return;
+ case O_REVERSE:
+ if (gimme == G_ARRAY)
+ sp = do_reverse(arglast);
+ else
+ sp = do_sreverse(str, arglast);
+ goto array_return;
+ case O_WARN:
+ if (arglast[2] - arglast[1] != 1) {
+ do_join(str,arglast);
+ tmps = str_get(str);
+ }
+ else {
+ str = st[2];
+ tmps = str_get(st[2]);
+ }
+ if (!tmps || !*tmps)
+ tmps = "Warning: something's wrong";
+ warn("%s",tmps);
+ goto say_yes;
+ case O_DIE:
+ if (arglast[2] - arglast[1] != 1) {
+ do_join(str,arglast);
+ tmps = str_get(str);
+ }
+ else {
+ str = st[2];
+ tmps = str_get(st[2]);
+ }
+ if (!tmps || !*tmps)
+ tmps = "Died";
+ fatal("%s",tmps);
+ goto say_zero;
+ case O_PRTF:
+ case O_PRINT:
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ if (!stab)
+ stab = defoutstab;
+ if (!stab_io(stab)) {
+ if (dowarn)
+ warn("Filehandle never opened");
+ goto say_zero;
+ }
+ if (!(fp = stab_io(stab)->ofp)) {
+ if (dowarn) {
+ if (stab_io(stab)->ifp)
+ warn("Filehandle opened only for input");
+ else
+ warn("Print on closed filehandle");
+ }
+ goto say_zero;
+ }
+ else {
+ if (optype == O_PRTF || arglast[2] - arglast[1] != 1)
+ value = (double)do_aprint(arg,fp,arglast);
+ else {
+ value = (double)do_print(st[2],fp);
+ if (orslen && optype == O_PRINT)
+ if (fwrite(ors, 1, orslen, fp) == 0)
+ goto say_zero;
+ }
+ if (stab_io(stab)->flags & IOF_FLUSH)
+ if (fflush(fp) == EOF)
+ goto say_zero;
+ }
+ goto donumset;
+ case O_CHDIR:
+ if (maxarg < 1)
+ tmps = Nullch;
+ else
+ tmps = str_get(st[1]);
+ if (!tmps || !*tmps) {
+ tmpstr = hfetch(stab_hash(envstab),"HOME",4,FALSE);
+ tmps = str_get(tmpstr);
+ }
+ if (!tmps || !*tmps) {
+ tmpstr = hfetch(stab_hash(envstab),"LOGDIR",6,FALSE);
+ tmps = str_get(tmpstr);
+ }
+#ifdef TAINT
+ taintproper("Insecure dependency in chdir");
+#endif
+ value = (double)(chdir(tmps) >= 0);
+ goto donumset;
+ case O_EXIT:
+ if (maxarg < 1)
+ anum = 0;
+ else
+ anum = (int)str_gnum(st[1]);
+ exit(anum);
+ goto say_zero;
+ case O_RESET:
+ if (maxarg < 1)
+ tmps = "";
+ else
+ tmps = str_get(st[1]);
+ str_reset(tmps,curcmd->c_stash);
+ value = 1.0;
+ goto donumset;
+ case O_LIST:
+ if (gimme == G_ARRAY)
+ goto array_return;
+ if (maxarg > 0)
+ str = st[sp - arglast[0]]; /* unwanted list, return last item */
+ else
+ str = &str_undef;
+ break;
+ case O_EOF:
+ if (maxarg <= 0)
+ stab = last_in_stab;
+ else if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ str_set(str, do_eof(stab) ? Yes : No);
+ STABSET(str);
+ break;
+ case O_GETC:
+ if (maxarg <= 0)
+ stab = stdinstab;
+ else if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ if (!stab)
+ stab = argvstab;
+ if (!stab || do_eof(stab)) /* make sure we have fp with something */
+ goto say_undef;
+ else {
+#ifdef TAINT
+ tainted = 1;
+#endif
+ str_set(str," ");
+ *str->str_ptr = getc(stab_io(stab)->ifp); /* should never be EOF */
+ }
+ STABSET(str);
+ break;
+ case O_TELL:
+ if (maxarg <= 0)
+ stab = last_in_stab;
+ else if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+#ifndef lint
+ value = (double)do_tell(stab);
+#else
+ (void)do_tell(stab);
+#endif
+ goto donumset;
+ case O_RECV:
+ case O_READ:
+ case O_SYSREAD:
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ tmps = str_get(st[2]);
+ anum = (int)str_gnum(st[3]);
+ errno = 0;
+ maxarg = sp - arglast[0];
+ if (maxarg > 4)
+ warn("Too many args on read");
+ if (maxarg == 4)
+ maxarg = (int)str_gnum(st[4]);
+ else
+ maxarg = 0;
+ if (!stab_io(stab) || !stab_io(stab)->ifp)
+ goto say_undef;
+#ifdef HAS_SOCKET
+ if (optype == O_RECV) {
+ argtype = sizeof buf;
+ STR_GROW(st[2], anum+1), (tmps = str_get(st[2])); /* sneaky */
+ anum = recvfrom(fileno(stab_io(stab)->ifp), tmps, anum, maxarg,
+ buf, &argtype);
+ if (anum >= 0) {
+ st[2]->str_cur = anum;
+ st[2]->str_ptr[anum] = '\0';
+ str_nset(str,buf,argtype);
+ }
+ else
+ str_sset(str,&str_undef);
+ break;
+ }
+#else
+ if (optype == O_RECV)
+ goto badsock;
+#endif
+ STR_GROW(st[2], anum+maxarg+1), (tmps = str_get(st[2])); /* sneaky */
+ if (optype == O_SYSREAD) {
+ anum = read(fileno(stab_io(stab)->ifp), tmps+maxarg, anum);
+ }
+ else
+#ifdef HAS_SOCKET
+ if (stab_io(stab)->type == 's') {
+ argtype = sizeof buf;
+ anum = recvfrom(fileno(stab_io(stab)->ifp), tmps+maxarg, anum, 0,
+ buf, &argtype);
+ }
+ else
+#endif
+ anum = fread(tmps+maxarg, 1, anum, stab_io(stab)->ifp);
+ if (anum < 0)
+ goto say_undef;
+ st[2]->str_cur = anum+maxarg;
+ st[2]->str_ptr[anum+maxarg] = '\0';
+ value = (double)anum;
+ goto donumset;
+ case O_SYSWRITE:
+ case O_SEND:
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ tmps = str_get(st[2]);
+ anum = (int)str_gnum(st[3]);
+ errno = 0;
+ stio = stab_io(stab);
+ maxarg = sp - arglast[0];
+ if (!stio || !stio->ifp) {
+ anum = -1;
+ if (dowarn) {
+ if (optype == O_SYSWRITE)
+ warn("Syswrite on closed filehandle");
+ else
+ warn("Send on closed socket");
+ }
+ }
+ else if (optype == O_SYSWRITE) {
+ if (maxarg > 4)
+ warn("Too many args on syswrite");
+ if (maxarg == 4)
+ optype = (int)str_gnum(st[4]);
+ else
+ optype = 0;
+ anum = write(fileno(stab_io(stab)->ifp), tmps+optype, anum);
+ }
+#ifdef HAS_SOCKET
+ else if (maxarg >= 4) {
+ if (maxarg > 4)
+ warn("Too many args on send");
+ tmps2 = str_get(st[4]);
+ anum = sendto(fileno(stab_io(stab)->ifp), tmps, st[2]->str_cur,
+ anum, tmps2, st[4]->str_cur);
+ }
+ else
+ anum = send(fileno(stab_io(stab)->ifp), tmps, st[2]->str_cur, anum);
+#else
+ else
+ goto badsock;
+#endif
+ if (anum < 0)
+ goto say_undef;
+ value = (double)anum;
+ goto donumset;
+ case O_SEEK:
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ value = str_gnum(st[2]);
+ str_set(str, do_seek(stab,
+ (long)value, (int)str_gnum(st[3]) ) ? Yes : No);
+ STABSET(str);
+ break;
+ case O_RETURN:
+ tmps = "_SUB_"; /* just fake up a "last _SUB_" */
+ optype = O_LAST;
+ if (curcsv && curcsv->wantarray == G_ARRAY) {
+ lastretstr = Nullstr;
+ lastspbase = arglast[1];
+ lastsize = arglast[2] - arglast[1];
+ }
+ else
+ lastretstr = str_mortal(st[arglast[2] - arglast[0]]);
+ goto dopop;
+ case O_REDO:
+ case O_NEXT:
+ case O_LAST:
+ tmps = Nullch;
+ if (maxarg > 0) {
+ tmps = str_get(arg[1].arg_ptr.arg_str);
+ dopop:
+ while (loop_ptr >= 0 && (!loop_stack[loop_ptr].loop_label ||
+ strNE(tmps,loop_stack[loop_ptr].loop_label) )) {
+#ifdef DEBUGGING
+ if (debug & 4) {
+ deb("(Skipping label #%d %s)\n",loop_ptr,
+ loop_stack[loop_ptr].loop_label);
+ }
+#endif
+ loop_ptr--;
+ }
+#ifdef DEBUGGING
+ if (debug & 4) {
+ deb("(Found label #%d %s)\n",loop_ptr,
+ loop_stack[loop_ptr].loop_label);
+ }
+#endif
+ }
+ if (loop_ptr < 0) {
+ if (tmps && strEQ(tmps, "_SUB_"))
+ fatal("Can't return outside a subroutine");
+ fatal("Bad label: %s", maxarg > 0 ? tmps : "<null>");
+ }
+ if (!lastretstr && optype == O_LAST && lastsize) {
+ st -= arglast[0];
+ st += lastspbase + 1;
+ optype = loop_stack[loop_ptr].loop_sp - lastspbase; /* negative */
+ if (optype) {
+ for (anum = lastsize; anum > 0; anum--,st++)
+ st[optype] = str_mortal(st[0]);
+ }
+ longjmp(loop_stack[loop_ptr].loop_env, O_LAST);
+ }
+ longjmp(loop_stack[loop_ptr].loop_env, optype);
+ case O_DUMP:
+ case O_GOTO:/* shudder */
+ goto_targ = str_get(arg[1].arg_ptr.arg_str);
+ if (!*goto_targ)
+ goto_targ = Nullch; /* just restart from top */
+ if (optype == O_DUMP) {
+ do_undump = 1;
+ my_unexec();
+ }
+ longjmp(top_env, 1);
+ case O_INDEX:
+ tmps = str_get(st[1]);
+ if (maxarg < 3)
+ anum = 0;
+ else {
+ anum = (int) str_gnum(st[3]) - arybase;
+ if (anum < 0)
+ anum = 0;
+ else if (anum > st[1]->str_cur)
+ anum = st[1]->str_cur;
+ }
+#ifndef lint
+ if (!(tmps2 = fbminstr((unsigned char*)tmps + anum,
+ (unsigned char*)tmps + st[1]->str_cur, st[2])))
+#else
+ if (tmps2 = fbminstr(Null(unsigned char*),Null(unsigned char*),Nullstr))
+#endif
+ value = (double)(-1 + arybase);
+ else
+ value = (double)(tmps2 - tmps + arybase);
+ goto donumset;
+ case O_RINDEX:
+ tmps = str_get(st[1]);
+ tmps2 = str_get(st[2]);
+ if (maxarg < 3)
+ anum = st[1]->str_cur;
+ else {
+ anum = (int) str_gnum(st[3]) - arybase + st[2]->str_cur;
+ if (anum < 0)
+ anum = 0;
+ else if (anum > st[1]->str_cur)
+ anum = st[1]->str_cur;
+ }
+#ifndef lint
+ if (!(tmps2 = rninstr(tmps, tmps + anum,
+ tmps2, tmps2 + st[2]->str_cur)))
+#else
+ if (tmps2 = rninstr(Nullch,Nullch,Nullch,Nullch))
+#endif
+ value = (double)(-1 + arybase);
+ else
+ value = (double)(tmps2 - tmps + arybase);
+ goto donumset;
+ case O_TIME:
+#ifndef lint
+ value = (double) time(Null(long*));
+#endif
+ goto donumset;
+ case O_TMS:
+ sp = do_tms(str,gimme,arglast);
+ goto array_return;
+ case O_LOCALTIME:
+ if (maxarg < 1)
+ (void)time(&when);
+ else
+ when = (time_t)str_gnum(st[1]);
+ sp = do_time(str,localtime(&when),
+ gimme,arglast);
+ goto array_return;
+ case O_GMTIME:
+ if (maxarg < 1)
+ (void)time(&when);
+ else
+ when = (time_t)str_gnum(st[1]);
+ sp = do_time(str,gmtime(&when),
+ gimme,arglast);
+ goto array_return;
+ case O_TRUNCATE:
+ sp = do_truncate(str,arg,
+ gimme,arglast);
+ goto array_return;
+ case O_LSTAT:
+ case O_STAT:
+ sp = do_stat(str,arg,
+ gimme,arglast);
+ goto array_return;
+ case O_CRYPT:
+#ifdef HAS_CRYPT
+ tmps = str_get(st[1]);
+#ifdef FCRYPT
+ str_set(str,fcrypt(tmps,str_get(st[2])));
+#else
+ str_set(str,crypt(tmps,str_get(st[2])));
+#endif
+#else
+ fatal(
+ "The crypt() function is unimplemented due to excessive paranoia.");
+#endif
+ break;
+ case O_ATAN2:
+ value = str_gnum(st[1]);
+ value = atan2(value,str_gnum(st[2]));
+ goto donumset;
+ case O_SIN:
+ if (maxarg < 1)
+ value = str_gnum(stab_val(defstab));
+ else
+ value = str_gnum(st[1]);
+ value = sin(value);
+ goto donumset;
+ case O_COS:
+ if (maxarg < 1)
+ value = str_gnum(stab_val(defstab));
+ else
+ value = str_gnum(st[1]);
+ value = cos(value);
+ goto donumset;
+ case O_RAND:
+ if (maxarg < 1)
+ value = 1.0;
+ else
+ value = str_gnum(st[1]);
+ if (value == 0.0)
+ value = 1.0;
+#if RANDBITS == 31
+ value = rand() * value / 2147483648.0;
+#else
+#if RANDBITS == 16
+ value = rand() * value / 65536.0;
+#else
+#if RANDBITS == 15
+ value = rand() * value / 32768.0;
+#else
+ value = rand() * value / (double)(((unsigned long)1) << RANDBITS);
+#endif
+#endif
+#endif
+ goto donumset;
+ case O_SRAND:
+ if (maxarg < 1) {
+ (void)time(&when);
+ anum = when;
+ }
+ else
+ anum = (int)str_gnum(st[1]);
+ (void)srand(anum);
+ goto say_yes;
+ case O_EXP:
+ if (maxarg < 1)
+ value = str_gnum(stab_val(defstab));
+ else
+ value = str_gnum(st[1]);
+ value = exp(value);
+ goto donumset;
+ case O_LOG:
+ if (maxarg < 1)
+ value = str_gnum(stab_val(defstab));
+ else
+ value = str_gnum(st[1]);
+ if (value <= 0.0)
+ fatal("Can't take log of %g\n", value);
+ value = log(value);
+ goto donumset;
+ case O_SQRT:
+ if (maxarg < 1)
+ value = str_gnum(stab_val(defstab));
+ else
+ value = str_gnum(st[1]);
+ if (value < 0.0)
+ fatal("Can't take sqrt of %g\n", value);
+ value = sqrt(value);
+ goto donumset;
+ case O_INT:
+ if (maxarg < 1)
+ value = str_gnum(stab_val(defstab));
+ else
+ value = str_gnum(st[1]);
+ if (value >= 0.0)
+ (void)modf(value,&value);
+ else {
+ (void)modf(-value,&value);
+ value = -value;
+ }
+ goto donumset;
+ case O_ORD:
+ if (maxarg < 1)
+ tmps = str_get(stab_val(defstab));
+ else
+ tmps = str_get(st[1]);
+#ifndef I286
+ value = (double) (*tmps & 255);
+#else
+ anum = (int) *tmps;
+ value = (double) (anum & 255);
+#endif
+ goto donumset;
+ case O_ALARM:
+#ifdef HAS_ALARM
+ if (maxarg < 1)
+ tmps = str_get(stab_val(defstab));
+ else
+ tmps = str_get(st[1]);
+ if (!tmps)
+ tmps = "0";
+ anum = alarm((unsigned int)atoi(tmps));
+ if (anum < 0)
+ goto say_undef;
+ value = (double)anum;
+ goto donumset;
+#else
+ fatal("Unsupported function alarm");
+ break;
+#endif
+ case O_SLEEP:
+ if (maxarg < 1)
+ tmps = Nullch;
+ else
+ tmps = str_get(st[1]);
+ (void)time(&when);
+ if (!tmps || !*tmps)
+ sleep((32767<<16)+32767);
+ else
+ sleep((unsigned int)atoi(tmps));
+#ifndef lint
+ value = (double)when;
+ (void)time(&when);
+ value = ((double)when) - value;
+#endif
+ goto donumset;
+ case O_RANGE:
+ sp = do_range(gimme,arglast);
+ goto array_return;
+ case O_F_OR_R:
+ if (gimme == G_ARRAY) { /* it's a range */
+ /* can we optimize to constant array? */
+ if ((arg[1].arg_type & A_MASK) == A_SINGLE &&
+ (arg[2].arg_type & A_MASK) == A_SINGLE) {
+ st[2] = arg[2].arg_ptr.arg_str;
+ sp = do_range(gimme,arglast);
+ st = stack->ary_array;
+ maxarg = sp - arglast[0];
+ str_free(arg[1].arg_ptr.arg_str);
+ arg[1].arg_ptr.arg_str = Nullstr;
+ str_free(arg[2].arg_ptr.arg_str);
+ arg[2].arg_ptr.arg_str = Nullstr;
+ arg->arg_type = O_ARRAY;
+ arg[1].arg_type = A_STAB|A_DONT;
+ arg->arg_len = 1;
+ stab = arg[1].arg_ptr.arg_stab = aadd(genstab());
+ ary = stab_array(stab);
+ afill(ary,maxarg - 1);
+ anum = maxarg;
+ st += arglast[0]+1;
+ while (maxarg-- > 0)
+ ary->ary_array[maxarg] = str_smake(st[maxarg]);
+ st -= arglast[0]+1;
+ goto array_return;
+ }
+ arg->arg_type = optype = O_RANGE;
+ maxarg = arg->arg_len = 2;
+ anum = 2;
+ arg[anum].arg_flags &= ~AF_ARYOK;
+ argflags = arg[anum].arg_flags;
+ argtype = arg[anum].arg_type & A_MASK;
+ arg[anum].arg_type = argtype;
+ argptr = arg[anum].arg_ptr;
+ sp = arglast[0];
+ st -= sp;
+ sp++;
+ goto re_eval;
+ }
+ arg->arg_type = O_FLIP;
+ /* FALL THROUGH */
+ case O_FLIP:
+ if ((arg[1].arg_type & A_MASK) == A_SINGLE ?
+ last_in_stab && (int)str_gnum(st[1]) == stab_io(last_in_stab)->lines
+ :
+ str_true(st[1]) ) {
+ arg[2].arg_type &= ~A_DONT;
+ arg[1].arg_type |= A_DONT;
+ arg->arg_type = optype = O_FLOP;
+ if (arg->arg_flags & AF_COMMON) {
+ str_numset(str,0.0);
+ anum = 2;
+ argflags = arg[2].arg_flags;
+ argtype = arg[2].arg_type & A_MASK;
+ argptr = arg[2].arg_ptr;
+ sp = arglast[0];
+ st -= sp++;
+ goto re_eval;
+ }
+ else {
+ str_numset(str,1.0);
+ break;
+ }
+ }
+ str_set(str,"");
+ break;
+ case O_FLOP:
+ str_inc(str);
+ if ((arg[2].arg_type & A_MASK) == A_SINGLE ?
+ last_in_stab && (int)str_gnum(st[2]) == stab_io(last_in_stab)->lines
+ :
+ str_true(st[2]) ) {
+ arg->arg_type = O_FLIP;
+ arg[1].arg_type &= ~A_DONT;
+ arg[2].arg_type |= A_DONT;
+ str_cat(str,"E0");
+ }
+ break;
+ case O_FORK:
+#ifdef HAS_FORK
+ anum = fork();
+ if (anum < 0)
+ goto say_undef;
+ if (!anum) {
+ /*SUPPRESS 560*/
+ if (tmpstab = stabent("$",allstabs))
+ str_numset(STAB_STR(tmpstab),(double)getpid());
+ hclear(pidstatus, FALSE); /* no kids, so don't wait for 'em */
+ }
+ value = (double)anum;
+ goto donumset;
+#else
+ fatal("Unsupported function fork");
+ break;
+#endif
+ case O_WAIT:
+#ifdef HAS_WAIT
+#ifndef lint
+ anum = wait(&argflags);
+ if (anum > 0)
+ pidgone(anum,argflags);
+ value = (double)anum;
+#endif
+ statusvalue = (unsigned short)argflags;
+ goto donumset;
+#else
+ fatal("Unsupported function wait");
+ break;
+#endif
+ case O_WAITPID:
+#ifdef HAS_WAIT
+#ifndef lint
+ anum = (int)str_gnum(st[1]);
+ optype = (int)str_gnum(st[2]);
+ anum = wait4pid(anum, &argflags,optype);
+ value = (double)anum;
+#endif
+ statusvalue = (unsigned short)argflags;
+ goto donumset;
+#else
+ fatal("Unsupported function wait");
+ break;
+#endif
+ case O_SYSTEM:
+#ifdef HAS_FORK
+#ifdef TAINT
+ if (arglast[2] - arglast[1] == 1) {
+ taintenv();
+ tainted |= st[2]->str_tainted;
+ taintproper("Insecure dependency in system");
+ }
+#endif
+ while ((anum = vfork()) == -1) {
+ if (errno != EAGAIN) {
+ value = -1.0;
+ goto donumset;
+ }
+ sleep(5);
+ }
+ if (anum > 0) {
+#ifndef lint
+ ihand = signal(SIGINT, SIG_IGN);
+ qhand = signal(SIGQUIT, SIG_IGN);
+ argtype = wait4pid(anum, &argflags, 0);
+#else
+ ihand = qhand = 0;
+#endif
+ (void)signal(SIGINT, ihand);
+ (void)signal(SIGQUIT, qhand);
+ statusvalue = (unsigned short)argflags;
+ if (argtype < 0)
+ value = -1.0;
+ else {
+ value = (double)((unsigned int)argflags & 0xffff);
+ }
+ do_execfree(); /* free any memory child malloced on vfork */
+ goto donumset;
+ }
+ if ((arg[1].arg_type & A_MASK) == A_STAB)
+ value = (double)do_aexec(st[1],arglast);
+ else if (arglast[2] - arglast[1] != 1)
+ value = (double)do_aexec(Nullstr,arglast);
+ else {
+ value = (double)do_exec(str_get(str_mortal(st[2])));
+ }
+ _exit(-1);
+#else /* ! FORK */
+ if ((arg[1].arg_type & A_MASK) == A_STAB)
+ value = (double)do_aspawn(st[1],arglast);
+ else if (arglast[2] - arglast[1] != 1)
+ value = (double)do_aspawn(Nullstr,arglast);
+ else {
+ value = (double)do_spawn(str_get(str_mortal(st[2])));
+ }
+ goto donumset;
+#endif /* FORK */
+ case O_EXEC_OP:
+ if ((arg[1].arg_type & A_MASK) == A_STAB)
+ value = (double)do_aexec(st[1],arglast);
+ else if (arglast[2] - arglast[1] != 1)
+ value = (double)do_aexec(Nullstr,arglast);
+ else {
+#ifdef TAINT
+ taintenv();
+ tainted |= st[2]->str_tainted;
+ taintproper("Insecure dependency in exec");
+#endif
+ value = (double)do_exec(str_get(str_mortal(st[2])));
+ }
+ goto donumset;
+ case O_HEX:
+ if (maxarg < 1)
+ tmps = str_get(stab_val(defstab));
+ else
+ tmps = str_get(st[1]);
+ value = (double)scanhex(tmps, 99, &argtype);
+ goto donumset;
+
+ case O_OCT:
+ if (maxarg < 1)
+ tmps = str_get(stab_val(defstab));
+ else
+ tmps = str_get(st[1]);
+ while (*tmps && (isSPACE(*tmps) || *tmps == '0'))
+ tmps++;
+ if (*tmps == 'x')
+ value = (double)scanhex(++tmps, 99, &argtype);
+ else
+ value = (double)scanoct(tmps, 99, &argtype);
+ goto donumset;
+
+/* These common exits are hidden here in the middle of the switches for the
+ benefit of those machines with limited branch addressing. Sigh. */
+
+array_return:
+#ifdef DEBUGGING
+ if (debug) {
+ dlevel--;
+ if (debug & 8) {
+ anum = sp - arglast[0];
+ switch (anum) {
+ case 0:
+ deb("%s RETURNS ()\n",opname[optype]);
+ break;
+ case 1:
+ deb("%s RETURNS (\"%s\")\n",opname[optype],
+ st[1] ? str_get(st[1]) : "");
+ break;
+ default:
+ tmps = st[1] ? str_get(st[1]) : "";
+ deb("%s RETURNS %d ARGS (\"%s\",%s\"%s\")\n",opname[optype],
+ anum,tmps,anum==2?"":"...,",
+ st[anum] ? str_get(st[anum]) : "");
+ break;
+ }
+ }
+ }
+#endif
+ return sp;
+
+say_yes:
+ str = &str_yes;
+ goto normal_return;
+
+say_no:
+ str = &str_no;
+ goto normal_return;
+
+say_undef:
+ str = &str_undef;
+ goto normal_return;
+
+say_zero:
+ value = 0.0;
+ /* FALL THROUGH */
+
+donumset:
+ str_numset(str,value);
+ STABSET(str);
+ st[1] = str;
+#ifdef DEBUGGING
+ if (debug) {
+ dlevel--;
+ if (debug & 8)
+ deb("%s RETURNS \"%f\"\n",opname[optype],value);
+ }
+#endif
+ return arglast[0] + 1;
+#ifdef SMALLSWITCHES
+ }
+ else
+ switch (optype) {
+#endif
+ case O_CHOWN:
+#ifdef HAS_CHOWN
+ value = (double)apply(optype,arglast);
+ goto donumset;
+#else
+ fatal("Unsupported function chown");
+ break;
+#endif
+ case O_KILL:
+#ifdef HAS_KILL
+ value = (double)apply(optype,arglast);
+ goto donumset;
+#else
+ fatal("Unsupported function kill");
+ break;
+#endif
+ case O_UNLINK:
+ case O_CHMOD:
+ case O_UTIME:
+ value = (double)apply(optype,arglast);
+ goto donumset;
+ case O_UMASK:
+#ifdef HAS_UMASK
+ if (maxarg < 1) {
+ anum = umask(0);
+ (void)umask(anum);
+ }
+ else
+ anum = umask((int)str_gnum(st[1]));
+ value = (double)anum;
+#ifdef TAINT
+ taintproper("Insecure dependency in umask");
+#endif
+ goto donumset;
+#else
+ fatal("Unsupported function umask");
+ break;
+#endif
+#if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM)
+ case O_MSGGET:
+ case O_SHMGET:
+ case O_SEMGET:
+ if ((anum = do_ipcget(optype, arglast)) == -1)
+ goto say_undef;
+ value = (double)anum;
+ goto donumset;
+ case O_MSGCTL:
+ case O_SHMCTL:
+ case O_SEMCTL:
+ anum = do_ipcctl(optype, arglast);
+ if (anum == -1)
+ goto say_undef;
+ if (anum != 0) {
+ value = (double)anum;
+ goto donumset;
+ }
+ str_set(str,"0 but true");
+ STABSET(str);
+ break;
+ case O_MSGSND:
+ value = (double)(do_msgsnd(arglast) >= 0);
+ goto donumset;
+ case O_MSGRCV:
+ value = (double)(do_msgrcv(arglast) >= 0);
+ goto donumset;
+ case O_SEMOP:
+ value = (double)(do_semop(arglast) >= 0);
+ goto donumset;
+ case O_SHMREAD:
+ case O_SHMWRITE:
+ value = (double)(do_shmio(optype, arglast) >= 0);
+ goto donumset;
+#else /* not SYSVIPC */
+ case O_MSGGET:
+ case O_MSGCTL:
+ case O_MSGSND:
+ case O_MSGRCV:
+ case O_SEMGET:
+ case O_SEMCTL:
+ case O_SEMOP:
+ case O_SHMGET:
+ case O_SHMCTL:
+ case O_SHMREAD:
+ case O_SHMWRITE:
+ fatal("System V IPC is not implemented on this machine");
+#endif /* not SYSVIPC */
+ case O_RENAME:
+ tmps = str_get(st[1]);
+ tmps2 = str_get(st[2]);
+#ifdef TAINT
+ taintproper("Insecure dependency in rename");
+#endif
+#ifdef HAS_RENAME
+ value = (double)(rename(tmps,tmps2) >= 0);
+#else
+ if (same_dirent(tmps2, tmps)) /* can always rename to same name */
+ anum = 1;
+ else {
+ if (euid || stat(tmps2,&statbuf) < 0 || !S_ISDIR(statbuf.st_mode))
+ (void)UNLINK(tmps2);
+ if (!(anum = link(tmps,tmps2)))
+ anum = UNLINK(tmps);
+ }
+ value = (double)(anum >= 0);
+#endif
+ goto donumset;
+ case O_LINK:
+#ifdef HAS_LINK
+ tmps = str_get(st[1]);
+ tmps2 = str_get(st[2]);
+#ifdef TAINT
+ taintproper("Insecure dependency in link");
+#endif
+ value = (double)(link(tmps,tmps2) >= 0);
+ goto donumset;
+#else
+ fatal("Unsupported function link");
+ break;
+#endif
+ case O_MKDIR:
+ tmps = str_get(st[1]);
+ anum = (int)str_gnum(st[2]);
+#ifdef TAINT
+ taintproper("Insecure dependency in mkdir");
+#endif
+#ifdef HAS_MKDIR
+ value = (double)(mkdir(tmps,anum) >= 0);
+ goto donumset;
+#else
+ (void)strcpy(buf,"mkdir ");
+#endif
+#if !defined(HAS_MKDIR) || !defined(HAS_RMDIR)
+ one_liner:
+ for (tmps2 = buf+6; *tmps; ) {
+ *tmps2++ = '\\';
+ *tmps2++ = *tmps++;
+ }
+ (void)strcpy(tmps2," 2>&1");
+ rsfp = mypopen(buf,"r");
+ if (rsfp) {
+ *buf = '\0';
+ tmps2 = fgets(buf,sizeof buf,rsfp);
+ (void)mypclose(rsfp);
+ if (tmps2 != Nullch) {
+ for (errno = 1; errno < sys_nerr; errno++) {
+ if (instr(buf,sys_errlist[errno])) /* you don't see this */
+ goto say_zero;
+ }
+ errno = 0;
+#ifndef EACCES
+#define EACCES EPERM
+#endif
+ if (instr(buf,"cannot make"))
+ errno = EEXIST;
+ else if (instr(buf,"existing file"))
+ errno = EEXIST;
+ else if (instr(buf,"ile exists"))
+ errno = EEXIST;
+ else if (instr(buf,"non-exist"))
+ errno = ENOENT;
+ else if (instr(buf,"does not exist"))
+ errno = ENOENT;
+ else if (instr(buf,"not empty"))
+ errno = EBUSY;
+ else if (instr(buf,"cannot access"))
+ errno = EACCES;
+ else
+ errno = EPERM;
+ goto say_zero;
+ }
+ else { /* some mkdirs return no failure indication */
+ tmps = str_get(st[1]);
+ anum = (stat(tmps,&statbuf) >= 0);
+ if (optype == O_RMDIR)
+ anum = !anum;
+ if (anum)
+ errno = 0;
+ else
+ errno = EACCES; /* a guess */
+ value = (double)anum;
+ }
+ goto donumset;
+ }
+ else
+ goto say_zero;
+#endif
+ case O_RMDIR:
+ if (maxarg < 1)
+ tmps = str_get(stab_val(defstab));
+ else
+ tmps = str_get(st[1]);
+#ifdef TAINT
+ taintproper("Insecure dependency in rmdir");
+#endif
+#ifdef HAS_RMDIR
+ value = (double)(rmdir(tmps) >= 0);
+ goto donumset;
+#else
+ (void)strcpy(buf,"rmdir ");
+ goto one_liner; /* see above in HAS_MKDIR */
+#endif
+ case O_GETPPID:
+#ifdef HAS_GETPPID
+ value = (double)getppid();
+ goto donumset;
+#else
+ fatal("Unsupported function getppid");
+ break;
+#endif
+ case O_GETPGRP:
+#ifdef HAS_GETPGRP
+ if (maxarg < 1)
+ anum = 0;
+ else
+ anum = (int)str_gnum(st[1]);
+#ifdef _POSIX_SOURCE
+ if (anum != 0)
+ fatal("POSIX getpgrp can't take an argument");
+ value = (double)getpgrp();
+#else
+ value = (double)getpgrp(anum);
+#endif
+ goto donumset;
+#else
+ fatal("The getpgrp() function is unimplemented on this machine");
+ break;
+#endif
+ case O_SETPGRP:
+#ifdef HAS_SETPGRP
+ argtype = (int)str_gnum(st[1]);
+ anum = (int)str_gnum(st[2]);
+#ifdef TAINT
+ taintproper("Insecure dependency in setpgrp");
+#endif
+ value = (double)(setpgrp(argtype,anum) >= 0);
+ goto donumset;
+#else
+ fatal("The setpgrp() function is unimplemented on this machine");
+ break;
+#endif
+ case O_GETPRIORITY:
+#ifdef HAS_GETPRIORITY
+ argtype = (int)str_gnum(st[1]);
+ anum = (int)str_gnum(st[2]);
+ value = (double)getpriority(argtype,anum);
+ goto donumset;
+#else
+ fatal("The getpriority() function is unimplemented on this machine");
+ break;
+#endif
+ case O_SETPRIORITY:
+#ifdef HAS_SETPRIORITY
+ argtype = (int)str_gnum(st[1]);
+ anum = (int)str_gnum(st[2]);
+ optype = (int)str_gnum(st[3]);
+#ifdef TAINT
+ taintproper("Insecure dependency in setpriority");
+#endif
+ value = (double)(setpriority(argtype,anum,optype) >= 0);
+ goto donumset;
+#else
+ fatal("The setpriority() function is unimplemented on this machine");
+ break;
+#endif
+ case O_CHROOT:
+#ifdef HAS_CHROOT
+ if (maxarg < 1)
+ tmps = str_get(stab_val(defstab));
+ else
+ tmps = str_get(st[1]);
+#ifdef TAINT
+ taintproper("Insecure dependency in chroot");
+#endif
+ value = (double)(chroot(tmps) >= 0);
+ goto donumset;
+#else
+ fatal("Unsupported function chroot");
+ break;
+#endif
+ case O_FCNTL:
+ case O_IOCTL:
+ if (maxarg <= 0)
+ stab = last_in_stab;
+ else if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ argtype = U_I(str_gnum(st[2]));
+#ifdef TAINT
+ taintproper("Insecure dependency in ioctl");
+#endif
+ anum = do_ctl(optype,stab,argtype,st[3]);
+ if (anum == -1)
+ goto say_undef;
+ if (anum != 0) {
+ value = (double)anum;
+ goto donumset;
+ }
+ str_set(str,"0 but true");
+ STABSET(str);
+ break;
+ case O_FLOCK:
+#ifdef HAS_FLOCK
+ if (maxarg <= 0)
+ stab = last_in_stab;
+ else if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ if (stab && stab_io(stab))
+ fp = stab_io(stab)->ifp;
+ else
+ fp = Nullfp;
+ if (fp) {
+ argtype = (int)str_gnum(st[2]);
+ value = (double)(flock(fileno(fp),argtype) >= 0);
+ }
+ else
+ value = 0;
+ goto donumset;
+#else
+ fatal("The flock() function is unimplemented on this machine");
+ break;
+#endif
+ case O_UNSHIFT:
+ ary = stab_array(arg[1].arg_ptr.arg_stab);
+ if (arglast[2] - arglast[1] != 1)
+ do_unshift(ary,arglast);
+ else {
+ STR *tmpstr = Str_new(52,0); /* must copy the STR */
+ str_sset(tmpstr,st[2]);
+ aunshift(ary,1);
+ (void)astore(ary,0,tmpstr);
+ }
+ value = (double)(ary->ary_fill + 1);
+ goto donumset;
+
+ case O_TRY:
+ sp = do_try(arg[1].arg_ptr.arg_cmd,
+ gimme,arglast);
+ goto array_return;
+
+ case O_EVALONCE:
+ sp = do_eval(st[1], O_EVAL, curcmd->c_stash, TRUE,
+ gimme,arglast);
+ if (eval_root) {
+ str_free(arg[1].arg_ptr.arg_str);
+ arg[1].arg_ptr.arg_cmd = eval_root;
+ arg[1].arg_type = (A_CMD|A_DONT);
+ arg[0].arg_type = O_TRY;
+ }
+ goto array_return;
+
+ case O_REQUIRE:
+ case O_DOFILE:
+ case O_EVAL:
+ if (maxarg < 1)
+ tmpstr = stab_val(defstab);
+ else
+ tmpstr =
+ (arg[1].arg_type & A_MASK) != A_NULL ? st[1] : stab_val(defstab);
+#ifdef TAINT
+ tainted |= tmpstr->str_tainted;
+ taintproper("Insecure dependency in eval");
+#endif
+ sp = do_eval(tmpstr, optype, curcmd->c_stash, FALSE,
+ gimme,arglast);
+ goto array_return;
+
+ case O_FTRREAD:
+ argtype = 0;
+ anum = S_IRUSR;
+ goto check_perm;
+ case O_FTRWRITE:
+ argtype = 0;
+ anum = S_IWUSR;
+ goto check_perm;
+ case O_FTREXEC:
+ argtype = 0;
+ anum = S_IXUSR;
+ goto check_perm;
+ case O_FTEREAD:
+ argtype = 1;
+ anum = S_IRUSR;
+ goto check_perm;
+ case O_FTEWRITE:
+ argtype = 1;
+ anum = S_IWUSR;
+ goto check_perm;
+ case O_FTEEXEC:
+ argtype = 1;
+ anum = S_IXUSR;
+ check_perm:
+ if (mystat(arg,st[1]) < 0)
+ goto say_undef;
+ if (cando(anum,argtype,&statcache))
+ goto say_yes;
+ goto say_no;
+
+ case O_FTIS:
+ if (mystat(arg,st[1]) < 0)
+ goto say_undef;
+ goto say_yes;
+ case O_FTEOWNED:
+ case O_FTROWNED:
+ if (mystat(arg,st[1]) < 0)
+ goto say_undef;
+ if (statcache.st_uid == (optype == O_FTEOWNED ? euid : uid) )
+ goto say_yes;
+ goto say_no;
+ case O_FTZERO:
+ if (mystat(arg,st[1]) < 0)
+ goto say_undef;
+ if (!statcache.st_size)
+ goto say_yes;
+ goto say_no;
+ case O_FTSIZE:
+ if (mystat(arg,st[1]) < 0)
+ goto say_undef;
+ value = (double)statcache.st_size;
+ goto donumset;
+
+ case O_FTMTIME:
+ if (mystat(arg,st[1]) < 0)
+ goto say_undef;
+ value = (double)(basetime - statcache.st_mtime) / 86400.0;
+ goto donumset;
+ case O_FTATIME:
+ if (mystat(arg,st[1]) < 0)
+ goto say_undef;
+ value = (double)(basetime - statcache.st_atime) / 86400.0;
+ goto donumset;
+ case O_FTCTIME:
+ if (mystat(arg,st[1]) < 0)
+ goto say_undef;
+ value = (double)(basetime - statcache.st_ctime) / 86400.0;
+ goto donumset;
+
+ case O_FTSOCK:
+ if (mystat(arg,st[1]) < 0)
+ goto say_undef;
+ if (S_ISSOCK(statcache.st_mode))
+ goto say_yes;
+ goto say_no;
+ case O_FTCHR:
+ if (mystat(arg,st[1]) < 0)
+ goto say_undef;
+ if (S_ISCHR(statcache.st_mode))
+ goto say_yes;
+ goto say_no;
+ case O_FTBLK:
+ if (mystat(arg,st[1]) < 0)
+ goto say_undef;
+ if (S_ISBLK(statcache.st_mode))
+ goto say_yes;
+ goto say_no;
+ case O_FTFILE:
+ if (mystat(arg,st[1]) < 0)
+ goto say_undef;
+ if (S_ISREG(statcache.st_mode))
+ goto say_yes;
+ goto say_no;
+ case O_FTDIR:
+ if (mystat(arg,st[1]) < 0)
+ goto say_undef;
+ if (S_ISDIR(statcache.st_mode))
+ goto say_yes;
+ goto say_no;
+ case O_FTPIPE:
+ if (mystat(arg,st[1]) < 0)
+ goto say_undef;
+ if (S_ISFIFO(statcache.st_mode))
+ goto say_yes;
+ goto say_no;
+ case O_FTLINK:
+ if (mylstat(arg,st[1]) < 0)
+ goto say_undef;
+ if (S_ISLNK(statcache.st_mode))
+ goto say_yes;
+ goto say_no;
+ case O_SYMLINK:
+#ifdef HAS_SYMLINK
+ tmps = str_get(st[1]);
+ tmps2 = str_get(st[2]);
+#ifdef TAINT
+ taintproper("Insecure dependency in symlink");
+#endif
+ value = (double)(symlink(tmps,tmps2) >= 0);
+ goto donumset;
+#else
+ fatal("Unsupported function symlink");
+#endif
+ case O_READLINK:
+#ifdef HAS_SYMLINK
+ if (maxarg < 1)
+ tmps = str_get(stab_val(defstab));
+ else
+ tmps = str_get(st[1]);
+ anum = readlink(tmps,buf,sizeof buf);
+ if (anum < 0)
+ goto say_undef;
+ str_nset(str,buf,anum);
+ break;
+#else
+ goto say_undef; /* just pretend it's a normal file */
+#endif
+ case O_FTSUID:
+#ifdef S_ISUID
+ anum = S_ISUID;
+ goto check_xid;
+#else
+ goto say_no;
+#endif
+ case O_FTSGID:
+#ifdef S_ISGID
+ anum = S_ISGID;
+ goto check_xid;
+#else
+ goto say_no;
+#endif
+ case O_FTSVTX:
+#ifdef S_ISVTX
+ anum = S_ISVTX;
+#else
+ goto say_no;
+#endif
+ check_xid:
+ if (mystat(arg,st[1]) < 0)
+ goto say_undef;
+ if (statcache.st_mode & anum)
+ goto say_yes;
+ goto say_no;
+ case O_FTTTY:
+ if (arg[1].arg_type & A_DONT) {
+ stab = arg[1].arg_ptr.arg_stab;
+ tmps = "";
+ }
+ else
+ stab = stabent(tmps = str_get(st[1]),FALSE);
+ if (stab && stab_io(stab) && stab_io(stab)->ifp)
+ anum = fileno(stab_io(stab)->ifp);
+ else if (isDIGIT(*tmps))
+ anum = atoi(tmps);
+ else
+ goto say_undef;
+ if (isatty(anum))
+ goto say_yes;
+ goto say_no;
+ case O_FTTEXT:
+ case O_FTBINARY:
+ str = do_fttext(arg,st[1]);
+ break;
+#ifdef HAS_SOCKET
+ case O_SOCKET:
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+#ifndef lint
+ value = (double)do_socket(stab,arglast);
+#else
+ (void)do_socket(stab,arglast);
+#endif
+ goto donumset;
+ case O_BIND:
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+#ifndef lint
+ value = (double)do_bind(stab,arglast);
+#else
+ (void)do_bind(stab,arglast);
+#endif
+ goto donumset;
+ case O_CONNECT:
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+#ifndef lint
+ value = (double)do_connect(stab,arglast);
+#else
+ (void)do_connect(stab,arglast);
+#endif
+ goto donumset;
+ case O_LISTEN:
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+#ifndef lint
+ value = (double)do_listen(stab,arglast);
+#else
+ (void)do_listen(stab,arglast);
+#endif
+ goto donumset;
+ case O_ACCEPT:
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ if ((arg[2].arg_type & A_MASK) == A_WORD)
+ stab2 = arg[2].arg_ptr.arg_stab;
+ else
+ stab2 = stabent(str_get(st[2]),TRUE);
+ do_accept(str,stab,stab2);
+ STABSET(str);
+ break;
+ case O_GHBYNAME:
+ if (maxarg < 1)
+ goto say_undef;
+ case O_GHBYADDR:
+ case O_GHOSTENT:
+ sp = do_ghent(optype,
+ gimme,arglast);
+ goto array_return;
+ case O_GNBYNAME:
+ if (maxarg < 1)
+ goto say_undef;
+ case O_GNBYADDR:
+ case O_GNETENT:
+ sp = do_gnent(optype,
+ gimme,arglast);
+ goto array_return;
+ case O_GPBYNAME:
+ if (maxarg < 1)
+ goto say_undef;
+ case O_GPBYNUMBER:
+ case O_GPROTOENT:
+ sp = do_gpent(optype,
+ gimme,arglast);
+ goto array_return;
+ case O_GSBYNAME:
+ if (maxarg < 1)
+ goto say_undef;
+ case O_GSBYPORT:
+ case O_GSERVENT:
+ sp = do_gsent(optype,
+ gimme,arglast);
+ goto array_return;
+ case O_SHOSTENT:
+ value = (double) sethostent((int)str_gnum(st[1]));
+ goto donumset;
+ case O_SNETENT:
+ value = (double) setnetent((int)str_gnum(st[1]));
+ goto donumset;
+ case O_SPROTOENT:
+ value = (double) setprotoent((int)str_gnum(st[1]));
+ goto donumset;
+ case O_SSERVENT:
+ value = (double) setservent((int)str_gnum(st[1]));
+ goto donumset;
+ case O_EHOSTENT:
+ value = (double) endhostent();
+ goto donumset;
+ case O_ENETENT:
+ value = (double) endnetent();
+ goto donumset;
+ case O_EPROTOENT:
+ value = (double) endprotoent();
+ goto donumset;
+ case O_ESERVENT:
+ value = (double) endservent();
+ goto donumset;
+ case O_SOCKPAIR:
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ if ((arg[2].arg_type & A_MASK) == A_WORD)
+ stab2 = arg[2].arg_ptr.arg_stab;
+ else
+ stab2 = stabent(str_get(st[2]),TRUE);
+#ifndef lint
+ value = (double)do_spair(stab,stab2,arglast);
+#else
+ (void)do_spair(stab,stab2,arglast);
+#endif
+ goto donumset;
+ case O_SHUTDOWN:
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+#ifndef lint
+ value = (double)do_shutdown(stab,arglast);
+#else
+ (void)do_shutdown(stab,arglast);
+#endif
+ goto donumset;
+ case O_GSOCKOPT:
+ case O_SSOCKOPT:
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ sp = do_sopt(optype,stab,arglast);
+ goto array_return;
+ case O_GETSOCKNAME:
+ case O_GETPEERNAME:
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ if (!stab)
+ goto say_undef;
+ sp = do_getsockname(optype,stab,arglast);
+ goto array_return;
+
+#else /* HAS_SOCKET not defined */
+ case O_SOCKET:
+ case O_BIND:
+ case O_CONNECT:
+ case O_LISTEN:
+ case O_ACCEPT:
+ case O_SOCKPAIR:
+ case O_GHBYNAME:
+ case O_GHBYADDR:
+ case O_GHOSTENT:
+ case O_GNBYNAME:
+ case O_GNBYADDR:
+ case O_GNETENT:
+ case O_GPBYNAME:
+ case O_GPBYNUMBER:
+ case O_GPROTOENT:
+ case O_GSBYNAME:
+ case O_GSBYPORT:
+ case O_GSERVENT:
+ case O_SHOSTENT:
+ case O_SNETENT:
+ case O_SPROTOENT:
+ case O_SSERVENT:
+ case O_EHOSTENT:
+ case O_ENETENT:
+ case O_EPROTOENT:
+ case O_ESERVENT:
+ case O_SHUTDOWN:
+ case O_GSOCKOPT:
+ case O_SSOCKOPT:
+ case O_GETSOCKNAME:
+ case O_GETPEERNAME:
+ badsock:
+ fatal("Unsupported socket function");
+#endif /* HAS_SOCKET */
+ case O_SSELECT:
+#ifdef HAS_SELECT
+ sp = do_select(gimme,arglast);
+ goto array_return;
+#else
+ fatal("select not implemented");
+#endif
+ case O_FILENO:
+ if (maxarg < 1)
+ goto say_undef;
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ if (!stab || !(stio = stab_io(stab)) || !(fp = stio->ifp))
+ goto say_undef;
+ value = fileno(fp);
+ goto donumset;
+ case O_BINMODE:
+ if (maxarg < 1)
+ goto say_undef;
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ if (!stab || !(stio = stab_io(stab)) || !(fp = stio->ifp))
+ goto say_undef;
+#ifdef DOSISH
+#ifdef atarist
+ if(fflush(fp))
+ str_set(str, No);
+ else
+ {
+ fp->_flag |= _IOBIN;
+ str_set(str, Yes);
+ }
+#else
+ str_set(str, (setmode(fileno(fp), O_BINARY) != -1) ? Yes : No);
+#endif
+#else
+ str_set(str, Yes);
+#endif
+ STABSET(str);
+ break;
+ case O_VEC:
+ sp = do_vec(str == st[1], arg->arg_ptr.arg_str, arglast);
+ goto array_return;
+ case O_GPWNAM:
+ case O_GPWUID:
+ case O_GPWENT:
+#ifdef HAS_PASSWD
+ sp = do_gpwent(optype,
+ gimme,arglast);
+ goto array_return;
+ case O_SPWENT:
+ value = (double) setpwent();
+ goto donumset;
+ case O_EPWENT:
+ value = (double) endpwent();
+ goto donumset;
+#else
+ case O_EPWENT:
+ case O_SPWENT:
+ fatal("Unsupported password function");
+ break;
+#endif
+ case O_GGRNAM:
+ case O_GGRGID:
+ case O_GGRENT:
+#ifdef HAS_GROUP
+ sp = do_ggrent(optype,
+ gimme,arglast);
+ goto array_return;
+ case O_SGRENT:
+ value = (double) setgrent();
+ goto donumset;
+ case O_EGRENT:
+ value = (double) endgrent();
+ goto donumset;
+#else
+ case O_EGRENT:
+ case O_SGRENT:
+ fatal("Unsupported group function");
+ break;
+#endif
+ case O_GETLOGIN:
+#ifdef HAS_GETLOGIN
+ if (!(tmps = getlogin()))
+ goto say_undef;
+ str_set(str,tmps);
+#else
+ fatal("Unsupported function getlogin");
+#endif
+ break;
+ case O_OPEN_DIR:
+ case O_READDIR:
+ case O_TELLDIR:
+ case O_SEEKDIR:
+ case O_REWINDDIR:
+ case O_CLOSEDIR:
+ if (maxarg < 1)
+ goto say_undef;
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ if (!stab)
+ goto say_undef;
+ sp = do_dirop(optype,stab,gimme,arglast);
+ goto array_return;
+ case O_SYSCALL:
+ value = (double)do_syscall(arglast);
+ goto donumset;
+ case O_PIPE_OP:
+#ifdef HAS_PIPE
+ if ((arg[1].arg_type & A_MASK) == A_WORD)
+ stab = arg[1].arg_ptr.arg_stab;
+ else
+ stab = stabent(str_get(st[1]),TRUE);
+ if ((arg[2].arg_type & A_MASK) == A_WORD)
+ stab2 = arg[2].arg_ptr.arg_stab;
+ else
+ stab2 = stabent(str_get(st[2]),TRUE);
+ do_pipe(str,stab,stab2);
+ STABSET(str);
+#else
+ fatal("Unsupported function pipe");
+#endif
+ break;
+ }
+
+ normal_return:
+ st[1] = str;
+#ifdef DEBUGGING
+ if (debug) {
+ dlevel--;
+ if (debug & 8)
+ deb("%s RETURNS \"%s\"\n",opname[optype],str_get(str));
+ }
+#endif
+ return arglast[0] + 1;
+}
diff --git a/gnu/usr.bin/perl/perl/form.c b/gnu/usr.bin/perl/perl/form.c
new file mode 100644
index 0000000..57fc5de
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/form.c
@@ -0,0 +1,419 @@
+/* $RCSfile: form.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:36 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: form.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:36 nate
+ * PERL!
+ *
+ * Revision 4.0.1.4 1993/02/05 19:34:32 lwall
+ * patch36: formats now ignore literal text for ~~ loop determination
+ *
+ * Revision 4.0.1.3 92/06/08 13:21:42 lwall
+ * patch20: removed implicit int declarations on funcions
+ * patch20: form feed for formats is now specifiable via $^L
+ * patch20: Perl now distinguishes overlapped copies from non-overlapped
+ *
+ * Revision 4.0.1.2 91/11/05 17:18:43 lwall
+ * patch11: formats didn't fill their fields as well as they could
+ * patch11: ^ fields chopped hyphens on line break
+ * patch11: # fields could write outside allocated memory
+ *
+ * Revision 4.0.1.1 91/06/07 11:07:59 lwall
+ * patch4: new copyright notice
+ * patch4: default top-of-form format is now FILEHANDLE_TOP
+ *
+ * Revision 4.0 91/03/20 01:19:23 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+
+/* Forms stuff */
+
+static int countlines();
+
+void
+form_parseargs(fcmd)
+register FCMD *fcmd;
+{
+ register int i;
+ register ARG *arg;
+ register int items;
+ STR *str;
+ ARG *parselist();
+ line_t oldline = curcmd->c_line;
+ int oldsave = savestack->ary_fill;
+
+ str = fcmd->f_unparsed;
+ curcmd->c_line = fcmd->f_line;
+ fcmd->f_unparsed = Nullstr;
+ (void)savehptr(&curstash);
+ curstash = str->str_u.str_hash;
+ arg = parselist(str);
+ restorelist(oldsave);
+
+ items = arg->arg_len - 1; /* ignore $$ on end */
+ for (i = 1; i <= items; i++) {
+ if (!fcmd || fcmd->f_type == F_NULL)
+ fatal("Too many field values");
+ dehoist(arg,i);
+ fcmd->f_expr = make_op(O_ITEM,1,
+ arg[i].arg_ptr.arg_arg,Nullarg,Nullarg);
+ if (fcmd->f_flags & FC_CHOP) {
+ if ((fcmd->f_expr[1].arg_type & A_MASK) == A_STAB)
+ fcmd->f_expr[1].arg_type = A_LVAL;
+ else if ((fcmd->f_expr[1].arg_type & A_MASK) == A_EXPR)
+ fcmd->f_expr[1].arg_type = A_LEXPR;
+ else
+ fatal("^ field requires scalar lvalue");
+ }
+ fcmd = fcmd->f_next;
+ }
+ if (fcmd && fcmd->f_type)
+ fatal("Not enough field values");
+ curcmd->c_line = oldline;
+ Safefree(arg);
+ str_free(str);
+}
+
+int newsize;
+
+#define CHKLEN(allow) \
+newsize = (d - orec->o_str) + (allow); \
+if (newsize >= curlen) { \
+ curlen = d - orec->o_str; \
+ GROWSTR(&orec->o_str,&orec->o_len,orec->o_len + (allow)); \
+ d = orec->o_str + curlen; /* in case it moves */ \
+ curlen = orec->o_len - 2; \
+}
+
+void
+format(orec,fcmd,sp)
+register struct outrec *orec;
+register FCMD *fcmd;
+int sp;
+{
+ register char *d = orec->o_str;
+ register char *s;
+ register int curlen = orec->o_len - 2;
+ register int size;
+ FCMD *nextfcmd;
+ FCMD *linebeg = fcmd;
+ char tmpchar;
+ char *t;
+ CMD mycmd;
+ STR *str;
+ char *chophere;
+ int blank = TRUE;
+
+ mycmd.c_type = C_NULL;
+ orec->o_lines = 0;
+ for (; fcmd; fcmd = nextfcmd) {
+ nextfcmd = fcmd->f_next;
+ CHKLEN(fcmd->f_presize);
+ /*SUPPRESS 560*/
+ if (s = fcmd->f_pre) {
+ while (*s) {
+ if (*s == '\n') {
+ t = orec->o_str;
+ if (blank && (fcmd->f_flags & FC_REPEAT)) {
+ while (d > t && (d[-1] != '\n'))
+ d--;
+ }
+ else {
+ while (d > t && (d[-1] == ' ' || d[-1] == '\t'))
+ d--;
+ }
+ if (fcmd->f_flags & FC_NOBLANK) {
+ if (blank || d == orec->o_str || d[-1] == '\n') {
+ orec->o_lines--; /* don't print blank line */
+ linebeg = fcmd->f_next;
+ break;
+ }
+ else if (fcmd->f_flags & FC_REPEAT)
+ nextfcmd = linebeg;
+ else
+ linebeg = fcmd->f_next;
+ }
+ else
+ linebeg = fcmd->f_next;
+ blank = TRUE;
+ }
+ *d++ = *s++;
+ }
+ }
+ if (fcmd->f_unparsed)
+ form_parseargs(fcmd);
+ switch (fcmd->f_type) {
+ case F_NULL:
+ orec->o_lines++;
+ break;
+ case F_LEFT:
+ (void)eval(fcmd->f_expr,G_SCALAR,sp);
+ str = stack->ary_array[sp+1];
+ s = str_get(str);
+ size = fcmd->f_size;
+ CHKLEN(size);
+ chophere = Nullch;
+ while (size && *s && *s != '\n') {
+ if (*s == '\t')
+ *s = ' ';
+ else if (*s != ' ')
+ blank = FALSE;
+ size--;
+ if (*s && index(chopset,(*d++ = *s++)))
+ chophere = s;
+ if (*s == '\n' && (fcmd->f_flags & FC_CHOP))
+ *s = ' ';
+ }
+ if (size || !*s)
+ chophere = s;
+ else if (chophere && chophere < s && *s && index(chopset,*s))
+ chophere = s;
+ if (fcmd->f_flags & FC_CHOP) {
+ if (!chophere)
+ chophere = s;
+ size += (s - chophere);
+ d -= (s - chophere);
+ if (fcmd->f_flags & FC_MORE &&
+ *chophere && strNE(chophere,"\n")) {
+ while (size < 3) {
+ d--;
+ size++;
+ }
+ while (d[-1] == ' ' && size < fcmd->f_size) {
+ d--;
+ size++;
+ }
+ *d++ = '.';
+ *d++ = '.';
+ *d++ = '.';
+ size -= 3;
+ }
+ while (*chophere && index(chopset,*chophere)
+ && isSPACE(*chophere))
+ chophere++;
+ str_chop(str,chophere);
+ }
+ if (fcmd->f_next && fcmd->f_next->f_pre[0] == '\n')
+ size = 0; /* no spaces before newline */
+ while (size) {
+ size--;
+ *d++ = ' ';
+ }
+ break;
+ case F_RIGHT:
+ (void)eval(fcmd->f_expr,G_SCALAR,sp);
+ str = stack->ary_array[sp+1];
+ t = s = str_get(str);
+ size = fcmd->f_size;
+ CHKLEN(size);
+ chophere = Nullch;
+ while (size && *s && *s != '\n') {
+ if (*s == '\t')
+ *s = ' ';
+ else if (*s != ' ')
+ blank = FALSE;
+ size--;
+ if (*s && index(chopset,*s++))
+ chophere = s;
+ if (*s == '\n' && (fcmd->f_flags & FC_CHOP))
+ *s = ' ';
+ }
+ if (size || !*s)
+ chophere = s;
+ else if (chophere && chophere < s && *s && index(chopset,*s))
+ chophere = s;
+ if (fcmd->f_flags & FC_CHOP) {
+ if (!chophere)
+ chophere = s;
+ size += (s - chophere);
+ s = chophere;
+ while (*chophere && index(chopset,*chophere)
+ && isSPACE(*chophere))
+ chophere++;
+ }
+ tmpchar = *s;
+ *s = '\0';
+ while (size) {
+ size--;
+ *d++ = ' ';
+ }
+ size = s - t;
+ Copy(t,d,size,char);
+ d += size;
+ *s = tmpchar;
+ if (fcmd->f_flags & FC_CHOP)
+ str_chop(str,chophere);
+ break;
+ case F_CENTER: {
+ int halfsize;
+
+ (void)eval(fcmd->f_expr,G_SCALAR,sp);
+ str = stack->ary_array[sp+1];
+ t = s = str_get(str);
+ size = fcmd->f_size;
+ CHKLEN(size);
+ chophere = Nullch;
+ while (size && *s && *s != '\n') {
+ if (*s == '\t')
+ *s = ' ';
+ else if (*s != ' ')
+ blank = FALSE;
+ size--;
+ if (*s && index(chopset,*s++))
+ chophere = s;
+ if (*s == '\n' && (fcmd->f_flags & FC_CHOP))
+ *s = ' ';
+ }
+ if (size || !*s)
+ chophere = s;
+ else if (chophere && chophere < s && *s && index(chopset,*s))
+ chophere = s;
+ if (fcmd->f_flags & FC_CHOP) {
+ if (!chophere)
+ chophere = s;
+ size += (s - chophere);
+ s = chophere;
+ while (*chophere && index(chopset,*chophere)
+ && isSPACE(*chophere))
+ chophere++;
+ }
+ tmpchar = *s;
+ *s = '\0';
+ halfsize = size / 2;
+ while (size > halfsize) {
+ size--;
+ *d++ = ' ';
+ }
+ size = s - t;
+ Copy(t,d,size,char);
+ d += size;
+ *s = tmpchar;
+ if (fcmd->f_next && fcmd->f_next->f_pre[0] == '\n')
+ size = 0; /* no spaces before newline */
+ else
+ size = halfsize;
+ while (size) {
+ size--;
+ *d++ = ' ';
+ }
+ if (fcmd->f_flags & FC_CHOP)
+ str_chop(str,chophere);
+ break;
+ }
+ case F_LINES:
+ (void)eval(fcmd->f_expr,G_SCALAR,sp);
+ str = stack->ary_array[sp+1];
+ s = str_get(str);
+ size = str_len(str);
+ CHKLEN(size+1);
+ orec->o_lines += countlines(s,size) - 1;
+ Copy(s,d,size,char);
+ d += size;
+ if (size && s[size-1] != '\n') {
+ *d++ = '\n';
+ orec->o_lines++;
+ }
+ linebeg = fcmd->f_next;
+ break;
+ case F_DECIMAL: {
+ double value;
+
+ (void)eval(fcmd->f_expr,G_SCALAR,sp);
+ str = stack->ary_array[sp+1];
+ size = fcmd->f_size;
+ CHKLEN(size+1);
+ /* If the field is marked with ^ and the value is undefined,
+ blank it out. */
+ if ((fcmd->f_flags & FC_CHOP) && !str->str_pok && !str->str_nok) {
+ while (size) {
+ size--;
+ *d++ = ' ';
+ }
+ break;
+ }
+ blank = FALSE;
+ value = str_gnum(str);
+ if (fcmd->f_flags & FC_DP) {
+ sprintf(d, "%#*.*f", size, fcmd->f_decimals, value);
+ } else {
+ sprintf(d, "%*.0f", size, value);
+ }
+ d += size;
+ break;
+ }
+ }
+ }
+ CHKLEN(1);
+ *d++ = '\0';
+}
+
+static int
+countlines(s,size)
+register char *s;
+register int size;
+{
+ register int count = 0;
+
+ while (size--) {
+ if (*s++ == '\n')
+ count++;
+ }
+ return count;
+}
+
+void
+do_write(orec,stab,sp)
+struct outrec *orec;
+STAB *stab;
+int sp;
+{
+ register STIO *stio = stab_io(stab);
+ FILE *ofp = stio->ofp;
+
+#ifdef DEBUGGING
+ if (debug & 256)
+ fprintf(stderr,"left=%ld, todo=%ld\n",
+ (long)stio->lines_left, (long)orec->o_lines);
+#endif
+ if (stio->lines_left < orec->o_lines) {
+ if (!stio->top_stab) {
+ STAB *topstab;
+ char tmpbuf[256];
+
+ if (!stio->top_name) {
+ if (!stio->fmt_name)
+ stio->fmt_name = savestr(stab_name(stab));
+ sprintf(tmpbuf, "%s_TOP", stio->fmt_name);
+ topstab = stabent(tmpbuf,FALSE);
+ if (topstab && stab_form(topstab))
+ stio->top_name = savestr(tmpbuf);
+ else
+ stio->top_name = savestr("top");
+ }
+ topstab = stabent(stio->top_name,FALSE);
+ if (!topstab || !stab_form(topstab)) {
+ stio->lines_left = 100000000;
+ goto forget_top;
+ }
+ stio->top_stab = topstab;
+ }
+ if (stio->lines_left >= 0 && stio->page > 0)
+ fwrite(formfeed->str_ptr, formfeed->str_cur, 1, ofp);
+ stio->lines_left = stio->page_len;
+ stio->page++;
+ format(&toprec,stab_form(stio->top_stab),sp);
+ fputs(toprec.o_str,ofp);
+ stio->lines_left -= toprec.o_lines;
+ }
+ forget_top:
+ fputs(orec->o_str,ofp);
+ stio->lines_left -= orec->o_lines;
+}
diff --git a/gnu/usr.bin/perl/perl/form.h b/gnu/usr.bin/perl/perl/form.h
new file mode 100644
index 0000000..1e3e6f4
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/form.h
@@ -0,0 +1,48 @@
+/* $RCSfile: form.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:36 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: form.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:36 nate
+ * PERL!
+ *
+ * Revision 4.0.1.1 91/06/07 11:08:20 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:19:37 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#define F_NULL 0
+#define F_LEFT 1
+#define F_RIGHT 2
+#define F_CENTER 3
+#define F_LINES 4
+#define F_DECIMAL 5
+
+struct formcmd {
+ struct formcmd *f_next;
+ ARG *f_expr;
+ STR *f_unparsed;
+ line_t f_line;
+ char *f_pre;
+ short f_presize;
+ short f_size;
+ short f_decimals;
+ char f_type;
+ char f_flags;
+};
+
+#define FC_CHOP 1
+#define FC_NOBLANK 2
+#define FC_MORE 4
+#define FC_REPEAT 8
+#define FC_DP 16
+
+#define Nullfcmd Null(FCMD*)
+
+EXT char *chopset INIT(" \n-");
diff --git a/gnu/usr.bin/perl/perl/handy.h b/gnu/usr.bin/perl/perl/handy.h
new file mode 100644
index 0000000..46231ae
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/handy.h
@@ -0,0 +1,150 @@
+/* $RCSfile: handy.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:36 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: handy.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:36 nate
+ * PERL!
+ *
+ * Revision 4.0.1.4 92/06/08 13:23:17 lwall
+ * patch20: isascii() may now be supplied by a library routine
+ * patch20: Perl now distinguishes overlapped copies from non-overlapped
+ *
+ * Revision 4.0.1.3 91/11/05 22:54:26 lwall
+ * patch11: erratum
+ *
+ * Revision 4.0.1.2 91/11/05 17:23:38 lwall
+ * patch11: prepared for ctype implementations that don't define isascii()
+ *
+ * Revision 4.0.1.1 91/06/07 11:09:56 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:22:15 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#ifdef NULL
+#undef NULL
+#endif
+#ifndef I286
+# define NULL 0
+#else
+# define NULL 0L
+#endif
+#define Null(type) ((type)NULL)
+#define Nullch Null(char*)
+#define Nullfp Null(FILE*)
+
+#ifdef UTS
+#define bool int
+#else
+#define bool char
+#endif
+
+#ifdef TRUE
+#undef TRUE
+#endif
+#ifdef FALSE
+#undef FALSE
+#endif
+#define TRUE (1)
+#define FALSE (0)
+
+#define Ctl(ch) (ch & 037)
+
+#define strNE(s1,s2) (strcmp(s1,s2))
+#define strEQ(s1,s2) (!strcmp(s1,s2))
+#define strLT(s1,s2) (strcmp(s1,s2) < 0)
+#define strLE(s1,s2) (strcmp(s1,s2) <= 0)
+#define strGT(s1,s2) (strcmp(s1,s2) > 0)
+#define strGE(s1,s2) (strcmp(s1,s2) >= 0)
+#define strnNE(s1,s2,l) (strncmp(s1,s2,l))
+#define strnEQ(s1,s2,l) (!strncmp(s1,s2,l))
+
+#if defined(CTYPE256) || (!defined(isascii) && !defined(HAS_ISASCII))
+#define isALNUM(c) (isalpha(c) || isdigit(c) || c == '_')
+#define isALPHA(c) isalpha(c)
+#define isSPACE(c) isspace(c)
+#define isDIGIT(c) isdigit(c)
+#define isUPPER(c) isupper(c)
+#define isLOWER(c) islower(c)
+#else
+#define isALNUM(c) (isascii(c) && (isalpha(c) || isdigit(c) || c == '_'))
+#define isALPHA(c) (isascii(c) && isalpha(c))
+#define isSPACE(c) (isascii(c) && isspace(c))
+#define isDIGIT(c) (isascii(c) && isdigit(c))
+#define isUPPER(c) (isascii(c) && isupper(c))
+#define isLOWER(c) (isascii(c) && islower(c))
+#endif
+
+/* Line numbers are unsigned, 16 bits. */
+typedef unsigned short line_t;
+#ifdef lint
+#define NOLINE ((line_t)0)
+#else
+#define NOLINE ((line_t) 65535)
+#endif
+
+#ifndef lint
+#ifndef LEAKTEST
+#ifndef safemalloc
+char *safemalloc();
+char *saferealloc();
+void safefree();
+#endif
+#ifndef MSDOS
+#define New(x,v,n,t) (v = (t*)safemalloc((MEM_SIZE)((n) * sizeof(t))))
+#define Newc(x,v,n,t,c) (v = (c*)safemalloc((MEM_SIZE)((n) * sizeof(t))))
+#define Newz(x,v,n,t) (v = (t*)safemalloc((MEM_SIZE)((n) * sizeof(t)))), \
+ memzero((char*)(v), (n) * sizeof(t))
+#define Renew(v,n,t) (v = (t*)saferealloc((char*)(v),(MEM_SIZE)((n)*sizeof(t))))
+#define Renewc(v,n,t,c) (v = (c*)saferealloc((char*)(v),(MEM_SIZE)((n)*sizeof(t))))
+#else
+#define New(x,v,n,t) (v = (t*)safemalloc(((unsigned long)(n) * sizeof(t))))
+#define Newc(x,v,n,t,c) (v = (c*)safemalloc(((unsigned long)(n) * sizeof(t))))
+#define Newz(x,v,n,t) (v = (t*)safemalloc(((unsigned long)(n) * sizeof(t)))), \
+ memzero((char*)(v), (n) * sizeof(t))
+#define Renew(v,n,t) (v = (t*)saferealloc((char*)(v),((unsigned long)(n)*sizeof(t))))
+#define Renewc(v,n,t,c) (v = (c*)saferealloc((char*)(v),((unsigned long)(n)*sizeof(t))))
+#endif /* MSDOS */
+#define Safefree(d) safefree((char*)d)
+#define Str_new(x,len) str_new(len)
+#else /* LEAKTEST */
+char *safexmalloc();
+char *safexrealloc();
+void safexfree();
+#define New(x,v,n,t) (v = (t*)safexmalloc(x,(MEM_SIZE)((n) * sizeof(t))))
+#define Newc(x,v,n,t,c) (v = (c*)safexmalloc(x,(MEM_SIZE)((n) * sizeof(t))))
+#define Newz(x,v,n,t) (v = (t*)safexmalloc(x,(MEM_SIZE)((n) * sizeof(t)))), \
+ memzero((char*)(v), (n) * sizeof(t))
+#define Renew(v,n,t) (v = (t*)safexrealloc((char*)(v),(MEM_SIZE)((n)*sizeof(t))))
+#define Renewc(v,n,t,c) (v = (c*)safexrealloc((char*)(v),(MEM_SIZE)((n)*sizeof(t))))
+#define Safefree(d) safexfree((char*)d)
+#define Str_new(x,len) str_new(x,len)
+#define MAXXCOUNT 1200
+long xcount[MAXXCOUNT];
+long lastxcount[MAXXCOUNT];
+#endif /* LEAKTEST */
+#define Move(s,d,n,t) (void)memmove((char*)(d),(char*)(s), (n) * sizeof(t))
+#define Copy(s,d,n,t) (void)memcpy((char*)(d),(char*)(s), (n) * sizeof(t))
+#define Zero(d,n,t) (void)memzero((char*)(d), (n) * sizeof(t))
+#else /* lint */
+#define New(x,v,n,s) (v = Null(s *))
+#define Newc(x,v,n,s,c) (v = Null(s *))
+#define Newz(x,v,n,s) (v = Null(s *))
+#define Renew(v,n,s) (v = Null(s *))
+#define Move(s,d,n,t)
+#define Copy(s,d,n,t)
+#define Zero(d,n,t)
+#define Safefree(d) d = d
+#endif /* lint */
+
+#ifdef STRUCTCOPY
+#define StructCopy(s,d,t) *((t*)(d)) = *((t*)(s))
+#else
+#define StructCopy(s,d,t) Copy(s,d,1,t)
+#endif
diff --git a/gnu/usr.bin/perl/perl/hash.c b/gnu/usr.bin/perl/perl/hash.c
new file mode 100644
index 0000000..15cc116
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/hash.c
@@ -0,0 +1,715 @@
+/* $RCSfile: hash.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:37 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: hash.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:37 nate
+ * PERL!
+ *
+ * Revision 4.0.1.3 92/06/08 13:26:29 lwall
+ * patch20: removed implicit int declarations on functions
+ * patch20: delete could cause %array to give too low a count of buckets filled
+ * patch20: hash tables now split only if the memory is available to do so
+ *
+ * Revision 4.0.1.2 91/11/05 17:24:13 lwall
+ * patch11: saberized perl
+ *
+ * Revision 4.0.1.1 91/06/07 11:10:11 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:22:26 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+
+static void hsplit();
+
+static char coeff[] = {
+ 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+ 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+ 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+ 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+ 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+ 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+ 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+ 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1};
+
+static void hfreeentries();
+
+STR *
+hfetch(tb,key,klen,lval)
+register HASH *tb;
+char *key;
+unsigned int klen;
+int lval;
+{
+ register char *s;
+ register int i;
+ register int hash;
+ register HENT *entry;
+ register int maxi;
+ STR *str;
+#ifdef SOME_DBM
+ datum dkey,dcontent;
+#endif
+
+ if (!tb)
+ return &str_undef;
+ if (!tb->tbl_array) {
+ if (lval)
+ Newz(503,tb->tbl_array, tb->tbl_max + 1, HENT*);
+ else
+ return &str_undef;
+ }
+
+ /* The hash function we use on symbols has to be equal to the first
+ * character when taken modulo 128, so that str_reset() can be implemented
+ * efficiently. We throw in the second character and the last character
+ * (times 128) so that long chains of identifiers starting with the
+ * same letter don't have to be strEQ'ed within hfetch(), since it
+ * compares hash values before trying strEQ().
+ */
+ if (!tb->tbl_coeffsize)
+ hash = *key + 128 * key[1] + 128 * key[klen-1]; /* assuming klen > 0 */
+ else { /* use normal coefficients */
+ if (klen < tb->tbl_coeffsize)
+ maxi = klen;
+ else
+ maxi = tb->tbl_coeffsize;
+ for (s=key, i=0, hash = 0;
+ i < maxi; /*SUPPRESS 8*/
+ s++, i++, hash *= 5) {
+ hash += *s * coeff[i];
+ }
+ }
+
+ entry = tb->tbl_array[hash & tb->tbl_max];
+ for (; entry; entry = entry->hent_next) {
+ if (entry->hent_hash != hash) /* strings can't be equal */
+ continue;
+ if (entry->hent_klen != klen)
+ continue;
+ if (bcmp(entry->hent_key,key,klen)) /* is this it? */
+ continue;
+ return entry->hent_val;
+ }
+#ifdef SOME_DBM
+ if (tb->tbl_dbm) {
+ dkey.dptr = key;
+ dkey.dsize = klen;
+#ifdef HAS_GDBM
+ dcontent = gdbm_fetch(tb->tbl_dbm,dkey);
+#else
+ dcontent = dbm_fetch(tb->tbl_dbm,dkey);
+#endif
+ if (dcontent.dptr) { /* found one */
+ str = Str_new(60,dcontent.dsize);
+ str_nset(str,dcontent.dptr,dcontent.dsize);
+ hstore(tb,key,klen,str,hash); /* cache it */
+ return str;
+ }
+ }
+#endif
+ if (lval) { /* gonna assign to this, so it better be there */
+ str = Str_new(61,0);
+ hstore(tb,key,klen,str,hash);
+ return str;
+ }
+ return &str_undef;
+}
+
+bool
+hstore(tb,key,klen,val,hash)
+register HASH *tb;
+char *key;
+unsigned int klen;
+STR *val;
+register int hash;
+{
+ register char *s;
+ register int i;
+ register HENT *entry;
+ register HENT **oentry;
+ register int maxi;
+
+ if (!tb)
+ return FALSE;
+
+ if (hash)
+ /*SUPPRESS 530*/
+ ;
+ else if (!tb->tbl_coeffsize)
+ hash = *key + 128 * key[1] + 128 * key[klen-1];
+ else { /* use normal coefficients */
+ if (klen < tb->tbl_coeffsize)
+ maxi = klen;
+ else
+ maxi = tb->tbl_coeffsize;
+ for (s=key, i=0, hash = 0;
+ i < maxi; /*SUPPRESS 8*/
+ s++, i++, hash *= 5) {
+ hash += *s * coeff[i];
+ }
+ }
+
+ if (!tb->tbl_array)
+ Newz(505,tb->tbl_array, tb->tbl_max + 1, HENT*);
+
+ oentry = &(tb->tbl_array[hash & tb->tbl_max]);
+ i = 1;
+
+ for (entry = *oentry; entry; i=0, entry = entry->hent_next) {
+ if (entry->hent_hash != hash) /* strings can't be equal */
+ continue;
+ if (entry->hent_klen != klen)
+ continue;
+ if (bcmp(entry->hent_key,key,klen)) /* is this it? */
+ continue;
+ Safefree(entry->hent_val);
+ entry->hent_val = val;
+ return TRUE;
+ }
+ New(501,entry, 1, HENT);
+
+ entry->hent_klen = klen;
+ entry->hent_key = nsavestr(key,klen);
+ entry->hent_val = val;
+ entry->hent_hash = hash;
+ entry->hent_next = *oentry;
+ *oentry = entry;
+
+ /* hdbmstore not necessary here because it's called from stabset() */
+
+ if (i) { /* initial entry? */
+ tb->tbl_fill++;
+#ifdef SOME_DBM
+ if (tb->tbl_dbm && tb->tbl_max >= DBM_CACHE_MAX)
+ return FALSE;
+#endif
+ if (tb->tbl_fill > tb->tbl_dosplit)
+ hsplit(tb);
+ }
+#ifdef SOME_DBM
+ else if (tb->tbl_dbm) { /* is this just a cache for dbm file? */
+ void hentdelayfree();
+
+ entry = tb->tbl_array[hash & tb->tbl_max];
+ oentry = &entry->hent_next;
+ entry = *oentry;
+ while (entry) { /* trim chain down to 1 entry */
+ *oentry = entry->hent_next;
+ hentdelayfree(entry); /* no doubt they'll want this next. */
+ entry = *oentry;
+ }
+ }
+#endif
+
+ return FALSE;
+}
+
+STR *
+hdelete(tb,key,klen)
+register HASH *tb;
+char *key;
+unsigned int klen;
+{
+ register char *s;
+ register int i;
+ register int hash;
+ register HENT *entry;
+ register HENT **oentry;
+ STR *str;
+ int maxi;
+#ifdef SOME_DBM
+ datum dkey;
+#endif
+
+ if (!tb || !tb->tbl_array)
+ return Nullstr;
+ if (!tb->tbl_coeffsize)
+ hash = *key + 128 * key[1] + 128 * key[klen-1];
+ else { /* use normal coefficients */
+ if (klen < tb->tbl_coeffsize)
+ maxi = klen;
+ else
+ maxi = tb->tbl_coeffsize;
+ for (s=key, i=0, hash = 0;
+ i < maxi; /*SUPPRESS 8*/
+ s++, i++, hash *= 5) {
+ hash += *s * coeff[i];
+ }
+ }
+
+ oentry = &(tb->tbl_array[hash & tb->tbl_max]);
+ entry = *oentry;
+ i = 1;
+ for (; entry; i=0, oentry = &entry->hent_next, entry = *oentry) {
+ if (entry->hent_hash != hash) /* strings can't be equal */
+ continue;
+ if (entry->hent_klen != klen)
+ continue;
+ if (bcmp(entry->hent_key,key,klen)) /* is this it? */
+ continue;
+ *oentry = entry->hent_next;
+ if (i && !*oentry)
+ tb->tbl_fill--;
+ str = str_mortal(entry->hent_val);
+ hentfree(entry);
+#ifdef SOME_DBM
+ do_dbm_delete:
+ if (tb->tbl_dbm) {
+ dkey.dptr = key;
+ dkey.dsize = klen;
+#ifdef HAS_GDBM
+ gdbm_delete(tb->tbl_dbm,dkey);
+#else
+ dbm_delete(tb->tbl_dbm,dkey);
+#endif
+ }
+#endif
+ return str;
+ }
+#ifdef SOME_DBM
+ str = Nullstr;
+ goto do_dbm_delete;
+#else
+ return Nullstr;
+#endif
+}
+
+static void
+hsplit(tb)
+HASH *tb;
+{
+ int oldsize = tb->tbl_max + 1;
+ register int newsize = oldsize * 2;
+ register int i;
+ register HENT **a;
+ register HENT **b;
+ register HENT *entry;
+ register HENT **oentry;
+
+ a = tb->tbl_array;
+ nomemok = TRUE;
+ Renew(a, newsize, HENT*);
+ nomemok = FALSE;
+ if (!a) {
+ tb->tbl_dosplit = tb->tbl_max + 1; /* never split again */
+ return;
+ }
+ Zero(&a[oldsize], oldsize, HENT*); /* zero 2nd half*/
+ tb->tbl_max = --newsize;
+ tb->tbl_dosplit = tb->tbl_max * FILLPCT / 100;
+ tb->tbl_array = a;
+
+ for (i=0; i<oldsize; i++,a++) {
+ if (!*a) /* non-existent */
+ continue;
+ b = a+oldsize;
+ for (oentry = a, entry = *a; entry; entry = *oentry) {
+ if ((entry->hent_hash & newsize) != i) {
+ *oentry = entry->hent_next;
+ entry->hent_next = *b;
+ if (!*b)
+ tb->tbl_fill++;
+ *b = entry;
+ continue;
+ }
+ else
+ oentry = &entry->hent_next;
+ }
+ if (!*a) /* everything moved */
+ tb->tbl_fill--;
+ }
+}
+
+HASH *
+hnew(lookat)
+unsigned int lookat;
+{
+ register HASH *tb;
+
+ Newz(502,tb, 1, HASH);
+ if (lookat) {
+ tb->tbl_coeffsize = lookat;
+ tb->tbl_max = 7; /* it's a normal associative array */
+ tb->tbl_dosplit = tb->tbl_max * FILLPCT / 100;
+ }
+ else {
+ tb->tbl_max = 127; /* it's a symbol table */
+ tb->tbl_dosplit = 128; /* so never split */
+ }
+ tb->tbl_fill = 0;
+#ifdef SOME_DBM
+ tb->tbl_dbm = 0;
+#endif
+ (void)hiterinit(tb); /* so each() will start off right */
+ return tb;
+}
+
+void
+hentfree(hent)
+register HENT *hent;
+{
+ if (!hent)
+ return;
+ str_free(hent->hent_val);
+ Safefree(hent->hent_key);
+ Safefree(hent);
+}
+
+void
+hentdelayfree(hent)
+register HENT *hent;
+{
+ if (!hent)
+ return;
+ str_2mortal(hent->hent_val); /* free between statements */
+ Safefree(hent->hent_key);
+ Safefree(hent);
+}
+
+void
+hclear(tb,dodbm)
+register HASH *tb;
+int dodbm;
+{
+ if (!tb)
+ return;
+ hfreeentries(tb,dodbm);
+ tb->tbl_fill = 0;
+#ifndef lint
+ if (tb->tbl_array)
+ (void)memzero((char*)tb->tbl_array, (tb->tbl_max + 1) * sizeof(HENT*));
+#endif
+}
+
+static void
+hfreeentries(tb,dodbm)
+register HASH *tb;
+int dodbm;
+{
+ register HENT *hent;
+ register HENT *ohent = Null(HENT*);
+#ifdef SOME_DBM
+ datum dkey;
+ datum nextdkey;
+#ifdef HAS_GDBM
+ GDBM_FILE old_dbm;
+#else
+#ifdef HAS_NDBM
+ DBM *old_dbm;
+#else
+ int old_dbm;
+#endif
+#endif
+#endif
+
+ if (!tb || !tb->tbl_array)
+ return;
+#ifdef SOME_DBM
+ if ((old_dbm = tb->tbl_dbm) && dodbm) {
+#ifdef HAS_GDBM
+ while (dkey = gdbm_firstkey(tb->tbl_dbm), dkey.dptr) {
+#else
+ while (dkey = dbm_firstkey(tb->tbl_dbm), dkey.dptr) {
+#endif
+ do {
+#ifdef HAS_GDBM
+ nextdkey = gdbm_nextkey(tb->tbl_dbm, dkey);
+#else
+#ifdef HAS_NDBM
+#ifdef _CX_UX
+ nextdkey = dbm_nextkey(tb->tbl_dbm, dkey);
+#else
+ nextdkey = dbm_nextkey(tb->tbl_dbm);
+#endif
+#else
+ nextdkey = nextkey(dkey);
+#endif
+#endif
+#ifdef HAS_GDBM
+ gdbm_delete(tb->tbl_dbm,dkey);
+#else
+ dbm_delete(tb->tbl_dbm,dkey);
+#endif
+ dkey = nextdkey;
+ } while (dkey.dptr); /* one way or another, this works */
+ }
+ }
+ tb->tbl_dbm = 0; /* now clear just cache */
+#endif
+ (void)hiterinit(tb);
+ /*SUPPRESS 560*/
+ while (hent = hiternext(tb)) { /* concise but not very efficient */
+ hentfree(ohent);
+ ohent = hent;
+ }
+ hentfree(ohent);
+#ifdef SOME_DBM
+ tb->tbl_dbm = old_dbm;
+#endif
+}
+
+void
+hfree(tb,dodbm)
+register HASH *tb;
+int dodbm;
+{
+ if (!tb)
+ return;
+ hfreeentries(tb,dodbm);
+ Safefree(tb->tbl_array);
+ Safefree(tb);
+}
+
+int
+hiterinit(tb)
+register HASH *tb;
+{
+ tb->tbl_riter = -1;
+ tb->tbl_eiter = Null(HENT*);
+ return tb->tbl_fill;
+}
+
+HENT *
+hiternext(tb)
+register HASH *tb;
+{
+ register HENT *entry;
+#ifdef SOME_DBM
+ datum key;
+#endif
+
+ entry = tb->tbl_eiter;
+#ifdef SOME_DBM
+ if (tb->tbl_dbm) {
+ if (entry) {
+#ifdef HAS_GDBM
+ key.dptr = entry->hent_key;
+ key.dsize = entry->hent_klen;
+ key = gdbm_nextkey(tb->tbl_dbm, key);
+#else
+#ifdef HAS_NDBM
+#ifdef _CX_UX
+ key.dptr = entry->hent_key;
+ key.dsize = entry->hent_klen;
+ key = dbm_nextkey(tb->tbl_dbm, key);
+#else
+ key = dbm_nextkey(tb->tbl_dbm);
+#endif /* _CX_UX */
+#else
+ key.dptr = entry->hent_key;
+ key.dsize = entry->hent_klen;
+ key = nextkey(key);
+#endif
+#endif
+ }
+ else {
+ Newz(504,entry, 1, HENT);
+ tb->tbl_eiter = entry;
+#ifdef HAS_GDBM
+ key = gdbm_firstkey(tb->tbl_dbm);
+#else
+ key = dbm_firstkey(tb->tbl_dbm);
+#endif
+ }
+ entry->hent_key = key.dptr;
+ entry->hent_klen = key.dsize;
+ if (!key.dptr) {
+ if (entry->hent_val)
+ str_free(entry->hent_val);
+ Safefree(entry);
+ tb->tbl_eiter = Null(HENT*);
+ return Null(HENT*);
+ }
+ return entry;
+ }
+#endif
+ if (!tb->tbl_array)
+ Newz(506,tb->tbl_array, tb->tbl_max + 1, HENT*);
+ do {
+ if (entry)
+ entry = entry->hent_next;
+ if (!entry) {
+ tb->tbl_riter++;
+ if (tb->tbl_riter > tb->tbl_max) {
+ tb->tbl_riter = -1;
+ break;
+ }
+ entry = tb->tbl_array[tb->tbl_riter];
+ }
+ } while (!entry);
+
+ tb->tbl_eiter = entry;
+ return entry;
+}
+
+char *
+hiterkey(entry,retlen)
+register HENT *entry;
+int *retlen;
+{
+ *retlen = entry->hent_klen;
+ return entry->hent_key;
+}
+
+STR *
+hiterval(tb,entry)
+register HASH *tb;
+register HENT *entry;
+{
+#ifdef SOME_DBM
+ datum key, content;
+
+ if (tb->tbl_dbm) {
+ key.dptr = entry->hent_key;
+ key.dsize = entry->hent_klen;
+#ifdef HAS_GDBM
+ content = gdbm_fetch(tb->tbl_dbm,key);
+#else
+ content = dbm_fetch(tb->tbl_dbm,key);
+#endif
+ if (!entry->hent_val)
+ entry->hent_val = Str_new(62,0);
+ str_nset(entry->hent_val,content.dptr,content.dsize);
+ }
+#endif
+ return entry->hent_val;
+}
+
+#ifdef SOME_DBM
+
+#ifndef O_CREAT
+# ifdef I_FCNTL
+# include <fcntl.h>
+# endif
+# ifdef I_SYS_FILE
+# include <sys/file.h>
+# endif
+#endif
+
+#ifndef O_RDONLY
+#define O_RDONLY 0
+#endif
+#ifndef O_RDWR
+#define O_RDWR 2
+#endif
+#ifndef O_CREAT
+#define O_CREAT 01000
+#endif
+
+#ifdef HAS_ODBM
+static int dbmrefcnt = 0;
+#endif
+
+bool
+hdbmopen(tb,fname,mode)
+register HASH *tb;
+char *fname;
+int mode;
+{
+ if (!tb)
+ return FALSE;
+#ifdef HAS_ODBM
+ if (tb->tbl_dbm) /* never really closed it */
+ return TRUE;
+#endif
+ if (tb->tbl_dbm) {
+ hdbmclose(tb);
+ tb->tbl_dbm = 0;
+ }
+ hclear(tb, FALSE); /* clear cache */
+#ifdef HAS_GDBM
+ if (mode >= 0)
+ tb->tbl_dbm = gdbm_open(fname, 0, GDBM_WRCREAT,mode, (void *) NULL);
+ if (!tb->tbl_dbm)
+ tb->tbl_dbm = gdbm_open(fname, 0, GDBM_WRITER, mode, (void *) NULL);
+ if (!tb->tbl_dbm)
+ tb->tbl_dbm = gdbm_open(fname, 0, GDBM_READER, mode, (void *) NULL);
+#else
+#ifdef HAS_NDBM
+ if (mode >= 0)
+ tb->tbl_dbm = dbm_open(fname, O_RDWR|O_CREAT, mode);
+ if (!tb->tbl_dbm)
+ tb->tbl_dbm = dbm_open(fname, O_RDWR, mode);
+ if (!tb->tbl_dbm)
+ tb->tbl_dbm = dbm_open(fname, O_RDONLY, mode);
+#else
+ if (dbmrefcnt++)
+ fatal("Old dbm can only open one database");
+ sprintf(buf,"%s.dir",fname);
+ if (stat(buf, &statbuf) < 0) {
+ if (mode < 0 || close(creat(buf,mode)) < 0)
+ return FALSE;
+ sprintf(buf,"%s.pag",fname);
+ if (close(creat(buf,mode)) < 0)
+ return FALSE;
+ }
+ tb->tbl_dbm = dbminit(fname) >= 0;
+#endif
+#endif
+ if (!tb->tbl_array && tb->tbl_dbm != 0)
+ Newz(507,tb->tbl_array, tb->tbl_max + 1, HENT*);
+ return tb->tbl_dbm != 0;
+}
+
+void
+hdbmclose(tb)
+register HASH *tb;
+{
+ if (tb && tb->tbl_dbm) {
+#ifdef HAS_GDBM
+ gdbm_close(tb->tbl_dbm);
+ tb->tbl_dbm = 0;
+#else
+#ifdef HAS_NDBM
+ dbm_close(tb->tbl_dbm);
+ tb->tbl_dbm = 0;
+#else
+ /* dbmrefcnt--; */ /* doesn't work, rats */
+#endif
+#endif
+ }
+ else if (dowarn)
+ warn("Close on unopened dbm file");
+}
+
+bool
+hdbmstore(tb,key,klen,str)
+register HASH *tb;
+char *key;
+unsigned int klen;
+register STR *str;
+{
+ datum dkey, dcontent;
+ int error;
+
+ if (!tb || !tb->tbl_dbm)
+ return FALSE;
+ dkey.dptr = key;
+ dkey.dsize = klen;
+ dcontent.dptr = str_get(str);
+ dcontent.dsize = str->str_cur;
+#ifdef HAS_GDBM
+ error = gdbm_store(tb->tbl_dbm, dkey, dcontent, GDBM_REPLACE);
+#else
+ error = dbm_store(tb->tbl_dbm, dkey, dcontent, DBM_REPLACE);
+#endif
+ if (error) {
+ if (errno == EPERM)
+ fatal("No write permission to dbm file");
+ warn("dbm store returned %d, errno %d, key \"%s\"",error,errno,key);
+#ifdef HAS_NDBM
+ dbm_clearerr(tb->tbl_dbm);
+#endif
+ }
+ return !error;
+}
+#endif /* SOME_DBM */
diff --git a/gnu/usr.bin/perl/perl/hash.h b/gnu/usr.bin/perl/perl/hash.h
new file mode 100644
index 0000000..858721f
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/hash.h
@@ -0,0 +1,75 @@
+/* $RCSfile: hash.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:37 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: hash.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:37 nate
+ * PERL!
+ *
+ * Revision 4.0.1.2 91/11/05 17:24:31 lwall
+ * patch11: random cleanup
+ *
+ * Revision 4.0.1.1 91/06/07 11:10:33 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:22:38 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#define FILLPCT 80 /* don't make greater than 99 */
+#define DBM_CACHE_MAX 63 /* cache 64 entries for dbm file */
+ /* (resident array acts as a write-thru cache)*/
+
+#define COEFFSIZE (16 * 8) /* size of coeff array */
+
+typedef struct hentry HENT;
+
+struct hentry {
+ HENT *hent_next;
+ char *hent_key;
+ STR *hent_val;
+ int hent_hash;
+ int hent_klen;
+};
+
+struct htbl {
+ HENT **tbl_array;
+ int tbl_max; /* subscript of last element of tbl_array */
+ int tbl_dosplit; /* how full to get before splitting */
+ int tbl_fill; /* how full tbl_array currently is */
+ int tbl_riter; /* current root of iterator */
+ HENT *tbl_eiter; /* current entry of iterator */
+ SPAT *tbl_spatroot; /* list of spats for this package */
+ char *tbl_name; /* name, if a symbol table */
+#ifdef SOME_DBM
+#ifdef HAS_GDBM
+ GDBM_FILE tbl_dbm;
+#else
+#ifdef HAS_NDBM
+ DBM *tbl_dbm;
+#else
+ int tbl_dbm;
+#endif
+#endif
+#endif
+ unsigned char tbl_coeffsize; /* is 0 for symbol tables */
+};
+
+STR *hfetch();
+bool hstore();
+STR *hdelete();
+HASH *hnew();
+void hclear();
+void hentfree();
+void hfree();
+int hiterinit();
+HENT *hiternext();
+char *hiterkey();
+STR *hiterval();
+bool hdbmopen();
+void hdbmclose();
+bool hdbmstore();
diff --git a/gnu/usr.bin/perl/perl/malloc.c b/gnu/usr.bin/perl/perl/malloc.c
new file mode 100644
index 0000000..8bfee40
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/malloc.c
@@ -0,0 +1,510 @@
+/* $RCSfile: malloc.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:37 $
+ *
+ * $Log: malloc.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:37 nate
+ * PERL!
+ *
+ * Revision 4.0.1.4 92/06/08 14:28:38 lwall
+ * patch20: removed implicit int declarations on functions
+ * patch20: hash tables now split only if the memory is available to do so
+ * patch20: realloc(0, size) now does malloc in case library routines call it
+ *
+ * Revision 4.0.1.3 91/11/05 17:57:40 lwall
+ * patch11: safe malloc code now integrated into Perl's malloc when possible
+ *
+ * Revision 4.0.1.2 91/06/07 11:20:45 lwall
+ * patch4: many, many itty-bitty portability fixes
+ *
+ * Revision 4.0.1.1 91/04/11 17:48:31 lwall
+ * patch1: Configure now figures out malloc ptr type
+ *
+ * Revision 4.0 91/03/20 01:28:52 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#ifndef lint
+/*SUPPRESS 592*/
+static char sccsid[] = "@(#)malloc.c 4.3 (Berkeley) 9/16/83";
+
+#ifdef DEBUGGING
+#define RCHECK
+#endif
+/*
+ * malloc.c (Caltech) 2/21/82
+ * Chris Kingsley, kingsley@cit-20.
+ *
+ * This is a very fast storage allocator. It allocates blocks of a small
+ * number of different sizes, and keeps free lists of each size. Blocks that
+ * don't exactly fit are passed up to the next larger size. In this
+ * implementation, the available sizes are 2^n-4 (or 2^n-12) bytes long.
+ * This is designed for use in a program that uses vast quantities of memory,
+ * but bombs when it runs out.
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+
+static findbucket(), morecore();
+
+/* I don't much care whether these are defined in sys/types.h--LAW */
+
+#define u_char unsigned char
+#define u_int unsigned int
+#define u_short unsigned short
+
+/*
+ * The overhead on a block is at least 4 bytes. When free, this space
+ * contains a pointer to the next free block, and the bottom two bits must
+ * be zero. When in use, the first byte is set to MAGIC, and the second
+ * byte is the size index. The remaining bytes are for alignment.
+ * If range checking is enabled and the size of the block fits
+ * in two bytes, then the top two bytes hold the size of the requested block
+ * plus the range checking words, and the header word MINUS ONE.
+ */
+union overhead {
+ union overhead *ov_next; /* when free */
+#if ALIGN_BYTES > 4
+ double strut; /* alignment problems */
+#endif
+ struct {
+ u_char ovu_magic; /* magic number */
+ u_char ovu_index; /* bucket # */
+#ifdef RCHECK
+ u_short ovu_size; /* actual block size */
+ u_int ovu_rmagic; /* range magic number */
+#endif
+ } ovu;
+#define ov_magic ovu.ovu_magic
+#define ov_index ovu.ovu_index
+#define ov_size ovu.ovu_size
+#define ov_rmagic ovu.ovu_rmagic
+};
+
+#define MAGIC 0xff /* magic # on accounting info */
+#define OLDMAGIC 0x7f /* same after a free() */
+#define RMAGIC 0x55555555 /* magic # on range info */
+#ifdef RCHECK
+#define RSLOP sizeof (u_int)
+#else
+#define RSLOP 0
+#endif
+
+/*
+ * nextf[i] is the pointer to the next free block of size 2^(i+3). The
+ * smallest allocatable block is 8 bytes. The overhead information
+ * precedes the data area returned to the user.
+ */
+#define NBUCKETS 30
+static union overhead *nextf[NBUCKETS];
+extern char *sbrk();
+
+#ifdef MSTATS
+/*
+ * nmalloc[i] is the difference between the number of mallocs and frees
+ * for a given block size.
+ */
+static u_int nmalloc[NBUCKETS];
+#include <stdio.h>
+#endif
+
+#ifdef debug
+#define ASSERT(p) if (!(p)) botch("p"); else
+static void
+botch(s)
+ char *s;
+{
+
+ printf("assertion botched: %s\n", s);
+ abort();
+}
+#else
+#define ASSERT(p)
+#endif
+
+#ifdef safemalloc
+static int an = 0;
+#endif
+
+MALLOCPTRTYPE *
+malloc(nbytes)
+ register MEM_SIZE nbytes;
+{
+ register union overhead *p;
+ register int bucket = 0;
+ register MEM_SIZE shiftr;
+
+#ifdef safemalloc
+#ifdef DEBUGGING
+ MEM_SIZE size = nbytes;
+#endif
+
+#ifdef MSDOS
+ if (nbytes > 0xffff) {
+ fprintf(stderr, "Allocation too large: %lx\n", (long)nbytes);
+ exit(1);
+ }
+#endif /* MSDOS */
+#ifdef DEBUGGING
+ if ((long)nbytes < 0)
+ fatal("panic: malloc");
+#endif
+#endif /* safemalloc */
+
+ /*
+ * Convert amount of memory requested into
+ * closest block size stored in hash buckets
+ * which satisfies request. Account for
+ * space used per block for accounting.
+ */
+ nbytes += sizeof (union overhead) + RSLOP;
+ nbytes = (nbytes + 3) &~ 3;
+ shiftr = (nbytes - 1) >> 2;
+ /* apart from this loop, this is O(1) */
+ while (shiftr >>= 1)
+ bucket++;
+ /*
+ * If nothing in hash bucket right now,
+ * request more memory from the system.
+ */
+ if (nextf[bucket] == NULL)
+ morecore(bucket);
+ if ((p = (union overhead *)nextf[bucket]) == NULL) {
+#ifdef safemalloc
+ if (!nomemok) {
+ fputs("Out of memory!\n", stderr);
+ exit(1);
+ }
+#else
+ return (NULL);
+#endif
+ }
+
+#ifdef safemalloc
+#ifdef DEBUGGING
+# if !(defined(I286) || defined(atarist))
+ if (debug & 128)
+ fprintf(stderr,"0x%x: (%05d) malloc %ld bytes\n",p+1,an++,(long)size);
+# else
+ if (debug & 128)
+ fprintf(stderr,"0x%lx: (%05d) malloc %ld bytes\n",p+1,an++,(long)size);
+# endif
+#endif
+#endif /* safemalloc */
+
+ /* remove from linked list */
+#ifdef RCHECK
+ if (*((int*)p) & (sizeof(union overhead) - 1))
+#if !(defined(I286) || defined(atarist))
+ fprintf(stderr,"Corrupt malloc ptr 0x%x at 0x%x\n",*((int*)p),p);
+#else
+ fprintf(stderr,"Corrupt malloc ptr 0x%lx at 0x%lx\n",*((int*)p),p);
+#endif
+#endif
+ nextf[bucket] = p->ov_next;
+ p->ov_magic = MAGIC;
+ p->ov_index= bucket;
+#ifdef MSTATS
+ nmalloc[bucket]++;
+#endif
+#ifdef RCHECK
+ /*
+ * Record allocated size of block and
+ * bound space with magic numbers.
+ */
+ if (nbytes <= 0x10000)
+ p->ov_size = nbytes - 1;
+ p->ov_rmagic = RMAGIC;
+ *((u_int *)((caddr_t)p + nbytes - RSLOP)) = RMAGIC;
+#endif
+ return ((MALLOCPTRTYPE *)(p + 1));
+}
+
+/*
+ * Allocate more memory to the indicated bucket.
+ */
+static
+morecore(bucket)
+ register int bucket;
+{
+ register union overhead *op;
+ register int rnu; /* 2^rnu bytes will be requested */
+ register int nblks; /* become nblks blocks of the desired size */
+ register MEM_SIZE siz;
+
+ if (nextf[bucket])
+ return;
+ /*
+ * Insure memory is allocated
+ * on a page boundary. Should
+ * make getpageize call?
+ */
+#ifndef atarist /* on the atari we dont have to worry about this */
+ op = (union overhead *)sbrk(0);
+#ifndef I286
+ if ((int)op & 0x3ff)
+ (void)sbrk(1024 - ((int)op & 0x3ff));
+#else
+ /* The sbrk(0) call on the I286 always returns the next segment */
+#endif
+#endif /* atarist */
+
+#if !(defined(I286) || defined(atarist))
+ /* take 2k unless the block is bigger than that */
+ rnu = (bucket <= 8) ? 11 : bucket + 3;
+#else
+ /* take 16k unless the block is bigger than that
+ (80286s like large segments!), probably good on the atari too */
+ rnu = (bucket <= 11) ? 14 : bucket + 3;
+#endif
+ nblks = 1 << (rnu - (bucket + 3)); /* how many blocks to get */
+ if (rnu < bucket)
+ rnu = bucket;
+ op = (union overhead *)sbrk(1L << rnu);
+ /* no more room! */
+ if ((int)op == -1)
+ return;
+ /*
+ * Round up to minimum allocation size boundary
+ * and deduct from block count to reflect.
+ */
+#ifndef I286
+ if ((int)op & 7) {
+ op = (union overhead *)(((MEM_SIZE)op + 8) &~ 7);
+ nblks--;
+ }
+#else
+ /* Again, this should always be ok on an 80286 */
+#endif
+ /*
+ * Add new memory allocated to that on
+ * free list for this hash bucket.
+ */
+ nextf[bucket] = op;
+ siz = 1 << (bucket + 3);
+ while (--nblks > 0) {
+ op->ov_next = (union overhead *)((caddr_t)op + siz);
+ op = (union overhead *)((caddr_t)op + siz);
+ }
+}
+
+void
+free(mp)
+ MALLOCPTRTYPE *mp;
+{
+ register MEM_SIZE size;
+ register union overhead *op;
+ char *cp = (char*)mp;
+
+#ifdef safemalloc
+#ifdef DEBUGGING
+# if !(defined(I286) || defined(atarist))
+ if (debug & 128)
+ fprintf(stderr,"0x%x: (%05d) free\n",cp,an++);
+# else
+ if (debug & 128)
+ fprintf(stderr,"0x%lx: (%05d) free\n",cp,an++);
+# endif
+#endif
+#endif /* safemalloc */
+
+ if (cp == NULL)
+ return;
+ op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
+#ifdef debug
+ ASSERT(op->ov_magic == MAGIC); /* make sure it was in use */
+#else
+ if (op->ov_magic != MAGIC) {
+ warn("%s free() ignored",
+ op->ov_magic == OLDMAGIC ? "Duplicate" : "Bad");
+ return; /* sanity */
+ }
+ op->ov_magic = OLDMAGIC;
+#endif
+#ifdef RCHECK
+ ASSERT(op->ov_rmagic == RMAGIC);
+ if (op->ov_index <= 13)
+ ASSERT(*(u_int *)((caddr_t)op + op->ov_size + 1 - RSLOP) == RMAGIC);
+#endif
+ ASSERT(op->ov_index < NBUCKETS);
+ size = op->ov_index;
+ op->ov_next = nextf[size];
+ nextf[size] = op;
+#ifdef MSTATS
+ nmalloc[size]--;
+#endif
+}
+
+/*
+ * When a program attempts "storage compaction" as mentioned in the
+ * old malloc man page, it realloc's an already freed block. Usually
+ * this is the last block it freed; occasionally it might be farther
+ * back. We have to search all the free lists for the block in order
+ * to determine its bucket: 1st we make one pass thru the lists
+ * checking only the first block in each; if that fails we search
+ * ``reall_srchlen'' blocks in each list for a match (the variable
+ * is extern so the caller can modify it). If that fails we just copy
+ * however many bytes was given to realloc() and hope it's not huge.
+ */
+int reall_srchlen = 4; /* 4 should be plenty, -1 =>'s whole list */
+
+MALLOCPTRTYPE *
+realloc(mp, nbytes)
+ MALLOCPTRTYPE *mp;
+ MEM_SIZE nbytes;
+{
+ register MEM_SIZE onb;
+ union overhead *op;
+ char *res;
+ register int i;
+ int was_alloced = 0;
+ char *cp = (char*)mp;
+
+#ifdef safemalloc
+#ifdef DEBUGGING
+ MEM_SIZE size = nbytes;
+#endif
+
+#ifdef MSDOS
+ if (nbytes > 0xffff) {
+ fprintf(stderr, "Reallocation too large: %lx\n", size);
+ exit(1);
+ }
+#endif /* MSDOS */
+ if (!cp)
+ return malloc(nbytes);
+#ifdef DEBUGGING
+ if ((long)nbytes < 0)
+ fatal("panic: realloc");
+#endif
+#endif /* safemalloc */
+
+ op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
+ if (op->ov_magic == MAGIC) {
+ was_alloced++;
+ i = op->ov_index;
+ } else {
+ /*
+ * Already free, doing "compaction".
+ *
+ * Search for the old block of memory on the
+ * free list. First, check the most common
+ * case (last element free'd), then (this failing)
+ * the last ``reall_srchlen'' items free'd.
+ * If all lookups fail, then assume the size of
+ * the memory block being realloc'd is the
+ * smallest possible.
+ */
+ if ((i = findbucket(op, 1)) < 0 &&
+ (i = findbucket(op, reall_srchlen)) < 0)
+ i = 0;
+ }
+ onb = (1L << (i + 3)) - sizeof (*op) - RSLOP;
+ /* avoid the copy if same size block */
+ if (was_alloced &&
+ nbytes <= onb && nbytes > (onb >> 1) - sizeof(*op) - RSLOP) {
+#ifdef RCHECK
+ /*
+ * Record new allocated size of block and
+ * bound space with magic numbers.
+ */
+ if (op->ov_index <= 13) {
+ /*
+ * Convert amount of memory requested into
+ * closest block size stored in hash buckets
+ * which satisfies request. Account for
+ * space used per block for accounting.
+ */
+ nbytes += sizeof (union overhead) + RSLOP;
+ nbytes = (nbytes + 3) &~ 3;
+ op->ov_size = nbytes - 1;
+ *((u_int *)((caddr_t)op + nbytes - RSLOP)) = RMAGIC;
+ }
+#endif
+ res = cp;
+ }
+ else {
+ if ((res = (char*)malloc(nbytes)) == NULL)
+ return (NULL);
+ if (cp != res) /* common optimization */
+ Copy(cp, res, (MEM_SIZE)(nbytes<onb?nbytes:onb), char);
+ if (was_alloced)
+ free(cp);
+ }
+
+#ifdef safemalloc
+#ifdef DEBUGGING
+# if !(defined(I286) || defined(atarist))
+ if (debug & 128) {
+ fprintf(stderr,"0x%x: (%05d) rfree\n",res,an++);
+ fprintf(stderr,"0x%x: (%05d) realloc %ld bytes\n",res,an++,(long)size);
+ }
+# else
+ if (debug & 128) {
+ fprintf(stderr,"0x%lx: (%05d) rfree\n",res,an++);
+ fprintf(stderr,"0x%lx: (%05d) realloc %ld bytes\n",res,an++,(long)size);
+ }
+# endif
+#endif
+#endif /* safemalloc */
+ return ((MALLOCPTRTYPE*)res);
+}
+
+/*
+ * Search ``srchlen'' elements of each free list for a block whose
+ * header starts at ``freep''. If srchlen is -1 search the whole list.
+ * Return bucket number, or -1 if not found.
+ */
+static int
+findbucket(freep, srchlen)
+ union overhead *freep;
+ int srchlen;
+{
+ register union overhead *p;
+ register int i, j;
+
+ for (i = 0; i < NBUCKETS; i++) {
+ j = 0;
+ for (p = nextf[i]; p && j != srchlen; p = p->ov_next) {
+ if (p == freep)
+ return (i);
+ j++;
+ }
+ }
+ return (-1);
+}
+
+#ifdef MSTATS
+/*
+ * mstats - print out statistics about malloc
+ *
+ * Prints two lines of numbers, one showing the length of the free list
+ * for each size category, the second showing the number of mallocs -
+ * frees for each size category.
+ */
+void
+mstats(s)
+ char *s;
+{
+ register int i, j;
+ register union overhead *p;
+ int totfree = 0,
+ totused = 0;
+
+ fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s);
+ for (i = 0; i < NBUCKETS; i++) {
+ for (j = 0, p = nextf[i]; p; p = p->ov_next, j++)
+ ;
+ fprintf(stderr, " %d", j);
+ totfree += j * (1 << (i + 3));
+ }
+ fprintf(stderr, "\nused:\t");
+ for (i = 0; i < NBUCKETS; i++) {
+ fprintf(stderr, " %d", nmalloc[i]);
+ totused += nmalloc[i] * (1 << (i + 3));
+ }
+ fprintf(stderr, "\n\tTotal in use: %d, total free: %d\n",
+ totused, totfree);
+}
+#endif
+#endif /* lint */
diff --git a/gnu/usr.bin/perl/perl/patchlevel.h b/gnu/usr.bin/perl/perl/patchlevel.h
new file mode 100644
index 0000000..d248b35
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/patchlevel.h
@@ -0,0 +1 @@
+#define PATCHLEVEL 36
diff --git a/gnu/usr.bin/perl/perl/perl.1 b/gnu/usr.bin/perl/perl/perl.1
new file mode 100644
index 0000000..d074e74
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/perl.1
@@ -0,0 +1,6010 @@
+.rn '' }`
+''' $RCSfile: perl.man,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:37 $
+'''
+''' $Log: perl.man,v $
+.\" Revision 1.1.1.1 1993/08/23 21:29:37 nate
+.\" PERL!
+.\"
+''' Revision 4.0.1.6 92/06/08 15:07:29 lwall
+''' patch20: documented that numbers may contain underline
+''' patch20: clarified that DATA may only be read from main script
+''' patch20: relaxed requirement for semicolon at the end of a block
+''' patch20: added ... as variant on ..
+''' patch20: documented need for 1; at the end of a required file
+''' patch20: extended bracket-style quotes to two-arg operators: s()() and tr()()
+''' patch20: paragraph mode now skips extra newlines automatically
+''' patch20: documented PERLLIB and PERLDB
+''' patch20: documented limit on size of regexp
+'''
+''' Revision 4.0.1.5 91/11/11 16:42:00 lwall
+''' patch19: added little-endian pack/unpack options
+'''
+''' Revision 4.0.1.4 91/11/05 18:11:05 lwall
+''' patch11: added sort {} LIST
+''' patch11: added eval {}
+''' patch11: documented meaning of scalar(%foo)
+''' patch11: sprintf() now supports any length of s field
+'''
+''' Revision 4.0.1.3 91/06/10 01:26:02 lwall
+''' patch10: documented some newer features in addenda
+'''
+''' Revision 4.0.1.2 91/06/07 11:41:23 lwall
+''' patch4: added global modifier for pattern matches
+''' patch4: default top-of-form format is now FILEHANDLE_TOP
+''' patch4: added $^P variable to control calling of perldb routines
+''' patch4: added $^F variable to specify maximum system fd, default 2
+''' patch4: changed old $^P to $^X
+'''
+''' Revision 4.0.1.1 91/04/11 17:50:44 lwall
+''' patch1: fixed some typos
+'''
+''' Revision 4.0 91/03/20 01:38:08 lwall
+''' 4.0 baseline.
+'''
+'''
+.de Sh
+.br
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp
+.if t .sp .5v
+.if n .sp
+..
+.de Ip
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+'''
+''' Set up \*(-- to give an unbreakable dash;
+''' string Tr holds user defined translation string.
+''' Bell System Logo is used as a dummy character.
+'''
+.tr \(*W-|\(bv\*(Tr
+.ie n \{\
+.ds -- \(*W-
+.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+.ds L" ""
+.ds R" ""
+.ds L' '
+.ds R' '
+'br\}
+.el\{\
+.ds -- \(em\|
+.tr \*(Tr
+.ds L" ``
+.ds R" ''
+.ds L' `
+.ds R' '
+'br\}
+.TH PERL 1 "\*(RP"
+.UC
+.SH NAME
+perl \- Practical Extraction and Report Language
+.SH SYNOPSIS
+.B perl
+[options] filename args
+.SH DESCRIPTION
+.I Perl
+is an interpreted language optimized for scanning arbitrary text files,
+extracting information from those text files, and printing reports based
+on that information.
+It's also a good language for many system management tasks.
+The language is intended to be practical (easy to use, efficient, complete)
+rather than beautiful (tiny, elegant, minimal).
+It combines (in the author's opinion, anyway) some of the best features of C,
+\fIsed\fR, \fIawk\fR, and \fIsh\fR,
+so people familiar with those languages should have little difficulty with it.
+(Language historians will also note some vestiges of \fIcsh\fR, Pascal, and
+even BASIC-PLUS.)
+Expression syntax corresponds quite closely to C expression syntax.
+Unlike most Unix utilities,
+.I perl
+does not arbitrarily limit the size of your data\*(--if you've got
+the memory,
+.I perl
+can slurp in your whole file as a single string.
+Recursion is of unlimited depth.
+And the hash tables used by associative arrays grow as necessary to prevent
+degraded performance.
+.I Perl
+uses sophisticated pattern matching techniques to scan large amounts of
+data very quickly.
+Although optimized for scanning text,
+.I perl
+can also deal with binary data, and can make dbm files look like associative
+arrays (where dbm is available).
+Setuid
+.I perl
+scripts are safer than C programs
+through a dataflow tracing mechanism which prevents many stupid security holes.
+If you have a problem that would ordinarily use \fIsed\fR
+or \fIawk\fR or \fIsh\fR, but it
+exceeds their capabilities or must run a little faster,
+and you don't want to write the silly thing in C, then
+.I perl
+may be for you.
+There are also translators to turn your
+.I sed
+and
+.I awk
+scripts into
+.I perl
+scripts.
+OK, enough hype.
+.PP
+Upon startup,
+.I perl
+looks for your script in one of the following places:
+.Ip 1. 4 2
+Specified line by line via
+.B \-e
+switches on the command line.
+.Ip 2. 4 2
+Contained in the file specified by the first filename on the command line.
+(Note that systems supporting the #! notation invoke interpreters this way.)
+.Ip 3. 4 2
+Passed in implicitly via standard input.
+This only works if there are no filename arguments\*(--to pass
+arguments to a
+.I stdin
+script you must explicitly specify a \- for the script name.
+.PP
+After locating your script,
+.I perl
+compiles it to an internal form.
+If the script is syntactically correct, it is executed.
+.Sh "Options"
+Note: on first reading this section may not make much sense to you. It's here
+at the front for easy reference.
+.PP
+A single-character option may be combined with the following option, if any.
+This is particularly useful when invoking a script using the #! construct which
+only allows one argument. Example:
+.nf
+
+.ne 2
+ #!/usr/bin/perl \-spi.bak # same as \-s \-p \-i.bak
+ .\|.\|.
+
+.fi
+Options include:
+.TP 5
+.BI \-0 digits
+specifies the record separator ($/) as an octal number.
+If there are no digits, the null character is the separator.
+Other switches may precede or follow the digits.
+For example, if you have a version of
+.I find
+which can print filenames terminated by the null character, you can say this:
+.nf
+
+ find . \-name '*.bak' \-print0 | perl \-n0e unlink
+
+.fi
+The special value 00 will cause Perl to slurp files in paragraph mode.
+The value 0777 will cause Perl to slurp files whole since there is no
+legal character with that value.
+.TP 5
+.B \-a
+turns on autosplit mode when used with a
+.B \-n
+or
+.BR \-p .
+An implicit split command to the @F array
+is done as the first thing inside the implicit while loop produced by
+the
+.B \-n
+or
+.BR \-p .
+.nf
+
+ perl \-ane \'print pop(@F), "\en";\'
+
+is equivalent to
+
+ while (<>) {
+ @F = split(\' \');
+ print pop(@F), "\en";
+ }
+
+.fi
+.TP 5
+.B \-c
+causes
+.I perl
+to check the syntax of the script and then exit without executing it.
+.TP 5
+.BI \-d
+runs the script under the perl debugger.
+See the section on Debugging.
+.TP 5
+.BI \-D number
+sets debugging flags.
+To watch how it executes your script, use
+.BR \-D14 .
+(This only works if debugging is compiled into your
+.IR perl .)
+Another nice value is \-D1024, which lists your compiled syntax tree.
+And \-D512 displays compiled regular expressions.
+.TP 5
+.BI \-e " commandline"
+may be used to enter one line of script.
+Multiple
+.B \-e
+commands may be given to build up a multi-line script.
+If
+.B \-e
+is given,
+.I perl
+will not look for a script filename in the argument list.
+.TP 5
+.BI \-i extension
+specifies that files processed by the <> construct are to be edited
+in-place.
+It does this by renaming the input file, opening the output file by the
+same name, and selecting that output file as the default for print statements.
+The extension, if supplied, is added to the name of the
+old file to make a backup copy.
+If no extension is supplied, no backup is made.
+Saying \*(L"perl \-p \-i.bak \-e "s/foo/bar/;" .\|.\|. \*(R" is the same as using
+the script:
+.nf
+
+.ne 2
+ #!/usr/bin/perl \-pi.bak
+ s/foo/bar/;
+
+which is equivalent to
+
+.ne 14
+ #!/usr/bin/perl
+ while (<>) {
+ if ($ARGV ne $oldargv) {
+ rename($ARGV, $ARGV . \'.bak\');
+ open(ARGVOUT, ">$ARGV");
+ select(ARGVOUT);
+ $oldargv = $ARGV;
+ }
+ s/foo/bar/;
+ }
+ continue {
+ print; # this prints to original filename
+ }
+ select(STDOUT);
+
+.fi
+except that the
+.B \-i
+form doesn't need to compare $ARGV to $oldargv to know when
+the filename has changed.
+It does, however, use ARGVOUT for the selected filehandle.
+Note that
+.I STDOUT
+is restored as the default output filehandle after the loop.
+.Sp
+You can use eof to locate the end of each input file, in case you want
+to append to each file, or reset line numbering (see example under eof).
+.TP 5
+.BI \-I directory
+may be used in conjunction with
+.B \-P
+to tell the C preprocessor where to look for include files.
+By default /usr/include and /usr/lib/perl are searched.
+.TP 5
+.BI \-l octnum
+enables automatic line-ending processing. It has two effects:
+first, it automatically chops the line terminator when used with
+.B \-n
+or
+.B \-p ,
+and second, it assigns $\e to have the value of
+.I octnum
+so that any print statements will have that line terminator added back on. If
+.I octnum
+is omitted, sets $\e to the current value of $/.
+For instance, to trim lines to 80 columns:
+.nf
+
+ perl -lpe \'substr($_, 80) = ""\'
+
+.fi
+Note that the assignment $\e = $/ is done when the switch is processed,
+so the input record separator can be different than the output record
+separator if the
+.B \-l
+switch is followed by a
+.B \-0
+switch:
+.nf
+
+ gnufind / -print0 | perl -ln0e 'print "found $_" if -p'
+
+.fi
+This sets $\e to newline and then sets $/ to the null character.
+.TP 5
+.B \-n
+causes
+.I perl
+to assume the following loop around your script, which makes it iterate
+over filename arguments somewhat like \*(L"sed \-n\*(R" or \fIawk\fR:
+.nf
+
+.ne 3
+ while (<>) {
+ .\|.\|. # your script goes here
+ }
+
+.fi
+Note that the lines are not printed by default.
+See
+.B \-p
+to have lines printed.
+Here is an efficient way to delete all files older than a week:
+.nf
+
+ find . \-mtime +7 \-print | perl \-nle \'unlink;\'
+
+.fi
+This is faster than using the \-exec switch of find because you don't have to
+start a process on every filename found.
+.TP 5
+.B \-p
+causes
+.I perl
+to assume the following loop around your script, which makes it iterate
+over filename arguments somewhat like \fIsed\fR:
+.nf
+
+.ne 5
+ while (<>) {
+ .\|.\|. # your script goes here
+ } continue {
+ print;
+ }
+
+.fi
+Note that the lines are printed automatically.
+To suppress printing use the
+.B \-n
+switch.
+A
+.B \-p
+overrides a
+.B \-n
+switch.
+.TP 5
+.B \-P
+causes your script to be run through the C preprocessor before
+compilation by
+.IR perl .
+(Since both comments and cpp directives begin with the # character,
+you should avoid starting comments with any words recognized
+by the C preprocessor such as \*(L"if\*(R", \*(L"else\*(R" or \*(L"define\*(R".)
+.TP 5
+.B \-s
+enables some rudimentary switch parsing for switches on the command line
+after the script name but before any filename arguments (or before a \-\|\-).
+Any switch found there is removed from @ARGV and sets the corresponding variable in the
+.I perl
+script.
+The following script prints \*(L"true\*(R" if and only if the script is
+invoked with a \-xyz switch.
+.nf
+
+.ne 2
+ #!/usr/bin/perl \-s
+ if ($xyz) { print "true\en"; }
+
+.fi
+.TP 5
+.B \-S
+makes
+.I perl
+use the PATH environment variable to search for the script
+(unless the name of the script starts with a slash).
+Typically this is used to emulate #! startup on machines that don't
+support #!, in the following manner:
+.nf
+
+ #!/usr/bin/perl
+ eval "exec /usr/bin/perl \-S $0 $*"
+ if $running_under_some_shell;
+
+.fi
+The system ignores the first line and feeds the script to /bin/sh,
+which proceeds to try to execute the
+.I perl
+script as a shell script.
+The shell executes the second line as a normal shell command, and thus
+starts up the
+.I perl
+interpreter.
+On some systems $0 doesn't always contain the full pathname,
+so the
+.B \-S
+tells
+.I perl
+to search for the script if necessary.
+After
+.I perl
+locates the script, it parses the lines and ignores them because
+the variable $running_under_some_shell is never true.
+A better construct than $* would be ${1+"$@"}, which handles embedded spaces
+and such in the filenames, but doesn't work if the script is being interpreted
+by csh.
+In order to start up sh rather than csh, some systems may have to replace the
+#! line with a line containing just
+a colon, which will be politely ignored by perl.
+Other systems can't control that, and need a totally devious construct that
+will work under any of csh, sh or perl, such as the following:
+.nf
+
+.ne 3
+ eval '(exit $?0)' && eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
+ & eval 'exec /usr/bin/perl -S $0 $argv:q'
+ if 0;
+
+.fi
+.TP 5
+.B \-u
+causes
+.I perl
+to dump core after compiling your script.
+You can then take this core dump and turn it into an executable file
+by using the undump program (not supplied).
+This speeds startup at the expense of some disk space (which you can
+minimize by stripping the executable).
+(Still, a "hello world" executable comes out to about 200K on my machine.)
+If you are going to run your executable as a set-id program then you
+should probably compile it using taintperl rather than normal perl.
+If you want to execute a portion of your script before dumping, use the
+dump operator instead.
+Note: availability of undump is platform specific and may not be available
+for a specific port of perl.
+.TP 5
+.B \-U
+allows
+.I perl
+to do unsafe operations.
+Currently the only \*(L"unsafe\*(R" operations are the unlinking of directories while
+running as superuser, and running setuid programs with fatal taint checks
+turned into warnings.
+.TP 5
+.B \-v
+prints the version and patchlevel of your
+.I perl
+executable.
+.TP 5
+.B \-w
+prints warnings about identifiers that are mentioned only once, and scalar
+variables that are used before being set.
+Also warns about redefined subroutines, and references to undefined
+filehandles or filehandles opened readonly that you are attempting to
+write on.
+Also warns you if you use == on values that don't look like numbers, and if
+your subroutines recurse more than 100 deep.
+.TP 5
+.BI \-x directory
+tells
+.I perl
+that the script is embedded in a message.
+Leading garbage will be discarded until the first line that starts
+with #! and contains the string "perl".
+Any meaningful switches on that line will be applied (but only one
+group of switches, as with normal #! processing).
+If a directory name is specified, Perl will switch to that directory
+before running the script.
+The
+.B \-x
+switch only controls the the disposal of leading garbage.
+The script must be terminated with _\|_END_\|_ if there is trailing garbage
+to be ignored (the script can process any or all of the trailing garbage
+via the DATA filehandle if desired).
+.Sh "Data Types and Objects"
+.PP
+.I Perl
+has three data types: scalars, arrays of scalars, and
+associative arrays of scalars.
+Normal arrays are indexed by number, and associative arrays by string.
+.PP
+The interpretation of operations and values in perl sometimes
+depends on the requirements
+of the context around the operation or value.
+There are three major contexts: string, numeric and array.
+Certain operations return array values
+in contexts wanting an array, and scalar values otherwise.
+(If this is true of an operation it will be mentioned in the documentation
+for that operation.)
+Operations which return scalars don't care whether the context is looking
+for a string or a number, but
+scalar variables and values are interpreted as strings or numbers
+as appropriate to the context.
+A scalar is interpreted as TRUE in the boolean sense if it is not the null
+string or 0.
+Booleans returned by operators are 1 for true and 0 or \'\' (the null
+string) for false.
+.PP
+There are actually two varieties of null string: defined and undefined.
+Undefined null strings are returned when there is no real value for something,
+such as when there was an error, or at end of file, or when you refer
+to an uninitialized variable or element of an array.
+An undefined null string may become defined the first time you access it, but
+prior to that you can use the defined() operator to determine whether the
+value is defined or not.
+.PP
+References to scalar variables always begin with \*(L'$\*(R', even when referring
+to a scalar that is part of an array.
+Thus:
+.nf
+
+.ne 3
+ $days \h'|2i'# a simple scalar variable
+ $days[28] \h'|2i'# 29th element of array @days
+ $days{\'Feb\'}\h'|2i'# one value from an associative array
+ $#days \h'|2i'# last index of array @days
+
+but entire arrays or array slices are denoted by \*(L'@\*(R':
+
+ @days \h'|2i'# ($days[0], $days[1],\|.\|.\|. $days[n])
+ @days[3,4,5]\h'|2i'# same as @days[3.\|.5]
+ @days{'a','c'}\h'|2i'# same as ($days{'a'},$days{'c'})
+
+and entire associative arrays are denoted by \*(L'%\*(R':
+
+ %days \h'|2i'# (key1, val1, key2, val2 .\|.\|.)
+.fi
+.PP
+Any of these eight constructs may serve as an lvalue,
+that is, may be assigned to.
+(It also turns out that an assignment is itself an lvalue in
+certain contexts\*(--see examples under s, tr and chop.)
+Assignment to a scalar evaluates the righthand side in a scalar context,
+while assignment to an array or array slice evaluates the righthand side
+in an array context.
+.PP
+You may find the length of array @days by evaluating
+\*(L"$#days\*(R", as in
+.IR csh .
+(Actually, it's not the length of the array, it's the subscript of the last element, since there is (ordinarily) a 0th element.)
+Assigning to $#days changes the length of the array.
+Shortening an array by this method does not actually destroy any values.
+Lengthening an array that was previously shortened recovers the values that
+were in those elements.
+You can also gain some measure of efficiency by preextending an array that
+is going to get big.
+(You can also extend an array by assigning to an element that is off the
+end of the array.
+This differs from assigning to $#whatever in that intervening values
+are set to null rather than recovered.)
+You can truncate an array down to nothing by assigning the null list () to
+it.
+The following are exactly equivalent
+.nf
+
+ @whatever = ();
+ $#whatever = $[ \- 1;
+
+.fi
+.PP
+If you evaluate an array in a scalar context, it returns the length of
+the array.
+The following is always true:
+.nf
+
+ scalar(@whatever) == $#whatever \- $[ + 1;
+
+.fi
+If you evaluate an associative array in a scalar context, it returns
+a value which is true if and only if the array contains any elements.
+(If there are any elements, the value returned is a string consisting
+of the number of used buckets and the number of allocated buckets, separated
+by a slash.)
+.PP
+Multi-dimensional arrays are not directly supported, but see the discussion
+of the $; variable later for a means of emulating multiple subscripts with
+an associative array.
+You could also write a subroutine to turn multiple subscripts into a single
+subscript.
+.PP
+Every data type has its own namespace.
+You can, without fear of conflict, use the same name for a scalar variable,
+an array, an associative array, a filehandle, a subroutine name, and/or
+a label.
+Since variable and array references always start with \*(L'$\*(R', \*(L'@\*(R',
+or \*(L'%\*(R', the \*(L"reserved\*(R" words aren't in fact reserved
+with respect to variable names.
+(They ARE reserved with respect to labels and filehandles, however, which
+don't have an initial special character.
+Hint: you could say open(LOG,\'logfile\') rather than open(log,\'logfile\').
+Using uppercase filehandles also improves readability and protects you
+from conflict with future reserved words.)
+Case IS significant\*(--\*(L"FOO\*(R", \*(L"Foo\*(R" and \*(L"foo\*(R" are all
+different names.
+Names which start with a letter may also contain digits and underscores.
+Names which do not start with a letter are limited to one character,
+e.g. \*(L"$%\*(R" or \*(L"$$\*(R".
+(Most of the one character names have a predefined significance to
+.IR perl .
+More later.)
+.PP
+Numeric literals are specified in any of the usual floating point or
+integer formats:
+.nf
+
+.ne 6
+ 12345
+ 12345.67
+ .23E-10
+ 0xffff # hex
+ 0377 # octal
+ 4_294_967_296
+
+.fi
+String literals are delimited by either single or double quotes.
+They work much like shell quotes:
+double-quoted string literals are subject to backslash and variable
+substitution; single-quoted strings are not (except for \e\' and \e\e).
+The usual backslash rules apply for making characters such as newline, tab,
+etc., as well as some more exotic forms:
+.nf
+
+ \et tab
+ \en newline
+ \er return
+ \ef form feed
+ \eb backspace
+ \ea alarm (bell)
+ \ee escape
+ \e033 octal char
+ \ex1b hex char
+ \ec[ control char
+ \el lowercase next char
+ \eu uppercase next char
+ \eL lowercase till \eE
+ \eU uppercase till \eE
+ \eE end case modification
+
+.fi
+You can also embed newlines directly in your strings, i.e. they can end on
+a different line than they begin.
+This is nice, but if you forget your trailing quote, the error will not be
+reported until
+.I perl
+finds another line containing the quote character, which
+may be much further on in the script.
+Variable substitution inside strings is limited to scalar variables, normal
+array values, and array slices.
+(In other words, identifiers beginning with $ or @, followed by an optional
+bracketed expression as a subscript.)
+The following code segment prints out \*(L"The price is $100.\*(R"
+.nf
+
+.ne 2
+ $Price = \'$100\';\h'|3.5i'# not interpreted
+ print "The price is $Price.\e\|n";\h'|3.5i'# interpreted
+
+.fi
+Note that you can put curly brackets around the identifier to delimit it
+from following alphanumerics.
+Also note that a single quoted string must be separated from a preceding
+word by a space, since single quote is a valid character in an identifier
+(see Packages).
+.PP
+Two special literals are _\|_LINE_\|_ and _\|_FILE_\|_, which represent the current
+line number and filename at that point in your program.
+They may only be used as separate tokens; they will not be interpolated
+into strings.
+In addition, the token _\|_END_\|_ may be used to indicate the logical end of the
+script before the actual end of file.
+Any following text is ignored, but may be read via the DATA filehandle.
+(The DATA filehandle may read data only from the main script, but not from
+any required file or evaluated string.)
+The two control characters ^D and ^Z are synonyms for _\|_END_\|_.
+.PP
+A word that doesn't have any other interpretation in the grammar will be
+treated as if it had single quotes around it.
+For this purpose, a word consists only of alphanumeric characters and underline,
+and must start with an alphabetic character.
+As with filehandles and labels, a bare word that consists entirely of
+lowercase letters risks conflict with future reserved words, and if you
+use the
+.B \-w
+switch, Perl will warn you about any such words.
+.PP
+Array values are interpolated into double-quoted strings by joining all the
+elements of the array with the delimiter specified in the $" variable,
+space by default.
+(Since in versions of perl prior to 3.0 the @ character was not a metacharacter
+in double-quoted strings, the interpolation of @array, $array[EXPR],
+@array[LIST], $array{EXPR}, or @array{LIST} only happens if array is
+referenced elsewhere in the program or is predefined.)
+The following are equivalent:
+.nf
+
+.ne 4
+ $temp = join($",@ARGV);
+ system "echo $temp";
+
+ system "echo @ARGV";
+
+.fi
+Within search patterns (which also undergo double-quotish substitution)
+there is a bad ambiguity: Is /$foo[bar]/ to be
+interpreted as /${foo}[bar]/ (where [bar] is a character class for the
+regular expression) or as /${foo[bar]}/ (where [bar] is the subscript to
+array @foo)?
+If @foo doesn't otherwise exist, then it's obviously a character class.
+If @foo exists, perl takes a good guess about [bar], and is almost always right.
+If it does guess wrong, or if you're just plain paranoid,
+you can force the correct interpretation with curly brackets as above.
+.PP
+A line-oriented form of quoting is based on the shell here-is syntax.
+Following a << you specify a string to terminate the quoted material, and all lines
+following the current line down to the terminating string are the value
+of the item.
+The terminating string may be either an identifier (a word), or some
+quoted text.
+If quoted, the type of quotes you use determines the treatment of the text,
+just as in regular quoting.
+An unquoted identifier works like double quotes.
+There must be no space between the << and the identifier.
+(If you put a space it will be treated as a null identifier, which is
+valid, and matches the first blank line\*(--see Merry Christmas example below.)
+The terminating string must appear by itself (unquoted and with no surrounding
+whitespace) on the terminating line.
+.nf
+
+ print <<EOF; # same as above
+The price is $Price.
+EOF
+
+ print <<"EOF"; # same as above
+The price is $Price.
+EOF
+
+ print << x 10; # null identifier is delimiter
+Merry Christmas!
+
+ print <<`EOC`; # execute commands
+echo hi there
+echo lo there
+EOC
+
+ print <<foo, <<bar; # you can stack them
+I said foo.
+foo
+I said bar.
+bar
+
+.fi
+Array literals are denoted by separating individual values by commas, and
+enclosing the list in parentheses:
+.nf
+
+ (LIST)
+
+.fi
+In a context not requiring an array value, the value of the array literal
+is the value of the final element, as in the C comma operator.
+For example,
+.nf
+
+.ne 4
+ @foo = (\'cc\', \'\-E\', $bar);
+
+assigns the entire array value to array foo, but
+
+ $foo = (\'cc\', \'\-E\', $bar);
+
+.fi
+assigns the value of variable bar to variable foo.
+Note that the value of an actual array in a scalar context is the length
+of the array; the following assigns to $foo the value 3:
+.nf
+
+.ne 2
+ @foo = (\'cc\', \'\-E\', $bar);
+ $foo = @foo; # $foo gets 3
+
+.fi
+You may have an optional comma before the closing parenthesis of an
+array literal, so that you can say:
+.nf
+
+ @foo = (
+ 1,
+ 2,
+ 3,
+ );
+
+.fi
+When a LIST is evaluated, each element of the list is evaluated in
+an array context, and the resulting array value is interpolated into LIST
+just as if each individual element were a member of LIST. Thus arrays
+lose their identity in a LIST\*(--the list
+
+ (@foo,@bar,&SomeSub)
+
+contains all the elements of @foo followed by all the elements of @bar,
+followed by all the elements returned by the subroutine named SomeSub.
+.PP
+A list value may also be subscripted like a normal array.
+Examples:
+.nf
+
+ $time = (stat($file))[8]; # stat returns array value
+ $digit = ('a','b','c','d','e','f')[$digit-10];
+ return (pop(@foo),pop(@foo))[0];
+
+.fi
+.PP
+Array lists may be assigned to if and only if each element of the list
+is an lvalue:
+.nf
+
+ ($a, $b, $c) = (1, 2, 3);
+
+ ($map{\'red\'}, $map{\'blue\'}, $map{\'green\'}) = (0x00f, 0x0f0, 0xf00);
+
+The final element may be an array or an associative array:
+
+ ($a, $b, @rest) = split;
+ local($a, $b, %rest) = @_;
+
+.fi
+You can actually put an array anywhere in the list, but the first array
+in the list will soak up all the values, and anything after it will get
+a null value.
+This may be useful in a local().
+.PP
+An associative array literal contains pairs of values to be interpreted
+as a key and a value:
+.nf
+
+.ne 2
+ # same as map assignment above
+ %map = ('red',0x00f,'blue',0x0f0,'green',0xf00);
+
+.fi
+Array assignment in a scalar context returns the number of elements
+produced by the expression on the right side of the assignment:
+.nf
+
+ $x = (($foo,$bar) = (3,2,1)); # set $x to 3, not 2
+
+.fi
+.PP
+There are several other pseudo-literals that you should know about.
+If a string is enclosed by backticks (grave accents), it first undergoes
+variable substitution just like a double quoted string.
+It is then interpreted as a command, and the output of that command
+is the value of the pseudo-literal, like in a shell.
+In a scalar context, a single string consisting of all the output is
+returned.
+In an array context, an array of values is returned, one for each line
+of output.
+(You can set $/ to use a different line terminator.)
+The command is executed each time the pseudo-literal is evaluated.
+The status value of the command is returned in $? (see Predefined Names
+for the interpretation of $?).
+Unlike in \f2csh\f1, no translation is done on the return
+data\*(--newlines remain newlines.
+Unlike in any of the shells, single quotes do not hide variable names
+in the command from interpretation.
+To pass a $ through to the shell you need to hide it with a backslash.
+.PP
+Evaluating a filehandle in angle brackets yields the next line
+from that file (newline included, so it's never false until EOF, at
+which time an undefined value is returned).
+Ordinarily you must assign that value to a variable,
+but there is one situation where an automatic assignment happens.
+If (and only if) the input symbol is the only thing inside the conditional of a
+.I while
+loop, the value is
+automatically assigned to the variable \*(L"$_\*(R".
+(This may seem like an odd thing to you, but you'll use the construct
+in almost every
+.I perl
+script you write.)
+Anyway, the following lines are equivalent to each other:
+.nf
+
+.ne 5
+ while ($_ = <STDIN>) { print; }
+ while (<STDIN>) { print; }
+ for (\|;\|<STDIN>;\|) { print; }
+ print while $_ = <STDIN>;
+ print while <STDIN>;
+
+.fi
+The filehandles
+.IR STDIN ,
+.I STDOUT
+and
+.I STDERR
+are predefined.
+(The filehandles
+.IR stdin ,
+.I stdout
+and
+.I stderr
+will also work except in packages, where they would be interpreted as
+local identifiers rather than global.)
+Additional filehandles may be created with the
+.I open
+function.
+.PP
+If a <FILEHANDLE> is used in a context that is looking for an array, an array
+consisting of all the input lines is returned, one line per array element.
+It's easy to make a LARGE data space this way, so use with care.
+.PP
+The null filehandle <> is special and can be used to emulate the behavior of
+\fIsed\fR and \fIawk\fR.
+Input from <> comes either from standard input, or from each file listed on
+the command line.
+Here's how it works: the first time <> is evaluated, the ARGV array is checked,
+and if it is null, $ARGV[0] is set to \'-\', which when opened gives you standard
+input.
+The ARGV array is then processed as a list of filenames.
+The loop
+.nf
+
+.ne 3
+ while (<>) {
+ .\|.\|. # code for each line
+ }
+
+.ne 10
+is equivalent to the following Perl-like pseudo code:
+
+ unshift(@ARGV, \'\-\') \|if \|$#ARGV < $[;
+ while ($ARGV = shift) {
+ open(ARGV, $ARGV);
+ while (<ARGV>) {
+ .\|.\|. # code for each line
+ }
+ }
+
+.fi
+except that it isn't as cumbersome to say, and will actually work.
+It really does shift array ARGV and put the current filename into
+variable ARGV.
+It also uses filehandle ARGV internally\*(--<> is just a synonym for
+<ARGV>, which is magical.
+(The pseudo code above doesn't work because it treats <ARGV> as non-magical.)
+.PP
+You can modify @ARGV before the first <> as long as the array ends up
+containing the list of filenames you really want.
+Line numbers ($.) continue as if the input was one big happy file.
+(But see example under eof for how to reset line numbers on each file.)
+.PP
+.ne 5
+If you want to set @ARGV to your own list of files, go right ahead.
+If you want to pass switches into your script, you can
+put a loop on the front like this:
+.nf
+
+.ne 10
+ while ($_ = $ARGV[0], /\|^\-/\|) {
+ shift;
+ last if /\|^\-\|\-$\|/\|;
+ /\|^\-D\|(.*\|)/ \|&& \|($debug = $1);
+ /\|^\-v\|/ \|&& \|$verbose++;
+ .\|.\|. # other switches
+ }
+ while (<>) {
+ .\|.\|. # code for each line
+ }
+
+.fi
+The <> symbol will return FALSE only once.
+If you call it again after this it will assume you are processing another
+@ARGV list, and if you haven't set @ARGV, will input from
+.IR STDIN .
+.PP
+If the string inside the angle brackets is a reference to a scalar variable
+(e.g. <$foo>),
+then that variable contains the name of the filehandle to input from.
+.PP
+If the string inside angle brackets is not a filehandle, it is interpreted
+as a filename pattern to be globbed, and either an array of filenames or the
+next filename in the list is returned, depending on context.
+One level of $ interpretation is done first, but you can't say <$foo>
+because that's an indirect filehandle as explained in the previous
+paragraph.
+You could insert curly brackets to force interpretation as a
+filename glob: <${foo}>.
+Example:
+.nf
+
+.ne 3
+ while (<*.c>) {
+ chmod 0644, $_;
+ }
+
+is equivalent to
+
+.ne 5
+ open(foo, "echo *.c | tr \-s \' \et\er\ef\' \'\e\e012\e\e012\e\e012\e\e012\'|");
+ while (<foo>) {
+ chop;
+ chmod 0644, $_;
+ }
+
+.fi
+In fact, it's currently implemented that way.
+(Which means it will not work on filenames with spaces in them unless
+you have /bin/csh on your machine.)
+Of course, the shortest way to do the above is:
+.nf
+
+ chmod 0644, <*.c>;
+
+.fi
+.Sh "Syntax"
+.PP
+A
+.I perl
+script consists of a sequence of declarations and commands.
+The only things that need to be declared in
+.I perl
+are report formats and subroutines.
+See the sections below for more information on those declarations.
+All uninitialized user-created objects are assumed to
+start with a null or 0 value until they
+are defined by some explicit operation such as assignment.
+The sequence of commands is executed just once, unlike in
+.I sed
+and
+.I awk
+scripts, where the sequence of commands is executed for each input line.
+While this means that you must explicitly loop over the lines of your input file
+(or files), it also means you have much more control over which files and which
+lines you look at.
+(Actually, I'm lying\*(--it is possible to do an implicit loop with either the
+.B \-n
+or
+.B \-p
+switch.)
+.PP
+A declaration can be put anywhere a command can, but has no effect on the
+execution of the primary sequence of commands\*(--declarations all take effect
+at compile time.
+Typically all the declarations are put at the beginning or the end of the script.
+.PP
+.I Perl
+is, for the most part, a free-form language.
+(The only exception to this is format declarations, for fairly obvious reasons.)
+Comments are indicated by the # character, and extend to the end of the line.
+If you attempt to use /* */ C comments, it will be interpreted either as
+division or pattern matching, depending on the context.
+So don't do that.
+.Sh "Compound statements"
+In
+.IR perl ,
+a sequence of commands may be treated as one command by enclosing it
+in curly brackets.
+We will call this a BLOCK.
+.PP
+The following compound commands may be used to control flow:
+.nf
+
+.ne 4
+ if (EXPR) BLOCK
+ if (EXPR) BLOCK else BLOCK
+ if (EXPR) BLOCK elsif (EXPR) BLOCK .\|.\|. else BLOCK
+ LABEL while (EXPR) BLOCK
+ LABEL while (EXPR) BLOCK continue BLOCK
+ LABEL for (EXPR; EXPR; EXPR) BLOCK
+ LABEL foreach VAR (ARRAY) BLOCK
+ LABEL BLOCK continue BLOCK
+
+.fi
+Note that, unlike C and Pascal, these are defined in terms of BLOCKs, not
+statements.
+This means that the curly brackets are \fIrequired\fR\*(--no dangling statements allowed.
+If you want to write conditionals without curly brackets there are several
+other ways to do it.
+The following all do the same thing:
+.nf
+
+.ne 5
+ if (!open(foo)) { die "Can't open $foo: $!"; }
+ die "Can't open $foo: $!" unless open(foo);
+ open(foo) || die "Can't open $foo: $!"; # foo or bust!
+ open(foo) ? \'hi mom\' : die "Can't open $foo: $!";
+ # a bit exotic, that last one
+
+.fi
+.PP
+The
+.I if
+statement is straightforward.
+Since BLOCKs are always bounded by curly brackets, there is never any
+ambiguity about which
+.I if
+an
+.I else
+goes with.
+If you use
+.I unless
+in place of
+.IR if ,
+the sense of the test is reversed.
+.PP
+The
+.I while
+statement executes the block as long as the expression is true
+(does not evaluate to the null string or 0).
+The LABEL is optional, and if present, consists of an identifier followed by
+a colon.
+The LABEL identifies the loop for the loop control statements
+.IR next ,
+.IR last ,
+and
+.I redo
+(see below).
+If there is a
+.I continue
+BLOCK, it is always executed just before
+the conditional is about to be evaluated again, similarly to the third part
+of a
+.I for
+loop in C.
+Thus it can be used to increment a loop variable, even when the loop has
+been continued via the
+.I next
+statement (similar to the C \*(L"continue\*(R" statement).
+.PP
+If the word
+.I while
+is replaced by the word
+.IR until ,
+the sense of the test is reversed, but the conditional is still tested before
+the first iteration.
+.PP
+In either the
+.I if
+or the
+.I while
+statement, you may replace \*(L"(EXPR)\*(R" with a BLOCK, and the conditional
+is true if the value of the last command in that block is true.
+.PP
+The
+.I for
+loop works exactly like the corresponding
+.I while
+loop:
+.nf
+
+.ne 12
+ for ($i = 1; $i < 10; $i++) {
+ .\|.\|.
+ }
+
+is the same as
+
+ $i = 1;
+ while ($i < 10) {
+ .\|.\|.
+ } continue {
+ $i++;
+ }
+.fi
+.PP
+The foreach loop iterates over a normal array value and sets the variable
+VAR to be each element of the array in turn.
+The variable is implicitly local to the loop, and regains its former value
+upon exiting the loop.
+The \*(L"foreach\*(R" keyword is actually identical to the \*(L"for\*(R" keyword,
+so you can use \*(L"foreach\*(R" for readability or \*(L"for\*(R" for brevity.
+If VAR is omitted, $_ is set to each value.
+If ARRAY is an actual array (as opposed to an expression returning an array
+value), you can modify each element of the array
+by modifying VAR inside the loop.
+Examples:
+.nf
+
+.ne 5
+ for (@ary) { s/foo/bar/; }
+
+ foreach $elem (@elements) {
+ $elem *= 2;
+ }
+
+.ne 3
+ for ((10,9,8,7,6,5,4,3,2,1,\'BOOM\')) {
+ print $_, "\en"; sleep(1);
+ }
+
+ for (1..15) { print "Merry Christmas\en"; }
+
+.ne 3
+ foreach $item (split(/:[\e\e\en:]*/, $ENV{\'TERMCAP\'})) {
+ print "Item: $item\en";
+ }
+
+.fi
+.PP
+The BLOCK by itself (labeled or not) is equivalent to a loop that executes
+once.
+Thus you can use any of the loop control statements in it to leave or
+restart the block.
+The
+.I continue
+block is optional.
+This construct is particularly nice for doing case structures.
+.nf
+
+.ne 6
+ foo: {
+ if (/^abc/) { $abc = 1; last foo; }
+ if (/^def/) { $def = 1; last foo; }
+ if (/^xyz/) { $xyz = 1; last foo; }
+ $nothing = 1;
+ }
+
+.fi
+There is no official switch statement in perl, because there
+are already several ways to write the equivalent.
+In addition to the above, you could write
+.nf
+
+.ne 6
+ foo: {
+ $abc = 1, last foo if /^abc/;
+ $def = 1, last foo if /^def/;
+ $xyz = 1, last foo if /^xyz/;
+ $nothing = 1;
+ }
+
+or
+
+.ne 6
+ foo: {
+ /^abc/ && do { $abc = 1; last foo; };
+ /^def/ && do { $def = 1; last foo; };
+ /^xyz/ && do { $xyz = 1; last foo; };
+ $nothing = 1;
+ }
+
+or
+
+.ne 6
+ foo: {
+ /^abc/ && ($abc = 1, last foo);
+ /^def/ && ($def = 1, last foo);
+ /^xyz/ && ($xyz = 1, last foo);
+ $nothing = 1;
+ }
+
+or even
+
+.ne 8
+ if (/^abc/)
+ { $abc = 1; }
+ elsif (/^def/)
+ { $def = 1; }
+ elsif (/^xyz/)
+ { $xyz = 1; }
+ else
+ {$nothing = 1;}
+
+.fi
+As it happens, these are all optimized internally to a switch structure,
+so perl jumps directly to the desired statement, and you needn't worry
+about perl executing a lot of unnecessary statements when you have a string
+of 50 elsifs, as long as you are testing the same simple scalar variable
+using ==, eq, or pattern matching as above.
+(If you're curious as to whether the optimizer has done this for a particular
+case statement, you can use the \-D1024 switch to list the syntax tree
+before execution.)
+.Sh "Simple statements"
+The only kind of simple statement is an expression evaluated for its side
+effects.
+Every simple statement must be terminated with a semicolon, unless it is the
+final statement in a block, in which case the semicolon is optional.
+(Semicolon is still encouraged there if the block takes up more than one line).
+.PP
+Any simple statement may optionally be followed by a
+single modifier, just before the terminating semicolon.
+The possible modifiers are:
+.nf
+
+.ne 4
+ if EXPR
+ unless EXPR
+ while EXPR
+ until EXPR
+
+.fi
+The
+.I if
+and
+.I unless
+modifiers have the expected semantics.
+The
+.I while
+and
+.I until
+modifiers also have the expected semantics (conditional evaluated first),
+except when applied to a do-BLOCK or a do-SUBROUTINE command,
+in which case the block executes once before the conditional is evaluated.
+This is so that you can write loops like:
+.nf
+
+.ne 4
+ do {
+ $_ = <STDIN>;
+ .\|.\|.
+ } until $_ \|eq \|".\|\e\|n";
+
+.fi
+(See the
+.I do
+operator below. Note also that the loop control commands described later will
+NOT work in this construct, since modifiers don't take loop labels.
+Sorry.)
+.Sh "Expressions"
+Since
+.I perl
+expressions work almost exactly like C expressions, only the differences
+will be mentioned here.
+.PP
+Here's what
+.I perl
+has that C doesn't:
+.Ip ** 8 2
+The exponentiation operator.
+.Ip **= 8
+The exponentiation assignment operator.
+.Ip (\|) 8 3
+The null list, used to initialize an array to null.
+.Ip . 8
+Concatenation of two strings.
+.Ip .= 8
+The concatenation assignment operator.
+.Ip eq 8
+String equality (== is numeric equality).
+For a mnemonic just think of \*(L"eq\*(R" as a string.
+(If you are used to the
+.I awk
+behavior of using == for either string or numeric equality
+based on the current form of the comparands, beware!
+You must be explicit here.)
+.Ip ne 8
+String inequality (!= is numeric inequality).
+.Ip lt 8
+String less than.
+.Ip gt 8
+String greater than.
+.Ip le 8
+String less than or equal.
+.Ip ge 8
+String greater than or equal.
+.Ip cmp 8
+String comparison, returning -1, 0, or 1.
+.Ip <=> 8
+Numeric comparison, returning -1, 0, or 1.
+.Ip =~ 8 2
+Certain operations search or modify the string \*(L"$_\*(R" by default.
+This operator makes that kind of operation work on some other string.
+The right argument is a search pattern, substitution, or translation.
+The left argument is what is supposed to be searched, substituted, or
+translated instead of the default \*(L"$_\*(R".
+The return value indicates the success of the operation.
+(If the right argument is an expression other than a search pattern,
+substitution, or translation, it is interpreted as a search pattern
+at run time.
+This is less efficient than an explicit search, since the pattern must
+be compiled every time the expression is evaluated.)
+The precedence of this operator is lower than unary minus and autoincrement/decrement, but higher than everything else.
+.Ip !~ 8
+Just like =~ except the return value is negated.
+.Ip x 8
+The repetition operator.
+Returns a string consisting of the left operand repeated the
+number of times specified by the right operand.
+In an array context, if the left operand is a list in parens, it repeats
+the list.
+.nf
+
+ print \'\-\' x 80; # print row of dashes
+ print \'\-\' x80; # illegal, x80 is identifier
+
+ print "\et" x ($tab/8), \' \' x ($tab%8); # tab over
+
+ @ones = (1) x 80; # an array of 80 1's
+ @ones = (5) x @ones; # set all elements to 5
+
+.fi
+.Ip x= 8
+The repetition assignment operator.
+Only works on scalars.
+.Ip .\|. 8
+The range operator, which is really two different operators depending
+on the context.
+In an array context, returns an array of values counting (by ones)
+from the left value to the right value.
+This is useful for writing \*(L"for (1..10)\*(R" loops and for doing
+slice operations on arrays.
+.Sp
+In a scalar context, .\|. returns a boolean value.
+The operator is bistable, like a flip-flop, and
+emulates the line-range (comma) operator of sed, awk, and various editors.
+Each .\|. operator maintains its own boolean state.
+It is false as long as its left operand is false.
+Once the left operand is true, the range operator stays true
+until the right operand is true,
+AFTER which the range operator becomes false again.
+(It doesn't become false till the next time the range operator is evaluated.
+It can test the right operand and become false on the
+same evaluation it became true (as in awk), but it still returns true once.
+If you don't want it to test the right operand till the next
+evaluation (as in sed), use three dots (.\|.\|.) instead of two.)
+The right operand is not evaluated while the operator is in the \*(L"false\*(R" state,
+and the left operand is not evaluated while the operator is in the \*(L"true\*(R" state.
+The precedence is a little lower than || and &&.
+The value returned is either the null string for false, or a sequence number
+(beginning with 1) for true.
+The sequence number is reset for each range encountered.
+The final sequence number in a range has the string \'E0\' appended to it, which
+doesn't affect its numeric value, but gives you something to search for if you
+want to exclude the endpoint.
+You can exclude the beginning point by waiting for the sequence number to be
+greater than 1.
+If either operand of scalar .\|. is static, that operand is implicitly compared
+to the $. variable, the current line number.
+Examples:
+.nf
+
+.ne 6
+As a scalar operator:
+ if (101 .\|. 200) { print; } # print 2nd hundred lines
+
+ next line if (1 .\|. /^$/); # skip header lines
+
+ s/^/> / if (/^$/ .\|. eof()); # quote body
+
+.ne 4
+As an array operator:
+ for (101 .\|. 200) { print; } # print $_ 100 times
+
+ @foo = @foo[$[ .\|. $#foo]; # an expensive no-op
+ @foo = @foo[$#foo-4 .\|. $#foo]; # slice last 5 items
+
+.fi
+.Ip \-x 8
+A file test.
+This unary operator takes one argument, either a filename or a filehandle,
+and tests the associated file to see if something is true about it.
+If the argument is omitted, tests $_, except for \-t, which tests
+.IR STDIN .
+It returns 1 for true and \'\' for false, or the undefined value if the
+file doesn't exist.
+Precedence is higher than logical and relational operators, but lower than
+arithmetic operators.
+The operator may be any of:
+.nf
+ \-r File is readable by effective uid/gid.
+ \-w File is writable by effective uid/gid.
+ \-x File is executable by effective uid/gid.
+ \-o File is owned by effective uid.
+ \-R File is readable by real uid/gid.
+ \-W File is writable by real uid/gid.
+ \-X File is executable by real uid/gid.
+ \-O File is owned by real uid.
+ \-e File exists.
+ \-z File has zero size.
+ \-s File has non-zero size (returns size).
+ \-f File is a plain file.
+ \-d File is a directory.
+ \-l File is a symbolic link.
+ \-p File is a named pipe (FIFO).
+ \-S File is a socket.
+ \-b File is a block special file.
+ \-c File is a character special file.
+ \-u File has setuid bit set.
+ \-g File has setgid bit set.
+ \-k File has sticky bit set.
+ \-t Filehandle is opened to a tty.
+ \-T File is a text file.
+ \-B File is a binary file (opposite of \-T).
+ \-M Age of file in days when script started.
+ \-A Same for access time.
+ \-C Same for inode change time.
+
+.fi
+The interpretation of the file permission operators \-r, \-R, \-w, \-W, \-x and \-X
+is based solely on the mode of the file and the uids and gids of the user.
+There may be other reasons you can't actually read, write or execute the file.
+Also note that, for the superuser, \-r, \-R, \-w and \-W always return 1, and
+\-x and \-X return 1 if any execute bit is set in the mode.
+Scripts run by the superuser may thus need to do a stat() in order to determine
+the actual mode of the file, or temporarily set the uid to something else.
+.Sp
+Example:
+.nf
+.ne 7
+
+ while (<>) {
+ chop;
+ next unless \-f $_; # ignore specials
+ .\|.\|.
+ }
+
+.fi
+Note that \-s/a/b/ does not do a negated substitution.
+Saying \-exp($foo) still works as expected, however\*(--only single letters
+following a minus are interpreted as file tests.
+.Sp
+The \-T and \-B switches work as follows.
+The first block or so of the file is examined for odd characters such as
+strange control codes or metacharacters.
+If too many odd characters (>10%) are found, it's a \-B file, otherwise it's a \-T file.
+Also, any file containing null in the first block is considered a binary file.
+If \-T or \-B is used on a filehandle, the current stdio buffer is examined
+rather than the first block.
+Both \-T and \-B return TRUE on a null file, or a file at EOF when testing
+a filehandle.
+.PP
+If any of the file tests (or either stat operator) are given the special
+filehandle consisting of a solitary underline, then the stat structure
+of the previous file test (or stat operator) is used, saving a system
+call.
+(This doesn't work with \-t, and you need to remember that lstat and -l
+will leave values in the stat structure for the symbolic link, not the
+real file.)
+Example:
+.nf
+
+ print "Can do.\en" if -r $a || -w _ || -x _;
+
+.ne 9
+ stat($filename);
+ print "Readable\en" if -r _;
+ print "Writable\en" if -w _;
+ print "Executable\en" if -x _;
+ print "Setuid\en" if -u _;
+ print "Setgid\en" if -g _;
+ print "Sticky\en" if -k _;
+ print "Text\en" if -T _;
+ print "Binary\en" if -B _;
+
+.fi
+.PP
+Here is what C has that
+.I perl
+doesn't:
+.Ip "unary &" 12
+Address-of operator.
+.Ip "unary *" 12
+Dereference-address operator.
+.Ip "(TYPE)" 12
+Type casting operator.
+.PP
+Like C,
+.I perl
+does a certain amount of expression evaluation at compile time, whenever
+it determines that all of the arguments to an operator are static and have
+no side effects.
+In particular, string concatenation happens at compile time between literals that don't do variable substitution.
+Backslash interpretation also happens at compile time.
+You can say
+.nf
+
+.ne 2
+ \'Now is the time for all\' . "\|\e\|n" .
+ \'good men to come to.\'
+
+.fi
+and this all reduces to one string internally.
+.PP
+The autoincrement operator has a little extra built-in magic to it.
+If you increment a variable that is numeric, or that has ever been used in
+a numeric context, you get a normal increment.
+If, however, the variable has only been used in string contexts since it
+was set, and has a value that is not null and matches the
+pattern /^[a\-zA\-Z]*[0\-9]*$/, the increment is done
+as a string, preserving each character within its range, with carry:
+.nf
+
+ print ++($foo = \'99\'); # prints \*(L'100\*(R'
+ print ++($foo = \'a0\'); # prints \*(L'a1\*(R'
+ print ++($foo = \'Az\'); # prints \*(L'Ba\*(R'
+ print ++($foo = \'zz\'); # prints \*(L'aaa\*(R'
+
+.fi
+The autodecrement is not magical.
+.PP
+The range operator (in an array context) makes use of the magical
+autoincrement algorithm if the minimum and maximum are strings.
+You can say
+
+ @alphabet = (\'A\' .. \'Z\');
+
+to get all the letters of the alphabet, or
+
+ $hexdigit = (0 .. 9, \'a\' .. \'f\')[$num & 15];
+
+to get a hexadecimal digit, or
+
+ @z2 = (\'01\' .. \'31\'); print @z2[$mday];
+
+to get dates with leading zeros.
+(If the final value specified is not in the sequence that the magical increment
+would produce, the sequence goes until the next value would be longer than
+the final value specified.)
+.PP
+The || and && operators differ from C's in that, rather than returning 0 or 1,
+they return the last value evaluated.
+Thus, a portable way to find out the home directory might be:
+.nf
+
+ $home = $ENV{'HOME'} || $ENV{'LOGDIR'} ||
+ (getpwuid($<))[7] || die "You're homeless!\en";
+
+.fi
+.PP
+Along with the literals and variables mentioned earlier,
+the operations in the following section can serve as terms in an expression.
+Some of these operations take a LIST as an argument.
+Such a list can consist of any combination of scalar arguments or array values;
+the array values will be included in the list as if each individual element were
+interpolated at that point in the list, forming a longer single-dimensional
+array value.
+Elements of the LIST should be separated by commas.
+If an operation is listed both with and without parentheses around its
+arguments, it means you can either use it as a unary operator or
+as a function call.
+To use it as a function call, the next token on the same line must
+be a left parenthesis.
+(There may be intervening white space.)
+Such a function then has highest precedence, as you would expect from
+a function.
+If any token other than a left parenthesis follows, then it is a
+unary operator, with a precedence depending only on whether it is a LIST
+operator or not.
+LIST operators have lowest precedence.
+All other unary operators have a precedence greater than relational operators
+but less than arithmetic operators.
+See the section on Precedence.
+.PP
+For operators that can be used in either a scalar or array context,
+failure is generally indicated in a scalar context by returning
+the undefined value, and in an array context by returning the null list.
+Remember though that
+THERE IS NO GENERAL RULE FOR CONVERTING A LIST INTO A SCALAR.
+Each operator decides which sort of scalar it would be most
+appropriate to return.
+Some operators return the length of the list
+that would have been returned in an array context.
+Some operators return the first value in the list.
+Some operators return the last value in the list.
+Some operators return a count of successful operations.
+In general, they do what you want, unless you want consistency.
+.Ip "/PATTERN/" 8 4
+See m/PATTERN/.
+.Ip "?PATTERN?" 8 4
+This is just like the /pattern/ search, except that it matches only once between
+calls to the
+.I reset
+operator.
+This is a useful optimization when you only want to see the first occurrence of
+something in each file of a set of files, for instance.
+Only ?? patterns local to the current package are reset.
+.Ip "accept(NEWSOCKET,GENERICSOCKET)" 8 2
+Does the same thing that the accept system call does.
+Returns true if it succeeded, false otherwise.
+See example in section on Interprocess Communication.
+.Ip "alarm(SECONDS)" 8 4
+.Ip "alarm SECONDS" 8
+Arranges to have a SIGALRM delivered to this process after the specified number
+of seconds (minus 1, actually) have elapsed. Thus, alarm(15) will cause
+a SIGALRM at some point more than 14 seconds in the future.
+Only one timer may be counting at once. Each call disables the previous
+timer, and an argument of 0 may be supplied to cancel the previous timer
+without starting a new one.
+The returned value is the amount of time remaining on the previous timer.
+.Ip "atan2(Y,X)" 8 2
+Returns the arctangent of Y/X in the range
+.if t \-\(*p to \(*p.
+.if n \-PI to PI.
+.Ip "bind(SOCKET,NAME)" 8 2
+Does the same thing that the bind system call does.
+Returns true if it succeeded, false otherwise.
+NAME should be a packed address of the proper type for the socket.
+See example in section on Interprocess Communication.
+.Ip "binmode(FILEHANDLE)" 8 4
+.Ip "binmode FILEHANDLE" 8 4
+Arranges for the file to be read in \*(L"binary\*(R" mode in operating systems
+that distinguish between binary and text files.
+Files that are not read in binary mode have CR LF sequences translated
+to LF on input and LF translated to CR LF on output.
+Binmode has no effect under Unix.
+If FILEHANDLE is an expression, the value is taken as the name of
+the filehandle.
+.Ip "caller(EXPR)"
+.Ip "caller"
+Returns the context of the current subroutine call:
+.nf
+
+ ($package,$filename,$line) = caller;
+
+.fi
+With EXPR, returns some extra information that the debugger uses to print
+a stack trace. The value of EXPR indicates how many call frames to go
+back before the current one.
+.Ip "chdir(EXPR)" 8 2
+.Ip "chdir EXPR" 8 2
+Changes the working directory to EXPR, if possible.
+If EXPR is omitted, changes to home directory.
+Returns 1 upon success, 0 otherwise.
+See example under
+.IR die .
+.Ip "chmod(LIST)" 8 2
+.Ip "chmod LIST" 8 2
+Changes the permissions of a list of files.
+The first element of the list must be the numerical mode.
+Returns the number of files successfully changed.
+.nf
+
+.ne 2
+ $cnt = chmod 0755, \'foo\', \'bar\';
+ chmod 0755, @executables;
+
+.fi
+.Ip "chop(LIST)" 8 7
+.Ip "chop(VARIABLE)" 8
+.Ip "chop VARIABLE" 8
+.Ip "chop" 8
+Chops off the last character of a string and returns the character chopped.
+It's used primarily to remove the newline from the end of an input record,
+but is much more efficient than s/\en// because it neither scans nor copies
+the string.
+If VARIABLE is omitted, chops $_.
+Example:
+.nf
+
+.ne 5
+ while (<>) {
+ chop; # avoid \en on last field
+ @array = split(/:/);
+ .\|.\|.
+ }
+
+.fi
+You can actually chop anything that's an lvalue, including an assignment:
+.nf
+
+ chop($cwd = \`pwd\`);
+ chop($answer = <STDIN>);
+
+.fi
+If you chop a list, each element is chopped.
+Only the value of the last chop is returned.
+.Ip "chown(LIST)" 8 2
+.Ip "chown LIST" 8 2
+Changes the owner (and group) of a list of files.
+The first two elements of the list must be the NUMERICAL uid and gid,
+in that order.
+Returns the number of files successfully changed.
+.nf
+
+.ne 2
+ $cnt = chown $uid, $gid, \'foo\', \'bar\';
+ chown $uid, $gid, @filenames;
+
+.fi
+.ne 23
+Here's an example that looks up non-numeric uids in the passwd file:
+.nf
+
+ print "User: ";
+ $user = <STDIN>;
+ chop($user);
+ print "Files: "
+ $pattern = <STDIN>;
+ chop($pattern);
+.ie t \{\
+ open(pass, \'/etc/passwd\') || die "Can't open passwd: $!\en";
+'br\}
+.el \{\
+ open(pass, \'/etc/passwd\')
+ || die "Can't open passwd: $!\en";
+'br\}
+ while (<pass>) {
+ ($login,$pass,$uid,$gid) = split(/:/);
+ $uid{$login} = $uid;
+ $gid{$login} = $gid;
+ }
+ @ary = <${pattern}>; # get filenames
+ if ($uid{$user} eq \'\') {
+ die "$user not in passwd file";
+ }
+ else {
+ chown $uid{$user}, $gid{$user}, @ary;
+ }
+
+.fi
+.Ip "chroot(FILENAME)" 8 5
+.Ip "chroot FILENAME" 8
+Does the same as the system call of that name.
+If you don't know what it does, don't worry about it.
+If FILENAME is omitted, does chroot to $_.
+.Ip "close(FILEHANDLE)" 8 5
+.Ip "close FILEHANDLE" 8
+Closes the file or pipe associated with the file handle.
+You don't have to close FILEHANDLE if you are immediately going to
+do another open on it, since open will close it for you.
+(See
+.IR open .)
+However, an explicit close on an input file resets the line counter ($.), while
+the implicit close done by
+.I open
+does not.
+Also, closing a pipe will wait for the process executing on the pipe to complete,
+in case you want to look at the output of the pipe afterwards.
+Closing a pipe explicitly also puts the status value of the command into $?.
+Example:
+.nf
+
+.ne 4
+ open(OUTPUT, \'|sort >foo\'); # pipe to sort
+ .\|.\|. # print stuff to output
+ close OUTPUT; # wait for sort to finish
+ open(INPUT, \'foo\'); # get sort's results
+
+.fi
+FILEHANDLE may be an expression whose value gives the real filehandle name.
+.Ip "closedir(DIRHANDLE)" 8 5
+.Ip "closedir DIRHANDLE" 8
+Closes a directory opened by opendir().
+.Ip "connect(SOCKET,NAME)" 8 2
+Does the same thing that the connect system call does.
+Returns true if it succeeded, false otherwise.
+NAME should be a package address of the proper type for the socket.
+See example in section on Interprocess Communication.
+.Ip "cos(EXPR)" 8 6
+.Ip "cos EXPR" 8 6
+Returns the cosine of EXPR (expressed in radians).
+If EXPR is omitted takes cosine of $_.
+.Ip "crypt(PLAINTEXT,SALT)" 8 6
+Encrypts a string exactly like the crypt() function in the C library.
+Useful for checking the password file for lousy passwords.
+Only the guys wearing white hats should do this.
+.Ip "dbmclose(ASSOC_ARRAY)" 8 6
+.Ip "dbmclose ASSOC_ARRAY" 8
+Breaks the binding between a dbm file and an associative array.
+The values remaining in the associative array are meaningless unless
+you happen to want to know what was in the cache for the dbm file.
+This function is only useful if you have ndbm.
+.Ip "dbmopen(ASSOC,DBNAME,MODE)" 8 6
+This binds a dbm or ndbm file to an associative array.
+ASSOC is the name of the associative array.
+(Unlike normal open, the first argument is NOT a filehandle, even though
+it looks like one).
+DBNAME is the name of the database (without the .dir or .pag extension).
+If the database does not exist, it is created with protection specified
+by MODE (as modified by the umask).
+If your system only supports the older dbm functions, you may perform only one
+dbmopen in your program.
+If your system has neither dbm nor ndbm, calling dbmopen produces a fatal
+error.
+.Sp
+Values assigned to the associative array prior to the dbmopen are lost.
+A certain number of values from the dbm file are cached in memory.
+By default this number is 64, but you can increase it by preallocating
+that number of garbage entries in the associative array before the dbmopen.
+You can flush the cache if necessary with the reset command.
+.Sp
+If you don't have write access to the dbm file, you can only read
+associative array variables, not set them.
+If you want to test whether you can write, either use file tests or
+try setting a dummy array entry inside an eval, which will trap the error.
+.Sp
+Note that functions such as keys() and values() may return huge array values
+when used on large dbm files.
+You may prefer to use the each() function to iterate over large dbm files.
+Example:
+.nf
+
+.ne 6
+ # print out history file offsets
+ dbmopen(HIST,'/usr/lib/news/history',0666);
+ while (($key,$val) = each %HIST) {
+ print $key, ' = ', unpack('L',$val), "\en";
+ }
+ dbmclose(HIST);
+
+.fi
+.Ip "defined(EXPR)" 8 6
+.Ip "defined EXPR" 8
+Returns a boolean value saying whether the lvalue EXPR has a real value
+or not.
+Many operations return the undefined value under exceptional conditions,
+such as end of file, uninitialized variable, system error and such.
+This function allows you to distinguish between an undefined null string
+and a defined null string with operations that might return a real null
+string, in particular referencing elements of an array.
+You may also check to see if arrays or subroutines exist.
+Use on predefined variables is not guaranteed to produce intuitive results.
+Examples:
+.nf
+
+.ne 7
+ print if defined $switch{'D'};
+ print "$val\en" while defined($val = pop(@ary));
+ die "Can't readlink $sym: $!"
+ unless defined($value = readlink $sym);
+ eval '@foo = ()' if defined(@foo);
+ die "No XYZ package defined" unless defined %_XYZ;
+ sub foo { defined &$bar ? &$bar(@_) : die "No bar"; }
+
+.fi
+See also undef.
+.Ip "delete $ASSOC{KEY}" 8 6
+Deletes the specified value from the specified associative array.
+Returns the deleted value, or the undefined value if nothing was deleted.
+Deleting from $ENV{} modifies the environment.
+Deleting from an array bound to a dbm file deletes the entry from the dbm
+file.
+.Sp
+The following deletes all the values of an associative array:
+.nf
+
+.ne 3
+ foreach $key (keys %ARRAY) {
+ delete $ARRAY{$key};
+ }
+
+.fi
+(But it would be faster to use the
+.I reset
+command.
+Saying undef %ARRAY is faster yet.)
+.Ip "die(LIST)" 8
+.Ip "die LIST" 8
+Outside of an eval, prints the value of LIST to
+.I STDERR
+and exits with the current value of $!
+(errno).
+If $! is 0, exits with the value of ($? >> 8) (\`command\` status).
+If ($? >> 8) is 0, exits with 255.
+Inside an eval, the error message is stuffed into $@ and the eval is terminated
+with the undefined value.
+.Sp
+Equivalent examples:
+.nf
+
+.ne 3
+.ie t \{\
+ die "Can't cd to spool: $!\en" unless chdir \'/usr/spool/news\';
+'br\}
+.el \{\
+ die "Can't cd to spool: $!\en"
+ unless chdir \'/usr/spool/news\';
+'br\}
+
+ chdir \'/usr/spool/news\' || die "Can't cd to spool: $!\en"
+
+.fi
+.Sp
+If the value of EXPR does not end in a newline, the current script line
+number and input line number (if any) are also printed, and a newline is
+supplied.
+Hint: sometimes appending \*(L", stopped\*(R" to your message will cause it to make
+better sense when the string \*(L"at foo line 123\*(R" is appended.
+Suppose you are running script \*(L"canasta\*(R".
+.nf
+
+.ne 7
+ die "/etc/games is no good";
+ die "/etc/games is no good, stopped";
+
+produce, respectively
+
+ /etc/games is no good at canasta line 123.
+ /etc/games is no good, stopped at canasta line 123.
+
+.fi
+See also
+.IR exit .
+.Ip "do BLOCK" 8 4
+Returns the value of the last command in the sequence of commands indicated
+by BLOCK.
+When modified by a loop modifier, executes the BLOCK once before testing the
+loop condition.
+(On other statements the loop modifiers test the conditional first.)
+.Ip "do SUBROUTINE (LIST)" 8 3
+Executes a SUBROUTINE declared by a
+.I sub
+declaration, and returns the value
+of the last expression evaluated in SUBROUTINE.
+If there is no subroutine by that name, produces a fatal error.
+(You may use the \*(L"defined\*(R" operator to determine if a subroutine
+exists.)
+If you pass arrays as part of LIST you may wish to pass the length
+of the array in front of each array.
+(See the section on subroutines later on.)
+The parentheses are required to avoid confusion with the \*(L"do EXPR\*(R"
+form.
+.Sp
+SUBROUTINE may also be a single scalar variable, in which case
+the name of the subroutine to execute is taken from the variable.
+.Sp
+As an alternate (and preferred) form,
+you may call a subroutine by prefixing the name with
+an ampersand: &foo(@args).
+If you aren't passing any arguments, you don't have to use parentheses.
+If you omit the parentheses, no @_ array is passed to the subroutine.
+The & form is also used to specify subroutines to the defined and undef
+operators:
+.nf
+
+ if (defined &$var) { &$var($parm); undef &$var; }
+
+.fi
+.Ip "do EXPR" 8 3
+Uses the value of EXPR as a filename and executes the contents of the file
+as a
+.I perl
+script.
+Its primary use is to include subroutines from a
+.I perl
+subroutine library.
+.nf
+
+ do \'stat.pl\';
+
+is just like
+
+ eval \`cat stat.pl\`;
+
+.fi
+except that it's more efficient, more concise, keeps track of the current
+filename for error messages, and searches all the
+.B \-I
+libraries if the file
+isn't in the current directory (see also the @INC array in Predefined Names).
+It's the same, however, in that it does reparse the file every time you
+call it, so if you are going to use the file inside a loop you might prefer
+to use \-P and #include, at the expense of a little more startup time.
+(The main problem with #include is that cpp doesn't grok # comments\*(--a
+workaround is to use \*(L";#\*(R" for standalone comments.)
+Note that the following are NOT equivalent:
+.nf
+
+.ne 2
+ do $foo; # eval a file
+ do $foo(); # call a subroutine
+
+.fi
+Note that inclusion of library routines is better done with
+the \*(L"require\*(R" operator.
+.Ip "dump LABEL" 8 6
+This causes an immediate core dump.
+Primarily this is so that you can use the undump program to turn your
+core dump into an executable binary after having initialized all your
+variables at the beginning of the program.
+When the new binary is executed it will begin by executing a "goto LABEL"
+(with all the restrictions that goto suffers).
+Think of it as a goto with an intervening core dump and reincarnation.
+If LABEL is omitted, restarts the program from the top.
+WARNING: any files opened at the time of the dump will NOT be open any more
+when the program is reincarnated, with possible resulting confusion on the part
+of perl.
+See also \-u.
+.Sp
+Example:
+.nf
+
+.ne 16
+ #!/usr/bin/perl
+ require 'getopt.pl';
+ require 'stat.pl';
+ %days = (
+ 'Sun',1,
+ 'Mon',2,
+ 'Tue',3,
+ 'Wed',4,
+ 'Thu',5,
+ 'Fri',6,
+ 'Sat',7);
+
+ dump QUICKSTART if $ARGV[0] eq '-d';
+
+ QUICKSTART:
+ do Getopt('f');
+
+.fi
+.Ip "each(ASSOC_ARRAY)" 8 6
+.Ip "each ASSOC_ARRAY" 8
+Returns a 2 element array consisting of the key and value for the next
+value of an associative array, so that you can iterate over it.
+Entries are returned in an apparently random order.
+When the array is entirely read, a null array is returned (which when
+assigned produces a FALSE (0) value).
+The next call to each() after that will start iterating again.
+The iterator can be reset only by reading all the elements from the array.
+You must not modify the array while iterating over it.
+There is a single iterator for each associative array, shared by all
+each(), keys() and values() function calls in the program.
+The following prints out your environment like the printenv program, only
+in a different order:
+.nf
+
+.ne 3
+ while (($key,$value) = each %ENV) {
+ print "$key=$value\en";
+ }
+
+.fi
+See also keys() and values().
+.Ip "eof(FILEHANDLE)" 8 8
+.Ip "eof()" 8
+.Ip "eof" 8
+Returns 1 if the next read on FILEHANDLE will return end of file, or if
+FILEHANDLE is not open.
+FILEHANDLE may be an expression whose value gives the real filehandle name.
+(Note that this function actually reads a character and then ungetc's it,
+so it is not very useful in an interactive context.)
+An eof without an argument returns the eof status for the last file read.
+Empty parentheses () may be used to indicate the pseudo file formed of the
+files listed on the command line, i.e. eof() is reasonable to use inside
+a while (<>) loop to detect the end of only the last file.
+Use eof(ARGV) or eof without the parentheses to test EACH file in a while (<>) loop.
+Examples:
+.nf
+
+.ne 7
+ # insert dashes just before last line of last file
+ while (<>) {
+ if (eof()) {
+ print "\-\|\-\|\-\|\-\|\-\|\-\|\-\|\-\|\-\|\-\|\-\|\-\|\-\|\-\en";
+ }
+ print;
+ }
+
+.ne 7
+ # reset line numbering on each input file
+ while (<>) {
+ print "$.\et$_";
+ if (eof) { # Not eof().
+ close(ARGV);
+ }
+ }
+
+.fi
+.Ip "eval(EXPR)" 8 6
+.Ip "eval EXPR" 8 6
+.Ip "eval BLOCK" 8 6
+EXPR is parsed and executed as if it were a little
+.I perl
+program.
+It is executed in the context of the current
+.I perl
+program, so that
+any variable settings, subroutine or format definitions remain afterwards.
+The value returned is the value of the last expression evaluated, just
+as with subroutines.
+If there is a syntax error or runtime error, or a die statement is
+executed, an undefined value is returned by
+eval, and $@ is set to the error message.
+If there was no error, $@ is guaranteed to be a null string.
+If EXPR is omitted, evaluates $_.
+The final semicolon, if any, may be omitted from the expression.
+.Sp
+Note that, since eval traps otherwise-fatal errors, it is useful for
+determining whether a particular feature
+(such as dbmopen or symlink) is implemented.
+It is also Perl's exception trapping mechanism, where the die operator is
+used to raise exceptions.
+.Sp
+If the code to be executed doesn't vary, you may use
+the eval-BLOCK form to trap run-time errors without incurring
+the penalty of recompiling each time.
+The error, if any, is still returned in $@.
+Evaluating a single-quoted string (as EXPR) has the same effect, except that
+the eval-EXPR form reports syntax errors at run time via $@, whereas the
+eval-BLOCK form reports syntax errors at compile time. The eval-EXPR form
+is optimized to eval-BLOCK the first time it succeeds. (Since the replacement
+side of a substitution is considered a single-quoted string when you
+use the e modifier, the same optimization occurs there.) Examples:
+.nf
+
+.ne 11
+ # make divide-by-zero non-fatal
+ eval { $answer = $a / $b; }; warn $@ if $@;
+
+ # optimized to same thing after first use
+ eval '$answer = $a / $b'; warn $@ if $@;
+
+ # a compile-time error
+ eval { $answer = };
+
+ # a run-time error
+ eval '$answer ='; # sets $@
+
+.fi
+.Ip "exec(LIST)" 8 8
+.Ip "exec LIST" 8 6
+If there is more than one argument in LIST, or if LIST is an array with
+more than one value,
+calls execvp() with the arguments in LIST.
+If there is only one scalar argument, the argument is checked for shell metacharacters.
+If there are any, the entire argument is passed to \*(L"/bin/sh \-c\*(R" for parsing.
+If there are none, the argument is split into words and passed directly to
+execvp(), which is more efficient.
+Note: exec (and system) do not flush your output buffer, so you may need to
+set $| to avoid lost output.
+Examples:
+.nf
+
+ exec \'/bin/echo\', \'Your arguments are: \', @ARGV;
+ exec "sort $outfile | uniq";
+
+.fi
+.Sp
+If you don't really want to execute the first argument, but want to lie
+to the program you are executing about its own name, you can specify
+the program you actually want to run by assigning that to a variable and
+putting the name of the variable in front of the LIST without a comma.
+(This always forces interpretation of the LIST as a multi-valued list, even
+if there is only a single scalar in the list.)
+Example:
+.nf
+
+.ne 2
+ $shell = '/bin/csh';
+ exec $shell '-sh'; # pretend it's a login shell
+
+.fi
+.Ip "exit(EXPR)" 8 6
+.Ip "exit EXPR" 8
+Evaluates EXPR and exits immediately with that value.
+Example:
+.nf
+
+.ne 2
+ $ans = <STDIN>;
+ exit 0 \|if \|$ans \|=~ \|/\|^[Xx]\|/\|;
+
+.fi
+See also
+.IR die .
+If EXPR is omitted, exits with 0 status.
+.Ip "exp(EXPR)" 8 3
+.Ip "exp EXPR" 8
+Returns
+.I e
+to the power of EXPR.
+If EXPR is omitted, gives exp($_).
+.Ip "fcntl(FILEHANDLE,FUNCTION,SCALAR)" 8 4
+Implements the fcntl(2) function.
+You'll probably have to say
+.nf
+
+ require "fcntl.ph"; # probably /usr/local/lib/perl/fcntl.ph
+
+.fi
+first to get the correct function definitions.
+If fcntl.ph doesn't exist or doesn't have the correct definitions
+you'll have to roll
+your own, based on your C header files such as <sys/fcntl.h>.
+(There is a perl script called h2ph that comes with the perl kit
+which may help you in this.)
+Argument processing and value return works just like ioctl below.
+Note that fcntl will produce a fatal error if used on a machine that doesn't implement
+fcntl(2).
+.Ip "fileno(FILEHANDLE)" 8 4
+.Ip "fileno FILEHANDLE" 8 4
+Returns the file descriptor for a filehandle.
+Useful for constructing bitmaps for select().
+If FILEHANDLE is an expression, the value is taken as the name of
+the filehandle.
+.Ip "flock(FILEHANDLE,OPERATION)" 8 4
+Calls flock(2) on FILEHANDLE.
+See manual page for flock(2) for definition of OPERATION.
+Returns true for success, false on failure.
+Will produce a fatal error if used on a machine that doesn't implement
+flock(2).
+Here's a mailbox appender for BSD systems.
+.nf
+
+.ne 20
+ $LOCK_SH = 1;
+ $LOCK_EX = 2;
+ $LOCK_NB = 4;
+ $LOCK_UN = 8;
+
+ sub lock {
+ flock(MBOX,$LOCK_EX);
+ # and, in case someone appended
+ # while we were waiting...
+ seek(MBOX, 0, 2);
+ }
+
+ sub unlock {
+ flock(MBOX,$LOCK_UN);
+ }
+
+ open(MBOX, ">>/usr/spool/mail/$ENV{'USER'}")
+ || die "Can't open mailbox: $!";
+
+ do lock();
+ print MBOX $msg,"\en\en";
+ do unlock();
+
+.fi
+.Ip "fork" 8 4
+Does a fork() call.
+Returns the child pid to the parent process and 0 to the child process.
+Note: unflushed buffers remain unflushed in both processes, which means
+you may need to set $| to avoid duplicate output.
+.Ip "getc(FILEHANDLE)" 8 4
+.Ip "getc FILEHANDLE" 8
+.Ip "getc" 8
+Returns the next character from the input file attached to FILEHANDLE, or
+a null string at EOF.
+If FILEHANDLE is omitted, reads from STDIN.
+.Ip "getlogin" 8 3
+Returns the current login from /etc/utmp, if any.
+If null, use getpwuid.
+
+ $login = getlogin || (getpwuid($<))[0] || "Somebody";
+
+.Ip "getpeername(SOCKET)" 8 3
+Returns the packed sockaddr address of other end of the SOCKET connection.
+.nf
+
+.ne 4
+ # An internet sockaddr
+ $sockaddr = 'S n a4 x8';
+ $hersockaddr = getpeername(S);
+.ie t \{\
+ ($family, $port, $heraddr) = unpack($sockaddr,$hersockaddr);
+'br\}
+.el \{\
+ ($family, $port, $heraddr) =
+ unpack($sockaddr,$hersockaddr);
+'br\}
+
+.fi
+.Ip "getpgrp(PID)" 8 4
+.Ip "getpgrp PID" 8
+Returns the current process group for the specified PID, 0 for the current
+process.
+Will produce a fatal error if used on a machine that doesn't implement
+getpgrp(2).
+If EXPR is omitted, returns process group of current process.
+.Ip "getppid" 8 4
+Returns the process id of the parent process.
+.Ip "getpriority(WHICH,WHO)" 8 4
+Returns the current priority for a process, a process group, or a user.
+(See getpriority(2).)
+Will produce a fatal error if used on a machine that doesn't implement
+getpriority(2).
+.Ip "getpwnam(NAME)" 8
+.Ip "getgrnam(NAME)" 8
+.Ip "gethostbyname(NAME)" 8
+.Ip "getnetbyname(NAME)" 8
+.Ip "getprotobyname(NAME)" 8
+.Ip "getpwuid(UID)" 8
+.Ip "getgrgid(GID)" 8
+.Ip "getservbyname(NAME,PROTO)" 8
+.Ip "gethostbyaddr(ADDR,ADDRTYPE)" 8
+.Ip "getnetbyaddr(ADDR,ADDRTYPE)" 8
+.Ip "getprotobynumber(NUMBER)" 8
+.Ip "getservbyport(PORT,PROTO)" 8
+.Ip "getpwent" 8
+.Ip "getgrent" 8
+.Ip "gethostent" 8
+.Ip "getnetent" 8
+.Ip "getprotoent" 8
+.Ip "getservent" 8
+.Ip "setpwent" 8
+.Ip "setgrent" 8
+.Ip "sethostent(STAYOPEN)" 8
+.Ip "setnetent(STAYOPEN)" 8
+.Ip "setprotoent(STAYOPEN)" 8
+.Ip "setservent(STAYOPEN)" 8
+.Ip "endpwent" 8
+.Ip "endgrent" 8
+.Ip "endhostent" 8
+.Ip "endnetent" 8
+.Ip "endprotoent" 8
+.Ip "endservent" 8
+These routines perform the same functions as their counterparts in the
+system library.
+Within an array context,
+the return values from the various get routines are as follows:
+.nf
+
+ ($name,$passwd,$uid,$gid,
+ $quota,$comment,$gcos,$dir,$shell) = getpw.\|.\|.
+ ($name,$passwd,$gid,$members) = getgr.\|.\|.
+ ($name,$aliases,$addrtype,$length,@addrs) = gethost.\|.\|.
+ ($name,$aliases,$addrtype,$net) = getnet.\|.\|.
+ ($name,$aliases,$proto) = getproto.\|.\|.
+ ($name,$aliases,$port,$proto) = getserv.\|.\|.
+
+.fi
+(If the entry doesn't exist you get a null list.)
+.Sp
+Within a scalar context, you get the name, unless the function was a
+lookup by name, in which case you get the other thing, whatever it is.
+(If the entry doesn't exist you get the undefined value.)
+For example:
+.nf
+
+ $uid = getpwnam
+ $name = getpwuid
+ $name = getpwent
+ $gid = getgrnam
+ $name = getgrgid
+ $name = getgrent
+ etc.
+
+.fi
+The $members value returned by getgr.\|.\|. is a space separated list
+of the login names of the members of the group.
+.Sp
+For the gethost.\|.\|. functions, if the h_errno variable is supported in C,
+it will be returned to you via $? if the function call fails.
+The @addrs value returned by a successful call is a list of the
+raw addresses returned by the corresponding system library call.
+In the Internet domain, each address is four bytes long and you can unpack
+it by saying something like:
+.nf
+
+ ($a,$b,$c,$d) = unpack('C4',$addr[0]);
+
+.fi
+.Ip "getsockname(SOCKET)" 8 3
+Returns the packed sockaddr address of this end of the SOCKET connection.
+.nf
+
+.ne 4
+ # An internet sockaddr
+ $sockaddr = 'S n a4 x8';
+ $mysockaddr = getsockname(S);
+.ie t \{\
+ ($family, $port, $myaddr) = unpack($sockaddr,$mysockaddr);
+'br\}
+.el \{\
+ ($family, $port, $myaddr) =
+ unpack($sockaddr,$mysockaddr);
+'br\}
+
+.fi
+.Ip "getsockopt(SOCKET,LEVEL,OPTNAME)" 8 3
+Returns the socket option requested, or undefined if there is an error.
+.Ip "gmtime(EXPR)" 8 4
+.Ip "gmtime EXPR" 8
+Converts a time as returned by the time function to a 9-element array with
+the time analyzed for the Greenwich timezone.
+Typically used as follows:
+.nf
+
+.ne 3
+.ie t \{\
+ ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time);
+'br\}
+.el \{\
+ ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
+ gmtime(time);
+'br\}
+
+.fi
+All array elements are numeric, and come straight out of a struct tm.
+In particular this means that $mon has the range 0.\|.11 and $wday has the
+range 0.\|.6.
+If EXPR is omitted, does gmtime(time).
+.Ip "goto LABEL" 8 6
+Finds the statement labeled with LABEL and resumes execution there.
+Currently you may only go to statements in the main body of the program
+that are not nested inside a do {} construct.
+This statement is not implemented very efficiently, and is here only to make
+the
+.IR sed -to- perl
+translator easier.
+I may change its semantics at any time, consistent with support for translated
+.I sed
+scripts.
+Use it at your own risk.
+Better yet, don't use it at all.
+.Ip "grep(EXPR,LIST)" 8 4
+Evaluates EXPR for each element of LIST (locally setting $_ to each element)
+and returns the array value consisting of those elements for which the
+expression evaluated to true.
+In a scalar context, returns the number of times the expression was true.
+.nf
+
+ @foo = grep(!/^#/, @bar); # weed out comments
+
+.fi
+Note that, since $_ is a reference into the array value, it can be
+used to modify the elements of the array.
+While this is useful and supported, it can cause bizarre results if
+the LIST is not a named array.
+.Ip "hex(EXPR)" 8 4
+.Ip "hex EXPR" 8
+Returns the decimal value of EXPR interpreted as an hex string.
+(To interpret strings that might start with 0 or 0x see oct().)
+If EXPR is omitted, uses $_.
+.Ip "index(STR,SUBSTR,POSITION)" 8 4
+.Ip "index(STR,SUBSTR)" 8 4
+Returns the position of the first occurrence of SUBSTR in STR at or after
+POSITION.
+If POSITION is omitted, starts searching from the beginning of the string.
+The return value is based at 0, or whatever you've
+set the $[ variable to.
+If the substring is not found, returns one less than the base, ordinarily \-1.
+.Ip "int(EXPR)" 8 4
+.Ip "int EXPR" 8
+Returns the integer portion of EXPR.
+If EXPR is omitted, uses $_.
+.Ip "ioctl(FILEHANDLE,FUNCTION,SCALAR)" 8 4
+Implements the ioctl(2) function.
+You'll probably have to say
+.nf
+
+ require "ioctl.ph"; # probably /usr/local/lib/perl/ioctl.ph
+
+.fi
+first to get the correct function definitions.
+If ioctl.ph doesn't exist or doesn't have the correct definitions
+you'll have to roll
+your own, based on your C header files such as <sys/ioctl.h>.
+(There is a perl script called h2ph that comes with the perl kit
+which may help you in this.)
+SCALAR will be read and/or written depending on the FUNCTION\*(--a pointer
+to the string value of SCALAR will be passed as the third argument of
+the actual ioctl call.
+(If SCALAR has no string value but does have a numeric value, that value
+will be passed rather than a pointer to the string value.
+To guarantee this to be true, add a 0 to the scalar before using it.)
+The pack() and unpack() functions are useful for manipulating the values
+of structures used by ioctl().
+The following example sets the erase character to DEL.
+.nf
+
+.ne 9
+ require 'ioctl.ph';
+ $sgttyb_t = "ccccs"; # 4 chars and a short
+ if (ioctl(STDIN,$TIOCGETP,$sgttyb)) {
+ @ary = unpack($sgttyb_t,$sgttyb);
+ $ary[2] = 127;
+ $sgttyb = pack($sgttyb_t,@ary);
+ ioctl(STDIN,$TIOCSETP,$sgttyb)
+ || die "Can't ioctl: $!";
+ }
+
+.fi
+The return value of ioctl (and fcntl) is as follows:
+.nf
+
+.ne 4
+ if OS returns:\h'|3i'perl returns:
+ -1\h'|3i' undefined value
+ 0\h'|3i' string "0 but true"
+ anything else\h'|3i' that number
+
+.fi
+Thus perl returns true on success and false on failure, yet you can still
+easily determine the actual value returned by the operating system:
+.nf
+
+ ($retval = ioctl(...)) || ($retval = -1);
+ printf "System returned %d\en", $retval;
+.fi
+.Ip "join(EXPR,LIST)" 8 8
+.Ip "join(EXPR,ARRAY)" 8
+Joins the separate strings of LIST or ARRAY into a single string with fields
+separated by the value of EXPR, and returns the string.
+Example:
+.nf
+
+.ie t \{\
+ $_ = join(\|\':\', $login,$passwd,$uid,$gid,$gcos,$home,$shell);
+'br\}
+.el \{\
+ $_ = join(\|\':\',
+ $login,$passwd,$uid,$gid,$gcos,$home,$shell);
+'br\}
+
+.fi
+See
+.IR split .
+.Ip "keys(ASSOC_ARRAY)" 8 6
+.Ip "keys ASSOC_ARRAY" 8
+Returns a normal array consisting of all the keys of the named associative
+array.
+The keys are returned in an apparently random order, but it is the same order
+as either the values() or each() function produces (given that the associative array
+has not been modified).
+Here is yet another way to print your environment:
+.nf
+
+.ne 5
+ @keys = keys %ENV;
+ @values = values %ENV;
+ while ($#keys >= 0) {
+ print pop(@keys), \'=\', pop(@values), "\en";
+ }
+
+or how about sorted by key:
+
+.ne 3
+ foreach $key (sort(keys %ENV)) {
+ print $key, \'=\', $ENV{$key}, "\en";
+ }
+
+.fi
+.Ip "kill(LIST)" 8 8
+.Ip "kill LIST" 8 2
+Sends a signal to a list of processes.
+The first element of the list must be the signal to send.
+Returns the number of processes successfully signaled.
+.nf
+
+ $cnt = kill 1, $child1, $child2;
+ kill 9, @goners;
+
+.fi
+If the signal is negative, kills process groups instead of processes.
+(On System V, a negative \fIprocess\fR number will also kill process groups,
+but that's not portable.)
+You may use a signal name in quotes.
+.Ip "last LABEL" 8 8
+.Ip "last" 8
+The
+.I last
+command is like the
+.I break
+statement in C (as used in loops); it immediately exits the loop in question.
+If the LABEL is omitted, the command refers to the innermost enclosing loop.
+The
+.I continue
+block, if any, is not executed:
+.nf
+
+.ne 4
+ line: while (<STDIN>) {
+ last line if /\|^$/; # exit when done with header
+ .\|.\|.
+ }
+
+.fi
+.Ip "length(EXPR)" 8 4
+.Ip "length EXPR" 8
+Returns the length in characters of the value of EXPR.
+If EXPR is omitted, returns length of $_.
+.Ip "link(OLDFILE,NEWFILE)" 8 2
+Creates a new filename linked to the old filename.
+Returns 1 for success, 0 otherwise.
+.Ip "listen(SOCKET,QUEUESIZE)" 8 2
+Does the same thing that the listen system call does.
+Returns true if it succeeded, false otherwise.
+See example in section on Interprocess Communication.
+.Ip "local(LIST)" 8 4
+Declares the listed variables to be local to the enclosing block,
+subroutine, eval or \*(L"do\*(R".
+All the listed elements must be legal lvalues.
+This operator works by saving the current values of those variables in LIST
+on a hidden stack and restoring them upon exiting the block, subroutine or eval.
+This means that called subroutines can also reference the local variable,
+but not the global one.
+The LIST may be assigned to if desired, which allows you to initialize
+your local variables.
+(If no initializer is given for a particular variable, it is created with
+an undefined value.)
+Commonly this is used to name the parameters to a subroutine.
+Examples:
+.nf
+
+.ne 13
+ sub RANGEVAL {
+ local($min, $max, $thunk) = @_;
+ local($result) = \'\';
+ local($i);
+
+ # Presumably $thunk makes reference to $i
+
+ for ($i = $min; $i < $max; $i++) {
+ $result .= eval $thunk;
+ }
+
+ $result;
+ }
+
+.ne 6
+ if ($sw eq \'-v\') {
+ # init local array with global array
+ local(@ARGV) = @ARGV;
+ unshift(@ARGV,\'echo\');
+ system @ARGV;
+ }
+ # @ARGV restored
+
+.ne 6
+ # temporarily add to digits associative array
+ if ($base12) {
+ # (NOTE: not claiming this is efficient!)
+ local(%digits) = (%digits,'t',10,'e',11);
+ do parse_num();
+ }
+
+.fi
+Note that local() is a run-time command, and so gets executed every time
+through a loop, using up more stack storage each time until it's all
+released at once when the loop is exited.
+.Ip "localtime(EXPR)" 8 4
+.Ip "localtime EXPR" 8
+Converts a time as returned by the time function to a 9-element array with
+the time analyzed for the local timezone.
+Typically used as follows:
+.nf
+
+.ne 3
+.ie t \{\
+ ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
+'br\}
+.el \{\
+ ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
+ localtime(time);
+'br\}
+
+.fi
+All array elements are numeric, and come straight out of a struct tm.
+In particular this means that $mon has the range 0.\|.11 and $wday has the
+range 0.\|.6.
+If EXPR is omitted, does localtime(time).
+.Ip "log(EXPR)" 8 4
+.Ip "log EXPR" 8
+Returns logarithm (base
+.IR e )
+of EXPR.
+If EXPR is omitted, returns log of $_.
+.Ip "lstat(FILEHANDLE)" 8 6
+.Ip "lstat FILEHANDLE" 8
+.Ip "lstat(EXPR)" 8
+.Ip "lstat SCALARVARIABLE" 8
+Does the same thing as the stat() function, but stats a symbolic link
+instead of the file the symbolic link points to.
+If symbolic links are unimplemented on your system, a normal stat is done.
+.Ip "m/PATTERN/gio" 8 4
+.Ip "/PATTERN/gio" 8
+Searches a string for a pattern match, and returns true (1) or false (\'\').
+If no string is specified via the =~ or !~ operator,
+the $_ string is searched.
+(The string specified with =~ need not be an lvalue\*(--it may be the result of an expression evaluation, but remember the =~ binds rather tightly.)
+See also the section on regular expressions.
+.Sp
+If / is the delimiter then the initial \*(L'm\*(R' is optional.
+With the \*(L'm\*(R' you can use any pair of non-alphanumeric characters
+as delimiters.
+This is particularly useful for matching Unix path names that contain \*(L'/\*(R'.
+If the final delimiter is followed by the optional letter \*(L'i\*(R', the matching is
+done in a case-insensitive manner.
+PATTERN may contain references to scalar variables, which will be interpolated
+(and the pattern recompiled) every time the pattern search is evaluated.
+(Note that $) and $| may not be interpolated because they look like end-of-string tests.)
+If you want such a pattern to be compiled only once, add an \*(L"o\*(R" after
+the trailing delimiter.
+This avoids expensive run-time recompilations, and
+is useful when the value you are interpolating won't change over the
+life of the script.
+If the PATTERN evaluates to a null string, the most recent successful
+regular expression is used instead.
+.Sp
+If used in a context that requires an array value, a pattern match returns an
+array consisting of the subexpressions matched by the parentheses in the
+pattern,
+i.e. ($1, $2, $3.\|.\|.).
+It does NOT actually set $1, $2, etc. in this case, nor does it set $+, $`, $&
+or $'.
+If the match fails, a null array is returned.
+If the match succeeds, but there were no parentheses, an array value of (1)
+is returned.
+.Sp
+Examples:
+.nf
+
+.ne 4
+ open(tty, \'/dev/tty\');
+ <tty> \|=~ \|/\|^y\|/i \|&& \|do foo(\|); # do foo if desired
+
+ if (/Version: \|*\|([0\-9.]*\|)\|/\|) { $version = $1; }
+
+ next if m#^/usr/spool/uucp#;
+
+.ne 5
+ # poor man's grep
+ $arg = shift;
+ while (<>) {
+ print if /$arg/o; # compile only once
+ }
+
+ if (($F1, $F2, $Etc) = ($foo =~ /^(\eS+)\es+(\eS+)\es*(.*)/))
+
+.fi
+This last example splits $foo into the first two words and the remainder
+of the line, and assigns those three fields to $F1, $F2 and $Etc.
+The conditional is true if any variables were assigned, i.e. if the pattern
+matched.
+.Sp
+The \*(L"g\*(R" modifier specifies global pattern matching\*(--that is,
+matching as many times as possible within the string. How it behaves
+depends on the context. In an array context, it returns a list of
+all the substrings matched by all the parentheses in the regular expression.
+If there are no parentheses, it returns a list of all the matched strings,
+as if there were parentheses around the whole pattern. In a scalar context,
+it iterates through the string, returning TRUE each time it matches, and
+FALSE when it eventually runs out of matches. (In other words, it remembers
+where it left off last time and restarts the search at that point.) It
+presumes that you have not modified the string since the last match.
+Modifying the string between matches may result in undefined behavior.
+(You can actually get away with in-place modifications via substr()
+that do not change the length of the entire string. In general, however,
+you should be using s///g for such modifications.) Examples:
+.nf
+
+ # array context
+ ($one,$five,$fifteen) = (\`uptime\` =~ /(\ed+\e.\ed+)/g);
+
+ # scalar context
+ $/ = ""; $* = 1;
+ while ($paragraph = <>) {
+ while ($paragraph =~ /[a-z][\'")]*[.!?]+[\'")]*\es/g) {
+ $sentences++;
+ }
+ }
+ print "$sentences\en";
+
+.fi
+.Ip "mkdir(FILENAME,MODE)" 8 3
+Creates the directory specified by FILENAME, with permissions specified by
+MODE (as modified by umask).
+If it succeeds it returns 1, otherwise it returns 0 and sets $! (errno).
+.Ip "msgctl(ID,CMD,ARG)" 8 4
+Calls the System V IPC function msgctl. If CMD is &IPC_STAT, then ARG
+must be a variable which will hold the returned msqid_ds structure.
+Returns like ioctl: the undefined value for error, "0 but true" for
+zero, or the actual return value otherwise.
+.Ip "msgget(KEY,FLAGS)" 8 4
+Calls the System V IPC function msgget. Returns the message queue id,
+or the undefined value if there is an error.
+.Ip "msgsnd(ID,MSG,FLAGS)" 8 4
+Calls the System V IPC function msgsnd to send the message MSG to the
+message queue ID. MSG must begin with the long integer message type,
+which may be created with pack("L", $type). Returns true if
+successful, or false if there is an error.
+.Ip "msgrcv(ID,VAR,SIZE,TYPE,FLAGS)" 8 4
+Calls the System V IPC function msgrcv to receive a message from
+message queue ID into variable VAR with a maximum message size of
+SIZE. Note that if a message is received, the message type will be
+the first thing in VAR, and the maximum length of VAR is SIZE plus the
+size of the message type. Returns true if successful, or false if
+there is an error.
+.Ip "next LABEL" 8 8
+.Ip "next" 8
+The
+.I next
+command is like the
+.I continue
+statement in C; it starts the next iteration of the loop:
+.nf
+
+.ne 4
+ line: while (<STDIN>) {
+ next line if /\|^#/; # discard comments
+ .\|.\|.
+ }
+
+.fi
+Note that if there were a
+.I continue
+block on the above, it would get executed even on discarded lines.
+If the LABEL is omitted, the command refers to the innermost enclosing loop.
+.Ip "oct(EXPR)" 8 4
+.Ip "oct EXPR" 8
+Returns the decimal value of EXPR interpreted as an octal string.
+(If EXPR happens to start off with 0x, interprets it as a hex string instead.)
+The following will handle decimal, octal and hex in the standard notation:
+.nf
+
+ $val = oct($val) if $val =~ /^0/;
+
+.fi
+If EXPR is omitted, uses $_.
+.Ip "open(FILEHANDLE,EXPR)" 8 8
+.Ip "open(FILEHANDLE)" 8
+.Ip "open FILEHANDLE" 8
+Opens the file whose filename is given by EXPR, and associates it with
+FILEHANDLE.
+If FILEHANDLE is an expression, its value is used as the name of the
+real filehandle wanted.
+If EXPR is omitted, the scalar variable of the same name as the FILEHANDLE
+contains the filename.
+If the filename begins with \*(L"<\*(R" or nothing, the file is opened for
+input.
+If the filename begins with \*(L">\*(R", the file is opened for output.
+If the filename begins with \*(L">>\*(R", the file is opened for appending.
+(You can put a \'+\' in front of the \'>\' or \'<\' to indicate that you
+want both read and write access to the file.)
+If the filename begins with \*(L"|\*(R", the filename is interpreted
+as a command to which output is to be piped, and if the filename ends
+with a \*(L"|\*(R", the filename is interpreted as command which pipes
+input to us.
+(You may not have a command that pipes both in and out.)
+Opening \'\-\' opens
+.I STDIN
+and opening \'>\-\' opens
+.IR STDOUT .
+Open returns non-zero upon success, the undefined value otherwise.
+If the open involved a pipe, the return value happens to be the pid
+of the subprocess.
+Examples:
+.nf
+
+.ne 3
+ $article = 100;
+ open article || die "Can't find article $article: $!\en";
+ while (<article>) {\|.\|.\|.
+
+.ie t \{\
+ open(LOG, \'>>/usr/spool/news/twitlog\'\|); # (log is reserved)
+'br\}
+.el \{\
+ open(LOG, \'>>/usr/spool/news/twitlog\'\|);
+ # (log is reserved)
+'br\}
+
+.ie t \{\
+ open(article, "caesar <$article |"\|); # decrypt article
+'br\}
+.el \{\
+ open(article, "caesar <$article |"\|);
+ # decrypt article
+'br\}
+
+.ie t \{\
+ open(extract, "|sort >/tmp/Tmp$$"\|); # $$ is our process#
+'br\}
+.el \{\
+ open(extract, "|sort >/tmp/Tmp$$"\|);
+ # $$ is our process#
+'br\}
+
+.ne 7
+ # process argument list of files along with any includes
+
+ foreach $file (@ARGV) {
+ do process($file, \'fh00\'); # no pun intended
+ }
+
+ sub process {
+ local($filename, $input) = @_;
+ $input++; # this is a string increment
+ unless (open($input, $filename)) {
+ print STDERR "Can't open $filename: $!\en";
+ return;
+ }
+.ie t \{\
+ while (<$input>) { # note the use of indirection
+'br\}
+.el \{\
+ while (<$input>) { # note use of indirection
+'br\}
+ if (/^#include "(.*)"/) {
+ do process($1, $input);
+ next;
+ }
+ .\|.\|. # whatever
+ }
+ }
+
+.fi
+You may also, in the Bourne shell tradition, specify an EXPR beginning
+with \*(L">&\*(R", in which case the rest of the string
+is interpreted as the name of a filehandle
+(or file descriptor, if numeric) which is to be duped and opened.
+You may use & after >, >>, <, +>, +>> and +<.
+The mode you specify should match the mode of the original filehandle.
+Here is a script that saves, redirects, and restores
+.I STDOUT
+and
+.IR STDERR :
+.nf
+
+.ne 21
+ #!/usr/bin/perl
+ open(SAVEOUT, ">&STDOUT");
+ open(SAVEERR, ">&STDERR");
+
+ open(STDOUT, ">foo.out") || die "Can't redirect stdout";
+ open(STDERR, ">&STDOUT") || die "Can't dup stdout";
+
+ select(STDERR); $| = 1; # make unbuffered
+ select(STDOUT); $| = 1; # make unbuffered
+
+ print STDOUT "stdout 1\en"; # this works for
+ print STDERR "stderr 1\en"; # subprocesses too
+
+ close(STDOUT);
+ close(STDERR);
+
+ open(STDOUT, ">&SAVEOUT");
+ open(STDERR, ">&SAVEERR");
+
+ print STDOUT "stdout 2\en";
+ print STDERR "stderr 2\en";
+
+.fi
+If you open a pipe on the command \*(L"\-\*(R", i.e. either \*(L"|\-\*(R" or \*(L"\-|\*(R",
+then there is an implicit fork done, and the return value of open
+is the pid of the child within the parent process, and 0 within the child
+process.
+(Use defined($pid) to determine if the open was successful.)
+The filehandle behaves normally for the parent, but i/o to that
+filehandle is piped from/to the
+.IR STDOUT / STDIN
+of the child process.
+In the child process the filehandle isn't opened\*(--i/o happens from/to
+the new
+.I STDOUT
+or
+.IR STDIN .
+Typically this is used like the normal piped open when you want to exercise
+more control over just how the pipe command gets executed, such as when
+you are running setuid, and don't want to have to scan shell commands
+for metacharacters.
+The following pairs are more or less equivalent:
+.nf
+
+.ne 5
+ open(FOO, "|tr \'[a\-z]\' \'[A\-Z]\'");
+ open(FOO, "|\-") || exec \'tr\', \'[a\-z]\', \'[A\-Z]\';
+
+ open(FOO, "cat \-n '$file'|");
+ open(FOO, "\-|") || exec \'cat\', \'\-n\', $file;
+
+.fi
+Explicitly closing any piped filehandle causes the parent process to wait for the
+child to finish, and returns the status value in $?.
+Note: on any operation which may do a fork,
+unflushed buffers remain unflushed in both
+processes, which means you may need to set $| to
+avoid duplicate output.
+.Sp
+The filename that is passed to open will have leading and trailing
+whitespace deleted.
+In order to open a file with arbitrary weird characters in it, it's necessary
+to protect any leading and trailing whitespace thusly:
+.nf
+
+.ne 2
+ $file =~ s#^(\es)#./$1#;
+ open(FOO, "< $file\e0");
+
+.fi
+.Ip "opendir(DIRHANDLE,EXPR)" 8 3
+Opens a directory named EXPR for processing by readdir(), telldir(), seekdir(),
+rewinddir() and closedir().
+Returns true if successful.
+DIRHANDLEs have their own namespace separate from FILEHANDLEs.
+.Ip "ord(EXPR)" 8 4
+.Ip "ord EXPR" 8
+Returns the numeric ascii value of the first character of EXPR.
+If EXPR is omitted, uses $_.
+''' Comments on f & d by gnb@melba.bby.oz.au 22/11/89
+.Ip "pack(TEMPLATE,LIST)" 8 4
+Takes an array or list of values and packs it into a binary structure,
+returning the string containing the structure.
+The TEMPLATE is a sequence of characters that give the order and type
+of values, as follows:
+.nf
+
+ A An ascii string, will be space padded.
+ a An ascii string, will be null padded.
+ c A signed char value.
+ C An unsigned char value.
+ s A signed short value.
+ S An unsigned short value.
+ i A signed integer value.
+ I An unsigned integer value.
+ l A signed long value.
+ L An unsigned long value.
+ n A short in \*(L"network\*(R" order.
+ N A long in \*(L"network\*(R" order.
+ f A single-precision float in the native format.
+ d A double-precision float in the native format.
+ p A pointer to a string.
+ v A short in \*(L"VAX\*(R" (little-endian) order.
+ V A long in \*(L"VAX\*(R" (little-endian) order.
+ x A null byte.
+ X Back up a byte.
+ @ Null fill to absolute position.
+ u A uuencoded string.
+ b A bit string (ascending bit order, like vec()).
+ B A bit string (descending bit order).
+ h A hex string (low nybble first).
+ H A hex string (high nybble first).
+
+.fi
+Each letter may optionally be followed by a number which gives a repeat
+count.
+With all types except "a", "A", "b", "B", "h" and "H",
+the pack function will gobble up that many values
+from the LIST.
+A * for the repeat count means to use however many items are left.
+The "a" and "A" types gobble just one value, but pack it as a string of length
+count,
+padding with nulls or spaces as necessary.
+(When unpacking, "A" strips trailing spaces and nulls, but "a" does not.)
+Likewise, the "b" and "B" fields pack a string that many bits long.
+The "h" and "H" fields pack a string that many nybbles long.
+Real numbers (floats and doubles) are in the native machine format
+only; due to the multiplicity of floating formats around, and the lack
+of a standard \*(L"network\*(R" representation, no facility for
+interchange has been made.
+This means that packed floating point data
+written on one machine may not be readable on another - even if both
+use IEEE floating point arithmetic (as the endian-ness of the memory
+representation is not part of the IEEE spec).
+Note that perl uses
+doubles internally for all numeric calculation, and converting from
+double -> float -> double will lose precision (i.e. unpack("f",
+pack("f", $foo)) will not in general equal $foo).
+.br
+Examples:
+.nf
+
+ $foo = pack("cccc",65,66,67,68);
+ # foo eq "ABCD"
+ $foo = pack("c4",65,66,67,68);
+ # same thing
+
+ $foo = pack("ccxxcc",65,66,67,68);
+ # foo eq "AB\e0\e0CD"
+
+ $foo = pack("s2",1,2);
+ # "\e1\e0\e2\e0" on little-endian
+ # "\e0\e1\e0\e2" on big-endian
+
+ $foo = pack("a4","abcd","x","y","z");
+ # "abcd"
+
+ $foo = pack("aaaa","abcd","x","y","z");
+ # "axyz"
+
+ $foo = pack("a14","abcdefg");
+ # "abcdefg\e0\e0\e0\e0\e0\e0\e0"
+
+ $foo = pack("i9pl", gmtime);
+ # a real struct tm (on my system anyway)
+
+ sub bintodec {
+ unpack("N", pack("B32", substr("0" x 32 . shift, -32)));
+ }
+.fi
+The same template may generally also be used in the unpack function.
+.Ip "pipe(READHANDLE,WRITEHANDLE)" 8 3
+Opens a pair of connected pipes like the corresponding system call.
+Note that if you set up a loop of piped processes, deadlock can occur
+unless you are very careful.
+In addition, note that perl's pipes use stdio buffering, so you may need
+to set $| to flush your WRITEHANDLE after each command, depending on
+the application.
+[Requires version 3.0 patchlevel 9.]
+.Ip "pop(ARRAY)" 8
+.Ip "pop ARRAY" 8 6
+Pops and returns the last value of the array, shortening the array by 1.
+Has the same effect as
+.nf
+
+ $tmp = $ARRAY[$#ARRAY\-\|\-];
+
+.fi
+If there are no elements in the array, returns the undefined value.
+.Ip "print(FILEHANDLE LIST)" 8 10
+.Ip "print(LIST)" 8
+.Ip "print FILEHANDLE LIST" 8
+.Ip "print LIST" 8
+.Ip "print" 8
+Prints a string or a comma-separated list of strings.
+Returns non-zero if successful.
+FILEHANDLE may be a scalar variable name, in which case the variable contains
+the name of the filehandle, thus introducing one level of indirection.
+(NOTE: If FILEHANDLE is a variable and the next token is a term, it may be
+misinterpreted as an operator unless you interpose a + or put parens around
+the arguments.)
+If FILEHANDLE is omitted, prints by default to standard output (or to the
+last selected output channel\*(--see select()).
+If LIST is also omitted, prints $_ to
+.IR STDOUT .
+To set the default output channel to something other than
+.I STDOUT
+use the select operation.
+Note that, because print takes a LIST, anything in the LIST is evaluated
+in an array context, and any subroutine that you call will have one or more
+of its expressions evaluated in an array context.
+Also be careful not to follow the print keyword with a left parenthesis
+unless you want the corresponding right parenthesis to terminate the
+arguments to the print\*(--interpose a + or put parens around all the arguments.
+.Ip "printf(FILEHANDLE LIST)" 8 10
+.Ip "printf(LIST)" 8
+.Ip "printf FILEHANDLE LIST" 8
+.Ip "printf LIST" 8
+Equivalent to a \*(L"print FILEHANDLE sprintf(LIST)\*(R".
+.Ip "push(ARRAY,LIST)" 8 7
+Treats ARRAY (@ is optional) as a stack, and pushes the values of LIST
+onto the end of ARRAY.
+The length of ARRAY increases by the length of LIST.
+Has the same effect as
+.nf
+
+ for $value (LIST) {
+ $ARRAY[++$#ARRAY] = $value;
+ }
+
+.fi
+but is more efficient.
+.Ip "q/STRING/" 8 5
+.Ip "qq/STRING/" 8
+.Ip "qx/STRING/" 8
+These are not really functions, but simply syntactic sugar to let you
+avoid putting too many backslashes into quoted strings.
+The q operator is a generalized single quote, and the qq operator a
+generalized double quote.
+The qx operator is a generalized backquote.
+Any non-alphanumeric delimiter can be used in place of /, including newline.
+If the delimiter is an opening bracket or parenthesis, the final delimiter
+will be the corresponding closing bracket or parenthesis.
+(Embedded occurrences of the closing bracket need to be backslashed as usual.)
+Examples:
+.nf
+
+.ne 5
+ $foo = q!I said, "You said, \'She said it.\'"!;
+ $bar = q(\'This is it.\');
+ $today = qx{ date };
+ $_ .= qq
+*** The previous line contains the naughty word "$&".\en
+ if /(ibm|apple|awk)/; # :-)
+
+.fi
+.Ip "rand(EXPR)" 8 8
+.Ip "rand EXPR" 8
+.Ip "rand" 8
+Returns a random fractional number between 0 and the value of EXPR.
+(EXPR should be positive.)
+If EXPR is omitted, returns a value between 0 and 1.
+See also srand().
+.Ip "read(FILEHANDLE,SCALAR,LENGTH,OFFSET)" 8 5
+.Ip "read(FILEHANDLE,SCALAR,LENGTH)" 8 5
+Attempts to read LENGTH bytes of data into variable SCALAR from the specified
+FILEHANDLE.
+Returns the number of bytes actually read, or undef if there was an error.
+SCALAR will be grown or shrunk to the length actually read.
+An OFFSET may be specified to place the read data at some other place
+than the beginning of the string.
+This call is actually implemented in terms of stdio's fread call. To get
+a true read system call, see sysread.
+.Ip "readdir(DIRHANDLE)" 8 3
+.Ip "readdir DIRHANDLE" 8
+Returns the next directory entry for a directory opened by opendir().
+If used in an array context, returns all the rest of the entries in the
+directory.
+If there are no more entries, returns an undefined value in a scalar context
+or a null list in an array context.
+.Ip "readlink(EXPR)" 8 6
+.Ip "readlink EXPR" 8
+Returns the value of a symbolic link, if symbolic links are implemented.
+If not, gives a fatal error.
+If there is some system error, returns the undefined value and sets $! (errno).
+If EXPR is omitted, uses $_.
+.Ip "recv(SOCKET,SCALAR,LEN,FLAGS)" 8 4
+Receives a message on a socket.
+Attempts to receive LENGTH bytes of data into variable SCALAR from the specified
+SOCKET filehandle.
+Returns the address of the sender, or the undefined value if there's an error.
+SCALAR will be grown or shrunk to the length actually read.
+Takes the same flags as the system call of the same name.
+.Ip "redo LABEL" 8 8
+.Ip "redo" 8
+The
+.I redo
+command restarts the loop block without evaluating the conditional again.
+The
+.I continue
+block, if any, is not executed.
+If the LABEL is omitted, the command refers to the innermost enclosing loop.
+This command is normally used by programs that want to lie to themselves
+about what was just input:
+.nf
+
+.ne 16
+ # a simpleminded Pascal comment stripper
+ # (warning: assumes no { or } in strings)
+ line: while (<STDIN>) {
+ while (s|\|({.*}.*\|){.*}|$1 \||) {}
+ s|{.*}| \||;
+ if (s|{.*| \||) {
+ $front = $_;
+ while (<STDIN>) {
+ if (\|/\|}/\|) { # end of comment?
+ s|^|$front{|;
+ redo line;
+ }
+ }
+ }
+ print;
+ }
+
+.fi
+.Ip "rename(OLDNAME,NEWNAME)" 8 2
+Changes the name of a file.
+Returns 1 for success, 0 otherwise.
+Will not work across filesystem boundaries.
+.Ip "require(EXPR)" 8 6
+.Ip "require EXPR" 8
+.Ip "require" 8
+Includes the library file specified by EXPR, or by $_ if EXPR is not supplied.
+Has semantics similar to the following subroutine:
+.nf
+
+ sub require {
+ local($filename) = @_;
+ return 1 if $INC{$filename};
+ local($realfilename,$result);
+ ITER: {
+ foreach $prefix (@INC) {
+ $realfilename = "$prefix/$filename";
+ if (-f $realfilename) {
+ $result = do $realfilename;
+ last ITER;
+ }
+ }
+ die "Can't find $filename in \e@INC";
+ }
+ die $@ if $@;
+ die "$filename did not return true value" unless $result;
+ $INC{$filename} = $realfilename;
+ $result;
+ }
+
+.fi
+Note that the file will not be included twice under the same specified name.
+The file must return true as the last statement to indicate successful
+execution of any initialization code, so it's customary to end
+such a file with \*(L"1;\*(R" unless you're sure it'll return true otherwise.
+.Ip "reset(EXPR)" 8 6
+.Ip "reset EXPR" 8
+.Ip "reset" 8
+Generally used in a
+.I continue
+block at the end of a loop to clear variables and reset ?? searches
+so that they work again.
+The expression is interpreted as a list of single characters (hyphens allowed
+for ranges).
+All variables and arrays beginning with one of those letters are reset to
+their pristine state.
+If the expression is omitted, one-match searches (?pattern?) are reset to
+match again.
+Only resets variables or searches in the current package.
+Always returns 1.
+Examples:
+.nf
+
+.ne 3
+ reset \'X\'; \h'|2i'# reset all X variables
+ reset \'a\-z\';\h'|2i'# reset lower case variables
+ reset; \h'|2i'# just reset ?? searches
+
+.fi
+Note: resetting \*(L"A\-Z\*(R" is not recommended since you'll wipe out your ARGV and ENV
+arrays.
+.Sp
+The use of reset on dbm associative arrays does not change the dbm file.
+(It does, however, flush any entries cached by perl, which may be useful if
+you are sharing the dbm file.
+Then again, maybe not.)
+.Ip "return LIST" 8 3
+Returns from a subroutine with the value specified.
+(Note that a subroutine can automatically return
+the value of the last expression evaluated.
+That's the preferred method\*(--use of an explicit
+.I return
+is a bit slower.)
+.Ip "reverse(LIST)" 8 4
+.Ip "reverse LIST" 8
+In an array context, returns an array value consisting of the elements
+of LIST in the opposite order.
+In a scalar context, returns a string value consisting of the bytes of
+the first element of LIST in the opposite order.
+.Ip "rewinddir(DIRHANDLE)" 8 5
+.Ip "rewinddir DIRHANDLE" 8
+Sets the current position to the beginning of the directory for the readdir() routine on DIRHANDLE.
+.Ip "rindex(STR,SUBSTR,POSITION)" 8 6
+.Ip "rindex(STR,SUBSTR)" 8 4
+Works just like index except that it
+returns the position of the LAST occurrence of SUBSTR in STR.
+If POSITION is specified, returns the last occurrence at or before that
+position.
+.Ip "rmdir(FILENAME)" 8 4
+.Ip "rmdir FILENAME" 8
+Deletes the directory specified by FILENAME if it is empty.
+If it succeeds it returns 1, otherwise it returns 0 and sets $! (errno).
+If FILENAME is omitted, uses $_.
+.Ip "s/PATTERN/REPLACEMENT/gieo" 8 3
+Searches a string for a pattern, and if found, replaces that pattern with the
+replacement text and returns the number of substitutions made.
+Otherwise it returns false (0).
+The \*(L"g\*(R" is optional, and if present, indicates that all occurrences
+of the pattern are to be replaced.
+The \*(L"i\*(R" is also optional, and if present, indicates that matching
+is to be done in a case-insensitive manner.
+The \*(L"e\*(R" is likewise optional, and if present, indicates that
+the replacement string is to be evaluated as an expression rather than just
+as a double-quoted string.
+Any non-alphanumeric delimiter may replace the slashes;
+if single quotes are used, no
+interpretation is done on the replacement string (the e modifier overrides
+this, however); if backquotes are used, the replacement string is a command
+to execute whose output will be used as the actual replacement text.
+If the PATTERN is delimited by bracketing quotes, the REPLACEMENT
+has its own pair of quotes, which may or may not be bracketing quotes, e.g.
+s(foo)(bar) or s<foo>/bar/.
+If no string is specified via the =~ or !~ operator,
+the $_ string is searched and modified.
+(The string specified with =~ must be a scalar variable, an array element,
+or an assignment to one of those, i.e. an lvalue.)
+If the pattern contains a $ that looks like a variable rather than an
+end-of-string test, the variable will be interpolated into the pattern at
+run-time.
+If you only want the pattern compiled once the first time the variable is
+interpolated, add an \*(L"o\*(R" at the end.
+If the PATTERN evaluates to a null string, the most recent successful
+regular expression is used instead.
+See also the section on regular expressions.
+Examples:
+.nf
+
+ s/\|\e\|bgreen\e\|b/mauve/g; # don't change wintergreen
+
+ $path \|=~ \|s|\|/usr/bin|\|/usr/local/bin|;
+
+ s/Login: $foo/Login: $bar/; # run-time pattern
+
+ ($foo = $bar) =~ s/bar/foo/;
+
+ $_ = \'abc123xyz\';
+ s/\ed+/$&*2/e; # yields \*(L'abc246xyz\*(R'
+ s/\ed+/sprintf("%5d",$&)/e; # yields \*(L'abc 246xyz\*(R'
+ s/\ew/$& x 2/eg; # yields \*(L'aabbcc 224466xxyyzz\*(R'
+
+ s/\|([^ \|]*\|) *\|([^ \|]*\|)\|/\|$2 $1/; # reverse 1st two fields
+
+.fi
+(Note the use of $ instead of \|\e\| in the last example. See section
+on regular expressions.)
+.Ip "scalar(EXPR)" 8 3
+Forces EXPR to be interpreted in a scalar context and returns the value
+of EXPR.
+.Ip "seek(FILEHANDLE,POSITION,WHENCE)" 8 3
+Randomly positions the file pointer for FILEHANDLE, just like the fseek()
+call of stdio.
+FILEHANDLE may be an expression whose value gives the name of the filehandle.
+Returns 1 upon success, 0 otherwise.
+.Ip "seekdir(DIRHANDLE,POS)" 8 3
+Sets the current position for the readdir() routine on DIRHANDLE.
+POS must be a value returned by telldir().
+Has the same caveats about possible directory compaction as the corresponding
+system library routine.
+.Ip "select(FILEHANDLE)" 8 3
+.Ip "select" 8 3
+Returns the currently selected filehandle.
+Sets the current default filehandle for output, if FILEHANDLE is supplied.
+This has two effects: first, a
+.I write
+or a
+.I print
+without a filehandle will default to this FILEHANDLE.
+Second, references to variables related to output will refer to this output
+channel.
+For example, if you have to set the top of form format for more than
+one output channel, you might do the following:
+.nf
+
+.ne 4
+ select(REPORT1);
+ $^ = \'report1_top\';
+ select(REPORT2);
+ $^ = \'report2_top\';
+
+.fi
+FILEHANDLE may be an expression whose value gives the name of the actual filehandle.
+Thus:
+.nf
+
+ $oldfh = select(STDERR); $| = 1; select($oldfh);
+
+.fi
+.Ip "select(RBITS,WBITS,EBITS,TIMEOUT)" 8 3
+This calls the select system call with the bitmasks specified, which can
+be constructed using fileno() and vec(), along these lines:
+.nf
+
+ $rin = $win = $ein = '';
+ vec($rin,fileno(STDIN),1) = 1;
+ vec($win,fileno(STDOUT),1) = 1;
+ $ein = $rin | $win;
+
+.fi
+If you want to select on many filehandles you might wish to write a subroutine:
+.nf
+
+ sub fhbits {
+ local(@fhlist) = split(' ',$_[0]);
+ local($bits);
+ for (@fhlist) {
+ vec($bits,fileno($_),1) = 1;
+ }
+ $bits;
+ }
+ $rin = &fhbits('STDIN TTY SOCK');
+
+.fi
+The usual idiom is:
+.nf
+
+ ($nfound,$timeleft) =
+ select($rout=$rin, $wout=$win, $eout=$ein, $timeout);
+
+or to block until something becomes ready:
+
+.ie t \{\
+ $nfound = select($rout=$rin, $wout=$win, $eout=$ein, undef);
+'br\}
+.el \{\
+ $nfound = select($rout=$rin, $wout=$win,
+ $eout=$ein, undef);
+'br\}
+
+.fi
+Any of the bitmasks can also be undef.
+The timeout, if specified, is in seconds, which may be fractional.
+NOTE: not all implementations are capable of returning the $timeleft.
+If not, they always return $timeleft equal to the supplied $timeout.
+.Ip "semctl(ID,SEMNUM,CMD,ARG)" 8 4
+Calls the System V IPC function semctl. If CMD is &IPC_STAT or
+&GETALL, then ARG must be a variable which will hold the returned
+semid_ds structure or semaphore value array. Returns like ioctl: the
+undefined value for error, "0 but true" for zero, or the actual return
+value otherwise.
+.Ip "semget(KEY,NSEMS,SIZE,FLAGS)" 8 4
+Calls the System V IPC function semget. Returns the semaphore id, or
+the undefined value if there is an error.
+.Ip "semop(KEY,OPSTRING)" 8 4
+Calls the System V IPC function semop to perform semaphore operations
+such as signaling and waiting. OPSTRING must be a packed array of
+semop structures. Each semop structure can be generated with
+\&'pack("sss", $semnum, $semop, $semflag)'. The number of semaphore
+operations is implied by the length of OPSTRING. Returns true if
+successful, or false if there is an error. As an example, the
+following code waits on semaphore $semnum of semaphore id $semid:
+.nf
+
+ $semop = pack("sss", $semnum, -1, 0);
+ die "Semaphore trouble: $!\en" unless semop($semid, $semop);
+
+.fi
+To signal the semaphore, replace "-1" with "1".
+.Ip "send(SOCKET,MSG,FLAGS,TO)" 8 4
+.Ip "send(SOCKET,MSG,FLAGS)" 8
+Sends a message on a socket.
+Takes the same flags as the system call of the same name.
+On unconnected sockets you must specify a destination to send TO.
+Returns the number of characters sent, or the undefined value if
+there is an error.
+.Ip "setpgrp(PID,PGRP)" 8 4
+Sets the current process group for the specified PID, 0 for the current
+process.
+Will produce a fatal error if used on a machine that doesn't implement
+setpgrp(2).
+.Ip "setpriority(WHICH,WHO,PRIORITY)" 8 4
+Sets the current priority for a process, a process group, or a user.
+(See setpriority(2).)
+Will produce a fatal error if used on a machine that doesn't implement
+setpriority(2).
+.Ip "setsockopt(SOCKET,LEVEL,OPTNAME,OPTVAL)" 8 3
+Sets the socket option requested.
+Returns undefined if there is an error.
+OPTVAL may be specified as undef if you don't want to pass an argument.
+.Ip "shift(ARRAY)" 8 6
+.Ip "shift ARRAY" 8
+.Ip "shift" 8
+Shifts the first value of the array off and returns it,
+shortening the array by 1 and moving everything down.
+If there are no elements in the array, returns the undefined value.
+If ARRAY is omitted, shifts the @ARGV array in the main program, and the @_
+array in subroutines.
+(This is determined lexically.)
+See also unshift(), push() and pop().
+Shift() and unshift() do the same thing to the left end of an array that push()
+and pop() do to the right end.
+.Ip "shmctl(ID,CMD,ARG)" 8 4
+Calls the System V IPC function shmctl. If CMD is &IPC_STAT, then ARG
+must be a variable which will hold the returned shmid_ds structure.
+Returns like ioctl: the undefined value for error, "0 but true" for
+zero, or the actual return value otherwise.
+.Ip "shmget(KEY,SIZE,FLAGS)" 8 4
+Calls the System V IPC function shmget. Returns the shared memory
+segment id, or the undefined value if there is an error.
+.Ip "shmread(ID,VAR,POS,SIZE)" 8 4
+.Ip "shmwrite(ID,STRING,POS,SIZE)" 8
+Reads or writes the System V shared memory segment ID starting at
+position POS for size SIZE by attaching to it, copying in/out, and
+detaching from it. When reading, VAR must be a variable which
+will hold the data read. When writing, if STRING is too long,
+only SIZE bytes are used; if STRING is too short, nulls are
+written to fill out SIZE bytes. Return true if successful, or
+false if there is an error.
+.Ip "shutdown(SOCKET,HOW)" 8 3
+Shuts down a socket connection in the manner indicated by HOW, which has
+the same interpretation as in the system call of the same name.
+.Ip "sin(EXPR)" 8 4
+.Ip "sin EXPR" 8
+Returns the sine of EXPR (expressed in radians).
+If EXPR is omitted, returns sine of $_.
+.Ip "sleep(EXPR)" 8 6
+.Ip "sleep EXPR" 8
+.Ip "sleep" 8
+Causes the script to sleep for EXPR seconds, or forever if no EXPR.
+May be interrupted by sending the process a SIGALRM.
+Returns the number of seconds actually slept.
+You probably cannot mix alarm() and sleep() calls, since sleep() is
+often implemented using alarm().
+.Ip "socket(SOCKET,DOMAIN,TYPE,PROTOCOL)" 8 3
+Opens a socket of the specified kind and attaches it to filehandle SOCKET.
+DOMAIN, TYPE and PROTOCOL are specified the same as for the system call
+of the same name.
+You may need to run h2ph on sys/socket.h to get the proper values handy
+in a perl library file.
+Return true if successful.
+See the example in the section on Interprocess Communication.
+.Ip "socketpair(SOCKET1,SOCKET2,DOMAIN,TYPE,PROTOCOL)" 8 3
+Creates an unnamed pair of sockets in the specified domain, of the specified
+type.
+DOMAIN, TYPE and PROTOCOL are specified the same as for the system call
+of the same name.
+If unimplemented, yields a fatal error.
+Return true if successful.
+.Ip "sort(SUBROUTINE LIST)" 8 9
+.Ip "sort(LIST)" 8
+.Ip "sort SUBROUTINE LIST" 8
+.Ip "sort BLOCK LIST" 8
+.Ip "sort LIST" 8
+Sorts the LIST and returns the sorted array value.
+Nonexistent values of arrays are stripped out.
+If SUBROUTINE or BLOCK is omitted, sorts in standard string comparison order.
+If SUBROUTINE is specified, gives the name of a subroutine that returns
+an integer less than, equal to, or greater than 0,
+depending on how the elements of the array are to be ordered.
+(The <=> and cmp operators are extremely useful in such routines.)
+SUBROUTINE may be a scalar variable name, in which case the value provides
+the name of the subroutine to use.
+In place of a SUBROUTINE name, you can provide a BLOCK as an anonymous,
+in-line sort subroutine.
+.Sp
+In the interests of efficiency the normal calling code for subroutines
+is bypassed, with the following effects: the subroutine may not be a recursive
+subroutine, and the two elements to be compared are passed into the subroutine
+not via @_ but as $a and $b (see example below).
+They are passed by reference so don't modify $a and $b.
+.Sp
+Examples:
+.nf
+
+.ne 2
+ # sort lexically
+ @articles = sort @files;
+
+.ne 2
+ # same thing, but with explicit sort routine
+ @articles = sort {$a cmp $b} @files;
+
+.ne 2
+ # same thing in reversed order
+ @articles = sort {$b cmp $a} @files;
+
+.ne 2
+ # sort numerically ascending
+ @articles = sort {$a <=> $b} @files;
+
+.ne 2
+ # sort numerically descending
+ @articles = sort {$b <=> $a} @files;
+
+.ne 5
+ # sort using explicit subroutine name
+ sub byage {
+ $age{$a} <=> $age{$b}; # presuming integers
+ }
+ @sortedclass = sort byage @class;
+
+.ne 9
+ sub reverse { $b cmp $a; }
+ @harry = (\'dog\',\'cat\',\'x\',\'Cain\',\'Abel\');
+ @george = (\'gone\',\'chased\',\'yz\',\'Punished\',\'Axed\');
+ print sort @harry;
+ # prints AbelCaincatdogx
+ print sort reverse @harry;
+ # prints xdogcatCainAbel
+ print sort @george, \'to\', @harry;
+ # prints AbelAxedCainPunishedcatchaseddoggonetoxyz
+
+.fi
+.Ip "splice(ARRAY,OFFSET,LENGTH,LIST)" 8 8
+.Ip "splice(ARRAY,OFFSET,LENGTH)" 8
+.Ip "splice(ARRAY,OFFSET)" 8
+Removes the elements designated by OFFSET and LENGTH from an array, and
+replaces them with the elements of LIST, if any.
+Returns the elements removed from the array.
+The array grows or shrinks as necessary.
+If LENGTH is omitted, removes everything from OFFSET onward.
+The following equivalencies hold (assuming $[ == 0):
+.nf
+
+ push(@a,$x,$y)\h'|3.5i'splice(@a,$#a+1,0,$x,$y)
+ pop(@a)\h'|3.5i'splice(@a,-1)
+ shift(@a)\h'|3.5i'splice(@a,0,1)
+ unshift(@a,$x,$y)\h'|3.5i'splice(@a,0,0,$x,$y)
+ $a[$x] = $y\h'|3.5i'splice(@a,$x,1,$y);
+
+Example, assuming array lengths are passed before arrays:
+
+ sub aeq { # compare two array values
+ local(@a) = splice(@_,0,shift);
+ local(@b) = splice(@_,0,shift);
+ return 0 unless @a == @b; # same len?
+ while (@a) {
+ return 0 if pop(@a) ne pop(@b);
+ }
+ return 1;
+ }
+ if (&aeq($len,@foo[1..$len],0+@bar,@bar)) { ... }
+
+.fi
+.Ip "split(/PATTERN/,EXPR,LIMIT)" 8 8
+.Ip "split(/PATTERN/,EXPR)" 8 8
+.Ip "split(/PATTERN/)" 8
+.Ip "split" 8
+Splits a string into an array of strings, and returns it.
+(If not in an array context, returns the number of fields found and splits
+into the @_ array.
+(In an array context, you can force the split into @_
+by using ?? as the pattern delimiters, but it still returns the array value.))
+If EXPR is omitted, splits the $_ string.
+If PATTERN is also omitted, splits on whitespace (/[\ \et\en]+/).
+Anything matching PATTERN is taken to be a delimiter separating the fields.
+(Note that the delimiter may be longer than one character.)
+If LIMIT is specified, splits into no more than that many fields (though it
+may split into fewer).
+If LIMIT is unspecified, trailing null fields are stripped (which
+potential users of pop() would do well to remember).
+A pattern matching the null string (not to be confused with a null pattern //,
+which is just one member of the set of patterns matching a null string)
+will split the value of EXPR into separate characters at each point it
+matches that way.
+For example:
+.nf
+
+ print join(\':\', split(/ */, \'hi there\'));
+
+.fi
+produces the output \*(L'h:i:t:h:e:r:e\*(R'.
+.Sp
+The LIMIT parameter can be used to partially split a line
+.nf
+
+ ($login, $passwd, $remainder) = split(\|/\|:\|/\|, $_, 3);
+
+.fi
+(When assigning to a list, if LIMIT is omitted, perl supplies a LIMIT one
+larger than the number of variables in the list, to avoid unnecessary work.
+For the list above LIMIT would have been 4 by default.
+In time critical applications it behooves you not to split into
+more fields than you really need.)
+.Sp
+If the PATTERN contains parentheses, additional array elements are created
+from each matching substring in the delimiter.
+.Sp
+ split(/([,-])/,"1-10,20");
+.Sp
+produces the array value
+.Sp
+ (1,'-',10,',',20)
+.Sp
+The pattern /PATTERN/ may be replaced with an expression to specify patterns
+that vary at runtime.
+(To do runtime compilation only once, use /$variable/o.)
+As a special case, specifying a space (\'\ \') will split on white space
+just as split with no arguments does, but leading white space does NOT
+produce a null first field.
+Thus, split(\'\ \') can be used to emulate
+.IR awk 's
+default behavior, whereas
+split(/\ /) will give you as many null initial fields as there are
+leading spaces.
+.Sp
+Example:
+.nf
+
+.ne 5
+ open(passwd, \'/etc/passwd\');
+ while (<passwd>) {
+.ie t \{\
+ ($login, $passwd, $uid, $gid, $gcos, $home, $shell) = split(\|/\|:\|/\|);
+'br\}
+.el \{\
+ ($login, $passwd, $uid, $gid, $gcos, $home, $shell)
+ = split(\|/\|:\|/\|);
+'br\}
+ .\|.\|.
+ }
+
+.fi
+(Note that $shell above will still have a newline on it. See chop().)
+See also
+.IR join .
+.Ip "sprintf(FORMAT,LIST)" 8 4
+Returns a string formatted by the usual printf conventions.
+The * character is not supported.
+.Ip "sqrt(EXPR)" 8 4
+.Ip "sqrt EXPR" 8
+Return the square root of EXPR.
+If EXPR is omitted, returns square root of $_.
+.Ip "srand(EXPR)" 8 4
+.Ip "srand EXPR" 8
+Sets the random number seed for the
+.I rand
+operator.
+If EXPR is omitted, does srand(time).
+.Ip "stat(FILEHANDLE)" 8 8
+.Ip "stat FILEHANDLE" 8
+.Ip "stat(EXPR)" 8
+.Ip "stat SCALARVARIABLE" 8
+Returns a 13-element array giving the statistics for a file, either the file
+opened via FILEHANDLE, or named by EXPR.
+Returns a null list if the stat fails.
+Typically used as follows:
+.nf
+
+.ne 3
+ ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
+ $atime,$mtime,$ctime,$blksize,$blocks)
+ = stat($filename);
+
+.fi
+If stat is passed the special filehandle consisting of an underline,
+no stat is done, but the current contents of the stat structure from
+the last stat or filetest are returned.
+Example:
+.nf
+
+.ne 3
+ if (-x $file && (($d) = stat(_)) && $d < 0) {
+ print "$file is executable NFS file\en";
+ }
+
+.fi
+(This only works on machines for which the device number is negative under NFS.)
+.Ip "study(SCALAR)" 8 6
+.Ip "study SCALAR" 8
+.Ip "study"
+Takes extra time to study SCALAR ($_ if unspecified) in anticipation of
+doing many pattern matches on the string before it is next modified.
+This may or may not save time, depending on the nature and number of patterns
+you are searching on, and on the distribution of character frequencies in
+the string to be searched\*(--you probably want to compare runtimes with and
+without it to see which runs faster.
+Those loops which scan for many short constant strings (including the constant
+parts of more complex patterns) will benefit most.
+You may have only one study active at a time\*(--if you study a different
+scalar the first is \*(L"unstudied\*(R".
+(The way study works is this: a linked list of every character in the string
+to be searched is made, so we know, for example, where all the \*(L'k\*(R' characters
+are.
+From each search string, the rarest character is selected, based on some
+static frequency tables constructed from some C programs and English text.
+Only those places that contain this \*(L"rarest\*(R" character are examined.)
+.Sp
+For example, here is a loop which inserts index producing entries before any line
+containing a certain pattern:
+.nf
+
+.ne 8
+ while (<>) {
+ study;
+ print ".IX foo\en" if /\ebfoo\eb/;
+ print ".IX bar\en" if /\ebbar\eb/;
+ print ".IX blurfl\en" if /\ebblurfl\eb/;
+ .\|.\|.
+ print;
+ }
+
+.fi
+In searching for /\ebfoo\eb/, only those locations in $_ that contain \*(L'f\*(R'
+will be looked at, because \*(L'f\*(R' is rarer than \*(L'o\*(R'.
+In general, this is a big win except in pathological cases.
+The only question is whether it saves you more time than it took to build
+the linked list in the first place.
+.Sp
+Note that if you have to look for strings that you don't know till runtime,
+you can build an entire loop as a string and eval that to avoid recompiling
+all your patterns all the time.
+Together with undefining $/ to input entire files as one record, this can
+be very fast, often faster than specialized programs like fgrep.
+The following scans a list of files (@files)
+for a list of words (@words), and prints out the names of those files that
+contain a match:
+.nf
+
+.ne 12
+ $search = \'while (<>) { study;\';
+ foreach $word (@words) {
+ $search .= "++\e$seen{\e$ARGV} if /\e\eb$word\e\eb/;\en";
+ }
+ $search .= "}";
+ @ARGV = @files;
+ undef $/;
+ eval $search; # this screams
+ $/ = "\en"; # put back to normal input delim
+ foreach $file (sort keys(%seen)) {
+ print $file, "\en";
+ }
+
+.fi
+.Ip "substr(EXPR,OFFSET,LEN)" 8 2
+.Ip "substr(EXPR,OFFSET)" 8 2
+Extracts a substring out of EXPR and returns it.
+First character is at offset 0, or whatever you've set $[ to.
+If OFFSET is negative, starts that far from the end of the string.
+If LEN is omitted, returns everything to the end of the string.
+You can use the substr() function as an lvalue, in which case EXPR must
+be an lvalue.
+If you assign something shorter than LEN, the string will shrink, and
+if you assign something longer than LEN, the string will grow to accommodate it.
+To keep the string the same length you may need to pad or chop your value using
+sprintf().
+.Ip "symlink(OLDFILE,NEWFILE)" 8 2
+Creates a new filename symbolically linked to the old filename.
+Returns 1 for success, 0 otherwise.
+On systems that don't support symbolic links, produces a fatal error at
+run time.
+To check for that, use eval:
+.nf
+
+ $symlink_exists = (eval \'symlink("","");\', $@ eq \'\');
+
+.fi
+.Ip "syscall(LIST)" 8 6
+.Ip "syscall LIST" 8
+Calls the system call specified as the first element of the list, passing
+the remaining elements as arguments to the system call.
+If unimplemented, produces a fatal error.
+The arguments are interpreted as follows: if a given argument is numeric,
+the argument is passed as an int.
+If not, the pointer to the string value is passed.
+You are responsible to make sure a string is pre-extended long enough
+to receive any result that might be written into a string.
+If your integer arguments are not literals and have never been interpreted
+in a numeric context, you may need to add 0 to them to force them to look
+like numbers.
+.nf
+
+ require 'syscall.ph'; # may need to run h2ph
+ syscall(&SYS_write, fileno(STDOUT), "hi there\en", 9);
+
+.fi
+.Ip "sysread(FILEHANDLE,SCALAR,LENGTH,OFFSET)" 8 5
+.Ip "sysread(FILEHANDLE,SCALAR,LENGTH)" 8 5
+Attempts to read LENGTH bytes of data into variable SCALAR from the specified
+FILEHANDLE, using the system call read(2).
+It bypasses stdio, so mixing this with other kinds of reads may cause
+confusion.
+Returns the number of bytes actually read, or undef if there was an error.
+SCALAR will be grown or shrunk to the length actually read.
+An OFFSET may be specified to place the read data at some other place
+than the beginning of the string.
+.Ip "system(LIST)" 8 6
+.Ip "system LIST" 8
+Does exactly the same thing as \*(L"exec LIST\*(R" except that a fork
+is done first, and the parent process waits for the child process to complete.
+Note that argument processing varies depending on the number of arguments.
+The return value is the exit status of the program as returned by the wait()
+call.
+To get the actual exit value divide by 256.
+See also
+.IR exec .
+.Ip "syswrite(FILEHANDLE,SCALAR,LENGTH,OFFSET)" 8 5
+.Ip "syswrite(FILEHANDLE,SCALAR,LENGTH)" 8 5
+Attempts to write LENGTH bytes of data from variable SCALAR to the specified
+FILEHANDLE, using the system call write(2).
+It bypasses stdio, so mixing this with prints may cause
+confusion.
+Returns the number of bytes actually written, or undef if there was an error.
+An OFFSET may be specified to place the read data at some other place
+than the beginning of the string.
+.Ip "tell(FILEHANDLE)" 8 6
+.Ip "tell FILEHANDLE" 8 6
+.Ip "tell" 8
+Returns the current file position for FILEHANDLE.
+FILEHANDLE may be an expression whose value gives the name of the actual
+filehandle.
+If FILEHANDLE is omitted, assumes the file last read.
+.Ip "telldir(DIRHANDLE)" 8 5
+.Ip "telldir DIRHANDLE" 8
+Returns the current position of the readdir() routines on DIRHANDLE.
+Value may be given to seekdir() to access a particular location in
+a directory.
+Has the same caveats about possible directory compaction as the corresponding
+system library routine.
+.Ip "time" 8 4
+Returns the number of non-leap seconds since 00:00:00 UTC, January 1, 1970.
+Suitable for feeding to gmtime() and localtime().
+.Ip "times" 8 4
+Returns a four-element array giving the user and system times, in seconds, for this
+process and the children of this process.
+.Sp
+ ($user,$system,$cuser,$csystem) = times;
+.Sp
+.Ip "tr/SEARCHLIST/REPLACEMENTLIST/cds" 8 5
+.Ip "y/SEARCHLIST/REPLACEMENTLIST/cds" 8
+Translates all occurrences of the characters found in the search list with
+the corresponding character in the replacement list.
+It returns the number of characters replaced or deleted.
+If no string is specified via the =~ or !~ operator,
+the $_ string is translated.
+(The string specified with =~ must be a scalar variable, an array element,
+or an assignment to one of those, i.e. an lvalue.)
+For
+.I sed
+devotees,
+.I y
+is provided as a synonym for
+.IR tr .
+If the SEARCHLIST is delimited by bracketing quotes, the REPLACEMENTLIST
+has its own pair of quotes, which may or may not be bracketing quotes, e.g.
+tr[A-Z][a-z] or tr(+-*/)/ABCD/.
+.Sp
+If the c modifier is specified, the SEARCHLIST character set is complemented.
+If the d modifier is specified, any characters specified by SEARCHLIST that
+are not found in REPLACEMENTLIST are deleted.
+(Note that this is slightly more flexible than the behavior of some
+.I tr
+programs, which delete anything they find in the SEARCHLIST, period.)
+If the s modifier is specified, sequences of characters that were translated
+to the same character are squashed down to 1 instance of the character.
+.Sp
+If the d modifier was used, the REPLACEMENTLIST is always interpreted exactly
+as specified.
+Otherwise, if the REPLACEMENTLIST is shorter than the SEARCHLIST,
+the final character is replicated till it is long enough.
+If the REPLACEMENTLIST is null, the SEARCHLIST is replicated.
+This latter is useful for counting characters in a class, or for squashing
+character sequences in a class.
+.Sp
+Examples:
+.nf
+
+ $ARGV[1] \|=~ \|y/A\-Z/a\-z/; \h'|3i'# canonicalize to lower case
+
+ $cnt = tr/*/*/; \h'|3i'# count the stars in $_
+
+ $cnt = tr/0\-9//; \h'|3i'# count the digits in $_
+
+ tr/a\-zA\-Z//s; \h'|3i'# bookkeeper \-> bokeper
+
+ ($HOST = $host) =~ tr/a\-z/A\-Z/;
+
+ y/a\-zA\-Z/ /cs; \h'|3i'# change non-alphas to single space
+
+ tr/\e200\-\e377/\e0\-\e177/;\h'|3i'# delete 8th bit
+
+.fi
+.Ip "truncate(FILEHANDLE,LENGTH)" 8 4
+.Ip "truncate(EXPR,LENGTH)" 8
+Truncates the file opened on FILEHANDLE, or named by EXPR, to the specified
+length.
+Produces a fatal error if truncate isn't implemented on your system.
+.Ip "umask(EXPR)" 8 4
+.Ip "umask EXPR" 8
+.Ip "umask" 8
+Sets the umask for the process and returns the old one.
+If EXPR is omitted, merely returns current umask.
+.Ip "undef(EXPR)" 8 6
+.Ip "undef EXPR" 8
+.Ip "undef" 8
+Undefines the value of EXPR, which must be an lvalue.
+Use only on a scalar value, an entire array, or a subroutine name (using &).
+(Undef will probably not do what you expect on most predefined variables or
+dbm array values.)
+Always returns the undefined value.
+You can omit the EXPR, in which case nothing is undefined, but you still
+get an undefined value that you could, for instance, return from a subroutine.
+Examples:
+.nf
+
+.ne 6
+ undef $foo;
+ undef $bar{'blurfl'};
+ undef @ary;
+ undef %assoc;
+ undef &mysub;
+ return (wantarray ? () : undef) if $they_blew_it;
+
+.fi
+.Ip "unlink(LIST)" 8 4
+.Ip "unlink LIST" 8
+Deletes a list of files.
+Returns the number of files successfully deleted.
+.nf
+
+.ne 2
+ $cnt = unlink \'a\', \'b\', \'c\';
+ unlink @goners;
+ unlink <*.bak>;
+
+.fi
+Note: unlink will not delete directories unless you are superuser and the
+.B \-U
+flag is supplied to
+.IR perl .
+Even if these conditions are met, be warned that unlinking a directory
+can inflict damage on your filesystem.
+Use rmdir instead.
+.Ip "unpack(TEMPLATE,EXPR)" 8 4
+Unpack does the reverse of pack: it takes a string representing
+a structure and expands it out into an array value, returning the array
+value.
+(In a scalar context, it merely returns the first value produced.)
+The TEMPLATE has the same format as in the pack function.
+Here's a subroutine that does substring:
+.nf
+
+.ne 4
+ sub substr {
+ local($what,$where,$howmuch) = @_;
+ unpack("x$where a$howmuch", $what);
+ }
+
+.ne 3
+and then there's
+
+ sub ord { unpack("c",$_[0]); }
+
+.fi
+In addition, you may prefix a field with a %<number> to indicate that
+you want a <number>-bit checksum of the items instead of the items themselves.
+Default is a 16-bit checksum.
+For example, the following computes the same number as the System V sum program:
+.nf
+
+.ne 4
+ while (<>) {
+ $checksum += unpack("%16C*", $_);
+ }
+ $checksum %= 65536;
+
+.fi
+.Ip "unshift(ARRAY,LIST)" 8 4
+Does the opposite of a
+.IR shift .
+Or the opposite of a
+.IR push ,
+depending on how you look at it.
+Prepends list to the front of the array, and returns the number of elements
+in the new array.
+.nf
+
+ unshift(ARGV, \'\-e\') unless $ARGV[0] =~ /^\-/;
+
+.fi
+.Ip "utime(LIST)" 8 2
+.Ip "utime LIST" 8 2
+Changes the access and modification times on each file of a list of files.
+The first two elements of the list must be the NUMERICAL access and
+modification times, in that order.
+Returns the number of files successfully changed.
+The inode modification time of each file is set to the current time.
+Example of a \*(L"touch\*(R" command:
+.nf
+
+.ne 3
+ #!/usr/bin/perl
+ $now = time;
+ utime $now, $now, @ARGV;
+
+.fi
+.Ip "values(ASSOC_ARRAY)" 8 6
+.Ip "values ASSOC_ARRAY" 8
+Returns a normal array consisting of all the values of the named associative
+array.
+The values are returned in an apparently random order, but it is the same order
+as either the keys() or each() function would produce on the same array.
+See also keys() and each().
+.Ip "vec(EXPR,OFFSET,BITS)" 8 2
+Treats a string as a vector of unsigned integers, and returns the value
+of the bitfield specified.
+May also be assigned to.
+BITS must be a power of two from 1 to 32.
+.Sp
+Vectors created with vec() can also be manipulated with the logical operators
+|, & and ^,
+which will assume a bit vector operation is desired when both operands are
+strings.
+This interpretation is not enabled unless there is at least one vec() in
+your program, to protect older programs.
+.Sp
+To transform a bit vector into a string or array of 0's and 1's, use these:
+.nf
+
+ $bits = unpack("b*", $vector);
+ @bits = split(//, unpack("b*", $vector));
+
+.fi
+If you know the exact length in bits, it can be used in place of the *.
+.Ip "wait" 8 6
+Waits for a child process to terminate and returns the pid of the deceased
+process, or -1 if there are no child processes.
+The status is returned in $?.
+.Ip "waitpid(PID,FLAGS)" 8 6
+Waits for a particular child process to terminate and returns the pid of the deceased
+process, or -1 if there is no such child process.
+The status is returned in $?.
+If you say
+.nf
+
+ require "sys/wait.h";
+ .\|.\|.
+ waitpid(-1,&WNOHANG);
+
+.fi
+then you can do a non-blocking wait for any process. Non-blocking wait
+is only available on machines supporting either the
+.I waitpid (2)
+or
+.I wait4 (2)
+system calls.
+However, waiting for a particular pid with FLAGS of 0 is implemented
+everywhere. (Perl emulates the system call by remembering the status
+values of processes that have exited but have not been harvested by the
+Perl script yet.)
+.Ip "wantarray" 8 4
+Returns true if the context of the currently executing subroutine
+is looking for an array value.
+Returns false if the context is looking for a scalar.
+.nf
+
+ return wantarray ? () : undef;
+
+.fi
+.Ip "warn(LIST)" 8 4
+.Ip "warn LIST" 8
+Produces a message on STDERR just like \*(L"die\*(R", but doesn't exit.
+.Ip "write(FILEHANDLE)" 8 6
+.Ip "write(EXPR)" 8
+.Ip "write" 8
+Writes a formatted record (possibly multi-line) to the specified file,
+using the format associated with that file.
+By default the format for a file is the one having the same name is the
+filehandle, but the format for the current output channel (see
+.IR select )
+may be set explicitly
+by assigning the name of the format to the $~ variable.
+.Sp
+Top of form processing is handled automatically:
+if there is insufficient room on the current page for the formatted
+record, the page is advanced by writing a form feed,
+a special top-of-page format is used
+to format the new page header, and then the record is written.
+By default the top-of-page format is the name of the filehandle with
+\*(L"_TOP\*(R" appended, but it may be dynamicallly set to the
+format of your choice by assigning the name to the $^ variable while
+the filehandle is selected.
+The number of lines remaining on the current page is in variable $-, which
+can be set to 0 to force a new page.
+.Sp
+If FILEHANDLE is unspecified, output goes to the current default output channel,
+which starts out as
+.I STDOUT
+but may be changed by the
+.I select
+operator.
+If the FILEHANDLE is an EXPR, then the expression is evaluated and the
+resulting string is used to look up the name of the FILEHANDLE at run time.
+For more on formats, see the section on formats later on.
+.Sp
+Note that write is NOT the opposite of read.
+.Sh "Precedence"
+.I Perl
+operators have the following associativity and precedence:
+.nf
+
+nonassoc\h'|1i'print printf exec system sort reverse
+\h'1.5i'chmod chown kill unlink utime die return
+left\h'|1i',
+right\h'|1i'= += \-= *= etc.
+right\h'|1i'?:
+nonassoc\h'|1i'.\|.
+left\h'|1i'||
+left\h'|1i'&&
+left\h'|1i'| ^
+left\h'|1i'&
+nonassoc\h'|1i'== != <=> eq ne cmp
+nonassoc\h'|1i'< > <= >= lt gt le ge
+nonassoc\h'|1i'chdir exit eval reset sleep rand umask
+nonassoc\h'|1i'\-r \-w \-x etc.
+left\h'|1i'<< >>
+left\h'|1i'+ \- .
+left\h'|1i'* / % x
+left\h'|1i'=~ !~
+right\h'|1i'! ~ and unary minus
+right\h'|1i'**
+nonassoc\h'|1i'++ \-\|\-
+left\h'|1i'\*(L'(\*(R'
+
+.fi
+As mentioned earlier, if any list operator (print, etc.) or
+any unary operator (chdir, etc.)
+is followed by a left parenthesis as the next token on the same line,
+the operator and arguments within parentheses are taken to
+be of highest precedence, just like a normal function call.
+Examples:
+.nf
+
+ chdir $foo || die;\h'|3i'# (chdir $foo) || die
+ chdir($foo) || die;\h'|3i'# (chdir $foo) || die
+ chdir ($foo) || die;\h'|3i'# (chdir $foo) || die
+ chdir +($foo) || die;\h'|3i'# (chdir $foo) || die
+
+but, because * is higher precedence than ||:
+
+ chdir $foo * 20;\h'|3i'# chdir ($foo * 20)
+ chdir($foo) * 20;\h'|3i'# (chdir $foo) * 20
+ chdir ($foo) * 20;\h'|3i'# (chdir $foo) * 20
+ chdir +($foo) * 20;\h'|3i'# chdir ($foo * 20)
+
+ rand 10 * 20;\h'|3i'# rand (10 * 20)
+ rand(10) * 20;\h'|3i'# (rand 10) * 20
+ rand (10) * 20;\h'|3i'# (rand 10) * 20
+ rand +(10) * 20;\h'|3i'# rand (10 * 20)
+
+.fi
+In the absence of parentheses,
+the precedence of list operators such as print, sort or chmod is
+either very high or very low depending on whether you look at the left
+side of operator or the right side of it.
+For example, in
+.nf
+
+ @ary = (1, 3, sort 4, 2);
+ print @ary; # prints 1324
+
+.fi
+the commas on the right of the sort are evaluated before the sort, but
+the commas on the left are evaluated after.
+In other words, list operators tend to gobble up all the arguments that
+follow them, and then act like a simple term with regard to the preceding
+expression.
+Note that you have to be careful with parens:
+.nf
+
+.ne 3
+ # These evaluate exit before doing the print:
+ print($foo, exit); # Obviously not what you want.
+ print $foo, exit; # Nor is this.
+
+.ne 4
+ # These do the print before evaluating exit:
+ (print $foo), exit; # This is what you want.
+ print($foo), exit; # Or this.
+ print ($foo), exit; # Or even this.
+
+Also note that
+
+ print ($foo & 255) + 1, "\en";
+
+.fi
+probably doesn't do what you expect at first glance.
+.Sh "Subroutines"
+A subroutine may be declared as follows:
+.nf
+
+ sub NAME BLOCK
+
+.fi
+.PP
+Any arguments passed to the routine come in as array @_,
+that is ($_[0], $_[1], .\|.\|.).
+The array @_ is a local array, but its values are references to the
+actual scalar parameters.
+The return value of the subroutine is the value of the last expression
+evaluated, and can be either an array value or a scalar value.
+Alternately, a return statement may be used to specify the returned value and
+exit the subroutine.
+To create local variables see the
+.I local
+operator.
+.PP
+A subroutine is called using the
+.I do
+operator or the & operator.
+.nf
+
+.ne 12
+Example:
+
+ sub MAX {
+ local($max) = pop(@_);
+ foreach $foo (@_) {
+ $max = $foo \|if \|$max < $foo;
+ }
+ $max;
+ }
+
+ .\|.\|.
+ $bestday = &MAX($mon,$tue,$wed,$thu,$fri);
+
+.ne 21
+Example:
+
+ # get a line, combining continuation lines
+ # that start with whitespace
+ sub get_line {
+ $thisline = $lookahead;
+ line: while ($lookahead = <STDIN>) {
+ if ($lookahead \|=~ \|/\|^[ \^\e\|t]\|/\|) {
+ $thisline \|.= \|$lookahead;
+ }
+ else {
+ last line;
+ }
+ }
+ $thisline;
+ }
+
+ $lookahead = <STDIN>; # get first line
+ while ($_ = do get_line(\|)) {
+ .\|.\|.
+ }
+
+.fi
+.nf
+.ne 6
+Use array assignment to a local list to name your formal arguments:
+
+ sub maybeset {
+ local($key, $value) = @_;
+ $foo{$key} = $value unless $foo{$key};
+ }
+
+.fi
+This also has the effect of turning call-by-reference into call-by-value,
+since the assignment copies the values.
+.Sp
+Subroutines may be called recursively.
+If a subroutine is called using the & form, the argument list is optional.
+If omitted, no @_ array is set up for the subroutine; the @_ array at the
+time of the call is visible to subroutine instead.
+.nf
+
+ do foo(1,2,3); # pass three arguments
+ &foo(1,2,3); # the same
+
+ do foo(); # pass a null list
+ &foo(); # the same
+ &foo; # pass no arguments\*(--more efficient
+
+.fi
+.Sh "Passing By Reference"
+Sometimes you don't want to pass the value of an array to a subroutine but
+rather the name of it, so that the subroutine can modify the global copy
+of it rather than working with a local copy.
+In perl you can refer to all the objects of a particular name by prefixing
+the name with a star: *foo.
+When evaluated, it produces a scalar value that represents all the objects
+of that name, including any filehandle, format or subroutine.
+When assigned to within a local() operation, it causes the name mentioned
+to refer to whatever * value was assigned to it.
+Example:
+.nf
+
+ sub doubleary {
+ local(*someary) = @_;
+ foreach $elem (@someary) {
+ $elem *= 2;
+ }
+ }
+ do doubleary(*foo);
+ do doubleary(*bar);
+
+.fi
+Assignment to *name is currently recommended only inside a local().
+You can actually assign to *name anywhere, but the previous referent of
+*name may be stranded forever.
+This may or may not bother you.
+.Sp
+Note that scalars are already passed by reference, so you can modify scalar
+arguments without using this mechanism by referring explicitly to the $_[nnn]
+in question.
+You can modify all the elements of an array by passing all the elements
+as scalars, but you have to use the * mechanism to push, pop or change the
+size of an array.
+The * mechanism will probably be more efficient in any case.
+.Sp
+Since a *name value contains unprintable binary data, if it is used as
+an argument in a print, or as a %s argument in a printf or sprintf, it
+then has the value '*name', just so it prints out pretty.
+.Sp
+Even if you don't want to modify an array, this mechanism is useful for
+passing multiple arrays in a single LIST, since normally the LIST mechanism
+will merge all the array values so that you can't extract out the
+individual arrays.
+.Sh "Regular Expressions"
+The patterns used in pattern matching are regular expressions such as
+those supplied in the Version 8 regexp routines.
+(In fact, the routines are derived from Henry Spencer's freely redistributable
+reimplementation of the V8 routines.)
+In addition, \ew matches an alphanumeric character (including \*(L"_\*(R") and \eW a nonalphanumeric.
+Word boundaries may be matched by \eb, and non-boundaries by \eB.
+A whitespace character is matched by \es, non-whitespace by \eS.
+A numeric character is matched by \ed, non-numeric by \eD.
+You may use \ew, \es and \ed within character classes.
+Also, \en, \er, \ef, \et and \eNNN have their normal interpretations.
+Within character classes \eb represents backspace rather than a word boundary.
+Alternatives may be separated by |.
+The bracketing construct \|(\ .\|.\|.\ \|) may also be used, in which case \e<digit>
+matches the digit'th substring.
+(Outside of the pattern, always use $ instead of \e in front of the digit.
+The scope of $<digit> (and $\`, $& and $\')
+extends to the end of the enclosing BLOCK or eval string, or to
+the next pattern match with subexpressions.
+The \e<digit> notation sometimes works outside the current pattern, but should
+not be relied upon.)
+You may have as many parentheses as you wish. If you have more than 9
+substrings, the variables $10, $11, ... refer to the corresponding
+substring. Within the pattern, \e10, \e11,
+etc. refer back to substrings if there have been at least that many left parens
+before the backreference. Otherwise (for backward compatibilty) \e10
+is the same as \e010, a backspace,
+and \e11 the same as \e011, a tab.
+And so on.
+(\e1 through \e9 are always backreferences.)
+.PP
+$+ returns whatever the last bracket match matched.
+$& returns the entire matched string.
+($0 used to return the same thing, but not any more.)
+$\` returns everything before the matched string.
+$\' returns everything after the matched string.
+Examples:
+.nf
+
+ s/\|^\|([^ \|]*\|) \|*([^ \|]*\|)\|/\|$2 $1\|/; # swap first two words
+
+.ne 5
+ if (/\|Time: \|(.\|.\|):\|(.\|.\|):\|(.\|.\|)\|/\|) {
+ $hours = $1;
+ $minutes = $2;
+ $seconds = $3;
+ }
+
+.fi
+By default, the ^ character is only guaranteed to match at the beginning
+of the string,
+the $ character only at the end (or before the newline at the end)
+and
+.I perl
+does certain optimizations with the assumption that the string contains
+only one line.
+The behavior of ^ and $ on embedded newlines will be inconsistent.
+You may, however, wish to treat a string as a multi-line buffer, such that
+the ^ will match after any newline within the string, and $ will match
+before any newline.
+At the cost of a little more overhead, you can do this by setting the variable
+$* to 1.
+Setting it back to 0 makes
+.I perl
+revert to its old behavior.
+.PP
+To facilitate multi-line substitutions, the . character never matches a newline
+(even when $* is 0).
+In particular, the following leaves a newline on the $_ string:
+.nf
+
+ $_ = <STDIN>;
+ s/.*(some_string).*/$1/;
+
+If the newline is unwanted, try one of
+
+ s/.*(some_string).*\en/$1/;
+ s/.*(some_string)[^\e000]*/$1/;
+ s/.*(some_string)(.|\en)*/$1/;
+ chop; s/.*(some_string).*/$1/;
+ /(some_string)/ && ($_ = $1);
+
+.fi
+Any item of a regular expression may be followed with digits in curly brackets
+of the form {n,m}, where n gives the minimum number of times to match the item
+and m gives the maximum.
+The form {n} is equivalent to {n,n} and matches exactly n times.
+The form {n,} matches n or more times.
+(If a curly bracket occurs in any other context, it is treated as a regular
+character.)
+The * modifier is equivalent to {0,}, the + modifier to {1,} and the ? modifier
+to {0,1}.
+There is no limit to the size of n or m, but large numbers will chew up
+more memory.
+.Sp
+You will note that all backslashed metacharacters in
+.I perl
+are alphanumeric,
+such as \eb, \ew, \en.
+Unlike some other regular expression languages, there are no backslashed
+symbols that aren't alphanumeric.
+So anything that looks like \e\e, \e(, \e), \e<, \e>, \e{, or \e} is always
+interpreted as a literal character, not a metacharacter.
+This makes it simple to quote a string that you want to use for a pattern
+but that you are afraid might contain metacharacters.
+Simply quote all the non-alphanumeric characters:
+.nf
+
+ $pattern =~ s/(\eW)/\e\e$1/g;
+
+.fi
+.Sh "Formats"
+Output record formats for use with the
+.I write
+operator may declared as follows:
+.nf
+
+.ne 3
+ format NAME =
+ FORMLIST
+ .
+
+.fi
+If name is omitted, format \*(L"STDOUT\*(R" is defined.
+FORMLIST consists of a sequence of lines, each of which may be of one of three
+types:
+.Ip 1. 4
+A comment.
+.Ip 2. 4
+A \*(L"picture\*(R" line giving the format for one output line.
+.Ip 3. 4
+An argument line supplying values to plug into a picture line.
+.PP
+Picture lines are printed exactly as they look, except for certain fields
+that substitute values into the line.
+Each picture field starts with either @ or ^.
+The @ field (not to be confused with the array marker @) is the normal
+case; ^ fields are used
+to do rudimentary multi-line text block filling.
+The length of the field is supplied by padding out the field
+with multiple <, >, or | characters to specify, respectively, left justification,
+right justification, or centering.
+As an alternate form of right justification,
+you may also use # characters (with an optional .) to specify a numeric field.
+(Use of ^ instead of @ causes the field to be blanked if undefined.)
+If any of the values supplied for these fields contains a newline, only
+the text up to the newline is printed.
+The special field @* can be used for printing multi-line values.
+It should appear by itself on a line.
+.PP
+The values are specified on the following line, in the same order as
+the picture fields.
+The values should be separated by commas.
+.PP
+Picture fields that begin with ^ rather than @ are treated specially.
+The value supplied must be a scalar variable name which contains a text
+string.
+.I Perl
+puts as much text as it can into the field, and then chops off the front
+of the string so that the next time the variable is referenced,
+more of the text can be printed.
+Normally you would use a sequence of fields in a vertical stack to print
+out a block of text.
+If you like, you can end the final field with .\|.\|., which will appear in the
+output if the text was too long to appear in its entirety.
+You can change which characters are legal to break on by changing the
+variable $: to a list of the desired characters.
+.PP
+Since use of ^ fields can produce variable length records if the text to be
+formatted is short, you can suppress blank lines by putting the tilde (~)
+character anywhere in the line.
+(Normally you should put it in the front if possible, for visibility.)
+The tilde will be translated to a space upon output.
+If you put a second tilde contiguous to the first, the line will be repeated
+until all the fields on the line are exhausted.
+(If you use a field of the @ variety, the expression you supply had better
+not give the same value every time forever!)
+.PP
+Examples:
+.nf
+.lg 0
+.cs R 25
+.ft C
+
+.ne 10
+# a report on the /etc/passwd file
+format STDOUT_TOP =
+\& Passwd File
+Name Login Office Uid Gid Home
+------------------------------------------------------------------
+\&.
+format STDOUT =
+@<<<<<<<<<<<<<<<<<< @||||||| @<<<<<<@>>>> @>>>> @<<<<<<<<<<<<<<<<<
+$name, $login, $office,$uid,$gid, $home
+\&.
+
+.ne 29
+# a report from a bug report form
+format STDOUT_TOP =
+\& Bug Reports
+@<<<<<<<<<<<<<<<<<<<<<<< @||| @>>>>>>>>>>>>>>>>>>>>>>>
+$system, $%, $date
+------------------------------------------------------------------
+\&.
+format STDOUT =
+Subject: @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+\& $subject
+Index: @<<<<<<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+\& $index, $description
+Priority: @<<<<<<<<<< Date: @<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+\& $priority, $date, $description
+From: @<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+\& $from, $description
+Assigned to: @<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+\& $programmer, $description
+\&~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+\& $description
+\&~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+\& $description
+\&~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+\& $description
+\&~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+\& $description
+\&~ ^<<<<<<<<<<<<<<<<<<<<<<<...
+\& $description
+\&.
+
+.ft R
+.cs R
+.lg
+.fi
+It is possible to intermix prints with writes on the same output channel,
+but you'll have to handle $\- (lines left on the page) yourself.
+.PP
+If you are printing lots of fields that are usually blank, you should consider
+using the reset operator between records.
+Not only is it more efficient, but it can prevent the bug of adding another
+field and forgetting to zero it.
+.Sh "Interprocess Communication"
+The IPC facilities of perl are built on the Berkeley socket mechanism.
+If you don't have sockets, you can ignore this section.
+The calls have the same names as the corresponding system calls,
+but the arguments tend to differ, for two reasons.
+First, perl file handles work differently than C file descriptors.
+Second, perl already knows the length of its strings, so you don't need
+to pass that information.
+Here is a sample client (untested):
+.nf
+
+ ($them,$port) = @ARGV;
+ $port = 2345 unless $port;
+ $them = 'localhost' unless $them;
+
+ $SIG{'INT'} = 'dokill';
+ sub dokill { kill 9,$child if $child; }
+
+ require 'sys/socket.ph';
+
+ $sockaddr = 'S n a4 x8';
+ chop($hostname = `hostname`);
+
+ ($name, $aliases, $proto) = getprotobyname('tcp');
+ ($name, $aliases, $port) = getservbyname($port, 'tcp')
+ unless $port =~ /^\ed+$/;
+.ie t \{\
+ ($name, $aliases, $type, $len, $thisaddr) = gethostbyname($hostname);
+'br\}
+.el \{\
+ ($name, $aliases, $type, $len, $thisaddr) =
+ gethostbyname($hostname);
+'br\}
+ ($name, $aliases, $type, $len, $thataddr) = gethostbyname($them);
+
+ $this = pack($sockaddr, &AF_INET, 0, $thisaddr);
+ $that = pack($sockaddr, &AF_INET, $port, $thataddr);
+
+ socket(S, &PF_INET, &SOCK_STREAM, $proto) || die "socket: $!";
+ bind(S, $this) || die "bind: $!";
+ connect(S, $that) || die "connect: $!";
+
+ select(S); $| = 1; select(stdout);
+
+ if ($child = fork) {
+ while (<>) {
+ print S;
+ }
+ sleep 3;
+ do dokill();
+ }
+ else {
+ while (<S>) {
+ print;
+ }
+ }
+
+.fi
+And here's a server:
+.nf
+
+ ($port) = @ARGV;
+ $port = 2345 unless $port;
+
+ require 'sys/socket.ph';
+
+ $sockaddr = 'S n a4 x8';
+
+ ($name, $aliases, $proto) = getprotobyname('tcp');
+ ($name, $aliases, $port) = getservbyname($port, 'tcp')
+ unless $port =~ /^\ed+$/;
+
+ $this = pack($sockaddr, &AF_INET, $port, "\e0\e0\e0\e0");
+
+ select(NS); $| = 1; select(stdout);
+
+ socket(S, &PF_INET, &SOCK_STREAM, $proto) || die "socket: $!";
+ bind(S, $this) || die "bind: $!";
+ listen(S, 5) || die "connect: $!";
+
+ select(S); $| = 1; select(stdout);
+
+ for (;;) {
+ print "Listening again\en";
+ ($addr = accept(NS,S)) || die $!;
+ print "accept ok\en";
+
+ ($af,$port,$inetaddr) = unpack($sockaddr,$addr);
+ @inetaddr = unpack('C4',$inetaddr);
+ print "$af $port @inetaddr\en";
+
+ while (<NS>) {
+ print;
+ print NS;
+ }
+ }
+
+.fi
+.Sh "Predefined Names"
+The following names have special meaning to
+.IR perl .
+I could have used alphabetic symbols for some of these, but I didn't want
+to take the chance that someone would say reset \*(L"a\-zA\-Z\*(R" and wipe them all
+out.
+You'll just have to suffer along with these silly symbols.
+Most of them have reasonable mnemonics, or analogues in one of the shells.
+.Ip $_ 8
+The default input and pattern-searching space.
+The following pairs are equivalent:
+.nf
+
+.ne 2
+ while (<>) {\|.\|.\|. # only equivalent in while!
+ while ($_ = <>) {\|.\|.\|.
+
+.ne 2
+ /\|^Subject:/
+ $_ \|=~ \|/\|^Subject:/
+
+.ne 2
+ y/a\-z/A\-Z/
+ $_ =~ y/a\-z/A\-Z/
+
+.ne 2
+ chop
+ chop($_)
+
+.fi
+(Mnemonic: underline is understood in certain operations.)
+.Ip $. 8
+The current input line number of the last filehandle that was read.
+Readonly.
+Remember that only an explicit close on the filehandle resets the line number.
+Since <> never does an explicit close, line numbers increase across ARGV files
+(but see examples under eof).
+(Mnemonic: many programs use . to mean the current line number.)
+.Ip $/ 8
+The input record separator, newline by default.
+Works like
+.IR awk 's
+RS variable, including treating blank lines as delimiters
+if set to the null string.
+You may set it to a multicharacter string to match a multi-character
+delimiter.
+Note that setting it to "\en\en" means something slightly different
+than setting it to "", if the file contains consecutive blank lines.
+Setting it to "" will treat two or more consecutive blank lines as a single
+blank line.
+Setting it to "\en\en" will blindly assume that the next input character
+belongs to the next paragraph, even if it's a newline.
+(Mnemonic: / is used to delimit line boundaries when quoting poetry.)
+.Ip $, 8
+The output field separator for the print operator.
+Ordinarily the print operator simply prints out the comma separated fields
+you specify.
+In order to get behavior more like
+.IR awk ,
+set this variable as you would set
+.IR awk 's
+OFS variable to specify what is printed between fields.
+(Mnemonic: what is printed when there is a , in your print statement.)
+.Ip $"" 8
+This is like $, except that it applies to array values interpolated into
+a double-quoted string (or similar interpreted string).
+Default is a space.
+(Mnemonic: obvious, I think.)
+.Ip $\e 8
+The output record separator for the print operator.
+Ordinarily the print operator simply prints out the comma separated fields
+you specify, with no trailing newline or record separator assumed.
+In order to get behavior more like
+.IR awk ,
+set this variable as you would set
+.IR awk 's
+ORS variable to specify what is printed at the end of the print.
+(Mnemonic: you set $\e instead of adding \en at the end of the print.
+Also, it's just like /, but it's what you get \*(L"back\*(R" from
+.IR perl .)
+.Ip $# 8
+The output format for printed numbers.
+This variable is a half-hearted attempt to emulate
+.IR awk 's
+OFMT variable.
+There are times, however, when
+.I awk
+and
+.I perl
+have differing notions of what
+is in fact numeric.
+Also, the initial value is %.20g rather than %.6g, so you need to set $#
+explicitly to get
+.IR awk 's
+value.
+(Mnemonic: # is the number sign.)
+.Ip $% 8
+The current page number of the currently selected output channel.
+(Mnemonic: % is page number in nroff.)
+.Ip $= 8
+The current page length (printable lines) of the currently selected output
+channel.
+Default is 60.
+(Mnemonic: = has horizontal lines.)
+.Ip $\- 8
+The number of lines left on the page of the currently selected output channel.
+(Mnemonic: lines_on_page \- lines_printed.)
+.Ip $~ 8
+The name of the current report format for the currently selected output
+channel.
+Default is name of the filehandle.
+(Mnemonic: brother to $^.)
+.Ip $^ 8
+The name of the current top-of-page format for the currently selected output
+channel.
+Default is name of the filehandle with \*(L"_TOP\*(R" appended.
+(Mnemonic: points to top of page.)
+.Ip $| 8
+If set to nonzero, forces a flush after every write or print on the currently
+selected output channel.
+Default is 0.
+Note that
+.I STDOUT
+will typically be line buffered if output is to the
+terminal and block buffered otherwise.
+Setting this variable is useful primarily when you are outputting to a pipe,
+such as when you are running a
+.I perl
+script under rsh and want to see the
+output as it's happening.
+(Mnemonic: when you want your pipes to be piping hot.)
+.Ip $$ 8
+The process number of the
+.I perl
+running this script.
+(Mnemonic: same as shells.)
+.Ip $? 8
+The status returned by the last pipe close, backtick (\`\`) command or
+.I system
+operator.
+Note that this is the status word returned by the wait() system
+call, so the exit value of the subprocess is actually ($? >> 8).
+$? & 255 gives which signal, if any, the process died from, and whether
+there was a core dump.
+(Mnemonic: similar to sh and ksh.)
+.Ip $& 8 4
+The string matched by the last successful pattern match
+(not counting any matches hidden
+within a BLOCK or eval enclosed by the current BLOCK).
+(Mnemonic: like & in some editors.)
+.Ip $\` 8 4
+The string preceding whatever was matched by the last successful pattern match
+(not counting any matches hidden within a BLOCK or eval enclosed by the current
+BLOCK).
+(Mnemonic: \` often precedes a quoted string.)
+.Ip $\' 8 4
+The string following whatever was matched by the last successful pattern match
+(not counting any matches hidden within a BLOCK or eval enclosed by the current
+BLOCK).
+(Mnemonic: \' often follows a quoted string.)
+Example:
+.nf
+
+.ne 3
+ $_ = \'abcdefghi\';
+ /def/;
+ print "$\`:$&:$\'\en"; # prints abc:def:ghi
+
+.fi
+.Ip $+ 8 4
+The last bracket matched by the last search pattern.
+This is useful if you don't know which of a set of alternative patterns
+matched.
+For example:
+.nf
+
+ /Version: \|(.*\|)|Revision: \|(.*\|)\|/ \|&& \|($rev = $+);
+
+.fi
+(Mnemonic: be positive and forward looking.)
+.Ip $* 8 2
+Set to 1 to do multiline matching within a string, 0 to tell
+.I perl
+that it can assume that strings contain a single line, for the purpose
+of optimizing pattern matches.
+Pattern matches on strings containing multiple newlines can produce confusing
+results when $* is 0.
+Default is 0.
+(Mnemonic: * matches multiple things.)
+Note that this variable only influences the interpretation of ^ and $.
+A literal newline can be searched for even when $* == 0.
+.Ip $0 8
+Contains the name of the file containing the
+.I perl
+script being executed.
+Assigning to $0 modifies the argument area that the ps(1) program sees.
+(Mnemonic: same as sh and ksh.)
+.Ip $<digit> 8
+Contains the subpattern from the corresponding set of parentheses in the last
+pattern matched, not counting patterns matched in nested blocks that have
+been exited already.
+(Mnemonic: like \edigit.)
+.Ip $[ 8 2
+The index of the first element in an array, and of the first character in
+a substring.
+Default is 0, but you could set it to 1 to make
+.I perl
+behave more like
+.I awk
+(or Fortran)
+when subscripting and when evaluating the index() and substr() functions.
+(Mnemonic: [ begins subscripts.)
+.Ip $] 8 2
+The string printed out when you say \*(L"perl -v\*(R".
+It can be used to determine at the beginning of a script whether the perl
+interpreter executing the script is in the right range of versions.
+If used in a numeric context, returns the version + patchlevel / 1000.
+Example:
+.nf
+
+.ne 8
+ # see if getc is available
+ ($version,$patchlevel) =
+ $] =~ /(\ed+\e.\ed+).*\enPatch level: (\ed+)/;
+ print STDERR "(No filename completion available.)\en"
+ if $version * 1000 + $patchlevel < 2016;
+
+or, used numerically,
+
+ warn "No checksumming!\en" if $] < 3.019;
+
+.fi
+(Mnemonic: Is this version of perl in the right bracket?)
+.Ip $; 8 2
+The subscript separator for multi-dimensional array emulation.
+If you refer to an associative array element as
+.nf
+ $foo{$a,$b,$c}
+
+it really means
+
+ $foo{join($;, $a, $b, $c)}
+
+But don't put
+
+ @foo{$a,$b,$c} # a slice\*(--note the @
+
+which means
+
+ ($foo{$a},$foo{$b},$foo{$c})
+
+.fi
+Default is "\e034", the same as SUBSEP in
+.IR awk .
+Note that if your keys contain binary data there might not be any safe
+value for $;.
+(Mnemonic: comma (the syntactic subscript separator) is a semi-semicolon.
+Yeah, I know, it's pretty lame, but $, is already taken for something more
+important.)
+.Ip $! 8 2
+If used in a numeric context, yields the current value of errno, with all the
+usual caveats.
+(This means that you shouldn't depend on the value of $! to be anything
+in particular unless you've gotten a specific error return indicating a
+system error.)
+If used in a string context, yields the corresponding system error string.
+You can assign to $! in order to set errno
+if, for instance, you want $! to return the string for error n, or you want
+to set the exit value for the die operator.
+(Mnemonic: What just went bang?)
+.Ip $@ 8 2
+The perl syntax error message from the last eval command.
+If null, the last eval parsed and executed correctly (although the operations
+you invoked may have failed in the normal fashion).
+(Mnemonic: Where was the syntax error \*(L"at\*(R"?)
+.Ip $< 8 2
+The real uid of this process.
+(Mnemonic: it's the uid you came FROM, if you're running setuid.)
+.Ip $> 8 2
+The effective uid of this process.
+Example:
+.nf
+
+.ne 2
+ $< = $>; # set real uid to the effective uid
+ ($<,$>) = ($>,$<); # swap real and effective uid
+
+.fi
+(Mnemonic: it's the uid you went TO, if you're running setuid.)
+Note: $< and $> can only be swapped on machines supporting setreuid().
+.Ip $( 8 2
+The real gid of this process.
+If you are on a machine that supports membership in multiple groups
+simultaneously, gives a space separated list of groups you are in.
+The first number is the one returned by getgid(), and the subsequent ones
+by getgroups(), one of which may be the same as the first number.
+(Mnemonic: parentheses are used to GROUP things.
+The real gid is the group you LEFT, if you're running setgid.)
+.Ip $) 8 2
+The effective gid of this process.
+If you are on a machine that supports membership in multiple groups
+simultaneously, gives a space separated list of groups you are in.
+The first number is the one returned by getegid(), and the subsequent ones
+by getgroups(), one of which may be the same as the first number.
+(Mnemonic: parentheses are used to GROUP things.
+The effective gid is the group that's RIGHT for you, if you're running setgid.)
+.Sp
+Note: $<, $>, $( and $) can only be set on machines that support the
+corresponding set[re][ug]id() routine.
+$( and $) can only be swapped on machines supporting setregid().
+.Ip $: 8 2
+The current set of characters after which a string may be broken to
+fill continuation fields (starting with ^) in a format.
+Default is "\ \en-", to break on whitespace or hyphens.
+(Mnemonic: a \*(L"colon\*(R" in poetry is a part of a line.)
+.Ip $^D 8 2
+The current value of the debugging flags.
+(Mnemonic: value of
+.B \-D
+switch.)
+.Ip $^F 8 2
+The maximum system file descriptor, ordinarily 2. System file descriptors
+are passed to subprocesses, while higher file descriptors are not.
+During an open, system file descriptors are preserved even if the open
+fails. Ordinary file descriptors are closed before the open is attempted.
+.Ip $^I 8 2
+The current value of the inplace-edit extension.
+Use undef to disable inplace editing.
+(Mnemonic: value of
+.B \-i
+switch.)
+.Ip $^L 8 2
+What formats output to perform a formfeed. Default is \ef.
+.Ip $^P 8 2
+The internal flag that the debugger clears so that it doesn't
+debug itself. You could conceivable disable debugging yourself
+by clearing it.
+.Ip $^T 8 2
+The time at which the script began running, in seconds since the epoch.
+The values returned by the
+.B \-M ,
+.B \-A
+and
+.B \-C
+filetests are based on this value.
+.Ip $^W 8 2
+The current value of the warning switch.
+(Mnemonic: related to the
+.B \-w
+switch.)
+.Ip $^X 8 2
+The name that Perl itself was executed as, from argv[0].
+.Ip $ARGV 8 3
+contains the name of the current file when reading from <>.
+.Ip @ARGV 8 3
+The array ARGV contains the command line arguments intended for the script.
+Note that $#ARGV is the generally number of arguments minus one, since
+$ARGV[0] is the first argument, NOT the command name.
+See $0 for the command name.
+.Ip @INC 8 3
+The array INC contains the list of places to look for
+.I perl
+scripts to be
+evaluated by the \*(L"do EXPR\*(R" command or the \*(L"require\*(R" command.
+It initially consists of the arguments to any
+.B \-I
+command line switches, followed
+by the default
+.I perl
+library, probably \*(L"/usr/local/lib/perl\*(R",
+followed by \*(L".\*(R", to represent the current directory.
+.Ip %INC 8 3
+The associative array INC contains entries for each filename that has
+been included via \*(L"do\*(R" or \*(L"require\*(R".
+The key is the filename you specified, and the value is the location of
+the file actually found.
+The \*(L"require\*(R" command uses this array to determine whether
+a given file has already been included.
+.Ip $ENV{expr} 8 2
+The associative array ENV contains your current environment.
+Setting a value in ENV changes the environment for child processes.
+.Ip $SIG{expr} 8 2
+The associative array SIG is used to set signal handlers for various signals.
+Example:
+.nf
+
+.ne 12
+ sub handler { # 1st argument is signal name
+ local($sig) = @_;
+ print "Caught a SIG$sig\-\|\-shutting down\en";
+ close(LOG);
+ exit(0);
+ }
+
+ $SIG{\'INT\'} = \'handler\';
+ $SIG{\'QUIT\'} = \'handler\';
+ .\|.\|.
+ $SIG{\'INT\'} = \'DEFAULT\'; # restore default action
+ $SIG{\'QUIT\'} = \'IGNORE\'; # ignore SIGQUIT
+
+.fi
+The SIG array only contains values for the signals actually set within
+the perl script.
+.Sh "Packages"
+Perl provides a mechanism for alternate namespaces to protect packages from
+stomping on each others variables.
+By default, a perl script starts compiling into the package known as \*(L"main\*(R".
+By use of the
+.I package
+declaration, you can switch namespaces.
+The scope of the package declaration is from the declaration itself to the end
+of the enclosing block (the same scope as the local() operator).
+Typically it would be the first declaration in a file to be included by
+the \*(L"require\*(R" operator.
+You can switch into a package in more than one place; it merely influences
+which symbol table is used by the compiler for the rest of that block.
+You can refer to variables and filehandles in other packages by prefixing
+the identifier with the package name and a single quote.
+If the package name is null, the \*(L"main\*(R" package as assumed.
+.PP
+Only identifiers starting with letters are stored in the packages symbol
+table.
+All other symbols are kept in package \*(L"main\*(R".
+In addition, the identifiers STDIN, STDOUT, STDERR, ARGV, ARGVOUT, ENV, INC
+and SIG are forced to be in package \*(L"main\*(R", even when used for
+other purposes than their built-in one.
+Note also that, if you have a package called \*(L"m\*(R", \*(L"s\*(R"
+or \*(L"y\*(R", the you can't use the qualified form of an identifier since it
+will be interpreted instead as a pattern match, a substitution
+or a translation.
+.PP
+Eval'ed strings are compiled in the package in which the eval was compiled
+in.
+(Assignments to $SIG{}, however, assume the signal handler specified is in the
+main package.
+Qualify the signal handler name if you wish to have a signal handler in
+a package.)
+For an example, examine perldb.pl in the perl library.
+It initially switches to the DB package so that the debugger doesn't interfere
+with variables in the script you are trying to debug.
+At various points, however, it temporarily switches back to the main package
+to evaluate various expressions in the context of the main package.
+.PP
+The symbol table for a package happens to be stored in the associative array
+of that name prepended with an underscore.
+The value in each entry of the associative array is
+what you are referring to when you use the *name notation.
+In fact, the following have the same effect (in package main, anyway),
+though the first is more
+efficient because it does the symbol table lookups at compile time:
+.nf
+
+.ne 2
+ local(*foo) = *bar;
+ local($_main{'foo'}) = $_main{'bar'};
+
+.fi
+You can use this to print out all the variables in a package, for instance.
+Here is dumpvar.pl from the perl library:
+.nf
+.ne 11
+ package dumpvar;
+
+ sub main'dumpvar {
+ \& ($package) = @_;
+ \& local(*stab) = eval("*_$package");
+ \& while (($key,$val) = each(%stab)) {
+ \& {
+ \& local(*entry) = $val;
+ \& if (defined $entry) {
+ \& print "\e$$key = '$entry'\en";
+ \& }
+.ne 7
+ \& if (defined @entry) {
+ \& print "\e@$key = (\en";
+ \& foreach $num ($[ .. $#entry) {
+ \& print " $num\et'",$entry[$num],"'\en";
+ \& }
+ \& print ")\en";
+ \& }
+.ne 10
+ \& if ($key ne "_$package" && defined %entry) {
+ \& print "\e%$key = (\en";
+ \& foreach $key (sort keys(%entry)) {
+ \& print " $key\et'",$entry{$key},"'\en";
+ \& }
+ \& print ")\en";
+ \& }
+ \& }
+ \& }
+ }
+
+.fi
+Note that, even though the subroutine is compiled in package dumpvar, the
+name of the subroutine is qualified so that its name is inserted into package
+\*(L"main\*(R".
+.Sh "Style"
+Each programmer will, of course, have his or her own preferences in regards
+to formatting, but there are some general guidelines that will make your
+programs easier to read.
+.Ip 1. 4 4
+Just because you CAN do something a particular way doesn't mean that
+you SHOULD do it that way.
+.I Perl
+is designed to give you several ways to do anything, so consider picking
+the most readable one.
+For instance
+
+ open(FOO,$foo) || die "Can't open $foo: $!";
+
+is better than
+
+ die "Can't open $foo: $!" unless open(FOO,$foo);
+
+because the second way hides the main point of the statement in a
+modifier.
+On the other hand
+
+ print "Starting analysis\en" if $verbose;
+
+is better than
+
+ $verbose && print "Starting analysis\en";
+
+since the main point isn't whether the user typed -v or not.
+.Sp
+Similarly, just because an operator lets you assume default arguments
+doesn't mean that you have to make use of the defaults.
+The defaults are there for lazy systems programmers writing one-shot
+programs.
+If you want your program to be readable, consider supplying the argument.
+.Sp
+Along the same lines, just because you
+.I can
+omit parentheses in many places doesn't mean that you ought to:
+.nf
+
+ return print reverse sort num values array;
+ return print(reverse(sort num (values(%array))));
+
+.fi
+When in doubt, parenthesize.
+At the very least it will let some poor schmuck bounce on the % key in vi.
+.Sp
+Even if you aren't in doubt, consider the mental welfare of the person who
+has to maintain the code after you, and who will probably put parens in
+the wrong place.
+.Ip 2. 4 4
+Don't go through silly contortions to exit a loop at the top or the
+bottom, when
+.I perl
+provides the "last" operator so you can exit in the middle.
+Just outdent it a little to make it more visible:
+.nf
+
+.ne 7
+ line:
+ for (;;) {
+ statements;
+ last line if $foo;
+ next line if /^#/;
+ statements;
+ }
+
+.fi
+.Ip 3. 4 4
+Don't be afraid to use loop labels\*(--they're there to enhance readability as
+well as to allow multi-level loop breaks.
+See last example.
+.Ip 4. 4 4
+For portability, when using features that may not be implemented on every
+machine, test the construct in an eval to see if it fails.
+If you know what version or patchlevel a particular feature was implemented,
+you can test $] to see if it will be there.
+.Ip 5. 4 4
+Choose mnemonic identifiers.
+.Ip 6. 4 4
+Be consistent.
+.Sh "Debugging"
+If you invoke
+.I perl
+with a
+.B \-d
+switch, your script will be run under a debugging monitor.
+It will halt before the first executable statement and ask you for a
+command, such as:
+.Ip "h" 12 4
+Prints out a help message.
+.Ip "T" 12 4
+Stack trace.
+.Ip "s" 12 4
+Single step.
+Executes until it reaches the beginning of another statement.
+.Ip "n" 12 4
+Next.
+Executes over subroutine calls, until it reaches the beginning of the
+next statement.
+.Ip "f" 12 4
+Finish.
+Executes statements until it has finished the current subroutine.
+.Ip "c" 12 4
+Continue.
+Executes until the next breakpoint is reached.
+.Ip "c line" 12 4
+Continue to the specified line.
+Inserts a one-time-only breakpoint at the specified line.
+.Ip "<CR>" 12 4
+Repeat last n or s.
+.Ip "l min+incr" 12 4
+List incr+1 lines starting at min.
+If min is omitted, starts where last listing left off.
+If incr is omitted, previous value of incr is used.
+.Ip "l min-max" 12 4
+List lines in the indicated range.
+.Ip "l line" 12 4
+List just the indicated line.
+.Ip "l" 12 4
+List next window.
+.Ip "-" 12 4
+List previous window.
+.Ip "w line" 12 4
+List window around line.
+.Ip "l subname" 12 4
+List subroutine.
+If it's a long subroutine it just lists the beginning.
+Use \*(L"l\*(R" to list more.
+.Ip "/pattern/" 12 4
+Regular expression search forward for pattern; the final / is optional.
+.Ip "?pattern?" 12 4
+Regular expression search backward for pattern; the final ? is optional.
+.Ip "L" 12 4
+List lines that have breakpoints or actions.
+.Ip "S" 12 4
+Lists the names of all subroutines.
+.Ip "t" 12 4
+Toggle trace mode on or off.
+.Ip "b line condition" 12 4
+Set a breakpoint.
+If line is omitted, sets a breakpoint on the
+line that is about to be executed.
+If a condition is specified, it is evaluated each time the statement is
+reached and a breakpoint is taken only if the condition is true.
+Breakpoints may only be set on lines that begin an executable statement.
+.Ip "b subname condition" 12 4
+Set breakpoint at first executable line of subroutine.
+.Ip "d line" 12 4
+Delete breakpoint.
+If line is omitted, deletes the breakpoint on the
+line that is about to be executed.
+.Ip "D" 12 4
+Delete all breakpoints.
+.Ip "a line command" 12 4
+Set an action for line.
+A multi-line command may be entered by backslashing the newlines.
+.Ip "A" 12 4
+Delete all line actions.
+.Ip "< command" 12 4
+Set an action to happen before every debugger prompt.
+A multi-line command may be entered by backslashing the newlines.
+.Ip "> command" 12 4
+Set an action to happen after the prompt when you've just given a command
+to return to executing the script.
+A multi-line command may be entered by backslashing the newlines.
+.Ip "V package" 12 4
+List all variables in package.
+Default is main package.
+.Ip "! number" 12 4
+Redo a debugging command.
+If number is omitted, redoes the previous command.
+.Ip "! -number" 12 4
+Redo the command that was that many commands ago.
+.Ip "H -number" 12 4
+Display last n commands.
+Only commands longer than one character are listed.
+If number is omitted, lists them all.
+.Ip "q or ^D" 12 4
+Quit.
+.Ip "command" 12 4
+Execute command as a perl statement.
+A missing semicolon will be supplied.
+.Ip "p expr" 12 4
+Same as \*(L"print DB'OUT expr\*(R".
+The DB'OUT filehandle is opened to /dev/tty, regardless of where STDOUT
+may be redirected to.
+.PP
+If you want to modify the debugger, copy perldb.pl from the perl library
+to your current directory and modify it as necessary.
+(You'll also have to put -I. on your command line.)
+You can do some customization by setting up a .perldb file which contains
+initialization code.
+For instance, you could make aliases like these:
+.nf
+
+ $DB'alias{'len'} = 's/^len(.*)/p length($1)/';
+ $DB'alias{'stop'} = 's/^stop (at|in)/b/';
+ $DB'alias{'.'} =
+ 's/^\e./p "\e$DB\e'sub(\e$DB\e'line):\et",\e$DB\e'line[\e$DB\e'line]/';
+
+.fi
+.Sh "Setuid Scripts"
+.I Perl
+is designed to make it easy to write secure setuid and setgid scripts.
+Unlike shells, which are based on multiple substitution passes on each line
+of the script,
+.I perl
+uses a more conventional evaluation scheme with fewer hidden \*(L"gotchas\*(R".
+Additionally, since the language has more built-in functionality, it
+has to rely less upon external (and possibly untrustworthy) programs to
+accomplish its purposes.
+.PP
+In an unpatched 4.2 or 4.3bsd kernel, setuid scripts are intrinsically
+insecure, but this kernel feature can be disabled.
+If it is,
+.I perl
+can emulate the setuid and setgid mechanism when it notices the otherwise
+useless setuid/gid bits on perl scripts.
+If the kernel feature isn't disabled,
+.I perl
+will complain loudly that your setuid script is insecure.
+You'll need to either disable the kernel setuid script feature, or put
+a C wrapper around the script.
+.PP
+When perl is executing a setuid script, it takes special precautions to
+prevent you from falling into any obvious traps.
+(In some ways, a perl script is more secure than the corresponding
+C program.)
+Any command line argument, environment variable, or input is marked as
+\*(L"tainted\*(R", and may not be used, directly or indirectly, in any
+command that invokes a subshell, or in any command that modifies files,
+directories or processes.
+Any variable that is set within an expression that has previously referenced
+a tainted value also becomes tainted (even if it is logically impossible
+for the tainted value to influence the variable).
+For example:
+.nf
+
+.ne 5
+ $foo = shift; # $foo is tainted
+ $bar = $foo,\'bar\'; # $bar is also tainted
+ $xxx = <>; # Tainted
+ $path = $ENV{\'PATH\'}; # Tainted, but see below
+ $abc = \'abc\'; # Not tainted
+
+.ne 4
+ system "echo $foo"; # Insecure
+ system "/bin/echo", $foo; # Secure (doesn't use sh)
+ system "echo $bar"; # Insecure
+ system "echo $abc"; # Insecure until PATH set
+
+.ne 5
+ $ENV{\'PATH\'} = \'/bin:/usr/bin\';
+ $ENV{\'IFS\'} = \'\' if $ENV{\'IFS\'} ne \'\';
+
+ $path = $ENV{\'PATH\'}; # Not tainted
+ system "echo $abc"; # Is secure now!
+
+.ne 5
+ open(FOO,"$foo"); # OK
+ open(FOO,">$foo"); # Not OK
+
+ open(FOO,"echo $foo|"); # Not OK, but...
+ open(FOO,"-|") || exec \'echo\', $foo; # OK
+
+ $zzz = `echo $foo`; # Insecure, zzz tainted
+
+ unlink $abc,$foo; # Insecure
+ umask $foo; # Insecure
+
+.ne 3
+ exec "echo $foo"; # Insecure
+ exec "echo", $foo; # Secure (doesn't use sh)
+ exec "sh", \'-c\', $foo; # Considered secure, alas
+
+.fi
+The taintedness is associated with each scalar value, so some elements
+of an array can be tainted, and others not.
+.PP
+If you try to do something insecure, you will get a fatal error saying
+something like \*(L"Insecure dependency\*(R" or \*(L"Insecure PATH\*(R".
+Note that you can still write an insecure system call or exec,
+but only by explicitly doing something like the last example above.
+You can also bypass the tainting mechanism by referencing
+subpatterns\*(--\c
+.I perl
+presumes that if you reference a substring using $1, $2, etc, you knew
+what you were doing when you wrote the pattern:
+.nf
+
+ $ARGV[0] =~ /^\-P(\ew+)$/;
+ $printer = $1; # Not tainted
+
+.fi
+This is fairly secure since \ew+ doesn't match shell metacharacters.
+Use of .+ would have been insecure, but
+.I perl
+doesn't check for that, so you must be careful with your patterns.
+This is the ONLY mechanism for untainting user supplied filenames if you
+want to do file operations on them (unless you make $> equal to $<).
+.PP
+It's also possible to get into trouble with other operations that don't care
+whether they use tainted values.
+Make judicious use of the file tests in dealing with any user-supplied
+filenames.
+When possible, do opens and such after setting $> = $<.
+.I Perl
+doesn't prevent you from opening tainted filenames for reading, so be
+careful what you print out.
+The tainting mechanism is intended to prevent stupid mistakes, not to remove
+the need for thought.
+.SH ENVIRONMENT
+.Ip HOME 12 4
+Used if chdir has no argument.
+.Ip LOGDIR 12 4
+Used if chdir has no argument and HOME is not set.
+.Ip PATH 12 4
+Used in executing subprocesses, and in finding the script if \-S
+is used.
+.Ip PERLLIB 12 4
+A colon-separated list of directories in which to look for Perl library
+files before looking in the standard library and the current directory.
+.Ip PERLDB 12 4
+The command used to get the debugger code. If unset, uses
+.br
+
+ require 'perldb.pl'
+
+.PP
+Apart from these,
+.I perl
+uses no other environment variables, except to make them available
+to the script being executed, and to child processes.
+However, scripts running setuid would do well to execute the following lines
+before doing anything else, just to keep people honest:
+.nf
+
+.ne 3
+ $ENV{\'PATH\'} = \'/bin:/usr/bin\'; # or whatever you need
+ $ENV{\'SHELL\'} = \'/bin/sh\' if $ENV{\'SHELL\'} ne \'\';
+ $ENV{\'IFS\'} = \'\' if $ENV{\'IFS\'} ne \'\';
+
+.fi
+.SH AUTHOR
+Larry Wall <lwall@netlabs.com>
+.br
+MS-DOS port by Diomidis Spinellis <dds@cc.ic.ac.uk>
+.SH FILES
+/tmp/perl\-eXXXXXX temporary file for
+.B \-e
+commands.
+.SH SEE ALSO
+a2p awk to perl translator
+.br
+s2p sed to perl translator
+.SH DIAGNOSTICS
+Compilation errors will tell you the line number of the error, with an
+indication of the next token or token type that was to be examined.
+(In the case of a script passed to
+.I perl
+via
+.B \-e
+switches, each
+.B \-e
+is counted as one line.)
+.PP
+Setuid scripts have additional constraints that can produce error messages
+such as \*(L"Insecure dependency\*(R".
+See the section on setuid scripts.
+.SH TRAPS
+Accustomed
+.IR awk
+users should take special note of the following:
+.Ip * 4 2
+Semicolons are required after all simple statements in
+.I perl
+(except at the end of a block).
+Newline is not a statement delimiter.
+.Ip * 4 2
+Curly brackets are required on ifs and whiles.
+.Ip * 4 2
+Variables begin with $ or @ in
+.IR perl .
+.Ip * 4 2
+Arrays index from 0 unless you set $[.
+Likewise string positions in substr() and index().
+.Ip * 4 2
+You have to decide whether your array has numeric or string indices.
+.Ip * 4 2
+Associative array values do not spring into existence upon mere reference.
+.Ip * 4 2
+You have to decide whether you want to use string or numeric comparisons.
+.Ip * 4 2
+Reading an input line does not split it for you. You get to split it yourself
+to an array.
+And the
+.I split
+operator has different arguments.
+.Ip * 4 2
+The current input line is normally in $_, not $0.
+It generally does not have the newline stripped.
+($0 is the name of the program executed.)
+.Ip * 4 2
+$<digit> does not refer to fields\*(--it refers to substrings matched by the last
+match pattern.
+.Ip * 4 2
+The
+.I print
+statement does not add field and record separators unless you set
+$, and $\e.
+.Ip * 4 2
+You must open your files before you print to them.
+.Ip * 4 2
+The range operator is \*(L".\|.\*(R", not comma.
+(The comma operator works as in C.)
+.Ip * 4 2
+The match operator is \*(L"=~\*(R", not \*(L"~\*(R".
+(\*(L"~\*(R" is the one's complement operator, as in C.)
+.Ip * 4 2
+The exponentiation operator is \*(L"**\*(R", not \*(L"^\*(R".
+(\*(L"^\*(R" is the XOR operator, as in C.)
+.Ip * 4 2
+The concatenation operator is \*(L".\*(R", not the null string.
+(Using the null string would render \*(L"/pat/ /pat/\*(R" unparsable,
+since the third slash would be interpreted as a division operator\*(--the
+tokener is in fact slightly context sensitive for operators like /, ?, and <.
+And in fact, . itself can be the beginning of a number.)
+.Ip * 4 2
+.IR Next ,
+.I exit
+and
+.I continue
+work differently.
+.Ip * 4 2
+The following variables work differently
+.nf
+
+ Awk \h'|2.5i'Perl
+ ARGC \h'|2.5i'$#ARGV
+ ARGV[0] \h'|2.5i'$0
+ FILENAME\h'|2.5i'$ARGV
+ FNR \h'|2.5i'$. \- something
+ FS \h'|2.5i'(whatever you like)
+ NF \h'|2.5i'$#Fld, or some such
+ NR \h'|2.5i'$.
+ OFMT \h'|2.5i'$#
+ OFS \h'|2.5i'$,
+ ORS \h'|2.5i'$\e
+ RLENGTH \h'|2.5i'length($&)
+ RS \h'|2.5i'$/
+ RSTART \h'|2.5i'length($\`)
+ SUBSEP \h'|2.5i'$;
+
+.fi
+.Ip * 4 2
+When in doubt, run the
+.I awk
+construct through a2p and see what it gives you.
+.PP
+Cerebral C programmers should take note of the following:
+.Ip * 4 2
+Curly brackets are required on ifs and whiles.
+.Ip * 4 2
+You should use \*(L"elsif\*(R" rather than \*(L"else if\*(R"
+.Ip * 4 2
+.I Break
+and
+.I continue
+become
+.I last
+and
+.IR next ,
+respectively.
+.Ip * 4 2
+There's no switch statement.
+.Ip * 4 2
+Variables begin with $ or @ in
+.IR perl .
+.Ip * 4 2
+Printf does not implement *.
+.Ip * 4 2
+Comments begin with #, not /*.
+.Ip * 4 2
+You can't take the address of anything.
+.Ip * 4 2
+ARGV must be capitalized.
+.Ip * 4 2
+The \*(L"system\*(R" calls link, unlink, rename, etc. return nonzero for success, not 0.
+.Ip * 4 2
+Signal handlers deal with signal names, not numbers.
+.PP
+Seasoned
+.I sed
+programmers should take note of the following:
+.Ip * 4 2
+Backreferences in substitutions use $ rather than \e.
+.Ip * 4 2
+The pattern matching metacharacters (, ), and | do not have backslashes in front.
+.Ip * 4 2
+The range operator is .\|. rather than comma.
+.PP
+Sharp shell programmers should take note of the following:
+.Ip * 4 2
+The backtick operator does variable interpretation without regard to the
+presence of single quotes in the command.
+.Ip * 4 2
+The backtick operator does no translation of the return value, unlike csh.
+.Ip * 4 2
+Shells (especially csh) do several levels of substitution on each command line.
+.I Perl
+does substitution only in certain constructs such as double quotes,
+backticks, angle brackets and search patterns.
+.Ip * 4 2
+Shells interpret scripts a little bit at a time.
+.I Perl
+compiles the whole program before executing it.
+.Ip * 4 2
+The arguments are available via @ARGV, not $1, $2, etc.
+.Ip * 4 2
+The environment is not automatically made available as variables.
+.SH ERRATA\0AND\0ADDENDA
+The Perl book,
+.I Programming\0Perl ,
+has the following omissions and goofs.
+.PP
+On page 5, the examples which read
+.nf
+
+ eval "/usr/bin/perl
+
+should read
+
+ eval "exec /usr/bin/perl
+
+.fi
+.PP
+On page 195, the equivalent to the System V sum program only works for
+very small files. To do larger files, use
+.nf
+
+ undef $/;
+ $checksum = unpack("%32C*",<>) % 32767;
+
+.fi
+.PP
+The descriptions of alarm and sleep refer to signal SIGALARM. These
+should refer to SIGALRM.
+.PP
+The
+.B \-0
+switch to set the initial value of $/ was added to Perl after the book
+went to press.
+.PP
+The
+.B \-l
+switch now does automatic line ending processing.
+.PP
+The qx// construct is now a synonym for backticks.
+.PP
+$0 may now be assigned to set the argument displayed by
+.I ps (1).
+.PP
+The new @###.## format was omitted accidentally from the description
+on formats.
+.PP
+It wasn't known at press time that s///ee caused multiple evaluations of
+the replacement expression. This is to be construed as a feature.
+.PP
+(LIST) x $count now does array replication.
+.PP
+There is now no limit on the number of parentheses in a regular expression.
+.PP
+In double-quote context, more escapes are supported: \ee, \ea, \ex1b, \ec[,
+\el, \eL, \eu, \eU, \eE. The latter five control up/lower case translation.
+.PP
+The
+.B $/
+variable may now be set to a multi-character delimiter.
+.PP
+There is now a g modifier on ordinary pattern matching that causes it
+to iterate through a string finding multiple matches.
+.PP
+All of the $^X variables are new except for $^T.
+.PP
+The default top-of-form format for FILEHANDLE is now FILEHANDLE_TOP rather
+than top.
+.PP
+The eval {} and sort {} constructs were added in version 4.018.
+.PP
+The v and V (little-endian) template options for pack and unpack were
+added in 4.019.
+.SH BUGS
+.PP
+.I Perl
+is at the mercy of your machine's definitions of various operations
+such as type casting, atof() and sprintf().
+.PP
+If your stdio requires an seek or eof between reads and writes on a particular
+stream, so does
+.IR perl .
+(This doesn't apply to sysread() and syswrite().)
+.PP
+While none of the built-in data types have any arbitrary size limits (apart
+from memory size), there are still a few arbitrary limits:
+a given identifier may not be longer than 255 characters,
+and no component of your PATH may be longer than 255 if you use \-S.
+A regular expression may not compile to more than 32767 bytes internally.
+.PP
+.I Perl
+actually stands for Pathologically Eclectic Rubbish Lister, but don't tell
+anyone I said that.
+.rn }` ''
diff --git a/gnu/usr.bin/perl/perl/perl.c b/gnu/usr.bin/perl/perl/perl.c
new file mode 100644
index 0000000..a963db8
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/perl.c
@@ -0,0 +1,1449 @@
+char rcsid[] = "$RCSfile: perl.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:37 $\nPatch level: ###\n";
+/*
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: perl.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:37 nate
+ * PERL!
+ *
+ * Revision 4.0.1.8 1993/02/05 19:39:30 lwall
+ * patch36: the taintanyway code wasn't tainting anyway
+ * patch36: Malformed cmd links core dump apparently fixed
+ *
+ * Revision 4.0.1.7 92/06/08 14:50:39 lwall
+ * patch20: PERLLIB now supports multiple directories
+ * patch20: running taintperl explicitly now does checks even if $< == $>
+ * patch20: -e 'cmd' no longer fails silently if /tmp runs out of space
+ * patch20: perl -P now uses location of sed determined by Configure
+ * patch20: form feed for formats is now specifiable via $^L
+ * patch20: paragraph mode now skips extra newlines automatically
+ * patch20: eval "1 #comment" didn't work
+ * patch20: couldn't require . files
+ * patch20: semantic compilation errors didn't abort execution
+ *
+ * Revision 4.0.1.6 91/11/11 16:38:45 lwall
+ * patch19: default arg for shift was wrong after first subroutine definition
+ * patch19: op/regexp.t failed from missing arg to bcmp()
+ *
+ * Revision 4.0.1.5 91/11/05 18:03:32 lwall
+ * patch11: random cleanup
+ * patch11: $0 was being truncated at times
+ * patch11: cppstdin now installed outside of source directory
+ * patch11: -P didn't allow use of #elif or #undef
+ * patch11: prepared for ctype implementations that don't define isascii()
+ * patch11: added eval {}
+ * patch11: eval confused by string containing null
+ *
+ * Revision 4.0.1.4 91/06/10 01:23:07 lwall
+ * patch10: perl -v printed incorrect copyright notice
+ *
+ * Revision 4.0.1.3 91/06/07 11:40:18 lwall
+ * patch4: changed old $^P to $^X
+ *
+ * Revision 4.0.1.2 91/06/07 11:26:16 lwall
+ * patch4: new copyright notice
+ * patch4: added $^P variable to control calling of perldb routines
+ * patch4: added $^F variable to specify maximum system fd, default 2
+ * patch4: debugger lost track of lines in eval
+ *
+ * Revision 4.0.1.1 91/04/11 17:49:05 lwall
+ * patch1: fixed undefined environ problem
+ *
+ * Revision 4.0 91/03/20 01:37:44 lwall
+ * 4.0 baseline.
+ *
+ */
+
+/*SUPPRESS 560*/
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "perly.h"
+#include "patchlevel.h"
+
+char *getenv();
+
+#ifdef IAMSUID
+#ifndef DOSUID
+#define DOSUID
+#endif
+#endif
+
+#ifdef SETUID_SCRIPTS_ARE_SECURE_NOW
+#ifdef DOSUID
+#undef DOSUID
+#endif
+#endif
+
+static char* moreswitches();
+static void incpush();
+static char* cddir;
+static bool minus_c;
+static char patchlevel[6];
+static char *nrs = "\n";
+static int nrschar = '\n'; /* final char of rs, or 0777 if none */
+static int nrslen = 1;
+
+main(argc,argv,env)
+register int argc;
+register char **argv;
+register char **env;
+{
+ register STR *str;
+ register char *s;
+ char *scriptname;
+ char *getenv();
+ bool dosearch = FALSE;
+#ifdef DOSUID
+ char *validarg = "";
+#endif
+
+#ifdef SETUID_SCRIPTS_ARE_SECURE_NOW
+#ifdef IAMSUID
+#undef IAMSUID
+ fatal("suidperl is no longer needed since the kernel can now execute\n\
+setuid perl scripts securely.\n");
+#endif
+#endif
+
+ origargv = argv;
+ origargc = argc;
+ origenviron = environ;
+ uid = (int)getuid();
+ euid = (int)geteuid();
+ gid = (int)getgid();
+ egid = (int)getegid();
+ sprintf(patchlevel,"%3.3s%2.2d", index(rcsid,'4'), PATCHLEVEL);
+#ifdef MSDOS
+ /*
+ * There is no way we can refer to them from Perl so close them to save
+ * space. The other alternative would be to provide STDAUX and STDPRN
+ * filehandles.
+ */
+ (void)fclose(stdaux);
+ (void)fclose(stdprn);
+#endif
+ if (do_undump) {
+ origfilename = savestr(argv[0]);
+ do_undump = 0;
+ loop_ptr = -1; /* start label stack again */
+ goto just_doit;
+ }
+#ifdef TAINT
+#ifndef DOSUID
+ if (uid == euid && gid == egid)
+ taintanyway = TRUE; /* running taintperl explicitly */
+#endif
+#endif
+ (void)sprintf(index(rcsid,'#'), "%d\n", PATCHLEVEL);
+ linestr = Str_new(65,80);
+ str_nset(linestr,"",0);
+ str = str_make("",0); /* first used for -I flags */
+ curstash = defstash = hnew(0);
+ curstname = str_make("main",4);
+ stab_xhash(stabent("_main",TRUE)) = defstash;
+ defstash->tbl_name = "main";
+ incstab = hadd(aadd(stabent("INC",TRUE)));
+ incstab->str_pok |= SP_MULTI;
+ for (argc--,argv++; argc > 0; argc--,argv++) {
+ if (argv[0][0] != '-' || !argv[0][1])
+ break;
+#ifdef DOSUID
+ if (*validarg)
+ validarg = " PHOOEY ";
+ else
+ validarg = argv[0];
+#endif
+ s = argv[0]+1;
+ reswitch:
+ switch (*s) {
+ case '0':
+ case 'a':
+ case 'c':
+ case 'd':
+ case 'D':
+ case 'i':
+ case 'l':
+ case 'n':
+ case 'p':
+ case 'u':
+ case 'U':
+ case 'v':
+ case 'w':
+ if (s = moreswitches(s))
+ goto reswitch;
+ break;
+
+ case 'e':
+#ifdef TAINT
+ if (euid != uid || egid != gid)
+ fatal("No -e allowed in setuid scripts");
+#endif
+ if (!e_fp) {
+ e_tmpname = savestr(TMPPATH);
+ (void)mktemp(e_tmpname);
+ if (!*e_tmpname)
+ fatal("Can't mktemp()");
+ e_fp = fopen(e_tmpname,"w");
+ if (!e_fp)
+ fatal("Cannot open temporary file");
+ }
+ if (argv[1]) {
+ fputs(argv[1],e_fp);
+ argc--,argv++;
+ }
+ (void)putc('\n', e_fp);
+ break;
+ case 'I':
+#ifdef TAINT
+ if (euid != uid || egid != gid)
+ fatal("No -I allowed in setuid scripts");
+#endif
+ str_cat(str,"-");
+ str_cat(str,s);
+ str_cat(str," ");
+ if (*++s) {
+ (void)apush(stab_array(incstab),str_make(s,0));
+ }
+ else if (argv[1]) {
+ (void)apush(stab_array(incstab),str_make(argv[1],0));
+ str_cat(str,argv[1]);
+ argc--,argv++;
+ str_cat(str," ");
+ }
+ break;
+ case 'P':
+#ifdef TAINT
+ if (euid != uid || egid != gid)
+ fatal("No -P allowed in setuid scripts");
+#endif
+ preprocess = TRUE;
+ s++;
+ goto reswitch;
+ case 's':
+#ifdef TAINT
+ if (euid != uid || egid != gid)
+ fatal("No -s allowed in setuid scripts");
+#endif
+ doswitches = TRUE;
+ s++;
+ goto reswitch;
+ case 'S':
+#ifdef TAINT
+ if (euid != uid || egid != gid)
+ fatal("No -S allowed in setuid scripts");
+#endif
+ dosearch = TRUE;
+ s++;
+ goto reswitch;
+ case 'x':
+ doextract = TRUE;
+ s++;
+ if (*s)
+ cddir = savestr(s);
+ break;
+ case '-':
+ argc--,argv++;
+ goto switch_end;
+ case 0:
+ break;
+ default:
+ fatal("Unrecognized switch: -%s",s);
+ }
+ }
+ switch_end:
+ scriptname = argv[0];
+ if (e_fp) {
+ if (fflush(e_fp) || ferror(e_fp) || fclose(e_fp))
+ fatal("Can't write to temp file for -e: %s", strerror(errno));
+ argc++,argv--;
+ scriptname = e_tmpname;
+ }
+
+#ifdef DOSISH
+#define PERLLIB_SEP ';'
+#else
+#define PERLLIB_SEP ':'
+#endif
+#ifndef TAINT /* Can't allow arbitrary PERLLIB in setuid script */
+ incpush(getenv("PERLLIB"));
+#endif /* TAINT */
+
+#ifndef PRIVLIB
+#define PRIVLIB "/usr/local/lib/perl"
+#endif
+ incpush(PRIVLIB);
+ (void)apush(stab_array(incstab),str_make(".",1));
+
+ str_set(&str_no,No);
+ str_set(&str_yes,Yes);
+
+ /* open script */
+
+ if (scriptname == Nullch)
+#ifdef MSDOS
+ {
+ if ( isatty(fileno(stdin)) )
+ moreswitches("v");
+ scriptname = "-";
+ }
+#else
+ scriptname = "-";
+#endif
+ if (dosearch && !index(scriptname, '/') && (s = getenv("PATH"))) {
+ char *xfound = Nullch, *xfailed = Nullch;
+ int len;
+
+ bufend = s + strlen(s);
+ while (*s) {
+#ifndef DOSISH
+ s = cpytill(tokenbuf,s,bufend,':',&len);
+#else
+#ifdef atarist
+ for (len = 0; *s && *s != ',' && *s != ';'; tokenbuf[len++] = *s++);
+ tokenbuf[len] = '\0';
+#else
+ for (len = 0; *s && *s != ';'; tokenbuf[len++] = *s++);
+ tokenbuf[len] = '\0';
+#endif
+#endif
+ if (*s)
+ s++;
+#ifndef DOSISH
+ if (len && tokenbuf[len-1] != '/')
+#else
+#ifdef atarist
+ if (len && ((tokenbuf[len-1] != '\\') && (tokenbuf[len-1] != '/')))
+#else
+ if (len && tokenbuf[len-1] != '\\')
+#endif
+#endif
+ (void)strcat(tokenbuf+len,"/");
+ (void)strcat(tokenbuf+len,scriptname);
+#ifdef DEBUGGING
+ if (debug & 1)
+ fprintf(stderr,"Looking for %s\n",tokenbuf);
+#endif
+ if (stat(tokenbuf,&statbuf) < 0) /* not there? */
+ continue;
+ if (S_ISREG(statbuf.st_mode)
+ && cando(S_IRUSR,TRUE,&statbuf) && cando(S_IXUSR,TRUE,&statbuf)) {
+ xfound = tokenbuf; /* bingo! */
+ break;
+ }
+ if (!xfailed)
+ xfailed = savestr(tokenbuf);
+ }
+ if (!xfound)
+ fatal("Can't execute %s", xfailed ? xfailed : scriptname );
+ if (xfailed)
+ Safefree(xfailed);
+ scriptname = savestr(xfound);
+ }
+
+ fdpid = anew(Nullstab); /* for remembering popen pids by fd */
+ pidstatus = hnew(COEFFSIZE);/* for remembering status of dead pids */
+
+ origfilename = savestr(scriptname);
+ curcmd->c_filestab = fstab(origfilename);
+ if (strEQ(origfilename,"-"))
+ scriptname = "";
+ if (preprocess) {
+ char *cpp = CPPSTDIN;
+
+ if (strEQ(cpp,"cppstdin"))
+ sprintf(tokenbuf, "%s/%s", SCRIPTDIR, cpp);
+ else
+ sprintf(tokenbuf, "%s", cpp);
+ str_cat(str,"-I");
+ str_cat(str,PRIVLIB);
+#ifdef MSDOS
+ (void)sprintf(buf, "\
+sed %s -e \"/^[^#]/b\" \
+ -e \"/^#[ ]*include[ ]/b\" \
+ -e \"/^#[ ]*define[ ]/b\" \
+ -e \"/^#[ ]*if[ ]/b\" \
+ -e \"/^#[ ]*ifdef[ ]/b\" \
+ -e \"/^#[ ]*ifndef[ ]/b\" \
+ -e \"/^#[ ]*else/b\" \
+ -e \"/^#[ ]*elif[ ]/b\" \
+ -e \"/^#[ ]*undef[ ]/b\" \
+ -e \"/^#[ ]*endif/b\" \
+ -e \"s/^#.*//\" \
+ %s | %s -C %s %s",
+ (doextract ? "-e \"1,/^#/d\n\"" : ""),
+#else
+ (void)sprintf(buf, "\
+%s %s -e '/^[^#]/b' \
+ -e '/^#[ ]*include[ ]/b' \
+ -e '/^#[ ]*define[ ]/b' \
+ -e '/^#[ ]*if[ ]/b' \
+ -e '/^#[ ]*ifdef[ ]/b' \
+ -e '/^#[ ]*ifndef[ ]/b' \
+ -e '/^#[ ]*else/b' \
+ -e '/^#[ ]*elif[ ]/b' \
+ -e '/^#[ ]*undef[ ]/b' \
+ -e '/^#[ ]*endif/b' \
+ -e 's/^[ ]*#.*//' \
+ %s | %s -C %s %s",
+#ifdef LOC_SED
+ LOC_SED,
+#else
+ "sed",
+#endif
+ (doextract ? "-e '1,/^#/d\n'" : ""),
+#endif
+ scriptname, tokenbuf, str_get(str), CPPMINUS);
+#ifdef DEBUGGING
+ if (debug & 64) {
+ fputs(buf,stderr);
+ fputs("\n",stderr);
+ }
+#endif
+ doextract = FALSE;
+#ifdef IAMSUID /* actually, this is caught earlier */
+ if (euid != uid && !euid) { /* if running suidperl */
+#ifdef HAS_SETEUID
+ (void)seteuid(uid); /* musn't stay setuid root */
+#else
+#ifdef HAS_SETREUID
+ (void)setreuid(-1, uid);
+#else
+ setuid(uid);
+#endif
+#endif
+ if (geteuid() != uid)
+ fatal("Can't do seteuid!\n");
+ }
+#endif /* IAMSUID */
+ rsfp = mypopen(buf,"r");
+ }
+ else if (!*scriptname) {
+#ifdef TAINT
+ if (euid != uid || egid != gid)
+ fatal("Can't take set-id script from stdin");
+#endif
+ rsfp = stdin;
+ }
+ else
+ rsfp = fopen(scriptname,"r");
+ if ((FILE*)rsfp == Nullfp) {
+#ifdef DOSUID
+#ifndef IAMSUID /* in case script is not readable before setuid */
+ if (euid && stat(stab_val(curcmd->c_filestab)->str_ptr,&statbuf) >= 0 &&
+ statbuf.st_mode & (S_ISUID|S_ISGID)) {
+ (void)sprintf(buf, "%s/sperl%s", BIN, patchlevel);
+ execv(buf, origargv); /* try again */
+ fatal("Can't do setuid\n");
+ }
+#endif
+#endif
+ fatal("Can't open perl script \"%s\": %s\n",
+ stab_val(curcmd->c_filestab)->str_ptr, strerror(errno));
+ }
+ str_free(str); /* free -I directories */
+ str = Nullstr;
+
+ /* do we need to emulate setuid on scripts? */
+
+ /* This code is for those BSD systems that have setuid #! scripts disabled
+ * in the kernel because of a security problem. Merely defining DOSUID
+ * in perl will not fix that problem, but if you have disabled setuid
+ * scripts in the kernel, this will attempt to emulate setuid and setgid
+ * on scripts that have those now-otherwise-useless bits set. The setuid
+ * root version must be called suidperl or sperlN.NNN. If regular perl
+ * discovers that it has opened a setuid script, it calls suidperl with
+ * the same argv that it had. If suidperl finds that the script it has
+ * just opened is NOT setuid root, it sets the effective uid back to the
+ * uid. We don't just make perl setuid root because that loses the
+ * effective uid we had before invoking perl, if it was different from the
+ * uid.
+ *
+ * DOSUID must be defined in both perl and suidperl, and IAMSUID must
+ * be defined in suidperl only. suidperl must be setuid root. The
+ * Configure script will set this up for you if you want it.
+ *
+ * There is also the possibility of have a script which is running
+ * set-id due to a C wrapper. We want to do the TAINT checks
+ * on these set-id scripts, but don't want to have the overhead of
+ * them in normal perl, and can't use suidperl because it will lose
+ * the effective uid info, so we have an additional non-setuid root
+ * version called taintperl or tperlN.NNN that just does the TAINT checks.
+ */
+
+#ifdef DOSUID
+ if (fstat(fileno(rsfp),&statbuf) < 0) /* normal stat is insecure */
+ fatal("Can't stat script \"%s\"",origfilename);
+ if (statbuf.st_mode & (S_ISUID|S_ISGID)) {
+ int len;
+
+#ifdef IAMSUID
+#ifndef HAS_SETREUID
+ /* On this access check to make sure the directories are readable,
+ * there is actually a small window that the user could use to make
+ * filename point to an accessible directory. So there is a faint
+ * chance that someone could execute a setuid script down in a
+ * non-accessible directory. I don't know what to do about that.
+ * But I don't think it's too important. The manual lies when
+ * it says access() is useful in setuid programs.
+ */
+ if (access(stab_val(curcmd->c_filestab)->str_ptr,1)) /*double check*/
+ fatal("Permission denied");
+#else
+ /* If we can swap euid and uid, then we can determine access rights
+ * with a simple stat of the file, and then compare device and
+ * inode to make sure we did stat() on the same file we opened.
+ * Then we just have to make sure he or she can execute it.
+ */
+ {
+ struct stat tmpstatbuf;
+
+ if (setreuid(euid,uid) < 0 || getuid() != euid || geteuid() != uid)
+ fatal("Can't swap uid and euid"); /* really paranoid */
+ if (stat(stab_val(curcmd->c_filestab)->str_ptr,&tmpstatbuf) < 0)
+ fatal("Permission denied"); /* testing full pathname here */
+ if (tmpstatbuf.st_dev != statbuf.st_dev ||
+ tmpstatbuf.st_ino != statbuf.st_ino) {
+ (void)fclose(rsfp);
+ if (rsfp = mypopen("/bin/mail root","w")) { /* heh, heh */
+ fprintf(rsfp,
+"User %d tried to run dev %d ino %d in place of dev %d ino %d!\n\
+(Filename of set-id script was %s, uid %d gid %d.)\n\nSincerely,\nperl\n",
+ uid,tmpstatbuf.st_dev, tmpstatbuf.st_ino,
+ statbuf.st_dev, statbuf.st_ino,
+ stab_val(curcmd->c_filestab)->str_ptr,
+ statbuf.st_uid, statbuf.st_gid);
+ (void)mypclose(rsfp);
+ }
+ fatal("Permission denied\n");
+ }
+ if (setreuid(uid,euid) < 0 || getuid() != uid || geteuid() != euid)
+ fatal("Can't reswap uid and euid");
+ if (!cando(S_IXUSR,FALSE,&statbuf)) /* can real uid exec? */
+ fatal("Permission denied\n");
+ }
+#endif /* HAS_SETREUID */
+#endif /* IAMSUID */
+
+ if (!S_ISREG(statbuf.st_mode))
+ fatal("Permission denied");
+ if (statbuf.st_mode & S_IWOTH)
+ fatal("Setuid/gid script is writable by world");
+ doswitches = FALSE; /* -s is insecure in suid */
+ curcmd->c_line++;
+ if (fgets(tokenbuf,sizeof tokenbuf, rsfp) == Nullch ||
+ strnNE(tokenbuf,"#!",2) ) /* required even on Sys V */
+ fatal("No #! line");
+ s = tokenbuf+2;
+ if (*s == ' ') s++;
+ while (!isSPACE(*s)) s++;
+ if (strnNE(s-4,"perl",4) && strnNE(s-9,"perl",4)) /* sanity check */
+ fatal("Not a perl script");
+ while (*s == ' ' || *s == '\t') s++;
+ /*
+ * #! arg must be what we saw above. They can invoke it by
+ * mentioning suidperl explicitly, but they may not add any strange
+ * arguments beyond what #! says if they do invoke suidperl that way.
+ */
+ len = strlen(validarg);
+ if (strEQ(validarg," PHOOEY ") ||
+ strnNE(s,validarg,len) || !isSPACE(s[len]))
+ fatal("Args must match #! line");
+
+#ifndef IAMSUID
+ if (euid != uid && (statbuf.st_mode & S_ISUID) &&
+ euid == statbuf.st_uid)
+ if (!do_undump)
+ fatal("YOU HAVEN'T DISABLED SET-ID SCRIPTS IN THE KERNEL YET!\n\
+FIX YOUR KERNEL, PUT A C WRAPPER AROUND THIS SCRIPT, OR USE -u AND UNDUMP!\n");
+#endif /* IAMSUID */
+
+ if (euid) { /* oops, we're not the setuid root perl */
+ (void)fclose(rsfp);
+#ifndef IAMSUID
+ (void)sprintf(buf, "%s/sperl%s", BIN, patchlevel);
+ execv(buf, origargv); /* try again */
+#endif
+ fatal("Can't do setuid\n");
+ }
+
+ if (statbuf.st_mode & S_ISGID && statbuf.st_gid != egid) {
+#ifdef HAS_SETEGID
+ (void)setegid(statbuf.st_gid);
+#else
+#ifdef HAS_SETREGID
+ (void)setregid((GIDTYPE)-1,statbuf.st_gid);
+#else
+ setgid(statbuf.st_gid);
+#endif
+#endif
+ if (getegid() != statbuf.st_gid)
+ fatal("Can't do setegid!\n");
+ }
+ if (statbuf.st_mode & S_ISUID) {
+ if (statbuf.st_uid != euid)
+#ifdef HAS_SETEUID
+ (void)seteuid(statbuf.st_uid); /* all that for this */
+#else
+#ifdef HAS_SETREUID
+ (void)setreuid((UIDTYPE)-1,statbuf.st_uid);
+#else
+ setuid(statbuf.st_uid);
+#endif
+#endif
+ if (geteuid() != statbuf.st_uid)
+ fatal("Can't do seteuid!\n");
+ }
+ else if (uid) { /* oops, mustn't run as root */
+#ifdef HAS_SETEUID
+ (void)seteuid((UIDTYPE)uid);
+#else
+#ifdef HAS_SETREUID
+ (void)setreuid((UIDTYPE)-1,(UIDTYPE)uid);
+#else
+ setuid((UIDTYPE)uid);
+#endif
+#endif
+ if (geteuid() != uid)
+ fatal("Can't do seteuid!\n");
+ }
+ uid = (int)getuid();
+ euid = (int)geteuid();
+ gid = (int)getgid();
+ egid = (int)getegid();
+ if (!cando(S_IXUSR,TRUE,&statbuf))
+ fatal("Permission denied\n"); /* they can't do this */
+ }
+#ifdef IAMSUID
+ else if (preprocess)
+ fatal("-P not allowed for setuid/setgid script\n");
+ else
+ fatal("Script is not setuid/setgid in suidperl\n");
+#else
+#ifndef TAINT /* we aren't taintperl or suidperl */
+ /* script has a wrapper--can't run suidperl or we lose euid */
+ else if (euid != uid || egid != gid) {
+ (void)fclose(rsfp);
+ (void)sprintf(buf, "%s/tperl%s", BIN, patchlevel);
+ execv(buf, origargv); /* try again */
+ fatal("Can't run setuid script with taint checks");
+ }
+#endif /* TAINT */
+#endif /* IAMSUID */
+#else /* !DOSUID */
+#ifndef TAINT /* we aren't taintperl or suidperl */
+ if (euid != uid || egid != gid) { /* (suidperl doesn't exist, in fact) */
+#ifndef SETUID_SCRIPTS_ARE_SECURE_NOW
+ fstat(fileno(rsfp),&statbuf); /* may be either wrapped or real suid */
+ if ((euid != uid && euid == statbuf.st_uid && statbuf.st_mode & S_ISUID)
+ ||
+ (egid != gid && egid == statbuf.st_gid && statbuf.st_mode & S_ISGID)
+ )
+ if (!do_undump)
+ fatal("YOU HAVEN'T DISABLED SET-ID SCRIPTS IN THE KERNEL YET!\n\
+FIX YOUR KERNEL, PUT A C WRAPPER AROUND THIS SCRIPT, OR USE -u AND UNDUMP!\n");
+#endif /* SETUID_SCRIPTS_ARE_SECURE_NOW */
+ /* not set-id, must be wrapped */
+ (void)fclose(rsfp);
+ (void)sprintf(buf, "%s/tperl%s", BIN, patchlevel);
+ execv(buf, origargv); /* try again */
+ fatal("Can't run setuid script with taint checks");
+ }
+#endif /* TAINT */
+#endif /* DOSUID */
+
+#if !defined(IAMSUID) && !defined(TAINT)
+
+ /* skip forward in input to the real script? */
+
+ while (doextract) {
+ if ((s = str_gets(linestr, rsfp, 0)) == Nullch)
+ fatal("No Perl script found in input\n");
+ if (*s == '#' && s[1] == '!' && instr(s,"perl")) {
+ ungetc('\n',rsfp); /* to keep line count right */
+ doextract = FALSE;
+ if (s = instr(s,"perl -")) {
+ s += 6;
+ /*SUPPRESS 530*/
+ while (s = moreswitches(s)) ;
+ }
+ if (cddir && chdir(cddir) < 0)
+ fatal("Can't chdir to %s",cddir);
+ }
+ }
+#endif /* !defined(IAMSUID) && !defined(TAINT) */
+
+ defstab = stabent("_",TRUE);
+
+ subname = str_make("main",4);
+ if (perldb) {
+ debstash = hnew(0);
+ stab_xhash(stabent("_DB",TRUE)) = debstash;
+ curstash = debstash;
+ dbargs = stab_xarray(aadd((tmpstab = stabent("args",TRUE))));
+ tmpstab->str_pok |= SP_MULTI;
+ dbargs->ary_flags = 0;
+ DBstab = stabent("DB",TRUE);
+ DBstab->str_pok |= SP_MULTI;
+ DBline = stabent("dbline",TRUE);
+ DBline->str_pok |= SP_MULTI;
+ DBsub = hadd(tmpstab = stabent("sub",TRUE));
+ tmpstab->str_pok |= SP_MULTI;
+ DBsingle = stab_val((tmpstab = stabent("single",TRUE)));
+ tmpstab->str_pok |= SP_MULTI;
+ DBtrace = stab_val((tmpstab = stabent("trace",TRUE)));
+ tmpstab->str_pok |= SP_MULTI;
+ DBsignal = stab_val((tmpstab = stabent("signal",TRUE)));
+ tmpstab->str_pok |= SP_MULTI;
+ curstash = defstash;
+ }
+
+ /* init tokener */
+
+ bufend = bufptr = str_get(linestr);
+
+ savestack = anew(Nullstab); /* for saving non-local values */
+ stack = anew(Nullstab); /* for saving non-local values */
+ stack->ary_flags = 0; /* not a real array */
+ afill(stack,63); afill(stack,-1); /* preextend stack */
+ afill(savestack,63); afill(savestack,-1);
+
+ /* now parse the script */
+
+ error_count = 0;
+ if (yyparse() || error_count) {
+ if (minus_c)
+ fatal("%s had compilation errors.\n", origfilename);
+ else {
+ fatal("Execution of %s aborted due to compilation errors.\n",
+ origfilename);
+ }
+ }
+
+ New(50,loop_stack,128,struct loop);
+#ifdef DEBUGGING
+ if (debug) {
+ New(51,debname,128,char);
+ New(52,debdelim,128,char);
+ }
+#endif
+ curstash = defstash;
+
+ preprocess = FALSE;
+ if (e_fp) {
+ e_fp = Nullfp;
+ (void)UNLINK(e_tmpname);
+ }
+
+ /* initialize everything that won't change if we undump */
+
+ if (sigstab = stabent("SIG",allstabs)) {
+ sigstab->str_pok |= SP_MULTI;
+ (void)hadd(sigstab);
+ }
+
+ magicalize("!#?^~=-%.+&*()<>,\\/[|`':\004\t\020\024\027\006");
+ userinit(); /* in case linked C routines want magical variables */
+
+ amperstab = stabent("&",allstabs);
+ leftstab = stabent("`",allstabs);
+ rightstab = stabent("'",allstabs);
+ sawampersand = (amperstab || leftstab || rightstab);
+ if (tmpstab = stabent(":",allstabs))
+ str_set(stab_val(tmpstab),chopset);
+ if (tmpstab = stabent("\024",allstabs))
+ time(&basetime);
+
+ /* these aren't necessarily magical */
+ if (tmpstab = stabent("\014",allstabs)) {
+ str_set(stab_val(tmpstab),"\f");
+ formfeed = stab_val(tmpstab);
+ }
+ if (tmpstab = stabent(";",allstabs))
+ str_set(STAB_STR(tmpstab),"\034");
+ if (tmpstab = stabent("]",allstabs)) {
+ str = STAB_STR(tmpstab);
+ str_set(str,rcsid);
+ str->str_u.str_nval = atof(patchlevel);
+ str->str_nok = 1;
+ }
+ str_nset(stab_val(stabent("\"", TRUE)), " ", 1);
+
+ stdinstab = stabent("STDIN",TRUE);
+ stdinstab->str_pok |= SP_MULTI;
+ if (!stab_io(stdinstab))
+ stab_io(stdinstab) = stio_new();
+ stab_io(stdinstab)->ifp = stdin;
+ tmpstab = stabent("stdin",TRUE);
+ stab_io(tmpstab) = stab_io(stdinstab);
+ tmpstab->str_pok |= SP_MULTI;
+
+ tmpstab = stabent("STDOUT",TRUE);
+ tmpstab->str_pok |= SP_MULTI;
+ if (!stab_io(tmpstab))
+ stab_io(tmpstab) = stio_new();
+ stab_io(tmpstab)->ofp = stab_io(tmpstab)->ifp = stdout;
+ defoutstab = tmpstab;
+ tmpstab = stabent("stdout",TRUE);
+ stab_io(tmpstab) = stab_io(defoutstab);
+ tmpstab->str_pok |= SP_MULTI;
+
+ curoutstab = stabent("STDERR",TRUE);
+ curoutstab->str_pok |= SP_MULTI;
+ if (!stab_io(curoutstab))
+ stab_io(curoutstab) = stio_new();
+ stab_io(curoutstab)->ofp = stab_io(curoutstab)->ifp = stderr;
+ tmpstab = stabent("stderr",TRUE);
+ stab_io(tmpstab) = stab_io(curoutstab);
+ tmpstab->str_pok |= SP_MULTI;
+ curoutstab = defoutstab; /* switch back to STDOUT */
+
+ statname = Str_new(66,0); /* last filename we did stat on */
+
+ /* now that script is parsed, we can modify record separator */
+
+ rs = nrs;
+ rslen = nrslen;
+ rschar = nrschar;
+ rspara = (nrslen == 2);
+ str_nset(stab_val(stabent("/", TRUE)), rs, rslen);
+
+ if (do_undump)
+ my_unexec();
+
+ just_doit: /* come here if running an undumped a.out */
+ argc--,argv++; /* skip name of script */
+ if (doswitches) {
+ for (; argc > 0 && **argv == '-'; argc--,argv++) {
+ if (argv[0][1] == '-') {
+ argc--,argv++;
+ break;
+ }
+ if (s = index(argv[0], '=')) {
+ *s++ = '\0';
+ str_set(stab_val(stabent(argv[0]+1,TRUE)),s);
+ }
+ else
+ str_numset(stab_val(stabent(argv[0]+1,TRUE)),(double)1.0);
+ }
+ }
+#ifdef TAINT
+ tainted = 1;
+#endif
+ if (tmpstab = stabent("0",allstabs)) {
+ str_set(stab_val(tmpstab),origfilename);
+ magicname("0", Nullch, 0);
+ }
+ if (tmpstab = stabent("\030",allstabs))
+ str_set(stab_val(tmpstab),origargv[0]);
+ if (argvstab = stabent("ARGV",allstabs)) {
+ argvstab->str_pok |= SP_MULTI;
+ (void)aadd(argvstab);
+ aclear(stab_array(argvstab));
+ for (; argc > 0; argc--,argv++) {
+ (void)apush(stab_array(argvstab),str_make(argv[0],0));
+ }
+ }
+#ifdef TAINT
+ (void) stabent("ENV",TRUE); /* must test PATH and IFS */
+#endif
+ if (envstab = stabent("ENV",allstabs)) {
+ envstab->str_pok |= SP_MULTI;
+ (void)hadd(envstab);
+ hclear(stab_hash(envstab), FALSE);
+ if (env != environ)
+ environ[0] = Nullch;
+ for (; *env; env++) {
+ if (!(s = index(*env,'=')))
+ continue;
+ *s++ = '\0';
+ str = str_make(s--,0);
+ str_magic(str, envstab, 'E', *env, s - *env);
+ (void)hstore(stab_hash(envstab), *env, s - *env, str, 0);
+ *s = '=';
+ }
+ }
+#ifdef TAINT
+ tainted = 0;
+#endif
+ if (tmpstab = stabent("$",allstabs))
+ str_numset(STAB_STR(tmpstab),(double)getpid());
+
+ if (dowarn) {
+ stab_check('A','Z');
+ stab_check('a','z');
+ }
+
+ if (setjmp(top_env)) /* sets goto_targ on longjump */
+ loop_ptr = -1; /* start label stack again */
+
+#ifdef DEBUGGING
+ if (debug & 1024)
+ dump_all();
+ if (debug)
+ fprintf(stderr,"\nEXECUTING...\n\n");
+#endif
+
+ if (minus_c) {
+ fprintf(stderr,"%s syntax OK\n", origfilename);
+ exit(0);
+ }
+
+ /* do it */
+
+ (void) cmd_exec(main_root,G_SCALAR,-1);
+
+ if (goto_targ)
+ fatal("Can't find label \"%s\"--aborting",goto_targ);
+ exit(0);
+ /* NOTREACHED */
+}
+
+void
+magicalize(list)
+register char *list;
+{
+ char sym[2];
+
+ sym[1] = '\0';
+ while (*sym = *list++)
+ magicname(sym, Nullch, 0);
+}
+
+void
+magicname(sym,name,namlen)
+char *sym;
+char *name;
+int namlen;
+{
+ register STAB *stab;
+
+ if (stab = stabent(sym,allstabs)) {
+ stab_flags(stab) = SF_VMAGIC;
+ str_magic(stab_val(stab), stab, 0, name, namlen);
+ }
+}
+
+static void
+incpush(p)
+char *p;
+{
+ char *s;
+
+ if (!p)
+ return;
+
+ /* Break at all separators */
+ while (*p) {
+ /* First, skip any consecutive separators */
+ while ( *p == PERLLIB_SEP ) {
+ /* Uncomment the next line for PATH semantics */
+ /* (void)apush(stab_array(incstab), str_make(".", 1)); */
+ p++;
+ }
+ if ( (s = index(p, PERLLIB_SEP)) != Nullch ) {
+ (void)apush(stab_array(incstab), str_make(p, (int)(s - p)));
+ p = s + 1;
+ } else {
+ (void)apush(stab_array(incstab), str_make(p, 0));
+ break;
+ }
+ }
+}
+
+void
+savelines(array, str)
+ARRAY *array;
+STR *str;
+{
+ register char *s = str->str_ptr;
+ register char *send = str->str_ptr + str->str_cur;
+ register char *t;
+ register int line = 1;
+
+ while (s && s < send) {
+ STR *tmpstr = Str_new(85,0);
+
+ t = index(s, '\n');
+ if (t)
+ t++;
+ else
+ t = send;
+
+ str_nset(tmpstr, s, t - s);
+ astore(array, line++, tmpstr);
+ s = t;
+ }
+}
+
+/* this routine is in perl.c by virtue of being sort of an alternate main() */
+
+int
+do_eval(str,optype,stash,savecmd,gimme,arglast)
+STR *str;
+int optype;
+HASH *stash;
+int savecmd;
+int gimme;
+int *arglast;
+{
+ STR **st = stack->ary_array;
+ int retval;
+ CMD *myroot = Nullcmd;
+ ARRAY *ar;
+ int i;
+ CMD * VOLATILE oldcurcmd = curcmd;
+ VOLATILE int oldtmps_base = tmps_base;
+ VOLATILE int oldsave = savestack->ary_fill;
+ VOLATILE int oldperldb = perldb;
+ SPAT * VOLATILE oldspat = curspat;
+ SPAT * VOLATILE oldlspat = lastspat;
+ static char *last_eval = Nullch;
+ static long last_elen = 0;
+ static CMD *last_root = Nullcmd;
+ VOLATILE int sp = arglast[0];
+ char *specfilename;
+ char *tmpfilename;
+ int parsing = 1;
+
+ tmps_base = tmps_max;
+ if (curstash != stash) {
+ (void)savehptr(&curstash);
+ curstash = stash;
+ }
+ str_set(stab_val(stabent("@",TRUE)),"");
+ if (curcmd->c_line == 0) /* don't debug debugger... */
+ perldb = FALSE;
+ curcmd = &compiling;
+ if (optype == O_EVAL) { /* normal eval */
+ curcmd->c_filestab = fstab("(eval)");
+ curcmd->c_line = 1;
+ str_sset(linestr,str);
+ str_cat(linestr,";\n;\n"); /* be kind to them */
+ if (perldb)
+ savelines(stab_xarray(curcmd->c_filestab), linestr);
+ }
+ else {
+ if (last_root && !in_eval) {
+ Safefree(last_eval);
+ last_eval = Nullch;
+ cmd_free(last_root);
+ last_root = Nullcmd;
+ }
+ specfilename = str_get(str);
+ str_set(linestr,"");
+ if (optype == O_REQUIRE && &str_undef !=
+ hfetch(stab_hash(incstab), specfilename, strlen(specfilename), 0)) {
+ curcmd = oldcurcmd;
+ tmps_base = oldtmps_base;
+ st[++sp] = &str_yes;
+ perldb = oldperldb;
+ return sp;
+ }
+ tmpfilename = savestr(specfilename);
+ if (*tmpfilename == '/' ||
+ (*tmpfilename == '.' &&
+ (tmpfilename[1] == '/' ||
+ (tmpfilename[1] == '.' && tmpfilename[2] == '/'))))
+ {
+ rsfp = fopen(tmpfilename,"r");
+ }
+ else {
+ ar = stab_array(incstab);
+ for (i = 0; i <= ar->ary_fill; i++) {
+ (void)sprintf(buf, "%s/%s",
+ str_get(afetch(ar,i,TRUE)), specfilename);
+ rsfp = fopen(buf,"r");
+ if (rsfp) {
+ char *s = buf;
+
+ if (*s == '.' && s[1] == '/')
+ s += 2;
+ Safefree(tmpfilename);
+ tmpfilename = savestr(s);
+ break;
+ }
+ }
+ }
+ curcmd->c_filestab = fstab(tmpfilename);
+ Safefree(tmpfilename);
+ tmpfilename = Nullch;
+ if (!rsfp) {
+ curcmd = oldcurcmd;
+ tmps_base = oldtmps_base;
+ if (optype == O_REQUIRE) {
+ sprintf(tokenbuf,"Can't locate %s in @INC", specfilename);
+ if (instr(tokenbuf,".h "))
+ strcat(tokenbuf," (change .h to .ph maybe?)");
+ if (instr(tokenbuf,".ph "))
+ strcat(tokenbuf," (did you run h2ph?)");
+ fatal("%s",tokenbuf);
+ }
+ if (gimme != G_ARRAY)
+ st[++sp] = &str_undef;
+ perldb = oldperldb;
+ return sp;
+ }
+ curcmd->c_line = 0;
+ }
+ in_eval++;
+ oldoldbufptr = oldbufptr = bufptr = str_get(linestr);
+ bufend = bufptr + linestr->str_cur;
+ if (++loop_ptr >= loop_max) {
+ loop_max += 128;
+ Renew(loop_stack, loop_max, struct loop);
+ }
+ loop_stack[loop_ptr].loop_label = "_EVAL_";
+ loop_stack[loop_ptr].loop_sp = sp;
+#ifdef DEBUGGING
+ if (debug & 4) {
+ deb("(Pushing label #%d _EVAL_)\n", loop_ptr);
+ }
+#endif
+ eval_root = Nullcmd;
+ if (setjmp(loop_stack[loop_ptr].loop_env)) {
+ retval = 1;
+ }
+ else {
+ error_count = 0;
+ if (rsfp) {
+ retval = yyparse();
+ retval |= error_count;
+ }
+ else if (last_root && last_elen == bufend - bufptr
+ && *bufptr == *last_eval && !bcmp(bufptr,last_eval,last_elen)){
+ retval = 0;
+ eval_root = last_root; /* no point in reparsing */
+ }
+ else if (in_eval == 1 && !savecmd) {
+ if (last_root) {
+ Safefree(last_eval);
+ last_eval = Nullch;
+ cmd_free(last_root);
+ }
+ last_root = Nullcmd;
+ last_elen = bufend - bufptr;
+ last_eval = nsavestr(bufptr, last_elen);
+ retval = yyparse();
+ retval |= error_count;
+ if (!retval)
+ last_root = eval_root;
+ if (!last_root) {
+ Safefree(last_eval);
+ last_eval = Nullch;
+ }
+ }
+ else
+ retval = yyparse();
+ }
+ myroot = eval_root; /* in case cmd_exec does another eval! */
+
+ if (retval || error_count) {
+ st = stack->ary_array;
+ sp = arglast[0];
+ if (gimme != G_ARRAY)
+ st[++sp] = &str_undef;
+ if (parsing) {
+#ifndef MANGLEDPARSE
+#ifdef DEBUGGING
+ if (debug & 128)
+ fprintf(stderr,"Freeing eval_root %lx\n",(long)eval_root);
+#endif
+ cmd_free(eval_root);
+#endif
+ /*SUPPRESS 29*/ /*SUPPRESS 30*/
+ if ((CMD*)eval_root == last_root)
+ last_root = Nullcmd;
+ eval_root = myroot = Nullcmd;
+ }
+ if (rsfp) {
+ fclose(rsfp);
+ rsfp = 0;
+ }
+ }
+ else {
+ parsing = 0;
+ sp = cmd_exec(eval_root,gimme,sp);
+ st = stack->ary_array;
+ for (i = arglast[0] + 1; i <= sp; i++)
+ st[i] = str_mortal(st[i]);
+ /* if we don't save result, free zaps it */
+ if (savecmd)
+ eval_root = myroot;
+ else if (in_eval != 1 && myroot != last_root)
+ cmd_free(myroot);
+ if (eval_root == myroot)
+ eval_root = Nullcmd;
+ }
+
+ perldb = oldperldb;
+ in_eval--;
+#ifdef DEBUGGING
+ if (debug & 4) {
+ char *tmps = loop_stack[loop_ptr].loop_label;
+ deb("(Popping label #%d %s)\n",loop_ptr,
+ tmps ? tmps : "" );
+ }
+#endif
+ loop_ptr--;
+ tmps_base = oldtmps_base;
+ curspat = oldspat;
+ lastspat = oldlspat;
+ if (savestack->ary_fill > oldsave) /* let them use local() */
+ restorelist(oldsave);
+
+ if (optype != O_EVAL) {
+ if (retval) {
+ if (optype == O_REQUIRE)
+ fatal("%s", str_get(stab_val(stabent("@",TRUE))));
+ }
+ else {
+ curcmd = oldcurcmd;
+ if (gimme == G_SCALAR ? str_true(st[sp]) : sp > arglast[0]) {
+ (void)hstore(stab_hash(incstab), specfilename,
+ strlen(specfilename), str_smake(stab_val(curcmd->c_filestab)),
+ 0 );
+ }
+ else if (optype == O_REQUIRE)
+ fatal("%s did not return a true value", specfilename);
+ }
+ }
+ curcmd = oldcurcmd;
+ return sp;
+}
+
+int
+do_try(cmd,gimme,arglast)
+CMD *cmd;
+int gimme;
+int *arglast;
+{
+ STR **st = stack->ary_array;
+
+ CMD * VOLATILE oldcurcmd = curcmd;
+ VOLATILE int oldtmps_base = tmps_base;
+ VOLATILE int oldsave = savestack->ary_fill;
+ SPAT * VOLATILE oldspat = curspat;
+ SPAT * VOLATILE oldlspat = lastspat;
+ VOLATILE int sp = arglast[0];
+
+ tmps_base = tmps_max;
+ str_set(stab_val(stabent("@",TRUE)),"");
+ in_eval++;
+ if (++loop_ptr >= loop_max) {
+ loop_max += 128;
+ Renew(loop_stack, loop_max, struct loop);
+ }
+ loop_stack[loop_ptr].loop_label = "_EVAL_";
+ loop_stack[loop_ptr].loop_sp = sp;
+#ifdef DEBUGGING
+ if (debug & 4) {
+ deb("(Pushing label #%d _EVAL_)\n", loop_ptr);
+ }
+#endif
+ if (setjmp(loop_stack[loop_ptr].loop_env)) {
+ st = stack->ary_array;
+ sp = arglast[0];
+ if (gimme != G_ARRAY)
+ st[++sp] = &str_undef;
+ }
+ else {
+ sp = cmd_exec(cmd,gimme,sp);
+ st = stack->ary_array;
+/* for (i = arglast[0] + 1; i <= sp; i++)
+ st[i] = str_mortal(st[i]); not needed, I think */
+ /* if we don't save result, free zaps it */
+ }
+
+ in_eval--;
+#ifdef DEBUGGING
+ if (debug & 4) {
+ char *tmps = loop_stack[loop_ptr].loop_label;
+ deb("(Popping label #%d %s)\n",loop_ptr,
+ tmps ? tmps : "" );
+ }
+#endif
+ loop_ptr--;
+ tmps_base = oldtmps_base;
+ curspat = oldspat;
+ lastspat = oldlspat;
+ curcmd = oldcurcmd;
+ if (savestack->ary_fill > oldsave) /* let them use local() */
+ restorelist(oldsave);
+
+ return sp;
+}
+
+/* This routine handles any switches that can be given during run */
+
+static char *
+moreswitches(s)
+char *s;
+{
+ int numlen;
+
+ switch (*s) {
+ case '0':
+ nrschar = scanoct(s, 4, &numlen);
+ nrs = nsavestr("\n",1);
+ *nrs = nrschar;
+ if (nrschar > 0377) {
+ nrslen = 0;
+ nrs = "";
+ }
+ else if (!nrschar && numlen >= 2) {
+ nrslen = 2;
+ nrs = "\n\n";
+ nrschar = '\n';
+ }
+ return s + numlen;
+ case 'a':
+ minus_a = TRUE;
+ s++;
+ return s;
+ case 'c':
+ minus_c = TRUE;
+ s++;
+ return s;
+ case 'd':
+#ifdef TAINT
+ if (euid != uid || egid != gid)
+ fatal("No -d allowed in setuid scripts");
+#endif
+ perldb = TRUE;
+ s++;
+ return s;
+ case 'D':
+#ifdef DEBUGGING
+#ifdef TAINT
+ if (euid != uid || egid != gid)
+ fatal("No -D allowed in setuid scripts");
+#endif
+ debug = atoi(s+1) | 32768;
+#else
+ warn("Recompile perl with -DDEBUGGING to use -D switch\n");
+#endif
+ /*SUPPRESS 530*/
+ for (s++; isDIGIT(*s); s++) ;
+ return s;
+ case 'i':
+ inplace = savestr(s+1);
+ /*SUPPRESS 530*/
+ for (s = inplace; *s && !isSPACE(*s); s++) ;
+ *s = '\0';
+ break;
+ case 'I':
+#ifdef TAINT
+ if (euid != uid || egid != gid)
+ fatal("No -I allowed in setuid scripts");
+#endif
+ if (*++s) {
+ (void)apush(stab_array(incstab),str_make(s,0));
+ }
+ else
+ fatal("No space allowed after -I");
+ break;
+ case 'l':
+ minus_l = TRUE;
+ s++;
+ if (isDIGIT(*s)) {
+ ors = savestr("\n");
+ orslen = 1;
+ *ors = scanoct(s, 3 + (*s == '0'), &numlen);
+ s += numlen;
+ }
+ else {
+ ors = nsavestr(nrs,nrslen);
+ orslen = nrslen;
+ }
+ return s;
+ case 'n':
+ minus_n = TRUE;
+ s++;
+ return s;
+ case 'p':
+ minus_p = TRUE;
+ s++;
+ return s;
+ case 'u':
+ do_undump = TRUE;
+ s++;
+ return s;
+ case 'U':
+ unsafe = TRUE;
+ s++;
+ return s;
+ case 'v':
+ fputs("\nThis is perl, version 4.0\n\n",stdout);
+ fputs(rcsid,stdout);
+ fputs("\nCopyright (c) 1989, 1990, 1991, Larry Wall\n",stdout);
+#ifdef MSDOS
+ fputs("MS-DOS port Copyright (c) 1989, 1990, Diomidis Spinellis\n",
+ stdout);
+#ifdef OS2
+ fputs("OS/2 port Copyright (c) 1990, 1991, Raymond Chen, Kai Uwe Rommel\n",
+ stdout);
+#endif
+#endif
+#ifdef atarist
+ fputs("atariST series port, ++jrb bammi@cadence.com\n", stdout);
+#endif
+ fputs("\n\
+Perl may be copied only under the terms of either the Artistic License or the\n\
+GNU General Public License, which may be found in the Perl 4.0 source kit.\n",stdout);
+#ifdef MSDOS
+ usage(origargv[0]);
+#endif
+ exit(0);
+ case 'w':
+ dowarn = TRUE;
+ s++;
+ return s;
+ case ' ':
+ case '\n':
+ case '\t':
+ break;
+ default:
+ fatal("Switch meaningless after -x: -%s",s);
+ }
+ return Nullch;
+}
+
+/* compliments of Tom Christiansen */
+
+/* unexec() can be found in the Gnu emacs distribution */
+
+void
+my_unexec()
+{
+#ifdef UNEXEC
+ int status;
+ extern int etext;
+ static char dumpname[BUFSIZ];
+ static char perlpath[256];
+
+ sprintf (dumpname, "%s.perldump", origfilename);
+ sprintf (perlpath, "%s/perl", BIN);
+
+ status = unexec(dumpname, perlpath, &etext, sbrk(0), 0);
+ if (status)
+ fprintf(stderr, "unexec of %s into %s failed!\n", perlpath, dumpname);
+ exit(status);
+#else
+#ifdef DOSISH
+ abort(); /* nothing else to do */
+#else /* ! MSDOS */
+# ifndef SIGABRT
+# define SIGABRT SIGILL
+# endif
+# ifndef SIGILL
+# define SIGILL 6 /* blech */
+# endif
+ kill(getpid(),SIGABRT); /* for use with undump */
+#endif /* ! MSDOS */
+#endif
+}
+
diff --git a/gnu/usr.bin/perl/perl/perl.h b/gnu/usr.bin/perl/perl/perl.h
new file mode 100644
index 0000000..8249c62
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/perl.h
@@ -0,0 +1,1063 @@
+/* $RCSfile: perl.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:35 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: perl.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:35 nate
+ * PERL!
+ *
+ * Revision 4.0.1.7 1993/02/05 19:40:30 lwall
+ * patch36: worked around certain busted compilers that don't init statics right
+ *
+ * Revision 4.0.1.6 92/06/08 14:55:10 lwall
+ * patch20: added Atari ST portability
+ * patch20: bcopy() and memcpy() now tested for overlap safety
+ * patch20: Perl now distinguishes overlapped copies from non-overlapped
+ * patch20: removed implicit int declarations on functions
+ *
+ * Revision 4.0.1.5 91/11/11 16:41:07 lwall
+ * patch19: uts wrongly defines S_ISDIR() et al
+ * patch19: too many preprocessors can't expand a macro right in #if
+ * patch19: added little-endian pack/unpack options
+ *
+ * Revision 4.0.1.4 91/11/05 18:06:10 lwall
+ * patch11: various portability fixes
+ * patch11: added support for dbz
+ * patch11: added some support for 64-bit integers
+ * patch11: hex() didn't understand leading 0x
+ *
+ * Revision 4.0.1.3 91/06/10 01:25:10 lwall
+ * patch10: certain pattern optimizations were botched
+ *
+ * Revision 4.0.1.2 91/06/07 11:28:33 lwall
+ * patch4: new copyright notice
+ * patch4: made some allowances for "semi-standard" C
+ * patch4: many, many itty-bitty portability fixes
+ *
+ * Revision 4.0.1.1 91/04/11 17:49:51 lwall
+ * patch1: hopefully straightened out some of the Xenix mess
+ *
+ * Revision 4.0 91/03/20 01:37:56 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#define VOIDWANT 1
+#include "config.h"
+
+#ifdef MYMALLOC
+# ifdef HIDEMYMALLOC
+# define malloc Mymalloc
+# define realloc Myremalloc
+# define free Myfree
+# endif
+# define safemalloc malloc
+# define saferealloc realloc
+# define safefree free
+#endif
+
+/* work around some libPW problems */
+#define fatal Myfatal
+#ifdef DOINIT
+char Error[1];
+#endif
+
+/* define this once if either system, instead of cluttering up the src */
+#if defined(MSDOS) || defined(atarist)
+#define DOSISH 1
+#endif
+
+#ifdef DOSISH
+/* This stuff now in the MS-DOS config.h file. */
+#else /* !MSDOS */
+
+/*
+ * The following symbols are defined if your operating system supports
+ * functions by that name. All Unixes I know of support them, thus they
+ * are not checked by the configuration script, but are directly defined
+ * here.
+ */
+#define HAS_ALARM
+#define HAS_CHOWN
+#define HAS_CHROOT
+#define HAS_FORK
+#define HAS_GETLOGIN
+#define HAS_GETPPID
+#define HAS_KILL
+#define HAS_LINK
+#define HAS_PIPE
+#define HAS_WAIT
+#define HAS_UMASK
+/*
+ * The following symbols are defined if your operating system supports
+ * password and group functions in general. All Unix systems do.
+ */
+#define HAS_GROUP
+#define HAS_PASSWD
+
+#endif /* !MSDOS */
+
+#if defined(__STDC__) || defined(_AIX) || defined(__stdc__)
+# define STANDARD_C 1
+#endif
+
+#if defined(HASVOLATILE) || defined(STANDARD_C)
+#define VOLATILE volatile
+#else
+#define VOLATILE
+#endif
+
+#ifdef IAMSUID
+# ifndef TAINT
+# define TAINT
+# endif
+#endif
+
+#ifndef HAS_VFORK
+# define vfork fork
+#endif
+
+#ifdef HAS_GETPGRP2
+# ifndef HAS_GETPGRP
+# define HAS_GETPGRP
+# endif
+# define getpgrp getpgrp2
+#endif
+
+#ifdef HAS_SETPGRP2
+# ifndef HAS_SETPGRP
+# define HAS_SETPGRP
+# endif
+# define setpgrp setpgrp2
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <setjmp.h>
+#ifndef MSDOS
+#ifdef PARAM_NEEDS_TYPES
+#include <sys/types.h>
+#endif
+#include <sys/param.h>
+#endif
+#ifdef STANDARD_C
+/* Use all the "standard" definitions */
+#include <stdlib.h>
+#include <string.h>
+#define MEM_SIZE size_t
+#else
+typedef unsigned int MEM_SIZE;
+#endif /* STANDARD_C */
+
+#if defined(HAS_MEMCMP) && defined(mips) && defined(ultrix)
+#undef HAS_MEMCMP
+#endif
+
+#ifdef HAS_MEMCPY
+# ifndef STANDARD_C
+# ifndef memcpy
+ extern char * memcpy();
+# endif
+# endif
+#else
+# ifndef memcpy
+# ifdef HAS_BCOPY
+# define memcpy(d,s,l) bcopy(s,d,l)
+# else
+# define memcpy(d,s,l) my_bcopy(s,d,l)
+# endif
+# endif
+#endif /* HAS_MEMCPY */
+
+#ifdef HAS_MEMSET
+# ifndef STANDARD_C
+# ifndef memset
+ extern char *memset();
+# endif
+# endif
+# define memzero(d,l) memset(d,0,l)
+#else
+# ifndef memzero
+# ifdef HAS_BZERO
+# define memzero(d,l) bzero(d,l)
+# else
+# define memzero(d,l) my_bzero(d,l)
+# endif
+# endif
+#endif /* HAS_MEMSET */
+
+#ifdef HAS_MEMCMP
+# ifndef STANDARD_C
+# ifndef memcmp
+ extern int memcmp();
+# endif
+# endif
+#else
+# ifndef memcmp
+# define memcmp(s1,s2,l) my_memcmp(s1,s2,l)
+# endif
+#endif /* HAS_MEMCMP */
+
+/* we prefer bcmp slightly for comparisons that don't care about ordering */
+#ifndef HAS_BCMP
+# ifndef bcmp
+# define bcmp(s1,s2,l) memcmp(s1,s2,l)
+# endif
+#endif /* HAS_BCMP */
+
+#ifndef HAS_MEMMOVE
+#if defined(HAS_BCOPY) && defined(SAFE_BCOPY)
+#define memmove(d,s,l) bcopy(s,d,l)
+#else
+#if defined(HAS_MEMCPY) && defined(SAFE_MEMCPY)
+#define memmove(d,s,l) memcpy(d,s,l)
+#else
+#define memmove(d,s,l) my_bcopy(s,d,l)
+#endif
+#endif
+#endif
+
+#ifndef _TYPES_ /* If types.h defines this it's easy. */
+#ifndef major /* Does everyone's types.h define this? */
+#include <sys/types.h>
+#endif
+#endif
+
+#ifdef I_NETINET_IN
+#include <netinet/in.h>
+#endif
+
+#include <sys/stat.h>
+#if defined(uts) || defined(UTekV)
+#undef S_ISDIR
+#undef S_ISCHR
+#undef S_ISBLK
+#undef S_ISREG
+#undef S_ISFIFO
+#undef S_ISLNK
+#define S_ISDIR(P) (((P)&S_IFMT)==S_IFDIR)
+#define S_ISCHR(P) (((P)&S_IFMT)==S_IFCHR)
+#define S_ISBLK(P) (((P)&S_IFMT)==S_IFBLK)
+#define S_ISREG(P) (((P)&S_IFMT)==S_IFREG)
+#define S_ISFIFO(P) (((P)&S_IFMT)==S_IFIFO)
+#ifdef S_IFLNK
+#define S_ISLNK(P) (((P)&S_IFMT)==S_IFLNK)
+#endif
+#endif
+
+#ifdef I_TIME
+# include <time.h>
+#endif
+
+#ifdef I_SYS_TIME
+# ifdef SYSTIMEKERNEL
+# define KERNEL
+# endif
+# include <sys/time.h>
+# ifdef SYSTIMEKERNEL
+# undef KERNEL
+# endif
+#endif
+
+#ifndef MSDOS
+#include <sys/times.h>
+#endif
+
+#if defined(HAS_STRERROR) && (!defined(HAS_MKDIR) || !defined(HAS_RMDIR))
+#undef HAS_STRERROR
+#endif
+
+#include <errno.h>
+#ifndef MSDOS
+#ifndef errno
+extern int errno; /* ANSI allows errno to be an lvalue expr */
+#endif
+#endif
+
+#ifndef strerror
+#ifdef HAS_STRERROR
+char *strerror();
+#else
+extern int sys_nerr;
+extern char *sys_errlist[];
+#define strerror(e) ((e) < 0 || (e) >= sys_nerr ? "(unknown)" : sys_errlist[e])
+#endif
+#endif
+
+#ifdef I_SYSIOCTL
+#ifndef _IOCTL_
+#include <sys/ioctl.h>
+#endif
+#endif
+
+#if defined(mc300) || defined(mc500) || defined(mc700) || defined(mc6000)
+#ifdef HAS_SOCKETPAIR
+#undef HAS_SOCKETPAIR
+#endif
+#ifdef HAS_NDBM
+#undef HAS_NDBM
+#endif
+#endif
+
+#ifdef WANT_DBZ
+#include <dbz.h>
+#define SOME_DBM
+#define dbm_fetch(db,dkey) fetch(dkey)
+#define dbm_delete(db,dkey) fatal("dbz doesn't implement delete")
+#define dbm_store(db,dkey,dcontent,flags) store(dkey,dcontent)
+#define dbm_close(db) dbmclose()
+#define dbm_firstkey(db) (fatal("dbz doesn't implement traversal"),fetch())
+#define nextkey() (fatal("dbz doesn't implement traversal"),fetch())
+#define dbm_nextkey(db) (fatal("dbz doesn't implement traversal"),fetch())
+#ifdef HAS_NDBM
+#undef HAS_NDBM
+#endif
+#ifndef HAS_ODBM
+#define HAS_ODBM
+#endif
+#else
+#ifdef HAS_GDBM
+#ifdef I_GDBM
+#include <gdbm.h>
+#endif
+#define SOME_DBM
+#ifdef HAS_NDBM
+#undef HAS_NDBM
+#endif
+#ifdef HAS_ODBM
+#undef HAS_ODBM
+#endif
+#else
+#ifdef HAS_NDBM
+#include <ndbm.h>
+#define SOME_DBM
+#ifdef HAS_ODBM
+#undef HAS_ODBM
+#endif
+#else
+#ifdef HAS_ODBM
+#ifdef NULL
+#undef NULL /* suppress redefinition message */
+#endif
+#include <dbm.h>
+#ifdef NULL
+#undef NULL
+#endif
+#define NULL 0 /* silly thing is, we don't even use this */
+#define SOME_DBM
+#define dbm_fetch(db,dkey) fetch(dkey)
+#define dbm_delete(db,dkey) delete(dkey)
+#define dbm_store(db,dkey,dcontent,flags) store(dkey,dcontent)
+#define dbm_close(db) dbmclose()
+#define dbm_firstkey(db) firstkey()
+#endif /* HAS_ODBM */
+#endif /* HAS_NDBM */
+#endif /* HAS_GDBM */
+#endif /* WANT_DBZ */
+#ifdef SOME_DBM
+EXT char *dbmkey;
+EXT int dbmlen;
+#endif
+
+#if INTSIZE == 2
+#define htoni htons
+#define ntohi ntohs
+#else
+#define htoni htonl
+#define ntohi ntohl
+#endif
+
+#if defined(I_DIRENT)
+# include <dirent.h>
+# define DIRENT dirent
+#else
+# ifdef I_SYS_NDIR
+# include <sys/ndir.h>
+# define DIRENT direct
+# else
+# ifdef I_SYS_DIR
+# ifdef hp9000s500
+# include <ndir.h> /* may be wrong in the future */
+# else
+# include <sys/dir.h>
+# endif
+# define DIRENT direct
+# endif
+# endif
+#endif
+
+#ifdef FPUTS_BOTCH
+/* work around botch in SunOS 4.0.1 and 4.0.2 */
+# ifndef fputs
+# define fputs(str,fp) fprintf(fp,"%s",str)
+# endif
+#endif
+
+/*
+ * The following gobbledygook brought to you on behalf of __STDC__.
+ * (I could just use #ifndef __STDC__, but this is more bulletproof
+ * in the face of half-implementations.)
+ */
+
+#ifndef S_IFMT
+# ifdef _S_IFMT
+# define S_IFMT _S_IFMT
+# else
+# define S_IFMT 0170000
+# endif
+#endif
+
+#ifndef S_ISDIR
+# define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR)
+#endif
+
+#ifndef S_ISCHR
+# define S_ISCHR(m) ((m & S_IFMT) == S_IFCHR)
+#endif
+
+#ifndef S_ISBLK
+# ifdef S_IFBLK
+# define S_ISBLK(m) ((m & S_IFMT) == S_IFBLK)
+# else
+# define S_ISBLK(m) (0)
+# endif
+#endif
+
+#ifndef S_ISREG
+# define S_ISREG(m) ((m & S_IFMT) == S_IFREG)
+#endif
+
+#ifndef S_ISFIFO
+# ifdef S_IFIFO
+# define S_ISFIFO(m) ((m & S_IFMT) == S_IFIFO)
+# else
+# define S_ISFIFO(m) (0)
+# endif
+#endif
+
+#ifndef S_ISLNK
+# ifdef _S_ISLNK
+# define S_ISLNK(m) _S_ISLNK(m)
+# else
+# ifdef _S_IFLNK
+# define S_ISLNK(m) ((m & S_IFMT) == _S_IFLNK)
+# else
+# ifdef S_IFLNK
+# define S_ISLNK(m) ((m & S_IFMT) == S_IFLNK)
+# else
+# define S_ISLNK(m) (0)
+# endif
+# endif
+# endif
+#endif
+
+#ifndef S_ISSOCK
+# ifdef _S_ISSOCK
+# define S_ISSOCK(m) _S_ISSOCK(m)
+# else
+# ifdef _S_IFSOCK
+# define S_ISSOCK(m) ((m & S_IFMT) == _S_IFSOCK)
+# else
+# ifdef S_IFSOCK
+# define S_ISSOCK(m) ((m & S_IFMT) == S_IFSOCK)
+# else
+# define S_ISSOCK(m) (0)
+# endif
+# endif
+# endif
+#endif
+
+#ifndef S_IRUSR
+# ifdef S_IREAD
+# define S_IRUSR S_IREAD
+# define S_IWUSR S_IWRITE
+# define S_IXUSR S_IEXEC
+# else
+# define S_IRUSR 0400
+# define S_IWUSR 0200
+# define S_IXUSR 0100
+# endif
+# define S_IRGRP (S_IRUSR>>3)
+# define S_IWGRP (S_IWUSR>>3)
+# define S_IXGRP (S_IXUSR>>3)
+# define S_IROTH (S_IRUSR>>6)
+# define S_IWOTH (S_IWUSR>>6)
+# define S_IXOTH (S_IXUSR>>6)
+#endif
+
+#ifndef S_ISUID
+# define S_ISUID 04000
+#endif
+
+#ifndef S_ISGID
+# define S_ISGID 02000
+#endif
+
+#ifdef f_next
+#undef f_next
+#endif
+
+#if defined(cray) || defined(gould) || defined(i860)
+# define SLOPPYDIVIDE
+#endif
+
+#if defined(cray) || defined(convex) || defined (uts) || BYTEORDER > 0xffff
+# define QUAD
+#endif
+
+#ifdef QUAD
+# ifdef cray
+# define quad int
+# else
+# if defined(convex) || defined (uts)
+# define quad long long
+# else
+# define quad long
+# endif
+# endif
+#endif
+
+typedef MEM_SIZE STRLEN;
+
+typedef struct arg ARG;
+typedef struct cmd CMD;
+typedef struct formcmd FCMD;
+typedef struct scanpat SPAT;
+typedef struct stio STIO;
+typedef struct sub SUBR;
+typedef struct string STR;
+typedef struct atbl ARRAY;
+typedef struct htbl HASH;
+typedef struct regexp REGEXP;
+typedef struct stabptrs STBP;
+typedef struct stab STAB;
+typedef struct callsave CSV;
+
+#include "handy.h"
+#include "regexp.h"
+#include "str.h"
+#include "util.h"
+#include "form.h"
+#include "stab.h"
+#include "spat.h"
+#include "arg.h"
+#include "cmd.h"
+#include "array.h"
+#include "hash.h"
+
+#if defined(iAPX286) || defined(M_I286) || defined(I80286)
+# define I286
+#endif
+
+#ifndef STANDARD_C
+#ifdef CHARSPRINTF
+ char *sprintf();
+#else
+ int sprintf();
+#endif
+#endif
+
+EXT char *Yes INIT("1");
+EXT char *No INIT("");
+
+/* "gimme" values */
+
+/* Note: cmd.c assumes that it can use && to produce one of these values! */
+#define G_SCALAR 0
+#define G_ARRAY 1
+
+#ifdef CRIPPLED_CC
+int str_true();
+#else /* !CRIPPLED_CC */
+#define str_true(str) (Str = (str), \
+ (Str->str_pok ? \
+ ((*Str->str_ptr > '0' || \
+ Str->str_cur > 1 || \
+ (Str->str_cur && *Str->str_ptr != '0')) ? 1 : 0) \
+ : \
+ (Str->str_nok ? (Str->str_u.str_nval != 0.0) : 0 ) ))
+#endif /* CRIPPLED_CC */
+
+#ifdef DEBUGGING
+#define str_peek(str) (Str = (str), \
+ (Str->str_pok ? \
+ Str->str_ptr : \
+ (Str->str_nok ? \
+ (sprintf(tokenbuf,"num(%g)",Str->str_u.str_nval), \
+ (char*)tokenbuf) : \
+ "" )))
+#endif
+
+#ifdef CRIPPLED_CC
+char *str_get();
+#else
+#ifdef TAINT
+#define str_get(str) (Str = (str), tainted |= Str->str_tainted, \
+ (Str->str_pok ? Str->str_ptr : str_2ptr(Str)))
+#else
+#define str_get(str) (Str = (str), (Str->str_pok ? Str->str_ptr : str_2ptr(Str)))
+#endif /* TAINT */
+#endif /* CRIPPLED_CC */
+
+#ifdef CRIPPLED_CC
+double str_gnum();
+#else /* !CRIPPLED_CC */
+#ifdef TAINT
+#define str_gnum(str) (Str = (str), tainted |= Str->str_tainted, \
+ (Str->str_nok ? Str->str_u.str_nval : str_2num(Str)))
+#else /* !TAINT */
+#define str_gnum(str) (Str = (str), (Str->str_nok ? Str->str_u.str_nval : str_2num(Str)))
+#endif /* TAINT*/
+#endif /* CRIPPLED_CC */
+EXT STR *Str;
+
+#define GROWSTR(pp,lp,len) if (*(lp) < (len)) growstr(pp,lp,len)
+
+#ifndef DOSISH
+#define STR_GROW(str,len) if ((str)->str_len < (len)) str_grow(str,len)
+#define Str_Grow str_grow
+#else
+/* extra parentheses intentionally NOT placed around "len"! */
+#define STR_GROW(str,len) if ((str)->str_len < (unsigned long)len) \
+ str_grow(str,(unsigned long)len)
+#define Str_Grow(str,len) str_grow(str,(unsigned long)(len))
+#endif /* DOSISH */
+
+#ifndef BYTEORDER
+#define BYTEORDER 0x1234
+#endif
+
+#if defined(htonl) && !defined(HAS_HTONL)
+#define HAS_HTONL
+#endif
+#if defined(htons) && !defined(HAS_HTONS)
+#define HAS_HTONS
+#endif
+#if defined(ntohl) && !defined(HAS_NTOHL)
+#define HAS_NTOHL
+#endif
+#if defined(ntohs) && !defined(HAS_NTOHS)
+#define HAS_NTOHS
+#endif
+#ifndef HAS_HTONL
+#if (BYTEORDER & 0xffff) != 0x4321
+#define HAS_HTONS
+#define HAS_HTONL
+#define HAS_NTOHS
+#define HAS_NTOHL
+#define MYSWAP
+#define htons my_swap
+#define htonl my_htonl
+#define ntohs my_swap
+#define ntohl my_ntohl
+#endif
+#else
+#if (BYTEORDER & 0xffff) == 0x4321
+#undef HAS_HTONS
+#undef HAS_HTONL
+#undef HAS_NTOHS
+#undef HAS_NTOHL
+#endif
+#endif
+
+/*
+ * Little-endian byte order functions - 'v' for 'VAX', or 'reVerse'.
+ * -DWS
+ */
+#if BYTEORDER != 0x1234
+# define HAS_VTOHL
+# define HAS_VTOHS
+# define HAS_HTOVL
+# define HAS_HTOVS
+# if BYTEORDER == 0x4321
+# define vtohl(x) ((((x)&0xFF)<<24) \
+ +(((x)>>24)&0xFF) \
+ +(((x)&0x0000FF00)<<8) \
+ +(((x)&0x00FF0000)>>8) )
+# define vtohs(x) ((((x)&0xFF)<<8) + (((x)>>8)&0xFF))
+# define htovl(x) vtohl(x)
+# define htovs(x) vtohs(x)
+# endif
+ /* otherwise default to functions in util.c */
+#endif
+
+#ifdef CASTNEGFLOAT
+#define U_S(what) ((unsigned short)(what))
+#define U_I(what) ((unsigned int)(what))
+#define U_L(what) ((unsigned long)(what))
+#else
+unsigned long castulong();
+#define U_S(what) ((unsigned int)castulong(what))
+#define U_I(what) ((unsigned int)castulong(what))
+#define U_L(what) (castulong(what))
+#endif
+
+CMD *add_label();
+CMD *block_head();
+CMD *append_line();
+CMD *make_acmd();
+CMD *make_ccmd();
+CMD *make_icmd();
+CMD *invert();
+CMD *addcond();
+CMD *addloop();
+CMD *wopt();
+CMD *over();
+
+STAB *stabent();
+STAB *genstab();
+
+ARG *stab2arg();
+ARG *op_new();
+ARG *make_op();
+ARG *make_match();
+ARG *make_split();
+ARG *rcatmaybe();
+ARG *listish();
+ARG *maybelistish();
+ARG *localize();
+ARG *fixeval();
+ARG *jmaybe();
+ARG *l();
+ARG *fixl();
+ARG *mod_match();
+ARG *make_list();
+ARG *cmd_to_arg();
+ARG *addflags();
+ARG *hide_ary();
+ARG *cval_to_arg();
+
+STR *str_new();
+STR *stab_str();
+
+int apply();
+int do_each();
+int do_subr();
+int do_match();
+int do_unpack();
+int eval(); /* this evaluates expressions */
+int do_eval(); /* this evaluates eval operator */
+int do_assign();
+
+SUBR *make_sub();
+
+FCMD *load_format();
+
+char *scanpat();
+char *scansubst();
+char *scantrans();
+char *scanstr();
+char *scanident();
+char *str_append_till();
+char *str_gets();
+char *str_grow();
+
+bool do_open();
+bool do_close();
+bool do_print();
+bool do_aprint();
+bool do_exec();
+bool do_aexec();
+
+int do_subst();
+int cando();
+int ingroup();
+int whichsig();
+int userinit();
+#ifdef CRYPTSCRIPT
+void cryptswitch();
+#endif
+
+void str_replace();
+void str_inc();
+void str_dec();
+void str_free();
+void cmd_free();
+void arg_free();
+void spat_free();
+void regfree();
+void stab_clear();
+void do_chop();
+void do_vop();
+void do_write();
+void do_join();
+void do_sprintf();
+void do_accept();
+void do_pipe();
+void do_vecset();
+void do_unshift();
+void do_execfree();
+void magicalize();
+void magicname();
+void savelist();
+void saveitem();
+void saveint();
+void savelong();
+void savesptr();
+void savehptr();
+void restorelist();
+void repeatcpy();
+void make_form();
+void dehoist();
+void format();
+void my_unexec();
+void fatal();
+void warn();
+#ifdef DEBUGGING
+void dump_all();
+void dump_cmd();
+void dump_arg();
+void dump_flags();
+void dump_stab();
+void dump_spat();
+#endif
+#ifdef MSTATS
+void mstats();
+#endif
+
+HASH *savehash();
+ARRAY *saveary();
+
+EXT char **origargv;
+EXT int origargc;
+EXT char **origenviron;
+extern char **environ;
+
+EXT long subline INIT(0);
+EXT STR *subname INIT(Nullstr);
+EXT int arybase INIT(0);
+
+struct outrec {
+ long o_lines;
+ char *o_str;
+ int o_len;
+};
+
+EXT struct outrec outrec;
+EXT struct outrec toprec;
+
+EXT STAB *stdinstab INIT(Nullstab);
+EXT STAB *last_in_stab INIT(Nullstab);
+EXT STAB *defstab INIT(Nullstab);
+EXT STAB *argvstab INIT(Nullstab);
+EXT STAB *envstab INIT(Nullstab);
+EXT STAB *sigstab INIT(Nullstab);
+EXT STAB *defoutstab INIT(Nullstab);
+EXT STAB *curoutstab INIT(Nullstab);
+EXT STAB *argvoutstab INIT(Nullstab);
+EXT STAB *incstab INIT(Nullstab);
+EXT STAB *leftstab INIT(Nullstab);
+EXT STAB *amperstab INIT(Nullstab);
+EXT STAB *rightstab INIT(Nullstab);
+EXT STAB *DBstab INIT(Nullstab);
+EXT STAB *DBline INIT(Nullstab);
+EXT STAB *DBsub INIT(Nullstab);
+
+EXT HASH *defstash; /* main symbol table */
+EXT HASH *curstash; /* symbol table for current package */
+EXT HASH *debstash; /* symbol table for perldb package */
+
+EXT STR *curstname; /* name of current package */
+
+EXT STR *freestrroot INIT(Nullstr);
+EXT STR *lastretstr INIT(Nullstr);
+EXT STR *DBsingle INIT(Nullstr);
+EXT STR *DBtrace INIT(Nullstr);
+EXT STR *DBsignal INIT(Nullstr);
+EXT STR *formfeed INIT(Nullstr);
+
+EXT int lastspbase;
+EXT int lastsize;
+
+EXT char *hexdigit INIT("0123456789abcdef0123456789ABCDEFx");
+EXT char *origfilename;
+EXT FILE * VOLATILE rsfp INIT(Nullfp);
+EXT char buf[1024];
+EXT char *bufptr;
+EXT char *oldbufptr;
+EXT char *oldoldbufptr;
+EXT char *bufend;
+
+EXT STR *linestr INIT(Nullstr);
+
+EXT char *rs INIT("\n");
+EXT int rschar INIT('\n'); /* final char of rs, or 0777 if none */
+EXT int rslen INIT(1);
+EXT bool rspara INIT(FALSE);
+EXT char *ofs INIT(Nullch);
+EXT int ofslen INIT(0);
+EXT char *ors INIT(Nullch);
+EXT int orslen INIT(0);
+EXT char *ofmt INIT(Nullch);
+EXT char *inplace INIT(Nullch);
+EXT char *nointrp INIT("");
+
+EXT bool preprocess INIT(FALSE);
+EXT bool minus_n INIT(FALSE);
+EXT bool minus_p INIT(FALSE);
+EXT bool minus_l INIT(FALSE);
+EXT bool minus_a INIT(FALSE);
+EXT bool doswitches INIT(FALSE);
+EXT bool dowarn INIT(FALSE);
+EXT bool doextract INIT(FALSE);
+EXT bool allstabs INIT(FALSE); /* init all customary symbols in symbol table?*/
+EXT bool sawampersand INIT(FALSE); /* must save all match strings */
+EXT bool sawstudy INIT(FALSE); /* do fbminstr on all strings */
+EXT bool sawi INIT(FALSE); /* study must assume case insensitive */
+EXT bool sawvec INIT(FALSE);
+EXT bool localizing INIT(FALSE); /* are we processing a local() list? */
+
+#ifndef MAXSYSFD
+# define MAXSYSFD 2
+#endif
+EXT int maxsysfd INIT(MAXSYSFD); /* top fd to pass to subprocesses */
+
+#ifdef CSH
+EXT char *cshname INIT(CSH);
+EXT int cshlen INIT(0);
+#endif /* CSH */
+
+#ifdef TAINT
+EXT bool tainted INIT(FALSE); /* using variables controlled by $< */
+EXT bool taintanyway INIT(FALSE); /* force taint checks when !set?id */
+#endif
+
+EXT bool nomemok INIT(FALSE); /* let malloc context handle nomem */
+
+#ifndef DOSISH
+#define TMPPATH "/tmp/perl-eXXXXXX"
+#else
+#define TMPPATH "plXXXXXX"
+#endif /* MSDOS */
+EXT char *e_tmpname;
+EXT FILE *e_fp INIT(Nullfp);
+
+EXT char tokenbuf[256];
+EXT int expectterm INIT(TRUE); /* how to interpret ambiguous tokens */
+EXT VOLATILE int in_eval INIT(FALSE); /* trap fatal errors? */
+EXT int multiline INIT(0); /* $*--do strings hold >1 line? */
+EXT int forkprocess; /* so do_open |- can return proc# */
+EXT int do_undump INIT(0); /* -u or dump seen? */
+EXT int error_count INIT(0); /* how many errors so far, max 10 */
+EXT int multi_start INIT(0); /* 1st line of multi-line string */
+EXT int multi_end INIT(0); /* last line of multi-line string */
+EXT int multi_open INIT(0); /* delimiter of said string */
+EXT int multi_close INIT(0); /* delimiter of said string */
+
+FILE *popen();
+/* char *str_get(); */
+STR *interp();
+void free_arg();
+STIO *stio_new();
+void hoistmust();
+void scanconst();
+
+EXT struct stat statbuf;
+EXT struct stat statcache;
+EXT STAB *statstab INIT(Nullstab);
+EXT STR *statname INIT(Nullstr);
+#ifndef MSDOS
+EXT struct tms timesbuf;
+#endif
+EXT int uid;
+EXT int euid;
+EXT int gid;
+EXT int egid;
+UIDTYPE getuid();
+UIDTYPE geteuid();
+GIDTYPE getgid();
+GIDTYPE getegid();
+EXT int unsafe;
+
+#ifdef DEBUGGING
+EXT VOLATILE int debug INIT(0);
+EXT int dlevel INIT(0);
+EXT int dlmax INIT(128);
+EXT char *debname;
+EXT char *debdelim;
+#define YYDEBUG 1
+#endif
+EXT int perldb INIT(0);
+#define YYMAXDEPTH 300
+
+EXT line_t cmdline INIT(NOLINE);
+
+EXT STR str_undef;
+EXT STR str_no;
+EXT STR str_yes;
+
+/* runtime control stuff */
+
+EXT struct loop {
+ char *loop_label; /* what the loop was called, if anything */
+ int loop_sp; /* stack pointer to copy stuff down to */
+ jmp_buf loop_env;
+} *loop_stack;
+
+EXT int loop_ptr INIT(-1);
+EXT int loop_max INIT(128);
+
+EXT jmp_buf top_env;
+
+EXT char * VOLATILE goto_targ INIT(Nullch); /* cmd_exec gets strange when set */
+
+struct ufuncs {
+ int (*uf_val)();
+ int (*uf_set)();
+ int uf_index;
+};
+
+EXT ARRAY *stack; /* THE STACK */
+
+EXT ARRAY * VOLATILE savestack; /* to save non-local values on */
+
+EXT ARRAY *tosave; /* strings to save on recursive subroutine */
+
+EXT ARRAY *lineary; /* lines of script for debugger */
+EXT ARRAY *dbargs; /* args to call listed by caller function */
+
+EXT ARRAY *fdpid; /* keep fd-to-pid mappings for mypopen */
+EXT HASH *pidstatus; /* keep pid-to-status mappings for waitpid */
+
+EXT int *di; /* for tmp use in debuggers */
+EXT char *dc;
+EXT short *ds;
+
+/* Fix these up for __STDC__ */
+EXT time_t basetime INIT(0);
+char *mktemp();
+#ifndef STANDARD_C
+/* All of these are in stdlib.h or time.h for ANSI C */
+double atof();
+long time();
+struct tm *gmtime(), *localtime();
+char *index(), *rindex();
+char *strcpy(), *strcat();
+#endif /* ! STANDARD_C */
+
+#ifdef EUNICE
+#define UNLINK unlnk
+int unlnk();
+#else
+#define UNLINK unlink
+#endif
+
+#ifndef HAS_SETREUID
+#ifdef HAS_SETRESUID
+#define setreuid(r,e) setresuid(r,e,-1)
+#define HAS_SETREUID
+#endif
+#endif
+#ifndef HAS_SETREGID
+#ifdef HAS_SETRESGID
+#define setregid(r,e) setresgid(r,e,-1)
+#define HAS_SETREGID
+#endif
+#endif
+
+#define SCAN_DEF 0
+#define SCAN_TR 1
+#define SCAN_REPL 2
diff --git a/gnu/usr.bin/perl/perl/perly.c b/gnu/usr.bin/perl/perl/perly.c
new file mode 100644
index 0000000..1084cc4
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/perly.c
@@ -0,0 +1,3063 @@
+#ifndef lint
+static char yysccsid[] = "@(#)yaccpar 1.9 (Berkeley) 02/21/93";
+#endif
+#define YYBYACC 1
+#define YYMAJOR 1
+#define YYMINOR 9
+#define yyclearin (yychar=(-1))
+#define yyerrok (yyerrflag=0)
+#define YYRECOVERING (yyerrflag!=0)
+#define YYPREFIX "yy"
+#line 43 "perly.y"
+#include "INTERN.h"
+#include "perl.h"
+
+/*SUPPRESS 530*/
+/*SUPPRESS 593*/
+/*SUPPRESS 595*/
+
+STAB *scrstab;
+ARG *arg4; /* rarely used arguments to make_op() */
+ARG *arg5;
+
+#line 58 "perly.y"
+typedef union {
+ int ival;
+ char *cval;
+ ARG *arg;
+ CMD *cmdval;
+ struct compcmd compval;
+ STAB *stabval;
+ FCMD *formval;
+} YYSTYPE;
+#line 34 "y.tab.c"
+#define WORD 257
+#define LABEL 258
+#define APPEND 259
+#define OPEN 260
+#define SSELECT 261
+#define LOOPEX 262
+#define DOTDOT 263
+#define USING 264
+#define FORMAT 265
+#define DO 266
+#define SHIFT 267
+#define PUSH 268
+#define POP 269
+#define LVALFUN 270
+#define WHILE 271
+#define UNTIL 272
+#define IF 273
+#define UNLESS 274
+#define ELSE 275
+#define ELSIF 276
+#define CONTINUE 277
+#define SPLIT 278
+#define FLIST 279
+#define FOR 280
+#define FILOP 281
+#define FILOP2 282
+#define FILOP3 283
+#define FILOP4 284
+#define FILOP22 285
+#define FILOP25 286
+#define FUNC0 287
+#define FUNC1 288
+#define FUNC2 289
+#define FUNC2x 290
+#define FUNC3 291
+#define FUNC4 292
+#define FUNC5 293
+#define HSHFUN 294
+#define HSHFUN3 295
+#define FLIST2 296
+#define SUB 297
+#define FILETEST 298
+#define LOCAL 299
+#define DELETE 300
+#define RELOP 301
+#define EQOP 302
+#define MULOP 303
+#define ADDOP 304
+#define PACKAGE 305
+#define AMPER 306
+#define FORMLIST 307
+#define REG 308
+#define ARYLEN 309
+#define ARY 310
+#define HSH 311
+#define STAR 312
+#define SUBST 313
+#define PATTERN 314
+#define RSTRING 315
+#define TRANS 316
+#define LISTOP 317
+#define OROR 318
+#define ANDAND 319
+#define UNIOP 320
+#define LS 321
+#define RS 322
+#define MATCH 323
+#define NMATCH 324
+#define UMINUS 325
+#define POW 326
+#define INC 327
+#define DEC 328
+#define YYERRCODE 256
+short yylhs[] = { -1,
+ 26, 0, 25, 25, 12, 12, 12, 5, 3, 6,
+ 6, 7, 7, 7, 7, 7, 10, 10, 10, 10,
+ 10, 10, 9, 9, 9, 9, 8, 8, 8, 8,
+ 8, 8, 8, 8, 11, 11, 21, 21, 24, 24,
+ 1, 1, 1, 2, 2, 27, 28, 15, 13, 13,
+ 16, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 22, 22, 22, 22, 22, 22, 18, 18, 19,
+ 19, 20, 20, 4, 4, 23,
+};
+short yylen[] = { 2,
+ 0, 2, 3, 2, 0, 2, 5, 4, 0, 0,
+ 2, 1, 2, 1, 2, 3, 1, 1, 3, 3,
+ 3, 3, 5, 5, 3, 3, 6, 6, 4, 4,
+ 7, 6, 10, 2, 0, 1, 0, 1, 0, 1,
+ 1, 1, 1, 4, 3, 3, 3, 2, 3, 1,
+ 2, 3, 4, 4, 4, 4, 4, 4, 4, 4,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 5, 3, 3, 1, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 1, 4, 3,
+ 2, 2, 2, 1, 1, 4, 1, 1, 5, 6,
+ 5, 4, 5, 6, 8, 1, 1, 1, 1, 1,
+ 5, 5, 4, 4, 2, 5, 5, 4, 4, 2,
+ 1, 2, 1, 2, 2, 1, 2, 4, 7, 2,
+ 4, 5, 4, 2, 2, 3, 1, 5, 6, 6,
+ 7, 9, 6, 2, 4, 2, 4, 1, 1, 6,
+ 5, 4, 5, 4, 2, 1, 1, 3, 3, 4,
+ 5, 5, 6, 6, 7, 8, 4, 2, 6, 1,
+ 1, 1, 2, 2, 3, 3, 3, 1, 1, 1,
+ 1, 1, 1, 2, 1, 1,
+};
+short yydefred[] = { 1,
+ 0, 10, 0, 40, 0, 0, 0, 12, 41, 11,
+ 14, 0, 42, 43, 0, 0, 0, 0, 17, 9,
+ 186, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 106, 0, 97,
+ 95, 109, 108, 107, 110, 0, 0, 0, 0, 0,
+ 0, 0, 15, 0, 0, 0, 13, 0, 0, 0,
+ 0, 171, 170, 34, 0, 45, 46, 47, 10, 130,
+ 0, 127, 0, 122, 0, 0, 93, 0, 180, 181,
+ 0, 146, 0, 0, 144, 155, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 134, 135,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 182, 183, 0, 168, 0, 0, 86,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 124, 0, 0, 0, 84, 85,
+ 0, 0, 0, 0, 0, 0, 0, 4, 16, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 82, 83, 44, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 29, 0,
+ 30, 0, 25, 0, 26, 0, 0, 0, 36, 0,
+ 0, 136, 0, 0, 0, 0, 0, 0, 158, 159,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 185, 0, 0, 6, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 8, 131,
+ 0, 0, 0, 0, 128, 113, 0, 118, 0, 147,
+ 0, 145, 0, 0, 0, 0, 152, 0, 154, 0,
+ 0, 0, 133, 0, 0, 0, 0, 0, 160, 0,
+ 0, 0, 0, 0, 167, 0, 0, 89, 0, 0,
+ 114, 0, 119, 0, 0, 96, 0, 102, 0, 184,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 132, 0, 0, 111, 116, 0, 27, 28,
+ 23, 24, 151, 0, 0, 0, 32, 138, 0, 0,
+ 0, 0, 161, 162, 0, 0, 0, 0, 0, 153,
+ 0, 0, 112, 117, 99, 103, 101, 0, 0, 0,
+ 0, 143, 150, 31, 0, 139, 0, 140, 0, 163,
+ 164, 0, 0, 169, 104, 0, 100, 7, 129, 0,
+ 141, 0, 165, 0, 0, 0, 0, 166, 105, 33,
+ 142,
+};
+short yydgoto[] = { 1,
+ 8, 9, 89, 255, 76, 3, 10, 11, 77, 219,
+ 220, 168, 79, 80, 292, 294, 81, 198, 102, 137,
+ 208, 82, 83, 12, 84, 2, 13, 14,
+};
+short yysindex[] = { 0,
+ 0, 0, -193, 0, -54, -229, -212, 0, 0, 0,
+ 0, 383, 0, 0, 22, -232, -31, 40, 0, 0,
+ 0, -37, -36, -132, 562, -7, 107, -2, 2819, -29,
+ -23, -22, -21, 109, 111, -3, -4, 113, 123, 143,
+ 170, 172, 173, 174, 175, 186, 187, 188, 189, -34,
+ 191, 200, 2896, 202, 11, -228, -60, 0, -32, 0,
+ 0, 0, 0, 0, 0, 741, 848, 2819, 2819, 2819,
+ 2819, 912, 0, 2819, 2819, -98, 0, 98, -35, 1707,
+ -197, 0, 0, 0, -64, 0, 0, 0, 0, 0,
+ 3011, 0, 3105, 0, 204, -30, 0, -122, 0, 0,
+ -231, 0, -231, -231, 0, 0, 2819, -31, 2819, -31,
+ 2819, -31, 2819, -31, 2819, 2819, 205, 1027, 0, 0,
+ 1144, 3105, 3105, 3105, 3105, 3105, 206, 1208, 2819, 2819,
+ 2819, 2819, 2819, 0, 0, -237, 0, -237, 2819, 0,
+ -122, 2819, 131, -62, 216, 220, 2819, 2819, 2819, 2819,
+ 2819, 5754, 2819, 221, 0, -122, -197, -197, 0, 0,
+ 229, 32, -197, -197, -31, 224, -31, 0, 0, 2819,
+ 2819, 2819, 2819, 2819, 2819, 2819, 2819, 1319, 1494, 2819,
+ 2819, 2819, 2819, 1605, 1669, 1780, 1955, 2066, 2819, 2819,
+ 2130, 0, 0, 0, -113, 281, 1707, 279, 0, 5647,
+ 283, 2244, 2362, 284, 289, 293, 221, 294, 0, 65,
+ 0, 67, 0, 126, 0, 5528, 32, 2819, 0, 286,
+ -39, 0, 305, 279, 303, 303, 328, 329, 0, 0,
+ 130, 5658, 5647, 5647, 5647, 5647, 333, 303, 5658, 32,
+ 2819, 252, 2436, 2533, 74, -12, 84, -9, 221, 221,
+ 221, 2819, 0, 2725, 297, 0, 2819, 0, 221, 221,
+ 221, 221, 1707, 474, -122, -187, 2819, -257, 2819, -138,
+ 1707, 954, 117, 512, 2819, 238, 2819, 238, 2819, 486,
+ 2819, -162, 2819, -162, 51, 51, 2819, 51, 0, 0,
+ 2819, 348, 2819, 303, 0, 0, 32, 0, 32, 0,
+ 2819, 0, -31, -31, -31, -31, 0, 146, 0, 32,
+ 2819, -31, 0, 352, 279, 303, 3105, 3105, 0, 353,
+ 153, 279, 303, 303, 0, 279, 355, 0, 100, 2819,
+ 0, 32, 0, 32, 276, 0, 277, 0, 3, 0,
+ 2819, 154, 1707, 1707, 2819, 1707, 1707, 1707, 1707, 1707,
+ 1707, 221, 0, 1707, 303, 0, 0, 32, 0, 0,
+ 0, 0, 0, 362, -31, 345, 0, 0, 364, 279,
+ 365, 303, 0, 0, 367, 369, 279, 303, 370, 0,
+ 287, 114, 0, 0, 0, 0, 0, 14, -31, 1989,
+ 372, 0, 0, 0, 1027, 0, 373, 0, 303, 0,
+ 0, 374, 279, 0, 0, 292, 0, 0, 0, 381,
+ 0, 279, 0, 384, 386, -31, 393, 0, 0, 0,
+ 0,
+};
+short yyrindex[] = { 0,
+ 0, 0, 70, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3189, 3385, 0, 3460, 0, 0, 5031, 0,
+ 0, 0, 0, 3497, 0, 0, 3539, 0, 0, 0,
+ 0, 0, 3576, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 5098, 0, 0, 0, 3661, 0, 3793, 0,
+ 0, 0, 0, 0, 0, 5195, 5207, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, -11, 2796,
+ 5280, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3867, 3661, 0, 4782, 0, 0,
+ 0, 0, 0, 0, 0, 0, 396, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 385, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4825, 0, 0, 0, 3909, 3952, 0, 0, 0, 0,
+ 5347, 3661, 0, 4158, 0, 4849, 5444, 5501, 0, 0,
+ 3994, 0, 5570, 5596, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 452, 2647, 164, 0, 128, 404,
+ 0, 0, 0, 0, 0, 0, 23, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 389, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 4200, 4243,
+ 4285, 0, 0, 0, 4084, 0, 0, 0, 30, 64,
+ 79, 81, 3733, 1030, 4916, 3409, 0, 4534, 0, 4576,
+ 3830, 0, 1883, 1422, 0, 5843, 0, 5894, 0, 4957,
+ 0, 4666, 0, 4740, 4375, 4449, 0, 4491, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 394, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3878, 4698, 0, 4858, 4989, 5104, 5370, 5668,
+ 5820, 417, 0, 165, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1582,
+ 0, 0, 0, 0, 418, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+};
+short yygindex[] = { 0,
+ 0, 0, 0, -178, -17, 335, 0, 0, 0, 448,
+ 66, 0, 6067, 342, 168, -148, 430, -69, -6, -96,
+ 151, 0, 0, 0, -87, 0, 0, 0,
+};
+#define YYTABLESIZE 6408
+short yytable[] = { 87,
+ 5, 253, 91, 93, 254, 136, 16, 97, 174, 203,
+ 107, 289, 108, 110, 112, 114, 109, 111, 113, 134,
+ 209, 105, 211, 201, 213, 99, 215, 17, 145, 18,
+ 148, 174, 101, 5, 174, 121, 118, 104, 309, 237,
+ 5, 238, 312, 5, 18, 5, 174, 18, 153, 155,
+ 144, 223, 224, 225, 226, 227, 228, 174, 150, 5,
+ 148, 328, 147, 38, 4, 189, 190, 308, 191, 2,
+ 21, 5, 253, 135, 86, 254, 315, 316, 100, 146,
+ 336, 38, 85, 338, 321, 322, 323, 324, 21, 326,
+ 149, 20, 147, 20, 204, 387, 205, 206, 88, 20,
+ 20, 20, 39, 6, 22, 304, 407, 305, 174, 39,
+ 174, 7, 39, 176, 39, 178, 179, 174, 356, 19,
+ 357, 20, 22, 5, 94, 5, 5, 174, 39, 192,
+ 193, 365, 335, 187, 188, 189, 190, 19, 191, 20,
+ 178, 179, 337, 174, 4, 355, 103, 256, 115, 258,
+ 116, 5, 122, 383, 186, 384, 169, 174, 381, 364,
+ 189, 190, 123, 191, 178, 186, 306, 370, 178, 174,
+ 319, 178, 406, 174, 377, 378, 165, 166, 167, 392,
+ 178, 179, 124, 6, 189, 190, 363, 191, 186, 293,
+ 186, 7, 39, 374, 389, 39, 291, 174, 187, 188,
+ 189, 190, 15, 191, 179, 51, 391, 179, 51, 125,
+ 185, 126, 127, 128, 129, 359, 360, 361, 362, 90,
+ 92, 186, 134, 399, 367, 130, 131, 132, 133, 403,
+ 138, 170, 171, 172, 173, 170, 171, 172, 173, 139,
+ 184, 142, 194, 202, 218, 242, 229, 371, 372, 99,
+ 412, 186, 119, 241, 99, 243, 5, 5, 5, 244,
+ 5, 5, 5, 257, 174, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 186, 135, 394, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 408, 100, 120, 117, 5, 5, 100, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 143, 252,
+ 5, 290, 291, 295, 300, 39, 39, 5, 5, 39,
+ 39, 39, 301, 302, 303, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 311, 313, 293, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 98, 39, 39, 39,
+ 106, 317, 318, 325, 330, 39, 191, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 341, 353, 39,
+ 186, 314, 368, 373, 141, 380, 39, 39, 420, 320,
+ 385, 386, 393, 395, 396, 398, 327, 400, 156, 401,
+ 404, 405, 409, 411, 413, 68, 415, 176, 177, 178,
+ 179, 416, 72, 195, 418, 75, 419, 74, 186, 186,
+ 186, 186, 197, 421, 200, 183, 37, 187, 188, 189,
+ 190, 73, 191, 35, 179, 186, 186, 18, 186, 186,
+ 186, 186, 37, 186, 186, 186, 216, 48, 35, 78,
+ 410, 366, 197, 197, 197, 197, 197, 197, 0, 0,
+ 232, 233, 234, 235, 236, 0, 0, 0, 0, 0,
+ 239, 0, 369, 0, 39, 0, 0, 0, 375, 376,
+ 0, 39, 0, 379, 39, 0, 39, 157, 158, 159,
+ 160, 0, 0, 163, 164, 20, 0, 0, 69, 0,
+ 39, 186, 0, 0, 0, 263, 264, 265, 266, 268,
+ 270, 271, 272, 273, 274, 276, 278, 280, 282, 284,
+ 285, 286, 288, 0, 0, 0, 0, 397, 176, 177,
+ 178, 179, 0, 0, 402, 0, 0, 0, 0, 186,
+ 0, 0, 0, 0, 0, 0, 0, 0, 187, 188,
+ 189, 190, 0, 191, 0, 0, 0, 185, 0, 0,
+ 414, 0, 0, 0, 39, 0, 0, 39, 0, 417,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 68, 263, 0, 184, 0, 0,
+ 0, 72, 0, 0, 75, 185, 74, 0, 343, 0,
+ 344, 0, 0, 0, 0, 0, 346, 0, 347, 0,
+ 348, 0, 349, 0, 350, 0, 0, 0, 351, 0,
+ 0, 0, 0, 0, 354, 184, 0, 0, 19, 21,
+ 0, 0, 22, 23, 24, 0, 0, 0, 25, 26,
+ 27, 28, 29, 30, 31, 32, 33, 0, 197, 197,
+ 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 49, 50, 51, 52, 0,
+ 53, 54, 55, 0, 20, 0, 390, 69, 56, 0,
+ 57, 58, 59, 60, 61, 62, 63, 64, 65, 66,
+ 0, 0, 67, 0, 0, 0, 0, 39, 39, 70,
+ 71, 39, 39, 39, 0, 0, 0, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 0, 0, 0, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 0, 39,
+ 39, 39, 0, 0, 0, 0, 0, 39, 0, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 0,
+ 0, 39, 0, 68, 176, 177, 178, 179, 39, 39,
+ 72, 0, 0, 75, 0, 74, 176, 177, 178, 179,
+ 0, 182, 183, 0, 187, 188, 189, 190, 0, 191,
+ 0, 0, 0, 0, 0, 0, 187, 188, 189, 190,
+ 0, 191, 176, 177, 178, 179, 0, 0, 95, 0,
+ 0, 22, 23, 24, 0, 0, 0, 25, 26, 27,
+ 28, 29, 187, 188, 189, 190, 0, 191, 0, 34,
+ 35, 0, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 0, 53,
+ 54, 55, 0, 20, 0, 0, 69, 56, 0, 96,
+ 58, 59, 60, 61, 62, 63, 64, 65, 66, 0,
+ 68, 67, 0, 0, 0, 0, 0, 72, 70, 71,
+ 75, 0, 74, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 68, 0, 0, 0, 0, 0,
+ 0, 72, 161, 0, 75, 0, 74, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 20, 0, 0, 69, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 186, 0, 0, 0, 0, 0, 151, 0, 0,
+ 22, 23, 24, 0, 0, 0, 25, 26, 27, 28,
+ 29, 345, 0, 0, 180, 0, 181, 0, 34, 35,
+ 0, 37, 38, 39, 40, 41, 42, 43, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 69, 53, 54,
+ 55, 0, 0, 0, 0, 0, 56, 185, 152, 58,
+ 59, 60, 61, 62, 63, 64, 65, 66, 0, 68,
+ 67, 0, 0, 0, 0, 0, 72, 70, 71, 75,
+ 71, 74, 0, 71, 0, 0, 0, 184, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 71, 71, 0,
+ 71, 0, 71, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 21, 0, 0, 22, 23, 24,
+ 0, 0, 0, 25, 26, 27, 28, 29, 0, 0,
+ 0, 0, 71, 0, 0, 34, 35, 0, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 0, 53, 54, 55, 0, 0,
+ 0, 0, 69, 56, 0, 57, 58, 59, 60, 61,
+ 62, 63, 64, 65, 66, 0, 0, 67, 21, 0,
+ 0, 22, 23, 24, 70, 71, 68, 25, 26, 27,
+ 28, 29, 0, 72, 222, 0, 75, 0, 74, 34,
+ 35, 0, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 0, 53,
+ 54, 55, 0, 0, 0, 0, 175, 56, 0, 57,
+ 58, 59, 60, 61, 62, 63, 64, 65, 66, 0,
+ 0, 67, 0, 0, 0, 0, 0, 0, 70, 71,
+ 68, 0, 0, 0, 0, 0, 0, 72, 230, 0,
+ 75, 0, 74, 0, 176, 177, 178, 179, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 69,
+ 0, 182, 183, 0, 187, 188, 189, 190, 0, 191,
+ 0, 0, 19, 21, 0, 0, 22, 23, 24, 0,
+ 0, 0, 25, 26, 27, 28, 29, 0, 0, 0,
+ 71, 71, 71, 71, 34, 35, 0, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 0, 53, 54, 55, 0, 0, 0,
+ 0, 0, 56, 69, 57, 58, 59, 60, 61, 62,
+ 63, 64, 65, 66, 0, 0, 67, 0, 0, 0,
+ 0, 68, 0, 70, 71, 0, 71, 71, 72, 0,
+ 0, 75, 0, 74, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 267,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 199, 0, 0, 22, 23, 24, 0, 0, 0, 25,
+ 26, 27, 28, 29, 0, 0, 0, 0, 0, 0,
+ 0, 34, 35, 0, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 0, 53, 54, 55, 69, 0, 0, 0, 0, 56,
+ 0, 57, 58, 59, 60, 61, 62, 63, 64, 65,
+ 66, 0, 72, 67, 21, 72, 0, 22, 23, 24,
+ 70, 71, 0, 25, 26, 27, 28, 29, 0, 72,
+ 72, 0, 72, 0, 72, 34, 35, 0, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 0, 53, 54, 55, 0, 0,
+ 0, 0, 0, 56, 72, 57, 58, 59, 60, 61,
+ 62, 63, 64, 65, 66, 0, 68, 67, 0, 0,
+ 0, 0, 0, 72, 70, 71, 75, 0, 74, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 269, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 21, 0, 0, 22, 23,
+ 24, 0, 0, 0, 25, 26, 27, 28, 29, 0,
+ 0, 0, 0, 0, 0, 0, 34, 35, 0, 37,
+ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 0, 53, 54, 55, 69,
+ 0, 0, 74, 0, 56, 74, 57, 58, 59, 60,
+ 61, 62, 63, 64, 65, 66, 0, 68, 67, 74,
+ 74, 0, 74, 0, 72, 70, 71, 75, 0, 74,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 275, 0, 0, 0, 0,
+ 0, 0, 0, 0, 74, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 72, 0, 0, 0, 0, 0,
+ 0, 0, 72, 72, 72, 72, 0, 0, 0, 0,
+ 0, 68, 0, 0, 0, 0, 0, 0, 72, 0,
+ 0, 75, 0, 74, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 277,
+ 69, 0, 0, 0, 0, 0, 0, 0, 0, 72,
+ 72, 0, 0, 0, 186, 0, 0, 0, 72, 72,
+ 21, 0, 0, 22, 23, 24, 0, 0, 0, 25,
+ 26, 27, 28, 29, 0, 0, 0, 180, 0, 181,
+ 0, 34, 35, 0, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 0, 53, 54, 55, 69, 0, 0, 0, 0, 56,
+ 185, 57, 58, 59, 60, 61, 62, 63, 64, 65,
+ 66, 0, 68, 67, 0, 0, 0, 0, 0, 72,
+ 70, 71, 75, 0, 74, 0, 0, 0, 0, 0,
+ 184, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 279, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 74, 74, 74, 74, 0, 0, 0, 0,
+ 0, 21, 0, 0, 22, 23, 24, 0, 0, 0,
+ 25, 26, 27, 28, 29, 0, 0, 0, 0, 0,
+ 0, 0, 34, 35, 0, 37, 38, 39, 40, 41,
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 0, 53, 54, 55, 69, 0, 0, 74, 74,
+ 56, 0, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 0, 73, 67, 21, 73, 0, 22, 23,
+ 24, 70, 71, 0, 25, 26, 27, 28, 29, 0,
+ 73, 73, 0, 73, 0, 73, 34, 35, 0, 37,
+ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 0, 53, 54, 55, 175,
+ 0, 0, 0, 0, 56, 73, 57, 58, 59, 60,
+ 61, 62, 63, 64, 65, 66, 0, 68, 67, 0,
+ 0, 0, 0, 0, 72, 70, 71, 75, 0, 74,
+ 0, 0, 0, 0, 0, 0, 0, 176, 177, 178,
+ 179, 0, 0, 0, 0, 281, 0, 0, 0, 0,
+ 0, 0, 0, 0, 182, 183, 186, 187, 188, 189,
+ 190, 0, 191, 0, 0, 0, 21, 0, 0, 22,
+ 23, 24, 0, 0, 0, 25, 26, 27, 28, 29,
+ 0, 181, 0, 0, 0, 0, 0, 34, 35, 0,
+ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+ 47, 48, 49, 50, 51, 52, 0, 53, 54, 55,
+ 69, 0, 185, 0, 0, 56, 0, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 0, 68, 67,
+ 0, 0, 0, 0, 0, 72, 70, 71, 75, 0,
+ 74, 0, 184, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 283, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 73, 0, 0, 0, 0,
+ 0, 0, 0, 73, 73, 73, 73, 0, 0, 0,
+ 0, 0, 68, 0, 0, 0, 0, 0, 0, 72,
+ 0, 0, 75, 0, 74, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 287, 69, 0, 0, 0, 0, 0, 0, 0, 0,
+ 73, 0, 0, 0, 0, 0, 0, 0, 0, 73,
+ 73, 21, 0, 0, 22, 23, 24, 0, 0, 0,
+ 25, 26, 27, 28, 29, 0, 0, 0, 0, 0,
+ 0, 0, 34, 35, 0, 37, 38, 39, 40, 41,
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 175, 53, 54, 55, 69, 0, 0, 0, 0,
+ 56, 0, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 0, 0, 67, 0, 68, 0, 0, 0,
+ 0, 70, 71, 72, 296, 0, 75, 0, 74, 176,
+ 177, 178, 179, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 182, 183, 0, 187,
+ 188, 189, 190, 0, 191, 0, 0, 0, 0, 0,
+ 0, 0, 21, 0, 0, 22, 23, 24, 0, 0,
+ 0, 25, 26, 27, 28, 29, 0, 0, 0, 0,
+ 0, 0, 0, 34, 35, 0, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
+ 51, 52, 0, 53, 54, 55, 0, 0, 0, 69,
+ 0, 56, 0, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 0, 0, 67, 21, 0, 0, 22,
+ 23, 24, 70, 71, 68, 25, 26, 27, 28, 29,
+ 0, 72, 298, 0, 75, 0, 74, 34, 35, 0,
+ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+ 47, 48, 49, 50, 51, 52, 0, 53, 54, 55,
+ 0, 0, 0, 0, 0, 56, 0, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 0, 0, 67,
+ 0, 0, 0, 0, 0, 0, 70, 71, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 68, 0,
+ 0, 0, 0, 0, 0, 72, 331, 0, 75, 0,
+ 74, 0, 0, 0, 0, 0, 0, 69, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 21, 0, 0, 22, 23, 24, 0, 0, 0, 25,
+ 26, 27, 28, 29, 0, 0, 0, 0, 0, 0,
+ 0, 34, 35, 0, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 0, 53, 54, 55, 0, 0, 0, 0, 0, 56,
+ 0, 57, 58, 59, 60, 61, 62, 63, 64, 65,
+ 66, 69, 0, 67, 0, 68, 0, 0, 0, 0,
+ 70, 71, 72, 333, 0, 75, 0, 74, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 21, 0,
+ 0, 22, 23, 24, 0, 0, 0, 25, 26, 27,
+ 28, 29, 0, 0, 0, 0, 0, 0, 0, 34,
+ 35, 0, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 69, 53,
+ 54, 55, 0, 0, 0, 0, 0, 56, 0, 57,
+ 58, 59, 60, 61, 62, 63, 64, 65, 66, 0,
+ 0, 67, 0, 0, 186, 0, 0, 0, 70, 71,
+ 178, 0, 21, 0, 0, 22, 23, 24, 0, 0,
+ 0, 25, 26, 27, 28, 29, 0, 186, 0, 186,
+ 0, 0, 0, 34, 35, 0, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
+ 51, 52, 0, 53, 54, 55, 0, 0, 0, 0,
+ 186, 56, 0, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 0, 0, 67, 0, 68, 0, 0,
+ 0, 0, 70, 71, 72, 340, 0, 75, 0, 74,
+ 186, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 21,
+ 0, 0, 22, 23, 24, 0, 0, 0, 25, 26,
+ 27, 28, 29, 0, 0, 0, 0, 0, 0, 0,
+ 34, 35, 0, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 49, 50, 51, 52, 0,
+ 53, 54, 55, 0, 0, 0, 50, 0, 56, 50,
+ 57, 58, 59, 60, 61, 62, 63, 64, 65, 66,
+ 69, 68, 67, 50, 50, 0, 0, 0, 72, 70,
+ 71, 75, 0, 74, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 50, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 186,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 68, 0,
+ 0, 0, 0, 0, 0, 72, 0, 0, 75, 0,
+ 74, 0, 0, 0, 69, 0, 0, 186, 186, 186,
+ 186, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 186, 186, 0, 186, 186, 186,
+ 186, 0, 186, 186, 186, 0, 0, 0, 0, 0,
+ 0, 21, 0, 0, 22, 23, 24, 0, 0, 0,
+ 25, 26, 27, 28, 29, 0, 0, 0, 0, 0,
+ 0, 0, 34, 35, 0, 37, 38, 39, 40, 41,
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 69, 53, 54, 55, 0, 0, 0, 0, 0,
+ 56, 0, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 0, 68, 67, 0, 0, 0, 0, 0,
+ 72, 70, 71, 75, 0, 74, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 50, 50, 50, 50,
+ 0, 0, 0, 0, 0, 21, 0, 0, 22, 23,
+ 24, 0, 0, 0, 25, 26, 27, 28, 29, 0,
+ 0, 0, 0, 0, 0, 0, 34, 35, 0, 37,
+ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 0, 53, 54, 55, 0,
+ 0, 0, 50, 50, 56, 0, 57, 58, 59, 60,
+ 61, 62, 63, 64, 65, 66, 69, 68, 67, 0,
+ 0, 0, 0, 0, 72, 70, 71, 75, 0, 74,
+ 0, 0, 140, 0, 0, 22, 23, 24, 0, 0,
+ 0, 25, 26, 27, 28, 29, 0, 0, 0, 0,
+ 0, 0, 0, 34, 35, 0, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
+ 51, 52, 0, 53, 54, 55, 0, 0, 0, 0,
+ 0, 56, 0, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 0, 0, 67, 0, 0, 0, 0,
+ 0, 0, 70, 71, 0, 0, 126, 0, 0, 126,
+ 69, 0, 126, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 126, 126, 0, 126,
+ 0, 126, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 196, 0, 0,
+ 22, 23, 24, 0, 0, 0, 25, 26, 27, 28,
+ 29, 126, 126, 0, 0, 0, 0, 0, 34, 35,
+ 0, 37, 38, 39, 40, 41, 42, 43, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 0, 53, 54,
+ 55, 0, 126, 0, 0, 0, 56, 0, 57, 58,
+ 59, 60, 61, 62, 63, 64, 65, 66, 0, 0,
+ 67, 0, 0, 0, 0, 0, 0, 70, 71, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 199, 0, 0, 22, 23, 24, 0, 0, 0,
+ 25, 26, 27, 28, 29, 0, 0, 0, 0, 0,
+ 0, 0, 34, 35, 0, 37, 38, 39, 40, 41,
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 0, 53, 54, 55, 0, 0, 0, 0, 0,
+ 56, 0, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 121, 0, 67, 121, 0, 0, 121, 0,
+ 0, 70, 71, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 121, 121, 0, 121, 67, 121, 0, 67,
+ 0, 126, 67, 0, 0, 0, 0, 0, 0, 126,
+ 126, 126, 126, 0, 0, 0, 67, 67, 0, 67,
+ 0, 67, 0, 0, 0, 0, 0, 121, 121, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 126,
+ 126, 126, 126, 0, 0, 0, 0, 148, 0, 0,
+ 148, 67, 67, 148, 0, 0, 126, 126, 121, 126,
+ 126, 126, 126, 0, 126, 126, 126, 148, 148, 0,
+ 148, 0, 148, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 67, 0, 149, 0, 0, 149, 0, 0,
+ 149, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 148, 148, 149, 149, 0, 149, 0, 149,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 137, 0, 0, 137,
+ 0, 0, 137, 148, 0, 0, 0, 0, 0, 149,
+ 149, 0, 0, 0, 0, 0, 137, 137, 0, 137,
+ 0, 137, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 157, 0, 0, 157, 0, 0, 157,
+ 149, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 137, 137, 157, 157, 0, 157, 0, 157, 0,
+ 0, 0, 0, 0, 0, 0, 0, 121, 0, 0,
+ 0, 0, 0, 0, 0, 121, 121, 121, 121, 0,
+ 0, 0, 137, 0, 0, 0, 0, 0, 157, 157,
+ 0, 67, 0, 0, 0, 0, 0, 0, 0, 67,
+ 67, 67, 67, 0, 0, 121, 121, 121, 121, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 94, 157,
+ 0, 94, 121, 121, 94, 121, 121, 121, 121, 0,
+ 121, 121, 121, 0, 0, 0, 0, 0, 94, 94,
+ 0, 94, 148, 94, 0, 0, 67, 67, 0, 0,
+ 148, 148, 148, 148, 0, 67, 67, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 94, 94, 0, 0, 0, 0, 149,
+ 148, 148, 148, 148, 0, 0, 0, 149, 149, 149,
+ 149, 0, 0, 49, 0, 0, 49, 148, 148, 0,
+ 148, 148, 148, 148, 94, 148, 148, 148, 0, 0,
+ 49, 49, 0, 0, 0, 0, 0, 149, 149, 149,
+ 149, 137, 0, 0, 0, 0, 0, 0, 0, 137,
+ 137, 137, 137, 0, 149, 149, 0, 149, 149, 149,
+ 149, 0, 149, 149, 149, 49, 0, 0, 0, 0,
+ 98, 0, 0, 98, 0, 0, 98, 0, 157, 137,
+ 137, 137, 137, 0, 0, 0, 157, 157, 157, 157,
+ 98, 98, 0, 98, 0, 98, 137, 137, 0, 137,
+ 137, 137, 137, 0, 137, 137, 137, 0, 0, 0,
+ 52, 0, 0, 52, 0, 0, 157, 157, 157, 157,
+ 0, 0, 0, 0, 0, 98, 98, 52, 52, 0,
+ 0, 0, 0, 157, 157, 0, 157, 157, 157, 157,
+ 0, 157, 157, 157, 186, 0, 0, 186, 0, 0,
+ 186, 0, 0, 0, 0, 0, 98, 0, 54, 0,
+ 0, 54, 52, 94, 186, 186, 0, 186, 0, 186,
+ 0, 94, 94, 94, 94, 54, 54, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 115, 0, 0, 115,
+ 0, 0, 115, 0, 0, 0, 0, 0, 0, 186,
+ 186, 94, 94, 94, 94, 0, 115, 115, 0, 115,
+ 54, 115, 0, 0, 0, 0, 0, 0, 94, 94,
+ 0, 94, 94, 94, 94, 0, 94, 94, 94, 120,
+ 186, 0, 120, 0, 0, 120, 0, 0, 0, 0,
+ 0, 115, 115, 49, 49, 49, 49, 0, 0, 120,
+ 120, 0, 120, 0, 120, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 91, 115, 0, 91, 0, 0, 91, 0, 0,
+ 0, 0, 0, 0, 120, 120, 0, 0, 0, 0,
+ 0, 91, 91, 0, 91, 98, 91, 0, 0, 49,
+ 49, 0, 0, 98, 98, 98, 98, 0, 0, 0,
+ 0, 0, 0, 0, 0, 120, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 91, 91, 0, 0,
+ 0, 0, 0, 98, 98, 98, 98, 0, 0, 0,
+ 52, 52, 52, 52, 0, 0, 0, 0, 0, 0,
+ 98, 98, 0, 98, 98, 98, 98, 91, 98, 98,
+ 98, 90, 0, 0, 90, 0, 0, 90, 0, 186,
+ 0, 0, 0, 0, 0, 0, 0, 186, 186, 186,
+ 186, 90, 90, 0, 90, 0, 90, 0, 54, 54,
+ 54, 54, 0, 0, 0, 0, 52, 52, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 186, 186, 186,
+ 186, 115, 0, 0, 0, 0, 90, 90, 0, 115,
+ 115, 115, 115, 0, 186, 186, 0, 186, 186, 186,
+ 186, 0, 186, 186, 186, 173, 0, 0, 173, 0,
+ 0, 0, 0, 0, 54, 54, 0, 90, 0, 115,
+ 115, 115, 115, 0, 120, 173, 173, 0, 173, 0,
+ 173, 0, 120, 120, 120, 120, 115, 115, 0, 115,
+ 115, 115, 115, 0, 115, 115, 115, 175, 0, 0,
+ 175, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 173, 173, 120, 120, 120, 120, 91, 175, 175, 0,
+ 175, 0, 175, 0, 91, 91, 91, 91, 0, 120,
+ 120, 0, 120, 120, 120, 120, 0, 120, 120, 120,
+ 176, 173, 0, 176, 0, 0, 0, 0, 0, 0,
+ 0, 0, 175, 175, 91, 91, 91, 91, 0, 0,
+ 176, 176, 0, 176, 0, 176, 0, 0, 0, 0,
+ 0, 91, 91, 0, 91, 91, 91, 91, 0, 91,
+ 91, 91, 177, 175, 0, 177, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 176, 176, 0, 0, 0,
+ 0, 0, 177, 177, 0, 177, 90, 177, 0, 0,
+ 0, 0, 0, 0, 90, 90, 90, 90, 0, 0,
+ 0, 0, 0, 0, 0, 0, 176, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 177, 177, 0,
+ 0, 0, 0, 0, 90, 90, 90, 90, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 90, 90, 0, 90, 90, 90, 90, 177, 90,
+ 90, 90, 75, 0, 0, 75, 0, 0, 75, 0,
+ 173, 0, 0, 0, 0, 0, 0, 0, 173, 173,
+ 173, 173, 75, 75, 0, 75, 0, 75, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 173, 173,
+ 173, 173, 175, 0, 0, 0, 0, 75, 75, 0,
+ 175, 175, 175, 175, 0, 173, 173, 0, 173, 173,
+ 173, 173, 0, 173, 173, 173, 76, 0, 0, 76,
+ 0, 0, 76, 0, 0, 0, 0, 0, 75, 0,
+ 175, 175, 175, 175, 0, 176, 76, 76, 0, 76,
+ 0, 76, 0, 176, 176, 176, 176, 175, 175, 0,
+ 175, 175, 175, 175, 0, 175, 175, 175, 61, 0,
+ 0, 61, 0, 0, 61, 0, 0, 0, 0, 0,
+ 0, 76, 76, 176, 176, 176, 176, 177, 61, 61,
+ 0, 61, 0, 61, 0, 177, 177, 177, 177, 0,
+ 176, 176, 0, 176, 176, 176, 176, 0, 176, 176,
+ 176, 62, 76, 0, 62, 0, 0, 62, 0, 0,
+ 0, 0, 0, 61, 61, 177, 177, 177, 177, 0,
+ 0, 62, 62, 0, 62, 0, 62, 0, 0, 0,
+ 0, 0, 177, 177, 0, 177, 177, 177, 177, 0,
+ 177, 177, 177, 63, 61, 0, 63, 0, 0, 63,
+ 0, 0, 0, 0, 0, 0, 62, 62, 0, 0,
+ 0, 0, 0, 63, 63, 0, 63, 75, 63, 0,
+ 0, 0, 0, 0, 0, 75, 75, 75, 75, 0,
+ 0, 0, 0, 0, 0, 0, 0, 62, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 63, 63,
+ 0, 0, 0, 0, 0, 75, 75, 75, 75, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 75, 75, 0, 75, 75, 75, 75, 63,
+ 0, 75, 75, 64, 0, 0, 64, 0, 0, 64,
+ 0, 76, 0, 0, 0, 0, 0, 0, 0, 76,
+ 76, 76, 76, 64, 64, 0, 64, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 55, 0,
+ 0, 55, 0, 0, 0, 0, 0, 0, 0, 76,
+ 76, 76, 76, 61, 0, 55, 55, 0, 64, 64,
+ 0, 61, 61, 61, 61, 0, 76, 76, 0, 76,
+ 76, 76, 76, 0, 0, 76, 76, 65, 0, 0,
+ 65, 0, 0, 65, 0, 0, 0, 0, 0, 64,
+ 55, 61, 61, 61, 61, 0, 62, 65, 65, 0,
+ 65, 0, 65, 0, 62, 62, 62, 62, 61, 61,
+ 0, 61, 61, 61, 61, 0, 0, 61, 61, 92,
+ 0, 0, 92, 0, 0, 92, 0, 0, 0, 0,
+ 0, 0, 65, 65, 62, 62, 62, 62, 63, 92,
+ 92, 0, 92, 0, 92, 0, 63, 63, 63, 63,
+ 0, 62, 62, 0, 62, 62, 0, 0, 0, 0,
+ 62, 62, 87, 65, 0, 87, 0, 0, 87, 0,
+ 0, 0, 0, 0, 92, 92, 63, 63, 0, 63,
+ 0, 0, 87, 87, 0, 87, 125, 87, 0, 125,
+ 0, 0, 125, 63, 63, 0, 63, 63, 60, 0,
+ 0, 60, 63, 63, 0, 92, 125, 125, 0, 125,
+ 0, 125, 0, 0, 0, 60, 60, 87, 87, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 64, 0,
+ 0, 0, 0, 0, 0, 0, 64, 64, 64, 64,
+ 0, 125, 125, 0, 0, 0, 0, 0, 87, 0,
+ 60, 0, 0, 66, 0, 0, 66, 0, 0, 66,
+ 0, 0, 0, 0, 0, 0, 64, 64, 55, 55,
+ 55, 55, 125, 66, 66, 0, 66, 0, 66, 0,
+ 0, 0, 0, 64, 64, 0, 64, 64, 0, 0,
+ 0, 0, 64, 64, 68, 0, 0, 68, 0, 0,
+ 68, 0, 65, 0, 0, 0, 0, 0, 66, 66,
+ 65, 65, 65, 65, 68, 68, 0, 68, 0, 68,
+ 0, 0, 0, 0, 55, 55, 0, 0, 0, 59,
+ 0, 0, 59, 0, 0, 0, 0, 0, 0, 66,
+ 65, 65, 0, 0, 92, 0, 59, 59, 0, 68,
+ 68, 0, 92, 92, 92, 92, 0, 65, 65, 0,
+ 65, 65, 0, 0, 0, 0, 65, 65, 156, 0,
+ 0, 156, 0, 0, 156, 0, 0, 0, 0, 0,
+ 68, 59, 92, 92, 0, 0, 0, 87, 156, 156,
+ 0, 156, 0, 156, 0, 87, 87, 87, 87, 92,
+ 92, 0, 0, 0, 0, 0, 0, 0, 92, 92,
+ 0, 125, 0, 0, 0, 0, 0, 0, 0, 125,
+ 125, 125, 125, 156, 156, 87, 87, 0, 60, 60,
+ 60, 60, 0, 0, 0, 88, 0, 0, 88, 0,
+ 0, 88, 87, 87, 58, 0, 0, 58, 0, 125,
+ 125, 87, 87, 0, 156, 88, 88, 0, 88, 0,
+ 88, 58, 58, 0, 0, 0, 125, 125, 0, 0,
+ 0, 0, 0, 0, 0, 125, 125, 0, 66, 0,
+ 0, 0, 0, 0, 60, 60, 66, 66, 66, 66,
+ 88, 88, 0, 0, 0, 0, 58, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 66, 0, 68,
+ 0, 88, 0, 0, 0, 0, 0, 68, 68, 68,
+ 68, 0, 172, 66, 66, 172, 0, 0, 172, 0,
+ 0, 0, 66, 66, 123, 0, 0, 123, 0, 0,
+ 123, 0, 172, 172, 0, 172, 0, 172, 0, 59,
+ 59, 59, 59, 0, 123, 123, 0, 123, 0, 123,
+ 0, 0, 0, 0, 68, 68, 0, 0, 0, 0,
+ 0, 0, 0, 68, 68, 0, 0, 172, 172, 0,
+ 0, 0, 0, 156, 0, 0, 0, 0, 0, 123,
+ 123, 156, 156, 156, 156, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 59, 59, 77, 172, 0,
+ 77, 0, 0, 77, 0, 0, 0, 0, 0, 0,
+ 123, 156, 156, 156, 156, 0, 0, 77, 77, 0,
+ 77, 0, 77, 0, 0, 0, 0, 0, 156, 156,
+ 0, 156, 156, 156, 156, 0, 156, 0, 0, 0,
+ 88, 0, 0, 0, 0, 0, 0, 0, 88, 88,
+ 88, 88, 77, 77, 58, 58, 58, 58, 0, 0,
+ 0, 0, 0, 0, 174, 0, 0, 174, 0, 0,
+ 174, 0, 0, 0, 0, 0, 0, 0, 88, 88,
+ 88, 88, 0, 77, 174, 174, 0, 174, 0, 174,
+ 56, 0, 0, 56, 0, 88, 88, 0, 88, 88,
+ 88, 88, 0, 88, 0, 0, 0, 56, 56, 0,
+ 58, 58, 0, 0, 0, 0, 0, 0, 0, 174,
+ 174, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 172, 0, 0,
+ 0, 0, 56, 0, 0, 172, 172, 172, 172, 123,
+ 174, 0, 0, 0, 0, 0, 0, 123, 123, 123,
+ 123, 80, 0, 0, 80, 0, 0, 80, 0, 0,
+ 0, 0, 0, 0, 0, 172, 172, 172, 172, 0,
+ 0, 80, 80, 0, 80, 0, 80, 123, 123, 123,
+ 123, 0, 172, 172, 0, 172, 172, 172, 172, 0,
+ 172, 0, 0, 0, 123, 123, 0, 123, 123, 123,
+ 123, 0, 123, 0, 0, 0, 80, 80, 81, 0,
+ 0, 81, 77, 0, 81, 0, 0, 0, 0, 0,
+ 77, 77, 77, 77, 0, 0, 0, 0, 81, 81,
+ 0, 81, 0, 81, 0, 186, 0, 80, 307, 0,
+ 0, 293, 0, 0, 0, 0, 0, 0, 0, 0,
+ 77, 77, 77, 77, 0, 0, 0, 0, 180, 0,
+ 181, 0, 0, 81, 81, 0, 0, 77, 77, 0,
+ 77, 77, 77, 77, 0, 77, 0, 78, 0, 174,
+ 78, 0, 0, 78, 0, 0, 0, 174, 174, 174,
+ 174, 185, 0, 0, 81, 0, 0, 78, 78, 0,
+ 78, 0, 78, 79, 0, 0, 79, 0, 0, 79,
+ 56, 56, 56, 56, 0, 0, 0, 174, 174, 174,
+ 174, 184, 0, 79, 79, 0, 79, 0, 79, 0,
+ 0, 0, 78, 78, 174, 174, 0, 174, 174, 174,
+ 174, 0, 174, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 186, 0, 0, 0, 79, 79,
+ 293, 0, 0, 78, 0, 186, 56, 56, 0, 0,
+ 0, 291, 0, 0, 0, 0, 80, 180, 57, 181,
+ 0, 57, 0, 0, 80, 80, 80, 80, 180, 79,
+ 181, 0, 0, 0, 0, 57, 57, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 185, 0, 0, 0, 80, 80, 80, 80, 0, 0,
+ 0, 185, 0, 0, 0, 0, 0, 0, 0, 0,
+ 57, 80, 80, 81, 80, 80, 80, 80, 0, 80,
+ 184, 81, 81, 81, 81, 0, 0, 0, 0, 0,
+ 0, 184, 0, 0, 0, 0, 68, 0, 0, 0,
+ 175, 0, 0, 72, 0, 0, 75, 0, 74, 0,
+ 0, 81, 81, 81, 81, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 81, 81,
+ 0, 81, 81, 81, 81, 0, 81, 0, 176, 177,
+ 178, 179, 78, 0, 0, 0, 0, 0, 0, 0,
+ 78, 78, 78, 78, 148, 182, 183, 0, 187, 188,
+ 189, 190, 0, 191, 0, 0, 0, 0, 79, 0,
+ 53, 0, 0, 53, 0, 0, 79, 79, 79, 79,
+ 78, 78, 78, 78, 0, 0, 147, 53, 53, 69,
+ 0, 0, 0, 70, 0, 0, 70, 78, 78, 0,
+ 78, 78, 78, 78, 0, 78, 79, 79, 79, 79,
+ 70, 70, 0, 70, 0, 70, 0, 0, 0, 175,
+ 0, 0, 53, 79, 79, 0, 79, 79, 79, 79,
+ 175, 79, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 69, 70, 70, 69, 57, 57,
+ 57, 57, 0, 0, 0, 0, 0, 176, 177, 178,
+ 179, 69, 69, 0, 69, 0, 69, 0, 176, 177,
+ 178, 179, 0, 0, 182, 183, 70, 187, 188, 189,
+ 190, 0, 191, 0, 0, 182, 183, 0, 187, 188,
+ 189, 190, 0, 191, 0, 0, 69, 69, 0, 0,
+ 0, 0, 0, 0, 57, 57, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 21, 0, 0, 22, 23, 24, 0, 69, 0, 25,
+ 26, 27, 28, 29, 0, 0, 0, 0, 0, 0,
+ 0, 34, 35, 0, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 0, 53, 54, 55, 0, 0, 0, 0, 0, 56,
+ 0, 57, 58, 59, 60, 61, 62, 63, 64, 65,
+ 66, 0, 0, 67, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 53, 53, 53, 53, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 70, 0, 0, 0, 0,
+ 0, 0, 0, 70, 70, 70, 70, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 154, 0, 0, 0, 0, 0, 162, 0,
+ 0, 0, 0, 0, 0, 0, 53, 53, 0, 0,
+ 0, 0, 0, 0, 0, 0, 69, 0, 0, 0,
+ 70, 70, 0, 0, 69, 69, 69, 69, 0, 70,
+ 70, 0, 0, 207, 0, 210, 0, 212, 0, 214,
+ 0, 0, 217, 0, 221, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 231, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 240, 0,
+ 0, 69, 69, 245, 246, 247, 248, 249, 250, 251,
+ 69, 69, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 259, 260, 261, 262,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 297, 299,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 310, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 329, 0, 332,
+ 334, 0, 0, 0, 0, 0, 0, 0, 339, 0,
+ 0, 0, 0, 342, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 352, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 358, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 207, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 382, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 388,
+};
+short yycheck[] = { 17,
+ 0, 41, 40, 40, 44, 40, 61, 25, 44, 40,
+ 40, 125, 30, 31, 32, 33, 40, 40, 40, 257,
+ 108, 28, 110, 93, 112, 257, 114, 257, 257, 41,
+ 91, 44, 40, 33, 44, 40, 40, 40, 217, 136,
+ 40, 138, 221, 43, 257, 45, 44, 59, 66, 67,
+ 40, 121, 122, 123, 124, 125, 126, 44, 91, 59,
+ 91, 240, 123, 41, 258, 323, 324, 216, 326, 0,
+ 41, 265, 41, 311, 307, 44, 225, 226, 310, 308,
+ 93, 59, 61, 93, 233, 234, 235, 236, 59, 238,
+ 123, 123, 123, 123, 101, 93, 103, 104, 59, 123,
+ 123, 123, 33, 297, 41, 41, 93, 41, 44, 40,
+ 44, 305, 43, 301, 45, 303, 304, 44, 297, 41,
+ 299, 41, 59, 123, 257, 125, 126, 44, 59, 327,
+ 328, 310, 59, 321, 322, 323, 324, 59, 326, 59,
+ 303, 304, 59, 44, 258, 294, 40, 165, 40, 167,
+ 40, 265, 40, 332, 38, 334, 59, 44, 59, 308,
+ 323, 324, 40, 326, 303, 38, 41, 316, 41, 44,
+ 41, 44, 59, 44, 323, 324, 275, 276, 277, 358,
+ 303, 304, 40, 297, 323, 324, 41, 326, 61, 44,
+ 63, 305, 123, 41, 41, 126, 44, 44, 321, 322,
+ 323, 324, 257, 326, 41, 41, 355, 44, 44, 40,
+ 94, 40, 40, 40, 40, 303, 304, 305, 306, 257,
+ 257, 94, 257, 372, 312, 40, 40, 40, 40, 378,
+ 40, 271, 272, 273, 274, 271, 272, 273, 274, 40,
+ 124, 40, 307, 40, 40, 308, 41, 317, 318, 257,
+ 399, 124, 257, 123, 257, 40, 256, 257, 258, 40,
+ 260, 261, 262, 40, 44, 265, 266, 267, 268, 269,
+ 270, 271, 272, 273, 274, 38, 311, 365, 278, 279,
+ 280, 281, 282, 283, 284, 285, 286, 287, 288, 289,
+ 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
+ 300, 389, 310, 308, 308, 305, 306, 310, 308, 309,
+ 310, 311, 312, 313, 314, 315, 316, 317, 308, 91,
+ 320, 41, 44, 41, 41, 256, 257, 327, 328, 260,
+ 261, 262, 44, 41, 41, 266, 267, 268, 269, 270,
+ 271, 272, 273, 274, 59, 41, 44, 278, 279, 280,
+ 281, 282, 283, 284, 285, 286, 287, 288, 289, 290,
+ 291, 292, 293, 294, 295, 296, 25, 298, 299, 300,
+ 29, 44, 44, 41, 123, 306, 326, 308, 309, 310,
+ 311, 312, 313, 314, 315, 316, 317, 91, 41, 320,
+ 263, 224, 41, 41, 53, 41, 327, 328, 416, 232,
+ 125, 125, 41, 59, 41, 41, 239, 41, 67, 41,
+ 41, 125, 41, 41, 41, 33, 125, 301, 302, 303,
+ 304, 41, 40, 89, 41, 43, 41, 45, 301, 302,
+ 303, 304, 91, 41, 93, 319, 41, 321, 322, 323,
+ 324, 59, 326, 59, 41, 318, 319, 59, 321, 322,
+ 323, 324, 59, 326, 327, 328, 115, 41, 41, 12,
+ 395, 311, 121, 122, 123, 124, 125, 126, -1, -1,
+ 129, 130, 131, 132, 133, -1, -1, -1, -1, -1,
+ 139, -1, 315, -1, 33, -1, -1, -1, 321, 322,
+ -1, 40, -1, 326, 43, -1, 45, 68, 69, 70,
+ 71, -1, -1, 74, 75, 123, -1, -1, 126, -1,
+ 59, 38, -1, -1, -1, 174, 175, 176, 177, 178,
+ 179, 180, 181, 182, 183, 184, 185, 186, 187, 188,
+ 189, 190, 191, -1, -1, -1, -1, 370, 301, 302,
+ 303, 304, -1, -1, 377, -1, -1, -1, -1, 38,
+ -1, -1, -1, -1, -1, -1, -1, -1, 321, 322,
+ 323, 324, -1, 326, -1, -1, -1, 94, -1, -1,
+ 403, -1, -1, -1, 123, -1, -1, 126, -1, 412,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 33, 254, -1, 124, -1, -1,
+ -1, 40, -1, -1, 43, 94, 45, -1, 267, -1,
+ 269, -1, -1, -1, -1, -1, 275, -1, 277, -1,
+ 279, -1, 281, -1, 283, -1, -1, -1, 287, -1,
+ -1, -1, -1, -1, 293, 124, -1, -1, 256, 257,
+ -1, -1, 260, 261, 262, -1, -1, -1, 266, 267,
+ 268, 269, 270, 271, 272, 273, 274, -1, 317, 318,
+ 278, 279, 280, 281, 282, 283, 284, 285, 286, 287,
+ 288, 289, 290, 291, 292, 293, 294, 295, 296, -1,
+ 298, 299, 300, -1, 123, -1, 345, 126, 306, -1,
+ 308, 309, 310, 311, 312, 313, 314, 315, 316, 317,
+ -1, -1, 320, -1, -1, -1, -1, 256, 257, 327,
+ 328, 260, 261, 262, -1, -1, -1, 266, 267, 268,
+ 269, 270, 271, 272, 273, 274, -1, -1, -1, 278,
+ 279, 280, 281, 282, 283, 284, 285, 286, 287, 288,
+ 289, 290, 291, 292, 293, 294, 295, 296, -1, 298,
+ 299, 300, -1, -1, -1, -1, -1, 306, -1, 308,
+ 309, 310, 311, 312, 313, 314, 315, 316, 317, -1,
+ -1, 320, -1, 33, 301, 302, 303, 304, 327, 328,
+ 40, -1, -1, 43, -1, 45, 301, 302, 303, 304,
+ -1, 318, 319, -1, 321, 322, 323, 324, -1, 326,
+ -1, -1, -1, -1, -1, -1, 321, 322, 323, 324,
+ -1, 326, 301, 302, 303, 304, -1, -1, 257, -1,
+ -1, 260, 261, 262, -1, -1, -1, 266, 267, 268,
+ 269, 270, 321, 322, 323, 324, -1, 326, -1, 278,
+ 279, -1, 281, 282, 283, 284, 285, 286, 287, 288,
+ 289, 290, 291, 292, 293, 294, 295, 296, -1, 298,
+ 299, 300, -1, 123, -1, -1, 126, 306, -1, 308,
+ 309, 310, 311, 312, 313, 314, 315, 316, 317, -1,
+ 33, 320, -1, -1, -1, -1, -1, 40, 327, 328,
+ 43, -1, 45, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 33, -1, -1, -1, -1, -1,
+ -1, 40, 41, -1, 43, -1, 45, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 123, -1, -1, 126, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 38, -1, -1, -1, -1, -1, 257, -1, -1,
+ 260, 261, 262, -1, -1, -1, 266, 267, 268, 269,
+ 270, 58, -1, -1, 61, -1, 63, -1, 278, 279,
+ -1, 281, 282, 283, 284, 285, 286, 287, 288, 289,
+ 290, 291, 292, 293, 294, 295, 296, 126, 298, 299,
+ 300, -1, -1, -1, -1, -1, 306, 94, 308, 309,
+ 310, 311, 312, 313, 314, 315, 316, 317, -1, 33,
+ 320, -1, -1, -1, -1, -1, 40, 327, 328, 43,
+ 41, 45, -1, 44, -1, -1, -1, 124, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 58, 59, -1,
+ 61, -1, 63, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 257, -1, -1, 260, 261, 262,
+ -1, -1, -1, 266, 267, 268, 269, 270, -1, -1,
+ -1, -1, 93, -1, -1, 278, 279, -1, 281, 282,
+ 283, 284, 285, 286, 287, 288, 289, 290, 291, 292,
+ 293, 294, 295, 296, -1, 298, 299, 300, -1, -1,
+ -1, -1, 126, 306, -1, 308, 309, 310, 311, 312,
+ 313, 314, 315, 316, 317, -1, -1, 320, 257, -1,
+ -1, 260, 261, 262, 327, 328, 33, 266, 267, 268,
+ 269, 270, -1, 40, 41, -1, 43, -1, 45, 278,
+ 279, -1, 281, 282, 283, 284, 285, 286, 287, 288,
+ 289, 290, 291, 292, 293, 294, 295, 296, -1, 298,
+ 299, 300, -1, -1, -1, -1, 263, 306, -1, 308,
+ 309, 310, 311, 312, 313, 314, 315, 316, 317, -1,
+ -1, 320, -1, -1, -1, -1, -1, -1, 327, 328,
+ 33, -1, -1, -1, -1, -1, -1, 40, 41, -1,
+ 43, -1, 45, -1, 301, 302, 303, 304, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 126,
+ -1, 318, 319, -1, 321, 322, 323, 324, -1, 326,
+ -1, -1, 256, 257, -1, -1, 260, 261, 262, -1,
+ -1, -1, 266, 267, 268, 269, 270, -1, -1, -1,
+ 271, 272, 273, 274, 278, 279, -1, 281, 282, 283,
+ 284, 285, 286, 287, 288, 289, 290, 291, 292, 293,
+ 294, 295, 296, -1, 298, 299, 300, -1, -1, -1,
+ -1, -1, 306, 126, 308, 309, 310, 311, 312, 313,
+ 314, 315, 316, 317, -1, -1, 320, -1, -1, -1,
+ -1, 33, -1, 327, 328, -1, 327, 328, 40, -1,
+ -1, 43, -1, 45, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 61,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 257, -1, -1, 260, 261, 262, -1, -1, -1, 266,
+ 267, 268, 269, 270, -1, -1, -1, -1, -1, -1,
+ -1, 278, 279, -1, 281, 282, 283, 284, 285, 286,
+ 287, 288, 289, 290, 291, 292, 293, 294, 295, 296,
+ -1, 298, 299, 300, 126, -1, -1, -1, -1, 306,
+ -1, 308, 309, 310, 311, 312, 313, 314, 315, 316,
+ 317, -1, 41, 320, 257, 44, -1, 260, 261, 262,
+ 327, 328, -1, 266, 267, 268, 269, 270, -1, 58,
+ 59, -1, 61, -1, 63, 278, 279, -1, 281, 282,
+ 283, 284, 285, 286, 287, 288, 289, 290, 291, 292,
+ 293, 294, 295, 296, -1, 298, 299, 300, -1, -1,
+ -1, -1, -1, 306, 93, 308, 309, 310, 311, 312,
+ 313, 314, 315, 316, 317, -1, 33, 320, -1, -1,
+ -1, -1, -1, 40, 327, 328, 43, -1, 45, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 61, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 257, -1, -1, 260, 261,
+ 262, -1, -1, -1, 266, 267, 268, 269, 270, -1,
+ -1, -1, -1, -1, -1, -1, 278, 279, -1, 281,
+ 282, 283, 284, 285, 286, 287, 288, 289, 290, 291,
+ 292, 293, 294, 295, 296, -1, 298, 299, 300, 126,
+ -1, -1, 41, -1, 306, 44, 308, 309, 310, 311,
+ 312, 313, 314, 315, 316, 317, -1, 33, 320, 58,
+ 59, -1, 61, -1, 40, 327, 328, 43, -1, 45,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 61, -1, -1, -1, -1,
+ -1, -1, -1, -1, 93, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 263, -1, -1, -1, -1, -1,
+ -1, -1, 271, 272, 273, 274, -1, -1, -1, -1,
+ -1, 33, -1, -1, -1, -1, -1, -1, 40, -1,
+ -1, 43, -1, 45, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 61,
+ 126, -1, -1, -1, -1, -1, -1, -1, -1, 318,
+ 319, -1, -1, -1, 38, -1, -1, -1, 327, 328,
+ 257, -1, -1, 260, 261, 262, -1, -1, -1, 266,
+ 267, 268, 269, 270, -1, -1, -1, 61, -1, 63,
+ -1, 278, 279, -1, 281, 282, 283, 284, 285, 286,
+ 287, 288, 289, 290, 291, 292, 293, 294, 295, 296,
+ -1, 298, 299, 300, 126, -1, -1, -1, -1, 306,
+ 94, 308, 309, 310, 311, 312, 313, 314, 315, 316,
+ 317, -1, 33, 320, -1, -1, -1, -1, -1, 40,
+ 327, 328, 43, -1, 45, -1, -1, -1, -1, -1,
+ 124, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 61, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 271, 272, 273, 274, -1, -1, -1, -1,
+ -1, 257, -1, -1, 260, 261, 262, -1, -1, -1,
+ 266, 267, 268, 269, 270, -1, -1, -1, -1, -1,
+ -1, -1, 278, 279, -1, 281, 282, 283, 284, 285,
+ 286, 287, 288, 289, 290, 291, 292, 293, 294, 295,
+ 296, -1, 298, 299, 300, 126, -1, -1, 327, 328,
+ 306, -1, 308, 309, 310, 311, 312, 313, 314, 315,
+ 316, 317, -1, 41, 320, 257, 44, -1, 260, 261,
+ 262, 327, 328, -1, 266, 267, 268, 269, 270, -1,
+ 58, 59, -1, 61, -1, 63, 278, 279, -1, 281,
+ 282, 283, 284, 285, 286, 287, 288, 289, 290, 291,
+ 292, 293, 294, 295, 296, -1, 298, 299, 300, 263,
+ -1, -1, -1, -1, 306, 93, 308, 309, 310, 311,
+ 312, 313, 314, 315, 316, 317, -1, 33, 320, -1,
+ -1, -1, -1, -1, 40, 327, 328, 43, -1, 45,
+ -1, -1, -1, -1, -1, -1, -1, 301, 302, 303,
+ 304, -1, -1, -1, -1, 61, -1, -1, -1, -1,
+ -1, -1, -1, -1, 318, 319, 38, 321, 322, 323,
+ 324, -1, 326, -1, -1, -1, 257, -1, -1, 260,
+ 261, 262, -1, -1, -1, 266, 267, 268, 269, 270,
+ -1, 63, -1, -1, -1, -1, -1, 278, 279, -1,
+ 281, 282, 283, 284, 285, 286, 287, 288, 289, 290,
+ 291, 292, 293, 294, 295, 296, -1, 298, 299, 300,
+ 126, -1, 94, -1, -1, 306, -1, 308, 309, 310,
+ 311, 312, 313, 314, 315, 316, 317, -1, 33, 320,
+ -1, -1, -1, -1, -1, 40, 327, 328, 43, -1,
+ 45, -1, 124, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 61, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 263, -1, -1, -1, -1,
+ -1, -1, -1, 271, 272, 273, 274, -1, -1, -1,
+ -1, -1, 33, -1, -1, -1, -1, -1, -1, 40,
+ -1, -1, 43, -1, 45, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 61, 126, -1, -1, -1, -1, -1, -1, -1, -1,
+ 318, -1, -1, -1, -1, -1, -1, -1, -1, 327,
+ 328, 257, -1, -1, 260, 261, 262, -1, -1, -1,
+ 266, 267, 268, 269, 270, -1, -1, -1, -1, -1,
+ -1, -1, 278, 279, -1, 281, 282, 283, 284, 285,
+ 286, 287, 288, 289, 290, 291, 292, 293, 294, 295,
+ 296, 263, 298, 299, 300, 126, -1, -1, -1, -1,
+ 306, -1, 308, 309, 310, 311, 312, 313, 314, 315,
+ 316, 317, -1, -1, 320, -1, 33, -1, -1, -1,
+ -1, 327, 328, 40, 41, -1, 43, -1, 45, 301,
+ 302, 303, 304, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 318, 319, -1, 321,
+ 322, 323, 324, -1, 326, -1, -1, -1, -1, -1,
+ -1, -1, 257, -1, -1, 260, 261, 262, -1, -1,
+ -1, 266, 267, 268, 269, 270, -1, -1, -1, -1,
+ -1, -1, -1, 278, 279, -1, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, -1, 298, 299, 300, -1, -1, -1, 126,
+ -1, 306, -1, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, -1, -1, 320, 257, -1, -1, 260,
+ 261, 262, 327, 328, 33, 266, 267, 268, 269, 270,
+ -1, 40, 41, -1, 43, -1, 45, 278, 279, -1,
+ 281, 282, 283, 284, 285, 286, 287, 288, 289, 290,
+ 291, 292, 293, 294, 295, 296, -1, 298, 299, 300,
+ -1, -1, -1, -1, -1, 306, -1, 308, 309, 310,
+ 311, 312, 313, 314, 315, 316, 317, -1, -1, 320,
+ -1, -1, -1, -1, -1, -1, 327, 328, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 33, -1,
+ -1, -1, -1, -1, -1, 40, 41, -1, 43, -1,
+ 45, -1, -1, -1, -1, -1, -1, 126, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 257, -1, -1, 260, 261, 262, -1, -1, -1, 266,
+ 267, 268, 269, 270, -1, -1, -1, -1, -1, -1,
+ -1, 278, 279, -1, 281, 282, 283, 284, 285, 286,
+ 287, 288, 289, 290, 291, 292, 293, 294, 295, 296,
+ -1, 298, 299, 300, -1, -1, -1, -1, -1, 306,
+ -1, 308, 309, 310, 311, 312, 313, 314, 315, 316,
+ 317, 126, -1, 320, -1, 33, -1, -1, -1, -1,
+ 327, 328, 40, 41, -1, 43, -1, 45, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 257, -1,
+ -1, 260, 261, 262, -1, -1, -1, 266, 267, 268,
+ 269, 270, -1, -1, -1, -1, -1, -1, -1, 278,
+ 279, -1, 281, 282, 283, 284, 285, 286, 287, 288,
+ 289, 290, 291, 292, 293, 294, 295, 296, 126, 298,
+ 299, 300, -1, -1, -1, -1, -1, 306, -1, 308,
+ 309, 310, 311, 312, 313, 314, 315, 316, 317, -1,
+ -1, 320, -1, -1, 38, -1, -1, -1, 327, 328,
+ 44, -1, 257, -1, -1, 260, 261, 262, -1, -1,
+ -1, 266, 267, 268, 269, 270, -1, 61, -1, 63,
+ -1, -1, -1, 278, 279, -1, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, -1, 298, 299, 300, -1, -1, -1, -1,
+ 94, 306, -1, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, -1, -1, 320, -1, 33, -1, -1,
+ -1, -1, 327, 328, 40, 41, -1, 43, -1, 45,
+ 124, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 257,
+ -1, -1, 260, 261, 262, -1, -1, -1, 266, 267,
+ 268, 269, 270, -1, -1, -1, -1, -1, -1, -1,
+ 278, 279, -1, 281, 282, 283, 284, 285, 286, 287,
+ 288, 289, 290, 291, 292, 293, 294, 295, 296, -1,
+ 298, 299, 300, -1, -1, -1, 41, -1, 306, 44,
+ 308, 309, 310, 311, 312, 313, 314, 315, 316, 317,
+ 126, 33, 320, 58, 59, -1, -1, -1, 40, 327,
+ 328, 43, -1, 45, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 93, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 263,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 33, -1,
+ -1, -1, -1, -1, -1, 40, -1, -1, 43, -1,
+ 45, -1, -1, -1, 126, -1, -1, 301, 302, 303,
+ 304, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 318, 319, -1, 321, 322, 323,
+ 324, -1, 326, 327, 328, -1, -1, -1, -1, -1,
+ -1, 257, -1, -1, 260, 261, 262, -1, -1, -1,
+ 266, 267, 268, 269, 270, -1, -1, -1, -1, -1,
+ -1, -1, 278, 279, -1, 281, 282, 283, 284, 285,
+ 286, 287, 288, 289, 290, 291, 292, 293, 294, 295,
+ 296, 126, 298, 299, 300, -1, -1, -1, -1, -1,
+ 306, -1, 308, 309, 310, 311, 312, 313, 314, 315,
+ 316, 317, -1, 33, 320, -1, -1, -1, -1, -1,
+ 40, 327, 328, 43, -1, 45, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 271, 272, 273, 274,
+ -1, -1, -1, -1, -1, 257, -1, -1, 260, 261,
+ 262, -1, -1, -1, 266, 267, 268, 269, 270, -1,
+ -1, -1, -1, -1, -1, -1, 278, 279, -1, 281,
+ 282, 283, 284, 285, 286, 287, 288, 289, 290, 291,
+ 292, 293, 294, 295, 296, -1, 298, 299, 300, -1,
+ -1, -1, 327, 328, 306, -1, 308, 309, 310, 311,
+ 312, 313, 314, 315, 316, 317, 126, 33, 320, -1,
+ -1, -1, -1, -1, 40, 327, 328, 43, -1, 45,
+ -1, -1, 257, -1, -1, 260, 261, 262, -1, -1,
+ -1, 266, 267, 268, 269, 270, -1, -1, -1, -1,
+ -1, -1, -1, 278, 279, -1, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, -1, 298, 299, 300, -1, -1, -1, -1,
+ -1, 306, -1, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, -1, -1, 320, -1, -1, -1, -1,
+ -1, -1, 327, 328, -1, -1, 38, -1, -1, 41,
+ 126, -1, 44, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 58, 59, -1, 61,
+ -1, 63, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 257, -1, -1,
+ 260, 261, 262, -1, -1, -1, 266, 267, 268, 269,
+ 270, 93, 94, -1, -1, -1, -1, -1, 278, 279,
+ -1, 281, 282, 283, 284, 285, 286, 287, 288, 289,
+ 290, 291, 292, 293, 294, 295, 296, -1, 298, 299,
+ 300, -1, 124, -1, -1, -1, 306, -1, 308, 309,
+ 310, 311, 312, 313, 314, 315, 316, 317, -1, -1,
+ 320, -1, -1, -1, -1, -1, -1, 327, 328, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 257, -1, -1, 260, 261, 262, -1, -1, -1,
+ 266, 267, 268, 269, 270, -1, -1, -1, -1, -1,
+ -1, -1, 278, 279, -1, 281, 282, 283, 284, 285,
+ 286, 287, 288, 289, 290, 291, 292, 293, 294, 295,
+ 296, -1, 298, 299, 300, -1, -1, -1, -1, -1,
+ 306, -1, 308, 309, 310, 311, 312, 313, 314, 315,
+ 316, 317, 38, -1, 320, 41, -1, -1, 44, -1,
+ -1, 327, 328, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 58, 59, -1, 61, 38, 63, -1, 41,
+ -1, 263, 44, -1, -1, -1, -1, -1, -1, 271,
+ 272, 273, 274, -1, -1, -1, 58, 59, -1, 61,
+ -1, 63, -1, -1, -1, -1, -1, 93, 94, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 301,
+ 302, 303, 304, -1, -1, -1, -1, 38, -1, -1,
+ 41, 93, 94, 44, -1, -1, 318, 319, 124, 321,
+ 322, 323, 324, -1, 326, 327, 328, 58, 59, -1,
+ 61, -1, 63, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 124, -1, 38, -1, -1, 41, -1, -1,
+ 44, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 93, 94, 58, 59, -1, 61, -1, 63,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 38, -1, -1, 41,
+ -1, -1, 44, 124, -1, -1, -1, -1, -1, 93,
+ 94, -1, -1, -1, -1, -1, 58, 59, -1, 61,
+ -1, 63, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 38, -1, -1, 41, -1, -1, 44,
+ 124, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 93, 94, 58, 59, -1, 61, -1, 63, -1,
+ -1, -1, -1, -1, -1, -1, -1, 263, -1, -1,
+ -1, -1, -1, -1, -1, 271, 272, 273, 274, -1,
+ -1, -1, 124, -1, -1, -1, -1, -1, 93, 94,
+ -1, 263, -1, -1, -1, -1, -1, -1, -1, 271,
+ 272, 273, 274, -1, -1, 301, 302, 303, 304, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 38, 124,
+ -1, 41, 318, 319, 44, 321, 322, 323, 324, -1,
+ 326, 327, 328, -1, -1, -1, -1, -1, 58, 59,
+ -1, 61, 263, 63, -1, -1, 318, 319, -1, -1,
+ 271, 272, 273, 274, -1, 327, 328, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 93, 94, -1, -1, -1, -1, 263,
+ 301, 302, 303, 304, -1, -1, -1, 271, 272, 273,
+ 274, -1, -1, 41, -1, -1, 44, 318, 319, -1,
+ 321, 322, 323, 324, 124, 326, 327, 328, -1, -1,
+ 58, 59, -1, -1, -1, -1, -1, 301, 302, 303,
+ 304, 263, -1, -1, -1, -1, -1, -1, -1, 271,
+ 272, 273, 274, -1, 318, 319, -1, 321, 322, 323,
+ 324, -1, 326, 327, 328, 93, -1, -1, -1, -1,
+ 38, -1, -1, 41, -1, -1, 44, -1, 263, 301,
+ 302, 303, 304, -1, -1, -1, 271, 272, 273, 274,
+ 58, 59, -1, 61, -1, 63, 318, 319, -1, 321,
+ 322, 323, 324, -1, 326, 327, 328, -1, -1, -1,
+ 41, -1, -1, 44, -1, -1, 301, 302, 303, 304,
+ -1, -1, -1, -1, -1, 93, 94, 58, 59, -1,
+ -1, -1, -1, 318, 319, -1, 321, 322, 323, 324,
+ -1, 326, 327, 328, 38, -1, -1, 41, -1, -1,
+ 44, -1, -1, -1, -1, -1, 124, -1, 41, -1,
+ -1, 44, 93, 263, 58, 59, -1, 61, -1, 63,
+ -1, 271, 272, 273, 274, 58, 59, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 38, -1, -1, 41,
+ -1, -1, 44, -1, -1, -1, -1, -1, -1, 93,
+ 94, 301, 302, 303, 304, -1, 58, 59, -1, 61,
+ 93, 63, -1, -1, -1, -1, -1, -1, 318, 319,
+ -1, 321, 322, 323, 324, -1, 326, 327, 328, 38,
+ 124, -1, 41, -1, -1, 44, -1, -1, -1, -1,
+ -1, 93, 94, 271, 272, 273, 274, -1, -1, 58,
+ 59, -1, 61, -1, 63, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 38, 124, -1, 41, -1, -1, 44, -1, -1,
+ -1, -1, -1, -1, 93, 94, -1, -1, -1, -1,
+ -1, 58, 59, -1, 61, 263, 63, -1, -1, 327,
+ 328, -1, -1, 271, 272, 273, 274, -1, -1, -1,
+ -1, -1, -1, -1, -1, 124, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 93, 94, -1, -1,
+ -1, -1, -1, 301, 302, 303, 304, -1, -1, -1,
+ 271, 272, 273, 274, -1, -1, -1, -1, -1, -1,
+ 318, 319, -1, 321, 322, 323, 324, 124, 326, 327,
+ 328, 38, -1, -1, 41, -1, -1, 44, -1, 263,
+ -1, -1, -1, -1, -1, -1, -1, 271, 272, 273,
+ 274, 58, 59, -1, 61, -1, 63, -1, 271, 272,
+ 273, 274, -1, -1, -1, -1, 327, 328, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 301, 302, 303,
+ 304, 263, -1, -1, -1, -1, 93, 94, -1, 271,
+ 272, 273, 274, -1, 318, 319, -1, 321, 322, 323,
+ 324, -1, 326, 327, 328, 38, -1, -1, 41, -1,
+ -1, -1, -1, -1, 327, 328, -1, 124, -1, 301,
+ 302, 303, 304, -1, 263, 58, 59, -1, 61, -1,
+ 63, -1, 271, 272, 273, 274, 318, 319, -1, 321,
+ 322, 323, 324, -1, 326, 327, 328, 38, -1, -1,
+ 41, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 93, 94, 301, 302, 303, 304, 263, 58, 59, -1,
+ 61, -1, 63, -1, 271, 272, 273, 274, -1, 318,
+ 319, -1, 321, 322, 323, 324, -1, 326, 327, 328,
+ 38, 124, -1, 41, -1, -1, -1, -1, -1, -1,
+ -1, -1, 93, 94, 301, 302, 303, 304, -1, -1,
+ 58, 59, -1, 61, -1, 63, -1, -1, -1, -1,
+ -1, 318, 319, -1, 321, 322, 323, 324, -1, 326,
+ 327, 328, 38, 124, -1, 41, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 93, 94, -1, -1, -1,
+ -1, -1, 58, 59, -1, 61, 263, 63, -1, -1,
+ -1, -1, -1, -1, 271, 272, 273, 274, -1, -1,
+ -1, -1, -1, -1, -1, -1, 124, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 93, 94, -1,
+ -1, -1, -1, -1, 301, 302, 303, 304, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 318, 319, -1, 321, 322, 323, 324, 124, 326,
+ 327, 328, 38, -1, -1, 41, -1, -1, 44, -1,
+ 263, -1, -1, -1, -1, -1, -1, -1, 271, 272,
+ 273, 274, 58, 59, -1, 61, -1, 63, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 301, 302,
+ 303, 304, 263, -1, -1, -1, -1, 93, 94, -1,
+ 271, 272, 273, 274, -1, 318, 319, -1, 321, 322,
+ 323, 324, -1, 326, 327, 328, 38, -1, -1, 41,
+ -1, -1, 44, -1, -1, -1, -1, -1, 124, -1,
+ 301, 302, 303, 304, -1, 263, 58, 59, -1, 61,
+ -1, 63, -1, 271, 272, 273, 274, 318, 319, -1,
+ 321, 322, 323, 324, -1, 326, 327, 328, 38, -1,
+ -1, 41, -1, -1, 44, -1, -1, -1, -1, -1,
+ -1, 93, 94, 301, 302, 303, 304, 263, 58, 59,
+ -1, 61, -1, 63, -1, 271, 272, 273, 274, -1,
+ 318, 319, -1, 321, 322, 323, 324, -1, 326, 327,
+ 328, 38, 124, -1, 41, -1, -1, 44, -1, -1,
+ -1, -1, -1, 93, 94, 301, 302, 303, 304, -1,
+ -1, 58, 59, -1, 61, -1, 63, -1, -1, -1,
+ -1, -1, 318, 319, -1, 321, 322, 323, 324, -1,
+ 326, 327, 328, 38, 124, -1, 41, -1, -1, 44,
+ -1, -1, -1, -1, -1, -1, 93, 94, -1, -1,
+ -1, -1, -1, 58, 59, -1, 61, 263, 63, -1,
+ -1, -1, -1, -1, -1, 271, 272, 273, 274, -1,
+ -1, -1, -1, -1, -1, -1, -1, 124, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 93, 94,
+ -1, -1, -1, -1, -1, 301, 302, 303, 304, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 318, 319, -1, 321, 322, 323, 324, 124,
+ -1, 327, 328, 38, -1, -1, 41, -1, -1, 44,
+ -1, 263, -1, -1, -1, -1, -1, -1, -1, 271,
+ 272, 273, 274, 58, 59, -1, 61, -1, 63, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 41, -1,
+ -1, 44, -1, -1, -1, -1, -1, -1, -1, 301,
+ 302, 303, 304, 263, -1, 58, 59, -1, 93, 94,
+ -1, 271, 272, 273, 274, -1, 318, 319, -1, 321,
+ 322, 323, 324, -1, -1, 327, 328, 38, -1, -1,
+ 41, -1, -1, 44, -1, -1, -1, -1, -1, 124,
+ 93, 301, 302, 303, 304, -1, 263, 58, 59, -1,
+ 61, -1, 63, -1, 271, 272, 273, 274, 318, 319,
+ -1, 321, 322, 323, 324, -1, -1, 327, 328, 38,
+ -1, -1, 41, -1, -1, 44, -1, -1, -1, -1,
+ -1, -1, 93, 94, 301, 302, 303, 304, 263, 58,
+ 59, -1, 61, -1, 63, -1, 271, 272, 273, 274,
+ -1, 318, 319, -1, 321, 322, -1, -1, -1, -1,
+ 327, 328, 38, 124, -1, 41, -1, -1, 44, -1,
+ -1, -1, -1, -1, 93, 94, 301, 302, -1, 304,
+ -1, -1, 58, 59, -1, 61, 38, 63, -1, 41,
+ -1, -1, 44, 318, 319, -1, 321, 322, 41, -1,
+ -1, 44, 327, 328, -1, 124, 58, 59, -1, 61,
+ -1, 63, -1, -1, -1, 58, 59, 93, 94, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 263, -1,
+ -1, -1, -1, -1, -1, -1, 271, 272, 273, 274,
+ -1, 93, 94, -1, -1, -1, -1, -1, 124, -1,
+ 93, -1, -1, 38, -1, -1, 41, -1, -1, 44,
+ -1, -1, -1, -1, -1, -1, 301, 302, 271, 272,
+ 273, 274, 124, 58, 59, -1, 61, -1, 63, -1,
+ -1, -1, -1, 318, 319, -1, 321, 322, -1, -1,
+ -1, -1, 327, 328, 38, -1, -1, 41, -1, -1,
+ 44, -1, 263, -1, -1, -1, -1, -1, 93, 94,
+ 271, 272, 273, 274, 58, 59, -1, 61, -1, 63,
+ -1, -1, -1, -1, 327, 328, -1, -1, -1, 41,
+ -1, -1, 44, -1, -1, -1, -1, -1, -1, 124,
+ 301, 302, -1, -1, 263, -1, 58, 59, -1, 93,
+ 94, -1, 271, 272, 273, 274, -1, 318, 319, -1,
+ 321, 322, -1, -1, -1, -1, 327, 328, 38, -1,
+ -1, 41, -1, -1, 44, -1, -1, -1, -1, -1,
+ 124, 93, 301, 302, -1, -1, -1, 263, 58, 59,
+ -1, 61, -1, 63, -1, 271, 272, 273, 274, 318,
+ 319, -1, -1, -1, -1, -1, -1, -1, 327, 328,
+ -1, 263, -1, -1, -1, -1, -1, -1, -1, 271,
+ 272, 273, 274, 93, 94, 301, 302, -1, 271, 272,
+ 273, 274, -1, -1, -1, 38, -1, -1, 41, -1,
+ -1, 44, 318, 319, 41, -1, -1, 44, -1, 301,
+ 302, 327, 328, -1, 124, 58, 59, -1, 61, -1,
+ 63, 58, 59, -1, -1, -1, 318, 319, -1, -1,
+ -1, -1, -1, -1, -1, 327, 328, -1, 263, -1,
+ -1, -1, -1, -1, 327, 328, 271, 272, 273, 274,
+ 93, 94, -1, -1, -1, -1, 93, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 302, -1, 263,
+ -1, 124, -1, -1, -1, -1, -1, 271, 272, 273,
+ 274, -1, 38, 318, 319, 41, -1, -1, 44, -1,
+ -1, -1, 327, 328, 38, -1, -1, 41, -1, -1,
+ 44, -1, 58, 59, -1, 61, -1, 63, -1, 271,
+ 272, 273, 274, -1, 58, 59, -1, 61, -1, 63,
+ -1, -1, -1, -1, 318, 319, -1, -1, -1, -1,
+ -1, -1, -1, 327, 328, -1, -1, 93, 94, -1,
+ -1, -1, -1, 263, -1, -1, -1, -1, -1, 93,
+ 94, 271, 272, 273, 274, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 327, 328, 38, 124, -1,
+ 41, -1, -1, 44, -1, -1, -1, -1, -1, -1,
+ 124, 301, 302, 303, 304, -1, -1, 58, 59, -1,
+ 61, -1, 63, -1, -1, -1, -1, -1, 318, 319,
+ -1, 321, 322, 323, 324, -1, 326, -1, -1, -1,
+ 263, -1, -1, -1, -1, -1, -1, -1, 271, 272,
+ 273, 274, 93, 94, 271, 272, 273, 274, -1, -1,
+ -1, -1, -1, -1, 38, -1, -1, 41, -1, -1,
+ 44, -1, -1, -1, -1, -1, -1, -1, 301, 302,
+ 303, 304, -1, 124, 58, 59, -1, 61, -1, 63,
+ 41, -1, -1, 44, -1, 318, 319, -1, 321, 322,
+ 323, 324, -1, 326, -1, -1, -1, 58, 59, -1,
+ 327, 328, -1, -1, -1, -1, -1, -1, -1, 93,
+ 94, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 263, -1, -1,
+ -1, -1, 93, -1, -1, 271, 272, 273, 274, 263,
+ 124, -1, -1, -1, -1, -1, -1, 271, 272, 273,
+ 274, 38, -1, -1, 41, -1, -1, 44, -1, -1,
+ -1, -1, -1, -1, -1, 301, 302, 303, 304, -1,
+ -1, 58, 59, -1, 61, -1, 63, 301, 302, 303,
+ 304, -1, 318, 319, -1, 321, 322, 323, 324, -1,
+ 326, -1, -1, -1, 318, 319, -1, 321, 322, 323,
+ 324, -1, 326, -1, -1, -1, 93, 94, 38, -1,
+ -1, 41, 263, -1, 44, -1, -1, -1, -1, -1,
+ 271, 272, 273, 274, -1, -1, -1, -1, 58, 59,
+ -1, 61, -1, 63, -1, 38, -1, 124, 41, -1,
+ -1, 44, -1, -1, -1, -1, -1, -1, -1, -1,
+ 301, 302, 303, 304, -1, -1, -1, -1, 61, -1,
+ 63, -1, -1, 93, 94, -1, -1, 318, 319, -1,
+ 321, 322, 323, 324, -1, 326, -1, 38, -1, 263,
+ 41, -1, -1, 44, -1, -1, -1, 271, 272, 273,
+ 274, 94, -1, -1, 124, -1, -1, 58, 59, -1,
+ 61, -1, 63, 38, -1, -1, 41, -1, -1, 44,
+ 271, 272, 273, 274, -1, -1, -1, 301, 302, 303,
+ 304, 124, -1, 58, 59, -1, 61, -1, 63, -1,
+ -1, -1, 93, 94, 318, 319, -1, 321, 322, 323,
+ 324, -1, 326, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 38, -1, -1, -1, 93, 94,
+ 44, -1, -1, 124, -1, 38, 327, 328, -1, -1,
+ -1, 44, -1, -1, -1, -1, 263, 61, 41, 63,
+ -1, 44, -1, -1, 271, 272, 273, 274, 61, 124,
+ 63, -1, -1, -1, -1, 58, 59, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 94, -1, -1, -1, 301, 302, 303, 304, -1, -1,
+ -1, 94, -1, -1, -1, -1, -1, -1, -1, -1,
+ 93, 318, 319, 263, 321, 322, 323, 324, -1, 326,
+ 124, 271, 272, 273, 274, -1, -1, -1, -1, -1,
+ -1, 124, -1, -1, -1, -1, 33, -1, -1, -1,
+ 263, -1, -1, 40, -1, -1, 43, -1, 45, -1,
+ -1, 301, 302, 303, 304, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 318, 319,
+ -1, 321, 322, 323, 324, -1, 326, -1, 301, 302,
+ 303, 304, 263, -1, -1, -1, -1, -1, -1, -1,
+ 271, 272, 273, 274, 91, 318, 319, -1, 321, 322,
+ 323, 324, -1, 326, -1, -1, -1, -1, 263, -1,
+ 41, -1, -1, 44, -1, -1, 271, 272, 273, 274,
+ 301, 302, 303, 304, -1, -1, 123, 58, 59, 126,
+ -1, -1, -1, 41, -1, -1, 44, 318, 319, -1,
+ 321, 322, 323, 324, -1, 326, 301, 302, 303, 304,
+ 58, 59, -1, 61, -1, 63, -1, -1, -1, 263,
+ -1, -1, 93, 318, 319, -1, 321, 322, 323, 324,
+ 263, 326, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 41, 93, 94, 44, 271, 272,
+ 273, 274, -1, -1, -1, -1, -1, 301, 302, 303,
+ 304, 58, 59, -1, 61, -1, 63, -1, 301, 302,
+ 303, 304, -1, -1, 318, 319, 124, 321, 322, 323,
+ 324, -1, 326, -1, -1, 318, 319, -1, 321, 322,
+ 323, 324, -1, 326, -1, -1, 93, 94, -1, -1,
+ -1, -1, -1, -1, 327, 328, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 257, -1, -1, 260, 261, 262, -1, 124, -1, 266,
+ 267, 268, 269, 270, -1, -1, -1, -1, -1, -1,
+ -1, 278, 279, -1, 281, 282, 283, 284, 285, 286,
+ 287, 288, 289, 290, 291, 292, 293, 294, 295, 296,
+ -1, 298, 299, 300, -1, -1, -1, -1, -1, 306,
+ -1, 308, 309, 310, 311, 312, 313, 314, 315, 316,
+ 317, -1, -1, 320, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 271, 272, 273, 274, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 263, -1, -1, -1, -1,
+ -1, -1, -1, 271, 272, 273, 274, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 66, -1, -1, -1, -1, -1, 72, -1,
+ -1, -1, -1, -1, -1, -1, 327, 328, -1, -1,
+ -1, -1, -1, -1, -1, -1, 263, -1, -1, -1,
+ 318, 319, -1, -1, 271, 272, 273, 274, -1, 327,
+ 328, -1, -1, 107, -1, 109, -1, 111, -1, 113,
+ -1, -1, 116, -1, 118, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 128, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 142, -1,
+ -1, 318, 319, 147, 148, 149, 150, 151, 152, 153,
+ 327, 328, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 170, 171, 172, 173,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 202, 203,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 218, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 241, -1, 243,
+ 244, -1, -1, -1, -1, -1, -1, -1, 252, -1,
+ -1, -1, -1, 257, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 291, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 301, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 311, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 330, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 341,
+};
+#define YYFINAL 1
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYMAXTOKEN 328
+#if YYDEBUG
+char *yyname[] = {
+"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+"'!'",0,0,0,0,"'&'",0,"'('","')'",0,"'+'","','","'-'",0,0,0,0,0,0,0,0,0,0,0,0,
+"':'","';'",0,"'='",0,"'?'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,"'['",0,"']'","'^'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+"'{'","'|'","'}'","'~'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"WORD","LABEL","APPEND","OPEN",
+"SSELECT","LOOPEX","DOTDOT","USING","FORMAT","DO","SHIFT","PUSH","POP",
+"LVALFUN","WHILE","UNTIL","IF","UNLESS","ELSE","ELSIF","CONTINUE","SPLIT",
+"FLIST","FOR","FILOP","FILOP2","FILOP3","FILOP4","FILOP22","FILOP25","FUNC0",
+"FUNC1","FUNC2","FUNC2x","FUNC3","FUNC4","FUNC5","HSHFUN","HSHFUN3","FLIST2",
+"SUB","FILETEST","LOCAL","DELETE","RELOP","EQOP","MULOP","ADDOP","PACKAGE",
+"AMPER","FORMLIST","REG","ARYLEN","ARY","HSH","STAR","SUBST","PATTERN",
+"RSTRING","TRANS","LISTOP","OROR","ANDAND","UNIOP","LS","RS","MATCH","NMATCH",
+"UMINUS","POW","INC","DEC",
+};
+char *yyrule[] = {
+"$accept : prog",
+"$$1 :",
+"prog : $$1 lineseq",
+"compblock : block CONTINUE block",
+"compblock : block else",
+"else :",
+"else : ELSE block",
+"else : ELSIF '(' expr ')' compblock",
+"block : '{' remember lineseq '}'",
+"remember :",
+"lineseq :",
+"lineseq : lineseq line",
+"line : decl",
+"line : label cond",
+"line : loop",
+"line : label ';'",
+"line : label sideff ';'",
+"sideff : error",
+"sideff : expr",
+"sideff : expr IF expr",
+"sideff : expr UNLESS expr",
+"sideff : expr WHILE expr",
+"sideff : expr UNTIL expr",
+"cond : IF '(' expr ')' compblock",
+"cond : UNLESS '(' expr ')' compblock",
+"cond : IF block compblock",
+"cond : UNLESS block compblock",
+"loop : label WHILE '(' texpr ')' compblock",
+"loop : label UNTIL '(' expr ')' compblock",
+"loop : label WHILE block compblock",
+"loop : label UNTIL block compblock",
+"loop : label FOR REG '(' expr crp compblock",
+"loop : label FOR '(' expr crp compblock",
+"loop : label FOR '(' nexpr ';' texpr ';' nexpr ')' block",
+"loop : label compblock",
+"nexpr :",
+"nexpr : sideff",
+"texpr :",
+"texpr : expr",
+"label :",
+"label : LABEL",
+"decl : format",
+"decl : subrout",
+"decl : package",
+"format : FORMAT WORD '=' FORMLIST",
+"format : FORMAT '=' FORMLIST",
+"subrout : SUB WORD block",
+"package : PACKAGE WORD ';'",
+"cexpr : ',' expr",
+"expr : expr ',' sexpr",
+"expr : sexpr",
+"csexpr : ',' sexpr",
+"sexpr : sexpr '=' sexpr",
+"sexpr : sexpr POW '=' sexpr",
+"sexpr : sexpr MULOP '=' sexpr",
+"sexpr : sexpr ADDOP '=' sexpr",
+"sexpr : sexpr LS '=' sexpr",
+"sexpr : sexpr RS '=' sexpr",
+"sexpr : sexpr '&' '=' sexpr",
+"sexpr : sexpr '^' '=' sexpr",
+"sexpr : sexpr '|' '=' sexpr",
+"sexpr : sexpr POW sexpr",
+"sexpr : sexpr MULOP sexpr",
+"sexpr : sexpr ADDOP sexpr",
+"sexpr : sexpr LS sexpr",
+"sexpr : sexpr RS sexpr",
+"sexpr : sexpr RELOP sexpr",
+"sexpr : sexpr EQOP sexpr",
+"sexpr : sexpr '&' sexpr",
+"sexpr : sexpr '^' sexpr",
+"sexpr : sexpr '|' sexpr",
+"sexpr : sexpr DOTDOT sexpr",
+"sexpr : sexpr ANDAND sexpr",
+"sexpr : sexpr OROR sexpr",
+"sexpr : sexpr '?' sexpr ':' sexpr",
+"sexpr : sexpr MATCH sexpr",
+"sexpr : sexpr NMATCH sexpr",
+"sexpr : term",
+"term : '-' term",
+"term : '+' term",
+"term : '!' term",
+"term : '~' term",
+"term : term INC",
+"term : term DEC",
+"term : INC term",
+"term : DEC term",
+"term : FILETEST WORD",
+"term : FILETEST sexpr",
+"term : FILETEST",
+"term : LOCAL '(' expr crp",
+"term : '(' expr crp",
+"term : '(' ')'",
+"term : DO sexpr",
+"term : DO block",
+"term : REG",
+"term : STAR",
+"term : REG '[' expr ']'",
+"term : HSH",
+"term : ARY",
+"term : REG '{' expr ';' '}'",
+"term : '(' expr crp '[' expr ']'",
+"term : '(' ')' '[' expr ']'",
+"term : ARY '[' expr ']'",
+"term : ARY '{' expr ';' '}'",
+"term : DELETE REG '{' expr ';' '}'",
+"term : DELETE '(' REG '{' expr ';' '}' ')'",
+"term : ARYLEN",
+"term : RSTRING",
+"term : PATTERN",
+"term : SUBST",
+"term : TRANS",
+"term : DO WORD '(' expr crp",
+"term : AMPER WORD '(' expr crp",
+"term : DO WORD '(' ')'",
+"term : AMPER WORD '(' ')'",
+"term : AMPER WORD",
+"term : DO REG '(' expr crp",
+"term : AMPER REG '(' expr crp",
+"term : DO REG '(' ')'",
+"term : AMPER REG '(' ')'",
+"term : AMPER REG",
+"term : LOOPEX",
+"term : LOOPEX WORD",
+"term : UNIOP",
+"term : UNIOP block",
+"term : UNIOP sexpr",
+"term : SSELECT",
+"term : SSELECT WORD",
+"term : SSELECT '(' handle ')'",
+"term : SSELECT '(' sexpr csexpr csexpr csexpr ')'",
+"term : OPEN WORD",
+"term : OPEN '(' WORD ')'",
+"term : OPEN '(' handle cexpr ')'",
+"term : FILOP '(' handle ')'",
+"term : FILOP WORD",
+"term : FILOP REG",
+"term : FILOP '(' ')'",
+"term : FILOP",
+"term : FILOP2 '(' handle cexpr ')'",
+"term : FILOP3 '(' handle csexpr cexpr ')'",
+"term : FILOP22 '(' handle ',' handle ')'",
+"term : FILOP4 '(' handle csexpr csexpr cexpr ')'",
+"term : FILOP25 '(' handle ',' handle csexpr csexpr cexpr ')'",
+"term : PUSH '(' aryword ',' expr crp",
+"term : POP aryword",
+"term : POP '(' aryword ')'",
+"term : SHIFT aryword",
+"term : SHIFT '(' aryword ')'",
+"term : SHIFT",
+"term : SPLIT",
+"term : SPLIT '(' sexpr csexpr csexpr ')'",
+"term : SPLIT '(' sexpr csexpr ')'",
+"term : SPLIT '(' sexpr ')'",
+"term : FLIST2 '(' sexpr cexpr ')'",
+"term : FLIST '(' expr crp",
+"term : LVALFUN sexpr",
+"term : LVALFUN",
+"term : FUNC0",
+"term : FUNC0 '(' ')'",
+"term : FUNC1 '(' ')'",
+"term : FUNC1 '(' expr ')'",
+"term : FUNC2 '(' sexpr cexpr ')'",
+"term : FUNC2x '(' sexpr csexpr ')'",
+"term : FUNC2x '(' sexpr csexpr cexpr ')'",
+"term : FUNC3 '(' sexpr csexpr cexpr ')'",
+"term : FUNC4 '(' sexpr csexpr csexpr cexpr ')'",
+"term : FUNC5 '(' sexpr csexpr csexpr csexpr cexpr ')'",
+"term : HSHFUN '(' hshword ')'",
+"term : HSHFUN hshword",
+"term : HSHFUN3 '(' hshword csexpr cexpr ')'",
+"term : bareword",
+"term : listop",
+"listop : LISTOP",
+"listop : LISTOP expr",
+"listop : LISTOP WORD",
+"listop : LISTOP WORD expr",
+"listop : LISTOP REG expr",
+"listop : LISTOP block expr",
+"handle : WORD",
+"handle : sexpr",
+"aryword : WORD",
+"aryword : ARY",
+"hshword : WORD",
+"hshword : HSH",
+"crp : ',' ')'",
+"crp : ')'",
+"bareword : WORD",
+};
+#endif
+#ifdef YYSTACKSIZE
+#undef YYMAXDEPTH
+#define YYMAXDEPTH YYSTACKSIZE
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 500
+#define YYMAXDEPTH 500
+#endif
+#endif
+int yydebug;
+int yynerrs;
+int yyerrflag;
+int yychar;
+short *yyssp;
+YYSTYPE *yyvsp;
+YYSTYPE yyval;
+YYSTYPE yylval;
+short yyss[YYSTACKSIZE];
+YYSTYPE yyvs[YYSTACKSIZE];
+#define yystacksize YYSTACKSIZE
+#line 876 "perly.y"
+ /* PROGRAM */
+#line 1820 "y.tab.c"
+#define YYABORT goto yyabort
+#define YYREJECT goto yyabort
+#define YYACCEPT goto yyaccept
+#define YYERROR goto yyerrlab
+int
+yyparse()
+{
+ register int yym, yyn, yystate;
+#if YYDEBUG
+ register char *yys;
+ extern char *getenv();
+
+ if (yys = getenv("YYDEBUG"))
+ {
+ yyn = *yys;
+ if (yyn >= '0' && yyn <= '9')
+ yydebug = yyn - '0';
+ }
+#endif
+
+ yynerrs = 0;
+ yyerrflag = 0;
+ yychar = (-1);
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+ *yyssp = yystate = 0;
+
+yyloop:
+ if (yyn = yydefred[yystate]) goto yyreduce;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ }
+ if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, shifting to state %d\n",
+ YYPREFIX, yystate, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ yychar = (-1);
+ if (yyerrflag > 0) --yyerrflag;
+ goto yyloop;
+ }
+ if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+ yyn = yytable[yyn];
+ goto yyreduce;
+ }
+ if (yyerrflag) goto yyinrecovery;
+#ifdef lint
+ goto yynewerror;
+#endif
+yynewerror:
+ yyerror("syntax error");
+#ifdef lint
+ goto yyerrlab;
+#endif
+yyerrlab:
+ ++yynerrs;
+yyinrecovery:
+ if (yyerrflag < 3)
+ {
+ yyerrflag = 3;
+ for (;;)
+ {
+ if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, error recovery shifting\
+ to state %d\n", YYPREFIX, *yyssp, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ goto yyloop;
+ }
+ else
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: error recovery discarding state %d\n",
+ YYPREFIX, *yyssp);
+#endif
+ if (yyssp <= yyss) goto yyabort;
+ --yyssp;
+ --yyvsp;
+ }
+ }
+ }
+ else
+ {
+ if (yychar == 0) goto yyabort;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, error recovery discards token %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ yychar = (-1);
+ goto yyloop;
+ }
+yyreduce:
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, reducing by rule %d (%s)\n",
+ YYPREFIX, yystate, yyn, yyrule[yyn]);
+#endif
+ yym = yylen[yyn];
+ yyval = yyvsp[1-yym];
+ switch (yyn)
+ {
+case 1:
+#line 115 "perly.y"
+{
+#if defined(YYDEBUG) && defined(DEBUGGING)
+ yydebug = (debug & 1);
+#endif
+ expectterm = 2;
+ }
+break;
+case 2:
+#line 122 "perly.y"
+{ if (in_eval)
+ eval_root = block_head(yyvsp[0].cmdval);
+ else
+ main_root = block_head(yyvsp[0].cmdval); }
+break;
+case 3:
+#line 129 "perly.y"
+{ yyval.compval.comp_true = yyvsp[-2].cmdval; yyval.compval.comp_alt = yyvsp[0].cmdval; }
+break;
+case 4:
+#line 131 "perly.y"
+{ yyval.compval.comp_true = yyvsp[-1].cmdval; yyval.compval.comp_alt = yyvsp[0].cmdval; }
+break;
+case 5:
+#line 135 "perly.y"
+{ yyval.cmdval = Nullcmd; }
+break;
+case 6:
+#line 137 "perly.y"
+{ yyval.cmdval = yyvsp[0].cmdval; }
+break;
+case 7:
+#line 139 "perly.y"
+{ cmdline = yyvsp[-4].ival;
+ yyval.cmdval = make_ccmd(C_ELSIF,1,yyvsp[-2].arg,yyvsp[0].compval); }
+break;
+case 8:
+#line 144 "perly.y"
+{ yyval.cmdval = block_head(yyvsp[-1].cmdval);
+ if (cmdline > (line_t)yyvsp[-3].ival)
+ cmdline = yyvsp[-3].ival;
+ if (savestack->ary_fill > yyvsp[-2].ival)
+ restorelist(yyvsp[-2].ival);
+ expectterm = 2; }
+break;
+case 9:
+#line 153 "perly.y"
+{ yyval.ival = savestack->ary_fill; }
+break;
+case 10:
+#line 157 "perly.y"
+{ yyval.cmdval = Nullcmd; }
+break;
+case 11:
+#line 159 "perly.y"
+{ yyval.cmdval = append_line(yyvsp[-1].cmdval,yyvsp[0].cmdval); }
+break;
+case 12:
+#line 163 "perly.y"
+{ yyval.cmdval = Nullcmd; }
+break;
+case 13:
+#line 165 "perly.y"
+{ yyval.cmdval = add_label(yyvsp[-1].cval,yyvsp[0].cmdval); }
+break;
+case 15:
+#line 168 "perly.y"
+{ if (yyvsp[-1].cval != Nullch) {
+ yyval.cmdval = add_label(yyvsp[-1].cval, make_acmd(C_EXPR, Nullstab,
+ Nullarg, Nullarg) );
+ }
+ else {
+ yyval.cmdval = Nullcmd;
+ cmdline = NOLINE;
+ }
+ expectterm = 2; }
+break;
+case 16:
+#line 178 "perly.y"
+{ yyval.cmdval = add_label(yyvsp[-2].cval,yyvsp[-1].cmdval);
+ expectterm = 2; }
+break;
+case 17:
+#line 183 "perly.y"
+{ yyval.cmdval = Nullcmd; }
+break;
+case 18:
+#line 185 "perly.y"
+{ yyval.cmdval = make_acmd(C_EXPR, Nullstab, yyvsp[0].arg, Nullarg); }
+break;
+case 19:
+#line 187 "perly.y"
+{ yyval.cmdval = addcond(
+ make_acmd(C_EXPR, Nullstab, Nullarg, yyvsp[-2].arg), yyvsp[0].arg); }
+break;
+case 20:
+#line 190 "perly.y"
+{ yyval.cmdval = addcond(invert(
+ make_acmd(C_EXPR, Nullstab, Nullarg, yyvsp[-2].arg)), yyvsp[0].arg); }
+break;
+case 21:
+#line 193 "perly.y"
+{ yyval.cmdval = addloop(
+ make_acmd(C_EXPR, Nullstab, Nullarg, yyvsp[-2].arg), yyvsp[0].arg); }
+break;
+case 22:
+#line 196 "perly.y"
+{ yyval.cmdval = addloop(invert(
+ make_acmd(C_EXPR, Nullstab, Nullarg, yyvsp[-2].arg)), yyvsp[0].arg); }
+break;
+case 23:
+#line 201 "perly.y"
+{ cmdline = yyvsp[-4].ival;
+ yyval.cmdval = make_icmd(C_IF,yyvsp[-2].arg,yyvsp[0].compval); }
+break;
+case 24:
+#line 204 "perly.y"
+{ cmdline = yyvsp[-4].ival;
+ yyval.cmdval = invert(make_icmd(C_IF,yyvsp[-2].arg,yyvsp[0].compval)); }
+break;
+case 25:
+#line 207 "perly.y"
+{ cmdline = yyvsp[-2].ival;
+ yyval.cmdval = make_icmd(C_IF,cmd_to_arg(yyvsp[-1].cmdval),yyvsp[0].compval); }
+break;
+case 26:
+#line 210 "perly.y"
+{ cmdline = yyvsp[-2].ival;
+ yyval.cmdval = invert(make_icmd(C_IF,cmd_to_arg(yyvsp[-1].cmdval),yyvsp[0].compval)); }
+break;
+case 27:
+#line 215 "perly.y"
+{ cmdline = yyvsp[-4].ival;
+ yyval.cmdval = wopt(add_label(yyvsp[-5].cval,
+ make_ccmd(C_WHILE,1,yyvsp[-2].arg,yyvsp[0].compval) )); }
+break;
+case 28:
+#line 219 "perly.y"
+{ cmdline = yyvsp[-4].ival;
+ yyval.cmdval = wopt(add_label(yyvsp[-5].cval,
+ invert(make_ccmd(C_WHILE,1,yyvsp[-2].arg,yyvsp[0].compval)) )); }
+break;
+case 29:
+#line 223 "perly.y"
+{ cmdline = yyvsp[-2].ival;
+ yyval.cmdval = wopt(add_label(yyvsp[-3].cval,
+ make_ccmd(C_WHILE, 1, cmd_to_arg(yyvsp[-1].cmdval),yyvsp[0].compval) )); }
+break;
+case 30:
+#line 227 "perly.y"
+{ cmdline = yyvsp[-2].ival;
+ yyval.cmdval = wopt(add_label(yyvsp[-3].cval,
+ invert(make_ccmd(C_WHILE,1,cmd_to_arg(yyvsp[-1].cmdval),yyvsp[0].compval)) )); }
+break;
+case 31:
+#line 231 "perly.y"
+{ cmdline = yyvsp[-5].ival;
+ /*
+ * The following gobbledygook catches EXPRs that
+ * aren't explicit array refs and translates
+ * foreach VAR (EXPR) {
+ * into
+ * @ary = EXPR;
+ * foreach VAR (@ary) {
+ * where @ary is a hidden array made by genstab().
+ * (Note that @ary may become a local array if
+ * it is determined that it might be called
+ * recursively. See cmd_tosave().)
+ */
+ if (yyvsp[-2].arg->arg_type != O_ARRAY) {
+ scrstab = aadd(genstab());
+ yyval.cmdval = append_line(
+ make_acmd(C_EXPR, Nullstab,
+ l(make_op(O_ASSIGN,2,
+ listish(make_op(O_ARRAY, 1,
+ stab2arg(A_STAB,scrstab),
+ Nullarg,Nullarg )),
+ listish(make_list(yyvsp[-2].arg)),
+ Nullarg)),
+ Nullarg),
+ wopt(over(yyvsp[-4].stabval,add_label(yyvsp[-6].cval,
+ make_ccmd(C_WHILE, 0,
+ make_op(O_ARRAY, 1,
+ stab2arg(A_STAB,scrstab),
+ Nullarg,Nullarg ),
+ yyvsp[0].compval)))));
+ yyval.cmdval->c_line = yyvsp[-5].ival;
+ yyval.cmdval->c_head->c_line = yyvsp[-5].ival;
+ }
+ else {
+ yyval.cmdval = wopt(over(yyvsp[-4].stabval,add_label(yyvsp[-6].cval,
+ make_ccmd(C_WHILE,1,yyvsp[-2].arg,yyvsp[0].compval) )));
+ }
+ }
+break;
+case 32:
+#line 270 "perly.y"
+{ cmdline = yyvsp[-4].ival;
+ if (yyvsp[-2].arg->arg_type != O_ARRAY) {
+ scrstab = aadd(genstab());
+ yyval.cmdval = append_line(
+ make_acmd(C_EXPR, Nullstab,
+ l(make_op(O_ASSIGN,2,
+ listish(make_op(O_ARRAY, 1,
+ stab2arg(A_STAB,scrstab),
+ Nullarg,Nullarg )),
+ listish(make_list(yyvsp[-2].arg)),
+ Nullarg)),
+ Nullarg),
+ wopt(over(defstab,add_label(yyvsp[-5].cval,
+ make_ccmd(C_WHILE, 0,
+ make_op(O_ARRAY, 1,
+ stab2arg(A_STAB,scrstab),
+ Nullarg,Nullarg ),
+ yyvsp[0].compval)))));
+ yyval.cmdval->c_line = yyvsp[-4].ival;
+ yyval.cmdval->c_head->c_line = yyvsp[-4].ival;
+ }
+ else { /* lisp, anyone? */
+ yyval.cmdval = wopt(over(defstab,add_label(yyvsp[-5].cval,
+ make_ccmd(C_WHILE,1,yyvsp[-2].arg,yyvsp[0].compval) )));
+ }
+ }
+break;
+case 33:
+#line 298 "perly.y"
+{ yyval.compval.comp_true = yyvsp[0].cmdval;
+ yyval.compval.comp_alt = yyvsp[-2].cmdval;
+ cmdline = yyvsp[-8].ival;
+ yyval.cmdval = append_line(yyvsp[-6].cmdval,wopt(add_label(yyvsp[-9].cval,
+ make_ccmd(C_WHILE,1,yyvsp[-4].arg,yyval.compval) ))); }
+break;
+case 34:
+#line 304 "perly.y"
+{ yyval.cmdval = add_label(yyvsp[-1].cval,make_ccmd(C_BLOCK,1,Nullarg,yyvsp[0].compval)); }
+break;
+case 35:
+#line 308 "perly.y"
+{ yyval.cmdval = Nullcmd; }
+break;
+case 37:
+#line 313 "perly.y"
+{ (void)scanstr("1",SCAN_DEF); yyval.arg = yylval.arg; }
+break;
+case 39:
+#line 318 "perly.y"
+{ yyval.cval = Nullch; }
+break;
+case 41:
+#line 323 "perly.y"
+{ yyval.ival = 0; }
+break;
+case 42:
+#line 325 "perly.y"
+{ yyval.ival = 0; }
+break;
+case 43:
+#line 327 "perly.y"
+{ yyval.ival = 0; }
+break;
+case 44:
+#line 331 "perly.y"
+{ if (strEQ(yyvsp[-2].cval,"stdout"))
+ make_form(stabent("STDOUT",TRUE),yyvsp[0].formval);
+ else if (strEQ(yyvsp[-2].cval,"stderr"))
+ make_form(stabent("STDERR",TRUE),yyvsp[0].formval);
+ else
+ make_form(stabent(yyvsp[-2].cval,TRUE),yyvsp[0].formval);
+ Safefree(yyvsp[-2].cval); yyvsp[-2].cval = Nullch; }
+break;
+case 45:
+#line 339 "perly.y"
+{ make_form(stabent("STDOUT",TRUE),yyvsp[0].formval); }
+break;
+case 46:
+#line 343 "perly.y"
+{ make_sub(yyvsp[-1].cval,yyvsp[0].cmdval);
+ cmdline = NOLINE;
+ if (savestack->ary_fill > yyvsp[-2].ival)
+ restorelist(yyvsp[-2].ival); }
+break;
+case 47:
+#line 350 "perly.y"
+{ char tmpbuf[256];
+ STAB *tmpstab;
+
+ savehptr(&curstash);
+ saveitem(curstname);
+ str_set(curstname,yyvsp[-1].cval);
+ sprintf(tmpbuf,"'_%s",yyvsp[-1].cval);
+ tmpstab = stabent(tmpbuf,TRUE);
+ if (!stab_xhash(tmpstab))
+ stab_xhash(tmpstab) = hnew(0);
+ curstash = stab_xhash(tmpstab);
+ if (!curstash->tbl_name)
+ curstash->tbl_name = savestr(yyvsp[-1].cval);
+ curstash->tbl_coeffsize = 0;
+ Safefree(yyvsp[-1].cval); yyvsp[-1].cval = Nullch;
+ cmdline = NOLINE;
+ expectterm = 2;
+ }
+break;
+case 48:
+#line 371 "perly.y"
+{ yyval.arg = yyvsp[0].arg; }
+break;
+case 49:
+#line 375 "perly.y"
+{ yyval.arg = make_op(O_COMMA, 2, yyvsp[-2].arg, yyvsp[0].arg, Nullarg); }
+break;
+case 51:
+#line 380 "perly.y"
+{ yyval.arg = yyvsp[0].arg; }
+break;
+case 52:
+#line 384 "perly.y"
+{ yyvsp[-2].arg = listish(yyvsp[-2].arg);
+ if (yyvsp[-2].arg->arg_type == O_ASSIGN && yyvsp[-2].arg->arg_len == 1)
+ yyvsp[-2].arg->arg_type = O_ITEM; /* a local() */
+ if (yyvsp[-2].arg->arg_type == O_LIST)
+ yyvsp[0].arg = listish(yyvsp[0].arg);
+ yyval.arg = l(make_op(O_ASSIGN, 2, yyvsp[-2].arg, yyvsp[0].arg, Nullarg)); }
+break;
+case 53:
+#line 391 "perly.y"
+{ yyval.arg = l(make_op(O_POW, 2, yyvsp[-3].arg, yyvsp[0].arg, Nullarg)); }
+break;
+case 54:
+#line 393 "perly.y"
+{ yyval.arg = l(make_op(yyvsp[-2].ival, 2, yyvsp[-3].arg, yyvsp[0].arg, Nullarg)); }
+break;
+case 55:
+#line 395 "perly.y"
+{ yyval.arg = rcatmaybe(l(make_op(yyvsp[-2].ival, 2, yyvsp[-3].arg, yyvsp[0].arg, Nullarg)));}
+break;
+case 56:
+#line 397 "perly.y"
+{ yyval.arg = l(make_op(O_LEFT_SHIFT, 2, yyvsp[-3].arg, yyvsp[0].arg, Nullarg)); }
+break;
+case 57:
+#line 399 "perly.y"
+{ yyval.arg = l(make_op(O_RIGHT_SHIFT, 2, yyvsp[-3].arg, yyvsp[0].arg, Nullarg)); }
+break;
+case 58:
+#line 401 "perly.y"
+{ yyval.arg = l(make_op(O_BIT_AND, 2, yyvsp[-3].arg, yyvsp[0].arg, Nullarg)); }
+break;
+case 59:
+#line 403 "perly.y"
+{ yyval.arg = l(make_op(O_XOR, 2, yyvsp[-3].arg, yyvsp[0].arg, Nullarg)); }
+break;
+case 60:
+#line 405 "perly.y"
+{ yyval.arg = l(make_op(O_BIT_OR, 2, yyvsp[-3].arg, yyvsp[0].arg, Nullarg)); }
+break;
+case 61:
+#line 409 "perly.y"
+{ yyval.arg = make_op(O_POW, 2, yyvsp[-2].arg, yyvsp[0].arg, Nullarg); }
+break;
+case 62:
+#line 411 "perly.y"
+{ if (yyvsp[-1].ival == O_REPEAT)
+ yyvsp[-2].arg = listish(yyvsp[-2].arg);
+ yyval.arg = make_op(yyvsp[-1].ival, 2, yyvsp[-2].arg, yyvsp[0].arg, Nullarg);
+ if (yyvsp[-1].ival == O_REPEAT) {
+ if (yyval.arg[1].arg_type != A_EXPR ||
+ yyval.arg[1].arg_ptr.arg_arg->arg_type != O_LIST)
+ yyval.arg[1].arg_flags &= ~AF_ARYOK;
+ } }
+break;
+case 63:
+#line 420 "perly.y"
+{ yyval.arg = make_op(yyvsp[-1].ival, 2, yyvsp[-2].arg, yyvsp[0].arg, Nullarg); }
+break;
+case 64:
+#line 422 "perly.y"
+{ yyval.arg = make_op(O_LEFT_SHIFT, 2, yyvsp[-2].arg, yyvsp[0].arg, Nullarg); }
+break;
+case 65:
+#line 424 "perly.y"
+{ yyval.arg = make_op(O_RIGHT_SHIFT, 2, yyvsp[-2].arg, yyvsp[0].arg, Nullarg); }
+break;
+case 66:
+#line 426 "perly.y"
+{ yyval.arg = make_op(yyvsp[-1].ival, 2, yyvsp[-2].arg, yyvsp[0].arg, Nullarg); }
+break;
+case 67:
+#line 428 "perly.y"
+{ yyval.arg = make_op(yyvsp[-1].ival, 2, yyvsp[-2].arg, yyvsp[0].arg, Nullarg); }
+break;
+case 68:
+#line 430 "perly.y"
+{ yyval.arg = make_op(O_BIT_AND, 2, yyvsp[-2].arg, yyvsp[0].arg, Nullarg); }
+break;
+case 69:
+#line 432 "perly.y"
+{ yyval.arg = make_op(O_XOR, 2, yyvsp[-2].arg, yyvsp[0].arg, Nullarg); }
+break;
+case 70:
+#line 434 "perly.y"
+{ yyval.arg = make_op(O_BIT_OR, 2, yyvsp[-2].arg, yyvsp[0].arg, Nullarg); }
+break;
+case 71:
+#line 436 "perly.y"
+{ arg4 = Nullarg;
+ yyval.arg = make_op(O_F_OR_R, 4, yyvsp[-2].arg, yyvsp[0].arg, Nullarg);
+ yyval.arg[0].arg_flags |= yyvsp[-1].ival; }
+break;
+case 72:
+#line 440 "perly.y"
+{ yyval.arg = make_op(O_AND, 2, yyvsp[-2].arg, yyvsp[0].arg, Nullarg); }
+break;
+case 73:
+#line 442 "perly.y"
+{ yyval.arg = make_op(O_OR, 2, yyvsp[-2].arg, yyvsp[0].arg, Nullarg); }
+break;
+case 74:
+#line 444 "perly.y"
+{ yyval.arg = make_op(O_COND_EXPR, 3, yyvsp[-4].arg, yyvsp[-2].arg, yyvsp[0].arg); }
+break;
+case 75:
+#line 446 "perly.y"
+{ yyval.arg = mod_match(O_MATCH, yyvsp[-2].arg, yyvsp[0].arg); }
+break;
+case 76:
+#line 448 "perly.y"
+{ yyval.arg = mod_match(O_NMATCH, yyvsp[-2].arg, yyvsp[0].arg); }
+break;
+case 77:
+#line 450 "perly.y"
+{ yyval.arg = yyvsp[0].arg; }
+break;
+case 78:
+#line 454 "perly.y"
+{ yyval.arg = make_op(O_NEGATE, 1, yyvsp[0].arg, Nullarg, Nullarg); }
+break;
+case 79:
+#line 456 "perly.y"
+{ yyval.arg = yyvsp[0].arg; }
+break;
+case 80:
+#line 458 "perly.y"
+{ yyval.arg = make_op(O_NOT, 1, yyvsp[0].arg, Nullarg, Nullarg); }
+break;
+case 81:
+#line 460 "perly.y"
+{ yyval.arg = make_op(O_COMPLEMENT, 1, yyvsp[0].arg, Nullarg, Nullarg);}
+break;
+case 82:
+#line 462 "perly.y"
+{ yyval.arg = addflags(1, AF_POST|AF_UP,
+ l(make_op(O_ITEM,1,yyvsp[-1].arg,Nullarg,Nullarg))); }
+break;
+case 83:
+#line 465 "perly.y"
+{ yyval.arg = addflags(1, AF_POST,
+ l(make_op(O_ITEM,1,yyvsp[-1].arg,Nullarg,Nullarg))); }
+break;
+case 84:
+#line 468 "perly.y"
+{ yyval.arg = addflags(1, AF_PRE|AF_UP,
+ l(make_op(O_ITEM,1,yyvsp[0].arg,Nullarg,Nullarg))); }
+break;
+case 85:
+#line 471 "perly.y"
+{ yyval.arg = addflags(1, AF_PRE,
+ l(make_op(O_ITEM,1,yyvsp[0].arg,Nullarg,Nullarg))); }
+break;
+case 86:
+#line 474 "perly.y"
+{ opargs[yyvsp[-1].ival] = 0; /* force it special */
+ yyval.arg = make_op(yyvsp[-1].ival, 1,
+ stab2arg(A_STAB,stabent(yyvsp[0].cval,TRUE)),
+ Nullarg, Nullarg);
+ Safefree(yyvsp[0].cval); yyvsp[0].cval = Nullch;
+ }
+break;
+case 87:
+#line 481 "perly.y"
+{ opargs[yyvsp[-1].ival] = 1;
+ yyval.arg = make_op(yyvsp[-1].ival, 1, yyvsp[0].arg, Nullarg, Nullarg); }
+break;
+case 88:
+#line 484 "perly.y"
+{ opargs[yyvsp[0].ival] = (yyvsp[0].ival != O_FTTTY);
+ yyval.arg = make_op(yyvsp[0].ival, 1,
+ stab2arg(A_STAB,
+ yyvsp[0].ival == O_FTTTY?stabent("STDIN",TRUE):defstab),
+ Nullarg, Nullarg); }
+break;
+case 89:
+#line 490 "perly.y"
+{ yyval.arg = l(localize(make_op(O_ASSIGN, 1,
+ localize(listish(make_list(yyvsp[-1].arg))),
+ Nullarg,Nullarg))); }
+break;
+case 90:
+#line 494 "perly.y"
+{ yyval.arg = make_list(yyvsp[-1].arg); }
+break;
+case 91:
+#line 496 "perly.y"
+{ yyval.arg = make_list(Nullarg); }
+break;
+case 92:
+#line 498 "perly.y"
+{ yyval.arg = make_op(O_DOFILE,2,yyvsp[0].arg,Nullarg,Nullarg);
+ allstabs = TRUE;}
+break;
+case 93:
+#line 501 "perly.y"
+{ yyval.arg = cmd_to_arg(yyvsp[0].cmdval); }
+break;
+case 94:
+#line 503 "perly.y"
+{ yyval.arg = stab2arg(A_STAB,yyvsp[0].stabval); }
+break;
+case 95:
+#line 505 "perly.y"
+{ yyval.arg = stab2arg(A_STAR,yyvsp[0].stabval); }
+break;
+case 96:
+#line 507 "perly.y"
+{ yyval.arg = make_op(O_AELEM, 2,
+ stab2arg(A_STAB,aadd(yyvsp[-3].stabval)), yyvsp[-1].arg, Nullarg); }
+break;
+case 97:
+#line 510 "perly.y"
+{ yyval.arg = make_op(O_HASH, 1,
+ stab2arg(A_STAB,yyvsp[0].stabval),
+ Nullarg, Nullarg); }
+break;
+case 98:
+#line 514 "perly.y"
+{ yyval.arg = make_op(O_ARRAY, 1,
+ stab2arg(A_STAB,yyvsp[0].stabval),
+ Nullarg, Nullarg); }
+break;
+case 99:
+#line 518 "perly.y"
+{ yyval.arg = make_op(O_HELEM, 2,
+ stab2arg(A_STAB,hadd(yyvsp[-4].stabval)),
+ jmaybe(yyvsp[-2].arg),
+ Nullarg);
+ expectterm = FALSE; }
+break;
+case 100:
+#line 524 "perly.y"
+{ yyval.arg = make_op(O_LSLICE, 3,
+ Nullarg,
+ listish(make_list(yyvsp[-1].arg)),
+ listish(make_list(yyvsp[-4].arg))); }
+break;
+case 101:
+#line 529 "perly.y"
+{ yyval.arg = make_op(O_LSLICE, 3,
+ Nullarg,
+ listish(make_list(yyvsp[-1].arg)),
+ Nullarg); }
+break;
+case 102:
+#line 534 "perly.y"
+{ yyval.arg = make_op(O_ASLICE, 2,
+ stab2arg(A_STAB,aadd(yyvsp[-3].stabval)),
+ listish(make_list(yyvsp[-1].arg)),
+ Nullarg); }
+break;
+case 103:
+#line 539 "perly.y"
+{ yyval.arg = make_op(O_HSLICE, 2,
+ stab2arg(A_STAB,hadd(yyvsp[-4].stabval)),
+ listish(make_list(yyvsp[-2].arg)),
+ Nullarg);
+ expectterm = FALSE; }
+break;
+case 104:
+#line 545 "perly.y"
+{ yyval.arg = make_op(O_DELETE, 2,
+ stab2arg(A_STAB,hadd(yyvsp[-4].stabval)),
+ jmaybe(yyvsp[-2].arg),
+ Nullarg);
+ expectterm = FALSE; }
+break;
+case 105:
+#line 551 "perly.y"
+{ yyval.arg = make_op(O_DELETE, 2,
+ stab2arg(A_STAB,hadd(yyvsp[-5].stabval)),
+ jmaybe(yyvsp[-3].arg),
+ Nullarg);
+ expectterm = FALSE; }
+break;
+case 106:
+#line 557 "perly.y"
+{ yyval.arg = stab2arg(A_ARYLEN,yyvsp[0].stabval); }
+break;
+case 107:
+#line 559 "perly.y"
+{ yyval.arg = yyvsp[0].arg; }
+break;
+case 108:
+#line 561 "perly.y"
+{ yyval.arg = yyvsp[0].arg; }
+break;
+case 109:
+#line 563 "perly.y"
+{ yyval.arg = yyvsp[0].arg; }
+break;
+case 110:
+#line 565 "perly.y"
+{ yyval.arg = yyvsp[0].arg; }
+break;
+case 111:
+#line 567 "perly.y"
+{ yyval.arg = make_op((perldb ? O_DBSUBR : O_SUBR), 2,
+ stab2arg(A_WORD,stabent(yyvsp[-3].cval,MULTI)),
+ make_list(yyvsp[-1].arg),
+ Nullarg); Safefree(yyvsp[-3].cval); yyvsp[-3].cval = Nullch;
+ yyval.arg->arg_flags |= AF_DEPR; }
+break;
+case 112:
+#line 573 "perly.y"
+{ yyval.arg = make_op((perldb ? O_DBSUBR : O_SUBR), 2,
+ stab2arg(A_WORD,stabent(yyvsp[-3].cval,MULTI)),
+ make_list(yyvsp[-1].arg),
+ Nullarg); Safefree(yyvsp[-3].cval); yyvsp[-3].cval = Nullch; }
+break;
+case 113:
+#line 578 "perly.y"
+{ yyval.arg = make_op((perldb ? O_DBSUBR : O_SUBR), 2,
+ stab2arg(A_WORD,stabent(yyvsp[-2].cval,MULTI)),
+ make_list(Nullarg),
+ Nullarg);
+ Safefree(yyvsp[-2].cval); yyvsp[-2].cval = Nullch;
+ yyval.arg->arg_flags |= AF_DEPR; }
+break;
+case 114:
+#line 585 "perly.y"
+{ yyval.arg = make_op((perldb ? O_DBSUBR : O_SUBR), 2,
+ stab2arg(A_WORD,stabent(yyvsp[-2].cval,MULTI)),
+ make_list(Nullarg),
+ Nullarg);
+ Safefree(yyvsp[-2].cval); yyvsp[-2].cval = Nullch;
+ }
+break;
+case 115:
+#line 592 "perly.y"
+{ yyval.arg = make_op((perldb ? O_DBSUBR : O_SUBR), 2,
+ stab2arg(A_WORD,stabent(yyvsp[0].cval,MULTI)),
+ Nullarg,
+ Nullarg);
+ Safefree(yyvsp[0].cval); yyvsp[0].cval = Nullch;
+ }
+break;
+case 116:
+#line 599 "perly.y"
+{ yyval.arg = make_op((perldb ? O_DBSUBR : O_SUBR), 2,
+ stab2arg(A_STAB,yyvsp[-3].stabval),
+ make_list(yyvsp[-1].arg),
+ Nullarg);
+ yyval.arg->arg_flags |= AF_DEPR; }
+break;
+case 117:
+#line 605 "perly.y"
+{ yyval.arg = make_op((perldb ? O_DBSUBR : O_SUBR), 2,
+ stab2arg(A_STAB,yyvsp[-3].stabval),
+ make_list(yyvsp[-1].arg),
+ Nullarg); }
+break;
+case 118:
+#line 610 "perly.y"
+{ yyval.arg = make_op((perldb ? O_DBSUBR : O_SUBR), 2,
+ stab2arg(A_STAB,yyvsp[-2].stabval),
+ make_list(Nullarg),
+ Nullarg);
+ yyval.arg->arg_flags |= AF_DEPR; }
+break;
+case 119:
+#line 616 "perly.y"
+{ yyval.arg = make_op((perldb ? O_DBSUBR : O_SUBR), 2,
+ stab2arg(A_STAB,yyvsp[-2].stabval),
+ make_list(Nullarg),
+ Nullarg); }
+break;
+case 120:
+#line 621 "perly.y"
+{ yyval.arg = make_op((perldb ? O_DBSUBR : O_SUBR), 2,
+ stab2arg(A_STAB,yyvsp[0].stabval),
+ Nullarg,
+ Nullarg); }
+break;
+case 121:
+#line 626 "perly.y"
+{ yyval.arg = make_op(yyvsp[0].ival,0,Nullarg,Nullarg,Nullarg); }
+break;
+case 122:
+#line 628 "perly.y"
+{ yyval.arg = make_op(yyvsp[-1].ival,1,cval_to_arg(yyvsp[0].cval),
+ Nullarg,Nullarg); }
+break;
+case 123:
+#line 631 "perly.y"
+{ yyval.arg = make_op(yyvsp[0].ival,0,Nullarg,Nullarg,Nullarg); }
+break;
+case 124:
+#line 633 "perly.y"
+{ yyval.arg = make_op(yyvsp[-1].ival,1,cmd_to_arg(yyvsp[0].cmdval),Nullarg,Nullarg); }
+break;
+case 125:
+#line 635 "perly.y"
+{ yyval.arg = make_op(yyvsp[-1].ival,1,yyvsp[0].arg,Nullarg,Nullarg); }
+break;
+case 126:
+#line 637 "perly.y"
+{ yyval.arg = make_op(O_SELECT, 0, Nullarg, Nullarg, Nullarg);}
+break;
+case 127:
+#line 639 "perly.y"
+{ yyval.arg = make_op(O_SELECT, 1,
+ stab2arg(A_WORD,stabent(yyvsp[0].cval,TRUE)),
+ Nullarg,
+ Nullarg);
+ Safefree(yyvsp[0].cval); yyvsp[0].cval = Nullch; }
+break;
+case 128:
+#line 645 "perly.y"
+{ yyval.arg = make_op(O_SELECT, 1, yyvsp[-1].arg, Nullarg, Nullarg); }
+break;
+case 129:
+#line 647 "perly.y"
+{ arg4 = yyvsp[-1].arg;
+ yyval.arg = make_op(O_SSELECT, 4, yyvsp[-4].arg, yyvsp[-3].arg, yyvsp[-2].arg); }
+break;
+case 130:
+#line 650 "perly.y"
+{ yyval.arg = make_op(O_OPEN, 2,
+ stab2arg(A_WORD,stabent(yyvsp[0].cval,TRUE)),
+ stab2arg(A_STAB,stabent(yyvsp[0].cval,TRUE)),
+ Nullarg);
+ Safefree(yyvsp[0].cval); yyvsp[0].cval = Nullch;
+ }
+break;
+case 131:
+#line 657 "perly.y"
+{ yyval.arg = make_op(O_OPEN, 2,
+ stab2arg(A_WORD,stabent(yyvsp[-1].cval,TRUE)),
+ stab2arg(A_STAB,stabent(yyvsp[-1].cval,TRUE)),
+ Nullarg);
+ Safefree(yyvsp[-1].cval); yyvsp[-1].cval = Nullch;
+ }
+break;
+case 132:
+#line 664 "perly.y"
+{ yyval.arg = make_op(O_OPEN, 2,
+ yyvsp[-2].arg,
+ yyvsp[-1].arg, Nullarg); }
+break;
+case 133:
+#line 668 "perly.y"
+{ yyval.arg = make_op(yyvsp[-3].ival, 1,
+ yyvsp[-1].arg,
+ Nullarg, Nullarg); }
+break;
+case 134:
+#line 672 "perly.y"
+{ yyval.arg = make_op(yyvsp[-1].ival, 1,
+ stab2arg(A_WORD,stabent(yyvsp[0].cval,TRUE)),
+ Nullarg, Nullarg);
+ Safefree(yyvsp[0].cval); yyvsp[0].cval = Nullch; }
+break;
+case 135:
+#line 677 "perly.y"
+{ yyval.arg = make_op(yyvsp[-1].ival, 1,
+ stab2arg(A_STAB,yyvsp[0].stabval),
+ Nullarg, Nullarg); }
+break;
+case 136:
+#line 681 "perly.y"
+{ yyval.arg = make_op(yyvsp[-2].ival, 1,
+ stab2arg(A_WORD,Nullstab),
+ Nullarg, Nullarg); }
+break;
+case 137:
+#line 685 "perly.y"
+{ yyval.arg = make_op(yyvsp[0].ival, 0,
+ Nullarg, Nullarg, Nullarg); }
+break;
+case 138:
+#line 688 "perly.y"
+{ yyval.arg = make_op(yyvsp[-4].ival, 2, yyvsp[-2].arg, yyvsp[-1].arg, Nullarg); }
+break;
+case 139:
+#line 690 "perly.y"
+{ yyval.arg = make_op(yyvsp[-5].ival, 3, yyvsp[-3].arg, yyvsp[-2].arg, make_list(yyvsp[-1].arg)); }
+break;
+case 140:
+#line 692 "perly.y"
+{ yyval.arg = make_op(yyvsp[-5].ival, 2, yyvsp[-3].arg, yyvsp[-1].arg, Nullarg); }
+break;
+case 141:
+#line 694 "perly.y"
+{ arg4 = yyvsp[-1].arg; yyval.arg = make_op(yyvsp[-6].ival, 4, yyvsp[-4].arg, yyvsp[-3].arg, yyvsp[-2].arg); }
+break;
+case 142:
+#line 696 "perly.y"
+{ arg4 = yyvsp[-2].arg; arg5 = yyvsp[-1].arg;
+ yyval.arg = make_op(yyvsp[-8].ival, 5, yyvsp[-6].arg, yyvsp[-4].arg, yyvsp[-3].arg); }
+break;
+case 143:
+#line 699 "perly.y"
+{ yyval.arg = make_op(yyvsp[-5].ival, 2,
+ yyvsp[-3].arg,
+ make_list(yyvsp[-1].arg),
+ Nullarg); }
+break;
+case 144:
+#line 704 "perly.y"
+{ yyval.arg = make_op(O_POP, 1, yyvsp[0].arg, Nullarg, Nullarg); }
+break;
+case 145:
+#line 706 "perly.y"
+{ yyval.arg = make_op(O_POP, 1, yyvsp[-1].arg, Nullarg, Nullarg); }
+break;
+case 146:
+#line 708 "perly.y"
+{ yyval.arg = make_op(O_SHIFT, 1, yyvsp[0].arg, Nullarg, Nullarg); }
+break;
+case 147:
+#line 710 "perly.y"
+{ yyval.arg = make_op(O_SHIFT, 1, yyvsp[-1].arg, Nullarg, Nullarg); }
+break;
+case 148:
+#line 712 "perly.y"
+{ yyval.arg = make_op(O_SHIFT, 1,
+ stab2arg(A_STAB,
+ aadd(stabent(subline ? "_" : "ARGV", TRUE))),
+ Nullarg, Nullarg); }
+break;
+case 149:
+#line 717 "perly.y"
+{ static char p[]="/\\s+/";
+ char *oldend = bufend;
+ ARG *oldarg = yylval.arg;
+
+ bufend=p+5;
+ (void)scanpat(p);
+ bufend=oldend;
+ yyval.arg = make_split(defstab,yylval.arg,Nullarg);
+ yylval.arg = oldarg; }
+break;
+case 150:
+#line 727 "perly.y"
+{ yyval.arg = mod_match(O_MATCH, yyvsp[-2].arg,
+ make_split(defstab,yyvsp[-3].arg,yyvsp[-1].arg));}
+break;
+case 151:
+#line 730 "perly.y"
+{ yyval.arg = mod_match(O_MATCH, yyvsp[-1].arg,
+ make_split(defstab,yyvsp[-2].arg,Nullarg) ); }
+break;
+case 152:
+#line 733 "perly.y"
+{ yyval.arg = mod_match(O_MATCH,
+ stab2arg(A_STAB,defstab),
+ make_split(defstab,yyvsp[-1].arg,Nullarg) ); }
+break;
+case 153:
+#line 737 "perly.y"
+{ yyval.arg = make_op(yyvsp[-4].ival, 2,
+ yyvsp[-2].arg,
+ listish(make_list(yyvsp[-1].arg)),
+ Nullarg); }
+break;
+case 154:
+#line 742 "perly.y"
+{ yyval.arg = make_op(yyvsp[-3].ival, 1,
+ make_list(yyvsp[-1].arg),
+ Nullarg,
+ Nullarg); }
+break;
+case 155:
+#line 747 "perly.y"
+{ yyval.arg = l(make_op(yyvsp[-1].ival, 1, fixl(yyvsp[-1].ival,yyvsp[0].arg),
+ Nullarg, Nullarg)); }
+break;
+case 156:
+#line 750 "perly.y"
+{ yyval.arg = l(make_op(yyvsp[0].ival, 1,
+ stab2arg(A_STAB,defstab),
+ Nullarg, Nullarg)); }
+break;
+case 157:
+#line 754 "perly.y"
+{ yyval.arg = make_op(yyvsp[0].ival, 0, Nullarg, Nullarg, Nullarg); }
+break;
+case 158:
+#line 756 "perly.y"
+{ yyval.arg = make_op(yyvsp[-2].ival, 0, Nullarg, Nullarg, Nullarg); }
+break;
+case 159:
+#line 758 "perly.y"
+{ yyval.arg = make_op(yyvsp[-2].ival, 0, Nullarg, Nullarg, Nullarg); }
+break;
+case 160:
+#line 760 "perly.y"
+{ yyval.arg = make_op(yyvsp[-3].ival, 1, yyvsp[-1].arg, Nullarg, Nullarg); }
+break;
+case 161:
+#line 762 "perly.y"
+{ yyval.arg = make_op(yyvsp[-4].ival, 2, yyvsp[-2].arg, yyvsp[-1].arg, Nullarg);
+ if (yyvsp[-4].ival == O_INDEX && yyval.arg[2].arg_type == A_SINGLE)
+ fbmcompile(yyval.arg[2].arg_ptr.arg_str,0); }
+break;
+case 162:
+#line 766 "perly.y"
+{ yyval.arg = make_op(yyvsp[-4].ival, 2, yyvsp[-2].arg, yyvsp[-1].arg, Nullarg);
+ if (yyvsp[-4].ival == O_INDEX && yyval.arg[2].arg_type == A_SINGLE)
+ fbmcompile(yyval.arg[2].arg_ptr.arg_str,0); }
+break;
+case 163:
+#line 770 "perly.y"
+{ yyval.arg = make_op(yyvsp[-5].ival, 3, yyvsp[-3].arg, yyvsp[-2].arg, yyvsp[-1].arg);
+ if (yyvsp[-5].ival == O_INDEX && yyval.arg[2].arg_type == A_SINGLE)
+ fbmcompile(yyval.arg[2].arg_ptr.arg_str,0); }
+break;
+case 164:
+#line 774 "perly.y"
+{ yyval.arg = make_op(yyvsp[-5].ival, 3, yyvsp[-3].arg, yyvsp[-2].arg, yyvsp[-1].arg); }
+break;
+case 165:
+#line 776 "perly.y"
+{ arg4 = yyvsp[-1].arg;
+ yyval.arg = make_op(yyvsp[-6].ival, 4, yyvsp[-4].arg, yyvsp[-3].arg, yyvsp[-2].arg); }
+break;
+case 166:
+#line 779 "perly.y"
+{ arg4 = yyvsp[-2].arg; arg5 = yyvsp[-1].arg;
+ yyval.arg = make_op(yyvsp[-7].ival, 5, yyvsp[-5].arg, yyvsp[-4].arg, yyvsp[-3].arg); }
+break;
+case 167:
+#line 782 "perly.y"
+{ yyval.arg = make_op(yyvsp[-3].ival, 1,
+ yyvsp[-1].arg,
+ Nullarg,
+ Nullarg); }
+break;
+case 168:
+#line 787 "perly.y"
+{ yyval.arg = make_op(yyvsp[-1].ival, 1,
+ yyvsp[0].arg,
+ Nullarg,
+ Nullarg); }
+break;
+case 169:
+#line 792 "perly.y"
+{ yyval.arg = make_op(yyvsp[-5].ival, 3, yyvsp[-3].arg, yyvsp[-2].arg, yyvsp[-1].arg); }
+break;
+case 172:
+#line 798 "perly.y"
+{ yyval.arg = make_op(yyvsp[0].ival,2,
+ stab2arg(A_WORD,Nullstab),
+ stab2arg(A_STAB,defstab),
+ Nullarg); }
+break;
+case 173:
+#line 803 "perly.y"
+{ yyval.arg = make_op(yyvsp[-1].ival,2,
+ stab2arg(A_WORD,Nullstab),
+ maybelistish(yyvsp[-1].ival,make_list(yyvsp[0].arg)),
+ Nullarg); }
+break;
+case 174:
+#line 808 "perly.y"
+{ yyval.arg = make_op(yyvsp[-1].ival,2,
+ stab2arg(A_WORD,stabent(yyvsp[0].cval,TRUE)),
+ stab2arg(A_STAB,defstab),
+ Nullarg);
+ Safefree(yyvsp[0].cval); yyvsp[0].cval = Nullch;
+ }
+break;
+case 175:
+#line 815 "perly.y"
+{ yyval.arg = make_op(yyvsp[-2].ival,2,
+ stab2arg(A_WORD,stabent(yyvsp[-1].cval,TRUE)),
+ maybelistish(yyvsp[-2].ival,make_list(yyvsp[0].arg)),
+ Nullarg); Safefree(yyvsp[-1].cval); yyvsp[-1].cval = Nullch; }
+break;
+case 176:
+#line 820 "perly.y"
+{ yyval.arg = make_op(yyvsp[-2].ival,2,
+ stab2arg(A_STAB,yyvsp[-1].stabval),
+ maybelistish(yyvsp[-2].ival,make_list(yyvsp[0].arg)),
+ Nullarg); }
+break;
+case 177:
+#line 825 "perly.y"
+{ yyval.arg = make_op(yyvsp[-2].ival,2,
+ cmd_to_arg(yyvsp[-1].cmdval),
+ maybelistish(yyvsp[-2].ival,make_list(yyvsp[0].arg)),
+ Nullarg); }
+break;
+case 178:
+#line 832 "perly.y"
+{ yyval.arg = stab2arg(A_WORD,stabent(yyvsp[0].cval,TRUE));
+ Safefree(yyvsp[0].cval); yyvsp[0].cval = Nullch;}
+break;
+case 180:
+#line 838 "perly.y"
+{ yyval.arg = stab2arg(A_WORD,aadd(stabent(yyvsp[0].cval,TRUE)));
+ Safefree(yyvsp[0].cval); yyvsp[0].cval = Nullch; }
+break;
+case 181:
+#line 841 "perly.y"
+{ yyval.arg = stab2arg(A_STAB,yyvsp[0].stabval); }
+break;
+case 182:
+#line 845 "perly.y"
+{ yyval.arg = stab2arg(A_WORD,hadd(stabent(yyvsp[0].cval,TRUE)));
+ Safefree(yyvsp[0].cval); yyvsp[0].cval = Nullch; }
+break;
+case 183:
+#line 848 "perly.y"
+{ yyval.arg = stab2arg(A_STAB,yyvsp[0].stabval); }
+break;
+case 184:
+#line 852 "perly.y"
+{ yyval.ival = 1; }
+break;
+case 185:
+#line 854 "perly.y"
+{ yyval.ival = 0; }
+break;
+case 186:
+#line 863 "perly.y"
+{ char *s;
+ yyval.arg = op_new(1);
+ yyval.arg->arg_type = O_ITEM;
+ yyval.arg[1].arg_type = A_SINGLE;
+ yyval.arg[1].arg_ptr.arg_str = str_make(yyvsp[0].cval,0);
+ for (s = yyvsp[0].cval; *s && isLOWER(*s); s++) ;
+ if (dowarn && !*s)
+ warn(
+ "\"%s\" may clash with future reserved word",
+ yyvsp[0].cval );
+ Safefree(yyvsp[0].cval); yyvsp[0].cval = Nullch;
+ }
+break;
+#line 3008 "y.tab.c"
+ }
+ yyssp -= yym;
+ yystate = *yyssp;
+ yyvsp -= yym;
+ yym = yylhs[yyn];
+ if (yystate == 0 && yym == 0)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state 0 to\
+ state %d\n", YYPREFIX, YYFINAL);
+#endif
+ yystate = YYFINAL;
+ *++yyssp = YYFINAL;
+ *++yyvsp = yyval;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, YYFINAL, yychar, yys);
+ }
+#endif
+ }
+ if (yychar == 0) goto yyaccept;
+ goto yyloop;
+ }
+ if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yystate)
+ yystate = yytable[yyn];
+ else
+ yystate = yydgoto[yym];
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state %d \
+to state %d\n", YYPREFIX, *yyssp, yystate);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate;
+ *++yyvsp = yyval;
+ goto yyloop;
+yyoverflow:
+ yyerror("yacc stack overflow");
+yyabort:
+ return (1);
+yyaccept:
+ return (0);
+}
diff --git a/gnu/usr.bin/perl/perl/perly.h b/gnu/usr.bin/perl/perl/perly.h
new file mode 100644
index 0000000..c6f13d1
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/perly.h
@@ -0,0 +1,83 @@
+#define WORD 257
+#define LABEL 258
+#define APPEND 259
+#define OPEN 260
+#define SSELECT 261
+#define LOOPEX 262
+#define DOTDOT 263
+#define USING 264
+#define FORMAT 265
+#define DO 266
+#define SHIFT 267
+#define PUSH 268
+#define POP 269
+#define LVALFUN 270
+#define WHILE 271
+#define UNTIL 272
+#define IF 273
+#define UNLESS 274
+#define ELSE 275
+#define ELSIF 276
+#define CONTINUE 277
+#define SPLIT 278
+#define FLIST 279
+#define FOR 280
+#define FILOP 281
+#define FILOP2 282
+#define FILOP3 283
+#define FILOP4 284
+#define FILOP22 285
+#define FILOP25 286
+#define FUNC0 287
+#define FUNC1 288
+#define FUNC2 289
+#define FUNC2x 290
+#define FUNC3 291
+#define FUNC4 292
+#define FUNC5 293
+#define HSHFUN 294
+#define HSHFUN3 295
+#define FLIST2 296
+#define SUB 297
+#define FILETEST 298
+#define LOCAL 299
+#define DELETE 300
+#define RELOP 301
+#define EQOP 302
+#define MULOP 303
+#define ADDOP 304
+#define PACKAGE 305
+#define AMPER 306
+#define FORMLIST 307
+#define REG 308
+#define ARYLEN 309
+#define ARY 310
+#define HSH 311
+#define STAR 312
+#define SUBST 313
+#define PATTERN 314
+#define RSTRING 315
+#define TRANS 316
+#define LISTOP 317
+#define OROR 318
+#define ANDAND 319
+#define UNIOP 320
+#define LS 321
+#define RS 322
+#define MATCH 323
+#define NMATCH 324
+#define UMINUS 325
+#define POW 326
+#define INC 327
+#define DEC 328
+typedef union {
+ int ival;
+ char *cval;
+ ARG *arg;
+ CMD *cmdval;
+ struct compcmd compval;
+ STAB *stabval;
+ FCMD *formval;
+} YYSTYPE;
+extern YYSTYPE yylval;
+extern YYSTYPE yylval;
diff --git a/gnu/usr.bin/perl/perl/regcomp.c b/gnu/usr.bin/perl/perl/regcomp.c
new file mode 100644
index 0000000..8337e9c
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/regcomp.c
@@ -0,0 +1,1478 @@
+/* NOTE: this is derived from Henry Spencer's regexp code, and should not
+ * confused with the original package (see point 3 below). Thanks, Henry!
+ */
+
+/* Additional note: this code is very heavily munged from Henry's version
+ * in places. In some spots I've traded clarity for efficiency, so don't
+ * blame Henry for some of the lack of readability.
+ */
+
+/* $RCSfile: regcomp.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:39 $
+ *
+ * $Log: regcomp.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:39 nate
+ * PERL!
+ *
+ * Revision 4.0.1.5 92/06/08 15:23:36 lwall
+ * patch20: Perl now distinguishes overlapped copies from non-overlapped
+ * patch20: /^stuff/ wrongly assumed an implicit $* == 1
+ * patch20: /x{0}/ was wrongly interpreted as /x{0,}/
+ * patch20: added \W, \S and \D inside /[...]/
+ *
+ * Revision 4.0.1.4 91/11/05 22:55:14 lwall
+ * patch11: Erratum
+ *
+ * Revision 4.0.1.3 91/11/05 18:22:28 lwall
+ * patch11: minimum match length calculation in regexp is now cumulative
+ * patch11: initial .* in pattern had dependency on value of $*
+ * patch11: certain patterns made use of garbage pointers from uncleared memory
+ * patch11: prepared for ctype implementations that don't define isascii()
+ *
+ * Revision 4.0.1.2 91/06/07 11:48:24 lwall
+ * patch4: new copyright notice
+ * patch4: /(x+) \1/ incorrectly optimized to not match "xxx xx"
+ * patch4: // wouldn't use previous pattern if it started with a null character
+ *
+ * Revision 4.0.1.1 91/04/12 09:04:45 lwall
+ * patch1: random cleanup in cpp namespace
+ *
+ * Revision 4.0 91/03/20 01:39:01 lwall
+ * 4.0 baseline.
+ *
+ */
+/*SUPPRESS 112*/
+/*
+ * regcomp and regexec -- regsub and regerror are not used in perl
+ *
+ * Copyright (c) 1986 by University of Toronto.
+ * Written by Henry Spencer. Not derived from licensed software.
+ *
+ * Permission is granted to anyone to use this software for any
+ * purpose on any computer system, and to redistribute it freely,
+ * subject to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of
+ * this software, no matter how awful, even if they arise
+ * from defects in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either
+ * by explicit claim or by omission.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ *
+ **** Alterations to Henry's code are...
+ ****
+ **** Copyright (c) 1991, Larry Wall
+ ****
+ **** You may distribute under the terms of either the GNU General Public
+ **** License or the Artistic License, as specified in the README file.
+
+ *
+ * Beware that some of this code is subtly aware of the way operator
+ * precedence is structured in regular expressions. Serious changes in
+ * regular-expression syntax might require a total rethink.
+ */
+#include "EXTERN.h"
+#include "perl.h"
+#include "INTERN.h"
+#include "regcomp.h"
+
+#ifdef MSDOS
+# if defined(BUGGY_MSC6)
+ /* MSC 6.00A breaks on op/regexp.t test 85 unless we turn this off */
+ # pragma optimize("a",off)
+ /* But MSC 6.00A is happy with 'w', for aliases only across function calls*/
+ # pragma optimize("w",on )
+# endif /* BUGGY_MSC6 */
+#endif /* MSDOS */
+
+#ifndef STATIC
+#define STATIC static
+#endif
+
+#define ISMULT1(c) ((c) == '*' || (c) == '+' || (c) == '?')
+#define ISMULT2(s) ((*s) == '*' || (*s) == '+' || (*s) == '?' || \
+ ((*s) == '{' && regcurly(s)))
+#ifdef atarist
+#define PERL_META "^$.[()|?+*\\"
+#else
+#define META "^$.[()|?+*\\"
+#endif
+
+#ifdef SPSTART
+#undef SPSTART /* dratted cpp namespace... */
+#endif
+/*
+ * Flags to be passed up and down.
+ */
+#define HASWIDTH 01 /* Known never to match null string. */
+#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */
+#define SPSTART 04 /* Starts with * or +. */
+#define WORST 0 /* Worst case. */
+
+/*
+ * Global work variables for regcomp().
+ */
+static char *regprecomp; /* uncompiled string. */
+static char *regparse; /* Input-scan pointer. */
+static char *regxend; /* End of input for compile */
+static int regnpar; /* () count. */
+static char *regcode; /* Code-emit pointer; &regdummy = don't. */
+static long regsize; /* Code size. */
+static int regfold;
+static int regsawbracket; /* Did we do {d,d} trick? */
+static int regsawback; /* Did we see \1, ...? */
+
+/*
+ * Forward declarations for regcomp()'s friends.
+ */
+STATIC int regcurly();
+STATIC char *reg();
+STATIC char *regbranch();
+STATIC char *regpiece();
+STATIC char *regatom();
+STATIC char *regclass();
+STATIC char *regnode();
+STATIC char *reganode();
+STATIC void regc();
+STATIC void reginsert();
+STATIC void regtail();
+STATIC void regoptail();
+
+/*
+ - regcomp - compile a regular expression into internal code
+ *
+ * We can't allocate space until we know how big the compiled form will be,
+ * but we can't compile it (and thus know how big it is) until we've got a
+ * place to put the code. So we cheat: we compile it twice, once with code
+ * generation turned off and size counting turned on, and once "for real".
+ * This also means that we don't allocate space until we are sure that the
+ * thing really will compile successfully, and we never have to move the
+ * code and thus invalidate pointers into it. (Note that it has to be in
+ * one piece because free() must be able to free it all.) [NB: not true in perl]
+ *
+ * Beware that the optimization-preparation code in here knows about some
+ * of the structure of the compiled regexp. [I'll say.]
+ */
+regexp *
+regcomp(exp,xend,fold)
+char *exp;
+char *xend;
+int fold;
+{
+ register regexp *r;
+ register char *scan;
+ register STR *longish;
+ STR *longest;
+ register int len;
+ register char *first;
+ int flags;
+ int backish;
+ int backest;
+ int curback;
+ int minlen;
+ int sawplus = 0;
+ int sawopen = 0;
+
+ if (exp == NULL)
+ fatal("NULL regexp argument");
+
+ /* First pass: determine size, legality. */
+ regfold = fold;
+ regparse = exp;
+ regxend = xend;
+ regprecomp = nsavestr(exp,xend-exp);
+ regsawbracket = 0;
+ regsawback = 0;
+ regnpar = 1;
+ regsize = 0L;
+ regcode = &regdummy;
+ regc((char)MAGIC);
+ if (reg(0, &flags) == NULL) {
+ Safefree(regprecomp);
+ regprecomp = Nullch;
+ return(NULL);
+ }
+
+ /* Small enough for pointer-storage convention? */
+ if (regsize >= 32767L) /* Probably could be 65535L. */
+ FAIL("regexp too big");
+
+ /* Allocate space. */
+ Newc(1001, r, sizeof(regexp) + (unsigned)regsize, char, regexp);
+ if (r == NULL)
+ FAIL("regexp out of space");
+
+ /* Second pass: emit code. */
+ if (regsawbracket)
+ Copy(regprecomp,exp,xend-exp,char);
+ r->prelen = xend-exp;
+ r->precomp = regprecomp;
+ r->subbeg = r->subbase = NULL;
+ regparse = exp;
+ regnpar = 1;
+ regcode = r->program;
+ regc((char)MAGIC);
+ if (reg(0, &flags) == NULL)
+ return(NULL);
+
+ /* Dig out information for optimizations. */
+ r->regstart = Nullstr; /* Worst-case defaults. */
+ r->reganch = 0;
+ r->regmust = Nullstr;
+ r->regback = -1;
+ r->regstclass = Nullch;
+ scan = r->program+1; /* First BRANCH. */
+ if (OP(regnext(scan)) == END) {/* Only one top-level choice. */
+ scan = NEXTOPER(scan);
+
+ first = scan;
+ while ((OP(first) == OPEN && (sawopen = 1)) ||
+ (OP(first) == BRANCH && OP(regnext(first)) != BRANCH) ||
+ (OP(first) == PLUS) ||
+ (OP(first) == CURLY && ARG1(first) > 0) ) {
+ if (OP(first) == PLUS)
+ sawplus = 1;
+ else
+ first += regarglen[OP(first)];
+ first = NEXTOPER(first);
+ }
+
+ /* Starting-point info. */
+ again:
+ if (OP(first) == EXACTLY) {
+ r->regstart =
+ str_make(OPERAND(first)+1,*OPERAND(first));
+ if (r->regstart->str_cur > !(sawstudy|fold))
+ fbmcompile(r->regstart,fold);
+ }
+ else if ((exp = index(simple,OP(first))) && exp > simple)
+ r->regstclass = first;
+ else if (OP(first) == BOUND || OP(first) == NBOUND)
+ r->regstclass = first;
+ else if (OP(first) == BOL) {
+ r->reganch = ROPT_ANCH;
+ first = NEXTOPER(first);
+ goto again;
+ }
+ else if ((OP(first) == STAR && OP(NEXTOPER(first)) == ANY) &&
+ !(r->reganch & ROPT_ANCH) ) {
+ /* turn .* into ^.* with an implied $*=1 */
+ r->reganch = ROPT_ANCH | ROPT_IMPLICIT;
+ first = NEXTOPER(first);
+ goto again;
+ }
+ if (sawplus && (!sawopen || !regsawback))
+ r->reganch |= ROPT_SKIP; /* x+ must match 1st of run */
+
+#ifdef DEBUGGING
+ if (debug & 512)
+ fprintf(stderr,"first %d next %d offset %d\n",
+ OP(first), OP(NEXTOPER(first)), first - scan);
+#endif
+ /*
+ * If there's something expensive in the r.e., find the
+ * longest literal string that must appear and make it the
+ * regmust. Resolve ties in favor of later strings, since
+ * the regstart check works with the beginning of the r.e.
+ * and avoiding duplication strengthens checking. Not a
+ * strong reason, but sufficient in the absence of others.
+ * [Now we resolve ties in favor of the earlier string if
+ * it happens that curback has been invalidated, since the
+ * earlier string may buy us something the later one won't.]
+ */
+ longish = str_make("",0);
+ longest = str_make("",0);
+ len = 0;
+ minlen = 0;
+ curback = 0;
+ backish = 0;
+ backest = 0;
+ while (OP(scan) != END) {
+ if (OP(scan) == BRANCH) {
+ if (OP(regnext(scan)) == BRANCH) {
+ curback = -30000;
+ while (OP(scan) == BRANCH)
+ scan = regnext(scan);
+ }
+ else /* single branch is ok */
+ scan = NEXTOPER(scan);
+ }
+ if (OP(scan) == EXACTLY) {
+ char *t;
+
+ first = scan;
+ while (OP(t = regnext(scan)) == CLOSE)
+ scan = t;
+ minlen += *OPERAND(first);
+ if (curback - backish == len) {
+ str_ncat(longish, OPERAND(first)+1,
+ *OPERAND(first));
+ len += *OPERAND(first);
+ curback += *OPERAND(first);
+ first = regnext(scan);
+ }
+ else if (*OPERAND(first) >= len + (curback >= 0)) {
+ len = *OPERAND(first);
+ str_nset(longish, OPERAND(first)+1,len);
+ backish = curback;
+ curback += len;
+ first = regnext(scan);
+ }
+ else
+ curback += *OPERAND(first);
+ }
+ else if (index(varies,OP(scan))) {
+ curback = -30000;
+ len = 0;
+ if (longish->str_cur > longest->str_cur) {
+ str_sset(longest,longish);
+ backest = backish;
+ }
+ str_nset(longish,"",0);
+ if (OP(scan) == PLUS &&
+ index(simple,OP(NEXTOPER(scan))))
+ minlen++;
+ else if (OP(scan) == CURLY &&
+ index(simple,OP(NEXTOPER(scan)+4)))
+ minlen += ARG1(scan);
+ }
+ else if (index(simple,OP(scan))) {
+ curback++;
+ minlen++;
+ len = 0;
+ if (longish->str_cur > longest->str_cur) {
+ str_sset(longest,longish);
+ backest = backish;
+ }
+ str_nset(longish,"",0);
+ }
+ scan = regnext(scan);
+ }
+
+ /* Prefer earlier on tie, unless we can tail match latter */
+
+ if (longish->str_cur + (OP(first) == EOL) > longest->str_cur) {
+ str_sset(longest,longish);
+ backest = backish;
+ }
+ else
+ str_nset(longish,"",0);
+ if (longest->str_cur
+ &&
+ (!r->regstart
+ ||
+ !fbminstr((unsigned char*) r->regstart->str_ptr,
+ (unsigned char *) r->regstart->str_ptr
+ + r->regstart->str_cur,
+ longest)
+ )
+ )
+ {
+ r->regmust = longest;
+ if (backest < 0)
+ backest = -1;
+ r->regback = backest;
+ if (longest->str_cur
+ > !(sawstudy || fold || OP(first) == EOL) )
+ fbmcompile(r->regmust,fold);
+ r->regmust->str_u.str_useful = 100;
+ if (OP(first) == EOL && longish->str_cur)
+ r->regmust->str_pok |= SP_TAIL;
+ }
+ else {
+ str_free(longest);
+ longest = Nullstr;
+ }
+ str_free(longish);
+ }
+
+ r->do_folding = fold;
+ r->nparens = regnpar - 1;
+ r->minlen = minlen;
+ Newz(1002, r->startp, regnpar, char*);
+ Newz(1002, r->endp, regnpar, char*);
+#ifdef DEBUGGING
+ if (debug & 512)
+ regdump(r);
+#endif
+ return(r);
+}
+
+/*
+ - reg - regular expression, i.e. main body or parenthesized thing
+ *
+ * Caller must absorb opening parenthesis.
+ *
+ * Combining parenthesis handling with the base level of regular expression
+ * is a trifle forced, but the need to tie the tails of the branches to what
+ * follows makes it hard to avoid.
+ */
+static char *
+reg(paren, flagp)
+int paren; /* Parenthesized? */
+int *flagp;
+{
+ register char *ret;
+ register char *br;
+ register char *ender;
+ register int parno;
+ int flags;
+
+ *flagp = HASWIDTH; /* Tentatively. */
+
+ /* Make an OPEN node, if parenthesized. */
+ if (paren) {
+ parno = regnpar;
+ regnpar++;
+ ret = reganode(OPEN, parno);
+ } else
+ ret = NULL;
+
+ /* Pick up the branches, linking them together. */
+ br = regbranch(&flags);
+ if (br == NULL)
+ return(NULL);
+ if (ret != NULL)
+ regtail(ret, br); /* OPEN -> first. */
+ else
+ ret = br;
+ if (!(flags&HASWIDTH))
+ *flagp &= ~HASWIDTH;
+ *flagp |= flags&SPSTART;
+ while (*regparse == '|') {
+ regparse++;
+ br = regbranch(&flags);
+ if (br == NULL)
+ return(NULL);
+ regtail(ret, br); /* BRANCH -> BRANCH. */
+ if (!(flags&HASWIDTH))
+ *flagp &= ~HASWIDTH;
+ *flagp |= flags&SPSTART;
+ }
+
+ /* Make a closing node, and hook it on the end. */
+ if (paren)
+ ender = reganode(CLOSE, parno);
+ else
+ ender = regnode(END);
+ regtail(ret, ender);
+
+ /* Hook the tails of the branches to the closing node. */
+ for (br = ret; br != NULL; br = regnext(br))
+ regoptail(br, ender);
+
+ /* Check for proper termination. */
+ if (paren && *regparse++ != ')') {
+ FAIL("unmatched () in regexp");
+ } else if (!paren && regparse < regxend) {
+ if (*regparse == ')') {
+ FAIL("unmatched () in regexp");
+ } else
+ FAIL("junk on end of regexp"); /* "Can't happen". */
+ /* NOTREACHED */
+ }
+
+ return(ret);
+}
+
+/*
+ - regbranch - one alternative of an | operator
+ *
+ * Implements the concatenation operator.
+ */
+static char *
+regbranch(flagp)
+int *flagp;
+{
+ register char *ret;
+ register char *chain;
+ register char *latest;
+ int flags;
+
+ *flagp = WORST; /* Tentatively. */
+
+ ret = regnode(BRANCH);
+ chain = NULL;
+ while (regparse < regxend && *regparse != '|' && *regparse != ')') {
+ latest = regpiece(&flags);
+ if (latest == NULL)
+ return(NULL);
+ *flagp |= flags&HASWIDTH;
+ if (chain == NULL) /* First piece. */
+ *flagp |= flags&SPSTART;
+ else
+ regtail(chain, latest);
+ chain = latest;
+ }
+ if (chain == NULL) /* Loop ran zero times. */
+ (void) regnode(NOTHING);
+
+ return(ret);
+}
+
+/*
+ - regpiece - something followed by possible [*+?]
+ *
+ * Note that the branching code sequences used for ? and the general cases
+ * of * and + are somewhat optimized: they use the same NOTHING node as
+ * both the endmarker for their branch list and the body of the last branch.
+ * It might seem that this node could be dispensed with entirely, but the
+ * endmarker role is not redundant.
+ */
+static char *
+regpiece(flagp)
+int *flagp;
+{
+ register char *ret;
+ register char op;
+ register char *next;
+ int flags;
+ char *origparse = regparse;
+ int orignpar = regnpar;
+ char *max;
+ int iter;
+ char ch;
+
+ ret = regatom(&flags);
+ if (ret == NULL)
+ return(NULL);
+
+ op = *regparse;
+
+ /* Here's a total kludge: if after the atom there's a {\d+,?\d*}
+ * then we decrement the first number by one and reset our
+ * parsing back to the beginning of the same atom. If the first number
+ * is down to 0, decrement the second number instead and fake up
+ * a ? after it. Given the way this compiler doesn't keep track
+ * of offsets on the first pass, this is the only way to replicate
+ * a piece of code. Sigh.
+ */
+ if (op == '{' && regcurly(regparse)) {
+ next = regparse + 1;
+ max = Nullch;
+ while (isDIGIT(*next) || *next == ',') {
+ if (*next == ',') {
+ if (max)
+ break;
+ else
+ max = next;
+ }
+ next++;
+ }
+ if (*next == '}') { /* got one */
+ if (!max)
+ max = next;
+ regparse++;
+ iter = atoi(regparse);
+ if (flags&SIMPLE) { /* we can do it right after all */
+ int tmp;
+
+ reginsert(CURLY, ret);
+ if (iter > 0)
+ *flagp = (WORST|HASWIDTH);
+ if (*max == ',')
+ max++;
+ else
+ max = regparse;
+ tmp = atoi(max);
+ if (!tmp && *max != '0')
+ tmp = 32767; /* meaning "infinity" */
+ if (tmp && tmp < iter)
+ fatal("Can't do {n,m} with n > m");
+ if (regcode != &regdummy) {
+#ifdef REGALIGN
+ *(unsigned short *)(ret+3) = iter;
+ *(unsigned short *)(ret+5) = tmp;
+#else
+ ret[3] = iter >> 8; ret[4] = iter & 0377;
+ ret[5] = tmp >> 8; ret[6] = tmp & 0377;
+#endif
+ }
+ regparse = next;
+ goto nest_check;
+ }
+ regsawbracket++; /* remember we clobbered exp */
+ if (iter > 0) {
+ ch = *max;
+ sprintf(regparse,"%.*d", max-regparse, iter - 1);
+ *max = ch;
+ if (*max == ',' && max[1] != '}') {
+ if (atoi(max+1) <= 0)
+ fatal("Can't do {n,m} with n > m");
+ ch = *next;
+ sprintf(max+1,"%.*d", next-(max+1), atoi(max+1) - 1);
+ *next = ch;
+ }
+ if (iter != 1 || *max == ',') {
+ regparse = origparse; /* back up input pointer */
+ regnpar = orignpar; /* don't make more parens */
+ }
+ else {
+ regparse = next;
+ goto nest_check;
+ }
+ *flagp = flags;
+ return ret;
+ }
+ if (*max == ',') {
+ max++;
+ iter = atoi(max);
+ if (max == next) { /* any number more? */
+ regparse = next;
+ op = '*'; /* fake up one with a star */
+ }
+ else if (iter > 0) {
+ op = '?'; /* fake up optional atom */
+ ch = *next;
+ sprintf(max,"%.*d", next-max, iter - 1);
+ *next = ch;
+ if (iter == 1)
+ regparse = next;
+ else {
+ regparse = origparse - 1; /* offset ++ below */
+ regnpar = orignpar;
+ }
+ }
+ else
+ fatal("Can't do {n,0}");
+ }
+ else
+ fatal("Can't do {0}");
+ }
+ }
+
+ if (!ISMULT1(op)) {
+ *flagp = flags;
+ return(ret);
+ }
+
+ if (!(flags&HASWIDTH) && op != '?')
+ FAIL("regexp *+ operand could be empty");
+ *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
+
+ if (op == '*' && (flags&SIMPLE))
+ reginsert(STAR, ret);
+ else if (op == '*') {
+ /* Emit x* as (x&|), where & means "self". */
+ reginsert(BRANCH, ret); /* Either x */
+ regoptail(ret, regnode(BACK)); /* and loop */
+ regoptail(ret, ret); /* back */
+ regtail(ret, regnode(BRANCH)); /* or */
+ regtail(ret, regnode(NOTHING)); /* null. */
+ } else if (op == '+' && (flags&SIMPLE))
+ reginsert(PLUS, ret);
+ else if (op == '+') {
+ /* Emit x+ as x(&|), where & means "self". */
+ next = regnode(BRANCH); /* Either */
+ regtail(ret, next);
+ regtail(regnode(BACK), ret); /* loop back */
+ regtail(next, regnode(BRANCH)); /* or */
+ regtail(ret, regnode(NOTHING)); /* null. */
+ } else if (op == '?') {
+ /* Emit x? as (x|) */
+ reginsert(BRANCH, ret); /* Either x */
+ regtail(ret, regnode(BRANCH)); /* or */
+ next = regnode(NOTHING); /* null. */
+ regtail(ret, next);
+ regoptail(ret, next);
+ }
+ nest_check:
+ regparse++;
+ if (ISMULT2(regparse))
+ FAIL("nested *?+ in regexp");
+
+ return(ret);
+}
+
+/*
+ - regatom - the lowest level
+ *
+ * Optimization: gobbles an entire sequence of ordinary characters so that
+ * it can turn them into a single node, which is smaller to store and
+ * faster to run. Backslashed characters are exceptions, each becoming a
+ * separate node; the code is simpler that way and it's not worth fixing.
+ *
+ * [Yes, it is worth fixing, some scripts can run twice the speed.]
+ */
+static char *
+regatom(flagp)
+int *flagp;
+{
+ register char *ret;
+ int flags;
+
+ *flagp = WORST; /* Tentatively. */
+
+ switch (*regparse++) {
+ case '^':
+ ret = regnode(BOL);
+ break;
+ case '$':
+ ret = regnode(EOL);
+ break;
+ case '.':
+ ret = regnode(ANY);
+ *flagp |= HASWIDTH|SIMPLE;
+ break;
+ case '[':
+ ret = regclass();
+ *flagp |= HASWIDTH|SIMPLE;
+ break;
+ case '(':
+ ret = reg(1, &flags);
+ if (ret == NULL)
+ return(NULL);
+ *flagp |= flags&(HASWIDTH|SPSTART);
+ break;
+ case '|':
+ case ')':
+ FAIL("internal urp in regexp"); /* Supposed to be caught earlier. */
+ break;
+ case '?':
+ case '+':
+ case '*':
+ FAIL("?+* follows nothing in regexp");
+ break;
+ case '\\':
+ switch (*regparse) {
+ case 'w':
+ ret = regnode(ALNUM);
+ *flagp |= HASWIDTH|SIMPLE;
+ regparse++;
+ break;
+ case 'W':
+ ret = regnode(NALNUM);
+ *flagp |= HASWIDTH|SIMPLE;
+ regparse++;
+ break;
+ case 'b':
+ ret = regnode(BOUND);
+ *flagp |= SIMPLE;
+ regparse++;
+ break;
+ case 'B':
+ ret = regnode(NBOUND);
+ *flagp |= SIMPLE;
+ regparse++;
+ break;
+ case 's':
+ ret = regnode(SPACE);
+ *flagp |= HASWIDTH|SIMPLE;
+ regparse++;
+ break;
+ case 'S':
+ ret = regnode(NSPACE);
+ *flagp |= HASWIDTH|SIMPLE;
+ regparse++;
+ break;
+ case 'd':
+ ret = regnode(DIGIT);
+ *flagp |= HASWIDTH|SIMPLE;
+ regparse++;
+ break;
+ case 'D':
+ ret = regnode(NDIGIT);
+ *flagp |= HASWIDTH|SIMPLE;
+ regparse++;
+ break;
+ case 'n':
+ case 'r':
+ case 't':
+ case 'f':
+ case 'e':
+ case 'a':
+ case 'x':
+ case 'c':
+ case '0':
+ goto defchar;
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ int num = atoi(regparse);
+
+ if (num > 9 && num >= regnpar)
+ goto defchar;
+ else {
+ regsawback = 1;
+ ret = reganode(REF, num);
+ while (isDIGIT(*regparse))
+ regparse++;
+ *flagp |= SIMPLE;
+ }
+ }
+ break;
+ case '\0':
+ if (regparse >= regxend)
+ FAIL("trailing \\ in regexp");
+ /* FALL THROUGH */
+ default:
+ goto defchar;
+ }
+ break;
+ default: {
+ register int len;
+ register char ender;
+ register char *p;
+ char *oldp;
+ int numlen;
+
+ defchar:
+ ret = regnode(EXACTLY);
+ regc(0); /* save spot for len */
+ for (len=0, p=regparse-1;
+ len < 127 && p < regxend;
+ len++)
+ {
+ oldp = p;
+ switch (*p) {
+ case '^':
+ case '$':
+ case '.':
+ case '[':
+ case '(':
+ case ')':
+ case '|':
+ goto loopdone;
+ case '\\':
+ switch (*++p) {
+ case 'w':
+ case 'W':
+ case 'b':
+ case 'B':
+ case 's':
+ case 'S':
+ case 'd':
+ case 'D':
+ --p;
+ goto loopdone;
+ case 'n':
+ ender = '\n';
+ p++;
+ break;
+ case 'r':
+ ender = '\r';
+ p++;
+ break;
+ case 't':
+ ender = '\t';
+ p++;
+ break;
+ case 'f':
+ ender = '\f';
+ p++;
+ break;
+ case 'e':
+ ender = '\033';
+ p++;
+ break;
+ case 'a':
+ ender = '\007';
+ p++;
+ break;
+ case 'x':
+ ender = scanhex(++p, 2, &numlen);
+ p += numlen;
+ break;
+ case 'c':
+ p++;
+ ender = *p++;
+ if (isLOWER(ender))
+ ender = toupper(ender);
+ ender ^= 64;
+ break;
+ case '0': case '1': case '2': case '3':case '4':
+ case '5': case '6': case '7': case '8':case '9':
+ if (*p == '0' ||
+ (isDIGIT(p[1]) && atoi(p) >= regnpar) ) {
+ ender = scanoct(p, 3, &numlen);
+ p += numlen;
+ }
+ else {
+ --p;
+ goto loopdone;
+ }
+ break;
+ case '\0':
+ if (p >= regxend)
+ FAIL("trailing \\ in regexp");
+ /* FALL THROUGH */
+ default:
+ ender = *p++;
+ break;
+ }
+ break;
+ default:
+ ender = *p++;
+ break;
+ }
+ if (regfold && isUPPER(ender))
+ ender = tolower(ender);
+ if (ISMULT2(p)) { /* Back off on ?+*. */
+ if (len)
+ p = oldp;
+ else {
+ len++;
+ regc(ender);
+ }
+ break;
+ }
+ regc(ender);
+ }
+ loopdone:
+ regparse = p;
+ if (len <= 0)
+ FAIL("internal disaster in regexp");
+ *flagp |= HASWIDTH;
+ if (len == 1)
+ *flagp |= SIMPLE;
+ if (regcode != &regdummy)
+ *OPERAND(ret) = len;
+ regc('\0');
+ }
+ break;
+ }
+
+ return(ret);
+}
+
+static void
+regset(bits,def,c)
+char *bits;
+int def;
+register int c;
+{
+ if (regcode == &regdummy)
+ return;
+ c &= 255;
+ if (def)
+ bits[c >> 3] &= ~(1 << (c & 7));
+ else
+ bits[c >> 3] |= (1 << (c & 7));
+}
+
+static char *
+regclass()
+{
+ register char *bits;
+ register int class;
+ register int lastclass;
+ register int range = 0;
+ register char *ret;
+ register int def;
+ int numlen;
+
+ ret = regnode(ANYOF);
+ if (*regparse == '^') { /* Complement of range. */
+ regparse++;
+ def = 0;
+ } else {
+ def = 255;
+ }
+ bits = regcode;
+ for (class = 0; class < 32; class++)
+ regc(def);
+ if (*regparse == ']' || *regparse == '-')
+ goto skipcond; /* allow 1st char to be ] or - */
+ while (regparse < regxend && *regparse != ']') {
+ skipcond:
+ class = UCHARAT(regparse++);
+ if (class == '\\') {
+ class = UCHARAT(regparse++);
+ switch (class) {
+ case 'w':
+ for (class = 0; class < 256; class++)
+ if (isALNUM(class))
+ regset(bits,def,class);
+ lastclass = 1234;
+ continue;
+ case 'W':
+ for (class = 0; class < 256; class++)
+ if (!isALNUM(class))
+ regset(bits,def,class);
+ lastclass = 1234;
+ continue;
+ case 's':
+ for (class = 0; class < 256; class++)
+ if (isSPACE(class))
+ regset(bits,def,class);
+ lastclass = 1234;
+ continue;
+ case 'S':
+ for (class = 0; class < 256; class++)
+ if (!isSPACE(class))
+ regset(bits,def,class);
+ lastclass = 1234;
+ continue;
+ case 'd':
+ for (class = '0'; class <= '9'; class++)
+ regset(bits,def,class);
+ lastclass = 1234;
+ continue;
+ case 'D':
+ for (class = 0; class < '0'; class++)
+ regset(bits,def,class);
+ for (class = '9' + 1; class < 256; class++)
+ regset(bits,def,class);
+ lastclass = 1234;
+ continue;
+ case 'n':
+ class = '\n';
+ break;
+ case 'r':
+ class = '\r';
+ break;
+ case 't':
+ class = '\t';
+ break;
+ case 'f':
+ class = '\f';
+ break;
+ case 'b':
+ class = '\b';
+ break;
+ case 'e':
+ class = '\033';
+ break;
+ case 'a':
+ class = '\007';
+ break;
+ case 'x':
+ class = scanhex(regparse, 2, &numlen);
+ regparse += numlen;
+ break;
+ case 'c':
+ class = *regparse++;
+ if (isLOWER(class))
+ class = toupper(class);
+ class ^= 64;
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ class = scanoct(--regparse, 3, &numlen);
+ regparse += numlen;
+ break;
+ }
+ }
+ if (range) {
+ if (lastclass > class)
+ FAIL("invalid [] range in regexp");
+ range = 0;
+ }
+ else {
+ lastclass = class;
+ if (*regparse == '-' && regparse+1 < regxend &&
+ regparse[1] != ']') {
+ regparse++;
+ range = 1;
+ continue; /* do it next time */
+ }
+ }
+ for ( ; lastclass <= class; lastclass++) {
+ regset(bits,def,lastclass);
+ if (regfold && isUPPER(lastclass))
+ regset(bits,def,tolower(lastclass));
+ }
+ lastclass = class;
+ }
+ if (*regparse != ']')
+ FAIL("unmatched [] in regexp");
+ regparse++;
+ return ret;
+}
+
+/*
+ - regnode - emit a node
+ */
+static char * /* Location. */
+regnode(op)
+char op;
+{
+ register char *ret;
+ register char *ptr;
+
+ ret = regcode;
+ if (ret == &regdummy) {
+#ifdef REGALIGN
+ if (!(regsize & 1))
+ regsize++;
+#endif
+ regsize += 3;
+ return(ret);
+ }
+
+#ifdef REGALIGN
+#ifndef lint
+ if (!((long)ret & 1))
+ *ret++ = 127;
+#endif
+#endif
+ ptr = ret;
+ *ptr++ = op;
+ *ptr++ = '\0'; /* Null "next" pointer. */
+ *ptr++ = '\0';
+ regcode = ptr;
+
+ return(ret);
+}
+
+/*
+ - reganode - emit a node with an argument
+ */
+static char * /* Location. */
+reganode(op, arg)
+char op;
+unsigned short arg;
+{
+ register char *ret;
+ register char *ptr;
+
+ ret = regcode;
+ if (ret == &regdummy) {
+#ifdef REGALIGN
+ if (!(regsize & 1))
+ regsize++;
+#endif
+ regsize += 5;
+ return(ret);
+ }
+
+#ifdef REGALIGN
+#ifndef lint
+ if (!((long)ret & 1))
+ *ret++ = 127;
+#endif
+#endif
+ ptr = ret;
+ *ptr++ = op;
+ *ptr++ = '\0'; /* Null "next" pointer. */
+ *ptr++ = '\0';
+#ifdef REGALIGN
+ *(unsigned short *)(ret+3) = arg;
+#else
+ ret[3] = arg >> 8; ret[4] = arg & 0377;
+#endif
+ ptr += 2;
+ regcode = ptr;
+
+ return(ret);
+}
+
+/*
+ - regc - emit (if appropriate) a byte of code
+ */
+static void
+regc(b)
+char b;
+{
+ if (regcode != &regdummy)
+ *regcode++ = b;
+ else
+ regsize++;
+}
+
+/*
+ - reginsert - insert an operator in front of already-emitted operand
+ *
+ * Means relocating the operand.
+ */
+static void
+reginsert(op, opnd)
+char op;
+char *opnd;
+{
+ register char *src;
+ register char *dst;
+ register char *place;
+ register offset = (op == CURLY ? 4 : 0);
+
+ if (regcode == &regdummy) {
+#ifdef REGALIGN
+ regsize += 4 + offset;
+#else
+ regsize += 3 + offset;
+#endif
+ return;
+ }
+
+ src = regcode;
+#ifdef REGALIGN
+ regcode += 4 + offset;
+#else
+ regcode += 3 + offset;
+#endif
+ dst = regcode;
+ while (src > opnd)
+ *--dst = *--src;
+
+ place = opnd; /* Op node, where operand used to be. */
+ *place++ = op;
+ *place++ = '\0';
+ *place++ = '\0';
+ while (offset-- > 0)
+ *place++ = '\0';
+#ifdef REGALIGN
+ *place++ = '\177';
+#endif
+}
+
+/*
+ - regtail - set the next-pointer at the end of a node chain
+ */
+static void
+regtail(p, val)
+char *p;
+char *val;
+{
+ register char *scan;
+ register char *temp;
+ register int offset;
+
+ if (p == &regdummy)
+ return;
+
+ /* Find last node. */
+ scan = p;
+ for (;;) {
+ temp = regnext(scan);
+ if (temp == NULL)
+ break;
+ scan = temp;
+ }
+
+#ifdef REGALIGN
+ offset = val - scan;
+#ifndef lint
+ *(short*)(scan+1) = offset;
+#else
+ offset = offset;
+#endif
+#else
+ if (OP(scan) == BACK)
+ offset = scan - val;
+ else
+ offset = val - scan;
+ *(scan+1) = (offset>>8)&0377;
+ *(scan+2) = offset&0377;
+#endif
+}
+
+/*
+ - regoptail - regtail on operand of first argument; nop if operandless
+ */
+static void
+regoptail(p, val)
+char *p;
+char *val;
+{
+ /* "Operandless" and "op != BRANCH" are synonymous in practice. */
+ if (p == NULL || p == &regdummy || OP(p) != BRANCH)
+ return;
+ regtail(NEXTOPER(p), val);
+}
+
+/*
+ - regcurly - a little FSA that accepts {\d+,?\d*}
+ */
+STATIC int
+regcurly(s)
+register char *s;
+{
+ if (*s++ != '{')
+ return FALSE;
+ if (!isDIGIT(*s))
+ return FALSE;
+ while (isDIGIT(*s))
+ s++;
+ if (*s == ',')
+ s++;
+ while (isDIGIT(*s))
+ s++;
+ if (*s != '}')
+ return FALSE;
+ return TRUE;
+}
+
+#ifdef DEBUGGING
+
+/*
+ - regdump - dump a regexp onto stderr in vaguely comprehensible form
+ */
+void
+regdump(r)
+regexp *r;
+{
+ register char *s;
+ register char op = EXACTLY; /* Arbitrary non-END op. */
+ register char *next;
+
+
+ s = r->program + 1;
+ while (op != END) { /* While that wasn't END last time... */
+#ifdef REGALIGN
+ if (!((long)s & 1))
+ s++;
+#endif
+ op = OP(s);
+ fprintf(stderr,"%2d%s", s-r->program, regprop(s)); /* Where, what. */
+ next = regnext(s);
+ s += regarglen[op];
+ if (next == NULL) /* Next ptr. */
+ fprintf(stderr,"(0)");
+ else
+ fprintf(stderr,"(%d)", (s-r->program)+(next-s));
+ s += 3;
+ if (op == ANYOF) {
+ s += 32;
+ }
+ if (op == EXACTLY) {
+ /* Literal string, where present. */
+ s++;
+ while (*s != '\0') {
+ (void)putchar(*s);
+ s++;
+ }
+ s++;
+ }
+ (void)putchar('\n');
+ }
+
+ /* Header fields of interest. */
+ if (r->regstart)
+ fprintf(stderr,"start `%s' ", r->regstart->str_ptr);
+ if (r->regstclass)
+ fprintf(stderr,"stclass `%s' ", regprop(r->regstclass));
+ if (r->reganch & ROPT_ANCH)
+ fprintf(stderr,"anchored ");
+ if (r->reganch & ROPT_SKIP)
+ fprintf(stderr,"plus ");
+ if (r->reganch & ROPT_IMPLICIT)
+ fprintf(stderr,"implicit ");
+ if (r->regmust != NULL)
+ fprintf(stderr,"must have \"%s\" back %d ", r->regmust->str_ptr,
+ r->regback);
+ fprintf(stderr, "minlen %d ", r->minlen);
+ fprintf(stderr,"\n");
+}
+
+/*
+ - regprop - printable representation of opcode
+ */
+char *
+regprop(op)
+char *op;
+{
+ register char *p;
+
+ (void) strcpy(buf, ":");
+
+ switch (OP(op)) {
+ case BOL:
+ p = "BOL";
+ break;
+ case EOL:
+ p = "EOL";
+ break;
+ case ANY:
+ p = "ANY";
+ break;
+ case ANYOF:
+ p = "ANYOF";
+ break;
+ case BRANCH:
+ p = "BRANCH";
+ break;
+ case EXACTLY:
+ p = "EXACTLY";
+ break;
+ case NOTHING:
+ p = "NOTHING";
+ break;
+ case BACK:
+ p = "BACK";
+ break;
+ case END:
+ p = "END";
+ break;
+ case ALNUM:
+ p = "ALNUM";
+ break;
+ case NALNUM:
+ p = "NALNUM";
+ break;
+ case BOUND:
+ p = "BOUND";
+ break;
+ case NBOUND:
+ p = "NBOUND";
+ break;
+ case SPACE:
+ p = "SPACE";
+ break;
+ case NSPACE:
+ p = "NSPACE";
+ break;
+ case DIGIT:
+ p = "DIGIT";
+ break;
+ case NDIGIT:
+ p = "NDIGIT";
+ break;
+ case CURLY:
+ (void)sprintf(buf+strlen(buf), "CURLY {%d,%d}",
+ ARG1(op),ARG2(op));
+ p = NULL;
+ break;
+ case REF:
+ (void)sprintf(buf+strlen(buf), "REF%d", ARG1(op));
+ p = NULL;
+ break;
+ case OPEN:
+ (void)sprintf(buf+strlen(buf), "OPEN%d", ARG1(op));
+ p = NULL;
+ break;
+ case CLOSE:
+ (void)sprintf(buf+strlen(buf), "CLOSE%d", ARG1(op));
+ p = NULL;
+ break;
+ case STAR:
+ p = "STAR";
+ break;
+ case PLUS:
+ p = "PLUS";
+ break;
+ default:
+ FAIL("corrupted regexp opcode");
+ }
+ if (p != NULL)
+ (void) strcat(buf, p);
+ return(buf);
+}
+#endif /* DEBUGGING */
+
+void
+regfree(r)
+struct regexp *r;
+{
+ if (r->precomp) {
+ Safefree(r->precomp);
+ r->precomp = Nullch;
+ }
+ if (r->subbase) {
+ Safefree(r->subbase);
+ r->subbase = Nullch;
+ }
+ if (r->regmust) {
+ str_free(r->regmust);
+ r->regmust = Nullstr;
+ }
+ if (r->regstart) {
+ str_free(r->regstart);
+ r->regstart = Nullstr;
+ }
+ Safefree(r->startp);
+ Safefree(r->endp);
+ Safefree(r);
+}
diff --git a/gnu/usr.bin/perl/perl/regcomp.h b/gnu/usr.bin/perl/perl/regcomp.h
new file mode 100644
index 0000000..6dcd482
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/regcomp.h
@@ -0,0 +1,200 @@
+/* $RCSfile: regcomp.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:39 $
+ *
+ * $Log: regcomp.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:39 nate
+ * PERL!
+ *
+ * Revision 4.0.1.1 91/06/07 11:49:40 lwall
+ * patch4: no change
+ *
+ * Revision 4.0 91/03/20 01:39:09 lwall
+ * 4.0 baseline.
+ *
+ */
+
+/*
+ * The "internal use only" fields in regexp.h are present to pass info from
+ * compile to execute that permits the execute phase to run lots faster on
+ * simple cases. They are:
+ *
+ * regstart str that must begin a match; Nullch if none obvious
+ * reganch is the match anchored (at beginning-of-line only)?
+ * regmust string (pointer into program) that match must include, or NULL
+ * [regmust changed to STR* for bminstr()--law]
+ * regmlen length of regmust string
+ * [regmlen not used currently]
+ *
+ * Regstart and reganch permit very fast decisions on suitable starting points
+ * for a match, cutting down the work a lot. Regmust permits fast rejection
+ * of lines that cannot possibly match. The regmust tests are costly enough
+ * that regcomp() supplies a regmust only if the r.e. contains something
+ * potentially expensive (at present, the only such thing detected is * or +
+ * at the start of the r.e., which can involve a lot of backup). Regmlen is
+ * supplied because the test in regexec() needs it and regcomp() is computing
+ * it anyway.
+ * [regmust is now supplied always. The tests that use regmust have a
+ * heuristic that disables the test if it usually matches.]
+ *
+ * [In fact, we now use regmust in many cases to locate where the search
+ * starts in the string, so if regback is >= 0, the regmust search is never
+ * wasted effort. The regback variable says how many characters back from
+ * where regmust matched is the earliest possible start of the match.
+ * For instance, /[a-z].foo/ has a regmust of 'foo' and a regback of 2.]
+ */
+
+/*
+ * Structure for regexp "program". This is essentially a linear encoding
+ * of a nondeterministic finite-state machine (aka syntax charts or
+ * "railroad normal form" in parsing technology). Each node is an opcode
+ * plus a "next" pointer, possibly plus an operand. "Next" pointers of
+ * all nodes except BRANCH implement concatenation; a "next" pointer with
+ * a BRANCH on both ends of it is connecting two alternatives. (Here we
+ * have one of the subtle syntax dependencies: an individual BRANCH (as
+ * opposed to a collection of them) is never concatenated with anything
+ * because of operator precedence.) The operand of some types of node is
+ * a literal string; for others, it is a node leading into a sub-FSM. In
+ * particular, the operand of a BRANCH node is the first node of the branch.
+ * (NB this is *not* a tree structure: the tail of the branch connects
+ * to the thing following the set of BRANCHes.) The opcodes are:
+ */
+
+/* definition number opnd? meaning */
+#define END 0 /* no End of program. */
+#define BOL 1 /* no Match "" at beginning of line. */
+#define EOL 2 /* no Match "" at end of line. */
+#define ANY 3 /* no Match any one character. */
+#define ANYOF 4 /* str Match character in (or not in) this class. */
+#define CURLY 5 /* str Match this simple thing {n,m} times. */
+#define BRANCH 6 /* node Match this alternative, or the next... */
+#define BACK 7 /* no Match "", "next" ptr points backward. */
+#define EXACTLY 8 /* str Match this string (preceded by length). */
+#define NOTHING 9 /* no Match empty string. */
+#define STAR 10 /* node Match this (simple) thing 0 or more times. */
+#define PLUS 11 /* node Match this (simple) thing 1 or more times. */
+#define ALNUM 12 /* no Match any alphanumeric character */
+#define NALNUM 13 /* no Match any non-alphanumeric character */
+#define BOUND 14 /* no Match "" at any word boundary */
+#define NBOUND 15 /* no Match "" at any word non-boundary */
+#define SPACE 16 /* no Match any whitespace character */
+#define NSPACE 17 /* no Match any non-whitespace character */
+#define DIGIT 18 /* no Match any numeric character */
+#define NDIGIT 19 /* no Match any non-numeric character */
+#define REF 20 /* num Match some already matched string */
+#define OPEN 21 /* num Mark this point in input as start of #n. */
+#define CLOSE 22 /* num Analogous to OPEN. */
+
+/*
+ * Opcode notes:
+ *
+ * BRANCH The set of branches constituting a single choice are hooked
+ * together with their "next" pointers, since precedence prevents
+ * anything being concatenated to any individual branch. The
+ * "next" pointer of the last BRANCH in a choice points to the
+ * thing following the whole choice. This is also where the
+ * final "next" pointer of each individual branch points; each
+ * branch starts with the operand node of a BRANCH node.
+ *
+ * BACK Normal "next" pointers all implicitly point forward; BACK
+ * exists to make loop structures possible.
+ *
+ * STAR,PLUS '?', and complex '*' and '+', are implemented as circular
+ * BRANCH structures using BACK. Simple cases (one character
+ * per match) are implemented with STAR and PLUS for speed
+ * and to minimize recursive plunges.
+ *
+ * OPEN,CLOSE ...are numbered at compile time.
+ */
+
+#ifndef DOINIT
+extern char regarglen[];
+#else
+char regarglen[] = {0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2};
+#endif
+
+/* The following have no fixed length. */
+#ifndef DOINIT
+extern char varies[];
+#else
+char varies[] = {BRANCH,BACK,STAR,PLUS,CURLY,REF,0};
+#endif
+
+/* The following always have a length of 1. */
+#ifndef DOINIT
+extern char simple[];
+#else
+char simple[] = {ANY,ANYOF,ALNUM,NALNUM,SPACE,NSPACE,DIGIT,NDIGIT,0};
+#endif
+
+EXT char regdummy;
+
+/*
+ * A node is one char of opcode followed by two chars of "next" pointer.
+ * "Next" pointers are stored as two 8-bit pieces, high order first. The
+ * value is a positive offset from the opcode of the node containing it.
+ * An operand, if any, simply follows the node. (Note that much of the
+ * code generation knows about this implicit relationship.)
+ *
+ * Using two bytes for the "next" pointer is vast overkill for most things,
+ * but allows patterns to get big without disasters.
+ *
+ * [If REGALIGN is defined, the "next" pointer is always aligned on an even
+ * boundary, and reads the offset directly as a short. Also, there is no
+ * special test to reverse the sign of BACK pointers since the offset is
+ * stored negative.]
+ */
+
+#ifndef gould
+#ifndef cray
+#ifndef eta10
+#define REGALIGN
+#endif
+#endif
+#endif
+
+#define OP(p) (*(p))
+
+#ifndef lint
+#ifdef REGALIGN
+#define NEXT(p) (*(short*)(p+1))
+#define ARG1(p) (*(unsigned short*)(p+3))
+#define ARG2(p) (*(unsigned short*)(p+5))
+#else
+#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
+#define ARG1(p) (((*((p)+3)&0377)<<8) + (*((p)+4)&0377))
+#define ARG2(p) (((*((p)+5)&0377)<<8) + (*((p)+6)&0377))
+#endif
+#else /* lint */
+#define NEXT(p) 0
+#endif /* lint */
+
+#define OPERAND(p) ((p) + 3)
+
+#ifdef REGALIGN
+#define NEXTOPER(p) ((p) + 4)
+#else
+#define NEXTOPER(p) ((p) + 3)
+#endif
+
+#define MAGIC 0234
+
+/*
+ * Utility definitions.
+ */
+#ifndef lint
+#ifndef CHARBITS
+#define UCHARAT(p) ((int)*(unsigned char *)(p))
+#else
+#define UCHARAT(p) ((int)*(p)&CHARBITS)
+#endif
+#else /* lint */
+#define UCHARAT(p) regdummy
+#endif /* lint */
+
+#define FAIL(m) fatal("/%s/: %s",regprecomp,m)
+
+char *regnext();
+#ifdef DEBUGGING
+void regdump();
+char *regprop();
+#endif
+
diff --git a/gnu/usr.bin/perl/perl/regexec.c b/gnu/usr.bin/perl/perl/regexec.c
new file mode 100644
index 0000000..7802465f
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/regexec.c
@@ -0,0 +1,910 @@
+/* NOTE: this is derived from Henry Spencer's regexp code, and should not
+ * confused with the original package (see point 3 below). Thanks, Henry!
+ */
+
+/* Additional note: this code is very heavily munged from Henry's version
+ * in places. In some spots I've traded clarity for efficiency, so don't
+ * blame Henry for some of the lack of readability.
+ */
+
+/* $RCSfile: regexec.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:39 $
+ *
+ * $Log: regexec.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:39 nate
+ * PERL!
+ *
+ * Revision 4.0.1.4 92/06/08 15:25:50 lwall
+ * patch20: pattern modifiers i and g didn't interact right
+ * patch20: in some cases $` and $' didn't get set by match
+ * patch20: /x{0}/ was wrongly interpreted as /x{0,}/
+ *
+ * Revision 4.0.1.3 91/11/05 18:23:55 lwall
+ * patch11: prepared for ctype implementations that don't define isascii()
+ * patch11: initial .* in pattern had dependency on value of $*
+ *
+ * Revision 4.0.1.2 91/06/07 11:50:33 lwall
+ * patch4: new copyright notice
+ * patch4: // wouldn't use previous pattern if it started with a null character
+ *
+ * Revision 4.0.1.1 91/04/12 09:07:39 lwall
+ * patch1: regexec only allocated space for 9 subexpresssions
+ *
+ * Revision 4.0 91/03/20 01:39:16 lwall
+ * 4.0 baseline.
+ *
+ */
+/*SUPPRESS 112*/
+/*
+ * regcomp and regexec -- regsub and regerror are not used in perl
+ *
+ * Copyright (c) 1986 by University of Toronto.
+ * Written by Henry Spencer. Not derived from licensed software.
+ *
+ * Permission is granted to anyone to use this software for any
+ * purpose on any computer system, and to redistribute it freely,
+ * subject to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of
+ * this software, no matter how awful, even if they arise
+ * from defects in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either
+ * by explicit claim or by omission.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ **** Alterations to Henry's code are...
+ ****
+ **** Copyright (c) 1991, Larry Wall
+ ****
+ **** You may distribute under the terms of either the GNU General Public
+ **** License or the Artistic License, as specified in the README file.
+ *
+ * Beware that some of this code is subtly aware of the way operator
+ * precedence is structured in regular expressions. Serious changes in
+ * regular-expression syntax might require a total rethink.
+ */
+#include "EXTERN.h"
+#include "perl.h"
+#include "regcomp.h"
+
+#ifndef STATIC
+#define STATIC static
+#endif
+
+#ifdef DEBUGGING
+int regnarrate = 0;
+#endif
+
+/*
+ * regexec and friends
+ */
+
+/*
+ * Global work variables for regexec().
+ */
+static char *regprecomp;
+static char *reginput; /* String-input pointer. */
+static char regprev; /* char before regbol, \n if none */
+static char *regbol; /* Beginning of input, for ^ check. */
+static char *regeol; /* End of input, for $ check. */
+static char **regstartp; /* Pointer to startp array. */
+static char **regendp; /* Ditto for endp. */
+static char *reglastparen; /* Similarly for lastparen. */
+static char *regtill;
+
+static int regmyp_size = 0;
+static char **regmystartp = Null(char**);
+static char **regmyendp = Null(char**);
+
+/*
+ * Forwards.
+ */
+STATIC int regtry();
+STATIC int regmatch();
+STATIC int regrepeat();
+
+extern int multiline;
+
+/*
+ - regexec - match a regexp against a string
+ */
+int
+regexec(prog, stringarg, strend, strbeg, minend, screamer, safebase)
+register regexp *prog;
+char *stringarg;
+register char *strend; /* pointer to null at end of string */
+char *strbeg; /* real beginning of string */
+int minend; /* end of match must be at least minend after stringarg */
+STR *screamer;
+int safebase; /* no need to remember string in subbase */
+{
+ register char *s;
+ register int i;
+ register char *c;
+ register char *string = stringarg;
+ register int tmp;
+ int minlen = 0; /* must match at least this many chars */
+ int dontbother = 0; /* how many characters not to try at end */
+
+ /* Be paranoid... */
+ if (prog == NULL || string == NULL) {
+ fatal("NULL regexp parameter");
+ return(0);
+ }
+
+ if (string == strbeg) /* is ^ valid at stringarg? */
+ regprev = '\n';
+ else {
+ regprev = stringarg[-1];
+ if (!multiline && regprev == '\n')
+ regprev = '\0'; /* force ^ to NOT match */
+ }
+ regprecomp = prog->precomp;
+ /* Check validity of program. */
+ if (UCHARAT(prog->program) != MAGIC) {
+ FAIL("corrupted regexp program");
+ }
+
+ if (prog->do_folding) {
+ i = strend - string;
+ New(1101,c,i+1,char);
+ Copy(string, c, i+1, char);
+ string = c;
+ strend = string + i;
+ for (s = string; s < strend; s++)
+ if (isUPPER(*s))
+ *s = tolower(*s);
+ }
+
+ /* If there is a "must appear" string, look for it. */
+ s = string;
+ if (prog->regmust != Nullstr &&
+ (!(prog->reganch & ROPT_ANCH)
+ || (multiline && prog->regback >= 0)) ) {
+ if (stringarg == strbeg && screamer) {
+ if (screamfirst[prog->regmust->str_rare] >= 0)
+ s = screaminstr(screamer,prog->regmust);
+ else
+ s = Nullch;
+ }
+#ifndef lint
+ else
+ s = fbminstr((unsigned char*)s, (unsigned char*)strend,
+ prog->regmust);
+#endif
+ if (!s) {
+ ++prog->regmust->str_u.str_useful; /* hooray */
+ goto phooey; /* not present */
+ }
+ else if (prog->regback >= 0) {
+ s -= prog->regback;
+ if (s < string)
+ s = string;
+ minlen = prog->regback + prog->regmust->str_cur;
+ }
+ else if (--prog->regmust->str_u.str_useful < 0) { /* boo */
+ str_free(prog->regmust);
+ prog->regmust = Nullstr; /* disable regmust */
+ s = string;
+ }
+ else {
+ s = string;
+ minlen = prog->regmust->str_cur;
+ }
+ }
+
+ /* Mark beginning of line for ^ . */
+ regbol = string;
+
+ /* Mark end of line for $ (and such) */
+ regeol = strend;
+
+ /* see how far we have to get to not match where we matched before */
+ regtill = string+minend;
+
+ /* Allocate our backreference arrays */
+ if ( regmyp_size < prog->nparens + 1 ) {
+ /* Allocate or enlarge the arrays */
+ regmyp_size = prog->nparens + 1;
+ if ( regmyp_size < 10 ) regmyp_size = 10; /* minimum */
+ if ( regmystartp ) {
+ /* reallocate larger */
+ Renew(regmystartp,regmyp_size,char*);
+ Renew(regmyendp, regmyp_size,char*);
+ }
+ else {
+ /* Initial allocation */
+ New(1102,regmystartp,regmyp_size,char*);
+ New(1102,regmyendp, regmyp_size,char*);
+ }
+
+ }
+
+ /* Simplest case: anchored match need be tried only once. */
+ /* [unless multiline is set] */
+ if (prog->reganch & ROPT_ANCH) {
+ if (regtry(prog, string))
+ goto got_it;
+ else if (multiline || (prog->reganch & ROPT_IMPLICIT)) {
+ if (minlen)
+ dontbother = minlen - 1;
+ strend -= dontbother;
+ /* for multiline we only have to try after newlines */
+ if (s > string)
+ s--;
+ while (s < strend) {
+ if (*s++ == '\n') {
+ if (s < strend && regtry(prog, s))
+ goto got_it;
+ }
+ }
+ }
+ goto phooey;
+ }
+
+ /* Messy cases: unanchored match. */
+ if (prog->regstart) {
+ if (prog->reganch & ROPT_SKIP) { /* we have /x+whatever/ */
+ /* it must be a one character string */
+ i = prog->regstart->str_ptr[0];
+ while (s < strend) {
+ if (*s == i) {
+ if (regtry(prog, s))
+ goto got_it;
+ s++;
+ while (s < strend && *s == i)
+ s++;
+ }
+ s++;
+ }
+ }
+ else if (prog->regstart->str_pok == 3) {
+ /* We know what string it must start with. */
+#ifndef lint
+ while ((s = fbminstr((unsigned char*)s,
+ (unsigned char*)strend, prog->regstart)) != NULL)
+#else
+ while (s = Nullch)
+#endif
+ {
+ if (regtry(prog, s))
+ goto got_it;
+ s++;
+ }
+ }
+ else {
+ c = prog->regstart->str_ptr;
+ while ((s = ninstr(s, strend,
+ c, c + prog->regstart->str_cur )) != NULL) {
+ if (regtry(prog, s))
+ goto got_it;
+ s++;
+ }
+ }
+ goto phooey;
+ }
+ /*SUPPRESS 560*/
+ if (c = prog->regstclass) {
+ int doevery = (prog->reganch & ROPT_SKIP) == 0;
+
+ if (minlen)
+ dontbother = minlen - 1;
+ strend -= dontbother; /* don't bother with what can't match */
+ tmp = 1;
+ /* We know what class it must start with. */
+ switch (OP(c)) {
+ case ANYOF:
+ c = OPERAND(c);
+ while (s < strend) {
+ i = UCHARAT(s);
+ if (!(c[i >> 3] & (1 << (i&7)))) {
+ if (tmp && regtry(prog, s))
+ goto got_it;
+ else
+ tmp = doevery;
+ }
+ else
+ tmp = 1;
+ s++;
+ }
+ break;
+ case BOUND:
+ if (minlen)
+ dontbother++,strend--;
+ if (s != string) {
+ i = s[-1];
+ tmp = isALNUM(i);
+ }
+ else
+ tmp = isALNUM(regprev); /* assume not alphanumeric */
+ while (s < strend) {
+ i = *s;
+ if (tmp != isALNUM(i)) {
+ tmp = !tmp;
+ if (regtry(prog, s))
+ goto got_it;
+ }
+ s++;
+ }
+ if ((minlen || tmp) && regtry(prog,s))
+ goto got_it;
+ break;
+ case NBOUND:
+ if (minlen)
+ dontbother++,strend--;
+ if (s != string) {
+ i = s[-1];
+ tmp = isALNUM(i);
+ }
+ else
+ tmp = isALNUM(regprev); /* assume not alphanumeric */
+ while (s < strend) {
+ i = *s;
+ if (tmp != isALNUM(i))
+ tmp = !tmp;
+ else if (regtry(prog, s))
+ goto got_it;
+ s++;
+ }
+ if ((minlen || !tmp) && regtry(prog,s))
+ goto got_it;
+ break;
+ case ALNUM:
+ while (s < strend) {
+ i = *s;
+ if (isALNUM(i)) {
+ if (tmp && regtry(prog, s))
+ goto got_it;
+ else
+ tmp = doevery;
+ }
+ else
+ tmp = 1;
+ s++;
+ }
+ break;
+ case NALNUM:
+ while (s < strend) {
+ i = *s;
+ if (!isALNUM(i)) {
+ if (tmp && regtry(prog, s))
+ goto got_it;
+ else
+ tmp = doevery;
+ }
+ else
+ tmp = 1;
+ s++;
+ }
+ break;
+ case SPACE:
+ while (s < strend) {
+ if (isSPACE(*s)) {
+ if (tmp && regtry(prog, s))
+ goto got_it;
+ else
+ tmp = doevery;
+ }
+ else
+ tmp = 1;
+ s++;
+ }
+ break;
+ case NSPACE:
+ while (s < strend) {
+ if (!isSPACE(*s)) {
+ if (tmp && regtry(prog, s))
+ goto got_it;
+ else
+ tmp = doevery;
+ }
+ else
+ tmp = 1;
+ s++;
+ }
+ break;
+ case DIGIT:
+ while (s < strend) {
+ if (isDIGIT(*s)) {
+ if (tmp && regtry(prog, s))
+ goto got_it;
+ else
+ tmp = doevery;
+ }
+ else
+ tmp = 1;
+ s++;
+ }
+ break;
+ case NDIGIT:
+ while (s < strend) {
+ if (!isDIGIT(*s)) {
+ if (tmp && regtry(prog, s))
+ goto got_it;
+ else
+ tmp = doevery;
+ }
+ else
+ tmp = 1;
+ s++;
+ }
+ break;
+ }
+ }
+ else {
+ if (minlen)
+ dontbother = minlen - 1;
+ strend -= dontbother;
+ /* We don't know much -- general case. */
+ do {
+ if (regtry(prog, s))
+ goto got_it;
+ } while (s++ < strend);
+ }
+
+ /* Failure. */
+ goto phooey;
+
+ got_it:
+ prog->subbeg = strbeg;
+ prog->subend = strend;
+ if ((!safebase && (prog->nparens || sawampersand)) || prog->do_folding){
+ strend += dontbother; /* uncheat */
+ if (safebase) /* no need for $digit later */
+ s = strbeg;
+ else if (strbeg != prog->subbase) {
+ i = strend - string + (stringarg - strbeg);
+ s = nsavestr(strbeg,i); /* so $digit will work later */
+ if (prog->subbase)
+ Safefree(prog->subbase);
+ prog->subbeg = prog->subbase = s;
+ prog->subend = s+i;
+ }
+ else {
+ i = strend - string + (stringarg - strbeg);
+ prog->subbeg = s = prog->subbase;
+ prog->subend = s+i;
+ }
+ s += (stringarg - strbeg);
+ for (i = 0; i <= prog->nparens; i++) {
+ if (prog->endp[i]) {
+ prog->startp[i] = s + (prog->startp[i] - string);
+ prog->endp[i] = s + (prog->endp[i] - string);
+ }
+ }
+ if (prog->do_folding)
+ Safefree(string);
+ }
+ return(1);
+
+ phooey:
+ if (prog->do_folding)
+ Safefree(string);
+ return(0);
+}
+
+/*
+ - regtry - try match at specific point
+ */
+static int /* 0 failure, 1 success */
+regtry(prog, string)
+regexp *prog;
+char *string;
+{
+ register int i;
+ register char **sp;
+ register char **ep;
+
+ reginput = string;
+ regstartp = prog->startp;
+ regendp = prog->endp;
+ reglastparen = &prog->lastparen;
+ prog->lastparen = 0;
+
+ sp = prog->startp;
+ ep = prog->endp;
+ if (prog->nparens) {
+ for (i = prog->nparens; i >= 0; i--) {
+ *sp++ = NULL;
+ *ep++ = NULL;
+ }
+ }
+ if (regmatch(prog->program + 1) && reginput >= regtill) {
+ prog->startp[0] = string;
+ prog->endp[0] = reginput;
+ return(1);
+ } else
+ return(0);
+}
+
+/*
+ - regmatch - main matching routine
+ *
+ * Conceptually the strategy is simple: check to see whether the current
+ * node matches, call self recursively to see whether the rest matches,
+ * and then act accordingly. In practice we make some effort to avoid
+ * recursion, in particular by going through "ordinary" nodes (that don't
+ * need to know whether the rest of the match failed) by a loop instead of
+ * by recursion.
+ */
+/* [lwall] I've hoisted the register declarations to the outer block in order to
+ * maybe save a little bit of pushing and popping on the stack. It also takes
+ * advantage of machines that use a register save mask on subroutine entry.
+ */
+static int /* 0 failure, 1 success */
+regmatch(prog)
+char *prog;
+{
+ register char *scan; /* Current node. */
+ char *next; /* Next node. */
+ register int nextchar;
+ register int n; /* no or next */
+ register int ln; /* len or last */
+ register char *s; /* operand or save */
+ register char *locinput = reginput;
+
+ nextchar = *locinput;
+ scan = prog;
+#ifdef DEBUGGING
+ if (scan != NULL && regnarrate)
+ fprintf(stderr, "%s(\n", regprop(scan));
+#endif
+ while (scan != NULL) {
+#ifdef DEBUGGING
+ if (regnarrate)
+ fprintf(stderr, "%s...\n", regprop(scan));
+#endif
+
+#ifdef REGALIGN
+ next = scan + NEXT(scan);
+ if (next == scan)
+ next = NULL;
+#else
+ next = regnext(scan);
+#endif
+
+ switch (OP(scan)) {
+ case BOL:
+ if (locinput == regbol ? regprev == '\n' :
+ ((nextchar || locinput < regeol) &&
+ locinput[-1] == '\n') )
+ {
+ /* regtill = regbol; */
+ break;
+ }
+ return(0);
+ case EOL:
+ if ((nextchar || locinput < regeol) && nextchar != '\n')
+ return(0);
+ if (!multiline && regeol - locinput > 1)
+ return 0;
+ /* regtill = regbol; */
+ break;
+ case ANY:
+ if ((nextchar == '\0' && locinput >= regeol) ||
+ nextchar == '\n')
+ return(0);
+ nextchar = *++locinput;
+ break;
+ case EXACTLY:
+ s = OPERAND(scan);
+ ln = *s++;
+ /* Inline the first character, for speed. */
+ if (*s != nextchar)
+ return(0);
+ if (regeol - locinput < ln)
+ return 0;
+ if (ln > 1 && bcmp(s, locinput, ln) != 0)
+ return(0);
+ locinput += ln;
+ nextchar = *locinput;
+ break;
+ case ANYOF:
+ s = OPERAND(scan);
+ if (nextchar < 0)
+ nextchar = UCHARAT(locinput);
+ if (s[nextchar >> 3] & (1 << (nextchar&7)))
+ return(0);
+ if (!nextchar && locinput >= regeol)
+ return 0;
+ nextchar = *++locinput;
+ break;
+ case ALNUM:
+ if (!nextchar)
+ return(0);
+ if (!isALNUM(nextchar))
+ return(0);
+ nextchar = *++locinput;
+ break;
+ case NALNUM:
+ if (!nextchar && locinput >= regeol)
+ return(0);
+ if (isALNUM(nextchar))
+ return(0);
+ nextchar = *++locinput;
+ break;
+ case NBOUND:
+ case BOUND:
+ if (locinput == regbol) /* was last char in word? */
+ ln = isALNUM(regprev);
+ else
+ ln = isALNUM(locinput[-1]);
+ n = isALNUM(nextchar); /* is next char in word? */
+ if ((ln == n) == (OP(scan) == BOUND))
+ return(0);
+ break;
+ case SPACE:
+ if (!nextchar && locinput >= regeol)
+ return(0);
+ if (!isSPACE(nextchar))
+ return(0);
+ nextchar = *++locinput;
+ break;
+ case NSPACE:
+ if (!nextchar)
+ return(0);
+ if (isSPACE(nextchar))
+ return(0);
+ nextchar = *++locinput;
+ break;
+ case DIGIT:
+ if (!isDIGIT(nextchar))
+ return(0);
+ nextchar = *++locinput;
+ break;
+ case NDIGIT:
+ if (!nextchar && locinput >= regeol)
+ return(0);
+ if (isDIGIT(nextchar))
+ return(0);
+ nextchar = *++locinput;
+ break;
+ case REF:
+ n = ARG1(scan); /* which paren pair */
+ s = regmystartp[n];
+ if (!s)
+ return(0);
+ if (!regmyendp[n])
+ return(0);
+ if (s == regmyendp[n])
+ break;
+ /* Inline the first character, for speed. */
+ if (*s != nextchar)
+ return(0);
+ ln = regmyendp[n] - s;
+ if (locinput + ln > regeol)
+ return 0;
+ if (ln > 1 && bcmp(s, locinput, ln) != 0)
+ return(0);
+ locinput += ln;
+ nextchar = *locinput;
+ break;
+
+ case NOTHING:
+ break;
+ case BACK:
+ break;
+ case OPEN:
+ n = ARG1(scan); /* which paren pair */
+ reginput = locinput;
+
+ regmystartp[n] = locinput; /* for REF */
+ if (regmatch(next)) {
+ /*
+ * Don't set startp if some later
+ * invocation of the same parentheses
+ * already has.
+ */
+ if (regstartp[n] == NULL)
+ regstartp[n] = locinput;
+ return(1);
+ } else
+ return(0);
+ /* NOTREACHED */
+ case CLOSE: {
+ n = ARG1(scan); /* which paren pair */
+ reginput = locinput;
+
+ regmyendp[n] = locinput; /* for REF */
+ if (regmatch(next)) {
+ /*
+ * Don't set endp if some later
+ * invocation of the same parentheses
+ * already has.
+ */
+ if (regendp[n] == NULL) {
+ regendp[n] = locinput;
+ if (n > *reglastparen)
+ *reglastparen = n;
+ }
+ return(1);
+ } else
+ return(0);
+ }
+ /*NOTREACHED*/
+ case BRANCH: {
+ if (OP(next) != BRANCH) /* No choice. */
+ next = NEXTOPER(scan); /* Avoid recursion. */
+ else {
+ do {
+ reginput = locinput;
+ if (regmatch(NEXTOPER(scan)))
+ return(1);
+#ifdef REGALIGN
+ /*SUPPRESS 560*/
+ if (n = NEXT(scan))
+ scan += n;
+ else
+ scan = NULL;
+#else
+ scan = regnext(scan);
+#endif
+ } while (scan != NULL && OP(scan) == BRANCH);
+ return(0);
+ /* NOTREACHED */
+ }
+ }
+ break;
+ case CURLY:
+ ln = ARG1(scan); /* min to match */
+ n = ARG2(scan); /* max to match */
+ scan = NEXTOPER(scan) + 4;
+ goto repeat;
+ case STAR:
+ ln = 0;
+ n = 32767;
+ scan = NEXTOPER(scan);
+ goto repeat;
+ case PLUS:
+ /*
+ * Lookahead to avoid useless match attempts
+ * when we know what character comes next.
+ */
+ ln = 1;
+ n = 32767;
+ scan = NEXTOPER(scan);
+ repeat:
+ if (OP(next) == EXACTLY)
+ nextchar = *(OPERAND(next)+1);
+ else
+ nextchar = -1000;
+ reginput = locinput;
+ n = regrepeat(scan, n);
+ if (!multiline && OP(next) == EOL && ln < n)
+ ln = n; /* why back off? */
+ while (n >= ln) {
+ /* If it could work, try it. */
+ if (nextchar == -1000 || *reginput == nextchar)
+ if (regmatch(next))
+ return(1);
+ /* Couldn't or didn't -- back up. */
+ n--;
+ reginput = locinput + n;
+ }
+ return(0);
+ case END:
+ reginput = locinput; /* put where regtry can find it */
+ return(1); /* Success! */
+ default:
+ printf("%x %d\n",scan,scan[1]);
+ FAIL("regexp memory corruption");
+ }
+
+ scan = next;
+ }
+
+ /*
+ * We get here only if there's trouble -- normally "case END" is
+ * the terminating point.
+ */
+ FAIL("corrupted regexp pointers");
+ /*NOTREACHED*/
+#ifdef lint
+ return 0;
+#endif
+}
+
+/*
+ - regrepeat - repeatedly match something simple, report how many
+ */
+/*
+ * [This routine now assumes that it will only match on things of length 1.
+ * That was true before, but now we assume scan - reginput is the count,
+ * rather than incrementing count on every character.]
+ */
+static int
+regrepeat(p, max)
+char *p;
+int max;
+{
+ register char *scan;
+ register char *opnd;
+ register int c;
+ register char *loceol = regeol;
+
+ scan = reginput;
+ if (max != 32767 && max < loceol - scan)
+ loceol = scan + max;
+ opnd = OPERAND(p);
+ switch (OP(p)) {
+ case ANY:
+ while (scan < loceol && *scan != '\n')
+ scan++;
+ break;
+ case EXACTLY: /* length of string is 1 */
+ opnd++;
+ while (scan < loceol && *opnd == *scan)
+ scan++;
+ break;
+ case ANYOF:
+ c = UCHARAT(scan);
+ while (scan < loceol && !(opnd[c >> 3] & (1 << (c & 7)))) {
+ scan++;
+ c = UCHARAT(scan);
+ }
+ break;
+ case ALNUM:
+ while (scan < loceol && isALNUM(*scan))
+ scan++;
+ break;
+ case NALNUM:
+ while (scan < loceol && !isALNUM(*scan))
+ scan++;
+ break;
+ case SPACE:
+ while (scan < loceol && isSPACE(*scan))
+ scan++;
+ break;
+ case NSPACE:
+ while (scan < loceol && !isSPACE(*scan))
+ scan++;
+ break;
+ case DIGIT:
+ while (scan < loceol && isDIGIT(*scan))
+ scan++;
+ break;
+ case NDIGIT:
+ while (scan < loceol && !isDIGIT(*scan))
+ scan++;
+ break;
+ default: /* Oh dear. Called inappropriately. */
+ FAIL("internal regexp foulup");
+ /* NOTREACHED */
+ }
+
+ c = scan - reginput;
+ reginput = scan;
+
+ return(c);
+}
+
+/*
+ - regnext - dig the "next" pointer out of a node
+ *
+ * [Note, when REGALIGN is defined there are two places in regmatch()
+ * that bypass this code for speed.]
+ */
+char *
+regnext(p)
+register char *p;
+{
+ register int offset;
+
+ if (p == &regdummy)
+ return(NULL);
+
+ offset = NEXT(p);
+ if (offset == 0)
+ return(NULL);
+
+#ifdef REGALIGN
+ return(p+offset);
+#else
+ if (OP(p) == BACK)
+ return(p-offset);
+ else
+ return(p+offset);
+#endif
+}
diff --git a/gnu/usr.bin/perl/perl/regexp.h b/gnu/usr.bin/perl/perl/regexp.h
new file mode 100644
index 0000000..66a1b88
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/regexp.h
@@ -0,0 +1,53 @@
+/*
+ * Definitions etc. for regexp(3) routines.
+ *
+ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof],
+ * not the System V one.
+ */
+
+/* $RCSfile: regexp.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:39 $
+ *
+ * $Log: regexp.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:39 nate
+ * PERL!
+ *
+ * Revision 4.0.1.2 91/11/05 18:24:31 lwall
+ * patch11: minimum match length calculation in regexp is now cumulative
+ * patch11: initial .* in pattern had dependency on value of $*
+ *
+ * Revision 4.0.1.1 91/06/07 11:51:18 lwall
+ * patch4: new copyright notice
+ * patch4: // wouldn't use previous pattern if it started with a null character
+ * patch4: $` was busted inside s///
+ *
+ * Revision 4.0 91/03/20 01:39:23 lwall
+ * 4.0 baseline.
+ *
+ */
+
+typedef struct regexp {
+ char **startp;
+ char **endp;
+ STR *regstart; /* Internal use only. */
+ char *regstclass;
+ STR *regmust; /* Internal use only. */
+ int regback; /* Can regmust locate first try? */
+ int minlen; /* mininum possible length of $& */
+ int prelen; /* length of precomp */
+ char *precomp; /* pre-compilation regular expression */
+ char *subbase; /* saved string so \digit works forever */
+ char *subbeg; /* same, but not responsible for allocation */
+ char *subend; /* end of subbase */
+ char reganch; /* Internal use only. */
+ char do_folding; /* do case-insensitive match? */
+ char lastparen; /* last paren matched */
+ char nparens; /* number of parentheses */
+ char program[1]; /* Unwarranted chumminess with compiler. */
+} regexp;
+
+#define ROPT_ANCH 1
+#define ROPT_SKIP 2
+#define ROPT_IMPLICIT 4
+
+regexp *regcomp();
+int regexec();
diff --git a/gnu/usr.bin/perl/perl/spat.h b/gnu/usr.bin/perl/perl/spat.h
new file mode 100644
index 0000000..2a840e2
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/spat.h
@@ -0,0 +1,46 @@
+/* $RCSfile: spat.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:39 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: spat.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:39 nate
+ * PERL!
+ *
+ * Revision 4.0.1.1 91/06/07 11:51:59 lwall
+ * patch4: new copyright notice
+ * patch4: added global modifier for pattern matches
+ *
+ * Revision 4.0 91/03/20 01:39:36 lwall
+ * 4.0 baseline.
+ *
+ */
+
+struct scanpat {
+ SPAT *spat_next; /* list of all scanpats */
+ REGEXP *spat_regexp; /* compiled expression */
+ ARG *spat_repl; /* replacement string for subst */
+ ARG *spat_runtime; /* compile pattern at runtime */
+ STR *spat_short; /* for a fast bypass of execute() */
+ short spat_flags;
+ char spat_slen;
+};
+
+#define SPAT_USED 1 /* spat has been used once already */
+#define SPAT_ONCE 2 /* use pattern only once per reset */
+#define SPAT_SCANFIRST 4 /* initial constant not anchored */
+#define SPAT_ALL 8 /* initial constant is whole pat */
+#define SPAT_SKIPWHITE 16 /* skip leading whitespace for split */
+#define SPAT_FOLD 32 /* case insensitivity */
+#define SPAT_CONST 64 /* subst replacement is constant */
+#define SPAT_KEEP 128 /* keep 1st runtime pattern forever */
+#define SPAT_GLOBAL 256 /* pattern had a g modifier */
+
+EXT SPAT *curspat; /* what to do \ interps from */
+EXT SPAT *lastspat; /* what to use in place of null pattern */
+
+EXT char *hint INIT(Nullch); /* hint from cmd_exec to do_match et al */
+
+#define Nullspat Null(SPAT*)
diff --git a/gnu/usr.bin/perl/perl/stab.c b/gnu/usr.bin/perl/perl/stab.c
new file mode 100644
index 0000000..7082725
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/stab.c
@@ -0,0 +1,1055 @@
+/* $RCSfile: stab.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:39 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: stab.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:39 nate
+ * PERL!
+ *
+ * Revision 4.0.1.5 1993/02/05 19:42:47 lwall
+ * patch36: length returned wrong value on certain semi-magical variables
+ *
+ * Revision 4.0.1.4 92/06/08 15:32:19 lwall
+ * patch20: fixed confusion between a *var's real name and its effective name
+ * patch20: the debugger now warns you on lines that can't set a breakpoint
+ * patch20: the debugger made perl forget the last pattern used by //
+ * patch20: paragraph mode now skips extra newlines automatically
+ * patch20: ($<,$>) = ... didn't work on some architectures
+ *
+ * Revision 4.0.1.3 91/11/05 18:35:33 lwall
+ * patch11: length($x) was sometimes wrong for numeric $x
+ * patch11: perl now issues warning if $SIG{'ALARM'} is referenced
+ * patch11: *foo = undef coredumped
+ * patch11: solitary subroutine references no longer trigger typo warnings
+ * patch11: local(*FILEHANDLE) had a memory leak
+ *
+ * Revision 4.0.1.2 91/06/07 11:55:53 lwall
+ * patch4: new copyright notice
+ * patch4: added $^P variable to control calling of perldb routines
+ * patch4: added $^F variable to specify maximum system fd, default 2
+ * patch4: $` was busted inside s///
+ * patch4: default top-of-form format is now FILEHANDLE_TOP
+ * patch4: length($`), length($&), length($') now optimized to avoid string copy
+ * patch4: $^D |= 1024 now does syntax tree dump at run-time
+ *
+ * Revision 4.0.1.1 91/04/12 09:10:24 lwall
+ * patch1: Configure now differentiates getgroups() type from getgid() type
+ * patch1: you may now use "die" and "caller" in a signal handler
+ *
+ * Revision 4.0 91/03/20 01:39:41 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+
+#if !defined(NSIG) || defined(M_UNIX) || defined(M_XENIX)
+#include <signal.h>
+#endif
+
+static char *sig_name[] = {
+ SIG_NAME,0
+};
+
+#ifdef VOIDSIG
+#define handlertype void
+#else
+#define handlertype int
+#endif
+
+static handlertype sighandler();
+
+static int origalen = 0;
+
+STR *
+stab_str(str)
+STR *str;
+{
+ STAB *stab = str->str_u.str_stab;
+ register int paren;
+ register char *s;
+ register int i;
+
+ if (str->str_rare)
+ return stab_val(stab);
+
+ switch (*stab->str_magic->str_ptr) {
+ case '\004': /* ^D */
+#ifdef DEBUGGING
+ str_numset(stab_val(stab),(double)(debug & 32767));
+#endif
+ break;
+ case '\006': /* ^F */
+ str_numset(stab_val(stab),(double)maxsysfd);
+ break;
+ case '\t': /* ^I */
+ if (inplace)
+ str_set(stab_val(stab), inplace);
+ else
+ str_sset(stab_val(stab),&str_undef);
+ break;
+ case '\020': /* ^P */
+ str_numset(stab_val(stab),(double)perldb);
+ break;
+ case '\024': /* ^T */
+ str_numset(stab_val(stab),(double)basetime);
+ break;
+ case '\027': /* ^W */
+ str_numset(stab_val(stab),(double)dowarn);
+ break;
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9': case '&':
+ if (curspat) {
+ paren = atoi(stab_ename(stab));
+ getparen:
+ if (curspat->spat_regexp &&
+ paren <= curspat->spat_regexp->nparens &&
+ (s = curspat->spat_regexp->startp[paren]) ) {
+ i = curspat->spat_regexp->endp[paren] - s;
+ if (i >= 0)
+ str_nset(stab_val(stab),s,i);
+ else
+ str_sset(stab_val(stab),&str_undef);
+ }
+ else
+ str_sset(stab_val(stab),&str_undef);
+ }
+ break;
+ case '+':
+ if (curspat) {
+ paren = curspat->spat_regexp->lastparen;
+ goto getparen;
+ }
+ break;
+ case '`':
+ if (curspat) {
+ if (curspat->spat_regexp &&
+ (s = curspat->spat_regexp->subbeg) ) {
+ i = curspat->spat_regexp->startp[0] - s;
+ if (i >= 0)
+ str_nset(stab_val(stab),s,i);
+ else
+ str_nset(stab_val(stab),"",0);
+ }
+ else
+ str_nset(stab_val(stab),"",0);
+ }
+ break;
+ case '\'':
+ if (curspat) {
+ if (curspat->spat_regexp &&
+ (s = curspat->spat_regexp->endp[0]) ) {
+ str_nset(stab_val(stab),s, curspat->spat_regexp->subend - s);
+ }
+ else
+ str_nset(stab_val(stab),"",0);
+ }
+ break;
+ case '.':
+#ifndef lint
+ if (last_in_stab && stab_io(last_in_stab)) {
+ str_numset(stab_val(stab),(double)stab_io(last_in_stab)->lines);
+ }
+#endif
+ break;
+ case '?':
+ str_numset(stab_val(stab),(double)statusvalue);
+ break;
+ case '^':
+ s = stab_io(curoutstab)->top_name;
+ if (s)
+ str_set(stab_val(stab),s);
+ else {
+ str_set(stab_val(stab),stab_ename(curoutstab));
+ str_cat(stab_val(stab),"_TOP");
+ }
+ break;
+ case '~':
+ s = stab_io(curoutstab)->fmt_name;
+ if (!s)
+ s = stab_ename(curoutstab);
+ str_set(stab_val(stab),s);
+ break;
+#ifndef lint
+ case '=':
+ str_numset(stab_val(stab),(double)stab_io(curoutstab)->page_len);
+ break;
+ case '-':
+ str_numset(stab_val(stab),(double)stab_io(curoutstab)->lines_left);
+ break;
+ case '%':
+ str_numset(stab_val(stab),(double)stab_io(curoutstab)->page);
+ break;
+#endif
+ case ':':
+ break;
+ case '/':
+ break;
+ case '[':
+ str_numset(stab_val(stab),(double)arybase);
+ break;
+ case '|':
+ if (!stab_io(curoutstab))
+ stab_io(curoutstab) = stio_new();
+ str_numset(stab_val(stab),
+ (double)((stab_io(curoutstab)->flags & IOF_FLUSH) != 0) );
+ break;
+ case ',':
+ str_nset(stab_val(stab),ofs,ofslen);
+ break;
+ case '\\':
+ str_nset(stab_val(stab),ors,orslen);
+ break;
+ case '#':
+ str_set(stab_val(stab),ofmt);
+ break;
+ case '!':
+ str_numset(stab_val(stab), (double)errno);
+ str_set(stab_val(stab), errno ? strerror(errno) : "");
+ stab_val(stab)->str_nok = 1; /* what a wonderful hack! */
+ break;
+ case '<':
+ str_numset(stab_val(stab),(double)uid);
+ break;
+ case '>':
+ str_numset(stab_val(stab),(double)euid);
+ break;
+ case '(':
+ s = buf;
+ (void)sprintf(s,"%d",(int)gid);
+ goto add_groups;
+ case ')':
+ s = buf;
+ (void)sprintf(s,"%d",(int)egid);
+ add_groups:
+ while (*s) s++;
+#ifdef HAS_GETGROUPS
+#ifndef NGROUPS
+#define NGROUPS 32
+#endif
+ {
+ GROUPSTYPE gary[NGROUPS];
+
+ i = getgroups(NGROUPS,gary);
+ while (--i >= 0) {
+ (void)sprintf(s," %ld", (long)gary[i]);
+ while (*s) s++;
+ }
+ }
+#endif
+ str_set(stab_val(stab),buf);
+ break;
+ case '*':
+ break;
+ case '0':
+ break;
+ default:
+ {
+ struct ufuncs *uf = (struct ufuncs *)str->str_ptr;
+
+ if (uf && uf->uf_val)
+ (*uf->uf_val)(uf->uf_index, stab_val(stab));
+ }
+ break;
+ }
+ return stab_val(stab);
+}
+
+STRLEN
+stab_len(str)
+STR *str;
+{
+ STAB *stab = str->str_u.str_stab;
+ int paren;
+ int i;
+ char *s;
+
+ if (str->str_rare)
+ return str_len(stab_val(stab));
+
+ switch (*stab->str_magic->str_ptr) {
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9': case '&':
+ if (curspat) {
+ paren = atoi(stab_ename(stab));
+ getparen:
+ if (curspat->spat_regexp &&
+ paren <= curspat->spat_regexp->nparens &&
+ (s = curspat->spat_regexp->startp[paren]) ) {
+ i = curspat->spat_regexp->endp[paren] - s;
+ if (i >= 0)
+ return i;
+ else
+ return 0;
+ }
+ else
+ return 0;
+ }
+ break;
+ case '+':
+ if (curspat) {
+ paren = curspat->spat_regexp->lastparen;
+ goto getparen;
+ }
+ break;
+ case '`':
+ if (curspat) {
+ if (curspat->spat_regexp &&
+ (s = curspat->spat_regexp->subbeg) ) {
+ i = curspat->spat_regexp->startp[0] - s;
+ if (i >= 0)
+ return i;
+ else
+ return 0;
+ }
+ else
+ return 0;
+ }
+ break;
+ case '\'':
+ if (curspat) {
+ if (curspat->spat_regexp &&
+ (s = curspat->spat_regexp->endp[0]) ) {
+ return (STRLEN) (curspat->spat_regexp->subend - s);
+ }
+ else
+ return 0;
+ }
+ break;
+ case ',':
+ return (STRLEN)ofslen;
+ case '\\':
+ return (STRLEN)orslen;
+ }
+ return str_len(stab_str(str));
+}
+
+void
+stabset(mstr,str)
+register STR *mstr;
+STR *str;
+{
+ STAB *stab;
+ register char *s;
+ int i;
+
+ switch (mstr->str_rare) {
+ case 'E':
+ my_setenv(mstr->str_ptr,str_get(str));
+ /* And you'll never guess what the dog had */
+ /* in its mouth... */
+#ifdef TAINT
+ if (strEQ(mstr->str_ptr,"PATH")) {
+ char *strend = str->str_ptr + str->str_cur;
+
+ s = str->str_ptr;
+ while (s < strend) {
+ s = cpytill(tokenbuf,s,strend,':',&i);
+ s++;
+ if (*tokenbuf != '/'
+ || (stat(tokenbuf,&statbuf) && (statbuf.st_mode & 2)) )
+ str->str_tainted = 2;
+ }
+ }
+#endif
+ break;
+ case 'S':
+ s = str_get(str);
+ i = whichsig(mstr->str_ptr); /* ...no, a brick */
+ if (!i && (dowarn || strEQ(mstr->str_ptr,"ALARM")))
+ warn("No such signal: SIG%s", mstr->str_ptr);
+ if (strEQ(s,"IGNORE"))
+#ifndef lint
+ (void)signal(i,SIG_IGN);
+#else
+ ;
+#endif
+ else if (strEQ(s,"DEFAULT") || !*s)
+ (void)signal(i,SIG_DFL);
+ else {
+ (void)signal(i,sighandler);
+ if (!index(s,'\'')) {
+ sprintf(tokenbuf, "main'%s",s);
+ str_set(str,tokenbuf);
+ }
+ }
+ break;
+#ifdef SOME_DBM
+ case 'D':
+ stab = mstr->str_u.str_stab;
+ hdbmstore(stab_hash(stab),mstr->str_ptr,mstr->str_cur,str);
+ break;
+#endif
+ case 'L':
+ {
+ CMD *cmd;
+
+ stab = mstr->str_u.str_stab;
+ i = str_true(str);
+ str = afetch(stab_xarray(stab),atoi(mstr->str_ptr), FALSE);
+ if (str->str_magic && (cmd = str->str_magic->str_u.str_cmd)) {
+ cmd->c_flags &= ~CF_OPTIMIZE;
+ cmd->c_flags |= i? CFT_D1 : CFT_D0;
+ }
+ else
+ warn("Can't break at that line\n");
+ }
+ break;
+ case '#':
+ stab = mstr->str_u.str_stab;
+ afill(stab_array(stab), (int)str_gnum(str) - arybase);
+ break;
+ case 'X': /* merely a copy of a * string */
+ break;
+ case '*':
+ s = str->str_pok ? str_get(str) : "";
+ if (strNE(s,"StB") || str->str_cur != sizeof(STBP)) {
+ stab = mstr->str_u.str_stab;
+ if (!*s) {
+ STBP *stbp;
+
+ /*SUPPRESS 701*/
+ (void)savenostab(stab); /* schedule a free of this stab */
+ if (stab->str_len)
+ Safefree(stab->str_ptr);
+ Newz(601,stbp, 1, STBP);
+ stab->str_ptr = stbp;
+ stab->str_len = stab->str_cur = sizeof(STBP);
+ stab->str_pok = 1;
+ strcpy(stab_magic(stab),"StB");
+ stab_val(stab) = Str_new(70,0);
+ stab_line(stab) = curcmd->c_line;
+ stab_estab(stab) = stab;
+ }
+ else {
+ stab = stabent(s,TRUE);
+ if (!stab_xarray(stab))
+ aadd(stab);
+ if (!stab_xhash(stab))
+ hadd(stab);
+ if (!stab_io(stab))
+ stab_io(stab) = stio_new();
+ }
+ str_sset(str, (STR*) stab);
+ }
+ break;
+ case 's': {
+ struct lstring *lstr = (struct lstring*)str;
+ char *tmps;
+
+ mstr->str_rare = 0;
+ str->str_magic = Nullstr;
+ tmps = str_get(str);
+ str_insert(mstr,lstr->lstr_offset,lstr->lstr_len,
+ tmps,str->str_cur);
+ }
+ break;
+
+ case 'v':
+ do_vecset(mstr,str);
+ break;
+
+ case 0:
+ /*SUPPRESS 560*/
+ if (!(stab = mstr->str_u.str_stab))
+ break;
+ switch (*stab->str_magic->str_ptr) {
+ case '\004': /* ^D */
+#ifdef DEBUGGING
+ debug = (int)(str_gnum(str)) | 32768;
+ if (debug & 1024)
+ dump_all();
+#endif
+ break;
+ case '\006': /* ^F */
+ maxsysfd = (int)str_gnum(str);
+ break;
+ case '\t': /* ^I */
+ if (inplace)
+ Safefree(inplace);
+ if (str->str_pok || str->str_nok)
+ inplace = savestr(str_get(str));
+ else
+ inplace = Nullch;
+ break;
+ case '\020': /* ^P */
+ i = (int)str_gnum(str);
+ if (i != perldb) {
+ static SPAT *oldlastspat;
+
+ if (perldb)
+ oldlastspat = lastspat;
+ else
+ lastspat = oldlastspat;
+ }
+ perldb = i;
+ break;
+ case '\024': /* ^T */
+ basetime = (time_t)str_gnum(str);
+ break;
+ case '\027': /* ^W */
+ dowarn = (bool)str_gnum(str);
+ break;
+ case '.':
+ if (localizing)
+ savesptr((STR**)&last_in_stab);
+ break;
+ case '^':
+ Safefree(stab_io(curoutstab)->top_name);
+ stab_io(curoutstab)->top_name = s = savestr(str_get(str));
+ stab_io(curoutstab)->top_stab = stabent(s,TRUE);
+ break;
+ case '~':
+ Safefree(stab_io(curoutstab)->fmt_name);
+ stab_io(curoutstab)->fmt_name = s = savestr(str_get(str));
+ stab_io(curoutstab)->fmt_stab = stabent(s,TRUE);
+ break;
+ case '=':
+ stab_io(curoutstab)->page_len = (long)str_gnum(str);
+ break;
+ case '-':
+ stab_io(curoutstab)->lines_left = (long)str_gnum(str);
+ if (stab_io(curoutstab)->lines_left < 0L)
+ stab_io(curoutstab)->lines_left = 0L;
+ break;
+ case '%':
+ stab_io(curoutstab)->page = (long)str_gnum(str);
+ break;
+ case '|':
+ if (!stab_io(curoutstab))
+ stab_io(curoutstab) = stio_new();
+ stab_io(curoutstab)->flags &= ~IOF_FLUSH;
+ if (str_gnum(str) != 0.0) {
+ stab_io(curoutstab)->flags |= IOF_FLUSH;
+ }
+ break;
+ case '*':
+ i = (int)str_gnum(str);
+ multiline = (i != 0);
+ break;
+ case '/':
+ if (str->str_pok) {
+ rs = str_get(str);
+ rslen = str->str_cur;
+ if (rspara = !rslen) {
+ rs = "\n\n";
+ rslen = 2;
+ }
+ rschar = rs[rslen - 1];
+ }
+ else {
+ rschar = 0777; /* fake a non-existent char */
+ rslen = 1;
+ }
+ break;
+ case '\\':
+ if (ors)
+ Safefree(ors);
+ ors = savestr(str_get(str));
+ orslen = str->str_cur;
+ break;
+ case ',':
+ if (ofs)
+ Safefree(ofs);
+ ofs = savestr(str_get(str));
+ ofslen = str->str_cur;
+ break;
+ case '#':
+ if (ofmt)
+ Safefree(ofmt);
+ ofmt = savestr(str_get(str));
+ break;
+ case '[':
+ arybase = (int)str_gnum(str);
+ break;
+ case '?':
+ statusvalue = U_S(str_gnum(str));
+ break;
+ case '!':
+ errno = (int)str_gnum(str); /* will anyone ever use this? */
+ break;
+ case '<':
+ uid = (int)str_gnum(str);
+ if (delaymagic) {
+ delaymagic |= DM_RUID;
+ break; /* don't do magic till later */
+ }
+#ifdef HAS_SETRUID
+ (void)setruid((UIDTYPE)uid);
+#else
+#ifdef HAS_SETREUID
+ (void)setreuid((UIDTYPE)uid, (UIDTYPE)-1);
+#else
+ if (uid == euid) /* special case $< = $> */
+ (void)setuid(uid);
+ else
+ fatal("setruid() not implemented");
+#endif
+#endif
+ uid = (int)getuid();
+ break;
+ case '>':
+ euid = (int)str_gnum(str);
+ if (delaymagic) {
+ delaymagic |= DM_EUID;
+ break; /* don't do magic till later */
+ }
+#ifdef HAS_SETEUID
+ (void)seteuid((UIDTYPE)euid);
+#else
+#ifdef HAS_SETREUID
+ (void)setreuid((UIDTYPE)-1, (UIDTYPE)euid);
+#else
+ if (euid == uid) /* special case $> = $< */
+ setuid(euid);
+ else
+ fatal("seteuid() not implemented");
+#endif
+#endif
+ euid = (int)geteuid();
+ break;
+ case '(':
+ gid = (int)str_gnum(str);
+ if (delaymagic) {
+ delaymagic |= DM_RGID;
+ break; /* don't do magic till later */
+ }
+#ifdef HAS_SETRGID
+ (void)setrgid((GIDTYPE)gid);
+#else
+#ifdef HAS_SETREGID
+ (void)setregid((GIDTYPE)gid, (GIDTYPE)-1);
+#else
+ if (gid == egid) /* special case $( = $) */
+ (void)setgid(gid);
+ else
+ fatal("setrgid() not implemented");
+#endif
+#endif
+ gid = (int)getgid();
+ break;
+ case ')':
+ egid = (int)str_gnum(str);
+ if (delaymagic) {
+ delaymagic |= DM_EGID;
+ break; /* don't do magic till later */
+ }
+#ifdef HAS_SETEGID
+ (void)setegid((GIDTYPE)egid);
+#else
+#ifdef HAS_SETREGID
+ (void)setregid((GIDTYPE)-1, (GIDTYPE)egid);
+#else
+ if (egid == gid) /* special case $) = $( */
+ (void)setgid(egid);
+ else
+ fatal("setegid() not implemented");
+#endif
+#endif
+ egid = (int)getegid();
+ break;
+ case ':':
+ chopset = str_get(str);
+ break;
+ case '0':
+ if (!origalen) {
+ s = origargv[0];
+ s += strlen(s);
+ /* See if all the arguments are contiguous in memory */
+ for (i = 1; i < origargc; i++) {
+ if (origargv[i] == s + 1)
+ s += strlen(++s); /* this one is ok too */
+ }
+ if (origenviron[0] == s + 1) { /* can grab env area too? */
+ my_setenv("NoNeSuCh", Nullch);
+ /* force copy of environment */
+ for (i = 0; origenviron[i]; i++)
+ if (origenviron[i] == s + 1)
+ s += strlen(++s);
+ }
+ origalen = s - origargv[0];
+ }
+ s = str_get(str);
+ i = str->str_cur;
+ if (i >= origalen) {
+ i = origalen;
+ str->str_cur = i;
+ str->str_ptr[i] = '\0';
+ Copy(s, origargv[0], i, char);
+ }
+ else {
+ Copy(s, origargv[0], i, char);
+ s = origargv[0]+i;
+ *s++ = '\0';
+ while (++i < origalen)
+ *s++ = ' ';
+ }
+ break;
+ default:
+ {
+ struct ufuncs *uf = (struct ufuncs *)str->str_magic->str_ptr;
+
+ if (uf && uf->uf_set)
+ (*uf->uf_set)(uf->uf_index, str);
+ }
+ break;
+ }
+ break;
+ }
+}
+
+int
+whichsig(sig)
+char *sig;
+{
+ register char **sigv;
+
+ for (sigv = sig_name+1; *sigv; sigv++)
+ if (strEQ(sig,*sigv))
+ return sigv - sig_name;
+#ifdef SIGCLD
+ if (strEQ(sig,"CHLD"))
+ return SIGCLD;
+#endif
+#ifdef SIGCHLD
+ if (strEQ(sig,"CLD"))
+ return SIGCHLD;
+#endif
+ return 0;
+}
+
+static handlertype
+sighandler(sig)
+int sig;
+{
+ STAB *stab;
+ STR *str;
+ int oldsave = savestack->ary_fill;
+ int oldtmps_base = tmps_base;
+ register CSV *csv;
+ SUBR *sub;
+
+#ifdef OS2 /* or anybody else who requires SIG_ACK */
+ signal(sig, SIG_ACK);
+#endif
+ stab = stabent(
+ str_get(hfetch(stab_hash(sigstab),sig_name[sig],strlen(sig_name[sig]),
+ TRUE)), TRUE);
+ sub = stab_sub(stab);
+ if (!sub && *sig_name[sig] == 'C' && instr(sig_name[sig],"LD")) {
+ if (sig_name[sig][1] == 'H')
+ stab = stabent(str_get(hfetch(stab_hash(sigstab),"CLD",3,TRUE)),
+ TRUE);
+ else
+ stab = stabent(str_get(hfetch(stab_hash(sigstab),"CHLD",4,TRUE)),
+ TRUE);
+ sub = stab_sub(stab); /* gag */
+ }
+ if (!sub) {
+ if (dowarn)
+ warn("SIG%s handler \"%s\" not defined.\n",
+ sig_name[sig], stab_ename(stab) );
+ return;
+ }
+ /*SUPPRESS 701*/
+ saveaptr(&stack);
+ str = Str_new(15, sizeof(CSV));
+ str->str_state = SS_SCSV;
+ (void)apush(savestack,str);
+ csv = (CSV*)str->str_ptr;
+ csv->sub = sub;
+ csv->stab = stab;
+ csv->curcsv = curcsv;
+ csv->curcmd = curcmd;
+ csv->depth = sub->depth;
+ csv->wantarray = G_SCALAR;
+ csv->hasargs = TRUE;
+ csv->savearray = stab_xarray(defstab);
+ csv->argarray = stab_xarray(defstab) = stack = anew(defstab);
+ stack->ary_flags = 0;
+ curcsv = csv;
+ str = str_mortal(&str_undef);
+ str_set(str,sig_name[sig]);
+ (void)apush(stab_xarray(defstab),str);
+ sub->depth++;
+ if (sub->depth >= 2) { /* save temporaries on recursion? */
+ if (sub->depth == 100 && dowarn)
+ warn("Deep recursion on subroutine \"%s\"",stab_ename(stab));
+ savelist(sub->tosave->ary_array,sub->tosave->ary_fill);
+ }
+
+ tmps_base = tmps_max; /* protect our mortal string */
+ (void)cmd_exec(sub->cmd,G_SCALAR,0); /* so do it already */
+ tmps_base = oldtmps_base;
+
+ restorelist(oldsave); /* put everything back */
+}
+
+STAB *
+aadd(stab)
+register STAB *stab;
+{
+ if (!stab_xarray(stab))
+ stab_xarray(stab) = anew(stab);
+ return stab;
+}
+
+STAB *
+hadd(stab)
+register STAB *stab;
+{
+ if (!stab_xhash(stab))
+ stab_xhash(stab) = hnew(COEFFSIZE);
+ return stab;
+}
+
+STAB *
+fstab(name)
+char *name;
+{
+ char tmpbuf[1200];
+ STAB *stab;
+
+ sprintf(tmpbuf,"'_<%s", name);
+ stab = stabent(tmpbuf, TRUE);
+ str_set(stab_val(stab), name);
+ if (perldb)
+ (void)hadd(aadd(stab));
+ return stab;
+}
+
+STAB *
+stabent(name,add)
+register char *name;
+int add;
+{
+ register STAB *stab;
+ register STBP *stbp;
+ int len;
+ register char *namend;
+ HASH *stash;
+ char *sawquote = Nullch;
+ char *prevquote = Nullch;
+ bool global = FALSE;
+
+ if (isUPPER(*name)) {
+ if (*name > 'I') {
+ if (*name == 'S' && (
+ strEQ(name, "SIG") ||
+ strEQ(name, "STDIN") ||
+ strEQ(name, "STDOUT") ||
+ strEQ(name, "STDERR") ))
+ global = TRUE;
+ }
+ else if (*name > 'E') {
+ if (*name == 'I' && strEQ(name, "INC"))
+ global = TRUE;
+ }
+ else if (*name > 'A') {
+ if (*name == 'E' && strEQ(name, "ENV"))
+ global = TRUE;
+ }
+ else if (*name == 'A' && (
+ strEQ(name, "ARGV") ||
+ strEQ(name, "ARGVOUT") ))
+ global = TRUE;
+ }
+ for (namend = name; *namend; namend++) {
+ if (*namend == '\'' && namend[1])
+ prevquote = sawquote, sawquote = namend;
+ }
+ if (sawquote == name && name[1]) {
+ stash = defstash;
+ sawquote = Nullch;
+ name++;
+ }
+ else if (!isALPHA(*name) || global)
+ stash = defstash;
+ else if ((CMD*)curcmd == &compiling)
+ stash = curstash;
+ else
+ stash = curcmd->c_stash;
+ if (sawquote) {
+ char tmpbuf[256];
+ char *s, *d;
+
+ *sawquote = '\0';
+ /*SUPPRESS 560*/
+ if (s = prevquote) {
+ strncpy(tmpbuf,name,s-name+1);
+ d = tmpbuf+(s-name+1);
+ *d++ = '_';
+ strcpy(d,s+1);
+ }
+ else {
+ *tmpbuf = '_';
+ strcpy(tmpbuf+1,name);
+ }
+ stab = stabent(tmpbuf,TRUE);
+ if (!(stash = stab_xhash(stab)))
+ stash = stab_xhash(stab) = hnew(0);
+ if (!stash->tbl_name)
+ stash->tbl_name = savestr(name);
+ name = sawquote+1;
+ *sawquote = '\'';
+ }
+ len = namend - name;
+ stab = (STAB*)hfetch(stash,name,len,add);
+ if (stab == (STAB*)&str_undef)
+ return Nullstab;
+ if (stab->str_pok) {
+ stab->str_pok |= SP_MULTI;
+ return stab;
+ }
+ else {
+ if (stab->str_len)
+ Safefree(stab->str_ptr);
+ Newz(602,stbp, 1, STBP);
+ stab->str_ptr = stbp;
+ stab->str_len = stab->str_cur = sizeof(STBP);
+ stab->str_pok = 1;
+ strcpy(stab_magic(stab),"StB");
+ stab_val(stab) = Str_new(72,0);
+ stab_line(stab) = curcmd->c_line;
+ stab_estab(stab) = stab;
+ str_magic((STR*)stab, stab, '*', name, len);
+ stab_stash(stab) = stash;
+ if (isDIGIT(*name) && *name != '0') {
+ stab_flags(stab) = SF_VMAGIC;
+ str_magic(stab_val(stab), stab, 0, Nullch, 0);
+ }
+ if (add & 2)
+ stab->str_pok |= SP_MULTI;
+ return stab;
+ }
+}
+
+void
+stab_fullname(str,stab)
+STR *str;
+STAB *stab;
+{
+ HASH *tb = stab_stash(stab);
+
+ if (!tb)
+ return;
+ str_set(str,tb->tbl_name);
+ str_ncat(str,"'", 1);
+ str_scat(str,stab->str_magic);
+}
+
+void
+stab_efullname(str,stab)
+STR *str;
+STAB *stab;
+{
+ HASH *tb = stab_estash(stab);
+
+ if (!tb)
+ return;
+ str_set(str,tb->tbl_name);
+ str_ncat(str,"'", 1);
+ str_scat(str,stab_estab(stab)->str_magic);
+}
+
+STIO *
+stio_new()
+{
+ STIO *stio;
+
+ Newz(603,stio,1,STIO);
+ stio->page_len = 60;
+ return stio;
+}
+
+void
+stab_check(min,max)
+int min;
+register int max;
+{
+ register HENT *entry;
+ register int i;
+ register STAB *stab;
+
+ for (i = min; i <= max; i++) {
+ for (entry = defstash->tbl_array[i]; entry; entry = entry->hent_next) {
+ stab = (STAB*)entry->hent_val;
+ if (stab->str_pok & SP_MULTI)
+ continue;
+ curcmd->c_line = stab_line(stab);
+ warn("Possible typo: \"%s\"", stab_name(stab));
+ }
+ }
+}
+
+static int gensym = 0;
+
+STAB *
+genstab()
+{
+ (void)sprintf(tokenbuf,"_GEN_%d",gensym++);
+ return stabent(tokenbuf,TRUE);
+}
+
+/* hopefully this is only called on local symbol table entries */
+
+void
+stab_clear(stab)
+register STAB *stab;
+{
+ STIO *stio;
+ SUBR *sub;
+
+ if (!stab || !stab->str_ptr)
+ return;
+ afree(stab_xarray(stab));
+ stab_xarray(stab) = Null(ARRAY*);
+ (void)hfree(stab_xhash(stab), FALSE);
+ stab_xhash(stab) = Null(HASH*);
+ str_free(stab_val(stab));
+ stab_val(stab) = Nullstr;
+ /*SUPPRESS 560*/
+ if (stio = stab_io(stab)) {
+ do_close(stab,FALSE);
+ Safefree(stio->top_name);
+ Safefree(stio->fmt_name);
+ Safefree(stio);
+ }
+ /*SUPPRESS 560*/
+ if (sub = stab_sub(stab)) {
+ afree(sub->tosave);
+ cmd_free(sub->cmd);
+ }
+ Safefree(stab->str_ptr);
+ stab->str_ptr = Null(STBP*);
+ stab->str_len = 0;
+ stab->str_cur = 0;
+}
+
+#if defined(CRIPPLED_CC) && (defined(iAPX286) || defined(M_I286) || defined(I80286))
+#define MICROPORT
+#endif
+
+#ifdef MICROPORT /* Microport 2.4 hack */
+ARRAY *stab_array(stab)
+register STAB *stab;
+{
+ if (((STBP*)(stab->str_ptr))->stbp_array)
+ return ((STBP*)(stab->str_ptr))->stbp_array;
+ else
+ return ((STBP*)(aadd(stab)->str_ptr))->stbp_array;
+}
+
+HASH *stab_hash(stab)
+register STAB *stab;
+{
+ if (((STBP*)(stab->str_ptr))->stbp_hash)
+ return ((STBP*)(stab->str_ptr))->stbp_hash;
+ else
+ return ((STBP*)(hadd(stab)->str_ptr))->stbp_hash;
+}
+#endif /* Microport 2.4 hack */
diff --git a/gnu/usr.bin/perl/perl/stab.h b/gnu/usr.bin/perl/perl/stab.h
new file mode 100644
index 0000000..7bce082
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/stab.h
@@ -0,0 +1,145 @@
+/* $RCSfile: stab.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:39 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: stab.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:39 nate
+ * PERL!
+ *
+ * Revision 4.0.1.3 92/06/08 15:33:44 lwall
+ * patch20: fixed confusion between a *var's real name and its effective name
+ * patch20: ($<,$>) = ... didn't work on some architectures
+ *
+ * Revision 4.0.1.2 91/11/05 18:36:15 lwall
+ * patch11: length($x) was sometimes wrong for numeric $x
+ *
+ * Revision 4.0.1.1 91/06/07 11:56:35 lwall
+ * patch4: new copyright notice
+ * patch4: length($`), length($&), length($') now optimized to avoid string copy
+ *
+ * Revision 4.0 91/03/20 01:39:49 lwall
+ * 4.0 baseline.
+ *
+ */
+
+struct stabptrs {
+ char stbp_magic[4];
+ STR *stbp_val; /* scalar value */
+ struct stio *stbp_io; /* filehandle value */
+ FCMD *stbp_form; /* format value */
+ ARRAY *stbp_array; /* array value */
+ HASH *stbp_hash; /* associative array value */
+ STAB *stbp_stab; /* effective stab, if *glob */
+ SUBR *stbp_sub; /* subroutine value */
+ int stbp_lastexpr; /* used by nothing_in_common() */
+ line_t stbp_line; /* line first declared at (for -w) */
+ char stbp_flags;
+};
+
+#if defined(CRIPPLED_CC) && (defined(iAPX286) || defined(M_I286) || defined(I80286))
+#define MICROPORT
+#endif
+
+#define stab_magic(stab) (((STBP*)(stab->str_ptr))->stbp_magic)
+#define stab_val(stab) (((STBP*)(stab->str_ptr))->stbp_val)
+#define stab_io(stab) (((STBP*)(stab->str_ptr))->stbp_io)
+#define stab_form(stab) (((STBP*)(stab->str_ptr))->stbp_form)
+#define stab_xarray(stab) (((STBP*)(stab->str_ptr))->stbp_array)
+#ifdef MICROPORT /* Microport 2.4 hack */
+ARRAY *stab_array();
+#else
+#define stab_array(stab) (((STBP*)(stab->str_ptr))->stbp_array ? \
+ ((STBP*)(stab->str_ptr))->stbp_array : \
+ ((STBP*)(aadd(stab)->str_ptr))->stbp_array)
+#endif
+#define stab_xhash(stab) (((STBP*)(stab->str_ptr))->stbp_hash)
+#ifdef MICROPORT /* Microport 2.4 hack */
+HASH *stab_hash();
+#else
+#define stab_hash(stab) (((STBP*)(stab->str_ptr))->stbp_hash ? \
+ ((STBP*)(stab->str_ptr))->stbp_hash : \
+ ((STBP*)(hadd(stab)->str_ptr))->stbp_hash)
+#endif /* Microport 2.4 hack */
+#define stab_sub(stab) (((STBP*)(stab->str_ptr))->stbp_sub)
+#define stab_lastexpr(stab) (((STBP*)(stab->str_ptr))->stbp_lastexpr)
+#define stab_line(stab) (((STBP*)(stab->str_ptr))->stbp_line)
+#define stab_flags(stab) (((STBP*)(stab->str_ptr))->stbp_flags)
+
+#define stab_stab(stab) (stab->str_magic->str_u.str_stab)
+#define stab_estab(stab) (((STBP*)(stab->str_ptr))->stbp_stab)
+
+#define stab_name(stab) (stab->str_magic->str_ptr)
+#define stab_ename(stab) stab_name(stab_estab(stab))
+
+#define stab_stash(stab) (stab->str_magic->str_u.str_stash)
+#define stab_estash(stab) stab_stash(stab_estab(stab))
+
+#define SF_VMAGIC 1 /* call routine to dereference STR val */
+#define SF_MULTI 2 /* seen more than once */
+
+struct stio {
+ FILE *ifp; /* ifp and ofp are normally the same */
+ FILE *ofp; /* but sockets need separate streams */
+#ifdef HAS_READDIR
+ DIR *dirp; /* for opendir, readdir, etc */
+#endif
+ long lines; /* $. */
+ long page; /* $% */
+ long page_len; /* $= */
+ long lines_left; /* $- */
+ char *top_name; /* $^ */
+ STAB *top_stab; /* $^ */
+ char *fmt_name; /* $~ */
+ STAB *fmt_stab; /* $~ */
+ short subprocess; /* -| or |- */
+ char type;
+ char flags;
+};
+
+#define IOF_ARGV 1 /* this fp iterates over ARGV */
+#define IOF_START 2 /* check for null ARGV and substitute '-' */
+#define IOF_FLUSH 4 /* this fp wants a flush after write op */
+
+struct sub {
+ CMD *cmd;
+ int (*usersub)();
+ int userindex;
+ STAB *filestab;
+ long depth; /* >= 2 indicates recursive call */
+ ARRAY *tosave;
+};
+
+#define Nullstab Null(STAB*)
+
+STRLEN stab_len();
+
+#define STAB_STR(s) (tmpstab = (s), stab_flags(tmpstab) & SF_VMAGIC ? stab_str(stab_val(tmpstab)->str_magic) : stab_val(tmpstab))
+#define STAB_LEN(s) (tmpstab = (s), stab_flags(tmpstab) & SF_VMAGIC ? stab_len(stab_val(tmpstab)->str_magic) : str_len(stab_val(tmpstab)))
+#define STAB_GET(s) (tmpstab = (s), str_get(stab_flags(tmpstab) & SF_VMAGIC ? stab_str(tmpstab->str_magic) : stab_val(tmpstab)))
+#define STAB_GNUM(s) (tmpstab = (s), str_gnum(stab_flags(tmpstab) & SF_VMAGIC ? stab_str(tmpstab->str_magic) : stab_val(tmpstab)))
+
+EXT STAB *tmpstab;
+
+EXT STAB *stab_index[128];
+
+EXT unsigned short statusvalue;
+
+EXT int delaymagic INIT(0);
+#define DM_UID 0x003
+#define DM_RUID 0x001
+#define DM_EUID 0x002
+#define DM_GID 0x030
+#define DM_RGID 0x010
+#define DM_EGID 0x020
+#define DM_DELAY 0x100
+
+STAB *aadd();
+STAB *hadd();
+STAB *fstab();
+void stabset();
+void stab_fullname();
+void stab_efullname();
+void stab_check();
diff --git a/gnu/usr.bin/perl/perl/str.c b/gnu/usr.bin/perl/perl/str.c
new file mode 100644
index 0000000..f034292
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/str.c
@@ -0,0 +1,1599 @@
+/* $RCSfile: str.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:39 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: str.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:39 nate
+ * PERL!
+ *
+ * Revision 4.0.1.7 1993/02/05 19:43:47 lwall
+ * patch36: the non-std stdio input code wasn't null-proof
+ *
+ * Revision 4.0.1.6 92/06/11 21:14:21 lwall
+ * patch34: quotes containing subscripts containing variables didn't parse right
+ *
+ * Revision 4.0.1.5 92/06/08 15:40:43 lwall
+ * patch20: removed implicit int declarations on functions
+ * patch20: Perl now distinguishes overlapped copies from non-overlapped
+ * patch20: paragraph mode now skips extra newlines automatically
+ * patch20: fixed memory leak in doube-quote interpretation
+ * patch20: made /\$$foo/ look for literal '$foo'
+ * patch20: "$var{$foo'bar}" didn't scan subscript correctly
+ * patch20: a splice on non-existent array elements could dump core
+ * patch20: running taintperl explicitly now does checks even if $< == $>
+ *
+ * Revision 4.0.1.4 91/11/05 18:40:51 lwall
+ * patch11: $foo .= <BAR> could overrun malloced memory
+ * patch11: \$ didn't always make it through double-quoter to regexp routines
+ * patch11: prepared for ctype implementations that don't define isascii()
+ *
+ * Revision 4.0.1.3 91/06/10 01:27:54 lwall
+ * patch10: $) and $| incorrectly handled in run-time patterns
+ *
+ * Revision 4.0.1.2 91/06/07 11:58:13 lwall
+ * patch4: new copyright notice
+ * patch4: taint check on undefined string could cause core dump
+ *
+ * Revision 4.0.1.1 91/04/12 09:15:30 lwall
+ * patch1: fixed undefined environ problem
+ * patch1: substr($ENV{"PATH"},0,0) = "/foo:" didn't modify environment
+ * patch1: $foo .= <BAR> could cause core dump for certain lengths of $foo
+ *
+ * Revision 4.0 91/03/20 01:39:55 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "perly.h"
+
+static void ucase();
+static void lcase();
+
+#ifndef str_get
+char *
+str_get(str)
+STR *str;
+{
+#ifdef TAINT
+ tainted |= str->str_tainted;
+#endif
+ return str->str_pok ? str->str_ptr : str_2ptr(str);
+}
+#endif
+
+/* dlb ... guess we have a "crippled cc".
+ * dlb the following functions are usually macros.
+ */
+#ifndef str_true
+int
+str_true(Str)
+STR *Str;
+{
+ if (Str->str_pok) {
+ if (*Str->str_ptr > '0' ||
+ Str->str_cur > 1 ||
+ (Str->str_cur && *Str->str_ptr != '0'))
+ return 1;
+ return 0;
+ }
+ if (Str->str_nok)
+ return (Str->str_u.str_nval != 0.0);
+ return 0;
+}
+#endif /* str_true */
+
+#ifndef str_gnum
+double str_gnum(Str)
+STR *Str;
+{
+#ifdef TAINT
+ tainted |= Str->str_tainted;
+#endif /* TAINT*/
+ if (Str->str_nok)
+ return Str->str_u.str_nval;
+ return str_2num(Str);
+}
+#endif /* str_gnum */
+/* dlb ... end of crutch */
+
+char *
+str_grow(str,newlen)
+register STR *str;
+#ifndef DOSISH
+register int newlen;
+#else
+unsigned long newlen;
+#endif
+{
+ register char *s = str->str_ptr;
+
+#ifdef MSDOS
+ if (newlen >= 0x10000) {
+ fprintf(stderr, "Allocation too large: %lx\n", newlen);
+ exit(1);
+ }
+#endif /* MSDOS */
+ if (str->str_state == SS_INCR) { /* data before str_ptr? */
+ str->str_len += str->str_u.str_useful;
+ str->str_ptr -= str->str_u.str_useful;
+ str->str_u.str_useful = 0L;
+ Move(s, str->str_ptr, str->str_cur+1, char);
+ s = str->str_ptr;
+ str->str_state = SS_NORM; /* normal again */
+ if (newlen > str->str_len)
+ newlen += 10 * (newlen - str->str_cur); /* avoid copy each time */
+ }
+ if (newlen > str->str_len) { /* need more room? */
+ if (str->str_len)
+ Renew(s,newlen,char);
+ else
+ New(703,s,newlen,char);
+ str->str_ptr = s;
+ str->str_len = newlen;
+ }
+ return s;
+}
+
+void
+str_numset(str,num)
+register STR *str;
+double num;
+{
+ if (str->str_pok) {
+ str->str_pok = 0; /* invalidate pointer */
+ if (str->str_state == SS_INCR)
+ Str_Grow(str,0);
+ }
+ str->str_u.str_nval = num;
+ str->str_state = SS_NORM;
+ str->str_nok = 1; /* validate number */
+#ifdef TAINT
+ str->str_tainted = tainted;
+#endif
+}
+
+char *
+str_2ptr(str)
+register STR *str;
+{
+ register char *s;
+ int olderrno;
+
+ if (!str)
+ return "";
+ if (str->str_nok) {
+ STR_GROW(str, 30);
+ s = str->str_ptr;
+ olderrno = errno; /* some Xenix systems wipe out errno here */
+#if defined(scs) && defined(ns32000)
+ gcvt(str->str_u.str_nval,20,s);
+#else
+#ifdef apollo
+ if (str->str_u.str_nval == 0.0)
+ (void)strcpy(s,"0");
+ else
+#endif /*apollo*/
+ (void)sprintf(s,"%.20g",str->str_u.str_nval);
+#endif /*scs*/
+ errno = olderrno;
+ while (*s) s++;
+#ifdef hcx
+ if (s[-1] == '.')
+ s--;
+#endif
+ }
+ else {
+ if (str == &str_undef)
+ return No;
+ if (dowarn)
+ warn("Use of uninitialized variable");
+ STR_GROW(str, 30);
+ s = str->str_ptr;
+ }
+ *s = '\0';
+ str->str_cur = s - str->str_ptr;
+ str->str_pok = 1;
+#ifdef DEBUGGING
+ if (debug & 32)
+ fprintf(stderr,"0x%lx ptr(%s)\n",str,str->str_ptr);
+#endif
+ return str->str_ptr;
+}
+
+double
+str_2num(str)
+register STR *str;
+{
+ if (!str)
+ return 0.0;
+ if (str->str_state == SS_INCR)
+ Str_Grow(str,0); /* just force copy down */
+ str->str_state = SS_NORM;
+ if (str->str_len && str->str_pok)
+ str->str_u.str_nval = atof(str->str_ptr);
+ else {
+ if (str == &str_undef)
+ return 0.0;
+ if (dowarn)
+ warn("Use of uninitialized variable");
+ str->str_u.str_nval = 0.0;
+ }
+ str->str_nok = 1;
+#ifdef DEBUGGING
+ if (debug & 32)
+ fprintf(stderr,"0x%lx num(%g)\n",str,str->str_u.str_nval);
+#endif
+ return str->str_u.str_nval;
+}
+
+/* Note: str_sset() should not be called with a source string that needs
+ * be reused, since it may destroy the source string if it is marked
+ * as temporary.
+ */
+
+void
+str_sset(dstr,sstr)
+STR *dstr;
+register STR *sstr;
+{
+#ifdef TAINT
+ if (sstr)
+ tainted |= sstr->str_tainted;
+#endif
+ if (sstr == dstr || dstr == &str_undef)
+ return;
+ if (!sstr)
+ dstr->str_pok = dstr->str_nok = 0;
+ else if (sstr->str_pok) {
+
+ /*
+ * Check to see if we can just swipe the string. If so, it's a
+ * possible small lose on short strings, but a big win on long ones.
+ * It might even be a win on short strings if dstr->str_ptr
+ * has to be allocated and sstr->str_ptr has to be freed.
+ */
+
+ if (sstr->str_pok & SP_TEMP) { /* slated for free anyway? */
+ if (dstr->str_ptr) {
+ if (dstr->str_state == SS_INCR)
+ dstr->str_ptr -= dstr->str_u.str_useful;
+ Safefree(dstr->str_ptr);
+ }
+ dstr->str_ptr = sstr->str_ptr;
+ dstr->str_len = sstr->str_len;
+ dstr->str_cur = sstr->str_cur;
+ dstr->str_state = sstr->str_state;
+ dstr->str_pok = sstr->str_pok & ~SP_TEMP;
+#ifdef TAINT
+ dstr->str_tainted = sstr->str_tainted;
+#endif
+ sstr->str_ptr = Nullch;
+ sstr->str_len = 0;
+ sstr->str_pok = 0; /* wipe out any weird flags */
+ sstr->str_state = 0; /* so sstr frees uneventfully */
+ }
+ else { /* have to copy actual string */
+ if (dstr->str_ptr) {
+ if (dstr->str_state == SS_INCR) {
+ Str_Grow(dstr,0);
+ }
+ }
+ str_nset(dstr,sstr->str_ptr,sstr->str_cur);
+ }
+ /*SUPPRESS 560*/
+ if (dstr->str_nok = sstr->str_nok)
+ dstr->str_u.str_nval = sstr->str_u.str_nval;
+ else {
+#ifdef STRUCTCOPY
+ dstr->str_u = sstr->str_u;
+#else
+ dstr->str_u.str_nval = sstr->str_u.str_nval;
+#endif
+ if (dstr->str_cur == sizeof(STBP)) {
+ char *tmps = dstr->str_ptr;
+
+ if (*tmps == 'S' && bcmp(tmps,"StB",4) == 0) {
+ if (dstr->str_magic && dstr->str_magic->str_rare == 'X') {
+ str_free(dstr->str_magic);
+ dstr->str_magic = Nullstr;
+ }
+ if (!dstr->str_magic) {
+ dstr->str_magic = str_smake(sstr->str_magic);
+ dstr->str_magic->str_rare = 'X';
+ }
+ }
+ }
+ }
+ }
+ else if (sstr->str_nok)
+ str_numset(dstr,sstr->str_u.str_nval);
+ else {
+ if (dstr->str_state == SS_INCR)
+ Str_Grow(dstr,0); /* just force copy down */
+
+#ifdef STRUCTCOPY
+ dstr->str_u = sstr->str_u;
+#else
+ dstr->str_u.str_nval = sstr->str_u.str_nval;
+#endif
+ dstr->str_pok = dstr->str_nok = 0;
+ }
+}
+
+void
+str_nset(str,ptr,len)
+register STR *str;
+register char *ptr;
+register STRLEN len;
+{
+ if (str == &str_undef)
+ return;
+ STR_GROW(str, len + 1);
+ if (ptr)
+ Move(ptr,str->str_ptr,len,char);
+ str->str_cur = len;
+ *(str->str_ptr+str->str_cur) = '\0';
+ str->str_nok = 0; /* invalidate number */
+ str->str_pok = 1; /* validate pointer */
+#ifdef TAINT
+ str->str_tainted = tainted;
+#endif
+}
+
+void
+str_set(str,ptr)
+register STR *str;
+register char *ptr;
+{
+ register STRLEN len;
+
+ if (str == &str_undef)
+ return;
+ if (!ptr)
+ ptr = "";
+ len = strlen(ptr);
+ STR_GROW(str, len + 1);
+ Move(ptr,str->str_ptr,len+1,char);
+ str->str_cur = len;
+ str->str_nok = 0; /* invalidate number */
+ str->str_pok = 1; /* validate pointer */
+#ifdef TAINT
+ str->str_tainted = tainted;
+#endif
+}
+
+void
+str_chop(str,ptr) /* like set but assuming ptr is in str */
+register STR *str;
+register char *ptr;
+{
+ register STRLEN delta;
+
+ if (!ptr || !(str->str_pok))
+ return;
+ delta = ptr - str->str_ptr;
+ str->str_len -= delta;
+ str->str_cur -= delta;
+ str->str_ptr += delta;
+ if (str->str_state == SS_INCR)
+ str->str_u.str_useful += delta;
+ else {
+ str->str_u.str_useful = delta;
+ str->str_state = SS_INCR;
+ }
+ str->str_nok = 0; /* invalidate number */
+ str->str_pok = 1; /* validate pointer (and unstudy str) */
+}
+
+void
+str_ncat(str,ptr,len)
+register STR *str;
+register char *ptr;
+register STRLEN len;
+{
+ if (str == &str_undef)
+ return;
+ if (!(str->str_pok))
+ (void)str_2ptr(str);
+ STR_GROW(str, str->str_cur + len + 1);
+ Move(ptr,str->str_ptr+str->str_cur,len,char);
+ str->str_cur += len;
+ *(str->str_ptr+str->str_cur) = '\0';
+ str->str_nok = 0; /* invalidate number */
+ str->str_pok = 1; /* validate pointer */
+#ifdef TAINT
+ str->str_tainted |= tainted;
+#endif
+}
+
+void
+str_scat(dstr,sstr)
+STR *dstr;
+register STR *sstr;
+{
+ if (!sstr)
+ return;
+#ifdef TAINT
+ tainted |= sstr->str_tainted;
+#endif
+ if (!(sstr->str_pok))
+ (void)str_2ptr(sstr);
+ if (sstr)
+ str_ncat(dstr,sstr->str_ptr,sstr->str_cur);
+}
+
+void
+str_cat(str,ptr)
+register STR *str;
+register char *ptr;
+{
+ register STRLEN len;
+
+ if (str == &str_undef)
+ return;
+ if (!ptr)
+ return;
+ if (!(str->str_pok))
+ (void)str_2ptr(str);
+ len = strlen(ptr);
+ STR_GROW(str, str->str_cur + len + 1);
+ Move(ptr,str->str_ptr+str->str_cur,len+1,char);
+ str->str_cur += len;
+ str->str_nok = 0; /* invalidate number */
+ str->str_pok = 1; /* validate pointer */
+#ifdef TAINT
+ str->str_tainted |= tainted;
+#endif
+}
+
+char *
+str_append_till(str,from,fromend,delim,keeplist)
+register STR *str;
+register char *from;
+register char *fromend;
+register int delim;
+char *keeplist;
+{
+ register char *to;
+ register STRLEN len;
+
+ if (str == &str_undef)
+ return Nullch;
+ if (!from)
+ return Nullch;
+ len = fromend - from;
+ STR_GROW(str, str->str_cur + len + 1);
+ str->str_nok = 0; /* invalidate number */
+ str->str_pok = 1; /* validate pointer */
+ to = str->str_ptr+str->str_cur;
+ for (; from < fromend; from++,to++) {
+ if (*from == '\\' && from+1 < fromend && delim != '\\') {
+ if (!keeplist) {
+ if (from[1] == delim || from[1] == '\\')
+ from++;
+ else
+ *to++ = *from++;
+ }
+ else if (from[1] && index(keeplist,from[1]))
+ *to++ = *from++;
+ else
+ from++;
+ }
+ else if (*from == delim)
+ break;
+ *to = *from;
+ }
+ *to = '\0';
+ str->str_cur = to - str->str_ptr;
+ return from;
+}
+
+STR *
+#ifdef LEAKTEST
+str_new(x,len)
+int x;
+#else
+str_new(len)
+#endif
+STRLEN len;
+{
+ register STR *str;
+
+ if (freestrroot) {
+ str = freestrroot;
+ freestrroot = str->str_magic;
+ str->str_magic = Nullstr;
+ str->str_state = SS_NORM;
+ }
+ else {
+ Newz(700+x,str,1,STR);
+ }
+ if (len)
+ STR_GROW(str, len + 1);
+ return str;
+}
+
+void
+str_magic(str, stab, how, name, namlen)
+register STR *str;
+STAB *stab;
+int how;
+char *name;
+STRLEN namlen;
+{
+ if (str == &str_undef || str->str_magic)
+ return;
+ str->str_magic = Str_new(75,namlen);
+ str = str->str_magic;
+ str->str_u.str_stab = stab;
+ str->str_rare = how;
+ if (name)
+ str_nset(str,name,namlen);
+}
+
+void
+str_insert(bigstr,offset,len,little,littlelen)
+STR *bigstr;
+STRLEN offset;
+STRLEN len;
+char *little;
+STRLEN littlelen;
+{
+ register char *big;
+ register char *mid;
+ register char *midend;
+ register char *bigend;
+ register int i;
+
+ if (bigstr == &str_undef)
+ return;
+ bigstr->str_nok = 0;
+ bigstr->str_pok = SP_VALID; /* disable possible screamer */
+
+ i = littlelen - len;
+ if (i > 0) { /* string might grow */
+ STR_GROW(bigstr, bigstr->str_cur + i + 1);
+ big = bigstr->str_ptr;
+ mid = big + offset + len;
+ midend = bigend = big + bigstr->str_cur;
+ bigend += i;
+ *bigend = '\0';
+ while (midend > mid) /* shove everything down */
+ *--bigend = *--midend;
+ Move(little,big+offset,littlelen,char);
+ bigstr->str_cur += i;
+ STABSET(bigstr);
+ return;
+ }
+ else if (i == 0) {
+ Move(little,bigstr->str_ptr+offset,len,char);
+ STABSET(bigstr);
+ return;
+ }
+
+ big = bigstr->str_ptr;
+ mid = big + offset;
+ midend = mid + len;
+ bigend = big + bigstr->str_cur;
+
+ if (midend > bigend)
+ fatal("panic: str_insert");
+
+ if (mid - big > bigend - midend) { /* faster to shorten from end */
+ if (littlelen) {
+ Move(little, mid, littlelen,char);
+ mid += littlelen;
+ }
+ i = bigend - midend;
+ if (i > 0) {
+ Move(midend, mid, i,char);
+ mid += i;
+ }
+ *mid = '\0';
+ bigstr->str_cur = mid - big;
+ }
+ /*SUPPRESS 560*/
+ else if (i = mid - big) { /* faster from front */
+ midend -= littlelen;
+ mid = midend;
+ str_chop(bigstr,midend-i);
+ big += i;
+ while (i--)
+ *--midend = *--big;
+ if (littlelen)
+ Move(little, mid, littlelen,char);
+ }
+ else if (littlelen) {
+ midend -= littlelen;
+ str_chop(bigstr,midend);
+ Move(little,midend,littlelen,char);
+ }
+ else {
+ str_chop(bigstr,midend);
+ }
+ STABSET(bigstr);
+}
+
+/* make str point to what nstr did */
+
+void
+str_replace(str,nstr)
+register STR *str;
+register STR *nstr;
+{
+ if (str == &str_undef)
+ return;
+ if (str->str_state == SS_INCR)
+ Str_Grow(str,0); /* just force copy down */
+ if (nstr->str_state == SS_INCR)
+ Str_Grow(nstr,0);
+ if (str->str_ptr)
+ Safefree(str->str_ptr);
+ str->str_ptr = nstr->str_ptr;
+ str->str_len = nstr->str_len;
+ str->str_cur = nstr->str_cur;
+ str->str_pok = nstr->str_pok;
+ str->str_nok = nstr->str_nok;
+#ifdef STRUCTCOPY
+ str->str_u = nstr->str_u;
+#else
+ str->str_u.str_nval = nstr->str_u.str_nval;
+#endif
+#ifdef TAINT
+ str->str_tainted = nstr->str_tainted;
+#endif
+ if (nstr->str_magic)
+ str_free(nstr->str_magic);
+ Safefree(nstr);
+}
+
+void
+str_free(str)
+register STR *str;
+{
+ if (!str || str == &str_undef)
+ return;
+ if (str->str_state) {
+ if (str->str_state == SS_FREE) /* already freed */
+ return;
+ if (str->str_state == SS_INCR && !(str->str_pok & 2)) {
+ str->str_ptr -= str->str_u.str_useful;
+ str->str_len += str->str_u.str_useful;
+ }
+ }
+ if (str->str_magic)
+ str_free(str->str_magic);
+ str->str_magic = freestrroot;
+#ifdef LEAKTEST
+ if (str->str_len) {
+ Safefree(str->str_ptr);
+ str->str_ptr = Nullch;
+ }
+ if ((str->str_pok & SP_INTRP) && str->str_u.str_args)
+ arg_free(str->str_u.str_args);
+ Safefree(str);
+#else /* LEAKTEST */
+ if (str->str_len) {
+ if (str->str_len > 127) { /* next user not likely to want more */
+ Safefree(str->str_ptr); /* so give it back to malloc */
+ str->str_ptr = Nullch;
+ str->str_len = 0;
+ }
+ else
+ str->str_ptr[0] = '\0';
+ }
+ if ((str->str_pok & SP_INTRP) && str->str_u.str_args)
+ arg_free(str->str_u.str_args);
+ str->str_cur = 0;
+ str->str_nok = 0;
+ str->str_pok = 0;
+ str->str_state = SS_FREE;
+#ifdef TAINT
+ str->str_tainted = 0;
+#endif
+ freestrroot = str;
+#endif /* LEAKTEST */
+}
+
+STRLEN
+str_len(str)
+register STR *str;
+{
+ if (!str)
+ return 0;
+ if (!(str->str_pok))
+ (void)str_2ptr(str);
+ if (str->str_ptr)
+ return str->str_cur;
+ else
+ return 0;
+}
+
+int
+str_eq(str1,str2)
+register STR *str1;
+register STR *str2;
+{
+ if (!str1 || str1 == &str_undef)
+ return (str2 == Nullstr || str2 == &str_undef || !str2->str_cur);
+ if (!str2 || str2 == &str_undef)
+ return !str1->str_cur;
+
+ if (!str1->str_pok)
+ (void)str_2ptr(str1);
+ if (!str2->str_pok)
+ (void)str_2ptr(str2);
+
+ if (str1->str_cur != str2->str_cur)
+ return 0;
+
+ return !bcmp(str1->str_ptr, str2->str_ptr, str1->str_cur);
+}
+
+int
+str_cmp(str1,str2)
+register STR *str1;
+register STR *str2;
+{
+ int retval;
+
+ if (!str1 || str1 == &str_undef)
+ return (str2 == Nullstr || str2 == &str_undef || !str2->str_cur)?0:-1;
+ if (!str2 || str2 == &str_undef)
+ return str1->str_cur != 0;
+
+ if (!str1->str_pok)
+ (void)str_2ptr(str1);
+ if (!str2->str_pok)
+ (void)str_2ptr(str2);
+
+ if (str1->str_cur < str2->str_cur) {
+ /*SUPPRESS 560*/
+ if (retval = memcmp(str1->str_ptr, str2->str_ptr, str1->str_cur))
+ return retval < 0 ? -1 : 1;
+ else
+ return -1;
+ }
+ /*SUPPRESS 560*/
+ else if (retval = memcmp(str1->str_ptr, str2->str_ptr, str2->str_cur))
+ return retval < 0 ? -1 : 1;
+ else if (str1->str_cur == str2->str_cur)
+ return 0;
+ else
+ return 1;
+}
+
+char *
+str_gets(str,fp,append)
+register STR *str;
+register FILE *fp;
+int append;
+{
+ register char *bp; /* we're going to steal some values */
+ register int cnt; /* from the stdio struct and put EVERYTHING */
+ register STDCHAR *ptr; /* in the innermost loop into registers */
+ register int newline = rschar;/* (assuming >= 6 registers) */
+ int i;
+ STRLEN bpx;
+ int shortbuffered;
+
+ if (str == &str_undef)
+ return Nullch;
+ if (rspara) { /* have to do this both before and after */
+ do { /* to make sure file boundaries work right */
+ i = getc(fp);
+ if (i != '\n') {
+ ungetc(i,fp);
+ break;
+ }
+ } while (i != EOF);
+ }
+#ifdef STDSTDIO /* Here is some breathtakingly efficient cheating */
+ cnt = fp->_cnt; /* get count into register */
+ str->str_nok = 0; /* invalidate number */
+ str->str_pok = 1; /* validate pointer */
+ if (str->str_len - append <= cnt + 1) { /* make sure we have the room */
+ if (cnt > 80 && str->str_len > append) {
+ shortbuffered = cnt - str->str_len + append + 1;
+ cnt -= shortbuffered;
+ }
+ else {
+ shortbuffered = 0;
+ STR_GROW(str, append+cnt+2);/* (remembering cnt can be -1) */
+ }
+ }
+ else
+ shortbuffered = 0;
+ bp = str->str_ptr + append; /* move these two too to registers */
+ ptr = fp->_ptr;
+ for (;;) {
+ screamer:
+ while (--cnt >= 0) { /* this */ /* eat */
+ if ((*bp++ = *ptr++) == newline) /* really */ /* dust */
+ goto thats_all_folks; /* screams */ /* sed :-) */
+ }
+
+ if (shortbuffered) { /* oh well, must extend */
+ cnt = shortbuffered;
+ shortbuffered = 0;
+ bpx = bp - str->str_ptr; /* prepare for possible relocation */
+ str->str_cur = bpx;
+ STR_GROW(str, str->str_len + append + cnt + 2);
+ bp = str->str_ptr + bpx; /* reconstitute our pointer */
+ continue;
+ }
+
+ fp->_cnt = cnt; /* deregisterize cnt and ptr */
+ fp->_ptr = ptr;
+ i = _filbuf(fp); /* get more characters */
+ cnt = fp->_cnt;
+ ptr = fp->_ptr; /* reregisterize cnt and ptr */
+
+ bpx = bp - str->str_ptr; /* prepare for possible relocation */
+ str->str_cur = bpx;
+ STR_GROW(str, bpx + cnt + 2);
+ bp = str->str_ptr + bpx; /* reconstitute our pointer */
+
+ if (i == newline) { /* all done for now? */
+ *bp++ = i;
+ goto thats_all_folks;
+ }
+ else if (i == EOF) /* all done for ever? */
+ goto thats_really_all_folks;
+ *bp++ = i; /* now go back to screaming loop */
+ }
+
+thats_all_folks:
+ if (rslen > 1 && (bp - str->str_ptr < rslen || bcmp(bp - rslen, rs, rslen)))
+ goto screamer; /* go back to the fray */
+thats_really_all_folks:
+ if (shortbuffered)
+ cnt += shortbuffered;
+ fp->_cnt = cnt; /* put these back or we're in trouble */
+ fp->_ptr = ptr;
+ *bp = '\0';
+ str->str_cur = bp - str->str_ptr; /* set length */
+
+#else /* !STDSTDIO */ /* The big, slow, and stupid way */
+
+ {
+ static char buf[8192];
+ char * bpe = buf + sizeof(buf) - 3;
+
+screamer:
+ bp = buf;
+ while ((i = getc(fp)) != EOF && (*bp++ = i) != newline && bp < bpe) ;
+
+ if (append)
+ str_ncat(str, buf, bp - buf);
+ else
+ str_nset(str, buf, bp - buf);
+ if (i != EOF /* joy */
+ &&
+ (i != newline
+ ||
+ (rslen > 1
+ &&
+ (str->str_cur < rslen
+ ||
+ bcmp(str->str_ptr + str->str_cur - rslen, rs, rslen)
+ )
+ )
+ )
+ )
+ {
+ append = -1;
+ goto screamer;
+ }
+ }
+
+#endif /* STDSTDIO */
+
+ if (rspara) {
+ while (i != EOF) {
+ i = getc(fp);
+ if (i != '\n') {
+ ungetc(i,fp);
+ break;
+ }
+ }
+ }
+ return str->str_cur - append ? str->str_ptr : Nullch;
+}
+
+ARG *
+parselist(str)
+STR *str;
+{
+ register CMD *cmd;
+ register ARG *arg;
+ CMD *oldcurcmd = curcmd;
+ int oldperldb = perldb;
+ int retval;
+
+ perldb = 0;
+ str_sset(linestr,str);
+ in_eval++;
+ oldoldbufptr = oldbufptr = bufptr = str_get(linestr);
+ bufend = bufptr + linestr->str_cur;
+ if (++loop_ptr >= loop_max) {
+ loop_max += 128;
+ Renew(loop_stack, loop_max, struct loop);
+ }
+ loop_stack[loop_ptr].loop_label = "_EVAL_";
+ loop_stack[loop_ptr].loop_sp = 0;
+#ifdef DEBUGGING
+ if (debug & 4) {
+ deb("(Pushing label #%d _EVAL_)\n", loop_ptr);
+ }
+#endif
+ if (setjmp(loop_stack[loop_ptr].loop_env)) {
+ in_eval--;
+ loop_ptr--;
+ perldb = oldperldb;
+ fatal("%s\n",stab_val(stabent("@",TRUE))->str_ptr);
+ }
+#ifdef DEBUGGING
+ if (debug & 4) {
+ char *tmps = loop_stack[loop_ptr].loop_label;
+ deb("(Popping label #%d %s)\n",loop_ptr,
+ tmps ? tmps : "" );
+ }
+#endif
+ loop_ptr--;
+ error_count = 0;
+ curcmd = &compiling;
+ curcmd->c_line = oldcurcmd->c_line;
+ retval = yyparse();
+ curcmd = oldcurcmd;
+ perldb = oldperldb;
+ in_eval--;
+ if (retval || error_count)
+ fatal("Invalid component in string or format");
+ cmd = eval_root;
+ arg = cmd->c_expr;
+ if (cmd->c_type != C_EXPR || cmd->c_next || arg->arg_type != O_LIST)
+ fatal("panic: error in parselist %d %x %d", cmd->c_type,
+ cmd->c_next, arg ? arg->arg_type : -1);
+ cmd->c_expr = Nullarg;
+ cmd_free(cmd);
+ eval_root = Nullcmd;
+ return arg;
+}
+
+void
+intrpcompile(src)
+STR *src;
+{
+ register char *s = str_get(src);
+ register char *send = s + src->str_cur;
+ register STR *str;
+ register char *t;
+ STR *toparse;
+ STRLEN len;
+ register int brackets;
+ register char *d;
+ STAB *stab;
+ char *checkpoint;
+ int sawcase = 0;
+
+ toparse = Str_new(76,0);
+ str = Str_new(77,0);
+
+ str_nset(str,"",0);
+ str_nset(toparse,"",0);
+ t = s;
+ while (s < send) {
+ if (*s == '\\' && s[1] && index("$@[{\\]}lLuUE",s[1])) {
+ str_ncat(str, t, s - t);
+ ++s;
+ if (isALPHA(*s)) {
+ str_ncat(str, "$c", 2);
+ sawcase = (*s != 'E');
+ }
+ else {
+ if (*nointrp) { /* in a regular expression */
+ if (*s == '@') /* always strip \@ */ /*SUPPRESS 530*/
+ ;
+ else /* don't strip \\, \[, \{ etc. */
+ str_ncat(str,s-1,1);
+ }
+ str_ncat(str, "$b", 2);
+ }
+ str_ncat(str, s, 1);
+ ++s;
+ t = s;
+ }
+ else if (*s == '$' && s+1 < send && *nointrp && index(nointrp,s[1])) {
+ str_ncat(str, t, s - t);
+ str_ncat(str, "$b", 2);
+ str_ncat(str, s, 2);
+ s += 2;
+ t = s;
+ }
+ else if ((*s == '@' || *s == '$') && s+1 < send) {
+ str_ncat(str,t,s-t);
+ t = s;
+ if (*s == '$' && s[1] == '#' && (isALPHA(s[2]) || s[2] == '_'))
+ s++;
+ s = scanident(s,send,tokenbuf);
+ if (*t == '@' &&
+ (!(stab = stabent(tokenbuf,FALSE)) ||
+ (*s == '{' ? !stab_xhash(stab) : !stab_xarray(stab)) )) {
+ str_ncat(str,"@",1);
+ s = ++t;
+ continue; /* grandfather @ from old scripts */
+ }
+ str_ncat(str,"$a",2);
+ str_ncat(toparse,",",1);
+ if (t[1] != '{' && (*s == '[' || *s == '{' /* }} */ ) &&
+ (stab = stabent(tokenbuf,FALSE)) &&
+ ((*s == '[') ? (stab_xarray(stab) != 0) : (stab_xhash(stab) != 0)) ) {
+ brackets = 0;
+ checkpoint = s;
+ do {
+ switch (*s) {
+ case '[':
+ brackets++;
+ break;
+ case '{':
+ brackets++;
+ break;
+ case ']':
+ brackets--;
+ break;
+ case '}':
+ brackets--;
+ break;
+ case '$':
+ case '%':
+ case '@':
+ case '&':
+ case '*':
+ s = scanident(s,send,tokenbuf);
+ continue;
+ case '\'':
+ case '"':
+ /*SUPPRESS 68*/
+ s = cpytill(tokenbuf,s+1,send,*s,&len);
+ if (s >= send)
+ fatal("Unterminated string");
+ break;
+ }
+ s++;
+ } while (brackets > 0 && s < send);
+ if (s > send)
+ fatal("Unmatched brackets in string");
+ if (*nointrp) { /* we're in a regular expression */
+ d = checkpoint;
+ if (*d == '{' && s[-1] == '}') { /* maybe {n,m} */
+ ++d;
+ if (isDIGIT(*d)) { /* matches /^{\d,?\d*}$/ */
+ if (*++d == ',')
+ ++d;
+ while (isDIGIT(*d))
+ d++;
+ if (d == s - 1)
+ s = checkpoint; /* Is {n,m}! Backoff! */
+ }
+ }
+ else if (*d == '[' && s[-1] == ']') { /* char class? */
+ int weight = 2; /* let's weigh the evidence */
+ char seen[256];
+ unsigned char un_char = 0, last_un_char;
+
+ Zero(seen,256,char);
+ *--s = '\0';
+ if (d[1] == '^')
+ weight += 150;
+ else if (d[1] == '$')
+ weight -= 3;
+ if (isDIGIT(d[1])) {
+ if (d[2]) {
+ if (isDIGIT(d[2]) && !d[3])
+ weight -= 10;
+ }
+ else
+ weight -= 100;
+ }
+ for (d++; d < s; d++) {
+ last_un_char = un_char;
+ un_char = (unsigned char)*d;
+ switch (*d) {
+ case '&':
+ case '$':
+ weight -= seen[un_char] * 10;
+ if (isALNUM(d[1])) {
+ d = scanident(d,s,tokenbuf);
+ if (stabent(tokenbuf,FALSE))
+ weight -= 100;
+ else
+ weight -= 10;
+ }
+ else if (*d == '$' && d[1] &&
+ index("[#!%*<>()-=",d[1])) {
+ if (!d[2] || /*{*/ index("])} =",d[2]))
+ weight -= 10;
+ else
+ weight -= 1;
+ }
+ break;
+ case '\\':
+ un_char = 254;
+ if (d[1]) {
+ if (index("wds",d[1]))
+ weight += 100;
+ else if (seen['\''] || seen['"'])
+ weight += 1;
+ else if (index("rnftb",d[1]))
+ weight += 40;
+ else if (isDIGIT(d[1])) {
+ weight += 40;
+ while (d[1] && isDIGIT(d[1]))
+ d++;
+ }
+ }
+ else
+ weight += 100;
+ break;
+ case '-':
+ if (last_un_char < (unsigned char) d[1]
+ || d[1] == '\\') {
+ if (index("aA01! ",last_un_char))
+ weight += 30;
+ if (index("zZ79~",d[1]))
+ weight += 30;
+ }
+ else
+ weight -= 1;
+ default:
+ if (isALPHA(*d) && d[1] && isALPHA(d[1])) {
+ bufptr = d;
+ if (yylex() != WORD)
+ weight -= 150;
+ d = bufptr;
+ }
+ if (un_char == last_un_char + 1)
+ weight += 5;
+ weight -= seen[un_char];
+ break;
+ }
+ seen[un_char]++;
+ }
+#ifdef DEBUGGING
+ if (debug & 512)
+ fprintf(stderr,"[%s] weight %d\n",
+ checkpoint+1,weight);
+#endif
+ *s++ = ']';
+ if (weight >= 0) /* probably a character class */
+ s = checkpoint;
+ }
+ }
+ }
+ if (*t == '@')
+ str_ncat(toparse, "join($\",", 8);
+ if (t[1] == '{' && s[-1] == '}') {
+ str_ncat(toparse, t, 1);
+ str_ncat(toparse, t+2, s - t - 3);
+ }
+ else
+ str_ncat(toparse, t, s - t);
+ if (*t == '@')
+ str_ncat(toparse, ")", 1);
+ t = s;
+ }
+ else
+ s++;
+ }
+ str_ncat(str,t,s-t);
+ if (sawcase)
+ str_ncat(str, "$cE", 3);
+ if (toparse->str_ptr && *toparse->str_ptr == ',') {
+ *toparse->str_ptr = '(';
+ str_ncat(toparse,",$$);",5);
+ str->str_u.str_args = parselist(toparse);
+ str->str_u.str_args->arg_len--; /* ignore $$ reference */
+ }
+ else
+ str->str_u.str_args = Nullarg;
+ str_free(toparse);
+ str->str_pok |= SP_INTRP;
+ str->str_nok = 0;
+ str_replace(src,str);
+}
+
+STR *
+interp(str,src,sp)
+register STR *str;
+STR *src;
+int sp;
+{
+ register char *s;
+ register char *t;
+ register char *send;
+ register STR **elem;
+ int docase = 0;
+ int l = 0;
+ int u = 0;
+ int L = 0;
+ int U = 0;
+
+ if (str == &str_undef)
+ return Nullstr;
+ if (!(src->str_pok & SP_INTRP)) {
+ int oldsave = savestack->ary_fill;
+
+ (void)savehptr(&curstash);
+ curstash = curcmd->c_stash; /* so stabent knows right package */
+ intrpcompile(src);
+ restorelist(oldsave);
+ }
+ s = src->str_ptr; /* assumed valid since str_pok set */
+ t = s;
+ send = s + src->str_cur;
+
+ if (src->str_u.str_args) {
+ (void)eval(src->str_u.str_args,G_ARRAY,sp);
+ /* Assuming we have correct # of args */
+ elem = stack->ary_array + sp;
+ }
+
+ str_nset(str,"",0);
+ while (s < send) {
+ if (*s == '$' && s+1 < send) {
+ if (s-t > 0)
+ str_ncat(str,t,s-t);
+ switch(*++s) {
+ default:
+ fatal("panic: unknown interp cookie\n");
+ break;
+ case 'a':
+ str_scat(str,*++elem);
+ break;
+ case 'b':
+ str_ncat(str,++s,1);
+ break;
+ case 'c':
+ if (docase && str->str_cur >= docase) {
+ char *b = str->str_ptr + --docase;
+
+ if (L)
+ lcase(b, str->str_ptr + str->str_cur);
+ else if (U)
+ ucase(b, str->str_ptr + str->str_cur);
+
+ if (u) /* note that l & u are independent of L & U */
+ ucase(b, b+1);
+ else if (l)
+ lcase(b, b+1);
+ l = u = 0;
+ }
+ docase = str->str_cur + 1;
+ switch (*++s) {
+ case 'u':
+ u = 1;
+ l = 0;
+ break;
+ case 'U':
+ U = 1;
+ L = 0;
+ break;
+ case 'l':
+ l = 1;
+ u = 0;
+ break;
+ case 'L':
+ L = 1;
+ U = 0;
+ break;
+ case 'E':
+ docase = L = U = l = u = 0;
+ break;
+ }
+ break;
+ }
+ t = ++s;
+ }
+ else
+ s++;
+ }
+ if (s-t > 0)
+ str_ncat(str,t,s-t);
+ return str;
+}
+
+static void
+ucase(s,send)
+register char *s;
+register char *send;
+{
+ while (s < send) {
+ if (isLOWER(*s))
+ *s = toupper(*s);
+ s++;
+ }
+}
+
+static void
+lcase(s,send)
+register char *s;
+register char *send;
+{
+ while (s < send) {
+ if (isUPPER(*s))
+ *s = tolower(*s);
+ s++;
+ }
+}
+
+void
+str_inc(str)
+register STR *str;
+{
+ register char *d;
+
+ if (!str || str == &str_undef)
+ return;
+ if (str->str_nok) {
+ str->str_u.str_nval += 1.0;
+ str->str_pok = 0;
+ return;
+ }
+ if (!str->str_pok || !*str->str_ptr) {
+ str->str_u.str_nval = 1.0;
+ str->str_nok = 1;
+ str->str_pok = 0;
+ return;
+ }
+ d = str->str_ptr;
+ while (isALPHA(*d)) d++;
+ while (isDIGIT(*d)) d++;
+ if (*d) {
+ str_numset(str,atof(str->str_ptr) + 1.0); /* punt */
+ return;
+ }
+ d--;
+ while (d >= str->str_ptr) {
+ if (isDIGIT(*d)) {
+ if (++*d <= '9')
+ return;
+ *(d--) = '0';
+ }
+ else {
+ ++*d;
+ if (isALPHA(*d))
+ return;
+ *(d--) -= 'z' - 'a' + 1;
+ }
+ }
+ /* oh,oh, the number grew */
+ STR_GROW(str, str->str_cur + 2);
+ str->str_cur++;
+ for (d = str->str_ptr + str->str_cur; d > str->str_ptr; d--)
+ *d = d[-1];
+ if (isDIGIT(d[1]))
+ *d = '1';
+ else
+ *d = d[1];
+}
+
+void
+str_dec(str)
+register STR *str;
+{
+ if (!str || str == &str_undef)
+ return;
+ if (str->str_nok) {
+ str->str_u.str_nval -= 1.0;
+ str->str_pok = 0;
+ return;
+ }
+ if (!str->str_pok) {
+ str->str_u.str_nval = -1.0;
+ str->str_nok = 1;
+ return;
+ }
+ str_numset(str,atof(str->str_ptr) - 1.0);
+}
+
+/* Make a string that will exist for the duration of the expression
+ * evaluation. Actually, it may have to last longer than that, but
+ * hopefully cmd_exec won't free it until it has been assigned to a
+ * permanent location. */
+
+static long tmps_size = -1;
+
+STR *
+str_mortal(oldstr)
+STR *oldstr;
+{
+ register STR *str = Str_new(78,0);
+
+ str_sset(str,oldstr);
+ if (++tmps_max > tmps_size) {
+ tmps_size = tmps_max;
+ if (!(tmps_size & 127)) {
+ if (tmps_size)
+ Renew(tmps_list, tmps_size + 128, STR*);
+ else
+ New(702,tmps_list, 128, STR*);
+ }
+ }
+ tmps_list[tmps_max] = str;
+ if (str->str_pok)
+ str->str_pok |= SP_TEMP;
+ return str;
+}
+
+/* same thing without the copying */
+
+STR *
+str_2mortal(str)
+register STR *str;
+{
+ if (!str || str == &str_undef)
+ return str;
+ if (++tmps_max > tmps_size) {
+ tmps_size = tmps_max;
+ if (!(tmps_size & 127)) {
+ if (tmps_size)
+ Renew(tmps_list, tmps_size + 128, STR*);
+ else
+ New(704,tmps_list, 128, STR*);
+ }
+ }
+ tmps_list[tmps_max] = str;
+ if (str->str_pok)
+ str->str_pok |= SP_TEMP;
+ return str;
+}
+
+STR *
+str_make(s,len)
+char *s;
+STRLEN len;
+{
+ register STR *str = Str_new(79,0);
+
+ if (!len)
+ len = strlen(s);
+ str_nset(str,s,len);
+ return str;
+}
+
+STR *
+str_nmake(n)
+double n;
+{
+ register STR *str = Str_new(80,0);
+
+ str_numset(str,n);
+ return str;
+}
+
+/* make an exact duplicate of old */
+
+STR *
+str_smake(old)
+register STR *old;
+{
+ register STR *new = Str_new(81,0);
+
+ if (!old)
+ return Nullstr;
+ if (old->str_state == SS_FREE) {
+ warn("semi-panic: attempt to dup freed string");
+ return Nullstr;
+ }
+ if (old->str_state == SS_INCR && !(old->str_pok & 2))
+ Str_Grow(old,0);
+ if (new->str_ptr)
+ Safefree(new->str_ptr);
+ StructCopy(old,new,STR);
+ if (old->str_ptr) {
+ new->str_ptr = nsavestr(old->str_ptr,old->str_len);
+ new->str_pok &= ~SP_TEMP;
+ }
+ return new;
+}
+
+void
+str_reset(s,stash)
+register char *s;
+HASH *stash;
+{
+ register HENT *entry;
+ register STAB *stab;
+ register STR *str;
+ register int i;
+ register SPAT *spat;
+ register int max;
+
+ if (!*s) { /* reset ?? searches */
+ for (spat = stash->tbl_spatroot;
+ spat != Nullspat;
+ spat = spat->spat_next) {
+ spat->spat_flags &= ~SPAT_USED;
+ }
+ return;
+ }
+
+ /* reset variables */
+
+ if (!stash->tbl_array)
+ return;
+ while (*s) {
+ i = *s;
+ if (s[1] == '-') {
+ s += 2;
+ }
+ max = *s++;
+ for ( ; i <= max; i++) {
+ for (entry = stash->tbl_array[i];
+ entry;
+ entry = entry->hent_next) {
+ stab = (STAB*)entry->hent_val;
+ str = stab_val(stab);
+ str->str_cur = 0;
+ str->str_nok = 0;
+#ifdef TAINT
+ str->str_tainted = tainted;
+#endif
+ if (str->str_ptr != Nullch)
+ str->str_ptr[0] = '\0';
+ if (stab_xarray(stab)) {
+ aclear(stab_xarray(stab));
+ }
+ if (stab_xhash(stab)) {
+ hclear(stab_xhash(stab), FALSE);
+ if (stab == envstab)
+ environ[0] = Nullch;
+ }
+ }
+ }
+ }
+}
+
+#ifdef TAINT
+void
+taintproper(s)
+char *s;
+{
+#ifdef DEBUGGING
+ if (debug & 2048)
+ fprintf(stderr,"%s %d %d %d\n",s,tainted,uid, euid);
+#endif
+ if (tainted && (!euid || euid != uid || egid != gid || taintanyway)) {
+ if (!unsafe)
+ fatal("%s", s);
+ else if (dowarn)
+ warn("%s", s);
+ }
+}
+
+void
+taintenv()
+{
+ register STR *envstr;
+
+ envstr = hfetch(stab_hash(envstab),"PATH",4,FALSE);
+ if (envstr == &str_undef || envstr->str_tainted) {
+ tainted = 1;
+ if (envstr->str_tainted == 2)
+ taintproper("Insecure directory in PATH");
+ else
+ taintproper("Insecure PATH");
+ }
+ envstr = hfetch(stab_hash(envstab),"IFS",3,FALSE);
+ if (envstr != &str_undef && envstr->str_tainted) {
+ tainted = 1;
+ taintproper("Insecure IFS");
+ }
+}
+#endif /* TAINT */
diff --git a/gnu/usr.bin/perl/perl/str.h b/gnu/usr.bin/perl/perl/str.h
new file mode 100644
index 0000000..7eb4b69
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/str.h
@@ -0,0 +1,171 @@
+/* $RCSfile: str.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:39 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: str.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:39 nate
+ * PERL!
+ *
+ * Revision 4.0.1.4 92/06/08 15:41:45 lwall
+ * patch20: fixed confusion between a *var's real name and its effective name
+ * patch20: removed implicit int declarations on functions
+ *
+ * Revision 4.0.1.3 91/11/05 18:41:47 lwall
+ * patch11: random cleanup
+ * patch11: solitary subroutine references no longer trigger typo warnings
+ *
+ * Revision 4.0.1.2 91/06/07 11:58:33 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0.1.1 91/04/12 09:16:12 lwall
+ * patch1: you may now use "die" and "caller" in a signal handler
+ *
+ * Revision 4.0 91/03/20 01:40:04 lwall
+ * 4.0 baseline.
+ *
+ */
+
+struct string {
+ char * str_ptr; /* pointer to malloced string */
+ STRLEN str_len; /* allocated size */
+ union {
+ double str_nval; /* numeric value, if any */
+ long str_useful; /* is this search optimization effective? */
+ ARG *str_args; /* list of args for interpreted string */
+ HASH *str_hash; /* string represents an assoc array (stab?) */
+ ARRAY *str_array; /* string represents an array */
+ CMD *str_cmd; /* command for this source line */
+ struct {
+ STAB *stb_stab; /* magic stab for magic "key" string */
+ HASH *stb_stash; /* which symbol table this stab is in */
+ } stb_u;
+ } str_u;
+ STRLEN str_cur; /* length of str_ptr as a C string */
+ STR *str_magic; /* while free, link to next free str */
+ /* while in use, ptr to "key" for magic items */
+ unsigned char str_pok; /* state of str_ptr */
+ unsigned char str_nok; /* state of str_nval */
+ unsigned char str_rare; /* used by search strings */
+ unsigned char str_state; /* one of SS_* below */
+ /* also used by search strings for backoff */
+#ifdef TAINT
+ bool str_tainted; /* 1 if possibly under control of $< */
+#endif
+};
+
+struct stab { /* should be identical, except for str_ptr */
+ STBP * str_ptr; /* pointer to malloced string */
+ STRLEN str_len; /* allocated size */
+ union {
+ double str_nval; /* numeric value, if any */
+ long str_useful; /* is this search optimization effective? */
+ ARG *str_args; /* list of args for interpreted string */
+ HASH *str_hash; /* string represents an assoc array (stab?) */
+ ARRAY *str_array; /* string represents an array */
+ CMD *str_cmd; /* command for this source line */
+ struct {
+ STAB *stb_stab; /* magic stab for magic "key" string */
+ HASH *stb_stash; /* which symbol table this stab is in */
+ } stb_u;
+ } str_u;
+ STRLEN str_cur; /* length of str_ptr as a C string */
+ STR *str_magic; /* while free, link to next free str */
+ /* while in use, ptr to "key" for magic items */
+ unsigned char str_pok; /* state of str_ptr */
+ unsigned char str_nok; /* state of str_nval */
+ unsigned char str_rare; /* used by search strings */
+ unsigned char str_state; /* one of SS_* below */
+ /* also used by search strings for backoff */
+#ifdef TAINT
+ bool str_tainted; /* 1 if possibly under control of $< */
+#endif
+};
+
+#define str_stab stb_u.stb_stab
+#define str_stash stb_u.stb_stash
+
+/* some extra info tacked to some lvalue strings */
+
+struct lstring {
+ struct string lstr;
+ STRLEN lstr_offset;
+ STRLEN lstr_len;
+};
+
+/* These are the values of str_pok: */
+#define SP_VALID 1 /* str_ptr is valid */
+#define SP_FBM 2 /* string was compiled for fbm search */
+#define SP_STUDIED 4 /* string was studied */
+#define SP_CASEFOLD 8 /* case insensitive fbm search */
+#define SP_INTRP 16 /* string was compiled for interping */
+#define SP_TAIL 32 /* fbm string is tail anchored: /foo$/ */
+#define SP_MULTI 64 /* symbol table entry probably isn't a typo */
+#define SP_TEMP 128 /* string slated to die, so can be plundered */
+
+#define Nullstr Null(STR*)
+
+/* These are the values of str_state: */
+#define SS_NORM 0 /* normal string */
+#define SS_INCR 1 /* normal string, incremented ptr */
+#define SS_SARY 2 /* array on save stack */
+#define SS_SHASH 3 /* associative array on save stack */
+#define SS_SINT 4 /* integer on save stack */
+#define SS_SLONG 5 /* long on save stack */
+#define SS_SSTRP 6 /* STR* on save stack */
+#define SS_SHPTR 7 /* HASH* on save stack */
+#define SS_SNSTAB 8 /* non-stab on save stack */
+#define SS_SCSV 9 /* callsave structure on save stack */
+#define SS_SAPTR 10 /* ARRAY* on save stack */
+#define SS_HASH 253 /* carrying an hash */
+#define SS_ARY 254 /* carrying an array */
+#define SS_FREE 255 /* in free list */
+/* str_state may have any value 0-255 when used to hold fbm pattern, in which */
+/* case it indicates offset to rarest character in screaminstr key */
+
+/* the following macro updates any magic values this str is associated with */
+
+#ifdef TAINT
+#define STABSET(x) \
+ (x)->str_tainted |= tainted; \
+ if ((x)->str_magic) \
+ stabset((x)->str_magic,(x))
+#else
+#define STABSET(x) \
+ if ((x)->str_magic) \
+ stabset((x)->str_magic,(x))
+#endif
+
+#define STR_SSET(dst,src) if (dst != src) str_sset(dst,src)
+
+EXT STR **tmps_list;
+EXT int tmps_max INIT(-1);
+EXT int tmps_base INIT(-1);
+
+char *str_2ptr();
+double str_2num();
+STR *str_mortal();
+STR *str_2mortal();
+STR *str_make();
+STR *str_nmake();
+STR *str_smake();
+int str_cmp();
+int str_eq();
+void str_magic();
+void str_insert();
+void str_numset();
+void str_sset();
+void str_nset();
+void str_set();
+void str_chop();
+void str_cat();
+void str_scat();
+void str_ncat();
+void str_reset();
+void str_taintproper();
+void str_taintenv();
+STRLEN str_len();
+
+#define MULTI (3)
diff --git a/gnu/usr.bin/perl/perl/t/README b/gnu/usr.bin/perl/perl/t/README
new file mode 100644
index 0000000..47ab845
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/README
@@ -0,0 +1,11 @@
+This is the perl test library. To run all the tests, just type 'TEST'.
+
+To add new tests, just look at the current tests and do likewise.
+
+If a test fails, run it by itself to see if it prints any informative
+diagnostics. If not, modify the test to print informative diagnostics.
+If you put out extra lines with a '#' character on the front, you don't
+have to worry about removing the extra print statements later since TEST
+ignores lines beginning with '#'.
+
+If you come up with new tests, send them to lwall@netlabs.com.
diff --git a/gnu/usr.bin/perl/perl/t/TEST b/gnu/usr.bin/perl/perl/t/TEST
new file mode 100755
index 0000000..957a868
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/TEST
@@ -0,0 +1,102 @@
+#!./perl
+
+# $RCSfile: TEST,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:01 $
+
+# This is written in a peculiar style, since we're trying to avoid
+# most of the constructs we'll be testing for.
+
+$| = 1;
+
+if ($ARGV[0] eq '-v') {
+ $verbose = 1;
+ shift;
+}
+
+chdir 't' if -f 't/TEST';
+
+if ($ARGV[0] eq '') {
+ @ARGV = split(/[ \n]/,
+ `echo base/*.t comp/*.t cmd/*.t io/*.t; echo op/*.t lib/*.t`);
+}
+
+open(CONFIG,"../config.sh");
+while (<CONFIG>) {
+ if (/sharpbang='(.*)'/) {
+ $sharpbang = ($1 eq '#!');
+ last;
+ }
+}
+$bad = 0;
+while ($test = shift) {
+ if ($test =~ /^$/) {
+ next;
+ }
+ $te = $test;
+ chop($te);
+ print "$te" . '.' x (15 - length($te));
+ if ($sharpbang) {
+ open(results,"./$test|") || (print "can't run.\n");
+ } else {
+ open(script,"$test") || die "Can't run $test.\n";
+ $_ = <script>;
+ close(script);
+ if (/#!..perl(.*)/) {
+ $switch = $1;
+ } else {
+ $switch = '';
+ }
+ open(results,"./perl$switch $test|") || (print "can't run.\n");
+ }
+ $ok = 0;
+ $next = 0;
+ while (<results>) {
+ if ($verbose) {
+ print $_;
+ }
+ unless (/^#/) {
+ if (/^1\.\.([0-9]+)/) {
+ $max = $1;
+ $totmax += $max;
+ $files += 1;
+ $next = 1;
+ $ok = 1;
+ } else {
+ $next = $1, $ok = 0, last if /^not ok ([0-9]*)/;
+ if (/^ok (.*)/ && $1 == $next) {
+ $next = $next + 1;
+ } else {
+ $ok = 0;
+ }
+ }
+ }
+ }
+ $next = $next - 1;
+ if ($ok && $next == $max) {
+ print "ok\n";
+ } else {
+ $next += 1;
+ print "FAILED on test $next\n";
+ $bad = $bad + 1;
+ $_ = $test;
+ if (/^base/) {
+ die "Failed a basic test--cannot continue.\n";
+ }
+ }
+}
+
+if ($bad == 0) {
+ if ($ok) {
+ print "All tests successful.\n";
+ } else {
+ die "FAILED--no tests were run for some reason.\n";
+ }
+} else {
+ if ($bad == 1) {
+ die "Failed 1 test.\n";
+ } else {
+ die "Failed $bad tests.\n";
+ }
+}
+($user,$sys,$cuser,$csys) = times;
+print sprintf("u=%g s=%g cu=%g cs=%g files=%d tests=%d\n",
+ $user,$sys,$cuser,$csys,$files,$totmax);
diff --git a/gnu/usr.bin/perl/perl/t/base/cond.t b/gnu/usr.bin/perl/perl/t/base/cond.t
new file mode 100755
index 0000000..7d49f4e
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/base/cond.t
@@ -0,0 +1,19 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/base/cond.t,v 1.1.1.1 1993/08/23 21:30:05 nate Exp $
+
+# make sure conditional operators work
+
+print "1..4\n";
+
+$x = '0';
+
+$x eq $x && (print "ok 1\n");
+$x ne $x && (print "not ok 1\n");
+$x eq $x || (print "not ok 2\n");
+$x ne $x || (print "ok 2\n");
+
+$x == $x && (print "ok 3\n");
+$x != $x && (print "not ok 3\n");
+$x == $x || (print "not ok 4\n");
+$x != $x || (print "ok 4\n");
diff --git a/gnu/usr.bin/perl/perl/t/base/if.t b/gnu/usr.bin/perl/perl/t/base/if.t
new file mode 100755
index 0000000..2a9b82c
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/base/if.t
@@ -0,0 +1,11 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/base/if.t,v 1.1.1.1 1993/08/23 21:30:05 nate Exp $
+
+print "1..2\n";
+
+# first test to see if we can run the tests.
+
+$x = 'test';
+if ($x eq $x) { print "ok 1\n"; } else { print "not ok 1\n";}
+if ($x ne $x) { print "not ok 2\n"; } else { print "ok 2\n";}
diff --git a/gnu/usr.bin/perl/perl/t/base/lex.t b/gnu/usr.bin/perl/perl/t/base/lex.t
new file mode 100755
index 0000000..cd6321d
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/base/lex.t
@@ -0,0 +1,78 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/base/lex.t,v 1.1.1.1 1993/08/23 21:30:05 nate Exp $
+
+print "1..18\n";
+
+$ # this is the register <space>
+= 'x';
+
+print "#1 :$ : eq :x:\n";
+if ($ eq 'x') {print "ok 1\n";} else {print "not ok 1\n";}
+
+$x = $#; # this is the register $#
+
+if ($x eq '') {print "ok 2\n";} else {print "not ok 2\n";}
+
+$x = $#x;
+
+if ($x eq '-1') {print "ok 3\n";} else {print "not ok 3\n";}
+
+$x = '\\'; # ';
+
+if (length($x) == 1) {print "ok 4\n";} else {print "not ok 4\n";}
+
+eval 'while (0) {
+ print "foo\n";
+}
+/^/ && (print "ok 5\n");
+';
+
+eval '$foo{1} / 1;';
+if (!$@) {print "ok 6\n";} else {print "not ok 6\n";}
+
+eval '$foo = 123+123.4+123e4+123.4E5+123.4e+5+.12;';
+
+$foo = int($foo * 100 + .5);
+if ($foo eq 2591024652) {print "ok 7\n";} else {print "not ok 7 :$foo:\n";}
+
+print <<'EOF';
+ok 8
+EOF
+
+$foo = 'ok 9';
+print <<EOF;
+$foo
+EOF
+
+eval <<\EOE, print $@;
+print <<'EOF';
+ok 10
+EOF
+
+$foo = 'ok 11';
+print <<EOF;
+$foo
+EOF
+EOE
+
+print <<`EOS` . <<\EOF;
+echo ok 12
+EOS
+ok 13
+EOF
+
+print qq/ok 14\n/;
+print qq(ok 15\n);
+
+print qq
+ok 16\n
+;
+
+print q<ok 17
+>;
+
+print <<; # Yow!
+ok 18
+
+# previous line intentionally left blank.
diff --git a/gnu/usr.bin/perl/perl/t/base/pat.t b/gnu/usr.bin/perl/perl/t/base/pat.t
new file mode 100755
index 0000000..2c8d9a9
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/base/pat.t
@@ -0,0 +1,11 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/base/pat.t,v 1.1.1.1 1993/08/23 21:30:05 nate Exp $
+
+print "1..2\n";
+
+# first test to see if we can run the tests.
+
+$_ = 'test';
+if (/^test/) { print "ok 1\n"; } else { print "not ok 1\n";}
+if (/^foo/) { print "not ok 2\n"; } else { print "ok 2\n";}
diff --git a/gnu/usr.bin/perl/perl/t/base/term.t b/gnu/usr.bin/perl/perl/t/base/term.t
new file mode 100755
index 0000000..c049c58
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/base/term.t
@@ -0,0 +1,42 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/base/term.t,v 1.1.1.1 1993/08/23 21:30:05 nate Exp $
+
+print "1..6\n";
+
+# check "" interpretation
+
+$x = "\n";
+if ($x lt ' ') {print "ok 1\n";} else {print "not ok 1\n";}
+
+# check `` processing
+
+$x = `echo hi there`;
+if ($x eq "hi there\n") {print "ok 2\n";} else {print "not ok 2\n";}
+
+# check $#array
+
+$x[0] = 'foo';
+$x[1] = 'foo';
+$tmp = $#x;
+print "#3\t:$tmp: == :1:\n";
+if ($#x == '1') {print "ok 3\n";} else {print "not ok 3\n";}
+
+# check numeric literal
+
+$x = 1;
+if ($x == '1') {print "ok 4\n";} else {print "not ok 4\n";}
+
+# check <> pseudoliteral
+
+open(try, "/dev/null") || (die "Can't open /dev/null.");
+if (<try> eq '') {
+ print "ok 5\n";
+}
+else {
+ print "not ok 5\n";
+ die "/dev/null IS NOT A CHARACTER SPECIAL FILE!!!!\n" unless -c '/dev/null';
+}
+
+open(try, "../Makefile") || (die "Can't open ../Makefile.");
+if (<try> ne '') {print "ok 6\n";} else {print "not ok 6\n";}
diff --git a/gnu/usr.bin/perl/perl/t/cmd/elsif.t b/gnu/usr.bin/perl/perl/t/cmd/elsif.t
new file mode 100755
index 0000000..0e3457f
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/cmd/elsif.t
@@ -0,0 +1,25 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/cmd/elsif.t,v 1.1.1.1 1993/08/23 21:30:05 nate Exp $
+
+sub foo {
+ if ($_[0] == 1) {
+ 1;
+ }
+ elsif ($_[0] == 2) {
+ 2;
+ }
+ elsif ($_[0] == 3) {
+ 3;
+ }
+ else {
+ 4;
+ }
+}
+
+print "1..4\n";
+
+if (($x = do foo(1)) == 1) {print "ok 1\n";} else {print "not ok 1 '$x'\n";}
+if (($x = do foo(2)) == 2) {print "ok 2\n";} else {print "not ok 2 '$x'\n";}
+if (($x = do foo(3)) == 3) {print "ok 3\n";} else {print "not ok 3 '$x'\n";}
+if (($x = do foo(4)) == 4) {print "ok 4\n";} else {print "not ok 4 '$x'\n";}
diff --git a/gnu/usr.bin/perl/perl/t/cmd/for.t b/gnu/usr.bin/perl/perl/t/cmd/for.t
new file mode 100755
index 0000000..4a0c922
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/cmd/for.t
@@ -0,0 +1,49 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/cmd/for.t,v 1.1.1.1 1993/08/23 21:30:05 nate Exp $
+
+print "1..7\n";
+
+for ($i = 0; $i <= 10; $i++) {
+ $x[$i] = $i;
+}
+$y = $x[10];
+print "#1 :$y: eq :10:\n";
+$y = join(' ', @x);
+print "#1 :$y: eq :0 1 2 3 4 5 6 7 8 9 10:\n";
+if (join(' ', @x) eq '0 1 2 3 4 5 6 7 8 9 10') {
+ print "ok 1\n";
+} else {
+ print "not ok 1\n";
+}
+
+$i = $c = 0;
+for (;;) {
+ $c++;
+ last if $i++ > 10;
+}
+if ($c == 12) {print "ok 2\n";} else {print "not ok 2\n";}
+
+$foo = 3210;
+@ary = (1,2,3,4,5);
+foreach $foo (@ary) {
+ $foo *= 2;
+}
+if (join('',@ary) eq '246810') {print "ok 3\n";} else {print "not ok 3\n";}
+
+for (@ary) {
+ s/(.*)/ok $1\n/;
+}
+
+print $ary[1];
+
+# test for internal scratch array generation
+# this also tests that $foo was restored to 3210 after test 3
+for (split(' ','a b c d e')) {
+ $foo .= $_;
+}
+if ($foo eq '3210abcde') {print "ok 5\n";} else {print "not ok 5 $foo\n";}
+
+foreach $foo (("ok 6\n","ok 7\n")) {
+ print $foo;
+}
diff --git a/gnu/usr.bin/perl/perl/t/cmd/mod.t b/gnu/usr.bin/perl/perl/t/cmd/mod.t
new file mode 100755
index 0000000..eeb44d9
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/cmd/mod.t
@@ -0,0 +1,33 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/cmd/mod.t,v 1.1.1.1 1993/08/23 21:30:05 nate Exp $
+
+print "1..7\n";
+
+print "ok 1\n" if 1;
+print "not ok 1\n" unless 1;
+
+print "ok 2\n" unless 0;
+print "not ok 2\n" if 0;
+
+1 && (print "not ok 3\n") if 0;
+1 && (print "ok 3\n") if 1;
+0 || (print "not ok 4\n") if 0;
+0 || (print "ok 4\n") if 1;
+
+$x = 0;
+do {$x[$x] = $x;} while ($x++) < 10;
+if (join(' ',@x) eq '0 1 2 3 4 5 6 7 8 9 10') {
+ print "ok 5\n";
+} else {
+ print "not ok 5\n";
+}
+
+$x = 15;
+$x = 10 while $x < 10;
+if ($x == 15) {print "ok 6\n";} else {print "not ok 6\n";}
+
+open(foo,'TEST') || open(foo,'t/TEST');
+$x = 0;
+$x++ while <foo>;
+print $x > 50 && $x < 1000 ? "ok 7\n" : "not ok 7\n";
diff --git a/gnu/usr.bin/perl/perl/t/cmd/subval.t b/gnu/usr.bin/perl/perl/t/cmd/subval.t
new file mode 100755
index 0000000..f7f411b
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/cmd/subval.t
@@ -0,0 +1,179 @@
+#!./perl
+
+# $RCSfile: subval.t,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:05 $
+
+sub foo1 {
+ 'true1';
+ if ($_[0]) { 'true2'; }
+}
+
+sub foo2 {
+ 'true1';
+ if ($_[0]) { return 'true2'; } else { return 'true3'; }
+ 'true0';
+}
+
+sub foo3 {
+ 'true1';
+ unless ($_[0]) { 'true2'; }
+}
+
+sub foo4 {
+ 'true1';
+ unless ($_[0]) { 'true2'; } else { 'true3'; }
+}
+
+sub foo5 {
+ 'true1';
+ 'true2' if $_[0];
+}
+
+sub foo6 {
+ 'true1';
+ 'true2' unless $_[0];
+}
+
+print "1..34\n";
+
+if (do foo1(0) eq '0') {print "ok 1\n";} else {print "not ok 1 $foo\n";}
+if (do foo1(1) eq 'true2') {print "ok 2\n";} else {print "not ok 2\n";}
+if (do foo2(0) eq 'true3') {print "ok 3\n";} else {print "not ok 3\n";}
+if (do foo2(1) eq 'true2') {print "ok 4\n";} else {print "not ok 4\n";}
+
+if (do foo3(0) eq 'true2') {print "ok 5\n";} else {print "not ok 5\n";}
+if (do foo3(1) eq '1') {print "ok 6\n";} else {print "not ok 6\n";}
+if (do foo4(0) eq 'true2') {print "ok 7\n";} else {print "not ok 7\n";}
+if (do foo4(1) eq 'true3') {print "ok 8\n";} else {print "not ok 8\n";}
+
+if (do foo5(0) eq '0') {print "ok 9\n";} else {print "not ok 9\n";}
+if (do foo5(1) eq 'true2') {print "ok 10\n";} else {print "not ok 10\n";}
+if (do foo6(0) eq 'true2') {print "ok 11\n";} else {print "not ok 11\n";}
+if (do foo6(1) eq '1') {print "ok 12\n";} else {print "not ok 12 $x\n";}
+
+# Now test to see that recursion works using a Fibonacci number generator
+
+sub fib {
+ local($arg) = @_;
+ local($foo);
+ $level++;
+ if ($arg <= 2) {
+ $foo = 1;
+ }
+ else {
+ $foo = do fib($arg-1) + do fib($arg-2);
+ }
+ $level--;
+ $foo;
+}
+
+@good = (0,1,1,2,3,5,8,13,21,34,55,89);
+
+for ($i = 1; $i <= 10; $i++) {
+ $foo = $i + 12;
+ if (do fib($i) == $good[$i]) {
+ print "ok $foo\n";
+ }
+ else {
+ print "not ok $foo\n";
+ }
+}
+
+sub ary1 {
+ (1,2,3);
+}
+
+print &ary1 eq 3 ? "ok 23\n" : "not ok 23\n";
+
+print join(':',&ary1) eq '1:2:3' ? "ok 24\n" : "not ok 24\n";
+
+sub ary2 {
+ do {
+ return (1,2,3);
+ (3,2,1);
+ };
+ 0;
+}
+
+print &ary2 eq 3 ? "ok 25\n" : "not ok 25\n";
+
+$x = join(':',&ary2);
+print $x eq '1:2:3' ? "ok 26\n" : "not ok 26 $x\n";
+
+sub somesub {
+ local($num,$P,$F,$L) = @_;
+ ($p,$f,$l) = caller;
+ print "$p:$f:$l" eq "$P:$F:$L" ? "ok $num\n" : "not ok $num $p:$f:$l ne $P:$F:$L\n";
+}
+
+&somesub(27, 'main', __FILE__, __LINE__);
+
+package foo;
+&main'somesub(28, 'foo', __FILE__, __LINE__);
+
+package main;
+$i = 28;
+open(FOO,">Cmd_subval.tmp");
+print FOO "blah blah\n";
+close FOO;
+
+&file_main(*F);
+close F;
+&info_main;
+
+&file_package(*F);
+close F;
+&info_package;
+
+unlink 'Cmd_subval.tmp';
+
+sub file_main {
+ local(*F) = @_;
+
+ open(F, 'Cmd_subval.tmp') || die "can't open\n";
+ $i++;
+ eof F ? print "not ok $i\n" : print "ok $i\n";
+}
+
+sub info_main {
+ local(*F);
+
+ open(F, 'Cmd_subval.tmp') || die "test: can't open\n";
+ $i++;
+ eof F ? print "not ok $i\n" : print "ok $i\n";
+ &iseof(*F);
+ close F;
+}
+
+sub iseof {
+ local(*UNIQ) = @_;
+
+ $i++;
+ eof UNIQ ? print "(not ok $i)\n" : print "ok $i\n";
+}
+
+{package foo;
+
+ sub main'file_package {
+ local(*F) = @_;
+
+ open(F, 'Cmd_subval.tmp') || die "can't open\n";
+ $main'i++;
+ eof F ? print "not ok $main'i\n" : print "ok $main'i\n";
+ }
+
+ sub main'info_package {
+ local(*F);
+
+ open(F, 'Cmd_subval.tmp') || die "can't open\n";
+ $main'i++;
+ eof F ? print "not ok $main'i\n" : print "ok $main'i\n";
+ &iseof(*F);
+ }
+
+ sub iseof {
+ local(*UNIQ) = @_;
+
+ $main'i++;
+ eof UNIQ ? print "not ok $main'i\n" : print "ok $main'i\n";
+ }
+}
diff --git a/gnu/usr.bin/perl/perl/t/cmd/switch.t b/gnu/usr.bin/perl/perl/t/cmd/switch.t
new file mode 100755
index 0000000..d0d4c93
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/cmd/switch.t
@@ -0,0 +1,75 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/cmd/switch.t,v 1.1.1.1 1993/08/23 21:30:05 nate Exp $
+
+print "1..18\n";
+
+sub foo1 {
+ $_ = shift(@_);
+ $a = 0;
+ until ($a++) {
+ next if $_ eq 1;
+ next if $_ eq 2;
+ next if $_ eq 3;
+ next if $_ eq 4;
+ return 20;
+ }
+ continue {
+ return $_;
+ }
+}
+
+print do foo1(0) == 20 ? "ok 1\n" : "not ok 1\n";
+print do foo1(1) == 1 ? "ok 2\n" : "not ok 2\n";
+print do foo1(2) == 2 ? "ok 3\n" : "not ok 3\n";
+print do foo1(3) == 3 ? "ok 4\n" : "not ok 4\n";
+print do foo1(4) == 4 ? "ok 5\n" : "not ok 5\n";
+print do foo1(5) == 20 ? "ok 6\n" : "not ok 6\n";
+
+sub foo2 {
+ $_ = shift(@_);
+ {
+ last if $_ == 1;
+ last if $_ == 2;
+ last if $_ == 3;
+ last if $_ == 4;
+ }
+ continue {
+ return 20;
+ }
+ return $_;
+}
+
+print do foo2(0) == 20 ? "ok 7\n" : "not ok 1\n";
+print do foo2(1) == 1 ? "ok 8\n" : "not ok 8\n";
+print do foo2(2) == 2 ? "ok 9\n" : "not ok 9\n";
+print do foo2(3) == 3 ? "ok 10\n" : "not ok 10\n";
+print do foo2(4) == 4 ? "ok 11\n" : "not ok 11\n";
+print do foo2(5) == 20 ? "ok 12\n" : "not ok 12\n";
+
+sub foo3 {
+ $_ = shift(@_);
+ if (/^1/) {
+ return 1;
+ }
+ elsif (/^2/) {
+ return 2;
+ }
+ elsif (/^3/) {
+ return 3;
+ }
+ elsif (/^4/) {
+ return 4;
+ }
+ else {
+ return 20;
+ }
+ return 40;
+}
+
+print do foo3(0) == 20 ? "ok 13\n" : "not ok 13\n";
+print do foo3(1) == 1 ? "ok 14\n" : "not ok 14\n";
+print do foo3(2) == 2 ? "ok 15\n" : "not ok 15\n";
+print do foo3(3) == 3 ? "ok 16\n" : "not ok 16\n";
+print do foo3(4) == 4 ? "ok 17\n" : "not ok 17\n";
+print do foo3(5) == 20 ? "ok 18\n" : "not ok 18\n";
diff --git a/gnu/usr.bin/perl/perl/t/cmd/while.t b/gnu/usr.bin/perl/perl/t/cmd/while.t
new file mode 100755
index 0000000..006e251
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/cmd/while.t
@@ -0,0 +1,110 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/cmd/while.t,v 1.1.1.1 1993/08/23 21:30:05 nate Exp $
+
+print "1..10\n";
+
+open (tmp,'>Cmd.while.tmp') || die "Can't create Cmd.while.tmp.";
+print tmp "tvi925\n";
+print tmp "tvi920\n";
+print tmp "vt100\n";
+print tmp "Amiga\n";
+print tmp "paper\n";
+close tmp;
+
+# test "last" command
+
+open(fh,'Cmd.while.tmp') || die "Can't open Cmd.while.tmp.";
+while (<fh>) {
+ last if /vt100/;
+}
+if (!eof && /vt100/) {print "ok 1\n";} else {print "not ok 1 $_\n";}
+
+# test "next" command
+
+$bad = '';
+open(fh,'Cmd.while.tmp') || die "Can't open Cmd.while.tmp.";
+while (<fh>) {
+ next if /vt100/;
+ $bad = 1 if /vt100/;
+}
+if (!eof || /vt100/ || $bad) {print "not ok 2\n";} else {print "ok 2\n";}
+
+# test "redo" command
+
+$bad = '';
+open(fh,'Cmd.while.tmp') || die "Can't open Cmd.while.tmp.";
+while (<fh>) {
+ if (s/vt100/VT100/g) {
+ s/VT100/Vt100/g;
+ redo;
+ }
+ $bad = 1 if /vt100/;
+ $bad = 1 if /VT100/;
+}
+if (!eof || $bad) {print "not ok 3\n";} else {print "ok 3\n";}
+
+# now do the same with a label and a continue block
+
+# test "last" command
+
+$badcont = '';
+open(fh,'Cmd.while.tmp') || die "Can't open Cmd.while.tmp.";
+line: while (<fh>) {
+ if (/vt100/) {last line;}
+} continue {
+ $badcont = 1 if /vt100/;
+}
+if (!eof && /vt100/) {print "ok 4\n";} else {print "not ok 4\n";}
+if (!$badcont) {print "ok 5\n";} else {print "not ok 5\n";}
+
+# test "next" command
+
+$bad = '';
+$badcont = 1;
+open(fh,'Cmd.while.tmp') || die "Can't open Cmd.while.tmp.";
+entry: while (<fh>) {
+ next entry if /vt100/;
+ $bad = 1 if /vt100/;
+} continue {
+ $badcont = '' if /vt100/;
+}
+if (!eof || /vt100/ || $bad) {print "not ok 6\n";} else {print "ok 6\n";}
+if (!$badcont) {print "ok 7\n";} else {print "not ok 7\n";}
+
+# test "redo" command
+
+$bad = '';
+$badcont = '';
+open(fh,'Cmd.while.tmp') || die "Can't open Cmd.while.tmp.";
+loop: while (<fh>) {
+ if (s/vt100/VT100/g) {
+ s/VT100/Vt100/g;
+ redo loop;
+ }
+ $bad = 1 if /vt100/;
+ $bad = 1 if /VT100/;
+} continue {
+ $badcont = 1 if /vt100/;
+}
+if (!eof || $bad) {print "not ok 8\n";} else {print "ok 8\n";}
+if (!$badcont) {print "ok 9\n";} else {print "not ok 9\n";}
+
+`/bin/rm -f Cmd.while.tmp`;
+
+#$x = 0;
+#while (1) {
+# if ($x > 1) {last;}
+# next;
+#} continue {
+# if ($x++ > 10) {last;}
+# next;
+#}
+#
+#if ($x < 10) {print "ok 10\n";} else {print "not ok 10\n";}
+
+$i = 9;
+{
+ $i++;
+}
+print "ok $i\n";
diff --git a/gnu/usr.bin/perl/perl/t/comp/cmdopt.t b/gnu/usr.bin/perl/perl/t/comp/cmdopt.t
new file mode 100755
index 0000000..3c47130
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/comp/cmdopt.t
@@ -0,0 +1,83 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/comp/cmdopt.t,v 1.1.1.1 1993/08/23 21:30:06 nate Exp $
+
+print "1..40\n";
+
+# test the optimization of constants
+
+if (1) { print "ok 1\n";} else { print "not ok 1\n";}
+unless (0) { print "ok 2\n";} else { print "not ok 2\n";}
+
+if (0) { print "not ok 3\n";} else { print "ok 3\n";}
+unless (1) { print "not ok 4\n";} else { print "ok 4\n";}
+
+unless (!1) { print "ok 5\n";} else { print "not ok 5\n";}
+if (!0) { print "ok 6\n";} else { print "not ok 6\n";}
+
+unless (!0) { print "not ok 7\n";} else { print "ok 7\n";}
+if (!1) { print "not ok 8\n";} else { print "ok 8\n";}
+
+$x = 1;
+if (1 && $x) { print "ok 9\n";} else { print "not ok 9\n";}
+if (0 && $x) { print "not ok 10\n";} else { print "ok 10\n";}
+$x = '';
+if (1 && $x) { print "not ok 11\n";} else { print "ok 11\n";}
+if (0 && $x) { print "not ok 12\n";} else { print "ok 12\n";}
+
+$x = 1;
+if (1 || $x) { print "ok 13\n";} else { print "not ok 13\n";}
+if (0 || $x) { print "ok 14\n";} else { print "not ok 14\n";}
+$x = '';
+if (1 || $x) { print "ok 15\n";} else { print "not ok 15\n";}
+if (0 || $x) { print "not ok 16\n";} else { print "ok 16\n";}
+
+
+# test the optimization of registers
+
+$x = 1;
+if ($x) { print "ok 17\n";} else { print "not ok 17\n";}
+unless ($x) { print "not ok 18\n";} else { print "ok 18\n";}
+
+$x = '';
+if ($x) { print "not ok 19\n";} else { print "ok 19\n";}
+unless ($x) { print "ok 20\n";} else { print "not ok 20\n";}
+
+# test optimization of string operations
+
+$a = 'a';
+if ($a eq 'a') { print "ok 21\n";} else { print "not ok 21\n";}
+if ($a ne 'a') { print "not ok 22\n";} else { print "ok 22\n";}
+
+if ($a =~ /a/) { print "ok 23\n";} else { print "not ok 23\n";}
+if ($a !~ /a/) { print "not ok 24\n";} else { print "ok 24\n";}
+# test interaction of logicals and other operations
+
+$a = 'a';
+$x = 1;
+if ($a eq 'a' && $x) { print "ok 25\n";} else { print "not ok 25\n";}
+if ($a ne 'a' && $x) { print "not ok 26\n";} else { print "ok 26\n";}
+$x = '';
+if ($a eq 'a' && $x) { print "not ok 27\n";} else { print "ok 27\n";}
+if ($a ne 'a' && $x) { print "not ok 28\n";} else { print "ok 28\n";}
+
+$x = 1;
+if ($a eq 'a' || $x) { print "ok 29\n";} else { print "not ok 29\n";}
+if ($a ne 'a' || $x) { print "ok 30\n";} else { print "not ok 30\n";}
+$x = '';
+if ($a eq 'a' || $x) { print "ok 31\n";} else { print "not ok 31\n";}
+if ($a ne 'a' || $x) { print "not ok 32\n";} else { print "ok 32\n";}
+
+$x = 1;
+if ($a =~ /a/ && $x) { print "ok 33\n";} else { print "not ok 33\n";}
+if ($a !~ /a/ && $x) { print "not ok 34\n";} else { print "ok 34\n";}
+$x = '';
+if ($a =~ /a/ && $x) { print "not ok 35\n";} else { print "ok 35\n";}
+ if ($a !~ /a/ && $x) { print "not ok 36\n";} else { print "ok 36\n";}
+
+$x = 1;
+if ($a =~ /a/ || $x) { print "ok 37\n";} else { print "not ok 37\n";}
+if ($a !~ /a/ || $x) { print "ok 38\n";} else { print "not ok 38\n";}
+$x = '';
+if ($a =~ /a/ || $x) { print "ok 39\n";} else { print "not ok 39\n";}
+if ($a !~ /a/ || $x) { print "not ok 40\n";} else { print "ok 40\n";}
diff --git a/gnu/usr.bin/perl/perl/t/comp/cpp.t b/gnu/usr.bin/perl/perl/t/comp/cpp.t
new file mode 100755
index 0000000..8cceb73
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/comp/cpp.t
@@ -0,0 +1,51 @@
+#!./perl -P
+
+# $RCSfile: cpp.t,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:06 $
+
+open(CONFIG,"../config.sh") || die;
+while (<CONFIG>) {
+ if (/^cppstdin/) {
+ if (/^cppstdin='(.*cppstdin)'/ && ! -e $1) {
+ print "1..0\n";
+ exit; # Can't test till after install, alas.
+ }
+ last;
+ }
+}
+close CONFIG;
+
+print "1..3\n";
+
+#this is a comment
+#define MESS "ok 1\n"
+print MESS;
+
+#If you capitalize, it's a comment.
+#ifdef MESS
+ print "ok 2\n";
+#else
+ print "not ok 2\n";
+#endif
+
+open(TRY,">Comp.cpp.tmp") || die "Can't open temp perl file.";
+
+($prog = <<'END') =~ s/X//g;
+X$ok = "not ok 3\n";
+X#include "Comp.cpp.inc"
+X#ifdef OK
+X$ok = OK;
+X#endif
+Xprint $ok;
+END
+print TRY $prog;
+close TRY;
+
+open(TRY,">Comp.cpp.inc") || (die "Can't open temp include file.");
+print TRY '#define OK "ok 3\n"' . "\n";
+close TRY;
+
+$pwd=`pwd`;
+$pwd =~ s/\n//;
+$x = `./perl -P Comp.cpp.tmp`;
+print $x;
+unlink "Comp.cpp.tmp", "Comp.cpp.inc";
diff --git a/gnu/usr.bin/perl/perl/t/comp/decl.t b/gnu/usr.bin/perl/perl/t/comp/decl.t
new file mode 100755
index 0000000..f1c84c2
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/comp/decl.t
@@ -0,0 +1,49 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/comp/decl.t,v 1.1.1.1 1993/08/23 21:30:07 nate Exp $
+
+# check to see if subroutine declarations work everwhere
+
+sub one {
+ print "ok 1\n";
+}
+format one =
+ok 5
+.
+
+print "1..7\n";
+
+do one();
+do two();
+
+sub two {
+ print "ok 2\n";
+}
+format two =
+@<<<
+$foo
+.
+
+if ($x eq $x) {
+ sub three {
+ print "ok 3\n";
+ }
+ do three();
+}
+
+do four();
+$~ = 'one';
+write;
+$~ = 'two';
+$foo = "ok 6";
+write;
+$~ = 'three';
+write;
+
+format three =
+ok 7
+.
+
+sub four {
+ print "ok 4\n";
+}
diff --git a/gnu/usr.bin/perl/perl/t/comp/multiline.t b/gnu/usr.bin/perl/perl/t/comp/multiline.t
new file mode 100755
index 0000000..78df482
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/comp/multiline.t
@@ -0,0 +1,40 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/comp/multiline.t,v 1.1.1.1 1993/08/23 21:30:06 nate Exp $
+
+print "1..5\n";
+
+open(try,'>Comp.try') || (die "Can't open temp file.");
+
+$x = 'now is the time
+for all good men
+to come to.
+';
+
+$y = 'now is the time' . "\n" .
+'for all good men' . "\n" .
+'to come to.' . "\n";
+
+if ($x eq $y) {print "ok 1\n";} else {print "not ok 1\n";}
+
+print try $x;
+close try;
+
+open(try,'Comp.try') || (die "Can't reopen temp file.");
+$count = 0;
+$z = '';
+while (<try>) {
+ $z .= $_;
+ $count = $count + 1;
+}
+
+if ($z eq $y) {print "ok 2\n";} else {print "not ok 2\n";}
+
+if ($count == 3) {print "ok 3\n";} else {print "not ok 3\n";}
+
+$_ = `cat Comp.try`;
+
+if (/.*\n.*\n.*\n$/) {print "ok 4\n";} else {print "not ok 4\n";}
+`/bin/rm -f Comp.try`;
+
+if ($_ eq $y) {print "ok 5\n";} else {print "not ok 5\n";}
diff --git a/gnu/usr.bin/perl/perl/t/comp/package.t b/gnu/usr.bin/perl/perl/t/comp/package.t
new file mode 100755
index 0000000..5237011
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/comp/package.t
@@ -0,0 +1,33 @@
+#!./perl
+
+print "1..7\n";
+
+$blurfl = 123;
+$foo = 3;
+
+package XYZ;
+
+$bar = 4;
+
+{
+ package ABC;
+ $blurfl = 5;
+ $main'a = $'b;
+}
+
+$ABC'dyick = 6;
+
+$xyz = 2;
+
+$main = join(':', sort(keys _main));
+$XYZ = join(':', sort(keys _XYZ));
+$ABC = join(':', sort(keys _ABC));
+
+print $XYZ eq 'ABC:XYZ:bar:main:xyz' ? "ok 1\n" : "not ok 1 '$XYZ'\n";
+print $ABC eq 'blurfl:dyick' ? "ok 2\n" : "not ok 2\n";
+print $main'blurfl == 123 ? "ok 3\n" : "not ok 3\n";
+package ABC;
+print $blurfl == 5 ? "ok 4\n" : "not ok 4\n";
+eval 'print $blurfl == 5 ? "ok 5\n" : "not ok 5\n";';
+eval 'package main; print $blurfl == 123 ? "ok 6\n" : "not ok 6\n";';
+print $blurfl == 5 ? "ok 7\n" : "not ok 7\n";
diff --git a/gnu/usr.bin/perl/perl/t/comp/script.t b/gnu/usr.bin/perl/perl/t/comp/script.t
new file mode 100755
index 0000000..9dcf901
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/comp/script.t
@@ -0,0 +1,23 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/comp/script.t,v 1.1.1.1 1993/08/23 21:30:06 nate Exp $
+
+print "1..3\n";
+
+$x = `./perl -e 'print "ok\n";'`;
+
+if ($x eq "ok\n") {print "ok 1\n";} else {print "not ok 1\n";}
+
+open(try,">Comp.script") || (die "Can't open temp file.");
+print try 'print "ok\n";'; print try "\n";
+close try;
+
+$x = `./perl Comp.script`;
+
+if ($x eq "ok\n") {print "ok 2\n";} else {print "not ok 2\n";}
+
+$x = `./perl <Comp.script`;
+
+if ($x eq "ok\n") {print "ok 3\n";} else {print "not ok 3\n";}
+
+`/bin/rm -f Comp.script`;
diff --git a/gnu/usr.bin/perl/perl/t/comp/term.t b/gnu/usr.bin/perl/perl/t/comp/term.t
new file mode 100755
index 0000000..70b23fd
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/comp/term.t
@@ -0,0 +1,35 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/comp/term.t,v 1.1.1.1 1993/08/23 21:30:06 nate Exp $
+
+# tests that aren't important enough for base.term
+
+print "1..14\n";
+
+$x = "\\n";
+print "#1\t:$x: eq " . ':\n:' . "\n";
+if ($x eq '\n') {print "ok 1\n";} else {print "not ok 1\n";}
+
+$x = "#2\t:$x: eq :\\n:\n";
+print $x;
+unless (index($x,'\\\\')>0) {print "ok 2\n";} else {print "not ok 2\n";}
+
+if (length('\\\\') == 2) {print "ok 3\n";} else {print "not ok 3\n";}
+
+$one = 'a';
+
+if (length("\\n") == 2) {print "ok 4\n";} else {print "not ok 4\n";}
+if (length("\\\n") == 2) {print "ok 5\n";} else {print "not ok 5\n";}
+if (length("$one\\n") == 3) {print "ok 6\n";} else {print "not ok 6\n";}
+if (length("$one\\\n") == 3) {print "ok 7\n";} else {print "not ok 7\n";}
+if (length("\\n$one") == 3) {print "ok 8\n";} else {print "not ok 8\n";}
+if (length("\\\n$one") == 3) {print "ok 9\n";} else {print "not ok 9\n";}
+if (length("\\${one}") == 2) {print "ok 10\n";} else {print "not ok 10\n";}
+
+if ("${one}b" eq "ab") { print "ok 11\n";} else {print "not ok 11\n";}
+
+@foo = (1,2,3);
+if ("$foo[1]b" eq "2b") { print "ok 12\n";} else {print "not ok 12\n";}
+if ("@foo[0..1]b" eq "1 2b") { print "ok 13\n";} else {print "not ok 13\n";}
+$" = '::';
+if ("@foo[0..1]b" eq "1::2b") { print "ok 14\n";} else {print "not ok 14\n";}
diff --git a/gnu/usr.bin/perl/perl/t/io/argv.t b/gnu/usr.bin/perl/perl/t/io/argv.t
new file mode 100755
index 0000000..99c5936
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/io/argv.t
@@ -0,0 +1,36 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/io/argv.t,v 1.1.1.1 1993/08/23 21:30:06 nate Exp $
+
+print "1..5\n";
+
+open(try, '>Io.argv.tmp') || (die "Can't open temp file.");
+print try "a line\n";
+close try;
+
+$x = `./perl -e 'while (<>) {print \$.,\$_;}' Io.argv.tmp Io.argv.tmp`;
+
+if ($x eq "1a line\n2a line\n") {print "ok 1\n";} else {print "not ok 1\n";}
+
+$x = `echo foo|./perl -e 'while (<>) {print $_;}' Io.argv.tmp -`;
+
+if ($x eq "a line\nfoo\n") {print "ok 2\n";} else {print "not ok 2\n";}
+
+$x = `echo foo|./perl -e 'while (<>) {print $_;}'`;
+
+if ($x eq "foo\n") {print "ok 3\n";} else {print "not ok 3 :$x:\n";}
+
+@ARGV = ('Io.argv.tmp', 'Io.argv.tmp', '/dev/null', 'Io.argv.tmp');
+while (<>) {
+ $y .= $. . $_;
+ if (eof()) {
+ if ($. == 3) {print "ok 4\n";} else {print "not ok 4\n";}
+ }
+}
+
+if ($y eq "1a line\n2a line\n3a line\n")
+ {print "ok 5\n";}
+else
+ {print "not ok 5\n";}
+
+`/bin/rm -f Io.argv.tmp`;
diff --git a/gnu/usr.bin/perl/perl/t/io/dup.t b/gnu/usr.bin/perl/perl/t/io/dup.t
new file mode 100755
index 0000000..8d11eca
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/io/dup.t
@@ -0,0 +1,32 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/io/dup.t,v 1.1.1.1 1993/08/23 21:30:06 nate Exp $
+
+print "1..6\n";
+
+print "ok 1\n";
+
+open(dupout,">&STDOUT");
+open(duperr,">&STDERR");
+
+open(STDOUT,">Io.dup") || die "Can't open stdout";
+open(STDERR,">&STDOUT") || die "Can't open stderr";
+
+select(STDERR); $| = 1;
+select(STDOUT); $| = 1;
+
+print STDOUT "ok 2\n";
+print STDERR "ok 3\n";
+system 'echo ok 4';
+system 'echo ok 5 1>&2';
+
+close(STDOUT);
+close(STDERR);
+
+open(STDOUT,">&dupout");
+open(STDERR,">&duperr");
+
+system 'cat Io.dup';
+unlink 'Io.dup';
+
+print STDOUT "ok 6\n";
diff --git a/gnu/usr.bin/perl/perl/t/io/fs.t b/gnu/usr.bin/perl/perl/t/io/fs.t
new file mode 100755
index 0000000..1d95cdc
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/io/fs.t
@@ -0,0 +1,85 @@
+#!./perl
+
+# $RCSfile: fs.t,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:05 $
+
+print "1..22\n";
+
+$wd = `pwd`;
+chop($wd);
+
+`rm -f tmp 2>/dev/null; mkdir tmp 2>/dev/null`;
+chdir './tmp';
+`/bin/rm -rf a b c x`;
+
+umask(022);
+
+if ((umask(0)&0777) == 022) {print "ok 1\n";} else {print "not ok 1\n";}
+open(fh,'>x') || die "Can't create x";
+close(fh);
+open(fh,'>a') || die "Can't create a";
+close(fh);
+
+if (link('a','b')) {print "ok 2\n";} else {print "not ok 2\n";}
+
+if (link('b','c')) {print "ok 3\n";} else {print "not ok 3\n";}
+
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat('c');
+
+if ($nlink == 3) {print "ok 4\n";} else {print "not ok 4\n";}
+if (($mode & 0777) == 0666) {print "ok 5\n";} else {print "not ok 5\n";}
+
+if ((chmod 0777,'a') == 1) {print "ok 6\n";} else {print "not ok 6\n";}
+
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat('c');
+if (($mode & 0777) == 0777) {print "ok 7\n";} else {print "not ok 7\n";}
+
+if ((chmod 0700,'c','x') == 2) {print "ok 8\n";} else {print "not ok 8\n";}
+
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat('c');
+if (($mode & 0777) == 0700) {print "ok 9\n";} else {print "not ok 9\n";}
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat('x');
+if (($mode & 0777) == 0700) {print "ok 10\n";} else {print "not ok 10\n";}
+
+if ((unlink 'b','x') == 2) {print "ok 11\n";} else {print "not ok 11\n";}
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat('b');
+if ($ino == 0) {print "ok 12\n";} else {print "not ok 12\n";}
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat('x');
+if ($ino == 0) {print "ok 13\n";} else {print "not ok 13\n";}
+
+if (rename('a','b')) {print "ok 14\n";} else {print "not ok 14\n";}
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat('a');
+if ($ino == 0) {print "ok 15\n";} else {print "not ok 15\n";}
+$foo = (utime 500000000,500000001,'b');
+if ($foo == 1) {print "ok 16\n";} else {print "not ok 16 $foo\n";}
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat('b');
+if ($ino) {print "ok 17\n";} else {print "not ok 17\n";}
+if (($atime == 500000000 && $mtime == 500000001) || $wd =~ m#/afs/#)
+ {print "ok 18\n";}
+else
+ {print "not ok 18 $atime $mtime\n";}
+
+if ((unlink 'b') == 1) {print "ok 19\n";} else {print "not ok 19\n";}
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat('b');
+if ($ino == 0) {print "ok 20\n";} else {print "not ok 20\n";}
+unlink 'c';
+
+chdir $wd || die "Can't cd back to $wd";
+
+unlink 'c';
+if (`ls -l perl 2>/dev/null` =~ /^l.*->/) { # we have symbolic links
+ if (symlink("TEST","c")) {print "ok 21\n";} else {print "not ok 21\n";}
+ $foo = `grep perl c`;
+ if ($foo) {print "ok 22\n";} else {print "not ok 22\n";}
+}
+else {
+ print "ok 21\nok 22\n";
+}
diff --git a/gnu/usr.bin/perl/perl/t/io/inplace.t b/gnu/usr.bin/perl/perl/t/io/inplace.t
new file mode 100755
index 0000000..b22afda
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/io/inplace.t
@@ -0,0 +1,21 @@
+#!./perl
+
+$^I = '.bak';
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/io/inplace.t,v 1.1.1.1 1993/08/23 21:30:05 nate Exp $
+
+print "1..2\n";
+
+@ARGV = ('.a','.b','.c');
+`echo foo | tee .a .b .c`;
+while (<>) {
+ s/foo/bar/;
+}
+continue {
+ print;
+}
+
+if (`cat .a .b .c` eq "bar\nbar\nbar\n") {print "ok 1\n";} else {print "not ok 1\n";}
+if (`cat .a.bak .b.bak .c.bak` eq "foo\nfoo\nfoo\n") {print "ok 2\n";} else {print "not ok 2\n";}
+
+unlink '.a', '.b', '.c', '.a.bak', '.b.bak', '.c.bak';
diff --git a/gnu/usr.bin/perl/perl/t/io/pipe.t b/gnu/usr.bin/perl/perl/t/io/pipe.t
new file mode 100755
index 0000000..791a9c7
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/io/pipe.t
@@ -0,0 +1,56 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/io/pipe.t,v 1.1.1.1 1993/08/23 21:30:06 nate Exp $
+
+$| = 1;
+print "1..8\n";
+
+open(PIPE, "|-") || (exec 'tr', '[A-Z]', '[a-z]');
+print PIPE "OK 1\n";
+print PIPE "ok 2\n";
+close PIPE;
+
+if (open(PIPE, "-|")) {
+ while(<PIPE>) {
+ s/^not //;
+ print;
+ }
+}
+else {
+ print STDOUT "not ok 3\n";
+ exec 'echo', 'not ok 4';
+}
+
+pipe(READER,WRITER) || die "Can't open pipe";
+
+if ($pid = fork) {
+ close WRITER;
+ while(<READER>) {
+ s/^not //;
+ y/A-Z/a-z/;
+ print;
+ }
+}
+else {
+ die "Couldn't fork" unless defined $pid;
+ close READER;
+ print WRITER "not ok 5\n";
+ open(STDOUT,">&WRITER") || die "Can't dup WRITER to STDOUT";
+ close WRITER;
+ exec 'echo', 'not ok 6';
+}
+
+
+pipe(READER,WRITER) || die "Can't open pipe";
+close READER;
+
+$SIG{'PIPE'} = 'broken_pipe';
+
+sub broken_pipe {
+ print "ok 7\n";
+}
+
+print WRITER "not ok 7\n";
+close WRITER;
+
+print "ok 8\n";
diff --git a/gnu/usr.bin/perl/perl/t/io/print.t b/gnu/usr.bin/perl/perl/t/io/print.t
new file mode 100755
index 0000000..1185442
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/io/print.t
@@ -0,0 +1,32 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/io/print.t,v 1.1.1.1 1993/08/23 21:30:06 nate Exp $
+
+print "1..16\n";
+
+$foo = 'STDOUT';
+print $foo "ok 1\n";
+
+print "ok 2\n","ok 3\n","ok 4\n";
+print STDOUT "ok 5\n";
+
+open(foo,">-");
+print foo "ok 6\n";
+
+printf "ok %d\n",7;
+printf("ok %d\n",8);
+
+@a = ("ok %d%c",9,ord("\n"));
+printf @a;
+
+$a[1] = 10;
+printf STDOUT @a;
+
+$, = ' ';
+$\ = "\n";
+
+print "ok","11";
+
+@x = ("ok","12\nok","13\nok");
+@y = ("15\nok","16");
+print @x,"14\nok",@y;
diff --git a/gnu/usr.bin/perl/perl/t/io/tell.t b/gnu/usr.bin/perl/perl/t/io/tell.t
new file mode 100755
index 0000000..27e69a0
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/io/tell.t
@@ -0,0 +1,44 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/io/tell.t,v 1.1.1.1 1993/08/23 21:30:06 nate Exp $
+
+print "1..13\n";
+
+$TST = 'tst';
+
+open($TST, '../Makefile') || (die "Can't open ../Makefile");
+
+if (eof(tst)) { print "not ok 1\n"; } else { print "ok 1\n"; }
+
+$firstline = <$TST>;
+$secondpos = tell;
+
+$x = 0;
+while (<tst>) {
+ if (eof) {$x++;}
+}
+if ($x == 1) { print "ok 2\n"; } else { print "not ok 2\n"; }
+
+$lastpos = tell;
+
+unless (eof) { print "not ok 3\n"; } else { print "ok 3\n"; }
+
+if (seek($TST,0,0)) { print "ok 4\n"; } else { print "not ok 4\n"; }
+
+if (eof) { print "not ok 5\n"; } else { print "ok 5\n"; }
+
+if ($firstline eq <tst>) { print "ok 6\n"; } else { print "not ok 6\n"; }
+
+if ($secondpos == tell) { print "ok 7\n"; } else { print "not ok 7\n"; }
+
+if (seek(tst,0,1)) { print "ok 8\n"; } else { print "not ok 8\n"; }
+
+if (eof($TST)) { print "not ok 9\n"; } else { print "ok 9\n"; }
+
+if ($secondpos == tell) { print "ok 10\n"; } else { print "not ok 10\n"; }
+
+if (seek(tst,0,2)) { print "ok 11\n"; } else { print "not ok 11\n"; }
+
+if ($lastpos == tell) { print "ok 12\n"; } else { print "not ok 12\n"; }
+
+unless (eof) { print "not ok 13\n"; } else { print "ok 13\n"; }
diff --git a/gnu/usr.bin/perl/perl/t/lib/big.t b/gnu/usr.bin/perl/perl/t/lib/big.t
new file mode 100755
index 0000000..9b27a5f
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/lib/big.t
@@ -0,0 +1,280 @@
+#!./perl
+require "../../lib/bigint.pl";
+
+$test = 0;
+$| = 1;
+print "1..246\n";
+while (<DATA>) {
+ chop;
+ if (/^&/) {
+ $f = $_;
+ } else {
+ ++$test;
+ @args = split(/:/,$_,99);
+ $ans = pop(@args);
+ $try = "$f('" . join("','", @args) . "');";
+ if (($ans1 = eval($try)) eq $ans) {
+ print "ok $test\n";
+ } else {
+ print "not ok $test\n";
+ print "# '$try' expected: '$ans' got: '$ans1'\n";
+ }
+ }
+}
+__END__
+&bnorm
+abc:NaN
+ 1 a:NaN
+1bcd2:NaN
+11111b:NaN
++1z:NaN
+-1z:NaN
+0:+0
++0:+0
++00:+0
++0 0 0:+0
+000000 0000000 00000:+0
+-0:+0
+-0000:+0
++1:+1
++01:+1
++001:+1
++00000100000:+100000
+123456789:+123456789
+-1:-1
+-01:-1
+-001:-1
+-123456789:-123456789
+-00000100000:-100000
+&bneg
+abd:NaN
++0:+0
++1:-1
+-1:+1
++123456789:-123456789
+-123456789:+123456789
+&babs
+abc:NaN
++0:+0
++1:+1
+-1:+1
++123456789:+123456789
+-123456789:+123456789
+&bcmp
+abc:abc:
+abc:+0:
++0:abc:
++0:+0:0
+-1:+0:-1
++0:-1:1
++1:+0:1
++0:+1:-1
+-1:+1:-1
++1:-1:1
+-1:-1:0
++1:+1:0
++123:+123:0
++123:+12:1
++12:+123:-1
+-123:-123:0
+-123:-12:-1
+-12:-123:1
++123:+124:-1
++124:+123:1
+-123:-124:1
+-124:-123:-1
+&badd
+abc:abc:NaN
+abc:+0:NaN
++0:abc:NaN
++0:+0:+0
++1:+0:+1
++0:+1:+1
++1:+1:+2
+-1:+0:-1
++0:-1:-1
+-1:-1:-2
+-1:+1:+0
++1:-1:+0
++9:+1:+10
++99:+1:+100
++999:+1:+1000
++9999:+1:+10000
++99999:+1:+100000
++999999:+1:+1000000
++9999999:+1:+10000000
++99999999:+1:+100000000
++999999999:+1:+1000000000
++9999999999:+1:+10000000000
++99999999999:+1:+100000000000
++10:-1:+9
++100:-1:+99
++1000:-1:+999
++10000:-1:+9999
++100000:-1:+99999
++1000000:-1:+999999
++10000000:-1:+9999999
++100000000:-1:+99999999
++1000000000:-1:+999999999
++10000000000:-1:+9999999999
++123456789:+987654321:+1111111110
+-123456789:+987654321:+864197532
+-123456789:-987654321:-1111111110
++123456789:-987654321:-864197532
+&bsub
+abc:abc:NaN
+abc:+0:NaN
++0:abc:NaN
++0:+0:+0
++1:+0:+1
++0:+1:-1
++1:+1:+0
+-1:+0:-1
++0:-1:+1
+-1:-1:+0
+-1:+1:-2
++1:-1:+2
++9:+1:+8
++99:+1:+98
++999:+1:+998
++9999:+1:+9998
++99999:+1:+99998
++999999:+1:+999998
++9999999:+1:+9999998
++99999999:+1:+99999998
++999999999:+1:+999999998
++9999999999:+1:+9999999998
++99999999999:+1:+99999999998
++10:-1:+11
++100:-1:+101
++1000:-1:+1001
++10000:-1:+10001
++100000:-1:+100001
++1000000:-1:+1000001
++10000000:-1:+10000001
++100000000:-1:+100000001
++1000000000:-1:+1000000001
++10000000000:-1:+10000000001
++123456789:+987654321:-864197532
+-123456789:+987654321:-1111111110
+-123456789:-987654321:+864197532
++123456789:-987654321:+1111111110
+&bmul
+abc:abc:NaN
+abc:+0:NaN
++0:abc:NaN
++0:+0:+0
++0:+1:+0
++1:+0:+0
++0:-1:+0
+-1:+0:+0
++123456789123456789:+0:+0
++0:+123456789123456789:+0
+-1:-1:+1
+-1:+1:-1
++1:-1:-1
++1:+1:+1
++2:+3:+6
+-2:+3:-6
++2:-3:-6
+-2:-3:+6
++111:+111:+12321
++10101:+10101:+102030201
++1001001:+1001001:+1002003002001
++100010001:+100010001:+10002000300020001
++10000100001:+10000100001:+100002000030000200001
++11111111111:+9:+99999999999
++22222222222:+9:+199999999998
++33333333333:+9:+299999999997
++44444444444:+9:+399999999996
++55555555555:+9:+499999999995
++66666666666:+9:+599999999994
++77777777777:+9:+699999999993
++88888888888:+9:+799999999992
++99999999999:+9:+899999999991
+&bdiv
+abc:abc:NaN
+abc:+1:abc:NaN
++1:abc:NaN
++0:+0:NaN
++0:+1:+0
++1:+0:NaN
++0:-1:+0
+-1:+0:NaN
++1:+1:+1
+-1:-1:+1
++1:-1:-1
+-1:+1:-1
++1:+2:+0
++2:+1:+2
++1000000000:+9:+111111111
++2000000000:+9:+222222222
++3000000000:+9:+333333333
++4000000000:+9:+444444444
++5000000000:+9:+555555555
++6000000000:+9:+666666666
++7000000000:+9:+777777777
++8000000000:+9:+888888888
++9000000000:+9:+1000000000
++35500000:+113:+314159
++71000000:+226:+314159
++106500000:+339:+314159
++1000000000:+3:+333333333
++10:+5:+2
++100:+4:+25
++1000:+8:+125
++10000:+16:+625
++999999999999:+9:+111111111111
++999999999999:+99:+10101010101
++999999999999:+999:+1001001001
++999999999999:+9999:+100010001
++999999999999999:+99999:+10000100001
+&bmod
+abc:abc:NaN
+abc:+1:abc:NaN
++1:abc:NaN
++0:+0:NaN
++0:+1:+0
++1:+0:NaN
++0:-1:+0
+-1:+0:NaN
++1:+1:+0
+-1:-1:+0
++1:-1:+0
+-1:+1:+0
++1:+2:+1
++2:+1:+0
++1000000000:+9:+1
++2000000000:+9:+2
++3000000000:+9:+3
++4000000000:+9:+4
++5000000000:+9:+5
++6000000000:+9:+6
++7000000000:+9:+7
++8000000000:+9:+8
++9000000000:+9:+0
++35500000:+113:+33
++71000000:+226:+66
++106500000:+339:+99
++1000000000:+3:+1
++10:+5:+0
++100:+4:+0
++1000:+8:+0
++10000:+16:+0
++999999999999:+9:+0
++999999999999:+99:+0
++999999999999:+999:+0
++999999999999:+9999:+0
++999999999999999:+99999:+0
+&bgcd
+abc:abc:NaN
+abc:+0:NaN
++0:abc:NaN
++0:+0:+0
++0:+1:+1
++1:+0:+1
++1:+1:+1
++2:+3:+1
++3:+2:+1
++100:+625:+25
++4096:+81:+1
diff --git a/gnu/usr.bin/perl/perl/t/op/Op.dbmx.db b/gnu/usr.bin/perl/perl/t/op/Op.dbmx.db
new file mode 100644
index 0000000..33a09bb
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/Op.dbmx.db
Binary files differ
diff --git a/gnu/usr.bin/perl/perl/t/op/append.t b/gnu/usr.bin/perl/perl/t/op/append.t
new file mode 100755
index 0000000..92c6f48
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/append.t
@@ -0,0 +1,21 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/append.t,v 1.1.1.1 1993/08/23 21:30:04 nate Exp $
+
+print "1..3\n";
+
+$a = 'ab' . 'c'; # compile time
+$b = 'def';
+
+$c = $a . $b;
+print "#1\t:$c: eq :abcdef:\n";
+if ($c eq 'abcdef') {print "ok 1\n";} else {print "not ok 1\n";}
+
+$c .= 'xyz';
+print "#2\t:$c: eq :abcdefxyz:\n";
+if ($c eq 'abcdefxyz') {print "ok 2\n";} else {print "not ok 2\n";}
+
+$_ = $a;
+$_ .= $b;
+print "#3\t:$_: eq :abcdef:\n";
+if ($_ eq 'abcdef') {print "ok 3\n";} else {print "not ok 3\n";}
diff --git a/gnu/usr.bin/perl/perl/t/op/array.t b/gnu/usr.bin/perl/perl/t/op/array.t
new file mode 100755
index 0000000..39e05e3
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/array.t
@@ -0,0 +1,120 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/array.t,v 1.1.1.1 1993/08/23 21:30:02 nate Exp $
+
+print "1..36\n";
+
+@ary = (1,2,3,4,5);
+if (join('',@ary) eq '12345') {print "ok 1\n";} else {print "not ok 1\n";}
+
+$tmp = $ary[$#ary]; --$#ary;
+if ($tmp == 5) {print "ok 2\n";} else {print "not ok 2\n";}
+if ($#ary == 3) {print "ok 3\n";} else {print "not ok 3\n";}
+if (join('',@ary) eq '1234') {print "ok 4\n";} else {print "not ok 4\n";}
+
+$[ = 1;
+@ary = (1,2,3,4,5);
+if (join('',@ary) eq '12345') {print "ok 5\n";} else {print "not ok 5\n";}
+
+$tmp = $ary[$#ary]; --$#ary;
+if ($tmp == 5) {print "ok 6\n";} else {print "not ok 6\n";}
+if ($#ary == 4) {print "ok 7\n";} else {print "not ok 7\n";}
+if (join('',@ary) eq '1234') {print "ok 8\n";} else {print "not ok 8\n";}
+
+if ($ary[5] eq '') {print "ok 9\n";} else {print "not ok 9\n";}
+
+$#ary += 1; # see if we can recover element 5
+if ($#ary == 5) {print "ok 10\n";} else {print "not ok 10\n";}
+if ($ary[5] == 5) {print "ok 11\n";} else {print "not ok 11\n";}
+
+$[ = 0;
+@foo = ();
+$r = join(',', $#foo, @foo);
+if ($r eq "-1") {print "ok 12\n";} else {print "not ok 12 $r\n";}
+$foo[0] = '0';
+$r = join(',', $#foo, @foo);
+if ($r eq "0,0") {print "ok 13\n";} else {print "not ok 13 $r\n";}
+$foo[2] = '2';
+$r = join(',', $#foo, @foo);
+if ($r eq "2,0,,2") {print "ok 14\n";} else {print "not ok 14 $r\n";}
+@bar = ();
+$bar[0] = '0';
+$bar[1] = '1';
+$r = join(',', $#bar, @bar);
+if ($r eq "1,0,1") {print "ok 15\n";} else {print "not ok 15 $r\n";}
+@bar = ();
+$r = join(',', $#bar, @bar);
+if ($r eq "-1") {print "ok 16\n";} else {print "not ok 16 $r\n";}
+$bar[0] = '0';
+$r = join(',', $#bar, @bar);
+if ($r eq "0,0") {print "ok 17\n";} else {print "not ok 17 $r\n";}
+$bar[2] = '2';
+$r = join(',', $#bar, @bar);
+if ($r eq "2,0,,2") {print "ok 18\n";} else {print "not ok 18 $r\n";}
+reset 'b';
+@bar = ();
+$bar[0] = '0';
+$r = join(',', $#bar, @bar);
+if ($r eq "0,0") {print "ok 19\n";} else {print "not ok 19 $r\n";}
+$bar[2] = '2';
+$r = join(',', $#bar, @bar);
+if ($r eq "2,0,,2") {print "ok 20\n";} else {print "not ok 20 $r\n";}
+
+$foo = 'now is the time';
+if (($F1,$F2,$Etc) = ($foo =~ /^(\S+)\s+(\S+)\s*(.*)/)) {
+ if ($F1 eq 'now' && $F2 eq 'is' && $Etc eq 'the time') {
+ print "ok 21\n";
+ }
+ else {
+ print "not ok 21\n";
+ }
+}
+else {
+ print "not ok 21\n";
+}
+
+$foo = 'lskjdf';
+if ($cnt = (($F1,$F2,$Etc) = ($foo =~ /^(\S+)\s+(\S+)\s*(.*)/))) {
+ print "not ok 22 $cnt $F1:$F2:$Etc\n";
+}
+else {
+ print "ok 22\n";
+}
+
+%foo = ('blurfl','dyick','foo','bar','etc.','etc.');
+%bar = %foo;
+print $bar{'foo'} eq 'bar' ? "ok 23\n" : "not ok 23\n";
+%bar = ();
+print $bar{'foo'} eq '' ? "ok 24\n" : "not ok 24\n";
+(%bar,$a,$b) = (%foo,'how','now');
+print $bar{'foo'} eq 'bar' ? "ok 25\n" : "not ok 25\n";
+print $bar{'how'} eq 'now' ? "ok 26\n" : "not ok 26\n";
+@bar{keys %foo} = values %foo;
+print $bar{'foo'} eq 'bar' ? "ok 27\n" : "not ok 27\n";
+print $bar{'how'} eq 'now' ? "ok 28\n" : "not ok 28\n";
+
+@foo = grep(/e/,split(' ','now is the time for all good men to come to'));
+print join(' ',@foo) eq 'the time men come' ? "ok 29\n" : "not ok 29\n";
+
+@foo = grep(!/e/,split(' ','now is the time for all good men to come to'));
+print join(' ',@foo) eq 'now is for all good to to' ? "ok 30\n" : "not ok 30\n";
+
+$foo = join('',('a','b','c','d','e','f')[0..5]);
+print $foo eq 'abcdef' ? "ok 31\n" : "not ok 31\n";
+
+$foo = join('',('a','b','c','d','e','f')[0..1]);
+print $foo eq 'ab' ? "ok 32\n" : "not ok 32\n";
+
+$foo = join('',('a','b','c','d','e','f')[6]);
+print $foo eq '' ? "ok 33\n" : "not ok 33\n";
+
+@foo = ('a','b','c','d','e','f')[0,2,4];
+@bar = ('a','b','c','d','e','f')[1,3,5];
+$foo = join('',(@foo,@bar)[0..5]);
+print $foo eq 'acebdf' ? "ok 34\n" : "not ok 34\n";
+
+$foo = ('a','b','c','d','e','f')[0,2,4];
+print $foo eq 'e' ? "ok 35\n" : "not ok 35\n";
+
+$foo = ('a','b','c','d','e','f')[1];
+print $foo eq 'b' ? "ok 36\n" : "not ok 36\n";
diff --git a/gnu/usr.bin/perl/perl/t/op/auto.t b/gnu/usr.bin/perl/perl/t/op/auto.t
new file mode 100755
index 0000000..5301f93
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/auto.t
@@ -0,0 +1,48 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/auto.t,v 1.1.1.1 1993/08/23 21:30:01 nate Exp $
+
+print "1..34\n";
+
+$x = 10000;
+if (0 + ++$x - 1 == 10000) { print "ok 1\n";} else {print "not ok 1\n";}
+if (0 + $x-- - 1 == 10000) { print "ok 2\n";} else {print "not ok 2\n";}
+if (1 * $x == 10000) { print "ok 3\n";} else {print "not ok 3\n";}
+if (0 + $x-- - 0 == 10000) { print "ok 4\n";} else {print "not ok 4\n";}
+if (1 + $x == 10000) { print "ok 5\n";} else {print "not ok 5\n";}
+if (1 + $x++ == 10000) { print "ok 6\n";} else {print "not ok 6\n";}
+if (0 + $x == 10000) { print "ok 7\n";} else {print "not ok 7\n";}
+if (0 + --$x + 1 == 10000) { print "ok 8\n";} else {print "not ok 8\n";}
+if (0 + ++$x + 0 == 10000) { print "ok 9\n";} else {print "not ok 9\n";}
+if ($x == 10000) { print "ok 10\n";} else {print "not ok 10\n";}
+
+$x[0] = 10000;
+if (0 + ++$x[0] - 1 == 10000) { print "ok 11\n";} else {print "not ok 11\n";}
+if (0 + $x[0]-- - 1 == 10000) { print "ok 12\n";} else {print "not ok 12\n";}
+if (1 * $x[0] == 10000) { print "ok 13\n";} else {print "not ok 13\n";}
+if (0 + $x[0]-- - 0 == 10000) { print "ok 14\n";} else {print "not ok 14\n";}
+if (1 + $x[0] == 10000) { print "ok 15\n";} else {print "not ok 15\n";}
+if (1 + $x[0]++ == 10000) { print "ok 16\n";} else {print "not ok 16\n";}
+if (0 + $x[0] == 10000) { print "ok 17\n";} else {print "not ok 17\n";}
+if (0 + --$x[0] + 1 == 10000) { print "ok 18\n";} else {print "not ok 18\n";}
+if (0 + ++$x[0] + 0 == 10000) { print "ok 19\n";} else {print "not ok 19\n";}
+if ($x[0] == 10000) { print "ok 20\n";} else {print "not ok 20\n";}
+
+$x{0} = 10000;
+if (0 + ++$x{0} - 1 == 10000) { print "ok 21\n";} else {print "not ok 21\n";}
+if (0 + $x{0}-- - 1 == 10000) { print "ok 22\n";} else {print "not ok 22\n";}
+if (1 * $x{0} == 10000) { print "ok 23\n";} else {print "not ok 23\n";}
+if (0 + $x{0}-- - 0 == 10000) { print "ok 24\n";} else {print "not ok 24\n";}
+if (1 + $x{0} == 10000) { print "ok 25\n";} else {print "not ok 25\n";}
+if (1 + $x{0}++ == 10000) { print "ok 26\n";} else {print "not ok 26\n";}
+if (0 + $x{0} == 10000) { print "ok 27\n";} else {print "not ok 27\n";}
+if (0 + --$x{0} + 1 == 10000) { print "ok 28\n";} else {print "not ok 28\n";}
+if (0 + ++$x{0} + 0 == 10000) { print "ok 29\n";} else {print "not ok 29\n";}
+if ($x{0} == 10000) { print "ok 30\n";} else {print "not ok 30\n";}
+
+# test magical autoincrement
+
+if (++($foo = '99') eq '100') {print "ok 31\n";} else {print "not ok 31\n";}
+if (++($foo = 'a0') eq 'a1') {print "ok 32\n";} else {print "not ok 32\n";}
+if (++($foo = 'Az') eq 'Ba') {print "ok 33\n";} else {print "not ok 33\n";}
+if (++($foo = 'zz') eq 'aaa') {print "ok 34\n";} else {print "not ok 34\n";}
diff --git a/gnu/usr.bin/perl/perl/t/op/chop.t b/gnu/usr.bin/perl/perl/t/op/chop.t
new file mode 100755
index 0000000..d691d73
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/chop.t
@@ -0,0 +1,30 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/chop.t,v 1.1.1.1 1993/08/23 21:30:04 nate Exp $
+
+print "1..4\n";
+
+# optimized
+
+$_ = 'abc';
+$c = do foo();
+if ($c . $_ eq 'cab') {print "ok 1\n";} else {print "not ok 1 $c$_\n";}
+
+# unoptimized
+
+$_ = 'abc';
+$c = chop($_);
+if ($c . $_ eq 'cab') {print "ok 2\n";} else {print "not ok 2\n";}
+
+sub foo {
+ chop;
+}
+
+@foo = ("hi \n","there\n","!\n");
+@bar = @foo;
+chop(@bar);
+print join('',@bar) eq 'hi there!' ? "ok 3\n" : "not ok 3\n";
+
+$foo = "\n";
+chop($foo,@foo);
+print join('',$foo,@foo) eq 'hi there!' ? "ok 4\n" : "not ok 4\n";
diff --git a/gnu/usr.bin/perl/perl/t/op/cond.t b/gnu/usr.bin/perl/perl/t/op/cond.t
new file mode 100755
index 0000000..054a5ff
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/cond.t
@@ -0,0 +1,12 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/cond.t,v 1.1.1.1 1993/08/23 21:30:04 nate Exp $
+
+print "1..4\n";
+
+print 1 ? "ok 1\n" : "not ok 1\n"; # compile time
+print 0 ? "not ok 2\n" : "ok 2\n";
+
+$x = 1;
+print $x ? "ok 3\n" : "not ok 3\n"; # run time
+print !$x ? "not ok 4\n" : "ok 4\n";
diff --git a/gnu/usr.bin/perl/perl/t/op/dbm.t b/gnu/usr.bin/perl/perl/t/op/dbm.t
new file mode 100755
index 0000000..23d4d98
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/dbm.t
@@ -0,0 +1,106 @@
+#!./perl
+
+# $RCSfile: dbm.t,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:02 $
+
+if (!-r '/usr/include/dbm.h' && !-r '/usr/include/ndbm.h'
+ && !-r '/usr/include/rpcsvc/dbm.h') {
+ print "1..0\n";
+ exit;
+}
+
+print "1..12\n";
+
+unlink <Op.dbmx.*>;
+umask(0);
+print (dbmopen(h,'Op.dbmx',0640) ? "ok 1\n" : "not ok 1\n");
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat('Op.dbmx.pag');
+print (($mode & 0777) == 0640 ? "ok 2\n" : "not ok 2\n");
+while (($key,$value) = each(h)) {
+ $i++;
+}
+print (!$i ? "ok 3\n" : "not ok 3\n");
+
+$h{'goner1'} = 'snork';
+
+$h{'abc'} = 'ABC';
+$h{'def'} = 'DEF';
+$h{'jkl','mno'} = "JKL\034MNO";
+$h{'a',2,3,4,5} = join("\034",'A',2,3,4,5);
+$h{'a'} = 'A';
+$h{'b'} = 'B';
+$h{'c'} = 'C';
+$h{'d'} = 'D';
+$h{'e'} = 'E';
+$h{'f'} = 'F';
+$h{'g'} = 'G';
+$h{'h'} = 'H';
+$h{'i'} = 'I';
+
+$h{'goner2'} = 'snork';
+delete $h{'goner2'};
+
+dbmclose(h);
+print (dbmopen(h,'Op.dbmx',0640) ? "ok 4\n" : "not ok 4\n");
+
+$h{'j'} = 'J';
+$h{'k'} = 'K';
+$h{'l'} = 'L';
+$h{'m'} = 'M';
+$h{'n'} = 'N';
+$h{'o'} = 'O';
+$h{'p'} = 'P';
+$h{'q'} = 'Q';
+$h{'r'} = 'R';
+$h{'s'} = 'S';
+$h{'t'} = 'T';
+$h{'u'} = 'U';
+$h{'v'} = 'V';
+$h{'w'} = 'W';
+$h{'x'} = 'X';
+$h{'y'} = 'Y';
+$h{'z'} = 'Z';
+
+$h{'goner3'} = 'snork';
+
+delete $h{'goner1'};
+delete $h{'goner3'};
+
+@keys = keys(%h);
+@values = values(%h);
+
+if ($#keys == 29 && $#values == 29) {print "ok 5\n";} else {print "not ok 5\n";}
+
+while (($key,$value) = each(h)) {
+ if ($key eq $keys[$i] && $value eq $values[$i] && $key gt $value) {
+ $key =~ y/a-z/A-Z/;
+ $i++ if $key eq $value;
+ }
+}
+
+if ($i == 30) {print "ok 6\n";} else {print "not ok 6\n";}
+
+@keys = ('blurfl', keys(h), 'dyick');
+if ($#keys == 31) {print "ok 7\n";} else {print "not ok 7\n";}
+
+$h{'foo'} = '';
+$h{''} = 'bar';
+
+# check cache overflow and numeric keys and contents
+$ok = 1;
+for ($i = 1; $i < 200; $i++) { $h{$i + 0} = $i + 0; }
+for ($i = 1; $i < 200; $i++) { $ok = 0 unless $h{$i} == $i; }
+print ($ok ? "ok 8\n" : "not ok 8\n");
+
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat('Op.dbmx.pag');
+print ($size > 0 ? "ok 9\n" : "not ok 9\n");
+
+@h{0..200} = 200..400;
+@foo = @h{0..200};
+print join(':',200..400) eq join(':',@foo) ? "ok 10\n" : "not ok 10\n";
+
+print ($h{'foo'} eq '' ? "ok 11\n" : "not ok 11\n");
+print ($h{''} eq 'bar' ? "ok 12\n" : "not ok 12\n");
+
+unlink 'Op.dbmx.dir', 'Op.dbmx.pag';
diff --git a/gnu/usr.bin/perl/perl/t/op/delete.t b/gnu/usr.bin/perl/perl/t/op/delete.t
new file mode 100755
index 0000000..e4e1fea
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/delete.t
@@ -0,0 +1,29 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/delete.t,v 1.1.1.1 1993/08/23 21:30:03 nate Exp $
+
+print "1..6\n";
+
+$foo{1} = 'a';
+$foo{2} = 'b';
+$foo{3} = 'c';
+
+$foo = delete $foo{2};
+
+if ($foo eq 'b') {print "ok 1\n";} else {print "not ok 1 $foo\n";}
+if ($foo{2} eq '') {print "ok 2\n";} else {print "not ok 2 $foo{2}\n";}
+if ($foo{1} eq 'a') {print "ok 3\n";} else {print "not ok 3\n";}
+if ($foo{3} eq 'c') {print "ok 4\n";} else {print "not ok 4\n";}
+
+$foo = join('',values(foo));
+if ($foo eq 'ac' || $foo eq 'ca') {print "ok 5\n";} else {print "not ok 5\n";}
+
+foreach $key (keys foo) {
+ delete $foo{$key};
+}
+
+$foo{'foo'} = 'x';
+$foo{'bar'} = 'y';
+
+$foo = join('',values(foo));
+if ($foo eq 'xy' || $foo eq 'yx') {print "ok 6\n";} else {print "not ok 6\n";}
diff --git a/gnu/usr.bin/perl/perl/t/op/do.t b/gnu/usr.bin/perl/perl/t/op/do.t
new file mode 100755
index 0000000..370012c
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/do.t
@@ -0,0 +1,44 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/do.t,v 1.1.1.1 1993/08/23 21:30:03 nate Exp $
+
+sub foo1
+{
+ print $_[0];
+ 'value';
+}
+
+sub foo2
+{
+ shift(_);
+ print $_[0];
+ $x = 'value';
+ $x;
+}
+
+print "1..15\n";
+
+$_[0] = "not ok 1\n";
+$result = do foo1("ok 1\n");
+print "#2\t:$result: eq :value:\n";
+if ($result EQ 'value') { print "ok 2\n"; } else { print "not ok 2\n"; }
+if ($_[0] EQ "not ok 1\n") { print "ok 3\n"; } else { print "not ok 3\n"; }
+
+$_[0] = "not ok 4\n";
+$result = do foo2("not ok 4\n","ok 4\n","not ok 4\n");
+print "#5\t:$result: eq :value:\n";
+if ($result EQ 'value') { print "ok 5\n"; } else { print "not ok 5\n"; }
+if ($_[0] EQ "not ok 4\n") { print "ok 6\n"; } else { print "not ok 6\n"; }
+
+$result = do{print "ok 7\n"; 'value';};
+print "#8\t:$result: eq :value:\n";
+if ($result EQ 'value') { print "ok 8\n"; } else { print "not ok 8\n"; }
+
+sub blather {
+ print @_;
+}
+
+do blather("ok 9\n","ok 10\n");
+@x = ("ok 11\n", "ok 12\n");
+@y = ("ok 14\n", "ok 15\n");
+do blather(@x,"ok 13\n",@y);
diff --git a/gnu/usr.bin/perl/perl/t/op/each.t b/gnu/usr.bin/perl/perl/t/op/each.t
new file mode 100755
index 0000000..532d1b0
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/each.t
@@ -0,0 +1,53 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/each.t,v 1.1.1.1 1993/08/23 21:30:03 nate Exp $
+
+print "1..3\n";
+
+$h{'abc'} = 'ABC';
+$h{'def'} = 'DEF';
+$h{'jkl','mno'} = "JKL\034MNO";
+$h{'a',2,3,4,5} = join("\034",'A',2,3,4,5);
+$h{'a'} = 'A';
+$h{'b'} = 'B';
+$h{'c'} = 'C';
+$h{'d'} = 'D';
+$h{'e'} = 'E';
+$h{'f'} = 'F';
+$h{'g'} = 'G';
+$h{'h'} = 'H';
+$h{'i'} = 'I';
+$h{'j'} = 'J';
+$h{'k'} = 'K';
+$h{'l'} = 'L';
+$h{'m'} = 'M';
+$h{'n'} = 'N';
+$h{'o'} = 'O';
+$h{'p'} = 'P';
+$h{'q'} = 'Q';
+$h{'r'} = 'R';
+$h{'s'} = 'S';
+$h{'t'} = 'T';
+$h{'u'} = 'U';
+$h{'v'} = 'V';
+$h{'w'} = 'W';
+$h{'x'} = 'X';
+$h{'y'} = 'Y';
+$h{'z'} = 'Z';
+
+@keys = keys %h;
+@values = values %h;
+
+if ($#keys == 29 && $#values == 29) {print "ok 1\n";} else {print "not ok 1\n";}
+
+while (($key,$value) = each(h)) {
+ if ($key eq $keys[$i] && $value eq $values[$i] && $key gt $value) {
+ $key =~ y/a-z/A-Z/;
+ $i++ if $key eq $value;
+ }
+}
+
+if ($i == 30) {print "ok 2\n";} else {print "not ok 2\n";}
+
+@keys = ('blurfl', keys(%h), 'dyick');
+if ($#keys == 31) {print "ok 3\n";} else {print "not ok 3\n";}
diff --git a/gnu/usr.bin/perl/perl/t/op/eval.t b/gnu/usr.bin/perl/perl/t/op/eval.t
new file mode 100755
index 0000000..b21b44d
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/eval.t
@@ -0,0 +1,57 @@
+#!./perl
+
+# $RCSfile: eval.t,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:02 $
+
+print "1..16\n";
+
+eval 'print "ok 1\n";';
+
+if ($@ eq '') {print "ok 2\n";} else {print "not ok 2\n";}
+
+eval "\$foo\n = # this is a comment\n'ok 3';";
+print $foo,"\n";
+
+eval "\$foo\n = # this is a comment\n'ok 4\n';";
+print $foo;
+
+print eval '
+$foo ='; # this tests for a call through yyerror()
+if ($@ =~ /line 2/) {print "ok 5\n";} else {print "not ok 5\n";}
+
+print eval '$foo = /'; # this tests for a call through fatal()
+if ($@ =~ /Search/) {print "ok 6\n";} else {print "not ok 6\n";}
+
+print eval '"ok 7\n";';
+
+# calculate a factorial with recursive evals
+
+$foo = 5;
+$fact = 'if ($foo <= 1) {1;} else {push(@x,$foo--); (eval $fact) * pop(@x);}';
+$ans = eval $fact;
+if ($ans == 120) {print "ok 8\n";} else {print "not ok 8\n";}
+
+$foo = 5;
+$fact = 'local($foo)=$foo; $foo <= 1 ? 1 : $foo-- * (eval $fact);';
+$ans = eval $fact;
+if ($ans == 120) {print "ok 9\n";} else {print "not ok 9 $ans\n";}
+
+open(try,'>Op.eval');
+print try 'print "ok 10\n"; unlink "Op.eval";',"\n";
+close try;
+
+do 'Op.eval'; print $@;
+
+# Test the singlequoted eval optimizer
+
+$i = 11;
+for (1..3) {
+ eval 'print "ok ", $i++, "\n"';
+}
+
+eval {
+ print "ok 14\n";
+ die "ok 16\n";
+ 1;
+} || print "ok 15\n$@";
+
+
diff --git a/gnu/usr.bin/perl/perl/t/op/exec.t b/gnu/usr.bin/perl/perl/t/op/exec.t
new file mode 100755
index 0000000..69909f7
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/exec.t
@@ -0,0 +1,21 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/exec.t,v 1.1.1.1 1993/08/23 21:30:03 nate Exp $
+
+$| = 1; # flush stdout
+print "1..8\n";
+
+print "not ok 1\n" if system "echo ok \\1"; # shell interpreted
+print "not ok 2\n" if system "echo ok 2"; # split and directly called
+print "not ok 3\n" if system "echo", "ok", "3"; # directly called
+
+if (system "true") {print "not ok 4\n";} else {print "ok 4\n";}
+
+if ((system "/bin/sh -c 'exit 1'") != 256) { print "not "; }
+print "ok 5\n";
+
+if ((system "lskdfj") == 255 << 8) {print "ok 6\n";} else {print "not ok 6\n";}
+
+unless (exec "lskdjfalksdjfdjfkls") {print "ok 7\n";} else {print "not ok 7\n";}
+
+exec "echo","ok","8";
diff --git a/gnu/usr.bin/perl/perl/t/op/exp.t b/gnu/usr.bin/perl/perl/t/op/exp.t
new file mode 100755
index 0000000..2195e54
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/exp.t
@@ -0,0 +1,27 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/exp.t,v 1.1.1.1 1993/08/23 21:30:03 nate Exp $
+
+print "1..6\n";
+
+# compile time evaluation
+
+$s = sqrt(2);
+if (substr($s,0,5) eq '1.414') {print "ok 1\n";} else {print "not ok 1\n";}
+
+$s = exp(1);
+if (substr($s,0,7) eq '2.71828') {print "ok 2\n";} else {print "not ok 2\n";}
+
+if (exp(log(1)) == 1) {print "ok 3\n";} else {print "not ok 3\n";}
+
+# run time evaluation
+
+$x1 = 1;
+$x2 = 2;
+$s = sqrt($x2);
+if (substr($s,0,5) eq '1.414') {print "ok 4\n";} else {print "not ok 4\n";}
+
+$s = exp($x1);
+if (substr($s,0,7) eq '2.71828') {print "ok 5\n";} else {print "not ok 5\n";}
+
+if (exp(log($x1)) == 1) {print "ok 6\n";} else {print "not ok 6\n";}
diff --git a/gnu/usr.bin/perl/perl/t/op/flip.t b/gnu/usr.bin/perl/perl/t/op/flip.t
new file mode 100755
index 0000000..74ba508
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/flip.t
@@ -0,0 +1,26 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/flip.t,v 1.1.1.1 1993/08/23 21:30:04 nate Exp $
+
+print "1..8\n";
+
+@a = (1,2,3,4,5,6,7,8,9,10,11,12);
+
+while ($_ = shift(a)) {
+ if ($x = /4/../8/) { $z = $x; print "ok ", $x + 0, "\n"; }
+ $y .= /1/../2/;
+}
+
+if ($z eq '5E0') {print "ok 6\n";} else {print "not ok 6\n";}
+
+if ($y eq '12E0123E0') {print "ok 7\n";} else {print "not ok 7\n";}
+
+@a = ('a','b','c','d','e','f','g');
+
+open(of,'../Makefile');
+while (<of>) {
+ (3 .. 5) && $foo .= $_;
+}
+$x = ($foo =~ y/\n/\n/);
+
+if ($x eq 3) {print "ok 8\n";} else {print "not ok 8 $x:$foo:\n";}
diff --git a/gnu/usr.bin/perl/perl/t/op/fork.t b/gnu/usr.bin/perl/perl/t/op/fork.t
new file mode 100755
index 0000000..10b54a2
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/fork.t
@@ -0,0 +1,16 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/fork.t,v 1.1.1.1 1993/08/23 21:30:04 nate Exp $
+
+$| = 1;
+print "1..2\n";
+
+if ($cid = fork) {
+ sleep 2;
+ if ($result = (kill 9, $cid)) {print "ok 2\n";} else {print "not ok 2 $result\n";}
+}
+else {
+ $| = 1;
+ print "ok 1\n";
+ sleep 10;
+}
diff --git a/gnu/usr.bin/perl/perl/t/op/glob.t b/gnu/usr.bin/perl/perl/t/op/glob.t
new file mode 100755
index 0000000..68b0844
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/glob.t
@@ -0,0 +1,22 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/glob.t,v 1.1.1.1 1993/08/23 21:30:04 nate Exp $
+
+print "1..4\n";
+
+@ops = <op/*>;
+$list = join(' ',@ops);
+
+chop($otherway = `echo op/*`);
+
+print $list eq $otherway ? "ok 1\n" : "not ok 1\n$list\n$otherway\n";
+
+print $/ eq "\n" ? "ok 2\n" : "not ok 2\n";
+
+while (<jskdfjskdfj* op/* jskdjfjkosvk*>) {
+ $not = "not " unless $_ eq shift @ops;
+ $not = "not at all " if $/ eq "\0";
+}
+print "${not}ok 3\n";
+
+print $/ eq "\n" ? "ok 4\n" : "not ok 4\n";
diff --git a/gnu/usr.bin/perl/perl/t/op/goto.t b/gnu/usr.bin/perl/perl/t/op/goto.t
new file mode 100755
index 0000000..44ef343
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/goto.t
@@ -0,0 +1,33 @@
+#!./perl
+
+# $RCSfile: goto.t,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:03 $
+
+print "1..3\n";
+
+while (0) {
+ $foo = 1;
+ label1:
+ $foo = 2;
+ goto label2;
+} continue {
+ $foo = 0;
+ goto label4;
+ label3:
+ $foo = 4;
+ goto label4;
+}
+goto label1;
+
+$foo = 3;
+
+label2:
+print "#1\t:$foo: == 2\n";
+if ($foo == 2) {print "ok 1\n";} else {print "not ok 1\n";}
+goto label3;
+
+label4:
+print "#2\t:$foo: == 4\n";
+if ($foo == 4) {print "ok 2\n";} else {print "not ok 2\n";}
+
+$x = `./perl -e 'goto foo;' 2>&1`;
+if ($x =~ /label/) {print "ok 3\n";} else {print "not ok 3\n";}
diff --git a/gnu/usr.bin/perl/perl/t/op/groups.t b/gnu/usr.bin/perl/perl/t/op/groups.t
new file mode 100755
index 0000000..e1520cc
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/groups.t
@@ -0,0 +1,47 @@
+#!./perl
+
+if (! -x '/usr/ucb/groups') {
+ print "1..0\n";
+ exit 0;
+}
+
+print "1..2\n";
+
+$pwgid = $( + 0;
+($pwgnam) = getgrgid($pwgid);
+@basegroup{$pwgid,$pwgnam} = (1,1);
+
+$seen{$pwgid}++;
+
+for (split(' ', $()) {
+ next if $seen{$_}++;
+ ($group) = getgrgid($_);
+ if (defined $group) {
+ push(@gr, $group);
+ }
+ else {
+ push(@gr, $_);
+ }
+}
+
+$gr1 = join(' ', sort @gr);
+
+$gr2 = join(' ', grep(!$basegroup{$_}, sort split(' ',`/usr/ucb/groups`)));
+
+if ($gr1 eq $gr2) {
+ print "ok 1\n";
+}
+else {
+ print "#gr1 is <$gr1>\n";
+ print "#gr2 is <$gr2>\n";
+ print "not ok 1\n";
+}
+
+# multiple 0's indicate GROUPSTYPE is currently long but should be short
+
+if ($pwgid == 0 || $seen{0} < 2) {
+ print "ok 2\n";
+}
+else {
+ print "not ok 2 (groupstype should be type short, not long)\n";
+}
diff --git a/gnu/usr.bin/perl/perl/t/op/index.t b/gnu/usr.bin/perl/perl/t/op/index.t
new file mode 100755
index 0000000..769314b
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/index.t
@@ -0,0 +1,42 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/index.t,v 1.1.1.1 1993/08/23 21:30:01 nate Exp $
+
+print "1..20\n";
+
+
+$foo = 'Now is the time for all good men to come to the aid of their country.';
+
+$first = substr($foo,0,index($foo,'the'));
+print ($first eq "Now is " ? "ok 1\n" : "not ok 1\n");
+
+$last = substr($foo,rindex($foo,'the'),100);
+print ($last eq "their country." ? "ok 2\n" : "not ok 2\n");
+
+$last = substr($foo,index($foo,'Now'),2);
+print ($last eq "No" ? "ok 3\n" : "not ok 3\n");
+
+$last = substr($foo,rindex($foo,'Now'),2);
+print ($last eq "No" ? "ok 4\n" : "not ok 4\n");
+
+$last = substr($foo,index($foo,'.'),100);
+print ($last eq "." ? "ok 5\n" : "not ok 5\n");
+
+$last = substr($foo,rindex($foo,'.'),100);
+print ($last eq "." ? "ok 6\n" : "not ok 6\n");
+
+print index("ababa","a",-1) == 0 ? "ok 7\n" : "not ok 7\n";
+print index("ababa","a",0) == 0 ? "ok 8\n" : "not ok 8\n";
+print index("ababa","a",1) == 2 ? "ok 9\n" : "not ok 9\n";
+print index("ababa","a",2) == 2 ? "ok 10\n" : "not ok 10\n";
+print index("ababa","a",3) == 4 ? "ok 11\n" : "not ok 11\n";
+print index("ababa","a",4) == 4 ? "ok 12\n" : "not ok 12\n";
+print index("ababa","a",5) == -1 ? "ok 13\n" : "not ok 13\n";
+
+print rindex("ababa","a",-1) == -1 ? "ok 14\n" : "not ok 14\n";
+print rindex("ababa","a",0) == 0 ? "ok 15\n" : "not ok 15\n";
+print rindex("ababa","a",1) == 0 ? "ok 16\n" : "not ok 16\n";
+print rindex("ababa","a",2) == 2 ? "ok 17\n" : "not ok 17\n";
+print rindex("ababa","a",3) == 2 ? "ok 18\n" : "not ok 18\n";
+print rindex("ababa","a",4) == 4 ? "ok 19\n" : "not ok 19\n";
+print rindex("ababa","a",5) == 4 ? "ok 20\n" : "not ok 20\n";
diff --git a/gnu/usr.bin/perl/perl/t/op/int.t b/gnu/usr.bin/perl/perl/t/op/int.t
new file mode 100755
index 0000000..09434b8
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/int.t
@@ -0,0 +1,17 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/int.t,v 1.1.1.1 1993/08/23 21:30:04 nate Exp $
+
+print "1..4\n";
+
+# compile time evaluation
+
+if (int(1.234) == 1) {print "ok 1\n";} else {print "not ok 1\n";}
+
+if (int(-1.234) == -1) {print "ok 2\n";} else {print "not ok 2\n";}
+
+# run time evaluation
+
+$x = 1.234;
+if (int($x) == 1) {print "ok 3\n";} else {print "not ok 3\n";}
+if (int(-$x) == -1) {print "ok 4\n";} else {print "not ok 4\n";}
diff --git a/gnu/usr.bin/perl/perl/t/op/join.t b/gnu/usr.bin/perl/perl/t/op/join.t
new file mode 100755
index 0000000..a6678e9
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/join.t
@@ -0,0 +1,12 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/join.t,v 1.1.1.1 1993/08/23 21:30:02 nate Exp $
+
+print "1..3\n";
+
+@x = (1, 2, 3);
+if (join(':',@x) eq '1:2:3') {print "ok 1\n";} else {print "not ok 1\n";}
+
+if (join('',1,2,3) eq '123') {print "ok 2\n";} else {print "not ok 2\n";}
+
+if (join(':',split(/ /,"1 2 3")) eq '1:2:3') {print "ok 3\n";} else {print "not ok 3\n";}
diff --git a/gnu/usr.bin/perl/perl/t/op/list.t b/gnu/usr.bin/perl/perl/t/op/list.t
new file mode 100755
index 0000000..52b2347
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/list.t
@@ -0,0 +1,83 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/list.t,v 1.1.1.1 1993/08/23 21:30:02 nate Exp $
+
+print "1..27\n";
+
+@foo = (1, 2, 3, 4);
+if ($foo[0] == 1 && $foo[3] == 4) {print "ok 1\n";} else {print "not ok 1\n";}
+
+$_ = join(':',@foo);
+if ($_ eq '1:2:3:4') {print "ok 2\n";} else {print "not ok 2\n";}
+
+($a,$b,$c,$d) = (1,2,3,4);
+if ("$a;$b;$c;$d" eq '1;2;3;4') {print "ok 3\n";} else {print "not ok 3\n";}
+
+($c,$b,$a) = split(/ /,"111 222 333");
+if ("$a;$b;$c" eq '333;222;111') {print "ok 4\n";} else {print "not ok 4\n";}
+
+($a,$b,$c) = ($c,$b,$a);
+if ("$a;$b;$c" eq '111;222;333') {print "ok 5\n";} else {print "not ok 5 $a;$b;$c\n";}
+
+($a, $b) = ($b, $a);
+if ("$a;$b;$c" eq '222;111;333') {print "ok 6\n";} else {print "not ok 6\n";}
+
+($a, $b[1], $c{2}, $d) = (1, 2, 3, 4);
+if ($a eq 1) {print "ok 7\n";} else {print "not ok 7\n";}
+if ($b[1] eq 2) {print "ok 8\n";} else {print "not ok 8\n";}
+if ($c{2} eq 3) {print "ok 9\n";} else {print "not ok 9\n";}
+if ($d eq 4) {print "ok 10\n";} else {print "not ok 10\n";}
+
+@foo = (1,2,3,4,5,6,7,8);
+($a, $b, $c, $d) = @foo;
+print "#11 $a;$b;$c;$d eq 1;2;3;4\n";
+if ("$a;$b;$c;$d" eq '1;2;3;4') {print "ok 11\n";} else {print "not ok 11\n";}
+
+@foo = @bar = (1);
+if (join(':',@foo,@bar) eq '1:1') {print "ok 12\n";} else {print "not ok 12\n";}
+
+@foo = ();
+@foo = 1+2+3;
+if (join(':',@foo) eq '6') {print "ok 13\n";} else {print "not ok 13\n";}
+
+for ($x = 0; $x < 3; $x++) {
+ ($a, $b, $c) =
+ $x == 0?
+ ('ok ', 14, "\n"):
+ $x == 1?
+ ('ok ', 15, "\n"):
+ # default
+ ('ok ', 16, "\n");
+
+ print $a,$b,$c;
+}
+
+@a = ($x == 12345 || (1,2,3));
+if (join('',@a) eq '123') {print "ok 17\n";} else {print "not ok 17\n";}
+
+@a = ($x == $x || (4,5,6));
+if (join('',@a) eq '1') {print "ok 18\n";} else {print "not ok 18\n";}
+
+if (join('',1,2,(3,4,5)) eq '12345'){print "ok 19\n";}else{print "not ok 19\n";}
+if (join('',(1,2,3,4,5)) eq '12345'){print "ok 20\n";}else{print "not ok 20\n";}
+if (join('',(1,2,3,4),5) eq '12345'){print "ok 21\n";}else{print "not ok 21\n";}
+if (join('',1,(2,3,4),5) eq '12345'){print "ok 22\n";}else{print "not ok 22\n";}
+if (join('',1,2,(3,4),5) eq '12345'){print "ok 23\n";}else{print "not ok 23\n";}
+if (join('',1,2,3,(4),5) eq '12345'){print "ok 24\n";}else{print "not ok 24\n";}
+
+for ($x = 0; $x < 3; $x++) {
+ ($a, $b, $c) = do {
+ if ($x == 0) {
+ ('ok ', 25, "\n");
+ }
+ elsif ($x == 1) {
+ ('ok ', 26, "\n");
+ }
+ else {
+ ('ok ', 27, "\n");
+ }
+ };
+
+ print $a,$b,$c;
+}
+
diff --git a/gnu/usr.bin/perl/perl/t/op/local.t b/gnu/usr.bin/perl/perl/t/op/local.t
new file mode 100755
index 0000000..67396e7
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/local.t
@@ -0,0 +1,45 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/local.t,v 1.1.1.1 1993/08/23 21:30:03 nate Exp $
+
+print "1..20\n";
+
+sub foo {
+ local($a, $b) = @_;
+ local($c, $d);
+ $c = "ok 3\n";
+ $d = "ok 4\n";
+ { local($a,$c) = ("ok 9\n", "ok 10\n"); ($x, $y) = ($a, $c); }
+ print $a, $b;
+ $c . $d;
+}
+
+$a = "ok 5\n";
+$b = "ok 6\n";
+$c = "ok 7\n";
+$d = "ok 8\n";
+
+print do foo("ok 1\n","ok 2\n");
+
+print $a,$b,$c,$d,$x,$y;
+
+# same thing, only with arrays and associative arrays
+
+sub foo2 {
+ local($a, @b) = @_;
+ local(@c, %d);
+ @c = "ok 13\n";
+ $d{''} = "ok 14\n";
+ { local($a,@c) = ("ok 19\n", "ok 20\n"); ($x, $y) = ($a, @c); }
+ print $a, @b;
+ $c[0] . $d{''};
+}
+
+$a = "ok 15\n";
+@b = "ok 16\n";
+@c = "ok 17\n";
+$d{''} = "ok 18\n";
+
+print do foo2("ok 11\n","ok 12\n");
+
+print $a,@b,@c,%d,$x,$y;
diff --git a/gnu/usr.bin/perl/perl/t/op/magic.t b/gnu/usr.bin/perl/perl/t/op/magic.t
new file mode 100755
index 0000000..1f47a99
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/magic.t
@@ -0,0 +1,32 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/magic.t,v 1.1.1.1 1993/08/23 21:30:03 nate Exp $
+
+$| = 1; # command buffering
+
+print "1..5\n";
+
+eval '$ENV{"foo"} = "hi there";'; # check that ENV is inited inside eval
+if (`echo \$foo` eq "hi there\n") {print "ok 1\n";} else {print "not ok 1\n";}
+
+unlink 'ajslkdfpqjsjfk';
+$! = 0;
+open(foo,'ajslkdfpqjsjfk');
+if ($! == 2) {print "ok 2\n";} else {print "not ok 2\n";}
+
+# the next tests are embedded inside system simply because sh spits out
+# a newline onto stderr when a child process kills itself with SIGINT.
+
+system './perl',
+'-e', '$| = 1; # command buffering',
+
+'-e', '$SIG{"INT"} = "ok3"; kill 2,$$;',
+'-e', '$SIG{"INT"} = "IGNORE"; kill 2,$$; print "ok 4\n";',
+'-e', '$SIG{"INT"} = "DEFAULT"; kill 2,$$; print "not ok\n";',
+
+'-e', 'sub ok3 { print "ok 3\n" if pop(@_) eq "INT"; }';
+
+@val1 = @ENV{keys(%ENV)}; # can we slice ENV?
+@val2 = values(%ENV);
+
+print join(':',@val1) eq join(':',@val2) ? "ok 5\n" : "not ok 5\n";
diff --git a/gnu/usr.bin/perl/perl/t/op/mkdir.t b/gnu/usr.bin/perl/perl/t/op/mkdir.t
new file mode 100755
index 0000000..0290ed4
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/mkdir.t
@@ -0,0 +1,15 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/mkdir.t,v 1.1.1.1 1993/08/23 21:30:04 nate Exp $
+
+print "1..7\n";
+
+`rm -rf blurfl`;
+
+print (mkdir('blurfl',0777) ? "ok 1\n" : "not ok 1\n");
+print (mkdir('blurfl',0777) ? "not ok 2\n" : "ok 2\n");
+print ($! =~ /exist/ ? "ok 3\n" : "not ok 3\n");
+print (-d 'blurfl' ? "ok 4\n" : "not ok 4\n");
+print (rmdir('blurfl') ? "ok 5\n" : "not ok 5\n");
+print (rmdir('blurfl') ? "not ok 6\n" : "ok 6\n");
+print ($! =~ /such|exist/ ? "ok 7\n" : "not ok 7\n");
diff --git a/gnu/usr.bin/perl/perl/t/op/oct.t b/gnu/usr.bin/perl/perl/t/op/oct.t
new file mode 100755
index 0000000..9322cf0
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/oct.t
@@ -0,0 +1,9 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/oct.t,v 1.1.1.1 1993/08/23 21:30:04 nate Exp $
+
+print "1..3\n";
+
+if (oct('01234') == 01234) {print "ok 1\n";} else {print "not ok 1\n";}
+if (oct('0x1234') == 0x1234) {print "ok 2\n";} else {print "not ok 2\n";}
+if (hex('01234') == 0x1234) {print "ok 3\n";} else {print "not ok 3\n";}
diff --git a/gnu/usr.bin/perl/perl/t/op/ord.t b/gnu/usr.bin/perl/perl/t/op/ord.t
new file mode 100755
index 0000000..9d31988
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/ord.t
@@ -0,0 +1,14 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/ord.t,v 1.1.1.1 1993/08/23 21:30:04 nate Exp $
+
+print "1..2\n";
+
+# compile time evaluation
+
+if (ord('A') == 65) {print "ok 1\n";} else {print "not ok 1\n";}
+
+# run time evaluation
+
+$x = 'ABC';
+if (ord($x) == 65) {print "ok 2\n";} else {print "not ok 2\n";}
diff --git a/gnu/usr.bin/perl/perl/t/op/pack.t b/gnu/usr.bin/perl/perl/t/op/pack.t
new file mode 100755
index 0000000..1dfaddf
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/pack.t
@@ -0,0 +1,20 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/pack.t,v 1.1.1.1 1993/08/23 21:30:03 nate Exp $
+
+print "1..3\n";
+
+$format = "c2x5CCxsdila6";
+# Need the expression in here to force ary[5] to be numeric. This avoids
+# test2 failing because ary2 goes str->numeric->str and ary doesn't.
+@ary = (1,-100,127,128,32767,987.654321098 / 100.0,12345,123456,"abcdef");
+$foo = pack($format,@ary);
+@ary2 = unpack($format,$foo);
+
+print ($#ary == $#ary2 ? "ok 1\n" : "not ok 1\n");
+
+$out1=join(':',@ary);
+$out2=join(':',@ary2);
+print ($out1 eq $out2 ? "ok 2\n" : "not ok 2\n");
+
+print ($foo =~ /def/ ? "ok 3\n" : "not ok 3\n");
diff --git a/gnu/usr.bin/perl/perl/t/op/pat.t b/gnu/usr.bin/perl/perl/t/op/pat.t
new file mode 100755
index 0000000..ce9f35c
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/pat.t
@@ -0,0 +1,184 @@
+#!./perl
+
+# $RCSfile: pat.t,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:01 $
+
+print "1..51\n";
+
+$x = "abc\ndef\n";
+
+if ($x =~ /^abc/) {print "ok 1\n";} else {print "not ok 1\n";}
+if ($x !~ /^def/) {print "ok 2\n";} else {print "not ok 2\n";}
+
+$* = 1;
+if ($x =~ /^def/) {print "ok 3\n";} else {print "not ok 3\n";}
+$* = 0;
+
+$_ = '123';
+if (/^([0-9][0-9]*)/) {print "ok 4\n";} else {print "not ok 4\n";}
+
+if ($x =~ /^xxx/) {print "not ok 5\n";} else {print "ok 5\n";}
+if ($x !~ /^abc/) {print "not ok 6\n";} else {print "ok 6\n";}
+
+if ($x =~ /def/) {print "ok 7\n";} else {print "not ok 7\n";}
+if ($x !~ /def/) {print "not ok 8\n";} else {print "ok 8\n";}
+
+if ($x !~ /.def/) {print "ok 9\n";} else {print "not ok 9\n";}
+if ($x =~ /.def/) {print "not ok 10\n";} else {print "ok 10\n";}
+
+if ($x =~ /\ndef/) {print "ok 11\n";} else {print "not ok 11\n";}
+if ($x !~ /\ndef/) {print "not ok 12\n";} else {print "ok 12\n";}
+
+$_ = 'aaabbbccc';
+if (/(a*b*)(c*)/ && $1 eq 'aaabbb' && $2 eq 'ccc') {
+ print "ok 13\n";
+} else {
+ print "not ok 13\n";
+}
+if (/(a+b+c+)/ && $1 eq 'aaabbbccc') {
+ print "ok 14\n";
+} else {
+ print "not ok 14\n";
+}
+
+if (/a+b?c+/) {print "not ok 15\n";} else {print "ok 15\n";}
+
+$_ = 'aaabccc';
+if (/a+b?c+/) {print "ok 16\n";} else {print "not ok 16\n";}
+if (/a*b+c*/) {print "ok 17\n";} else {print "not ok 17\n";}
+
+$_ = 'aaaccc';
+if (/a*b?c*/) {print "ok 18\n";} else {print "not ok 18\n";}
+if (/a*b+c*/) {print "not ok 19\n";} else {print "ok 19\n";}
+
+$_ = 'abcdef';
+if (/bcd|xyz/) {print "ok 20\n";} else {print "not ok 20\n";}
+if (/xyz|bcd/) {print "ok 21\n";} else {print "not ok 21\n";}
+
+if (m|bc/*d|) {print "ok 22\n";} else {print "not ok 22\n";}
+
+if (/^$_$/) {print "ok 23\n";} else {print "not ok 23\n";}
+
+$* = 1; # test 3 only tested the optimized version--this one is for real
+if ("ab\ncd\n" =~ /^cd/) {print "ok 24\n";} else {print "not ok 24\n";}
+$* = 0;
+
+$XXX{123} = 123;
+$XXX{234} = 234;
+$XXX{345} = 345;
+
+@XXX = ('ok 25','not ok 25', 'ok 26','not ok 26','not ok 27');
+while ($_ = shift(XXX)) {
+ ?(.*)? && (print $1,"\n");
+ /not/ && reset;
+ /not ok 26/ && reset 'X';
+}
+
+while (($key,$val) = each(XXX)) {
+ print "not ok 27\n";
+ exit;
+}
+
+print "ok 27\n";
+
+'cde' =~ /[^ab]*/;
+'xyz' =~ //;
+if ($& eq 'xyz') {print "ok 28\n";} else {print "not ok 28\n";}
+
+$foo = '[^ab]*';
+'cde' =~ /$foo/;
+'xyz' =~ //;
+if ($& eq 'xyz') {print "ok 29\n";} else {print "not ok 29\n";}
+
+$foo = '[^ab]*';
+'cde' =~ /$foo/;
+'xyz' =~ /$null/;
+if ($& eq 'xyz') {print "ok 30\n";} else {print "not ok 30\n";}
+
+$_ = 'abcdefghi';
+/def/; # optimized up to cmd
+if ("$`:$&:$'" eq 'abc:def:ghi') {print "ok 31\n";} else {print "not ok 31\n";}
+
+/cde/ + 0; # optimized only to spat
+if ("$`:$&:$'" eq 'ab:cde:fghi') {print "ok 32\n";} else {print "not ok 32\n";}
+
+/[d][e][f]/; # not optimized
+if ("$`:$&:$'" eq 'abc:def:ghi') {print "ok 33\n";} else {print "not ok 33\n";}
+
+$_ = 'now is the {time for all} good men to come to.';
+/ {([^}]*)}/;
+if ($1 eq 'time for all') {print "ok 34\n";} else {print "not ok 34 $1\n";}
+
+$_ = 'xxx {3,4} yyy zzz';
+print /( {3,4})/ ? "ok 35\n" : "not ok 35\n";
+print $1 eq ' ' ? "ok 36\n" : "not ok 36\n";
+print /( {4,})/ ? "not ok 37\n" : "ok 37\n";
+print /( {2,3}.)/ ? "ok 38\n" : "not ok 38\n";
+print $1 eq ' y' ? "ok 39\n" : "not ok 39\n";
+print /(y{2,3}.)/ ? "ok 40\n" : "not ok 40\n";
+print $1 eq 'yyy ' ? "ok 41\n" : "not ok 41\n";
+print /x {3,4}/ ? "not ok 42\n" : "ok 42\n";
+print /^xxx {3,4}/ ? "not ok 43\n" : "ok 43\n";
+
+$_ = "now is the time for all good men to come to.";
+@words = /(\w+)/g;
+print join(':',@words) eq "now:is:the:time:for:all:good:men:to:come:to"
+ ? "ok 44\n"
+ : "not ok 44\n";
+
+@words = ();
+while (/\w+/g) {
+ push(@words, $&);
+}
+print join(':',@words) eq "now:is:the:time:for:all:good:men:to:come:to"
+ ? "ok 45\n"
+ : "not ok 45\n";
+
+@words = ();
+while (/to/g) {
+ push(@words, $&);
+}
+print join(':',@words) eq "to:to"
+ ? "ok 46\n"
+ : "not ok 46 @words\n";
+
+@words = /to/g;
+print join(':',@words) eq "to:to"
+ ? "ok 47\n"
+ : "not ok 47 @words\n";
+
+$_ = "abcdefghi";
+
+$pat1 = 'def';
+$pat2 = '^def';
+$pat3 = '.def.';
+$pat4 = 'abc';
+$pat5 = '^abc';
+$pat6 = 'abc$';
+$pat7 = 'ghi';
+$pat8 = '\w*ghi';
+$pat9 = 'ghi$';
+
+$t1=$t2=$t3=$t4=$t5=$t6=$t7=$t8=$t9=0;
+
+for $iter (1..5) {
+ $t1++ if /$pat1/o;
+ $t2++ if /$pat2/o;
+ $t3++ if /$pat3/o;
+ $t4++ if /$pat4/o;
+ $t5++ if /$pat5/o;
+ $t6++ if /$pat6/o;
+ $t7++ if /$pat7/o;
+ $t8++ if /$pat8/o;
+ $t9++ if /$pat9/o;
+}
+
+$x = "$t1$t2$t3$t4$t5$t6$t7$t8$t9";
+print $x eq '505550555' ? "ok 48\n" : "not ok 48 $x\n";
+
+$xyz = 'xyz';
+print "abc" =~ /^abc$|$xyz/ ? "ok 49\n" : "not ok 49\n";
+
+# perl 4.009 says "unmatched ()"
+eval '"abc" =~ /a(bc$)|$xyz/; $result = "$&:$1"';
+print $@ eq "" ? "ok 50\n" : "not ok 50\n";
+print $result eq "abc:bc" ? "ok 51\n" : "not ok 51\n";
diff --git a/gnu/usr.bin/perl/perl/t/op/push.t b/gnu/usr.bin/perl/perl/t/op/push.t
new file mode 100755
index 0000000..3d738ac
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/push.t
@@ -0,0 +1,44 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/push.t,v 1.1.1.1 1993/08/23 21:30:03 nate Exp $
+
+@tests = split(/\n/, <<EOF);
+0 3, 0 1 2, 3 4 5 6 7
+0 0 a b c, , a b c 0 1 2 3 4 5 6 7
+8 0 a b c, , 0 1 2 3 4 5 6 7 a b c
+7 0 6.5, , 0 1 2 3 4 5 6 6.5 7
+1 0 a b c d e f g h i j,, 0 a b c d e f g h i j 1 2 3 4 5 6 7
+0 1 a, 0, a 1 2 3 4 5 6 7
+1 6 x y z, 1 2 3 4 5 6, 0 x y z 7
+0 7 x y z, 0 1 2 3 4 5 6, x y z 7
+1 7 x y z, 1 2 3 4 5 6 7, 0 x y z
+4, 4 5 6 7, 0 1 2 3
+-4, 4 5 6 7, 0 1 2 3
+EOF
+
+print "1..", 2 + @tests, "\n";
+die "blech" unless @tests;
+
+@x = (1,2,3);
+push(@x,@x);
+if (join(':',@x) eq '1:2:3:1:2:3') {print "ok 1\n";} else {print "not ok 1\n";}
+push(x,4);
+if (join(':',@x) eq '1:2:3:1:2:3:4') {print "ok 2\n";} else {print "not ok 2\n";}
+
+$test = 3;
+foreach $line (@tests) {
+ ($list,$get,$leave) = split(/,\t*/,$line);
+ @list = split(' ',$list);
+ @get = split(' ',$get);
+ @leave = split(' ',$leave);
+ @x = (0,1,2,3,4,5,6,7);
+ @got = splice(@x,@list);
+ if (join(':',@got) eq join(':',@get) &&
+ join(':',@x) eq join(':',@leave)) {
+ print "ok ",$test++,"\n";
+ }
+ else {
+ print "not ok ",$test++," got: @got == @get left: @x == @leave\n";
+ }
+}
+
diff --git a/gnu/usr.bin/perl/perl/t/op/range.t b/gnu/usr.bin/perl/perl/t/op/range.t
new file mode 100755
index 0000000..6214f95
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/range.t
@@ -0,0 +1,36 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/range.t,v 1.1.1.1 1993/08/23 21:30:03 nate Exp $
+
+print "1..8\n";
+
+print join(':',1..5) eq '1:2:3:4:5' ? "ok 1\n" : "not ok 1\n";
+
+@foo = (1,2,3,4,5,6,7,8,9);
+@foo[2..4] = ('c','d','e');
+
+print join(':',@foo[$foo[0]..5]) eq '2:c:d:e:6' ? "ok 2\n" : "not ok 2\n";
+
+@bar[2..4] = ('c','d','e');
+print join(':',@bar[1..5]) eq ':c:d:e:' ? "ok 3\n" : "not ok 3\n";
+
+($a,@bcd[0..2],$e) = ('a','b','c','d','e');
+print join(':',$a,@bcd[0..2],$e) eq 'a:b:c:d:e' ? "ok 4\n" : "not ok 4\n";
+
+$x = 0;
+for (1..100) {
+ $x += $_;
+}
+print $x == 5050 ? "ok 5\n" : "not ok 5 $x\n";
+
+$x = 0;
+for ((100,2..99,1)) {
+ $x += $_;
+}
+print $x == 5050 ? "ok 6\n" : "not ok 6 $x\n";
+
+$x = join('','a'..'z');
+print $x eq 'abcdefghijklmnopqrstuvwxyz' ? "ok 7\n" : "not ok 7 $x\n";
+
+@x = 'A'..'ZZ';
+print @x == 27 * 26 ? "ok 8\n" : "not ok 8\n";
diff --git a/gnu/usr.bin/perl/perl/t/op/re_tests b/gnu/usr.bin/perl/perl/t/op/re_tests
new file mode 100644
index 0000000..ee03d6f
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/re_tests
@@ -0,0 +1,274 @@
+abc abc y $& abc
+abc xbc n - -
+abc axc n - -
+abc abx n - -
+abc xabcy y $& abc
+abc ababc y $& abc
+ab*c abc y $& abc
+ab*bc abc y $& abc
+ab*bc abbc y $& abbc
+ab*bc abbbbc y $& abbbbc
+ab{0,}bc abbbbc y $& abbbbc
+ab+bc abbc y $& abbc
+ab+bc abc n - -
+ab+bc abq n - -
+ab{1,}bc abq n - -
+ab+bc abbbbc y $& abbbbc
+ab{1,}bc abbbbc y $& abbbbc
+ab{1,3}bc abbbbc y $& abbbbc
+ab{3,4}bc abbbbc y $& abbbbc
+ab{4,5}bc abbbbc n - -
+ab?bc abbc y $& abbc
+ab?bc abc y $& abc
+ab{0,1}bc abc y $& abc
+ab?bc abbbbc n - -
+ab?c abc y $& abc
+ab{0,1}c abc y $& abc
+^abc$ abc y $& abc
+^abc$ abcc n - -
+^abc abcc y $& abc
+^abc$ aabc n - -
+abc$ aabc y $& abc
+^ abc y $&
+$ abc y $&
+a.c abc y $& abc
+a.c axc y $& axc
+a.*c axyzc y $& axyzc
+a.*c axyzd n - -
+a[bc]d abc n - -
+a[bc]d abd y $& abd
+a[b-d]e abd n - -
+a[b-d]e ace y $& ace
+a[b-d] aac y $& ac
+a[-b] a- y $& a-
+a[b-] a- y $& a-
+a[b-a] - c - -
+a[]b - c - -
+a[ - c - -
+a] a] y $& a]
+a[]]b a]b y $& a]b
+a[^bc]d aed y $& aed
+a[^bc]d abd n - -
+a[^-b]c adc y $& adc
+a[^-b]c a-c n - -
+a[^]b]c a]c n - -
+a[^]b]c adc y $& adc
+ab|cd abc y $& ab
+ab|cd abcd y $& ab
+()ef def y $&-$1 ef-
+()* - c - -
+*a - c - -
+^* - c - -
+$* - c - -
+(*)b - c - -
+$b b n - -
+a\ - c - -
+a\(b a(b y $&-$1 a(b-
+a\(*b ab y $& ab
+a\(*b a((b y $& a((b
+a\\b a\b y $& a\b
+abc) - c - -
+(abc - c - -
+((a)) abc y $&-$1-$2 a-a-a
+(a)b(c) abc y $&-$1-$2 abc-a-c
+a+b+c aabbabc y $& abc
+a{1,}b{1,}c aabbabc y $& abc
+a** - c - -
+a*? - c - -
+(a*)* - c - -
+(a*)+ - c - -
+(a|)* - c - -
+(a*|b)* - c - -
+(a+|b)* ab y $&-$1 ab-b
+(a+|b){0,} ab y $&-$1 ab-b
+(a+|b)+ ab y $&-$1 ab-b
+(a+|b){1,} ab y $&-$1 ab-b
+(a+|b)? ab y $&-$1 a-a
+(a+|b){0,1} ab y $&-$1 a-a
+(^)* - c - -
+(ab|)* - c - -
+)( - c - -
+[^ab]* cde y $& cde
+abc n - -
+a* y $&
+([abc])*d abbbcd y $&-$1 abbbcd-c
+([abc])*bcd abcd y $&-$1 abcd-a
+a|b|c|d|e e y $& e
+(a|b|c|d|e)f ef y $&-$1 ef-e
+((a*|b))* - c - -
+abcd*efg abcdefg y $& abcdefg
+ab* xabyabbbz y $& ab
+ab* xayabbbz y $& a
+(ab|cd)e abcde y $&-$1 cde-cd
+[abhgefdc]ij hij y $& hij
+^(ab|cd)e abcde n x$1y xy
+(abc|)ef abcdef y $&-$1 ef-
+(a|b)c*d abcd y $&-$1 bcd-b
+(ab|ab*)bc abc y $&-$1 abc-a
+a([bc]*)c* abc y $&-$1 abc-bc
+a([bc]*)(c*d) abcd y $&-$1-$2 abcd-bc-d
+a([bc]+)(c*d) abcd y $&-$1-$2 abcd-bc-d
+a([bc]*)(c+d) abcd y $&-$1-$2 abcd-b-cd
+a[bcd]*dcdcde adcdcde y $& adcdcde
+a[bcd]+dcdcde adcdcde n - -
+(ab|a)b*c abc y $&-$1 abc-ab
+((a)(b)c)(d) abcd y $1-$2-$3-$4 abc-a-b-d
+[a-zA-Z_][a-zA-Z0-9_]* alpha y $& alpha
+^a(bc+|b[eh])g|.h$ abh y $&-$1 bh-
+(bc+d$|ef*g.|h?i(j|k)) effgz y $&-$1-$2 effgz-effgz-
+(bc+d$|ef*g.|h?i(j|k)) ij y $&-$1-$2 ij-ij-j
+(bc+d$|ef*g.|h?i(j|k)) effg n - -
+(bc+d$|ef*g.|h?i(j|k)) bcdd n - -
+(bc+d$|ef*g.|h?i(j|k)) reffgz y $&-$1-$2 effgz-effgz-
+((((((((((a)))))))))) a y $10 a
+((((((((((a))))))))))\10 aa y $& aa
+((((((((((a))))))))))\41 aa n - -
+((((((((((a))))))))))\41 a! y $& a!
+(((((((((a))))))))) a y $& a
+multiple words of text uh-uh n - -
+multiple words multiple words, yeah y $& multiple words
+(.*)c(.*) abcde y $&-$1-$2 abcde-ab-de
+\((.*), (.*)\) (a, b) y ($2, $1) (b, a)
+[k] ab n - -
+abcd abcd y $&-\$&-\\$& abcd-$&-\abcd
+a(bc)d abcd y $1-\$1-\\$1 bc-$1-\bc
+a[-]?c ac y $& ac
+(abc)\1 abcabc y $1 abc
+([a-c]*)\1 abcabc y $1 abc
+'abc'i ABC y $& ABC
+'abc'i XBC n - -
+'abc'i AXC n - -
+'abc'i ABX n - -
+'abc'i XABCY y $& ABC
+'abc'i ABABC y $& ABC
+'ab*c'i ABC y $& ABC
+'ab*bc'i ABC y $& ABC
+'ab*bc'i ABBC y $& ABBC
+'ab*bc'i ABBBBC y $& ABBBBC
+'ab{0,}bc'i ABBBBC y $& ABBBBC
+'ab+bc'i ABBC y $& ABBC
+'ab+bc'i ABC n - -
+'ab+bc'i ABQ n - -
+'ab{1,}bc'i ABQ n - -
+'ab+bc'i ABBBBC y $& ABBBBC
+'ab{1,}bc'i ABBBBC y $& ABBBBC
+'ab{1,3}bc'i ABBBBC y $& ABBBBC
+'ab{3,4}bc'i ABBBBC y $& ABBBBC
+'ab{4,5}bc'i ABBBBC n - -
+'ab?bc'i ABBC y $& ABBC
+'ab?bc'i ABC y $& ABC
+'ab{0,1}bc'i ABC y $& ABC
+'ab?bc'i ABBBBC n - -
+'ab?c'i ABC y $& ABC
+'ab{0,1}c'i ABC y $& ABC
+'^abc$'i ABC y $& ABC
+'^abc$'i ABCC n - -
+'^abc'i ABCC y $& ABC
+'^abc$'i AABC n - -
+'abc$'i AABC y $& ABC
+'^'i ABC y $&
+'$'i ABC y $&
+'a.c'i ABC y $& ABC
+'a.c'i AXC y $& AXC
+'a.*c'i AXYZC y $& AXYZC
+'a.*c'i AXYZD n - -
+'a[bc]d'i ABC n - -
+'a[bc]d'i ABD y $& ABD
+'a[b-d]e'i ABD n - -
+'a[b-d]e'i ACE y $& ACE
+'a[b-d]'i AAC y $& AC
+'a[-b]'i A- y $& A-
+'a[b-]'i A- y $& A-
+'a[b-a]'i - c - -
+'a[]b'i - c - -
+'a['i - c - -
+'a]'i A] y $& A]
+'a[]]b'i A]B y $& A]B
+'a[^bc]d'i AED y $& AED
+'a[^bc]d'i ABD n - -
+'a[^-b]c'i ADC y $& ADC
+'a[^-b]c'i A-C n - -
+'a[^]b]c'i A]C n - -
+'a[^]b]c'i ADC y $& ADC
+'ab|cd'i ABC y $& AB
+'ab|cd'i ABCD y $& AB
+'()ef'i DEF y $&-$1 EF-
+'()*'i - c - -
+'*a'i - c - -
+'^*'i - c - -
+'$*'i - c - -
+'(*)b'i - c - -
+'$b'i B n - -
+'a\'i - c - -
+'a\(b'i A(B y $&-$1 A(B-
+'a\(*b'i AB y $& AB
+'a\(*b'i A((B y $& A((B
+'a\\b'i A\B y $& A\B
+'abc)'i - c - -
+'(abc'i - c - -
+'((a))'i ABC y $&-$1-$2 A-A-A
+'(a)b(c)'i ABC y $&-$1-$2 ABC-A-C
+'a+b+c'i AABBABC y $& ABC
+'a{1,}b{1,}c'i AABBABC y $& ABC
+'a**'i - c - -
+'a*?'i - c - -
+'(a*)*'i - c - -
+'(a*)+'i - c - -
+'(a|)*'i - c - -
+'(a*|b)*'i - c - -
+'(a+|b)*'i AB y $&-$1 AB-B
+'(a+|b){0,}'i AB y $&-$1 AB-B
+'(a+|b)+'i AB y $&-$1 AB-B
+'(a+|b){1,}'i AB y $&-$1 AB-B
+'(a+|b)?'i AB y $&-$1 A-A
+'(a+|b){0,1}'i AB y $&-$1 A-A
+'(^)*'i - c - -
+'(ab|)*'i - c - -
+')('i - c - -
+'[^ab]*'i CDE y $& CDE
+'abc'i n - -
+'a*'i y $&
+'([abc])*d'i ABBBCD y $&-$1 ABBBCD-C
+'([abc])*bcd'i ABCD y $&-$1 ABCD-A
+'a|b|c|d|e'i E y $& E
+'(a|b|c|d|e)f'i EF y $&-$1 EF-E
+'((a*|b))*'i - c - -
+'abcd*efg'i ABCDEFG y $& ABCDEFG
+'ab*'i XABYABBBZ y $& AB
+'ab*'i XAYABBBZ y $& A
+'(ab|cd)e'i ABCDE y $&-$1 CDE-CD
+'[abhgefdc]ij'i HIJ y $& HIJ
+'^(ab|cd)e'i ABCDE n x$1y XY
+'(abc|)ef'i ABCDEF y $&-$1 EF-
+'(a|b)c*d'i ABCD y $&-$1 BCD-B
+'(ab|ab*)bc'i ABC y $&-$1 ABC-A
+'a([bc]*)c*'i ABC y $&-$1 ABC-BC
+'a([bc]*)(c*d)'i ABCD y $&-$1-$2 ABCD-BC-D
+'a([bc]+)(c*d)'i ABCD y $&-$1-$2 ABCD-BC-D
+'a([bc]*)(c+d)'i ABCD y $&-$1-$2 ABCD-B-CD
+'a[bcd]*dcdcde'i ADCDCDE y $& ADCDCDE
+'a[bcd]+dcdcde'i ADCDCDE n - -
+'(ab|a)b*c'i ABC y $&-$1 ABC-AB
+'((a)(b)c)(d)'i ABCD y $1-$2-$3-$4 ABC-A-B-D
+'[a-zA-Z_][a-zA-Z0-9_]*'i ALPHA y $& ALPHA
+'^a(bc+|b[eh])g|.h$'i ABH y $&-$1 BH-
+'(bc+d$|ef*g.|h?i(j|k))'i EFFGZ y $&-$1-$2 EFFGZ-EFFGZ-
+'(bc+d$|ef*g.|h?i(j|k))'i IJ y $&-$1-$2 IJ-IJ-J
+'(bc+d$|ef*g.|h?i(j|k))'i EFFG n - -
+'(bc+d$|ef*g.|h?i(j|k))'i BCDD n - -
+'(bc+d$|ef*g.|h?i(j|k))'i REFFGZ y $&-$1-$2 EFFGZ-EFFGZ-
+'((((((((((a))))))))))'i A y $10 A
+'((((((((((a))))))))))\10'i AA y $& AA
+'((((((((((a))))))))))\41'i AA n - -
+'((((((((((a))))))))))\41'i A! y $& A!
+'(((((((((a)))))))))'i A y $& A
+'multiple words of text'i UH-UH n - -
+'multiple words'i MULTIPLE WORDS, YEAH y $& MULTIPLE WORDS
+'(.*)c(.*)'i ABCDE y $&-$1-$2 ABCDE-AB-DE
+'\((.*), (.*)\)'i (A, B) y ($2, $1) (B, A)
+'[k]'i AB n - -
+'abcd'i ABCD y $&-\$&-\\$& ABCD-$&-\ABCD
+'a(bc)d'i ABCD y $1-\$1-\\$1 BC-$1-\BC
+'a[-]?c'i AC y $& AC
+'(abc)\1'i ABCABC y $1 ABC
+'([a-c]*)\1'i ABCABC y $1 ABC
diff --git a/gnu/usr.bin/perl/perl/t/op/read.t b/gnu/usr.bin/perl/perl/t/op/read.t
new file mode 100755
index 0000000..4151e5c
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/read.t
@@ -0,0 +1,20 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/read.t,v 1.1.1.1 1993/08/23 21:30:04 nate Exp $
+
+print "1..4\n";
+
+
+open(FOO,'op/read.t') || open(FOO,'./read.t') || open(FOO,'t/op/read.t') || die "Can't open op.read";
+seek(FOO,4,0);
+$got = read(FOO,$buf,4);
+print "This is got ... $got\n";
+
+print ($got == 4 ? "ok 1\n" : "not ok 1\n");
+print ($buf eq "perl" ? "ok 2\n" : "not ok 2 :$buf:\n");
+
+seek(FOO,20000,0);
+$got = read(FOO,$buf,4);
+
+print ($got == 0 ? "ok 3\n" : "not ok 3\n");
+print ($buf eq "" ? "ok 4\n" : "not ok 4\n");
diff --git a/gnu/usr.bin/perl/perl/t/op/readdir.t b/gnu/usr.bin/perl/perl/t/op/readdir.t
new file mode 100755
index 0000000..1800699
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/readdir.t
@@ -0,0 +1,20 @@
+#!./perl
+
+eval 'opendir(NOSUCH, "no/such/directory");';
+if ($@) { print "1..0\n"; exit; }
+
+print "1..3\n";
+
+if (opendir(OP, "op")) { print "ok 1\n"; } else { print "not ok 1\n"; }
+@D = grep(/^[^\.].*\.t$/, readdir(OP));
+closedir(OP);
+
+if (@D > 20 && @D < 100) { print "ok 2\n"; } else { print "not ok 2\n"; }
+
+@R = sort @D;
+@G = <op/*.t>;
+while (@R && @G && "op/".$R[0] eq $G[0]) {
+ shift(@R);
+ shift(@G);
+}
+if (@R == 0 && @G == 0) { print "ok 3\n"; } else { print "not ok 3\n"; }
diff --git a/gnu/usr.bin/perl/perl/t/op/regexp.t b/gnu/usr.bin/perl/perl/t/op/regexp.t
new file mode 100755
index 0000000..58f6666
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/regexp.t
@@ -0,0 +1,35 @@
+#!./perl
+
+# $RCSfile: regexp.t,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:03 $
+
+open(TESTS,'op/re_tests') || open(TESTS,'t/op/re_tests')
+ || die "Can't open re_tests";
+while (<TESTS>) { }
+$numtests = $.;
+close(TESTS);
+
+print "1..$numtests\n";
+open(TESTS,'op/re_tests') || open(TESTS,'t/op/re_tests')
+ || die "Can't open re_tests";
+$| = 1;
+while (<TESTS>) {
+ ($pat, $subject, $result, $repl, $expect) = split(/[\t\n]/,$_);
+ $input = join(':',$pat,$subject,$result,$repl,$expect);
+ $pat = "'$pat'" unless $pat =~ /^'/;
+ eval "\$match = (\$subject =~ m$pat); \$got = \"$repl\";";
+ if ($result eq 'c') {
+ if ($@ ne '') {print "ok $.\n";} else {print "not ok $.\n";}
+ }
+ elsif ($result eq 'n') {
+ if (!$match) {print "ok $.\n";} else {print "not ok $. $input => $got\n";}
+ }
+ else {
+ if ($match && $got eq $expect) {
+ print "ok $.\n";
+ }
+ else {
+ print "not ok $. $input => $got\n";
+ }
+ }
+}
+close(TESTS);
diff --git a/gnu/usr.bin/perl/perl/t/op/repeat.t b/gnu/usr.bin/perl/perl/t/op/repeat.t
new file mode 100755
index 0000000..68c61fc
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/repeat.t
@@ -0,0 +1,42 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/repeat.t,v 1.1.1.1 1993/08/23 21:30:02 nate Exp $
+
+print "1..19\n";
+
+# compile time
+
+if ('-' x 5 eq '-----') {print "ok 1\n";} else {print "not ok 1\n";}
+if ('-' x 1 eq '-') {print "ok 2\n";} else {print "not ok 2\n";}
+if ('-' x 0 eq '') {print "ok 3\n";} else {print "not ok 3\n";}
+
+if ('ab' x 3 eq 'ababab') {print "ok 4\n";} else {print "not ok 4\n";}
+
+# run time
+
+$a = '-';
+if ($a x 5 eq '-----') {print "ok 5\n";} else {print "not ok 5\n";}
+if ($a x 1 eq '-') {print "ok 6\n";} else {print "not ok 6\n";}
+if ($a x 0 eq '') {print "ok 7\n";} else {print "not ok 7\n";}
+
+$a = 'ab';
+if ($a x 3 eq 'ababab') {print "ok 8\n";} else {print "not ok 8\n";}
+
+$a = 'xyz';
+$a x= 2;
+if ($a eq 'xyzxyz') {print "ok 9\n";} else {print "not ok 9\n";}
+$a x= 1;
+if ($a eq 'xyzxyz') {print "ok 10\n";} else {print "not ok 10\n";}
+$a x= 0;
+if ($a eq '') {print "ok 11\n";} else {print "not ok 11\n";}
+
+@x = (1,2,3);
+
+print join('', @x x 4) eq '3333' ? "ok 12\n" : "not ok 12\n";
+print join('', (@x) x 4) eq '123123123123' ? "ok 13\n" : "not ok 13\n";
+print join('', (@x,()) x 4) eq '123123123123' ? "ok 14\n" : "not ok 14\n";
+print join('', (@x,1) x 4) eq '1231123112311231' ? "ok 15\n" : "not ok 15\n";
+print join(':', () x 4) eq '' ? "ok 16\n" : "not ok 16\n";
+print join(':', (9) x 4) eq '9:9:9:9' ? "ok 17\n" : "not ok 17\n";
+print join(':', (9,9) x 4) eq '9:9:9:9:9:9:9:9' ? "ok 18\n" : "not ok 18\n";
+print join('', (split(//,"123")) x 2) eq '123123' ? "ok 19\n" : "not ok 19\n";
diff --git a/gnu/usr.bin/perl/perl/t/op/s.t b/gnu/usr.bin/perl/perl/t/op/s.t
new file mode 100755
index 0000000..5930020
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/s.t
@@ -0,0 +1,179 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/s.t,v 1.1.1.1 1993/08/23 21:30:01 nate Exp $
+
+print "1..51\n";
+
+$x = 'foo';
+$_ = "x";
+s/x/\$x/;
+print "#1\t:$_: eq :\$x:\n";
+if ($_ eq '$x') {print "ok 1\n";} else {print "not ok 1\n";}
+
+$_ = "x";
+s/x/$x/;
+print "#2\t:$_: eq :foo:\n";
+if ($_ eq 'foo') {print "ok 2\n";} else {print "not ok 2\n";}
+
+$_ = "x";
+s/x/\$x $x/;
+print "#3\t:$_: eq :\$x foo:\n";
+if ($_ eq '$x foo') {print "ok 3\n";} else {print "not ok 3\n";}
+
+$b = 'cd';
+($a = 'abcdef') =~ s'(b${b}e)'\n$1';
+print "#4\t:$1: eq :bcde:\n";
+print "#4\t:$a: eq :a\\n\$1f:\n";
+if ($1 eq 'bcde' && $a eq 'a\n$1f') {print "ok 4\n";} else {print "not ok 4\n";}
+
+$a = 'abacada';
+if (($a =~ s/a/x/g) == 4 && $a eq 'xbxcxdx')
+ {print "ok 5\n";} else {print "not ok 5\n";}
+
+if (($a =~ s/a/y/g) == 0 && $a eq 'xbxcxdx')
+ {print "ok 6\n";} else {print "not ok 6 $a\n";}
+
+if (($a =~ s/b/y/g) == 1 && $a eq 'xyxcxdx')
+ {print "ok 7\n";} else {print "not ok 7 $a\n";}
+
+$_ = 'ABACADA';
+if (/a/i && s///gi && $_ eq 'BCD') {print "ok 8\n";} else {print "not ok 8 $_\n";}
+
+$_ = '\\' x 4;
+if (length($_) == 4) {print "ok 9\n";} else {print "not ok 9\n";}
+s/\\/\\\\/g;
+if ($_ eq '\\' x 8) {print "ok 10\n";} else {print "not ok 10 $_\n";}
+
+$_ = '\/' x 4;
+if (length($_) == 8) {print "ok 11\n";} else {print "not ok 11\n";}
+s/\//\/\//g;
+if ($_ eq '\\//' x 4) {print "ok 12\n";} else {print "not ok 12\n";}
+if (length($_) == 12) {print "ok 13\n";} else {print "not ok 13\n";}
+
+$_ = 'aaaXXXXbbb';
+s/^a//;
+print $_ eq 'aaXXXXbbb' ? "ok 14\n" : "not ok 14\n";
+
+$_ = 'aaaXXXXbbb';
+s/a//;
+print $_ eq 'aaXXXXbbb' ? "ok 15\n" : "not ok 15\n";
+
+$_ = 'aaaXXXXbbb';
+s/^a/b/;
+print $_ eq 'baaXXXXbbb' ? "ok 16\n" : "not ok 16\n";
+
+$_ = 'aaaXXXXbbb';
+s/a/b/;
+print $_ eq 'baaXXXXbbb' ? "ok 17\n" : "not ok 17\n";
+
+$_ = 'aaaXXXXbbb';
+s/aa//;
+print $_ eq 'aXXXXbbb' ? "ok 18\n" : "not ok 18\n";
+
+$_ = 'aaaXXXXbbb';
+s/aa/b/;
+print $_ eq 'baXXXXbbb' ? "ok 19\n" : "not ok 19\n";
+
+$_ = 'aaaXXXXbbb';
+s/b$//;
+print $_ eq 'aaaXXXXbb' ? "ok 20\n" : "not ok 20\n";
+
+$_ = 'aaaXXXXbbb';
+s/b//;
+print $_ eq 'aaaXXXXbb' ? "ok 21\n" : "not ok 21\n";
+
+$_ = 'aaaXXXXbbb';
+s/bb//;
+print $_ eq 'aaaXXXXb' ? "ok 22\n" : "not ok 22\n";
+
+$_ = 'aaaXXXXbbb';
+s/aX/y/;
+print $_ eq 'aayXXXbbb' ? "ok 23\n" : "not ok 23\n";
+
+$_ = 'aaaXXXXbbb';
+s/Xb/z/;
+print $_ eq 'aaaXXXzbb' ? "ok 24\n" : "not ok 24\n";
+
+$_ = 'aaaXXXXbbb';
+s/aaX.*Xbb//;
+print $_ eq 'ab' ? "ok 25\n" : "not ok 25\n";
+
+$_ = 'aaaXXXXbbb';
+s/bb/x/;
+print $_ eq 'aaaXXXXxb' ? "ok 26\n" : "not ok 26\n";
+
+# now for some unoptimized versions of the same.
+
+$_ = 'aaaXXXXbbb';
+$x ne $x || s/^a//;
+print $_ eq 'aaXXXXbbb' ? "ok 27\n" : "not ok 27\n";
+
+$_ = 'aaaXXXXbbb';
+$x ne $x || s/a//;
+print $_ eq 'aaXXXXbbb' ? "ok 28\n" : "not ok 28\n";
+
+$_ = 'aaaXXXXbbb';
+$x ne $x || s/^a/b/;
+print $_ eq 'baaXXXXbbb' ? "ok 29\n" : "not ok 29\n";
+
+$_ = 'aaaXXXXbbb';
+$x ne $x || s/a/b/;
+print $_ eq 'baaXXXXbbb' ? "ok 30\n" : "not ok 30\n";
+
+$_ = 'aaaXXXXbbb';
+$x ne $x || s/aa//;
+print $_ eq 'aXXXXbbb' ? "ok 31\n" : "not ok 31\n";
+
+$_ = 'aaaXXXXbbb';
+$x ne $x || s/aa/b/;
+print $_ eq 'baXXXXbbb' ? "ok 32\n" : "not ok 32\n";
+
+$_ = 'aaaXXXXbbb';
+$x ne $x || s/b$//;
+print $_ eq 'aaaXXXXbb' ? "ok 33\n" : "not ok 33\n";
+
+$_ = 'aaaXXXXbbb';
+$x ne $x || s/b//;
+print $_ eq 'aaaXXXXbb' ? "ok 34\n" : "not ok 34\n";
+
+$_ = 'aaaXXXXbbb';
+$x ne $x || s/bb//;
+print $_ eq 'aaaXXXXb' ? "ok 35\n" : "not ok 35\n";
+
+$_ = 'aaaXXXXbbb';
+$x ne $x || s/aX/y/;
+print $_ eq 'aayXXXbbb' ? "ok 36\n" : "not ok 36\n";
+
+$_ = 'aaaXXXXbbb';
+$x ne $x || s/Xb/z/;
+print $_ eq 'aaaXXXzbb' ? "ok 37\n" : "not ok 37\n";
+
+$_ = 'aaaXXXXbbb';
+$x ne $x || s/aaX.*Xbb//;
+print $_ eq 'ab' ? "ok 38\n" : "not ok 38\n";
+
+$_ = 'aaaXXXXbbb';
+$x ne $x || s/bb/x/;
+print $_ eq 'aaaXXXXxb' ? "ok 39\n" : "not ok 39\n";
+
+$_ = 'abc123xyz';
+s/\d+/$&*2/e; # yields 'abc246xyz'
+print $_ eq 'abc246xyz' ? "ok 40\n" : "not ok 40\n";
+s/\d+/sprintf("%5d",$&)/e; # yields 'abc 246xyz'
+print $_ eq 'abc 246xyz' ? "ok 41\n" : "not ok 41\n";
+s/\w/$& x 2/eg; # yields 'aabbcc 224466xxyyzz'
+print $_ eq 'aabbcc 224466xxyyzz' ? "ok 42\n" : "not ok 42\n";
+
+$_ = "aaaaa";
+print y/a/b/ == 5 ? "ok 43\n" : "not ok 43\n";
+print y/a/b/ == 0 ? "ok 44\n" : "not ok 44\n";
+print y/b// == 5 ? "ok 45\n" : "not ok 45\n";
+print y/b/c/s == 5 ? "ok 46\n" : "not ok 46\n";
+print y/c// == 1 ? "ok 47\n" : "not ok 47\n";
+print y/c//d == 1 ? "ok 48\n" : "not ok 48\n";
+print $_ eq "" ? "ok 49\n" : "not ok 49\n";
+
+$_ = "Now is the %#*! time for all good men...";
+print (($x=(y/a-zA-Z //cd)) == 7 ? "ok 50\n" : "not ok 50\n");
+print y/ / /s == 8 ? "ok 51\n" : "not ok 51\n";
+
diff --git a/gnu/usr.bin/perl/perl/t/op/sleep.t b/gnu/usr.bin/perl/perl/t/op/sleep.t
new file mode 100755
index 0000000..81113712
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/sleep.t
@@ -0,0 +1,8 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/sleep.t,v 1.1.1.1 1993/08/23 21:30:04 nate Exp $
+
+print "1..1\n";
+
+$x = sleep 2;
+if ($x >= 2 && $x <= 10) {print "ok 1\n";} else {print "not ok 1 $x\n";}
diff --git a/gnu/usr.bin/perl/perl/t/op/sort.t b/gnu/usr.bin/perl/perl/t/op/sort.t
new file mode 100755
index 0000000..4692ee4
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/sort.t
@@ -0,0 +1,48 @@
+#!./perl
+
+# $RCSfile: sort.t,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:02 $
+
+print "1..10\n";
+
+sub reverse { $a lt $b ? 1 : $a gt $b ? -1 : 0; }
+
+@harry = ('dog','cat','x','Cain','Abel');
+@george = ('gone','chased','yz','Punished','Axed');
+
+$x = join('', sort @harry);
+print ($x eq 'AbelCaincatdogx' ? "ok 1\n" : "not ok 1\n");
+
+$x = join('', sort reverse @harry);
+print ($x eq 'xdogcatCainAbel' ? "ok 2\n" : "not ok 2\n");
+
+$x = join('', sort @george, 'to', @harry);
+print ($x eq 'AbelAxedCainPunishedcatchaseddoggonetoxyz'?"ok 3\n":"not ok 3\n");
+
+@a = ();
+@b = reverse @a;
+print ("@b" eq "" ? "ok 4\n" : "not ok 4 (@b)\n");
+
+@a = (1);
+@b = reverse @a;
+print ("@b" eq "1" ? "ok 5\n" : "not ok 5 (@b)\n");
+
+@a = (1,2);
+@b = reverse @a;
+print ("@b" eq "2 1" ? "ok 6\n" : "not ok 6 (@b)\n");
+
+@a = (1,2,3);
+@b = reverse @a;
+print ("@b" eq "3 2 1" ? "ok 7\n" : "not ok 7 (@b)\n");
+
+@a = (1,2,3,4);
+@b = reverse @a;
+print ("@b" eq "4 3 2 1" ? "ok 8\n" : "not ok 8 (@b)\n");
+
+@a = (10,2,3,4);
+@b = sort {$a <=> $b;} @a;
+print ("@b" eq "2 3 4 10" ? "ok 9\n" : "not ok 9 (@b)\n");
+
+$sub = 'reverse';
+$x = join('', sort $sub @harry);
+print ($x eq 'xdogcatCainAbel' ? "ok 10\n" : "not ok 10\n");
+
diff --git a/gnu/usr.bin/perl/perl/t/op/split.t b/gnu/usr.bin/perl/perl/t/op/split.t
new file mode 100755
index 0000000..63bf3c7
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/split.t
@@ -0,0 +1,57 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/split.t,v 1.1.1.1 1993/08/23 21:30:02 nate Exp $
+
+print "1..12\n";
+
+$FS = ':';
+
+$_ = 'a:b:c';
+
+($a,$b,$c) = split($FS,$_);
+
+if (join(';',$a,$b,$c) eq 'a;b;c') {print "ok 1\n";} else {print "not ok 1\n";}
+
+@ary = split(/:b:/);
+if (join("$_",@ary) eq 'aa:b:cc') {print "ok 2\n";} else {print "not ok 2\n";}
+
+$_ = "abc\n";
+@xyz = (@ary = split(//));
+if (join(".",@ary) eq "a.b.c.\n") {print "ok 3\n";} else {print "not ok 3\n";}
+
+$_ = "a:b:c::::";
+@ary = split(/:/);
+if (join(".",@ary) eq "a.b.c") {print "ok 4\n";} else {print "not ok 4\n";}
+
+$_ = join(':',split(' '," a b\tc \t d "));
+if ($_ eq 'a:b:c:d') {print "ok 5\n";} else {print "not ok 5 #$_#\n";}
+
+$_ = join(':',split(/ */,"foo bar bie\tdoll"));
+if ($_ eq "f:o:o:b:a:r:b:i:e:\t:d:o:l:l")
+ {print "ok 6\n";} else {print "not ok 6\n";}
+
+$_ = join(':', 'foo', split(/ /,'a b c'), 'bar');
+if ($_ eq "foo:a:b::c:bar") {print "ok 7\n";} else {print "not ok 7 $_\n";}
+
+# Can we say how many fields to split to?
+$_ = join(':', split(' ','1 2 3 4 5 6', 3));
+print $_ eq '1:2:3 4 5 6' ? "ok 8\n" : "not ok 8 $_\n";
+
+# Can we do it as a variable?
+$x = 4;
+$_ = join(':', split(' ','1 2 3 4 5 6', $x));
+print $_ eq '1:2:3:4 5 6' ? "ok 9\n" : "not ok 9 $_\n";
+
+# Does the 999 suppress null field chopping?
+$_ = join(':', split(/:/,'1:2:3:4:5:6:::', 999));
+print $_ eq '1:2:3:4:5:6:::' ? "ok 10\n" : "not ok 10 $_\n";
+
+# Does assignment to a list imply split to one more field than that?
+$foo = `./perl -D1024 -e '(\$a,\$b) = split;' 2>&1`;
+print $foo =~ /DEBUGGING/ || $foo =~ /num\(3\)/ ? "ok 11\n" : "not ok 11\n";
+
+# Can we say how many fields to split to when assigning to a list?
+($a,$b) = split(' ','1 2 3 4 5 6', 2);
+$_ = join(':',$a,$b);
+print $_ eq '1:2 3 4 5 6' ? "ok 12\n" : "not ok 12 $_\n";
+
diff --git a/gnu/usr.bin/perl/perl/t/op/sprintf.t b/gnu/usr.bin/perl/perl/t/op/sprintf.t
new file mode 100755
index 0000000..cdb4af5
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/sprintf.t
@@ -0,0 +1,8 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/sprintf.t,v 1.1.1.1 1993/08/23 21:30:01 nate Exp $
+
+print "1..1\n";
+
+$x = sprintf("%3s %-4s%%foo %5d%c%3.1f","hi",123,456,65,3.0999);
+if ($x eq ' hi 123 %foo 456A3.1') {print "ok 1\n";} else {print "not ok 1 '$x'\n";}
diff --git a/gnu/usr.bin/perl/perl/t/op/stat.t b/gnu/usr.bin/perl/perl/t/op/stat.t
new file mode 100755
index 0000000..a5db14f
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/stat.t
@@ -0,0 +1,176 @@
+#!./perl
+
+# $RCSfile: stat.t,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:01 $
+
+print "1..56\n";
+
+chop($cwd = `pwd`);
+
+$DEV = `ls -l /dev`;
+
+unlink "Op.stat.tmp";
+open(FOO, ">Op.stat.tmp");
+
+$junk = `ls Op.stat.tmp`; # hack to make Apollo update link count
+
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat(FOO);
+if ($nlink == 1) {print "ok 1\n";} else {print "not ok 1\n";}
+if ($mtime && $mtime == $ctime) {print "ok 2\n";} else {print "not ok 2\n";}
+
+print FOO "Now is the time for all good men to come to.\n";
+close(FOO);
+
+sleep 2;
+
+`rm -f Op.stat.tmp2; ln Op.stat.tmp Op.stat.tmp2; chmod 644 Op.stat.tmp`;
+
+($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
+ $blksize,$blocks) = stat('Op.stat.tmp');
+
+if ($nlink == 2) {print "ok 3\n";} else {print "not ok 3\n";}
+if (($mtime && $mtime != $ctime) || $cwd =~ m#/afs/#) {
+ print "ok 4\n";
+}
+else {
+ print "not ok 4\n";
+}
+print "#4 :$mtime: != :$ctime:\n";
+
+`rm -f Op.stat.tmp`;
+`touch Op.stat.tmp`;
+
+if (-z 'Op.stat.tmp') {print "ok 5\n";} else {print "not ok 5\n";}
+if (! -s 'Op.stat.tmp') {print "ok 6\n";} else {print "not ok 6\n";}
+
+`echo hi >Op.stat.tmp`;
+if (! -z 'Op.stat.tmp') {print "ok 7\n";} else {print "not ok 7\n";}
+if (-s 'Op.stat.tmp') {print "ok 8\n";} else {print "not ok 8\n";}
+
+unlink 'Op.stat.tmp';
+$olduid = $>; # can't test -r if uid == 0
+`echo hi >Op.stat.tmp`;
+chmod 0,'Op.stat.tmp';
+eval '$> = 1;'; # so switch uid (may not be implemented)
+if (!$> || ! -r 'Op.stat.tmp') {print "ok 9\n";} else {print "not ok 9\n";}
+if (!$> || ! -w 'Op.stat.tmp') {print "ok 10\n";} else {print "not ok 10\n";}
+eval '$> = $olduid;'; # switch uid back (may not be implemented)
+print "# olduid=$olduid, newuid=$>\n" unless ($> == $olduid);
+if (! -x 'Op.stat.tmp') {print "ok 11\n";} else {print "not ok 11\n";}
+
+foreach ((12,13,14,15,16,17)) {
+ print "ok $_\n"; #deleted tests
+}
+
+chmod 0700,'Op.stat.tmp';
+if (-r 'Op.stat.tmp') {print "ok 18\n";} else {print "not ok 18\n";}
+if (-w 'Op.stat.tmp') {print "ok 19\n";} else {print "not ok 19\n";}
+if (-x 'Op.stat.tmp') {print "ok 20\n";} else {print "not ok 20\n";}
+
+if (-f 'Op.stat.tmp') {print "ok 21\n";} else {print "not ok 21\n";}
+if (! -d 'Op.stat.tmp') {print "ok 22\n";} else {print "not ok 22\n";}
+
+if (-d '.') {print "ok 23\n";} else {print "not ok 23\n";}
+if (! -f '.') {print "ok 24\n";} else {print "not ok 24\n";}
+
+if (`ls -l perl` =~ /^l.*->/) {
+ if (-l 'perl') {print "ok 25\n";} else {print "not ok 25\n";}
+}
+else {
+ print "ok 25\n";
+}
+
+if (-o 'Op.stat.tmp') {print "ok 26\n";} else {print "not ok 26\n";}
+
+if (-e 'Op.stat.tmp') {print "ok 27\n";} else {print "not ok 27\n";}
+`rm -f Op.stat.tmp Op.stat.tmp2`;
+if (! -e 'Op.stat.tmp') {print "ok 28\n";} else {print "not ok 28\n";}
+
+if ($DEV !~ /\nc.* (\S+)\n/)
+ {print "ok 29\n";}
+elsif (-c "/dev/$1")
+ {print "ok 29\n";}
+else
+ {print "not ok 29\n";}
+if (! -c '.') {print "ok 30\n";} else {print "not ok 30\n";}
+
+if ($DEV !~ /\ns.* (\S+)\n/)
+ {print "ok 31\n";}
+elsif (-S "/dev/$1")
+ {print "ok 31\n";}
+else
+ {print "not ok 31\n";}
+if (! -S '.') {print "ok 32\n";} else {print "not ok 32\n";}
+
+if ($DEV !~ /\nb.* (\S+)\n/)
+ {print "ok 33\n";}
+elsif (-b "/dev/$1")
+ {print "ok 33\n";}
+else
+ {print "not ok 33\n";}
+if (! -b '.') {print "ok 34\n";} else {print "not ok 34\n";}
+
+$cnt = $uid = 0;
+
+die "Can't run op/stat.t test 35 without pwd working" unless $cwd;
+chdir '/usr/bin' || die "Can't cd to /usr/bin";
+while (defined($_ = <*>)) {
+ $cnt++;
+ $uid++ if -u;
+ last if $uid && $uid < $cnt;
+}
+chdir $cwd || die "Can't cd back to $cwd";
+
+# I suppose this is going to fail somewhere...
+if ($uid > 0 && $uid < $cnt) {print "ok 35\n";} else {print "not ok 35\n";}
+
+unless (open(tty,"/dev/tty")) {
+ print STDERR "Can't open /dev/tty--run t/TEST outside of make.\n";
+}
+if (-t tty) {print "ok 36\n";} else {print "not ok 36\n";}
+if (-c tty) {print "ok 37\n";} else {print "not ok 37\n";}
+close(tty);
+if (! -t tty) {print "ok 38\n";} else {print "not ok 38\n";}
+open(null,"/dev/null");
+if (! -t null || -e '/xenix') {print "ok 39\n";} else {print "not ok 39\n";}
+close(null);
+if (-t) {print "ok 40\n";} else {print "not ok 40\n";}
+
+# These aren't strictly "stat" calls, but so what?
+
+if (-T 'op/stat.t') {print "ok 41\n";} else {print "not ok 41\n";}
+if (! -B 'op/stat.t') {print "ok 42\n";} else {print "not ok 42\n";}
+
+if (-B './perl') {print "ok 43\n";} else {print "not ok 43\n";}
+if (! -T './perl') {print "ok 44\n";} else {print "not ok 44\n";}
+
+open(FOO,'op/stat.t');
+eval { -T FOO; };
+if ($@ =~ /not implemented/) {
+ print "# $@";
+ for (45 .. 54) {
+ print "ok $_\n";
+ }
+}
+else {
+ if (-T FOO) {print "ok 45\n";} else {print "not ok 45\n";}
+ if (! -B FOO) {print "ok 46\n";} else {print "not ok 46\n";}
+ $_ = <FOO>;
+ if (/perl/) {print "ok 47\n";} else {print "not ok 47\n";}
+ if (-T FOO) {print "ok 48\n";} else {print "not ok 48\n";}
+ if (! -B FOO) {print "ok 49\n";} else {print "not ok 49\n";}
+ close(FOO);
+
+ open(FOO,'op/stat.t');
+ $_ = <FOO>;
+ if (/perl/) {print "ok 50\n";} else {print "not ok 50\n";}
+ if (-T FOO) {print "ok 51\n";} else {print "not ok 51\n";}
+ if (! -B FOO) {print "ok 52\n";} else {print "not ok 52\n";}
+ seek(FOO,0,0);
+ if (-T FOO) {print "ok 53\n";} else {print "not ok 53\n";}
+ if (! -B FOO) {print "ok 54\n";} else {print "not ok 54\n";}
+}
+close(FOO);
+
+if (-T '/dev/null') {print "ok 55\n";} else {print "not ok 55\n";}
+if (-B '/dev/null') {print "ok 56\n";} else {print "not ok 56\n";}
diff --git a/gnu/usr.bin/perl/perl/t/op/study.t b/gnu/usr.bin/perl/perl/t/op/study.t
new file mode 100755
index 0000000..a0fdc4c
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/study.t
@@ -0,0 +1,69 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/study.t,v 1.1.1.1 1993/08/23 21:30:02 nate Exp $
+
+print "1..24\n";
+
+$x = "abc\ndef\n";
+study($x);
+
+if ($x =~ /^abc/) {print "ok 1\n";} else {print "not ok 1\n";}
+if ($x !~ /^def/) {print "ok 2\n";} else {print "not ok 2\n";}
+
+$* = 1;
+if ($x =~ /^def/) {print "ok 3\n";} else {print "not ok 3\n";}
+$* = 0;
+
+$_ = '123';
+study;
+if (/^([0-9][0-9]*)/) {print "ok 4\n";} else {print "not ok 4\n";}
+
+if ($x =~ /^xxx/) {print "not ok 5\n";} else {print "ok 5\n";}
+if ($x !~ /^abc/) {print "not ok 6\n";} else {print "ok 6\n";}
+
+if ($x =~ /def/) {print "ok 7\n";} else {print "not ok 7\n";}
+if ($x !~ /def/) {print "not ok 8\n";} else {print "ok 8\n";}
+
+study($x);
+if ($x !~ /.def/) {print "ok 9\n";} else {print "not ok 9\n";}
+if ($x =~ /.def/) {print "not ok 10\n";} else {print "ok 10\n";}
+
+if ($x =~ /\ndef/) {print "ok 11\n";} else {print "not ok 11\n";}
+if ($x !~ /\ndef/) {print "not ok 12\n";} else {print "ok 12\n";}
+
+$_ = 'aaabbbccc';
+study;
+if (/(a*b*)(c*)/ && $1 eq 'aaabbb' && $2 eq 'ccc') {
+ print "ok 13\n";
+} else {
+ print "not ok 13\n";
+}
+if (/(a+b+c+)/ && $1 eq 'aaabbbccc') {
+ print "ok 14\n";
+} else {
+ print "not ok 14\n";
+}
+
+if (/a+b?c+/) {print "not ok 15\n";} else {print "ok 15\n";}
+
+$_ = 'aaabccc';
+study;
+if (/a+b?c+/) {print "ok 16\n";} else {print "not ok 16\n";}
+if (/a*b+c*/) {print "ok 17\n";} else {print "not ok 17\n";}
+
+$_ = 'aaaccc';
+study;
+if (/a*b?c*/) {print "ok 18\n";} else {print "not ok 18\n";}
+if (/a*b+c*/) {print "not ok 19\n";} else {print "ok 19\n";}
+
+$_ = 'abcdef';
+study;
+if (/bcd|xyz/) {print "ok 20\n";} else {print "not ok 20\n";}
+if (/xyz|bcd/) {print "ok 21\n";} else {print "not ok 21\n";}
+
+if (m|bc/*d|) {print "ok 22\n";} else {print "not ok 22\n";}
+
+if (/^$_$/) {print "ok 23\n";} else {print "not ok 23\n";}
+
+$* = 1; # test 3 only tested the optimized version--this one is for real
+if ("ab\ncd\n" =~ /^cd/) {print "ok 24\n";} else {print "not ok 24\n";}
diff --git a/gnu/usr.bin/perl/perl/t/op/substr.t b/gnu/usr.bin/perl/perl/t/op/substr.t
new file mode 100755
index 0000000..09f312f
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/substr.t
@@ -0,0 +1,47 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/substr.t,v 1.1.1.1 1993/08/23 21:30:01 nate Exp $
+
+print "1..22\n";
+
+$a = 'abcdefxyz';
+
+print (substr($a,0,3) eq 'abc' ? "ok 1\n" : "not ok 1\n");
+print (substr($a,3,3) eq 'def' ? "ok 2\n" : "not ok 2\n");
+print (substr($a,6,999) eq 'xyz' ? "ok 3\n" : "not ok 3\n");
+print (substr($a,999,999) eq '' ? "ok 4\n" : "not ok 4\n");
+print (substr($a,6,-1) eq '' ? "ok 5\n" : "not ok 5\n");
+print (substr($a,-3,1) eq 'x' ? "ok 6\n" : "not ok 6\n");
+
+$[ = 1;
+
+print (substr($a,1,3) eq 'abc' ? "ok 7\n" : "not ok 7\n");
+print (substr($a,4,3) eq 'def' ? "ok 8\n" : "not ok 8\n");
+print (substr($a,7,999) eq 'xyz' ? "ok 9\n" : "not ok 9\n");
+print (substr($a,999,999) eq '' ? "ok 10\n" : "not ok 10\n");
+print (substr($a,7,-1) eq '' ? "ok 11\n" : "not ok 11\n");
+print (substr($a,-3,1) eq 'x' ? "ok 12\n" : "not ok 12\n");
+
+$[ = 0;
+
+substr($a,3,3) = 'XYZ';
+print $a eq 'abcXYZxyz' ? "ok 13\n" : "not ok 13\n";
+substr($a,0,2) = '';
+print $a eq 'cXYZxyz' ? "ok 14\n" : "not ok 14\n";
+y/a/a/;
+substr($a,0,0) = 'ab';
+print $a eq 'abcXYZxyz' ? "ok 15\n" : "not ok 15 $a\n";
+substr($a,0,0) = '12345678';
+print $a eq '12345678abcXYZxyz' ? "ok 16\n" : "not ok 16\n";
+substr($a,-3,3) = 'def';
+print $a eq '12345678abcXYZdef' ? "ok 17\n" : "not ok 17\n";
+substr($a,-3,3) = '<';
+print $a eq '12345678abcXYZ<' ? "ok 18\n" : "not ok 18\n";
+substr($a,-1,1) = '12345678';
+print $a eq '12345678abcXYZ12345678' ? "ok 19\n" : "not ok 19\n";
+
+$a = 'abcdefxyz';
+
+print (substr($a,6) eq 'xyz' ? "ok 20\n" : "not ok 20\n");
+print (substr($a,-3) eq 'xyz' ? "ok 21\n" : "not ok 21\n");
+print (substr($a,999) eq '' ? "ok 22\n" : "not ok 22\n");
diff --git a/gnu/usr.bin/perl/perl/t/op/time.t b/gnu/usr.bin/perl/perl/t/op/time.t
new file mode 100755
index 0000000..f8e5545
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/time.t
@@ -0,0 +1,43 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/time.t,v 1.1.1.1 1993/08/23 21:30:03 nate Exp $
+
+print "1..5\n";
+
+($beguser,$begsys) = times;
+
+$beg = time;
+
+while (($now = time) == $beg) {}
+
+if ($now > $beg && $now - $beg < 10){print "ok 1\n";} else {print "not ok 1\n";}
+
+for ($i = 0; $i < 100000; $i++) {
+ ($nowuser, $nowsys) = times;
+ $i = 200000 if $nowuser > $beguser && $nowsys > $begsys;
+ last if time - $beg > 20;
+}
+
+if ($i >= 200000) {print "ok 2\n";} else {print "not ok 2\n";}
+
+($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($beg);
+($xsec,$foo) = localtime($now);
+$localyday = $yday;
+
+if ($sec != $xsec && $mday && $year)
+ {print "ok 3\n";}
+else
+ {print "not ok 3\n";}
+
+($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($beg);
+($xsec,$foo) = localtime($now);
+
+if ($sec != $xsec && $mday && $year)
+ {print "ok 4\n";}
+else
+ {print "not ok 4\n";}
+
+if (index(" :0:1:-1:365:366:-365:-366:",':' . ($localyday - $yday) . ':') > 0)
+ {print "ok 5\n";}
+else
+ {print "not ok 5\n";}
diff --git a/gnu/usr.bin/perl/perl/t/op/undef.t b/gnu/usr.bin/perl/perl/t/op/undef.t
new file mode 100755
index 0000000..b4827db
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/undef.t
@@ -0,0 +1,56 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/undef.t,v 1.1.1.1 1993/08/23 21:30:02 nate Exp $
+
+print "1..21\n";
+
+print defined($a) ? "not ok 1\n" : "ok 1\n";
+
+$a = 1+1;
+print defined($a) ? "ok 2\n" : "not ok 2\n";
+
+undef $a;
+print defined($a) ? "not ok 3\n" : "ok 3\n";
+
+$a = "hi";
+print defined($a) ? "ok 4\n" : "not ok 4\n";
+
+$a = $b;
+print defined($a) ? "not ok 5\n" : "ok 5\n";
+
+@ary = ("1arg");
+$a = pop(@ary);
+print defined($a) ? "ok 6\n" : "not ok 6\n";
+$a = pop(@ary);
+print defined($a) ? "not ok 7\n" : "ok 7\n";
+
+@ary = ("1arg");
+$a = shift(@ary);
+print defined($a) ? "ok 8\n" : "not ok 8\n";
+$a = shift(@ary);
+print defined($a) ? "not ok 9\n" : "ok 9\n";
+
+$ary{'foo'} = 'hi';
+print defined($ary{'foo'}) ? "ok 10\n" : "not ok 10\n";
+print defined($ary{'bar'}) ? "not ok 11\n" : "ok 11\n";
+undef $ary{'foo'};
+print defined($ary{'foo'}) ? "not ok 12\n" : "ok 12\n";
+
+print defined(@ary) ? "ok 13\n" : "not ok 13\n";
+print defined(%ary) ? "ok 14\n" : "not ok 14\n";
+undef @ary;
+print defined(@ary) ? "not ok 15\n" : "ok 15\n";
+undef %ary;
+print defined(%ary) ? "not ok 16\n" : "ok 16\n";
+@ary = (1);
+print defined @ary ? "ok 17\n" : "not ok 17\n";
+%ary = (1,1);
+print defined %ary ? "ok 18\n" : "not ok 18\n";
+
+sub foo { print "ok 19\n"; }
+
+&foo || print "not ok 19\n";
+
+print defined &foo ? "ok 20\n" : "not ok 20\n";
+undef &foo;
+print defined(&foo) ? "not ok 21\n" : "ok 21\n";
diff --git a/gnu/usr.bin/perl/perl/t/op/unshift.t b/gnu/usr.bin/perl/perl/t/op/unshift.t
new file mode 100755
index 0000000..53d7388
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/unshift.t
@@ -0,0 +1,14 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/unshift.t,v 1.1.1.1 1993/08/23 21:30:02 nate Exp $
+
+print "1..2\n";
+
+@a = (1,2,3);
+$cnt1 = unshift(a,0);
+
+if (join(' ',@a) eq '0 1 2 3') {print "ok 1\n";} else {print "not ok 1\n";}
+$cnt2 = unshift(a,3,2,1);
+if (join(' ',@a) eq '3 2 1 0 1 2 3') {print "ok 2\n";} else {print "not ok 2\n";}
+
+
diff --git a/gnu/usr.bin/perl/perl/t/op/vec.t b/gnu/usr.bin/perl/perl/t/op/vec.t
new file mode 100755
index 0000000..5134476
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/vec.t
@@ -0,0 +1,24 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/vec.t,v 1.1.1.1 1993/08/23 21:30:03 nate Exp $
+
+print "1..13\n";
+
+print vec($foo,0,1) == 0 ? "ok 1\n" : "not ok 1\n";
+print length($foo) == 0 ? "ok 2\n" : "not ok 2\n";
+vec($foo,0,1) = 1;
+print length($foo) == 1 ? "ok 3\n" : "not ok 3\n";
+print ord($foo) == 1 ? "ok 4\n" : "not ok 4\n";
+print vec($foo,0,1) == 1 ? "ok 5\n" : "not ok 5\n";
+
+print vec($foo,20,1) == 0 ? "ok 6\n" : "not ok 6\n";
+vec($foo,20,1) = 1;
+print vec($foo,20,1) == 1 ? "ok 7\n" : "not ok 7\n";
+print length($foo) == 3 ? "ok 8\n" : "not ok 8\n";
+print vec($foo,1,8) == 0 ? "ok 9\n" : "not ok 9\n";
+vec($foo,1,8) = 0xf1;
+print vec($foo,1,8) == 0xf1 ? "ok 10\n" : "not ok 10\n";
+print ((ord(substr($foo,1,1)) & 255) == 0xf1 ? "ok 11\n" : "not ok 11\n");
+print vec($foo,2,4) == 1 ? "ok 12\n" : "not ok 12\n";
+print vec($foo,3,4) == 15 ? "ok 13\n" : "not ok 13\n";
+
diff --git a/gnu/usr.bin/perl/perl/t/op/write.t b/gnu/usr.bin/perl/perl/t/op/write.t
new file mode 100755
index 0000000..d17f195
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/op/write.t
@@ -0,0 +1,129 @@
+#!./perl
+
+# $Header: /home/cvs/386BSD/ports/lang/perl/t/op/write.t,v 1.1.1.1 1993/08/23 21:30:02 nate Exp $
+
+print "1..3\n";
+
+format OUT =
+the quick brown @<<
+$fox
+jumped
+@*
+$multiline
+^<<<<<<<<<
+$foo
+^<<<<<<<<<
+$foo
+^<<<<<<...
+$foo
+now @<<the@>>>> for all@|||||men to come @<<<<
+'i' . 's', "time\n", $good, 'to'
+.
+
+open(OUT, '>Op.write.tmp') || die "Can't create Op.write.tmp";
+
+$fox = 'foxiness';
+$good = 'good';
+$multiline = "forescore\nand\nseven years\n";
+$foo = 'when in the course of human events it becomes necessary';
+write(OUT);
+close OUT;
+
+$right =
+"the quick brown fox
+jumped
+forescore
+and
+seven years
+when in
+the course
+of huma...
+now is the time for all good men to come to\n";
+
+if (`cat Op.write.tmp` eq $right)
+ { print "ok 1\n"; unlink 'Op.write.tmp'; }
+else
+ { print "not ok 1\n"; }
+
+format OUT2 =
+the quick brown @<<
+$fox
+jumped
+@*
+$multiline
+^<<<<<<<<< ~~
+$foo
+now @<<the@>>>> for all@|||||men to come @<<<<
+'i' . 's', "time\n", $good, 'to'
+.
+
+open(OUT2, '>Op.write.tmp') || die "Can't create Op.write.tmp";
+
+$fox = 'foxiness';
+$good = 'good';
+$multiline = "forescore\nand\nseven years\n";
+$foo = 'when in the course of human events it becomes necessary';
+write(OUT2);
+close OUT2;
+
+$right =
+"the quick brown fox
+jumped
+forescore
+and
+seven years
+when in
+the course
+of human
+events it
+becomes
+necessary
+now is the time for all good men to come to\n";
+
+if (`cat Op.write.tmp` eq $right)
+ { print "ok 2\n"; unlink 'Op.write.tmp'; }
+else
+ { print "not ok 2\n"; }
+
+eval <<'EOFORMAT';
+format OUT2 =
+the brown quick @<<
+$fox
+jumped
+@*
+$multiline
+^<<<<<<<<< ~~
+$foo
+now @<<the@>>>> for all@|||||men to come @<<<<
+'i' . 's', "time\n", $good, 'to'
+.
+EOFORMAT
+
+open(OUT2, '>Op.write.tmp') || die "Can't create Op.write.tmp";
+
+$fox = 'foxiness';
+$good = 'good';
+$multiline = "forescore\nand\nseven years\n";
+$foo = 'when in the course of human events it becomes necessary';
+write(OUT2);
+close OUT2;
+
+$right =
+"the brown quick fox
+jumped
+forescore
+and
+seven years
+when in
+the course
+of human
+events it
+becomes
+necessary
+now is the time for all good men to come to\n";
+
+if (`cat Op.write.tmp` eq $right)
+ { print "ok 3\n"; unlink 'Op.write.tmp'; }
+else
+ { print "not ok 3\n"; }
+
diff --git a/gnu/usr.bin/perl/perl/t/printme b/gnu/usr.bin/perl/perl/t/printme
new file mode 100644
index 0000000..feefccb2
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/t/printme
@@ -0,0 +1,6 @@
+
+
+
+
+print "Hello World\n";
+
diff --git a/gnu/usr.bin/perl/perl/tdoio.c b/gnu/usr.bin/perl/perl/tdoio.c
new file mode 100644
index 0000000..5dd8065
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/tdoio.c
@@ -0,0 +1,2948 @@
+/* $RCSfile: doio.c,v $$Revision: 1.2 $$Date: 1994/03/09 22:24:27 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: doio.c,v $
+ * Revision 1.2 1994/03/09 22:24:27 ache
+ * (cast) added for last argument of semctl
+ *
+ * Revision 1.1.1.1 1993/08/23 21:29:36 nate
+ * PERL!
+ *
+ * Revision 4.0.1.6 92/06/11 21:08:16 lwall
+ * patch34: some systems don't declare h_errno extern in header files
+ *
+ * Revision 4.0.1.5 92/06/08 13:00:21 lwall
+ * patch20: some machines don't define ENOTSOCK in errno.h
+ * patch20: new warnings for failed use of stat operators on filenames with \n
+ * patch20: wait failed when STDOUT or STDERR reopened to a pipe
+ * patch20: end of file latch not reset on reopen of STDIN
+ * patch20: seek(HANDLE, 0, 1) went to eof because of ancient Ultrix workaround
+ * patch20: fixed memory leak on system() for vfork() machines
+ * patch20: get*by* routines now return something useful in a scalar context
+ * patch20: h_errno now accessible via $?
+ *
+ * Revision 4.0.1.4 91/11/05 16:51:43 lwall
+ * patch11: prepared for ctype implementations that don't define isascii()
+ * patch11: perl mistook some streams for sockets because they return mode 0 too
+ * patch11: reopening STDIN, STDOUT and STDERR failed on some machines
+ * patch11: certain perl errors should set EBADF so that $! looks better
+ * patch11: truncate on a closed filehandle could dump
+ * patch11: stats of _ forgot whether prior stat was actually lstat
+ * patch11: -T returned true on NFS directory
+ *
+ * Revision 4.0.1.3 91/06/10 01:21:19 lwall
+ * patch10: read didn't work from character special files open for writing
+ * patch10: close-on-exec wrongly set on system file descriptors
+ *
+ * Revision 4.0.1.2 91/06/07 10:53:39 lwall
+ * patch4: new copyright notice
+ * patch4: system fd's are now treated specially
+ * patch4: added $^F variable to specify maximum system fd, default 2
+ * patch4: character special files now opened with bidirectional stdio buffers
+ * patch4: taintchecks could improperly modify parent in vfork()
+ * patch4: many, many itty-bitty portability fixes
+ *
+ * Revision 4.0.1.1 91/04/11 17:41:06 lwall
+ * patch1: hopefully straightened out some of the Xenix mess
+ *
+ * Revision 4.0 91/03/20 01:07:06 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+#include <unistd.h>
+#include <stdio.h>
+
+#ifdef HAS_SOCKET
+#include <sys/socket.h>
+#include <netdb.h>
+#ifndef ENOTSOCK
+#include <net/errno.h>
+#endif
+#endif
+
+#ifdef HAS_SELECT
+#ifdef I_SYS_SELECT
+#ifndef I_SYS_TIME
+#include <sys/select.h>
+#endif
+#endif
+#endif
+
+#ifdef HOST_NOT_FOUND
+extern int h_errno;
+#endif
+
+#if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM)
+#include <sys/ipc.h>
+#ifdef HAS_MSG
+#include <sys/msg.h>
+#endif
+#ifdef HAS_SEM
+#include <sys/sem.h>
+#endif
+#ifdef HAS_SHM
+#include <sys/shm.h>
+#endif
+#endif
+
+#ifdef I_PWD
+#include <pwd.h>
+#endif
+#ifdef I_GRP
+#include <grp.h>
+#endif
+#ifdef I_UTIME
+#include <utime.h>
+#endif
+#ifdef I_FCNTL
+#include <fcntl.h>
+#endif
+#ifdef I_SYS_FILE
+#include <sys/file.h>
+#endif
+
+int laststatval = -1;
+int laststype = O_STAT;
+
+static char* warn_nl = "Unsuccessful %s on filename containing newline";
+
+bool
+do_open(stab,name,len)
+STAB *stab;
+register char *name;
+int len;
+{
+ FILE *fp;
+ register STIO *stio = stab_io(stab);
+ char *myname = savestr(name);
+ int result;
+ int fd;
+ int writing = 0;
+ char mode[3]; /* stdio file mode ("r\0" or "r+\0") */
+ FILE *saveifp = Nullfp;
+ FILE *saveofp = Nullfp;
+ char savetype = ' ';
+
+ mode[0] = mode[1] = mode[2] = '\0';
+ name = myname;
+ forkprocess = 1; /* assume true if no fork */
+ while (len && isSPACE(name[len-1]))
+ name[--len] = '\0';
+ if (!stio)
+ stio = stab_io(stab) = stio_new();
+ else if (stio->ifp) {
+ fd = fileno(stio->ifp);
+ if (stio->type == '-')
+ result = 0;
+ else if (fd <= maxsysfd) {
+ saveifp = stio->ifp;
+ saveofp = stio->ofp;
+ savetype = stio->type;
+ result = 0;
+ }
+ else if (stio->type == '|')
+ result = mypclose(stio->ifp);
+ else if (stio->ifp != stio->ofp) {
+ if (stio->ofp) {
+ result = fclose(stio->ofp);
+ fclose(stio->ifp); /* clear stdio, fd already closed */
+ }
+ else
+ result = fclose(stio->ifp);
+ }
+ else
+ result = fclose(stio->ifp);
+ if (result == EOF && fd > maxsysfd)
+ fprintf(stderr,"Warning: unable to close filehandle %s properly.\n",
+ stab_ename(stab));
+ stio->ofp = stio->ifp = Nullfp;
+ }
+ if (*name == '+' && len > 1 && name[len-1] != '|') { /* scary */
+ mode[1] = *name++;
+ mode[2] = '\0';
+ --len;
+ writing = 1;
+ }
+ else {
+ mode[1] = '\0';
+ }
+ stio->type = *name;
+ if (*name == '|') {
+ /*SUPPRESS 530*/
+ for (name++; isSPACE(*name); name++) ;
+#ifdef TAINT
+ taintenv();
+ taintproper("Insecure dependency in piped open");
+#endif
+ fp = mypopen(name,"w");
+ writing = 1;
+ }
+ else if (*name == '>') {
+#ifdef TAINT
+ taintproper("Insecure dependency in open");
+#endif
+ name++;
+ if (*name == '>') {
+ mode[0] = stio->type = 'a';
+ name++;
+ }
+ else
+ mode[0] = 'w';
+ writing = 1;
+ if (*name == '&') {
+ duplicity:
+ name++;
+ while (isSPACE(*name))
+ name++;
+ if (isDIGIT(*name))
+ fd = atoi(name);
+ else {
+ stab = stabent(name,FALSE);
+ if (!stab || !stab_io(stab)) {
+#ifdef EINVAL
+ errno = EINVAL;
+#endif
+ goto say_false;
+ }
+ if (stab_io(stab) && stab_io(stab)->ifp) {
+ fd = fileno(stab_io(stab)->ifp);
+ if (stab_io(stab)->type == 's')
+ stio->type = 's';
+ }
+ else
+ fd = -1;
+ }
+ if (!(fp = fdopen(fd = dup(fd),mode))) {
+ close(fd);
+ }
+ }
+ else {
+ while (isSPACE(*name))
+ name++;
+ if (strEQ(name,"-")) {
+ fp = stdout;
+ stio->type = '-';
+ }
+ else {
+ fp = fopen(name,mode);
+ }
+ }
+ }
+ else {
+ if (*name == '<') {
+ mode[0] = 'r';
+ name++;
+ while (isSPACE(*name))
+ name++;
+ if (*name == '&')
+ goto duplicity;
+ if (strEQ(name,"-")) {
+ fp = stdin;
+ stio->type = '-';
+ }
+ else
+ fp = fopen(name,mode);
+ }
+ else if (name[len-1] == '|') {
+#ifdef TAINT
+ taintenv();
+ taintproper("Insecure dependency in piped open");
+#endif
+ name[--len] = '\0';
+ while (len && isSPACE(name[len-1]))
+ name[--len] = '\0';
+ /*SUPPRESS 530*/
+ for (; isSPACE(*name); name++) ;
+ fp = mypopen(name,"r");
+ stio->type = '|';
+ }
+ else {
+ stio->type = '<';
+ /*SUPPRESS 530*/
+ for (; isSPACE(*name); name++) ;
+ if (strEQ(name,"-")) {
+ fp = stdin;
+ stio->type = '-';
+ }
+ else
+ fp = fopen(name,"r");
+ }
+ }
+ if (!fp) {
+ if (dowarn && stio->type == '<' && index(name, '\n'))
+ warn(warn_nl, "open");
+ Safefree(myname);
+ goto say_false;
+ }
+ Safefree(myname);
+ if (stio->type &&
+ stio->type != '|' && stio->type != '-') {
+ if (fstat(fileno(fp),&statbuf) < 0) {
+ (void)fclose(fp);
+ goto say_false;
+ }
+ if (S_ISSOCK(statbuf.st_mode))
+ stio->type = 's'; /* in case a socket was passed in to us */
+#ifdef HAS_SOCKET
+ else if (
+#ifdef S_IFMT
+ !(statbuf.st_mode & S_IFMT)
+#else
+ !statbuf.st_mode
+#endif
+ ) {
+ int buflen = sizeof tokenbuf;
+ if (getsockname(fileno(fp), tokenbuf, &buflen) >= 0
+ || errno != ENOTSOCK)
+ stio->type = 's'; /* some OS's return 0 on fstat()ed socket */
+ /* but some return 0 for streams too, sigh */
+ }
+#endif
+ }
+ if (saveifp) { /* must use old fp? */
+ fd = fileno(saveifp);
+ if (saveofp) {
+ fflush(saveofp); /* emulate fclose() */
+ if (saveofp != saveifp) { /* was a socket? */
+ fclose(saveofp);
+ if (fd > 2)
+ Safefree(saveofp);
+ }
+ }
+ if (fd != fileno(fp)) {
+ int pid;
+ STR *str;
+
+ dup2(fileno(fp), fd);
+ str = afetch(fdpid,fileno(fp),TRUE);
+ pid = str->str_u.str_useful;
+ str->str_u.str_useful = 0;
+ str = afetch(fdpid,fd,TRUE);
+ str->str_u.str_useful = pid;
+ fclose(fp);
+
+ }
+ fp = saveifp;
+ clearerr(fp);
+ }
+#if defined(HAS_FCNTL) && defined(F_SETFD)
+ fd = fileno(fp);
+ fcntl(fd,F_SETFD,fd > maxsysfd);
+#endif
+ stio->ifp = fp;
+ if (writing) {
+ if (stio->type == 's'
+ || (stio->type == '>' && S_ISCHR(statbuf.st_mode)) ) {
+ if (!(stio->ofp = fdopen(fileno(fp),"w"))) {
+ fclose(fp);
+ stio->ifp = Nullfp;
+ goto say_false;
+ }
+ }
+ else
+ stio->ofp = fp;
+ }
+ return TRUE;
+
+say_false:
+ stio->ifp = saveifp;
+ stio->ofp = saveofp;
+ stio->type = savetype;
+ return FALSE;
+}
+
+FILE *
+nextargv(stab)
+register STAB *stab;
+{
+ register STR *str;
+#ifndef FLEXFILENAMES
+ int filedev;
+ int fileino;
+#endif
+ int fileuid;
+ int filegid;
+ static int filemode = 0;
+ static int lastfd;
+ static char *oldname;
+
+ if (!argvoutstab)
+ argvoutstab = stabent("ARGVOUT",TRUE);
+ if (filemode & (S_ISUID|S_ISGID)) {
+ fflush(stab_io(argvoutstab)->ifp); /* chmod must follow last write */
+#ifdef HAS_FCHMOD
+ (void)fchmod(lastfd,filemode);
+#else
+ (void)chmod(oldname,filemode);
+#endif
+ }
+ filemode = 0;
+ while (alen(stab_xarray(stab)) >= 0) {
+ str = ashift(stab_xarray(stab));
+ str_sset(stab_val(stab),str);
+ STABSET(stab_val(stab));
+ oldname = str_get(stab_val(stab));
+ if (do_open(stab,oldname,stab_val(stab)->str_cur)) {
+ if (inplace) {
+#ifdef TAINT
+ taintproper("Insecure dependency in inplace open");
+#endif
+ if (strEQ(oldname,"-")) {
+ str_free(str);
+ defoutstab = stabent("STDOUT",TRUE);
+ return stab_io(stab)->ifp;
+ }
+#ifndef FLEXFILENAMES
+ filedev = statbuf.st_dev;
+ fileino = statbuf.st_ino;
+#endif
+ filemode = statbuf.st_mode;
+ fileuid = statbuf.st_uid;
+ filegid = statbuf.st_gid;
+ if (!S_ISREG(filemode)) {
+ warn("Can't do inplace edit: %s is not a regular file",
+ oldname );
+ do_close(stab,FALSE);
+ str_free(str);
+ continue;
+ }
+ if (*inplace) {
+#ifdef SUFFIX
+ add_suffix(str,inplace);
+#else
+ str_cat(str,inplace);
+#endif
+#ifndef FLEXFILENAMES
+ if (stat(str->str_ptr,&statbuf) >= 0
+ && statbuf.st_dev == filedev
+ && statbuf.st_ino == fileino ) {
+ warn("Can't do inplace edit: %s > 14 characters",
+ str->str_ptr );
+ do_close(stab,FALSE);
+ str_free(str);
+ continue;
+ }
+#endif
+#ifdef HAS_RENAME
+#ifndef DOSISH
+ if (rename(oldname,str->str_ptr) < 0) {
+ warn("Can't rename %s to %s: %s, skipping file",
+ oldname, str->str_ptr, strerror(errno) );
+ do_close(stab,FALSE);
+ str_free(str);
+ continue;
+ }
+#else
+ do_close(stab,FALSE);
+ (void)unlink(str->str_ptr);
+ (void)rename(oldname,str->str_ptr);
+ do_open(stab,str->str_ptr,stab_val(stab)->str_cur);
+#endif /* MSDOS */
+#else
+ (void)UNLINK(str->str_ptr);
+ if (link(oldname,str->str_ptr) < 0) {
+ warn("Can't rename %s to %s: %s, skipping file",
+ oldname, str->str_ptr, strerror(errno) );
+ do_close(stab,FALSE);
+ str_free(str);
+ continue;
+ }
+ (void)UNLINK(oldname);
+#endif
+ }
+ else {
+#ifndef DOSISH
+ if (UNLINK(oldname) < 0) {
+ warn("Can't rename %s to %s: %s, skipping file",
+ oldname, str->str_ptr, strerror(errno) );
+ do_close(stab,FALSE);
+ str_free(str);
+ continue;
+ }
+#else
+ fatal("Can't do inplace edit without backup");
+#endif
+ }
+
+ str_nset(str,">",1);
+ str_cat(str,oldname);
+ errno = 0; /* in case sprintf set errno */
+ if (!do_open(argvoutstab,str->str_ptr,str->str_cur)) {
+ warn("Can't do inplace edit on %s: %s",
+ oldname, strerror(errno) );
+ do_close(stab,FALSE);
+ str_free(str);
+ continue;
+ }
+ defoutstab = argvoutstab;
+ lastfd = fileno(stab_io(argvoutstab)->ifp);
+ (void)fstat(lastfd,&statbuf);
+#ifdef HAS_FCHMOD
+ (void)fchmod(lastfd,filemode);
+#else
+ (void)chmod(oldname,filemode);
+#endif
+ if (fileuid != statbuf.st_uid || filegid != statbuf.st_gid) {
+#ifdef HAS_FCHOWN
+ (void)fchown(lastfd,fileuid,filegid);
+#else
+#ifdef HAS_CHOWN
+ (void)chown(oldname,fileuid,filegid);
+#endif
+#endif
+ }
+ }
+ str_free(str);
+ return stab_io(stab)->ifp;
+ }
+ else
+ fprintf(stderr,"Can't open %s: %s\n",str_get(str), strerror(errno));
+ str_free(str);
+ }
+ if (inplace) {
+ (void)do_close(argvoutstab,FALSE);
+ defoutstab = stabent("STDOUT",TRUE);
+ }
+ return Nullfp;
+}
+
+#ifdef HAS_PIPE
+void
+do_pipe(str, rstab, wstab)
+STR *str;
+STAB *rstab;
+STAB *wstab;
+{
+ register STIO *rstio;
+ register STIO *wstio;
+ int fd[2];
+
+ if (!rstab)
+ goto badexit;
+ if (!wstab)
+ goto badexit;
+
+ rstio = stab_io(rstab);
+ wstio = stab_io(wstab);
+
+ if (!rstio)
+ rstio = stab_io(rstab) = stio_new();
+ else if (rstio->ifp)
+ do_close(rstab,FALSE);
+ if (!wstio)
+ wstio = stab_io(wstab) = stio_new();
+ else if (wstio->ifp)
+ do_close(wstab,FALSE);
+
+ if (pipe(fd) < 0)
+ goto badexit;
+ rstio->ifp = fdopen(fd[0], "r");
+ wstio->ofp = fdopen(fd[1], "w");
+ wstio->ifp = wstio->ofp;
+ rstio->type = '<';
+ wstio->type = '>';
+ if (!rstio->ifp || !wstio->ofp) {
+ if (rstio->ifp) fclose(rstio->ifp);
+ else close(fd[0]);
+ if (wstio->ofp) fclose(wstio->ofp);
+ else close(fd[1]);
+ goto badexit;
+ }
+
+ str_sset(str,&str_yes);
+ return;
+
+badexit:
+ str_sset(str,&str_undef);
+ return;
+}
+#endif
+
+bool
+do_close(stab,explicit)
+STAB *stab;
+bool explicit;
+{
+ bool retval = FALSE;
+ register STIO *stio;
+ int status;
+
+ if (!stab)
+ stab = argvstab;
+ if (!stab) {
+ errno = EBADF;
+ return FALSE;
+ }
+ stio = stab_io(stab);
+ if (!stio) { /* never opened */
+ if (dowarn && explicit)
+ warn("Close on unopened file <%s>",stab_ename(stab));
+ return FALSE;
+ }
+ if (stio->ifp) {
+ if (stio->type == '|') {
+ status = mypclose(stio->ifp);
+ retval = (status == 0);
+ statusvalue = (unsigned short)status & 0xffff;
+ }
+ else if (stio->type == '-')
+ retval = TRUE;
+ else {
+ if (stio->ofp && stio->ofp != stio->ifp) { /* a socket */
+ retval = (fclose(stio->ofp) != EOF);
+ fclose(stio->ifp); /* clear stdio, fd already closed */
+ }
+ else
+ retval = (fclose(stio->ifp) != EOF);
+ }
+ stio->ofp = stio->ifp = Nullfp;
+ }
+ if (explicit)
+ stio->lines = 0;
+ stio->type = ' ';
+ return retval;
+}
+
+bool
+do_eof(stab)
+STAB *stab;
+{
+ register STIO *stio;
+ int ch;
+
+ if (!stab) { /* eof() */
+ if (argvstab)
+ stio = stab_io(argvstab);
+ else
+ return TRUE;
+ }
+ else
+ stio = stab_io(stab);
+
+ if (!stio)
+ return TRUE;
+
+ while (stio->ifp) {
+
+#ifdef STDSTDIO /* (the code works without this) */
+ if (stio->ifp->_cnt > 0) /* cheat a little, since */
+ return FALSE; /* this is the most usual case */
+#endif
+
+ ch = getc(stio->ifp);
+ if (ch != EOF) {
+ (void)ungetc(ch, stio->ifp);
+ return FALSE;
+ }
+#ifdef STDSTDIO
+ if (stio->ifp->_cnt < -1)
+ stio->ifp->_cnt = -1;
+#endif
+ if (!stab) { /* not necessarily a real EOF yet? */
+ if (!nextargv(argvstab)) /* get another fp handy */
+ return TRUE;
+ }
+ else
+ return TRUE; /* normal fp, definitely end of file */
+ }
+ return TRUE;
+}
+
+long
+do_tell(stab)
+STAB *stab;
+{
+ register STIO *stio;
+
+ if (!stab)
+ goto phooey;
+
+ stio = stab_io(stab);
+ if (!stio || !stio->ifp)
+ goto phooey;
+
+#ifdef ULTRIX_STDIO_BOTCH
+ if (feof(stio->ifp))
+ (void)fseek (stio->ifp, 0L, 2); /* ultrix 1.2 workaround */
+#endif
+
+ return ftell(stio->ifp);
+
+phooey:
+ if (dowarn)
+ warn("tell() on unopened file");
+ errno = EBADF;
+ return -1L;
+}
+
+bool
+do_seek(stab, pos, whence)
+STAB *stab;
+long pos;
+int whence;
+{
+ register STIO *stio;
+
+ if (!stab)
+ goto nuts;
+
+ stio = stab_io(stab);
+ if (!stio || !stio->ifp)
+ goto nuts;
+
+#ifdef ULTRIX_STDIO_BOTCH
+ if (feof(stio->ifp))
+ (void)fseek (stio->ifp, 0L, 2); /* ultrix 1.2 workaround */
+#endif
+
+ return fseek(stio->ifp, pos, whence) >= 0;
+
+nuts:
+ if (dowarn)
+ warn("seek() on unopened file");
+ errno = EBADF;
+ return FALSE;
+}
+
+int
+do_ctl(optype,stab,func,argstr)
+int optype;
+STAB *stab;
+int func;
+STR *argstr;
+{
+ register STIO *stio;
+ register char *s;
+ int retval;
+
+ if (!stab || !argstr || !(stio = stab_io(stab)) || !stio->ifp) {
+ errno = EBADF; /* well, sort of... */
+ return -1;
+ }
+
+ if (argstr->str_pok || !argstr->str_nok) {
+ if (!argstr->str_pok)
+ s = str_get(argstr);
+
+#ifdef IOCPARM_MASK
+#ifndef IOCPARM_LEN
+#define IOCPARM_LEN(x) (((x) >> 16) & IOCPARM_MASK)
+#endif
+#endif
+#ifdef IOCPARM_LEN
+ retval = IOCPARM_LEN(func); /* on BSDish systes we're safe */
+#else
+ retval = 256; /* otherwise guess at what's safe */
+#endif
+ if (argstr->str_cur < retval) {
+ Str_Grow(argstr,retval+1);
+ argstr->str_cur = retval;
+ }
+
+ s = argstr->str_ptr;
+ s[argstr->str_cur] = 17; /* a little sanity check here */
+ }
+ else {
+ retval = (int)str_gnum(argstr);
+#ifdef DOSISH
+ s = (char*)(long)retval; /* ouch */
+#else
+ s = (char*)retval; /* ouch */
+#endif
+ }
+
+#ifndef lint
+ if (optype == O_IOCTL)
+ retval = ioctl(fileno(stio->ifp), func, s);
+ else
+#ifdef DOSISH
+ fatal("fcntl is not implemented");
+#else
+#ifdef HAS_FCNTL
+ retval = fcntl(fileno(stio->ifp), func, s);
+#else
+ fatal("fcntl is not implemented");
+#endif
+#endif
+#else /* lint */
+ retval = 0;
+#endif /* lint */
+
+ if (argstr->str_pok) {
+ if (s[argstr->str_cur] != 17)
+ fatal("Return value overflowed string");
+ s[argstr->str_cur] = 0; /* put our null back */
+ }
+ return retval;
+}
+
+int
+do_stat(str,arg,gimme,arglast)
+STR *str;
+register ARG *arg;
+int gimme;
+int *arglast;
+{
+ register ARRAY *ary = stack;
+ register int sp = arglast[0] + 1;
+ int max = 13;
+
+ if ((arg[1].arg_type & A_MASK) == A_WORD) {
+ tmpstab = arg[1].arg_ptr.arg_stab;
+ if (tmpstab != defstab) {
+ laststype = O_STAT;
+ statstab = tmpstab;
+ str_set(statname,"");
+ if (!stab_io(tmpstab) || !stab_io(tmpstab)->ifp ||
+ fstat(fileno(stab_io(tmpstab)->ifp),&statcache) < 0) {
+ max = 0;
+ laststatval = -1;
+ }
+ }
+ else if (laststatval < 0)
+ max = 0;
+ }
+ else {
+ str_set(statname,str_get(ary->ary_array[sp]));
+ statstab = Nullstab;
+#ifdef HAS_LSTAT
+ laststype = arg->arg_type;
+ if (arg->arg_type == O_LSTAT)
+ laststatval = lstat(str_get(statname),&statcache);
+ else
+#endif
+ laststatval = stat(str_get(statname),&statcache);
+ if (laststatval < 0) {
+ if (dowarn && index(str_get(statname), '\n'))
+ warn(warn_nl, "stat");
+ max = 0;
+ }
+ }
+
+ if (gimme != G_ARRAY) {
+ if (max)
+ str_sset(str,&str_yes);
+ else
+ str_sset(str,&str_undef);
+ STABSET(str);
+ ary->ary_array[sp] = str;
+ return sp;
+ }
+ sp--;
+ if (max) {
+#ifndef lint
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_dev)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_ino)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_mode)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_nlink)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_uid)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_gid)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_rdev)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_size)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_atime)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_mtime)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_ctime)));
+#ifdef STATBLOCKS
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_blksize)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_nmake((double)statcache.st_blocks)));
+#else
+ (void)astore(ary,++sp,
+ str_2mortal(str_make("",0)));
+ (void)astore(ary,++sp,
+ str_2mortal(str_make("",0)));
+#endif
+#else /* lint */
+ (void)astore(ary,++sp,str_nmake(0.0));
+#endif /* lint */
+ }
+ return sp;
+}
+
+#if !defined(HAS_TRUNCATE) && !defined(HAS_CHSIZE) && defined(F_FREESP)
+ /* code courtesy of William Kucharski */
+#define HAS_CHSIZE
+
+int chsize(fd, length)
+int fd; /* file descriptor */
+off_t length; /* length to set file to */
+{
+ extern long lseek();
+ struct flock fl;
+ struct stat filebuf;
+
+ if (fstat(fd, &filebuf) < 0)
+ return -1;
+
+ if (filebuf.st_size < length) {
+
+ /* extend file length */
+
+ if ((lseek(fd, (length - 1), 0)) < 0)
+ return -1;
+
+ /* write a "0" byte */
+
+ if ((write(fd, "", 1)) != 1)
+ return -1;
+ }
+ else {
+ /* truncate length */
+
+ fl.l_whence = 0;
+ fl.l_len = 0;
+ fl.l_start = length;
+ fl.l_type = F_WRLCK; /* write lock on file space */
+
+ /*
+ * This relies on the UNDOCUMENTED F_FREESP argument to
+ * fcntl(2), which truncates the file so that it ends at the
+ * position indicated by fl.l_start.
+ *
+ * Will minor miracles never cease?
+ */
+
+ if (fcntl(fd, F_FREESP, &fl) < 0)
+ return -1;
+
+ }
+
+ return 0;
+}
+#endif /* F_FREESP */
+
+int /*SUPPRESS 590*/
+do_truncate(str,arg,gimme,arglast)
+STR *str;
+register ARG *arg;
+int gimme;
+int *arglast;
+{
+ register ARRAY *ary = stack;
+ register int sp = arglast[0] + 1;
+ off_t len = (off_t)str_gnum(ary->ary_array[sp+1]);
+ int result = 1;
+ STAB *tmpstab;
+
+#if defined(HAS_TRUNCATE) || defined(HAS_CHSIZE)
+#ifdef HAS_TRUNCATE
+ if ((arg[1].arg_type & A_MASK) == A_WORD) {
+ tmpstab = arg[1].arg_ptr.arg_stab;
+ if (!stab_io(tmpstab) || !stab_io(tmpstab)->ifp ||
+ ftruncate(fileno(stab_io(tmpstab)->ifp), len) < 0)
+ result = 0;
+ }
+ else if (truncate(str_get(ary->ary_array[sp]), len) < 0)
+ result = 0;
+#else
+ if ((arg[1].arg_type & A_MASK) == A_WORD) {
+ tmpstab = arg[1].arg_ptr.arg_stab;
+ if (!stab_io(tmpstab) || !stab_io(tmpstab)->ifp ||
+ chsize(fileno(stab_io(tmpstab)->ifp), len) < 0)
+ result = 0;
+ }
+ else {
+ int tmpfd;
+
+ if ((tmpfd = open(str_get(ary->ary_array[sp]), 0)) < 0)
+ result = 0;
+ else {
+ if (chsize(tmpfd, len) < 0)
+ result = 0;
+ close(tmpfd);
+ }
+ }
+#endif
+
+ if (result)
+ str_sset(str,&str_yes);
+ else
+ str_sset(str,&str_undef);
+ STABSET(str);
+ ary->ary_array[sp] = str;
+ return sp;
+#else
+ fatal("truncate not implemented");
+#endif
+}
+
+int
+looks_like_number(str)
+STR *str;
+{
+ register char *s;
+ register char *send;
+
+ if (!str->str_pok)
+ return TRUE;
+ s = str->str_ptr;
+ send = s + str->str_cur;
+ while (isSPACE(*s))
+ s++;
+ if (s >= send)
+ return FALSE;
+ if (*s == '+' || *s == '-')
+ s++;
+ while (isDIGIT(*s))
+ s++;
+ if (s == send)
+ return TRUE;
+ if (*s == '.')
+ s++;
+ else if (s == str->str_ptr)
+ return FALSE;
+ while (isDIGIT(*s))
+ s++;
+ if (s == send)
+ return TRUE;
+ if (*s == 'e' || *s == 'E') {
+ s++;
+ if (*s == '+' || *s == '-')
+ s++;
+ while (isDIGIT(*s))
+ s++;
+ }
+ while (isSPACE(*s))
+ s++;
+ if (s >= send)
+ return TRUE;
+ return FALSE;
+}
+
+bool
+do_print(str,fp)
+register STR *str;
+FILE *fp;
+{
+ register char *tmps;
+
+ if (!fp) {
+ if (dowarn)
+ warn("print to unopened file");
+ errno = EBADF;
+ return FALSE;
+ }
+ if (!str)
+ return TRUE;
+ if (ofmt &&
+ ((str->str_nok && str->str_u.str_nval != 0.0)
+ || (looks_like_number(str) && str_gnum(str) != 0.0) ) ) {
+ fprintf(fp, ofmt, str->str_u.str_nval);
+ return !ferror(fp);
+ }
+ else {
+ tmps = str_get(str);
+ if (*tmps == 'S' && tmps[1] == 't' && tmps[2] == 'B' && tmps[3] == '\0'
+ && str->str_cur == sizeof(STBP) && strlen(tmps) < str->str_cur) {
+ STR *tmpstr = str_mortal(&str_undef);
+ stab_efullname(tmpstr,((STAB*)str));/* a stab value, be nice */
+ str = tmpstr;
+ tmps = str->str_ptr;
+ putc('*',fp);
+ }
+ if (str->str_cur && (fwrite(tmps,1,str->str_cur,fp) == 0 || ferror(fp)))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool
+do_aprint(arg,fp,arglast)
+register ARG *arg;
+register FILE *fp;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register int retval;
+ register int items = arglast[2] - sp;
+
+ if (!fp) {
+ if (dowarn)
+ warn("print to unopened file");
+ errno = EBADF;
+ return FALSE;
+ }
+ st += ++sp;
+ if (arg->arg_type == O_PRTF) {
+ do_sprintf(arg->arg_ptr.arg_str,items,st);
+ retval = do_print(arg->arg_ptr.arg_str,fp);
+ }
+ else {
+ retval = (items <= 0);
+ for (; items > 0; items--,st++) {
+ if (retval && ofslen) {
+ if (fwrite(ofs, 1, ofslen, fp) == 0 || ferror(fp)) {
+ retval = FALSE;
+ break;
+ }
+ }
+ if (!(retval = do_print(*st, fp)))
+ break;
+ }
+ if (retval && orslen)
+ if (fwrite(ors, 1, orslen, fp) == 0 || ferror(fp))
+ retval = FALSE;
+ }
+ return retval;
+}
+
+int
+mystat(arg,str)
+ARG *arg;
+STR *str;
+{
+ STIO *stio;
+
+ if (arg[1].arg_type & A_DONT) {
+ stio = stab_io(arg[1].arg_ptr.arg_stab);
+ if (stio && stio->ifp) {
+ statstab = arg[1].arg_ptr.arg_stab;
+ str_set(statname,"");
+ laststype = O_STAT;
+ return (laststatval = fstat(fileno(stio->ifp), &statcache));
+ }
+ else {
+ if (arg[1].arg_ptr.arg_stab == defstab)
+ return laststatval;
+ if (dowarn)
+ warn("Stat on unopened file <%s>",
+ stab_ename(arg[1].arg_ptr.arg_stab));
+ statstab = Nullstab;
+ str_set(statname,"");
+ return (laststatval = -1);
+ }
+ }
+ else {
+ statstab = Nullstab;
+ str_set(statname,str_get(str));
+ laststype = O_STAT;
+ laststatval = stat(str_get(str),&statcache);
+ if (laststatval < 0 && dowarn && index(str_get(str), '\n'))
+ warn(warn_nl, "stat");
+ return laststatval;
+ }
+}
+
+int
+mylstat(arg,str)
+ARG *arg;
+STR *str;
+{
+ if (arg[1].arg_type & A_DONT) {
+ if (arg[1].arg_ptr.arg_stab == defstab) {
+ if (laststype != O_LSTAT)
+ fatal("The stat preceding -l _ wasn't an lstat");
+ return laststatval;
+ }
+ fatal("You can't use -l on a filehandle");
+ }
+
+ laststype = O_LSTAT;
+ statstab = Nullstab;
+ str_set(statname,str_get(str));
+#ifdef HAS_LSTAT
+ laststatval = lstat(str_get(str),&statcache);
+#else
+ laststatval = stat(str_get(str),&statcache);
+#endif
+ if (laststatval < 0 && dowarn && index(str_get(str), '\n'))
+ warn(warn_nl, "lstat");
+ return laststatval;
+}
+
+STR *
+do_fttext(arg,str)
+register ARG *arg;
+STR *str;
+{
+ int i;
+ int len;
+ int odd = 0;
+ STDCHAR tbuf[512];
+ register STDCHAR *s;
+ register STIO *stio;
+
+ if (arg[1].arg_type & A_DONT) {
+ if (arg[1].arg_ptr.arg_stab == defstab) {
+ if (statstab)
+ stio = stab_io(statstab);
+ else {
+ str = statname;
+ goto really_filename;
+ }
+ }
+ else {
+ statstab = arg[1].arg_ptr.arg_stab;
+ str_set(statname,"");
+ stio = stab_io(statstab);
+ }
+ if (stio && stio->ifp) {
+#if defined(STDSTDIO) || defined(atarist) /* this will work with atariST */
+ fstat(fileno(stio->ifp),&statcache);
+ if (S_ISDIR(statcache.st_mode)) /* handle NFS glitch */
+ return arg->arg_type == O_FTTEXT ? &str_no : &str_yes;
+ if (stio->ifp->_cnt <= 0) {
+ i = getc(stio->ifp);
+ if (i != EOF)
+ (void)ungetc(i,stio->ifp);
+ }
+ if (stio->ifp->_cnt <= 0) /* null file is anything */
+ return &str_yes;
+ len = stio->ifp->_cnt + (stio->ifp->_ptr - stio->ifp->_base);
+ s = stio->ifp->_base;
+#else
+ fatal("-T and -B not implemented on filehandles");
+#endif
+ }
+ else {
+ if (dowarn)
+ warn("Test on unopened file <%s>",
+ stab_ename(arg[1].arg_ptr.arg_stab));
+ errno = EBADF;
+ return &str_undef;
+ }
+ }
+ else {
+ statstab = Nullstab;
+ str_set(statname,str_get(str));
+ really_filename:
+ i = open(str_get(str),0);
+ if (i < 0) {
+ if (dowarn && index(str_get(str), '\n'))
+ warn(warn_nl, "open");
+ return &str_undef;
+ }
+ fstat(i,&statcache);
+ len = read(i,tbuf,512);
+ (void)close(i);
+ if (len <= 0) {
+ if (S_ISDIR(statcache.st_mode) && arg->arg_type == O_FTTEXT)
+ return &str_no; /* special case NFS directories */
+ return &str_yes; /* null file is anything */
+ }
+ s = tbuf;
+ }
+
+ /* now scan s to look for textiness */
+
+ for (i = 0; i < len; i++,s++) {
+ if (!*s) { /* null never allowed in text */
+ odd += len;
+ break;
+ }
+ else if (*s & 128)
+ odd++;
+ else if (*s < 32 &&
+ *s != '\n' && *s != '\r' && *s != '\b' &&
+ *s != '\t' && *s != '\f' && *s != 27)
+ odd++;
+ }
+
+ if ((odd * 10 > len) == (arg->arg_type == O_FTTEXT)) /* allow 10% odd */
+ return &str_no;
+ else
+ return &str_yes;
+}
+
+static char **Argv = Null(char **);
+static char *Cmd = Nullch;
+
+bool
+do_aexec(really,arglast)
+STR *really;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register int items = arglast[2] - sp;
+ register char **a;
+ char *tmps;
+
+ if (items) {
+ New(401,Argv, items+1, char*);
+ a = Argv;
+ for (st += ++sp; items > 0; items--,st++) {
+ if (*st)
+ *a++ = str_get(*st);
+ else
+ *a++ = "";
+ }
+ *a = Nullch;
+#ifdef TAINT
+ if (*Argv[0] != '/') /* will execvp use PATH? */
+ taintenv(); /* testing IFS here is overkill, probably */
+#endif
+ if (really && *(tmps = str_get(really)))
+ execvp(tmps,Argv);
+ else
+ execvp(Argv[0],Argv);
+ }
+ do_execfree();
+ return FALSE;
+}
+
+void
+do_execfree()
+{
+ if (Argv) {
+ Safefree(Argv);
+ Argv = Null(char **);
+ }
+ if (Cmd) {
+ Safefree(Cmd);
+ Cmd = Nullch;
+ }
+}
+
+bool
+do_exec(cmd)
+char *cmd;
+{
+ register char **a;
+ register char *s;
+ char flags[10];
+
+ /* save an extra exec if possible */
+
+#ifdef CSH
+ if (strnEQ(cmd,cshname,cshlen) && strnEQ(cmd+cshlen," -c",3)) {
+ strcpy(flags,"-c");
+ s = cmd+cshlen+3;
+ if (*s == 'f') {
+ s++;
+ strcat(flags,"f");
+ }
+ if (*s == ' ')
+ s++;
+ if (*s++ == '\'') {
+ char *ncmd = s;
+
+ while (*s)
+ s++;
+ if (s[-1] == '\n')
+ *--s = '\0';
+ if (s[-1] == '\'') {
+ *--s = '\0';
+ execl(cshname,"csh", flags,ncmd,(char*)0);
+ *s = '\'';
+ return FALSE;
+ }
+ }
+ }
+#endif /* CSH */
+
+ /* see if there are shell metacharacters in it */
+
+ /*SUPPRESS 530*/
+ for (s = cmd; *s && isALPHA(*s); s++) ; /* catch VAR=val gizmo */
+ if (*s == '=')
+ goto doshell;
+ for (s = cmd; *s; s++) {
+ if (*s != ' ' && !isALPHA(*s) && index("$&*(){}[]'\";\\|?<>~`\n",*s)) {
+ if (*s == '\n' && !s[1]) {
+ *s = '\0';
+ break;
+ }
+ doshell:
+ execl("/bin/sh","sh","-c",cmd,(char*)0);
+ return FALSE;
+ }
+ }
+ New(402,Argv, (s - cmd) / 2 + 2, char*);
+ Cmd = nsavestr(cmd, s-cmd);
+ a = Argv;
+ for (s = Cmd; *s;) {
+ while (*s && isSPACE(*s)) s++;
+ if (*s)
+ *(a++) = s;
+ while (*s && !isSPACE(*s)) s++;
+ if (*s)
+ *s++ = '\0';
+ }
+ *a = Nullch;
+ if (Argv[0]) {
+ execvp(Argv[0],Argv);
+ if (errno == ENOEXEC) { /* for system V NIH syndrome */
+ do_execfree();
+ goto doshell;
+ }
+ }
+ do_execfree();
+ return FALSE;
+}
+
+#ifdef HAS_SOCKET
+int
+do_socket(stab, arglast)
+STAB *stab;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register STIO *stio;
+ int domain, type, protocol, fd;
+
+ if (!stab) {
+ errno = EBADF;
+ return FALSE;
+ }
+
+ stio = stab_io(stab);
+ if (!stio)
+ stio = stab_io(stab) = stio_new();
+ else if (stio->ifp)
+ do_close(stab,FALSE);
+
+ domain = (int)str_gnum(st[++sp]);
+ type = (int)str_gnum(st[++sp]);
+ protocol = (int)str_gnum(st[++sp]);
+#ifdef TAINT
+ taintproper("Insecure dependency in socket");
+#endif
+ fd = socket(domain,type,protocol);
+ if (fd < 0)
+ return FALSE;
+ stio->ifp = fdopen(fd, "r"); /* stdio gets confused about sockets */
+ stio->ofp = fdopen(fd, "w");
+ stio->type = 's';
+ if (!stio->ifp || !stio->ofp) {
+ if (stio->ifp) fclose(stio->ifp);
+ if (stio->ofp) fclose(stio->ofp);
+ if (!stio->ifp && !stio->ofp) close(fd);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+int
+do_bind(stab, arglast)
+STAB *stab;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register STIO *stio;
+ char *addr;
+
+ if (!stab)
+ goto nuts;
+
+ stio = stab_io(stab);
+ if (!stio || !stio->ifp)
+ goto nuts;
+
+ addr = str_get(st[++sp]);
+#ifdef TAINT
+ taintproper("Insecure dependency in bind");
+#endif
+ return bind(fileno(stio->ifp), addr, st[sp]->str_cur) >= 0;
+
+nuts:
+ if (dowarn)
+ warn("bind() on closed fd");
+ errno = EBADF;
+ return FALSE;
+
+}
+
+int
+do_connect(stab, arglast)
+STAB *stab;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register STIO *stio;
+ char *addr;
+
+ if (!stab)
+ goto nuts;
+
+ stio = stab_io(stab);
+ if (!stio || !stio->ifp)
+ goto nuts;
+
+ addr = str_get(st[++sp]);
+#ifdef TAINT
+ taintproper("Insecure dependency in connect");
+#endif
+ return connect(fileno(stio->ifp), addr, st[sp]->str_cur) >= 0;
+
+nuts:
+ if (dowarn)
+ warn("connect() on closed fd");
+ errno = EBADF;
+ return FALSE;
+
+}
+
+int
+do_listen(stab, arglast)
+STAB *stab;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register STIO *stio;
+ int backlog;
+
+ if (!stab)
+ goto nuts;
+
+ stio = stab_io(stab);
+ if (!stio || !stio->ifp)
+ goto nuts;
+
+ backlog = (int)str_gnum(st[++sp]);
+ return listen(fileno(stio->ifp), backlog) >= 0;
+
+nuts:
+ if (dowarn)
+ warn("listen() on closed fd");
+ errno = EBADF;
+ return FALSE;
+}
+
+void
+do_accept(str, nstab, gstab)
+STR *str;
+STAB *nstab;
+STAB *gstab;
+{
+ register STIO *nstio;
+ register STIO *gstio;
+ int len = sizeof buf;
+ int fd;
+
+ if (!nstab)
+ goto badexit;
+ if (!gstab)
+ goto nuts;
+
+ gstio = stab_io(gstab);
+ nstio = stab_io(nstab);
+
+ if (!gstio || !gstio->ifp)
+ goto nuts;
+ if (!nstio)
+ nstio = stab_io(nstab) = stio_new();
+ else if (nstio->ifp)
+ do_close(nstab,FALSE);
+
+ fd = accept(fileno(gstio->ifp),(struct sockaddr *)buf,&len);
+ if (fd < 0)
+ goto badexit;
+ nstio->ifp = fdopen(fd, "r");
+ nstio->ofp = fdopen(fd, "w");
+ nstio->type = 's';
+ if (!nstio->ifp || !nstio->ofp) {
+ if (nstio->ifp) fclose(nstio->ifp);
+ if (nstio->ofp) fclose(nstio->ofp);
+ if (!nstio->ifp && !nstio->ofp) close(fd);
+ goto badexit;
+ }
+
+ str_nset(str, buf, len);
+ return;
+
+nuts:
+ if (dowarn)
+ warn("accept() on closed fd");
+ errno = EBADF;
+badexit:
+ str_sset(str,&str_undef);
+ return;
+}
+
+int
+do_shutdown(stab, arglast)
+STAB *stab;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register STIO *stio;
+ int how;
+
+ if (!stab)
+ goto nuts;
+
+ stio = stab_io(stab);
+ if (!stio || !stio->ifp)
+ goto nuts;
+
+ how = (int)str_gnum(st[++sp]);
+ return shutdown(fileno(stio->ifp), how) >= 0;
+
+nuts:
+ if (dowarn)
+ warn("shutdown() on closed fd");
+ errno = EBADF;
+ return FALSE;
+
+}
+
+int
+do_sopt(optype, stab, arglast)
+int optype;
+STAB *stab;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register STIO *stio;
+ int fd;
+ unsigned int lvl;
+ unsigned int optname;
+
+ if (!stab)
+ goto nuts;
+
+ stio = stab_io(stab);
+ if (!stio || !stio->ifp)
+ goto nuts;
+
+ fd = fileno(stio->ifp);
+ lvl = (unsigned int)str_gnum(st[sp+1]);
+ optname = (unsigned int)str_gnum(st[sp+2]);
+ switch (optype) {
+ case O_GSOCKOPT:
+ st[sp] = str_2mortal(Str_new(22,257));
+ st[sp]->str_cur = 256;
+ st[sp]->str_pok = 1;
+ if (getsockopt(fd, lvl, optname, st[sp]->str_ptr,
+ (int*)&st[sp]->str_cur) < 0)
+ goto nuts;
+ break;
+ case O_SSOCKOPT:
+ st[sp] = st[sp+3];
+ if (setsockopt(fd, lvl, optname, st[sp]->str_ptr, st[sp]->str_cur) < 0)
+ goto nuts;
+ st[sp] = &str_yes;
+ break;
+ }
+
+ return sp;
+
+nuts:
+ if (dowarn)
+ warn("[gs]etsockopt() on closed fd");
+ st[sp] = &str_undef;
+ errno = EBADF;
+ return sp;
+
+}
+
+int
+do_getsockname(optype, stab, arglast)
+int optype;
+STAB *stab;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register STIO *stio;
+ int fd;
+
+ if (!stab)
+ goto nuts;
+
+ stio = stab_io(stab);
+ if (!stio || !stio->ifp)
+ goto nuts;
+
+ st[sp] = str_2mortal(Str_new(22,257));
+ st[sp]->str_cur = 256;
+ st[sp]->str_pok = 1;
+ fd = fileno(stio->ifp);
+ switch (optype) {
+ case O_GETSOCKNAME:
+ if (getsockname(fd, st[sp]->str_ptr, (int*)&st[sp]->str_cur) < 0)
+ goto nuts2;
+ break;
+ case O_GETPEERNAME:
+ if (getpeername(fd, st[sp]->str_ptr, (int*)&st[sp]->str_cur) < 0)
+ goto nuts2;
+ break;
+ }
+
+ return sp;
+
+nuts:
+ if (dowarn)
+ warn("get{sock,peer}name() on closed fd");
+ errno = EBADF;
+nuts2:
+ st[sp] = &str_undef;
+ return sp;
+
+}
+
+int
+do_ghent(which,gimme,arglast)
+int which;
+int gimme;
+int *arglast;
+{
+ register ARRAY *ary = stack;
+ register int sp = arglast[0];
+ register char **elem;
+ register STR *str;
+ struct hostent *gethostbyname();
+ struct hostent *gethostbyaddr();
+#ifdef HAS_GETHOSTENT
+ struct hostent *gethostent();
+#endif
+ struct hostent *hent;
+ unsigned long len;
+
+ if (which == O_GHBYNAME) {
+ char *name = str_get(ary->ary_array[sp+1]);
+
+ hent = gethostbyname(name);
+ }
+ else if (which == O_GHBYADDR) {
+ STR *addrstr = ary->ary_array[sp+1];
+ int addrtype = (int)str_gnum(ary->ary_array[sp+2]);
+ char *addr = str_get(addrstr);
+
+ hent = gethostbyaddr(addr,addrstr->str_cur,addrtype);
+ }
+ else
+#ifdef HAS_GETHOSTENT
+ hent = gethostent();
+#else
+ fatal("gethostent not implemented");
+#endif
+
+#ifdef HOST_NOT_FOUND
+ if (!hent)
+ statusvalue = (unsigned short)h_errno & 0xffff;
+#endif
+
+ if (gimme != G_ARRAY) {
+ astore(ary, ++sp, str = str_mortal(&str_undef));
+ if (hent) {
+ if (which == O_GHBYNAME) {
+#ifdef h_addr
+ str_nset(str, *hent->h_addr, hent->h_length);
+#else
+ str_nset(str, hent->h_addr, hent->h_length);
+#endif
+ }
+ else
+ str_set(str, hent->h_name);
+ }
+ return sp;
+ }
+
+ if (hent) {
+#ifndef lint
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, hent->h_name);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ for (elem = hent->h_aliases; *elem; elem++) {
+ str_cat(str, *elem);
+ if (elem[1])
+ str_ncat(str," ",1);
+ }
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_numset(str, (double)hent->h_addrtype);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ len = hent->h_length;
+ str_numset(str, (double)len);
+#ifdef h_addr
+ for (elem = hent->h_addr_list; *elem; elem++) {
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_nset(str, *elem, len);
+ }
+#else
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_nset(str, hent->h_addr, len);
+#endif /* h_addr */
+#else /* lint */
+ elem = Nullch;
+ elem = elem;
+ (void)astore(ary, ++sp, str_mortal(&str_no));
+#endif /* lint */
+ }
+
+ return sp;
+}
+
+int
+do_gnent(which,gimme,arglast)
+int which;
+int gimme;
+int *arglast;
+{
+ register ARRAY *ary = stack;
+ register int sp = arglast[0];
+ register char **elem;
+ register STR *str;
+ struct netent *getnetbyname();
+ struct netent *getnetbyaddr();
+ struct netent *getnetent();
+ struct netent *nent;
+
+ if (which == O_GNBYNAME) {
+ char *name = str_get(ary->ary_array[sp+1]);
+
+ nent = getnetbyname(name);
+ }
+ else if (which == O_GNBYADDR) {
+ unsigned long addr = U_L(str_gnum(ary->ary_array[sp+1]));
+ int addrtype = (int)str_gnum(ary->ary_array[sp+2]);
+
+ nent = getnetbyaddr((long)addr,addrtype);
+ }
+ else
+ nent = getnetent();
+
+ if (gimme != G_ARRAY) {
+ astore(ary, ++sp, str = str_mortal(&str_undef));
+ if (nent) {
+ if (which == O_GNBYNAME)
+ str_numset(str, (double)nent->n_net);
+ else
+ str_set(str, nent->n_name);
+ }
+ return sp;
+ }
+
+ if (nent) {
+#ifndef lint
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, nent->n_name);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ for (elem = nent->n_aliases; *elem; elem++) {
+ str_cat(str, *elem);
+ if (elem[1])
+ str_ncat(str," ",1);
+ }
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_numset(str, (double)nent->n_addrtype);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_numset(str, (double)nent->n_net);
+#else /* lint */
+ elem = Nullch;
+ elem = elem;
+ (void)astore(ary, ++sp, str_mortal(&str_no));
+#endif /* lint */
+ }
+
+ return sp;
+}
+
+int
+do_gpent(which,gimme,arglast)
+int which;
+int gimme;
+int *arglast;
+{
+ register ARRAY *ary = stack;
+ register int sp = arglast[0];
+ register char **elem;
+ register STR *str;
+ struct protoent *getprotobyname();
+ struct protoent *getprotobynumber();
+ struct protoent *getprotoent();
+ struct protoent *pent;
+
+ if (which == O_GPBYNAME) {
+ char *name = str_get(ary->ary_array[sp+1]);
+
+ pent = getprotobyname(name);
+ }
+ else if (which == O_GPBYNUMBER) {
+ int proto = (int)str_gnum(ary->ary_array[sp+1]);
+
+ pent = getprotobynumber(proto);
+ }
+ else
+ pent = getprotoent();
+
+ if (gimme != G_ARRAY) {
+ astore(ary, ++sp, str = str_mortal(&str_undef));
+ if (pent) {
+ if (which == O_GPBYNAME)
+ str_numset(str, (double)pent->p_proto);
+ else
+ str_set(str, pent->p_name);
+ }
+ return sp;
+ }
+
+ if (pent) {
+#ifndef lint
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, pent->p_name);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ for (elem = pent->p_aliases; *elem; elem++) {
+ str_cat(str, *elem);
+ if (elem[1])
+ str_ncat(str," ",1);
+ }
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_numset(str, (double)pent->p_proto);
+#else /* lint */
+ elem = Nullch;
+ elem = elem;
+ (void)astore(ary, ++sp, str_mortal(&str_no));
+#endif /* lint */
+ }
+
+ return sp;
+}
+
+int
+do_gsent(which,gimme,arglast)
+int which;
+int gimme;
+int *arglast;
+{
+ register ARRAY *ary = stack;
+ register int sp = arglast[0];
+ register char **elem;
+ register STR *str;
+ struct servent *getservbyname();
+ struct servent *getservbynumber();
+ struct servent *getservent();
+ struct servent *sent;
+
+ if (which == O_GSBYNAME) {
+ char *name = str_get(ary->ary_array[sp+1]);
+ char *proto = str_get(ary->ary_array[sp+2]);
+
+ if (proto && !*proto)
+ proto = Nullch;
+
+ sent = getservbyname(name,proto);
+ }
+ else if (which == O_GSBYPORT) {
+ int port = (int)str_gnum(ary->ary_array[sp+1]);
+ char *proto = str_get(ary->ary_array[sp+2]);
+
+ sent = getservbyport(port,proto);
+ }
+ else
+ sent = getservent();
+
+ if (gimme != G_ARRAY) {
+ astore(ary, ++sp, str = str_mortal(&str_undef));
+ if (sent) {
+ if (which == O_GSBYNAME) {
+#ifdef HAS_NTOHS
+ str_numset(str, (double)ntohs(sent->s_port));
+#else
+ str_numset(str, (double)(sent->s_port));
+#endif
+ }
+ else
+ str_set(str, sent->s_name);
+ }
+ return sp;
+ }
+
+ if (sent) {
+#ifndef lint
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, sent->s_name);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ for (elem = sent->s_aliases; *elem; elem++) {
+ str_cat(str, *elem);
+ if (elem[1])
+ str_ncat(str," ",1);
+ }
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+#ifdef HAS_NTOHS
+ str_numset(str, (double)ntohs(sent->s_port));
+#else
+ str_numset(str, (double)(sent->s_port));
+#endif
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, sent->s_proto);
+#else /* lint */
+ elem = Nullch;
+ elem = elem;
+ (void)astore(ary, ++sp, str_mortal(&str_no));
+#endif /* lint */
+ }
+
+ return sp;
+}
+
+#endif /* HAS_SOCKET */
+
+#ifdef HAS_SELECT
+int
+do_select(gimme,arglast)
+int gimme;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[0];
+ register int i;
+ register int j;
+ register char *s;
+ register STR *str;
+ double value;
+ int maxlen = 0;
+ int nfound;
+ struct timeval timebuf;
+ struct timeval *tbuf = &timebuf;
+ int growsize;
+#if BYTEORDER != 0x1234 && BYTEORDER != 0x12345678
+ int masksize;
+ int offset;
+ char *fd_sets[4];
+ int k;
+
+#if BYTEORDER & 0xf0000
+#define ORDERBYTE (0x88888888 - BYTEORDER)
+#else
+#define ORDERBYTE (0x4444 - BYTEORDER)
+#endif
+
+#endif
+
+ for (i = 1; i <= 3; i++) {
+ j = st[sp+i]->str_cur;
+ if (maxlen < j)
+ maxlen = j;
+ }
+
+#if BYTEORDER == 0x1234 || BYTEORDER == 0x12345678
+ growsize = maxlen; /* little endians can use vecs directly */
+#else
+#ifdef NFDBITS
+
+#ifndef NBBY
+#define NBBY 8
+#endif
+
+ masksize = NFDBITS / NBBY;
+#else
+ masksize = sizeof(long); /* documented int, everyone seems to use long */
+#endif
+ growsize = maxlen + (masksize - (maxlen % masksize));
+ Zero(&fd_sets[0], 4, char*);
+#endif
+
+ for (i = 1; i <= 3; i++) {
+ str = st[sp+i];
+ j = str->str_len;
+ if (j < growsize) {
+ if (str->str_pok) {
+ Str_Grow(str,growsize);
+ s = str_get(str) + j;
+ while (++j <= growsize) {
+ *s++ = '\0';
+ }
+ }
+ else if (str->str_ptr) {
+ Safefree(str->str_ptr);
+ str->str_ptr = Nullch;
+ }
+ }
+#if BYTEORDER != 0x1234 && BYTEORDER != 0x12345678
+ s = str->str_ptr;
+ if (s) {
+ New(403, fd_sets[i], growsize, char);
+ for (offset = 0; offset < growsize; offset += masksize) {
+ for (j = 0, k=ORDERBYTE; j < masksize; j++, (k >>= 4))
+ fd_sets[i][j+offset] = s[(k % masksize) + offset];
+ }
+ }
+#endif
+ }
+ str = st[sp+4];
+ if (str->str_nok || str->str_pok) {
+ value = str_gnum(str);
+ if (value < 0.0)
+ value = 0.0;
+ timebuf.tv_sec = (long)value;
+ value -= (double)timebuf.tv_sec;
+ timebuf.tv_usec = (long)(value * 1000000.0);
+ }
+ else
+ tbuf = Null(struct timeval*);
+
+#if BYTEORDER == 0x1234 || BYTEORDER == 0x12345678
+ nfound = select(
+ maxlen * 8,
+ st[sp+1]->str_ptr,
+ st[sp+2]->str_ptr,
+ st[sp+3]->str_ptr,
+ tbuf);
+#else
+ nfound = select(
+ maxlen * 8,
+ fd_sets[1],
+ fd_sets[2],
+ fd_sets[3],
+ tbuf);
+ for (i = 1; i <= 3; i++) {
+ if (fd_sets[i]) {
+ str = st[sp+i];
+ s = str->str_ptr;
+ for (offset = 0; offset < growsize; offset += masksize) {
+ for (j = 0, k=ORDERBYTE; j < masksize; j++, (k >>= 4))
+ s[(k % masksize) + offset] = fd_sets[i][j+offset];
+ }
+ Safefree(fd_sets[i]);
+ }
+ }
+#endif
+
+ st[++sp] = str_mortal(&str_no);
+ str_numset(st[sp], (double)nfound);
+ if (gimme == G_ARRAY && tbuf) {
+ value = (double)(timebuf.tv_sec) +
+ (double)(timebuf.tv_usec) / 1000000.0;
+ st[++sp] = str_mortal(&str_no);
+ str_numset(st[sp], value);
+ }
+ return sp;
+}
+#endif /* SELECT */
+
+#ifdef HAS_SOCKET
+int
+do_spair(stab1, stab2, arglast)
+STAB *stab1;
+STAB *stab2;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[2];
+ register STIO *stio1;
+ register STIO *stio2;
+ int domain, type, protocol, fd[2];
+
+ if (!stab1 || !stab2)
+ return FALSE;
+
+ stio1 = stab_io(stab1);
+ stio2 = stab_io(stab2);
+ if (!stio1)
+ stio1 = stab_io(stab1) = stio_new();
+ else if (stio1->ifp)
+ do_close(stab1,FALSE);
+ if (!stio2)
+ stio2 = stab_io(stab2) = stio_new();
+ else if (stio2->ifp)
+ do_close(stab2,FALSE);
+
+ domain = (int)str_gnum(st[++sp]);
+ type = (int)str_gnum(st[++sp]);
+ protocol = (int)str_gnum(st[++sp]);
+#ifdef TAINT
+ taintproper("Insecure dependency in socketpair");
+#endif
+#ifdef HAS_SOCKETPAIR
+ if (socketpair(domain,type,protocol,fd) < 0)
+ return FALSE;
+#else
+ fatal("Socketpair unimplemented");
+#endif
+ stio1->ifp = fdopen(fd[0], "r");
+ stio1->ofp = fdopen(fd[0], "w");
+ stio1->type = 's';
+ stio2->ifp = fdopen(fd[1], "r");
+ stio2->ofp = fdopen(fd[1], "w");
+ stio2->type = 's';
+ if (!stio1->ifp || !stio1->ofp || !stio2->ifp || !stio2->ofp) {
+ if (stio1->ifp) fclose(stio1->ifp);
+ if (stio1->ofp) fclose(stio1->ofp);
+ if (!stio1->ifp && !stio1->ofp) close(fd[0]);
+ if (stio2->ifp) fclose(stio2->ifp);
+ if (stio2->ofp) fclose(stio2->ofp);
+ if (!stio2->ifp && !stio2->ofp) close(fd[1]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#endif /* HAS_SOCKET */
+
+int
+do_gpwent(which,gimme,arglast)
+int which;
+int gimme;
+int *arglast;
+{
+#ifdef I_PWD
+ register ARRAY *ary = stack;
+ register int sp = arglast[0];
+ register STR *str;
+ struct passwd *getpwnam();
+ struct passwd *getpwuid();
+ struct passwd *getpwent();
+ struct passwd *pwent;
+
+ if (which == O_GPWNAM) {
+ char *name = str_get(ary->ary_array[sp+1]);
+
+ pwent = getpwnam(name);
+ }
+ else if (which == O_GPWUID) {
+ int uid = (int)str_gnum(ary->ary_array[sp+1]);
+
+ pwent = getpwuid(uid);
+ }
+ else
+ pwent = getpwent();
+
+ if (gimme != G_ARRAY) {
+ astore(ary, ++sp, str = str_mortal(&str_undef));
+ if (pwent) {
+ if (which == O_GPWNAM)
+ str_numset(str, (double)pwent->pw_uid);
+ else
+ str_set(str, pwent->pw_name);
+ }
+ return sp;
+ }
+
+ if (pwent) {
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, pwent->pw_name);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, pwent->pw_passwd);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_numset(str, (double)pwent->pw_uid);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_numset(str, (double)pwent->pw_gid);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+#ifdef PWCHANGE
+ str_numset(str, (double)pwent->pw_change);
+#else
+#ifdef PWQUOTA
+ str_numset(str, (double)pwent->pw_quota);
+#else
+#ifdef PWAGE
+ str_set(str, pwent->pw_age);
+#endif
+#endif
+#endif
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+#ifdef PWCLASS
+ str_set(str,pwent->pw_class);
+#else
+#ifdef PWCOMMENT
+ str_set(str, pwent->pw_comment);
+#endif
+#endif
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, pwent->pw_gecos);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, pwent->pw_dir);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, pwent->pw_shell);
+#ifdef PWEXPIRE
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_numset(str, (double)pwent->pw_expire);
+#endif
+ }
+
+ return sp;
+#else
+ fatal("password routines not implemented");
+#endif
+}
+
+int
+do_ggrent(which,gimme,arglast)
+int which;
+int gimme;
+int *arglast;
+{
+#ifdef I_GRP
+ register ARRAY *ary = stack;
+ register int sp = arglast[0];
+ register char **elem;
+ register STR *str;
+ struct group *getgrnam();
+ struct group *getgrgid();
+ struct group *getgrent();
+ struct group *grent;
+
+ if (which == O_GGRNAM) {
+ char *name = str_get(ary->ary_array[sp+1]);
+
+ grent = getgrnam(name);
+ }
+ else if (which == O_GGRGID) {
+ int gid = (int)str_gnum(ary->ary_array[sp+1]);
+
+ grent = getgrgid(gid);
+ }
+ else
+ grent = getgrent();
+
+ if (gimme != G_ARRAY) {
+ astore(ary, ++sp, str = str_mortal(&str_undef));
+ if (grent) {
+ if (which == O_GGRNAM)
+ str_numset(str, (double)grent->gr_gid);
+ else
+ str_set(str, grent->gr_name);
+ }
+ return sp;
+ }
+
+ if (grent) {
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, grent->gr_name);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_set(str, grent->gr_passwd);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ str_numset(str, (double)grent->gr_gid);
+ (void)astore(ary, ++sp, str = str_mortal(&str_no));
+ for (elem = grent->gr_mem; *elem; elem++) {
+ str_cat(str, *elem);
+ if (elem[1])
+ str_ncat(str," ",1);
+ }
+ }
+
+ return sp;
+#else
+ fatal("group routines not implemented");
+#endif
+}
+
+int
+do_dirop(optype,stab,gimme,arglast)
+int optype;
+STAB *stab;
+int gimme;
+int *arglast;
+{
+#if defined(DIRENT) && defined(HAS_READDIR)
+ register ARRAY *ary = stack;
+ register STR **st = ary->ary_array;
+ register int sp = arglast[1];
+ register STIO *stio;
+ long along;
+#ifndef apollo
+ struct DIRENT *readdir();
+#endif
+ register struct DIRENT *dp;
+
+ if (!stab)
+ goto nope;
+ if (!(stio = stab_io(stab)))
+ stio = stab_io(stab) = stio_new();
+ if (!stio->dirp && optype != O_OPEN_DIR)
+ goto nope;
+ st[sp] = &str_yes;
+ switch (optype) {
+ case O_OPEN_DIR:
+ if (stio->dirp)
+ closedir(stio->dirp);
+ if (!(stio->dirp = opendir(str_get(st[sp+1]))))
+ goto nope;
+ break;
+ case O_READDIR:
+ if (gimme == G_ARRAY) {
+ --sp;
+ /*SUPPRESS 560*/
+ while (dp = readdir(stio->dirp)) {
+#ifdef DIRNAMLEN
+ (void)astore(ary,++sp,
+ str_2mortal(str_make(dp->d_name,dp->d_namlen)));
+#else
+ (void)astore(ary,++sp,
+ str_2mortal(str_make(dp->d_name,0)));
+#endif
+ }
+ }
+ else {
+ if (!(dp = readdir(stio->dirp)))
+ goto nope;
+ st[sp] = str_mortal(&str_undef);
+#ifdef DIRNAMLEN
+ str_nset(st[sp], dp->d_name, dp->d_namlen);
+#else
+ str_set(st[sp], dp->d_name);
+#endif
+ }
+ break;
+#if defined(HAS_TELLDIR) || defined(telldir)
+ case O_TELLDIR: {
+#ifndef telldir
+ long telldir();
+#endif
+ st[sp] = str_mortal(&str_undef);
+ str_numset(st[sp], (double)telldir(stio->dirp));
+ break;
+ }
+#endif
+#if defined(HAS_SEEKDIR) || defined(seekdir)
+ case O_SEEKDIR:
+ st[sp] = str_mortal(&str_undef);
+ along = (long)str_gnum(st[sp+1]);
+ (void)seekdir(stio->dirp,along);
+ break;
+#endif
+#if defined(HAS_REWINDDIR) || defined(rewinddir)
+ case O_REWINDDIR:
+ st[sp] = str_mortal(&str_undef);
+ (void)rewinddir(stio->dirp);
+ break;
+#endif
+ case O_CLOSEDIR:
+ st[sp] = str_mortal(&str_undef);
+ (void)closedir(stio->dirp);
+ stio->dirp = 0;
+ break;
+ default:
+ goto phooey;
+ }
+ return sp;
+
+nope:
+ st[sp] = &str_undef;
+ if (!errno)
+ errno = EBADF;
+ return sp;
+
+#endif
+phooey:
+ fatal("Unimplemented directory operation");
+}
+
+int
+apply(type,arglast)
+int type;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[1];
+ register int items = arglast[2] - sp;
+ register int val;
+ register int val2;
+ register int tot = 0;
+ char *s;
+
+#ifdef TAINT
+ for (st += ++sp; items--; st++)
+ tainted |= (*st)->str_tainted;
+ st = stack->ary_array;
+ sp = arglast[1];
+ items = arglast[2] - sp;
+#endif
+ switch (type) {
+ case O_CHMOD:
+#ifdef TAINT
+ taintproper("Insecure dependency in chmod");
+#endif
+ if (--items > 0) {
+ tot = items;
+ val = (int)str_gnum(st[++sp]);
+ while (items--) {
+ if (chmod(str_get(st[++sp]),val))
+ tot--;
+ }
+ }
+ break;
+#ifdef HAS_CHOWN
+ case O_CHOWN:
+#ifdef TAINT
+ taintproper("Insecure dependency in chown");
+#endif
+ if (items > 2) {
+ items -= 2;
+ tot = items;
+ val = (int)str_gnum(st[++sp]);
+ val2 = (int)str_gnum(st[++sp]);
+ while (items--) {
+ if (chown(str_get(st[++sp]),val,val2))
+ tot--;
+ }
+ }
+ break;
+#endif
+#ifdef HAS_KILL
+ case O_KILL:
+#ifdef TAINT
+ taintproper("Insecure dependency in kill");
+#endif
+ if (--items > 0) {
+ tot = items;
+ s = str_get(st[++sp]);
+ if (isUPPER(*s)) {
+ if (*s == 'S' && s[1] == 'I' && s[2] == 'G')
+ s += 3;
+ if (!(val = whichsig(s)))
+ fatal("Unrecognized signal name \"%s\"",s);
+ }
+ else
+ val = (int)str_gnum(st[sp]);
+ if (val < 0) {
+ val = -val;
+ while (items--) {
+ int proc = (int)str_gnum(st[++sp]);
+#ifdef HAS_KILLPG
+ if (killpg(proc,val)) /* BSD */
+#else
+ if (kill(-proc,val)) /* SYSV */
+#endif
+ tot--;
+ }
+ }
+ else {
+ while (items--) {
+ if (kill((int)(str_gnum(st[++sp])),val))
+ tot--;
+ }
+ }
+ }
+ break;
+#endif
+ case O_UNLINK:
+#ifdef TAINT
+ taintproper("Insecure dependency in unlink");
+#endif
+ tot = items;
+ while (items--) {
+ s = str_get(st[++sp]);
+ if (euid || unsafe) {
+ if (UNLINK(s))
+ tot--;
+ }
+ else { /* don't let root wipe out directories without -U */
+#ifdef HAS_LSTAT
+ if (lstat(s,&statbuf) < 0 || S_ISDIR(statbuf.st_mode))
+#else
+ if (stat(s,&statbuf) < 0 || S_ISDIR(statbuf.st_mode))
+#endif
+ tot--;
+ else {
+ if (UNLINK(s))
+ tot--;
+ }
+ }
+ }
+ break;
+ case O_UTIME:
+#ifdef TAINT
+ taintproper("Insecure dependency in utime");
+#endif
+ if (items > 2) {
+#ifdef I_UTIME
+ struct utimbuf utbuf;
+#else
+ struct {
+ long actime;
+ long modtime;
+ } utbuf;
+#endif
+
+ Zero(&utbuf, sizeof utbuf, char);
+ utbuf.actime = (long)str_gnum(st[++sp]); /* time accessed */
+ utbuf.modtime = (long)str_gnum(st[++sp]); /* time modified */
+ items -= 2;
+#ifndef lint
+ tot = items;
+ while (items--) {
+ if (utime(str_get(st[++sp]),&utbuf))
+ tot--;
+ }
+#endif
+ }
+ else
+ items = 0;
+ break;
+ }
+ return tot;
+}
+
+/* Do the permissions allow some operation? Assumes statcache already set. */
+
+int
+cando(bit, effective, statbufp)
+int bit;
+int effective;
+register struct stat *statbufp;
+{
+#ifdef DOSISH
+ /* [Comments and code from Len Reed]
+ * MS-DOS "user" is similar to UNIX's "superuser," but can't write
+ * to write-protected files. The execute permission bit is set
+ * by the Miscrosoft C library stat() function for the following:
+ * .exe files
+ * .com files
+ * .bat files
+ * directories
+ * All files and directories are readable.
+ * Directories and special files, e.g. "CON", cannot be
+ * write-protected.
+ * [Comment by Tom Dinger -- a directory can have the write-protect
+ * bit set in the file system, but DOS permits changes to
+ * the directory anyway. In addition, all bets are off
+ * here for networked software, such as Novell and
+ * Sun's PC-NFS.]
+ */
+
+ /* Atari stat() does pretty much the same thing. we set x_bit_set_in_stat
+ * too so it will actually look into the files for magic numbers
+ */
+ return (bit & statbufp->st_mode) ? TRUE : FALSE;
+
+#else /* ! MSDOS */
+ if ((effective ? euid : uid) == 0) { /* root is special */
+ if (bit == S_IXUSR) {
+ if (statbufp->st_mode & 0111 || S_ISDIR(statbufp->st_mode))
+ return TRUE;
+ }
+ else
+ return TRUE; /* root reads and writes anything */
+ return FALSE;
+ }
+ if (statbufp->st_uid == (effective ? euid : uid) ) {
+ if (statbufp->st_mode & bit)
+ return TRUE; /* ok as "user" */
+ }
+ else if (ingroup((int)statbufp->st_gid,effective)) {
+ if (statbufp->st_mode & bit >> 3)
+ return TRUE; /* ok as "group" */
+ }
+ else if (statbufp->st_mode & bit >> 6)
+ return TRUE; /* ok as "other" */
+ return FALSE;
+#endif /* ! MSDOS */
+}
+
+int
+ingroup(testgid,effective)
+int testgid;
+int effective;
+{
+ if (testgid == (effective ? egid : gid))
+ return TRUE;
+#ifdef HAS_GETGROUPS
+#ifndef NGROUPS
+#define NGROUPS 32
+#endif
+ {
+ GROUPSTYPE gary[NGROUPS];
+ int anum;
+
+ anum = getgroups(NGROUPS,gary);
+ while (--anum >= 0)
+ if (gary[anum] == testgid)
+ return TRUE;
+ }
+#endif
+ return FALSE;
+}
+
+#if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM)
+
+int
+do_ipcget(optype, arglast)
+int optype;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[0];
+ key_t key;
+ int n, flags;
+
+ key = (key_t)str_gnum(st[++sp]);
+ n = (optype == O_MSGGET) ? 0 : (int)str_gnum(st[++sp]);
+ flags = (int)str_gnum(st[++sp]);
+ errno = 0;
+ switch (optype)
+ {
+#ifdef HAS_MSG
+ case O_MSGGET:
+ return msgget(key, flags);
+#endif
+#ifdef HAS_SEM
+ case O_SEMGET:
+ return semget(key, n, flags);
+#endif
+#ifdef HAS_SHM
+ case O_SHMGET:
+ return shmget(key, n, flags);
+#endif
+#if !defined(HAS_MSG) || !defined(HAS_SEM) || !defined(HAS_SHM)
+ default:
+ fatal("%s not implemented", opname[optype]);
+#endif
+ }
+ return -1; /* should never happen */
+}
+
+int
+do_ipcctl(optype, arglast)
+int optype;
+int *arglast;
+{
+ register STR **st = stack->ary_array;
+ register int sp = arglast[0];
+ STR *astr;
+ char *a;
+ int id, n, cmd, infosize, getinfo, ret;
+
+ id = (int)str_gnum(st[++sp]);
+ n = (optype == O_SEMCTL) ? (int)str_gnum(st[++sp]) : 0;
+ cmd = (int)str_gnum(st[++sp]);
+ astr = st[++sp];
+
+ infosize = 0;
+ getinfo = (cmd == IPC_STAT);
+
+ switch (optype)
+ {
+#ifdef HAS_MSG
+ case O_MSGCTL:
+ if (cmd == IPC_STAT || cmd == IPC_SET)
+ infosize = sizeof(struct msqid_ds);
+ break;
+#endif
+#ifdef HAS_SHM
+ case O_SHMCTL:
+ if (cmd == IPC_STAT || cmd == IPC_SET)
+ infosize = sizeof(struct shmid_ds);
+ break;
+#endif
+#ifdef HAS_SEM
+ case O_SEMCTL:
+ if (cmd == IPC_STAT || cmd == IPC_SET)
+ infosize = sizeof(struct semid_ds);
+ else if (cmd == GETALL || cmd == SETALL)
+ {
+ struct semid_ds semds;
+ if (semctl(id, 0, IPC_STAT, (union semun)&semds) == -1)
+ return -1;
+ getinfo = (cmd == GETALL);
+ infosize = semds.sem_nsems * sizeof(short);
+ /* "short" is technically wrong but much more portable
+ than guessing about u_?short(_t)? */
+ }
+ break;
+#endif
+#if !defined(HAS_MSG) || !defined(HAS_SEM) || !defined(HAS_SHM)
+ default:
+ fatal("%s not implemented", opname[optype]);
+#endif
+ }
+
+ if (infosize)
+ {
+ if (getinfo)
+ {
+ STR_GROW(astr, infosize+1);
+ a = str_get(astr);
+ }
+ else
+ {
+ a = str_get(astr);
+ if (astr->str_cur != infosize)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ int i = (int)str_gnum(astr);
+ a = (char *)i; /* ouch */
+ }
+ errno = 0;
+ switch (optype)
+ {
+#ifdef HAS_MSG
+ case O_MSGCTL:
+ ret = msgctl(id, cmd, (struct msqid_ds *)a);
+ break;
+#endif
+#ifdef HAS_SEM
+ case O_SEMCTL:
+ ret = semctl(id, n, cmd, (union semun)((int)a));
+ break;
+#endif
+#ifdef HAS_SHM
+ case O_SHMCTL:
+ ret = shmctl(id, cmd, (struct shmid_ds *)a);
+ break;
+#endif
+ }
+ if (getinfo && ret >= 0) {
+ astr->str_cur = infosize;
+ astr->str_ptr[infosize] = '\0';
+ }
+ return ret;
+}
+
+int
+do_msgsnd(arglast)
+int *arglast;
+{
+#ifdef HAS_MSG
+ register STR **st = stack->ary_array;
+ register int sp = arglast[0];
+ STR *mstr;
+ char *mbuf;
+ int id, msize, flags;
+
+ id = (int)str_gnum(st[++sp]);
+ mstr = st[++sp];
+ flags = (int)str_gnum(st[++sp]);
+ mbuf = str_get(mstr);
+ if ((msize = mstr->str_cur - sizeof(long)) < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ errno = 0;
+ return msgsnd(id, (struct msgbuf *)mbuf, msize, flags);
+#else
+ fatal("msgsnd not implemented");
+#endif
+}
+
+int
+do_msgrcv(arglast)
+int *arglast;
+{
+#ifdef HAS_MSG
+ register STR **st = stack->ary_array;
+ register int sp = arglast[0];
+ STR *mstr;
+ char *mbuf;
+ long mtype;
+ int id, msize, flags, ret;
+
+ id = (int)str_gnum(st[++sp]);
+ mstr = st[++sp];
+ msize = (int)str_gnum(st[++sp]);
+ mtype = (long)str_gnum(st[++sp]);
+ flags = (int)str_gnum(st[++sp]);
+ mbuf = str_get(mstr);
+ if (mstr->str_cur < sizeof(long)+msize+1) {
+ STR_GROW(mstr, sizeof(long)+msize+1);
+ mbuf = str_get(mstr);
+ }
+ errno = 0;
+ ret = msgrcv(id, (struct msgbuf *)mbuf, msize, mtype, flags);
+ if (ret >= 0) {
+ mstr->str_cur = sizeof(long)+ret;
+ mstr->str_ptr[sizeof(long)+ret] = '\0';
+ }
+ return ret;
+#else
+ fatal("msgrcv not implemented");
+#endif
+}
+
+int
+do_semop(arglast)
+int *arglast;
+{
+#ifdef HAS_SEM
+ register STR **st = stack->ary_array;
+ register int sp = arglast[0];
+ STR *opstr;
+ char *opbuf;
+ int id, opsize;
+
+ id = (int)str_gnum(st[++sp]);
+ opstr = st[++sp];
+ opbuf = str_get(opstr);
+ opsize = opstr->str_cur;
+ if (opsize < sizeof(struct sembuf)
+ || (opsize % sizeof(struct sembuf)) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ errno = 0;
+ return semop(id, (struct sembuf *)opbuf, opsize/sizeof(struct sembuf));
+#else
+ fatal("semop not implemented");
+#endif
+}
+
+int
+do_shmio(optype, arglast)
+int optype;
+int *arglast;
+{
+#ifdef HAS_SHM
+ register STR **st = stack->ary_array;
+ register int sp = arglast[0];
+ STR *mstr;
+ char *mbuf, *shm;
+ int id, mpos, msize;
+ struct shmid_ds shmds;
+#ifndef VOIDSHMAT
+ extern char *shmat();
+#endif
+
+ id = (int)str_gnum(st[++sp]);
+ mstr = st[++sp];
+ mpos = (int)str_gnum(st[++sp]);
+ msize = (int)str_gnum(st[++sp]);
+ errno = 0;
+ if (shmctl(id, IPC_STAT, &shmds) == -1)
+ return -1;
+ if (mpos < 0 || msize < 0 || mpos + msize > shmds.shm_segsz) {
+ errno = EFAULT; /* can't do as caller requested */
+ return -1;
+ }
+ shm = (char*)shmat(id, (char*)NULL, (optype == O_SHMREAD) ? SHM_RDONLY : 0);
+ if (shm == (char *)-1) /* I hate System V IPC, I really do */
+ return -1;
+ mbuf = str_get(mstr);
+ if (optype == O_SHMREAD) {
+ if (mstr->str_cur < msize) {
+ STR_GROW(mstr, msize+1);
+ mbuf = str_get(mstr);
+ }
+ Copy(shm + mpos, mbuf, msize, char);
+ mstr->str_cur = msize;
+ mstr->str_ptr[msize] = '\0';
+ }
+ else {
+ int n;
+
+ if ((n = mstr->str_cur) > msize)
+ n = msize;
+ Copy(mbuf, shm + mpos, n, char);
+ if (n < msize)
+ memzero(shm + mpos + n, msize - n);
+ }
+ return shmdt(shm);
+#else
+ fatal("shm I/O not implemented");
+#endif
+}
+
+#endif /* SYSV IPC */
diff --git a/gnu/usr.bin/perl/perl/toke.c b/gnu/usr.bin/perl/perl/toke.c
new file mode 100644
index 0000000..34603ce
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/toke.c
@@ -0,0 +1,2764 @@
+/* $RCSfile: toke.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:40 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: toke.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:40 nate
+ * PERL!
+ *
+ * Revision 4.0.1.9 1993/02/05 19:48:43 lwall
+ * patch36: now detects ambiguous use of filetest operators as well as unary
+ * patch36: fixed ambiguity on - within tr///
+ *
+ * Revision 4.0.1.8 92/06/23 12:33:45 lwall
+ * patch35: bad interaction between backslash and hyphen in tr///
+ *
+ * Revision 4.0.1.7 92/06/11 21:16:30 lwall
+ * patch34: expectterm incorrectly set to indicate start of program or block
+ *
+ * Revision 4.0.1.6 92/06/08 16:03:49 lwall
+ * patch20: an EXPR may now start with a bareword
+ * patch20: print $fh EXPR can now expect term rather than operator in EXPR
+ * patch20: added ... as variant on ..
+ * patch20: new warning on spurious backslash
+ * patch20: new warning on missing $ for foreach variable
+ * patch20: "foo"x1024 now legal without space after x
+ * patch20: new warning on print accidentally used as function
+ * patch20: tr/stuff// wasn't working right
+ * patch20: 2. now eats the dot
+ * patch20: <@ARGV> now notices @ARGV
+ * patch20: tr/// now lets you say \-
+ *
+ * Revision 4.0.1.5 91/11/11 16:45:51 lwall
+ * patch19: default arg for shift was wrong after first subroutine definition
+ *
+ * Revision 4.0.1.4 91/11/05 19:02:48 lwall
+ * patch11: \x and \c were subject to double interpretation in regexps
+ * patch11: prepared for ctype implementations that don't define isascii()
+ * patch11: nested list operators could miscount parens
+ * patch11: once-thru blocks didn't display right in the debugger
+ * patch11: sort eval "whatever" didn't work
+ * patch11: underscore is now allowed within literal octal and hex numbers
+ *
+ * Revision 4.0.1.3 91/06/10 01:32:26 lwall
+ * patch10: m'$foo' now treats string as single quoted
+ * patch10: certain pattern optimizations were botched
+ *
+ * Revision 4.0.1.2 91/06/07 12:05:56 lwall
+ * patch4: new copyright notice
+ * patch4: debugger lost track of lines in eval
+ * patch4: //o and s///o now optimize themselves fully at runtime
+ * patch4: added global modifier for pattern matches
+ *
+ * Revision 4.0.1.1 91/04/12 09:18:18 lwall
+ * patch1: perl -de "print" wouldn't stop at the first statement
+ *
+ * Revision 4.0 91/03/20 01:42:14 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "perly.h"
+
+static void set_csh();
+
+#ifdef I_FCNTL
+#include <fcntl.h>
+#endif
+#ifdef I_SYS_FILE
+#include <sys/file.h>
+#endif
+
+#ifdef f_next
+#undef f_next
+#endif
+
+/* which backslash sequences to keep in m// or s// */
+
+static char *patleave = "\\.^$@dDwWsSbB+*?|()-nrtfeaxc0123456789[{]}";
+
+char *reparse; /* if non-null, scanident found ${foo[$bar]} */
+
+void checkcomma();
+
+#ifdef CLINE
+#undef CLINE
+#endif
+#define CLINE (cmdline = (curcmd->c_line < cmdline ? curcmd->c_line : cmdline))
+
+#ifdef atarist
+#define PERL_META(c) ((c) | 128)
+#else
+#define META(c) ((c) | 128)
+#endif
+
+#define RETURN(retval) return (bufptr = s,(int)retval)
+#define OPERATOR(retval) return (expectterm = TRUE,bufptr = s,(int)retval)
+#define TERM(retval) return (CLINE, expectterm = FALSE,bufptr = s,(int)retval)
+#define LOOPX(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)LOOPEX)
+#define FTST(f) return(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)FILETEST)
+#define FUN0(f) return(yylval.ival = f,expectterm = FALSE,bufptr = s,(int)FUNC0)
+#define FUN1(f) return(yylval.ival = f,expectterm = FALSE,bufptr = s,(int)FUNC1)
+#define FUN2(f) return(yylval.ival = f,expectterm = FALSE,bufptr = s,(int)FUNC2)
+#define FUN2x(f) return(yylval.ival = f,expectterm = FALSE,bufptr = s,(int)FUNC2x)
+#define FUN3(f) return(yylval.ival = f,expectterm = FALSE,bufptr = s,(int)FUNC3)
+#define FUN4(f) return(yylval.ival = f,expectterm = FALSE,bufptr = s,(int)FUNC4)
+#define FUN5(f) return(yylval.ival = f,expectterm = FALSE,bufptr = s,(int)FUNC5)
+#define FL(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)FLIST)
+#define FL2(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)FLIST2)
+#define HFUN(f) return(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)HSHFUN)
+#define HFUN3(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)HSHFUN3)
+#define LFUN(f) return(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)LVALFUN)
+#define AOP(f) return(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)ADDOP)
+#define MOP(f) return(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)MULOP)
+#define EOP(f) return(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)EQOP)
+#define ROP(f) return(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)RELOP)
+#define FOP(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)FILOP)
+#define FOP2(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)FILOP2)
+#define FOP3(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)FILOP3)
+#define FOP4(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)FILOP4)
+#define FOP22(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)FILOP22)
+#define FOP25(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)FILOP25)
+
+static char *last_uni;
+
+/* This bit of chicanery makes a unary function followed by
+ * a parenthesis into a function with one argument, highest precedence.
+ */
+#define UNI(f) return(yylval.ival = f, \
+ expectterm = TRUE, \
+ bufptr = s, \
+ last_uni = oldbufptr, \
+ (*s == '(' || (s = skipspace(s), *s == '(') ? (int)FUNC1 : (int)UNIOP) )
+
+/* This does similarly for list operators, merely by pretending that the
+ * paren came before the listop rather than after.
+ */
+#ifdef atarist
+#define LOP(f) return(CLINE, *s == '(' || (s = skipspace(s), *s == '(') ? \
+ (*s = (char) PERL_META('('), bufptr = oldbufptr, '(') : \
+ (yylval.ival=f,expectterm = TRUE,bufptr = s,(int)LISTOP))
+#else
+#define LOP(f) return(CLINE, *s == '(' || (s = skipspace(s), *s == '(') ? \
+ (*s = (char) META('('), bufptr = oldbufptr, '(') : \
+ (yylval.ival=f,expectterm = TRUE,bufptr = s,(int)LISTOP))
+#endif
+/* grandfather return to old style */
+#define OLDLOP(f) return(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)LISTOP)
+
+char *
+skipspace(s)
+register char *s;
+{
+ while (s < bufend && isSPACE(*s))
+ s++;
+ return s;
+}
+
+void
+check_uni() {
+ char *s;
+ char ch;
+
+ if (oldoldbufptr != last_uni)
+ return;
+ while (isSPACE(*last_uni))
+ last_uni++;
+ for (s = last_uni; isALNUM(*s) || *s == '-'; s++) ;
+ ch = *s;
+ *s = '\0';
+ warn("Warning: Use of \"%s\" without parens is ambiguous", last_uni);
+ *s = ch;
+}
+
+#ifdef CRIPPLED_CC
+
+#undef UNI
+#undef LOP
+#define UNI(f) return uni(f,s)
+#define LOP(f) return lop(f,s)
+
+int
+uni(f,s)
+int f;
+char *s;
+{
+ yylval.ival = f;
+ expectterm = TRUE;
+ bufptr = s;
+ last_uni = oldbufptr;
+ if (*s == '(')
+ return FUNC1;
+ s = skipspace(s);
+ if (*s == '(')
+ return FUNC1;
+ else
+ return UNIOP;
+}
+
+int
+lop(f,s)
+int f;
+char *s;
+{
+ CLINE;
+ if (*s != '(')
+ s = skipspace(s);
+ if (*s == '(') {
+#ifdef atarist
+ *s = PERL_META('(');
+#else
+ *s = META('(');
+#endif
+ bufptr = oldbufptr;
+ return '(';
+ }
+ else {
+ yylval.ival=f;
+ expectterm = TRUE;
+ bufptr = s;
+ return LISTOP;
+ }
+}
+
+#endif /* CRIPPLED_CC */
+
+int
+yylex()
+{
+ register char *s = bufptr;
+ register char *d;
+ register int tmp;
+ static bool in_format = FALSE;
+ static bool firstline = TRUE;
+ extern int yychar; /* last token */
+
+ oldoldbufptr = oldbufptr;
+ oldbufptr = s;
+
+ retry:
+#ifdef YYDEBUG
+ if (debug & 1)
+ if (index(s,'\n'))
+ fprintf(stderr,"Tokener at %s",s);
+ else
+ fprintf(stderr,"Tokener at %s\n",s);
+#endif
+#ifdef BADSWITCH
+ if (*s & 128) {
+ if ((*s & 127) == '(') {
+ *s++ = '(';
+ oldbufptr = s;
+ }
+ else if ((*s & 127) == '}') {
+ *s++ = '}';
+ RETURN('}');
+ }
+ else
+ warn("Unrecognized character \\%03o ignored", *s++ & 255);
+ goto retry;
+ }
+#endif
+ switch (*s) {
+ default:
+ if ((*s & 127) == '(') {
+ *s++ = '(';
+ oldbufptr = s;
+ }
+ else if ((*s & 127) == '}') {
+ *s++ = '}';
+ RETURN('}');
+ }
+ else
+ warn("Unrecognized character \\%03o ignored", *s++ & 255);
+ goto retry;
+ case 4:
+ case 26:
+ goto fake_eof; /* emulate EOF on ^D or ^Z */
+ case 0:
+ if (!rsfp)
+ RETURN(0);
+ if (s++ < bufend)
+ goto retry; /* ignore stray nulls */
+ last_uni = 0;
+ if (firstline) {
+ firstline = FALSE;
+ if (minus_n || minus_p || perldb) {
+ str_set(linestr,"");
+ if (perldb) {
+ char *getenv();
+ char *pdb = getenv("PERLDB");
+
+ str_cat(linestr, pdb ? pdb : "require 'perldb.pl'");
+ str_cat(linestr, ";");
+ }
+ if (minus_n || minus_p) {
+ str_cat(linestr,"line: while (<>) {");
+ if (minus_l)
+ str_cat(linestr,"chop;");
+ if (minus_a)
+ str_cat(linestr,"@F=split(' ');");
+ }
+ oldoldbufptr = oldbufptr = s = str_get(linestr);
+ bufend = linestr->str_ptr + linestr->str_cur;
+ goto retry;
+ }
+ }
+ if (in_format) {
+ bufptr = bufend;
+ yylval.formval = load_format();
+ in_format = FALSE;
+ oldoldbufptr = oldbufptr = s = str_get(linestr) + 1;
+ bufend = linestr->str_ptr + linestr->str_cur;
+ OPERATOR(FORMLIST);
+ }
+ curcmd->c_line++;
+#ifdef CRYPTSCRIPT
+ cryptswitch();
+#endif /* CRYPTSCRIPT */
+ do {
+ if ((s = str_gets(linestr, rsfp, 0)) == Nullch) {
+ fake_eof:
+ if (rsfp) {
+ if (preprocess)
+ (void)mypclose(rsfp);
+ else if ((FILE*)rsfp == stdin)
+ clearerr(stdin);
+ else
+ (void)fclose(rsfp);
+ rsfp = Nullfp;
+ }
+ if (minus_n || minus_p) {
+ str_set(linestr,minus_p ? ";}continue{print" : "");
+ str_cat(linestr,";}");
+ oldoldbufptr = oldbufptr = s = str_get(linestr);
+ bufend = linestr->str_ptr + linestr->str_cur;
+ minus_n = minus_p = 0;
+ goto retry;
+ }
+ oldoldbufptr = oldbufptr = s = str_get(linestr);
+ str_set(linestr,"");
+ RETURN(';'); /* not infinite loop because rsfp is NULL now */
+ }
+ if (doextract && *linestr->str_ptr == '#')
+ doextract = FALSE;
+ } while (doextract);
+ oldoldbufptr = oldbufptr = bufptr = s;
+ if (perldb) {
+ STR *str = Str_new(85,0);
+
+ str_sset(str,linestr);
+ astore(stab_xarray(curcmd->c_filestab),(int)curcmd->c_line,str);
+ }
+#ifdef DEBUG
+ if (firstline) {
+ char *showinput();
+ s = showinput();
+ }
+#endif
+ bufend = linestr->str_ptr + linestr->str_cur;
+ if (curcmd->c_line == 1) {
+ if (*s == '#' && s[1] == '!') {
+ if (!in_eval && !instr(s,"perl") && instr(origargv[0],"perl")) {
+ char **newargv;
+ char *cmd;
+
+ s += 2;
+ if (*s == ' ')
+ s++;
+ cmd = s;
+ while (s < bufend && !isSPACE(*s))
+ s++;
+ *s++ = '\0';
+ while (s < bufend && isSPACE(*s))
+ s++;
+ if (s < bufend) {
+ Newz(899,newargv,origargc+3,char*);
+ newargv[1] = s;
+ while (s < bufend && !isSPACE(*s))
+ s++;
+ *s = '\0';
+ Copy(origargv+1, newargv+2, origargc+1, char*);
+ }
+ else
+ newargv = origargv;
+ newargv[0] = cmd;
+ execv(cmd,newargv);
+ fatal("Can't exec %s", cmd);
+ }
+ }
+ else {
+ while (s < bufend && isSPACE(*s))
+ s++;
+ if (*s == ':') /* for csh's that have to exec sh scripts */
+ s++;
+ }
+ }
+ goto retry;
+ case ' ': case '\t': case '\f': case '\r': case 013:
+ s++;
+ goto retry;
+ case '#':
+ if (preprocess && s == str_get(linestr) &&
+ s[1] == ' ' && (isDIGIT(s[2]) || strnEQ(s+2,"line ",5)) ) {
+ while (*s && !isDIGIT(*s))
+ s++;
+ curcmd->c_line = atoi(s)-1;
+ while (isDIGIT(*s))
+ s++;
+ d = bufend;
+ while (s < d && isSPACE(*s)) s++;
+ s[strlen(s)-1] = '\0'; /* wipe out newline */
+ if (*s == '"') {
+ s++;
+ s[strlen(s)-1] = '\0'; /* wipe out trailing quote */
+ }
+ if (*s)
+ curcmd->c_filestab = fstab(s);
+ else
+ curcmd->c_filestab = fstab(origfilename);
+ oldoldbufptr = oldbufptr = s = str_get(linestr);
+ }
+ /* FALL THROUGH */
+ case '\n':
+ if (in_eval && !rsfp) {
+ d = bufend;
+ while (s < d && *s != '\n')
+ s++;
+ if (s < d)
+ s++;
+ if (in_format) {
+ bufptr = s;
+ yylval.formval = load_format();
+ in_format = FALSE;
+ oldoldbufptr = oldbufptr = s = bufptr + 1;
+ TERM(FORMLIST);
+ }
+ curcmd->c_line++;
+ }
+ else {
+ *s = '\0';
+ bufend = s;
+ }
+ goto retry;
+ case '-':
+ if (s[1] && isALPHA(s[1]) && !isALPHA(s[2])) {
+ s++;
+ last_uni = oldbufptr;
+ switch (*s++) {
+ case 'r': FTST(O_FTEREAD);
+ case 'w': FTST(O_FTEWRITE);
+ case 'x': FTST(O_FTEEXEC);
+ case 'o': FTST(O_FTEOWNED);
+ case 'R': FTST(O_FTRREAD);
+ case 'W': FTST(O_FTRWRITE);
+ case 'X': FTST(O_FTREXEC);
+ case 'O': FTST(O_FTROWNED);
+ case 'e': FTST(O_FTIS);
+ case 'z': FTST(O_FTZERO);
+ case 's': FTST(O_FTSIZE);
+ case 'f': FTST(O_FTFILE);
+ case 'd': FTST(O_FTDIR);
+ case 'l': FTST(O_FTLINK);
+ case 'p': FTST(O_FTPIPE);
+ case 'S': FTST(O_FTSOCK);
+ case 'u': FTST(O_FTSUID);
+ case 'g': FTST(O_FTSGID);
+ case 'k': FTST(O_FTSVTX);
+ case 'b': FTST(O_FTBLK);
+ case 'c': FTST(O_FTCHR);
+ case 't': FTST(O_FTTTY);
+ case 'T': FTST(O_FTTEXT);
+ case 'B': FTST(O_FTBINARY);
+ case 'M': stabent("\024",TRUE); FTST(O_FTMTIME);
+ case 'A': stabent("\024",TRUE); FTST(O_FTATIME);
+ case 'C': stabent("\024",TRUE); FTST(O_FTCTIME);
+ default:
+ s -= 2;
+ break;
+ }
+ }
+ tmp = *s++;
+ if (*s == tmp) {
+ s++;
+ RETURN(DEC);
+ }
+ if (expectterm) {
+ if (isSPACE(*s) || !isSPACE(*bufptr))
+ check_uni();
+ OPERATOR('-');
+ }
+ else
+ AOP(O_SUBTRACT);
+ case '+':
+ tmp = *s++;
+ if (*s == tmp) {
+ s++;
+ RETURN(INC);
+ }
+ if (expectterm) {
+ if (isSPACE(*s) || !isSPACE(*bufptr))
+ check_uni();
+ OPERATOR('+');
+ }
+ else
+ AOP(O_ADD);
+
+ case '*':
+ if (expectterm) {
+ check_uni();
+ s = scanident(s,bufend,tokenbuf);
+ yylval.stabval = stabent(tokenbuf,TRUE);
+ TERM(STAR);
+ }
+ tmp = *s++;
+ if (*s == tmp) {
+ s++;
+ OPERATOR(POW);
+ }
+ MOP(O_MULTIPLY);
+ case '%':
+ if (expectterm) {
+ if (!isALPHA(s[1]))
+ check_uni();
+ s = scanident(s,bufend,tokenbuf);
+ yylval.stabval = hadd(stabent(tokenbuf,TRUE));
+ TERM(HSH);
+ }
+ s++;
+ MOP(O_MODULO);
+
+ case '^':
+ case '~':
+ case '(':
+ case ',':
+ case ':':
+ case '[':
+ tmp = *s++;
+ OPERATOR(tmp);
+ case '{':
+ tmp = *s++;
+ yylval.ival = curcmd->c_line;
+ if (isSPACE(*s) || *s == '#')
+ cmdline = NOLINE; /* invalidate current command line number */
+ expectterm = 2;
+ RETURN(tmp);
+ case ';':
+ if (curcmd->c_line < cmdline)
+ cmdline = curcmd->c_line;
+ tmp = *s++;
+ OPERATOR(tmp);
+ case ')':
+ case ']':
+ tmp = *s++;
+ TERM(tmp);
+ case '}':
+ *s |= 128;
+ RETURN(';');
+ case '&':
+ s++;
+ tmp = *s++;
+ if (tmp == '&')
+ OPERATOR(ANDAND);
+ s--;
+ if (expectterm) {
+ d = bufend;
+ while (s < d && isSPACE(*s))
+ s++;
+ if (isALPHA(*s) || *s == '_' || *s == '\'')
+ *(--s) = '\\'; /* force next ident to WORD */
+ else
+ check_uni();
+ OPERATOR(AMPER);
+ }
+ OPERATOR('&');
+ case '|':
+ s++;
+ tmp = *s++;
+ if (tmp == '|')
+ OPERATOR(OROR);
+ s--;
+ OPERATOR('|');
+ case '=':
+ s++;
+ tmp = *s++;
+ if (tmp == '=')
+ EOP(O_EQ);
+ if (tmp == '~')
+ OPERATOR(MATCH);
+ s--;
+ OPERATOR('=');
+ case '!':
+ s++;
+ tmp = *s++;
+ if (tmp == '=')
+ EOP(O_NE);
+ if (tmp == '~')
+ OPERATOR(NMATCH);
+ s--;
+ OPERATOR('!');
+ case '<':
+ if (expectterm) {
+ if (s[1] != '<' && !index(s,'>'))
+ check_uni();
+ s = scanstr(s, SCAN_DEF);
+ TERM(RSTRING);
+ }
+ s++;
+ tmp = *s++;
+ if (tmp == '<')
+ OPERATOR(LS);
+ if (tmp == '=') {
+ tmp = *s++;
+ if (tmp == '>')
+ EOP(O_NCMP);
+ s--;
+ ROP(O_LE);
+ }
+ s--;
+ ROP(O_LT);
+ case '>':
+ s++;
+ tmp = *s++;
+ if (tmp == '>')
+ OPERATOR(RS);
+ if (tmp == '=')
+ ROP(O_GE);
+ s--;
+ ROP(O_GT);
+
+#define SNARFWORD \
+ d = tokenbuf; \
+ while (isALNUM(*s) || *s == '\'') \
+ *d++ = *s++; \
+ while (d[-1] == '\'') \
+ d--,s--; \
+ *d = '\0'; \
+ d = tokenbuf;
+
+ case '$':
+ if (s[1] == '#' && (isALPHA(s[2]) || s[2] == '_')) {
+ s++;
+ s = scanident(s,bufend,tokenbuf);
+ yylval.stabval = aadd(stabent(tokenbuf,TRUE));
+ TERM(ARYLEN);
+ }
+ d = s;
+ s = scanident(s,bufend,tokenbuf);
+ if (reparse) { /* turn ${foo[bar]} into ($foo[bar]) */
+ do_reparse:
+ s[-1] = ')';
+ s = d;
+ s[1] = s[0];
+ s[0] = '(';
+ goto retry;
+ }
+ yylval.stabval = stabent(tokenbuf,TRUE);
+ expectterm = FALSE;
+ if (isSPACE(*s) && oldoldbufptr && oldoldbufptr < bufptr) {
+ s++;
+ while (isSPACE(*oldoldbufptr))
+ oldoldbufptr++;
+ if (*oldoldbufptr == 'p' && strnEQ(oldoldbufptr,"print",5)) {
+ if (index("&*<%", *s) && isALPHA(s[1]))
+ expectterm = TRUE; /* e.g. print $fh &sub */
+ else if (*s == '.' && isDIGIT(s[1]))
+ expectterm = TRUE; /* e.g. print $fh .3 */
+ else if (index("/?-+", *s) && !isSPACE(s[1]))
+ expectterm = TRUE; /* e.g. print $fh -1 */
+ }
+ }
+ RETURN(REG);
+
+ case '@':
+ d = s;
+ s = scanident(s,bufend,tokenbuf);
+ if (reparse)
+ goto do_reparse;
+ yylval.stabval = aadd(stabent(tokenbuf,TRUE));
+ TERM(ARY);
+
+ case '/': /* may either be division or pattern */
+ case '?': /* may either be conditional or pattern */
+ if (expectterm) {
+ check_uni();
+ s = scanpat(s);
+ TERM(PATTERN);
+ }
+ tmp = *s++;
+ if (tmp == '/')
+ MOP(O_DIVIDE);
+ OPERATOR(tmp);
+
+ case '.':
+ if (!expectterm || !isDIGIT(s[1])) {
+ tmp = *s++;
+ if (*s == tmp) {
+ s++;
+ if (*s == tmp) {
+ s++;
+ yylval.ival = 0;
+ }
+ else
+ yylval.ival = AF_COMMON;
+ OPERATOR(DOTDOT);
+ }
+ if (expectterm)
+ check_uni();
+ AOP(O_CONCAT);
+ }
+ /* FALL THROUGH */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '\'': case '"': case '`':
+ s = scanstr(s, SCAN_DEF);
+ TERM(RSTRING);
+
+ case '\\': /* some magic to force next word to be a WORD */
+ s++; /* used by do and sub to force a separate namespace */
+ if (!isALPHA(*s) && *s != '_' && *s != '\'') {
+ warn("Spurious backslash ignored");
+ goto retry;
+ }
+ /* FALL THROUGH */
+ case '_':
+ SNARFWORD;
+ if (d[1] == '_') {
+ if (strEQ(d,"__LINE__") || strEQ(d,"__FILE__")) {
+ ARG *arg = op_new(1);
+
+ yylval.arg = arg;
+ arg->arg_type = O_ITEM;
+ if (d[2] == 'L')
+ (void)sprintf(tokenbuf,"%ld",(long)curcmd->c_line);
+ else
+ strcpy(tokenbuf, stab_val(curcmd->c_filestab)->str_ptr);
+ arg[1].arg_type = A_SINGLE;
+ arg[1].arg_ptr.arg_str = str_make(tokenbuf,strlen(tokenbuf));
+ TERM(RSTRING);
+ }
+ else if (strEQ(d,"__END__")) {
+ STAB *stab;
+ int fd;
+
+ /*SUPPRESS 560*/
+ if (!in_eval && (stab = stabent("DATA",FALSE))) {
+ stab->str_pok |= SP_MULTI;
+ if (!stab_io(stab))
+ stab_io(stab) = stio_new();
+ stab_io(stab)->ifp = rsfp;
+#if defined(HAS_FCNTL) && defined(F_SETFD)
+ fd = fileno(rsfp);
+ fcntl(fd,F_SETFD,fd >= 3);
+#endif
+ if (preprocess)
+ stab_io(stab)->type = '|';
+ else if ((FILE*)rsfp == stdin)
+ stab_io(stab)->type = '-';
+ else
+ stab_io(stab)->type = '<';
+ rsfp = Nullfp;
+ }
+ goto fake_eof;
+ }
+ }
+ break;
+ case 'a': case 'A':
+ SNARFWORD;
+ if (strEQ(d,"alarm"))
+ UNI(O_ALARM);
+ if (strEQ(d,"accept"))
+ FOP22(O_ACCEPT);
+ if (strEQ(d,"atan2"))
+ FUN2(O_ATAN2);
+ break;
+ case 'b': case 'B':
+ SNARFWORD;
+ if (strEQ(d,"bind"))
+ FOP2(O_BIND);
+ if (strEQ(d,"binmode"))
+ FOP(O_BINMODE);
+ break;
+ case 'c': case 'C':
+ SNARFWORD;
+ if (strEQ(d,"chop"))
+ LFUN(O_CHOP);
+ if (strEQ(d,"continue"))
+ OPERATOR(CONTINUE);
+ if (strEQ(d,"chdir")) {
+ (void)stabent("ENV",TRUE); /* may use HOME */
+ UNI(O_CHDIR);
+ }
+ if (strEQ(d,"close"))
+ FOP(O_CLOSE);
+ if (strEQ(d,"closedir"))
+ FOP(O_CLOSEDIR);
+ if (strEQ(d,"cmp"))
+ EOP(O_SCMP);
+ if (strEQ(d,"caller"))
+ UNI(O_CALLER);
+ if (strEQ(d,"crypt")) {
+#ifdef FCRYPT
+ static int cryptseen = 0;
+
+ if (!cryptseen++)
+ init_des();
+#endif
+ FUN2(O_CRYPT);
+ }
+ if (strEQ(d,"chmod"))
+ LOP(O_CHMOD);
+ if (strEQ(d,"chown"))
+ LOP(O_CHOWN);
+ if (strEQ(d,"connect"))
+ FOP2(O_CONNECT);
+ if (strEQ(d,"cos"))
+ UNI(O_COS);
+ if (strEQ(d,"chroot"))
+ UNI(O_CHROOT);
+ break;
+ case 'd': case 'D':
+ SNARFWORD;
+ if (strEQ(d,"do")) {
+ d = bufend;
+ while (s < d && isSPACE(*s))
+ s++;
+ if (isALPHA(*s) || *s == '_')
+ *(--s) = '\\'; /* force next ident to WORD */
+ OPERATOR(DO);
+ }
+ if (strEQ(d,"die"))
+ LOP(O_DIE);
+ if (strEQ(d,"defined"))
+ LFUN(O_DEFINED);
+ if (strEQ(d,"delete"))
+ OPERATOR(DELETE);
+ if (strEQ(d,"dbmopen"))
+ HFUN3(O_DBMOPEN);
+ if (strEQ(d,"dbmclose"))
+ HFUN(O_DBMCLOSE);
+ if (strEQ(d,"dump"))
+ LOOPX(O_DUMP);
+ break;
+ case 'e': case 'E':
+ SNARFWORD;
+ if (strEQ(d,"else"))
+ OPERATOR(ELSE);
+ if (strEQ(d,"elsif")) {
+ yylval.ival = curcmd->c_line;
+ OPERATOR(ELSIF);
+ }
+ if (strEQ(d,"eq") || strEQ(d,"EQ"))
+ EOP(O_SEQ);
+ if (strEQ(d,"exit"))
+ UNI(O_EXIT);
+ if (strEQ(d,"eval")) {
+ allstabs = TRUE; /* must initialize everything since */
+ UNI(O_EVAL); /* we don't know what will be used */
+ }
+ if (strEQ(d,"eof"))
+ FOP(O_EOF);
+ if (strEQ(d,"exp"))
+ UNI(O_EXP);
+ if (strEQ(d,"each"))
+ HFUN(O_EACH);
+ if (strEQ(d,"exec")) {
+ set_csh();
+ LOP(O_EXEC_OP);
+ }
+ if (strEQ(d,"endhostent"))
+ FUN0(O_EHOSTENT);
+ if (strEQ(d,"endnetent"))
+ FUN0(O_ENETENT);
+ if (strEQ(d,"endservent"))
+ FUN0(O_ESERVENT);
+ if (strEQ(d,"endprotoent"))
+ FUN0(O_EPROTOENT);
+ if (strEQ(d,"endpwent"))
+ FUN0(O_EPWENT);
+ if (strEQ(d,"endgrent"))
+ FUN0(O_EGRENT);
+ break;
+ case 'f': case 'F':
+ SNARFWORD;
+ if (strEQ(d,"for") || strEQ(d,"foreach")) {
+ yylval.ival = curcmd->c_line;
+ while (s < bufend && isSPACE(*s))
+ s++;
+ if (isALPHA(*s))
+ fatal("Missing $ on loop variable");
+ OPERATOR(FOR);
+ }
+ if (strEQ(d,"format")) {
+ d = bufend;
+ while (s < d && isSPACE(*s))
+ s++;
+ if (isALPHA(*s) || *s == '_')
+ *(--s) = '\\'; /* force next ident to WORD */
+ in_format = TRUE;
+ allstabs = TRUE; /* must initialize everything since */
+ OPERATOR(FORMAT); /* we don't know what will be used */
+ }
+ if (strEQ(d,"fork"))
+ FUN0(O_FORK);
+ if (strEQ(d,"fcntl"))
+ FOP3(O_FCNTL);
+ if (strEQ(d,"fileno"))
+ FOP(O_FILENO);
+ if (strEQ(d,"flock"))
+ FOP2(O_FLOCK);
+ break;
+ case 'g': case 'G':
+ SNARFWORD;
+ if (strEQ(d,"gt") || strEQ(d,"GT"))
+ ROP(O_SGT);
+ if (strEQ(d,"ge") || strEQ(d,"GE"))
+ ROP(O_SGE);
+ if (strEQ(d,"grep"))
+ FL2(O_GREP);
+ if (strEQ(d,"goto"))
+ LOOPX(O_GOTO);
+ if (strEQ(d,"gmtime"))
+ UNI(O_GMTIME);
+ if (strEQ(d,"getc"))
+ FOP(O_GETC);
+ if (strnEQ(d,"get",3)) {
+ d += 3;
+ if (*d == 'p') {
+ if (strEQ(d,"ppid"))
+ FUN0(O_GETPPID);
+ if (strEQ(d,"pgrp"))
+ UNI(O_GETPGRP);
+ if (strEQ(d,"priority"))
+ FUN2(O_GETPRIORITY);
+ if (strEQ(d,"protobyname"))
+ UNI(O_GPBYNAME);
+ if (strEQ(d,"protobynumber"))
+ FUN1(O_GPBYNUMBER);
+ if (strEQ(d,"protoent"))
+ FUN0(O_GPROTOENT);
+ if (strEQ(d,"pwent"))
+ FUN0(O_GPWENT);
+ if (strEQ(d,"pwnam"))
+ FUN1(O_GPWNAM);
+ if (strEQ(d,"pwuid"))
+ FUN1(O_GPWUID);
+ if (strEQ(d,"peername"))
+ FOP(O_GETPEERNAME);
+ }
+ else if (*d == 'h') {
+ if (strEQ(d,"hostbyname"))
+ UNI(O_GHBYNAME);
+ if (strEQ(d,"hostbyaddr"))
+ FUN2(O_GHBYADDR);
+ if (strEQ(d,"hostent"))
+ FUN0(O_GHOSTENT);
+ }
+ else if (*d == 'n') {
+ if (strEQ(d,"netbyname"))
+ UNI(O_GNBYNAME);
+ if (strEQ(d,"netbyaddr"))
+ FUN2(O_GNBYADDR);
+ if (strEQ(d,"netent"))
+ FUN0(O_GNETENT);
+ }
+ else if (*d == 's') {
+ if (strEQ(d,"servbyname"))
+ FUN2(O_GSBYNAME);
+ if (strEQ(d,"servbyport"))
+ FUN2(O_GSBYPORT);
+ if (strEQ(d,"servent"))
+ FUN0(O_GSERVENT);
+ if (strEQ(d,"sockname"))
+ FOP(O_GETSOCKNAME);
+ if (strEQ(d,"sockopt"))
+ FOP3(O_GSOCKOPT);
+ }
+ else if (*d == 'g') {
+ if (strEQ(d,"grent"))
+ FUN0(O_GGRENT);
+ if (strEQ(d,"grnam"))
+ FUN1(O_GGRNAM);
+ if (strEQ(d,"grgid"))
+ FUN1(O_GGRGID);
+ }
+ else if (*d == 'l') {
+ if (strEQ(d,"login"))
+ FUN0(O_GETLOGIN);
+ }
+ d -= 3;
+ }
+ break;
+ case 'h': case 'H':
+ SNARFWORD;
+ if (strEQ(d,"hex"))
+ UNI(O_HEX);
+ break;
+ case 'i': case 'I':
+ SNARFWORD;
+ if (strEQ(d,"if")) {
+ yylval.ival = curcmd->c_line;
+ OPERATOR(IF);
+ }
+ if (strEQ(d,"index"))
+ FUN2x(O_INDEX);
+ if (strEQ(d,"int"))
+ UNI(O_INT);
+ if (strEQ(d,"ioctl"))
+ FOP3(O_IOCTL);
+ break;
+ case 'j': case 'J':
+ SNARFWORD;
+ if (strEQ(d,"join"))
+ FL2(O_JOIN);
+ break;
+ case 'k': case 'K':
+ SNARFWORD;
+ if (strEQ(d,"keys"))
+ HFUN(O_KEYS);
+ if (strEQ(d,"kill"))
+ LOP(O_KILL);
+ break;
+ case 'l': case 'L':
+ SNARFWORD;
+ if (strEQ(d,"last"))
+ LOOPX(O_LAST);
+ if (strEQ(d,"local"))
+ OPERATOR(LOCAL);
+ if (strEQ(d,"length"))
+ UNI(O_LENGTH);
+ if (strEQ(d,"lt") || strEQ(d,"LT"))
+ ROP(O_SLT);
+ if (strEQ(d,"le") || strEQ(d,"LE"))
+ ROP(O_SLE);
+ if (strEQ(d,"localtime"))
+ UNI(O_LOCALTIME);
+ if (strEQ(d,"log"))
+ UNI(O_LOG);
+ if (strEQ(d,"link"))
+ FUN2(O_LINK);
+ if (strEQ(d,"listen"))
+ FOP2(O_LISTEN);
+ if (strEQ(d,"lstat"))
+ FOP(O_LSTAT);
+ break;
+ case 'm': case 'M':
+ if (s[1] == '\'') {
+ d = "m";
+ s++;
+ }
+ else {
+ SNARFWORD;
+ }
+ if (strEQ(d,"m")) {
+ s = scanpat(s-1);
+ if (yylval.arg)
+ TERM(PATTERN);
+ else
+ RETURN(1); /* force error */
+ }
+ switch (d[1]) {
+ case 'k':
+ if (strEQ(d,"mkdir"))
+ FUN2(O_MKDIR);
+ break;
+ case 's':
+ if (strEQ(d,"msgctl"))
+ FUN3(O_MSGCTL);
+ if (strEQ(d,"msgget"))
+ FUN2(O_MSGGET);
+ if (strEQ(d,"msgrcv"))
+ FUN5(O_MSGRCV);
+ if (strEQ(d,"msgsnd"))
+ FUN3(O_MSGSND);
+ break;
+ }
+ break;
+ case 'n': case 'N':
+ SNARFWORD;
+ if (strEQ(d,"next"))
+ LOOPX(O_NEXT);
+ if (strEQ(d,"ne") || strEQ(d,"NE"))
+ EOP(O_SNE);
+ break;
+ case 'o': case 'O':
+ SNARFWORD;
+ if (strEQ(d,"open"))
+ OPERATOR(OPEN);
+ if (strEQ(d,"ord"))
+ UNI(O_ORD);
+ if (strEQ(d,"oct"))
+ UNI(O_OCT);
+ if (strEQ(d,"opendir"))
+ FOP2(O_OPEN_DIR);
+ break;
+ case 'p': case 'P':
+ SNARFWORD;
+ if (strEQ(d,"print")) {
+ checkcomma(s,d,"filehandle");
+ LOP(O_PRINT);
+ }
+ if (strEQ(d,"printf")) {
+ checkcomma(s,d,"filehandle");
+ LOP(O_PRTF);
+ }
+ if (strEQ(d,"push")) {
+ yylval.ival = O_PUSH;
+ OPERATOR(PUSH);
+ }
+ if (strEQ(d,"pop"))
+ OPERATOR(POP);
+ if (strEQ(d,"pack"))
+ FL2(O_PACK);
+ if (strEQ(d,"package"))
+ OPERATOR(PACKAGE);
+ if (strEQ(d,"pipe"))
+ FOP22(O_PIPE_OP);
+ break;
+ case 'q': case 'Q':
+ SNARFWORD;
+ if (strEQ(d,"q")) {
+ s = scanstr(s-1, SCAN_DEF);
+ TERM(RSTRING);
+ }
+ if (strEQ(d,"qq")) {
+ s = scanstr(s-2, SCAN_DEF);
+ TERM(RSTRING);
+ }
+ if (strEQ(d,"qx")) {
+ s = scanstr(s-2, SCAN_DEF);
+ TERM(RSTRING);
+ }
+ break;
+ case 'r': case 'R':
+ SNARFWORD;
+ if (strEQ(d,"return"))
+ OLDLOP(O_RETURN);
+ if (strEQ(d,"require")) {
+ allstabs = TRUE; /* must initialize everything since */
+ UNI(O_REQUIRE); /* we don't know what will be used */
+ }
+ if (strEQ(d,"reset"))
+ UNI(O_RESET);
+ if (strEQ(d,"redo"))
+ LOOPX(O_REDO);
+ if (strEQ(d,"rename"))
+ FUN2(O_RENAME);
+ if (strEQ(d,"rand"))
+ UNI(O_RAND);
+ if (strEQ(d,"rmdir"))
+ UNI(O_RMDIR);
+ if (strEQ(d,"rindex"))
+ FUN2x(O_RINDEX);
+ if (strEQ(d,"read"))
+ FOP3(O_READ);
+ if (strEQ(d,"readdir"))
+ FOP(O_READDIR);
+ if (strEQ(d,"rewinddir"))
+ FOP(O_REWINDDIR);
+ if (strEQ(d,"recv"))
+ FOP4(O_RECV);
+ if (strEQ(d,"reverse"))
+ LOP(O_REVERSE);
+ if (strEQ(d,"readlink"))
+ UNI(O_READLINK);
+ break;
+ case 's': case 'S':
+ if (s[1] == '\'') {
+ d = "s";
+ s++;
+ }
+ else {
+ SNARFWORD;
+ }
+ if (strEQ(d,"s")) {
+ s = scansubst(s);
+ if (yylval.arg)
+ TERM(SUBST);
+ else
+ RETURN(1); /* force error */
+ }
+ switch (d[1]) {
+ case 'a':
+ case 'b':
+ break;
+ case 'c':
+ if (strEQ(d,"scalar"))
+ UNI(O_SCALAR);
+ break;
+ case 'd':
+ break;
+ case 'e':
+ if (strEQ(d,"select"))
+ OPERATOR(SSELECT);
+ if (strEQ(d,"seek"))
+ FOP3(O_SEEK);
+ if (strEQ(d,"semctl"))
+ FUN4(O_SEMCTL);
+ if (strEQ(d,"semget"))
+ FUN3(O_SEMGET);
+ if (strEQ(d,"semop"))
+ FUN2(O_SEMOP);
+ if (strEQ(d,"send"))
+ FOP3(O_SEND);
+ if (strEQ(d,"setpgrp"))
+ FUN2(O_SETPGRP);
+ if (strEQ(d,"setpriority"))
+ FUN3(O_SETPRIORITY);
+ if (strEQ(d,"sethostent"))
+ FUN1(O_SHOSTENT);
+ if (strEQ(d,"setnetent"))
+ FUN1(O_SNETENT);
+ if (strEQ(d,"setservent"))
+ FUN1(O_SSERVENT);
+ if (strEQ(d,"setprotoent"))
+ FUN1(O_SPROTOENT);
+ if (strEQ(d,"setpwent"))
+ FUN0(O_SPWENT);
+ if (strEQ(d,"setgrent"))
+ FUN0(O_SGRENT);
+ if (strEQ(d,"seekdir"))
+ FOP2(O_SEEKDIR);
+ if (strEQ(d,"setsockopt"))
+ FOP4(O_SSOCKOPT);
+ break;
+ case 'f':
+ case 'g':
+ break;
+ case 'h':
+ if (strEQ(d,"shift"))
+ TERM(SHIFT);
+ if (strEQ(d,"shmctl"))
+ FUN3(O_SHMCTL);
+ if (strEQ(d,"shmget"))
+ FUN3(O_SHMGET);
+ if (strEQ(d,"shmread"))
+ FUN4(O_SHMREAD);
+ if (strEQ(d,"shmwrite"))
+ FUN4(O_SHMWRITE);
+ if (strEQ(d,"shutdown"))
+ FOP2(O_SHUTDOWN);
+ break;
+ case 'i':
+ if (strEQ(d,"sin"))
+ UNI(O_SIN);
+ break;
+ case 'j':
+ case 'k':
+ break;
+ case 'l':
+ if (strEQ(d,"sleep"))
+ UNI(O_SLEEP);
+ break;
+ case 'm':
+ case 'n':
+ break;
+ case 'o':
+ if (strEQ(d,"socket"))
+ FOP4(O_SOCKET);
+ if (strEQ(d,"socketpair"))
+ FOP25(O_SOCKPAIR);
+ if (strEQ(d,"sort")) {
+ checkcomma(s,d,"subroutine name");
+ d = bufend;
+ while (s < d && isSPACE(*s)) s++;
+ if (*s == ';' || *s == ')') /* probably a close */
+ fatal("sort is now a reserved word");
+ if (isALPHA(*s) || *s == '_') {
+ /*SUPPRESS 530*/
+ for (d = s; isALNUM(*d); d++) ;
+ strncpy(tokenbuf,s,d-s);
+ tokenbuf[d-s] = '\0';
+ if (strNE(tokenbuf,"keys") &&
+ strNE(tokenbuf,"values") &&
+ strNE(tokenbuf,"split") &&
+ strNE(tokenbuf,"grep") &&
+ strNE(tokenbuf,"readdir") &&
+ strNE(tokenbuf,"unpack") &&
+ strNE(tokenbuf,"do") &&
+ strNE(tokenbuf,"eval") &&
+ (d >= bufend || isSPACE(*d)) )
+ *(--s) = '\\'; /* force next ident to WORD */
+ }
+ LOP(O_SORT);
+ }
+ break;
+ case 'p':
+ if (strEQ(d,"split"))
+ TERM(SPLIT);
+ if (strEQ(d,"sprintf"))
+ FL(O_SPRINTF);
+ if (strEQ(d,"splice")) {
+ yylval.ival = O_SPLICE;
+ OPERATOR(PUSH);
+ }
+ break;
+ case 'q':
+ if (strEQ(d,"sqrt"))
+ UNI(O_SQRT);
+ break;
+ case 'r':
+ if (strEQ(d,"srand"))
+ UNI(O_SRAND);
+ break;
+ case 's':
+ break;
+ case 't':
+ if (strEQ(d,"stat"))
+ FOP(O_STAT);
+ if (strEQ(d,"study")) {
+ sawstudy++;
+ LFUN(O_STUDY);
+ }
+ break;
+ case 'u':
+ if (strEQ(d,"substr"))
+ FUN2x(O_SUBSTR);
+ if (strEQ(d,"sub")) {
+ yylval.ival = savestack->ary_fill; /* restore stuff on reduce */
+ savelong(&subline);
+ saveitem(subname);
+
+ subline = curcmd->c_line;
+ d = bufend;
+ while (s < d && isSPACE(*s))
+ s++;
+ if (isALPHA(*s) || *s == '_' || *s == '\'') {
+ str_sset(subname,curstname);
+ str_ncat(subname,"'",1);
+ for (d = s+1; isALNUM(*d) || *d == '\''; d++)
+ /*SUPPRESS 530*/
+ ;
+ if (d[-1] == '\'')
+ d--;
+ str_ncat(subname,s,d-s);
+ *(--s) = '\\'; /* force next ident to WORD */
+ }
+ else
+ str_set(subname,"?");
+ OPERATOR(SUB);
+ }
+ break;
+ case 'v':
+ case 'w':
+ case 'x':
+ break;
+ case 'y':
+ if (strEQ(d,"system")) {
+ set_csh();
+ LOP(O_SYSTEM);
+ }
+ if (strEQ(d,"symlink"))
+ FUN2(O_SYMLINK);
+ if (strEQ(d,"syscall"))
+ LOP(O_SYSCALL);
+ if (strEQ(d,"sysread"))
+ FOP3(O_SYSREAD);
+ if (strEQ(d,"syswrite"))
+ FOP3(O_SYSWRITE);
+ break;
+ case 'z':
+ break;
+ }
+ break;
+ case 't': case 'T':
+ SNARFWORD;
+ if (strEQ(d,"tr")) {
+ s = scantrans(s);
+ if (yylval.arg)
+ TERM(TRANS);
+ else
+ RETURN(1); /* force error */
+ }
+ if (strEQ(d,"tell"))
+ FOP(O_TELL);
+ if (strEQ(d,"telldir"))
+ FOP(O_TELLDIR);
+ if (strEQ(d,"time"))
+ FUN0(O_TIME);
+ if (strEQ(d,"times"))
+ FUN0(O_TMS);
+ if (strEQ(d,"truncate"))
+ FOP2(O_TRUNCATE);
+ break;
+ case 'u': case 'U':
+ SNARFWORD;
+ if (strEQ(d,"using"))
+ OPERATOR(USING);
+ if (strEQ(d,"until")) {
+ yylval.ival = curcmd->c_line;
+ OPERATOR(UNTIL);
+ }
+ if (strEQ(d,"unless")) {
+ yylval.ival = curcmd->c_line;
+ OPERATOR(UNLESS);
+ }
+ if (strEQ(d,"unlink"))
+ LOP(O_UNLINK);
+ if (strEQ(d,"undef"))
+ LFUN(O_UNDEF);
+ if (strEQ(d,"unpack"))
+ FUN2(O_UNPACK);
+ if (strEQ(d,"utime"))
+ LOP(O_UTIME);
+ if (strEQ(d,"umask"))
+ UNI(O_UMASK);
+ if (strEQ(d,"unshift")) {
+ yylval.ival = O_UNSHIFT;
+ OPERATOR(PUSH);
+ }
+ break;
+ case 'v': case 'V':
+ SNARFWORD;
+ if (strEQ(d,"values"))
+ HFUN(O_VALUES);
+ if (strEQ(d,"vec")) {
+ sawvec = TRUE;
+ FUN3(O_VEC);
+ }
+ break;
+ case 'w': case 'W':
+ SNARFWORD;
+ if (strEQ(d,"while")) {
+ yylval.ival = curcmd->c_line;
+ OPERATOR(WHILE);
+ }
+ if (strEQ(d,"warn"))
+ LOP(O_WARN);
+ if (strEQ(d,"wait"))
+ FUN0(O_WAIT);
+ if (strEQ(d,"waitpid"))
+ FUN2(O_WAITPID);
+ if (strEQ(d,"wantarray")) {
+ yylval.arg = op_new(1);
+ yylval.arg->arg_type = O_ITEM;
+ yylval.arg[1].arg_type = A_WANTARRAY;
+ TERM(RSTRING);
+ }
+ if (strEQ(d,"write"))
+ FOP(O_WRITE);
+ break;
+ case 'x': case 'X':
+ if (*s == 'x' && isDIGIT(s[1]) && !expectterm) {
+ s++;
+ MOP(O_REPEAT);
+ }
+ SNARFWORD;
+ if (strEQ(d,"x")) {
+ if (!expectterm)
+ MOP(O_REPEAT);
+ check_uni();
+ }
+ break;
+ case 'y': case 'Y':
+ if (s[1] == '\'') {
+ d = "y";
+ s++;
+ }
+ else {
+ SNARFWORD;
+ }
+ if (strEQ(d,"y")) {
+ s = scantrans(s);
+ TERM(TRANS);
+ }
+ break;
+ case 'z': case 'Z':
+ SNARFWORD;
+ break;
+ }
+ yylval.cval = savestr(d);
+ if (expectterm == 2) { /* special case: start of statement */
+ while (isSPACE(*s)) s++;
+ if (*s == ':') {
+ s++;
+ CLINE;
+ OPERATOR(LABEL);
+ }
+ TERM(WORD);
+ }
+ expectterm = FALSE;
+ if (oldoldbufptr && oldoldbufptr < bufptr) {
+ while (isSPACE(*oldoldbufptr))
+ oldoldbufptr++;
+ if (*oldoldbufptr == 'p' && strnEQ(oldoldbufptr,"print",5))
+ expectterm = TRUE;
+ else if (*oldoldbufptr == 's' && strnEQ(oldoldbufptr,"sort",4))
+ expectterm = TRUE;
+ }
+ return (CLINE, bufptr = s, (int)WORD);
+}
+
+void
+checkcomma(s,name,what)
+register char *s;
+char *name;
+char *what;
+{
+ char *w;
+
+ if (dowarn && *s == ' ' && s[1] == '(') {
+ w = index(s,')');
+ if (w)
+ for (w++; *w && isSPACE(*w); w++) ;
+ if (!w || !*w || !index(";|}", *w)) /* an advisory hack only... */
+ warn("%s (...) interpreted as function",name);
+ }
+ while (s < bufend && isSPACE(*s))
+ s++;
+ if (*s == '(')
+ s++;
+ while (s < bufend && isSPACE(*s))
+ s++;
+ if (isALPHA(*s) || *s == '_') {
+ w = s++;
+ while (isALNUM(*s))
+ s++;
+ while (s < bufend && isSPACE(*s))
+ s++;
+ if (*s == ',') {
+ *s = '\0';
+ w = instr(
+ "tell eof times getlogin wait length shift umask getppid \
+ cos exp int log rand sin sqrt ord wantarray",
+ w);
+ *s = ',';
+ if (w)
+ return;
+ fatal("No comma allowed after %s", what);
+ }
+ }
+}
+
+char *
+scanident(s,send,dest)
+register char *s;
+register char *send;
+char *dest;
+{
+ register char *d;
+ int brackets = 0;
+
+ reparse = Nullch;
+ s++;
+ d = dest;
+ if (isDIGIT(*s)) {
+ while (isDIGIT(*s))
+ *d++ = *s++;
+ }
+ else {
+ while (isALNUM(*s) || *s == '\'')
+ *d++ = *s++;
+ }
+ while (d > dest+1 && d[-1] == '\'')
+ d--,s--;
+ *d = '\0';
+ d = dest;
+ if (!*d) {
+ *d = *s++;
+ if (*d == '{' /* } */ ) {
+ d = dest;
+ brackets++;
+ while (s < send && brackets) {
+ if (!reparse && (d == dest || (*s && isALNUM(*s) ))) {
+ *d++ = *s++;
+ continue;
+ }
+ else if (!reparse)
+ reparse = s;
+ switch (*s++) {
+ /* { */
+ case '}':
+ brackets--;
+ if (reparse && reparse == s - 1)
+ reparse = Nullch;
+ break;
+ case '{': /* } */
+ brackets++;
+ break;
+ }
+ }
+ *d = '\0';
+ d = dest;
+ }
+ else
+ d[1] = '\0';
+ }
+ if (*d == '^' && (isUPPER(*s) || index("[\\]^_?", *s))) {
+#ifdef DEBUGGING
+ if (*s == 'D')
+ debug |= 32768;
+#endif
+ *d = *s++ ^ 64;
+ }
+ return s;
+}
+
+void
+scanconst(spat,string,len)
+SPAT *spat;
+char *string;
+int len;
+{
+ register STR *tmpstr;
+ register char *t;
+ register char *d;
+ register char *e;
+ char *origstring = string;
+ static char *vert = "|";
+
+ if (ninstr(string, string+len, vert, vert+1))
+ return;
+ if (*string == '^')
+ string++, len--;
+ tmpstr = Str_new(86,len);
+ str_nset(tmpstr,string,len);
+ t = str_get(tmpstr);
+ e = t + len;
+ tmpstr->str_u.str_useful = 100;
+ for (d=t; d < e; ) {
+ switch (*d) {
+ case '{':
+ if (isDIGIT(d[1]))
+ e = d;
+ else
+ goto defchar;
+ break;
+ case '.': case '[': case '$': case '(': case ')': case '|': case '+':
+ case '^':
+ e = d;
+ break;
+ case '\\':
+ if (d[1] && index("wWbB0123456789sSdDlLuUExc",d[1])) {
+ e = d;
+ break;
+ }
+ Move(d+1,d,e-d,char);
+ e--;
+ switch(*d) {
+ case 'n':
+ *d = '\n';
+ break;
+ case 't':
+ *d = '\t';
+ break;
+ case 'f':
+ *d = '\f';
+ break;
+ case 'r':
+ *d = '\r';
+ break;
+ case 'e':
+ *d = '\033';
+ break;
+ case 'a':
+ *d = '\007';
+ break;
+ }
+ /* FALL THROUGH */
+ default:
+ defchar:
+ if (d[1] == '*' || (d[1] == '{' && d[2] == '0') || d[1] == '?') {
+ e = d;
+ break;
+ }
+ d++;
+ }
+ }
+ if (d == t) {
+ str_free(tmpstr);
+ return;
+ }
+ *d = '\0';
+ tmpstr->str_cur = d - t;
+ if (d == t+len)
+ spat->spat_flags |= SPAT_ALL;
+ if (*origstring != '^')
+ spat->spat_flags |= SPAT_SCANFIRST;
+ spat->spat_short = tmpstr;
+ spat->spat_slen = d - t;
+}
+
+char *
+scanpat(s)
+register char *s;
+{
+ register SPAT *spat;
+ register char *d;
+ register char *e;
+ int len;
+ SPAT savespat;
+ STR *str = Str_new(93,0);
+ char delim;
+
+ Newz(801,spat,1,SPAT);
+ spat->spat_next = curstash->tbl_spatroot; /* link into spat list */
+ curstash->tbl_spatroot = spat;
+
+ switch (*s++) {
+ case 'm':
+ s++;
+ break;
+ case '/':
+ break;
+ case '?':
+ spat->spat_flags |= SPAT_ONCE;
+ break;
+ default:
+ fatal("panic: scanpat");
+ }
+ s = str_append_till(str,s,bufend,s[-1],patleave);
+ if (s >= bufend) {
+ str_free(str);
+ yyerror("Search pattern not terminated");
+ yylval.arg = Nullarg;
+ return s;
+ }
+ delim = *s++;
+ while (*s == 'i' || *s == 'o' || *s == 'g') {
+ if (*s == 'i') {
+ s++;
+ sawi = TRUE;
+ spat->spat_flags |= SPAT_FOLD;
+ }
+ if (*s == 'o') {
+ s++;
+ spat->spat_flags |= SPAT_KEEP;
+ }
+ if (*s == 'g') {
+ s++;
+ spat->spat_flags |= SPAT_GLOBAL;
+ }
+ }
+ len = str->str_cur;
+ e = str->str_ptr + len;
+ if (delim == '\'')
+ d = e;
+ else
+ d = str->str_ptr;
+ for (; d < e; d++) {
+ if (*d == '\\')
+ d++;
+ else if ((*d == '$' && d[1] && d[1] != '|' && d[1] != ')') ||
+ (*d == '@')) {
+ register ARG *arg;
+
+ spat->spat_runtime = arg = op_new(1);
+ arg->arg_type = O_ITEM;
+ arg[1].arg_type = A_DOUBLE;
+ arg[1].arg_ptr.arg_str = str_smake(str);
+ d = scanident(d,bufend,buf);
+ (void)stabent(buf,TRUE); /* make sure it's created */
+ for (; d < e; d++) {
+ if (*d == '\\')
+ d++;
+ else if (*d == '$' && d[1] && d[1] != '|' && d[1] != ')') {
+ d = scanident(d,bufend,buf);
+ (void)stabent(buf,TRUE);
+ }
+ else if (*d == '@') {
+ d = scanident(d,bufend,buf);
+ if (strEQ(buf,"ARGV") || strEQ(buf,"ENV") ||
+ strEQ(buf,"SIG") || strEQ(buf,"INC"))
+ (void)stabent(buf,TRUE);
+ }
+ }
+ goto got_pat; /* skip compiling for now */
+ }
+ }
+ if (spat->spat_flags & SPAT_FOLD)
+ StructCopy(spat, &savespat, SPAT);
+ scanconst(spat,str->str_ptr,len);
+ if ((spat->spat_flags & SPAT_ALL) && (spat->spat_flags & SPAT_SCANFIRST)) {
+ fbmcompile(spat->spat_short, spat->spat_flags & SPAT_FOLD);
+ spat->spat_regexp = regcomp(str->str_ptr,str->str_ptr+len,
+ spat->spat_flags & SPAT_FOLD);
+ /* Note that this regexp can still be used if someone says
+ * something like /a/ && s//b/; so we can't delete it.
+ */
+ }
+ else {
+ if (spat->spat_flags & SPAT_FOLD)
+ StructCopy(&savespat, spat, SPAT);
+ if (spat->spat_short)
+ fbmcompile(spat->spat_short, spat->spat_flags & SPAT_FOLD);
+ spat->spat_regexp = regcomp(str->str_ptr,str->str_ptr+len,
+ spat->spat_flags & SPAT_FOLD);
+ hoistmust(spat);
+ }
+ got_pat:
+ str_free(str);
+ yylval.arg = make_match(O_MATCH,stab2arg(A_STAB,defstab),spat);
+ return s;
+}
+
+char *
+scansubst(start)
+char *start;
+{
+ register char *s = start;
+ register SPAT *spat;
+ register char *d;
+ register char *e;
+ int len;
+ STR *str = Str_new(93,0);
+ char term = *s;
+
+ if (term && (d = index("([{< )]}> )]}>",term)))
+ term = d[5];
+
+ Newz(802,spat,1,SPAT);
+ spat->spat_next = curstash->tbl_spatroot; /* link into spat list */
+ curstash->tbl_spatroot = spat;
+
+ s = str_append_till(str,s+1,bufend,term,patleave);
+ if (s >= bufend) {
+ str_free(str);
+ yyerror("Substitution pattern not terminated");
+ yylval.arg = Nullarg;
+ return s;
+ }
+ len = str->str_cur;
+ e = str->str_ptr + len;
+ for (d = str->str_ptr; d < e; d++) {
+ if (*d == '\\')
+ d++;
+ else if ((*d == '$' && d[1] && d[1] != '|' && /*(*/ d[1] != ')') ||
+ *d == '@' ) {
+ register ARG *arg;
+
+ spat->spat_runtime = arg = op_new(1);
+ arg->arg_type = O_ITEM;
+ arg[1].arg_type = A_DOUBLE;
+ arg[1].arg_ptr.arg_str = str_smake(str);
+ d = scanident(d,e,buf);
+ (void)stabent(buf,TRUE); /* make sure it's created */
+ for (; *d; d++) {
+ if (*d == '$' && d[1] && d[-1] != '\\' && d[1] != '|') {
+ d = scanident(d,e,buf);
+ (void)stabent(buf,TRUE);
+ }
+ else if (*d == '@' && d[-1] != '\\') {
+ d = scanident(d,e,buf);
+ if (strEQ(buf,"ARGV") || strEQ(buf,"ENV") ||
+ strEQ(buf,"SIG") || strEQ(buf,"INC"))
+ (void)stabent(buf,TRUE);
+ }
+ }
+ goto get_repl; /* skip compiling for now */
+ }
+ }
+ scanconst(spat,str->str_ptr,len);
+get_repl:
+ if (term != *start)
+ s++;
+ s = scanstr(s, SCAN_REPL);
+ if (s >= bufend) {
+ str_free(str);
+ yyerror("Substitution replacement not terminated");
+ yylval.arg = Nullarg;
+ return s;
+ }
+ spat->spat_repl = yylval.arg;
+ if ((spat->spat_repl[1].arg_type & A_MASK) == A_SINGLE)
+ spat->spat_flags |= SPAT_CONST;
+ else if ((spat->spat_repl[1].arg_type & A_MASK) == A_DOUBLE) {
+ STR *tmpstr;
+ register char *t;
+
+ spat->spat_flags |= SPAT_CONST;
+ tmpstr = spat->spat_repl[1].arg_ptr.arg_str;
+ e = tmpstr->str_ptr + tmpstr->str_cur;
+ for (t = tmpstr->str_ptr; t < e; t++) {
+ if (*t == '$' && t[1] && (index("`'&+0123456789",t[1]) ||
+ (t[1] == '{' /*}*/ && isDIGIT(t[2])) ))
+ spat->spat_flags &= ~SPAT_CONST;
+ }
+ }
+ while (*s == 'g' || *s == 'i' || *s == 'e' || *s == 'o') {
+ int es = 0;
+
+ if (*s == 'e') {
+ s++;
+ es++;
+ if ((spat->spat_repl[1].arg_type & A_MASK) == A_DOUBLE)
+ spat->spat_repl[1].arg_type = A_SINGLE;
+ spat->spat_repl = make_op(
+ (!es && spat->spat_repl[1].arg_type == A_SINGLE
+ ? O_EVALONCE
+ : O_EVAL),
+ 2,
+ spat->spat_repl,
+ Nullarg,
+ Nullarg);
+ spat->spat_flags &= ~SPAT_CONST;
+ }
+ if (*s == 'g') {
+ s++;
+ spat->spat_flags |= SPAT_GLOBAL;
+ }
+ if (*s == 'i') {
+ s++;
+ sawi = TRUE;
+ spat->spat_flags |= SPAT_FOLD;
+ if (!(spat->spat_flags & SPAT_SCANFIRST)) {
+ str_free(spat->spat_short); /* anchored opt doesn't do */
+ spat->spat_short = Nullstr; /* case insensitive match */
+ spat->spat_slen = 0;
+ }
+ }
+ if (*s == 'o') {
+ s++;
+ spat->spat_flags |= SPAT_KEEP;
+ }
+ }
+ if (spat->spat_short && (spat->spat_flags & SPAT_SCANFIRST))
+ fbmcompile(spat->spat_short, spat->spat_flags & SPAT_FOLD);
+ if (!spat->spat_runtime) {
+ spat->spat_regexp = regcomp(str->str_ptr,str->str_ptr+len,
+ spat->spat_flags & SPAT_FOLD);
+ hoistmust(spat);
+ }
+ yylval.arg = make_match(O_SUBST,stab2arg(A_STAB,defstab),spat);
+ str_free(str);
+ return s;
+}
+
+void
+hoistmust(spat)
+register SPAT *spat;
+{
+ if (!spat->spat_short && spat->spat_regexp->regstart &&
+ (!spat->spat_regexp->regmust || spat->spat_regexp->reganch & ROPT_ANCH)
+ ) {
+ if (!(spat->spat_regexp->reganch & ROPT_ANCH))
+ spat->spat_flags |= SPAT_SCANFIRST;
+ else if (spat->spat_flags & SPAT_FOLD)
+ return;
+ spat->spat_short = str_smake(spat->spat_regexp->regstart);
+ }
+ else if (spat->spat_regexp->regmust) {/* is there a better short-circuit? */
+ if (spat->spat_short &&
+ str_eq(spat->spat_short,spat->spat_regexp->regmust))
+ {
+ if (spat->spat_flags & SPAT_SCANFIRST) {
+ str_free(spat->spat_short);
+ spat->spat_short = Nullstr;
+ }
+ else {
+ str_free(spat->spat_regexp->regmust);
+ spat->spat_regexp->regmust = Nullstr;
+ return;
+ }
+ }
+ if (!spat->spat_short || /* promote the better string */
+ ((spat->spat_flags & SPAT_SCANFIRST) &&
+ (spat->spat_short->str_cur < spat->spat_regexp->regmust->str_cur) )){
+ str_free(spat->spat_short); /* ok if null */
+ spat->spat_short = spat->spat_regexp->regmust;
+ spat->spat_regexp->regmust = Nullstr;
+ spat->spat_flags |= SPAT_SCANFIRST;
+ }
+ }
+}
+
+char *
+scantrans(start)
+char *start;
+{
+ register char *s = start;
+ ARG *arg =
+ l(make_op(O_TRANS,2,stab2arg(A_STAB,defstab),Nullarg,Nullarg));
+ STR *tstr;
+ STR *rstr;
+ register char *t;
+ register char *r;
+ register short *tbl;
+ register int i;
+ register int j;
+ int tlen, rlen;
+ int squash;
+ int delete;
+ int complement;
+
+ New(803,tbl,256,short);
+ arg[2].arg_type = A_NULL;
+ arg[2].arg_ptr.arg_cval = (char*) tbl;
+
+ s = scanstr(s, SCAN_TR);
+ if (s >= bufend) {
+ yyerror("Translation pattern not terminated");
+ yylval.arg = Nullarg;
+ return s;
+ }
+ tstr = yylval.arg[1].arg_ptr.arg_str;
+ yylval.arg[1].arg_ptr.arg_str = Nullstr;
+ arg_free(yylval.arg);
+ t = tstr->str_ptr;
+ tlen = tstr->str_cur;
+
+ if (s[-1] == *start)
+ s--;
+
+ s = scanstr(s, SCAN_TR|SCAN_REPL);
+ if (s >= bufend) {
+ yyerror("Translation replacement not terminated");
+ yylval.arg = Nullarg;
+ return s;
+ }
+ rstr = yylval.arg[1].arg_ptr.arg_str;
+ yylval.arg[1].arg_ptr.arg_str = Nullstr;
+ arg_free(yylval.arg);
+ r = rstr->str_ptr;
+ rlen = rstr->str_cur;
+
+ complement = delete = squash = 0;
+ while (*s == 'c' || *s == 'd' || *s == 's') {
+ if (*s == 'c')
+ complement = 1;
+ else if (*s == 'd')
+ delete = 2;
+ else
+ squash = 1;
+ s++;
+ }
+ arg[2].arg_len = delete|squash;
+ yylval.arg = arg;
+ if (complement) {
+ Zero(tbl, 256, short);
+ for (i = 0; i < tlen; i++)
+ tbl[t[i] & 0377] = -1;
+ for (i = 0, j = 0; i < 256; i++) {
+ if (!tbl[i]) {
+ if (j >= rlen) {
+ if (delete)
+ tbl[i] = -2;
+ else if (rlen)
+ tbl[i] = r[j-1] & 0377;
+ else
+ tbl[i] = i;
+ }
+ else
+ tbl[i] = r[j++] & 0377;
+ }
+ }
+ }
+ else {
+ if (!rlen && !delete) {
+ r = t; rlen = tlen;
+ }
+ for (i = 0; i < 256; i++)
+ tbl[i] = -1;
+ for (i = 0, j = 0; i < tlen; i++,j++) {
+ if (j >= rlen) {
+ if (delete) {
+ if (tbl[t[i] & 0377] == -1)
+ tbl[t[i] & 0377] = -2;
+ continue;
+ }
+ --j;
+ }
+ if (tbl[t[i] & 0377] == -1)
+ tbl[t[i] & 0377] = r[j] & 0377;
+ }
+ }
+ str_free(tstr);
+ str_free(rstr);
+ return s;
+}
+
+char *
+scanstr(start, in_what)
+char *start;
+int in_what;
+{
+ register char *s = start;
+ register char term;
+ register char *d;
+ register ARG *arg;
+ register char *send;
+ register bool makesingle = FALSE;
+ register STAB *stab;
+ bool alwaysdollar = FALSE;
+ bool hereis = FALSE;
+ STR *herewas;
+ STR *str;
+ /* which backslash sequences to keep */
+ char *leave = (in_what & SCAN_TR)
+ ? "\\$@nrtfbeacx0123456789-"
+ : "\\$@nrtfbeacx0123456789[{]}lLuUE";
+ int len;
+
+ arg = op_new(1);
+ yylval.arg = arg;
+ arg->arg_type = O_ITEM;
+
+ switch (*s) {
+ default: /* a substitution replacement */
+ arg[1].arg_type = A_DOUBLE;
+ makesingle = TRUE; /* maybe disable runtime scanning */
+ term = *s;
+ if (term == '\'')
+ leave = Nullch;
+ goto snarf_it;
+ case '0':
+ {
+ unsigned long i;
+ int shift;
+
+ arg[1].arg_type = A_SINGLE;
+ if (s[1] == 'x') {
+ shift = 4;
+ s += 2;
+ }
+ else if (s[1] == '.')
+ goto decimal;
+ else
+ shift = 3;
+ i = 0;
+ for (;;) {
+ switch (*s) {
+ default:
+ goto out;
+ case '_':
+ s++;
+ break;
+ case '8': case '9':
+ if (shift != 4)
+ yyerror("Illegal octal digit");
+ /* FALL THROUGH */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7':
+ i <<= shift;
+ i += *s++ & 15;
+ break;
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ if (shift != 4)
+ goto out;
+ i <<= 4;
+ i += (*s++ & 7) + 9;
+ break;
+ }
+ }
+ out:
+ str = Str_new(92,0);
+ str_numset(str,(double)i);
+ if (str->str_ptr) {
+ Safefree(str->str_ptr);
+ str->str_ptr = Nullch;
+ str->str_len = str->str_cur = 0;
+ }
+ arg[1].arg_ptr.arg_str = str;
+ }
+ break;
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9': case '.':
+ decimal:
+ arg[1].arg_type = A_SINGLE;
+ d = tokenbuf;
+ while (isDIGIT(*s) || *s == '_') {
+ if (*s == '_')
+ s++;
+ else
+ *d++ = *s++;
+ }
+ if (*s == '.' && s[1] != '.') {
+ *d++ = *s++;
+ while (isDIGIT(*s) || *s == '_') {
+ if (*s == '_')
+ s++;
+ else
+ *d++ = *s++;
+ }
+ }
+ if (*s && index("eE",*s) && index("+-0123456789",s[1])) {
+ *d++ = *s++;
+ if (*s == '+' || *s == '-')
+ *d++ = *s++;
+ while (isDIGIT(*s))
+ *d++ = *s++;
+ }
+ *d = '\0';
+ str = Str_new(92,0);
+ str_numset(str,atof(tokenbuf));
+ if (str->str_ptr) {
+ Safefree(str->str_ptr);
+ str->str_ptr = Nullch;
+ str->str_len = str->str_cur = 0;
+ }
+ arg[1].arg_ptr.arg_str = str;
+ break;
+ case '<':
+ if (in_what & (SCAN_REPL|SCAN_TR))
+ goto do_double;
+ if (*++s == '<') {
+ hereis = TRUE;
+ d = tokenbuf;
+ if (!rsfp)
+ *d++ = '\n';
+ if (*++s && index("`'\"",*s)) {
+ term = *s++;
+ s = cpytill(d,s,bufend,term,&len);
+ if (s < bufend)
+ s++;
+ d += len;
+ }
+ else {
+ if (*s == '\\')
+ s++, term = '\'';
+ else
+ term = '"';
+ while (isALNUM(*s))
+ *d++ = *s++;
+ } /* assuming tokenbuf won't clobber */
+ *d++ = '\n';
+ *d = '\0';
+ len = d - tokenbuf;
+ d = "\n";
+ if (rsfp || !(d=ninstr(s,bufend,d,d+1)))
+ herewas = str_make(s,bufend-s);
+ else
+ s--, herewas = str_make(s,d-s);
+ s += herewas->str_cur;
+ if (term == '\'')
+ goto do_single;
+ if (term == '`')
+ goto do_back;
+ goto do_double;
+ }
+ d = tokenbuf;
+ s = cpytill(d,s,bufend,'>',&len);
+ if (s < bufend)
+ s++;
+ else
+ fatal("Unterminated <> operator");
+
+ if (*d == '$') d++;
+ while (*d && (isALNUM(*d) || *d == '\''))
+ d++;
+ if (d - tokenbuf != len) {
+ s = start;
+ term = *s;
+ arg[1].arg_type = A_GLOB;
+ set_csh();
+ alwaysdollar = TRUE; /* treat $) and $| as variables */
+ goto snarf_it;
+ }
+ else {
+ d = tokenbuf;
+ if (!len)
+ (void)strcpy(d,"ARGV");
+ if (*d == '$') {
+ arg[1].arg_type = A_INDREAD;
+ arg[1].arg_ptr.arg_stab = stabent(d+1,TRUE);
+ }
+ else {
+ arg[1].arg_type = A_READ;
+ arg[1].arg_ptr.arg_stab = stabent(d,TRUE);
+ if (!stab_io(arg[1].arg_ptr.arg_stab))
+ stab_io(arg[1].arg_ptr.arg_stab) = stio_new();
+ if (strEQ(d,"ARGV")) {
+ (void)aadd(arg[1].arg_ptr.arg_stab);
+ stab_io(arg[1].arg_ptr.arg_stab)->flags |=
+ IOF_ARGV|IOF_START;
+ }
+ }
+ }
+ break;
+
+ case 'q':
+ s++;
+ if (*s == 'q') {
+ s++;
+ goto do_double;
+ }
+ if (*s == 'x') {
+ s++;
+ goto do_back;
+ }
+ /* FALL THROUGH */
+ case '\'':
+ do_single:
+ term = *s;
+ arg[1].arg_type = A_SINGLE;
+ leave = Nullch;
+ goto snarf_it;
+
+ case '"':
+ do_double:
+ term = *s;
+ arg[1].arg_type = A_DOUBLE;
+ makesingle = TRUE; /* maybe disable runtime scanning */
+ alwaysdollar = TRUE; /* treat $) and $| as variables */
+ goto snarf_it;
+ case '`':
+ do_back:
+ term = *s;
+ arg[1].arg_type = A_BACKTICK;
+ set_csh();
+ alwaysdollar = TRUE; /* treat $) and $| as variables */
+ snarf_it:
+ {
+ STR *tmpstr;
+ STR *tmpstr2 = Nullstr;
+ char *tmps;
+ char *start;
+ bool dorange = FALSE;
+
+ CLINE;
+ multi_start = curcmd->c_line;
+ if (hereis)
+ multi_open = multi_close = '<';
+ else {
+ multi_open = term;
+ if (term && (tmps = index("([{< )]}> )]}>",term)))
+ term = tmps[5];
+ multi_close = term;
+ }
+ tmpstr = Str_new(87,80);
+ if (hereis) {
+ term = *tokenbuf;
+ if (!rsfp) {
+ d = s;
+ while (s < bufend &&
+ (*s != term || bcmp(s,tokenbuf,len) != 0) ) {
+ if (*s++ == '\n')
+ curcmd->c_line++;
+ }
+ if (s >= bufend) {
+ curcmd->c_line = multi_start;
+ fatal("EOF in string");
+ }
+ str_nset(tmpstr,d+1,s-d);
+ s += len - 1;
+ str_ncat(herewas,s,bufend-s);
+ str_replace(linestr,herewas);
+ oldoldbufptr = oldbufptr = bufptr = s = str_get(linestr);
+ bufend = linestr->str_ptr + linestr->str_cur;
+ hereis = FALSE;
+ }
+ else
+ str_nset(tmpstr,"",0); /* avoid "uninitialized" warning */
+ }
+ else
+ s = str_append_till(tmpstr,s+1,bufend,term,leave);
+ while (s >= bufend) { /* multiple line string? */
+ if (!rsfp ||
+ !(oldoldbufptr = oldbufptr = s = str_gets(linestr, rsfp, 0))) {
+ curcmd->c_line = multi_start;
+ fatal("EOF in string");
+ }
+ curcmd->c_line++;
+ if (perldb) {
+ STR *str = Str_new(88,0);
+
+ str_sset(str,linestr);
+ astore(stab_xarray(curcmd->c_filestab),
+ (int)curcmd->c_line,str);
+ }
+ bufend = linestr->str_ptr + linestr->str_cur;
+ if (hereis) {
+ if (*s == term && bcmp(s,tokenbuf,len) == 0) {
+ s = bufend - 1;
+ *s = ' ';
+ str_scat(linestr,herewas);
+ bufend = linestr->str_ptr + linestr->str_cur;
+ }
+ else {
+ s = bufend;
+ str_scat(tmpstr,linestr);
+ }
+ }
+ else
+ s = str_append_till(tmpstr,s,bufend,term,leave);
+ }
+ multi_end = curcmd->c_line;
+ s++;
+ if (tmpstr->str_cur + 5 < tmpstr->str_len) {
+ tmpstr->str_len = tmpstr->str_cur + 1;
+ Renew(tmpstr->str_ptr, tmpstr->str_len, char);
+ }
+ if (arg[1].arg_type == A_SINGLE) {
+ arg[1].arg_ptr.arg_str = tmpstr;
+ break;
+ }
+ tmps = s;
+ s = tmpstr->str_ptr;
+ send = s + tmpstr->str_cur;
+ while (s < send) { /* see if we can make SINGLE */
+ if (*s == '\\' && s[1] && isDIGIT(s[1]) && !isDIGIT(s[2]) &&
+ !alwaysdollar && s[1] != '0')
+ *s = '$'; /* grandfather \digit in subst */
+ if ((*s == '$' || *s == '@') && s+1 < send &&
+ (alwaysdollar || (s[1] != ')' && s[1] != '|'))) {
+ makesingle = FALSE; /* force interpretation */
+ }
+ else if (*s == '\\' && s+1 < send) {
+ if (index("lLuUE",s[1]))
+ makesingle = FALSE;
+ s++;
+ }
+ s++;
+ }
+ s = d = start = tmpstr->str_ptr; /* assuming shrinkage only */
+ while (s < send || dorange) {
+ if (in_what & SCAN_TR) {
+ if (dorange) {
+ int i;
+ int max;
+ if (!tmpstr2) { /* oops, have to grow */
+ tmpstr2 = str_smake(tmpstr);
+ s = tmpstr2->str_ptr + (s - tmpstr->str_ptr);
+ send = tmpstr2->str_ptr + (send - tmpstr->str_ptr);
+ }
+ i = d - tmpstr->str_ptr;
+ STR_GROW(tmpstr, tmpstr->str_len + 256);
+ d = tmpstr->str_ptr + i;
+ d -= 2;
+ max = d[1] & 0377;
+ for (i = (*d & 0377); i <= max; i++)
+ *d++ = i;
+ start = s;
+ dorange = FALSE;
+ continue;
+ }
+ else if (*s == '-' && s+1 < send && s != start) {
+ dorange = TRUE;
+ s++;
+ }
+ }
+ else {
+ if ((*s == '$' && s+1 < send &&
+ (alwaysdollar || /*(*/(s[1] != ')' && s[1] != '|')) ) ||
+ (*s == '@' && s+1 < send) ) {
+ if (s[1] == '#' && (isALPHA(s[2]) || s[2] == '_'))
+ *d++ = *s++;
+ len = scanident(s,send,tokenbuf) - s;
+ if (*s == '$' || strEQ(tokenbuf,"ARGV")
+ || strEQ(tokenbuf,"ENV")
+ || strEQ(tokenbuf,"SIG")
+ || strEQ(tokenbuf,"INC") )
+ (void)stabent(tokenbuf,TRUE); /* add symbol */
+ while (len--)
+ *d++ = *s++;
+ continue;
+ }
+ }
+ if (*s == '\\' && s+1 < send) {
+ s++;
+ switch (*s) {
+ case '-':
+ if (in_what & SCAN_TR) {
+ *d++ = *s++;
+ continue;
+ }
+ /* FALL THROUGH */
+ default:
+ if (!makesingle && (!leave || (*s && index(leave,*s))))
+ *d++ = '\\';
+ *d++ = *s++;
+ continue;
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ *d++ = scanoct(s, 3, &len);
+ s += len;
+ continue;
+ case 'x':
+ *d++ = scanhex(++s, 2, &len);
+ s += len;
+ continue;
+ case 'c':
+ s++;
+ *d = *s++;
+ if (isLOWER(*d))
+ *d = toupper(*d);
+ *d++ ^= 64;
+ continue;
+ case 'b':
+ *d++ = '\b';
+ break;
+ case 'n':
+ *d++ = '\n';
+ break;
+ case 'r':
+ *d++ = '\r';
+ break;
+ case 'f':
+ *d++ = '\f';
+ break;
+ case 't':
+ *d++ = '\t';
+ break;
+ case 'e':
+ *d++ = '\033';
+ break;
+ case 'a':
+ *d++ = '\007';
+ break;
+ }
+ s++;
+ continue;
+ }
+ *d++ = *s++;
+ }
+ *d = '\0';
+
+ if (arg[1].arg_type == A_DOUBLE && makesingle)
+ arg[1].arg_type = A_SINGLE; /* now we can optimize on it */
+
+ tmpstr->str_cur = d - tmpstr->str_ptr;
+ if (arg[1].arg_type == A_GLOB) {
+ arg[1].arg_ptr.arg_stab = stab = genstab();
+ stab_io(stab) = stio_new();
+ str_sset(stab_val(stab), tmpstr);
+ }
+ else
+ arg[1].arg_ptr.arg_str = tmpstr;
+ s = tmps;
+ if (tmpstr2)
+ str_free(tmpstr2);
+ break;
+ }
+ }
+ if (hereis)
+ str_free(herewas);
+ return s;
+}
+
+FCMD *
+load_format()
+{
+ FCMD froot;
+ FCMD *flinebeg;
+ char *eol;
+ register FCMD *fprev = &froot;
+ register FCMD *fcmd;
+ register char *s;
+ register char *t;
+ register STR *str;
+ bool noblank;
+ bool repeater;
+
+ Zero(&froot, 1, FCMD);
+ s = bufptr;
+ while (s < bufend || (rsfp && (s = str_gets(linestr,rsfp, 0)) != Nullch)) {
+ curcmd->c_line++;
+ if (in_eval && !rsfp) {
+ eol = index(s,'\n');
+ if (!eol++)
+ eol = bufend;
+ }
+ else
+ eol = bufend = linestr->str_ptr + linestr->str_cur;
+ if (perldb) {
+ STR *tmpstr = Str_new(89,0);
+
+ str_nset(tmpstr, s, eol-s);
+ astore(stab_xarray(curcmd->c_filestab), (int)curcmd->c_line,tmpstr);
+ }
+ if (*s == '.') {
+ /*SUPPRESS 530*/
+ for (t = s+1; *t == ' ' || *t == '\t'; t++) ;
+ if (*t == '\n') {
+ bufptr = s;
+ return froot.f_next;
+ }
+ }
+ if (*s == '#') {
+ s = eol;
+ continue;
+ }
+ flinebeg = Nullfcmd;
+ noblank = FALSE;
+ repeater = FALSE;
+ while (s < eol) {
+ Newz(804,fcmd,1,FCMD);
+ fprev->f_next = fcmd;
+ fprev = fcmd;
+ for (t=s; t < eol && *t != '@' && *t != '^'; t++) {
+ if (*t == '~') {
+ noblank = TRUE;
+ *t = ' ';
+ if (t[1] == '~') {
+ repeater = TRUE;
+ t[1] = ' ';
+ }
+ }
+ }
+ fcmd->f_pre = nsavestr(s, t-s);
+ fcmd->f_presize = t-s;
+ s = t;
+ if (s >= eol) {
+ if (noblank)
+ fcmd->f_flags |= FC_NOBLANK;
+ if (repeater)
+ fcmd->f_flags |= FC_REPEAT;
+ break;
+ }
+ if (!flinebeg)
+ flinebeg = fcmd; /* start values here */
+ if (*s++ == '^')
+ fcmd->f_flags |= FC_CHOP; /* for doing text filling */
+ switch (*s) {
+ case '*':
+ fcmd->f_type = F_LINES;
+ *s = '\0';
+ break;
+ case '<':
+ fcmd->f_type = F_LEFT;
+ while (*s == '<')
+ s++;
+ break;
+ case '>':
+ fcmd->f_type = F_RIGHT;
+ while (*s == '>')
+ s++;
+ break;
+ case '|':
+ fcmd->f_type = F_CENTER;
+ while (*s == '|')
+ s++;
+ break;
+ case '#':
+ case '.':
+ /* Catch the special case @... and handle it as a string
+ field. */
+ if (*s == '.' && s[1] == '.') {
+ goto default_format;
+ }
+ fcmd->f_type = F_DECIMAL;
+ {
+ char *p;
+
+ /* Read a format in the form @####.####, where either group
+ of ### may be empty, or the final .### may be missing. */
+ while (*s == '#')
+ s++;
+ if (*s == '.') {
+ s++;
+ p = s;
+ while (*s == '#')
+ s++;
+ fcmd->f_decimals = s-p;
+ fcmd->f_flags |= FC_DP;
+ } else {
+ fcmd->f_decimals = 0;
+ }
+ }
+ break;
+ default:
+ default_format:
+ fcmd->f_type = F_LEFT;
+ break;
+ }
+ if (fcmd->f_flags & FC_CHOP && *s == '.') {
+ fcmd->f_flags |= FC_MORE;
+ while (*s == '.')
+ s++;
+ }
+ fcmd->f_size = s-t;
+ }
+ if (flinebeg) {
+ again:
+ if (s >= bufend &&
+ (!rsfp || (s = str_gets(linestr, rsfp, 0)) == Nullch) )
+ goto badform;
+ curcmd->c_line++;
+ if (in_eval && !rsfp) {
+ eol = index(s,'\n');
+ if (!eol++)
+ eol = bufend;
+ }
+ else
+ eol = bufend = linestr->str_ptr + linestr->str_cur;
+ if (perldb) {
+ STR *tmpstr = Str_new(90,0);
+
+ str_nset(tmpstr, s, eol-s);
+ astore(stab_xarray(curcmd->c_filestab),
+ (int)curcmd->c_line,tmpstr);
+ }
+ if (strnEQ(s,".\n",2)) {
+ bufptr = s;
+ yyerror("Missing values line");
+ return froot.f_next;
+ }
+ if (*s == '#') {
+ s = eol;
+ goto again;
+ }
+ str = flinebeg->f_unparsed = Str_new(91,eol - s);
+ str->str_u.str_hash = curstash;
+ str_nset(str,"(",1);
+ flinebeg->f_line = curcmd->c_line;
+ eol[-1] = '\0';
+ if (!flinebeg->f_next->f_type || index(s, ',')) {
+ eol[-1] = '\n';
+ str_ncat(str, s, eol - s - 1);
+ str_ncat(str,",$$);",5);
+ s = eol;
+ }
+ else {
+ eol[-1] = '\n';
+ while (s < eol && isSPACE(*s))
+ s++;
+ t = s;
+ while (s < eol) {
+ switch (*s) {
+ case ' ': case '\t': case '\n': case ';':
+ str_ncat(str, t, s - t);
+ str_ncat(str, "," ,1);
+ while (s < eol && (isSPACE(*s) || *s == ';'))
+ s++;
+ t = s;
+ break;
+ case '$':
+ str_ncat(str, t, s - t);
+ t = s;
+ s = scanident(s,eol,tokenbuf);
+ str_ncat(str, t, s - t);
+ t = s;
+ if (s < eol && *s && index("$'\"",*s))
+ str_ncat(str, ",", 1);
+ break;
+ case '"': case '\'':
+ str_ncat(str, t, s - t);
+ t = s;
+ s++;
+ while (s < eol && (*s != *t || s[-1] == '\\'))
+ s++;
+ if (s < eol)
+ s++;
+ str_ncat(str, t, s - t);
+ t = s;
+ if (s < eol && *s && index("$'\"",*s))
+ str_ncat(str, ",", 1);
+ break;
+ default:
+ yyerror("Please use commas to separate fields");
+ }
+ }
+ str_ncat(str,"$$);",4);
+ }
+ }
+ }
+ badform:
+ bufptr = str_get(linestr);
+ yyerror("Format not terminated");
+ return froot.f_next;
+}
+
+static void
+set_csh()
+{
+#ifdef CSH
+ if (!cshlen)
+ cshlen = strlen(cshname);
+#endif
+}
diff --git a/gnu/usr.bin/perl/perl/usersub.c b/gnu/usr.bin/perl/perl/usersub.c
new file mode 100644
index 0000000..d0ee952
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/usersub.c
@@ -0,0 +1,148 @@
+/* $RCSfile: usersub.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:40 $
+ *
+ * This file contains stubs for routines that the user may define to
+ * set up glue routines for C libraries or to decrypt encrypted scripts
+ * for execution.
+ *
+ * $Log: usersub.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:40 nate
+ * PERL!
+ *
+ * Revision 4.0.1.2 92/06/08 16:04:24 lwall
+ * patch20: removed implicit int declarations on functions
+ *
+ * Revision 4.0.1.1 91/11/11 16:47:17 lwall
+ * patch19: deleted some unused functions from usersub.c
+ *
+ * Revision 4.0 91/03/20 01:55:56 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+
+int
+userinit()
+{
+ return 0;
+}
+
+/*
+ * The following is supplied by John Macdonald as a means of decrypting
+ * and executing (presumably proprietary) scripts that have been encrypted
+ * by a (presumably secret) method. The idea is that you supply your own
+ * routine in place of cryptfilter (which is purposefully a very weak
+ * encryption). If an encrypted script is detected, a process is forked
+ * off to run the cryptfilter routine as input to perl.
+ */
+
+#ifdef CRYPTSCRIPT
+
+#include <signal.h>
+#ifdef I_VFORK
+#include <vfork.h>
+#endif
+
+#ifdef CRYPTLOCAL
+
+#include "cryptlocal.h"
+
+#else /* ndef CRYPTLOCAL */
+
+#define CRYPT_MAGIC_1 0xfb
+#define CRYPT_MAGIC_2 0xf1
+
+void
+cryptfilter( fil )
+FILE * fil;
+{
+ int ch;
+
+ while( (ch = getc( fil )) != EOF ) {
+ putchar( (ch ^ 0x80) );
+ }
+}
+
+#endif /* CRYPTLOCAL */
+
+#ifndef MSDOS
+static FILE *lastpipefile;
+static int pipepid;
+
+#ifdef VOIDSIG
+# define VOID void
+#else
+# define VOID int
+#endif
+
+FILE *
+mypfiopen(fil,func) /* open a pipe to function call for input */
+FILE *fil;
+VOID (*func)();
+{
+ int p[2];
+ STR *str;
+
+ if (pipe(p) < 0) {
+ fclose( fil );
+ fatal("Can't get pipe for decrypt");
+ }
+
+ /* make sure that the child doesn't get anything extra */
+ fflush(stdout);
+ fflush(stderr);
+
+ while ((pipepid = fork()) < 0) {
+ if (errno != EAGAIN) {
+ close(p[0]);
+ close(p[1]);
+ fclose( fil );
+ fatal("Can't fork for decrypt");
+ }
+ sleep(5);
+ }
+ if (pipepid == 0) {
+ close(p[0]);
+ if (p[1] != 1) {
+ dup2(p[1], 1);
+ close(p[1]);
+ }
+ (*func)(fil);
+ fflush(stdout);
+ fflush(stderr);
+ _exit(0);
+ }
+ close(p[1]);
+ close(fileno(fil));
+ fclose(fil);
+ str = afetch(fdpid,p[0],TRUE);
+ str->str_u.str_useful = pipepid;
+ return fdopen(p[0], "r");
+}
+
+void
+cryptswitch()
+{
+ int ch;
+#ifdef STDSTDIO
+ /* cheat on stdio if possible */
+ if (rsfp->_cnt > 0 && (*rsfp->_ptr & 0xff) != CRYPT_MAGIC_1)
+ return;
+#endif
+ ch = getc(rsfp);
+ if (ch == CRYPT_MAGIC_1) {
+ if (getc(rsfp) == CRYPT_MAGIC_2) {
+ if( perldb ) fatal("can't debug an encrypted script");
+ rsfp = mypfiopen( rsfp, cryptfilter );
+ preprocess = 1; /* force call to pclose when done */
+ }
+ else
+ fatal( "bad encryption format" );
+ }
+ else
+ ungetc(ch,rsfp);
+}
+#endif /* !MSDOS */
+
+#endif /* CRYPTSCRIPT */
diff --git a/gnu/usr.bin/perl/perl/usub/Makefile b/gnu/usr.bin/perl/perl/usub/Makefile
new file mode 100644
index 0000000..965a98a
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/usub/Makefile
@@ -0,0 +1,16 @@
+SRC = ..
+GLOBINCS =
+LOCINCS =
+LIBS = -lcurses -ltermcap `. $(SRC)/config.sh; echo $$libs`
+
+curseperl: $(SRC)/uperl.o usersub.o curses.o
+ cc $(SRC)/uperl.o usersub.o curses.o $(LIBS) -o curseperl
+
+usersub.o: usersub.c
+ cc -c -I$(SRC) $(GLOBINCS) -DDEBUGGING -g usersub.c
+
+curses.o: curses.c
+ cc -c -I$(SRC) $(GLOBINCS) -DDEBUGGING -g curses.c
+
+curses.c: curses.mus
+ mus curses.mus >curses.c
diff --git a/gnu/usr.bin/perl/perl/usub/README b/gnu/usr.bin/perl/perl/usub/README
new file mode 100644
index 0000000..a80a650
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/usub/README
@@ -0,0 +1,114 @@
+This directory contains an example of how you might link in C subroutines
+with perl to make your own special copy of perl. In the perl distribution
+directory, there will be (after make is run) a file called uperl.o, which
+is all of perl except for a single undefined subroutine, named userinit().
+See usersub.c.
+
+The sole purpose of the userinit() routine is to call the initialization
+routines for any modules that you want to link in. In this example, we just
+call init_curses(), which sets up to link in the System V curses routines.
+You'll find this in the file curses.c, which is the processed output of
+curses.mus. (To get BSD curses, replace curses.mus with bsdcurses.mus.)
+
+The magicname() routine adds variable names into the symbol table. Along
+with the name of the variable as Perl knows it, we pass a structure containing
+an index identifying the variable, and the names of two C functions that
+know how to set or evaluate a variable given the index of the variable.
+Our example uses a macro to handle this conveniently.
+
+The init routine calls make_usub() to add user-defined subroutine names
+into the symbol table. The arguments are
+
+ make_usub(subname, subindex, subfunc, filename);
+ char *subname;
+ int subindex;
+ int subfunc();
+ char *filename;
+
+The subname is the name that will be used in the Perl program. The subindex
+will be passed to subfunc() when it is called to tell it which C function
+is desired. subfunc() is a glue routine that translates the arguments
+from Perl internal stack form to the form required by the routine in
+question, calls the desired C function, and then translates any return
+value back into the stack format. The glue routine used by curses just
+has a large switch statement, each branch of which does the processing
+for a particular C function. The subindex could, however, be used to look
+up a function in a dynamically linked library. No example of this is
+provided.
+
+As a help in producing the glue routine, a preprocessor called "mus" lets
+you specify argument and return value types in a tabular format. An entry
+such as:
+
+ CASE int waddstr
+ I WINDOW* win
+ I char* str
+ END
+
+indicates that waddstr takes two input arguments, the first of which is a
+pointer to a window, and the second of which is an ordinary C string. It
+also indicates that an integer is returned. The mus program turns this into:
+
+ case US_waddstr:
+ if (items != 2)
+ fatal("Usage: &waddstr($win, $str)");
+ else {
+ int retval;
+ WINDOW* win = *(WINDOW**) str_get(st[1]);
+ char* str = (char*) str_get(st[2]);
+
+ retval = waddstr(win, str);
+ str_numset(st[0], (double) retval);
+ }
+ return sp;
+
+It's also possible to have output parameters, indicated by O, and input/ouput
+parameters indicated by IO.
+
+The mus program isn't perfect. You'll note that curses.mus has some
+cases which are hand coded. They'll be passed straight through unmodified.
+You can produce similar cases by analogy to what's in curses.c, as well
+as similar routines in the doarg.c, dolist.c and doio.c routines of Perl.
+The mus program is only intended to get you about 90% there. It's not clear,
+for instance, how a given structure should be passed to Perl. But that
+shouldn't bother you--if you've gotten this far, it's already obvious
+that you are totally mad.
+
+Here's an example of how to return an array value:
+
+ case US_appl_errlist:
+ if (!wantarray) {
+ str_numset(st[0], (double) appl_nerr);
+ return sp;
+ }
+ astore(stack, sp + appl_nerr, Nullstr); /* extend stack */
+ st = stack->ary_array + sp; /* possibly realloced */
+ for (i = 0; i < appl_nerr; i++) {
+ tmps = appl_errlist[i];
+ st[i] = str_2mortal(str_make(tmps,strlen(tmps)));
+ }
+ return sp + appl_nerr - 1;
+
+
+In addition, there is a program, man2mus, that will scan a man page for
+function prototypes and attempt to construct a mus CASE entry for you. It has
+to guess about input/output parameters, so you'll have to tidy up after it.
+But it can save you a lot of time if the man pages for a library are
+reasonably well formed.
+
+If you happen to have curses on your machine, you might try compiling
+a copy of curseperl. The "pager" program in this directory is a rudimentary
+start on writing a pager--don't believe the help message, which is stolen
+from the less program.
+
+User-defined subroutines may not currently be called as a signal handler,
+though a signal handler may itself call a user-defined subroutine.
+
+There are now glue routines to call back from C into Perl. In usersub.c
+in this directory, you'll find callback() and callv(). The callback()
+routine presumes that any arguments to pass to the Perl subroutine
+have already been pushed onto the Perl stack. The callv() routine
+is a wrapper that pushes an argv-style array of strings onto the
+stack for you, and then calls callback(). Be sure to recheck your
+stack pointer after returning from these routine, since the Perl code
+may have reallocated it.
diff --git a/gnu/usr.bin/perl/perl/usub/bsdcurses.mus b/gnu/usr.bin/perl/perl/usub/bsdcurses.mus
new file mode 100644
index 0000000..1a1f11b
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/usub/bsdcurses.mus
@@ -0,0 +1,699 @@
+/* $RCSfile: bsdcurses.mus,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:07 $
+ *
+ * $Log: bsdcurses.mus,v $
+# Revision 1.1.1.1 1993/08/23 21:30:07 nate
+# PERL!
+#
+ * Revision 4.0.1.2 92/06/08 16:05:28 lwall
+ * patch20: &getcap eventually dumped core in bsdcurses
+ *
+ * Revision 4.0.1.1 91/11/05 19:04:53 lwall
+ * initial checkin
+ *
+ * Revision 4.0 91/03/20 01:56:13 lwall
+ * 4.0 baseline.
+ *
+ * Revision 3.0.1.1 90/08/09 04:05:21 lwall
+ * patch19: Initial revision
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+
+char *savestr();
+
+#include <curses.h>
+
+static enum uservars {
+ UV_curscr,
+ UV_stdscr,
+ UV_Def_term,
+ UV_My_term,
+ UV_ttytype,
+ UV_LINES,
+ UV_COLS,
+ UV_ERR,
+ UV_OK,
+};
+
+static enum usersubs {
+ US_addch,
+ US_waddch,
+ US_addstr,
+ US_waddstr,
+ US_box,
+ US_clear,
+ US_wclear,
+ US_clearok,
+ US_clrtobot,
+ US_wclrtobot,
+ US_clrtoeol,
+ US_wclrtoeol,
+ US_delch,
+ US_wdelch,
+ US_deleteln,
+ US_wdeleteln,
+ US_erase,
+ US_werase,
+ US_flushok,
+ US_idlok,
+ US_insch,
+ US_winsch,
+ US_insertln,
+ US_winsertln,
+ US_move,
+ US_wmove,
+ US_overlay,
+ US_overwrite,
+ US_printw,
+ US_wprintw,
+ US_refresh,
+ US_wrefresh,
+ US_standout,
+ US_wstandout,
+ US_standend,
+ US_wstandend,
+ US_cbreak,
+ US_nocbreak,
+ US_echo,
+ US_noecho,
+ US_getch,
+ US_wgetch,
+ US_getstr,
+ US_wgetstr,
+ US_raw,
+ US_noraw,
+ US_scanw,
+ US_wscanw,
+ US_baudrate,
+ US_delwin,
+ US_endwin,
+ US_erasechar,
+ US_getcap,
+ US_getyx,
+ US_inch,
+ US_winch,
+ US_initscr,
+ US_killchar,
+ US_leaveok,
+ US_longname,
+ US_fullname,
+ US_mvwin,
+ US_newwin,
+ US_nl,
+ US_nonl,
+ US_scrollok,
+ US_subwin,
+ US_touchline,
+ US_touchoverlap,
+ US_touchwin,
+ US_unctrl,
+ US_gettmode,
+ US_mvcur,
+ US_scroll,
+ US_savetty,
+ US_resetty,
+ US_setterm,
+ US_tstp,
+ US__putchar,
+ US_testcallback,
+};
+
+static int usersub();
+static int userset();
+static int userval();
+
+int
+init_curses()
+{
+ struct ufuncs uf;
+ char *filename = "curses.c";
+
+ uf.uf_set = userset;
+ uf.uf_val = userval;
+
+#define MAGICVAR(name, ix) uf.uf_index = ix, magicname(name, &uf, sizeof uf)
+
+ MAGICVAR("curscr", UV_curscr);
+ MAGICVAR("stdscr", UV_stdscr);
+ MAGICVAR("Def_term",UV_Def_term);
+ MAGICVAR("My_term", UV_My_term);
+ MAGICVAR("ttytype", UV_ttytype);
+ MAGICVAR("LINES", UV_LINES);
+ MAGICVAR("COLS", UV_COLS);
+ MAGICVAR("ERR", UV_ERR);
+ MAGICVAR("OK", UV_OK);
+
+ make_usub("addch", US_addch, usersub, filename);
+ make_usub("waddch", US_waddch, usersub, filename);
+ make_usub("addstr", US_addstr, usersub, filename);
+ make_usub("waddstr", US_waddstr, usersub, filename);
+ make_usub("box", US_box, usersub, filename);
+ make_usub("clear", US_clear, usersub, filename);
+ make_usub("wclear", US_wclear, usersub, filename);
+ make_usub("clearok", US_clearok, usersub, filename);
+ make_usub("clrtobot", US_clrtobot, usersub, filename);
+ make_usub("wclrtobot", US_wclrtobot, usersub, filename);
+ make_usub("clrtoeol", US_clrtoeol, usersub, filename);
+ make_usub("wclrtoeol", US_wclrtoeol, usersub, filename);
+ make_usub("delch", US_delch, usersub, filename);
+ make_usub("wdelch", US_wdelch, usersub, filename);
+ make_usub("deleteln", US_deleteln, usersub, filename);
+ make_usub("wdeleteln", US_wdeleteln, usersub, filename);
+ make_usub("erase", US_erase, usersub, filename);
+ make_usub("werase", US_werase, usersub, filename);
+ make_usub("flushok", US_flushok, usersub, filename);
+ make_usub("idlok", US_idlok, usersub, filename);
+ make_usub("insch", US_insch, usersub, filename);
+ make_usub("winsch", US_winsch, usersub, filename);
+ make_usub("insertln", US_insertln, usersub, filename);
+ make_usub("winsertln", US_winsertln, usersub, filename);
+ make_usub("move", US_move, usersub, filename);
+ make_usub("wmove", US_wmove, usersub, filename);
+ make_usub("overlay", US_overlay, usersub, filename);
+ make_usub("overwrite", US_overwrite, usersub, filename);
+ make_usub("printw", US_printw, usersub, filename);
+ make_usub("wprintw", US_wprintw, usersub, filename);
+ make_usub("refresh", US_refresh, usersub, filename);
+ make_usub("wrefresh", US_wrefresh, usersub, filename);
+ make_usub("standout", US_standout, usersub, filename);
+ make_usub("wstandout", US_wstandout, usersub, filename);
+ make_usub("standend", US_standend, usersub, filename);
+ make_usub("wstandend", US_wstandend, usersub, filename);
+ make_usub("cbreak", US_cbreak, usersub, filename);
+ make_usub("nocbreak", US_nocbreak, usersub, filename);
+ make_usub("echo", US_echo, usersub, filename);
+ make_usub("noecho", US_noecho, usersub, filename);
+ make_usub("getch", US_getch, usersub, filename);
+ make_usub("wgetch", US_wgetch, usersub, filename);
+ make_usub("getstr", US_getstr, usersub, filename);
+ make_usub("wgetstr", US_wgetstr, usersub, filename);
+ make_usub("raw", US_raw, usersub, filename);
+ make_usub("noraw", US_noraw, usersub, filename);
+ make_usub("scanw", US_scanw, usersub, filename);
+ make_usub("wscanw", US_wscanw, usersub, filename);
+ make_usub("baudrate", US_baudrate, usersub, filename);
+ make_usub("delwin", US_delwin, usersub, filename);
+ make_usub("endwin", US_endwin, usersub, filename);
+ make_usub("erasechar", US_erasechar, usersub, filename);
+ make_usub("getcap", US_getcap, usersub, filename);
+ make_usub("getyx", US_getyx, usersub, filename);
+ make_usub("inch", US_inch, usersub, filename);
+ make_usub("winch", US_winch, usersub, filename);
+ make_usub("initscr", US_initscr, usersub, filename);
+ make_usub("killchar", US_killchar, usersub, filename);
+ make_usub("leaveok", US_leaveok, usersub, filename);
+ make_usub("longname", US_longname, usersub, filename);
+ make_usub("fullname", US_fullname, usersub, filename);
+ make_usub("mvwin", US_mvwin, usersub, filename);
+ make_usub("newwin", US_newwin, usersub, filename);
+ make_usub("nl", US_nl, usersub, filename);
+ make_usub("nonl", US_nonl, usersub, filename);
+ make_usub("scrollok", US_scrollok, usersub, filename);
+ make_usub("subwin", US_subwin, usersub, filename);
+ make_usub("touchline", US_touchline, usersub, filename);
+ make_usub("touchoverlap", US_touchoverlap,usersub, filename);
+ make_usub("touchwin", US_touchwin, usersub, filename);
+ make_usub("unctrl", US_unctrl, usersub, filename);
+ make_usub("gettmode", US_gettmode, usersub, filename);
+ make_usub("mvcur", US_mvcur, usersub, filename);
+ make_usub("scroll", US_scroll, usersub, filename);
+ make_usub("savetty", US_savetty, usersub, filename);
+ make_usub("resetty", US_resetty, usersub, filename);
+ make_usub("setterm", US_setterm, usersub, filename);
+ make_usub("tstp", US_tstp, usersub, filename);
+ make_usub("_putchar", US__putchar, usersub, filename);
+ make_usub("testcallback", US_testcallback,usersub, filename);
+};
+
+static int
+usersub(ix, sp, items)
+int ix;
+register int sp;
+register int items;
+{
+ STR **st = stack->ary_array + sp;
+ register int i;
+ register char *tmps;
+ register STR *Str; /* used in str_get and str_gnum macros */
+
+ switch (ix) {
+CASE int addch
+I char ch
+END
+
+CASE int waddch
+I WINDOW* win
+I char ch
+END
+
+CASE int addstr
+I char* str
+END
+
+CASE int waddstr
+I WINDOW* win
+I char* str
+END
+
+CASE int box
+I WINDOW* win
+I char vert
+I char hor
+END
+
+CASE int clear
+END
+
+CASE int wclear
+I WINDOW* win
+END
+
+CASE int clearok
+I WINDOW* win
+I bool boolf
+END
+
+CASE int clrtobot
+END
+
+CASE int wclrtobot
+I WINDOW* win
+END
+
+CASE int clrtoeol
+END
+
+CASE int wclrtoeol
+I WINDOW* win
+END
+
+CASE int delch
+END
+
+CASE int wdelch
+I WINDOW* win
+END
+
+CASE int deleteln
+END
+
+CASE int wdeleteln
+I WINDOW* win
+END
+
+CASE int erase
+END
+
+CASE int werase
+I WINDOW* win
+END
+
+CASE int flushok
+I WINDOW* win
+I bool boolf
+END
+
+CASE int idlok
+I WINDOW* win
+I bool boolf
+END
+
+CASE int insch
+I char c
+END
+
+CASE int winsch
+I WINDOW* win
+I char c
+END
+
+CASE int insertln
+END
+
+CASE int winsertln
+I WINDOW* win
+END
+
+CASE int move
+I int y
+I int x
+END
+
+CASE int wmove
+I WINDOW* win
+I int y
+I int x
+END
+
+CASE int overlay
+I WINDOW* win1
+I WINDOW* win2
+END
+
+CASE int overwrite
+I WINDOW* win1
+I WINDOW* win2
+END
+
+ case US_printw:
+ if (items < 1)
+ fatal("Usage: &printw($fmt, $arg1, $arg2, ... )");
+ else {
+ int retval;
+ STR* str = str_new(0);
+
+ do_sprintf(str, items - 1, st + 1);
+ retval = addstr(str->str_ptr);
+ str_numset(st[0], (double) retval);
+ str_free(str);
+ }
+ return sp;
+
+ case US_wprintw:
+ if (items < 2)
+ fatal("Usage: &wprintw($win, $fmt, $arg1, $arg2, ... )");
+ else {
+ int retval;
+ STR* str = str_new(0);
+ WINDOW* win = *(WINDOW**) str_get(st[1]);
+
+ do_sprintf(str, items - 1, st + 1);
+ retval = waddstr(win, str->str_ptr);
+ str_numset(st[0], (double) retval);
+ str_free(str);
+ }
+ return sp;
+
+CASE int refresh
+END
+
+CASE int wrefresh
+I WINDOW* win
+END
+
+CASE int standout
+END
+
+CASE int wstandout
+I WINDOW* win
+END
+
+CASE int standend
+END
+
+CASE int wstandend
+I WINDOW* win
+END
+
+CASE int cbreak
+END
+
+CASE int nocbreak
+END
+
+CASE int echo
+END
+
+CASE int noecho
+END
+
+ case US_getch:
+ if (items != 0)
+ fatal("Usage: &getch()");
+ else {
+ int retval;
+ char retch;
+
+ retval = getch();
+ if (retval == EOF)
+ st[0] = &str_undef;
+ else {
+ retch = retval;
+ str_nset(st[0], &retch, 1);
+ }
+ }
+ return sp;
+
+ case US_wgetch:
+ if (items != 1)
+ fatal("Usage: &wgetch($win)");
+ else {
+ int retval;
+ char retch;
+ WINDOW* win = *(WINDOW**) str_get(st[1]);
+
+ retval = wgetch(win);
+ if (retval == EOF)
+ st[0] = &str_undef;
+ else {
+ retch = retval;
+ str_nset(st[0], &retch, 1);
+ }
+ }
+ return sp;
+
+CASE int getstr
+IO char* str
+END
+
+CASE int wgetstr
+I WINDOW* win
+IO char* str
+END
+
+CASE int raw
+END
+
+CASE int noraw
+END
+
+CASE int baudrate
+END
+
+CASE int delwin
+I WINDOW* win
+END
+
+CASE int endwin
+END
+
+CASE int erasechar
+END
+
+ case US_getcap:
+ if (items != 1)
+ fatal("Usage: &getcap($str)");
+ else {
+ char* retval;
+ char* str = (char*) str_get(st[1]);
+ char output[50], *outputp = output;
+
+ retval = tgetstr(str, &outputp);
+ str_set(st[0], (char*) retval);
+ }
+ return sp;
+
+ case US_getyx:
+ if (items != 3)
+ fatal("Usage: &getyx($win, $y, $x)");
+ else {
+ int retval;
+ STR* str = str_new(0);
+ WINDOW* win = *(WINDOW**) str_get(st[1]);
+ int y;
+ int x;
+
+ do_sprintf(str, items - 1, st + 1);
+ retval = getyx(win, y, x);
+ str_numset(st[2], (double)y);
+ str_numset(st[3], (double)x);
+ str_numset(st[0], (double) retval);
+ str_free(str);
+ }
+ return sp;
+
+
+CASE int inch
+END
+
+CASE int winch
+I WINDOW* win
+END
+
+CASE WINDOW* initscr
+END
+
+CASE int killchar
+END
+
+CASE int leaveok
+I WINDOW* win
+I bool boolf
+END
+
+CASE char* longname
+I char* termbuf
+IO char* name
+END
+
+CASE int fullname
+I char* termbuf
+IO char* name
+END
+
+CASE int mvwin
+I WINDOW* win
+I int y
+I int x
+END
+
+CASE WINDOW* newwin
+I int lines
+I int cols
+I int begin_y
+I int begin_x
+END
+
+CASE int nl
+END
+
+CASE int nonl
+END
+
+CASE int scrollok
+I WINDOW* win
+I bool boolf
+END
+
+CASE WINDOW* subwin
+I WINDOW* win
+I int lines
+I int cols
+I int begin_y
+I int begin_x
+END
+
+CASE int touchline
+I WINDOW* win
+I int y
+I int startx
+I int endx
+END
+
+CASE int touchoverlap
+I WINDOW* win1
+I WINDOW* win2
+END
+
+CASE int touchwin
+I WINDOW* win
+END
+
+CASE char* unctrl
+I char ch
+END
+
+CASE int gettmode
+END
+
+CASE int mvcur
+I int lasty
+I int lastx
+I int newy
+I int newx
+END
+
+CASE int scroll
+I WINDOW* win
+END
+
+CASE int savetty
+END
+
+CASE void resetty
+END
+
+CASE int setterm
+I char* name
+END
+
+CASE int tstp
+END
+
+CASE int _putchar
+I char ch
+END
+
+ case US_testcallback:
+ sp = callback("callback", sp + items, curcsv->wantarray, 1, items);
+ break;
+
+ default:
+ fatal("Unimplemented user-defined subroutine");
+ }
+ return sp;
+}
+
+static int
+userval(ix, str)
+int ix;
+STR *str;
+{
+ switch (ix) {
+ case UV_COLS:
+ str_numset(str, (double)COLS);
+ break;
+ case UV_Def_term:
+ str_set(str, Def_term);
+ break;
+ case UV_ERR:
+ str_numset(str, (double)ERR);
+ break;
+ case UV_LINES:
+ str_numset(str, (double)LINES);
+ break;
+ case UV_My_term:
+ str_numset(str, (double)My_term);
+ break;
+ case UV_OK:
+ str_numset(str, (double)OK);
+ break;
+ case UV_curscr:
+ str_nset(str, &curscr, sizeof(WINDOW*));
+ break;
+ case UV_stdscr:
+ str_nset(str, &stdscr, sizeof(WINDOW*));
+ break;
+ case UV_ttytype:
+ str_set(str, ttytype);
+ break;
+ }
+ return 0;
+}
+
+static int
+userset(ix, str)
+int ix;
+STR *str;
+{
+ switch (ix) {
+ case UV_COLS:
+ COLS = (int)str_gnum(str);
+ break;
+ case UV_Def_term:
+ Def_term = savestr(str_get(str)); /* never freed */
+ break;
+ case UV_LINES:
+ LINES = (int)str_gnum(str);
+ break;
+ case UV_My_term:
+ My_term = (bool)str_gnum(str);
+ break;
+ case UV_ttytype:
+ strcpy(ttytype, str_get(str)); /* hope it fits */
+ break;
+ }
+ return 0;
+}
diff --git a/gnu/usr.bin/perl/perl/usub/curses.mus b/gnu/usr.bin/perl/perl/usub/curses.mus
new file mode 100644
index 0000000..f305bf5
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/usub/curses.mus
@@ -0,0 +1,890 @@
+/* $RCSfile: curses.mus,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:07 $
+ *
+ * $Log: curses.mus,v $
+# Revision 1.1.1.1 1993/08/23 21:30:07 nate
+# PERL!
+#
+ * Revision 4.0.1.2 92/06/08 16:06:12 lwall
+ * patch20: function key support added to curses.mus
+ *
+ * Revision 4.0.1.1 91/11/05 19:06:19 lwall
+ * patch11: usub/curses.mus now supports SysV curses
+ *
+ * Revision 4.0 91/03/20 01:56:13 lwall
+ * 4.0 baseline.
+ *
+ * Revision 3.0.1.1 90/08/09 04:05:21 lwall
+ * patch19: Initial revision
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+
+char *savestr();
+
+#undef bool
+#include <curses.h>
+
+#ifndef A_UNDERLINE
+#define NOSETATTR
+#define A_STANDOUT 0x0200
+#define A_UNDERLINE 0x0100
+#define A_REVERSE 0x0200
+#define A_BLINK 0x0400
+#define A_BOLD 0x0800
+#define A_ALTCHARSET 0x1000
+#define A_NORMAL 0
+#endif
+
+#ifdef USG
+static char *tcbuf = NULL;
+#endif
+
+#ifdef NOSETATTR
+static unsigned curattr = NORMAL;
+#endif
+
+static enum uservars {
+ UV_curscr,
+ UV_stdscr,
+ UV_ttytype,
+ UV_LINES,
+ UV_COLS,
+ UV_ERR,
+ UV_OK,
+#ifdef BSD
+ UV_Def_term,
+ UV_My_term,
+#endif
+ UV_A_STANDOUT,
+ UV_A_UNDERLINE,
+ UV_A_REVERSE,
+ UV_A_BLINK,
+ UV_A_DIM,
+ UV_A_BOLD,
+ UV_A_NORMAL,
+};
+
+static enum usersubs {
+ US_addch,
+ US_waddch,
+ US_addstr,
+ US_waddstr,
+ US_box,
+ US_clear,
+ US_wclear,
+ US_clearok,
+ US_clrtobot,
+ US_wclrtobot,
+ US_clrtoeol,
+ US_wclrtoeol,
+ US_delch,
+ US_wdelch,
+ US_deleteln,
+ US_wdeleteln,
+ US_erase,
+ US_werase,
+ US_idlok,
+ US_insch,
+ US_winsch,
+ US_insertln,
+ US_winsertln,
+ US_move,
+ US_wmove,
+ US_overlay,
+ US_overwrite,
+ US_refresh,
+ US_wrefresh,
+ US_standout,
+ US_wstandout,
+ US_standend,
+ US_wstandend,
+ US_cbreak,
+ US_nocbreak,
+ US_echo,
+ US_noecho,
+ US_getch,
+ US_wgetch,
+ US_getstr,
+ US_wgetstr,
+ US_raw,
+ US_noraw,
+ US_baudrate,
+ US_delwin,
+ US_endwin,
+ US_erasechar,
+ US_getyx,
+ US_inch,
+ US_winch,
+ US_initscr,
+ US_killchar,
+ US_leaveok,
+ US_longname,
+ US_mvwin,
+ US_newwin,
+ US_nl,
+ US_nonl,
+ US_scrollok,
+ US_subwin,
+ US_touchline,
+ US_touchwin,
+ US_unctrl,
+ US_gettmode,
+ US_mvcur,
+ US_scroll,
+ US_savetty,
+ US_resetty,
+ US_setterm,
+ US_attroff,
+ US_wattroff,
+ US_attron,
+ US_wattron,
+ US_attrset,
+ US_wattrset,
+#ifdef CURSEFMT
+ US_printw, /* remove */
+ US_wprintw, /* remove */
+ US_scanw, /* delete */
+ US_wscanw, /* delete */
+#endif
+ US_getcap,
+#ifdef BSD
+ US_flushok,
+ US_fullname,
+ US_touchoverlap,
+ US_tstp,
+ US__putchar,
+#endif
+ US_mysub,
+ US_testcallback,
+};
+
+static int usersub();
+static int userset();
+static int userval();
+
+int
+init_curses()
+{
+ struct ufuncs uf;
+ char *filename = "curses.c";
+
+ uf.uf_set = userset;
+ uf.uf_val = userval;
+
+#define MAGICVAR(name, ix) uf.uf_index = ix, magicname(name, &uf, sizeof uf)
+
+ MAGICVAR("curscr", UV_curscr);
+ MAGICVAR("stdscr", UV_stdscr);
+ MAGICVAR("ttytype", UV_ttytype);
+ MAGICVAR("LINES", UV_LINES);
+ MAGICVAR("COLS", UV_COLS);
+ MAGICVAR("ERR", UV_ERR);
+ MAGICVAR("OK", UV_OK);
+#ifdef BSD
+ MAGICVAR("Def_term",UV_Def_term);
+ MAGICVAR("My_term", UV_My_term);
+#endif
+ MAGICVAR("A_STANDOUT", UV_A_STANDOUT);
+ MAGICVAR("A_UNDERLINE", UV_A_UNDERLINE);
+ MAGICVAR("A_REVERSE", UV_A_REVERSE);
+ MAGICVAR("A_BLINK", UV_A_BLINK);
+ MAGICVAR("A_DIM", UV_A_DIM);
+ MAGICVAR("A_BOLD", UV_A_BOLD);
+ MAGICVAR("A_NORMAL", UV_A_NORMAL);
+
+ make_usub("addch", US_addch, usersub, filename);
+ make_usub("waddch", US_waddch, usersub, filename);
+ make_usub("addstr", US_addstr, usersub, filename);
+ make_usub("waddstr", US_waddstr, usersub, filename);
+ make_usub("box", US_box, usersub, filename);
+ make_usub("clear", US_clear, usersub, filename);
+ make_usub("wclear", US_wclear, usersub, filename);
+ make_usub("clearok", US_clearok, usersub, filename);
+ make_usub("clrtobot", US_clrtobot, usersub, filename);
+ make_usub("wclrtobot", US_wclrtobot, usersub, filename);
+ make_usub("clrtoeol", US_clrtoeol, usersub, filename);
+ make_usub("wclrtoeol", US_wclrtoeol, usersub, filename);
+ make_usub("delch", US_delch, usersub, filename);
+ make_usub("wdelch", US_wdelch, usersub, filename);
+ make_usub("deleteln", US_deleteln, usersub, filename);
+ make_usub("wdeleteln", US_wdeleteln, usersub, filename);
+ make_usub("erase", US_erase, usersub, filename);
+ make_usub("werase", US_werase, usersub, filename);
+ make_usub("idlok", US_idlok, usersub, filename);
+ make_usub("insch", US_insch, usersub, filename);
+ make_usub("winsch", US_winsch, usersub, filename);
+ make_usub("insertln", US_insertln, usersub, filename);
+ make_usub("winsertln", US_winsertln, usersub, filename);
+ make_usub("move", US_move, usersub, filename);
+ make_usub("wmove", US_wmove, usersub, filename);
+ make_usub("overlay", US_overlay, usersub, filename);
+ make_usub("overwrite", US_overwrite, usersub, filename);
+ make_usub("refresh", US_refresh, usersub, filename);
+ make_usub("wrefresh", US_wrefresh, usersub, filename);
+ make_usub("standout", US_standout, usersub, filename);
+ make_usub("wstandout", US_wstandout, usersub, filename);
+ make_usub("standend", US_standend, usersub, filename);
+ make_usub("wstandend", US_wstandend, usersub, filename);
+ make_usub("cbreak", US_cbreak, usersub, filename);
+ make_usub("nocbreak", US_nocbreak, usersub, filename);
+ make_usub("echo", US_echo, usersub, filename);
+ make_usub("noecho", US_noecho, usersub, filename);
+ make_usub("getch", US_getch, usersub, filename);
+ make_usub("wgetch", US_wgetch, usersub, filename);
+ make_usub("getstr", US_getstr, usersub, filename);
+ make_usub("wgetstr", US_wgetstr, usersub, filename);
+ make_usub("raw", US_raw, usersub, filename);
+ make_usub("noraw", US_noraw, usersub, filename);
+ make_usub("baudrate", US_baudrate, usersub, filename);
+ make_usub("delwin", US_delwin, usersub, filename);
+ make_usub("endwin", US_endwin, usersub, filename);
+ make_usub("erasechar", US_erasechar, usersub, filename);
+ make_usub("getyx", US_getyx, usersub, filename);
+ make_usub("inch", US_inch, usersub, filename);
+ make_usub("winch", US_winch, usersub, filename);
+ make_usub("initscr", US_initscr, usersub, filename);
+ make_usub("killchar", US_killchar, usersub, filename);
+ make_usub("leaveok", US_leaveok, usersub, filename);
+ make_usub("longname", US_longname, usersub, filename);
+ make_usub("mvwin", US_mvwin, usersub, filename);
+ make_usub("newwin", US_newwin, usersub, filename);
+ make_usub("nl", US_nl, usersub, filename);
+ make_usub("nonl", US_nonl, usersub, filename);
+ make_usub("scrollok", US_scrollok, usersub, filename);
+ make_usub("subwin", US_subwin, usersub, filename);
+ make_usub("touchline", US_touchline, usersub, filename);
+ make_usub("touchwin", US_touchwin, usersub, filename);
+ make_usub("unctrl", US_unctrl, usersub, filename);
+ make_usub("gettmode", US_gettmode, usersub, filename);
+ make_usub("mvcur", US_mvcur, usersub, filename);
+ make_usub("scroll", US_scroll, usersub, filename);
+ make_usub("savetty", US_savetty, usersub, filename);
+ make_usub("resetty", US_resetty, usersub, filename);
+ make_usub("setterm", US_setterm, usersub, filename);
+ make_usub("getcap", US_getcap, usersub, filename);
+ make_usub("attroff", US_attroff, usersub, filename);
+ make_usub("wattroff", US_wattroff, usersub, filename);
+ make_usub("attron", US_attron, usersub, filename);
+ make_usub("wattron", US_wattron, usersub, filename);
+ make_usub("attrset", US_attrset, usersub, filename);
+ make_usub("wattrset", US_wattrset, usersub, filename);
+#ifdef CURSEFMT
+ make_usub("printw", US_printw, usersub, filename);
+ make_usub("wprintw", US_wprintw, usersub, filename);
+ make_usub("scanw", US_scanw, usersub, filename);
+ make_usub("wscanw", US_wscanw, usersub, filename);
+#endif
+#ifdef BSD
+ make_usub("flushok", US_flushok, usersub, filename);
+ make_usub("fullname", US_fullname, usersub, filename);
+ make_usub("touchoverlap", US_touchoverlap,usersub, filename);
+ make_usub("tstp", US_tstp, usersub, filename);
+ make_usub("_putchar", US__putchar, usersub, filename);
+#endif
+ make_usub("testcallback", US_testcallback,usersub, filename);
+ };
+
+#ifdef USG
+static char
+*getcap(cap)
+register char *cap;
+{
+ static char nocaperr[] = "Cannot read termcap entry.";
+
+ extern char *tgetstr();
+
+ if (tcbuf == NULL) {
+ if ((tcbuf = malloc(1024)) == NULL) {
+ fatal(nocaperr);
+ }
+ if (tgetent(tcbuf, ttytype) == -1) {
+ fatal(nocaperr);
+ }
+ }
+
+ return (tgetstr(cap, NULL));
+}
+#endif
+
+#ifdef NOSETATTR
+#define attron(attr) wattron(stdscr, attr)
+#define attroff(attr) wattroff(stdscr, attr)
+#define attset(attr) wattset(stdscr, attr)
+
+int
+wattron(win, attr)
+WINDOW *win;
+chtype attr;
+{
+ curattr |= attr;
+ if (curattr & A_STANDOUT) {
+ return(wstandout(win));
+ } else {
+ return(wstandend(win));
+ }
+}
+
+int
+wattroff(win, attr)
+WINDOW *win;
+chtype attr;
+{
+ curattr &= (~attr);
+ if (curattr & A_STANDOUT) {
+ return(wstandout(win));
+ } else {
+ return(wstandend(win));
+ }
+}
+
+int
+wattrset(win, attr)
+WINDOW *win;
+chtype attr;
+{
+ curattr = attr;
+ if (curattr & A_STANDOUT) {
+ return(wstandout(win));
+ } else {
+ return(wstandend(win));
+ }
+}
+
+#endif
+
+static int
+usersub(ix, sp, items)
+int ix;
+register int sp;
+register int items;
+{
+ STR **st = stack->ary_array + sp;
+ register int i;
+ register char *tmps;
+ register STR *Str; /* used in str_get and str_gnum macros */
+
+ switch (ix) {
+CASE int addch
+I char ch
+END
+
+CASE int waddch
+I WINDOW* win
+I char ch
+END
+
+CASE int addstr
+I char* str
+END
+
+CASE int waddstr
+I WINDOW* win
+I char* str
+END
+
+CASE int box
+I WINDOW* win
+I char vert
+I char hor
+END
+
+CASE int clear
+END
+
+CASE int wclear
+I WINDOW* win
+END
+
+CASE int clearok
+I WINDOW* win
+I bool boolf
+END
+
+CASE int clrtobot
+END
+
+CASE int wclrtobot
+I WINDOW* win
+END
+
+CASE int clrtoeol
+END
+
+CASE int wclrtoeol
+I WINDOW* win
+END
+
+CASE int delch
+END
+
+CASE int wdelch
+I WINDOW* win
+END
+
+CASE int deleteln
+END
+
+CASE int wdeleteln
+I WINDOW* win
+END
+
+CASE int erase
+END
+
+CASE int werase
+I WINDOW* win
+END
+
+CASE int idlok
+I WINDOW* win
+I bool boolf
+END
+
+CASE int insch
+I char c
+END
+
+CASE int winsch
+I WINDOW* win
+I char c
+END
+
+CASE int insertln
+END
+
+CASE int winsertln
+I WINDOW* win
+END
+
+CASE int move
+I int y
+I int x
+END
+
+CASE int wmove
+I WINDOW* win
+I int y
+I int x
+END
+
+CASE int overlay
+I WINDOW* win1
+I WINDOW* win2
+END
+
+CASE int overwrite
+I WINDOW* win1
+I WINDOW* win2
+END
+
+CASE int refresh
+END
+
+CASE int wrefresh
+I WINDOW* win
+END
+
+CASE int standout
+END
+
+CASE int wstandout
+I WINDOW* win
+END
+
+CASE int standend
+END
+
+CASE int wstandend
+I WINDOW* win
+END
+
+CASE int cbreak
+END
+
+CASE int nocbreak
+END
+
+CASE int echo
+END
+
+CASE int noecho
+END
+
+ case US_getch:
+ if (items != 0)
+ fatal("Usage: &getch()");
+ else {
+ int retval;
+ char retch;
+
+ retval = getch();
+ if (retval == EOF)
+ st[0] = &str_undef;
+ else {
+ retch = retval;
+ if (retval > 0377)
+ str_numset(st[0], (double) retval);
+ else
+ str_nset(st[0], &retch, 1);
+ }
+ }
+ return sp;
+
+ case US_wgetch:
+ if (items != 1)
+ fatal("Usage: &wgetch($win)");
+ else {
+ int retval;
+ char retch;
+ WINDOW* win = *(WINDOW**) str_get(st[1]);
+
+ retval = wgetch(win);
+ if (retval == EOF)
+ st[0] = &str_undef;
+ else {
+ retch = retval;
+ if (retval > 0377)
+ str_numset(st[0], (double) retval);
+ else
+ str_nset(st[0], &retch, 1);
+ }
+ }
+ return sp;
+
+CASE int getstr
+O char* str
+END
+
+CASE int wgetstr
+I WINDOW* win
+O char* str
+END
+
+CASE int raw
+END
+
+CASE int noraw
+END
+
+CASE int baudrate
+END
+
+CASE int delwin
+I WINDOW* win
+END
+
+CASE int endwin
+END
+
+CASE int erasechar
+END
+
+ case US_getyx:
+ if (items != 3)
+ fatal("Usage: &getyx($win, $y, $x)");
+ else {
+ int retval;
+ STR* str = str_new(0);
+ WINDOW* win = *(WINDOW**) str_get(st[1]);
+ int y;
+ int x;
+
+ do_sprintf(str, items - 1, st + 1);
+ retval = getyx(win, y, x);
+ str_numset(st[2], (double)y);
+ str_numset(st[3], (double)x);
+ str_numset(st[0], (double) retval);
+ str_free(str);
+ }
+ return sp;
+
+CASE int inch
+END
+
+CASE int winch
+I WINDOW* win
+END
+
+CASE WINDOW* initscr
+END
+
+CASE int killchar
+END
+
+CASE int leaveok
+I WINDOW* win
+I bool boolf
+END
+
+#ifdef BSD
+CASE char* longname
+I char* termbuf
+IO char* name
+END
+#else
+CASE char* longname
+I char* termbug
+I char* name
+END
+#endif
+
+CASE int mvwin
+I WINDOW* win
+I int y
+I int x
+END
+
+CASE WINDOW* newwin
+I int lines
+I int cols
+I int begin_y
+I int begin_x
+END
+
+CASE int nl
+END
+
+CASE int nonl
+END
+
+CASE int scrollok
+I WINDOW* win
+I bool boolf
+END
+
+CASE WINDOW* subwin
+I WINDOW* win
+I int lines
+I int cols
+I int begin_y
+I int begin_x
+END
+
+CASE int touchline
+I WINDOW* win
+I int y
+I int startx
+I int endx
+END
+
+CASE int touchwin
+I WINDOW* win
+END
+
+CASE char* unctrl
+I char ch
+END
+
+CASE int gettmode
+END
+
+CASE int mvcur
+I int lasty
+I int lastx
+I int newy
+I int newx
+END
+
+CASE int scroll
+I WINDOW* win
+END
+
+CASE int savetty
+END
+
+CASE void resetty
+END
+
+CASE int setterm
+I char* name
+END
+
+CASE int attroff
+I chtype str
+END
+
+CASE int wattroff
+I chtype str
+END
+
+CASE int wattron
+I chtype str
+END
+
+CASE int attron
+I chtype str
+END
+
+CASE int attrset
+I chtype str
+END
+
+CASE int wattrset
+I chtype str
+END
+
+#ifdef CURSEFMT
+ case US_printw:
+ if (items < 1)
+ fatal("Usage: &printw($fmt, $arg1, $arg2, ... )");
+ else {
+ int retval;
+ STR* str = str_new(0);
+
+ do_sprintf(str, items - 1, st + 1);
+ retval = addstr(str->str_ptr);
+ str_numset(st[0], (double) retval);
+ str_free(str);
+ }
+ return sp;
+
+ case US_wprintw:
+ if (items < 2)
+ fatal("Usage: &wprintw($win, $fmt, $arg1, $arg2, ... )");
+ else {
+ int retval;
+ STR* str = str_new(0);
+ WINDOW* win = *(WINDOW**) str_get(st[1]);
+
+ do_sprintf(str, items - 1, st + 1);
+ retval = waddstr(win, str->str_ptr);
+ str_numset(st[0], (double) retval);
+ str_free(str);
+ }
+ return sp;
+
+#endif
+
+CASE char* getcap
+I char* str
+END
+
+#ifdef BSD
+CASE int flushok
+I WINDOW* win
+I bool boolf
+END
+
+CASE int fullname
+I char* termbuf
+IO char* name
+END
+
+CASE int touchoverlap
+I WINDOW* win1
+I WINDOW* win2
+END
+
+CASE int tstp
+END
+
+CASE int _putchar
+I char ch
+END
+
+ case US_testcallback:
+ sp = callback("callback", sp + items, curcsv->wantarray, 1, items);
+ break;
+
+#endif
+
+ default:
+ fatal("Unimplemented user-defined subroutine");
+ }
+ return sp;
+}
+
+static int
+userval(ix, str)
+int ix;
+STR *str;
+{
+ switch (ix) {
+ case UV_COLS:
+ str_numset(str, (double)COLS);
+ break;
+ case UV_ERR:
+ str_numset(str, (double)ERR);
+ break;
+ case UV_LINES:
+ str_numset(str, (double)LINES);
+ break;
+ case UV_OK:
+ str_numset(str, (double)OK);
+ break;
+ case UV_curscr:
+ str_nset(str, &curscr, sizeof(WINDOW*));
+ break;
+ case UV_stdscr:
+ str_nset(str, &stdscr, sizeof(WINDOW*));
+ break;
+ case UV_ttytype:
+ str_set(str, ttytype);
+ break;
+#ifdef BSD
+ case UV_Def_term:
+ str_set(str, Def_term);
+ break;
+ case UV_My_term:
+ str_numset(str, (double)My_term);
+ break;
+#endif
+ case UV_A_STANDOUT:
+ str_numset(str, (double)A_STANDOUT);
+ break;
+ case UV_A_UNDERLINE:
+ str_numset(str, (double)A_UNDERLINE);
+ break;
+ case UV_A_REVERSE:
+ str_numset(str, (double)A_REVERSE);
+ break;
+ case UV_A_BLINK:
+ str_numset(str, (double)A_BLINK);
+ break;
+ case UV_A_DIM:
+ str_numset(str, (double)A_DIM);
+ break;
+ case UV_A_BOLD:
+ str_numset(str, (double)A_BOLD);
+ break;
+ case UV_A_NORMAL:
+ str_numset(str, (double)A_NORMAL);
+ break;
+ }
+ return 0;
+}
+
+static int
+userset(ix, str)
+int ix;
+STR *str;
+{
+ switch (ix) {
+ case UV_COLS:
+ COLS = (int)str_gnum(str);
+ break;
+ case UV_LINES:
+ LINES = (int)str_gnum(str);
+ break;
+ case UV_ttytype:
+ strcpy(ttytype, str_get(str)); /* hope it fits */
+#ifdef USG
+ if (tcbuf != NULL) {
+ free(tcbuf);
+ tcbuf = NULL;
+ }
+#endif
+ break;
+#ifdef BSD
+ case UV_Def_term:
+ Def_term = savestr(str_get(str)); /* never freed */
+ break;
+ case UV_My_term:
+ My_term = (bool)str_gnum(str);
+ break;
+#endif
+ }
+ return 0;
+}
diff --git a/gnu/usr.bin/perl/perl/usub/man2mus b/gnu/usr.bin/perl/perl/usub/man2mus
new file mode 100644
index 0000000..a304678
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/usub/man2mus
@@ -0,0 +1,66 @@
+#!/usr/bin/perl
+while (<>) {
+ if (/^\.SH SYNOPSIS/) {
+ $spec = '';
+ for ($_ = <>; $_ && !/^\.SH/; $_ = <>) {
+ s/^\.[IRB][IRB]\s*//;
+ s/^\.[IRB]\s+//;
+ next if /^\./;
+ s/\\f\w//g;
+ s/\\&//g;
+ s/^\s+//;
+ next if /^$/;
+ next if /^#/;
+ $spec .= $_;
+ }
+ $_ = $spec;
+ 0 while s/\(([^),;]*)\s*,\s*([^);]*)\)/($1|$2)/g;
+ s/\(\*([^,;]*)\)\(\)/(*)()$1/g;
+ s/(\w+)\[\]/*$1/g;
+
+ s/\n/ /g;
+ s/\s+/ /g;
+ s/(\w+) \(([^*])/$1($2/g;
+ s/^ //;
+ s/ ?; ?/\n/g;
+ s/\) /)\n/g;
+ s/ \* / \*/g;
+ s/\* / \*/g;
+
+ $* = 1;
+ 0 while s/^((struct )?\w+ )([^\n,]*), ?(.*)/$1$3\n$1$4/g;
+ $* = 0;
+ s/\|/,/g;
+
+ @cases = ();
+ for (reverse split(/\n/,$_)) {
+ if (/\)$/) {
+ ($type,$name,$args) = split(/(\w+)\(/);
+ $type =~ s/ $//;
+ if ($type =~ /^(\w+) =/) {
+ $type = $type{$1} if $type{$1};
+ }
+ $type = 'int' if $type eq '';
+ @args = grep(/./, split(/[,)]/,$args));
+ $case = "CASE $type $name\n";
+ foreach $arg (@args) {
+ $type = $type{$arg} || "int";
+ $type =~ s/ //g;
+ $type .= "\t" if length($type) < 8;
+ if ($type =~ /\*/) {
+ $case .= "IO $type $arg\n";
+ }
+ else {
+ $case .= "I $type $arg\n";
+ }
+ }
+ $case .= "END\n\n";
+ unshift(@cases, $case);
+ }
+ else {
+ $type{$name} = $type if ($type,$name) = /(.*\W)(\w+)$/;
+ }
+ }
+ print @cases;
+ }
+}
diff --git a/gnu/usr.bin/perl/perl/usub/mus b/gnu/usr.bin/perl/perl/usub/mus
new file mode 100755
index 0000000..b1675fd
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/usub/mus
@@ -0,0 +1,135 @@
+#!/usr/bin/perl
+
+while (<>) {
+ if (s/^CASE\s+//) {
+ @fields = split;
+ $funcname = pop(@fields);
+ $rettype = "@fields";
+ @modes = ();
+ @types = ();
+ @names = ();
+ @outies = ();
+ @callnames = ();
+ $pre = "\n";
+ $post = '';
+
+ while (<>) {
+ last unless /^[IO]+\s/;
+ @fields = split(' ');
+ push(@modes, shift(@fields));
+ push(@names, pop(@fields));
+ push(@types, "@fields");
+ }
+ while (s/^<\s//) {
+ $pre .= "\t $_";
+ $_ = <>;
+ }
+ while (s/^>\s//) {
+ $post .= "\t $_";
+ $_ = <>;
+ }
+ $items = @names;
+ $namelist = '$' . join(', $', @names);
+ $namelist = '' if $namelist eq '$';
+ print <<EOF;
+ case US_$funcname:
+ if (items != $items)
+ fatal("Usage: &$funcname($namelist)");
+ else {
+EOF
+ if ($rettype eq 'void') {
+ print <<EOF;
+ int retval = 1;
+EOF
+ }
+ else {
+ print <<EOF;
+ $rettype retval;
+EOF
+ }
+ foreach $i (1..@names) {
+ $mode = $modes[$i-1];
+ $type = $types[$i-1];
+ $name = $names[$i-1];
+ if ($type =~ /^[A-Z]+\*$/) {
+ $cast = "*($type*)";
+ }
+ else {
+ $cast = "($type)";
+ }
+ $what = ($type =~ /^(struct\s+\w+|char|[A-Z]+)\s*\*$/ ? "get" : "gnum");
+ $type .= "\t" if length($type) < 4;
+ $cast .= "\t" if length($cast) < 8;
+ $x = "\t" x (length($name) < 6);
+ if ($mode =~ /O/) {
+ if ($what eq 'gnum') {
+ push(@outies, "\t str_numset(st[$i], (double) $name);\n");
+ push(@callnames, "&$name");
+ }
+ else {
+ push(@outies, "\t str_set(st[$i], (char*) $name);\n");
+ push(@callnames, "$name");
+ }
+ }
+ else {
+ push(@callnames, $name);
+ }
+ if ($mode =~ /I/) {
+ print <<EOF;
+ $type $name =$x $cast str_$what(st[$i]);
+EOF
+ }
+ elsif ($type =~ /char/) {
+ print <<EOF;
+ char ${name}[133];
+EOF
+ }
+ else {
+ print <<EOF;
+ $type $name;
+EOF
+ }
+ }
+ $callnames = join(', ', @callnames);
+ $outies = join("\n",@outies);
+ if ($rettype eq 'void') {
+ print <<EOF;
+$pre (void)$funcname($callnames);
+EOF
+ }
+ else {
+ print <<EOF;
+$pre retval = $funcname($callnames);
+EOF
+ }
+ if ($rettype =~ /^(struct\s+\w+|char)\s*\*$/) {
+ print <<EOF;
+ str_set(st[0], (char*) retval);
+EOF
+ }
+ elsif ($rettype =~ /^[A-Z]+\s*\*$/) {
+ print <<EOF;
+ str_nset(st[0], (char*) &retval, sizeof retval);
+EOF
+ }
+ else {
+ print <<EOF;
+ str_numset(st[0], (double) retval);
+EOF
+ }
+ print $outies if $outies;
+ print $post if $post;
+ if (/^END/) {
+ print "\t}\n\treturn sp;\n";
+ }
+ else {
+ redo;
+ }
+ }
+ elsif (/^END/) {
+ print "\t}\n\treturn sp;\n";
+ }
+ else {
+ print;
+ }
+}
diff --git a/gnu/usr.bin/perl/perl/usub/pager b/gnu/usr.bin/perl/perl/usub/pager
new file mode 100644
index 0000000..407bc50
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/usub/pager
@@ -0,0 +1,190 @@
+#!./curseperl
+
+eval <<'EndOfMain'; $evaloffset = __LINE__;
+
+ $SIG{'INT'} = 'endit';
+ $| = 1; # command buffering on stdout
+ &initterm;
+ &inithelp;
+ &slurpfile && &pagearray;
+
+EndOfMain
+
+&endit;
+
+################################################################################
+
+sub initterm {
+
+ &initscr; &cbreak; &noecho; &scrollok($stdscr, 1);
+ &defbell unless defined &bell;
+
+ $lines = $LINES; $lines1 = $lines - 1; $lines2 = $lines - 2;
+ $cols = $COLS; $cols1 = $cols - 1; $cols2 = $cols - 2;;
+
+ $dl = &getcap('dl');
+ $al = &getcap('al');
+ $ho = &getcap('ho');
+ $ce = &getcap('ce');
+}
+
+sub slurpfile {
+ while (<>) {
+ s/^(\t+)/' ' x length($1)/e;
+ &expand($_) if /\t/;
+ if (length($_) < $cols) {
+ push(@lines, $_);
+ }
+ else {
+ while ($_ && $_ ne "\n") {
+ push(@lines, substr($_,0,$cols));
+ substr($_,0,$cols) = '';
+ }
+ }
+ }
+ 1;
+}
+
+sub drawscreen {
+ &move(0,0);
+ for ($line .. $line + $lines2) {
+ &addstr($lines[$_]);
+ }
+ &clrtobot;
+ &percent;
+ &refresh;
+}
+
+sub expand {
+ while (($off = index($_[0],"\t")) >= 0) {
+ substr($_[0], $off, 1) = ' ' x (8 - $off % 8);
+ }
+}
+
+sub pagearray {
+ $line = 0;
+
+ $| = 1;
+
+ for (&drawscreen;;&drawscreen) {
+
+ $ch = &getch;
+ $ch = 'j' if $ch eq "\n";
+
+ if ($ch eq ' ') {
+ last if $percent >= 100;
+ &move(0,0);
+ $line += $lines1;
+ }
+ elsif ($ch eq 'b') {
+ $line -= $lines1;
+ &move(0,0);
+ $line = 0 if $line < 0;
+ }
+ elsif ($ch eq 'j') {
+ next if $percent >= 100;
+ $line += 1;
+ if ($dl && $ho) {
+ print $ho, $dl;
+ &mvcur(0,0,$lines2,0);
+ print $ce,$lines[$line+$lines2],$ce;
+ &wmove($curscr,0,0);
+ &wdeleteln($curscr);
+ &wmove($curscr,$lines2,0);
+ &waddstr($curscr,$lines[$line+$lines2]);
+ }
+ &wmove($stdscr,0,0);
+ &wdeleteln($stdscr);
+ &wmove($stdscr,$lines2,0);
+ &waddstr($stdscr,$lines[$line+$lines2]);
+ &percent;
+ &refresh;
+ redo;
+ }
+ elsif ($ch eq 'k') {
+ next if $line <= 0;
+ $line -= 1;
+ if ($al && $ho && $ce) {
+ print $ho, $al, $ce, $lines[$line];
+ &wmove($curscr,0,0);
+ &winsertln($curscr);
+ &waddstr($curscr,$lines[$line]);
+ }
+ &wmove($stdscr,0,0);
+ &winsertln($stdscr);
+ &waddstr($stdscr,$lines[$line]);
+ &percent;
+ &refresh;
+ redo;
+ }
+ elsif ($ch eq "\f") {
+ &clear;
+ }
+ elsif ($ch eq 'q') {
+ last;
+ }
+ elsif ($ch eq 'h') {
+ &clear;
+ &help;
+ &clear;
+ }
+ else {
+ &bell;
+ }
+ }
+}
+
+sub defbell {
+ eval q#
+ sub bell {
+ print "\007";
+ }
+ #;
+}
+
+sub help {
+ local(*lines) = *helplines;
+ local($line);
+ &pagearray;
+}
+
+sub inithelp {
+ @helplines = split(/\n/,<<'EOT');
+
+ h Display this help.
+ q Exit.
+
+ SPACE Forward screen.
+ b Backward screen.
+ j, CR Forward 1 line.
+ k Backward 1 line.
+ FF Repaint screen.
+EOT
+ for (@helplines) {
+ s/$/\n/;
+ }
+}
+
+sub percent {
+ &standout;
+ $percent = int(($line + $lines1) * 100 / @lines);
+ &move($lines1,0);
+ &addstr("($percent%)");
+ &standend;
+ &clrtoeol;
+}
+
+sub endit {
+ &move($lines1,0);
+ &clrtoeol;
+ &refresh;
+ &endwin;
+
+ if ($@) {
+ print ""; # force flush of stdout
+ $@ =~ s/\(eval\)/$0/ && $@ =~ s/line (\d+)/'line ' . ($1 + $evaloffset)/e;
+ die $@;
+ }
+
+ exit;
+}
diff --git a/gnu/usr.bin/perl/perl/usub/usersub.c b/gnu/usr.bin/perl/perl/usub/usersub.c
new file mode 100644
index 0000000..ca9d2ba
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/usub/usersub.c
@@ -0,0 +1,75 @@
+/* $RCSfile: usersub.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:07 $
+ *
+ * $Log: usersub.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:30:07 nate
+ * PERL!
+ *
+ * Revision 4.0.1.1 91/11/05 19:07:24 lwall
+ * patch11: there are now subroutines for calling back from C into Perl
+ *
+ * Revision 4.0 91/03/20 01:56:34 lwall
+ * 4.0 baseline.
+ *
+ * Revision 3.0.1.1 90/08/09 04:06:10 lwall
+ * patch19: Initial revision
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+
+int
+userinit()
+{
+ init_curses();
+}
+
+/* Be sure to refetch the stack pointer after calling these routines. */
+
+int
+callback(subname, sp, gimme, hasargs, numargs)
+char *subname;
+int sp; /* stack pointer after args are pushed */
+int gimme; /* called in array or scalar context */
+int hasargs; /* whether to create a @_ array for routine */
+int numargs; /* how many args are pushed on the stack */
+{
+ static ARG myarg[3]; /* fake syntax tree node */
+ int arglast[3];
+
+ arglast[2] = sp;
+ sp -= numargs;
+ arglast[1] = sp--;
+ arglast[0] = sp;
+
+ if (!myarg[0].arg_ptr.arg_str)
+ myarg[0].arg_ptr.arg_str = str_make("",0);
+
+ myarg[1].arg_type = A_WORD;
+ myarg[1].arg_ptr.arg_stab = stabent(subname, FALSE);
+
+ myarg[2].arg_type = hasargs ? A_EXPR : A_NULL;
+
+ return do_subr(myarg, gimme, arglast);
+}
+
+int
+callv(subname, sp, gimme, argv)
+char *subname;
+register int sp; /* current stack pointer */
+int gimme; /* called in array or scalar context */
+register char **argv; /* null terminated arg list, NULL for no arglist */
+{
+ register int items = 0;
+ int hasargs = (argv != 0);
+
+ astore(stack, ++sp, Nullstr); /* reserve spot for 1st return arg */
+ if (hasargs) {
+ while (*argv) {
+ astore(stack, ++sp, str_2mortal(str_make(*argv,0)));
+ items++;
+ argv++;
+ }
+ }
+ return callback(subname, sp, gimme, hasargs, items);
+}
diff --git a/gnu/usr.bin/perl/perl/util.c b/gnu/usr.bin/perl/perl/util.c
new file mode 100644
index 0000000..5748983
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/util.c
@@ -0,0 +1,1780 @@
+/* $RCSfile: util.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:40 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: util.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:40 nate
+ * PERL!
+ *
+ * Revision 4.0.1.6 92/06/11 21:18:47 lwall
+ * patch34: boneheaded typo in my_bcopy()
+ *
+ * Revision 4.0.1.5 92/06/08 16:08:37 lwall
+ * patch20: removed implicit int declarations on functions
+ * patch20: Perl now distinguishes overlapped copies from non-overlapped
+ * patch20: fixed confusion between a *var's real name and its effective name
+ * patch20: bcopy() and memcpy() now tested for overlap safety
+ * patch20: added Atari ST portability
+ *
+ * Revision 4.0.1.4 91/11/11 16:48:54 lwall
+ * patch19: study was busted by 4.018
+ * patch19: added little-endian pack/unpack options
+ *
+ * Revision 4.0.1.3 91/11/05 19:18:26 lwall
+ * patch11: safe malloc code now integrated into Perl's malloc when possible
+ * patch11: index("little", "longer string") could visit faraway places
+ * patch11: warn '-' x 10000 dumped core
+ * patch11: forked exec on non-existent program now issues a warning
+ *
+ * Revision 4.0.1.2 91/06/07 12:10:42 lwall
+ * patch4: new copyright notice
+ * patch4: made some allowances for "semi-standard" C
+ * patch4: index() could blow up searching for null string
+ * patch4: taintchecks could improperly modify parent in vfork()
+ * patch4: exec would close files even if you cleared close-on-exec flag
+ *
+ * Revision 4.0.1.1 91/04/12 09:19:25 lwall
+ * patch1: random cleanup in cpp namespace
+ *
+ * Revision 4.0 91/03/20 01:56:39 lwall
+ * 4.0 baseline.
+ *
+ */
+/*SUPPRESS 112*/
+
+#include "EXTERN.h"
+#include "perl.h"
+
+#if !defined(NSIG) || defined(M_UNIX) || defined(M_XENIX)
+#include <signal.h>
+#endif
+
+#ifdef I_VFORK
+# include <vfork.h>
+#endif
+
+#ifdef I_VARARGS
+# include <varargs.h>
+#endif
+
+#ifdef I_FCNTL
+# include <fcntl.h>
+#endif
+#ifdef I_SYS_FILE
+# include <sys/file.h>
+#endif
+
+#define FLUSH
+
+#ifndef safemalloc
+
+static char nomem[] = "Out of memory!\n";
+
+/* paranoid version of malloc */
+
+#ifdef DEBUGGING
+static int an = 0;
+#endif
+
+/* NOTE: Do not call the next three routines directly. Use the macros
+ * in handy.h, so that we can easily redefine everything to do tracking of
+ * allocated hunks back to the original New to track down any memory leaks.
+ */
+
+char *
+safemalloc(size)
+#ifdef MSDOS
+unsigned long size;
+#else
+MEM_SIZE size;
+#endif /* MSDOS */
+{
+ char *ptr;
+#ifndef STANDARD_C
+ char *malloc();
+#endif /* ! STANDARD_C */
+
+#ifdef MSDOS
+ if (size > 0xffff) {
+ fprintf(stderr, "Allocation too large: %lx\n", size) FLUSH;
+ exit(1);
+ }
+#endif /* MSDOS */
+#ifdef DEBUGGING
+ if ((long)size < 0)
+ fatal("panic: malloc");
+#endif
+ ptr = malloc(size?size:1); /* malloc(0) is NASTY on our system */
+#ifdef DEBUGGING
+# if !(defined(I286) || defined(atarist))
+ if (debug & 128)
+ fprintf(stderr,"0x%x: (%05d) malloc %ld bytes\n",ptr,an++,(long)size);
+# else
+ if (debug & 128)
+ fprintf(stderr,"0x%lx: (%05d) malloc %ld bytes\n",ptr,an++,(long)size);
+# endif
+#endif
+ if (ptr != Nullch)
+ return ptr;
+ else if (nomemok)
+ return Nullch;
+ else {
+ fputs(nomem,stderr) FLUSH;
+ exit(1);
+ }
+ /*NOTREACHED*/
+#ifdef lint
+ return ptr;
+#endif
+}
+
+/* paranoid version of realloc */
+
+char *
+saferealloc(where,size)
+char *where;
+#ifndef MSDOS
+MEM_SIZE size;
+#else
+unsigned long size;
+#endif /* MSDOS */
+{
+ char *ptr;
+#ifndef STANDARD_C
+ char *realloc();
+#endif /* ! STANDARD_C */
+
+#ifdef MSDOS
+ if (size > 0xffff) {
+ fprintf(stderr, "Reallocation too large: %lx\n", size) FLUSH;
+ exit(1);
+ }
+#endif /* MSDOS */
+ if (!where)
+ fatal("Null realloc");
+#ifdef DEBUGGING
+ if ((long)size < 0)
+ fatal("panic: realloc");
+#endif
+ ptr = realloc(where,size?size:1); /* realloc(0) is NASTY on our system */
+#ifdef DEBUGGING
+# if !(defined(I286) || defined(atarist))
+ if (debug & 128) {
+ fprintf(stderr,"0x%x: (%05d) rfree\n",where,an++);
+ fprintf(stderr,"0x%x: (%05d) realloc %ld bytes\n",ptr,an++,(long)size);
+ }
+# else
+ if (debug & 128) {
+ fprintf(stderr,"0x%lx: (%05d) rfree\n",where,an++);
+ fprintf(stderr,"0x%lx: (%05d) realloc %ld bytes\n",ptr,an++,(long)size);
+ }
+# endif
+#endif
+ if (ptr != Nullch)
+ return ptr;
+ else if (nomemok)
+ return Nullch;
+ else {
+ fputs(nomem,stderr) FLUSH;
+ exit(1);
+ }
+ /*NOTREACHED*/
+#ifdef lint
+ return ptr;
+#endif
+}
+
+/* safe version of free */
+
+void
+safefree(where)
+char *where;
+{
+#ifdef DEBUGGING
+# if !(defined(I286) || defined(atarist))
+ if (debug & 128)
+ fprintf(stderr,"0x%x: (%05d) free\n",where,an++);
+# else
+ if (debug & 128)
+ fprintf(stderr,"0x%lx: (%05d) free\n",where,an++);
+# endif
+#endif
+ if (where) {
+ /*SUPPRESS 701*/
+ free(where);
+ }
+}
+
+#endif /* !safemalloc */
+
+#ifdef LEAKTEST
+
+#define ALIGN sizeof(long)
+
+char *
+safexmalloc(x,size)
+int x;
+MEM_SIZE size;
+{
+ register char *where;
+
+ where = safemalloc(size + ALIGN);
+ xcount[x]++;
+ where[0] = x % 100;
+ where[1] = x / 100;
+ return where + ALIGN;
+}
+
+char *
+safexrealloc(where,size)
+char *where;
+MEM_SIZE size;
+{
+ return saferealloc(where - ALIGN, size + ALIGN) + ALIGN;
+}
+
+void
+safexfree(where)
+char *where;
+{
+ int x;
+
+ if (!where)
+ return;
+ where -= ALIGN;
+ x = where[0] + 100 * where[1];
+ xcount[x]--;
+ safefree(where);
+}
+
+static void
+xstat()
+{
+ register int i;
+
+ for (i = 0; i < MAXXCOUNT; i++) {
+ if (xcount[i] > lastxcount[i]) {
+ fprintf(stderr,"%2d %2d\t%ld\n", i / 100, i % 100, xcount[i]);
+ lastxcount[i] = xcount[i];
+ }
+ }
+}
+
+#endif /* LEAKTEST */
+
+/* copy a string up to some (non-backslashed) delimiter, if any */
+
+char *
+cpytill(to,from,fromend,delim,retlen)
+register char *to;
+register char *from;
+register char *fromend;
+register int delim;
+int *retlen;
+{
+ char *origto = to;
+
+ for (; from < fromend; from++,to++) {
+ if (*from == '\\') {
+ if (from[1] == delim)
+ from++;
+ else if (from[1] == '\\')
+ *to++ = *from++;
+ }
+ else if (*from == delim)
+ break;
+ *to = *from;
+ }
+ *to = '\0';
+ *retlen = to - origto;
+ return from;
+}
+
+/* return ptr to little string in big string, NULL if not found */
+/* This routine was donated by Corey Satten. */
+
+char *
+instr(big, little)
+register char *big;
+register char *little;
+{
+ register char *s, *x;
+ register int first;
+
+ if (!little)
+ return big;
+ first = *little++;
+ if (!first)
+ return big;
+ while (*big) {
+ if (*big++ != first)
+ continue;
+ for (x=big,s=little; *s; /**/ ) {
+ if (!*x)
+ return Nullch;
+ if (*s++ != *x++) {
+ s--;
+ break;
+ }
+ }
+ if (!*s)
+ return big-1;
+ }
+ return Nullch;
+}
+
+/* same as instr but allow embedded nulls */
+
+char *
+ninstr(big, bigend, little, lend)
+register char *big;
+register char *bigend;
+char *little;
+char *lend;
+{
+ register char *s, *x;
+ register int first = *little;
+ register char *littleend = lend;
+
+ if (!first && little > littleend)
+ return big;
+ if (bigend - big < littleend - little)
+ return Nullch;
+ bigend -= littleend - little++;
+ while (big <= bigend) {
+ if (*big++ != first)
+ continue;
+ for (x=big,s=little; s < littleend; /**/ ) {
+ if (*s++ != *x++) {
+ s--;
+ break;
+ }
+ }
+ if (s >= littleend)
+ return big-1;
+ }
+ return Nullch;
+}
+
+/* reverse of the above--find last substring */
+
+char *
+rninstr(big, bigend, little, lend)
+register char *big;
+char *bigend;
+char *little;
+char *lend;
+{
+ register char *bigbeg;
+ register char *s, *x;
+ register int first = *little;
+ register char *littleend = lend;
+
+ if (!first && little > littleend)
+ return bigend;
+ bigbeg = big;
+ big = bigend - (littleend - little++);
+ while (big >= bigbeg) {
+ if (*big-- != first)
+ continue;
+ for (x=big+2,s=little; s < littleend; /**/ ) {
+ if (*s++ != *x++) {
+ s--;
+ break;
+ }
+ }
+ if (s >= littleend)
+ return big+1;
+ }
+ return Nullch;
+}
+
+unsigned char fold[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+ 'x', 'y', 'z', 91, 92, 93, 94, 95,
+ 96, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+ 'X', 'Y', 'Z', 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135,
+ 136, 137, 138, 139, 140, 141, 142, 143,
+ 144, 145, 146, 147, 148, 149, 150, 151,
+ 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167,
+ 168, 169, 170, 171, 172, 173, 174, 175,
+ 176, 177, 178, 179, 180, 181, 182, 183,
+ 184, 185, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 205, 206, 207,
+ 208, 209, 210, 211, 212, 213, 214, 215,
+ 216, 217, 218, 219, 220, 221, 222, 223,
+ 224, 225, 226, 227, 228, 229, 230, 231,
+ 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247,
+ 248, 249, 250, 251, 252, 253, 254, 255
+};
+
+static unsigned char freq[] = {
+ 1, 2, 84, 151, 154, 155, 156, 157,
+ 165, 246, 250, 3, 158, 7, 18, 29,
+ 40, 51, 62, 73, 85, 96, 107, 118,
+ 129, 140, 147, 148, 149, 150, 152, 153,
+ 255, 182, 224, 205, 174, 176, 180, 217,
+ 233, 232, 236, 187, 235, 228, 234, 226,
+ 222, 219, 211, 195, 188, 193, 185, 184,
+ 191, 183, 201, 229, 181, 220, 194, 162,
+ 163, 208, 186, 202, 200, 218, 198, 179,
+ 178, 214, 166, 170, 207, 199, 209, 206,
+ 204, 160, 212, 216, 215, 192, 175, 173,
+ 243, 172, 161, 190, 203, 189, 164, 230,
+ 167, 248, 227, 244, 242, 255, 241, 231,
+ 240, 253, 169, 210, 245, 237, 249, 247,
+ 239, 168, 252, 251, 254, 238, 223, 221,
+ 213, 225, 177, 197, 171, 196, 159, 4,
+ 5, 6, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 19, 20, 21, 22,
+ 23, 24, 25, 26, 27, 28, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 74, 75,
+ 76, 77, 78, 79, 80, 81, 82, 83,
+ 86, 87, 88, 89, 90, 91, 92, 93,
+ 94, 95, 97, 98, 99, 100, 101, 102,
+ 103, 104, 105, 106, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, 128,
+ 130, 131, 132, 133, 134, 135, 136, 137,
+ 138, 139, 141, 142, 143, 144, 145, 146
+};
+
+void
+fbmcompile(str, iflag)
+STR *str;
+int iflag;
+{
+ register unsigned char *s;
+ register unsigned char *table;
+ register unsigned int i;
+ register unsigned int len = str->str_cur;
+ int rarest = 0;
+ unsigned int frequency = 256;
+
+ Str_Grow(str,len+258);
+#ifndef lint
+ table = (unsigned char*)(str->str_ptr + len + 1);
+#else
+ table = Null(unsigned char*);
+#endif
+ s = table - 2;
+ for (i = 0; i < 256; i++) {
+ table[i] = len;
+ }
+ i = 0;
+#ifndef lint
+ while (s >= (unsigned char*)(str->str_ptr))
+#endif
+ {
+ if (table[*s] == len) {
+#ifndef pdp11
+ if (iflag)
+ table[*s] = table[fold[*s]] = i;
+#else
+ if (iflag) {
+ int j;
+ j = fold[*s];
+ table[j] = i;
+ table[*s] = i;
+ }
+#endif /* pdp11 */
+ else
+ table[*s] = i;
+ }
+ s--,i++;
+ }
+ str->str_pok |= SP_FBM; /* deep magic */
+
+#ifndef lint
+ s = (unsigned char*)(str->str_ptr); /* deeper magic */
+#else
+ s = Null(unsigned char*);
+#endif
+ if (iflag) {
+ register unsigned int tmp, foldtmp;
+ str->str_pok |= SP_CASEFOLD;
+ for (i = 0; i < len; i++) {
+ tmp=freq[s[i]];
+ foldtmp=freq[fold[s[i]]];
+ if (tmp < frequency && foldtmp < frequency) {
+ rarest = i;
+ /* choose most frequent among the two */
+ frequency = (tmp > foldtmp) ? tmp : foldtmp;
+ }
+ }
+ }
+ else {
+ for (i = 0; i < len; i++) {
+ if (freq[s[i]] < frequency) {
+ rarest = i;
+ frequency = freq[s[i]];
+ }
+ }
+ }
+ str->str_rare = s[rarest];
+ str->str_state = rarest;
+#ifdef DEBUGGING
+ if (debug & 512)
+ fprintf(stderr,"rarest char %c at %d\n",str->str_rare, str->str_state);
+#endif
+}
+
+char *
+fbminstr(big, bigend, littlestr)
+unsigned char *big;
+register unsigned char *bigend;
+STR *littlestr;
+{
+ register unsigned char *s;
+ register int tmp;
+ register int littlelen;
+ register unsigned char *little;
+ register unsigned char *table;
+ register unsigned char *olds;
+ register unsigned char *oldlittle;
+
+#ifndef lint
+ if (!(littlestr->str_pok & SP_FBM)) {
+ if (!littlestr->str_ptr)
+ return (char*)big;
+ return ninstr((char*)big,(char*)bigend,
+ littlestr->str_ptr, littlestr->str_ptr + littlestr->str_cur);
+ }
+#endif
+
+ littlelen = littlestr->str_cur;
+#ifndef lint
+ if (littlestr->str_pok & SP_TAIL && !multiline) { /* tail anchored? */
+ if (littlelen > bigend - big)
+ return Nullch;
+ little = (unsigned char*)littlestr->str_ptr;
+ if (littlestr->str_pok & SP_CASEFOLD) { /* oops, fake it */
+ big = bigend - littlelen; /* just start near end */
+ if (bigend[-1] == '\n' && little[littlelen-1] != '\n')
+ big--;
+ }
+ else {
+ s = bigend - littlelen;
+ if (*s == *little && bcmp(s,little,littlelen)==0)
+ return (char*)s; /* how sweet it is */
+ else if (bigend[-1] == '\n' && little[littlelen-1] != '\n'
+ && s > big) {
+ s--;
+ if (*s == *little && bcmp(s,little,littlelen)==0)
+ return (char*)s;
+ }
+ return Nullch;
+ }
+ }
+ table = (unsigned char*)(littlestr->str_ptr + littlelen + 1);
+#else
+ table = Null(unsigned char*);
+#endif
+ if (--littlelen >= bigend - big)
+ return Nullch;
+ s = big + littlelen;
+ oldlittle = little = table - 2;
+ if (littlestr->str_pok & SP_CASEFOLD) { /* case insensitive? */
+ if (s < bigend) {
+ top1:
+ /*SUPPRESS 560*/
+ if (tmp = table[*s]) {
+#ifdef POINTERRIGOR
+ if (bigend - s > tmp) {
+ s += tmp;
+ goto top1;
+ }
+#else
+ if ((s += tmp) < bigend)
+ goto top1;
+#endif
+ return Nullch;
+ }
+ else {
+ tmp = littlelen; /* less expensive than calling strncmp() */
+ olds = s;
+ while (tmp--) {
+ if (*--s == *--little || fold[*s] == *little)
+ continue;
+ s = olds + 1; /* here we pay the price for failure */
+ little = oldlittle;
+ if (s < bigend) /* fake up continue to outer loop */
+ goto top1;
+ return Nullch;
+ }
+#ifndef lint
+ return (char *)s;
+#endif
+ }
+ }
+ }
+ else {
+ if (s < bigend) {
+ top2:
+ /*SUPPRESS 560*/
+ if (tmp = table[*s]) {
+#ifdef POINTERRIGOR
+ if (bigend - s > tmp) {
+ s += tmp;
+ goto top2;
+ }
+#else
+ if ((s += tmp) < bigend)
+ goto top2;
+#endif
+ return Nullch;
+ }
+ else {
+ tmp = littlelen; /* less expensive than calling strncmp() */
+ olds = s;
+ while (tmp--) {
+ if (*--s == *--little)
+ continue;
+ s = olds + 1; /* here we pay the price for failure */
+ little = oldlittle;
+ if (s < bigend) /* fake up continue to outer loop */
+ goto top2;
+ return Nullch;
+ }
+#ifndef lint
+ return (char *)s;
+#endif
+ }
+ }
+ }
+ return Nullch;
+}
+
+char *
+screaminstr(bigstr, littlestr)
+STR *bigstr;
+STR *littlestr;
+{
+ register unsigned char *s, *x;
+ register unsigned char *big;
+ register int pos;
+ register int previous;
+ register int first;
+ register unsigned char *little;
+ register unsigned char *bigend;
+ register unsigned char *littleend;
+
+ if ((pos = screamfirst[littlestr->str_rare]) < 0)
+ return Nullch;
+#ifndef lint
+ little = (unsigned char *)(littlestr->str_ptr);
+#else
+ little = Null(unsigned char *);
+#endif
+ littleend = little + littlestr->str_cur;
+ first = *little++;
+ previous = littlestr->str_state;
+#ifndef lint
+ big = (unsigned char *)(bigstr->str_ptr);
+#else
+ big = Null(unsigned char*);
+#endif
+ bigend = big + bigstr->str_cur;
+ while (pos < previous) {
+#ifndef lint
+ if (!(pos += screamnext[pos]))
+#endif
+ return Nullch;
+ }
+#ifdef POINTERRIGOR
+ if (littlestr->str_pok & SP_CASEFOLD) { /* case insignificant? */
+ do {
+ if (big[pos-previous] != first && big[pos-previous] != fold[first])
+ continue;
+ for (x=big+pos+1-previous,s=little; s < littleend; /**/ ) {
+ if (x >= bigend)
+ return Nullch;
+ if (*s++ != *x++ && fold[*(s-1)] != *(x-1)) {
+ s--;
+ break;
+ }
+ }
+ if (s == littleend)
+#ifndef lint
+ return (char *)(big+pos-previous);
+#else
+ return Nullch;
+#endif
+ } while (
+#ifndef lint
+ pos += screamnext[pos] /* does this goof up anywhere? */
+#else
+ pos += screamnext[0]
+#endif
+ );
+ }
+ else {
+ do {
+ if (big[pos-previous] != first)
+ continue;
+ for (x=big+pos+1-previous,s=little; s < littleend; /**/ ) {
+ if (x >= bigend)
+ return Nullch;
+ if (*s++ != *x++) {
+ s--;
+ break;
+ }
+ }
+ if (s == littleend)
+#ifndef lint
+ return (char *)(big+pos-previous);
+#else
+ return Nullch;
+#endif
+ } while (
+#ifndef lint
+ pos += screamnext[pos]
+#else
+ pos += screamnext[0]
+#endif
+ );
+ }
+#else /* !POINTERRIGOR */
+ big -= previous;
+ if (littlestr->str_pok & SP_CASEFOLD) { /* case insignificant? */
+ do {
+ if (big[pos] != first && big[pos] != fold[first])
+ continue;
+ for (x=big+pos+1,s=little; s < littleend; /**/ ) {
+ if (x >= bigend)
+ return Nullch;
+ if (*s++ != *x++ && fold[*(s-1)] != *(x-1)) {
+ s--;
+ break;
+ }
+ }
+ if (s == littleend)
+#ifndef lint
+ return (char *)(big+pos);
+#else
+ return Nullch;
+#endif
+ } while (
+#ifndef lint
+ pos += screamnext[pos] /* does this goof up anywhere? */
+#else
+ pos += screamnext[0]
+#endif
+ );
+ }
+ else {
+ do {
+ if (big[pos] != first)
+ continue;
+ for (x=big+pos+1,s=little; s < littleend; /**/ ) {
+ if (x >= bigend)
+ return Nullch;
+ if (*s++ != *x++) {
+ s--;
+ break;
+ }
+ }
+ if (s == littleend)
+#ifndef lint
+ return (char *)(big+pos);
+#else
+ return Nullch;
+#endif
+ } while (
+#ifndef lint
+ pos += screamnext[pos]
+#else
+ pos += screamnext[0]
+#endif
+ );
+ }
+#endif /* POINTERRIGOR */
+ return Nullch;
+}
+
+/* copy a string to a safe spot */
+
+char *
+savestr(str)
+char *str;
+{
+ register char *newaddr;
+
+ New(902,newaddr,strlen(str)+1,char);
+ (void)strcpy(newaddr,str);
+ return newaddr;
+}
+
+/* same thing but with a known length */
+
+char *
+nsavestr(str, len)
+char *str;
+register int len;
+{
+ register char *newaddr;
+
+ New(903,newaddr,len+1,char);
+ Copy(str,newaddr,len,char); /* might not be null terminated */
+ newaddr[len] = '\0'; /* is now */
+ return newaddr;
+}
+
+/* grow a static string to at least a certain length */
+
+void
+growstr(strptr,curlen,newlen)
+char **strptr;
+int *curlen;
+int newlen;
+{
+ if (newlen > *curlen) { /* need more room? */
+ if (*curlen)
+ Renew(*strptr,newlen,char);
+ else
+ New(905,*strptr,newlen,char);
+ *curlen = newlen;
+ }
+}
+
+#ifndef I_VARARGS
+/*VARARGS1*/
+char *
+mess(pat,a1,a2,a3,a4)
+char *pat;
+long a1, a2, a3, a4;
+{
+ char *s;
+ int usermess = strEQ(pat,"%s");
+ STR *tmpstr;
+
+ s = buf;
+ if (usermess) {
+ tmpstr = str_mortal(&str_undef);
+ str_set(tmpstr, (char*)a1);
+ *s++ = tmpstr->str_ptr[tmpstr->str_cur-1];
+ }
+ else {
+ (void)sprintf(s,pat,a1,a2,a3,a4);
+ s += strlen(s);
+ }
+
+ if (s[-1] != '\n') {
+ if (curcmd->c_line) {
+ (void)sprintf(s," at %s line %ld",
+ stab_val(curcmd->c_filestab)->str_ptr, (long)curcmd->c_line);
+ s += strlen(s);
+ }
+ if (last_in_stab &&
+ stab_io(last_in_stab) &&
+ stab_io(last_in_stab)->lines ) {
+ (void)sprintf(s,", <%s> line %ld",
+ last_in_stab == argvstab ? "" : stab_ename(last_in_stab),
+ (long)stab_io(last_in_stab)->lines);
+ s += strlen(s);
+ }
+ (void)strcpy(s,".\n");
+ if (usermess)
+ str_cat(tmpstr,buf+1);
+ }
+ if (usermess)
+ return tmpstr->str_ptr;
+ else
+ return buf;
+}
+
+/*VARARGS1*/
+void fatal(pat,a1,a2,a3,a4)
+char *pat;
+long a1, a2, a3, a4;
+{
+ extern FILE *e_fp;
+ extern char *e_tmpname;
+ char *tmps;
+ char *message;
+
+ message = mess(pat,a1,a2,a3,a4);
+ if (in_eval) {
+ str_set(stab_val(stabent("@",TRUE)),message);
+ tmps = "_EVAL_";
+ while (loop_ptr >= 0 && (!loop_stack[loop_ptr].loop_label ||
+ strNE(tmps,loop_stack[loop_ptr].loop_label) )) {
+#ifdef DEBUGGING
+ if (debug & 4) {
+ deb("(Skipping label #%d %s)\n",loop_ptr,
+ loop_stack[loop_ptr].loop_label);
+ }
+#endif
+ loop_ptr--;
+ }
+#ifdef DEBUGGING
+ if (debug & 4) {
+ deb("(Found label #%d %s)\n",loop_ptr,
+ loop_stack[loop_ptr].loop_label);
+ }
+#endif
+ if (loop_ptr < 0) {
+ in_eval = 0;
+ fatal("Bad label: %s", tmps);
+ }
+ longjmp(loop_stack[loop_ptr].loop_env, 1);
+ }
+ fputs(message,stderr);
+ (void)fflush(stderr);
+ if (e_fp)
+ (void)UNLINK(e_tmpname);
+ statusvalue >>= 8;
+ exit((int)((errno&255)?errno:((statusvalue&255)?statusvalue:255)));
+}
+
+/*VARARGS1*/
+void warn(pat,a1,a2,a3,a4)
+char *pat;
+long a1, a2, a3, a4;
+{
+ char *message;
+
+ message = mess(pat,a1,a2,a3,a4);
+ fputs(message,stderr);
+#ifdef LEAKTEST
+#ifdef DEBUGGING
+ if (debug & 4096)
+ xstat();
+#endif
+#endif
+ (void)fflush(stderr);
+}
+#else
+/*VARARGS0*/
+char *
+mess(args)
+va_list args;
+{
+ char *pat;
+ char *s;
+ STR *tmpstr;
+ int usermess;
+#ifndef HAS_VPRINTF
+#ifdef CHARVSPRINTF
+ char *vsprintf();
+#else
+ int vsprintf();
+#endif
+#endif
+
+#ifdef lint
+ pat = Nullch;
+#else
+ pat = va_arg(args, char *);
+#endif
+ s = buf;
+ usermess = strEQ(pat, "%s");
+ if (usermess) {
+ tmpstr = str_mortal(&str_undef);
+ str_set(tmpstr, va_arg(args, char *));
+ *s++ = tmpstr->str_ptr[tmpstr->str_cur-1];
+ }
+ else {
+ (void) vsprintf(s,pat,args);
+ s += strlen(s);
+ }
+
+ if (s[-1] != '\n') {
+ if (curcmd->c_line) {
+ (void)sprintf(s," at %s line %ld",
+ stab_val(curcmd->c_filestab)->str_ptr, (long)curcmd->c_line);
+ s += strlen(s);
+ }
+ if (last_in_stab &&
+ stab_io(last_in_stab) &&
+ stab_io(last_in_stab)->lines ) {
+ (void)sprintf(s,", <%s> line %ld",
+ last_in_stab == argvstab ? "" : last_in_stab->str_magic->str_ptr,
+ (long)stab_io(last_in_stab)->lines);
+ s += strlen(s);
+ }
+ (void)strcpy(s,".\n");
+ if (usermess)
+ str_cat(tmpstr,buf+1);
+ }
+
+ if (usermess)
+ return tmpstr->str_ptr;
+ else
+ return buf;
+}
+
+/*VARARGS0*/
+void fatal(va_alist)
+va_dcl
+{
+ va_list args;
+ extern FILE *e_fp;
+ extern char *e_tmpname;
+ char *tmps;
+ char *message;
+
+#ifndef lint
+ va_start(args);
+#else
+ args = 0;
+#endif
+ message = mess(args);
+ va_end(args);
+ if (in_eval) {
+ str_set(stab_val(stabent("@",TRUE)),message);
+ tmps = "_EVAL_";
+ while (loop_ptr >= 0 && (!loop_stack[loop_ptr].loop_label ||
+ strNE(tmps,loop_stack[loop_ptr].loop_label) )) {
+#ifdef DEBUGGING
+ if (debug & 4) {
+ deb("(Skipping label #%d %s)\n",loop_ptr,
+ loop_stack[loop_ptr].loop_label);
+ }
+#endif
+ loop_ptr--;
+ }
+#ifdef DEBUGGING
+ if (debug & 4) {
+ deb("(Found label #%d %s)\n",loop_ptr,
+ loop_stack[loop_ptr].loop_label);
+ }
+#endif
+ if (loop_ptr < 0) {
+ in_eval = 0;
+ fatal("Bad label: %s", tmps);
+ }
+ longjmp(loop_stack[loop_ptr].loop_env, 1);
+ }
+ fputs(message,stderr);
+ (void)fflush(stderr);
+ if (e_fp)
+ (void)UNLINK(e_tmpname);
+ statusvalue >>= 8;
+ exit((int)((errno&255)?errno:((statusvalue&255)?statusvalue:255)));
+}
+
+/*VARARGS0*/
+void warn(va_alist)
+va_dcl
+{
+ va_list args;
+ char *message;
+
+#ifndef lint
+ va_start(args);
+#else
+ args = 0;
+#endif
+ message = mess(args);
+ va_end(args);
+
+ fputs(message,stderr);
+#ifdef LEAKTEST
+#ifdef DEBUGGING
+ if (debug & 4096)
+ xstat();
+#endif
+#endif
+ (void)fflush(stderr);
+}
+#endif
+
+void
+my_setenv(nam,val)
+char *nam, *val;
+{
+ register int i=envix(nam); /* where does it go? */
+
+ if (environ == origenviron) { /* need we copy environment? */
+ int j;
+ int max;
+ char **tmpenv;
+
+ /*SUPPRESS 530*/
+ for (max = i; environ[max]; max++) ;
+ New(901,tmpenv, max+2, char*);
+ for (j=0; j<max; j++) /* copy environment */
+ tmpenv[j] = savestr(environ[j]);
+ tmpenv[max] = Nullch;
+ environ = tmpenv; /* tell exec where it is now */
+ }
+ if (!val) {
+ while (environ[i]) {
+ environ[i] = environ[i+1];
+ i++;
+ }
+ return;
+ }
+ if (!environ[i]) { /* does not exist yet */
+ Renew(environ, i+2, char*); /* just expand it a bit */
+ environ[i+1] = Nullch; /* make sure it's null terminated */
+ }
+ else
+ Safefree(environ[i]);
+ New(904, environ[i], strlen(nam) + strlen(val) + 2, char);
+#ifndef MSDOS
+ (void)sprintf(environ[i],"%s=%s",nam,val);/* all that work just for this */
+#else
+ /* MS-DOS requires environment variable names to be in uppercase */
+ /* [Tom Dinger, 27 August 1990: Well, it doesn't _require_ it, but
+ * some utilities and applications may break because they only look
+ * for upper case strings. (Fixed strupr() bug here.)]
+ */
+ strcpy(environ[i],nam); strupr(environ[i]);
+ (void)sprintf(environ[i] + strlen(nam),"=%s",val);
+#endif /* MSDOS */
+}
+
+int
+envix(nam)
+char *nam;
+{
+ register int i, len = strlen(nam);
+
+ for (i = 0; environ[i]; i++) {
+ if (strnEQ(environ[i],nam,len) && environ[i][len] == '=')
+ break; /* strnEQ must come first to avoid */
+ } /* potential SEGV's */
+ return i;
+}
+
+#ifdef EUNICE
+int
+unlnk(f) /* unlink all versions of a file */
+char *f;
+{
+ int i;
+
+ for (i = 0; unlink(f) >= 0; i++) ;
+ return i ? 0 : -1;
+}
+#endif
+
+#if !defined(HAS_BCOPY) || !defined(SAFE_BCOPY)
+char *
+my_bcopy(from,to,len)
+register char *from;
+register char *to;
+register int len;
+{
+ char *retval = to;
+
+ if (from - to >= 0) {
+ while (len--)
+ *to++ = *from++;
+ }
+ else {
+ to += len;
+ from += len;
+ while (len--)
+ *(--to) = *(--from);
+ }
+ return retval;
+}
+#endif
+
+#if !defined(HAS_BZERO) && !defined(HAS_MEMSET)
+char *
+my_bzero(loc,len)
+register char *loc;
+register int len;
+{
+ char *retval = loc;
+
+ while (len--)
+ *loc++ = 0;
+ return retval;
+}
+#endif
+
+#ifndef HAS_MEMCMP
+int
+my_memcmp(s1,s2,len)
+register unsigned char *s1;
+register unsigned char *s2;
+register int len;
+{
+ register int tmp;
+
+ while (len--) {
+ if (tmp = *s1++ - *s2++)
+ return tmp;
+ }
+ return 0;
+}
+#endif /* HAS_MEMCMP */
+
+#ifdef I_VARARGS
+#ifndef HAS_VPRINTF
+
+#ifdef CHARVSPRINTF
+char *
+#else
+int
+#endif
+vsprintf(dest, pat, args)
+char *dest, *pat, *args;
+{
+ FILE fakebuf;
+
+ fakebuf._ptr = dest;
+ fakebuf._cnt = 32767;
+#ifndef _IOSTRG
+#define _IOSTRG 0
+#endif
+ fakebuf._flag = _IOWRT|_IOSTRG;
+ _doprnt(pat, args, &fakebuf); /* what a kludge */
+ (void)putc('\0', &fakebuf);
+#ifdef CHARVSPRINTF
+ return(dest);
+#else
+ return 0; /* perl doesn't use return value */
+#endif
+}
+
+#ifdef DEBUGGING
+int
+vfprintf(fd, pat, args)
+FILE *fd;
+char *pat, *args;
+{
+ _doprnt(pat, args, fd);
+ return 0; /* wrong, but perl doesn't use the return value */
+}
+#endif
+#endif /* HAS_VPRINTF */
+#endif /* I_VARARGS */
+
+/*
+ * I think my_swap(), htonl() and ntohl() have never been used.
+ * perl.h contains last-chance references to my_swap(), my_htonl()
+ * and my_ntohl(). I presume these are the intended functions;
+ * but htonl() and ntohl() have the wrong names. There are no
+ * functions my_htonl() and my_ntohl() defined anywhere.
+ * -DWS
+ */
+#ifdef MYSWAP
+#if BYTEORDER != 0x4321
+short
+my_swap(s)
+short s;
+{
+#if (BYTEORDER & 1) == 0
+ short result;
+
+ result = ((s & 255) << 8) + ((s >> 8) & 255);
+ return result;
+#else
+ return s;
+#endif
+}
+
+long
+htonl(l)
+register long l;
+{
+ union {
+ long result;
+ char c[sizeof(long)];
+ } u;
+
+#if BYTEORDER == 0x1234
+ u.c[0] = (l >> 24) & 255;
+ u.c[1] = (l >> 16) & 255;
+ u.c[2] = (l >> 8) & 255;
+ u.c[3] = l & 255;
+ return u.result;
+#else
+#if ((BYTEORDER - 0x1111) & 0x444) || !(BYTEORDER & 0xf)
+ fatal("Unknown BYTEORDER\n");
+#else
+ register int o;
+ register int s;
+
+ for (o = BYTEORDER - 0x1111, s = 0; s < (sizeof(long)*8); o >>= 4, s += 8) {
+ u.c[o & 0xf] = (l >> s) & 255;
+ }
+ return u.result;
+#endif
+#endif
+}
+
+long
+ntohl(l)
+register long l;
+{
+ union {
+ long l;
+ char c[sizeof(long)];
+ } u;
+
+#if BYTEORDER == 0x1234
+ u.c[0] = (l >> 24) & 255;
+ u.c[1] = (l >> 16) & 255;
+ u.c[2] = (l >> 8) & 255;
+ u.c[3] = l & 255;
+ return u.l;
+#else
+#if ((BYTEORDER - 0x1111) & 0x444) || !(BYTEORDER & 0xf)
+ fatal("Unknown BYTEORDER\n");
+#else
+ register int o;
+ register int s;
+
+ u.l = l;
+ l = 0;
+ for (o = BYTEORDER - 0x1111, s = 0; s < (sizeof(long)*8); o >>= 4, s += 8) {
+ l |= (u.c[o & 0xf] & 255) << s;
+ }
+ return l;
+#endif
+#endif
+}
+
+#endif /* BYTEORDER != 0x4321 */
+#endif /* MYSWAP */
+
+/*
+ * Little-endian byte order functions - 'v' for 'VAX', or 'reVerse'.
+ * If these functions are defined,
+ * the BYTEORDER is neither 0x1234 nor 0x4321.
+ * However, this is not assumed.
+ * -DWS
+ */
+
+#define HTOV(name,type) \
+ type \
+ name (n) \
+ register type n; \
+ { \
+ union { \
+ type value; \
+ char c[sizeof(type)]; \
+ } u; \
+ register int i; \
+ register int s; \
+ for (i = 0, s = 0; i < sizeof(u.c); i++, s += 8) { \
+ u.c[i] = (n >> s) & 0xFF; \
+ } \
+ return u.value; \
+ }
+
+#define VTOH(name,type) \
+ type \
+ name (n) \
+ register type n; \
+ { \
+ union { \
+ type value; \
+ char c[sizeof(type)]; \
+ } u; \
+ register int i; \
+ register int s; \
+ u.value = n; \
+ n = 0; \
+ for (i = 0, s = 0; i < sizeof(u.c); i++, s += 8) { \
+ n += (u.c[i] & 0xFF) << s; \
+ } \
+ return n; \
+ }
+
+#if defined(HAS_HTOVS) && !defined(htovs)
+HTOV(htovs,short)
+#endif
+#if defined(HAS_HTOVL) && !defined(htovl)
+HTOV(htovl,long)
+#endif
+#if defined(HAS_VTOHS) && !defined(vtohs)
+VTOH(vtohs,short)
+#endif
+#if defined(HAS_VTOHL) && !defined(vtohl)
+VTOH(vtohl,long)
+#endif
+
+#ifndef DOSISH
+FILE *
+mypopen(cmd,mode)
+char *cmd;
+char *mode;
+{
+ int p[2];
+ register int this, that;
+ register int pid;
+ STR *str;
+ int doexec = strNE(cmd,"-");
+
+ if (pipe(p) < 0)
+ return Nullfp;
+ this = (*mode == 'w');
+ that = !this;
+#ifdef TAINT
+ if (doexec) {
+ taintenv();
+ taintproper("Insecure dependency in exec");
+ }
+#endif
+ while ((pid = (doexec?vfork():fork())) < 0) {
+ if (errno != EAGAIN) {
+ close(p[this]);
+ if (!doexec)
+ fatal("Can't fork");
+ return Nullfp;
+ }
+ sleep(5);
+ }
+ if (pid == 0) {
+#define THIS that
+#define THAT this
+ close(p[THAT]);
+ if (p[THIS] != (*mode == 'r')) {
+ dup2(p[THIS], *mode == 'r');
+ close(p[THIS]);
+ }
+ if (doexec) {
+#if !defined(HAS_FCNTL) || !defined(F_SETFD)
+ int fd;
+
+#ifndef NOFILE
+#define NOFILE 20
+#endif
+ for (fd = maxsysfd + 1; fd < NOFILE; fd++)
+ close(fd);
+#endif
+ do_exec(cmd); /* may or may not use the shell */
+ warn("Can't exec \"%s\": %s", cmd, strerror(errno));
+ _exit(1);
+ }
+ /*SUPPRESS 560*/
+ if (tmpstab = stabent("$",allstabs))
+ str_numset(STAB_STR(tmpstab),(double)getpid());
+ forkprocess = 0;
+ hclear(pidstatus, FALSE); /* we have no children */
+ return Nullfp;
+#undef THIS
+#undef THAT
+ }
+ do_execfree(); /* free any memory malloced by child on vfork */
+ close(p[that]);
+ if (p[that] < p[this]) {
+ dup2(p[this], p[that]);
+ close(p[this]);
+ p[this] = p[that];
+ }
+ str = afetch(fdpid,p[this],TRUE);
+ str->str_u.str_useful = pid;
+ forkprocess = pid;
+ return fdopen(p[this], mode);
+}
+#else
+#ifdef atarist
+FILE *popen();
+FILE *
+mypopen(cmd,mode)
+char *cmd;
+char *mode;
+{
+ return popen(cmd, mode);
+}
+#endif
+
+#endif /* !DOSISH */
+
+#ifdef NOTDEF
+dumpfds(s)
+char *s;
+{
+ int fd;
+ struct stat tmpstatbuf;
+
+ fprintf(stderr,"%s", s);
+ for (fd = 0; fd < 32; fd++) {
+ if (fstat(fd,&tmpstatbuf) >= 0)
+ fprintf(stderr," %d",fd);
+ }
+ fprintf(stderr,"\n");
+}
+#endif
+
+#ifndef HAS_DUP2
+dup2(oldfd,newfd)
+int oldfd;
+int newfd;
+{
+#if defined(HAS_FCNTL) && defined(F_DUPFD)
+ close(newfd);
+ fcntl(oldfd, F_DUPFD, newfd);
+#else
+ int fdtmp[256];
+ int fdx = 0;
+ int fd;
+
+ if (oldfd == newfd)
+ return 0;
+ close(newfd);
+ while ((fd = dup(oldfd)) != newfd) /* good enough for low fd's */
+ fdtmp[fdx++] = fd;
+ while (fdx > 0)
+ close(fdtmp[--fdx]);
+#endif
+}
+#endif
+
+#ifndef DOSISH
+int
+mypclose(ptr)
+FILE *ptr;
+{
+#ifdef VOIDSIG
+ void (*hstat)(), (*istat)(), (*qstat)();
+#else
+ int (*hstat)(), (*istat)(), (*qstat)();
+#endif
+ int status;
+ STR *str;
+ int pid;
+
+ str = afetch(fdpid,fileno(ptr),TRUE);
+ pid = (int)str->str_u.str_useful;
+ astore(fdpid,fileno(ptr),Nullstr);
+ fclose(ptr);
+#ifdef UTS
+ if(kill(pid, 0) < 0) { return(pid); } /* HOM 12/23/91 */
+#endif
+ hstat = signal(SIGHUP, SIG_IGN);
+ istat = signal(SIGINT, SIG_IGN);
+ qstat = signal(SIGQUIT, SIG_IGN);
+ pid = wait4pid(pid, &status, 0);
+ signal(SIGHUP, hstat);
+ signal(SIGINT, istat);
+ signal(SIGQUIT, qstat);
+ return(pid < 0 ? pid : status);
+}
+
+int
+wait4pid(pid,statusp,flags)
+int pid;
+int *statusp;
+int flags;
+{
+#if !defined(HAS_WAIT4) && !defined(HAS_WAITPID)
+ int result;
+ STR *str;
+ char spid[16];
+#endif
+
+ if (!pid)
+ return -1;
+#ifdef HAS_WAIT4
+ return wait4((pid==-1)?0:pid,statusp,flags,Null(struct rusage *));
+#else
+#ifdef HAS_WAITPID
+ return waitpid(pid,statusp,flags);
+#else
+ if (pid > 0) {
+ sprintf(spid, "%d", pid);
+ str = hfetch(pidstatus,spid,strlen(spid),FALSE);
+ if (str != &str_undef) {
+ *statusp = (int)str->str_u.str_useful;
+ hdelete(pidstatus,spid,strlen(spid));
+ return pid;
+ }
+ }
+ else {
+ HENT *entry;
+
+ hiterinit(pidstatus);
+ if (entry = hiternext(pidstatus)) {
+ pid = atoi(hiterkey(entry,statusp));
+ str = hiterval(pidstatus,entry);
+ *statusp = (int)str->str_u.str_useful;
+ sprintf(spid, "%d", pid);
+ hdelete(pidstatus,spid,strlen(spid));
+ return pid;
+ }
+ }
+ if (flags)
+ fatal("Can't do waitpid with flags");
+ else {
+ while ((result = wait(statusp)) != pid && pid > 0 && result >= 0)
+ pidgone(result,*statusp);
+ if (result < 0)
+ *statusp = -1;
+ }
+ return result;
+#endif
+#endif
+}
+#endif /* !DOSISH */
+
+void
+/*SUPPRESS 590*/
+pidgone(pid,status)
+int pid;
+int status;
+{
+#if defined(HAS_WAIT4) || defined(HAS_WAITPID)
+#else
+ register STR *str;
+ char spid[16];
+
+ sprintf(spid, "%d", pid);
+ str = hfetch(pidstatus,spid,strlen(spid),TRUE);
+ str->str_u.str_useful = status;
+#endif
+ return;
+}
+
+#ifdef atarist
+int pclose();
+int
+mypclose(ptr)
+FILE *ptr;
+{
+ return pclose(ptr);
+}
+#endif
+
+void
+repeatcpy(to,from,len,count)
+register char *to;
+register char *from;
+int len;
+register int count;
+{
+ register int todo;
+ register char *frombase = from;
+
+ if (len == 1) {
+ todo = *from;
+ while (count-- > 0)
+ *to++ = todo;
+ return;
+ }
+ while (count-- > 0) {
+ for (todo = len; todo > 0; todo--) {
+ *to++ = *from++;
+ }
+ from = frombase;
+ }
+}
+
+#ifndef CASTNEGFLOAT
+unsigned long
+castulong(f)
+double f;
+{
+ long along;
+
+#if CASTFLAGS & 2
+# define BIGDOUBLE 2147483648.0
+ if (f >= BIGDOUBLE)
+ return (unsigned long)(f-(long)(f/BIGDOUBLE)*BIGDOUBLE)|0x80000000;
+#endif
+ if (f >= 0.0)
+ return (unsigned long)f;
+ along = (long)f;
+ return (unsigned long)along;
+}
+#endif
+
+#ifndef HAS_RENAME
+int
+same_dirent(a,b)
+char *a;
+char *b;
+{
+ char *fa = rindex(a,'/');
+ char *fb = rindex(b,'/');
+ struct stat tmpstatbuf1;
+ struct stat tmpstatbuf2;
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif
+ char tmpbuf[MAXPATHLEN+1];
+
+ if (fa)
+ fa++;
+ else
+ fa = a;
+ if (fb)
+ fb++;
+ else
+ fb = b;
+ if (strNE(a,b))
+ return FALSE;
+ if (fa == a)
+ strcpy(tmpbuf,".");
+ else
+ strncpy(tmpbuf, a, fa - a);
+ if (stat(tmpbuf, &tmpstatbuf1) < 0)
+ return FALSE;
+ if (fb == b)
+ strcpy(tmpbuf,".");
+ else
+ strncpy(tmpbuf, b, fb - b);
+ if (stat(tmpbuf, &tmpstatbuf2) < 0)
+ return FALSE;
+ return tmpstatbuf1.st_dev == tmpstatbuf2.st_dev &&
+ tmpstatbuf1.st_ino == tmpstatbuf2.st_ino;
+}
+#endif /* !HAS_RENAME */
+
+unsigned long
+scanoct(start, len, retlen)
+char *start;
+int len;
+int *retlen;
+{
+ register char *s = start;
+ register unsigned long retval = 0;
+
+ while (len-- && *s >= '0' && *s <= '7') {
+ retval <<= 3;
+ retval |= *s++ - '0';
+ }
+ *retlen = s - start;
+ return retval;
+}
+
+unsigned long
+scanhex(start, len, retlen)
+char *start;
+int len;
+int *retlen;
+{
+ register char *s = start;
+ register unsigned long retval = 0;
+ char *tmp;
+
+ while (len-- && *s && (tmp = index(hexdigit, *s))) {
+ retval <<= 4;
+ retval |= (tmp - hexdigit) & 15;
+ s++;
+ }
+ *retlen = s - start;
+ return retval;
+}
diff --git a/gnu/usr.bin/perl/perl/util.h b/gnu/usr.bin/perl/perl/util.h
new file mode 100644
index 0000000..ff64f85
--- /dev/null
+++ b/gnu/usr.bin/perl/perl/util.h
@@ -0,0 +1,61 @@
+/* $RCSfile: util.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:29:40 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: util.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:29:40 nate
+ * PERL!
+ *
+ * Revision 4.0.1.4 92/06/11 21:19:36 lwall
+ * patch34: pidgone() wasn't declared right
+ *
+ * Revision 4.0.1.3 92/06/08 16:09:20 lwall
+ * patch20: bcopy() and memcpy() now tested for overlap safety
+ *
+ * Revision 4.0.1.2 91/11/05 19:18:40 lwall
+ * patch11: safe malloc code now integrated into Perl's malloc when possible
+ *
+ * Revision 4.0.1.1 91/06/07 12:11:00 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:56:48 lwall
+ * 4.0 baseline.
+ *
+ */
+
+EXT int *screamfirst INIT(Null(int*));
+EXT int *screamnext INIT(Null(int*));
+
+#ifndef safemalloc
+char *safemalloc();
+char *saferealloc();
+#endif
+char *cpytill();
+char *instr();
+char *fbminstr();
+char *screaminstr();
+void fbmcompile();
+char *savestr();
+void my_setenv();
+int envix();
+void growstr();
+char *ninstr();
+char *rninstr();
+char *nsavestr();
+FILE *mypopen();
+int mypclose();
+#if !defined(HAS_BCOPY) || !defined(SAFE_BCOPY)
+char *my_bcopy();
+#endif
+#if !defined(HAS_BZERO) && !defined(HAS_MEMSET)
+char *my_bzero();
+#endif
+#ifndef HAS_MEMCMP
+int my_memcmp();
+#endif
+unsigned long scanoct();
+unsigned long scanhex();
+void pidgone();
diff --git a/gnu/usr.bin/perl/sperl/Makefile b/gnu/usr.bin/perl/sperl/Makefile
new file mode 100644
index 0000000..c690f98
--- /dev/null
+++ b/gnu/usr.bin/perl/sperl/Makefile
@@ -0,0 +1,26 @@
+#
+#
+
+PROG= suidperl
+
+SRCS+= array.c cmd.c cons.c consarg.c
+SRCS+= doarg.c doio.c dolist.c dump.c
+SRCS+= eval.c form.c hash.c malloc.c
+SRCS+= perl.c perly.c regcomp.c regexec.c
+SRCS+= stab.c str.c toke.c util.c
+SRCS+= usersub.c
+.PATH: ${.CURDIR}/../perl
+
+
+CFLAGS+= -I${.CURDIR}/../perl -DIAMSUID -DTAINT
+LDADD= -lm
+DPADD= ${LIBM}
+
+LDADD+= -lcrypt
+DPADD+= ${LIBCRYPT}
+
+NOMAN=
+
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/perl/tperl/Makefile b/gnu/usr.bin/perl/tperl/Makefile
new file mode 100644
index 0000000..95ae50d
--- /dev/null
+++ b/gnu/usr.bin/perl/tperl/Makefile
@@ -0,0 +1,26 @@
+#
+#
+
+PROG= tperl
+
+SRCS+= array.c cmd.c cons.c consarg.c
+SRCS+= doarg.c doio.c dolist.c dump.c
+SRCS+= eval.c form.c hash.c malloc.c
+SRCS+= perl.c perly.c regcomp.c regexec.c
+SRCS+= stab.c str.c toke.c util.c
+SRCS+= usersub.c
+.PATH: ${.CURDIR}/../perl
+
+
+CFLAGS+= -I${.CURDIR}/../perl -DTAINT
+LDADD+= -lm
+DPADD= ${LIBM}
+
+LDADD+= -lcrypt
+DPADD+= ${LIBCRYPT}
+
+NOMAN=
+
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/perl/x2p/EXTERN.h b/gnu/usr.bin/perl/x2p/EXTERN.h
new file mode 100644
index 0000000..f3048ed
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/EXTERN.h
@@ -0,0 +1,26 @@
+/* $RCSfile: EXTERN.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:11 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: EXTERN.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:30:11 nate
+ * PERL!
+ *
+ * Revision 4.0.1.1 91/06/07 12:11:15 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:56:53 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#undef EXT
+#define EXT extern
+
+#undef INIT
+#define INIT(x)
+
+#undef DOINIT
diff --git a/gnu/usr.bin/perl/x2p/INTERN.h b/gnu/usr.bin/perl/x2p/INTERN.h
new file mode 100644
index 0000000..e1967d2
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/INTERN.h
@@ -0,0 +1,26 @@
+/* $RCSfile: INTERN.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:11 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: INTERN.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:30:11 nate
+ * PERL!
+ *
+ * Revision 4.0.1.1 91/06/07 12:11:20 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:56:58 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#undef EXT
+#define EXT
+
+#undef INIT
+#define INIT(x) = x
+
+#define DOINIT
diff --git a/gnu/usr.bin/perl/x2p/Makefile b/gnu/usr.bin/perl/x2p/Makefile
new file mode 100644
index 0000000..15146d8
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/Makefile
@@ -0,0 +1,19 @@
+
+PROG= a2p
+
+SRCS+= a2p.c hash.c str.c walk.c util.c
+CFLAGS+= -I${.CURDIR}/../perl
+
+LDADD= -lm
+DPADD= ${LIBM}
+
+MAN1+= a2p.1 s2p.1 h2ph.1
+
+beforeinstall:
+ install -c -o ${BINOWN} -g ${BINGRP} -m 555 ${.CURDIR}/s2p \
+ ${DESTDIR}${BINDIR}
+ install -c -o ${BINOWN} -g ${BINGRP} -m 555 ${.CURDIR}/h2ph \
+ ${DESTDIR}${BINDIR}
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/perl/x2p/a2p.1 b/gnu/usr.bin/perl/x2p/a2p.1
new file mode 100644
index 0000000..58d8c07
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/a2p.1
@@ -0,0 +1,199 @@
+.rn '' }`
+''' $Header: /home/cvs/386BSD/ports/lang/perl/x2p/a2p.man,v 1.1.1.1 1993/08/23 21:30:10 nate Exp $
+'''
+''' $Log: a2p.man,v $
+.\" Revision 1.1.1.1 1993/08/23 21:30:10 nate
+.\" PERL!
+.\"
+''' Revision 4.0 91/03/20 01:57:11 lwall
+''' 4.0 baseline.
+'''
+''' Revision 3.0 89/10/18 15:34:22 lwall
+''' 3.0 baseline
+'''
+''' Revision 2.0.1.1 88/07/11 23:16:25 root
+''' patch2: changes related to 1985 awk
+'''
+''' Revision 2.0 88/06/05 00:15:36 root
+''' Baseline version 2.0.
+'''
+'''
+.de Sh
+.br
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp
+.if t .sp .5v
+.if n .sp
+..
+.de Ip
+.br
+.ie \\n.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+'''
+''' Set up \*(-- to give an unbreakable dash;
+''' string Tr holds user defined translation string.
+''' Bell System Logo is used as a dummy character.
+'''
+.tr \(*W-|\(bv\*(Tr
+.ie n \{\
+.ds -- \(*W-
+.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+.ds L" ""
+.ds R" ""
+.ds L' '
+.ds R' '
+'br\}
+.el\{\
+.ds -- \(em\|
+.tr \*(Tr
+.ds L" ``
+.ds R" ''
+.ds L' `
+.ds R' '
+'br\}
+.TH A2P 1 LOCAL
+.SH NAME
+a2p - Awk to Perl translator
+.SH SYNOPSIS
+.B a2p [options] filename
+.SH DESCRIPTION
+.I A2p
+takes an awk script specified on the command line (or from standard input)
+and produces a comparable
+.I perl
+script on the standard output.
+.Sh "Options"
+Options include:
+.TP 5
+.B \-D<number>
+sets debugging flags.
+.TP 5
+.B \-F<character>
+tells a2p that this awk script is always invoked with this -F switch.
+.TP 5
+.B \-n<fieldlist>
+specifies the names of the input fields if input does not have to be split into
+an array.
+If you were translating an awk script that processes the password file, you
+might say:
+.sp
+ a2p -7 -nlogin.password.uid.gid.gcos.shell.home
+.sp
+Any delimiter can be used to separate the field names.
+.TP 5
+.B \-<number>
+causes a2p to assume that input will always have that many fields.
+.Sh "Considerations"
+A2p cannot do as good a job translating as a human would, but it usually
+does pretty well.
+There are some areas where you may want to examine the perl script produced
+and tweak it some.
+Here are some of them, in no particular order.
+.PP
+There is an awk idiom of putting int() around a string expression to force
+numeric interpretation, even though the argument is always integer anyway.
+This is generally unneeded in perl, but a2p can't tell if the argument
+is always going to be integer, so it leaves it in.
+You may wish to remove it.
+.PP
+Perl differentiates numeric comparison from string comparison.
+Awk has one operator for both that decides at run time which comparison
+to do.
+A2p does not try to do a complete job of awk emulation at this point.
+Instead it guesses which one you want.
+It's almost always right, but it can be spoofed.
+All such guesses are marked with the comment \*(L"#???\*(R".
+You should go through and check them.
+You might want to run at least once with the \-w switch to perl, which
+will warn you if you use == where you should have used eq.
+.PP
+Perl does not attempt to emulate the behavior of awk in which nonexistent
+array elements spring into existence simply by being referenced.
+If somehow you are relying on this mechanism to create null entries for
+a subsequent for...in, they won't be there in perl.
+.PP
+If a2p makes a split line that assigns to a list of variables that looks
+like (Fld1, Fld2, Fld3...) you may want
+to rerun a2p using the \-n option mentioned above.
+This will let you name the fields throughout the script.
+If it splits to an array instead, the script is probably referring to the number
+of fields somewhere.
+.PP
+The exit statement in awk doesn't necessarily exit; it goes to the END
+block if there is one.
+Awk scripts that do contortions within the END block to bypass the block under
+such circumstances can be simplified by removing the conditional
+in the END block and just exiting directly from the perl script.
+.PP
+Perl has two kinds of array, numerically-indexed and associative.
+Awk arrays are usually translated to associative arrays, but if you happen
+to know that the index is always going to be numeric you could change
+the {...} to [...].
+Iteration over an associative array is done using the keys() function, but
+iteration over a numeric array is NOT.
+You might need to modify any loop that is iterating over the array in question.
+.PP
+Awk starts by assuming OFMT has the value %.6g.
+Perl starts by assuming its equivalent, $#, to have the value %.20g.
+You'll want to set $# explicitly if you use the default value of OFMT.
+.PP
+Near the top of the line loop will be the split operation that is implicit in
+the awk script.
+There are times when you can move this down past some conditionals that
+test the entire record so that the split is not done as often.
+.PP
+For aesthetic reasons you may wish to change the array base $[ from 1 back
+to perl's default of 0, but remember to change all array subscripts AND
+all substr() and index() operations to match.
+.PP
+Cute comments that say "# Here is a workaround because awk is dumb" are passed
+through unmodified.
+.PP
+Awk scripts are often embedded in a shell script that pipes stuff into and
+out of awk.
+Often the shell script wrapper can be incorporated into the perl script, since
+perl can start up pipes into and out of itself, and can do other things that
+awk can't do by itself.
+.PP
+Scripts that refer to the special variables RSTART and RLENGTH can often
+be simplified by referring to the variables $`, $& and $', as long as they
+are within the scope of the pattern match that sets them.
+.PP
+The produced perl script may have subroutines defined to deal with awk's
+semantics regarding getline and print.
+Since a2p usually picks correctness over efficiency.
+it is almost always possible to rewrite such code to be more efficient by
+discarding the semantic sugar.
+.PP
+For efficiency, you may wish to remove the keyword from any return statement
+that is the last statement executed in a subroutine.
+A2p catches the most common case, but doesn't analyze embedded blocks for
+subtler cases.
+.PP
+ARGV[0] translates to $ARGV0, but ARGV[n] translates to $ARGV[$n].
+A loop that tries to iterate over ARGV[0] won't find it.
+.SH ENVIRONMENT
+A2p uses no environment variables.
+.SH AUTHOR
+Larry Wall <lwall@jpl-devvax.Jpl.Nasa.Gov>
+.SH FILES
+.SH SEE ALSO
+perl The perl compiler/interpreter
+.br
+s2p sed to perl translator
+.SH DIAGNOSTICS
+.SH BUGS
+It would be possible to emulate awk's behavior in selecting string versus
+numeric operations at run time by inspection of the operands, but it would
+be gross and inefficient.
+Besides, a2p almost always guesses right.
+.PP
+Storage for the awk syntax tree is currently static, and can run out.
+.rn }` ''
diff --git a/gnu/usr.bin/perl/x2p/a2p.c b/gnu/usr.bin/perl/x2p/a2p.c
new file mode 100644
index 0000000..c441634
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/a2p.c
@@ -0,0 +1,2715 @@
+#ifndef lint
+static char yysccsid[] = "@(#)yaccpar 1.9 (Berkeley) 02/21/93";
+#endif
+#define YYBYACC 1
+#define YYMAJOR 1
+#define YYMINOR 9
+#define yyclearin (yychar=(-1))
+#define yyerrok (yyerrflag=0)
+#define YYRECOVERING (yyerrflag!=0)
+#define YYPREFIX "yy"
+#line 2 "a2p.y"
+/* $RCSfile: a2p.y,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:09 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: a2p.y,v $
+ * Revision 1.1.1.1 1993/08/23 21:30:09 nate
+ * PERL!
+ *
+ * Revision 4.0.1.2 92/06/08 16:13:03 lwall
+ * patch20: in a2p, getline should allow variable to be array element
+ *
+ * Revision 4.0.1.1 91/06/07 12:12:41 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:57:21 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "INTERN.h"
+#include "a2p.h"
+
+int root;
+int begins = Nullop;
+int ends = Nullop;
+
+#line 42 "y.tab.c"
+#define BEGIN 257
+#define END 258
+#define REGEX 259
+#define SEMINEW 260
+#define NEWLINE 261
+#define COMMENT 262
+#define FUN1 263
+#define FUNN 264
+#define GRGR 265
+#define PRINT 266
+#define PRINTF 267
+#define SPRINTF 268
+#define SPLIT 269
+#define IF 270
+#define ELSE 271
+#define WHILE 272
+#define FOR 273
+#define IN 274
+#define EXIT 275
+#define NEXT 276
+#define BREAK 277
+#define CONTINUE 278
+#define RET 279
+#define GETLINE 280
+#define DO 281
+#define SUB 282
+#define GSUB 283
+#define MATCH 284
+#define FUNCTION 285
+#define USERFUN 286
+#define DELETE 287
+#define ASGNOP 288
+#define OROR 289
+#define ANDAND 290
+#define NUMBER 291
+#define VAR 292
+#define SUBSTR 293
+#define INDEX 294
+#define MATCHOP 295
+#define RELOP 296
+#define OR 297
+#define STRING 298
+#define UMINUS 299
+#define NOT 300
+#define INCR 301
+#define DECR 302
+#define FIELD 303
+#define VFIELD 304
+#define YYERRCODE 256
+short yylhs[] = { -1,
+ 0, 3, 6, 6, 2, 2, 7, 7, 7, 7,
+ 7, 7, 9, 8, 8, 11, 11, 11, 11, 15,
+ 15, 15, 15, 14, 14, 14, 14, 13, 13, 13,
+ 13, 12, 12, 12, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 17, 17, 17, 17, 10, 10, 10, 18, 18, 18,
+ 1, 1, 19, 19, 19, 19, 4, 4, 20, 20,
+ 21, 21, 21, 21, 5, 5, 22, 22, 22, 22,
+ 25, 25, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 26, 26, 26, 24, 24,
+ 24, 24, 24, 24, 24, 24,
+};
+short yylen[] = { 2,
+ 2, 6, 5, 2, 3, 0, 1, 5, 10, 4,
+ 1, 1, 1, 1, 3, 1, 1, 1, 1, 3,
+ 4, 4, 2, 3, 3, 3, 3, 3, 3, 1,
+ 3, 1, 2, 3, 1, 1, 1, 3, 3, 3,
+ 3, 3, 3, 3, 5, 2, 2, 2, 2, 2,
+ 2, 3, 1, 2, 3, 4, 3, 4, 1, 3,
+ 4, 4, 4, 2, 8, 6, 8, 8, 6, 6,
+ 6, 6, 6, 6, 6, 6, 8, 8, 8, 8,
+ 1, 4, 1, 2, 1, 1, 0, 4, 4, 3,
+ 2, 0, 1, 1, 1, 1, 2, 0, 1, 1,
+ 2, 2, 2, 2, 2, 0, 3, 2, 2, 1,
+ 1, 0, 1, 4, 2, 4, 2, 1, 1, 1,
+ 2, 1, 1, 2, 5, 1, 1, 1, 6, 9,
+ 6, 7, 10, 9, 6, 5,
+};
+short yydefred[] = { 92,
+ 0, 0, 94, 95, 96, 93, 0, 91, 0, 0,
+ 30, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 36, 0, 0, 0, 37, 0, 0, 0, 0,
+ 0, 83, 0, 98, 0, 11, 0, 92, 0, 0,
+ 0, 17, 18, 19, 0, 0, 98, 98, 0, 0,
+ 0, 64, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 23,
+ 48, 49, 0, 0, 0, 0, 0, 0, 4, 0,
+ 98, 98, 98, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 46,
+ 47, 0, 0, 60, 0, 0, 0, 0, 0, 98,
+ 98, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 99, 100, 0, 97, 52, 31,
+ 27, 20, 0, 0, 0, 0, 29, 0, 0, 0,
+ 0, 44, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 61, 62, 90, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 13, 63, 82, 0,
+ 0, 98, 0, 0, 0, 0, 0, 0, 119, 118,
+ 122, 0, 98, 0, 98, 10, 98, 0, 105, 0,
+ 110, 0, 0, 21, 0, 58, 92, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 98, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 98, 98, 98, 98, 98, 8, 0, 0,
+ 69, 0, 74, 0, 73, 0, 76, 0, 75, 0,
+ 71, 72, 0, 66, 0, 70, 127, 126, 128, 0,
+ 0, 0, 0, 0, 111, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 98, 0, 0, 0, 98, 98, 98, 0, 0, 0,
+ 98, 68, 67, 78, 77, 80, 79, 0, 65, 0,
+ 0, 0, 0, 0, 0, 125, 0, 0, 0, 131,
+ 135, 0, 0, 0, 9, 98, 98, 0, 132, 0,
+ 0, 98, 130, 134, 0, 133,
+};
+short yydgoto[] = { 1,
+ 2, 7, 36, 74, 127, 37, 38, 39, 166, 52,
+ 75, 188, 42, 43, 44, 45, 46, 54, 8, 128,
+ 227, 189, 190, 191, 256, 250,
+};
+short yysindex[] = { 0,
+ 0, -45, 0, 0, 0, 0, 4961, 0, -114, -98,
+ 0, -11, 7, 7279, 10, 6, 17, 25, 45, -195,
+ 62, 0, 5, 77, 83, 0, 7332, 7332, 5086, -252,
+ -252, 0, 7332, 0, 5086, 0, -154, 0, 8, -41,
+ 1605, 0, 0, 0, 118, -234, 0, 0, 6059, 7279,
+ 5487, 0, 5843, 85, 7332, 7332, 75, 6108, 6154, 7332,
+ 97, 7279, 7279, 7332, 7332, 5086, -73, -266, -73, 0,
+ 0, 0, 26, -183, -39, 102, 106, 116, 0, -45,
+ 0, 0, 0, 5086, 6219, 7332, 7332, 7332, 118, -133,
+ 7332, 7332, 7332, 7332, 7332, 7332, 7332, -116, 5086, 0,
+ 0, -183, -183, 0, 6289, 125, 5487, 166, 29, 0,
+ 0, 6335, 1522, 7332, 128, 6381, 134, 6423, 6484, 7279,
+ 141, 90, 6534, 6580, 0, 0, 5197, 0, 0, 0,
+ 0, 0, -183, 5362, 5362, -206, 0, 1522, 1522, 1522,
+ 1522, 0, -32, 395, 395, -73, -73, -73, -73, -252,
+ -206, 5242, 5288, 0, 0, 0, 3391, 3391, -108, 1522,
+ 7332, 7332, 7332, 7332, 6626, 145, 0, 0, 0, 7332,
+ 7332, 0, 7279, 7279, 147, 148, 149, 7332, 0, 0,
+ 0, 7332, 0, -102, 0, 0, 0, 7332, 0, -42,
+ 0, 5553, -99, 0, 7332, 0, 0, 0, 7332, 7332,
+ 31, 2565, 3715, 3794, 5752, 153, 6688, 0, 6017, 6749,
+ -183, -38, -38, 5086, 5086, 2408, 7332, 7332, 4312, 104,
+ -183, -183, 0, 0, 0, 0, 0, 0, 118, -45,
+ 0, 6802, 0, 7332, 0, 7332, 0, 7332, 0, 7332,
+ 0, 0, -86, 0, 7332, 0, 0, 0, 0, 7332,
+ 7332, -37, -35, 6856, 0, 137, -74, 7332, 5598, -183,
+ -183, -183, -183, -183, 156, 6898, 6953, 7014, 7067, 7128,
+ 0, 7170, 7332, 7332, 0, 0, 0, 2698, 159, 7218,
+ 0, 0, 0, 0, 0, 0, 0, -183, 0, 4312,
+ 4312, 4312, 2408, -52, 5086, 0, -183, 5643, -71, 0,
+ 0, 160, 2408, -33, 0, 0, 0, 161, 0, 4312,
+ 4312, 0, 0, 0, 4312, 0,
+};
+short yyrindex[] = { 0,
+ 0, 2363, 0, 0, 0, 0, 203, 0, 0, 0,
+ 0, 56, 0, 3556, 0, 2835, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2231, 0, 2279, 1209,
+ 3881, 0, 0, 0, 1818, 1664, 0, 0, 0, 173,
+ 0, 0, 3764, 111, 0, 0, 381, 0, 0, 0,
+ 0, 173, 129, 0, 0, 0, 564, 834, 889, 0,
+ 0, 0, 436, 5689, 0, -200, -180, -156, 0, 2462,
+ 0, 0, 0, 0, 0, 0, 0, 0, 2084, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5689, 5689, 0, 0, 0, 0, -22, 0, 0,
+ 0, 0, 2883, 0, 0, 0, 0, 0, 0, 173,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 5689, 0, 0, 2181, 0, 3122, 3167, 3212,
+ 3281, 0, 0, 1719, 1770, 943, 1016, 1286, 1340, 2780,
+ 1394, 0, 0, 0, 0, 0, 0, 0, 0, 3489,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 12, 12, 0, 0, 0, -29, 0, 0,
+ 0, 46, 0, 0, 0, 0, 0, 67, 0, 0,
+ 0, 0, 491, 0, 0, 0, 0, 0, 3604, 3676,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4374, 71, 103, 0, 0, 162, 126, 241, 0, 0,
+ 5689, 4441, 0, 0, 0, 0, 0, 0, 2132, 2510,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 165, 0, 0, 0, 0, 0, 4486,
+ 4751, 4796, 4841, 4887, 0, 0, 0, 0, 0, 0,
+ 0, 0, 296, 357, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 5689, 0, 0,
+ 0, 0, 184, 0, 0, 0, 5152, 0, 5954, 0,
+ 0, 0, 184, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+};
+short yygindex[] = { 0,
+ -20, 0, 0, 4150, -75, 0, 0, 0, 0, 19,
+ -7, 4065, -19, -3, -1, 7551, 4353, -31, 0, 0,
+ 0, -170, -161, 0, -270, 16,
+};
+#define YYTABLESIZE 7831
+short yytable[] = { 40,
+ 81, 129, 84, 275, 96, 276, 303, 309, 47, 94,
+ 92, 120, 93, 6, 95, 76, 226, 80, 16, 109,
+ 97, 70, 302, 248, 48, 195, 152, 153, 49, 120,
+ 91, 77, 308, 78, 100, 101, 271, 81, 98, 23,
+ 81, 81, 81, 81, 81, 81, 50, 81, 257, 55,
+ 32, 33, 87, 99, 255, 59, 58, 192, 81, 81,
+ 81, 97, 81, 81, 59, 56, 100, 101, 106, 156,
+ 87, 231, 111, 87, 232, 109, 136, 125, 126, 98,
+ 121, 122, 82, 83, 60, 249, 123, 76, 17, 17,
+ 61, 151, 59, 81, 81, 63, 59, 59, 59, 59,
+ 59, 62, 59, 77, 123, 78, 79, 113, 18, 18,
+ 86, 115, 81, 59, 59, 59, 64, 59, 59, 299,
+ 300, 301, 65, 81, 81, 113, 193, 194, 111, 115,
+ 81, 255, 19, 19, 114, 87, 120, 98, 167, 313,
+ 314, 255, 130, 117, 316, 259, 131, 86, 59, 59,
+ 86, 86, 86, 86, 96, 86, 132, 86, 142, 94,
+ 92, 117, 93, 150, 95, 155, 121, 59, 86, 86,
+ 86, 161, 86, 86, 125, 126, 230, 163, 59, 59,
+ 91, 168, 169, 201, 121, 208, 214, 215, 216, 220,
+ 83, 212, 213, 241, 258, 278, 282, 279, 295, 306,
+ 307, 312, 1, 86, 86, 66, 252, 253, 27, 110,
+ 28, 97, 298, 87, 3, 4, 5, 223, 224, 225,
+ 112, 87, 86, 113, 112, 87, 247, 88, 251, 98,
+ 120, 120, 120, 86, 86, 0, 82, 83, 0, 0,
+ 0, 90, 0, 0, 0, 0, 0, 82, 83, 82,
+ 83, 82, 83, 82, 83, 82, 83, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 16, 16, 81, 81,
+ 294, 87, 87, 87, 81, 0, 87, 0, 0, 0,
+ 81, 124, 81, 81, 81, 81, 81, 304, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 23, 81, 124,
+ 81, 81, 81, 81, 81, 123, 123, 123, 32, 33,
+ 0, 0, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 0, 0, 59, 59, 0, 113, 113, 113, 59,
+ 115, 115, 115, 0, 0, 59, 114, 59, 59, 59,
+ 59, 59, 0, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 0, 59, 114, 59, 59, 59, 59, 59,
+ 0, 0, 117, 117, 117, 0, 0, 86, 86, 86,
+ 86, 86, 86, 86, 86, 86, 0, 0, 86, 86,
+ 54, 0, 0, 0, 86, 121, 121, 121, 0, 0,
+ 86, 90, 86, 86, 86, 86, 86, 116, 86, 86,
+ 86, 86, 86, 86, 86, 86, 86, 0, 86, 0,
+ 86, 86, 86, 86, 86, 116, 0, 54, 0, 0,
+ 54, 54, 54, 54, 54, 54, 0, 54, 12, 13,
+ 0, 96, 0, 14, 15, 84, 94, 0, 54, 54,
+ 0, 95, 54, 54, 0, 16, 0, 17, 18, 19,
+ 0, 21, 0, 0, 0, 0, 22, 23, 24, 25,
+ 85, 86, 0, 26, 0, 0, 30, 31, 32, 33,
+ 0, 0, 84, 54, 54, 84, 84, 84, 84, 84,
+ 84, 0, 84, 0, 0, 0, 0, 0, 97, 0,
+ 22, 0, 54, 84, 84, 84, 0, 84, 84, 0,
+ 124, 124, 124, 54, 54, 0, 98, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 22, 84, 84,
+ 22, 22, 22, 22, 22, 22, 0, 22, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 22, 22,
+ 22, 0, 22, 22, 0, 114, 114, 114, 84, 84,
+ 0, 0, 0, 51, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 22, 22, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 51, 0, 22, 51, 51, 51, 51, 51, 51, 0,
+ 51, 0, 0, 22, 22, 0, 116, 116, 116, 0,
+ 0, 51, 51, 51, 0, 51, 51, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 0, 0, 54, 54,
+ 0, 0, 0, 0, 54, 0, 51, 0, 0, 0,
+ 54, 0, 54, 54, 54, 54, 54, 0, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 0, 54, 0,
+ 54, 54, 54, 54, 54, 0, 51, 51, 0, 0,
+ 0, 0, 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 0, 0, 84, 84, 0, 0, 0, 0, 84,
+ 0, 0, 0, 0, 0, 84, 0, 84, 84, 84,
+ 84, 84, 0, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 0, 84, 0, 84, 84, 84, 84, 84,
+ 0, 0, 0, 0, 0, 0, 0, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 0, 0, 22, 22,
+ 0, 0, 0, 0, 22, 0, 0, 0, 0, 0,
+ 22, 0, 22, 22, 22, 22, 22, 0, 22, 22,
+ 0, 22, 22, 22, 22, 22, 22, 0, 22, 0,
+ 22, 22, 22, 22, 22, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 0,
+ 0, 51, 51, 35, 0, 0, 0, 51, 0, 0,
+ 0, 0, 0, 51, 0, 51, 51, 51, 51, 51,
+ 0, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 0, 51, 0, 51, 51, 51, 51, 51, 0, 0,
+ 35, 0, 0, 35, 35, 35, 35, 35, 35, 0,
+ 35, 0, 0, 0, 0, 0, 0, 0, 50, 0,
+ 0, 35, 35, 35, 0, 35, 35, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 50, 35, 35, 50, 50,
+ 50, 50, 50, 50, 0, 50, 0, 0, 0, 0,
+ 0, 0, 40, 0, 0, 35, 50, 50, 50, 0,
+ 50, 50, 0, 0, 0, 0, 35, 35, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 40,
+ 0, 50, 40, 40, 40, 40, 40, 40, 0, 40,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 40, 40, 40, 0, 40, 40, 0, 0, 0, 0,
+ 0, 50, 50, 0, 0, 41, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 40, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 41, 0, 0, 41, 41, 41, 41, 41,
+ 41, 0, 41, 0, 0, 40, 40, 0, 0, 0,
+ 0, 0, 0, 41, 41, 41, 0, 41, 41, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 0,
+ 0, 35, 35, 0, 0, 0, 0, 35, 41, 0,
+ 0, 0, 0, 35, 0, 35, 35, 35, 35, 35,
+ 0, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 0, 35, 0, 35, 0, 0, 35, 35, 41, 41,
+ 0, 0, 0, 0, 0, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 0, 0, 50, 50, 0, 0,
+ 0, 0, 50, 0, 0, 0, 0, 0, 50, 0,
+ 50, 50, 50, 50, 50, 0, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 0, 50, 0, 50, 50,
+ 50, 50, 50, 0, 0, 0, 0, 0, 0, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40, 14, 0,
+ 40, 40, 0, 0, 0, 0, 40, 0, 0, 0,
+ 0, 0, 40, 0, 40, 40, 40, 40, 40, 0,
+ 40, 40, 40, 40, 40, 40, 40, 40, 40, 0,
+ 40, 0, 40, 40, 40, 40, 40, 0, 14, 0,
+ 0, 14, 0, 14, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 14, 0, 0,
+ 0, 0, 41, 41, 41, 41, 41, 41, 41, 41,
+ 41, 0, 0, 41, 41, 42, 0, 0, 0, 41,
+ 0, 0, 0, 0, 0, 41, 0, 41, 41, 41,
+ 41, 41, 0, 41, 41, 41, 41, 41, 41, 41,
+ 41, 41, 0, 41, 0, 41, 41, 41, 41, 41,
+ 0, 0, 42, 0, 0, 42, 42, 42, 42, 42,
+ 42, 14, 42, 0, 0, 0, 0, 0, 0, 43,
+ 0, 0, 0, 42, 42, 42, 0, 42, 42, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 43, 0, 42, 43,
+ 43, 43, 43, 43, 43, 0, 43, 0, 0, 0,
+ 0, 0, 0, 34, 0, 0, 0, 43, 43, 43,
+ 0, 43, 43, 0, 0, 0, 0, 0, 42, 42,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 34, 0, 43, 34, 34, 34, 34, 34, 34, 0,
+ 34, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 34, 34, 34, 0, 34, 34, 0, 0, 0,
+ 0, 0, 43, 43, 0, 14, 14, 14, 14, 14,
+ 14, 14, 14, 0, 0, 0, 14, 14, 0, 0,
+ 0, 0, 0, 0, 0, 0, 34, 34, 14, 0,
+ 14, 14, 14, 14, 14, 0, 0, 0, 0, 14,
+ 14, 14, 14, 0, 0, 34, 14, 0, 14, 14,
+ 14, 14, 14, 0, 0, 0, 34, 34, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 0, 0, 42, 42, 0, 0, 0, 0, 42,
+ 0, 66, 0, 0, 27, 42, 28, 42, 42, 42,
+ 42, 42, 0, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 0, 42, 0, 42, 42, 42, 42, 42,
+ 0, 0, 0, 0, 0, 0, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 0, 0, 43, 43, 0,
+ 0, 0, 0, 43, 0, 0, 0, 0, 0, 43,
+ 0, 43, 43, 43, 43, 43, 0, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 0, 43, 0, 43,
+ 43, 43, 43, 43, 66, 0, 0, 27, 0, 28,
+ 34, 34, 34, 34, 34, 34, 34, 34, 34, 0,
+ 0, 34, 34, 35, 87, 0, 88, 34, 0, 0,
+ 0, 0, 0, 34, 0, 34, 34, 34, 34, 34,
+ 0, 34, 0, 0, 34, 34, 34, 34, 34, 34,
+ 0, 34, 0, 34, 34, 34, 34, 34, 0, 0,
+ 35, 0, 0, 35, 35, 35, 35, 35, 35, 0,
+ 35, 0, 0, 0, 0, 0, 0, 0, 38, 0,
+ 0, 35, 35, 35, 0, 35, 35, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 35, 35, 38, 38,
+ 0, 38, 38, 38, 0, 0, 0, 0, 0, 39,
+ 0, 0, 0, 0, 0, 35, 38, 38, 38, 0,
+ 38, 38, 0, 0, 12, 13, 35, 35, 0, 14,
+ 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 16, 0, 17, 18, 19, 0, 21, 0, 39,
+ 39, 38, 39, 39, 39, 0, 0, 32, 0, 26,
+ 0, 0, 30, 31, 32, 33, 0, 39, 39, 39,
+ 0, 39, 39, 0, 0, 0, 0, 0, 0, 0,
+ 0, 38, 38, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 32, 32, 0,
+ 0, 32, 39, 0, 0, 0, 0, 12, 13, 0,
+ 0, 0, 14, 15, 0, 32, 32, 32, 0, 32,
+ 0, 0, 0, 0, 16, 0, 17, 18, 19, 0,
+ 21, 0, 39, 39, 0, 22, 23, 24, 25, 85,
+ 86, 0, 26, 0, 0, 30, 31, 32, 33, 0,
+ 32, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 0,
+ 0, 35, 35, 0, 0, 0, 0, 35, 0, 0,
+ 32, 32, 0, 35, 0, 35, 35, 35, 35, 35,
+ 0, 0, 35, 35, 35, 35, 35, 35, 35, 35,
+ 0, 35, 0, 35, 0, 0, 35, 35, 0, 0,
+ 0, 0, 0, 0, 0, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 0, 0, 38, 38, 0, 0,
+ 0, 0, 38, 0, 0, 0, 0, 0, 38, 0,
+ 38, 38, 38, 38, 38, 0, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 0, 38, 0, 38, 38,
+ 38, 38, 38, 0, 0, 0, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 0, 0, 39, 39, 0,
+ 0, 0, 0, 39, 0, 0, 0, 0, 0, 39,
+ 0, 39, 39, 39, 39, 39, 0, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 0, 39, 0, 39,
+ 39, 39, 39, 39, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 33, 0, 32, 32, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 32, 0, 32,
+ 32, 32, 32, 32, 0, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 0, 32, 0, 32, 32, 32,
+ 32, 32, 0, 33, 33, 0, 0, 33, 0, 0,
+ 0, 45, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 33, 33, 33, 0, 33, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 45, 45, 0, 0, 45, 33, 0, 0, 0,
+ 15, 0, 0, 0, 0, 0, 0, 0, 0, 45,
+ 45, 45, 0, 45, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 33, 33, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 15, 0, 0, 15, 45, 15, 0, 0, 0, 0,
+ 12, 0, 0, 0, 0, 0, 0, 0, 0, 15,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 45, 45, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 12, 0, 0, 12, 0, 12, 0, 0, 7, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 12,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 15, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 7, 0,
+ 0, 7, 0, 7, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 7, 0, 0,
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 0,
+ 0, 33, 33, 12, 0, 0, 0, 0, 0, 0,
+ 0, 0, 6, 33, 0, 33, 33, 33, 33, 33,
+ 0, 33, 33, 33, 33, 33, 33, 33, 33, 33,
+ 0, 33, 0, 33, 33, 33, 33, 33, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 0, 0, 45,
+ 45, 0, 6, 0, 0, 6, 0, 6, 0, 0,
+ 0, 45, 0, 45, 45, 45, 45, 45, 0, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 0, 45,
+ 0, 45, 45, 45, 45, 45, 0, 15, 15, 15,
+ 15, 15, 15, 15, 15, 0, 0, 66, 15, 15,
+ 27, 0, 28, 0, 0, 0, 0, 0, 0, 0,
+ 15, 5, 15, 15, 15, 15, 15, 0, 0, 0,
+ 0, 15, 15, 15, 15, 0, 0, 0, 15, 0,
+ 15, 15, 15, 15, 15, 6, 0, 12, 12, 12,
+ 12, 0, 12, 12, 12, 0, 0, 0, 12, 12,
+ 0, 5, 0, 0, 5, 0, 5, 0, 0, 2,
+ 12, 0, 12, 12, 12, 12, 12, 0, 0, 0,
+ 0, 12, 12, 12, 12, 0, 0, 0, 12, 0,
+ 12, 12, 12, 12, 12, 7, 7, 7, 7, 7,
+ 7, 7, 7, 0, 0, 0, 7, 7, 0, 2,
+ 0, 0, 2, 0, 2, 0, 0, 0, 7, 0,
+ 7, 7, 7, 7, 7, 0, 0, 0, 0, 7,
+ 7, 7, 7, 0, 0, 0, 7, 0, 7, 7,
+ 7, 7, 7, 0, 5, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 66, 233, 0, 27, 234, 28,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 6,
+ 6, 6, 0, 0, 0, 6, 6, 0, 0, 0,
+ 6, 6, 2, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 6, 0, 6, 6, 6, 6, 6, 0,
+ 0, 0, 0, 6, 6, 6, 6, 0, 0, 0,
+ 6, 0, 6, 6, 6, 6, 6, 0, 0, 0,
+ 12, 13, 0, 173, 174, 14, 15, 0, 0, 0,
+ 0, 0, 178, 179, 180, 181, 182, 16, 0, 17,
+ 18, 19, 0, 21, 184, 0, 0, 0, 22, 23,
+ 24, 25, 0, 0, 0, 26, 0, 0, 30, 31,
+ 32, 33, 0, 0, 0, 0, 0, 0, 5, 5,
+ 5, 0, 0, 0, 5, 5, 0, 0, 0, 5,
+ 5, 0, 0, 0, 0, 0, 0, 35, 0, 0,
+ 27, 5, 28, 5, 5, 5, 5, 5, 0, 0,
+ 0, 0, 5, 5, 5, 5, 293, 0, 0, 5,
+ 0, 5, 5, 5, 5, 5, 2, 2, 2, 0,
+ 0, 0, 2, 2, 0, 0, 0, 2, 2, 57,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
+ 0, 2, 2, 2, 2, 2, 0, 0, 0, 0,
+ 2, 2, 2, 2, 0, 0, 0, 2, 0, 2,
+ 2, 2, 2, 2, 0, 0, 57, 0, 0, 57,
+ 57, 57, 57, 57, 57, 0, 57, 12, 13, 0,
+ 0, 0, 14, 15, 53, 0, 0, 57, 57, 57,
+ 0, 57, 57, 0, 16, 0, 17, 18, 19, 0,
+ 21, 0, 0, 0, 0, 22, 23, 24, 25, 0,
+ 0, 0, 26, 0, 0, 30, 31, 32, 33, 0,
+ 0, 53, 57, 57, 53, 53, 53, 53, 53, 53,
+ 0, 53, 55, 0, 0, 0, 0, 0, 0, 0,
+ 0, 57, 53, 53, 0, 0, 53, 53, 0, 0,
+ 0, 0, 57, 57, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 55,
+ 0, 0, 0, 55, 55, 0, 55, 53, 53, 55,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 55, 55, 55, 0, 55, 55, 53, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 11, 53, 53, 0,
+ 12, 13, 0, 0, 0, 14, 15, 0, 0, 0,
+ 0, 0, 0, 0, 0, 55, 55, 16, 0, 17,
+ 18, 19, 0, 21, 0, 0, 0, 0, 22, 23,
+ 24, 25, 0, 0, 55, 26, 0, 29, 30, 31,
+ 32, 33, 0, 0, 0, 55, 55, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 0, 0, 57, 57, 0,
+ 0, 0, 0, 57, 0, 0, 0, 0, 0, 57,
+ 0, 57, 57, 57, 57, 57, 0, 57, 57, 57,
+ 57, 0, 57, 57, 57, 57, 0, 57, 0, 57,
+ 57, 57, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 53, 53, 53, 53, 53, 53, 53, 53, 53,
+ 0, 0, 53, 53, 0, 0, 0, 0, 53, 0,
+ 0, 0, 0, 0, 53, 0, 53, 53, 53, 53,
+ 53, 28, 53, 53, 53, 53, 0, 53, 53, 53,
+ 53, 0, 53, 0, 53, 53, 53, 0, 0, 55,
+ 55, 55, 55, 55, 55, 0, 0, 55, 0, 0,
+ 0, 0, 0, 0, 0, 0, 55, 0, 28, 0,
+ 0, 0, 28, 28, 0, 28, 24, 55, 28, 0,
+ 55, 55, 55, 55, 55, 55, 55, 55, 55, 28,
+ 28, 28, 55, 28, 28, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 24, 0, 0, 0, 24, 24, 0,
+ 24, 26, 0, 24, 28, 28, 0, 0, 0, 0,
+ 0, 0, 0, 0, 24, 24, 24, 0, 24, 24,
+ 0, 0, 0, 28, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 28, 28, 0, 0, 26, 0,
+ 0, 0, 26, 26, 0, 26, 0, 0, 26, 24,
+ 24, 0, 0, 0, 0, 0, 0, 0, 0, 26,
+ 26, 26, 0, 26, 26, 0, 0, 0, 24, 0,
+ 25, 0, 0, 0, 0, 0, 0, 0, 0, 24,
+ 24, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 26, 26, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 25, 0, 0,
+ 0, 25, 25, 26, 25, 0, 0, 25, 0, 0,
+ 0, 0, 0, 0, 26, 26, 0, 0, 25, 25,
+ 25, 0, 25, 25, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 25, 25, 0, 0, 0, 28, 28,
+ 28, 28, 28, 28, 0, 0, 28, 0, 0, 0,
+ 0, 0, 25, 0, 0, 28, 0, 0, 0, 0,
+ 0, 0, 0, 25, 25, 0, 28, 0, 0, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 0, 0,
+ 0, 28, 0, 24, 24, 24, 24, 24, 24, 0,
+ 66, 24, 0, 27, 0, 28, 0, 0, 0, 0,
+ 24, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 24, 0, 0, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 0, 0, 0, 24, 0, 26, 26,
+ 26, 26, 26, 26, 0, 0, 26, 0, 0, 0,
+ 0, 0, 0, 0, 0, 26, 0, 0, 56, 0,
+ 0, 0, 0, 0, 0, 0, 26, 0, 0, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 0, 0,
+ 0, 26, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 56, 0, 0, 0, 56,
+ 56, 0, 56, 0, 0, 56, 0, 25, 25, 25,
+ 25, 25, 25, 0, 0, 25, 56, 56, 56, 0,
+ 56, 56, 0, 0, 25, 87, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 25, 0, 0, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 0, 0, 0,
+ 25, 56, 56, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 87, 0, 0, 0, 87, 87, 0, 87,
+ 56, 0, 87, 88, 0, 0, 0, 0, 0, 0,
+ 0, 56, 56, 87, 87, 87, 0, 87, 87, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 88, 0, 0, 0, 88, 88, 0, 88, 87, 87,
+ 88, 125, 126, 12, 13, 0, 0, 0, 14, 15,
+ 0, 88, 88, 88, 0, 88, 88, 87, 0, 0,
+ 16, 0, 17, 18, 19, 89, 21, 0, 87, 87,
+ 0, 22, 23, 24, 25, 0, 0, 0, 26, 0,
+ 0, 30, 31, 32, 33, 0, 88, 88, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 89, 0, 0, 88, 89, 89, 0, 89,
+ 0, 0, 89, 0, 0, 0, 88, 88, 0, 0,
+ 0, 0, 0, 89, 89, 89, 0, 89, 89, 0,
+ 0, 0, 0, 0, 0, 56, 56, 56, 56, 56,
+ 56, 0, 0, 56, 66, 235, 0, 27, 236, 28,
+ 0, 0, 56, 85, 0, 0, 0, 0, 89, 89,
+ 0, 0, 0, 56, 0, 0, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 0, 0, 89, 56, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 89, 89,
+ 85, 0, 0, 0, 85, 85, 0, 0, 0, 0,
+ 85, 0, 87, 87, 87, 87, 87, 87, 0, 0,
+ 87, 85, 85, 85, 0, 85, 85, 0, 0, 87,
+ 0, 0, 0, 66, 237, 0, 27, 238, 28, 0,
+ 87, 0, 0, 87, 87, 87, 0, 0, 0, 0,
+ 87, 87, 0, 0, 0, 87, 85, 85, 0, 0,
+ 88, 88, 88, 88, 88, 88, 0, 0, 88, 0,
+ 0, 0, 0, 0, 0, 85, 0, 88, 0, 0,
+ 16, 0, 0, 0, 0, 0, 85, 85, 88, 0,
+ 0, 88, 88, 88, 0, 0, 0, 0, 88, 88,
+ 0, 0, 0, 88, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 16, 0, 0,
+ 0, 16, 16, 0, 16, 0, 0, 16, 0, 0,
+ 0, 0, 89, 89, 89, 89, 89, 89, 16, 16,
+ 89, 0, 0, 16, 0, 0, 0, 0, 0, 89,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 89, 0, 0, 89, 89, 89, 0, 0, 0, 0,
+ 89, 89, 0, 16, 16, 89, 0, 12, 13, 0,
+ 0, 0, 14, 15, 0, 0, 0, 0, 0, 0,
+ 0, 0, 16, 0, 16, 0, 17, 18, 19, 0,
+ 21, 0, 0, 16, 16, 22, 23, 24, 25, 0,
+ 0, 0, 26, 0, 0, 30, 31, 32, 33, 0,
+ 85, 85, 85, 85, 85, 85, 0, 0, 85, 0,
+ 0, 0, 0, 0, 0, 0, 0, 85, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 85, 0,
+ 0, 85, 85, 85, 0, 0, 12, 13, 85, 85,
+ 0, 14, 15, 85, 0, 0, 0, 0, 0, 0,
+ 0, 41, 0, 16, 0, 17, 18, 19, 53, 21,
+ 0, 0, 0, 0, 22, 23, 24, 25, 0, 0,
+ 0, 26, 0, 41, 30, 31, 32, 33, 0, 41,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 105, 53, 108, 0, 0, 0, 112,
+ 113, 0, 116, 118, 119, 0, 53, 53, 123, 124,
+ 41, 0, 0, 0, 0, 0, 0, 16, 16, 16,
+ 16, 16, 16, 0, 0, 16, 0, 0, 41, 138,
+ 139, 140, 141, 0, 16, 0, 0, 0, 0, 0,
+ 0, 0, 0, 41, 0, 16, 0, 0, 16, 16,
+ 16, 108, 0, 0, 0, 0, 0, 0, 160, 0,
+ 16, 0, 0, 0, 53, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 102, 103, 41, 41,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 199, 200, 0, 0, 202, 203, 204, 205, 207,
+ 133, 134, 135, 0, 209, 210, 0, 53, 53, 0,
+ 0, 0, 217, 0, 0, 0, 218, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 157,
+ 158, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 41, 41,
+ 254, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 266, 0, 267, 0,
+ 268, 0, 269, 0, 270, 0, 0, 0, 0, 272,
+ 0, 0, 0, 0, 273, 274, 0, 0, 0, 0,
+ 0, 211, 280, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 219, 0, 221, 0, 222, 0, 0, 0,
+ 0, 0, 41, 0, 0, 0, 0, 0, 0, 0,
+ 0, 66, 0, 0, 27, 0, 28, 243, 0, 41,
+ 0, 0, 0, 0, 0, 0, 0, 0, 57, 0,
+ 187, 0, 260, 261, 262, 263, 264, 0, 0, 68,
+ 68, 0, 71, 72, 0, 68, 0, 0, 0, 0,
+ 0, 0, 0, 68, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 68, 0, 0, 0, 0,
+ 0, 0, 0, 109, 0, 0, 109, 0, 109, 0,
+ 288, 0, 0, 0, 290, 291, 292, 0, 0, 0,
+ 297, 0, 109, 0, 185, 0, 0, 0, 0, 0,
+ 0, 0, 0, 68, 68, 68, 68, 68, 68, 68,
+ 0, 0, 0, 0, 0, 310, 311, 68, 0, 0,
+ 68, 315, 0, 0, 68, 68, 0, 0, 68, 0,
+ 68, 68, 0, 0, 0, 68, 68, 0, 0, 0,
+ 108, 0, 0, 108, 0, 108, 0, 0, 0, 0,
+ 68, 68, 68, 68, 0, 0, 109, 0, 109, 108,
+ 0, 0, 196, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 68, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 102, 0, 0, 102, 0,
+ 102, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 68, 0, 0, 0, 102, 0, 0, 68, 0, 0,
+ 0, 68, 68, 0, 68, 68, 68, 68, 0, 68,
+ 0, 68, 68, 108, 0, 108, 0, 0, 0, 68,
+ 68, 172, 125, 126, 12, 13, 0, 173, 174, 14,
+ 15, 175, 0, 176, 177, 0, 178, 179, 180, 181,
+ 182, 16, 183, 17, 18, 19, 0, 21, 184, 0,
+ 0, 0, 22, 23, 24, 25, 68, 0, 102, 26,
+ 102, 0, 30, 31, 32, 33, 0, 0, 68, 68,
+ 68, 68, 68, 0, 68, 68, 68, 0, 0, 0,
+ 0, 0, 68, 109, 0, 0, 109, 109, 0, 109,
+ 109, 109, 109, 109, 109, 109, 109, 0, 109, 109,
+ 109, 109, 109, 109, 109, 109, 109, 109, 0, 109,
+ 109, 0, 0, 0, 109, 109, 109, 109, 0, 0,
+ 0, 109, 0, 0, 109, 109, 109, 109, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 108, 0, 0, 108, 108, 0, 108, 108, 108, 108,
+ 108, 108, 108, 108, 0, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 0, 108, 108, 0, 0,
+ 0, 108, 108, 108, 108, 0, 0, 0, 108, 0,
+ 0, 108, 108, 108, 108, 102, 0, 0, 102, 102,
+ 0, 102, 102, 102, 102, 102, 102, 102, 102, 0,
+ 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
+ 0, 102, 102, 0, 0, 0, 102, 102, 102, 102,
+ 0, 0, 0, 102, 0, 0, 102, 102, 102, 102,
+ 103, 0, 0, 103, 0, 103, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 103,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 104, 0, 0, 104, 0,
+ 104, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 104, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 103, 0, 103, 0, 0, 0, 0,
+ 101, 0, 0, 101, 0, 101, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 101,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 104, 0,
+ 104, 0, 0, 0, 0, 0, 107, 0, 0, 107,
+ 0, 107, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 107, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 101, 0, 101, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 35, 0, 0, 27, 0, 28, 0, 0, 0, 107,
+ 103, 107, 0, 103, 103, 0, 103, 103, 103, 103,
+ 103, 103, 103, 103, 0, 103, 103, 103, 103, 103,
+ 103, 103, 103, 103, 103, 0, 103, 103, 0, 0,
+ 0, 103, 103, 103, 103, 0, 0, 0, 103, 0,
+ 0, 103, 103, 103, 103, 104, 0, 0, 104, 104,
+ 0, 104, 104, 104, 104, 104, 104, 104, 104, 0,
+ 104, 104, 104, 104, 104, 104, 104, 104, 104, 104,
+ 0, 104, 104, 34, 0, 0, 104, 104, 104, 104,
+ 0, 0, 0, 104, 0, 0, 104, 104, 104, 104,
+ 101, 0, 0, 101, 101, 0, 101, 101, 101, 101,
+ 101, 101, 101, 101, 0, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 35, 101, 101, 27, 0,
+ 28, 101, 101, 101, 101, 0, 0, 0, 101, 0,
+ 0, 101, 101, 101, 101, 0, 107, 0, 0, 107,
+ 107, 0, 107, 107, 107, 107, 107, 107, 107, 107,
+ 0, 107, 107, 107, 107, 107, 107, 107, 107, 107,
+ 107, 0, 107, 107, 0, 0, 0, 107, 107, 107,
+ 107, 0, 0, 0, 107, 0, 0, 107, 107, 107,
+ 107, 136, 0, 0, 136, 0, 136, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 136, 0, 0, 0, 0, 0, 0, 9, 10, 11,
+ 0, 0, 0, 12, 13, 0, 0, 0, 14, 15,
+ 0, 0, 0, 0, 0, 0, 66, 0, 0, 27,
+ 16, 28, 17, 18, 19, 20, 21, 0, 0, 0,
+ 0, 22, 23, 24, 25, 187, 0, 0, 26, 0,
+ 29, 30, 31, 32, 33, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 136, 0, 136, 0, 0, 0,
+ 0, 66, 0, 0, 27, 0, 28, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 187, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 185,
+ 0, 186, 0, 0, 0, 0, 0, 66, 0, 0,
+ 27, 0, 28, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 11, 0, 187, 0, 12, 13,
+ 0, 0, 0, 14, 15, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 185, 16, 197, 17, 18, 19,
+ 0, 21, 0, 0, 0, 0, 22, 23, 24, 25,
+ 0, 0, 0, 26, 0, 29, 30, 31, 32, 33,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 35, 0, 0, 27, 0, 28, 0, 0, 0,
+ 185, 136, 198, 0, 136, 136, 0, 136, 136, 136,
+ 136, 136, 136, 136, 136, 0, 136, 136, 136, 136,
+ 136, 136, 136, 136, 136, 136, 0, 136, 136, 0,
+ 0, 0, 136, 136, 136, 136, 0, 0, 0, 136,
+ 0, 0, 136, 136, 136, 136, 172, 0, 0, 12,
+ 13, 0, 173, 174, 14, 15, 175, 0, 176, 177,
+ 0, 178, 179, 180, 181, 182, 16, 183, 17, 18,
+ 19, 0, 21, 184, 0, 0, 0, 22, 23, 24,
+ 25, 0, 0, 0, 26, 0, 0, 30, 31, 32,
+ 33, 172, 0, 0, 12, 13, 0, 173, 174, 14,
+ 15, 175, 0, 176, 177, 0, 178, 179, 180, 181,
+ 182, 16, 183, 17, 18, 19, 107, 21, 184, 27,
+ 0, 28, 22, 23, 24, 25, 0, 0, 0, 26,
+ 0, 0, 30, 31, 32, 33, 0, 172, 0, 0,
+ 12, 13, 0, 173, 174, 14, 15, 175, 0, 176,
+ 177, 0, 178, 179, 180, 181, 182, 16, 183, 17,
+ 18, 19, 0, 21, 184, 0, 0, 0, 22, 23,
+ 24, 25, 0, 0, 0, 26, 0, 0, 30, 31,
+ 32, 33, 66, 0, 0, 27, 0, 28, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 187, 0, 0, 0, 0, 0, 0, 0, 0,
+ 11, 0, 125, 126, 12, 13, 0, 0, 0, 14,
+ 15, 0, 0, 0, 0, 0, 0, 66, 0, 0,
+ 27, 16, 28, 17, 18, 19, 0, 21, 0, 0,
+ 0, 0, 22, 23, 24, 25, 187, 0, 0, 26,
+ 0, 29, 30, 31, 32, 33, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 185, 0, 228, 0, 0,
+ 0, 0, 66, 0, 0, 27, 0, 28, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 187, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 185, 0, 281, 0, 0, 0, 0, 0, 106, 0,
+ 0, 106, 0, 106, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 11, 0, 106, 0, 12,
+ 13, 0, 0, 0, 14, 15, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 185, 16, 305, 17, 18,
+ 19, 0, 21, 0, 0, 0, 0, 22, 23, 24,
+ 25, 0, 0, 0, 26, 0, 29, 30, 31, 32,
+ 33, 66, 239, 0, 27, 240, 28, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 106, 172, 106, 0, 12, 13, 0, 173, 174,
+ 14, 15, 175, 0, 176, 177, 0, 178, 179, 180,
+ 181, 182, 16, 183, 17, 18, 19, 0, 21, 184,
+ 0, 0, 0, 22, 23, 24, 25, 0, 0, 0,
+ 26, 0, 0, 30, 31, 32, 33, 172, 0, 0,
+ 12, 13, 0, 173, 174, 14, 15, 175, 0, 176,
+ 177, 0, 178, 179, 180, 181, 182, 16, 183, 17,
+ 18, 19, 66, 21, 184, 27, 110, 28, 22, 23,
+ 24, 25, 0, 0, 0, 26, 0, 0, 30, 31,
+ 32, 33, 172, 0, 0, 12, 13, 0, 173, 174,
+ 14, 15, 175, 0, 176, 177, 0, 178, 179, 180,
+ 181, 182, 16, 183, 17, 18, 19, 0, 21, 184,
+ 0, 0, 0, 22, 23, 24, 25, 0, 0, 0,
+ 26, 0, 0, 30, 31, 32, 33, 0, 106, 0,
+ 0, 106, 106, 0, 106, 106, 106, 106, 106, 0,
+ 106, 106, 0, 106, 106, 106, 106, 106, 106, 106,
+ 106, 106, 106, 0, 106, 106, 0, 0, 0, 106,
+ 106, 106, 106, 0, 0, 0, 106, 0, 0, 106,
+ 106, 106, 106, 129, 0, 0, 129, 0, 129, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 129, 0, 12, 13, 0, 0, 0, 14,
+ 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 16, 0, 17, 18, 19, 0, 21, 0, 0,
+ 0, 0, 22, 23, 24, 25, 0, 0, 0, 26,
+ 0, 0, 30, 31, 32, 33, 66, 244, 0, 27,
+ 245, 28, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 129, 0, 129, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 66, 104,
+ 0, 27, 0, 28, 0, 12, 13, 0, 0, 0,
+ 14, 15, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 16, 0, 17, 18, 19, 0, 21, 0,
+ 0, 0, 0, 22, 23, 24, 25, 0, 0, 0,
+ 26, 0, 0, 30, 31, 32, 33, 66, 0, 0,
+ 27, 0, 28, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 66, 0, 0, 27, 0, 28, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 129, 0, 0, 129, 129, 0, 129,
+ 129, 129, 129, 129, 0, 129, 129, 0, 129, 129,
+ 129, 129, 129, 129, 129, 129, 129, 129, 0, 129,
+ 129, 0, 0, 0, 129, 129, 129, 129, 0, 0,
+ 0, 129, 0, 0, 129, 129, 129, 129, 66, 0,
+ 0, 27, 0, 28, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 12,
+ 13, 0, 0, 0, 14, 15, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 16, 0, 17, 18,
+ 19, 0, 21, 0, 0, 0, 0, 22, 23, 24,
+ 25, 0, 0, 0, 26, 0, 0, 30, 31, 32,
+ 33, 12, 13, 0, 0, 0, 14, 15, 66, 154,
+ 0, 27, 0, 28, 0, 0, 0, 0, 16, 0,
+ 17, 18, 19, 0, 21, 0, 0, 0, 0, 22,
+ 23, 24, 25, 0, 0, 0, 26, 0, 0, 30,
+ 31, 32, 33, 0, 0, 0, 115, 0, 0, 0,
+ 12, 13, 0, 0, 66, 14, 15, 27, 159, 28,
+ 0, 0, 0, 0, 0, 0, 0, 16, 0, 17,
+ 18, 19, 0, 21, 0, 0, 0, 0, 22, 23,
+ 24, 25, 0, 0, 0, 26, 0, 0, 30, 31,
+ 32, 33, 117, 0, 0, 0, 12, 13, 0, 0,
+ 66, 14, 15, 27, 162, 28, 0, 0, 0, 0,
+ 0, 0, 0, 16, 0, 17, 18, 19, 0, 21,
+ 0, 0, 0, 0, 22, 23, 24, 25, 0, 0,
+ 0, 26, 0, 0, 30, 31, 32, 33, 0, 0,
+ 0, 0, 66, 0, 0, 27, 164, 28, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 137, 0, 0,
+ 0, 12, 13, 0, 0, 0, 14, 15, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 16, 0,
+ 17, 18, 19, 0, 21, 0, 0, 0, 0, 22,
+ 23, 24, 25, 0, 0, 0, 26, 0, 0, 30,
+ 31, 32, 33, 66, 0, 0, 27, 165, 28, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 12, 13, 0, 0, 0, 14, 15, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 16, 0,
+ 17, 18, 19, 66, 21, 0, 27, 170, 28, 22,
+ 23, 24, 25, 0, 0, 0, 26, 0, 0, 30,
+ 31, 32, 33, 0, 0, 0, 0, 12, 13, 0,
+ 0, 0, 14, 15, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 16, 0, 17, 18, 19, 66,
+ 21, 0, 27, 171, 28, 22, 23, 24, 25, 0,
+ 0, 0, 26, 0, 0, 30, 31, 32, 33, 0,
+ 0, 0, 0, 12, 13, 0, 0, 0, 14, 15,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 16, 0, 17, 18, 19, 66, 21, 0, 27, 0,
+ 28, 22, 23, 24, 25, 0, 0, 0, 26, 0,
+ 0, 30, 31, 32, 33, 12, 13, 0, 0, 0,
+ 14, 15, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 16, 0, 17, 18, 19, 0, 21, 0,
+ 0, 0, 0, 22, 23, 24, 25, 0, 0, 0,
+ 26, 0, 0, 30, 31, 32, 33, 66, 242, 0,
+ 27, 0, 28, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 12, 13, 0, 0,
+ 0, 14, 15, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 16, 0, 17, 18, 19, 0, 21,
+ 0, 0, 0, 0, 22, 23, 24, 25, 0, 0,
+ 0, 26, 0, 0, 30, 31, 32, 33, 66, 246,
+ 0, 27, 0, 28, 0, 0, 12, 13, 0, 0,
+ 0, 14, 15, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 16, 0, 17, 18, 19, 0, 21,
+ 0, 0, 0, 0, 22, 23, 24, 25, 0, 0,
+ 0, 26, 0, 0, 30, 31, 32, 33, 0, 0,
+ 0, 66, 12, 13, 27, 0, 28, 14, 15, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,
+ 0, 17, 18, 19, 0, 21, 0, 0, 0, 0,
+ 22, 23, 24, 25, 0, 0, 0, 26, 0, 0,
+ 30, 31, 32, 33, 206, 0, 0, 0, 12, 13,
+ 0, 0, 0, 14, 15, 66, 277, 0, 27, 0,
+ 28, 0, 0, 0, 0, 16, 0, 17, 18, 19,
+ 0, 21, 0, 0, 0, 0, 22, 23, 24, 25,
+ 0, 0, 0, 26, 0, 0, 30, 31, 32, 33,
+ 0, 0, 0, 0, 0, 0, 0, 66, 283, 0,
+ 27, 0, 28, 0, 0, 0, 0, 0, 0, 0,
+ 12, 13, 0, 0, 0, 14, 15, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 16, 0, 17,
+ 18, 19, 0, 21, 0, 0, 0, 0, 22, 23,
+ 24, 25, 0, 0, 0, 26, 0, 0, 30, 31,
+ 32, 33, 66, 284, 0, 27, 0, 28, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 12, 13, 0, 0, 0, 14, 15, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 16, 0,
+ 17, 18, 19, 0, 21, 0, 0, 0, 0, 22,
+ 23, 24, 25, 0, 0, 0, 26, 0, 0, 30,
+ 31, 32, 33, 66, 285, 0, 27, 0, 28, 0,
+ 265, 0, 0, 0, 12, 13, 0, 0, 0, 14,
+ 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 16, 0, 17, 18, 19, 0, 21, 0, 0,
+ 0, 0, 22, 23, 24, 25, 0, 0, 0, 26,
+ 0, 0, 30, 31, 32, 33, 66, 286, 0, 27,
+ 0, 28, 0, 0, 0, 0, 0, 0, 12, 13,
+ 0, 0, 0, 14, 15, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 16, 0, 17, 18, 19,
+ 0, 21, 0, 0, 0, 0, 22, 23, 24, 25,
+ 0, 0, 0, 26, 0, 0, 30, 31, 32, 33,
+ 12, 13, 0, 0, 0, 14, 15, 66, 287, 0,
+ 27, 0, 28, 0, 0, 0, 0, 16, 0, 17,
+ 18, 19, 0, 21, 0, 0, 0, 0, 22, 23,
+ 24, 25, 0, 0, 0, 26, 0, 0, 30, 31,
+ 32, 33, 0, 0, 0, 0, 0, 0, 0, 66,
+ 289, 0, 27, 0, 28, 12, 13, 0, 0, 0,
+ 14, 15, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 16, 0, 17, 18, 19, 0, 21, 0,
+ 0, 0, 0, 22, 23, 24, 25, 0, 0, 0,
+ 26, 0, 0, 30, 31, 32, 33, 66, 0, 0,
+ 27, 0, 28, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 12, 13, 0, 0,
+ 0, 14, 15, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 16, 0, 17, 18, 19, 0, 21,
+ 0, 0, 0, 0, 22, 23, 24, 25, 0, 0,
+ 296, 26, 0, 0, 30, 31, 32, 33, 51, 0,
+ 0, 27, 0, 28, 0, 0, 0, 0, 0, 12,
+ 13, 0, 0, 0, 14, 15, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 16, 0, 17, 18,
+ 19, 0, 21, 0, 0, 0, 0, 22, 23, 24,
+ 25, 0, 0, 0, 26, 0, 0, 30, 31, 32,
+ 33, 66, 0, 0, 27, 0, 28, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 12, 13, 0, 0, 0, 14, 15, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 16, 0, 17,
+ 18, 19, 0, 21, 0, 0, 0, 0, 22, 23,
+ 24, 25, 0, 0, 0, 26, 0, 0, 30, 31,
+ 32, 33, 12, 13, 0, 0, 0, 14, 15, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,
+ 0, 17, 18, 19, 0, 21, 0, 0, 0, 0,
+ 22, 23, 24, 25, 0, 0, 0, 26, 0, 0,
+ 30, 31, 32, 33, 0, 0, 0, 0, 0, 0,
+ 12, 13, 0, 0, 0, 14, 15, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 16, 0, 17,
+ 18, 19, 0, 21, 0, 0, 0, 0, 22, 23,
+ 24, 25, 0, 0, 0, 26, 0, 0, 30, 31,
+ 32, 33, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 12, 13, 0, 0, 0, 14, 15, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 16, 0,
+ 17, 18, 19, 0, 21, 0, 0, 0, 0, 22,
+ 23, 24, 25, 0, 0, 0, 26, 67, 69, 30,
+ 31, 32, 33, 73, 0, 0, 0, 0, 0, 0,
+ 0, 89, 0, 0, 12, 13, 0, 0, 0, 14,
+ 15, 0, 0, 89, 0, 0, 0, 0, 0, 0,
+ 0, 16, 0, 17, 18, 19, 0, 21, 0, 0,
+ 0, 0, 22, 23, 24, 25, 0, 0, 0, 26,
+ 0, 0, 30, 31, 32, 33, 0, 0, 0, 0,
+ 0, 143, 144, 145, 146, 147, 148, 149, 0, 0,
+ 0, 0, 0, 0, 0, 89, 0, 0, 89, 0,
+ 0, 0, 89, 89, 0, 0, 89, 0, 89, 89,
+ 0, 0, 0, 89, 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 89, 89,
+ 89, 89, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 89, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 89, 0,
+ 0, 0, 0, 0, 0, 229, 0, 0, 0, 89,
+ 89, 0, 89, 89, 89, 89, 0, 89, 0, 89,
+ 89, 0, 0, 0, 0, 0, 0, 89, 89, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 89, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 89, 89, 89, 89,
+ 89, 0, 89, 89, 89, 0, 0, 0, 0, 0,
+ 89,
+};
+short yycheck[] = { 7,
+ 0, 41, 44, 41, 37, 41, 59, 41, 123, 42,
+ 43, 41, 45, 59, 47, 35, 59, 38, 41, 51,
+ 94, 29, 293, 62, 123, 58, 102, 103, 40, 59,
+ 63, 35, 303, 35, 301, 302, 123, 37, 112, 292,
+ 40, 41, 42, 43, 44, 45, 40, 47, 219, 40,
+ 303, 304, 41, 288, 216, 0, 40, 133, 58, 59,
+ 60, 94, 62, 63, 40, 60, 301, 302, 50, 41,
+ 59, 41, 44, 62, 44, 107, 84, 261, 262, 112,
+ 62, 63, 289, 290, 40, 124, 41, 107, 289, 290,
+ 286, 99, 37, 93, 94, 91, 41, 42, 43, 44,
+ 45, 40, 47, 107, 59, 107, 261, 41, 289, 290,
+ 0, 41, 112, 58, 59, 60, 40, 62, 63, 290,
+ 291, 292, 40, 123, 124, 59, 134, 135, 44, 59,
+ 123, 293, 289, 290, 60, 124, 40, 112, 120, 310,
+ 311, 303, 41, 41, 315, 221, 41, 37, 93, 94,
+ 40, 41, 42, 43, 37, 45, 41, 47, 292, 42,
+ 43, 59, 45, 280, 47, 41, 41, 112, 58, 59,
+ 60, 44, 62, 63, 261, 262, 197, 44, 123, 124,
+ 63, 41, 93, 292, 59, 41, 40, 40, 40, 292,
+ 290, 173, 174, 41, 91, 59, 41, 272, 40, 271,
+ 41, 41, 0, 93, 94, 40, 214, 215, 43, 44,
+ 45, 94, 288, 41, 260, 261, 262, 260, 261, 262,
+ 59, 93, 112, 59, 41, 60, 265, 62, 213, 112,
+ 260, 261, 262, 123, 124, -1, 289, 290, -1, -1,
+ -1, 274, -1, -1, -1, -1, -1, 289, 290, 289,
+ 290, 289, 290, 289, 290, 289, 290, 257, 258, 259,
+ 260, 261, 262, 263, 264, 265, 289, 290, 268, 269,
+ 278, 260, 261, 262, 274, -1, 265, -1, -1, -1,
+ 280, 41, 282, 283, 284, 285, 286, 295, 288, 289,
+ 290, 291, 292, 293, 294, 295, 296, 292, 298, 59,
+ 300, 301, 302, 303, 304, 260, 261, 262, 303, 304,
+ -1, -1, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, -1, -1, 268, 269, -1, 260, 261, 262, 274,
+ 260, 261, 262, -1, -1, 280, 41, 282, 283, 284,
+ 285, 286, -1, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, -1, 298, 59, 300, 301, 302, 303, 304,
+ -1, -1, 260, 261, 262, -1, -1, 257, 258, 259,
+ 260, 261, 262, 263, 264, 265, -1, -1, 268, 269,
+ 0, -1, -1, -1, 274, 260, 261, 262, -1, -1,
+ 280, 274, 282, 283, 284, 285, 286, 41, 288, 289,
+ 290, 291, 292, 293, 294, 295, 296, -1, 298, -1,
+ 300, 301, 302, 303, 304, 59, -1, 37, -1, -1,
+ 40, 41, 42, 43, 44, 45, -1, 47, 263, 264,
+ -1, 37, -1, 268, 269, 0, 42, -1, 58, 59,
+ -1, 47, 62, 63, -1, 280, -1, 282, 283, 284,
+ -1, 286, -1, -1, -1, -1, 291, 292, 293, 294,
+ 295, 296, -1, 298, -1, -1, 301, 302, 303, 304,
+ -1, -1, 37, 93, 94, 40, 41, 42, 43, 44,
+ 45, -1, 47, -1, -1, -1, -1, -1, 94, -1,
+ 0, -1, 112, 58, 59, 60, -1, 62, 63, -1,
+ 260, 261, 262, 123, 124, -1, 112, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 37, 93, 94,
+ 40, 41, 42, 43, 44, 45, -1, 47, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 58, 59,
+ 60, -1, 62, 63, -1, 260, 261, 262, 123, 124,
+ -1, -1, -1, 0, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 93, 94, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 37, -1, 112, 40, 41, 42, 43, 44, 45, -1,
+ 47, -1, -1, 123, 124, -1, 260, 261, 262, -1,
+ -1, 58, 59, 60, -1, 62, 63, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 257, 258, 259,
+ 260, 261, 262, 263, 264, 265, -1, -1, 268, 269,
+ -1, -1, -1, -1, 274, -1, 93, -1, -1, -1,
+ 280, -1, 282, 283, 284, 285, 286, -1, 288, 289,
+ 290, 291, 292, 293, 294, 295, 296, -1, 298, -1,
+ 300, 301, 302, 303, 304, -1, 123, 124, -1, -1,
+ -1, -1, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, -1, -1, 268, 269, -1, -1, -1, -1, 274,
+ -1, -1, -1, -1, -1, 280, -1, 282, 283, 284,
+ 285, 286, -1, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, -1, 298, -1, 300, 301, 302, 303, 304,
+ -1, -1, -1, -1, -1, -1, -1, 257, 258, 259,
+ 260, 261, 262, 263, 264, 265, -1, -1, 268, 269,
+ -1, -1, -1, -1, 274, -1, -1, -1, -1, -1,
+ 280, -1, 282, 283, 284, 285, 286, -1, 288, 289,
+ -1, 291, 292, 293, 294, 295, 296, -1, 298, -1,
+ 300, 301, 302, 303, 304, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 257, 258, 259, 260, 261, 262, 263, 264, 265, -1,
+ -1, 268, 269, 0, -1, -1, -1, 274, -1, -1,
+ -1, -1, -1, 280, -1, 282, 283, 284, 285, 286,
+ -1, 288, 289, 290, 291, 292, 293, 294, 295, 296,
+ -1, 298, -1, 300, 301, 302, 303, 304, -1, -1,
+ 37, -1, -1, 40, 41, 42, 43, 44, 45, -1,
+ 47, -1, -1, -1, -1, -1, -1, -1, 0, -1,
+ -1, 58, 59, 60, -1, 62, 63, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 37, 93, 94, 40, 41,
+ 42, 43, 44, 45, -1, 47, -1, -1, -1, -1,
+ -1, -1, 0, -1, -1, 112, 58, 59, 60, -1,
+ 62, 63, -1, -1, -1, -1, 123, 124, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 37,
+ -1, 93, 40, 41, 42, 43, 44, 45, -1, 47,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 58, 59, 60, -1, 62, 63, -1, -1, -1, -1,
+ -1, 123, 124, -1, -1, 0, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 93, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 37, -1, -1, 40, 41, 42, 43, 44,
+ 45, -1, 47, -1, -1, 123, 124, -1, -1, -1,
+ -1, -1, -1, 58, 59, 60, -1, 62, 63, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 257, 258, 259, 260, 261, 262, 263, 264, 265, -1,
+ -1, 268, 269, -1, -1, -1, -1, 274, 93, -1,
+ -1, -1, -1, 280, -1, 282, 283, 284, 285, 286,
+ -1, 288, 289, 290, 291, 292, 293, 294, 295, 296,
+ -1, 298, -1, 300, -1, -1, 303, 304, 123, 124,
+ -1, -1, -1, -1, -1, 257, 258, 259, 260, 261,
+ 262, 263, 264, 265, -1, -1, 268, 269, -1, -1,
+ -1, -1, 274, -1, -1, -1, -1, -1, 280, -1,
+ 282, 283, 284, 285, 286, -1, 288, 289, 290, 291,
+ 292, 293, 294, 295, 296, -1, 298, -1, 300, 301,
+ 302, 303, 304, -1, -1, -1, -1, -1, -1, 257,
+ 258, 259, 260, 261, 262, 263, 264, 265, 0, -1,
+ 268, 269, -1, -1, -1, -1, 274, -1, -1, -1,
+ -1, -1, 280, -1, 282, 283, 284, 285, 286, -1,
+ 288, 289, 290, 291, 292, 293, 294, 295, 296, -1,
+ 298, -1, 300, 301, 302, 303, 304, -1, 40, -1,
+ -1, 43, -1, 45, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 59, -1, -1,
+ -1, -1, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, -1, -1, 268, 269, 0, -1, -1, -1, 274,
+ -1, -1, -1, -1, -1, 280, -1, 282, 283, 284,
+ 285, 286, -1, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, -1, 298, -1, 300, 301, 302, 303, 304,
+ -1, -1, 37, -1, -1, 40, 41, 42, 43, 44,
+ 45, 123, 47, -1, -1, -1, -1, -1, -1, 0,
+ -1, -1, -1, 58, 59, 60, -1, 62, 63, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 37, -1, 93, 40,
+ 41, 42, 43, 44, 45, -1, 47, -1, -1, -1,
+ -1, -1, -1, 0, -1, -1, -1, 58, 59, 60,
+ -1, 62, 63, -1, -1, -1, -1, -1, 123, 124,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 37, -1, 93, 40, 41, 42, 43, 44, 45, -1,
+ 47, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 58, 59, 60, -1, 62, 63, -1, -1, -1,
+ -1, -1, 123, 124, -1, 257, 258, 259, 260, 261,
+ 262, 263, 264, -1, -1, -1, 268, 269, -1, -1,
+ -1, -1, -1, -1, -1, -1, 93, 94, 280, -1,
+ 282, 283, 284, 285, 286, -1, -1, -1, -1, 291,
+ 292, 293, 294, -1, -1, 112, 298, -1, 300, 301,
+ 302, 303, 304, -1, -1, -1, 123, 124, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, -1, -1, 268, 269, -1, -1, -1, -1, 274,
+ -1, 40, -1, -1, 43, 280, 45, 282, 283, 284,
+ 285, 286, -1, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, -1, 298, -1, 300, 301, 302, 303, 304,
+ -1, -1, -1, -1, -1, -1, 257, 258, 259, 260,
+ 261, 262, 263, 264, 265, -1, -1, 268, 269, -1,
+ -1, -1, -1, 274, -1, -1, -1, -1, -1, 280,
+ -1, 282, 283, 284, 285, 286, -1, 288, 289, 290,
+ 291, 292, 293, 294, 295, 296, -1, 298, -1, 300,
+ 301, 302, 303, 304, 40, -1, -1, 43, -1, 45,
+ 257, 258, 259, 260, 261, 262, 263, 264, 265, -1,
+ -1, 268, 269, 0, 60, -1, 62, 274, -1, -1,
+ -1, -1, -1, 280, -1, 282, 283, 284, 285, 286,
+ -1, 288, -1, -1, 291, 292, 293, 294, 295, 296,
+ -1, 298, -1, 300, 301, 302, 303, 304, -1, -1,
+ 37, -1, -1, 40, 41, 42, 43, 44, 45, -1,
+ 47, -1, -1, -1, -1, -1, -1, -1, 0, -1,
+ -1, 58, 59, 60, -1, 62, 63, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 93, 94, 40, 41,
+ -1, 43, 44, 45, -1, -1, -1, -1, -1, 0,
+ -1, -1, -1, -1, -1, 112, 58, 59, 60, -1,
+ 62, 63, -1, -1, 263, 264, 123, 124, -1, 268,
+ 269, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 280, -1, 282, 283, 284, -1, 286, -1, 40,
+ 41, 93, 43, 44, 45, -1, -1, 0, -1, 298,
+ -1, -1, 301, 302, 303, 304, -1, 58, 59, 60,
+ -1, 62, 63, -1, -1, -1, -1, -1, -1, -1,
+ -1, 123, 124, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 40, 41, -1,
+ -1, 44, 93, -1, -1, -1, -1, 263, 264, -1,
+ -1, -1, 268, 269, -1, 58, 59, 60, -1, 62,
+ -1, -1, -1, -1, 280, -1, 282, 283, 284, -1,
+ 286, -1, 123, 124, -1, 291, 292, 293, 294, 295,
+ 296, -1, 298, -1, -1, 301, 302, 303, 304, -1,
+ 93, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 257, 258, 259, 260, 261, 262, 263, 264, 265, -1,
+ -1, 268, 269, -1, -1, -1, -1, 274, -1, -1,
+ 123, 124, -1, 280, -1, 282, 283, 284, 285, 286,
+ -1, -1, 289, 290, 291, 292, 293, 294, 295, 296,
+ -1, 298, -1, 300, -1, -1, 303, 304, -1, -1,
+ -1, -1, -1, -1, -1, 257, 258, 259, 260, 261,
+ 262, 263, 264, 265, -1, -1, 268, 269, -1, -1,
+ -1, -1, 274, -1, -1, -1, -1, -1, 280, -1,
+ 282, 283, 284, 285, 286, -1, 288, 289, 290, 291,
+ 292, 293, 294, 295, 296, -1, 298, -1, 300, 301,
+ 302, 303, 304, -1, -1, -1, 257, 258, 259, 260,
+ 261, 262, 263, 264, 265, -1, -1, 268, 269, -1,
+ -1, -1, -1, 274, -1, -1, -1, -1, -1, 280,
+ -1, 282, 283, 284, 285, 286, -1, 288, 289, 290,
+ 291, 292, 293, 294, 295, 296, -1, 298, -1, 300,
+ 301, 302, 303, 304, 257, 258, 259, 260, 261, 262,
+ 263, 264, 265, 0, -1, 268, 269, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 280, -1, 282,
+ 283, 284, 285, 286, -1, 288, 289, 290, 291, 292,
+ 293, 294, 295, 296, -1, 298, -1, 300, 301, 302,
+ 303, 304, -1, 40, 41, -1, -1, 44, -1, -1,
+ -1, 0, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 58, 59, 60, -1, 62, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 40, 41, -1, -1, 44, 93, -1, -1, -1,
+ 0, -1, -1, -1, -1, -1, -1, -1, -1, 58,
+ 59, 60, -1, 62, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 123, 124, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 40, -1, -1, 43, 93, 45, -1, -1, -1, -1,
+ 0, -1, -1, -1, -1, -1, -1, -1, -1, 59,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 123, 124, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 40, -1, -1, 43, -1, 45, -1, -1, 0, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 59,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 123, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 40, -1,
+ -1, 43, -1, 45, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 59, -1, -1,
+ 257, 258, 259, 260, 261, 262, 263, 264, 265, -1,
+ -1, 268, 269, 123, -1, -1, -1, -1, -1, -1,
+ -1, -1, 0, 280, -1, 282, 283, 284, 285, 286,
+ -1, 288, 289, 290, 291, 292, 293, 294, 295, 296,
+ -1, 298, -1, 300, 301, 302, 303, 304, 257, 258,
+ 259, 260, 261, 262, 263, 264, 265, -1, -1, 268,
+ 269, -1, 40, -1, -1, 43, -1, 45, -1, -1,
+ -1, 280, -1, 282, 283, 284, 285, 286, -1, 288,
+ 289, 290, 291, 292, 293, 294, 295, 296, -1, 298,
+ -1, 300, 301, 302, 303, 304, -1, 257, 258, 259,
+ 260, 261, 262, 263, 264, -1, -1, 40, 268, 269,
+ 43, -1, 45, -1, -1, -1, -1, -1, -1, -1,
+ 280, 0, 282, 283, 284, 285, 286, -1, -1, -1,
+ -1, 291, 292, 293, 294, -1, -1, -1, 298, -1,
+ 300, 301, 302, 303, 304, 123, -1, 257, 258, 259,
+ 260, -1, 262, 263, 264, -1, -1, -1, 268, 269,
+ -1, 40, -1, -1, 43, -1, 45, -1, -1, 0,
+ 280, -1, 282, 283, 284, 285, 286, -1, -1, -1,
+ -1, 291, 292, 293, 294, -1, -1, -1, 298, -1,
+ 300, 301, 302, 303, 304, 257, 258, 259, 260, 261,
+ 262, 263, 264, -1, -1, -1, 268, 269, -1, 40,
+ -1, -1, 43, -1, 45, -1, -1, -1, 280, -1,
+ 282, 283, 284, 285, 286, -1, -1, -1, -1, 291,
+ 292, 293, 294, -1, -1, -1, 298, -1, 300, 301,
+ 302, 303, 304, -1, 123, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 40, 41, -1, 43, 44, 45,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 257,
+ 258, 259, -1, -1, -1, 263, 264, -1, -1, -1,
+ 268, 269, 123, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 280, -1, 282, 283, 284, 285, 286, -1,
+ -1, -1, -1, 291, 292, 293, 294, -1, -1, -1,
+ 298, -1, 300, 301, 302, 303, 304, -1, -1, -1,
+ 263, 264, -1, 266, 267, 268, 269, -1, -1, -1,
+ -1, -1, 275, 276, 277, 278, 279, 280, -1, 282,
+ 283, 284, -1, 286, 287, -1, -1, -1, 291, 292,
+ 293, 294, -1, -1, -1, 298, -1, -1, 301, 302,
+ 303, 304, -1, -1, -1, -1, -1, -1, 257, 258,
+ 259, -1, -1, -1, 263, 264, -1, -1, -1, 268,
+ 269, -1, -1, -1, -1, -1, -1, 40, -1, -1,
+ 43, 280, 45, 282, 283, 284, 285, 286, -1, -1,
+ -1, -1, 291, 292, 293, 294, 59, -1, -1, 298,
+ -1, 300, 301, 302, 303, 304, 257, 258, 259, -1,
+ -1, -1, 263, 264, -1, -1, -1, 268, 269, 0,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 280,
+ -1, 282, 283, 284, 285, 286, -1, -1, -1, -1,
+ 291, 292, 293, 294, -1, -1, -1, 298, -1, 300,
+ 301, 302, 303, 304, -1, -1, 37, -1, -1, 40,
+ 41, 42, 43, 44, 45, -1, 47, 263, 264, -1,
+ -1, -1, 268, 269, 0, -1, -1, 58, 59, 60,
+ -1, 62, 63, -1, 280, -1, 282, 283, 284, -1,
+ 286, -1, -1, -1, -1, 291, 292, 293, 294, -1,
+ -1, -1, 298, -1, -1, 301, 302, 303, 304, -1,
+ -1, 37, 93, 94, 40, 41, 42, 43, 44, 45,
+ -1, 47, 0, -1, -1, -1, -1, -1, -1, -1,
+ -1, 112, 58, 59, -1, -1, 62, 63, -1, -1,
+ -1, -1, 123, 124, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 37,
+ -1, -1, -1, 41, 42, -1, 44, 93, 94, 47,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 58, 59, 60, -1, 62, 63, 112, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 259, 123, 124, -1,
+ 263, 264, -1, -1, -1, 268, 269, -1, -1, -1,
+ -1, -1, -1, -1, -1, 93, 94, 280, -1, 282,
+ 283, 284, -1, 286, -1, -1, -1, -1, 291, 292,
+ 293, 294, -1, -1, 112, 298, -1, 300, 301, 302,
+ 303, 304, -1, -1, -1, 123, 124, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 257, 258, 259, 260,
+ 261, 262, 263, 264, 265, -1, -1, 268, 269, -1,
+ -1, -1, -1, 274, -1, -1, -1, -1, -1, 280,
+ -1, 282, 283, 284, 285, 286, -1, 288, 289, 290,
+ 291, -1, 293, 294, 295, 296, -1, 298, -1, 300,
+ 301, 302, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 257, 258, 259, 260, 261, 262, 263, 264, 265,
+ -1, -1, 268, 269, -1, -1, -1, -1, 274, -1,
+ -1, -1, -1, -1, 280, -1, 282, 283, 284, 285,
+ 286, 0, 288, 289, 290, 291, -1, 293, 294, 295,
+ 296, -1, 298, -1, 300, 301, 302, -1, -1, 257,
+ 258, 259, 260, 261, 262, -1, -1, 265, -1, -1,
+ -1, -1, -1, -1, -1, -1, 274, -1, 37, -1,
+ -1, -1, 41, 42, -1, 44, 0, 285, 47, -1,
+ 288, 289, 290, 291, 292, 293, 294, 295, 296, 58,
+ 59, 60, 300, 62, 63, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 37, -1, -1, -1, 41, 42, -1,
+ 44, 0, -1, 47, 93, 94, -1, -1, -1, -1,
+ -1, -1, -1, -1, 58, 59, 60, -1, 62, 63,
+ -1, -1, -1, 112, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 123, 124, -1, -1, 37, -1,
+ -1, -1, 41, 42, -1, 44, -1, -1, 47, 93,
+ 94, -1, -1, -1, -1, -1, -1, -1, -1, 58,
+ 59, 60, -1, 62, 63, -1, -1, -1, 112, -1,
+ 0, -1, -1, -1, -1, -1, -1, -1, -1, 123,
+ 124, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 93, 94, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 37, -1, -1,
+ -1, 41, 42, 112, 44, -1, -1, 47, -1, -1,
+ -1, -1, -1, -1, 123, 124, -1, -1, 58, 59,
+ 60, -1, 62, 63, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 93, 94, -1, -1, -1, 257, 258,
+ 259, 260, 261, 262, -1, -1, 265, -1, -1, -1,
+ -1, -1, 112, -1, -1, 274, -1, -1, -1, -1,
+ -1, -1, -1, 123, 124, -1, 285, -1, -1, 288,
+ 289, 290, 291, 292, 293, 294, 295, 296, -1, -1,
+ -1, 300, -1, 257, 258, 259, 260, 261, 262, -1,
+ 40, 265, -1, 43, -1, 45, -1, -1, -1, -1,
+ 274, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 285, -1, -1, 288, 289, 290, 291, 292, 293,
+ 294, 295, 296, -1, -1, -1, 300, -1, 257, 258,
+ 259, 260, 261, 262, -1, -1, 265, -1, -1, -1,
+ -1, -1, -1, -1, -1, 274, -1, -1, 0, -1,
+ -1, -1, -1, -1, -1, -1, 285, -1, -1, 288,
+ 289, 290, 291, 292, 293, 294, 295, 296, -1, -1,
+ -1, 300, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 37, -1, -1, -1, 41,
+ 42, -1, 44, -1, -1, 47, -1, 257, 258, 259,
+ 260, 261, 262, -1, -1, 265, 58, 59, 60, -1,
+ 62, 63, -1, -1, 274, 0, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 285, -1, -1, 288, 289,
+ 290, 291, 292, 293, 294, 295, 296, -1, -1, -1,
+ 300, 93, 94, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 37, -1, -1, -1, 41, 42, -1, 44,
+ 112, -1, 47, 0, -1, -1, -1, -1, -1, -1,
+ -1, 123, 124, 58, 59, 60, -1, 62, 63, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 37, -1, -1, -1, 41, 42, -1, 44, 93, 94,
+ 47, 261, 262, 263, 264, -1, -1, -1, 268, 269,
+ -1, 58, 59, 60, -1, 62, 63, 112, -1, -1,
+ 280, -1, 282, 283, 284, 0, 286, -1, 123, 124,
+ -1, 291, 292, 293, 294, -1, -1, -1, 298, -1,
+ -1, 301, 302, 303, 304, -1, 93, 94, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 37, -1, -1, 112, 41, 42, -1, 44,
+ -1, -1, 47, -1, -1, -1, 123, 124, -1, -1,
+ -1, -1, -1, 58, 59, 60, -1, 62, 63, -1,
+ -1, -1, -1, -1, -1, 257, 258, 259, 260, 261,
+ 262, -1, -1, 265, 40, 41, -1, 43, 44, 45,
+ -1, -1, 274, 0, -1, -1, -1, -1, 93, 94,
+ -1, -1, -1, 285, -1, -1, 288, 289, 290, 291,
+ 292, 293, 294, 295, 296, -1, -1, 112, 300, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 123, 124,
+ 37, -1, -1, -1, 41, 42, -1, -1, -1, -1,
+ 47, -1, 257, 258, 259, 260, 261, 262, -1, -1,
+ 265, 58, 59, 60, -1, 62, 63, -1, -1, 274,
+ -1, -1, -1, 40, 41, -1, 43, 44, 45, -1,
+ 285, -1, -1, 288, 289, 290, -1, -1, -1, -1,
+ 295, 296, -1, -1, -1, 300, 93, 94, -1, -1,
+ 257, 258, 259, 260, 261, 262, -1, -1, 265, -1,
+ -1, -1, -1, -1, -1, 112, -1, 274, -1, -1,
+ 0, -1, -1, -1, -1, -1, 123, 124, 285, -1,
+ -1, 288, 289, 290, -1, -1, -1, -1, 295, 296,
+ -1, -1, -1, 300, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 37, -1, -1,
+ -1, 41, 42, -1, 44, -1, -1, 47, -1, -1,
+ -1, -1, 257, 258, 259, 260, 261, 262, 58, 59,
+ 265, -1, -1, 63, -1, -1, -1, -1, -1, 274,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 285, -1, -1, 288, 289, 290, -1, -1, -1, -1,
+ 295, 296, -1, 93, 94, 300, -1, 263, 264, -1,
+ -1, -1, 268, 269, -1, -1, -1, -1, -1, -1,
+ -1, -1, 112, -1, 280, -1, 282, 283, 284, -1,
+ 286, -1, -1, 123, 124, 291, 292, 293, 294, -1,
+ -1, -1, 298, -1, -1, 301, 302, 303, 304, -1,
+ 257, 258, 259, 260, 261, 262, -1, -1, 265, -1,
+ -1, -1, -1, -1, -1, -1, -1, 274, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 285, -1,
+ -1, 288, 289, 290, -1, -1, 263, 264, 295, 296,
+ -1, 268, 269, 300, -1, -1, -1, -1, -1, -1,
+ -1, 7, -1, 280, -1, 282, 283, 284, 14, 286,
+ -1, -1, -1, -1, 291, 292, 293, 294, -1, -1,
+ -1, 298, -1, 29, 301, 302, 303, 304, -1, 35,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 49, 50, 51, -1, -1, -1, 55,
+ 56, -1, 58, 59, 60, -1, 62, 63, 64, 65,
+ 66, -1, -1, -1, -1, -1, -1, 257, 258, 259,
+ 260, 261, 262, -1, -1, 265, -1, -1, 84, 85,
+ 86, 87, 88, -1, 274, -1, -1, -1, -1, -1,
+ -1, -1, -1, 99, -1, 285, -1, -1, 288, 289,
+ 290, 107, -1, -1, -1, -1, -1, -1, 114, -1,
+ 300, -1, -1, -1, 120, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 47, 48, 134, 135,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 157, 158, -1, -1, 161, 162, 163, 164, 165,
+ 81, 82, 83, -1, 170, 171, -1, 173, 174, -1,
+ -1, -1, 178, -1, -1, -1, 182, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 110,
+ 111, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 214, 215,
+ 216, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 232, -1, 234, -1,
+ 236, -1, 238, -1, 240, -1, -1, -1, -1, 245,
+ -1, -1, -1, -1, 250, 251, -1, -1, -1, -1,
+ -1, 172, 258, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 183, -1, 185, -1, 187, -1, -1, -1,
+ -1, -1, 278, -1, -1, -1, -1, -1, -1, -1,
+ -1, 40, -1, -1, 43, -1, 45, 208, -1, 295,
+ -1, -1, -1, -1, -1, -1, -1, -1, 16, -1,
+ 59, -1, 223, 224, 225, 226, 227, -1, -1, 27,
+ 28, -1, 30, 31, -1, 33, -1, -1, -1, -1,
+ -1, -1, -1, 41, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 53, -1, -1, -1, -1,
+ -1, -1, -1, 40, -1, -1, 43, -1, 45, -1,
+ 271, -1, -1, -1, 275, 276, 277, -1, -1, -1,
+ 281, -1, 59, -1, 123, -1, -1, -1, -1, -1,
+ -1, -1, -1, 91, 92, 93, 94, 95, 96, 97,
+ -1, -1, -1, -1, -1, 306, 307, 105, -1, -1,
+ 108, 312, -1, -1, 112, 113, -1, -1, 116, -1,
+ 118, 119, -1, -1, -1, 123, 124, -1, -1, -1,
+ 40, -1, -1, 43, -1, 45, -1, -1, -1, -1,
+ 138, 139, 140, 141, -1, -1, 123, -1, 125, 59,
+ -1, -1, 150, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 160, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 40, -1, -1, 43, -1,
+ 45, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 188, -1, -1, -1, 59, -1, -1, 195, -1, -1,
+ -1, 199, 200, -1, 202, 203, 204, 205, -1, 207,
+ -1, 209, 210, 123, -1, 125, -1, -1, -1, 217,
+ 218, 260, 261, 262, 263, 264, -1, 266, 267, 268,
+ 269, 270, -1, 272, 273, -1, 275, 276, 277, 278,
+ 279, 280, 281, 282, 283, 284, -1, 286, 287, -1,
+ -1, -1, 291, 292, 293, 294, 254, -1, 123, 298,
+ 125, -1, 301, 302, 303, 304, -1, -1, 266, 267,
+ 268, 269, 270, -1, 272, 273, 274, -1, -1, -1,
+ -1, -1, 280, 260, -1, -1, 263, 264, -1, 266,
+ 267, 268, 269, 270, 271, 272, 273, -1, 275, 276,
+ 277, 278, 279, 280, 281, 282, 283, 284, -1, 286,
+ 287, -1, -1, -1, 291, 292, 293, 294, -1, -1,
+ -1, 298, -1, -1, 301, 302, 303, 304, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 260, -1, -1, 263, 264, -1, 266, 267, 268, 269,
+ 270, 271, 272, 273, -1, 275, 276, 277, 278, 279,
+ 280, 281, 282, 283, 284, -1, 286, 287, -1, -1,
+ -1, 291, 292, 293, 294, -1, -1, -1, 298, -1,
+ -1, 301, 302, 303, 304, 260, -1, -1, 263, 264,
+ -1, 266, 267, 268, 269, 270, 271, 272, 273, -1,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ -1, 286, 287, -1, -1, -1, 291, 292, 293, 294,
+ -1, -1, -1, 298, -1, -1, 301, 302, 303, 304,
+ 40, -1, -1, 43, -1, 45, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 59,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 40, -1, -1, 43, -1,
+ 45, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 59, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 123, -1, 125, -1, -1, -1, -1,
+ 40, -1, -1, 43, -1, 45, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 59,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 123, -1,
+ 125, -1, -1, -1, -1, -1, 40, -1, -1, 43,
+ -1, 45, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 59, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 123, -1, 125, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 40, -1, -1, 43, -1, 45, -1, -1, -1, 123,
+ 260, 125, -1, 263, 264, -1, 266, 267, 268, 269,
+ 270, 271, 272, 273, -1, 275, 276, 277, 278, 279,
+ 280, 281, 282, 283, 284, -1, 286, 287, -1, -1,
+ -1, 291, 292, 293, 294, -1, -1, -1, 298, -1,
+ -1, 301, 302, 303, 304, 260, -1, -1, 263, 264,
+ -1, 266, 267, 268, 269, 270, 271, 272, 273, -1,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ -1, 286, 287, 123, -1, -1, 291, 292, 293, 294,
+ -1, -1, -1, 298, -1, -1, 301, 302, 303, 304,
+ 260, -1, -1, 263, 264, -1, 266, 267, 268, 269,
+ 270, 271, 272, 273, -1, 275, 276, 277, 278, 279,
+ 280, 281, 282, 283, 284, 40, 286, 287, 43, -1,
+ 45, 291, 292, 293, 294, -1, -1, -1, 298, -1,
+ -1, 301, 302, 303, 304, -1, 260, -1, -1, 263,
+ 264, -1, 266, 267, 268, 269, 270, 271, 272, 273,
+ -1, 275, 276, 277, 278, 279, 280, 281, 282, 283,
+ 284, -1, 286, 287, -1, -1, -1, 291, 292, 293,
+ 294, -1, -1, -1, 298, -1, -1, 301, 302, 303,
+ 304, 40, -1, -1, 43, -1, 45, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 59, -1, -1, -1, -1, -1, -1, 257, 258, 259,
+ -1, -1, -1, 263, 264, -1, -1, -1, 268, 269,
+ -1, -1, -1, -1, -1, -1, 40, -1, -1, 43,
+ 280, 45, 282, 283, 284, 285, 286, -1, -1, -1,
+ -1, 291, 292, 293, 294, 59, -1, -1, 298, -1,
+ 300, 301, 302, 303, 304, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 123, -1, 125, -1, -1, -1,
+ -1, 40, -1, -1, 43, -1, 45, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 59, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 123,
+ -1, 125, -1, -1, -1, -1, -1, 40, -1, -1,
+ 43, -1, 45, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 259, -1, 59, -1, 263, 264,
+ -1, -1, -1, 268, 269, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 123, 280, 125, 282, 283, 284,
+ -1, 286, -1, -1, -1, -1, 291, 292, 293, 294,
+ -1, -1, -1, 298, -1, 300, 301, 302, 303, 304,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 40, -1, -1, 43, -1, 45, -1, -1, -1,
+ 123, 260, 125, -1, 263, 264, -1, 266, 267, 268,
+ 269, 270, 271, 272, 273, -1, 275, 276, 277, 278,
+ 279, 280, 281, 282, 283, 284, -1, 286, 287, -1,
+ -1, -1, 291, 292, 293, 294, -1, -1, -1, 298,
+ -1, -1, 301, 302, 303, 304, 260, -1, -1, 263,
+ 264, -1, 266, 267, 268, 269, 270, -1, 272, 273,
+ -1, 275, 276, 277, 278, 279, 280, 281, 282, 283,
+ 284, -1, 286, 287, -1, -1, -1, 291, 292, 293,
+ 294, -1, -1, -1, 298, -1, -1, 301, 302, 303,
+ 304, 260, -1, -1, 263, 264, -1, 266, 267, 268,
+ 269, 270, -1, 272, 273, -1, 275, 276, 277, 278,
+ 279, 280, 281, 282, 283, 284, 40, 286, 287, 43,
+ -1, 45, 291, 292, 293, 294, -1, -1, -1, 298,
+ -1, -1, 301, 302, 303, 304, -1, 260, -1, -1,
+ 263, 264, -1, 266, 267, 268, 269, 270, -1, 272,
+ 273, -1, 275, 276, 277, 278, 279, 280, 281, 282,
+ 283, 284, -1, 286, 287, -1, -1, -1, 291, 292,
+ 293, 294, -1, -1, -1, 298, -1, -1, 301, 302,
+ 303, 304, 40, -1, -1, 43, -1, 45, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 59, -1, -1, -1, -1, -1, -1, -1, -1,
+ 259, -1, 261, 262, 263, 264, -1, -1, -1, 268,
+ 269, -1, -1, -1, -1, -1, -1, 40, -1, -1,
+ 43, 280, 45, 282, 283, 284, -1, 286, -1, -1,
+ -1, -1, 291, 292, 293, 294, 59, -1, -1, 298,
+ -1, 300, 301, 302, 303, 304, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 123, -1, 125, -1, -1,
+ -1, -1, 40, -1, -1, 43, -1, 45, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 59, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 123, -1, 125, -1, -1, -1, -1, -1, 40, -1,
+ -1, 43, -1, 45, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 259, -1, 59, -1, 263,
+ 264, -1, -1, -1, 268, 269, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 123, 280, 125, 282, 283,
+ 284, -1, 286, -1, -1, -1, -1, 291, 292, 293,
+ 294, -1, -1, -1, 298, -1, 300, 301, 302, 303,
+ 304, 40, 41, -1, 43, 44, 45, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 123, 260, 125, -1, 263, 264, -1, 266, 267,
+ 268, 269, 270, -1, 272, 273, -1, 275, 276, 277,
+ 278, 279, 280, 281, 282, 283, 284, -1, 286, 287,
+ -1, -1, -1, 291, 292, 293, 294, -1, -1, -1,
+ 298, -1, -1, 301, 302, 303, 304, 260, -1, -1,
+ 263, 264, -1, 266, 267, 268, 269, 270, -1, 272,
+ 273, -1, 275, 276, 277, 278, 279, 280, 281, 282,
+ 283, 284, 40, 286, 287, 43, 44, 45, 291, 292,
+ 293, 294, -1, -1, -1, 298, -1, -1, 301, 302,
+ 303, 304, 260, -1, -1, 263, 264, -1, 266, 267,
+ 268, 269, 270, -1, 272, 273, -1, 275, 276, 277,
+ 278, 279, 280, 281, 282, 283, 284, -1, 286, 287,
+ -1, -1, -1, 291, 292, 293, 294, -1, -1, -1,
+ 298, -1, -1, 301, 302, 303, 304, -1, 260, -1,
+ -1, 263, 264, -1, 266, 267, 268, 269, 270, -1,
+ 272, 273, -1, 275, 276, 277, 278, 279, 280, 281,
+ 282, 283, 284, -1, 286, 287, -1, -1, -1, 291,
+ 292, 293, 294, -1, -1, -1, 298, -1, -1, 301,
+ 302, 303, 304, 40, -1, -1, 43, -1, 45, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 59, -1, 263, 264, -1, -1, -1, 268,
+ 269, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 280, -1, 282, 283, 284, -1, 286, -1, -1,
+ -1, -1, 291, 292, 293, 294, -1, -1, -1, 298,
+ -1, -1, 301, 302, 303, 304, 40, 41, -1, 43,
+ 44, 45, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 123, -1, 125, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 40, 41,
+ -1, 43, -1, 45, -1, 263, 264, -1, -1, -1,
+ 268, 269, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 280, -1, 282, 283, 284, -1, 286, -1,
+ -1, -1, -1, 291, 292, 293, 294, -1, -1, -1,
+ 298, -1, -1, 301, 302, 303, 304, 40, -1, -1,
+ 43, -1, 45, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 40, -1, -1, 43, -1, 45, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 260, -1, -1, 263, 264, -1, 266,
+ 267, 268, 269, 270, -1, 272, 273, -1, 275, 276,
+ 277, 278, 279, 280, 281, 282, 283, 284, -1, 286,
+ 287, -1, -1, -1, 291, 292, 293, 294, -1, -1,
+ -1, 298, -1, -1, 301, 302, 303, 304, 40, -1,
+ -1, 43, -1, 45, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 263,
+ 264, -1, -1, -1, 268, 269, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 280, -1, 282, 283,
+ 284, -1, 286, -1, -1, -1, -1, 291, 292, 293,
+ 294, -1, -1, -1, 298, -1, -1, 301, 302, 303,
+ 304, 263, 264, -1, -1, -1, 268, 269, 40, 41,
+ -1, 43, -1, 45, -1, -1, -1, -1, 280, -1,
+ 282, 283, 284, -1, 286, -1, -1, -1, -1, 291,
+ 292, 293, 294, -1, -1, -1, 298, -1, -1, 301,
+ 302, 303, 304, -1, -1, -1, 259, -1, -1, -1,
+ 263, 264, -1, -1, 40, 268, 269, 43, 44, 45,
+ -1, -1, -1, -1, -1, -1, -1, 280, -1, 282,
+ 283, 284, -1, 286, -1, -1, -1, -1, 291, 292,
+ 293, 294, -1, -1, -1, 298, -1, -1, 301, 302,
+ 303, 304, 259, -1, -1, -1, 263, 264, -1, -1,
+ 40, 268, 269, 43, 44, 45, -1, -1, -1, -1,
+ -1, -1, -1, 280, -1, 282, 283, 284, -1, 286,
+ -1, -1, -1, -1, 291, 292, 293, 294, -1, -1,
+ -1, 298, -1, -1, 301, 302, 303, 304, -1, -1,
+ -1, -1, 40, -1, -1, 43, 44, 45, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 259, -1, -1,
+ -1, 263, 264, -1, -1, -1, 268, 269, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 280, -1,
+ 282, 283, 284, -1, 286, -1, -1, -1, -1, 291,
+ 292, 293, 294, -1, -1, -1, 298, -1, -1, 301,
+ 302, 303, 304, 40, -1, -1, 43, 44, 45, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 263, 264, -1, -1, -1, 268, 269, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 280, -1,
+ 282, 283, 284, 40, 286, -1, 43, 44, 45, 291,
+ 292, 293, 294, -1, -1, -1, 298, -1, -1, 301,
+ 302, 303, 304, -1, -1, -1, -1, 263, 264, -1,
+ -1, -1, 268, 269, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 280, -1, 282, 283, 284, 40,
+ 286, -1, 43, 44, 45, 291, 292, 293, 294, -1,
+ -1, -1, 298, -1, -1, 301, 302, 303, 304, -1,
+ -1, -1, -1, 263, 264, -1, -1, -1, 268, 269,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 280, -1, 282, 283, 284, 40, 286, -1, 43, -1,
+ 45, 291, 292, 293, 294, -1, -1, -1, 298, -1,
+ -1, 301, 302, 303, 304, 263, 264, -1, -1, -1,
+ 268, 269, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 280, -1, 282, 283, 284, -1, 286, -1,
+ -1, -1, -1, 291, 292, 293, 294, -1, -1, -1,
+ 298, -1, -1, 301, 302, 303, 304, 40, 41, -1,
+ 43, -1, 45, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 263, 264, -1, -1,
+ -1, 268, 269, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 280, -1, 282, 283, 284, -1, 286,
+ -1, -1, -1, -1, 291, 292, 293, 294, -1, -1,
+ -1, 298, -1, -1, 301, 302, 303, 304, 40, 41,
+ -1, 43, -1, 45, -1, -1, 263, 264, -1, -1,
+ -1, 268, 269, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 280, -1, 282, 283, 284, -1, 286,
+ -1, -1, -1, -1, 291, 292, 293, 294, -1, -1,
+ -1, 298, -1, -1, 301, 302, 303, 304, -1, -1,
+ -1, 40, 263, 264, 43, -1, 45, 268, 269, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 280,
+ -1, 282, 283, 284, -1, 286, -1, -1, -1, -1,
+ 291, 292, 293, 294, -1, -1, -1, 298, -1, -1,
+ 301, 302, 303, 304, 259, -1, -1, -1, 263, 264,
+ -1, -1, -1, 268, 269, 40, 41, -1, 43, -1,
+ 45, -1, -1, -1, -1, 280, -1, 282, 283, 284,
+ -1, 286, -1, -1, -1, -1, 291, 292, 293, 294,
+ -1, -1, -1, 298, -1, -1, 301, 302, 303, 304,
+ -1, -1, -1, -1, -1, -1, -1, 40, 41, -1,
+ 43, -1, 45, -1, -1, -1, -1, -1, -1, -1,
+ 263, 264, -1, -1, -1, 268, 269, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 280, -1, 282,
+ 283, 284, -1, 286, -1, -1, -1, -1, 291, 292,
+ 293, 294, -1, -1, -1, 298, -1, -1, 301, 302,
+ 303, 304, 40, 41, -1, 43, -1, 45, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 263, 264, -1, -1, -1, 268, 269, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 280, -1,
+ 282, 283, 284, -1, 286, -1, -1, -1, -1, 291,
+ 292, 293, 294, -1, -1, -1, 298, -1, -1, 301,
+ 302, 303, 304, 40, 41, -1, 43, -1, 45, -1,
+ 259, -1, -1, -1, 263, 264, -1, -1, -1, 268,
+ 269, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 280, -1, 282, 283, 284, -1, 286, -1, -1,
+ -1, -1, 291, 292, 293, 294, -1, -1, -1, 298,
+ -1, -1, 301, 302, 303, 304, 40, 41, -1, 43,
+ -1, 45, -1, -1, -1, -1, -1, -1, 263, 264,
+ -1, -1, -1, 268, 269, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 280, -1, 282, 283, 284,
+ -1, 286, -1, -1, -1, -1, 291, 292, 293, 294,
+ -1, -1, -1, 298, -1, -1, 301, 302, 303, 304,
+ 263, 264, -1, -1, -1, 268, 269, 40, 41, -1,
+ 43, -1, 45, -1, -1, -1, -1, 280, -1, 282,
+ 283, 284, -1, 286, -1, -1, -1, -1, 291, 292,
+ 293, 294, -1, -1, -1, 298, -1, -1, 301, 302,
+ 303, 304, -1, -1, -1, -1, -1, -1, -1, 40,
+ 41, -1, 43, -1, 45, 263, 264, -1, -1, -1,
+ 268, 269, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 280, -1, 282, 283, 284, -1, 286, -1,
+ -1, -1, -1, 291, 292, 293, 294, -1, -1, -1,
+ 298, -1, -1, 301, 302, 303, 304, 40, -1, -1,
+ 43, -1, 45, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 263, 264, -1, -1,
+ -1, 268, 269, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 280, -1, 282, 283, 284, -1, 286,
+ -1, -1, -1, -1, 291, 292, 293, 294, -1, -1,
+ 93, 298, -1, -1, 301, 302, 303, 304, 40, -1,
+ -1, 43, -1, 45, -1, -1, -1, -1, -1, 263,
+ 264, -1, -1, -1, 268, 269, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 280, -1, 282, 283,
+ 284, -1, 286, -1, -1, -1, -1, 291, 292, 293,
+ 294, -1, -1, -1, 298, -1, -1, 301, 302, 303,
+ 304, 40, -1, -1, 43, -1, 45, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 263, 264, -1, -1, -1, 268, 269, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 280, -1, 282,
+ 283, 284, -1, 286, -1, -1, -1, -1, 291, 292,
+ 293, 294, -1, -1, -1, 298, -1, -1, 301, 302,
+ 303, 304, 263, 264, -1, -1, -1, 268, 269, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 280,
+ -1, 282, 283, 284, -1, 286, -1, -1, -1, -1,
+ 291, 292, 293, 294, -1, -1, -1, 298, -1, -1,
+ 301, 302, 303, 304, -1, -1, -1, -1, -1, -1,
+ 263, 264, -1, -1, -1, 268, 269, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 280, -1, 282,
+ 283, 284, -1, 286, -1, -1, -1, -1, 291, 292,
+ 293, 294, -1, -1, -1, 298, -1, -1, 301, 302,
+ 303, 304, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 263, 264, -1, -1, -1, 268, 269, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 280, -1,
+ 282, 283, 284, -1, 286, -1, -1, -1, -1, 291,
+ 292, 293, 294, -1, -1, -1, 298, 27, 28, 301,
+ 302, 303, 304, 33, -1, -1, -1, -1, -1, -1,
+ -1, 41, -1, -1, 263, 264, -1, -1, -1, 268,
+ 269, -1, -1, 53, -1, -1, -1, -1, -1, -1,
+ -1, 280, -1, 282, 283, 284, -1, 286, -1, -1,
+ -1, -1, 291, 292, 293, 294, -1, -1, -1, 298,
+ -1, -1, 301, 302, 303, 304, -1, -1, -1, -1,
+ -1, 91, 92, 93, 94, 95, 96, 97, -1, -1,
+ -1, -1, -1, -1, -1, 105, -1, -1, 108, -1,
+ -1, -1, 112, 113, -1, -1, 116, -1, 118, 119,
+ -1, -1, -1, 123, 124, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 138, 139,
+ 140, 141, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 160, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 188, -1,
+ -1, -1, -1, -1, -1, 195, -1, -1, -1, 199,
+ 200, -1, 202, 203, 204, 205, -1, 207, -1, 209,
+ 210, -1, -1, -1, -1, -1, -1, 217, 218, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 254, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 266, 267, 268, 269,
+ 270, -1, 272, 273, 274, -1, -1, -1, -1, -1,
+ 280,
+};
+#define YYFINAL 1
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYMAXTOKEN 304
+#if YYDEBUG
+char *yyname[] = {
+"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,"'%'",0,0,"'('","')'","'*'","'+'","','","'-'",0,"'/'",0,0,0,0,0,0,0,0,0,0,
+"':'","';'","'<'",0,"'>'","'?'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,"'['",0,"']'","'^'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'p'",0,0,0,0,0,0,0,
+0,0,0,"'{'","'|'","'}'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"BEGIN","END","REGEX","SEMINEW",
+"NEWLINE","COMMENT","FUN1","FUNN","GRGR","PRINT","PRINTF","SPRINTF","SPLIT",
+"IF","ELSE","WHILE","FOR","IN","EXIT","NEXT","BREAK","CONTINUE","RET","GETLINE",
+"DO","SUB","GSUB","MATCH","FUNCTION","USERFUN","DELETE","ASGNOP","OROR",
+"ANDAND","NUMBER","VAR","SUBSTR","INDEX","MATCHOP","RELOP","OR","STRING",
+"UMINUS","NOT","INCR","DECR","FIELD","VFIELD",
+};
+char *yyrule[] = {
+"$accept : program",
+"program : junk hunks",
+"begin : BEGIN '{' maybe states '}' junk",
+"end : END '{' maybe states '}'",
+"end : end NEWLINE",
+"hunks : hunks hunk junk",
+"hunks :",
+"hunk : patpat",
+"hunk : patpat '{' maybe states '}'",
+"hunk : FUNCTION USERFUN '(' arg_list ')' maybe '{' maybe states '}'",
+"hunk : '{' maybe states '}'",
+"hunk : begin",
+"hunk : end",
+"arg_list : expr_list",
+"patpat : cond",
+"patpat : cond ',' cond",
+"cond : expr",
+"cond : match",
+"cond : rel",
+"cond : compound_cond",
+"compound_cond : '(' compound_cond ')'",
+"compound_cond : cond ANDAND maybe cond",
+"compound_cond : cond OROR maybe cond",
+"compound_cond : NOT cond",
+"rel : expr RELOP expr",
+"rel : expr '>' expr",
+"rel : expr '<' expr",
+"rel : '(' rel ')'",
+"match : expr MATCHOP expr",
+"match : expr MATCHOP REGEX",
+"match : REGEX",
+"match : '(' match ')'",
+"expr : term",
+"expr : expr term",
+"expr : variable ASGNOP cond",
+"term : variable",
+"term : NUMBER",
+"term : STRING",
+"term : term '+' term",
+"term : term '-' term",
+"term : term '*' term",
+"term : term '/' term",
+"term : term '%' term",
+"term : term '^' term",
+"term : term IN VAR",
+"term : term '?' term ':' term",
+"term : variable INCR",
+"term : variable DECR",
+"term : INCR variable",
+"term : DECR variable",
+"term : '-' term",
+"term : '+' term",
+"term : '(' cond ')'",
+"term : GETLINE",
+"term : GETLINE variable",
+"term : GETLINE '<' expr",
+"term : GETLINE variable '<' expr",
+"term : term 'p' GETLINE",
+"term : term 'p' GETLINE variable",
+"term : FUN1",
+"term : FUN1 '(' ')'",
+"term : FUN1 '(' expr ')'",
+"term : FUNN '(' expr_list ')'",
+"term : USERFUN '(' expr_list ')'",
+"term : SPRINTF expr_list",
+"term : SUBSTR '(' expr ',' expr ',' expr ')'",
+"term : SUBSTR '(' expr ',' expr ')'",
+"term : SPLIT '(' expr ',' VAR ',' expr ')'",
+"term : SPLIT '(' expr ',' VAR ',' REGEX ')'",
+"term : SPLIT '(' expr ',' VAR ')'",
+"term : INDEX '(' expr ',' expr ')'",
+"term : MATCH '(' expr ',' REGEX ')'",
+"term : MATCH '(' expr ',' expr ')'",
+"term : SUB '(' expr ',' expr ')'",
+"term : SUB '(' REGEX ',' expr ')'",
+"term : GSUB '(' expr ',' expr ')'",
+"term : GSUB '(' REGEX ',' expr ')'",
+"term : SUB '(' expr ',' expr ',' expr ')'",
+"term : SUB '(' REGEX ',' expr ',' expr ')'",
+"term : GSUB '(' expr ',' expr ',' expr ')'",
+"term : GSUB '(' REGEX ',' expr ',' expr ')'",
+"variable : VAR",
+"variable : VAR '[' expr_list ']'",
+"variable : FIELD",
+"variable : VFIELD term",
+"expr_list : expr",
+"expr_list : clist",
+"expr_list :",
+"clist : expr ',' maybe expr",
+"clist : clist ',' maybe expr",
+"clist : '(' clist ')'",
+"junk : junk hunksep",
+"junk :",
+"hunksep : ';'",
+"hunksep : SEMINEW",
+"hunksep : NEWLINE",
+"hunksep : COMMENT",
+"maybe : maybe nlstuff",
+"maybe :",
+"nlstuff : NEWLINE",
+"nlstuff : COMMENT",
+"separator : ';' maybe",
+"separator : SEMINEW maybe",
+"separator : NEWLINE maybe",
+"separator : COMMENT maybe",
+"states : states statement",
+"states :",
+"statement : simple separator maybe",
+"statement : ';' maybe",
+"statement : SEMINEW maybe",
+"statement : compound",
+"simpnull : simple",
+"simpnull :",
+"simple : expr",
+"simple : PRINT expr_list redir expr",
+"simple : PRINT expr_list",
+"simple : PRINTF expr_list redir expr",
+"simple : PRINTF expr_list",
+"simple : BREAK",
+"simple : NEXT",
+"simple : EXIT",
+"simple : EXIT expr",
+"simple : CONTINUE",
+"simple : RET",
+"simple : RET expr",
+"simple : DELETE VAR '[' expr ']'",
+"redir : '>'",
+"redir : GRGR",
+"redir : '|'",
+"compound : IF '(' cond ')' maybe statement",
+"compound : IF '(' cond ')' maybe statement ELSE maybe statement",
+"compound : WHILE '(' cond ')' maybe statement",
+"compound : DO maybe statement WHILE '(' cond ')'",
+"compound : FOR '(' simpnull ';' cond ';' simpnull ')' maybe statement",
+"compound : FOR '(' simpnull ';' ';' simpnull ')' maybe statement",
+"compound : FOR '(' expr ')' maybe statement",
+"compound : '{' maybe states '}' maybe",
+};
+#endif
+#ifndef YYSTYPE
+typedef int YYSTYPE;
+#endif
+#ifdef YYSTACKSIZE
+#undef YYMAXDEPTH
+#define YYMAXDEPTH YYSTACKSIZE
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 500
+#define YYMAXDEPTH 500
+#endif
+#endif
+int yydebug;
+int yynerrs;
+int yyerrflag;
+int yychar;
+short *yyssp;
+YYSTYPE *yyvsp;
+YYSTYPE yyval;
+YYSTYPE yylval;
+short yyss[YYSTACKSIZE];
+YYSTYPE yyvs[YYSTACKSIZE];
+#define yystacksize YYSTACKSIZE
+#line 406 "a2p.y"
+#include "a2py.c"
+#line 1997 "y.tab.c"
+#define YYABORT goto yyabort
+#define YYREJECT goto yyabort
+#define YYACCEPT goto yyaccept
+#define YYERROR goto yyerrlab
+int
+yyparse()
+{
+ register int yym, yyn, yystate;
+#if YYDEBUG
+ register char *yys;
+ extern char *getenv();
+
+ if (yys = getenv("YYDEBUG"))
+ {
+ yyn = *yys;
+ if (yyn >= '0' && yyn <= '9')
+ yydebug = yyn - '0';
+ }
+#endif
+
+ yynerrs = 0;
+ yyerrflag = 0;
+ yychar = (-1);
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+ *yyssp = yystate = 0;
+
+yyloop:
+ if (yyn = yydefred[yystate]) goto yyreduce;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ }
+ if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, shifting to state %d\n",
+ YYPREFIX, yystate, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ yychar = (-1);
+ if (yyerrflag > 0) --yyerrflag;
+ goto yyloop;
+ }
+ if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+ yyn = yytable[yyn];
+ goto yyreduce;
+ }
+ if (yyerrflag) goto yyinrecovery;
+#ifdef lint
+ goto yynewerror;
+#endif
+yynewerror:
+ yyerror("syntax error");
+#ifdef lint
+ goto yyerrlab;
+#endif
+yyerrlab:
+ ++yynerrs;
+yyinrecovery:
+ if (yyerrflag < 3)
+ {
+ yyerrflag = 3;
+ for (;;)
+ {
+ if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, error recovery shifting\
+ to state %d\n", YYPREFIX, *yyssp, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ goto yyloop;
+ }
+ else
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: error recovery discarding state %d\n",
+ YYPREFIX, *yyssp);
+#endif
+ if (yyssp <= yyss) goto yyabort;
+ --yyssp;
+ --yyvsp;
+ }
+ }
+ }
+ else
+ {
+ if (yychar == 0) goto yyabort;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, error recovery discards token %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ yychar = (-1);
+ goto yyloop;
+ }
+yyreduce:
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, reducing by rule %d (%s)\n",
+ YYPREFIX, yystate, yyn, yyrule[yyn]);
+#endif
+ yym = yylen[yyn];
+ yyval = yyvsp[1-yym];
+ switch (yyn)
+ {
+case 1:
+#line 63 "a2p.y"
+{ root = oper4(OPROG,yyvsp[-1],begins,yyvsp[0],ends); }
+break;
+case 2:
+#line 67 "a2p.y"
+{ begins = oper4(OJUNK,begins,yyvsp[-3],yyvsp[-2],yyvsp[0]); in_begin = FALSE;
+ yyval = Nullop; }
+break;
+case 3:
+#line 72 "a2p.y"
+{ ends = oper3(OJUNK,ends,yyvsp[-2],yyvsp[-1]); yyval = Nullop; }
+break;
+case 4:
+#line 74 "a2p.y"
+{ yyval = yyvsp[-1]; }
+break;
+case 5:
+#line 78 "a2p.y"
+{ yyval = oper3(OHUNKS,yyvsp[-2],yyvsp[-1],yyvsp[0]); }
+break;
+case 6:
+#line 80 "a2p.y"
+{ yyval = Nullop; }
+break;
+case 7:
+#line 84 "a2p.y"
+{ yyval = oper1(OHUNK,yyvsp[0]); need_entire = TRUE; }
+break;
+case 8:
+#line 86 "a2p.y"
+{ yyval = oper2(OHUNK,yyvsp[-4],oper2(OJUNK,yyvsp[-2],yyvsp[-1])); }
+break;
+case 9:
+#line 88 "a2p.y"
+{ fixfargs(yyvsp[-8],yyvsp[-6],0); yyval = oper5(OUSERDEF,yyvsp[-8],yyvsp[-6],yyvsp[-4],yyvsp[-2],yyvsp[-1]); }
+break;
+case 10:
+#line 90 "a2p.y"
+{ yyval = oper2(OHUNK,Nullop,oper2(OJUNK,yyvsp[-2],yyvsp[-1])); }
+break;
+case 13:
+#line 96 "a2p.y"
+{ yyval = rememberargs(yyval); }
+break;
+case 14:
+#line 100 "a2p.y"
+{ yyval = oper1(OPAT,yyvsp[0]); }
+break;
+case 15:
+#line 102 "a2p.y"
+{ yyval = oper2(ORANGE,yyvsp[-2],yyvsp[0]); }
+break;
+case 20:
+#line 113 "a2p.y"
+{ yyval = oper1(OCPAREN,yyvsp[-1]); }
+break;
+case 21:
+#line 115 "a2p.y"
+{ yyval = oper3(OCANDAND,yyvsp[-3],yyvsp[-1],yyvsp[0]); }
+break;
+case 22:
+#line 117 "a2p.y"
+{ yyval = oper3(OCOROR,yyvsp[-3],yyvsp[-1],yyvsp[0]); }
+break;
+case 23:
+#line 119 "a2p.y"
+{ yyval = oper1(OCNOT,yyvsp[0]); }
+break;
+case 24:
+#line 123 "a2p.y"
+{ yyval = oper3(ORELOP,yyvsp[-1],yyvsp[-2],yyvsp[0]); }
+break;
+case 25:
+#line 125 "a2p.y"
+{ yyval = oper3(ORELOP,string(">",1),yyvsp[-2],yyvsp[0]); }
+break;
+case 26:
+#line 127 "a2p.y"
+{ yyval = oper3(ORELOP,string("<",1),yyvsp[-2],yyvsp[0]); }
+break;
+case 27:
+#line 129 "a2p.y"
+{ yyval = oper1(ORPAREN,yyvsp[-1]); }
+break;
+case 28:
+#line 133 "a2p.y"
+{ yyval = oper3(OMATCHOP,yyvsp[-1],yyvsp[-2],yyvsp[0]); }
+break;
+case 29:
+#line 135 "a2p.y"
+{ yyval = oper3(OMATCHOP,yyvsp[-1],yyvsp[-2],oper1(OREGEX,yyvsp[0])); }
+break;
+case 30:
+#line 137 "a2p.y"
+{ yyval = oper1(OREGEX,yyvsp[0]); }
+break;
+case 31:
+#line 139 "a2p.y"
+{ yyval = oper1(OMPAREN,yyvsp[-1]); }
+break;
+case 32:
+#line 143 "a2p.y"
+{ yyval = yyvsp[0]; }
+break;
+case 33:
+#line 145 "a2p.y"
+{ yyval = oper2(OCONCAT,yyvsp[-1],yyvsp[0]); }
+break;
+case 34:
+#line 147 "a2p.y"
+{ yyval = oper3(OASSIGN,yyvsp[-1],yyvsp[-2],yyvsp[0]);
+ if ((ops[yyvsp[-2]].ival & 255) == OFLD)
+ lval_field = TRUE;
+ if ((ops[yyvsp[-2]].ival & 255) == OVFLD)
+ lval_field = TRUE;
+ }
+break;
+case 35:
+#line 156 "a2p.y"
+{ yyval = yyvsp[0]; }
+break;
+case 36:
+#line 158 "a2p.y"
+{ yyval = oper1(ONUM,yyvsp[0]); }
+break;
+case 37:
+#line 160 "a2p.y"
+{ yyval = oper1(OSTR,yyvsp[0]); }
+break;
+case 38:
+#line 162 "a2p.y"
+{ yyval = oper2(OADD,yyvsp[-2],yyvsp[0]); }
+break;
+case 39:
+#line 164 "a2p.y"
+{ yyval = oper2(OSUBTRACT,yyvsp[-2],yyvsp[0]); }
+break;
+case 40:
+#line 166 "a2p.y"
+{ yyval = oper2(OMULT,yyvsp[-2],yyvsp[0]); }
+break;
+case 41:
+#line 168 "a2p.y"
+{ yyval = oper2(ODIV,yyvsp[-2],yyvsp[0]); }
+break;
+case 42:
+#line 170 "a2p.y"
+{ yyval = oper2(OMOD,yyvsp[-2],yyvsp[0]); }
+break;
+case 43:
+#line 172 "a2p.y"
+{ yyval = oper2(OPOW,yyvsp[-2],yyvsp[0]); }
+break;
+case 44:
+#line 174 "a2p.y"
+{ yyval = oper2(ODEFINED,aryrefarg(yyvsp[0]),yyvsp[-2]); }
+break;
+case 45:
+#line 176 "a2p.y"
+{ yyval = oper3(OCOND,yyvsp[-4],yyvsp[-2],yyvsp[0]); }
+break;
+case 46:
+#line 178 "a2p.y"
+{ yyval = oper1(OPOSTINCR,yyvsp[-1]); }
+break;
+case 47:
+#line 180 "a2p.y"
+{ yyval = oper1(OPOSTDECR,yyvsp[-1]); }
+break;
+case 48:
+#line 182 "a2p.y"
+{ yyval = oper1(OPREINCR,yyvsp[0]); }
+break;
+case 49:
+#line 184 "a2p.y"
+{ yyval = oper1(OPREDECR,yyvsp[0]); }
+break;
+case 50:
+#line 186 "a2p.y"
+{ yyval = oper1(OUMINUS,yyvsp[0]); }
+break;
+case 51:
+#line 188 "a2p.y"
+{ yyval = oper1(OUPLUS,yyvsp[0]); }
+break;
+case 52:
+#line 190 "a2p.y"
+{ yyval = oper1(OPAREN,yyvsp[-1]); }
+break;
+case 53:
+#line 192 "a2p.y"
+{ yyval = oper0(OGETLINE); }
+break;
+case 54:
+#line 194 "a2p.y"
+{ yyval = oper1(OGETLINE,yyvsp[0]); }
+break;
+case 55:
+#line 196 "a2p.y"
+{ yyval = oper3(OGETLINE,Nullop,string("<",1),yyvsp[0]);
+ if (ops[yyvsp[0]].ival != OSTR + (1<<8)) do_fancy_opens = TRUE; }
+break;
+case 56:
+#line 199 "a2p.y"
+{ yyval = oper3(OGETLINE,yyvsp[-2],string("<",1),yyvsp[0]);
+ if (ops[yyvsp[0]].ival != OSTR + (1<<8)) do_fancy_opens = TRUE; }
+break;
+case 57:
+#line 202 "a2p.y"
+{ yyval = oper3(OGETLINE,Nullop,string("|",1),yyvsp[-2]);
+ if (ops[yyvsp[-2]].ival != OSTR + (1<<8)) do_fancy_opens = TRUE; }
+break;
+case 58:
+#line 205 "a2p.y"
+{ yyval = oper3(OGETLINE,yyvsp[0],string("|",1),yyvsp[-3]);
+ if (ops[yyvsp[-3]].ival != OSTR + (1<<8)) do_fancy_opens = TRUE; }
+break;
+case 59:
+#line 208 "a2p.y"
+{ yyval = oper0(yyvsp[0]); need_entire = do_chop = TRUE; }
+break;
+case 60:
+#line 210 "a2p.y"
+{ yyval = oper1(yyvsp[-2],Nullop); need_entire = do_chop = TRUE; }
+break;
+case 61:
+#line 212 "a2p.y"
+{ yyval = oper1(yyvsp[-3],yyvsp[-1]); }
+break;
+case 62:
+#line 214 "a2p.y"
+{ yyval = oper1(yyvsp[-3],yyvsp[-1]); }
+break;
+case 63:
+#line 216 "a2p.y"
+{ yyval = oper2(OUSERFUN,yyvsp[-3],yyvsp[-1]); }
+break;
+case 64:
+#line 218 "a2p.y"
+{ yyval = oper1(OSPRINTF,yyvsp[0]); }
+break;
+case 65:
+#line 220 "a2p.y"
+{ yyval = oper3(OSUBSTR,yyvsp[-5],yyvsp[-3],yyvsp[-1]); }
+break;
+case 66:
+#line 222 "a2p.y"
+{ yyval = oper2(OSUBSTR,yyvsp[-3],yyvsp[-1]); }
+break;
+case 67:
+#line 224 "a2p.y"
+{ yyval = oper3(OSPLIT,yyvsp[-5],aryrefarg(numary(yyvsp[-3])),yyvsp[-1]); }
+break;
+case 68:
+#line 226 "a2p.y"
+{ yyval = oper3(OSPLIT,yyvsp[-5],aryrefarg(numary(yyvsp[-3])),oper1(OREGEX,yyvsp[-1]));}
+break;
+case 69:
+#line 228 "a2p.y"
+{ yyval = oper2(OSPLIT,yyvsp[-3],aryrefarg(numary(yyvsp[-1]))); }
+break;
+case 70:
+#line 230 "a2p.y"
+{ yyval = oper2(OINDEX,yyvsp[-3],yyvsp[-1]); }
+break;
+case 71:
+#line 232 "a2p.y"
+{ yyval = oper2(OMATCH,yyvsp[-3],oper1(OREGEX,yyvsp[-1])); }
+break;
+case 72:
+#line 234 "a2p.y"
+{ yyval = oper2(OMATCH,yyvsp[-3],yyvsp[-1]); }
+break;
+case 73:
+#line 236 "a2p.y"
+{ yyval = oper2(OSUB,yyvsp[-3],yyvsp[-1]); }
+break;
+case 74:
+#line 238 "a2p.y"
+{ yyval = oper2(OSUB,oper1(OREGEX,yyvsp[-3]),yyvsp[-1]); }
+break;
+case 75:
+#line 240 "a2p.y"
+{ yyval = oper2(OGSUB,yyvsp[-3],yyvsp[-1]); }
+break;
+case 76:
+#line 242 "a2p.y"
+{ yyval = oper2(OGSUB,oper1(OREGEX,yyvsp[-3]),yyvsp[-1]); }
+break;
+case 77:
+#line 244 "a2p.y"
+{ yyval = oper3(OSUB,yyvsp[-5],yyvsp[-3],yyvsp[-1]); }
+break;
+case 78:
+#line 246 "a2p.y"
+{ yyval = oper3(OSUB,oper1(OREGEX,yyvsp[-5]),yyvsp[-3],yyvsp[-1]); }
+break;
+case 79:
+#line 248 "a2p.y"
+{ yyval = oper3(OGSUB,yyvsp[-5],yyvsp[-3],yyvsp[-1]); }
+break;
+case 80:
+#line 250 "a2p.y"
+{ yyval = oper3(OGSUB,oper1(OREGEX,yyvsp[-5]),yyvsp[-3],yyvsp[-1]); }
+break;
+case 81:
+#line 254 "a2p.y"
+{ yyval = oper1(OVAR,yyvsp[0]); }
+break;
+case 82:
+#line 256 "a2p.y"
+{ yyval = oper2(OVAR,aryrefarg(yyvsp[-3]),yyvsp[-1]); }
+break;
+case 83:
+#line 258 "a2p.y"
+{ yyval = oper1(OFLD,yyvsp[0]); }
+break;
+case 84:
+#line 260 "a2p.y"
+{ yyval = oper1(OVFLD,yyvsp[0]); }
+break;
+case 87:
+#line 267 "a2p.y"
+{ yyval = Nullop; }
+break;
+case 88:
+#line 271 "a2p.y"
+{ yyval = oper3(OCOMMA,yyvsp[-3],yyvsp[-1],yyvsp[0]); }
+break;
+case 89:
+#line 273 "a2p.y"
+{ yyval = oper3(OCOMMA,yyvsp[-3],yyvsp[-1],yyvsp[0]); }
+break;
+case 90:
+#line 275 "a2p.y"
+{ yyval = yyvsp[-1]; }
+break;
+case 91:
+#line 279 "a2p.y"
+{ yyval = oper2(OJUNK,yyvsp[-1],yyvsp[0]); }
+break;
+case 92:
+#line 281 "a2p.y"
+{ yyval = Nullop; }
+break;
+case 93:
+#line 285 "a2p.y"
+{ yyval = oper2(OJUNK,oper0(OSEMICOLON),oper0(ONEWLINE)); }
+break;
+case 94:
+#line 287 "a2p.y"
+{ yyval = oper2(OJUNK,oper0(OSEMICOLON),oper0(ONEWLINE)); }
+break;
+case 95:
+#line 289 "a2p.y"
+{ yyval = oper0(ONEWLINE); }
+break;
+case 96:
+#line 291 "a2p.y"
+{ yyval = oper1(OCOMMENT,yyvsp[0]); }
+break;
+case 97:
+#line 295 "a2p.y"
+{ yyval = oper2(OJUNK,yyvsp[-1],yyvsp[0]); }
+break;
+case 98:
+#line 297 "a2p.y"
+{ yyval = Nullop; }
+break;
+case 99:
+#line 301 "a2p.y"
+{ yyval = oper0(ONEWLINE); }
+break;
+case 100:
+#line 303 "a2p.y"
+{ yyval = oper1(OCOMMENT,yyvsp[0]); }
+break;
+case 101:
+#line 308 "a2p.y"
+{ yyval = oper2(OJUNK,oper0(OSEMICOLON),yyvsp[0]); }
+break;
+case 102:
+#line 310 "a2p.y"
+{ yyval = oper2(OJUNK,oper0(OSNEWLINE),yyvsp[0]); }
+break;
+case 103:
+#line 312 "a2p.y"
+{ yyval = oper2(OJUNK,oper0(OSNEWLINE),yyvsp[0]); }
+break;
+case 104:
+#line 314 "a2p.y"
+{ yyval = oper2(OJUNK,oper1(OSCOMMENT,yyvsp[-1]),yyvsp[0]); }
+break;
+case 105:
+#line 318 "a2p.y"
+{ yyval = oper2(OSTATES,yyvsp[-1],yyvsp[0]); }
+break;
+case 106:
+#line 320 "a2p.y"
+{ yyval = Nullop; }
+break;
+case 107:
+#line 325 "a2p.y"
+{ yyval = oper2(OJUNK,oper2(OSTATE,yyvsp[-2],yyvsp[-1]),yyvsp[0]); }
+break;
+case 108:
+#line 327 "a2p.y"
+{ yyval = oper2(OSTATE,Nullop,oper2(OJUNK,oper0(OSEMICOLON),yyvsp[0])); }
+break;
+case 109:
+#line 329 "a2p.y"
+{ yyval = oper2(OSTATE,Nullop,oper2(OJUNK,oper0(OSNEWLINE),yyvsp[0])); }
+break;
+case 112:
+#line 335 "a2p.y"
+{ yyval = Nullop; }
+break;
+case 114:
+#line 341 "a2p.y"
+{ yyval = oper3(OPRINT,yyvsp[-2],yyvsp[-1],yyvsp[0]);
+ do_opens = TRUE;
+ saw_ORS = saw_OFS = TRUE;
+ if (!yyvsp[-2]) need_entire = TRUE;
+ if (ops[yyvsp[0]].ival != OSTR + (1<<8)) do_fancy_opens = TRUE; }
+break;
+case 115:
+#line 347 "a2p.y"
+{ yyval = oper1(OPRINT,yyvsp[0]);
+ if (!yyvsp[0]) need_entire = TRUE;
+ saw_ORS = saw_OFS = TRUE;
+ }
+break;
+case 116:
+#line 352 "a2p.y"
+{ yyval = oper3(OPRINTF,yyvsp[-2],yyvsp[-1],yyvsp[0]);
+ do_opens = TRUE;
+ if (!yyvsp[-2]) need_entire = TRUE;
+ if (ops[yyvsp[0]].ival != OSTR + (1<<8)) do_fancy_opens = TRUE; }
+break;
+case 117:
+#line 357 "a2p.y"
+{ yyval = oper1(OPRINTF,yyvsp[0]);
+ if (!yyvsp[0]) need_entire = TRUE;
+ }
+break;
+case 118:
+#line 361 "a2p.y"
+{ yyval = oper0(OBREAK); }
+break;
+case 119:
+#line 363 "a2p.y"
+{ yyval = oper0(ONEXT); }
+break;
+case 120:
+#line 365 "a2p.y"
+{ yyval = oper0(OEXIT); }
+break;
+case 121:
+#line 367 "a2p.y"
+{ yyval = oper1(OEXIT,yyvsp[0]); }
+break;
+case 122:
+#line 369 "a2p.y"
+{ yyval = oper0(OCONTINUE); }
+break;
+case 123:
+#line 371 "a2p.y"
+{ yyval = oper0(ORETURN); }
+break;
+case 124:
+#line 373 "a2p.y"
+{ yyval = oper1(ORETURN,yyvsp[0]); }
+break;
+case 125:
+#line 375 "a2p.y"
+{ yyval = oper2(ODELETE,aryrefarg(yyvsp[-3]),yyvsp[-1]); }
+break;
+case 126:
+#line 379 "a2p.y"
+{ yyval = oper1(OREDIR,string(">",1)); }
+break;
+case 127:
+#line 381 "a2p.y"
+{ yyval = oper1(OREDIR,string(">>",2)); }
+break;
+case 128:
+#line 383 "a2p.y"
+{ yyval = oper1(OREDIR,string("|",1)); }
+break;
+case 129:
+#line 388 "a2p.y"
+{ yyval = oper2(OIF,yyvsp[-3],bl(yyvsp[0],yyvsp[-1])); }
+break;
+case 130:
+#line 390 "a2p.y"
+{ yyval = oper3(OIF,yyvsp[-6],bl(yyvsp[-3],yyvsp[-4]),bl(yyvsp[0],yyvsp[-1])); }
+break;
+case 131:
+#line 392 "a2p.y"
+{ yyval = oper2(OWHILE,yyvsp[-3],bl(yyvsp[0],yyvsp[-1])); }
+break;
+case 132:
+#line 394 "a2p.y"
+{ yyval = oper2(ODO,bl(yyvsp[-4],yyvsp[-5]),yyvsp[-1]); }
+break;
+case 133:
+#line 396 "a2p.y"
+{ yyval = oper4(OFOR,yyvsp[-7],yyvsp[-5],yyvsp[-3],bl(yyvsp[0],yyvsp[-1])); }
+break;
+case 134:
+#line 398 "a2p.y"
+{ yyval = oper4(OFOR,yyvsp[-6],string("",0),yyvsp[-3],bl(yyvsp[0],yyvsp[-1])); }
+break;
+case 135:
+#line 400 "a2p.y"
+{ yyval = oper2(OFORIN,yyvsp[-3],bl(yyvsp[0],yyvsp[-1])); }
+break;
+case 136:
+#line 402 "a2p.y"
+{ yyval = oper3(OBLOCK,oper2(OJUNK,yyvsp[-3],yyvsp[-2]),Nullop,yyvsp[0]); }
+break;
+#line 2660 "y.tab.c"
+ }
+ yyssp -= yym;
+ yystate = *yyssp;
+ yyvsp -= yym;
+ yym = yylhs[yyn];
+ if (yystate == 0 && yym == 0)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state 0 to\
+ state %d\n", YYPREFIX, YYFINAL);
+#endif
+ yystate = YYFINAL;
+ *++yyssp = YYFINAL;
+ *++yyvsp = yyval;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, YYFINAL, yychar, yys);
+ }
+#endif
+ }
+ if (yychar == 0) goto yyaccept;
+ goto yyloop;
+ }
+ if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yystate)
+ yystate = yytable[yyn];
+ else
+ yystate = yydgoto[yym];
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state %d \
+to state %d\n", YYPREFIX, *yyssp, yystate);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate;
+ *++yyvsp = yyval;
+ goto yyloop;
+yyoverflow:
+ yyerror("yacc stack overflow");
+yyabort:
+ return (1);
+yyaccept:
+ return (0);
+}
diff --git a/gnu/usr.bin/perl/x2p/a2p.h b/gnu/usr.bin/perl/x2p/a2p.h
new file mode 100644
index 0000000..7c6a66dd
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/a2p.h
@@ -0,0 +1,341 @@
+/* $RCSfile: a2p.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:09 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: a2p.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:30:09 nate
+ * PERL!
+ *
+ * Revision 4.0.1.2 92/06/08 16:12:23 lwall
+ * patch20: hash tables now split only if the memory is available to do so
+ *
+ * Revision 4.0.1.1 91/06/07 12:12:27 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:57:07 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#define VOIDUSED 1
+#include "config.h"
+
+#ifndef HAS_BCOPY
+# define bcopy(s1,s2,l) memcpy(s2,s1,l)
+#endif
+#ifndef HAS_BZERO
+# define bzero(s,l) memset(s,0,l)
+#endif
+
+#include "handy.h"
+#define Nullop 0
+
+#define OPROG 1
+#define OJUNK 2
+#define OHUNKS 3
+#define ORANGE 4
+#define OPAT 5
+#define OHUNK 6
+#define OPPAREN 7
+#define OPANDAND 8
+#define OPOROR 9
+#define OPNOT 10
+#define OCPAREN 11
+#define OCANDAND 12
+#define OCOROR 13
+#define OCNOT 14
+#define ORELOP 15
+#define ORPAREN 16
+#define OMATCHOP 17
+#define OMPAREN 18
+#define OCONCAT 19
+#define OASSIGN 20
+#define OADD 21
+#define OSUBTRACT 22
+#define OMULT 23
+#define ODIV 24
+#define OMOD 25
+#define OPOSTINCR 26
+#define OPOSTDECR 27
+#define OPREINCR 28
+#define OPREDECR 29
+#define OUMINUS 30
+#define OUPLUS 31
+#define OPAREN 32
+#define OGETLINE 33
+#define OSPRINTF 34
+#define OSUBSTR 35
+#define OSTRING 36
+#define OSPLIT 37
+#define OSNEWLINE 38
+#define OINDEX 39
+#define ONUM 40
+#define OSTR 41
+#define OVAR 42
+#define OFLD 43
+#define ONEWLINE 44
+#define OCOMMENT 45
+#define OCOMMA 46
+#define OSEMICOLON 47
+#define OSCOMMENT 48
+#define OSTATES 49
+#define OSTATE 50
+#define OPRINT 51
+#define OPRINTF 52
+#define OBREAK 53
+#define ONEXT 54
+#define OEXIT 55
+#define OCONTINUE 56
+#define OREDIR 57
+#define OIF 58
+#define OWHILE 59
+#define OFOR 60
+#define OFORIN 61
+#define OVFLD 62
+#define OBLOCK 63
+#define OREGEX 64
+#define OLENGTH 65
+#define OLOG 66
+#define OEXP 67
+#define OSQRT 68
+#define OINT 69
+#define ODO 70
+#define OPOW 71
+#define OSUB 72
+#define OGSUB 73
+#define OMATCH 74
+#define OUSERFUN 75
+#define OUSERDEF 76
+#define OCLOSE 77
+#define OATAN2 78
+#define OSIN 79
+#define OCOS 80
+#define ORAND 81
+#define OSRAND 82
+#define ODELETE 83
+#define OSYSTEM 84
+#define OCOND 85
+#define ORETURN 86
+#define ODEFINED 87
+#define OSTAR 88
+
+#ifdef DOINIT
+char *opname[] = {
+ "0",
+ "PROG",
+ "JUNK",
+ "HUNKS",
+ "RANGE",
+ "PAT",
+ "HUNK",
+ "PPAREN",
+ "PANDAND",
+ "POROR",
+ "PNOT",
+ "CPAREN",
+ "CANDAND",
+ "COROR",
+ "CNOT",
+ "RELOP",
+ "RPAREN",
+ "MATCHOP",
+ "MPAREN",
+ "CONCAT",
+ "ASSIGN",
+ "ADD",
+ "SUBTRACT",
+ "MULT",
+ "DIV",
+ "MOD",
+ "POSTINCR",
+ "POSTDECR",
+ "PREINCR",
+ "PREDECR",
+ "UMINUS",
+ "UPLUS",
+ "PAREN",
+ "GETLINE",
+ "SPRINTF",
+ "SUBSTR",
+ "STRING",
+ "SPLIT",
+ "SNEWLINE",
+ "INDEX",
+ "NUM",
+ "STR",
+ "VAR",
+ "FLD",
+ "NEWLINE",
+ "COMMENT",
+ "COMMA",
+ "SEMICOLON",
+ "SCOMMENT",
+ "STATES",
+ "STATE",
+ "PRINT",
+ "PRINTF",
+ "BREAK",
+ "NEXT",
+ "EXIT",
+ "CONTINUE",
+ "REDIR",
+ "IF",
+ "WHILE",
+ "FOR",
+ "FORIN",
+ "VFLD",
+ "BLOCK",
+ "REGEX",
+ "LENGTH",
+ "LOG",
+ "EXP",
+ "SQRT",
+ "INT",
+ "DO",
+ "POW",
+ "SUB",
+ "GSUB",
+ "MATCH",
+ "USERFUN",
+ "USERDEF",
+ "CLOSE",
+ "ATAN2",
+ "SIN",
+ "COS",
+ "RAND",
+ "SRAND",
+ "DELETE",
+ "SYSTEM",
+ "COND",
+ "RETURN",
+ "DEFINED",
+ "STAR",
+ "89"
+};
+#else
+extern char *opname[];
+#endif
+
+EXT int mop INIT(1);
+
+union u_ops {
+ int ival;
+ char *cval;
+};
+#if defined(iAPX286) || defined(M_I286) || defined(I80286) /* 80286 hack */
+#define OPSMAX (64000/sizeof(union u_ops)) /* approx. max segment size */
+#else
+#define OPSMAX 50000
+#endif /* 80286 hack */
+union u_ops ops[OPSMAX];
+
+#include <stdio.h>
+#include <ctype.h>
+
+typedef struct string STR;
+typedef struct htbl HASH;
+
+#include "str.h"
+#include "hash.h"
+
+/* A string is TRUE if not "" or "0". */
+#define True(val) (tmps = (val), (*tmps && !(*tmps == '0' && !tmps[1])))
+EXT char *Yes INIT("1");
+EXT char *No INIT("");
+
+#define str_true(str) (Str = (str), (Str->str_pok ? True(Str->str_ptr) : (Str->str_nok ? (Str->str_nval != 0.0) : 0 )))
+
+#define str_peek(str) (Str = (str), (Str->str_pok ? Str->str_ptr : (Str->str_nok ? (sprintf(buf,"num(%g)",Str->str_nval),buf) : "" )))
+#define str_get(str) (Str = (str), (Str->str_pok ? Str->str_ptr : str_2ptr(Str)))
+#define str_gnum(str) (Str = (str), (Str->str_nok ? Str->str_nval : str_2num(Str)))
+EXT STR *Str;
+
+#define GROWSTR(pp,lp,len) if (*(lp) < (len)) growstr(pp,lp,len)
+
+STR *str_new();
+
+char *scanpat();
+char *scannum();
+
+void str_free();
+
+EXT int line INIT(0);
+
+EXT FILE *rsfp;
+EXT char buf[2048];
+EXT char *bufptr INIT(buf);
+
+EXT STR *linestr INIT(Nullstr);
+
+EXT char tokenbuf[2048];
+EXT int expectterm INIT(TRUE);
+
+#ifdef DEBUGGING
+EXT int debug INIT(0);
+EXT int dlevel INIT(0);
+#define YYDEBUG 1
+extern int yydebug;
+#endif
+
+EXT STR *freestrroot INIT(Nullstr);
+
+EXT STR str_no;
+EXT STR str_yes;
+
+EXT bool do_split INIT(FALSE);
+EXT bool split_to_array INIT(FALSE);
+EXT bool set_array_base INIT(FALSE);
+EXT bool saw_RS INIT(FALSE);
+EXT bool saw_OFS INIT(FALSE);
+EXT bool saw_ORS INIT(FALSE);
+EXT bool saw_line_op INIT(FALSE);
+EXT bool in_begin INIT(TRUE);
+EXT bool do_opens INIT(FALSE);
+EXT bool do_fancy_opens INIT(FALSE);
+EXT bool lval_field INIT(FALSE);
+EXT bool do_chop INIT(FALSE);
+EXT bool need_entire INIT(FALSE);
+EXT bool absmaxfld INIT(FALSE);
+EXT bool saw_altinput INIT(FALSE);
+
+EXT bool nomemok INIT(FALSE);
+
+EXT char const_FS INIT(0);
+EXT char *namelist INIT(Nullch);
+EXT char fswitch INIT(0);
+
+EXT int saw_FS INIT(0);
+EXT int maxfld INIT(0);
+EXT int arymax INIT(0);
+char *nameary[100];
+
+EXT STR *opens;
+
+EXT HASH *symtab;
+EXT HASH *curarghash;
+
+#define P_MIN 0
+#define P_LISTOP 5
+#define P_COMMA 10
+#define P_ASSIGN 15
+#define P_COND 20
+#define P_DOTDOT 25
+#define P_OROR 30
+#define P_ANDAND 35
+#define P_OR 40
+#define P_AND 45
+#define P_EQ 50
+#define P_REL 55
+#define P_UNI 60
+#define P_FILETEST 65
+#define P_SHIFT 70
+#define P_ADD 75
+#define P_MUL 80
+#define P_MATCH 85
+#define P_UNARY 90
+#define P_POW 95
+#define P_AUTO 100
+#define P_MAX 999
diff --git a/gnu/usr.bin/perl/x2p/a2p.y b/gnu/usr.bin/perl/x2p/a2p.y
new file mode 100644
index 0000000..bc86632
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/a2p.y
@@ -0,0 +1,406 @@
+%{
+/* $RCSfile: a2p.y,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:09 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: a2p.y,v $
+ * Revision 1.1.1.1 1993/08/23 21:30:09 nate
+ * PERL!
+ *
+ * Revision 4.0.1.2 92/06/08 16:13:03 lwall
+ * patch20: in a2p, getline should allow variable to be array element
+ *
+ * Revision 4.0.1.1 91/06/07 12:12:41 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:57:21 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "INTERN.h"
+#include "a2p.h"
+
+int root;
+int begins = Nullop;
+int ends = Nullop;
+
+%}
+%token BEGIN END
+%token REGEX
+%token SEMINEW NEWLINE COMMENT
+%token FUN1 FUNN GRGR
+%token PRINT PRINTF SPRINTF SPLIT
+%token IF ELSE WHILE FOR IN
+%token EXIT NEXT BREAK CONTINUE RET
+%token GETLINE DO SUB GSUB MATCH
+%token FUNCTION USERFUN DELETE
+
+%right ASGNOP
+%right '?' ':'
+%left OROR
+%left ANDAND
+%left IN
+%left NUMBER VAR SUBSTR INDEX
+%left MATCHOP
+%left RELOP '<' '>'
+%left OR
+%left STRING
+%left '+' '-'
+%left '*' '/' '%'
+%right UMINUS
+%left NOT
+%right '^'
+%left INCR DECR
+%left FIELD VFIELD
+
+%%
+
+program : junk hunks
+ { root = oper4(OPROG,$1,begins,$2,ends); }
+ ;
+
+begin : BEGIN '{' maybe states '}' junk
+ { begins = oper4(OJUNK,begins,$3,$4,$6); in_begin = FALSE;
+ $$ = Nullop; }
+ ;
+
+end : END '{' maybe states '}'
+ { ends = oper3(OJUNK,ends,$3,$4); $$ = Nullop; }
+ | end NEWLINE
+ { $$ = $1; }
+ ;
+
+hunks : hunks hunk junk
+ { $$ = oper3(OHUNKS,$1,$2,$3); }
+ | /* NULL */
+ { $$ = Nullop; }
+ ;
+
+hunk : patpat
+ { $$ = oper1(OHUNK,$1); need_entire = TRUE; }
+ | patpat '{' maybe states '}'
+ { $$ = oper2(OHUNK,$1,oper2(OJUNK,$3,$4)); }
+ | FUNCTION USERFUN '(' arg_list ')' maybe '{' maybe states '}'
+ { fixfargs($2,$4,0); $$ = oper5(OUSERDEF,$2,$4,$6,$8,$9); }
+ | '{' maybe states '}'
+ { $$ = oper2(OHUNK,Nullop,oper2(OJUNK,$2,$3)); }
+ | begin
+ | end
+ ;
+
+arg_list: expr_list
+ { $$ = rememberargs($$); }
+ ;
+
+patpat : cond
+ { $$ = oper1(OPAT,$1); }
+ | cond ',' cond
+ { $$ = oper2(ORANGE,$1,$3); }
+ ;
+
+cond : expr
+ | match
+ | rel
+ | compound_cond
+ ;
+
+compound_cond
+ : '(' compound_cond ')'
+ { $$ = oper1(OCPAREN,$2); }
+ | cond ANDAND maybe cond
+ { $$ = oper3(OCANDAND,$1,$3,$4); }
+ | cond OROR maybe cond
+ { $$ = oper3(OCOROR,$1,$3,$4); }
+ | NOT cond
+ { $$ = oper1(OCNOT,$2); }
+ ;
+
+rel : expr RELOP expr
+ { $$ = oper3(ORELOP,$2,$1,$3); }
+ | expr '>' expr
+ { $$ = oper3(ORELOP,string(">",1),$1,$3); }
+ | expr '<' expr
+ { $$ = oper3(ORELOP,string("<",1),$1,$3); }
+ | '(' rel ')'
+ { $$ = oper1(ORPAREN,$2); }
+ ;
+
+match : expr MATCHOP expr
+ { $$ = oper3(OMATCHOP,$2,$1,$3); }
+ | expr MATCHOP REGEX
+ { $$ = oper3(OMATCHOP,$2,$1,oper1(OREGEX,$3)); }
+ | REGEX %prec MATCHOP
+ { $$ = oper1(OREGEX,$1); }
+ | '(' match ')'
+ { $$ = oper1(OMPAREN,$2); }
+ ;
+
+expr : term
+ { $$ = $1; }
+ | expr term
+ { $$ = oper2(OCONCAT,$1,$2); }
+ | variable ASGNOP cond
+ { $$ = oper3(OASSIGN,$2,$1,$3);
+ if ((ops[$1].ival & 255) == OFLD)
+ lval_field = TRUE;
+ if ((ops[$1].ival & 255) == OVFLD)
+ lval_field = TRUE;
+ }
+ ;
+
+term : variable
+ { $$ = $1; }
+ | NUMBER
+ { $$ = oper1(ONUM,$1); }
+ | STRING
+ { $$ = oper1(OSTR,$1); }
+ | term '+' term
+ { $$ = oper2(OADD,$1,$3); }
+ | term '-' term
+ { $$ = oper2(OSUBTRACT,$1,$3); }
+ | term '*' term
+ { $$ = oper2(OMULT,$1,$3); }
+ | term '/' term
+ { $$ = oper2(ODIV,$1,$3); }
+ | term '%' term
+ { $$ = oper2(OMOD,$1,$3); }
+ | term '^' term
+ { $$ = oper2(OPOW,$1,$3); }
+ | term IN VAR
+ { $$ = oper2(ODEFINED,aryrefarg($3),$1); }
+ | term '?' term ':' term
+ { $$ = oper3(OCOND,$1,$3,$5); }
+ | variable INCR
+ { $$ = oper1(OPOSTINCR,$1); }
+ | variable DECR
+ { $$ = oper1(OPOSTDECR,$1); }
+ | INCR variable
+ { $$ = oper1(OPREINCR,$2); }
+ | DECR variable
+ { $$ = oper1(OPREDECR,$2); }
+ | '-' term %prec UMINUS
+ { $$ = oper1(OUMINUS,$2); }
+ | '+' term %prec UMINUS
+ { $$ = oper1(OUPLUS,$2); }
+ | '(' cond ')'
+ { $$ = oper1(OPAREN,$2); }
+ | GETLINE
+ { $$ = oper0(OGETLINE); }
+ | GETLINE variable
+ { $$ = oper1(OGETLINE,$2); }
+ | GETLINE '<' expr
+ { $$ = oper3(OGETLINE,Nullop,string("<",1),$3);
+ if (ops[$3].ival != OSTR + (1<<8)) do_fancy_opens = TRUE; }
+ | GETLINE variable '<' expr
+ { $$ = oper3(OGETLINE,$2,string("<",1),$4);
+ if (ops[$4].ival != OSTR + (1<<8)) do_fancy_opens = TRUE; }
+ | term 'p' GETLINE
+ { $$ = oper3(OGETLINE,Nullop,string("|",1),$1);
+ if (ops[$1].ival != OSTR + (1<<8)) do_fancy_opens = TRUE; }
+ | term 'p' GETLINE variable
+ { $$ = oper3(OGETLINE,$4,string("|",1),$1);
+ if (ops[$1].ival != OSTR + (1<<8)) do_fancy_opens = TRUE; }
+ | FUN1
+ { $$ = oper0($1); need_entire = do_chop = TRUE; }
+ | FUN1 '(' ')'
+ { $$ = oper1($1,Nullop); need_entire = do_chop = TRUE; }
+ | FUN1 '(' expr ')'
+ { $$ = oper1($1,$3); }
+ | FUNN '(' expr_list ')'
+ { $$ = oper1($1,$3); }
+ | USERFUN '(' expr_list ')'
+ { $$ = oper2(OUSERFUN,$1,$3); }
+ | SPRINTF expr_list
+ { $$ = oper1(OSPRINTF,$2); }
+ | SUBSTR '(' expr ',' expr ',' expr ')'
+ { $$ = oper3(OSUBSTR,$3,$5,$7); }
+ | SUBSTR '(' expr ',' expr ')'
+ { $$ = oper2(OSUBSTR,$3,$5); }
+ | SPLIT '(' expr ',' VAR ',' expr ')'
+ { $$ = oper3(OSPLIT,$3,aryrefarg(numary($5)),$7); }
+ | SPLIT '(' expr ',' VAR ',' REGEX ')'
+ { $$ = oper3(OSPLIT,$3,aryrefarg(numary($5)),oper1(OREGEX,$7));}
+ | SPLIT '(' expr ',' VAR ')'
+ { $$ = oper2(OSPLIT,$3,aryrefarg(numary($5))); }
+ | INDEX '(' expr ',' expr ')'
+ { $$ = oper2(OINDEX,$3,$5); }
+ | MATCH '(' expr ',' REGEX ')'
+ { $$ = oper2(OMATCH,$3,oper1(OREGEX,$5)); }
+ | MATCH '(' expr ',' expr ')'
+ { $$ = oper2(OMATCH,$3,$5); }
+ | SUB '(' expr ',' expr ')'
+ { $$ = oper2(OSUB,$3,$5); }
+ | SUB '(' REGEX ',' expr ')'
+ { $$ = oper2(OSUB,oper1(OREGEX,$3),$5); }
+ | GSUB '(' expr ',' expr ')'
+ { $$ = oper2(OGSUB,$3,$5); }
+ | GSUB '(' REGEX ',' expr ')'
+ { $$ = oper2(OGSUB,oper1(OREGEX,$3),$5); }
+ | SUB '(' expr ',' expr ',' expr ')'
+ { $$ = oper3(OSUB,$3,$5,$7); }
+ | SUB '(' REGEX ',' expr ',' expr ')'
+ { $$ = oper3(OSUB,oper1(OREGEX,$3),$5,$7); }
+ | GSUB '(' expr ',' expr ',' expr ')'
+ { $$ = oper3(OGSUB,$3,$5,$7); }
+ | GSUB '(' REGEX ',' expr ',' expr ')'
+ { $$ = oper3(OGSUB,oper1(OREGEX,$3),$5,$7); }
+ ;
+
+variable: VAR
+ { $$ = oper1(OVAR,$1); }
+ | VAR '[' expr_list ']'
+ { $$ = oper2(OVAR,aryrefarg($1),$3); }
+ | FIELD
+ { $$ = oper1(OFLD,$1); }
+ | VFIELD term
+ { $$ = oper1(OVFLD,$2); }
+ ;
+
+expr_list
+ : expr
+ | clist
+ | /* NULL */
+ { $$ = Nullop; }
+ ;
+
+clist : expr ',' maybe expr
+ { $$ = oper3(OCOMMA,$1,$3,$4); }
+ | clist ',' maybe expr
+ { $$ = oper3(OCOMMA,$1,$3,$4); }
+ | '(' clist ')' /* these parens are invisible */
+ { $$ = $2; }
+ ;
+
+junk : junk hunksep
+ { $$ = oper2(OJUNK,$1,$2); }
+ | /* NULL */
+ { $$ = Nullop; }
+ ;
+
+hunksep : ';'
+ { $$ = oper2(OJUNK,oper0(OSEMICOLON),oper0(ONEWLINE)); }
+ | SEMINEW
+ { $$ = oper2(OJUNK,oper0(OSEMICOLON),oper0(ONEWLINE)); }
+ | NEWLINE
+ { $$ = oper0(ONEWLINE); }
+ | COMMENT
+ { $$ = oper1(OCOMMENT,$1); }
+ ;
+
+maybe : maybe nlstuff
+ { $$ = oper2(OJUNK,$1,$2); }
+ | /* NULL */
+ { $$ = Nullop; }
+ ;
+
+nlstuff : NEWLINE
+ { $$ = oper0(ONEWLINE); }
+ | COMMENT
+ { $$ = oper1(OCOMMENT,$1); }
+ ;
+
+separator
+ : ';' maybe
+ { $$ = oper2(OJUNK,oper0(OSEMICOLON),$2); }
+ | SEMINEW maybe
+ { $$ = oper2(OJUNK,oper0(OSNEWLINE),$2); }
+ | NEWLINE maybe
+ { $$ = oper2(OJUNK,oper0(OSNEWLINE),$2); }
+ | COMMENT maybe
+ { $$ = oper2(OJUNK,oper1(OSCOMMENT,$1),$2); }
+ ;
+
+states : states statement
+ { $$ = oper2(OSTATES,$1,$2); }
+ | /* NULL */
+ { $$ = Nullop; }
+ ;
+
+statement
+ : simple separator maybe
+ { $$ = oper2(OJUNK,oper2(OSTATE,$1,$2),$3); }
+ | ';' maybe
+ { $$ = oper2(OSTATE,Nullop,oper2(OJUNK,oper0(OSEMICOLON),$2)); }
+ | SEMINEW maybe
+ { $$ = oper2(OSTATE,Nullop,oper2(OJUNK,oper0(OSNEWLINE),$2)); }
+ | compound
+ ;
+
+simpnull: simple
+ | /* NULL */
+ { $$ = Nullop; }
+ ;
+
+simple
+ : expr
+ | PRINT expr_list redir expr
+ { $$ = oper3(OPRINT,$2,$3,$4);
+ do_opens = TRUE;
+ saw_ORS = saw_OFS = TRUE;
+ if (!$2) need_entire = TRUE;
+ if (ops[$4].ival != OSTR + (1<<8)) do_fancy_opens = TRUE; }
+ | PRINT expr_list
+ { $$ = oper1(OPRINT,$2);
+ if (!$2) need_entire = TRUE;
+ saw_ORS = saw_OFS = TRUE;
+ }
+ | PRINTF expr_list redir expr
+ { $$ = oper3(OPRINTF,$2,$3,$4);
+ do_opens = TRUE;
+ if (!$2) need_entire = TRUE;
+ if (ops[$4].ival != OSTR + (1<<8)) do_fancy_opens = TRUE; }
+ | PRINTF expr_list
+ { $$ = oper1(OPRINTF,$2);
+ if (!$2) need_entire = TRUE;
+ }
+ | BREAK
+ { $$ = oper0(OBREAK); }
+ | NEXT
+ { $$ = oper0(ONEXT); }
+ | EXIT
+ { $$ = oper0(OEXIT); }
+ | EXIT expr
+ { $$ = oper1(OEXIT,$2); }
+ | CONTINUE
+ { $$ = oper0(OCONTINUE); }
+ | RET
+ { $$ = oper0(ORETURN); }
+ | RET expr
+ { $$ = oper1(ORETURN,$2); }
+ | DELETE VAR '[' expr ']'
+ { $$ = oper2(ODELETE,aryrefarg($2),$4); }
+ ;
+
+redir : '>' %prec FIELD
+ { $$ = oper1(OREDIR,string(">",1)); }
+ | GRGR
+ { $$ = oper1(OREDIR,string(">>",2)); }
+ | '|'
+ { $$ = oper1(OREDIR,string("|",1)); }
+ ;
+
+compound
+ : IF '(' cond ')' maybe statement
+ { $$ = oper2(OIF,$3,bl($6,$5)); }
+ | IF '(' cond ')' maybe statement ELSE maybe statement
+ { $$ = oper3(OIF,$3,bl($6,$5),bl($9,$8)); }
+ | WHILE '(' cond ')' maybe statement
+ { $$ = oper2(OWHILE,$3,bl($6,$5)); }
+ | DO maybe statement WHILE '(' cond ')'
+ { $$ = oper2(ODO,bl($3,$2),$6); }
+ | FOR '(' simpnull ';' cond ';' simpnull ')' maybe statement
+ { $$ = oper4(OFOR,$3,$5,$7,bl($10,$9)); }
+ | FOR '(' simpnull ';' ';' simpnull ')' maybe statement
+ { $$ = oper4(OFOR,$3,string("",0),$6,bl($9,$8)); }
+ | FOR '(' expr ')' maybe statement
+ { $$ = oper2(OFORIN,$3,bl($6,$5)); }
+ | '{' maybe states '}' maybe
+ { $$ = oper3(OBLOCK,oper2(OJUNK,$2,$3),Nullop,$5); }
+ ;
+
+%%
+#include "a2py.c"
diff --git a/gnu/usr.bin/perl/x2p/a2py.c b/gnu/usr.bin/perl/x2p/a2py.c
new file mode 100644
index 0000000..1558f99
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/a2py.c
@@ -0,0 +1,1301 @@
+/* $RCSfile: a2py.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:10 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: a2py.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:30:10 nate
+ * PERL!
+ *
+ * Revision 4.0.1.2 92/06/08 16:15:16 lwall
+ * patch20: in a2p, now warns about spurious backslashes
+ * patch20: in a2p, now allows [ to be backslashed in pattern
+ * patch20: in a2p, now allows numbers of the form 2.
+ *
+ * Revision 4.0.1.1 91/06/07 12:12:59 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:57:26 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#ifdef OS2
+#include "../patchlevel.h"
+#endif
+#include "util.h"
+char *index();
+
+char *filename;
+char *myname;
+
+int checkers = 0;
+STR *walk();
+
+#ifdef OS2
+usage()
+{
+ printf("\nThis is the AWK to PERL translator, version 4.0, patchlevel %d\n", PATCHLEVEL);
+ printf("\nUsage: %s [-D<number>] [-F<char>] [-n<fieldlist>] [-<number>] filename\n", myname);
+ printf("\n -D<number> sets debugging flags."
+ "\n -F<character> the awk script to translate is always invoked with"
+ "\n this -F switch."
+ "\n -n<fieldlist> specifies the names of the input fields if input does"
+ "\n not have to be split into an array."
+ "\n -<number> causes a2p to assume that input will always have that"
+ "\n many fields.\n");
+ exit(1);
+}
+#endif
+main(argc,argv,env)
+register int argc;
+register char **argv;
+register char **env;
+{
+ register STR *str;
+ register char *s;
+ int i;
+ STR *tmpstr;
+
+ myname = argv[0];
+ linestr = str_new(80);
+ str = str_new(0); /* first used for -I flags */
+ for (argc--,argv++; argc; argc--,argv++) {
+ if (argv[0][0] != '-' || !argv[0][1])
+ break;
+ reswitch:
+ switch (argv[0][1]) {
+#ifdef DEBUGGING
+ case 'D':
+ debug = atoi(argv[0]+2);
+#ifdef YYDEBUG
+ yydebug = (debug & 1);
+#endif
+ break;
+#endif
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ maxfld = atoi(argv[0]+1);
+ absmaxfld = TRUE;
+ break;
+ case 'F':
+ fswitch = argv[0][2];
+ break;
+ case 'n':
+ namelist = savestr(argv[0]+2);
+ break;
+ case '-':
+ argc--,argv++;
+ goto switch_end;
+ case 0:
+ break;
+ default:
+ fatal("Unrecognized switch: %s\n",argv[0]);
+#ifdef OS2
+ usage();
+#endif
+ }
+ }
+ switch_end:
+
+ /* open script */
+
+ if (argv[0] == Nullch) {
+#ifdef OS2
+ if ( isatty(fileno(stdin)) )
+ usage();
+#endif
+ argv[0] = "-";
+ }
+ filename = savestr(argv[0]);
+
+ filename = savestr(argv[0]);
+ if (strEQ(filename,"-"))
+ argv[0] = "";
+ if (!*argv[0])
+ rsfp = stdin;
+ else
+ rsfp = fopen(argv[0],"r");
+ if (rsfp == Nullfp)
+ fatal("Awk script \"%s\" doesn't seem to exist.\n",filename);
+
+ /* init tokener */
+
+ bufptr = str_get(linestr);
+ symtab = hnew();
+ curarghash = hnew();
+
+ /* now parse the report spec */
+
+ if (yyparse())
+ fatal("Translation aborted due to syntax errors.\n");
+
+#ifdef DEBUGGING
+ if (debug & 2) {
+ int type, len;
+
+ for (i=1; i<mop;) {
+ type = ops[i].ival;
+ len = type >> 8;
+ type &= 255;
+ printf("%d\t%d\t%d\t%-10s",i++,type,len,opname[type]);
+ if (type == OSTRING)
+ printf("\t\"%s\"\n",ops[i].cval),i++;
+ else {
+ while (len--) {
+ printf("\t%d",ops[i].ival),i++;
+ }
+ putchar('\n');
+ }
+ }
+ }
+ if (debug & 8)
+ dump(root);
+#endif
+
+ /* first pass to look for numeric variables */
+
+ prewalk(0,0,root,&i);
+
+ /* second pass to produce new program */
+
+ tmpstr = walk(0,0,root,&i,P_MIN);
+ str = str_make("#!");
+ str_cat(str, BIN);
+ str_cat(str, "/perl\neval \"exec ");
+ str_cat(str, BIN);
+ str_cat(str, "/perl -S $0 $*\"\n\
+ if $running_under_some_shell;\n\
+ # this emulates #! processing on NIH machines.\n\
+ # (remove #! line above if indigestible)\n\n");
+ str_cat(str,
+ "eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_]+=)(.*)/ && shift;\n");
+ str_cat(str,
+ " # process any FOO=bar switches\n\n");
+ if (do_opens && opens) {
+ str_scat(str,opens);
+ str_free(opens);
+ str_cat(str,"\n");
+ }
+ str_scat(str,tmpstr);
+ str_free(tmpstr);
+#ifdef DEBUGGING
+ if (!(debug & 16))
+#endif
+ fixup(str);
+ putlines(str);
+ if (checkers) {
+ fprintf(stderr,
+ "Please check my work on the %d line%s I've marked with \"#???\".\n",
+ checkers, checkers == 1 ? "" : "s" );
+ fprintf(stderr,
+ "The operation I've selected may be wrong for the operand types.\n");
+ }
+ exit(0);
+}
+
+#define RETURN(retval) return (bufptr = s,retval)
+#define XTERM(retval) return (expectterm = TRUE,bufptr = s,retval)
+#define XOP(retval) return (expectterm = FALSE,bufptr = s,retval)
+#define ID(x) return (yylval=string(x,0),expectterm = FALSE,bufptr = s,idtype)
+
+int idtype;
+
+yylex()
+{
+ register char *s = bufptr;
+ register char *d;
+ register int tmp;
+
+ retry:
+#ifdef YYDEBUG
+ if (yydebug)
+ if (index(s,'\n'))
+ fprintf(stderr,"Tokener at %s",s);
+ else
+ fprintf(stderr,"Tokener at %s\n",s);
+#endif
+ switch (*s) {
+ default:
+ fprintf(stderr,
+ "Unrecognized character %c in file %s line %d--ignoring.\n",
+ *s++,filename,line);
+ goto retry;
+ case '\\':
+ s++;
+ if (*s && *s != '\n') {
+ yyerror("Ignoring spurious backslash");
+ goto retry;
+ }
+ /*FALLSTHROUGH*/
+ case 0:
+ s = str_get(linestr);
+ *s = '\0';
+ if (!rsfp)
+ RETURN(0);
+ line++;
+ if ((s = str_gets(linestr, rsfp)) == Nullch) {
+ if (rsfp != stdin)
+ fclose(rsfp);
+ rsfp = Nullfp;
+ s = str_get(linestr);
+ RETURN(0);
+ }
+ goto retry;
+ case ' ': case '\t':
+ s++;
+ goto retry;
+ case '\n':
+ *s = '\0';
+ XTERM(NEWLINE);
+ case '#':
+ yylval = string(s,0);
+ *s = '\0';
+ XTERM(COMMENT);
+ case ';':
+ tmp = *s++;
+ if (*s == '\n') {
+ s++;
+ XTERM(SEMINEW);
+ }
+ XTERM(tmp);
+ case '(':
+ tmp = *s++;
+ XTERM(tmp);
+ case '{':
+ case '[':
+ case ')':
+ case ']':
+ case '?':
+ case ':':
+ tmp = *s++;
+ XOP(tmp);
+ case 127:
+ s++;
+ XTERM('}');
+ case '}':
+ for (d = s + 1; isspace(*d); d++) ;
+ if (!*d)
+ s = d - 1;
+ *s = 127;
+ XTERM(';');
+ case ',':
+ tmp = *s++;
+ XTERM(tmp);
+ case '~':
+ s++;
+ yylval = string("~",1);
+ XTERM(MATCHOP);
+ case '+':
+ case '-':
+ if (s[1] == *s) {
+ s++;
+ if (*s++ == '+')
+ XTERM(INCR);
+ else
+ XTERM(DECR);
+ }
+ /* FALL THROUGH */
+ case '*':
+ case '%':
+ case '^':
+ tmp = *s++;
+ if (*s == '=') {
+ if (tmp == '^')
+ yylval = string("**=",3);
+ else
+ yylval = string(s-1,2);
+ s++;
+ XTERM(ASGNOP);
+ }
+ XTERM(tmp);
+ case '&':
+ s++;
+ tmp = *s++;
+ if (tmp == '&')
+ XTERM(ANDAND);
+ s--;
+ XTERM('&');
+ case '|':
+ s++;
+ tmp = *s++;
+ if (tmp == '|')
+ XTERM(OROR);
+ s--;
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (strnEQ(s,"getline",7))
+ XTERM('p');
+ else
+ XTERM('|');
+ case '=':
+ s++;
+ tmp = *s++;
+ if (tmp == '=') {
+ yylval = string("==",2);
+ XTERM(RELOP);
+ }
+ s--;
+ yylval = string("=",1);
+ XTERM(ASGNOP);
+ case '!':
+ s++;
+ tmp = *s++;
+ if (tmp == '=') {
+ yylval = string("!=",2);
+ XTERM(RELOP);
+ }
+ if (tmp == '~') {
+ yylval = string("!~",2);
+ XTERM(MATCHOP);
+ }
+ s--;
+ XTERM(NOT);
+ case '<':
+ s++;
+ tmp = *s++;
+ if (tmp == '=') {
+ yylval = string("<=",2);
+ XTERM(RELOP);
+ }
+ s--;
+ XTERM('<');
+ case '>':
+ s++;
+ tmp = *s++;
+ if (tmp == '>') {
+ yylval = string(">>",2);
+ XTERM(GRGR);
+ }
+ if (tmp == '=') {
+ yylval = string(">=",2);
+ XTERM(RELOP);
+ }
+ s--;
+ XTERM('>');
+
+#define SNARFWORD \
+ d = tokenbuf; \
+ while (isalpha(*s) || isdigit(*s) || *s == '_') \
+ *d++ = *s++; \
+ *d = '\0'; \
+ d = tokenbuf; \
+ if (*s == '(') \
+ idtype = USERFUN; \
+ else \
+ idtype = VAR;
+
+ case '$':
+ s++;
+ if (*s == '0') {
+ s++;
+ do_chop = TRUE;
+ need_entire = TRUE;
+ idtype = VAR;
+ ID("0");
+ }
+ do_split = TRUE;
+ if (isdigit(*s)) {
+ for (d = s; isdigit(*s); s++) ;
+ yylval = string(d,s-d);
+ tmp = atoi(d);
+ if (tmp > maxfld)
+ maxfld = tmp;
+ XOP(FIELD);
+ }
+ split_to_array = set_array_base = TRUE;
+ XOP(VFIELD);
+
+ case '/': /* may either be division or pattern */
+ if (expectterm) {
+ s = scanpat(s);
+ XTERM(REGEX);
+ }
+ tmp = *s++;
+ if (*s == '=') {
+ yylval = string("/=",2);
+ s++;
+ XTERM(ASGNOP);
+ }
+ XTERM(tmp);
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9': case '.':
+ s = scannum(s);
+ XOP(NUMBER);
+ case '"':
+ s++;
+ s = cpy2(tokenbuf,s,s[-1]);
+ if (!*s)
+ fatal("String not terminated:\n%s",str_get(linestr));
+ s++;
+ yylval = string(tokenbuf,0);
+ XOP(STRING);
+
+ case 'a': case 'A':
+ SNARFWORD;
+ if (strEQ(d,"ARGC"))
+ set_array_base = TRUE;
+ if (strEQ(d,"ARGV")) {
+ yylval=numary(string("ARGV",0));
+ XOP(VAR);
+ }
+ if (strEQ(d,"atan2")) {
+ yylval = OATAN2;
+ XTERM(FUNN);
+ }
+ ID(d);
+ case 'b': case 'B':
+ SNARFWORD;
+ if (strEQ(d,"break"))
+ XTERM(BREAK);
+ if (strEQ(d,"BEGIN"))
+ XTERM(BEGIN);
+ ID(d);
+ case 'c': case 'C':
+ SNARFWORD;
+ if (strEQ(d,"continue"))
+ XTERM(CONTINUE);
+ if (strEQ(d,"cos")) {
+ yylval = OCOS;
+ XTERM(FUN1);
+ }
+ if (strEQ(d,"close")) {
+ do_fancy_opens = 1;
+ yylval = OCLOSE;
+ XTERM(FUN1);
+ }
+ if (strEQ(d,"chdir"))
+ *d = toupper(*d);
+ else if (strEQ(d,"crypt"))
+ *d = toupper(*d);
+ else if (strEQ(d,"chop"))
+ *d = toupper(*d);
+ else if (strEQ(d,"chmod"))
+ *d = toupper(*d);
+ else if (strEQ(d,"chown"))
+ *d = toupper(*d);
+ ID(d);
+ case 'd': case 'D':
+ SNARFWORD;
+ if (strEQ(d,"do"))
+ XTERM(DO);
+ if (strEQ(d,"delete"))
+ XTERM(DELETE);
+ if (strEQ(d,"die"))
+ *d = toupper(*d);
+ ID(d);
+ case 'e': case 'E':
+ SNARFWORD;
+ if (strEQ(d,"END"))
+ XTERM(END);
+ if (strEQ(d,"else"))
+ XTERM(ELSE);
+ if (strEQ(d,"exit")) {
+ saw_line_op = TRUE;
+ XTERM(EXIT);
+ }
+ if (strEQ(d,"exp")) {
+ yylval = OEXP;
+ XTERM(FUN1);
+ }
+ if (strEQ(d,"elsif"))
+ *d = toupper(*d);
+ else if (strEQ(d,"eq"))
+ *d = toupper(*d);
+ else if (strEQ(d,"eval"))
+ *d = toupper(*d);
+ else if (strEQ(d,"eof"))
+ *d = toupper(*d);
+ else if (strEQ(d,"each"))
+ *d = toupper(*d);
+ else if (strEQ(d,"exec"))
+ *d = toupper(*d);
+ ID(d);
+ case 'f': case 'F':
+ SNARFWORD;
+ if (strEQ(d,"FS")) {
+ saw_FS++;
+ if (saw_FS == 1 && in_begin) {
+ for (d = s; *d && isspace(*d); d++) ;
+ if (*d == '=') {
+ for (d++; *d && isspace(*d); d++) ;
+ if (*d == '"' && d[2] == '"')
+ const_FS = d[1];
+ }
+ }
+ ID(tokenbuf);
+ }
+ if (strEQ(d,"for"))
+ XTERM(FOR);
+ else if (strEQ(d,"function"))
+ XTERM(FUNCTION);
+ if (strEQ(d,"FILENAME"))
+ d = "ARGV";
+ if (strEQ(d,"foreach"))
+ *d = toupper(*d);
+ else if (strEQ(d,"format"))
+ *d = toupper(*d);
+ else if (strEQ(d,"fork"))
+ *d = toupper(*d);
+ else if (strEQ(d,"fh"))
+ *d = toupper(*d);
+ ID(d);
+ case 'g': case 'G':
+ SNARFWORD;
+ if (strEQ(d,"getline"))
+ XTERM(GETLINE);
+ if (strEQ(d,"gsub"))
+ XTERM(GSUB);
+ if (strEQ(d,"ge"))
+ *d = toupper(*d);
+ else if (strEQ(d,"gt"))
+ *d = toupper(*d);
+ else if (strEQ(d,"goto"))
+ *d = toupper(*d);
+ else if (strEQ(d,"gmtime"))
+ *d = toupper(*d);
+ ID(d);
+ case 'h': case 'H':
+ SNARFWORD;
+ if (strEQ(d,"hex"))
+ *d = toupper(*d);
+ ID(d);
+ case 'i': case 'I':
+ SNARFWORD;
+ if (strEQ(d,"if"))
+ XTERM(IF);
+ if (strEQ(d,"in"))
+ XTERM(IN);
+ if (strEQ(d,"index")) {
+ set_array_base = TRUE;
+ XTERM(INDEX);
+ }
+ if (strEQ(d,"int")) {
+ yylval = OINT;
+ XTERM(FUN1);
+ }
+ ID(d);
+ case 'j': case 'J':
+ SNARFWORD;
+ if (strEQ(d,"join"))
+ *d = toupper(*d);
+ ID(d);
+ case 'k': case 'K':
+ SNARFWORD;
+ if (strEQ(d,"keys"))
+ *d = toupper(*d);
+ else if (strEQ(d,"kill"))
+ *d = toupper(*d);
+ ID(d);
+ case 'l': case 'L':
+ SNARFWORD;
+ if (strEQ(d,"length")) {
+ yylval = OLENGTH;
+ XTERM(FUN1);
+ }
+ if (strEQ(d,"log")) {
+ yylval = OLOG;
+ XTERM(FUN1);
+ }
+ if (strEQ(d,"last"))
+ *d = toupper(*d);
+ else if (strEQ(d,"local"))
+ *d = toupper(*d);
+ else if (strEQ(d,"lt"))
+ *d = toupper(*d);
+ else if (strEQ(d,"le"))
+ *d = toupper(*d);
+ else if (strEQ(d,"locatime"))
+ *d = toupper(*d);
+ else if (strEQ(d,"link"))
+ *d = toupper(*d);
+ ID(d);
+ case 'm': case 'M':
+ SNARFWORD;
+ if (strEQ(d,"match")) {
+ set_array_base = TRUE;
+ XTERM(MATCH);
+ }
+ if (strEQ(d,"m"))
+ *d = toupper(*d);
+ ID(d);
+ case 'n': case 'N':
+ SNARFWORD;
+ if (strEQ(d,"NF"))
+ do_chop = do_split = split_to_array = set_array_base = TRUE;
+ if (strEQ(d,"next")) {
+ saw_line_op = TRUE;
+ XTERM(NEXT);
+ }
+ if (strEQ(d,"ne"))
+ *d = toupper(*d);
+ ID(d);
+ case 'o': case 'O':
+ SNARFWORD;
+ if (strEQ(d,"ORS")) {
+ saw_ORS = TRUE;
+ d = "\\";
+ }
+ if (strEQ(d,"OFS")) {
+ saw_OFS = TRUE;
+ d = ",";
+ }
+ if (strEQ(d,"OFMT")) {
+ d = "#";
+ }
+ if (strEQ(d,"open"))
+ *d = toupper(*d);
+ else if (strEQ(d,"ord"))
+ *d = toupper(*d);
+ else if (strEQ(d,"oct"))
+ *d = toupper(*d);
+ ID(d);
+ case 'p': case 'P':
+ SNARFWORD;
+ if (strEQ(d,"print")) {
+ XTERM(PRINT);
+ }
+ if (strEQ(d,"printf")) {
+ XTERM(PRINTF);
+ }
+ if (strEQ(d,"push"))
+ *d = toupper(*d);
+ else if (strEQ(d,"pop"))
+ *d = toupper(*d);
+ ID(d);
+ case 'q': case 'Q':
+ SNARFWORD;
+ ID(d);
+ case 'r': case 'R':
+ SNARFWORD;
+ if (strEQ(d,"RS")) {
+ d = "/";
+ saw_RS = TRUE;
+ }
+ if (strEQ(d,"rand")) {
+ yylval = ORAND;
+ XTERM(FUN1);
+ }
+ if (strEQ(d,"return"))
+ XTERM(RET);
+ if (strEQ(d,"reset"))
+ *d = toupper(*d);
+ else if (strEQ(d,"redo"))
+ *d = toupper(*d);
+ else if (strEQ(d,"rename"))
+ *d = toupper(*d);
+ ID(d);
+ case 's': case 'S':
+ SNARFWORD;
+ if (strEQ(d,"split")) {
+ set_array_base = TRUE;
+ XOP(SPLIT);
+ }
+ if (strEQ(d,"substr")) {
+ set_array_base = TRUE;
+ XTERM(SUBSTR);
+ }
+ if (strEQ(d,"sub"))
+ XTERM(SUB);
+ if (strEQ(d,"sprintf"))
+ XTERM(SPRINTF);
+ if (strEQ(d,"sqrt")) {
+ yylval = OSQRT;
+ XTERM(FUN1);
+ }
+ if (strEQ(d,"SUBSEP")) {
+ d = ";";
+ }
+ if (strEQ(d,"sin")) {
+ yylval = OSIN;
+ XTERM(FUN1);
+ }
+ if (strEQ(d,"srand")) {
+ yylval = OSRAND;
+ XTERM(FUN1);
+ }
+ if (strEQ(d,"system")) {
+ yylval = OSYSTEM;
+ XTERM(FUN1);
+ }
+ if (strEQ(d,"s"))
+ *d = toupper(*d);
+ else if (strEQ(d,"shift"))
+ *d = toupper(*d);
+ else if (strEQ(d,"select"))
+ *d = toupper(*d);
+ else if (strEQ(d,"seek"))
+ *d = toupper(*d);
+ else if (strEQ(d,"stat"))
+ *d = toupper(*d);
+ else if (strEQ(d,"study"))
+ *d = toupper(*d);
+ else if (strEQ(d,"sleep"))
+ *d = toupper(*d);
+ else if (strEQ(d,"symlink"))
+ *d = toupper(*d);
+ else if (strEQ(d,"sort"))
+ *d = toupper(*d);
+ ID(d);
+ case 't': case 'T':
+ SNARFWORD;
+ if (strEQ(d,"tr"))
+ *d = toupper(*d);
+ else if (strEQ(d,"tell"))
+ *d = toupper(*d);
+ else if (strEQ(d,"time"))
+ *d = toupper(*d);
+ else if (strEQ(d,"times"))
+ *d = toupper(*d);
+ ID(d);
+ case 'u': case 'U':
+ SNARFWORD;
+ if (strEQ(d,"until"))
+ *d = toupper(*d);
+ else if (strEQ(d,"unless"))
+ *d = toupper(*d);
+ else if (strEQ(d,"umask"))
+ *d = toupper(*d);
+ else if (strEQ(d,"unshift"))
+ *d = toupper(*d);
+ else if (strEQ(d,"unlink"))
+ *d = toupper(*d);
+ else if (strEQ(d,"utime"))
+ *d = toupper(*d);
+ ID(d);
+ case 'v': case 'V':
+ SNARFWORD;
+ if (strEQ(d,"values"))
+ *d = toupper(*d);
+ ID(d);
+ case 'w': case 'W':
+ SNARFWORD;
+ if (strEQ(d,"while"))
+ XTERM(WHILE);
+ if (strEQ(d,"write"))
+ *d = toupper(*d);
+ else if (strEQ(d,"wait"))
+ *d = toupper(*d);
+ ID(d);
+ case 'x': case 'X':
+ SNARFWORD;
+ if (strEQ(d,"x"))
+ *d = toupper(*d);
+ ID(d);
+ case 'y': case 'Y':
+ SNARFWORD;
+ if (strEQ(d,"y"))
+ *d = toupper(*d);
+ ID(d);
+ case 'z': case 'Z':
+ SNARFWORD;
+ ID(d);
+ }
+}
+
+char *
+scanpat(s)
+register char *s;
+{
+ register char *d;
+
+ switch (*s++) {
+ case '/':
+ break;
+ default:
+ fatal("Search pattern not found:\n%s",str_get(linestr));
+ }
+
+ d = tokenbuf;
+ for (; *s; s++,d++) {
+ if (*s == '\\') {
+ if (s[1] == '/')
+ *d++ = *s++;
+ else if (s[1] == '\\')
+ *d++ = *s++;
+ else if (s[1] == '[')
+ *d++ = *s++;
+ }
+ else if (*s == '[') {
+ *d++ = *s++;
+ do {
+ if (*s == '\\' && s[1])
+ *d++ = *s++;
+ if (*s == '/' || (*s == '-' && s[1] == ']'))
+ *d++ = '\\';
+ *d++ = *s++;
+ } while (*s && *s != ']');
+ }
+ else if (*s == '/')
+ break;
+ *d = *s;
+ }
+ *d = '\0';
+
+ if (!*s)
+ fatal("Search pattern not terminated:\n%s",str_get(linestr));
+ s++;
+ yylval = string(tokenbuf,0);
+ return s;
+}
+
+yyerror(s)
+char *s;
+{
+ fprintf(stderr,"%s in file %s at line %d\n",
+ s,filename,line);
+}
+
+char *
+scannum(s)
+register char *s;
+{
+ register char *d;
+
+ switch (*s) {
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9': case '0' : case '.':
+ d = tokenbuf;
+ while (isdigit(*s)) {
+ *d++ = *s++;
+ }
+ if (*s == '.') {
+ if (isdigit(s[1])) {
+ *d++ = *s++;
+ while (isdigit(*s)) {
+ *d++ = *s++;
+ }
+ }
+ else
+ s++;
+ }
+ if (index("eE",*s) && index("+-0123456789",s[1])) {
+ *d++ = *s++;
+ if (*s == '+' || *s == '-')
+ *d++ = *s++;
+ while (isdigit(*s))
+ *d++ = *s++;
+ }
+ *d = '\0';
+ yylval = string(tokenbuf,0);
+ break;
+ }
+ return s;
+}
+
+string(ptr,len)
+char *ptr;
+{
+ int retval = mop;
+
+ ops[mop++].ival = OSTRING + (1<<8);
+ if (!len)
+ len = strlen(ptr);
+ ops[mop].cval = safemalloc(len+1);
+ strncpy(ops[mop].cval,ptr,len);
+ ops[mop++].cval[len] = '\0';
+ if (mop >= OPSMAX)
+ fatal("Recompile a2p with larger OPSMAX\n");
+ return retval;
+}
+
+oper0(type)
+int type;
+{
+ int retval = mop;
+
+ if (type > 255)
+ fatal("type > 255 (%d)\n",type);
+ ops[mop++].ival = type;
+ if (mop >= OPSMAX)
+ fatal("Recompile a2p with larger OPSMAX\n");
+ return retval;
+}
+
+oper1(type,arg1)
+int type;
+int arg1;
+{
+ int retval = mop;
+
+ if (type > 255)
+ fatal("type > 255 (%d)\n",type);
+ ops[mop++].ival = type + (1<<8);
+ ops[mop++].ival = arg1;
+ if (mop >= OPSMAX)
+ fatal("Recompile a2p with larger OPSMAX\n");
+ return retval;
+}
+
+oper2(type,arg1,arg2)
+int type;
+int arg1;
+int arg2;
+{
+ int retval = mop;
+
+ if (type > 255)
+ fatal("type > 255 (%d)\n",type);
+ ops[mop++].ival = type + (2<<8);
+ ops[mop++].ival = arg1;
+ ops[mop++].ival = arg2;
+ if (mop >= OPSMAX)
+ fatal("Recompile a2p with larger OPSMAX\n");
+ return retval;
+}
+
+oper3(type,arg1,arg2,arg3)
+int type;
+int arg1;
+int arg2;
+int arg3;
+{
+ int retval = mop;
+
+ if (type > 255)
+ fatal("type > 255 (%d)\n",type);
+ ops[mop++].ival = type + (3<<8);
+ ops[mop++].ival = arg1;
+ ops[mop++].ival = arg2;
+ ops[mop++].ival = arg3;
+ if (mop >= OPSMAX)
+ fatal("Recompile a2p with larger OPSMAX\n");
+ return retval;
+}
+
+oper4(type,arg1,arg2,arg3,arg4)
+int type;
+int arg1;
+int arg2;
+int arg3;
+int arg4;
+{
+ int retval = mop;
+
+ if (type > 255)
+ fatal("type > 255 (%d)\n",type);
+ ops[mop++].ival = type + (4<<8);
+ ops[mop++].ival = arg1;
+ ops[mop++].ival = arg2;
+ ops[mop++].ival = arg3;
+ ops[mop++].ival = arg4;
+ if (mop >= OPSMAX)
+ fatal("Recompile a2p with larger OPSMAX\n");
+ return retval;
+}
+
+oper5(type,arg1,arg2,arg3,arg4,arg5)
+int type;
+int arg1;
+int arg2;
+int arg3;
+int arg4;
+int arg5;
+{
+ int retval = mop;
+
+ if (type > 255)
+ fatal("type > 255 (%d)\n",type);
+ ops[mop++].ival = type + (5<<8);
+ ops[mop++].ival = arg1;
+ ops[mop++].ival = arg2;
+ ops[mop++].ival = arg3;
+ ops[mop++].ival = arg4;
+ ops[mop++].ival = arg5;
+ if (mop >= OPSMAX)
+ fatal("Recompile a2p with larger OPSMAX\n");
+ return retval;
+}
+
+int depth = 0;
+
+dump(branch)
+int branch;
+{
+ register int type;
+ register int len;
+ register int i;
+
+ type = ops[branch].ival;
+ len = type >> 8;
+ type &= 255;
+ for (i=depth; i; i--)
+ printf(" ");
+ if (type == OSTRING) {
+ printf("%-5d\"%s\"\n",branch,ops[branch+1].cval);
+ }
+ else {
+ printf("(%-5d%s %d\n",branch,opname[type],len);
+ depth++;
+ for (i=1; i<=len; i++)
+ dump(ops[branch+i].ival);
+ depth--;
+ for (i=depth; i; i--)
+ printf(" ");
+ printf(")\n");
+ }
+}
+
+bl(arg,maybe)
+int arg;
+int maybe;
+{
+ if (!arg)
+ return 0;
+ else if ((ops[arg].ival & 255) != OBLOCK)
+ return oper2(OBLOCK,arg,maybe);
+ else if ((ops[arg].ival >> 8) < 2)
+ return oper2(OBLOCK,ops[arg+1].ival,maybe);
+ else
+ return arg;
+}
+
+fixup(str)
+STR *str;
+{
+ register char *s;
+ register char *t;
+
+ for (s = str->str_ptr; *s; s++) {
+ if (*s == ';' && s[1] == ' ' && s[2] == '\n') {
+ strcpy(s+1,s+2);
+ s++;
+ }
+ else if (*s == '\n') {
+ for (t = s+1; isspace(*t & 127); t++) ;
+ t--;
+ while (isspace(*t & 127) && *t != '\n') t--;
+ if (*t == '\n' && t-s > 1) {
+ if (s[-1] == '{')
+ s--;
+ strcpy(s+1,t);
+ }
+ s++;
+ }
+ }
+}
+
+putlines(str)
+STR *str;
+{
+ register char *d, *s, *t, *e;
+ register int pos, newpos;
+
+ d = tokenbuf;
+ pos = 0;
+ for (s = str->str_ptr; *s; s++) {
+ *d++ = *s;
+ pos++;
+ if (*s == '\n') {
+ *d = '\0';
+ d = tokenbuf;
+ pos = 0;
+ putone();
+ }
+ else if (*s == '\t')
+ pos += 7;
+ if (pos > 78) { /* split a long line? */
+ *d-- = '\0';
+ newpos = 0;
+ for (t = tokenbuf; isspace(*t & 127); t++) {
+ if (*t == '\t')
+ newpos += 8;
+ else
+ newpos += 1;
+ }
+ e = d;
+ while (d > tokenbuf && (*d != ' ' || d[-1] != ';'))
+ d--;
+ if (d < t+10) {
+ d = e;
+ while (d > tokenbuf &&
+ (*d != ' ' || d[-1] != '|' || d[-2] != '|') )
+ d--;
+ }
+ if (d < t+10) {
+ d = e;
+ while (d > tokenbuf &&
+ (*d != ' ' || d[-1] != '&' || d[-2] != '&') )
+ d--;
+ }
+ if (d < t+10) {
+ d = e;
+ while (d > tokenbuf && (*d != ' ' || d[-1] != ','))
+ d--;
+ }
+ if (d < t+10) {
+ d = e;
+ while (d > tokenbuf && *d != ' ')
+ d--;
+ }
+ if (d > t+3) {
+ char save[2048];
+ strcpy(save, d);
+ *d = '\n';
+ d[1] = '\0';
+ putone();
+ putchar('\n');
+ if (d[-1] != ';' && !(newpos % 4)) {
+ *t++ = ' ';
+ *t++ = ' ';
+ newpos += 2;
+ }
+ strcpy(t,save+1);
+ newpos += strlen(t);
+ d = t + strlen(t);
+ pos = newpos;
+ }
+ else
+ d = e + 1;
+ }
+ }
+}
+
+putone()
+{
+ register char *t;
+
+ for (t = tokenbuf; *t; t++) {
+ *t &= 127;
+ if (*t == 127) {
+ *t = ' ';
+ strcpy(t+strlen(t)-1, "\t#???\n");
+ checkers++;
+ }
+ }
+ t = tokenbuf;
+ if (*t == '#') {
+ if (strnEQ(t,"#!/bin/awk",10) || strnEQ(t,"#! /bin/awk",11))
+ return;
+ if (strnEQ(t,"#!/usr/bin/awk",14) || strnEQ(t,"#! /usr/bin/awk",15))
+ return;
+ }
+ fputs(tokenbuf,stdout);
+}
+
+numary(arg)
+int arg;
+{
+ STR *key;
+ int dummy;
+
+ key = walk(0,0,arg,&dummy,P_MIN);
+ str_cat(key,"[]");
+ hstore(symtab,key->str_ptr,str_make("1"));
+ str_free(key);
+ set_array_base = TRUE;
+ return arg;
+}
+
+rememberargs(arg)
+int arg;
+{
+ int type;
+ STR *str;
+
+ if (!arg)
+ return arg;
+ type = ops[arg].ival & 255;
+ if (type == OCOMMA) {
+ rememberargs(ops[arg+1].ival);
+ rememberargs(ops[arg+3].ival);
+ }
+ else if (type == OVAR) {
+ str = str_new(0);
+ hstore(curarghash,ops[ops[arg+1].ival+1].cval,str);
+ }
+ else
+ fatal("panic: unknown argument type %d, line %d\n",type,line);
+ return arg;
+}
+
+aryrefarg(arg)
+int arg;
+{
+ int type = ops[arg].ival & 255;
+ STR *str;
+
+ if (type != OSTRING)
+ fatal("panic: aryrefarg %d, line %d\n",type,line);
+ str = hfetch(curarghash,ops[arg+1].cval);
+ if (str)
+ str_set(str,"*");
+ return arg;
+}
+
+fixfargs(name,arg,prevargs)
+int name;
+int arg;
+int prevargs;
+{
+ int type;
+ STR *str;
+ int numargs;
+
+ if (!arg)
+ return prevargs;
+ type = ops[arg].ival & 255;
+ if (type == OCOMMA) {
+ numargs = fixfargs(name,ops[arg+1].ival,prevargs);
+ numargs = fixfargs(name,ops[arg+3].ival,numargs);
+ }
+ else if (type == OVAR) {
+ str = hfetch(curarghash,ops[ops[arg+1].ival+1].cval);
+ if (strEQ(str_get(str),"*")) {
+ char tmpbuf[128];
+
+ str_set(str,""); /* in case another routine has this */
+ ops[arg].ival &= ~255;
+ ops[arg].ival |= OSTAR;
+ sprintf(tmpbuf,"%s:%d",ops[name+1].cval,prevargs);
+ fprintf(stderr,"Adding %s\n",tmpbuf);
+ str = str_new(0);
+ str_set(str,"*");
+ hstore(curarghash,tmpbuf,str);
+ }
+ numargs = prevargs + 1;
+ }
+ else
+ fatal("panic: unknown argument type %d, arg %d, line %d\n",
+ type,prevargs+1,line);
+ return numargs;
+}
+
+fixrargs(name,arg,prevargs)
+char *name;
+int arg;
+int prevargs;
+{
+ int type;
+ STR *str;
+ int numargs;
+
+ if (!arg)
+ return prevargs;
+ type = ops[arg].ival & 255;
+ if (type == OCOMMA) {
+ numargs = fixrargs(name,ops[arg+1].ival,prevargs);
+ numargs = fixrargs(name,ops[arg+3].ival,numargs);
+ }
+ else {
+ char tmpbuf[128];
+
+ sprintf(tmpbuf,"%s:%d",name,prevargs);
+ str = hfetch(curarghash,tmpbuf);
+ if (str && strEQ(str->str_ptr,"*")) {
+ if (type == OVAR || type == OSTAR) {
+ ops[arg].ival &= ~255;
+ ops[arg].ival |= OSTAR;
+ }
+ else
+ fatal("Can't pass expression by reference as arg %d of %s\n",
+ prevargs+1, name);
+ }
+ numargs = prevargs + 1;
+ }
+ return numargs;
+}
+
diff --git a/gnu/usr.bin/perl/x2p/find2perl b/gnu/usr.bin/perl/x2p/find2perl
new file mode 100755
index 0000000..6d7f0ad
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/find2perl
@@ -0,0 +1,568 @@
+#!/usr/gnu/bin/perl
+
+eval 'exec /usr/gnu/bin/perl -S $0 ${1+"$@"}'
+ if $running_under_some_shell;
+
+$bin = "/usr/gnu/bin";
+
+
+while ($ARGV[0] =~ /^[^-!(]/) {
+ push(@roots, shift);
+}
+@roots = ('.') unless @roots;
+for (@roots) { $_ = &quote($_); }
+$roots = join(',', @roots);
+
+$indent = 1;
+
+while (@ARGV) {
+ $_ = shift;
+ s/^-// || /^[()!]/ || die "Unrecognized switch: $_\n";
+ if ($_ eq '(') {
+ $out .= &tab . "(\n";
+ $indent++;
+ next;
+ }
+ elsif ($_ eq ')') {
+ $indent--;
+ $out .= &tab . ")";
+ }
+ elsif ($_ eq '!') {
+ $out .= &tab . "!";
+ next;
+ }
+ elsif ($_ eq 'name') {
+ $out .= &tab;
+ $pat = &fileglob_to_re(shift);
+ $out .= '/' . $pat . "/";
+ }
+ elsif ($_ eq 'perm') {
+ $onum = shift;
+ die "Malformed -perm argument: $onum\n" unless $onum =~ /^-?[0-7]+$/;
+ if ($onum =~ s/^-//) {
+ $onum = '0' . sprintf("%o", oct($onum) & 017777); # s/b 07777 ?
+ $out .= &tab . "((\$mode & $onum) == $onum)";
+ }
+ else {
+ $onum = '0' . $onum unless $onum =~ /^0/;
+ $out .= &tab . "((\$mode & 0777) == $onum)";
+ }
+ }
+ elsif ($_ eq 'type') {
+ ($filetest = shift) =~ tr/s/S/;
+ $out .= &tab . "-$filetest _";
+ }
+ elsif ($_ eq 'print') {
+ $out .= &tab . 'print("$name\n")';
+ }
+ elsif ($_ eq 'print0') {
+ $out .= &tab . 'print("$name\0")';
+ }
+ elsif ($_ eq 'fstype') {
+ $out .= &tab;
+ $type = shift;
+ if ($type eq 'nfs')
+ { $out .= '$dev < 0'; }
+ else
+ { $out .= '$dev >= 0'; }
+ }
+ elsif ($_ eq 'user') {
+ $uname = shift;
+ $out .= &tab . "\$uid == \$uid{'$uname'}";
+ $inituser++;
+ }
+ elsif ($_ eq 'group') {
+ $gname = shift;
+ $out .= &tab . "\$gid == \$gid{'$gname'}";
+ $initgroup++;
+ }
+ elsif ($_ eq 'nouser') {
+ $out .= &tab . '!defined $uid{$uid}';
+ $inituser++;
+ }
+ elsif ($_ eq 'nogroup') {
+ $out .= &tab . '!defined $gid{$gid}';
+ $initgroup++;
+ }
+ elsif ($_ eq 'links') {
+ $out .= &tab . '$nlink ' . &n(shift);
+ }
+ elsif ($_ eq 'inum') {
+ $out .= &tab . '$ino ' . &n(shift);
+ }
+ elsif ($_ eq 'size') {
+ $out .= &tab . 'int((-s _ + 511) / 512) ' . &n(shift);
+ }
+ elsif ($_ eq 'atime') {
+ $out .= &tab . 'int(-A _) ' . &n(shift);
+ }
+ elsif ($_ eq 'mtime') {
+ $out .= &tab . 'int(-M _) ' . &n(shift);
+ }
+ elsif ($_ eq 'ctime') {
+ $out .= &tab . 'int(-C _) ' . &n(shift);
+ }
+ elsif ($_ eq 'exec') {
+ for (@cmd = (); @ARGV && $ARGV[0] ne ';'; push(@cmd,shift)) { }
+ shift;
+ $_ = "@cmd";
+ if (m#^(/bin/)?rm -f {}$#) {
+ if (!@ARGV) {
+ $out .= &tab . 'unlink($_)';
+ }
+ else {
+ $out .= &tab . '(unlink($_) || 1)';
+ }
+ }
+ elsif (m#^(/bin/)?rm {}$#) {
+ $out .= &tab . '(unlink($_) || warn "$name: $!\n")';
+ }
+ else {
+ for (@cmd) { s/'/\\'/g; }
+ $" = "','";
+ $out .= &tab . "&exec(0, '@cmd')";
+ $" = ' ';
+ $initexec++;
+ }
+ }
+ elsif ($_ eq 'ok') {
+ for (@cmd = (); @ARGV && $ARGV[0] ne ';'; push(@cmd,shift)) { }
+ shift;
+ for (@cmd) { s/'/\\'/g; }
+ $" = "','";
+ $out .= &tab . "&exec(1, '@cmd')";
+ $" = ' ';
+ $initexec++;
+ }
+ elsif ($_ eq 'prune') {
+ $out .= &tab . '($prune = 1)';
+ }
+ elsif ($_ eq 'xdev') {
+ $out .= &tab . '(($prune |= ($dev != $topdev)),1)';
+ }
+ elsif ($_ eq 'newer') {
+ $out .= &tab;
+ $file = shift;
+ $newername = 'AGE_OF' . $file;
+ $newername =~ s/[^\w]/_/g;
+ $newername = '$' . $newername;
+ $out .= "-M _ < $newername";
+ $initnewer .= "$newername = -M " . &quote($file) . ";\n";
+ }
+ elsif ($_ eq 'eval') {
+ $prog = &quote(shift);
+ $out .= &tab . "eval $prog";
+ }
+ elsif ($_ eq 'depth') {
+ $depth++;
+ next;
+ }
+ elsif ($_ eq 'ls') {
+ $out .= &tab . "&ls";
+ $initls++;
+ }
+ elsif ($_ eq 'tar') {
+ $out .= &tab;
+ die "-tar must have a filename argument\n" unless @ARGV;
+ $file = shift;
+ $fh = 'FH' . $file;
+ $fh =~ s/[^\w]/_/g;
+ $out .= "&tar($fh)";
+ $file = '>' . $file;
+ $initfile .= "open($fh, " . &quote($file) .
+ qq{) || die "Can't open $fh: \$!\\n";\n};
+ $inittar++;
+ $flushall = "\n&tflushall;\n";
+ }
+ elsif (/^n?cpio$/) {
+ $depth++;
+ $out .= &tab;
+ die "-$_ must have a filename argument\n" unless @ARGV;
+ $file = shift;
+ $fh = 'FH' . $file;
+ $fh =~ s/[^\w]/_/g;
+ $out .= "&cpio('" . substr($_,0,1) . "', $fh)";
+ $file = '>' . $file;
+ $initfile .= "open($fh, " . &quote($file) .
+ qq{) || die "Can't open $fh: \$!\\n";\n};
+ $initcpio++;
+ $flushall = "\n&flushall;\n";
+ }
+ else {
+ die "Unrecognized switch: -$_\n";
+ }
+ if (@ARGV) {
+ if ($ARGV[0] eq '-o') {
+ { local($statdone) = 1; $out .= "\n" . &tab . "||\n"; }
+ $statdone = 0 if $indent == 1 && $delayedstat;
+ $saw_or++;
+ shift;
+ }
+ else {
+ $out .= " &&" unless $ARGV[0] eq ')';
+ $out .= "\n";
+ shift if $ARGV[0] eq '-a';
+ }
+ }
+}
+
+print <<"END";
+#!$bin/perl
+
+eval 'exec $bin/perl -S \$0 \${1+"\$@"}'
+ if \$running_under_some_shell;
+
+END
+
+if ($initls) {
+ print <<'END';
+@rwx = ('---','--x','-w-','-wx','r--','r-x','rw-','rwx');
+@moname = (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec);
+
+END
+}
+
+if ($inituser || $initls) {
+ print 'while (($name, $pw, $uid) = getpwent) {', "\n";
+ print ' $uid{$name} = $uid{$uid} = $uid;', "\n" if $inituser;
+ print ' $user{$uid} = $name unless $user{$uid};', "\n" if $initls;
+ print "}\n\n";
+}
+
+if ($initgroup || $initls) {
+ print 'while (($name, $pw, $gid) = getgrent) {', "\n";
+ print ' $gid{$name} = $gid{$gid} = $gid;', "\n" if $initgroup;
+ print ' $group{$gid} = $name unless $group{$gid};', "\n" if $initls;
+ print "}\n\n";
+}
+
+print $initnewer, "\n" if $initnewer;
+
+print $initfile, "\n" if $initfile;
+
+$find = $depth ? "finddepth" : "find";
+print <<"END";
+require "$find.pl";
+
+# Traverse desired filesystems
+
+&$find($roots);
+$flushall
+exit;
+
+sub wanted {
+$out;
+}
+
+END
+
+if ($initexec) {
+ print <<'END';
+sub exec {
+ local($ok, @cmd) = @_;
+ foreach $word (@cmd) {
+ $word =~ s#{}#$name#g;
+ }
+ if ($ok) {
+ local($old) = select(STDOUT);
+ $| = 1;
+ print "@cmd";
+ select($old);
+ return 0 unless <STDIN> =~ /^y/;
+ }
+ chdir $cwd; # sigh
+ system @cmd;
+ chdir $dir;
+ return !$?;
+}
+
+END
+}
+
+if ($initls) {
+ print <<'END';
+sub ls {
+ ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$sizemm,
+ $atime,$mtime,$ctime,$blksize,$blocks) = lstat(_);
+
+ $pname = $name;
+
+ if (defined $blocks) {
+ $blocks = int(($blocks + 1) / 2);
+ }
+ else {
+ $blocks = int(($size + 1023) / 1024);
+ }
+
+ if (-f _) { $perms = '-'; }
+ elsif (-d _) { $perms = 'd'; }
+ elsif (-c _) { $perms = 'c'; $sizemm = &sizemm; }
+ elsif (-b _) { $perms = 'b'; $sizemm = &sizemm; }
+ elsif (-p _) { $perms = 'p'; }
+ elsif (-S _) { $perms = 's'; }
+ else { $perms = 'l'; $pname .= ' -> ' . readlink($_); }
+
+ $tmpmode = $mode;
+ $tmp = $rwx[$tmpmode & 7];
+ $tmpmode >>= 3;
+ $tmp = $rwx[$tmpmode & 7] . $tmp;
+ $tmpmode >>= 3;
+ $tmp = $rwx[$tmpmode & 7] . $tmp;
+ substr($tmp,2,1) =~ tr/-x/Ss/ if -u _;
+ substr($tmp,5,1) =~ tr/-x/Ss/ if -g _;
+ substr($tmp,8,1) =~ tr/-x/Tt/ if -k _;
+ $perms .= $tmp;
+
+ $user = $user{$uid} || $uid;
+ $group = $group{$gid} || $gid;
+
+ ($sec,$min,$hour,$mday,$mon,$year) = localtime($mtime);
+ $moname = $moname[$mon];
+ if (-M _ > 365.25 / 2) {
+ $timeyear = '19' . $year;
+ }
+ else {
+ $timeyear = sprintf("%02d:%02d", $hour, $min);
+ }
+
+ printf "%5lu %4ld %-10s %2d %-8s %-8s %8s %s %2d %5s %s\n",
+ $ino,
+ $blocks,
+ $perms,
+ $nlink,
+ $user,
+ $group,
+ $sizemm,
+ $moname,
+ $mday,
+ $timeyear,
+ $pname;
+ 1;
+}
+
+sub sizemm {
+ sprintf("%3d, %3d", ($rdev >> 8) & 255, $rdev & 255);
+}
+
+END
+}
+
+if ($initcpio) {
+print <<'END';
+sub cpio {
+ local($nc,$fh) = @_;
+ local($text);
+
+ if ($name eq 'TRAILER!!!') {
+ $text = '';
+ $size = 0;
+ }
+ else {
+ ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
+ $atime,$mtime,$ctime,$blksize,$blocks) = lstat(_);
+ if (-f _) {
+ open(IN, "./$_\0") || do {
+ warn "Couldn't open $name: $!\n";
+ return;
+ };
+ }
+ else {
+ $text = readlink($_);
+ $size = 0 unless defined $text;
+ }
+ }
+
+ ($nm = $name) =~ s#^\./##;
+ $nc{$fh} = $nc;
+ if ($nc eq 'n') {
+ $cpout{$fh} .=
+ sprintf("%06o%06o%06o%06o%06o%06o%06o%06o%011lo%06o%011lo%s\0",
+ 070707,
+ $dev & 0777777,
+ $ino & 0777777,
+ $mode & 0777777,
+ $uid & 0777777,
+ $gid & 0777777,
+ $nlink & 0777777,
+ $rdev & 0177777,
+ $mtime,
+ length($nm)+1,
+ $size,
+ $nm);
+ }
+ else {
+ $cpout{$fh} .= "\0" if length($cpout{$fh}) & 1;
+ $cpout{$fh} .= pack("SSSSSSSSLSLa*",
+ 070707, $dev, $ino, $mode, $uid, $gid, $nlink, $rdev, $mtime,
+ length($nm)+1, $size, $nm . (length($nm) & 1 ? "\0" : "\0\0"));
+ }
+ if ($text ne '') {
+ $cpout{$fh} .= $text;
+ }
+ elsif ($size) {
+ &flush($fh) while ($l = length($cpout{$fh})) >= 5120;
+ while (sysread(IN, $cpout{$fh}, 5120 - $l, $l)) {
+ &flush($fh);
+ $l = length($cpout{$fh});
+ }
+ }
+ close IN;
+}
+
+sub flush {
+ local($fh) = @_;
+
+ while (length($cpout{$fh}) >= 5120) {
+ syswrite($fh,$cpout{$fh},5120);
+ ++$blocks{$fh};
+ substr($cpout{$fh}, 0, 5120) = '';
+ }
+}
+
+sub flushall {
+ $name = 'TRAILER!!!';
+ foreach $fh (keys %cpout) {
+ &cpio($nc{$fh},$fh);
+ $cpout{$fh} .= "0" x (5120 - length($cpout{$fh}));
+ &flush($fh);
+ print $blocks{$fh} * 10, " blocks\n";
+ }
+}
+
+END
+}
+
+if ($inittar) {
+print <<'END';
+sub tar {
+ local($fh) = @_;
+ local($linkname,$header,$l,$slop);
+ local($linkflag) = "\0";
+
+ ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
+ $atime,$mtime,$ctime,$blksize,$blocks) = lstat(_);
+ $nm = $name;
+ if ($nlink > 1) {
+ if ($linkname = $linkseen{$fh,$dev,$ino}) {
+ $linkflag = 1;
+ }
+ else {
+ $linkseen{$fh,$dev,$ino} = $nm;
+ }
+ }
+ if (-f _) {
+ open(IN, "./$_\0") || do {
+ warn "Couldn't open $name: $!\n";
+ return;
+ };
+ $size = 0 if $linkflag ne "\0";
+ }
+ else {
+ $linkname = readlink($_);
+ $linkflag = 2 if defined $linkname;
+ $nm .= '/' if -d _;
+ $size = 0;
+ }
+
+ $header = pack("a100a8a8a8a12a12a8a1a100",
+ $nm,
+ sprintf("%6o ", $mode & 0777),
+ sprintf("%6o ", $uid & 0777777),
+ sprintf("%6o ", $gid & 0777777),
+ sprintf("%11o ", $size),
+ sprintf("%11o ", $mtime),
+ " ",
+ $linkflag,
+ $linkname);
+ $l = length($header) % 512;
+ substr($header, 148, 6) = sprintf("%6o", unpack("%16C*", $header));
+ substr($header, 154, 1) = "\0"; # blech
+ $tarout{$fh} .= $header;
+ $tarout{$fh} .= "\0" x (512 - $l) if $l;
+ if ($size) {
+ &tflush($fh) while ($l = length($tarout{$fh})) >= 10240;
+ while (sysread(IN, $tarout{$fh}, 10240 - $l, $l)) {
+ $slop = length($tarout{$fh}) % 512;
+ $tarout{$fh} .= "\0" x (512 - $slop) if $slop;
+ &tflush($fh);
+ $l = length($tarout{$fh});
+ }
+ }
+ close IN;
+}
+
+sub tflush {
+ local($fh) = @_;
+
+ while (length($tarout{$fh}) >= 10240) {
+ syswrite($fh,$tarout{$fh},10240);
+ ++$blocks{$fh};
+ substr($tarout{$fh}, 0, 10240) = '';
+ }
+}
+
+sub tflushall {
+ local($len);
+
+ foreach $fh (keys %tarout) {
+ $len = 10240 - length($tarout{$fh});
+ $len += 10240 if $len < 1024;
+ $tarout{$fh} .= "\0" x $len;
+ &tflush($fh);
+ }
+}
+
+END
+}
+
+exit;
+
+############################################################################
+
+sub tab {
+ local($tabstring);
+
+ $tabstring = "\t" x ($indent / 2) . ' ' x ($indent % 2 * 4);
+ if (!$statdone) {
+ if ($_ =~ /^(name|print|prune|exec|ok|\(|\))/) {
+ $delayedstat++;
+ }
+ else {
+ if ($saw_or) {
+ $tabstring .= <<'ENDOFSTAT' . $tabstring;
+($nlink || (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_))) &&
+ENDOFSTAT
+ }
+ else {
+ $tabstring .= <<'ENDOFSTAT' . $tabstring;
+(($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&
+ENDOFSTAT
+ }
+ $statdone = 1;
+ }
+ }
+ $tabstring =~ s/^\s+/ / if $out =~ /!$/;
+ $tabstring;
+}
+
+sub fileglob_to_re {
+ local($tmp) = @_;
+
+ $tmp =~ s/([.^\$()])/\\$1/g;
+ $tmp =~ s/([?*])/.$1/g;
+ "^$tmp$";
+}
+
+sub n {
+ local($n) = @_;
+
+ $n =~ s/^-/< / || $n =~ s/^\+/> / || $n =~ s/^/== /;
+ $n =~ s/ 0*(\d)/ $1/;
+ $n;
+}
+
+sub quote {
+ local($string) = @_;
+ $string =~ s/'/\\'/;
+ "'$string'";
+}
diff --git a/gnu/usr.bin/perl/x2p/h2ph b/gnu/usr.bin/perl/x2p/h2ph
new file mode 100755
index 0000000..f093626
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/h2ph
@@ -0,0 +1,253 @@
+#!/usr/gnu/bin/perl
+'di';
+'ig00';
+
+$perlincl = '/usr/gnu/lib/perl';
+
+chdir '/usr/include' || die "Can't cd /usr/include";
+
+@isatype = split(' ',<<END);
+ char uchar u_char
+ short ushort u_short
+ int uint u_int
+ long ulong u_long
+ FILE
+END
+
+@isatype{@isatype} = (1) x @isatype;
+
+@ARGV = ('-') unless @ARGV;
+
+foreach $file (@ARGV) {
+ if ($file eq '-') {
+ open(IN, "-");
+ open(OUT, ">-");
+ }
+ else {
+ ($outfile = $file) =~ s/\.h$/.ph/ || next;
+ print "$file -> $outfile\n";
+ if ($file =~ m|^(.*)/|) {
+ $dir = $1;
+ if (!-d "$perlincl/$dir") {
+ mkdir("$perlincl/$dir",0777);
+ }
+ }
+ open(IN,"$file") || ((warn "Can't open $file: $!\n"),next);
+ open(OUT,">$perlincl/$outfile") || die "Can't create $outfile: $!\n";
+ }
+ while (<IN>) {
+ chop;
+ while (/\\$/) {
+ chop;
+ $_ .= <IN>;
+ chop;
+ }
+ if (s:/\*:\200:g) {
+ s:\*/:\201:g;
+ s/\200[^\201]*\201//g; # delete single line comments
+ if (s/\200.*//) { # begin multi-line comment?
+ $_ .= '/*';
+ $_ .= <IN>;
+ redo;
+ }
+ }
+ if (s/^#\s*//) {
+ if (s/^define\s+(\w+)//) {
+ $name = $1;
+ $new = '';
+ s/\s+$//;
+ if (s/^\(([\w,\s]*)\)//) {
+ $args = $1;
+ if ($args ne '') {
+ foreach $arg (split(/,\s*/,$args)) {
+ $arg =~ s/^\s*([^\s].*[^\s])\s*$/$1/;
+ $curargs{$arg} = 1;
+ }
+ $args =~ s/\b(\w)/\$$1/g;
+ $args = "local($args) = \@_;\n$t ";
+ }
+ s/^\s+//;
+ do expr();
+ $new =~ s/(["\\])/\\$1/g;
+ if ($t ne '') {
+ $new =~ s/(['\\])/\\$1/g;
+ print OUT $t,
+ "eval 'sub $name {\n$t ${args}eval \"$new\";\n$t}';\n";
+ }
+ else {
+ print OUT "sub $name {\n ${args}eval \"$new\";\n}\n";
+ }
+ %curargs = ();
+ }
+ else {
+ s/^\s+//;
+ do expr();
+ $new = 1 if $new eq '';
+ if ($t ne '') {
+ $new =~ s/(['\\])/\\$1/g;
+ print OUT $t,"eval 'sub $name {",$new,";}';\n";
+ }
+ else {
+ print OUT $t,"sub $name {",$new,";}\n";
+ }
+ }
+ }
+ elsif (/^include\s+<(.*)>/) {
+ ($incl = $1) =~ s/\.h$/.ph/;
+ print OUT $t,"require '$incl';\n";
+ }
+ elsif (/^ifdef\s+(\w+)/) {
+ print OUT $t,"if (defined &$1) {\n";
+ $tab += 4;
+ $t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
+ }
+ elsif (/^ifndef\s+(\w+)/) {
+ print OUT $t,"if (!defined &$1) {\n";
+ $tab += 4;
+ $t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
+ }
+ elsif (s/^if\s+//) {
+ $new = '';
+ do expr();
+ print OUT $t,"if ($new) {\n";
+ $tab += 4;
+ $t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
+ }
+ elsif (s/^elif\s+//) {
+ $new = '';
+ do expr();
+ $tab -= 4;
+ $t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
+ print OUT $t,"}\n${t}elsif ($new) {\n";
+ $tab += 4;
+ $t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
+ }
+ elsif (/^else/) {
+ $tab -= 4;
+ $t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
+ print OUT $t,"}\n${t}else {\n";
+ $tab += 4;
+ $t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
+ }
+ elsif (/^endif/) {
+ $tab -= 4;
+ $t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
+ print OUT $t,"}\n";
+ }
+ }
+ }
+ print OUT "1;\n";
+}
+
+sub expr {
+ while ($_ ne '') {
+ s/^(\s+)// && do {$new .= ' '; next;};
+ s/^(0x[0-9a-fA-F]+)// && do {$new .= $1; next;};
+ s/^(\d+)// && do {$new .= $1; next;};
+ s/^("(\\"|[^"])*")// && do {$new .= $1; next;};
+ s/^'((\\"|[^"])*)'// && do {
+ if ($curargs{$1}) {
+ $new .= "ord('\$$1')";
+ }
+ else {
+ $new .= "ord('$1')";
+ }
+ next;
+ };
+ s/^sizeof\s*\(([^)]+)\)/{$1}/ && do {
+ $new .= '$sizeof';
+ next;
+ };
+ s/^([_a-zA-Z]\w*)// && do {
+ $id = $1;
+ if ($id eq 'struct') {
+ s/^\s+(\w+)//;
+ $id .= ' ' . $1;
+ $isatype{$id} = 1;
+ }
+ elsif ($id eq 'unsigned') {
+ s/^\s+(\w+)//;
+ $id .= ' ' . $1;
+ $isatype{$id} = 1;
+ }
+ if ($curargs{$id}) {
+ $new .= '$' . $id;
+ }
+ elsif ($id eq 'defined') {
+ $new .= 'defined';
+ }
+ elsif (/^\(/) {
+ s/^\((\w),/("$1",/ if $id =~ /^_IO[WR]*$/i; # cheat
+ $new .= " &$id";
+ }
+ elsif ($isatype{$id}) {
+ if ($new =~ /{\s*$/) {
+ $new .= "'$id'";
+ }
+ elsif ($new =~ /\(\s*$/ && /^[\s*]*\)/) {
+ $new =~ s/\(\s*$//;
+ s/^[\s*]*\)//;
+ }
+ else {
+ $new .= $id;
+ }
+ }
+ else {
+ $new .= ' &' . $id;
+ }
+ next;
+ };
+ s/^(.)// && do {$new .= $1; next;};
+ }
+}
+##############################################################################
+
+ # These next few lines are legal in both Perl and nroff.
+
+.00; # finish .ig
+
+'di \" finish diversion--previous line must be blank
+.nr nl 0-1 \" fake up transition to first page again
+.nr % 0 \" start at page 1
+'; __END__ ############# From here on it's a standard manual page ############
+.TH H2PH 1 "August 8, 1990"
+.AT 3
+.SH NAME
+h2ph \- convert .h C header files to .ph Perl header files
+.SH SYNOPSIS
+.B h2ph [headerfiles]
+.SH DESCRIPTION
+.I h2ph
+converts any C header files specified to the corresponding Perl header file
+format.
+It is most easily run while in /usr/include:
+.nf
+
+ cd /usr/include; h2ph * sys/*
+
+.fi
+If run with no arguments, filters standard input to standard output.
+.SH ENVIRONMENT
+No environment variables are used.
+.SH FILES
+/usr/include/*.h
+.br
+/usr/include/sys/*.h
+.br
+etc.
+.SH AUTHOR
+Larry Wall
+.SH "SEE ALSO"
+perl(1)
+.SH DIAGNOSTICS
+The usual warnings if it can't read or write the files involved.
+.SH BUGS
+Doesn't construct the %sizeof array for you.
+.PP
+It doesn't handle all C constructs, but it does attempt to isolate
+definitions inside evals so that you can get at the definitions
+that it can translate.
+.PP
+It's only intended as a rough tool.
+You may need to dicker with the files produced.
+.ex
diff --git a/gnu/usr.bin/perl/x2p/h2ph.1 b/gnu/usr.bin/perl/x2p/h2ph.1
new file mode 100755
index 0000000..f093626
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/h2ph.1
@@ -0,0 +1,253 @@
+#!/usr/gnu/bin/perl
+'di';
+'ig00';
+
+$perlincl = '/usr/gnu/lib/perl';
+
+chdir '/usr/include' || die "Can't cd /usr/include";
+
+@isatype = split(' ',<<END);
+ char uchar u_char
+ short ushort u_short
+ int uint u_int
+ long ulong u_long
+ FILE
+END
+
+@isatype{@isatype} = (1) x @isatype;
+
+@ARGV = ('-') unless @ARGV;
+
+foreach $file (@ARGV) {
+ if ($file eq '-') {
+ open(IN, "-");
+ open(OUT, ">-");
+ }
+ else {
+ ($outfile = $file) =~ s/\.h$/.ph/ || next;
+ print "$file -> $outfile\n";
+ if ($file =~ m|^(.*)/|) {
+ $dir = $1;
+ if (!-d "$perlincl/$dir") {
+ mkdir("$perlincl/$dir",0777);
+ }
+ }
+ open(IN,"$file") || ((warn "Can't open $file: $!\n"),next);
+ open(OUT,">$perlincl/$outfile") || die "Can't create $outfile: $!\n";
+ }
+ while (<IN>) {
+ chop;
+ while (/\\$/) {
+ chop;
+ $_ .= <IN>;
+ chop;
+ }
+ if (s:/\*:\200:g) {
+ s:\*/:\201:g;
+ s/\200[^\201]*\201//g; # delete single line comments
+ if (s/\200.*//) { # begin multi-line comment?
+ $_ .= '/*';
+ $_ .= <IN>;
+ redo;
+ }
+ }
+ if (s/^#\s*//) {
+ if (s/^define\s+(\w+)//) {
+ $name = $1;
+ $new = '';
+ s/\s+$//;
+ if (s/^\(([\w,\s]*)\)//) {
+ $args = $1;
+ if ($args ne '') {
+ foreach $arg (split(/,\s*/,$args)) {
+ $arg =~ s/^\s*([^\s].*[^\s])\s*$/$1/;
+ $curargs{$arg} = 1;
+ }
+ $args =~ s/\b(\w)/\$$1/g;
+ $args = "local($args) = \@_;\n$t ";
+ }
+ s/^\s+//;
+ do expr();
+ $new =~ s/(["\\])/\\$1/g;
+ if ($t ne '') {
+ $new =~ s/(['\\])/\\$1/g;
+ print OUT $t,
+ "eval 'sub $name {\n$t ${args}eval \"$new\";\n$t}';\n";
+ }
+ else {
+ print OUT "sub $name {\n ${args}eval \"$new\";\n}\n";
+ }
+ %curargs = ();
+ }
+ else {
+ s/^\s+//;
+ do expr();
+ $new = 1 if $new eq '';
+ if ($t ne '') {
+ $new =~ s/(['\\])/\\$1/g;
+ print OUT $t,"eval 'sub $name {",$new,";}';\n";
+ }
+ else {
+ print OUT $t,"sub $name {",$new,";}\n";
+ }
+ }
+ }
+ elsif (/^include\s+<(.*)>/) {
+ ($incl = $1) =~ s/\.h$/.ph/;
+ print OUT $t,"require '$incl';\n";
+ }
+ elsif (/^ifdef\s+(\w+)/) {
+ print OUT $t,"if (defined &$1) {\n";
+ $tab += 4;
+ $t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
+ }
+ elsif (/^ifndef\s+(\w+)/) {
+ print OUT $t,"if (!defined &$1) {\n";
+ $tab += 4;
+ $t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
+ }
+ elsif (s/^if\s+//) {
+ $new = '';
+ do expr();
+ print OUT $t,"if ($new) {\n";
+ $tab += 4;
+ $t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
+ }
+ elsif (s/^elif\s+//) {
+ $new = '';
+ do expr();
+ $tab -= 4;
+ $t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
+ print OUT $t,"}\n${t}elsif ($new) {\n";
+ $tab += 4;
+ $t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
+ }
+ elsif (/^else/) {
+ $tab -= 4;
+ $t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
+ print OUT $t,"}\n${t}else {\n";
+ $tab += 4;
+ $t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
+ }
+ elsif (/^endif/) {
+ $tab -= 4;
+ $t = "\t" x ($tab / 8) . ' ' x ($tab % 8);
+ print OUT $t,"}\n";
+ }
+ }
+ }
+ print OUT "1;\n";
+}
+
+sub expr {
+ while ($_ ne '') {
+ s/^(\s+)// && do {$new .= ' '; next;};
+ s/^(0x[0-9a-fA-F]+)// && do {$new .= $1; next;};
+ s/^(\d+)// && do {$new .= $1; next;};
+ s/^("(\\"|[^"])*")// && do {$new .= $1; next;};
+ s/^'((\\"|[^"])*)'// && do {
+ if ($curargs{$1}) {
+ $new .= "ord('\$$1')";
+ }
+ else {
+ $new .= "ord('$1')";
+ }
+ next;
+ };
+ s/^sizeof\s*\(([^)]+)\)/{$1}/ && do {
+ $new .= '$sizeof';
+ next;
+ };
+ s/^([_a-zA-Z]\w*)// && do {
+ $id = $1;
+ if ($id eq 'struct') {
+ s/^\s+(\w+)//;
+ $id .= ' ' . $1;
+ $isatype{$id} = 1;
+ }
+ elsif ($id eq 'unsigned') {
+ s/^\s+(\w+)//;
+ $id .= ' ' . $1;
+ $isatype{$id} = 1;
+ }
+ if ($curargs{$id}) {
+ $new .= '$' . $id;
+ }
+ elsif ($id eq 'defined') {
+ $new .= 'defined';
+ }
+ elsif (/^\(/) {
+ s/^\((\w),/("$1",/ if $id =~ /^_IO[WR]*$/i; # cheat
+ $new .= " &$id";
+ }
+ elsif ($isatype{$id}) {
+ if ($new =~ /{\s*$/) {
+ $new .= "'$id'";
+ }
+ elsif ($new =~ /\(\s*$/ && /^[\s*]*\)/) {
+ $new =~ s/\(\s*$//;
+ s/^[\s*]*\)//;
+ }
+ else {
+ $new .= $id;
+ }
+ }
+ else {
+ $new .= ' &' . $id;
+ }
+ next;
+ };
+ s/^(.)// && do {$new .= $1; next;};
+ }
+}
+##############################################################################
+
+ # These next few lines are legal in both Perl and nroff.
+
+.00; # finish .ig
+
+'di \" finish diversion--previous line must be blank
+.nr nl 0-1 \" fake up transition to first page again
+.nr % 0 \" start at page 1
+'; __END__ ############# From here on it's a standard manual page ############
+.TH H2PH 1 "August 8, 1990"
+.AT 3
+.SH NAME
+h2ph \- convert .h C header files to .ph Perl header files
+.SH SYNOPSIS
+.B h2ph [headerfiles]
+.SH DESCRIPTION
+.I h2ph
+converts any C header files specified to the corresponding Perl header file
+format.
+It is most easily run while in /usr/include:
+.nf
+
+ cd /usr/include; h2ph * sys/*
+
+.fi
+If run with no arguments, filters standard input to standard output.
+.SH ENVIRONMENT
+No environment variables are used.
+.SH FILES
+/usr/include/*.h
+.br
+/usr/include/sys/*.h
+.br
+etc.
+.SH AUTHOR
+Larry Wall
+.SH "SEE ALSO"
+perl(1)
+.SH DIAGNOSTICS
+The usual warnings if it can't read or write the files involved.
+.SH BUGS
+Doesn't construct the %sizeof array for you.
+.PP
+It doesn't handle all C constructs, but it does attempt to isolate
+definitions inside evals so that you can get at the definitions
+that it can translate.
+.PP
+It's only intended as a rough tool.
+You may need to dicker with the files produced.
+.ex
diff --git a/gnu/usr.bin/perl/x2p/handy.h b/gnu/usr.bin/perl/x2p/handy.h
new file mode 100644
index 0000000..85a777c
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/handy.h
@@ -0,0 +1,46 @@
+/* $RCSfile: handy.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:10 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: handy.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:30:10 nate
+ * PERL!
+ *
+ * Revision 4.0.1.2 91/06/07 12:15:43 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0.1.1 91/04/12 09:29:08 lwall
+ * patch1: random cleanup in cpp namespace
+ *
+ * Revision 4.0 91/03/20 01:57:45 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#define Null(type) ((type)0)
+#define Nullch Null(char*)
+#define Nullfp Null(FILE*)
+
+#define bool char
+#ifdef TRUE
+#undef TRUE
+#endif
+#ifdef FALSE
+#undef FALSE
+#endif
+#define TRUE (1)
+#define FALSE (0)
+
+#define Ctl(ch) (ch & 037)
+
+#define strNE(s1,s2) (strcmp(s1,s2))
+#define strEQ(s1,s2) (!strcmp(s1,s2))
+#define strLT(s1,s2) (strcmp(s1,s2) < 0)
+#define strLE(s1,s2) (strcmp(s1,s2) <= 0)
+#define strGT(s1,s2) (strcmp(s1,s2) > 0)
+#define strGE(s1,s2) (strcmp(s1,s2) >= 0)
+#define strnNE(s1,s2,l) (strncmp(s1,s2,l))
+#define strnEQ(s1,s2,l) (!strncmp(s1,s2,l))
diff --git a/gnu/usr.bin/perl/x2p/hash.c b/gnu/usr.bin/perl/x2p/hash.c
new file mode 100644
index 0000000..d525882
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/hash.c
@@ -0,0 +1,250 @@
+/* $RCSfile: hash.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:10 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: hash.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:30:10 nate
+ * PERL!
+ *
+ * Revision 4.0.1.1 91/06/07 12:15:55 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:57:49 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include <stdio.h>
+#include "EXTERN.h"
+#include "handy.h"
+#include "util.h"
+#include "a2p.h"
+
+STR *
+hfetch(tb,key)
+register HASH *tb;
+char *key;
+{
+ register char *s;
+ register int i;
+ register int hash;
+ register HENT *entry;
+
+ if (!tb)
+ return Nullstr;
+ for (s=key, i=0, hash = 0;
+ /* while */ *s;
+ s++, i++, hash *= 5) {
+ hash += *s * coeff[i];
+ }
+ entry = tb->tbl_array[hash & tb->tbl_max];
+ for (; entry; entry = entry->hent_next) {
+ if (entry->hent_hash != hash) /* strings can't be equal */
+ continue;
+ if (strNE(entry->hent_key,key)) /* is this it? */
+ continue;
+ return entry->hent_val;
+ }
+ return Nullstr;
+}
+
+bool
+hstore(tb,key,val)
+register HASH *tb;
+char *key;
+STR *val;
+{
+ register char *s;
+ register int i;
+ register int hash;
+ register HENT *entry;
+ register HENT **oentry;
+
+ if (!tb)
+ return FALSE;
+ for (s=key, i=0, hash = 0;
+ /* while */ *s;
+ s++, i++, hash *= 5) {
+ hash += *s * coeff[i];
+ }
+
+ oentry = &(tb->tbl_array[hash & tb->tbl_max]);
+ i = 1;
+
+ for (entry = *oentry; entry; i=0, entry = entry->hent_next) {
+ if (entry->hent_hash != hash) /* strings can't be equal */
+ continue;
+ if (strNE(entry->hent_key,key)) /* is this it? */
+ continue;
+ /*NOSTRICT*/
+ safefree((char*)entry->hent_val);
+ entry->hent_val = val;
+ return TRUE;
+ }
+ /*NOSTRICT*/
+ entry = (HENT*) safemalloc(sizeof(HENT));
+
+ entry->hent_key = savestr(key);
+ entry->hent_val = val;
+ entry->hent_hash = hash;
+ entry->hent_next = *oentry;
+ *oentry = entry;
+
+ if (i) { /* initial entry? */
+ tb->tbl_fill++;
+ if ((tb->tbl_fill * 100 / (tb->tbl_max + 1)) > FILLPCT)
+ hsplit(tb);
+ }
+
+ return FALSE;
+}
+
+#ifdef NOTUSED
+bool
+hdelete(tb,key)
+register HASH *tb;
+char *key;
+{
+ register char *s;
+ register int i;
+ register int hash;
+ register HENT *entry;
+ register HENT **oentry;
+
+ if (!tb)
+ return FALSE;
+ for (s=key, i=0, hash = 0;
+ /* while */ *s;
+ s++, i++, hash *= 5) {
+ hash += *s * coeff[i];
+ }
+
+ oentry = &(tb->tbl_array[hash & tb->tbl_max]);
+ entry = *oentry;
+ i = 1;
+ for (; entry; i=0, oentry = &entry->hent_next, entry = entry->hent_next) {
+ if (entry->hent_hash != hash) /* strings can't be equal */
+ continue;
+ if (strNE(entry->hent_key,key)) /* is this it? */
+ continue;
+ safefree((char*)entry->hent_val);
+ safefree(entry->hent_key);
+ *oentry = entry->hent_next;
+ safefree((char*)entry);
+ if (i)
+ tb->tbl_fill--;
+ return TRUE;
+ }
+ return FALSE;
+}
+#endif
+
+hsplit(tb)
+HASH *tb;
+{
+ int oldsize = tb->tbl_max + 1;
+ register int newsize = oldsize * 2;
+ register int i;
+ register HENT **a;
+ register HENT **b;
+ register HENT *entry;
+ register HENT **oentry;
+
+ a = (HENT**) saferealloc((char*)tb->tbl_array, newsize * sizeof(HENT*));
+ bzero((char*)&a[oldsize], oldsize * sizeof(HENT*)); /* zero second half */
+ tb->tbl_max = --newsize;
+ tb->tbl_array = a;
+
+ for (i=0; i<oldsize; i++,a++) {
+ if (!*a) /* non-existent */
+ continue;
+ b = a+oldsize;
+ for (oentry = a, entry = *a; entry; entry = *oentry) {
+ if ((entry->hent_hash & newsize) != i) {
+ *oentry = entry->hent_next;
+ entry->hent_next = *b;
+ if (!*b)
+ tb->tbl_fill++;
+ *b = entry;
+ continue;
+ }
+ else
+ oentry = &entry->hent_next;
+ }
+ if (!*a) /* everything moved */
+ tb->tbl_fill--;
+ }
+}
+
+HASH *
+hnew()
+{
+ register HASH *tb = (HASH*)safemalloc(sizeof(HASH));
+
+ tb->tbl_array = (HENT**) safemalloc(8 * sizeof(HENT*));
+ tb->tbl_fill = 0;
+ tb->tbl_max = 7;
+ hiterinit(tb); /* so each() will start off right */
+ bzero((char*)tb->tbl_array, 8 * sizeof(HENT*));
+ return tb;
+}
+
+#ifdef NOTUSED
+hshow(tb)
+register HASH *tb;
+{
+ fprintf(stderr,"%5d %4d (%2d%%)\n",
+ tb->tbl_max+1,
+ tb->tbl_fill,
+ tb->tbl_fill * 100 / (tb->tbl_max+1));
+}
+#endif
+
+hiterinit(tb)
+register HASH *tb;
+{
+ tb->tbl_riter = -1;
+ tb->tbl_eiter = Null(HENT*);
+ return tb->tbl_fill;
+}
+
+HENT *
+hiternext(tb)
+register HASH *tb;
+{
+ register HENT *entry;
+
+ entry = tb->tbl_eiter;
+ do {
+ if (entry)
+ entry = entry->hent_next;
+ if (!entry) {
+ tb->tbl_riter++;
+ if (tb->tbl_riter > tb->tbl_max) {
+ tb->tbl_riter = -1;
+ break;
+ }
+ entry = tb->tbl_array[tb->tbl_riter];
+ }
+ } while (!entry);
+
+ tb->tbl_eiter = entry;
+ return entry;
+}
+
+char *
+hiterkey(entry)
+register HENT *entry;
+{
+ return entry->hent_key;
+}
+
+STR *
+hiterval(entry)
+register HENT *entry;
+{
+ return entry->hent_val;
+}
diff --git a/gnu/usr.bin/perl/x2p/hash.h b/gnu/usr.bin/perl/x2p/hash.h
new file mode 100644
index 0000000..ae4ffaf
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/hash.h
@@ -0,0 +1,60 @@
+/* $RCSfile: hash.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:10 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: hash.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:30:10 nate
+ * PERL!
+ *
+ * Revision 4.0.1.1 91/06/07 12:16:04 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:57:53 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#define FILLPCT 60 /* don't make greater than 99 */
+
+#ifdef DOINIT
+char coeff[] = {
+ 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+ 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+ 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+ 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+ 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+ 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+ 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
+ 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1};
+#else
+extern char coeff[];
+#endif
+
+typedef struct hentry HENT;
+
+struct hentry {
+ HENT *hent_next;
+ char *hent_key;
+ STR *hent_val;
+ int hent_hash;
+};
+
+struct htbl {
+ HENT **tbl_array;
+ int tbl_max;
+ int tbl_fill;
+ int tbl_riter; /* current root of iterator */
+ HENT *tbl_eiter; /* current entry of iterator */
+};
+
+STR *hfetch();
+bool hstore();
+bool hdelete();
+HASH *hnew();
+int hiterinit();
+HENT *hiternext();
+char *hiterkey();
+STR *hiterval();
diff --git a/gnu/usr.bin/perl/x2p/malloc.c b/gnu/usr.bin/perl/x2p/malloc.c
new file mode 100644
index 0000000..dd43390
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/malloc.c
@@ -0,0 +1,513 @@
+/* $RCSfile: malloc.c,v $$Revision: 1.2 $$Date: 1993/08/24 17:57:39 $
+ *
+ * $Log: malloc.c,v $
+ * Revision 1.2 1993/08/24 17:57:39 nate
+ * Fix for ALIGN macros in PERL that conflict with 4.4 macros
+ *
+ * Revision 1.1.1.1 1993/08/23 21:30:11 nate
+ * PERL!
+ *
+ * Revision 4.0.1.4 92/06/08 14:28:38 lwall
+ * patch20: removed implicit int declarations on functions
+ * patch20: hash tables now split only if the memory is available to do so
+ * patch20: realloc(0, size) now does malloc in case library routines call it
+ *
+ * Revision 4.0.1.3 91/11/05 17:57:40 lwall
+ * patch11: safe malloc code now integrated into Perl's malloc when possible
+ *
+ * Revision 4.0.1.2 91/06/07 11:20:45 lwall
+ * patch4: many, many itty-bitty portability fixes
+ *
+ * Revision 4.0.1.1 91/04/11 17:48:31 lwall
+ * patch1: Configure now figures out malloc ptr type
+ *
+ * Revision 4.0 91/03/20 01:28:52 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#ifndef lint
+/*SUPPRESS 592*/
+static char sccsid[] = "@(#)malloc.c 4.3 (Berkeley) 9/16/83";
+
+#ifdef DEBUGGING
+#define RCHECK
+#endif
+/*
+ * malloc.c (Caltech) 2/21/82
+ * Chris Kingsley, kingsley@cit-20.
+ *
+ * This is a very fast storage allocator. It allocates blocks of a small
+ * number of different sizes, and keeps free lists of each size. Blocks that
+ * don't exactly fit are passed up to the next larger size. In this
+ * implementation, the available sizes are 2^n-4 (or 2^n-12) bytes long.
+ * This is designed for use in a program that uses vast quantities of memory,
+ * but bombs when it runs out.
+ */
+
+#include "EXTERN.h"
+#include "../perl.h"
+
+static findbucket(), morecore();
+
+/* I don't much care whether these are defined in sys/types.h--LAW */
+
+#define u_char unsigned char
+#define u_int unsigned int
+#define u_short unsigned short
+
+/*
+ * The overhead on a block is at least 4 bytes. When free, this space
+ * contains a pointer to the next free block, and the bottom two bits must
+ * be zero. When in use, the first byte is set to MAGIC, and the second
+ * byte is the size index. The remaining bytes are for alignment.
+ * If range checking is enabled and the size of the block fits
+ * in two bytes, then the top two bytes hold the size of the requested block
+ * plus the range checking words, and the header word MINUS ONE.
+ */
+union overhead {
+ union overhead *ov_next; /* when free */
+#if ALIGN_BYTES > 4
+ double strut; /* alignment problems */
+#endif
+ struct {
+ u_char ovu_magic; /* magic number */
+ u_char ovu_index; /* bucket # */
+#ifdef RCHECK
+ u_short ovu_size; /* actual block size */
+ u_int ovu_rmagic; /* range magic number */
+#endif
+ } ovu;
+#define ov_magic ovu.ovu_magic
+#define ov_index ovu.ovu_index
+#define ov_size ovu.ovu_size
+#define ov_rmagic ovu.ovu_rmagic
+};
+
+#define MAGIC 0xff /* magic # on accounting info */
+#define OLDMAGIC 0x7f /* same after a free() */
+#define RMAGIC 0x55555555 /* magic # on range info */
+#ifdef RCHECK
+#define RSLOP sizeof (u_int)
+#else
+#define RSLOP 0
+#endif
+
+/*
+ * nextf[i] is the pointer to the next free block of size 2^(i+3). The
+ * smallest allocatable block is 8 bytes. The overhead information
+ * precedes the data area returned to the user.
+ */
+#define NBUCKETS 30
+static union overhead *nextf[NBUCKETS];
+extern char *sbrk();
+
+#ifdef MSTATS
+/*
+ * nmalloc[i] is the difference between the number of mallocs and frees
+ * for a given block size.
+ */
+static u_int nmalloc[NBUCKETS];
+#include <stdio.h>
+#endif
+
+#ifdef debug
+#define ASSERT(p) if (!(p)) botch("p"); else
+static void
+botch(s)
+ char *s;
+{
+
+ printf("assertion botched: %s\n", s);
+ abort();
+}
+#else
+#define ASSERT(p)
+#endif
+
+#ifdef safemalloc
+static int an = 0;
+#endif
+
+MALLOCPTRTYPE *
+malloc(nbytes)
+ register MEM_SIZE nbytes;
+{
+ register union overhead *p;
+ register int bucket = 0;
+ register MEM_SIZE shiftr;
+
+#ifdef safemalloc
+#ifdef DEBUGGING
+ MEM_SIZE size = nbytes;
+#endif
+
+#ifdef MSDOS
+ if (nbytes > 0xffff) {
+ fprintf(stderr, "Allocation too large: %lx\n", (long)nbytes);
+ exit(1);
+ }
+#endif /* MSDOS */
+#ifdef DEBUGGING
+ if ((long)nbytes < 0)
+ fatal("panic: malloc");
+#endif
+#endif /* safemalloc */
+
+ /*
+ * Convert amount of memory requested into
+ * closest block size stored in hash buckets
+ * which satisfies request. Account for
+ * space used per block for accounting.
+ */
+ nbytes += sizeof (union overhead) + RSLOP;
+ nbytes = (nbytes + 3) &~ 3;
+ shiftr = (nbytes - 1) >> 2;
+ /* apart from this loop, this is O(1) */
+ while (shiftr >>= 1)
+ bucket++;
+ /*
+ * If nothing in hash bucket right now,
+ * request more memory from the system.
+ */
+ if (nextf[bucket] == NULL)
+ morecore(bucket);
+ if ((p = (union overhead *)nextf[bucket]) == NULL) {
+#ifdef safemalloc
+ if (!nomemok) {
+ fputs("Out of memory!\n", stderr);
+ exit(1);
+ }
+#else
+ return (NULL);
+#endif
+ }
+
+#ifdef safemalloc
+#ifdef DEBUGGING
+# if !(defined(I286) || defined(atarist))
+ if (debug & 128)
+ fprintf(stderr,"0x%x: (%05d) malloc %ld bytes\n",p+1,an++,(long)size);
+# else
+ if (debug & 128)
+ fprintf(stderr,"0x%lx: (%05d) malloc %ld bytes\n",p+1,an++,(long)size);
+# endif
+#endif
+#endif /* safemalloc */
+
+ /* remove from linked list */
+#ifdef RCHECK
+ if (*((int*)p) & (sizeof(union overhead) - 1))
+#if !(defined(I286) || defined(atarist))
+ fprintf(stderr,"Corrupt malloc ptr 0x%x at 0x%x\n",*((int*)p),p);
+#else
+ fprintf(stderr,"Corrupt malloc ptr 0x%lx at 0x%lx\n",*((int*)p),p);
+#endif
+#endif
+ nextf[bucket] = p->ov_next;
+ p->ov_magic = MAGIC;
+ p->ov_index= bucket;
+#ifdef MSTATS
+ nmalloc[bucket]++;
+#endif
+#ifdef RCHECK
+ /*
+ * Record allocated size of block and
+ * bound space with magic numbers.
+ */
+ if (nbytes <= 0x10000)
+ p->ov_size = nbytes - 1;
+ p->ov_rmagic = RMAGIC;
+ *((u_int *)((caddr_t)p + nbytes - RSLOP)) = RMAGIC;
+#endif
+ return ((MALLOCPTRTYPE *)(p + 1));
+}
+
+/*
+ * Allocate more memory to the indicated bucket.
+ */
+static
+morecore(bucket)
+ register int bucket;
+{
+ register union overhead *op;
+ register int rnu; /* 2^rnu bytes will be requested */
+ register int nblks; /* become nblks blocks of the desired size */
+ register MEM_SIZE siz;
+
+ if (nextf[bucket])
+ return;
+ /*
+ * Insure memory is allocated
+ * on a page boundary. Should
+ * make getpageize call?
+ */
+#ifndef atarist /* on the atari we dont have to worry about this */
+ op = (union overhead *)sbrk(0);
+#ifndef I286
+ if ((int)op & 0x3ff)
+ (void)sbrk(1024 - ((int)op & 0x3ff));
+#else
+ /* The sbrk(0) call on the I286 always returns the next segment */
+#endif
+#endif /* atarist */
+
+#if !(defined(I286) || defined(atarist))
+ /* take 2k unless the block is bigger than that */
+ rnu = (bucket <= 8) ? 11 : bucket + 3;
+#else
+ /* take 16k unless the block is bigger than that
+ (80286s like large segments!), probably good on the atari too */
+ rnu = (bucket <= 11) ? 14 : bucket + 3;
+#endif
+ nblks = 1 << (rnu - (bucket + 3)); /* how many blocks to get */
+ if (rnu < bucket)
+ rnu = bucket;
+ op = (union overhead *)sbrk(1L << rnu);
+ /* no more room! */
+ if ((int)op == -1)
+ return;
+ /*
+ * Round up to minimum allocation size boundary
+ * and deduct from block count to reflect.
+ */
+#ifndef I286
+ if ((int)op & 7) {
+ op = (union overhead *)(((MEM_SIZE)op + 8) &~ 7);
+ nblks--;
+ }
+#else
+ /* Again, this should always be ok on an 80286 */
+#endif
+ /*
+ * Add new memory allocated to that on
+ * free list for this hash bucket.
+ */
+ nextf[bucket] = op;
+ siz = 1 << (bucket + 3);
+ while (--nblks > 0) {
+ op->ov_next = (union overhead *)((caddr_t)op + siz);
+ op = (union overhead *)((caddr_t)op + siz);
+ }
+}
+
+void
+free(mp)
+ MALLOCPTRTYPE *mp;
+{
+ register MEM_SIZE size;
+ register union overhead *op;
+ char *cp = (char*)mp;
+
+#ifdef safemalloc
+#ifdef DEBUGGING
+# if !(defined(I286) || defined(atarist))
+ if (debug & 128)
+ fprintf(stderr,"0x%x: (%05d) free\n",cp,an++);
+# else
+ if (debug & 128)
+ fprintf(stderr,"0x%lx: (%05d) free\n",cp,an++);
+# endif
+#endif
+#endif /* safemalloc */
+
+ if (cp == NULL)
+ return;
+ op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
+#ifdef debug
+ ASSERT(op->ov_magic == MAGIC); /* make sure it was in use */
+#else
+ if (op->ov_magic != MAGIC) {
+ warn("%s free() ignored",
+ op->ov_magic == OLDMAGIC ? "Duplicate" : "Bad");
+ return; /* sanity */
+ }
+ op->ov_magic = OLDMAGIC;
+#endif
+#ifdef RCHECK
+ ASSERT(op->ov_rmagic == RMAGIC);
+ if (op->ov_index <= 13)
+ ASSERT(*(u_int *)((caddr_t)op + op->ov_size + 1 - RSLOP) == RMAGIC);
+#endif
+ ASSERT(op->ov_index < NBUCKETS);
+ size = op->ov_index;
+ op->ov_next = nextf[size];
+ nextf[size] = op;
+#ifdef MSTATS
+ nmalloc[size]--;
+#endif
+}
+
+/*
+ * When a program attempts "storage compaction" as mentioned in the
+ * old malloc man page, it realloc's an already freed block. Usually
+ * this is the last block it freed; occasionally it might be farther
+ * back. We have to search all the free lists for the block in order
+ * to determine its bucket: 1st we make one pass thru the lists
+ * checking only the first block in each; if that fails we search
+ * ``reall_srchlen'' blocks in each list for a match (the variable
+ * is extern so the caller can modify it). If that fails we just copy
+ * however many bytes was given to realloc() and hope it's not huge.
+ */
+int reall_srchlen = 4; /* 4 should be plenty, -1 =>'s whole list */
+
+MALLOCPTRTYPE *
+realloc(mp, nbytes)
+ MALLOCPTRTYPE *mp;
+ MEM_SIZE nbytes;
+{
+ register MEM_SIZE onb;
+ union overhead *op;
+ char *res;
+ register int i;
+ int was_alloced = 0;
+ char *cp = (char*)mp;
+
+#ifdef safemalloc
+#ifdef DEBUGGING
+ MEM_SIZE size = nbytes;
+#endif
+
+#ifdef MSDOS
+ if (nbytes > 0xffff) {
+ fprintf(stderr, "Reallocation too large: %lx\n", size);
+ exit(1);
+ }
+#endif /* MSDOS */
+ if (!cp)
+ return malloc(nbytes);
+#ifdef DEBUGGING
+ if ((long)nbytes < 0)
+ fatal("panic: realloc");
+#endif
+#endif /* safemalloc */
+
+ op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
+ if (op->ov_magic == MAGIC) {
+ was_alloced++;
+ i = op->ov_index;
+ } else {
+ /*
+ * Already free, doing "compaction".
+ *
+ * Search for the old block of memory on the
+ * free list. First, check the most common
+ * case (last element free'd), then (this failing)
+ * the last ``reall_srchlen'' items free'd.
+ * If all lookups fail, then assume the size of
+ * the memory block being realloc'd is the
+ * smallest possible.
+ */
+ if ((i = findbucket(op, 1)) < 0 &&
+ (i = findbucket(op, reall_srchlen)) < 0)
+ i = 0;
+ }
+ onb = (1L << (i + 3)) - sizeof (*op) - RSLOP;
+ /* avoid the copy if same size block */
+ if (was_alloced &&
+ nbytes <= onb && nbytes > (onb >> 1) - sizeof(*op) - RSLOP) {
+#ifdef RCHECK
+ /*
+ * Record new allocated size of block and
+ * bound space with magic numbers.
+ */
+ if (op->ov_index <= 13) {
+ /*
+ * Convert amount of memory requested into
+ * closest block size stored in hash buckets
+ * which satisfies request. Account for
+ * space used per block for accounting.
+ */
+ nbytes += sizeof (union overhead) + RSLOP;
+ nbytes = (nbytes + 3) &~ 3;
+ op->ov_size = nbytes - 1;
+ *((u_int *)((caddr_t)op + nbytes - RSLOP)) = RMAGIC;
+ }
+#endif
+ res = cp;
+ }
+ else {
+ if ((res = (char*)malloc(nbytes)) == NULL)
+ return (NULL);
+ if (cp != res) /* common optimization */
+ Copy(cp, res, (MEM_SIZE)(nbytes<onb?nbytes:onb), char);
+ if (was_alloced)
+ free(cp);
+ }
+
+#ifdef safemalloc
+#ifdef DEBUGGING
+# if !(defined(I286) || defined(atarist))
+ if (debug & 128) {
+ fprintf(stderr,"0x%x: (%05d) rfree\n",res,an++);
+ fprintf(stderr,"0x%x: (%05d) realloc %ld bytes\n",res,an++,(long)size);
+ }
+# else
+ if (debug & 128) {
+ fprintf(stderr,"0x%lx: (%05d) rfree\n",res,an++);
+ fprintf(stderr,"0x%lx: (%05d) realloc %ld bytes\n",res,an++,(long)size);
+ }
+# endif
+#endif
+#endif /* safemalloc */
+ return ((MALLOCPTRTYPE*)res);
+}
+
+/*
+ * Search ``srchlen'' elements of each free list for a block whose
+ * header starts at ``freep''. If srchlen is -1 search the whole list.
+ * Return bucket number, or -1 if not found.
+ */
+static int
+findbucket(freep, srchlen)
+ union overhead *freep;
+ int srchlen;
+{
+ register union overhead *p;
+ register int i, j;
+
+ for (i = 0; i < NBUCKETS; i++) {
+ j = 0;
+ for (p = nextf[i]; p && j != srchlen; p = p->ov_next) {
+ if (p == freep)
+ return (i);
+ j++;
+ }
+ }
+ return (-1);
+}
+
+#ifdef MSTATS
+/*
+ * mstats - print out statistics about malloc
+ *
+ * Prints two lines of numbers, one showing the length of the free list
+ * for each size category, the second showing the number of mallocs -
+ * frees for each size category.
+ */
+void
+mstats(s)
+ char *s;
+{
+ register int i, j;
+ register union overhead *p;
+ int totfree = 0,
+ totused = 0;
+
+ fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s);
+ for (i = 0; i < NBUCKETS; i++) {
+ for (j = 0, p = nextf[i]; p; p = p->ov_next, j++)
+ ;
+ fprintf(stderr, " %d", j);
+ totfree += j * (1 << (i + 3));
+ }
+ fprintf(stderr, "\nused:\t");
+ for (i = 0; i < NBUCKETS; i++) {
+ fprintf(stderr, " %d", nmalloc[i]);
+ totused += nmalloc[i] * (1 << (i + 3));
+ }
+ fprintf(stderr, "\n\tTotal in use: %d, total free: %d\n",
+ totused, totfree);
+}
+#endif
+#endif /* lint */
diff --git a/gnu/usr.bin/perl/x2p/s2p b/gnu/usr.bin/perl/x2p/s2p
new file mode 100755
index 0000000..8a8a74c
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/s2p
@@ -0,0 +1,766 @@
+#!/usr/gnu/bin/perl
+
+eval 'exec /usr/gnu/bin/perl -S $0 ${1+"$@"}'
+ if $running_under_some_shell;
+
+$bin = '/usr/gnu/bin';
+
+# $RCSfile: s2p,v $$Revision: 1.2 $$Date: 1994/03/05 01:28:48 $
+#
+# $Log: s2p,v $
+# Revision 1.2 1994/03/05 01:28:48 ache
+# 1) Perl uses scrambler crypt() version from libc instead of proper one
+# from -lcrypt (if exist)
+# 2) We have now all sem/shm/msg stuff, add it to perl too
+#
+# Revision 1.1.1.1 1993/08/23 21:30:10 nate
+# PERL!
+#
+# Revision 4.0.1.2 92/06/08 17:26:31 lwall
+# patch20: s2p didn't output portable startup code
+# patch20: added ... as variant on ..
+# patch20: s2p didn't translate s/pat/\&/ or s/pat/\$/ or s/pat/\\1/ right
+#
+# Revision 4.0.1.1 91/06/07 12:19:18 lwall
+# patch4: s2p now handles embedded newlines better and optimizes common idioms
+#
+# Revision 4.0 91/03/20 01:57:59 lwall
+# 4.0 baseline.
+#
+#
+
+$indent = 4;
+$shiftwidth = 4;
+$l = '{'; $r = '}';
+
+while ($ARGV[0] =~ /^-/) {
+ $_ = shift;
+ last if /^--/;
+ if (/^-D/) {
+ $debug++;
+ open(BODY,'>-');
+ next;
+ }
+ if (/^-n/) {
+ $assumen++;
+ next;
+ }
+ if (/^-p/) {
+ $assumep++;
+ next;
+ }
+ die "I don't recognize this switch: $_\n";
+}
+
+unless ($debug) {
+ open(BODY,">/tmp/sperl$$") ||
+ &Die("Can't open temp file: $!\n");
+}
+
+if (!$assumen && !$assumep) {
+ print BODY &q(<<'EOT');
+: while ($ARGV[0] =~ /^-/) {
+: $_ = shift;
+: last if /^--/;
+: if (/^-n/) {
+: $nflag++;
+: next;
+: }
+: die "I don't recognize this switch: $_\\n";
+: }
+:
+EOT
+}
+
+print BODY &q(<<'EOT');
+: #ifdef PRINTIT
+: #ifdef ASSUMEP
+: $printit++;
+: #else
+: $printit++ unless $nflag;
+: #endif
+: #endif
+: <><>
+: $\ = "\n"; # automatically add newline on print
+: <><>
+: #ifdef TOPLABEL
+: LINE:
+: while (chop($_ = <>)) {
+: #else
+: LINE:
+: while (<>) {
+: chop;
+: #endif
+EOT
+
+LINE:
+while (<>) {
+
+ # Wipe out surrounding whitespace.
+
+ s/[ \t]*(.*)\n$/$1/;
+
+ # Perhaps it's a label/comment.
+
+ if (/^:/) {
+ s/^:[ \t]*//;
+ $label = &make_label($_);
+ if ($. == 1) {
+ $toplabel = $label;
+ if (/^(top|(re)?start|redo|begin(ning)|again|input)$/i) {
+ $_ = <>;
+ redo LINE; # Never referenced, so delete it if not a comment.
+ }
+ }
+ $_ = "$label:";
+ if ($lastlinewaslabel++) {
+ $indent += 4;
+ print BODY &tab, ";\n";
+ $indent -= 4;
+ }
+ if ($indent >= 2) {
+ $indent -= 2;
+ $indmod = 2;
+ }
+ next;
+ } else {
+ $lastlinewaslabel = '';
+ }
+
+ # Look for one or two address clauses
+
+ $addr1 = '';
+ $addr2 = '';
+ if (s/^([0-9]+)//) {
+ $addr1 = "$1";
+ $addr1 = "\$. == $addr1" unless /^,/;
+ }
+ elsif (s/^\$//) {
+ $addr1 = 'eof()';
+ }
+ elsif (s|^/||) {
+ $addr1 = &fetchpat('/');
+ }
+ if (s/^,//) {
+ if (s/^([0-9]+)//) {
+ $addr2 = "$1";
+ } elsif (s/^\$//) {
+ $addr2 = "eof()";
+ } elsif (s|^/||) {
+ $addr2 = &fetchpat('/');
+ } else {
+ &Die("Invalid second address at line $.\n");
+ }
+ if ($addr2 =~ /^\d+$/) {
+ $addr1 .= "..$addr2";
+ }
+ else {
+ $addr1 .= "...$addr2";
+ }
+ }
+
+ # Now we check for metacommands {, }, and ! and worry
+ # about indentation.
+
+ s/^[ \t]+//;
+ # a { to keep vi happy
+ if ($_ eq '}') {
+ $indent -= 4;
+ next;
+ }
+ if (s/^!//) {
+ $if = 'unless';
+ $else = "$r else $l\n";
+ } else {
+ $if = 'if';
+ $else = '';
+ }
+ if (s/^{//) { # a } to keep vi happy
+ $indmod = 4;
+ $redo = $_;
+ $_ = '';
+ $rmaybe = '';
+ } else {
+ $rmaybe = "\n$r";
+ if ($addr2 || $addr1) {
+ $space = ' ' x $shiftwidth;
+ } else {
+ $space = '';
+ }
+ $_ = &transmogrify();
+ }
+
+ # See if we can optimize to modifier form.
+
+ if ($addr1) {
+ if ($_ !~ /[\n{}]/ && $rmaybe && !$change &&
+ $_ !~ / if / && $_ !~ / unless /) {
+ s/;$/ $if $addr1;/;
+ $_ = substr($_,$shiftwidth,1000);
+ } else {
+ $_ = "$if ($addr1) $l\n$change$_$rmaybe";
+ }
+ $change = '';
+ next LINE;
+ }
+} continue {
+ @lines = split(/\n/,$_);
+ for (@lines) {
+ unless (s/^ *<<--//) {
+ print BODY &tab;
+ }
+ print BODY $_, "\n";
+ }
+ $indent += $indmod;
+ $indmod = 0;
+ if ($redo) {
+ $_ = $redo;
+ $redo = '';
+ redo LINE;
+ }
+}
+if ($lastlinewaslabel++) {
+ $indent += 4;
+ print BODY &tab, ";\n";
+ $indent -= 4;
+}
+
+if ($appendseen || $tseen || !$assumen) {
+ $printit++ if $dseen || (!$assumen && !$assumep);
+ print BODY &q(<<'EOT');
+: #ifdef SAWNEXT
+: }
+: continue {
+: #endif
+: #ifdef PRINTIT
+: #ifdef DSEEN
+: #ifdef ASSUMEP
+: print if $printit++;
+: #else
+: if ($printit)
+: { print; }
+: else
+: { $printit++ unless $nflag; }
+: #endif
+: #else
+: print if $printit;
+: #endif
+: #else
+: print;
+: #endif
+: #ifdef TSEEN
+: $tflag = 0;
+: #endif
+: #ifdef APPENDSEEN
+: if ($atext) { chop $atext; print $atext; $atext = ''; }
+: #endif
+EOT
+
+print BODY &q(<<'EOT');
+: }
+EOT
+}
+
+close BODY;
+
+unless ($debug) {
+ open(HEAD,">/tmp/sperl2$$.c")
+ || &Die("Can't open temp file 2: $!\n");
+ print HEAD "#define PRINTIT\n" if $printit;
+ print HEAD "#define APPENDSEEN\n" if $appendseen;
+ print HEAD "#define TSEEN\n" if $tseen;
+ print HEAD "#define DSEEN\n" if $dseen;
+ print HEAD "#define ASSUMEN\n" if $assumen;
+ print HEAD "#define ASSUMEP\n" if $assumep;
+ print HEAD "#define TOPLABEL\n" if $toplabel;
+ print HEAD "#define SAWNEXT\n" if $sawnext;
+ if ($opens) {print HEAD "$opens\n";}
+ open(BODY,"/tmp/sperl$$")
+ || &Die("Can't reopen temp file: $!\n");
+ while (<BODY>) {
+ print HEAD $_;
+ }
+ close HEAD;
+
+ print &q(<<"EOT");
+: #!$bin/perl
+: eval 'exec $bin/perl -S \$0 \${1+"\$@"}'
+: if \$running_under_some_shell;
+:
+EOT
+ open(BODY,"cc -E /tmp/sperl2$$.c |") ||
+ &Die("Can't reopen temp file: $!\n");
+ while (<BODY>) {
+ /^# [0-9]/ && next;
+ /^[ \t]*$/ && next;
+ s/^<><>//;
+ print;
+ }
+}
+
+&Cleanup;
+exit;
+
+sub Cleanup {
+ chdir "/tmp";
+ unlink "sperl$$", "sperl2$$", "sperl2$$.c";
+}
+sub Die {
+ &Cleanup;
+ die $_[0];
+}
+sub tab {
+ "\t" x ($indent / 8) . ' ' x ($indent % 8);
+}
+sub make_filehandle {
+ local($_) = $_[0];
+ local($fname) = $_;
+ if (!$seen{$fname}) {
+ $_ = "FH_" . $_ if /^\d/;
+ s/[^a-zA-Z0-9]/_/g;
+ s/^_*//;
+ $_ = "\U$_";
+ if ($fhseen{$_}) {
+ for ($tmp = "a"; $fhseen{"$_$tmp"}; $a++) {}
+ $_ .= $tmp;
+ }
+ $fhseen{$_} = 1;
+ $opens .= &q(<<"EOT");
+: open($_, '>$fname') || die "Can't create $fname: \$!";
+EOT
+ $seen{$fname} = $_;
+ }
+ $seen{$fname};
+}
+
+sub make_label {
+ local($label) = @_;
+ $label =~ s/[^a-zA-Z0-9]/_/g;
+ if ($label =~ /^[0-9_]/) { $label = 'L' . $label; }
+ $label = substr($label,0,8);
+
+ # Could be a reserved word, so capitalize it.
+ substr($label,0,1) =~ y/a-z/A-Z/
+ if $label =~ /^[a-z]/;
+
+ $label;
+}
+
+sub transmogrify {
+ { # case
+ if (/^d/) {
+ $dseen++;
+ chop($_ = &q(<<'EOT'));
+: <<--#ifdef PRINTIT
+: $printit = 0;
+: <<--#endif
+: next LINE;
+EOT
+ $sawnext++;
+ next;
+ }
+
+ if (/^n/) {
+ chop($_ = &q(<<'EOT'));
+: <<--#ifdef PRINTIT
+: <<--#ifdef DSEEN
+: <<--#ifdef ASSUMEP
+: print if $printit++;
+: <<--#else
+: if ($printit)
+: { print; }
+: else
+: { $printit++ unless $nflag; }
+: <<--#endif
+: <<--#else
+: print if $printit;
+: <<--#endif
+: <<--#else
+: print;
+: <<--#endif
+: <<--#ifdef APPENDSEEN
+: if ($atext) {chop $atext; print $atext; $atext = '';}
+: <<--#endif
+: $_ = <>;
+: chop;
+: <<--#ifdef TSEEN
+: $tflag = 0;
+: <<--#endif
+EOT
+ next;
+ }
+
+ if (/^a/) {
+ $appendseen++;
+ $command = $space . "\$atext .= <<'End_Of_Text';\n<<--";
+ $lastline = 0;
+ while (<>) {
+ s/^[ \t]*//;
+ s/^[\\]//;
+ unless (s|\\$||) { $lastline = 1;}
+ s/^([ \t]*\n)/<><>$1/;
+ $command .= $_;
+ $command .= '<<--';
+ last if $lastline;
+ }
+ $_ = $command . "End_Of_Text";
+ last;
+ }
+
+ if (/^[ic]/) {
+ if (/^c/) { $change = 1; }
+ $addr1 = 1 if $addr1 eq '';
+ $addr1 = '$iter = (' . $addr1 . ')';
+ $command = $space .
+ " if (\$iter == 1) { print <<'End_Of_Text'; }\n<<--";
+ $lastline = 0;
+ while (<>) {
+ s/^[ \t]*//;
+ s/^[\\]//;
+ unless (s/\\$//) { $lastline = 1;}
+ s/'/\\'/g;
+ s/^([ \t]*\n)/<><>$1/;
+ $command .= $_;
+ $command .= '<<--';
+ last if $lastline;
+ }
+ $_ = $command . "End_Of_Text";
+ if ($change) {
+ $dseen++;
+ $change = "$_\n";
+ chop($_ = &q(<<"EOT"));
+: <<--#ifdef PRINTIT
+: $space\$printit = 0;
+: <<--#endif
+: ${space}next LINE;
+EOT
+ $sawnext++;
+ }
+ last;
+ }
+
+ if (/^s/) {
+ $delim = substr($_,1,1);
+ $len = length($_);
+ $repl = $end = 0;
+ $inbracket = 0;
+ for ($i = 2; $i < $len; $i++) {
+ $c = substr($_,$i,1);
+ if ($c eq $delim) {
+ if ($inbracket) {
+ substr($_, $i, 0) = '\\';
+ $i++;
+ $len++;
+ }
+ else {
+ if ($repl) {
+ $end = $i;
+ last;
+ } else {
+ $repl = $i;
+ }
+ }
+ }
+ elsif ($c eq '\\') {
+ $i++;
+ if ($i >= $len) {
+ $_ .= 'n';
+ $_ .= <>;
+ $len = length($_);
+ $_ = substr($_,0,--$len);
+ }
+ elsif (substr($_,$i,1) =~ /^[n]$/) {
+ ;
+ }
+ elsif (!$repl &&
+ substr($_,$i,1) =~ /^[(){}\w]$/) {
+ $i--;
+ $len--;
+ substr($_, $i, 1) = '';
+ }
+ elsif (!$repl &&
+ substr($_,$i,1) =~ /^[<>]$/) {
+ substr($_,$i,1) = 'b';
+ }
+ elsif ($repl && substr($_,$i,1) =~ /^\d$/) {
+ substr($_,$i-1,1) = '$';
+ }
+ }
+ elsif ($c eq '&' && $repl) {
+ substr($_, $i, 0) = '$';
+ $i++;
+ $len++;
+ }
+ elsif ($c eq '$' && $repl) {
+ substr($_, $i, 0) = '\\';
+ $i++;
+ $len++;
+ }
+ elsif ($c eq '[' && !$repl) {
+ $i++ if substr($_,$i,1) eq '^';
+ $i++ if substr($_,$i,1) eq ']';
+ $inbracket = 1;
+ }
+ elsif ($c eq ']') {
+ $inbracket = 0;
+ }
+ elsif ($c eq "\t") {
+ substr($_, $i, 1) = '\\t';
+ $i++;
+ $len++;
+ }
+ elsif (!$repl && index("()+",$c) >= 0) {
+ substr($_, $i, 0) = '\\';
+ $i++;
+ $len++;
+ }
+ }
+ &Die("Malformed substitution at line $.\n")
+ unless $end;
+ $pat = substr($_, 0, $repl + 1);
+ $repl = substr($_, $repl+1, $end-$repl-1);
+ $end = substr($_, $end + 1, 1000);
+ &simplify($pat);
+ $dol = '$';
+ $subst = "$pat$repl$delim";
+ $cmd = '';
+ while ($end) {
+ if ($end =~ s/^g//) {
+ $subst .= 'g';
+ next;
+ }
+ if ($end =~ s/^p//) {
+ $cmd .= ' && (print)';
+ next;
+ }
+ if ($end =~ s/^w[ \t]*//) {
+ $fh = &make_filehandle($end);
+ $cmd .= " && (print $fh \$_)";
+ $end = '';
+ next;
+ }
+ &Die("Unrecognized substitution command".
+ "($end) at line $.\n");
+ }
+ chop ($_ = &q(<<"EOT"));
+: <<--#ifdef TSEEN
+: $subst && \$tflag++$cmd;
+: <<--#else
+: $subst$cmd;
+: <<--#endif
+EOT
+ next;
+ }
+
+ if (/^p/) {
+ $_ = 'print;';
+ next;
+ }
+
+ if (/^w/) {
+ s/^w[ \t]*//;
+ $fh = &make_filehandle($_);
+ $_ = "print $fh \$_;";
+ next;
+ }
+
+ if (/^r/) {
+ $appendseen++;
+ s/^r[ \t]*//;
+ $file = $_;
+ $_ = "\$atext .= `cat $file 2>/dev/null`;";
+ next;
+ }
+
+ if (/^P/) {
+ $_ = 'print $1 if /^(.*)/;';
+ next;
+ }
+
+ if (/^D/) {
+ chop($_ = &q(<<'EOT'));
+: s/^.*\n?//;
+: redo LINE if $_;
+: next LINE;
+EOT
+ $sawnext++;
+ next;
+ }
+
+ if (/^N/) {
+ chop($_ = &q(<<'EOT'));
+: $_ .= "\n";
+: $len1 = length;
+: $_ .= <>;
+: chop if $len1 < length;
+: <<--#ifdef TSEEN
+: $tflag = 0;
+: <<--#endif
+EOT
+ next;
+ }
+
+ if (/^h/) {
+ $_ = '$hold = $_;';
+ next;
+ }
+
+ if (/^H/) {
+ $_ = '$hold .= "\n"; $hold .= $_;';
+ next;
+ }
+
+ if (/^g/) {
+ $_ = '$_ = $hold;';
+ next;
+ }
+
+ if (/^G/) {
+ $_ = '$_ .= "\n"; $_ .= $hold;';
+ next;
+ }
+
+ if (/^x/) {
+ $_ = '($_, $hold) = ($hold, $_);';
+ next;
+ }
+
+ if (/^b$/) {
+ $_ = 'next LINE;';
+ $sawnext++;
+ next;
+ }
+
+ if (/^b/) {
+ s/^b[ \t]*//;
+ $lab = &make_label($_);
+ if ($lab eq $toplabel) {
+ $_ = 'redo LINE;';
+ } else {
+ $_ = "goto $lab;";
+ }
+ next;
+ }
+
+ if (/^t$/) {
+ $_ = 'next LINE if $tflag;';
+ $sawnext++;
+ $tseen++;
+ next;
+ }
+
+ if (/^t/) {
+ s/^t[ \t]*//;
+ $lab = &make_label($_);
+ $_ = q/if ($tflag) {$tflag = 0; /;
+ if ($lab eq $toplabel) {
+ $_ .= 'redo LINE;}';
+ } else {
+ $_ .= "goto $lab;}";
+ }
+ $tseen++;
+ next;
+ }
+
+ if (/^y/) {
+ s/abcdefghijklmnopqrstuvwxyz/a-z/g;
+ s/ABCDEFGHIJKLMNOPQRSTUVWXYZ/A-Z/g;
+ s/abcdef/a-f/g;
+ s/ABCDEF/A-F/g;
+ s/0123456789/0-9/g;
+ s/01234567/0-7/g;
+ $_ .= ';';
+ }
+
+ if (/^=/) {
+ $_ = 'print $.;';
+ next;
+ }
+
+ if (/^q/) {
+ chop($_ = &q(<<'EOT'));
+: close(ARGV);
+: @ARGV = ();
+: next LINE;
+EOT
+ $sawnext++;
+ next;
+ }
+ } continue {
+ if ($space) {
+ s/^/$space/;
+ s/(\n)(.)/$1$space$2/g;
+ }
+ last;
+ }
+ $_;
+}
+
+sub fetchpat {
+ local($outer) = @_;
+ local($addr) = $outer;
+ local($inbracket);
+ local($prefix,$delim,$ch);
+
+ # Process pattern one potential delimiter at a time.
+
+ DELIM: while (s#^([^\]+(|)[\\/]*)([]+(|)[\\/])##) {
+ $prefix = $1;
+ $delim = $2;
+ if ($delim eq '\\') {
+ s/(.)//;
+ $ch = $1;
+ $delim = '' if $ch =~ /^[(){}A-Za-mo-z]$/;
+ $ch = 'b' if $ch =~ /^[<>]$/;
+ $delim .= $ch;
+ }
+ elsif ($delim eq '[') {
+ $inbracket = 1;
+ s/^\^// && ($delim .= '^');
+ s/^]// && ($delim .= ']');
+ }
+ elsif ($delim eq ']') {
+ $inbracket = 0;
+ }
+ elsif ($inbracket || $delim ne $outer) {
+ $delim = '\\' . $delim;
+ }
+ $addr .= $prefix;
+ $addr .= $delim;
+ if ($delim eq $outer && !$inbracket) {
+ last DELIM;
+ }
+ }
+ $addr =~ s/\t/\\t/g;
+ &simplify($addr);
+ $addr;
+}
+
+sub q {
+ local($string) = @_;
+ local($*) = 1;
+ $string =~ s/^:\t?//g;
+ $string;
+}
+
+sub simplify {
+ $_[0] =~ s/_a-za-z0-9/\\w/ig;
+ $_[0] =~ s/a-z_a-z0-9/\\w/ig;
+ $_[0] =~ s/a-za-z_0-9/\\w/ig;
+ $_[0] =~ s/a-za-z0-9_/\\w/ig;
+ $_[0] =~ s/_0-9a-za-z/\\w/ig;
+ $_[0] =~ s/0-9_a-za-z/\\w/ig;
+ $_[0] =~ s/0-9a-z_a-z/\\w/ig;
+ $_[0] =~ s/0-9a-za-z_/\\w/ig;
+ $_[0] =~ s/\[\\w\]/\\w/g;
+ $_[0] =~ s/\[^\\w\]/\\W/g;
+ $_[0] =~ s/\[0-9\]/\\d/g;
+ $_[0] =~ s/\[^0-9\]/\\D/g;
+ $_[0] =~ s/\\d\\d\*/\\d+/g;
+ $_[0] =~ s/\\D\\D\*/\\D+/g;
+ $_[0] =~ s/\\w\\w\*/\\w+/g;
+ $_[0] =~ s/\\t\\t\*/\\t+/g;
+ $_[0] =~ s/(\[.[^]]*\])\1\*/$1+/g;
+ $_[0] =~ s/([\w\s!@#%^&-=,:;'"])\1\*/$1+/g;
+}
+
diff --git a/gnu/usr.bin/perl/x2p/s2p.1 b/gnu/usr.bin/perl/x2p/s2p.1
new file mode 100644
index 0000000..ab74717
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/s2p.1
@@ -0,0 +1,108 @@
+.rn '' }`
+''' $RCSfile: s2p.man,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:10 $
+'''
+''' $Log: s2p.man,v $
+.\" Revision 1.1.1.1 1993/08/23 21:30:10 nate
+.\" PERL!
+.\"
+''' Revision 4.0.1.1 91/06/07 12:19:57 lwall
+''' patch4: s2p now handles embedded newlines better and optimizes common idioms
+'''
+''' Revision 4.0 91/03/20 01:58:07 lwall
+''' 4.0 baseline.
+'''
+''' Revision 3.0 89/10/18 15:35:09 lwall
+''' 3.0 baseline
+'''
+''' Revision 2.0 88/06/05 00:15:59 root
+''' Baseline version 2.0.
+'''
+'''
+.de Sh
+.br
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp
+.if t .sp .5v
+.if n .sp
+..
+.de Ip
+.br
+.ie \\n.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+'''
+''' Set up \*(-- to give an unbreakable dash;
+''' string Tr holds user defined translation string.
+''' Bell System Logo is used as a dummy character.
+'''
+.tr \(*W-|\(bv\*(Tr
+.ie n \{\
+.ds -- \(*W-
+.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+.ds L" ""
+.ds R" ""
+.ds L' '
+.ds R' '
+'br\}
+.el\{\
+.ds -- \(em\|
+.tr \*(Tr
+.ds L" ``
+.ds R" ''
+.ds L' `
+.ds R' '
+'br\}
+.TH S2P 1 NEW
+.SH NAME
+s2p - Sed to Perl translator
+.SH SYNOPSIS
+.B s2p [options] filename
+.SH DESCRIPTION
+.I S2p
+takes a sed script specified on the command line (or from standard input)
+and produces a comparable
+.I perl
+script on the standard output.
+.Sh "Options"
+Options include:
+.TP 5
+.B \-D<number>
+sets debugging flags.
+.TP 5
+.B \-n
+specifies that this sed script was always invoked with a sed -n.
+Otherwise a switch parser is prepended to the front of the script.
+.TP 5
+.B \-p
+specifies that this sed script was never invoked with a sed -n.
+Otherwise a switch parser is prepended to the front of the script.
+.Sh "Considerations"
+The perl script produced looks very sed-ish, and there may very well be
+better ways to express what you want to do in perl.
+For instance, s2p does not make any use of the split operator, but you might
+want to.
+.PP
+The perl script you end up with may be either faster or slower than the original
+sed script.
+If you're only interested in speed you'll just have to try it both ways.
+Of course, if you want to do something sed doesn't do, you have no choice.
+It's often possible to speed up the perl script by various methods, such
+as deleting all references to $\e and chop.
+.SH ENVIRONMENT
+S2p uses no environment variables.
+.SH AUTHOR
+Larry Wall <lwall@jpl-devvax.Jpl.Nasa.Gov>
+.SH FILES
+.SH SEE ALSO
+perl The perl compiler/interpreter
+.br
+a2p awk to perl translator
+.SH DIAGNOSTICS
+.SH BUGS
+.rn }` ''
diff --git a/gnu/usr.bin/perl/x2p/str.c b/gnu/usr.bin/perl/x2p/str.c
new file mode 100644
index 0000000..0461194
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/str.c
@@ -0,0 +1,467 @@
+/* $RCSfile: str.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:09 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: str.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:30:09 nate
+ * PERL!
+ *
+ * Revision 4.0.1.1 91/06/07 12:20:08 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:58:15 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "handy.h"
+#include "EXTERN.h"
+#include "util.h"
+#include "a2p.h"
+
+str_numset(str,num)
+register STR *str;
+double num;
+{
+ str->str_nval = num;
+ str->str_pok = 0; /* invalidate pointer */
+ str->str_nok = 1; /* validate number */
+}
+
+char *
+str_2ptr(str)
+register STR *str;
+{
+ register char *s;
+
+ if (!str)
+ return "";
+ GROWSTR(&(str->str_ptr), &(str->str_len), 24);
+ s = str->str_ptr;
+ if (str->str_nok) {
+ sprintf(s,"%.20g",str->str_nval);
+ while (*s) s++;
+ }
+ *s = '\0';
+ str->str_cur = s - str->str_ptr;
+ str->str_pok = 1;
+#ifdef DEBUGGING
+ if (debug & 32)
+ fprintf(stderr,"0x%lx ptr(%s)\n",str,str->str_ptr);
+#endif
+ return str->str_ptr;
+}
+
+double
+str_2num(str)
+register STR *str;
+{
+ if (!str)
+ return 0.0;
+ if (str->str_len && str->str_pok)
+ str->str_nval = atof(str->str_ptr);
+ else
+ str->str_nval = 0.0;
+ str->str_nok = 1;
+#ifdef DEBUGGING
+ if (debug & 32)
+ fprintf(stderr,"0x%lx num(%g)\n",str,str->str_nval);
+#endif
+ return str->str_nval;
+}
+
+str_sset(dstr,sstr)
+STR *dstr;
+register STR *sstr;
+{
+ if (!sstr)
+ str_nset(dstr,No,0);
+ else if (sstr->str_nok)
+ str_numset(dstr,sstr->str_nval);
+ else if (sstr->str_pok)
+ str_nset(dstr,sstr->str_ptr,sstr->str_cur);
+ else
+ str_nset(dstr,"",0);
+}
+
+str_nset(str,ptr,len)
+register STR *str;
+register char *ptr;
+register int len;
+{
+ GROWSTR(&(str->str_ptr), &(str->str_len), len + 1);
+ bcopy(ptr,str->str_ptr,len);
+ str->str_cur = len;
+ *(str->str_ptr+str->str_cur) = '\0';
+ str->str_nok = 0; /* invalidate number */
+ str->str_pok = 1; /* validate pointer */
+}
+
+str_set(str,ptr)
+register STR *str;
+register char *ptr;
+{
+ register int len;
+
+ if (!ptr)
+ ptr = "";
+ len = strlen(ptr);
+ GROWSTR(&(str->str_ptr), &(str->str_len), len + 1);
+ bcopy(ptr,str->str_ptr,len+1);
+ str->str_cur = len;
+ str->str_nok = 0; /* invalidate number */
+ str->str_pok = 1; /* validate pointer */
+}
+
+str_chop(str,ptr) /* like set but assuming ptr is in str */
+register STR *str;
+register char *ptr;
+{
+ if (!(str->str_pok))
+ str_2ptr(str);
+ str->str_cur -= (ptr - str->str_ptr);
+ bcopy(ptr,str->str_ptr, str->str_cur + 1);
+ str->str_nok = 0; /* invalidate number */
+ str->str_pok = 1; /* validate pointer */
+}
+
+str_ncat(str,ptr,len)
+register STR *str;
+register char *ptr;
+register int len;
+{
+ if (!(str->str_pok))
+ str_2ptr(str);
+ GROWSTR(&(str->str_ptr), &(str->str_len), str->str_cur + len + 1);
+ bcopy(ptr,str->str_ptr+str->str_cur,len);
+ str->str_cur += len;
+ *(str->str_ptr+str->str_cur) = '\0';
+ str->str_nok = 0; /* invalidate number */
+ str->str_pok = 1; /* validate pointer */
+}
+
+str_scat(dstr,sstr)
+STR *dstr;
+register STR *sstr;
+{
+ if (!(sstr->str_pok))
+ str_2ptr(sstr);
+ if (sstr)
+ str_ncat(dstr,sstr->str_ptr,sstr->str_cur);
+}
+
+str_cat(str,ptr)
+register STR *str;
+register char *ptr;
+{
+ register int len;
+
+ if (!ptr)
+ return;
+ if (!(str->str_pok))
+ str_2ptr(str);
+ len = strlen(ptr);
+ GROWSTR(&(str->str_ptr), &(str->str_len), str->str_cur + len + 1);
+ bcopy(ptr,str->str_ptr+str->str_cur,len+1);
+ str->str_cur += len;
+ str->str_nok = 0; /* invalidate number */
+ str->str_pok = 1; /* validate pointer */
+}
+
+char *
+str_append_till(str,from,delim,keeplist)
+register STR *str;
+register char *from;
+register int delim;
+char *keeplist;
+{
+ register char *to;
+ register int len;
+
+ if (!from)
+ return Nullch;
+ len = strlen(from);
+ GROWSTR(&(str->str_ptr), &(str->str_len), str->str_cur + len + 1);
+ str->str_nok = 0; /* invalidate number */
+ str->str_pok = 1; /* validate pointer */
+ to = str->str_ptr+str->str_cur;
+ for (; *from; from++,to++) {
+ if (*from == '\\' && from[1] && delim != '\\') {
+ if (!keeplist) {
+ if (from[1] == delim || from[1] == '\\')
+ from++;
+ else
+ *to++ = *from++;
+ }
+ else if (index(keeplist,from[1]))
+ *to++ = *from++;
+ else
+ from++;
+ }
+ else if (*from == delim)
+ break;
+ *to = *from;
+ }
+ *to = '\0';
+ str->str_cur = to - str->str_ptr;
+ return from;
+}
+
+STR *
+str_new(len)
+int len;
+{
+ register STR *str;
+
+ if (freestrroot) {
+ str = freestrroot;
+ freestrroot = str->str_link.str_next;
+ }
+ else {
+ str = (STR *) safemalloc(sizeof(STR));
+ bzero((char*)str,sizeof(STR));
+ }
+ if (len)
+ GROWSTR(&(str->str_ptr), &(str->str_len), len + 1);
+ return str;
+}
+
+void
+str_grow(str,len)
+register STR *str;
+int len;
+{
+ if (len && str)
+ GROWSTR(&(str->str_ptr), &(str->str_len), len + 1);
+}
+
+/* make str point to what nstr did */
+
+void
+str_replace(str,nstr)
+register STR *str;
+register STR *nstr;
+{
+ safefree(str->str_ptr);
+ str->str_ptr = nstr->str_ptr;
+ str->str_len = nstr->str_len;
+ str->str_cur = nstr->str_cur;
+ str->str_pok = nstr->str_pok;
+ if (str->str_nok = nstr->str_nok)
+ str->str_nval = nstr->str_nval;
+ safefree((char*)nstr);
+}
+
+void
+str_free(str)
+register STR *str;
+{
+ if (!str)
+ return;
+ if (str->str_len)
+ str->str_ptr[0] = '\0';
+ str->str_cur = 0;
+ str->str_nok = 0;
+ str->str_pok = 0;
+ str->str_link.str_next = freestrroot;
+ freestrroot = str;
+}
+
+str_len(str)
+register STR *str;
+{
+ if (!str)
+ return 0;
+ if (!(str->str_pok))
+ str_2ptr(str);
+ if (str->str_len)
+ return str->str_cur;
+ else
+ return 0;
+}
+
+char *
+str_gets(str,fp)
+register STR *str;
+register FILE *fp;
+{
+#ifdef STDSTDIO /* Here is some breathtakingly efficient cheating */
+
+ register char *bp; /* we're going to steal some values */
+ register int cnt; /* from the stdio struct and put EVERYTHING */
+ register STDCHAR *ptr; /* in the innermost loop into registers */
+ register char newline = '\n'; /* (assuming at least 6 registers) */
+ int i;
+ int bpx;
+
+ cnt = fp->_cnt; /* get count into register */
+ str->str_nok = 0; /* invalidate number */
+ str->str_pok = 1; /* validate pointer */
+ if (str->str_len <= cnt) /* make sure we have the room */
+ GROWSTR(&(str->str_ptr), &(str->str_len), cnt+1);
+ bp = str->str_ptr; /* move these two too to registers */
+ ptr = fp->_ptr;
+ for (;;) {
+ while (--cnt >= 0) {
+ if ((*bp++ = *ptr++) == newline)
+ if (bp <= str->str_ptr || bp[-2] != '\\')
+ goto thats_all_folks;
+ else {
+ line++;
+ bp -= 2;
+ }
+ }
+
+ fp->_cnt = cnt; /* deregisterize cnt and ptr */
+ fp->_ptr = ptr;
+ i = _filbuf(fp); /* get more characters */
+ cnt = fp->_cnt;
+ ptr = fp->_ptr; /* reregisterize cnt and ptr */
+
+ bpx = bp - str->str_ptr; /* prepare for possible relocation */
+ GROWSTR(&(str->str_ptr), &(str->str_len), str->str_cur + cnt + 1);
+ bp = str->str_ptr + bpx; /* reconstitute our pointer */
+
+ if (i == newline) { /* all done for now? */
+ *bp++ = i;
+ goto thats_all_folks;
+ }
+ else if (i == EOF) /* all done for ever? */
+ goto thats_all_folks;
+ *bp++ = i; /* now go back to screaming loop */
+ }
+
+thats_all_folks:
+ fp->_cnt = cnt; /* put these back or we're in trouble */
+ fp->_ptr = ptr;
+ *bp = '\0';
+ str->str_cur = bp - str->str_ptr; /* set length */
+
+#else /* !STDSTDIO */ /* The big, slow, and stupid way */
+
+ static char buf[4192];
+
+ if (fgets(buf, sizeof buf, fp) != Nullch)
+ str_set(str, buf);
+ else
+ str_set(str, No);
+
+#endif /* STDSTDIO */
+
+ return str->str_cur ? str->str_ptr : Nullch;
+}
+
+void
+str_inc(str)
+register STR *str;
+{
+ register char *d;
+
+ if (!str)
+ return;
+ if (str->str_nok) {
+ str->str_nval += 1.0;
+ str->str_pok = 0;
+ return;
+ }
+ if (!str->str_pok) {
+ str->str_nval = 1.0;
+ str->str_nok = 1;
+ return;
+ }
+ for (d = str->str_ptr; *d && *d != '.'; d++) ;
+ d--;
+ if (!isdigit(*str->str_ptr) || !isdigit(*d) ) {
+ str_numset(str,atof(str->str_ptr) + 1.0); /* punt */
+ return;
+ }
+ while (d >= str->str_ptr) {
+ if (++*d <= '9')
+ return;
+ *(d--) = '0';
+ }
+ /* oh,oh, the number grew */
+ GROWSTR(&(str->str_ptr), &(str->str_len), str->str_cur + 2);
+ str->str_cur++;
+ for (d = str->str_ptr + str->str_cur; d > str->str_ptr; d--)
+ *d = d[-1];
+ *d = '1';
+}
+
+void
+str_dec(str)
+register STR *str;
+{
+ register char *d;
+
+ if (!str)
+ return;
+ if (str->str_nok) {
+ str->str_nval -= 1.0;
+ str->str_pok = 0;
+ return;
+ }
+ if (!str->str_pok) {
+ str->str_nval = -1.0;
+ str->str_nok = 1;
+ return;
+ }
+ for (d = str->str_ptr; *d && *d != '.'; d++) ;
+ d--;
+ if (!isdigit(*str->str_ptr) || !isdigit(*d) || (*d == '0' && d == str->str_ptr)) {
+ str_numset(str,atof(str->str_ptr) - 1.0); /* punt */
+ return;
+ }
+ while (d >= str->str_ptr) {
+ if (--*d >= '0')
+ return;
+ *(d--) = '9';
+ }
+}
+
+/* make a string that will exist for the duration of the expression eval */
+
+STR *
+str_mortal(oldstr)
+STR *oldstr;
+{
+ register STR *str = str_new(0);
+ static long tmps_size = -1;
+
+ str_sset(str,oldstr);
+ if (++tmps_max > tmps_size) {
+ tmps_size = tmps_max;
+ if (!(tmps_size & 127)) {
+ if (tmps_size)
+ tmps_list = (STR**)saferealloc((char*)tmps_list,
+ (tmps_size + 128) * sizeof(STR*) );
+ else
+ tmps_list = (STR**)safemalloc(128 * sizeof(char*));
+ }
+ }
+ tmps_list[tmps_max] = str;
+ return str;
+}
+
+STR *
+str_make(s)
+char *s;
+{
+ register STR *str = str_new(0);
+
+ str_set(str,s);
+ return str;
+}
+
+STR *
+str_nmake(n)
+double n;
+{
+ register STR *str = str_new(0);
+
+ str_numset(str,n);
+ return str;
+}
diff --git a/gnu/usr.bin/perl/x2p/str.h b/gnu/usr.bin/perl/x2p/str.h
new file mode 100644
index 0000000..765d9ea
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/str.h
@@ -0,0 +1,46 @@
+/* $RCSfile: str.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:10 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: str.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:30:10 nate
+ * PERL!
+ *
+ * Revision 4.0.1.1 91/06/07 12:20:22 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:58:21 lwall
+ * 4.0 baseline.
+ *
+ */
+
+struct string {
+ char * str_ptr; /* pointer to malloced string */
+ double str_nval; /* numeric value, if any */
+ int str_len; /* allocated size */
+ int str_cur; /* length of str_ptr as a C string */
+ union {
+ STR *str_next; /* while free, link to next free str */
+ } str_link;
+ char str_pok; /* state of str_ptr */
+ char str_nok; /* state of str_nval */
+};
+
+#define Nullstr Null(STR*)
+
+/* the following macro updates any magic values this str is associated with */
+
+#define STABSET(x) (x->str_link.str_magic && stabset(x->str_link.str_magic,x))
+
+EXT STR **tmps_list;
+EXT long tmps_max INIT(-1);
+
+char *str_2ptr();
+double str_2num();
+STR *str_mortal();
+STR *str_make();
+STR *str_nmake();
+char *str_gets();
diff --git a/gnu/usr.bin/perl/x2p/util.c b/gnu/usr.bin/perl/x2p/util.c
new file mode 100644
index 0000000..4c5b078
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/util.c
@@ -0,0 +1,268 @@
+/* $RCSfile: util.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:10 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: util.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:30:10 nate
+ * PERL!
+ *
+ * Revision 4.0.1.1 91/06/07 12:20:35 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:58:25 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include <stdio.h>
+
+#include "handy.h"
+#include "EXTERN.h"
+#include "a2p.h"
+#include "INTERN.h"
+#include "util.h"
+
+#define FLUSH
+#define MEM_SIZE unsigned int
+
+static char nomem[] = "Out of memory!\n";
+
+/* paranoid version of malloc */
+
+static int an = 0;
+
+char *
+safemalloc(size)
+MEM_SIZE size;
+{
+ char *ptr;
+ char *malloc();
+
+ ptr = malloc(size?size:1); /* malloc(0) is NASTY on our system */
+#ifdef DEBUGGING
+ if (debug & 128)
+ fprintf(stderr,"0x%x: (%05d) malloc %d bytes\n",ptr,an++,size);
+#endif
+ if (ptr != Nullch)
+ return ptr;
+ else {
+ fputs(nomem,stdout) FLUSH;
+ exit(1);
+ }
+ /*NOTREACHED*/
+}
+
+/* paranoid version of realloc */
+
+char *
+saferealloc(where,size)
+char *where;
+MEM_SIZE size;
+{
+ char *ptr;
+ char *realloc();
+
+ ptr = realloc(where,size?size:1); /* realloc(0) is NASTY on our system */
+#ifdef DEBUGGING
+ if (debug & 128) {
+ fprintf(stderr,"0x%x: (%05d) rfree\n",where,an++);
+ fprintf(stderr,"0x%x: (%05d) realloc %d bytes\n",ptr,an++,size);
+ }
+#endif
+ if (ptr != Nullch)
+ return ptr;
+ else {
+ fputs(nomem,stdout) FLUSH;
+ exit(1);
+ }
+ /*NOTREACHED*/
+}
+
+/* safe version of free */
+
+safefree(where)
+char *where;
+{
+#ifdef DEBUGGING
+ if (debug & 128)
+ fprintf(stderr,"0x%x: (%05d) free\n",where,an++);
+#endif
+ free(where);
+}
+
+/* safe version of string copy */
+
+char *
+safecpy(to,from,len)
+char *to;
+register char *from;
+register int len;
+{
+ register char *dest = to;
+
+ if (from != Nullch)
+ for (len--; len && (*dest++ = *from++); len--) ;
+ *dest = '\0';
+ return to;
+}
+
+/* copy a string up to some (non-backslashed) delimiter, if any */
+
+char *
+cpytill(to,from,delim)
+register char *to, *from;
+register int delim;
+{
+ for (; *from; from++,to++) {
+ if (*from == '\\') {
+ if (from[1] == delim)
+ from++;
+ else if (from[1] == '\\')
+ *to++ = *from++;
+ }
+ else if (*from == delim)
+ break;
+ *to = *from;
+ }
+ *to = '\0';
+ return from;
+}
+
+
+char *
+cpy2(to,from,delim)
+register char *to, *from;
+register int delim;
+{
+ for (; *from; from++,to++) {
+ if (*from == '\\')
+ *to++ = *from++;
+ else if (*from == '$')
+ *to++ = '\\';
+ else if (*from == delim)
+ break;
+ *to = *from;
+ }
+ *to = '\0';
+ return from;
+}
+
+/* return ptr to little string in big string, NULL if not found */
+
+char *
+instr(big, little)
+char *big, *little;
+
+{
+ register char *t, *s, *x;
+
+ for (t = big; *t; t++) {
+ for (x=t,s=little; *s; x++,s++) {
+ if (!*x)
+ return Nullch;
+ if (*s != *x)
+ break;
+ }
+ if (!*s)
+ return t;
+ }
+ return Nullch;
+}
+
+/* copy a string to a safe spot */
+
+char *
+savestr(str)
+char *str;
+{
+ register char *newaddr = safemalloc((MEM_SIZE)(strlen(str)+1));
+
+ (void)strcpy(newaddr,str);
+ return newaddr;
+}
+
+/* grow a static string to at least a certain length */
+
+void
+growstr(strptr,curlen,newlen)
+char **strptr;
+int *curlen;
+int newlen;
+{
+ if (newlen > *curlen) { /* need more room? */
+ if (*curlen)
+ *strptr = saferealloc(*strptr,(MEM_SIZE)newlen);
+ else
+ *strptr = safemalloc((MEM_SIZE)newlen);
+ *curlen = newlen;
+ }
+}
+
+/*VARARGS1*/
+fatal(pat,a1,a2,a3,a4)
+char *pat;
+{
+ fprintf(stderr,pat,a1,a2,a3,a4);
+ exit(1);
+}
+
+/*VARARGS1*/
+warn(pat,a1,a2,a3,a4)
+char *pat;
+{
+ fprintf(stderr,pat,a1,a2,a3,a4);
+}
+
+static bool firstsetenv = TRUE;
+extern char **environ;
+
+void
+setenv(nam,val)
+char *nam, *val;
+{
+ register int i=envix(nam); /* where does it go? */
+
+ if (!environ[i]) { /* does not exist yet */
+ if (firstsetenv) { /* need we copy environment? */
+ int j;
+#ifndef lint
+ char **tmpenv = (char**) /* point our wand at memory */
+ safemalloc((i+2) * sizeof(char*));
+#else
+ char **tmpenv = Null(char **);
+#endif /* lint */
+
+ firstsetenv = FALSE;
+ for (j=0; j<i; j++) /* copy environment */
+ tmpenv[j] = environ[j];
+ environ = tmpenv; /* tell exec where it is now */
+ }
+#ifndef lint
+ else
+ environ = (char**) saferealloc((char*) environ,
+ (i+2) * sizeof(char*));
+ /* just expand it a bit */
+#endif /* lint */
+ environ[i+1] = Nullch; /* make sure it's null terminated */
+ }
+ environ[i] = safemalloc(strlen(nam) + strlen(val) + 2);
+ /* this may or may not be in */
+ /* the old environ structure */
+ sprintf(environ[i],"%s=%s",nam,val);/* all that work just for this */
+}
+
+int
+envix(nam)
+char *nam;
+{
+ register int i, len = strlen(nam);
+
+ for (i = 0; environ[i]; i++) {
+ if (strnEQ(environ[i],nam,len) && environ[i][len] == '=')
+ break; /* strnEQ must come first to avoid */
+ } /* potential SEGV's */
+ return i;
+}
diff --git a/gnu/usr.bin/perl/x2p/util.h b/gnu/usr.bin/perl/x2p/util.h
new file mode 100644
index 0000000..15bf78e5
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/util.h
@@ -0,0 +1,53 @@
+/* $RCSfile: util.h,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:10 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: util.h,v $
+ * Revision 1.1.1.1 1993/08/23 21:30:10 nate
+ * PERL!
+ *
+ * Revision 4.0.1.2 91/11/05 19:21:20 lwall
+ * patch11: various portability fixes
+ *
+ * Revision 4.0.1.1 91/06/07 12:20:43 lwall
+ * patch4: new copyright notice
+ *
+ * Revision 4.0 91/03/20 01:58:29 lwall
+ * 4.0 baseline.
+ *
+ */
+
+/* is the string for makedir a directory name or a filename? */
+
+#define fatal Myfatal
+
+#define MD_DIR 0
+#define MD_FILE 1
+
+void util_init();
+int doshell();
+char *safemalloc();
+char *saferealloc();
+char *safecpy();
+char *safecat();
+char *cpytill();
+char *cpy2();
+char *instr();
+#ifdef SETUIDGID
+ int eaccess();
+#endif
+char *getwd();
+void cat();
+void prexit();
+char *get_a_line();
+char *savestr();
+int makedir();
+void setenv();
+int envix();
+void notincl();
+char *getval();
+void growstr();
+void setdef();
diff --git a/gnu/usr.bin/perl/x2p/walk.c b/gnu/usr.bin/perl/x2p/walk.c
new file mode 100644
index 0000000..4cc4a79
--- /dev/null
+++ b/gnu/usr.bin/perl/x2p/walk.c
@@ -0,0 +1,2086 @@
+/* $RCSfile: walk.c,v $$Revision: 1.1.1.1 $$Date: 1993/08/23 21:30:11 $
+ *
+ * Copyright (c) 1991, Larry Wall
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file.
+ *
+ * $Log: walk.c,v $
+ * Revision 1.1.1.1 1993/08/23 21:30:11 nate
+ * PERL!
+ *
+ * Revision 4.0.1.3 92/06/08 17:33:46 lwall
+ * patch20: in a2p, simplified the filehandle model
+ * patch20: in a2p, made RS="" translate to $/ = "\n\n"
+ * patch20: in a2p, do {...} while ... was missing some reconstruction code
+ * patch20: in a2p, getline should allow variable to be array element
+ *
+ * Revision 4.0.1.2 91/11/05 19:25:09 lwall
+ * patch11: in a2p, split on whitespace produced extra null field
+ *
+ * Revision 4.0.1.1 91/06/07 12:22:04 lwall
+ * patch4: new copyright notice
+ * patch4: a2p didn't correctly implement -n switch
+ *
+ * Revision 4.0 91/03/20 01:58:36 lwall
+ * 4.0 baseline.
+ *
+ */
+
+#include "handy.h"
+#include "EXTERN.h"
+#include "util.h"
+#include "a2p.h"
+
+bool exitval = FALSE;
+bool realexit = FALSE;
+bool saw_getline = FALSE;
+bool subretnum = FALSE;
+bool saw_FNR = FALSE;
+bool saw_argv0 = FALSE;
+bool saw_fh = FALSE;
+int maxtmp = 0;
+char *lparen;
+char *rparen;
+char *limit;
+STR *subs;
+STR *curargs = Nullstr;
+
+STR *
+walk(useval,level,node,numericptr,minprec)
+int useval;
+int level;
+register int node;
+int *numericptr;
+int minprec; /* minimum precedence without parens */
+{
+ register int len;
+ register STR *str;
+ register int type;
+ register int i;
+ register STR *tmpstr;
+ STR *tmp2str;
+ STR *tmp3str;
+ char *t;
+ char *d, *s;
+ int numarg;
+ int numeric = FALSE;
+ STR *fstr;
+ int prec = P_MAX; /* assume no parens needed */
+ char *index();
+
+ if (!node) {
+ *numericptr = 0;
+ return str_make("");
+ }
+ type = ops[node].ival;
+ len = type >> 8;
+ type &= 255;
+ switch (type) {
+ case OPROG:
+ arymax = 0;
+ if (namelist) {
+ while (isalpha(*namelist)) {
+ for (d = tokenbuf,s=namelist;
+ isalpha(*s) || isdigit(*s) || *s == '_';
+ *d++ = *s++) ;
+ *d = '\0';
+ while (*s && !isalpha(*s)) s++;
+ namelist = s;
+ nameary[++arymax] = savestr(tokenbuf);
+ }
+ }
+ if (maxfld < arymax)
+ maxfld = arymax;
+ opens = str_new(0);
+ subs = str_new(0);
+ str = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
+ if (do_split && need_entire && !absmaxfld)
+ split_to_array = TRUE;
+ if (do_split && split_to_array)
+ set_array_base = TRUE;
+ if (set_array_base) {
+ str_cat(str,"$[ = 1;\t\t\t# set array base to 1\n");
+ }
+ if (fswitch && !const_FS)
+ const_FS = fswitch;
+ if (saw_FS > 1 || saw_RS)
+ const_FS = 0;
+ if (saw_ORS && need_entire)
+ do_chop = TRUE;
+ if (fswitch) {
+ str_cat(str,"$FS = '");
+ if (index("*+?.[]()|^$\\",fswitch))
+ str_cat(str,"\\");
+ sprintf(tokenbuf,"%c",fswitch);
+ str_cat(str,tokenbuf);
+ str_cat(str,"';\t\t# field separator from -F switch\n");
+ }
+ else if (saw_FS && !const_FS) {
+ str_cat(str,"$FS = ' ';\t\t# set field separator\n");
+ }
+ if (saw_OFS) {
+ str_cat(str,"$, = ' ';\t\t# set output field separator\n");
+ }
+ if (saw_ORS) {
+ str_cat(str,"$\\ = \"\\n\";\t\t# set output record separator\n");
+ }
+ if (saw_argv0) {
+ str_cat(str,"$ARGV0 = $0;\t\t# remember what we ran as\n");
+ }
+ if (str->str_cur > 20)
+ str_cat(str,"\n");
+ if (ops[node+2].ival) {
+ str_scat(str,fstr=walk(0,level,ops[node+2].ival,&numarg,P_MIN));
+ str_free(fstr);
+ str_cat(str,"\n\n");
+ }
+ fstr = walk(0,level+1,ops[node+3].ival,&numarg,P_MIN);
+ if (*fstr->str_ptr) {
+ if (saw_line_op)
+ str_cat(str,"line: ");
+ str_cat(str,"while (<>) {\n");
+ tab(str,++level);
+ if (saw_FS && !const_FS)
+ do_chop = TRUE;
+ if (do_chop) {
+ str_cat(str,"chop;\t# strip record separator\n");
+ tab(str,level);
+ }
+ if (do_split)
+ emit_split(str,level);
+ str_scat(str,fstr);
+ str_free(fstr);
+ fixtab(str,--level);
+ str_cat(str,"}\n");
+ if (saw_FNR)
+ str_cat(str,"continue {\n $FNRbase = $. if eof;\n}\n");
+ }
+ else
+ str_cat(str,"while (<>) { } # (no line actions)\n");
+ if (ops[node+4].ival) {
+ realexit = TRUE;
+ str_cat(str,"\n");
+ tab(str,level);
+ str_scat(str,fstr=walk(0,level,ops[node+4].ival,&numarg,P_MIN));
+ str_free(fstr);
+ str_cat(str,"\n");
+ }
+ if (exitval)
+ str_cat(str,"exit $ExitValue;\n");
+ if (subs->str_ptr) {
+ str_cat(str,"\n");
+ str_scat(str,subs);
+ }
+ if (saw_getline) {
+ for (len = 0; len < 4; len++) {
+ if (saw_getline & (1 << len)) {
+ sprintf(tokenbuf,"\nsub Getline%d {\n",len);
+ str_cat(str, tokenbuf);
+ if (len & 2) {
+ if (do_fancy_opens)
+ str_cat(str," &Pick('',@_);\n");
+ else
+ str_cat(str," ($fh) = @_;\n");
+ }
+ else {
+ if (saw_FNR)
+ str_cat(str," $FNRbase = $. if eof;\n");
+ }
+ if (len & 1)
+ str_cat(str," local($_);\n");
+ if (len & 2)
+ str_cat(str,
+ " if ($getline_ok = (($_ = <$fh>) ne ''))");
+ else
+ str_cat(str,
+ " if ($getline_ok = (($_ = <>) ne ''))");
+ str_cat(str, " {\n");
+ level += 2;
+ tab(str,level);
+ i = 0;
+ if (do_chop) {
+ i++;
+ str_cat(str,"chop;\t# strip record separator\n");
+ tab(str,level);
+ }
+ if (do_split && !(len & 1)) {
+ i++;
+ emit_split(str,level);
+ }
+ if (!i)
+ str_cat(str,";\n");
+ fixtab(str,--level);
+ str_cat(str,"}\n $_;\n}\n");
+ --level;
+ }
+ }
+ }
+ if (do_fancy_opens) {
+ str_cat(str,"\n\
+sub Pick {\n\
+ local($mode,$name,$pipe) = @_;\n\
+ $fh = $name;\n\
+ open($name,$mode.$name.$pipe) unless $opened{$name}++;\n\
+}\n\
+");
+ }
+ break;
+ case OHUNKS:
+ str = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
+ str_scat(str,fstr=walk(0,level,ops[node+2].ival,&numarg,P_MIN));
+ str_free(fstr);
+ if (len == 3) {
+ str_scat(str,fstr=walk(0,level,ops[node+3].ival,&numarg,P_MIN));
+ str_free(fstr);
+ }
+ else {
+ }
+ break;
+ case ORANGE:
+ prec = P_DOTDOT;
+ str = walk(1,level,ops[node+1].ival,&numarg,prec+1);
+ str_cat(str," .. ");
+ str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
+ str_free(fstr);
+ break;
+ case OPAT:
+ goto def;
+ case OREGEX:
+ str = str_new(0);
+ str_set(str,"/");
+ tmpstr=walk(0,level,ops[node+1].ival,&numarg,P_MIN);
+ /* translate \nnn to [\nnn] */
+ for (s = tmpstr->str_ptr, d = tokenbuf; *s; s++, d++) {
+ if (*s == '\\' && isdigit(s[1]) && isdigit(s[2]) && isdigit(s[3])){
+ *d++ = '[';
+ *d++ = *s++;
+ *d++ = *s++;
+ *d++ = *s++;
+ *d++ = *s;
+ *d = ']';
+ }
+ else
+ *d = *s;
+ }
+ *d = '\0';
+ for (d=tokenbuf; *d; d++)
+ *d += 128;
+ str_cat(str,tokenbuf);
+ str_free(tmpstr);
+ str_cat(str,"/");
+ break;
+ case OHUNK:
+ if (len == 1) {
+ str = str_new(0);
+ str = walk(0,level,oper1(OPRINT,0),&numarg,P_MIN);
+ str_cat(str," if ");
+ str_scat(str,fstr=walk(0,level,ops[node+1].ival,&numarg,P_MIN));
+ str_free(fstr);
+ str_cat(str,";");
+ }
+ else {
+ tmpstr = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
+ if (*tmpstr->str_ptr) {
+ str = str_new(0);
+ str_set(str,"if (");
+ str_scat(str,tmpstr);
+ str_cat(str,") {\n");
+ tab(str,++level);
+ str_scat(str,fstr=walk(0,level,ops[node+2].ival,&numarg,P_MIN));
+ str_free(fstr);
+ fixtab(str,--level);
+ str_cat(str,"}\n");
+ tab(str,level);
+ }
+ else {
+ str = walk(0,level,ops[node+2].ival,&numarg,P_MIN);
+ }
+ }
+ break;
+ case OPPAREN:
+ str = str_new(0);
+ str_set(str,"(");
+ str_scat(str,fstr=walk(useval != 0,level,ops[node+1].ival,&numarg,P_MIN));
+ str_free(fstr);
+ str_cat(str,")");
+ break;
+ case OPANDAND:
+ prec = P_ANDAND;
+ str = walk(1,level,ops[node+1].ival,&numarg,prec);
+ str_cat(str," && ");
+ str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
+ str_free(fstr);
+ str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,prec+1));
+ str_free(fstr);
+ break;
+ case OPOROR:
+ prec = P_OROR;
+ str = walk(1,level,ops[node+1].ival,&numarg,prec);
+ str_cat(str," || ");
+ str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
+ str_free(fstr);
+ str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,prec+1));
+ str_free(fstr);
+ break;
+ case OPNOT:
+ prec = P_UNARY;
+ str = str_new(0);
+ str_set(str,"!");
+ str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,prec));
+ str_free(fstr);
+ break;
+ case OCOND:
+ prec = P_COND;
+ str = walk(1,level,ops[node+1].ival,&numarg,prec);
+ str_cat(str," ? ");
+ str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
+ str_free(fstr);
+ str_cat(str," : ");
+ str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,prec+1));
+ str_free(fstr);
+ break;
+ case OCPAREN:
+ str = str_new(0);
+ str_set(str,"(");
+ str_scat(str,fstr=walk(useval != 0,level,ops[node+1].ival,&numarg,P_MIN));
+ str_free(fstr);
+ numeric |= numarg;
+ str_cat(str,")");
+ break;
+ case OCANDAND:
+ prec = P_ANDAND;
+ str = walk(1,level,ops[node+1].ival,&numarg,prec);
+ numeric = 1;
+ str_cat(str," && ");
+ str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
+ str_free(fstr);
+ str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,prec+1));
+ str_free(fstr);
+ break;
+ case OCOROR:
+ prec = P_OROR;
+ str = walk(1,level,ops[node+1].ival,&numarg,prec);
+ numeric = 1;
+ str_cat(str," || ");
+ str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
+ str_free(fstr);
+ str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,prec+1));
+ str_free(fstr);
+ break;
+ case OCNOT:
+ prec = P_UNARY;
+ str = str_new(0);
+ str_set(str,"!");
+ str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,prec));
+ str_free(fstr);
+ numeric = 1;
+ break;
+ case ORELOP:
+ prec = P_REL;
+ str = walk(1,level,ops[node+2].ival,&numarg,prec+1);
+ numeric |= numarg;
+ tmpstr = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
+ tmp2str = walk(1,level,ops[node+3].ival,&numarg,prec+1);
+ numeric |= numarg;
+ if (!numeric ||
+ (!numarg && (*tmp2str->str_ptr == '"' || *tmp2str->str_ptr == '\''))) {
+ t = tmpstr->str_ptr;
+ if (strEQ(t,"=="))
+ str_set(tmpstr,"eq");
+ else if (strEQ(t,"!="))
+ str_set(tmpstr,"ne");
+ else if (strEQ(t,"<"))
+ str_set(tmpstr,"lt");
+ else if (strEQ(t,"<="))
+ str_set(tmpstr,"le");
+ else if (strEQ(t,">"))
+ str_set(tmpstr,"gt");
+ else if (strEQ(t,">="))
+ str_set(tmpstr,"ge");
+ if (!index(tmpstr->str_ptr,'\'') && !index(tmpstr->str_ptr,'"') &&
+ !index(tmp2str->str_ptr,'\'') && !index(tmp2str->str_ptr,'"') )
+ numeric |= 2;
+ }
+ if (numeric & 2) {
+ if (numeric & 1) /* numeric is very good guess */
+ str_cat(str," ");
+ else
+ str_cat(str,"\377");
+ numeric = 1;
+ }
+ else
+ str_cat(str," ");
+ str_scat(str,tmpstr);
+ str_free(tmpstr);
+ str_cat(str," ");
+ str_scat(str,tmp2str);
+ str_free(tmp2str);
+ numeric = 1;
+ break;
+ case ORPAREN:
+ str = str_new(0);
+ str_set(str,"(");
+ str_scat(str,fstr=walk(useval != 0,level,ops[node+1].ival,&numarg,P_MIN));
+ str_free(fstr);
+ numeric |= numarg;
+ str_cat(str,")");
+ break;
+ case OMATCHOP:
+ prec = P_MATCH;
+ str = walk(1,level,ops[node+2].ival,&numarg,prec+1);
+ str_cat(str," ");
+ tmpstr = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
+ if (strEQ(tmpstr->str_ptr,"~"))
+ str_cat(str,"=~");
+ else {
+ str_scat(str,tmpstr);
+ str_free(tmpstr);
+ }
+ str_cat(str," ");
+ str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,prec+1));
+ str_free(fstr);
+ numeric = 1;
+ break;
+ case OMPAREN:
+ str = str_new(0);
+ str_set(str,"(");
+ str_scat(str,
+ fstr=walk(useval != 0,level,ops[node+1].ival,&numarg,P_MIN));
+ str_free(fstr);
+ numeric |= numarg;
+ str_cat(str,")");
+ break;
+ case OCONCAT:
+ prec = P_ADD;
+ type = ops[ops[node+1].ival].ival & 255;
+ str = walk(1,level,ops[node+1].ival,&numarg,prec+(type != OCONCAT));
+ str_cat(str," . ");
+ type = ops[ops[node+2].ival].ival & 255;
+ str_scat(str,
+ fstr=walk(1,level,ops[node+2].ival,&numarg,prec+(type != OCONCAT)));
+ str_free(fstr);
+ break;
+ case OASSIGN:
+ prec = P_ASSIGN;
+ str = walk(0,level,ops[node+2].ival,&numarg,prec+1);
+ str_cat(str," ");
+ tmpstr = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
+ str_scat(str,tmpstr);
+ if (str_len(tmpstr) > 1)
+ numeric = 1;
+ str_free(tmpstr);
+ str_cat(str," ");
+ str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,prec));
+ str_free(fstr);
+ numeric |= numarg;
+ if (strEQ(str->str_ptr,"$/ = ''"))
+ str_set(str, "$/ = \"\\n\\n\"");
+ break;
+ case OADD:
+ prec = P_ADD;
+ str = walk(1,level,ops[node+1].ival,&numarg,prec);
+ str_cat(str," + ");
+ str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
+ str_free(fstr);
+ numeric = 1;
+ break;
+ case OSUBTRACT:
+ prec = P_ADD;
+ str = walk(1,level,ops[node+1].ival,&numarg,prec);
+ str_cat(str," - ");
+ str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
+ str_free(fstr);
+ numeric = 1;
+ break;
+ case OMULT:
+ prec = P_MUL;
+ str = walk(1,level,ops[node+1].ival,&numarg,prec);
+ str_cat(str," * ");
+ str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
+ str_free(fstr);
+ numeric = 1;
+ break;
+ case ODIV:
+ prec = P_MUL;
+ str = walk(1,level,ops[node+1].ival,&numarg,prec);
+ str_cat(str," / ");
+ str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
+ str_free(fstr);
+ numeric = 1;
+ break;
+ case OPOW:
+ prec = P_POW;
+ str = walk(1,level,ops[node+1].ival,&numarg,prec+1);
+ str_cat(str," ** ");
+ str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec));
+ str_free(fstr);
+ numeric = 1;
+ break;
+ case OMOD:
+ prec = P_MUL;
+ str = walk(1,level,ops[node+1].ival,&numarg,prec);
+ str_cat(str," % ");
+ str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,prec+1));
+ str_free(fstr);
+ numeric = 1;
+ break;
+ case OPOSTINCR:
+ prec = P_AUTO;
+ str = walk(1,level,ops[node+1].ival,&numarg,prec+1);
+ str_cat(str,"++");
+ numeric = 1;
+ break;
+ case OPOSTDECR:
+ prec = P_AUTO;
+ str = walk(1,level,ops[node+1].ival,&numarg,prec+1);
+ str_cat(str,"--");
+ numeric = 1;
+ break;
+ case OPREINCR:
+ prec = P_AUTO;
+ str = str_new(0);
+ str_set(str,"++");
+ str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,prec+1));
+ str_free(fstr);
+ numeric = 1;
+ break;
+ case OPREDECR:
+ prec = P_AUTO;
+ str = str_new(0);
+ str_set(str,"--");
+ str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,prec+1));
+ str_free(fstr);
+ numeric = 1;
+ break;
+ case OUMINUS:
+ prec = P_UNARY;
+ str = str_new(0);
+ str_set(str,"-");
+ str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,prec));
+ str_free(fstr);
+ numeric = 1;
+ break;
+ case OUPLUS:
+ numeric = 1;
+ goto def;
+ case OPAREN:
+ str = str_new(0);
+ str_set(str,"(");
+ str_scat(str,
+ fstr=walk(useval != 0,level,ops[node+1].ival,&numarg,P_MIN));
+ str_free(fstr);
+ str_cat(str,")");
+ numeric |= numarg;
+ break;
+ case OGETLINE:
+ str = str_new(0);
+ if (useval)
+ str_cat(str,"(");
+ if (len > 0) {
+ str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
+ if (!*fstr->str_ptr) {
+ str_cat(str,"$_");
+ len = 2; /* a legal fiction */
+ }
+ str_free(fstr);
+ }
+ else
+ str_cat(str,"$_");
+ if (len > 1) {
+ tmpstr=walk(1,level,ops[node+3].ival,&numarg,P_MIN);
+ fstr=walk(1,level,ops[node+2].ival,&numarg,P_MIN);
+ if (!do_fancy_opens) {
+ t = tmpstr->str_ptr;
+ if (*t == '"' || *t == '\'')
+ t = cpytill(tokenbuf,t+1,*t);
+ else
+ fatal("Internal error: OGETLINE %s", t);
+ d = savestr(t);
+ s = savestr(tokenbuf);
+ for (t = tokenbuf; *t; t++) {
+ *t &= 127;
+ if (islower(*t))
+ *t = toupper(*t);
+ if (!isalpha(*t) && !isdigit(*t))
+ *t = '_';
+ }
+ if (!index(tokenbuf,'_'))
+ strcpy(t,"_FH");
+ tmp3str = hfetch(symtab,tokenbuf);
+ if (!tmp3str) {
+ do_opens = TRUE;
+ str_cat(opens,"open(");
+ str_cat(opens,tokenbuf);
+ str_cat(opens,", ");
+ d[1] = '\0';
+ str_cat(opens,d);
+ str_cat(opens,tmpstr->str_ptr+1);
+ opens->str_cur--;
+ if (*fstr->str_ptr == '|')
+ str_cat(opens,"|");
+ str_cat(opens,d);
+ if (*fstr->str_ptr == '|')
+ str_cat(opens,") || die 'Cannot pipe from \"");
+ else
+ str_cat(opens,") || die 'Cannot open file \"");
+ if (*d == '"')
+ str_cat(opens,"'.\"");
+ str_cat(opens,s);
+ if (*d == '"')
+ str_cat(opens,"\".'");
+ str_cat(opens,"\".';\n");
+ hstore(symtab,tokenbuf,str_make("x"));
+ }
+ safefree(s);
+ safefree(d);
+ str_set(tmpstr,"'");
+ str_cat(tmpstr,tokenbuf);
+ str_cat(tmpstr,"'");
+ }
+ if (*fstr->str_ptr == '|')
+ str_cat(tmpstr,", '|'");
+ str_free(fstr);
+ }
+ else
+ tmpstr = str_make("");
+ sprintf(tokenbuf," = &Getline%d(%s)",len,tmpstr->str_ptr);
+ str_cat(str,tokenbuf);
+ str_free(tmpstr);
+ if (useval)
+ str_cat(str,",$getline_ok)");
+ saw_getline |= 1 << len;
+ break;
+ case OSPRINTF:
+ str = str_new(0);
+ str_set(str,"sprintf(");
+ str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
+ str_free(fstr);
+ str_cat(str,")");
+ break;
+ case OSUBSTR:
+ str = str_new(0);
+ str_set(str,"substr(");
+ str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_COMMA+1));
+ str_free(fstr);
+ str_cat(str,", ");
+ str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,P_COMMA+1));
+ str_free(fstr);
+ str_cat(str,", ");
+ if (len == 3) {
+ str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,P_COMMA+1));
+ str_free(fstr);
+ }
+ else
+ str_cat(str,"999999");
+ str_cat(str,")");
+ break;
+ case OSTRING:
+ str = str_new(0);
+ str_set(str,ops[node+1].cval);
+ break;
+ case OSPLIT:
+ str = str_new(0);
+ limit = ", 9999)";
+ numeric = 1;
+ tmpstr = walk(1,level,ops[node+2].ival,&numarg,P_MIN);
+ if (useval)
+ str_set(str,"(@");
+ else
+ str_set(str,"@");
+ str_scat(str,tmpstr);
+ str_cat(str," = split(");
+ if (len == 3) {
+ fstr = walk(1,level,ops[node+3].ival,&numarg,P_COMMA+1);
+ if (str_len(fstr) == 3 && *fstr->str_ptr == '\'') {
+ i = fstr->str_ptr[1] & 127;
+ if (index("*+?.[]()|^$\\",i))
+ sprintf(tokenbuf,"/\\%c/",i);
+ else if (i == ' ')
+ sprintf(tokenbuf,"' '");
+ else
+ sprintf(tokenbuf,"/%c/",i);
+ str_cat(str,tokenbuf);
+ }
+ else
+ str_scat(str,fstr);
+ str_free(fstr);
+ }
+ else if (const_FS) {
+ sprintf(tokenbuf,"/[%c\\n]/",const_FS);
+ str_cat(str,tokenbuf);
+ }
+ else if (saw_FS)
+ str_cat(str,"$FS");
+ else {
+ str_cat(str,"' '");
+ limit = ")";
+ }
+ str_cat(str,", ");
+ str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_COMMA+1));
+ str_free(fstr);
+ str_cat(str,limit);
+ if (useval) {
+ str_cat(str,")");
+ }
+ str_free(tmpstr);
+ break;
+ case OINDEX:
+ str = str_new(0);
+ str_set(str,"index(");
+ str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_COMMA+1));
+ str_free(fstr);
+ str_cat(str,", ");
+ str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,P_COMMA+1));
+ str_free(fstr);
+ str_cat(str,")");
+ numeric = 1;
+ break;
+ case OMATCH:
+ str = str_new(0);
+ prec = P_ANDAND;
+ str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_MATCH+1));
+ str_free(fstr);
+ str_cat(str," =~ ");
+ str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,P_MATCH+1));
+ str_free(fstr);
+ str_cat(str," && ($RLENGTH = length($&), $RSTART = length($`)+1)");
+ numeric = 1;
+ break;
+ case OUSERDEF:
+ str = str_new(0);
+ subretnum = FALSE;
+ fstr=walk(1,level-1,ops[node+2].ival,&numarg,P_MIN);
+ curargs = str_new(0);
+ str_sset(curargs,fstr);
+ str_cat(curargs,",");
+ tmp2str=walk(1,level,ops[node+5].ival,&numarg,P_MIN);
+ str_free(curargs);
+ curargs = Nullstr;
+ level--;
+ subretnum |= numarg;
+ s = Nullch;
+ t = tmp2str->str_ptr;
+ while (t = instr(t,"return "))
+ s = t++;
+ if (s) {
+ i = 0;
+ for (t = s+7; *t; t++) {
+ if (*t == ';' || *t == '}')
+ i++;
+ }
+ if (i == 1) {
+ strcpy(s,s+7);
+ tmp2str->str_cur -= 7;
+ }
+ }
+ str_set(str,"\n");
+ tab(str,level);
+ str_cat(str,"sub ");
+ str_scat(str,tmpstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
+ str_cat(str," {\n");
+ tab(str,++level);
+ if (fstr->str_cur) {
+ str_cat(str,"local(");
+ str_scat(str,fstr);
+ str_cat(str,") = @_;");
+ }
+ str_free(fstr);
+ str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,P_MIN));
+ str_free(fstr);
+ fixtab(str,level);
+ str_scat(str,fstr=walk(1,level,ops[node+4].ival,&numarg,P_MIN));
+ str_free(fstr);
+ fixtab(str,level);
+ str_scat(str,tmp2str);
+ str_free(tmp2str);
+ fixtab(str,--level);
+ str_cat(str,"}\n");
+ tab(str,level);
+ str_scat(subs,str);
+ str_set(str,"");
+ str_cat(tmpstr,"(");
+ tmp2str = str_new(0);
+ if (subretnum)
+ str_set(tmp2str,"1");
+ hstore(symtab,tmpstr->str_ptr,tmp2str);
+ str_free(tmpstr);
+ level++;
+ break;
+ case ORETURN:
+ str = str_new(0);
+ if (len > 0) {
+ str_cat(str,"return ");
+ str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_UNI+1));
+ str_free(fstr);
+ if (numarg)
+ subretnum = TRUE;
+ }
+ else
+ str_cat(str,"return");
+ break;
+ case OUSERFUN:
+ str = str_new(0);
+ str_set(str,"&");
+ str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
+ str_free(fstr);
+ str_cat(str,"(");
+ tmpstr = hfetch(symtab,str->str_ptr+3);
+ if (tmpstr && tmpstr->str_ptr)
+ numeric |= atoi(tmpstr->str_ptr);
+ str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,P_MIN));
+ str_free(fstr);
+ str_cat(str,")");
+ break;
+ case OGSUB:
+ case OSUB:
+ if (type == OGSUB)
+ s = "g";
+ else
+ s = "";
+ str = str_new(0);
+ tmpstr = str_new(0);
+ i = 0;
+ if (len == 3) {
+ tmpstr = walk(1,level,ops[node+3].ival,&numarg,P_MATCH+1);
+ if (strNE(tmpstr->str_ptr,"$_")) {
+ str_cat(tmpstr, " =~ s");
+ i++;
+ }
+ else
+ str_set(tmpstr, "s");
+ }
+ else
+ str_set(tmpstr, "s");
+ type = ops[ops[node+2].ival].ival;
+ len = type >> 8;
+ type &= 255;
+ tmp3str = str_new(0);
+ if (type == OSTR) {
+ tmp2str=walk(1,level,ops[ops[node+2].ival+1].ival,&numarg,P_MIN);
+ for (t = tmp2str->str_ptr, d=tokenbuf; *t; d++,t++) {
+ if (*t == '&')
+ *d++ = '$' + 128;
+ else if (*t == '$')
+ *d++ = '\\' + 128;
+ *d = *t + 128;
+ }
+ *d = '\0';
+ str_set(tmp2str,tokenbuf);
+ }
+ else {
+ tmp2str=walk(1,level,ops[node+2].ival,&numarg,P_MIN);
+ str_set(tmp3str,"($s_ = '\"'.(");
+ str_scat(tmp3str,tmp2str);
+ str_cat(tmp3str,").'\"') =~ s/&/\\$&/g, ");
+ str_set(tmp2str,"eval $s_");
+ s = (*s == 'g' ? "ge" : "e");
+ i++;
+ }
+ type = ops[ops[node+1].ival].ival;
+ len = type >> 8;
+ type &= 255;
+ fstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN);
+ if (type == OREGEX) {
+ if (useval && i)
+ str_cat(str,"(");
+ str_scat(str,tmp3str);
+ str_scat(str,tmpstr);
+ str_scat(str,fstr);
+ str_scat(str,tmp2str);
+ str_cat(str,"/");
+ str_cat(str,s);
+ }
+ else if ((type == OFLD && !split_to_array) || (type == OVAR && len == 1)) {
+ if (useval && i)
+ str_cat(str,"(");
+ str_scat(str,tmp3str);
+ str_scat(str,tmpstr);
+ str_cat(str,"/");
+ str_scat(str,fstr);
+ str_cat(str,"/");
+ str_scat(str,tmp2str);
+ str_cat(str,"/");
+ str_cat(str,s);
+ }
+ else {
+ i++;
+ if (useval)
+ str_cat(str,"(");
+ str_cat(str,"$s = ");
+ str_scat(str,fstr);
+ str_cat(str,", ");
+ str_scat(str,tmp3str);
+ str_scat(str,tmpstr);
+ str_cat(str,"/$s/");
+ str_scat(str,tmp2str);
+ str_cat(str,"/");
+ str_cat(str,s);
+ }
+ if (useval && i)
+ str_cat(str,")");
+ str_free(fstr);
+ str_free(tmpstr);
+ str_free(tmp2str);
+ str_free(tmp3str);
+ numeric = 1;
+ break;
+ case ONUM:
+ str = walk(1,level,ops[node+1].ival,&numarg,P_MIN);
+ numeric = 1;
+ break;
+ case OSTR:
+ tmpstr = walk(1,level,ops[node+1].ival,&numarg,P_MIN);
+ s = "'";
+ for (t = tmpstr->str_ptr, d=tokenbuf; *t; d++,t++) {
+ if (*t == '\'')
+ s = "\"";
+ else if (*t == '\\') {
+ s = "\"";
+ *d++ = *t++ + 128;
+ switch (*t) {
+ case '\\': case '"': case 'n': case 't': case '$':
+ break;
+ default: /* hide this from perl */
+ *d++ = '\\' + 128;
+ }
+ }
+ *d = *t + 128;
+ }
+ *d = '\0';
+ str = str_new(0);
+ str_set(str,s);
+ str_cat(str,tokenbuf);
+ str_free(tmpstr);
+ str_cat(str,s);
+ break;
+ case ODEFINED:
+ prec = P_UNI;
+ str = str_new(0);
+ str_set(str,"defined $");
+ goto addvar;
+ case ODELETE:
+ str = str_new(0);
+ str_set(str,"delete $");
+ goto addvar;
+ case OSTAR:
+ str = str_new(0);
+ str_set(str,"*");
+ goto addvar;
+ case OVAR:
+ str = str_new(0);
+ str_set(str,"$");
+ addvar:
+ str_scat(str,tmpstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
+ if (len == 1) {
+ tmp2str = hfetch(symtab,tmpstr->str_ptr);
+ if (tmp2str && atoi(tmp2str->str_ptr))
+ numeric = 2;
+ if (strEQ(str->str_ptr,"$FNR")) {
+ numeric = 1;
+ saw_FNR++;
+ str_set(str,"($.-$FNRbase)");
+ }
+ else if (strEQ(str->str_ptr,"$NR")) {
+ numeric = 1;
+ str_set(str,"$.");
+ }
+ else if (strEQ(str->str_ptr,"$NF")) {
+ numeric = 1;
+ str_set(str,"$#Fld");
+ }
+ else if (strEQ(str->str_ptr,"$0"))
+ str_set(str,"$_");
+ else if (strEQ(str->str_ptr,"$ARGC"))
+ str_set(str,"($#ARGV+1)");
+ }
+ else {
+#ifdef NOTDEF
+ if (curargs) {
+ sprintf(tokenbuf,"$%s,",tmpstr->str_ptr);
+ ??? if (instr(curargs->str_ptr,tokenbuf))
+ str_cat(str,"\377"); /* can't translate yet */
+ }
+#endif
+ str_cat(tmpstr,"[]");
+ tmp2str = hfetch(symtab,tmpstr->str_ptr);
+ if (tmp2str && atoi(tmp2str->str_ptr))
+ str_cat(str,"[");
+ else
+ str_cat(str,"{");
+ str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,P_MIN));
+ str_free(fstr);
+ if (strEQ(str->str_ptr,"$ARGV[0")) {
+ str_set(str,"$ARGV0");
+ saw_argv0++;
+ }
+ else {
+ if (tmp2str && atoi(tmp2str->str_ptr))
+ strcpy(tokenbuf,"]");
+ else
+ strcpy(tokenbuf,"}");
+ *tokenbuf += 128;
+ str_cat(str,tokenbuf);
+ }
+ }
+ str_free(tmpstr);
+ break;
+ case OFLD:
+ str = str_new(0);
+ if (split_to_array) {
+ str_set(str,"$Fld");
+ str_cat(str,"[");
+ str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
+ str_free(fstr);
+ str_cat(str,"]");
+ }
+ else {
+ i = atoi(walk(1,level,ops[node+1].ival,&numarg,P_MIN)->str_ptr);
+ if (i <= arymax)
+ sprintf(tokenbuf,"$%s",nameary[i]);
+ else
+ sprintf(tokenbuf,"$Fld%d",i);
+ str_set(str,tokenbuf);
+ }
+ break;
+ case OVFLD:
+ str = str_new(0);
+ str_set(str,"$Fld[");
+ i = ops[node+1].ival;
+ if ((ops[i].ival & 255) == OPAREN)
+ i = ops[i+1].ival;
+ tmpstr=walk(1,level,i,&numarg,P_MIN);
+ str_scat(str,tmpstr);
+ str_free(tmpstr);
+ str_cat(str,"]");
+ break;
+ case OJUNK:
+ goto def;
+ case OSNEWLINE:
+ str = str_new(2);
+ str_set(str,";\n");
+ tab(str,level);
+ break;
+ case ONEWLINE:
+ str = str_new(1);
+ str_set(str,"\n");
+ tab(str,level);
+ break;
+ case OSCOMMENT:
+ str = str_new(0);
+ str_set(str,";");
+ tmpstr = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
+ for (s = tmpstr->str_ptr; *s && *s != '\n'; s++)
+ *s += 128;
+ str_scat(str,tmpstr);
+ str_free(tmpstr);
+ tab(str,level);
+ break;
+ case OCOMMENT:
+ str = str_new(0);
+ tmpstr = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
+ for (s = tmpstr->str_ptr; *s && *s != '\n'; s++)
+ *s += 128;
+ str_scat(str,tmpstr);
+ str_free(tmpstr);
+ tab(str,level);
+ break;
+ case OCOMMA:
+ prec = P_COMMA;
+ str = walk(1,level,ops[node+1].ival,&numarg,prec);
+ str_cat(str,", ");
+ str_scat(str,fstr=walk(1,level,ops[node+2].ival,&numarg,P_MIN));
+ str_free(fstr);
+ str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,prec+1));
+ str_free(fstr);
+ break;
+ case OSEMICOLON:
+ str = str_new(1);
+ str_set(str,";\n");
+ tab(str,level);
+ break;
+ case OSTATES:
+ str = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
+ str_scat(str,fstr=walk(0,level,ops[node+2].ival,&numarg,P_MIN));
+ str_free(fstr);
+ break;
+ case OSTATE:
+ str = str_new(0);
+ if (len >= 1) {
+ str_scat(str,fstr=walk(0,level,ops[node+1].ival,&numarg,P_MIN));
+ str_free(fstr);
+ if (len >= 2) {
+ tmpstr = walk(0,level,ops[node+2].ival,&numarg,P_MIN);
+ if (*tmpstr->str_ptr == ';') {
+ addsemi(str);
+ str_cat(str,tmpstr->str_ptr+1);
+ }
+ str_free(tmpstr);
+ }
+ }
+ break;
+ case OCLOSE:
+ str = str_make("close(");
+ tmpstr = walk(1,level,ops[node+1].ival,&numarg,P_MIN);
+ if (!do_fancy_opens) {
+ t = tmpstr->str_ptr;
+ if (*t == '"' || *t == '\'')
+ t = cpytill(tokenbuf,t+1,*t);
+ else
+ fatal("Internal error: OCLOSE %s",t);
+ s = savestr(tokenbuf);
+ for (t = tokenbuf; *t; t++) {
+ *t &= 127;
+ if (islower(*t))
+ *t = toupper(*t);
+ if (!isalpha(*t) && !isdigit(*t))
+ *t = '_';
+ }
+ if (!index(tokenbuf,'_'))
+ strcpy(t,"_FH");
+ str_free(tmpstr);
+ safefree(s);
+ str_set(str,"close ");
+ str_cat(str,tokenbuf);
+ }
+ else {
+ sprintf(tokenbuf,"delete $opened{%s} && close(%s)",
+ tmpstr->str_ptr, tmpstr->str_ptr);
+ str_free(tmpstr);
+ str_set(str,tokenbuf);
+ }
+ break;
+ case OPRINTF:
+ case OPRINT:
+ lparen = ""; /* set to parens if necessary */
+ rparen = "";
+ str = str_new(0);
+ if (len == 3) { /* output redirection */
+ tmpstr = walk(1,level,ops[node+3].ival,&numarg,P_MIN);
+ tmp2str = walk(1,level,ops[node+2].ival,&numarg,P_MIN);
+ if (!do_fancy_opens) {
+ t = tmpstr->str_ptr;
+ if (*t == '"' || *t == '\'')
+ t = cpytill(tokenbuf,t+1,*t);
+ else
+ fatal("Internal error: OPRINT");
+ d = savestr(t);
+ s = savestr(tokenbuf);
+ for (t = tokenbuf; *t; t++) {
+ *t &= 127;
+ if (islower(*t))
+ *t = toupper(*t);
+ if (!isalpha(*t) && !isdigit(*t))
+ *t = '_';
+ }
+ if (!index(tokenbuf,'_'))
+ strcpy(t,"_FH");
+ tmp3str = hfetch(symtab,tokenbuf);
+ if (!tmp3str) {
+ str_cat(opens,"open(");
+ str_cat(opens,tokenbuf);
+ str_cat(opens,", ");
+ d[1] = '\0';
+ str_cat(opens,d);
+ str_scat(opens,tmp2str);
+ str_cat(opens,tmpstr->str_ptr+1);
+ if (*tmp2str->str_ptr == '|')
+ str_cat(opens,") || die 'Cannot pipe to \"");
+ else
+ str_cat(opens,") || die 'Cannot create file \"");
+ if (*d == '"')
+ str_cat(opens,"'.\"");
+ str_cat(opens,s);
+ if (*d == '"')
+ str_cat(opens,"\".'");
+ str_cat(opens,"\".';\n");
+ hstore(symtab,tokenbuf,str_make("x"));
+ }
+ str_free(tmpstr);
+ str_free(tmp2str);
+ safefree(s);
+ safefree(d);
+ }
+ else {
+ sprintf(tokenbuf,"&Pick('%s', %s) &&\n",
+ tmp2str->str_ptr, tmpstr->str_ptr);
+ str_cat(str,tokenbuf);
+ tab(str,level+1);
+ strcpy(tokenbuf,"$fh");
+ str_free(tmpstr);
+ str_free(tmp2str);
+ lparen = "(";
+ rparen = ")";
+ }
+ }
+ else
+ strcpy(tokenbuf,"");
+ str_cat(str,lparen); /* may be null */
+ if (type == OPRINTF)
+ str_cat(str,"printf");
+ else
+ str_cat(str,"print");
+ saw_fh = 0;
+ if (len == 3 || do_fancy_opens) {
+ if (*tokenbuf) {
+ str_cat(str," ");
+ saw_fh = 1;
+ }
+ str_cat(str,tokenbuf);
+ }
+ tmpstr = walk(1+(type==OPRINT),level,ops[node+1].ival,&numarg,P_MIN);
+ if (!*tmpstr->str_ptr && lval_field) {
+ t = saw_OFS ? "$," : "' '";
+ if (split_to_array) {
+ sprintf(tokenbuf,"join(%s,@Fld)",t);
+ str_cat(tmpstr,tokenbuf);
+ }
+ else {
+ for (i = 1; i < maxfld; i++) {
+ if (i <= arymax)
+ sprintf(tokenbuf,"$%s, ",nameary[i]);
+ else
+ sprintf(tokenbuf,"$Fld%d, ",i);
+ str_cat(tmpstr,tokenbuf);
+ }
+ if (maxfld <= arymax)
+ sprintf(tokenbuf,"$%s",nameary[maxfld]);
+ else
+ sprintf(tokenbuf,"$Fld%d",maxfld);
+ str_cat(tmpstr,tokenbuf);
+ }
+ }
+ if (*tmpstr->str_ptr) {
+ str_cat(str," ");
+ if (!saw_fh && *tmpstr->str_ptr == '(') {
+ str_cat(str,"(");
+ str_scat(str,tmpstr);
+ str_cat(str,")");
+ }
+ else
+ str_scat(str,tmpstr);
+ }
+ else {
+ str_cat(str," $_");
+ }
+ str_cat(str,rparen); /* may be null */
+ str_free(tmpstr);
+ break;
+ case ORAND:
+ str = str_make("rand(1)");
+ break;
+ case OSRAND:
+ str = str_make("srand(");
+ goto maybe0;
+ case OATAN2:
+ str = str_make("atan2(");
+ goto maybe0;
+ case OSIN:
+ str = str_make("sin(");
+ goto maybe0;
+ case OCOS:
+ str = str_make("cos(");
+ goto maybe0;
+ case OSYSTEM:
+ str = str_make("system(");
+ goto maybe0;
+ case OLENGTH:
+ str = str_make("length(");
+ goto maybe0;
+ case OLOG:
+ str = str_make("log(");
+ goto maybe0;
+ case OEXP:
+ str = str_make("exp(");
+ goto maybe0;
+ case OSQRT:
+ str = str_make("sqrt(");
+ goto maybe0;
+ case OINT:
+ str = str_make("int(");
+ maybe0:
+ numeric = 1;
+ if (len > 0)
+ tmpstr = walk(1,level,ops[node+1].ival,&numarg,P_MIN);
+ else
+ tmpstr = str_new(0);;
+ if (!tmpstr->str_ptr || !*tmpstr->str_ptr) {
+ if (lval_field) {
+ t = saw_OFS ? "$," : "' '";
+ if (split_to_array) {
+ sprintf(tokenbuf,"join(%s,@Fld)",t);
+ str_cat(tmpstr,tokenbuf);
+ }
+ else {
+ sprintf(tokenbuf,"join(%s, ",t);
+ str_cat(tmpstr,tokenbuf);
+ for (i = 1; i < maxfld; i++) {
+ if (i <= arymax)
+ sprintf(tokenbuf,"$%s,",nameary[i]);
+ else
+ sprintf(tokenbuf,"$Fld%d,",i);
+ str_cat(tmpstr,tokenbuf);
+ }
+ if (maxfld <= arymax)
+ sprintf(tokenbuf,"$%s)",nameary[maxfld]);
+ else
+ sprintf(tokenbuf,"$Fld%d)",maxfld);
+ str_cat(tmpstr,tokenbuf);
+ }
+ }
+ else
+ str_cat(tmpstr,"$_");
+ }
+ if (strEQ(tmpstr->str_ptr,"$_")) {
+ if (type == OLENGTH && !do_chop) {
+ str = str_make("(length(");
+ str_cat(tmpstr,") - 1");
+ }
+ }
+ str_scat(str,tmpstr);
+ str_free(tmpstr);
+ str_cat(str,")");
+ break;
+ case OBREAK:
+ str = str_new(0);
+ str_set(str,"last");
+ break;
+ case ONEXT:
+ str = str_new(0);
+ str_set(str,"next line");
+ break;
+ case OEXIT:
+ str = str_new(0);
+ if (realexit) {
+ prec = P_UNI;
+ str_set(str,"exit");
+ if (len == 1) {
+ str_cat(str," ");
+ exitval = TRUE;
+ str_scat(str,
+ fstr=walk(1,level,ops[node+1].ival,&numarg,prec+1));
+ str_free(fstr);
+ }
+ }
+ else {
+ if (len == 1) {
+ str_set(str,"$ExitValue = ");
+ exitval = TRUE;
+ str_scat(str,
+ fstr=walk(1,level,ops[node+1].ival,&numarg,P_ASSIGN));
+ str_free(fstr);
+ str_cat(str,"; ");
+ }
+ str_cat(str,"last line");
+ }
+ break;
+ case OCONTINUE:
+ str = str_new(0);
+ str_set(str,"next");
+ break;
+ case OREDIR:
+ goto def;
+ case OIF:
+ str = str_new(0);
+ str_set(str,"if (");
+ str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
+ str_free(fstr);
+ str_cat(str,") ");
+ str_scat(str,fstr=walk(0,level,ops[node+2].ival,&numarg,P_MIN));
+ str_free(fstr);
+ if (len == 3) {
+ i = ops[node+3].ival;
+ if (i) {
+ if ((ops[i].ival & 255) == OBLOCK) {
+ i = ops[i+1].ival;
+ if (i) {
+ if ((ops[i].ival & 255) != OIF)
+ i = 0;
+ }
+ }
+ else
+ i = 0;
+ }
+ if (i) {
+ str_cat(str,"els");
+ str_scat(str,fstr=walk(0,level,i,&numarg,P_MIN));
+ str_free(fstr);
+ }
+ else {
+ str_cat(str,"else ");
+ str_scat(str,fstr=walk(0,level,ops[node+3].ival,&numarg,P_MIN));
+ str_free(fstr);
+ }
+ }
+ break;
+ case OWHILE:
+ str = str_new(0);
+ str_set(str,"while (");
+ str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
+ str_free(fstr);
+ str_cat(str,") ");
+ str_scat(str,fstr=walk(0,level,ops[node+2].ival,&numarg,P_MIN));
+ str_free(fstr);
+ break;
+ case ODO:
+ str = str_new(0);
+ str_set(str,"do ");
+ str_scat(str,fstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
+ str_free(fstr);
+ if (str->str_ptr[str->str_cur - 1] == '\n')
+ --str->str_cur;;
+ str_cat(str," while (");
+ str_scat(str,fstr=walk(0,level,ops[node+2].ival,&numarg,P_MIN));
+ str_free(fstr);
+ str_cat(str,");");
+ break;
+ case OFOR:
+ str = str_new(0);
+ str_set(str,"for (");
+ str_scat(str,tmpstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
+ i = numarg;
+ if (i) {
+ t = s = tmpstr->str_ptr;
+ while (isalpha(*t) || isdigit(*t) || *t == '$' || *t == '_')
+ t++;
+ i = t - s;
+ if (i < 2)
+ i = 0;
+ }
+ str_cat(str,"; ");
+ fstr=walk(1,level,ops[node+2].ival,&numarg,P_MIN);
+ if (i && (t = index(fstr->str_ptr,0377))) {
+ if (strnEQ(fstr->str_ptr,s,i))
+ *t = ' ';
+ }
+ str_scat(str,fstr);
+ str_free(fstr);
+ str_free(tmpstr);
+ str_cat(str,"; ");
+ str_scat(str,fstr=walk(1,level,ops[node+3].ival,&numarg,P_MIN));
+ str_free(fstr);
+ str_cat(str,") ");
+ str_scat(str,fstr=walk(0,level,ops[node+4].ival,&numarg,P_MIN));
+ str_free(fstr);
+ break;
+ case OFORIN:
+ tmpstr = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
+ d = index(tmpstr->str_ptr,'$');
+ if (!d)
+ fatal("Illegal for loop: %s",tmpstr->str_ptr);
+ s = index(d,'{');
+ if (!s)
+ s = index(d,'[');
+ if (!s)
+ fatal("Illegal for loop: %s",d);
+ *s++ = '\0';
+ for (t = s; i = *t; t++) {
+ i &= 127;
+ if (i == '}' || i == ']')
+ break;
+ }
+ if (*t)
+ *t = '\0';
+ str = str_new(0);
+ str_set(str,d+1);
+ str_cat(str,"[]");
+ tmp2str = hfetch(symtab,str->str_ptr);
+ if (tmp2str && atoi(tmp2str->str_ptr)) {
+ sprintf(tokenbuf,
+ "foreach %s ($[ .. $#%s) ",
+ s,
+ d+1);
+ }
+ else {
+ sprintf(tokenbuf,
+ "foreach %s (keys %%%s) ",
+ s,
+ d+1);
+ }
+ str_set(str,tokenbuf);
+ str_scat(str,fstr=walk(0,level,ops[node+2].ival,&numarg,P_MIN));
+ str_free(fstr);
+ str_free(tmpstr);
+ break;
+ case OBLOCK:
+ str = str_new(0);
+ str_set(str,"{");
+ if (len >= 2 && ops[node+2].ival) {
+ str_scat(str,fstr=walk(0,level,ops[node+2].ival,&numarg,P_MIN));
+ str_free(fstr);
+ }
+ fixtab(str,++level);
+ str_scat(str,fstr=walk(0,level,ops[node+1].ival,&numarg,P_MIN));
+ str_free(fstr);
+ addsemi(str);
+ fixtab(str,--level);
+ str_cat(str,"}\n");
+ tab(str,level);
+ if (len >= 3) {
+ str_scat(str,fstr=walk(0,level,ops[node+3].ival,&numarg,P_MIN));
+ str_free(fstr);
+ }
+ break;
+ default:
+ def:
+ if (len) {
+ if (len > 5)
+ fatal("Garbage length in walk");
+ str = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
+ for (i = 2; i<= len; i++) {
+ str_scat(str,fstr=walk(0,level,ops[node+i].ival,&numarg,P_MIN));
+ str_free(fstr);
+ }
+ }
+ else {
+ str = Nullstr;
+ }
+ break;
+ }
+ if (!str)
+ str = str_new(0);
+
+ if (useval && prec < minprec) { /* need parens? */
+ fstr = str_new(str->str_cur+2);
+ str_nset(fstr,"(",1);
+ str_scat(fstr,str);
+ str_ncat(fstr,")",1);
+ str_free(str);
+ str = fstr;
+ }
+
+ *numericptr = numeric;
+#ifdef DEBUGGING
+ if (debug & 4) {
+ printf("%3d %5d %15s %d %4d ",level,node,opname[type],len,str->str_cur);
+ for (t = str->str_ptr; *t && t - str->str_ptr < 40; t++)
+ if (*t == '\n')
+ printf("\\n");
+ else if (*t == '\t')
+ printf("\\t");
+ else
+ putchar(*t);
+ putchar('\n');
+ }
+#endif
+ return str;
+}
+
+tab(str,lvl)
+register STR *str;
+register int lvl;
+{
+ while (lvl > 1) {
+ str_cat(str,"\t");
+ lvl -= 2;
+ }
+ if (lvl)
+ str_cat(str," ");
+}
+
+fixtab(str,lvl)
+register STR *str;
+register int lvl;
+{
+ register char *s;
+
+ /* strip trailing white space */
+
+ s = str->str_ptr+str->str_cur - 1;
+ while (s >= str->str_ptr && (*s == ' ' || *s == '\t' || *s == '\n'))
+ s--;
+ s[1] = '\0';
+ str->str_cur = s + 1 - str->str_ptr;
+ if (s >= str->str_ptr && *s != '\n')
+ str_cat(str,"\n");
+
+ tab(str,lvl);
+}
+
+addsemi(str)
+register STR *str;
+{
+ register char *s;
+
+ s = str->str_ptr+str->str_cur - 1;
+ while (s >= str->str_ptr && (*s == ' ' || *s == '\t' || *s == '\n'))
+ s--;
+ if (s >= str->str_ptr && *s != ';' && *s != '}')
+ str_cat(str,";");
+}
+
+emit_split(str,level)
+register STR *str;
+int level;
+{
+ register int i;
+
+ if (split_to_array)
+ str_cat(str,"@Fld");
+ else {
+ str_cat(str,"(");
+ for (i = 1; i < maxfld; i++) {
+ if (i <= arymax)
+ sprintf(tokenbuf,"$%s,",nameary[i]);
+ else
+ sprintf(tokenbuf,"$Fld%d,",i);
+ str_cat(str,tokenbuf);
+ }
+ if (maxfld <= arymax)
+ sprintf(tokenbuf,"$%s)",nameary[maxfld]);
+ else
+ sprintf(tokenbuf,"$Fld%d)",maxfld);
+ str_cat(str,tokenbuf);
+ }
+ if (const_FS) {
+ sprintf(tokenbuf," = split(/[%c\\n]/, $_, 9999);\n",const_FS);
+ str_cat(str,tokenbuf);
+ }
+ else if (saw_FS)
+ str_cat(str," = split($FS, $_, 9999);\n");
+ else
+ str_cat(str," = split(' ', $_, 9999);\n");
+ tab(str,level);
+}
+
+prewalk(numit,level,node,numericptr)
+int numit;
+int level;
+register int node;
+int *numericptr;
+{
+ register int len;
+ register int type;
+ register int i;
+ char *t;
+ char *d, *s;
+ int numarg;
+ int numeric = FALSE;
+ STR *tmpstr;
+ STR *tmp2str;
+
+ if (!node) {
+ *numericptr = 0;
+ return 0;
+ }
+ type = ops[node].ival;
+ len = type >> 8;
+ type &= 255;
+ switch (type) {
+ case OPROG:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ if (ops[node+2].ival) {
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ }
+ ++level;
+ prewalk(0,level,ops[node+3].ival,&numarg);
+ --level;
+ if (ops[node+3].ival) {
+ prewalk(0,level,ops[node+4].ival,&numarg);
+ }
+ break;
+ case OHUNKS:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ if (len == 3) {
+ prewalk(0,level,ops[node+3].ival,&numarg);
+ }
+ break;
+ case ORANGE:
+ prewalk(1,level,ops[node+1].ival,&numarg);
+ prewalk(1,level,ops[node+2].ival,&numarg);
+ break;
+ case OPAT:
+ goto def;
+ case OREGEX:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ break;
+ case OHUNK:
+ if (len == 1) {
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ }
+ else {
+ i = prewalk(0,level,ops[node+1].ival,&numarg);
+ if (i) {
+ ++level;
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ --level;
+ }
+ else {
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ }
+ }
+ break;
+ case OPPAREN:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ break;
+ case OPANDAND:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ break;
+ case OPOROR:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ break;
+ case OPNOT:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ break;
+ case OCPAREN:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ numeric |= numarg;
+ break;
+ case OCANDAND:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ numeric = 1;
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ break;
+ case OCOROR:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ numeric = 1;
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ break;
+ case OCNOT:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ numeric = 1;
+ break;
+ case ORELOP:
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ numeric |= numarg;
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ prewalk(0,level,ops[node+3].ival,&numarg);
+ numeric |= numarg;
+ numeric = 1;
+ break;
+ case ORPAREN:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ numeric |= numarg;
+ break;
+ case OMATCHOP:
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ prewalk(0,level,ops[node+3].ival,&numarg);
+ numeric = 1;
+ break;
+ case OMPAREN:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ numeric |= numarg;
+ break;
+ case OCONCAT:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ break;
+ case OASSIGN:
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ prewalk(0,level,ops[node+3].ival,&numarg);
+ if (numarg || strlen(ops[ops[node+1].ival+1].cval) > 1) {
+ numericize(ops[node+2].ival);
+ if (!numarg)
+ numericize(ops[node+3].ival);
+ }
+ numeric |= numarg;
+ break;
+ case OADD:
+ prewalk(1,level,ops[node+1].ival,&numarg);
+ prewalk(1,level,ops[node+2].ival,&numarg);
+ numeric = 1;
+ break;
+ case OSUBTRACT:
+ prewalk(1,level,ops[node+1].ival,&numarg);
+ prewalk(1,level,ops[node+2].ival,&numarg);
+ numeric = 1;
+ break;
+ case OMULT:
+ prewalk(1,level,ops[node+1].ival,&numarg);
+ prewalk(1,level,ops[node+2].ival,&numarg);
+ numeric = 1;
+ break;
+ case ODIV:
+ prewalk(1,level,ops[node+1].ival,&numarg);
+ prewalk(1,level,ops[node+2].ival,&numarg);
+ numeric = 1;
+ break;
+ case OPOW:
+ prewalk(1,level,ops[node+1].ival,&numarg);
+ prewalk(1,level,ops[node+2].ival,&numarg);
+ numeric = 1;
+ break;
+ case OMOD:
+ prewalk(1,level,ops[node+1].ival,&numarg);
+ prewalk(1,level,ops[node+2].ival,&numarg);
+ numeric = 1;
+ break;
+ case OPOSTINCR:
+ prewalk(1,level,ops[node+1].ival,&numarg);
+ numeric = 1;
+ break;
+ case OPOSTDECR:
+ prewalk(1,level,ops[node+1].ival,&numarg);
+ numeric = 1;
+ break;
+ case OPREINCR:
+ prewalk(1,level,ops[node+1].ival,&numarg);
+ numeric = 1;
+ break;
+ case OPREDECR:
+ prewalk(1,level,ops[node+1].ival,&numarg);
+ numeric = 1;
+ break;
+ case OUMINUS:
+ prewalk(1,level,ops[node+1].ival,&numarg);
+ numeric = 1;
+ break;
+ case OUPLUS:
+ prewalk(1,level,ops[node+1].ival,&numarg);
+ numeric = 1;
+ break;
+ case OPAREN:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ numeric |= numarg;
+ break;
+ case OGETLINE:
+ break;
+ case OSPRINTF:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ break;
+ case OSUBSTR:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ prewalk(1,level,ops[node+2].ival,&numarg);
+ if (len == 3) {
+ prewalk(1,level,ops[node+3].ival,&numarg);
+ }
+ break;
+ case OSTRING:
+ break;
+ case OSPLIT:
+ numeric = 1;
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ if (len == 3)
+ prewalk(0,level,ops[node+3].ival,&numarg);
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ break;
+ case OINDEX:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ numeric = 1;
+ break;
+ case OMATCH:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ numeric = 1;
+ break;
+ case OUSERDEF:
+ subretnum = FALSE;
+ --level;
+ tmpstr = walk(0,level,ops[node+1].ival,&numarg,P_MIN);
+ ++level;
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ prewalk(0,level,ops[node+4].ival,&numarg);
+ prewalk(0,level,ops[node+5].ival,&numarg);
+ --level;
+ str_cat(tmpstr,"(");
+ tmp2str = str_new(0);
+ if (subretnum || numarg)
+ str_set(tmp2str,"1");
+ hstore(symtab,tmpstr->str_ptr,tmp2str);
+ str_free(tmpstr);
+ level++;
+ break;
+ case ORETURN:
+ if (len > 0) {
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ if (numarg)
+ subretnum = TRUE;
+ }
+ break;
+ case OUSERFUN:
+ tmp2str = str_new(0);
+ str_scat(tmp2str,tmpstr=walk(1,level,ops[node+1].ival,&numarg,P_MIN));
+ fixrargs(tmpstr->str_ptr,ops[node+2].ival,0);
+ str_free(tmpstr);
+ str_cat(tmp2str,"(");
+ tmpstr = hfetch(symtab,tmp2str->str_ptr);
+ if (tmpstr && tmpstr->str_ptr)
+ numeric |= atoi(tmpstr->str_ptr);
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ str_free(tmp2str);
+ break;
+ case OGSUB:
+ case OSUB:
+ if (len >= 3)
+ prewalk(0,level,ops[node+3].ival,&numarg);
+ prewalk(0,level,ops[ops[node+2].ival+1].ival,&numarg);
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ numeric = 1;
+ break;
+ case ONUM:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ numeric = 1;
+ break;
+ case OSTR:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ break;
+ case ODEFINED:
+ case ODELETE:
+ case OSTAR:
+ case OVAR:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ if (len == 1) {
+ if (numit)
+ numericize(node);
+ }
+ else {
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ }
+ break;
+ case OFLD:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ break;
+ case OVFLD:
+ i = ops[node+1].ival;
+ prewalk(0,level,i,&numarg);
+ break;
+ case OJUNK:
+ goto def;
+ case OSNEWLINE:
+ break;
+ case ONEWLINE:
+ break;
+ case OSCOMMENT:
+ break;
+ case OCOMMENT:
+ break;
+ case OCOMMA:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ prewalk(0,level,ops[node+3].ival,&numarg);
+ break;
+ case OSEMICOLON:
+ break;
+ case OSTATES:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ break;
+ case OSTATE:
+ if (len >= 1) {
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ if (len >= 2) {
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ }
+ }
+ break;
+ case OCLOSE:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ break;
+ case OPRINTF:
+ case OPRINT:
+ if (len == 3) { /* output redirection */
+ prewalk(0,level,ops[node+3].ival,&numarg);
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ }
+ prewalk(0+(type==OPRINT),level,ops[node+1].ival,&numarg);
+ break;
+ case ORAND:
+ break;
+ case OSRAND:
+ goto maybe0;
+ case OATAN2:
+ goto maybe0;
+ case OSIN:
+ goto maybe0;
+ case OCOS:
+ goto maybe0;
+ case OSYSTEM:
+ goto maybe0;
+ case OLENGTH:
+ goto maybe0;
+ case OLOG:
+ goto maybe0;
+ case OEXP:
+ goto maybe0;
+ case OSQRT:
+ goto maybe0;
+ case OINT:
+ maybe0:
+ numeric = 1;
+ if (len > 0)
+ prewalk(type != OLENGTH && type != OSYSTEM,
+ level,ops[node+1].ival,&numarg);
+ break;
+ case OBREAK:
+ break;
+ case ONEXT:
+ break;
+ case OEXIT:
+ if (len == 1) {
+ prewalk(1,level,ops[node+1].ival,&numarg);
+ }
+ break;
+ case OCONTINUE:
+ break;
+ case OREDIR:
+ goto def;
+ case OIF:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ if (len == 3) {
+ prewalk(0,level,ops[node+3].ival,&numarg);
+ }
+ break;
+ case OWHILE:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ break;
+ case OFOR:
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ prewalk(0,level,ops[node+3].ival,&numarg);
+ prewalk(0,level,ops[node+4].ival,&numarg);
+ break;
+ case OFORIN:
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ break;
+ case OBLOCK:
+ if (len == 2) {
+ prewalk(0,level,ops[node+2].ival,&numarg);
+ }
+ ++level;
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ --level;
+ break;
+ default:
+ def:
+ if (len) {
+ if (len > 5)
+ fatal("Garbage length in prewalk");
+ prewalk(0,level,ops[node+1].ival,&numarg);
+ for (i = 2; i<= len; i++) {
+ prewalk(0,level,ops[node+i].ival,&numarg);
+ }
+ }
+ break;
+ }
+ *numericptr = numeric;
+ return 1;
+}
+
+numericize(node)
+register int node;
+{
+ register int len;
+ register int type;
+ register int i;
+ STR *tmpstr;
+ STR *tmp2str;
+ int numarg;
+
+ type = ops[node].ival;
+ len = type >> 8;
+ type &= 255;
+ if (type == OVAR && len == 1) {
+ tmpstr=walk(0,0,ops[node+1].ival,&numarg,P_MIN);
+ tmp2str = str_make("1");
+ hstore(symtab,tmpstr->str_ptr,tmp2str);
+ }
+}
diff --git a/gnu/usr.bin/pr/Makefile b/gnu/usr.bin/pr/Makefile
index 6c8d431..d651c20 100644
--- a/gnu/usr.bin/pr/Makefile
+++ b/gnu/usr.bin/pr/Makefile
@@ -1,7 +1,11 @@
-
PROG= pr
SRCS= pr.c getopt.c getopt1.c error.c xmalloc.c version.c
-CFLAGS+=-I${.CURDIR}
+CFLAGS+=-I${.CURDIR} -DDIRENT=1 -DHAVE_LONG_DOUBLE=1 -DHAVE_LONG_DOUBLE=1 \
+ -DHAVE_ST_BLKSIZE=1 -DHAVE_VPRINTF=1 -DRETSIGTYPE=void \
+ -DSTDC_HEADERS=1 -DDHAVE_STRERROR=1 -DHAVE_FCNTL_H=1 \
+ -DHAVE_LIMITS_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRING_H=1 \
+ -DHAVE_UNISTD_H=1
+
.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/pr/error.c b/gnu/usr.bin/pr/error.c
index e849c5b..41d66fb 100644
--- a/gnu/usr.bin/pr/error.c
+++ b/gnu/usr.bin/pr/error.c
@@ -1,5 +1,5 @@
/* error.c -- error handler for noninteractive utilities
- Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc.
+ Copyright (C) 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,6 +17,17 @@
/* Written by David MacKenzie. */
+#ifdef HAVE_CONFIG_H
+#if defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
#include <stdio.h>
#ifdef HAVE_VPRINTF
diff --git a/gnu/usr.bin/pr/getopt.c b/gnu/usr.bin/pr/getopt.c
index a59a013..7a4673b 100644
--- a/gnu/usr.bin/pr/getopt.c
+++ b/gnu/usr.bin/pr/getopt.c
@@ -20,31 +20,24 @@
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-/* NOTE!!! AIX requires this to be the first thing in the file.
- Do not put ANYTHING before it! */
-#if !defined (__GNUC__) && defined (_AIX)
- #pragma alloca
-#endif
-
#ifdef HAVE_CONFIG_H
+#if defined (emacs) || defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
#include "config.h"
#endif
-
-#ifdef __GNUC__
-#define alloca __builtin_alloca
-#else /* not __GNUC__ */
-#if defined (HAVE_ALLOCA_H) || (defined(sparc) && (defined(sun) || (!defined(USG) && !defined(SVR4) && !defined(__svr4__))))
-#include <alloca.h>
-#else
-#ifndef _AIX
-char *alloca ();
#endif
-#endif /* alloca.h */
-#endif /* not __GNUC__ */
-#if !__STDC__ && !defined(const) && IN_GCC
+#ifndef __STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
#define const
#endif
+#endif
/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. */
#ifndef _NO_PROTO
@@ -67,12 +60,9 @@ char *alloca ();
/* This needs to come after some library #include
to get __GNU_LIBRARY__ defined. */
#ifdef __GNU_LIBRARY__
-#undef alloca
/* Don't include stdlib.h for non-GNU C libraries because some of them
contain conflicting prototypes for getopt. */
#include <stdlib.h>
-#else /* Not GNU C library. */
-#define __alloca alloca
#endif /* GNU C library. */
/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
@@ -180,7 +170,6 @@ static enum
in GCC. */
#include <string.h>
#define my_index strchr
-#define my_bcopy(src, dst, n) memcpy ((dst), (src), (n))
#else
/* Avoid depending on library functions or files
@@ -202,16 +191,19 @@ my_index (str, chr)
return 0;
}
-static void
-my_bcopy (from, to, size)
- const char *from;
- char *to;
- int size;
-{
- int i;
- for (i = 0; i < size; i++)
- to[i] = from[i];
-}
+/* If using GCC, we can safely declare strlen this way.
+ If not using GCC, it is ok not to declare it.
+ (Supposedly there are some machines where it might get a warning,
+ but changing this conditional to __STDC__ is too risky.) */
+#ifdef __GNUC__
+#ifdef IN_GCC
+#include "gstddef.h"
+#else
+#include <stddef.h>
+#endif
+extern size_t strlen (const char *);
+#endif
+
#endif /* GNU C library. */
/* Handle permutation of arguments. */
@@ -236,17 +228,51 @@ static void
exchange (argv)
char **argv;
{
- int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *);
- char **temp = (char **) __alloca (nonopts_size);
+ int bottom = first_nonopt;
+ int middle = last_nonopt;
+ int top = optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
- /* Interchange the two blocks of data in ARGV. */
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
- my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size);
- my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt],
- (optind - last_nonopt) * sizeof (char *));
- my_bcopy ((char *) temp,
- (char *) &argv[first_nonopt + optind - last_nonopt],
- nonopts_size);
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
/* Update records for the slots the non-options now occupy. */
diff --git a/gnu/usr.bin/pr/getopt1.c b/gnu/usr.bin/pr/getopt1.c
index a32615c..f784b57 100644
--- a/gnu/usr.bin/pr/getopt1.c
+++ b/gnu/usr.bin/pr/getopt1.c
@@ -17,14 +17,25 @@
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifdef HAVE_CONFIG_H
+#if defined (emacs) || defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
#include "config.h"
#endif
+#endif
#include "getopt.h"
-#if !__STDC__ && !defined(const) && IN_GCC
+#ifndef __STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
#define const
#endif
+#endif
#include <stdio.h>
diff --git a/gnu/usr.bin/pr/pr.1 b/gnu/usr.bin/pr/pr.1
index 10cdd8c..814f4e1 100644
--- a/gnu/usr.bin/pr/pr.1
+++ b/gnu/usr.bin/pr/pr.1
@@ -97,7 +97,7 @@ blank lines or formfeeds).
Print unprintable characters in octal backslash notation.
.TP
.I "\-\-version"
-Print version information on standard error then exit.
+Print version information on standard output then exit.
.TP
.I "\-w page-width"
Set the page width to \fIpage-width\fP columns. The default is 72.
diff --git a/gnu/usr.bin/pr/pr.c b/gnu/usr.bin/pr/pr.c
index 250f532..8ecc2c6 100644
--- a/gnu/usr.bin/pr/pr.c
+++ b/gnu/usr.bin/pr/pr.c
@@ -94,6 +94,17 @@
-w width Set the page width to WIDTH characters. */
+#ifdef HAVE_CONFIG_H
+#if defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
@@ -426,15 +437,15 @@ static int *clump_buff;
static int truncate_lines = FALSE;
/* If non-zero, display usage information and exit. */
-static int flag_help;
+static int show_help;
-/* If non-zero, print the version on standard error. */
-static int flag_version;
+/* If non-zero, print the version on standard output then exit. */
+static int show_version;
static struct option const long_options[] =
{
- {"help", no_argument, &flag_help, 1},
- {"version", no_argument, &flag_version, 1},
+ {"help", no_argument, &show_help, 1},
+ {"version", no_argument, &show_version, 1},
{0, 0, 0, 0}
};
@@ -469,7 +480,9 @@ main (argc, argv)
program_name = argv[0];
n_files = 0;
- file_names = (char **) xmalloc ((argc - 1) * sizeof (char *));
+ file_names = (argc > 1
+ ? (char **) xmalloc ((argc - 1) * sizeof (char *))
+ : NULL);
while (1)
{
@@ -486,7 +499,7 @@ main (argc, argv)
if (!ISDIGIT (*s))
{
error (0, 0, "`+' requires a numeric argument");
- usage ();
+ usage (2);
}
/* FIXME: use strtol */
first_page_number = atoi (s);
@@ -591,7 +604,7 @@ main (argc, argv)
fprintf (stderr, "\
%s: extra characters in the argument to the `-s' option: `%s'\n",
program_name, s);
- usage ();
+ usage (2);
}
}
break;
@@ -605,19 +618,19 @@ main (argc, argv)
chars_per_line = atoi (optarg);
break;
default:
- usage ();
+ usage (2);
break;
}
}
- if (flag_version)
+ if (show_version)
{
- fprintf (stderr, "%s\n", version_string);
+ printf ("%s\n", version_string);
exit (0);
}
- if (flag_help)
- usage ();
+ if (show_help)
+ usage (0);
if (parallel_files && explicit_columns)
error (1, 0,
@@ -682,7 +695,7 @@ getoptarg (arg, switch_char, character, number)
fprintf (stderr, "\
%s: extra characters in the argument to the `-%c' option: `%s'\n",
program_name, switch_char, arg);
- usage ();
+ usage (2);
}
}
}
@@ -1863,13 +1876,45 @@ cleanup ()
/* Complain, print a usage message, and die. */
static void
-usage ()
+usage (status)
+ int status;
{
- fprintf (stderr, "\
-Usage: %s [+PAGE] [-COLUMN] [-abcdfFmrtv] [-e[in-tab-char[in-tab-width]]]\n\
- [-h header] [-i[out-tab-char[out-tab-width]]] [-l page-length]\n\
- [-n[number-separator[digits]]] [-o left-margin]\n\
- [-s[column-separator]] [-w page-width] [--help] [--version] [file...]\n",
- program_name);
- exit (2);
+ if (status != 0)
+ fprintf (stderr, "Try `%s --help' for more information.\n",
+ program_name);
+ else
+ {
+ printf ("\
+Usage: %s [OPTION]... [FILE]...\n\
+",
+ program_name);
+ printf ("\
+\n\
+ +PAGE begin printing with page PAGE\n\
+ -COLUMN produce COLUMN-column output and print columns down\n\
+ -F, -f simulate formfeed with newlines on output\n\
+ -a print columns across rather than down\n\
+ -b balance columns on the last page\n\
+ -c use hat notation (^G) and octal backslash notation\n\
+ -d double space the output\n\
+ -e[CHAR[WIDTH]] expand input CHARs (TABs) to tab WIDTH (8)\n\
+ -h HEADER use HEADER instead of filename in page headers\n\
+ -i[CHAR[WIDTH]] replace spaces with CHARs (TABs) to tab WIDTH (8)\n\
+ -l PAGE_LENGTH set the page length to PAGE_LENGTH (66) lines\n\
+ -m print all files in parallel, one in each column\n\
+ -n[SEP[DIGITS]] number lines, use DIGITS (5) digits, then SEP (TAB)\n\
+ -o MARGIN offset each line with MARGIN spaces (do not affect -w)\n\
+ -r inhibit warning when a file cannot be opened\n\
+ -s[SEP] separate columns by character SEP (TAB)\n\
+ -t inhibit 5-line page headers and trailers\n\
+ -v use octal backslash notation\n\
+ -w PAGE_WIDTH set page width to PAGE_WIDTH (72) columns\n\
+ --help display this help and exit\n\
+ --version output version information and exit\n\
+\n\
+-t implied by -l N when N < 10. Without -s, columns are separated by\n\
+spaces. With no FILE, or when FILE is -, read standard input.\n\
+");
+ }
+ exit (status);
}
diff --git a/gnu/usr.bin/pr/system.h b/gnu/usr.bin/pr/system.h
index 2e03ea8..4aeaaea 100644
--- a/gnu/usr.bin/pr/system.h
+++ b/gnu/usr.bin/pr/system.h
@@ -18,6 +18,40 @@
/* Include sys/types.h before this file. */
#include <sys/stat.h>
+
+#ifdef STAT_MACROS_BROKEN
+#ifdef S_ISBLK
+#undef S_ISBLK
+#endif
+#ifdef S_ISCHR
+#undef S_ISCHR
+#endif
+#ifdef S_ISDIR
+#undef S_ISDIR
+#endif
+#ifdef S_ISFIFO
+#undef S_ISFIFO
+#endif
+#ifdef S_ISLNK
+#undef S_ISLNK
+#endif
+#ifdef S_ISMPB
+#undef S_ISMPB
+#endif
+#ifdef S_ISMPC
+#undef S_ISMPC
+#endif
+#ifdef S_ISNWK
+#undef S_ISNWK
+#endif
+#ifdef S_ISREG
+#undef S_ISREG
+#endif
+#ifdef S_ISSOCK
+#undef S_ISSOCK
+#endif
+#endif /* STAT_MACROS_BROKEN. */
+
#ifndef S_ISREG /* Doesn't have POSIX.1 stat stuff. */
#define mode_t unsigned short
#endif
diff --git a/gnu/usr.bin/pr/version.c b/gnu/usr.bin/pr/version.c
index a629418..64c62b19 100644
--- a/gnu/usr.bin/pr/version.c
+++ b/gnu/usr.bin/pr/version.c
@@ -1,2 +1,13 @@
+#ifdef HAVE_CONFIG_H
+#if defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
#include "version.h"
-const char *version_string = "GNU textutils 1.6";
+const char *version_string = "GNU textutils 1.9";
diff --git a/gnu/usr.bin/pr/xmalloc.c b/gnu/usr.bin/pr/xmalloc.c
index f989004..58a81b5 100644
--- a/gnu/usr.bin/pr/xmalloc.c
+++ b/gnu/usr.bin/pr/xmalloc.c
@@ -1,5 +1,5 @@
/* xmalloc.c -- malloc with out of memory checking
- Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+ Copyright (C) 1990, 1991, 1993 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -15,23 +15,46 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-#ifdef STDC_HEADERS
+#ifdef HAVE_CONFIG_H
+#if defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
+#if __STDC__
+#define VOID void
+#else
+#define VOID char
+#endif
+
+#include <sys/types.h>
+
+#if STDC_HEADERS
#include <stdlib.h>
#else
-char *malloc ();
-char *realloc ();
+VOID *malloc ();
+VOID *realloc ();
void free ();
#endif
+#if __STDC__ && defined (HAVE_VPRINTF)
+void error (int, int, char const *, ...);
+#else
void error ();
+#endif
/* Allocate N bytes of memory dynamically, with error checking. */
-char *
+VOID *
xmalloc (n)
- unsigned n;
+ size_t n;
{
- char *p;
+ VOID *p;
p = malloc (n);
if (p == 0)
@@ -45,10 +68,10 @@ xmalloc (n)
If P is NULL, run xmalloc.
If N is 0, run free and return NULL. */
-char *
+VOID *
xrealloc (p, n)
- char *p;
- unsigned n;
+ VOID *p;
+ size_t n;
{
if (p == 0)
return xmalloc (n);
diff --git a/gnu/usr.bin/ptx/.stamp-h.in b/gnu/usr.bin/ptx/.stamp-h.in
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gnu/usr.bin/ptx/.stamp-h.in
diff --git a/gnu/usr.bin/ptx/COPYING b/gnu/usr.bin/ptx/COPYING
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/gnu/usr.bin/ptx/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/gnu/usr.bin/ptx/ChangeLog b/gnu/usr.bin/ptx/ChangeLog
new file mode 100644
index 0000000..fffb47f
--- /dev/null
+++ b/gnu/usr.bin/ptx/ChangeLog
@@ -0,0 +1,546 @@
+Fri Nov 5 23:10:07 1993 Francois Pinard (pinard@icule)
+
+ * Version 0.3
+
+ * check-out: New name for check_out.
+ * Makefile.in: Change check_out for check-out everywhere.
+ Reported by Jim Meyering <meyering@comco.com>.
+
+ * Makefile.in (realclean): Do not remove .stamp-h.in and
+ config.h.in. One should not need Autoconf installed.
+ Reported by Nelson Beebe <beebe@math.utah.edu>.
+
+ * ptx.c: Add missing definition of isxdigit.
+ Reported by Nelson Beebe <beebe@math.utah.edu>.
+
+ * ptx.c: Define S_ISREG if not defined, then use it.
+ Reported by Karl Berry <karl@cs.umb.edu>.
+
+Wed Nov 3 15:53:00 1993 Francois Pinard (pinard@icule)
+
+ * mkinstalldirs: New, from elsewhere.
+ * Makefile.in: Use it.
+
+Mon Nov 1 00:48:34 1993 Francois Pinard (pinard@lagrande.IRO.UMontreal.CA)
+
+ * Makefile.in (clean): Delete ptx, not the obsolete $(PROGS).
+
+Sun Oct 31 15:04:57 1993 Francois Pinard (pinard@raptor.IRO.UMontreal.CA)
+
+ * ptx.c (alloc_and_compile_regex): Zero out the whole allocated
+ pattern, not just a few fields.
+
+ * ptx.c (alloc_and_compile_regex): Clarify error message.
+
+Thu Oct 28 08:29:29 1993 Francois Pinard (pinard@compy.IRO.UMontreal.CA)
+
+ * ptx.c (print_copyright): Deleted. Rather use a "copyright"
+ variable, print to standard output instead of standard error.
+
+ * ptx.c: Use error instead of fprintf (stderr, ...).
+
+ * ptx.c: Rename fold_lower_to_upper to ignore_case.
+
+Wed Oct 27 18:41:52 1993 Francois Pinard (pinard@lagrande.IRO.UMontreal.CA)
+
+ * ptx.c: Add option -M for using another macro name than "xx".
+ Reported by Thorsten Ohl <ohl@physics.harvard.edu>.
+
+ * examples/ignore/: New files.
+ * eign: Linked to examples/ignore/eign.
+ * Makefile.in: Install and uninstall $(datadir)/eign.
+ * configure.in: Remove testing of a default ignore file.
+ Reported by Nelson Beebe <beebe@math.utah.edu>.
+
+ * ptx.c (main): Add --help and --version processing.
+ (print_version): Deleted.
+
+ * ptx.c: Use -traditional instead of --no-gnu-extensions,
+ --ignore-case instead of --fold-letter-case, --format=<format>
+ instead of --tex-output and --roff-output.
+ * argmatch.c: New file. Taken from fileutils/lib.
+ Reported by Karl Berry <karl@cs.umb.edu>.
+
+Tue Oct 26 08:39:14 1993 Francois Pinard (pinard@icule)
+
+ * ptx.c (usage): New name for usage_and_exit. Accept an exit
+ status parameter. If zero, print full help on stdout. If
+ non-zero, print a one-line helper on stderr.
+
+ * ptx.c: Remove sizeof_occurs and OCCURS_ALIGNMENT complexity.
+ The memory savings did not justify the portability headaches.
+
+ * ptx.c (copy_unescaped_string): New function.
+ (main): Use it with options -F, -S and -W.
+ Reported by Dave Cottingham <dc@haiti.gsfc.nasa.gov>.
+
+ * ptx.c (fix_output_parameters): Force edit of '\f', because some
+ systems does not consider it to be whitespace.
+ Reported by Stephane Berube <berube@iro.umontreal.ca>.
+
+ * ptx.c (fix_output_parameters): For roff output, do not disallow
+ characters with 8th bit set.
+ Reported by James Clark <jjc@jclark.com>.
+
+ * Makefile.in (dist): Include examples/ in distribution.
+
+Mon Oct 25 15:46:16 1993 Francois Pinard (pinard@icule)
+
+ * ptx.c: Change --display-width to --width, for consistency with
+ other GNU programs.
+
+ * examples/ajay/: New files.
+ Reported by Ajay Shah <ajayshah@cmie.ernet.in>.
+ Reported by Rakesh Chauhan <rk@cmie.ernet.in>.
+
+ * examples/luke/: New files.
+ Reported by Luke Kendall <luke@research.canon.oz.au>.
+
+ * examples/latex/: New files.
+
+ * ptx.c (find_occurs_in_text): Assign 0 to refererence_length so
+ GNU C will not warn anymore against its unitialized use.
+ Reported by Loic Dachary <L.Dachary@cs.ucl.ac.uk>.
+
+ * lib/: Move routines in main directory first, then destroy.
+ * Makefile.in: Merge lib/Makefile.in, clean up.
+ * configure.in: Do not create lib/Makefile.in.
+
+ * acconfig.h: New file.
+ * .stamp-h.in: Used for timestamping autoheader.
+ * Makefile.in: Use acconfig.h and .stamp-h.in. Force
+ autoheader whenever acconfig.h is modified.
+
+Wed Jun 9 15:01:28 1993 Francois Pinard (pinard@icule)
+
+ * Makefile.in (dist): Replace "echo `pwd`" by a mere "pwd".
+ Create a gzip file.
+
+Sat May 22 20:18:31 1993 Francois Pinard (pinard@icule)
+
+ * Makefile.in: Replace $(PROGS) by ptx.
+
+ * diacrit.h: Change `c' to `chr', better protect it.
+
+ * lib/COPYING.LIB: Deleted.
+ * lib/Makefile.in: Adjust accordingly.
+
+Sat Feb 6 15:03:13 1993 Francois Pinard (pinard@icule)
+
+ * Makefile.in, lib/Makefile.in: In dist goals, ensure 777 mode for
+ directories, so older tar's will restore file modes properly.
+
+Sun Jan 17 15:42:35 1993 Francois Pinard (pinard@icule)
+
+ * Makefile.in, lib/Makefile.in: Put $(CFLAGS) after $(CPPFLAGS),
+ so the installer can override automatically configured choices.
+ Reported by Karl Berry <karl@cs.umb.edu>.
+
+Tue Jan 12 09:21:22 1993 Francois Pinard (pinard at icule)
+
+ * configure.in: Check for setchrclass().
+ * diacrit.[hc]: New file, extracted from my own ctype.[hc].
+ * ctype.[hc]: Deleted.
+ * Makefile.in: Distribute diacrit.[hc], but not ctype.[hc].
+ * ptx.c: Include "diacrit.h" rather than "ctype.h".
+ Include <ctype.h> for ANSI C, or else, use our own definitions.
+ (initialize_regex): Use ctype.h macros for making the folding
+ table and for making the \w+ fastmap. Previously, was reusing the
+ regex syntax table or looking at character bit structure.
+ (main): Execute setchrclass (NULL) if available and ANSI C.
+
+ * Spelling fixes in various files.
+ Reported by Jim Meyering <meyering@cs.utexas.edu>.
+
+Thu Jan 7 20:19:25 1993 Francois Pinard (pinard at icule)
+
+ * Makefile.in: Using autoheader, derive config.h.in from
+ configure.in. Distribute config.h.in.
+ Use config.status for reconstructing config.h from config.h.in.
+ Have all $(OBJECTS) depend upon config.h.
+ Always use -I. calling the C compiler, for config.h to be found.
+ Remove config.h in distclean-local.
+ * lib/Makefile.in: Always use -I.. calling the C compiler, for
+ config.h to be found. Also use $(DEFS).
+ Have all $(OBJECTS) depend upon ../config.h.
+ * configure.in: Create config.h from config.h.in.
+ * ptx.c, ctype.c: Conditionnaly include config.h.
+
+Fri Jan 1 19:52:49 1993 Francois Pinard (pinard at icule)
+
+ * Makefile.in, lib/Makefile.in: Reinstate $(CPPFLAGS), use it.
+ Richard wants it there. Remove $(ALLFLAGS) and reequilibrate.
+
+Sun Dec 27 05:57:55 1992 Francois Pinard (pinard at icule)
+
+ * ptx.c (find_occurs_in_text): Introduce word_start and word_end
+ variables, and use them instead of the word_regs structure. This
+ takes care of the fact newer regex.h does not allocate the arrays
+ any more, and these were used even when regexps were not compiled.
+
+ * Makefile, lib/Makefile.in: Define CHAR_SET_SIZE for SYNTAX_TABLE
+ to work correctly.
+
+ * configure.in: Replace AC_USG by AC_HAVE_HEADERS(string.h).
+ Cleanup and reorganize a little.
+
+ * ptx.c: Renamed from gptx.c. Add -G (--no-gnu-extensions)
+ and clarify some long option names by making them more
+ explicit. Remove all PTX_COMPATIBILITY conditionals.
+ Introduce gnu_extensions variable initialized to 1. Let -G
+ give it the value 0, but still allow and process GNU specific
+ options and long option names. The Ignore file is now the same
+ whatever the value of gnu_extensions.
+ * ptx.texinfo: Renamed from gptx.texinfo, adjusted.
+ * Makefile.in, configure.in: Adjusted accordingly. Now
+ installs only one program under the name $(binprefix)ptx.
+
+ * gptx.c (perror_and_exit): Deleted. Use error() directly.
+
+ * gptx.c: Remove unneeded prototypes for system library routines.
+
+ * gptx.c (compare_words, compare_occurs): #define first and second
+ instead of using an intermediate variable.
+
+ * configure.in: Use AC_CONST.
+ * gptx.h: Do not define const.
+ * Define volatile dependent on __GNUC__, not __STDC__, and define
+ it to __volatile__.
+
+ * gptx.h, version.c: Deleted, integrated into gptx.c.
+ * Remove src/ and doc/ subdirectories, merging them in main.
+ * Move lib/bumpalloc.h, lib/ctype.[ch] in main directory.
+ * Integrate all ChangeLogs in main ChangeLog.
+ * Integrate all Makefiles in main Makefile and lib/Makefile,
+ rewriting them all along the way.
+
+Fri Nov 13 00:10:31 1992 Francois Pinard (pinard at icule)
+
+ * Makefile.in (dist): chmod a+r before making the tar file.
+
+Tue Oct 6 12:47:00 1992 Francois Pinard (pinard at icule)
+
+ * {,doc/,lib/,src/}Makefile.in: Use exec_prefix. Add `uninstall'.
+
+Wed Aug 19 16:02:09 1992 Francois Pinard (pinard at icule)
+
+ * ansi2knr.c: New file, from Ghostscript distribution.
+ * gptx.c: Get rid of many __STDC__ tests.
+ * version.c: Idem.
+
+Fri Aug 14 22:53:05 1992 Francois Pinard (pinard at icule)
+
+ * gptx.c: Use HAVE_MCHECK instead of MCHECK_MISSING.
+ * configure.in: Use AC_HAVE_FUNCS instead of AC_MISSING_FUNCS.
+
+ * configure.in: Autoconfigure for mcheck and strerror.
+ Reported by Bernd Nordhausen <bernd@iss.nus.sg>.
+
+Thu Jun 18 09:15:12 1992 Francois Pinard (pinard at icule)
+
+ * configure.in, all Makefile's: Adapt to Autoconf 0.118.
+
+Sun Feb 2 16:23:47 1992 Francois Pinard (pinard at icule)
+
+ * gptx.c (main): Returns int.
+
+Tue Dec 10 09:53:21 1991 Francois Pinard (pinard at icule)
+
+ * gptx.c (usage_and_exit): Print --OPTION instead of +OPTION.
+
+Wed Dec 4 10:31:06 1991 Francois Pinard (pinard at icule)
+
+ * gptx.c (compare_occurs, compare_words): Change parameters to
+ (void *) to comply with qsort ANSI declaration, and cast the true
+ type inside the function, each time a parameter is used.
+ Reported by Byron Rakitzis <byron@archone.tamu.edu>.
+
+Mon Dec 2 10:41:43 1991 Francois Pinard (pinard at icule)
+
+ * gptx.c: Removed comma at end of enum.
+
+ * version.c: Add a few missing `const's.
+
+ * gptx.c: Add prototypes for close, fstat, open, perror and read
+ if __STDC__.
+
+ * gptx.c: Remove useless alloca declaration.
+
+Sat Nov 9 20:03:37 1991 Francois Pinard (pinard at icule)
+
+ * configure.in, all/Makefile.in: Directory reorganization,
+ including separate src and doc, in plus of lib. Ensure all
+ Makefile's can be used independently.
+
+Thu Nov 7 11:20:38 1991 Francois Pinard (pinard at icule)
+
+ * gptx.texinfo: Renamed from gptx.texi. Now `TeX'able.
+ * Makefile.in: Ensure distributing texinfo.tex.
+ Reported by Karl Berry <karl@cs.umb.edu>.
+
+ * configure.in: Take care of POSIXish ISC.
+ Reported by Karl Berry <karl@cs.umb.edu>.
+
+Tue Nov 5 09:42:58 1991 Francois Pinard (pinard at icule)
+
+ * configure.in, Makefile.in: Do not absolutize $(srcdir), because
+ this could create problems with automounters.
+
+ * configure.in, Makefile.in: Remove IF_* devices, they were
+ solving a problem caused only by non timestamping shars, and
+ gptx is now distributed in tar format.
+
+Mon Oct 28 14:39:36 1991 Francois Pinard (pinard at icule)
+
+ * configure.in: New file.
+ * configure: Automatically generated from file configure.in
+ and David MacKenzie's autoconf.
+
+Sat Oct 19 20:06:28 1991 Francois Pinard (pinard at icule)
+
+ * configure: Use ANSI header files if present, even with non ANSI
+ compilers.
+ Reported by David MacKenzie <djm@eng.umd.edu>.
+
+Tue Oct 15 08:43:13 1991 Francois Pinard (pinard at icule)
+
+ * Makefile.in: Install gptx and ptx separately. On DEC Ultrix
+ 4.1, install cannot install more than one file at a time.
+ Reported by Simon Leinen <simon@liasun1.epfl.ch>.
+
+Fri Oct 11 15:19:42 1991 Francois Pinard (pinard at icule)
+
+ * Makefile.in: `realclean' did not work, because lib/Makefile was
+ disappearing at `distclean' time. I tried separate doc and src
+ directories, but this is not worth the heaviness. Split some
+ goals instead, using _doc, _lib and _src suffixes.
+
+Fri Oct 10 18:04:21 1991 Francois Pinard (pinard at icule)
+
+ * Version 0.2
+
+Wed Oct 9 16:13:42 1991 Francois Pinard (pinard at icule)
+
+ * configure, Makefile.in: New files.
+ * Makefile, GNUmakefile, Depends: Deleted.
+
+ * gptx.c: Change -A output from `FILE(NN):' to `FILE:NN:'.
+
+ * gptx.c, gptx.h, version.c: Reinstate __STDC__ tests.
+
+Tue Jun 25 11:35:32 1991 Francois Pinard (pinard at icule)
+
+ * gptx.c: Something is wrong in -r reference allocation, I suspect
+ casting does not do what I expect. I relax the constraints so to
+ make it work for now. To be revisited.
+
+ * gptx.c: Call initialize_regex sooner, to ensure folded_chars is
+ properly initialized when -f and -i are simultaneously used.
+
+ * gptx.c: Remove -p option and rather compile two separate
+ programs, one by defining PTX_COMPATIBILITY, to conform a GNU
+ standard asking to not depend on the program installed name. This
+ also removes the -p option, so loosing the debatable advantage of
+ dynamically reverting to ptx compatibility mode.
+
+ * gptx.h: Cleanup. Don't duplicate stdlib.h.
+
+Wed Dec 5 18:00:23 1990 Francois Pinard (pinard at icule)
+
+ * gptx.c (usage_and_exit): Change -C explanation.
+
+Sun Oct 28 16:11:36 1990 Francois Pinard (pinard at icule)
+
+ * gptx.h: Remove the PROTO macros and usage.
+ * gptx.c: Remove all the #ifdef __STDC__ noise.
+ * version.c: Remove all the #ifdef __STDC__ noise.
+
+Wed Jul 25 12:20:45 1990 Francois Pinard (pinard at icule)
+
+ * ctype.[ch]: Linked from my library.
+
+Wed Jul 11 10:53:13 1990 Francois Pinard (pinard at icule)
+
+ * bumpalloc.h: Linked from my library.
+
+Sun Aug 5 13:17:25 1990 Francois Pinard (pinard at icule)
+
+ * Version 0.1
+
+ * gptx.c: Implement IGNORE and PIGNORE defines.
+
+ * gptx.c: Implement special character protection for roff and TeX
+ output, through the edited_flag array.
+
+Fri Aug 3 12:47:35 1990 Francois Pinard (pinard at icule)
+
+ * gptx.c: Implement new -R option for automatic referencing, with
+ the possibility of multiple input files in normal mode. Now,
+ option -r implies ptx compatibility mode default for -S; exclude
+ reference from context whenever easy to do, and allow coselection
+ of both -r and -R.
+
+Wed Aug 1 12:00:07 1990 Francois Pinard (pinard at icule)
+
+ * gptx.[hc]: Define and use OCCURS_ALIGNMENT, to avoid those
+ `Bus error's on Sparcs.
+
+Fri Jul 27 12:04:40 1990 Francois Pinard (pinard at icule)
+
+ * gptx.c (initialize_regex): Use only isalpha and "ctype.h" to
+ initialize Sword syntax, getting rid of any other explicit ISO
+ 8859-1 references. This will make the MS-DOS port easier,
+ character set wise.
+
+ * gptx.c (swallow_file_in_memory): Revised along the lines of
+ io.c from GNU diff 1.14, so it could handle stin and fifos,
+ and work faster.
+
+ * gptx.c (perror_and_exit): New function, use it where convenient.
+
+Thu Jul 26 13:28:13 1990 Francois Pinard (pinard at icule)
+
+ * gptx.c (swallow_input_text): Remove white space compression even
+ if not in ptx compatibility mode. This being out of the way, use
+ swallow_file_in_memory instead of inputting characters one by one.
+
+Wed Jul 25 12:20:45 1990 Francois Pinard (pinard at icule)
+
+ * gptx.c (find_occurs_in_text): Include the sentence separator as
+ part of the right context, except for separator's suffix white
+ space. Formerly, it was excluded from all contexts.
+
+ * gptx.h: Check STDLIB_PROTO_ALREADY to conditionalize prototype
+ declarations for standard C library routines; check __GNUC__
+ before using `volatile' on function prototypes.
+
+ * gptx.c: (find_occurs_in_text): Maintain the maximum length of
+ all words read.
+ (define_all_fields): Optimize scanning longish left contexts by
+ sometimes doing a backward jump from the keyword instead of always
+ scanning forward from the left context boundary.
+
+Sun Jul 22 09:18:21 1990 Francois Pinard (pinard at icule)
+
+ * gptx (alloc_and_compile_regex): Realloc out all extra allocated
+ space.
+
+Mon Jul 16 09:07:25 1990 Francois Pinard (pinard at icule)
+
+ * gptx.c: In OCCURS structure, modify left, right and reference
+ pointers and make them displacements, to save some space. Define
+ DELTA typedef, use it, make all other necessary changes.
+
+ * gptx.c: Work on portability. Define const and volatile to
+ nothing if not __STDC__. On BSD, define str[r]chr to be [r]index.
+ Avoid writings specific to GNU C.
+
+Sun Jul 15 17:28:39 1990 Francois Pinard (pinard at icule)
+
+ * gptx.c: Add a word_fastmap array and use it if -W has not been
+ specified, instead of using default regexps. Finish implementing
+ the Break files.
+
+Sat Jul 14 10:54:21 1990 Francois Pinard (pinard at icule)
+
+ * gptx.[ch], version.c: Use prototypes in all header
+ functions. Add some missing const declarations.
+
+Fri Jul 13 10:16:34 1990 Francois Pinard (pinard at icule)
+
+ * gptx.c: Enforce ptx compatibility mode by disallowing normal
+ mode extensions. Disallow -p if extensions are used.
+
+ * gptx.c: Finish implementation of Ignore and Only files.
+
+Wed Jul 11 10:53:13 1990 Francois Pinard (pinard at icule)
+
+ * gptx.c: Revise WORD typedef and use it in OCCURS typedef;
+ adjust all usages. Add BLOCK and WORD_ARRAY typedefs, revise in
+ various place to make better usage of these. Use BUMP_ALLOC.
+
+Tue Jul 10 09:02:26 1990 Francois Pinard (pinard at icule)
+
+ * gptx.c: Add -L option, `latin1_charset' variable and support.
+
+ * gptx.c: Remove old generate_roff and generate_tex variables,
+ replace with output_format which is of enum type.
+
+Mon Jul 9 10:40:41 1990 Francois Pinard (pinard at icule)
+
+ * gptx.c (compare_words): Check word_regex.translate and do not
+ use the translation table if not computed. Also protect against
+ possible 8-bit problems.
+
+ * gptx.c (alloc_and_compile_regex): New function.
+
+Sun Jul 8 17:52:14 1990 Francois Pinard (pinard at icule)
+
+ * gptx.c: Make a more systematic use of SKIP_* macros, to get rid
+ of explicit ' ' references when possible.
+
+ * gptx.c: Replace `head' field by `left' in the OCCURS structure,
+ delay the `before' computation from find_occurs_in_text to
+ define_all_fields, and make all necessary adjustments. Also
+ add a `right' field in the OCCURS structure, use it to get rid of
+ explicit '\n' references when possible.
+
+ * gptx.c (initialize_regex): New function. Compute the syntax
+ table for regex. Get rid of previous break_chars_init variable
+ and break_chars array, use word_regex and word_regex_string
+ instead.
+
+ * gptx.c: Use re_search to find words and re_match to skip over
+ them. Add -W option and support. Use re_search to find end of
+ lines or end of sentences, add -S option and support.
+
+Sat Jul 7 08:50:40 1990 Francois Pinard (pinard at icule)
+
+ * gptx.c: Change PRINT_SPACES and PRINT_FIELD macros to
+ print_spaces and print_field routines, respectively.
+
+Fri Jul 6 09:44:39 1990 Francois Pinard (pinard at icule)
+
+ * gptx.c (generate_output): Split into define_all_fields,
+ generate_all_output, output_one_roff_line, output_one_tex_line,
+ and output_one_tty_line.
+
+ * gptx.c: Move the inline code to reallocate the text buffer into
+ reallocate_text_buffer. Correct a small bug in this area.
+
+ * gptx.c: Modify -F to accept a STRING argument, modify output
+ routines to handle truncation marks having more than one
+ character.
+
+Thu Jul 5 11:08:59 1990 Francois Pinard (pinard at icule)
+
+ * gptx.c: Add -F option and logic.
+
+ * gptx.c: Select ptx compatibility mode if program is
+ installed under the name `ptx'. Install both gptx and ptx.
+
+Thu Jun 7 17:21:25 1990 Francois Pinard (pinard at icule)
+
+ * gptx.c: Make each OCCURS a variable size thing, depending on
+ various options; mark occurs_found table size with an integer
+ counter instead of an end pointer.
+
+Sat Apr 14 20:01:09 1990 Francois Pinard (pinard at icule)
+
+ * Version 0.0
+
+ * gptx.c: Removed limitations on table sizes: it should now go
+ until an `Out of memory' error. Use xmalloc. Rename some
+ variables.
+
+ * version.c, gptx.c (usage_and_exit): Add -C option to print
+ Copyright.
+
+Mon Mar 12 17:59:42 1990 Francois Pinard (pinard at icule)
+
+ * ChangeLog initialisation. Previous experiments towards gptx
+ were done at the defunct site ora.odyssee.qc.ca, which was a
+ Sun-3/160 running SunOS 3.0. The files have been stocked for
+ a long time to kovic.iro archives, then imported to icule.
+
+ * gptx.c: GCC linted.
diff --git a/gnu/usr.bin/ptx/Makefile b/gnu/usr.bin/ptx/Makefile
new file mode 100644
index 0000000..340c09a
--- /dev/null
+++ b/gnu/usr.bin/ptx/Makefile
@@ -0,0 +1,10 @@
+PROG= ptx
+SRCS= argmatch.c diacrit.c error.c getopt.c getopt1.c ptx.c xmalloc.c
+
+LDADD+= -lgnuregex
+DPADD+= ${GNUREGEX}
+CFLAGS+= -DHAVE_CONFIG_H -DDEFAULT_IGNORE_FILE=\"/usr/share/dict/eign\"
+
+NOMAN=
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/ptx/NEWS b/gnu/usr.bin/ptx/NEWS
new file mode 100644
index 0000000..6f97bf9
--- /dev/null
+++ b/gnu/usr.bin/ptx/NEWS
@@ -0,0 +1,53 @@
+GNU permuted indexer NEWS - User visible changes.
+Copyright (C) 1990, 1991, 1993 Free Software Foundation, Inc.
+Francois Pinard <pinard@iro.umontreal.ca>, 1992.
+
+Version 0.3 - 1993-10-??, by Franc,ois Pinard
+
+* GNU ptx installs as a single program, -G option dynamically reverts
+to the System V compatible behaviour, yet being liberal with options.
+
+* It should install more easily on more systems, source code is
+unprotoized on the fly for older C compilers.
+
+* A default ignore file is installed along with GNU ptx, ptx uses it.
+
+* Options -F, -S and -W interpret most \-escapes themselves.
+
+* Option -M can be use to change "xx" to another macro name.
+
+* CHRCLASS environment variable is obeyed for systems supporting it.
+
+* Long option names have been cleaned up a little.
+
+* Some examples are given in the example/ directory structure.
+
+
+Version 0.2 - 1991-10-10, by Franc,ois Pinard
+
+* Reference format (with -A) has been modified slightly to better
+comply with GNU standards for line reporting.
+
+* Option -p removed, rather compile two separate programs, one with
+GNU extensions, the other being strict on System V compatibility.
+
+
+Version 0.1 - 1990-08-05, by Franc,ois Pinard
+
+* Add many options: -L for Latin1, -R for automatic referencing, -W
+for regular expressions describing words, -S for regular expressions
+describing end of lines or sentences. Let -F specify the truncation
+strings.
+
+* Implementing Ignore files and Only files.
+
+* Option -p dynamically enforces strict System V compatibility.
+
+* Correct a few bugs and portability problems, have faster input,
+faster processing, and use less memory.
+
+
+Version 0.0 - 1990-04-14, by Franc,ois Pinard
+
+* Initial release.
+
diff --git a/gnu/usr.bin/ptx/README b/gnu/usr.bin/ptx/README
new file mode 100644
index 0000000..240b7ee
--- /dev/null
+++ b/gnu/usr.bin/ptx/README
@@ -0,0 +1,23 @@
+This is an beta release of GNU ptx, a permuted index generator. GNU
+ptx can handle multiple input files at once, produce TeX compatible
+output, or a readable KWIC (keywords in their context) without the
+need of nroff. This version does not handle huge input files, that
+is, those which do not fit in memory all at once.
+
+The command syntax is not the same as UNIX ptx: all given files are
+input files, the results are produced on standard output by default.
+GNU ptx manual is provided in Texinfo format. Calling `ptx --help'
+prints an option summary. Please note that an overall renaming of all
+options is foreseeable: GNU ptx specifications are not frozen yet.
+
+See the file COPYING for copying conditions.
+
+See the file THANKS for a list of contributors.
+
+See the file NEWS for a list of major changes in the current release.
+
+See the file INSTALL for compilation and installation instructions.
+
+Mail suggestions and bug reports (including documentation errors) for
+these programs to bug-gnu-utils@prep.ai.mit.edu.
+
diff --git a/gnu/usr.bin/ptx/THANKS b/gnu/usr.bin/ptx/THANKS
new file mode 100644
index 0000000..e6a45cf
--- /dev/null
+++ b/gnu/usr.bin/ptx/THANKS
@@ -0,0 +1,23 @@
+GNU permuted indexer has originally been written by François Pinard.
+Other people contributed to the GNU permuted index by reporting
+problems, suggesting various improvements or submitting actual code.
+Here is a list of these people. Help me keep it complete and exempt
+of errors.
+
+Ajay Shah ajayshah@cmie.ernet.in
+Bernd Nordhausen bernd@iss.nus.sg
+Byron Rakitzis byron@archone.tamu.edu
+Dave Cottingham dc@haiti.gsfc.nasa.gov
+David J. MacKenzie djm@eng.umd.edu
+Francois Pinard pinard@iro.umontreal.ca
+Janne Himanka shem@syy.oulu.fi
+James Clark jjc@jclark.com
+Jim Meyering meyering@comco.com
+Karl Berry karl@cs.umb.edu
+Loic Dachary L.Dachary@cs.ucl.ac.uk
+Luke Kendall luke@research.canon.oz.au
+Nelson Beebe beebe@math.utah.edu
+Rakesh Chauhan rk@cmie.ernet.in
+Simon Leinen simon@liasun1.epfl.ch
+Stephane Berube berube@iro.umontreal.ca
+Thorsten Ohl ohl@physics.harvard.edu
diff --git a/gnu/usr.bin/ptx/TODO b/gnu/usr.bin/ptx/TODO
new file mode 100644
index 0000000..6714313
--- /dev/null
+++ b/gnu/usr.bin/ptx/TODO
@@ -0,0 +1,94 @@
+TODO file for GNU ptx - last revised 05 November 1993.
+Copyright (C) 1992, 1993 Free Software Foundation, Inc.
+Francois Pinard <pinard@iro.umontreal.ca>, 1992.
+
+The following are more or less in decreasing order of priority.
+
+* Use rx instead of regex.
+
+* Correct the infinite loop using -S '$' or -S '^'.
+
+* Use mmap for swallowing files (maybe wrong when memory edited).
+
+* Understand and mimic `-t' option, if I can.
+
+* Sort keywords intelligently for Latin-1 code. See how to interface
+this character set with various output formats. Also, introduce
+options to inverse-sort and possibly to reverse-sort.
+
+* Improve speed for Ignore and Only tables. Consider hashing instead
+of sorting. Consider playing with obstacks to digest them.
+
+* Provide better handling of format effectors obtained from input, and
+also attempt white space compression on output which would still
+maximize full output width usage.
+
+* See how TeX mode could be made more useful, and if a texinfo mode
+would mean something to someone.
+
+* Provide multiple language support
+
+Most of the boosting work should go along the line of fast recognition
+of multiple and complex boundaries, which define various `languages'.
+Each such language has its own rules for words, sentences, paragraphs,
+and reporting requests. This is less difficult than I first thought:
+
+ . Recognize language modifiers with each option. At least -b, -i, -o,
+-W, -S, and also new language switcher options, will have such
+modifiers. Modifiers on language switchers will allow or disallow
+language transitions.
+
+ . Complete the transformation of underlying variables into arrays in
+the code.
+
+ . Implement a heap of positions in the input file. There is one entry
+in the heap for each compiled regexp; it is initialized by a re_search
+after each regexp compile. Regexps reschedule themselves in the heap
+when their position passes while scanning input. In this way, looking
+simultaneously for a lot of regexps should not be too inefficient,
+once the scanning starts. If this works ok, maybe consider accepting
+regexps in Only and Ignore tables.
+
+ . Merge with language processing boundary processing options, really
+integrating -S processing as a special case. Maybe, implement several
+level of boundaries. See how to implement a stack of languages, for
+handling quotations. See if more sophisticated references could be
+handled as another special case of a language.
+
+* Tackle other aspects, in a more long term view
+
+ . Add options for statistics, frequency lists, referencing, and all
+other prescreening tools and subsidiary tasks of concordance
+production.
+
+ . Develop an interactive mode. Even better, construct a GNU emacs
+interface. I'm looking at Gene Myers <gene@cs.arizona.edu> suffix
+arrays as a possible implementation along those ideas.
+
+ . Implement hooks so word classification and tagging should be merged
+in. See how to effectively hook in lemmatisation or other
+morphological features. It is far from being clear by now how to
+interface this correctly, so some experimentation is mandatory.
+
+ . Profile and speed up the whole thing.
+
+ . Make it work on small address space machines. Consider three levels
+of hugeness for files, and three corresponding algorithms to make
+optimal use of memory. The first case is when all the input files and
+all the word references fit in memory: this is the case currently
+implemented. The second case is when the files cannot fit all together
+in memory, but the word references do. The third case is when even
+the word references cannot fit in memory.
+
+ . There also are subsidiary developments for in-core incremental sort
+routines as well as for external sort packages. The need for more
+flexible sort packages comes partly from the fact that linguists use
+kinds of keys which compare in unusual and more sophisticated ways.
+GNU `sort' and `ptx' could evolve together.
+
+
+Local Variables:
+mode: outline
+outline-regexp: " *[-+*.] \\| "
+eval: (hide-body)
+End:
diff --git a/gnu/usr.bin/ptx/alloca.c b/gnu/usr.bin/ptx/alloca.c
new file mode 100644
index 0000000..bd4932a
--- /dev/null
+++ b/gnu/usr.bin/ptx/alloca.c
@@ -0,0 +1,484 @@
+/* alloca.c -- allocate automatically reclaimed memory
+ (Mostly) portable public-domain implementation -- D A Gwyn
+
+ This implementation of the PWB library alloca function,
+ which is used to allocate space off the run-time stack so
+ that it is automatically reclaimed upon procedure exit,
+ was inspired by discussions with J. Q. Johnson of Cornell.
+ J.Otto Tennant <jot@cray.com> contributed the Cray support.
+
+ There are some preprocessor constants that can
+ be defined when compiling for your specific system, for
+ improved efficiency; however, the defaults should be okay.
+
+ The general concept of this implementation is to keep
+ track of all alloca-allocated blocks, and reclaim any
+ that are found to be deeper in the stack than the current
+ invocation. This heuristic does not reclaim storage as
+ soon as it becomes invalid, but it will do so eventually.
+
+ As a special case, alloca(0) reclaims storage without
+ allocating any. It is a good idea to use alloca(0) in
+ your main control loop, etc. to force garbage collection. */
+
+#ifdef HAVE_CONFIG_H
+#if defined (emacs) || defined (CONFIG_BROKETS)
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
+/* If compiling with GCC 2, this file's not needed. */
+#if !defined (__GNUC__) || __GNUC__ < 2
+
+/* If someone has defined alloca as a macro,
+ there must be some other way alloca is supposed to work. */
+#ifndef alloca
+
+#ifdef emacs
+#ifdef static
+/* actually, only want this if static is defined as ""
+ -- this is for usg, in which emacs must undefine static
+ in order to make unexec workable
+ */
+#ifndef STACK_DIRECTION
+you
+lose
+-- must know STACK_DIRECTION at compile-time
+#endif /* STACK_DIRECTION undefined */
+#endif /* static */
+#endif /* emacs */
+
+/* If your stack is a linked list of frames, you have to
+ provide an "address metric" ADDRESS_FUNCTION macro. */
+
+#if defined (CRAY) && defined (CRAY_STACKSEG_END)
+long i00afunc ();
+#define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg))
+#else
+#define ADDRESS_FUNCTION(arg) &(arg)
+#endif
+
+#if __STDC__
+typedef void *pointer;
+#else
+typedef char *pointer;
+#endif
+
+#define NULL 0
+
+/* Different portions of Emacs need to call different versions of
+ malloc. The Emacs executable needs alloca to call xmalloc, because
+ ordinary malloc isn't protected from input signals. On the other
+ hand, the utilities in lib-src need alloca to call malloc; some of
+ them are very simple, and don't have an xmalloc routine.
+
+ Non-Emacs programs expect this to call use xmalloc.
+
+ Callers below should use malloc. */
+
+#ifndef emacs
+#define malloc xmalloc
+#endif
+extern pointer malloc ();
+
+/* Define STACK_DIRECTION if you know the direction of stack
+ growth for your system; otherwise it will be automatically
+ deduced at run-time.
+
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown */
+
+#ifndef STACK_DIRECTION
+#define STACK_DIRECTION 0 /* Direction unknown. */
+#endif
+
+#if STACK_DIRECTION != 0
+
+#define STACK_DIR STACK_DIRECTION /* Known at compile-time. */
+
+#else /* STACK_DIRECTION == 0; need run-time code. */
+
+static int stack_dir; /* 1 or -1 once known. */
+#define STACK_DIR stack_dir
+
+static void
+find_stack_direction ()
+{
+ static char *addr = NULL; /* Address of first `dummy', once known. */
+ auto char dummy; /* To get stack address. */
+
+ if (addr == NULL)
+ { /* Initial entry. */
+ addr = ADDRESS_FUNCTION (dummy);
+
+ find_stack_direction (); /* Recurse once. */
+ }
+ else
+ {
+ /* Second entry. */
+ if (ADDRESS_FUNCTION (dummy) > addr)
+ stack_dir = 1; /* Stack grew upward. */
+ else
+ stack_dir = -1; /* Stack grew downward. */
+ }
+}
+
+#endif /* STACK_DIRECTION == 0 */
+
+/* An "alloca header" is used to:
+ (a) chain together all alloca'ed blocks;
+ (b) keep track of stack depth.
+
+ It is very important that sizeof(header) agree with malloc
+ alignment chunk size. The following default should work okay. */
+
+#ifndef ALIGN_SIZE
+#define ALIGN_SIZE sizeof(double)
+#endif
+
+typedef union hdr
+{
+ char align[ALIGN_SIZE]; /* To force sizeof(header). */
+ struct
+ {
+ union hdr *next; /* For chaining headers. */
+ char *deep; /* For stack depth measure. */
+ } h;
+} header;
+
+static header *last_alloca_header = NULL; /* -> last alloca header. */
+
+/* Return a pointer to at least SIZE bytes of storage,
+ which will be automatically reclaimed upon exit from
+ the procedure that called alloca. Originally, this space
+ was supposed to be taken from the current stack frame of the
+ caller, but that method cannot be made to work for some
+ implementations of C, for example under Gould's UTX/32. */
+
+pointer
+alloca (size)
+ unsigned size;
+{
+ auto char probe; /* Probes stack depth: */
+ register char *depth = ADDRESS_FUNCTION (probe);
+
+#if STACK_DIRECTION == 0
+ if (STACK_DIR == 0) /* Unknown growth direction. */
+ find_stack_direction ();
+#endif
+
+ /* Reclaim garbage, defined as all alloca'd storage that
+ was allocated from deeper in the stack than currently. */
+
+ {
+ register header *hp; /* Traverses linked list. */
+
+ for (hp = last_alloca_header; hp != NULL;)
+ if ((STACK_DIR > 0 && hp->h.deep > depth)
+ || (STACK_DIR < 0 && hp->h.deep < depth))
+ {
+ register header *np = hp->h.next;
+
+ free ((pointer) hp); /* Collect garbage. */
+
+ hp = np; /* -> next header. */
+ }
+ else
+ break; /* Rest are not deeper. */
+
+ last_alloca_header = hp; /* -> last valid storage. */
+ }
+
+ if (size == 0)
+ return NULL; /* No allocation required. */
+
+ /* Allocate combined header + user data storage. */
+
+ {
+ register pointer new = malloc (sizeof (header) + size);
+ /* Address of header. */
+
+ ((header *) new)->h.next = last_alloca_header;
+ ((header *) new)->h.deep = depth;
+
+ last_alloca_header = (header *) new;
+
+ /* User storage begins just after header. */
+
+ return (pointer) ((char *) new + sizeof (header));
+ }
+}
+
+#if defined (CRAY) && defined (CRAY_STACKSEG_END)
+
+#ifdef DEBUG_I00AFUNC
+#include <stdio.h>
+#endif
+
+#ifndef CRAY_STACK
+#define CRAY_STACK
+#ifndef CRAY2
+/* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */
+struct stack_control_header
+ {
+ long shgrow:32; /* Number of times stack has grown. */
+ long shaseg:32; /* Size of increments to stack. */
+ long shhwm:32; /* High water mark of stack. */
+ long shsize:32; /* Current size of stack (all segments). */
+ };
+
+/* The stack segment linkage control information occurs at
+ the high-address end of a stack segment. (The stack
+ grows from low addresses to high addresses.) The initial
+ part of the stack segment linkage control information is
+ 0200 (octal) words. This provides for register storage
+ for the routine which overflows the stack. */
+
+struct stack_segment_linkage
+ {
+ long ss[0200]; /* 0200 overflow words. */
+ long sssize:32; /* Number of words in this segment. */
+ long ssbase:32; /* Offset to stack base. */
+ long:32;
+ long sspseg:32; /* Offset to linkage control of previous
+ segment of stack. */
+ long:32;
+ long sstcpt:32; /* Pointer to task common address block. */
+ long sscsnm; /* Private control structure number for
+ microtasking. */
+ long ssusr1; /* Reserved for user. */
+ long ssusr2; /* Reserved for user. */
+ long sstpid; /* Process ID for pid based multi-tasking. */
+ long ssgvup; /* Pointer to multitasking thread giveup. */
+ long sscray[7]; /* Reserved for Cray Research. */
+ long ssa0;
+ long ssa1;
+ long ssa2;
+ long ssa3;
+ long ssa4;
+ long ssa5;
+ long ssa6;
+ long ssa7;
+ long sss0;
+ long sss1;
+ long sss2;
+ long sss3;
+ long sss4;
+ long sss5;
+ long sss6;
+ long sss7;
+ };
+
+#else /* CRAY2 */
+/* The following structure defines the vector of words
+ returned by the STKSTAT library routine. */
+struct stk_stat
+ {
+ long now; /* Current total stack size. */
+ long maxc; /* Amount of contiguous space which would
+ be required to satisfy the maximum
+ stack demand to date. */
+ long high_water; /* Stack high-water mark. */
+ long overflows; /* Number of stack overflow ($STKOFEN) calls. */
+ long hits; /* Number of internal buffer hits. */
+ long extends; /* Number of block extensions. */
+ long stko_mallocs; /* Block allocations by $STKOFEN. */
+ long underflows; /* Number of stack underflow calls ($STKRETN). */
+ long stko_free; /* Number of deallocations by $STKRETN. */
+ long stkm_free; /* Number of deallocations by $STKMRET. */
+ long segments; /* Current number of stack segments. */
+ long maxs; /* Maximum number of stack segments so far. */
+ long pad_size; /* Stack pad size. */
+ long current_address; /* Current stack segment address. */
+ long current_size; /* Current stack segment size. This
+ number is actually corrupted by STKSTAT to
+ include the fifteen word trailer area. */
+ long initial_address; /* Address of initial segment. */
+ long initial_size; /* Size of initial segment. */
+ };
+
+/* The following structure describes the data structure which trails
+ any stack segment. I think that the description in 'asdef' is
+ out of date. I only describe the parts that I am sure about. */
+
+struct stk_trailer
+ {
+ long this_address; /* Address of this block. */
+ long this_size; /* Size of this block (does not include
+ this trailer). */
+ long unknown2;
+ long unknown3;
+ long link; /* Address of trailer block of previous
+ segment. */
+ long unknown5;
+ long unknown6;
+ long unknown7;
+ long unknown8;
+ long unknown9;
+ long unknown10;
+ long unknown11;
+ long unknown12;
+ long unknown13;
+ long unknown14;
+ };
+
+#endif /* CRAY2 */
+#endif /* not CRAY_STACK */
+
+#ifdef CRAY2
+/* Determine a "stack measure" for an arbitrary ADDRESS.
+ I doubt that "lint" will like this much. */
+
+static long
+i00afunc (long *address)
+{
+ struct stk_stat status;
+ struct stk_trailer *trailer;
+ long *block, size;
+ long result = 0;
+
+ /* We want to iterate through all of the segments. The first
+ step is to get the stack status structure. We could do this
+ more quickly and more directly, perhaps, by referencing the
+ $LM00 common block, but I know that this works. */
+
+ STKSTAT (&status);
+
+ /* Set up the iteration. */
+
+ trailer = (struct stk_trailer *) (status.current_address
+ + status.current_size
+ - 15);
+
+ /* There must be at least one stack segment. Therefore it is
+ a fatal error if "trailer" is null. */
+
+ if (trailer == 0)
+ abort ();
+
+ /* Discard segments that do not contain our argument address. */
+
+ while (trailer != 0)
+ {
+ block = (long *) trailer->this_address;
+ size = trailer->this_size;
+ if (block == 0 || size == 0)
+ abort ();
+ trailer = (struct stk_trailer *) trailer->link;
+ if ((block <= address) && (address < (block + size)))
+ break;
+ }
+
+ /* Set the result to the offset in this segment and add the sizes
+ of all predecessor segments. */
+
+ result = address - block;
+
+ if (trailer == 0)
+ {
+ return result;
+ }
+
+ do
+ {
+ if (trailer->this_size <= 0)
+ abort ();
+ result += trailer->this_size;
+ trailer = (struct stk_trailer *) trailer->link;
+ }
+ while (trailer != 0);
+
+ /* We are done. Note that if you present a bogus address (one
+ not in any segment), you will get a different number back, formed
+ from subtracting the address of the first block. This is probably
+ not what you want. */
+
+ return (result);
+}
+
+#else /* not CRAY2 */
+/* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP.
+ Determine the number of the cell within the stack,
+ given the address of the cell. The purpose of this
+ routine is to linearize, in some sense, stack addresses
+ for alloca. */
+
+static long
+i00afunc (long address)
+{
+ long stkl = 0;
+
+ long size, pseg, this_segment, stack;
+ long result = 0;
+
+ struct stack_segment_linkage *ssptr;
+
+ /* Register B67 contains the address of the end of the
+ current stack segment. If you (as a subprogram) store
+ your registers on the stack and find that you are past
+ the contents of B67, you have overflowed the segment.
+
+ B67 also points to the stack segment linkage control
+ area, which is what we are really interested in. */
+
+ stkl = CRAY_STACKSEG_END ();
+ ssptr = (struct stack_segment_linkage *) stkl;
+
+ /* If one subtracts 'size' from the end of the segment,
+ one has the address of the first word of the segment.
+
+ If this is not the first segment, 'pseg' will be
+ nonzero. */
+
+ pseg = ssptr->sspseg;
+ size = ssptr->sssize;
+
+ this_segment = stkl - size;
+
+ /* It is possible that calling this routine itself caused
+ a stack overflow. Discard stack segments which do not
+ contain the target address. */
+
+ while (!(this_segment <= address && address <= stkl))
+ {
+#ifdef DEBUG_I00AFUNC
+ fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl);
+#endif
+ if (pseg == 0)
+ break;
+ stkl = stkl - pseg;
+ ssptr = (struct stack_segment_linkage *) stkl;
+ size = ssptr->sssize;
+ pseg = ssptr->sspseg;
+ this_segment = stkl - size;
+ }
+
+ result = address - this_segment;
+
+ /* If you subtract pseg from the current end of the stack,
+ you get the address of the previous stack segment's end.
+ This seems a little convoluted to me, but I'll bet you save
+ a cycle somewhere. */
+
+ while (pseg != 0)
+ {
+#ifdef DEBUG_I00AFUNC
+ fprintf (stderr, "%011o %011o\n", pseg, size);
+#endif
+ stkl = stkl - pseg;
+ ssptr = (struct stack_segment_linkage *) stkl;
+ size = ssptr->sssize;
+ pseg = ssptr->sspseg;
+ result += size;
+ }
+ return (result);
+}
+
+#endif /* not CRAY2 */
+#endif /* CRAY */
+
+#endif /* no alloca */
+#endif /* not GCC version 2 */
diff --git a/gnu/usr.bin/ptx/argmatch.c b/gnu/usr.bin/ptx/argmatch.c
new file mode 100644
index 0000000..17e088b
--- /dev/null
+++ b/gnu/usr.bin/ptx/argmatch.c
@@ -0,0 +1,94 @@
+/* argmatch.c -- find a match for a string in an array
+ Copyright (C) 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by David MacKenzie <djm@ai.mit.edu> */
+
+#ifdef HAVE_CONFIG_H
+#if defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
+#include <stdio.h>
+#ifdef STDC_HEADERS
+#include <string.h>
+#endif
+
+extern char *program_name;
+
+/* If ARG is an unambiguous match for an element of the
+ null-terminated array OPTLIST, return the index in OPTLIST
+ of the matched element, else -1 if it does not match any element
+ or -2 if it is ambiguous (is a prefix of more than one element). */
+
+int
+argmatch (arg, optlist)
+ char *arg;
+ char **optlist;
+{
+ int i; /* Temporary index in OPTLIST. */
+ int arglen; /* Length of ARG. */
+ int matchind = -1; /* Index of first nonexact match. */
+ int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */
+
+ arglen = strlen (arg);
+
+ /* Test all elements for either exact match or abbreviated matches. */
+ for (i = 0; optlist[i]; i++)
+ {
+ if (!strncmp (optlist[i], arg, arglen))
+ {
+ if (strlen (optlist[i]) == arglen)
+ /* Exact match found. */
+ return i;
+ else if (matchind == -1)
+ /* First nonexact match found. */
+ matchind = i;
+ else
+ /* Second nonexact match found. */
+ ambiguous = 1;
+ }
+ }
+ if (ambiguous)
+ return -2;
+ else
+ return matchind;
+}
+
+/* Error reporting for argmatch.
+ KIND is a description of the type of entity that was being matched.
+ VALUE is the invalid value that was given.
+ PROBLEM is the return value from argmatch. */
+
+void
+invalid_arg (kind, value, problem)
+ char *kind;
+ char *value;
+ int problem;
+{
+ fprintf (stderr, "%s: ", program_name);
+ if (problem == -1)
+ fprintf (stderr, "invalid");
+ else /* Assume -2. */
+ fprintf (stderr, "ambiguous");
+ fprintf (stderr, " %s `%s'\n", kind, value);
+}
diff --git a/gnu/usr.bin/ptx/bumpalloc.h b/gnu/usr.bin/ptx/bumpalloc.h
new file mode 100644
index 0000000..bbf901f
--- /dev/null
+++ b/gnu/usr.bin/ptx/bumpalloc.h
@@ -0,0 +1,58 @@
+/* BUMP_ALLOC macro - increase table allocation by one element.
+ Copyright (C) 1990, 1991, 1993 Free Software Foundation, Inc.
+ Francois Pinard <pinard@iro.umontreal.ca>, 1990.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*-------------------------------------------------------------------------.
+| Bump the allocation of the array pointed to by TABLE whenever required. |
+| The table already has already COUNT elements in it, this macro ensure it |
+| has enough space to accommodate at least one more element. Space is |
+| allocated (2 ^ EXPONENT) elements at a time. Each element of the array |
+| is of type TYPE. |
+`-------------------------------------------------------------------------*/
+
+/* Routines `xmalloc' and `xrealloc' are called to do the actual memory
+ management. This implies that the program will abort with an `Virtual
+ Memory exhausted!' error if any problem arise.
+
+ To work correctly, at least EXPONENT and TYPE should always be the
+ same for all uses of this macro for any given TABLE. A secure way to
+ achieve this is to never use this macro directly, but use it to define
+ other macros, which would then be TABLE-specific.
+
+ The first time through, COUNT is usually zero. Note that COUNT is not
+ updated by this macro, but it should be update elsewhere, later. This
+ is convenient, because it allows TABLE[COUNT] to refer to the new
+ element at the end. Once its construction is completed, COUNT++ will
+ record it in the table. Calling this macro several times in a row
+ without updating COUNT is a bad thing to do. */
+
+#define BUMP_ALLOC(Table, Count, Exponent, Type) \
+ BUMP_ALLOC_WITH_SIZE ((Table), (Count), (Exponent), Type, sizeof (Type))
+
+/* In cases `sizeof TYPE' would not always yield the correct value for
+ the size of each element entry, this macro accepts a supplementary
+ SIZE argument. The EXPONENT, TYPE and SIZE parameters should still
+ have the same value for all macro calls related to a specific TABLE. */
+
+#define BUMP_ALLOC_WITH_SIZE(Table, Count, Exponent, Type, Size) \
+ if (((Count) & (~(~0 << (Exponent)))) == 0) \
+ if ((Count) == 0) \
+ (Table) = (Type *) xmalloc ((1 << (Exponent)) * (Size)); \
+ else \
+ (Table) = (Type *) \
+ xrealloc ((Table), ((Count) + (1 << (Exponent))) * (Size)); \
+ else
diff --git a/gnu/usr.bin/ptx/check-out b/gnu/usr.bin/ptx/check-out
new file mode 100644
index 0000000..4d13c48
--- /dev/null
+++ b/gnu/usr.bin/ptx/check-out
@@ -0,0 +1,65 @@
+:30: /ranslate to certain respons ibilities for you if you distr/
+:183: c/ These actions are proh ibited by law if you do not ac
+:278: AS BEEN ADVISED OF THE POSS IBILITY OF SUCH DAMAGES. /Y H
+:232: /his License may add an expl icit geographical distribution/
+:267: /COST OF ALL NECESSARY SERV ICING, REPAIR OR CORRECTION.
+:216: /ht claims or to contest val idity of any such claims; this/
+:45: e/ If the software is mod ified by someone else and pass
+:57: pying, distribution and mod ification follow. /for co
+:60: /PYING, DISTRIBUTION AND MOD IFICATION 0. This License a/
+:68: /either verbatim or with mod ifications and/or translated i/
+:70: limitation in the term "mod ification".) /ithout
+:72: /pying, distribution and mod ification are not covered by t/
+:92: /opy and distribute such mod ifications or work under the t/
+:95: /a) You must cause the mod ified files to carry prominent/
+:103: ommands in/ c) If the mod ified program normally reads c
+:114: quirements apply to the mod ified work as a whole. /se re
+:115: are not derived/ If ident ifiable sections of that work
+:156: of the work for making mod ifications to it. /ed form
+:243: Lice/ If the Program spec ifies a version number of this
+:46: /hat they have is not the or iginal, so that any problems i/
+:47: /will not reflect on the or iginal authors' reputations.
+:191: /eives a license from the or iginal licensor to copy, distr/
+:231: /yrighted interfaces, the or iginal copyright holder who pl/
+:265: /ED WARRANTIES OF MERCHANTAB ILITY AND FITNESS FOR A PARTIC/
+:274: /NG OUT OF THE USE OR INAB ILITY TO USE THE PROGRAM (INCL/
+:303: /warranty of MERCHANTAB ILITY or FITNESS FOR A PARTICU/
+:69: /ation is included without l imitation in the term "modific/
+:198: /for any other reason (not l imited to patent issues), cond/
+:232: /geographical distribution l imitation excluding those coun/
+:235: /License incorporates the l imitation as if written in the/
+:239: Such new versions will be s imilar in spirit to the presen/
+:264: /PLIED, INCLUDING, BUT NOT L IMITED TO, THE IMPLIED WARRANT/
+:274: /ROGRAM (INCLUDING BUT NOT L IMITED TO LOSS OF DATA OR DATA/
+:67: /hat is to say, a work conta ining the Program or a portion/
+:158: /ny associated interface def inition files, plus the script/
+:34: /fee, you must give the rec ipients all the rights that yo/
+:46: /passed on, we want its rec ipients to know that what they/
+:84: /nty; and give any other rec ipients of the Program a copy/
+:190: /ed on the Program), the rec ipient automatically receives/
+:193: /her restrictions on the rec ipients' exercise of the right/
+:239: /sions will be similar in sp irit to the present version, b/
+:254: o goals of prese/ Our dec ision will be guided by the tw
+:273: /OR CONSEQUENTIAL DAMAGES AR ISING OUT OF THE USE OR INAB/
+:315: /teractive mode: Gnomov ision version 69, Copyright (C/
+:316: /y name of author Gnomov ision comes with ABSOLUTELY NO/
+:330: /st in the program `Gnomov ision' (which makes passes at/
+:30: /late to certain responsibil ities for you if you distribut/
+:56: The precise terms and cond itions for copying, distributi/
+:60: /C LICENSE TERMS AND COND ITIONS FOR COPYING, DISTRIBUTI/
+:93: /also meet all of these cond itions: a) You must cause/
+:109: /rogram under these cond itions, and telling the user h/
+:129: ther work not bas/ In add ition, mere aggregation of ano
+:186: /and all its terms and cond itions for copying, distributi/
+:192: ect to these terms and cond itions. /am subj
+:199: /ted to patent issues), cond itions are imposed on you (whe/
+:200: /e) that contradict the cond itions of this License, they d/
+:201: ot excuse you from the cond itions of this License. /do n
+:244: /ollowing the terms and cond itions either of that version/
+:251: /ams whose distribution cond itions are different, write to/
+:262: /WHEN OTHERWISE STATED IN WR ITING THE COPYRIGHT HOLDERS AN/
+:270: /ABLE LAW OR AGREED TO IN WR ITING WILL ANY COPYRIGHT HOLDE/
+:280: ly/ END OF TERMS AND COND ITIONS Appendix: How to App
+:318: /e it under certain cond itions; type `show c' for deta/
+:52: /of a free program will ind ividually obtain patent licens/
+:72: stribution and mod/ Act ivities other than copying, di
diff --git a/gnu/usr.bin/ptx/config.h b/gnu/usr.bin/ptx/config.h
new file mode 100644
index 0000000..93e7ed1
--- /dev/null
+++ b/gnu/usr.bin/ptx/config.h
@@ -0,0 +1,57 @@
+/* config.h. Generated automatically by configure. */
+/* config.h.in. Generated automatically from configure.in by autoheader. */
+
+/* Define if using alloca.c. */
+/* #undef C_ALLOCA */
+
+/* Define if type char is unsigned and you are not using gcc. */
+/* #undef __CHAR_UNSIGNED__ */
+
+/* Define to empty if the keyword does not work. */
+/* #undef const */
+
+/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems.
+ This function is required for alloca.c support on those systems. */
+/* #undef CRAY_STACKSEG_END */
+
+/* Define if you have alloca.h and it should be used (not Ultrix). */
+/* #undef HAVE_ALLOCA_H */
+
+/* Define if you don't have vprintf but do have _doprnt. */
+/* #undef HAVE_DOPRNT */
+
+/* Define if you have the vprintf function. */
+#define HAVE_VPRINTF 1
+
+/* Define if you need to in order for stat and other things to work. */
+/* #undef _POSIX_SOURCE */
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at run-time.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown
+ */
+/* #undef STACK_DIRECTION */
+
+/* Define if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* In regex, request the capability of modifying the letter syntax. */
+#define SYNTAX_TABLE 1
+
+/* In regex, use 8 bits per character. */
+#define CHAR_SET_SIZE 256
+
+/* Define if you have mcheck. */
+/* #undef HAVE_MCHECK */
+
+/* Define if you have setchrclass. */
+/* #undef HAVE_SETCHRCLASS */
+
+/* Define if you have strerror. */
+#define HAVE_STRERROR 1
+
+/* Define if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
diff --git a/gnu/usr.bin/ptx/diacrit.c b/gnu/usr.bin/ptx/diacrit.c
new file mode 100644
index 0000000..29e319b
--- /dev/null
+++ b/gnu/usr.bin/ptx/diacrit.c
@@ -0,0 +1,148 @@
+/* Diacritics processing for a few character codes.
+ Copyright (C) 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+ Francois Pinard <pinard@iro.umontreal.ca>, 1988.
+
+ All this file is a temporary hack, waiting for locales in GNU.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "diacrit.h"
+
+/* ISO 8859-1 Latin-1 code is used as the underlying character set. If
+ MSDOS is defined, IBM-PC's character set code is used instead. */
+
+/*--------------------------------------------------------------------.
+| For each alphabetic character, returns what it would be without its |
+| possible diacritic symbol. |
+`--------------------------------------------------------------------*/
+
+const char diacrit_base[256] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+ 'X', 'Y', 'Z', 0, 0, 0, 0, 0,
+ 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+ 'x', 'y', 'z', 0, 0, 0, 0, 0,
+
+#ifdef MSDOS
+
+ 'C', 'u', 'e', 'a', 'a', 'a', 'a', 'c',
+ 'e', 'e', 'e', 'i', 'i', 'i', 'A', 'A',
+ 'E', 'e', 'E', 'o', 'o', 'o', 'u', 'u',
+ 'y', 'O', 'U', 0, 0, 0, 0, 0,
+ 'a', 'i', 'o', 'u', 'n', 'N', 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+#else /* not MSDOS */
+
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'C',
+ 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I',
+ 0, 'N', 'O', 'O', 'O', 'O', 'O', 0,
+ 'O', 'U', 'U', 'U', 'U', 'Y', 0, 0,
+ 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'c',
+ 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i',
+ 0, 'n', 'o', 'o', 'o', 'o', 'o', 0,
+ 'o', 'u', 'u', 'u', 'u', 'y', 0, 'y',
+
+#endif /* not MSDOS */
+};
+
+/*------------------------------------------------------------------------.
+| For each alphabetic character, returns a code of what its diacritic is, |
+| according to the following codes: 1 (eE) over aA for latin diphtongs; 2 |
+| (') acute accent; 3 (`) grave accent; 4 (^) circumflex accent; 5 (") |
+| umlaut or diaraesis; 6 (~) tilda; 7 (,) cedilla; 8 (o) covering degree |
+| symbol; 9 (|) slashed character. |
+`------------------------------------------------------------------------*/
+
+const char diacrit_diac[256] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 4, 0,
+ 3, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 6, 0,
+
+#ifdef MSDOS
+
+ 7, 5, 2, 4, 5, 3, 8, 7,
+ 4, 5, 3, 5, 4, 3, 5, 8,
+ 2, 1, 1, 4, 5, 3, 4, 3,
+ 5, 5, 5, 0, 0, 0, 0, 0,
+ 2, 2, 2, 2, 6, 6, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+#else /* not MSDOS */
+
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 3, 2, 4, 6, 5, 8, 1, 7,
+ 3, 2, 4, 5, 3, 2, 4, 5,
+ 0, 6, 3, 2, 4, 6, 5, 0,
+ 9, 3, 2, 4, 5, 2, 0, 0,
+ 3, 2, 4, 6, 5, 8, 1, 7,
+ 3, 2, 4, 5, 3, 2, 4, 5,
+ 0, 6, 3, 2, 4, 6, 5, 0,
+ 9, 3, 2, 4, 5, 2, 0, 0,
+
+#endif /* not MSDOS */
+};
diff --git a/gnu/usr.bin/ptx/diacrit.h b/gnu/usr.bin/ptx/diacrit.h
new file mode 100644
index 0000000..c880a45
--- /dev/null
+++ b/gnu/usr.bin/ptx/diacrit.h
@@ -0,0 +1,16 @@
+/* Diacritics processing for a few character codes.
+ Copyright (C) 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+ Francois Pinard <pinard@iro.umontreal.ca>, 1988.
+
+ All this file is a temporary hack, waiting for locales in GNU.
+*/
+
+extern const char diacrit_base[]; /* characters without diacritics */
+extern const char diacrit_diac[]; /* diacritic code for each character */
+
+/* Returns CHR without its diacritic. CHR is known to be alphabetic. */
+#define tobase(chr) (diacrit_base[(unsigned char) (chr)])
+
+/* Returns a diacritic code for CHR. CHR is known to be alphabetic. */
+#define todiac(chr) (diacrit_diac[(unsigned char) (chr)])
+
diff --git a/gnu/usr.bin/ptx/error.c b/gnu/usr.bin/ptx/error.c
new file mode 100644
index 0000000..41d66fb
--- /dev/null
+++ b/gnu/usr.bin/ptx/error.c
@@ -0,0 +1,117 @@
+/* error.c -- error handler for noninteractive utilities
+ Copyright (C) 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by David MacKenzie. */
+
+#ifdef HAVE_CONFIG_H
+#if defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
+#include <stdio.h>
+
+#ifdef HAVE_VPRINTF
+
+#if __STDC__
+#include <stdarg.h>
+#define VA_START(args, lastarg) va_start(args, lastarg)
+#else /* !__STDC__ */
+#include <varargs.h>
+#define VA_START(args, lastarg) va_start(args)
+#endif /* !__STDC__ */
+
+#else /* !HAVE_VPRINTF */
+
+#ifdef HAVE_DOPRNT
+#define va_alist args
+#define va_dcl int args;
+#else /* !HAVE_DOPRNT */
+#define va_alist a1, a2, a3, a4, a5, a6, a7, a8
+#define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
+#endif /* !HAVE_DOPRNT */
+
+#endif /* !HAVE_VPRINTF */
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#include <string.h>
+#else /* !STDC_HEADERS */
+void exit ();
+#endif /* !STDC_HEADERS */
+
+extern char *program_name;
+
+#ifndef HAVE_STRERROR
+static char *
+private_strerror (errnum)
+ int errnum;
+{
+ extern char *sys_errlist[];
+ extern int sys_nerr;
+
+ if (errnum > 0 && errnum <= sys_nerr)
+ return sys_errlist[errnum];
+ return "Unknown system error";
+}
+#define strerror private_strerror
+#endif /* !HAVE_STRERROR */
+
+/* Print the program name and error message MESSAGE, which is a printf-style
+ format string with optional args.
+ If ERRNUM is nonzero, print its corresponding system error message.
+ Exit with status STATUS if it is nonzero. */
+/* VARARGS */
+void
+#if defined (HAVE_VPRINTF) && __STDC__
+error (int status, int errnum, char *message, ...)
+#else /* !HAVE_VPRINTF or !__STDC__ */
+error (status, errnum, message, va_alist)
+ int status;
+ int errnum;
+ char *message;
+ va_dcl
+#endif /* !HAVE_VPRINTF or !__STDC__ */
+{
+#ifdef HAVE_VPRINTF
+ va_list args;
+#endif /* HAVE_VPRINTF */
+
+ fprintf (stderr, "%s: ", program_name);
+#ifdef HAVE_VPRINTF
+ VA_START (args, message);
+ vfprintf (stderr, message, args);
+ va_end (args);
+#else /* !HAVE_VPRINTF */
+#ifdef HAVE_DOPRNT
+ _doprnt (message, &args, stderr);
+#else /* !HAVE_DOPRNT */
+ fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif /* !HAVE_DOPRNT */
+#endif /* !HAVE_VPRINTF */
+ if (errnum)
+ fprintf (stderr, ": %s", strerror (errnum));
+ putc ('\n', stderr);
+ fflush (stderr);
+ if (status)
+ exit (status);
+}
diff --git a/gnu/usr.bin/ptx/examples/README b/gnu/usr.bin/ptx/examples/README
new file mode 100644
index 0000000..038034f
--- /dev/null
+++ b/gnu/usr.bin/ptx/examples/README
@@ -0,0 +1,21 @@
+Various examples of GNU ptx usages.
+Francois Pinard <pinard@iro.umontreal.ca>, 1993.
+
+This directory contains a few examples contributed by GNU ptx users.
+Feel free to look at them for tricks or ideas. When an example
+requires many files, a subdirectory is used to hold them together.
+I have not necessarily tested these examples recently, if at all.
+
+If you have examples you would like to share, please submit them to
+me. You may also submit corrections to the examples given in this
+directory, however, please write to the authors first, since they most
+probably will like to have their say about their own contribution.
+
+* include.pl: A Perl script studying system include files.
+
+* luke/: A shell script permuting indices for man pages. It contains
+two examples of an .xx definition for *roff, one simple, one complex.
+
+* latex/: A simple example of \xx definition for latex.
+
+* ajay/: A more complex application of latex with ptx.
diff --git a/gnu/usr.bin/ptx/examples/ajay/Makefile b/gnu/usr.bin/ptx/examples/ajay/Makefile
new file mode 100644
index 0000000..bff099c
--- /dev/null
+++ b/gnu/usr.bin/ptx/examples/ajay/Makefile
@@ -0,0 +1,28 @@
+JUNKFILES = tip-index.ps tip-index.dvi tip-index.tex tip-index.log \
+ tip-index.aux
+
+tip-index.ps : tip-index.dvi
+ dvips tip-index.dvi
+
+tip-index.dvi : tip-index.tex
+ latex tip-index.tex
+
+tip-index.tex : tip.texified header.tex footer.tex
+ cat header.tex tip.texified footer.tex > tip-index.tex
+
+tip.texified : tip.eign tip.forgptx Makefile
+ gptx -f -r -i ./tip.eign -T < tip.forgptx | x.pl > tip.texified
+
+tip.eign : /usr/lib/eign exclude-words
+ cat /usr/lib/eign exclude-words > tip.eign
+
+screenlist : tip.texified
+ cat tip.texified \
+ | gawk -F\{ '{count[$$4]++} \
+ END {for (s in count) printf("%d %20s\n", count[s], s)}' \
+ | tr -d '}' \
+ | sort -n > screenlist
+ @echo "Check (say) the last 100 lines of ./screenlist".
+
+clean :
+ rm -f tip.eign tip.texified $(JUNKFILES) screenlist
diff --git a/gnu/usr.bin/ptx/examples/ajay/README b/gnu/usr.bin/ptx/examples/ajay/README
new file mode 100644
index 0000000..7b55ca2
--- /dev/null
+++ b/gnu/usr.bin/ptx/examples/ajay/README
@@ -0,0 +1,41 @@
+To: pinard@iro.umontreal.ca
+Subject: Re: Gptx suggestions and help request
+Date: Tue, 28 Sep 93 11:30:04 +0500
+From: ajayshah@cmie.ernet.in
+
+[...] My plaintext input looks like: "pagenum multiword-phrase" where
+the multiword phrase is atmost five words. So [...], I'm doing two
+columns in small type.
+
+I got one of the programmers here to write me a tex macro for my
+problem. When it goes into production I'll mail you a few files: a
+sample input, the gptx command, the output, and the tex macro. If you
+find these interesting you can ship them with future gptx releases.
+
+Thanks a lot for gptx. If you have a mailing list of loyal users,
+you can add us to it :-)
+
+
+To: pinard@iro.umontreal.ca
+Cc: rk@cmie.ernet.in
+Subject: All glue code I used with gptx
+Date: Tue, 05 Oct 93 15:23:44 +0500
+From: ajayshah@zigma.cmie.ernet.in
+
+That is a full set of a files for an example of "production use". You
+are welcome to post them, or use them as a sample supplied with the
+gptx distribution, etc., with absolutely no restrictions on what
+anyone does with this. In case you do so, please acknowledge the
+contribution of Rakesh Chauhan, rk@cmie.ernet.in, who is the author of
+x.pl and header.tex. [...]
+
+As you can tell, I used it for a 100% realworld problem, and it
+worked. Thanks a million. If you'd like, I can send you a hardcopy
+of the full finished document (just send me your mailing address). If
+you would like to mention the name of this document when you use
+these files as a demo, it is
+
+ Trends in Industrial Production
+ September 1993
+ Centre for Monitoring Indian Economy, Bombay, India.
+
diff --git a/gnu/usr.bin/ptx/examples/ajay/footer.tex b/gnu/usr.bin/ptx/examples/ajay/footer.tex
new file mode 100644
index 0000000..6b47932
--- /dev/null
+++ b/gnu/usr.bin/ptx/examples/ajay/footer.tex
@@ -0,0 +1 @@
+\end{document}
diff --git a/gnu/usr.bin/ptx/examples/ajay/header.tex b/gnu/usr.bin/ptx/examples/ajay/header.tex
new file mode 100644
index 0000000..04a9c64
--- /dev/null
+++ b/gnu/usr.bin/ptx/examples/ajay/header.tex
@@ -0,0 +1,21 @@
+\documentstyle [twocolumn,a4]{article}
+
+\pagestyle{empty}
+
+\textwidth 6.8in
+\oddsidemargin -.8in
+\evensidemargin -.8in
+\textheight 10in
+\topmargin -1in
+% \columnseprule 1pt
+
+\begin{document}
+
+\def\xx #1#2#3#4#5#6{\hbox to \hsize{%
+\hbox to 1.4in{\hfill #2}\hskip .05in%
+\hbox to .8in{\it #3\hfil}\hskip .05in%
+\hbox to 1.4in{#4\hfil}\hskip .05in%
+\hbox{\hfil #6}\hfil}%
+}
+
+\scriptsize
diff --git a/gnu/usr.bin/ptx/examples/ajay/tip.forgptx b/gnu/usr.bin/ptx/examples/ajay/tip.forgptx
new file mode 100644
index 0000000..ecf6e0e
--- /dev/null
+++ b/gnu/usr.bin/ptx/examples/ajay/tip.forgptx
@@ -0,0 +1,10 @@
+1 Zinc concentrate
+1 Coal
+1 Ball clay
+1 Non-coking coal
+1 Calcareous sand
+1 Natural Gas
+1 Chalk
+1 Bauxite
+1 Clay (others)
+1 Copper ore
diff --git a/gnu/usr.bin/ptx/examples/ajay/x.pl b/gnu/usr.bin/ptx/examples/ajay/x.pl
new file mode 100644
index 0000000..e0615ba
--- /dev/null
+++ b/gnu/usr.bin/ptx/examples/ajay/x.pl
@@ -0,0 +1,22 @@
+#! /usr/local/bin/perl
+
+while ($l = <>)
+{
+chop $l;
+
+$l =~ s/\\xx //;
+$l =~ s/}{/|/g;
+$l =~ s/{//g;
+$l =~ s/}//g;
+@x = split(/\|/, $l);
+
+printf ("\\xx ");
+for ($i = 0; $i <= $#x; $i++)
+ {
+ $v = substr($x[$i], 0, 17);
+ $v =~ s/\\$//;
+ printf("{%s}", $v);
+ }
+printf ("\n");
+
+}
diff --git a/gnu/usr.bin/ptx/examples/ignore/README b/gnu/usr.bin/ptx/examples/ignore/README
new file mode 100644
index 0000000..33ee19e
--- /dev/null
+++ b/gnu/usr.bin/ptx/examples/ignore/README
@@ -0,0 +1,65 @@
+From beebe@math.utah.edu Wed Oct 27 19:37:22 1993
+Date: Tue, 26 Oct 93 15:43:19 MDT
+From: "Nelson H. F. Beebe" <beebe@math.utah.edu>
+To: pinard@iro.umontreal.ca
+Subject: Re: Another short comment on gptx 0.2
+
+/usr/lib/eign: DECstation 5000, ULTRIX 4.3
+ HP 9000/735, HP-UX 9.0
+ IBM RS/6000, AIX 2.3
+ IBM 3090, AIX MP370 2.1
+ Stardent 1520, OS 2.2
+ Sun SPARCstation, SunOS 4.x
+
+No eign anywhere on: HP 375, BSD 4.3 (ptx.c is in /usr/src/usr.bin,
+ and the source code refers to /usr/lib/eign,
+ but I could not find it in the source tree)
+ NeXT, Mach 3.0 (though documented in man pages)
+ Sun SPARCstation, Solaris 2.x
+ SGI Indigo, IRIX 4.0.x
+
+The contents of the eign files that I found on the above machines were
+almost identical. With the exception of the Stardent and the IBM
+3090, there were only two such files, one with 150 words, and the
+other with 133, with only a few differences between them (some words
+in the 133-word file were not in the 150-word file). I found the
+133-word variant in groff-1.06/src/indxbib. I used archie to search
+for eign, and it found 7 sites, all with the groff versions.
+
+The Stardent and IBM 3090 eign files have the same contents as the
+150-word version, but have a multiline copyright comment at the
+beginning. None of the others contains a copyright.
+
+I recently had occasion to build a similar list of words for bibindex,
+which indexes a BibTeX .bib file, and for which omission of common
+words, like articles and prepositions, helps to reduce the size of the
+index. I didn't use eign to build that list, but instead, went
+through the word lists from 3.8MB of .bib files in the tuglib
+collection on ftp.math.utah.edu:pub/tex/bib, and collected words to be
+ignored. That list includes words from several languages. I'll leave
+it up to you to decide whether you wish to merge them or not; I
+suspect it may be a better design choice to keep a separate eign file
+for each language, although in my own application of ptx-ing
+bibliographies, the titles do occur in multiple languages, so a
+mixed-language eign is appropriate. Since there are standard ISO
+2-letter abbreviations for every country, perhaps one could have
+eign.xy for country xy (of course, only approximately is country ==
+language). The exact list of words in eign is not so critical; its
+only purpose is to reduce the size of the output by not indexing words
+that occur very frequently and have little content in themselves.
+
+I'm enclosing a shar bundle at the end of this message with the merger
+of the multiple eign versions (duplicates eliminated, and the list
+sorted into 179 unique words), followed by the bibindex list.
+
+
+
+========================================================================
+Nelson H. F. Beebe Tel: +1 801 581 5254
+Center for Scientific Computing FAX: +1 801 581 4148
+Department of Mathematics, 105 JWB Internet: beebe@math.utah.edu
+University of Utah
+Salt Lake City, UT 84112, USA
+========================================================================
+
+
diff --git a/gnu/usr.bin/ptx/examples/ignore/bix b/gnu/usr.bin/ptx/examples/ignore/bix
new file mode 100644
index 0000000..b9a8ba6
--- /dev/null
+++ b/gnu/usr.bin/ptx/examples/ignore/bix
@@ -0,0 +1,109 @@
+ab
+aber
+als
+an
+and
+are
+as
+auf
+aus
+az
+bei
+bir
+but
+da
+das
+dat
+de
+dei
+dem
+den
+der
+des
+det
+di
+die
+dos
+een
+eene
+egy
+ei
+ein
+eine
+einen
+einer
+eines
+eit
+el
+en
+er
+es
+et
+ett
+eyn
+eyne
+for
+from
+fuer
+fur
+gl
+gli
+ha
+haben
+had
+hai
+has
+hat
+have
+he
+heis
+hen
+hena
+henas
+het
+hin
+hinar
+hinir
+hinn
+hith
+ho
+hoi
+il
+in
+ist
+ka
+ke
+la
+las
+le
+les
+lo
+los
+mia
+mit
+na
+nji
+not
+oder
+of
+on
+or
+os
+others
+sie
+sind
+so
+ta
+the
+to
+um
+uma
+un
+una
+und
+une
+uno
+unter
+von
+with
+yr
diff --git a/gnu/usr.bin/ptx/examples/ignore/eign b/gnu/usr.bin/ptx/examples/ignore/eign
new file mode 100644
index 0000000..0401245
--- /dev/null
+++ b/gnu/usr.bin/ptx/examples/ignore/eign
@@ -0,0 +1,163 @@
+a
+about
+after
+against
+all
+also
+an
+and
+another
+any
+are
+as
+at
+back
+be
+because
+been
+before
+being
+between
+both
+but
+by
+came
+can
+come
+could
+current
+day
+did
+do
+down
+each
+end
+even
+first
+for
+from
+get
+go
+good
+great
+had
+has
+have
+he
+her
+here
+him
+his
+how
+i
+if
+in
+into
+is
+it
+its
+just
+know
+last
+life
+like
+little
+long
+made
+make
+man
+many
+may
+me
+men
+might
+more
+most
+mr
+much
+must
+my
+name
+never
+new
+no
+not
+now
+of
+off
+old
+on
+one
+only
+or
+other
+our
+out
+over
+own
+part
+people
+point
+right
+said
+same
+say
+see
+she
+should
+since
+so
+some
+start
+state
+still
+such
+take
+than
+that
+the
+their
+them
+then
+there
+these
+they
+this
+those
+three
+through
+time
+to
+too
+true
+try
+two
+under
+up
+us
+use
+used
+value
+very
+was
+way
+we
+well
+were
+what
+when
+where
+which
+while
+who
+why
+will
+with
+without
+work
+world
+would
+year
+years
+you
+your
diff --git a/gnu/usr.bin/ptx/examples/include.pl b/gnu/usr.bin/ptx/examples/include.pl
new file mode 100755
index 0000000..cb3c0ff
--- /dev/null
+++ b/gnu/usr.bin/ptx/examples/include.pl
@@ -0,0 +1,79 @@
+#!/usr/bin/perl -- # -*-Perl-*-
+eval "exec /usr/bin/perl -S $0 $*"
+ if $running_under_some_shell;
+
+# Construct a permuted index for all system include files.
+# Copyright (C) 1991 Free Software Foundation, Inc.
+# Francois Pinard <pinard@iro.umontreal.ca>, June 1991.
+
+# NOTE: about removing asm statements?
+# NOTE: about removing strings?
+# NOTE: about ignoring 0xHEXDIGITS, unchar/ushort/etc.
+
+# Construct a sorted list of system include files.
+
+opendir (DIR, "/usr/include");
+@includes = sort grep (-f "/usr/include/$_", readdir (DIR));
+opendir (DIR, "/usr/include/sys");
+foreach (sort grep (-f "/usr/include/sys/$_", readdir (DIR))) {
+ push (@includes, "sys/$_");
+}
+closedir (DIR);
+
+# Launch the permuted indexer, with a list of ignore words.
+
+$ignore = "/tmp/incptx.$$";
+open (IGNORE, "> $ignore");
+print IGNORE join ("\n", split (' ', <<IGNORE)), "\n";
+asm at at386 break bss case ch char continue copyright corporation
+default define defined do double dst else endif enum extern file flag
+float for goto i286 i386 ident if ifdef ifndef int interactive len
+lint long m32 mpat num pdp11 printf ptr register return sco5 short siz
+sizeof src static str struct sun switch sys systems type typedef u370
+u3b u3b15 u3b2 u3b5 undef union unsigned vax void while win
+IGNORE
+close IGNORE;
+exit 0;
+
+open (OUTPUT, "| ptx -r -f -W '[a-zA-Z_][a-zA-Z_0-9]+' -F ... -i $ignore")
+ || die "ptx did not start\n";
+select (OUTPUT);
+
+# Reformat all files, removing C comments and adding a reference field.
+
+foreach $include (@includes)
+{
+ warn "Reading /usr/include/$include\n";
+ open (INPUT, "/usr/include/$include");
+ while (<INPUT>)
+ {
+
+ # Get rid of comments.
+
+ $comment = $next_comment;
+ if ($comment)
+ {
+ $next_comment = !s,^.*\*/,,;
+ }
+ else
+ {
+ s,/\*.*\*/,,g;
+ $next_comment = s,/\*.*,,;
+ }
+ next if $comment && $next_comment;
+
+ # Remove extraneous white space.
+
+ s/[ \t]+/ /g;
+ s/ $//;
+ next if /^$/;
+
+ # Print the line with its reference.
+
+ print "$include($.): ", $_;
+ }
+}
+
+warn "All read, now ptx' game!\n";
+close OUTPUT || die "ptx failed...\n";
+unlink $ignore;
diff --git a/gnu/usr.bin/ptx/examples/latex/Makefile b/gnu/usr.bin/ptx/examples/latex/Makefile
new file mode 100644
index 0000000..5f930b2
--- /dev/null
+++ b/gnu/usr.bin/ptx/examples/latex/Makefile
@@ -0,0 +1,15 @@
+# Example of using ptx with latex.
+# Copyright (C) 1993 Free Software Foundation, Inc.
+# Francois Pinard <pinard@iro.umontreal.ca>, 1993.
+
+PTX = ../ptx
+PTX_OPTIONS = -AfTWi.i
+
+try: latex.dvi
+ xdvi latex
+
+latex.dvi: latex.tex table.tex
+ latex latex
+
+table.tex: Makefile ../COPYING
+ $(PTX) $(PTX_OPTIONS) ../COPYING | sed 's/ //' > table.tex
diff --git a/gnu/usr.bin/ptx/examples/latex/README b/gnu/usr.bin/ptx/examples/latex/README
new file mode 100644
index 0000000..fc5098a
--- /dev/null
+++ b/gnu/usr.bin/ptx/examples/latex/README
@@ -0,0 +1,10 @@
+Date: Sun, 26 Sep 93 19:07:10 EDT
+From: Francois Pinard <pinard@iro.umontreal.ca>
+To: ajayshah@cmie.ernet.in
+Subject: Re: Gptx suggestions and help request
+
+ In fact, if you could send me such a macro right now I would be
+ thrilled :-)
+
+Ok, I worked out this example for you. Even if a little rude, you can
+still start from it for your own need. [...]
diff --git a/gnu/usr.bin/ptx/examples/latex/latex.tex b/gnu/usr.bin/ptx/examples/latex/latex.tex
new file mode 100644
index 0000000..1f0a2f1
--- /dev/null
+++ b/gnu/usr.bin/ptx/examples/latex/latex.tex
@@ -0,0 +1,11 @@
+\documentstyle[11pt]{article}
+\begin{document}
+
+\def\xx#1#2#3#4#5#6{\hbox{
+ \hbox to2.5in{\hfil#5#2}
+ \hbox to3.0in{{\sl #3}\,#4#1\hfil}
+ \hbox to1.5in{\tiny#6\hfil}
+}}
+\input table
+
+\end{document}
diff --git a/gnu/usr.bin/ptx/examples/latex/table.tex b/gnu/usr.bin/ptx/examples/latex/table.tex
new file mode 100644
index 0000000..b68ea38
--- /dev/null
+++ b/gnu/usr.bin/ptx/examples/latex/table.tex
@@ -0,0 +1,65 @@
+\xx {}{ate to certain respons}{ibi}{lities for you if you}{}{../COPYING:30}
+\xx {}{These actions are proh}{ibi}{ted by law if you do n}{}{../COPYING:183}
+\xx {}{EN ADVISED OF THE POSS}{IBI}{LITY OF SUCH DAMAGES.}{}{../COPYING:278}
+\xx {}{icense may add an expl}{ici}{t geographical distrib}{}{../COPYING:232}
+\xx {}{OF ALL NECESSARY SERV}{ICI}{NG, REPAIR OR CORRECTI}{}{../COPYING:267}
+\xx {}{aims or to contest val}{idi}{ty of any such claims;}{}{../COPYING:216}
+\xx {}{If the software is mod}{ifi}{ed by someone else and}{}{../COPYING:45}
+\xx {}{, distribution and mod}{ifi}{cation follow.}{pying}{../COPYING:57}
+\xx {}{, DISTRIBUTION AND MOD}{IFI}{CATION 0. This Lice}{}{../COPYING:60}
+\xx {}{r verbatim or with mod}{ifi}{cations and/or transla}{}{../COPYING:68}
+\xx {}{ation in the term "mod}{ifi}{cation".)}{t limit}{../COPYING:70}
+\xx {}{, distribution and mod}{ifi}{cation are not covered}{}{../COPYING:72}
+\xx {}{nd distribute such mod}{ifi}{cations or work under}{}{../COPYING:92}
+\xx {}{You must cause the mod}{ifi}{ed files to carry prom}{}{../COPYING:95}
+\xx {ads c}{c) If the mod}{ifi}{ed program normally re}{}{../COPYING:103}
+\xx {}{ments apply to the mod}{ifi}{ed work as a whole.}{}{../COPYING:114}
+\xx {work are n}{If ident}{ifi}{able sections of that}{}{../COPYING:115}
+\xx {}{he work for making mod}{ifi}{cations to it.}{of t}{../COPYING:156}
+\xx {}{If the Program spec}{ifi}{es a version number of}{}{../COPYING:243}
+\xx {}{hey have is not the or}{igi}{nal, so that any probl}{}{../COPYING:46}
+\xx {}{not reflect on the or}{igi}{nal authors' reputatio}{}{../COPYING:47}
+\xx {}{a license from the or}{igi}{nal licensor to copy,}{}{../COPYING:191}
+\xx {}{ted interfaces, the or}{igi}{nal copyright holder w}{}{../COPYING:231}
+\xx {}{RRANTIES OF MERCHANTAB}{ILI}{TY AND FITNESS FOR A P}{}{../COPYING:265}
+\xx {}{OUT OF THE USE OR INAB}{ILI}{TY TO USE THE PROGRAM}{}{../COPYING:274}
+\xx {}{anty of MERCHANTAB}{ILI}{TY or FITNESS FOR A PA}{}{../COPYING:303}
+\xx {}{is included without l}{imi}{tation in the term "mo}{}{../COPYING:69}
+\xx {}{ny other reason (not l}{imi}{ted to patent issues),}{}{../COPYING:198}
+\xx {}{aphical distribution l}{imi}{tation excluding those}{}{../COPYING:232}
+\xx {}{nse incorporates the l}{imi}{tation as if written i}{}{../COPYING:235}
+\xx {}{new versions will be s}{imi}{lar in spirit to the p}{}{../COPYING:239}
+\xx {}{, INCLUDING, BUT NOT L}{IMI}{TED TO, THE IMPLIED WA}{}{../COPYING:264}
+\xx {}{M (INCLUDING BUT NOT L}{IMI}{TED TO LOSS OF DATA OR}{}{../COPYING:274}
+\xx {}{s to say, a work conta}{ini}{ng the Program or a po}{}{../COPYING:67}
+\xx {}{sociated interface def}{ini}{tion files, plus the s}{}{../COPYING:158}
+\xx {}{you must give the rec}{ipi}{ents all the rights th}{}{../COPYING:34}
+\xx {}{ed on, we want its rec}{ipi}{ents to know that what}{}{../COPYING:46}
+\xx {}{and give any other rec}{ipi}{ents of the Program a}{}{../COPYING:84}
+\xx {}{the Program), the rec}{ipi}{ent automatically rece}{}{../COPYING:190}
+\xx {}{estrictions on the rec}{ipi}{ents' exercise of the}{}{../COPYING:193}
+\xx {}{will be similar in sp}{iri}{t to the present versi}{}{../COPYING:239}
+\xx {he two goal}{Our dec}{isi}{on will be guided by t}{}{../COPYING:254}
+\xx {}{NSEQUENTIAL DAMAGES AR}{ISI}{NG OUT OF THE USE OR I}{}{../COPYING:273}
+\xx {}{tive mode: Gnomov}{isi}{on version 69, Copyrig}{}{../COPYING:315}
+\xx {}{e of author Gnomov}{isi}{on comes with ABSOLUTE}{}{../COPYING:316}
+\xx {}{the program `Gnomov}{isi}{on' (which makes passe}{}{../COPYING:330}
+\xx {}{to certain responsibil}{iti}{es for you if you dist}{}{../COPYING:30}
+\xx {}{precise terms and cond}{iti}{ons for copying, distr}{}{../COPYING:56}
+\xx {}{ENSE TERMS AND COND}{ITI}{ONS FOR COPYING, DISTR}{}{../COPYING:60}
+\xx {}{meet all of these cond}{iti}{ons: a) You must}{}{../COPYING:93}
+\xx {}{m under these cond}{iti}{ons, and telling the u}{}{../COPYING:109}
+\xx {f another wo}{In add}{iti}{on, mere aggregation o}{}{../COPYING:129}
+\xx {}{all its terms and cond}{iti}{ons for copying, distr}{}{../COPYING:186}
+\xx {}{o these terms and cond}{iti}{ons.}{bject t}{../COPYING:192}
+\xx {}{o patent issues), cond}{iti}{ons are imposed on you}{}{../COPYING:199}
+\xx {}{at contradict the cond}{iti}{ons of this License, t}{}{../COPYING:200}
+\xx {}{cuse you from the cond}{iti}{ons of this License.}{}{../COPYING:201}
+\xx {}{ing the terms and cond}{iti}{ons either of that ver}{}{../COPYING:244}
+\xx {}{hose distribution cond}{iti}{ons are different, wri}{}{../COPYING:251}
+\xx {}{OTHERWISE STATED IN WR}{ITI}{NG THE COPYRIGHT HOLDE}{}{../COPYING:262}
+\xx {}{LAW OR AGREED TO IN WR}{ITI}{NG WILL ANY COPYRIGHT}{}{../COPYING:270}
+\xx {}{END OF TERMS AND COND}{ITI}{ONS Appendix: How t}{}{../COPYING:280}
+\xx {}{under certain cond}{iti}{ons; type `show c' for}{}{../COPYING:318}
+\xx {}{free program will ind}{ivi}{dually obtain patent l}{}{../COPYING:52}
+\xx {g, distribution}{Act}{ivi}{ties other than copyin}{}{../COPYING:72}
diff --git a/gnu/usr.bin/ptx/examples/luke/README b/gnu/usr.bin/ptx/examples/luke/README
new file mode 100644
index 0000000..6291861
--- /dev/null
+++ b/gnu/usr.bin/ptx/examples/luke/README
@@ -0,0 +1,2 @@
+From: Luke Kendall <luke@research.canon.oz.au>
+Date: Wed, 16 Oct 91 12:26:39 EST
diff --git a/gnu/usr.bin/ptx/examples/luke/xxroff.sh b/gnu/usr.bin/ptx/examples/luke/xxroff.sh
new file mode 100644
index 0000000..55ef908
--- /dev/null
+++ b/gnu/usr.bin/ptx/examples/luke/xxroff.sh
@@ -0,0 +1,108 @@
+#!/bin/sh
+#
+# Author: Luke Kendall
+#
+MYNAME=`basename $0`
+usage="usage: $MYNAME [man-directory]
+ (generates permuted index of -man files in directory)"
+md=/usr/man
+#
+if [ $# = 0 ]
+then
+ echo "$MYNAME: no man directory specified: assuming $md"
+elif [ $# != 1 ]
+then
+ echo "$usage"
+ exit 1
+elif [ -d $1 ]
+then
+ md="$1"
+else
+ echo "$usage"
+ exit 1
+fi
+echo "Permuted index of $md:"
+out=ptx.tr
+# ------ clumsy permuted index macros (replaced by stuff below) ------------
+cat <<'EOF' > $out
+.pn 1
+.de xx
+\\$1 \\$2 \\fB\\$3\\fR \\$4 \\s-1\\$5\\s0
+..
+.pl 10i
+.de NP
+.ev 1
+.ft 1
+.ps 10
+.sp 0.75c
+.tl '\s-2\\fIpermuted index\\fP\s0'\- \\n% \-'\s-2\\fIpermuted index\\fP\s0'
+.pn +1
+.bp
+.ev
+..
+.wh 9i NP
+.nf
+.na
+.ta 6.5i-1.1iR 6.5iR 6.51iR 6.52R
+.ll 6.0i
+.po 0i
+.sp 0.25i
+'\"
+EOF
+# ------ ------- ------- ------- ------- -------
+# ------ alternate permuted index macros (from net) ------------
+cat <<'EOF' > $out
+.pl 10i
+.de NP
+.ev 1
+.ft 1
+.ps 10
+.sp 0.75c
+.tl '\s-2\\fIpermuted index\\fP\s0'\- \\n% \-'\s-2\\fIpermuted index\\fP\s0'
+.pn +1
+.bp
+.ev
+..
+.wh 9i NP
+.po 0.5i
+.sp 0.25i
+.tr ~ \" tildes will translate to blanks
+'\".ll 80 \" line length of output
+.ll 6.0i \" line length of output
+.nf \" must be in no-fill mode
+.nr )r \n(.lu-10n \" set position of reference in line (10 less than length)
+.nr )k \n()ru/2u \" set position of keyword (approx. centered)
+.ds s2 ~~~ \" this is the center gap -- 3 spaces
+.de xx \"definition of xx macro
+.ds s1\" \" initialise to null string
+.if \w@\\$2@ .ds s1 ~\" \"set to single blank if there is second arg
+.ds s3\" \" initialise to null string
+.if \w@\\$4@ .ds s3 ~\" \"set to single blank if there is second arg
+.ds s4 ~\" \" set to single blank
+.ds s5 ~\" \" set to single blank
+.ds y \\*(s4\a\\*(s5\" \" blank, leader, blank
+.ta \\n()ru-\w@\\*(s5@u \" set tab just to left of ref
+\h@\\n()ku-\w@\\$1\\*(s1\\$2\\*(s2@u@\\$1\\*(s1\\$2\\*(s2\\$3\\*(s3\\$4\\*y\\$5
+..
+ ~
+EOF
+# ------ ------- ------- ------- ------- -------
+find $md -type f -name "*.[1-8nl]*" -print |
+while read f
+do
+ man=`basename $f`
+ man=`expr "$man" : "\(.*\)\.[^\.]*"`
+echo $man:
+ #
+ # Use 1st non-"." and non-"'" started line as input to ptx (this
+ # should be the synopsis after the `.SH NAME');
+ # strip any "\-" from it (a silly sort key for ptx to avoid);
+ # insert a leading man page name for the -r option to find
+ #
+ sed -n '/^[^.]/s/\\-//g;/^[^.]/p;/^[^.]/q' $f | sed "s/^/($man) /"
+done | ptx -t -f -r >> $out
+#
+# Turn the troff'able permuted index file into PostScript
+#
+psroff -t -rL10i $out > ptx.ps
+echo "$out and ptx.ps produced from man directory $md."
diff --git a/gnu/usr.bin/ptx/getopt.c b/gnu/usr.bin/ptx/getopt.c
new file mode 100644
index 0000000..7a4673b
--- /dev/null
+++ b/gnu/usr.bin/ptx/getopt.c
@@ -0,0 +1,757 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef HAVE_CONFIG_H
+#if defined (emacs) || defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
+#ifndef __STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+#include <stdlib.h>
+#endif /* GNU C library. */
+
+/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
+ long-named option. Because this is not POSIX.2 compliant, it is
+ being phased out. */
+/* #define GETOPT_COMPAT */
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = 0;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* XXX 1003.2 says this must be 1 before any call. */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return EOF with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+#include <string.h>
+#define my_index strchr
+#else
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+char *getenv ();
+
+static char *
+my_index (str, chr)
+ const char *str;
+ int chr;
+{
+ while (*str)
+ {
+ if (*str == chr)
+ return (char *) str;
+ str++;
+ }
+ return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+ If not using GCC, it is ok not to declare it.
+ (Supposedly there are some machines where it might get a warning,
+ but changing this conditional to __STDC__ is too risky.) */
+#ifdef __GNUC__
+#ifdef IN_GCC
+#include "gstddef.h"
+#else
+#include <stddef.h>
+#endif
+extern size_t strlen (const char *);
+#endif
+
+#endif /* GNU C library. */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int bottom = first_nonopt;
+ int middle = last_nonopt;
+ int top = optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
+
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns `EOF'.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ int option_index;
+
+ optarg = 0;
+
+ /* Initialize the internal data when the first call is made.
+ Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ if (optind == 0)
+ {
+ first_nonopt = last_nonopt = optind = 1;
+
+ nextchar = NULL;
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (getenv ("POSIXLY_CORRECT") != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+ }
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Now skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc
+ && (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* Special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return EOF;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if ((argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ {
+ if (ordering == REQUIRE_ORDER)
+ return EOF;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Start decoding its characters. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ if (longopts != NULL
+ && ((argv[optind][0] == '-'
+ && (argv[optind][1] == '-' || long_only))
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ ))
+ {
+ const struct option *p;
+ char *s = nextchar;
+ int exact = 0;
+ int ambig = 0;
+ const struct option *pfound = NULL;
+ int indfound;
+
+ while (*s && *s != '=')
+ s++;
+
+ /* Test all options for either exact match or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name;
+ p++, option_index++)
+ if (!strncmp (p->name, nextchar, s - nextchar))
+ {
+ if (s - nextchar == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' is ambiguous\n",
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*s)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = s + 1;
+ else
+ {
+ if (opterr)
+ {
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ "%s: option `--%s' doesn't allow an argument\n",
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ "%s: option `%c%s' doesn't allow an argument\n",
+ argv[0], argv[optind - 1][0], pfound->name);
+ }
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' requires an argument\n",
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, "%s: unrecognized option `--%s'\n",
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+#if 0
+ if (c < 040 || c >= 0177)
+ fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
+ argv[0], c);
+ else
+ fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
+#endif
+ }
+ optopt = c;
+ return '?';
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = 0;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+#if 0
+ fprintf (stderr, "%s: option `-%c' requires an argument\n",
+ argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: option requires an argument -- %c\n",
+ argv[0], c);
+#endif
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/gnu/usr.bin/ptx/getopt.h b/gnu/usr.bin/ptx/getopt.h
new file mode 100644
index 0000000..45541f5
--- /dev/null
+++ b/gnu/usr.bin/ptx/getopt.h
@@ -0,0 +1,129 @@
+/* Declarations for getopt.
+ Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized. */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+#if __STDC__
+ const char *name;
+#else
+ char *name;
+#endif
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+#if __STDC__
+#if defined(__GNU_LIBRARY__)
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* not __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind);
+
+/* Internal only. Users should not call this directly. */
+extern int _getopt_internal (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* not __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/gnu/usr.bin/ptx/getopt1.c b/gnu/usr.bin/ptx/getopt1.c
new file mode 100644
index 0000000..f784b57
--- /dev/null
+++ b/gnu/usr.bin/ptx/getopt1.c
@@ -0,0 +1,187 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef HAVE_CONFIG_H
+#if defined (emacs) || defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
+#include "getopt.h"
+
+#ifndef __STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#else
+char *getenv ();
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/gnu/usr.bin/ptx/mkinstalldirs b/gnu/usr.bin/ptx/mkinstalldirs
new file mode 100755
index 0000000..0e29377
--- /dev/null
+++ b/gnu/usr.bin/ptx/mkinstalldirs
@@ -0,0 +1,35 @@
+#!/bin/sh
+# Make directory hierarchy.
+# Written by Noah Friedman <friedman@prep.ai.mit.edu>
+# Public domain.
+
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+errstatus=0
+
+for file in ${1+"$@"} ; do
+ oIFS="${IFS}"
+ # Some sh's can't handle IFS=/ for some reason.
+ IFS='%'
+ set - `echo ${file} | sed -e 's@/@%@g' -e 's@^%@/@'`
+ IFS="${oIFS}"
+
+ pathcomp=''
+
+ for d in ${1+"$@"} ; do
+ pathcomp="${pathcomp}${d}"
+
+ if test ! -d "${pathcomp}"; then
+ echo "mkdir $pathcomp" 1>&2
+ mkdir "${pathcomp}" || errstatus=$?
+ fi
+
+ pathcomp="${pathcomp}/"
+ done
+done
+
+exit $errstatus
+
+# eof
diff --git a/gnu/usr.bin/ptx/ptx.c b/gnu/usr.bin/ptx/ptx.c
new file mode 100644
index 0000000..2dc306e
--- /dev/null
+++ b/gnu/usr.bin/ptx/ptx.c
@@ -0,0 +1,2237 @@
+/* Permuted index for GNU, with keywords in their context.
+ Copyright (C) 1990, 1991, 1993 Free Software Foundation, Inc.
+ Francois Pinard <pinard@iro.umontreal.ca>, 1988.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+const char *version_string = "GNU ptx version 0.3";
+
+char *const copyright = "\
+This program is free software; you can redistribute it and/or modify\n\
+it under the terms of the GNU General Public License as published by\n\
+the Free Software Foundation; either version 2, or (at your option)\n\
+any later version.\n\
+\n\
+This program is distributed in the hope that it will be useful,\n\
+but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
+GNU General Public License for more details.\n\
+\n\
+You should have received a copy of the GNU General Public License\n\
+along with this program; if not, write to the Free Software\n\
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n";
+
+/* Reallocation step when swallowing non regular files. The value is not
+ the actual reallocation step, but its base two logarithm. */
+#define SWALLOW_REALLOC_LOG 12
+
+/* Imported from "regex.c". */
+#define Sword 1
+
+#ifdef STDC_HEADERS
+
+#include <stdlib.h>
+#include <ctype.h>
+
+#else /* not STDC_HEADERS */
+
+/* These definitions work, for all 256 characters. */
+#define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
+#define isxdigit(c) \
+ (((unsigned char) (c) >= 'a' && (unsigned char) (c) <= 'f') \
+ || ((unsigned char) (c) >= 'A' && (unsigned char) (c) <= 'F') \
+ || ((unsigned char) (c) >= '0' && (unsigned char) (c) <= '9'))
+#define islower(c) ((unsigned char) (c) >= 'a' && (unsigned char) (c) <= 'z')
+#define isupper(c) ((unsigned char) (c) >= 'A' && (unsigned char) (c) <= 'Z')
+#define isalpha(c) (islower (c) || isupper (c))
+#define toupper(c) (islower (c) ? (c) - 'a' + 'A' : (c))
+
+#endif /* not STDC_HEADERS */
+
+#if !defined (isascii) || defined (STDC_HEADERS)
+#undef isascii
+#define isascii(c) 1
+#endif
+
+#define ISXDIGIT(c) (isascii (c) && isxdigit (c))
+#define ISODIGIT(c) ((c) >= '0' && (c) <= '7')
+#define HEXTOBIN(c) ((c)>='a'&&(c)<='f' ? (c)-'a'+10 : (c)>='A'&&(c)<='F' ? (c)-'A'+10 : (c)-'0')
+#define OCTTOBIN(c) ((c) - '0')
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if !defined(S_ISREG) && defined(S_IFREG)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else /* not HAVE_STRING_H */
+#include <strings.h>
+#define strchr index
+#define strrchr rindex
+#endif /* not HAVE_STRING_H */
+
+#include "getopt.h"
+
+#include <errno.h>
+#ifndef errno
+extern int errno;
+#endif
+
+#include "bumpalloc.h"
+#include "diacrit.h"
+#include "regex.h"
+
+#ifndef __STDC__
+void *xmalloc ();
+void *xrealloc ();
+#else
+void *xmalloc (int);
+void *xrealloc (void *, int);
+#endif
+
+
+/* Global definitions. */
+
+const char *program_name; /* name of this program */
+static int show_help = 0; /* display usage information and exit */
+static int show_version = 0; /* print the version and exit */
+
+/* Program options. */
+
+enum Format
+{
+ DUMB_FORMAT, /* output for a dumb terminal */
+ ROFF_FORMAT, /* output for `troff' or `nroff' */
+ TEX_FORMAT, /* output for `TeX' or `LaTeX' */
+ UNKNOWN_FORMAT /* output format still unknown */
+};
+
+int gnu_extensions = 1; /* trigger all GNU extensions */
+int auto_reference = 0; /* references are `file_name:line_number:' */
+int input_reference = 0; /* references at beginning of input lines */
+int right_reference = 0; /* output references after right context */
+int line_width = 72; /* output line width in characters */
+int gap_size = 3; /* number of spaces between output fields */
+const char *truncation_string = "/";
+ /* string used to mark line truncations */
+const char *macro_name = "xx"; /* macro name for roff or TeX output */
+enum Format output_format = UNKNOWN_FORMAT;
+ /* output format */
+
+int ignore_case = 0; /* fold lower to upper case for sorting */
+const char *context_regex_string = NULL;
+ /* raw regex for end of context */
+const char *word_regex_string = NULL;
+ /* raw regex for a keyword */
+const char *break_file = NULL; /* name of the `Break characters' file */
+const char *only_file = NULL; /* name of the `Only words' file */
+const char *ignore_file = NULL; /* name of the `Ignore words' file */
+
+/* A BLOCK delimit a region in memory of arbitrary size, like the copy of a
+ whole file. A WORD is something smaller, its length should fit in a
+ short integer. A WORD_TABLE may contain several WORDs. */
+
+typedef struct
+ {
+ char *start; /* pointer to beginning of region */
+ char *end; /* pointer to end + 1 of region */
+ }
+BLOCK;
+
+typedef struct
+ {
+ char *start; /* pointer to beginning of region */
+ short size; /* length of the region */
+ }
+WORD;
+
+typedef struct
+ {
+ WORD *start; /* array of WORDs */
+ size_t length; /* number of entries */
+ }
+WORD_TABLE;
+
+/* Pattern description tables. */
+
+/* For each character, provide its folded equivalent. */
+unsigned char folded_chars[CHAR_SET_SIZE];
+
+/* For each character, indicate if it is part of a word. */
+char syntax_table[CHAR_SET_SIZE];
+char *re_syntax_table = syntax_table;
+
+/* Compiled regex for end of context. */
+struct re_pattern_buffer *context_regex;
+
+/* End of context pattern register indices. */
+struct re_registers context_regs;
+
+/* Compiled regex for a keyword. */
+struct re_pattern_buffer *word_regex;
+
+/* Keyword pattern register indices. */
+struct re_registers word_regs;
+
+/* A word characters fastmap is used only when no word regexp has been
+ provided. A word is then made up of a sequence of one or more characters
+ allowed by the fastmap. Contains !0 if character allowed in word. Not
+ only this is faster in most cases, but it simplifies the implementation
+ of the Break files. */
+char word_fastmap[CHAR_SET_SIZE];
+
+/* Maximum length of any word read. */
+int maximum_word_length;
+
+/* Maximum width of any reference used. */
+int reference_max_width;
+
+
+/* Ignore and Only word tables. */
+
+WORD_TABLE ignore_table; /* table of words to ignore */
+WORD_TABLE only_table; /* table of words to select */
+
+#define ALLOC_NEW_WORD(table) \
+ BUMP_ALLOC ((table)->start, (table)->length, 8, WORD)
+
+/* Source text table, and scanning macros. */
+
+int number_input_files; /* number of text input files */
+int total_line_count; /* total number of lines seen so far */
+const char **input_file_name; /* array of text input file names */
+int *file_line_count; /* array of `total_line_count' values at end */
+
+BLOCK text_buffer; /* file to study */
+char *text_buffer_maxend; /* allocated end of text_buffer */
+
+/* SKIP_NON_WHITE used only for getting or skipping the reference. */
+
+#define SKIP_NON_WHITE(cursor, limit) \
+ while (cursor < limit && !isspace(*cursor)) \
+ cursor++
+
+#define SKIP_WHITE(cursor, limit) \
+ while (cursor < limit && isspace(*cursor)) \
+ cursor++
+
+#define SKIP_WHITE_BACKWARDS(cursor, start) \
+ while (cursor > start && isspace(cursor[-1])) \
+ cursor--
+
+#define SKIP_SOMETHING(cursor, limit) \
+ do \
+ if (word_regex_string) \
+ { \
+ int count; \
+ count = re_match (word_regex, cursor, limit - cursor, 0, NULL); \
+ cursor += count <= 0 ? 1 : count; \
+ } \
+ else if (word_fastmap[(unsigned char) *cursor]) \
+ while (cursor < limit && word_fastmap[(unsigned char) *cursor]) \
+ cursor++; \
+ else \
+ cursor++; \
+ while (0)
+
+/* Occurrences table.
+
+ The `keyword' pointer provides the central word, which is surrounded
+ by a left context and a right context. The `keyword' and `length'
+ field allow full 8-bit characters keys, even including NULs. At other
+ places in this program, the name `keyafter' refers to the keyword
+ followed by its right context.
+
+ The left context does not extend, towards the beginning of the file,
+ further than a distance given by the `left' value. This value is
+ relative to the keyword beginning, it is usually negative. This
+ insures that, except for white space, we will never have to backward
+ scan the source text, when it is time to generate the final output
+ lines.
+
+ The right context, indirectly attainable through the keyword end, does
+ not extend, towards the end of the file, further than a distance given
+ by the `right' value. This value is relative to the keyword
+ beginning, it is usually positive.
+
+ When automatic references are used, the `reference' value is the
+ overall line number in all input files read so far, in this case, it
+ is of type (int). When input references are used, the `reference'
+ value indicates the distance between the keyword beginning and the
+ start of the reference field, it is of type (DELTA) and usually
+ negative. */
+
+typedef short DELTA; /* to hold displacement within one context */
+
+typedef struct
+ {
+ WORD key; /* description of the keyword */
+ DELTA left; /* distance to left context start */
+ DELTA right; /* distance to right context end */
+ int reference; /* reference descriptor */
+ }
+OCCURS;
+
+/* The various OCCURS tables are indexed by the language. But the time
+ being, there is no such multiple language support. */
+
+OCCURS *occurs_table[1]; /* all words retained from the read text */
+size_t number_of_occurs[1]; /* number of used slots in occurs_table */
+
+#define ALLOC_NEW_OCCURS(language) \
+ BUMP_ALLOC (occurs_table[language], number_of_occurs[language], 9, OCCURS)
+
+
+/* Communication among output routines. */
+
+/* Indicate if special output processing is requested for each character. */
+char edited_flag[CHAR_SET_SIZE];
+
+int half_line_width; /* half of line width, reference excluded */
+int before_max_width; /* maximum width of before field */
+int keyafter_max_width; /* maximum width of keyword-and-after field */
+int truncation_string_length; /* length of string used to flag truncation */
+
+/* When context is limited by lines, wraparound may happen on final output:
+ the `head' pointer gives access to some supplementary left context which
+ will be seen at the end of the output line, the `tail' pointer gives
+ access to some supplementary right context which will be seen at the
+ beginning of the output line. */
+
+BLOCK tail; /* tail field */
+int tail_truncation; /* flag truncation after the tail field */
+
+BLOCK before; /* before field */
+int before_truncation; /* flag truncation before the before field */
+
+BLOCK keyafter; /* keyword-and-after field */
+int keyafter_truncation; /* flag truncation after the keyafter field */
+
+BLOCK head; /* head field */
+int head_truncation; /* flag truncation before the head field */
+
+BLOCK reference; /* reference field for input reference mode */
+
+
+/* Miscellaneous routines. */
+
+/*------------------------------------------------------.
+| Duplicate string STRING, while evaluating \-escapes. |
+`------------------------------------------------------*/
+
+/* Loosely adapted from GNU shellutils printf.c code. */
+
+char *
+copy_unescaped_string (const char *string)
+{
+ char *result; /* allocated result */
+ char *cursor; /* cursor in result */
+ int value; /* value of \nnn escape */
+ int length; /* length of \nnn escape */
+
+ result = xmalloc (strlen (string) + 1);
+ cursor = result;
+
+ while (*string)
+ if (*string == '\\')
+ {
+ string++;
+ switch (*string)
+ {
+ case 'x': /* \xhhh escape, 3 chars maximum */
+ value = 0;
+ for (length = 0, string++;
+ length < 3 && ISXDIGIT (*string);
+ length++, string++)
+ value = value * 16 + HEXTOBIN (*string);
+ if (length == 0)
+ {
+ *cursor++ = '\\';
+ *cursor++ = 'x';
+ }
+ else
+ *cursor++ = value;
+ break;
+
+ case '0': /* \0ooo escape, 3 chars maximum */
+ value = 0;
+ for (length = 0, string++;
+ length < 3 && ISODIGIT (*string);
+ length++, string++)
+ value = value * 8 + OCTTOBIN (*string);
+ *cursor++ = value;
+ break;
+
+ case 'a': /* alert */
+#if __STDC__
+ *cursor++ = '\a';
+#else
+ *cursor++ = 7;
+#endif
+ string++;
+ break;
+
+ case 'b': /* backspace */
+ *cursor++ = '\b';
+ string++;
+ break;
+
+ case 'c': /* cancel the rest of the output */
+ while (*string)
+ string++;
+ break;
+
+ case 'f': /* form feed */
+ *cursor++ = '\f';
+ string++;
+ break;
+
+ case 'n': /* new line */
+ *cursor++ = '\n';
+ string++;
+ break;
+
+ case 'r': /* carriage return */
+ *cursor++ = '\r';
+ string++;
+ break;
+
+ case 't': /* horizontal tab */
+ *cursor++ = '\t';
+ string++;
+ break;
+
+ case 'v': /* vertical tab */
+#if __STDC__
+ *cursor++ = '\v';
+#else
+ *cursor++ = 11;
+#endif
+ string++;
+ break;
+
+ default:
+ *cursor++ = '\\';
+ *cursor++ = *string++;
+ break;
+ }
+ }
+ else
+ *cursor++ = *string++;
+
+ *cursor = '\0';
+ return result;
+}
+
+/*-------------------------------------------------------------------.
+| Compile the regex represented by STRING, diagnose and abort if any |
+| error. Returns the compiled regex structure. |
+`-------------------------------------------------------------------*/
+
+struct re_pattern_buffer *
+alloc_and_compile_regex (const char *string)
+{
+ struct re_pattern_buffer *pattern; /* newly allocated structure */
+ const char *message; /* error message returned by regex.c */
+
+ pattern = (struct re_pattern_buffer *)
+ xmalloc (sizeof (struct re_pattern_buffer));
+ memset (pattern, 0, sizeof (struct re_pattern_buffer));
+
+ pattern->buffer = NULL;
+ pattern->allocated = 0;
+ pattern->translate = ignore_case ? (char *) folded_chars : NULL;
+ pattern->fastmap = (char *) xmalloc (CHAR_SET_SIZE);
+
+ message = re_compile_pattern (string, strlen (string), pattern);
+ if (message)
+ error (1, 0, "%s (for regexp `%s')", message, string);
+
+ /* The fastmap should be compiled before `re_match'. The following
+ call is not mandatory, because `re_search' is always called sooner,
+ and it compiles the fastmap if this has not been done yet. */
+
+ re_compile_fastmap (pattern);
+
+ /* Do not waste extra allocated space. */
+
+ if (pattern->allocated > pattern->used)
+ {
+ pattern->buffer
+ = (unsigned char *) xrealloc (pattern->buffer, pattern->used);
+ pattern->allocated = pattern->used;
+ }
+
+ return pattern;
+}
+
+/*------------------------------------------------------------------------.
+| This will initialize various tables for pattern match and compiles some |
+| regexps. |
+`------------------------------------------------------------------------*/
+
+void
+initialize_regex (void)
+{
+ int character; /* character value */
+
+ /* Initialize the regex syntax table. */
+
+ for (character = 0; character < CHAR_SET_SIZE; character++)
+ syntax_table[character] = isalpha (character) ? Sword : 0;
+
+ /* Initialize the case folding table. */
+
+ if (ignore_case)
+ for (character = 0; character < CHAR_SET_SIZE; character++)
+ folded_chars[character] = toupper (character);
+
+ /* Unless the user already provided a description of the end of line or
+ end of sentence sequence, select an end of line sequence to compile.
+ If the user provided an empty definition, thus disabling end of line
+ or sentence feature, make it NULL to speed up tests. If GNU
+ extensions are enabled, use end of sentence like in GNU emacs. If
+ disabled, use end of lines. */
+
+ if (context_regex_string)
+ {
+ if (!*context_regex_string)
+ context_regex_string = NULL;
+ }
+ else if (gnu_extensions && !input_reference)
+ context_regex_string = "[.?!][]\"')}]*\\($\\|\t\\| \\)[ \t\n]*";
+ else
+ context_regex_string = "\n";
+
+ if (context_regex_string)
+ context_regex = alloc_and_compile_regex (context_regex_string);
+
+ /* If the user has already provided a non-empty regexp to describe
+ words, compile it. Else, unless this has already been done through
+ a user provided Break character file, construct a fastmap of
+ characters that may appear in a word. If GNU extensions enabled,
+ include only letters of the underlying character set. If disabled,
+ include almost everything, even punctuations; stop only on white
+ space. */
+
+ if (word_regex_string && *word_regex_string)
+ word_regex = alloc_and_compile_regex (word_regex_string);
+ else if (!break_file)
+ if (gnu_extensions)
+ {
+
+ /* Simulate \w+. */
+
+ for (character = 0; character < CHAR_SET_SIZE; character++)
+ word_fastmap[character] = isalpha (character);
+ }
+ else
+ {
+
+ /* Simulate [^ \t\n]+. */
+
+ memset (word_fastmap, 1, CHAR_SET_SIZE);
+ word_fastmap[' '] = 0;
+ word_fastmap['\t'] = 0;
+ word_fastmap['\n'] = 0;
+ }
+}
+
+/*------------------------------------------------------------------------.
+| This routine will attempt to swallow a whole file name FILE_NAME into a |
+| contiguous region of memory and return a description of it into BLOCK. |
+| Standard input is assumed whenever FILE_NAME is NULL, empty or "-". |
+| |
+| Previously, in some cases, white space compression was attempted while |
+| inputting text. This was defeating some regexps like default end of |
+| sentence, which checks for two consecutive spaces. If white space |
+| compression is ever reinstated, it should be in output routines. |
+`------------------------------------------------------------------------*/
+
+void
+swallow_file_in_memory (const char *file_name, BLOCK *block)
+{
+ int file_handle; /* file descriptor number */
+ struct stat stat_block; /* stat block for file */
+ int allocated_length; /* allocated length of memory buffer */
+ int used_length; /* used length in memory buffer */
+ int read_length; /* number of character gotten on last read */
+
+ /* As special cases, a file name which is NULL or "-" indicates standard
+ input, which is already opened. In all other cases, open the file from
+ its name. */
+
+ if (!file_name || !*file_name || strcmp (file_name, "-") == 0)
+ file_handle = fileno (stdin);
+ else
+ if ((file_handle = open (file_name, O_RDONLY)) < 0)
+ error (1, errno, file_name);
+
+ /* If the file is a plain, regular file, allocate the memory buffer all at
+ once and swallow the file in one blow. In other cases, read the file
+ repeatedly in smaller chunks until we have it all, reallocating memory
+ once in a while, as we go. */
+
+ if (fstat (file_handle, &stat_block) < 0)
+ error (1, errno, file_name);
+
+ if (S_ISREG (stat_block.st_mode))
+ {
+ block->start = (char *) xmalloc ((int) stat_block.st_size);
+
+ if (read (file_handle, block->start, (int) stat_block.st_size)
+ != stat_block.st_size)
+ error (1, errno, file_name);
+
+ block->end = block->start + stat_block.st_size;
+ }
+ else
+ {
+ block->start = (char *) xmalloc (1 << SWALLOW_REALLOC_LOG);
+ used_length = 0;
+ allocated_length = (1 << SWALLOW_REALLOC_LOG);
+
+ while ((read_length = read (file_handle,
+ block->start + used_length,
+ allocated_length - used_length)) > 0)
+ {
+ used_length += read_length;
+ if (used_length == allocated_length)
+ {
+ allocated_length += (1 << SWALLOW_REALLOC_LOG);
+ block->start
+ = (char *) xrealloc (block->start, allocated_length);
+ }
+ }
+
+ if (read_length < 0)
+ error (1, errno, file_name);
+
+ block->end = block->start + used_length;
+ }
+
+ /* Close the file, but only if it was not the standard input. */
+
+ if (file_handle != fileno (stdin))
+ close (file_handle);
+}
+
+/* Sort and search routines. */
+
+/*--------------------------------------------------------------------------.
+| Compare two words, FIRST and SECOND, and return 0 if they are identical. |
+| Return less than 0 if the first word goes before the second; return |
+| greater than 0 if the first word goes after the second. |
+| |
+| If a word is indeed a prefix of the other, the shorter should go first. |
+`--------------------------------------------------------------------------*/
+
+int
+compare_words (const void *void_first, const void *void_second)
+{
+#define first ((WORD *) void_first)
+#define second ((WORD *) void_second)
+ int length; /* minimum of two lengths */
+ int counter; /* cursor in words */
+ int value; /* value of comparison */
+
+ length = first->size < second->size ? first->size : second->size;
+
+ if (ignore_case)
+ {
+ for (counter = 0; counter < length; counter++)
+ {
+ value = (folded_chars [(unsigned char) (first->start[counter])]
+ - folded_chars [(unsigned char) (second->start[counter])]);
+ if (value != 0)
+ return value;
+ }
+ }
+ else
+ {
+ for (counter = 0; counter < length; counter++)
+ {
+ value = ((unsigned char) first->start[counter]
+ - (unsigned char) second->start[counter]);
+ if (value != 0)
+ return value;
+ }
+ }
+
+ return first->size - second->size;
+#undef first
+#undef second
+}
+
+/*-----------------------------------------------------------------------.
+| Decides which of two OCCURS, FIRST or SECOND, should lexicographically |
+| go first. In case of a tie, preserve the original order through a |
+| pointer comparison. |
+`-----------------------------------------------------------------------*/
+
+int
+compare_occurs (const void *void_first, const void *void_second)
+{
+#define first ((OCCURS *) void_first)
+#define second ((OCCURS *) void_second)
+ int value;
+
+ value = compare_words (&first->key, &second->key);
+ return value == 0 ? first->key.start - second->key.start : value;
+#undef first
+#undef second
+}
+
+/*------------------------------------------------------------.
+| Return !0 if WORD appears in TABLE. Uses a binary search. |
+`------------------------------------------------------------*/
+
+int
+search_table (WORD *word, WORD_TABLE *table)
+{
+ int lowest; /* current lowest possible index */
+ int highest; /* current highest possible index */
+ int middle; /* current middle index */
+ int value; /* value from last comparison */
+
+ lowest = 0;
+ highest = table->length - 1;
+ while (lowest <= highest)
+ {
+ middle = (lowest + highest) / 2;
+ value = compare_words (word, table->start + middle);
+ if (value < 0)
+ highest = middle - 1;
+ else if (value > 0)
+ lowest = middle + 1;
+ else
+ return 1;
+ }
+ return 0;
+}
+
+/*---------------------------------------------------------------------.
+| Sort the whole occurs table in memory. Presumably, `qsort' does not |
+| take intermediate copies or table elements, so the sort will be |
+| stabilized throughout the comparison routine. |
+`---------------------------------------------------------------------*/
+
+void
+sort_found_occurs (void)
+{
+
+ /* Only one language for the time being. */
+
+ qsort (occurs_table[0], number_of_occurs[0], sizeof (OCCURS),
+ compare_occurs);
+}
+
+/* Parameter files reading routines. */
+
+/*----------------------------------------------------------------------.
+| Read a file named FILE_NAME, containing a set of break characters. |
+| Build a content to the array word_fastmap in which all characters are |
+| allowed except those found in the file. Characters may be repeated. |
+`----------------------------------------------------------------------*/
+
+void
+digest_break_file (const char *file_name)
+{
+ BLOCK file_contents; /* to receive a copy of the file */
+ char *cursor; /* cursor in file copy */
+
+ swallow_file_in_memory (file_name, &file_contents);
+
+ /* Make the fastmap and record the file contents in it. */
+
+ memset (word_fastmap, 1, CHAR_SET_SIZE);
+ for (cursor = file_contents.start; cursor < file_contents.end; cursor++)
+ word_fastmap[(unsigned char) *cursor] = 0;
+
+ if (!gnu_extensions)
+ {
+
+ /* If GNU extensions are enabled, the only way to avoid newline as
+ a break character is to write all the break characters in the
+ file with no newline at all, not even at the end of the file.
+ If disabled, spaces, tabs and newlines are always considered as
+ break characters even if not included in the break file. */
+
+ word_fastmap[' '] = 0;
+ word_fastmap['\t'] = 0;
+ word_fastmap['\n'] = 0;
+ }
+
+ /* Return the space of the file, which is no more required. */
+
+ free (file_contents.start);
+}
+
+/*-----------------------------------------------------------------------.
+| Read a file named FILE_NAME, containing one word per line, then |
+| construct in TABLE a table of WORD descriptors for them. The routine |
+| swallows the whole file in memory; this is at the expense of space |
+| needed for newlines, which are useless; however, the reading is fast. |
+`-----------------------------------------------------------------------*/
+
+void
+digest_word_file (const char *file_name, WORD_TABLE *table)
+{
+ BLOCK file_contents; /* to receive a copy of the file */
+ char *cursor; /* cursor in file copy */
+ char *word_start; /* start of the current word */
+
+ swallow_file_in_memory (file_name, &file_contents);
+
+ table->start = NULL;
+ table->length = 0;
+
+ /* Read the whole file. */
+
+ cursor = file_contents.start;
+ while (cursor < file_contents.end)
+ {
+
+ /* Read one line, and save the word in contains. */
+
+ word_start = cursor;
+ while (cursor < file_contents.end && *cursor != '\n')
+ cursor++;
+
+ /* Record the word in table if it is not empty. */
+
+ if (cursor > word_start)
+ {
+ ALLOC_NEW_WORD (table);
+ table->start[table->length].start = word_start;
+ table->start[table->length].size = cursor - word_start;
+ table->length++;
+ }
+
+ /* This test allows for an incomplete line at end of file. */
+
+ if (cursor < file_contents.end)
+ cursor++;
+ }
+
+ /* Finally, sort all the words read. */
+
+ qsort (table->start, table->length, (size_t) sizeof (WORD), compare_words);
+}
+
+
+/* Keyword recognition and selection. */
+
+/*----------------------------------------------------------------------.
+| For each keyword in the source text, constructs an OCCURS structure. |
+`----------------------------------------------------------------------*/
+
+void
+find_occurs_in_text (void)
+{
+ char *cursor; /* for scanning the source text */
+ char *scan; /* for scanning the source text also */
+ char *line_start; /* start of the current input line */
+ char *line_scan; /* newlines scanned until this point */
+ int reference_length; /* length of reference in input mode */
+ WORD possible_key; /* possible key, to ease searches */
+ OCCURS *occurs_cursor; /* current OCCURS under construction */
+
+ char *context_start; /* start of left context */
+ char *context_end; /* end of right context */
+ char *word_start; /* start of word */
+ char *word_end; /* end of word */
+ char *next_context_start; /* next start of left context */
+
+ /* reference_length is always used within `if (input_reference)'.
+ However, GNU C diagnoses that it may be used uninitialized. The
+ following assignment is merely to shut it up. */
+
+ reference_length = 0;
+
+ /* Tracking where lines start is helpful for reference processing. In
+ auto reference mode, this allows counting lines. In input reference
+ mode, this permits finding the beginning of the references.
+
+ The first line begins with the file, skip immediately this very first
+ reference in input reference mode, to help further rejection any word
+ found inside it. Also, unconditionally assigning these variable has
+ the happy effect of shutting up lint. */
+
+ line_start = text_buffer.start;
+ line_scan = line_start;
+ if (input_reference)
+ {
+ SKIP_NON_WHITE (line_scan, text_buffer.end);
+ reference_length = line_scan - line_start;
+ SKIP_WHITE (line_scan, text_buffer.end);
+ }
+
+ /* Process the whole buffer, one line or one sentence at a time. */
+
+ for (cursor = text_buffer.start;
+ cursor < text_buffer.end;
+ cursor = next_context_start)
+ {
+
+ /* `context_start' gets initialized before the processing of each
+ line, or once for the whole buffer if no end of line or sentence
+ sequence separator. */
+
+ context_start = cursor;
+
+ /* If a end of line or end of sentence sequence is defined and
+ non-empty, `next_context_start' will be recomputed to be the end of
+ each line or sentence, before each one is processed. If no such
+ sequence, then `next_context_start' is set at the end of the whole
+ buffer, which is then considered to be a single line or sentence.
+ This test also accounts for the case of an incomplete line or
+ sentence at the end of the buffer. */
+
+ if (context_regex_string
+ && (re_search (context_regex, cursor, text_buffer.end - cursor,
+ 0, text_buffer.end - cursor, &context_regs)
+ >= 0))
+ next_context_start = cursor + context_regs.end[0];
+
+ else
+ next_context_start = text_buffer.end;
+
+ /* Include the separator into the right context, but not any suffix
+ white space in this separator; this insures it will be seen in
+ output and will not take more space than necessary. */
+
+ context_end = next_context_start;
+ SKIP_WHITE_BACKWARDS (context_end, context_start);
+
+ /* Read and process a single input line or sentence, one word at a
+ time. */
+
+ while (1)
+ {
+ if (word_regex)
+
+ /* If a word regexp has been compiled, use it to skip at the
+ beginning of the next word. If there is no such word, exit
+ the loop. */
+
+ {
+ if (re_search (word_regex, cursor, context_end - cursor,
+ 0, context_end - cursor, &word_regs)
+ < 0)
+ break;
+ word_start = cursor + word_regs.start[0];
+ word_end = cursor + word_regs.end[0];
+ }
+ else
+
+ /* Avoid re_search and use the fastmap to skip to the
+ beginning of the next word. If there is no more word in
+ the buffer, exit the loop. */
+
+ {
+ scan = cursor;
+ while (scan < context_end
+ && !word_fastmap[(unsigned char) *scan])
+ scan++;
+
+ if (scan == context_end)
+ break;
+
+ word_start = scan;
+
+ while (scan < context_end
+ && word_fastmap[(unsigned char) *scan])
+ scan++;
+
+ word_end = scan;
+ }
+
+ /* Skip right to the beginning of the found word. */
+
+ cursor = word_start;
+
+ /* Skip any zero length word. Just advance a single position,
+ then go fetch the next word. */
+
+ if (word_end == word_start)
+ {
+ cursor++;
+ continue;
+ }
+
+ /* This is a genuine, non empty word, so save it as a possible
+ key. Then skip over it. Also, maintain the maximum length of
+ all words read so far. It is mandatory to take the maximum
+ length of all words in the file, without considering if they
+ are actually kept or rejected, because backward jumps at output
+ generation time may fall in *any* word. */
+
+ possible_key.start = cursor;
+ possible_key.size = word_end - word_start;
+ cursor += possible_key.size;
+
+ if (possible_key.size > maximum_word_length)
+ maximum_word_length = possible_key.size;
+
+ /* In input reference mode, update `line_start' from its previous
+ value. Count the lines just in case auto reference mode is
+ also selected. If it happens that the word just matched is
+ indeed part of a reference; just ignore it. */
+
+ if (input_reference)
+ {
+ while (line_scan < possible_key.start)
+ if (*line_scan == '\n')
+ {
+ total_line_count++;
+ line_scan++;
+ line_start = line_scan;
+ SKIP_NON_WHITE (line_scan, text_buffer.end);
+ reference_length = line_scan - line_start;
+ }
+ else
+ line_scan++;
+ if (line_scan > possible_key.start)
+ continue;
+ }
+
+ /* Ignore the word if an `Ignore words' table exists and if it is
+ part of it. Also ignore the word if an `Only words' table and
+ if it is *not* part of it.
+
+ It is allowed that both tables be used at once, even if this
+ may look strange for now. Just ignore a word that would appear
+ in both. If regexps are eventually implemented for these
+ tables, the Ignore table could then reject words that would
+ have been previously accepted by the Only table. */
+
+ if (ignore_file && search_table (&possible_key, &ignore_table))
+ continue;
+ if (only_file && !search_table (&possible_key, &only_table))
+ continue;
+
+ /* A non-empty word has been found. First of all, insure
+ proper allocation of the next OCCURS, and make a pointer to
+ where it will be constructed. */
+
+ ALLOC_NEW_OCCURS (0);
+ occurs_cursor = occurs_table[0] + number_of_occurs[0];
+
+ /* Define the refence field, if any. */
+
+ if (auto_reference)
+ {
+
+ /* While auto referencing, update `line_start' from its
+ previous value, counting lines as we go. If input
+ referencing at the same time, `line_start' has been
+ advanced earlier, and the following loop is never really
+ executed. */
+
+ while (line_scan < possible_key.start)
+ if (*line_scan == '\n')
+ {
+ total_line_count++;
+ line_scan++;
+ line_start = line_scan;
+ SKIP_NON_WHITE (line_scan, text_buffer.end);
+ }
+ else
+ line_scan++;
+
+ occurs_cursor->reference = total_line_count;
+ }
+ else if (input_reference)
+ {
+
+ /* If only input referencing, `line_start' has been computed
+ earlier to detect the case the word matched would be part
+ of the reference. The reference position is simply the
+ value of `line_start'. */
+
+ occurs_cursor->reference
+ = (DELTA) (line_start - possible_key.start);
+ if (reference_length > reference_max_width)
+ reference_max_width = reference_length;
+ }
+
+ /* Exclude the reference from the context in simple cases. */
+
+ if (input_reference && line_start == context_start)
+ {
+ SKIP_NON_WHITE (context_start, context_end);
+ SKIP_WHITE (context_start, context_end);
+ }
+
+ /* Completes the OCCURS structure. */
+
+ occurs_cursor->key = possible_key;
+ occurs_cursor->left = context_start - possible_key.start;
+ occurs_cursor->right = context_end - possible_key.start;
+
+ number_of_occurs[0]++;
+ }
+ }
+}
+
+/* Formatting and actual output - service routines. */
+
+/*-----------------------------------------.
+| Prints some NUMBER of spaces on stdout. |
+`-----------------------------------------*/
+
+void
+print_spaces (int number)
+{
+ int counter;
+
+ for (counter = number; counter > 0; counter--)
+ putchar (' ');
+}
+
+/*-------------------------------------.
+| Prints the field provided by FIELD. |
+`-------------------------------------*/
+
+void
+print_field (BLOCK field)
+{
+ char *cursor; /* Cursor in field to print */
+ int character; /* Current character */
+ int base; /* Base character, without diacritic */
+ int diacritic; /* Diacritic code for the character */
+
+ /* Whitespace is not really compressed. Instead, each white space
+ character (tab, vt, ht etc.) is printed as one single space. */
+
+ for (cursor = field.start; cursor < field.end; cursor++)
+ {
+ character = (unsigned char) *cursor;
+ if (edited_flag[character])
+ {
+
+ /* First check if this is a diacriticized character.
+
+ This works only for TeX. I do not know how diacriticized
+ letters work with `roff'. Please someone explain it to me! */
+
+ diacritic = todiac (character);
+ if (diacritic != 0 && output_format == TEX_FORMAT)
+ {
+ base = tobase (character);
+ switch (diacritic)
+ {
+
+ case 1: /* Latin diphthongs */
+ switch (base)
+ {
+ case 'o':
+ printf ("\\oe{}");
+ break;
+
+ case 'O':
+ printf ("\\OE{}");
+ break;
+
+ case 'a':
+ printf ("\\ae{}");
+ break;
+
+ case 'A':
+ printf ("\\AE{}");
+ break;
+
+ default:
+ putchar (' ');
+ }
+ break;
+
+ case 2: /* Acute accent */
+ printf ("\\'%s%c", (base == 'i' ? "\\" : ""), base);
+ break;
+
+ case 3: /* Grave accent */
+ printf ("\\`%s%c", (base == 'i' ? "\\" : ""), base);
+ break;
+
+ case 4: /* Circumflex accent */
+ printf ("\\^%s%c", (base == 'i' ? "\\" : ""), base);
+ break;
+
+ case 5: /* Diaeresis */
+ printf ("\\\"%s%c", (base == 'i' ? "\\" : ""), base);
+ break;
+
+ case 6: /* Tilde accent */
+ printf ("\\~%s%c", (base == 'i' ? "\\" : ""), base);
+ break;
+
+ case 7: /* Cedilla */
+ printf ("\\c{%c}", base);
+ break;
+
+ case 8: /* Small circle beneath */
+ switch (base)
+ {
+ case 'a':
+ printf ("\\aa{}");
+ break;
+
+ case 'A':
+ printf ("\\AA{}");
+ break;
+
+ default:
+ putchar (' ');
+ }
+ break;
+
+ case 9: /* Strike through */
+ switch (base)
+ {
+ case 'o':
+ printf ("\\o{}");
+ break;
+
+ case 'O':
+ printf ("\\O{}");
+ break;
+
+ default:
+ putchar (' ');
+ }
+ break;
+ }
+ }
+ else
+
+ /* This is not a diacritic character, so handle cases which are
+ really specific to `roff' or TeX. All white space processing
+ is done as the default case of this switch. */
+
+ switch (character)
+ {
+ case '"':
+ /* In roff output format, double any quote. */
+ putchar ('"');
+ putchar ('"');
+ break;
+
+ case '$':
+ case '%':
+ case '&':
+ case '#':
+ case '_':
+ /* In TeX output format, precede these with a backslash. */
+ putchar ('\\');
+ putchar (character);
+ break;
+
+ case '{':
+ case '}':
+ /* In TeX output format, precede these with a backslash and
+ force mathematical mode. */
+ printf ("$\\%c$", character);
+ break;
+
+ case '\\':
+ /* In TeX output mode, request production of a backslash. */
+ printf ("\\backslash{}");
+ break;
+
+ default:
+ /* Any other flagged character produces a single space. */
+ putchar (' ');
+ }
+ }
+ else
+ putchar (*cursor);
+ }
+}
+
+
+/* Formatting and actual output - planning routines. */
+
+/*--------------------------------------------------------------------.
+| From information collected from command line options and input file |
+| readings, compute and fix some output parameter values. |
+`--------------------------------------------------------------------*/
+
+void
+fix_output_parameters (void)
+{
+ int file_index; /* index in text input file arrays */
+ int line_ordinal; /* line ordinal value for reference */
+ char ordinal_string[12]; /* edited line ordinal for reference */
+ int reference_width; /* width for the whole reference */
+ int character; /* character ordinal */
+ const char *cursor; /* cursor in some constant strings */
+
+ /* In auto reference mode, the maximum width of this field is
+ precomputed and subtracted from the overall line width. Add one for
+ the column which separate the file name from the line number. */
+
+ if (auto_reference)
+ {
+ reference_max_width = 0;
+ for (file_index = 0; file_index < number_input_files; file_index++)
+ {
+ line_ordinal = file_line_count[file_index] + 1;
+ if (file_index > 0)
+ line_ordinal -= file_line_count[file_index - 1];
+ sprintf (ordinal_string, "%d", line_ordinal);
+ reference_width = strlen (ordinal_string);
+ if (input_file_name[file_index])
+ reference_width += strlen (input_file_name[file_index]);
+ if (reference_width > reference_max_width)
+ reference_max_width = reference_width;
+ }
+ reference_max_width++;
+ reference.start = (char *) xmalloc (reference_max_width + 1);
+ }
+
+ /* If the reference appears to the left of the output line, reserve some
+ space for it right away, including one gap size. */
+
+ if ((auto_reference || input_reference) && !right_reference)
+ line_width -= reference_max_width + gap_size;
+
+ /* The output lines, minimally, will contain from left to right a left
+ context, a gap, and a keyword followed by the right context with no
+ special intervening gap. Half of the line width is dedicated to the
+ left context and the gap, the other half is dedicated to the keyword
+ and the right context; these values are computed once and for all here.
+ There also are tail and head wrap around fields, used when the keyword
+ is near the beginning or the end of the line, or when some long word
+ cannot fit in, but leave place from wrapped around shorter words. The
+ maximum width of these fields are recomputed separately for each line,
+ on a case by case basis. It is worth noting that it cannot happen that
+ both the tail and head fields are used at once. */
+
+ half_line_width = line_width / 2;
+ before_max_width = half_line_width - gap_size;
+ keyafter_max_width = half_line_width;
+
+ /* If truncation_string is the empty string, make it NULL to speed up
+ tests. In this case, truncation_string_length will never get used, so
+ there is no need to set it. */
+
+ if (truncation_string && *truncation_string)
+ truncation_string_length = strlen (truncation_string);
+ else
+ truncation_string = NULL;
+
+ if (gnu_extensions)
+ {
+
+ /* When flagging truncation at the left of the keyword, the
+ truncation mark goes at the beginning of the before field,
+ unless there is a head field, in which case the mark goes at the
+ left of the head field. When flagging truncation at the right
+ of the keyword, the mark goes at the end of the keyafter field,
+ unless there is a tail field, in which case the mark goes at the
+ end of the tail field. Only eight combination cases could arise
+ for truncation marks:
+
+ . None.
+ . One beginning the before field.
+ . One beginning the head field.
+ . One ending the keyafter field.
+ . One ending the tail field.
+ . One beginning the before field, another ending the keyafter field.
+ . One ending the tail field, another beginning the before field.
+ . One ending the keyafter field, another beginning the head field.
+
+ So, there is at most two truncation marks, which could appear both
+ on the left side of the center of the output line, both on the
+ right side, or one on either side. */
+
+ before_max_width -= 2 * truncation_string_length;
+ keyafter_max_width -= 2 * truncation_string_length;
+ }
+ else
+ {
+
+ /* I never figured out exactly how UNIX' ptx plans the output width
+ of its various fields. If GNU extensions are disabled, do not
+ try computing the field widths correctly; instead, use the
+ following formula, which does not completely imitate UNIX' ptx,
+ but almost. */
+
+ keyafter_max_width -= 2 * truncation_string_length + 1;
+ }
+
+ /* Compute which characters need special output processing. Initialize
+ by flagging any white space character. Some systems do not consider
+ form feed as a space character, but we do. */
+
+ for (character = 0; character < CHAR_SET_SIZE; character++)
+ edited_flag[character] = isspace (character);
+ edited_flag['\f'] = 1;
+
+ /* Complete the special character flagging according to selected output
+ format. */
+
+ switch (output_format)
+ {
+ case UNKNOWN_FORMAT:
+ /* Should never happen. */
+
+ case DUMB_FORMAT:
+ break;
+
+ case ROFF_FORMAT:
+
+ /* `Quote' characters should be doubled. */
+
+ edited_flag['"'] = 1;
+ break;
+
+ case TEX_FORMAT:
+
+ /* Various characters need special processing. */
+
+ for (cursor = "$%&#_{}\\"; *cursor; cursor++)
+ edited_flag[*cursor] = 1;
+
+ /* Any character with 8th bit set will print to a single space, unless
+ it is diacriticized. */
+
+ for (character = 0200; character < CHAR_SET_SIZE; character++)
+ edited_flag[character] = todiac (character) != 0;
+ break;
+ }
+}
+
+/*------------------------------------------------------------------.
+| Compute the position and length of all the output fields, given a |
+| pointer to some OCCURS. |
+`------------------------------------------------------------------*/
+
+void
+define_all_fields (OCCURS *occurs)
+{
+ int tail_max_width; /* allowable width of tail field */
+ int head_max_width; /* allowable width of head field */
+ char *cursor; /* running cursor in source text */
+ char *left_context_start; /* start of left context */
+ char *right_context_end; /* end of right context */
+ char *left_field_start; /* conservative start for `head'/`before' */
+ int file_index; /* index in text input file arrays */
+ const char *file_name; /* file name for reference */
+ int line_ordinal; /* line ordinal for reference */
+
+ /* Define `keyafter', start of left context and end of right context.
+ `keyafter' starts at the saved position for keyword and extend to the
+ right from the end of the keyword, eating separators or full words, but
+ not beyond maximum allowed width for `keyafter' field or limit for the
+ right context. Suffix spaces will be removed afterwards. */
+
+ keyafter.start = occurs->key.start;
+ keyafter.end = keyafter.start + occurs->key.size;
+ left_context_start = keyafter.start + occurs->left;
+ right_context_end = keyafter.start + occurs->right;
+
+ cursor = keyafter.end;
+ while (cursor < right_context_end
+ && cursor <= keyafter.start + keyafter_max_width)
+ {
+ keyafter.end = cursor;
+ SKIP_SOMETHING (cursor, right_context_end);
+ }
+ if (cursor <= keyafter.start + keyafter_max_width)
+ keyafter.end = cursor;
+
+ keyafter_truncation = truncation_string && keyafter.end < right_context_end;
+
+ SKIP_WHITE_BACKWARDS (keyafter.end, keyafter.start);
+
+ /* When the left context is wide, it might take some time to catch up from
+ the left context boundary to the beginning of the `head' or `before'
+ fields. So, in this case, to speed the catchup, we jump back from the
+ keyword, using some secure distance, possibly falling in the middle of
+ a word. A secure backward jump would be at least half the maximum
+ width of a line, plus the size of the longest word met in the whole
+ input. We conclude this backward jump by a skip forward of at least
+ one word. In this manner, we should not inadvertently accept only part
+ of a word. From the reached point, when it will be time to fix the
+ beginning of `head' or `before' fields, we will skip forward words or
+ delimiters until we get sufficiently near. */
+
+ if (-occurs->left > half_line_width + maximum_word_length)
+ {
+ left_field_start
+ = keyafter.start - (half_line_width + maximum_word_length);
+ SKIP_SOMETHING (left_field_start, keyafter.start);
+ }
+ else
+ left_field_start = keyafter.start + occurs->left;
+
+ /* `before' certainly ends at the keyword, but not including separating
+ spaces. It starts after than the saved value for the left context, by
+ advancing it until it falls inside the maximum allowed width for the
+ before field. There will be no prefix spaces either. `before' only
+ advances by skipping single separators or whole words. */
+
+ before.start = left_field_start;
+ before.end = keyafter.start;
+ SKIP_WHITE_BACKWARDS (before.end, before.start);
+
+ while (before.start + before_max_width < before.end)
+ SKIP_SOMETHING (before.start, before.end);
+
+ if (truncation_string)
+ {
+ cursor = before.start;
+ SKIP_WHITE_BACKWARDS (cursor, text_buffer.start);
+ before_truncation = cursor > left_context_start;
+ }
+ else
+ before_truncation = 0;
+
+ SKIP_WHITE (before.start, text_buffer.end);
+
+ /* The tail could not take more columns than what has been left in the
+ left context field, and a gap is mandatory. It starts after the
+ right context, and does not contain prefixed spaces. It ends at
+ the end of line, the end of buffer or when the tail field is full,
+ whichever comes first. It cannot contain only part of a word, and
+ has no suffixed spaces. */
+
+ tail_max_width
+ = before_max_width - (before.end - before.start) - gap_size;
+
+ if (tail_max_width > 0)
+ {
+ tail.start = keyafter.end;
+ SKIP_WHITE (tail.start, text_buffer.end);
+
+ tail.end = tail.start;
+ cursor = tail.end;
+ while (cursor < right_context_end
+ && cursor < tail.start + tail_max_width)
+ {
+ tail.end = cursor;
+ SKIP_SOMETHING (cursor, right_context_end);
+ }
+
+ if (cursor < tail.start + tail_max_width)
+ tail.end = cursor;
+
+ if (tail.end > tail.start)
+ {
+ keyafter_truncation = 0;
+ tail_truncation = truncation_string && tail.end < right_context_end;
+ }
+ else
+ tail_truncation = 0;
+
+ SKIP_WHITE_BACKWARDS (tail.end, tail.start);
+ }
+ else
+ {
+
+ /* No place left for a tail field. */
+
+ tail.start = NULL;
+ tail.end = NULL;
+ tail_truncation = 0;
+ }
+
+ /* `head' could not take more columns than what has been left in the right
+ context field, and a gap is mandatory. It ends before the left
+ context, and does not contain suffixed spaces. Its pointer is advanced
+ until the head field has shrunk to its allowed width. It cannot
+ contain only part of a word, and has no suffixed spaces. */
+
+ head_max_width
+ = keyafter_max_width - (keyafter.end - keyafter.start) - gap_size;
+
+ if (head_max_width > 0)
+ {
+ head.end = before.start;
+ SKIP_WHITE_BACKWARDS (head.end, text_buffer.start);
+
+ head.start = left_field_start;
+ while (head.start + head_max_width < head.end)
+ SKIP_SOMETHING (head.start, head.end);
+
+ if (head.end > head.start)
+ {
+ before_truncation = 0;
+ head_truncation = (truncation_string
+ && head.start > left_context_start);
+ }
+ else
+ head_truncation = 0;
+
+ SKIP_WHITE (head.start, head.end);
+ }
+ else
+ {
+
+ /* No place left for a head field. */
+
+ head.start = NULL;
+ head.end = NULL;
+ head_truncation = 0;
+ }
+
+ if (auto_reference)
+ {
+
+ /* Construct the reference text in preallocated space from the file
+ name and the line number. Find out in which file the reference
+ occurred. Standard input yields an empty file name. Insure line
+ numbers are one based, even if they are computed zero based. */
+
+ file_index = 0;
+ while (file_line_count[file_index] < occurs->reference)
+ file_index++;
+
+ file_name = input_file_name[file_index];
+ if (!file_name)
+ file_name = "";
+
+ line_ordinal = occurs->reference + 1;
+ if (file_index > 0)
+ line_ordinal -= file_line_count[file_index - 1];
+
+ sprintf (reference.start, "%s:%d", file_name, line_ordinal);
+ reference.end = reference.start + strlen (reference.start);
+ }
+ else if (input_reference)
+ {
+
+ /* Reference starts at saved position for reference and extends right
+ until some white space is met. */
+
+ reference.start = keyafter.start + (DELTA) occurs->reference;
+ reference.end = reference.start;
+ SKIP_NON_WHITE (reference.end, right_context_end);
+ }
+}
+
+
+/* Formatting and actual output - control routines. */
+
+/*----------------------------------------------------------------------.
+| Output the current output fields as one line for `troff' or `nroff'. |
+`----------------------------------------------------------------------*/
+
+void
+output_one_roff_line (void)
+{
+ /* Output the `tail' field. */
+
+ printf (".%s \"", macro_name);
+ print_field (tail);
+ if (tail_truncation)
+ printf ("%s", truncation_string);
+ putchar ('"');
+
+ /* Output the `before' field. */
+
+ printf (" \"");
+ if (before_truncation)
+ printf ("%s", truncation_string);
+ print_field (before);
+ putchar ('"');
+
+ /* Output the `keyafter' field. */
+
+ printf (" \"");
+ print_field (keyafter);
+ if (keyafter_truncation)
+ printf ("%s", truncation_string);
+ putchar ('"');
+
+ /* Output the `head' field. */
+
+ printf (" \"");
+ if (head_truncation)
+ printf ("%s", truncation_string);
+ print_field (head);
+ putchar ('"');
+
+ /* Conditionally output the `reference' field. */
+
+ if (auto_reference || input_reference)
+ {
+ printf (" \"");
+ print_field (reference);
+ putchar ('"');
+ }
+
+ putchar ('\n');
+}
+
+/*---------------------------------------------------------.
+| Output the current output fields as one line for `TeX'. |
+`---------------------------------------------------------*/
+
+void
+output_one_tex_line (void)
+{
+ BLOCK key; /* key field, isolated */
+ BLOCK after; /* after field, isolated */
+ char *cursor; /* running cursor in source text */
+
+ printf ("\\%s ", macro_name);
+ printf ("{");
+ print_field (tail);
+ printf ("}{");
+ print_field (before);
+ printf ("}{");
+ key.start = keyafter.start;
+ after.end = keyafter.end;
+ cursor = keyafter.start;
+ SKIP_SOMETHING (cursor, keyafter.end);
+ key.end = cursor;
+ after.start = cursor;
+ print_field (key);
+ printf ("}{");
+ print_field (after);
+ printf ("}{");
+ print_field (head);
+ printf ("}");
+ if (auto_reference || input_reference)
+ {
+ printf ("{");
+ print_field (reference);
+ printf ("}");
+ }
+ printf ("\n");
+}
+
+/*-------------------------------------------------------------------.
+| Output the current output fields as one line for a dumb terminal. |
+`-------------------------------------------------------------------*/
+
+void
+output_one_dumb_line (void)
+{
+ if (!right_reference)
+ if (auto_reference)
+ {
+
+ /* Output the `reference' field, in such a way that GNU emacs
+ next-error will handle it. The ending colon is taken from the
+ gap which follows. */
+
+ print_field (reference);
+ putchar (':');
+ print_spaces (reference_max_width
+ + gap_size
+ - (reference.end - reference.start)
+ - 1);
+ }
+ else
+ {
+
+ /* Output the `reference' field and its following gap. */
+
+ print_field (reference);
+ print_spaces (reference_max_width
+ + gap_size
+ - (reference.end - reference.start));
+ }
+
+ if (tail.start < tail.end)
+ {
+ /* Output the `tail' field. */
+
+ print_field (tail);
+ if (tail_truncation)
+ printf ("%s", truncation_string);
+
+ print_spaces (half_line_width - gap_size
+ - (before.end - before.start)
+ - (before_truncation ? truncation_string_length : 0)
+ - (tail.end - tail.start)
+ - (tail_truncation ? truncation_string_length : 0));
+ }
+ else
+ print_spaces (half_line_width - gap_size
+ - (before.end - before.start)
+ - (before_truncation ? truncation_string_length : 0));
+
+ /* Output the `before' field. */
+
+ if (before_truncation)
+ printf ("%s", truncation_string);
+ print_field (before);
+
+ print_spaces (gap_size);
+
+ /* Output the `keyafter' field. */
+
+ print_field (keyafter);
+ if (keyafter_truncation)
+ printf ("%s", truncation_string);
+
+ if (head.start < head.end)
+ {
+ /* Output the `head' field. */
+
+ print_spaces (half_line_width
+ - (keyafter.end - keyafter.start)
+ - (keyafter_truncation ? truncation_string_length : 0)
+ - (head.end - head.start)
+ - (head_truncation ? truncation_string_length : 0));
+ if (head_truncation)
+ printf ("%s", truncation_string);
+ print_field (head);
+ }
+ else
+
+ if ((auto_reference || input_reference) && right_reference)
+ print_spaces (half_line_width
+ - (keyafter.end - keyafter.start)
+ - (keyafter_truncation ? truncation_string_length : 0));
+
+ if ((auto_reference || input_reference) && right_reference)
+ {
+ /* Output the `reference' field. */
+
+ print_spaces (gap_size);
+ print_field (reference);
+ }
+
+ printf ("\n");
+}
+
+/*------------------------------------------------------------------------.
+| Scan the whole occurs table and, for each entry, output one line in the |
+| appropriate format. |
+`------------------------------------------------------------------------*/
+
+void
+generate_all_output (void)
+{
+ int occurs_index; /* index of keyword entry being processed */
+ OCCURS *occurs_cursor; /* current keyword entry being processed */
+
+
+ /* The following assignments are useful to provide default values in case
+ line contexts or references are not used, in which case these variables
+ would never be computed. */
+
+ tail.start = NULL;
+ tail.end = NULL;
+ tail_truncation = 0;
+
+ head.start = NULL;
+ head.end = NULL;
+ head_truncation = 0;
+
+
+ /* Loop over all keyword occurrences. */
+
+ occurs_cursor = occurs_table[0];
+
+ for (occurs_index = 0; occurs_index < number_of_occurs[0]; occurs_index++)
+ {
+ /* Compute the exact size of every field and whenever truncation flags
+ are present or not. */
+
+ define_all_fields (occurs_cursor);
+
+ /* Produce one output line according to selected format. */
+
+ switch (output_format)
+ {
+ case UNKNOWN_FORMAT:
+ /* Should never happen. */
+
+ case DUMB_FORMAT:
+ output_one_dumb_line ();
+ break;
+
+ case ROFF_FORMAT:
+ output_one_roff_line ();
+ break;
+
+ case TEX_FORMAT:
+ output_one_tex_line ();
+ break;
+ }
+
+ /* Advance the cursor into the occurs table. */
+
+ occurs_cursor++;
+ }
+}
+
+/* Option decoding and main program. */
+
+/*------------------------------------------------------.
+| Print program identification and options, then exit. |
+`------------------------------------------------------*/
+
+void
+usage (int status)
+{
+ if (status != 0)
+ fprintf (stderr, "Try `%s --help' for more information.\n", program_name);
+ else
+ {
+ printf ("\
+Usage: %s [OPTION]... [INPUT]... (without -G)\n\
+ or: %s -G [OPTION]... [INPUT [OUTPUT]]\n", program_name, program_name);
+ printf ("\
+\n\
+ -A, --auto-reference output automatically generated references\n\
+ -C, --copyright display Copyright and copying conditions\n\
+ -G, --traditional behave more like System V `ptx'\n\
+ -F, --flag-truncation=STRING use STRING for flagging line truncations\n\
+ -M, --macro-name=STRING macro name to use instead of `xx'\n\
+ -O, --format=roff generate output as roff directives\n\
+ -R, --right-side-refs put references at right, not counted in -w\n\
+ -S, --sentence-regexp=REGEXP for end of lines or end of sentences\n\
+ -T, --format=tex generate output as TeX directives\n\
+ -W, --word-regexp=REGEXP use REGEXP to match each keyword\n\
+ -b, --break-file=FILE word break characters in this FILE\n\
+ -f, --ignore-case fold lower case to upper case for sorting\n\
+ -g, --gap-size=NUMBER gap size in columns between output fields\n\
+ -i, --ignore-file=FILE read ignore word list from FILE\n\
+ -o, --only-file=FILE read only word list from this FILE\n\
+ -r, --references first field of each line is a reference\n\
+ -t, --typeset-mode - not implemented -\n\
+ -w, --width=NUMBER output width in columns, reference excluded\n\
+ --help display this help and exit\n\
+ --version output version information and exit\n\
+\n\
+With no FILE or if FILE is -, read Standard Input. `-F /' by default.\n");
+ }
+ exit (status);
+}
+
+/*----------------------------------------------------------------------.
+| Main program. Decode ARGC arguments passed through the ARGV array of |
+| strings, then launch execution. |
+`----------------------------------------------------------------------*/
+
+/* Long options equivalences. */
+const struct option long_options[] =
+{
+ {"auto-reference", no_argument, NULL, 'A'},
+ {"break-file", required_argument, NULL, 'b'},
+ {"copyright", no_argument, NULL, 'C'},
+ {"flag-truncation", required_argument, NULL, 'F'},
+ {"ignore-case", no_argument, NULL, 'f'},
+ {"gap-size", required_argument, NULL, 'g'},
+ {"help", no_argument, &show_help, 1},
+ {"ignore-file", required_argument, NULL, 'i'},
+ {"macro-name", required_argument, NULL, 'M'},
+ {"only-file", required_argument, NULL, 'o'},
+ {"references", no_argument, NULL, 'r'},
+ {"right-side-refs", no_argument, NULL, 'R'},
+ {"format", required_argument, NULL, 10},
+ {"sentence-regexp", required_argument, NULL, 'S'},
+ {"traditional", no_argument, NULL, 'G'},
+ {"typeset-mode", no_argument, NULL, 't'},
+ {"version", no_argument, &show_version, 1},
+ {"width", required_argument, NULL, 'w'},
+ {"word-regexp", required_argument, NULL, 'W'},
+ {0, 0, 0, 0},
+};
+
+static char const* const format_args[] =
+{
+ "roff", "tex", 0
+};
+
+int
+main (int argc, char *const argv[])
+{
+ int optchar; /* argument character */
+ extern int optind; /* index of argument */
+ extern char *optarg; /* value or argument */
+ int file_index; /* index in text input file arrays */
+
+#ifdef HAVE_MCHECK
+ /* Use GNU malloc checking. It has proven to be useful! */
+ mcheck ();
+#endif /* HAVE_MCHECK */
+
+#ifdef STDC_HEADERS
+#ifdef HAVE_SETCHRCLASS
+ setchrclass (NULL);
+#endif
+#endif
+
+ /* Decode program options. */
+
+ program_name = argv[0];
+
+ while ((optchar = getopt_long (argc, argv, "ACF:GM:ORS:TW:b:i:fg:o:trw:",
+ long_options, NULL)),
+ optchar != EOF)
+ {
+ switch (optchar)
+ {
+ default:
+ usage (1);
+
+ case 0:
+ break;
+
+ case 'C':
+ printf ("%s", copyright);
+ exit (0);
+
+ case 'G':
+ gnu_extensions = 0;
+ break;
+
+ case 'b':
+ break_file = optarg;
+ break;
+
+ case 'f':
+ ignore_case = 1;
+ break;
+
+ case 'g':
+ gap_size = atoi (optarg);
+ break;
+
+ case 'i':
+ ignore_file = optarg;
+ break;
+
+ case 'o':
+ only_file = optarg;
+ break;
+
+ case 'r':
+ input_reference = 1;
+ break;
+
+ case 't':
+ /* A decouvrir... */
+ break;
+
+ case 'w':
+ line_width = atoi (optarg);
+ break;
+
+ case 'A':
+ auto_reference = 1;
+ break;
+
+ case 'F':
+ truncation_string = copy_unescaped_string (optarg);
+ break;
+
+ case 'M':
+ macro_name = optarg;
+ break;
+
+ case 'O':
+ output_format = ROFF_FORMAT;
+ break;
+
+ case 'R':
+ right_reference = 1;
+ break;
+
+ case 'S':
+ context_regex_string = copy_unescaped_string (optarg);
+ break;
+
+ case 'T':
+ output_format = TEX_FORMAT;
+ break;
+
+ case 'W':
+ word_regex_string = copy_unescaped_string (optarg);
+ break;
+
+ case 10:
+ switch (argmatch (optarg, format_args))
+ {
+ default:
+ usage (1);
+
+ case 0:
+ output_format = ROFF_FORMAT;
+ break;
+
+ case 1:
+ output_format = TEX_FORMAT;
+ break;
+ }
+ }
+ }
+
+ /* Process trivial options. */
+
+ if (show_help)
+ usage (0);
+
+ if (show_version)
+ {
+ printf ("%s\n", version_string);
+ exit (0);
+ }
+
+ /* Change the default Ignore file if one is defined. */
+
+#ifdef DEFAULT_IGNORE_FILE
+ if (!ignore_file)
+ ignore_file = DEFAULT_IGNORE_FILE;
+#endif
+
+ /* Process remaining arguments. If GNU extensions are enabled, process
+ all arguments as input parameters. If disabled, accept at most two
+ arguments, the second of which is an output parameter. */
+
+ if (optind == argc)
+ {
+
+ /* No more argument simply means: read standard input. */
+
+ input_file_name = (const char **) xmalloc (sizeof (const char *));
+ file_line_count = (int *) xmalloc (sizeof (int));
+ number_input_files = 1;
+ input_file_name[0] = NULL;
+ }
+ else if (gnu_extensions)
+ {
+ number_input_files = argc - optind;
+ input_file_name
+ = (const char **) xmalloc (number_input_files * sizeof (const char *));
+ file_line_count
+ = (int *) xmalloc (number_input_files * sizeof (int));
+
+ for (file_index = 0; file_index < number_input_files; file_index++)
+ {
+ input_file_name[file_index] = argv[optind];
+ if (!*argv[optind] || strcmp (argv[optind], "-") == 0)
+ input_file_name[0] = NULL;
+ else
+ input_file_name[0] = argv[optind];
+ optind++;
+ }
+ }
+ else
+ {
+
+ /* There is one necessary input file. */
+
+ number_input_files = 1;
+ input_file_name = (const char **) xmalloc (sizeof (const char *));
+ file_line_count = (int *) xmalloc (sizeof (int));
+ if (!*argv[optind] || strcmp (argv[optind], "-") == 0)
+ input_file_name[0] = NULL;
+ else
+ input_file_name[0] = argv[optind];
+ optind++;
+
+ /* Redirect standard output, only if requested. */
+
+ if (optind < argc)
+ {
+ fclose (stdout);
+ if (fopen (argv[optind], "w") == NULL)
+ error (1, errno, argv[optind]);
+ optind++;
+ }
+
+ /* Diagnose any other argument as an error. */
+
+ if (optind < argc)
+ usage (1);
+ }
+
+ /* If the output format has not been explicitly selected, choose dumb
+ terminal format if GNU extensions are enabled, else `roff' format. */
+
+ if (output_format == UNKNOWN_FORMAT)
+ output_format = gnu_extensions ? DUMB_FORMAT : ROFF_FORMAT;
+
+ /* Initialize the main tables. */
+
+ initialize_regex ();
+
+ /* Read `Break character' file, if any. */
+
+ if (break_file)
+ digest_break_file (break_file);
+
+ /* Read `Ignore words' file and `Only words' files, if any. If any of
+ these files is empty, reset the name of the file to NULL, to avoid
+ unnecessary calls to search_table. */
+
+ if (ignore_file)
+ {
+ digest_word_file (ignore_file, &ignore_table);
+ if (ignore_table.length == 0)
+ ignore_file = NULL;
+ }
+
+ if (only_file)
+ {
+ digest_word_file (only_file, &only_table);
+ if (only_table.length == 0)
+ only_file = NULL;
+ }
+
+ /* Prepare to study all the input files. */
+
+ number_of_occurs[0] = 0;
+ total_line_count = 0;
+ maximum_word_length = 0;
+ reference_max_width = 0;
+
+ for (file_index = 0; file_index < number_input_files; file_index++)
+ {
+
+ /* Read the file in core, than study it. */
+
+ swallow_file_in_memory (input_file_name[file_index], &text_buffer);
+ find_occurs_in_text ();
+
+ /* Maintain for each file how many lines has been read so far when its
+ end is reached. Incrementing the count first is a simple kludge to
+ handle a possible incomplete line at end of file. */
+
+ total_line_count++;
+ file_line_count[file_index] = total_line_count;
+ }
+
+ /* Do the output process phase. */
+
+ sort_found_occurs ();
+ fix_output_parameters ();
+ generate_all_output ();
+
+ /* All done. */
+
+ exit (0);
+}
diff --git a/gnu/usr.bin/ptx/ptx.info b/gnu/usr.bin/ptx/ptx.info
new file mode 100644
index 0000000..3bbd1bb
--- /dev/null
+++ b/gnu/usr.bin/ptx/ptx.info
@@ -0,0 +1,496 @@
+This is Info file ptx.info, produced by Makeinfo-1.47 from the input
+file ./ptx.texinfo.
+
+ This file documents the `ptx' command, which has the purpose of
+generated permuted indices for group of files.
+
+ Copyright (C) 1990, 1991, 1993 by the Free Software Foundation, Inc.
+
+ Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of
+this manual under the conditions for verbatim copying, provided that
+the entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+ Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be stated in a
+translation approved by the Foundation.
+
+
+File: ptx.info, Node: Top, Next: Invoking ptx, Prev: (dir), Up: (dir)
+
+Introduction
+************
+
+ This is the 0.3 beta release of `ptx', the GNU version of a permuted
+index generator. This software has the main goal of providing a
+replacement for the traditional `ptx' as found on System V machines,
+able to handle small files quickly, while providing a platform for more
+development.
+
+ This version reimplements and extends traditional `ptx'. Among
+other things, it can produce a readable "KWIC" (keywords in their
+context) without the need of `nroff', there is also an option to
+produce TeX compatible output. This version does not handle huge input
+files, that is, those files which do not fit in memory all at once.
+
+ *Please note* that an overall renaming of all options is
+foreseeable. In fact, GNU ptx specifications are not frozen yet.
+
+* Menu:
+
+* Invoking ptx:: How to use this program
+* Compatibility:: The GNU extensions to `ptx'
+
+ -- The Detailed Node Listing --
+
+How to use this program
+
+* General options:: Options which affect general program behaviour.
+* Charset selection:: Underlying character set considerations.
+* Input processing:: Input fields, contexts, and keyword selection.
+* Output formatting:: Types of output format, and sizing the fields.
+
+
+File: ptx.info, Node: Invoking ptx, Next: Compatibility, Prev: Top, Up: Top
+
+How to use this program
+***********************
+
+ This tool reads a text file and essentially produces a permuted
+index, with each keyword in its context. The calling sketch is one of:
+
+ ptx [OPTION ...] [FILE ...]
+
+ or:
+
+ ptx -G [OPTION ...] [INPUT [OUTPUT]]
+
+ The `-G' (or its equivalent: `--traditional') option disables all
+GNU extensions and revert to traditional mode, thus introducing some
+limitations, and changes several of the program's default option values.
+When `-G' is not specified, GNU extensions are always enabled. GNU
+extensions to `ptx' are documented wherever appropriate in this
+document. See *Note Compatibility:: for an explicit list of them.
+
+ Individual options are explained later in this document.
+
+ When GNU extensions are enabled, there may be zero, one or several
+FILE after the options. If there is no FILE, the program reads the
+standard input. If there is one or several FILE, they give the name of
+input files which are all read in turn, as if all the input files were
+concatenated. However, there is a full contextual break between each
+file and, when automatic referencing is requested, file names and line
+numbers refer to individual text input files. In all cases, the
+program produces the permuted index onto the standard output.
+
+ When GNU extensions are *not* enabled, that is, when the program
+operates in traditional mode, there may be zero, one or two parameters
+besides the options. If there is no parameters, the program reads the
+standard input and produces the permuted index onto the standard output.
+If there is only one parameter, it names the text INPUT to be read
+instead of the standard input. If two parameters are given, they give
+respectively the name of the INPUT file to read and the name of the
+OUTPUT file to produce. *Be very careful* to note that, in this case,
+the contents of file given by the second parameter is destroyed. This
+behaviour is dictated only by System V `ptx' compatibility, because GNU
+Standards discourage output parameters not introduced by an option.
+
+ Note that for *any* file named as the value of an option or as an
+input text file, a single dash `-' may be used, in which case standard
+input is assumed. However, it would not make sense to use this
+convention more than once per program invocation.
+
+* Menu:
+
+* General options:: Options which affect general program behaviour.
+* Charset selection:: Underlying character set considerations.
+* Input processing:: Input fields, contexts, and keyword selection.
+* Output formatting:: Types of output format, and sizing the fields.
+
+
+File: ptx.info, Node: General options, Next: Charset selection, Prev: Invoking ptx, Up: Invoking ptx
+
+General options
+===============
+
+`-C'
+`--copyright'
+ Prints a short note about the Copyright and copying conditions,
+ then exit without further processing.
+
+`-G'
+`--traditional'
+ As already explained, this option disables all GNU extensions to
+ `ptx' and switch to traditional mode.
+
+`--help'
+ Prints a short help on standard output, then exit without further
+ processing.
+
+`--version'
+ Prints the program verison on standard output, then exit without
+ further processing.
+
+
+File: ptx.info, Node: Charset selection, Next: Input processing, Prev: General options, Up: Invoking ptx
+
+Charset selection
+=================
+
+ As it is setup now, the program assumes that the input file is coded
+using 8-bit ISO 8859-1 code, also known as Latin-1 character set,
+*unless* if it is compiled for MS-DOS, in which case it uses the
+character set of the IBM-PC. (GNU `ptx' is not known to work on
+smaller MS-DOS machines anymore.) Compared to 7-bit ASCII, the set of
+characters which are letters is then different, this fact alters the
+behaviour of regular expression matching. Thus, the default regular
+expression for a keyword allows foreign or diacriticized letters.
+Keyword sorting, however, is still crude; it obeys the underlying
+character set ordering quite blindly.
+
+`-f'
+`--ignore-case'
+ Fold lower case letters to upper case for sorting.
+
+
+File: ptx.info, Node: Input processing, Next: Output formatting, Prev: Charset selection, Up: Invoking ptx
+
+Word selection
+==============
+
+`-b FILE'
+`--break-file=FILE'
+ This option is an alternative way to option `-W' for describing
+ which characters make up words. This option introduces the name
+ of a file which contains a list of characters which can*not* be
+ part of one word, this file is called the "Break file". Any
+ character which is not part of the Break file is a word
+ constituent. If both options `-b' and `-W' are specified, then
+ `-W' has precedence and `-b' is ignored.
+
+ When GNU extensions are enabled, the only way to avoid newline as a
+ break character is to write all the break characters in the file
+ with no newline at all, not even at the end of the file. When GNU
+ extensions are disabled, spaces, tabs and newlines are always
+ considered as break characters even if not included in the Break
+ file.
+
+`-i FILE'
+`--ignore-file=FILE'
+ The file associated with this option contains a list of words
+ which will never be taken as keywords in concordance output. It
+ is called the "Ignore file". The file contains exactly one word
+ in each line; the end of line separation of words is not subject
+ to the value of the `-S' option.
+
+ There is a default Ignore file used by `ptx' when this option is
+ not specified, usually found in `/usr/local/lib/eign' if this has
+ not been changed at installation time. If you want to deactivate
+ the default Ignore file, specify `/dev/null' instead.
+
+`-o FILE'
+`--only-file=FILE'
+ The file associated with this option contains a list of words
+ which will be retained in concordance output, any word not
+ mentioned in this file is ignored. The file is called the "Only
+ file". The file contains exactly one word in each line; the end
+ of line separation of words is not subject to the value of the
+ `-S' option.
+
+ There is no default for the Only file. In the case there are both
+ an Only file and an Ignore file, a word will be subject to be a
+ keyword only if it is given in the Only file and not given in the
+ Ignore file.
+
+`-r'
+`--references'
+ On each input line, the leading sequence of non white characters
+ will be taken to be a reference that has the purpose of
+ identifying this input line on the produced permuted index. See
+ *Note Output formatting:: for more information about reference
+ production. Using this option change the default value for option
+ `-S'.
+
+ Using this option, the program does not try very hard to remove
+ references from contexts in output, but it succeeds in doing so
+ *when* the context ends exactly at the newline. If option `-r' is
+ used with `-S' default value, or when GNU extensions are disabled,
+ this condition is always met and references are completely
+ excluded from the output contexts.
+
+`-S REGEXP'
+`--sentence-regexp=REGEXP'
+ This option selects which regular expression will describe the end
+ of a line or the end of a sentence. In fact, there is other
+ distinction between end of lines or end of sentences than the
+ effect of this regular expression, and input line boundaries have
+ no special significance outside this option. By default, when GNU
+ extensions are enabled and if `-r' option is not used, end of
+ sentences are used. In this case, the precise REGEX is imported
+ from GNU emacs:
+
+ [.?!][]\"')}]*\\($\\|\t\\| \\)[ \t\n]*
+
+ Whenever GNU extensions are disabled or if `-r' option is used, end
+ of lines are used; in this case, the default REGEXP is just:
+
+ \n
+
+ Using an empty REGEXP is equivalent to completely disabling end of
+ line or end of sentence recognition. In this case, the whole file
+ is considered to be a single big line or sentence. The user might
+ want to disallow all truncation flag generation as well, through
+ option `-F ""'. *Note Syntax of Regular Expressions:
+ (emacs)Regexps.
+
+ When the keywords happen to be near the beginning of the input
+ line or sentence, this often creates an unused area at the
+ beginning of the output context line; when the keywords happen to
+ be near the end of the input line or sentence, this often creates
+ an unused area at the end of the output context line. The program
+ tries to fill those unused areas by wrapping around context in
+ them; the tail of the input line or sentence is used to fill the
+ unused area on the left of the output line; the head of the input
+ line or sentence is used to fill the unused area on the right of
+ the output line.
+
+ As a matter of convenience to the user, many usual backslashed
+ escape sequences, as found in the C language, are recognized and
+ converted to the corresponding characters by `ptx' itself.
+
+`-W REGEXP'
+`--word-regexp=REGEXP'
+ This option selects which regular expression will describe each
+ keyword. By default, if GNU extensions are enabled, a word is a
+ sequence of letters; the REGEXP used is `\w+'. When GNU
+ extensions are disabled, a word is by default anything which ends
+ with a space, a tab or a newline; the REGEXP used is `[^ \t\n]+'.
+
+ An empty REGEXP is equivalent to not using this option, letting the
+ default dive in. *Note Syntax of Regular Expressions:
+ (emacs)Regexps.
+
+ As a matter of convenience to the user, many usual backslashed
+ escape sequences, as found in the C language, are recognized and
+ converted to the corresponding characters by `ptx' itself.
+
+
+File: ptx.info, Node: Output formatting, Prev: Input processing, Up: Invoking ptx
+
+Output formatting
+=================
+
+ Output format is mainly controlled by `-O' and `-T' options,
+described in the table below. When neither `-O' nor `-T' is selected,
+and if GNU extensions are enabled, the program choose an output format
+suited for a dumb terminal. Each keyword occurrence is output to the
+center of one line, surrounded by its left and right contexts. Each
+field is properly justified, so the concordance output could readily be
+observed. As a special feature, if automatic references are selected
+by option `-A' and are output before the left context, that is, if
+option `-R' is *not* selected, then a colon is added after the
+reference; this nicely interfaces with GNU Emacs `next-error'
+processing. In this default output format, each white space character,
+like newline and tab, is merely changed to exactly one space, with no
+special attempt to compress consecutive spaces. This might change in
+the future. Except for those white space characters, every other
+character of the underlying set of 256 characters is transmitted
+verbatim.
+
+ Output format is further controlled by the following options.
+
+`-g NUMBER'
+`--gap-size=NUMBER'
+ Select the size of the minimum white gap between the fields on the
+ output line.
+
+`-w NUMBER'
+`--width=NUMBER'
+ Select the output maximum width of each final line. If references
+ are used, they are included or excluded from the output maximum
+ width depending on the value of option `-R'. If this option is not
+ selected, that is, when references are output before the left
+ context, the output maximum width takes into account the maximum
+ length of all references. If this options is selected, that is,
+ when references are output after the right context, the output
+ maximum width does not take into account the space taken by
+ references, nor the gap that precedes them.
+
+`-A'
+`--auto-reference'
+ Select automatic references. Each input line will have an
+ automatic reference made up of the file name and the line ordinal,
+ with a single colon between them. However, the file name will be
+ empty when standard input is being read. If both `-A' and `-r'
+ are selected, then the input reference is still read and skipped,
+ but the automatic reference is used at output time, overriding the
+ input reference.
+
+`-R'
+`--right-side-refs'
+ In default output format, when option `-R' is not used, any
+ reference produced by the effect of options `-r' or `-A' are given
+ to the far right of output lines, after the right context. In
+ default output format, when option `-R' is specified, references
+ are rather given to the beginning of each output line, before the
+ left context. For any other output format, option `-R' is almost
+ ignored, except for the fact that the width of references is *not*
+ taken into account in total output width given by `-w' whenever
+ `-R' is selected.
+
+ This option is automatically selected whenever GNU extensions are
+ disabled.
+
+`-F STRING'
+`--flac-truncation=STRING'
+ This option will request that any truncation in the output be
+ reported using the string STRING. Most output fields
+ theoretically extend towards the beginning or the end of the
+ current line, or current sentence, as selected with option `-S'.
+ But there is a maximum allowed output line width, changeable
+ through option `-w', which is further divided into space for
+ various output fields. When a field has to be truncated because
+ cannot extend until the beginning or the end of the current line
+ to fit in the, then a truncation occurs. By default, the string
+ used is a single slash, as in `-F /'.
+
+ STRING may have more than one character, as in `-F ...'. Also, in
+ the particular case STRING is empty (`-F ""'), truncation flagging
+ is disabled, and no truncation marks are appended in this case.
+
+ As a matter of convenience to the user, many usual backslashed
+ escape sequences, as found in the C language, are recognized and
+ converted to the corresponding characters by `ptx' itself.
+
+`-M STRING'
+`--macro-name=STRING'
+ Select another STRING to be used instead of `xx', while generating
+ output suitable for `nroff', `troff' or TeX.
+
+`-O'
+`--format=roff'
+ Choose an output format suitable for `nroff' or `troff'
+ processing. Each output line will look like:
+
+ .xx "TAIL" "BEFORE" "KEYWORD_AND_AFTER" "HEAD" "REF"
+
+ so it will be possible to write an `.xx' roff macro to take care of
+ the output typesetting. This is the default output format when GNU
+ extensions are disabled. Option `-M' might be used to change `xx'
+ to another macro name.
+
+ In this output format, each non-graphical character, like newline
+ and tab, is merely changed to exactly one space, with no special
+ attempt to compress consecutive spaces. Each quote character: `"'
+ is doubled so it will be correctly processed by `nroff' or `troff'.
+
+`-T'
+`--format=tex'
+ Choose an output format suitable for TeX processing. Each output
+ line will look like:
+
+ \xx {TAIL}{BEFORE}{KEYWORD}{AFTER}{HEAD}{REF}
+
+ so it will be possible to write write a `\xx' definition to take
+ care of the output typesetting. Note that when references are not
+ being produced, that is, neither option `-A' nor option `-r' is
+ selected, the last parameter of each `\xx' call is inhibited.
+ Option `-M' might be used to change `xx' to another macro name.
+
+ In this output format, some special characters, like `$', `%',
+ `&', `#' and `_' are automatically protected with a backslash.
+ Curly brackets `{', `}' are also protected with a backslash, but
+ also enclosed in a pair of dollar signs to force mathematical
+ mode. The backslash itself produces the sequence `\backslash{}'.
+ Circumflex and tilde diacritics produce the sequence `^\{ }' and
+ `~\{ }' respectively. Other diacriticized characters of the
+ underlying character set produce an appropriate TeX sequence as
+ far as possible. The other non-graphical characters, like newline
+ and tab, and all others characters which are not part of ASCII,
+ are merely changed to exactly one space, with no special attempt
+ to compress consecutive spaces. Let me know how to improve this
+ special character processing for TeX.
+
+
+File: ptx.info, Node: Compatibility, Prev: Invoking ptx, Up: Top
+
+The GNU extensions to `ptx'
+***************************
+
+ This version of `ptx' contains a few features which do not exist in
+System V `ptx'. These extra features are suppressed by using the `-G'
+command line option, unless overridden by other command line options.
+Some GNU extensions cannot be recovered by overriding, so the simple
+rule is to avoid `-G' if you care about GNU extensions. Here are the
+differences between this program and System V `ptx'.
+
+ * This program can read many input files at once, it always writes
+ the resulting concordance on standard output. On the other end,
+ System V `ptx' reads only one file and produce the result on
+ standard output or, if a second FILE parameter is given on the
+ command, to that FILE.
+
+ Having output parameters not introduced by options is a quite
+ dangerous practice which GNU avoids as far as possible. So, for
+ using `ptx' portably between GNU and System V, you should pay
+ attention to always use it with a single input file, and always
+ expect the result on standard output. You might also want to
+ automatically configure in a `-G' option to `ptx' calls in
+ products using `ptx', if the configurator finds that the installed
+ `ptx' accepts `-G'.
+
+ * The only options available in System V `ptx' are options `-b',
+ `-f', `-g', `-i', `-o', `-r', `-t' and `-w'. All other options
+ are GNU extensions and are not repeated in this enumeration.
+ Moreover, some options have a slightly different meaning when GNU
+ extensions are enabled, as explained below.
+
+ * By default, concordance output is not formatted for `troff' or
+ `nroff'. It is rather formatted for a dumb terminal. `troff' or
+ `nroff' output may still be selected through option `-O'.
+
+ * Unless `-R' option is used, the maximum reference width is
+ subtracted from the total output line width. With GNU extensions
+ disabled, width of references is not taken into account in the
+ output line width computations.
+
+ * All 256 characters, even `NUL's, are always read and processed from
+ input file with no adverse effect, even if GNU extensions are
+ disabled. However, System V `ptx' does not accept 8-bit
+ characters, a few control characters are rejected, and the tilda
+ `~' is condemned.
+
+ * Input line length is only limited by available memory, even if GNU
+ extensions are disabled. However, System V `ptx' processes only
+ the first 200 characters in each line.
+
+ * The break (non-word) characters default to be every character
+ except all letters of the underlying character set, diacriticized
+ or not. When GNU extensions are disabled, the break characters
+ default to space, tab and newline only.
+
+ * The program makes better use of output line width. If GNU
+ extensions are disabled, the program rather tries to imitate
+ System V `ptx', but still, there are some slight disposition
+ glitches this program does not completely reproduce.
+
+ * The user can specify both an Ignore file and an Only file. This
+ is not allowed with System V `ptx'.
+
+
+
+Tag Table:
+Node: Top939
+Node: Invoking ptx2298
+Node: General options5025
+Node: Charset selection5639
+Node: Input processing6514
+Node: Output formatting12205
+Node: Compatibility18737
+
+End Tag Table
diff --git a/gnu/usr.bin/ptx/ptx.texinfo b/gnu/usr.bin/ptx/ptx.texinfo
new file mode 100644
index 0000000..e690c55
--- /dev/null
+++ b/gnu/usr.bin/ptx/ptx.texinfo
@@ -0,0 +1,554 @@
+\input texinfo @c -*-texinfo-*-
+@c %**start of header
+@setfilename ptx.info
+@settitle GNU @code{ptx} reference manual
+@finalout
+@c %**end of header
+
+@ifinfo
+This file documents the @code{ptx} command, which has the purpose of
+generated permuted indices for group of files.
+
+Copyright (C) 1990, 1991, 1993 by the Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+@ignore
+Permission is granted to process this file through TeX and print the
+results, provided the printed document carries copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+@end ignore
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the entire
+resulting derived work is distributed under the terms of a permission
+notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation approved
+by the Foundation.
+@end ifinfo
+
+@titlepage
+@title ptx
+@subtitle The GNU permuted indexer
+@subtitle Edition 0.3, for ptx version 0.3
+@subtitle November 1993
+@author by Francois Pinard
+
+@page
+@vskip 0pt plus 1filll
+Copyright @copyright{} 1990, 1991, 1993 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the entire
+resulting derived work is distributed under the terms of a permission
+notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation approved
+by the Foundation.
+@end titlepage
+
+@node Top, Invoking ptx, (dir), (dir)
+@chapter Introduction
+
+This is the 0.3 beta release of @code{ptx}, the GNU version of a
+permuted index generator. This software has the main goal of providing
+a replacement for the traditional @code{ptx} as found on System V
+machines, able to handle small files quickly, while providing a platform
+for more development.
+
+This version reimplements and extends traditional @code{ptx}. Among
+other things, it can produce a readable @dfn{KWIC} (keywords in their
+context) without the need of @code{nroff}, there is also an option to
+produce @TeX{} compatible output. This version does not handle huge
+input files, that is, those files which do not fit in memory all at
+once.
+
+@emph{Please note} that an overall renaming of all options is
+foreseeable. In fact, GNU ptx specifications are not frozen yet.
+
+@menu
+* Invoking ptx:: How to use this program
+* Compatibility:: The GNU extensions to @code{ptx}
+
+ --- The Detailed Node Listing ---
+
+How to use this program
+
+* General options:: Options which affect general program behaviour.
+* Charset selection:: Underlying character set considerations.
+* Input processing:: Input fields, contexts, and keyword selection.
+* Output formatting:: Types of output format, and sizing the fields.
+@end menu
+
+@node Invoking ptx, Compatibility, Top, Top
+@chapter How to use this program
+
+This tool reads a text file and essentially produces a permuted index, with
+each keyword in its context. The calling sketch is one of:
+
+@example
+ptx [@var{option} @dots{}] [@var{file} @dots{}]
+@end example
+
+or:
+
+@example
+ptx -G [@var{option} @dots{}] [@var{input} [@var{output}]]
+@end example
+
+The @samp{-G} (or its equivalent: @samp{--traditional}) option disables
+all GNU extensions and revert to traditional mode, thus introducing some
+limitations, and changes several of the program's default option values.
+When @samp{-G} is not specified, GNU extensions are always enabled. GNU
+extensions to @code{ptx} are documented wherever appropriate in this
+document. See @xref{Compatibility} for an explicit list of them.
+
+Individual options are explained later in this document.
+
+When GNU extensions are enabled, there may be zero, one or several
+@var{file} after the options. If there is no @var{file}, the program
+reads the standard input. If there is one or several @var{file}, they
+give the name of input files which are all read in turn, as if all the
+input files were concatenated. However, there is a full contextual
+break between each file and, when automatic referencing is requested,
+file names and line numbers refer to individual text input files. In
+all cases, the program produces the permuted index onto the standard
+output.
+
+When GNU extensions are @emph{not} enabled, that is, when the program
+operates in traditional mode, there may be zero, one or two parameters
+besides the options. If there is no parameters, the program reads the
+standard input and produces the permuted index onto the standard output.
+If there is only one parameter, it names the text @var{input} to be read
+instead of the standard input. If two parameters are given, they give
+respectively the name of the @var{input} file to read and the name of
+the @var{output} file to produce. @emph{Be very careful} to note that,
+in this case, the contents of file given by the second parameter is
+destroyed. This behaviour is dictated only by System V @code{ptx}
+compatibility, because GNU Standards discourage output parameters not
+introduced by an option.
+
+Note that for @emph{any} file named as the value of an option or as an
+input text file, a single dash @kbd{-} may be used, in which case
+standard input is assumed. However, it would not make sense to use this
+convention more than once per program invocation.
+
+@menu
+* General options:: Options which affect general program behaviour.
+* Charset selection:: Underlying character set considerations.
+* Input processing:: Input fields, contexts, and keyword selection.
+* Output formatting:: Types of output format, and sizing the fields.
+@end menu
+
+@node General options, Charset selection, Invoking ptx, Invoking ptx
+@section General options
+
+@table @code
+
+@item -C
+@itemx --copyright
+Prints a short note about the Copyright and copying conditions, then
+exit without further processing.
+
+@item -G
+@itemx --traditional
+As already explained, this option disables all GNU extensions to
+@code{ptx} and switch to traditional mode.
+
+@item --help
+Prints a short help on standard output, then exit without further
+processing.
+
+@item --version
+Prints the program verison on standard output, then exit without further
+processing.
+
+@end table
+
+@node Charset selection, Input processing, General options, Invoking ptx
+@section Charset selection
+
+As it is setup now, the program assumes that the input file is coded
+using 8-bit ISO 8859-1 code, also known as Latin-1 character set,
+@emph{unless} if it is compiled for MS-DOS, in which case it uses the
+character set of the IBM-PC. (GNU @code{ptx} is not known to work on
+smaller MS-DOS machines anymore.) Compared to 7-bit ASCII, the set of
+characters which are letters is then different, this fact alters the
+behaviour of regular expression matching. Thus, the default regular
+expression for a keyword allows foreign or diacriticized letters.
+Keyword sorting, however, is still crude; it obeys the underlying
+character set ordering quite blindly.
+
+@table @code
+
+@item -f
+@itemx --ignore-case
+Fold lower case letters to upper case for sorting.
+
+@end table
+
+@node Input processing, Output formatting, Charset selection, Invoking ptx
+@section Word selection
+
+@table @code
+
+@item -b @var{file}
+@item --break-file=@var{file}
+
+This option is an alternative way to option @code{-W} for describing
+which characters make up words. This option introduces the name of a
+file which contains a list of characters which can@emph{not} be part of
+one word, this file is called the @dfn{Break file}. Any character which
+is not part of the Break file is a word constituent. If both options
+@code{-b} and @code{-W} are specified, then @code{-W} has precedence and
+@code{-b} is ignored.
+
+When GNU extensions are enabled, the only way to avoid newline as a
+break character is to write all the break characters in the file with no
+newline at all, not even at the end of the file. When GNU extensions
+are disabled, spaces, tabs and newlines are always considered as break
+characters even if not included in the Break file.
+
+@item -i @var{file}
+@itemx --ignore-file=@var{file}
+
+The file associated with this option contains a list of words which will
+never be taken as keywords in concordance output. It is called the
+@dfn{Ignore file}. The file contains exactly one word in each line; the
+end of line separation of words is not subject to the value of the
+@code{-S} option.
+
+There is a default Ignore file used by @code{ptx} when this option is
+not specified, usually found in @file{/usr/local/lib/eign} if this has
+not been changed at installation time. If you want to deactivate the
+default Ignore file, specify @code{/dev/null} instead.
+
+@item -o @var{file}
+@itemx --only-file=@var{file}
+
+The file associated with this option contains a list of words which will
+be retained in concordance output, any word not mentioned in this file
+is ignored. The file is called the @dfn{Only file}. The file contains
+exactly one word in each line; the end of line separation of words is
+not subject to the value of the @code{-S} option.
+
+There is no default for the Only file. In the case there are both an
+Only file and an Ignore file, a word will be subject to be a keyword
+only if it is given in the Only file and not given in the Ignore file.
+
+@item -r
+@itemx --references
+
+On each input line, the leading sequence of non white characters will be
+taken to be a reference that has the purpose of identifying this input
+line on the produced permuted index. See @xref{Output formatting} for
+more information about reference production. Using this option change
+the default value for option @code{-S}.
+
+Using this option, the program does not try very hard to remove
+references from contexts in output, but it succeeds in doing so
+@emph{when} the context ends exactly at the newline. If option
+@code{-r} is used with @code{-S} default value, or when GNU extensions
+are disabled, this condition is always met and references are completely
+excluded from the output contexts.
+
+@item -S @var{regexp}
+@itemx --sentence-regexp=@var{regexp}
+
+This option selects which regular expression will describe the end of a
+line or the end of a sentence. In fact, there is other distinction
+between end of lines or end of sentences than the effect of this regular
+expression, and input line boundaries have no special significance
+outside this option. By default, when GNU extensions are enabled and if
+@code{-r} option is not used, end of sentences are used. In this
+case, the precise @var{regex} is imported from GNU emacs:
+
+@example
+[.?!][]\"')@}]*\\($\\|\t\\| \\)[ \t\n]*
+@end example
+
+Whenever GNU extensions are disabled or if @code{-r} option is used, end
+of lines are used; in this case, the default @var{regexp} is just:
+
+@example
+\n
+@end example
+
+Using an empty REGEXP is equivalent to completely disabling end of line or end
+of sentence recognition. In this case, the whole file is considered to
+be a single big line or sentence. The user might want to disallow all
+truncation flag generation as well, through option @code{-F ""}.
+@xref{Regexps, , Syntax of Regular Expressions, emacs, The GNU Emacs
+Manual}.
+
+When the keywords happen to be near the beginning of the input line or
+sentence, this often creates an unused area at the beginning of the
+output context line; when the keywords happen to be near the end of the
+input line or sentence, this often creates an unused area at the end of
+the output context line. The program tries to fill those unused areas
+by wrapping around context in them; the tail of the input line or
+sentence is used to fill the unused area on the left of the output line;
+the head of the input line or sentence is used to fill the unused area
+on the right of the output line.
+
+As a matter of convenience to the user, many usual backslashed escape
+sequences, as found in the C language, are recognized and converted to
+the corresponding characters by @code{ptx} itself.
+
+@item -W @var{regexp}
+@itemx --word-regexp=@var{regexp}
+
+This option selects which regular expression will describe each keyword.
+By default, if GNU extensions are enabled, a word is a sequence of
+letters; the @var{regexp} used is @code{\w+}. When GNU extensions are
+disabled, a word is by default anything which ends with a space, a tab
+or a newline; the @var{regexp} used is @code{[^ \t\n]+}.
+
+An empty REGEXP is equivalent to not using this option, letting the
+default dive in. @xref{Regexps, , Syntax of Regular Expressions, emacs,
+The GNU Emacs Manual}.
+
+As a matter of convenience to the user, many usual backslashed escape
+sequences, as found in the C language, are recognized and converted to
+the corresponding characters by @code{ptx} itself.
+
+@end table
+
+@node Output formatting, , Input processing, Invoking ptx
+@section Output formatting
+
+Output format is mainly controlled by @code{-O} and @code{-T} options,
+described in the table below. When neither @code{-O} nor @code{-T} is
+selected, and if GNU extensions are enabled, the program choose an
+output format suited for a dumb terminal. Each keyword occurrence is
+output to the center of one line, surrounded by its left and right
+contexts. Each field is properly justified, so the concordance output
+could readily be observed. As a special feature, if automatic
+references are selected by option @code{-A} and are output before the
+left context, that is, if option @code{-R} is @emph{not} selected, then
+a colon is added after the reference; this nicely interfaces with GNU
+Emacs @code{next-error} processing. In this default output format, each
+white space character, like newline and tab, is merely changed to
+exactly one space, with no special attempt to compress consecutive
+spaces. This might change in the future. Except for those white space
+characters, every other character of the underlying set of 256
+characters is transmitted verbatim.
+
+Output format is further controlled by the following options.
+
+@table @code
+
+@item -g @var{number}
+@itemx --gap-size=@var{number}
+
+Select the size of the minimum white gap between the fields on the output
+line.
+
+@item -w @var{number}
+@itemx --width=@var{number}
+
+Select the output maximum width of each final line. If references are
+used, they are included or excluded from the output maximum width
+depending on the value of option @code{-R}. If this option is not
+selected, that is, when references are output before the left context,
+the output maximum width takes into account the maximum length of all
+references. If this options is selected, that is, when references are
+output after the right context, the output maximum width does not take
+into account the space taken by references, nor the gap that precedes
+them.
+
+@item -A
+@itemx --auto-reference
+
+Select automatic references. Each input line will have an automatic
+reference made up of the file name and the line ordinal, with a single
+colon between them. However, the file name will be empty when standard
+input is being read. If both @code{-A} and @code{-r} are selected, then
+the input reference is still read and skipped, but the automatic
+reference is used at output time, overriding the input reference.
+
+@item -R
+@itemx --right-side-refs
+
+In default output format, when option @code{-R} is not used, any
+reference produced by the effect of options @code{-r} or @code{-A} are
+given to the far right of output lines, after the right context. In
+default output format, when option @code{-R} is specified, references
+are rather given to the beginning of each output line, before the left
+context. For any other output format, option @code{-R} is almost
+ignored, except for the fact that the width of references is @emph{not}
+taken into account in total output width given by @code{-w} whenever
+@code{-R} is selected.
+
+This option is automatically selected whenever GNU extensions are
+disabled.
+
+@item -F @var{string}
+@itemx --flac-truncation=@var{string}
+
+This option will request that any truncation in the output be reported
+using the string @var{string}. Most output fields theoretically extend
+towards the beginning or the end of the current line, or current
+sentence, as selected with option @code{-S}. But there is a maximum
+allowed output line width, changeable through option @code{-w}, which is
+further divided into space for various output fields. When a field has
+to be truncated because cannot extend until the beginning or the end of
+the current line to fit in the, then a truncation occurs. By default,
+the string used is a single slash, as in @code{-F /}.
+
+@var{string} may have more than one character, as in @code{-F ...}.
+Also, in the particular case @var{string} is empty (@code{-F ""}),
+truncation flagging is disabled, and no truncation marks are appended in
+this case.
+
+As a matter of convenience to the user, many usual backslashed escape
+sequences, as found in the C language, are recognized and converted to
+the corresponding characters by @code{ptx} itself.
+
+@item -M @var{string}
+@itemx --macro-name=@var{string}
+
+Select another @var{string} to be used instead of @samp{xx}, while
+generating output suitable for @code{nroff}, @code{troff} or @TeX{}.
+
+@item -O
+@itemx --format=roff
+
+Choose an output format suitable for @code{nroff} or @code{troff}
+processing. Each output line will look like:
+
+@example
+.xx "@var{tail}" "@var{before}" "@var{keyword_and_after}" "@var{head}" "@var{ref}"
+@end example
+
+so it will be possible to write an @samp{.xx} roff macro to take care of
+the output typesetting. This is the default output format when GNU
+extensions are disabled. Option @samp{-M} might be used to change
+@samp{xx} to another macro name.
+
+In this output format, each non-graphical character, like newline and
+tab, is merely changed to exactly one space, with no special attempt to
+compress consecutive spaces. Each quote character: @kbd{"} is doubled
+so it will be correctly processed by @code{nroff} or @code{troff}.
+
+@item -T
+@itemx --format=tex
+
+Choose an output format suitable for @TeX{} processing. Each output
+line will look like:
+
+@example
+\xx @{@var{tail}@}@{@var{before}@}@{@var{keyword}@}@{@var{after}@}@{@var{head}@}@{@var{ref}@}
+@end example
+
+@noindent
+so it will be possible to write write a @code{\xx} definition to take
+care of the output typesetting. Note that when references are not being
+produced, that is, neither option @code{-A} nor option @code{-r} is
+selected, the last parameter of each @code{\xx} call is inhibited.
+Option @samp{-M} might be used to change @samp{xx} to another macro
+name.
+
+In this output format, some special characters, like @kbd{$}, @kbd{%},
+@kbd{&}, @kbd{#} and @kbd{_} are automatically protected with a
+backslash. Curly brackets @kbd{@{}, @kbd{@}} are also protected with a
+backslash, but also enclosed in a pair of dollar signs to force
+mathematical mode. The backslash itself produces the sequence
+@code{\backslash@{@}}. Circumflex and tilde diacritics produce the
+sequence @code{^\@{ @}} and @code{~\@{ @}} respectively. Other
+diacriticized characters of the underlying character set produce an
+appropriate @TeX{} sequence as far as possible. The other non-graphical
+characters, like newline and tab, and all others characters which are
+not part of ASCII, are merely changed to exactly one space, with no
+special attempt to compress consecutive spaces. Let me know how to
+improve this special character processing for @TeX{}.
+
+@end table
+
+@node Compatibility, , Invoking ptx, Top
+@chapter The GNU extensions to @code{ptx}
+
+This version of @code{ptx} contains a few features which do not exist in
+System V @code{ptx}. These extra features are suppressed by using the
+@samp{-G} command line option, unless overridden by other command line
+options. Some GNU extensions cannot be recovered by overriding, so the
+simple rule is to avoid @samp{-G} if you care about GNU extensions.
+Here are the differences between this program and System V @code{ptx}.
+
+@itemize @bullet
+
+@item
+This program can read many input files at once, it always writes the
+resulting concordance on standard output. On the other end, System V
+@code{ptx} reads only one file and produce the result on standard output
+or, if a second @var{file} parameter is given on the command, to that
+@var{file}.
+
+Having output parameters not introduced by options is a quite dangerous
+practice which GNU avoids as far as possible. So, for using @code{ptx}
+portably between GNU and System V, you should pay attention to always
+use it with a single input file, and always expect the result on
+standard output. You might also want to automatically configure in a
+@samp{-G} option to @code{ptx} calls in products using @code{ptx}, if
+the configurator finds that the installed @code{ptx} accepts @samp{-G}.
+
+@item
+The only options available in System V @code{ptx} are options @samp{-b},
+@samp{-f}, @samp{-g}, @samp{-i}, @samp{-o}, @samp{-r}, @samp{-t} and
+@samp{-w}. All other options are GNU extensions and are not repeated in
+this enumeration. Moreover, some options have a slightly different
+meaning when GNU extensions are enabled, as explained below.
+
+@item
+By default, concordance output is not formatted for @code{troff} or
+@code{nroff}. It is rather formatted for a dumb terminal. @code{troff}
+or @code{nroff} output may still be selected through option @code{-O}.
+
+@item
+Unless @code{-R} option is used, the maximum reference width is
+subtracted from the total output line width. With GNU extensions
+disabled, width of references is not taken into account in the output
+line width computations.
+
+@item
+All 256 characters, even @kbd{NUL}s, are always read and processed from
+input file with no adverse effect, even if GNU extensions are disabled.
+However, System V @code{ptx} does not accept 8-bit characters, a few
+control characters are rejected, and the tilda @kbd{~} is condemned.
+
+@item
+Input line length is only limited by available memory, even if GNU
+extensions are disabled. However, System V @code{ptx} processes only
+the first 200 characters in each line.
+
+@item
+The break (non-word) characters default to be every character except all
+letters of the underlying character set, diacriticized or not. When GNU
+extensions are disabled, the break characters default to space, tab and
+newline only.
+
+@item
+The program makes better use of output line width. If GNU extensions
+are disabled, the program rather tries to imitate System V @code{ptx},
+but still, there are some slight disposition glitches this program does
+not completely reproduce.
+
+@item
+The user can specify both an Ignore file and an Only file. This is not
+allowed with System V @code{ptx}.
+
+@end itemize
+
+@bye
diff --git a/gnu/usr.bin/ptx/regex.h b/gnu/usr.bin/ptx/regex.h
new file mode 100644
index 0000000..a495005
--- /dev/null
+++ b/gnu/usr.bin/ptx/regex.h
@@ -0,0 +1,490 @@
+/* Definitions for data structures and routines for the regular
+ expression library, version 0.12.
+
+ Copyright (C) 1985, 89, 90, 91, 92, 1993 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __REGEXP_LIBRARY_H__
+#define __REGEXP_LIBRARY_H__
+
+/* POSIX says that <sys/types.h> must be included (by the caller) before
+ <regex.h>. */
+
+#ifdef VMS
+/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it
+ should be there. */
+#include <stddef.h>
+#endif
+
+
+/* The following bits are used to determine the regexp syntax we
+ recognize. The set/not-set meanings are chosen so that Emacs syntax
+ remains the value 0. The bits are given in alphabetical order, and
+ the definitions shifted by one from the previous bit; thus, when we
+ add or remove a bit, only one other definition need change. */
+typedef unsigned reg_syntax_t;
+
+/* If this bit is not set, then \ inside a bracket expression is literal.
+ If set, then such a \ quotes the following character. */
+#define RE_BACKSLASH_ESCAPE_IN_LISTS (1)
+
+/* If this bit is not set, then + and ? are operators, and \+ and \? are
+ literals.
+ If set, then \+ and \? are operators and + and ? are literals. */
+#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
+
+/* If this bit is set, then character classes are supported. They are:
+ [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
+ [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+ If not set, then character classes are not supported. */
+#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
+
+/* If this bit is set, then ^ and $ are always anchors (outside bracket
+ expressions, of course).
+ If this bit is not set, then it depends:
+ ^ is an anchor if it is at the beginning of a regular
+ expression or after an open-group or an alternation operator;
+ $ is an anchor if it is at the end of a regular expression, or
+ before a close-group or an alternation operator.
+
+ This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
+ POSIX draft 11.2 says that * etc. in leading positions is undefined.
+ We already implemented a previous draft which made those constructs
+ invalid, though, so we haven't changed the code back. */
+#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
+
+/* If this bit is set, then special characters are always special
+ regardless of where they are in the pattern.
+ If this bit is not set, then special characters are special only in
+ some contexts; otherwise they are ordinary. Specifically,
+ * + ? and intervals are only special when not after the beginning,
+ open-group, or alternation operator. */
+#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
+
+/* If this bit is set, then *, +, ?, and { cannot be first in an re or
+ immediately after an alternation or begin-group operator. */
+#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
+
+/* If this bit is set, then . matches newline.
+ If not set, then it doesn't. */
+#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
+
+/* If this bit is set, then . doesn't match NUL.
+ If not set, then it does. */
+#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
+
+/* If this bit is set, nonmatching lists [^...] do not match newline.
+ If not set, they do. */
+#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
+
+/* If this bit is set, either \{...\} or {...} defines an
+ interval, depending on RE_NO_BK_BRACES.
+ If not set, \{, \}, {, and } are literals. */
+#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
+
+/* If this bit is set, +, ? and | aren't recognized as operators.
+ If not set, they are. */
+#define RE_LIMITED_OPS (RE_INTERVALS << 1)
+
+/* If this bit is set, newline is an alternation operator.
+ If not set, newline is literal. */
+#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
+
+/* If this bit is set, then `{...}' defines an interval, and \{ and \}
+ are literals.
+ If not set, then `\{...\}' defines an interval. */
+#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
+
+/* If this bit is set, (...) defines a group, and \( and \) are literals.
+ If not set, \(...\) defines a group, and ( and ) are literals. */
+#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
+
+/* If this bit is set, then \<digit> matches <digit>.
+ If not set, then \<digit> is a back-reference. */
+#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
+
+/* If this bit is set, then | is an alternation operator, and \| is literal.
+ If not set, then \| is an alternation operator, and | is literal. */
+#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
+
+/* If this bit is set, then an ending range point collating higher
+ than the starting range point, as in [z-a], is invalid.
+ If not set, then when ending range point collates higher than the
+ starting range point, the range is ignored. */
+#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
+
+/* If this bit is set, then an unmatched ) is ordinary.
+ If not set, then an unmatched ) is invalid. */
+#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+
+/* This global variable defines the particular regexp syntax to use (for
+ some interfaces). When a regexp is compiled, the syntax used is
+ stored in the pattern buffer, so changing this does not affect
+ already-compiled regexps. */
+extern reg_syntax_t re_syntax_options;
+
+/* Define combinations of the above bits for the standard possibilities.
+ (The [[[ comments delimit what gets put into the Texinfo file, so
+ don't delete them!) */
+/* [[[begin syntaxes]]] */
+#define RE_SYNTAX_EMACS 0
+
+#define RE_SYNTAX_AWK \
+ (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS \
+ | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \
+ | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+#define RE_SYNTAX_POSIX_AWK \
+ (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS)
+
+#define RE_SYNTAX_GREP \
+ (RE_BK_PLUS_QM | RE_CHAR_CLASSES \
+ | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \
+ | RE_NEWLINE_ALT)
+
+#define RE_SYNTAX_EGREP \
+ (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \
+ | RE_NEWLINE_ALT | RE_NO_BK_PARENS \
+ | RE_NO_BK_VBAR)
+
+#define RE_SYNTAX_POSIX_EGREP \
+ (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
+
+/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */
+#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
+
+#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
+
+/* Syntax bits common to both basic and extended POSIX regex syntax. */
+#define _RE_SYNTAX_POSIX_COMMON \
+ (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \
+ | RE_INTERVALS | RE_NO_EMPTY_RANGES)
+
+#define RE_SYNTAX_POSIX_BASIC \
+ (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM)
+
+/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
+ RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this
+ isn't minimal, since other operators, such as \`, aren't disabled. */
+#define RE_SYNTAX_POSIX_MINIMAL_BASIC \
+ (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
+
+#define RE_SYNTAX_POSIX_EXTENDED \
+ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \
+ | RE_NO_BK_PARENS | RE_NO_BK_VBAR \
+ | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS
+ replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added. */
+#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \
+ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS \
+ | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD)
+/* [[[end syntaxes]]] */
+
+/* Maximum number of duplicates an interval can allow. Some systems
+ (erroneously) define this in other header files, but we want our
+ value, so remove any previous define. */
+#ifdef RE_DUP_MAX
+#undef RE_DUP_MAX
+#endif
+#define RE_DUP_MAX ((1 << 15) - 1)
+
+
+/* POSIX `cflags' bits (i.e., information for `regcomp'). */
+
+/* If this bit is set, then use extended regular expression syntax.
+ If not set, then use basic regular expression syntax. */
+#define REG_EXTENDED 1
+
+/* If this bit is set, then ignore case when matching.
+ If not set, then case is significant. */
+#define REG_ICASE (REG_EXTENDED << 1)
+
+/* If this bit is set, then anchors do not match at newline
+ characters in the string.
+ If not set, then anchors do match at newlines. */
+#define REG_NEWLINE (REG_ICASE << 1)
+
+/* If this bit is set, then report only success or fail in regexec.
+ If not set, then returns differ between not matching and errors. */
+#define REG_NOSUB (REG_NEWLINE << 1)
+
+
+/* POSIX `eflags' bits (i.e., information for regexec). */
+
+/* If this bit is set, then the beginning-of-line operator doesn't match
+ the beginning of the string (presumably because it's not the
+ beginning of a line).
+ If not set, then the beginning-of-line operator does match the
+ beginning of the string. */
+#define REG_NOTBOL 1
+
+/* Like REG_NOTBOL, except for the end-of-line. */
+#define REG_NOTEOL (1 << 1)
+
+
+/* If any error codes are removed, changed, or added, update the
+ `re_error_msg' table in regex.c. */
+typedef enum
+{
+ REG_NOERROR = 0, /* Success. */
+ REG_NOMATCH, /* Didn't find a match (for regexec). */
+
+ /* POSIX regcomp return error codes. (In the order listed in the
+ standard.) */
+ REG_BADPAT, /* Invalid pattern. */
+ REG_ECOLLATE, /* Not implemented. */
+ REG_ECTYPE, /* Invalid character class name. */
+ REG_EESCAPE, /* Trailing backslash. */
+ REG_ESUBREG, /* Invalid back reference. */
+ REG_EBRACK, /* Unmatched left bracket. */
+ REG_EPAREN, /* Parenthesis imbalance. */
+ REG_EBRACE, /* Unmatched \{. */
+ REG_BADBR, /* Invalid contents of \{\}. */
+ REG_ERANGE, /* Invalid range end. */
+ REG_ESPACE, /* Ran out of memory. */
+ REG_BADRPT, /* No preceding re for repetition op. */
+
+ /* Error codes we've added. */
+ REG_EEND, /* Premature end. */
+ REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */
+ REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */
+} reg_errcode_t;
+
+/* This data structure represents a compiled pattern. Before calling
+ the pattern compiler, the fields `buffer', `allocated', `fastmap',
+ `translate', and `no_sub' can be set. After the pattern has been
+ compiled, the `re_nsub' field is available. All other fields are
+ private to the regex routines. */
+
+struct re_pattern_buffer
+{
+/* [[[begin pattern_buffer]]] */
+ /* Space that holds the compiled pattern. It is declared as
+ `unsigned char *' because its elements are
+ sometimes used as array indexes. */
+ unsigned char *buffer;
+
+ /* Number of bytes to which `buffer' points. */
+ unsigned long allocated;
+
+ /* Number of bytes actually used in `buffer'. */
+ unsigned long used;
+
+ /* Syntax setting with which the pattern was compiled. */
+ reg_syntax_t syntax;
+
+ /* Pointer to a fastmap, if any, otherwise zero. re_search uses
+ the fastmap, if there is one, to skip over impossible
+ starting points for matches. */
+ char *fastmap;
+
+ /* Either a translate table to apply to all characters before
+ comparing them, or zero for no translation. The translation
+ is applied to a pattern when it is compiled and to a string
+ when it is matched. */
+ char *translate;
+
+ /* Number of subexpressions found by the compiler. */
+ size_t re_nsub;
+
+ /* Zero if this pattern cannot match the empty string, one else.
+ Well, in truth it's used only in `re_search_2', to see
+ whether or not we should use the fastmap, so we don't set
+ this absolutely perfectly; see `re_compile_fastmap' (the
+ `duplicate' case). */
+ unsigned can_be_null : 1;
+
+ /* If REGS_UNALLOCATED, allocate space in the `regs' structure
+ for `max (RE_NREGS, re_nsub + 1)' groups.
+ If REGS_REALLOCATE, reallocate space if necessary.
+ If REGS_FIXED, use what's there. */
+#define REGS_UNALLOCATED 0
+#define REGS_REALLOCATE 1
+#define REGS_FIXED 2
+ unsigned regs_allocated : 2;
+
+ /* Set to zero when `regex_compile' compiles a pattern; set to one
+ by `re_compile_fastmap' if it updates the fastmap. */
+ unsigned fastmap_accurate : 1;
+
+ /* If set, `re_match_2' does not return information about
+ subexpressions. */
+ unsigned no_sub : 1;
+
+ /* If set, a beginning-of-line anchor doesn't match at the
+ beginning of the string. */
+ unsigned not_bol : 1;
+
+ /* Similarly for an end-of-line anchor. */
+ unsigned not_eol : 1;
+
+ /* If true, an anchor at a newline matches. */
+ unsigned newline_anchor : 1;
+
+/* [[[end pattern_buffer]]] */
+};
+
+typedef struct re_pattern_buffer regex_t;
+
+
+/* search.c (search_buffer) in Emacs needs this one opcode value. It is
+ defined both in `regex.c' and here. */
+#define RE_EXACTN_VALUE 1
+
+/* Type for byte offsets within the string. POSIX mandates this. */
+typedef int regoff_t;
+
+
+/* This is the structure we store register match data in. See
+ regex.texinfo for a full description of what registers match. */
+struct re_registers
+{
+ unsigned num_regs;
+ regoff_t *start;
+ regoff_t *end;
+};
+
+
+/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
+ `re_match_2' returns information about at least this many registers
+ the first time a `regs' structure is passed. */
+#ifndef RE_NREGS
+#define RE_NREGS 30
+#endif
+
+
+/* POSIX specification for registers. Aside from the different names than
+ `re_registers', POSIX uses an array of structures, instead of a
+ structure of arrays. */
+typedef struct
+{
+ regoff_t rm_so; /* Byte offset from string's start to substring's start. */
+ regoff_t rm_eo; /* Byte offset from string's start to substring's end. */
+} regmatch_t;
+
+/* Declarations for routines. */
+
+/* To avoid duplicating every routine declaration -- once with a
+ prototype (if we are ANSI), and once without (if we aren't) -- we
+ use the following macro to declare argument types. This
+ unfortunately clutters up the declarations a bit, but I think it's
+ worth it. */
+
+#if __STDC__
+
+#define _RE_ARGS(args) args
+
+#else /* not __STDC__ */
+
+#define _RE_ARGS(args) ()
+
+#endif /* not __STDC__ */
+
+/* Sets the current default syntax to SYNTAX, and return the old syntax.
+ You can also simply assign to the `re_syntax_options' variable. */
+extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));
+
+/* Compile the regular expression PATTERN, with length LENGTH
+ and syntax given by the global `re_syntax_options', into the buffer
+ BUFFER. Return NULL if successful, and an error string if not. */
+extern const char *re_compile_pattern
+ _RE_ARGS ((const char *pattern, int length,
+ struct re_pattern_buffer *buffer));
+
+
+/* Compile a fastmap for the compiled pattern in BUFFER; used to
+ accelerate searches. Return 0 if successful and -2 if was an
+ internal error. */
+extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
+
+
+/* Search in the string STRING (with length LENGTH) for the pattern
+ compiled into BUFFER. Start searching at position START, for RANGE
+ characters. Return the starting position of the match, -1 for no
+ match, or -2 for an internal error. Also return register
+ information in REGS (if REGS and BUFFER->no_sub are nonzero). */
+extern int re_search
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+ int length, int start, int range, struct re_registers *regs));
+
+
+/* Like `re_search', but search in the concatenation of STRING1 and
+ STRING2. Also, stop searching at index START + STOP. */
+extern int re_search_2
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+ int length1, const char *string2, int length2,
+ int start, int range, struct re_registers *regs, int stop));
+
+
+/* Like `re_search', but return how many characters in STRING the regexp
+ in BUFFER matched, starting at position START. */
+extern int re_match
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+ int length, int start, struct re_registers *regs));
+
+
+/* Relates to `re_match' as `re_search_2' relates to `re_search'. */
+extern int re_match_2
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+ int length1, const char *string2, int length2,
+ int start, struct re_registers *regs, int stop));
+
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+ ENDS. Subsequent matches using BUFFER and REGS will use this memory
+ for recording register information. STARTS and ENDS must be
+ allocated with malloc, and must each be at least `NUM_REGS * sizeof
+ (regoff_t)' bytes long.
+
+ If NUM_REGS == 0, then subsequent matches should allocate their own
+ register data.
+
+ Unless this function is called, the first search or match using
+ PATTERN_BUFFER will allocate its own register data, without
+ freeing the old data. */
+extern void re_set_registers
+ _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
+ unsigned num_regs, regoff_t *starts, regoff_t *ends));
+
+/* 4.2 bsd compatibility. */
+extern char *re_comp _RE_ARGS ((const char *));
+extern int re_exec _RE_ARGS ((const char *));
+
+/* POSIX compatibility. */
+extern int regcomp _RE_ARGS ((regex_t *preg, const char *pattern, int cflags));
+extern int regexec
+ _RE_ARGS ((const regex_t *preg, const char *string, size_t nmatch,
+ regmatch_t pmatch[], int eflags));
+extern size_t regerror
+ _RE_ARGS ((int errcode, const regex_t *preg, char *errbuf,
+ size_t errbuf_size));
+extern void regfree _RE_ARGS ((regex_t *preg));
+
+#endif /* not __REGEXP_LIBRARY_H__ */
+
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
diff --git a/gnu/usr.bin/ptx/xmalloc.c b/gnu/usr.bin/ptx/xmalloc.c
new file mode 100644
index 0000000..58a81b5
--- /dev/null
+++ b/gnu/usr.bin/ptx/xmalloc.c
@@ -0,0 +1,88 @@
+/* xmalloc.c -- malloc with out of memory checking
+ Copyright (C) 1990, 1991, 1993 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef HAVE_CONFIG_H
+#if defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
+#if __STDC__
+#define VOID void
+#else
+#define VOID char
+#endif
+
+#include <sys/types.h>
+
+#if STDC_HEADERS
+#include <stdlib.h>
+#else
+VOID *malloc ();
+VOID *realloc ();
+void free ();
+#endif
+
+#if __STDC__ && defined (HAVE_VPRINTF)
+void error (int, int, char const *, ...);
+#else
+void error ();
+#endif
+
+/* Allocate N bytes of memory dynamically, with error checking. */
+
+VOID *
+xmalloc (n)
+ size_t n;
+{
+ VOID *p;
+
+ p = malloc (n);
+ if (p == 0)
+ /* Must exit with 2 for `cmp'. */
+ error (2, 0, "virtual memory exhausted");
+ return p;
+}
+
+/* Change the size of an allocated block of memory P to N bytes,
+ with error checking.
+ If P is NULL, run xmalloc.
+ If N is 0, run free and return NULL. */
+
+VOID *
+xrealloc (p, n)
+ VOID *p;
+ size_t n;
+{
+ if (p == 0)
+ return xmalloc (n);
+ if (n == 0)
+ {
+ free (p);
+ return 0;
+ }
+ p = realloc (p, n);
+ if (p == 0)
+ /* Must exit with 2 for `cmp'. */
+ error (2, 0, "virtual memory exhausted");
+ return p;
+}
diff --git a/gnu/usr.bin/rcs/Makefile b/gnu/usr.bin/rcs/Makefile
index 2181815..4a9fd08 100644
--- a/gnu/usr.bin/rcs/Makefile
+++ b/gnu/usr.bin/rcs/Makefile
@@ -1,3 +1,3 @@
-SUBDIR= lib ci co ident merge rcs rcsdiff rcsmerge rlog rcsfreeze
+SUBDIR= lib ci co ident merge rcs rcsclean rcsdiff rcsmerge rlog rcsfreeze
.include <bsd.subdir.mk>
diff --git a/gnu/usr.bin/rcs/Makefile.inc b/gnu/usr.bin/rcs/Makefile.inc
index b9eca7d..cd593ed 100644
--- a/gnu/usr.bin/rcs/Makefile.inc
+++ b/gnu/usr.bin/rcs/Makefile.inc
@@ -1,3 +1,7 @@
-# @(#)Makefile.inc 5.1 (Berkeley) 5/11/90
+# Location of librcs
-BINDIR?= /usr/bin
+.if exists(${.CURDIR}/../lib/obj)
+LIBRCS= ${.CURDIR}/../lib/obj/librcs.a
+.else
+LIBRCS= ${.CURDIR}/../lib/librcs.a
+.endif
diff --git a/gnu/usr.bin/rcs/ci/Makefile b/gnu/usr.bin/rcs/ci/Makefile
index 9b64e08..3ac3d29 100644
--- a/gnu/usr.bin/rcs/ci/Makefile
+++ b/gnu/usr.bin/rcs/ci/Makefile
@@ -1,7 +1,8 @@
-PROG= ci
-
+PROG= ci
SRCS= ci.c
-LDADD= -L${.CURDIR}/../lib/obj -lrcs
CFLAGS+= -I${.CURDIR}/../lib
+LDADD= ${LIBRCS}
+DPADD= ${LIBRCS}
+.include "../../Makefile.inc"
.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/rcs/co/Makefile b/gnu/usr.bin/rcs/co/Makefile
index e9de8da..a759d1f 100644
--- a/gnu/usr.bin/rcs/co/Makefile
+++ b/gnu/usr.bin/rcs/co/Makefile
@@ -1,7 +1,8 @@
-PROG= co
-
+PROG= co
SRCS= co.c
-LDADD= -L${.CURDIR}/../lib/obj -lrcs
CFLAGS+= -I${.CURDIR}/../lib
+LDADD= ${LIBRCS}
+DPADD= ${LIBRCS}
+.include "../../Makefile.inc"
.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/rcs/co/co.1 b/gnu/usr.bin/rcs/co/co.1
index d9ce65e..1fce152 100644
--- a/gnu/usr.bin/rcs/co/co.1
+++ b/gnu/usr.bin/rcs/co/co.1
@@ -2,7 +2,7 @@
.ds Rv \\$3
.ds Dt \\$4
..
-.Id $Id: co.1,v 5.7 1991/08/19 03:13:55 eggert Exp $
+.Id $Id: co.1,v 1.1.1.1 1993/06/18 04:22:11 jkh Exp $
.ds g \&\s-1UTC\s0
.ds r \&\s-1RCS\s0
.if n .ds - \%--
@@ -141,6 +141,14 @@ See also
.SM "FILE MODES"
below.
.TP
+.B \-K\f2keywordlist\fP
+Exclude or include keyword expansion when checking out a file.
+By default all keywords are expanded, you can turn individual
+keywords off with
+.BR \-K\f2eKeyword\fP
+or on with
+.BR \-K\f2iKeyword\fP .
+.TP
.B \-kkv
Generate keyword strings using the default form, e.g.\&
.B "$\&Revision: \*(Rv $"
diff --git a/gnu/usr.bin/rcs/co/co.c b/gnu/usr.bin/rcs/co/co.c
index 9435574..82f7cc8 100644
--- a/gnu/usr.bin/rcs/co/co.c
+++ b/gnu/usr.bin/rcs/co/co.c
@@ -34,6 +34,9 @@ Report problems and direct all questions to:
/* $Log: co.c,v $
+ * Revision 1.1.1.1 1993/06/18 04:22:11 jkh
+ * Updated GNU utilities
+ *
* Revision 5.9 1991/10/07 17:32:46 eggert
* ci -u src/RCS/co.c,v src/co.c <<\.
* -k affects just working file, not RCS file.
@@ -152,7 +155,7 @@ static void cleanup P((void));
static char const quietarg[] = "-q";
-static char const *expandarg, *join, *suffixarg, *versionarg;
+static char const *expandarg, *join, *suffixarg, *versionarg, *incexcarg;
static char const *joinlist[joinlength]; /* revisions to be joined */
static FILE *neworkptr;
static int exitstatus;
@@ -164,7 +167,7 @@ static struct hshentries *gendeltas; /* deltas to be generated */
static struct hshentry *targetdelta; /* final delta to be generated */
static struct stat workstat;
-mainProg(coId, "co", "$Id: co.c,v 5.9 1991/10/07 17:32:46 eggert Exp $")
+mainProg(coId, "co", "$Id: co.c,v 1.1.1.1 1993/06/18 04:22:11 jkh Exp $")
{
static char const cmdusage[] =
"\nco usage: co -{flpqru}[rev] -ddate -jjoinlist -sstate -w[login] -Vn file ...";
@@ -270,6 +273,10 @@ mainProg(coId, "co", "$Id: co.c,v 5.9 1991/10/07 17:32:46 eggert Exp $")
setRCSversion(versionarg);
break;
+ case 'K': /* set keyword inclusions/exclusions */
+ incexcarg = *argv;
+ setIncExc(incexcarg);
+ break;
case 'k': /* set keyword expand mode */
expandarg = *argv;
if (0 <= expmode) redefined('k');
diff --git a/gnu/usr.bin/rcs/ident/Makefile b/gnu/usr.bin/rcs/ident/Makefile
index 1a618e5..25e028b 100644
--- a/gnu/usr.bin/rcs/ident/Makefile
+++ b/gnu/usr.bin/rcs/ident/Makefile
@@ -1,7 +1,8 @@
-PROG= ident
-
+PROG= ident
SRCS= ident.c
-LDADD= -L${.CURDIR}/../lib/obj -lrcs
CFLAGS+= -I${.CURDIR}/../lib
+LDADD= ${LIBRCS}
+DPADD= ${LIBRCS}
+.include "../../Makefile.inc"
.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/rcs/lib/Makefile b/gnu/usr.bin/rcs/lib/Makefile
index b198e9e..a69000b 100644
--- a/gnu/usr.bin/rcs/lib/Makefile
+++ b/gnu/usr.bin/rcs/lib/Makefile
@@ -1,5 +1,14 @@
-LIB= rcs
-SRCS= maketime.c partime.c rcsedit.c rcsfcmp.c rcsfnms.c rcsgen.c rcskeep.c \
- rcskeys.c rcslex.c rcsmap.c rcsrev.c rcssyn.c rcsutil.c merger.c
+# Define FSYNC_ALL to get slower but safer writes in case of crashes in
+# the middle of CVS/RCS changes
+#CFLAGS += -DFSYNC_ALL
+
+LIB = rcs
+SRCS = maketime.c partime.c rcsedit.c rcsfcmp.c rcsfnms.c rcsgen.c \
+ rcskeep.c rcskeys.c rcslex.c rcsmap.c rcsrev.c rcssyn.c rcsutil.c \
+ merger.c
+
+NOPROFILE=noprofile
+
+install:
.include <bsd.lib.mk>
diff --git a/gnu/usr.bin/rcs/lib/conf.h b/gnu/usr.bin/rcs/lib/conf.h
index d29e511..b8f4abd 100644
--- a/gnu/usr.bin/rcs/lib/conf.h
+++ b/gnu/usr.bin/rcs/lib/conf.h
@@ -1,6 +1,6 @@
/* RCS compile-time configuration */
- /* $Id: conf.sh,v 5.14 1991/11/20 18:21:10 eggert Exp $ */
+ /* $Id: conf.h,v 1.2 1994/08/05 22:33:44 wollman Exp $ */
/*
* This file is generated automatically.
@@ -79,7 +79,7 @@
/* Fix any uncommented typedefs that are wrong. */
/* typedef int mode_t; */
/* typedef int pid_t; */
-typedef int sig_atomic_t;
+/* typedef int sig_atomic_t; */
/* typedef unsigned size_t; */
/* typedef int ssize_t; */
/* typedef long time_t; */
@@ -142,7 +142,7 @@ typedef int sig_atomic_t;
#define bad_fopen_wplus 0 /* Does fopen(f,FOPEN_WPLUS) fail to truncate f? */
#define getlogin_is_secure 0 /* Is getlogin() secure? Usually it's not. */
#define has_dirent 1 /* Do opendir(), readdir(), closedir() work? */
-#define has_fchmod 0 /* Does fchmod() work? */
+#define has_fchmod 1 /* Does fchmod() work? */
#define has_fputs 0 /* Does fputs() work? */
#define has_ftruncate 1 /* Does ftruncate() work? */
#define has_getuid 1 /* Does getuid() work? */
@@ -152,7 +152,7 @@ typedef int sig_atomic_t;
#define has_memcpy 1 /* Does memcpy() work? */
#define has_memmove 1 /* Does memmove() work? */
#define has_madvise 0 /* Does madvise() work? */
-#define has_mmap 0 /* Does mmap() work on regular files? */
+#define has_mmap 1 /* Does mmap() work on regular files? */
#define has_rename 1 /* Does rename() work? */
#define bad_a_rename 0 /* Does rename(A,B) fail if A is unwritable? */
#define bad_b_rename 0 /* Does rename(A,B) fail if B is unwritable? */
@@ -167,7 +167,7 @@ typedef int sig_atomic_t;
/* #define has_sigblock ? */ /* Does sigblock() work? */
/* #define sigmask(s) (1 << ((s)-1)) */ /* Yield mask for signal number. */
#define has_sys_siglist 0 /* Does sys_siglist[] work? */
-typedef ssize_t fread_type; /* type returned by fread() and fwrite() */
+typedef size_t fread_type; /* type returned by fread() and fwrite() */
typedef size_t freadarg_type; /* type of their size arguments */
typedef void *malloc_type; /* type returned by malloc() */
#define has_getcwd 1 /* Does getcwd() work? */
@@ -177,17 +177,17 @@ typedef void *malloc_type; /* type returned by malloc() */
/* #define strchr index */ /* Use old-fashioned name for strchr()? */
/* #define strrchr rindex */ /* Use old-fashioned name for strrchr()? */
#define bad_unlink 0 /* Does unlink() fail on unwritable files? */
-#define has_vfork 0 /* Does vfork() work? */
+#define has_vfork 1 /* Does vfork() work? */
#define has_fork 1 /* Does fork() work? */
#define has_spawn 0 /* Does spawn*() work? */
#define has_wait 1 /* Does wait() work? */
-#define has_waitpid 0 /* Does waitpid() work? */
+#define has_waitpid 1 /* Does waitpid() work? */
#define RCS_SHELL "/bin/sh" /* shell to run RCS subprograms */
#define has_vfprintf 1 /* Does vfprintf() work? */
/* #define has__doprintf ? */ /* Does _doprintf() work? */
/* #define has__doprnt ? */ /* Does _doprnt() work? */
/* #undef EXIT_FAILURE */ /* Uncomment this if EXIT_FAILURE is broken. */
-#define large_memory 0 /* Can main memory hold entire RCS files? */
+#define large_memory 1 /* Can main memory hold entire RCS files? */
/* #undef ULONG_MAX */ /* Uncomment this if ULONG_MAX is broken (e.g. < 0). */
/* struct utimbuf { time_t actime, modtime; }; */ /* Uncomment this if needed. */
#define CO "/usr/bin/co" /* name of 'co' program */
@@ -195,6 +195,7 @@ typedef void *malloc_type; /* type returned by malloc() */
#define DATEFORM "%.2d.%.2d.%.2d.%.2d.%.2d.%.2d" /* e.g. 01.01.01.01.01.01 */
#define DIFF "/usr/bin/diff" /* name of 'diff' program */
#define DIFF3 "/usr/bin/diff3" /* name of 'diff3' program */
+#define DIFF3_A 1 /* Does diff3 have an -A option? */
#define DIFF3_BIN 1 /* Is diff3 user-visible (not the /usr/lib auxiliary)? */
#define DIFF_FLAGS , "-an" /* Make diff output suitable for RCS. */
#define DIFF_L 1 /* Does diff -L work? */
@@ -464,8 +465,6 @@ void perror P((char const*));
/* <stdlib.h> */
char *getenv P((char const*));
-exiting void _exit P((int));
-exiting void exit P((int));
malloc_type malloc P((size_t));
malloc_type realloc P((malloc_type,size_t));
void free P((malloc_type));
diff --git a/gnu/usr.bin/rcs/lib/rcsbase.h b/gnu/usr.bin/rcs/lib/rcsbase.h
index c0904bb..17a123d 100644
--- a/gnu/usr.bin/rcs/lib/rcsbase.h
+++ b/gnu/usr.bin/rcs/lib/rcsbase.h
@@ -2,7 +2,7 @@
/*
* RCS common definitions and data structures
*/
-#define RCSBASE "$Id: rcsbase.h,v 5.11 1991/10/07 17:32:46 eggert Exp $"
+#define RCSBASE "$Id: rcsbase.h,v 1.1.1.1 1993/06/18 04:22:13 jkh Exp $"
/* Copyright (C) 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991 by Paul Eggert
@@ -43,6 +43,9 @@ Report problems and direct all questions to:
/* $Log: rcsbase.h,v $
+ * Revision 1.1.1.1 1993/06/18 04:22:13 jkh
+ * Updated GNU utilities
+ *
* Revision 5.11 1991/10/07 17:32:46 eggert
* Support piece tables even if !has_mmap.
*
@@ -380,10 +383,11 @@ struct assoc {
#define REVISION "Revision"
#define SOURCE "Source"
#define STATE "State"
+#define FREEBSD "FreeBSD"
#define keylength 8 /* max length of any of the above keywords */
enum markers { Nomatch, Author, Date, Header, Id,
- Locker, Log, RCSfile, Revision, Source, State };
+ Locker, Log, RCSfile, Revision, Source, State, FreeBSD };
/* This must be in the same order as rcskeys.c's Keyword[] array. */
#define DELNUMFORM "\n\n%s\n%s\n"
diff --git a/gnu/usr.bin/rcs/lib/rcsedit.c b/gnu/usr.bin/rcs/lib/rcsedit.c
index fab4f62..98e4c4b 100644
--- a/gnu/usr.bin/rcs/lib/rcsedit.c
+++ b/gnu/usr.bin/rcs/lib/rcsedit.c
@@ -36,6 +36,9 @@ Report problems and direct all questions to:
/* $Log: rcsedit.c,v $
+ * Revision 1.1.1.1 1993/06/18 04:22:12 jkh
+ * Updated GNU utilities
+ *
* Revision 5.11 1991/11/03 01:11:44 eggert
* Move the warning about link breaking to where they're actually being broken.
*
@@ -154,7 +157,7 @@ Report problems and direct all questions to:
#include "rcsbase.h"
-libId(editId, "$Id: rcsedit.c,v 5.11 1991/11/03 01:11:44 eggert Exp $")
+libId(editId, "$Id: rcsedit.c,v 1.1.1.1 1993/06/18 04:22:12 jkh Exp $")
static void keyreplace P((enum markers,struct hshentry const*,FILE*));
@@ -959,10 +962,14 @@ keyreplace(marker,delta,out)
case Date:
aputs(date2str(date,datebuf), out);
break;
+ /*
+ * The FreeBSD keyword is identical to Id.
+ */
+ case FreeBSD:
case Id:
case Header:
aprintf(out, "%s %s %s %s %s",
- marker==Id || RCSv<VERSION(4)
+ marker==Id || marker==FreeBSD || RCSv<VERSION(4)
? basename(RCSfilename)
: getfullRCSname(),
delta->num,
diff --git a/gnu/usr.bin/rcs/lib/rcskeys.c b/gnu/usr.bin/rcs/lib/rcskeys.c
index 82850a7..cb80314 100644
--- a/gnu/usr.bin/rcs/lib/rcskeys.c
+++ b/gnu/usr.bin/rcs/lib/rcskeys.c
@@ -31,6 +31,19 @@ Report problems and direct all questions to:
/* $Log: rcskeys.c,v $
+ * Revision 1.3 1994/05/15 22:15:14 rgrimes
+ * To truely have the OLD behavior of RCS by default make the expansion
+ * of $FreeBSD$ false by default. This should keep them out
+ * of the pre 2.x repository. (Or at least make them useless in it).
+ *
+ * Revision 1.2 1994/05/14 07:00:23 rgrimes
+ * Add new option -K from David Dawes that allows you to turn on and off
+ * specific keyword substitution during a rcs co command.
+ * Add the new keyword FreeBSD that is IDENTICAL in operation to $Id: rcskeys.c,v 1.3 1994/05/15 22:15:14 rgrimes Exp $.
+ *
+ * Revision 1.1.1.1 1993/06/18 04:22:12 jkh
+ * Updated GNU utilities
+ *
* Revision 5.2 1991/08/19 03:13:55 eggert
* Say `T const' instead of `const T'; it's less confusing for pointer types.
* (This change was made in other source files too.)
@@ -60,17 +73,26 @@ Report problems and direct all questions to:
#include "rcsbase.h"
-libId(keysId, "$Id: rcskeys.c,v 5.2 1991/08/19 03:13:55 eggert Exp $")
+libId(keysId, "$Id: rcskeys.c,v 1.3 1994/05/15 22:15:14 rgrimes Exp $")
char const *const Keyword[] = {
/* This must be in the same order as rcsbase.h's enum markers type. */
nil,
AUTHOR, DATE, HEADER, IDH,
- LOCKER, LOG, RCSFILE, REVISION, SOURCE, STATE
+ LOCKER, LOG, RCSFILE, REVISION, SOURCE, STATE,
+ FREEBSD
};
+/* Expand all keywords by default */
+
+static int ExpandKeyword[] = {
+ nil,
+ true, true, true, true,
+ true, true, true, true, true, true,
+ false
+};
enum markers
trymatch(string)
@@ -83,6 +105,8 @@ trymatch(string)
register int j;
register char const *p, *s;
for (j = sizeof(Keyword)/sizeof(*Keyword); (--j); ) {
+ if (!ExpandKeyword[j])
+ continue;
/* try next keyword */
p = Keyword[j];
s = string;
@@ -100,3 +124,35 @@ trymatch(string)
return(Nomatch);
}
+
+setIncExc(arg)
+ char *arg;
+/* Sets up the ExpandKeyword table according to command-line flags */
+{
+ char *key;
+ int include = 0, j;
+
+ arg += 2;
+ switch (*arg++) {
+ case 'e':
+ include = false;
+ break;
+ case 'i':
+ include = true;
+ break;
+ default:
+ return(false);
+ }
+ if (include)
+ for (j = sizeof(Keyword)/sizeof(*Keyword); (--j); )
+ ExpandKeyword[j] = false;
+ key = strtok(arg, ",");
+ while (key) {
+ for (j = sizeof(Keyword)/sizeof(*Keyword); (--j); )
+ if (!strcmp(key, Keyword[j]))
+ ExpandKeyword[j] = include;
+ key = strtok(NULL, ",");
+ }
+ return(true);
+}
+
diff --git a/gnu/usr.bin/rcs/lib/rcslex.c b/gnu/usr.bin/rcs/lib/rcslex.c
index 51e31f3..cedbc40 100644
--- a/gnu/usr.bin/rcs/lib/rcslex.c
+++ b/gnu/usr.bin/rcs/lib/rcslex.c
@@ -39,6 +39,9 @@ Report problems and direct all questions to:
/* $Log: rcslex.c,v $
+ * Revision 1.1.1.1 1993/06/18 04:22:12 jkh
+ * Updated GNU utilities
+ *
* Revision 5.11 1991/11/03 03:30:44 eggert
* Fix porting bug to ancient hosts lacking vfprintf.
*
@@ -132,7 +135,7 @@ Report problems and direct all questions to:
#include "rcsbase.h"
-libId(lexId, "$Id: rcslex.c,v 5.11 1991/11/03 03:30:44 eggert Exp $")
+libId(lexId, "$Id: rcslex.c,v 1.1.1.1 1993/06/18 04:22:12 jkh Exp $")
static struct hshentry *nexthsh; /*pointer to next hash entry, set by lookup*/
@@ -935,7 +938,13 @@ void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); }
void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); }
void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); }
+#ifndef FSYNC_ALL
void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); }
+#else
+void Ofclose(f) FILE *f; { if (f && (fflush(f)!=0 ||
+ fsync(fileno(f))!=0 ||
+ fclose(f)!=0)) Oerror(); }
+#endif
void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; }
void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; }
diff --git a/gnu/usr.bin/rcs/merge/Makefile b/gnu/usr.bin/rcs/merge/Makefile
index d14afb2..9022bc4 100644
--- a/gnu/usr.bin/rcs/merge/Makefile
+++ b/gnu/usr.bin/rcs/merge/Makefile
@@ -1,7 +1,8 @@
-PROG= merge
-
+PROG= merge
SRCS= merge.c
-LDADD= -L${.CURDIR}/../lib/obj -lrcs
CFLAGS+= -I${.CURDIR}/../lib
+LDADD= ${LIBRCS}
+DPADD= ${LIBRCS}
+.include "../../Makefile.inc"
.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/rcs/rcs/Makefile b/gnu/usr.bin/rcs/rcs/Makefile
index d62c8d1..e3cd12e 100644
--- a/gnu/usr.bin/rcs/rcs/Makefile
+++ b/gnu/usr.bin/rcs/rcs/Makefile
@@ -1,10 +1,11 @@
-PROG= rcs
-
+PROG= rcs
SRCS= rcs.c
-LDADD= -L${.CURDIR}/../lib/obj -lrcs
CFLAGS+= -I${.CURDIR}/../lib
+LDADD= ${LIBRCS}
+DPADD= ${LIBRCS}
-MAN1= rcs.0 rcsintro.0
-MAN5= rcsfile.0
+MAN1= rcs.1 rcsintro.1
+MAN5= rcsfile.5
+.include "../../Makefile.inc"
.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/rcs/rcsclean/Makefile b/gnu/usr.bin/rcs/rcsclean/Makefile
index fc0c626..2651a3f 100644
--- a/gnu/usr.bin/rcs/rcsclean/Makefile
+++ b/gnu/usr.bin/rcs/rcsclean/Makefile
@@ -1,7 +1,8 @@
-PROG= rcsclean
-
+PROG= rcsclean
SRCS= rcsclean.c
-LDADD= -L${.CURDIR}/../lib/obj -lrcs
CFLAGS+= -I${.CURDIR}/../lib
+LDADD= ${LIBRCS}
+DPADD= ${LIBRCS}
+.include "../../Makefile.inc"
.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/rcs/rcsdiff/Makefile b/gnu/usr.bin/rcs/rcsdiff/Makefile
index 837c241..070e231 100644
--- a/gnu/usr.bin/rcs/rcsdiff/Makefile
+++ b/gnu/usr.bin/rcs/rcsdiff/Makefile
@@ -1,7 +1,8 @@
-PROG= rcsdiff
-
+PROG= rcsdiff
SRCS= rcsdiff.c
-LDADD= -L${.CURDIR}/../lib/obj -lrcs
CFLAGS+= -I${.CURDIR}/../lib
+LDADD= ${LIBRCS}
+DPADD= ${LIBRCS}
+.include "../../Makefile.inc"
.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/rcs/rcsfreeze/Makefile b/gnu/usr.bin/rcs/rcsfreeze/Makefile
index 825d4bf..c1b4841 100644
--- a/gnu/usr.bin/rcs/rcsfreeze/Makefile
+++ b/gnu/usr.bin/rcs/rcsfreeze/Makefile
@@ -1,7 +1,8 @@
-# Do nothing for the following
-obj clean cleandir depend rcsfreeze all:
- @echo No need to make $@ for rcsfreeze\; ignored
+MAN1= rcsfreeze.1
-install:
- install -c -o bin -g bin -m 555 rcsfreeze.sh /usr/bin/rcsfreeze
- install -c -o bin -g bin -m 444 rcsfreeze.1 /usr/share/man/man1
+afterinstall:
+ install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/rcsfreeze.sh ${DESTDIR}${BINDIR}/rcsfreeze
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/rcs/rcsmerge/Makefile b/gnu/usr.bin/rcs/rcsmerge/Makefile
index 0c1f643..801b01e 100644
--- a/gnu/usr.bin/rcs/rcsmerge/Makefile
+++ b/gnu/usr.bin/rcs/rcsmerge/Makefile
@@ -1,7 +1,8 @@
-PROG= rcsmerge
-
+PROG= rcsmerge
SRCS= rcsmerge.c
-LDADD= -L${.CURDIR}/../lib/obj -lrcs
CFLAGS+= -I${.CURDIR}/../lib
+LDADD= ${LIBRCS}
+DPADD= ${LIBRCS}
+.include "../../Makefile.inc"
.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/rcs/rlog/Makefile b/gnu/usr.bin/rcs/rlog/Makefile
index b6a1268..a1d19aa 100644
--- a/gnu/usr.bin/rcs/rlog/Makefile
+++ b/gnu/usr.bin/rcs/rlog/Makefile
@@ -1,7 +1,8 @@
-PROG= rlog
-
+PROG= rlog
SRCS= rlog.c
-LDADD= -L${.CURDIR}/../lib/obj -lrcs
CFLAGS+= -I${.CURDIR}/../lib
+LDADD= ${LIBRCS}
+DPADD= ${LIBRCS}
+.include "../../Makefile.inc"
.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/rcs/rlog/rlog.1 b/gnu/usr.bin/rcs/rlog/rlog.1
index fa627ff..e3deb68 100644
--- a/gnu/usr.bin/rcs/rlog/rlog.1
+++ b/gnu/usr.bin/rcs/rlog/rlog.1
@@ -2,7 +2,7 @@
.ds Rv \\$3
.ds Dt \\$4
..
-.Id $Id: rlog.1,v 5.3 1991/08/22 06:50:48 eggert Exp $
+.Id $Id: rlog.1,v 1.2 1994/05/11 22:39:43 phk Exp $
.ds g \&\s-1UTC\s0
.ds r \&\s-1RCS\s0
.if n .ds - \%--
@@ -54,6 +54,10 @@ Print only the name of the \*r file.
This is convenient for translating a
working pathname into an \*r pathname.
.TP
+.BI \-v "[string]"
+Print only the working pathname and tip-revision.
+The optional string is prepended to the outputline.
+.TP
.B \-h
Print only the \*r pathname, working pathname, head,
default branch, access list, locks,
diff --git a/gnu/usr.bin/rcs/rlog/rlog.c b/gnu/usr.bin/rcs/rlog/rlog.c
index b18b0c9..bf59e66 100644
--- a/gnu/usr.bin/rcs/rlog/rlog.c
+++ b/gnu/usr.bin/rcs/rlog/rlog.c
@@ -36,6 +36,19 @@ Report problems and direct all questions to:
/* $Log: rlog.c,v $
+ * Revision 1.4 1994/05/12 00:37:59 phk
+ * made -v produce tip-revision, which was what I wanted in the first place...
+ *
+ * Revision 1.3 1994/05/11 22:39:44 phk
+ * Added -v option to rlog. This gives a quick way to get a list of versions.
+ *
+ * Revision 1.2 1993/08/06 16:47:16 nate
+ * Have rlog output be much easier to parse. (Added one line which is not
+ * used by any CVS/RCS commands)
+ *
+ * Revision 1.1.1.1 1993/06/18 04:22:17 jkh
+ * Updated GNU utilities
+ *
* Revision 5.9 1991/09/17 19:07:40 eggert
* Getscript() didn't uncache partial lines.
*
@@ -191,10 +204,10 @@ static struct lockers *lockerlist;
static struct stateattri *statelist;
-mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.9 1991/09/17 19:07:40 eggert Exp $")
+mainProg(rlogId, "rlog", "$Id: rlog.c,v 1.4 1994/05/12 00:37:59 phk Exp $")
{
static char const cmdusage[] =
- "\nrlog usage: rlog -{bhLRt} -ddates -l[lockers] -rrevs -sstates -w[logins] -Vn file ...";
+ "\nrlog usage: rlog -{bhLRt} [-v[string]] -ddates -l[lockers] -rrevs -sstates -w[logins] -Vn file ...";
register FILE *out;
char *a, **newargv;
@@ -207,11 +220,14 @@ mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.9 1991/09/17 19:07:40 eggert Exp $")
struct lock const *currlock;
int descflag, selectflag;
int onlylockflag; /* print only files with locks */
+ int versionlist;
+ char *vstring;
int onlyRCSflag; /* print only RCS file name */
unsigned revno;
descflag = selectflag = true;
- onlylockflag = onlyRCSflag = false;
+ versionlist = onlylockflag = onlyRCSflag = false;
+ vstring=0;
out = stdout;
suffixes = X_DEFAULT;
@@ -274,6 +290,11 @@ mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.9 1991/09/17 19:07:40 eggert Exp $")
setRCSversion(*argv);
break;
+ case 'v':
+ versionlist = true;
+ vstring = a;
+ break;
+
default:
faterror("unknown option: %s%s", *argv, cmdusage);
@@ -323,6 +344,12 @@ mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.9 1991/09/17 19:07:40 eggert Exp $")
if (onlylockflag && !Locks)
continue;
+ if ( versionlist ) {
+ gettree();
+ aprintf(out, "%s%s %s\n", vstring, workfilename, tiprev());
+ continue;
+ }
+
if ( onlyRCSflag ) {
aprintf(out, "%s\n", RCSfilename);
continue;
@@ -402,6 +429,7 @@ mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.9 1991/09/17 19:07:40 eggert Exp $")
putrunk();
putree(Head);
}
+ aputs("----------------------------\n", out);
aputs("=============================================================================\n",out);
} while (cleanup(),
++argv, --argc >= 1);
diff --git a/gnu/usr.bin/sdiff/Makefile b/gnu/usr.bin/sdiff/Makefile
new file mode 100644
index 0000000..449a933
--- /dev/null
+++ b/gnu/usr.bin/sdiff/Makefile
@@ -0,0 +1,9 @@
+PROG= sdiff
+SRCS= sdiff.c getopt.c getopt1.c version.c
+CFLAGS+= -I$(.CURDIR)/../diff -DHAVE_CONFIG_H \
+ -DDIFF_PROGRAM=\"/usr/bin/diff\"
+.PATH: $(.CURDIR)/../diff
+MAN= sdiff.1
+
+.include <bsd.prog.mk>
+.PATH: $(.CURDIR)/../diff
diff --git a/gnu/usr.bin/sdiff/sdiff.1 b/gnu/usr.bin/sdiff/sdiff.1
new file mode 100644
index 0000000..8b7e88c
--- /dev/null
+++ b/gnu/usr.bin/sdiff/sdiff.1
@@ -0,0 +1,198 @@
+.TH SDIFF 1 "22sep1993" "GNU Tools" "GNU Tools"
+.SH NAME
+sdiff \- find differences between two files and merge interactively
+.SH SYNOPSIS
+.B sdiff
+.B -o
+outfile [options] from-file to-file
+.SH DESCRIPTION
+The
+.I sdiff
+command merges two files and interactively outputs the
+results to
+.IR outfile .
+
+If
+.I from-file
+is a directory and
+.I to-file
+is not,
+.I sdiff
+compares the file in
+.I from-file
+whose file name is that of
+.IR to-file ,
+and vice versa.
+.I from-file
+and
+.I to-file
+may not both be
+directories.
+
+.I sdiff
+options begin with
+.BR \- ,
+so normally
+.I from-file
+and
+.I to-file
+may not begin with
+.BR \- .
+However,
+.B \-\-
+as an
+argument by itself treats the remaining arguments as file names even if
+they begin with
+.BR \- .
+You may not use
+.B \-
+as an input file.
+
+.I sdiff
+without
+.B \-o
+(or
+.BR \-\-output )
+produces a
+side-by-side difference. This usage is obsolete; use
+.B "diff \-\-side\-by\-side"
+instead.
+.SS Options
+Below is a summary of all of the options that GNU
+.I sdiff
+accepts.
+Each option has two equivalent names, one of which is a single
+letter preceded by
+.BR \- ,
+and the other of which is a long name
+preceded by
+.BR \-\- .
+Multiple single letter options (unless they take
+an argument) can be combined into a single command line argument. Long
+named options can be abbreviated to any unique prefix of their name.
+.TP
+.B \-a
+Treat all files as text and compare them line-by-line, even if they
+do not appear to be text.
+.TP
+.B \-b
+Ignore changes in amount of white space.
+.TP
+.B \-B
+Ignore changes that just insert or delete blank lines.
+.TP
+.B \-d
+Change the algorithm to perhaps find a smaller set of changes. This
+makes
+.I sdiff
+slower (sometimes much slower).
+.TP
+.B \-H
+Use heuristics to speed handling of large files that have numerous
+scattered small changes.
+.TP
+.B \-\-expand\-tabs
+Expand tabs to spaces in the output, to preserve the alignment of tabs
+in the input files.
+.TP
+.B \-i
+Ignore changes in case; consider upper- and lower-case to be the same.
+.TP
+.BI "\-I " regexp
+Ignore changes that just insert or delete lines that match
+.IR regexp .
+.TP
+.B \-\-ignore\-all\-space
+Ignore white space when comparing lines.
+.TP
+.B \-\-ignore\-blank\-lines
+Ignore changes that just insert or delete blank lines.
+.TP
+.B \-\-ignore\-case
+Ignore changes in case; consider upper- and lower-case to be the same.
+.TP
+.BI \-\-ignore\-matching\-lines= regexp
+Ignore changes that just insert or delete lines that match
+.IR regexp .
+.TP
+.B \-\-ignore\-space\-change
+Ignore changes in amount of white space.
+.TP
+.B \-l
+.br
+.ns
+.TP
+.B \-\-left\-column
+Print only the left column of two common lines.
+.TP
+.B \-\-minimal
+Change the algorithm to perhaps find a smaller set of changes. This
+makes
+.I sdiff
+slower (sometimes much slower).
+.TP
+.BI "\-o " file
+.br
+.ns
+.TP
+.BI \-\-output= file
+Put merged output into
+.IR file .
+This option is required for merging.
+.TP
+.B \-s
+.br
+.ns
+.TP
+.B \-\-suppress\-common\-lines
+Do not print common lines.
+.TP
+.B \-\-speed\-large\-files
+Use heuristics to speed handling of large files that have numerous
+scattered small changes.
+.TP
+.B \-t
+Expand tabs to spaces in the output, to preserve the alignment of tabs
+in the input files.
+.TP
+.B \-\-text
+Treat all files as text and compare them line-by-line, even if they
+do not appear to be text.
+.TP
+.B \-v
+.br
+.ns
+.TP
+.B \-\-version
+Output the version number of
+.IR sdiff .
+.TP
+.BI "\-w " columns
+.br
+.ns
+.TP
+.BI \-\-width= columns
+Use an output width of
+.IR columns .
+Note that for historical reasons, this option is
+.B \-W
+in
+.IR diff ,
+.B \-w
+in
+.IR sdiff .
+.TP
+.B \-W
+Ignore horizontal white space when comparing lines.
+Note that for historical reasons, this option is
+.B \-w
+in
+.IR diff ,
+.B \-W
+in
+.IR sdiff .
+.SH SEE ALSO
+cmp(1), comm(1), diff(1), diff3(1).
+.SH DIAGNOSTICS
+An exit status of 0 means no differences were found, 1 means some
+differences were found, and 2 means trouble.
diff --git a/gnu/usr.bin/send-pr/COPYING b/gnu/usr.bin/send-pr/COPYING
new file mode 100644
index 0000000..515b6d3
--- /dev/null
+++ b/gnu/usr.bin/send-pr/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+he GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/gnu/usr.bin/send-pr/Makefile b/gnu/usr.bin/send-pr/Makefile
new file mode 100644
index 0000000..7d12793
--- /dev/null
+++ b/gnu/usr.bin/send-pr/Makefile
@@ -0,0 +1,29 @@
+#
+# Makefile for building a standalone send-pr.
+#
+
+BINDIR= /usr/bin
+NOOBJ=noobj
+MAN1= send-pr.1
+DATADIR=/etc
+SUBMITTERS=current-users
+RELEASE=`uname -rsm`
+
+all: ${.CURDIR}/send-pr.sh ${.CURDIR}/send-pr-el.in ${.CURDIR}/install-sid.sh
+ sed -e 's,@DATADIR@,$(DATADIR),g' \
+ -e 's/@DEFAULT_RELEASE@/$(RELEASE)/g' ${.CURDIR}/send-pr.sh > send-pr
+ sed -e 's,@DATADIR@,$(DATADIR),g' \
+ -e 's/@DEFAULT_RELEASE@/$(RELEASE)/g' ${.CURDIR}/send-pr-el.in > send-pr.el
+ sed -e 's,@BINDIR@,$(BINDIR),g' ${.CURDIR}/install-sid.sh > install-sid
+
+beforeinstall:
+ install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ send-pr ${DESTDIR}/usr/bin/send-pr
+ install -c -o ${BINOWN} -g ${BINGRP} -m 0644 \
+ ${.CURDIR}/categories ${DESTDIR}/etc/gnats/freefall
+ sh install-sid ${SUBMITTERS}
+
+clean:
+ rm -f send-pr send-pr.el install-sid
+
+.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/send-pr/README b/gnu/usr.bin/send-pr/README
new file mode 100644
index 0000000..b479179
--- /dev/null
+++ b/gnu/usr.bin/send-pr/README
@@ -0,0 +1,43 @@
+ send-pr - sends bug reports to a central support site
+
+`send-pr' uses electronic mail to submit support questions and
+software bugs to a central site. No piece of software is perfect, and
+software organizations understand this `send-pr' is designed to allow
+users who have problems to submit reports of these problems to sites
+responsible for supporting the software in question, in a defined form
+which can be read by an electronically managed database.
+
+`send-pr' is part of a suite of programs known collectively as GNATS,
+an acronym for Problem Report Management System. GNATS consists of
+several programs which, used in concert, formulate and partially
+administer a database of Problem Reports, or PRs, at a central support
+site. A PR goes through several states in its lifetime; GNATS tracks
+the PR and all information associated with it through each state and
+finally acts as an archive for PRs which have been resolved.
+
+The same engine can be used to submit bugs to any number of support
+sites by setting up aliases for each of them; `send-pr' error-checks
+each PR as it is sent to be sure that the category specified matches a
+category supported by the site in question.
+
+`send-pr' invokes an editor on a problem report template (after trying
+to fill in some fields with reasonable default values). When you exit
+the editor, `send-pr' sends the completed form to the support site.
+At the support site, the PR is assigned a unique number and is stored
+in the GNATS database according to its category and customer-id. GNATS
+automatically replies with an acknowledgement, citing the category and
+the PR number.
+
+See the Texinfo file `send-pr.texi' or the Info file `send-pr.info'
+for detailed installation and usage information.
+
+See the file MANIFEST for a list of the files which should have
+accompanied this distribution.
+
+See `send-pr.texi', `send-pr.info', or the file INSTALL for the
+installation procedure for `send-pr'.
+
+
+Copyright (c) 1993, Free Software Foundation, Inc.
+See the file COPYING for copyright information concerning this
+distribution and all its components.
diff --git a/gnu/usr.bin/send-pr/categories b/gnu/usr.bin/send-pr/categories
new file mode 100644
index 0000000..2f60d41
--- /dev/null
+++ b/gnu/usr.bin/send-pr/categories
@@ -0,0 +1,7 @@
+bin
+conf
+docs
+gnu
+i386
+kern
+misc
diff --git a/gnu/usr.bin/send-pr/install-sid.sh b/gnu/usr.bin/send-pr/install-sid.sh
new file mode 100755
index 0000000..f7f24e5
--- /dev/null
+++ b/gnu/usr.bin/send-pr/install-sid.sh
@@ -0,0 +1,82 @@
+#!/bin/sh
+# Drop in the SUBMITTER id into a site's installed send-pr script.
+# Copyright (C) 1993 Free Software Foundation, Inc.
+# Contributed by Brendan Kehoe (brendan@cygnus.com), based on a
+# version written by Heinz G. Seidl (hgs@ide.com).
+#
+# This file is part of GNU GNATS.
+#
+# GNU GNATS is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU GNATS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU GNATS; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+COMMAND=`echo $0 | sed -e 's,.*/,,g'`
+USAGE="Usage: $COMMAND [--install-dir=prefix] [--help] [--version] submitter-id"
+
+VERSION=3.2
+
+BINDIR=@BINDIR@
+
+SUBMITTER=
+TEMP=/tmp/sp$$
+
+if [ $# -eq 0 ]; then
+ echo "$USAGE"
+ exit 1
+fi
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -install-dir=*|--install-dir=*|--install-di=*|--install-d=*|--install-=*|--install=*|--instal=*|--insta=*|--inst=*|--ins=*|--in=*|--i=*)
+ I=`echo "$1" | sed 's/-*i[a-z\-]*=//'`
+ BINDIR=$I/bin ;;
+ --version) echo $COMMAND version $VERSION ; exit 1 ;;
+ -*) echo "$USAGE" ; exit 1 ;;
+ *) SUBMITTER=$1 ;;
+ esac
+ shift
+done
+
+path=`echo $0 | sed -e "s;${COMMAND};;"`
+
+[ -z "$path" ] && path=.
+
+if [ -f $BINDIR/send-pr ]; then
+ SPPATH=$BINDIR/send-pr
+elif [ -f $path/send-pr ]; then
+ SPPATH=$path/send-pr
+else
+ echo "$COMMAND: cannot find \`$BINDIR/send-pr' or \`$path/send-pr'" >&2
+ exit 1
+fi
+
+trap 'rm -f $TEMP ; exit 0' 0
+trap 'echo "$COM: Aborting ..."; rm -f $TEMP ; exit 1' 1 2 3 13 15
+
+sed -e "s/^SUBMITTER=.*/SUBMITTER=${SUBMITTER}/" $SPPATH > $TEMP
+
+if grep $SUBMITTER $TEMP > /dev/null; then
+ cp $SPPATH $SPPATH.orig &&
+ rm -f $SPPATH &&
+ cp $TEMP $SPPATH &&
+ chmod a+rx $SPPATH &&
+ rm -f $TEMP $SPPATH.orig ||
+ { echo "$COMMAND: unable to replace send-pr" >&2 ; exit 1; }
+else
+ echo "$COMMAND: something went wrong when sed-ing the submitter into send-pr" >&2
+ exit 1
+fi
+
+echo "$COMMAND: \`$SUBMITTER' is now the default submitter ID for send-pr"
+
+exit 0
diff --git a/gnu/usr.bin/send-pr/send-pr-el.in b/gnu/usr.bin/send-pr/send-pr-el.in
new file mode 100644
index 0000000..036b4e7
--- /dev/null
+++ b/gnu/usr.bin/send-pr/send-pr-el.in
@@ -0,0 +1,744 @@
+;;;; -*-emacs-lisp-*-
+;;;;---------------------------------------------------------------------------
+;;;; EMACS interface for send-pr (by Heinz G. Seidl, hgs@cygnus.com)
+;;;; Slightly hacked by Brendan Kehoe (brendan@cygnus.com).
+;;;;
+;;;; This file is part of the Problem Report Management System (GNATS)
+;;;; Copyright 1992, 1993 Cygnus Support
+;;;;
+;;;; This program is free software; you can redistribute it and/or
+;;;; modify it under the terms of the GNU General Public
+;;;; License as published by the Free Software Foundation; either
+;;;; version 2 of the License, or (at your option) any later version.
+;;;;
+;;;; This program is distributed in the hope that it will be useful,
+;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;;;; General Public License for more details.
+;;;;
+;;;; You should have received a copy of the GNU Library General Public
+;;;; License along with this program; if not, write to the Free
+;;;; Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+;;;;
+;;;;---------------------------------------------------------------------------
+;;;;
+;;;; This file contains the EMACS interface to the Problem Report Management
+;;;; System (GNATS):
+;;;;
+;;;; - The `send-pr' command and the `send-pr-mode' for sending
+;;;; Problem Reports (PRs).
+;;;;
+;;;; For more information about how to send a PR see send-pr(1).
+;;;;
+;;;;---------------------------------------------------------------------------
+;;;;
+;;;; Configuration: the symbol `DEFAULT-RELEASE' can be replaced by
+;;;; site/release specific strings during the configuration/installation
+;;;; process.
+;;;;
+;;;; Install this file in your EMACS library directory.
+;;;;
+;;;;---------------------------------------------------------------------------
+
+(provide 'send-pr)
+
+;;;;---------------------------------------------------------------------------
+;;;; Customization: put the following forms into your default.el file
+;;;; (or into your .emacs)
+;;;;---------------------------------------------------------------------------
+
+;(autoload 'send-pr-mode "send-pr"
+; "Major mode for sending problem reports." t)
+
+;(autoload 'send-pr "send-pr"
+; "Command to create and send a problem report." t)
+
+;;;;---------------------------------------------------------------------------
+;;;; End of Customization Section
+;;;;---------------------------------------------------------------------------
+
+(autoload 'server-buffer-done "server")
+(defvar server-buffer-clients nil)
+(defvar mail-self-blind nil)
+(defvar mail-default-reply-to nil)
+
+(defconst send-pr::version "3.2")
+
+(defvar gnats:root "/home/gnats"
+ "*The top of the tree containing the GNATS database.")
+
+;;;;---------------------------------------------------------------------------
+;;;; hooks
+;;;;---------------------------------------------------------------------------
+
+(defvar text-mode-hook nil) ; we define it here in case it's not defined
+(defvar send-pr-mode-hook text-mode-hook "Called when send-pr is invoked.")
+
+;;;;---------------------------------------------------------------------------
+;;;; Domains and default values for (some of) the Problem Report fields;
+;;;; constants and definitions.
+;;;;---------------------------------------------------------------------------
+
+(defconst gnats::emacs-19p
+ (not (or (and (boundp 'epoch::version) epoch::version)
+ (string-lessp emacs-version "19")))
+ "Is this emacs v19?")
+
+;;; These may be changed during configuration/installation or by the individual
+;;; user in his/her .emacs file.
+;;;
+(defun gnats::get-config (var)
+ (let ((shell-file-name "/bin/sh")
+ (buf (generate-new-buffer " *GNATS config*"))
+ ret)
+ (save-excursion
+ (set-buffer buf)
+ (shell-command (concat ". " gnats:root "/gnats-adm/config; echo $" var )
+ t)
+ (if (looking-at "/bin/sh:\\|\n")
+ (setq ret nil)
+ (setq ret (buffer-substring (point-min) (- (point-max) 1)))))
+ (kill-buffer buf)
+ ret))
+
+;; const because it must match the script's value
+(defconst send-pr:datadir (or (gnats::get-config "DATADIR") "@DATADIR@")
+ "*Where the `gnats' subdirectory containing category lists lives.")
+
+(defvar send-pr::sites nil
+ "List of GNATS support sites; computed at runtime.")
+(defvar send-pr:default-site
+ (or (gnats::get-config "GNATS_SITE") "freefall")
+ "Default site to send bugs to.")
+(defvar send-pr:::site send-pr:default-site
+ "The site to which a problem report is currently being submitted, or NIL
+if using the default site (buffer local).")
+
+(defvar send-pr:::categories nil
+ "Buffer local list of available categories, derived at runtime from
+send-pr:::site and send-pr::category-alist.")
+(defvar send-pr::category-alist nil
+ "Alist of GNATS support sites and the categories supported at each; computed
+at runtime.")
+
+;;; Ideally we would get all the following values from a central database
+;;; during runtime instead of having them here in the code.
+;;;
+(defconst send-pr::fields
+ (` (("Category" send-pr::set-categories
+ (, (or (gnats::get-config "DEFAULT_CATEGORY") nil)) enum)
+ ("Class" (("sw-bug") ("doc-bug") ("change-request") ("support"))
+ (, (or (gnats::get-config "DEFAULT_CONFIDENTIAL") 0)) enum)
+ ("Confidential" (("yes") ("no"))
+ (, (or (gnats::get-config "DEFAULT_CONFIDENTIAL") 1)) enum)
+ ("Severity" (("non-critical") ("serious") ("critical"))
+ (, (or (gnats::get-config "DEFAULT_SEVERITY") 1)) enum)
+ ("Priority" (("low") ("medium") ("high"))
+ (, (or (gnats::get-config "DEFAULT_PRIORITY") 1)) enum)
+ ("Release" nil
+ (, (or (gnats::get-config "DEFAULT_RELEASE") "@DEFAULT_RELEASE@"))
+ text)
+ ("Submitter-Id" nil
+ (, (or (gnats::get-config "DEFAULT_SUBMITTER") "unknown"))
+ text)
+ ("Synopsis" nil nil text
+ (lambda (a b c) (gnats::set-mail-field "Subject" c)))))
+ "AList, keyed on the name of the field, of:
+1) The field name.
+2) The list of completions. This can be a list, a function to call, or nil.
+3) The default value.
+4) The type of the field.
+5) A sub-function to call when changed.")
+
+(defvar gnats::fields nil)
+
+(defmacro gnats::push (i l)
+ (` (setq (, l) (cons (,@ (list i l))))))
+
+(defun send-pr::set-categories (&optional arg)
+ "Get the list of categories for the current site out of
+send-pr::category-alist if there or from send-pr if not. With arg, force
+update."
+ ;;
+ (let ((entry (assoc send-pr:::site send-pr::category-alist)))
+ (or (and entry (null arg))
+ (let ((oldpr (getenv "GNATS_ROOT")) cats)
+ (send-pr::set-sites arg)
+ (setenv "GNATS_ROOT" gnats:root)
+ (setq cats (gnats::get-value-from-shell
+ "send-pr" "-CL" send-pr:::site))
+ (setenv "GNATS_ROOT" oldpr)
+ (if entry (setcdr entry cats)
+ (setq entry (cons send-pr:::site cats))
+ (gnats::push entry send-pr::category-alist))))
+ (setq send-pr:::categories (cdr entry))))
+
+(defun send-pr::set-sites (&optional arg)
+ "Get the list of sites (by listing the contents of DATADIR/gnats) and assign
+it to send-pr::sites. With arg, force update."
+ (or (and (null arg) send-pr::sites)
+ (progn
+ (setq send-pr::sites nil)
+ (mapcar
+ (function
+ (lambda (file)
+ (or (memq t (mapcar (function (lambda (x) (string= x file)))
+ '("." ".." "pr-edit" "pr-addr")))
+ (not (file-readable-p file))
+ (gnats::push (list (file-name-nondirectory file))
+ send-pr::sites))))
+ (directory-files (format "%s/gnats" send-pr:datadir) t))
+ (setq send-pr::sites (reverse send-pr::sites)))))
+
+(defconst send-pr::pr-buffer-name "*send-pr*"
+ "Name of the temporary buffer, where the problem report gets composed.")
+
+(defconst send-pr::err-buffer-name "*send-pr-error*"
+ "Name of the temporary buffer, where send-pr error messages appear.")
+
+(defvar send-pr:::err-buffer nil
+ "The error buffer used by the current PR buffer.")
+
+(defconst gnats::indent 17 "Indent for formatting the value.")
+
+;;;;---------------------------------------------------------------------------
+;;;; `send-pr' - command for creating and sending of problem reports
+;;;;---------------------------------------------------------------------------
+
+(fset 'send-pr 'send-pr:send-pr)
+(defun send-pr:send-pr (&optional site)
+ "Create a buffer and read in the result of `send-pr -P'.
+When finished with editing the problem report use \\[send-pr:submit-pr]
+to send the PR with `send-pr -b -f -'."
+ ;;
+ (interactive
+ (if current-prefix-arg
+ (list (completing-read "Site: " (send-pr::set-sites 'recheck) nil t
+ send-pr:default-site))))
+ (or site (setq site send-pr:default-site))
+ (let ((buf (get-buffer send-pr::pr-buffer-name)))
+ (if (or (not buf)
+ (progn (switch-to-buffer buf)
+ (cond ((or (not (buffer-modified-p buf))
+ (y-or-n-p "Erase previous problem report? "))
+ (erase-buffer) t)
+ (t nil))))
+ (send-pr::start-up site))))
+
+(defun send-pr::start-up (site)
+ (switch-to-buffer (get-buffer-create send-pr::pr-buffer-name))
+ (setq default-directory (expand-file-name "~/"))
+ (auto-save-mode auto-save-default)
+ (let ((oldpr (getenv "GNATS_ROOT"))
+ (case-fold-search nil))
+ (setenv "GNATS_ROOT" gnats:root)
+ (shell-command (concat "send-pr -P " site) t)
+ (setenv "GNATS_ROOT" oldpr)
+ (if (looking-at "send-pr:")
+ (cond ((looking-at "send-pr: .* does not have a categories list")
+ (setq send-pr::sites nil)
+ (error "send-pr: the GNATS site %s does not have a categories list" site))
+ (t (error (buffer-substring (point-min) (point-max)))))
+ (save-excursion
+ ;; Clear cruft inserted by bdamaged .cshrcs
+ (re-search-forward "^SEND-PR:")
+ (delete-region 1 (match-beginning 0)))))
+ (set-buffer-modified-p nil)
+ (send-pr:send-pr-mode)
+ (setq send-pr:::site site)
+ (send-pr::set-categories)
+ (if (null send-pr:::categories)
+ (progn
+ (and send-pr:::err-buffer (kill-buffer send-pr:::err-buffer))
+ (kill-buffer nil)
+ (message "send-pr: no categories found"))
+ (and mail-default-reply-to
+ (gnats::set-mail-field "Reply-To" mail-default-reply-to))
+ (and mail-self-blind
+ (gnats::set-mail-field "BCC" (user-login-name)))
+ (mapcar 'send-pr::maybe-change-field send-pr::fields)
+ (gnats::position-on-field "Description")
+ (message (substitute-command-keys
+ "To send the problem report use: \\[send-pr:submit-pr]"))))
+
+(fset 'do-send-pr 'send-pr:submit-pr) ;backward compat
+(defun send-pr:submit-pr ()
+ "Pipe the contents of the buffer *send-pr* to `send-pr -f -.' unless this
+buffer was loaded with emacsclient, in which case save the buffer and exit."
+ ;;
+ (interactive)
+ (cond
+ ((and (boundp 'server-buffer-clients)
+ server-buffer-clients)
+ (let ((buffer (current-buffer))
+ (version-control nil) (buffer-backed-up nil))
+ (save-buffer buffer)
+ (kill-buffer buffer)
+ (server-buffer-done buffer)))
+ (t
+ (or (and send-pr:::err-buffer
+ (buffer-name send-pr:::err-buffer))
+ (setq send-pr:::err-buffer
+ (get-buffer-create send-pr::err-buffer-name)))
+ (let ((err-buffer send-pr:::err-buffer) mesg ok)
+ (save-excursion (set-buffer err-buffer) (erase-buffer))
+ (message "running send-pr...")
+ (let ((oldpr (getenv "GNATS_ROOT")))
+ (setenv "GNATS_ROOT" gnats:root)
+ (call-process-region (point-min) (point-max) "send-pr"
+ nil err-buffer nil send-pr:::site
+ "-b" "-f" "-")
+ (setenv "GNATS_ROOT" oldpr))
+ (message "running send-pr...done")
+ ;; stupidly we cannot check the return value in EMACS 18.57, thus we need
+ ;; this kluge to find out whether send-pr succeeded.
+ (if (save-excursion
+ (set-buffer err-buffer)
+ (goto-char (point-min))
+ (setq mesg (buffer-substring (point-min) (- (point-max) 1)))
+ (search-forward "problem report sent" nil t))
+ (progn (message mesg)
+ (kill-buffer err-buffer)
+ (delete-auto-save-file-if-necessary)
+ (set-buffer-modified-p nil)
+ (bury-buffer))
+ (pop-to-buffer err-buffer))
+ ))))
+
+;;;;---------------------------------------------------------------------------
+;;;; send-pr:send-pr-mode mode
+;;;;---------------------------------------------------------------------------
+
+(defvar send-pr-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map "\C-c\C-c" 'send-pr:submit-pr)
+ (define-key map "\C-c\C-f" 'gnats:change-field)
+ (define-key map "\M-n" 'gnats:next-field)
+ (define-key map "\M-p" 'gnats:previous-field)
+ (define-key map "\C-\M-f" 'gnats:forward-field)
+ (define-key map "\C-\M-b" 'gnats:backward-field)
+ map)
+ "Keymap for send-pr mode.")
+
+(defconst gnats::keyword "^>\\([-a-zA-Z]+\\):")
+(defconst gnats::before-keyword "[ \t\n\f]*[\n\f]+>\\([-a-zA-Z]+\\):")
+(defconst gnats::after-keyword "^>\\([-a-zA-Z]+\\):[ \t\n\f]+")
+
+(fset 'send-pr-mode 'send-pr:send-pr-mode)
+(defun send-pr:send-pr-mode ()
+ "Major mode for submitting problem reports.
+For information about the form see gnats(1) and send-pr(1).
+Special commands: \\{send-pr-mode-map}
+Turning on send-pr-mode calls the value of the variable send-pr-mode-hook,
+if it is not nil."
+ (interactive)
+ (gnats::patch-exec-path)
+ (put 'send-pr:send-pr-mode 'mode-class 'special)
+ (kill-all-local-variables)
+ (setq major-mode 'send-pr:send-pr-mode)
+ (setq mode-name "send-pr")
+ (use-local-map send-pr-mode-map)
+ (set-syntax-table text-mode-syntax-table)
+ (setq local-abbrev-table text-mode-abbrev-table)
+ (setq buffer-offer-save t)
+ (make-local-variable 'send-pr:::site)
+ (make-local-variable 'send-pr:::categories)
+ (make-local-variable 'send-pr:::err-buffer)
+ (make-local-variable 'paragraph-separate)
+ (setq paragraph-separate (concat (default-value 'paragraph-separate)
+ "\\|" gnats::keyword "[ \t\n\f]*$"))
+ (make-local-variable 'paragraph-start)
+ (setq paragraph-start (concat (default-value 'paragraph-start)
+ "\\|" gnats::keyword))
+ (run-hooks 'send-pr-mode-hook)
+ t)
+
+;;;;---------------------------------------------------------------------------
+;;;; Functions to read and replace field values.
+;;;;---------------------------------------------------------------------------
+
+(defun gnats::position-on-field (field)
+ (goto-char (point-min))
+ (if (not (re-search-forward (concat "^>" field ":") nil t))
+ (error "Field `>%s:' not found." field)
+ (re-search-forward "[ \t\n\f]*")
+ (if (looking-at gnats::keyword)
+ (backward-char 1))
+ t))
+
+(defun gnats::mail-position-on-field (field)
+ (let (end
+ (case-fold-search t))
+ (goto-char (point-min))
+ (re-search-forward "^$")
+ (setq end (match-beginning 0))
+ (goto-char (point-min))
+ (if (not (re-search-forward (concat "^" field ":") end 'go-to-end))
+ (insert field ": \n")
+ (re-search-forward "[ \t\n\f]*"))
+ (skip-chars-backward "\n")
+ t))
+
+(defun gnats::field-contents (field &optional elem move)
+ (let (pos)
+ (unwind-protect
+ (save-excursion
+ (if (not (gnats::position-on-field field))
+ nil
+ (setq pos (point-marker))
+ (if (or (looking-at "<.*>$") (eolp))
+ t
+ (looking-at ".*$") ; to set match-{beginning,end}
+ (gnats::nth-word
+ (buffer-substring (match-beginning 0) (match-end 0))
+ elem))))
+ (and move pos (goto-char pos)))))
+
+(defun gnats::functionp (thing)
+ (or (and (symbolp thing) (fboundp thing))
+ (and (listp thing) (eq (car thing) 'lambda))))
+
+(defun gnats::field-values (field)
+ "Return the possible (known) values for field FIELD."
+ (let* ((fields (if (eq major-mode 'gnats:gnats-mode) gnats::fields
+ send-pr::fields))
+ (thing (elt (assoc field fields) 1)))
+ (cond ((gnats::functionp thing) (funcall thing))
+ ((listp thing) thing)
+ (t (error "ACK")))))
+
+(defun gnats::field-default (field)
+ "Return the default value for field FIELD."
+ (let* ((fields (if (eq major-mode 'gnats:gnats-mode) gnats::fields
+ send-pr::fields))
+ (thing (elt (assoc field fields) 2)))
+ (cond ((stringp thing) thing)
+ ((null thing) "")
+ ((numberp thing) (car (elt (gnats::field-values field) thing)))
+ ((gnats::functionp thing)
+ (funcall thing (gnats::field-contents field)))
+ ((eq thing t) (gnats::field-contents field))
+ (t (error "ACK")))))
+
+(defun gnats::field-type (field)
+ "Return the type of field FIELD."
+ (let* ((fields (if (eq major-mode 'gnats:gnats-mode) gnats::fields
+ send-pr::fields))
+ (thing (elt (assoc field fields) 3)))
+ thing))
+
+(defun gnats::field-action (field)
+ "Return the extra handling function for field FIELD."
+ (let* ((fields (if (eq major-mode 'gnats:gnats-mode) gnats::fields
+ send-pr::fields))
+ (thing (elt (assoc field fields) 4)))
+ (cond ((null thing) 'ignore)
+ ((gnats::functionp thing) thing)
+ (t (error "ACK")))))
+
+;;;;---------------------------------------------------------------------------
+;;;; Point movement functions
+;;;;---------------------------------------------------------------------------
+
+(or (fboundp 'defsubst) (fset 'defsubst 'defun))
+
+(defun send-pr::maybe-change-field (field)
+ (setq field (car field))
+ (let ((thing (gnats::field-contents field)))
+ (and thing (eq t thing)
+ (not (eq 'multi-text (gnats::field-type field)))
+ (gnats:change-field field))))
+
+(defun gnats:change-field (&optional field default)
+ "Change the value of the field containing the cursor. With arg, ask the
+user for the field to change. From a program, the function takes optional
+arguments of the field to change and the default value to use."
+ (interactive)
+ (or field current-prefix-arg (setq field (gnats::current-field)))
+ (or field
+ (setq field
+ (completing-read "Field: "
+ (if (eq major-mode 'gnats:gnats-mode)
+ gnats::fields
+ send-pr::fields)
+ nil t)))
+ (gnats::position-on-field field)
+ (sit-for 0)
+ (let* ((old (gnats::field-contents field))
+ new)
+ (if (null old)
+ (error "ACK")
+ (let ((prompt (concat ">" field ": "))
+ (domain (gnats::field-values field))
+ (type (gnats::field-type field))
+ (action (gnats::field-action field)))
+ (or default (setq default (gnats::field-default field)))
+ (setq new (if (eq type 'enum)
+ (completing-read prompt domain nil t
+ (if gnats::emacs-19p (cons default 0)
+ default))
+ (read-string prompt (if gnats::emacs-19p (cons default 1)
+ default))))
+ (gnats::set-field field new)
+ (funcall action field old new)
+ new))))
+
+(defun gnats::set-field (field value)
+ (save-excursion
+ (gnats::position-on-field field)
+ (delete-horizontal-space)
+ (looking-at ".*$")
+ (replace-match
+ (concat (make-string (- gnats::indent (length field) 2) ?\40 ) value) t)))
+
+(defun gnats::set-mail-field (field value)
+ (save-excursion
+ (gnats::mail-position-on-field field)
+ (delete-horizontal-space)
+ (looking-at ".*$")
+ (replace-match (concat " " value) t)))
+
+(defun gnats::before-keyword (&optional where)
+ "Returns t if point is in some white space before a keyword.
+If where is nil, then point is not changed; if where is t then point is moved
+to the beginning of the keyword, otherwise it is moved to the beginning
+of the white space it was in."
+ ;;
+ (if (looking-at gnats::before-keyword)
+ (prog1 t
+ (cond ((eq where t)
+ (re-search-forward "^>") (backward-char))
+ ((not (eq where nil))
+ (re-search-backward "[^ \t\n\f]") (forward-char))))
+ nil))
+
+(defun gnats::after-keyword (&optional where)
+ "Returns t if point is in some white space after a keyword.
+If where is nil, then point is not changed; if where is t then point is moved
+to the beginning of the keyword, otherwise it is moved to the end of the white
+space it was in."
+ ;;
+ (if (gnats::looking-after gnats::after-keyword)
+ (prog1 t
+ (cond ((eq where t)
+ (re-search-backward "^>"))
+ ((not (eq where nil))
+ (re-search-forward "[^ \t\n\f]") (backward-char))))
+ nil))
+
+(defun gnats::in-keyword (&optional where)
+ "Returns t if point is within a keyword.
+If where is nil, then point is not changed; if where is t then point is moved
+to the beginning of the keyword."
+ ;;
+ (let ((old-point (point-marker)))
+ (beginning-of-line)
+ (cond ((and (looking-at gnats::keyword)
+ (< old-point (match-end 0)))
+ (prog1 t
+ (if (eq where t)
+ t
+ (goto-char old-point))))
+ (t (goto-char old-point)
+ nil))))
+
+(defun gnats::forward-bofield ()
+ "Moves point to the beginning of a field. Assumes that point is in the
+keyword."
+ ;;
+ (if (re-search-forward "[ \t\n\f]+[^ \t\n\f]" (point-max) '-)
+ (backward-char)
+ t))
+
+(defun gnats::backward-eofield ()
+ "Moves point to the end of a field. Assumes point is in the keyword."
+ ;;
+ (if (re-search-backward "[^ \t\n\f][ \t\n\f]+" (point-min) '-)
+ (forward-char)
+ t))
+
+(defun gnats::forward-eofield ()
+ "Moves point to the end of a field. Assumes that point is in the field."
+ ;;
+ ;; look for the next field
+ (if (re-search-forward gnats::keyword (point-max) '-)
+ (progn (beginning-of-line) (gnats::backward-eofield))
+ (re-search-backward "[^ \t\n\f][ \t\n\f]*" (point-min) '-)
+ (forward-char)))
+
+(defun gnats::backward-bofield ()
+ "Moves point to the beginning of a field. Assumes that point is in the
+field."
+ ;;
+ ;;look for previous field
+ (if (re-search-backward gnats::keyword (point-min) '-)
+ (gnats::forward-bofield)
+ t))
+
+
+(defun gnats:forward-field ()
+ "Move point forward to the end of the field or to the beginning of the next
+field."
+ ;;
+ (interactive)
+ (if (or (gnats::before-keyword t) (gnats::in-keyword t)
+ (gnats::after-keyword t))
+ (gnats::forward-bofield)
+ (gnats::forward-eofield)))
+
+(defun gnats:backward-field ()
+ "Move point backward to the beginning/end of a field."
+ ;;
+ (interactive)
+ (backward-char)
+ (if (or (gnats::before-keyword t) (gnats::in-keyword t)
+ (gnats::after-keyword t))
+ (gnats::backward-eofield)
+ (gnats::backward-bofield)))
+
+(defun gnats:next-field ()
+ "Move point to the beginning of the next field."
+ ;;
+ (interactive)
+ (if (or (gnats::before-keyword t) (gnats::in-keyword t)
+ (gnats::after-keyword t))
+ (gnats::forward-bofield)
+ (if (re-search-forward gnats::keyword (point-max) '-)
+ (gnats::forward-bofield)
+ t)))
+
+(defun gnats:previous-field ()
+ "Move point to the beginning of the previous field."
+ ;;
+ (interactive)
+ (backward-char)
+ (if (or (gnats::after-keyword t) (gnats::in-keyword t)
+ (gnats::before-keyword t))
+ (progn (re-search-backward gnats::keyword (point-min) '-)
+ (gnats::forward-bofield))
+ (gnats::backward-bofield)))
+
+(defun gnats:beginning-of-field ()
+ "Move point to the beginning of the current field."
+ (interactive)
+ (cond ((gnats::in-keyword t)
+ (gnats::forward-bofield))
+ ((gnats::after-keyword 0))
+ (t
+ (gnats::backward-bofield))))
+
+(defun gnats::current-field ()
+ (save-excursion
+ (if (cond ((or (gnats::in-keyword t) (gnats::after-keyword t))
+ (looking-at gnats::keyword))
+ ((re-search-backward gnats::keyword nil t)))
+ (buffer-substring (match-beginning 1) (match-end 1))
+ nil)))
+
+;;;;---------------------------------------------------------------------------
+;;;; Support functions
+;;;;---------------------------------------------------------------------------
+
+(defun gnats::looking-after (regex)
+ "Returns t if point is after regex."
+ ;;
+ (let* ((old-point (point))
+ (start (if (eobp)
+ old-point
+ (forward-char) (point))))
+ (cond ((re-search-backward regex (point-min) t)
+ (goto-char old-point)
+ (cond ((eq (match-end 0) start)
+ t))))))
+
+(defun gnats::nth-word (string &optional elem)
+ "Returns the elem-th word of the string.
+If elem is nil, then the first wort is returned, if elem is 0 then
+the whole string is returned."
+ ;;
+ (if (integerp elem)
+ (cond ((eq elem 0) string)
+ ((eq elem 1) (gnats::first-word string))
+ ((equal string "") "")
+ ((>= elem 2)
+ (let ((i 0) (value ""))
+ (setq string ; strip leading blanks
+ (substring string (or (string-match "[^ \t]" string) 0)))
+ (while (< i elem)
+ (setq value
+ (substring string 0
+ (string-match "[ \t]*$\\|[ \t]+" string)))
+ (setq string
+ (substring string (match-end 0)))
+ (setq i (+ i 1)))
+ value)))
+ (gnats::first-word string)))
+
+(defun gnats::first-word (string)
+ (setq string
+ (substring string (or (string-match "[^ \t]" string) 0)))
+ (substring string 0 (string-match "[ \t]*$\\|[ \t]+" string)))
+
+;;;;---------------------------------------------------------------------------
+
+(defun gnats::patch-exec-path ()
+ ;;
+ "Replaces `//' by `/' in `exec-path'."
+ ;;
+ ;(make-local-variable 'exec-path)
+ (let ((err-buffer (get-buffer-create " *gnats::patch-exec-path*"))
+ (ret))
+ (setq exec-path (save-excursion (set-buffer err-buffer)
+ (prin1 exec-path err-buffer)
+ (goto-char (point-min))
+ (replace-string "//" "/")
+ (goto-char (point-min))
+ (setq ret (read err-buffer))
+ (kill-buffer err-buffer)
+ ret
+ ))))
+
+(defun gnats::get-value-from-shell (&rest command)
+ "Execute shell command to get a list of valid values for `variable'."
+ ;;
+ (let ((err-buffer (get-buffer-create " *gnats::get-value-from-shell*")))
+ (save-excursion
+ (set-buffer err-buffer)
+ (unwind-protect
+ (condition-case var
+ (progn
+ (apply 'call-process
+ (car command) nil err-buffer nil (cdr command))
+ (goto-char (point-min))
+ (if (looking-at "[-a-z]+: ")
+ (error (buffer-substring (point-min) (point-max))))
+ (read err-buffer))
+ (error nil))
+ (kill-buffer err-buffer)))))
+
+(or (fboundp 'setenv)
+ (defun setenv (variable &optional value)
+ "Set the value of the environment variable named VARIABLE to VALUE.
+VARIABLE should be a string. VALUE is optional; if not provided or is
+`nil', the environment variable VARIABLE will be removed.
+This function works by modifying `process-environment'."
+ (interactive "sSet environment variable: \nsSet %s to value: ")
+ (if (string-match "=" variable)
+ (error "Environment variable name `%s' contains `='" variable)
+ (let ((pattern (concat "\\`" (regexp-quote (concat variable "="))))
+ (case-fold-search nil)
+ (scan process-environment))
+ (while scan
+ (cond
+ ((string-match pattern (car scan))
+ (if (eq nil value)
+ (setq process-environment (delq (car scan)
+ process-environment))
+ (setcar scan (concat variable "=" value)))
+ (setq scan nil))
+ ((null (setq scan (cdr scan)))
+ (setq process-environment
+ (cons (concat variable "=" value)
+ process-environment)))))))))
+
+;;;; end of send-pr.el
diff --git a/gnu/usr.bin/send-pr/send-pr.1 b/gnu/usr.bin/send-pr/send-pr.1
new file mode 100644
index 0000000..3717ebd
--- /dev/null
+++ b/gnu/usr.bin/send-pr/send-pr.1
@@ -0,0 +1,269 @@
+.\" -*- nroff -*-
+.\" ---------------------------------------------------------------------------
+.\" man page for send-pr (by Heinz G. Seidl, hgs@cygnus.com)
+.\" updated Feb 1993 for GNATS 3.00 by Jeffrey Osier, jeffrey@cygnus.com
+.\"
+.\" This file is part of the Problem Report Management System (GNATS)
+.\" Copyright 1992 Cygnus Support
+.\"
+.\" This program is free software; you can redistribute it and/or
+.\" modify it under the terms of the GNU General Public
+.\" License as published by the Free Software Foundation; either
+.\" version 2 of the License, or (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU Library General Public
+.\" License along with this program; if not, write to the Free
+.\" Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
+.\"
+.\" ---------------------------------------------------------------------------
+.nh
+.TH SEND-PR 1 xVERSIONx "February 1993"
+.SH NAME
+send-pr \- send problem report (PR) to a central support site
+.SH SYNOPSIS
+.B send-pr
+[
+.I site
+]
+[
+.B \-f
+.I problem-report
+]
+[
+.B \-t
+.I mail-address
+]
+.br
+.in +0.8i
+[
+.B \-P
+]
+[
+.B \-L
+]
+[
+.B \-\-request-id
+]
+[
+.B \-v
+]
+.SH DESCRIPTION
+.B send-pr
+is a tool used to submit
+.I problem reports
+.\" SITE ADMINISTRATORS - change this if you use a local default
+(PRs) to a central support site. In most cases the correct
+.I site
+will be the default. This argument indicates the support site which
+is responsible for the category of problem involved. Some sites may
+use a local address as a default.
+.I site
+values are defined by using the
+.BR aliases (5).
+.LP
+.B send-pr
+invokes an editor on a problem report template (after trying to fill
+in some fields with reasonable default values). When you exit the
+editor,
+.B send-pr
+sends the completed form to the
+.I Problem Report Management System
+(\fBGNATS\fR) at a central support site. At the support site, the PR
+is assigned a unique number and is stored in the \fBGNATS\fR database
+according to its category and submitter-id. \fBGNATS\fR automatically
+replies with an acknowledgement, citing the category and the PR
+number.
+.LP
+To ensure that a PR is handled promptly, it should contain your (unique)
+\fIsubmitter-id\fR and one of the available \fIcategories\fR to identify the
+problem area. (Use
+.B `send-pr -L'
+to see a list of categories.)
+.LP
+The
+.B send-pr
+template at your site should already be customized with your
+submitter-id (running `\|\fBinstall-sid\fP \fIsubmitter-id\fP\|' to
+accomplish this is part of the installation procedures for
+.BR send-pr ).
+If this hasn't been done, see your system administrator for your
+submitter-id, or request one from your support site by invoking
+.B `send-pr \-\-request\-id'.
+If your site does not distinguish between different user sites, or if
+you are not affiliated with the support site, use
+.B `net'
+for this field.
+.LP
+The more precise your problem description and the more complete your
+information, the faster your support team can solve your problems.
+.SH OPTIONS
+.TP
+.BI \-f " problem-report"
+specify a file (\fIproblem-report\fR) which already contains a
+complete problem report.
+.B send-pr
+sends the contents of the file without invoking the editor. If
+the value for
+.I problem-report
+is
+.BR `\|\-\|' ,
+then
+.B send-pr
+reads from standard input.
+.TP
+.BI \-t " mail-address"
+Change mail address at the support site for problem reports. The
+default
+.I mail-address
+is the address used for the default
+.IR site .
+Use the
+.I site
+argument rather than this option in nearly all cases.
+.TP
+.B \-P
+print the form specified by the environment variable
+.B PR_FORM
+on standard output. If
+.B PR_FORM
+is not set, print the standard blank PR template. No mail is sent.
+.TP
+.B -L
+print the list of available categories. No mail is sent.
+.TP
+.B \-\-request\-id
+sends mail to the default support site, or
+.I site
+if specified, with a request for your
+.IR submitter-id .
+If you are
+not affiliated with
+.IR site ,
+use a
+.I submitter-id
+of
+.BR net \|'.
+.TP
+.B \-v
+Display the
+.B send-pr
+version number.
+.LP
+Note: use
+.B send-pr
+to submit problem reports rather than mailing them directly. Using
+both the template and
+.B send-pr
+itself will help ensure all necessary information will reach the
+support site.
+.SH ENVIRONMENT
+The environment variable
+.B EDITOR
+specifies the editor to invoke on the template.
+.br
+default:
+.B vi
+.sp
+If the environment variable
+.B PR_FORM
+is set, then its value is used as the file name of the template for
+your problem-report editing session. You can use this to start with a
+partially completed form (for example, a form with the identification
+fields already completed).
+.SH "HOW TO FILL OUT A PROBLEM REPORT"
+Problem reports have to be in a particular form so that a program can
+easily manage them. Please remember the following guidelines:
+.IP \(bu 3m
+describe only
+.B one problem
+with each problem report.
+.IP \(bu 3m
+For follow-up mail, use the same subject line as the one in the automatic
+acknowledgent. It consists of category, PR number and the original synopsis
+line. This allows the support site to relate several mail messages to a
+particular PR and to record them automatically.
+.IP \(bu 3m
+Please try to be as accurate as possible in the subject and/or synopsis line.
+.IP \(bu 3m
+The subject and the synopsis line are not confidential. This is
+because open-bugs lists are compiled from them. Avoid confidential
+information there.
+.LP
+See the GNU
+.B Info
+file
+.B send-pr.info
+or the document \fIReporting Problems With send-pr\fR\ for detailed
+information on reporting problems
+.SH "HOW TO SUBMIT TEST CASES, CODE, ETC."
+Submit small code samples with the PR. Contact the support site for
+instructions on submitting larger test cases and problematic source
+code.
+.SH FILES
+.ta \w'/tmp/pbad$$ 'u
+/tmp/p$$ copy of PR used in editing session
+.br
+/tmp/pf$$ copy of empty PR form, for testing purposes
+.br
+/tmp/pbad$$ file for rejected PRs
+.SH EMACS USER INTERFACE
+An Emacs user interface for
+.B send-pr
+with completion of field values is part of the
+.B send-pr
+distribution (invoked with
+.BR "M-x send-pr" ).
+See the file
+.B send-pr.info
+or the ASCII file
+.B INSTALL
+in the top level directory of the distribution for configuration and
+installation information. The Emacs LISP template file is
+.B send-pr-el.in
+and is installed as
+.BR send-pr.el .
+.SH INSTALLATION AND CONFIGURATION
+See
+.B send-pr.info
+or
+.B INSTALL
+for installation instructions.
+.SH SEE ALSO
+.I Reporting Problems Using send-pr
+(also installed as the GNU Info file
+.BR send-pr.info ).
+.LP
+.BR gnats (l),
+.BR query-pr (1),
+.BR edit-pr (1),
+.BR gnats (8),
+.BR queue-pr (8),
+.BR at-pr (8),
+.BR mkcat (8),
+.BR mkdist (8).
+.SH AUTHORS
+Jeffrey Osier, Brendan Kehoe, Jason Merrill, Heinz G. Seidl (Cygnus
+Support)
+.SH COPYING
+Copyright (c) 1992, 1993 Free Software Foundation, Inc.
+.PP
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+.PP
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+.PP
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+
diff --git a/gnu/usr.bin/send-pr/send-pr.info b/gnu/usr.bin/send-pr/send-pr.info
new file mode 100644
index 0000000..4e98715
--- /dev/null
+++ b/gnu/usr.bin/send-pr/send-pr.info
@@ -0,0 +1,1604 @@
+This is Info file send-pr.info, produced by Makeinfo-1.55 from the
+input file ./send-pr.texi.
+
+START-INFO-DIR-ENTRY
+* send-pr:: Reporting problems--using send-pr
+END-INFO-DIR-ENTRY
+
+ Copyright (C) 1993 Free Software Foundation, Inc.
+
+ Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of
+this manual under the conditions for verbatim copying, provided also
+that the entire resulting derived work is distributed under the terms
+of a permission notice identical to this one.
+
+ Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions.
+
+
+File: send-pr.info, Node: Top, Next: send-pr in detail, Prev: (DIR), Up: (DIR)
+
+Overview
+********
+
+ This manual documents `send-pr', version 3.2, which uses electronic
+mail to submit support questions and problem reports to a central
+Support Site. No body of work is perfect, and support organizations
+understand this; `send-pr' is designed to allow users who have problems
+to submit reports of these problems to sites responsible for supporting
+the products in question, in a defined form which can be read by an
+electronically managed database.
+
+ `send-pr' is part of a suite of programs known collectively as
+GNATS, the GNU Problem Report Management System. GNATS consists of
+several programs which, used in concert, formulate and partially
+administer a database of "Problem Reports", or "PRs", at a central
+Support Site. A PR goes through several states in its lifetime; GNATS
+tracks the PR and all information associated with it through each state
+and finally acts as an archive for PRs which have been "closed".
+
+ Because `send-pr' exists as a shell (`/bin/sh') script and as an
+Elisp file for use with GNU Emacs, it can be used from any machine on
+your network which can run a shell script and/or Emacs.
+
+ In general, you can use any editor and mailer to submit valid Problem
+Reports, as long as the format required by GNATS is preserved.
+`send-pr' automates the process, however, and ensures that certain
+fields necessary for automatic processing are present. `send-pr' is
+strongly recommended for all initial problem-oriented correspondence
+with your Support Site. The organization you submit Problem Reports to
+supplies an address to which further information can be sent; the person
+responsible for the category of the problem you report contacts you
+directly.
+
+* Menu:
+
+* send-pr in detail:: Details about send-pr and GNATS
+* Invoking send-pr:: Editing and sending PRs
+* An Example:: A working example
+* Installing send-pr:: Installing send-pr on your system
+* Index::
+
+
+File: send-pr.info, Node: send-pr in detail, Next: Invoking send-pr, Prev: Top, Up: Top
+
+Details about send-pr and GNATS
+*******************************
+
+ A "Problem Report" is a message that describes a problem you are
+having with a body of work. `send-pr' organizes this message into a
+form which can be understood and automatically processed by GNATS, the
+GNU Problem Report Management System. A Problem Report is organized
+into "fields" which contain data describing you, your organization, and
+the problem you are announcing (*note Problem Report format: Fields.).
+Problem Reports go through several defined states in their lifetimes,
+from "open" to "closed" (*note States of Problem Reports: States.).
+
+* Menu:
+
+* States:: States of Problem Reports
+* Fields:: Problem Report format
+
+
+File: send-pr.info, Node: States, Next: Fields, Up: send-pr in detail
+
+States of Problem Reports
+=========================
+
+ Each PR goes through a defined series of states between origination
+and closure. The originator of a PR receives notification
+automatically of any state changes.
+
+"open"
+ The initial state of a Problem Report. This means the PR has been
+ filed and the responsible person(s) notified.
+
+"analyzed"
+ The responsible person has analyzed the problem. The analysis
+ should contain a preliminary evaluation of the problem and an
+ estimate of the amount of time and resources necessary to solve
+ the problem. It should also suggest possible workarounds.
+
+"feedback"
+ The problem has been solved, and the originator has been given a
+ patch or other fix. The PR remains in this state until the
+ originator acknowledges that the solution works.
+
+"closed"
+ A Problem Report is closed ("the bug stops here") only when any
+ changes have been integrated, documented, and tested, and the
+ submitter has confirmed the solution.
+
+"suspended"
+ Work on the problem has been postponed. This happens if a timely
+ solution is not possible or is not cost-effective at the present
+ time. The PR continues to exist, though a solution is not being
+ actively sought. If the problem cannot be solved at all, it
+ should be closed rather than suspended.
+
+
+File: send-pr.info, Node: Fields, Prev: States, Up: send-pr in detail
+
+Problem Report format
+=====================
+
+ The format of a PR is designed to reflect the nature of GNATS as a
+database. Information is arranged into "fields", and kept in
+individual records (Problem Reports).
+
+ Problem Report fields are denoted by a keyword which begins with `>'
+and ends with `:', as in `>Confidential:'. Fields belong to one of
+three data types:
+
+ENUMERATED
+ One of a specific set of values, which vary according to the
+ field. The value for each keyword must be on the same line as the
+ keyword. These values are not configurable (yet).
+
+ For each ENUMERATED keyword, the possible choices are listed in the
+ `send-pr' template as a comment. The following fields are
+ ENUMERATED format; see the descriptions of fields below for
+ explanations of each field in detail:
+
+ >Confidential: >Severity: >Priority:
+ >Class: >State: >Number:
+
+TEXT
+ One single line of text which must begin and end on the same line
+ (i.e., before a newline) as the keyword. See the descriptions of
+ fields below for explanations of each field in detail. The
+ following fields are TEXT format:
+
+ >Submitter-Id: >Originator: >Synopsis:
+ >Category: >Release: >Responsible:
+ >Arrival-Date:
+
+MULTITEXT
+ Text of any length may occur in this field. MULTITEXT may span
+ multiple lines and may also include blank lines. A MULTITEXT field
+ ends only when another keyword appears. See the descriptions of
+ fields below for explanations of each field in detail.
+
+ The following fields are MULTITEXT format:
+
+ >Organization: >Environment: >Description:
+ >How-To-Repeat: >Fix: >Audit-Trail:
+ >Unformatted:
+
+ A Problem Report contains two different types of fields: "Mail
+Header" fields, which are used by the mail handler for delivery, and
+"Problem Report" fields, which contain information relevant to the
+Problem Report and its submitter. A Problem Report is essentially a
+specially formatted electronic mail message.
+
+ The following is an example Problem Report. Mail headers are at the
+top, followed by GNATS fields, which begin with `>' and end with `:'.
+The `Subject:' line in the mail header and the `>Synopsis:' field are
+usually duplicates of each other.
+
+ Message-Id: MESSAGE-ID
+ Date: DATE
+ From: ADDRESS
+ Reply-To: ADDRESS
+ To: BUG-ADDRESS
+ Subject: SUBJECT
+
+ >Number: GNATS-ID
+ >Category: CATEGORY
+ >Synopsis: SYNOPSIS
+ >Confidential: yes *or* no
+ >Severity: critical, serious, *or* non-critical
+ >Priority: high, medium *or* low
+ >Responsible: RESPONSIBLE
+ >State: open, analyzed, suspended, feedback, *or* closed
+ >Class: sw-bug, doc-bug, change-request, support,
+ *or* duplicate
+ >Submitter-Id: SUBMITTER-ID
+ >Arrival-Date: DATE
+ >Originator: NAME
+ >Organization: ORGANIZATION
+ >Release: RELEASE
+ >Environment:
+ ENVIRONMENT
+ >Description:
+ DESCRIPTION
+ >How-To-Repeat:
+ HOW-TO-REPEAT
+ >Fix:
+ FIX
+ >Audit-Trail:
+ APPENDED-MESSAGES...
+ State-Changed-From-To: FROM-TO
+ State-Changed-When: DATE
+ State-Changed-Why:
+ REASON
+ Responsible-Changed-From-To: FROM-TO
+ Responsible-Changed-When: DATE
+ Responsible-Changed-Why:
+ REASON
+ >Unformatted:
+ MISCELLANEOUS
+
+* Menu:
+
+* Mail header fields::
+* Problem Report fields::
+
+
+File: send-pr.info, Node: Mail header fields, Next: Problem Report fields, Up: Fields
+
+Mail header fields
+------------------
+
+ A Problem Report may contain any mail header field described in the
+Internet standard RFC-822. However, only the fields which identify the
+sender and the subject are required by `send-pr':
+
+`To:'
+ The preconfigured mail address for the Support Site where the PR
+ is to be sent, automatically supplied by `send-pr'.
+
+`Subject:'
+ A terse description of the problem. This field normally contains
+ the same information as the `>Synopsis:' field.
+
+`From:'
+ Usually supplied automatically by the originator's mailer; should
+ contain the originator's electronic mail address.
+
+`Reply-To:'
+ A return address to which electronic replies can be sent; in most
+ cases, the same address as the `From:' field.
+
+
+File: send-pr.info, Node: Problem Report fields, Prev: Mail header fields, Up: Fields
+
+Problem Report fields
+---------------------
+
+Field descriptions
+------------------
+
+ The following fields are present whenever a PR is submitted via the
+program `send-pr'. GNATS adds additional fields when the PR arrives at
+the Support Site; explanations of these follow this list.
+
+`>Submitter-Id:'
+ (TEXT) A unique identification code assigned by the Support Site.
+ It is used to identify all Problem Reports coming from a particular
+ site. (Submitters without a value for this field can invoke
+ `send-pr' with the `--request-id' option to apply for one from the
+ support organization. Problem Reports from those not affiliated
+ with the support organization should use the default value of `net'
+ for this field.)
+
+`>Originator:'
+ (TEXT) Originator's real name. The default is the value of the
+ originator's environment variable `NAME'.
+
+`>Organization:'
+ (MULTITEXT) The originator's organization. The default value is
+ set with the variable `DEFAULT_ORGANIZATION' in the `send-pr'
+ shell script.
+
+`>Confidential:'
+ (ENUMERATED) Use of this field depends on the originator's
+ relationship with the support organization; contractual agreements
+ often have provisions for preserving confidentiality. Conversely,
+ a lack of a contract often means that any data provided will not
+ be considered confidential. Submitters should be advised to
+ contact the support organization directly if this is an issue.
+
+ If the originator's relationship to the support organization
+ provides for confidentiality, then if the value of this field is
+ `yes' the support organization treats the PR as confidential; any
+ code samples provided are not made publicly available (e.g., in
+ regression test suites). The default value is `yes'.
+
+`>Synopsis:'
+ (TEXT) One-line summary of the problem. `send-pr' copies this
+ information to the `Subject:' line when you submit a Problem
+ Report.
+
+`>Severity:'
+ (ENUMERATED) The severity of the problem. Accepted values include:
+
+ `critical'
+ The product, component or concept is completely
+ non-operational or some essential functionality is missing.
+ No workaround is known.
+
+ `serious'
+ The product, component or concept is not working properly or
+ significant functionality is missing. Problems that would
+ otherwise be considered `critical' are rated `serious' when a
+ workaround is known.
+
+ `non-critical'
+ The product, component or concept is working in general, but
+ lacks features, has irritating behavior, does something
+ wrong, or doesn't match its documentation. The default value
+ is `serious'.
+
+`>Priority:'
+ (ENUMERATED) How soon the originator requires a solution. Accepted
+ values include:
+
+ `high'
+ A solution is needed as soon as possible.
+
+ `medium'
+ The problem should be solved in the next release.
+
+ `low'
+ The problem should be solved in a future release.
+
+ The default value is `medium'.
+
+`>Category:'
+ (TEXT) The name of the product, component or concept where the
+ problem lies. The values for this field are defined by the Support
+ Site.
+
+`>Class:'
+ (ENUMERATED) The class of a problem can be one of the following:
+
+ `sw-bug'
+ A general product problem. (`sw' stands for "software".)
+
+ `doc-bug'
+ A problem with the documentation.
+
+ `change-request'
+ A request for a change in behavior, etc.
+
+ `support'
+ A support problem or question.
+
+ `duplicate (PR-NUMBER)'
+ Duplicate PR. PR-NUMBER should be the number of the original
+ PR.
+
+ The default is `sw-bug'.
+
+`>Release:'
+ (TEXT) Release or version number of the product, component or
+ concept.
+
+`>Environment:'
+ (MULTITEXT) Description of the environment where the problem
+ occured: machine architecture, operating system, host and target
+ types, libraries, pathnames, etc.
+
+`>Description:'
+ (MULTITEXT) Precise description of the problem.
+
+`>How-To-Repeat:'
+ (MULTITEXT) Example code, input, or activities to reproduce the
+ problem. The support organization uses example code both to
+ reproduce the problem and to test whether the problem is fixed.
+ Include all preconditions, inputs, outputs, conditions after the
+ problem, and symptoms. Any additional important information
+ should be included. Include all the details that would be
+ necessary for someone else to recreate the problem reported,
+ however obvious. Sometimes seemingly arbitrary or obvious
+ information can point the way toward a solution. See also *Note
+ Helpful hints: Helpful hints.
+
+`>Fix:'
+ (MULTITEXT) A description of a solution to the problem, or a patch
+ which solves the problem. (This field is most often filled in at
+ the Support Site; we provide it to the submitter in case she has
+ solved the problem.)
+
+GNATS adds the following fields when the PR arrives at the Support Site:
+
+`>Number:'
+ (ENUMERATED) The incremental identification number for this PR.
+
+ The `>Number:' field is often paired with the `>Category:' field as
+
+ CATEGORY/NUMBER
+
+ in subsequent email messages. This is for historical reasons, as
+ well as because Problem Reports are stored in subdirectories which
+ are named by category.
+
+`>State:'
+ (ENUMERATED) The current state of the PR. Accepted values are:
+
+ `open'
+ The PR has been filed and the responsible person notified.
+
+ `analyzed'
+ The responsible person has analyzed the problem.
+
+ `feedback'
+ The problem has been solved, and the originator has been
+ given a patch or other fix.
+
+ `closed'
+ The changes have been integrated, documented, and tested, and
+ the originator has confirmed that the solution works.
+
+ `suspended'
+ Work on the problem has been postponed.
+
+ The initial state of a PR is `open'. *Note States of Problem
+ Reports: States.
+
+`>Responsible:'
+ (TEXT) The person responsible for this category.
+
+`>Arrival-Date:'
+ (TEXT) The time that this PR was received by GNATS. The date is
+ provided automatically by GNATS.
+
+`>Audit-Trail:'
+ (MULTITEXT) Tracks related electronic mail as well as changes in
+ the `>State:' and `>Responsible:' fields with the sub-fields:
+
+ `State-Changed-<From>-<To>: OLDSTATE>-<NEWSTATE'
+ The old and new `>State:' field values.
+
+ `Responsible-Changed-<From>-<To>: OLDRESP>-<NEWRESP'
+ The old and new `>Responsible:' field values.
+
+ `State-Changed-By: NAME'
+ `Responsible-Changed-By: NAME'
+ The name of the maintainer who effected the change.
+
+ `State-Changed-When: TIMESTAMP'
+ `Responsible-Changed-When: TIMESTAMP'
+ The time the change was made.
+
+ `State-Changed-Why: REASON...'
+ `Responsible-Changed-Why: REASON...'
+ The reason for the change.
+
+ The `>Audit-Trail:' field also contains any mail messages received
+ by GNATS related to this PR, in the order received.
+
+`>Unformatted:'
+ (MULTITEXT) Any random text found outside the fields in the
+ original Problem Report.
+
+
+File: send-pr.info, Node: Invoking send-pr, Next: An Example, Prev: send-pr in detail, Up: Top
+
+Editing and sending PRs
+***********************
+
+ You can invoke `send-pr' from a shell prompt or from within GNU
+Emacs using `M-x send-pr'.
+
+* Menu:
+
+* using send-pr:: Creating new Problem Reports
+* send-pr in Emacs:: Using send-pr from within Emacs
+* send-pr from the shell:: Invoking send-pr from the shell
+* Helpful hints::
+
+
+File: send-pr.info, Node: using send-pr, Next: send-pr in Emacs, Up: Invoking send-pr
+
+Creating new Problem Reports
+============================
+
+ Invoking `send-pr' presents a PR "template" with a number of fields
+already filled in. Complete the template as thoroughly as possible to
+make a useful bug report. Submit only one bug with each PR.
+
+ A template consists of three sections:
+
+"Comments"
+ The top several lines of a blank template consist of a series of
+ comments that provide some basic instructions for completing the
+ Problem Report, as well as a list of valid entries for the
+ `>Category:' field. These comments are all preceded by the string
+ `SEND-PR:' and are erased automatically when the PR is submitted.
+ The instructional comments within `<' and `>' are also removed.
+ (Only these comments are removed; lines you provide that happen to
+ have those characters in them, such as examples of shell-level
+ redirection, are not affected.)
+
+"Mail Header"
+ `send-pr' creates a standard mail header. `send-pr' completes all
+ fields except the `Subject:' line with default values. (*Note
+ Problem Report format: Fields.)
+
+"GNATS fields"
+ These are the informational fields that GNATS uses to route your
+ Problem Report to the responsible party for further action. They
+ should be filled out as completely as possible. (*Note Problem
+ Report format: Fields. Also see *Note Helpful hints: Helpful
+ hints.)
+
+For examples of a Problem Report template and complete Problem Report,
+see *Note An Example::.
+
+ The default template contains your preconfigured `>Submitter-Id:'.
+`send-pr' attempts to determine values for the `>Originator:' and
+`>Organization:' fields (*note Problem Report format: Fields.).
+`send-pr' also attempts to find out some information about your system
+and architecture, and places this information in the `>Environment:'
+field if it finds any.
+
+ You may submit problem reports to different Support Sites from the
+default site by specifying the alternate site when you invoke
+`send-pr'. Each `site' has its own list of categories for which it
+accepts Problem Reports. (*Note Setting a default SITE: default site.)
+
+ `send-pr' also provides the mail header section of the template with
+default values in the `To:', `From:', and `Reply-To:' fields. The
+`Subject:' field is empty.
+
+ The template begins with a comment section:
+
+ SEND-PR: -*- send-pr -*-
+ SEND-PR: Lines starting with `SEND-PR' will be removed
+ SEND-PR: automatically as well as all comments (the text
+ SEND-PR: below enclosed in `<' and `>').
+ SEND-PR:
+ SEND-PR: Please consult the document `Reporting Problems
+ SEND-PR: Using send-pr' if you are not sure how to fill out
+ SEND-PR: a problem report.
+ SEND-PR:
+ SEND-PR: Choose from the following categories:
+
+and also contains a list of valid `>Category:' values for the Support
+Site to whom you are submitting this Problem Report. One (and only
+one) of these values should be placed in the `>Category:' field. A
+complete sample bug report, from template to completed PR, is shown in
+*Note An Example::. For a complete list of valid categories, type
+`send-pr -L' at your prompt. *Note Valid Categories: Valid Categories,
+for a sample list of categories, .
+
+ The mail header is just below the comment section. Fill out the
+`Subject:' field, if it is not already completed using the value of
+`>Synopsis:'. The other mail header fields contain default values.
+
+ To: SUPPORT-SITE
+ Subject: *complete this field*
+ From: YOUR-LOGIN@YOUR-SITE
+ Reply-To: YOUR-LOGIN@YOUR-SITE
+ X-send-pr-version: send-pr 3.2
+
+where SUPPORT-SITE is an alias for the Support Site you wish to submit
+this PR to.
+
+ The rest of the template contains GNATS fields. Each field is
+either automatically completed with valid information (such as your
+`>Submitter-Id:') or contains a one-line instruction specifying the
+information that field requires in order to be correct. For example,
+the `>Confidential:' field expects a value of `yes' or `no', and the
+answer must fit on one line; similarly, the `>Synopsis:' field expects
+a short synopsis of the problem, which must also fit on one line. Fill
+out the fields as completely as possible. *Note Helpful hints: Helpful
+hints, for suggestions as to what kinds of information to include.
+
+ In this example, words in *italics* are filled in with
+pre-configured information:
+
+ >Submitter-Id: *your submitter-id*
+ >Originator: *your name here*
+ >Organization:
+ *your organization*
+ >Confidential:<[ yes | no ] (one line)>
+ >Synopsis: <synopsis of the problem (one line)>
+ >Severity: <[non-critical | serious | critical](one line)>
+ >Priority: <[ low | medium | high ] (one line)>
+ >Category: <name of the product (one line)>
+ >Class: <[sw-bug | doc-bug | change-request | support]>
+ >Release: <release number or tag (one line)>
+ >Environment:
+ <machine, os, target, libraries (multiple lines)>
+
+ >Description:
+ <precise description of the problem (multiple lines)>
+ >How-To-Repeat:
+ <code/input/activities to reproduce (multiple lines)>
+ >Fix:
+ <how to correct or work around the problem, if known
+ (multiple lines)>
+
+ When you finish editing the Problem Report, `send-pr' mails it to
+the address named in the `To:' field in the mail header. `send-pr'
+checks that the complete form contains a valid `>Category:'.
+
+ Your copy of `send-pr' should have already been customized on
+installation to reflect your `>Submitter-Id:'. (*Note Installing
+`send-pr' on your system: Installing send-pr.) If you don't know your
+`>Submitter-Id:', you can request it using `send-pr --request-id'. If
+your organization is not affiliated with the site you send Problem
+Reports to, a good generic `>Submitter-Id:' to use is `net'.
+
+ If your PR has an invalid value in one of the ENUMERATED fields
+(*note Problem Report format: Fields.), `send-pr' places the PR in a
+temporary file named `/tmp/pbadNNNN' on your machine. NNNN is the
+process identification number given to your current `send-pr' session.
+If you are running `send-pr' from the shell, you are prompted as to
+whether or not you wish to try editing the same Problem Report again.
+If you are running `send-pr' from Emacs, the Problem Report is placed
+in the buffer `*send-pr-error*'; you can edit this file and then submit
+it with
+
+ M-x gnats-submit-pr
+
+ Any further mail concerning this Problem Report should be
+carbon-copied to the GNATS mailing address as well, with the category
+and identification number in the `Subject:' line of the message.
+
+ Subject: Re: PR CATEGORY/GNATS-ID: ORIGINAL MESSAGE SUBJECT
+
+Messages which arrive with `Subject:' lines of this form are
+automatically appended to the Problem Report in the `>Audit-Trail:'
+field in the order received.
+
+
+File: send-pr.info, Node: send-pr in Emacs, Next: send-pr from the shell, Prev: using send-pr, Up: Invoking send-pr
+
+Using `send-pr' from within Emacs
+=================================
+
+ You can use an interactive `send-pr' interface from within GNU Emacs
+to fill out your Problem Report. We recommend that you familiarize
+yourself with Emacs before using this feature (*note Introduction:
+(emacs)Introduction.).
+
+ Call `send-pr' with `M-x send-pr'.(1) `send-pr' responds with a
+Problem Report template preconfigured for the Support Site from which
+you received `send-pr'. (If you use `send-pr' locally, the default
+Support Site is probably your local site.)
+
+ You may also submit problem reports to different Support Sites from
+the default site. To use this feature, invoke `send-pr' with
+
+ C-u M-x send-pr
+
+ `send-pr' prompts you for the name of a SITE. SITE is an alias on
+your local machine which points to an alternate Support Site.
+
+ `send-pr' displays the template and prompts you in the minibuffer
+with the line:
+ >Category: other
+
+Delete the default value `other' *in the minibuffer* and replace it
+with the keyword corresponding to your problem (the list of valid
+categories is in the topmost section of the PR template). For example,
+if the problem you wish to report has to do with the GNU C compiler,
+and your support organization accepts bugs submitted for this program
+under the category `gcc', delete `other' and then type `gcc[RET]'.
+`send-pr' replaces the line
+
+ >Category: <name of the product (one line)>
+
+in the template with
+
+ >Category: gcc
+
+and moves on to another field.
+
+ `send-pr' provides name completion in the minibuffer. For instance,
+you can also type `gc[TAB]', and `send-pr' attempts to complete the
+entry for you. Typing `g[TAB]' may not have the same effect if several
+possible entries begin with `g'. In that case `send-pr' cannot
+complete the entry because it cannot determine whether you mean `gcc'
+or, for example, `gdb', if both of those are possible categories.
+`send-pr' continues to prompt you for a valid entry until you enter one.
+
+ `send-pr' prompts you interactively to enter each field for which
+there is a range of specific choices. If you attempt to enter a value
+which is not in the range of acceptable entries, `send-pr' responds
+with `[No match]' and allows you to change the entry until it contains
+an acceptable value. This avoids unusable information (at least in
+these fields) and also avoids typographical errors which could cause
+problems later.
+
+ `send-pr' prompts you for the following fields:
+
+ >Category:
+ >Confidential: (*default*: no)
+ >Severity: (*default*: serious)
+ >Priority: (*default*: medium)
+ >Class: (*default*: sw-bug)
+ >Release:
+ >Synopsis: (*this value is copied to `Subject:'*)
+
+After you complete these fields, `send-pr' places the cursor in the
+`>Description:' field and displays the message
+
+ To send the problem report use: C-c C-c
+
+in the minibuffer. At this point, edit the file in the main buffer to
+reflect your specific problem, putting relevant information in the
+proper fields. *Note An Example::, for a sample Problem Report.
+
+ `send-pr' provides a few key bindings to make moving around in a
+template buffer more simple:
+
+`C-c C-f'
+`M-x change-field'
+ Changes the field under the cursor. `edit-pr' prompts you for a
+ new value.
+
+`M-C-b'
+`M-x gnats-backward-field'
+ Moves the cursor to the beginning of the value of the current
+ field.
+
+`M-C-f'
+`M-x gnats-forward-field'
+ Moves the cursor to the end of the value of the current field.
+
+`M-p'
+`M-x gnats-previous-field'
+ Moves the cursor back one field to the beginning of the value of
+ the previous field.
+
+`M-n'
+`M-x gnats-next-field'
+ Moves the cursor forward one field to the beginning of the value
+ of the next field.
+
+ `send-pr' takes over again when you type `C-c C-c' to send the
+message. `send-pr' reports any errors in a separate buffer, which
+remains in existence until you send the PR properly (or, of course,
+until you explicitly kill the buffer).
+
+ For detailed instructions on using Emacs, see *Note Introduction:
+(emacs)Introduction.
+
+ ---------- Footnotes ----------
+
+ (1) If typing `M-x send-pr' doesn't work, see your system
+administrator for help loading `send-pr' into Emacs.
+
+
+File: send-pr.info, Node: send-pr from the shell, Next: Helpful hints, Prev: send-pr in Emacs, Up: Invoking send-pr
+
+Invoking `send-pr' from the shell
+=================================
+
+ send-pr [ SITE ]
+ [ -f PROBLEM-REPORT | --file PROBLEM-REPORT ]
+ [ -t MAIL-ADDRESS | --to MAIL-ADDRESS ]
+ [ --request-id ]
+ [ -L | --list ] [ -P | --print ]
+ [ -V | --version] [ -h | --help ]
+
+ SITE is an alias on your local machine which points to an address
+used by a Support Site. If this argument is not present, the default
+SITE is usually the site which you received `send-pr' from, or your
+local site if you use GNATS locally. (*Note Setting a default SITE:
+default site.)
+
+ Invoking `send-pr' with no options calls the editor named in your
+environment variable `EDITOR' on a default PR template. If the
+environment variable `PR_FORM' is set, its value is used as a file name
+which contains a valid template. If `PR_FORM' points to a missing or
+unreadable file, or if the file is empty, `send-pr' generates an error
+message and opens the editor on a default template.
+
+`-f PROBLEM-REPORT'
+`--file PROBLEM-REPORT'
+ Specifies a file, PROBLEM-REPORT, where a completed Problem Report
+ already exists. `send-pr' sends the contents of the file without
+ invoking an editor. If PROBLEM-REPORT is `-', `send-pr' reads
+ from standard input.
+
+`-t MAIL-ADDRESS'
+`--to MAIL-ADDRESS'
+ Sends the PR to MAIL-ADDRESS. The default is preset when `send-pr'
+ is configured. *This option is not recommended*; instead, use the
+ argument SITE on the command line.
+
+`--request-id'
+ Sends a request for a `>Submitter-Id:' to the Support Site.
+
+`-L'
+`--list'
+ Prints the list of valid `>Category:' values on standard output.
+ No mail is sent.
+
+`-P'
+`--print'
+ Displays the PR template. If the variable `PR_FORM' is set in your
+ environment, the file it specifies is printed. If `PR_FORM' is not
+ set, `send-pr' prints the standard blank form. If the file
+ specified by `PR_FORM' doesn't exist, `send-pr' displays an error
+ message. No mail is sent.
+
+`-V'
+`--version'
+ Displays the `send-pr' version number and a usage summary. No mail
+ is sent.
+
+`-h'
+`--help'
+ Displays a usage summary for `send-pr'. No mail is sent.
+
+
+File: send-pr.info, Node: Helpful hints, Prev: send-pr from the shell, Up: Invoking send-pr
+
+Helpful hints
+=============
+
+ There is no orthodox standard for submitting effective bug reports,
+though you might do well to consult the section on submitting bugs for
+GNU `gcc' in *Note Reporting Bugs: (gcc)Bugs, by Richard Stallman.
+This section contains instructions on what kinds of information to
+include and what kinds of mistakes to avoid.
+
+ In general, common sense (assuming such an animal exists) dictates
+the kind of information that would be most helpful in tracking down and
+resolving problems in software.
+ * Include anything *you* would want to know if you were looking at
+ the report from the other end. There's no need to include every
+ minute detail about your environment, although anything that might
+ be different from someone else's environment should be included
+ (your path, for instance).
+
+ * Narratives are often useful, given a certain degree of restraint.
+ If a person responsible for a bug can see that A was executed, and
+ then B and then C, knowing that sequence of events might trigger
+ the realization of an intermediate step that was missing, or an
+ extra step that might have changed the environment enough to cause
+ a visible problem. Again, restraint is always in order ("I set
+ the build running, went to get a cup of coffee (Columbian, cream
+ but no sugar), talked to Sheila on the phone, and then THIS
+ happened...") but be sure to include anything relevant.
+
+ * Richard Stallman writes, "The fundamental principle of reporting
+ bugs usefully is this: *report all the facts*. If you are not sure
+ whether to state a fact or leave it out, state it!" This holds
+ true across all problem reporting systems, for computer software
+ or social injustice or motorcycle maintenance. It is especially
+ important in the software field due to the major differences
+ seemingly insignificant changes can make (a changed variable, a
+ missing semicolon, etc.).
+
+ * Submit only *one* problem with each Problem Report. If you have
+ multiple problems, use multiple PRs. This aids in tracking each
+ problem and also in analyzing the problems associated with a given
+ program.
+
+ * It never hurts to do a little research to find out if the bug
+ you've found has already been reported. Most software releases
+ contain lists of known bugs in the Release Notes which come with
+ the software; see your system administrator if you don't have a
+ copy of these.
+
+ * The more closely a PR adheres to the standard format, the less
+ interaction is required by a database administrator to route the
+ information to the proper place. Keep in mind that anything that
+ requires human interaction also requires time that might be better
+ spent in actually fixing the problem. It is therefore in
+ everyone's best interest that the information contained in a PR be
+ as correct as possible (in both format and content) at the time of
+ submission.
+
+
+File: send-pr.info, Node: An Example, Next: Installing send-pr, Prev: Invoking send-pr, Up: Top
+
+An Example
+**********
+
+ Cygnus Support in Mountain View, CA, uses GNATS and `send-pr'
+extensively for their support activities. As a support company, Cygnus
+finds problem tracking to be a crucial part of everyday business.
+Cygnus supports the GNU compiling tools (including GNATS and `send-pr')
+over several many platforms
+
+ With each shipment of the Cygnus Support Developer's Kit, customers
+receive the latest version of `send-pr', which contains an up-to-date
+listing of valid categories (values for the `>Category:' field). Using
+these tools, Cygnus' customers can communicate their problems to Cygnus
+effectively and receive automatic confirmation of receipt as well as
+notification of changes in the status of their reported problems. Much
+of Cygnus' support mechanism relies on electronic mail.
+
+ As an example, let's pretend we're a customer of Cygnus Support, and
+that we're having a problem compiling some of our software using the
+GNU C compiler, which Cygnus supports.
+
+ Assume that we're getting an error in our `bifrabulator' program
+wherein the `prestidigitation' routines don't match with the
+`whatsitsname'. We've made sure we're following the rules of the
+program and checked the Release Notes from Cygnus and found that the bug
+isn't already known. In other words, we're pretty sure we've found a
+bug.
+
+ Our first step is to call `send-pr'. It really doesn't matter
+whether we use `send-pr' from the shell or from within Emacs. Indeed,
+if we use Emacs as a primary editor, calling `send-pr' from the shell
+is likely to start `send-pr' in an Emacs buffer anyway. So, since our
+company, *Imaginary Software, Ltd.*, uses GNU software extensively,
+we're pretty familiar with Emacs, so from within Emacs we type
+ M-x send-pr
+
+and we're greeted with the following screen:
+
+ SEND-PR: -*- text -*-
+ SEND-PR: Lines starting with `SEND-PR' will be removed
+ SEND-PR: automatically as well as all comments (the text
+ SEND-PR: below enclosed in `<' and `>').
+ SEND-PR: Please consult the manual if you are not sure
+ SEND-PR: how to fill out a problem report.
+ SEND-PR:
+ SEND-PR: Choose from the following categories:
+ SEND-PR:
+ SEND-PR: bfd binutils bison
+ SEND-PR: byacc clib config cvs diff
+ SEND-PR: doc emacs flex g++ gas
+ SEND-PR: gcc gdb glob gprof grep
+ SEND-PR: info ispell kerberos ld libg++
+ SEND-PR: libiberty make makeinfo mas newlib
+ SEND-PR: other patch rcs readline send-pr
+ SEND-PR: test texindex texinfo texinfo.tex
+ SEND-PR: bifrabulator <---*note: this one is fake*
+ SEND-PR:
+ To: cygnus-bugs@cygnus.com
+ Subject:
+ From: jeffrey@imaginary.com
+ Reply-To: jeffrey@imaginary.com
+ X-send-pr-version: send-pr 3.2
+
+ >Submitter-Id: imaginary
+ >Originator: Jeffrey Osier
+ >Organization:
+ Imaginary Software, Ltd.
+ >Confidential: <[ yes | no ] (one line)>
+ >Synopsis: <synopsis of the problem (one line)>
+ >Severity: <[ non-critical | serious | critical ] (one line)>
+ >Priority: <[ low | medium | high ] (one line)>
+ >Category: <name of the product (one line)>
+ >Class: <[sw-bug|doc-bug|change-request|support](oneline)>
+ >Release: <release number or tag (one line)>
+ >Environment:
+ <machine, os, target, libraries (multiple lines)>
+ System: SunOS imaginary.com 4.1.1 1 sun4
+ Architecture: sun4
+
+ >Description:
+ <precise description of the problem (multiple lines)>
+ >How-To-Repeat:
+ <code/input/activities to reproduce (multiple lines)>
+ >Fix:
+ -----Emacs: *send-pr* (send-pr Fill)----All------------------
+ >Category: other[]
+
+ We know from past experience that we need to set certain information
+into each field, so we compile all the information we know about our
+problem. We have some sample code which we know should work, even
+though it doesn't, so we'll include that. Below is the completed PR;
+we send this using `C-c C-c'. (The comments have been truncated).
+
+ SEND-PR: Lines starting with `SEND-PR' will be removed
+ SEND-PR: automatically as well as all comments (the text
+ SEND-PR: ...
+ SEND-PR:
+ To: cygnus-bugs@cygnus.com
+ Subject: bifrabulator routines don't match
+ From: jeffrey@imaginary.com
+ Reply-To: jeffrey@imaginary.com
+ X-send-pr-version: send-pr 3.2
+
+ >Submitter-Id: imaginary
+ >Originator: Jeffrey Osier
+ >Organization:
+ Imaginary Software, Ltd.
+ >Confidential: no
+ >Synopsis: bifrabulator routines don't match
+ >Severity: serious
+ >Priority: medium
+ >Category: bifrabulator
+ >Class: sw-bug
+ >Release: progressive-930101
+ >Environment:
+ System: SunOS imaginary.com 4.1.1 1 sun4
+ Architecture: sun4 (SPARC)
+
+ >Description:
+ the following code I fed into the bifrabulator came back
+ with a strange error. apparently, the prestidigitation
+ routine doesn't match with the whatsitsname in all cases.
+
+ >How-To-Repeat:
+ call the bifrabulator on the following code.
+ *code sample...*
+
+ >Fix:
+ -----Emacs: *send-pr* (send-pr Fill)----All------------------
+ To send the problem report use: C-c C-c
+
+ We type `C-c C-c', and off it goes. Now, we depend on Cygnus
+Support to figure out the answer to our problem.
+
+ Soon afterward, we get the following message from Cygnus:
+
+ From: gnats (GNATS management)
+ Sender: gnats-admin
+ Reply-To: hacker@cygnus.com
+ To: jeffrey@imaginary.com
+ Subject: Re: bifrabulator/1425: routines don't match
+
+ Thank you very much for your problem report.
+ It has the internal identification: g++/1425.
+ The individual assigned to look at your bug is: hacker
+ (F.B. Hacker)
+
+ Category: bifrabulator
+ Responsible: hacker
+ Synopsis: bifrabulator routines don't match
+ Arrival-Date: Sat Feb 30 03:12:55 1993
+
+This is our receipt that the bug has been accepted and forwarded to the
+responsible party.
+
+A while later, we get the analysis:
+
+ To: jeffrey@imaginary.com
+ From: hacker@cygnus.com
+ Subject: Re: bifrabulator/1425: routines don't match
+ Reply-To: hacker@cygnus.com
+
+ Got your message, Jeff. It seems that the bifrabulator was
+ confusing the prestidigitation routines with the realitychecker
+ when lexically parsing the whatsitsname.
+
+ I'm working on robustisizing the bifrabulator now.
+
+ How about lunch next week?
+ --
+ F.B. Hacker
+ Cygnus Support, Mountain View, CA 415 903 1400
+ #include <std-disclaimer.h>
+
+About the same time, we get another message from Cygnus.
+
+ From: hacker@cygnus.com
+ To: jeffrey@imaginary.com
+ Subject: Re: bifrabulator/1425: doesn't match prestidig
+ Reply-To: hacker@cygnus.com
+
+
+ `F.B. Hacker' changed the state to `analyzed'.
+
+ State-Changed-From-To: open-analyzed
+ State-Changed-By: hacker
+ State-Changed-When: Fri Feb 31 1993 08:59:16 1993
+ State-Changed-Why:
+ figured out the problem, working on a patch this afternoon
+ --
+ F.B. Hacker
+ Cygnus Support, Mountain View, CA 415 903 1400
+ #include <std-disclaimer.h>
+
+The bug has now been analyzed, and Cygnus is working on a solution.
+
+Sometime later, we get more mail from F.B.:
+
+ To: jeffrey@imaginary.com
+ From: hacker@cygnus.com
+ Subject: Re: bifrabulator/1425: routines don't match
+ Reply-To: hacker@cygnus.com
+
+ There's a patch now that you can ftp over and check out.
+
+ Hey, that joke you sent me was great! The one about the
+ strings walking into a bar... my boss laughed for an hour!
+ --
+ F.B. Hacker
+ Cygnus Support, Mountain View, CA 415 903 1400
+ #include <std-disclaimer.h>
+
+ From: hacker@cygnus.com
+ To: jeffrey@imaginary.com
+ Subject: Re: bifrabulator/1425: doesn't match prestidig
+ Reply-To: hacker@cygnus.com
+
+
+ `F.B. Hacker' changed the state to `feedback'.
+
+ State-Changed-From-To: analyzed-feedback
+ State-Changed-By: hacker
+ State-Changed-When: Fri Feb 31 1993 23:43:16 1993
+ State-Changed-Why:
+ got the patch finished, notified Jeff at Imaginary Software
+ --
+ F.B. Hacker
+ Cygnus Support, Mountain View, CA 415 903 1400
+ #include <std-disclaimer.h>
+
+The bug has gone into "feedback" status now, until we get the patch,
+install it and test it. When everything tests well, we can mail F.B.
+back and tell him the bug's been fixed, and he can change the state of
+the PR from "feedback" to "closed".
+
+ Following is a list of valid `>Category:' entries that are supported
+by Cygnus.
+
+* Menu:
+
+* Valid Categories::
+
+
+File: send-pr.info, Node: Valid Categories, Up: An Example
+
+Valid Categories
+================
+
+`bfd'
+ GNU binary file descriptor library.
+
+`bifrabulator'
+ This one doesn't actually exist.
+
+`binutils'
+ GNU utilities for binary files (`ar', `nm', `size'...).
+
+`bison'
+ GNU parser generator.
+
+`byacc'
+ Free parser generator.
+
+`config'
+ Cygnus Support Software configuration and installation.
+
+`cvs'
+ Concurrent Version System.
+
+`diff'
+ GNU `diff' program.
+
+`doc'
+ Documentation and manuals.
+
+`emacs'
+ GNU Emacs editor and related functions.
+
+`flex'
+ GNU lexical analyzer.
+
+`g++'
+ GNU C++ compiler.
+
+`gas'
+ GNU assembler.
+
+`gcc'
+ GNU C compiler.
+
+`gdb'
+ GNU source code debugger.
+
+`glob'
+ The filename globbing functions.
+
+`gprof'
+ GNU profiler.
+
+`grep'
+ GNU `grep' program.
+
+`info'
+ GNU `info' hypertext reader.
+
+`ispell'
+ GNU spelling checker.
+
+`kerberos'
+ Kerberos authentication system.
+
+`ld'
+ GNU linker.
+
+`libc'
+ Cygnus Support C Support Library.
+
+`libg++'
+ GNU C++ class library.
+
+`libiberty'
+ GNU `libiberty' library.
+
+`libm'
+ Cygnus Support C Math Library.
+
+`make'
+ GNU `make' program.
+
+`makeinfo'
+ GNU utility to build Info files from Texinfo documents.
+
+`mas'
+ GNU Motorola syntax assembler.
+
+`newlib'
+ Cygnus Support C Support and Math Libraries.
+
+`patch'
+ GNU bug patch program.
+
+`gnats'
+ GNU Problem Report Management System.
+
+`rcs'
+ Revision Control System.
+
+`readline'
+ GNU `readline' library.
+
+`send-pr'
+ GNU Problem Report submitting program.
+
+`test'
+ Category to use when testing `send-pr'.
+
+`texindex'
+ GNU documentation indexing utility.
+
+`texinfo'
+ GNU documentation macros.
+
+`other'
+ Anything which is not covered by the above categories.
+
+
+File: send-pr.info, Node: Installing send-pr, Next: Index, Prev: An Example, Up: Top
+
+Installing `send-pr' on your system
+***********************************
+
+ If you receive `send-pr' as part of a larger software distribution,
+it probably gets installed when the full distribution is installed. If
+you are using GNATS at your site as well, you must decide where
+`send-pr' sends Problem Reports by default; see *Note Setting a default
+SITE: default site.
+
+* Menu:
+
+* installation:: installing `send-pr' by itself
+* default site:: setting a default site
+
+
+File: send-pr.info, Node: installation, Next: default site, Up: Installing send-pr
+
+Installing `send-pr' by itself
+==============================
+
+ Install `send-pr' by following these steps (you may need `root'
+access in order to change the `aliases' file and to install `send-pr'):
+
+ * Unpack the distribution into a directory which we refer to as
+ SRCDIR.
+
+ * Edit the file `Makefile' to reflect local conventions.
+ Specifically, you should edit the variable `prefix' to alter the
+ installation location. The default is `/usr/local'. All files are
+ installed under `prefix' (see below).
+
+ * *Run*
+ make all install [ info ] [ install-info ] [ clean ]
+
+ The targets mean the following:
+
+ `all'
+ Builds `send-pr' and `install-sid'
+
+ `install'
+ Installs the following:
+
+ `install-sid'
+ `send-pr'
+ into `PREFIX/bin'
+
+ `send-pr.1'
+ into `PREFIX/man/man1'
+
+ `SITE'
+ the list of valid CATEGORIES for the Support Site from
+ which you received `send-pr', installed as
+ `PREFIX/lib/gnats/SITE'
+
+ `send-pr.el'
+ into `PREFIX/lib/emacs/lisp'(1)
+
+ `info (*optional*)'
+ Builds `send-pr.info' from `send-pr.texi'
+ (`send-pr.info' is included with this distribution)
+
+ `install-info (*optional*)'
+ Installs `send-pr.info' into `PREFIX/info'
+
+ `clean (*optional*)'
+ Removes all intermediary build files that can be rebuilt from
+ source code
+
+ * Run
+
+ install-sid YOUR-SID
+
+ where YOUR-SID is the identification code you received with
+ `send-pr'. `send-pr' automatically inserts this value into the
+ template field `>Submitter-Id:'. If you've downloaded `send-pr'
+ from the Net, use `net' for this value.
+
+ * Place the following line in `PREFIX/lib/emacs/lisp/default.el', or
+ instruct your users to place the following line in their `.emacs'
+ files:
+
+ (autoload 'send-pr "send-pr" "Submit a Problem Report." t)
+
+ * Create a mail alias for the Support Site from which you received
+ `send-pr', and for every site with which you wish to use `send-pr'
+ to communicate. Each alias must have a suffix of `-gnats'. The
+ Support Site(s) will provide the correct addresses where these
+ aliases should point. For instance, edit your mail aliases file
+ to contain something like:
+
+ # support sites; for use with send-pr
+ cygnus-gnats: bugs@cygnus.com # Cygnus Support
+ bumblebee-gnats: bumblebugs@bumblebee.com # Bumblebee Inc.
+ mycompany-gnats: bugs@my.company.com (*if you use GNATS locally*)
+
+ `send-pr' automatically searches for these aliases when you type
+
+ send-pr cygnus
+ send-pr bumblebee
+ send-pr SITE...
+
+ `send-pr' also uses SITE to determine the categories of problems
+ accepted by the site in question by looking in
+
+ PREFIX/lib/gnats/SITE
+
+ ---------- Footnotes ----------
+
+ (1) If your main Emacs lisp repository is in a different directory
+from this, substitute that directory for `PREFIX/lib/emacs/lisp'.
+
+
+File: send-pr.info, Node: default site, Prev: installation, Up: Installing send-pr
+
+Setting a default SITE
+======================
+
+ `send-pr' is capable of sending Problem Reports to any number of
+Support Sites, using mail aliases which have `-gnats' appended them.
+`send-pr' automatically appends the suffix, so that when you type
+
+ send-pr SITE
+
+the Problem Report goes to the address noted in the `aliases' file as
+`SITE-gnats'. You can do this in the Emacs version of `send-pr' by
+invoking the program with
+
+ C-u M-x send-pr
+
+You are prompted for SITE.
+
+ SITE is also used to error-check the `>Category:' field, as a
+precaution against sending mistaken information (and against sending
+information to the wrong site).
+
+ You may also simply type
+
+ send-pr
+
+from the shell (or `M-x send-pr' in Emacs), and the Problem Report you
+generate will be sent to the SITE, which is usually the site from which
+you received your distribution of `send-pr'. If you use GNATS at your
+own organization, the default is usually your local address for
+reporting problems.
+
+ To change this, simply edit the file `Makefile' before installing
+and change the line
+
+ GNATS_SITE = SITE
+
+to reflect the site where you wish to send PRs by default.
+
+
+File: send-pr.info, Node: Index, Prev: Installing send-pr, Up: Top
+
+Index
+*****
+
+* Menu:
+
+* >Arrival-Date:: Problem Report fields.
+* >Audit-Trail:: Problem Report fields.
+* >Category:: Problem Report fields.
+* >Class:: Problem Report fields.
+* >Confidential:: Problem Report fields.
+* >Description:: Problem Report fields.
+* >Environment:: Problem Report fields.
+* >Fix:: Problem Report fields.
+* >How-To-Repeat:: Problem Report fields.
+* >Number:: Problem Report fields.
+* >Organization:: Problem Report fields.
+* >Originator:: Problem Report fields.
+* >Priority:: Problem Report fields.
+* >Release:: Problem Report fields.
+* >Responsible:: Problem Report fields.
+* >Severity:: Problem Report fields.
+* >State:: Problem Report fields.
+* >Submitter-Id:: using send-pr.
+* >Submitter-Id:: Problem Report fields.
+* >Synopsis:: Problem Report fields.
+* >Unformatted:: Problem Report fields.
+* Arrival-Date field: Problem Report fields.
+* Audit-Trail field: Problem Report fields.
+* bifrabulator: An Example.
+* Category field: Problem Report fields.
+* Class field: Problem Report fields.
+* Confidential field: Problem Report fields.
+* Description field: Problem Report fields.
+* Environment field: Problem Report fields.
+* Fix field: Problem Report fields.
+* From: header: Mail header fields.
+* How-To-Repeat field: Problem Report fields.
+* Number field: Problem Report fields.
+* Organization field: Problem Report fields.
+* Originator field: Problem Report fields.
+* Priority field: Problem Report fields.
+* Release field: Problem Report fields.
+* Reply-To: header: Mail header fields.
+* Responsible-Changed-<From>-<To>: in >Audit-Trail:: Problem Report fields.
+* Responsible-Changed-By: in >Audit-Trail:: Problem Report fields.
+* Responsible-Changed-When: in >Audit-Trail:: Problem Report fields.
+* Responsible-Changed-Why: in >Audit-Trail:: Problem Report fields.
+* Responsible field: Problem Report fields.
+* send-pr fields: using send-pr.
+* send-pr within Emacs: send-pr in Emacs.
+* Severity field: Problem Report fields.
+* State-Changed-<From>-<To>: in >Audit-Trail:: Problem Report fields.
+* State-Changed-By: in >Audit-Trail:: Problem Report fields.
+* State-Changed-When: in >Audit-Trail:: Problem Report fields.
+* State-Changed-Why: in >Audit-Trail:: Problem Report fields.
+* State field: Problem Report fields.
+* Subject: header: Mail header fields.
+* Submitter-Id field: Problem Report fields.
+* Submitter-Id field: using send-pr.
+* Synopsis field: Problem Report fields.
+* To: header: Mail header fields.
+* Unformatted field: Problem Report fields.
+* *analyzed* state: States.
+* *change-request* class: Problem Report fields.
+* *closed* state: States.
+* *critical* severity problems: Problem Report fields.
+* *doc-bug* class: Problem Report fields.
+* *duplicate* class: Problem Report fields.
+* *Enumerated* data types: Fields.
+* *feedback* state: States.
+* *high* priority problems: Problem Report fields.
+* *low* priority problems: Problem Report fields.
+* *medium* priority problems: Problem Report fields.
+* *MultiText* data types: Fields.
+* *non-critical* severity problems: Problem Report fields.
+* *open* state: States.
+* *serious* severity problems: Problem Report fields.
+* *support* class: Problem Report fields.
+* *suspended* state: States.
+* *sw-bug* class: Problem Report fields.
+* *Text* data types: Fields.
+* an example: An Example.
+* appending PRs: using send-pr.
+* appending PRs: Problem Report fields.
+* automatic notification: States.
+* bad Problem Reports: using send-pr.
+* blank PR template: An Example.
+* command line options: send-pr from the shell.
+* comment section in the PR template: using send-pr.
+* completed Problem Report: An Example.
+* completion in Emacs: send-pr in Emacs.
+* confidentiality in PRs: Problem Report fields.
+* Cygnus Support: An Example.
+* database similarities: Fields.
+* default SITE: default site.
+* default PR template: An Example.
+* details about send-pr: send-pr in detail.
+* editing and sending PRs: Invoking send-pr.
+* effective problem reporting: Helpful hints.
+* Emacs: send-pr in Emacs.
+* errors: using send-pr.
+* example of a completed PR: An Example.
+* example of a default template: An Example.
+* example of a list of valid categories: Valid Categories.
+* example of a state change: An Example.
+* example PR: An Example.
+* example Problem Report: Fields.
+* field format: Problem Report fields.
+* fields: Fields.
+* fields - list: Problem Report fields.
+* final state ("closed"): States.
+* foreword to send-pr: Top.
+* format: Fields.
+* generating new PRs: Invoking send-pr.
+* GNATS: Top.
+* GNATS database fields: Problem Report fields.
+* GNATS fields - list: Problem Report fields.
+* GNU software support: An Example.
+* helpful hints: Helpful hints.
+* Imaginary Software, Ltd.: An Example.
+* information to submit: Helpful hints.
+* initial state ("open"): States.
+* installation: Installing send-pr.
+* installation procedure: installation.
+* interactive interface: send-pr in Emacs.
+* Internet standard RFC-822: Mail header fields.
+* introduction to send-pr: Top.
+* invalid Problem Reports: using send-pr.
+* invoking send-pr from Emacs: send-pr in Emacs.
+* invoking send-pr from the shell: send-pr from the shell.
+* invoking send-pr: Invoking send-pr.
+* kinds of helpful information: Helpful hints.
+* life-cycle of a Problem Report: States.
+* listing valid categories: send-pr from the shell.
+* mail header fields: Mail header fields.
+* mail header section: using send-pr.
+* name completion in Emacs: send-pr in Emacs.
+* other mail: using send-pr.
+* other mail: Problem Report fields.
+* overview to send-pr: Top.
+* PR confidentiality: Problem Report fields.
+* Problem Report data types: Fields.
+* Problem Report format: Fields.
+* Problem Report states: States.
+* Problem Report template: Fields.
+* Problem Reports: send-pr in detail.
+* related mail: Problem Report fields.
+* related mail: using send-pr.
+* Report all the facts!: Helpful hints.
+* sample Problem Report: Fields.
+* saving related mail: Problem Report fields.
+* saving related mail: using send-pr.
+* sending PRs: Invoking send-pr.
+* setting a default SITE: default site.
+* shell invocation: send-pr from the shell.
+* state change example: An Example.
+* state--"analyzed": States.
+* state--"closed": States.
+* state--"feedback": States.
+* state--"open": States.
+* state--"suspended": States.
+* states of Problem Reports: States.
+* subsequent mail: Problem Report fields.
+* subsequent mail: using send-pr.
+* template: using send-pr.
+* template comment section: using send-pr.
+* using send-pr from within Emacs: send-pr in Emacs.
+* Using and Porting GNU CC: Helpful hints.
+* using send-pr: Invoking send-pr.
+* valid categories: Valid Categories.
+
+
+
+Tag Table:
+Node: Top827
+Node: send-pr in detail2851
+Node: States3690
+Node: Fields5122
+Node: Mail header fields8791
+Node: Problem Report fields9656
+Node: Invoking send-pr17045
+Node: using send-pr17502
+Node: send-pr in Emacs24509
+Node: send-pr from the shell28914
+Node: Helpful hints31261
+Node: An Example34366
+Node: Valid Categories43466
+Node: Installing send-pr45285
+Node: installation45852
+Node: default site49077
+Node: Index50334
+
+End Tag Table
diff --git a/gnu/usr.bin/send-pr/send-pr.sh b/gnu/usr.bin/send-pr/send-pr.sh
new file mode 100644
index 0000000..a707f46
--- /dev/null
+++ b/gnu/usr.bin/send-pr/send-pr.sh
@@ -0,0 +1,523 @@
+#!/bin/sh
+# Submit a problem report to a GNATS site.
+# Copyright (C) 1993 Free Software Foundation, Inc.
+# Contributed by Brendan Kehoe (brendan@cygnus.com), based on a
+# version written by Heinz G. Seidl (hgs@ide.com).
+#
+# This file is part of GNU GNATS.
+#
+# GNU GNATS is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU GNATS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU GNATS; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# The version of this send-pr.
+VERSION=3.2
+
+# The submitter-id for your site.
+SUBMITTER=unknown
+
+# Where the GNATS directory lives, if at all.
+[ -z "$GNATS_ROOT" ] &&
+GNATS_ROOT=
+
+# The default mail address for PR submissions.
+GNATS_ADDR=FreeBSD-gnats-submit@freefall.cdrom.com
+
+# Where the gnats category tree lives.
+DATADIR=@DATADIR@
+
+# If we've been moved around, try using GCC_EXEC_PREFIX.
+[ ! -d $DATADIR/gnats -a -d "$GCC_EXEC_PREFIX" ] && DATADIR=@DATADIR@
+
+# The default release for this host.
+DEFAULT_RELEASE="@DEFAULT_RELEASE@"
+
+# The default organization.
+DEFAULT_ORGANIZATION=
+
+# The default site to look for.
+GNATS_SITE=freefall
+
+# Newer config information?
+[ -f ${GNATS_ROOT}/gnats-adm/config ] && . ${GNATS_ROOT}/gnats-adm/config
+
+# What mailer to use. This must come after the config file, since it is
+# host-dependent.
+MAIL_AGENT="/usr/sbin/sendmail -oi -t"
+
+ECHON=bsd
+
+if [ $ECHON = bsd ] ; then
+ ECHON1="echo -n"
+ ECHON2=
+elif [ $ECHON = sysv ] ; then
+ ECHON1=echo
+ ECHON2='\c'
+else
+ ECHON1=echo
+ ECHON2=
+fi
+
+#
+
+[ -z "$TMPDIR" ] && TMPDIR=/tmp
+
+TEMP=$TMPDIR/p$$
+BAD=$TMPDIR/pbad$$
+REF=$TMPDIR/pf$$
+
+if [ -z "$LOGNAME" -a -n "$USER" ]; then
+ LOGNAME=$USER
+fi
+
+FROM="$LOGNAME"
+REPLY_TO="$LOGNAME"
+
+# Find out the name of the originator of this PR.
+if [ -n "$NAME" ]; then
+ ORIGINATOR="$NAME"
+elif [ -f $HOME/.fullname ]; then
+ ORIGINATOR="`sed -e '1q' $HOME/.fullname`"
+elif [ -f /bin/domainname ]; then
+ if [ "`/bin/domainname`" != "" -a -f /usr/bin/ypcat ]; then
+ # Must use temp file due to incompatibilities in quoting behavior
+ # and to protect shell metacharacters in the expansion of $LOGNAME
+ /usr/bin/ypcat passwd 2>/dev/null | cat - /etc/passwd | grep "^$LOGNAME:" |
+ cut -f5 -d':' | sed -e 's/,.*//' > $TEMP
+ ORIGINATOR="`cat $TEMP`"
+ rm -f $TEMP
+ fi
+fi
+
+if [ "$ORIGINATOR" = "" ]; then
+ grep "^$LOGNAME:" /etc/passwd | cut -f5 -d':' | sed -e 's/,.*//' > $TEMP
+ ORIGINATOR="`cat $TEMP`"
+ rm -f $TEMP
+fi
+
+if [ -n "$ORGANIZATION" ]; then
+ if [ -f "$ORGANIZATION" ]; then
+ ORGANIZATION="`cat $ORGANIZATION`"
+ fi
+else
+ if [ -n "$DEFAULT_ORGANIZATION" ]; then
+ ORGANIZATION="$DEFAULT_ORGANIZATION"
+ elif [ -f $HOME/.organization ]; then
+ ORGANIZATION="`cat $HOME/.organization`"
+ elif [ -f $HOME/.signature ]; then
+ ORGANIZATION="`cat $HOME/.signature`"
+ fi
+fi
+
+# If they don't have a preferred editor set, then use
+if [ -z "$VISUAL" ]; then
+ if [ -z "$EDITOR" ]; then
+ EDIT=vi
+ else
+ EDIT="$EDITOR"
+ fi
+else
+ EDIT="$VISUAL"
+fi
+
+# Find out some information.
+SYSTEM=`( [ -f /bin/uname ] && /bin/uname -a ) || \
+ ( [ -f /usr/bin/uname ] && /usr/bin/uname -a ) || echo ""`
+ARCH=`[ -f /bin/arch ] && /bin/arch`
+MACHINE=`[ -f /bin/machine ] && /bin/machine`
+
+COMMAND=`echo $0 | sed -e 's,.*/,,'`
+USAGE="Usage: $COMMAND [-PVL] [-t address] [-f filename] [--request-id]
+[--version]"
+REMOVE=
+BATCH=
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -r) ;; # Ignore for backward compat.
+ -t | --to) if [ $# -eq 1 ]; then echo "$USAGE"; exit 1; fi
+ shift ; GNATS_ADDR="$1"
+ EXPLICIT_GNATS_ADDR=true
+ ;;
+ -f | --file) if [ $# -eq 1 ]; then echo "$USAGE"; exit 1; fi
+ shift ; IN_FILE="$1"
+ if [ "$IN_FILE" != "-" -a ! -r "$IN_FILE" ]; then
+ echo "$COMMAND: cannot read $IN_FILE"
+ exit 1
+ fi
+ ;;
+ -b | --batch) BATCH=true ;;
+ -p | -P | --print) PRINT=true ;;
+ -L | --list) FORMAT=norm ;;
+ -l | -CL | --lisp) FORMAT=lisp ;;
+ --request-id) REQUEST_ID=true ;;
+ -h | --help) echo "$USAGE"; exit 0 ;;
+ -V | --version) echo "$VERSION"; exit 0 ;;
+ -*) echo "$USAGE" ; exit 1 ;;
+ *) if [ -z "$USER_GNATS_SITE" ]; then
+ if [ ! -r "$DATADIR/gnats/$1" ]; then
+ echo "$COMMAND: the GNATS site $1 does not have a categories list."
+ exit 1
+ else
+ # The site name is the alias they'll have to have created.
+ USER_GNATS_SITE=$1
+ fi
+ else
+ echo "$USAGE" ; exit 1
+ fi
+ ;;
+ esac
+ shift
+done
+
+if [ -n "$USER_GNATS_SITE" ]; then
+ GNATS_SITE=$USER_GNATS_SITE
+ GNATS_ADDR=$USER_GNATS_SITE-gnats
+fi
+
+if [ "$SUBMITTER" = "unknown" -a -z "$REQUEST_ID" -a -z "$IN_FILE" ]; then
+ cat << '__EOF__'
+It seems that send-pr is not installed with your unique submitter-id.
+You need to run
+
+ install-sid YOUR-SID
+
+where YOUR-SID is the identification code you received with `send-pr'.
+`send-pr' will automatically insert this value into the template field
+`>Submitter-Id'. If you've downloaded `send-pr' from the Net, use `net'
+for this value. If you do not know your id, run `send-pr --request-id' to
+get one from your support site.
+__EOF__
+ exit 1
+fi
+
+if [ -r "$DATADIR/gnats/$GNATS_SITE" ]; then
+ CATEGORIES=`grep -v '^#' $DATADIR/gnats/$GNATS_SITE | sort`
+else
+ echo "$COMMAND: could not read $DATADIR/gnats/$GNATS_SITE for categories list."
+ exit 1
+fi
+
+if [ -z "$CATEGORIES" ]; then
+ echo "$COMMAND: the categories list for $GNATS_SITE was empty!"
+ exit 1
+fi
+
+case "$FORMAT" in
+ lisp) echo "$CATEGORIES" | \
+ awk 'BEGIN {printf "( "} {printf "(\"%s\") ",$0} END {printf ")\n"}'
+ exit 0
+ ;;
+ norm) l=`echo "$CATEGORIES" | \
+ awk 'BEGIN {max = 0; } { if (length($0) > max) { max = length($0); } }
+ END {print max + 1;}'`
+ c=`expr 70 / $l`
+ if [ $c -eq 0 ]; then c=1; fi
+ echo "$CATEGORIES" | \
+ awk 'BEGIN {print "Known categories:"; i = 0 }
+ { printf ("%-'$l'.'$l's", $0); if ((++i % '$c') == 0) { print "" } }
+ END { print ""; }'
+ exit 0
+ ;;
+esac
+
+ORIGINATOR_C='<Name of the PR author (one line)>'
+ORGANIZATION_C='<Organization of PR author (multiple lines)>'
+CONFIDENTIAL_C='<[ yes | no ] (one line)>'
+SYNOPSIS_C='<Synopsis of the problem (one line)>'
+SEVERITY_C='<[ non-critical | serious | critical ] (one line)>'
+PRIORITY_C='<[ low | medium | high ] (one line)>'
+CATEGORY_C='<Problem category (as listed above)>'
+CLASS_C='<[ sw-bug | doc-bug | change-request | support ] (one line)>'
+RELEASE_C='<Release number or tag (one line)>'
+ENVIRONMENT_C='<Relevant environment information (multiple lines)>'
+DESCRIPTION_C='<Precise description of the problem (multiple lines)>'
+HOW_TO_REPEAT_C='<Code/input/activities to reproduce the problem (multiple lines)>'
+FIX_C='<How to correct or work around the problem, if known (multiple lines)>'
+
+# Catch some signals. ($xs kludge needed by Sun /bin/sh)
+xs=0
+trap 'rm -f $REF $TEMP; exit $xs' 0
+trap 'echo "$COMMAND: Aborting ..."; rm -f $REF $TEMP; xs=1; exit' 1 2 3 13 15
+
+# If they told us to use a specific file, then do so.
+if [ -n "$IN_FILE" ]; then
+ if [ "$IN_FILE" = "-" ]; then
+ # The PR is coming from the standard input.
+ if [ -n "$EXPLICIT_GNATS_ADDR" ]; then
+ sed -e "s;^[Tt][Oo]:.*;To: $GNATS_ADDR;" > $TEMP
+ else
+ cat > $TEMP
+ fi
+ else
+ # Use the file they named.
+ if [ -n "$EXPLICIT_GNATS_ADDR" ]; then
+ sed -e "s;^[Tt][Oo]:.*;To: $GNATS_ADDR;" $IN_FILE > $TEMP
+ else
+ cat $IN_FILE > $TEMP
+ fi
+ fi
+else
+
+ if [ -n "$PR_FORM" -a -z "$PRINT_INTERN" ]; then
+ # If their PR_FORM points to a bogus entry, then bail.
+ if [ ! -f "$PR_FORM" -o ! -r "$PR_FORM" -o ! -s "$PR_FORM" ]; then
+ echo "$COMMAND: can't seem to read your template file (\`$PR_FORM'), ignoring PR_FORM"
+ sleep 1
+ PRINT_INTERN=bad_prform
+ fi
+ fi
+
+ if [ -n "$PR_FORM" -a -z "$PRINT_INTERN" ]; then
+ cp $PR_FORM $TEMP ||
+ ( echo "$COMMAND: could not copy $PR_FORM" ; xs=1; exit )
+ else
+ for file in $TEMP $REF ; do
+ cat > $file << '__EOF__'
+SEND-PR: -*- send-pr -*-
+SEND-PR: Lines starting with `SEND-PR' will be removed automatically, as
+SEND-PR: will all comments (text enclosed in `<' and `>').
+SEND-PR:
+SEND-PR: Please consult the send-pr man page `send-pr(1)' or the Texinfo
+SEND-PR: manual if you are not sure how to fill out a problem report.
+SEND-PR:
+SEND-PR: Choose from the following categories:
+SEND-PR:
+__EOF__
+
+ # Format the categories so they fit onto lines.
+ l=`echo "$CATEGORIES" | \
+ awk 'BEGIN {max = 0; } { if (length($0) > max) { max = length($0); } }
+ END {print max + 1;}'`
+ c=`expr 61 / $l`
+ if [ $c -eq 0 ]; then c=1; fi
+ echo "$CATEGORIES" | \
+ awk 'BEGIN {printf "SEND-PR: "; i = 0 }
+ { printf ("%-'$l'.'$l's", $0);
+ if ((++i % '$c') == 0) { printf "\nSEND-PR: " } }
+ END { printf "\nSEND-PR:\n"; }' >> $file
+
+
+
+ cat >> $file << __EOF__
+To: $GNATS_ADDR
+Subject:
+From: $FROM
+Reply-To: $REPLY_TO
+X-send-pr-version: $VERSION
+
+
+>Submitter-Id: $SUBMITTER
+>Originator: $ORIGINATOR
+>Organization: `if [ -n "$ORGANIZATION" ]; then
+ echo "$ORGANIZATION"
+ else
+ echo "$ORGANIZATION_C" ;
+ fi ; `
+>Confidential: $CONFIDENTIAL_C
+>Synopsis: $SYNOPSIS_C
+>Severity: $SEVERITY_C
+>Priority: $PRIORITY_C
+>Category: $CATEGORY_C
+>Release: `if [ -n "$DEFAULT_RELEASE" ]; then
+ echo "$DEFAULT_RELEASE"
+ else
+ echo "$RELEASE_C"
+ fi ; `
+>Class: $CLASS_C
+>Environment:
+
+ $ENVIRONMENT_C
+
+>Description:
+
+ $DESCRIPTION_C
+
+>How-To-Repeat:
+
+ $HOW_TO_REPEAT_C
+
+>Fix:
+
+ $FIX_C
+
+__EOF__
+ done
+ fi
+
+ if [ "$PRINT" = true -o "$PRINT_INTERN" = true ]; then
+ cat $TEMP
+ xs=0; exit
+ fi
+
+ chmod u+w $TEMP
+ if [ -z "$REQUEST_ID" ]; then
+ eval $EDIT $TEMP
+ else
+ ed -s $TEMP << '__EOF__'
+/^Subject/s/^Subject:.*/Subject: request for a customer id/
+/^>Category/s/^>Category:.*/>Category: send-pr/
+w
+q
+__EOF__
+ fi
+
+ if cmp -s $REF $TEMP ; then
+ echo "$COMMAND: problem report not filled out, therefore not sent"
+ xs=1; exit
+ fi
+fi
+
+#
+# Check the enumeration fields
+
+# This is a "sed-subroutine" with one keyword parameter
+# (with workaround for Sun sed bug)
+#
+SED_CMD='
+/$PATTERN/{
+s|||
+s|<.*>||
+s|^[ ]*||
+s|[ ]*$||
+p
+q
+}'
+
+
+while [ -z "$REQUEST_ID" ]; do
+ CNT=0
+
+ # 1) Confidential
+ #
+ PATTERN=">Confidential:"
+ CONFIDENTIAL=`eval sed -n -e "\"$SED_CMD\"" $TEMP`
+ case "$CONFIDENTIAL" in
+ ""|yes|no) CNT=`expr $CNT + 1` ;;
+ *) echo "$COMMAND: \`$CONFIDENTIAL' is not a valid value for \`Confidential'." ;;
+ esac
+ #
+ # 2) Severity
+ #
+ PATTERN=">Severity:"
+ SEVERITY=`eval sed -n -e "\"$SED_CMD\"" $TEMP`
+ case "$SEVERITY" in
+ ""|non-critical|serious|critical) CNT=`expr $CNT + 1` ;;
+ *) echo "$COMMAND: \`$SEVERITY' is not a valid value for \`Severity'."
+ esac
+ #
+ # 3) Priority
+ #
+ PATTERN=">Priority:"
+ PRIORITY=`eval sed -n -e "\"$SED_CMD\"" $TEMP`
+ case "$PRIORITY" in
+ ""|low|medium|high) CNT=`expr $CNT + 1` ;;
+ *) echo "$COMMAND: \`$PRIORITY' is not a valid value for \`Priority'."
+ esac
+ #
+ # 4) Category
+ #
+ PATTERN=">Category:"
+ CATEGORY=`eval sed -n -e "\"$SED_CMD\"" $TEMP`
+ FOUND=
+ for C in $CATEGORIES
+ do
+ if [ "$C" = "$CATEGORY" ]; then FOUND=true ; break ; fi
+ done
+ if [ -n "$FOUND" ]; then
+ CNT=`expr $CNT + 1`
+ else
+ if [ -z "$CATEGORY" ]; then
+ echo "$COMMAND: you must include a Category: field in your report."
+ else
+ echo "$COMMAND: \`$CATEGORY' is not a known category."
+ fi
+ fi
+ #
+ # 5) Class
+ #
+ PATTERN=">Class:"
+ CLASS=`eval sed -n -e "\"$SED_CMD\"" $TEMP`
+ case "$CLASS" in
+ ""|sw-bug|doc-bug|change-request|support) CNT=`expr $CNT + 1` ;;
+ *) echo "$COMMAND: \`$CLASS' is not a valid value for \`Class'."
+ esac
+
+ [ $CNT -lt 5 -a -z "$BATCH" ] &&
+ echo "Errors were found with the problem report."
+
+ while true; do
+ if [ -z "$BATCH" ]; then
+ $ECHON1 "a)bort, e)dit or s)end? $ECHON2"
+ read input
+ else
+ if [ $CNT -eq 5 ]; then
+ input=s
+ else
+ input=a
+ fi
+ fi
+ case "$input" in
+ a*)
+ if [ -z "$BATCH" ]; then
+ echo "$COMMAND: the problem report remains in $BAD and is not sent."
+ mv $TEMP $BAD
+ else
+ echo "$COMMAND: the problem report is not sent."
+ fi
+ xs=1; exit
+ ;;
+ e*)
+ eval $EDIT $TEMP
+ continue 2
+ ;;
+ s*)
+ break 2
+ ;;
+ esac
+ done
+done
+#
+# Remove comments and send the problem report
+# (we have to use patterns, where the comment contains regex chars)
+#
+# /^>Originator:/s;$ORIGINATOR;;
+sed -e "
+/^SEND-PR:/d
+/^>Organization:/,/^>[A-Za-z-]*:/s;$ORGANIZATION_C;;
+/^>Confidential:/s;<.*>;;
+/^>Synopsis:/s;$SYNOPSIS_C;;
+/^>Severity:/s;<.*>;;
+/^>Priority:/s;<.*>;;
+/^>Category:/s;$CATEGORY_C;;
+/^>Class:/s;<.*>;;
+/^>Release:/,/^>[A-Za-z-]*:/s;$RELEASE_C;;
+/^>Environment:/,/^>[A-Za-z-]*:/s;$ENVIRONMENT_C;;
+/^>Description:/,/^>[A-Za-z-]*:/s;$DESCRIPTION_C;;
+/^>How-To-Repeat:/,/^>[A-Za-z-]*:/s;$HOW_TO_REPEAT_C;;
+/^>Fix:/,/^>[A-Za-z-]*:/s;$FIX_C;;
+" $TEMP > $REF
+
+if $MAIL_AGENT < $REF; then
+ echo "$COMMAND: problem report sent"
+ xs=0; exit
+else
+ echo "$COMMAND: mysterious mail failure."
+ if [ -z "$BATCH" ]; then
+ echo "$COMMAND: the problem report remains in $BAD and is not sent."
+ mv $REF $BAD
+ else
+ echo "$COMMAND: the problem report is not sent."
+ fi
+ xs=1; exit
+fi
diff --git a/gnu/usr.bin/send-pr/send-pr.texi b/gnu/usr.bin/send-pr/send-pr.texi
new file mode 100644
index 0000000..b9edd9c
--- /dev/null
+++ b/gnu/usr.bin/send-pr/send-pr.texi
@@ -0,0 +1,657 @@
+\input texinfo @c -*-texinfo-*-
+@setfilename send-pr.info
+@settitle Reporting Problems Using send-pr
+
+@setchapternewpage odd
+
+@include version.texi
+@set SENDPR
+
+@ifinfo
+@format
+START-INFO-DIR-ENTRY
+* send-pr:: Reporting problems--using send-pr
+END-INFO-DIR-ENTRY
+@end format
+@end ifinfo
+
+@ifinfo
+Copyright @copyright{} 1993 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+@ignore
+Permission is granted to process this file through TeX and print the
+results, provided the printed document carries a copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+@end ignore
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided also that
+the entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions.
+@end ifinfo
+
+@titlepage
+@finalout
+@title Reporting Problems
+@subtitle Using @code{send-pr}, version @value{VERSION}
+@subtitle October 1993
+@author Jeffrey M. Osier
+@author Cygnus Support
+@page
+
+@vskip 0pt plus 1filll
+
+Copyright @copyright{} 1993 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided also that
+the entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions.
+
+@end titlepage
+
+@c ---------------------------------------------------------------
+@node Top
+@top Overview
+@cindex foreword to @code{send-pr}
+@cindex overview to @code{send-pr}
+@cindex introduction to @code{send-pr}
+
+This manual documents @code{send-pr},
+@ifinfo
+version @value{VERSION},
+@end ifinfo
+which uses electronic mail to submit support questions and problem
+reports to a central Support Site. No body of work is perfect, and
+support organizations understand this; @code{send-pr} is designed to
+allow users who have problems to submit reports of these problems to
+sites responsible for supporting the products in question, in a defined
+form which can be read by an electronically managed database.
+
+@cindex GNATS
+@code{send-pr} is part of a suite of programs known collectively as
+@sc{gnats}, the @sc{gnu} Problem Report Management System. @sc{gnats}
+consists of several programs which, used in concert, formulate and
+partially administer a database of @dfn{Problem Reports}, or @dfn{PRs},
+at a central Support Site. A PR goes through several states in its
+lifetime; @sc{gnats} tracks the PR and all information associated with it
+through each state and finally acts as an archive for PRs which have
+been @dfn{closed}.
+
+Because @code{send-pr} exists as a shell (@file{/bin/sh}) script and as
+an Elisp file for use with @sc{gnu} Emacs, it can be used from any
+machine on your network which can run a shell script and/or Emacs.
+
+In general, you can use any editor and mailer to submit valid Problem
+Reports, as long as the format required by @sc{gnats} is preserved.
+@code{send-pr} automates the process, however, and ensures that certain
+fields necessary for automatic processing are present. @code{send-pr}
+is strongly recommended for all initial problem-oriented correspondence
+with your Support Site. The organization you submit Problem Reports to
+supplies an address to which further information can be sent; the person
+responsible for the category of the problem you report contacts you
+directly.
+
+@menu
+* send-pr in detail:: Details about send-pr and GNATS
+* Invoking send-pr:: Editing and sending PRs
+* An Example:: A working example
+* Installing send-pr:: Installing send-pr on your system
+* Index::
+@end menu
+
+@node send-pr in detail
+@chapter Details about send-pr and GNATS
+
+@cindex details about @code{send-pr}
+@cindex Problem Reports
+A @dfn{Problem Report} is a message that describes a problem you are
+having with a body of work. @code{send-pr} organizes this message into
+a form which can be understood and automatically processed by @sc{gnats},
+the @sc{gnu} Problem Report Management System. A Problem Report is
+organized into @dfn{fields} which contain data describing you, your
+organization, and the problem you are announcing (@pxref{Fields,,Problem
+Report format}). Problem Reports go through several defined states in
+their lifetimes, from @dfn{open} to @dfn{closed} (@pxref{States,,States
+of Problem Reports}).
+
+@menu
+* States:: States of Problem Reports
+* Fields:: Problem Report format
+@end menu
+
+@include states.texi
+
+@include fields.texi
+
+@node Invoking send-pr
+@chapter Editing and sending PRs
+@cindex editing and sending PRs
+@cindex sending PRs
+@cindex invoking send-pr
+@cindex using send-pr
+@cindex generating new PRs
+
+@include s-usage.texi
+
+@node An Example
+@chapter An Example
+@cindex an example
+@cindex example PR
+@cindex Cygnus Support
+@cindex GNU software support
+
+Cygnus Support in Mountain View, CA, uses @sc{gnats} and @code{send-pr}
+extensively for their support activities. As a support company, Cygnus
+finds problem tracking to be a crucial part of everyday business.
+Cygnus supports the @sc{gnu} compiling tools (including @sc{gnats} and
+@code{send-pr}) over several many platforms
+
+With each shipment of the Cygnus Support Developer's Kit, customers
+receive the latest version of @code{send-pr}, which contains an
+up-to-date listing of valid categories (values for the @code{>Category:}
+field). Using these tools, Cygnus' customers can communicate their
+problems to Cygnus effectively and receive automatic confirmation of
+receipt as well as notification of changes in the status of their
+reported problems. Much of Cygnus' support mechanism relies on
+electronic mail.
+
+As an example, let's pretend we're a customer of Cygnus Support, and
+that we're having a problem compiling some of our software using the
+@sc{gnu} C compiler, which Cygnus supports.
+
+Assume that we're getting an error in our @code{bifrabulator} program
+wherein the @samp{prestidigitation} routines don't match with the
+@samp{whatsitsname}. We've made sure we're following the rules of the
+program and checked the Release Notes from Cygnus and found that the bug
+isn't already known. In other words, we're pretty sure we've found a
+bug.
+
+@cindex Imaginary Software, Ltd.
+Our first step is to call @code{send-pr}. It really doesn't matter
+whether we use @code{send-pr} from the shell or from within Emacs.
+Indeed, if we use Emacs as a primary editor, calling @code{send-pr} from
+the shell is likely to start @code{send-pr} in an Emacs buffer anyway.
+So, since our company, @emph{Imaginary Software, Ltd.}, uses @sc{gnu}
+software extensively, we're pretty familiar with Emacs, so from within
+Emacs we type
+@smallexample
+M-x send-pr
+@end smallexample
+@noindent
+and we're greeted with the following screen:
+
+@cindex default PR template
+@cindex example of a default template
+@cindex blank PR template
+@cindex @code{bifrabulator}
+@cartouche
+@smallexample
+SEND-PR: -*- text -*-
+SEND-PR: Lines starting with `SEND-PR' will be removed
+SEND-PR: automatically as well as all comments (the text
+SEND-PR: below enclosed in `<' and `>').
+SEND-PR: Please consult the manual if you are not sure
+SEND-PR: how to fill out a problem report.
+SEND-PR:
+SEND-PR: Choose from the following categories:
+SEND-PR:
+SEND-PR: bfd binutils bison
+SEND-PR: byacc clib config cvs diff
+SEND-PR: doc emacs flex g++ gas
+SEND-PR: gcc gdb glob gprof grep
+SEND-PR: info ispell kerberos ld libg++
+SEND-PR: libiberty make makeinfo mas newlib
+SEND-PR: other patch rcs readline send-pr
+SEND-PR: test texindex texinfo texinfo.tex
+SEND-PR: bifrabulator <---@emph{note: this one is fake}
+SEND-PR:
+To: cygnus-bugs@@cygnus.com
+Subject:
+From: jeffrey@@imaginary.com
+Reply-To: jeffrey@@imaginary.com
+X-send-pr-version: send-pr @value{VERSION}
+
+>Submitter-Id: imaginary
+>Originator: Jeffrey Osier
+>Organization:
+Imaginary Software, Ltd.
+>Confidential: <[ yes | no ] (one line)>
+>Synopsis: <synopsis of the problem (one line)>
+>Severity: <[ non-critical | serious | critical ] (one line)>
+>Priority: <[ low | medium | high ] (one line)>
+>Category: <name of the product (one line)>
+>Class: <[sw-bug|doc-bug|change-request|support](oneline)>
+>Release: <release number or tag (one line)>
+>Environment:
+ <machine, os, target, libraries (multiple lines)>
+System: SunOS imaginary.com 4.1.1 1 sun4
+Architecture: sun4
+
+>Description:
+ <precise description of the problem (multiple lines)>
+>How-To-Repeat:
+ <code/input/activities to reproduce (multiple lines)>
+>Fix:
+@iftex
+@hrule
+@end iftex
+-----Emacs: *send-pr* (send-pr Fill)----All------------------
+@iftex
+@hrule
+@end iftex
+>Category: other[]
+@end smallexample
+@end cartouche
+@page
+We know from past experience that we need to set certain information into
+each field, so we compile all the information we know about our problem.
+We have some sample code which we know should work, even though it
+doesn't, so we'll include that. Below is the completed PR; we send this
+using @kbd{C-c C-c}. (The comments have been truncated).
+
+@cindex completed Problem Report
+@cindex example of a completed PR
+@cartouche
+@smallexample
+SEND-PR: Lines starting with `SEND-PR' will be removed
+SEND-PR: automatically as well as all comments (the text
+SEND-PR: @dots{}
+SEND-PR:
+To: cygnus-bugs@@cygnus.com
+Subject: bifrabulator routines don't match
+From: jeffrey@@imaginary.com
+Reply-To: jeffrey@@imaginary.com
+X-send-pr-version: send-pr @value{VERSION}
+
+>Submitter-Id: imaginary
+>Originator: Jeffrey Osier
+>Organization:
+Imaginary Software, Ltd.
+>Confidential: no
+>Synopsis: bifrabulator routines don't match
+>Severity: serious
+>Priority: medium
+>Category: bifrabulator
+>Class: sw-bug
+>Release: progressive-930101
+>Environment:
+System: SunOS imaginary.com 4.1.1 1 sun4
+Architecture: sun4 (SPARC)
+
+>Description:
+ the following code I fed into the bifrabulator came back
+ with a strange error. apparently, the prestidigitation
+ routine doesn't match with the whatsitsname in all cases.
+
+>How-To-Repeat:
+ call the bifrabulator on the following code.
+ @emph{code sample@dots{}}
+
+>Fix:
+@iftex
+@hrule
+@end iftex
+-----Emacs: *send-pr* (send-pr Fill)----All------------------
+@iftex
+@hrule
+@end iftex
+To send the problem report use: C-c C-c
+@end smallexample
+@end cartouche
+
+We type @kbd{C-c C-c}, and off it goes. Now, we depend on Cygnus
+Support to figure out the answer to our problem.
+
+Soon afterward, we get the following message from Cygnus:
+
+@smallexample
+@group
+From: gnats (GNATS management)
+Sender: gnats-admin
+Reply-To: hacker@@cygnus.com
+To: jeffrey@@imaginary.com
+Subject: Re: bifrabulator/1425: routines don't match
+
+Thank you very much for your problem report.
+It has the internal identification: g++/1425.
+The individual assigned to look at your bug is: hacker
+(F.B. Hacker)
+
+Category: bifrabulator
+Responsible: hacker
+Synopsis: bifrabulator routines don't match
+Arrival-Date: Sat Feb 30 03:12:55 1993
+@end group
+@end smallexample
+
+@noindent
+This is our receipt that the bug has been accepted and forwarded to the
+responsible party.
+
+@noindent
+A while later, we get the analysis:
+
+@smallexample
+@group
+To: jeffrey@@imaginary.com
+From: hacker@@cygnus.com
+Subject: Re: bifrabulator/1425: routines don't match
+Reply-To: hacker@@cygnus.com
+
+Got your message, Jeff. It seems that the bifrabulator was
+confusing the prestidigitation routines with the realitychecker
+when lexically parsing the whatsitsname.
+
+I'm working on robustisizing the bifrabulator now.
+
+How about lunch next week?
+--
+F.B. Hacker
+Cygnus Support, Mountain View, CA 415 903 1400
+#include <std-disclaimer.h>
+@end group
+@end smallexample
+
+@noindent
+About the same time, we get another message from Cygnus.
+
+@cindex state change example
+@cindex example of a state change
+@smallexample
+@group
+From: hacker@@cygnus.com
+To: jeffrey@@imaginary.com
+Subject: Re: bifrabulator/1425: doesn't match prestidig
+Reply-To: hacker@@cygnus.com
+
+
+ `F.B. Hacker' changed the state to `analyzed'.
+
+State-Changed-From-To: open-analyzed
+State-Changed-By: hacker
+State-Changed-When: Fri Feb 31 1993 08:59:16 1993
+State-Changed-Why:
+ figured out the problem, working on a patch this afternoon
+--
+F.B. Hacker
+Cygnus Support, Mountain View, CA 415 903 1400
+#include <std-disclaimer.h>
+@end group
+@end smallexample
+
+@noindent
+The bug has now been analyzed, and Cygnus is working on a solution.
+
+@noindent
+Sometime later, we get more mail from F.B.:
+
+@smallexample
+@group
+To: jeffrey@@imaginary.com
+From: hacker@@cygnus.com
+Subject: Re: bifrabulator/1425: routines don't match
+Reply-To: hacker@@cygnus.com
+
+There's a patch now that you can ftp over and check out.
+
+Hey, that joke you sent me was great! The one about the
+strings walking into a bar... my boss laughed for an hour!
+--
+F.B. Hacker
+Cygnus Support, Mountain View, CA 415 903 1400
+#include <std-disclaimer.h>
+@end group
+@end smallexample
+@sp 2
+@smallexample
+@group
+From: hacker@@cygnus.com
+To: jeffrey@@imaginary.com
+Subject: Re: bifrabulator/1425: doesn't match prestidig
+Reply-To: hacker@@cygnus.com
+
+
+ `F.B. Hacker' changed the state to `feedback'.
+
+State-Changed-From-To: analyzed-feedback
+State-Changed-By: hacker
+State-Changed-When: Fri Feb 31 1993 23:43:16 1993
+State-Changed-Why:
+ got the patch finished, notified Jeff at Imaginary Software
+--
+F.B. Hacker
+Cygnus Support, Mountain View, CA 415 903 1400
+#include <std-disclaimer.h>
+@end group
+@end smallexample
+
+@noindent
+The bug has gone into @dfn{feedback} status now, until we get the patch,
+install it and test it. When everything tests well, we can mail F.B.
+back and tell him the bug's been fixed, and he can change the state of
+the PR from @dfn{feedback} to @dfn{closed}.
+
+Following is a list of valid @samp{>Category:} entries that are
+supported by Cygnus.
+
+@menu
+* Valid Categories::
+@end menu
+
+@c FIXME - is this list up to date?
+@include categ.texi
+
+@node Installing send-pr
+@appendix Installing @code{send-pr} on your system
+@cindex installation
+
+If you receive @code{send-pr} as part of a larger software distribution,
+it probably gets installed when the full distribution is installed. If
+you are using @sc{gnats} at your site as well, you must decide where
+@code{send-pr} sends Problem Reports by default; see @ref{default site,,
+Setting a default @var{site}}.
+
+@menu
+* installation:: installing `send-pr' by itself
+* default site:: setting a default site
+@end menu
+
+@node installation
+@section Installing @code{send-pr} by itself
+@cindex installation procedure
+
+Install @code{send-pr} by following these steps (you may need
+@code{root} access in order to change the @file{aliases} file and to
+install @code{send-pr}):
+
+@itemize @bullet
+@item
+Unpack the distribution into a directory which we refer to as
+@var{srcdir}.
+
+@item
+Edit the file @file{Makefile} to reflect local conventions.
+Specifically, you should edit the variable @samp{prefix} to alter the
+installation location. The default is @file{/usr/local}. All files are
+installed under @samp{prefix} (see below).
+
+@item @emph{Run}
+@smallexample
+make all install [ info ] [ install-info ] [ clean ]
+@end smallexample
+
+@noindent
+The targets mean the following:
+
+@table @code
+@item all
+Builds @code{send-pr} and @code{install-sid}
+
+@item install
+Installs the following:
+
+@table @code
+@item install-sid
+@itemx send-pr
+into @file{@var{prefix}/bin}
+
+@item send-pr.1
+into @file{@var{prefix}/man/man1}
+
+@item @var{site}
+the list of valid @var{categories} for the Support Site from which you
+received @code{send-pr}, installed as
+@w{@file{@var{prefix}/lib/gnats/@var{site}}}
+
+@item send-pr.el
+into @w{@file{@var{prefix}/lib/emacs/lisp}}@footnote{If your main Emacs
+lisp repository is in a different directory from this, substitute that
+directory for @w{@file{@var{prefix}/lib/emacs/lisp}}.}
+@end table
+
+@item info (@emph{optional})
+Builds @file{send-pr.info} from @file{send-pr.texi}@*
+@c FIXME - is this still true?
+(@file{send-pr.info} is included with this distribution)
+
+@item install-info (@emph{optional})
+Installs @file{send-pr.info} into @w{@file{@var{prefix}/info}}
+
+@item clean (@emph{optional})
+Removes all intermediary build files that can be rebuilt from source
+code
+@end table
+
+@item
+Run
+
+@smallexample
+install-sid @var{your-sid}
+@end smallexample
+
+@noindent
+where @var{your-sid} is the identification code you received with
+@w{@code{send-pr}}. @code{send-pr} automatically inserts this value
+into the template field @samp{>Submitter-Id:}. If you've downloaded
+@code{send-pr} from the Net, use @samp{net} for this value.
+
+@item
+Place the following line in
+@w{@file{@var{prefix}/lib/emacs/lisp/default.el}}, or instruct your
+users to place the following line in their @file{.emacs} files:
+
+@smallexample
+(autoload 'send-pr "send-pr" "Submit a Problem Report." t)
+@end smallexample
+
+@item
+Create a mail alias for the Support Site from which you received
+@code{send-pr}, and for every site with which you wish to use
+@code{send-pr} to communicate. Each alias must have a suffix of
+@samp{-gnats}. The Support Site(s) will provide the correct addresses
+where these aliases should point. For instance, edit your mail aliases
+file to contain something like:
+
+@smallexample
+# support sites; for use with send-pr
+cygnus-gnats: bugs@@cygnus.com # Cygnus Support
+bumblebee-gnats: bumblebugs@@bumblebee.com # Bumblebee Inc.
+mycompany-gnats: bugs@@my.company.com (@emph{if you use @sc{gnats} locally})
+@end smallexample
+
+@code{send-pr} automatically searches for these aliases when you type
+
+@smallexample
+send-pr cygnus
+send-pr bumblebee
+send-pr @var{site}@dots{}
+@end smallexample
+
+@noindent
+@code{send-pr} also uses @var{site} to determine the categories of
+problems accepted by the site in question by looking in
+
+@smallexample
+@var{prefix}/lib/gnats/@var{site}
+@end smallexample
+
+@end itemize
+
+@node default site
+@section Setting a default @var{site}
+@cindex default @var{site}
+@cindex setting a default @var{site}
+
+@code{send-pr} is capable of sending Problem Reports to any number of
+Support Sites, using mail aliases which have @samp{-gnats} appended them.
+@code{send-pr} automatically appends the suffix, so that when you type
+
+@smallexample
+send-pr @var{site}
+@end smallexample
+
+@noindent
+the Problem Report goes to the address noted in the @file{aliases} file
+as @w{@samp{@var{site}-gnats}}. You can do this in the Emacs version of
+@code{send-pr} by invoking the program with
+
+@smallexample
+C-u M-x send-pr
+@end smallexample
+
+@noindent
+You are prompted for @var{site}.
+
+@var{site} is also used to error-check the @samp{>Category:} field, as a
+precaution against sending mistaken information (and against sending
+information to the wrong site).
+
+You may also simply type
+
+@smallexample
+send-pr
+@end smallexample
+
+@noindent
+from the shell (or @w{@samp{M-x send-pr}} in Emacs), and the Problem
+Report you generate will be sent to the @var{site}, which is usually the
+site from which you received your distribution of @w{@code{send-pr}}.
+If you use @sc{gnats} at your own organization, the default is usually
+your local address for reporting problems.
+
+To change this, simply edit the file @file{Makefile} before installing
+and change the line
+
+@smallexample
+GNATS_SITE = @var{site}
+@end smallexample
+
+@noindent
+to reflect the site where you wish to send PRs by default.
+
+@c ---------------------------------------------------------------
+@node Index
+@unnumbered Index
+
+@printindex cp
+
+@c ---------------------------------------------------------------
+@contents
+@bye
diff --git a/gnu/usr.bin/sort/COPYING b/gnu/usr.bin/sort/COPYING
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/gnu/usr.bin/sort/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/gnu/usr.bin/sort/Makefile b/gnu/usr.bin/sort/Makefile
index 00d9276..f4e2f88 100644
--- a/gnu/usr.bin/sort/Makefile
+++ b/gnu/usr.bin/sort/Makefile
@@ -1,5 +1,11 @@
-
PROG= sort
-SRCS= sort.c error.c
+SRCS= sort.c error.c version.c long-options.c getopt.c getopt1.c
+
+CFLAGS+=-I${.CURDIR} -DDIRENT=1 -DHAVE_LONG_DOUBLE=1 -DHAVE_LONG_DOUBLE=1 \
+ -DHAVE_ST_BLKSIZE=1 -DHAVE_VPRINTF=1 -DRETSIGTYPE=void \
+ -DSTDC_HEADERS=1 -DDHAVE_STRERROR=1 -DHAVE_FCNTL_H=1 \
+ -DHAVE_LIMITS_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRING_H=1 \
+ -DHAVE_UNISTD_H=1
+
.include <bsd.prog.mk>
diff --git a/gnu/usr.bin/sort/error.c b/gnu/usr.bin/sort/error.c
new file mode 100644
index 0000000..611f220
--- /dev/null
+++ b/gnu/usr.bin/sort/error.c
@@ -0,0 +1,116 @@
+/* error.c -- error handler for noninteractive utilities
+ Copyright (C) 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by David MacKenzie. */
+
+#ifdef HAVE_CONFIG_H
+#if defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
+#include <stdio.h>
+
+#ifdef HAVE_VPRINTF
+
+#if __STDC__
+#include <stdarg.h>
+#define VA_START(args, lastarg) va_start(args, lastarg)
+#else /* !__STDC__ */
+#include <varargs.h>
+#define VA_START(args, lastarg) va_start(args)
+#endif /* !__STDC__ */
+
+#else /* !HAVE_VPRINTF */
+
+#ifdef HAVE_DOPRNT
+#define va_alist args
+#define va_dcl int args;
+#else /* !HAVE_DOPRNT */
+#define va_alist a1, a2, a3, a4, a5, a6, a7, a8
+#define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
+#endif /* !HAVE_DOPRNT */
+
+#endif /* !HAVE_VPRINTF */
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#include <string.h>
+#else /* !STDC_HEADERS */
+void exit ();
+#endif /* !STDC_HEADERS */
+
+extern char *program_name;
+
+#ifndef HAVE_STRERROR
+static char *
+private_strerror (errnum)
+ int errnum;
+{
+ extern int sys_nerr;
+
+ if (errnum > 0 && errnum <= sys_nerr)
+ return sys_errlist[errnum];
+ return "Unknown system error";
+}
+#define strerror private_strerror
+#endif /* !HAVE_STRERROR */
+
+/* Print the program name and error message MESSAGE, which is a printf-style
+ format string with optional args.
+ If ERRNUM is nonzero, print its corresponding system error message.
+ Exit with status STATUS if it is nonzero. */
+/* VARARGS */
+void
+#if defined (HAVE_VPRINTF) && __STDC__
+error (int status, int errnum, char *message, ...)
+#else /* !HAVE_VPRINTF or !__STDC__ */
+error (status, errnum, message, va_alist)
+ int status;
+ int errnum;
+ char *message;
+ va_dcl
+#endif /* !HAVE_VPRINTF or !__STDC__ */
+{
+#ifdef HAVE_VPRINTF
+ va_list args;
+#endif /* HAVE_VPRINTF */
+
+ fprintf (stderr, "%s: ", program_name);
+#ifdef HAVE_VPRINTF
+ VA_START (args, message);
+ vfprintf (stderr, message, args);
+ va_end (args);
+#else /* !HAVE_VPRINTF */
+#ifdef HAVE_DOPRNT
+ _doprnt (message, &args, stderr);
+#else /* !HAVE_DOPRNT */
+ fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif /* !HAVE_DOPRNT */
+#endif /* !HAVE_VPRINTF */
+ if (errnum)
+ fprintf (stderr, ": %s", strerror (errnum));
+ putc ('\n', stderr);
+ fflush (stderr);
+ if (status)
+ exit (status);
+}
diff --git a/gnu/usr.bin/sort/getopt.c b/gnu/usr.bin/sort/getopt.c
new file mode 100644
index 0000000..7a4673b
--- /dev/null
+++ b/gnu/usr.bin/sort/getopt.c
@@ -0,0 +1,757 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef HAVE_CONFIG_H
+#if defined (emacs) || defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
+#ifndef __STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+#include <stdlib.h>
+#endif /* GNU C library. */
+
+/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
+ long-named option. Because this is not POSIX.2 compliant, it is
+ being phased out. */
+/* #define GETOPT_COMPAT */
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = 0;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* XXX 1003.2 says this must be 1 before any call. */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return EOF with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+#include <string.h>
+#define my_index strchr
+#else
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+char *getenv ();
+
+static char *
+my_index (str, chr)
+ const char *str;
+ int chr;
+{
+ while (*str)
+ {
+ if (*str == chr)
+ return (char *) str;
+ str++;
+ }
+ return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+ If not using GCC, it is ok not to declare it.
+ (Supposedly there are some machines where it might get a warning,
+ but changing this conditional to __STDC__ is too risky.) */
+#ifdef __GNUC__
+#ifdef IN_GCC
+#include "gstddef.h"
+#else
+#include <stddef.h>
+#endif
+extern size_t strlen (const char *);
+#endif
+
+#endif /* GNU C library. */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int bottom = first_nonopt;
+ int middle = last_nonopt;
+ int top = optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
+
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns `EOF'.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ int option_index;
+
+ optarg = 0;
+
+ /* Initialize the internal data when the first call is made.
+ Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ if (optind == 0)
+ {
+ first_nonopt = last_nonopt = optind = 1;
+
+ nextchar = NULL;
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (getenv ("POSIXLY_CORRECT") != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+ }
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Now skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc
+ && (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* Special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return EOF;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if ((argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ {
+ if (ordering == REQUIRE_ORDER)
+ return EOF;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Start decoding its characters. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ if (longopts != NULL
+ && ((argv[optind][0] == '-'
+ && (argv[optind][1] == '-' || long_only))
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ ))
+ {
+ const struct option *p;
+ char *s = nextchar;
+ int exact = 0;
+ int ambig = 0;
+ const struct option *pfound = NULL;
+ int indfound;
+
+ while (*s && *s != '=')
+ s++;
+
+ /* Test all options for either exact match or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name;
+ p++, option_index++)
+ if (!strncmp (p->name, nextchar, s - nextchar))
+ {
+ if (s - nextchar == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' is ambiguous\n",
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*s)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = s + 1;
+ else
+ {
+ if (opterr)
+ {
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ "%s: option `--%s' doesn't allow an argument\n",
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ "%s: option `%c%s' doesn't allow an argument\n",
+ argv[0], argv[optind - 1][0], pfound->name);
+ }
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' requires an argument\n",
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, "%s: unrecognized option `--%s'\n",
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+#if 0
+ if (c < 040 || c >= 0177)
+ fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
+ argv[0], c);
+ else
+ fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
+#endif
+ }
+ optopt = c;
+ return '?';
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = 0;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+#if 0
+ fprintf (stderr, "%s: option `-%c' requires an argument\n",
+ argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: option requires an argument -- %c\n",
+ argv[0], c);
+#endif
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/gnu/usr.bin/sort/getopt.h b/gnu/usr.bin/sort/getopt.h
new file mode 100644
index 0000000..45541f5
--- /dev/null
+++ b/gnu/usr.bin/sort/getopt.h
@@ -0,0 +1,129 @@
+/* Declarations for getopt.
+ Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized. */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+#if __STDC__
+ const char *name;
+#else
+ char *name;
+#endif
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+#if __STDC__
+#if defined(__GNU_LIBRARY__)
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* not __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind);
+
+/* Internal only. Users should not call this directly. */
+extern int _getopt_internal (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* not __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/gnu/usr.bin/sort/getopt1.c b/gnu/usr.bin/sort/getopt1.c
new file mode 100644
index 0000000..f784b57
--- /dev/null
+++ b/gnu/usr.bin/sort/getopt1.c
@@ -0,0 +1,187 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef HAVE_CONFIG_H
+#if defined (emacs) || defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
+#include "getopt.h"
+
+#ifndef __STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#else
+char *getenv ();
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/gnu/usr.bin/sort/long-options.c b/gnu/usr.bin/sort/long-options.c
new file mode 100644
index 0000000..03305aa
--- /dev/null
+++ b/gnu/usr.bin/sort/long-options.c
@@ -0,0 +1,87 @@
+/* Utility to accept --help and --version options as unobtrusively as possible.
+ Copyright (C) 1993 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Jim Meyering (meyering@comco.com) */
+
+#ifdef HAVE_CONFIG_H
+#if defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include "system.h"
+#include "version.h"
+#include "long-options.h"
+
+static struct option const long_options[] =
+{
+ {"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'v'},
+ {0, 0, 0, 0}
+};
+
+/* Process long options --help and --version, but only if argc == 2.
+ Be careful not to gobble up `--'. */
+
+void
+parse_long_options (argc, argv, usage)
+ int argc;
+ char **argv;
+ void (*usage)();
+{
+ int c;
+ int saved_opterr;
+ int saved_optind;
+
+ saved_opterr = opterr;
+ saved_optind = optind;
+
+ /* Don't print an error message for unrecognized options. */
+ opterr = 0;
+
+ if (argc == 2
+ && (c = getopt_long (argc, argv, "+", long_options, (int *) 0)) != EOF)
+ {
+ switch (c)
+ {
+ case 'h':
+ usage (0);
+
+ case 'v':
+ printf ("%s\n", version_string);
+ exit (0);
+
+ default:
+ /* Don't process any other long-named options. */
+ break;
+ }
+ }
+
+ /* Restore previous value. */
+ opterr = saved_opterr;
+
+ /* Restore optind in case it has advanced past a leading `--'. */
+ optind = saved_optind;
+}
diff --git a/gnu/usr.bin/sort/long-options.h b/gnu/usr.bin/sort/long-options.h
new file mode 100644
index 0000000..9d1a9c7
--- /dev/null
+++ b/gnu/usr.bin/sort/long-options.h
@@ -0,0 +1 @@
+void parse_long_options ();
diff --git a/gnu/usr.bin/sort/sort.1 b/gnu/usr.bin/sort/sort.1
new file mode 100644
index 0000000..19532c8
--- /dev/null
+++ b/gnu/usr.bin/sort/sort.1
@@ -0,0 +1,231 @@
+.TH SORT 1L \" -*- nroff -*-
+.SH NAME
+sort \- sort lines of text files
+.SH SYNOPSIS
+.B sort
+[\-cmus] [\-t separator] [\-o output-file] [\-T tempdir] [\-bdfiMnr]
+[+POS1 [\-POS2]] [\-k POS1[,POS2]] [file...]
+.br
+.B sort
+{\-\-help,\-\-version}
+.SH DESCRIPTION
+This manual page
+documents the GNU version of
+.BR sort .
+.B sort
+sorts, merges, or compares all the lines from the given files, or the standard
+input if no files are given. A file name of `-' means standard input.
+By default,
+.B sort
+writes the results to the standard output.
+.PP
+.B sort
+has three modes of operation: sort (the default), merge, and check for
+sortedness. The following options change the operation mode:
+.TP
+.I \-c
+Check whether the given files are already sorted: if they are not all
+sorted, print an error message and exit with a status of 1.
+.TP
+.I \-m
+Merge the given files by sorting them as a group. Each input file
+should already be individually sorted. It always works to sort
+instead of merge; merging is provided because it is faster, in the
+case where it works.
+.PP
+A pair of lines is compared as follows:
+if any key fields have been specified,
+.B sort
+compares each pair of fields, in the order specified on the command
+line, according to the associated ordering options, until a difference
+is found or no fields are left.
+.PP
+If any of the global options
+.I Mbdfinr
+are given but no key fields are
+specified,
+.B sort
+compares the entire lines according to the global options.
+.PP
+Finally, as a last resort when all keys compare equal
+(or if no ordering options were specified at all),
+.B sort
+compares the lines byte by byte in machine collating sequence.
+The last resort comparison honors the
+.I -r
+global option.
+The
+.I \-s
+(stable) option disables this last-resort comparison so that
+lines in which all fields compare equal are left in their original
+relative order. If no fields or global options are specified,
+.I \-s
+has no effect.
+.PP
+GNU
+.B sort
+has no limits on input line length or restrictions on bytes allowed
+within lines. In addition, if the final byte of an input file is not
+a newline, GNU
+.B sort
+silently supplies one.
+.PP
+If the environment variable
+.B TMPDIR
+is set,
+.B sort
+uses it as the directory in which to put temporary files instead of
+the default, /tmp. The
+.I "\-T tempdir"
+option is another way to select the directory for temporary files; it
+overrides the environment variable.
+.PP
+The following options affect the ordering of output lines. They may
+be specified globally or as part of a specific key field. If no key
+fields are specified, global options apply to comparison of entire
+lines; otherwise the global options are inherited by key fields that
+do not specify any special options of their own.
+.TP
+.I \-b
+Ignore leading blanks when finding sort keys in each line.
+.TP
+.I \-d
+Sort in `phone directory' order: ignore all characters except letters,
+digits and blanks when sorting.
+.TP
+.I \-f
+Fold lower case characters into the equivalent upper case characters
+when sorting so that, for example, `b' is sorted the same way `B' is.
+.TP
+.I \-i
+Ignore characters outside the ASCII range 040-0176 octal (inclusive)
+when sorting.
+.TP
+.I \-M
+An initial string, consisting of any amount of white space, followed
+by three letters abbreviating a month name, is folded to UPPER case
+and compared in the order `JAN' < `FEB' < ... < `DEC.' Invalid names
+compare low to valid names.
+.TP
+.I \-n
+Compare according to arithmetic value an initial numeric string
+consisting of optional white space, an optional \- sign, and zero or
+more digits, optionally followed by a decimal point and zero or more
+digits.
+.TP
+.I \-r
+Reverse the result of comparison, so that lines with greater key
+values appear earlier in the output instead of later.
+.PP
+Other options are:
+.TP
+.I "\-o output-file"
+Write output to
+.I output-file
+instead of to the standard output. If
+.I output-file
+is one of the input files,
+.B sort
+copies it to a temporary file before sorting and writing the output to
+.IR output-file .
+.TP
+.I "\-t separator"
+Use character
+.I separator
+as the field separator when finding the sort keys in each line. By
+default, fields are separated by the empty string between a
+non-whitespace character and a whitespace character. That is to say,
+given the input line ` foo bar',
+.B sort
+breaks it into fields ` foo' and ` bar'. The field separator is not
+considered to be part of either the field preceding or the field
+following it.
+.TP
+.I \-u
+For the default case or the
+.I \-m
+option, only output the first of a sequence of lines that compare
+equal. For the
+.I \-c
+option, check that no pair of consecutive lines compares equal.
+.TP
+.I "+POS1 [\-POS2]"
+Specify a field within each line to use as a sorting key. The field
+consists of the portion of the line starting at POS1 and up to (but
+not including) POS2 (or to the end of the line if POS2 is not given).
+The fields and character positions are numbered starting with 0.
+.TP
+.I "\-k POS1[,POS2]"
+An alternate syntax for specifying sorting keys.
+The fields and character positions are numbered starting with 1.
+.PP
+A position has the form \fIf\fP.\fIc\fP, where \fIf\fP is the number
+of the field to use and \fIc\fP is the number of the first character
+from the beginning of the field (for \fI+pos\fP) or from the end of
+the previous field (for \fI\-pos\fP). The .\fIc\fP part of a position
+may be omitted in which case it is taken to be the first character in
+the field. If the
+.I \-b
+option has been given, the .\fIc\fP part of a field specification is
+counted from the first nonblank character of the field (for
+\fI+pos\fP) or from the first nonblank character following the
+previous field (for \fI\-pos\fP).
+.PP
+A \fI+pos\fP or \fI-pos\fP argument may also have any of the option
+letters
+.I Mbdfinr
+appended to it, in which case the global ordering options are not used
+for that particular field. The
+.I \-b
+option may be independently attached to either or both of the
+\fI+pos\fP and \fI\-pos\fP parts of a field specification, and if it
+is inherited from the global options it will be attached to both.
+If a
+.I \-n
+or
+.I \-M
+option is used, thus implying a
+.I \-b
+option, the
+.I \-b
+option is taken to apply to both the \fI+pos\fP and the \fI\-pos\fP
+parts of a key specification. Keys may span multiple fields.
+.PP
+In addition, when GNU
+.B join
+is invoked with exactly one argument, the following options are recognized:
+.TP
+.I "\-\-help"
+Print a usage message on standard output and exit successfully.
+.TP
+.I "\-\-version"
+Print version information on standard output then exit successfully.
+.SH COMPATIBILITY
+.PP
+Historical (BSD and System V) implementations of
+.B sort
+have differed in their interpretation of some options,
+particularly
+.IR \-b ,
+.IR \-f ,
+and
+.IR \-n .
+GNU sort follows the POSIX behavior, which is
+usually (but not always!) like the System V behavior.
+According to POSIX
+.I \-n
+no longer implies
+.IR \-b .
+For consistency,
+.I \-M
+has been changed in the same way.
+This may affect the meaning of character positions in field
+specifications in obscure cases.
+If this bites you the fix is to add an explicit
+.IR \-b .
+.SH BUGS
+The different meaning of field numbers depending
+on whether
+.I -k
+is used is confusing.
+It's all POSIX's fault!
diff --git a/gnu/usr.bin/sort/sort.c b/gnu/usr.bin/sort/sort.c
new file mode 100644
index 0000000..74d4b46
--- /dev/null
+++ b/gnu/usr.bin/sort/sort.c
@@ -0,0 +1,1829 @@
+/* sort - sort lines of text (with all kinds of options).
+ Copyright (C) 1988, 1991 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Written December 1988 by Mike Haertel.
+ The author may be reached (Email) at the address mike@gnu.ai.mit.edu,
+ or (US mail) as Mike Haertel c/o Free Software Foundation. */
+
+#ifdef HAVE_CONFIG_H
+#if defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
+/* Get isblank from GNU libc. */
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <signal.h>
+#include <stdio.h>
+#include "system.h"
+#include "long-options.h"
+
+#ifdef _POSIX_VERSION
+#include <limits.h>
+#else
+#ifndef UCHAR_MAX
+#define UCHAR_MAX 255
+#endif
+#endif
+#ifndef STDC_HEADERS
+char *malloc ();
+char *realloc ();
+void free ();
+#endif
+
+void error ();
+static void usage ();
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#define UCHAR_LIM (UCHAR_MAX + 1)
+#define UCHAR(c) ((unsigned char) (c))
+
+/* The kind of blanks for '-b' to skip in various options. */
+enum blanktype { bl_start, bl_end, bl_both };
+
+/* The name this program was run with. */
+char *program_name;
+
+/* Table of digits. */
+static int digits[UCHAR_LIM];
+
+/* Table of white space. */
+static int blanks[UCHAR_LIM];
+
+/* Table of non-printing characters. */
+static int nonprinting[UCHAR_LIM];
+
+/* Table of non-dictionary characters (not letters, digits, or blanks). */
+static int nondictionary[UCHAR_LIM];
+
+/* Translation table folding lower case to upper. */
+static char fold_toupper[UCHAR_LIM];
+
+/* Table mapping 3-letter month names to integers.
+ Alphabetic order allows binary search. */
+static struct month
+{
+ char *name;
+ int val;
+} const monthtab[] =
+{
+ {"APR", 4},
+ {"AUG", 8},
+ {"DEC", 12},
+ {"FEB", 2},
+ {"JAN", 1},
+ {"JUL", 7},
+ {"JUN", 6},
+ {"MAR", 3},
+ {"MAY", 5},
+ {"NOV", 11},
+ {"OCT", 10},
+ {"SEP", 9}
+};
+
+/* During the merge phase, the number of files to merge at once. */
+#define NMERGE 16
+
+/* Initial buffer size for in core sorting. Will not grow unless a
+ line longer than this is seen. */
+static int sortalloc = 524288;
+
+/* Initial buffer size for in core merge buffers. Bear in mind that
+ up to NMERGE * mergealloc bytes may be allocated for merge buffers. */
+static int mergealloc = 16384;
+
+/* Guess of average line length. */
+static int linelength = 30;
+
+/* Maximum number of elements for the array(s) of struct line's, in bytes. */
+#define LINEALLOC 262144
+
+/* Prefix for temporary file names. */
+static char *temp_file_prefix;
+
+/* Flag to reverse the order of all comparisons. */
+static int reverse;
+
+/* Flag for stable sort. This turns off the last ditch bytewise
+ comparison of lines, and instead leaves lines in the same order
+ they were read if all keys compare equal. */
+static int stable;
+
+/* Tab character separating fields. If NUL, then fields are separated
+ by the empty string between a non-whitespace character and a whitespace
+ character. */
+static char tab;
+
+/* Flag to remove consecutive duplicate lines from the output.
+ Only the last of a sequence of equal lines will be output. */
+static int unique;
+
+/* Nonzero if any of the input files are the standard input. */
+static int have_read_stdin;
+
+/* Lines are held in core as counted strings. */
+struct line
+{
+ char *text; /* Text of the line. */
+ int length; /* Length not including final newline. */
+ char *keybeg; /* Start of first key. */
+ char *keylim; /* Limit of first key. */
+};
+
+/* Arrays of lines. */
+struct lines
+{
+ struct line *lines; /* Dynamically allocated array of lines. */
+ int used; /* Number of slots used. */
+ int alloc; /* Number of slots allocated. */
+ int limit; /* Max number of slots to allocate. */
+};
+
+/* Input buffers. */
+struct buffer
+{
+ char *buf; /* Dynamically allocated buffer. */
+ int used; /* Number of bytes used. */
+ int alloc; /* Number of bytes allocated. */
+ int left; /* Number of bytes left after line parsing. */
+};
+
+/* Lists of key field comparisons to be tried. */
+static struct keyfield
+{
+ int sword; /* Zero-origin 'word' to start at. */
+ int schar; /* Additional characters to skip. */
+ int skipsblanks; /* Skip leading white space at start. */
+ int eword; /* Zero-origin first word after field. */
+ int echar; /* Additional characters in field. */
+ int skipeblanks; /* Skip trailing white space at finish. */
+ int *ignore; /* Boolean array of characters to ignore. */
+ char *translate; /* Translation applied to characters. */
+ int numeric; /* Flag for numeric comparison. */
+ int month; /* Flag for comparison by month name. */
+ int reverse; /* Reverse the sense of comparison. */
+ struct keyfield *next; /* Next keyfield to try. */
+} keyhead;
+
+/* The list of temporary files. */
+static struct tempnode
+{
+ char *name;
+ struct tempnode *next;
+} temphead;
+
+/* Clean up any remaining temporary files. */
+
+static void
+cleanup ()
+{
+ struct tempnode *node;
+
+ for (node = temphead.next; node; node = node->next)
+ unlink (node->name);
+}
+
+/* Allocate N bytes of memory dynamically, with error checking. */
+
+char *
+xmalloc (n)
+ unsigned n;
+{
+ char *p;
+
+ p = malloc (n);
+ if (p == 0)
+ {
+ error (0, 0, "virtual memory exhausted");
+ cleanup ();
+ exit (2);
+ }
+ return p;
+}
+
+/* Change the size of an allocated block of memory P to N bytes,
+ with error checking.
+ If P is NULL, run xmalloc.
+ If N is 0, run free and return NULL. */
+
+char *
+xrealloc (p, n)
+ char *p;
+ unsigned n;
+{
+ if (p == 0)
+ return xmalloc (n);
+ if (n == 0)
+ {
+ free (p);
+ return 0;
+ }
+ p = realloc (p, n);
+ if (p == 0)
+ {
+ error (0, 0, "virtual memory exhausted");
+ cleanup ();
+ exit (2);
+ }
+ return p;
+}
+
+static FILE *
+xfopen (file, how)
+ char *file, *how;
+{
+ FILE *fp = strcmp (file, "-") ? fopen (file, how) : stdin;
+
+ if (fp == 0)
+ {
+ error (0, errno, "%s", file);
+ cleanup ();
+ exit (2);
+ }
+ if (fp == stdin)
+ have_read_stdin = 1;
+ return fp;
+}
+
+static void
+xfclose (fp)
+ FILE *fp;
+{
+ fflush (fp);
+ if (fp != stdin && fp != stdout)
+ {
+ if (fclose (fp) != 0)
+ {
+ error (0, errno, "error closing file");
+ cleanup ();
+ exit (2);
+ }
+ }
+ else
+ /* Allow reading stdin from tty more than once. */
+ clearerr (fp);
+}
+
+static void
+xfwrite (buf, size, nelem, fp)
+ char *buf;
+ int size, nelem;
+ FILE *fp;
+{
+ if (fwrite (buf, size, nelem, fp) != nelem)
+ {
+ error (0, errno, "write error");
+ cleanup ();
+ exit (2);
+ }
+}
+
+/* Return a name for a temporary file. */
+
+static char *
+tempname ()
+{
+ static int seq;
+ int len = strlen (temp_file_prefix);
+ char *name = xmalloc (len + 16);
+ struct tempnode *node =
+ (struct tempnode *) xmalloc (sizeof (struct tempnode));
+
+ if (len && temp_file_prefix[len - 1] != '/')
+ sprintf (name, "%s/sort%5.5d%5.5d", temp_file_prefix, getpid (), ++seq);
+ else
+ sprintf (name, "%ssort%5.5d%5.5d", temp_file_prefix, getpid (), ++seq);
+ node->name = name;
+ node->next = temphead.next;
+ temphead.next = node;
+ return name;
+}
+
+/* Search through the list of temporary files for NAME;
+ remove it if it is found on the list. */
+
+static void
+zaptemp (name)
+ char *name;
+{
+ struct tempnode *node, *temp;
+
+ for (node = &temphead; node->next; node = node->next)
+ if (!strcmp (name, node->next->name))
+ break;
+ if (node->next)
+ {
+ temp = node->next;
+ unlink (temp->name);
+ free (temp->name);
+ node->next = temp->next;
+ free ((char *) temp);
+ }
+}
+
+/* Initialize the character class tables. */
+
+static void
+inittables ()
+{
+ int i;
+
+ for (i = 0; i < UCHAR_LIM; ++i)
+ {
+ if (ISBLANK (i))
+ blanks[i] = 1;
+ if (ISDIGIT (i))
+ digits[i] = 1;
+ if (!ISPRINT (i))
+ nonprinting[i] = 1;
+ if (!ISALNUM (i) && !ISBLANK (i))
+ nondictionary[i] = 1;
+ if (ISLOWER (i))
+ fold_toupper[i] = toupper (i);
+ else
+ fold_toupper[i] = i;
+ }
+}
+
+/* Initialize BUF, allocating ALLOC bytes initially. */
+
+static void
+initbuf (buf, alloc)
+ struct buffer *buf;
+ int alloc;
+{
+ buf->alloc = alloc;
+ buf->buf = xmalloc (buf->alloc);
+ buf->used = buf->left = 0;
+}
+
+/* Fill BUF reading from FP, moving buf->left bytes from the end
+ of buf->buf to the beginning first. If EOF is reached and the
+ file wasn't terminated by a newline, supply one. Return a count
+ of bytes buffered. */
+
+static int
+fillbuf (buf, fp)
+ struct buffer *buf;
+ FILE *fp;
+{
+ int cc;
+
+ bcopy (buf->buf + buf->used - buf->left, buf->buf, buf->left);
+ buf->used = buf->left;
+
+ while (!feof (fp) && (buf->used == 0 || !memchr (buf->buf, '\n', buf->used)))
+ {
+ if (buf->used == buf->alloc)
+ {
+ buf->alloc *= 2;
+ buf->buf = xrealloc (buf->buf, buf->alloc);
+ }
+ cc = fread (buf->buf + buf->used, 1, buf->alloc - buf->used, fp);
+ if (ferror (fp))
+ {
+ error (0, errno, "read error");
+ cleanup ();
+ exit (2);
+ }
+ buf->used += cc;
+ }
+
+ if (feof (fp) && buf->used && buf->buf[buf->used - 1] != '\n')
+ {
+ if (buf->used == buf->alloc)
+ {
+ buf->alloc *= 2;
+ buf->buf = xrealloc (buf->buf, buf->alloc);
+ }
+ buf->buf[buf->used++] = '\n';
+ }
+
+ return buf->used;
+}
+
+/* Initialize LINES, allocating space for ALLOC lines initially.
+ LIMIT is the maximum possible number of lines to allocate space
+ for, ever. */
+
+static void
+initlines (lines, alloc, limit)
+ struct lines *lines;
+ int alloc;
+ int limit;
+{
+ lines->alloc = alloc;
+ lines->lines = (struct line *) xmalloc (lines->alloc * sizeof (struct line));
+ lines->used = 0;
+ lines->limit = limit;
+}
+
+/* Return a pointer to the first character of the field specified
+ by KEY in LINE. */
+
+static char *
+begfield (line, key)
+ struct line *line;
+ struct keyfield *key;
+{
+ register char *ptr = line->text, *lim = ptr + line->length;
+ register int sword = key->sword, schar = key->schar;
+
+ if (tab)
+ while (ptr < lim && sword--)
+ {
+ while (ptr < lim && *ptr != tab)
+ ++ptr;
+ if (ptr < lim)
+ ++ptr;
+ }
+ else
+ while (ptr < lim && sword--)
+ {
+ while (ptr < lim && blanks[UCHAR (*ptr)])
+ ++ptr;
+ while (ptr < lim && !blanks[UCHAR (*ptr)])
+ ++ptr;
+ }
+
+ if (key->skipsblanks)
+ while (ptr < lim && blanks[UCHAR (*ptr)])
+ ++ptr;
+
+ while (ptr < lim && schar--)
+ ++ptr;
+
+ return ptr;
+}
+
+/* Return the limit of (a pointer to the first character after) the field
+ in LINE specified by KEY. */
+
+static char *
+limfield (line, key)
+ struct line *line;
+ struct keyfield *key;
+{
+ register char *ptr = line->text, *lim = ptr + line->length;
+ register int eword = key->eword, echar = key->echar;
+
+ if (tab)
+ while (ptr < lim && eword--)
+ {
+ while (ptr < lim && *ptr != tab)
+ ++ptr;
+ if (ptr < lim && (eword || key->skipeblanks))
+ ++ptr;
+ }
+ else
+ while (ptr < lim && eword--)
+ {
+ while (ptr < lim && blanks[UCHAR (*ptr)])
+ ++ptr;
+ while (ptr < lim && !blanks[UCHAR (*ptr)])
+ ++ptr;
+ }
+
+ if (key->skipeblanks)
+ while (ptr < lim && blanks[UCHAR (*ptr)])
+ ++ptr;
+
+ while (ptr < lim && echar--)
+ ++ptr;
+
+ return ptr;
+}
+
+/* Find the lines in BUF, storing pointers and lengths in LINES.
+ Also replace newlines with NULs. */
+
+static void
+findlines (buf, lines)
+ struct buffer *buf;
+ struct lines *lines;
+{
+ register char *beg = buf->buf, *lim = buf->buf + buf->used, *ptr;
+ struct keyfield *key = keyhead.next;
+
+ lines->used = 0;
+
+ while (beg < lim && (ptr = memchr (beg, '\n', lim - beg))
+ && lines->used < lines->limit)
+ {
+ /* There are various places in the code that rely on a NUL
+ being at the end of in-core lines; NULs inside the lines
+ will not cause trouble, though. */
+ *ptr = '\0';
+
+ if (lines->used == lines->alloc)
+ {
+ lines->alloc *= 2;
+ lines->lines = (struct line *)
+ xrealloc ((char *) lines->lines,
+ lines->alloc * sizeof (struct line));
+ }
+
+ lines->lines[lines->used].text = beg;
+ lines->lines[lines->used].length = ptr - beg;
+
+ /* Precompute the position of the first key for efficiency. */
+ if (key)
+ {
+ if (key->eword >= 0)
+ lines->lines[lines->used].keylim =
+ limfield (&lines->lines[lines->used], key);
+ else
+ lines->lines[lines->used].keylim = ptr;
+
+ if (key->sword >= 0)
+ lines->lines[lines->used].keybeg =
+ begfield (&lines->lines[lines->used], key);
+ else
+ {
+ if (key->skipsblanks)
+ while (blanks[UCHAR (*beg)])
+ ++beg;
+ lines->lines[lines->used].keybeg = beg;
+ }
+ }
+ else
+ {
+ lines->lines[lines->used].keybeg = 0;
+ lines->lines[lines->used].keylim = 0;
+ }
+
+ ++lines->used;
+ beg = ptr + 1;
+ }
+
+ buf->left = lim - beg;
+}
+
+/* Compare strings A and B containing decimal fractions < 1. Each string
+ should begin with a decimal point followed immediately by the digits
+ of the fraction. Strings not of this form are considered to be zero. */
+
+static int
+fraccompare (a, b)
+ register char *a, *b;
+{
+ register tmpa = UCHAR (*a), tmpb = UCHAR (*b);
+
+ if (tmpa == '.' && tmpb == '.')
+ {
+ do
+ tmpa = UCHAR (*++a), tmpb = UCHAR (*++b);
+ while (tmpa == tmpb && digits[tmpa]);
+ if (digits[tmpa] && digits[tmpb])
+ return tmpa - tmpb;
+ if (digits[tmpa])
+ {
+ while (tmpa == '0')
+ tmpa = UCHAR (*++a);
+ if (digits[tmpa])
+ return 1;
+ return 0;
+ }
+ if (digits[tmpb])
+ {
+ while (tmpb == '0')
+ tmpb = UCHAR (*++b);
+ if (digits[tmpb])
+ return -1;
+ return 0;
+ }
+ return 0;
+ }
+ else if (tmpa == '.')
+ {
+ do
+ tmpa = UCHAR (*++a);
+ while (tmpa == '0');
+ if (digits[tmpa])
+ return 1;
+ return 0;
+ }
+ else if (tmpb == '.')
+ {
+ do
+ tmpb = UCHAR (*++b);
+ while (tmpb == '0');
+ if (digits[tmpb])
+ return -1;
+ return 0;
+ }
+ return 0;
+}
+
+/* Compare strings A and B as numbers without explicitly converting them to
+ machine numbers. Comparatively slow for short strings, but asymptotically
+ hideously fast. */
+
+static int
+numcompare (a, b)
+ register char *a, *b;
+{
+ register int tmpa, tmpb, loga, logb, tmp;
+
+ tmpa = UCHAR (*a), tmpb = UCHAR (*b);
+
+ while (blanks[tmpa])
+ tmpa = UCHAR (*++a);
+ while (blanks[tmpb])
+ tmpb = UCHAR (*++b);
+
+ if (tmpa == '-')
+ {
+ tmpa = UCHAR (*++a);
+ if (tmpb != '-')
+ {
+ if (digits[tmpa] && digits[tmpb])
+ return -1;
+ return 0;
+ }
+ tmpb = UCHAR (*++b);
+
+ while (tmpa == '0')
+ tmpa = UCHAR (*++a);
+ while (tmpb == '0')
+ tmpb = UCHAR (*++b);
+
+ while (tmpa == tmpb && digits[tmpa])
+ tmpa = UCHAR (*++a), tmpb = UCHAR (*++b);
+
+ if ((tmpa == '.' && !digits[tmpb]) || (tmpb == '.' && !digits[tmpa]))
+ return -fraccompare (a, b);
+
+ if (digits[tmpa])
+ for (loga = 1; digits[UCHAR (*++a)]; ++loga)
+ ;
+ else
+ loga = 0;
+
+ if (digits[tmpb])
+ for (logb = 1; digits[UCHAR (*++b)]; ++logb)
+ ;
+ else
+ logb = 0;
+
+ if ((tmp = logb - loga) != 0)
+ return tmp;
+
+ if (!loga)
+ return 0;
+
+ return tmpb - tmpa;
+ }
+ else if (tmpb == '-')
+ {
+ if (digits[UCHAR (tmpa)] && digits[UCHAR (*++b)])
+ return 1;
+ return 0;
+ }
+ else
+ {
+ while (tmpa == '0')
+ tmpa = UCHAR (*++a);
+ while (tmpb == '0')
+ tmpb = UCHAR (*++b);
+
+ while (tmpa == tmpb && digits[tmpa])
+ tmpa = UCHAR (*++a), tmpb = UCHAR (*++b);
+
+ if ((tmpa == '.' && !digits[tmpb]) || (tmpb == '.' && !digits[tmpa]))
+ return fraccompare (a, b);
+
+ if (digits[tmpa])
+ for (loga = 1; digits[UCHAR (*++a)]; ++loga)
+ ;
+ else
+ loga = 0;
+
+ if (digits[tmpb])
+ for (logb = 1; digits[UCHAR (*++b)]; ++logb)
+ ;
+ else
+ logb = 0;
+
+ if ((tmp = loga - logb) != 0)
+ return tmp;
+
+ if (!loga)
+ return 0;
+
+ return tmpa - tmpb;
+ }
+}
+
+/* Return an integer <= 12 associated with month name S with length LEN,
+ 0 if the name in S is not recognized. */
+
+static int
+getmonth (s, len)
+ char *s;
+ int len;
+{
+ char month[4];
+ register int i, lo = 0, hi = 12;
+
+ while (len > 0 && blanks[UCHAR(*s)])
+ ++s, --len;
+
+ if (len < 3)
+ return 0;
+
+ for (i = 0; i < 3; ++i)
+ month[i] = fold_toupper[UCHAR (s[i])];
+ month[3] = '\0';
+
+ while (hi - lo > 1)
+ if (strcmp (month, monthtab[(lo + hi) / 2].name) < 0)
+ hi = (lo + hi) / 2;
+ else
+ lo = (lo + hi) / 2;
+ if (!strcmp (month, monthtab[lo].name))
+ return monthtab[lo].val;
+ return 0;
+}
+
+/* Compare two lines A and B trying every key in sequence until there
+ are no more keys or a difference is found. */
+
+static int
+keycompare (a, b)
+ struct line *a, *b;
+{
+ register char *texta, *textb, *lima, *limb, *translate;
+ register int *ignore;
+ struct keyfield *key;
+ int diff = 0, iter = 0, lena, lenb;
+
+ for (key = keyhead.next; key; key = key->next, ++iter)
+ {
+ ignore = key->ignore;
+ translate = key->translate;
+
+ /* Find the beginning and limit of each field. */
+ if (iter || a->keybeg == NULL || b->keybeg == NULL)
+ {
+ if (key->eword >= 0)
+ lima = limfield (a, key), limb = limfield (b, key);
+ else
+ lima = a->text + a->length, limb = b->text + b->length;
+
+ if (key->sword >= 0)
+ texta = begfield (a, key), textb = begfield (b, key);
+ else
+ {
+ texta = a->text, textb = b->text;
+ if (key->skipsblanks)
+ {
+ while (texta < lima && blanks[UCHAR (*texta)])
+ ++texta;
+ while (textb < limb && blanks[UCHAR (*textb)])
+ ++textb;
+ }
+ }
+ }
+ else
+ {
+ /* For the first iteration only, the key positions have
+ been precomputed for us. */
+ texta = a->keybeg, lima = a->keylim;
+ textb = b->keybeg, limb = b->keylim;
+ }
+
+ /* Find the lengths. */
+ lena = lima - texta, lenb = limb - textb;
+ if (lena < 0)
+ lena = 0;
+ if (lenb < 0)
+ lenb = 0;
+
+ /* Actually compare the fields. */
+ if (key->numeric)
+ {
+ if (*lima || *limb)
+ {
+ char savea = *lima, saveb = *limb;
+
+ *lima = *limb = '\0';
+ diff = numcompare (texta, textb);
+ *lima = savea, *limb = saveb;
+ }
+ else
+ diff = numcompare (texta, textb);
+
+ if (diff)
+ return key->reverse ? -diff : diff;
+ continue;
+ }
+ else if (key->month)
+ {
+ diff = getmonth (texta, lena) - getmonth (textb, lenb);
+ if (diff)
+ return key->reverse ? -diff : diff;
+ continue;
+ }
+ else if (ignore && translate)
+ while (texta < lima && textb < limb)
+ {
+ while (texta < lima && ignore[UCHAR (*texta)])
+ ++texta;
+ while (textb < limb && ignore[UCHAR (*textb)])
+ ++textb;
+ if (texta < lima && textb < limb &&
+ translate[UCHAR (*texta++)] != translate[UCHAR (*textb++)])
+ {
+ diff = translate[UCHAR (*--texta)] - translate[UCHAR (*--textb)];
+ break;
+ }
+ }
+ else if (ignore)
+ while (texta < lima && textb < limb)
+ {
+ while (texta < lima && ignore[UCHAR (*texta)])
+ ++texta;
+ while (textb < limb && ignore[UCHAR (*textb)])
+ ++textb;
+ if (texta < lima && textb < limb && *texta++ != *textb++)
+ {
+ diff = *--texta - *--textb;
+ break;
+ }
+ }
+ else if (translate)
+ while (texta < lima && textb < limb)
+ {
+ if (translate[UCHAR (*texta++)] != translate[UCHAR (*textb++)])
+ {
+ diff = translate[UCHAR (*--texta)] - translate[UCHAR (*--textb)];
+ break;
+ }
+ }
+ else
+ diff = memcmp (texta, textb, min (lena, lenb));
+
+ if (diff)
+ return key->reverse ? -diff : diff;
+ if ((diff = lena - lenb) != 0)
+ return key->reverse ? -diff : diff;
+ }
+
+ return 0;
+}
+
+/* Compare two lines A and B, returning negative, zero, or positive
+ depending on whether A compares less than, equal to, or greater than B. */
+
+static int
+compare (a, b)
+ register struct line *a, *b;
+{
+ int diff, tmpa, tmpb, mini;
+
+ /* First try to compare on the specified keys (if any).
+ The only two cases with no key at all are unadorned sort,
+ and unadorned sort -r. */
+ if (keyhead.next)
+ {
+ diff = keycompare (a, b);
+ if (diff != 0)
+ return diff;
+ if (unique || stable)
+ return 0;
+ }
+
+ /* If the keys all compare equal (or no keys were specified)
+ fall through to the default byte-by-byte comparison. */
+ tmpa = a->length, tmpb = b->length;
+ mini = min (tmpa, tmpb);
+ if (mini == 0)
+ diff = tmpa - tmpb;
+ else
+ {
+ char *ap = a->text, *bp = b->text;
+
+ diff = UCHAR (*ap) - UCHAR (*bp);
+ if (diff == 0)
+ {
+ diff = memcmp (ap, bp, mini);
+ if (diff == 0)
+ diff = tmpa - tmpb;
+ }
+ }
+
+ return reverse ? -diff : diff;
+}
+
+/* Check that the lines read from the given FP come in order. Return
+ 1 if they do and 0 if there is a disorder. */
+
+static int
+checkfp (fp)
+ FILE *fp;
+{
+ struct buffer buf; /* Input buffer. */
+ struct lines lines; /* Lines scanned from the buffer. */
+ struct line temp; /* Copy of previous line. */
+ int cc; /* Character count. */
+ int cmp; /* Result of calling compare. */
+ int alloc, i, success = 1;
+
+ initbuf (&buf, mergealloc);
+ initlines (&lines, mergealloc / linelength + 1,
+ LINEALLOC / ((NMERGE + NMERGE) * sizeof (struct line)));
+ alloc = linelength;
+ temp.text = xmalloc (alloc);
+
+ cc = fillbuf (&buf, fp);
+ findlines (&buf, &lines);
+
+ if (cc)
+ do
+ {
+ /* Compare each line in the buffer with its successor. */
+ for (i = 0; i < lines.used - 1; ++i)
+ {
+ cmp = compare (&lines.lines[i], &lines.lines[i + 1]);
+ if ((unique && cmp >= 0) || (cmp > 0))
+ {
+ success = 0;
+ goto finish;
+ }
+ }
+
+ /* Save the last line of the buffer and refill the buffer. */
+ if (lines.lines[lines.used - 1].length > alloc)
+ {
+ while (lines.lines[lines.used - 1].length + 1 > alloc)
+ alloc *= 2;
+ temp.text = xrealloc (temp.text, alloc);
+ }
+ bcopy (lines.lines[lines.used - 1].text, temp.text,
+ lines.lines[lines.used - 1].length + 1);
+ temp.length = lines.lines[lines.used - 1].length;
+
+ cc = fillbuf (&buf, fp);
+ if (cc)
+ {
+ findlines (&buf, &lines);
+ /* Make sure the line saved from the old buffer contents is
+ less than or equal to the first line of the new buffer. */
+ cmp = compare (&temp, &lines.lines[0]);
+ if ((unique && cmp >= 0) || (cmp > 0))
+ {
+ success = 0;
+ break;
+ }
+ }
+ }
+ while (cc);
+
+finish:
+ xfclose (fp);
+ free (buf.buf);
+ free ((char *) lines.lines);
+ free (temp.text);
+ return success;
+}
+
+/* Merge lines from FPS onto OFP. NFPS cannot be greater than NMERGE.
+ Close FPS before returning. */
+
+static void
+mergefps (fps, nfps, ofp)
+ FILE *fps[], *ofp;
+ register int nfps;
+{
+ struct buffer buffer[NMERGE]; /* Input buffers for each file. */
+ struct lines lines[NMERGE]; /* Line tables for each buffer. */
+ struct line saved; /* Saved line for unique check. */
+ int savedflag = 0; /* True if there is a saved line. */
+ int savealloc; /* Size allocated for the saved line. */
+ int cur[NMERGE]; /* Current line in each line table. */
+ int ord[NMERGE]; /* Table representing a permutation of fps,
+ such that lines[ord[0]].lines[cur[ord[0]]]
+ is the smallest line and will be next
+ output. */
+ register int i, j, t;
+
+ /* Allocate space for a saved line if necessary. */
+ if (unique)
+ {
+ savealloc = linelength;
+ saved.text = xmalloc (savealloc);
+ }
+
+ /* Read initial lines from each input file. */
+ for (i = 0; i < nfps; ++i)
+ {
+ initbuf (&buffer[i], mergealloc);
+ /* If a file is empty, eliminate it from future consideration. */
+ while (i < nfps && !fillbuf (&buffer[i], fps[i]))
+ {
+ xfclose (fps[i]);
+ --nfps;
+ for (j = i; j < nfps; ++j)
+ fps[j] = fps[j + 1];
+ }
+ if (i == nfps)
+ free (buffer[i].buf);
+ else
+ {
+ initlines (&lines[i], mergealloc / linelength + 1,
+ LINEALLOC / ((NMERGE + NMERGE) * sizeof (struct line)));
+ findlines (&buffer[i], &lines[i]);
+ cur[i] = 0;
+ }
+ }
+
+ /* Set up the ord table according to comparisons among input lines.
+ Since this only reorders two items if one is strictly greater than
+ the other, it is stable. */
+ for (i = 0; i < nfps; ++i)
+ ord[i] = i;
+ for (i = 1; i < nfps; ++i)
+ if (compare (&lines[ord[i - 1]].lines[cur[ord[i - 1]]],
+ &lines[ord[i]].lines[cur[ord[i]]]) > 0)
+ t = ord[i - 1], ord[i - 1] = ord[i], ord[i] = t, i = 0;
+
+ /* Repeatedly output the smallest line until no input remains. */
+ while (nfps)
+ {
+ /* If uniqified output is turned out, output only the first of
+ an identical series of lines. */
+ if (unique)
+ {
+ if (savedflag && compare (&saved, &lines[ord[0]].lines[cur[ord[0]]]))
+ {
+ xfwrite (saved.text, 1, saved.length, ofp);
+ putc ('\n', ofp);
+ savedflag = 0;
+ }
+ if (!savedflag)
+ {
+ if (savealloc < lines[ord[0]].lines[cur[ord[0]]].length + 1)
+ {
+ while (savealloc < lines[ord[0]].lines[cur[ord[0]]].length + 1)
+ savealloc *= 2;
+ saved.text = xrealloc (saved.text, savealloc);
+ }
+ saved.length = lines[ord[0]].lines[cur[ord[0]]].length;
+ bcopy (lines[ord[0]].lines[cur[ord[0]]].text, saved.text,
+ saved.length + 1);
+ if (lines[ord[0]].lines[cur[ord[0]]].keybeg != NULL)
+ {
+ saved.keybeg = saved.text +
+ (lines[ord[0]].lines[cur[ord[0]]].keybeg
+ - lines[ord[0]].lines[cur[ord[0]]].text);
+ }
+ if (lines[ord[0]].lines[cur[ord[0]]].keylim != NULL)
+ {
+ saved.keylim = saved.text +
+ (lines[ord[0]].lines[cur[ord[0]]].keylim
+ - lines[ord[0]].lines[cur[ord[0]]].text);
+ }
+ savedflag = 1;
+ }
+ }
+ else
+ {
+ xfwrite (lines[ord[0]].lines[cur[ord[0]]].text, 1,
+ lines[ord[0]].lines[cur[ord[0]]].length, ofp);
+ putc ('\n', ofp);
+ }
+
+ /* Check if we need to read more lines into core. */
+ if (++cur[ord[0]] == lines[ord[0]].used)
+ if (fillbuf (&buffer[ord[0]], fps[ord[0]]))
+ {
+ findlines (&buffer[ord[0]], &lines[ord[0]]);
+ cur[ord[0]] = 0;
+ }
+ else
+ {
+ /* We reached EOF on fps[ord[0]]. */
+ for (i = 1; i < nfps; ++i)
+ if (ord[i] > ord[0])
+ --ord[i];
+ --nfps;
+ xfclose (fps[ord[0]]);
+ free (buffer[ord[0]].buf);
+ free ((char *) lines[ord[0]].lines);
+ for (i = ord[0]; i < nfps; ++i)
+ {
+ fps[i] = fps[i + 1];
+ buffer[i] = buffer[i + 1];
+ lines[i] = lines[i + 1];
+ cur[i] = cur[i + 1];
+ }
+ for (i = 0; i < nfps; ++i)
+ ord[i] = ord[i + 1];
+ continue;
+ }
+
+ /* The new line just read in may be larger than other lines
+ already in core; push it back in the queue until we encounter
+ a line larger than it. */
+ for (i = 1; i < nfps; ++i)
+ {
+ t = compare (&lines[ord[0]].lines[cur[ord[0]]],
+ &lines[ord[i]].lines[cur[ord[i]]]);
+ if (!t)
+ t = ord[0] - ord[i];
+ if (t < 0)
+ break;
+ }
+ t = ord[0];
+ for (j = 1; j < i; ++j)
+ ord[j - 1] = ord[j];
+ ord[i - 1] = t;
+ }
+
+ if (unique && savedflag)
+ {
+ xfwrite (saved.text, 1, saved.length, ofp);
+ putc ('\n', ofp);
+ free (saved.text);
+ }
+}
+
+/* Sort the array LINES with NLINES members, using TEMP for temporary space. */
+
+static void
+sortlines (lines, nlines, temp)
+ struct line *lines, *temp;
+ int nlines;
+{
+ register struct line *lo, *hi, *t;
+ register int nlo, nhi;
+
+ if (nlines == 2)
+ {
+ if (compare (&lines[0], &lines[1]) > 0)
+ *temp = lines[0], lines[0] = lines[1], lines[1] = *temp;
+ return;
+ }
+
+ nlo = nlines / 2;
+ lo = lines;
+ nhi = nlines - nlo;
+ hi = lines + nlo;
+
+ if (nlo > 1)
+ sortlines (lo, nlo, temp);
+
+ if (nhi > 1)
+ sortlines (hi, nhi, temp);
+
+ t = temp;
+
+ while (nlo && nhi)
+ if (compare (lo, hi) <= 0)
+ *t++ = *lo++, --nlo;
+ else
+ *t++ = *hi++, --nhi;
+ while (nlo--)
+ *t++ = *lo++;
+
+ for (lo = lines, nlo = nlines - nhi, t = temp; nlo; --nlo)
+ *lo++ = *t++;
+}
+
+/* Check that each of the NFILES FILES is ordered.
+ Return a count of disordered files. */
+
+static int
+check (files, nfiles)
+ char *files[];
+ int nfiles;
+{
+ int i, disorders = 0;
+ FILE *fp;
+
+ for (i = 0; i < nfiles; ++i)
+ {
+ fp = xfopen (files[i], "r");
+ if (!checkfp (fp))
+ {
+ printf ("%s: disorder on %s\n", program_name, files[i]);
+ ++disorders;
+ }
+ }
+ return disorders;
+}
+
+/* Merge NFILES FILES onto OFP. */
+
+static void
+merge (files, nfiles, ofp)
+ char *files[];
+ int nfiles;
+ FILE *ofp;
+{
+ int i, j, t;
+ char *temp;
+ FILE *fps[NMERGE], *tfp;
+
+ while (nfiles > NMERGE)
+ {
+ t = 0;
+ for (i = 0; i < nfiles / NMERGE; ++i)
+ {
+ for (j = 0; j < NMERGE; ++j)
+ fps[j] = xfopen (files[i * NMERGE + j], "r");
+ tfp = xfopen (temp = tempname (), "w");
+ mergefps (fps, NMERGE, tfp);
+ xfclose (tfp);
+ for (j = 0; j < NMERGE; ++j)
+ zaptemp (files[i * NMERGE + j]);
+ files[t++] = temp;
+ }
+ for (j = 0; j < nfiles % NMERGE; ++j)
+ fps[j] = xfopen (files[i * NMERGE + j], "r");
+ tfp = xfopen (temp = tempname (), "w");
+ mergefps (fps, nfiles % NMERGE, tfp);
+ xfclose (tfp);
+ for (j = 0; j < nfiles % NMERGE; ++j)
+ zaptemp (files[i * NMERGE + j]);
+ files[t++] = temp;
+ nfiles = t;
+ }
+
+ for (i = 0; i < nfiles; ++i)
+ fps[i] = xfopen (files[i], "r");
+ mergefps (fps, i, ofp);
+ for (i = 0; i < nfiles; ++i)
+ zaptemp (files[i]);
+}
+
+/* Sort NFILES FILES onto OFP. */
+
+static void
+sort (files, nfiles, ofp)
+ char **files;
+ int nfiles;
+ FILE *ofp;
+{
+ struct buffer buf;
+ struct lines lines;
+ struct line *tmp;
+ int i, ntmp;
+ FILE *fp, *tfp;
+ struct tempnode *node;
+ int ntemp = 0;
+ char **tempfiles;
+
+ initbuf (&buf, sortalloc);
+ initlines (&lines, sortalloc / linelength + 1,
+ LINEALLOC / sizeof (struct line));
+ ntmp = lines.alloc;
+ tmp = (struct line *) xmalloc (ntmp * sizeof (struct line));
+
+ while (nfiles--)
+ {
+ fp = xfopen (*files++, "r");
+ while (fillbuf (&buf, fp))
+ {
+ findlines (&buf, &lines);
+ if (lines.used > ntmp)
+ {
+ while (lines.used > ntmp)
+ ntmp *= 2;
+ tmp = (struct line *)
+ xrealloc ((char *) tmp, ntmp * sizeof (struct line));
+ }
+ sortlines (lines.lines, lines.used, tmp);
+ if (feof (fp) && !nfiles && !ntemp && !buf.left)
+ tfp = ofp;
+ else
+ {
+ ++ntemp;
+ tfp = xfopen (tempname (), "w");
+ }
+ for (i = 0; i < lines.used; ++i)
+ if (!unique || i == 0
+ || compare (&lines.lines[i], &lines.lines[i - 1]))
+ {
+ xfwrite (lines.lines[i].text, 1, lines.lines[i].length, tfp);
+ putc ('\n', tfp);
+ }
+ if (tfp != ofp)
+ xfclose (tfp);
+ }
+ xfclose (fp);
+ }
+
+ free (buf.buf);
+ free ((char *) lines.lines);
+ free ((char *) tmp);
+
+ if (ntemp)
+ {
+ tempfiles = (char **) xmalloc (ntemp * sizeof (char *));
+ i = ntemp;
+ for (node = temphead.next; i > 0; node = node->next)
+ tempfiles[--i] = node->name;
+ merge (tempfiles, ntemp, ofp);
+ free ((char *) tempfiles);
+ }
+}
+
+/* Insert key KEY at the end of the list (`keyhead'). */
+
+static void
+insertkey (key)
+ struct keyfield *key;
+{
+ struct keyfield *k = &keyhead;
+
+ while (k->next)
+ k = k->next;
+ k->next = key;
+ key->next = NULL;
+}
+
+static void
+badfieldspec (s)
+ char *s;
+{
+ error (2, 0, "invalid field specification `%s'", s);
+}
+
+/* Handle interrupts and hangups. */
+
+static void
+sighandler (sig)
+ int sig;
+{
+#ifdef _POSIX_VERSION
+ struct sigaction sigact;
+
+ sigact.sa_handler = SIG_DFL;
+ sigemptyset (&sigact.sa_mask);
+ sigact.sa_flags = 0;
+ sigaction (sig, &sigact, NULL);
+#else /* !_POSIX_VERSION */
+ signal (sig, SIG_DFL);
+#endif /* _POSIX_VERSION */
+ cleanup ();
+ kill (getpid (), sig);
+}
+
+/* Set the ordering options for KEY specified in S.
+ Return the address of the first character in S that
+ is not a valid ordering option.
+ BLANKTYPE is the kind of blanks that 'b' should skip. */
+
+static char *
+set_ordering (s, key, blanktype)
+ register char *s;
+ struct keyfield *key;
+ enum blanktype blanktype;
+{
+ while (*s)
+ {
+ switch (*s)
+ {
+ case 'b':
+ if (blanktype == bl_start || blanktype == bl_both)
+ key->skipsblanks = 1;
+ if (blanktype == bl_end || blanktype == bl_both)
+ key->skipeblanks = 1;
+ break;
+ case 'd':
+ key->ignore = nondictionary;
+ break;
+ case 'f':
+ key->translate = fold_toupper;
+ break;
+#if 0
+ case 'g':
+ /* Reserved for comparing floating-point numbers. */
+ break;
+#endif
+ case 'i':
+ key->ignore = nonprinting;
+ break;
+ case 'M':
+ key->month = 1;
+ break;
+ case 'n':
+ key->numeric = 1;
+ break;
+ case 'r':
+ key->reverse = 1;
+ break;
+ default:
+ return s;
+ }
+ ++s;
+ }
+ return s;
+}
+
+void
+main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct keyfield *key = NULL, gkey;
+ char *s;
+ int i, t, t2;
+ int checkonly = 0, mergeonly = 0, nfiles = 0;
+ char *minus = "-", *outfile = minus, **files, *tmp;
+ FILE *ofp;
+#ifdef _POSIX_VERSION
+ struct sigaction oldact, newact;
+#endif /* _POSIX_VERSION */
+
+ program_name = argv[0];
+
+ parse_long_options (argc, argv, usage);
+
+ have_read_stdin = 0;
+ inittables ();
+
+ temp_file_prefix = getenv ("TMPDIR");
+ if (temp_file_prefix == NULL)
+ temp_file_prefix = "/tmp";
+
+#ifdef _POSIX_VERSION
+ newact.sa_handler = sighandler;
+ sigemptyset (&newact.sa_mask);
+ newact.sa_flags = 0;
+
+ sigaction (SIGINT, NULL, &oldact);
+ if (oldact.sa_handler != SIG_IGN)
+ sigaction (SIGINT, &newact, NULL);
+ sigaction (SIGHUP, NULL, &oldact);
+ if (oldact.sa_handler != SIG_IGN)
+ sigaction (SIGHUP, &newact, NULL);
+ sigaction (SIGPIPE, NULL, &oldact);
+ if (oldact.sa_handler != SIG_IGN)
+ sigaction (SIGPIPE, &newact, NULL);
+ sigaction (SIGTERM, NULL, &oldact);
+ if (oldact.sa_handler != SIG_IGN)
+ sigaction (SIGTERM, &newact, NULL);
+#else /* !_POSIX_VERSION */
+ if (signal (SIGINT, SIG_IGN) != SIG_IGN)
+ signal (SIGINT, sighandler);
+ if (signal (SIGHUP, SIG_IGN) != SIG_IGN)
+ signal (SIGHUP, sighandler);
+ if (signal (SIGPIPE, SIG_IGN) != SIG_IGN)
+ signal (SIGPIPE, sighandler);
+ if (signal (SIGTERM, SIG_IGN) != SIG_IGN)
+ signal (SIGTERM, sighandler);
+#endif /* !_POSIX_VERSION */
+
+ gkey.sword = gkey.eword = -1;
+ gkey.ignore = NULL;
+ gkey.translate = NULL;
+ gkey.numeric = gkey.month = gkey.reverse = 0;
+ gkey.skipsblanks = gkey.skipeblanks = 0;
+
+ files = (char **) xmalloc (sizeof (char *) * argc);
+
+ for (i = 1; i < argc; ++i)
+ {
+ if (argv[i][0] == '+')
+ {
+ if (key)
+ insertkey (key);
+ key = (struct keyfield *) xmalloc (sizeof (struct keyfield));
+ key->eword = -1;
+ key->ignore = NULL;
+ key->translate = NULL;
+ key->skipsblanks = key->skipeblanks = 0;
+ key->numeric = key->month = key->reverse = 0;
+ s = argv[i] + 1;
+ if (!digits[UCHAR (*s)])
+ badfieldspec (argv[i]);
+ for (t = 0; digits[UCHAR (*s)]; ++s)
+ t = 10 * t + *s - '0';
+ t2 = 0;
+ if (*s == '.')
+ for (++s; digits[UCHAR (*s)]; ++s)
+ t2 = 10 * t2 + *s - '0';
+ if (t2 || t)
+ {
+ key->sword = t;
+ key->schar = t2;
+ }
+ else
+ key->sword = -1;
+ s = set_ordering (s, key, bl_start);
+ if (*s)
+ badfieldspec (argv[i]);
+ }
+ else if (argv[i][0] == '-' && argv[i][1])
+ {
+ s = argv[i] + 1;
+ if (digits[UCHAR (*s)])
+ {
+ if (!key)
+ usage (2);
+ for (t = 0; digits[UCHAR (*s)]; ++s)
+ t = t * 10 + *s - '0';
+ t2 = 0;
+ if (*s == '.')
+ for (++s; digits[UCHAR (*s)]; ++s)
+ t2 = t2 * 10 + *s - '0';
+ key->eword = t;
+ key->echar = t2;
+ s = set_ordering (s, key, bl_end);
+ if (*s)
+ badfieldspec (argv[i]);
+ insertkey (key);
+ key = NULL;
+ }
+ else
+ while (*s)
+ {
+ s = set_ordering (s, &gkey, bl_both);
+ switch (*s)
+ {
+ case '\0':
+ break;
+ case 'c':
+ checkonly = 1;
+ break;
+ case 'k':
+ if (s[1])
+ ++s;
+ else
+ {
+ if (i == argc - 1)
+ error (2, 0, "option `-k' requires an argument");
+ else
+ s = argv[++i];
+ }
+ if (key)
+ insertkey (key);
+ key = (struct keyfield *)
+ xmalloc (sizeof (struct keyfield));
+ key->eword = -1;
+ key->ignore = NULL;
+ key->translate = NULL;
+ key->skipsblanks = key->skipeblanks = 0;
+ key->numeric = key->month = key->reverse = 0;
+ /* Get POS1. */
+ if (!digits[UCHAR (*s)])
+ badfieldspec (argv[i]);
+ for (t = 0; digits[UCHAR (*s)]; ++s)
+ t = 10 * t + *s - '0';
+ if (t)
+ t--;
+ t2 = 0;
+ if (*s == '.')
+ {
+ for (++s; digits[UCHAR (*s)]; ++s)
+ t2 = 10 * t2 + *s - '0';
+ if (t2)
+ t2--;
+ }
+ if (t2 || t)
+ {
+ key->sword = t;
+ key->schar = t2;
+ }
+ else
+ key->sword = -1;
+ s = set_ordering (s, key, bl_start);
+ if (*s && *s != ',')
+ badfieldspec (argv[i]);
+ else if (*s++)
+ {
+ /* Get POS2. */
+ for (t = 0; digits[UCHAR (*s)]; ++s)
+ t = t * 10 + *s - '0';
+ t2 = 0;
+ if (*s == '.')
+ {
+ for (++s; digits[UCHAR (*s)]; ++s)
+ t2 = t2 * 10 + *s - '0';
+ if (t2)
+ t--;
+ }
+ key->eword = t;
+ key->echar = t2;
+ s = set_ordering (s, key, bl_end);
+ if (*s)
+ badfieldspec (argv[i]);
+ }
+ insertkey (key);
+ key = NULL;
+ goto outer;
+ case 'm':
+ mergeonly = 1;
+ break;
+ case 'o':
+ if (s[1])
+ outfile = s + 1;
+ else
+ {
+ if (i == argc - 1)
+ error (2, 0, "option `-o' requires an argument");
+ else
+ outfile = argv[++i];
+ }
+ goto outer;
+ case 's':
+ stable = 1;
+ break;
+ case 't':
+ if (s[1])
+ tab = *++s;
+ else if (i < argc - 1)
+ {
+ tab = *argv[++i];
+ goto outer;
+ }
+ else
+ error (2, 0, "option `-t' requires an argument");
+ break;
+ case 'T':
+ if (s[1])
+ temp_file_prefix = ++s;
+ else if (i < argc - 1)
+ {
+ temp_file_prefix = argv[++i];
+ goto outer;
+ }
+ else
+ error (2, 0, "option `-T' requires an argument");
+ break;
+ case 'u':
+ unique = 1;
+ break;
+ case 'y':
+ /* Accept and ignore e.g. -y0 for compatibility with
+ Solaris 2. */
+ goto outer;
+ default:
+ fprintf (stderr, "%s: unrecognized option `-%c'\n",
+ argv[0], *s);
+ usage (2);
+ }
+ if (*s)
+ ++s;
+ }
+ }
+ else /* Not an option. */
+ {
+ files[nfiles++] = argv[i];
+ }
+ outer:;
+ }
+
+ if (key)
+ insertkey (key);
+
+ /* Inheritance of global options to individual keys. */
+ for (key = keyhead.next; key; key = key->next)
+ if (!key->ignore && !key->translate && !key->skipsblanks && !key->reverse
+ && !key->skipeblanks && !key->month && !key->numeric)
+ {
+ key->ignore = gkey.ignore;
+ key->translate = gkey.translate;
+ key->skipsblanks = gkey.skipsblanks;
+ key->skipeblanks = gkey.skipeblanks;
+ key->month = gkey.month;
+ key->numeric = gkey.numeric;
+ key->reverse = gkey.reverse;
+ }
+
+ if (!keyhead.next && (gkey.ignore || gkey.translate || gkey.skipsblanks
+ || gkey.skipeblanks || gkey.month || gkey.numeric))
+ insertkey (&gkey);
+ reverse = gkey.reverse;
+
+ if (nfiles == 0)
+ {
+ nfiles = 1;
+ files = &minus;
+ }
+
+ if (checkonly)
+ exit (check (files, nfiles) != 0);
+
+ if (strcmp (outfile, "-"))
+ {
+ for (i = 0; i < nfiles; ++i)
+ if (!strcmp (outfile, files[i]))
+ break;
+ if (i == nfiles)
+ ofp = xfopen (outfile, "w");
+ else
+ {
+ char buf[8192];
+ FILE *fp = xfopen (outfile, "r");
+ int cc;
+
+ tmp = tempname ();
+ ofp = xfopen (tmp, "w");
+ while ((cc = fread (buf, 1, sizeof buf, fp)) > 0)
+ xfwrite (buf, 1, cc, ofp);
+ if (ferror (fp))
+ {
+ error (0, errno, "%s", outfile);
+ cleanup ();
+ exit (2);
+ }
+ xfclose (ofp);
+ xfclose (fp);
+ files[i] = tmp;
+ ofp = xfopen (outfile, "w");
+ }
+ }
+ else
+ ofp = stdout;
+
+ if (mergeonly)
+ merge (files, nfiles, ofp);
+ else
+ sort (files, nfiles, ofp);
+ cleanup ();
+
+ /* If we wait for the implicit flush on exit, and the parent process
+ has closed stdout (e.g., exec >&- in a shell), then the output file
+ winds up empty. I don't understand why. This is under SunOS,
+ Solaris, Ultrix, and Irix. This premature fflush makes the output
+ reappear. --karl@cs.umb.edu */
+ if (fflush (ofp) < 0)
+ error (1, errno, "fflush", outfile);
+
+ if (have_read_stdin && fclose (stdin) == EOF)
+ error (1, errno, "-");
+ if (ferror (stdout) || fclose (stdout) == EOF)
+ error (1, errno, "write error");
+
+ exit (0);
+}
+
+static void
+usage (status)
+ int status;
+{
+ if (status != 0)
+ fprintf (stderr, "Try `%s --help' for more information.\n",
+ program_name);
+ else
+ {
+ printf ("\
+Usage: %s [OPTION]... [FILE]...\n\
+",
+ program_name);
+ printf ("\
+\n\
+ +POS1 [-POS2] start a key at POS1, end it before POS2\n\
+ -M compare (unknown) < `JAN' < ... < `DEC', imply -b\n\
+ -T DIRECT use DIRECTfor temporary files, not $TEMPDIR nor /tmp\n\
+ -b ignore leading blanks in sort fields or keys\n\
+ -c check if given files already sorted, do not sort\n\
+ -d consider only [a-zA-Z0-9 ] characters in keys\n\
+ -f fold lower case to upper case characters in keys\n\
+ -i consider only [\\040-\\0176] characters in keys\n\
+ -k POS1[,POS2] same as +POS1 [-POS2], but all positions counted from 1\n\
+ -m merge already sorted files, do not sort\n\
+ -n compare according to string numerical value, imply -b\n\
+ -o FILE write result on FILE instead of standard output\n\
+ -r reverse the result of comparisons\n\
+ -s stabilize sort by disabling last resort comparison\n\
+ -t SEP use SEParator instead of non- to whitespace transition\n\
+ -u with -c, check for strict ordering\n\
+ -u with -m, only output the first of an equal sequence\n\
+ --help display this help and exit\n\
+ --version output version information and exit\n\
+\n\
+POS is F[.C][OPTS], where F is the field number and C the character\n\
+position in the field, both counted from zero. OPTS is made up of one\n\
+or more of Mbdfinr, this effectively disable global -Mbdfinr settings\n\
+for that key. If no key given, use the entire line as key. With no\n\
+FILE, or when FILE is -, read standard input.\n\
+");
+ }
+ exit (status);
+}
diff --git a/gnu/usr.bin/sort/system.h b/gnu/usr.bin/sort/system.h
new file mode 100644
index 0000000..4aeaaea
--- /dev/null
+++ b/gnu/usr.bin/sort/system.h
@@ -0,0 +1,200 @@
+/* system-dependent definitions for textutils programs.
+ Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Include sys/types.h before this file. */
+
+#include <sys/stat.h>
+
+#ifdef STAT_MACROS_BROKEN
+#ifdef S_ISBLK
+#undef S_ISBLK
+#endif
+#ifdef S_ISCHR
+#undef S_ISCHR
+#endif
+#ifdef S_ISDIR
+#undef S_ISDIR
+#endif
+#ifdef S_ISFIFO
+#undef S_ISFIFO
+#endif
+#ifdef S_ISLNK
+#undef S_ISLNK
+#endif
+#ifdef S_ISMPB
+#undef S_ISMPB
+#endif
+#ifdef S_ISMPC
+#undef S_ISMPC
+#endif
+#ifdef S_ISNWK
+#undef S_ISNWK
+#endif
+#ifdef S_ISREG
+#undef S_ISREG
+#endif
+#ifdef S_ISSOCK
+#undef S_ISSOCK
+#endif
+#endif /* STAT_MACROS_BROKEN. */
+
+#ifndef S_ISREG /* Doesn't have POSIX.1 stat stuff. */
+#define mode_t unsigned short
+#endif
+#if !defined(S_ISBLK) && defined(S_IFBLK)
+#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
+#endif
+#if !defined(S_ISCHR) && defined(S_IFCHR)
+#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
+#endif
+#if !defined(S_ISDIR) && defined(S_IFDIR)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISREG) && defined(S_IFREG)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISFIFO) && defined(S_IFIFO)
+#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
+#endif
+#if !defined(S_ISLNK) && defined(S_IFLNK)
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#endif
+#if !defined(S_ISSOCK) && defined(S_IFSOCK)
+#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
+#endif
+#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */
+#define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB)
+#define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC)
+#endif
+#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */
+#define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK)
+#endif
+#if !defined(HAVE_MKFIFO)
+#define mkfifo(path, mode) (mknod ((path), (mode) | S_IFIFO, 0))
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifndef _POSIX_VERSION
+off_t lseek ();
+#endif
+
+#if defined(HAVE_STRING_H) || defined(STDC_HEADERS)
+#if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H)
+#include <memory.h>
+#endif
+#include <string.h>
+#ifndef index
+#define index strchr
+#endif
+#ifndef rindex
+#define rindex strrchr
+#endif
+/* Don't define bcopy; we need one that can handle overlaps. */
+#ifndef bzero
+#define bzero(s, n) memset ((s), 0, (n))
+#endif
+#ifndef bcmp
+#define bcmp(s1, s2, n) memcmp ((s1), (s2), (n))
+#endif
+#else
+#include <strings.h>
+char *memchr ();
+#endif
+
+#include <errno.h>
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+char *getenv ();
+extern int errno;
+#endif
+
+#if defined(HAVE_FCNTL_H) || defined(_POSIX_VERSION)
+#include <fcntl.h>
+#else
+#include <sys/file.h>
+#endif
+
+#if !defined(SEEK_SET)
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+#endif
+
+#ifndef _POSIX_SOURCE
+#include <sys/param.h>
+#endif
+
+/* Get or fake the disk device blocksize.
+ Usually defined by sys/param.h (if at all). */
+#if !defined(DEV_BSIZE) && defined(BSIZE)
+#define DEV_BSIZE BSIZE
+#endif
+#if !defined(DEV_BSIZE) && defined(BBSIZE) /* SGI */
+#define DEV_BSIZE BBSIZE
+#endif
+#ifndef DEV_BSIZE
+#define DEV_BSIZE 4096
+#endif
+
+/* Extract or fake data from a `struct stat'.
+ ST_BLKSIZE: Optimal I/O blocksize for the file, in bytes. */
+#ifndef HAVE_ST_BLKSIZE
+# define ST_BLKSIZE(statbuf) DEV_BSIZE
+#else /* HAVE_ST_BLKSIZE */
+/* Some systems, like Sequents, return st_blksize of 0 on pipes. */
+# define ST_BLKSIZE(statbuf) ((statbuf).st_blksize > 0 \
+ ? (statbuf).st_blksize : DEV_BSIZE)
+#endif /* HAVE_ST_BLKSIZE */
+
+#ifndef S_ISLNK
+#define lstat stat
+#endif
+
+#ifndef RETSIGTYPE
+#define RETSIGTYPE void
+#endif
+
+#include <ctype.h>
+
+#ifndef isascii
+#define isascii(c) 1
+#endif
+
+#ifdef isblank
+#define ISBLANK(c) (isascii (c) && isblank (c))
+#else
+#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+#ifdef isgraph
+#define ISGRAPH(c) (isascii (c) && isgraph (c))
+#else
+#define ISGRAPH(c) (isascii (c) && isprint (c) && !isspace (c))
+#endif
+
+#define ISPRINT(c) (isascii (c) && isprint (c))
+#define ISDIGIT(c) (isascii (c) && isdigit (c))
+#define ISALNUM(c) (isascii (c) && isalnum (c))
+#define ISALPHA(c) (isascii (c) && isalpha (c))
+#define ISCNTRL(c) (isascii (c) && iscntrl (c))
+#define ISLOWER(c) (isascii (c) && islower (c))
+#define ISPUNCT(c) (isascii (c) && ispunct (c))
+#define ISSPACE(c) (isascii (c) && isspace (c))
+#define ISUPPER(c) (isascii (c) && isupper (c))
+#define ISXDIGIT(c) (isascii (c) && isxdigit (c))
diff --git a/gnu/usr.bin/sort/version.c b/gnu/usr.bin/sort/version.c
new file mode 100644
index 0000000..64c62b19
--- /dev/null
+++ b/gnu/usr.bin/sort/version.c
@@ -0,0 +1,13 @@
+#ifdef HAVE_CONFIG_H
+#if defined (CONFIG_BROKETS)
+/* We use <config.h> instead of "config.h" so that a compilation
+ using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
+ (which it would do because it found this file in $srcdir). */
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
+#include "version.h"
+const char *version_string = "GNU textutils 1.9";
diff --git a/gnu/usr.bin/sort/version.h b/gnu/usr.bin/sort/version.h
new file mode 100644
index 0000000..63de4fd
--- /dev/null
+++ b/gnu/usr.bin/sort/version.h
@@ -0,0 +1 @@
+extern const char *version_string;
diff --git a/gnu/usr.bin/tar/COPYING b/gnu/usr.bin/tar/COPYING
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/gnu/usr.bin/tar/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/gnu/usr.bin/tar/ChangeLog b/gnu/usr.bin/tar/ChangeLog
new file mode 100644
index 0000000..7934f2b
--- /dev/null
+++ b/gnu/usr.bin/tar/ChangeLog
@@ -0,0 +1,1732 @@
+Thu Mar 25 13:32:40 1993 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * version.c: Released version 1.11.2.
+
+ * Makefile.in (dist): Do the link differently; some of the
+ files have changed filesystems which makes it more complex.
+
+ * Makefile.in (dist, shar): Use gzip instead of compress.
+
+ * create.c (dump_file): Test for curdev==-1, not curdev<0.
+ Some losing NFS systems give negative device numbers
+ sometimes.
+
+Thu Mar 25 11:55:15 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * level-0, level-1 (TAR_PART1): Use `--block-size', not just
+ `--block', which is now ambiguous.
+
+Wed Mar 24 22:12:51 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * backup-specs (TAR): New variable.
+
+ * level-0, level-1 (TAR_PART1): Get path of GNU tar from `TAR'
+ variable, don't hardcode it.
+
+Sat Mar 20 00:20:05 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * backup-specs (SLEEP_MESSAGE): put backslashes in front of nested
+ double quotes.
+
+ * level-0, level-1 (BACKUP_DIRS): Don't put in quotes.
+ (LOGFILE): Use sed to construct name, not awk.
+
+ * dump-remind (recipients): Replaced inefficient pipeline with a
+ single, simple sed script.
+ (volno): Deal with the possibility that VOLNO_FILE may not be
+ created yet.
+
+Fri Mar 19 15:05:15 1993 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * backup-specs (VOLNO_FILE): Removed abusive comment by Noah.
+
+ * buffer.c (new_volume): Write the global volume number to the
+ volno file before running the info script, so that the script
+ can look at it.
+
+Thu Mar 18 20:11:54 1993 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * Makefile.in (AUX): Include `dump-remind' in distribution.
+
+ * backup-specs (SLEEP_MESSAGE): New variable.
+ level-0, level-1: Use it instead of external `dont_touch' file.
+
+ * level-0, level-1: Put most of the script in () and pipe
+ everything from the subshell through tee -a $LOGFILE. Since you
+ really want most of the output to go to the logfile anyway, and
+ since all those pipelines were preventing one from getting the
+ exit status of most commands, this seems like the right idea.
+
+ * level-0, level-1 (LOGFILE): Use YYYY-MM-DD (all numeric) format
+ for log file name, since that makes the file names sortable in a
+ coherent way. Suffix should always be `level-n' where n is the
+ dump level. level-0 script was just using `-full' instead.
+
+ * level-0, level-1 (DUMP_LEVEL): New variable. Set to `0' or `1'
+ in each script as appropriate.
+
+ * level-0, level-1 (HOST): Renamed to `localhost' for clarity.
+ (host): renamed to `remotehost' for clarity.
+
+ * level-0, level-1 (startdate): New variable. Use it in Subject
+ line of mailed report.
+
+ * level-0, level-1: Fixed all instances where sed is called with a
+ script on the command line to use `-e' option.
+
+ * level-0, level-1: Don't try to call logfile.sed to filter
+ LOGFILE. It's not distributed with tar and was never really used
+ anyway.
+
+ * level-0, level-1: Put quotes around most variable names (barring
+ those that are known to intentionally contain text that should be
+ expanded into multiple words, like `TAR_PART1').
+
+ * level-0, level-1: Got rid of annoying trailing backslashes in awk
+ scripts. They were gratuitous. Made them a little more readable
+ by adding some whitespace.
+
+Wed Mar 17 10:30:58 1993 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * tar.c (describe, long_options): Changed --compress-block to
+ --block-compress.
+ (options): Fixed f_compress_block sanity check error message
+ to give the correct name of the option.
+
+Tue Mar 16 14:52:40 1993 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * extract.c (extract_archive): case LF_DIR: Do chown when
+ necessary. Don't bother jumping to set_filestat for
+ f_modified; repeat the chmod code here. Replace `break',
+ deleted on 2 September 1992.
+
+ * tar.c (describe, long_options, options): Added gzip options
+ and use-compress-program option.
+ * tar.h: Added new compression options.
+ * buffer.c (child_open, open_archive): Use new compression options.
+
+ * create.c (start_header): Only mask off high bits when
+ creating old-style archives.
+ * list.c (decode_header): Mask off potentially misleading
+ high bits from the mode when reading headers.
+
+Mon Mar 15 11:34:34 1993 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * extract.c (extract_archive): Put arguments in the right
+ order for error message.
+
+ * create.c (deal_with_sparse): if the last byte was null, we
+ didn't write it out.
+
+ * gnu.c, create.c, extract.c, diffarch.c, list.c throughout:
+ Replace malloc calls with ck_malloc and realloc with ck_realloc.
+
+ * tar.c (describe): Improve doc for -L.
+
+ * tar.c (name_next): Don't apply exclusion to explicitly named
+ files.
+
+ * tar.c (long_options, describe): Added new-volume-script as
+ an alias for info-script.
+
+ * extract.c (extract_archive): LF_DUMPDIR case; misplaced paren.
+
+ * extract.c (extract_archive): extract_file case, first if,
+ include space for null in namelen computation.
+
+ * extract.c (extract_sparse_file): Use value returned by write
+ to properly create error message.
+
+ * create.c (create_archive): Don't assume we have anything to
+ dump.
+
+ * buffer.c (open_archive): Set current_file_name for the
+ volume header so that verbose listings work properly.
+
+ * Makefile.in (realclean): Added getdate.c.
+
+Thu Jan 14 23:38:44 1993 David J. MacKenzie (djm@kropotkin.gnu.ai.mit.edu)
+
+ * tar.c: Include fnmatch.h after port.h to make sure we get our FNM_*
+ (e.g. on HPUX 8).
+
+Tue Nov 24 08:30:54 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu)
+
+ * tar.c (addname), gnu.c (read_dir_file): Use HAVE_GETCWD, not USG.
+
+ * port.h, rmt.h: Use HAVE_STRING_H, not USG.
+
+ * port.h: Add dir header decls.
+ * create.c, gnu.c: Use SYSNDIR, SYSDIR, and NDIR
+ instead of BSD42 and USG. Rename DP_NAMELEN to NLENGTH.
+ Use `struct dirent' instead of `struct direct'.
+ * create.c, gnu.c, tar.c: Remove dir header decls.
+
+Wed Nov 18 15:31:30 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu)
+
+ * tar.c: Change FNM_TARPATH to FNM_LEADING_DIR to match change
+ in fnmatch.[ch].
+
+Wed Oct 21 00:52:24 1992 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * level-0, level-1: put curly braces around variables for clarity.
+
+ * backup-specs (DUMP_REMIND_SCRIPT): define it (but commented out
+ so that distributed dump scripts won't use it by default).
+ level-0, level-1 (TAR_PART1): use --info-script if
+ DUMP_REMIND_SCRIPT is defined.
+ dump-remind: new file (intended as an example).
+
+Thu Oct 15 03:33:28 1992 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * level-0, level-1: remove $LOGFILE.tmp files before exiting.
+
+Fri Oct 2 00:28:01 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu)
+
+ * tar.c (describe): Fix some tab alignments.
+
+ * Makefile.in (SRC3): Add getdate.c, for systems without bison/yacc
+ (like MS-DOS).
+
+ * diffarch.c (diff_sparse_files): Add missing arg to fprintf calls.
+
+ * extract.c (extract_archive, restore_saved_dir_info),
+ buffer.c (child_open), list.c (decode_header, print_header):
+ Delete unused vars.
+
+ * port.c [__MSDOS__]: Have strstr, rename, and mkdir. Don't
+ define ck_pipe.
+
+ * buffer.c, tar.c (init_volume_number, closeout_volume_number),
+ create.c (write_long): Declare as void, not int, since they
+ don't return a value.
+
+Thu Sep 24 00:06:02 1992 Michael I Bushnell (mib@churchy.gnu.ai.mit.edu)
+
+ * level-0, level-1 (TAR_PART1): remove --atime-preserve
+ because of a total screw.
+
+Tue Sep 22 14:15:48 1992 Michael I Bushnell (mib@wookumz.gnu.ai.mit.edu)
+
+ * buffer.c (close_archive): Removed leftover `break' from when
+ this was a switch.
+
+Tue Sep 22 08:33:16 1992 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * create.c, port.h: indented #pragma directives with 1 space.
+
+Fri Sep 18 14:15:17 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * All source files: re indented using GNU indent.
+
+ * rtapelib.c (__rmt_read): Only read the amount left in the
+ buffer; otherwise a broken rmt server (which puts too much
+ data out) could overwrite past our buffer.
+
+Thu Sep 17 14:08:58 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * create.c: Throughout, use struct utimbuf rather than array
+ of longs.
+
+ * configure.in: Check for getpwuid and getgrgid.
+
+ * Makefile.in (SRC3, AUX): Move alloca.c to SRC3.
+ (OBJ3): Add @ALLOCA@.
+
+ * Makefile.in (getdate.c): Look in srcdir for getdate.y.
+
+ * buffer.c (close_archive): We can't check WTERMSIG
+ meaningfully unless we already know tha WIFSIGNALED is true.
+ (There is no guarantee it WTERMSIG will return zero when
+ WIFSIGNALED is false.)
+ * port.c (rmdir, mkdir): Check WIFSIGNALED rather than
+ WTERMSIG.
+
+ * Makefile.in (getdate.c): Use $(YACC) instead of `yacc'.
+
+Tue Sep 15 14:49:48 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * version.c: Released version 1.11.1.
+
+ * Makefile (AUX): Added NEWS.
+
+ * Makefile.in (rmt): Added $(LIBS).
+ * configure.in: Added tests for libraries needed on Solaris.
+
+ * mangle.c (extract_mangle): Null terminate link name for
+ losing archives missing it.
+
+ * Makefile.in: added target and rule for getdate.c: getdate.y;
+ some makes don't have one built in.
+
+Mon Sep 14 16:23:15 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * tar.c (options, main): Advise use of --help rather than
+ +help.
+
+ * create.c (write_long): Using hstat here is a Bad Idea, and
+ totally unnecessary at that.
+
+ * list.c (read_header): Compute both signed and normal
+ checksums.
+
+ * configure.in: Define BSD in the presence of /sdmach or
+ /../../mach.
+
+ * diffarch.c, buffer.c: Declare valloc as void* rather than
+ char*.
+
+ * Makefile.in: Don't install info files.
+
+ * configure.in: Check for malloc was scrambled.
+
+ * port.h: Undefine index and rindex if necessary; some
+ string.h's define them for us.
+
+ * tar.c (addname): Missing braces after if.
+ * gnu.c (read_dir_file): Missing braces after if.
+
+ * names.c: Add include of <stdio.h>,
+
+ * create.c (start_header): Set current_file_name so that
+ print_header (used for verbose create) works properly.
+ (dump_file): Set current_link_name when setting up symlink
+ and hardlink records.
+
+Fri Sep 11 01:05:52 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * fnmatch.[ch]: New files.
+ * wildmat.c: File removed.
+ * tar.c: Include fnmatch.h and use fnmatch instead of wildmat.
+ * Makefile.in, makefile.pc: Replace wildmat.o(bj) with fnmatch.
+
+Thu Sep 10 23:19:30 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * buffer.c, tar.c: Remove redundant decls of getenv, rindex.
+
+ * Makefile.in: Add uninstall target.
+ Define libdir instead of hardcoding /etc for installing rmt.
+
+Thu Sep 10 13:06:03 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * list.c (read_header): On second thought, that doesn't work
+ either, so just store the names in malloced areas. Sigh.
+
+ * NEWS: New file.
+ * README: Removed things that belong in NEWS; point to it.
+
+ * list.c (read_header): current_file_name and
+ current_link_name need to be set to the arrays in head rather
+ than header; header is the actual read buffer and will change.
+
+ * extract.c (extract_archive):
+ * buffer.c (new_volume): `#' directives need to start in
+ column 1.
+
+Thu Sep 10 06:09:18 1992 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * level-0, level-1 (TAR_PART1): put --atime-preserve inside quotes.
+
+Wed Sep 9 13:34:26 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * Makefile.in (AUX): Add getpagesize.h.
+ (AUX): Comment out manuals.
+ (all): Comment out dependency on tar.info.
+
+ * version.c: Release of version 1.11.
+
+ * level-0, level-1 (TAR_PART1): Use --atime-preserve.
+
+ * Makefile, configure.in: Arrange to use local malloc on HP-UX.
+
+ * port.h Use the canonical Autoconf chunk for alloca instead
+ of just looking for gcc.
+
+Wed Sep 9 03:16:58 1992 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * port.h: If compiling with gcc, use __builtin_alloca.
+
+Tue Sep 8 16:13:41 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * extract.c: Removed long name support from here.
+ * list.c (read_header): Understand and skip longname/longlink
+ headers here. Names for current file are stored in new global
+ variables. All source files except create.c changed to refer
+ to current_file_name and current_link_name instead of fields
+ directly from the current header.
+
+Thu Sep 3 12:41:08 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * create.c (write_long): New function.
+ (dump_file): When writing link records or symlink records, use
+ new write_long function instead of mangling when the link
+ target is too long.
+ (start_header): Use write_long instead of mangling for long
+ names.
+ * extract.c (saverec): Recognize LF_LONGNAME and LF_LONGLINK.
+ (saverec): Throughout, use longname and longlink if they are set.
+
+Wed Sep 2 14:41:13 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * mangle.c: This is now deprecated; retain extract_mangle for
+ backward compatability.
+
+ * list.c (print_header): patch from Chris Arthur to prevent
+ printing 0 when the gid or uid is null.
+
+ * list.c (decode_header): patch from Chris Arthur to use the
+ gid field when the gid is empty, and similarly for uid.
+
+ * extract.c: saved_dir_info, saved_dir_info_head: new type and
+ var.
+ (extract_archive): When extracting directories, now save info
+ in saved_dir_info_head.
+ (restore_saved_dir_info): New function.
+ * list.c (read_and): Call restore_saved_dir_info at the end of
+ the run.
+ This patch is from Chris Arthur (csa@pennies.sw.stratus.com).
+
+Mon Aug 31 15:39:55 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * create.c (create_archive): If there are no names specified,
+ write nothing on the archive instead of dumping ".".
+
+ * buffer.c (open_archive): Useful error message.
+
+ * tar.c, tar.h: Recognize f_atime_preserve.
+ * create.c (dump_file): Implement f_atime_preserve.
+
+ * rmt.h (_remdev): Don't require /dev/ to be in remote archive
+ names; obey new force-local flag.
+ * tar.c, tar.h: Implement new force-local flag.
+
+ * tar.c (describe): same-owner and same-order were confused.
+
+ * create.c (dump_file): Check for toplevel had sense reversed.
+
+ * buffer.c (new_archive): Don't free old_name...when these
+ come from the command line, they aren't malloced, and it isn't
+ important to save this trivial amount of memory.
+
+ * tar.h: replace ar_file with ar_files, n_ar_files,
+ cur_ar_files.
+ * buffer.c (open_archive): multi-volume compressed archives
+ never worked; give an appropriate error. Change open of
+ ar_file to open of ar_files[0].
+ (writeerror, readerror, flush_archive): use
+ ar_files[cur_ar_file] instead of ar_file.
+ (new_archive): Necessary changes to support ar_files.
+ * tar.c (options): handle multiple tape drive arguments.
+
+Fri Aug 28 17:42:13 1992 Michael I Bushnell (mib@wookumz.gnu.ai.mit.edu)
+
+ * list.c (decode_header), create.c (start_header), tar.h (TMAGIC):
+ Undo djm's changes below; tar does not support the final
+ Posix.1 format; it's bad to make it look like it does.
+
+Sun Jul 19 02:13:46 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * port.h: Try to prevent redefining major.
+ * port.c: HAVE_BZERO -> minix. Fix a typo.
+
+ * list.c (decode_header): Recognize the final POSIX.1 magic as
+ well as the early draft magic for ustar.
+ * create.c (start_header): Create a final POSIX.1 magic string
+ instead of an early draft string for ustar.
+ * tar.h (TMAGIC): Remove the trailing blanks.
+
+ * rmt.c, rtapelib.c: Use POSIX and STDC headers if available.
+ * rmt.h: Declare the external functions defined in rtapelib.c.
+
+Tue Jul 14 00:44:37 1992 David J. MacKenzie (djm@apple-gunkies.gnu.ai.mit.edu)
+
+ * pathmax.h: New file.
+ * port.h: Include it.
+ * create.c (create_archive): Allocate PATH_MAX instead of
+ NAME_MAX for temporary buffer so we don't have to figure out
+ what NAME_MAX is (portably).
+
+Fri Jul 10 08:30:42 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * gnu.c (collect_and_sort_names): write_dir_file has no argument.
+
+ * level-0, level-1: Avoid silly Sun awk lossage.
+
+Mon Jul 6 20:11:32 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * port.c (rename): If unlinking the source at the end fails,
+ unlink the destination instead to avoid leaving a mess.
+
+Fri Jul 3 15:16:42 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * buffer.c, diffarch.c, update.c, rtapelib.c: Change NO_MTIO to
+ HAVE_SYS_MTIO_H.
+
+ * port.c, tar.h: Change FOO_MISSING to HAVE_FOO.
+
+Tue Jun 23 23:39:02 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu)
+
+ * rmt.c: Add #ifdefs to work on ISC.
+
+Wed May 20 00:12:27 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu)
+
+ * port.h: Define major, minor, makedev if the system doesn't.
+
+Wed May 13 21:16:38 1992 Michael I Bushnell (mib@apple-gunkies.gnu.ai.mit.edu)
+
+ * gnu.c (add_dir_name): Store legitimate value into
+ dir_contents when get_dir_contents returns NULL.
+
+Thu May 7 23:44:35 1992 Michael I Bushnell (mib@apple-gunkies.gnu.ai.mit.edu)
+
+ * gnu.c (add_dir_name): Check for return of NULL from get_dir_contents;
+ see djm's change of Fri Jul 26 01:12:58 1991.
+
+Mon May 4 22:50:57 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu)
+
+ * tar.h: Make comments for option names say -- instead of +.
+
+Thu Apr 30 03:09:16 1992 Noah Friedman (friedman@nutrimat.gnu.ai.mit.edu)
+
+ * level-1: Added `$' before VOLNO_FILE in definition of TAR_PART1.
+ Added line to remove $VOLNO_FILE from any previous dump before
+ starting.
+
+ * level-0, level-1: Change long options to use `--' instead of `+'
+ (support for `+' will go away soon)
+
+Wed Apr 29 14:23:10 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * tar.c, tar.t: Added +volno-file option.
+ buffer.c: New functions init_volume_number,
+ closeout_volume_number.
+ tar.c (main): Call new functions in the right place.
+
+ * buffer.c (fl_write, fl_read): Mod to allow losing tape
+ drives which use short counts to indicate end of tape
+ correctly handle the multi-tape stuff. The read half won't
+ co-exist with f_reblock; there's no way to fix that, of
+ course.
+
+ * tar.c, tar.h: Added new option +show-omitted-dirs, from
+ Karl Berry.
+ list.c (read_and): Implemented show-omitted-dirs.
+
+ * tar.c, tar.h: Added new option +checkpoint.
+ buffer.c (fl_read, fl_write): Implemented +checkpoint lazily.
+
+ * create.c (dump_file): Added toplevel argument; some devices
+ can be negative, so the old method was bogus. All callers
+ changed.
+
+ * tar.c, tar.h: Added new option +ignore-failed-read.
+ create.c (dump_file): Implemented +ignore-failed-read.
+
+ * create.c (finish_sparse_file): Commented out debugging printf.
+
+ * tar.c, tar.h: Added new option +remove-files to delete files
+ after they are added to the archive.
+ create.c (dump_file): Implemented +remove-files for
+ everything but directories. I don't think they need it.
+
+Tue Apr 28 13:21:42 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * create.c: (dump_file): save_name needs to be set equal to p,
+ not something inside the header, because the header changes at
+ the first buffer flush.
+
+Fri Apr 24 10:41:13 1992 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * create.c: Djm incorrectly moved the include of port.h to
+ precede the include of sys/file.h; restored.
+
+ * tar.c (main): Cases CMD_EXTRACT and CMD_LIST: declare error
+ string with const.
+
+ * gnu.c (collect_and_sort_names): Leave if around
+ write_dir_file in place.
+
+Wed Apr 22 02:16:14 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu)
+
+ * rtapelib.c: SIGTYPE -> RETSIGTYPE.
+
+Mon Mar 9 22:42:05 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * rtapelib.c: Reformat and make comments more complete.
+ Rename a few variables for clarity.
+
+Thu Mar 5 14:07:34 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * tar.c (describe): Document long options as starting with --.
+
+Thu Jan 23 22:54:41 1992 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * tar.c (options): Check get_date return value for error indication.
+
+Tue Dec 24 00:03:03 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * tar.c, gnu.c, extract.c, create.c, port.h, rmt.h: Change
+ POSIX ifdefs to HAVE_UNISTD_H and _POSIX_VERSION.
+
+Fri Dec 20 13:50:38 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
+
+ * testpad.c (main): flush stderr so perror and fprintf
+ cooperate right.
+
+Wed Dec 18 16:52:42 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * port.h: Check MAJOR_IN_MKDEV and MAJOR_IN_SYSMACROS to find
+ where to get major, minor and makedev.
+ * create.c, list.c, update.c: Don't check USG to include
+ sys/sysmacros.h.
+
+Thu Dec 12 21:57:10 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
+
+ * mangle.c (extract_mangle): Correctly null terminate name of
+ link target.
+
+Thu Nov 21 07:44:18 1991 Michael I Bushnell (mib at nutrimat)
+
+ * create.c (dump_file, at start of ISREG output loop): use
+ filename from header instead of real name to make sure that we
+ get the mangled version and not one that is too long and
+ overflows buffers.
+
+Sat Nov 16 01:37:45 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * tar.h: Use new criteria for STDC version of msg.
+
+Sat Nov 2 21:31:57 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * create.c, gnu.c, tar.c: Use DIRENT instead of NDIR to select
+ between dirent.h and ndir.h for USG.
+
+ * port.c: Rename WANT_FOO to FOO_MISSING to make sharing code
+ and configure script with other utilities easier. Use
+ VPRINTF_MISSING and DOPRNT_MISSING instead of FOO_MSG to
+ select error reporting routines.
+
+Thu Oct 17 20:19:02 1991 Michael I Bushnell (mib at churchy.gnu.ai.mit.edu)
+
+ * level-0: Repair damage from previous mod: stdin to rsh must
+ be the terminal or tar's questions lose.
+
+Sat Aug 31 15:05:27 1991 Noah Friedman (friedman at nutrimat.gnu.ai.mit.edu)
+
+ * level-0: Fixed several syntax errors associated with
+ stdout/stderr redirection.
+ Made sure remote host executes commands from sh where redirection
+ is necessary, since root's shell might be csh in some places and
+ the redirect syntax differs.
+
+Thu Aug 29 00:54:01 1991 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * tar.c (long_options). Fixed info-script long option.
+
+Mon Aug 26 16:53:50 1991 David J. MacKenzie (djm at pogo.gnu.ai.mit.edu)
+
+ * configure, Makefile.in: Only put $< in Makefiles if VPATH
+ is being used, because older makes don't understand it.
+
+Mon Aug 19 01:47:57 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * create.c: Indent '#pragma alloca' so non-ANSI compilers
+ don't choke on it.
+
+Wed Aug 14 14:10:43 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
+
+ * list.c (UGSWIDTH): Increase from 11 (sort of like Unix tar) to
+ 18, so that with normal user and group names of <= 8 chars,
+ the columns never shift in a tar -t listing.
+
+Fri Aug 2 00:41:08 1991 David J. MacKenzie (djm at apple-gunkies)
+
+ * Makefile.in (dist): Include texinfo.tex and tar.info*.
+ (install): Install tar.info*.
+ * configure: Set INSTALLDATA.
+
+ * configure: Create config.status. Remove it and Makefile if
+ interrupted while creating them.
+
+ * configure: Check for +srcdir etc. arg and look for
+ Makefile.in in that directory. Set VPATH if srcdir is not `.'.
+ * Makefile.in: Add `prefix'.
+ (tar.info): New target.
+
+Tue Jul 30 17:08:04 1991 David J. MacKenzie (djm at apple-gunkies)
+
+ * configure: NEED_TZSET has become FTIME_MISSING.
+
+Mon Jul 29 19:23:10 1991 David J. MacKenzie (djm at wombat.gnu.ai.mit.edu)
+
+ * port.c [F_CHSIZE]: Additional version.
+
+Sat Jul 27 22:27:47 1991 David J. MacKenzie (djm at wombat.gnu.ai.mit.edu)
+
+ * rmt.h: Clean up ifdefs.
+
+ * makefile.pc: Fix typo.
+ port.h: Change MSDOS to __MSDOS__.
+ [__MSDOS__]: Define off_t. Include io.h and not sys/param.h.
+ [__TURBOC__]: Use void * and don't define const.
+
+Fri Jul 26 01:12:58 1991 David J. MacKenzie (djm at bleen)
+
+ * buffer.c: Rename `eof' to `hit_eof' to avoid conflict with an
+ MSDOS function.
+ * gnu.c (get_dir_contents): Return NULL, not "\0\0\0\0", on error.
+ * diffarch.c (diff_archive): Open files in binary mode.
+ Don't use or free a non-malloc'd return value from get_dir_contents.
+ * msd_dir.c [__TURBOC__]: Include stdlib.h.
+ * rmt.h: lseek returns off_t, not long.
+
+ * tar.c (describe): -X is +exclude-from, not +exclude.
+ (names_notfound): Free memory only if amiga, not !unix.
+
+ * tar.h, tar.c: Add +null option to make -T read
+ null-terminated filenames (such as those produced by GNU find
+ -print0), and disable -C option.
+ This guarantees that odd filenames will get archived.
+ * tar.c (read_name_from_file): New function.
+ (name_next): Call it instead of fgets.
+
+Wed Jul 24 11:17:48 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * create.c [_AIX]: Declare alloca.
+
+ * buffer.c (open_archive): Check for successful open before,
+ not after, fstatting the fd.
+
+Tue Jul 23 20:51:31 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * configure: Only define BSD42 if sys/file.h exists.
+ If alloca is missing and /usr/ucblib exists (SVR4), use it
+ instead of -lPW.
+
+ * port.h [!__STDC__]: #define const.
+ * gnu.c (dirent_cmp): Fix args to agree with ANSI C prototype.
+ * create.c: Declare ck_realloc.
+ * gnu.c, diffarch.c: Move check for symlinks to after port.h include.
+
+Sat Jul 20 00:03:54 1991 David J. MacKenzie (djm at apple-gunkies)
+
+ * msd_dir.[ch]: Use POSIX-style `struct dirent' instead of
+ `struct direct'.
+ * create.c, gnu.c, tar.c: Adjust callers.
+
+Thu Jul 18 00:05:01 1991 David J. MacKenzie (djm at bleen)
+
+ * port.c (ck_malloc, ck_realloc): Return PTR, not char *.
+ * gnu.c, create.c, tar.c: Fix decls.
+
+ * port.c: Don't use the preprocessor to guess missing
+ functions on Unix; let configure do it.
+ [WANT_GETWD] (getwd): Function removed; not needed because
+ getcwd is used if needed.
+ * gnu.c, tar.c: Use getcwd if POSIX.
+
+ * rtapelib.c: Use SIGTYPE instead of testing SIGNAL_VOID.
+ Default to void (more common these days) instead of int.
+
+ * tar.c, gnu.c, mangle.c: Remove VOIDSTAR defn. Use PTR instead.
+ * port.h: Define PTR.
+
+ * gnu.c, tar.c [__MSDOS__ || USG]: Remove incorrect getcwd
+ decl; put correct one in port.h [!POSIX].
+
+ * tar.c (describe): Print on stdout instead of stderr; it's
+ not so much a usage message (since you have to ask for it
+ explicitly) as on-line help, and you really need to be able to
+ page it because it's more than a screen long.
+
+ * Make #ifdefs for sys/file.h or fcntl.h, directory header,
+ sys/mtio.h consistent between files. Use NO_MTIO instead of
+ tricks with USG and HAVE_MTIO and NO_RMTIOCTL.
+ * Move decls of ANSI C and POSIX functions to port.h and
+ use standard headers to declare them if available
+ [STDC_HEADERS or POSIX].
+ * Add many missing function declarations and return types.
+ * Some places used __MSDOS__, some MSDOS; standardize on __MSDOS__.
+ * Change S_IF macros to S_IS for POSIX.
+ * port.h: Define appropriate S_IS macros if missing.
+ * port.h: Rename macros for testing exit status to conform to
+ POSIX; use the system's versions if available [POSIX].
+ * Use POSIX PATH_MAX and NAME_MAX instead of MAXPATHLEN and MAXNAMLEN.
+ * port.h: Define PATH_MAX and NAME_MAX.
+ * create.c, gnu.c, tar.c: Use ck_malloc and free instead of
+ auto arrays of size PATH_MAX or NAME_MAX, since with pathconf
+ they might not be constants.
+ * Move all definitions of O_* to port.h to reduce redundancy.
+ * Make all source files that now need to include port.h do so.
+ * port.c: Remove #undefs of WANT_* so you can use -DWANT_*
+ when compiling, instead of having to edit port.c.
+ [WANT_DUMB_GET_DATE] (get_date): Function removed.
+ Even systems without bison can get bison output and compile it.
+ [WANT_STRING] (index, rindex, bcopy, bzero, bcmp): Functions
+ removed; the translation is now done by macros in port.h.
+ * wildmat.c (wildmat): Use POSIX.2 '!' instead of '^' to negate
+ character classes.
+
+Mon Jul 15 13:47:45 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * testpad.c (main): Return type void.
+
+ * port.c [WANT_STRING]: Don't include memory.h if NO_MEMORY_H.
+
+ * create.c (dump_file) [AIX]: Fix typo, `allocate' for `alloca'.
+ * gnu.c (collect_and_sort_names): Move misplaced brace out of #ifdef.
+ From: Minh Tran-Le <TRANLE@intellicorp.com>.
+
+ * configure: Also look in sys/signal.h for signal decl.
+
+Wed Jul 10 01:42:55 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * Rename rtape_server.c to rmt.c and rtape_lib.c to rtapelib.c.
+
+ * configure, Makefile.in: $(INSTALLPROG) -> $(INSTALL).
+
+Tue Jul 9 01:38:37 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * Most files: Refer to GPL version 2.
+ * COPYING: Use version 2.
+
+ * port.c [__TURBOC__] (utime): New function.
+
+ * xmalloc: New function (just calls ck_malloc), for alloca.c
+ and bison.simple (in getdate.y output).
+
+ * Makefile.in (AUX): Include alloca.c and tcexparg.c, a
+ command line globber for Turbo C.
+
+Mon Jul 8 14:30:52 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
+
+ * testpad.c: Open and write to testpad.h instead of stdout,
+ because some MS-DOS makes (Borland's at least) can't do
+ redirection in commands.
+ * Makefile.in: Don't redirect testpad output.
+
+Mon Jul 8 12:56:35 1991 Michael I Bushnell (mib at churchy.gnu.ai.mit.edu)
+
+ * buffer.c (fl_read): Missing \n in printf.
+
+Mon Jul 8 03:40:28 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
+
+ * create.c, extract.c, gnu.c, diffarch.c, tar.c: Comment out
+ unused variables.
+
+ * tar.c (options): Cast get_date arg to VOIDSTAR instead of
+ `struct timeb *', since on some non-BSD systems the latter is
+ undefined.
+
+Sat Jul 6 04:53:14 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
+
+ * Replace Makefile with configure, Makefile.in, and makefile.pc.
+ Update README with current compilation instructions.
+
+ * port.c [WANT_RENAME] (rename): New function.
+
+Wed Jul 3 18:10:52 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
+
+ * testpad.c (main): Avoid warning from some compilers on array
+ address.
+
+ * rtape_server.c (sys_errlist): Should be declared extern.
+
+Mon Jul 1 14:14:06 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
+
+ * Release of version 1.10; appropriate changes to README.
+
+ * create.c: Removed printf's about sparse files.
+
+ * Fix a misplaced quote in level-0 and change some >& into
+ 2>&1.
+
+Fri Jun 21 23:04:31 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
+
+ * list.c (skip_extended_headers): Userec was being called in
+ the wrong place.
+
+Thu Jun 20 19:10:35 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
+
+ * tar.h: Use ANSI prototypes for msg and msg_perror if
+ STDC_MSG is defined, even if BSD42 is also.
+
+ * Makefile: Replace DESTDIR with bindir.
+ (install): Don't install tar.texinfo. There's no standard
+ place for texinfo files, and /usr/local/man is inappropriate.
+ Add TAGS, distclean, and realclean targets and SHELL= line.
+
+ * version.c: Move old change history to bottom of ChangeLog.
+
+Wed Jun 12 12:43:58 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
+
+ * rtape_lib.c (__rmt_write): #ifdef should reference
+ SIGNAL_VOID, not USG.
+
+Wed Jun 5 14:57:11 1991 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * tar.c (name_match, addname): Ugly hack to handle -C without
+ any files specified.
+ tar.h (struct name): New field for ugly hack.
+
+Mon Jun 3 14:46:46 1991 Michael I Bushnell (mib@geech.gnu.ai.mit.edu)
+
+ * testpad.c: New file to determine if we need special padding
+ in struct header in tar.h.
+
+ * tar.h (struct header): include padding if necessary, include
+ testpad.h.
+
+ * Makefile: rules to create testpad.h, etc.
+
+Wed May 22 16:02:35 1991 Michael I Bushnell (mib@churchy.gnu.ai.mit.edu)
+
+ * tar.c (options): -L takes an argument.
+
+ * rtape_lib.c (__rmt_open): add /usr/bin/nsh to the list of
+ remote shell programs.
+
+ * create.c: define MAXPATHLEN if we don't get it from a system
+ header file.
+
+ * create.c (deal_with_sparse): return a real return value if
+ we can't open the file.
+
+ * tar.c (long_options): +newer takes an argument.
+ (describe): fix printing in various trivial ways
+
+Tue May 21 17:15:19 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
+
+ * tar.c (long_options): +get and +concatentate don't require arguments
+
+Mon May 20 15:55:30 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
+
+ * create.c (write_eot): Don't try and write an EOF if we are
+ already at one.
+
+ * port.c (strstr): Looking for null string should return zero.
+
+Sun May 19 22:30:10 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
+
+ * tar.c (options): -l doesn't take an argument
+
+ * Makefile: minor fix for SGI 4D defines from torda@scum.ethz.ch
+
+ * rtape_server.c (main.c): Suggested mod for 386/AIX from
+ Minh Tran-Le. I'm suspicious about this one.
+
+ * create.c (dump_file): Mods from Minh Tran-Le for hidden
+ files on AIX.
+ gnu.c (collect_and_sort_name, get_dir_contents): AIX hidden file mod.
+
+ * tar.c: (name_next): Mod from David Taylor to allow -C inside
+ a file list given to -T.
+
+ * Makefile: Comment describing presence of USE_REXEC.
+
+ * extract.c (extract_archive, case LF_SPARSE): zero check for
+ last element on numbytes needs to look at value after
+ converted from octal.
+
+ * port.c: Don't always demand strstr, check for HAVE_STRSTR
+ instead.
+ Makefile: Comment describing presence of HAVE_STRSTR option.
+
+Sun May 19 18:39:48 1991 David J. MacKenzie (djm at churchy.gnu.ai.mit.edu)
+
+ * port.c (get_date): Renamed from getdate, to avoid SVR4 conflict.
+ * tar.c: Call get_date instead of getdate.
+
+Fri May 10 02:58:17 1991 Noah Friedman (friedman at nutrimat)
+
+ * tar.c: added "\n\" to the end of some documentation strings
+ where they were left off.
+
+Thu May 9 17:28:54 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
+
+ * Makefile: added level-0, level-1, and backup-specs to AUX.
+ * version.c: changed to 1.10 beta.
+ * README: updated for 1.10 beta release.
+
+Tue Apr 2 12:04:54 1991 Michael I Bushnell (mib at godwin)
+
+ * create.c (dump_file): HPUX's st_blocks is in 1024 byte units
+ instead of 512 like the rest of the world, so I special cased
+ it.
+ * tar.c: Undo Noah's changes.
+
+Mon Apr 1 17:49:28 1991 Noah Friedman (friedman at wookumz.gnu.ai.mit.edu)
+
+ (This ought to be temporary until things are fixed properly. )
+
+ * tar.c: (struct option long_options): flag for "sparse" zero if
+ compiling under hpux.
+ tar.c: (functon options): case 'S' is a no-op if compiling under
+ hpux.
+
+Sat Mar 30 12:20:41 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
+
+ * tar.h: new variable tape_length.
+
+ * tar.c (options): add new option +tape-length / -L.
+
+ * buffer.c (fl_write): Turn #ifdef TEST code for limited tape
+ length on always, for tape-length option.
+
+ * create.c (dump_file): avoid apollo lossage where S_IFIFO == S_IFSOCK.
+
+ * buffer.c: include regex.h
+ * buffer.c (fl_read, open_archive): Use regex routines for
+ volume header match.
+ * xmalloc.c: removed file; wasn't necessary.
+ * tar.c: (main) use ck_malloc instead of xmalloc.
+
+Thu Mar 28 04:05:05 1991 Noah Friedman (friedman at goldman)
+
+ * regex.c, regex.o: New links.
+ * tar.c: include regex.h.
+ * Makefile (OBJ2): Add regex.o.
+ (regex.o, tar.o): Depend on regex.h
+ (SRC2, AUX): Add the new files.
+
+Sat Mar 23 15:39:42 1991 Noah Friedman (friedman at wookumz.gnu.ai.mit.edu)
+
+ * Makefile: added default flags and options for compiling under
+ hpux.
+
+ * Added files alloca.c and xmalloc.c
+
+Sat Mar 23 14:35:31 1991 Michael I Bushnell (mib at geech.gnu.ai.mit.edu)
+
+ * port.c: Define WANT_VALLOC in HPUX.
+
+Fri Mar 15 06:20:15 1991 David J. MacKenzie (djm at geech.ai.mit.edu)
+
+ * rtape_lib.c: If USG and not HAVE_MTIO, define NO_RMTIOCTL
+ automatically.
+ (_rmt_rexec): Temporarily re-open stdin and stdout to
+ /dev/tty, to guarantee that rexec() can prompt and read the
+ login name and password from the user.
+ From pascal@cnam.cnam.fr (Pascal Meheut).
+ * Makefile: Mention -DUSE_REXEC.
+
+Fri Mar 8 20:15:11 1991 Michael I Bushnell (mib at wookumz.ai.mit.edu)
+
+ * tar.h, Makefile: Makefile CPP macro HAVE_SIZE_T might be
+ useful for some people.
+
+ * gnu.c: lstat->stat define where appropriate
+
+ * buffer.c (fl_write): keep track of amount written for +totals.
+ * tar.c, tar.h: set flag f_totals from +totals option
+ * tar.h (f_totals, tot_written): new variables
+ * tar.c (main): print total written with CMD_CREATE
+
+ * tar.c (main): return appropriate exit status
+
+Thu Jan 17 00:50:21 1991 David J. MacKenzie (djm at apple-gunkies)
+
+ * port.c: Remove a spurious `+' between functions (a remnant
+ of a context diff, apparently).
+
+Wed Jan 9 19:43:59 1991 Michael I Bushnell (mib at pogo.ai.mit.edu)
+
+ * create.c (where_is_data): Rewritten to be better, and then
+ #ifdef-ed out.
+ (deal_with_sparse): Severly pruned. Now we write or don't
+ write only complete blocks, not worrying about partial blocks.
+ This simplifies calculations, removes bugs, and elides the
+ second scan through the block. The first was zero_record, the
+ second was where_is_data.
+
+Mon Jan 7 17:13:29 1991 Michael I Bushnell (mib at wookumz.ai.mit.edu)
+
+ * create.c (deal_with_sparse): Second computation (for short
+ reads) of numbytes increment had subtraction backwards.
+ Need to handle calling where_is_data better when we did a
+ short read (it might go past the end of the read), also, set
+ sparsearray[...].offset in this case too.
+
+Fri Jan 4 12:24:38 EST 1991 Jay Fenlason (hack@ai.mit.edu)
+
+ * buffer.c Return a special error code if the archive you're
+ trying to read starts with a different label than the one specified
+ on the command line.
+
+Wed Jan 2 12:05:21 EST 1991 Jay Fenlason (hack@ai.mit.edu)
+
+ * gnu.c Prepend the current directory to the gnu_dumpfile, so that
+ -C's won't affect where the output goes. (sigh.)
+
+Tue Dec 18 18:05:59 EST 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * (gnu.c) Don't complain if the gnudumpfile we're reading info
+ from doesn't exist.
+
+ * create.c Write out gnudumpfile after finishing writing the archive.
+
+ * tar.c Add +exclude FNAME, and make +exclude-from do what +exclude
+ used to.
+
+ Make +version an operation, not an option.
+
+ add +confirmation alias for +interactive.
+
+Tue Dec 4 13:28:08 EST 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * tar.c (check_exclude) Don't let MUMBLE match MUMBLE.c or fooMUMBLE
+ but only foo/MUMBLE
+
+ * Add the name mangler (mangle.c, plus changes to create.c and
+ extract.c)
+
+ * extract.c Three small patches from Chip Salzenberg
+ (tct!chip@uunet.uu.net)
+
+ Don't complain when extracting a link, IFF it already exists.
+
+ Don't complain when extracting a directory IFF it already
+ exists.
+
+ Don't ad u+wx to directories when running as root.
+
+ * gnu.c Some changes from Chip Salzenberg to make
+ +listed-incremental work.
+
+ * port.c Add the F_FREESP emulation of the ftruncate syscall.
+
+Wed Nov 21 15:57:07 EST 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ Remove excess \n from lots of msg() calls.
+
+Mon Nov 19 14:09:43 EST 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * tar.c Rename +volume to +label
+
+Fri Nov 16 15:43:44 1990 David J. MacKenzie (djm at apple-gunkies)
+
+ * tar.c (describe): Include the default values for -b and -f
+ (as set in the Makefile) in the message.
+
+Thu Nov 15 13:36:45 EST 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * extract.c (extract_archive) Do the utime() call before the
+ chmod() call, 'cuz some versons of utime() trash the file's mode
+ bits.
+
+ * list.c (read_and) Call do_something on volume headers and
+ multivol files even if they don't match the names we're looking for,
+ etc. . .
+
+Tue Nov 6 13:51:46 EST 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * port.c (un-quote-string) Don't try to write a null
+ if there's already one there.
+
+Thu Nov 1 14:58:57 EST 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * buffer.c (new_volume) fflush(msg_file) before reading for
+ confirmation on new volume. On EOF or error, print error msg and
+ abort.
+
+Mon Oct 29 12:06:35 EST 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * getdate.y Use new version of getdate().
+
+ * tar.c (name_add) Use sizeof(char *) instead of sizeof(int)
+
+ * README give the correct return address.
+
+Thu Oct 25 16:03:58 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ rtape_lib.c Change RMTIOCTL to NO_RMTIOCTL, so it is on by default.
+
+ rmt.h Add _isrmt() #define for NO_REMOTE case.
+
+ gnu.c Add forward reference for add_dir_name().
+
+Tue Oct 16 11:04:52 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ 1.09 New -G file implementation of gnu-dump stuff.
+
+ * tar.c (name_add) Get the calls to ck_realloc and ck_malloc right.
+
+Thu Oct 11 11:23:38 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * gnu.c Fix A couple of typos.
+
+Wed Sep 19 13:35:03 1990 David J. MacKenzie (djm at apple-gunkies)
+
+ * getdate.y [USG] (ftime): Use `daylight' unless
+ DAYLIGHT_MISSING is defined.
+
+Mon Sep 17 18:04:21 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * gnu.c (gnu_restore) Don't use a passed char* for the
+ file name, use skipcrud+head->header.name, just like everything
+ else does. This means that gnu_restore will still work with
+ small buffers, etc.
+
+Thu Sep 13 15:01:17 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * tar.c (add_exclude) Don't bus-error if the exclude file doesn't
+ end with a newline.
+
+Sun Sep 9 22:35:27 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
+
+ * Makefile (dist): Remove .fname when done.
+
+Thu Sep 6 12:48:58 EDT 1990 Jay Fenlason (hack@ai.mti.edu)
+
+ * gnu.c (gnu_restore) Rember to skip_file() over the directory
+ contents, even if we don't have to do anything with them.
+
+ * create.c extract.c diffarch.c Free sparsearray after we're done
+ with it.
+
+Tue Sep 4 10:18:50 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * Makefile Include gnu.c in dist
+
+ * gnu.c move add_dir above read_dir_file so that cc doesn't complain
+ about add_dir returning void.
+
+Sun Sep 2 20:46:34 1990 David J. MacKenzie (djm at apple-gunkies)
+
+ * getdate.y: Declare some more functions and add storage
+ classes where omitted to shut compiler up.
+ [USG] (ftime): Don't use extern var `daylight'; appears that
+ some systems don't have it.
+
+Wed Aug 29 00:05:06 1990 David J. MacKenzie (djm at apple-gunkies)
+
+ * getdate.y (lookup): In the code that allows `Aug.' to be
+ recognized as `Aug', don't chop off the final `.' from words
+ like `a.m.', so they can be recognized.
+
+Thu Aug 16 11:34:07 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * buffer.c (open_archive) If -O, write verbosity to stderr
+ instead of stdout.
+
+Fri Aug 10 12:29:28 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * getdate.y Handle an explicit DST in the input string.
+ A dozen line patch from Per Foreby (perf@efd.lth.se).
+
+Mon Jul 16 13:05:11 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * tar.c rename -g -G +incremental, +listed-imcremental, etc.
+
+Fri Jul 13 14:10:33 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * tar.c Make +newer and +newer-mtime work according to their names.
+
+ * gnu.c If +newer or +newer-mtime, use the time specified on the
+ command line.
+
+ * buffer.c, create.c Add test to see if dimwit is trying to
+ archive the archive.
+
+ * tar.c (long_options[]) re-ordered, so that groups of similar
+ options are next to each other. . . I think.
+
+ (describe) Modified to more closely reflect reality.
+
+Fri Jul 6 13:13:59 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * tar.c add compile-time option for SYS V (?) style
+ tape-drive names /dev/rmt/{n}[lmh]
+
+ * tar.c Fix getopt-style stuff so that -C always works correctly.
+
+ * gnu.c, tar.c make filename to -G optional.
+
+ * {all over}, replace some fprintf(stderr...) calls with calls
+ to msg().
+
+ * port.c Make -Dmumble_MSG option on command line override
+ internal assumptions.
+
+ * Makefile Mention -Dmumble_MSG options
+
+Fri Jul 6 02:35:31 1990 David J. MacKenzie (djm at apple-gunkies)
+
+ * tar.c (options): Don't change `c' if it is 0, as getopt now
+ handles that internally.
+
+Mon Jul 2 15:21:13 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * gnu.c (new file) Moved all the f_gnudump stuff here where we
+ can keep track of it easier. Also made -G take a file name where it
+ stores the inode information about directories so that we can
+ detect moved directores.
+
+ * create.c (dump_file) Changed slightly to work with the new
+ f_gnudump.
+
+ * tar.c Moved the f_gnudump stuff to gnu.c
+
+ * tar.c, extract.c added the +do-chown option, which forces tar
+ to always try to chown the created files to their original owners.
+
+ * version.c New version 1.09
+
+Sun Jun 24 14:26:28 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
+
+ * create.c: Change ifdefs for directory library header
+ selection to be like the ones in tar.c.
+ * Makefile [Xenix]: Link with -ldir to get the dirent.h
+ directory library.
+
+Thu Jun 7 03:31:51 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
+
+ * Makefile, buffer.c, diffarch.c: Change MTIO symbol to HAVE_MTIO
+ because SCO Xenix defines 'MTIO' for an incompatible tape driver
+ system in a file included by termio.h.
+ * tar.h: Don't define size_t for Xenix.
+
+Tue Jun 5 11:38:00 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * create.c (dump_file) Only print the
+ "... is on a different filesystem..." if f_verbose is on.
+ also add a case for S_IFSOCK and treat it like a FIFO.
+ (Not sure if that's the right thing to do or not, but it's better
+ than all those Unknown File Type msgs.)
+
+Thu May 31 19:25:36 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * port.c Use #ifdef sparc instead of #ifdef SPARC since
+ the lowercase version is defined, and the uppercase one isn't.
+
+Tue May 22 11:49:18 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * port.c (ck_malloc) if size==0 pretend size=1
+ (ck_realloc) if(!ptr) call ck_malloc instead.
+
+Tue May 15 12:05:45 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * diffarch.c (diff_archive) If not f_absolute_paths, and attempt to
+ open a file listed in the archive fails, try /filename also. This will
+ allow diff to open the wrong file if both /filename and filename exist,
+ but there's nothing we can do about that.
+
+Fri May 11 16:17:43 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * Makefile, Descripbe new -DMTIO option.
+
+ * buffer.c diffarch.c Change ifdefs slightly, so that
+ -DMTIO will include sys/mtio.h even if USG is defined.
+ This is for HUPX and similar BSD/USG crossovers.
+
+Tue May 8 13:14:54 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+ * update.c (update_archive) Call reset_eof() when appropriate.
+
+ * buffer.c (reset_eof) New function, that turns of EOF flag, and
+ re-sets the ar_record and ar_last pointers. This will allow
+ 'tar rf non-existant-file' to not core-dump.
+
+Fri May 4 14:05:31 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
+
+ * tar.c: Recognize the +sparse option. It was documented, but
+ only the short form (-S) was actually recognized.
+
+Tue Apr 17 21:34:14 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * create.c Don't access location 0 if ->dir_contents is null.
+
+Wed Apr 11 17:30:03 EDT 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * buffer.c (flush_archive, close_archive, new_volume) Always check
+ the return value of rmtclose(), and only give a warning msg if it is
+ <0. Some device drivers (including Sun floppy disk, and HP
+ streaming tape) return -1 after an IO error (or something like that.)
+
+Fri Mar 23 00:06:30 1990 Jim Kingdon (kingdon at mole.ai.mit.edu)
+
+ * tar.c (long_options): Make it so +append +extract +list +update
+ +catenate and +delete don't take arguments.
+
+Mon Mar 12 13:33:53 EST 1990
+
+ * buffer.c (open_archive, fl_write) Set the mtime of the volume
+ header to the current time.
+
+Wed Mar 7 14:10:10 EST 1990 Jay Fenlason (hack@ai.mit.edu)
+
+ * buffer.c Fix +compress-block A two character patch from
+ Juha Sarlin (juha@tds.kth.se)
+ Replace #ifdef __GNU__ with #ifdef __STDC__
+ (new_volume) If open of new archive fails, ask again
+ (Is probably user error.)
+
+ * tar.c Replace #ifdef __GNU__ with #ifdef __STDC__
+
+ * port.c Clean up #ifdef and #defines a bit.
+ (quote_copy_string) Sometimes the malloc'd buffer
+ would be up to two characters too short.
+
+ * extract.c (extract_archive) Don't declare ind static.
+
+ * create.c (dump_file) Don't declare index_offset static.
+
+ * diffarch.c Remove diff_name variable, and always use
+ head->header.name, which will always work, unlike diff_name, which
+ becomes trash when the next block is read in.
+
+Thu Mar 1 13:43:30 EST 1990 Jay Fenlason (hack@wookumz.ai.mit.edu)
+
+ * Makefile Mention the -NO_REMOTE option.
+ * port.c Fix typo, and define WANT_FTRUNCATE on i386 machines.
+
+Mon Feb 26 17:44:53 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu)
+
+ * getdate.y: Declare yylex and yyerror as static.
+ #define yyparse to getdate_yyparse.
+
+Sun Feb 25 20:47:23 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
+
+ * tar.c: Remove +old option, since it is a valid abbreviation of
+ +old-archive, which does the same thing.
+ (describe): A few small cleanups in message.
+
+Mon Feb 5 14:29:21 EST 1990 Jay Fenlason (hack@wookumz)
+
+ * port.c define LOSING_MSG on sparc, since doprnt_msg doesn't work.
+ Fix typo in #ifdef WANT_GETWD
+
+Fri Jan 26 16:11:20 EST 1990 Jay Fenlason (hack@wookumz)
+
+ 1.08 Sparse file support added. Also various other features.
+
+ * diffarch.c (compare_chunk) Include correct arguments in
+ a call to fprintf() for an error msg.
+ (compare_chunks, compare_dir) First argument is a long, not an int.
+
+ * tar.c (options) Use tar variable (argv[0]) as the name to print
+ in an error msg, instead of a constant "tar".
+ (confirm) Use external variable char TTY_NAME[] for name of file
+ to open for confirmation input.
+
+ * buffer.c (new_volume) Ditto.
+
+ * port.c Add declaration for TTY_NAME[].
+
+ * rmt.h Add long declarations for lseek() and __rmt_lseek();
+
+Tue Jan 23 14:06:21 EST 1990 Jay Fenlason (hack@wookumz)
+ * tar.c, create.c Create the +newer-mtime option, which is like
+ +newer, but only looks for files whose mtime is newer than the
+ given date.
+
+ * rtape_lib.c Make *both* instances of signal-handler stuff use
+ void (*foo)() on USG systems.
+
+Thu Jan 11 14:03:45 EST 1990 Jay Fenlason (hack@wookumz)
+
+ * getdate.y Parse European dates of the form YYMMDD.
+ In ftime() Init timezone by calling localtime(), and remember that
+ timezone is in seconds, but we want timeb->timezone to be in minutes.
+ This small patch from Joergen Haegg (jh@aahas.se)
+
+ * rtape_lib.c (__rmt_open) Also look for /usr/bsd/rsh.
+ Declare signal handler as returning void instead of int if USG is
+ defined.
+
+ * port.c Declare WANT_GETWD for SGI 4-D IRIS.
+
+ * Makefile Include defines for SGI 4D version. There are a simple
+ patch from Mike Muuss (mike@brl.mil).
+
+ * buffer.c (fl_read) Work properly on broken Ultrix systems where
+ read() returns -1 with errno==ENOSPC on end of tape. Correctly go
+ on to the next volume if f_multivol.
+
+ * list.c (list_archive,print_header) Flush msg_file after printing
+ messages.
+
+ * port.c Delete unused references to alloca().
+ Don't crash if malloc() returns zero in quote_copy_string.
+ Flush stderr in msg() and msg_perror().
+
+ * tar.c Flush msg_file after printing confirmation msg.
+
+Wed Jan 10 01:58:46 1990 David J. MacKenzie (djm at hobbes.ai.mit.edu)
+
+ * tar.c (main): Change -help option and references to it to +help,
+ and remove suggestion to run info (which is unreleased, so not
+ likely to be of any help).
+
+Tue Jan 9 16:16:00 EST 1990 Jay Fenlason (hack @wookumz)
+
+ * create.c (dump_file) Close file descriptor if start_header()
+ fails.
+ (dump_file) Change test for ./ ness to not think that
+ .{any character} is a ./ These are both trivial changes from
+ Piercarlo "Peter" Grandi pcg%cs.aber.ac.uk@nsfnet-relay.ac.uk
+
+ * diffarch.c (diff_init) Print correct number of bytes in error
+ message.
+
+Tue Jan 9 03:19:49 1990 David J. MacKenzie (djm at hobbes.ai.mit.edu)
+
+ * Makefile: Add comment at top noting that two source files also
+ contain #defines that might need to be changed by hand.
+
+ * create.c, diffarch.c, extract.c: Change L_SET to 0 in lseek
+ calls, because only BSD defines it.
+ * create.c (dump_file): Make sparse file checking code conditional
+ on BSD42 because it uses st_blocks, which the other systems lack.
+
+Tue Jan 2 13:35:56 EST 1990 Jay Fenlason (hack@gnu)
+
+ * port.c (quote_copy_string) Fix so it doesn't scramble memory if
+ the last character is non-printable. A trivial fix from Kian-Tat Lim
+ (ktl@wag240.caltech.edu).
+
+Tue Dec 19 11:19:37 1989 Jim Kingdon (kingdon at pogo)
+
+ * port.c [BSD42]: Define DOPRNT_MSG.
+ tar.h [BSD42]: Do not prototype msg{,_perror}.
+
+Fri Dec 8 11:02:47 EST 1989 Jay Fenlason (hack@gnu)
+
+ * create.c (dump_file) Remove typo in msg.
+
+Fri Dec 1 19:26:47 1989 David J. MacKenzie (djm at trix)
+
+ * Makefile: Remove comments referring to certain systems lacking
+ getopt, since it is now provided always and needed by all systems.
+
+ * port.c: Remove copy of getopt.c, as it is now linked in
+ separately to always get the current version.
+
+ * tar.c: Rename +cat-tars option to +catenate or +concatenate,
+ and +local-filesystem to +one-file-system (preferred by rms
+ and used in GNU cp for the same purpose).
+ (describe): Reflect changes.
+
+Tue Nov 28 04:28:26 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu)
+
+ * port.c: Move declaration of alloca into #else /* sparc */
+ so it will compile on sparcs.
+
+Mon Nov 27 15:17:08 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu)
+
+ * tar.c (options): Remove -version option (replaced by +version).
+ (describe): Mention long options.
+
+Sat Nov 25 04:25:23 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu)
+
+ * getoldopt.c (getoldopt): Make `opt_index' argument a pointer to
+ an int, not char.
+
+ * tar.c: Modify long options per rms's suggestions:
+ Make preserve-permissions an alias for same-permissions.
+ Make preserve-order an alias for same-order.
+ Define preserve to mean both of those combined.
+ Make old an alias for old-archive.
+ Make portability an alias for old-archive, also.
+ Rename sym-links to dereference.
+ Rename gnudump to incremental.
+ Rename filename to file.
+ Make compare an alias for diff. Leave diff but prefer compare.
+ Rename blocking-factor to block-size.
+ Rename chdir to directory.
+ Make uncompress an alias for compress.
+ Rename confirm to interactive.
+ Make get an alias for extract.
+ Rename volume-header to volume.
+
+ Also make +version an alias for -version.
+
+ (options): Shorten code that interprets long options by using
+ the equivalent short options' code. This also makes it tons
+ easier to change the long options.
+
+ (describe): Make usage message more internally consistent
+ stylistically.
+
+Mon Nov 20 14:55:39 EST 1989 hack@ai.mit.edu
+
+ * list.c (read_and) Call check_exclude() to see if the files
+ should be skipped on extract or list.
+
+Thu Nov 9 18:59:32 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
+
+ * buffer.c (fl_read): Fix typos in error message
+ "tar EOF not on block boundary".
+
+Mon Oct 23 13:09:40 EDT 1989 (hack@ai.mit.edu)
+
+ * tar.c (long_options[]) Add an option for blocked compression.
+
+Thu Oct 19 13:38:16 EDT 1989 (hack@ai.mit.edu)
+
+ * buffer.c (writeerror) Print a more useful error msg.
+
+Wed Sep 27 18:33:41 EDT 1989 (hack@ai.mit.edu)
+
+ * tar.c (main) Mention "tar -help" if the luser types a non-workable
+ set of options.
+
+Mon Sep 11 15:03:29 EDT 1989 (hack@ai.mit.edu)
+
+ * tar.c (options) Have -F correctly set info_script.
+
+Tue Aug 29 12:58:06 EDT 1989 (hack@ai.mit.edu)
+
+ * Makefile Include ChangeLog in tar.tar and tar.tar.Z
+
+Mon Aug 28 17:42:24 EDT 1989 (hack@ai.mit.edu)
+
+ * tar.c (options) Made -F imply -M
+ Also remind tar that the -f option takes an argument!
+
+ * Modified -F option to make it do what (I think) it
+ should. e.g, if you say -F, tar won't send a msg to
+ msg_file and wait for a <return> It'll just run the program
+ it was given, and when the prog returns, the new tape had
+ *better* be ready. . .
+
+ * buffer.c (open_archive) Give error message and abort if
+ the luser didn't give an archive name.
+
+Fri Aug 25 20:05:27 EDT 1989 Joy Kendall (jak at hobbes)
+
+ * Added code to make a new option to run a specified script
+ at the end of each tape in a multi-volume backup. Changed:
+ tar.c: made new switch, -F, and new long-named option,
+ "info-script". Code is where you would expect.
+ tar.h: added flag f_run_script_at_end, and an extern char *
+ called info_script, which optarg gets set to.
+ buffer.c: line 1158 in new_volume(): if f_run_script_at_end
+ is set, we give info_script to system(), otherwise we do
+ what we've always done. **FIXME** I'm not sure if that's all
+ that has to be done here.
+
+Thu Aug 24 10:09:38 EDT 1989 Joy Kendall (jak at spiff)
+(These changes made over the course of 6/89 - 8/89)
+
+ * diffarch.c: diff_archive: Added switches for LF_SPARSE in the
+ case statements that needed it. Also, skip any extended headers
+ if we need to when we skip over a file. (need to change
+ the bit about, if the size doesn't agree AND the file is NOT
+ sparse, then there's a discrepancy, because I added another
+ field to the header which should be able to deal with the
+ sizes) If the file is sparse, call the added routine
+ "diff_sparse_files" to compare. Also added routine
+ "fill_in_sparse_array".
+
+ * extract.c: extract_archive: added the switch LF_SPARSE
+ to the case statement as needed, and code to treat the
+ sparse file. At label "again_file", modified opening the
+ file to see if we should have O_APPEND be one of the modes.
+ Added code at label "extract_file" to call the new routine
+ "extract_sparse_file" when we have an LF_SPARSE flag.
+
+ Note: really should erase the commented-out code in there,
+ because it's confusing.
+
+ * update.c: made sure that if a file needed to be "skipped"
+ over, it would check to see if the linkflag was sparse, and
+ if so, would then make sure to skip over any "extended
+ headers" that might come after the header itself. Do so by
+ calling "skip_extended_headers".
+
+ * create.c: create_archive: added code to detect a sparse
+ file when in the long case statement. Added ways to detect
+ extended headers, and label "extend" (ack! should get rid of
+ that, is atrocious). Call the new routine "finish_sparse_file"
+ if the linkflag is LF_SPARSE to write the info to the tape.
+ Also added routines "init_sparsearray", "deal_with_sparse",
+ "clear_buffer", "where_is_data", "zero_record", and
+ "find_new_file_size".
+
+ * tar.h: Added the #define's SPARSE_EXT_HDR and
+ SPARSE_IN_HDR. Added the struct sparse and the struct
+ sp_array. Added the linkflag LF_SPARSE. Changed the tar
+ header in several ways:
+ - added an array of struct sparse's SPARSE_IN_HDR long
+ - added a char flag isextended
+ - added a char string realsize to store the true
+ size of a sparse file
+ Added another choice to the union record called a
+ struct extended_header, which is an array of 21 struct
+ sparse's and a char isextended flag. Added flag
+ f_sparse_file to list of flags.
+
+ * tar.c: added long-named options to make tar compatible with
+ getopt_long, changed Makefile.
+
+... ... .. ..:..:.. ... .... Jay Fenlason (hack@ai.mit.edu)
+
+ 1.07 New version to go on beta tape with GCC 1.35
+ Better USG support. Also support for __builtin_alloca
+ if we're compiling with GCC.
+ diffarch.c: Include the correct header files so MTIOCTOP
+ is defined.
+ tar.c: Don't print the verbose list of options unless
+ given -help. The list of options is *way* too long.
+
+ 1.06 Use STDC_MSG if __STDC__ defined
+ ENXIO meand end-of-volume in archive (for the UNIX PC)
+ Added break after volume-header case (line 440) extract.c
+ Added patch from arnold@unix.cc.emory.edu to rtape_lib.c
+ Added f_absolute_paths option.
+ Deleted refereces to UN*X manual sections (dump(8), etc)
+ Fixed to not core-dump on illegal options
+ Modified msg_perror to call perror("") instead of perror(0)
+ patch so -X - works
+ Fixed tar.c so 'tar cf - -C dir' doesn't core-dump
+ tar.c (name_match): Fixed to chdir() to the appropriate
+ directory if the matching name's change_dir is set. This
+ makes tar xv -C foo {files} work.
+
+ 1.05 A fix to make confirm() work when the archive is on stdin
+ include 'extern FILE *msg_file;' in pr_mkdir(), and fix
+ tar.h to work with __STDC__
+
+ Added to port.c: mkdir() ftruncate() Removed: lstat()
+ Fixed -G to work with -X
+ Another fix to tar.texinfo
+ Changed tar.c to say argv[0]":you must specify exactly ...
+ buffer.c: modified child_open() to keep tar from hanging when
+ it is done reading/writing a compressed archive
+ added fflush(msg_file) before printing error messages
+ create.c: fixed to make link_names non-absolute
+
+ 1.04 Added functions msg() and msg_perror() Modified all the
+ files to call them. Also checked that all (I hope)
+ calls to msg_perror() have a valid errno value
+ (modified anno() to leave errno alone), etc
+ Re-fixed the -X option. This time for sure. . .
+ re-modified the msg stuff. flushed anno() completely
+ Modified the directory stuff so it should work on sysV boxes
+ added ftime() to getdate.y
+ Fixed un_quote_string() so it won't wedge on \" Also fixed
+ \ddd (like \123, etc)
+ More fixes to tar.texinfo
+
+ 1.03 Fixed buffer.c so 'tar tzf NON_EXISTENT_FILE' returns an error
+ message instead of hanging forever
+ More fixes to tar.texinfo
+
+ 1.02 Fixed tar.c so 'tar -h' and 'tar -v' don't cause core dump
+ Also fixed the 'usage' message to be more up-to-date.
+ Fixed diffarch.c so verify should compile without MTIOCTOP
+ defined
+
+ 1.01 Fixed typoes in tar.texinfo
+ Fixed a bug in the #define for rmtcreat()
+ Fixed the -X option to not call realloc() of 0.
+
+ Version 1.00: version.c added. -version option added
+ Installed new version of the remote-tape library
+ Added -help option
+
+Local Variables:
+mode: indented-text
+left-margin: 8
+version-control: never
+End:
diff --git a/gnu/usr.bin/tar/Makefile b/gnu/usr.bin/tar/Makefile
new file mode 100644
index 0000000..cab7e00
--- /dev/null
+++ b/gnu/usr.bin/tar/Makefile
@@ -0,0 +1,16 @@
+PROG= tar
+SRCS= buffer.c create.c diffarch.c extract.c fnmatch.c getdate.y \
+ getoldopt.c getopt.c getopt1.c gnu.c list.c mangle.c names.c port.c \
+ regex.c rtapelib.c tar.c update.c version.c
+CFLAGS+= -DRETSIGTYPE=void -DDIRENT=1 -DHAVE_SYS_MTIO_H=1 -DHAVE_UNISTD_H=1
+CFLAGS+= -DHAVE_GETGRGID=1 -DHAVE_GETPWUID=1 -DHAVE_STRING_H=1
+CFLAGS+= -DHAVE_LIMITS_H=1 -DHAVE_STRSTR=1 -DHAVE_VALLOC=1 -DHAVE_MKDIR=1
+CFLAGS+= -DHAVE_MKNOD=1 -DHAVE_RENAME=1 -DHAVE_FTRUNCATE=1 -DHAVE_GETCWD=1
+CFLAGS+= -DHAVE_VPRINTF=1 -DNEEDPAD -I${.CURDIR}
+CFLAGS+= -DDEF_AR_FILE=\"/dev/rst0\" -DDEFBLOCKING=20
+CLEANFILES+=y.tab.h
+NOMAN=noman
+NOSHARED=yes
+
+.include <bsd.prog.mk>
+.include "../../usr.bin/Makefile.inc"
diff --git a/gnu/usr.bin/tar/Makefile.gnu b/gnu/usr.bin/tar/Makefile.gnu
new file mode 100644
index 0000000..a03617a
--- /dev/null
+++ b/gnu/usr.bin/tar/Makefile.gnu
@@ -0,0 +1,185 @@
+# Generated automatically from Makefile.in by configure.
+# Un*x Makefile for GNU tar program.
+# Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#### Start of system configuration section. ####
+
+srcdir = .
+VPATH = .
+
+# If you use gcc, you should either run the fixincludes script that
+# comes with it or else use gcc with the -traditional option. Otherwise
+# ioctl calls will be compiled incorrectly on some systems.
+CC = gcc
+YACC = bison -y
+INSTALL = /usr/local/bin/install -c
+INSTALL_PROGRAM = $(INSTALL)
+INSTALL_DATA = $(INSTALL) -m 644
+
+# Things you might add to DEFS:
+# -DSTDC_HEADERS If you have ANSI C headers and libraries.
+# -DHAVE_UNISTD_H If you have unistd.h.
+# -DHAVE_STRING_H If you don't have ANSI C headers but have string.h.
+# -DHAVE_LIMITS_H If you have limits.h.
+# -DBSD42 If you have sys/dir.h (unless you use -DPOSIX),
+# sys/file.h, and st_blocks in `struct stat'.
+# -DDIRENT If you have dirent.h.
+# -DSYSNDIR Old Xenix systems (sys/ndir.h).
+# -DSYSDIR Old BSD systems (sys/dir.h).
+# -DNDIR Old System V systems (ndir.h).
+# -DMAJOR_IN_MKDEV If major, minor, makedev defined in sys/mkdev.h.
+# -DMAJOR_IN_SYSMACROS If major, minor, makedev defined in sys/sysmacros.h.
+# -DRETSIGTYPE=int If your signal handlers return int, not void.
+# -DHAVE_SYS_MTIO_H If you have sys/mtio.h (magtape ioctls).
+# -DHAVE_SYS_GENTAPE_H If you have sys/gentape.h (ISC magtape ioctls).
+# -DHAVE_NETDB_H To use rexec for remote tape operations
+# instead of forking rsh or remsh.
+# -DNO_REMOTE If you have neither a remote shell nor rexec.
+# -DHAVE_VPRINTF If you have vprintf function.
+# -DHAVE_DOPRNT If you have _doprnt function (but lack vprintf).
+# -DHAVE_FTIME If you have ftime system call.
+# -DHAVE_STRSTR If you have strstr function.
+# -DHAVE_VALLOC If you have valloc function.
+# -DHAVE_MKDIR If you have mkdir and rmdir system calls.
+# -DHAVE_MKNOD If you have mknod system call.
+# -DHAVE_RENAME If you have rename system call.
+# -DHAVE_GETCWD If not POSIX.1 but have getcwd function.
+# -DHAVE_FTRUNCATE If you have ftruncate system call.
+# -DV7 On Version 7 Unix (not tested in a long time).
+# -DEMUL_OPEN3 If you lack a 3-argument version of open, and want
+# to emulate it with system calls you do have.
+# -DNO_OPEN3 If you lack the 3-argument open and want to
+# disable the tar -k option instead of emulating open.
+# -DXENIX If you have sys/inode.h and need it to be included.
+
+DEF_AR_FILE = /dev/rst0
+DEFBLOCKING = 20
+DEFS = -DRETSIGTYPE=void -DDIRENT=1 -DHAVE_SYS_MTIO_H=1 -DHAVE_UNISTD_H=1 -DHAVE_GETGRGID=1 -DHAVE_GETPWUID=1 -DHAVE_STRING_H=1 -DHAVE_LIMITS_H=1 -DHAVE_STRSTR=1 -DHAVE_VALLOC=1 -DHAVE_MKDIR=1 -DHAVE_MKNOD=1 -DHAVE_RENAME=1 -DHAVE_FTRUNCATE=1 -DHAVE_GETCWD=1 -DHAVE_VPRINTF=1 -DDEF_AR_FILE=\"$(DEF_AR_FILE)\" -DDEFBLOCKING=$(DEFBLOCKING)
+
+# Set this to rtapelib.o unless you defined NO_REMOTE, in which case
+# make it empty.
+RTAPELIB = rtapelib.o
+LIBS =
+
+CFLAGS = -g
+LDFLAGS = -g
+
+prefix = /usr/bin
+exec_prefix = $(prefix)
+
+# Prefix for each installed program, normally empty or `g'.
+binprefix =
+
+# The directory to install tar in.
+bindir = $(exec_prefix)/bin
+
+# Where to put the rmt executable.
+libdir = /sbin
+
+# The directory to install the info files in.
+infodir = $(prefix)/info
+
+#### End of system configuration section. ####
+
+SHELL = /bin/sh
+
+SRC1 = tar.c create.c extract.c buffer.c getoldopt.c update.c gnu.c mangle.c
+SRC2 = version.c list.c names.c diffarch.c port.c fnmatch.c getopt.c malloc.c
+SRC3 = getopt1.c regex.c getdate.y getdate.c alloca.c
+SRCS = $(SRC1) $(SRC2) $(SRC3)
+OBJ1 = tar.o create.o extract.o buffer.o getoldopt.o update.o gnu.o mangle.o
+OBJ2 = version.o list.o names.o diffarch.o port.o fnmatch.o getopt.o
+OBJ3 = getopt1.o regex.o getdate.o $(RTAPELIB)
+OBJS = $(OBJ1) $(OBJ2) $(OBJ3)
+AUX = README INSTALL NEWS COPYING ChangeLog Makefile.in makefile.pc \
+ configure configure.in \
+ tar.h fnmatch.h pathmax.h port.h open3.h getopt.h regex.h \
+ rmt.h rmt.c rtapelib.c \
+ msd_dir.h msd_dir.c tcexparg.c \
+ level-0 level-1 backup-specs dump-remind getpagesize.h
+# tar.texinfo tar.info* texinfo.tex \
+
+all: tar rmt
+# tar.info
+
+.c.o:
+ $(CC) -c $(CFLAGS) $(CPPFLAGS) $(DEFS) -I$(srcdir) -I. $<
+
+tar: $(OBJS)
+ $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+rmt: rmt.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(srcdir)/rmt.c $(LIBS)
+
+tar.info: tar.texinfo
+ makeinfo $(srcdir)/tar.texinfo
+
+install: all
+ $(INSTALL_PROGRAM) tar $(bindir)/$(binprefix)tar
+ -test ! -f rmt || $(INSTALL_PROGRAM) rmt $(libdir)/rmt
+# for file in $(srcdir)/tar.info*; \
+# do $(INSTALL_DATA) $$file $(infodir)/$$file; \
+# done
+
+uninstall:
+ rm -f $(bindir)/$(binprefix)tar $(infodir)/tar.info*
+ -rm -f $(libdir)/rmt
+
+$(OBJS): tar.h pathmax.h port.h
+regex.o buffer.o tar.o: regex.h
+tar.o fnmatch.o: fnmatch.h
+
+getdate.c: getdate.y
+ $(YACC) $(srcdir)/getdate.y
+ mv y.tab.c getdate.c
+# getdate.y has 8 shift/reduce conflicts.
+
+TAGS: $(SRCS)
+ etags $(SRCS)
+
+clean:
+ rm -f *.o tar rmt core
+mostlyclean: clean
+
+distclean: clean
+ rm -f Makefile config.status
+
+realclean: distclean
+ rm -f TAGS *.info* getdate.c y.tab.c
+
+shar: $(SRCS) $(AUX)
+ shar $(SRCS) $(AUX) | gzip > tar-`sed -e '/version_string/!d' -e 's/[^0-9.]*\([0-9.]*\).*/\1/' -e q version.c`.shar.z
+
+dist: $(SRCS) $(AUX)
+ echo tar-`sed -e '/version_string/!d' -e 's/[^0-9.]*\([0-9.]*\).*/\1/' -e q version.c` > .fname
+ -rm -rf `cat .fname`
+ mkdir `cat .fname`
+ for file in $(SRCS) $(AUX); do \
+ ln $$file `cat .fname` || cp $$file `cat .fname`; done
+ tar chzf `cat .fname`.tar.z `cat .fname`
+ -rm -rf `cat .fname` .fname
+
+tar.zoo: $(SRCS) $(AUX)
+ -rm -rf tmp.dir
+ -mkdir tmp.dir
+ -rm tar.zoo
+ for X in $(SRCS) $(AUX) ; do echo $$X ; sed 's/$$/ /' $$X > tmp.dir/$$X ; done
+ cd tmp.dir ; zoo aM ../tar.zoo *
+ -rm -rf tmp.dir
+
+# Prevent GNU make v3 from overflowing arg limit on SysV.
+.NOEXPORT:
diff --git a/gnu/usr.bin/tar/README b/gnu/usr.bin/tar/README
new file mode 100644
index 0000000..4b577e7
--- /dev/null
+++ b/gnu/usr.bin/tar/README
@@ -0,0 +1,40 @@
+Hey! Emacs! Yo! This is -*- Text -*- !!!
+
+This GNU tar 1.11.2. Please send bug reports, etc., to
+bug-gnu-utils@prep.ai.mit.edu. This is a beta-test release. Please
+try it out. There is no manual; the release of version 1.12 will
+contain a manual.
+
+GNU tar is based heavily on John Gilmore's public domain tar, but with
+added features. The manual is currently being written.
+
+This distribution also includes rmt, the remote tape server (which
+normally must reside in /etc). The mt tape drive control program is
+in the GNU cpio distribution.
+
+See the file INSTALL for compilation and installation instructions for Unix.
+See the file NEWS for information on all that is new in this version
+of tar.
+
+makefile.pc is a makefile for Turbo C 2.0 on MS-DOS.
+
+Various people have been having problems using floppies on a NeXT. In
+order to have them work right, you need to kill the automounting
+program which tries to monut floppies as soon as they are added.
+
+If you want to do incremental dumps, use the distributed backup
+scripts. They are what we use at the FSF to do all our backups. Most
+importantly, do not use --incremental (-G) or --after-date (-N) or
+--newer-mtime to do incremental dumps. The only option that works
+correctly for this purpose is --listed-incremental. (When extracting
+incremental dumps, use --incremental (-G).)
+
+If your system needs to link with -lPW to get alloca, but has
+rename in the C library (so HAVE_RENAME is defined), -lPW might
+give you an incorrect version of rename. On HP-UX this manifests
+itself as an undefined data symbol called "Error" when linking cp, ln,
+and mv. If this happens, use `ar x' to extract alloca.o from libPW.a
+and `ar rc' to put it in a library liballoca.a, and put that in LIBS
+instead of -lPW. This problem does not occur when using gcc, which
+has alloca built in.
+
diff --git a/gnu/usr.bin/tar/buffer.c b/gnu/usr.bin/tar/buffer.c
new file mode 100644
index 0000000..e0ffc2d
--- /dev/null
+++ b/gnu/usr.bin/tar/buffer.c
@@ -0,0 +1,1584 @@
+/* Buffer management for tar.
+ Copyright (C) 1988, 1992, 1993 Free Software Foundation
+
+This file is part of GNU Tar.
+
+GNU Tar is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Tar is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Tar; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * Buffer management for tar.
+ *
+ * Written by John Gilmore, ihnp4!hoptoad!gnu, on 25 August 1985.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+#include <sys/types.h> /* For non-Berkeley systems */
+#include <signal.h>
+#include <time.h>
+time_t time ();
+
+#ifdef HAVE_SYS_MTIO_H
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#endif
+
+#ifdef BSD42
+#include <sys/file.h>
+#else
+#ifndef V7
+#include <fcntl.h>
+#endif
+#endif
+
+#ifdef __MSDOS__
+#include <process.h>
+#endif
+
+#ifdef XENIX
+#include <sys/inode.h>
+#endif
+
+#include "tar.h"
+#include "port.h"
+#include "rmt.h"
+#include "regex.h"
+
+/* Either stdout or stderr: The thing we write messages (standard msgs, not
+ errors) to. Stdout unless we're writing a pipe, in which case stderr */
+FILE *msg_file = stdout;
+
+#define STDIN 0 /* Standard input file descriptor */
+#define STDOUT 1 /* Standard output file descriptor */
+
+#define PREAD 0 /* Read file descriptor from pipe() */
+#define PWRITE 1 /* Write file descriptor from pipe() */
+
+#define MAGIC_STAT 105 /* Magic status returned by child, if
+ it can't exec. We hope compress/sh
+ never return this status! */
+
+void *valloc ();
+
+void writeerror ();
+void readerror ();
+
+void ck_pipe ();
+void ck_close ();
+
+int backspace_output ();
+extern void finish_header ();
+void flush_archive ();
+int isfile ();
+int new_volume ();
+void verify_volume ();
+extern void to_oct ();
+
+#ifndef __MSDOS__
+/* Obnoxious test to see if dimwit is trying to dump the archive */
+dev_t ar_dev;
+ino_t ar_ino;
+#endif
+
+/*
+ * The record pointed to by save_rec should not be overlaid
+ * when reading in a new tape block. Copy it to record_save_area first, and
+ * change the pointer in *save_rec to point to record_save_area.
+ * Saved_recno records the record number at the time of the save.
+ * This is used by annofile() to print the record number of a file's
+ * header record.
+ */
+static union record **save_rec;
+union record record_save_area;
+static long saved_recno;
+
+/*
+ * PID of child program, if f_compress or remote archive access.
+ */
+static int childpid = 0;
+
+/*
+ * Record number of the start of this block of records
+ */
+long baserec;
+
+/*
+ * Error recovery stuff
+ */
+static int r_error_count;
+
+/*
+ * Have we hit EOF yet?
+ */
+static int hit_eof;
+
+/* Checkpointing counter */
+static int checkpoint;
+
+/* JF we're reading, but we just read the last record and its time to update */
+extern time_to_start_writing;
+int file_to_switch_to = -1; /* If remote update, close archive, and use
+ this descriptor to write to */
+
+static int volno = 1; /* JF which volume of a multi-volume tape
+ we're on */
+static int global_volno = 1; /* Volume number to print in external messages. */
+
+char *save_name = 0; /* Name of the file we are currently writing */
+long save_totsize; /* total size of file we are writing. Only
+ valid if save_name is non_zero */
+long save_sizeleft; /* Where we are in the file we are writing.
+ Only valid if save_name is non-zero */
+
+int write_archive_to_stdout;
+
+/* Used by fl_read and fl_write to store the real info about saved names */
+static char real_s_name[NAMSIZ];
+static long real_s_totsize;
+static long real_s_sizeleft;
+
+/* Reset the EOF flag (if set), and re-set ar_record, etc */
+
+void
+reset_eof ()
+{
+ if (hit_eof)
+ {
+ hit_eof = 0;
+ ar_record = ar_block;
+ ar_last = ar_block + blocking;
+ ar_reading = 0;
+ }
+}
+
+/*
+ * Return the location of the next available input or output record.
+ * Return NULL for EOF. Once we have returned NULL, we just keep returning
+ * it, to avoid accidentally going on to the next file on the "tape".
+ */
+union record *
+findrec ()
+{
+ if (ar_record == ar_last)
+ {
+ if (hit_eof)
+ return (union record *) NULL; /* EOF */
+ flush_archive ();
+ if (ar_record == ar_last)
+ {
+ hit_eof++;
+ return (union record *) NULL; /* EOF */
+ }
+ }
+ return ar_record;
+}
+
+
+/*
+ * Indicate that we have used all records up thru the argument.
+ * (should the arg have an off-by-1? XXX FIXME)
+ */
+void
+userec (rec)
+ union record *rec;
+{
+ while (rec >= ar_record)
+ ar_record++;
+ /*
+ * Do NOT flush the archive here. If we do, the same
+ * argument to userec() could mean the next record (if the
+ * input block is exactly one record long), which is not what
+ * is intended.
+ */
+ if (ar_record > ar_last)
+ abort ();
+}
+
+
+/*
+ * Return a pointer to the end of the current records buffer.
+ * All the space between findrec() and endofrecs() is available
+ * for filling with data, or taking data from.
+ */
+union record *
+endofrecs ()
+{
+ return ar_last;
+}
+
+
+/*
+ * Duplicate a file descriptor into a certain slot.
+ * Equivalent to BSD "dup2" with error reporting.
+ */
+void
+dupto (from, to, msg)
+ int from, to;
+ char *msg;
+{
+ int err;
+
+ if (from != to)
+ {
+ err = close (to);
+ if (err < 0 && errno != EBADF)
+ {
+ msg_perror ("Cannot close descriptor %d", to);
+ exit (EX_SYSTEM);
+ }
+ err = dup (from);
+ if (err != to)
+ {
+ msg_perror ("cannot dup %s", msg);
+ exit (EX_SYSTEM);
+ }
+ ck_close (from);
+ }
+}
+
+#ifdef __MSDOS__
+void
+child_open ()
+{
+ fprintf (stderr, "MS-DOS %s can't use compressed or remote archives\n", tar);
+ exit (EX_ARGSBAD);
+}
+
+#else
+void
+child_open ()
+{
+ int pipe[2];
+ int err = 0;
+
+ int kidpipe[2];
+ int kidchildpid;
+
+#define READ 0
+#define WRITE 1
+
+ ck_pipe (pipe);
+
+ childpid = fork ();
+ if (childpid < 0)
+ {
+ msg_perror ("cannot fork");
+ exit (EX_SYSTEM);
+ }
+ if (childpid > 0)
+ {
+ /* We're the parent. Clean up and be happy */
+ /* This, at least, is easy */
+
+ if (ar_reading)
+ {
+ f_reblock++;
+ archive = pipe[READ];
+ ck_close (pipe[WRITE]);
+ }
+ else
+ {
+ archive = pipe[WRITE];
+ ck_close (pipe[READ]);
+ }
+ return;
+ }
+
+ /* We're the kid */
+ if (ar_reading)
+ {
+ dupto (pipe[WRITE], STDOUT, "(child) pipe to stdout");
+ ck_close (pipe[READ]);
+ }
+ else
+ {
+ dupto (pipe[READ], STDIN, "(child) pipe to stdin");
+ ck_close (pipe[WRITE]);
+ }
+
+ /* We need a child tar only if
+ 1: we're reading/writing stdin/out (to force reblocking)
+ 2: the file is to be accessed by rmt (compress doesn't know how)
+ 3: the file is not a plain file */
+#ifdef NO_REMOTE
+ if (!(ar_files[0][0] == '-' && ar_files[0][1] == '\0') && isfile (ar_files[0]))
+#else
+ if (!(ar_files[0][0] == '-' && ar_files[0][1] == '\0') && !_remdev (ar_files[0]) && isfile (ar_files[0]))
+#endif
+ {
+ /* We don't need a child tar. Open the archive */
+ if (ar_reading)
+ {
+ archive = open (ar_files[0], O_RDONLY | O_BINARY, 0666);
+ if (archive < 0)
+ {
+ msg_perror ("can't open archive %s", ar_files[0]);
+ exit (EX_BADARCH);
+ }
+ dupto (archive, STDIN, "archive to stdin");
+ /* close(archive); */
+ }
+ else
+ {
+ archive = creat (ar_files[0], 0666);
+ if (archive < 0)
+ {
+ msg_perror ("can't open archive %s", ar_files[0]);
+ exit (EX_BADARCH);
+ }
+ dupto (archive, STDOUT, "archive to stdout");
+ /* close(archive); */
+ }
+ }
+ else
+ {
+ /* We need a child tar */
+ ck_pipe (kidpipe);
+
+ kidchildpid = fork ();
+ if (kidchildpid < 0)
+ {
+ msg_perror ("child can't fork");
+ exit (EX_SYSTEM);
+ }
+
+ if (kidchildpid > 0)
+ {
+ /* About to exec compress: set up the files */
+ if (ar_reading)
+ {
+ dupto (kidpipe[READ], STDIN, "((child)) pipe to stdin");
+ ck_close (kidpipe[WRITE]);
+ /* dup2(pipe[WRITE],STDOUT); */
+ }
+ else
+ {
+ /* dup2(pipe[READ],STDIN); */
+ dupto (kidpipe[WRITE], STDOUT, "((child)) pipe to stdout");
+ ck_close (kidpipe[READ]);
+ }
+ /* ck_close(pipe[READ]); */
+ /* ck_close(pipe[WRITE]); */
+ /* ck_close(kidpipe[READ]);
+ ck_close(kidpipe[WRITE]); */
+ }
+ else
+ {
+ /* Grandchild. Do the right thing, namely sit here and
+ read/write the archive, and feed stuff back to compress */
+ tar = "tar (child)";
+ if (ar_reading)
+ {
+ dupto (kidpipe[WRITE], STDOUT, "[child] pipe to stdout");
+ ck_close (kidpipe[READ]);
+ }
+ else
+ {
+ dupto (kidpipe[READ], STDIN, "[child] pipe to stdin");
+ ck_close (kidpipe[WRITE]);
+ }
+
+ if (ar_files[0][0] == '-' && ar_files[0][1] == '\0')
+ {
+ if (ar_reading)
+ archive = STDIN;
+ else
+ archive = STDOUT;
+ }
+ else /* This can't happen if (ar_reading==2)
+ archive = rmtopen(ar_files[0], O_RDWR|O_CREAT|O_BINARY, 0666);
+ else */ if (ar_reading)
+ archive = rmtopen (ar_files[0], O_RDONLY | O_BINARY, 0666);
+ else
+ archive = rmtcreat (ar_files[0], 0666);
+
+ if (archive < 0)
+ {
+ msg_perror ("can't open archive %s", ar_files[0]);
+ exit (EX_BADARCH);
+ }
+
+ if (ar_reading)
+ {
+ for (;;)
+ {
+ char *ptr;
+ int max, count;
+
+ r_error_count = 0;
+ error_loop:
+ err = rmtread (archive, ar_block->charptr, (int) (blocksize));
+ if (err < 0)
+ {
+ readerror ();
+ goto error_loop;
+ }
+ if (err == 0)
+ break;
+ ptr = ar_block->charptr;
+ max = err;
+ while (max)
+ {
+ count = (max < RECORDSIZE) ? max : RECORDSIZE;
+ err = write (STDOUT, ptr, count);
+ if (err != count)
+ {
+ if (err < 0)
+ {
+ msg_perror ("can't write to compression program");
+ exit (EX_SYSTEM);
+ }
+ else
+ msg ("write to compression program short %d bytes",
+ count - err);
+ count = (err < 0) ? 0 : err;
+ }
+ ptr += count;
+ max -= count;
+ }
+ }
+ }
+ else
+ {
+ for (;;)
+ {
+ int n;
+ char *ptr;
+
+ n = blocksize;
+ ptr = ar_block->charptr;
+ while (n)
+ {
+ err = read (STDIN, ptr, (n < RECORDSIZE) ? n : RECORDSIZE);
+ if (err <= 0)
+ break;
+ n -= err;
+ ptr += err;
+ }
+ /* EOF */
+ if (err == 0)
+ {
+ if (!f_compress_block)
+ blocksize -= n;
+ else
+ bzero (ar_block->charptr + blocksize - n, n);
+ err = rmtwrite (archive, ar_block->charptr, blocksize);
+ if (err != (blocksize))
+ writeerror (err);
+ if (!f_compress_block)
+ blocksize += n;
+ break;
+ }
+ if (n)
+ {
+ msg_perror ("can't read from compression program");
+ exit (EX_SYSTEM);
+ }
+ err = rmtwrite (archive, ar_block->charptr, (int) blocksize);
+ if (err != blocksize)
+ writeerror (err);
+ }
+ }
+
+ /* close_archive(); */
+ exit (0);
+ }
+ }
+ /* So we should exec compress (-d) */
+ if (ar_reading)
+ execlp (f_compressprog, f_compressprog, "-d", (char *) 0);
+ else
+ execlp (f_compressprog, f_compressprog, (char *) 0);
+ msg_perror ("can't exec %s", f_compressprog);
+ _exit (EX_SYSTEM);
+}
+
+
+/* return non-zero if p is the name of a directory */
+int
+isfile (p)
+ char *p;
+{
+ struct stat stbuf;
+
+ if (stat (p, &stbuf) < 0)
+ return 1;
+ if (S_ISREG (stbuf.st_mode))
+ return 1;
+ return 0;
+}
+
+#endif
+
+/*
+ * Open an archive file. The argument specifies whether we are
+ * reading or writing.
+ */
+/* JF if the arg is 2, open for reading and writing. */
+void
+open_archive (reading)
+ int reading;
+{
+ msg_file = f_exstdout ? stderr : stdout;
+
+ if (blocksize == 0)
+ {
+ msg ("invalid value for blocksize");
+ exit (EX_ARGSBAD);
+ }
+
+ if (n_ar_files == 0)
+ {
+ msg ("No archive name given, what should I do?");
+ exit (EX_BADARCH);
+ }
+
+ /*NOSTRICT*/
+ if (f_multivol)
+ {
+ ar_block = (union record *) valloc ((unsigned) (blocksize + (2 * RECORDSIZE)));
+ if (ar_block)
+ ar_block += 2;
+ }
+ else
+ ar_block = (union record *) valloc ((unsigned) blocksize);
+ if (!ar_block)
+ {
+ msg ("could not allocate memory for blocking factor %d",
+ blocking);
+ exit (EX_ARGSBAD);
+ }
+
+ ar_record = ar_block;
+ ar_last = ar_block + blocking;
+ ar_reading = reading;
+
+ if (f_multivol && f_verify)
+ {
+ msg ("cannot verify multi-volume archives");
+ exit (EX_ARGSBAD);
+ }
+
+ if (f_compressprog)
+ {
+ if (reading == 2 || f_verify)
+ {
+ msg ("cannot update or verify compressed archives");
+ exit (EX_ARGSBAD);
+ }
+ if (f_multivol)
+ {
+ msg ("cannot use multi-volume compressed archives");
+ exit (EX_ARGSBAD);
+ }
+ child_open ();
+ if (!reading && ar_files[0][0] == '-' && ar_files[0][1] == '\0')
+ msg_file = stderr;
+ /* child_open(rem_host, rem_file); */
+ }
+ else if (ar_files[0][0] == '-' && ar_files[0][1] == '\0')
+ {
+ f_reblock++; /* Could be a pipe, be safe */
+ if (f_verify)
+ {
+ msg ("can't verify stdin/stdout archive");
+ exit (EX_ARGSBAD);
+ }
+ if (reading == 2)
+ {
+ archive = STDIN;
+ msg_file = stderr;
+ write_archive_to_stdout++;
+ }
+ else if (reading)
+ archive = STDIN;
+ else
+ {
+ archive = STDOUT;
+ msg_file = stderr;
+ }
+ }
+ else if (reading == 2 || f_verify)
+ {
+ archive = rmtopen (ar_files[0], O_RDWR | O_CREAT | O_BINARY, 0666);
+ }
+ else if (reading)
+ {
+ archive = rmtopen (ar_files[0], O_RDONLY | O_BINARY, 0666);
+ }
+ else
+ {
+ archive = rmtcreat (ar_files[0], 0666);
+ }
+ if (archive < 0)
+ {
+ msg_perror ("can't open %s", ar_files[0]);
+ exit (EX_BADARCH);
+ }
+#ifndef __MSDOS__
+ if (!_isrmt (archive))
+ {
+ struct stat tmp_stat;
+
+ fstat (archive, &tmp_stat);
+ if (S_ISREG (tmp_stat.st_mode))
+ {
+ ar_dev = tmp_stat.st_dev;
+ ar_ino = tmp_stat.st_ino;
+ }
+ }
+#endif
+
+#ifdef __MSDOS__
+ setmode (archive, O_BINARY);
+#endif
+
+ if (reading)
+ {
+ ar_last = ar_block; /* Set up for 1st block = # 0 */
+ (void) findrec (); /* Read it in, check for EOF */
+
+ if (f_volhdr)
+ {
+ union record *head;
+#if 0
+ char *ptr;
+
+ if (f_multivol)
+ {
+ ptr = malloc (strlen (f_volhdr) + 20);
+ sprintf (ptr, "%s Volume %d", f_volhdr, 1);
+ }
+ else
+ ptr = f_volhdr;
+#endif
+ head = findrec ();
+ if (!head)
+ {
+ msg ("Archive not labelled to match %s", f_volhdr);
+ exit (EX_BADVOL);
+ }
+ if (re_match (label_pattern, head->header.arch_name,
+ strlen (head->header.arch_name), 0, 0) < 0)
+ {
+ msg ("Volume mismatch! %s!=%s", f_volhdr,
+ head->header.arch_name);
+ exit (EX_BADVOL);
+ }
+#if 0
+ if (strcmp (ptr, head->header.name))
+ {
+ msg ("Volume mismatch! %s!=%s", ptr, head->header.name);
+ exit (EX_BADVOL);
+ }
+ if (ptr != f_volhdr)
+ free (ptr);
+#endif
+ }
+ }
+ else if (f_volhdr)
+ {
+ bzero ((void *) ar_block, RECORDSIZE);
+ if (f_multivol)
+ sprintf (ar_block->header.arch_name, "%s Volume 1", f_volhdr);
+ else
+ strcpy (ar_block->header.arch_name, f_volhdr);
+ current_file_name = ar_block->header.arch_name;
+ ar_block->header.linkflag = LF_VOLHDR;
+ to_oct (time (0), 1 + 12, ar_block->header.mtime);
+ finish_header (ar_block);
+ /* ar_record++; */
+ }
+}
+
+
+/*
+ * Remember a union record * as pointing to something that we
+ * need to keep when reading onward in the file. Only one such
+ * thing can be remembered at once, and it only works when reading
+ * an archive.
+ *
+ * We calculate "offset" then add it because some compilers end up
+ * adding (baserec+ar_record), doing a 9-bit shift of baserec, then
+ * subtracting ar_block from that, shifting it back, losing the top 9 bits.
+ */
+void
+saverec (pointer)
+ union record **pointer;
+{
+ long offset;
+
+ save_rec = pointer;
+ offset = ar_record - ar_block;
+ saved_recno = baserec + offset;
+}
+
+/*
+ * Perform a write to flush the buffer.
+ */
+
+/*send_buffer_to_file();
+ if(new_volume) {
+ deal_with_new_volume_stuff();
+ send_buffer_to_file();
+ }
+ */
+
+void
+fl_write ()
+{
+ int err;
+ int copy_back;
+ static long bytes_written = 0;
+
+ if (f_checkpoint && !(++checkpoint % 10))
+ msg ("Write checkpoint %d\n", checkpoint);
+ if (tape_length && bytes_written >= tape_length * 1024)
+ {
+ errno = ENOSPC;
+ err = 0;
+ }
+ else
+ err = rmtwrite (archive, ar_block->charptr, (int) blocksize);
+ if (err != blocksize && !f_multivol)
+ writeerror (err);
+ else if (f_totals)
+ tot_written += blocksize;
+
+ if (err > 0)
+ bytes_written += err;
+ if (err == blocksize)
+ {
+ if (f_multivol)
+ {
+ if (!save_name)
+ {
+ real_s_name[0] = '\0';
+ real_s_totsize = 0;
+ real_s_sizeleft = 0;
+ return;
+ }
+#ifdef __MSDOS__
+ if (save_name[1] == ':')
+ save_name += 2;
+#endif
+ while (*save_name == '/')
+ save_name++;
+
+ strcpy (real_s_name, save_name);
+ real_s_totsize = save_totsize;
+ real_s_sizeleft = save_sizeleft;
+ }
+ return;
+ }
+
+ /* We're multivol Panic if we didn't get the right kind of response */
+ /* ENXIO is for the UNIX PC */
+ if (err < 0 && errno != ENOSPC && errno != EIO && errno != ENXIO)
+ writeerror (err);
+
+ /* If error indicates a short write, we just move to the next tape. */
+
+ if (new_volume (0) < 0)
+ return;
+ bytes_written = 0;
+ if (f_volhdr && real_s_name[0])
+ {
+ copy_back = 2;
+ ar_block -= 2;
+ }
+ else if (f_volhdr || real_s_name[0])
+ {
+ copy_back = 1;
+ ar_block--;
+ }
+ else
+ copy_back = 0;
+ if (f_volhdr)
+ {
+ bzero ((void *) ar_block, RECORDSIZE);
+ sprintf (ar_block->header.arch_name, "%s Volume %d", f_volhdr, volno);
+ to_oct (time (0), 1 + 12, ar_block->header.mtime);
+ ar_block->header.linkflag = LF_VOLHDR;
+ finish_header (ar_block);
+ }
+ if (real_s_name[0])
+ {
+ int tmp;
+
+ if (f_volhdr)
+ ar_block++;
+ bzero ((void *) ar_block, RECORDSIZE);
+ strcpy (ar_block->header.arch_name, real_s_name);
+ ar_block->header.linkflag = LF_MULTIVOL;
+ to_oct ((long) real_s_sizeleft, 1 + 12,
+ ar_block->header.size);
+ to_oct ((long) real_s_totsize - real_s_sizeleft,
+ 1 + 12, ar_block->header.offset);
+ tmp = f_verbose;
+ f_verbose = 0;
+ finish_header (ar_block);
+ f_verbose = tmp;
+ if (f_volhdr)
+ ar_block--;
+ }
+
+ err = rmtwrite (archive, ar_block->charptr, (int) blocksize);
+ if (err != blocksize)
+ writeerror (err);
+ else if (f_totals)
+ tot_written += blocksize;
+
+
+ bytes_written = blocksize;
+ if (copy_back)
+ {
+ ar_block += copy_back;
+ bcopy ((void *) (ar_block + blocking - copy_back),
+ (void *) ar_record,
+ copy_back * RECORDSIZE);
+ ar_record += copy_back;
+
+ if (real_s_sizeleft >= copy_back * RECORDSIZE)
+ real_s_sizeleft -= copy_back * RECORDSIZE;
+ else if ((real_s_sizeleft + RECORDSIZE - 1) / RECORDSIZE <= copy_back)
+ real_s_name[0] = '\0';
+ else
+ {
+#ifdef __MSDOS__
+ if (save_name[1] == ':')
+ save_name += 2;
+#endif
+ while (*save_name == '/')
+ save_name++;
+
+ strcpy (real_s_name, save_name);
+ real_s_sizeleft = save_sizeleft;
+ real_s_totsize = save_totsize;
+ }
+ copy_back = 0;
+ }
+}
+
+/* Handle write errors on the archive. Write errors are always fatal */
+/* Hitting the end of a volume does not cause a write error unless the write
+* was the first block of the volume */
+
+void
+writeerror (err)
+ int err;
+{
+ if (err < 0)
+ {
+ msg_perror ("can't write to %s", ar_files[cur_ar_file]);
+ exit (EX_BADARCH);
+ }
+ else
+ {
+ msg ("only wrote %u of %u bytes to %s", err, blocksize, ar_files[cur_ar_file]);
+ exit (EX_BADARCH);
+ }
+}
+
+/*
+ * Handle read errors on the archive.
+ *
+ * If the read should be retried, readerror() returns to the caller.
+ */
+void
+readerror ()
+{
+# define READ_ERROR_MAX 10
+
+ read_error_flag++; /* Tell callers */
+
+ msg_perror ("read error on %s", ar_files[cur_ar_file]);
+
+ if (baserec == 0)
+ {
+ /* First block of tape. Probably stupidity error */
+ exit (EX_BADARCH);
+ }
+
+ /*
+ * Read error in mid archive. We retry up to READ_ERROR_MAX times
+ * and then give up on reading the archive. We set read_error_flag
+ * for our callers, so they can cope if they want.
+ */
+ if (r_error_count++ > READ_ERROR_MAX)
+ {
+ msg ("Too many errors, quitting.");
+ exit (EX_BADARCH);
+ }
+ return;
+}
+
+
+/*
+ * Perform a read to flush the buffer.
+ */
+void
+fl_read ()
+{
+ int err; /* Result from system call */
+ int left; /* Bytes left */
+ char *more; /* Pointer to next byte to read */
+
+ if (f_checkpoint && !(++checkpoint % 10))
+ msg ("Read checkpoint %d\n", checkpoint);
+
+ /*
+ * Clear the count of errors. This only applies to a single
+ * call to fl_read. We leave read_error_flag alone; it is
+ * only turned off by higher level software.
+ */
+ r_error_count = 0; /* Clear error count */
+
+ /*
+ * If we are about to wipe out a record that
+ * somebody needs to keep, copy it out to a holding
+ * area and adjust somebody's pointer to it.
+ */
+ if (save_rec &&
+ *save_rec >= ar_record &&
+ *save_rec < ar_last)
+ {
+ record_save_area = **save_rec;
+ *save_rec = &record_save_area;
+ }
+ if (write_archive_to_stdout && baserec != 0)
+ {
+ err = rmtwrite (1, ar_block->charptr, blocksize);
+ if (err != blocksize)
+ writeerror (err);
+ }
+ if (f_multivol)
+ {
+ if (save_name)
+ {
+ if (save_name != real_s_name)
+ {
+#ifdef __MSDOS__
+ if (save_name[1] == ':')
+ save_name += 2;
+#endif
+ while (*save_name == '/')
+ save_name++;
+
+ strcpy (real_s_name, save_name);
+ save_name = real_s_name;
+ }
+ real_s_totsize = save_totsize;
+ real_s_sizeleft = save_sizeleft;
+
+ }
+ else
+ {
+ real_s_name[0] = '\0';
+ real_s_totsize = 0;
+ real_s_sizeleft = 0;
+ }
+ }
+
+error_loop:
+ err = rmtread (archive, ar_block->charptr, (int) blocksize);
+ if (err == blocksize)
+ return;
+
+ if ((err == 0 || (err < 0 && errno == ENOSPC) || (err > 0 && !f_reblock)) && f_multivol)
+ {
+ union record *head;
+
+ try_volume:
+ if (new_volume ((cmd_mode == CMD_APPEND || cmd_mode == CMD_CAT || cmd_mode == CMD_UPDATE) ? 2 : 1) < 0)
+ return;
+ vol_error:
+ err = rmtread (archive, ar_block->charptr, (int) blocksize);
+ if (err < 0)
+ {
+ readerror ();
+ goto vol_error;
+ }
+ if (err != blocksize)
+ goto short_read;
+
+ head = ar_block;
+
+ if (head->header.linkflag == LF_VOLHDR)
+ {
+ if (f_volhdr)
+ {
+#if 0
+ char *ptr;
+
+ ptr = (char *) malloc (strlen (f_volhdr) + 20);
+ sprintf (ptr, "%s Volume %d", f_volhdr, volno);
+#endif
+ if (re_match (label_pattern, head->header.arch_name,
+ strlen (head->header.arch_name),
+ 0, 0) < 0)
+ {
+ msg ("Volume mismatch! %s!=%s", f_volhdr,
+ head->header.arch_name);
+ --volno;
+ --global_volno;
+ goto try_volume;
+ }
+
+#if 0
+ if (strcmp (ptr, head->header.name))
+ {
+ msg ("Volume mismatch! %s!=%s", ptr, head->header.name);
+ --volno;
+ --global_volno;
+ free (ptr);
+ goto try_volume;
+ }
+ free (ptr);
+#endif
+ }
+ if (f_verbose)
+ fprintf (msg_file, "Reading %s\n", head->header.arch_name);
+ head++;
+ }
+ else if (f_volhdr)
+ {
+ msg ("Warning: No volume header!");
+ }
+
+ if (real_s_name[0])
+ {
+ long from_oct ();
+
+ if (head->header.linkflag != LF_MULTIVOL || strcmp (head->header.arch_name, real_s_name))
+ {
+ msg ("%s is not continued on this volume!", real_s_name);
+ --volno;
+ --global_volno;
+ goto try_volume;
+ }
+ if (real_s_totsize != from_oct (1 + 12, head->header.size) + from_oct (1 + 12, head->header.offset))
+ {
+ msg ("%s is the wrong size (%ld!=%ld+%ld)",
+ head->header.arch_name, save_totsize,
+ from_oct (1 + 12, head->header.size),
+ from_oct (1 + 12, head->header.offset));
+ --volno;
+ --global_volno;
+ goto try_volume;
+ }
+ if (real_s_totsize - real_s_sizeleft != from_oct (1 + 12, head->header.offset))
+ {
+ msg ("This volume is out of sequence");
+ --volno;
+ --global_volno;
+ goto try_volume;
+ }
+ head++;
+ }
+ ar_record = head;
+ return;
+ }
+ else if (err < 0)
+ {
+ readerror ();
+ goto error_loop; /* Try again */
+ }
+
+short_read:
+ more = ar_block->charptr + err;
+ left = blocksize - err;
+
+again:
+ if (0 == (((unsigned) left) % RECORDSIZE))
+ {
+ /* FIXME, for size=0, multi vol support */
+ /* On the first block, warn about the problem */
+ if (!f_reblock && baserec == 0 && f_verbose && err > 0)
+ {
+ /* msg("Blocksize = %d record%s",
+ err / RECORDSIZE, (err > RECORDSIZE)? "s": "");*/
+ msg ("Blocksize = %d records", err / RECORDSIZE);
+ }
+ ar_last = ar_block + ((unsigned) (blocksize - left)) / RECORDSIZE;
+ return;
+ }
+ if (f_reblock)
+ {
+ /*
+ * User warned us about this. Fix up.
+ */
+ if (left > 0)
+ {
+ error2loop:
+ err = rmtread (archive, more, (int) left);
+ if (err < 0)
+ {
+ readerror ();
+ goto error2loop; /* Try again */
+ }
+ if (err == 0)
+ {
+ msg ("archive %s EOF not on block boundary", ar_files[cur_ar_file]);
+ exit (EX_BADARCH);
+ }
+ left -= err;
+ more += err;
+ goto again;
+ }
+ }
+ else
+ {
+ msg ("only read %d bytes from archive %s", err, ar_files[cur_ar_file]);
+ exit (EX_BADARCH);
+ }
+}
+
+
+/*
+ * Flush the current buffer to/from the archive.
+ */
+void
+flush_archive ()
+{
+ int c;
+
+ baserec += ar_last - ar_block;/* Keep track of block #s */
+ ar_record = ar_block; /* Restore pointer to start */
+ ar_last = ar_block + blocking;/* Restore pointer to end */
+
+ if (ar_reading)
+ {
+ if (time_to_start_writing)
+ {
+ time_to_start_writing = 0;
+ ar_reading = 0;
+
+ if (file_to_switch_to >= 0)
+ {
+ if ((c = rmtclose (archive)) < 0)
+ msg_perror ("Warning: can't close %s(%d,%d)", ar_files[cur_ar_file], archive, c);
+
+ archive = file_to_switch_to;
+ }
+ else
+ (void) backspace_output ();
+ fl_write ();
+ }
+ else
+ fl_read ();
+ }
+ else
+ {
+ fl_write ();
+ }
+}
+
+/* Backspace the archive descriptor by one blocks worth.
+ If its a tape, MTIOCTOP will work. If its something else,
+ we try to seek on it. If we can't seek, we lose! */
+int
+backspace_output ()
+{
+ long cur;
+ /* int er; */
+ extern char *output_start;
+
+#ifdef MTIOCTOP
+ struct mtop t;
+
+ t.mt_op = MTBSR;
+ t.mt_count = 1;
+ if ((rmtioctl (archive, MTIOCTOP, &t)) >= 0)
+ return 1;
+ if (errno == EIO && (rmtioctl (archive, MTIOCTOP, &t)) >= 0)
+ return 1;
+#endif
+
+ cur = rmtlseek (archive, 0L, 1);
+ cur -= blocksize;
+ /* Seek back to the beginning of this block and
+ start writing there. */
+
+ if (rmtlseek (archive, cur, 0) != cur)
+ {
+ /* Lseek failed. Try a different method */
+ msg ("Couldn't backspace archive file. It may be unreadable without -i.");
+ /* Replace the first part of the block with nulls */
+ if (ar_block->charptr != output_start)
+ bzero (ar_block->charptr, output_start - ar_block->charptr);
+ return 2;
+ }
+ return 3;
+}
+
+
+/*
+ * Close the archive file.
+ */
+void
+close_archive ()
+{
+ int child;
+ int status;
+ int c;
+
+ if (time_to_start_writing || !ar_reading)
+ flush_archive ();
+ if (cmd_mode == CMD_DELETE)
+ {
+ off_t pos;
+
+ pos = rmtlseek (archive, 0L, 1);
+#ifndef __MSDOS__
+ (void) ftruncate (archive, pos);
+#else
+ (void) rmtwrite (archive, "", 0);
+#endif
+ }
+ if (f_verify)
+ verify_volume ();
+
+ if ((c = rmtclose (archive)) < 0)
+ msg_perror ("Warning: can't close %s(%d,%d)", ar_files[cur_ar_file], archive, c);
+
+#ifndef __MSDOS__
+ if (childpid)
+ {
+ /*
+ * Loop waiting for the right child to die, or for
+ * no more kids.
+ */
+ while (((child = wait (&status)) != childpid) && child != -1)
+ ;
+
+ if (child != -1)
+ {
+ if (WIFSIGNALED (status))
+ {
+ /* SIGPIPE is OK, everything else is a problem. */
+ if (WTERMSIG (status) != SIGPIPE)
+ msg ("child died with signal %d%s", WTERMSIG (status),
+ WIFCOREDUMPED (status) ? " (core dumped)" : "");
+ }
+ else
+ {
+ /* Child voluntarily terminated -- but why? */
+ if (WEXITSTATUS (status) == MAGIC_STAT)
+ {
+ exit (EX_SYSTEM); /* Child had trouble */
+ }
+ if (WEXITSTATUS (status) == (SIGPIPE + 128))
+ {
+ /*
+ * /bin/sh returns this if its child
+ * dies with SIGPIPE. 'Sok.
+ */
+ /* Do nothing. */
+ }
+ else if (WEXITSTATUS (status))
+ msg ("child returned status %d",
+ WEXITSTATUS (status));
+ }
+ }
+ }
+#endif /* __MSDOS__ */
+}
+
+
+#ifdef DONTDEF
+/*
+ * Message management.
+ *
+ * anno writes a message prefix on stream (eg stdout, stderr).
+ *
+ * The specified prefix is normally output followed by a colon and a space.
+ * However, if other command line options are set, more output can come
+ * out, such as the record # within the archive.
+ *
+ * If the specified prefix is NULL, no output is produced unless the
+ * command line option(s) are set.
+ *
+ * If the third argument is 1, the "saved" record # is used; if 0, the
+ * "current" record # is used.
+ */
+void
+anno (stream, prefix, savedp)
+ FILE *stream;
+ char *prefix;
+ int savedp;
+{
+# define MAXANNO 50
+ char buffer[MAXANNO]; /* Holds annorecment */
+# define ANNOWIDTH 13
+ int space;
+ long offset;
+ int save_e;
+
+ save_e = errno;
+ /* Make sure previous output gets out in sequence */
+ if (stream == stderr)
+ fflush (stdout);
+ if (f_sayblock)
+ {
+ if (prefix)
+ {
+ fputs (prefix, stream);
+ putc (' ', stream);
+ }
+ offset = ar_record - ar_block;
+ (void) sprintf (buffer, "rec %d: ",
+ savedp ? saved_recno :
+ baserec + offset);
+ fputs (buffer, stream);
+ space = ANNOWIDTH - strlen (buffer);
+ if (space > 0)
+ {
+ fprintf (stream, "%*s", space, "");
+ }
+ }
+ else if (prefix)
+ {
+ fputs (prefix, stream);
+ fputs (": ", stream);
+ }
+ errno = save_e;
+}
+
+#endif
+
+/* Called to initialize the global volume number. */
+void
+init_volume_number ()
+{
+ FILE *vf;
+
+ vf = fopen (f_volno_file, "r");
+ if (!vf && errno != ENOENT)
+ msg_perror ("%s", f_volno_file);
+
+ if (vf)
+ {
+ fscanf (vf, "%d", &global_volno);
+ fclose (vf);
+ }
+}
+
+/* Called to write out the closing global volume number. */
+void
+closeout_volume_number ()
+{
+ FILE *vf;
+
+ vf = fopen (f_volno_file, "w");
+ if (!vf)
+ msg_perror ("%s", f_volno_file);
+ else
+ {
+ fprintf (vf, "%d\n", global_volno);
+ fclose (vf);
+ }
+}
+
+/* We've hit the end of the old volume. Close it and open the next one */
+/* Values for type: 0: writing 1: reading 2: updating */
+int
+new_volume (type)
+ int type;
+{
+ int c;
+ char inbuf[80];
+ char *p;
+ static FILE *read_file = 0;
+ extern int now_verifying;
+ extern char TTY_NAME[];
+ static int looped = 0;
+
+ if (!read_file && !f_run_script_at_end)
+ read_file = (archive == 0) ? fopen (TTY_NAME, "r") : stdin;
+
+ if (now_verifying)
+ return -1;
+ if (f_verify)
+ verify_volume ();
+ if ((c = rmtclose (archive)) < 0)
+ msg_perror ("Warning: can't close %s(%d,%d)", ar_files[cur_ar_file], archive, c);
+
+ global_volno++;
+ volno++;
+ cur_ar_file++;
+ if (cur_ar_file == n_ar_files)
+ {
+ cur_ar_file = 0;
+ looped = 1;
+ }
+
+tryagain:
+ if (looped)
+ {
+ /* We have to prompt from now on. */
+ if (f_run_script_at_end)
+ {
+ closeout_volume_number ();
+ system (info_script);
+ }
+ else
+ for (;;)
+ {
+ fprintf (msg_file, "\007Prepare volume #%d for %s and hit return: ", global_volno, ar_files[cur_ar_file]);
+ fflush (msg_file);
+ if (fgets (inbuf, sizeof (inbuf), read_file) == 0)
+ {
+ fprintf (msg_file, "EOF? What does that mean?");
+ if (cmd_mode != CMD_EXTRACT && cmd_mode != CMD_LIST && cmd_mode != CMD_DIFF)
+ msg ("Warning: Archive is INCOMPLETE!");
+ exit (EX_BADARCH);
+ }
+ if (inbuf[0] == '\n' || inbuf[0] == 'y' || inbuf[0] == 'Y')
+ break;
+
+ switch (inbuf[0])
+ {
+ case '?':
+ {
+ fprintf (msg_file, "\
+ n [name] Give a new filename for the next (and subsequent) volume(s)\n\
+ q Abort tar\n\
+ ! Spawn a subshell\n\
+ ? Print this list\n");
+ }
+ break;
+
+ case 'q': /* Quit */
+ fprintf (msg_file, "No new volume; exiting.\n");
+ if (cmd_mode != CMD_EXTRACT && cmd_mode != CMD_LIST && cmd_mode != CMD_DIFF)
+ msg ("Warning: Archive is INCOMPLETE!");
+ exit (EX_BADARCH);
+
+ case 'n': /* Get new file name */
+ {
+ char *q, *r;
+ static char *old_name;
+
+ for (q = &inbuf[1]; *q == ' ' || *q == '\t'; q++)
+ ;
+ for (r = q; *r; r++)
+ if (*r == '\n')
+ *r = '\0';
+ old_name = p = (char *) malloc ((unsigned) (strlen (q) + 2));
+ if (p == 0)
+ {
+ msg ("Can't allocate memory for name");
+ exit (EX_SYSTEM);
+ }
+ (void) strcpy (p, q);
+ ar_files[cur_ar_file] = p;
+ }
+ break;
+
+ case '!':
+#ifdef __MSDOS__
+ spawnl (P_WAIT, getenv ("COMSPEC"), "-", 0);
+#else
+ /* JF this needs work! */
+ switch (fork ())
+ {
+ case -1:
+ msg_perror ("can't fork!");
+ break;
+ case 0:
+ p = getenv ("SHELL");
+ if (p == 0)
+ p = "/bin/sh";
+ execlp (p, "-sh", "-i", 0);
+ msg_perror ("can't exec a shell %s", p);
+ _exit (55);
+ default:
+ wait (0);
+ break;
+ }
+#endif
+ break;
+ }
+ }
+ }
+
+
+ if (type == 2 || f_verify)
+ archive = rmtopen (ar_files[cur_ar_file], O_RDWR | O_CREAT, 0666);
+ else if (type == 1)
+ archive = rmtopen (ar_files[cur_ar_file], O_RDONLY, 0666);
+ else if (type == 0)
+ archive = rmtcreat (ar_files[cur_ar_file], 0666);
+ else
+ archive = -1;
+
+ if (archive < 0)
+ {
+ msg_perror ("can't open %s", ar_files[cur_ar_file]);
+ goto tryagain;
+ }
+#ifdef __MSDOS__
+ setmode (archive, O_BINARY);
+#endif
+ return 0;
+}
+
+/* this is a useless function that takes a buffer returned by wantbytes
+ and does nothing with it. If the function called by wantbytes returns
+ an error indicator (non-zero), this function is called for the rest of
+ the file.
+ */
+int
+no_op (size, data)
+ int size;
+ char *data;
+{
+ return 0;
+}
+
+/* Some other routine wants SIZE bytes in the archive. For each chunk of
+ the archive, call FUNC with the size of the chunk, and the address of
+ the chunk it can work with.
+ */
+int
+wantbytes (size, func)
+ long size;
+ int (*func) ();
+{
+ char *data;
+ long data_size;
+
+ while (size)
+ {
+ data = findrec ()->charptr;
+ if (data == NULL)
+ { /* Check it... */
+ msg ("Unexpected EOF on archive file");
+ return -1;
+ }
+ data_size = endofrecs ()->charptr - data;
+ if (data_size > size)
+ data_size = size;
+ if ((*func) (data_size, data))
+ func = no_op;
+ userec ((union record *) (data + data_size - 1));
+ size -= data_size;
+ }
+ return 0;
+}
diff --git a/gnu/usr.bin/tar/create.c b/gnu/usr.bin/tar/create.c
new file mode 100644
index 0000000..62b9c51
--- /dev/null
+++ b/gnu/usr.bin/tar/create.c
@@ -0,0 +1,1454 @@
+/* Create a tar archive.
+ Copyright (C) 1985, 1992, 1993 Free Software Foundation
+
+This file is part of GNU Tar.
+
+GNU Tar is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Tar is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Tar; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * Create a tar archive.
+ *
+ * Written 25 Aug 1985 by John Gilmore, ihnp4!hoptoad!gnu.
+ */
+
+#ifdef _AIX
+ #pragma alloca
+#endif
+#include <sys/types.h>
+#include <stdio.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+
+#ifdef BSD42
+#include <sys/file.h>
+#else
+#ifndef V7
+#include <fcntl.h>
+#endif
+#endif
+
+#include "tar.h"
+#include "port.h"
+
+#ifndef __MSDOS__
+#include <pwd.h>
+#include <grp.h>
+#endif
+
+#if defined (_POSIX_VERSION)
+#include <utime.h>
+#else
+struct utimbuf
+{
+ long actime;
+ long modtime;
+};
+
+#endif
+
+extern struct stat hstat; /* Stat struct corresponding */
+
+#ifndef __MSDOS__
+extern dev_t ar_dev;
+extern ino_t ar_ino;
+#endif
+
+/* JF */
+extern struct name *gnu_list_name;
+
+/*
+ * If there are no symbolic links, there is no lstat(). Use stat().
+ */
+#ifndef S_ISLNK
+#define lstat stat
+#endif
+
+extern void print_header ();
+
+union record *start_header ();
+void blank_name_list ();
+int check_exclude ();
+PTR ck_malloc ();
+PTR ck_realloc ();
+void clear_buffer ();
+void close_archive ();
+void collect_and_sort_names ();
+int confirm ();
+int deal_with_sparse ();
+void find_new_file_size ();
+void finish_header ();
+int finish_sparse_file ();
+void finduname ();
+void findgname ();
+int is_dot_or_dotdot ();
+void open_archive ();
+char *name_next ();
+void name_close ();
+void to_oct ();
+void dump_file ();
+void write_dir_file ();
+void write_eot ();
+void write_long ();
+int zero_record ();
+
+/* This code moved from tar.h since create.c is the only file that cares
+ about 'struct link's. This means that other files might not have to
+ include sys/types.h any more. */
+
+struct link
+ {
+ struct link *next;
+ dev_t dev;
+ ino_t ino;
+ short linkcount;
+ char name[1];
+ };
+
+struct link *linklist; /* Points to first link in list */
+
+static nolinks; /* Gets set if we run out of RAM */
+
+/*
+ * "Scratch" space to store the information about a sparse file before
+ * writing the info into the header or extended header
+ */
+/* struct sp_array *sparsearray;*/
+
+/* number of elts storable in the sparsearray */
+/*int sparse_array_size = 10;*/
+
+void
+create_archive ()
+{
+ register char *p;
+ char *name_from_list ();
+
+ open_archive (0); /* Open for writing */
+
+ if (f_gnudump)
+ {
+ char *buf = ck_malloc (PATH_MAX);
+ char *q, *bufp;
+
+ collect_and_sort_names ();
+
+ while (p = name_from_list ())
+ dump_file (p, -1, 1);
+ /* if(!f_dironly) { */
+ blank_name_list ();
+ while (p = name_from_list ())
+ {
+ strcpy (buf, p);
+ if (p[strlen (p) - 1] != '/')
+ strcat (buf, "/");
+ bufp = buf + strlen (buf);
+ for (q = gnu_list_name->dir_contents; q && *q; q += strlen (q) + 1)
+ {
+ if (*q == 'Y')
+ {
+ strcpy (bufp, q + 1);
+ dump_file (buf, -1, 1);
+ }
+ }
+ }
+ /* } */
+ free (buf);
+ }
+ else
+ {
+ while (p = name_next (1))
+ dump_file (p, -1, 1);
+ }
+
+ write_eot ();
+ close_archive ();
+ if (f_gnudump)
+ write_dir_file ();
+ name_close ();
+}
+
+/*
+ * Dump a single file. If it's a directory, recurse.
+ * Result is 1 for success, 0 for failure.
+ * Sets global "hstat" to stat() output for this file.
+ */
+void
+dump_file (p, curdev, toplevel)
+ char *p; /* File name to dump */
+ int curdev; /* Device our parent dir was on */
+ int toplevel; /* Whether we are a toplevel call */
+{
+ union record *header;
+ char type;
+ extern char *save_name; /* JF for multi-volume support */
+ extern long save_totsize;
+ extern long save_sizeleft;
+ union record *exhdr;
+ char save_linkflag;
+ extern time_t new_time;
+ int critical_error = 0;
+ struct utimbuf restore_times;
+ /* int sparse_ind = 0;*/
+
+
+ if (f_confirm && !confirm ("add", p))
+ return;
+
+ /*
+ * Use stat if following (rather than dumping) 4.2BSD's
+ * symbolic links. Otherwise, use lstat (which, on non-4.2
+ * systems, is #define'd to stat anyway.
+ */
+#ifdef STX_HIDDEN /* AIX */
+ if (0 != f_follow_links ?
+ statx (p, &hstat, STATSIZE, STX_HIDDEN) :
+ statx (p, &hstat, STATSIZE, STX_HIDDEN | STX_LINK))
+#else
+ if (0 != f_follow_links ? stat (p, &hstat) : lstat (p, &hstat))
+#endif
+ {
+ badperror:
+ msg_perror ("can't add file %s", p);
+ badfile:
+ if (!f_ignore_failed_read || critical_error)
+ errors++;
+ return;
+ }
+
+ restore_times.actime = hstat.st_atime;
+ restore_times.modtime = hstat.st_mtime;
+
+#ifdef S_ISHIDDEN
+ if (S_ISHIDDEN (hstat.st_mode))
+ {
+ char *new = (char *) alloca (strlen (p) + 2);
+ if (new)
+ {
+ strcpy (new, p);
+ strcat (new, "@");
+ p = new;
+ }
+ }
+#endif
+
+ /* See if we only want new files, and check if this one is too old to
+ put in the archive. */
+ if (f_new_files
+ && !f_gnudump
+ && new_time > hstat.st_mtime
+ && !S_ISDIR (hstat.st_mode)
+ && (f_new_files > 1 || new_time > hstat.st_ctime))
+ {
+ if (curdev == -1)
+ {
+ msg ("%s: is unchanged; not dumped", p);
+ }
+ return;
+ }
+
+#ifndef __MSDOS__
+ /* See if we are trying to dump the archive */
+ if (ar_dev && hstat.st_dev == ar_dev && hstat.st_ino == ar_ino)
+ {
+ msg ("%s is the archive; not dumped", p);
+ return;
+ }
+#endif
+ /*
+ * Check for multiple links.
+ *
+ * We maintain a list of all such files that we've written so
+ * far. Any time we see another, we check the list and
+ * avoid dumping the data again if we've done it once already.
+ */
+ if (hstat.st_nlink > 1
+ && (S_ISREG (hstat.st_mode)
+#ifdef S_ISCTG
+ || S_ISCTG (hstat.st_mode)
+#endif
+#ifdef S_ISCHR
+ || S_ISCHR (hstat.st_mode)
+#endif
+#ifdef S_ISBLK
+ || S_ISBLK (hstat.st_mode)
+#endif
+#ifdef S_ISFIFO
+ || S_ISFIFO (hstat.st_mode)
+#endif
+ ))
+ {
+ register struct link *lp;
+
+ /* First quick and dirty. Hashing, etc later FIXME */
+ for (lp = linklist; lp; lp = lp->next)
+ {
+ if (lp->ino == hstat.st_ino &&
+ lp->dev == hstat.st_dev)
+ {
+ char *link_name = lp->name;
+
+ /* We found a link. */
+ while (!f_absolute_paths && *link_name == '/')
+ {
+ static int link_warn = 0;
+
+ if (!link_warn)
+ {
+ msg ("Removing leading / from absolute links");
+ link_warn++;
+ }
+ link_name++;
+ }
+ if (link_name - lp->name >= NAMSIZ)
+ write_long (link_name, LF_LONGLINK);
+ current_link_name = link_name;
+
+ hstat.st_size = 0;
+ header = start_header (p, &hstat);
+ if (header == NULL)
+ {
+ critical_error = 1;
+ goto badfile;
+ }
+ strncpy (header->header.arch_linkname,
+ link_name, NAMSIZ);
+
+ /* Force null truncated */
+ header->header.arch_linkname[NAMSIZ - 1] = 0;
+
+ header->header.linkflag = LF_LINK;
+ finish_header (header);
+ /* FIXME: Maybe remove from list after all links found? */
+ if (f_remove_files)
+ {
+ if (unlink (p) == -1)
+ msg_perror ("cannot remove %s", p);
+ }
+ return; /* We dumped it */
+ }
+ }
+
+ /* Not found. Add it to the list of possible links. */
+ lp = (struct link *) ck_malloc ((unsigned) (sizeof (struct link) + strlen (p)));
+ if (!lp)
+ {
+ if (!nolinks)
+ {
+ msg (
+ "no memory for links, they will be dumped as separate files");
+ nolinks++;
+ }
+ }
+ lp->ino = hstat.st_ino;
+ lp->dev = hstat.st_dev;
+ strcpy (lp->name, p);
+ lp->next = linklist;
+ linklist = lp;
+ }
+
+ /*
+ * This is not a link to a previously dumped file, so dump it.
+ */
+ if (S_ISREG (hstat.st_mode)
+#ifdef S_ISCTG
+ || S_ISCTG (hstat.st_mode)
+#endif
+ )
+ {
+ int f; /* File descriptor */
+ long bufsize, count;
+ long sizeleft;
+ register union record *start;
+ int header_moved;
+ char isextended = 0;
+ int upperbound;
+ /* int end_nulls = 0; */
+
+ header_moved = 0;
+
+#ifdef BSD42
+ if (f_sparse_files)
+ {
+ /*
+ * JK - This is the test for sparseness: whether the
+ * "size" of the file matches the number of blocks
+ * allocated for it. If there is a smaller number
+ * of blocks that would be necessary to accommodate
+ * a file of this size, we have a sparse file, i.e.,
+ * at least one of those records in the file is just
+ * a useless hole.
+ */
+#ifdef hpux /* Nice of HPUX to gratuitiously change it, huh? - mib */
+ if (hstat.st_size - (hstat.st_blocks * 1024) > 1024)
+#else
+ if (hstat.st_size - (hstat.st_blocks * RECORDSIZE) > RECORDSIZE)
+#endif
+ {
+ int filesize = hstat.st_size;
+ register int i;
+
+ header = start_header (p, &hstat);
+ if (header == NULL)
+ {
+ critical_error = 1;
+ goto badfile;
+ }
+ header->header.linkflag = LF_SPARSE;
+ header_moved++;
+
+ /*
+ * Call the routine that figures out the
+ * layout of the sparse file in question.
+ * UPPERBOUND is the index of the last
+ * element of the "sparsearray," i.e.,
+ * the number of elements it needed to
+ * describe the file.
+ */
+
+ upperbound = deal_with_sparse (p, header);
+
+ /*
+ * See if we'll need an extended header
+ * later
+ */
+ if (upperbound > SPARSE_IN_HDR - 1)
+ header->header.isextended++;
+ /*
+ * We store the "real" file size so
+ * we can show that in case someone wants
+ * to list the archive, i.e., tar tvf <file>.
+ * It might be kind of disconcerting if the
+ * shrunken file size was the one that showed
+ * up.
+ */
+ to_oct ((long) hstat.st_size, 1 + 12,
+ header->header.realsize);
+
+ /*
+ * This will be the new "size" of the
+ * file, i.e., the size of the file
+ * minus the records of holes that we're
+ * skipping over.
+ */
+
+ find_new_file_size (&filesize, upperbound);
+ hstat.st_size = filesize;
+ to_oct ((long) filesize, 1 + 12,
+ header->header.size);
+ /* to_oct((long) end_nulls, 1+12,
+ header->header.ending_blanks);*/
+
+ for (i = 0; i < SPARSE_IN_HDR; i++)
+ {
+ if (!sparsearray[i].numbytes)
+ break;
+ to_oct (sparsearray[i].offset, 1 + 12,
+ header->header.sp[i].offset);
+ to_oct (sparsearray[i].numbytes, 1 + 12,
+ header->header.sp[i].numbytes);
+ }
+
+ }
+ }
+#else
+ upperbound = SPARSE_IN_HDR - 1;
+#endif
+
+ sizeleft = hstat.st_size;
+ /* Don't bother opening empty, world readable files. */
+ if (sizeleft > 0 || 0444 != (0444 & hstat.st_mode))
+ {
+ f = open (p, O_RDONLY | O_BINARY);
+ if (f < 0)
+ goto badperror;
+ }
+ else
+ {
+ f = -1;
+ }
+
+ /* If the file is sparse, we've already taken care of this */
+ if (!header_moved)
+ {
+ header = start_header (p, &hstat);
+ if (header == NULL)
+ {
+ if (f >= 0)
+ (void) close (f);
+ critical_error = 1;
+ goto badfile;
+ }
+ }
+#ifdef S_ISCTG
+ /* Mark contiguous files, if we support them */
+ if (f_standard && S_ISCTG (hstat.st_mode))
+ {
+ header->header.linkflag = LF_CONTIG;
+ }
+#endif
+ isextended = header->header.isextended;
+ save_linkflag = header->header.linkflag;
+ finish_header (header);
+ if (isextended)
+ {
+ /* int sum = 0;*/
+ register int i;
+ /* register union record *exhdr;*/
+ /* int arraybound = SPARSE_EXT_HDR;*/
+ /* static */ int index_offset = SPARSE_IN_HDR;
+
+ extend:exhdr = findrec ();
+
+ if (exhdr == NULL)
+ {
+ critical_error = 1;
+ goto badfile;
+ }
+ bzero (exhdr->charptr, RECORDSIZE);
+ for (i = 0; i < SPARSE_EXT_HDR; i++)
+ {
+ if (i + index_offset > upperbound)
+ break;
+ to_oct ((long) sparsearray[i + index_offset].numbytes,
+ 1 + 12,
+ exhdr->ext_hdr.sp[i].numbytes);
+ to_oct ((long) sparsearray[i + index_offset].offset,
+ 1 + 12,
+ exhdr->ext_hdr.sp[i].offset);
+ }
+ userec (exhdr);
+ /* sum += i;
+ if (sum < upperbound)
+ goto extend;*/
+ if (index_offset + i <= upperbound)
+ {
+ index_offset += i;
+ exhdr->ext_hdr.isextended++;
+ goto extend;
+ }
+
+ }
+ if (save_linkflag == LF_SPARSE)
+ {
+ if (finish_sparse_file (f, &sizeleft, hstat.st_size, p))
+ goto padit;
+ }
+ else
+ while (sizeleft > 0)
+ {
+
+ if (f_multivol)
+ {
+ save_name = p;
+ save_sizeleft = sizeleft;
+ save_totsize = hstat.st_size;
+ }
+ start = findrec ();
+
+ bufsize = endofrecs ()->charptr - start->charptr;
+
+ if (sizeleft < bufsize)
+ {
+ /* Last read -- zero out area beyond */
+ bufsize = (int) sizeleft;
+ count = bufsize % RECORDSIZE;
+ if (count)
+ bzero (start->charptr + sizeleft,
+ (int) (RECORDSIZE - count));
+ }
+ count = read (f, start->charptr, bufsize);
+ if (count < 0)
+ {
+ msg_perror ("read error at byte %ld, reading\
+ %d bytes, in file %s", hstat.st_size - sizeleft, bufsize, p);
+ goto padit;
+ }
+ sizeleft -= count;
+
+ /* This is nonportable (the type of userec's arg). */
+ userec (start + (count - 1) / RECORDSIZE);
+
+ if (count == bufsize)
+ continue;
+ msg ("file %s shrunk by %d bytes, padding with zeros.", p, sizeleft);
+ goto padit; /* Short read */
+ }
+
+ if (f_multivol)
+ save_name = 0;
+
+ if (f >= 0)
+ (void) close (f);
+
+ if (f_remove_files)
+ {
+ if (unlink (p) == -1)
+ msg_perror ("cannot remove %s", p);
+ }
+ if (f_atime_preserve)
+ utime (p, &restore_times);
+ return;
+
+ /*
+ * File shrunk or gave error, pad out tape to match
+ * the size we specified in the header.
+ */
+ padit:
+ while (sizeleft > 0)
+ {
+ save_sizeleft = sizeleft;
+ start = findrec ();
+ bzero (start->charptr, RECORDSIZE);
+ userec (start);
+ sizeleft -= RECORDSIZE;
+ }
+ if (f_multivol)
+ save_name = 0;
+ if (f >= 0)
+ (void) close (f);
+ if (f_atime_preserve)
+ utime (p, &restore_times);
+ return;
+ }
+
+#ifdef S_ISLNK
+ else if (S_ISLNK (hstat.st_mode))
+ {
+ int size;
+ char *buf = alloca (PATH_MAX + 1);
+
+ size = readlink (p, buf, PATH_MAX + 1);
+ if (size < 0)
+ goto badperror;
+ buf[size] = '\0';
+ if (size >= NAMSIZ)
+ write_long (buf, LF_LONGLINK);
+ current_link_name = buf;
+
+ hstat.st_size = 0; /* Force 0 size on symlink */
+ header = start_header (p, &hstat);
+ if (header == NULL)
+ {
+ critical_error = 1;
+ goto badfile;
+ }
+ strncpy (header->header.arch_linkname, buf, NAMSIZ);
+ header->header.arch_linkname[NAMSIZ - 1] = '\0';
+ header->header.linkflag = LF_SYMLINK;
+ finish_header (header); /* Nothing more to do to it */
+ if (f_remove_files)
+ {
+ if (unlink (p) == -1)
+ msg_perror ("cannot remove %s", p);
+ }
+ return;
+ }
+#endif
+
+ else if (S_ISDIR (hstat.st_mode))
+ {
+ register DIR *dirp;
+ register struct dirent *d;
+ char *namebuf;
+ int buflen;
+ register int len;
+ int our_device = hstat.st_dev;
+
+ /* Build new prototype name */
+ len = strlen (p);
+ buflen = len + NAMSIZ;
+ namebuf = ck_malloc (buflen + 1);
+ strncpy (namebuf, p, buflen);
+ while (len >= 1 && '/' == namebuf[len - 1])
+ len--; /* Delete trailing slashes */
+ namebuf[len++] = '/'; /* Now add exactly one back */
+ namebuf[len] = '\0'; /* Make sure null-terminated */
+
+ /*
+ * Output directory header record with permissions
+ * FIXME, do this AFTER files, to avoid R/O dir problems?
+ * If old archive format, don't write record at all.
+ */
+ if (!f_oldarch)
+ {
+ hstat.st_size = 0; /* Force 0 size on dir */
+ /*
+ * If people could really read standard archives,
+ * this should be: (FIXME)
+ header = start_header(f_standard? p: namebuf, &hstat);
+ * but since they'd interpret LF_DIR records as
+ * regular files, we'd better put the / on the name.
+ */
+ header = start_header (namebuf, &hstat);
+ if (header == NULL)
+ {
+ critical_error = 1;
+ goto badfile; /* eg name too long */
+ }
+
+ if (f_gnudump)
+ header->header.linkflag = LF_DUMPDIR;
+ else if (f_standard)
+ header->header.linkflag = LF_DIR;
+
+ /* If we're gnudumping, we aren't done yet so don't close it. */
+ if (!f_gnudump)
+ finish_header (header); /* Done with directory header */
+ }
+
+ if (f_gnudump)
+ {
+ int sizeleft;
+ int totsize;
+ int bufsize;
+ union record *start;
+ int count;
+ char *buf, *p_buf;
+
+ buf = gnu_list_name->dir_contents; /* FOO */
+ totsize = 0;
+ for (p_buf = buf; p_buf && *p_buf;)
+ {
+ int tmp;
+
+ tmp = strlen (p_buf) + 1;
+ totsize += tmp;
+ p_buf += tmp;
+ }
+ totsize++;
+ to_oct ((long) totsize, 1 + 12, header->header.size);
+ finish_header (header);
+ p_buf = buf;
+ sizeleft = totsize;
+ while (sizeleft > 0)
+ {
+ if (f_multivol)
+ {
+ save_name = p;
+ save_sizeleft = sizeleft;
+ save_totsize = totsize;
+ }
+ start = findrec ();
+ bufsize = endofrecs ()->charptr - start->charptr;
+ if (sizeleft < bufsize)
+ {
+ bufsize = sizeleft;
+ count = bufsize % RECORDSIZE;
+ if (count)
+ bzero (start->charptr + sizeleft, RECORDSIZE - count);
+ }
+ bcopy (p_buf, start->charptr, bufsize);
+ sizeleft -= bufsize;
+ p_buf += bufsize;
+ userec (start + (bufsize - 1) / RECORDSIZE);
+ }
+ if (f_multivol)
+ save_name = 0;
+ if (f_atime_preserve)
+ utime (p, &restore_times);
+ return;
+ }
+
+ /* Now output all the files in the directory */
+#if 0
+ if (f_dironly)
+ return; /* Unless the cmdline said not to */
+#endif
+ /*
+ * See if we are crossing from one file system to another,
+ * and avoid doing so if the user only wants to dump one file system.
+ */
+ if (f_local_filesys && !toplevel && curdev != hstat.st_dev)
+ {
+ if (f_verbose)
+ msg ("%s: is on a different filesystem; not dumped", p);
+ return;
+ }
+
+
+ errno = 0;
+ dirp = opendir (p);
+ if (!dirp)
+ {
+ if (errno)
+ {
+ msg_perror ("can't open directory %s", p);
+ }
+ else
+ {
+ msg ("error opening directory %s",
+ p);
+ }
+ return;
+ }
+
+ /* Hack to remove "./" from the front of all the file names */
+ if (len == 2 && namebuf[0] == '.' && namebuf[1] == '/')
+ len = 0;
+
+ /* Should speed this up by cd-ing into the dir, FIXME */
+ while (NULL != (d = readdir (dirp)))
+ {
+ /* Skip . and .. */
+ if (is_dot_or_dotdot (d->d_name))
+ continue;
+
+ if (NLENGTH (d) + len >= buflen)
+ {
+ buflen = len + NLENGTH (d);
+ namebuf = ck_realloc (namebuf, buflen + 1);
+ /* namebuf[len]='\0';
+ msg("file name %s%s too long",
+ namebuf, d->d_name);
+ continue; */
+ }
+ strcpy (namebuf + len, d->d_name);
+ if (f_exclude && check_exclude (namebuf))
+ continue;
+ dump_file (namebuf, our_device, 0);
+ }
+
+ closedir (dirp);
+ free (namebuf);
+ if (f_atime_preserve)
+ utime (p, &restore_times);
+ return;
+ }
+
+#ifdef S_ISCHR
+ else if (S_ISCHR (hstat.st_mode))
+ {
+ type = LF_CHR;
+ }
+#endif
+
+#ifdef S_ISBLK
+ else if (S_ISBLK (hstat.st_mode))
+ {
+ type = LF_BLK;
+ }
+#endif
+
+ /* Avoid screwy apollo lossage where S_IFIFO == S_IFSOCK */
+#if (_ISP__M68K == 0) && (_ISP__A88K == 0) && defined(S_ISFIFO)
+ else if (S_ISFIFO (hstat.st_mode))
+ {
+ type = LF_FIFO;
+ }
+#endif
+
+#ifdef S_ISSOCK
+ else if (S_ISSOCK (hstat.st_mode))
+ {
+ type = LF_FIFO;
+ }
+#endif
+ else
+ goto unknown;
+
+ if (!f_standard)
+ goto unknown;
+
+ hstat.st_size = 0; /* Force 0 size */
+ header = start_header (p, &hstat);
+ if (header == NULL)
+ {
+ critical_error = 1;
+ goto badfile; /* eg name too long */
+ }
+
+ header->header.linkflag = type;
+#if defined(S_IFBLK) || defined(S_IFCHR)
+ if (type != LF_FIFO)
+ {
+ to_oct ((long) major (hstat.st_rdev), 8,
+ header->header.devmajor);
+ to_oct ((long) minor (hstat.st_rdev), 8,
+ header->header.devminor);
+ }
+#endif
+
+ finish_header (header);
+ if (f_remove_files)
+ {
+ if (unlink (p) == -1)
+ msg_perror ("cannot remove %s", p);
+ }
+ return;
+
+unknown:
+ msg ("%s: Unknown file type; file ignored.", p);
+}
+
+int
+finish_sparse_file (fd, sizeleft, fullsize, name)
+ int fd;
+ long *sizeleft, fullsize;
+ char *name;
+{
+ union record *start;
+ char tempbuf[RECORDSIZE];
+ int bufsize, sparse_ind = 0, count;
+ long pos;
+ long nwritten = 0;
+
+
+ while (*sizeleft > 0)
+ {
+ start = findrec ();
+ bzero (start->charptr, RECORDSIZE);
+ bufsize = sparsearray[sparse_ind].numbytes;
+ if (!bufsize)
+ { /* we blew it, maybe */
+ msg ("Wrote %ld of %ld bytes to file %s",
+ fullsize - *sizeleft, fullsize, name);
+ break;
+ }
+ pos = lseek (fd, sparsearray[sparse_ind++].offset, 0);
+ /*
+ * If the number of bytes to be written here exceeds
+ * the size of the temporary buffer, do it in steps.
+ */
+ while (bufsize > RECORDSIZE)
+ {
+ /* if (amt_read) {
+ count = read(fd, start->charptr+amt_read, RECORDSIZE-amt_read);
+ bufsize -= RECORDSIZE - amt_read;
+ amt_read = 0;
+ userec(start);
+ start = findrec();
+ bzero(start->charptr, RECORDSIZE);
+ }*/
+ /* store the data */
+ count = read (fd, start->charptr, RECORDSIZE);
+ if (count < 0)
+ {
+ msg_perror ("read error at byte %ld, reading %d bytes, in file %s",
+ fullsize - *sizeleft, bufsize, name);
+ return 1;
+ }
+ bufsize -= count;
+ *sizeleft -= count;
+ userec (start);
+ nwritten += RECORDSIZE; /* XXX */
+ start = findrec ();
+ bzero (start->charptr, RECORDSIZE);
+ }
+
+
+ clear_buffer (tempbuf);
+ count = read (fd, tempbuf, bufsize);
+ bcopy (tempbuf, start->charptr, RECORDSIZE);
+ if (count < 0)
+ {
+ msg_perror ("read error at byte %ld, reading %d bytes, in file %s",
+ fullsize - *sizeleft, bufsize, name);
+ return 1;
+ }
+ /* if (amt_read >= RECORDSIZE) {
+ amt_read = 0;
+ userec(start+(count-1)/RECORDSIZE);
+ if (count != bufsize) {
+ msg("file %s shrunk by %d bytes, padding with zeros.", name, sizeleft);
+ return 1;
+ }
+ start = findrec();
+ } else
+ amt_read += bufsize;*/
+ nwritten += count; /* XXX */
+ *sizeleft -= count;
+ userec (start);
+
+ }
+ free (sparsearray);
+ /* printf ("Amount actually written is (I hope) %d.\n", nwritten); */
+ /* userec(start+(count-1)/RECORDSIZE);*/
+ return 0;
+
+}
+
+void
+init_sparsearray ()
+{
+ register int i;
+
+ sp_array_size = 10;
+ /*
+ * Make room for our scratch space -- initially is 10 elts long
+ */
+ sparsearray = (struct sp_array *) ck_malloc (sp_array_size * sizeof (struct sp_array));
+ for (i = 0; i < sp_array_size; i++)
+ {
+ sparsearray[i].offset = 0;
+ sparsearray[i].numbytes = 0;
+ }
+}
+
+
+
+/*
+ * Okay, we've got a sparse file on our hands -- now, what we need to do is
+ * make a pass through the file and carefully note where any data is, i.e.,
+ * we want to find how far into the file each instance of data is, and how
+ * many bytes are there. We store this information in the sparsearray,
+ * which will later be translated into header information. For now, we use
+ * the sparsearray as convenient storage.
+ *
+ * As a side note, this routine is a mess. If I could have found a cleaner
+ * way to do it, I would have. If anyone wants to find a nicer way to do
+ * this, feel free.
+ */
+
+/* There is little point in trimming small amounts of null data at the */
+/* head and tail of blocks -- it's ok if we only avoid dumping blocks */
+/* of complete null data */
+int
+deal_with_sparse (name, header, nulls_at_end)
+ char *name;
+ union record *header;
+ int nulls_at_end;
+{
+ long numbytes = 0;
+ long offset = 0;
+ /* long save_offset;*/
+ int fd;
+ /* int current_size = hstat.st_size;*/
+ int sparse_ind = 0, cc;
+ char buf[RECORDSIZE];
+#if 0
+ int read_last_data = 0; /* did we just read the last record? */
+#endif
+ int amidst_data = 0;
+
+ header->header.isextended = 0;
+ /*
+ * Can't open the file -- this problem will be caught later on,
+ * so just return.
+ */
+ if ((fd = open (name, O_RDONLY)) < 0)
+ return 0;
+
+ init_sparsearray ();
+ clear_buffer (buf);
+
+ while ((cc = read (fd, buf, sizeof buf)) != 0)
+ {
+
+ if (sparse_ind > sp_array_size - 1)
+ {
+
+ /*
+ * realloc the scratch area, since we've run out of room --
+ */
+ sparsearray = (struct sp_array *)
+ ck_realloc (sparsearray,
+ 2 * sp_array_size * (sizeof (struct sp_array)));
+ sp_array_size *= 2;
+ }
+ if (cc == sizeof buf)
+ {
+ if (zero_record (buf))
+ {
+ if (amidst_data)
+ {
+ sparsearray[sparse_ind++].numbytes
+ = numbytes;
+ amidst_data = 0;
+ }
+ }
+ else
+ { /* !zero_record(buf) */
+ if (amidst_data)
+ numbytes += cc;
+ else
+ {
+ amidst_data = 1;
+ numbytes = cc;
+ sparsearray[sparse_ind].offset
+ = offset;
+ }
+ }
+ }
+ else if (cc < sizeof buf)
+ {
+ /* This has to be the last bit of the file, so this */
+ /* is somewhat shorter than the above. */
+ if (!zero_record (buf))
+ {
+ if (!amidst_data)
+ {
+ amidst_data = 1;
+ numbytes = cc;
+ sparsearray[sparse_ind].offset
+ = offset;
+ }
+ else
+ numbytes += cc;
+ }
+ }
+ offset += cc;
+ clear_buffer (buf);
+ }
+ if (amidst_data)
+ sparsearray[sparse_ind++].numbytes = numbytes;
+ else
+ {
+ sparsearray[sparse_ind].offset = offset-1;
+ sparsearray[sparse_ind++].numbytes = 1;
+ }
+ close (fd);
+
+ return sparse_ind - 1;
+}
+
+/*
+ * Just zeroes out the buffer so we don't confuse ourselves with leftover
+ * data.
+ */
+void
+clear_buffer (buf)
+ char *buf;
+{
+ register int i;
+
+ for (i = 0; i < RECORDSIZE; i++)
+ buf[i] = '\0';
+}
+
+#if 0 /* I'm leaving this as a monument to Joy Kendall, who wrote it -mib */
+/*
+ * JK -
+ * This routine takes a character array, and tells where within that array
+ * the data can be found. It skips over any zeros, and sets the first
+ * non-zero point in the array to be the "start", and continues until it
+ * finds non-data again, which is marked as the "end." This routine is
+ * mainly for 1) seeing how far into a file we must lseek to data, given
+ * that we have a sparse file, and 2) determining the "real size" of the
+ * file, i.e., the number of bytes in the sparse file that are data, as
+ * opposed to the zeros we are trying to skip.
+ */
+where_is_data (from, to, buffer)
+ int *from, *to;
+ char *buffer;
+{
+ register int i = 0;
+ register int save_to = *to;
+ int amidst_data = 0;
+
+
+ while (!buffer[i])
+ i++;
+ *from = i;
+
+ if (*from < 16) /* don't bother */
+ *from = 0;
+ /* keep going to make sure there isn't more real
+ data in this record */
+ while (i < RECORDSIZE)
+ {
+ if (!buffer[i])
+ {
+ if (amidst_data)
+ {
+ save_to = i;
+ amidst_data = 0;
+ }
+ i++;
+ }
+ else if (buffer[i])
+ {
+ if (!amidst_data)
+ amidst_data = 1;
+ i++;
+ }
+ }
+ if (i == RECORDSIZE)
+ *to = i;
+ else
+ *to = save_to;
+
+}
+
+#endif
+
+/* Note that this routine is only called if zero_record returned true */
+#if 0 /* But we actually don't need it at all. */
+where_is_data (from, to, buffer)
+ int *from, *to;
+ char *buffer;
+{
+ char *fp, *tp;
+
+ for (fp = buffer; !*fp; fp++)
+ ;
+ for (tp = buffer + RECORDSIZE - 1; !*tp; tp--)
+ ;
+ *from = fp - buffer;
+ *to = tp - buffer + 1;
+}
+
+#endif
+
+
+
+/*
+ * Takes a recordful of data and basically cruises through it to see if
+ * it's made *entirely* of zeros, returning a 0 the instant it finds
+ * something that is a non-zero, i.e., useful data.
+ */
+int
+zero_record (buffer)
+ char *buffer;
+{
+ register int i;
+
+ for (i = 0; i < RECORDSIZE; i++)
+ if (buffer[i] != '\000')
+ return 0;
+ return 1;
+}
+
+void
+find_new_file_size (filesize, highest_index)
+ int *filesize;
+ int highest_index;
+{
+ register int i;
+
+ *filesize = 0;
+ for (i = 0; sparsearray[i].numbytes && i <= highest_index; i++)
+ *filesize += sparsearray[i].numbytes;
+}
+
+/*
+ * Make a header block for the file name whose stat info is st .
+ * Return header pointer for success, NULL if the name is too long.
+ */
+union record *
+start_header (name, st)
+ char *name;
+ register struct stat *st;
+{
+ register union record *header;
+
+ if (strlen (name) >= NAMSIZ)
+ write_long (name, LF_LONGNAME);
+
+ header = (union record *) findrec ();
+ bzero (header->charptr, sizeof (*header)); /* XXX speed up */
+
+ /*
+ * Check the file name and put it in the record.
+ */
+ if (!f_absolute_paths)
+ {
+ static int warned_once = 0;
+#ifdef __MSDOS__
+ if (name[1] == ':')
+ {
+ name += 2;
+ if (!warned_once++)
+ msg ("Removing drive spec from names in the archive");
+ }
+#endif
+ while ('/' == *name)
+ {
+ name++; /* Force relative path */
+ if (!warned_once++)
+ msg ("Removing leading / from absolute path names in the archive.");
+ }
+ }
+ current_file_name = name;
+ strncpy (header->header.arch_name, name, NAMSIZ);
+ header->header.arch_name[NAMSIZ - 1] = '\0';
+
+ to_oct ((long) (f_oldarch ? (st->st_mode & 07777) : st->st_mode),
+ 8, header->header.mode);
+ to_oct ((long) st->st_uid, 8, header->header.uid);
+ to_oct ((long) st->st_gid, 8, header->header.gid);
+ to_oct ((long) st->st_size, 1 + 12, header->header.size);
+ to_oct ((long) st->st_mtime, 1 + 12, header->header.mtime);
+ /* header->header.linkflag is left as null */
+ if (f_gnudump)
+ {
+ to_oct ((long) st->st_atime, 1 + 12, header->header.atime);
+ to_oct ((long) st->st_ctime, 1 + 12, header->header.ctime);
+ }
+
+#ifndef NONAMES
+ /* Fill in new Unix Standard fields if desired. */
+ if (f_standard)
+ {
+ header->header.linkflag = LF_NORMAL; /* New default */
+ strcpy (header->header.magic, TMAGIC); /* Mark as Unix Std */
+ finduname (header->header.uname, st->st_uid);
+ findgname (header->header.gname, st->st_gid);
+ }
+#endif
+ return header;
+}
+
+/*
+ * Finish off a filled-in header block and write it out.
+ * We also print the file name and/or full info if verbose is on.
+ */
+void
+finish_header (header)
+ register union record *header;
+{
+ register int i, sum;
+ register char *p;
+
+ bcopy (CHKBLANKS, header->header.chksum, sizeof (header->header.chksum));
+
+ sum = 0;
+ p = header->charptr;
+ for (i = sizeof (*header); --i >= 0;)
+ {
+ /*
+ * We can't use unsigned char here because of old compilers,
+ * e.g. V7.
+ */
+ sum += 0xFF & *p++;
+ }
+
+ /*
+ * Fill in the checksum field. It's formatted differently
+ * from the other fields: it has [6] digits, a null, then a
+ * space -- rather than digits, a space, then a null.
+ * We use to_oct then write the null in over to_oct's space.
+ * The final space is already there, from checksumming, and
+ * to_oct doesn't modify it.
+ *
+ * This is a fast way to do:
+ * (void) sprintf(header->header.chksum, "%6o", sum);
+ */
+ to_oct ((long) sum, 8, header->header.chksum);
+ header->header.chksum[6] = '\0'; /* Zap the space */
+
+ userec (header);
+
+ if (f_verbose)
+ {
+ extern union record *head;/* Points to current tape header */
+ extern int head_standard; /* Tape header is in ANSI format */
+
+ /* These globals are parameters to print_header, sigh */
+ head = header;
+ /* hstat is already set up */
+ head_standard = f_standard;
+ print_header ();
+ }
+
+ return;
+}
+
+
+/*
+ * Quick and dirty octal conversion.
+ * Converts long "value" into a "digs"-digit field at "where",
+ * including a trailing space and room for a null. "digs"==3 means
+ * 1 digit, a space, and room for a null.
+ *
+ * We assume the trailing null is already there and don't fill it in.
+ * This fact is used by start_header and finish_header, so don't change it!
+ *
+ * This should be equivalent to:
+ * (void) sprintf(where, "%*lo ", digs-2, value);
+ * except that sprintf fills in the trailing null and we don't.
+ */
+void
+to_oct (value, digs, where)
+ register long value;
+ register int digs;
+ register char *where;
+{
+
+ --digs; /* Trailing null slot is left alone */
+ where[--digs] = ' '; /* Put in the space, though */
+
+ /* Produce the digits -- at least one */
+ do
+ {
+ where[--digs] = '0' + (char) (value & 7); /* one octal digit */
+ value >>= 3;
+ }
+ while (digs > 0 && value != 0);
+
+ /* Leading spaces, if necessary */
+ while (digs > 0)
+ where[--digs] = ' ';
+
+}
+
+
+/*
+ * Write the EOT record(s).
+ * We actually zero at least one record, through the end of the block.
+ * Old tar writes garbage after two zeroed records -- and PDtar used to.
+ */
+void
+write_eot ()
+{
+ union record *p;
+ int bufsize;
+
+ p = findrec ();
+ if (p)
+ {
+ bufsize = endofrecs ()->charptr - p->charptr;
+ bzero (p->charptr, bufsize);
+ userec (p);
+ }
+}
+
+/* Write a LF_LONGLINK or LF_LONGNAME record. */
+void
+write_long (p, type)
+ char *p;
+ char type;
+{
+ int size = strlen (p) + 1;
+ int bufsize;
+ union record *header;
+ struct stat foo;
+
+
+ bzero (&foo, sizeof foo);
+ foo.st_size = size;
+
+ header = start_header ("././@LongLink", &foo);
+ header->header.linkflag = type;
+ finish_header (header);
+
+ header = findrec ();
+
+ bufsize = endofrecs ()->charptr - header->charptr;
+
+ while (bufsize < size)
+ {
+ bcopy (p, header->charptr, bufsize);
+ p += bufsize;
+ size -= bufsize;
+ userec (header + (bufsize - 1) / RECORDSIZE);
+ header = findrec ();
+ bufsize = endofrecs ()->charptr - header->charptr;
+ }
+ bcopy (p, header->charptr, size);
+ bzero (header->charptr + size, bufsize - size);
+ userec (header + (size - 1) / RECORDSIZE);
+}
diff --git a/gnu/usr.bin/tar/diffarch.c b/gnu/usr.bin/tar/diffarch.c
new file mode 100644
index 0000000..ce47d9d
--- /dev/null
+++ b/gnu/usr.bin/tar/diffarch.c
@@ -0,0 +1,759 @@
+/* Diff files from a tar archive.
+ Copyright (C) 1988, 1992, 1993 Free Software Foundation
+
+This file is part of GNU Tar.
+
+GNU Tar is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Tar is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Tar; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * Diff files from a tar archive.
+ *
+ * Written 30 April 1987 by John Gilmore, ihnp4!hoptoad!gnu.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+#include <sys/types.h>
+
+#ifdef BSD42
+#include <sys/file.h>
+#else
+#ifndef V7
+#include <fcntl.h>
+#endif
+#endif
+
+#ifdef HAVE_SYS_MTIO_H
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#endif
+
+#include "tar.h"
+#include "port.h"
+#include "rmt.h"
+
+#ifndef S_ISLNK
+#define lstat stat
+#endif
+
+extern void *valloc ();
+
+extern union record *head; /* Points to current tape header */
+extern struct stat hstat; /* Stat struct corresponding */
+extern int head_standard; /* Tape header is in ANSI format */
+
+void decode_header ();
+void diff_sparse_files ();
+void fill_in_sparse_array ();
+void fl_read ();
+long from_oct ();
+int do_stat ();
+extern void print_header ();
+int read_header ();
+void saverec ();
+void sigh ();
+extern void skip_file ();
+extern void skip_extended_headers ();
+int wantbytes ();
+
+extern FILE *msg_file;
+
+int now_verifying = 0; /* Are we verifying at the moment? */
+
+int diff_fd; /* Descriptor of file we're diffing */
+
+char *diff_buf = 0; /* Pointer to area for reading
+ file contents into */
+
+char *diff_dir; /* Directory contents for LF_DUMPDIR */
+
+int different = 0;
+
+/*struct sp_array *sparsearray;
+int sp_ar_size = 10;*/
+/*
+ * Initialize for a diff operation
+ */
+void
+diff_init ()
+{
+ /*NOSTRICT*/
+ diff_buf = (char *) valloc ((unsigned) blocksize);
+ if (!diff_buf)
+ {
+ msg ("could not allocate memory for diff buffer of %d bytes",
+ blocksize);
+ exit (EX_ARGSBAD);
+ }
+}
+
+/*
+ * Diff a file against the archive.
+ */
+void
+diff_archive ()
+{
+ register char *data;
+ int check, namelen;
+ int err;
+ long offset;
+ struct stat filestat;
+ int compare_chunk ();
+ int compare_dir ();
+ int no_op ();
+#ifndef __MSDOS__
+ dev_t dev;
+ ino_t ino;
+#endif
+ char *get_dir_contents ();
+ long from_oct ();
+
+ errno = EPIPE; /* FIXME, remove perrors */
+
+ saverec (&head); /* Make sure it sticks around */
+ userec (head); /* And go past it in the archive */
+ decode_header (head, &hstat, &head_standard, 1); /* Snarf fields */
+
+ /* Print the record from 'head' and 'hstat' */
+ if (f_verbose)
+ {
+ if (now_verifying)
+ fprintf (msg_file, "Verify ");
+ print_header ();
+ }
+
+ switch (head->header.linkflag)
+ {
+
+ default:
+ msg ("Unknown file type '%c' for %s, diffed as normal file",
+ head->header.linkflag, current_file_name);
+ /* FALL THRU */
+
+ case LF_OLDNORMAL:
+ case LF_NORMAL:
+ case LF_SPARSE:
+ case LF_CONTIG:
+ /*
+ * Appears to be a file.
+ * See if it's really a directory.
+ */
+ namelen = strlen (current_file_name) - 1;
+ if (current_file_name[namelen] == '/')
+ goto really_dir;
+
+
+ if (do_stat (&filestat))
+ {
+ if (head->header.isextended)
+ skip_extended_headers ();
+ skip_file ((long) hstat.st_size);
+ different++;
+ goto quit;
+ }
+
+ if (!S_ISREG (filestat.st_mode))
+ {
+ fprintf (msg_file, "%s: not a regular file\n",
+ current_file_name);
+ skip_file ((long) hstat.st_size);
+ different++;
+ goto quit;
+ }
+
+ filestat.st_mode &= 07777;
+ if (filestat.st_mode != hstat.st_mode)
+ sigh ("mode");
+ if (filestat.st_uid != hstat.st_uid)
+ sigh ("uid");
+ if (filestat.st_gid != hstat.st_gid)
+ sigh ("gid");
+ if (filestat.st_mtime != hstat.st_mtime)
+ sigh ("mod time");
+ if (head->header.linkflag != LF_SPARSE &&
+ filestat.st_size != hstat.st_size)
+ {
+ sigh ("size");
+ skip_file ((long) hstat.st_size);
+ goto quit;
+ }
+
+ diff_fd = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
+
+ if (diff_fd < 0 && !f_absolute_paths)
+ {
+ char tmpbuf[NAMSIZ + 2];
+
+ tmpbuf[0] = '/';
+ strcpy (&tmpbuf[1], current_file_name);
+ diff_fd = open (tmpbuf, O_NDELAY | O_RDONLY);
+ }
+ if (diff_fd < 0)
+ {
+ msg_perror ("cannot open %s", current_file_name);
+ if (head->header.isextended)
+ skip_extended_headers ();
+ skip_file ((long) hstat.st_size);
+ different++;
+ goto quit;
+ }
+ /*
+ * Need to treat sparse files completely differently here.
+ */
+ if (head->header.linkflag == LF_SPARSE)
+ diff_sparse_files (hstat.st_size);
+ else
+ wantbytes ((long) (hstat.st_size), compare_chunk);
+
+ check = close (diff_fd);
+ if (check < 0)
+ msg_perror ("Error while closing %s", current_file_name);
+
+ quit:
+ break;
+
+#ifndef __MSDOS__
+ case LF_LINK:
+ if (do_stat (&filestat))
+ break;
+ dev = filestat.st_dev;
+ ino = filestat.st_ino;
+ err = stat (current_link_name, &filestat);
+ if (err < 0)
+ {
+ if (errno == ENOENT)
+ {
+ fprintf (msg_file, "%s: does not exist\n", current_file_name);
+ }
+ else
+ {
+ msg_perror ("cannot stat file %s", current_file_name);
+ }
+ different++;
+ break;
+ }
+ if (filestat.st_dev != dev || filestat.st_ino != ino)
+ {
+ fprintf (msg_file, "%s not linked to %s\n", current_file_name, current_link_name);
+ break;
+ }
+ break;
+#endif
+
+#ifdef S_ISLNK
+ case LF_SYMLINK:
+ {
+ char linkbuf[NAMSIZ + 3];
+ check = readlink (current_file_name, linkbuf,
+ (sizeof linkbuf) - 1);
+
+ if (check < 0)
+ {
+ if (errno == ENOENT)
+ {
+ fprintf (msg_file,
+ "%s: no such file or directory\n",
+ current_file_name);
+ }
+ else
+ {
+ msg_perror ("cannot read link %s", current_file_name);
+ }
+ different++;
+ break;
+ }
+
+ linkbuf[check] = '\0'; /* Null-terminate it */
+ if (strncmp (current_link_name, linkbuf, check) != 0)
+ {
+ fprintf (msg_file, "%s: symlink differs\n",
+ current_link_name);
+ different++;
+ }
+ }
+ break;
+#endif
+
+#ifdef S_IFCHR
+ case LF_CHR:
+ hstat.st_mode |= S_IFCHR;
+ goto check_node;
+#endif
+
+#ifdef S_IFBLK
+ /* If local system doesn't support block devices, use default case */
+ case LF_BLK:
+ hstat.st_mode |= S_IFBLK;
+ goto check_node;
+#endif
+
+#ifdef S_ISFIFO
+ /* If local system doesn't support FIFOs, use default case */
+ case LF_FIFO:
+#ifdef S_IFIFO
+ hstat.st_mode |= S_IFIFO;
+#endif
+ hstat.st_rdev = 0; /* FIXME, do we need this? */
+ goto check_node;
+#endif
+
+ check_node:
+ /* FIXME, deal with umask */
+ if (do_stat (&filestat))
+ break;
+ if (hstat.st_rdev != filestat.st_rdev)
+ {
+ fprintf (msg_file, "%s: device numbers changed\n", current_file_name);
+ different++;
+ break;
+ }
+#ifdef S_IFMT
+ if (hstat.st_mode != filestat.st_mode)
+#else /* POSIX lossage */
+ if ((hstat.st_mode & 07777) != (filestat.st_mode & 07777))
+#endif
+ {
+ fprintf (msg_file, "%s: mode or device-type changed\n", current_file_name);
+ different++;
+ break;
+ }
+ break;
+
+ case LF_DUMPDIR:
+ data = diff_dir = get_dir_contents (current_file_name, 0);
+ if (data)
+ {
+ wantbytes ((long) (hstat.st_size), compare_dir);
+ free (data);
+ }
+ else
+ wantbytes ((long) (hstat.st_size), no_op);
+ /* FALL THROUGH */
+
+ case LF_DIR:
+ /* Check for trailing / */
+ namelen = strlen (current_file_name) - 1;
+ really_dir:
+ while (namelen && current_file_name[namelen] == '/')
+ current_file_name[namelen--] = '\0'; /* Zap / */
+
+ if (do_stat (&filestat))
+ break;
+ if (!S_ISDIR (filestat.st_mode))
+ {
+ fprintf (msg_file, "%s is no longer a directory\n", current_file_name);
+ different++;
+ break;
+ }
+ if ((filestat.st_mode & 07777) != (hstat.st_mode & 07777))
+ sigh ("mode");
+ break;
+
+ case LF_VOLHDR:
+ break;
+
+ case LF_MULTIVOL:
+ namelen = strlen (current_file_name) - 1;
+ if (current_file_name[namelen] == '/')
+ goto really_dir;
+
+ if (do_stat (&filestat))
+ break;
+
+ if (!S_ISREG (filestat.st_mode))
+ {
+ fprintf (msg_file, "%s: not a regular file\n",
+ current_file_name);
+ skip_file ((long) hstat.st_size);
+ different++;
+ break;
+ }
+
+ filestat.st_mode &= 07777;
+ offset = from_oct (1 + 12, head->header.offset);
+ if (filestat.st_size != hstat.st_size + offset)
+ {
+ sigh ("size");
+ skip_file ((long) hstat.st_size);
+ different++;
+ break;
+ }
+
+ diff_fd = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
+
+ if (diff_fd < 0)
+ {
+ msg_perror ("cannot open file %s", current_file_name);
+ skip_file ((long) hstat.st_size);
+ different++;
+ break;
+ }
+ err = lseek (diff_fd, offset, 0);
+ if (err != offset)
+ {
+ msg_perror ("cannot seek to %ld in file %s", offset, current_file_name);
+ different++;
+ break;
+ }
+
+ wantbytes ((long) (hstat.st_size), compare_chunk);
+
+ check = close (diff_fd);
+ if (check < 0)
+ {
+ msg_perror ("Error while closing %s", current_file_name);
+ }
+ break;
+
+ }
+
+ /* We don't need to save it any longer. */
+ saverec ((union record **) 0);/* Unsave it */
+}
+
+int
+compare_chunk (bytes, buffer)
+ long bytes;
+ char *buffer;
+{
+ int err;
+
+ err = read (diff_fd, diff_buf, bytes);
+ if (err != bytes)
+ {
+ if (err < 0)
+ {
+ msg_perror ("can't read %s", current_file_name);
+ }
+ else
+ {
+ fprintf (msg_file, "%s: could only read %d of %d bytes\n", current_file_name, err, bytes);
+ }
+ different++;
+ return -1;
+ }
+ if (bcmp (buffer, diff_buf, bytes))
+ {
+ fprintf (msg_file, "%s: data differs\n", current_file_name);
+ different++;
+ return -1;
+ }
+ return 0;
+}
+
+int
+compare_dir (bytes, buffer)
+ long bytes;
+ char *buffer;
+{
+ if (bcmp (buffer, diff_dir, bytes))
+ {
+ fprintf (msg_file, "%s: data differs\n", current_file_name);
+ different++;
+ return -1;
+ }
+ diff_dir += bytes;
+ return 0;
+}
+
+/*
+ * Sigh about something that differs.
+ */
+void
+sigh (what)
+ char *what;
+{
+
+ fprintf (msg_file, "%s: %s differs\n",
+ current_file_name, what);
+}
+
+void
+verify_volume ()
+{
+ int status;
+#ifdef MTIOCTOP
+ struct mtop t;
+ int er;
+#endif
+
+ if (!diff_buf)
+ diff_init ();
+#ifdef MTIOCTOP
+ t.mt_op = MTBSF;
+ t.mt_count = 1;
+ if ((er = rmtioctl (archive, MTIOCTOP, &t)) < 0)
+ {
+ if (errno != EIO || (er = rmtioctl (archive, MTIOCTOP, &t)) < 0)
+ {
+#endif
+ if (rmtlseek (archive, 0L, 0) != 0)
+ {
+ /* Lseek failed. Try a different method */
+ msg_perror ("Couldn't rewind archive file for verify");
+ return;
+ }
+#ifdef MTIOCTOP
+ }
+ }
+#endif
+ ar_reading = 1;
+ now_verifying = 1;
+ fl_read ();
+ for (;;)
+ {
+ status = read_header ();
+ if (status == 0)
+ {
+ unsigned n;
+
+ n = 0;
+ do
+ {
+ n++;
+ status = read_header ();
+ }
+ while (status == 0);
+ msg ("VERIFY FAILURE: %d invalid header%s detected!", n, n == 1 ? "" : "s");
+ }
+ if (status == 2 || status == EOF)
+ break;
+ diff_archive ();
+ }
+ ar_reading = 0;
+ now_verifying = 0;
+
+}
+
+int
+do_stat (statp)
+ struct stat *statp;
+{
+ int err;
+
+ err = f_follow_links ? stat (current_file_name, statp) : lstat (current_file_name, statp);
+ if (err < 0)
+ {
+ if (errno == ENOENT)
+ {
+ fprintf (msg_file, "%s: does not exist\n", current_file_name);
+ }
+ else
+ msg_perror ("can't stat file %s", current_file_name);
+ /* skip_file((long)hstat.st_size);
+ different++;*/
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/*
+ * JK
+ * Diff'ing a sparse file with its counterpart on the tar file is a
+ * bit of a different story than a normal file. First, we must know
+ * what areas of the file to skip through, i.e., we need to contruct
+ * a sparsearray, which will hold all the information we need. We must
+ * compare small amounts of data at a time as we find it.
+ */
+
+void
+diff_sparse_files (filesize)
+ int filesize;
+
+{
+ int sparse_ind = 0;
+ char *buf;
+ int buf_size = RECORDSIZE;
+ union record *datarec;
+ int err;
+ long numbytes;
+ /* int amt_read = 0;*/
+ int size = filesize;
+
+ buf = (char *) ck_malloc (buf_size * sizeof (char));
+
+ fill_in_sparse_array ();
+
+
+ while (size > 0)
+ {
+ datarec = findrec ();
+ if (!sparsearray[sparse_ind].numbytes)
+ break;
+
+ /*
+ * 'numbytes' is nicer to write than
+ * 'sparsearray[sparse_ind].numbytes' all the time ...
+ */
+ numbytes = sparsearray[sparse_ind].numbytes;
+
+ lseek (diff_fd, sparsearray[sparse_ind].offset, 0);
+ /*
+ * take care to not run out of room in our buffer
+ */
+ while (buf_size < numbytes)
+ {
+ buf = (char *) ck_realloc (buf, buf_size * 2 * sizeof (char));
+ buf_size *= 2;
+ }
+ while (numbytes > RECORDSIZE)
+ {
+ if ((err = read (diff_fd, buf, RECORDSIZE)) != RECORDSIZE)
+ {
+ if (err < 0)
+ msg_perror ("can't read %s", current_file_name);
+ else
+ fprintf (msg_file, "%s: could only read %d of %d bytes\n",
+ current_file_name, err, numbytes);
+ break;
+ }
+ if (bcmp (buf, datarec->charptr, RECORDSIZE))
+ {
+ different++;
+ break;
+ }
+ numbytes -= err;
+ size -= err;
+ userec (datarec);
+ datarec = findrec ();
+ }
+ if ((err = read (diff_fd, buf, numbytes)) != numbytes)
+ {
+ if (err < 0)
+ msg_perror ("can't read %s", current_file_name);
+ else
+ fprintf (msg_file, "%s: could only read %d of %d bytes\n",
+ current_file_name, err, numbytes);
+ break;
+ }
+
+ if (bcmp (buf, datarec->charptr, numbytes))
+ {
+ different++;
+ break;
+ }
+ /* amt_read += numbytes;
+ if (amt_read >= RECORDSIZE) {
+ amt_read = 0;
+ userec(datarec);
+ datarec = findrec();
+ }*/
+ userec (datarec);
+ sparse_ind++;
+ size -= numbytes;
+ }
+ /*
+ * if the number of bytes read isn't the
+ * number of bytes supposedly in the file,
+ * they're different
+ */
+ /* if (amt_read != filesize)
+ different++;*/
+ userec (datarec);
+ free (sparsearray);
+ if (different)
+ fprintf (msg_file, "%s: data differs\n", current_file_name);
+
+}
+
+/*
+ * JK
+ * This routine should be used more often than it is ... look into
+ * that. Anyhow, what it does is translate the sparse information
+ * on the header, and in any subsequent extended headers, into an
+ * array of structures with true numbers, as opposed to character
+ * strings. It simply makes our life much easier, doing so many
+ * comparisong and such.
+ */
+void
+fill_in_sparse_array ()
+{
+ int ind;
+
+ /*
+ * allocate space for our scratch space; it's initially
+ * 10 elements long, but can change in this routine if
+ * necessary
+ */
+ sp_array_size = 10;
+ sparsearray = (struct sp_array *) ck_malloc (sp_array_size * sizeof (struct sp_array));
+
+ /*
+ * there are at most five of these structures in the header
+ * itself; read these in first
+ */
+ for (ind = 0; ind < SPARSE_IN_HDR; ind++)
+ {
+ if (!head->header.sp[ind].numbytes)
+ break;
+ sparsearray[ind].offset =
+ from_oct (1 + 12, head->header.sp[ind].offset);
+ sparsearray[ind].numbytes =
+ from_oct (1 + 12, head->header.sp[ind].numbytes);
+ }
+ /*
+ * if the header's extended, we gotta read in exhdr's till
+ * we're done
+ */
+ if (head->header.isextended)
+ {
+ /* how far into the sparsearray we are 'so far' */
+ static int so_far_ind = SPARSE_IN_HDR;
+ union record *exhdr;
+
+ for (;;)
+ {
+ exhdr = findrec ();
+ for (ind = 0; ind < SPARSE_EXT_HDR; ind++)
+ {
+ if (ind + so_far_ind > sp_array_size - 1)
+ {
+ /*
+ * we just ran out of room in our
+ * scratch area - realloc it
+ */
+ sparsearray = (struct sp_array *)
+ ck_realloc (sparsearray,
+ sp_array_size * 2 * sizeof (struct sp_array));
+ sp_array_size *= 2;
+ }
+ /*
+ * convert the character strings into longs
+ */
+ sparsearray[ind + so_far_ind].offset =
+ from_oct (1 + 12, exhdr->ext_hdr.sp[ind].offset);
+ sparsearray[ind + so_far_ind].numbytes =
+ from_oct (1 + 12, exhdr->ext_hdr.sp[ind].numbytes);
+ }
+ /*
+ * if this is the last extended header for this
+ * file, we can stop
+ */
+ if (!exhdr->ext_hdr.isextended)
+ break;
+ else
+ {
+ so_far_ind += SPARSE_EXT_HDR;
+ userec (exhdr);
+ }
+ }
+ /* be sure to skip past the last one */
+ userec (exhdr);
+ }
+}
diff --git a/gnu/usr.bin/tar/extract.c b/gnu/usr.bin/tar/extract.c
new file mode 100644
index 0000000..57f8222
--- /dev/null
+++ b/gnu/usr.bin/tar/extract.c
@@ -0,0 +1,943 @@
+/* Extract files from a tar archive.
+ Copyright (C) 1988, 1992, 1993 Free Software Foundation
+
+This file is part of GNU Tar.
+
+GNU Tar is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Tar is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Tar; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * Extract files from a tar archive.
+ *
+ * Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+#include <sys/types.h>
+#include <time.h>
+time_t time ();
+
+#ifdef BSD42
+#include <sys/file.h>
+#else
+#ifndef V7
+#include <fcntl.h>
+#endif
+#endif
+
+#ifdef NO_OPEN3
+/* We need the #define's even though we don't use them. */
+#include "open3.h"
+#endif
+
+#ifdef EMUL_OPEN3
+/* Simulated 3-argument open for systems that don't have it */
+#include "open3.h"
+#endif
+
+#include "tar.h"
+#include "port.h"
+
+#if defined(_POSIX_VERSION)
+#include <utime.h>
+#else
+struct utimbuf
+{
+ long actime;
+ long modtime;
+};
+
+#endif
+
+extern FILE *msg_file;
+
+extern union record *head; /* Points to current tape header */
+extern struct stat hstat; /* Stat struct corresponding */
+extern int head_standard; /* Tape header is in ANSI format */
+
+extern char *save_name;
+extern long save_totsize;
+extern long save_sizeleft;
+
+int confirm ();
+void decode_header ();
+void extract_mangle ();
+void extract_sparse_file ();
+long from_oct ();
+void gnu_restore ();
+extern void print_header ();
+extern void skip_file ();
+extern void skip_extended_headers ();
+extern void pr_mkdir ();
+void saverec ();
+
+int make_dirs (); /* Makes required directories */
+
+static time_t now = 0; /* Current time */
+static we_are_root = 0; /* True if our effective uid == 0 */
+static int notumask = ~0; /* Masks out bits user doesn't want */
+
+/*
+ * "Scratch" space to store the information about a sparse file before
+ * writing the info into the header or extended header
+ */
+/*struct sp_array *sparsearray;*/
+
+/* number of elts storable in the sparsearray */
+/*int sp_array_size = 10;*/
+
+struct saved_dir_info
+{
+ char *path;
+ int mode;
+ int atime;
+ int mtime;
+ struct saved_dir_info *next;
+};
+
+struct saved_dir_info *saved_dir_info_head;
+
+/*
+ * Set up to extract files.
+ */
+void
+extr_init ()
+{
+ int ourmask;
+
+ now = time ((time_t *) 0);
+ if (geteuid () == 0)
+ we_are_root = 1;
+
+ /*
+ * We need to know our umask. But if f_use_protection is set,
+ * leave our kernel umask at 0, and our "notumask" at ~0.
+ */
+ ourmask = umask (0); /* Read it */
+ if (!f_use_protection)
+ {
+ (void) umask (ourmask); /* Set it back how it was */
+ notumask = ~ourmask; /* Make umask override permissions */
+ }
+}
+
+
+/*
+ * Extract a file from the archive.
+ */
+void
+extract_archive ()
+{
+ register char *data;
+ int fd, check, namelen, written, openflag;
+ long size;
+ struct utimbuf acc_upd_times;
+ register int skipcrud;
+ register int i;
+ /* int sparse_ind = 0;*/
+ union record *exhdr;
+ struct saved_dir_info *tmp;
+ /* int end_nulls; */
+
+ saverec (&head); /* Make sure it sticks around */
+ userec (head); /* And go past it in the archive */
+ decode_header (head, &hstat, &head_standard, 1); /* Snarf fields */
+
+ if (f_confirm && !confirm ("extract", current_file_name))
+ {
+ if (head->header.isextended)
+ skip_extended_headers ();
+ skip_file ((long) hstat.st_size);
+ saverec ((union record **) 0);
+ return;
+ }
+
+ /* Print the record from 'head' and 'hstat' */
+ if (f_verbose)
+ print_header ();
+
+ /*
+ * Check for fully specified pathnames and other atrocities.
+ *
+ * Note, we can't just make a pointer to the new file name,
+ * since saverec() might move the header and adjust "head".
+ * We have to start from "head" every time we want to touch
+ * the header record.
+ */
+ skipcrud = 0;
+ while (!f_absolute_paths
+ && '/' == current_file_name[skipcrud])
+ {
+ static int warned_once = 0;
+
+ skipcrud++; /* Force relative path */
+ if (!warned_once++)
+ {
+ msg ("Removing leading / from absolute path names in the archive.");
+ }
+ }
+
+ switch (head->header.linkflag)
+ {
+
+ default:
+ msg ("Unknown file type '%c' for %s, extracted as normal file",
+ head->header.linkflag, skipcrud + current_file_name);
+ /* FALL THRU */
+
+ /*
+ * JK - What we want to do if the file is sparse is loop through
+ * the array of sparse structures in the header and read in
+ * and translate the character strings representing 1) the offset
+ * at which to write and 2) how many bytes to write into numbers,
+ * which we store into the scratch array, "sparsearray". This
+ * array makes our life easier the same way it did in creating
+ * the tar file that had to deal with a sparse file.
+ *
+ * After we read in the first five (at most) sparse structures,
+ * we check to see if the file has an extended header, i.e.,
+ * if more sparse structures are needed to describe the contents
+ * of the new file. If so, we read in the extended headers
+ * and continue to store their contents into the sparsearray.
+ */
+ case LF_SPARSE:
+ sp_array_size = 10;
+ sparsearray = (struct sp_array *) ck_malloc (sp_array_size * sizeof (struct sp_array));
+ for (i = 0; i < SPARSE_IN_HDR; i++)
+ {
+ sparsearray[i].offset =
+ from_oct (1 + 12, head->header.sp[i].offset);
+ sparsearray[i].numbytes =
+ from_oct (1 + 12, head->header.sp[i].numbytes);
+ if (!sparsearray[i].numbytes)
+ break;
+ }
+
+ /* end_nulls = from_oct(1+12, head->header.ending_blanks);*/
+
+ if (head->header.isextended)
+ {
+ /* read in the list of extended headers
+ and translate them into the sparsearray
+ as before */
+
+ /* static */ int ind = SPARSE_IN_HDR;
+
+ for (;;)
+ {
+
+ exhdr = findrec ();
+ for (i = 0; i < SPARSE_EXT_HDR; i++)
+ {
+
+ if (i + ind > sp_array_size - 1)
+ {
+ /*
+ * realloc the scratch area
+ * since we've run out of room --
+ */
+ sparsearray = (struct sp_array *)
+ ck_realloc (sparsearray,
+ 2 * sp_array_size * (sizeof (struct sp_array)));
+ sp_array_size *= 2;
+ }
+ if (!exhdr->ext_hdr.sp[i].numbytes)
+ break;
+ sparsearray[i + ind].offset =
+ from_oct (1 + 12, exhdr->ext_hdr.sp[i].offset);
+ sparsearray[i + ind].numbytes =
+ from_oct (1 + 12, exhdr->ext_hdr.sp[i].numbytes);
+ }
+ if (!exhdr->ext_hdr.isextended)
+ break;
+ else
+ {
+ ind += SPARSE_EXT_HDR;
+ userec (exhdr);
+ }
+ }
+ userec (exhdr);
+ }
+
+ /* FALL THRU */
+ case LF_OLDNORMAL:
+ case LF_NORMAL:
+ case LF_CONTIG:
+ /*
+ * Appears to be a file.
+ * See if it's really a directory.
+ */
+ namelen = strlen (skipcrud + current_file_name) - 1;
+ if (current_file_name[skipcrud + namelen] == '/')
+ goto really_dir;
+
+ /* FIXME, deal with protection issues */
+ again_file:
+ openflag = (f_keep ?
+ O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_EXCL :
+ O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_TRUNC)
+ | ((head->header.linkflag == LF_SPARSE) ? 0 : O_APPEND);
+ /*
+ * JK - The last | is a kludge to solve the problem
+ * the O_APPEND flag causes with files we are
+ * trying to make sparse: when a file is opened
+ * with O_APPEND, it writes to the last place
+ * that something was written, thereby ignoring
+ * any lseeks that we have done. We add this
+ * extra condition to make it able to lseek when
+ * a file is sparse, i.e., we don't open the new
+ * file with this flag. (Grump -- this bug caused
+ * me to waste a good deal of time, I might add)
+ */
+
+ if (f_exstdout)
+ {
+ fd = 1;
+ goto extract_file;
+ }
+
+ if (f_unlink && !f_keep) {
+ if (unlink(skipcrud + current_file_name) == -1)
+ if (errno != ENOENT)
+ msg_perror ("Could not unlink %s",
+ skipcrud + current_file_name);
+ }
+
+#ifdef O_CTG
+ /*
+ * Contiguous files (on the Masscomp) have to specify
+ * the size in the open call that creates them.
+ */
+ if (head->header.linkflag == LF_CONTIG)
+ fd = open ((longname ? longname : head->header.name)
+ + skipcrud,
+ openflag | O_CTG,
+ hstat.st_mode, hstat.st_size);
+ else
+#endif
+ {
+#ifdef NO_OPEN3
+ /*
+ * On raw V7 we won't let them specify -k (f_keep), but
+ * we just bull ahead and create the files.
+ */
+ fd = creat ((longname
+ ? longname
+ : head->header.name) + skipcrud,
+ hstat.st_mode);
+#else
+ /*
+ * With 3-arg open(), we can do this up right.
+ */
+ fd = open (skipcrud + current_file_name,
+ openflag, hstat.st_mode);
+#endif
+ }
+
+ if (fd < 0)
+ {
+ if (make_dirs (skipcrud + current_file_name))
+ goto again_file;
+ msg_perror ("Could not create file %s",
+ skipcrud + current_file_name);
+ if (head->header.isextended)
+ skip_extended_headers ();
+ skip_file ((long) hstat.st_size);
+ goto quit;
+ }
+
+ extract_file:
+ if (head->header.linkflag == LF_SPARSE)
+ {
+ char *name;
+ int namelen;
+
+ /*
+ * Kludge alert. NAME is assigned to header.name
+ * because during the extraction, the space that
+ * contains the header will get scribbled on, and
+ * the name will get munged, so any error messages
+ * that happen to contain the filename will look
+ * REAL interesting unless we do this.
+ */
+ namelen = strlen (skipcrud + current_file_name) + 1;
+ name = (char *) ck_malloc ((sizeof (char)) * namelen);
+ bcopy (skipcrud + current_file_name, name, namelen);
+ size = hstat.st_size;
+ extract_sparse_file (fd, &size, hstat.st_size, name);
+ }
+ else
+ for (size = hstat.st_size;
+ size > 0;
+ size -= written)
+ {
+
+ /* long offset,
+ numbytes;*/
+
+ if (f_multivol)
+ {
+ save_name = current_file_name;
+ save_totsize = hstat.st_size;
+ save_sizeleft = size;
+ }
+
+ /*
+ * Locate data, determine max length
+ * writeable, write it, record that
+ * we have used the data, then check
+ * if the write worked.
+ */
+ data = findrec ()->charptr;
+ if (data == NULL)
+ { /* Check it... */
+ msg ("Unexpected EOF on archive file");
+ break;
+ }
+ /*
+ * JK - If the file is sparse, use the sparsearray
+ * that we created before to lseek into the new
+ * file the proper amount, and to see how many
+ * bytes we want to write at that position.
+ */
+ /* if (head->header.linkflag == LF_SPARSE) {
+ off_t pos;
+
+ pos = lseek(fd, (off_t) sparsearray[sparse_ind].offset, 0);
+ printf("%d at %d\n", (int) pos, sparse_ind);
+ written = sparsearray[sparse_ind++].numbytes;
+ } else*/
+ written = endofrecs ()->charptr - data;
+ if (written > size)
+ written = size;
+ errno = 0;
+ check = write (fd, data, written);
+ /*
+ * The following is in violation of strict
+ * typing, since the arg to userec
+ * should be a struct rec *. FIXME.
+ */
+ userec ((union record *) (data + written - 1));
+ if (check == written)
+ continue;
+ /*
+ * Error in writing to file.
+ * Print it, skip to next file in archive.
+ */
+ if (check < 0)
+ msg_perror ("couldn't write to file %s",
+ skipcrud + current_file_name);
+ else
+ msg ("could only write %d of %d bytes to file %s",
+ check, written, skipcrud + current_file_name);
+ skip_file ((long) (size - written));
+ break; /* Still do the close, mod time, chmod, etc */
+ }
+
+ if (f_multivol)
+ save_name = 0;
+
+ /* If writing to stdout, don't try to do anything
+ to the filename; it doesn't exist, or we don't
+ want to touch it anyway */
+ if (f_exstdout)
+ break;
+
+ /* if (head->header.isextended) {
+ register union record *exhdr;
+ register int i;
+
+ for (i = 0; i < 21; i++) {
+ long offset;
+
+ if (!exhdr->ext_hdr.sp[i].numbytes)
+ break;
+ offset = from_oct(1+12,
+ exhdr->ext_hdr.sp[i].offset);
+ written = from_oct(1+12,
+ exhdr->ext_hdr.sp[i].numbytes);
+ lseek(fd, offset, 0);
+ check = write(fd, data, written);
+ if (check == written) continue;
+
+ }
+
+
+ }*/
+ check = close (fd);
+ if (check < 0)
+ {
+ msg_perror ("Error while closing %s",
+ skipcrud + current_file_name);
+ }
+
+
+ set_filestat:
+
+ /*
+ * If we are root, set the owner and group of the extracted
+ * file. This does what is wanted both on real Unix and on
+ * System V. If we are running as a user, we extract as that
+ * user; if running as root, we extract as the original owner.
+ */
+ if (we_are_root || f_do_chown)
+ {
+ if (chown (skipcrud + current_file_name,
+ hstat.st_uid, hstat.st_gid) < 0)
+ {
+ msg_perror ("cannot chown file %s to uid %d gid %d",
+ skipcrud + current_file_name,
+ hstat.st_uid, hstat.st_gid);
+ }
+ }
+
+ /*
+ * Set the modified time of the file.
+ *
+ * Note that we set the accessed time to "now", which
+ * is really "the time we started extracting files".
+ * unless f_gnudump is used, in which case .st_atime is used
+ */
+ if (!f_modified)
+ {
+ /* fixme if f_gnudump should set ctime too, but how? */
+ if (f_gnudump)
+ acc_upd_times.actime = hstat.st_atime;
+ else
+ acc_upd_times.actime = now; /* Accessed now */
+ acc_upd_times.modtime = hstat.st_mtime; /* Mod'd */
+ if (utime (skipcrud + current_file_name,
+ &acc_upd_times) < 0)
+ {
+ msg_perror ("couldn't change access and modification times of %s", skipcrud + current_file_name);
+ }
+ }
+ /* We do the utime before the chmod because some versions of
+ utime are broken and trash the modes of the file. Since
+ we then change the mode anyway, we don't care. . . */
+
+ /*
+ * If '-k' is not set, open() or creat() could have saved
+ * the permission bits from a previously created file,
+ * ignoring the ones we specified.
+ * Even if -k is set, if the file has abnormal
+ * mode bits, we must chmod since writing or chown() has
+ * probably reset them.
+ *
+ * If -k is set, we know *we* created this file, so the mode
+ * bits were set by our open(). If the file is "normal", we
+ * skip the chmod. This works because we did umask(0) if -p
+ * is set, so umask will have left the specified mode alone.
+ */
+ if ((!f_keep)
+ || (hstat.st_mode & (S_ISUID | S_ISGID | S_ISVTX)))
+ {
+ if (chmod (skipcrud + current_file_name,
+ notumask & (int) hstat.st_mode) < 0)
+ {
+ msg_perror ("cannot change mode of file %s to %ld",
+ skipcrud + current_file_name,
+ notumask & (int) hstat.st_mode);
+ }
+ }
+
+ quit:
+ break;
+
+ case LF_LINK:
+ again_link:
+ {
+ struct stat st1, st2;
+
+ if (f_unlink && !f_keep) {
+ if (unlink(skipcrud + current_file_name) == -1)
+ if (errno != ENOENT)
+ msg_perror ("Could not unlink %s",
+ skipcrud + current_file_name);
+ }
+
+ check = link (current_link_name, skipcrud + current_file_name);
+
+ if (check == 0)
+ break;
+ if (make_dirs (skipcrud + current_file_name))
+ goto again_link;
+ if (f_gnudump && errno == EEXIST)
+ break;
+ if (stat (current_link_name, &st1) == 0
+ && stat (current_file_name + skipcrud, &st2) == 0
+ && st1.st_dev == st2.st_dev
+ && st1.st_ino == st2.st_ino)
+ break;
+ msg_perror ("Could not link %s to %s",
+ skipcrud + current_file_name,
+ current_link_name);
+ }
+ break;
+
+#ifdef S_ISLNK
+ case LF_SYMLINK:
+ again_symlink:
+ if (f_unlink && !f_keep) {
+ if (unlink(skipcrud + current_file_name) == -1)
+ if (errno != ENOENT)
+ msg_perror ("Could not unlink %s",
+ skipcrud + current_file_name);
+ }
+
+ check = symlink (current_link_name,
+ skipcrud + current_file_name);
+ /* FIXME, don't worry uid, gid, etc... */
+ if (check == 0)
+ break;
+ if (make_dirs (current_file_name + skipcrud))
+ goto again_symlink;
+ msg_perror ("Could not create symlink to %s",
+ current_link_name);
+ break;
+#endif
+
+#ifdef S_IFCHR
+ case LF_CHR:
+ hstat.st_mode |= S_IFCHR;
+ goto make_node;
+#endif
+
+#ifdef S_IFBLK
+ case LF_BLK:
+ hstat.st_mode |= S_IFBLK;
+#endif
+#if defined(S_IFCHR) || defined(S_IFBLK)
+ make_node:
+ if (f_unlink && !f_keep) {
+ if (unlink(skipcrud + current_file_name) == -1)
+ if (errno != ENOENT)
+ msg_perror ("Could not unlink %s",
+ skipcrud + current_file_name);
+ }
+
+ check = mknod (current_file_name + skipcrud,
+ (int) hstat.st_mode, (int) hstat.st_rdev);
+ if (check != 0)
+ {
+ if (make_dirs (skipcrud + current_file_name))
+ goto make_node;
+ msg_perror ("Could not make %s",
+ current_file_name + skipcrud);
+ break;
+ };
+ goto set_filestat;
+#endif
+
+#ifdef S_ISFIFO
+ /* If local system doesn't support FIFOs, use default case */
+ case LF_FIFO:
+ make_fifo:
+ if (f_unlink && !f_keep) {
+ if (unlink(skipcrud + current_file_name) == -1)
+ if (errno != ENOENT)
+ msg_perror ("Could not unlink %s",
+ skipcrud + current_file_name);
+ }
+
+ check = mkfifo (current_file_name + skipcrud,
+ (int) hstat.st_mode);
+ if (check != 0)
+ {
+ if (make_dirs (current_file_name + skipcrud))
+ goto make_fifo;
+ msg_perror ("Could not make %s",
+ skipcrud + current_file_name);
+ break;
+ };
+ goto set_filestat;
+#endif
+
+ case LF_DIR:
+ case LF_DUMPDIR:
+ namelen = strlen (current_file_name + skipcrud) - 1;
+ really_dir:
+ /* Check for trailing /, and zap as many as we find. */
+ while (namelen
+ && current_file_name[skipcrud + namelen] == '/')
+ current_file_name[skipcrud + namelen--] = '\0';
+ if (f_gnudump)
+ { /* Read the entry and delete files
+ that aren't listed in the archive */
+ gnu_restore (skipcrud);
+
+ }
+ else if (head->header.linkflag == LF_DUMPDIR)
+ skip_file ((long) (hstat.st_size));
+
+
+ again_dir:
+ check = mkdir (skipcrud + current_file_name,
+ (we_are_root ? 0 : 0300) | (int) hstat.st_mode);
+ if (check != 0)
+ {
+ struct stat st1;
+
+ if (make_dirs (skipcrud + current_file_name))
+ goto again_dir;
+ /* If we're trying to create '.', let it be. */
+ if (current_file_name[skipcrud + namelen] == '.' &&
+ (namelen == 0 ||
+ current_file_name[skipcrud + namelen - 1] == '/'))
+ goto check_perms;
+ if (errno == EEXIST
+ && stat (skipcrud + current_file_name, &st1) == 0
+ && (S_ISDIR (st1.st_mode)))
+ break;
+ msg_perror ("Could not create directory %s", skipcrud + current_file_name);
+ break;
+ }
+
+ check_perms:
+ if (!we_are_root && 0300 != (0300 & (int) hstat.st_mode))
+ {
+ hstat.st_mode |= 0300;
+ msg ("Added write and execute permission to directory %s",
+ skipcrud + current_file_name);
+ }
+
+ /*
+ * If we are root, set the owner and group of the extracted
+ * file. This does what is wanted both on real Unix and on
+ * System V. If we are running as a user, we extract as that
+ * user; if running as root, we extract as the original owner.
+ */
+ if (we_are_root || f_do_chown)
+ {
+ if (chown (skipcrud + current_file_name,
+ hstat.st_uid, hstat.st_gid) < 0)
+ {
+ msg_perror ("cannot chown file %s to uid %d gid %d",
+ skipcrud + current_file_name,
+ hstat.st_uid, hstat.st_gid);
+ }
+ }
+
+ if (!f_modified)
+ {
+ tmp = ((struct saved_dir_info *)
+ ck_malloc (sizeof (struct saved_dir_info)));
+ tmp->path = (char *) ck_malloc (strlen (skipcrud
+ + current_file_name) + 1);
+ strcpy (tmp->path, skipcrud + current_file_name);
+ tmp->mode = hstat.st_mode;
+ tmp->atime = hstat.st_atime;
+ tmp->mtime = hstat.st_mtime;
+ tmp->next = saved_dir_info_head;
+ saved_dir_info_head = tmp;
+ }
+ else
+ {
+ /* This functions exactly as the code for set_filestat above. */
+ if ((!f_keep)
+ || (hstat.st_mode & (S_ISUID | S_ISGID | S_ISVTX)))
+ {
+ if (chmod (skipcrud + current_file_name,
+ notumask & (int) hstat.st_mode) < 0)
+ {
+ msg_perror ("cannot change mode of file %s to %ld",
+ skipcrud + current_file_name,
+ notumask & (int) hstat.st_mode);
+ }
+ }
+ }
+ break;
+
+ case LF_VOLHDR:
+ if (f_verbose)
+ {
+ printf ("Reading %s\n", current_file_name);
+ }
+ break;
+
+ case LF_NAMES:
+ extract_mangle (head);
+ break;
+
+ case LF_MULTIVOL:
+ msg ("Can't extract '%s'--file is continued from another volume\n", current_file_name);
+ skip_file ((long) hstat.st_size);
+ break;
+
+ case LF_LONGNAME:
+ case LF_LONGLINK:
+ msg ("Visible long name error\n");
+ skip_file ((long) hstat.st_size);
+ break;
+ }
+
+ /* We don't need to save it any longer. */
+ saverec ((union record **) 0);/* Unsave it */
+}
+
+/*
+ * After a file/link/symlink/dir creation has failed, see if
+ * it's because some required directory was not present, and if
+ * so, create all required dirs.
+ */
+int
+make_dirs (pathname)
+ char *pathname;
+{
+ char *p; /* Points into path */
+ int madeone = 0; /* Did we do anything yet? */
+ int save_errno = errno; /* Remember caller's errno */
+ int check;
+
+ if (errno != ENOENT)
+ return 0; /* Not our problem */
+
+ for (p = index (pathname, '/'); p != NULL; p = index (p + 1, '/'))
+ {
+ /* Avoid mkdir of empty string, if leading or double '/' */
+ if (p == pathname || p[-1] == '/')
+ continue;
+ /* Avoid mkdir where last part of path is '.' */
+ if (p[-1] == '.' && (p == pathname + 1 || p[-2] == '/'))
+ continue;
+ *p = 0; /* Truncate the path there */
+ check = mkdir (pathname, 0777); /* Try to create it as a dir */
+ if (check == 0)
+ {
+ /* Fix ownership */
+ if (we_are_root)
+ {
+ if (chown (pathname, hstat.st_uid,
+ hstat.st_gid) < 0)
+ {
+ msg_perror ("cannot change owner of %s to uid %d gid %d", pathname, hstat.st_uid, hstat.st_gid);
+ }
+ }
+ pr_mkdir (pathname, p - pathname, notumask & 0777);
+ madeone++; /* Remember if we made one */
+ *p = '/';
+ continue;
+ }
+ *p = '/';
+ if (errno == EEXIST) /* Directory already exists */
+ continue;
+ /*
+ * Some other error in the mkdir. We return to the caller.
+ */
+ break;
+ }
+
+ errno = save_errno; /* Restore caller's errno */
+ return madeone; /* Tell them to retry if we made one */
+}
+
+void
+extract_sparse_file (fd, sizeleft, totalsize, name)
+ int fd;
+ long *sizeleft, totalsize;
+ char *name;
+{
+ /* register char *data;*/
+ union record *datarec;
+ int sparse_ind = 0;
+ int written, count;
+
+ /* assuming sizeleft is initially totalsize */
+
+
+ while (*sizeleft > 0)
+ {
+ datarec = findrec ();
+ if (datarec == NULL)
+ {
+ msg ("Unexpected EOF on archive file");
+ return;
+ }
+ lseek (fd, sparsearray[sparse_ind].offset, 0);
+ written = sparsearray[sparse_ind++].numbytes;
+ while (written > RECORDSIZE)
+ {
+ count = write (fd, datarec->charptr, RECORDSIZE);
+ if (count < 0)
+ msg_perror ("couldn't write to file %s", name);
+ written -= count;
+ *sizeleft -= count;
+ userec (datarec);
+ datarec = findrec ();
+ }
+
+ count = write (fd, datarec->charptr, written);
+
+ if (count < 0)
+ {
+ msg_perror ("couldn't write to file %s", name);
+ }
+ else if (count != written)
+ {
+ msg ("could only write %d of %d bytes to file %s", count,
+ totalsize, name);
+ skip_file ((long) (*sizeleft));
+ }
+
+ written -= count;
+ *sizeleft -= count;
+ userec (datarec);
+ }
+ free (sparsearray);
+ /* if (end_nulls) {
+ register int i;
+
+ printf("%d\n", (int) end_nulls);
+ for (i = 0; i < end_nulls; i++)
+ write(fd, "\000", 1);
+ }*/
+ userec (datarec);
+}
+
+/* Set back the utime and mode for all the extracted directories. */
+void
+restore_saved_dir_info ()
+{
+ struct utimbuf acc_upd_times;
+
+ while (saved_dir_info_head != NULL)
+ {
+ /* fixme if f_gnudump should set ctime too, but how? */
+ if (f_gnudump)
+ acc_upd_times.actime = saved_dir_info_head->atime;
+ else
+ acc_upd_times.actime = now; /* Accessed now */
+ acc_upd_times.modtime = saved_dir_info_head->mtime; /* Mod'd */
+ if (utime (saved_dir_info_head->path, &acc_upd_times) < 0)
+ {
+ msg_perror ("couldn't change access and modification times of %s",
+ saved_dir_info_head->path);
+ }
+ if ((!f_keep) || (saved_dir_info_head->mode & (S_ISUID | S_ISGID | S_ISVTX)))
+ {
+ if (chmod (saved_dir_info_head->path,
+ notumask & saved_dir_info_head->mode) < 0)
+ {
+ msg_perror ("cannot change mode of file %s to %ld",
+ saved_dir_info_head->path,
+ notumask & saved_dir_info_head->mode);
+ }
+ }
+ saved_dir_info_head = saved_dir_info_head->next;
+ }
+}
diff --git a/gnu/usr.bin/tar/fnmatch.c b/gnu/usr.bin/tar/fnmatch.c
new file mode 100644
index 0000000..ed8c9ee
--- /dev/null
+++ b/gnu/usr.bin/tar/fnmatch.c
@@ -0,0 +1,173 @@
+/* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+#include <fnmatch.h>
+
+#if !defined(__GNU_LIBRARY__) && !defined(STDC_HEADERS)
+extern int errno;
+#endif
+
+/* Match STRING against the filename pattern PATTERN, returning zero if
+ it matches, nonzero if not. */
+int
+fnmatch (pattern, string, flags)
+ const char *pattern;
+ const char *string;
+ int flags;
+{
+ register const char *p = pattern, *n = string;
+ register char c;
+
+ if ((flags & ~__FNM_FLAGS) != 0)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ while ((c = *p++) != '\0')
+ {
+ switch (c)
+ {
+ case '?':
+ if (*n == '\0')
+ return FNM_NOMATCH;
+ else if ((flags & FNM_PATHNAME) && *n == '/')
+ return FNM_NOMATCH;
+ else if ((flags & FNM_PERIOD) && *n == '.' &&
+ (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
+ return FNM_NOMATCH;
+ break;
+
+ case '\\':
+ if (!(flags & FNM_NOESCAPE))
+ c = *p++;
+ if (*n != c)
+ return FNM_NOMATCH;
+ break;
+
+ case '*':
+ if ((flags & FNM_PERIOD) && *n == '.' &&
+ (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
+ return FNM_NOMATCH;
+
+ for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
+ if (((flags & FNM_PATHNAME) && *n == '/') ||
+ (c == '?' && *n == '\0'))
+ return FNM_NOMATCH;
+
+ if (c == '\0')
+ return 0;
+
+ {
+ char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
+ for (--p; *n != '\0'; ++n)
+ if ((c == '[' || *n == c1) &&
+ fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
+ return 0;
+ return FNM_NOMATCH;
+ }
+
+ case '[':
+ {
+ /* Nonzero if the sense of the character class is inverted. */
+ register int not;
+
+ if (*n == '\0')
+ return FNM_NOMATCH;
+
+ if ((flags & FNM_PERIOD) && *n == '.' &&
+ (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
+ return FNM_NOMATCH;
+
+ not = (*p == '!' || *p == '^');
+ if (not)
+ ++p;
+
+ c = *p++;
+ for (;;)
+ {
+ register char cstart = c, cend = c;
+
+ if (!(flags & FNM_NOESCAPE) && c == '\\')
+ cstart = cend = *p++;
+
+ if (c == '\0')
+ /* [ (unterminated) loses. */
+ return FNM_NOMATCH;
+
+ c = *p++;
+
+ if ((flags & FNM_PATHNAME) && c == '/')
+ /* [/] can never match. */
+ return FNM_NOMATCH;
+
+ if (c == '-' && *p != ']')
+ {
+ cend = *p++;
+ if (!(flags & FNM_NOESCAPE) && cend == '\\')
+ cend = *p++;
+ if (cend == '\0')
+ return FNM_NOMATCH;
+ c = *p++;
+ }
+
+ if (*n >= cstart && *n <= cend)
+ goto matched;
+
+ if (c == ']')
+ break;
+ }
+ if (!not)
+ return FNM_NOMATCH;
+ break;
+
+ matched:;
+ /* Skip the rest of the [...] that already matched. */
+ while (c != ']')
+ {
+ if (c == '\0')
+ /* [... (unterminated) loses. */
+ return FNM_NOMATCH;
+
+ c = *p++;
+ if (!(flags & FNM_NOESCAPE) && c == '\\')
+ /* 1003.2d11 is unclear if this is right. %%% */
+ ++p;
+ }
+ if (not)
+ return FNM_NOMATCH;
+ }
+ break;
+
+ default:
+ if (c != *n)
+ return FNM_NOMATCH;
+ }
+
+ ++n;
+ }
+
+ if (*n == '\0')
+ return 0;
+
+ if ((flags & FNM_LEADING_DIR) && *n == '/')
+ /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */
+ return 0;
+
+ return FNM_NOMATCH;
+}
diff --git a/gnu/usr.bin/tar/fnmatch.h b/gnu/usr.bin/tar/fnmatch.h
new file mode 100644
index 0000000..d4150a9
--- /dev/null
+++ b/gnu/usr.bin/tar/fnmatch.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#ifndef _FNMATCH_H
+
+#define _FNMATCH_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined (__cplusplus) || (defined (__STDC__) && __STDC__)
+#undef __P
+#define __P(args) args
+#else /* Not C++ or ANSI C. */
+#undef __P
+#define __P(args) ()
+#undef const
+#define const
+#endif /* C++ or ANSI C. */
+
+/* Bits set in the FLAGS argument to `fnmatch'. */
+#ifdef FNM_PATHNAME /* Because it is already defined in <unistd.h> */
+#undef FNM_PATHNAME
+#endif
+#define FNM_PATHNAME (1 << 0)/* No wildcard can ever match `/'. */
+#define FNM_NOESCAPE (1 << 1)/* Backslashes don't quote special chars. */
+#define FNM_PERIOD (1 << 2)/* Leading `.' is matched only explicitly. */
+#define __FNM_FLAGS (FNM_PATHNAME|FNM_NOESCAPE|FNM_PERIOD|FNM_LEADING_DIR)
+
+#if !defined (_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined (_BSD_SOURCE)
+#define FNM_LEADING_DIR (1 << 3)/* Ignore `/...' after a match. */
+#define FNM_FILE_NAME FNM_PATHNAME
+#endif
+
+/* Value returned by `fnmatch' if STRING does not match PATTERN. */
+#define FNM_NOMATCH 1
+
+/* Match STRING against the filename pattern PATTERN,
+ returning zero if it matches, FNM_NOMATCH if not. */
+extern int fnmatch __P ((const char *__pattern, const char *__string,
+ int __flags));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* fnmatch.h */
diff --git a/gnu/usr.bin/tar/getdate.y b/gnu/usr.bin/tar/getdate.y
new file mode 100644
index 0000000..7b0ac79
--- /dev/null
+++ b/gnu/usr.bin/tar/getdate.y
@@ -0,0 +1,969 @@
+%{
+/* $Revision: 2.1 $
+**
+** Originally written by Steven M. Bellovin <smb@research.att.com> while
+** at the University of North Carolina at Chapel Hill. Later tweaked by
+** a couple of people on Usenet. Completely overhauled by Rich $alz
+** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
+** send any email to Rich.
+**
+** This grammar has eight shift/reduce conflicts.
+**
+** This code is in the public domain and has no copyright.
+*/
+/* SUPPRESS 287 on yaccpar_sccsid *//* Unusd static variable */
+/* SUPPRESS 288 on yyerrlab *//* Label unused */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#else
+#ifdef _AIX /* for Bison */
+ #pragma alloca
+#else
+char *alloca ();
+#endif
+#endif
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+
+/* The code at the top of get_date which figures out the offset of the
+ current time zone checks various CPP symbols to see if special
+ tricks are need, but defaults to using the gettimeofday system call.
+ Include <sys/time.h> if that will be used. */
+
+#if !defined (USG) && !defined (sgi) && !defined (__386BSD__)
+#include <sys/time.h>
+#endif
+
+#if defined(vms)
+
+#include <types.h>
+#include <time.h>
+
+#else
+
+#include <sys/types.h>
+
+#if defined(USG) || !defined(HAVE_FTIME)
+/*
+** If you need to do a tzset() call to set the
+** timezone, and don't have ftime().
+*/
+struct timeb {
+ time_t time; /* Seconds since the epoch */
+ unsigned short millitm; /* Field not used */
+ short timezone;
+ short dstflag; /* Field not used */
+};
+
+#else
+
+#include <sys/timeb.h>
+
+#endif /* defined(USG) && !defined(HAVE_FTIME) */
+
+#if defined(BSD4_2) || defined(BSD4_1C) || (defined (hp9000) && !defined (hpux))
+#include <sys/time.h>
+#else
+#if defined(_AIX)
+#include <sys/time.h>
+#endif
+#include <time.h>
+#endif /* defined(BSD4_2) */
+
+#endif /* defined(vms) */
+
+#if defined (STDC_HEADERS) || defined (USG)
+#include <string.h>
+#endif
+
+#if sgi
+#undef timezone
+#endif
+
+extern struct tm *localtime();
+
+#define yyparse getdate_yyparse
+#define yylex getdate_yylex
+#define yyerror getdate_yyerror
+
+#if !defined(lint) && !defined(SABER)
+static char RCS[] =
+ "$Header: str2date.y,v 2.1 90/09/06 08:15:06 cronan Exp $";
+#endif /* !defined(lint) && !defined(SABER) */
+
+
+#define EPOCH 1970
+#define HOUR(x) ((time_t)(x) * 60)
+#define SECSPERDAY (24L * 60L * 60L)
+
+
+/*
+** An entry in the lexical lookup table.
+*/
+typedef struct _TABLE {
+ char *name;
+ int type;
+ time_t value;
+} TABLE;
+
+
+/*
+** Daylight-savings mode: on, off, or not yet known.
+*/
+typedef enum _DSTMODE {
+ DSTon, DSToff, DSTmaybe
+} DSTMODE;
+
+/*
+** Meridian: am, pm, or 24-hour style.
+*/
+typedef enum _MERIDIAN {
+ MERam, MERpm, MER24
+} MERIDIAN;
+
+
+/*
+** Global variables. We could get rid of most of these by using a good
+** union as the yacc stack. (This routine was originally written before
+** yacc had the %union construct.) Maybe someday; right now we only use
+** the %union very rarely.
+*/
+static char *yyInput;
+static DSTMODE yyDSTmode;
+static time_t yyDayOrdinal;
+static time_t yyDayNumber;
+static int yyHaveDate;
+static int yyHaveDay;
+static int yyHaveRel;
+static int yyHaveTime;
+static int yyHaveZone;
+static time_t yyTimezone;
+static time_t yyDay;
+static time_t yyHour;
+static time_t yyMinutes;
+static time_t yyMonth;
+static time_t yySeconds;
+static time_t yyYear;
+static MERIDIAN yyMeridian;
+static time_t yyRelMonth;
+static time_t yyRelSeconds;
+
+%}
+
+%union {
+ time_t Number;
+ enum _MERIDIAN Meridian;
+}
+
+%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
+%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
+
+%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
+%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
+%type <Meridian> tMERIDIAN o_merid
+
+%%
+
+spec : /* NULL */
+ | spec item
+ ;
+
+item : time {
+ yyHaveTime++;
+ }
+ | zone {
+ yyHaveZone++;
+ }
+ | date {
+ yyHaveDate++;
+ }
+ | day {
+ yyHaveDay++;
+ }
+ | rel {
+ yyHaveRel++;
+ }
+ | number
+ ;
+
+time : tUNUMBER tMERIDIAN {
+ yyHour = $1;
+ yyMinutes = 0;
+ yySeconds = 0;
+ yyMeridian = $2;
+ }
+ | tUNUMBER ':' tUNUMBER o_merid {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = 0;
+ yyMeridian = $4;
+ }
+ | tUNUMBER ':' tUNUMBER tSNUMBER {
+ yyHour = $1;
+ yyMinutes = $3;
+ yyMeridian = MER24;
+ yyDSTmode = DSToff;
+ yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
+ }
+ | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = $5;
+ yyMeridian = $6;
+ }
+ | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = $5;
+ yyMeridian = MER24;
+ yyDSTmode = DSToff;
+ yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
+ }
+ ;
+
+zone : tZONE {
+ yyTimezone = $1;
+ yyDSTmode = DSToff;
+ }
+ | tDAYZONE {
+ yyTimezone = $1;
+ yyDSTmode = DSTon;
+ }
+ |
+ tZONE tDST {
+ yyTimezone = $1;
+ yyDSTmode = DSTon;
+ }
+ ;
+
+day : tDAY {
+ yyDayOrdinal = 1;
+ yyDayNumber = $1;
+ }
+ | tDAY ',' {
+ yyDayOrdinal = 1;
+ yyDayNumber = $1;
+ }
+ | tUNUMBER tDAY {
+ yyDayOrdinal = $1;
+ yyDayNumber = $2;
+ }
+ ;
+
+date : tUNUMBER '/' tUNUMBER {
+ yyMonth = $1;
+ yyDay = $3;
+ }
+ | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
+ yyMonth = $1;
+ yyDay = $3;
+ yyYear = $5;
+ }
+ | tUNUMBER tSNUMBER tSNUMBER {
+ /* ISO 8601 format. yyyy-mm-dd. */
+ yyYear = $1;
+ yyMonth = -$2;
+ yyDay = -$3;
+ }
+ | tMONTH tUNUMBER {
+ yyMonth = $1;
+ yyDay = $2;
+ }
+ | tMONTH tUNUMBER ',' tUNUMBER {
+ yyMonth = $1;
+ yyDay = $2;
+ yyYear = $4;
+ }
+ | tUNUMBER tMONTH {
+ yyMonth = $2;
+ yyDay = $1;
+ }
+ | tUNUMBER tMONTH tUNUMBER {
+ yyMonth = $2;
+ yyDay = $1;
+ yyYear = $3;
+ }
+ ;
+
+rel : relunit tAGO {
+ yyRelSeconds = -yyRelSeconds;
+ yyRelMonth = -yyRelMonth;
+ }
+ | relunit
+ ;
+
+relunit : tUNUMBER tMINUTE_UNIT {
+ yyRelSeconds += $1 * $2 * 60L;
+ }
+ | tSNUMBER tMINUTE_UNIT {
+ yyRelSeconds += $1 * $2 * 60L;
+ }
+ | tMINUTE_UNIT {
+ yyRelSeconds += $1 * 60L;
+ }
+ | tSNUMBER tSEC_UNIT {
+ yyRelSeconds += $1;
+ }
+ | tUNUMBER tSEC_UNIT {
+ yyRelSeconds += $1;
+ }
+ | tSEC_UNIT {
+ yyRelSeconds++;
+ }
+ | tSNUMBER tMONTH_UNIT {
+ yyRelMonth += $1 * $2;
+ }
+ | tUNUMBER tMONTH_UNIT {
+ yyRelMonth += $1 * $2;
+ }
+ | tMONTH_UNIT {
+ yyRelMonth += $1;
+ }
+ ;
+
+number : tUNUMBER {
+ if (yyHaveTime && yyHaveDate && !yyHaveRel)
+ yyYear = $1;
+ else {
+ if($1>10000) {
+ time_t date_part;
+
+ date_part= $1/10000;
+ yyHaveDate++;
+ yyDay= (date_part)%100;
+ yyMonth= (date_part/100)%100;
+ yyYear = date_part/10000;
+ }
+ yyHaveTime++;
+ if ($1 < 100) {
+ yyHour = $1;
+ yyMinutes = 0;
+ }
+ else {
+ yyHour = $1 / 100;
+ yyMinutes = $1 % 100;
+ }
+ yySeconds = 0;
+ yyMeridian = MER24;
+ }
+ }
+ ;
+
+o_merid : /* NULL */ {
+ $$ = MER24;
+ }
+ | tMERIDIAN {
+ $$ = $1;
+ }
+ ;
+
+%%
+
+/* Month and day table. */
+static TABLE const MonthDayTable[] = {
+ { "january", tMONTH, 1 },
+ { "february", tMONTH, 2 },
+ { "march", tMONTH, 3 },
+ { "april", tMONTH, 4 },
+ { "may", tMONTH, 5 },
+ { "june", tMONTH, 6 },
+ { "july", tMONTH, 7 },
+ { "august", tMONTH, 8 },
+ { "september", tMONTH, 9 },
+ { "sept", tMONTH, 9 },
+ { "october", tMONTH, 10 },
+ { "november", tMONTH, 11 },
+ { "december", tMONTH, 12 },
+ { "sunday", tDAY, 0 },
+ { "monday", tDAY, 1 },
+ { "tuesday", tDAY, 2 },
+ { "tues", tDAY, 2 },
+ { "wednesday", tDAY, 3 },
+ { "wednes", tDAY, 3 },
+ { "thursday", tDAY, 4 },
+ { "thur", tDAY, 4 },
+ { "thurs", tDAY, 4 },
+ { "friday", tDAY, 5 },
+ { "saturday", tDAY, 6 },
+ { NULL }
+};
+
+/* Time units table. */
+static TABLE const UnitsTable[] = {
+ { "year", tMONTH_UNIT, 12 },
+ { "month", tMONTH_UNIT, 1 },
+ { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
+ { "week", tMINUTE_UNIT, 7 * 24 * 60 },
+ { "day", tMINUTE_UNIT, 1 * 24 * 60 },
+ { "hour", tMINUTE_UNIT, 60 },
+ { "minute", tMINUTE_UNIT, 1 },
+ { "min", tMINUTE_UNIT, 1 },
+ { "second", tSEC_UNIT, 1 },
+ { "sec", tSEC_UNIT, 1 },
+ { NULL }
+};
+
+/* Assorted relative-time words. */
+static TABLE const OtherTable[] = {
+ { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
+ { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
+ { "today", tMINUTE_UNIT, 0 },
+ { "now", tMINUTE_UNIT, 0 },
+ { "last", tUNUMBER, -1 },
+ { "this", tMINUTE_UNIT, 0 },
+ { "next", tUNUMBER, 2 },
+ { "first", tUNUMBER, 1 },
+/* { "second", tUNUMBER, 2 }, */
+ { "third", tUNUMBER, 3 },
+ { "fourth", tUNUMBER, 4 },
+ { "fifth", tUNUMBER, 5 },
+ { "sixth", tUNUMBER, 6 },
+ { "seventh", tUNUMBER, 7 },
+ { "eighth", tUNUMBER, 8 },
+ { "ninth", tUNUMBER, 9 },
+ { "tenth", tUNUMBER, 10 },
+ { "eleventh", tUNUMBER, 11 },
+ { "twelfth", tUNUMBER, 12 },
+ { "ago", tAGO, 1 },
+ { NULL }
+};
+
+/* The timezone table. */
+/* Some of these are commented out because a time_t can't store a float. */
+static TABLE const TimezoneTable[] = {
+ { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
+ { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
+ { "utc", tZONE, HOUR( 0) },
+ { "wet", tZONE, HOUR( 0) }, /* Western European */
+ { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
+ { "wat", tZONE, HOUR( 1) }, /* West Africa */
+ { "at", tZONE, HOUR( 2) }, /* Azores */
+#if 0
+ /* For completeness. BST is also British Summer, and GST is
+ * also Guam Standard. */
+ { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
+ { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
+#endif
+#if 0
+ { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
+ { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
+ { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
+#endif
+ { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
+ { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
+ { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
+ { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
+ { "cst", tZONE, HOUR( 6) }, /* Central Standard */
+ { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
+ { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
+ { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
+ { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
+ { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
+ { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
+ { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
+ { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
+ { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
+ { "cat", tZONE, HOUR(10) }, /* Central Alaska */
+ { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
+ { "nt", tZONE, HOUR(11) }, /* Nome */
+ { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
+ { "cet", tZONE, -HOUR(1) }, /* Central European */
+ { "met", tZONE, -HOUR(1) }, /* Middle European */
+ { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
+ { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
+ { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
+ { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
+ { "fwt", tZONE, -HOUR(1) }, /* French Winter */
+ { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
+ { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
+ { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
+#if 0
+ { "it", tZONE, -HOUR(3.5) },/* Iran */
+#endif
+ { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
+ { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
+#if 0
+ { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
+#endif
+ { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
+#if 0
+ /* For completeness. NST is also Newfoundland Stanard, and SST is
+ * also Swedish Summer. */
+ { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
+ { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
+#endif /* 0 */
+ { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
+ { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
+#if 0
+ { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
+#endif
+ { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
+ { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
+#if 0
+ { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
+ { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
+#endif
+ { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
+ { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
+ { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
+ { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
+ { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
+ { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
+ { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
+ { NULL }
+};
+
+/* Military timezone table. */
+static TABLE const MilitaryTable[] = {
+ { "a", tZONE, HOUR( 1) },
+ { "b", tZONE, HOUR( 2) },
+ { "c", tZONE, HOUR( 3) },
+ { "d", tZONE, HOUR( 4) },
+ { "e", tZONE, HOUR( 5) },
+ { "f", tZONE, HOUR( 6) },
+ { "g", tZONE, HOUR( 7) },
+ { "h", tZONE, HOUR( 8) },
+ { "i", tZONE, HOUR( 9) },
+ { "k", tZONE, HOUR( 10) },
+ { "l", tZONE, HOUR( 11) },
+ { "m", tZONE, HOUR( 12) },
+ { "n", tZONE, HOUR(- 1) },
+ { "o", tZONE, HOUR(- 2) },
+ { "p", tZONE, HOUR(- 3) },
+ { "q", tZONE, HOUR(- 4) },
+ { "r", tZONE, HOUR(- 5) },
+ { "s", tZONE, HOUR(- 6) },
+ { "t", tZONE, HOUR(- 7) },
+ { "u", tZONE, HOUR(- 8) },
+ { "v", tZONE, HOUR(- 9) },
+ { "w", tZONE, HOUR(-10) },
+ { "x", tZONE, HOUR(-11) },
+ { "y", tZONE, HOUR(-12) },
+ { "z", tZONE, HOUR( 0) },
+ { NULL }
+};
+
+
+
+
+/* ARGSUSED */
+static int
+yyerror(s)
+ char *s;
+{
+ return 0;
+}
+
+
+static time_t
+ToSeconds(Hours, Minutes, Seconds, Meridian)
+ time_t Hours;
+ time_t Minutes;
+ time_t Seconds;
+ MERIDIAN Meridian;
+{
+ if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
+ return -1;
+ switch (Meridian) {
+ case MER24:
+ if (Hours < 0 || Hours > 23)
+ return -1;
+ return (Hours * 60L + Minutes) * 60L + Seconds;
+ case MERam:
+ if (Hours < 1 || Hours > 12)
+ return -1;
+ return (Hours * 60L + Minutes) * 60L + Seconds;
+ case MERpm:
+ if (Hours < 1 || Hours > 12)
+ return -1;
+ return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
+ }
+ /* NOTREACHED */
+}
+
+
+static time_t
+Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
+ time_t Month;
+ time_t Day;
+ time_t Year;
+ time_t Hours;
+ time_t Minutes;
+ time_t Seconds;
+ MERIDIAN Meridian;
+ DSTMODE DSTmode;
+{
+ static int DaysInMonth[12] = {
+ 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ time_t tod;
+ time_t Julian;
+ int i;
+
+ if (Year < 0)
+ Year = -Year;
+ if (Year < 100)
+ Year += 1900;
+ DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
+ ? 29 : 28;
+ if (Year < EPOCH || Year > 1999
+ || Month < 1 || Month > 12
+ /* Lint fluff: "conversion from long may lose accuracy" */
+ || Day < 1 || Day > DaysInMonth[(int)--Month])
+ return -1;
+
+ for (Julian = Day - 1, i = 0; i < Month; i++)
+ Julian += DaysInMonth[i];
+ for (i = EPOCH; i < Year; i++)
+ Julian += 365 + (i % 4 == 0);
+ Julian *= SECSPERDAY;
+ Julian += yyTimezone * 60L;
+ if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
+ return -1;
+ Julian += tod;
+ if (DSTmode == DSTon
+ || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
+ Julian -= 60 * 60;
+ return Julian;
+}
+
+
+static time_t
+DSTcorrect(Start, Future)
+ time_t Start;
+ time_t Future;
+{
+ time_t StartDay;
+ time_t FutureDay;
+
+ StartDay = (localtime(&Start)->tm_hour + 1) % 24;
+ FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
+ return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
+}
+
+
+static time_t
+RelativeDate(Start, DayOrdinal, DayNumber)
+ time_t Start;
+ time_t DayOrdinal;
+ time_t DayNumber;
+{
+ struct tm *tm;
+ time_t now;
+
+ now = Start;
+ tm = localtime(&now);
+ now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
+ now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
+ return DSTcorrect(Start, now);
+}
+
+
+static time_t
+RelativeMonth(Start, RelMonth)
+ time_t Start;
+ time_t RelMonth;
+{
+ struct tm *tm;
+ time_t Month;
+ time_t Year;
+
+ if (RelMonth == 0)
+ return 0;
+ tm = localtime(&Start);
+ Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
+ Year = Month / 12;
+ Month = Month % 12 + 1;
+ return DSTcorrect(Start,
+ Convert(Month, (time_t)tm->tm_mday, Year,
+ (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
+ MER24, DSTmaybe));
+}
+
+
+static int
+LookupWord(buff)
+ char *buff;
+{
+ register char *p;
+ register char *q;
+ register const TABLE *tp;
+ int i;
+ int abbrev;
+
+ /* Make it lowercase. */
+ for (p = buff; *p; p++)
+ if (isupper(*p))
+ *p = tolower(*p);
+
+ if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
+ yylval.Meridian = MERam;
+ return tMERIDIAN;
+ }
+ if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
+ yylval.Meridian = MERpm;
+ return tMERIDIAN;
+ }
+
+ /* See if we have an abbreviation for a month. */
+ if (strlen(buff) == 3)
+ abbrev = 1;
+ else if (strlen(buff) == 4 && buff[3] == '.') {
+ abbrev = 1;
+ buff[3] = '\0';
+ }
+ else
+ abbrev = 0;
+
+ for (tp = MonthDayTable; tp->name; tp++) {
+ if (abbrev) {
+ if (strncmp(buff, tp->name, 3) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+ else if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ for (tp = TimezoneTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ if (strcmp(buff, "dst") == 0)
+ return tDST;
+
+ for (tp = UnitsTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ /* Strip off any plural and try the units table again. */
+ i = strlen(buff) - 1;
+ if (buff[i] == 's') {
+ buff[i] = '\0';
+ for (tp = UnitsTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ buff[i] = 's'; /* Put back for "this" in OtherTable. */
+ }
+
+ for (tp = OtherTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ /* Military timezones. */
+ if (buff[1] == '\0' && isalpha(*buff)) {
+ for (tp = MilitaryTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ /* Drop out any periods and try the timezone table again. */
+ for (i = 0, p = q = buff; *q; q++)
+ if (*q != '.')
+ *p++ = *q;
+ else
+ i++;
+ *p = '\0';
+ if (i)
+ for (tp = TimezoneTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ return tID;
+}
+
+
+static int
+yylex()
+{
+ register char c;
+ register char *p;
+ char buff[20];
+ int Count;
+ int sign;
+
+ for ( ; ; ) {
+ while (isspace(*yyInput))
+ yyInput++;
+
+ if (isdigit(c = *yyInput) || c == '-' || c == '+') {
+ if (c == '-' || c == '+') {
+ sign = c == '-' ? -1 : 1;
+ if (!isdigit(*++yyInput))
+ /* skip the '-' sign */
+ continue;
+ }
+ else
+ sign = 0;
+ for (yylval.Number = 0; isdigit(c = *yyInput++); )
+ yylval.Number = 10 * yylval.Number + c - '0';
+ yyInput--;
+ if (sign < 0)
+ yylval.Number = -yylval.Number;
+ return sign ? tSNUMBER : tUNUMBER;
+ }
+ if (isalpha(c)) {
+ for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
+ if (p < &buff[sizeof buff - 1])
+ *p++ = c;
+ *p = '\0';
+ yyInput--;
+ return LookupWord(buff);
+ }
+ if (c != '(')
+ return *yyInput++;
+ Count = 0;
+ do {
+ c = *yyInput++;
+ if (c == '\0')
+ return c;
+ if (c == '(')
+ Count++;
+ else if (c == ')')
+ Count--;
+ } while (Count > 0);
+ }
+}
+
+
+time_t
+get_date(p, now)
+ char *p;
+ struct timeb *now;
+{
+ struct tm *tm;
+ struct timeb ftz;
+ time_t Start;
+ time_t tod;
+
+ yyInput = p;
+ if (now == NULL) {
+ now = &ftz;
+#if !defined(HAVE_FTIME)
+ (void)time(&ftz.time);
+ /* Set the timezone global. */
+ tzset();
+ {
+#if sgi
+ ftz.timezone = (int) _timezone / 60;
+#else /* not sgi */
+#ifdef __386BSD__
+ ftz.timezone = 0;
+#else /* neither sgi nor 386BSD */
+#if defined (USG)
+ extern time_t timezone;
+
+ ftz.timezone = (int) timezone / 60;
+#else /* neither sgi nor 386BSD nor USG */
+ struct timeval tv;
+ struct timezone tz;
+
+ gettimeofday (&tv, &tz);
+ ftz.timezone = (int) tz.tz_minuteswest;
+#endif /* neither sgi nor 386BSD nor USG */
+#endif /* neither sgi nor 386BSD */
+#endif /* not sgi */
+ }
+#else /* HAVE_FTIME */
+ (void)ftime(&ftz);
+#endif /* HAVE_FTIME */
+ }
+
+ tm = localtime(&now->time);
+ yyYear = tm->tm_year;
+ yyMonth = tm->tm_mon + 1;
+ yyDay = tm->tm_mday;
+ yyTimezone = now->timezone;
+ yyDSTmode = DSTmaybe;
+ yyHour = 0;
+ yyMinutes = 0;
+ yySeconds = 0;
+ yyMeridian = MER24;
+ yyRelSeconds = 0;
+ yyRelMonth = 0;
+ yyHaveDate = 0;
+ yyHaveDay = 0;
+ yyHaveRel = 0;
+ yyHaveTime = 0;
+ yyHaveZone = 0;
+
+ if (yyparse()
+ || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
+ return -1;
+
+ if (yyHaveDate || yyHaveTime || yyHaveDay) {
+ Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
+ yyMeridian, yyDSTmode);
+ if (Start < 0)
+ return -1;
+ }
+ else {
+ Start = now->time;
+ if (!yyHaveRel)
+ Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
+ }
+
+ Start += yyRelSeconds;
+ Start += RelativeMonth(Start, yyRelMonth);
+
+ if (yyHaveDay && !yyHaveDate) {
+ tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
+ Start += tod;
+ }
+
+ /* Have to do *something* with a legitimate -1 so it's distinguishable
+ * from the error return value. (Alternately could set errno on error.) */
+ return Start == -1 ? 0 : Start;
+}
+
+
+#if defined(TEST)
+
+/* ARGSUSED */
+main(ac, av)
+ int ac;
+ char *av[];
+{
+ char buff[128];
+ time_t d;
+
+ (void)printf("Enter date, or blank line to exit.\n\t> ");
+ (void)fflush(stdout);
+ while (gets(buff) && buff[0]) {
+ d = get_date(buff, (struct timeb *)NULL);
+ if (d == -1)
+ (void)printf("Bad format - couldn't convert.\n");
+ else
+ (void)printf("%s", ctime(&d));
+ (void)printf("\t> ");
+ (void)fflush(stdout);
+ }
+ exit(0);
+ /* NOTREACHED */
+}
+#endif /* defined(TEST) */
diff --git a/gnu/usr.bin/tar/getoldopt.c b/gnu/usr.bin/tar/getoldopt.c
new file mode 100644
index 0000000..27511b9
--- /dev/null
+++ b/gnu/usr.bin/tar/getoldopt.c
@@ -0,0 +1,96 @@
+/* Replacement for getopt() that can be used by tar.
+ Copyright (C) 1988 Free Software Foundation
+
+This file is part of GNU Tar.
+
+GNU Tar is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Tar is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Tar; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * Plug-compatible replacement for getopt() for parsing tar-like
+ * arguments. If the first argument begins with "-", it uses getopt;
+ * otherwise, it uses the old rules used by tar, dump, and ps.
+ *
+ * Written 25 August 1985 by John Gilmore (ihnp4!hoptoad!gnu)
+ */
+
+#include <stdio.h>
+#include "getopt.h"
+#include "tar.h" /* For msg() declaration if STDC_MSG. */
+#include <sys/types.h>
+#include "port.h"
+
+int
+getoldopt (argc, argv, optstring, long_options, opt_index)
+ int argc;
+ char **argv;
+ char *optstring;
+ struct option *long_options;
+ int *opt_index;
+{
+ extern char *optarg; /* Points to next arg */
+ extern int optind; /* Global argv index */
+ static char *key; /* Points to next keyletter */
+ static char use_getopt; /* !=0 if argv[1][0] was '-' */
+ char c;
+ char *place;
+
+ optarg = NULL;
+
+ if (key == NULL)
+ { /* First time */
+ if (argc < 2)
+ return EOF;
+ key = argv[1];
+ if ((*key == '-') || (*key == '+'))
+ use_getopt++;
+ else
+ optind = 2;
+ }
+
+ if (use_getopt)
+ return getopt_long (argc, argv, optstring,
+ long_options, opt_index);
+
+ c = *key++;
+ if (c == '\0')
+ {
+ key--;
+ return EOF;
+ }
+ place = index (optstring, c);
+
+ if (place == NULL || c == ':')
+ {
+ msg ("unknown option %c", c);
+ return ('?');
+ }
+
+ place++;
+ if (*place == ':')
+ {
+ if (optind < argc)
+ {
+ optarg = argv[optind];
+ optind++;
+ }
+ else
+ {
+ msg ("%c argument missing", c);
+ return ('?');
+ }
+ }
+
+ return (c);
+}
diff --git a/gnu/usr.bin/tar/getopt.c b/gnu/usr.bin/tar/getopt.c
new file mode 100644
index 0000000..3db9abf
--- /dev/null
+++ b/gnu/usr.bin/tar/getopt.c
@@ -0,0 +1,712 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* NOTE!!! AIX requires this to be the first thing in the file.
+ Do not put ANYTHING before it! */
+#if !defined (__GNUC__) && defined (_AIX)
+ #pragma alloca
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not __GNUC__ */
+#if defined (HAVE_ALLOCA_H) || (defined(sparc) && (defined(sun) || (!defined(USG) && !defined(SVR4) && !defined(__svr4__))))
+#include <alloca.h>
+#else
+#ifndef _AIX
+char *alloca ();
+#endif
+#endif /* alloca.h */
+#endif /* not __GNUC__ */
+
+#if !__STDC__ && !defined(const) && IN_GCC
+#define const
+#endif
+
+#include <stdio.h>
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#undef alloca
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+#include <stdlib.h>
+#else /* Not GNU C library. */
+#define __alloca alloca
+#endif /* GNU C library. */
+
+/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
+ long-named option. Because this is not POSIX.2 compliant, it is
+ being phased out. */
+/* #define GETOPT_COMPAT */
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = 0;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* XXX 1003.2 says this must be 1 before any call. */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return EOF with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+#include <string.h>
+#define my_index strchr
+#define my_bcopy(src, dst, n) memcpy ((dst), (src), (n))
+#else
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+char *getenv ();
+
+static char *
+my_index (string, chr)
+ char *string;
+ int chr;
+{
+ while (*string)
+ {
+ if (*string == chr)
+ return string;
+ string++;
+ }
+ return 0;
+}
+
+static void
+my_bcopy (from, to, size)
+ char *from, *to;
+ int size;
+{
+ int i;
+ for (i = 0; i < size; i++)
+ to[i] = from[i];
+}
+#endif /* GNU C library. */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *);
+ char **temp = (char **) __alloca (nonopts_size);
+
+ /* Interchange the two blocks of data in ARGV. */
+
+ my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size);
+ my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt],
+ (optind - last_nonopt) * sizeof (char *));
+ my_bcopy ((char *) temp,
+ (char *) &argv[first_nonopt + optind - last_nonopt],
+ nonopts_size);
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns `EOF'.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ int option_index;
+
+ optarg = 0;
+
+ /* Initialize the internal data when the first call is made.
+ Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ if (optind == 0)
+ {
+ first_nonopt = last_nonopt = optind = 1;
+
+ nextchar = NULL;
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (getenv ("POSIXLY_CORRECT") != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+ }
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Now skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc
+ && (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* Special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return EOF;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if ((argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ {
+ if (ordering == REQUIRE_ORDER)
+ return EOF;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Start decoding its characters. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ if (longopts != NULL
+ && ((argv[optind][0] == '-'
+ && (argv[optind][1] == '-' || long_only))
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ ))
+ {
+ const struct option *p;
+ char *s = nextchar;
+ int exact = 0;
+ int ambig = 0;
+ const struct option *pfound = NULL;
+ int indfound;
+
+ while (*s && *s != '=')
+ s++;
+
+ /* Test all options for either exact match or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name;
+ p++, option_index++)
+ if (!strncmp (p->name, nextchar, s - nextchar))
+ {
+ if (s - nextchar == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' is ambiguous\n",
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*s)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = s + 1;
+ else
+ {
+ if (opterr)
+ {
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ "%s: option `--%s' doesn't allow an argument\n",
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ "%s: option `%c%s' doesn't allow an argument\n",
+ argv[0], argv[optind - 1][0], pfound->name);
+ }
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' requires an argument\n",
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, "%s: unrecognized option `--%s'\n",
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+#if 0
+ if (c < 040 || c >= 0177)
+ fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
+ argv[0], c);
+ else
+ fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
+#endif
+ }
+ optopt = c;
+ return '?';
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = 0;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+#if 0
+ fprintf (stderr, "%s: option `-%c' requires an argument\n",
+ argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: option requires an argument -- %c\n",
+ argv[0], c);
+#endif
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/gnu/usr.bin/tar/getopt.h b/gnu/usr.bin/tar/getopt.h
new file mode 100644
index 0000000..93a5cf7
--- /dev/null
+++ b/gnu/usr.bin/tar/getopt.h
@@ -0,0 +1,125 @@
+/* Declarations for getopt.
+ Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+#if __STDC__
+ const char *name;
+#else
+ char *name;
+#endif
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+#if __STDC__
+#if defined(__GNU_LIBRARY__)
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* not __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind);
+
+/* Internal only. Users should not call this directly. */
+extern int _getopt_internal (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* not __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/gnu/usr.bin/tar/getopt1.c b/gnu/usr.bin/tar/getopt1.c
new file mode 100644
index 0000000..c3582cf
--- /dev/null
+++ b/gnu/usr.bin/tar/getopt1.c
@@ -0,0 +1,161 @@
+/* Getopt for GNU.
+ Copyright (C) 1987, 88, 89, 90, 91, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "getopt.h"
+
+#if !__STDC__ && !defined(const) && IN_GCC
+#define const
+#endif
+
+#include <stdio.h>
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#else
+char *getenv ();
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/gnu/usr.bin/tar/getpagesize.h b/gnu/usr.bin/tar/getpagesize.h
new file mode 100644
index 0000000..2d43f26
--- /dev/null
+++ b/gnu/usr.bin/tar/getpagesize.h
@@ -0,0 +1,38 @@
+#ifdef BSD
+#ifndef BSD4_1
+#define HAVE_GETPAGESIZE
+#endif
+#endif
+
+#ifndef HAVE_GETPAGESIZE
+
+#ifdef VMS
+#define getpagesize() 512
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef _SC_PAGESIZE
+#define getpagesize() sysconf(_SC_PAGESIZE)
+#else
+
+#include <sys/param.h>
+
+#ifdef EXEC_PAGESIZE
+#define getpagesize() EXEC_PAGESIZE
+#else
+#ifdef NBPG
+#define getpagesize() NBPG * CLSIZE
+#ifndef CLSIZE
+#define CLSIZE 1
+#endif /* no CLSIZE */
+#else /* no NBPG */
+#define getpagesize() NBPC
+#endif /* no NBPG */
+#endif /* no EXEC_PAGESIZE */
+#endif /* no _SC_PAGESIZE */
+
+#endif /* not HAVE_GETPAGESIZE */
+
diff --git a/gnu/usr.bin/tar/gnu.c b/gnu/usr.bin/tar/gnu.c
new file mode 100644
index 0000000..ef51f2b
--- /dev/null
+++ b/gnu/usr.bin/tar/gnu.c
@@ -0,0 +1,677 @@
+/* GNU dump extensions to tar.
+ Copyright (C) 1988, 1992, 1993 Free Software Foundation
+
+This file is part of GNU Tar.
+
+GNU Tar is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Tar is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Tar; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+#include <time.h>
+time_t time ();
+
+#include "tar.h"
+#include "port.h"
+
+#ifndef S_ISLNK
+#define lstat stat
+#endif
+
+extern time_t new_time;
+extern FILE *msg_file;
+
+void addname ();
+int check_exclude ();
+extern PTR ck_malloc ();
+extern PTR ck_realloc ();
+int confirm ();
+extern PTR init_buffer ();
+extern char *get_buffer ();
+int is_dot_or_dotdot ();
+extern void add_buffer ();
+extern void flush_buffer ();
+void name_gather ();
+int recursively_delete ();
+void skip_file ();
+char *un_quote_string ();
+
+extern char *new_name ();
+
+static void add_dir_name ();
+
+struct dirname
+ {
+ struct dirname *next;
+ char *name;
+ char *dir_text;
+ int dev;
+ int ino;
+ int allnew;
+ };
+static struct dirname *dir_list;
+static time_t this_time;
+
+void
+add_dir (name, dev, ino, text)
+ char *name;
+ char *text;
+ dev_t dev;
+ ino_t ino;
+{
+ struct dirname *dp;
+
+ dp = (struct dirname *) ck_malloc (sizeof (struct dirname));
+ if (!dp)
+ abort ();
+ dp->next = dir_list;
+ dir_list = dp;
+ dp->dev = dev;
+ dp->ino = ino;
+ dp->name = ck_malloc (strlen (name) + 1);
+ strcpy (dp->name, name);
+ dp->dir_text = text;
+ dp->allnew = 0;
+}
+
+void
+read_dir_file ()
+{
+ int dev;
+ int ino;
+ char *strp;
+ FILE *fp;
+ char buf[512];
+ static char *path = 0;
+
+ if (path == 0)
+ path = ck_malloc (PATH_MAX);
+ time (&this_time);
+ if (gnu_dumpfile[0] != '/')
+ {
+#if defined(__MSDOS__) || defined(HAVE_GETCWD) || defined(_POSIX_VERSION)
+ if (!getcwd (path, PATH_MAX))
+ {
+ msg ("Couldn't get current directory.");
+ exit (EX_SYSTEM);
+ }
+#else
+ char *getwd ();
+
+ if (!getwd (path))
+ {
+ msg ("Couldn't get current directory: %s", path);
+ exit (EX_SYSTEM);
+ }
+#endif
+ /* If this doesn't fit, we're in serious trouble */
+ strcat (path, "/");
+ strcat (path, gnu_dumpfile);
+ gnu_dumpfile = path;
+ }
+ fp = fopen (gnu_dumpfile, "r");
+ if (fp == 0 && errno != ENOENT)
+ {
+ msg_perror ("Can't open %s", gnu_dumpfile);
+ return;
+ }
+ if (!fp)
+ return;
+ fgets (buf, sizeof (buf), fp);
+ if (!f_new_files)
+ {
+ f_new_files++;
+ new_time = atol (buf);
+ }
+ while (fgets (buf, sizeof (buf), fp))
+ {
+ strp = &buf[strlen (buf)];
+ if (strp[-1] == '\n')
+ strp[-1] = '\0';
+ strp = buf;
+ dev = atol (strp);
+ while (isdigit (*strp))
+ strp++;
+ ino = atol (strp);
+ while (isspace (*strp))
+ strp++;
+ while (isdigit (*strp))
+ strp++;
+ strp++;
+ add_dir (un_quote_string (strp), dev, ino, (char *) 0);
+ }
+ fclose (fp);
+}
+
+void
+write_dir_file ()
+{
+ FILE *fp;
+ struct dirname *dp;
+ char *str;
+ extern char *quote_copy_string ();
+
+ fp = fopen (gnu_dumpfile, "w");
+ if (fp == 0)
+ {
+ msg_perror ("Can't write to %s", gnu_dumpfile);
+ return;
+ }
+ fprintf (fp, "%lu\n", this_time);
+ for (dp = dir_list; dp; dp = dp->next)
+ {
+ if (!dp->dir_text)
+ continue;
+ str = quote_copy_string (dp->name);
+ if (str)
+ {
+ fprintf (fp, "%u %u %s\n", dp->dev, dp->ino, str);
+ free (str);
+ }
+ else
+ fprintf (fp, "%u %u %s\n", dp->dev, dp->ino, dp->name);
+ }
+ fclose (fp);
+}
+
+struct dirname *
+get_dir (name)
+ char *name;
+{
+ struct dirname *dp;
+
+ for (dp = dir_list; dp; dp = dp->next)
+ {
+ if (!strcmp (dp->name, name))
+ return dp;
+ }
+ return 0;
+}
+
+
+/* Collect all the names from argv[] (or whatever), then expand them into
+ a directory tree, and put all the directories at the beginning. */
+void
+collect_and_sort_names ()
+{
+ struct name *n, *n_next;
+ int num_names;
+ struct stat statbuf;
+ int name_cmp ();
+ char *merge_sort ();
+
+ name_gather ();
+
+ if (gnu_dumpfile)
+ read_dir_file ();
+ if (!namelist)
+ addname (".");
+ for (n = namelist; n; n = n_next)
+ {
+ n_next = n->next;
+ if (n->found || n->dir_contents)
+ continue;
+ if (n->regexp) /* FIXME just skip regexps for now */
+ continue;
+ if (n->change_dir)
+ if (chdir (n->change_dir) < 0)
+ {
+ msg_perror ("can't chdir to %s", n->change_dir);
+ continue;
+ }
+
+#ifdef AIX
+ if (statx (n->name, &statbuf, STATSIZE, STX_HIDDEN | STX_LINK))
+#else
+ if (lstat (n->name, &statbuf) < 0)
+#endif /* AIX */
+ {
+ msg_perror ("can't stat %s", n->name);
+ continue;
+ }
+ if (S_ISDIR (statbuf.st_mode))
+ {
+ n->found++;
+ add_dir_name (n->name, statbuf.st_dev);
+ }
+ }
+
+ num_names = 0;
+ for (n = namelist; n; n = n->next)
+ num_names++;
+ namelist = (struct name *) merge_sort ((PTR) namelist, num_names, (char *) (&(namelist->next)) - (char *) namelist, name_cmp);
+
+ for (n = namelist; n; n = n->next)
+ {
+ n->found = 0;
+ }
+ if (gnu_dumpfile)
+ write_dir_file ();
+}
+
+int
+name_cmp (n1, n2)
+ struct name *n1, *n2;
+{
+ if (n1->found)
+ {
+ if (n2->found)
+ return strcmp (n1->name, n2->name);
+ else
+ return -1;
+ }
+ else if (n2->found)
+ return 1;
+ else
+ return strcmp (n1->name, n2->name);
+}
+
+int
+dirent_cmp (p1, p2)
+ const PTR p1;
+ const PTR p2;
+{
+ char *frst, *scnd;
+
+ frst = (*(char **) p1) + 1;
+ scnd = (*(char **) p2) + 1;
+
+ return strcmp (frst, scnd);
+}
+
+char *
+get_dir_contents (p, device)
+ char *p;
+ int device;
+{
+ DIR *dirp;
+ register struct dirent *d;
+ char *new_buf;
+ char *namebuf;
+ int bufsiz;
+ int len;
+ PTR the_buffer;
+ char *buf;
+ size_t n_strs;
+ /* int n_size;*/
+ char *p_buf;
+ char **vec, **p_vec;
+
+ extern int errno;
+
+ errno = 0;
+ dirp = opendir (p);
+ bufsiz = strlen (p) + NAMSIZ;
+ namebuf = ck_malloc (bufsiz + 2);
+ if (!dirp)
+ {
+ if (errno)
+ msg_perror ("can't open directory %s", p);
+ else
+ msg ("error opening directory %s", p);
+ new_buf = NULL;
+ }
+ else
+ {
+ struct dirname *dp;
+ int all_children;
+
+ dp = get_dir (p);
+ all_children = dp ? dp->allnew : 0;
+ (void) strcpy (namebuf, p);
+ if (p[strlen (p) - 1] != '/')
+ (void) strcat (namebuf, "/");
+ len = strlen (namebuf);
+
+ the_buffer = init_buffer ();
+ while (d = readdir (dirp))
+ {
+ struct stat hs;
+
+ /* Skip . and .. */
+ if (is_dot_or_dotdot (d->d_name))
+ continue;
+ if (NLENGTH (d) + len >= bufsiz)
+ {
+ bufsiz += NAMSIZ;
+ namebuf = ck_realloc (namebuf, bufsiz + 2);
+ }
+ (void) strcpy (namebuf + len, d->d_name);
+#ifdef AIX
+ if (0 != f_follow_links ?
+ statx (namebuf, &hs, STATSIZE, STX_HIDDEN) :
+ statx (namebuf, &hs, STATSIZE, STX_HIDDEN | STX_LINK))
+#else
+ if (0 != f_follow_links ? stat (namebuf, &hs) : lstat (namebuf, &hs))
+#endif
+ {
+ msg_perror ("can't stat %s", namebuf);
+ continue;
+ }
+ if ((f_local_filesys && device != hs.st_dev)
+ || (f_exclude && check_exclude (namebuf)))
+ add_buffer (the_buffer, "N", 1);
+#ifdef AIX
+ else if (S_ISHIDDEN (hs.st_mode))
+ {
+ add_buffer (the_buffer, "D", 1);
+ strcat (d->d_name, "A");
+ d->d_namlen++;
+ }
+#endif /* AIX */
+ else if (S_ISDIR (hs.st_mode))
+ {
+ if (dp = get_dir (namebuf))
+ {
+ if (dp->dev != hs.st_dev
+ || dp->ino != hs.st_ino)
+ {
+ if (f_verbose)
+ msg ("directory %s has been renamed.", namebuf);
+ dp->allnew = 1;
+ dp->dev = hs.st_dev;
+ dp->ino = hs.st_ino;
+ }
+ dp->dir_text = "";
+ }
+ else
+ {
+ if (f_verbose)
+ msg ("Directory %s is new", namebuf);
+ add_dir (namebuf, hs.st_dev, hs.st_ino, "");
+ dp = get_dir (namebuf);
+ dp->allnew = 1;
+ }
+ if (all_children)
+ dp->allnew = 1;
+
+ add_buffer (the_buffer, "D", 1);
+ }
+ else if (!all_children
+ && f_new_files
+ && new_time > hs.st_mtime
+ && (f_new_files > 1
+ || new_time > hs.st_ctime))
+ add_buffer (the_buffer, "N", 1);
+ else
+ add_buffer (the_buffer, "Y", 1);
+ add_buffer (the_buffer, d->d_name, (int) (NLENGTH (d) + 1));
+ }
+ add_buffer (the_buffer, "\000\000", 2);
+ closedir (dirp);
+
+ /* Well, we've read in the contents of the dir, now sort them */
+ buf = get_buffer (the_buffer);
+ if (buf[0] == '\0')
+ {
+ flush_buffer (the_buffer);
+ new_buf = NULL;
+ }
+ else
+ {
+ n_strs = 0;
+ for (p_buf = buf; *p_buf;)
+ {
+ int tmp;
+
+ tmp = strlen (p_buf) + 1;
+ n_strs++;
+ p_buf += tmp;
+ }
+ vec = (char **) ck_malloc (sizeof (char *) * (n_strs + 1));
+ for (p_vec = vec, p_buf = buf; *p_buf; p_buf += strlen (p_buf) + 1)
+ *p_vec++ = p_buf;
+ *p_vec = 0;
+ qsort ((PTR) vec, n_strs, sizeof (char *), dirent_cmp);
+ new_buf = (char *) ck_malloc (p_buf - buf + 2);
+ for (p_vec = vec, p_buf = new_buf; *p_vec; p_vec++)
+ {
+ char *p_tmp;
+
+ for (p_tmp = *p_vec; *p_buf++ = *p_tmp++;)
+ ;
+ }
+ *p_buf++ = '\0';
+ free (vec);
+ flush_buffer (the_buffer);
+ }
+ }
+ free (namebuf);
+ return new_buf;
+}
+
+/* p is a directory. Add all the files in P to the namelist. If any of the
+ files is a directory, recurse on the subdirectory. . . */
+static void
+add_dir_name (p, device)
+ char *p;
+ int device;
+{
+ char *new_buf;
+ char *p_buf;
+
+ char *namebuf;
+ int buflen;
+ register int len;
+ int sublen;
+
+ /* PTR the_buffer;*/
+
+ /* char *buf;*/
+ /* char **vec,**p_vec;*/
+ /* int n_strs,n_size;*/
+
+ struct name *n;
+
+ int dirent_cmp ();
+
+ new_buf = get_dir_contents (p, device);
+
+ for (n = namelist; n; n = n->next)
+ {
+ if (!strcmp (n->name, p))
+ {
+ n->dir_contents = new_buf ? new_buf : "\0\0\0\0";
+ break;
+ }
+ }
+
+ if (new_buf)
+ {
+ len = strlen (p);
+ buflen = NAMSIZ <= len ? len + NAMSIZ : NAMSIZ;
+ namebuf = ck_malloc (buflen + 1);
+
+ (void) strcpy (namebuf, p);
+ if (namebuf[len - 1] != '/')
+ {
+ namebuf[len++] = '/';
+ namebuf[len] = '\0';
+ }
+ for (p_buf = new_buf; *p_buf; p_buf += sublen + 1)
+ {
+ sublen = strlen (p_buf);
+ if (*p_buf == 'D')
+ {
+ if (len + sublen >= buflen)
+ {
+ buflen += NAMSIZ;
+ namebuf = ck_realloc (namebuf, buflen + 1);
+ }
+ (void) strcpy (namebuf + len, p_buf + 1);
+ addname (namebuf);
+ add_dir_name (namebuf, device);
+ }
+ }
+ free (namebuf);
+ }
+}
+
+/* Returns non-zero if p is . or .. This could be a macro for speed. */
+int
+is_dot_or_dotdot (p)
+ char *p;
+{
+ return (p[0] == '.' && (p[1] == '\0' || (p[1] == '.' && p[2] == '\0')));
+}
+
+
+
+
+
+
+void
+gnu_restore (skipcrud)
+ int skipcrud;
+{
+ char *current_dir;
+ /* int current_dir_length; */
+
+ char *archive_dir;
+ /* int archive_dir_length; */
+ PTR the_buffer;
+ char *p;
+ DIR *dirp;
+ struct dirent *d;
+ char *cur, *arc;
+ extern struct stat hstat; /* Stat struct corresponding */
+ long size, copied;
+ char *from, *to;
+ extern union record *head;
+
+ dirp = opendir (skipcrud + current_file_name);
+
+ if (!dirp)
+ {
+ /* The directory doesn't exist now. It'll be created.
+ In any case, we don't have to delete any files out
+ of it */
+ skip_file ((long) hstat.st_size);
+ return;
+ }
+
+ the_buffer = init_buffer ();
+ while (d = readdir (dirp))
+ {
+ if (is_dot_or_dotdot (d->d_name))
+ continue;
+
+ add_buffer (the_buffer, d->d_name, (int) (NLENGTH (d) + 1));
+ }
+ closedir (dirp);
+ add_buffer (the_buffer, "", 1);
+
+ current_dir = get_buffer (the_buffer);
+ archive_dir = (char *) ck_malloc (hstat.st_size);
+ if (archive_dir == 0)
+ {
+ msg ("Can't allocate %d bytes for restore", hstat.st_size);
+ skip_file ((long) hstat.st_size);
+ return;
+ }
+ to = archive_dir;
+ for (size = hstat.st_size; size > 0; size -= copied)
+ {
+ from = findrec ()->charptr;
+ if (!from)
+ {
+ msg ("Unexpected EOF in archive\n");
+ break;
+ }
+ copied = endofrecs ()->charptr - from;
+ if (copied > size)
+ copied = size;
+ bcopy ((PTR) from, (PTR) to, (int) copied);
+ to += copied;
+ userec ((union record *) (from + copied - 1));
+ }
+
+ for (cur = current_dir; *cur; cur += strlen (cur) + 1)
+ {
+ for (arc = archive_dir; *arc; arc += strlen (arc) + 1)
+ {
+ arc++;
+ if (!strcmp (arc, cur))
+ break;
+ }
+ if (*arc == '\0')
+ {
+ p = new_name (skipcrud + current_file_name, cur);
+ if (f_confirm && !confirm ("delete", p))
+ {
+ free (p);
+ continue;
+ }
+ if (f_verbose)
+ fprintf (msg_file, "%s: deleting %s\n", tar, p);
+ if (recursively_delete (p))
+ {
+ msg ("%s: Error while deleting %s\n", tar, p);
+ }
+ free (p);
+ }
+
+ }
+ flush_buffer (the_buffer);
+ free (archive_dir);
+}
+
+int
+recursively_delete (path)
+ char *path;
+{
+ struct stat sbuf;
+ DIR *dirp;
+ struct dirent *dp;
+ char *path_buf;
+ /* int path_len; */
+
+
+ if (lstat (path, &sbuf) < 0)
+ return 1;
+ if (S_ISDIR (sbuf.st_mode))
+ {
+
+ /* path_len=strlen(path); */
+ dirp = opendir (path);
+ if (dirp == 0)
+ return 1;
+ while (dp = readdir (dirp))
+ {
+ if (is_dot_or_dotdot (dp->d_name))
+ continue;
+ path_buf = new_name (path, dp->d_name);
+ if (recursively_delete (path_buf))
+ {
+ free (path_buf);
+ closedir (dirp);
+ return 1;
+ }
+ free (path_buf);
+ }
+ closedir (dirp);
+
+ if (rmdir (path) < 0)
+ return 1;
+ return 0;
+ }
+ if (unlink (path) < 0)
+ return 1;
+ return 0;
+}
diff --git a/gnu/usr.bin/tar/list.c b/gnu/usr.bin/tar/list.c
new file mode 100644
index 0000000..a0c65a3
--- /dev/null
+++ b/gnu/usr.bin/tar/list.c
@@ -0,0 +1,881 @@
+/* List a tar archive.
+ Copyright (C) 1988, 1992, 1993 Free Software Foundation
+
+This file is part of GNU Tar.
+
+GNU Tar is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Tar is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Tar; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * List a tar archive.
+ *
+ * Also includes support routines for reading a tar archive.
+ *
+ * this version written 26 Aug 1985 by John Gilmore (ihnp4!hoptoad!gnu).
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+#include <time.h>
+
+#ifdef BSD42
+#include <sys/file.h>
+#else
+#ifndef V7
+#include <fcntl.h>
+#endif
+#endif
+
+#define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
+
+#include "tar.h"
+#include "port.h"
+
+extern FILE *msg_file;
+
+long from_oct (); /* Decode octal number */
+void demode (); /* Print file mode */
+void restore_saved_dir_info ();
+PTR ck_malloc ();
+
+union record *head; /* Points to current archive header */
+struct stat hstat; /* Stat struct corresponding */
+int head_standard; /* Tape header is in ANSI format */
+
+int check_exclude ();
+void close_archive ();
+void decode_header ();
+int findgid ();
+int finduid ();
+void name_gather ();
+int name_match ();
+void names_notfound ();
+void open_archive ();
+void print_header ();
+int read_header ();
+void saverec ();
+void skip_file ();
+void skip_extended_headers ();
+
+extern char *quote_copy_string ();
+
+
+/*
+ * Main loop for reading an archive.
+ */
+void
+read_and (do_something)
+ void (*do_something) ();
+{
+ int status = 3; /* Initial status at start of archive */
+ int prev_status;
+ extern time_t new_time;
+ char save_linkflag;
+
+ name_gather (); /* Gather all the names */
+ open_archive (1); /* Open for reading */
+
+ for (;;)
+ {
+ prev_status = status;
+ status = read_header ();
+ switch (status)
+ {
+
+ case 1: /* Valid header */
+ /* We should decode next field (mode) first... */
+ /* Ensure incoming names are null terminated. */
+
+ if (!name_match (current_file_name)
+ || (f_new_files && hstat.st_mtime < new_time)
+ || (f_exclude && check_exclude (current_file_name)))
+ {
+
+ int isextended = 0;
+
+ if (head->header.linkflag == LF_VOLHDR
+ || head->header.linkflag == LF_MULTIVOL
+ || head->header.linkflag == LF_NAMES)
+ {
+ (*do_something) ();
+ continue;
+ }
+ if (f_show_omitted_dirs
+ && head->header.linkflag == LF_DIR)
+ msg ("Omitting %s\n", current_file_name);
+ /* Skip past it in the archive */
+ if (head->header.isextended)
+ isextended = 1;
+ save_linkflag = head->header.linkflag;
+ userec (head);
+ if (isextended)
+ {
+ /* register union record *exhdr;
+
+ for (;;) {
+ exhdr = findrec();
+ if (!exhdr->ext_hdr.isextended) {
+ userec(exhdr);
+ break;
+ }
+ }
+ userec(exhdr);*/
+ skip_extended_headers ();
+ }
+ /* Skip to the next header on the archive */
+ if (save_linkflag != LF_DIR)
+ skip_file ((long) hstat.st_size);
+ continue;
+
+ }
+
+ (*do_something) ();
+ continue;
+
+ /*
+ * If the previous header was good, tell them
+ * that we are skipping bad ones.
+ */
+ case 0: /* Invalid header */
+ userec (head);
+ switch (prev_status)
+ {
+ case 3: /* Error on first record */
+ msg ("Hmm, this doesn't look like a tar archive.");
+ /* FALL THRU */
+ case 2: /* Error after record of zeroes */
+ case 1: /* Error after header rec */
+ msg ("Skipping to next file header...");
+ case 0: /* Error after error */
+ break;
+ }
+ continue;
+
+ case 2: /* Record of zeroes */
+ userec (head);
+ status = prev_status; /* If error after 0's */
+ if (f_ignorez)
+ continue;
+ /* FALL THRU */
+ case EOF: /* End of archive */
+ break;
+ }
+ break;
+ };
+
+ restore_saved_dir_info ();
+ close_archive ();
+ names_notfound (); /* Print names not found */
+}
+
+
+/*
+ * Print a header record, based on tar options.
+ */
+void
+list_archive ()
+{
+ extern char *save_name;
+ int isextended = 0; /* Flag to remember if head is extended */
+
+ /* Save the record */
+ saverec (&head);
+
+ /* Print the header record */
+ if (f_verbose)
+ {
+ if (f_verbose > 1)
+ decode_header (head, &hstat, &head_standard, 0);
+ print_header ();
+ }
+
+ if (f_gnudump && head->header.linkflag == LF_DUMPDIR)
+ {
+ size_t size, written, check;
+ char *data;
+ extern long save_totsize;
+ extern long save_sizeleft;
+
+ userec (head);
+ if (f_multivol)
+ {
+ save_name = current_file_name;
+ save_totsize = hstat.st_size;
+ }
+ for (size = hstat.st_size; size > 0; size -= written)
+ {
+ if (f_multivol)
+ save_sizeleft = size;
+ data = findrec ()->charptr;
+ if (data == NULL)
+ {
+ msg ("EOF in archive file?");
+ break;
+ }
+ written = endofrecs ()->charptr - data;
+ if (written > size)
+ written = size;
+ errno = 0;
+ check = fwrite (data, sizeof (char), written, msg_file);
+ userec ((union record *) (data + written - 1));
+ if (check != written)
+ {
+ msg_perror ("only wrote %ld of %ld bytes to file %s", check, written, current_file_name);
+ skip_file ((long) (size) - written);
+ break;
+ }
+ }
+ if (f_multivol)
+ save_name = 0;
+ saverec ((union record **) 0); /* Unsave it */
+ fputc ('\n', msg_file);
+ fflush (msg_file);
+ return;
+
+ }
+ saverec ((union record **) 0);/* Unsave it */
+ /* Check to see if we have an extended header to skip over also */
+ if (head->header.isextended)
+ isextended = 1;
+
+ /* Skip past the header in the archive */
+ userec (head);
+
+ /*
+ * If we needed to skip any extended headers, do so now, by
+ * reading extended headers and skipping past them in the
+ * archive.
+ */
+ if (isextended)
+ {
+ /* register union record *exhdr;
+
+ for (;;) {
+ exhdr = findrec();
+
+ if (!exhdr->ext_hdr.isextended) {
+ userec(exhdr);
+ break;
+ }
+ userec(exhdr);
+ }*/
+ skip_extended_headers ();
+ }
+
+ if (f_multivol)
+ save_name = current_file_name;
+ /* Skip to the next header on the archive */
+
+ skip_file ((long) hstat.st_size);
+
+ if (f_multivol)
+ save_name = 0;
+}
+
+
+/*
+ * Read a record that's supposed to be a header record.
+ * Return its address in "head", and if it is good, the file's
+ * size in hstat.st_size.
+ *
+ * Return 1 for success, 0 if the checksum is bad, EOF on eof,
+ * 2 for a record full of zeros (EOF marker).
+ *
+ * You must always userec(head) to skip past the header which this
+ * routine reads.
+ */
+int
+read_header ()
+{
+ register int i;
+ register long sum, signed_sum, recsum;
+ register char *p;
+ register union record *header;
+ long from_oct ();
+ char **longp;
+ char *bp, *data;
+ int size, written;
+ static char *next_long_name, *next_long_link;
+ char *name;
+
+recurse:
+
+ header = findrec ();
+ head = header; /* This is our current header */
+ if (NULL == header)
+ return EOF;
+
+ recsum = from_oct (8, header->header.chksum);
+
+ sum = 0;
+ p = header->charptr;
+ for (i = sizeof (*header); --i >= 0;)
+ {
+ /*
+ * We can't use unsigned char here because of old compilers,
+ * e.g. V7.
+ */
+ signed_sum += *p;
+ sum += 0xFF & *p++;
+ }
+
+ /* Adjust checksum to count the "chksum" field as blanks. */
+ for (i = sizeof (header->header.chksum); --i >= 0;)
+ {
+ sum -= 0xFF & header->header.chksum[i];
+ signed_sum -= (char) header->header.chksum[i];
+ }
+ sum += ' ' * sizeof header->header.chksum;
+ signed_sum += ' ' * sizeof header->header.chksum;
+
+ if (sum == 8 * ' ')
+ {
+ /*
+ * This is a zeroed record...whole record is 0's except
+ * for the 8 blanks we faked for the checksum field.
+ */
+ return 2;
+ }
+
+ if (sum != recsum && signed_sum != recsum)
+ return 0;
+
+ /*
+ * Good record. Decode file size and return.
+ */
+ if (header->header.linkflag == LF_LINK)
+ hstat.st_size = 0; /* Links 0 size on tape */
+ else
+ hstat.st_size = from_oct (1 + 12, header->header.size);
+
+ header->header.arch_name[NAMSIZ - 1] = '\0';
+ if (header->header.linkflag == LF_LONGNAME
+ || header->header.linkflag == LF_LONGLINK)
+ {
+ longp = ((header->header.linkflag == LF_LONGNAME)
+ ? &next_long_name
+ : &next_long_link);
+
+ userec (header);
+ if (*longp)
+ free (*longp);
+ bp = *longp = (char *) ck_malloc (hstat.st_size);
+
+ for (size = hstat.st_size;
+ size > 0;
+ size -= written)
+ {
+ data = findrec ()->charptr;
+ if (data == NULL)
+ {
+ msg ("Unexpected EOF on archive file");
+ break;
+ }
+ written = endofrecs ()->charptr - data;
+ if (written > size)
+ written = size;
+
+ bcopy (data, bp, written);
+ bp += written;
+ userec ((union record *) (data + written - 1));
+ }
+ goto recurse;
+ }
+ else
+ {
+ name = (next_long_name
+ ? next_long_name
+ : head->header.arch_name);
+ if (current_file_name)
+ free (current_file_name);
+ current_file_name = ck_malloc (strlen (name) + 1);
+ strcpy (current_file_name, name);
+
+ name = (next_long_link
+ ? next_long_link
+ : head->header.arch_linkname);
+ if (current_link_name)
+ free (current_link_name);
+ current_link_name = ck_malloc (strlen (name) + 1);
+ strcpy (current_link_name, name);
+
+ next_long_link = next_long_name = 0;
+ return 1;
+ }
+}
+
+
+/*
+ * Decode things from a file header record into a "struct stat".
+ * Also set "*stdp" to !=0 or ==0 depending whether header record is "Unix
+ * Standard" tar format or regular old tar format.
+ *
+ * read_header() has already decoded the checksum and length, so we don't.
+ *
+ * If wantug != 0, we want the uid/group info decoded from Unix Standard
+ * tapes (for extraction). If == 0, we are just printing anyway, so save time.
+ *
+ * decode_header should NOT be called twice for the same record, since the
+ * two calls might use different "wantug" values and thus might end up with
+ * different uid/gid for the two calls. If anybody wants the uid/gid they
+ * should decode it first, and other callers should decode it without uid/gid
+ * before calling a routine, e.g. print_header, that assumes decoded data.
+ */
+void
+decode_header (header, st, stdp, wantug)
+ register union record *header;
+ register struct stat *st;
+ int *stdp;
+ int wantug;
+{
+ long from_oct ();
+
+ st->st_mode = from_oct (8, header->header.mode);
+ st->st_mode &= 07777;
+ st->st_mtime = from_oct (1 + 12, header->header.mtime);
+ if (f_gnudump)
+ {
+ st->st_atime = from_oct (1 + 12, header->header.atime);
+ st->st_ctime = from_oct (1 + 12, header->header.ctime);
+ }
+
+ if (0 == strcmp (header->header.magic, TMAGIC))
+ {
+ /* Unix Standard tar archive */
+ *stdp = 1;
+ if (wantug)
+ {
+#ifdef NONAMES
+ st->st_uid = from_oct (8, header->header.uid);
+ st->st_gid = from_oct (8, header->header.gid);
+#else
+ st->st_uid =
+ (*header->header.uname
+ ? finduid (header->header.uname)
+ : from_oct (8, header->header.uid));
+ st->st_gid =
+ (*header->header.gname
+ ? findgid (header->header.gname)
+ : from_oct (8, header->header.gid));
+#endif
+ }
+#if defined(S_IFBLK) || defined(S_IFCHR)
+ switch (header->header.linkflag)
+ {
+ case LF_BLK:
+ case LF_CHR:
+ st->st_rdev = makedev (from_oct (8, header->header.devmajor),
+ from_oct (8, header->header.devminor));
+ }
+#endif
+ }
+ else
+ {
+ /* Old fashioned tar archive */
+ *stdp = 0;
+ st->st_uid = from_oct (8, header->header.uid);
+ st->st_gid = from_oct (8, header->header.gid);
+ st->st_rdev = 0;
+ }
+}
+
+
+/*
+ * Quick and dirty octal conversion.
+ *
+ * Result is -1 if the field is invalid (all blank, or nonoctal).
+ */
+long
+from_oct (digs, where)
+ register int digs;
+ register char *where;
+{
+ register long value;
+
+ while (isspace (*where))
+ { /* Skip spaces */
+ where++;
+ if (--digs <= 0)
+ return -1; /* All blank field */
+ }
+ value = 0;
+ while (digs > 0 && isodigit (*where))
+ { /* Scan til nonoctal */
+ value = (value << 3) | (*where++ - '0');
+ --digs;
+ }
+
+ if (digs > 0 && *where && !isspace (*where))
+ return -1; /* Ended on non-space/nul */
+
+ return value;
+}
+
+
+/*
+ * Actually print it.
+ *
+ * Plain and fancy file header block logging.
+ * Non-verbose just prints the name, e.g. for "tar t" or "tar x".
+ * This should just contain file names, so it can be fed back into tar
+ * with xargs or the "-T" option. The verbose option can give a bunch
+ * of info, one line per file. I doubt anybody tries to parse its
+ * format, or if they do, they shouldn't. Unix tar is pretty random here
+ * anyway.
+ *
+ * Note that print_header uses the globals <head>, <hstat>, and
+ * <head_standard>, which must be set up in advance. This is not very clean
+ * and should be cleaned up. FIXME.
+ */
+#define UGSWIDTH 18 /* min width of User, group, size */
+/* UGSWIDTH of 18 means that with user and group names <= 8 chars the columns
+ never shift during the listing. */
+#define DATEWIDTH 19 /* Last mod date */
+static int ugswidth = UGSWIDTH; /* Max width encountered so far */
+
+void
+print_header ()
+{
+ char modes[11];
+ char *timestamp;
+ char uform[11], gform[11]; /* These hold formatted ints */
+ char *user, *group;
+ char size[24]; /* Holds a formatted long or maj, min */
+ time_t longie; /* To make ctime() call portable */
+ int pad;
+ char *name;
+ extern long baserec;
+
+ if (f_sayblock)
+ fprintf (msg_file, "rec %10d: ", baserec + (ar_record - ar_block));
+ /* annofile(msg_file, (char *)NULL); */
+
+ if (f_verbose <= 1)
+ {
+ /* Just the fax, mam. */
+ char *name;
+
+ name = quote_copy_string (current_file_name);
+ if (name == 0)
+ name = current_file_name;
+ fprintf (msg_file, "%s\n", name);
+ if (name != current_file_name)
+ free (name);
+ }
+ else
+ {
+ /* File type and modes */
+ modes[0] = '?';
+ switch (head->header.linkflag)
+ {
+ case LF_VOLHDR:
+ modes[0] = 'V';
+ break;
+
+ case LF_MULTIVOL:
+ modes[0] = 'M';
+ break;
+
+ case LF_NAMES:
+ modes[0] = 'N';
+ break;
+
+ case LF_LONGNAME:
+ case LF_LONGLINK:
+ msg ("Visible longname error\n");
+ break;
+
+ case LF_SPARSE:
+ case LF_NORMAL:
+ case LF_OLDNORMAL:
+ case LF_LINK:
+ modes[0] = '-';
+ if ('/' == current_file_name[strlen (current_file_name) - 1])
+ modes[0] = 'd';
+ break;
+ case LF_DUMPDIR:
+ modes[0] = 'd';
+ break;
+ case LF_DIR:
+ modes[0] = 'd';
+ break;
+ case LF_SYMLINK:
+ modes[0] = 'l';
+ break;
+ case LF_BLK:
+ modes[0] = 'b';
+ break;
+ case LF_CHR:
+ modes[0] = 'c';
+ break;
+ case LF_FIFO:
+ modes[0] = 'p';
+ break;
+ case LF_CONTIG:
+ modes[0] = 'C';
+ break;
+ }
+
+ demode ((unsigned) hstat.st_mode, modes + 1);
+
+ /* Timestamp */
+ longie = hstat.st_mtime;
+ timestamp = ctime (&longie);
+ timestamp[16] = '\0';
+ timestamp[24] = '\0';
+
+ /* User and group names */
+ if (*head->header.uname && head_standard)
+ {
+ user = head->header.uname;
+ }
+ else
+ {
+ user = uform;
+ (void) sprintf (uform, "%d",
+ from_oct (8, head->header.uid));
+ }
+ if (*head->header.gname && head_standard)
+ {
+ group = head->header.gname;
+ }
+ else
+ {
+ group = gform;
+ (void) sprintf (gform, "%d",
+ from_oct (8, head->header.gid));
+ }
+
+ /* Format the file size or major/minor device numbers */
+ switch (head->header.linkflag)
+ {
+#if defined(S_IFBLK) || defined(S_IFCHR)
+ case LF_CHR:
+ case LF_BLK:
+ (void) sprintf (size, "%d,%d",
+ major (hstat.st_rdev),
+ minor (hstat.st_rdev));
+ break;
+#endif
+ case LF_SPARSE:
+ (void) sprintf (size, "%ld",
+ from_oct (1 + 12, head->header.realsize));
+ break;
+ default:
+ (void) sprintf (size, "%ld", (long) hstat.st_size);
+ }
+
+ /* Figure out padding and print the whole line. */
+ pad = strlen (user) + strlen (group) + strlen (size) + 1;
+ if (pad > ugswidth)
+ ugswidth = pad;
+
+ name = quote_copy_string (current_file_name);
+ if (!name)
+ name = current_file_name;
+ fprintf (msg_file, "%s %s/%s %*s%s %s %s %s",
+ modes,
+ user,
+ group,
+ ugswidth - pad,
+ "",
+ size,
+ timestamp + 4, timestamp + 20,
+ name);
+
+ if (name != current_file_name)
+ free (name);
+ switch (head->header.linkflag)
+ {
+ case LF_SYMLINK:
+ name = quote_copy_string (current_link_name);
+ if (!name)
+ name = current_link_name;
+ fprintf (msg_file, " -> %s\n", name);
+ if (name != current_link_name)
+ free (name);
+ break;
+
+ case LF_LINK:
+ name = quote_copy_string (current_link_name);
+ if (!name)
+ name = current_link_name;
+ fprintf (msg_file, " link to %s\n", current_link_name);
+ if (name != current_link_name)
+ free (name);
+ break;
+
+ default:
+ fprintf (msg_file, " unknown file type '%c'\n",
+ head->header.linkflag);
+ break;
+
+ case LF_OLDNORMAL:
+ case LF_NORMAL:
+ case LF_SPARSE:
+ case LF_CHR:
+ case LF_BLK:
+ case LF_DIR:
+ case LF_FIFO:
+ case LF_CONTIG:
+ case LF_DUMPDIR:
+ putc ('\n', msg_file);
+ break;
+
+ case LF_VOLHDR:
+ fprintf (msg_file, "--Volume Header--\n");
+ break;
+
+ case LF_MULTIVOL:
+ fprintf (msg_file, "--Continued at byte %ld--\n", from_oct (1 + 12, head->header.offset));
+ break;
+
+ case LF_NAMES:
+ fprintf (msg_file, "--Mangled file names--\n");
+ break;
+ }
+ }
+ fflush (msg_file);
+}
+
+/*
+ * Print a similar line when we make a directory automatically.
+ */
+void
+pr_mkdir (pathname, length, mode)
+ char *pathname;
+ int length;
+ int mode;
+{
+ char modes[11];
+ char *name;
+ extern long baserec;
+
+ if (f_verbose > 1)
+ {
+ /* File type and modes */
+ modes[0] = 'd';
+ demode ((unsigned) mode, modes + 1);
+
+ if (f_sayblock)
+ fprintf (msg_file, "rec %10d: ", baserec + (ar_record - ar_block));
+ /* annofile(msg_file, (char *)NULL); */
+ name = quote_copy_string (pathname);
+ if (!name)
+ name = pathname;
+ fprintf (msg_file, "%s %*s %.*s\n",
+ modes,
+ ugswidth + DATEWIDTH,
+ "Creating directory:",
+ length,
+ pathname);
+ if (name != pathname)
+ free (name);
+ }
+}
+
+
+/*
+ * Skip over <size> bytes of data in records in the archive.
+ */
+void
+skip_file (size)
+ register long size;
+{
+ union record *x;
+ extern long save_totsize;
+ extern long save_sizeleft;
+
+ if (f_multivol)
+ {
+ save_totsize = size;
+ save_sizeleft = size;
+ }
+
+ while (size > 0)
+ {
+ x = findrec ();
+ if (x == NULL)
+ { /* Check it... */
+ msg ("Unexpected EOF on archive file");
+ exit (EX_BADARCH);
+ }
+ userec (x);
+ size -= RECORDSIZE;
+ if (f_multivol)
+ save_sizeleft -= RECORDSIZE;
+ }
+}
+
+void
+skip_extended_headers ()
+{
+ register union record *exhdr;
+
+ for (;;)
+ {
+ exhdr = findrec ();
+ if (!exhdr->ext_hdr.isextended)
+ {
+ userec (exhdr);
+ break;
+ }
+ userec (exhdr);
+ }
+}
+
+/*
+ * Decode the mode string from a stat entry into a 9-char string and a null.
+ */
+void
+demode (mode, string)
+ register unsigned mode;
+ register char *string;
+{
+ register unsigned mask;
+ register char *rwx = "rwxrwxrwx";
+
+ for (mask = 0400; mask != 0; mask >>= 1)
+ {
+ if (mode & mask)
+ *string++ = *rwx++;
+ else
+ {
+ *string++ = '-';
+ rwx++;
+ }
+ }
+
+ if (mode & S_ISUID)
+ if (string[-7] == 'x')
+ string[-7] = 's';
+ else
+ string[-7] = 'S';
+ if (mode & S_ISGID)
+ if (string[-4] == 'x')
+ string[-4] = 's';
+ else
+ string[-4] = 'S';
+ if (mode & S_ISVTX)
+ if (string[-1] == 'x')
+ string[-1] = 't';
+ else
+ string[-1] = 'T';
+ *string = '\0';
+}
diff --git a/gnu/usr.bin/tar/mangle.c b/gnu/usr.bin/tar/mangle.c
new file mode 100644
index 0000000..6281684
--- /dev/null
+++ b/gnu/usr.bin/tar/mangle.c
@@ -0,0 +1,270 @@
+/* mangle.c -- encode long filenames
+ Copyright (C) 1988, 1992 Free Software Foundation
+
+This file is part of GNU Tar.
+
+GNU Tar is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Tar is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Tar; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <time.h>
+time_t time ();
+
+#include "tar.h"
+#include "port.h"
+
+void add_buffer ();
+extern PTR ck_malloc ();
+void finish_header ();
+extern PTR init_buffer ();
+extern char *quote_copy_string ();
+extern char *get_buffer ();
+char *un_quote_string ();
+
+extern union record *start_header ();
+
+extern struct stat hstat; /* Stat struct corresponding */
+
+struct mangled
+ {
+ struct mangled *next;
+ int type;
+ char mangled[NAMSIZ];
+ char *linked_to;
+ char normal[1];
+ };
+
+
+/* Should use a hash table, etc. . */
+struct mangled *first_mangle;
+int mangled_num = 0;
+
+#if 0 /* Deleted because there is now a better way to do all this */
+
+char *
+find_mangled (name)
+ char *name;
+{
+ struct mangled *munge;
+
+ for (munge = first_mangle; munge; munge = munge->next)
+ if (!strcmp (name, munge->normal))
+ return munge->mangled;
+ return 0;
+}
+
+
+#ifdef S_ISLNK
+void
+add_symlink_mangle (symlink, linkto, buffer)
+ char *symlink;
+ char *linkto;
+ char *buffer;
+{
+ struct mangled *munge, *kludge;
+
+ munge = (struct mangled *) ck_malloc (sizeof (struct mangled) + strlen (symlink) + strlen (linkto) + 2);
+ if (!first_mangle)
+ first_mangle = munge;
+ else
+ {
+ for (kludge = first_mangle; kludge->next; kludge = kludge->next)
+ ;
+ kludge->next = munge;
+ }
+ munge->type = 1;
+ munge->next = 0;
+ strcpy (munge->normal, symlink);
+ munge->linked_to = munge->normal + strlen (symlink) + 1;
+ strcpy (munge->linked_to, linkto);
+ sprintf (munge->mangled, "@@MaNgLeD.%d", mangled_num++);
+ strncpy (buffer, munge->mangled, NAMSIZ);
+}
+
+#endif
+
+void
+add_mangle (name, buffer)
+ char *name;
+ char *buffer;
+{
+ struct mangled *munge, *kludge;
+
+ munge = (struct mangled *) ck_malloc (sizeof (struct mangled) + strlen (name));
+ if (!first_mangle)
+ first_mangle = munge;
+ else
+ {
+ for (kludge = first_mangle; kludge->next; kludge = kludge->next)
+ ;
+ kludge->next = munge;
+ }
+ munge->next = 0;
+ munge->type = 0;
+ strcpy (munge->normal, name);
+ sprintf (munge->mangled, "@@MaNgLeD.%d", mangled_num++);
+ strncpy (buffer, munge->mangled, NAMSIZ);
+}
+
+void
+write_mangled ()
+{
+ struct mangled *munge;
+ struct stat hstat;
+ union record *header;
+ char *ptr1, *ptr2;
+ PTR the_buffer;
+ int size;
+ int bufsize;
+
+ if (!first_mangle)
+ return;
+ the_buffer = init_buffer ();
+ for (munge = first_mangle, size = 0; munge; munge = munge->next)
+ {
+ ptr1 = quote_copy_string (munge->normal);
+ if (!ptr1)
+ ptr1 = munge->normal;
+ if (munge->type)
+ {
+ add_buffer (the_buffer, "Symlink ", 8);
+ add_buffer (the_buffer, ptr1, strlen (ptr1));
+ add_buffer (the_buffer, " to ", 4);
+
+ if (ptr2 = quote_copy_string (munge->linked_to))
+ {
+ add_buffer (the_buffer, ptr2, strlen (ptr2));
+ free (ptr2);
+ }
+ else
+ add_buffer (the_buffer, munge->linked_to, strlen (munge->linked_to));
+ }
+ else
+ {
+ add_buffer (the_buffer, "Rename ", 7);
+ add_buffer (the_buffer, munge->mangled, strlen (munge->mangled));
+ add_buffer (the_buffer, " to ", 4);
+ add_buffer (the_buffer, ptr1, strlen (ptr1));
+ }
+ add_buffer (the_buffer, "\n", 1);
+ if (ptr1 != munge->normal)
+ free (ptr1);
+ }
+
+ bzero (&hstat, sizeof (struct stat));
+ hstat.st_atime = hstat.st_mtime = hstat.st_ctime = time (0);
+ ptr1 = get_buffer (the_buffer);
+ hstat.st_size = strlen (ptr1);
+
+ header = start_header ("././@MaNgLeD_NaMeS", &hstat);
+ header->header.linkflag = LF_NAMES;
+ finish_header (header);
+ size = hstat.st_size;
+ header = findrec ();
+ bufsize = endofrecs ()->charptr - header->charptr;
+
+ while (bufsize < size)
+ {
+ bcopy (ptr1, header->charptr, bufsize);
+ ptr1 += bufsize;
+ size -= bufsize;
+ userec (header + (bufsize - 1) / RECORDSIZE);
+ header = findrec ();
+ bufsize = endofrecs ()->charptr - header->charptr;
+ }
+ bcopy (ptr1, header->charptr, size);
+ bzero (header->charptr + size, bufsize - size);
+ userec (header + (size - 1) / RECORDSIZE);
+}
+
+#endif
+
+void
+extract_mangle (head)
+ union record *head;
+{
+ char *buf;
+ char *fromtape;
+ char *to;
+ char *ptr, *ptrend;
+ char *nam1, *nam1end;
+ int size;
+ int copied;
+
+ size = hstat.st_size;
+ buf = to = ck_malloc (size + 1);
+ buf[size] = '\0';
+ while (size > 0)
+ {
+ fromtape = findrec ()->charptr;
+ if (fromtape == 0)
+ {
+ msg ("Unexpected EOF in mangled names!");
+ return;
+ }
+ copied = endofrecs ()->charptr - fromtape;
+ if (copied > size)
+ copied = size;
+ bcopy (fromtape, to, copied);
+ to += copied;
+ size -= copied;
+ userec ((union record *) (fromtape + copied - 1));
+ }
+ for (ptr = buf; *ptr; ptr = ptrend)
+ {
+ ptrend = index (ptr, '\n');
+ *ptrend++ = '\0';
+
+ if (!strncmp (ptr, "Rename ", 7))
+ {
+ nam1 = ptr + 7;
+ nam1end = index (nam1, ' ');
+ while (strncmp (nam1end, " to ", 4))
+ {
+ nam1end++;
+ nam1end = index (nam1end, ' ');
+ }
+ *nam1end = '\0';
+ if (ptrend[-2] == '/')
+ ptrend[-2] = '\0';
+ un_quote_string (nam1end + 4);
+ if (rename (nam1, nam1end + 4))
+ msg_perror ("Can't rename %s to %s", nam1, nam1end + 4);
+ else if (f_verbose)
+ msg ("Renamed %s to %s", nam1, nam1end + 4);
+ }
+#ifdef S_ISLNK
+ else if (!strncmp (ptr, "Symlink ", 8))
+ {
+ nam1 = ptr + 8;
+ nam1end = index (nam1, ' ');
+ while (strncmp (nam1end, " to ", 4))
+ {
+ nam1end++;
+ nam1end = index (nam1end, ' ');
+ }
+ *nam1end = '\0';
+ un_quote_string (nam1);
+ un_quote_string (nam1end + 4);
+ if (symlink (nam1, nam1end + 4) && (unlink (nam1end + 4) || symlink (nam1, nam1end + 4)))
+ msg_perror ("Can't symlink %s to %s", nam1, nam1end + 4);
+ else if (f_verbose)
+ msg ("Symlinkd %s to %s", nam1, nam1end + 4);
+ }
+#endif
+ else
+ msg ("Unknown demangling command %s", ptr);
+ }
+}
diff --git a/gnu/usr.bin/tar/msd_dir.h b/gnu/usr.bin/tar/msd_dir.h
new file mode 100644
index 0000000..06c7a64
--- /dev/null
+++ b/gnu/usr.bin/tar/msd_dir.h
@@ -0,0 +1,44 @@
+/*
+ * @(#)msd_dir.h 1.4 87/11/06 Public Domain.
+ *
+ * A public domain implementation of BSD directory routines for
+ * MS-DOS. Written by Michael Rendell ({uunet,utai}michael@garfield),
+ * August 1897
+ */
+
+#define rewinddir(dirp) seekdir(dirp, 0L)
+
+#define MAXNAMLEN 12
+
+#ifdef __TURBOC__
+typedef int ino_t;
+typedef int dev_t;
+#endif
+
+struct dirent
+ {
+ ino_t d_ino; /* a bit of a farce */
+ int d_reclen; /* more farce */
+ int d_namlen; /* length of d_name */
+ char d_name[MAXNAMLEN + 1]; /* garentee null termination */
+ };
+
+struct _dircontents
+ {
+ char *_d_entry;
+ struct _dircontents *_d_next;
+ };
+
+typedef struct _dirdesc
+ {
+ int dd_id; /* uniquely identify each open directory */
+ long dd_loc; /* where we are in directory entry is this */
+ struct _dircontents *dd_contents; /* pointer to contents of dir */
+ struct _dircontents *dd_cp; /* pointer to current position */
+ } DIR;
+
+extern DIR *opendir ();
+extern struct dirent *readdir ();
+extern void seekdir ();
+extern long telldir ();
+extern void closedir ();
diff --git a/gnu/usr.bin/tar/names.c b/gnu/usr.bin/tar/names.c
new file mode 100644
index 0000000..0de6a88
--- /dev/null
+++ b/gnu/usr.bin/tar/names.c
@@ -0,0 +1,149 @@
+/* Look up user and/or group names.
+ Copyright (C) 1988, 1992 Free Software Foundation
+
+This file is part of GNU Tar.
+
+GNU Tar is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Tar is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Tar; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * Look up user and/or group names.
+ *
+ * This file should be modified for non-unix systems to do something
+ * reasonable.
+ */
+
+#include <sys/types.h>
+#include "tar.h"
+#include "port.h"
+
+#ifndef NONAMES
+/* Whole module goes away if NONAMES defined. Otherwise... */
+#include <stdio.h>
+#include <pwd.h>
+#include <grp.h>
+
+static int saveuid = -993;
+static char saveuname[TUNMLEN];
+static int my_uid = -993;
+
+static int savegid = -993;
+static char savegname[TGNMLEN];
+static int my_gid = -993;
+
+#define myuid ( my_uid < 0? (my_uid = getuid()): my_uid )
+#define mygid ( my_gid < 0? (my_gid = getgid()): my_gid )
+
+/*
+ * Look up a user or group name from a uid/gid, maintaining a cache.
+ * FIXME, for now it's a one-entry cache.
+ * FIXME2, the "-993" is to reduce the chance of a hit on the first lookup.
+ *
+ * This is ifdef'd because on Suns, it drags in about 38K of "yellow
+ * pages" code, roughly doubling the program size. Thanks guys.
+ */
+void
+finduname (uname, uid)
+ char uname[TUNMLEN];
+ int uid;
+{
+ struct passwd *pw;
+#ifndef HAVE_GETPWUID
+ extern struct passwd *getpwuid ();
+#endif
+
+ if (uid != saveuid)
+ {
+ saveuid = uid;
+ saveuname[0] = '\0';
+ pw = getpwuid (uid);
+ if (pw)
+ strncpy (saveuname, pw->pw_name, TUNMLEN);
+ }
+ strncpy (uname, saveuname, TUNMLEN);
+}
+
+int
+finduid (uname)
+ char uname[TUNMLEN];
+{
+ struct passwd *pw;
+ extern struct passwd *getpwnam ();
+
+ if (uname[0] != saveuname[0] /* Quick test w/o proc call */
+ || 0 != strncmp (uname, saveuname, TUNMLEN))
+ {
+ strncpy (saveuname, uname, TUNMLEN);
+ pw = getpwnam (uname);
+ if (pw)
+ {
+ saveuid = pw->pw_uid;
+ }
+ else
+ {
+ saveuid = myuid;
+ }
+ }
+ return saveuid;
+}
+
+
+void
+findgname (gname, gid)
+ char gname[TGNMLEN];
+ int gid;
+{
+ struct group *gr;
+#ifndef HAVE_GETGRGID
+ extern struct group *getgrgid ();
+#endif
+
+ if (gid != savegid)
+ {
+ savegid = gid;
+ savegname[0] = '\0';
+ (void) setgrent ();
+ gr = getgrgid (gid);
+ if (gr)
+ strncpy (savegname, gr->gr_name, TGNMLEN);
+ }
+ (void) strncpy (gname, savegname, TGNMLEN);
+}
+
+
+int
+findgid (gname)
+ char gname[TUNMLEN];
+{
+ struct group *gr;
+ extern struct group *getgrnam ();
+
+ if (gname[0] != savegname[0] /* Quick test w/o proc call */
+ || 0 != strncmp (gname, savegname, TUNMLEN))
+ {
+ strncpy (savegname, gname, TUNMLEN);
+ gr = getgrnam (gname);
+ if (gr)
+ {
+ savegid = gr->gr_gid;
+ }
+ else
+ {
+ savegid = mygid;
+ }
+ }
+ return savegid;
+}
+
+#endif
diff --git a/gnu/usr.bin/tar/open3.h b/gnu/usr.bin/tar/open3.h
new file mode 100644
index 0000000..c1c0e59
--- /dev/null
+++ b/gnu/usr.bin/tar/open3.h
@@ -0,0 +1,67 @@
+/* Defines for Sys V style 3-argument open call.
+ Copyright (C) 1988 Free Software Foundation
+
+This file is part of GNU Tar.
+
+GNU Tar is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Tar is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Tar; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * open3.h -- #defines for the various flags for the Sys V style 3-argument
+ * open() call. On BSD or System 5, the system already has this in an
+ * include file. This file is needed for V7 and MINIX systems for the
+ * benefit of open3() in port.c, a routine that emulates the 3-argument
+ * call using system calls available on V7/MINIX.
+ *
+ * This file is needed by PD tar even if we aren't using the
+ * emulator, since the #defines for O_WRONLY, etc. are used in
+ * a couple of places besides the open() calls, (e.g. in the assignment
+ * to openflag in extract.c). We just #include this rather than
+ * #ifdef them out.
+ *
+ * Written 6/10/87 by rmtodd@uokmax (Richard Todd).
+ *
+ * The names have been changed by John Gilmore, 31 July 1987, since
+ * Richard called it "bsdopen", and really this change was introduced in
+ * AT&T Unix systems before BSD picked it up.
+ */
+
+/* Only one of the next three should be specified */
+#define O_RDONLY 0 /* only allow read */
+#define O_WRONLY 1 /* only allow write */
+#define O_RDWR 2 /* both are allowed */
+
+/* The rest of these can be OR-ed in to the above. */
+/*
+ * O_NDELAY isn't implemented by the emulator. It's only useful (to tar) on
+ * systems that have named pipes anyway; it prevents tar's hanging by
+ * opening a named pipe. We #ifndef it because some systems already have
+ * it defined.
+ */
+#ifndef O_NDELAY
+#define O_NDELAY 4 /* don't block on opening devices that would
+ * block on open -- ignored by emulator. */
+#endif
+#define O_CREAT 8 /* create file if needed */
+#define O_EXCL 16 /* file cannot already exist */
+#define O_TRUNC 32 /* truncate file on open */
+#define O_APPEND 64 /* always write at end of file -- ignored by emul */
+
+#ifdef EMUL_OPEN3
+/*
+ * make emulation transparent to rest of file -- redirect all open() calls
+ * to our routine
+ */
+#define open open3
+#endif
diff --git a/gnu/usr.bin/tar/pathmax.h b/gnu/usr.bin/tar/pathmax.h
new file mode 100644
index 0000000..aeba9f7
--- /dev/null
+++ b/gnu/usr.bin/tar/pathmax.h
@@ -0,0 +1,53 @@
+/* Define PATH_MAX somehow. Requires sys/types.h.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _PATHMAX_H
+#define _PATHMAX_H
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
+ PATH_MAX but might cause redefinition warnings when sys/param.h is
+ later included (as on MORE/BSD 4.3). */
+#if defined(_POSIX_VERSION) || (defined(HAVE_LIMITS_H) && defined(USG))
+#include <limits.h>
+#endif
+
+#ifndef _POSIX_PATH_MAX
+#define _POSIX_PATH_MAX 255
+#endif
+
+#if !defined(PATH_MAX) && defined(_PC_PATH_MAX)
+#define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
+#endif
+
+/* Don't include sys/param.h if it already has been. */
+#if !defined(PATH_MAX) && !defined(MAXPATHLEN) && !defined(__MSDOS__)
+#include <sys/param.h>
+#endif
+
+#if !defined(PATH_MAX) && defined(MAXPATHLEN)
+#define PATH_MAX MAXPATHLEN
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX _POSIX_PATH_MAX
+#endif
+
+#endif /* _PATHMAX_H */
diff --git a/gnu/usr.bin/tar/port.c b/gnu/usr.bin/tar/port.c
new file mode 100644
index 0000000..10ec32e
--- /dev/null
+++ b/gnu/usr.bin/tar/port.c
@@ -0,0 +1,1256 @@
+/* Supporting routines which may sometimes be missing.
+ Copyright (C) 1988, 1992 Free Software Foundation
+
+This file is part of GNU Tar.
+
+GNU Tar is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Tar is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Tar; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+
+#ifdef BSD42
+#include <sys/file.h>
+#else
+#ifndef V7
+#include <fcntl.h>
+#endif
+#endif
+
+#include "tar.h"
+#include "port.h"
+
+extern long baserec;
+
+/* All machine-dependent #ifdefs should appear here, instead of
+ being scattered through the file. For UN*X systems, it is better to
+ figure out what is needed in the configure script, for most of the
+ features. */
+
+#ifdef __MSDOS__
+char TTY_NAME[] = "con";
+#define HAVE_STRSTR
+#define HAVE_RENAME
+#define HAVE_MKDIR
+#else
+char TTY_NAME[] = "/dev/tty";
+#endif
+
+/* End of system-dependent #ifdefs */
+
+
+#ifndef HAVE_VALLOC
+/*
+ * valloc() does a malloc() on a page boundary. On some systems,
+ * this can make large block I/O more efficient.
+ */
+char *
+valloc (size)
+ unsigned size;
+{
+ return (malloc (size));
+}
+
+#endif /* !HAVE_VALLOC */
+
+#ifndef HAVE_MKDIR
+/*
+ * Written by Robert Rother, Mariah Corporation, August 1985.
+ *
+ * If you want it, it's yours. All I ask in return is that if you
+ * figure out how to do this in a Bourne Shell script you send me
+ * a copy.
+ * sdcsvax!rmr or rmr@uscd
+ *
+ * Severely hacked over by John Gilmore to make a 4.2BSD compatible
+ * subroutine. 11Mar86; hoptoad!gnu
+ *
+ * Modified by rmtodd@uokmax 6-28-87 -- when making an already existing dir,
+ * subroutine didn't return EEXIST. It does now.
+ */
+
+/*
+ * Make a directory.
+ */
+int
+mkdir (dpath, dmode)
+ char *dpath;
+ int dmode;
+{
+ int cpid, status;
+ struct stat statbuf;
+
+ if (stat (dpath, &statbuf) == 0)
+ {
+ errno = EEXIST; /* Stat worked, so it already exists */
+ return -1;
+ }
+
+ /* If stat fails for a reason other than non-existence, return error */
+ if (errno != ENOENT)
+ return -1;
+
+ switch (cpid = fork ())
+ {
+
+ case -1: /* Error in fork() */
+ return (-1); /* Errno is set already */
+
+ case 0: /* Child process */
+ /*
+ * Cheap hack to set mode of new directory. Since this
+ * child process is going away anyway, we zap its umask.
+ * FIXME, this won't suffice to set SUID, SGID, etc. on this
+ * directory. Does anybody care?
+ */
+ status = umask (0); /* Get current umask */
+ status = umask (status | (0777 & ~dmode)); /* Set for mkdir */
+ execl ("/bin/mkdir", "mkdir", dpath, (char *) 0);
+ _exit (-1); /* Can't exec /bin/mkdir */
+
+ default: /* Parent process */
+ while (cpid != wait (&status)); /* Wait for kid to finish */
+ }
+
+ if (WIFSIGNALED (status) || WEXITSTATUS (status) != 0)
+ {
+ errno = EIO; /* We don't know why, but */
+ return -1; /* /bin/mkdir failed */
+ }
+
+ return 0;
+}
+
+int
+rmdir (dpath)
+ char *dpath;
+{
+ int cpid, status;
+ struct stat statbuf;
+
+ if (stat (dpath, &statbuf) != 0)
+ {
+ /* Stat just set errno. We don't have to */
+ return -1;
+ }
+
+ switch (cpid = fork ())
+ {
+
+ case -1: /* Error in fork() */
+ return (-1); /* Errno is set already */
+
+ case 0: /* Child process */
+ execl ("/bin/rmdir", "rmdir", dpath, (char *) 0);
+ _exit (-1); /* Can't exec /bin/mkdir */
+
+ default: /* Parent process */
+ while (cpid != wait (&status)); /* Wait for kid to finish */
+ }
+
+ if (WIFSIGNALED (status) || WEXITSTATUS (status) != 0)
+ {
+ errno = EIO; /* We don't know why, but */
+ return -1; /* /bin/mkdir failed */
+ }
+
+ return 0;
+}
+
+#endif /* !HAVE_MKDIR */
+
+#ifndef HAVE_RENAME
+/* Rename file FROM to file TO.
+ Return 0 if successful, -1 if not. */
+
+int
+rename (from, to)
+ char *from;
+ char *to;
+{
+ struct stat from_stats;
+
+ if (stat (from, &from_stats))
+ return -1;
+
+ if (unlink (to) && errno != ENOENT)
+ return -1;
+
+ if (link (from, to))
+ return -1;
+
+ if (unlink (from) && errno != ENOENT)
+ {
+ unlink (to);
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif /* !HAVE_RENAME */
+
+#ifdef minix
+/* Minix has bcopy but not bzero, and no memset. Thanks, Andy. */
+void
+bzero (s1, n)
+ register char *s1;
+ register int n;
+{
+ while (n--)
+ *s1++ = '\0';
+}
+
+/* It also has no bcmp() */
+int
+bcmp (s1, s2, n)
+ register char *s1, *s2;
+ register int n;
+{
+ for (; n--; ++s1, ++s2)
+ {
+ if (*s1 != *s2)
+ return *s1 - *s2;
+ }
+ return 0;
+}
+
+/*
+ * Groan, Minix doesn't have execlp either!
+ *
+ * execlp(file,arg0,arg1...argn,(char *)NULL)
+ * exec a program, automatically searching for the program through
+ * all the directories on the PATH.
+ *
+ * This version is naive about variable argument lists, it assumes
+ * a straightforward C calling sequence. If your system has odd stacks
+ * *and* doesn't have execlp, YOU get to fix it.
+ */
+int
+execlp (filename, arg0)
+ char *filename, *arg0;
+{
+ register char *p, *path;
+ register char *fnbuffer;
+ char **argstart = &arg0;
+ struct stat statbuf;
+ extern char **environ;
+
+ if ((p = getenv ("PATH")) == NULL)
+ {
+ /* couldn't find path variable -- try to exec given filename */
+ return execve (filename, argstart, environ);
+ }
+
+ /*
+ * make a place to build the filename. We malloc larger than we
+ * need, but we know it will fit in this.
+ */
+ fnbuffer = malloc (strlen (p) + 1 + strlen (filename));
+ if (fnbuffer == NULL)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /*
+ * try each component of the path to see if the file's there
+ * and executable.
+ */
+ for (path = p; path; path = p)
+ {
+ /* construct full path name to try */
+ if ((p = index (path, ':')) == NULL)
+ {
+ strcpy (fnbuffer, path);
+ }
+ else
+ {
+ strncpy (fnbuffer, path, p - path);
+ fnbuffer[p - path] = '\0';
+ p++; /* Skip : for next time */
+ }
+ if (strlen (fnbuffer) != 0)
+ strcat (fnbuffer, "/");
+ strcat (fnbuffer, filename);
+
+ /* check to see if file is there and is a normal file */
+ if (stat (fnbuffer, &statbuf) < 0)
+ {
+ if (errno == ENOENT)
+ continue; /* file not there,keep on looking */
+ else
+ goto fail; /* failed for some reason, return */
+ }
+ if (!S_ISREG (statbuf.st_mode))
+ continue;
+
+ if (execve (fnbuffer, argstart, environ) < 0
+ && errno != ENOENT
+ && errno != ENOEXEC)
+ {
+ /* failed, for some other reason besides "file
+ * not found" or "not a.out format"
+ */
+ goto fail;
+ }
+
+ /*
+ * If we got error ENOEXEC, the file is executable but is
+ * not an object file. Try to execute it as a shell script,
+ * returning error if we can't execute /bin/sh.
+ *
+ * FIXME, this code is broken in several ways. Shell
+ * scripts should not in general be executed by the user's
+ * SHELL variable program. On more mature systems, the
+ * script can specify with #!/bin/whatever. Also, this
+ * code clobbers argstart[-1] if the exec of the shell
+ * fails.
+ */
+ if (errno == ENOEXEC)
+ {
+ char *shell;
+
+ /* Try to execute command "sh arg0 arg1 ..." */
+ if ((shell = getenv ("SHELL")) == NULL)
+ shell = "/bin/sh";
+ argstart[-1] = shell;
+ argstart[0] = fnbuffer;
+ execve (shell, &argstart[-1], environ);
+ goto fail; /* Exec didn't work */
+ }
+
+ /*
+ * If we succeeded, the execve() doesn't return, so we
+ * can only be here is if the file hasn't been found yet.
+ * Try the next place on the path.
+ */
+ }
+
+ /* all attempts failed to locate the file. Give up. */
+ errno = ENOENT;
+
+fail:
+ free (fnbuffer);
+ return -1;
+}
+
+#endif /* minix */
+
+
+#ifdef EMUL_OPEN3
+#include "open3.h"
+/*
+ * open3 -- routine to emulate the 3-argument open system
+ * call that is present in most modern Unix systems.
+ * This version attempts to support all the flag bits except for O_NDELAY
+ * and O_APPEND, which are silently ignored. The emulation is not as efficient
+ * as the real thing (at worst, 4 system calls instead of one), but there's
+ * not much I can do about that.
+ *
+ * Written 6/10/87 by rmtodd@uokmax
+ *
+ * open3(path, flag, mode)
+ * Attempts to open the file specified by
+ * the given pathname. The following flag bits (#defined in tar.h)
+ * specify options to the routine:
+ * O_RDONLY file open for read only
+ * O_WRONLY file open for write only
+ * O_RDWR file open for both read & write
+ * (Needless to say, you should only specify one of the above).
+ * O_CREAT file is created with specified mode if it needs to be.
+ * O_TRUNC if file exists, it is truncated to 0 bytes
+ * O_EXCL used with O_CREAT--routine returns error if file exists
+ * Function returns file descriptor if successful, -1 and errno if not.
+ */
+
+/*
+ * array to give arguments to access for various modes
+ * FIXME, this table depends on the specific integer values of O_XXX,
+ * and also contains integers (args to 'access') that should be #define's.
+ */
+static int modes[] =
+{
+ 04, /* O_RDONLY */
+ 02, /* O_WRONLY */
+ 06, /* O_RDWR */
+ 06, /* invalid but we'd better cope -- O_WRONLY+O_RDWR */
+};
+
+/* Shut off the automatic emulation of open(), we'll need it. */
+#undef open
+
+int
+open3 (path, flags, mode)
+ char *path;
+ int flags, mode;
+{
+ int exists = 1;
+ int call_creat = 0;
+ int fd;
+ /*
+ * We actually do the work by calling the open() or creat() system
+ * call, depending on the flags. Call_creat is true if we will use
+ * creat(), false if we will use open().
+ */
+
+ /*
+ * See if the file exists and is accessible in the requested mode.
+ *
+ * Strictly speaking we shouldn't be using access, since access checks
+ * against real uid, and the open call should check against euid.
+ * Most cases real uid == euid, so it won't matter. FIXME.
+ * FIXME, the construction "flags & 3" and the modes table depends
+ * on the specific integer values of the O_XXX #define's. Foo!
+ */
+ if (access (path, modes[flags & 3]) < 0)
+ {
+ if (errno == ENOENT)
+ {
+ /* the file does not exist */
+ exists = 0;
+ }
+ else
+ {
+ /* probably permission violation */
+ if (flags & O_EXCL)
+ {
+ /* Oops, the file exists, we didn't want it. */
+ /* No matter what the error, claim EEXIST. */
+ errno = EEXIST;
+ }
+ return -1;
+ }
+ }
+
+ /* if we have the O_CREAT bit set, check for O_EXCL */
+ if (flags & O_CREAT)
+ {
+ if ((flags & O_EXCL) && exists)
+ {
+ /* Oops, the file exists and we didn't want it to. */
+ errno = EEXIST;
+ return -1;
+ }
+ /*
+ * If the file doesn't exist, be sure to call creat() so that
+ * it will be created with the proper mode.
+ */
+ if (!exists)
+ call_creat = 1;
+ }
+ else
+ {
+ /* If O_CREAT isn't set and the file doesn't exist, error. */
+ if (!exists)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ /*
+ * If the O_TRUNC flag is set and the file exists, we want to call
+ * creat() anyway, since creat() guarantees that the file will be
+ * truncated and open()-for-writing doesn't.
+ * (If the file doesn't exist, we're calling creat() anyway and the
+ * file will be created with zero length.)
+ */
+ if ((flags & O_TRUNC) && exists)
+ call_creat = 1;
+ /* actually do the call */
+ if (call_creat)
+ {
+ /*
+ * call creat. May have to close and reopen the file if we
+ * want O_RDONLY or O_RDWR access -- creat() only gives
+ * O_WRONLY.
+ */
+ fd = creat (path, mode);
+ if (fd < 0 || (flags & O_WRONLY))
+ return fd;
+ if (close (fd) < 0)
+ return -1;
+ /* Fall out to reopen the file we've created */
+ }
+
+ /*
+ * calling old open, we strip most of the new flags just in case.
+ */
+ return open (path, flags & (O_RDONLY | O_WRONLY | O_RDWR | O_BINARY));
+}
+
+#endif /* EMUL_OPEN3 */
+
+#ifndef HAVE_MKNOD
+#ifdef __MSDOS__
+typedef int dev_t;
+#endif
+/* Fake mknod by complaining */
+int
+mknod (path, mode, dev)
+ char *path;
+ unsigned short mode;
+ dev_t dev;
+{
+ int fd;
+
+ errno = ENXIO; /* No such device or address */
+ return -1; /* Just give an error */
+}
+
+/* Fake links by copying */
+int
+link (path1, path2)
+ char *path1;
+ char *path2;
+{
+ char buf[256];
+ int ifd, ofd;
+ int nrbytes;
+ int nwbytes;
+
+ fprintf (stderr, "%s: %s: cannot link to %s, copying instead\n",
+ tar, path1, path2);
+ if ((ifd = open (path1, O_RDONLY | O_BINARY)) < 0)
+ return -1;
+ if ((ofd = creat (path2, 0666)) < 0)
+ return -1;
+ setmode (ofd, O_BINARY);
+ while ((nrbytes = read (ifd, buf, sizeof (buf))) > 0)
+ {
+ if ((nwbytes = write (ofd, buf, nrbytes)) != nrbytes)
+ {
+ nrbytes = -1;
+ break;
+ }
+ }
+ /* Note use of "|" rather than "||" below: we want to close
+ * the files even if an error occurs.
+ */
+ if ((nrbytes < 0) | (0 != close (ifd)) | (0 != close (ofd)))
+ {
+ unlink (path2);
+ return -1;
+ }
+ return 0;
+}
+
+/* everyone owns everything on MS-DOS (or is it no one owns anything?) */
+int
+chown (path, uid, gid)
+ char *path;
+ int uid;
+ int gid;
+{
+ return 0;
+}
+
+int
+geteuid ()
+{
+ return 0;
+}
+
+#endif /* !HAVE_MKNOD */
+
+#ifdef __TURBOC__
+#include <time.h>
+#include <fcntl.h>
+#include <io.h>
+
+struct utimbuf
+{
+ time_t actime; /* Access time. */
+ time_t modtime; /* Modification time. */
+};
+
+int
+utime (char *filename, struct utimbuf *utb)
+{
+ struct tm *tm;
+ struct ftime filetime;
+ time_t when;
+ int fd;
+ int status;
+
+ if (utb == 0)
+ when = time (0);
+ else
+ when = utb->modtime;
+
+ fd = _open (filename, O_RDWR);
+ if (fd == -1)
+ return -1;
+
+ tm = localtime (&when);
+ if (tm->tm_year < 80)
+ filetime.ft_year = 0;
+ else
+ filetime.ft_year = tm->tm_year - 80;
+ filetime.ft_month = tm->tm_mon + 1;
+ filetime.ft_day = tm->tm_mday;
+ if (tm->tm_hour < 0)
+ filetime.ft_hour = 0;
+ else
+ filetime.ft_hour = tm->tm_hour;
+ filetime.ft_min = tm->tm_min;
+ filetime.ft_tsec = tm->tm_sec / 2;
+
+ status = setftime (fd, &filetime);
+ _close (fd);
+ return status;
+}
+
+#endif /* __TURBOC__ */
+
+/* Stash argv[0] here so panic will know what the program is called */
+char *myname = 0;
+
+void
+panic (s)
+ char *s;
+{
+ if (myname)
+ fprintf (stderr, "%s:", myname);
+ fprintf (stderr, s);
+ putc ('\n', stderr);
+ exit (12);
+}
+
+
+PTR
+ck_malloc (size)
+ size_t size;
+{
+ PTR ret;
+
+ if (!size)
+ size++;
+ ret = malloc (size);
+ if (ret == 0)
+ panic ("Couldn't allocate memory");
+ return ret;
+}
+
+/* Used by alloca.c and bison.simple. */
+char *
+xmalloc (size)
+ size_t size;
+{
+ return (char *) ck_malloc (size);
+}
+
+PTR
+ck_realloc (ptr, size)
+ PTR ptr;
+ size_t size;
+{
+ PTR ret;
+
+ if (!ptr)
+ ret = ck_malloc (size);
+ else
+ ret = realloc (ptr, size);
+ if (ret == 0)
+ panic ("Couldn't re-allocate memory");
+ return ret;
+}
+
+/* Implement a variable sized buffer of 'stuff'. We don't know what it is,
+ nor do we care, as long as it doesn't mind being aligned on a char boundry.
+ */
+
+struct buffer
+ {
+ int allocated;
+ int length;
+ char *b;
+ };
+
+#define MIN_ALLOCATE 50
+
+char *
+init_buffer ()
+{
+ struct buffer *b;
+
+ b = (struct buffer *) ck_malloc (sizeof (struct buffer));
+ b->allocated = MIN_ALLOCATE;
+ b->b = (char *) ck_malloc (MIN_ALLOCATE);
+ b->length = 0;
+ return (char *) b;
+}
+
+void
+flush_buffer (bb)
+ char *bb;
+{
+ struct buffer *b;
+
+ b = (struct buffer *) bb;
+ free (b->b);
+ b->b = 0;
+ b->allocated = 0;
+ b->length = 0;
+ free ((void *) b);
+}
+
+void
+add_buffer (bb, p, n)
+ char *bb;
+ char *p;
+ int n;
+{
+ struct buffer *b;
+
+ b = (struct buffer *) bb;
+ if (b->length + n > b->allocated)
+ {
+ b->allocated = b->length + n + MIN_ALLOCATE;
+ b->b = (char *) ck_realloc (b->b, b->allocated);
+ }
+ bcopy (p, b->b + b->length, n);
+ b->length += n;
+}
+
+char *
+get_buffer (bb)
+ char *bb;
+{
+ struct buffer *b;
+
+ b = (struct buffer *) bb;
+ return b->b;
+}
+
+char *
+merge_sort (list, n, off, cmp)
+ char *list;
+ int (*cmp) ();
+ unsigned n;
+ int off;
+{
+ char *ret;
+
+ char *alist, *blist;
+ unsigned alength, blength;
+
+ char *tptr;
+ int tmp;
+ char **prev;
+#define NEXTOF(ptr) (* ((char **)(((char *)(ptr))+off) ) )
+ if (n == 1)
+ return list;
+ if (n == 2)
+ {
+ if ((*cmp) (list, NEXTOF (list)) > 0)
+ {
+ ret = NEXTOF (list);
+ NEXTOF (ret) = list;
+ NEXTOF (list) = 0;
+ return ret;
+ }
+ return list;
+ }
+ alist = list;
+ alength = (n + 1) / 2;
+ blength = n / 2;
+ for (tptr = list, tmp = (n - 1) / 2; tmp; tptr = NEXTOF (tptr), tmp--)
+ ;
+ blist = NEXTOF (tptr);
+ NEXTOF (tptr) = 0;
+
+ alist = merge_sort (alist, alength, off, cmp);
+ blist = merge_sort (blist, blength, off, cmp);
+ prev = &ret;
+ for (; alist && blist;)
+ {
+ if ((*cmp) (alist, blist) < 0)
+ {
+ tptr = NEXTOF (alist);
+ *prev = alist;
+ prev = &(NEXTOF (alist));
+ alist = tptr;
+ }
+ else
+ {
+ tptr = NEXTOF (blist);
+ *prev = blist;
+ prev = &(NEXTOF (blist));
+ blist = tptr;
+ }
+ }
+ if (alist)
+ *prev = alist;
+ else
+ *prev = blist;
+
+ return ret;
+}
+
+void
+ck_close (fd)
+ int fd;
+{
+ if (close (fd) < 0)
+ {
+ msg_perror ("can't close a file #%d", fd);
+ exit (EX_SYSTEM);
+ }
+}
+
+#include <ctype.h>
+
+/* Quote_copy_string is like quote_string, but instead of modifying the
+ string in place, it malloc-s a copy of the string, and returns that.
+ If the string does not have to be quoted, it returns the NULL string.
+ The allocated copy can, of course, be freed with free() after the
+ caller is done with it.
+ */
+char *
+quote_copy_string (string)
+ char *string;
+{
+ char *from_here;
+ char *to_there = 0;
+ char *copy_buf = 0;
+ int c;
+ int copying = 0;
+
+ from_here = string;
+ while (*from_here)
+ {
+ c = *from_here++;
+ if (c == '\\')
+ {
+ if (!copying)
+ {
+ int n;
+
+ n = (from_here - string) - 1;
+ copying++;
+ copy_buf = (char *) malloc (n + 5 + strlen (from_here) * 4);
+ if (!copy_buf)
+ return 0;
+ bcopy (string, copy_buf, n);
+ to_there = copy_buf + n;
+ }
+ *to_there++ = '\\';
+ *to_there++ = '\\';
+ }
+ else if (isprint (c))
+ {
+ if (copying)
+ *to_there++ = c;
+ }
+ else
+ {
+ if (!copying)
+ {
+ int n;
+
+ n = (from_here - string) - 1;
+ copying++;
+ copy_buf = (char *) malloc (n + 5 + strlen (from_here) * 4);
+ if (!copy_buf)
+ return 0;
+ bcopy (string, copy_buf, n);
+ to_there = copy_buf + n;
+ }
+ *to_there++ = '\\';
+ if (c == '\n')
+ *to_there++ = 'n';
+ else if (c == '\t')
+ *to_there++ = 't';
+ else if (c == '\f')
+ *to_there++ = 'f';
+ else if (c == '\b')
+ *to_there++ = 'b';
+ else if (c == '\r')
+ *to_there++ = 'r';
+ else if (c == '\177')
+ *to_there++ = '?';
+ else
+ {
+ to_there[0] = (c >> 6) + '0';
+ to_there[1] = ((c >> 3) & 07) + '0';
+ to_there[2] = (c & 07) + '0';
+ to_there += 3;
+ }
+ }
+ }
+ if (copying)
+ {
+ *to_there = '\0';
+ return copy_buf;
+ }
+ return (char *) 0;
+}
+
+
+/* Un_quote_string takes a quoted c-string (like those produced by
+ quote_string or quote_copy_string and turns it back into the
+ un-quoted original. This is done in place.
+ */
+
+/* There is no un-quote-copy-string. Write it yourself */
+
+char *
+un_quote_string (string)
+ char *string;
+{
+ char *ret;
+ char *from_here;
+ char *to_there;
+ int tmp;
+
+ ret = string;
+ to_there = string;
+ from_here = string;
+ while (*from_here)
+ {
+ if (*from_here != '\\')
+ {
+ if (from_here != to_there)
+ *to_there++ = *from_here++;
+ else
+ from_here++, to_there++;
+ continue;
+ }
+ switch (*++from_here)
+ {
+ case '\\':
+ *to_there++ = *from_here++;
+ break;
+ case 'n':
+ *to_there++ = '\n';
+ from_here++;
+ break;
+ case 't':
+ *to_there++ = '\t';
+ from_here++;
+ break;
+ case 'f':
+ *to_there++ = '\f';
+ from_here++;
+ break;
+ case 'b':
+ *to_there++ = '\b';
+ from_here++;
+ break;
+ case 'r':
+ *to_there++ = '\r';
+ from_here++;
+ break;
+ case '?':
+ *to_there++ = 0177;
+ from_here++;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ tmp = *from_here - '0';
+ from_here++;
+ if (*from_here < '0' || *from_here > '7')
+ {
+ *to_there++ = tmp;
+ break;
+ }
+ tmp = tmp * 8 + *from_here - '0';
+ from_here++;
+ if (*from_here < '0' || *from_here > '7')
+ {
+ *to_there++ = tmp;
+ break;
+ }
+ tmp = tmp * 8 + *from_here - '0';
+ from_here++;
+ *to_there = tmp;
+ break;
+ default:
+ ret = 0;
+ *to_there++ = '\\';
+ *to_there++ = *from_here++;
+ break;
+ }
+ }
+ if (*to_there)
+ *to_there++ = '\0';
+ return ret;
+}
+
+#ifndef __MSDOS__
+void
+ck_pipe (pipes)
+ int *pipes;
+{
+ if (pipe (pipes) < 0)
+ {
+ msg_perror ("can't open a pipe");
+ exit (EX_SYSTEM);
+ }
+}
+#endif /* !__MSDOS__ */
+
+#ifndef HAVE_STRSTR
+/*
+ * strstr - find first occurrence of wanted in s
+ */
+
+char * /* found string, or NULL if none */
+strstr (s, wanted)
+ char *s;
+ char *wanted;
+{
+ register char *scan;
+ register size_t len;
+ register char firstc;
+
+ if (*wanted == '\0')
+ return (char *) 0;
+ /*
+ * The odd placement of the two tests is so "" is findable.
+ * Also, we inline the first char for speed.
+ * The ++ on scan has been moved down for optimization.
+ */
+ firstc = *wanted;
+ len = strlen (wanted);
+ for (scan = s; *scan != firstc || strncmp (scan, wanted, len) != 0;)
+ if (*scan++ == '\0')
+ return (char *) 0;
+ return scan;
+}
+
+#endif /* !HAVE_STRSTR */
+
+#ifndef HAVE_FTRUNCATE
+
+#ifdef F_CHSIZE
+int
+ftruncate (fd, length)
+ int fd;
+ off_t length;
+{
+ return fcntl (fd, F_CHSIZE, length);
+}
+
+#else /* !F_CHSIZE */
+#ifdef F_FREESP
+/* code courtesy of William Kucharski, kucharsk@Solbourne.com */
+
+int
+ftruncate (fd, length)
+ int fd; /* file descriptor */
+ off_t length; /* length to set file to */
+{
+ struct flock fl;
+
+ fl.l_whence = 0;
+ fl.l_len = 0;
+ fl.l_start = length;
+ fl.l_type = F_WRLCK; /* write lock on file space */
+
+ /*
+ * This relies on the UNDOCUMENTED F_FREESP argument to
+ * fcntl(2), which truncates the file so that it ends at the
+ * position indicated by fl.l_start.
+ *
+ * Will minor miracles never cease?
+ */
+
+ if (fcntl (fd, F_FREESP, &fl) < 0)
+ return -1;
+
+ return 0;
+}
+
+#else /* !F_FREESP */
+
+int
+ftruncate (fd, length)
+ int fd;
+ off_t length;
+{
+ errno = EIO;
+ return -1;
+}
+
+#endif /* !F_FREESP */
+#endif /* !F_CHSIZE */
+#endif /* !HAVE_FTRUNCATE */
+
+
+extern FILE *msg_file;
+
+#if defined (HAVE_VPRINTF) && __STDC__
+#include <stdarg.h>
+
+void
+msg (char *str,...)
+{
+ va_list args;
+
+ va_start (args, str);
+ fflush (msg_file);
+ fprintf (stderr, "%s: ", tar);
+ if (f_sayblock)
+ fprintf (stderr, "rec %d: ", baserec + (ar_record - ar_block));
+ vfprintf (stderr, str, args);
+ va_end (args);
+ putc ('\n', stderr);
+ fflush (stderr);
+}
+
+void
+msg_perror (char *str,...)
+{
+ va_list args;
+ int save_e;
+
+ save_e = errno;
+ fflush (msg_file);
+ fprintf (stderr, "%s: ", tar);
+ if (f_sayblock)
+ fprintf (stderr, "rec %d: ", baserec + (ar_record - ar_block));
+ va_start (args, str);
+ vfprintf (stderr, str, args);
+ va_end (args);
+ errno = save_e;
+ perror (" ");
+ fflush (stderr);
+}
+
+#endif /* HAVE_VPRINTF and __STDC__ */
+
+#if defined(HAVE_VPRINTF) && !__STDC__
+#include <varargs.h>
+void
+msg (str, va_alist)
+ char *str;
+ va_dcl
+{
+ va_list args;
+
+ fflush (msg_file);
+ fprintf (stderr, "%s: ", tar);
+ if (f_sayblock)
+ fprintf (stderr, "rec %d: ", baserec + (ar_record - ar_block));
+ va_start (args);
+ vfprintf (stderr, str, args);
+ va_end (args);
+ putc ('\n', stderr);
+ fflush (stderr);
+}
+
+void
+msg_perror (str, va_alist)
+ char *str;
+ va_dcl
+{
+ va_list args;
+ int save_e;
+
+ save_e = errno;
+ fflush (msg_file);
+ fprintf (stderr, "%s: ", tar);
+ if (f_sayblock)
+ fprintf (stderr, "rec %d: ", baserec + (ar_record - ar_block));
+ va_start (args);
+ vfprintf (stderr, str, args);
+ va_end (args);
+ errno = save_e;
+ perror (" ");
+ fflush (stderr);
+}
+
+#endif /* HAVE_VPRINTF and not __STDC__ */
+
+#if !defined(HAVE_VPRINTF) && defined(HAVE_DOPRNT)
+void
+msg (str, args)
+ char *str;
+ int args;
+{
+ fflush (msg_file);
+ fprintf (stderr, "%s: ", tar);
+ if (f_sayblock)
+ fprintf (stderr, "rec %d: ", baserec + (ar_record - ar_block));
+ _doprnt (str, &args, stderr);
+ putc ('\n', stderr);
+ fflush (stderr);
+}
+
+void
+msg_perror (str, args)
+ char *str;
+ int args;
+{
+ int save_e;
+
+ save_e = errno;
+ fflush (msg_file);
+ fprintf (stderr, "%s: ", tar);
+ if (f_sayblock)
+ fprintf (stderr, "rec %d: ", baserec + (ar_record - ar_block));
+ _doprnt (str, &args, stderr);
+ errno = save_e;
+ perror (" ");
+ fflush (stderr);
+}
+
+#endif /* !HAVE_VPRINTF and HAVE_DOPRNT */
+
+#if !defined(HAVE_VPRINTF) && !defined(HAVE_DOPRNT)
+void
+msg (str, a1, a2, a3, a4, a5, a6)
+ char *str;
+{
+ fflush (msg_file);
+ fprintf (stderr, "%s: ", tar);
+ if (f_sayblock)
+ fprintf (stderr, "rec %d: ", baserec + (ar_record - ar_block));
+ fprintf (stderr, str, a1, a2, a3, a4, a5, a6);
+ putc ('\n', stderr);
+ fflush (stderr);
+}
+
+void
+msg_perror (str, a1, a2, a3, a4, a5, a6)
+ char *str;
+{
+ int save_e;
+
+ save_e = errno;
+ fflush (msg_file);
+ fprintf (stderr, "%s: ", tar);
+ if (f_sayblock)
+ fprintf (stderr, "rec %d: ", baserec + (ar_record - ar_block));
+ fprintf (stderr, str, a1, a2, a3, a4, a5, a6);
+ fprintf (stderr, ": ");
+ errno = save_e;
+ perror (" ");
+}
+
+#endif /* !HAVE_VPRINTF and !HAVE_DOPRNT */
diff --git a/gnu/usr.bin/tar/port.h b/gnu/usr.bin/tar/port.h
new file mode 100644
index 0000000..4e65a9a
--- /dev/null
+++ b/gnu/usr.bin/tar/port.h
@@ -0,0 +1,215 @@
+/* Portability declarations. Requires sys/types.h.
+ Copyright (C) 1988, 1992 Free Software Foundation
+
+This file is part of GNU Tar.
+
+GNU Tar is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Tar is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Tar; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* AIX requires this to be the first thing in the file. */
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not __GNUC__ */
+#if HAVE_ALLOCA_H
+#include <alloca.h>
+#else /* not HAVE_ALLOCA_H */
+#ifdef _AIX
+ #pragma alloca
+#else /* not _AIX */
+char *alloca ();
+#endif /* not _AIX */
+#endif /* not HAVE_ALLOCA_H */
+#endif /* not __GNUC__ */
+
+#include "pathmax.h"
+
+#ifdef _POSIX_VERSION
+#include <sys/wait.h>
+#else /* !_POSIX_VERSION */
+#define WIFSTOPPED(w) (((w) & 0xff) == 0x7f)
+#define WIFSIGNALED(w) (((w) & 0xff) != 0x7f && ((w) & 0xff) != 0)
+#define WIFEXITED(w) (((w) & 0xff) == 0)
+
+#define WSTOPSIG(w) (((w) >> 8) & 0xff)
+#define WTERMSIG(w) ((w) & 0x7f)
+#define WEXITSTATUS(w) (((w) >> 8) & 0xff)
+#endif /* _POSIX_VERSION */
+
+/* nonstandard */
+#ifndef WIFCOREDUMPED
+#define WIFCOREDUMPED(w) (((w) & 0x80) != 0)
+#endif
+
+#ifdef __MSDOS__
+/* missing things from sys/stat.h */
+#define S_ISUID 0
+#define S_ISGID 0
+#define S_ISVTX 0
+
+/* device stuff */
+#define makedev(ma, mi) ((ma << 8) | mi)
+#define major(dev) (dev)
+#define minor(dev) (dev)
+typedef long off_t;
+#endif /* __MSDOS__ */
+
+#if defined(__STDC__) || defined(__TURBOC__)
+#define PTR void *
+#else
+#define PTR char *
+#define const
+#endif
+
+/* Since major is a function on SVR4, we can't just use `ifndef major'. */
+#ifdef major /* Might be defined in sys/types.h. */
+#define HAVE_MAJOR
+#endif
+
+#if !defined(HAVE_MAJOR) && defined(MAJOR_IN_MKDEV)
+#include <sys/mkdev.h>
+#define HAVE_MAJOR
+#endif
+
+#if !defined(HAVE_MAJOR) && defined(MAJOR_IN_SYSMACROS)
+#include <sys/sysmacros.h>
+#define HAVE_MAJOR
+#endif
+
+#ifndef HAVE_MAJOR
+#define major(dev) (((dev) >> 8) & 0xff)
+#define minor(dev) ((dev) & 0xff)
+#define makedev(maj, min) (((maj) << 8) | (min))
+#endif
+#undef HAVE_MAJOR
+
+#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
+#include <string.h>
+#if !defined(__MSDOS__) && !defined(STDC_HEADERS)
+#include <memory.h>
+#endif
+#ifdef index
+#undef index
+#endif
+#ifdef rindex
+#undef rindex
+#endif
+#define index strchr
+#define rindex strrchr
+#define bcopy(s, d, n) memcpy(d, s, n)
+#define bzero(s, n) memset(s, 0, n)
+#define bcmp memcmp
+#else
+#include <strings.h>
+#endif
+
+#if defined(STDC_HEADERS)
+#include <stdlib.h>
+#else
+char *malloc (), *realloc ();
+char *getenv ();
+#endif
+
+#ifndef _POSIX_VERSION
+#ifdef __MSDOS__
+#include <io.h>
+#else /* !__MSDOS__ */
+off_t lseek ();
+#endif /* !__MSDOS__ */
+char *getcwd ();
+#endif /* !_POSIX_VERSION */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_CREAT
+#define O_CREAT 0
+#endif
+#ifndef O_NDELAY
+#define O_NDELAY 0
+#endif
+#ifndef O_RDONLY
+#define O_RDONLY 0
+#endif
+#ifndef O_RDWR
+#define O_RDWR 2
+#endif
+
+#include <sys/stat.h>
+#ifndef S_ISREG /* Doesn't have POSIX.1 stat stuff. */
+#define mode_t unsigned short
+#endif
+#if !defined(S_ISBLK) && defined(S_IFBLK)
+#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
+#endif
+#if !defined(S_ISCHR) && defined(S_IFCHR)
+#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
+#endif
+#if !defined(S_ISDIR) && defined(S_IFDIR)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISREG) && defined(S_IFREG)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISFIFO) && defined(S_IFIFO)
+#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
+#define mkfifo(path, mode) (mknod ((path), (mode) | S_IFIFO, 0))
+#endif
+#if !defined(S_ISLNK) && defined(S_IFLNK)
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#endif
+#if !defined(S_ISSOCK) && defined(S_IFSOCK)
+#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
+#endif
+#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */
+#define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB)
+#define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC)
+#endif
+#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */
+#define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK)
+#endif
+#if !defined(S_ISCTG) && defined(S_IFCTG) /* contiguous file */
+#define S_ISCTG(m) (((m) & S_IFMT) == S_IFCTG)
+#endif
+#if !defined(S_ISVTX)
+#define S_ISVTX 0001000
+#endif
+
+#ifdef __MSDOS__
+#include "msd_dir.h"
+#define NLENGTH(direct) ((direct)->d_namlen)
+
+#else /* not __MSDOS__ */
+
+#if defined(DIRENT) || defined(_POSIX_VERSION)
+#include <dirent.h>
+#define NLENGTH(direct) (strlen((direct)->d_name))
+#else /* not (DIRENT or _POSIX_VERSION) */
+#define dirent direct
+#define NLENGTH(direct) ((direct)->d_namlen)
+#ifdef SYSNDIR
+#include <sys/ndir.h>
+#endif /* SYSNDIR */
+#ifdef SYSDIR
+#include <sys/dir.h>
+#endif /* SYSDIR */
+#ifdef NDIR
+#include <ndir.h>
+#endif /* NDIR */
+#endif /* DIRENT or _POSIX_VERSION */
+
+#endif /* not __MSDOS__ */
diff --git a/gnu/usr.bin/tar/regex.c b/gnu/usr.bin/tar/regex.c
new file mode 100644
index 0000000..cb94d59
--- /dev/null
+++ b/gnu/usr.bin/tar/regex.c
@@ -0,0 +1,4932 @@
+/* Extended regular expression matching and search library,
+ version 0.11.
+ (Implements POSIX draft P10003.2/D11.2, except for
+ internationalization features.)
+
+ Copyright (C) 1993 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* AIX requires this to be the first thing in the file. */
+#if defined (_AIX) && !defined (REGEX_MALLOC)
+ #pragma alloca
+#endif
+
+#define _GNU_SOURCE
+
+/* We need this for `regex.h', and perhaps for the Emacs include files. */
+#include <sys/types.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* The `emacs' switch turns on certain matching commands
+ that make sense only in Emacs. */
+#ifdef emacs
+
+#include "lisp.h"
+#include "buffer.h"
+#include "syntax.h"
+
+/* Emacs uses `NULL' as a predicate. */
+#undef NULL
+
+#else /* not emacs */
+
+/* We used to test for `BSTRING' here, but only GCC and Emacs define
+ `BSTRING', as far as I know, and neither of them use this code. */
+#if HAVE_STRING_H || STDC_HEADERS
+#include <string.h>
+#ifndef bcmp
+#define bcmp(s1, s2, n) memcmp ((s1), (s2), (n))
+#endif
+#ifndef bcopy
+#define bcopy(s, d, n) memcpy ((d), (s), (n))
+#endif
+#ifndef bzero
+#define bzero(s, n) memset ((s), 0, (n))
+#endif
+#else
+#include <strings.h>
+#endif
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+char *malloc ();
+char *realloc ();
+#endif
+
+
+/* Define the syntax stuff for \<, \>, etc. */
+
+/* This must be nonzero for the wordchar and notwordchar pattern
+ commands in re_match_2. */
+#ifndef Sword
+#define Sword 1
+#endif
+
+#ifdef SYNTAX_TABLE
+
+extern char *re_syntax_table;
+
+#else /* not SYNTAX_TABLE */
+
+/* How many characters in the character set. */
+#define CHAR_SET_SIZE 256
+
+static char re_syntax_table[CHAR_SET_SIZE];
+
+static void
+init_syntax_once ()
+{
+ register int c;
+ static int done = 0;
+
+ if (done)
+ return;
+
+ bzero (re_syntax_table, sizeof re_syntax_table);
+
+ for (c = 'a'; c <= 'z'; c++)
+ re_syntax_table[c] = Sword;
+
+ for (c = 'A'; c <= 'Z'; c++)
+ re_syntax_table[c] = Sword;
+
+ for (c = '0'; c <= '9'; c++)
+ re_syntax_table[c] = Sword;
+
+ re_syntax_table['_'] = Sword;
+
+ done = 1;
+}
+
+#endif /* not SYNTAX_TABLE */
+
+#define SYNTAX(c) re_syntax_table[c]
+
+#endif /* not emacs */
+
+/* Get the interface, including the syntax bits. */
+#include "regex.h"
+
+/* isalpha etc. are used for the character classes. */
+#include <ctype.h>
+
+#ifndef isascii
+#define isascii(c) 1
+#endif
+
+#ifdef isblank
+#define ISBLANK(c) (isascii (c) && isblank (c))
+#else
+#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+#ifdef isgraph
+#define ISGRAPH(c) (isascii (c) && isgraph (c))
+#else
+#define ISGRAPH(c) (isascii (c) && isprint (c) && !isspace (c))
+#endif
+
+#define ISPRINT(c) (isascii (c) && isprint (c))
+#define ISDIGIT(c) (isascii (c) && isdigit (c))
+#define ISALNUM(c) (isascii (c) && isalnum (c))
+#define ISALPHA(c) (isascii (c) && isalpha (c))
+#define ISCNTRL(c) (isascii (c) && iscntrl (c))
+#define ISLOWER(c) (isascii (c) && islower (c))
+#define ISPUNCT(c) (isascii (c) && ispunct (c))
+#define ISSPACE(c) (isascii (c) && isspace (c))
+#define ISUPPER(c) (isascii (c) && isupper (c))
+#define ISXDIGIT(c) (isascii (c) && isxdigit (c))
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/* We remove any previous definition of `SIGN_EXTEND_CHAR',
+ since ours (we hope) works properly with all combinations of
+ machines, compilers, `char' and `unsigned char' argument types.
+ (Per Bothner suggested the basic approach.) */
+#undef SIGN_EXTEND_CHAR
+#if __STDC__
+#define SIGN_EXTEND_CHAR(c) ((signed char) (c))
+#else /* not __STDC__ */
+/* As in Harbison and Steele. */
+#define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128)
+#endif
+
+/* Should we use malloc or alloca? If REGEX_MALLOC is not defined, we
+ use `alloca' instead of `malloc'. This is because using malloc in
+ re_search* or re_match* could cause memory leaks when C-g is used in
+ Emacs; also, malloc is slower and causes storage fragmentation. On
+ the other hand, malloc is more portable, and easier to debug.
+
+ Because we sometimes use alloca, some routines have to be macros,
+ not functions -- `alloca'-allocated space disappears at the end of the
+ function it is called in. */
+
+#ifdef REGEX_MALLOC
+
+#define REGEX_ALLOCATE malloc
+#define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize)
+
+#else /* not REGEX_MALLOC */
+
+/* Emacs already defines alloca, sometimes. */
+#ifndef alloca
+
+/* Make alloca work the best possible way. */
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not __GNUC__ */
+#if HAVE_ALLOCA_H
+#include <alloca.h>
+#else /* not __GNUC__ or HAVE_ALLOCA_H */
+#ifndef _AIX /* Already did AIX, up at the top. */
+char *alloca ();
+#endif /* not _AIX */
+#endif /* not HAVE_ALLOCA_H */
+#endif /* not __GNUC__ */
+
+#endif /* not alloca */
+
+#define REGEX_ALLOCATE alloca
+
+/* Assumes a `char *destination' variable. */
+#define REGEX_REALLOCATE(source, osize, nsize) \
+ (destination = (char *) alloca (nsize), \
+ bcopy (source, destination, osize), \
+ destination)
+
+#endif /* not REGEX_MALLOC */
+
+
+/* True if `size1' is non-NULL and PTR is pointing anywhere inside
+ `string1' or just past its end. This works if PTR is NULL, which is
+ a good thing. */
+#define FIRST_STRING_P(ptr) \
+ (size1 && string1 <= (ptr) && (ptr) <= string1 + size1)
+
+/* (Re)Allocate N items of type T using malloc, or fail. */
+#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t)))
+#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t)))
+#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t)))
+
+#define BYTEWIDTH 8 /* In bits. */
+
+#define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+typedef char boolean;
+#define false 0
+#define true 1
+
+/* These are the command codes that appear in compiled regular
+ expressions. Some opcodes are followed by argument bytes. A
+ command code can specify any interpretation whatsoever for its
+ arguments. Zero bytes may appear in the compiled regular expression.
+
+ The value of `exactn' is needed in search.c (search_buffer) in Emacs.
+ So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of
+ `exactn' we use here must also be 1. */
+
+typedef enum
+{
+ no_op = 0,
+
+ /* Followed by one byte giving n, then by n literal bytes. */
+ exactn = 1,
+
+ /* Matches any (more or less) character. */
+ anychar,
+
+ /* Matches any one char belonging to specified set. First
+ following byte is number of bitmap bytes. Then come bytes
+ for a bitmap saying which chars are in. Bits in each byte
+ are ordered low-bit-first. A character is in the set if its
+ bit is 1. A character too large to have a bit in the map is
+ automatically not in the set. */
+ charset,
+
+ /* Same parameters as charset, but match any character that is
+ not one of those specified. */
+ charset_not,
+
+ /* Start remembering the text that is matched, for storing in a
+ register. Followed by one byte with the register number, in
+ the range 0 to one less than the pattern buffer's re_nsub
+ field. Then followed by one byte with the number of groups
+ inner to this one. (This last has to be part of the
+ start_memory only because we need it in the on_failure_jump
+ of re_match_2.) */
+ start_memory,
+
+ /* Stop remembering the text that is matched and store it in a
+ memory register. Followed by one byte with the register
+ number, in the range 0 to one less than `re_nsub' in the
+ pattern buffer, and one byte with the number of inner groups,
+ just like `start_memory'. (We need the number of inner
+ groups here because we don't have any easy way of finding the
+ corresponding start_memory when we're at a stop_memory.) */
+ stop_memory,
+
+ /* Match a duplicate of something remembered. Followed by one
+ byte containing the register number. */
+ duplicate,
+
+ /* Fail unless at beginning of line. */
+ begline,
+
+ /* Fail unless at end of line. */
+ endline,
+
+ /* Succeeds if at beginning of buffer (if emacs) or at beginning
+ of string to be matched (if not). */
+ begbuf,
+
+ /* Analogously, for end of buffer/string. */
+ endbuf,
+
+ /* Followed by two byte relative address to which to jump. */
+ jump,
+
+ /* Same as jump, but marks the end of an alternative. */
+ jump_past_alt,
+
+ /* Followed by two-byte relative address of place to resume at
+ in case of failure. */
+ on_failure_jump,
+
+ /* Like on_failure_jump, but pushes a placeholder instead of the
+ current string position when executed. */
+ on_failure_keep_string_jump,
+
+ /* Throw away latest failure point and then jump to following
+ two-byte relative address. */
+ pop_failure_jump,
+
+ /* Change to pop_failure_jump if know won't have to backtrack to
+ match; otherwise change to jump. This is used to jump
+ back to the beginning of a repeat. If what follows this jump
+ clearly won't match what the repeat does, such that we can be
+ sure that there is no use backtracking out of repetitions
+ already matched, then we change it to a pop_failure_jump.
+ Followed by two-byte address. */
+ maybe_pop_jump,
+
+ /* Jump to following two-byte address, and push a dummy failure
+ point. This failure point will be thrown away if an attempt
+ is made to use it for a failure. A `+' construct makes this
+ before the first repeat. Also used as an intermediary kind
+ of jump when compiling an alternative. */
+ dummy_failure_jump,
+
+ /* Push a dummy failure point and continue. Used at the end of
+ alternatives. */
+ push_dummy_failure,
+
+ /* Followed by two-byte relative address and two-byte number n.
+ After matching N times, jump to the address upon failure. */
+ succeed_n,
+
+ /* Followed by two-byte relative address, and two-byte number n.
+ Jump to the address N times, then fail. */
+ jump_n,
+
+ /* Set the following two-byte relative address to the
+ subsequent two-byte number. The address *includes* the two
+ bytes of number. */
+ set_number_at,
+
+ wordchar, /* Matches any word-constituent character. */
+ notwordchar, /* Matches any char that is not a word-constituent. */
+
+ wordbeg, /* Succeeds if at word beginning. */
+ wordend, /* Succeeds if at word end. */
+
+ wordbound, /* Succeeds if at a word boundary. */
+ notwordbound /* Succeeds if not at a word boundary. */
+
+#ifdef emacs
+ ,before_dot, /* Succeeds if before point. */
+ at_dot, /* Succeeds if at point. */
+ after_dot, /* Succeeds if after point. */
+
+ /* Matches any character whose syntax is specified. Followed by
+ a byte which contains a syntax code, e.g., Sword. */
+ syntaxspec,
+
+ /* Matches any character whose syntax is not that specified. */
+ notsyntaxspec
+#endif /* emacs */
+} re_opcode_t;
+
+/* Common operations on the compiled pattern. */
+
+/* Store NUMBER in two contiguous bytes starting at DESTINATION. */
+
+#define STORE_NUMBER(destination, number) \
+ do { \
+ (destination)[0] = (number) & 0377; \
+ (destination)[1] = (number) >> 8; \
+ } while (0)
+
+/* Same as STORE_NUMBER, except increment DESTINATION to
+ the byte after where the number is stored. Therefore, DESTINATION
+ must be an lvalue. */
+
+#define STORE_NUMBER_AND_INCR(destination, number) \
+ do { \
+ STORE_NUMBER (destination, number); \
+ (destination) += 2; \
+ } while (0)
+
+/* Put into DESTINATION a number stored in two contiguous bytes starting
+ at SOURCE. */
+
+#define EXTRACT_NUMBER(destination, source) \
+ do { \
+ (destination) = *(source) & 0377; \
+ (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8; \
+ } while (0)
+
+#ifdef DEBUG
+static void
+extract_number (dest, source)
+ int *dest;
+ unsigned char *source;
+{
+ int temp = SIGN_EXTEND_CHAR (*(source + 1));
+ *dest = *source & 0377;
+ *dest += temp << 8;
+}
+
+#ifndef EXTRACT_MACROS /* To debug the macros. */
+#undef EXTRACT_NUMBER
+#define EXTRACT_NUMBER(dest, src) extract_number (&dest, src)
+#endif /* not EXTRACT_MACROS */
+
+#endif /* DEBUG */
+
+/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number.
+ SOURCE must be an lvalue. */
+
+#define EXTRACT_NUMBER_AND_INCR(destination, source) \
+ do { \
+ EXTRACT_NUMBER (destination, source); \
+ (source) += 2; \
+ } while (0)
+
+#ifdef DEBUG
+static void
+extract_number_and_incr (destination, source)
+ int *destination;
+ unsigned char **source;
+{
+ extract_number (destination, *source);
+ *source += 2;
+}
+
+#ifndef EXTRACT_MACROS
+#undef EXTRACT_NUMBER_AND_INCR
+#define EXTRACT_NUMBER_AND_INCR(dest, src) \
+ extract_number_and_incr (&dest, &src)
+#endif /* not EXTRACT_MACROS */
+
+#endif /* DEBUG */
+
+/* If DEBUG is defined, Regex prints many voluminous messages about what
+ it is doing (if the variable `debug' is nonzero). If linked with the
+ main program in `iregex.c', you can enter patterns and strings
+ interactively. And if linked with the main program in `main.c' and
+ the other test files, you can run the already-written tests. */
+
+#ifdef DEBUG
+
+/* We use standard I/O for debugging. */
+#include <stdio.h>
+
+/* It is useful to test things that ``must'' be true when debugging. */
+#include <assert.h>
+
+static int debug = 0;
+
+#define DEBUG_STATEMENT(e) e
+#define DEBUG_PRINT1(x) if (debug) printf (x)
+#define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2)
+#define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3)
+#define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4)
+#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) \
+ if (debug) print_partial_compiled_pattern (s, e)
+#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) \
+ if (debug) print_double_string (w, s1, sz1, s2, sz2)
+
+
+extern void printchar ();
+
+/* Print the fastmap in human-readable form. */
+
+void
+print_fastmap (fastmap)
+ char *fastmap;
+{
+ unsigned was_a_range = 0;
+ unsigned i = 0;
+
+ while (i < (1 << BYTEWIDTH))
+ {
+ if (fastmap[i++])
+ {
+ was_a_range = 0;
+ printchar (i - 1);
+ while (i < (1 << BYTEWIDTH) && fastmap[i])
+ {
+ was_a_range = 1;
+ i++;
+ }
+ if (was_a_range)
+ {
+ printf ("-");
+ printchar (i - 1);
+ }
+ }
+ }
+ putchar ('\n');
+}
+
+
+/* Print a compiled pattern string in human-readable form, starting at
+ the START pointer into it and ending just before the pointer END. */
+
+void
+print_partial_compiled_pattern (start, end)
+ unsigned char *start;
+ unsigned char *end;
+{
+ int mcnt, mcnt2;
+ unsigned char *p = start;
+ unsigned char *pend = end;
+
+ if (start == NULL)
+ {
+ printf ("(null)\n");
+ return;
+ }
+
+ /* Loop over pattern commands. */
+ while (p < pend)
+ {
+ switch ((re_opcode_t) *p++)
+ {
+ case no_op:
+ printf ("/no_op");
+ break;
+
+ case exactn:
+ mcnt = *p++;
+ printf ("/exactn/%d", mcnt);
+ do
+ {
+ putchar ('/');
+ printchar (*p++);
+ }
+ while (--mcnt);
+ break;
+
+ case start_memory:
+ mcnt = *p++;
+ printf ("/start_memory/%d/%d", mcnt, *p++);
+ break;
+
+ case stop_memory:
+ mcnt = *p++;
+ printf ("/stop_memory/%d/%d", mcnt, *p++);
+ break;
+
+ case duplicate:
+ printf ("/duplicate/%d", *p++);
+ break;
+
+ case anychar:
+ printf ("/anychar");
+ break;
+
+ case charset:
+ case charset_not:
+ {
+ register int c;
+
+ printf ("/charset%s",
+ (re_opcode_t) *(p - 1) == charset_not ? "_not" : "");
+
+ assert (p + *p < pend);
+
+ for (c = 0; c < *p; c++)
+ {
+ unsigned bit;
+ unsigned char map_byte = p[1 + c];
+
+ putchar ('/');
+
+ for (bit = 0; bit < BYTEWIDTH; bit++)
+ if (map_byte & (1 << bit))
+ printchar (c * BYTEWIDTH + bit);
+ }
+ p += 1 + *p;
+ break;
+ }
+
+ case begline:
+ printf ("/begline");
+ break;
+
+ case endline:
+ printf ("/endline");
+ break;
+
+ case on_failure_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/on_failure_jump/0/%d", mcnt);
+ break;
+
+ case on_failure_keep_string_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/on_failure_keep_string_jump/0/%d", mcnt);
+ break;
+
+ case dummy_failure_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/dummy_failure_jump/0/%d", mcnt);
+ break;
+
+ case push_dummy_failure:
+ printf ("/push_dummy_failure");
+ break;
+
+ case maybe_pop_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/maybe_pop_jump/0/%d", mcnt);
+ break;
+
+ case pop_failure_jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/pop_failure_jump/0/%d", mcnt);
+ break;
+
+ case jump_past_alt:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/jump_past_alt/0/%d", mcnt);
+ break;
+
+ case jump:
+ extract_number_and_incr (&mcnt, &p);
+ printf ("/jump/0/%d", mcnt);
+ break;
+
+ case succeed_n:
+ extract_number_and_incr (&mcnt, &p);
+ extract_number_and_incr (&mcnt2, &p);
+ printf ("/succeed_n/0/%d/0/%d", mcnt, mcnt2);
+ break;
+
+ case jump_n:
+ extract_number_and_incr (&mcnt, &p);
+ extract_number_and_incr (&mcnt2, &p);
+ printf ("/jump_n/0/%d/0/%d", mcnt, mcnt2);
+ break;
+
+ case set_number_at:
+ extract_number_and_incr (&mcnt, &p);
+ extract_number_and_incr (&mcnt2, &p);
+ printf ("/set_number_at/0/%d/0/%d", mcnt, mcnt2);
+ break;
+
+ case wordbound:
+ printf ("/wordbound");
+ break;
+
+ case notwordbound:
+ printf ("/notwordbound");
+ break;
+
+ case wordbeg:
+ printf ("/wordbeg");
+ break;
+
+ case wordend:
+ printf ("/wordend");
+
+#ifdef emacs
+ case before_dot:
+ printf ("/before_dot");
+ break;
+
+ case at_dot:
+ printf ("/at_dot");
+ break;
+
+ case after_dot:
+ printf ("/after_dot");
+ break;
+
+ case syntaxspec:
+ printf ("/syntaxspec");
+ mcnt = *p++;
+ printf ("/%d", mcnt);
+ break;
+
+ case notsyntaxspec:
+ printf ("/notsyntaxspec");
+ mcnt = *p++;
+ printf ("/%d", mcnt);
+ break;
+#endif /* emacs */
+
+ case wordchar:
+ printf ("/wordchar");
+ break;
+
+ case notwordchar:
+ printf ("/notwordchar");
+ break;
+
+ case begbuf:
+ printf ("/begbuf");
+ break;
+
+ case endbuf:
+ printf ("/endbuf");
+ break;
+
+ default:
+ printf ("?%d", *(p-1));
+ }
+ }
+ printf ("/\n");
+}
+
+
+void
+print_compiled_pattern (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ unsigned char *buffer = bufp->buffer;
+
+ print_partial_compiled_pattern (buffer, buffer + bufp->used);
+ printf ("%d bytes used/%d bytes allocated.\n", bufp->used, bufp->allocated);
+
+ if (bufp->fastmap_accurate && bufp->fastmap)
+ {
+ printf ("fastmap: ");
+ print_fastmap (bufp->fastmap);
+ }
+
+ printf ("re_nsub: %d\t", bufp->re_nsub);
+ printf ("regs_alloc: %d\t", bufp->regs_allocated);
+ printf ("can_be_null: %d\t", bufp->can_be_null);
+ printf ("newline_anchor: %d\n", bufp->newline_anchor);
+ printf ("no_sub: %d\t", bufp->no_sub);
+ printf ("not_bol: %d\t", bufp->not_bol);
+ printf ("not_eol: %d\t", bufp->not_eol);
+ printf ("syntax: %d\n", bufp->syntax);
+ /* Perhaps we should print the translate table? */
+}
+
+
+void
+print_double_string (where, string1, size1, string2, size2)
+ const char *where;
+ const char *string1;
+ const char *string2;
+ int size1;
+ int size2;
+{
+ unsigned this_char;
+
+ if (where == NULL)
+ printf ("(null)");
+ else
+ {
+ if (FIRST_STRING_P (where))
+ {
+ for (this_char = where - string1; this_char < size1; this_char++)
+ printchar (string1[this_char]);
+
+ where = string2;
+ }
+
+ for (this_char = where - string2; this_char < size2; this_char++)
+ printchar (string2[this_char]);
+ }
+}
+
+#else /* not DEBUG */
+
+#undef assert
+#define assert(e)
+
+#define DEBUG_STATEMENT(e)
+#define DEBUG_PRINT1(x)
+#define DEBUG_PRINT2(x1, x2)
+#define DEBUG_PRINT3(x1, x2, x3)
+#define DEBUG_PRINT4(x1, x2, x3, x4)
+#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)
+#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)
+
+#endif /* not DEBUG */
+
+/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can
+ also be assigned to arbitrarily: each pattern buffer stores its own
+ syntax, so it can be changed between regex compilations. */
+reg_syntax_t re_syntax_options = RE_SYNTAX_EMACS;
+
+
+/* Specify the precise syntax of regexps for compilation. This provides
+ for compatibility for various utilities which historically have
+ different, incompatible syntaxes.
+
+ The argument SYNTAX is a bit mask comprised of the various bits
+ defined in regex.h. We return the old syntax. */
+
+reg_syntax_t
+re_set_syntax (syntax)
+ reg_syntax_t syntax;
+{
+ reg_syntax_t ret = re_syntax_options;
+
+ re_syntax_options = syntax;
+ return ret;
+}
+
+/* This table gives an error message for each of the error codes listed
+ in regex.h. Obviously the order here has to be same as there. */
+
+static const char *re_error_msg[] =
+ { NULL, /* REG_NOERROR */
+ "No match", /* REG_NOMATCH */
+ "Invalid regular expression", /* REG_BADPAT */
+ "Invalid collation character", /* REG_ECOLLATE */
+ "Invalid character class name", /* REG_ECTYPE */
+ "Trailing backslash", /* REG_EESCAPE */
+ "Invalid back reference", /* REG_ESUBREG */
+ "Unmatched [ or [^", /* REG_EBRACK */
+ "Unmatched ( or \\(", /* REG_EPAREN */
+ "Unmatched \\{", /* REG_EBRACE */
+ "Invalid content of \\{\\}", /* REG_BADBR */
+ "Invalid range end", /* REG_ERANGE */
+ "Memory exhausted", /* REG_ESPACE */
+ "Invalid preceding regular expression", /* REG_BADRPT */
+ "Premature end of regular expression", /* REG_EEND */
+ "Regular expression too big", /* REG_ESIZE */
+ "Unmatched ) or \\)", /* REG_ERPAREN */
+ };
+
+/* Subroutine declarations and macros for regex_compile. */
+
+static void store_op1 (), store_op2 ();
+static void insert_op1 (), insert_op2 ();
+static boolean at_begline_loc_p (), at_endline_loc_p ();
+static boolean group_in_compile_stack ();
+static reg_errcode_t compile_range ();
+
+/* Fetch the next character in the uncompiled pattern---translating it
+ if necessary. Also cast from a signed character in the constant
+ string passed to us by the user to an unsigned char that we can use
+ as an array index (in, e.g., `translate'). */
+#define PATFETCH(c) \
+ do {if (p == pend) return REG_EEND; \
+ c = (unsigned char) *p++; \
+ if (translate) c = translate[c]; \
+ } while (0)
+
+/* Fetch the next character in the uncompiled pattern, with no
+ translation. */
+#define PATFETCH_RAW(c) \
+ do {if (p == pend) return REG_EEND; \
+ c = (unsigned char) *p++; \
+ } while (0)
+
+/* Go backwards one character in the pattern. */
+#define PATUNFETCH p--
+
+
+/* If `translate' is non-null, return translate[D], else just D. We
+ cast the subscript to translate because some data is declared as
+ `char *', to avoid warnings when a string constant is passed. But
+ when we use a character as a subscript we must make it unsigned. */
+#define TRANSLATE(d) (translate ? translate[(unsigned char) (d)] : (d))
+
+
+/* Macros for outputting the compiled pattern into `buffer'. */
+
+/* If the buffer isn't allocated when it comes in, use this. */
+#define INIT_BUF_SIZE 32
+
+/* Make sure we have at least N more bytes of space in buffer. */
+#define GET_BUFFER_SPACE(n) \
+ while (b - bufp->buffer + (n) > bufp->allocated) \
+ EXTEND_BUFFER ()
+
+/* Make sure we have one more byte of buffer space and then add C to it. */
+#define BUF_PUSH(c) \
+ do { \
+ GET_BUFFER_SPACE (1); \
+ *b++ = (unsigned char) (c); \
+ } while (0)
+
+
+/* Ensure we have two more bytes of buffer space and then append C1 and C2. */
+#define BUF_PUSH_2(c1, c2) \
+ do { \
+ GET_BUFFER_SPACE (2); \
+ *b++ = (unsigned char) (c1); \
+ *b++ = (unsigned char) (c2); \
+ } while (0)
+
+
+/* As with BUF_PUSH_2, except for three bytes. */
+#define BUF_PUSH_3(c1, c2, c3) \
+ do { \
+ GET_BUFFER_SPACE (3); \
+ *b++ = (unsigned char) (c1); \
+ *b++ = (unsigned char) (c2); \
+ *b++ = (unsigned char) (c3); \
+ } while (0)
+
+
+/* Store a jump with opcode OP at LOC to location TO. We store a
+ relative address offset by the three bytes the jump itself occupies. */
+#define STORE_JUMP(op, loc, to) \
+ store_op1 (op, loc, (to) - (loc) - 3)
+
+/* Likewise, for a two-argument jump. */
+#define STORE_JUMP2(op, loc, to, arg) \
+ store_op2 (op, loc, (to) - (loc) - 3, arg)
+
+/* Like `STORE_JUMP', but for inserting. Assume `b' is the buffer end. */
+#define INSERT_JUMP(op, loc, to) \
+ insert_op1 (op, loc, (to) - (loc) - 3, b)
+
+/* Like `STORE_JUMP2', but for inserting. Assume `b' is the buffer end. */
+#define INSERT_JUMP2(op, loc, to, arg) \
+ insert_op2 (op, loc, (to) - (loc) - 3, arg, b)
+
+
+/* This is not an arbitrary limit: the arguments which represent offsets
+ into the pattern are two bytes long. So if 2^16 bytes turns out to
+ be too small, many things would have to change. */
+#define MAX_BUF_SIZE (1L << 16)
+
+
+/* Extend the buffer by twice its current size via realloc and
+ reset the pointers that pointed into the old block to point to the
+ correct places in the new one. If extending the buffer results in it
+ being larger than MAX_BUF_SIZE, then flag memory exhausted. */
+#define EXTEND_BUFFER() \
+ do { \
+ unsigned char *old_buffer = bufp->buffer; \
+ if (bufp->allocated == MAX_BUF_SIZE) \
+ return REG_ESIZE; \
+ bufp->allocated <<= 1; \
+ if (bufp->allocated > MAX_BUF_SIZE) \
+ bufp->allocated = MAX_BUF_SIZE; \
+ bufp->buffer = (unsigned char *) realloc (bufp->buffer, bufp->allocated);\
+ if (bufp->buffer == NULL) \
+ return REG_ESPACE; \
+ /* If the buffer moved, move all the pointers into it. */ \
+ if (old_buffer != bufp->buffer) \
+ { \
+ b = (b - old_buffer) + bufp->buffer; \
+ begalt = (begalt - old_buffer) + bufp->buffer; \
+ if (fixup_alt_jump) \
+ fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\
+ if (laststart) \
+ laststart = (laststart - old_buffer) + bufp->buffer; \
+ if (pending_exact) \
+ pending_exact = (pending_exact - old_buffer) + bufp->buffer; \
+ } \
+ } while (0)
+
+
+/* Since we have one byte reserved for the register number argument to
+ {start,stop}_memory, the maximum number of groups we can report
+ things about is what fits in that byte. */
+#define MAX_REGNUM 255
+
+/* But patterns can have more than `MAX_REGNUM' registers. We just
+ ignore the excess. */
+typedef unsigned regnum_t;
+
+
+/* Macros for the compile stack. */
+
+/* Since offsets can go either forwards or backwards, this type needs to
+ be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1. */
+typedef int pattern_offset_t;
+
+typedef struct
+{
+ pattern_offset_t begalt_offset;
+ pattern_offset_t fixup_alt_jump;
+ pattern_offset_t inner_group_offset;
+ pattern_offset_t laststart_offset;
+ regnum_t regnum;
+} compile_stack_elt_t;
+
+
+typedef struct
+{
+ compile_stack_elt_t *stack;
+ unsigned size;
+ unsigned avail; /* Offset of next open position. */
+} compile_stack_type;
+
+
+#define INIT_COMPILE_STACK_SIZE 32
+
+#define COMPILE_STACK_EMPTY (compile_stack.avail == 0)
+#define COMPILE_STACK_FULL (compile_stack.avail == compile_stack.size)
+
+/* The next available element. */
+#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail])
+
+
+/* Set the bit for character C in a list. */
+#define SET_LIST_BIT(c) \
+ (b[((unsigned char) (c)) / BYTEWIDTH] \
+ |= 1 << (((unsigned char) c) % BYTEWIDTH))
+
+
+/* Get the next unsigned number in the uncompiled pattern. */
+#define GET_UNSIGNED_NUMBER(num) \
+ { if (p != pend) \
+ { \
+ PATFETCH (c); \
+ while (ISDIGIT (c)) \
+ { \
+ if (num < 0) \
+ num = 0; \
+ num = num * 10 + c - '0'; \
+ if (p == pend) \
+ break; \
+ PATFETCH (c); \
+ } \
+ } \
+ }
+
+#define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */
+
+#define IS_CHAR_CLASS(string) \
+ (STREQ (string, "alpha") || STREQ (string, "upper") \
+ || STREQ (string, "lower") || STREQ (string, "digit") \
+ || STREQ (string, "alnum") || STREQ (string, "xdigit") \
+ || STREQ (string, "space") || STREQ (string, "print") \
+ || STREQ (string, "punct") || STREQ (string, "graph") \
+ || STREQ (string, "cntrl") || STREQ (string, "blank"))
+
+/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX.
+ Returns one of error codes defined in `regex.h', or zero for success.
+
+ Assumes the `allocated' (and perhaps `buffer') and `translate'
+ fields are set in BUFP on entry.
+
+ If it succeeds, results are put in BUFP (if it returns an error, the
+ contents of BUFP are undefined):
+ `buffer' is the compiled pattern;
+ `syntax' is set to SYNTAX;
+ `used' is set to the length of the compiled pattern;
+ `fastmap_accurate' is zero;
+ `re_nsub' is the number of subexpressions in PATTERN;
+ `not_bol' and `not_eol' are zero;
+
+ The `fastmap' and `newline_anchor' fields are neither
+ examined nor set. */
+
+static reg_errcode_t
+regex_compile (pattern, size, syntax, bufp)
+ const char *pattern;
+ int size;
+ reg_syntax_t syntax;
+ struct re_pattern_buffer *bufp;
+{
+ /* We fetch characters from PATTERN here. Even though PATTERN is
+ `char *' (i.e., signed), we declare these variables as unsigned, so
+ they can be reliably used as array indices. */
+ register unsigned char c, c1;
+
+ /* A random tempory spot in PATTERN. */
+ const char *p1;
+
+ /* Points to the end of the buffer, where we should append. */
+ register unsigned char *b;
+
+ /* Keeps track of unclosed groups. */
+ compile_stack_type compile_stack;
+
+ /* Points to the current (ending) position in the pattern. */
+ const char *p = pattern;
+ const char *pend = pattern + size;
+
+ /* How to translate the characters in the pattern. */
+ char *translate = bufp->translate;
+
+ /* Address of the count-byte of the most recently inserted `exactn'
+ command. This makes it possible to tell if a new exact-match
+ character can be added to that command or if the character requires
+ a new `exactn' command. */
+ unsigned char *pending_exact = 0;
+
+ /* Address of start of the most recently finished expression.
+ This tells, e.g., postfix * where to find the start of its
+ operand. Reset at the beginning of groups and alternatives. */
+ unsigned char *laststart = 0;
+
+ /* Address of beginning of regexp, or inside of last group. */
+ unsigned char *begalt;
+
+ /* Place in the uncompiled pattern (i.e., the {) to
+ which to go back if the interval is invalid. */
+ const char *beg_interval;
+
+ /* Address of the place where a forward jump should go to the end of
+ the containing expression. Each alternative of an `or' -- except the
+ last -- ends with a forward jump of this sort. */
+ unsigned char *fixup_alt_jump = 0;
+
+ /* Counts open-groups as they are encountered. Remembered for the
+ matching close-group on the compile stack, so the same register
+ number is put in the stop_memory as the start_memory. */
+ regnum_t regnum = 0;
+
+#ifdef DEBUG
+ DEBUG_PRINT1 ("\nCompiling pattern: ");
+ if (debug)
+ {
+ unsigned debug_count;
+
+ for (debug_count = 0; debug_count < size; debug_count++)
+ printchar (pattern[debug_count]);
+ putchar ('\n');
+ }
+#endif /* DEBUG */
+
+ /* Initialize the compile stack. */
+ compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t);
+ if (compile_stack.stack == NULL)
+ return REG_ESPACE;
+
+ compile_stack.size = INIT_COMPILE_STACK_SIZE;
+ compile_stack.avail = 0;
+
+ /* Initialize the pattern buffer. */
+ bufp->syntax = syntax;
+ bufp->fastmap_accurate = 0;
+ bufp->not_bol = bufp->not_eol = 0;
+
+ /* Set `used' to zero, so that if we return an error, the pattern
+ printer (for debugging) will think there's no pattern. We reset it
+ at the end. */
+ bufp->used = 0;
+
+ /* Always count groups, whether or not bufp->no_sub is set. */
+ bufp->re_nsub = 0;
+
+#if !defined (emacs) && !defined (SYNTAX_TABLE)
+ /* Initialize the syntax table. */
+ init_syntax_once ();
+#endif
+
+ if (bufp->allocated == 0)
+ {
+ if (bufp->buffer)
+ { /* If zero allocated, but buffer is non-null, try to realloc
+ enough space. This loses if buffer's address is bogus, but
+ that is the user's responsibility. */
+ RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char);
+ }
+ else
+ { /* Caller did not allocate a buffer. Do it for them. */
+ bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char);
+ }
+ if (!bufp->buffer) return REG_ESPACE;
+
+ bufp->allocated = INIT_BUF_SIZE;
+ }
+
+ begalt = b = bufp->buffer;
+
+ /* Loop through the uncompiled pattern until we're at the end. */
+ while (p != pend)
+ {
+ PATFETCH (c);
+
+ switch (c)
+ {
+ case '^':
+ {
+ if ( /* If at start of pattern, it's an operator. */
+ p == pattern + 1
+ /* If context independent, it's an operator. */
+ || syntax & RE_CONTEXT_INDEP_ANCHORS
+ /* Otherwise, depends on what's come before. */
+ || at_begline_loc_p (pattern, p, syntax))
+ BUF_PUSH (begline);
+ else
+ goto normal_char;
+ }
+ break;
+
+
+ case '$':
+ {
+ if ( /* If at end of pattern, it's an operator. */
+ p == pend
+ /* If context independent, it's an operator. */
+ || syntax & RE_CONTEXT_INDEP_ANCHORS
+ /* Otherwise, depends on what's next. */
+ || at_endline_loc_p (p, pend, syntax))
+ BUF_PUSH (endline);
+ else
+ goto normal_char;
+ }
+ break;
+
+
+ case '+':
+ case '?':
+ if ((syntax & RE_BK_PLUS_QM)
+ || (syntax & RE_LIMITED_OPS))
+ goto normal_char;
+ handle_plus:
+ case '*':
+ /* If there is no previous pattern... */
+ if (!laststart)
+ {
+ if (syntax & RE_CONTEXT_INVALID_OPS)
+ return REG_BADRPT;
+ else if (!(syntax & RE_CONTEXT_INDEP_OPS))
+ goto normal_char;
+ }
+
+ {
+ /* Are we optimizing this jump? */
+ boolean keep_string_p = false;
+
+ /* 1 means zero (many) matches is allowed. */
+ char zero_times_ok = 0, many_times_ok = 0;
+
+ /* If there is a sequence of repetition chars, collapse it
+ down to just one (the right one). We can't combine
+ interval operators with these because of, e.g., `a{2}*',
+ which should only match an even number of `a's. */
+
+ for (;;)
+ {
+ zero_times_ok |= c != '+';
+ many_times_ok |= c != '?';
+
+ if (p == pend)
+ break;
+
+ PATFETCH (c);
+
+ if (c == '*'
+ || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?')))
+ ;
+
+ else if (syntax & RE_BK_PLUS_QM && c == '\\')
+ {
+ if (p == pend) return REG_EESCAPE;
+
+ PATFETCH (c1);
+ if (!(c1 == '+' || c1 == '?'))
+ {
+ PATUNFETCH;
+ PATUNFETCH;
+ break;
+ }
+
+ c = c1;
+ }
+ else
+ {
+ PATUNFETCH;
+ break;
+ }
+
+ /* If we get here, we found another repeat character. */
+ }
+
+ /* Star, etc. applied to an empty pattern is equivalent
+ to an empty pattern. */
+ if (!laststart)
+ break;
+
+ /* Now we know whether or not zero matches is allowed
+ and also whether or not two or more matches is allowed. */
+ if (many_times_ok)
+ { /* More than one repetition is allowed, so put in at the
+ end a backward relative jump from `b' to before the next
+ jump we're going to put in below (which jumps from
+ laststart to after this jump).
+
+ But if we are at the `*' in the exact sequence `.*\n',
+ insert an unconditional jump backwards to the .,
+ instead of the beginning of the loop. This way we only
+ push a failure point once, instead of every time
+ through the loop. */
+ assert (p - 1 > pattern);
+
+ /* Allocate the space for the jump. */
+ GET_BUFFER_SPACE (3);
+
+ /* We know we are not at the first character of the pattern,
+ because laststart was nonzero. And we've already
+ incremented `p', by the way, to be the character after
+ the `*'. Do we have to do something analogous here
+ for null bytes, because of RE_DOT_NOT_NULL? */
+ if (TRANSLATE (*(p - 2)) == TRANSLATE ('.')
+ && p < pend && TRANSLATE (*p) == TRANSLATE ('\n')
+ && !(syntax & RE_DOT_NEWLINE))
+ { /* We have .*\n. */
+ STORE_JUMP (jump, b, laststart);
+ keep_string_p = true;
+ }
+ else
+ /* Anything else. */
+ STORE_JUMP (maybe_pop_jump, b, laststart - 3);
+
+ /* We've added more stuff to the buffer. */
+ b += 3;
+ }
+
+ /* On failure, jump from laststart to b + 3, which will be the
+ end of the buffer after this jump is inserted. */
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump
+ : on_failure_jump,
+ laststart, b + 3);
+ pending_exact = 0;
+ b += 3;
+
+ if (!zero_times_ok)
+ {
+ /* At least one repetition is required, so insert a
+ `dummy_failure_jump' before the initial
+ `on_failure_jump' instruction of the loop. This
+ effects a skip over that instruction the first time
+ we hit that loop. */
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6);
+ b += 3;
+ }
+ }
+ break;
+
+
+ case '.':
+ laststart = b;
+ BUF_PUSH (anychar);
+ break;
+
+
+ case '[':
+ {
+ boolean had_char_class = false;
+
+ if (p == pend) return REG_EBRACK;
+
+ /* Ensure that we have enough space to push a charset: the
+ opcode, the length count, and the bitset; 34 bytes in all. */
+ GET_BUFFER_SPACE (34);
+
+ laststart = b;
+
+ /* We test `*p == '^' twice, instead of using an if
+ statement, so we only need one BUF_PUSH. */
+ BUF_PUSH (*p == '^' ? charset_not : charset);
+ if (*p == '^')
+ p++;
+
+ /* Remember the first position in the bracket expression. */
+ p1 = p;
+
+ /* Push the number of bytes in the bitmap. */
+ BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
+
+ /* Clear the whole map. */
+ bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH);
+
+ /* charset_not matches newline according to a syntax bit. */
+ if ((re_opcode_t) b[-2] == charset_not
+ && (syntax & RE_HAT_LISTS_NOT_NEWLINE))
+ SET_LIST_BIT ('\n');
+
+ /* Read in characters and ranges, setting map bits. */
+ for (;;)
+ {
+ if (p == pend) return REG_EBRACK;
+
+ PATFETCH (c);
+
+ /* \ might escape characters inside [...] and [^...]. */
+ if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\')
+ {
+ if (p == pend) return REG_EESCAPE;
+
+ PATFETCH (c1);
+ SET_LIST_BIT (c1);
+ continue;
+ }
+
+ /* Could be the end of the bracket expression. If it's
+ not (i.e., when the bracket expression is `[]' so
+ far), the ']' character bit gets set way below. */
+ if (c == ']' && p != p1 + 1)
+ break;
+
+ /* Look ahead to see if it's a range when the last thing
+ was a character class. */
+ if (had_char_class && c == '-' && *p != ']')
+ return REG_ERANGE;
+
+ /* Look ahead to see if it's a range when the last thing
+ was a character: if this is a hyphen not at the
+ beginning or the end of a list, then it's the range
+ operator. */
+ if (c == '-'
+ && !(p - 2 >= pattern && p[-2] == '[')
+ && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^')
+ && *p != ']')
+ {
+ reg_errcode_t ret
+ = compile_range (&p, pend, translate, syntax, b);
+ if (ret != REG_NOERROR) return ret;
+ }
+
+ else if (p[0] == '-' && p[1] != ']')
+ { /* This handles ranges made up of characters only. */
+ reg_errcode_t ret;
+
+ /* Move past the `-'. */
+ PATFETCH (c1);
+
+ ret = compile_range (&p, pend, translate, syntax, b);
+ if (ret != REG_NOERROR) return ret;
+ }
+
+ /* See if we're at the beginning of a possible character
+ class. */
+
+ else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':')
+ { /* Leave room for the null. */
+ char str[CHAR_CLASS_MAX_LENGTH + 1];
+
+ PATFETCH (c);
+ c1 = 0;
+
+ /* If pattern is `[[:'. */
+ if (p == pend) return REG_EBRACK;
+
+ for (;;)
+ {
+ PATFETCH (c);
+ if (c == ':' || c == ']' || p == pend
+ || c1 == CHAR_CLASS_MAX_LENGTH)
+ break;
+ str[c1++] = c;
+ }
+ str[c1] = '\0';
+
+ /* If isn't a word bracketed by `[:' and:`]':
+ undo the ending character, the letters, and leave
+ the leading `:' and `[' (but set bits for them). */
+ if (c == ':' && *p == ']')
+ {
+ int ch;
+ boolean is_alnum = STREQ (str, "alnum");
+ boolean is_alpha = STREQ (str, "alpha");
+ boolean is_blank = STREQ (str, "blank");
+ boolean is_cntrl = STREQ (str, "cntrl");
+ boolean is_digit = STREQ (str, "digit");
+ boolean is_graph = STREQ (str, "graph");
+ boolean is_lower = STREQ (str, "lower");
+ boolean is_print = STREQ (str, "print");
+ boolean is_punct = STREQ (str, "punct");
+ boolean is_space = STREQ (str, "space");
+ boolean is_upper = STREQ (str, "upper");
+ boolean is_xdigit = STREQ (str, "xdigit");
+
+ if (!IS_CHAR_CLASS (str)) return REG_ECTYPE;
+
+ /* Throw away the ] at the end of the character
+ class. */
+ PATFETCH (c);
+
+ if (p == pend) return REG_EBRACK;
+
+ for (ch = 0; ch < 1 << BYTEWIDTH; ch++)
+ {
+ if ( (is_alnum && ISALNUM (ch))
+ || (is_alpha && ISALPHA (ch))
+ || (is_blank && ISBLANK (ch))
+ || (is_cntrl && ISCNTRL (ch))
+ || (is_digit && ISDIGIT (ch))
+ || (is_graph && ISGRAPH (ch))
+ || (is_lower && ISLOWER (ch))
+ || (is_print && ISPRINT (ch))
+ || (is_punct && ISPUNCT (ch))
+ || (is_space && ISSPACE (ch))
+ || (is_upper && ISUPPER (ch))
+ || (is_xdigit && ISXDIGIT (ch)))
+ SET_LIST_BIT (ch);
+ }
+ had_char_class = true;
+ }
+ else
+ {
+ c1++;
+ while (c1--)
+ PATUNFETCH;
+ SET_LIST_BIT ('[');
+ SET_LIST_BIT (':');
+ had_char_class = false;
+ }
+ }
+ else
+ {
+ had_char_class = false;
+ SET_LIST_BIT (c);
+ }
+ }
+
+ /* Discard any (non)matching list bytes that are all 0 at the
+ end of the map. Decrease the map-length byte too. */
+ while ((int) b[-1] > 0 && b[b[-1] - 1] == 0)
+ b[-1]--;
+ b += b[-1];
+ }
+ break;
+
+
+ case '(':
+ if (syntax & RE_NO_BK_PARENS)
+ goto handle_open;
+ else
+ goto normal_char;
+
+
+ case ')':
+ if (syntax & RE_NO_BK_PARENS)
+ goto handle_close;
+ else
+ goto normal_char;
+
+
+ case '\n':
+ if (syntax & RE_NEWLINE_ALT)
+ goto handle_alt;
+ else
+ goto normal_char;
+
+
+ case '|':
+ if (syntax & RE_NO_BK_VBAR)
+ goto handle_alt;
+ else
+ goto normal_char;
+
+
+ case '{':
+ if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES)
+ goto handle_interval;
+ else
+ goto normal_char;
+
+
+ case '\\':
+ if (p == pend) return REG_EESCAPE;
+
+ /* Do not translate the character after the \, so that we can
+ distinguish, e.g., \B from \b, even if we normally would
+ translate, e.g., B to b. */
+ PATFETCH_RAW (c);
+
+ switch (c)
+ {
+ case '(':
+ if (syntax & RE_NO_BK_PARENS)
+ goto normal_backslash;
+
+ handle_open:
+ bufp->re_nsub++;
+ regnum++;
+
+ if (COMPILE_STACK_FULL)
+ {
+ RETALLOC (compile_stack.stack, compile_stack.size << 1,
+ compile_stack_elt_t);
+ if (compile_stack.stack == NULL) return REG_ESPACE;
+
+ compile_stack.size <<= 1;
+ }
+
+ /* These are the values to restore when we hit end of this
+ group. They are all relative offsets, so that if the
+ whole pattern moves because of realloc, they will still
+ be valid. */
+ COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer;
+ COMPILE_STACK_TOP.fixup_alt_jump
+ = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0;
+ COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer;
+ COMPILE_STACK_TOP.regnum = regnum;
+
+ /* We will eventually replace the 0 with the number of
+ groups inner to this one. But do not push a
+ start_memory for groups beyond the last one we can
+ represent in the compiled pattern. */
+ if (regnum <= MAX_REGNUM)
+ {
+ COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2;
+ BUF_PUSH_3 (start_memory, regnum, 0);
+ }
+
+ compile_stack.avail++;
+
+ fixup_alt_jump = 0;
+ laststart = 0;
+ begalt = b;
+ break;
+
+
+ case ')':
+ if (syntax & RE_NO_BK_PARENS) goto normal_backslash;
+
+ if (COMPILE_STACK_EMPTY)
+ if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+ goto normal_backslash;
+ else
+ return REG_ERPAREN;
+
+ handle_close:
+ if (fixup_alt_jump)
+ { /* Push a dummy failure point at the end of the
+ alternative for a possible future
+ `pop_failure_jump' to pop. See comments at
+ `push_dummy_failure' in `re_match_2'. */
+ BUF_PUSH (push_dummy_failure);
+
+ /* We allocated space for this jump when we assigned
+ to `fixup_alt_jump', in the `handle_alt' case below. */
+ STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1);
+ }
+
+ /* See similar code for backslashed left paren above. */
+ if (COMPILE_STACK_EMPTY)
+ if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+ goto normal_char;
+ else
+ return REG_ERPAREN;
+
+ /* Since we just checked for an empty stack above, this
+ ``can't happen''. */
+ assert (compile_stack.avail != 0);
+ {
+ /* We don't just want to restore into `regnum', because
+ later groups should continue to be numbered higher,
+ as in `(ab)c(de)' -- the second group is #2. */
+ regnum_t this_group_regnum;
+
+ compile_stack.avail--;
+ begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset;
+ fixup_alt_jump
+ = COMPILE_STACK_TOP.fixup_alt_jump
+ ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1
+ : 0;
+ laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset;
+ this_group_regnum = COMPILE_STACK_TOP.regnum;
+
+ /* We're at the end of the group, so now we know how many
+ groups were inside this one. */
+ if (this_group_regnum <= MAX_REGNUM)
+ {
+ unsigned char *inner_group_loc
+ = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset;
+
+ *inner_group_loc = regnum - this_group_regnum;
+ BUF_PUSH_3 (stop_memory, this_group_regnum,
+ regnum - this_group_regnum);
+ }
+ }
+ break;
+
+
+ case '|': /* `\|'. */
+ if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR)
+ goto normal_backslash;
+ handle_alt:
+ if (syntax & RE_LIMITED_OPS)
+ goto normal_char;
+
+ /* Insert before the previous alternative a jump which
+ jumps to this alternative if the former fails. */
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (on_failure_jump, begalt, b + 6);
+ pending_exact = 0;
+ b += 3;
+
+ /* The alternative before this one has a jump after it
+ which gets executed if it gets matched. Adjust that
+ jump so it will jump to this alternative's analogous
+ jump (put in below, which in turn will jump to the next
+ (if any) alternative's such jump, etc.). The last such
+ jump jumps to the correct final destination. A picture:
+ _____ _____
+ | | | |
+ | v | v
+ a | b | c
+
+ If we are at `b', then fixup_alt_jump right now points to a
+ three-byte space after `a'. We'll put in the jump, set
+ fixup_alt_jump to right after `b', and leave behind three
+ bytes which we'll fill in when we get to after `c'. */
+
+ if (fixup_alt_jump)
+ STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
+
+ /* Mark and leave space for a jump after this alternative,
+ to be filled in later either by next alternative or
+ when know we're at the end of a series of alternatives. */
+ fixup_alt_jump = b;
+ GET_BUFFER_SPACE (3);
+ b += 3;
+
+ laststart = 0;
+ begalt = b;
+ break;
+
+
+ case '{':
+ /* If \{ is a literal. */
+ if (!(syntax & RE_INTERVALS)
+ /* If we're at `\{' and it's not the open-interval
+ operator. */
+ || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ || (p - 2 == pattern && p == pend))
+ goto normal_backslash;
+
+ handle_interval:
+ {
+ /* If got here, then the syntax allows intervals. */
+
+ /* At least (most) this many matches must be made. */
+ int lower_bound = -1, upper_bound = -1;
+
+ beg_interval = p - 1;
+
+ if (p == pend)
+ {
+ if (syntax & RE_NO_BK_BRACES)
+ goto unfetch_interval;
+ else
+ return REG_EBRACE;
+ }
+
+ GET_UNSIGNED_NUMBER (lower_bound);
+
+ if (c == ',')
+ {
+ GET_UNSIGNED_NUMBER (upper_bound);
+ if (upper_bound < 0) upper_bound = RE_DUP_MAX;
+ }
+ else
+ /* Interval such as `{1}' => match exactly once. */
+ upper_bound = lower_bound;
+
+ if (lower_bound < 0 || upper_bound > RE_DUP_MAX
+ || lower_bound > upper_bound)
+ {
+ if (syntax & RE_NO_BK_BRACES)
+ goto unfetch_interval;
+ else
+ return REG_BADBR;
+ }
+
+ if (!(syntax & RE_NO_BK_BRACES))
+ {
+ if (c != '\\') return REG_EBRACE;
+
+ PATFETCH (c);
+ }
+
+ if (c != '}')
+ {
+ if (syntax & RE_NO_BK_BRACES)
+ goto unfetch_interval;
+ else
+ return REG_BADBR;
+ }
+
+ /* We just parsed a valid interval. */
+
+ /* If it's invalid to have no preceding re. */
+ if (!laststart)
+ {
+ if (syntax & RE_CONTEXT_INVALID_OPS)
+ return REG_BADRPT;
+ else if (syntax & RE_CONTEXT_INDEP_OPS)
+ laststart = b;
+ else
+ goto unfetch_interval;
+ }
+
+ /* If the upper bound is zero, don't want to succeed at
+ all; jump from `laststart' to `b + 3', which will be
+ the end of the buffer after we insert the jump. */
+ if (upper_bound == 0)
+ {
+ GET_BUFFER_SPACE (3);
+ INSERT_JUMP (jump, laststart, b + 3);
+ b += 3;
+ }
+
+ /* Otherwise, we have a nontrivial interval. When
+ we're all done, the pattern will look like:
+ set_number_at <jump count> <upper bound>
+ set_number_at <succeed_n count> <lower bound>
+ succeed_n <after jump addr> <succed_n count>
+ <body of loop>
+ jump_n <succeed_n addr> <jump count>
+ (The upper bound and `jump_n' are omitted if
+ `upper_bound' is 1, though.) */
+ else
+ { /* If the upper bound is > 1, we need to insert
+ more at the end of the loop. */
+ unsigned nbytes = 10 + (upper_bound > 1) * 10;
+
+ GET_BUFFER_SPACE (nbytes);
+
+ /* Initialize lower bound of the `succeed_n', even
+ though it will be set during matching by its
+ attendant `set_number_at' (inserted next),
+ because `re_compile_fastmap' needs to know.
+ Jump to the `jump_n' we might insert below. */
+ INSERT_JUMP2 (succeed_n, laststart,
+ b + 5 + (upper_bound > 1) * 5,
+ lower_bound);
+ b += 5;
+
+ /* Code to initialize the lower bound. Insert
+ before the `succeed_n'. The `5' is the last two
+ bytes of this `set_number_at', plus 3 bytes of
+ the following `succeed_n'. */
+ insert_op2 (set_number_at, laststart, 5, lower_bound, b);
+ b += 5;
+
+ if (upper_bound > 1)
+ { /* More than one repetition is allowed, so
+ append a backward jump to the `succeed_n'
+ that starts this interval.
+
+ When we've reached this during matching,
+ we'll have matched the interval once, so
+ jump back only `upper_bound - 1' times. */
+ STORE_JUMP2 (jump_n, b, laststart + 5,
+ upper_bound - 1);
+ b += 5;
+
+ /* The location we want to set is the second
+ parameter of the `jump_n'; that is `b-2' as
+ an absolute address. `laststart' will be
+ the `set_number_at' we're about to insert;
+ `laststart+3' the number to set, the source
+ for the relative address. But we are
+ inserting into the middle of the pattern --
+ so everything is getting moved up by 5.
+ Conclusion: (b - 2) - (laststart + 3) + 5,
+ i.e., b - laststart.
+
+ We insert this at the beginning of the loop
+ so that if we fail during matching, we'll
+ reinitialize the bounds. */
+ insert_op2 (set_number_at, laststart, b - laststart,
+ upper_bound - 1, b);
+ b += 5;
+ }
+ }
+ pending_exact = 0;
+ beg_interval = NULL;
+ }
+ break;
+
+ unfetch_interval:
+ /* If an invalid interval, match the characters as literals. */
+ assert (beg_interval);
+ p = beg_interval;
+ beg_interval = NULL;
+
+ /* normal_char and normal_backslash need `c'. */
+ PATFETCH (c);
+
+ if (!(syntax & RE_NO_BK_BRACES))
+ {
+ if (p > pattern && p[-1] == '\\')
+ goto normal_backslash;
+ }
+ goto normal_char;
+
+#ifdef emacs
+ /* There is no way to specify the before_dot and after_dot
+ operators. rms says this is ok. --karl */
+ case '=':
+ BUF_PUSH (at_dot);
+ break;
+
+ case 's':
+ laststart = b;
+ PATFETCH (c);
+ BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]);
+ break;
+
+ case 'S':
+ laststart = b;
+ PATFETCH (c);
+ BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]);
+ break;
+#endif /* emacs */
+
+
+ case 'w':
+ laststart = b;
+ BUF_PUSH (wordchar);
+ break;
+
+
+ case 'W':
+ laststart = b;
+ BUF_PUSH (notwordchar);
+ break;
+
+
+ case '<':
+ BUF_PUSH (wordbeg);
+ break;
+
+ case '>':
+ BUF_PUSH (wordend);
+ break;
+
+ case 'b':
+ BUF_PUSH (wordbound);
+ break;
+
+ case 'B':
+ BUF_PUSH (notwordbound);
+ break;
+
+ case '`':
+ BUF_PUSH (begbuf);
+ break;
+
+ case '\'':
+ BUF_PUSH (endbuf);
+ break;
+
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ if (syntax & RE_NO_BK_REFS)
+ goto normal_char;
+
+ c1 = c - '0';
+
+ if (c1 > regnum)
+ return REG_ESUBREG;
+
+ /* Can't back reference to a subexpression if inside of it. */
+ if (group_in_compile_stack (compile_stack, c1))
+ goto normal_char;
+
+ laststart = b;
+ BUF_PUSH_2 (duplicate, c1);
+ break;
+
+
+ case '+':
+ case '?':
+ if (syntax & RE_BK_PLUS_QM)
+ goto handle_plus;
+ else
+ goto normal_backslash;
+
+ default:
+ normal_backslash:
+ /* You might think it would be useful for \ to mean
+ not to translate; but if we don't translate it
+ it will never match anything. */
+ c = TRANSLATE (c);
+ goto normal_char;
+ }
+ break;
+
+
+ default:
+ /* Expects the character in `c'. */
+ normal_char:
+ /* If no exactn currently being built. */
+ if (!pending_exact
+
+ /* If last exactn not at current position. */
+ || pending_exact + *pending_exact + 1 != b
+
+ /* We have only one byte following the exactn for the count. */
+ || *pending_exact == (1 << BYTEWIDTH) - 1
+
+ /* If followed by a repetition operator. */
+ || *p == '*' || *p == '^'
+ || ((syntax & RE_BK_PLUS_QM)
+ ? *p == '\\' && (p[1] == '+' || p[1] == '?')
+ : (*p == '+' || *p == '?'))
+ || ((syntax & RE_INTERVALS)
+ && ((syntax & RE_NO_BK_BRACES)
+ ? *p == '{'
+ : (p[0] == '\\' && p[1] == '{'))))
+ {
+ /* Start building a new exactn. */
+
+ laststart = b;
+
+ BUF_PUSH_2 (exactn, 0);
+ pending_exact = b - 1;
+ }
+
+ BUF_PUSH (c);
+ (*pending_exact)++;
+ break;
+ } /* switch (c) */
+ } /* while p != pend */
+
+
+ /* Through the pattern now. */
+
+ if (fixup_alt_jump)
+ STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
+
+ if (!COMPILE_STACK_EMPTY)
+ return REG_EPAREN;
+
+ free (compile_stack.stack);
+
+ /* We have succeeded; set the length of the buffer. */
+ bufp->used = b - bufp->buffer;
+
+#ifdef DEBUG
+ if (debug)
+ {
+ DEBUG_PRINT1 ("\nCompiled pattern: ");
+ print_compiled_pattern (bufp);
+ }
+#endif /* DEBUG */
+
+ return REG_NOERROR;
+} /* regex_compile */
+
+/* Subroutines for `regex_compile'. */
+
+/* Store OP at LOC followed by two-byte integer parameter ARG. */
+
+static void
+store_op1 (op, loc, arg)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg;
+{
+ *loc = (unsigned char) op;
+ STORE_NUMBER (loc + 1, arg);
+}
+
+
+/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2. */
+
+static void
+store_op2 (op, loc, arg1, arg2)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg1, arg2;
+{
+ *loc = (unsigned char) op;
+ STORE_NUMBER (loc + 1, arg1);
+ STORE_NUMBER (loc + 3, arg2);
+}
+
+
+/* Copy the bytes from LOC to END to open up three bytes of space at LOC
+ for OP followed by two-byte integer parameter ARG. */
+
+static void
+insert_op1 (op, loc, arg, end)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg;
+ unsigned char *end;
+{
+ register unsigned char *pfrom = end;
+ register unsigned char *pto = end + 3;
+
+ while (pfrom != loc)
+ *--pto = *--pfrom;
+
+ store_op1 (op, loc, arg);
+}
+
+
+/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2. */
+
+static void
+insert_op2 (op, loc, arg1, arg2, end)
+ re_opcode_t op;
+ unsigned char *loc;
+ int arg1, arg2;
+ unsigned char *end;
+{
+ register unsigned char *pfrom = end;
+ register unsigned char *pto = end + 5;
+
+ while (pfrom != loc)
+ *--pto = *--pfrom;
+
+ store_op2 (op, loc, arg1, arg2);
+}
+
+
+/* P points to just after a ^ in PATTERN. Return true if that ^ comes
+ after an alternative or a begin-subexpression. We assume there is at
+ least one character before the ^. */
+
+static boolean
+at_begline_loc_p (pattern, p, syntax)
+ const char *pattern, *p;
+ reg_syntax_t syntax;
+{
+ const char *prev = p - 2;
+ boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\';
+
+ return
+ /* After a subexpression? */
+ (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash))
+ /* After an alternative? */
+ || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash));
+}
+
+
+/* The dual of at_begline_loc_p. This one is for $. We assume there is
+ at least one character after the $, i.e., `P < PEND'. */
+
+static boolean
+at_endline_loc_p (p, pend, syntax)
+ const char *p, *pend;
+ int syntax;
+{
+ const char *next = p;
+ boolean next_backslash = *next == '\\';
+ const char *next_next = p + 1 < pend ? p + 1 : NULL;
+
+ return
+ /* Before a subexpression? */
+ (syntax & RE_NO_BK_PARENS ? *next == ')'
+ : next_backslash && next_next && *next_next == ')')
+ /* Before an alternative? */
+ || (syntax & RE_NO_BK_VBAR ? *next == '|'
+ : next_backslash && next_next && *next_next == '|');
+}
+
+
+/* Returns true if REGNUM is in one of COMPILE_STACK's elements and
+ false if it's not. */
+
+static boolean
+group_in_compile_stack (compile_stack, regnum)
+ compile_stack_type compile_stack;
+ regnum_t regnum;
+{
+ int this_element;
+
+ for (this_element = compile_stack.avail - 1;
+ this_element >= 0;
+ this_element--)
+ if (compile_stack.stack[this_element].regnum == regnum)
+ return true;
+
+ return false;
+}
+
+
+/* Read the ending character of a range (in a bracket expression) from the
+ uncompiled pattern *P_PTR (which ends at PEND). We assume the
+ starting character is in `P[-2]'. (`P[-1]' is the character `-'.)
+ Then we set the translation of all bits between the starting and
+ ending characters (inclusive) in the compiled pattern B.
+
+ Return an error code.
+
+ We use these short variable names so we can use the same macros as
+ `regex_compile' itself. */
+
+static reg_errcode_t
+compile_range (p_ptr, pend, translate, syntax, b)
+ const char **p_ptr, *pend;
+ char *translate;
+ reg_syntax_t syntax;
+ unsigned char *b;
+{
+ unsigned this_char;
+
+ const char *p = *p_ptr;
+ int range_start, range_end;
+
+ if (p == pend)
+ return REG_ERANGE;
+
+ /* Even though the pattern is a signed `char *', we need to fetch
+ with unsigned char *'s; if the high bit of the pattern character
+ is set, the range endpoints will be negative if we fetch using a
+ signed char *.
+
+ We also want to fetch the endpoints without translating them; the
+ appropriate translation is done in the bit-setting loop below. */
+ range_start = ((unsigned char *) p)[-2];
+ range_end = ((unsigned char *) p)[0];
+
+ /* Have to increment the pointer into the pattern string, so the
+ caller isn't still at the ending character. */
+ (*p_ptr)++;
+
+ /* If the start is after the end, the range is empty. */
+ if (range_start > range_end)
+ return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR;
+
+ /* Here we see why `this_char' has to be larger than an `unsigned
+ char' -- the range is inclusive, so if `range_end' == 0xff
+ (assuming 8-bit characters), we would otherwise go into an infinite
+ loop, since all characters <= 0xff. */
+ for (this_char = range_start; this_char <= range_end; this_char++)
+ {
+ SET_LIST_BIT (TRANSLATE (this_char));
+ }
+
+ return REG_NOERROR;
+}
+
+/* Failure stack declarations and macros; both re_compile_fastmap and
+ re_match_2 use a failure stack. These have to be macros because of
+ REGEX_ALLOCATE. */
+
+
+/* Number of failure points for which to initially allocate space
+ when matching. If this number is exceeded, we allocate more
+ space, so it is not a hard limit. */
+#ifndef INIT_FAILURE_ALLOC
+#define INIT_FAILURE_ALLOC 5
+#endif
+
+/* Roughly the maximum number of failure points on the stack. Would be
+ exactly that if always used MAX_FAILURE_SPACE each time we failed.
+ This is a variable only so users of regex can assign to it; we never
+ change it ourselves. */
+int re_max_failures = 2000;
+
+typedef const unsigned char *fail_stack_elt_t;
+
+typedef struct
+{
+ fail_stack_elt_t *stack;
+ unsigned size;
+ unsigned avail; /* Offset of next open position. */
+} fail_stack_type;
+
+#define FAIL_STACK_EMPTY() (fail_stack.avail == 0)
+#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0)
+#define FAIL_STACK_FULL() (fail_stack.avail == fail_stack.size)
+#define FAIL_STACK_TOP() (fail_stack.stack[fail_stack.avail])
+
+
+/* Initialize `fail_stack'. Do `return -2' if the alloc fails. */
+
+#define INIT_FAIL_STACK() \
+ do { \
+ fail_stack.stack = (fail_stack_elt_t *) \
+ REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \
+ \
+ if (fail_stack.stack == NULL) \
+ return -2; \
+ \
+ fail_stack.size = INIT_FAILURE_ALLOC; \
+ fail_stack.avail = 0; \
+ } while (0)
+
+
+/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items.
+
+ Return 1 if succeeds, and 0 if either ran out of memory
+ allocating space for it or it was already too large.
+
+ REGEX_REALLOCATE requires `destination' be declared. */
+
+#define DOUBLE_FAIL_STACK(fail_stack) \
+ ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS \
+ ? 0 \
+ : ((fail_stack).stack = (fail_stack_elt_t *) \
+ REGEX_REALLOCATE ((fail_stack).stack, \
+ (fail_stack).size * sizeof (fail_stack_elt_t), \
+ ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)), \
+ \
+ (fail_stack).stack == NULL \
+ ? 0 \
+ : ((fail_stack).size <<= 1, \
+ 1)))
+
+
+/* Push PATTERN_OP on FAIL_STACK.
+
+ Return 1 if was able to do so and 0 if ran out of memory allocating
+ space to do so. */
+#define PUSH_PATTERN_OP(pattern_op, fail_stack) \
+ ((FAIL_STACK_FULL () \
+ && !DOUBLE_FAIL_STACK (fail_stack)) \
+ ? 0 \
+ : ((fail_stack).stack[(fail_stack).avail++] = pattern_op, \
+ 1))
+
+/* This pushes an item onto the failure stack. Must be a four-byte
+ value. Assumes the variable `fail_stack'. Probably should only
+ be called from within `PUSH_FAILURE_POINT'. */
+#define PUSH_FAILURE_ITEM(item) \
+ fail_stack.stack[fail_stack.avail++] = (fail_stack_elt_t) item
+
+/* The complement operation. Assumes `fail_stack' is nonempty. */
+#define POP_FAILURE_ITEM() fail_stack.stack[--fail_stack.avail]
+
+/* Used to omit pushing failure point id's when we're not debugging. */
+#ifdef DEBUG
+#define DEBUG_PUSH PUSH_FAILURE_ITEM
+#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_ITEM ()
+#else
+#define DEBUG_PUSH(item)
+#define DEBUG_POP(item_addr)
+#endif
+
+
+/* Push the information about the state we will need
+ if we ever fail back to it.
+
+ Requires variables fail_stack, regstart, regend, reg_info, and
+ num_regs be declared. DOUBLE_FAIL_STACK requires `destination' be
+ declared.
+
+ Does `return FAILURE_CODE' if runs out of memory. */
+
+#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \
+ do { \
+ char *destination; \
+ /* Must be int, so when we don't save any registers, the arithmetic \
+ of 0 + -1 isn't done as unsigned. */ \
+ int this_reg; \
+ \
+ DEBUG_STATEMENT (failure_id++); \
+ DEBUG_STATEMENT (nfailure_points_pushed++); \
+ DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \
+ DEBUG_PRINT2 (" Before push, next avail: %d\n", (fail_stack).avail);\
+ DEBUG_PRINT2 (" size: %d\n", (fail_stack).size);\
+ \
+ DEBUG_PRINT2 (" slots needed: %d\n", NUM_FAILURE_ITEMS); \
+ DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \
+ \
+ /* Ensure we have enough space allocated for what we will push. */ \
+ while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \
+ { \
+ if (!DOUBLE_FAIL_STACK (fail_stack)) \
+ return failure_code; \
+ \
+ DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \
+ (fail_stack).size); \
+ DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\
+ } \
+ \
+ /* Push the info, starting with the registers. */ \
+ DEBUG_PRINT1 ("\n"); \
+ \
+ for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \
+ this_reg++) \
+ { \
+ DEBUG_PRINT2 (" Pushing reg: %d\n", this_reg); \
+ DEBUG_STATEMENT (num_regs_pushed++); \
+ \
+ DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \
+ PUSH_FAILURE_ITEM (regstart[this_reg]); \
+ \
+ DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \
+ PUSH_FAILURE_ITEM (regend[this_reg]); \
+ \
+ DEBUG_PRINT2 (" info: 0x%x\n ", reg_info[this_reg]); \
+ DEBUG_PRINT2 (" match_null=%d", \
+ REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" matched_something=%d", \
+ MATCHED_SOMETHING (reg_info[this_reg])); \
+ DEBUG_PRINT2 (" ever_matched=%d", \
+ EVER_MATCHED_SOMETHING (reg_info[this_reg])); \
+ DEBUG_PRINT1 ("\n"); \
+ PUSH_FAILURE_ITEM (reg_info[this_reg].word); \
+ } \
+ \
+ DEBUG_PRINT2 (" Pushing low active reg: %d\n", lowest_active_reg);\
+ PUSH_FAILURE_ITEM (lowest_active_reg); \
+ \
+ DEBUG_PRINT2 (" Pushing high active reg: %d\n", highest_active_reg);\
+ PUSH_FAILURE_ITEM (highest_active_reg); \
+ \
+ DEBUG_PRINT2 (" Pushing pattern 0x%x: ", pattern_place); \
+ DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \
+ PUSH_FAILURE_ITEM (pattern_place); \
+ \
+ DEBUG_PRINT2 (" Pushing string 0x%x: `", string_place); \
+ DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \
+ size2); \
+ DEBUG_PRINT1 ("'\n"); \
+ PUSH_FAILURE_ITEM (string_place); \
+ \
+ DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \
+ DEBUG_PUSH (failure_id); \
+ } while (0)
+
+/* This is the number of items that are pushed and popped on the stack
+ for each register. */
+#define NUM_REG_ITEMS 3
+
+/* Individual items aside from the registers. */
+#ifdef DEBUG
+#define NUM_NONREG_ITEMS 5 /* Includes failure point id. */
+#else
+#define NUM_NONREG_ITEMS 4
+#endif
+
+/* We push at most this many items on the stack. */
+#define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS)
+
+/* We actually push this many items. */
+#define NUM_FAILURE_ITEMS \
+ ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS \
+ + NUM_NONREG_ITEMS)
+
+/* How many items can still be added to the stack without overflowing it. */
+#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail)
+
+
+/* Pops what PUSH_FAIL_STACK pushes.
+
+ We restore into the parameters, all of which should be lvalues:
+ STR -- the saved data position.
+ PAT -- the saved pattern position.
+ LOW_REG, HIGH_REG -- the highest and lowest active registers.
+ REGSTART, REGEND -- arrays of string positions.
+ REG_INFO -- array of information about each subexpression.
+
+ Also assumes the variables `fail_stack' and (if debugging), `bufp',
+ `pend', `string1', `size1', `string2', and `size2'. */
+
+#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\
+{ \
+ DEBUG_STATEMENT (fail_stack_elt_t failure_id;) \
+ int this_reg; \
+ const unsigned char *string_temp; \
+ \
+ assert (!FAIL_STACK_EMPTY ()); \
+ \
+ /* Remove failure points and point to how many regs pushed. */ \
+ DEBUG_PRINT1 ("POP_FAILURE_POINT:\n"); \
+ DEBUG_PRINT2 (" Before pop, next avail: %d\n", fail_stack.avail); \
+ DEBUG_PRINT2 (" size: %d\n", fail_stack.size); \
+ \
+ assert (fail_stack.avail >= NUM_NONREG_ITEMS); \
+ \
+ DEBUG_POP (&failure_id); \
+ DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id); \
+ \
+ /* If the saved string location is NULL, it came from an \
+ on_failure_keep_string_jump opcode, and we want to throw away the \
+ saved NULL, thus retaining our current position in the string. */ \
+ string_temp = POP_FAILURE_ITEM (); \
+ if (string_temp != NULL) \
+ str = (const char *) string_temp; \
+ \
+ DEBUG_PRINT2 (" Popping string 0x%x: `", str); \
+ DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \
+ DEBUG_PRINT1 ("'\n"); \
+ \
+ pat = (unsigned char *) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" Popping pattern 0x%x: ", pat); \
+ DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \
+ \
+ /* Restore register info. */ \
+ high_reg = (unsigned) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" Popping high active reg: %d\n", high_reg); \
+ \
+ low_reg = (unsigned) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" Popping low active reg: %d\n", low_reg); \
+ \
+ for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \
+ { \
+ DEBUG_PRINT2 (" Popping reg: %d\n", this_reg); \
+ \
+ reg_info[this_reg].word = POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" info: 0x%x\n", reg_info[this_reg]); \
+ \
+ regend[this_reg] = (const char *) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" end: 0x%x\n", regend[this_reg]); \
+ \
+ regstart[this_reg] = (const char *) POP_FAILURE_ITEM (); \
+ DEBUG_PRINT2 (" start: 0x%x\n", regstart[this_reg]); \
+ } \
+ \
+ DEBUG_STATEMENT (nfailure_points_popped++); \
+} /* POP_FAILURE_POINT */
+
+/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in
+ BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible
+ characters can start a string that matches the pattern. This fastmap
+ is used by re_search to skip quickly over impossible starting points.
+
+ The caller must supply the address of a (1 << BYTEWIDTH)-byte data
+ area as BUFP->fastmap.
+
+ We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in
+ the pattern buffer.
+
+ Returns 0 if we succeed, -2 if an internal error. */
+
+int
+re_compile_fastmap (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ int j, k;
+ fail_stack_type fail_stack;
+#ifndef REGEX_MALLOC
+ char *destination;
+#endif
+ /* We don't push any register information onto the failure stack. */
+ unsigned num_regs = 0;
+
+ register char *fastmap = bufp->fastmap;
+ unsigned char *pattern = bufp->buffer;
+ unsigned long size = bufp->used;
+ const unsigned char *p = pattern;
+ register unsigned char *pend = pattern + size;
+
+ /* Assume that each path through the pattern can be null until
+ proven otherwise. We set this false at the bottom of switch
+ statement, to which we get only if a particular path doesn't
+ match the empty string. */
+ boolean path_can_be_null = true;
+
+ /* We aren't doing a `succeed_n' to begin with. */
+ boolean succeed_n_p = false;
+
+ assert (fastmap != NULL && p != NULL);
+
+ INIT_FAIL_STACK ();
+ bzero (fastmap, 1 << BYTEWIDTH); /* Assume nothing's valid. */
+ bufp->fastmap_accurate = 1; /* It will be when we're done. */
+ bufp->can_be_null = 0;
+
+ while (p != pend || !FAIL_STACK_EMPTY ())
+ {
+ if (p == pend)
+ {
+ bufp->can_be_null |= path_can_be_null;
+
+ /* Reset for next path. */
+ path_can_be_null = true;
+
+ p = fail_stack.stack[--fail_stack.avail];
+ }
+
+ /* We should never be about to go beyond the end of the pattern. */
+ assert (p < pend);
+
+#ifdef SWITCH_ENUM_BUG
+ switch ((int) ((re_opcode_t) *p++))
+#else
+ switch ((re_opcode_t) *p++)
+#endif
+ {
+
+ /* I guess the idea here is to simply not bother with a fastmap
+ if a backreference is used, since it's too hard to figure out
+ the fastmap for the corresponding group. Setting
+ `can_be_null' stops `re_search_2' from using the fastmap, so
+ that is all we do. */
+ case duplicate:
+ bufp->can_be_null = 1;
+ return 0;
+
+
+ /* Following are the cases which match a character. These end
+ with `break'. */
+
+ case exactn:
+ fastmap[p[1]] = 1;
+ break;
+
+
+ case charset:
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
+ fastmap[j] = 1;
+ break;
+
+
+ case charset_not:
+ /* Chars beyond end of map must be allowed. */
+ for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
+ fastmap[j] = 1;
+
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
+ fastmap[j] = 1;
+ break;
+
+
+ case wordchar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) == Sword)
+ fastmap[j] = 1;
+ break;
+
+
+ case notwordchar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) != Sword)
+ fastmap[j] = 1;
+ break;
+
+
+ case anychar:
+ /* `.' matches anything ... */
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ fastmap[j] = 1;
+
+ /* ... except perhaps newline. */
+ if (!(bufp->syntax & RE_DOT_NEWLINE))
+ fastmap['\n'] = 0;
+
+ /* Return if we have already set `can_be_null'; if we have,
+ then the fastmap is irrelevant. Something's wrong here. */
+ else if (bufp->can_be_null)
+ return 0;
+
+ /* Otherwise, have to check alternative paths. */
+ break;
+
+
+#ifdef emacs
+ case syntaxspec:
+ k = *p++;
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) == (enum syntaxcode) k)
+ fastmap[j] = 1;
+ break;
+
+
+ case notsyntaxspec:
+ k = *p++;
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) != (enum syntaxcode) k)
+ fastmap[j] = 1;
+ break;
+
+
+ /* All cases after this match the empty string. These end with
+ `continue'. */
+
+
+ case before_dot:
+ case at_dot:
+ case after_dot:
+ continue;
+#endif /* not emacs */
+
+
+ case no_op:
+ case begline:
+ case endline:
+ case begbuf:
+ case endbuf:
+ case wordbound:
+ case notwordbound:
+ case wordbeg:
+ case wordend:
+ case push_dummy_failure:
+ continue;
+
+
+ case jump_n:
+ case pop_failure_jump:
+ case maybe_pop_jump:
+ case jump:
+ case jump_past_alt:
+ case dummy_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (j, p);
+ p += j;
+ if (j > 0)
+ continue;
+
+ /* Jump backward implies we just went through the body of a
+ loop and matched nothing. Opcode jumped to should be
+ `on_failure_jump' or `succeed_n'. Just treat it like an
+ ordinary jump. For a * loop, it has pushed its failure
+ point already; if so, discard that as redundant. */
+ if ((re_opcode_t) *p != on_failure_jump
+ && (re_opcode_t) *p != succeed_n)
+ continue;
+
+ p++;
+ EXTRACT_NUMBER_AND_INCR (j, p);
+ p += j;
+
+ /* If what's on the stack is where we are now, pop it. */
+ if (!FAIL_STACK_EMPTY ()
+ && fail_stack.stack[fail_stack.avail - 1] == p)
+ fail_stack.avail--;
+
+ continue;
+
+
+ case on_failure_jump:
+ case on_failure_keep_string_jump:
+ handle_on_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (j, p);
+
+ /* For some patterns, e.g., `(a?)?', `p+j' here points to the
+ end of the pattern. We don't want to push such a point,
+ since when we restore it above, entering the switch will
+ increment `p' past the end of the pattern. We don't need
+ to push such a point since we obviously won't find any more
+ fastmap entries beyond `pend'. Such a pattern can match
+ the null string, though. */
+ if (p + j < pend)
+ {
+ if (!PUSH_PATTERN_OP (p + j, fail_stack))
+ return -2;
+ }
+ else
+ bufp->can_be_null = 1;
+
+ if (succeed_n_p)
+ {
+ EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */
+ succeed_n_p = false;
+ }
+
+ continue;
+
+
+ case succeed_n:
+ /* Get to the number of times to succeed. */
+ p += 2;
+
+ /* Increment p past the n for when k != 0. */
+ EXTRACT_NUMBER_AND_INCR (k, p);
+ if (k == 0)
+ {
+ p -= 4;
+ succeed_n_p = true; /* Spaghetti code alert. */
+ goto handle_on_failure_jump;
+ }
+ continue;
+
+
+ case set_number_at:
+ p += 4;
+ continue;
+
+
+ case start_memory:
+ case stop_memory:
+ p += 2;
+ continue;
+
+
+ default:
+ abort (); /* We have listed all the cases. */
+ } /* switch *p++ */
+
+ /* Getting here means we have found the possible starting
+ characters for one path of the pattern -- and that the empty
+ string does not match. We need not follow this path further.
+ Instead, look at the next alternative (remembered on the
+ stack), or quit if no more. The test at the top of the loop
+ does these things. */
+ path_can_be_null = false;
+ p = pend;
+ } /* while p */
+
+ /* Set `can_be_null' for the last path (also the first path, if the
+ pattern is empty). */
+ bufp->can_be_null |= path_can_be_null;
+ return 0;
+} /* re_compile_fastmap */
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+ ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use
+ this memory for recording register information. STARTS and ENDS
+ must be allocated using the malloc library routine, and must each
+ be at least NUM_REGS * sizeof (regoff_t) bytes long.
+
+ If NUM_REGS == 0, then subsequent matches should allocate their own
+ register data.
+
+ Unless this function is called, the first search or match using
+ PATTERN_BUFFER will allocate its own register data, without
+ freeing the old data. */
+
+void
+re_set_registers (bufp, regs, num_regs, starts, ends)
+ struct re_pattern_buffer *bufp;
+ struct re_registers *regs;
+ unsigned num_regs;
+ regoff_t *starts, *ends;
+{
+ if (num_regs)
+ {
+ bufp->regs_allocated = REGS_REALLOCATE;
+ regs->num_regs = num_regs;
+ regs->start = starts;
+ regs->end = ends;
+ }
+ else
+ {
+ bufp->regs_allocated = REGS_UNALLOCATED;
+ regs->num_regs = 0;
+ regs->start = regs->end = (regoff_t) 0;
+ }
+}
+
+/* Searching routines. */
+
+/* Like re_search_2, below, but only one string is specified, and
+ doesn't let you say where to stop matching. */
+
+int
+re_search (bufp, string, size, startpos, range, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ int size, startpos, range;
+ struct re_registers *regs;
+{
+ return re_search_2 (bufp, NULL, 0, string, size, startpos, range,
+ regs, size);
+}
+
+
+/* Using the compiled pattern in BUFP->buffer, first tries to match the
+ virtual concatenation of STRING1 and STRING2, starting first at index
+ STARTPOS, then at STARTPOS + 1, and so on.
+
+ STRING1 and STRING2 have length SIZE1 and SIZE2, respectively.
+
+ RANGE is how far to scan while trying to match. RANGE = 0 means try
+ only at STARTPOS; in general, the last start tried is STARTPOS +
+ RANGE.
+
+ In REGS, return the indices of the virtual concatenation of STRING1
+ and STRING2 that matched the entire BUFP->buffer and its contained
+ subexpressions.
+
+ Do not consider matching one past the index STOP in the virtual
+ concatenation of STRING1 and STRING2.
+
+ We return either the position in the strings at which the match was
+ found, -1 if no match, or -2 if error (such as failure
+ stack overflow). */
+
+int
+re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ int size1, size2;
+ int startpos;
+ int range;
+ struct re_registers *regs;
+ int stop;
+{
+ int val;
+ register char *fastmap = bufp->fastmap;
+ register char *translate = bufp->translate;
+ int total_size = size1 + size2;
+ int endpos = startpos + range;
+
+ /* Check for out-of-range STARTPOS. */
+ if (startpos < 0 || startpos > total_size)
+ return -1;
+
+ /* Fix up RANGE if it might eventually take us outside
+ the virtual concatenation of STRING1 and STRING2. */
+ if (endpos < -1)
+ range = -1 - startpos;
+ else if (endpos > total_size)
+ range = total_size - startpos;
+
+ /* If the search isn't to be a backwards one, don't waste time in a
+ search for a pattern that must be anchored. */
+ if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf && range > 0)
+ {
+ if (startpos > 0)
+ return -1;
+ else
+ range = 1;
+ }
+
+ /* Update the fastmap now if not correct already. */
+ if (fastmap && !bufp->fastmap_accurate)
+ if (re_compile_fastmap (bufp) == -2)
+ return -2;
+
+ /* Loop through the string, looking for a place to start matching. */
+ for (;;)
+ {
+ /* If a fastmap is supplied, skip quickly over characters that
+ cannot be the start of a match. If the pattern can match the
+ null string, however, we don't need to skip characters; we want
+ the first null string. */
+ if (fastmap && startpos < total_size && !bufp->can_be_null)
+ {
+ if (range > 0) /* Searching forwards. */
+ {
+ register const char *d;
+ register int lim = 0;
+ int irange = range;
+
+ if (startpos < size1 && startpos + range >= size1)
+ lim = range - (size1 - startpos);
+
+ d = (startpos >= size1 ? string2 - size1 : string1) + startpos;
+
+ /* Written out as an if-else to avoid testing `translate'
+ inside the loop. */
+ if (translate)
+ while (range > lim
+ && !fastmap[(unsigned char)
+ translate[(unsigned char) *d++]])
+ range--;
+ else
+ while (range > lim && !fastmap[(unsigned char) *d++])
+ range--;
+
+ startpos += irange - range;
+ }
+ else /* Searching backwards. */
+ {
+ register char c = (size1 == 0 || startpos >= size1
+ ? string2[startpos - size1]
+ : string1[startpos]);
+
+ if (!fastmap[(unsigned char) TRANSLATE (c)])
+ goto advance;
+ }
+ }
+
+ /* If can't match the null string, and that's all we have left, fail. */
+ if (range >= 0 && startpos == total_size && fastmap
+ && !bufp->can_be_null)
+ return -1;
+
+ val = re_match_2 (bufp, string1, size1, string2, size2,
+ startpos, regs, stop);
+ if (val >= 0)
+ return startpos;
+
+ if (val == -2)
+ return -2;
+
+ advance:
+ if (!range)
+ break;
+ else if (range > 0)
+ {
+ range--;
+ startpos++;
+ }
+ else
+ {
+ range++;
+ startpos--;
+ }
+ }
+ return -1;
+} /* re_search_2 */
+
+/* Declarations and macros for re_match_2. */
+
+static int bcmp_translate ();
+static boolean alt_match_null_string_p (),
+ common_op_match_null_string_p (),
+ group_match_null_string_p ();
+
+/* Structure for per-register (a.k.a. per-group) information.
+ This must not be longer than one word, because we push this value
+ onto the failure stack. Other register information, such as the
+ starting and ending positions (which are addresses), and the list of
+ inner groups (which is a bits list) are maintained in separate
+ variables.
+
+ We are making a (strictly speaking) nonportable assumption here: that
+ the compiler will pack our bit fields into something that fits into
+ the type of `word', i.e., is something that fits into one item on the
+ failure stack. */
+typedef union
+{
+ fail_stack_elt_t word;
+ struct
+ {
+ /* This field is one if this group can match the empty string,
+ zero if not. If not yet determined, `MATCH_NULL_UNSET_VALUE'. */
+#define MATCH_NULL_UNSET_VALUE 3
+ unsigned match_null_string_p : 2;
+ unsigned is_active : 1;
+ unsigned matched_something : 1;
+ unsigned ever_matched_something : 1;
+ } bits;
+} register_info_type;
+
+#define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p)
+#define IS_ACTIVE(R) ((R).bits.is_active)
+#define MATCHED_SOMETHING(R) ((R).bits.matched_something)
+#define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something)
+
+
+/* Call this when have matched a real character; it sets `matched' flags
+ for the subexpressions which we are currently inside. Also records
+ that those subexprs have matched. */
+#define SET_REGS_MATCHED() \
+ do \
+ { \
+ unsigned r; \
+ for (r = lowest_active_reg; r <= highest_active_reg; r++) \
+ { \
+ MATCHED_SOMETHING (reg_info[r]) \
+ = EVER_MATCHED_SOMETHING (reg_info[r]) \
+ = 1; \
+ } \
+ } \
+ while (0)
+
+
+/* This converts PTR, a pointer into one of the search strings `string1'
+ and `string2' into an offset from the beginning of that string. */
+#define POINTER_TO_OFFSET(ptr) \
+ (FIRST_STRING_P (ptr) ? (ptr) - string1 : (ptr) - string2 + size1)
+
+/* Registers are set to a sentinel when they haven't yet matched. */
+#define REG_UNSET_VALUE ((char *) -1)
+#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
+
+
+/* Macros for dealing with the split strings in re_match_2. */
+
+#define MATCHING_IN_FIRST_STRING (dend == end_match_1)
+
+/* Call before fetching a character with *d. This switches over to
+ string2 if necessary. */
+#define PREFETCH() \
+ while (d == dend) \
+ { \
+ /* End of string2 => fail. */ \
+ if (dend == end_match_2) \
+ goto fail; \
+ /* End of string1 => advance to string2. */ \
+ d = string2; \
+ dend = end_match_2; \
+ }
+
+
+/* Test if at very beginning or at very end of the virtual concatenation
+ of `string1' and `string2'. If only one string, it's `string2'. */
+#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2)
+#define AT_STRINGS_END(d) ((d) == end2)
+
+
+/* Test if D points to a character which is word-constituent. We have
+ two special cases to check for: if past the end of string1, look at
+ the first character in string2; and if before the beginning of
+ string2, look at the last character in string1. */
+#define WORDCHAR_P(d) \
+ (SYNTAX ((d) == end1 ? *string2 \
+ : (d) == string2 - 1 ? *(end1 - 1) : *(d)) \
+ == Sword)
+
+/* Test if the character before D and the one at D differ with respect
+ to being word-constituent. */
+#define AT_WORD_BOUNDARY(d) \
+ (AT_STRINGS_BEG (d) || AT_STRINGS_END (d) \
+ || WORDCHAR_P (d - 1) != WORDCHAR_P (d))
+
+
+/* Free everything we malloc. */
+#ifdef REGEX_MALLOC
+#define FREE_VAR(var) if (var) free (var); var = NULL
+#define FREE_VARIABLES() \
+ do { \
+ FREE_VAR (fail_stack.stack); \
+ FREE_VAR (regstart); \
+ FREE_VAR (regend); \
+ FREE_VAR (old_regstart); \
+ FREE_VAR (old_regend); \
+ FREE_VAR (best_regstart); \
+ FREE_VAR (best_regend); \
+ FREE_VAR (reg_info); \
+ FREE_VAR (reg_dummy); \
+ FREE_VAR (reg_info_dummy); \
+ } while (0)
+#else /* not REGEX_MALLOC */
+/* Some MIPS systems (at least) want this to free alloca'd storage. */
+#define FREE_VARIABLES() alloca (0)
+#endif /* not REGEX_MALLOC */
+
+
+/* These values must meet several constraints. They must not be valid
+ register values; since we have a limit of 255 registers (because
+ we use only one byte in the pattern for the register number), we can
+ use numbers larger than 255. They must differ by 1, because of
+ NUM_FAILURE_ITEMS above. And the value for the lowest register must
+ be larger than the value for the highest register, so we do not try
+ to actually save any registers when none are active. */
+#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH)
+#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1)
+
+/* Matching routines. */
+
+#ifndef emacs /* Emacs never uses this. */
+/* re_match is like re_match_2 except it takes only a single string. */
+
+int
+re_match (bufp, string, size, pos, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ int size, pos;
+ struct re_registers *regs;
+ {
+ return re_match_2 (bufp, NULL, 0, string, size, pos, regs, size);
+}
+#endif /* not emacs */
+
+
+/* re_match_2 matches the compiled pattern in BUFP against the
+ the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
+ and SIZE2, respectively). We start matching at POS, and stop
+ matching at STOP.
+
+ If REGS is non-null and the `no_sub' field of BUFP is nonzero, we
+ store offsets for the substring each group matched in REGS. See the
+ documentation for exactly how many groups we fill.
+
+ We return -1 if no match, -2 if an internal error (such as the
+ failure stack overflowing). Otherwise, we return the length of the
+ matched substring. */
+
+int
+re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ int size1, size2;
+ int pos;
+ struct re_registers *regs;
+ int stop;
+{
+ /* General temporaries. */
+ int mcnt;
+ unsigned char *p1;
+
+ /* Just past the end of the corresponding string. */
+ const char *end1, *end2;
+
+ /* Pointers into string1 and string2, just past the last characters in
+ each to consider matching. */
+ const char *end_match_1, *end_match_2;
+
+ /* Where we are in the data, and the end of the current string. */
+ const char *d, *dend;
+
+ /* Where we are in the pattern, and the end of the pattern. */
+ unsigned char *p = bufp->buffer;
+ register unsigned char *pend = p + bufp->used;
+
+ /* We use this to map every character in the string. */
+ char *translate = bufp->translate;
+
+ /* Failure point stack. Each place that can handle a failure further
+ down the line pushes a failure point on this stack. It consists of
+ restart, regend, and reg_info for all registers corresponding to
+ the subexpressions we're currently inside, plus the number of such
+ registers, and, finally, two char *'s. The first char * is where
+ to resume scanning the pattern; the second one is where to resume
+ scanning the strings. If the latter is zero, the failure point is
+ a ``dummy''; if a failure happens and the failure point is a dummy,
+ it gets discarded and the next next one is tried. */
+ fail_stack_type fail_stack;
+#ifdef DEBUG
+ static unsigned failure_id = 0;
+ unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0;
+#endif
+
+ /* We fill all the registers internally, independent of what we
+ return, for use in backreferences. The number here includes
+ an element for register zero. */
+ unsigned num_regs = bufp->re_nsub + 1;
+
+ /* The currently active registers. */
+ unsigned lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+ unsigned highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+
+ /* Information on the contents of registers. These are pointers into
+ the input strings; they record just what was matched (on this
+ attempt) by a subexpression part of the pattern, that is, the
+ regnum-th regstart pointer points to where in the pattern we began
+ matching and the regnum-th regend points to right after where we
+ stopped matching the regnum-th subexpression. (The zeroth register
+ keeps track of what the whole pattern matches.) */
+ const char **regstart, **regend;
+
+ /* If a group that's operated upon by a repetition operator fails to
+ match anything, then the register for its start will need to be
+ restored because it will have been set to wherever in the string we
+ are when we last see its open-group operator. Similarly for a
+ register's end. */
+ const char **old_regstart, **old_regend;
+
+ /* The is_active field of reg_info helps us keep track of which (possibly
+ nested) subexpressions we are currently in. The matched_something
+ field of reg_info[reg_num] helps us tell whether or not we have
+ matched any of the pattern so far this time through the reg_num-th
+ subexpression. These two fields get reset each time through any
+ loop their register is in. */
+ register_info_type *reg_info;
+
+ /* The following record the register info as found in the above
+ variables when we find a match better than any we've seen before.
+ This happens as we backtrack through the failure points, which in
+ turn happens only if we have not yet matched the entire string. */
+ unsigned best_regs_set = false;
+ const char **best_regstart, **best_regend;
+
+ /* Logically, this is `best_regend[0]'. But we don't want to have to
+ allocate space for that if we're not allocating space for anything
+ else (see below). Also, we never need info about register 0 for
+ any of the other register vectors, and it seems rather a kludge to
+ treat `best_regend' differently than the rest. So we keep track of
+ the end of the best match so far in a separate variable. We
+ initialize this to NULL so that when we backtrack the first time
+ and need to test it, it's not garbage. */
+ const char *match_end = NULL;
+
+ /* Used when we pop values we don't care about. */
+ const char **reg_dummy;
+ register_info_type *reg_info_dummy;
+
+#ifdef DEBUG
+ /* Counts the total number of registers pushed. */
+ unsigned num_regs_pushed = 0;
+#endif
+
+ DEBUG_PRINT1 ("\n\nEntering re_match_2.\n");
+
+ INIT_FAIL_STACK ();
+
+ /* Do not bother to initialize all the register variables if there are
+ no groups in the pattern, as it takes a fair amount of time. If
+ there are groups, we include space for register 0 (the whole
+ pattern), even though we never use it, since it simplifies the
+ array indexing. We should fix this. */
+ if (bufp->re_nsub)
+ {
+ regstart = REGEX_TALLOC (num_regs, const char *);
+ regend = REGEX_TALLOC (num_regs, const char *);
+ old_regstart = REGEX_TALLOC (num_regs, const char *);
+ old_regend = REGEX_TALLOC (num_regs, const char *);
+ best_regstart = REGEX_TALLOC (num_regs, const char *);
+ best_regend = REGEX_TALLOC (num_regs, const char *);
+ reg_info = REGEX_TALLOC (num_regs, register_info_type);
+ reg_dummy = REGEX_TALLOC (num_regs, const char *);
+ reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type);
+
+ if (!(regstart && regend && old_regstart && old_regend && reg_info
+ && best_regstart && best_regend && reg_dummy && reg_info_dummy))
+ {
+ FREE_VARIABLES ();
+ return -2;
+ }
+ }
+#ifdef REGEX_MALLOC
+ else
+ {
+ /* We must initialize all our variables to NULL, so that
+ `FREE_VARIABLES' doesn't try to free them. */
+ regstart = regend = old_regstart = old_regend = best_regstart
+ = best_regend = reg_dummy = NULL;
+ reg_info = reg_info_dummy = (register_info_type *) NULL;
+ }
+#endif /* REGEX_MALLOC */
+
+ /* The starting position is bogus. */
+ if (pos < 0 || pos > size1 + size2)
+ {
+ FREE_VARIABLES ();
+ return -1;
+ }
+
+ /* Initialize subexpression text positions to -1 to mark ones that no
+ start_memory/stop_memory has been seen for. Also initialize the
+ register information struct. */
+ for (mcnt = 1; mcnt < num_regs; mcnt++)
+ {
+ regstart[mcnt] = regend[mcnt]
+ = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE;
+
+ REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE;
+ IS_ACTIVE (reg_info[mcnt]) = 0;
+ MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+ EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+ }
+
+ /* We move `string1' into `string2' if the latter's empty -- but not if
+ `string1' is null. */
+ if (size2 == 0 && string1 != NULL)
+ {
+ string2 = string1;
+ size2 = size1;
+ string1 = 0;
+ size1 = 0;
+ }
+ end1 = string1 + size1;
+ end2 = string2 + size2;
+
+ /* Compute where to stop matching, within the two strings. */
+ if (stop <= size1)
+ {
+ end_match_1 = string1 + stop;
+ end_match_2 = string2;
+ }
+ else
+ {
+ end_match_1 = end1;
+ end_match_2 = string2 + stop - size1;
+ }
+
+ /* `p' scans through the pattern as `d' scans through the data.
+ `dend' is the end of the input string that `d' points within. `d'
+ is advanced into the following input string whenever necessary, but
+ this happens before fetching; therefore, at the beginning of the
+ loop, `d' can be pointing at the end of a string, but it cannot
+ equal `string2'. */
+ if (size1 > 0 && pos <= size1)
+ {
+ d = string1 + pos;
+ dend = end_match_1;
+ }
+ else
+ {
+ d = string2 + pos - size1;
+ dend = end_match_2;
+ }
+
+ DEBUG_PRINT1 ("The compiled pattern is: ");
+ DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend);
+ DEBUG_PRINT1 ("The string to match is: `");
+ DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2);
+ DEBUG_PRINT1 ("'\n");
+
+ /* This loops over pattern commands. It exits by returning from the
+ function if the match is complete, or it drops through if the match
+ fails at this starting point in the input data. */
+ for (;;)
+ {
+ DEBUG_PRINT2 ("\n0x%x: ", p);
+
+ if (p == pend)
+ { /* End of pattern means we might have succeeded. */
+ DEBUG_PRINT1 ("end of pattern ... ");
+
+ /* If we haven't matched the entire string, and we want the
+ longest match, try backtracking. */
+ if (d != end_match_2)
+ {
+ DEBUG_PRINT1 ("backtracking.\n");
+
+ if (!FAIL_STACK_EMPTY ())
+ { /* More failure points to try. */
+ boolean same_str_p = (FIRST_STRING_P (match_end)
+ == MATCHING_IN_FIRST_STRING);
+
+ /* If exceeds best match so far, save it. */
+ if (!best_regs_set
+ || (same_str_p && d > match_end)
+ || (!same_str_p && !MATCHING_IN_FIRST_STRING))
+ {
+ best_regs_set = true;
+ match_end = d;
+
+ DEBUG_PRINT1 ("\nSAVING match as best so far.\n");
+
+ for (mcnt = 1; mcnt < num_regs; mcnt++)
+ {
+ best_regstart[mcnt] = regstart[mcnt];
+ best_regend[mcnt] = regend[mcnt];
+ }
+ }
+ goto fail;
+ }
+
+ /* If no failure points, don't restore garbage. */
+ else if (best_regs_set)
+ {
+ restore_best_regs:
+ /* Restore best match. It may happen that `dend ==
+ end_match_1' while the restored d is in string2.
+ For example, the pattern `x.*y.*z' against the
+ strings `x-' and `y-z-', if the two strings are
+ not consecutive in memory. */
+ DEBUG_PRINT1 ("Restoring best registers.\n");
+
+ d = match_end;
+ dend = ((d >= string1 && d <= end1)
+ ? end_match_1 : end_match_2);
+
+ for (mcnt = 1; mcnt < num_regs; mcnt++)
+ {
+ regstart[mcnt] = best_regstart[mcnt];
+ regend[mcnt] = best_regend[mcnt];
+ }
+ }
+ } /* d != end_match_2 */
+
+ DEBUG_PRINT1 ("Accepting match.\n");
+
+ /* If caller wants register contents data back, do it. */
+ if (regs && !bufp->no_sub)
+ {
+ /* Have the register data arrays been allocated? */
+ if (bufp->regs_allocated == REGS_UNALLOCATED)
+ { /* No. So allocate them with malloc. We need one
+ extra element beyond `num_regs' for the `-1' marker
+ GNU code uses. */
+ regs->num_regs = MAX (RE_NREGS, num_regs + 1);
+ regs->start = TALLOC (regs->num_regs, regoff_t);
+ regs->end = TALLOC (regs->num_regs, regoff_t);
+ if (regs->start == NULL || regs->end == NULL)
+ return -2;
+ bufp->regs_allocated = REGS_REALLOCATE;
+ }
+ else if (bufp->regs_allocated == REGS_REALLOCATE)
+ { /* Yes. If we need more elements than were already
+ allocated, reallocate them. If we need fewer, just
+ leave it alone. */
+ if (regs->num_regs < num_regs + 1)
+ {
+ regs->num_regs = num_regs + 1;
+ RETALLOC (regs->start, regs->num_regs, regoff_t);
+ RETALLOC (regs->end, regs->num_regs, regoff_t);
+ if (regs->start == NULL || regs->end == NULL)
+ return -2;
+ }
+ }
+ else
+ assert (bufp->regs_allocated == REGS_FIXED);
+
+ /* Convert the pointer data in `regstart' and `regend' to
+ indices. Register zero has to be set differently,
+ since we haven't kept track of any info for it. */
+ if (regs->num_regs > 0)
+ {
+ regs->start[0] = pos;
+ regs->end[0] = (MATCHING_IN_FIRST_STRING ? d - string1
+ : d - string2 + size1);
+ }
+
+ /* Go through the first `min (num_regs, regs->num_regs)'
+ registers, since that is all we initialized. */
+ for (mcnt = 1; mcnt < MIN (num_regs, regs->num_regs); mcnt++)
+ {
+ if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt]))
+ regs->start[mcnt] = regs->end[mcnt] = -1;
+ else
+ {
+ regs->start[mcnt] = POINTER_TO_OFFSET (regstart[mcnt]);
+ regs->end[mcnt] = POINTER_TO_OFFSET (regend[mcnt]);
+ }
+ }
+
+ /* If the regs structure we return has more elements than
+ were in the pattern, set the extra elements to -1. If
+ we (re)allocated the registers, this is the case,
+ because we always allocate enough to have at least one
+ -1 at the end. */
+ for (mcnt = num_regs; mcnt < regs->num_regs; mcnt++)
+ regs->start[mcnt] = regs->end[mcnt] = -1;
+ } /* regs && !bufp->no_sub */
+
+ FREE_VARIABLES ();
+ DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n",
+ nfailure_points_pushed, nfailure_points_popped,
+ nfailure_points_pushed - nfailure_points_popped);
+ DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed);
+
+ mcnt = d - pos - (MATCHING_IN_FIRST_STRING
+ ? string1
+ : string2 - size1);
+
+ DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt);
+
+ return mcnt;
+ }
+
+ /* Otherwise match next pattern command. */
+#ifdef SWITCH_ENUM_BUG
+ switch ((int) ((re_opcode_t) *p++))
+#else
+ switch ((re_opcode_t) *p++)
+#endif
+ {
+ /* Ignore these. Used to ignore the n of succeed_n's which
+ currently have n == 0. */
+ case no_op:
+ DEBUG_PRINT1 ("EXECUTING no_op.\n");
+ break;
+
+
+ /* Match the next n pattern characters exactly. The following
+ byte in the pattern defines n, and the n bytes after that
+ are the characters to match. */
+ case exactn:
+ mcnt = *p++;
+ DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt);
+
+ /* This is written out as an if-else so we don't waste time
+ testing `translate' inside the loop. */
+ if (translate)
+ {
+ do
+ {
+ PREFETCH ();
+ if (translate[(unsigned char) *d++] != (char) *p++)
+ goto fail;
+ }
+ while (--mcnt);
+ }
+ else
+ {
+ do
+ {
+ PREFETCH ();
+ if (*d++ != (char) *p++) goto fail;
+ }
+ while (--mcnt);
+ }
+ SET_REGS_MATCHED ();
+ break;
+
+
+ /* Match any character except possibly a newline or a null. */
+ case anychar:
+ DEBUG_PRINT1 ("EXECUTING anychar.\n");
+
+ PREFETCH ();
+
+ if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n')
+ || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000'))
+ goto fail;
+
+ SET_REGS_MATCHED ();
+ DEBUG_PRINT2 (" Matched `%d'.\n", *d);
+ d++;
+ break;
+
+
+ case charset:
+ case charset_not:
+ {
+ register unsigned char c;
+ boolean not = (re_opcode_t) *(p - 1) == charset_not;
+
+ DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : "");
+
+ PREFETCH ();
+ c = TRANSLATE (*d); /* The character to match. */
+
+ /* Cast to `unsigned' instead of `unsigned char' in case the
+ bit list is a full 32 bytes long. */
+ if (c < (unsigned) (*p * BYTEWIDTH)
+ && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+ not = !not;
+
+ p += 1 + *p;
+
+ if (!not) goto fail;
+
+ SET_REGS_MATCHED ();
+ d++;
+ break;
+ }
+
+
+ /* The beginning of a group is represented by start_memory.
+ The arguments are the register number in the next byte, and the
+ number of groups inner to this one in the next. The text
+ matched within the group is recorded (in the internal
+ registers data structure) under the register number. */
+ case start_memory:
+ DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]);
+
+ /* Find out if this group can match the empty string. */
+ p1 = p; /* To send to group_match_null_string_p. */
+
+ if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE)
+ REG_MATCH_NULL_STRING_P (reg_info[*p])
+ = group_match_null_string_p (&p1, pend, reg_info);
+
+ /* Save the position in the string where we were the last time
+ we were at this open-group operator in case the group is
+ operated upon by a repetition operator, e.g., with `(a*)*b'
+ against `ab'; then we want to ignore where we are now in
+ the string in case this attempt to match fails. */
+ old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+ ? REG_UNSET (regstart[*p]) ? d : regstart[*p]
+ : regstart[*p];
+ DEBUG_PRINT2 (" old_regstart: %d\n",
+ POINTER_TO_OFFSET (old_regstart[*p]));
+
+ regstart[*p] = d;
+ DEBUG_PRINT2 (" regstart: %d\n", POINTER_TO_OFFSET (regstart[*p]));
+
+ IS_ACTIVE (reg_info[*p]) = 1;
+ MATCHED_SOMETHING (reg_info[*p]) = 0;
+
+ /* This is the new highest active register. */
+ highest_active_reg = *p;
+
+ /* If nothing was active before, this is the new lowest active
+ register. */
+ if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+ lowest_active_reg = *p;
+
+ /* Move past the register number and inner group count. */
+ p += 2;
+ break;
+
+
+ /* The stop_memory opcode represents the end of a group. Its
+ arguments are the same as start_memory's: the register
+ number, and the number of inner groups. */
+ case stop_memory:
+ DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]);
+
+ /* We need to save the string position the last time we were at
+ this close-group operator in case the group is operated
+ upon by a repetition operator, e.g., with `((a*)*(b*)*)*'
+ against `aba'; then we want to ignore where we are now in
+ the string in case this attempt to match fails. */
+ old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+ ? REG_UNSET (regend[*p]) ? d : regend[*p]
+ : regend[*p];
+ DEBUG_PRINT2 (" old_regend: %d\n",
+ POINTER_TO_OFFSET (old_regend[*p]));
+
+ regend[*p] = d;
+ DEBUG_PRINT2 (" regend: %d\n", POINTER_TO_OFFSET (regend[*p]));
+
+ /* This register isn't active anymore. */
+ IS_ACTIVE (reg_info[*p]) = 0;
+
+ /* If this was the only register active, nothing is active
+ anymore. */
+ if (lowest_active_reg == highest_active_reg)
+ {
+ lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+ highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+ }
+ else
+ { /* We must scan for the new highest active register, since
+ it isn't necessarily one less than now: consider
+ (a(b)c(d(e)f)g). When group 3 ends, after the f), the
+ new highest active register is 1. */
+ unsigned char r = *p - 1;
+ while (r > 0 && !IS_ACTIVE (reg_info[r]))
+ r--;
+
+ /* If we end up at register zero, that means that we saved
+ the registers as the result of an `on_failure_jump', not
+ a `start_memory', and we jumped to past the innermost
+ `stop_memory'. For example, in ((.)*) we save
+ registers 1 and 2 as a result of the *, but when we pop
+ back to the second ), we are at the stop_memory 1.
+ Thus, nothing is active. */
+ if (r == 0)
+ {
+ lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+ highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+ }
+ else
+ highest_active_reg = r;
+ }
+
+ /* If just failed to match something this time around with a
+ group that's operated on by a repetition operator, try to
+ force exit from the ``loop'', and restore the register
+ information for this group that we had before trying this
+ last match. */
+ if ((!MATCHED_SOMETHING (reg_info[*p])
+ || (re_opcode_t) p[-3] == start_memory)
+ && (p + 2) < pend)
+ {
+ boolean is_a_jump_n = false;
+
+ p1 = p + 2;
+ mcnt = 0;
+ switch ((re_opcode_t) *p1++)
+ {
+ case jump_n:
+ is_a_jump_n = true;
+ case pop_failure_jump:
+ case maybe_pop_jump:
+ case jump:
+ case dummy_failure_jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ if (is_a_jump_n)
+ p1 += 2;
+ break;
+
+ default:
+ /* do nothing */ ;
+ }
+ p1 += mcnt;
+
+ /* If the next operation is a jump backwards in the pattern
+ to an on_failure_jump right before the start_memory
+ corresponding to this stop_memory, exit from the loop
+ by forcing a failure after pushing on the stack the
+ on_failure_jump's jump in the pattern, and d. */
+ if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump
+ && (re_opcode_t) p1[3] == start_memory && p1[4] == *p)
+ {
+ /* If this group ever matched anything, then restore
+ what its registers were before trying this last
+ failed match, e.g., with `(a*)*b' against `ab' for
+ regstart[1], and, e.g., with `((a*)*(b*)*)*'
+ against `aba' for regend[3].
+
+ Also restore the registers for inner groups for,
+ e.g., `((a*)(b*))*' against `aba' (register 3 would
+ otherwise get trashed). */
+
+ if (EVER_MATCHED_SOMETHING (reg_info[*p]))
+ {
+ unsigned r;
+
+ EVER_MATCHED_SOMETHING (reg_info[*p]) = 0;
+
+ /* Restore this and inner groups' (if any) registers. */
+ for (r = *p; r < *p + *(p + 1); r++)
+ {
+ regstart[r] = old_regstart[r];
+
+ /* xx why this test? */
+ if ((int) old_regend[r] >= (int) regstart[r])
+ regend[r] = old_regend[r];
+ }
+ }
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ PUSH_FAILURE_POINT (p1 + mcnt, d, -2);
+
+ goto fail;
+ }
+ }
+
+ /* Move past the register number and the inner group count. */
+ p += 2;
+ break;
+
+
+ /* \<digit> has been turned into a `duplicate' command which is
+ followed by the numeric value of <digit> as the register number. */
+ case duplicate:
+ {
+ register const char *d2, *dend2;
+ int regno = *p++; /* Get which register to match against. */
+ DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno);
+
+ /* Can't back reference a group which we've never matched. */
+ if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno]))
+ goto fail;
+
+ /* Where in input to try to start matching. */
+ d2 = regstart[regno];
+
+ /* Where to stop matching; if both the place to start and
+ the place to stop matching are in the same string, then
+ set to the place to stop, otherwise, for now have to use
+ the end of the first string. */
+
+ dend2 = ((FIRST_STRING_P (regstart[regno])
+ == FIRST_STRING_P (regend[regno]))
+ ? regend[regno] : end_match_1);
+ for (;;)
+ {
+ /* If necessary, advance to next segment in register
+ contents. */
+ while (d2 == dend2)
+ {
+ if (dend2 == end_match_2) break;
+ if (dend2 == regend[regno]) break;
+
+ /* End of string1 => advance to string2. */
+ d2 = string2;
+ dend2 = regend[regno];
+ }
+ /* At end of register contents => success */
+ if (d2 == dend2) break;
+
+ /* If necessary, advance to next segment in data. */
+ PREFETCH ();
+
+ /* How many characters left in this segment to match. */
+ mcnt = dend - d;
+
+ /* Want how many consecutive characters we can match in
+ one shot, so, if necessary, adjust the count. */
+ if (mcnt > dend2 - d2)
+ mcnt = dend2 - d2;
+
+ /* Compare that many; failure if mismatch, else move
+ past them. */
+ if (translate
+ ? bcmp_translate (d, d2, mcnt, translate)
+ : bcmp (d, d2, mcnt))
+ goto fail;
+ d += mcnt, d2 += mcnt;
+ }
+ }
+ break;
+
+
+ /* begline matches the empty string at the beginning of the string
+ (unless `not_bol' is set in `bufp'), and, if
+ `newline_anchor' is set, after newlines. */
+ case begline:
+ DEBUG_PRINT1 ("EXECUTING begline.\n");
+
+ if (AT_STRINGS_BEG (d))
+ {
+ if (!bufp->not_bol) break;
+ }
+ else if (d[-1] == '\n' && bufp->newline_anchor)
+ {
+ break;
+ }
+ /* In all other cases, we fail. */
+ goto fail;
+
+
+ /* endline is the dual of begline. */
+ case endline:
+ DEBUG_PRINT1 ("EXECUTING endline.\n");
+
+ if (AT_STRINGS_END (d))
+ {
+ if (!bufp->not_eol) break;
+ }
+
+ /* We have to ``prefetch'' the next character. */
+ else if ((d == end1 ? *string2 : *d) == '\n'
+ && bufp->newline_anchor)
+ {
+ break;
+ }
+ goto fail;
+
+
+ /* Match at the very beginning of the data. */
+ case begbuf:
+ DEBUG_PRINT1 ("EXECUTING begbuf.\n");
+ if (AT_STRINGS_BEG (d))
+ break;
+ goto fail;
+
+
+ /* Match at the very end of the data. */
+ case endbuf:
+ DEBUG_PRINT1 ("EXECUTING endbuf.\n");
+ if (AT_STRINGS_END (d))
+ break;
+ goto fail;
+
+
+ /* on_failure_keep_string_jump is used to optimize `.*\n'. It
+ pushes NULL as the value for the string on the stack. Then
+ `pop_failure_point' will keep the current value for the
+ string, instead of restoring it. To see why, consider
+ matching `foo\nbar' against `.*\n'. The .* matches the foo;
+ then the . fails against the \n. But the next thing we want
+ to do is match the \n against the \n; if we restored the
+ string value, we would be back at the foo.
+
+ Because this is used only in specific cases, we don't need to
+ check all the things that `on_failure_jump' does, to make
+ sure the right things get saved on the stack. Hence we don't
+ share its code. The only reason to push anything on the
+ stack at all is that otherwise we would have to change
+ `anychar's code to do something besides goto fail in this
+ case; that seems worse than this. */
+ case on_failure_keep_string_jump:
+ DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump");
+
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt);
+
+ PUSH_FAILURE_POINT (p + mcnt, NULL, -2);
+ break;
+
+
+ /* Uses of on_failure_jump:
+
+ Each alternative starts with an on_failure_jump that points
+ to the beginning of the next alternative. Each alternative
+ except the last ends with a jump that in effect jumps past
+ the rest of the alternatives. (They really jump to the
+ ending jump of the following alternative, because tensioning
+ these jumps is a hassle.)
+
+ Repeats start with an on_failure_jump that points past both
+ the repetition text and either the following jump or
+ pop_failure_jump back to this on_failure_jump. */
+ case on_failure_jump:
+ on_failure:
+ DEBUG_PRINT1 ("EXECUTING on_failure_jump");
+
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt);
+
+ /* If this on_failure_jump comes right before a group (i.e.,
+ the original * applied to a group), save the information
+ for that group and all inner ones, so that if we fail back
+ to this point, the group's information will be correct.
+ For example, in \(a*\)*\1, we need the preceding group,
+ and in \(\(a*\)b*\)\2, we need the inner group. */
+
+ /* We can't use `p' to check ahead because we push
+ a failure point to `p + mcnt' after we do this. */
+ p1 = p;
+
+ /* We need to skip no_op's before we look for the
+ start_memory in case this on_failure_jump is happening as
+ the result of a completed succeed_n, as in \(a\)\{1,3\}b\1
+ against aba. */
+ while (p1 < pend && (re_opcode_t) *p1 == no_op)
+ p1++;
+
+ if (p1 < pend && (re_opcode_t) *p1 == start_memory)
+ {
+ /* We have a new highest active register now. This will
+ get reset at the start_memory we are about to get to,
+ but we will have saved all the registers relevant to
+ this repetition op, as described above. */
+ highest_active_reg = *(p1 + 1) + *(p1 + 2);
+ if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+ lowest_active_reg = *(p1 + 1);
+ }
+
+ DEBUG_PRINT1 (":\n");
+ PUSH_FAILURE_POINT (p + mcnt, d, -2);
+ break;
+
+
+ /* A smart repeat ends with `maybe_pop_jump'.
+ We change it to either `pop_failure_jump' or `jump'. */
+ case maybe_pop_jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt);
+ {
+ register unsigned char *p2 = p;
+
+ /* Compare the beginning of the repeat with what in the
+ pattern follows its end. If we can establish that there
+ is nothing that they would both match, i.e., that we
+ would have to backtrack because of (as in, e.g., `a*a')
+ then we can change to pop_failure_jump, because we'll
+ never have to backtrack.
+
+ This is not true in the case of alternatives: in
+ `(a|ab)*' we do need to backtrack to the `ab' alternative
+ (e.g., if the string was `ab'). But instead of trying to
+ detect that here, the alternative has put on a dummy
+ failure point which is what we will end up popping. */
+
+ /* Skip over open/close-group commands. */
+ while (p2 + 2 < pend
+ && ((re_opcode_t) *p2 == stop_memory
+ || (re_opcode_t) *p2 == start_memory))
+ p2 += 3; /* Skip over args, too. */
+
+ /* If we're at the end of the pattern, we can change. */
+ if (p2 == pend)
+ {
+ /* Consider what happens when matching ":\(.*\)"
+ against ":/". I don't really understand this code
+ yet. */
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT1
+ (" End of pattern: change to `pop_failure_jump'.\n");
+ }
+
+ else if ((re_opcode_t) *p2 == exactn
+ || (bufp->newline_anchor && (re_opcode_t) *p2 == endline))
+ {
+ register unsigned char c
+ = *p2 == (unsigned char) endline ? '\n' : p2[2];
+ p1 = p + mcnt;
+
+ /* p1[0] ... p1[2] are the `on_failure_jump' corresponding
+ to the `maybe_finalize_jump' of this case. Examine what
+ follows. */
+ if ((re_opcode_t) p1[3] == exactn && p1[5] != c)
+ {
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n",
+ c, p1[5]);
+ }
+
+ else if ((re_opcode_t) p1[3] == charset
+ || (re_opcode_t) p1[3] == charset_not)
+ {
+ int not = (re_opcode_t) p1[3] == charset_not;
+
+ if (c < (unsigned char) (p1[4] * BYTEWIDTH)
+ && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+ not = !not;
+
+ /* `not' is equal to 1 if c would match, which means
+ that we can't change to pop_failure_jump. */
+ if (!not)
+ {
+ p[-3] = (unsigned char) pop_failure_jump;
+ DEBUG_PRINT1 (" No match => pop_failure_jump.\n");
+ }
+ }
+ }
+ }
+ p -= 2; /* Point at relative address again. */
+ if ((re_opcode_t) p[-1] != pop_failure_jump)
+ {
+ p[-1] = (unsigned char) jump;
+ DEBUG_PRINT1 (" Match => jump.\n");
+ goto unconditional_jump;
+ }
+ /* Note fall through. */
+
+
+ /* The end of a simple repeat has a pop_failure_jump back to
+ its matching on_failure_jump, where the latter will push a
+ failure point. The pop_failure_jump takes off failure
+ points put on by this pop_failure_jump's matching
+ on_failure_jump; we got through the pattern to here from the
+ matching on_failure_jump, so didn't fail. */
+ case pop_failure_jump:
+ {
+ /* We need to pass separate storage for the lowest and
+ highest registers, even though we don't care about the
+ actual values. Otherwise, we will restore only one
+ register from the stack, since lowest will == highest in
+ `pop_failure_point'. */
+ unsigned dummy_low_reg, dummy_high_reg;
+ unsigned char *pdummy;
+ const char *sdummy;
+
+ DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n");
+ POP_FAILURE_POINT (sdummy, pdummy,
+ dummy_low_reg, dummy_high_reg,
+ reg_dummy, reg_dummy, reg_info_dummy);
+ }
+ /* Note fall through. */
+
+
+ /* Unconditionally jump (without popping any failure points). */
+ case jump:
+ unconditional_jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p); /* Get the amount to jump. */
+ DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt);
+ p += mcnt; /* Do the jump. */
+ DEBUG_PRINT2 ("(to 0x%x).\n", p);
+ break;
+
+
+ /* We need this opcode so we can detect where alternatives end
+ in `group_match_null_string_p' et al. */
+ case jump_past_alt:
+ DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n");
+ goto unconditional_jump;
+
+
+ /* Normally, the on_failure_jump pushes a failure point, which
+ then gets popped at pop_failure_jump. We will end up at
+ pop_failure_jump, also, and with a pattern of, say, `a+', we
+ are skipping over the on_failure_jump, so we have to push
+ something meaningless for pop_failure_jump to pop. */
+ case dummy_failure_jump:
+ DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n");
+ /* It doesn't matter what we push for the string here. What
+ the code at `fail' tests is the value for the pattern. */
+ PUSH_FAILURE_POINT (0, 0, -2);
+ goto unconditional_jump;
+
+
+ /* At the end of an alternative, we need to push a dummy failure
+ point in case we are followed by a `pop_failure_jump', because
+ we don't want the failure point for the alternative to be
+ popped. For example, matching `(a|ab)*' against `aab'
+ requires that we match the `ab' alternative. */
+ case push_dummy_failure:
+ DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n");
+ /* See comments just above at `dummy_failure_jump' about the
+ two zeroes. */
+ PUSH_FAILURE_POINT (0, 0, -2);
+ break;
+
+ /* Have to succeed matching what follows at least n times.
+ After that, handle like `on_failure_jump'. */
+ case succeed_n:
+ EXTRACT_NUMBER (mcnt, p + 2);
+ DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt);
+
+ assert (mcnt >= 0);
+ /* Originally, this is how many times we HAVE to succeed. */
+ if (mcnt > 0)
+ {
+ mcnt--;
+ p += 2;
+ STORE_NUMBER_AND_INCR (p, mcnt);
+ DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p, mcnt);
+ }
+ else if (mcnt == 0)
+ {
+ DEBUG_PRINT2 (" Setting two bytes from 0x%x to no_op.\n", p+2);
+ p[2] = (unsigned char) no_op;
+ p[3] = (unsigned char) no_op;
+ goto on_failure;
+ }
+ break;
+
+ case jump_n:
+ EXTRACT_NUMBER (mcnt, p + 2);
+ DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt);
+
+ /* Originally, this is how many times we CAN jump. */
+ if (mcnt)
+ {
+ mcnt--;
+ STORE_NUMBER (p + 2, mcnt);
+ goto unconditional_jump;
+ }
+ /* If don't have to jump any more, skip over the rest of command. */
+ else
+ p += 4;
+ break;
+
+ case set_number_at:
+ {
+ DEBUG_PRINT1 ("EXECUTING set_number_at.\n");
+
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ p1 = p + mcnt;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p);
+ DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p1, mcnt);
+ STORE_NUMBER (p1, mcnt);
+ break;
+ }
+
+ case wordbound:
+ DEBUG_PRINT1 ("EXECUTING wordbound.\n");
+ if (AT_WORD_BOUNDARY (d))
+ break;
+ goto fail;
+
+ case notwordbound:
+ DEBUG_PRINT1 ("EXECUTING notwordbound.\n");
+ if (AT_WORD_BOUNDARY (d))
+ goto fail;
+ break;
+
+ case wordbeg:
+ DEBUG_PRINT1 ("EXECUTING wordbeg.\n");
+ if (WORDCHAR_P (d) && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1)))
+ break;
+ goto fail;
+
+ case wordend:
+ DEBUG_PRINT1 ("EXECUTING wordend.\n");
+ if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1)
+ && (!WORDCHAR_P (d) || AT_STRINGS_END (d)))
+ break;
+ goto fail;
+
+#ifdef emacs
+#ifdef emacs19
+ case before_dot:
+ DEBUG_PRINT1 ("EXECUTING before_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) >= point)
+ goto fail;
+ break;
+
+ case at_dot:
+ DEBUG_PRINT1 ("EXECUTING at_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) != point)
+ goto fail;
+ break;
+
+ case after_dot:
+ DEBUG_PRINT1 ("EXECUTING after_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) <= point)
+ goto fail;
+ break;
+#else /* not emacs19 */
+ case at_dot:
+ DEBUG_PRINT1 ("EXECUTING at_dot.\n");
+ if (PTR_CHAR_POS ((unsigned char *) d) + 1 != point)
+ goto fail;
+ break;
+#endif /* not emacs19 */
+
+ case syntaxspec:
+ DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt);
+ mcnt = *p++;
+ goto matchsyntax;
+
+ case wordchar:
+ DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n");
+ mcnt = (int) Sword;
+ matchsyntax:
+ PREFETCH ();
+ if (SYNTAX (*d++) != (enum syntaxcode) mcnt)
+ goto fail;
+ SET_REGS_MATCHED ();
+ break;
+
+ case notsyntaxspec:
+ DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt);
+ mcnt = *p++;
+ goto matchnotsyntax;
+
+ case notwordchar:
+ DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n");
+ mcnt = (int) Sword;
+ matchnotsyntax:
+ PREFETCH ();
+ if (SYNTAX (*d++) == (enum syntaxcode) mcnt)
+ goto fail;
+ SET_REGS_MATCHED ();
+ break;
+
+#else /* not emacs */
+ case wordchar:
+ DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n");
+ PREFETCH ();
+ if (!WORDCHAR_P (d))
+ goto fail;
+ SET_REGS_MATCHED ();
+ d++;
+ break;
+
+ case notwordchar:
+ DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n");
+ PREFETCH ();
+ if (WORDCHAR_P (d))
+ goto fail;
+ SET_REGS_MATCHED ();
+ d++;
+ break;
+#endif /* not emacs */
+
+ default:
+ abort ();
+ }
+ continue; /* Successfully executed one pattern command; keep going. */
+
+
+ /* We goto here if a matching operation fails. */
+ fail:
+ if (!FAIL_STACK_EMPTY ())
+ { /* A restart point is known. Restore to that state. */
+ DEBUG_PRINT1 ("\nFAIL:\n");
+ POP_FAILURE_POINT (d, p,
+ lowest_active_reg, highest_active_reg,
+ regstart, regend, reg_info);
+
+ /* If this failure point is a dummy, try the next one. */
+ if (!p)
+ goto fail;
+
+ /* If we failed to the end of the pattern, don't examine *p. */
+ assert (p <= pend);
+ if (p < pend)
+ {
+ boolean is_a_jump_n = false;
+
+ /* If failed to a backwards jump that's part of a repetition
+ loop, need to pop this failure point and use the next one. */
+ switch ((re_opcode_t) *p)
+ {
+ case jump_n:
+ is_a_jump_n = true;
+ case maybe_pop_jump:
+ case pop_failure_jump:
+ case jump:
+ p1 = p + 1;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ p1 += mcnt;
+
+ if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n)
+ || (!is_a_jump_n
+ && (re_opcode_t) *p1 == on_failure_jump))
+ goto fail;
+ break;
+ default:
+ /* do nothing */ ;
+ }
+ }
+
+ if (d >= string1 && d <= end1)
+ dend = end_match_1;
+ }
+ else
+ break; /* Matching at this starting point really fails. */
+ } /* for (;;) */
+
+ if (best_regs_set)
+ goto restore_best_regs;
+
+ FREE_VARIABLES ();
+
+ return -1; /* Failure to match. */
+} /* re_match_2 */
+
+/* Subroutine definitions for re_match_2. */
+
+
+/* We are passed P pointing to a register number after a start_memory.
+
+ Return true if the pattern up to the corresponding stop_memory can
+ match the empty string, and false otherwise.
+
+ If we find the matching stop_memory, sets P to point to one past its number.
+ Otherwise, sets P to an undefined byte less than or equal to END.
+
+ We don't handle duplicates properly (yet). */
+
+static boolean
+group_match_null_string_p (p, end, reg_info)
+ unsigned char **p, *end;
+ register_info_type *reg_info;
+{
+ int mcnt;
+ /* Point to after the args to the start_memory. */
+ unsigned char *p1 = *p + 2;
+
+ while (p1 < end)
+ {
+ /* Skip over opcodes that can match nothing, and return true or
+ false, as appropriate, when we get to one that can't, or to the
+ matching stop_memory. */
+
+ switch ((re_opcode_t) *p1)
+ {
+ /* Could be either a loop or a series of alternatives. */
+ case on_failure_jump:
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+
+ /* If the next operation is not a jump backwards in the
+ pattern. */
+
+ if (mcnt >= 0)
+ {
+ /* Go through the on_failure_jumps of the alternatives,
+ seeing if any of the alternatives cannot match nothing.
+ The last alternative starts with only a jump,
+ whereas the rest start with on_failure_jump and end
+ with a jump, e.g., here is the pattern for `a|b|c':
+
+ /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6
+ /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3
+ /exactn/1/c
+
+ So, we have to first go through the first (n-1)
+ alternatives and then deal with the last one separately. */
+
+
+ /* Deal with the first (n-1) alternatives, which start
+ with an on_failure_jump (see above) that jumps to right
+ past a jump_past_alt. */
+
+ while ((re_opcode_t) p1[mcnt-3] == jump_past_alt)
+ {
+ /* `mcnt' holds how many bytes long the alternative
+ is, including the ending `jump_past_alt' and
+ its number. */
+
+ if (!alt_match_null_string_p (p1, p1 + mcnt - 3,
+ reg_info))
+ return false;
+
+ /* Move to right after this alternative, including the
+ jump_past_alt. */
+ p1 += mcnt;
+
+ /* Break if it's the beginning of an n-th alternative
+ that doesn't begin with an on_failure_jump. */
+ if ((re_opcode_t) *p1 != on_failure_jump)
+ break;
+
+ /* Still have to check that it's not an n-th
+ alternative that starts with an on_failure_jump. */
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ if ((re_opcode_t) p1[mcnt-3] != jump_past_alt)
+ {
+ /* Get to the beginning of the n-th alternative. */
+ p1 -= 3;
+ break;
+ }
+ }
+
+ /* Deal with the last alternative: go back and get number
+ of the `jump_past_alt' just before it. `mcnt' contains
+ the length of the alternative. */
+ EXTRACT_NUMBER (mcnt, p1 - 2);
+
+ if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info))
+ return false;
+
+ p1 += mcnt; /* Get past the n-th alternative. */
+ } /* if mcnt > 0 */
+ break;
+
+
+ case stop_memory:
+ assert (p1[1] == **p);
+ *p = p1 + 2;
+ return true;
+
+
+ default:
+ if (!common_op_match_null_string_p (&p1, end, reg_info))
+ return false;
+ }
+ } /* while p1 < end */
+
+ return false;
+} /* group_match_null_string_p */
+
+
+/* Similar to group_match_null_string_p, but doesn't deal with alternatives:
+ It expects P to be the first byte of a single alternative and END one
+ byte past the last. The alternative can contain groups. */
+
+static boolean
+alt_match_null_string_p (p, end, reg_info)
+ unsigned char *p, *end;
+ register_info_type *reg_info;
+{
+ int mcnt;
+ unsigned char *p1 = p;
+
+ while (p1 < end)
+ {
+ /* Skip over opcodes that can match nothing, and break when we get
+ to one that can't. */
+
+ switch ((re_opcode_t) *p1)
+ {
+ /* It's a loop. */
+ case on_failure_jump:
+ p1++;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ p1 += mcnt;
+ break;
+
+ default:
+ if (!common_op_match_null_string_p (&p1, end, reg_info))
+ return false;
+ }
+ } /* while p1 < end */
+
+ return true;
+} /* alt_match_null_string_p */
+
+
+/* Deals with the ops common to group_match_null_string_p and
+ alt_match_null_string_p.
+
+ Sets P to one after the op and its arguments, if any. */
+
+static boolean
+common_op_match_null_string_p (p, end, reg_info)
+ unsigned char **p, *end;
+ register_info_type *reg_info;
+{
+ int mcnt;
+ boolean ret;
+ int reg_no;
+ unsigned char *p1 = *p;
+
+ switch ((re_opcode_t) *p1++)
+ {
+ case no_op:
+ case begline:
+ case endline:
+ case begbuf:
+ case endbuf:
+ case wordbeg:
+ case wordend:
+ case wordbound:
+ case notwordbound:
+#ifdef emacs
+ case before_dot:
+ case at_dot:
+ case after_dot:
+#endif
+ break;
+
+ case start_memory:
+ reg_no = *p1;
+ assert (reg_no > 0 && reg_no <= MAX_REGNUM);
+ ret = group_match_null_string_p (&p1, end, reg_info);
+
+ /* Have to set this here in case we're checking a group which
+ contains a group and a back reference to it. */
+
+ if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE)
+ REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret;
+
+ if (!ret)
+ return false;
+ break;
+
+ /* If this is an optimized succeed_n for zero times, make the jump. */
+ case jump:
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ if (mcnt >= 0)
+ p1 += mcnt;
+ else
+ return false;
+ break;
+
+ case succeed_n:
+ /* Get to the number of times to succeed. */
+ p1 += 2;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+
+ if (mcnt == 0)
+ {
+ p1 -= 4;
+ EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+ p1 += mcnt;
+ }
+ else
+ return false;
+ break;
+
+ case duplicate:
+ if (!REG_MATCH_NULL_STRING_P (reg_info[*p1]))
+ return false;
+ break;
+
+ case set_number_at:
+ p1 += 4;
+
+ default:
+ /* All other opcodes mean we cannot match the empty string. */
+ return false;
+ }
+
+ *p = p1;
+ return true;
+} /* common_op_match_null_string_p */
+
+
+/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN
+ bytes; nonzero otherwise. */
+
+static int
+bcmp_translate (s1, s2, len, translate)
+ unsigned char *s1, *s2;
+ register int len;
+ char *translate;
+{
+ register unsigned char *p1 = s1, *p2 = s2;
+ while (len)
+ {
+ if (translate[*p1++] != translate[*p2++]) return 1;
+ len--;
+ }
+ return 0;
+}
+
+/* Entry points for GNU code. */
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+ compiles PATTERN (of length SIZE) and puts the result in BUFP.
+ Returns 0 if the pattern was valid, otherwise an error string.
+
+ Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+ are set in BUFP on entry.
+
+ We call regex_compile to do the actual compilation. */
+
+const char *
+re_compile_pattern (pattern, length, bufp)
+ const char *pattern;
+ int length;
+ struct re_pattern_buffer *bufp;
+{
+ reg_errcode_t ret;
+
+ /* GNU code is written to assume at least RE_NREGS registers will be set
+ (and at least one extra will be -1). */
+ bufp->regs_allocated = REGS_UNALLOCATED;
+
+ /* And GNU code determines whether or not to get register information
+ by passing null for the REGS argument to re_match, etc., not by
+ setting no_sub. */
+ bufp->no_sub = 0;
+
+ /* Match anchors at newline. */
+ bufp->newline_anchor = 1;
+
+ ret = regex_compile (pattern, length, re_syntax_options, bufp);
+
+ return re_error_msg[(int) ret];
+}
+
+/* Entry points compatible with 4.2 BSD regex library. We don't define
+ them if this is an Emacs or POSIX compilation. */
+
+#if !defined (emacs) && !defined (_POSIX_SOURCE)
+
+/* BSD has one and only one pattern buffer. */
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+re_comp (s)
+ const char *s;
+{
+ reg_errcode_t ret;
+
+ if (!s)
+ {
+ if (!re_comp_buf.buffer)
+ return "No previous regular expression";
+ return 0;
+ }
+
+ if (!re_comp_buf.buffer)
+ {
+ re_comp_buf.buffer = (unsigned char *) malloc (200);
+ if (re_comp_buf.buffer == NULL)
+ return "Memory exhausted";
+ re_comp_buf.allocated = 200;
+
+ re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH);
+ if (re_comp_buf.fastmap == NULL)
+ return "Memory exhausted";
+ }
+
+ /* Since `re_exec' always passes NULL for the `regs' argument, we
+ don't need to initialize the pattern buffer fields which affect it. */
+
+ /* Match anchors at newlines. */
+ re_comp_buf.newline_anchor = 1;
+
+ ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf);
+
+ /* Yes, we're discarding `const' here. */
+ return (char *) re_error_msg[(int) ret];
+}
+
+
+int
+re_exec (s)
+ const char *s;
+{
+ const int len = strlen (s);
+ return
+ 0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0);
+}
+#endif /* not emacs and not _POSIX_SOURCE */
+
+/* POSIX.2 functions. Don't define these for Emacs. */
+
+#ifndef emacs
+
+/* regcomp takes a regular expression as a string and compiles it.
+
+ PREG is a regex_t *. We do not expect any fields to be initialized,
+ since POSIX says we shouldn't. Thus, we set
+
+ `buffer' to the compiled pattern;
+ `used' to the length of the compiled pattern;
+ `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+ REG_EXTENDED bit in CFLAGS is set; otherwise, to
+ RE_SYNTAX_POSIX_BASIC;
+ `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+ `fastmap' and `fastmap_accurate' to zero;
+ `re_nsub' to the number of subexpressions in PATTERN.
+
+ PATTERN is the address of the pattern string.
+
+ CFLAGS is a series of bits which affect compilation.
+
+ If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+ use POSIX basic syntax.
+
+ If REG_NEWLINE is set, then . and [^...] don't match newline.
+ Also, regexec will try a match beginning after every newline.
+
+ If REG_ICASE is set, then we considers upper- and lowercase
+ versions of letters to be equivalent when matching.
+
+ If REG_NOSUB is set, then when PREG is passed to regexec, that
+ routine will report only success or failure, and nothing about the
+ registers.
+
+ It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for
+ the return codes and their meanings.) */
+
+int
+regcomp (preg, pattern, cflags)
+ regex_t *preg;
+ const char *pattern;
+ int cflags;
+{
+ reg_errcode_t ret;
+ unsigned syntax
+ = (cflags & REG_EXTENDED) ?
+ RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC;
+
+ /* regex_compile will allocate the space for the compiled pattern. */
+ preg->buffer = 0;
+ preg->allocated = 0;
+
+ /* Don't bother to use a fastmap when searching. This simplifies the
+ REG_NEWLINE case: if we used a fastmap, we'd have to put all the
+ characters after newlines into the fastmap. This way, we just try
+ every character. */
+ preg->fastmap = 0;
+
+ if (cflags & REG_ICASE)
+ {
+ unsigned i;
+
+ preg->translate = (char *) malloc (CHAR_SET_SIZE);
+ if (preg->translate == NULL)
+ return (int) REG_ESPACE;
+
+ /* Map uppercase characters to corresponding lowercase ones. */
+ for (i = 0; i < CHAR_SET_SIZE; i++)
+ preg->translate[i] = ISUPPER (i) ? tolower (i) : i;
+ }
+ else
+ preg->translate = NULL;
+
+ /* If REG_NEWLINE is set, newlines are treated differently. */
+ if (cflags & REG_NEWLINE)
+ { /* REG_NEWLINE implies neither . nor [^...] match newline. */
+ syntax &= ~RE_DOT_NEWLINE;
+ syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+ /* It also changes the matching behavior. */
+ preg->newline_anchor = 1;
+ }
+ else
+ preg->newline_anchor = 0;
+
+ preg->no_sub = !!(cflags & REG_NOSUB);
+
+ /* POSIX says a null character in the pattern terminates it, so we
+ can use strlen here in compiling the pattern. */
+ ret = regex_compile (pattern, strlen (pattern), syntax, preg);
+
+ /* POSIX doesn't distinguish between an unmatched open-group and an
+ unmatched close-group: both are REG_EPAREN. */
+ if (ret == REG_ERPAREN) ret = REG_EPAREN;
+
+ return (int) ret;
+}
+
+
+/* regexec searches for a given pattern, specified by PREG, in the
+ string STRING.
+
+ If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+ `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at
+ least NMATCH elements, and we set them to the offsets of the
+ corresponding matched substrings.
+
+ EFLAGS specifies `execution flags' which affect matching: if
+ REG_NOTBOL is set, then ^ does not match at the beginning of the
+ string; if REG_NOTEOL is set, then $ does not match at the end.
+
+ We return 0 if we find a match and REG_NOMATCH if not. */
+
+int
+regexec (preg, string, nmatch, pmatch, eflags)
+ const regex_t *preg;
+ const char *string;
+ size_t nmatch;
+ regmatch_t pmatch[];
+ int eflags;
+{
+ int ret;
+ struct re_registers regs;
+ regex_t private_preg;
+ int len = strlen (string);
+ boolean want_reg_info = !preg->no_sub && nmatch > 0;
+
+ private_preg = *preg;
+
+ private_preg.not_bol = !!(eflags & REG_NOTBOL);
+ private_preg.not_eol = !!(eflags & REG_NOTEOL);
+
+ /* The user has told us exactly how many registers to return
+ information about, via `nmatch'. We have to pass that on to the
+ matching routines. */
+ private_preg.regs_allocated = REGS_FIXED;
+
+ if (want_reg_info)
+ {
+ regs.num_regs = nmatch;
+ regs.start = TALLOC (nmatch, regoff_t);
+ regs.end = TALLOC (nmatch, regoff_t);
+ if (regs.start == NULL || regs.end == NULL)
+ return (int) REG_NOMATCH;
+ }
+
+ /* Perform the searching operation. */
+ ret = re_search (&private_preg, string, len,
+ /* start: */ 0, /* range: */ len,
+ want_reg_info ? &regs : (struct re_registers *) 0);
+
+ /* Copy the register information to the POSIX structure. */
+ if (want_reg_info)
+ {
+ if (ret >= 0)
+ {
+ unsigned r;
+
+ for (r = 0; r < nmatch; r++)
+ {
+ pmatch[r].rm_so = regs.start[r];
+ pmatch[r].rm_eo = regs.end[r];
+ }
+ }
+
+ /* If we needed the temporary register info, free the space now. */
+ free (regs.start);
+ free (regs.end);
+ }
+
+ /* We want zero return to mean success, unlike `re_search'. */
+ return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH;
+}
+
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+ from either regcomp or regexec. We don't use PREG here. */
+
+size_t
+regerror (errcode, preg, errbuf, errbuf_size)
+ int errcode;
+ const regex_t *preg;
+ char *errbuf;
+ size_t errbuf_size;
+{
+ const char *msg;
+ size_t msg_size;
+
+ if (errcode < 0
+ || errcode >= (sizeof (re_error_msg) / sizeof (re_error_msg[0])))
+ /* Only error codes returned by the rest of the code should be passed
+ to this routine. If we are given anything else, or if other regex
+ code generates an invalid error code, then the program has a bug.
+ Dump core so we can fix it. */
+ abort ();
+
+ msg_size = strlen (msg) + 1; /* Includes the null. */
+
+ if (errbuf_size != 0)
+ {
+ if (msg_size > errbuf_size)
+ {
+ strncpy (errbuf, msg, errbuf_size - 1);
+ errbuf[errbuf_size - 1] = 0;
+ }
+ else
+ strcpy (errbuf, msg);
+ }
+
+ return msg_size;
+}
+
+
+/* Free dynamically allocated space used by PREG. */
+
+void
+regfree (preg)
+ regex_t *preg;
+{
+ if (preg->buffer != NULL)
+ free (preg->buffer);
+ preg->buffer = NULL;
+
+ preg->allocated = 0;
+ preg->used = 0;
+
+ if (preg->fastmap != NULL)
+ free (preg->fastmap);
+ preg->fastmap = NULL;
+ preg->fastmap_accurate = 0;
+
+ if (preg->translate != NULL)
+ free (preg->translate);
+ preg->translate = NULL;
+}
+
+#endif /* not emacs */
+
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
diff --git a/gnu/usr.bin/tar/regex.h b/gnu/usr.bin/tar/regex.h
new file mode 100644
index 0000000..0840861
--- /dev/null
+++ b/gnu/usr.bin/tar/regex.h
@@ -0,0 +1,490 @@
+/* Definitions for data structures and routines for the regular
+ expression library, version 0.11.
+
+ Copyright (C) 1985, 89, 90, 91, 92 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __REGEXP_LIBRARY_H__
+#define __REGEXP_LIBRARY_H__
+
+/* POSIX says that <sys/types.h> must be included (by the caller) before
+ <regex.h>. */
+
+#ifdef VMS
+/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it
+ should be there. */
+#include <stddef.h>
+#endif
+
+
+/* The following bits are used to determine the regexp syntax we
+ recognize. The set/not-set meanings are chosen so that Emacs syntax
+ remains the value 0. The bits are given in alphabetical order, and
+ the definitions shifted by one from the previous bit; thus, when we
+ add or remove a bit, only one other definition need change. */
+typedef unsigned reg_syntax_t;
+
+/* If this bit is not set, then \ inside a bracket expression is literal.
+ If set, then such a \ quotes the following character. */
+#define RE_BACKSLASH_ESCAPE_IN_LISTS (1)
+
+/* If this bit is not set, then + and ? are operators, and \+ and \? are
+ literals.
+ If set, then \+ and \? are operators and + and ? are literals. */
+#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
+
+/* If this bit is set, then character classes are supported. They are:
+ [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
+ [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+ If not set, then character classes are not supported. */
+#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
+
+/* If this bit is set, then ^ and $ are always anchors (outside bracket
+ expressions, of course).
+ If this bit is not set, then it depends:
+ ^ is an anchor if it is at the beginning of a regular
+ expression or after an open-group or an alternation operator;
+ $ is an anchor if it is at the end of a regular expression, or
+ before a close-group or an alternation operator.
+
+ This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
+ POSIX draft 11.2 says that * etc. in leading positions is undefined.
+ We already implemented a previous draft which made those constructs
+ invalid, though, so we haven't changed the code back. */
+#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
+
+/* If this bit is set, then special characters are always special
+ regardless of where they are in the pattern.
+ If this bit is not set, then special characters are special only in
+ some contexts; otherwise they are ordinary. Specifically,
+ * + ? and intervals are only special when not after the beginning,
+ open-group, or alternation operator. */
+#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
+
+/* If this bit is set, then *, +, ?, and { cannot be first in an re or
+ immediately after an alternation or begin-group operator. */
+#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
+
+/* If this bit is set, then . matches newline.
+ If not set, then it doesn't. */
+#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
+
+/* If this bit is set, then . doesn't match NUL.
+ If not set, then it does. */
+#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
+
+/* If this bit is set, nonmatching lists [^...] do not match newline.
+ If not set, they do. */
+#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
+
+/* If this bit is set, either \{...\} or {...} defines an
+ interval, depending on RE_NO_BK_BRACES.
+ If not set, \{, \}, {, and } are literals. */
+#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
+
+/* If this bit is set, +, ? and | aren't recognized as operators.
+ If not set, they are. */
+#define RE_LIMITED_OPS (RE_INTERVALS << 1)
+
+/* If this bit is set, newline is an alternation operator.
+ If not set, newline is literal. */
+#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
+
+/* If this bit is set, then `{...}' defines an interval, and \{ and \}
+ are literals.
+ If not set, then `\{...\}' defines an interval. */
+#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
+
+/* If this bit is set, (...) defines a group, and \( and \) are literals.
+ If not set, \(...\) defines a group, and ( and ) are literals. */
+#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
+
+/* If this bit is set, then \<digit> matches <digit>.
+ If not set, then \<digit> is a back-reference. */
+#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
+
+/* If this bit is set, then | is an alternation operator, and \| is literal.
+ If not set, then \| is an alternation operator, and | is literal. */
+#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
+
+/* If this bit is set, then an ending range point collating higher
+ than the starting range point, as in [z-a], is invalid.
+ If not set, then when ending range point collates higher than the
+ starting range point, the range is ignored. */
+#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
+
+/* If this bit is set, then an unmatched ) is ordinary.
+ If not set, then an unmatched ) is invalid. */
+#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+
+/* This global variable defines the particular regexp syntax to use (for
+ some interfaces). When a regexp is compiled, the syntax used is
+ stored in the pattern buffer, so changing this does not affect
+ already-compiled regexps. */
+extern reg_syntax_t re_syntax_options;
+
+/* Define combinations of the above bits for the standard possibilities.
+ (The [[[ comments delimit what gets put into the Texinfo file, so
+ don't delete them!) */
+/* [[[begin syntaxes]]] */
+#define RE_SYNTAX_EMACS 0
+
+#define RE_SYNTAX_AWK \
+ (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS \
+ | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \
+ | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+#define RE_SYNTAX_POSIX_AWK \
+ (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS)
+
+#define RE_SYNTAX_GREP \
+ (RE_BK_PLUS_QM | RE_CHAR_CLASSES \
+ | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \
+ | RE_NEWLINE_ALT)
+
+#define RE_SYNTAX_EGREP \
+ (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \
+ | RE_NEWLINE_ALT | RE_NO_BK_PARENS \
+ | RE_NO_BK_VBAR)
+
+#define RE_SYNTAX_POSIX_EGREP \
+ (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
+
+/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */
+#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
+
+#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
+
+/* Syntax bits common to both basic and extended POSIX regex syntax. */
+#define _RE_SYNTAX_POSIX_COMMON \
+ (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \
+ | RE_INTERVALS | RE_NO_EMPTY_RANGES)
+
+#define RE_SYNTAX_POSIX_BASIC \
+ (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM)
+
+/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
+ RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this
+ isn't minimal, since other operators, such as \`, aren't disabled. */
+#define RE_SYNTAX_POSIX_MINIMAL_BASIC \
+ (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
+
+#define RE_SYNTAX_POSIX_EXTENDED \
+ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \
+ | RE_NO_BK_PARENS | RE_NO_BK_VBAR \
+ | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS
+ replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added. */
+#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \
+ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS \
+ | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD)
+/* [[[end syntaxes]]] */
+
+/* Maximum number of duplicates an interval can allow. Some systems
+ (erroneously) define this in other header files, but we want our
+ value, so remove any previous define. */
+#ifdef RE_DUP_MAX
+#undef RE_DUP_MAX
+#endif
+#define RE_DUP_MAX ((1 << 15) - 1)
+
+
+/* POSIX `cflags' bits (i.e., information for `regcomp'). */
+
+/* If this bit is set, then use extended regular expression syntax.
+ If not set, then use basic regular expression syntax. */
+#define REG_EXTENDED 1
+
+/* If this bit is set, then ignore case when matching.
+ If not set, then case is significant. */
+#define REG_ICASE (REG_EXTENDED << 1)
+
+/* If this bit is set, then anchors do not match at newline
+ characters in the string.
+ If not set, then anchors do match at newlines. */
+#define REG_NEWLINE (REG_ICASE << 1)
+
+/* If this bit is set, then report only success or fail in regexec.
+ If not set, then returns differ between not matching and errors. */
+#define REG_NOSUB (REG_NEWLINE << 1)
+
+
+/* POSIX `eflags' bits (i.e., information for regexec). */
+
+/* If this bit is set, then the beginning-of-line operator doesn't match
+ the beginning of the string (presumably because it's not the
+ beginning of a line).
+ If not set, then the beginning-of-line operator does match the
+ beginning of the string. */
+#define REG_NOTBOL 1
+
+/* Like REG_NOTBOL, except for the end-of-line. */
+#define REG_NOTEOL (1 << 1)
+
+
+/* If any error codes are removed, changed, or added, update the
+ `re_error_msg' table in regex.c. */
+typedef enum
+{
+ REG_NOERROR = 0, /* Success. */
+ REG_NOMATCH, /* Didn't find a match (for regexec). */
+
+ /* POSIX regcomp return error codes. (In the order listed in the
+ standard.) */
+ REG_BADPAT, /* Invalid pattern. */
+ REG_ECOLLATE, /* Not implemented. */
+ REG_ECTYPE, /* Invalid character class name. */
+ REG_EESCAPE, /* Trailing backslash. */
+ REG_ESUBREG, /* Invalid back reference. */
+ REG_EBRACK, /* Unmatched left bracket. */
+ REG_EPAREN, /* Parenthesis imbalance. */
+ REG_EBRACE, /* Unmatched \{. */
+ REG_BADBR, /* Invalid contents of \{\}. */
+ REG_ERANGE, /* Invalid range end. */
+ REG_ESPACE, /* Ran out of memory. */
+ REG_BADRPT, /* No preceding re for repetition op. */
+
+ /* Error codes we've added. */
+ REG_EEND, /* Premature end. */
+ REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */
+ REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */
+} reg_errcode_t;
+
+/* This data structure represents a compiled pattern. Before calling
+ the pattern compiler, the fields `buffer', `allocated', `fastmap',
+ `translate', and `no_sub' can be set. After the pattern has been
+ compiled, the `re_nsub' field is available. All other fields are
+ private to the regex routines. */
+
+struct re_pattern_buffer
+{
+/* [[[begin pattern_buffer]]] */
+ /* Space that holds the compiled pattern. It is declared as
+ `unsigned char *' because its elements are
+ sometimes used as array indexes. */
+ unsigned char *buffer;
+
+ /* Number of bytes to which `buffer' points. */
+ unsigned long allocated;
+
+ /* Number of bytes actually used in `buffer'. */
+ unsigned long used;
+
+ /* Syntax setting with which the pattern was compiled. */
+ reg_syntax_t syntax;
+
+ /* Pointer to a fastmap, if any, otherwise zero. re_search uses
+ the fastmap, if there is one, to skip over impossible
+ starting points for matches. */
+ char *fastmap;
+
+ /* Either a translate table to apply to all characters before
+ comparing them, or zero for no translation. The translation
+ is applied to a pattern when it is compiled and to a string
+ when it is matched. */
+ char *translate;
+
+ /* Number of subexpressions found by the compiler. */
+ size_t re_nsub;
+
+ /* Zero if this pattern cannot match the empty string, one else.
+ Well, in truth it's used only in `re_search_2', to see
+ whether or not we should use the fastmap, so we don't set
+ this absolutely perfectly; see `re_compile_fastmap' (the
+ `duplicate' case). */
+ unsigned can_be_null : 1;
+
+ /* If REGS_UNALLOCATED, allocate space in the `regs' structure
+ for `max (RE_NREGS, re_nsub + 1)' groups.
+ If REGS_REALLOCATE, reallocate space if necessary.
+ If REGS_FIXED, use what's there. */
+#define REGS_UNALLOCATED 0
+#define REGS_REALLOCATE 1
+#define REGS_FIXED 2
+ unsigned regs_allocated : 2;
+
+ /* Set to zero when `regex_compile' compiles a pattern; set to one
+ by `re_compile_fastmap' if it updates the fastmap. */
+ unsigned fastmap_accurate : 1;
+
+ /* If set, `re_match_2' does not return information about
+ subexpressions. */
+ unsigned no_sub : 1;
+
+ /* If set, a beginning-of-line anchor doesn't match at the
+ beginning of the string. */
+ unsigned not_bol : 1;
+
+ /* Similarly for an end-of-line anchor. */
+ unsigned not_eol : 1;
+
+ /* If true, an anchor at a newline matches. */
+ unsigned newline_anchor : 1;
+
+/* [[[end pattern_buffer]]] */
+};
+
+typedef struct re_pattern_buffer regex_t;
+
+
+/* search.c (search_buffer) in Emacs needs this one opcode value. It is
+ defined both in `regex.c' and here. */
+#define RE_EXACTN_VALUE 1
+
+/* Type for byte offsets within the string. POSIX mandates this. */
+typedef int regoff_t;
+
+
+/* This is the structure we store register match data in. See
+ regex.texinfo for a full description of what registers match. */
+struct re_registers
+{
+ unsigned num_regs;
+ regoff_t *start;
+ regoff_t *end;
+};
+
+
+/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
+ `re_match_2' returns information about at least this many registers
+ the first time a `regs' structure is passed. */
+#ifndef RE_NREGS
+#define RE_NREGS 30
+#endif
+
+
+/* POSIX specification for registers. Aside from the different names than
+ `re_registers', POSIX uses an array of structures, instead of a
+ structure of arrays. */
+typedef struct
+{
+ regoff_t rm_so; /* Byte offset from string's start to substring's start. */
+ regoff_t rm_eo; /* Byte offset from string's start to substring's end. */
+} regmatch_t;
+
+/* Declarations for routines. */
+
+/* To avoid duplicating every routine declaration -- once with a
+ prototype (if we are ANSI), and once without (if we aren't) -- we
+ use the following macro to declare argument types. This
+ unfortunately clutters up the declarations a bit, but I think it's
+ worth it. */
+
+#if __STDC__
+
+#define _RE_ARGS(args) args
+
+#else /* not __STDC__ */
+
+#define _RE_ARGS(args) ()
+
+#endif /* not __STDC__ */
+
+/* Sets the current default syntax to SYNTAX, and return the old syntax.
+ You can also simply assign to the `re_syntax_options' variable. */
+extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));
+
+/* Compile the regular expression PATTERN, with length LENGTH
+ and syntax given by the global `re_syntax_options', into the buffer
+ BUFFER. Return NULL if successful, and an error string if not. */
+extern const char *re_compile_pattern
+ _RE_ARGS ((const char *pattern, int length,
+ struct re_pattern_buffer *buffer));
+
+
+/* Compile a fastmap for the compiled pattern in BUFFER; used to
+ accelerate searches. Return 0 if successful and -2 if was an
+ internal error. */
+extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
+
+
+/* Search in the string STRING (with length LENGTH) for the pattern
+ compiled into BUFFER. Start searching at position START, for RANGE
+ characters. Return the starting position of the match, -1 for no
+ match, or -2 for an internal error. Also return register
+ information in REGS (if REGS and BUFFER->no_sub are nonzero). */
+extern int re_search
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+ int length, int start, int range, struct re_registers *regs));
+
+
+/* Like `re_search', but search in the concatenation of STRING1 and
+ STRING2. Also, stop searching at index START + STOP. */
+extern int re_search_2
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+ int length1, const char *string2, int length2,
+ int start, int range, struct re_registers *regs, int stop));
+
+
+/* Like `re_search', but return how many characters in STRING the regexp
+ in BUFFER matched, starting at position START. */
+extern int re_match
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+ int length, int start, struct re_registers *regs));
+
+
+/* Relates to `re_match' as `re_search_2' relates to `re_search'. */
+extern int re_match_2
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+ int length1, const char *string2, int length2,
+ int start, struct re_registers *regs, int stop));
+
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+ ENDS. Subsequent matches using BUFFER and REGS will use this memory
+ for recording register information. STARTS and ENDS must be
+ allocated with malloc, and must each be at least `NUM_REGS * sizeof
+ (regoff_t)' bytes long.
+
+ If NUM_REGS == 0, then subsequent matches should allocate their own
+ register data.
+
+ Unless this function is called, the first search or match using
+ PATTERN_BUFFER will allocate its own register data, without
+ freeing the old data. */
+extern void re_set_registers
+ _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
+ unsigned num_regs, regoff_t *starts, regoff_t *ends));
+
+/* 4.2 bsd compatibility. */
+extern char *re_comp _RE_ARGS ((const char *));
+extern int re_exec _RE_ARGS ((const char *));
+
+/* POSIX compatibility. */
+extern int regcomp _RE_ARGS ((regex_t *preg, const char *pattern, int cflags));
+extern int regexec
+ _RE_ARGS ((const regex_t *preg, const char *string, size_t nmatch,
+ regmatch_t pmatch[], int eflags));
+extern size_t regerror
+ _RE_ARGS ((int errcode, const regex_t *preg, char *errbuf,
+ size_t errbuf_size));
+extern void regfree _RE_ARGS ((regex_t *preg));
+
+#endif /* not __REGEXP_LIBRARY_H__ */
+
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
diff --git a/gnu/usr.bin/tar/rmt.h b/gnu/usr.bin/tar/rmt.h
new file mode 100644
index 0000000..2155223
--- /dev/null
+++ b/gnu/usr.bin/tar/rmt.h
@@ -0,0 +1,98 @@
+/* Definitions for communicating with a remote tape drive.
+ Copyright (C) 1988, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if !defined(_POSIX_VERSION)
+#ifdef __MSDOS__
+#include <io.h>
+#else /* !__MSDOS__ */
+extern off_t lseek ();
+#endif /* __MSDOS__ */
+#endif /* _POSIX_VERSION */
+
+#ifdef NO_REMOTE
+#define _isrmt(f) 0
+#define rmtopen open
+#define rmtaccess access
+#define rmtstat stat
+#define rmtcreat creat
+#define rmtlstat lstat
+#define rmtread read
+#define rmtwrite write
+#define rmtlseek lseek
+#define rmtclose close
+#define rmtioctl ioctl
+#define rmtdup dup
+#define rmtfstat fstat
+#define rmtfcntl fcntl
+#define rmtisatty isatty
+
+#else /* !NO_REMOTE */
+
+#define __REM_BIAS 128
+#define RMTIOCTL
+
+#ifndef O_CREAT
+#define O_CREAT 01000
+#endif
+
+extern char *__rmt_path;
+
+#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
+#include <string.h>
+#ifndef index
+#define index strchr
+#endif
+#else
+extern char *index ();
+#endif
+
+#define _remdev(path) (!f_force_local && (__rmt_path=index(path, ':')))
+#define _isrmt(fd) ((fd) >= __REM_BIAS)
+
+#define rmtopen(path,oflag,mode) (_remdev(path) ? __rmt_open(path, oflag, mode, __REM_BIAS) : open(path, oflag, mode))
+#define rmtaccess(path, amode) (_remdev(path) ? 0 : access(path, amode))
+#define rmtstat(path, buf) (_remdev(path) ? (errno = EOPNOTSUPP), -1 : stat(path, buf))
+#define rmtcreat(path, mode) (_remdev(path) ? __rmt_open (path, 1 | O_CREAT, mode, __REM_BIAS) : creat(path, mode))
+#define rmtlstat(path,buf) (_remdev(path) ? (errno = EOPNOTSUPP), -1 : lstat(path,buf))
+
+#define rmtread(fd, buf, n) (_isrmt(fd) ? __rmt_read(fd - __REM_BIAS, buf, n) : read(fd, buf, n))
+#define rmtwrite(fd, buf, n) (_isrmt(fd) ? __rmt_write(fd - __REM_BIAS, buf, n) : write(fd, buf, n))
+#define rmtlseek(fd, off, wh) (_isrmt(fd) ? __rmt_lseek(fd - __REM_BIAS, off, wh) : lseek(fd, off, wh))
+#define rmtclose(fd) (_isrmt(fd) ? __rmt_close(fd - __REM_BIAS) : close(fd))
+#ifdef RMTIOCTL
+#define rmtioctl(fd,req,arg) (_isrmt(fd) ? __rmt_ioctl(fd - __REM_BIAS, req, arg) : ioctl(fd, req, arg))
+#else
+#define rmtioctl(fd,req,arg) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : ioctl(fd, req, arg))
+#endif
+#define rmtdup(fd) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : dup(fd))
+#define rmtfstat(fd, buf) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : fstat(fd, buf))
+#define rmtfcntl(fd,cmd,arg) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : fcntl (fd, cmd, arg))
+#define rmtisatty(fd) (_isrmt(fd) ? 0 : isatty(fd))
+
+#undef RMTIOCTL
+
+int __rmt_open ();
+int __rmt_close ();
+int __rmt_read ();
+int __rmt_write ();
+long __rmt_lseek ();
+int __rmt_ioctl ();
+#endif /* !NO_REMOTE */
diff --git a/gnu/usr.bin/tar/rtapelib.c b/gnu/usr.bin/tar/rtapelib.c
new file mode 100644
index 0000000..eece76f
--- /dev/null
+++ b/gnu/usr.bin/tar/rtapelib.c
@@ -0,0 +1,582 @@
+/* Functions for communicating with a remote tape drive.
+ Copyright (C) 1988, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* The man page rmt(8) for /etc/rmt documents the remote mag tape
+ protocol which rdump and rrestore use. Unfortunately, the man
+ page is *WRONG*. The author of the routines I'm including originally
+ wrote his code just based on the man page, and it didn't work, so he
+ went to the rdump source to figure out why. The only thing he had to
+ change was to check for the 'F' return code in addition to the 'E',
+ and to separate the various arguments with \n instead of a space. I
+ personally don't think that this is much of a problem, but I wanted to
+ point it out. -- Arnold Robbins
+
+ Originally written by Jeff Lee, modified some by Arnold Robbins.
+ Redone as a library that can replace open, read, write, etc., by
+ Fred Fish, with some additional work by Arnold Robbins.
+ Modified to make all rmtXXX calls into macros for speed by Jay Fenlason.
+ Use -DHAVE_NETDB_H for rexec code, courtesy of Dan Kegel, srs!dan. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_MTIO_H
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#include <errno.h>
+#include <setjmp.h>
+#include <sys/stat.h>
+
+#ifndef errno
+extern int errno;
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef STDC_HEADERS
+#include <string.h>
+#include <stdlib.h>
+#endif
+
+/* Maximum size of a fully qualified host name. */
+#define MAXHOSTLEN 257
+
+/* Size of buffers for reading and writing commands to rmt.
+ (An arbitrary limit.) */
+#define CMDBUFSIZE 64
+
+#ifndef RETSIGTYPE
+#define RETSIGTYPE void
+#endif
+
+/* Maximum number of simultaneous remote tape connections.
+ (Another arbitrary limit.) */
+#define MAXUNIT 4
+
+/* Return the parent's read side of remote tape connection FILDES. */
+#define READ(fildes) (from_rmt[fildes][0])
+
+/* Return the parent's write side of remote tape connection FILDES. */
+#define WRITE(fildes) (to_rmt[fildes][1])
+
+/* The pipes for receiving data from remote tape drives. */
+static int from_rmt[MAXUNIT][2] =
+{-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* The pipes for sending data to remote tape drives. */
+static int to_rmt[MAXUNIT][2] =
+{-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Temporary variable used by macros in rmt.h. */
+char *__rmt_path;
+
+/* Close remote tape connection FILDES. */
+
+static void
+_rmt_shutdown (fildes)
+ int fildes;
+{
+ close (READ (fildes));
+ close (WRITE (fildes));
+ READ (fildes) = -1;
+ WRITE (fildes) = -1;
+}
+
+/* Attempt to perform the remote tape command specified in BUF
+ on remote tape connection FILDES.
+ Return 0 if successful, -1 on error. */
+
+static int
+command (fildes, buf)
+ int fildes;
+ char *buf;
+{
+ register int buflen;
+ RETSIGTYPE (*pipe_handler) ();
+
+ /* Save the current pipe handler and try to make the request. */
+
+ pipe_handler = signal (SIGPIPE, SIG_IGN);
+ buflen = strlen (buf);
+ if (write (WRITE (fildes), buf, buflen) == buflen)
+ {
+ signal (SIGPIPE, pipe_handler);
+ return 0;
+ }
+
+ /* Something went wrong. Close down and go home. */
+
+ signal (SIGPIPE, pipe_handler);
+ _rmt_shutdown (fildes);
+ errno = EIO;
+ return -1;
+}
+
+/* Read and return the status from remote tape connection FILDES.
+ If an error occurred, return -1 and set errno. */
+
+static int
+status (fildes)
+ int fildes;
+{
+ int i;
+ char c, *cp;
+ char buffer[CMDBUFSIZE];
+
+ /* Read the reply command line. */
+
+ for (i = 0, cp = buffer; i < CMDBUFSIZE; i++, cp++)
+ {
+ if (read (READ (fildes), cp, 1) != 1)
+ {
+ _rmt_shutdown (fildes);
+ errno = EIO;
+ return -1;
+ }
+ if (*cp == '\n')
+ {
+ *cp = '\0';
+ break;
+ }
+ }
+
+ if (i == CMDBUFSIZE)
+ {
+ _rmt_shutdown (fildes);
+ errno = EIO;
+ return -1;
+ }
+
+ /* Check the return status. */
+
+ for (cp = buffer; *cp; cp++)
+ if (*cp != ' ')
+ break;
+
+ if (*cp == 'E' || *cp == 'F')
+ {
+ errno = atoi (cp + 1);
+ /* Skip the error message line. */
+ while (read (READ (fildes), &c, 1) == 1)
+ if (c == '\n')
+ break;
+
+ if (*cp == 'F')
+ _rmt_shutdown (fildes);
+
+ return -1;
+ }
+
+ /* Check for mis-synced pipes. */
+
+ if (*cp != 'A')
+ {
+ _rmt_shutdown (fildes);
+ errno = EIO;
+ return -1;
+ }
+
+ /* Got an `A' (success) response. */
+ return atoi (cp + 1);
+}
+
+#ifdef HAVE_NETDB_H
+/* Execute /etc/rmt as user USER on remote system HOST using rexec.
+ Return a file descriptor of a bidirectional socket for stdin and stdout.
+ If USER is NULL, or an empty string, use the current username.
+
+ By default, this code is not used, since it requires that
+ the user have a .netrc file in his/her home directory, or that the
+ application designer be willing to have rexec prompt for login and
+ password info. This may be unacceptable, and .rhosts files for use
+ with rsh are much more common on BSD systems. */
+
+static int
+_rmt_rexec (host, user)
+ char *host;
+ char *user;
+{
+ struct servent *rexecserv;
+ int save_stdin = dup (fileno (stdin));
+ int save_stdout = dup (fileno (stdout));
+ int tape_fd; /* Return value. */
+
+ /* When using cpio -o < filename, stdin is no longer the tty.
+ But the rexec subroutine reads the login and the passwd on stdin,
+ to allow remote execution of the command.
+ So, reopen stdin and stdout on /dev/tty before the rexec and
+ give them back their original value after. */
+ if (freopen ("/dev/tty", "r", stdin) == NULL)
+ freopen ("/dev/null", "r", stdin);
+ if (freopen ("/dev/tty", "w", stdout) == NULL)
+ freopen ("/dev/null", "w", stdout);
+
+ rexecserv = getservbyname ("exec", "tcp");
+ if (NULL == rexecserv)
+ {
+ fprintf (stderr, "exec/tcp: service not available");
+ exit (1);
+ }
+ if (user != NULL && *user == '\0')
+ user = NULL;
+ tape_fd = rexec (&host, rexecserv->s_port, user, NULL,
+ "/etc/rmt", (int *) NULL);
+ fclose (stdin);
+ fdopen (save_stdin, "r");
+ fclose (stdout);
+ fdopen (save_stdout, "w");
+
+ return tape_fd;
+}
+
+#endif /* HAVE_NETDB_H */
+
+/* Open a magtape device on the system specified in PATH, as the given user.
+ PATH has the form `[user@]system:/dev/????'.
+ If COMPAT is defined, it can also have the form `system[.user]:/dev/????'.
+
+ OFLAG is O_RDONLY, O_WRONLY, etc.
+ MODE is ignored; 0666 is always used.
+
+ If successful, return the remote tape pipe number plus BIAS.
+ On error, return -1. */
+
+int
+__rmt_open (path, oflag, mode, bias)
+ char *path;
+ int oflag;
+ int mode;
+ int bias;
+{
+ int i, rc;
+ char buffer[CMDBUFSIZE]; /* Command buffer. */
+ char system[MAXHOSTLEN]; /* The remote host name. */
+ char device[CMDBUFSIZE]; /* The remote device name. */
+ char login[CMDBUFSIZE]; /* The remote user name. */
+ char *sys, *dev, *user; /* For copying into the above buffers. */
+
+ sys = system;
+ dev = device;
+ user = login;
+
+ /* Find an unused pair of file descriptors. */
+
+ for (i = 0; i < MAXUNIT; i++)
+ if (READ (i) == -1 && WRITE (i) == -1)
+ break;
+
+ if (i == MAXUNIT)
+ {
+ errno = EMFILE;
+ return -1;
+ }
+
+ /* Pull apart the system and device, and optional user.
+ Don't munge the original string. */
+
+ while (*path != '@'
+#ifdef COMPAT
+ && *path != '.'
+#endif
+ && *path != ':')
+ {
+ *sys++ = *path++;
+ }
+ *sys = '\0';
+ path++;
+
+ if (*(path - 1) == '@')
+ {
+ /* Saw user part of user@host. Start over. */
+ strcpy (user, system);
+ sys = system;
+ while (*path != ':')
+ {
+ *sys++ = *path++;
+ }
+ *sys = '\0';
+ path++;
+ }
+#ifdef COMPAT
+ else if (*(path - 1) == '.')
+ {
+ while (*path != ':')
+ {
+ *user++ = *path++;
+ }
+ *user = '\0';
+ path++;
+ }
+#endif
+ else
+ *user = '\0';
+
+ while (*path)
+ {
+ *dev++ = *path++;
+ }
+ *dev = '\0';
+
+#ifdef HAVE_NETDB_H
+ /* Execute the remote command using rexec. */
+ READ (i) = WRITE (i) = _rmt_rexec (system, login);
+ if (READ (i) < 0)
+ return -1;
+#else /* !HAVE_NETDB_H */
+ /* Set up the pipes for the `rsh' command, and fork. */
+
+ if (pipe (to_rmt[i]) == -1 || pipe (from_rmt[i]) == -1)
+ return -1;
+
+ rc = fork ();
+ if (rc == -1)
+ return -1;
+
+ if (rc == 0)
+ {
+ /* Child. */
+ close (0);
+ dup (to_rmt[i][0]);
+ close (to_rmt[i][0]);
+ close (to_rmt[i][1]);
+
+ close (1);
+ dup (from_rmt[i][1]);
+ close (from_rmt[i][0]);
+ close (from_rmt[i][1]);
+
+ setuid (getuid ());
+ setgid (getgid ());
+
+ if (*login)
+ {
+ execl ("/usr/ucb/rsh", "rsh", system, "-l", login,
+ "/etc/rmt", (char *) 0);
+ execl ("/usr/bin/remsh", "remsh", system, "-l", login,
+ "/etc/rmt", (char *) 0);
+ execl ("/usr/bin/rsh", "rsh", system, "-l", login,
+ "/etc/rmt", (char *) 0);
+ execl ("/usr/bsd/rsh", "rsh", system, "-l", login,
+ "/etc/rmt", (char *) 0);
+ execl ("/usr/bin/nsh", "nsh", system, "-l", login,
+ "/etc/rmt", (char *) 0);
+ }
+ else
+ {
+ execl ("/usr/ucb/rsh", "rsh", system,
+ "/etc/rmt", (char *) 0);
+ execl ("/usr/bin/remsh", "remsh", system,
+ "/etc/rmt", (char *) 0);
+ execl ("/usr/bin/rsh", "rsh", system,
+ "/etc/rmt", (char *) 0);
+ execl ("/usr/bsd/rsh", "rsh", system,
+ "/etc/rmt", (char *) 0);
+ execl ("/usr/bin/nsh", "nsh", system,
+ "/etc/rmt", (char *) 0);
+ }
+
+ /* Bad problems if we get here. */
+
+ perror ("cannot execute remote shell");
+ _exit (1);
+ }
+
+ /* Parent. */
+ close (to_rmt[i][0]);
+ close (from_rmt[i][1]);
+#endif /* !HAVE_NETDB_H */
+
+ /* Attempt to open the tape device. */
+
+ sprintf (buffer, "O%s\n%d\n", device, oflag);
+ if (command (i, buffer) == -1 || status (i) == -1)
+ return -1;
+
+ return i + bias;
+}
+
+/* Close remote tape connection FILDES and shut down.
+ Return 0 if successful, -1 on error. */
+
+int
+__rmt_close (fildes)
+ int fildes;
+{
+ int rc;
+
+ if (command (fildes, "C\n") == -1)
+ return -1;
+
+ rc = status (fildes);
+ _rmt_shutdown (fildes);
+ return rc;
+}
+
+/* Read up to NBYTE bytes into BUF from remote tape connection FILDES.
+ Return the number of bytes read on success, -1 on error. */
+
+int
+__rmt_read (fildes, buf, nbyte)
+ int fildes;
+ char *buf;
+ unsigned int nbyte;
+{
+ int rc, i;
+ char buffer[CMDBUFSIZE];
+
+ sprintf (buffer, "R%d\n", nbyte);
+ if (command (fildes, buffer) == -1 || (rc = status (fildes)) == -1)
+ return -1;
+
+ for (i = 0; i < rc; i += nbyte, buf += nbyte)
+ {
+ nbyte = read (READ (fildes), buf, rc - i);
+ if (nbyte <= 0)
+ {
+ _rmt_shutdown (fildes);
+ errno = EIO;
+ return -1;
+ }
+ }
+
+ return rc;
+}
+
+/* Write NBYTE bytes from BUF to remote tape connection FILDES.
+ Return the number of bytes written on success, -1 on error. */
+
+int
+__rmt_write (fildes, buf, nbyte)
+ int fildes;
+ char *buf;
+ unsigned int nbyte;
+{
+ char buffer[CMDBUFSIZE];
+ RETSIGTYPE (*pipe_handler) ();
+
+ sprintf (buffer, "W%d\n", nbyte);
+ if (command (fildes, buffer) == -1)
+ return -1;
+
+ pipe_handler = signal (SIGPIPE, SIG_IGN);
+ if (write (WRITE (fildes), buf, nbyte) == nbyte)
+ {
+ signal (SIGPIPE, pipe_handler);
+ return status (fildes);
+ }
+
+ /* Write error. */
+ signal (SIGPIPE, pipe_handler);
+ _rmt_shutdown (fildes);
+ errno = EIO;
+ return -1;
+}
+
+/* Perform an imitation lseek operation on remote tape connection FILDES.
+ Return the new file offset if successful, -1 if on error. */
+
+long
+__rmt_lseek (fildes, offset, whence)
+ int fildes;
+ long offset;
+ int whence;
+{
+ char buffer[CMDBUFSIZE];
+
+ sprintf (buffer, "L%ld\n%d\n", offset, whence);
+ if (command (fildes, buffer) == -1)
+ return -1;
+
+ return status (fildes);
+}
+
+/* Perform a raw tape operation on remote tape connection FILDES.
+ Return the results of the ioctl, or -1 on error. */
+
+#ifdef MTIOCTOP
+int
+__rmt_ioctl (fildes, op, arg)
+ int fildes, op;
+ char *arg;
+{
+ char c;
+ int rc, cnt;
+ char buffer[CMDBUFSIZE];
+
+ switch (op)
+ {
+ default:
+ errno = EINVAL;
+ return -1;
+
+ case MTIOCTOP:
+ /* MTIOCTOP is the easy one. Nothing is transfered in binary. */
+ sprintf (buffer, "I%d\n%d\n", ((struct mtop *) arg)->mt_op,
+ ((struct mtop *) arg)->mt_count);
+ if (command (fildes, buffer) == -1)
+ return -1;
+ return status (fildes); /* Return the count. */
+
+ case MTIOCGET:
+ /* Grab the status and read it directly into the structure.
+ This assumes that the status buffer is not padded
+ and that 2 shorts fit in a long without any word
+ alignment problems; i.e., the whole struct is contiguous.
+ NOTE - this is probably NOT a good assumption. */
+
+ if (command (fildes, "S") == -1 || (rc = status (fildes)) == -1)
+ return -1;
+
+ for (; rc > 0; rc -= cnt, arg += cnt)
+ {
+ cnt = read (READ (fildes), arg, rc);
+ if (cnt <= 0)
+ {
+ _rmt_shutdown (fildes);
+ errno = EIO;
+ return -1;
+ }
+ }
+
+ /* Check for byte position. mt_type is a small integer field
+ (normally) so we will check its magnitude. If it is larger than
+ 256, we will assume that the bytes are swapped and go through
+ and reverse all the bytes. */
+
+ if (((struct mtget *) arg)->mt_type < 256)
+ return 0;
+
+ for (cnt = 0; cnt < rc; cnt += 2)
+ {
+ c = arg[cnt];
+ arg[cnt] = arg[cnt + 1];
+ arg[cnt + 1] = c;
+ }
+
+ return 0;
+ }
+}
+
+#endif
diff --git a/gnu/usr.bin/tar/tar.c b/gnu/usr.bin/tar/tar.c
new file mode 100644
index 0000000..ec2c3d1
--- /dev/null
+++ b/gnu/usr.bin/tar/tar.c
@@ -0,0 +1,1507 @@
+/* Tar -- a tape archiver.
+ Copyright (C) 1988, 1992, 1993 Free Software Foundation
+
+This file is part of GNU Tar.
+
+GNU Tar is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Tar is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Tar; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * A tar (tape archiver) program.
+ *
+ * Written by John Gilmore, ihnp4!hoptoad!gnu, starting 25 Aug 85.
+ */
+
+#include <stdio.h>
+#include <sys/types.h> /* Needed for typedefs in tar.h */
+#include "getopt.h"
+
+/*
+ * The following causes "tar.h" to produce definitions of all the
+ * global variables, rather than just "extern" declarations of them.
+ */
+#define TAR_EXTERN /**/
+#include "tar.h"
+
+#include "port.h"
+#include "regex.h"
+#include "fnmatch.h"
+
+/*
+ * We should use a conversion routine that does reasonable error
+ * checking -- atoi doesn't. For now, punt. FIXME.
+ */
+#define intconv atoi
+PTR ck_malloc ();
+PTR ck_realloc ();
+extern int getoldopt ();
+extern void read_and ();
+extern void list_archive ();
+extern void extract_archive ();
+extern void diff_archive ();
+extern void create_archive ();
+extern void update_archive ();
+extern void junk_archive ();
+extern void init_volume_number ();
+extern void closeout_volume_number ();
+
+/* JF */
+extern time_t get_date ();
+
+time_t new_time;
+
+static FILE *namef; /* File to read names from */
+static char **n_argv; /* Argv used by name routines */
+static int n_argc; /* Argc used by name routines */
+static char **n_ind; /* Store an array of names */
+static int n_indalloc; /* How big is the array? */
+static int n_indused; /* How many entries does it have? */
+static int n_indscan; /* How many of the entries have we scanned? */
+
+
+extern FILE *msg_file;
+
+int check_exclude ();
+void add_exclude ();
+void add_exclude_file ();
+void addname ();
+void describe ();
+void diff_init ();
+void extr_init ();
+int is_regex ();
+void name_add ();
+void name_init ();
+void options ();
+char *un_quote_string ();
+
+#ifndef S_ISLNK
+#define lstat stat
+#endif
+
+#ifndef DEFBLOCKING
+#define DEFBLOCKING 20
+#endif
+
+#ifndef DEF_AR_FILE
+#define DEF_AR_FILE "tar.out"
+#endif
+
+/* For long options that unconditionally set a single flag, we have getopt
+ do it. For the others, we share the code for the equivalent short
+ named option, the name of which is stored in the otherwise-unused `val'
+ field of the `struct option'; for long options that have no equivalent
+ short option, we use nongraphic characters as pseudo short option
+ characters, starting (for no particular reason) with character 10. */
+
+struct option long_options[] =
+{
+ {"create", 0, 0, 'c'},
+ {"append", 0, 0, 'r'},
+ {"extract", 0, 0, 'x'},
+ {"get", 0, 0, 'x'},
+ {"list", 0, 0, 't'},
+ {"update", 0, 0, 'u'},
+ {"catenate", 0, 0, 'A'},
+ {"concatenate", 0, 0, 'A'},
+ {"compare", 0, 0, 'd'},
+ {"diff", 0, 0, 'd'},
+ {"delete", 0, 0, 14},
+ {"help", 0, 0, 12},
+
+ {"null", 0, 0, 16},
+ {"directory", 1, 0, 'C'},
+ {"record-number", 0, &f_sayblock, 1},
+ {"files-from", 1, 0, 'T'},
+ {"label", 1, 0, 'V'},
+ {"exclude-from", 1, 0, 'X'},
+ {"exclude", 1, 0, 15},
+ {"file", 1, 0, 'f'},
+ {"block-size", 1, 0, 'b'},
+ {"version", 0, 0, 11},
+ {"verbose", 0, 0, 'v'},
+ {"totals", 0, &f_totals, 1},
+
+ {"read-full-blocks", 0, &f_reblock, 1},
+ {"starting-file", 1, 0, 'K'},
+ {"to-stdout", 0, &f_exstdout, 1},
+ {"ignore-zeros", 0, &f_ignorez, 1},
+ {"keep-old-files", 0, 0, 'k'},
+ {"same-permissions", 0, &f_use_protection, 1},
+ {"preserve-permissions", 0, &f_use_protection, 1},
+ {"modification-time", 0, &f_modified, 1},
+ {"preserve", 0, 0, 10},
+ {"same-order", 0, &f_sorted_names, 1},
+ {"same-owner", 0, &f_do_chown, 1},
+ {"preserve-order", 0, &f_sorted_names, 1},
+
+ {"newer", 1, 0, 'N'},
+ {"after-date", 1, 0, 'N'},
+ {"newer-mtime", 1, 0, 13},
+ {"incremental", 0, 0, 'G'},
+ {"listed-incremental", 1, 0, 'g'},
+ {"multi-volume", 0, &f_multivol, 1},
+ {"info-script", 1, 0, 'F'},
+ {"new-volume-script", 1, 0, 'F'},
+ {"absolute-paths", 0, &f_absolute_paths, 1},
+ {"interactive", 0, &f_confirm, 1},
+ {"confirmation", 0, &f_confirm, 1},
+
+ {"verify", 0, &f_verify, 1},
+ {"dereference", 0, &f_follow_links, 1},
+ {"one-file-system", 0, &f_local_filesys, 1},
+ {"old-archive", 0, 0, 'o'},
+ {"portability", 0, 0, 'o'},
+ {"compress", 0, 0, 'Z'},
+ {"uncompress", 0, 0, 'Z'},
+ {"block-compress", 0, &f_compress_block, 1},
+ {"gzip", 0, 0, 'z'},
+ {"ungzip", 0, 0, 'z'},
+ {"use-compress-program", 1, 0, 18},
+
+
+ {"same-permissions", 0, &f_use_protection, 1},
+ {"sparse", 0, &f_sparse_files, 1},
+ {"tape-length", 1, 0, 'L'},
+ {"remove-files", 0, &f_remove_files, 1},
+ {"ignore-failed-read", 0, &f_ignore_failed_read, 1},
+ {"checkpoint", 0, &f_checkpoint, 1},
+ {"show-omitted-dirs", 0, &f_show_omitted_dirs, 1},
+ {"volno-file", 1, 0, 17},
+ {"force-local", 0, &f_force_local, 1},
+ {"atime-preserve", 0, &f_atime_preserve, 1},
+
+ {"unlink", 0, &f_unlink, 1},
+
+ {0, 0, 0, 0}
+};
+
+/*
+ * Main routine for tar.
+ */
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char version_string[];
+
+ tar = argv[0]; /* JF: was "tar" Set program name */
+ filename_terminator = '\n';
+ errors = 0;
+
+ options (argc, argv);
+
+ if (!n_argv)
+ name_init (argc, argv);
+
+ if (f_volno_file)
+ init_volume_number ();
+
+ switch (cmd_mode)
+ {
+ case CMD_CAT:
+ case CMD_UPDATE:
+ case CMD_APPEND:
+ update_archive ();
+ break;
+ case CMD_DELETE:
+ junk_archive ();
+ break;
+ case CMD_CREATE:
+ create_archive ();
+ if (f_totals)
+ fprintf (stderr, "Total bytes written: %d\n", tot_written);
+ break;
+ case CMD_EXTRACT:
+ if (f_volhdr)
+ {
+ const char *err;
+ label_pattern = (struct re_pattern_buffer *)
+ ck_malloc (sizeof *label_pattern);
+ err = re_compile_pattern (f_volhdr, strlen (f_volhdr),
+ label_pattern);
+ if (err)
+ {
+ fprintf (stderr, "Bad regular expression: %s\n",
+ err);
+ errors++;
+ break;
+ }
+
+ }
+ extr_init ();
+ read_and (extract_archive);
+ break;
+ case CMD_LIST:
+ if (f_volhdr)
+ {
+ const char *err;
+ label_pattern = (struct re_pattern_buffer *)
+ ck_malloc (sizeof *label_pattern);
+ err = re_compile_pattern (f_volhdr, strlen (f_volhdr),
+ label_pattern);
+ if (err)
+ {
+ fprintf (stderr, "Bad regular expression: %s\n",
+ err);
+ errors++;
+ break;
+ }
+ }
+ read_and (list_archive);
+#if 0
+ if (!errors)
+ errors = different;
+#endif
+ break;
+ case CMD_DIFF:
+ diff_init ();
+ read_and (diff_archive);
+ break;
+ case CMD_VERSION:
+ fprintf (stderr, "%s\n", version_string);
+ break;
+ case CMD_NONE:
+ msg ("you must specify exactly one of the r, c, t, x, or d options\n");
+ fprintf (stderr, "For more information, type ``%s --help''.\n", tar);
+ exit (EX_ARGSBAD);
+ }
+ if (f_volno_file)
+ closeout_volume_number ();
+ exit (errors);
+ /* NOTREACHED */
+}
+
+
+/*
+ * Parse the options for tar.
+ */
+void
+options (argc, argv)
+ int argc;
+ char **argv;
+{
+ register int c; /* Option letter */
+ int ind = -1;
+
+ /* Set default option values */
+ blocking = DEFBLOCKING; /* From Makefile */
+ ar_files = (char **) ck_malloc (sizeof (char *) * 10);
+ ar_files_len = 10;
+ n_ar_files = 0;
+ cur_ar_file = 0;
+
+ /* Parse options */
+ while ((c = getoldopt (argc, argv,
+ "-01234567Ab:BcC:df:F:g:GhikK:lL:mMN:oOpPrRsStT:uvV:wWxX:zZ",
+ long_options, &ind)) != EOF)
+ {
+ switch (c)
+ {
+ case 0: /* long options that set a single flag */
+ break;
+ case 1:
+ /* File name or non-parsed option */
+ name_add (optarg);
+ break;
+ case 'C':
+ name_add ("-C");
+ name_add (optarg);
+ break;
+ case 10: /* preserve */
+ f_use_protection = f_sorted_names = 1;
+ break;
+ case 11:
+ if (cmd_mode != CMD_NONE)
+ goto badopt;
+ cmd_mode = CMD_VERSION;
+ break;
+ case 12: /* help */
+ printf ("This is GNU tar, the tape archiving program.\n");
+ describe ();
+ exit (1);
+ case 13:
+ f_new_files++;
+ goto get_newer;
+
+ case 14: /* Delete in the archive */
+ if (cmd_mode != CMD_NONE)
+ goto badopt;
+ cmd_mode = CMD_DELETE;
+ break;
+
+ case 15:
+ f_exclude++;
+ add_exclude (optarg);
+ break;
+
+ case 16: /* -T reads null terminated filenames. */
+ filename_terminator = '\0';
+ break;
+
+ case 17:
+ f_volno_file = optarg;
+ break;
+
+ case 18:
+ if (f_compressprog)
+ {
+ msg ("Only one compression option permitted\n");
+ exit (EX_ARGSBAD);
+ }
+ f_compressprog = optarg;
+ break;
+
+ case 'g': /* We are making a GNU dump; save
+ directories at the beginning of
+ the archive, and include in each
+ directory its contents */
+ if (f_oldarch)
+ goto badopt;
+ f_gnudump++;
+ gnu_dumpfile = optarg;
+ break;
+
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ /* JF this'll have to be modified for other
+ systems, of course! */
+ int d, add;
+ static char buf[50];
+
+ d = getoldopt (argc, argv, "lmh");
+#ifdef MAYBEDEF
+ sprintf (buf, "/dev/rmt/%d%c", c, d);
+#else
+#ifndef LOW_NUM
+#define LOW_NUM 0
+#define MID_NUM 8
+#define HGH_NUM 16
+#endif
+ if (d == 'l')
+ add = LOW_NUM;
+ else if (d == 'm')
+ add = MID_NUM;
+ else if (d == 'h')
+ add = HGH_NUM;
+ else
+ goto badopt;
+
+ sprintf (buf, "/dev/rmt%d", add + c - '0');
+#endif
+ if (n_ar_files == ar_files_len)
+ ar_files
+ = (char **)
+ ck_malloc (sizeof (char *)
+ * (ar_files_len *= 2));
+ ar_files[n_ar_files++] = buf;
+ }
+ break;
+
+ case 'A': /* Arguments are tar files,
+ just cat them onto the end
+ of the archive. */
+ if (cmd_mode != CMD_NONE)
+ goto badopt;
+ cmd_mode = CMD_CAT;
+ break;
+
+ case 'b': /* Set blocking factor */
+ blocking = intconv (optarg);
+ break;
+
+ case 'B': /* Try to reblock input */
+ f_reblock++; /* For reading 4.2BSD pipes */
+ break;
+
+ case 'c': /* Create an archive */
+ if (cmd_mode != CMD_NONE)
+ goto badopt;
+ cmd_mode = CMD_CREATE;
+ break;
+
+#if 0
+ case 'C':
+ if (chdir (optarg) < 0)
+ msg_perror ("Can't change directory to %d", optarg);
+ break;
+#endif
+
+ case 'd': /* Find difference tape/disk */
+ if (cmd_mode != CMD_NONE)
+ goto badopt;
+ cmd_mode = CMD_DIFF;
+ break;
+
+ case 'f': /* Use ar_file for the archive */
+ if (n_ar_files == ar_files_len)
+ ar_files
+ = (char **) ck_malloc (sizeof (char *)
+ * (ar_files_len *= 2));
+
+ ar_files[n_ar_files++] = optarg;
+ break;
+
+ case 'F':
+ /* Since -F is only useful with -M , make it implied */
+ f_run_script_at_end++;/* run this script at the end */
+ info_script = optarg; /* of each tape */
+ f_multivol++;
+ break;
+
+ case 'G': /* We are making a GNU dump; save
+ directories at the beginning of
+ the archive, and include in each
+ directory its contents */
+ if (f_oldarch)
+ goto badopt;
+ f_gnudump++;
+ gnu_dumpfile = 0;
+ break;
+
+ case 'h':
+ f_follow_links++; /* follow symbolic links */
+ break;
+
+ case 'i':
+ f_ignorez++; /* Ignore zero records (eofs) */
+ /*
+ * This can't be the default, because Unix tar
+ * writes two records of zeros, then pads out the
+ * block with garbage.
+ */
+ break;
+
+ case 'k': /* Don't overwrite files */
+#ifdef NO_OPEN3
+ msg ("can't keep old files on this system");
+ exit (EX_ARGSBAD);
+#else
+ f_keep++;
+#endif
+ break;
+
+ case 'K':
+ f_startfile++;
+ addname (optarg);
+ break;
+
+ case 'l': /* When dumping directories, don't
+ dump files/subdirectories that are
+ on other filesystems. */
+ f_local_filesys++;
+ break;
+
+ case 'L':
+ tape_length = intconv (optarg);
+ f_multivol++;
+ break;
+ case 'm':
+ f_modified++;
+ break;
+
+ case 'M': /* Make Multivolume archive:
+ When we can't write any more
+ into the archive, re-open it,
+ and continue writing */
+ f_multivol++;
+ break;
+
+ case 'N': /* Only write files newer than X */
+ get_newer:
+ f_new_files++;
+ new_time = get_date (optarg, (PTR) 0);
+ if (new_time == (time_t) - 1)
+ {
+ msg ("invalid date format `%s'", optarg);
+ exit (EX_ARGSBAD);
+ }
+ break;
+
+ case 'o': /* Generate old archive */
+ if (f_gnudump /* || f_dironly */ )
+ goto badopt;
+ f_oldarch++;
+ break;
+
+ case 'O':
+ f_exstdout++;
+ break;
+
+ case 'p':
+ f_use_protection++;
+ break;
+
+ case 'P':
+ f_absolute_paths++;
+ break;
+
+ case 'r': /* Append files to the archive */
+ if (cmd_mode != CMD_NONE)
+ goto badopt;
+ cmd_mode = CMD_APPEND;
+ break;
+
+ case 'R':
+ f_sayblock++; /* Print block #s for debug */
+ break; /* of bad tar archives */
+
+ case 's':
+ f_sorted_names++; /* Names to extr are sorted */
+ break;
+
+ case 'S': /* deal with sparse files */
+ f_sparse_files++;
+ break;
+ case 't':
+ if (cmd_mode != CMD_NONE)
+ goto badopt;
+ cmd_mode = CMD_LIST;
+ f_verbose++; /* "t" output == "cv" or "xv" */
+ break;
+
+ case 'T':
+ name_file = optarg;
+ f_namefile++;
+ break;
+
+ case 'u': /* Append files to the archive that
+ aren't there, or are newer than the
+ copy in the archive */
+ if (cmd_mode != CMD_NONE)
+ goto badopt;
+ cmd_mode = CMD_UPDATE;
+ break;
+
+ case 'v':
+ f_verbose++;
+ break;
+
+ case 'V':
+ f_volhdr = optarg;
+ break;
+
+ case 'w':
+ f_confirm++;
+ break;
+
+ case 'W':
+ f_verify++;
+ break;
+
+ case 'x': /* Extract files from the archive */
+ if (cmd_mode != CMD_NONE)
+ goto badopt;
+ cmd_mode = CMD_EXTRACT;
+ break;
+
+ case 'X':
+ f_exclude++;
+ add_exclude_file (optarg);
+ break;
+
+ case 'z':
+ if (f_compressprog)
+ {
+ msg ("Only one compression option permitted\n");
+ exit (EX_ARGSBAD);
+ }
+ f_compressprog = "gzip";
+ break;
+
+ case 'Z':
+ if (f_compressprog)
+ {
+ msg ("Only one compression option permitted\n");
+ exit (EX_ARGSBAD);
+ }
+ f_compressprog = "compress";
+ break;
+
+ case '?':
+ badopt:
+ msg ("Unknown option. Use '%s --help' for a complete list of options.", tar);
+ exit (EX_ARGSBAD);
+
+ }
+ }
+
+ blocksize = blocking * RECORDSIZE;
+ if (n_ar_files == 0)
+ {
+ n_ar_files = 1;
+ ar_files[0] = getenv ("TAPE"); /* From environment, or */
+ if (ar_files[0] == 0)
+ ar_files[0] = DEF_AR_FILE; /* From Makefile */
+ }
+ if (n_ar_files > 1 && !f_multivol)
+ {
+ msg ("Multiple archive files requires --multi-volume\n");
+ exit (EX_ARGSBAD);
+ }
+ if (f_compress_block && !f_compressprog)
+ {
+ msg ("You must use a compression option (--gzip, --compress\n\
+or --use-compress-program) with --block-compress.\n");
+ exit (EX_ARGSBAD);
+ }
+}
+
+
+/*
+ * Print as much help as the user's gonna get.
+ *
+ * We have to sprinkle in the KLUDGE lines because too many compilers
+ * cannot handle character strings longer than about 512 bytes. Yuk!
+ * In particular, MS-DOS and Xenix MSC and PDP-11 V7 Unix have this
+ * problem.
+ */
+void
+describe ()
+{
+ puts ("choose one of the following:");
+ fputs ("\
+-A, --catenate,\n\
+ --concatenate append tar files to an archive\n\
+-c, --create create a new archive\n\
+-d, --diff,\n\
+ --compare find differences between archive and file system\n\
+--delete delete from the archive (not for use on mag tapes!)\n\
+-r, --append append files to the end of an archive\n\
+-t, --list list the contents of an archive\n\
+-u, --update only append files that are newer than copy in archive\n\
+-x, --extract,\n\
+ --get extract files from an archive\n", stdout);
+
+ fprintf (stdout, "\
+Other options:\n\
+--atime-preserve don't change access times on dumped files\n\
+-b, --block-size N block size of Nx512 bytes (default N=%d)\n", DEFBLOCKING);
+ fputs ("\
+-B, --read-full-blocks reblock as we read (for reading 4.2BSD pipes)\n\
+-C, --directory DIR change to directory DIR\n\
+--checkpoint print directory names while reading the archive\n\
+", stdout); /* KLUDGE */
+ fprintf (stdout, "\
+-f, --file [HOSTNAME:]F use archive file or device F (default %s)\n",
+ DEF_AR_FILE);
+ fputs ("\
+--force-local archive file is local even if has a colon\n\
+-F, --info-script F\n\
+ --new-volume-script F run script at end of each tape (implies -M)\n\
+-G, --incremental create/list/extract old GNU-format incremental backup\n\
+-g, --listed-incremental F create/list/extract new GNU-format incremental backup\n\
+-h, --dereference don't dump symlinks; dump the files they point to\n\
+-i, --ignore-zeros ignore blocks of zeros in archive (normally mean EOF)\n\
+--ignore-failed-read don't exit with non-zero status on unreadable files\n\
+-k, --keep-old-files keep existing files; don't overwrite them from archive\n\
+-K, --starting-file F begin at file F in the archive\n\
+-l, --one-file-system stay in local file system when creating an archive\n\
+-L, --tape-length N change tapes after writing N*1024 bytes\n\
+", stdout); /* KLUDGE */
+ fputs ("\
+-m, --modification-time don't extract file modified time\n\
+-M, --multi-volume create/list/extract multi-volume archive\n\
+-N, --after-date DATE,\n\
+ --newer DATE only store files newer than DATE\n\
+-o, --old-archive,\n\
+ --portability write a V7 format archive, rather than ANSI format\n\
+-O, --to-stdout extract files to standard output\n\
+-p, --same-permissions,\n\
+ --preserve-permissions extract all protection information\n\
+-P, --absolute-paths don't strip leading `/'s from file names\n\
+--preserve like -p -s\n\
+", stdout); /* KLUDGE */
+ fputs ("\
+-R, --record-number show record number within archive with each message\n\
+--remove-files remove files after adding them to the archive\n\
+-s, --same-order,\n\
+ --preserve-order list of names to extract is sorted to match archive\n\
+--same-owner create extracted files with the same ownership \n\
+-S, --sparse handle sparse files efficiently\n\
+-T, --files-from F get names to extract or create from file F\n\
+--null -T reads null-terminated names, disable -C\n\
+--totals print total bytes written with --create\n\
+-v, --verbose verbosely list files processed\n\
+-V, --label NAME create archive with volume name NAME\n\
+--version print tar program version number\n\
+-w, --interactive,\n\
+ --confirmation ask for confirmation for every action\n\
+", stdout); /* KLUDGE */
+ fputs ("\
+-W, --verify attempt to verify the archive after writing it\n\
+--exclude FILE exclude file FILE\n\
+-X, --exclude-from FILE exclude files listed in FILE\n\
+-Z, --compress,\n\
+ --uncompress filter the archive through compress\n\
+-z, --gzip,\n\
+ --ungzip filter the archive through gzip\n\
+--use-compress-program PROG\n\
+ filter the archive through PROG (which must accept -d)\n\
+--block-compress block the output of compression program for tapes\n\
+-[0-7][lmh] specify drive and density\n\
+--unlink unlink files before creating them\n\
+", stdout);
+}
+
+void
+name_add (name)
+ char *name;
+{
+ if (n_indalloc == n_indused)
+ {
+ n_indalloc += 10;
+ n_ind = (char **) (n_indused ? ck_realloc (n_ind, n_indalloc * sizeof (char *)): ck_malloc (n_indalloc * sizeof (char *)));
+ }
+ n_ind[n_indused++] = name;
+}
+
+/*
+ * Set up to gather file names for tar.
+ *
+ * They can either come from stdin or from argv.
+ */
+void
+name_init (argc, argv)
+ int argc;
+ char **argv;
+{
+
+ if (f_namefile)
+ {
+ if (optind < argc)
+ {
+ msg ("too many args with -T option");
+ exit (EX_ARGSBAD);
+ }
+ if (!strcmp (name_file, "-"))
+ {
+ namef = stdin;
+ }
+ else
+ {
+ namef = fopen (name_file, "r");
+ if (namef == NULL)
+ {
+ msg_perror ("can't open file %s", name_file);
+ exit (EX_BADFILE);
+ }
+ }
+ }
+ else
+ {
+ /* Get file names from argv, after options. */
+ n_argc = argc;
+ n_argv = argv;
+ }
+}
+
+/* Read the next filename read from STREAM and null-terminate it.
+ Put it into BUFFER, reallocating and adjusting *PBUFFER_SIZE if necessary.
+ Return the new value for BUFFER, or NULL at end of file. */
+
+char *
+read_name_from_file (buffer, pbuffer_size, stream)
+ char *buffer;
+ size_t *pbuffer_size;
+ FILE *stream;
+{
+ register int c;
+ register int indx = 0;
+ register size_t buffer_size = *pbuffer_size;
+
+ while ((c = getc (stream)) != EOF && c != filename_terminator)
+ {
+ if (indx == buffer_size)
+ {
+ buffer_size += NAMSIZ;
+ buffer = ck_realloc (buffer, buffer_size + 2);
+ }
+ buffer[indx++] = c;
+ }
+ if (indx == 0 && c == EOF)
+ return NULL;
+ if (indx == buffer_size)
+ {
+ buffer_size += NAMSIZ;
+ buffer = ck_realloc (buffer, buffer_size + 2);
+ }
+ buffer[indx] = '\0';
+ *pbuffer_size = buffer_size;
+ return buffer;
+}
+
+/*
+ * Get the next name from argv or the name file.
+ *
+ * Result is in static storage and can't be relied upon across two calls.
+ *
+ * If CHANGE_DIRS is non-zero, treat a filename of the form "-C" as
+ * meaning that the next filename is the name of a directory to change to.
+ * If `filename_terminator' is '\0', CHANGE_DIRS is effectively always 0.
+ */
+
+char *
+name_next (change_dirs)
+ int change_dirs;
+{
+ static char *buffer; /* Holding pattern */
+ static int buffer_siz;
+ register char *p;
+ register char *q = 0;
+ register int next_name_is_dir = 0;
+ extern char *un_quote_string ();
+
+ if (buffer_siz == 0)
+ {
+ buffer = ck_malloc (NAMSIZ + 2);
+ buffer_siz = NAMSIZ;
+ }
+ if (filename_terminator == '\0')
+ change_dirs = 0;
+tryagain:
+ if (namef == NULL)
+ {
+ if (n_indscan < n_indused)
+ p = n_ind[n_indscan++];
+ else if (optind < n_argc)
+ /* Names come from argv, after options */
+ p = n_argv[optind++];
+ else
+ {
+ if (q)
+ msg ("Missing filename after -C");
+ return NULL;
+ }
+
+ /* JF trivial support for -C option. I don't know if
+ chdir'ing at this point is dangerous or not.
+ It seems to work, which is all I ask. */
+ if (change_dirs && !q && p[0] == '-' && p[1] == 'C' && p[2] == '\0')
+ {
+ q = p;
+ goto tryagain;
+ }
+ if (q)
+ {
+ if (chdir (p) < 0)
+ msg_perror ("Can't chdir to %s", p);
+ q = 0;
+ goto tryagain;
+ }
+ /* End of JF quick -C hack */
+
+#if 0
+ if (f_exclude && check_exclude (p))
+ goto tryagain;
+#endif
+ return un_quote_string (p);
+ }
+ while (p = read_name_from_file (buffer, &buffer_siz, namef))
+ {
+ buffer = p;
+ if (*p == '\0')
+ continue; /* Ignore empty lines. */
+ q = p + strlen (p) - 1;
+ while (q > p && *q == '/')/* Zap trailing "/"s. */
+ *q-- = '\0';
+ if (change_dirs && next_name_is_dir == 0
+ && p[0] == '-' && p[1] == 'C' && p[2] == '\0')
+ {
+ next_name_is_dir = 1;
+ goto tryagain;
+ }
+ if (next_name_is_dir)
+ {
+ if (chdir (p) < 0)
+ msg_perror ("Can't change to directory %s", p);
+ next_name_is_dir = 0;
+ goto tryagain;
+ }
+#if 0
+ if (f_exclude && check_exclude (p))
+ goto tryagain;
+#endif
+ return un_quote_string (p);
+ }
+ return NULL;
+}
+
+
+/*
+ * Close the name file, if any.
+ */
+void
+name_close ()
+{
+
+ if (namef != NULL && namef != stdin)
+ fclose (namef);
+}
+
+
+/*
+ * Gather names in a list for scanning.
+ * Could hash them later if we really care.
+ *
+ * If the names are already sorted to match the archive, we just
+ * read them one by one. name_gather reads the first one, and it
+ * is called by name_match as appropriate to read the next ones.
+ * At EOF, the last name read is just left in the buffer.
+ * This option lets users of small machines extract an arbitrary
+ * number of files by doing "tar t" and editing down the list of files.
+ */
+void
+name_gather ()
+{
+ register char *p;
+ static struct name *namebuf; /* One-name buffer */
+ static namelen;
+ static char *chdir_name;
+
+ if (f_sorted_names)
+ {
+ if (!namelen)
+ {
+ namelen = NAMSIZ;
+ namebuf = (struct name *) ck_malloc (sizeof (struct name) + NAMSIZ);
+ }
+ p = name_next (0);
+ if (p)
+ {
+ if (*p == '-' && p[1] == 'C' && p[2] == '\0')
+ {
+ chdir_name = name_next (0);
+ p = name_next (0);
+ if (!p)
+ {
+ msg ("Missing file name after -C");
+ exit (EX_ARGSBAD);
+ }
+ namebuf->change_dir = chdir_name;
+ }
+ namebuf->length = strlen (p);
+ if (namebuf->length >= namelen)
+ {
+ namebuf = (struct name *) ck_realloc (namebuf, sizeof (struct name) + namebuf->length);
+ namelen = namebuf->length;
+ }
+ strncpy (namebuf->name, p, namebuf->length);
+ namebuf->name[namebuf->length] = 0;
+ namebuf->next = (struct name *) NULL;
+ namebuf->found = 0;
+ namelist = namebuf;
+ namelast = namelist;
+ }
+ return;
+ }
+
+ /* Non sorted names -- read them all in */
+ while (p = name_next (0))
+ addname (p);
+}
+
+/*
+ * Add a name to the namelist.
+ */
+void
+addname (name)
+ char *name; /* pointer to name */
+{
+ register int i; /* Length of string */
+ register struct name *p; /* Current struct pointer */
+ static char *chdir_name;
+ char *new_name ();
+
+ if (name[0] == '-' && name[1] == 'C' && name[2] == '\0')
+ {
+ chdir_name = name_next (0);
+ name = name_next (0);
+ if (!chdir_name)
+ {
+ msg ("Missing file name after -C");
+ exit (EX_ARGSBAD);
+ }
+ if (chdir_name[0] != '/')
+ {
+ char *path = ck_malloc (PATH_MAX);
+#if defined(__MSDOS__) || defined(HAVE_GETCWD) || defined(_POSIX_VERSION)
+ if (!getcwd (path, PATH_MAX))
+ {
+ msg ("Couldn't get current directory.");
+ exit (EX_SYSTEM);
+ }
+#else
+ char *getwd ();
+
+ if (!getwd (path))
+ {
+ msg ("Couldn't get current directory: %s", path);
+ exit (EX_SYSTEM);
+ }
+#endif
+ chdir_name = new_name (path, chdir_name);
+ free (path);
+ }
+ }
+
+ if (name)
+ {
+ i = strlen (name);
+ /*NOSTRICT*/
+ p = (struct name *) malloc ((unsigned) (sizeof (struct name) + i));
+ }
+ else
+ p = (struct name *) malloc ((unsigned) (sizeof (struct name)));
+ if (!p)
+ {
+ if (name)
+ msg ("cannot allocate mem for name '%s'.", name);
+ else
+ msg ("cannot allocate mem for chdir record.");
+ exit (EX_SYSTEM);
+ }
+ p->next = (struct name *) NULL;
+ if (name)
+ {
+ p->fake = 0;
+ p->length = i;
+ strncpy (p->name, name, i);
+ p->name[i] = '\0'; /* Null term */
+ }
+ else
+ p->fake = 1;
+ p->found = 0;
+ p->regexp = 0; /* Assume not a regular expression */
+ p->firstch = 1; /* Assume first char is literal */
+ p->change_dir = chdir_name;
+ p->dir_contents = 0; /* JF */
+ if (name)
+ {
+ if (index (name, '*') || index (name, '[') || index (name, '?'))
+ {
+ p->regexp = 1; /* No, it's a regexp */
+ if (name[0] == '*' || name[0] == '[' || name[0] == '?')
+ p->firstch = 0; /* Not even 1st char literal */
+ }
+ }
+
+ if (namelast)
+ namelast->next = p;
+ namelast = p;
+ if (!namelist)
+ namelist = p;
+}
+
+/*
+ * Return nonzero if name P (from an archive) matches any name from
+ * the namelist, zero if not.
+ */
+int
+name_match (p)
+ register char *p;
+{
+ register struct name *nlp;
+ register int len;
+
+again:
+ if (0 == (nlp = namelist)) /* Empty namelist is easy */
+ return 1;
+ if (nlp->fake)
+ {
+ if (nlp->change_dir && chdir (nlp->change_dir))
+ msg_perror ("Can't change to directory %d", nlp->change_dir);
+ namelist = 0;
+ return 1;
+ }
+ len = strlen (p);
+ for (; nlp != 0; nlp = nlp->next)
+ {
+ /* If first chars don't match, quick skip */
+ if (nlp->firstch && nlp->name[0] != p[0])
+ continue;
+
+ /* Regular expressions (shell globbing, actually). */
+ if (nlp->regexp)
+ {
+ if (fnmatch (nlp->name, p, FNM_LEADING_DIR) == 0)
+ {
+ nlp->found = 1; /* Remember it matched */
+ if (f_startfile)
+ {
+ free ((void *) namelist);
+ namelist = 0;
+ }
+ if (nlp->change_dir && chdir (nlp->change_dir))
+ msg_perror ("Can't change to directory %s", nlp->change_dir);
+ return 1; /* We got a match */
+ }
+ continue;
+ }
+
+ /* Plain Old Strings */
+ if (nlp->length <= len /* Archive len >= specified */
+ && (p[nlp->length] == '\0' || p[nlp->length] == '/')
+ /* Full match on file/dirname */
+ && strncmp (p, nlp->name, nlp->length) == 0) /* Name compare */
+ {
+ nlp->found = 1; /* Remember it matched */
+ if (f_startfile)
+ {
+ free ((void *) namelist);
+ namelist = 0;
+ }
+ if (nlp->change_dir && chdir (nlp->change_dir))
+ msg_perror ("Can't change to directory %s", nlp->change_dir);
+ return 1; /* We got a match */
+ }
+ }
+
+ /*
+ * Filename from archive not found in namelist.
+ * If we have the whole namelist here, just return 0.
+ * Otherwise, read the next name in and compare it.
+ * If this was the last name, namelist->found will remain on.
+ * If not, we loop to compare the newly read name.
+ */
+ if (f_sorted_names && namelist->found)
+ {
+ name_gather (); /* Read one more */
+ if (!namelist->found)
+ goto again;
+ }
+ return 0;
+}
+
+
+/*
+ * Print the names of things in the namelist that were not matched.
+ */
+void
+names_notfound ()
+{
+ register struct name *nlp, *next;
+ register char *p;
+
+ for (nlp = namelist; nlp != 0; nlp = next)
+ {
+ next = nlp->next;
+ if (!nlp->found)
+ msg ("%s not found in archive", nlp->name);
+
+ /*
+ * We could free() the list, but the process is about
+ * to die anyway, so save some CPU time. Amigas and
+ * other similarly broken software will need to waste
+ * the time, though.
+ */
+#ifdef amiga
+ if (!f_sorted_names)
+ free (nlp);
+#endif
+ }
+ namelist = (struct name *) NULL;
+ namelast = (struct name *) NULL;
+
+ if (f_sorted_names)
+ {
+ while (0 != (p = name_next (1)))
+ msg ("%s not found in archive", p);
+ }
+}
+
+/* These next routines were created by JF */
+
+void
+name_expand ()
+{
+ ;
+}
+
+/* This is like name_match(), except that it returns a pointer to the name
+ it matched, and doesn't set ->found The caller will have to do that
+ if it wants to. Oh, and if the namelist is empty, it returns 0, unlike
+ name_match(), which returns TRUE */
+
+struct name *
+name_scan (p)
+ register char *p;
+{
+ register struct name *nlp;
+ register int len;
+
+again:
+ if (0 == (nlp = namelist)) /* Empty namelist is easy */
+ return 0;
+ len = strlen (p);
+ for (; nlp != 0; nlp = nlp->next)
+ {
+ /* If first chars don't match, quick skip */
+ if (nlp->firstch && nlp->name[0] != p[0])
+ continue;
+
+ /* Regular expressions */
+ if (nlp->regexp)
+ {
+ if (fnmatch (nlp->name, p, FNM_LEADING_DIR) == 0)
+ return nlp; /* We got a match */
+ continue;
+ }
+
+ /* Plain Old Strings */
+ if (nlp->length <= len /* Archive len >= specified */
+ && (p[nlp->length] == '\0' || p[nlp->length] == '/')
+ /* Full match on file/dirname */
+ && strncmp (p, nlp->name, nlp->length) == 0) /* Name compare */
+ return nlp; /* We got a match */
+ }
+
+ /*
+ * Filename from archive not found in namelist.
+ * If we have the whole namelist here, just return 0.
+ * Otherwise, read the next name in and compare it.
+ * If this was the last name, namelist->found will remain on.
+ * If not, we loop to compare the newly read name.
+ */
+ if (f_sorted_names && namelist->found)
+ {
+ name_gather (); /* Read one more */
+ if (!namelist->found)
+ goto again;
+ }
+ return (struct name *) 0;
+}
+
+/* This returns a name from the namelist which doesn't have ->found set.
+ It sets ->found before returning, so successive calls will find and return
+ all the non-found names in the namelist */
+
+struct name *gnu_list_name;
+
+char *
+name_from_list ()
+{
+ if (!gnu_list_name)
+ gnu_list_name = namelist;
+ while (gnu_list_name && gnu_list_name->found)
+ gnu_list_name = gnu_list_name->next;
+ if (gnu_list_name)
+ {
+ gnu_list_name->found++;
+ if (gnu_list_name->change_dir)
+ if (chdir (gnu_list_name->change_dir) < 0)
+ msg_perror ("can't chdir to %s", gnu_list_name->change_dir);
+ return gnu_list_name->name;
+ }
+ return (char *) 0;
+}
+
+void
+blank_name_list ()
+{
+ struct name *n;
+
+ gnu_list_name = 0;
+ for (n = namelist; n; n = n->next)
+ n->found = 0;
+}
+
+char *
+new_name (path, name)
+ char *path, *name;
+{
+ char *path_buf;
+
+ path_buf = (char *) malloc (strlen (path) + strlen (name) + 2);
+ if (path_buf == 0)
+ {
+ msg ("Can't allocate memory for name '%s/%s", path, name);
+ exit (EX_SYSTEM);
+ }
+ (void) sprintf (path_buf, "%s/%s", path, name);
+ return path_buf;
+}
+
+/* returns non-zero if the luser typed 'y' or 'Y', zero otherwise. */
+
+int
+confirm (action, file)
+ char *action, *file;
+{
+ int c, nl;
+ static FILE *confirm_file = 0;
+ extern FILE *msg_file;
+ extern char TTY_NAME[];
+
+ fprintf (msg_file, "%s %s?", action, file);
+ fflush (msg_file);
+ if (!confirm_file)
+ {
+ confirm_file = (archive == 0) ? fopen (TTY_NAME, "r") : stdin;
+ if (!confirm_file)
+ {
+ msg ("Can't read confirmation from user");
+ exit (EX_SYSTEM);
+ }
+ }
+ c = getc (confirm_file);
+ for (nl = c; nl != '\n' && nl != EOF; nl = getc (confirm_file))
+ ;
+ return (c == 'y' || c == 'Y');
+}
+
+char *x_buffer = 0;
+int size_x_buffer;
+int free_x_buffer;
+
+char **exclude = 0;
+int size_exclude = 0;
+int free_exclude = 0;
+
+char **re_exclude = 0;
+int size_re_exclude = 0;
+int free_re_exclude = 0;
+
+void
+add_exclude (name)
+ char *name;
+{
+ /* char *rname;*/
+ /* char **tmp_ptr;*/
+ int size_buf;
+
+ un_quote_string (name);
+ size_buf = strlen (name);
+
+ if (x_buffer == 0)
+ {
+ x_buffer = (char *) ck_malloc (size_buf + 1024);
+ free_x_buffer = 1024;
+ }
+ else if (free_x_buffer <= size_buf)
+ {
+ char *old_x_buffer;
+ char **tmp_ptr;
+
+ old_x_buffer = x_buffer;
+ x_buffer = (char *) ck_realloc (x_buffer, size_x_buffer + 1024);
+ free_x_buffer = 1024;
+ for (tmp_ptr = exclude; tmp_ptr < exclude + size_exclude; tmp_ptr++)
+ *tmp_ptr = x_buffer + ((*tmp_ptr) - old_x_buffer);
+ for (tmp_ptr = re_exclude; tmp_ptr < re_exclude + size_re_exclude; tmp_ptr++)
+ *tmp_ptr = x_buffer + ((*tmp_ptr) - old_x_buffer);
+ }
+
+ if (is_regex (name))
+ {
+ if (free_re_exclude == 0)
+ {
+ re_exclude = (char **) (re_exclude ? ck_realloc (re_exclude, (size_re_exclude + 32) * sizeof (char *)): ck_malloc (sizeof (char *) * 32));
+ free_re_exclude += 32;
+ }
+ re_exclude[size_re_exclude] = x_buffer + size_x_buffer;
+ size_re_exclude++;
+ free_re_exclude--;
+ }
+ else
+ {
+ if (free_exclude == 0)
+ {
+ exclude = (char **) (exclude ? ck_realloc (exclude, (size_exclude + 32) * sizeof (char *)): ck_malloc (sizeof (char *) * 32));
+ free_exclude += 32;
+ }
+ exclude[size_exclude] = x_buffer + size_x_buffer;
+ size_exclude++;
+ free_exclude--;
+ }
+ strcpy (x_buffer + size_x_buffer, name);
+ size_x_buffer += size_buf + 1;
+ free_x_buffer -= size_buf + 1;
+}
+
+void
+add_exclude_file (file)
+ char *file;
+{
+ FILE *fp;
+ char buf[1024];
+
+ if (strcmp (file, "-"))
+ fp = fopen (file, "r");
+ else
+ /* Let's hope the person knows what they're doing. */
+ /* Using -X - -T - -f - will get you *REALLY* strange
+ results. . . */
+ fp = stdin;
+
+ if (!fp)
+ {
+ msg_perror ("can't open %s", file);
+ exit (2);
+ }
+ while (fgets (buf, 1024, fp))
+ {
+ /* int size_buf;*/
+ char *end_str;
+
+ end_str = rindex (buf, '\n');
+ if (end_str)
+ *end_str = '\0';
+ add_exclude (buf);
+
+ }
+ fclose (fp);
+}
+
+int
+is_regex (str)
+ char *str;
+{
+ return index (str, '*') || index (str, '[') || index (str, '?');
+}
+
+/* Returns non-zero if the file 'name' should not be added/extracted */
+int
+check_exclude (name)
+ char *name;
+{
+ int n;
+ char *str;
+ extern char *strstr ();
+
+ for (n = 0; n < size_re_exclude; n++)
+ {
+ if (fnmatch (re_exclude[n], name, FNM_LEADING_DIR) == 0)
+ return 1;
+ }
+ for (n = 0; n < size_exclude; n++)
+ {
+ /* Accept the output from strstr only if it is the last
+ part of the string. There is certainly a faster way to
+ do this. . . */
+ if ((str = strstr (name, exclude[n]))
+ && (str == name || str[-1] == '/')
+ && str[strlen (exclude[n])] == '\0')
+ return 1;
+ }
+ return 0;
+}
diff --git a/gnu/usr.bin/tar/tar.h b/gnu/usr.bin/tar/tar.h
new file mode 100644
index 0000000..46d29d8
--- /dev/null
+++ b/gnu/usr.bin/tar/tar.h
@@ -0,0 +1,292 @@
+/* Declarations for tar archives.
+ Copyright (C) 1988, 1992, 1993 Free Software Foundation
+
+This file is part of GNU Tar.
+
+GNU Tar is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Tar is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Tar; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* major() and minor() macros (among other things) defined here for hpux */
+#ifdef hpux
+#include <sys/mknod.h>
+#endif
+
+/*
+ * Kludge for handling systems that can't cope with multiple
+ * external definitions of a variable. In ONE routine (tar.c),
+ * we #define TAR_EXTERN to null; here, we set it to "extern" if
+ * it is not already set.
+ */
+#ifndef TAR_EXTERN
+#define TAR_EXTERN extern
+#endif
+
+/*
+ * Header block on tape.
+ *
+ * I'm going to use traditional DP naming conventions here.
+ * A "block" is a big chunk of stuff that we do I/O on.
+ * A "record" is a piece of info that we care about.
+ * Typically many "record"s fit into a "block".
+ */
+#define RECORDSIZE 512
+#define NAMSIZ 100
+#define TUNMLEN 32
+#define TGNMLEN 32
+#define SPARSE_EXT_HDR 21
+#define SPARSE_IN_HDR 4
+
+struct sparse
+ {
+ char offset[12];
+ char numbytes[12];
+ };
+
+struct sp_array
+ {
+ int offset;
+ int numbytes;
+ };
+
+union record
+ {
+ char charptr[RECORDSIZE];
+ struct header
+ {
+ char arch_name[NAMSIZ];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char chksum[8];
+ char linkflag;
+ char arch_linkname[NAMSIZ];
+ char magic[8];
+ char uname[TUNMLEN];
+ char gname[TGNMLEN];
+ char devmajor[8];
+ char devminor[8];
+ /* these following fields were added by JF for gnu */
+ /* and are NOT standard */
+ char atime[12];
+ char ctime[12];
+ char offset[12];
+ char longnames[4];
+#ifdef NEEDPAD
+ char pad;
+#endif
+ struct sparse sp[SPARSE_IN_HDR];
+ char isextended;
+ char realsize[12]; /* true size of the sparse file */
+ /* char ending_blanks[12];*//* number of nulls at the
+ end of the file, if any */
+ }
+ header;
+ struct extended_header
+ {
+ struct sparse sp[21];
+ char isextended;
+ }
+ ext_hdr;
+ };
+
+/* The checksum field is filled with this while the checksum is computed. */
+#define CHKBLANKS " " /* 8 blanks, no null */
+
+/* The magic field is filled with this if uname and gname are valid. */
+#define TMAGIC "ustar " /* 7 chars and a null */
+
+/* The linkflag defines the type of file */
+#define LF_OLDNORMAL '\0' /* Normal disk file, Unix compat */
+#define LF_NORMAL '0' /* Normal disk file */
+#define LF_LINK '1' /* Link to previously dumped file */
+#define LF_SYMLINK '2' /* Symbolic link */
+#define LF_CHR '3' /* Character special file */
+#define LF_BLK '4' /* Block special file */
+#define LF_DIR '5' /* Directory */
+#define LF_FIFO '6' /* FIFO special file */
+#define LF_CONTIG '7' /* Contiguous file */
+/* Further link types may be defined later. */
+
+/* Note that the standards committee allows only capital A through
+ capital Z for user-defined expansion. This means that defining something
+ as, say '8' is a *bad* idea. */
+#define LF_DUMPDIR 'D' /* This is a dir entry that contains
+ the names of files that were in
+ the dir at the time the dump
+ was made */
+#define LF_LONGLINK 'K' /* Identifies the NEXT file on the tape
+ as having a long linkname */
+#define LF_LONGNAME 'L' /* Identifies the NEXT file on the tape
+ as having a long name. */
+#define LF_MULTIVOL 'M' /* This is the continuation
+ of a file that began on another
+ volume */
+#define LF_NAMES 'N' /* For storing filenames that didn't
+ fit in 100 characters */
+#define LF_SPARSE 'S' /* This is for sparse files */
+#define LF_VOLHDR 'V' /* This file is a tape/volume header */
+/* Ignore it on extraction */
+
+/*
+ * Exit codes from the "tar" program
+ */
+#define EX_SUCCESS 0 /* success! */
+#define EX_ARGSBAD 1 /* invalid args */
+#define EX_BADFILE 2 /* invalid filename */
+#define EX_BADARCH 3 /* bad archive */
+#define EX_SYSTEM 4 /* system gave unexpected error */
+#define EX_BADVOL 5 /* Special error code means
+ Tape volume doesn't match the one
+ specified on the command line */
+
+/*
+ * Global variables
+ */
+TAR_EXTERN union record *ar_block; /* Start of block of archive */
+TAR_EXTERN union record *ar_record; /* Current record of archive */
+TAR_EXTERN union record *ar_last; /* Last+1 record of archive block */
+TAR_EXTERN char ar_reading; /* 0 writing, !0 reading archive */
+TAR_EXTERN int blocking; /* Size of each block, in records */
+TAR_EXTERN int blocksize; /* Size of each block, in bytes */
+TAR_EXTERN char *info_script; /* Script to run at end of each tape change */
+TAR_EXTERN char *name_file; /* File containing names to work on */
+TAR_EXTERN char filename_terminator; /* \n or \0. */
+TAR_EXTERN char *tar; /* Name of this program */
+TAR_EXTERN struct sp_array *sparsearray; /* Pointer to the start of the scratch space */
+TAR_EXTERN int sp_array_size; /* Initial size of the sparsearray */
+TAR_EXTERN int tot_written; /* Total written to output */
+TAR_EXTERN struct re_pattern_buffer
+ *label_pattern; /* compiled regex for extract label */
+TAR_EXTERN char **ar_files; /* list of tape drive names */
+TAR_EXTERN int n_ar_files; /* number of tape drive names */
+TAR_EXTERN int cur_ar_file; /* tape drive currently being used */
+TAR_EXTERN int ar_files_len; /* malloced size of ar_files */
+TAR_EXTERN char *current_file_name, *current_link_name;
+
+/*
+ * Flags from the command line
+ */
+TAR_EXTERN int cmd_mode;
+#define CMD_NONE 0
+#define CMD_CAT 1 /* -A */
+#define CMD_CREATE 2 /* -c */
+#define CMD_DIFF 3 /* -d */
+#define CMD_APPEND 4 /* -r */
+#define CMD_LIST 5 /* -t */
+#define CMD_UPDATE 6 /* -u */
+#define CMD_EXTRACT 7 /* -x */
+#define CMD_DELETE 8 /* -D */
+#define CMD_VERSION 9 /* --version */
+
+
+TAR_EXTERN int f_reblock; /* -B */
+#if 0
+TAR_EXTERN char f_dironly; /* -D */
+#endif
+TAR_EXTERN int f_run_script_at_end; /* -F */
+TAR_EXTERN int f_gnudump; /* -G */
+TAR_EXTERN int f_follow_links; /* -h */
+TAR_EXTERN int f_ignorez; /* -i */
+TAR_EXTERN int f_keep; /* -k */
+TAR_EXTERN int f_startfile; /* -K */
+TAR_EXTERN int f_local_filesys; /* -l */
+TAR_EXTERN int tape_length; /* -L */
+TAR_EXTERN int f_modified; /* -m */
+TAR_EXTERN int f_multivol; /* -M */
+TAR_EXTERN int f_new_files; /* -N */
+TAR_EXTERN int f_oldarch; /* -o */
+TAR_EXTERN int f_exstdout; /* -O */
+TAR_EXTERN int f_use_protection;/* -p */
+TAR_EXTERN int f_absolute_paths;/* -P */
+TAR_EXTERN int f_sayblock; /* -R */
+TAR_EXTERN int f_sorted_names; /* -s */
+TAR_EXTERN int f_sparse_files; /* -S ... JK */
+TAR_EXTERN int f_namefile; /* -T */
+TAR_EXTERN int f_verbose; /* -v */
+TAR_EXTERN char *f_volhdr; /* -V */
+TAR_EXTERN int f_confirm; /* -w */
+TAR_EXTERN int f_verify; /* -W */
+TAR_EXTERN int f_exclude; /* -X */
+TAR_EXTERN char *f_compressprog; /* -z and -Z */
+TAR_EXTERN int f_do_chown; /* --do-chown */
+TAR_EXTERN int f_totals; /* --totals */
+TAR_EXTERN int f_remove_files; /* --remove-files */
+TAR_EXTERN int f_ignore_failed_read; /* --ignore-failed-read */
+TAR_EXTERN int f_checkpoint; /* --checkpoint */
+TAR_EXTERN int f_show_omitted_dirs; /* --show-omitted-dirs */
+TAR_EXTERN char *f_volno_file; /* --volno-file */
+TAR_EXTERN int f_force_local; /* --force-local */
+TAR_EXTERN int f_atime_preserve;/* --atime-preserve */
+TAR_EXTERN int f_compress_block; /* --compress-block */
+TAR_EXTERN int f_unlink; /* --unlink */
+
+/*
+ * We default to Unix Standard format rather than 4.2BSD tar format.
+ * The code can actually produce all three:
+ * f_standard ANSI standard
+ * f_oldarch V7
+ * neither 4.2BSD
+ * but we don't bother, since 4.2BSD can read ANSI standard format anyway.
+ * The only advantage to the "neither" option is that we can cmp our
+ * output to the output of 4.2BSD tar, for debugging.
+ */
+#define f_standard (!f_oldarch)
+
+/*
+ * Structure for keeping track of filenames and lists thereof.
+ */
+struct name
+ {
+ struct name *next;
+ short length; /* cached strlen(name) */
+ char found; /* A matching file has been found */
+ char firstch; /* First char is literally matched */
+ char regexp; /* This name is a regexp, not literal */
+ char *change_dir; /* JF set with the -C option */
+ char *dir_contents; /* JF for f_gnudump */
+ char fake; /* dummy entry */
+ char name[1];
+ };
+
+TAR_EXTERN struct name *namelist; /* Points to first name in list */
+TAR_EXTERN struct name *namelast; /* Points to last name in list */
+
+TAR_EXTERN int archive; /* File descriptor for archive file */
+TAR_EXTERN int errors; /* # of files in error */
+
+TAR_EXTERN char *gnu_dumpfile;
+
+/*
+ * Error recovery stuff
+ */
+TAR_EXTERN char read_error_flag;
+
+
+/*
+ * Declarations of functions available to the world.
+ */
+union record *findrec ();
+void userec ();
+union record *endofrecs ();
+void anno ();
+
+#if defined (HAVE_VPRINTF) && __STDC__
+void msg (char *,...);
+void msg_perror (char *,...);
+#else
+void msg ();
+void msg_perror ();
+#endif
diff --git a/gnu/usr.bin/tar/update.c b/gnu/usr.bin/tar/update.c
new file mode 100644
index 0000000..a64317c
--- /dev/null
+++ b/gnu/usr.bin/tar/update.c
@@ -0,0 +1,585 @@
+/* Update a tar archive.
+ Copyright (C) 1988, 1992 Free Software Foundation
+
+This file is part of GNU Tar.
+
+GNU Tar is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Tar is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Tar; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* JF implement the 'r' 'u' and 'A' options for tar. */
+/* The 'A' option is my own invention: It means that the file-names are
+ tar files, and they should simply be appended to the end of the archive.
+ No attempt is made to block the reads from the args; if they're on raw
+ tape or something like that, it'll probably lose. . . */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+
+#ifdef HAVE_SYS_MTIO_H
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#endif
+
+#ifdef BSD42
+#include <sys/file.h>
+#else
+#ifndef V7
+#include <fcntl.h>
+#endif
+#endif
+
+#ifndef __MSDOS__
+#include <pwd.h>
+#include <grp.h>
+#endif
+
+#define STDIN 0
+#define STDOUT 1
+
+#include "tar.h"
+#include "port.h"
+#include "rmt.h"
+
+int time_to_start_writing = 0; /* We've hit the end of the old stuff,
+ and its time to start writing new stuff
+ to the tape. This involves seeking
+ back one block and re-writing the current
+ block (which has been changed). */
+
+char *output_start; /* Pointer to where we started to write in
+ the first block we write out. This is used
+ if we can't backspace the output and have
+ to null out the first part of the block */
+
+extern void skip_file ();
+extern void skip_extended_headers ();
+
+extern union record *head;
+extern struct stat hstat;
+
+void append_file ();
+void close_archive ();
+int confirm ();
+void decode_header ();
+void fl_read ();
+void fl_write ();
+void flush_archive ();
+int move_arch ();
+struct name *name_scan ();
+char *name_from_list ();
+void name_expand ();
+void name_gather ();
+void names_notfound ();
+void open_archive ();
+int read_header ();
+void reset_eof ();
+void write_block ();
+void write_eot ();
+
+/* Implement the 'r' (add files to end of archive), and 'u' (add files to
+ end of archive if they arent there, or are more up to date than the
+ version in the archive.) commands.*/
+void
+update_archive ()
+{
+ int found_end = 0;
+ int status = 3;
+ int prev_status;
+ char *p;
+ struct name *name;
+ extern void dump_file ();
+
+ name_gather ();
+ if (cmd_mode == CMD_UPDATE)
+ name_expand ();
+ open_archive (2); /* Open for updating */
+
+ do
+ {
+ prev_status = status;
+ status = read_header ();
+ switch (status)
+ {
+ case EOF:
+ found_end = 1;
+ break;
+
+ case 0: /* A bad record */
+ userec (head);
+ switch (prev_status)
+ {
+ case 3:
+ msg ("This doesn't look like a tar archive.");
+ /* FALL THROUGH */
+ case 2:
+ case 1:
+ msg ("Skipping to next header");
+ case 0:
+ break;
+ }
+ break;
+
+ /* A good record */
+ case 1:
+ /* printf("File %s\n",head->header.name); */
+ /* head->header.name[NAMSIZ-1]='\0'; */
+ if (cmd_mode == CMD_UPDATE && (name = name_scan (current_file_name)))
+ {
+
+ /* struct stat hstat; */
+ struct stat nstat;
+ int head_standard;
+
+ decode_header (head, &hstat, &head_standard, 0);
+ if (stat (current_file_name, &nstat) < 0)
+ {
+ msg_perror ("can't stat %s:", current_file_name);
+ }
+ else
+ {
+ if (hstat.st_mtime >= nstat.st_mtime)
+ name->found++;
+ }
+ }
+ userec (head);
+ if (head->header.isextended)
+ skip_extended_headers ();
+ skip_file ((long) hstat.st_size);
+ break;
+
+ case 2:
+ ar_record = head;
+ found_end = 1;
+ break;
+ }
+ }
+ while (!found_end);
+
+ reset_eof ();
+ time_to_start_writing = 1;
+ output_start = ar_record->charptr;
+
+ while (p = name_from_list ())
+ {
+ if (f_confirm && !confirm ("add", p))
+ continue;
+ if (cmd_mode == CMD_CAT)
+ append_file (p);
+ else
+ dump_file (p, -1, 1);
+ }
+
+ write_eot ();
+ close_archive ();
+ names_notfound ();
+}
+
+/* Catenate file p to the archive without creating a header for it. It had
+ better be a tar file or the archive is screwed */
+
+void
+append_file (p)
+ char *p;
+{
+ int fd;
+ struct stat statbuf;
+ long bytes_left;
+ union record *start;
+ long bufsiz, count;
+
+ if (0 != stat (p, &statbuf) || (fd = open (p, O_RDONLY | O_BINARY)) < 0)
+ {
+ msg_perror ("can't open file %s", p);
+ errors++;
+ return;
+ }
+
+ bytes_left = statbuf.st_size;
+
+ while (bytes_left > 0)
+ {
+ start = findrec ();
+ bufsiz = endofrecs ()->charptr - start->charptr;
+ if (bytes_left < bufsiz)
+ {
+ bufsiz = bytes_left;
+ count = bufsiz % RECORDSIZE;
+ if (count)
+ bzero (start->charptr + bytes_left, (int) (RECORDSIZE - count));
+ }
+ count = read (fd, start->charptr, bufsiz);
+ if (count < 0)
+ {
+ msg_perror ("read error at byte %ld reading %d bytes in file %s", statbuf.st_size - bytes_left, bufsiz, p);
+ exit (EX_ARGSBAD); /* FOO */
+ }
+ bytes_left -= count;
+ userec (start + (count - 1) / RECORDSIZE);
+ if (count != bufsiz)
+ {
+ msg ("%s: file shrunk by %d bytes, yark!", p, bytes_left);
+ abort ();
+ }
+ }
+ (void) close (fd);
+}
+
+#ifdef DONTDEF
+bprint (fp, buf, num)
+ FILE *fp;
+ char *buf;
+{
+ int c;
+
+ if (num == 0 || num == -1)
+ return;
+ fputs (" '", fp);
+ while (num--)
+ {
+ c = *buf++;
+ if (c == '\\')
+ fputs ("\\\\", fp);
+ else if (c >= ' ' && c <= '~')
+ putc (c, fp);
+ else
+ switch (c)
+ {
+ case '\n':
+ fputs ("\\n", fp);
+ break;
+ case '\r':
+ fputs ("\\r", fp);
+ break;
+ case '\b':
+ fputs ("\\b", fp);
+ break;
+ case '\0':
+ /* fputs("\\-",fp); */
+ break;
+ default:
+ fprintf (fp, "\\%03o", c);
+ break;
+ }
+ }
+ fputs ("'\n", fp);
+}
+
+#endif
+
+int number_of_blocks_read = 0;
+
+int number_of_new_records = 0;
+int number_of_records_needed = 0;
+
+union record *new_block = 0;
+union record *save_block = 0;
+
+void
+junk_archive ()
+{
+ int found_stuff = 0;
+ int status = 3;
+ int prev_status;
+ struct name *name;
+
+ /* int dummy_head; */
+ int number_of_records_to_skip = 0;
+ int number_of_records_to_keep = 0;
+ int number_of_kept_records_in_block;
+ int sub_status;
+ extern int write_archive_to_stdout;
+
+ /* fprintf(stderr,"Junk files\n"); */
+ name_gather ();
+ open_archive (2);
+
+ while (!found_stuff)
+ {
+ prev_status = status;
+ status = read_header ();
+ switch (status)
+ {
+ case EOF:
+ found_stuff = 1;
+ break;
+
+ case 0:
+ userec (head);
+ switch (prev_status)
+ {
+ case 3:
+ msg ("This doesn't look like a tar archive.");
+ /* FALL THROUGH */
+ case 2:
+ case 1:
+ msg ("Skipping to next header");
+ /* FALL THROUGH */
+ case 0:
+ break;
+ }
+ break;
+
+ case 1:
+ /* head->header.name[NAMSIZ-1] = '\0'; */
+ /* fprintf(stderr,"file %s\n",head->header.name); */
+ if ((name = name_scan (current_file_name)) == (struct name *) 0)
+ {
+ userec (head);
+ /* fprintf(stderr,"Skip %ld\n",(long)(hstat.st_size)); */
+ if (head->header.isextended)
+ skip_extended_headers ();
+ skip_file ((long) (hstat.st_size));
+ break;
+ }
+ name->found = 1;
+ found_stuff = 2;
+ break;
+
+ case 2:
+ found_stuff = 1;
+ break;
+ }
+ }
+ /* fprintf(stderr,"Out of first loop\n"); */
+
+ if (found_stuff != 2)
+ {
+ write_eot ();
+ close_archive ();
+ names_notfound ();
+ return;
+ }
+
+ if (write_archive_to_stdout)
+ write_archive_to_stdout = 0;
+ new_block = (union record *) malloc (blocksize);
+ if (new_block == 0)
+ {
+ msg ("Can't allocate secondary block of %d bytes", blocksize);
+ exit (EX_SYSTEM);
+ }
+
+ /* Save away records before this one in this block */
+ number_of_new_records = ar_record - ar_block;
+ number_of_records_needed = blocking - number_of_new_records;
+ if (number_of_new_records)
+ bcopy ((void *) ar_block, (void *) new_block, (number_of_new_records) * RECORDSIZE);
+
+ /* fprintf(stderr,"Saved %d recs, need %d more\n",number_of_new_records,number_of_records_needed); */
+ userec (head);
+ if (head->header.isextended)
+ skip_extended_headers ();
+ skip_file ((long) (hstat.st_size));
+ found_stuff = 0;
+ /* goto flush_file; */
+
+ for (;;)
+ {
+ /* Fill in a block */
+ /* another_file: */
+ if (ar_record == ar_last)
+ {
+ /* fprintf(stderr,"New block\n"); */
+ flush_archive ();
+ number_of_blocks_read++;
+ }
+ sub_status = read_header ();
+ /* fprintf(stderr,"Header type %d\n",sub_status); */
+
+ if (sub_status == 2 && f_ignorez)
+ {
+ userec (head);
+ continue;
+ }
+ if (sub_status == EOF || sub_status == 2)
+ {
+ found_stuff = 1;
+ bzero (new_block[number_of_new_records].charptr, RECORDSIZE * number_of_records_needed);
+ number_of_new_records += number_of_records_needed;
+ number_of_records_needed = 0;
+ write_block (0);
+ break;
+ }
+
+ if (sub_status == 0)
+ {
+ msg ("Deleting non-header from archive.");
+ userec (head);
+ continue;
+ }
+
+ /* Found another header. Yipee! */
+ /* head->header.name[NAMSIZ-1] = '\0'; */
+ /* fprintf(stderr,"File %s ",head->header.name); */
+ if (name = name_scan (current_file_name))
+ {
+ name->found = 1;
+ /* fprintf(stderr,"Flush it\n"); */
+ /* flush_file: */
+ /* decode_header(head,&hstat,&dummy_head,0); */
+ userec (head);
+ number_of_records_to_skip = (hstat.st_size + RECORDSIZE - 1) / RECORDSIZE;
+ /* fprintf(stderr,"Flushing %d recs from %s\n",number_of_records_to_skip,head->header.name); */
+
+ while (ar_last - ar_record <= number_of_records_to_skip)
+ {
+
+ /* fprintf(stderr,"Block: %d <= %d ",ar_last-ar_record,number_of_records_to_skip); */
+ number_of_records_to_skip -= (ar_last - ar_record);
+ flush_archive ();
+ number_of_blocks_read++;
+ /* fprintf(stderr,"Block %d left\n",number_of_records_to_skip); */
+ }
+ ar_record += number_of_records_to_skip;
+ /* fprintf(stderr,"Final %d\n",number_of_records_to_skip); */
+ number_of_records_to_skip = 0;
+ continue;
+ }
+
+ /* copy_header: */
+ new_block[number_of_new_records] = *head;
+ number_of_new_records++;
+ number_of_records_needed--;
+ number_of_records_to_keep = (hstat.st_size + RECORDSIZE - 1) / RECORDSIZE;
+ userec (head);
+ if (number_of_records_needed == 0)
+ write_block (1);
+ /* copy_data: */
+ number_of_kept_records_in_block = ar_last - ar_record;
+ if (number_of_kept_records_in_block > number_of_records_to_keep)
+ number_of_kept_records_in_block = number_of_records_to_keep;
+
+ /* fprintf(stderr,"Need %d kept_in %d keep %d\n",blocking,number_of_kept_records_in_block,number_of_records_to_keep); */
+
+ while (number_of_records_to_keep)
+ {
+ int n;
+
+ if (ar_record == ar_last)
+ {
+ /* fprintf(stderr,"Flush. . .\n"); */
+ fl_read ();
+ number_of_blocks_read++;
+ ar_record = ar_block;
+ number_of_kept_records_in_block = blocking;
+ if (number_of_kept_records_in_block > number_of_records_to_keep)
+ number_of_kept_records_in_block = number_of_records_to_keep;
+ }
+ n = number_of_kept_records_in_block;
+ if (n > number_of_records_needed)
+ n = number_of_records_needed;
+
+ /* fprintf(stderr,"Copying %d\n",n); */
+ bcopy ((void *) ar_record, (void *) (new_block + number_of_new_records), n * RECORDSIZE);
+ number_of_new_records += n;
+ number_of_records_needed -= n;
+ ar_record += n;
+ number_of_records_to_keep -= n;
+ number_of_kept_records_in_block -= n;
+ /* fprintf(stderr,"Now new %d need %d keep %d keep_in %d rec %d/%d\n",
+ number_of_new_records,number_of_records_needed,number_of_records_to_keep,
+ number_of_kept_records_in_block,ar_record-ar_block,ar_last-ar_block); */
+
+ if (number_of_records_needed == 0)
+ {
+ write_block (1);
+ }
+ }
+ }
+
+ write_eot ();
+ close_archive ();
+ names_notfound ();
+}
+
+void
+write_block (f)
+ int f;
+{
+ /* fprintf(stderr,"Write block\n"); */
+ /* We've filled out a block. Write it out. */
+
+ /* Backspace back to where we started. . . */
+ if (archive != STDIN)
+ (void) move_arch (-(number_of_blocks_read + 1));
+
+ save_block = ar_block;
+ ar_block = new_block;
+
+ if (archive == STDIN)
+ archive = STDOUT;
+ fl_write ();
+
+ if (archive == STDOUT)
+ archive = STDIN;
+ ar_block = save_block;
+
+ if (f)
+ {
+ /* Move the tape head back to where we were */
+ if (archive != STDIN)
+ (void) move_arch (number_of_blocks_read);
+ number_of_blocks_read--;
+ }
+
+ number_of_records_needed = blocking;
+ number_of_new_records = 0;
+}
+
+/* Move archive descriptor by n blocks worth. If n is positive we move
+ forward, else we move negative. If its a tape, MTIOCTOP had better
+ work. If its something else, we try to seek on it. If we can't
+ seek, we lose! */
+int
+move_arch (n)
+ int n;
+{
+ long cur;
+
+#ifdef MTIOCTOP
+ struct mtop t;
+ int er;
+
+ if (n > 0)
+ {
+ t.mt_op = MTFSR;
+ t.mt_count = n;
+ }
+ else
+ {
+ t.mt_op = MTBSR;
+ t.mt_count = -n;
+ }
+ if ((er = rmtioctl (archive, MTIOCTOP, &t)) >= 0)
+ return 1;
+ if (errno == EIO && (er = rmtioctl (archive, MTIOCTOP, &t)) >= 0)
+ return 1;
+#endif
+
+ cur = rmtlseek (archive, 0L, 1);
+ cur += blocksize * n;
+
+ /* fprintf(stderr,"Fore to %x\n",cur); */
+ if (rmtlseek (archive, cur, 0) != cur)
+ {
+ /* Lseek failed. Try a different method */
+ msg ("Couldn't re-position archive file.");
+ exit (EX_BADARCH);
+ }
+ return 3;
+}
diff --git a/gnu/usr.bin/tar/version.c b/gnu/usr.bin/tar/version.c
new file mode 100644
index 0000000..4454f62
--- /dev/null
+++ b/gnu/usr.bin/tar/version.c
@@ -0,0 +1 @@
+char version_string[] = "GNU tar version 1.11.2";
diff --git a/gnu/usr.bin/tar/y.tab.h b/gnu/usr.bin/tar/y.tab.h
new file mode 100644
index 0000000..4a541d2
--- /dev/null
+++ b/gnu/usr.bin/tar/y.tab.h
@@ -0,0 +1,18 @@
+#define tAGO 257
+#define tDAY 258
+#define tDAYZONE 259
+#define tID 260
+#define tMERIDIAN 261
+#define tMINUTE_UNIT 262
+#define tMONTH 263
+#define tMONTH_UNIT 264
+#define tSEC_UNIT 265
+#define tSNUMBER 266
+#define tUNUMBER 267
+#define tZONE 268
+#define tDST 269
+typedef union {
+ time_t Number;
+ enum _MERIDIAN Meridian;
+} YYSTYPE;
+extern YYSTYPE yylval;
OpenPOWER on IntegriCloud